summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--BitKeeper/triggers/ciabot.config19
-rw-r--r--apt/apt-0.5.27/autofoo.patch89
-rw-r--r--apt/apt-0.6.25/autofoo.patch80
-rw-r--r--automake/automake-1.9.2/automake182-update-configscripts.patch201
-rw-r--r--automake/automake-1.9.3/automake182-update-configscripts.patch201
-rw-r--r--binutils/binutils-2.15.94.0.1/binutils-100_cflags_for_build.patch91
-rw-r--r--binutils/binutils-2.15.94.0.1/binutils-2.15.90.0.3-uclibc-200-build_modules.patch33
-rw-r--r--binutils/binutils-2.15.94.0.1/binutils-2.15.90.0.3-uclibc-210-cflags.patch30
-rw-r--r--binutils/binutils-2.15.94.0.1/binutils-2.15.91.0.1-uclibc-100-conf.patch604
-rw-r--r--bluez-utils/bluez-utils-dbus-2.11/dbus.patch52
-rw-r--r--busybox/busybox-1.00/hdparm_M.patch47
-rw-r--r--busybox/busybox-1.00/openslug/defconfig474
-rw-r--r--busybox/busybox-1.00/udhcppidfile.patch276
-rw-r--r--busybox/switchbox-1.00/switchbox.patch38
-rw-r--r--cpio/files/install.patch32
-rw-r--r--dpkg/files/autofoo.patch48
-rw-r--r--gcc/gcc-3.4.3/gcc-3.4.0-arm-bigendian-uclibc.patch30
-rw-r--r--gcc/gcc-3.4.3/gcc-3.4.0-arm-bigendian.patch70
-rw-r--r--gcc/gcc-3.4.3/gcc-3.4.0-arm-lib1asm.patch24
-rw-r--r--gcc/gcc-3.4.3/gcc-3.4.0-arm-nolibfloat.patch24
-rw-r--r--gcc/gcc-3.4.3/gcc-3.4.0-arm-softfloat.patch256
-rw-r--r--gcc/gcc-3.4.3/gcc-3.4.1-uclibc-100-conf.patch410
-rw-r--r--gcc/gcc-3.4.3/gcc-3.4.1-uclibc-200-locale.patch3216
-rw-r--r--gcc/gcc-3.4.3/gcc-uclibc-3.4.0-120-softfloat.patch14
-rw-r--r--gcc/gcc-3.4.3/gcc34-arm-ldm-peephole.patch79
-rw-r--r--gcc/gcc-3.4.3/gcc34-arm-ldm.patch119
-rw-r--r--gcc/gcc-3.4.3/gcc34-arm-tune.patch9
-rw-r--r--gcc/gcc-3.4.3/gcc34-reverse-compare.patch32
-rw-r--r--gcc/gcc-3.4.3/sdk-libstdc++-includes.patch22
-rw-r--r--gettext/gettext-0.14.1/fixchicken.patch26
-rw-r--r--gpe-beam/gpe-beam-0.2.6/vcard-send.patch33
-rw-r--r--gpe-bluetooth/gpe-bluetooth-0.37/hciattach-path.patch11
-rw-r--r--gpe-conf/gpe-conf-0.1.22/gpe-conf-0.1.22-20041124.patch227
-rw-r--r--gpe-gallery/files/mcheck.patch24
-rw-r--r--hostap/files/hostap_cs.modconf2
-rw-r--r--hotplug/files/collie/usbd1
-rw-r--r--hotplug/files/sysconfig-hotplug12
-rw-r--r--hotplug/files/tosa/sysconfig-usb2
-rw-r--r--hotplug/files/usbd2
-rw-r--r--hotplug/hotplug-20040920/userspecified_hcd.patch55
-rw-r--r--insttrain/files/gcc3.patch33
-rw-r--r--irk/files/install-default-conf.patch18
-rw-r--r--ixp425-eth/files/ethhdr.patch25
-rw-r--r--ixp425-eth/files/makefile.patch18
-rw-r--r--ixp4xx/ixp4xx-csr/badpaths.patch18
-rw-r--r--ixp4xx/ixp4xx-csr/build-timing-annoyance.patch10
-rw-r--r--lftp/lftp-3.0.11/pty.patch31
-rw-r--r--lftp/lftp-3.0.11/readline.patch24
-rw-r--r--libmikmod/libmikmod/autofoo.patch16
-rw-r--r--libtool/libtool-1.5.10/autotools.patch127
-rw-r--r--libtool/libtool-1.5.10/install-path-check.patch25
-rw-r--r--libtool/libtool-1.5.10/libdir-la.patch52
-rw-r--r--libtool/libtool-1.5.10/prefix.patch40
-rw-r--r--libtool/libtool-1.5.10/sedvar.patch16
-rw-r--r--libtool/libtool-1.5.10/tag.patch19
-rw-r--r--libtool/libtool-1.5.10/uclibc.patch19
-rw-r--r--libusb/libusb-0.1.8/amd64-fPIC.patch11
-rw-r--r--libusb/libusb-0.1.8/configure.patch99
-rw-r--r--libusb/libusb-0.1.8/pkgconfig.patch68
-rw-r--r--libxine/files/configure-1.0.0-rc7.patch177
-rw-r--r--libxine/files/cpu-1.0.0-rc7.patch112
-rw-r--r--libxine/libxine-opie-1.0.0-rc7/demuxogg.patch26
-rw-r--r--libxine/libxine-opie-1.0.0-rc7/fix-syntax.patch16
-rw-r--r--libxine/libxine-opie-1.0.0-rc7/fix-x.patch37
-rw-r--r--libxine/libxine-opie-1.0.0-rc7/libxine-arm-configure.patch18
-rw-r--r--libxine/libxine-opie-1.0.0-rc7/libxine-cut-memusage.patch63
-rw-r--r--libxine/libxine-opie-1.0.0-rc7/libxine-ffmpeg-enable-arm.patch20
-rw-r--r--libxine/libxine-opie-1.0.0-rc7/libxine-libavcodec.patch926
-rw-r--r--libxine/libxine-opie-1.0.0-rc7/libxine-libvorbis.patch37
-rw-r--r--libxine/libxine-opie-1.0.0-rc7/libxine-tremor-autoconf.patch16
-rw-r--r--libxine/libxine-opie-1.0.0-rc7/mpegvideo-static-inlinine.patch16
-rw-r--r--libxrender/files/autofoo.patch20
-rw-r--r--linux/linux-mtx-1-2.4.24/01-mtd-2004-01-27.diff51462
-rw-r--r--linux/linux-mtx-1-2.4.24/02-mtd-mtx-1-map.diff248
-rw-r--r--linux/linux-mtx-1-2.4.24/03-mtd-erase-compiler-bug.diff21
-rw-r--r--linux/linux-mtx-1-2.4.24/04-zboot-2.4.24.patch5286
-rw-r--r--linux/linux-mtx-1-2.4.24/05-zboot-cflags.diff15
-rw-r--r--linux/linux-mtx-1-2.4.24/06-zboot-mtx.diff27
-rw-r--r--linux/linux-mtx-1-2.4.24/07-zimage-flash-bin.patch11
-rw-r--r--linux/linux-mtx-1-2.4.24/08-usb-nonpci-2.4.24.patch173
-rw-r--r--linux/linux-mtx-1-2.4.24/09-iw-max-spy-32.diff11
-rw-r--r--linux/linux-mtx-1-2.4.24/10-mtx-pci-slots.diff23
-rw-r--r--linux/linux-mtx-1-2.4.24/11-mtx-extraversion.diff11
-rw-r--r--linux/linux-mtx-1-2.4.24/12-openswan-2.2.0-nat-t.diff143
-rw-r--r--linux/linux-mtx-1-2.4.24/13-openswan-2.2.0.patch61573
-rw-r--r--linux/linux-mtx-1-2.4.24/14-au1000-eth-vlan.diff10
-rw-r--r--linux/linux-mtx-1-2.4.24/15-mtd-proc-partition-rw.diff173
-rw-r--r--linux/linux-mtx-1-2.4.27/01-mtd-2004-01-27.diff51503
-rw-r--r--linux/linux-mtx-1-2.4.27/02-mtd-mtx-1-map.diff248
-rw-r--r--linux/linux-mtx-1-2.4.27/03-mtd-erase-compiler-bug.diff21
-rw-r--r--linux/linux-mtx-1-2.4.27/04-mtx-1-board-reset.diff15
-rw-r--r--linux/linux-mtx-1-2.4.27/05-mtx-1-pci-irq.diff18
-rw-r--r--linux/linux-mtx-1-2.4.27/06-zboot-2.4.26.patch5308
-rw-r--r--linux/linux-mtx-1-2.4.27/07-zboot-zimage-flash-bin.diff11
-rw-r--r--linux/linux-mtx-1-2.4.27/08-usb-nonpci-2.4.24.patch3185
-rw-r--r--linux/linux-mtx-1-2.4.27/09-au1000-eth-vlan.diff10
-rw-r--r--linux/linux-mtx-1-2.4.27/10-iw-max-spy-32.diff11
-rw-r--r--linux/linux-mtx-1-2.4.27/11-mtd-proc-partition-rw.diff173
-rw-r--r--linux/linux-mtx-1-2.4.27/12-openswan-2.2.0-nat-t.diff143
-rw-r--r--linux/linux-mtx-1-2.4.27/13-openswan-2.2.0.patch61573
-rw-r--r--linux/linux-mtx-1-2.4.27/14-au1000-eth-link-beat.diff64
-rw-r--r--linux/openslug-kernel-2.6.9/nslu2-io.c651
-rw-r--r--linux/openslug-kernel-2.6.9/nslu2-part.c120
-rw-r--r--linux/openslug-kernel-2.6.9/nslu2-pci.c87
-rw-r--r--linux/openslug-kernel-2.6.9/nslu2-setup.c132
-rw-r--r--linux/openslug-kernel-2.6.9/nslu2.h43
-rw-r--r--linux/openslug-kernel-2.6.9/x1205-rtc.c548
-rw-r--r--linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/1764-1.patch16
-rw-r--r--linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/bluetooth-2.4.18-mh15.patch32759
-rw-r--r--linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/corgi-fbcon-logo.patch281
-rw-r--r--linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/module_licence.patch96
-rw-r--r--linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/tosa_map.patch889
-rw-r--r--linux/openzaurus-sa-2.4.18-rmk7-pxa3-embedix20030509/1764-1.patch16
-rw-r--r--linux/openzaurus-sa-2.4.18-rmk7-pxa3-embedix20030509/bluetooth-2.4.18-mh15.patch32759
-rw-r--r--linux/unslung-kernel-2.3r25/ext3flash-on-disk1.patch41
-rw-r--r--lirc/files/lirc_sir-sa1100.patch10
-rw-r--r--m4/m4/make.patch41
-rw-r--r--make/make-3.80/SCCS.patch16
-rw-r--r--mozilla/firefox-1.0/extensions-hack.patch10
-rw-r--r--mozilla/firefox-1.0/mozconfig53
-rw-r--r--netkit-rpc/netkit-rpc/gcc4.patch37
-rw-r--r--netkit-tftp/netkit-tftp-0.17/mconfig.patch20
-rw-r--r--netkit-tftp/netkit-tftp-0.17/pack_tftphdr.patch52
-rw-r--r--netkit-tftp/netkit-tftp-0.17/tftpd_add_debug.patch28
-rw-r--r--notez/notez-1.1.0/use-homedir.patch92
-rw-r--r--nslu2-binary-only/unslung-ramdisk-2.3r25/able/mount_usbdevfs.patch10
-rw-r--r--ode/files/config.h48
-rw-r--r--popt/popt-1.7/intl.patch15
-rw-r--r--qpdf2/files/hack-the-hack.patch173
-rw-r--r--qpealarmclock/qpealarmclock-1.0.4/fix-compile.patch83
-rw-r--r--rdesktop/rdesktop-1.3.1/configure.patch134
-rw-r--r--t1lib/t1lib-5.0.2/configure.patch122
-rw-r--r--t1lib/t1lib-5.0.2/install.patch163
-rw-r--r--t1lib/t1lib-5.0.2/libtool.patch116
-rw-r--r--thttpd/files/acinclude.m4188
-rw-r--r--thttpd/files/install.patch87
-rw-r--r--tslib/tslib/initialize_djs.patch20
-rw-r--r--tslib/tslib/omap1610h2/tslib.sh5
-rw-r--r--unionfs/unionfs-1.0.3/gcc2-hack.patch28
-rw-r--r--unionfs/unionfs-1.0.3/make.patch124
-rw-r--r--usbutils/usbutils-0.11+cvs20041108/usbutils-0.11+cvs20041108.patch7817
-rw-r--r--util-linux/util-linux-2.12/ioctl.diff52
-rw-r--r--visiscript/files/qcleanuphandler.h127
-rw-r--r--visiscript/files/qptrlist.h9
-rw-r--r--wlan-ng/files/post-down31
-rw-r--r--wlan-ng/files/resume22
-rw-r--r--x11/files/autofoo.patch22
-rw-r--r--xau/files/autofoo.patch21
-rw-r--r--xdmcp/files/autofoo.patch22
-rw-r--r--xext/files/autofoo.patch18
-rw-r--r--xqt/files/imake-staging.patch38
-rw-r--r--xqt/files/moc_call.patch16
-rw-r--r--xserver/xserver-kdrive/fbdev-not-fix.patch14
153 files changed, 328960 insertions, 0 deletions
diff --git a/BitKeeper/triggers/ciabot.config b/BitKeeper/triggers/ciabot.config
index e69de29bb2..42d4e0c68d 100644
--- a/BitKeeper/triggers/ciabot.config
+++ b/BitKeeper/triggers/ciabot.config
@@ -0,0 +1,19 @@
+#
+# ciabot.config -- Configuration file for ciabot
+#
+# Note: The IRC network name is ignored for now, but must be included.
+# What you say here end up as a prefix on each row of your logs
+# on #commits. You must use the name of your projects IRC channel in
+# order to invite the bot there.
+#
+# example:
+# IRCCHANNEL=irc.freenode.net/ourprojectchannel
+IRCCHANNEL=irc.freenode.net/#openembedded
+
+# The email address where we send the commit messages.
+COMMITADDRESS=commits@picogui.org
+
+# Verbose flag, if not empty, show individual file history
+# VERBOSE=yes
+
+# End
diff --git a/apt/apt-0.5.27/autofoo.patch b/apt/apt-0.5.27/autofoo.patch
index e69de29bb2..d9a30d096c 100644
--- a/apt/apt-0.5.27/autofoo.patch
+++ b/apt/apt-0.5.27/autofoo.patch
@@ -0,0 +1,89 @@
+
+#
+# Patch managed by http://www.holgerschurig.de/patcher.html
+#
+
+--- apt-0.5.27/configure.in~autofoo
++++ apt-0.5.27/configure.in
+@@ -18,9 +18,9 @@
+ AC_CONFIG_HEADER(include/config.h:buildlib/config.h.in include/apti18n.h:buildlib/apti18n.h.in)
+
+ dnl -- SET THIS TO THE RELEASE VERSION --
+-AC_DEFINE_UNQUOTED(VERSION,"0.5.27")
++AC_DEFINE_UNQUOTED(VERSION,"0.5.27", [package version])
+ PACKAGE="apt"
+-AC_DEFINE_UNQUOTED(PACKAGE,"$PACKAGE")
++AC_DEFINE_UNQUOTED(PACKAGE,"$PACKAGE", [package name])
+ AC_SUBST(PACKAGE)
+
+ dnl Check the archs, we want the target type.
+@@ -47,7 +47,7 @@
+ LIBS="$SAVE_LIBS"
+
+ dnl Checks for pthread -- disabled due to glibc bugs jgg
+-dnl AC_CHECK_LIB(pthread, pthread_create,[AC_DEFINE(HAVE_PTHREAD) PTHREADLIB="-lpthread"])
++dnl AC_CHECK_LIB(pthread, pthread_create,[AC_DEFINE(HAVE_PTHREAD, 1, [define if we have pthreads]) PTHREADLIB="-lpthread"])
+ AC_SUBST(PTHREADLIB)
+ dnl if test "$PTHREADLIB" != "-lpthread"; then
+ dnl AC_MSG_ERROR(failed: I need posix threads, pthread)
+@@ -64,7 +64,7 @@
+ [#include <db.h>],
+ [int r, s, t; db_version(&r, &s, &t);]
+ )],
+- [AC_DEFINE(HAVE_BDB)
++ [AC_DEFINE(HAVE_BDB, 1, [define if we have BerkeleyDB])
+ BDBLIB="-ldb"
+ AC_MSG_RESULT(yes)],
+ [BDBLIB=""
+@@ -84,14 +84,14 @@
+ AC_MSG_ERROR(failed: use --host= or check buildlib/archtable)
+ fi
+ AC_MSG_RESULT($archset)
+-AC_DEFINE_UNQUOTED(COMMON_CPU,"$archset")
++AC_DEFINE_UNQUOTED(COMMON_CPU,"$archset",[define to the system architecture])
+
+ dnl Get a common name for the host OS - this is primarily only for HURD and is
+ dnl non fatal if it fails
+ AC_MSG_CHECKING(system OS)
+ osset="`awk \" ! /^#|^\\\$/ {if (match(\\\"$target_vendor-$target_os\\\",\\\$1)) {print \\\$2; exit}}\" $srcdir/buildlib/ostable`"
+ AC_MSG_RESULT($osset)
+-AC_DEFINE_UNQUOTED(COMMON_OS,"$osset")
++AC_DEFINE_UNQUOTED(COMMON_OS,"$osset",[define to the system OS])
+
+ dnl We use C99 types if at all possible
+ AC_CACHE_CHECK([for C99 integer types],c9x_ints,[
+@@ -105,13 +105,13 @@
+
+ dnl Arg, linux and bsd put their statfs function in different places
+ if test x"$HAVE_STATVFS" != x"yes"; then
+- AC_EGREP_HEADER(statfs,sys/vfs.h,[AC_DEFINE(HAVE_VFS_H)],[
+- AC_EGREP_HEADER(statfs,sys/mount.h,[AC_DEFINE(HAVE_MOUNT_H)],[AC_MSG_ERROR(failed: Need statvfs)])
++ AC_EGREP_HEADER(statfs,sys/vfs.h,[AC_DEFINE(HAVE_VFS_H, 1, [define if statfs is in sys/vfs.h])],[
++ AC_EGREP_HEADER(statfs,sys/mount.h,[AC_DEFINE(HAVE_MOUNT_H, 1, [define if statfs is in sys/mount.h])],[AC_MSG_ERROR(failed: Need statvfs)])
+ ])
+ fi
+
+ dnl We should use the real timegm function if we have it.
+-AC_CHECK_FUNC(timegm,AC_DEFINE(HAVE_TIMEGM))
++AC_CHECK_FUNC(timegm,AC_DEFINE(HAVE_TIMEGM, 1, [define if we have timegm]))
+ AC_SUBST(HAVE_TIMEGM)
+
+ dnl Check the sizes etc. of the architecture
+@@ -157,7 +157,7 @@
+ dnl HP-UX sux..
+ AC_MSG_CHECKING(for missing socklen_t)
+ AC_EGREP_HEADER(socklen_t, sys/socket.h,[AC_MSG_RESULT(no)],[
+- AC_DEFINE(NEED_SOCKLEN_T_DEFINE)
++ AC_DEFINE(NEED_SOCKLEN_T_DEFINE, 1, [define if we need to define socklen_t])
+ NEED_SOCKLEN_T_DEFINE=yes
+ AC_MSG_RESULT(missing.)])
+ AC_SUBST(NEED_SOCKLEN_T_DEFINE)
+@@ -195,7 +195,7 @@
+ ALL_LINGUAS="da de en_GB es fr hu it nl no_NO pl pt_BR ru sv zh_TW"
+ AM_GNU_GETTEXT(external)
+ if test x"$USE_NLS" = "xyes"; then
+- AC_DEFINE(USE_NLS)
++ AC_DEFINE(USE_NLS, 1, [define if we have native language support])
+ fi
+ AC_SUBST(USE_NLS)
+ AC_PATH_PROG(BASH, bash)
diff --git a/apt/apt-0.6.25/autofoo.patch b/apt/apt-0.6.25/autofoo.patch
index e69de29bb2..7083b838a2 100644
--- a/apt/apt-0.6.25/autofoo.patch
+++ b/apt/apt-0.6.25/autofoo.patch
@@ -0,0 +1,80 @@
+
+#
+# Patch managed by http://www.holgerschurig.de/patcher.html
+#
+
+--- apt-0.6.18/configure.in~autofoo
++++ apt-0.6.18/configure.in
+@@ -17,9 +17,9 @@
+ AC_CONFIG_HEADER(include/config.h:buildlib/config.h.in include/apti18n.h:buildlib/apti18n.h.in)
+
+ dnl -- SET THIS TO THE RELEASE VERSION --
+-AC_DEFINE_UNQUOTED(VERSION,"0.6.25")
++AC_DEFINE_UNQUOTED(VERSION,"0.6.25", [package version])
+ PACKAGE="apt"
+-AC_DEFINE_UNQUOTED(PACKAGE,"$PACKAGE")
++AC_DEFINE_UNQUOTED(PACKAGE,"$PACKAGE", [package name])
+ AC_SUBST(PACKAGE)
+
+ dnl Check the archs, we want the target type.
+@@ -46,7 +46,7 @@
+ LIBS="$SAVE_LIBS"
+
+ dnl Checks for pthread -- disabled due to glibc bugs jgg
+-dnl AC_CHECK_LIB(pthread, pthread_create,[AC_DEFINE(HAVE_PTHREAD) PTHREADLIB="-lpthread"])
++dnl AC_CHECK_LIB(pthread, pthread_create,[AC_DEFINE(HAVE_PTHREAD, 1, [define if we have pthreads]) PTHREADLIB="-lpthread"])
+ AC_SUBST(PTHREADLIB)
+ dnl if test "$PTHREADLIB" != "-lpthread"; then
+ dnl AC_MSG_ERROR(failed: I need posix threads, pthread)
+@@ -66,14 +66,14 @@
+ AC_MSG_ERROR(failed: use --host=)
+ fi
+ AC_MSG_RESULT($archset)
+-AC_DEFINE_UNQUOTED(COMMON_CPU,"$archset")
++AC_DEFINE_UNQUOTED(COMMON_CPU,"$archset",[define to the system architecture])
+
+ dnl Get a common name for the host OS - this is primarily only for HURD and is
+ dnl non fatal if it fails
+ AC_MSG_CHECKING(system OS)
+ osset="`awk \" ! /^#|^\\\$/ {if (match(\\\"$target_vendor-$target_os\\\",\\\$1)) {print \\\$2; exit}}\" $srcdir/buildlib/ostable`"
+ AC_MSG_RESULT($osset)
+-AC_DEFINE_UNQUOTED(COMMON_OS,"$osset")
++AC_DEFINE_UNQUOTED(COMMON_OS,"$osset",[define to the system OS])
+
+ dnl We use C99 types if at all possible
+ AC_CACHE_CHECK([for C99 integer types],c9x_ints,[
+@@ -87,13 +87,13 @@
+
+ dnl Arg, linux and bsd put their statfs function in different places
+ if test x"$HAVE_STATVFS" != x"yes"; then
+- AC_EGREP_HEADER(statfs,sys/vfs.h,[AC_DEFINE(HAVE_VFS_H)],[
+- AC_EGREP_HEADER(statfs,sys/mount.h,[AC_DEFINE(HAVE_MOUNT_H)],[AC_MSG_ERROR(failed: Need statvfs)])
++ AC_EGREP_HEADER(statfs,sys/vfs.h,[AC_DEFINE(HAVE_VFS_H, 1, [define if statfs is in sys/vfs.h])],[
++ AC_EGREP_HEADER(statfs,sys/mount.h,[AC_DEFINE(HAVE_MOUNT_H, 1, [define if statfs is in sys/mount.h])],[AC_MSG_ERROR(failed: Need statvfs)])
+ ])
+ fi
+
+ dnl We should use the real timegm function if we have it.
+-AC_CHECK_FUNC(timegm,AC_DEFINE(HAVE_TIMEGM))
++AC_CHECK_FUNC(timegm,AC_DEFINE(HAVE_TIMEGM, 1, [define if we have timegm]))
+ AC_SUBST(HAVE_TIMEGM)
+
+ dnl Check the sizes etc. of the architecture
+@@ -139,7 +139,7 @@
+ dnl HP-UX sux..
+ AC_MSG_CHECKING(for missing socklen_t)
+ AC_EGREP_HEADER(socklen_t, sys/socket.h,[AC_MSG_RESULT(no)],[
+- AC_DEFINE(NEED_SOCKLEN_T_DEFINE)
++ AC_DEFINE(NEED_SOCKLEN_T_DEFINE, 1, [define if we need to define socklen_t])
+ NEED_SOCKLEN_T_DEFINE=yes
+ AC_MSG_RESULT(missing.)])
+ AC_SUBST(NEED_SOCKLEN_T_DEFINE)
+@@ -174,7 +174,7 @@
+ ALL_LINGUAS="da de en_GB es fr hu it nl no_NO pl pt_BR ru sv zh_TW"
+ AM_GNU_GETTEXT(external)
+ if test x"$USE_NLS" = "xyes"; then
+- AC_DEFINE(USE_NLS)
++ AC_DEFINE(USE_NLS, 1, [define if we have native language support])
+ fi
+ AC_SUBST(USE_NLS)
+ AC_PATH_PROG(BASH, bash)
diff --git a/automake/automake-1.9.2/automake182-update-configscripts.patch b/automake/automake-1.9.2/automake182-update-configscripts.patch
index e69de29bb2..52992b3b26 100644
--- a/automake/automake-1.9.2/automake182-update-configscripts.patch
+++ b/automake/automake-1.9.2/automake182-update-configscripts.patch
@@ -0,0 +1,201 @@
+--- automake-1.8.2/lib/config.guess.old 2004-01-11 15:33:12.000000000 -0500
++++ automake-1.8.2/lib/config.guess 2004-04-24 22:42:44.000000000 -0400
+@@ -3,7 +3,7 @@
+ # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+ # 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
+
+-timestamp='2004-01-05'
++timestamp='2004-03-12'
+
+ # This file is free software; you can redistribute it and/or modify it
+ # under the terms of the GNU General Public License as published by
+@@ -197,12 +197,18 @@
+ # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
+ echo "${machine}-${os}${release}"
+ exit 0 ;;
++ amd64:OpenBSD:*:*)
++ echo x86_64-unknown-openbsd${UNAME_RELEASE}
++ exit 0 ;;
+ amiga:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ arc:OpenBSD:*:*)
+ echo mipsel-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
++ cats:OpenBSD:*:*)
++ echo arm-unknown-openbsd${UNAME_RELEASE}
++ exit 0 ;;
+ hp300:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+@@ -239,10 +245,24 @@
+ *:OpenBSD:*:*)
+ echo ${UNAME_MACHINE}-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
++ *:ekkoBSD:*:*)
++ echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE}
++ exit 0 ;;
++ macppc:MirBSD:*:*)
++ echo powerppc-unknown-mirbsd${UNAME_RELEASE}
++ exit 0 ;;
++ *:MirBSD:*:*)
++ echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE}
++ exit 0 ;;
+ alpha:OSF1:*:*)
+- if test $UNAME_RELEASE = "V4.0"; then
++ case $UNAME_RELEASE in
++ *4.0)
+ UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
+- fi
++ ;;
++ *5.*)
++ UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'`
++ ;;
++ esac
+ # According to Compaq, /usr/sbin/psrinfo has been available on
+ # OSF/1 and Tru64 systems produced since 1995. I hope that
+ # covers most systems running today. This code pipes the CPU
+@@ -280,11 +300,12 @@
+ "EV7.9 (21364A)")
+ UNAME_MACHINE="alphaev79" ;;
+ esac
++ # A Pn.n version is a patched version.
+ # A Vn.n version is a released version.
+ # A Tn.n version is a released field test version.
+ # A Xn.n version is an unreleased experimental baselevel.
+ # 1.2 uses "1.2" for uname -r.
+- echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[VTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
++ echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+ exit 0 ;;
+ Alpha*:OpenVMS:*:*)
+ echo alpha-hp-vms
+@@ -405,6 +426,9 @@
+ *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
+ echo m68k-unknown-mint${UNAME_RELEASE}
+ exit 0 ;;
++ m68k:machten:*:*)
++ echo m68k-apple-machten${UNAME_RELEASE}
++ exit 0 ;;
+ powerpc:machten:*:*)
+ echo powerpc-apple-machten${UNAME_RELEASE}
+ exit 0 ;;
+@@ -829,6 +853,9 @@
+ ia64:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit 0 ;;
++ m32r*:Linux:*:*)
++ echo ${UNAME_MACHINE}-unknown-linux-gnu
++ exit 0 ;;
+ m68*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit 0 ;;
+@@ -1230,8 +1257,8 @@
+ SEI:*:*:SEIUX)
+ echo mips-sei-seiux${UNAME_RELEASE}
+ exit 0 ;;
+- *:DRAGONFLY:*:*)
+- echo ${UNAME_MACHINE}-unknown-dragonfly${UNAME_RELEASE}
++ *:DragonFly:*:*)
++ echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`
+ exit 0 ;;
+ esac
+
+--- automake-1.8.2/lib/config.sub.old 2004-01-11 15:33:12.000000000 -0500
++++ automake-1.8.2/lib/config.sub 2004-04-24 22:42:44.000000000 -0400
+@@ -3,7 +3,7 @@
+ # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+ # 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
+
+-timestamp='2004-01-05'
++timestamp='2004-03-12'
+
+ # This file is (in principle) common to ALL GNU software.
+ # The presence of a machine in this file suggests that SOME GNU software
+@@ -237,7 +237,7 @@
+ | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
+ | i370 | i860 | i960 | ia64 \
+ | ip2k | iq2000 \
+- | m32r | m68000 | m68k | m88k | mcore \
++ | m32r | m32rle | m68000 | m68k | m88k | mcore \
+ | mips | mipsbe | mipseb | mipsel | mipsle \
+ | mips16 \
+ | mips64 | mips64el \
+@@ -262,7 +262,7 @@
+ | pyramid \
+ | sh | sh[1234] | sh[23]e | sh[34]eb | shbe | shle | sh[1234]le | sh3ele \
+ | sh64 | sh64le \
+- | sparc | sparc64 | sparc86x | sparclet | sparclite | sparcv9 | sparcv9b \
++ | sparc | sparc64 | sparc86x | sparclet | sparclite | sparcv8 | sparcv9 | sparcv9b \
+ | strongarm \
+ | tahoe | thumb | tic4x | tic80 | tron \
+ | v850 | v850e \
+@@ -308,7 +308,7 @@
+ | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \
+ | i*86-* | i860-* | i960-* | ia64-* \
+ | ip2k-* | iq2000-* \
+- | m32r-* \
++ | m32r-* | m32rle-* \
+ | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \
+ | m88110-* | m88k-* | mcore-* \
+ | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \
+@@ -336,7 +336,7 @@
+ | sh-* | sh[1234]-* | sh[23]e-* | sh[34]eb-* | shbe-* \
+ | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \
+ | sparc-* | sparc64-* | sparc86x-* | sparclet-* | sparclite-* \
+- | sparcv9-* | sparcv9b-* | strongarm-* | sv1-* | sx?-* \
++ | sparcv8-* | sparcv9-* | sparcv9b-* | strongarm-* | sv1-* | sx?-* \
+ | tahoe-* | thumb-* \
+ | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \
+ | tron-* \
+@@ -363,6 +363,9 @@
+ basic_machine=a29k-amd
+ os=-udi
+ ;;
++ abacus)
++ basic_machine=abacus-unknown
++ ;;
+ adobe68k)
+ basic_machine=m68010-adobe
+ os=-scout
+@@ -442,12 +445,20 @@
+ basic_machine=j90-cray
+ os=-unicos
+ ;;
++ cr16c)
++ basic_machine=cr16c-unknown
++ os=-elf
++ ;;
+ crds | unos)
+ basic_machine=m68k-crds
+ ;;
+ cris | cris-* | etrax*)
+ basic_machine=cris-axis
+ ;;
++ crx)
++ basic_machine=crx-unknown
++ os=-elf
++ ;;
+ da30 | da30-*)
+ basic_machine=m68k-da30
+ ;;
+@@ -1070,7 +1081,7 @@
+ sh64)
+ basic_machine=sh64-unknown
+ ;;
+- sparc | sparcv9 | sparcv9b)
++ sparc | sparcv8 | sparcv9 | sparcv9b)
+ basic_machine=sparc-sun
+ ;;
+ cydra)
+@@ -1143,8 +1154,9 @@
+ | -aos* \
+ | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
+ | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
+- | -hiux* | -386bsd* | -knetbsd* | -netbsd* | -openbsd* | -kfreebsd* | -freebsd* | -riscix* \
+- | -lynxos* | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \
++ | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* | -openbsd* \
++ | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \
++ | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \
+ | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \
+ | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \
+ | -chorusos* | -chorusrdb* \
diff --git a/automake/automake-1.9.3/automake182-update-configscripts.patch b/automake/automake-1.9.3/automake182-update-configscripts.patch
index e69de29bb2..52992b3b26 100644
--- a/automake/automake-1.9.3/automake182-update-configscripts.patch
+++ b/automake/automake-1.9.3/automake182-update-configscripts.patch
@@ -0,0 +1,201 @@
+--- automake-1.8.2/lib/config.guess.old 2004-01-11 15:33:12.000000000 -0500
++++ automake-1.8.2/lib/config.guess 2004-04-24 22:42:44.000000000 -0400
+@@ -3,7 +3,7 @@
+ # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+ # 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
+
+-timestamp='2004-01-05'
++timestamp='2004-03-12'
+
+ # This file is free software; you can redistribute it and/or modify it
+ # under the terms of the GNU General Public License as published by
+@@ -197,12 +197,18 @@
+ # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
+ echo "${machine}-${os}${release}"
+ exit 0 ;;
++ amd64:OpenBSD:*:*)
++ echo x86_64-unknown-openbsd${UNAME_RELEASE}
++ exit 0 ;;
+ amiga:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ arc:OpenBSD:*:*)
+ echo mipsel-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
++ cats:OpenBSD:*:*)
++ echo arm-unknown-openbsd${UNAME_RELEASE}
++ exit 0 ;;
+ hp300:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+@@ -239,10 +245,24 @@
+ *:OpenBSD:*:*)
+ echo ${UNAME_MACHINE}-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
++ *:ekkoBSD:*:*)
++ echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE}
++ exit 0 ;;
++ macppc:MirBSD:*:*)
++ echo powerppc-unknown-mirbsd${UNAME_RELEASE}
++ exit 0 ;;
++ *:MirBSD:*:*)
++ echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE}
++ exit 0 ;;
+ alpha:OSF1:*:*)
+- if test $UNAME_RELEASE = "V4.0"; then
++ case $UNAME_RELEASE in
++ *4.0)
+ UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
+- fi
++ ;;
++ *5.*)
++ UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'`
++ ;;
++ esac
+ # According to Compaq, /usr/sbin/psrinfo has been available on
+ # OSF/1 and Tru64 systems produced since 1995. I hope that
+ # covers most systems running today. This code pipes the CPU
+@@ -280,11 +300,12 @@
+ "EV7.9 (21364A)")
+ UNAME_MACHINE="alphaev79" ;;
+ esac
++ # A Pn.n version is a patched version.
+ # A Vn.n version is a released version.
+ # A Tn.n version is a released field test version.
+ # A Xn.n version is an unreleased experimental baselevel.
+ # 1.2 uses "1.2" for uname -r.
+- echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[VTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
++ echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+ exit 0 ;;
+ Alpha*:OpenVMS:*:*)
+ echo alpha-hp-vms
+@@ -405,6 +426,9 @@
+ *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
+ echo m68k-unknown-mint${UNAME_RELEASE}
+ exit 0 ;;
++ m68k:machten:*:*)
++ echo m68k-apple-machten${UNAME_RELEASE}
++ exit 0 ;;
+ powerpc:machten:*:*)
+ echo powerpc-apple-machten${UNAME_RELEASE}
+ exit 0 ;;
+@@ -829,6 +853,9 @@
+ ia64:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit 0 ;;
++ m32r*:Linux:*:*)
++ echo ${UNAME_MACHINE}-unknown-linux-gnu
++ exit 0 ;;
+ m68*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit 0 ;;
+@@ -1230,8 +1257,8 @@
+ SEI:*:*:SEIUX)
+ echo mips-sei-seiux${UNAME_RELEASE}
+ exit 0 ;;
+- *:DRAGONFLY:*:*)
+- echo ${UNAME_MACHINE}-unknown-dragonfly${UNAME_RELEASE}
++ *:DragonFly:*:*)
++ echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`
+ exit 0 ;;
+ esac
+
+--- automake-1.8.2/lib/config.sub.old 2004-01-11 15:33:12.000000000 -0500
++++ automake-1.8.2/lib/config.sub 2004-04-24 22:42:44.000000000 -0400
+@@ -3,7 +3,7 @@
+ # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+ # 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
+
+-timestamp='2004-01-05'
++timestamp='2004-03-12'
+
+ # This file is (in principle) common to ALL GNU software.
+ # The presence of a machine in this file suggests that SOME GNU software
+@@ -237,7 +237,7 @@
+ | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
+ | i370 | i860 | i960 | ia64 \
+ | ip2k | iq2000 \
+- | m32r | m68000 | m68k | m88k | mcore \
++ | m32r | m32rle | m68000 | m68k | m88k | mcore \
+ | mips | mipsbe | mipseb | mipsel | mipsle \
+ | mips16 \
+ | mips64 | mips64el \
+@@ -262,7 +262,7 @@
+ | pyramid \
+ | sh | sh[1234] | sh[23]e | sh[34]eb | shbe | shle | sh[1234]le | sh3ele \
+ | sh64 | sh64le \
+- | sparc | sparc64 | sparc86x | sparclet | sparclite | sparcv9 | sparcv9b \
++ | sparc | sparc64 | sparc86x | sparclet | sparclite | sparcv8 | sparcv9 | sparcv9b \
+ | strongarm \
+ | tahoe | thumb | tic4x | tic80 | tron \
+ | v850 | v850e \
+@@ -308,7 +308,7 @@
+ | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \
+ | i*86-* | i860-* | i960-* | ia64-* \
+ | ip2k-* | iq2000-* \
+- | m32r-* \
++ | m32r-* | m32rle-* \
+ | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \
+ | m88110-* | m88k-* | mcore-* \
+ | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \
+@@ -336,7 +336,7 @@
+ | sh-* | sh[1234]-* | sh[23]e-* | sh[34]eb-* | shbe-* \
+ | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \
+ | sparc-* | sparc64-* | sparc86x-* | sparclet-* | sparclite-* \
+- | sparcv9-* | sparcv9b-* | strongarm-* | sv1-* | sx?-* \
++ | sparcv8-* | sparcv9-* | sparcv9b-* | strongarm-* | sv1-* | sx?-* \
+ | tahoe-* | thumb-* \
+ | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \
+ | tron-* \
+@@ -363,6 +363,9 @@
+ basic_machine=a29k-amd
+ os=-udi
+ ;;
++ abacus)
++ basic_machine=abacus-unknown
++ ;;
+ adobe68k)
+ basic_machine=m68010-adobe
+ os=-scout
+@@ -442,12 +445,20 @@
+ basic_machine=j90-cray
+ os=-unicos
+ ;;
++ cr16c)
++ basic_machine=cr16c-unknown
++ os=-elf
++ ;;
+ crds | unos)
+ basic_machine=m68k-crds
+ ;;
+ cris | cris-* | etrax*)
+ basic_machine=cris-axis
+ ;;
++ crx)
++ basic_machine=crx-unknown
++ os=-elf
++ ;;
+ da30 | da30-*)
+ basic_machine=m68k-da30
+ ;;
+@@ -1070,7 +1081,7 @@
+ sh64)
+ basic_machine=sh64-unknown
+ ;;
+- sparc | sparcv9 | sparcv9b)
++ sparc | sparcv8 | sparcv9 | sparcv9b)
+ basic_machine=sparc-sun
+ ;;
+ cydra)
+@@ -1143,8 +1154,9 @@
+ | -aos* \
+ | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
+ | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
+- | -hiux* | -386bsd* | -knetbsd* | -netbsd* | -openbsd* | -kfreebsd* | -freebsd* | -riscix* \
+- | -lynxos* | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \
++ | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* | -openbsd* \
++ | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \
++ | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \
+ | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \
+ | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \
+ | -chorusos* | -chorusrdb* \
diff --git a/binutils/binutils-2.15.94.0.1/binutils-100_cflags_for_build.patch b/binutils/binutils-2.15.94.0.1/binutils-100_cflags_for_build.patch
index e69de29bb2..468808bf59 100644
--- a/binutils/binutils-2.15.94.0.1/binutils-100_cflags_for_build.patch
+++ b/binutils/binutils-2.15.94.0.1/binutils-100_cflags_for_build.patch
@@ -0,0 +1,91 @@
+
+#
+# Patch managed by http://www.holgerschurig.de/patcher.html
+#
+
+--- src/bfd/doc/Makefile.am~binutils-100_cflags_for_build.patch
++++ src/bfd/doc/Makefile.am
+@@ -55,10 +55,10 @@
+ MKDOC = chew$(EXEEXT_FOR_BUILD)
+
+ $(MKDOC): chew.o
+- $(CC_FOR_BUILD) -o $(MKDOC) chew.o $(CFLAGS) $(LOADLIBES) $(LDFLAGS)
++ $(CC_FOR_BUILD) -o $(MKDOC) chew.o $(CFLAGS_FOR_BUILD) $(LOADLIBES) $(LDFLAGS_FOR_BUILD)
+
+ chew.o: chew.c
+- $(CC_FOR_BUILD) -c -I.. -I$(srcdir)/.. -I$(srcdir)/../../include -I$(srcdir)/../../intl -I../../intl $(H_CFLAGS) $(CFLAGS) $(srcdir)/chew.c
++ $(CC_FOR_BUILD) -c -I.. -I$(srcdir)/.. -I$(srcdir)/../../include -I$(srcdir)/../../intl -I../../intl $(H_CFLAGS) $(CFLAGS_FOR_BUILD) $(srcdir)/chew.c
+
+ protos: libbfd.h libcoff.h bfd.h
+
+--- src/binutils/Makefile.am~binutils-100_cflags_for_build.patch
++++ src/binutils/Makefile.am
+@@ -219,20 +219,20 @@
+ ./sysinfo$(EXEEXT_FOR_BUILD) -d <$(srcdir)/sysroff.info >sysroff.h
+
+ sysinfo$(EXEEXT_FOR_BUILD): sysinfo.o syslex.o
+- $(CC_FOR_BUILD) $(CFLAGS) $(LDFLAGS) -o $@ sysinfo.o syslex.o
++ $(CC_FOR_BUILD) $(CFLAGS_FOR_BUILD) $(LDFLAGS_FOR_BUILD) -o $@ sysinfo.o syslex.o
+
+ syslex.o: syslex.c sysinfo.h
+ if [ -r syslex.c ]; then \
+- $(CC_FOR_BUILD) -c -I. $(CFLAGS) syslex.c ; \
++ $(CC_FOR_BUILD) -c -I. $(CFLAGS_FOR_BUILD) syslex.c ; \
+ else \
+- $(CC_FOR_BUILD) -c -I. -I$(srcdir) $(CFLAGS) $(srcdir)/syslex.c ;\
++ $(CC_FOR_BUILD) -c -I. -I$(srcdir) $(CFLAGS_FOR_BUILD) $(srcdir)/syslex.c ;\
+ fi
+
+ sysinfo.o: sysinfo.c
+ if [ -r sysinfo.c ]; then \
+- $(CC_FOR_BUILD) -c -I. $(CFLAGS) sysinfo.c ; \
++ $(CC_FOR_BUILD) -c -I. $(CFLAGS_FOR_BUILD) sysinfo.c ; \
+ else \
+- $(CC_FOR_BUILD) -c -I. $(CFLAGS) $(srcdir)/sysinfo.c ; \
++ $(CC_FOR_BUILD) -c -I. $(CFLAGS_FOR_BUILD) $(srcdir)/sysinfo.c ; \
+ fi
+
+ # We need these for parallel make.
+--- src/bfd/doc/Makefile.in~binutils-100_cflags_for_build.patch
++++ src/bfd/doc/Makefile.in
+@@ -470,10 +470,10 @@
+
+
+ $(MKDOC): chew.o
+- $(CC_FOR_BUILD) -o $(MKDOC) chew.o $(CFLAGS) $(LOADLIBES) $(LDFLAGS)
++ $(CC_FOR_BUILD) -o $(MKDOC) chew.o $(CFLAGS_FOR_BUILD) $(LOADLIBES) $(LDFLAGS_FOR_BUILD)
+
+ chew.o: chew.c
+- $(CC_FOR_BUILD) -c -I.. -I$(srcdir)/.. -I$(srcdir)/../../include -I$(srcdir)/../../intl -I../../intl $(H_CFLAGS) $(CFLAGS) $(srcdir)/chew.c
++ $(CC_FOR_BUILD) -c -I.. -I$(srcdir)/.. -I$(srcdir)/../../include -I$(srcdir)/../../intl -I../../intl $(H_CFLAGS) $(CFLAGS_FOR_BUILD) $(srcdir)/chew.c
+
+ protos: libbfd.h libcoff.h bfd.h
+
+--- src/binutils/Makefile.in~binutils-100_cflags_for_build.patch
++++ src/binutils/Makefile.in
+@@ -978,20 +978,20 @@
+ ./sysinfo$(EXEEXT_FOR_BUILD) -d <$(srcdir)/sysroff.info >sysroff.h
+
+ sysinfo$(EXEEXT_FOR_BUILD): sysinfo.o syslex.o
+- $(CC_FOR_BUILD) $(CFLAGS) $(LDFLAGS) -o $@ sysinfo.o syslex.o
++ $(CC_FOR_BUILD) $(CFLAGS_FOR_BUILD) $(LDFLAGS_FOR_BUILD) -o $@ sysinfo.o syslex.o
+
+ syslex.o: syslex.c sysinfo.h
+ if [ -r syslex.c ]; then \
+- $(CC_FOR_BUILD) -c -I. $(CFLAGS) syslex.c ; \
++ $(CC_FOR_BUILD) -c -I. $(CFLAGS_FOR_BUILD) syslex.c ; \
+ else \
+- $(CC_FOR_BUILD) -c -I. -I$(srcdir) $(CFLAGS) $(srcdir)/syslex.c ;\
++ $(CC_FOR_BUILD) -c -I. -I$(srcdir) $(CFLAGS_FOR_BUILD) $(srcdir)/syslex.c ;\
+ fi
+
+ sysinfo.o: sysinfo.c
+ if [ -r sysinfo.c ]; then \
+- $(CC_FOR_BUILD) -c -I. $(CFLAGS) sysinfo.c ; \
++ $(CC_FOR_BUILD) -c -I. $(CFLAGS_FOR_BUILD) sysinfo.c ; \
+ else \
+- $(CC_FOR_BUILD) -c -I. $(CFLAGS) $(srcdir)/sysinfo.c ; \
++ $(CC_FOR_BUILD) -c -I. $(CFLAGS_FOR_BUILD) $(srcdir)/sysinfo.c ; \
+ fi
+
+ # We need these for parallel make.
diff --git a/binutils/binutils-2.15.94.0.1/binutils-2.15.90.0.3-uclibc-200-build_modules.patch b/binutils/binutils-2.15.94.0.1/binutils-2.15.90.0.3-uclibc-200-build_modules.patch
index e69de29bb2..3560d2ca36 100644
--- a/binutils/binutils-2.15.94.0.1/binutils-2.15.90.0.3-uclibc-200-build_modules.patch
+++ b/binutils/binutils-2.15.94.0.1/binutils-2.15.90.0.3-uclibc-200-build_modules.patch
@@ -0,0 +1,33 @@
+
+#
+# Patch managed by http://www.holgerschurig.de/patcher.html
+#
+
+--- src/configure~binutils-2.15.90.0.3-uclibc-200-build_modules.patch
++++ src/configure
+@@ -940,6 +940,11 @@
+ build_configdirs=`echo ${build_libs} ${build_tools}`
+ fi
+
++case "$target" in
++ *-*-*-uclibc*)
++ build_modules=
++ ;;
++esac
+ ################################################################################
+
+ srcname="gnu development package"
+--- src/configure.in~binutils-2.15.90.0.3-uclibc-200-build_modules.patch
++++ src/configure.in
+@@ -189,6 +189,11 @@
+ build_configdirs=`echo ${build_libs} ${build_tools}`
+ fi
+
++case "$target" in
++ *-*-*-uclibc*)
++ build_modules=
++ ;;
++esac
+ ################################################################################
+
+ srcname="gnu development package"
diff --git a/binutils/binutils-2.15.94.0.1/binutils-2.15.90.0.3-uclibc-210-cflags.patch b/binutils/binutils-2.15.94.0.1/binutils-2.15.90.0.3-uclibc-210-cflags.patch
index e69de29bb2..40a71c602a 100644
--- a/binutils/binutils-2.15.94.0.1/binutils-2.15.90.0.3-uclibc-210-cflags.patch
+++ b/binutils/binutils-2.15.94.0.1/binutils-2.15.90.0.3-uclibc-210-cflags.patch
@@ -0,0 +1,30 @@
+--- binutils-2.15.90.0.3/bfd/doc/Makefile.am.cflags 2004-01-14 16:07:44.000000000 -0500
++++ binutils-2.15.90.0.3/bfd/doc/Makefile.am 2004-04-22 22:06:35.000000000 -0400
+@@ -55,10 +55,10 @@
+ MKDOC = chew$(EXEEXT_FOR_BUILD)
+
+ $(MKDOC): chew.o
+- $(CC_FOR_BUILD) -o $(MKDOC) chew.o $(CFLAGS) $(LOADLIBES) $(LDFLAGS)
++ $(CC_FOR_BUILD) -o $(MKDOC) chew.o $(CFLAGS_FOR_BUILD) $(LOADLIBES) $(LDFLAGS)
+
+ chew.o: chew.c
+- $(CC_FOR_BUILD) -c -I.. -I$(srcdir)/.. -I$(srcdir)/../../include -I$(srcdir)/../../intl -I../../intl $(H_CFLAGS) $(CFLAGS) $(srcdir)/chew.c
++ $(CC_FOR_BUILD) -c -I.. -I$(srcdir)/.. -I$(srcdir)/../../include -I$(srcdir)/../../intl -I../../intl $(H_CFLAGS) $(CFLAGS_FOR_BUILD) $(srcdir)/chew.c
+
+ protos: libbfd.h libcoff.h bfd.h
+
+--- binutils-2.15.90.0.3/bfd/doc/Makefile.in.cflags 2004-04-12 15:56:34.000000000 -0400
++++ binutils-2.15.90.0.3/bfd/doc/Makefile.in 2004-04-22 22:06:35.000000000 -0400
+@@ -472,10 +472,10 @@
+
+
+ $(MKDOC): chew.o
+- $(CC_FOR_BUILD) -o $(MKDOC) chew.o $(CFLAGS) $(LOADLIBES) $(LDFLAGS)
++ $(CC_FOR_BUILD) -o $(MKDOC) chew.o $(CFLAGS_FOR_BUILD) $(LOADLIBES) $(LDFLAGS)
+
+ chew.o: chew.c
+- $(CC_FOR_BUILD) -c -I.. -I$(srcdir)/.. -I$(srcdir)/../../include -I$(srcdir)/../../intl -I../../intl $(H_CFLAGS) $(CFLAGS) $(srcdir)/chew.c
++ $(CC_FOR_BUILD) -c -I.. -I$(srcdir)/.. -I$(srcdir)/../../include -I$(srcdir)/../../intl -I../../intl $(H_CFLAGS) $(CFLAGS_FOR_BUILD) $(srcdir)/chew.c
+
+ protos: libbfd.h libcoff.h bfd.h
+
diff --git a/binutils/binutils-2.15.94.0.1/binutils-2.15.91.0.1-uclibc-100-conf.patch b/binutils/binutils-2.15.94.0.1/binutils-2.15.91.0.1-uclibc-100-conf.patch
index e69de29bb2..d650cdbc91 100644
--- a/binutils/binutils-2.15.94.0.1/binutils-2.15.91.0.1-uclibc-100-conf.patch
+++ b/binutils/binutils-2.15.94.0.1/binutils-2.15.91.0.1-uclibc-100-conf.patch
@@ -0,0 +1,604 @@
+
+#
+# Patch managed by http://www.holgerschurig.de/patcher.html
+#
+
+--- binutils-2.15.94.0.1/bfd/config.bfd~binutils-2.15.91.0.1-uclibc-100-conf
++++ binutils-2.15.94.0.1/bfd/config.bfd
+@@ -140,7 +140,7 @@
+ targ_defvec=ecoffalpha_little_vec
+ targ_selvecs=bfd_elf64_alpha_vec
+ ;;
+- alpha*-*-linux-gnu* | alpha*-*-elf*)
++ alpha*-*-linux-gnu* | alpha*-*-linux-uclibc* | alpha*-*-elf*)
+ targ_defvec=bfd_elf64_alpha_vec
+ targ_selvecs=ecoffalpha_little_vec
+ ;;
+@@ -150,7 +150,7 @@
+ alpha*-*-*)
+ targ_defvec=ecoffalpha_little_vec
+ ;;
+- ia64*-*-freebsd* | ia64*-*-netbsd* | ia64*-*-linux-gnu* | ia64*-*-elf* | ia64*-*-kfreebsd*-gnu)
++ ia64*-*-freebsd* | ia64*-*-netbsd* | ia64*-*-linux-gnu* | ia64*-*-elf* | ia64*-*-linux-uclibc* | ia64*-*-kfreebsd*-gnu)
+ targ_defvec=bfd_elf64_ia64_little_vec
+ targ_selvecs="bfd_elf64_ia64_big_vec bfd_efi_app_ia64_vec"
+ ;;
+@@ -227,7 +227,7 @@
+ targ_defvec=bfd_elf32_littlearm_vec
+ targ_selvecs=bfd_elf32_bigarm_vec
+ ;;
+- armeb-*-elf | arm*b-*-linux-gnu*)
++ armeb-*-elf | arm*b-*-linux-gnu* | arm*b-*-linux-uclibc*)
+ targ_defvec=bfd_elf32_bigarm_vec
+ targ_selvecs=bfd_elf32_littlearm_vec
+ ;;
+@@ -237,7 +237,7 @@
+ ;;
+ arm-*-elf | arm-*-freebsd* | arm*-*-linux-gnu* | arm*-*-conix* | \
+ arm*-*-uclinux* | arm-*-kfreebsd*-gnu | arm-*-vxworks | \
+- arm*-*-eabi* )
++ arm*-*-eabi* | arm*-*-linux-uclibc* )
+ targ_defvec=bfd_elf32_littlearm_vec
+ targ_selvecs=bfd_elf32_bigarm_vec
+ ;;
+@@ -381,7 +381,7 @@
+ ;;
+
+ #ifdef BFD64
+- hppa*64*-*-linux-gnu*)
++ hppa*64*-*-linux-gnu* | hppa*64*-*-linux-uclibc*)
+ targ_defvec=bfd_elf64_hppa_linux_vec
+ targ_selvecs=bfd_elf64_hppa_vec
+ ;;
+@@ -392,7 +392,7 @@
+ ;;
+ #endif
+
+- hppa*-*-linux-gnu*)
++ hppa*-*-linux-gnu* | hppa*-*-linux-uclibc*)
+ targ_defvec=bfd_elf32_hppa_linux_vec
+ targ_selvecs=bfd_elf32_hppa_vec
+ ;;
+@@ -525,7 +525,7 @@
+ targ_selvecs=bfd_elf32_i386_vec
+ targ_underscore=yes
+ ;;
+- i[3-7]86-*-linux-gnu*)
++ i[3-7]86-*-linux-gnu* | i[3-7]86-*-linux-uclibc*)
+ targ_defvec=bfd_elf32_i386_vec
+ targ_selvecs="i386linux_vec bfd_efi_app_ia32_vec"
+ targ64_selvecs=bfd_elf64_x86_64_vec
+@@ -539,7 +539,7 @@
+ targ_defvec=bfd_elf64_x86_64_vec
+ targ_selvecs="bfd_elf32_i386_vec i386netbsd_vec i386coff_vec bfd_efi_app_ia32_vec"
+ ;;
+- x86_64-*-linux-gnu*)
++ x86_64-*-linux-gnu* | x86_64-*-linux-uclibc*)
+ targ_defvec=bfd_elf64_x86_64_vec
+ targ_selvecs="bfd_elf32_i386_vec i386linux_vec bfd_efi_app_ia32_vec"
+ ;;
+@@ -715,7 +715,7 @@
+ targ_selvecs=bfd_elf32_m68k_vec
+ targ_underscore=yes
+ ;;
+- m68*-*-linux-gnu*)
++ m68*-*-linux-gnu* | m68*-*-linux-uclibc*)
+ targ_defvec=bfd_elf32_m68k_vec
+ targ_selvecs=m68klinux_vec
+ ;;
+@@ -1001,7 +1001,8 @@
+ ;;
+ #endif
+ powerpc-*-*bsd* | powerpc-*-elf* | powerpc-*-sysv4* | powerpc-*-eabi* | \
+- powerpc-*-solaris2* | powerpc-*-linux-gnu* | powerpc-*-rtems* | \
++ powerpc-*-solaris2* | powerpc-*-linux-gnu* | powerpc-*-linux-uclibc* | \
++ powerpc-*-rtems* | \
+ powerpc-*-chorus* | powerpc-*-vxworks* | powerpc-*-windiss*)
+ targ_defvec=bfd_elf32_powerpc_vec
+ targ_selvecs="rs6000coff_vec bfd_elf32_powerpcle_vec ppcboot_vec"
+@@ -1038,8 +1039,8 @@
+ targ_selvecs="rs6000coff_vec bfd_elf32_powerpc_vec ppcboot_vec"
+ ;;
+ powerpcle-*-elf* | powerpcle-*-sysv4* | powerpcle-*-eabi* | \
+- powerpcle-*-solaris2* | powerpcle-*-linux-gnu* | powerpcle-*-vxworks* |\
+- powerpcle-*-rtems*)
++ powerpcle-*-solaris2* | powerpcle-*-linux-gnu* | powerpcle-*-linux-uclibc* |\
++ powerpcle-*-vxworks* | powerpcle-*-rtems*)
+ targ_defvec=bfd_elf32_powerpcle_vec
+ targ_selvecs="rs6000coff_vec bfd_elf32_powerpc_vec ppcboot_vec"
+ targ64_selvecs="bfd_elf64_powerpc_vec bfd_elf64_powerpcle_vec"
+@@ -1206,7 +1207,7 @@
+ targ_selvecs="bfd_elf32_sparc_vec sunos_big_vec"
+ targ_underscore=yes
+ ;;
+- sparc-*-linux-gnu*)
++ sparc-*-linux-gnu* | sparc-*-linux-uclibc*)
+ targ_defvec=bfd_elf32_sparc_vec
+ targ_selvecs="sparclinux_vec bfd_elf64_sparc_vec sunos_big_vec"
+ ;;
+@@ -1253,7 +1254,7 @@
+ targ_defvec=sunos_big_vec
+ targ_underscore=yes
+ ;;
+- sparc64-*-linux-gnu*)
++ sparc64-*-linux-gnu* | sparc64-*-linux-uclibc*)
+ targ_defvec=bfd_elf64_sparc_vec
+ targ_selvecs="bfd_elf32_sparc_vec sparclinux_vec sunos_big_vec"
+ ;;
+--- binutils-2.15.94.0.1/bfd/configure~binutils-2.15.91.0.1-uclibc-100-conf
++++ binutils-2.15.94.0.1/bfd/configure
+@@ -3583,6 +3583,11 @@
+ lt_cv_file_magic_test_file=`echo /lib/libc.so* /lib/libc-*.so`
+ ;;
+
++linux-uclibc*)
++ lt_cv_deplibs_check_method=pass_all
++ lt_cv_file_magic_test_file=`echo /lib/libuClibc-*.so`
++ ;;
++
+ netbsd* | knetbsd*-gnu)
+ if echo __ELF__ | $CC -E - | grep __ELF__ > /dev/null; then
+ lt_cv_deplibs_check_method='match_pattern /lib[^/\.]+\.so\.[0-9]+\.[0-9]+$'
+@@ -9914,7 +9919,7 @@
+ alpha*-*-freebsd* | alpha*-*-kfreebsd*-gnu)
+ COREFILE=''
+ ;;
+- alpha*-*-linux-gnu*)
++ alpha*-*-linux-gnu* | alpha*-*-linux-uclibc*)
+ COREFILE=trad-core.lo
+ TRAD_HEADER='"hosts/alphalinux.h"'
+ ;;
+@@ -9978,7 +9983,7 @@
+ COREFILE=trad-core.lo
+ TRAD_HEADER='"hosts/i386mach3.h"'
+ ;;
+- i[3-7]86-*-linux-gnu*)
++ i[3-7]86-*-linux-gnu* | i[3-7]86-*-linux-uclibc*)
+ COREFILE=trad-core.lo
+ TRAD_HEADER='"hosts/i386linux.h"'
+ ;;
+@@ -10016,7 +10021,7 @@
+ COREFILE=trad-core.lo
+ TRAD_HEADER='"hosts/hp300bsd.h"'
+ ;;
+- m68*-*-linux-gnu*)
++ m68*-*-linux-gnu* | m68*-*-linux-uclibc*)
+ COREFILE=trad-core.lo
+ TRAD_HEADER='"hosts/m68klinux.h"'
+ ;;
+--- binutils-2.15.94.0.1/bfd/configure.in~binutils-2.15.91.0.1-uclibc-100-conf
++++ binutils-2.15.94.0.1/bfd/configure.in
+@@ -163,7 +163,7 @@
+ alpha*-*-freebsd* | alpha*-*-kfreebsd*-gnu)
+ COREFILE=''
+ ;;
+- alpha*-*-linux-gnu*)
++ alpha*-*-linux-gnu* | alpha*-*-linux-uclibc*)
+ COREFILE=trad-core.lo
+ TRAD_HEADER='"hosts/alphalinux.h"'
+ ;;
+@@ -248,7 +248,7 @@
+ TRAD_HEADER='"hosts/i386mach3.h"'
+ ;;
+ changequote(,)dnl
+- i[3-7]86-*-linux-gnu*)
++ i[3-7]86-*-linux-gnu* | i[3-7]86-*-linux-uclibc*)
+ changequote([,])dnl
+ COREFILE=trad-core.lo
+ TRAD_HEADER='"hosts/i386linux.h"'
+@@ -289,7 +289,7 @@
+ COREFILE=trad-core.lo
+ TRAD_HEADER='"hosts/hp300bsd.h"'
+ ;;
+- m68*-*-linux-gnu*)
++ m68*-*-linux-gnu* | m68*-*-linux-uclibc*)
+ COREFILE=trad-core.lo
+ TRAD_HEADER='"hosts/m68klinux.h"'
+ ;;
+--- binutils-2.15.94.0.1/gas/configure~binutils-2.15.91.0.1-uclibc-100-conf
++++ binutils-2.15.94.0.1/gas/configure
+@@ -3420,6 +3420,11 @@
+ lt_cv_file_magic_test_file=`echo /lib/libc.so* /lib/libc-*.so`
+ ;;
+
++linux-uclibc*)
++ lt_cv_deplibs_check_method=pass_all
++ lt_cv_file_magic_test_file=`echo /lib/libuClibc-*.so`
++ ;;
++
+ netbsd* | knetbsd*-gnu)
+ if echo __ELF__ | $CC -E - | grep __ELF__ > /dev/null; then
+ lt_cv_deplibs_check_method='match_pattern /lib[^/\.]+\.so\.[0-9]+\.[0-9]+$'
+@@ -4256,6 +4261,7 @@
+ alpha*-*-osf*) fmt=ecoff ;;
+ alpha*-*-linuxecoff*) fmt=ecoff ;;
+ alpha*-*-linux-gnu*) fmt=elf em=linux ;;
++ alpha*-*-linux-uclibc*) fmt=elf em=linux ;;
+ alpha*-*-netbsd*) fmt=elf em=nbsd ;;
+ alpha*-*-openbsd*) fmt=elf em=obsd ;;
+
+@@ -4271,6 +4277,7 @@
+ arm*-*-conix*) fmt=elf ;;
+ arm-*-linux*aout*) fmt=aout em=linux ;;
+ arm*-*-linux-gnu*) fmt=elf em=linux ;;
++ arm*-*-linux-uclibc*) fmt=elf em=linux ;;
+ arm*-*-uclinux*) fmt=elf em=linux ;;
+ arm-*-netbsdelf*) fmt=elf em=nbsd ;;
+ arm-*-*n*bsd*) fmt=aout em=nbsd ;;
+@@ -4284,6 +4291,7 @@
+
+ cris-*-linux-gnu* | crisv32-*-linux-gnu*)
+ fmt=multi bfd_gas=yes em=linux ;;
++ cris-*-linux-uclibc*) fmt=multi bfd_gas=yes em=linux ;;
+ cris-*-* | crisv32-*-*) fmt=multi bfd_gas=yes ;;
+
+ crx-*-elf*) fmt=elf ;;
+@@ -4343,7 +4351,9 @@
+ i386-*-linux*oldld) fmt=aout em=linux ;;
+ i386-*-linux*coff*) fmt=coff em=linux ;;
+ i386-*-linux-gnu*) fmt=elf em=linux ;;
++ i386-*-linux-uclibc*) fmt=elf em=linux ;;
+ x86_64-*-linux-gnu*) fmt=elf em=linux ;;
++ x86_64-*-linux-uclibc*) fmt=elf em=linux ;;
+ i386-*-lynxos*) fmt=elf em=lynx bfd_gas=yes ;;
+ i386-*-sysv[45]*) fmt=elf ;;
+ i386-*-solaris*) fmt=elf ;;
+@@ -4403,6 +4413,7 @@
+ ia64-*-elf*) fmt=elf ;;
+ ia64-*-aix*) fmt=elf em=ia64aix ;;
+ ia64-*-linux-gnu*) fmt=elf em=linux ;;
++ ia64-*-linux-uclibc*) fmt=elf em=linux ;;
+ ia64-*-hpux*) fmt=elf em=hpux ;;
+ ia64-*-netbsd*) fmt=elf em=nbsd ;;
+
+@@ -4430,6 +4441,7 @@
+ m68k-*-hpux*) fmt=hp300 em=hp300 ;;
+ m68k-*-linux*aout*) fmt=aout em=linux ;;
+ m68k-*-linux-gnu*) fmt=elf em=linux ;;
++ m68k-*-linux-uclibc*) fmt=elf em=linux ;;
+ m68k-*-uclinux*) fmt=elf ;;
+ m68k-*-gnu*) fmt=elf ;;
+ m68k-*-lynxos*) fmt=coff em=lynx ;;
+@@ -4504,7 +4516,7 @@
+ ppc-*-beos*) fmt=coff ;;
+ ppc-*-*n*bsd* | ppc-*-elf*) fmt=elf ;;
+ ppc-*-eabi* | ppc-*-sysv4*) fmt=elf ;;
+- ppc-*-linux-gnu*) fmt=elf em=linux
++ ppc-*-linux-uclibc* | ppc-*-linux-gnu*) fmt=elf em=linux
+ case "$endian" in
+ big) ;;
+ *) { { echo "$as_me:$LINENO: error: GNU/Linux must be configured big endian" >&5
+@@ -4531,7 +4543,9 @@
+ ppc-*-lynxos*) fmt=elf em=lynx bfd_gas=yes ;;
+
+ s390x-*-linux-gnu*) fmt=elf em=linux ;;
++ s390x-*-linux-uclibc*) fmt=elf em=linux ;;
+ s390-*-linux-gnu*) fmt=elf em=linux ;;
++ s390-*-linux-uclibc*) fmt=elf em=linux ;;
+
+ sh*-*-linux*) fmt=elf em=linux
+ case ${cpu} in
+@@ -4566,6 +4580,7 @@
+ sparc-*-coff) fmt=coff ;;
+ sparc-*-linux*aout*) fmt=aout em=linux ;;
+ sparc-*-linux-gnu*) fmt=elf em=linux ;;
++ sparc-*-linux-uclibc*) fmt=elf em=linux ;;
+ sparc-*-lynxos*) fmt=coff em=lynx ;;
+ sparc-fujitsu-none) fmt=aout ;;
+ sparc-*-elf) fmt=elf ;;
+--- binutils-2.15.94.0.1/gas/configure.in~binutils-2.15.91.0.1-uclibc-100-conf
++++ binutils-2.15.94.0.1/gas/configure.in
+@@ -202,6 +202,7 @@
+ alpha*-*-osf*) fmt=ecoff ;;
+ alpha*-*-linuxecoff*) fmt=ecoff ;;
+ alpha*-*-linux-gnu*) fmt=elf em=linux ;;
++ alpha*-*-linux-uclibc*) fmt=elf em=linux ;;
+ alpha*-*-netbsd*) fmt=elf em=nbsd ;;
+ alpha*-*-openbsd*) fmt=elf em=obsd ;;
+
+@@ -217,6 +218,7 @@
+ arm*-*-conix*) fmt=elf ;;
+ arm-*-linux*aout*) fmt=aout em=linux ;;
+ arm*-*-linux-gnu*) fmt=elf em=linux ;;
++ arm*-*-linux-uclibc*) fmt=elf em=linux ;;
+ arm*-*-uclinux*) fmt=elf em=linux ;;
+ arm-*-netbsdelf*) fmt=elf em=nbsd ;;
+ arm-*-*n*bsd*) fmt=aout em=nbsd ;;
+@@ -230,6 +232,7 @@
+
+ cris-*-linux-gnu* | crisv32-*-linux-gnu*)
+ fmt=multi bfd_gas=yes em=linux ;;
++ cris-*-linux-uclibc*) fmt=multi bfd_gas=yes em=linux ;;
+ cris-*-* | crisv32-*-*) fmt=multi bfd_gas=yes ;;
+
+ crx-*-elf*) fmt=elf ;;
+@@ -289,7 +292,9 @@
+ i386-*-linux*oldld) fmt=aout em=linux ;;
+ i386-*-linux*coff*) fmt=coff em=linux ;;
+ i386-*-linux-gnu*) fmt=elf em=linux ;;
++ i386-*-linux-uclibc*) fmt=elf em=linux ;;
+ x86_64-*-linux-gnu*) fmt=elf em=linux ;;
++ x86_64-*-linux-uclibc*) fmt=elf em=linux ;;
+ i386-*-lynxos*) fmt=elf em=lynx bfd_gas=yes ;;
+ changequote(,)dnl
+ i386-*-sysv[45]*) fmt=elf ;;
+@@ -342,6 +347,7 @@
+ ia64-*-elf*) fmt=elf ;;
+ ia64-*-aix*) fmt=elf em=ia64aix ;;
+ ia64-*-linux-gnu*) fmt=elf em=linux ;;
++ ia64-*-linux-uclibc*) fmt=elf em=linux ;;
+ ia64-*-hpux*) fmt=elf em=hpux ;;
+ ia64-*-netbsd*) fmt=elf em=nbsd ;;
+
+@@ -369,6 +375,7 @@
+ m68k-*-hpux*) fmt=hp300 em=hp300 ;;
+ m68k-*-linux*aout*) fmt=aout em=linux ;;
+ m68k-*-linux-gnu*) fmt=elf em=linux ;;
++ m68k-*-linux-uclibc*) fmt=elf em=linux ;;
+ m68k-*-uclinux*) fmt=elf ;;
+ m68k-*-gnu*) fmt=elf ;;
+ m68k-*-lynxos*) fmt=coff em=lynx ;;
+@@ -440,7 +447,7 @@
+ ppc-*-beos*) fmt=coff ;;
+ ppc-*-*n*bsd* | ppc-*-elf*) fmt=elf ;;
+ ppc-*-eabi* | ppc-*-sysv4*) fmt=elf ;;
+- ppc-*-linux-gnu*) fmt=elf em=linux
++ ppc-*-linux-uclibc* | ppc-*-linux-gnu*) fmt=elf em=linux
+ case "$endian" in
+ big) ;;
+ *) AC_MSG_ERROR(GNU/Linux must be configured big endian) ;;
+@@ -460,7 +467,9 @@
+ ppc-*-lynxos*) fmt=elf em=lynx bfd_gas=yes ;;
+
+ s390x-*-linux-gnu*) fmt=elf em=linux ;;
++ s390x-*-linux-uclibc*) fmt=elf em=linux ;;
+ s390-*-linux-gnu*) fmt=elf em=linux ;;
++ s390-*-linux-uclibc*) fmt=elf em=linux ;;
+
+ sh*-*-linux*) fmt=elf em=linux
+ case ${cpu} in
+@@ -491,6 +500,7 @@
+ sparc-*-coff) fmt=coff ;;
+ sparc-*-linux*aout*) fmt=aout em=linux ;;
+ sparc-*-linux-gnu*) fmt=elf em=linux ;;
++ sparc-*-linux-uclibc*) fmt=elf em=linux ;;
+ sparc-*-lynxos*) fmt=coff em=lynx ;;
+ sparc-fujitsu-none) fmt=aout ;;
+ sparc-*-elf) fmt=elf ;;
+--- binutils-2.15.94.0.1/ld/configure~binutils-2.15.91.0.1-uclibc-100-conf
++++ binutils-2.15.94.0.1/ld/configure
+@@ -1579,6 +1579,11 @@
+ lt_cv_file_magic_test_file=`echo /lib/libc.so* /lib/libc-*.so`
+ ;;
+
++linux-uclibc*)
++ lt_cv_deplibs_check_method=pass_all
++ lt_cv_file_magic_test_file=`echo /lib/libuClibc-*.so`
++ ;;
++
+ netbsd* | knetbsd*-gnu)
+ if echo __ELF__ | $CC -E - | grep __ELF__ > /dev/null; then
+ lt_cv_deplibs_check_method='match_pattern /lib[^/\.]+\.so\.[0-9]+\.[0-9]+$'
+--- binutils-2.15.94.0.1/ld/configure.tgt~binutils-2.15.91.0.1-uclibc-100-conf
++++ binutils-2.15.94.0.1/ld/configure.tgt
+@@ -32,6 +32,7 @@
+ targ_extra_libpath=$targ_extra_emuls ;;
+ cris-*-linux-gnu* | cris-*-linux-gnu*)
+ targ_emul=crislinux ;;
++cris-*-linux-uclibc*) targ_emul=crislinux ;;
+ cris-*-* | crisv32-*-*) targ_emul=criself
+ targ_extra_emuls="crisaout crislinux"
+ targ_extra_libpath=$targ_extra_emuls ;;
+@@ -62,14 +63,16 @@
+ tdir_elf32_sparc=`echo ${targ_alias} | sed -e 's/aout//'`
+ tdir_sun4=sparc-sun-sunos4
+ ;;
+-sparc64-*-linux-gnu*) targ_emul=elf64_sparc
++sparc64-*-linux-gnu* | sparc64-*-linux-uclibc*) \
++ targ_emul=elf64_sparc
+ targ_extra_emuls="elf32_sparc sparclinux sun4"
+ targ_extra_libpath=elf32_sparc
+ tdir_elf32_sparc=`echo ${targ_alias} | sed -e 's/64//'`
+ tdir_sparclinux=${tdir_elf32_sparc}aout
+ tdir_sun4=sparc-sun-sunos4
+ ;;
+-sparc*-*-linux-gnu*) targ_emul=elf32_sparc
++sparc*-*-linux-gnu* | sparc*-*-linux-uclibc*) \
++ targ_emul=elf32_sparc
+ targ_extra_emuls="sparclinux elf64_sparc sun4"
+ targ_extra_libpath=elf64_sparc
+ tdir_sparclinux=${targ_alias}aout
+@@ -132,7 +135,7 @@
+ m68*-apple-aux*) targ_emul=m68kaux ;;
+ maxq-*-coff) targ_emul=maxqcoff;;
+ *-tandem-none) targ_emul=st2000 ;;
+-i370-*-elf* | i370-*-linux-gnu*) targ_emul=elf32i370 ;;
++i370-*-elf* | i370-*-linux-gnu* | i370-*-linux-uclibc*) targ_emul=elf32i370 ;;
+ i[3-7]86-*-nto-qnx*) targ_emul=i386nto ;;
+ i[3-7]86-*-vsta) targ_emul=vsta ;;
+ i[3-7]86-go32-rtems*) targ_emul=i386go32 ;;
+@@ -156,14 +159,16 @@
+ tdir_elf_i386=`echo ${targ_alias} | sed -e 's/aout//'`
+ ;;
+ i[3-7]86-*-linux*oldld) targ_emul=i386linux; targ_extra_emuls=elf_i386 ;;
+-i[3-7]86-*-linux-gnu*) targ_emul=elf_i386
++i[3-7]86-*-linux-gnu* | i[3-7]86-*-linux-uclibc*) \
++ targ_emul=elf_i386
+ targ_extra_emuls=i386linux
+ if test x${want64} = xtrue; then
+ targ_extra_emuls="$targ_extra_emuls elf_x86_64"
+ fi
+ tdir_i386linux=${targ_alias}aout
+ ;;
+-x86_64-*-linux-gnu*) targ_emul=elf_x86_64
++x86_64-*-linux-gnu* | x86_64-*-linux-uclibc*) \
++ targ_emul=elf_x86_64
+ targ_extra_emuls="elf_i386 i386linux"
+ targ_extra_libpath=elf_i386
+ tdir_i386linux=`echo ${targ_alias}aout | sed -e 's/x86_64/i386/'`
+@@ -263,11 +268,14 @@
+ arm-*-kaos*) targ_emul=armelf ;;
+ arm9e-*-elf) targ_emul=armelf ;;
+ arm*b-*-linux-gnu*) targ_emul=armelfb_linux; targ_extra_emuls=armelfb ;;
++arm*b-*-linux-uclibc*) targ_emul=armelfb_linux; targ_extra_emuls=armelfb ;;
+ arm*-*-linux-gnueabi) targ_emul=armelf_linux_eabi ;;
+ arm*-*-linux-gnu*) targ_emul=armelf_linux; targ_extra_emuls=armelf ;;
++arm*-*-linux-uclibc*) targ_emul=armelf_linux; targ_extra_emuls=armelf ;;
+ arm*-*-uclinux*) targ_emul=armelf_linux; targ_extra_emuls=armelf ;;
+ arm*-*-conix*) targ_emul=armelf ;;
+-thumb-*-linux-gnu* | thumb-*-uclinux*) targ_emul=armelf_linux; targ_extra_emuls=armelf ;;
++thumb-*-linux-gnu* | thumb-*-linux-uclibc* | thumb-*-uclinux*)
++ targ_emul=armelf_linux; targ_extra_emuls=armelf ;;
+ strongarm-*-coff) targ_emul=armcoff ;;
+ strongarm-*-elf) targ_emul=armelf ;;
+ strongarm-*-kaos*) targ_emul=armelf ;;
+@@ -371,7 +379,8 @@
+ targ_extra_emuls=m68kelf
+ tdir_m68kelf=`echo ${targ_alias} | sed -e 's/aout//'`
+ ;;
+-m68k-*-linux-gnu*) targ_emul=m68kelf
++m68k-*-linux-gnu* | m68k-*-linux-uclibc*) \
++ targ_emul=m68kelf
+ targ_extra_emuls=m68klinux
+ tdir_m68klinux=`echo ${targ_alias} | sed -e 's/linux/linuxaout/'`
+ ;;
+@@ -388,9 +397,9 @@
+ m68*-*-psos*) targ_emul=m68kpsos ;;
+ m68*-*-rtemscoff*) targ_emul=m68kcoff ;;
+ m68*-*-rtems*) targ_emul=m68kelf ;;
+-hppa*64*-*-linux-gnu*) targ_emul=hppa64linux ;;
++hppa*64*-*-linux-gnu* | hppa*64*-*-linux-uclibc*) targ_emul=hppa64linux ;;
+ hppa*64*-*) targ_emul=elf64hppa ;;
+-hppa*-*-linux-gnu*) targ_emul=hppalinux ;;
++hppa*-*-linux-gnu* | hppa*-*-linux-uclibc*) targ_emul=hppalinux ;;
+ hppa*-*-*elf*) targ_emul=hppaelf ;;
+ hppa*-*-lites*) targ_emul=hppaelf ;;
+ hppa*-*-netbsd*) targ_emul=hppanbsd ;;
+@@ -436,16 +445,20 @@
+ mips*-*-vxworks*) targ_emul=elf32ebmip
+ targ_extra_emuls="elf32elmip" ;;
+ mips*-*-windiss) targ_emul=elf32mipswindiss ;;
+-mips64*el-*-linux-gnu*) targ_emul=elf32ltsmipn32
++mips64*el-*-linux-gnu* | mips64*el-*-linux-uclibc*)
++ targ_emul=elf32ltsmipn32
+ targ_extra_emuls="elf32btsmipn32 elf32ltsmip elf32btsmip elf64ltsmip elf64btsmip"
+ ;;
+-mips64*-*-linux-gnu*) targ_emul=elf32btsmipn32
++mips64*-*-linux-gnu* | mips64*-*-linux-uclibc*)
++ targ_emul=elf32btsmipn32
+ targ_extra_emuls="elf32ltsmipn32 elf32btsmip elf32ltsmip elf64btsmip elf64ltsmip"
+ ;;
+-mips*el-*-linux-gnu*) targ_emul=elf32ltsmip
++mips*el-*-linux-gnu* | mips*el-*-linux-uclibc*)
++ targ_emul=elf32ltsmip
+ targ_extra_emuls="elf32btsmip elf32ltsmipn32 elf64ltsmip elf32btsmipn32 elf64btsmip"
+ ;;
+-mips*-*-linux-gnu*) targ_emul=elf32btsmip
++mips*-*-linux-gnu* | mips*-*-linux-uclibc*)
++ targ_emul=elf32btsmip
+ targ_extra_emuls="elf32ltsmip elf32btsmipn32 elf64btsmip elf32ltsmipn32 elf64ltsmip"
+ ;;
+ mips*-*-lnews*) targ_emul=mipslnews ;;
+@@ -468,6 +481,10 @@
+ alpha*-*-linux-gnu*) targ_emul=elf64alpha targ_extra_emuls=alpha
+ tdir_alpha=`echo ${targ_alias} | sed -e 's/linux/linuxecoff/'`
+ ;;
++alpha*-*-linux-uclibc*) targ_emul=elf64alpha targ_extra_emuls=alpha
++ # The following needs to be checked...
++ tdir_alpha=`echo ${targ_alias} | sed -e 's/linux/linuxecoff/'`
++ ;;
+ alpha*-*-osf*) targ_emul=alpha ;;
+ alpha*-*-gnu*) targ_emul=elf64alpha ;;
+ alpha*-*-netware*) targ_emul=alpha ;;
+--- binutils-2.15.94.0.1/configure~binutils-2.15.91.0.1-uclibc-100-conf
++++ binutils-2.15.94.0.1/configure
+@@ -1341,6 +1341,18 @@
+ i[3456789]86-*-coff | i[3456789]86-*-elf)
+ noconfigdirs="$noconfigdirs ${libgcj}"
+ ;;
++ i[3456789]86-*-linux-uclibc)
++ # This section makes it possible to build newlib natively on linux.
++ # If we are using a cross compiler then don't configure newlib.
++ if test x${is_cross_compiler} != xno ; then
++ noconfigdirs="$noconfigdirs target-newlib"
++ fi
++ noconfigdirs="$noconfigdirs target-libgloss"
++ # If we are not using a cross compiler, do configure newlib.
++ # Note however, that newlib will only be configured in this situation
++ # if the --with-newlib option has been given, because otherwise
++ # 'target-newlib' will appear in skipdirs.
++ ;;
+ i[3456789]86-*-linux*)
+ # The GCC port for glibc1 has no MD_FALLBACK_FRAME_STATE_FOR, so let's
+ # not build java stuff by default.
+--- binutils-2.15.94.0.1/configure.in~binutils-2.15.91.0.1-uclibc-100-conf
++++ binutils-2.15.94.0.1/configure.in
+@@ -563,6 +563,19 @@
+ i[[3456789]]86-*-coff | i[[3456789]]86-*-elf)
+ noconfigdirs="$noconfigdirs ${libgcj}"
+ ;;
++ i[3456789]86-*-linux-uclibc)
++ # This section makes it possible to build newlib natively on linux.
++ # If we are using a cross compiler then don't configure newlib.
++ if test x${is_cross_compiler} != xno ; then
++ noconfigdirs="$noconfigdirs target-newlib"
++ fi
++ noconfigdirs="$noconfigdirs target-libgloss"
++ build_modules=
++ # If we are not using a cross compiler, do configure newlib.
++ # Note however, that newlib will only be configured in this situation
++ # if the --with-newlib option has been given, because otherwise
++ # 'target-newlib' will appear in skipdirs.
++ ;;
+ i[[3456789]]86-*-linux*)
+ # The GCC port for glibc1 has no MD_FALLBACK_FRAME_STATE_FOR, so let's
+ # not build java stuff by default.
+--- binutils-2.15.94.0.1/libtool.m4~binutils-2.15.91.0.1-uclibc-100-conf
++++ binutils-2.15.94.0.1/libtool.m4
+@@ -645,6 +645,11 @@
+ lt_cv_file_magic_test_file=`echo /lib/libc.so* /lib/libc-*.so`
+ ;;
+
++linux-uclibc*)
++ lt_cv_deplibs_check_method=pass_all
++ lt_cv_file_magic_test_file=`echo /lib/libuClibc-*.so`
++ ;;
++
+ netbsd* | knetbsd*-gnu)
+ if echo __ELF__ | $CC -E - | grep __ELF__ > /dev/null; then
+ [lt_cv_deplibs_check_method='match_pattern /lib[^/\.]+\.so\.[0-9]+\.[0-9]+$']
+--- binutils-2.15.94.0.1/ltconfig~binutils-2.15.91.0.1-uclibc-100-conf
++++ binutils-2.15.94.0.1/ltconfig
+@@ -603,6 +603,7 @@
+ # Transform linux* to *-*-linux-gnu*, to support old configure scripts.
+ case $host_os in
+ linux-gnu*) ;;
++linux-uclibc*) ;;
+ linux*) host=`echo $host | sed 's/^\(.*-.*-linux\)\(.*\)$/\1-gnu\2/'`
+ esac
+
+@@ -1270,6 +1271,24 @@
+ dynamic_linker='GNU/Linux ld.so'
+ ;;
+
++linux-uclibc*)
++ version_type=linux
++ need_lib_prefix=no
++ need_version=no
++ library_names_spec='${libname}${release}.so$versuffix ${libname}${release}.so$major $libname.so'
++ soname_spec='${libname}${release}.so$major'
++ finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir'
++ shlibpath_var=LD_LIBRARY_PATH
++ shlibpath_overrides_runpath=no
++ # This implies no fast_install, which is unacceptable.
++ # Some rework will be needed to allow for fast_install
++ # before this can be enabled.
++ # Note: copied from linux-gnu, and may not be appropriate.
++ hardcode_into_libs=yes
++ # Assume using the uClibc dynamic linker.
++ dynamic_linker="uClibc ld.so"
++ ;;
++
+ netbsd*)
+ need_lib_prefix=no
+ need_version=no
diff --git a/bluez-utils/bluez-utils-dbus-2.11/dbus.patch b/bluez-utils/bluez-utils-dbus-2.11/dbus.patch
index e69de29bb2..b360c4dca2 100644
--- a/bluez-utils/bluez-utils-dbus-2.11/dbus.patch
+++ b/bluez-utils/bluez-utils-dbus-2.11/dbus.patch
@@ -0,0 +1,52 @@
+--- bluez-utils-2.10/acinclude.m4.old 2004-10-19 22:34:47.000000000 +0100
++++ bluez-utils-2.10/acinclude.m4 2004-10-19 22:50:47.000000000 +0100
+@@ -124,45 +124,14 @@
+ ])
+
+ AC_DEFUN([AC_PATH_DBUS], [
+- dbus_prefix=${prefix}
+-
+- AC_ARG_WITH(dbus, AC_HELP_STRING([--with-dbus=DIR], [D-BUS library is installed in DIR]), [
+- if (test "${withval}" != "yes"); then
+- dbus_prefix=${withval}
+- fi
+- ])
+-
+- ac_save_CPPFLAGS=$CPPFLAGS
+- ac_save_LDFLAGS=$LDFLAGS
+-
+- DBUS_CFLAGS="-DDBUS_API_SUBJECT_TO_CHANGE"
+- test -d "${dbus_prefix}/include/dbus-1.0" && DBUS_CFLAGS="$DBUS_CFLAGS -I${dbus_prefix}/include/dbus-1.0"
+- if (test "${prefix}" = "${bluez_prefix}"); then
+- test -d "${libdir}/dbus-1.0/include" && DBUS_CFLAGS="$DBUS_CFLAGS -I${libdir}/dbus-1.0/include"
+- else
+- test -d "${dbus_prefix}/lib64/dbus-1.0/include" && DBUS_CFLAGS="$DBUS_CFLAGS -I${dbus_prefix}/lib64/dbus-1.0/include"
+- test -d "${dbus_prefix}/lib/dbus-1.0/include" && DBUS_CFLAGS="$DBUS_CFLAGS -I${dbus_prefix}/lib/dbus-1.0/include"
+- fi
+-
++ if pkg-config --exists dbus-1; then
++ DBUS_CFLAGS="`pkg-config --cflags dbus-1` -DDBUS_API_SUBJECT_TO_CHANGE"
++ DBUS_LIBS="`pkg-config --libs dbus-1`"
+ CPPFLAGS="$CPPFLAGS $DBUS_CFLAGS"
+- AC_CHECK_HEADER(dbus/dbus.h, dbus_found=yes, dbus_found=no)
+-
+- DBUS_LIBS=""
+- if (test "${prefix}" = "${dbus_prefix}"); then
+- test -d "${libdir}" && DBUS_LIBS="$DBUS_LIBS -L${libdir}"
+- else
+- test -d "${dbus_prefix}/lib64" && DBUS_LIBS="$DBUS_LIBS -L${dbus_prefix}/lib64"
+- test -d "${dbus_prefix}/lib" && DBUS_LIBS="$DBUS_LIBS -L${dbus_prefix}/lib"
+- fi
+-
+ LDFLAGS="$LDFLAGS $DBUS_LIBS"
+- AC_CHECK_LIB(dbus-1, dbus_error_init, DBUS_LIBS="$DBUS_LIBS -ldbus-1", dbus_found=no)
+-
+- CPPFLAGS=$ac_save_CPPFLAGS
+- LDFLAGS=$ac_save_LDFLAGS
+-
+ AC_SUBST(DBUS_CFLAGS)
+ AC_SUBST(DBUS_LIBS)
++ fi
+ ])
+
+ AC_DEFUN([AC_ARG_BLUEZ], [
diff --git a/busybox/busybox-1.00/hdparm_M.patch b/busybox/busybox-1.00/hdparm_M.patch
index e69de29bb2..170906535e 100644
--- a/busybox/busybox-1.00/hdparm_M.patch
+++ b/busybox/busybox-1.00/hdparm_M.patch
@@ -0,0 +1,47 @@
+diff -Naur busybox-1.00/miscutils/hdparm.c busybox-1.00-patched/miscutils/hdparm.c
+--- busybox-1.00/miscutils/hdparm.c 2004-07-21 00:53:59.000000000 +0200
++++ busybox-1.00-patched/miscutils/hdparm.c 2004-10-21 15:17:13.000000000 +0200
+@@ -1254,6 +1254,7 @@
+ static unsigned long set_sleepnow = 0, get_sleepnow = 0;
+ static unsigned long get_powermode = 0;
+ static unsigned long set_apmmode = 0, get_apmmode= 0, apmmode = 0;
++static unsigned long set_acoustic = 0, get_acoustic = 0, acoustic = 0;
+ #endif
+ #ifdef CONFIG_FEATURE_HDPARM_GET_IDENTITY
+ static int get_IDentity = 0;
+@@ -2123,6 +2124,20 @@
+ }
+ bb_ioctl(fd, HDIO_DRIVE_CMD, &args,"HDIO_DRIVE_CMD");
+ }
++ if (set_acoustic)
++ {
++ no_scsi();
++ acoustic=check_if_min_and_set_val(acoustic,0);
++ acoustic=check_if_maj_and_set_val(acoustic,254);
++ if_printf(get_acoustic," setting AAM level to 0x%02lX (%ld)\n", acoustic, acoustic);
++ bb_ioctl(fd, HDIO_SET_ACOUSTIC, (int*)acoustic,"HDIO_SET_ACOUSTIC");
++ }
++ if (get_acoustic)
++ {
++ no_scsi();
++ bb_ioctl(fd, HDIO_GET_ACOUSTIC, (unsigned long*)&parm,"HDIO_GET_ACOUSTIC");
++ printf(" acoustic = %2ld (128=quiet ... 254=fast)\n", parm);
++ }
+ if (set_wcache)
+ {
+ #ifdef DO_FLUSHCACHE
+@@ -2831,6 +2846,13 @@
+ p = *argv++, --argc;
+ p=GET_NUMBER(p,&set_readahead,&Xreadahead);
+ break;
++ case 'M':
++ get_acoustic = noisy;
++ noisy = 1;
++ if (!*p && argc && isalnum(**argv))
++ p = *argv++, --argc;
++ p=GET_NUMBER(p,&set_acoustic,&acoustic);
++ break;
+ case 'B':
+ get_apmmode = noisy;
+ noisy = 1;
+
diff --git a/busybox/busybox-1.00/openslug/defconfig b/busybox/busybox-1.00/openslug/defconfig
index e69de29bb2..881184593b 100644
--- a/busybox/busybox-1.00/openslug/defconfig
+++ b/busybox/busybox-1.00/openslug/defconfig
@@ -0,0 +1,474 @@
+#
+# Automatically generated make config: don't edit
+#
+HAVE_DOT_CONFIG=y
+
+#
+# General Configuration
+#
+# CONFIG_FEATURE_BUFFERS_USE_MALLOC is not set
+CONFIG_FEATURE_BUFFERS_GO_ON_STACK=y
+# CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set
+CONFIG_FEATURE_VERBOSE_USAGE=y
+# CONFIG_FEATURE_INSTALLER is not set
+# CONFIG_LOCALE_SUPPORT is not set
+CONFIG_FEATURE_DEVFS=y
+CONFIG_FEATURE_DEVPTS=y
+# CONFIG_FEATURE_CLEAN_UP is not set
+# CONFIG_FEATURE_SUID is not set
+# CONFIG_SELINUX is not set
+
+#
+# Build Options
+#
+# CONFIG_STATIC is not set
+CONFIG_LFS=y
+# USING_CROSS_COMPILER is not set
+EXTRA_CFLAGS_OPTIONS=""
+
+#
+# Installation Options
+#
+# CONFIG_INSTALL_NO_USR is not set
+PREFIX="./_install"
+
+#
+# Archival Utilities
+#
+CONFIG_AR=y
+# CONFIG_FEATURE_AR_LONG_FILENAMES is not set
+CONFIG_BUNZIP2=y
+CONFIG_CPIO=y
+# CONFIG_DPKG is not set
+# CONFIG_DPKG_DEB is not set
+CONFIG_GUNZIP=y
+# CONFIG_FEATURE_GUNZIP_UNCOMPRESS is not set
+CONFIG_GZIP=y
+# CONFIG_RPM2CPIO is not set
+# CONFIG_RPM is not set
+CONFIG_TAR=y
+CONFIG_FEATURE_TAR_CREATE=y
+CONFIG_FEATURE_TAR_BZIP2=y
+CONFIG_FEATURE_TAR_FROM=y
+CONFIG_FEATURE_TAR_GZIP=y
+# CONFIG_FEATURE_TAR_COMPRESS is not set
+CONFIG_FEATURE_TAR_OLDGNU_COMPATABILITY=y
+CONFIG_FEATURE_TAR_GNU_EXTENSIONS=y
+# CONFIG_FEATURE_TAR_LONG_OPTIONS is not set
+# CONFIG_UNCOMPRESS is not set
+CONFIG_UNZIP=y
+
+#
+# Common options for cpio and tar
+#
+# CONFIG_FEATURE_UNARCHIVE_TAPE is not set
+
+#
+# Coreutils
+#
+CONFIG_BASENAME=y
+# CONFIG_CAL is not set
+CONFIG_CAT=y
+CONFIG_CHGRP=y
+CONFIG_CHMOD=y
+CONFIG_CHOWN=y
+CONFIG_CHROOT=y
+# CONFIG_CMP is not set
+CONFIG_CP=y
+CONFIG_CUT=y
+CONFIG_DATE=y
+
+#
+# date (forced enabled for use with watch)
+#
+# CONFIG_FEATURE_DATE_ISOFMT is not set
+CONFIG_DD=y
+CONFIG_DF=y
+CONFIG_DIRNAME=y
+# CONFIG_DOS2UNIX is not set
+CONFIG_DU=y
+CONFIG_FEATURE_DU_DEFALT_BLOCKSIZE_1K=y
+CONFIG_ECHO=y
+CONFIG_FEATURE_FANCY_ECHO=y
+CONFIG_ENV=y
+CONFIG_EXPR=y
+CONFIG_FALSE=y
+# CONFIG_FOLD is not set
+CONFIG_HEAD=y
+# CONFIG_FEATURE_FANCY_HEAD is not set
+# CONFIG_HOSTID is not set
+CONFIG_ID=y
+# CONFIG_INSTALL is not set
+# CONFIG_LENGTH is not set
+CONFIG_LN=y
+CONFIG_LOGNAME=y
+CONFIG_LS=y
+CONFIG_FEATURE_LS_FILETYPES=y
+CONFIG_FEATURE_LS_FOLLOWLINKS=y
+CONFIG_FEATURE_LS_RECURSIVE=y
+CONFIG_FEATURE_LS_SORTFILES=y
+CONFIG_FEATURE_LS_TIMESTAMPS=y
+CONFIG_FEATURE_LS_USERNAME=y
+CONFIG_FEATURE_LS_COLOR=y
+CONFIG_MD5SUM=y
+CONFIG_MKDIR=y
+CONFIG_MKFIFO=y
+CONFIG_MKNOD=y
+CONFIG_MV=y
+CONFIG_OD=y
+CONFIG_PRINTF=y
+CONFIG_PWD=y
+# CONFIG_REALPATH is not set
+CONFIG_RM=y
+CONFIG_RMDIR=y
+CONFIG_SEQ=y
+# CONFIG_SHA1SUM is not set
+CONFIG_SLEEP=y
+# CONFIG_FEATURE_FANCY_SLEEP is not set
+CONFIG_SORT=y
+CONFIG_STTY=y
+CONFIG_SYNC=y
+CONFIG_TAIL=y
+CONFIG_FEATURE_FANCY_TAIL=y
+CONFIG_TEE=y
+# CONFIG_FEATURE_TEE_USE_BLOCK_IO is not set
+CONFIG_TEST=y
+
+#
+# test (forced enabled for use with shell)
+#
+# CONFIG_FEATURE_TEST_64 is not set
+CONFIG_TOUCH=y
+CONFIG_TR=y
+CONFIG_TRUE=y
+CONFIG_TTY=y
+CONFIG_UNAME=y
+CONFIG_UNIQ=y
+# CONFIG_USLEEP is not set
+# CONFIG_UUDECODE is not set
+# CONFIG_UUENCODE is not set
+CONFIG_WATCH=y
+CONFIG_WC=y
+CONFIG_WHO=y
+CONFIG_WHOAMI=y
+CONFIG_YES=y
+
+#
+# Common options for cp and mv
+#
+# CONFIG_FEATURE_PRESERVE_HARDLINKS is not set
+
+#
+# Common options for ls and more
+#
+CONFIG_FEATURE_AUTOWIDTH=y
+
+#
+# Common options for df, du, ls
+#
+CONFIG_FEATURE_HUMAN_READABLE=y
+
+#
+# Common options for md5sum, sha1sum
+#
+# CONFIG_FEATURE_MD5_SHA1_SUM_CHECK is not set
+
+#
+# Console Utilities
+#
+CONFIG_CHVT=y
+CONFIG_CLEAR=y
+CONFIG_DEALLOCVT=y
+CONFIG_DUMPKMAP=y
+CONFIG_GETKEY=y
+CONFIG_LOADFONT=y
+CONFIG_LOADKMAP=y
+CONFIG_OPENVT=y
+CONFIG_RESET=y
+# CONFIG_SETKEYCODES is not set
+
+#
+# Debian Utilities
+#
+CONFIG_MKTEMP=y
+# CONFIG_PIPE_PROGRESS is not set
+CONFIG_READLINK=y
+CONFIG_RUN_PARTS=y
+CONFIG_START_STOP_DAEMON=y
+CONFIG_WHICH=y
+
+#
+# Editors
+#
+CONFIG_AWK=y
+CONFIG_FEATURE_AWK_MATH=y
+# CONFIG_PATCH is not set
+CONFIG_SED=y
+CONFIG_VI=y
+CONFIG_FEATURE_VI_COLON=y
+CONFIG_FEATURE_VI_YANKMARK=y
+CONFIG_FEATURE_VI_SEARCH=y
+CONFIG_FEATURE_VI_USE_SIGNALS=y
+# CONFIG_FEATURE_VI_DOT_CMD is not set
+# CONFIG_FEATURE_VI_READONLY is not set
+# CONFIG_FEATURE_VI_SETOPTS is not set
+# CONFIG_FEATURE_VI_SET is not set
+CONFIG_FEATURE_VI_WIN_RESIZE=y
+CONFIG_FEATURE_VI_OPTIMIZE_CURSOR=y
+
+#
+# Finding Utilities
+#
+CONFIG_FIND=y
+CONFIG_FEATURE_FIND_MTIME=y
+CONFIG_FEATURE_FIND_PERM=y
+CONFIG_FEATURE_FIND_TYPE=y
+CONFIG_FEATURE_FIND_XDEV=y
+CONFIG_FEATURE_FIND_NEWER=y
+# CONFIG_FEATURE_FIND_INUM is not set
+CONFIG_GREP=y
+CONFIG_FEATURE_GREP_EGREP_ALIAS=y
+CONFIG_FEATURE_GREP_FGREP_ALIAS=y
+CONFIG_FEATURE_GREP_CONTEXT=y
+CONFIG_XARGS=y
+# CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION is not set
+# CONFIG_FEATURE_XARGS_SUPPORT_QUOTES is not set
+# CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT is not set
+# CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM is not set
+
+#
+# Init Utilities
+#
+# CONFIG_INIT is not set
+# CONFIG_HALT is not set
+# CONFIG_POWEROFF is not set
+# CONFIG_REBOOT is not set
+# CONFIG_MESG is not set
+
+#
+# Login/Password Management Utilities
+#
+# CONFIG_USE_BB_PWD_GRP is not set
+# CONFIG_ADDGROUP is not set
+# CONFIG_DELGROUP is not set
+# CONFIG_ADDUSER is not set
+# CONFIG_DELUSER is not set
+# CONFIG_GETTY is not set
+CONFIG_FEATURE_U_W_TMP=y
+# CONFIG_LOGIN is not set
+# CONFIG_PASSWD is not set
+# CONFIG_SU is not set
+# CONFIG_SULOGIN is not set
+# CONFIG_VLOCK is not set
+
+#
+# Miscellaneous Utilities
+#
+# CONFIG_ADJTIMEX is not set
+# CONFIG_CROND is not set
+# CONFIG_CRONTAB is not set
+CONFIG_DC=y
+# CONFIG_DEVFSD is not set
+# CONFIG_LAST is not set
+# CONFIG_HDPARM is not set
+# CONFIG_MAKEDEVS is not set
+# CONFIG_MT is not set
+# CONFIG_RX is not set
+CONFIG_STRINGS=y
+CONFIG_TIME=y
+# CONFIG_WATCHDOG is not set
+
+#
+# Linux Module Utilities
+#
+CONFIG_INSMOD=y
+CONFIG_FEATURE_2_4_MODULES=y
+CONFIG_FEATURE_2_6_MODULES=y
+# CONFIG_FEATURE_INSMOD_VERSION_CHECKING is not set
+CONFIG_FEATURE_INSMOD_KSYMOOPS_SYMBOLS=y
+# CONFIG_FEATURE_INSMOD_LOADINKMEM is not set
+CONFIG_FEATURE_INSMOD_LOAD_MAP=y
+CONFIG_FEATURE_INSMOD_LOAD_MAP_FULL=y
+CONFIG_LSMOD=y
+CONFIG_MODPROBE=y
+CONFIG_RMMOD=y
+CONFIG_FEATURE_CHECK_TAINTED_MODULE=y
+
+#
+# Networking Utilities
+#
+CONFIG_FEATURE_IPV6=y
+# CONFIG_ARPING is not set
+# CONFIG_FTPGET is not set
+# CONFIG_FTPPUT is not set
+CONFIG_HOSTNAME=y
+# CONFIG_HTTPD is not set
+CONFIG_IFCONFIG=y
+CONFIG_FEATURE_IFCONFIG_STATUS=y
+# CONFIG_FEATURE_IFCONFIG_SLIP is not set
+# CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ is not set
+CONFIG_FEATURE_IFCONFIG_HW=y
+# CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS is not set
+CONFIG_IFUPDOWN=y
+# CONFIG_FEATURE_IFUPDOWN_IP is not set
+CONFIG_FEATURE_IFUPDOWN_IP_BUILTIN=y
+CONFIG_FEATURE_IFUPDOWN_IPV4=y
+CONFIG_FEATURE_IFUPDOWN_IPV6=y
+# CONFIG_FEATURE_IFUPDOWN_IPX is not set
+# CONFIG_FEATURE_IFUPDOWN_MAPPING is not set
+# CONFIG_INETD is not set
+CONFIG_IP=y
+CONFIG_FEATURE_IP_ADDRESS=y
+CONFIG_FEATURE_IP_LINK=y
+CONFIG_FEATURE_IP_ROUTE=y
+CONFIG_FEATURE_IP_TUNNEL=y
+# CONFIG_IPCALC is not set
+# CONFIG_IPADDR is not set
+# CONFIG_IPLINK is not set
+# CONFIG_IPROUTE is not set
+# CONFIG_IPTUNNEL is not set
+# CONFIG_NAMEIF is not set
+CONFIG_NC=y
+CONFIG_NETSTAT=y
+CONFIG_NSLOOKUP=y
+CONFIG_PING=y
+CONFIG_FEATURE_FANCY_PING=y
+CONFIG_PING6=y
+CONFIG_FEATURE_FANCY_PING6=y
+CONFIG_ROUTE=y
+CONFIG_TELNET=y
+# CONFIG_FEATURE_TELNET_TTYPE is not set
+CONFIG_FEATURE_TELNET_AUTOLOGIN=y
+# CONFIG_TELNETD is not set
+CONFIG_TFTP=y
+CONFIG_FEATURE_TFTP_GET=y
+CONFIG_FEATURE_TFTP_PUT=y
+# CONFIG_FEATURE_TFTP_BLOCKSIZE is not set
+# CONFIG_FEATURE_TFTP_DEBUG is not set
+CONFIG_TRACEROUTE=y
+# CONFIG_FEATURE_TRACEROUTE_VERBOSE is not set
+# CONFIG_VCONFIG is not set
+CONFIG_WGET=y
+CONFIG_FEATURE_WGET_STATUSBAR=y
+CONFIG_FEATURE_WGET_AUTHENTICATION=y
+CONFIG_FEATURE_WGET_IP6_LITERAL=y
+
+#
+# udhcp Server/Client
+#
+# CONFIG_UDHCPD is not set
+CONFIG_UDHCPC=y
+CONFIG_FEATURE_UDHCP_SYSLOG=y
+# CONFIG_FEATURE_UDHCP_DEBUG is not set
+
+#
+# Process Utilities
+#
+CONFIG_FREE=y
+CONFIG_KILL=y
+CONFIG_KILLALL=y
+CONFIG_PIDOF=y
+CONFIG_PS=y
+CONFIG_RENICE=y
+CONFIG_TOP=y
+FEATURE_CPU_USAGE_PERCENTAGE=y
+CONFIG_UPTIME=y
+# CONFIG_SYSCTL is not set
+
+#
+# Another Bourne-like Shell
+#
+CONFIG_FEATURE_SH_IS_ASH=y
+# CONFIG_FEATURE_SH_IS_HUSH is not set
+# CONFIG_FEATURE_SH_IS_LASH is not set
+# CONFIG_FEATURE_SH_IS_MSH is not set
+# CONFIG_FEATURE_SH_IS_NONE is not set
+CONFIG_ASH=y
+
+#
+# Ash Shell Options
+#
+CONFIG_ASH_JOB_CONTROL=y
+CONFIG_ASH_ALIAS=y
+CONFIG_ASH_MATH_SUPPORT=y
+# CONFIG_ASH_MATH_SUPPORT_64 is not set
+CONFIG_ASH_GETOPTS=y
+# CONFIG_ASH_CMDCMD is not set
+# CONFIG_ASH_MAIL is not set
+CONFIG_ASH_OPTIMIZE_FOR_SIZE=y
+# CONFIG_ASH_RANDOM_SUPPORT is not set
+# CONFIG_HUSH is not set
+# CONFIG_LASH is not set
+# CONFIG_MSH is not set
+
+#
+# Bourne Shell Options
+#
+CONFIG_FEATURE_SH_EXTRA_QUIET=y
+# CONFIG_FEATURE_SH_STANDALONE_SHELL is not set
+CONFIG_FEATURE_COMMAND_EDITING=y
+CONFIG_FEATURE_COMMAND_HISTORY=63
+# CONFIG_FEATURE_COMMAND_SAVEHISTORY is not set
+CONFIG_FEATURE_COMMAND_TAB_COMPLETION=y
+# CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION is not set
+CONFIG_FEATURE_SH_FANCY_PROMPT=y
+
+#
+# System Logging Utilities
+#
+CONFIG_SYSLOGD=y
+# CONFIG_FEATURE_ROTATE_LOGFILE is not set
+CONFIG_FEATURE_REMOTE_LOG=y
+CONFIG_FEATURE_IPC_SYSLOG=y
+CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE=16
+CONFIG_LOGREAD=y
+# CONFIG_FEATURE_LOGREAD_REDUCED_LOCKING is not set
+CONFIG_KLOGD=y
+CONFIG_LOGGER=y
+
+#
+# Linux System Utilities
+#
+CONFIG_DMESG=y
+CONFIG_FBSET=y
+CONFIG_FEATURE_FBSET_FANCY=y
+# CONFIG_FEATURE_FBSET_READMODE is not set
+# CONFIG_FDFLUSH is not set
+# CONFIG_FDFORMAT is not set
+CONFIG_FDISK=y
+FDISK_SUPPORT_LARGE_DISKS=y
+CONFIG_FEATURE_FDISK_WRITABLE=y
+# CONFIG_FEATURE_AIX_LABEL is not set
+# CONFIG_FEATURE_SGI_LABEL is not set
+# CONFIG_FEATURE_SUN_LABEL is not set
+# CONFIG_FEATURE_OSF_LABEL is not set
+# CONFIG_FEATURE_FDISK_ADVANCED is not set
+# CONFIG_FREERAMDISK is not set
+# CONFIG_FSCK_MINIX is not set
+# CONFIG_MKFS_MINIX is not set
+# CONFIG_GETOPT is not set
+CONFIG_HEXDUMP=y
+CONFIG_HWCLOCK=y
+CONFIG_FEATURE_HWCLOCK_LONGOPTIONS=y
+CONFIG_LOSETUP=y
+CONFIG_MKSWAP=y
+CONFIG_MORE=y
+CONFIG_FEATURE_USE_TERMIOS=y
+CONFIG_PIVOT_ROOT=y
+# CONFIG_RDATE is not set
+CONFIG_SWAPONOFF=y
+CONFIG_MOUNT=y
+CONFIG_NFSMOUNT=y
+CONFIG_UMOUNT=y
+CONFIG_FEATURE_MOUNT_FORCE=y
+
+#
+# Common options for mount/umount
+#
+CONFIG_FEATURE_MOUNT_LOOP=y
+# CONFIG_FEATURE_MTAB_SUPPORT is not set
+
+#
+# Debugging Options
+#
+# CONFIG_DEBUG is not set
diff --git a/busybox/busybox-1.00/udhcppidfile.patch b/busybox/busybox-1.00/udhcppidfile.patch
index e69de29bb2..fb2b2ec19a 100644
--- a/busybox/busybox-1.00/udhcppidfile.patch
+++ b/busybox/busybox-1.00/udhcppidfile.patch
@@ -0,0 +1,276 @@
+--- busybox-1.00/networking/udhcp/pidfile.h-dist 2004-04-15 03:51:26.000000000 +1000
++++ busybox-1.00/networking/udhcp/pidfile.h 2004-10-27 15:46:38.000000000 +1000
+@@ -21,5 +21,5 @@
+
+
+ int pidfile_acquire(const char *pidfile);
+-void pidfile_write_release(int pid_fd);
++int pidfile_reassign(const char *pidfile, int newpid);
+
+--- busybox-1.00/networking/udhcp/pidfile.c-dist 2004-04-15 03:51:25.000000000 +1000
++++ busybox-1.00/networking/udhcp/pidfile.c 2004-10-27 19:43:40.000000000 +1000
+@@ -25,6 +25,7 @@
+ #include <unistd.h>
+ #include <stdio.h>
+ #include <stdlib.h>
++#include <errno.h>
+
+ #include "pidfile.h"
+ #include "common.h"
+@@ -37,39 +38,146 @@
+ }
+
+
+-int pidfile_acquire(const char *pidfile)
++static int pidfile_open(const char *pidfile)
+ {
+- int pid_fd;
+- if (!pidfile) return -1;
++ int fd;
+
+- pid_fd = open(pidfile, O_CREAT | O_WRONLY, 0644);
+- if (pid_fd < 0) {
+- LOG(LOG_ERR, "Unable to open pidfile %s: %m\n", pidfile);
+- } else {
+- lockf(pid_fd, F_LOCK, 0);
+- if (!saved_pidfile)
+- atexit(pidfile_delete);
+- saved_pidfile = (char *) pidfile;
++ if ((fd = open(pidfile, O_CREAT | O_RDWR, 0644)) < 0) {
++ LOG(LOG_ERR, "pidfile_open: open %s failed: %m\n", pidfile);
++ return (-1);
++ }
++
++ /* NOTE: lockf is not inherited by child after fork */
++ if (lockf(fd, F_LOCK, 0) < 0) {
++ LOG(LOG_ERR, "pidfile_open: lock %s failed: %m\n", pidfile);
++ close(fd);
++ return (-1);
++ }
++
++ return (fd);
++}
++
++
++static int pidfile_check(int fd, const char *pidfile)
++{
++ int len, pid;
++ char buf[20];
++
++ if (lseek(fd, 0L, SEEK_SET) < 0) {
++ LOG(LOG_ERR, "pidfile_check: lseek %s failed: %m\n", pidfile);
++ return (-1);
++ }
++
++ if ((len = read(fd, buf, sizeof buf - 1)) < 0) {
++ LOG(LOG_ERR, "pidfile_check: read %s failed: %m\n", pidfile);
++ return (-1);
++ }
++
++ if (len == 0)
++ return (0);
++
++ buf[len] = '\0';
++
++ if ((pid = atoi(buf)) <= 1) {
++ LOG(LOG_WARNING,
++ "pidfile_check: ignoring bogus pid (%s) in %s\n",
++ buf, pidfile);
++ return (0);
++ }
++
++ if (kill((pid_t)pid, 0) == 0) {
++ LOG(LOG_ERR, "pidfile_check: process %d exists (%s)\n",
++ pid, pidfile);
++ return (-1);
++ }
++
++ if (errno != ESRCH) {
++ LOG(LOG_ERR, "pidfile_check: kill %d failed (%s): %m\n",
++ pid, pidfile);
++ return (-1);
++ }
++
++ return (0);
++}
++
++
++static int pidfile_store(int fd, const char *pidfile, int pid)
++{
++ int len;
++ char buf[20];
++
++ if (lseek(fd, 0L, SEEK_SET) < 0) {
++ LOG(LOG_ERR, "pidfile_store: lseek %s failed: %m\n", pidfile);
++ return (-1);
++ }
++
++ len = snprintf(buf, sizeof buf - 1, "%d\n", pid);
++ buf[len] = '\0';
++
++ if (write(fd, buf, len) < 0) {
++ LOG(LOG_ERR, "pidfile_store: write %s failed: %m\n",
++ pidfile);
++ return (-1);
++ }
++
++ if (ftruncate(fd, len) < 0) {
++ LOG(LOG_ERR, "pidfile_store: ftruncate %d failed (%s): %m\n",
++ len, pidfile);
++ return (-1);
+ }
+
+- return pid_fd;
++ return (0);
+ }
+
+
+-void pidfile_write_release(int pid_fd)
++static void pidfile_close(int fd)
+ {
+- FILE *out;
++ (void)lseek(fd, 0L, SEEK_SET);
++ (void)lockf(fd, F_ULOCK, 0);
++ (void)close(fd);
++}
+
+- if (pid_fd < 0) return;
+
+- if ((out = fdopen(pid_fd, "w")) != NULL) {
+- fprintf(out, "%d\n", getpid());
+- fclose(out);
++int pidfile_acquire(const char *pidfile)
++{
++ int fd, result;
++ if (!pidfile) return (-1);
++
++ if ((fd = pidfile_open(pidfile)) < 0)
++ return (-1);
++
++ if ((result = pidfile_check(fd, pidfile)) == 0)
++ result = pidfile_store(fd, pidfile, getpid());
++
++ pidfile_close(fd);
++
++ if (result == 0) {
++ saved_pidfile = (char *) pidfile;
++ atexit(pidfile_delete);
+ }
+- lockf(pid_fd, F_UNLCK, 0);
+- close(pid_fd);
++
++ return (result);
+ }
+
+
++/*
++ * reassign the pid in a pidfile - used just after a fork so a parent
++ * can store the pid of its child into the file without any window
++ * where the pid in the file is a dead process (which might let another
++ * instance of the program start). Note the parent must use _exit() to
++ * avoid triggering the unlink scheduled above in pidfile_acquire()
++ */
++int pidfile_reassign(const char *pidfile, int pid)
++{
++ int fd, result;
++ if (!pidfile) return (-1);
++
++ if ((fd = pidfile_open(pidfile)) < 0)
++ return (-1);
+
++ result = pidfile_store(fd, pidfile, pid);
+
++ pidfile_close(fd);
++
++ return (result);
++}
+--- busybox-1.00/networking/udhcp/common.c-dist 2004-05-19 19:18:04.000000000 +1000
++++ busybox-1.00/networking/udhcp/common.c 2004-10-27 19:58:10.000000000 +1000
+@@ -64,16 +64,34 @@
+ #ifdef __uClinux__
+ LOG(LOG_ERR, "Cannot background in uclinux (yet)");
+ #else /* __uClinux__ */
+- int pid_fd;
++ int pid, fd;
+
+- /* hold lock during fork. */
+- pid_fd = pidfile_acquire(pidfile);
+- if (daemon(0, 0) == -1) {
++ /* NOTE: lockf is not inherited by the child after fork */
++ if ((pid = fork()) < 0) {
+ perror("fork");
+ exit(1);
+ }
++
++ if (pid > 0) {
++ /* parent */
++ if (pidfile_reassign(pidfile, pid) < 0) {
++ (void)kill(pid, SIGKILL);
++ exit(1);
++ } else
++ _exit(0);
++ }
++
++ /* child */
++ (void)chdir("/");
++ if ((fd = open("/dev/null", O_RDWR)) >= 0) {
++ (void)dup2(fd, 0);
++ (void)dup2(fd, 1);
++ (void)dup2(fd, 2);
++ (void)close(fd);
++ }
++ (void)setsid();
++
+ daemonized++;
+- pidfile_write_release(pid_fd);
+ #endif /* __uClinux__ */
+ }
+
+@@ -97,14 +115,12 @@
+
+ void start_log_and_pid(const char *client_server, const char *pidfile)
+ {
+- int pid_fd;
+-
+ /* Make sure our syslog fd isn't overwritten */
+ sanitize_fds();
+
+ /* do some other misc startup stuff while we are here to save bytes */
+- pid_fd = pidfile_acquire(pidfile);
+- pidfile_write_release(pid_fd);
++ if (pidfile_acquire(pidfile) < 0)
++ exit(1);
+
+ /* equivelent of doing a fflush after every \n */
+ setlinebuf(stdout);
+@@ -150,8 +166,8 @@
+ sanitize_fds();
+
+ /* do some other misc startup stuff while we are here to save bytes */
+- pid_fd = pidfile_acquire(pidfile);
+- pidfile_write_release(pid_fd);
++ if (pidfile_acquire(pidfile) < 0)
++ exit(1);
+
+ /* equivelent of doing a fflush after every \n */
+ setlinebuf(stdout);
+--- busybox-1.00/networking/udhcp/common.h-dist 2004-05-19 18:29:05.000000000 +1000
++++ busybox-1.00/networking/udhcp/common.h 2004-10-27 15:10:16.000000000 +1000
+@@ -42,7 +42,6 @@
+ long uptime(void);
+ void background(const char *pidfile);
+ void start_log_and_pid(const char *client_server, const char *pidfile);
+-void background(const char *pidfile);
+ void udhcp_logging(int level, const char *fmt, ...);
+
+ #define LOG(level, str, args...) udhcp_logging(level, str, ## args)
+--- busybox-1.00/networking/udhcp/script.c-dist 2004-05-19 17:45:47.000000000 +1000
++++ busybox-1.00/networking/udhcp/script.c 2004-10-27 15:54:04.000000000 +1000
+@@ -228,6 +228,6 @@
+ execle(client_config.script, client_config.script,
+ name, NULL, envp);
+ LOG(LOG_ERR, "script %s failed: %m", client_config.script);
+- exit(1);
++ _exit(1);
+ }
+ }
diff --git a/busybox/switchbox-1.00/switchbox.patch b/busybox/switchbox-1.00/switchbox.patch
index e69de29bb2..c600c5d007 100644
--- a/busybox/switchbox-1.00/switchbox.patch
+++ b/busybox/switchbox-1.00/switchbox.patch
@@ -0,0 +1,38 @@
+--- busybox-1.00-rc3/applets/install.sh 2001-03-09 08:12:11.000000000 +1030
++++ switchbox/applets/install.sh 2004-08-31 11:27:23.000000000 +0930
+@@ -16,28 +16,28 @@
+ h=`sort busybox.links | uniq`
+
+
+-rm -f $prefix/bin/busybox || exit 1
++rm -f $prefix/bin/switchbox || exit 1
+ mkdir -p $prefix/bin || exit 1
+-install -m 755 busybox $prefix/bin/busybox || exit 1
++install -m 755 busybox $prefix/bin/switchbox || exit 1
+
+ for i in $h ; do
+ appdir=`dirname $i`
+ mkdir -p $prefix/$appdir || exit 1
+ if [ "$2" = "--hardlinks" ]; then
+- bb_path="$prefix/bin/busybox"
++ bb_path="$prefix/bin/switchbox"
+ else
+ case "$appdir" in
+ /)
+- bb_path="bin/busybox"
++ bb_path="bin/switchbox"
+ ;;
+ /bin)
+- bb_path="busybox"
++ bb_path="switchbox"
+ ;;
+ /sbin)
+- bb_path="../bin/busybox"
++ bb_path="../bin/switchbox"
+ ;;
+ /usr/bin|/usr/sbin)
+- bb_path="../../bin/busybox"
++ bb_path="../../bin/switchbox"
+ ;;
+ *)
+ echo "Unknown installation directory: $appdir"
diff --git a/cpio/files/install.patch b/cpio/files/install.patch
index e69de29bb2..eb36818b93 100644
--- a/cpio/files/install.patch
+++ b/cpio/files/install.patch
@@ -0,0 +1,32 @@
+
+#
+# Patch managed by http://www.holgerschurig.de/patcher.html
+#
+
+--- cpio-2.5/Makefile.in~install
++++ cpio-2.5/Makefile.in
+@@ -70,6 +70,7 @@
+ # Prefix for each installed man page, normally empty or `g'.
+ manprefix =
+
++DESTDIR =
+
+ # Where to install the cpio and mt executables.
+ bindir = @bindir@
+@@ -123,11 +124,11 @@
+ $(CC) -c $(CPPFLAGS) $(DEFS) -I$(srcdir) $(CFLAGS) $<
+
+ install:: installdirs all $(srcdir)/cpio.1 $(srcdir)/mt.1
+- $(INSTALL_PROGRAM) cpio $(bindir)/$(binprefix)cpio
+- test ! -f mt || $(INSTALL_PROGRAM) mt $(bindir)/$(binprefix)mt
+- -test ! -f rmt || $(INSTALL_PROGRAM) rmt $(libexecdir)/rmt
+- -$(INSTALL_DATA) $(srcdir)/cpio.1 $(mandir)/$(manprefix)cpio.$(manext)
+- -test ! -f mt || $(INSTALL_DATA) $(srcdir)/mt.1 $(mandir)/$(manprefix)mt.$(manext)
++ $(INSTALL_PROGRAM) cpio $(DESTDIR)$(bindir)/$(binprefix)cpio
++ test ! -f mt || $(INSTALL_PROGRAM) mt $(DESTDIR)$(bindir)/$(binprefix)mt
++ -test ! -f rmt || $(INSTALL_PROGRAM) rmt $(DESTDIR)$(libexecdir)/rmt
++ -$(INSTALL_DATA) $(srcdir)/cpio.1 $(DESTDIR)$(mandir)/$(manprefix)cpio.$(manext)
++ -test ! -f mt || $(INSTALL_DATA) $(srcdir)/mt.1 $(DESTDIR)$(mandir)/$(manprefix)mt.$(manext)
+
+ installdirs:
+ $(srcdir)/mkinstalldirs $(bindir) $(libexecdir) $(mandir) $(infodir)
diff --git a/dpkg/files/autofoo.patch b/dpkg/files/autofoo.patch
index e69de29bb2..691121e418 100644
--- a/dpkg/files/autofoo.patch
+++ b/dpkg/files/autofoo.patch
@@ -0,0 +1,48 @@
+
+#
+# Patch managed by http://www.holgerschurig.de/patcher.html
+#
+
+--- dpkg-1.10.23/configure.in~autofoo
++++ dpkg-1.10.23/configure.in
+@@ -227,21 +227,36 @@
+ # OpenBSD passes AC_TRY_COMPILE for va_copy even though
+ # it doesn't seem to exist, which is odd. We need to use
+ # AC_TRY_RUN.
++#
++# If crosscompiling, use AC_TRY_COMPILE. -CL
+ AC_TRY_RUN([
+ #include <stdarg.h>
+ main(){
+ va_list v1,v2;
+ va_copy(v1, v2);
+ exit(0);}
+-], [AC_MSG_RESULT(yes)
+-AC_DEFINE(HAVE_VA_COPY,,[Whether the va_copy macro exists])],[AC_MSG_RESULT(no)
+-AC_MSG_CHECKING([for va_list assignment copy])
++], [dpkg_cv_va_copy=yes], [dpkg_cv_va_copy=no],
+ AC_TRY_COMPILE([
+ #include <stdarg.h>
++main(){
++va_list v1,v2;
++va_copy(v1, v2);
++exit(0);}
++], [dpkg_cv_va_copy=yes], [dpkg_vc_va_copy=no]))
++
++if test "$dpkg_cv_va_copy" = "yes"; then
++ AC_MSG_RESULT(yes)
++ AC_DEFINE(HAVE_VA_COPY,,[Whether the va_copy macro exists])
++else
++ AC_MSG_RESULT(no)
++ AC_MSG_CHECKING([for va_list assignment copy])
++ AC_TRY_COMPILE([
++#include <stdarg.h>
+ ],[
+ va_list v1,v2;
+ v1 = v2;
+-], AC_MSG_RESULT(yes),AC_MSG_ERROR(no))])
++], AC_MSG_RESULT(yes), AC_MSG_ERROR(no))
++fi
+
+ DPKG_C_GCC_ATTRIBUTE([,,],supported,[int x],[,,],ATTRIB,[Define if function attributes a la GCC 2.5 and higher are available.],
+ DPKG_C_GCC_ATTRIBUTE(noreturn,noreturn,[int x],noreturn,NORETURN,[Define if nonreturning functions a la GCC 2.5 and higher are available.])
diff --git a/gcc/gcc-3.4.3/gcc-3.4.0-arm-bigendian-uclibc.patch b/gcc/gcc-3.4.3/gcc-3.4.0-arm-bigendian-uclibc.patch
index e69de29bb2..8fa9af880a 100644
--- a/gcc/gcc-3.4.3/gcc-3.4.0-arm-bigendian-uclibc.patch
+++ b/gcc/gcc-3.4.3/gcc-3.4.0-arm-bigendian-uclibc.patch
@@ -0,0 +1,30 @@
+
+#
+# Patch managed by http://www.holgerschurig.de/patcher.html
+#
+
+--- gcc-3.4.1/gcc/config.gcc~gcc-3.4.0-arm-bigendian-uclibc
++++ gcc-3.4.1/gcc/config.gcc
+@@ -666,6 +666,11 @@
+ ;;
+ arm*-*-linux-uclibc*) # ARM GNU/Linux with ELF - uClibc
+ tm_file="dbxelf.h elfos.h linux.h arm/elf.h arm/linux-gas.h arm/linux-elf.h arm/aout.h arm/arm.h"
++ case $target in
++ arm*b-*)
++ tm_defines="TARGET_BIG_ENDIAN_DEFAULT=1 $tm_defines"
++ ;;
++ esac
+ tmake_file="t-slibgcc-elf-ver t-linux-uclibc arm/t-linux"
+ extra_parts="crtbegin.o crtbeginS.o crtend.o crtendS.o"
+ gnu_ld=yes
+--- gcc-3.4.1/gcc/config/arm/linux-elf.h~gcc-3.4.0-arm-bigendian-uclibc
++++ gcc-3.4.1/gcc/config/arm/linux-elf.h
+@@ -120,7 +120,7 @@
+ %{rdynamic:-export-dynamic} \
+ %{!dynamic-linker:-dynamic-linker /lib/ld-linux.so.2} \
+ -X \
+- %{mbig-endian:-EB}" \
++ %{mbig-endian:-EB} %{mlittle-endian:-EL}" \
+ SUBTARGET_EXTRA_LINK_SPEC
+ #endif
+
diff --git a/gcc/gcc-3.4.3/gcc-3.4.0-arm-bigendian.patch b/gcc/gcc-3.4.3/gcc-3.4.0-arm-bigendian.patch
index e69de29bb2..c9288c6c15 100644
--- a/gcc/gcc-3.4.3/gcc-3.4.0-arm-bigendian.patch
+++ b/gcc/gcc-3.4.3/gcc-3.4.0-arm-bigendian.patch
@@ -0,0 +1,70 @@
+By Lennert Buytenhek <buytenh@wantstofly.org>
+Adds support for arm*b-linux* big-endian ARM targets
+
+See http://gcc.gnu.org/PR16350
+
+diff -urN gcc-3.4.0.orig/gcc/config/arm/linux-elf.h gcc-3.4.0/gcc/config/arm/linux-elf.h
+--- gcc-3.4.0.orig/gcc/config/arm/linux-elf.h 2004-01-31 07:18:11.000000000 +0100
++++ gcc-3.4.0/gcc/config/arm/linux-elf.h 2004-07-02 14:46:29.225443757 +0200
+@@ -30,17 +30,34 @@
+ /* Do not assume anything about header files. */
+ #define NO_IMPLICIT_EXTERN_C
+
++/*
++ * 'config.gcc' defines TARGET_BIG_ENDIAN_DEFAULT as 1 for arm*b-*
++ * (big endian) configurations.
++ */
++#if TARGET_BIG_ENDIAN_DEFAULT
++#define TARGET_ENDIAN_DEFAULT ARM_FLAG_BIG_END
++#define TARGET_ENDIAN_OPTION "mbig-endian"
++#define TARGET_LINKER_EMULATION "armelfb_linux"
++#else
++#define TARGET_ENDIAN_DEFAULT 0
++#define TARGET_ENDIAN_OPTION "mlittle-endian"
++#define TARGET_LINKER_EMULATION "armelf_linux"
++#endif
++
+ /* Default is to use APCS-32 mode. */
+ #undef TARGET_DEFAULT
+-#define TARGET_DEFAULT (ARM_FLAG_APCS_32 | ARM_FLAG_MMU_TRAPS)
++#define TARGET_DEFAULT \
++ ( ARM_FLAG_APCS_32 | \
++ ARM_FLAG_MMU_TRAPS | \
++ TARGET_ENDIAN_DEFAULT )
+
+ #define SUBTARGET_CPU_DEFAULT TARGET_CPU_arm6
+
+-#define SUBTARGET_EXTRA_LINK_SPEC " -m armelf_linux -p"
++#define SUBTARGET_EXTRA_LINK_SPEC " -m " TARGET_LINKER_EMULATION " -p"
+
+ #undef MULTILIB_DEFAULTS
+ #define MULTILIB_DEFAULTS \
+- { "marm", "mlittle-endian", "mhard-float", "mapcs-32", "mno-thumb-interwork" }
++ { "marm", TARGET_ENDIAN_OPTION, "mhard-float", "mapcs-32", "mno-thumb-interwork" }
+
+ #define CPP_APCS_PC_DEFAULT_SPEC "-D__APCS_32__"
+
+@@ -89,7 +106,7 @@
+ %{rdynamic:-export-dynamic} \
+ %{!dynamic-linker:-dynamic-linker /lib/ld-linux.so.2} \
+ -X \
+- %{mbig-endian:-EB}" \
++ %{mbig-endian:-EB} %{mlittle-endian:-EL}" \
+ SUBTARGET_EXTRA_LINK_SPEC
+
+ #define TARGET_OS_CPP_BUILTINS() LINUX_TARGET_OS_CPP_BUILTINS()
+diff -urN gcc-3.4.0.orig/gcc/config.gcc gcc-3.4.0/gcc/config.gcc
+--- gcc-3.4.0.orig/gcc/config.gcc 2004-04-17 04:28:24.000000000 +0200
++++ gcc-3.4.0/gcc/config.gcc 2004-07-02 14:44:40.045822542 +0200
+@@ -666,6 +666,11 @@
+ ;;
+ arm*-*-linux*) # ARM GNU/Linux with ELF
+ tm_file="dbxelf.h elfos.h linux.h arm/elf.h arm/linux-gas.h arm/linux-elf.h arm/aout.h arm/arm.h"
++ case $target in
++ arm*b-*)
++ tm_defines="TARGET_BIG_ENDIAN_DEFAULT=1 $tm_defines"
++ ;;
++ esac
+ tmake_file="t-slibgcc-elf-ver t-linux arm/t-linux"
+ extra_parts="crtbegin.o crtbeginS.o crtend.o crtendS.o"
+ gnu_ld=yes
diff --git a/gcc/gcc-3.4.3/gcc-3.4.0-arm-lib1asm.patch b/gcc/gcc-3.4.3/gcc-3.4.0-arm-lib1asm.patch
index e69de29bb2..ca42bfbc0a 100644
--- a/gcc/gcc-3.4.3/gcc-3.4.0-arm-lib1asm.patch
+++ b/gcc/gcc-3.4.3/gcc-3.4.0-arm-lib1asm.patch
@@ -0,0 +1,24 @@
+# Fixes errors like the following when building glibc (or any other executable
+# or shared library) when using gcc 3.4.0 for ARM with softfloat:
+#
+# .../libc_pic.os(.text+0x15834): In function `__modf': undefined reference to `__subdf3'
+# .../libc_pic.os(.text+0x158b8): In function `__modf': undefined reference to `__subdf3'
+# .../libc_pic.os(.text+0x1590c): In function `scalbn': undefined reference to `__muldf3'
+# .../libc_pic.os(.text+0x15e94): In function `__ldexpf': undefined reference to `__eqsf2'
+# .../libc_pic.os(.text+0xcee4c): In function `monstartup': undefined reference to `__fixsfsi'
+
+diff -urNd gcc-3.4.0-orig/gcc/config/arm/t-linux gcc-3.4.0/gcc/config/arm/t-linux
+--- gcc-3.4.0-orig/gcc/config/arm/t-linux 2003-09-20 23:09:07.000000000 +0200
++++ gcc-3.4.0/gcc/config/arm/t-linux 2004-05-01 20:31:59.102846400 +0200
+@@ -4,7 +4,10 @@
+ LIBGCC2_DEBUG_CFLAGS = -g0
+
+ LIB1ASMSRC = arm/lib1funcs.asm
+-LIB1ASMFUNCS = _udivsi3 _divsi3 _umodsi3 _modsi3 _dvmd_lnx
++LIB1ASMFUNCS = _udivsi3 _divsi3 _umodsi3 _modsi3 _dvmd_lnx \
++ _negdf2 _addsubdf3 _muldivdf3 _cmpdf2 _unorddf2 _fixdfsi _fixunsdfsi \
++ _truncdfsf2 _negsf2 _addsubsf3 _muldivsf3 _cmpsf2 _unordsf2 \
++ _fixsfsi _fixunssfsi
+
+ # MULTILIB_OPTIONS = mhard-float/msoft-float
+ # MULTILIB_DIRNAMES = hard-float soft-float
diff --git a/gcc/gcc-3.4.3/gcc-3.4.0-arm-nolibfloat.patch b/gcc/gcc-3.4.3/gcc-3.4.0-arm-nolibfloat.patch
index e69de29bb2..43eed3ef28 100644
--- a/gcc/gcc-3.4.3/gcc-3.4.0-arm-nolibfloat.patch
+++ b/gcc/gcc-3.4.3/gcc-3.4.0-arm-nolibfloat.patch
@@ -0,0 +1,24 @@
+# Dimitry Andric <dimitry@andric.com>, 2004-05-01
+#
+# * Removed the extra -lfloat option from LIBGCC_SPEC, since it isn't needed
+# anymore. (The required functions are now in libgcc.)
+#
+# Fixes errors like
+# arm-softfloat-linux-gnu/3.4.0/../../../../arm-softfloat-linux-gnu/bin/ld: cannot find -lfloat
+# collect2: ld returned 1 exit status
+# make[2]: *** [arm-softfloat-linux-gnu/gcc-3.4.0-glibc-2.3.2/build-glibc/iconvdata/ISO8859-1.so] Error 1
+# when building glibc-2.3.3 with gcc-3.4.0 for arm-softfloat
+
+diff -urNd gcc-3.4.0-orig/gcc/config/arm/linux-elf.h gcc-3.4.0/gcc/config/arm/linux-elf.h
+--- gcc-3.4.0-orig/gcc/config/arm/linux-elf.h 2004-01-31 07:18:11.000000000 +0100
++++ gcc-3.4.0/gcc/config/arm/linux-elf.h 2004-05-01 19:19:06.935979200 +0200
+@@ -55,7 +73,7 @@
+ %{shared:-lc} \
+ %{!shared:%{profile:-lc_p}%{!profile:-lc}}"
+
+-#define LIBGCC_SPEC "%{msoft-float:-lfloat} -lgcc"
++#define LIBGCC_SPEC "-lgcc"
+
+ /* Provide a STARTFILE_SPEC appropriate for GNU/Linux. Here we add
+ the GNU/Linux magical crtbegin.o file (see crtstuff.c) which
+
diff --git a/gcc/gcc-3.4.3/gcc-3.4.0-arm-softfloat.patch b/gcc/gcc-3.4.3/gcc-3.4.0-arm-softfloat.patch
index e69de29bb2..f53d64b374 100644
--- a/gcc/gcc-3.4.3/gcc-3.4.0-arm-softfloat.patch
+++ b/gcc/gcc-3.4.3/gcc-3.4.0-arm-softfloat.patch
@@ -0,0 +1,256 @@
+#
+# Submitted:
+#
+# Dimitry Andric <dimitry@andric.com>, 2004-05-01
+#
+# Description:
+#
+# Nicholas Pitre released this patch for gcc soft-float support here:
+# http://lists.arm.linux.org.uk/pipermail/linux-arm/2003-October/006436.html
+#
+# This version has been adapted to work with gcc 3.4.0.
+#
+# The original patch doesn't distinguish between softfpa and softvfp modes
+# in the way Nicholas Pitre probably meant. His description is:
+#
+# "Default is to use APCS-32 mode with soft-vfp. The old Linux default for
+# floats can be achieved with -mhard-float or with the configure
+# --with-float=hard option. If -msoft-float or --with-float=soft is used then
+# software float support will be used just like the default but with the legacy
+# big endian word ordering for double float representation instead."
+#
+# Which means the following:
+#
+# * If you compile without -mhard-float or -msoft-float, you should get
+# software floating point, using the VFP format. The produced object file
+# should have these flags in its header:
+#
+# private flags = 600: [APCS-32] [VFP float format] [software FP]
+#
+# * If you compile with -mhard-float, you should get hardware floating point,
+# which always uses the FPA format. Object file header flags should be:
+#
+# private flags = 0: [APCS-32] [FPA float format]
+#
+# * If you compile with -msoft-float, you should get software floating point,
+# using the FPA format. This is done for compatibility reasons with many
+# existing distributions. Object file header flags should be:
+#
+# private flags = 200: [APCS-32] [FPA float format] [software FP]
+#
+# The original patch from Nicholas Pitre contained the following constructs:
+#
+# #define SUBTARGET_EXTRA_ASM_SPEC "%{!mcpu=*:-mcpu=xscale} \
+# %{mhard-float:-mfpu=fpa} \
+# %{!mhard-float: %{msoft-float:-mfpu=softfpa;:-mfpu=softvfp}}"
+#
+# However, gcc doesn't accept this ";:" notation, used in the 3rd line. This
+# is probably the reason Robert Schwebel modified it to:
+#
+# #define SUBTARGET_EXTRA_ASM_SPEC "%{!mcpu=*:-mcpu=xscale} \
+# %{mhard-float:-mfpu=fpa} \
+# %{!mhard-float: %{msoft-float:-mfpu=softfpa -mfpu=softvfp}}"
+#
+# But this causes the following behaviour:
+#
+# * If you compile without -mhard-float or -msoft-float, the compiler generates
+# software floating point instructions, but *nothing* is passed to the
+# assembler, which results in an object file which has flags:
+#
+# private flags = 0: [APCS-32] [FPA float format]
+#
+# This is not correct!
+#
+# * If you compile with -mhard-float, the compiler generates hardware floating
+# point instructions, and passes "-mfpu=fpa" to the assembler, which results
+# in an object file which has the same flags as in the previous item, but now
+# those *are* correct.
+#
+# * If you compile with -msoft-float, the compiler generates software floating
+# point instructions, and passes "-mfpu=softfpa -mfpu=softvfp" (in that
+# order) to the assembler, which results in an object file with flags:
+#
+# private flags = 600: [APCS-32] [VFP float format] [software FP]
+#
+# This is not correct, because the last "-mfpu=" option on the assembler
+# command line determines the actual FPU convention used (which should be FPA
+# in this case).
+#
+# Therefore, I modified this patch to get the desired behaviour. Every
+# instance of the notation:
+#
+# %{msoft-float:-mfpu=softfpa -mfpu=softvfp}
+#
+# was changed to:
+#
+# %{msoft-float:-mfpu=softfpa} %{!msoft-float:-mfpu=softvfp}
+#
+# I also did the following:
+#
+# * Modified all TARGET_DEFAULT macros I could find to include ARM_FLAG_VFP, to
+# be consistent with Nicholas' original patch.
+# * Removed any "msoft-float" or "mhard-float" from all MULTILIB_DEFAULTS
+# macros I could find. I think that if you compile without any options, you
+# would like to get the defaults. :)
+# * Removed the extra -lfloat option from LIBGCC_SPEC, since it isn't needed
+# anymore. (The required functions are now in libgcc.)
+
+diff -urNd gcc-3.4.0-orig/gcc/config/arm/coff.h gcc-3.4.0/gcc/config/arm/coff.h
+--- gcc-3.4.0-orig/gcc/config/arm/coff.h 2004-02-24 15:25:22.000000000 +0100
++++ gcc-3.4.0/gcc/config/arm/coff.h 2004-05-01 19:07:06.059409600 +0200
+@@ -31,11 +31,16 @@
+ #define TARGET_VERSION fputs (" (ARM/coff)", stderr)
+
+ #undef TARGET_DEFAULT
+-#define TARGET_DEFAULT (ARM_FLAG_SOFT_FLOAT | ARM_FLAG_APCS_32 | ARM_FLAG_APCS_FRAME | ARM_FLAG_MMU_TRAPS)
++#define TARGET_DEFAULT \
++ ( ARM_FLAG_SOFT_FLOAT \
++ | ARM_FLAG_VFP \
++ | ARM_FLAG_APCS_32 \
++ | ARM_FLAG_APCS_FRAME \
++ | ARM_FLAG_MMU_TRAPS )
+
+ #ifndef MULTILIB_DEFAULTS
+ #define MULTILIB_DEFAULTS \
+- { "marm", "mlittle-endian", "msoft-float", "mapcs-32", "mno-thumb-interwork" }
++ { "marm", "mlittle-endian", "mapcs-32", "mno-thumb-interwork" }
+ #endif
+
+ /* This is COFF, but prefer stabs. */
+diff -urNd gcc-3.4.0-orig/gcc/config/arm/elf.h gcc-3.4.0/gcc/config/arm/elf.h
+--- gcc-3.4.0-orig/gcc/config/arm/elf.h 2004-02-24 15:25:22.000000000 +0100
++++ gcc-3.4.0/gcc/config/arm/elf.h 2004-05-01 19:12:16.976486400 +0200
+@@ -46,7 +46,9 @@
+
+ #ifndef SUBTARGET_ASM_FLOAT_SPEC
+ #define SUBTARGET_ASM_FLOAT_SPEC "\
+-%{mapcs-float:-mfloat} %{msoft-float:-mfpu=softfpa}"
++%{mapcs-float:-mfloat} \
++%{mhard-float:-mfpu=fpa} \
++%{!mhard-float: %{msoft-float:-mfpu=softfpa} %{!msoft-float:-mfpu=softvfp}}"
+ #endif
+
+ #ifndef ASM_SPEC
+@@ -106,12 +108,17 @@
+ #endif
+
+ #ifndef TARGET_DEFAULT
+-#define TARGET_DEFAULT (ARM_FLAG_SOFT_FLOAT | ARM_FLAG_APCS_32 | ARM_FLAG_APCS_FRAME | ARM_FLAG_MMU_TRAPS)
++#define TARGET_DEFAULT \
++ ( ARM_FLAG_SOFT_FLOAT \
++ | ARM_FLAG_VFP \
++ | ARM_FLAG_APCS_32 \
++ | ARM_FLAG_APCS_FRAME \
++ | ARM_FLAG_MMU_TRAPS )
+ #endif
+
+ #ifndef MULTILIB_DEFAULTS
+ #define MULTILIB_DEFAULTS \
+- { "marm", "mlittle-endian", "msoft-float", "mapcs-32", "mno-thumb-interwork", "fno-leading-underscore" }
++ { "marm", "mlittle-endian", "mapcs-32", "mno-thumb-interwork", "fno-leading-underscore" }
+ #endif
+
+ #define TARGET_ASM_FILE_START_APP_OFF true
+diff -urNd gcc-3.4.0-orig/gcc/config/arm/linux-elf.h gcc-3.4.0/gcc/config/arm/linux-elf.h
+--- gcc-3.4.0-orig/gcc/config/arm/linux-elf.h 2004-01-31 07:18:11.000000000 +0100
++++ gcc-3.4.0/gcc/config/arm/linux-elf.h 2004-05-01 19:19:06.935979200 +0200
+@@ -30,9 +30,27 @@
+ /* Do not assume anything about header files. */
+ #define NO_IMPLICIT_EXTERN_C
+
+-/* Default is to use APCS-32 mode. */
++/*
++ * Default is to use APCS-32 mode with soft-vfp.
++ * The old Linux default for floats can be achieved with -mhard-float
++ * or with the configure --with-float=hard option.
++ * If -msoft-float or --with-float=soft is used then software float
++ * support will be used just like the default but with the legacy
++ * big endian word ordering for double float representation instead.
++ */
++
+ #undef TARGET_DEFAULT
+-#define TARGET_DEFAULT (ARM_FLAG_APCS_32 | ARM_FLAG_MMU_TRAPS)
++#define TARGET_DEFAULT \
++ ( ARM_FLAG_APCS_32 \
++ | ARM_FLAG_SOFT_FLOAT \
++ | ARM_FLAG_VFP \
++ | ARM_FLAG_MMU_TRAPS )
++
++#undef SUBTARGET_EXTRA_ASM_SPEC
++#define SUBTARGET_EXTRA_ASM_SPEC "\
++%{!mcpu=*:-mcpu=xscale} \
++%{mhard-float:-mfpu=fpa} \
++%{!mhard-float: %{msoft-float:-mfpu=softfpa} %{!msoft-float:-mfpu=softvfp}}"
+
+ #define SUBTARGET_CPU_DEFAULT TARGET_CPU_arm6
+
+@@ -40,7 +58,7 @@
+
+ #undef MULTILIB_DEFAULTS
+ #define MULTILIB_DEFAULTS \
+- { "marm", "mlittle-endian", "mhard-float", "mapcs-32", "mno-thumb-interwork" }
++ { "marm", "mlittle-endian", "mapcs-32", "mno-thumb-interwork" }
+
+ #define CPP_APCS_PC_DEFAULT_SPEC "-D__APCS_32__"
+
+@@ -55,7 +73,7 @@
+ %{shared:-lc} \
+ %{!shared:%{profile:-lc_p}%{!profile:-lc}}"
+
+-#define LIBGCC_SPEC "%{msoft-float:-lfloat} -lgcc"
++#define LIBGCC_SPEC "-lgcc"
+
+ /* Provide a STARTFILE_SPEC appropriate for GNU/Linux. Here we add
+ the GNU/Linux magical crtbegin.o file (see crtstuff.c) which
+diff -urNd gcc-3.4.0-orig/gcc/config/arm/t-linux gcc-3.4.0/gcc/config/arm/t-linux
+--- gcc-3.4.0-orig/gcc/config/arm/t-linux 2003-09-20 23:09:07.000000000 +0200
++++ gcc-3.4.0/gcc/config/arm/t-linux 2004-05-01 20:31:59.102846400 +0200
+@@ -4,7 +4,10 @@
+ LIBGCC2_DEBUG_CFLAGS = -g0
+
+ LIB1ASMSRC = arm/lib1funcs.asm
+-LIB1ASMFUNCS = _udivsi3 _divsi3 _umodsi3 _modsi3 _dvmd_lnx
++LIB1ASMFUNCS = _udivsi3 _divsi3 _umodsi3 _modsi3 _dvmd_lnx \
++ _negdf2 _addsubdf3 _muldivdf3 _cmpdf2 _unorddf2 _fixdfsi _fixunsdfsi \
++ _truncdfsf2 _negsf2 _addsubsf3 _muldivsf3 _cmpsf2 _unordsf2 \
++ _fixsfsi _fixunssfsi
+
+ # MULTILIB_OPTIONS = mhard-float/msoft-float
+ # MULTILIB_DIRNAMES = hard-float soft-float
+diff -urNd gcc-3.4.0-orig/gcc/config/arm/unknown-elf.h gcc-3.4.0/gcc/config/arm/unknown-elf.h
+--- gcc-3.4.0-orig/gcc/config/arm/unknown-elf.h 2004-02-24 15:25:22.000000000 +0100
++++ gcc-3.4.0/gcc/config/arm/unknown-elf.h 2004-05-01 19:09:09.016212800 +0200
+@@ -30,7 +30,12 @@
+
+ /* Default to using APCS-32 and software floating point. */
+ #ifndef TARGET_DEFAULT
+-#define TARGET_DEFAULT (ARM_FLAG_SOFT_FLOAT | ARM_FLAG_APCS_32 | ARM_FLAG_APCS_FRAME | ARM_FLAG_MMU_TRAPS)
++#define TARGET_DEFAULT \
++ ( ARM_FLAG_SOFT_FLOAT \
++ | ARM_FLAG_VFP \
++ | ARM_FLAG_APCS_32 \
++ | ARM_FLAG_APCS_FRAME \
++ | ARM_FLAG_MMU_TRAPS )
+ #endif
+
+ /* Now we define the strings used to build the spec file. */
+diff -urNd gcc-3.4.0-orig/gcc/config/arm/xscale-elf.h gcc-3.4.0/gcc/config/arm/xscale-elf.h
+--- gcc-3.4.0-orig/gcc/config/arm/xscale-elf.h 2003-07-02 01:26:43.000000000 +0200
++++ gcc-3.4.0/gcc/config/arm/xscale-elf.h 2004-05-01 20:15:36.620105600 +0200
+@@ -49,11 +49,12 @@
+ endian, regardless of the endian-ness of the memory
+ system. */
+
+-#define SUBTARGET_EXTRA_ASM_SPEC "%{!mcpu=*:-mcpu=xscale} \
+- %{mhard-float:-mfpu=fpa} \
+- %{!mhard-float: %{msoft-float:-mfpu=softfpa;:-mfpu=softvfp}}"
++#define SUBTARGET_EXTRA_ASM_SPEC "\
++%{!mcpu=*:-mcpu=xscale} \
++%{mhard-float:-mfpu=fpa} \
++%{!mhard-float: %{msoft-float:-mfpu=softfpa} %{!msoft-float:-mfpu=softvfp}}"
+
+ #ifndef MULTILIB_DEFAULTS
+ #define MULTILIB_DEFAULTS \
+- { "mlittle-endian", "mno-thumb-interwork", "marm", "msoft-float" }
++ { "mlittle-endian", "mno-thumb-interwork", "marm" }
+ #endif
diff --git a/gcc/gcc-3.4.3/gcc-3.4.1-uclibc-100-conf.patch b/gcc/gcc-3.4.3/gcc-3.4.1-uclibc-100-conf.patch
index e69de29bb2..64e0dcae04 100644
--- a/gcc/gcc-3.4.3/gcc-3.4.1-uclibc-100-conf.patch
+++ b/gcc/gcc-3.4.3/gcc-3.4.1-uclibc-100-conf.patch
@@ -0,0 +1,410 @@
+diff -urN gcc-3.4.1-dist/boehm-gc/ltconfig gcc-3.4.1/boehm-gc/ltconfig
+--- gcc-3.4.1-dist/boehm-gc/ltconfig 2002-11-20 09:59:06.000000000 -0600
++++ gcc-3.4.1/boehm-gc/ltconfig 2004-07-28 12:23:42.000000000 -0500
+@@ -1981,6 +1981,23 @@
+ fi
+ ;;
+
++linux-uclibc*)
++ version_type=linux
++ need_lib_prefix=no
++ need_version=no
++ library_names_spec='${libname}${release}.so$versuffix ${libname}${release}.so$major $libname.so'
++ soname_spec='${libname}${release}.so$major'
++ finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir'
++ shlibpath_var=LD_LIBRARY_PATH
++ shlibpath_overrides_runpath=no
++ deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [LM]SB (shared object|dynamic lib )'
++ file_magic_cmd=/usr/bin/file
++ file_magic_test_file=`echo /lib/libuClibc-*.so`
++
++ # Assume using the uClibc dynamic linker.
++ dynamic_linker="uClibc ld.so"
++ ;;
++
+ netbsd*)
+ version_type=sunos
+ if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then
+diff -urN gcc-3.4.1-dist/gcc/config/arm/linux-elf.h gcc-3.4.1/gcc/config/arm/linux-elf.h
+--- gcc-3.4.1-dist/gcc/config/arm/linux-elf.h 2004-01-31 00:18:11.000000000 -0600
++++ gcc-3.4.1/gcc/config/arm/linux-elf.h 2004-07-28 12:11:47.000000000 -0500
+@@ -81,6 +81,18 @@
+ "%{!shared:crtend.o%s} %{shared:crtendS.o%s} crtn.o%s"
+
+ #undef LINK_SPEC
++#ifdef USE_UCLIBC
++#define LINK_SPEC "%{h*} %{version:-v} \
++ %{b} %{Wl,*:%*} \
++ %{static:-Bstatic} \
++ %{shared:-shared} \
++ %{symbolic:-Bsymbolic} \
++ %{rdynamic:-export-dynamic} \
++ %{!dynamic-linker:-dynamic-linker /lib/ld-uClibc.so.0} \
++ -X \
++ %{mbig-endian:-EB}" \
++ SUBTARGET_EXTRA_LINK_SPEC
++#else
+ #define LINK_SPEC "%{h*} %{version:-v} \
+ %{b} %{Wl,*:%*} \
+ %{static:-Bstatic} \
+@@ -91,6 +103,7 @@
+ -X \
+ %{mbig-endian:-EB}" \
+ SUBTARGET_EXTRA_LINK_SPEC
++#endif
+
+ #define TARGET_OS_CPP_BUILTINS() LINUX_TARGET_OS_CPP_BUILTINS()
+
+diff -urN gcc-3.4.1-dist/gcc/config/cris/linux.h gcc-3.4.1/gcc/config/cris/linux.h
+--- gcc-3.4.1-dist/gcc/config/cris/linux.h 2003-11-28 21:08:09.000000000 -0600
++++ gcc-3.4.1/gcc/config/cris/linux.h 2004-07-28 12:11:47.000000000 -0500
+@@ -79,6 +79,25 @@
+ #undef CRIS_DEFAULT_CPU_VERSION
+ #define CRIS_DEFAULT_CPU_VERSION CRIS_CPU_NG
+
++#ifdef USE_UCLIBC
++
++#undef CRIS_SUBTARGET_VERSION
++#define CRIS_SUBTARGET_VERSION " - cris-axis-linux-uclibc"
++
++#undef CRIS_LINK_SUBTARGET_SPEC
++#define CRIS_LINK_SUBTARGET_SPEC \
++ "-mcrislinux\
++ -rpath-link include/asm/../..%s\
++ %{shared} %{static}\
++ %{symbolic:-Bdynamic} %{shlib:-Bdynamic} %{static:-Bstatic}\
++ %{!shared: \
++ %{!static: \
++ %{rdynamic:-export-dynamic} \
++ %{!dynamic-linker:-dynamic-linker /lib/ld-uClibc.so.0}}} \
++ %{!r:%{O2|O3: --gc-sections}}"
++
++#else /* USE_UCLIBC */
++
+ #undef CRIS_SUBTARGET_VERSION
+ #define CRIS_SUBTARGET_VERSION " - cris-axis-linux-gnu"
+
+@@ -93,6 +112,8 @@
+ %{!shared:%{!static:%{rdynamic:-export-dynamic}}}\
+ %{!r:%{O2|O3: --gc-sections}}"
+
++#endif /* USE_UCLIBC */
++
+
+ /* Node: Run-time Target */
+
+diff -urN gcc-3.4.1-dist/gcc/config/cris/t-linux-uclibc gcc-3.4.1/gcc/config/cris/t-linux-uclibc
+--- gcc-3.4.1-dist/gcc/config/cris/t-linux-uclibc 1969-12-31 18:00:00.000000000 -0600
++++ gcc-3.4.1/gcc/config/cris/t-linux-uclibc 2004-07-28 12:11:47.000000000 -0500
+@@ -0,0 +1,3 @@
++T_CFLAGS = -DUSE_UCLIBC
++TARGET_LIBGCC2_CFLAGS += -fPIC
++CRTSTUFF_T_CFLAGS_S = $(TARGET_LIBGCC2_CFLAGS)
+diff -urN gcc-3.4.1-dist/gcc/config/i386/linux.h gcc-3.4.1/gcc/config/i386/linux.h
+--- gcc-3.4.1-dist/gcc/config/i386/linux.h 2003-11-28 21:08:10.000000000 -0600
++++ gcc-3.4.1/gcc/config/i386/linux.h 2004-07-28 12:11:47.000000000 -0500
+@@ -118,6 +118,15 @@
+ %{!dynamic-linker:-dynamic-linker /lib/ld-linux.so.1}} \
+ %{static:-static}}}"
+ #else
++#if defined USE_UCLIBC
++#define LINK_SPEC "-m elf_i386 %{shared:-shared} \
++ %{!shared: \
++ %{!ibcs: \
++ %{!static: \
++ %{rdynamic:-export-dynamic} \
++ %{!dynamic-linker:-dynamic-linker /lib/ld-uClibc.so.0}} \
++ %{static:-static}}}"
++#else
+ #define LINK_SPEC "-m elf_i386 %{shared:-shared} \
+ %{!shared: \
+ %{!ibcs: \
+@@ -126,6 +135,7 @@
+ %{!dynamic-linker:-dynamic-linker /lib/ld-linux.so.2}} \
+ %{static:-static}}}"
+ #endif
++#endif
+
+ /* A C statement (sans semicolon) to output to the stdio stream
+ FILE the assembler definition of uninitialized global DECL named
+diff -urN gcc-3.4.1-dist/gcc/config/mips/linux.h gcc-3.4.1/gcc/config/mips/linux.h
+--- gcc-3.4.1-dist/gcc/config/mips/linux.h 2004-06-15 20:42:24.000000000 -0500
++++ gcc-3.4.1/gcc/config/mips/linux.h 2004-07-28 12:11:47.000000000 -0500
+@@ -109,6 +109,17 @@
+
+ /* Borrowed from sparc/linux.h */
+ #undef LINK_SPEC
++#ifdef USE_UCLIBC
++#define LINK_SPEC \
++ "%(endian_spec) \
++ %{shared:-shared} \
++ %{!shared: \
++ %{!ibcs: \
++ %{!static: \
++ %{rdynamic:-export-dynamic} \
++ %{!dynamic-linker:-dynamic-linker /lib/ld-uClibc.so.0}} \
++ %{static:-static}}}"
++#else
+ #define LINK_SPEC \
+ "%(endian_spec) \
+ %{shared:-shared} \
+@@ -118,6 +129,7 @@
+ %{rdynamic:-export-dynamic} \
+ %{!dynamic-linker:-dynamic-linker /lib/ld.so.1}} \
+ %{static:-static}}}"
++#endif
+
+ #undef SUBTARGET_ASM_SPEC
+ #define SUBTARGET_ASM_SPEC "\
+diff -urN gcc-3.4.1-dist/gcc/config/rs6000/linux.h gcc-3.4.1/gcc/config/rs6000/linux.h
+--- gcc-3.4.1-dist/gcc/config/rs6000/linux.h 2004-02-25 09:11:19.000000000 -0600
++++ gcc-3.4.1/gcc/config/rs6000/linux.h 2004-07-28 12:11:47.000000000 -0500
+@@ -61,7 +61,11 @@
+ #define LINK_START_DEFAULT_SPEC "%(link_start_linux)"
+
+ #undef LINK_OS_DEFAULT_SPEC
++#ifdef USE_UCLIBC
++#define LINK_OS_DEFAULT_SPEC "%(link_os_linux_uclibc)"
++#else
+ #define LINK_OS_DEFAULT_SPEC "%(link_os_linux)"
++#endif
+
+ #define LINK_GCC_C_SEQUENCE_SPEC \
+ "%{static:--start-group} %G %L %{static:--end-group}%{!static:%G}"
+diff -urN gcc-3.4.1-dist/gcc/config/rs6000/sysv4.h gcc-3.4.1/gcc/config/rs6000/sysv4.h
+--- gcc-3.4.1-dist/gcc/config/rs6000/sysv4.h 2004-06-10 01:39:50.000000000 -0500
++++ gcc-3.4.1/gcc/config/rs6000/sysv4.h 2004-07-28 12:11:47.000000000 -0500
+@@ -947,6 +947,7 @@
+ mcall-linux : %(link_os_linux) ; \
+ mcall-gnu : %(link_os_gnu) ; \
+ mcall-netbsd : %(link_os_netbsd) ; \
++ mcall-linux-uclibc : %(link_os_linux_uclibc); \
+ mcall-openbsd: %(link_os_openbsd) ; \
+ : %(link_os_default) }"
+
+@@ -1124,6 +1125,10 @@
+ %{rdynamic:-export-dynamic} \
+ %{!dynamic-linker:-dynamic-linker /lib/ld.so.1}}}"
+
++#define LINK_OS_LINUX_UCLIBC_SPEC "-m elf32ppclinux %{!shared: %{!static: \
++ %{rdynamic:-export-dynamic} \
++ %{!dynamic-linker:-dynamic-linker /lib/ld-uClibc.so.0}}}"
++
+ #if defined(HAVE_LD_EH_FRAME_HDR)
+ # define LINK_EH_SPEC "%{!static:--eh-frame-hdr} "
+ #endif
+@@ -1290,6 +1295,7 @@
+ { "link_os_sim", LINK_OS_SIM_SPEC }, \
+ { "link_os_freebsd", LINK_OS_FREEBSD_SPEC }, \
+ { "link_os_linux", LINK_OS_LINUX_SPEC }, \
++ { "link_os_linux_uclibc", LINK_OS_LINUX_UCLIBC_SPEC }, \
+ { "link_os_gnu", LINK_OS_GNU_SPEC }, \
+ { "link_os_netbsd", LINK_OS_NETBSD_SPEC }, \
+ { "link_os_openbsd", LINK_OS_OPENBSD_SPEC }, \
+diff -urN gcc-3.4.1-dist/gcc/config/sh/linux.h gcc-3.4.1/gcc/config/sh/linux.h
+--- gcc-3.4.1-dist/gcc/config/sh/linux.h 2004-01-11 20:29:13.000000000 -0600
++++ gcc-3.4.1/gcc/config/sh/linux.h 2004-07-28 12:11:47.000000000 -0500
+@@ -73,12 +73,21 @@
+ #undef SUBTARGET_LINK_EMUL_SUFFIX
+ #define SUBTARGET_LINK_EMUL_SUFFIX "_linux"
+ #undef SUBTARGET_LINK_SPEC
++#ifdef USE_UCLIBC
++#define SUBTARGET_LINK_SPEC \
++ "%{shared:-shared} \
++ %{!static: \
++ %{rdynamic:-export-dynamic} \
++ %{!dynamic-linker:-dynamic-linker /lib/ld-uClibc.so.0}} \
++ %{static:-static}"
++#else
+ #define SUBTARGET_LINK_SPEC \
+ "%{shared:-shared} \
+ %{!static: \
+ %{rdynamic:-export-dynamic} \
+ %{!dynamic-linker:-dynamic-linker /lib/ld-linux.so.2}} \
+ %{static:-static}"
++#endif
+
+ #undef LIB_SPEC
+ #define LIB_SPEC \
+diff -urN gcc-3.4.1-dist/gcc/config/sh/t-linux-uclibc gcc-3.4.1/gcc/config/sh/t-linux-uclibc
+--- gcc-3.4.1-dist/gcc/config/sh/t-linux-uclibc 1969-12-31 18:00:00.000000000 -0600
++++ gcc-3.4.1/gcc/config/sh/t-linux-uclibc 2004-07-28 12:11:47.000000000 -0500
+@@ -0,0 +1,13 @@
++T_CFLAGS = -DUSE_UCLIBC
++
++TARGET_LIBGCC2_CFLAGS = -fpic -DNO_FPSCR_VALUES
++LIB1ASMFUNCS_CACHE = _ic_invalidate
++
++LIB2FUNCS_EXTRA=
++
++MULTILIB_OPTIONS= $(MULTILIB_ENDIAN) m3e/m4
++MULTILIB_DIRNAMES=
++MULTILIB_MATCHES =
++MULTILIB_EXCEPTIONS=
++
++EXTRA_MULTILIB_PARTS= crtbegin.o crtend.o crtbeginS.o crtendS.o crtbeginT.o
+diff -urN gcc-3.4.1-dist/gcc/config/sh/t-sh64-uclibc gcc-3.4.1/gcc/config/sh/t-sh64-uclibc
+--- gcc-3.4.1-dist/gcc/config/sh/t-sh64-uclibc 1969-12-31 18:00:00.000000000 -0600
++++ gcc-3.4.1/gcc/config/sh/t-sh64-uclibc 2004-07-28 12:11:47.000000000 -0500
+@@ -0,0 +1,13 @@
++EXTRA_MULTILIB_PARTS= crt1.o crti.o crtn.o crtbegin.o crtend.o
++
++LIB1ASMFUNCS = \
++ _sdivsi3 _sdivsi3_i4 _udivsi3 _udivsi3_i4 _set_fpscr \
++ _shcompact_call_trampoline _shcompact_return_trampoline \
++ _shcompact_incoming_args _ic_invalidate _nested_trampoline \
++ _push_pop_shmedia_regs \
++ _udivdi3 _divdi3 _umoddi3 _moddi3
++
++MULTILIB_OPTIONS = $(MULTILIB_ENDIAN) m5-32media-nofpu/m5-compact/m5-compact-nofpu/m5-64media/m5-64media-nofpu
++MULTILIB_DIRNAMES= $(MULTILIB_ENDIAN) nofpu compact nofpu/compact media64 nofpu/media64
++MULTILIB_MATCHES=
++MULTILIB_EXCEPTIONS=
+diff -urN gcc-3.4.1-dist/gcc/config/t-linux-uclibc gcc-3.4.1/gcc/config/t-linux-uclibc
+--- gcc-3.4.1-dist/gcc/config/t-linux-uclibc 1969-12-31 18:00:00.000000000 -0600
++++ gcc-3.4.1/gcc/config/t-linux-uclibc 2004-07-28 12:11:47.000000000 -0500
+@@ -0,0 +1,15 @@
++T_CFLAGS = -DUSE_UCLIBC
++
++# Compile crtbeginS.o and crtendS.o with pic.
++CRTSTUFF_T_CFLAGS_S = $(CRTSTUFF_T_CFLAGS) -fPIC
++# Compile libgcc2.a with pic.
++TARGET_LIBGCC2_CFLAGS = -fPIC
++
++# Override t-slibgcc-elf-ver to export some libgcc symbols with
++# the symbol versions that glibc used.
++SHLIB_MAPFILES += $(srcdir)/config/libgcc-glibc.ver
++
++# Use unwind-dw2-fde
++LIB2ADDEH = $(srcdir)/unwind-dw2.c $(srcdir)/unwind-dw2-fde.c \
++ $(srcdir)/unwind-sjlj.c $(srcdir)/gthr-gnat.c $(srcdir)/unwind-c.c
++LIB2ADDEHDEP = unwind.inc unwind-dw2-fde.h unwind-dw2-fde.c gthr-gnat.c
+diff -urN gcc-3.4.1-dist/gcc/config.gcc gcc-3.4.1/gcc/config.gcc
+--- gcc-3.4.1-dist/gcc/config.gcc 2004-04-21 10:12:35.000000000 -0500
++++ gcc-3.4.1/gcc/config.gcc 2004-07-28 12:11:48.000000000 -0500
+@@ -664,6 +664,12 @@
+ extra_parts=""
+ use_collect2=yes
+ ;;
++arm*-*-linux-uclibc*) # ARM GNU/Linux with ELF - uClibc
++ tm_file="dbxelf.h elfos.h linux.h arm/elf.h arm/linux-gas.h arm/linux-elf.h arm/aout.h arm/arm.h"
++ tmake_file="t-slibgcc-elf-ver t-linux-uclibc arm/t-linux"
++ extra_parts="crtbegin.o crtbeginS.o crtend.o crtendS.o"
++ gnu_ld=yes
++ ;;
+ arm*-*-linux*) # ARM GNU/Linux with ELF
+ tm_file="dbxelf.h elfos.h linux.h arm/elf.h arm/linux-gas.h arm/linux-elf.h arm/aout.h arm/arm.h"
+ tmake_file="t-slibgcc-elf-ver t-linux arm/t-linux"
+@@ -725,6 +731,10 @@
+ tmake_file="cris/t-cris cris/t-elfmulti"
+ gas=yes
+ ;;
++cris-*-linux-uclibc*)
++ tm_file="dbxelf.h elfos.h svr4.h ${tm_file} linux.h cris/linux.h"
++ tmake_file="cris/t-cris t-slibgcc-elf-ver cris/t-linux-uclibc"
++ ;;
+ cris-*-linux*)
+ tm_file="dbxelf.h elfos.h svr4.h ${tm_file} linux.h cris/linux.h"
+ tmake_file="cris/t-cris t-slibgcc-elf-ver cris/t-linux"
+@@ -988,6 +998,11 @@
+ thread_file='single'
+ fi
+ ;;
++i[34567]86-*-linux*uclibc*) # Intel 80386's running GNU/Linux
++ # with ELF format using uClibc
++ tm_file="${tm_file} i386/unix.h i386/att.h dbxelf.h elfos.h svr4.h linux.h i386/linux.h"
++ tmake_file="t-slibgcc-elf-ver t-linux-uclibc i386/t-crtstuff"
++ ;;
+ i[34567]86-*-linux*) # Intel 80386's running GNU/Linux
+ # with ELF format using glibc 2
+ # aka GNU/Linux C library 6
+@@ -1547,6 +1562,16 @@
+ gnu_ld=yes
+ gas=yes
+ ;;
++mips*-*-linux-uclibc*) # Linux MIPS, either endian. uClibc
++ tm_file="dbxelf.h elfos.h svr4.h linux.h ${tm_file} mips/linux.h"
++ case ${target} in
++ mipsisa32*-*)
++ target_cpu_default="MASK_SOFT_FLOAT"
++ tm_defines="MIPS_ISA_DEFAULT=32"
++ ;;
++ esac
++ tmake_file="t-slibgcc-elf-ver t-linux-uclibc mips/t-linux"
++ ;;
+ mips*-*-linux*) # Linux MIPS, either endian.
+ tm_file="dbxelf.h elfos.h svr4.h linux.h ${tm_file} mips/linux.h"
+ case ${target} in
+@@ -1764,6 +1789,10 @@
+ tm_file="${tm_file} dbxelf.h elfos.h svr4.h freebsd-spec.h rs6000/sysv4.h rs6000/linux.h rs6000/linuxspe.h"
+ tmake_file="rs6000/t-fprules rs6000/t-ppcos t-slibgcc-elf-ver t-linux rs6000/t-ppccomm"
+ ;;
++powerpc-*-linux-uclibc*)
++ tm_file="${tm_file} dbxelf.h elfos.h svr4.h freebsd-spec.h rs6000/sysv4.h rs6000/linux.h"
++ tmake_file="rs6000/t-fprules rs6000/t-ppcos t-slibgcc-elf-ver t-linux-uclibc rs6000/t-ppccomm"
++ ;;
+ powerpc-*-linux*)
+ tm_file="${tm_file} dbxelf.h elfos.h svr4.h freebsd-spec.h rs6000/sysv4.h rs6000/linux.h"
+ tmake_file="rs6000/t-fprules rs6000/t-ppcos t-slibgcc-elf-ver t-linux rs6000/t-ppccomm"
+@@ -1924,9 +1953,17 @@
+ tmake_file="${tmake_file} sh/t-le"
+ ;;
+ esac
+- tmake_file="${tmake_file} sh/t-linux"
++ case ${target} in
++ *-*-linux-uclibc*) tmake_file="${tmake_file} sh/t-linux-uclibc" ;;
++ *) tmake_file="${tmake_file} sh/t-linux" ;;
++ esac
+ tm_file="${tm_file} dbxelf.h elfos.h svr4.h sh/elf.h sh/linux.h"
+ case ${target} in
++ sh64*-*-linux-uclibc*)
++ tmake_file="${tmake_file} sh/t-sh64-uclibc"
++ tm_file="${tm_file} sh/sh64.h"
++ extra_headers="shmedia.h ushmedia.h sshmedia.h"
++ ;;
+ sh64*)
+ tmake_file="${tmake_file} sh/t-sh64"
+ tm_file="${tm_file} sh/sh64.h"
+diff -urN gcc-3.4.1-dist/libtool.m4 gcc-3.4.1/libtool.m4
+--- gcc-3.4.1-dist/libtool.m4 2004-05-18 04:08:37.000000000 -0500
++++ gcc-3.4.1/libtool.m4 2004-07-28 12:28:43.000000000 -0500
+@@ -689,6 +689,11 @@
+ lt_cv_file_magic_test_file=`echo /lib/libc.so* /lib/libc-*.so`
+ ;;
+
++linux-uclibc*)
++ lt_cv_deplibs_check_method=pass_all
++ lt_cv_file_magic_test_file=`echo /lib/libuClibc-*.so`
++ ;;
++
+ netbsd*)
+ if echo __ELF__ | $CC -E - | grep __ELF__ > /dev/null; then
+ [lt_cv_deplibs_check_method='match_pattern /lib[^/\.]+\.so\.[0-9]+\.[0-9]+$']
+diff -urN gcc-3.4.1-dist/ltconfig gcc-3.4.1/ltconfig
+--- gcc-3.4.1-dist/ltconfig 2004-03-05 15:05:41.000000000 -0600
++++ gcc-3.4.1/ltconfig 2004-07-28 12:18:22.000000000 -0500
+@@ -1262,6 +1262,24 @@
+ dynamic_linker='GNU/Linux ld.so'
+ ;;
+
++linux-uclibc*)
++ version_type=linux
++ need_lib_prefix=no
++ need_version=no
++ library_names_spec='${libname}${release}.so$versuffix ${libname}${release}.so$major $libname.so'
++ soname_spec='${libname}${release}.so$major'
++ finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir'
++ shlibpath_var=LD_LIBRARY_PATH
++ shlibpath_overrides_runpath=no
++ # This implies no fast_install, which is unacceptable.
++ # Some rework will be needed to allow for fast_install
++ # before this can be enabled.
++ # Note: copied from linux-gnu, and may not be appropriate.
++ hardcode_into_libs=yes
++ # Assume using the uClibc dynamic linker.
++ dynamic_linker="uClibc ld.so"
++ ;;
++
+ netbsd*)
+ need_lib_prefix=no
+ need_version=no
diff --git a/gcc/gcc-3.4.3/gcc-3.4.1-uclibc-200-locale.patch b/gcc/gcc-3.4.3/gcc-3.4.1-uclibc-200-locale.patch
index e69de29bb2..d70d20b508 100644
--- a/gcc/gcc-3.4.3/gcc-3.4.1-uclibc-200-locale.patch
+++ b/gcc/gcc-3.4.3/gcc-3.4.1-uclibc-200-locale.patch
@@ -0,0 +1,3216 @@
+diff -urN gcc-3.4.1-dist/libstdc++-v3/acinclude.m4 gcc-3.4.1/libstdc++-v3/acinclude.m4
+--- gcc-3.4.1-dist/libstdc++-v3/acinclude.m4 2004-05-14 05:53:11.000000000 -0500
++++ gcc-3.4.1/libstdc++-v3/acinclude.m4 2004-07-28 13:23:10.000000000 -0500
+@@ -996,7 +996,7 @@
+ AC_MSG_CHECKING([for C locale to use])
+ GLIBCXX_ENABLE(clocale,auto,[@<:@=MODEL@:>@],
+ [use MODEL for target locale package],
+- [permit generic|gnu|ieee_1003.1-2001|yes|no|auto])
++ [permit generic|gnu|ieee_1003.1-2001|uclibc|yes|no|auto])
+
+ # If they didn't use this option switch, or if they specified --enable
+ # with no specific model, we'll have to look for one. If they
+@@ -1012,6 +1012,9 @@
+ # Default to "generic".
+ if test $enable_clocale_flag = auto; then
+ case x${target_os} in
++ x*-uclibc*)
++ enable_clocale_flag=uclibc
++ ;;
+ xlinux* | xgnu* | xkfreebsd*-gnu | xknetbsd*-gnu)
+ AC_EGREP_CPP([_GLIBCXX_ok], [
+ #include <features.h>
+@@ -1138,6 +1141,41 @@
+ CTIME_CC=config/locale/generic/time_members.cc
+ CLOCALE_INTERNAL_H=config/locale/generic/c++locale_internal.h
+ ;;
++ uclibc)
++ AC_MSG_RESULT(uclibc)
++
++ # Declare intention to use gettext, and add support for specific
++ # languages.
++ # For some reason, ALL_LINGUAS has to be before AM-GNU-GETTEXT
++ ALL_LINGUAS="de fr"
++
++ # Don't call AM-GNU-GETTEXT here. Instead, assume glibc.
++ AC_CHECK_PROG(check_msgfmt, msgfmt, yes, no)
++ if test x"$check_msgfmt" = x"yes" && test x"$enable_nls" = x"yes"; then
++ USE_NLS=yes
++ fi
++ # Export the build objects.
++ for ling in $ALL_LINGUAS; do \
++ glibcxx_MOFILES="$glibcxx_MOFILES $ling.mo"; \
++ glibcxx_POFILES="$glibcxx_POFILES $ling.po"; \
++ done
++ AC_SUBST(glibcxx_MOFILES)
++ AC_SUBST(glibcxx_POFILES)
++
++ CLOCALE_H=config/locale/uclibc/c_locale.h
++ CLOCALE_CC=config/locale/uclibc/c_locale.cc
++ CCODECVT_H=config/locale/ieee_1003.1-2001/codecvt_specializations.h
++ CCODECVT_CC=config/locale/uclibc/codecvt_members.cc
++ CCOLLATE_CC=config/locale/uclibc/collate_members.cc
++ CCTYPE_CC=config/locale/uclibc/ctype_members.cc
++ CMESSAGES_H=config/locale/uclibc/messages_members.h
++ CMESSAGES_CC=config/locale/uclibc/messages_members.cc
++ CMONEY_CC=config/locale/uclibc/monetary_members.cc
++ CNUMERIC_CC=config/locale/uclibc/numeric_members.cc
++ CTIME_H=config/locale/uclibc/time_members.h
++ CTIME_CC=config/locale/uclibc/time_members.cc
++ CLOCALE_INTERNAL_H=config/locale/uclibc/c++locale_internal.h
++ ;;
+ esac
+
+ # This is where the testsuite looks for locale catalogs, using the
+diff -urN gcc-3.4.1-dist/libstdc++-v3/aclocal.m4 gcc-3.4.1/libstdc++-v3/aclocal.m4
+--- gcc-3.4.1-dist/libstdc++-v3/aclocal.m4 2004-05-15 15:43:59.000000000 -0500
++++ gcc-3.4.1/libstdc++-v3/aclocal.m4 2004-07-28 13:23:11.000000000 -0500
+@@ -1025,6 +1025,9 @@
+ # Default to "generic".
+ if test $enable_clocale_flag = auto; then
+ case x${target_os} in
++ x*-uclibc*)
++ enable_clocale_flag=uclibc
++ ;;
+ xlinux* | xgnu* | xkfreebsd*-gnu | xknetbsd*-gnu)
+ AC_EGREP_CPP([_GLIBCXX_ok], [
+ #include <features.h>
+@@ -1151,6 +1154,41 @@
+ CTIME_CC=config/locale/generic/time_members.cc
+ CLOCALE_INTERNAL_H=config/locale/generic/c++locale_internal.h
+ ;;
++ xuclibc)
++ AC_MSG_RESULT(uclibc)
++
++ # Declare intention to use gettext, and add support for specific
++ # languages.
++ # For some reason, ALL_LINGUAS has to be before AM-GNU-GETTEXT
++ ALL_LINGUAS="de fr"
++
++ # Don't call AM-GNU-GETTEXT here. Instead, assume glibc.
++ AC_CHECK_PROG(check_msgfmt, msgfmt, yes, no)
++ if test x"$check_msgfmt" = x"yes" && test x"$enable_nls" = x"yes"; then
++ USE_NLS=yes
++ fi
++ # Export the build objects.
++ for ling in $ALL_LINGUAS; do \
++ glibcxx_MOFILES="$glibcxx_MOFILES $ling.mo"; \
++ glibcxx_POFILES="$glibcxx_POFILES $ling.po"; \
++ done
++ AC_SUBST(glibcxx_MOFILES)
++ AC_SUBST(glibcxx_POFILES)
++
++ CLOCALE_H=config/locale/uclibc/c_locale.h
++ CLOCALE_CC=config/locale/uclibc/c_locale.cc
++ CCODECVT_H=config/locale/ieee_1003.1-2001/codecvt_specializations.h
++ CCODECVT_CC=config/locale/uclibc/codecvt_members.cc
++ CCOLLATE_CC=config/locale/uclibc/collate_members.cc
++ CCTYPE_CC=config/locale/uclibc/ctype_members.cc
++ CMESSAGES_H=config/locale/uclibc/messages_members.h
++ CMESSAGES_CC=config/locale/uclibc/messages_members.cc
++ CMONEY_CC=config/locale/uclibc/monetary_members.cc
++ CNUMERIC_CC=config/locale/uclibc/numeric_members.cc
++ CTIME_H=config/locale/uclibc/time_members.h
++ CTIME_CC=config/locale/uclibc/time_members.cc
++ CLOCALE_INTERNAL_H=config/locale/uclibc/c++locale_internal.h
++ ;;
+ esac
+
+ # This is where the testsuite looks for locale catalogs, using the
+diff -urN gcc-3.4.1-dist/libstdc++-v3/config/locale/uclibc/c++locale_internal.h gcc-3.4.1/libstdc++-v3/config/locale/uclibc/c++locale_internal.h
+--- gcc-3.4.1-dist/libstdc++-v3/config/locale/uclibc/c++locale_internal.h 1969-12-31 18:00:00.000000000 -0600
++++ gcc-3.4.1/libstdc++-v3/config/locale/uclibc/c++locale_internal.h 2004-07-28 13:00:52.000000000 -0500
+@@ -0,0 +1,59 @@
++// Prototypes for GLIBC thread locale __-prefixed functions -*- C++ -*-
++
++// Copyright (C) 2002, 2004 Free Software Foundation, Inc.
++//
++// This file is part of the GNU ISO C++ Library. This library is free
++// software; you can redistribute it and/or modify it under the
++// terms of the GNU General Public License as published by the
++// Free Software Foundation; either version 2, or (at your option)
++// any later version.
++
++// This library is distributed in the hope that it will be useful,
++// but WITHOUT ANY WARRANTY; without even the implied warranty of
++// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++// GNU General Public License for more details.
++
++// You should have received a copy of the GNU General Public License along
++// with this library; see the file COPYING. If not, write to the Free
++// Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307,
++// USA.
++
++// As a special exception, you may use this file as part of a free software
++// library without restriction. Specifically, if other files instantiate
++// templates or use macros or inline functions from this file, or you compile
++// this file and link it with other files to produce an executable, this
++// file does not by itself cause the resulting executable to be covered by
++// the GNU General Public License. This exception does not however
++// invalidate any other reasons why the executable file might be covered by
++// the GNU General Public License.
++
++// Written by Jakub Jelinek <jakub@redhat.com>
++
++#include <clocale>
++
++#ifdef __UCLIBC_MJN3_ONLY__
++#warning clean this up
++#endif
++
++#ifdef __UCLIBC_HAS_XLOCALE__
++
++extern "C" __typeof(iswctype_l) __iswctype_l;
++extern "C" __typeof(nl_langinfo_l) __nl_langinfo_l;
++extern "C" __typeof(strcoll_l) __strcoll_l;
++extern "C" __typeof(strftime_l) __strftime_l;
++extern "C" __typeof(strtod_l) __strtod_l;
++extern "C" __typeof(strtof_l) __strtof_l;
++extern "C" __typeof(strtold_l) __strtold_l;
++extern "C" __typeof(strxfrm_l) __strxfrm_l;
++extern "C" __typeof(towlower_l) __towlower_l;
++extern "C" __typeof(towupper_l) __towupper_l;
++extern "C" __typeof(wcscoll_l) __wcscoll_l;
++extern "C" __typeof(wcsftime_l) __wcsftime_l;
++extern "C" __typeof(wcsxfrm_l) __wcsxfrm_l;
++extern "C" __typeof(wctype_l) __wctype_l;
++extern "C" __typeof(newlocale) __newlocale;
++extern "C" __typeof(freelocale) __freelocale;
++extern "C" __typeof(duplocale) __duplocale;
++extern "C" __typeof(uselocale) __uselocale;
++
++#endif // GLIBC 2.3 and later
+diff -urN gcc-3.4.1-dist/libstdc++-v3/config/locale/uclibc/c_locale.cc gcc-3.4.1/libstdc++-v3/config/locale/uclibc/c_locale.cc
+--- gcc-3.4.1-dist/libstdc++-v3/config/locale/uclibc/c_locale.cc 1969-12-31 18:00:00.000000000 -0600
++++ gcc-3.4.1/libstdc++-v3/config/locale/uclibc/c_locale.cc 2004-07-28 13:00:52.000000000 -0500
+@@ -0,0 +1,160 @@
++// Wrapper for underlying C-language localization -*- C++ -*-
++
++// Copyright (C) 2001, 2002, 2003 Free Software Foundation, Inc.
++//
++// This file is part of the GNU ISO C++ Library. This library is free
++// software; you can redistribute it and/or modify it under the
++// terms of the GNU General Public License as published by the
++// Free Software Foundation; either version 2, or (at your option)
++// any later version.
++
++// This library is distributed in the hope that it will be useful,
++// but WITHOUT ANY WARRANTY; without even the implied warranty of
++// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++// GNU General Public License for more details.
++
++// You should have received a copy of the GNU General Public License along
++// with this library; see the file COPYING. If not, write to the Free
++// Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307,
++// USA.
++
++// As a special exception, you may use this file as part of a free software
++// library without restriction. Specifically, if other files instantiate
++// templates or use macros or inline functions from this file, or you compile
++// this file and link it with other files to produce an executable, this
++// file does not by itself cause the resulting executable to be covered by
++// the GNU General Public License. This exception does not however
++// invalidate any other reasons why the executable file might be covered by
++// the GNU General Public License.
++
++//
++// ISO C++ 14882: 22.8 Standard locale categories.
++//
++
++// Written by Benjamin Kosnik <bkoz@redhat.com>
++
++#include <cerrno> // For errno
++#include <locale>
++#include <stdexcept>
++#include <langinfo.h>
++#include <bits/c++locale_internal.h>
++
++#ifndef __UCLIBC_HAS_XLOCALE__
++#define __strtol_l(S, E, B, L) strtol((S), (E), (B))
++#define __strtoul_l(S, E, B, L) strtoul((S), (E), (B))
++#define __strtoll_l(S, E, B, L) strtoll((S), (E), (B))
++#define __strtoull_l(S, E, B, L) strtoull((S), (E), (B))
++#define __strtof_l(S, E, L) strtof((S), (E))
++#define __strtod_l(S, E, L) strtod((S), (E))
++#define __strtold_l(S, E, L) strtold((S), (E))
++#warning should dummy __newlocale check for C|POSIX ?
++#define __newlocale(a, b, c) NULL
++#define __freelocale(a) ((void)0)
++#define __duplocale(a) __c_locale()
++#endif
++
++namespace std
++{
++ template<>
++ void
++ __convert_to_v(const char* __s, float& __v, ios_base::iostate& __err,
++ const __c_locale& __cloc)
++ {
++ if (!(__err & ios_base::failbit))
++ {
++ char* __sanity;
++ errno = 0;
++ float __f = __strtof_l(__s, &__sanity, __cloc);
++ if (__sanity != __s && errno != ERANGE)
++ __v = __f;
++ else
++ __err |= ios_base::failbit;
++ }
++ }
++
++ template<>
++ void
++ __convert_to_v(const char* __s, double& __v, ios_base::iostate& __err,
++ const __c_locale& __cloc)
++ {
++ if (!(__err & ios_base::failbit))
++ {
++ char* __sanity;
++ errno = 0;
++ double __d = __strtod_l(__s, &__sanity, __cloc);
++ if (__sanity != __s && errno != ERANGE)
++ __v = __d;
++ else
++ __err |= ios_base::failbit;
++ }
++ }
++
++ template<>
++ void
++ __convert_to_v(const char* __s, long double& __v, ios_base::iostate& __err,
++ const __c_locale& __cloc)
++ {
++ if (!(__err & ios_base::failbit))
++ {
++ char* __sanity;
++ errno = 0;
++ long double __ld = __strtold_l(__s, &__sanity, __cloc);
++ if (__sanity != __s && errno != ERANGE)
++ __v = __ld;
++ else
++ __err |= ios_base::failbit;
++ }
++ }
++
++ void
++ locale::facet::_S_create_c_locale(__c_locale& __cloc, const char* __s,
++ __c_locale __old)
++ {
++ __cloc = __newlocale(1 << LC_ALL, __s, __old);
++#ifdef __UCLIBC_HAS_XLOCALE__
++ if (!__cloc)
++ {
++ // This named locale is not supported by the underlying OS.
++ __throw_runtime_error(__N("locale::facet::_S_create_c_locale "
++ "name not valid"));
++ }
++#endif
++ }
++
++ void
++ locale::facet::_S_destroy_c_locale(__c_locale& __cloc)
++ {
++ if (_S_get_c_locale() != __cloc)
++ __freelocale(__cloc);
++ }
++
++ __c_locale
++ locale::facet::_S_clone_c_locale(__c_locale& __cloc)
++ { return __duplocale(__cloc); }
++} // namespace std
++
++namespace __gnu_cxx
++{
++ const char* const category_names[6 + _GLIBCXX_NUM_CATEGORIES] =
++ {
++ "LC_CTYPE",
++ "LC_NUMERIC",
++ "LC_TIME",
++ "LC_COLLATE",
++ "LC_MONETARY",
++ "LC_MESSAGES",
++#if _GLIBCXX_NUM_CATEGORIES != 0
++ "LC_PAPER",
++ "LC_NAME",
++ "LC_ADDRESS",
++ "LC_TELEPHONE",
++ "LC_MEASUREMENT",
++ "LC_IDENTIFICATION"
++#endif
++ };
++}
++
++namespace std
++{
++ const char* const* const locale::_S_categories = __gnu_cxx::category_names;
++} // namespace std
+diff -urN gcc-3.4.1-dist/libstdc++-v3/config/locale/uclibc/c_locale.h gcc-3.4.1/libstdc++-v3/config/locale/uclibc/c_locale.h
+--- gcc-3.4.1-dist/libstdc++-v3/config/locale/uclibc/c_locale.h 1969-12-31 18:00:00.000000000 -0600
++++ gcc-3.4.1/libstdc++-v3/config/locale/uclibc/c_locale.h 2004-07-28 13:00:52.000000000 -0500
+@@ -0,0 +1,119 @@
++// Wrapper for underlying C-language localization -*- C++ -*-
++
++// Copyright (C) 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
++//
++// This file is part of the GNU ISO C++ Library. This library is free
++// software; you can redistribute it and/or modify it under the
++// terms of the GNU General Public License as published by the
++// Free Software Foundation; either version 2, or (at your option)
++// any later version.
++
++// This library is distributed in the hope that it will be useful,
++// but WITHOUT ANY WARRANTY; without even the implied warranty of
++// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++// GNU General Public License for more details.
++
++// You should have received a copy of the GNU General Public License along
++// with this library; see the file COPYING. If not, write to the Free
++// Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307,
++// USA.
++
++// As a special exception, you may use this file as part of a free software
++// library without restriction. Specifically, if other files instantiate
++// templates or use macros or inline functions from this file, or you compile
++// this file and link it with other files to produce an executable, this
++// file does not by itself cause the resulting executable to be covered by
++// the GNU General Public License. This exception does not however
++// invalidate any other reasons why the executable file might be covered by
++// the GNU General Public License.
++
++//
++// ISO C++ 14882: 22.8 Standard locale categories.
++//
++
++// Written by Benjamin Kosnik <bkoz@redhat.com>
++
++#ifndef _C_LOCALE_H
++#define _C_LOCALE_H 1
++
++#pragma GCC system_header
++
++#include <cstring> // get std::strlen
++#include <cstdio> // get std::snprintf or std::sprintf
++#include <clocale>
++#include <langinfo.h> // For codecvt
++#ifdef __UCLIBC_MJN3_ONLY__
++#warning fix this
++#endif
++#ifdef __UCLIBC_HAS_LOCALE__
++#include <iconv.h> // For codecvt using iconv, iconv_t
++#endif
++#ifdef __UCLIBC_HAS_GETTEXT_AWARENESS__
++#include <libintl.h> // For messages
++#endif
++
++#ifdef __UCLIBC_MJN3_ONLY__
++#warning what is _GLIBCXX_C_LOCALE_GNU for
++#endif
++#define _GLIBCXX_C_LOCALE_GNU 1
++
++#ifdef __UCLIBC_MJN3_ONLY__
++#warning fix categories
++#endif
++// #define _GLIBCXX_NUM_CATEGORIES 6
++#define _GLIBCXX_NUM_CATEGORIES 0
++
++#ifdef __UCLIBC_HAS_XLOCALE__
++namespace __gnu_cxx
++{
++ extern "C" __typeof(uselocale) __uselocale;
++}
++#endif
++
++namespace std
++{
++#ifdef __UCLIBC_HAS_XLOCALE__
++ typedef __locale_t __c_locale;
++#else
++ typedef int* __c_locale;
++#endif
++
++ // Convert numeric value of type _Tv to string and return length of
++ // string. If snprintf is available use it, otherwise fall back to
++ // the unsafe sprintf which, in general, can be dangerous and should
++ // be avoided.
++ template<typename _Tv>
++ int
++ __convert_from_v(char* __out, const int __size, const char* __fmt,
++#ifdef __UCLIBC_HAS_XLOCALE__
++ _Tv __v, const __c_locale& __cloc, int __prec = -1)
++ {
++ __c_locale __old = __gnu_cxx::__uselocale(__cloc);
++#else
++ _Tv __v, const __c_locale&, int __prec = -1)
++ {
++# ifdef __UCLIBC_HAS_LOCALE__
++ char* __old = std::setlocale(LC_ALL, NULL);
++ char* __sav = new char[std::strlen(__old) + 1];
++ std::strcpy(__sav, __old);
++ std::setlocale(LC_ALL, "C");
++# endif
++#endif
++
++ int __ret;
++ if (__prec >= 0)
++ __ret = std::snprintf(__out, __size, __fmt, __prec, __v);
++ else
++ __ret = std::snprintf(__out, __size, __fmt, __v);
++
++#ifdef __UCLIBC_HAS_XLOCALE__
++ __gnu_cxx::__uselocale(__old);
++#elif defined __UCLIBC_HAS_LOCALE__
++ std::setlocale(LC_ALL, __sav);
++ delete [] __sav;
++#endif
++ return __ret;
++ }
++}
++
++#endif
+diff -urN gcc-3.4.1-dist/libstdc++-v3/config/locale/uclibc/codecvt_members.cc gcc-3.4.1/libstdc++-v3/config/locale/uclibc/codecvt_members.cc
+--- gcc-3.4.1-dist/libstdc++-v3/config/locale/uclibc/codecvt_members.cc 1969-12-31 18:00:00.000000000 -0600
++++ gcc-3.4.1/libstdc++-v3/config/locale/uclibc/codecvt_members.cc 2004-07-28 13:00:52.000000000 -0500
+@@ -0,0 +1,306 @@
++// std::codecvt implementation details, GNU version -*- C++ -*-
++
++// Copyright (C) 2002, 2003 Free Software Foundation, Inc.
++//
++// This file is part of the GNU ISO C++ Library. This library is free
++// software; you can redistribute it and/or modify it under the
++// terms of the GNU General Public License as published by the
++// Free Software Foundation; either version 2, or (at your option)
++// any later version.
++
++// This library is distributed in the hope that it will be useful,
++// but WITHOUT ANY WARRANTY; without even the implied warranty of
++// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++// GNU General Public License for more details.
++
++// You should have received a copy of the GNU General Public License along
++// with this library; see the file COPYING. If not, write to the Free
++// Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307,
++// USA.
++
++// As a special exception, you may use this file as part of a free software
++// library without restriction. Specifically, if other files instantiate
++// templates or use macros or inline functions from this file, or you compile
++// this file and link it with other files to produce an executable, this
++// file does not by itself cause the resulting executable to be covered by
++// the GNU General Public License. This exception does not however
++// invalidate any other reasons why the executable file might be covered by
++// the GNU General Public License.
++
++//
++// ISO C++ 14882: 22.2.1.5 - Template class codecvt
++//
++
++// Written by Benjamin Kosnik <bkoz@redhat.com>
++
++#include <locale>
++#include <bits/c++locale_internal.h>
++
++namespace std
++{
++ // Specializations.
++#ifdef _GLIBCXX_USE_WCHAR_T
++ codecvt_base::result
++ codecvt<wchar_t, char, mbstate_t>::
++ do_out(state_type& __state, const intern_type* __from,
++ const intern_type* __from_end, const intern_type*& __from_next,
++ extern_type* __to, extern_type* __to_end,
++ extern_type*& __to_next) const
++ {
++ result __ret = ok;
++ state_type __tmp_state(__state);
++
++#ifdef __UCLIBC_HAS_XLOCALE__
++ __c_locale __old = __uselocale(_M_c_locale_codecvt);
++#endif
++
++ // wcsnrtombs is *very* fast but stops if encounters NUL characters:
++ // in case we fall back to wcrtomb and then continue, in a loop.
++ // NB: wcsnrtombs is a GNU extension
++ for (__from_next = __from, __to_next = __to;
++ __from_next < __from_end && __to_next < __to_end
++ && __ret == ok;)
++ {
++ const intern_type* __from_chunk_end = wmemchr(__from_next, L'\0',
++ __from_end - __from_next);
++ if (!__from_chunk_end)
++ __from_chunk_end = __from_end;
++
++ __from = __from_next;
++ const size_t __conv = wcsnrtombs(__to_next, &__from_next,
++ __from_chunk_end - __from_next,
++ __to_end - __to_next, &__state);
++ if (__conv == static_cast<size_t>(-1))
++ {
++ // In case of error, in order to stop at the exact place we
++ // have to start again from the beginning with a series of
++ // wcrtomb.
++ for (; __from < __from_next; ++__from)
++ __to_next += wcrtomb(__to_next, *__from, &__tmp_state);
++ __state = __tmp_state;
++ __ret = error;
++ }
++ else if (__from_next && __from_next < __from_chunk_end)
++ {
++ __to_next += __conv;
++ __ret = partial;
++ }
++ else
++ {
++ __from_next = __from_chunk_end;
++ __to_next += __conv;
++ }
++
++ if (__from_next < __from_end && __ret == ok)
++ {
++ extern_type __buf[MB_LEN_MAX];
++ __tmp_state = __state;
++ const size_t __conv = wcrtomb(__buf, *__from_next, &__tmp_state);
++ if (__conv > static_cast<size_t>(__to_end - __to_next))
++ __ret = partial;
++ else
++ {
++ memcpy(__to_next, __buf, __conv);
++ __state = __tmp_state;
++ __to_next += __conv;
++ ++__from_next;
++ }
++ }
++ }
++
++#ifdef __UCLIBC_HAS_XLOCALE__
++ __uselocale(__old);
++#endif
++
++ return __ret;
++ }
++
++ codecvt_base::result
++ codecvt<wchar_t, char, mbstate_t>::
++ do_in(state_type& __state, const extern_type* __from,
++ const extern_type* __from_end, const extern_type*& __from_next,
++ intern_type* __to, intern_type* __to_end,
++ intern_type*& __to_next) const
++ {
++ result __ret = ok;
++ state_type __tmp_state(__state);
++
++#ifdef __UCLIBC_HAS_XLOCALE__
++ __c_locale __old = __uselocale(_M_c_locale_codecvt);
++#endif
++
++ // mbsnrtowcs is *very* fast but stops if encounters NUL characters:
++ // in case we store a L'\0' and then continue, in a loop.
++ // NB: mbsnrtowcs is a GNU extension
++ for (__from_next = __from, __to_next = __to;
++ __from_next < __from_end && __to_next < __to_end
++ && __ret == ok;)
++ {
++ const extern_type* __from_chunk_end;
++ __from_chunk_end = static_cast<const extern_type*>(memchr(__from_next, '\0',
++ __from_end
++ - __from_next));
++ if (!__from_chunk_end)
++ __from_chunk_end = __from_end;
++
++ __from = __from_next;
++ size_t __conv = mbsnrtowcs(__to_next, &__from_next,
++ __from_chunk_end - __from_next,
++ __to_end - __to_next, &__state);
++ if (__conv == static_cast<size_t>(-1))
++ {
++ // In case of error, in order to stop at the exact place we
++ // have to start again from the beginning with a series of
++ // mbrtowc.
++ for (;; ++__to_next, __from += __conv)
++ {
++ __conv = mbrtowc(__to_next, __from, __from_end - __from,
++ &__tmp_state);
++ if (__conv == static_cast<size_t>(-1)
++ || __conv == static_cast<size_t>(-2))
++ break;
++ }
++ __from_next = __from;
++ __state = __tmp_state;
++ __ret = error;
++ }
++ else if (__from_next && __from_next < __from_chunk_end)
++ {
++ // It is unclear what to return in this case (see DR 382).
++ __to_next += __conv;
++ __ret = partial;
++ }
++ else
++ {
++ __from_next = __from_chunk_end;
++ __to_next += __conv;
++ }
++
++ if (__from_next < __from_end && __ret == ok)
++ {
++ if (__to_next < __to_end)
++ {
++ // XXX Probably wrong for stateful encodings
++ __tmp_state = __state;
++ ++__from_next;
++ *__to_next++ = L'\0';
++ }
++ else
++ __ret = partial;
++ }
++ }
++
++#ifdef __UCLIBC_HAS_XLOCALE__
++ __uselocale(__old);
++#endif
++
++ return __ret;
++ }
++
++ int
++ codecvt<wchar_t, char, mbstate_t>::
++ do_encoding() const throw()
++ {
++ // XXX This implementation assumes that the encoding is
++ // stateless and is either single-byte or variable-width.
++ int __ret = 0;
++#ifdef __UCLIBC_HAS_XLOCALE__
++ __c_locale __old = __uselocale(_M_c_locale_codecvt);
++#endif
++ if (MB_CUR_MAX == 1)
++ __ret = 1;
++#ifdef __UCLIBC_HAS_XLOCALE__
++ __uselocale(__old);
++#endif
++ return __ret;
++ }
++
++ int
++ codecvt<wchar_t, char, mbstate_t>::
++ do_max_length() const throw()
++ {
++#ifdef __UCLIBC_HAS_XLOCALE__
++ __c_locale __old = __uselocale(_M_c_locale_codecvt);
++#endif
++ // XXX Probably wrong for stateful encodings.
++ int __ret = MB_CUR_MAX;
++#ifdef __UCLIBC_HAS_XLOCALE__
++ __uselocale(__old);
++#endif
++ return __ret;
++ }
++
++ int
++ codecvt<wchar_t, char, mbstate_t>::
++ do_length(state_type& __state, const extern_type* __from,
++ const extern_type* __end, size_t __max) const
++ {
++ int __ret = 0;
++ state_type __tmp_state(__state);
++
++#ifdef __UCLIBC_HAS_XLOCALE__
++ __c_locale __old = __uselocale(_M_c_locale_codecvt);
++#endif
++
++ // mbsnrtowcs is *very* fast but stops if encounters NUL characters:
++ // in case we advance past it and then continue, in a loop.
++ // NB: mbsnrtowcs is a GNU extension
++
++ // A dummy internal buffer is needed in order for mbsnrtocws to consider
++ // its fourth parameter (it wouldn't with NULL as first parameter).
++ wchar_t* __to = static_cast<wchar_t*>(__builtin_alloca(sizeof(wchar_t)
++ * __max));
++ while (__from < __end && __max)
++ {
++ const extern_type* __from_chunk_end;
++ __from_chunk_end = static_cast<const extern_type*>(memchr(__from, '\0',
++ __end
++ - __from));
++ if (!__from_chunk_end)
++ __from_chunk_end = __end;
++
++ const extern_type* __tmp_from = __from;
++ size_t __conv = mbsnrtowcs(__to, &__from,
++ __from_chunk_end - __from,
++ __max, &__state);
++ if (__conv == static_cast<size_t>(-1))
++ {
++ // In case of error, in order to stop at the exact place we
++ // have to start again from the beginning with a series of
++ // mbrtowc.
++ for (__from = __tmp_from;; __from += __conv)
++ {
++ __conv = mbrtowc(NULL, __from, __end - __from,
++ &__tmp_state);
++ if (__conv == static_cast<size_t>(-1)
++ || __conv == static_cast<size_t>(-2))
++ break;
++ }
++ __state = __tmp_state;
++ __ret += __from - __tmp_from;
++ break;
++ }
++ if (!__from)
++ __from = __from_chunk_end;
++
++ __ret += __from - __tmp_from;
++ __max -= __conv;
++
++ if (__from < __end && __max)
++ {
++ // XXX Probably wrong for stateful encodings
++ __tmp_state = __state;
++ ++__from;
++ ++__ret;
++ --__max;
++ }
++ }
++
++#ifdef __UCLIBC_HAS_XLOCALE__
++ __uselocale(__old);
++#endif
++
++ return __ret;
++ }
++#endif
++}
+diff -urN gcc-3.4.1-dist/libstdc++-v3/config/locale/uclibc/collate_members.cc gcc-3.4.1/libstdc++-v3/config/locale/uclibc/collate_members.cc
+--- gcc-3.4.1-dist/libstdc++-v3/config/locale/uclibc/collate_members.cc 1969-12-31 18:00:00.000000000 -0600
++++ gcc-3.4.1/libstdc++-v3/config/locale/uclibc/collate_members.cc 2004-07-28 13:00:52.000000000 -0500
+@@ -0,0 +1,80 @@
++// std::collate implementation details, GNU version -*- C++ -*-
++
++// Copyright (C) 2001, 2002 Free Software Foundation, Inc.
++//
++// This file is part of the GNU ISO C++ Library. This library is free
++// software; you can redistribute it and/or modify it under the
++// terms of the GNU General Public License as published by the
++// Free Software Foundation; either version 2, or (at your option)
++// any later version.
++
++// This library is distributed in the hope that it will be useful,
++// but WITHOUT ANY WARRANTY; without even the implied warranty of
++// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++// GNU General Public License for more details.
++
++// You should have received a copy of the GNU General Public License along
++// with this library; see the file COPYING. If not, write to the Free
++// Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307,
++// USA.
++
++// As a special exception, you may use this file as part of a free software
++// library without restriction. Specifically, if other files instantiate
++// templates or use macros or inline functions from this file, or you compile
++// this file and link it with other files to produce an executable, this
++// file does not by itself cause the resulting executable to be covered by
++// the GNU General Public License. This exception does not however
++// invalidate any other reasons why the executable file might be covered by
++// the GNU General Public License.
++
++//
++// ISO C++ 14882: 22.2.4.1.2 collate virtual functions
++//
++
++// Written by Benjamin Kosnik <bkoz@redhat.com>
++
++#include <locale>
++#include <bits/c++locale_internal.h>
++
++#ifndef __UCLIBC_HAS_XLOCALE__
++#define __strcoll_l(S1, S2, L) strcoll((S1), (S2))
++#define __strxfrm_l(S1, S2, N, L) strxfrm((S1), (S2), (N))
++#define __wcscoll_l(S1, S2, L) wcscoll((S1), (S2))
++#define __wcsxfrm_l(S1, S2, N, L) wcsxfrm((S1), (S2), (N))
++#endif
++
++namespace std
++{
++ // These are basically extensions to char_traits, and perhaps should
++ // be put there instead of here.
++ template<>
++ int
++ collate<char>::_M_compare(const char* __one, const char* __two) const
++ {
++ int __cmp = __strcoll_l(__one, __two, _M_c_locale_collate);
++ return (__cmp >> (8 * sizeof (int) - 2)) | (__cmp != 0);
++ }
++
++ template<>
++ size_t
++ collate<char>::_M_transform(char* __to, const char* __from,
++ size_t __n) const
++ { return __strxfrm_l(__to, __from, __n, _M_c_locale_collate); }
++
++#ifdef _GLIBCXX_USE_WCHAR_T
++ template<>
++ int
++ collate<wchar_t>::_M_compare(const wchar_t* __one,
++ const wchar_t* __two) const
++ {
++ int __cmp = __wcscoll_l(__one, __two, _M_c_locale_collate);
++ return (__cmp >> (8 * sizeof (int) - 2)) | (__cmp != 0);
++ }
++
++ template<>
++ size_t
++ collate<wchar_t>::_M_transform(wchar_t* __to, const wchar_t* __from,
++ size_t __n) const
++ { return __wcsxfrm_l(__to, __from, __n, _M_c_locale_collate); }
++#endif
++}
+diff -urN gcc-3.4.1-dist/libstdc++-v3/config/locale/uclibc/ctype_members.cc gcc-3.4.1/libstdc++-v3/config/locale/uclibc/ctype_members.cc
+--- gcc-3.4.1-dist/libstdc++-v3/config/locale/uclibc/ctype_members.cc 1969-12-31 18:00:00.000000000 -0600
++++ gcc-3.4.1/libstdc++-v3/config/locale/uclibc/ctype_members.cc 2004-07-28 13:00:52.000000000 -0500
+@@ -0,0 +1,300 @@
++// std::ctype implementation details, GNU version -*- C++ -*-
++
++// Copyright (C) 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
++//
++// This file is part of the GNU ISO C++ Library. This library is free
++// software; you can redistribute it and/or modify it under the
++// terms of the GNU General Public License as published by the
++// Free Software Foundation; either version 2, or (at your option)
++// any later version.
++
++// This library is distributed in the hope that it will be useful,
++// but WITHOUT ANY WARRANTY; without even the implied warranty of
++// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++// GNU General Public License for more details.
++
++// You should have received a copy of the GNU General Public License along
++// with this library; see the file COPYING. If not, write to the Free
++// Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307,
++// USA.
++
++// As a special exception, you may use this file as part of a free software
++// library without restriction. Specifically, if other files instantiate
++// templates or use macros or inline functions from this file, or you compile
++// this file and link it with other files to produce an executable, this
++// file does not by itself cause the resulting executable to be covered by
++// the GNU General Public License. This exception does not however
++// invalidate any other reasons why the executable file might be covered by
++// the GNU General Public License.
++
++//
++// ISO C++ 14882: 22.2.1.1.2 ctype virtual functions.
++//
++
++// Written by Benjamin Kosnik <bkoz@redhat.com>
++
++#define _LIBC
++#include <locale>
++#undef _LIBC
++#include <bits/c++locale_internal.h>
++
++#ifndef __UCLIBC_HAS_XLOCALE__
++#define __wctype_l(S, L) wctype((S))
++#define __towupper_l(C, L) towupper((C))
++#define __towlower_l(C, L) towlower((C))
++#define __iswctype_l(C, M, L) iswctype((C), (M))
++#endif
++
++namespace std
++{
++ // NB: The other ctype<char> specializations are in src/locale.cc and
++ // various /config/os/* files.
++ template<>
++ ctype_byname<char>::ctype_byname(const char* __s, size_t __refs)
++ : ctype<char>(0, false, __refs)
++ {
++ if (std::strcmp(__s, "C") != 0 && std::strcmp(__s, "POSIX") != 0)
++ {
++ this->_S_destroy_c_locale(this->_M_c_locale_ctype);
++ this->_S_create_c_locale(this->_M_c_locale_ctype, __s);
++#ifdef __UCLIBC_HAS_XLOCALE__
++ this->_M_toupper = this->_M_c_locale_ctype->__ctype_toupper;
++ this->_M_tolower = this->_M_c_locale_ctype->__ctype_tolower;
++ this->_M_table = this->_M_c_locale_ctype->__ctype_b;
++#endif
++ }
++ }
++
++#ifdef _GLIBCXX_USE_WCHAR_T
++ ctype<wchar_t>::__wmask_type
++ ctype<wchar_t>::_M_convert_to_wmask(const mask __m) const
++ {
++ __wmask_type __ret;
++ switch (__m)
++ {
++ case space:
++ __ret = __wctype_l("space", _M_c_locale_ctype);
++ break;
++ case print:
++ __ret = __wctype_l("print", _M_c_locale_ctype);
++ break;
++ case cntrl:
++ __ret = __wctype_l("cntrl", _M_c_locale_ctype);
++ break;
++ case upper:
++ __ret = __wctype_l("upper", _M_c_locale_ctype);
++ break;
++ case lower:
++ __ret = __wctype_l("lower", _M_c_locale_ctype);
++ break;
++ case alpha:
++ __ret = __wctype_l("alpha", _M_c_locale_ctype);
++ break;
++ case digit:
++ __ret = __wctype_l("digit", _M_c_locale_ctype);
++ break;
++ case punct:
++ __ret = __wctype_l("punct", _M_c_locale_ctype);
++ break;
++ case xdigit:
++ __ret = __wctype_l("xdigit", _M_c_locale_ctype);
++ break;
++ case alnum:
++ __ret = __wctype_l("alnum", _M_c_locale_ctype);
++ break;
++ case graph:
++ __ret = __wctype_l("graph", _M_c_locale_ctype);
++ break;
++ default:
++ __ret = 0;
++ }
++ return __ret;
++ }
++
++ wchar_t
++ ctype<wchar_t>::do_toupper(wchar_t __c) const
++ { return __towupper_l(__c, _M_c_locale_ctype); }
++
++ const wchar_t*
++ ctype<wchar_t>::do_toupper(wchar_t* __lo, const wchar_t* __hi) const
++ {
++ while (__lo < __hi)
++ {
++ *__lo = __towupper_l(*__lo, _M_c_locale_ctype);
++ ++__lo;
++ }
++ return __hi;
++ }
++
++ wchar_t
++ ctype<wchar_t>::do_tolower(wchar_t __c) const
++ { return __towlower_l(__c, _M_c_locale_ctype); }
++
++ const wchar_t*
++ ctype<wchar_t>::do_tolower(wchar_t* __lo, const wchar_t* __hi) const
++ {
++ while (__lo < __hi)
++ {
++ *__lo = __towlower_l(*__lo, _M_c_locale_ctype);
++ ++__lo;
++ }
++ return __hi;
++ }
++
++ bool
++ ctype<wchar_t>::
++ do_is(mask __m, wchar_t __c) const
++ {
++ // Highest bitmask in ctype_base == 10, but extra in "C"
++ // library for blank.
++ bool __ret = false;
++ const size_t __bitmasksize = 11;
++ for (size_t __bitcur = 0; __bitcur <= __bitmasksize; ++__bitcur)
++ if (__m & _M_bit[__bitcur]
++ && __iswctype_l(__c, _M_wmask[__bitcur], _M_c_locale_ctype))
++ {
++ __ret = true;
++ break;
++ }
++ return __ret;
++ }
++
++ const wchar_t*
++ ctype<wchar_t>::
++ do_is(const wchar_t* __lo, const wchar_t* __hi, mask* __vec) const
++ {
++ for (; __lo < __hi; ++__vec, ++__lo)
++ {
++ // Highest bitmask in ctype_base == 10, but extra in "C"
++ // library for blank.
++ const size_t __bitmasksize = 11;
++ mask __m = 0;
++ for (size_t __bitcur = 0; __bitcur <= __bitmasksize; ++__bitcur)
++ if (__iswctype_l(*__lo, _M_wmask[__bitcur], _M_c_locale_ctype))
++ __m |= _M_bit[__bitcur];
++ *__vec = __m;
++ }
++ return __hi;
++ }
++
++ const wchar_t*
++ ctype<wchar_t>::
++ do_scan_is(mask __m, const wchar_t* __lo, const wchar_t* __hi) const
++ {
++ while (__lo < __hi && !this->do_is(__m, *__lo))
++ ++__lo;
++ return __lo;
++ }
++
++ const wchar_t*
++ ctype<wchar_t>::
++ do_scan_not(mask __m, const char_type* __lo, const char_type* __hi) const
++ {
++ while (__lo < __hi && this->do_is(__m, *__lo) != 0)
++ ++__lo;
++ return __lo;
++ }
++
++ wchar_t
++ ctype<wchar_t>::
++ do_widen(char __c) const
++ { return _M_widen[static_cast<unsigned char>(__c)]; }
++
++ const char*
++ ctype<wchar_t>::
++ do_widen(const char* __lo, const char* __hi, wchar_t* __dest) const
++ {
++ while (__lo < __hi)
++ {
++ *__dest = _M_widen[static_cast<unsigned char>(*__lo)];
++ ++__lo;
++ ++__dest;
++ }
++ return __hi;
++ }
++
++ char
++ ctype<wchar_t>::
++ do_narrow(wchar_t __wc, char __dfault) const
++ {
++ if (__wc >= 0 && __wc < 128 && _M_narrow_ok)
++ return _M_narrow[__wc];
++#ifdef __UCLIBC_HAS_XLOCALE__
++ __c_locale __old = __uselocale(_M_c_locale_ctype);
++#endif
++ const int __c = wctob(__wc);
++#ifdef __UCLIBC_HAS_XLOCALE__
++ __uselocale(__old);
++#endif
++ return (__c == EOF ? __dfault : static_cast<char>(__c));
++ }
++
++ const wchar_t*
++ ctype<wchar_t>::
++ do_narrow(const wchar_t* __lo, const wchar_t* __hi, char __dfault,
++ char* __dest) const
++ {
++#ifdef __UCLIBC_HAS_XLOCALE__
++ __c_locale __old = __uselocale(_M_c_locale_ctype);
++#endif
++ if (_M_narrow_ok)
++ while (__lo < __hi)
++ {
++ if (*__lo >= 0 && *__lo < 128)
++ *__dest = _M_narrow[*__lo];
++ else
++ {
++ const int __c = wctob(*__lo);
++ *__dest = (__c == EOF ? __dfault : static_cast<char>(__c));
++ }
++ ++__lo;
++ ++__dest;
++ }
++ else
++ while (__lo < __hi)
++ {
++ const int __c = wctob(*__lo);
++ *__dest = (__c == EOF ? __dfault : static_cast<char>(__c));
++ ++__lo;
++ ++__dest;
++ }
++#ifdef __UCLIBC_HAS_XLOCALE__
++ __uselocale(__old);
++#endif
++ return __hi;
++ }
++
++ void
++ ctype<wchar_t>::_M_initialize_ctype()
++ {
++#ifdef __UCLIBC_HAS_XLOCALE__
++ __c_locale __old = __uselocale(_M_c_locale_ctype);
++#endif
++ wint_t __i;
++ for (__i = 0; __i < 128; ++__i)
++ {
++ const int __c = wctob(__i);
++ if (__c == EOF)
++ break;
++ else
++ _M_narrow[__i] = static_cast<char>(__c);
++ }
++ if (__i == 128)
++ _M_narrow_ok = true;
++ else
++ _M_narrow_ok = false;
++ for (size_t __j = 0;
++ __j < sizeof(_M_widen) / sizeof(wint_t); ++__j)
++ _M_widen[__j] = btowc(__j);
++
++ for (size_t __k = 0; __k <= 11; ++__k)
++ {
++ _M_bit[__k] = static_cast<mask>(_ISbit(__k));
++ _M_wmask[__k] = _M_convert_to_wmask(_M_bit[__k]);
++ }
++#ifdef __UCLIBC_HAS_XLOCALE__
++ __uselocale(__old);
++#endif
++ }
++#endif // _GLIBCXX_USE_WCHAR_T
++}
+diff -urN gcc-3.4.1-dist/libstdc++-v3/config/locale/uclibc/messages_members.cc gcc-3.4.1/libstdc++-v3/config/locale/uclibc/messages_members.cc
+--- gcc-3.4.1-dist/libstdc++-v3/config/locale/uclibc/messages_members.cc 1969-12-31 18:00:00.000000000 -0600
++++ gcc-3.4.1/libstdc++-v3/config/locale/uclibc/messages_members.cc 2004-07-28 13:00:52.000000000 -0500
+@@ -0,0 +1,100 @@
++// std::messages implementation details, GNU version -*- C++ -*-
++
++// Copyright (C) 2001, 2002 Free Software Foundation, Inc.
++//
++// This file is part of the GNU ISO C++ Library. This library is free
++// software; you can redistribute it and/or modify it under the
++// terms of the GNU General Public License as published by the
++// Free Software Foundation; either version 2, or (at your option)
++// any later version.
++
++// This library is distributed in the hope that it will be useful,
++// but WITHOUT ANY WARRANTY; without even the implied warranty of
++// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++// GNU General Public License for more details.
++
++// You should have received a copy of the GNU General Public License along
++// with this library; see the file COPYING. If not, write to the Free
++// Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307,
++// USA.
++
++// As a special exception, you may use this file as part of a free software
++// library without restriction. Specifically, if other files instantiate
++// templates or use macros or inline functions from this file, or you compile
++// this file and link it with other files to produce an executable, this
++// file does not by itself cause the resulting executable to be covered by
++// the GNU General Public License. This exception does not however
++// invalidate any other reasons why the executable file might be covered by
++// the GNU General Public License.
++
++//
++// ISO C++ 14882: 22.2.7.1.2 messages virtual functions
++//
++
++// Written by Benjamin Kosnik <bkoz@redhat.com>
++
++#include <locale>
++#include <bits/c++locale_internal.h>
++
++#ifdef __UCLIBC_MJN3_ONLY__
++#warning fix gettext stuff
++#endif
++#ifdef __UCLIBC_HAS_GETTEXT_AWARENESS__
++extern "C" char *__dcgettext(const char *domainname,
++ const char *msgid, int category);
++#undef gettext
++#define gettext(msgid) __dcgettext(NULL, msgid, LC_MESSAGES)
++#else
++#undef gettext
++#define gettext(msgid) (msgid)
++#endif
++
++namespace std
++{
++ // Specializations.
++ template<>
++ string
++ messages<char>::do_get(catalog, int, int, const string& __dfault) const
++ {
++#ifdef __UCLIBC_HAS_XLOCALE__
++ __c_locale __old = __uselocale(_M_c_locale_messages);
++ const char* __msg = const_cast<const char*>(gettext(__dfault.c_str()));
++ __uselocale(__old);
++ return string(__msg);
++#elif defined __UCLIBC_HAS_LOCALE__
++ char* __old = strdup(setlocale(LC_ALL, NULL));
++ setlocale(LC_ALL, _M_name_messages);
++ const char* __msg = gettext(__dfault.c_str());
++ setlocale(LC_ALL, __old);
++ free(__old);
++ return string(__msg);
++#else
++ const char* __msg = gettext(__dfault.c_str());
++ return string(__msg);
++#endif
++ }
++
++#ifdef _GLIBCXX_USE_WCHAR_T
++ template<>
++ wstring
++ messages<wchar_t>::do_get(catalog, int, int, const wstring& __dfault) const
++ {
++# ifdef __UCLIBC_HAS_XLOCALE__
++ __c_locale __old = __uselocale(_M_c_locale_messages);
++ char* __msg = gettext(_M_convert_to_char(__dfault));
++ __uselocale(__old);
++ return _M_convert_from_char(__msg);
++# elif defined __UCLIBC_HAS_LOCALE__
++ char* __old = strdup(setlocale(LC_ALL, NULL));
++ setlocale(LC_ALL, _M_name_messages);
++ char* __msg = gettext(_M_convert_to_char(__dfault));
++ setlocale(LC_ALL, __old);
++ free(__old);
++ return _M_convert_from_char(__msg);
++# else
++ char* __msg = gettext(_M_convert_to_char(__dfault));
++ return _M_convert_from_char(__msg);
++# endif
++ }
++#endif
++}
+diff -urN gcc-3.4.1-dist/libstdc++-v3/config/locale/uclibc/messages_members.h gcc-3.4.1/libstdc++-v3/config/locale/uclibc/messages_members.h
+--- gcc-3.4.1-dist/libstdc++-v3/config/locale/uclibc/messages_members.h 1969-12-31 18:00:00.000000000 -0600
++++ gcc-3.4.1/libstdc++-v3/config/locale/uclibc/messages_members.h 2004-07-28 13:00:52.000000000 -0500
+@@ -0,0 +1,118 @@
++// std::messages implementation details, GNU version -*- C++ -*-
++
++// Copyright (C) 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
++//
++// This file is part of the GNU ISO C++ Library. This library is free
++// software; you can redistribute it and/or modify it under the
++// terms of the GNU General Public License as published by the
++// Free Software Foundation; either version 2, or (at your option)
++// any later version.
++
++// This library is distributed in the hope that it will be useful,
++// but WITHOUT ANY WARRANTY; without even the implied warranty of
++// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++// GNU General Public License for more details.
++
++// You should have received a copy of the GNU General Public License along
++// with this library; see the file COPYING. If not, write to the Free
++// Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307,
++// USA.
++
++// As a special exception, you may use this file as part of a free software
++// library without restriction. Specifically, if other files instantiate
++// templates or use macros or inline functions from this file, or you compile
++// this file and link it with other files to produce an executable, this
++// file does not by itself cause the resulting executable to be covered by
++// the GNU General Public License. This exception does not however
++// invalidate any other reasons why the executable file might be covered by
++// the GNU General Public License.
++
++//
++// ISO C++ 14882: 22.2.7.1.2 messages functions
++//
++
++// Written by Benjamin Kosnik <bkoz@redhat.com>
++
++#ifdef __UCLIBC_MJN3_ONLY__
++#warning fix prototypes for *textdomain funcs
++#endif
++#ifdef __UCLIBC_HAS_GETTEXT_AWARENESS__
++extern "C" char *__textdomain(const char *domainname);
++extern "C" char *__bindtextdomain(const char *domainname,
++ const char *dirname);
++#else
++#undef __textdomain
++#undef __bindtextdomain
++#define __textdomain(D) ((void)0)
++#define __bindtextdomain(D,P) ((void)0)
++#endif
++
++ // Non-virtual member functions.
++ template<typename _CharT>
++ messages<_CharT>::messages(size_t __refs)
++ : facet(__refs), _M_c_locale_messages(_S_get_c_locale()),
++ _M_name_messages(_S_get_c_name())
++ { }
++
++ template<typename _CharT>
++ messages<_CharT>::messages(__c_locale __cloc, const char* __s,
++ size_t __refs)
++ : facet(__refs), _M_c_locale_messages(_S_clone_c_locale(__cloc)),
++ _M_name_messages(__s)
++ {
++ char* __tmp = new char[std::strlen(__s) + 1];
++ std::strcpy(__tmp, __s);
++ _M_name_messages = __tmp;
++ }
++
++ template<typename _CharT>
++ typename messages<_CharT>::catalog
++ messages<_CharT>::open(const basic_string<char>& __s, const locale& __loc,
++ const char* __dir) const
++ {
++ __bindtextdomain(__s.c_str(), __dir);
++ return this->do_open(__s, __loc);
++ }
++
++ // Virtual member functions.
++ template<typename _CharT>
++ messages<_CharT>::~messages()
++ {
++ if (_M_name_messages != _S_get_c_name())
++ delete [] _M_name_messages;
++ _S_destroy_c_locale(_M_c_locale_messages);
++ }
++
++ template<typename _CharT>
++ typename messages<_CharT>::catalog
++ messages<_CharT>::do_open(const basic_string<char>& __s,
++ const locale&) const
++ {
++ // No error checking is done, assume the catalog exists and can
++ // be used.
++ __textdomain(__s.c_str());
++ return 0;
++ }
++
++ template<typename _CharT>
++ void
++ messages<_CharT>::do_close(catalog) const
++ { }
++
++ // messages_byname
++ template<typename _CharT>
++ messages_byname<_CharT>::messages_byname(const char* __s, size_t __refs)
++ : messages<_CharT>(__refs)
++ {
++ if (this->_M_name_messages != locale::facet::_S_get_c_name())
++ delete [] this->_M_name_messages;
++ char* __tmp = new char[std::strlen(__s) + 1];
++ std::strcpy(__tmp, __s);
++ this->_M_name_messages = __tmp;
++
++ if (std::strcmp(__s, "C") != 0 && std::strcmp(__s, "POSIX") != 0)
++ {
++ this->_S_destroy_c_locale(this->_M_c_locale_messages);
++ this->_S_create_c_locale(this->_M_c_locale_messages, __s);
++ }
++ }
+diff -urN gcc-3.4.1-dist/libstdc++-v3/config/locale/uclibc/monetary_members.cc gcc-3.4.1/libstdc++-v3/config/locale/uclibc/monetary_members.cc
+--- gcc-3.4.1-dist/libstdc++-v3/config/locale/uclibc/monetary_members.cc 1969-12-31 18:00:00.000000000 -0600
++++ gcc-3.4.1/libstdc++-v3/config/locale/uclibc/monetary_members.cc 2004-07-28 13:44:04.000000000 -0500
+@@ -0,0 +1,698 @@
++// std::moneypunct implementation details, GNU version -*- C++ -*-
++
++// Copyright (C) 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
++//
++// This file is part of the GNU ISO C++ Library. This library is free
++// software; you can redistribute it and/or modify it under the
++// terms of the GNU General Public License as published by the
++// Free Software Foundation; either version 2, or (at your option)
++// any later version.
++
++// This library is distributed in the hope that it will be useful,
++// but WITHOUT ANY WARRANTY; without even the implied warranty of
++// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++// GNU General Public License for more details.
++
++// You should have received a copy of the GNU General Public License along
++// with this library; see the file COPYING. If not, write to the Free
++// Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307,
++// USA.
++
++// As a special exception, you may use this file as part of a free software
++// library without restriction. Specifically, if other files instantiate
++// templates or use macros or inline functions from this file, or you compile
++// this file and link it with other files to produce an executable, this
++// file does not by itself cause the resulting executable to be covered by
++// the GNU General Public License. This exception does not however
++// invalidate any other reasons why the executable file might be covered by
++// the GNU General Public License.
++
++//
++// ISO C++ 14882: 22.2.6.3.2 moneypunct virtual functions
++//
++
++// Written by Benjamin Kosnik <bkoz@redhat.com>
++
++#define _LIBC
++#include <locale>
++#undef _LIBC
++#include <bits/c++locale_internal.h>
++
++#ifdef __UCLIBC_MJN3_ONLY__
++#warning optimize this for uclibc
++#warning tailor for stub locale support
++#endif
++
++#ifndef __UCLIBC_HAS_XLOCALE__
++#define __nl_langinfo_l(N, L) nl_langinfo((N))
++#endif
++
++namespace std
++{
++ // Construct and return valid pattern consisting of some combination of:
++ // space none symbol sign value
++ money_base::pattern
++ money_base::_S_construct_pattern(char __precedes, char __space, char __posn)
++ {
++ pattern __ret;
++
++ // This insanely complicated routine attempts to construct a valid
++ // pattern for use with monyepunct. A couple of invariants:
++
++ // if (__precedes) symbol -> value
++ // else value -> symbol
++
++ // if (__space) space
++ // else none
++
++ // none == never first
++ // space never first or last
++
++ // Any elegant implementations of this are welcome.
++ switch (__posn)
++ {
++ case 0:
++ case 1:
++ // 1 The sign precedes the value and symbol.
++ __ret.field[0] = sign;
++ if (__space)
++ {
++ // Pattern starts with sign.
++ if (__precedes)
++ {
++ __ret.field[1] = symbol;
++ __ret.field[3] = value;
++ }
++ else
++ {
++ __ret.field[1] = value;
++ __ret.field[3] = symbol;
++ }
++ __ret.field[2] = space;
++ }
++ else
++ {
++ // Pattern starts with sign and ends with none.
++ if (__precedes)
++ {
++ __ret.field[1] = symbol;
++ __ret.field[2] = value;
++ }
++ else
++ {
++ __ret.field[1] = value;
++ __ret.field[2] = symbol;
++ }
++ __ret.field[3] = none;
++ }
++ break;
++ case 2:
++ // 2 The sign follows the value and symbol.
++ if (__space)
++ {
++ // Pattern either ends with sign.
++ if (__precedes)
++ {
++ __ret.field[0] = symbol;
++ __ret.field[2] = value;
++ }
++ else
++ {
++ __ret.field[0] = value;
++ __ret.field[2] = symbol;
++ }
++ __ret.field[1] = space;
++ __ret.field[3] = sign;
++ }
++ else
++ {
++ // Pattern ends with sign then none.
++ if (__precedes)
++ {
++ __ret.field[0] = symbol;
++ __ret.field[1] = value;
++ }
++ else
++ {
++ __ret.field[0] = value;
++ __ret.field[1] = symbol;
++ }
++ __ret.field[2] = sign;
++ __ret.field[3] = none;
++ }
++ break;
++ case 3:
++ // 3 The sign immediately precedes the symbol.
++ if (__precedes)
++ {
++ __ret.field[0] = sign;
++ __ret.field[1] = symbol;
++ if (__space)
++ {
++ __ret.field[2] = space;
++ __ret.field[3] = value;
++ }
++ else
++ {
++ __ret.field[2] = value;
++ __ret.field[3] = none;
++ }
++ }
++ else
++ {
++ __ret.field[0] = value;
++ if (__space)
++ {
++ __ret.field[1] = space;
++ __ret.field[2] = sign;
++ __ret.field[3] = symbol;
++ }
++ else
++ {
++ __ret.field[1] = sign;
++ __ret.field[2] = symbol;
++ __ret.field[3] = none;
++ }
++ }
++ break;
++ case 4:
++ // 4 The sign immediately follows the symbol.
++ if (__precedes)
++ {
++ __ret.field[0] = symbol;
++ __ret.field[1] = sign;
++ if (__space)
++ {
++ __ret.field[2] = space;
++ __ret.field[3] = value;
++ }
++ else
++ {
++ __ret.field[2] = value;
++ __ret.field[3] = none;
++ }
++ }
++ else
++ {
++ __ret.field[0] = value;
++ if (__space)
++ {
++ __ret.field[1] = space;
++ __ret.field[2] = symbol;
++ __ret.field[3] = sign;
++ }
++ else
++ {
++ __ret.field[1] = symbol;
++ __ret.field[2] = sign;
++ __ret.field[3] = none;
++ }
++ }
++ break;
++ default:
++ ;
++ }
++ return __ret;
++ }
++
++ template<>
++ void
++ moneypunct<char, true>::_M_initialize_moneypunct(__c_locale __cloc,
++ const char*)
++ {
++ if (!_M_data)
++ _M_data = new __moneypunct_cache<char, true>;
++
++ if (!__cloc)
++ {
++ // "C" locale
++ _M_data->_M_decimal_point = '.';
++ _M_data->_M_thousands_sep = ',';
++ _M_data->_M_grouping = "";
++ _M_data->_M_grouping_size = 0;
++ _M_data->_M_curr_symbol = "";
++ _M_data->_M_curr_symbol_size = 0;
++ _M_data->_M_positive_sign = "";
++ _M_data->_M_positive_sign_size = 0;
++ _M_data->_M_negative_sign = "";
++ _M_data->_M_negative_sign_size = 0;
++ _M_data->_M_frac_digits = 0;
++ _M_data->_M_pos_format = money_base::_S_default_pattern;
++ _M_data->_M_neg_format = money_base::_S_default_pattern;
++
++ for (size_t __i = 0; __i < money_base::_S_end; ++__i)
++ _M_data->_M_atoms[__i] = money_base::_S_atoms[__i];
++ }
++ else
++ {
++ // Named locale.
++ _M_data->_M_decimal_point = *(__nl_langinfo_l(__MON_DECIMAL_POINT,
++ __cloc));
++ _M_data->_M_thousands_sep = *(__nl_langinfo_l(__MON_THOUSANDS_SEP,
++ __cloc));
++ _M_data->_M_grouping = __nl_langinfo_l(__MON_GROUPING, __cloc);
++ _M_data->_M_grouping_size = strlen(_M_data->_M_grouping);
++ _M_data->_M_positive_sign = __nl_langinfo_l(__POSITIVE_SIGN, __cloc);
++ _M_data->_M_positive_sign_size = strlen(_M_data->_M_positive_sign);
++
++ char __nposn = *(__nl_langinfo_l(__INT_N_SIGN_POSN, __cloc));
++ if (!__nposn)
++ _M_data->_M_negative_sign = "()";
++ else
++ _M_data->_M_negative_sign = __nl_langinfo_l(__NEGATIVE_SIGN,
++ __cloc);
++ _M_data->_M_negative_sign_size = strlen(_M_data->_M_negative_sign);
++
++ // _Intl == true
++ _M_data->_M_curr_symbol = __nl_langinfo_l(__INT_CURR_SYMBOL, __cloc);
++ _M_data->_M_curr_symbol_size = strlen(_M_data->_M_curr_symbol);
++ _M_data->_M_frac_digits = *(__nl_langinfo_l(__INT_FRAC_DIGITS,
++ __cloc));
++ char __pprecedes = *(__nl_langinfo_l(__INT_P_CS_PRECEDES, __cloc));
++ char __pspace = *(__nl_langinfo_l(__INT_P_SEP_BY_SPACE, __cloc));
++ char __pposn = *(__nl_langinfo_l(__INT_P_SIGN_POSN, __cloc));
++ _M_data->_M_pos_format = _S_construct_pattern(__pprecedes, __pspace,
++ __pposn);
++ char __nprecedes = *(__nl_langinfo_l(__INT_N_CS_PRECEDES, __cloc));
++ char __nspace = *(__nl_langinfo_l(__INT_N_SEP_BY_SPACE, __cloc));
++ _M_data->_M_neg_format = _S_construct_pattern(__nprecedes, __nspace,
++ __nposn);
++ }
++ }
++
++ template<>
++ void
++ moneypunct<char, false>::_M_initialize_moneypunct(__c_locale __cloc,
++ const char*)
++ {
++ if (!_M_data)
++ _M_data = new __moneypunct_cache<char, false>;
++
++ if (!__cloc)
++ {
++ // "C" locale
++ _M_data->_M_decimal_point = '.';
++ _M_data->_M_thousands_sep = ',';
++ _M_data->_M_grouping = "";
++ _M_data->_M_grouping_size = 0;
++ _M_data->_M_curr_symbol = "";
++ _M_data->_M_curr_symbol_size = 0;
++ _M_data->_M_positive_sign = "";
++ _M_data->_M_positive_sign_size = 0;
++ _M_data->_M_negative_sign = "";
++ _M_data->_M_negative_sign_size = 0;
++ _M_data->_M_frac_digits = 0;
++ _M_data->_M_pos_format = money_base::_S_default_pattern;
++ _M_data->_M_neg_format = money_base::_S_default_pattern;
++
++ for (size_t __i = 0; __i < money_base::_S_end; ++__i)
++ _M_data->_M_atoms[__i] = money_base::_S_atoms[__i];
++ }
++ else
++ {
++ // Named locale.
++ _M_data->_M_decimal_point = *(__nl_langinfo_l(__MON_DECIMAL_POINT,
++ __cloc));
++ _M_data->_M_thousands_sep = *(__nl_langinfo_l(__MON_THOUSANDS_SEP,
++ __cloc));
++ _M_data->_M_grouping = __nl_langinfo_l(__MON_GROUPING, __cloc);
++ _M_data->_M_grouping_size = strlen(_M_data->_M_grouping);
++ _M_data->_M_positive_sign = __nl_langinfo_l(__POSITIVE_SIGN, __cloc);
++ _M_data->_M_positive_sign_size = strlen(_M_data->_M_positive_sign);
++
++ char __nposn = *(__nl_langinfo_l(__N_SIGN_POSN, __cloc));
++ if (!__nposn)
++ _M_data->_M_negative_sign = "()";
++ else
++ _M_data->_M_negative_sign = __nl_langinfo_l(__NEGATIVE_SIGN,
++ __cloc);
++ _M_data->_M_negative_sign_size = strlen(_M_data->_M_negative_sign);
++
++ // _Intl == false
++ _M_data->_M_curr_symbol = __nl_langinfo_l(__CURRENCY_SYMBOL, __cloc);
++ _M_data->_M_curr_symbol_size = strlen(_M_data->_M_curr_symbol);
++ _M_data->_M_frac_digits = *(__nl_langinfo_l(__FRAC_DIGITS, __cloc));
++ char __pprecedes = *(__nl_langinfo_l(__P_CS_PRECEDES, __cloc));
++ char __pspace = *(__nl_langinfo_l(__P_SEP_BY_SPACE, __cloc));
++ char __pposn = *(__nl_langinfo_l(__P_SIGN_POSN, __cloc));
++ _M_data->_M_pos_format = _S_construct_pattern(__pprecedes, __pspace,
++ __pposn);
++ char __nprecedes = *(__nl_langinfo_l(__N_CS_PRECEDES, __cloc));
++ char __nspace = *(__nl_langinfo_l(__N_SEP_BY_SPACE, __cloc));
++ _M_data->_M_neg_format = _S_construct_pattern(__nprecedes, __nspace,
++ __nposn);
++ }
++ }
++
++ template<>
++ moneypunct<char, true>::~moneypunct()
++ { delete _M_data; }
++
++ template<>
++ moneypunct<char, false>::~moneypunct()
++ { delete _M_data; }
++
++#ifdef _GLIBCXX_USE_WCHAR_T
++ template<>
++ void
++ moneypunct<wchar_t, true>::_M_initialize_moneypunct(__c_locale __cloc,
++#ifdef __UCLIBC_HAS_XLOCALE__
++ const char*)
++#else
++ const char* __name)
++#endif
++ {
++ if (!_M_data)
++ _M_data = new __moneypunct_cache<wchar_t, true>;
++
++ if (!__cloc)
++ {
++ // "C" locale
++ _M_data->_M_decimal_point = L'.';
++ _M_data->_M_thousands_sep = L',';
++ _M_data->_M_grouping = "";
++ _M_data->_M_grouping_size = 0;
++ _M_data->_M_curr_symbol = L"";
++ _M_data->_M_curr_symbol_size = 0;
++ _M_data->_M_positive_sign = L"";
++ _M_data->_M_positive_sign_size = 0;
++ _M_data->_M_negative_sign = L"";
++ _M_data->_M_negative_sign_size = 0;
++ _M_data->_M_frac_digits = 0;
++ _M_data->_M_pos_format = money_base::_S_default_pattern;
++ _M_data->_M_neg_format = money_base::_S_default_pattern;
++
++ // Use ctype::widen code without the facet...
++ unsigned char uc;
++ for (size_t __i = 0; __i < money_base::_S_end; ++__i)
++ {
++ uc = static_cast<unsigned char>(money_base::_S_atoms[__i]);
++ _M_data->_M_atoms[__i] = btowc(uc);
++ }
++ }
++ else
++ {
++ // Named locale.
++#ifdef __UCLIBC_HAS_XLOCALE__
++ __c_locale __old = __uselocale(__cloc);
++#else
++ // Switch to named locale so that mbsrtowcs will work.
++ char* __old = strdup(setlocale(LC_ALL, NULL));
++ setlocale(LC_ALL, __name);
++#endif
++
++#ifdef __UCLIBC_MJN3_ONLY__
++#warning fix this... should be monetary
++#endif
++#ifdef __UCLIBC__
++# ifdef __UCLIBC_HAS_XLOCALE__
++ _M_data->_M_decimal_point = __cloc->decimal_point_wc;
++ _M_data->_M_thousands_sep = __cloc->thousands_sep_wc;
++# else
++ _M_data->_M_decimal_point = __global_locale->decimal_point_wc;
++ _M_data->_M_thousands_sep = __global_locale->thousands_sep_wc;
++# endif
++#else
++ union __s_and_w { const char *__s; unsigned int __w; } __u;
++ __u.__s = __nl_langinfo_l(_NL_MONETARY_DECIMAL_POINT_WC, __cloc);
++ _M_data->_M_decimal_point = static_cast<wchar_t>(__u.__w);
++
++ __u.__s = __nl_langinfo_l(_NL_MONETARY_THOUSANDS_SEP_WC, __cloc);
++ _M_data->_M_thousands_sep = static_cast<wchar_t>(__u.__w);
++#endif
++ _M_data->_M_grouping = __nl_langinfo_l(__MON_GROUPING, __cloc);
++ _M_data->_M_grouping_size = strlen(_M_data->_M_grouping);
++
++ const char* __cpossign = __nl_langinfo_l(__POSITIVE_SIGN, __cloc);
++ const char* __cnegsign = __nl_langinfo_l(__NEGATIVE_SIGN, __cloc);
++ const char* __ccurr = __nl_langinfo_l(__INT_CURR_SYMBOL, __cloc);
++
++ wchar_t* __wcs_ps = 0;
++ wchar_t* __wcs_ns = 0;
++ const char __nposn = *(__nl_langinfo_l(__INT_N_SIGN_POSN, __cloc));
++ try
++ {
++ mbstate_t __state;
++ size_t __len = strlen(__cpossign);
++ if (__len)
++ {
++ ++__len;
++ memset(&__state, 0, sizeof(mbstate_t));
++ __wcs_ps = new wchar_t[__len];
++ mbsrtowcs(__wcs_ps, &__cpossign, __len, &__state);
++ _M_data->_M_positive_sign = __wcs_ps;
++ }
++ else
++ _M_data->_M_positive_sign = L"";
++ _M_data->_M_positive_sign_size = wcslen(_M_data->_M_positive_sign);
++
++ __len = strlen(__cnegsign);
++ if (!__nposn)
++ _M_data->_M_negative_sign = L"()";
++ else if (__len)
++ {
++ ++__len;
++ memset(&__state, 0, sizeof(mbstate_t));
++ __wcs_ns = new wchar_t[__len];
++ mbsrtowcs(__wcs_ns, &__cnegsign, __len, &__state);
++ _M_data->_M_negative_sign = __wcs_ns;
++ }
++ else
++ _M_data->_M_negative_sign = L"";
++ _M_data->_M_negative_sign_size = wcslen(_M_data->_M_negative_sign);
++
++ // _Intl == true.
++ __len = strlen(__ccurr);
++ if (__len)
++ {
++ ++__len;
++ memset(&__state, 0, sizeof(mbstate_t));
++ wchar_t* __wcs = new wchar_t[__len];
++ mbsrtowcs(__wcs, &__ccurr, __len, &__state);
++ _M_data->_M_curr_symbol = __wcs;
++ }
++ else
++ _M_data->_M_curr_symbol = L"";
++ _M_data->_M_curr_symbol_size = wcslen(_M_data->_M_curr_symbol);
++ }
++ catch (...)
++ {
++ delete _M_data;
++ _M_data = 0;
++ delete __wcs_ps;
++ delete __wcs_ns;
++#ifdef __UCLIBC_HAS_XLOCALE__
++ __uselocale(__old);
++#else
++ setlocale(LC_ALL, __old);
++ free(__old);
++#endif
++ __throw_exception_again;
++ }
++
++ _M_data->_M_frac_digits = *(__nl_langinfo_l(__INT_FRAC_DIGITS,
++ __cloc));
++ char __pprecedes = *(__nl_langinfo_l(__INT_P_CS_PRECEDES, __cloc));
++ char __pspace = *(__nl_langinfo_l(__INT_P_SEP_BY_SPACE, __cloc));
++ char __pposn = *(__nl_langinfo_l(__INT_P_SIGN_POSN, __cloc));
++ _M_data->_M_pos_format = _S_construct_pattern(__pprecedes, __pspace,
++ __pposn);
++ char __nprecedes = *(__nl_langinfo_l(__INT_N_CS_PRECEDES, __cloc));
++ char __nspace = *(__nl_langinfo_l(__INT_N_SEP_BY_SPACE, __cloc));
++ _M_data->_M_neg_format = _S_construct_pattern(__nprecedes, __nspace,
++ __nposn);
++
++#ifdef __UCLIBC_HAS_XLOCALE__
++ __uselocale(__old);
++#else
++ setlocale(LC_ALL, __old);
++ free(__old);
++#endif
++ }
++ }
++
++ template<>
++ void
++ moneypunct<wchar_t, false>::_M_initialize_moneypunct(__c_locale __cloc,
++#ifdef __UCLIBC_HAS_XLOCALE__
++ const char*)
++#else
++ const char* __name)
++#endif
++ {
++ if (!_M_data)
++ _M_data = new __moneypunct_cache<wchar_t, false>;
++
++ if (!__cloc)
++ {
++ // "C" locale
++ _M_data->_M_decimal_point = L'.';
++ _M_data->_M_thousands_sep = L',';
++ _M_data->_M_grouping = "";
++ _M_data->_M_grouping_size = 0;
++ _M_data->_M_curr_symbol = L"";
++ _M_data->_M_curr_symbol_size = 0;
++ _M_data->_M_positive_sign = L"";
++ _M_data->_M_positive_sign_size = 0;
++ _M_data->_M_negative_sign = L"";
++ _M_data->_M_negative_sign_size = 0;
++ _M_data->_M_frac_digits = 0;
++ _M_data->_M_pos_format = money_base::_S_default_pattern;
++ _M_data->_M_neg_format = money_base::_S_default_pattern;
++
++ // Use ctype::widen code without the facet...
++ unsigned char uc;
++ for (size_t __i = 0; __i < money_base::_S_end; ++__i)
++ {
++ uc = static_cast<unsigned char>(money_base::_S_atoms[__i]);
++ _M_data->_M_atoms[__i] = btowc(uc);
++ }
++ }
++ else
++ {
++ // Named locale.
++#ifdef __UCLIBC_HAS_XLOCALE__
++ __c_locale __old = __uselocale(__cloc);
++#else
++ // Switch to named locale so that mbsrtowcs will work.
++ char* __old = strdup(setlocale(LC_ALL, NULL));
++ setlocale(LC_ALL, __name);
++#endif
++
++#ifdef __UCLIBC_MJN3_ONLY__
++#warning fix this... should be monetary
++#endif
++#ifdef __UCLIBC__
++# ifdef __UCLIBC_HAS_XLOCALE__
++ _M_data->_M_decimal_point = __cloc->decimal_point_wc;
++ _M_data->_M_thousands_sep = __cloc->thousands_sep_wc;
++# else
++ _M_data->_M_decimal_point = __global_locale->decimal_point_wc;
++ _M_data->_M_thousands_sep = __global_locale->thousands_sep_wc;
++# endif
++#else
++ union __s_and_w { const char *__s; unsigned int __w; } __u;
++ __u.__s = __nl_langinfo_l(_NL_MONETARY_DECIMAL_POINT_WC, __cloc);
++ _M_data->_M_decimal_point = static_cast<wchar_t>(__u.__w);
++
++ __u.__s = __nl_langinfo_l(_NL_MONETARY_THOUSANDS_SEP_WC, __cloc);
++ _M_data->_M_thousands_sep = static_cast<wchar_t>(__u.__w);
++#endif
++ _M_data->_M_grouping = __nl_langinfo_l(__MON_GROUPING, __cloc);
++ _M_data->_M_grouping_size = strlen(_M_data->_M_grouping);
++
++ const char* __cpossign = __nl_langinfo_l(__POSITIVE_SIGN, __cloc);
++ const char* __cnegsign = __nl_langinfo_l(__NEGATIVE_SIGN, __cloc);
++ const char* __ccurr = __nl_langinfo_l(__CURRENCY_SYMBOL, __cloc);
++
++ wchar_t* __wcs_ps = 0;
++ wchar_t* __wcs_ns = 0;
++ const char __nposn = *(__nl_langinfo_l(__N_SIGN_POSN, __cloc));
++ try
++ {
++ mbstate_t __state;
++ size_t __len;
++ __len = strlen(__cpossign);
++ if (__len)
++ {
++ ++__len;
++ memset(&__state, 0, sizeof(mbstate_t));
++ __wcs_ps = new wchar_t[__len];
++ mbsrtowcs(__wcs_ps, &__cpossign, __len, &__state);
++ _M_data->_M_positive_sign = __wcs_ps;
++ }
++ else
++ _M_data->_M_positive_sign = L"";
++ _M_data->_M_positive_sign_size = wcslen(_M_data->_M_positive_sign);
++
++ __len = strlen(__cnegsign);
++ if (!__nposn)
++ _M_data->_M_negative_sign = L"()";
++ else if (__len)
++ {
++ ++__len;
++ memset(&__state, 0, sizeof(mbstate_t));
++ __wcs_ns = new wchar_t[__len];
++ mbsrtowcs(__wcs_ns, &__cnegsign, __len, &__state);
++ _M_data->_M_negative_sign = __wcs_ns;
++ }
++ else
++ _M_data->_M_negative_sign = L"";
++ _M_data->_M_negative_sign_size = wcslen(_M_data->_M_negative_sign);
++
++ // _Intl == true.
++ __len = strlen(__ccurr);
++ if (__len)
++ {
++ ++__len;
++ memset(&__state, 0, sizeof(mbstate_t));
++ wchar_t* __wcs = new wchar_t[__len];
++ mbsrtowcs(__wcs, &__ccurr, __len, &__state);
++ _M_data->_M_curr_symbol = __wcs;
++ }
++ else
++ _M_data->_M_curr_symbol = L"";
++ _M_data->_M_curr_symbol_size = wcslen(_M_data->_M_curr_symbol);
++ }
++ catch (...)
++ {
++ delete _M_data;
++ _M_data = 0;
++ delete __wcs_ps;
++ delete __wcs_ns;
++#ifdef __UCLIBC_HAS_XLOCALE__
++ __uselocale(__old);
++#else
++ setlocale(LC_ALL, __old);
++ free(__old);
++#endif
++ __throw_exception_again;
++ }
++
++ _M_data->_M_frac_digits = *(__nl_langinfo_l(__FRAC_DIGITS, __cloc));
++ char __pprecedes = *(__nl_langinfo_l(__P_CS_PRECEDES, __cloc));
++ char __pspace = *(__nl_langinfo_l(__P_SEP_BY_SPACE, __cloc));
++ char __pposn = *(__nl_langinfo_l(__P_SIGN_POSN, __cloc));
++ _M_data->_M_pos_format = _S_construct_pattern(__pprecedes, __pspace,
++ __pposn);
++ char __nprecedes = *(__nl_langinfo_l(__N_CS_PRECEDES, __cloc));
++ char __nspace = *(__nl_langinfo_l(__N_SEP_BY_SPACE, __cloc));
++ _M_data->_M_neg_format = _S_construct_pattern(__nprecedes, __nspace,
++ __nposn);
++
++#ifdef __UCLIBC_HAS_XLOCALE__
++ __uselocale(__old);
++#else
++ setlocale(LC_ALL, __old);
++ free(__old);
++#endif
++ }
++ }
++
++ template<>
++ moneypunct<wchar_t, true>::~moneypunct()
++ {
++ if (_M_data->_M_positive_sign_size)
++ delete [] _M_data->_M_positive_sign;
++ if (_M_data->_M_negative_sign_size
++ && wcscmp(_M_data->_M_negative_sign, L"()") != 0)
++ delete [] _M_data->_M_negative_sign;
++ if (_M_data->_M_curr_symbol_size)
++ delete [] _M_data->_M_curr_symbol;
++ delete _M_data;
++ }
++
++ template<>
++ moneypunct<wchar_t, false>::~moneypunct()
++ {
++ if (_M_data->_M_positive_sign_size)
++ delete [] _M_data->_M_positive_sign;
++ if (_M_data->_M_negative_sign_size
++ && wcscmp(_M_data->_M_negative_sign, L"()") != 0)
++ delete [] _M_data->_M_negative_sign;
++ if (_M_data->_M_curr_symbol_size)
++ delete [] _M_data->_M_curr_symbol;
++ delete _M_data;
++ }
++#endif
++}
+diff -urN gcc-3.4.1-dist/libstdc++-v3/config/locale/uclibc/numeric_members.cc gcc-3.4.1/libstdc++-v3/config/locale/uclibc/numeric_members.cc
+--- gcc-3.4.1-dist/libstdc++-v3/config/locale/uclibc/numeric_members.cc 1969-12-31 18:00:00.000000000 -0600
++++ gcc-3.4.1/libstdc++-v3/config/locale/uclibc/numeric_members.cc 2004-07-28 13:00:52.000000000 -0500
+@@ -0,0 +1,183 @@
++// std::numpunct implementation details, GNU version -*- C++ -*-
++
++// Copyright (C) 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
++//
++// This file is part of the GNU ISO C++ Library. This library is free
++// software; you can redistribute it and/or modify it under the
++// terms of the GNU General Public License as published by the
++// Free Software Foundation; either version 2, or (at your option)
++// any later version.
++
++// This library is distributed in the hope that it will be useful,
++// but WITHOUT ANY WARRANTY; without even the implied warranty of
++// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++// GNU General Public License for more details.
++
++// You should have received a copy of the GNU General Public License along
++// with this library; see the file COPYING. If not, write to the Free
++// Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307,
++// USA.
++
++// As a special exception, you may use this file as part of a free software
++// library without restriction. Specifically, if other files instantiate
++// templates or use macros or inline functions from this file, or you compile
++// this file and link it with other files to produce an executable, this
++// file does not by itself cause the resulting executable to be covered by
++// the GNU General Public License. This exception does not however
++// invalidate any other reasons why the executable file might be covered by
++// the GNU General Public License.
++
++//
++// ISO C++ 14882: 22.2.3.1.2 numpunct virtual functions
++//
++
++// Written by Benjamin Kosnik <bkoz@redhat.com>
++
++#define _LIBC
++#include <locale>
++#undef _LIBC
++#include <bits/c++locale_internal.h>
++
++#ifdef __UCLIBC_MJN3_ONLY__
++#warning tailor for stub locale support
++#endif
++#ifndef __UCLIBC_HAS_XLOCALE__
++#define __nl_langinfo_l(N, L) nl_langinfo((N))
++#endif
++
++namespace std
++{
++ template<>
++ void
++ numpunct<char>::_M_initialize_numpunct(__c_locale __cloc)
++ {
++ if (!_M_data)
++ _M_data = new __numpunct_cache<char>;
++
++ if (!__cloc)
++ {
++ // "C" locale
++ _M_data->_M_grouping = "";
++ _M_data->_M_grouping_size = 0;
++ _M_data->_M_use_grouping = false;
++
++ _M_data->_M_decimal_point = '.';
++ _M_data->_M_thousands_sep = ',';
++
++ for (size_t __i = 0; __i < __num_base::_S_oend; ++__i)
++ _M_data->_M_atoms_out[__i] = __num_base::_S_atoms_out[__i];
++
++ for (size_t __j = 0; __j < __num_base::_S_iend; ++__j)
++ _M_data->_M_atoms_in[__j] = __num_base::_S_atoms_in[__j];
++ }
++ else
++ {
++ // Named locale.
++ _M_data->_M_decimal_point = *(__nl_langinfo_l(DECIMAL_POINT,
++ __cloc));
++ _M_data->_M_thousands_sep = *(__nl_langinfo_l(THOUSANDS_SEP,
++ __cloc));
++
++ // Check for NULL, which implies no grouping.
++ if (_M_data->_M_thousands_sep == '\0')
++ _M_data->_M_grouping = "";
++ else
++ _M_data->_M_grouping = __nl_langinfo_l(GROUPING, __cloc);
++ _M_data->_M_grouping_size = strlen(_M_data->_M_grouping);
++ }
++
++ // NB: There is no way to extact this info from posix locales.
++ // _M_truename = __nl_langinfo_l(YESSTR, __cloc);
++ _M_data->_M_truename = "true";
++ _M_data->_M_truename_size = strlen(_M_data->_M_truename);
++ // _M_falsename = __nl_langinfo_l(NOSTR, __cloc);
++ _M_data->_M_falsename = "false";
++ _M_data->_M_falsename_size = strlen(_M_data->_M_falsename);
++ }
++
++ template<>
++ numpunct<char>::~numpunct()
++ { delete _M_data; }
++
++#ifdef _GLIBCXX_USE_WCHAR_T
++ template<>
++ void
++ numpunct<wchar_t>::_M_initialize_numpunct(__c_locale __cloc)
++ {
++ if (!_M_data)
++ _M_data = new __numpunct_cache<wchar_t>;
++
++ if (!__cloc)
++ {
++ // "C" locale
++ _M_data->_M_grouping = "";
++ _M_data->_M_grouping_size = 0;
++ _M_data->_M_use_grouping = false;
++
++ _M_data->_M_decimal_point = L'.';
++ _M_data->_M_thousands_sep = L',';
++
++#if __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ > 2)
++ __c_locale __old = __uselocale(_S_get_c_locale());
++#endif
++ // Use ctype::widen code without the facet...
++ unsigned char uc;
++ for (size_t __i = 0; __i < __num_base::_S_oend; ++__i)
++ {
++ uc = static_cast<unsigned char>(__num_base::_S_atoms_out[__i]);
++ _M_data->_M_atoms_out[__i] = btowc(uc);
++ }
++
++ for (size_t __j = 0; __j < __num_base::_S_iend; ++__j)
++ {
++ uc = static_cast<unsigned char>(__num_base::_S_atoms_in[__j]);
++ _M_data->_M_atoms_in[__j] = btowc(uc);
++ }
++#if __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ > 2)
++ __uselocale(__old);
++#endif
++ }
++ else
++ {
++ // Named locale.
++#ifdef __UCLIBC_MJN3_ONLY__
++#warning fix this
++#endif
++#ifdef __UCLIBC__
++# ifdef __UCLIBC_HAS_XLOCALE__
++ _M_data->_M_decimal_point = __cloc->decimal_point_wc;
++ _M_data->_M_thousands_sep = __cloc->thousands_sep_wc;
++# else
++ _M_data->_M_decimal_point = __global_locale->decimal_point_wc;
++ _M_data->_M_thousands_sep = __global_locale->thousands_sep_wc;
++# endif
++#else
++ union __s_and_w { const char *__s; unsigned int __w; } __u;
++ __u.__s = __nl_langinfo_l(_NL_NUMERIC_DECIMAL_POINT_WC, __cloc);
++ _M_data->_M_decimal_point = static_cast<wchar_t>(__u.__w);
++
++ __u.__s = __nl_langinfo_l(_NL_NUMERIC_THOUSANDS_SEP_WC, __cloc);
++ _M_data->_M_thousands_sep = static_cast<wchar_t>(__u.__w);
++#endif
++
++ if (_M_data->_M_thousands_sep == L'\0')
++ _M_data->_M_grouping = "";
++ else
++ _M_data->_M_grouping = __nl_langinfo_l(GROUPING, __cloc);
++ _M_data->_M_grouping_size = strlen(_M_data->_M_grouping);
++ }
++
++ // NB: There is no way to extact this info from posix locales.
++ // _M_truename = __nl_langinfo_l(YESSTR, __cloc);
++ _M_data->_M_truename = L"true";
++ _M_data->_M_truename_size = wcslen(_M_data->_M_truename);
++ // _M_falsename = __nl_langinfo_l(NOSTR, __cloc);
++ _M_data->_M_falsename = L"false";
++ _M_data->_M_falsename_size = wcslen(_M_data->_M_falsename);
++ }
++
++ template<>
++ numpunct<wchar_t>::~numpunct()
++ { delete _M_data; }
++ #endif
++}
+diff -urN gcc-3.4.1-dist/libstdc++-v3/config/locale/uclibc/time_members.cc gcc-3.4.1/libstdc++-v3/config/locale/uclibc/time_members.cc
+--- gcc-3.4.1-dist/libstdc++-v3/config/locale/uclibc/time_members.cc 1969-12-31 18:00:00.000000000 -0600
++++ gcc-3.4.1/libstdc++-v3/config/locale/uclibc/time_members.cc 2004-07-28 13:00:52.000000000 -0500
+@@ -0,0 +1,347 @@
++// std::time_get, std::time_put implementation, GNU version -*- C++ -*-
++
++// Copyright (C) 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
++//
++// This file is part of the GNU ISO C++ Library. This library is free
++// software; you can redistribute it and/or modify it under the
++// terms of the GNU General Public License as published by the
++// Free Software Foundation; either version 2, or (at your option)
++// any later version.
++
++// This library is distributed in the hope that it will be useful,
++// but WITHOUT ANY WARRANTY; without even the implied warranty of
++// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++// GNU General Public License for more details.
++
++// You should have received a copy of the GNU General Public License along
++// with this library; see the file COPYING. If not, write to the Free
++// Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307,
++// USA.
++
++// As a special exception, you may use this file as part of a free software
++// library without restriction. Specifically, if other files instantiate
++// templates or use macros or inline functions from this file, or you compile
++// this file and link it with other files to produce an executable, this
++// file does not by itself cause the resulting executable to be covered by
++// the GNU General Public License. This exception does not however
++// invalidate any other reasons why the executable file might be covered by
++// the GNU General Public License.
++
++//
++// ISO C++ 14882: 22.2.5.1.2 - time_get virtual functions
++// ISO C++ 14882: 22.2.5.3.2 - time_put virtual functions
++//
++
++// Written by Benjamin Kosnik <bkoz@redhat.com>
++
++#include <locale>
++#include <bits/c++locale_internal.h>
++
++#ifdef __UCLIBC_MJN3_ONLY__
++#warning tailor for stub locale support
++#endif
++#ifndef __UCLIBC_HAS_XLOCALE__
++#define __nl_langinfo_l(N, L) nl_langinfo((N))
++#endif
++
++namespace std
++{
++ template<>
++ void
++ __timepunct<char>::
++ _M_put(char* __s, size_t __maxlen, const char* __format,
++ const tm* __tm) const
++ {
++#ifdef __UCLIBC_HAS_XLOCALE__
++ __strftime_l(__s, __maxlen, __format, __tm, _M_c_locale_timepunct);
++#else
++ char* __old = strdup(setlocale(LC_ALL, NULL));
++ setlocale(LC_ALL, _M_name_timepunct);
++ strftime(__s, __maxlen, __format, __tm);
++ setlocale(LC_ALL, __old);
++ free(__old);
++#endif
++ }
++
++ template<>
++ void
++ __timepunct<char>::_M_initialize_timepunct(__c_locale __cloc)
++ {
++ if (!_M_data)
++ _M_data = new __timepunct_cache<char>;
++
++ if (!__cloc)
++ {
++ // "C" locale
++ _M_c_locale_timepunct = _S_get_c_locale();
++
++ _M_data->_M_date_format = "%m/%d/%y";
++ _M_data->_M_date_era_format = "%m/%d/%y";
++ _M_data->_M_time_format = "%H:%M:%S";
++ _M_data->_M_time_era_format = "%H:%M:%S";
++ _M_data->_M_date_time_format = "";
++ _M_data->_M_date_time_era_format = "";
++ _M_data->_M_am = "AM";
++ _M_data->_M_pm = "PM";
++ _M_data->_M_am_pm_format = "";
++
++ // Day names, starting with "C"'s Sunday.
++ _M_data->_M_day1 = "Sunday";
++ _M_data->_M_day2 = "Monday";
++ _M_data->_M_day3 = "Tuesday";
++ _M_data->_M_day4 = "Wednesday";
++ _M_data->_M_day5 = "Thursday";
++ _M_data->_M_day6 = "Friday";
++ _M_data->_M_day7 = "Saturday";
++
++ // Abbreviated day names, starting with "C"'s Sun.
++ _M_data->_M_aday1 = "Sun";
++ _M_data->_M_aday2 = "Mon";
++ _M_data->_M_aday3 = "Tue";
++ _M_data->_M_aday4 = "Wed";
++ _M_data->_M_aday5 = "Thu";
++ _M_data->_M_aday6 = "Fri";
++ _M_data->_M_aday7 = "Sat";
++
++ // Month names, starting with "C"'s January.
++ _M_data->_M_month01 = "January";
++ _M_data->_M_month02 = "February";
++ _M_data->_M_month03 = "March";
++ _M_data->_M_month04 = "April";
++ _M_data->_M_month05 = "May";
++ _M_data->_M_month06 = "June";
++ _M_data->_M_month07 = "July";
++ _M_data->_M_month08 = "August";
++ _M_data->_M_month09 = "September";
++ _M_data->_M_month10 = "October";
++ _M_data->_M_month11 = "November";
++ _M_data->_M_month12 = "December";
++
++ // Abbreviated month names, starting with "C"'s Jan.
++ _M_data->_M_amonth01 = "Jan";
++ _M_data->_M_amonth02 = "Feb";
++ _M_data->_M_amonth03 = "Mar";
++ _M_data->_M_amonth04 = "Apr";
++ _M_data->_M_amonth05 = "May";
++ _M_data->_M_amonth06 = "Jun";
++ _M_data->_M_amonth07 = "Jul";
++ _M_data->_M_amonth08 = "Aug";
++ _M_data->_M_amonth09 = "Sep";
++ _M_data->_M_amonth10 = "Oct";
++ _M_data->_M_amonth11 = "Nov";
++ _M_data->_M_amonth12 = "Dec";
++ }
++ else
++ {
++ _M_c_locale_timepunct = _S_clone_c_locale(__cloc);
++
++ _M_data->_M_date_format = __nl_langinfo_l(D_FMT, __cloc);
++ _M_data->_M_date_era_format = __nl_langinfo_l(ERA_D_FMT, __cloc);
++ _M_data->_M_time_format = __nl_langinfo_l(T_FMT, __cloc);
++ _M_data->_M_time_era_format = __nl_langinfo_l(ERA_T_FMT, __cloc);
++ _M_data->_M_date_time_format = __nl_langinfo_l(D_T_FMT, __cloc);
++ _M_data->_M_date_time_era_format = __nl_langinfo_l(ERA_D_T_FMT, __cloc);
++ _M_data->_M_am = __nl_langinfo_l(AM_STR, __cloc);
++ _M_data->_M_pm = __nl_langinfo_l(PM_STR, __cloc);
++ _M_data->_M_am_pm_format = __nl_langinfo_l(T_FMT_AMPM, __cloc);
++
++ // Day names, starting with "C"'s Sunday.
++ _M_data->_M_day1 = __nl_langinfo_l(DAY_1, __cloc);
++ _M_data->_M_day2 = __nl_langinfo_l(DAY_2, __cloc);
++ _M_data->_M_day3 = __nl_langinfo_l(DAY_3, __cloc);
++ _M_data->_M_day4 = __nl_langinfo_l(DAY_4, __cloc);
++ _M_data->_M_day5 = __nl_langinfo_l(DAY_5, __cloc);
++ _M_data->_M_day6 = __nl_langinfo_l(DAY_6, __cloc);
++ _M_data->_M_day7 = __nl_langinfo_l(DAY_7, __cloc);
++
++ // Abbreviated day names, starting with "C"'s Sun.
++ _M_data->_M_aday1 = __nl_langinfo_l(ABDAY_1, __cloc);
++ _M_data->_M_aday2 = __nl_langinfo_l(ABDAY_2, __cloc);
++ _M_data->_M_aday3 = __nl_langinfo_l(ABDAY_3, __cloc);
++ _M_data->_M_aday4 = __nl_langinfo_l(ABDAY_4, __cloc);
++ _M_data->_M_aday5 = __nl_langinfo_l(ABDAY_5, __cloc);
++ _M_data->_M_aday6 = __nl_langinfo_l(ABDAY_6, __cloc);
++ _M_data->_M_aday7 = __nl_langinfo_l(ABDAY_7, __cloc);
++
++ // Month names, starting with "C"'s January.
++ _M_data->_M_month01 = __nl_langinfo_l(MON_1, __cloc);
++ _M_data->_M_month02 = __nl_langinfo_l(MON_2, __cloc);
++ _M_data->_M_month03 = __nl_langinfo_l(MON_3, __cloc);
++ _M_data->_M_month04 = __nl_langinfo_l(MON_4, __cloc);
++ _M_data->_M_month05 = __nl_langinfo_l(MON_5, __cloc);
++ _M_data->_M_month06 = __nl_langinfo_l(MON_6, __cloc);
++ _M_data->_M_month07 = __nl_langinfo_l(MON_7, __cloc);
++ _M_data->_M_month08 = __nl_langinfo_l(MON_8, __cloc);
++ _M_data->_M_month09 = __nl_langinfo_l(MON_9, __cloc);
++ _M_data->_M_month10 = __nl_langinfo_l(MON_10, __cloc);
++ _M_data->_M_month11 = __nl_langinfo_l(MON_11, __cloc);
++ _M_data->_M_month12 = __nl_langinfo_l(MON_12, __cloc);
++
++ // Abbreviated month names, starting with "C"'s Jan.
++ _M_data->_M_amonth01 = __nl_langinfo_l(ABMON_1, __cloc);
++ _M_data->_M_amonth02 = __nl_langinfo_l(ABMON_2, __cloc);
++ _M_data->_M_amonth03 = __nl_langinfo_l(ABMON_3, __cloc);
++ _M_data->_M_amonth04 = __nl_langinfo_l(ABMON_4, __cloc);
++ _M_data->_M_amonth05 = __nl_langinfo_l(ABMON_5, __cloc);
++ _M_data->_M_amonth06 = __nl_langinfo_l(ABMON_6, __cloc);
++ _M_data->_M_amonth07 = __nl_langinfo_l(ABMON_7, __cloc);
++ _M_data->_M_amonth08 = __nl_langinfo_l(ABMON_8, __cloc);
++ _M_data->_M_amonth09 = __nl_langinfo_l(ABMON_9, __cloc);
++ _M_data->_M_amonth10 = __nl_langinfo_l(ABMON_10, __cloc);
++ _M_data->_M_amonth11 = __nl_langinfo_l(ABMON_11, __cloc);
++ _M_data->_M_amonth12 = __nl_langinfo_l(ABMON_12, __cloc);
++ }
++ }
++
++#ifdef _GLIBCXX_USE_WCHAR_T
++ template<>
++ void
++ __timepunct<wchar_t>::
++ _M_put(wchar_t* __s, size_t __maxlen, const wchar_t* __format,
++ const tm* __tm) const
++ {
++#ifdef __UCLIBC_HAS_XLOCALE__
++ __wcsftime_l(__s, __maxlen, __format, __tm, _M_c_locale_timepunct);
++#else
++ char* __old = strdup(setlocale(LC_ALL, NULL));
++ setlocale(LC_ALL, _M_name_timepunct);
++ wcsftime(__s, __maxlen, __format, __tm);
++ setlocale(LC_ALL, __old);
++ free(__old);
++#endif
++ }
++
++ template<>
++ void
++ __timepunct<wchar_t>::_M_initialize_timepunct(__c_locale __cloc)
++ {
++ if (!_M_data)
++ _M_data = new __timepunct_cache<wchar_t>;
++
++#warning wide time stuff
++// if (!__cloc)
++ {
++ // "C" locale
++ _M_c_locale_timepunct = _S_get_c_locale();
++
++ _M_data->_M_date_format = L"%m/%d/%y";
++ _M_data->_M_date_era_format = L"%m/%d/%y";
++ _M_data->_M_time_format = L"%H:%M:%S";
++ _M_data->_M_time_era_format = L"%H:%M:%S";
++ _M_data->_M_date_time_format = L"";
++ _M_data->_M_date_time_era_format = L"";
++ _M_data->_M_am = L"AM";
++ _M_data->_M_pm = L"PM";
++ _M_data->_M_am_pm_format = L"";
++
++ // Day names, starting with "C"'s Sunday.
++ _M_data->_M_day1 = L"Sunday";
++ _M_data->_M_day2 = L"Monday";
++ _M_data->_M_day3 = L"Tuesday";
++ _M_data->_M_day4 = L"Wednesday";
++ _M_data->_M_day5 = L"Thursday";
++ _M_data->_M_day6 = L"Friday";
++ _M_data->_M_day7 = L"Saturday";
++
++ // Abbreviated day names, starting with "C"'s Sun.
++ _M_data->_M_aday1 = L"Sun";
++ _M_data->_M_aday2 = L"Mon";
++ _M_data->_M_aday3 = L"Tue";
++ _M_data->_M_aday4 = L"Wed";
++ _M_data->_M_aday5 = L"Thu";
++ _M_data->_M_aday6 = L"Fri";
++ _M_data->_M_aday7 = L"Sat";
++
++ // Month names, starting with "C"'s January.
++ _M_data->_M_month01 = L"January";
++ _M_data->_M_month02 = L"February";
++ _M_data->_M_month03 = L"March";
++ _M_data->_M_month04 = L"April";
++ _M_data->_M_month05 = L"May";
++ _M_data->_M_month06 = L"June";
++ _M_data->_M_month07 = L"July";
++ _M_data->_M_month08 = L"August";
++ _M_data->_M_month09 = L"September";
++ _M_data->_M_month10 = L"October";
++ _M_data->_M_month11 = L"November";
++ _M_data->_M_month12 = L"December";
++
++ // Abbreviated month names, starting with "C"'s Jan.
++ _M_data->_M_amonth01 = L"Jan";
++ _M_data->_M_amonth02 = L"Feb";
++ _M_data->_M_amonth03 = L"Mar";
++ _M_data->_M_amonth04 = L"Apr";
++ _M_data->_M_amonth05 = L"May";
++ _M_data->_M_amonth06 = L"Jun";
++ _M_data->_M_amonth07 = L"Jul";
++ _M_data->_M_amonth08 = L"Aug";
++ _M_data->_M_amonth09 = L"Sep";
++ _M_data->_M_amonth10 = L"Oct";
++ _M_data->_M_amonth11 = L"Nov";
++ _M_data->_M_amonth12 = L"Dec";
++ }
++#if 0
++ else
++ {
++ _M_c_locale_timepunct = _S_clone_c_locale(__cloc);
++
++ _M_data->_M_date_format = reinterpret_cast<wchar_t*>(__nl_langinfo_l(_NL_WD_FMT, __cloc));
++ _M_data->_M_date_era_format = reinterpret_cast<wchar_t*>(__nl_langinfo_l(_NL_WERA_D_FMT, __cloc));
++ _M_data->_M_time_format = reinterpret_cast<wchar_t*>(__nl_langinfo_l(_NL_WT_FMT, __cloc));
++ _M_data->_M_time_era_format = reinterpret_cast<wchar_t*>(__nl_langinfo_l(_NL_WERA_T_FMT, __cloc));
++ _M_data->_M_date_time_format = reinterpret_cast<wchar_t*>(__nl_langinfo_l(_NL_WD_T_FMT, __cloc));
++ _M_data->_M_date_time_era_format = reinterpret_cast<wchar_t*>(__nl_langinfo_l(_NL_WERA_D_T_FMT, __cloc));
++ _M_data->_M_am = reinterpret_cast<wchar_t*>(__nl_langinfo_l(_NL_WAM_STR, __cloc));
++ _M_data->_M_pm = reinterpret_cast<wchar_t*>(__nl_langinfo_l(_NL_WPM_STR, __cloc));
++ _M_data->_M_am_pm_format = reinterpret_cast<wchar_t*>(__nl_langinfo_l(_NL_WT_FMT_AMPM, __cloc));
++
++ // Day names, starting with "C"'s Sunday.
++ _M_data->_M_day1 = reinterpret_cast<wchar_t*>(__nl_langinfo_l(_NL_WDAY_1, __cloc));
++ _M_data->_M_day2 = reinterpret_cast<wchar_t*>(__nl_langinfo_l(_NL_WDAY_2, __cloc));
++ _M_data->_M_day3 = reinterpret_cast<wchar_t*>(__nl_langinfo_l(_NL_WDAY_3, __cloc));
++ _M_data->_M_day4 = reinterpret_cast<wchar_t*>(__nl_langinfo_l(_NL_WDAY_4, __cloc));
++ _M_data->_M_day5 = reinterpret_cast<wchar_t*>(__nl_langinfo_l(_NL_WDAY_5, __cloc));
++ _M_data->_M_day6 = reinterpret_cast<wchar_t*>(__nl_langinfo_l(_NL_WDAY_6, __cloc));
++ _M_data->_M_day7 = reinterpret_cast<wchar_t*>(__nl_langinfo_l(_NL_WDAY_7, __cloc));
++
++ // Abbreviated day names, starting with "C"'s Sun.
++ _M_data->_M_aday1 = reinterpret_cast<wchar_t*>(__nl_langinfo_l(_NL_WABDAY_1, __cloc));
++ _M_data->_M_aday2 = reinterpret_cast<wchar_t*>(__nl_langinfo_l(_NL_WABDAY_2, __cloc));
++ _M_data->_M_aday3 = reinterpret_cast<wchar_t*>(__nl_langinfo_l(_NL_WABDAY_3, __cloc));
++ _M_data->_M_aday4 = reinterpret_cast<wchar_t*>(__nl_langinfo_l(_NL_WABDAY_4, __cloc));
++ _M_data->_M_aday5 = reinterpret_cast<wchar_t*>(__nl_langinfo_l(_NL_WABDAY_5, __cloc));
++ _M_data->_M_aday6 = reinterpret_cast<wchar_t*>(__nl_langinfo_l(_NL_WABDAY_6, __cloc));
++ _M_data->_M_aday7 = reinterpret_cast<wchar_t*>(__nl_langinfo_l(_NL_WABDAY_7, __cloc));
++
++ // Month names, starting with "C"'s January.
++ _M_data->_M_month01 = reinterpret_cast<wchar_t*>(__nl_langinfo_l(_NL_WMON_1, __cloc));
++ _M_data->_M_month02 = reinterpret_cast<wchar_t*>(__nl_langinfo_l(_NL_WMON_2, __cloc));
++ _M_data->_M_month03 = reinterpret_cast<wchar_t*>(__nl_langinfo_l(_NL_WMON_3, __cloc));
++ _M_data->_M_month04 = reinterpret_cast<wchar_t*>(__nl_langinfo_l(_NL_WMON_4, __cloc));
++ _M_data->_M_month05 = reinterpret_cast<wchar_t*>(__nl_langinfo_l(_NL_WMON_5, __cloc));
++ _M_data->_M_month06 = reinterpret_cast<wchar_t*>(__nl_langinfo_l(_NL_WMON_6, __cloc));
++ _M_data->_M_month07 = reinterpret_cast<wchar_t*>(__nl_langinfo_l(_NL_WMON_7, __cloc));
++ _M_data->_M_month08 = reinterpret_cast<wchar_t*>(__nl_langinfo_l(_NL_WMON_8, __cloc));
++ _M_data->_M_month09 = reinterpret_cast<wchar_t*>(__nl_langinfo_l(_NL_WMON_9, __cloc));
++ _M_data->_M_month10 = reinterpret_cast<wchar_t*>(__nl_langinfo_l(_NL_WMON_10, __cloc));
++ _M_data->_M_month11 = reinterpret_cast<wchar_t*>(__nl_langinfo_l(_NL_WMON_11, __cloc));
++ _M_data->_M_month12 = reinterpret_cast<wchar_t*>(__nl_langinfo_l(_NL_WMON_12, __cloc));
++
++ // Abbreviated month names, starting with "C"'s Jan.
++ _M_data->_M_amonth01 = reinterpret_cast<wchar_t*>(__nl_langinfo_l(_NL_WABMON_1, __cloc));
++ _M_data->_M_amonth02 = reinterpret_cast<wchar_t*>(__nl_langinfo_l(_NL_WABMON_2, __cloc));
++ _M_data->_M_amonth03 = reinterpret_cast<wchar_t*>(__nl_langinfo_l(_NL_WABMON_3, __cloc));
++ _M_data->_M_amonth04 = reinterpret_cast<wchar_t*>(__nl_langinfo_l(_NL_WABMON_4, __cloc));
++ _M_data->_M_amonth05 = reinterpret_cast<wchar_t*>(__nl_langinfo_l(_NL_WABMON_5, __cloc));
++ _M_data->_M_amonth06 = reinterpret_cast<wchar_t*>(__nl_langinfo_l(_NL_WABMON_6, __cloc));
++ _M_data->_M_amonth07 = reinterpret_cast<wchar_t*>(__nl_langinfo_l(_NL_WABMON_7, __cloc));
++ _M_data->_M_amonth08 = reinterpret_cast<wchar_t*>(__nl_langinfo_l(_NL_WABMON_8, __cloc));
++ _M_data->_M_amonth09 = reinterpret_cast<wchar_t*>(__nl_langinfo_l(_NL_WABMON_9, __cloc));
++ _M_data->_M_amonth10 = reinterpret_cast<wchar_t*>(__nl_langinfo_l(_NL_WABMON_10, __cloc));
++ _M_data->_M_amonth11 = reinterpret_cast<wchar_t*>(__nl_langinfo_l(_NL_WABMON_11, __cloc));
++ _M_data->_M_amonth12 = reinterpret_cast<wchar_t*>(__nl_langinfo_l(_NL_WABMON_12, __cloc));
++ }
++#endif // 0
++ }
++#endif
++}
+diff -urN gcc-3.4.1-dist/libstdc++-v3/config/locale/uclibc/time_members.h gcc-3.4.1/libstdc++-v3/config/locale/uclibc/time_members.h
+--- gcc-3.4.1-dist/libstdc++-v3/config/locale/uclibc/time_members.h 1969-12-31 18:00:00.000000000 -0600
++++ gcc-3.4.1/libstdc++-v3/config/locale/uclibc/time_members.h 2004-07-28 13:00:52.000000000 -0500
+@@ -0,0 +1,68 @@
++// std::time_get, std::time_put implementation, GNU version -*- C++ -*-
++
++// Copyright (C) 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
++//
++// This file is part of the GNU ISO C++ Library. This library is free
++// software; you can redistribute it and/or modify it under the
++// terms of the GNU General Public License as published by the
++// Free Software Foundation; either version 2, or (at your option)
++// any later version.
++
++// This library is distributed in the hope that it will be useful,
++// but WITHOUT ANY WARRANTY; without even the implied warranty of
++// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++// GNU General Public License for more details.
++
++// You should have received a copy of the GNU General Public License along
++// with this library; see the file COPYING. If not, write to the Free
++// Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307,
++// USA.
++
++// As a special exception, you may use this file as part of a free software
++// library without restriction. Specifically, if other files instantiate
++// templates or use macros or inline functions from this file, or you compile
++// this file and link it with other files to produce an executable, this
++// file does not by itself cause the resulting executable to be covered by
++// the GNU General Public License. This exception does not however
++// invalidate any other reasons why the executable file might be covered by
++// the GNU General Public License.
++
++//
++// ISO C++ 14882: 22.2.5.1.2 - time_get functions
++// ISO C++ 14882: 22.2.5.3.2 - time_put functions
++//
++
++// Written by Benjamin Kosnik <bkoz@redhat.com>
++
++ template<typename _CharT>
++ __timepunct<_CharT>::__timepunct(size_t __refs)
++ : facet(__refs), _M_data(NULL), _M_c_locale_timepunct(NULL),
++ _M_name_timepunct(_S_get_c_name())
++ { _M_initialize_timepunct(); }
++
++ template<typename _CharT>
++ __timepunct<_CharT>::__timepunct(__cache_type* __cache, size_t __refs)
++ : facet(__refs), _M_data(__cache), _M_c_locale_timepunct(NULL),
++ _M_name_timepunct(_S_get_c_name())
++ { _M_initialize_timepunct(); }
++
++ template<typename _CharT>
++ __timepunct<_CharT>::__timepunct(__c_locale __cloc, const char* __s,
++ size_t __refs)
++ : facet(__refs), _M_data(NULL), _M_c_locale_timepunct(NULL),
++ _M_name_timepunct(__s)
++ {
++ char* __tmp = new char[std::strlen(__s) + 1];
++ std::strcpy(__tmp, __s);
++ _M_name_timepunct = __tmp;
++ _M_initialize_timepunct(__cloc);
++ }
++
++ template<typename _CharT>
++ __timepunct<_CharT>::~__timepunct()
++ {
++ if (_M_name_timepunct != _S_get_c_name())
++ delete [] _M_name_timepunct;
++ delete _M_data;
++ _S_destroy_c_locale(_M_c_locale_timepunct);
++ }
+diff -urN gcc-3.4.1-dist/libstdc++-v3/config/os/uclibc/ctype_base.h gcc-3.4.1/libstdc++-v3/config/os/uclibc/ctype_base.h
+--- gcc-3.4.1-dist/libstdc++-v3/config/os/uclibc/ctype_base.h 1969-12-31 18:00:00.000000000 -0600
++++ gcc-3.4.1/libstdc++-v3/config/os/uclibc/ctype_base.h 2004-07-28 13:00:52.000000000 -0500
+@@ -0,0 +1,58 @@
++// Locale support -*- C++ -*-
++
++// Copyright (C) 1997, 1998, 1999, 2000, 2002, 2003
++// Free Software Foundation, Inc.
++//
++// This file is part of the GNU ISO C++ Library. This library is free
++// software; you can redistribute it and/or modify it under the
++// terms of the GNU General Public License as published by the
++// Free Software Foundation; either version 2, or (at your option)
++// any later version.
++
++// This library is distributed in the hope that it will be useful,
++// but WITHOUT ANY WARRANTY; without even the implied warranty of
++// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++// GNU General Public License for more details.
++
++// You should have received a copy of the GNU General Public License along
++// with this library; see the file COPYING. If not, write to the Free
++// Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307,
++// USA.
++
++// As a special exception, you may use this file as part of a free software
++// library without restriction. Specifically, if other files instantiate
++// templates or use macros or inline functions from this file, or you compile
++// this file and link it with other files to produce an executable, this
++// file does not by itself cause the resulting executable to be covered by
++// the GNU General Public License. This exception does not however
++// invalidate any other reasons why the executable file might be covered by
++// the GNU General Public License.
++
++//
++// ISO C++ 14882: 22.1 Locales
++//
++
++// Information as gleaned from /usr/include/ctype.h
++
++ struct ctype_base
++ {
++ // Note: In uClibc, the following two types depend on configuration.
++
++ // Non-standard typedefs.
++ typedef const __ctype_touplow_t* __to_type;
++
++ // NB: Offsets into ctype<char>::_M_table force a particular size
++ // on the mask type. Because of this, we don't use an enum.
++ typedef __ctype_mask_t mask;
++ static const mask upper = _ISupper;
++ static const mask lower = _ISlower;
++ static const mask alpha = _ISalpha;
++ static const mask digit = _ISdigit;
++ static const mask xdigit = _ISxdigit;
++ static const mask space = _ISspace;
++ static const mask print = _ISprint;
++ static const mask graph = _ISalpha | _ISdigit | _ISpunct;
++ static const mask cntrl = _IScntrl;
++ static const mask punct = _ISpunct;
++ static const mask alnum = _ISalpha | _ISdigit;
++ };
+diff -urN gcc-3.4.1-dist/libstdc++-v3/config/os/uclibc/ctype_inline.h gcc-3.4.1/libstdc++-v3/config/os/uclibc/ctype_inline.h
+--- gcc-3.4.1-dist/libstdc++-v3/config/os/uclibc/ctype_inline.h 1969-12-31 18:00:00.000000000 -0600
++++ gcc-3.4.1/libstdc++-v3/config/os/uclibc/ctype_inline.h 2004-07-28 13:00:52.000000000 -0500
+@@ -0,0 +1,69 @@
++// Locale support -*- C++ -*-
++
++// Copyright (C) 2000, 2002 Free Software Foundation, Inc.
++//
++// This file is part of the GNU ISO C++ Library. This library is free
++// software; you can redistribute it and/or modify it under the
++// terms of the GNU General Public License as published by the
++// Free Software Foundation; either version 2, or (at your option)
++// any later version.
++
++// This library is distributed in the hope that it will be useful,
++// but WITHOUT ANY WARRANTY; without even the implied warranty of
++// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++// GNU General Public License for more details.
++
++// You should have received a copy of the GNU General Public License along
++// with this library; see the file COPYING. If not, write to the Free
++// Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307,
++// USA.
++
++// As a special exception, you may use this file as part of a free software
++// library without restriction. Specifically, if other files instantiate
++// templates or use macros or inline functions from this file, or you compile
++// this file and link it with other files to produce an executable, this
++// file does not by itself cause the resulting executable to be covered by
++// the GNU General Public License. This exception does not however
++// invalidate any other reasons why the executable file might be covered by
++// the GNU General Public License.
++
++//
++// ISO C++ 14882: 22.1 Locales
++//
++
++// ctype bits to be inlined go here. Non-inlinable (ie virtual do_*)
++// functions go in ctype.cc
++
++ bool
++ ctype<char>::
++ is(mask __m, char __c) const
++ { return _M_table[static_cast<unsigned char>(__c)] & __m; }
++
++ const char*
++ ctype<char>::
++ is(const char* __low, const char* __high, mask* __vec) const
++ {
++ while (__low < __high)
++ *__vec++ = _M_table[static_cast<unsigned char>(*__low++)];
++ return __high;
++ }
++
++ const char*
++ ctype<char>::
++ scan_is(mask __m, const char* __low, const char* __high) const
++ {
++ while (__low < __high
++ && !(_M_table[static_cast<unsigned char>(*__low)] & __m))
++ ++__low;
++ return __low;
++ }
++
++ const char*
++ ctype<char>::
++ scan_not(mask __m, const char* __low, const char* __high) const
++ {
++ while (__low < __high
++ && (_M_table[static_cast<unsigned char>(*__low)] & __m) != 0)
++ ++__low;
++ return __low;
++ }
+diff -urN gcc-3.4.1-dist/libstdc++-v3/config/os/uclibc/ctype_noninline.h gcc-3.4.1/libstdc++-v3/config/os/uclibc/ctype_noninline.h
+--- gcc-3.4.1-dist/libstdc++-v3/config/os/uclibc/ctype_noninline.h 1969-12-31 18:00:00.000000000 -0600
++++ gcc-3.4.1/libstdc++-v3/config/os/uclibc/ctype_noninline.h 2004-07-28 13:00:52.000000000 -0500
+@@ -0,0 +1,92 @@
++// Locale support -*- C++ -*-
++
++// Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2004
++// Free Software Foundation, Inc.
++//
++// This file is part of the GNU ISO C++ Library. This library is free
++// software; you can redistribute it and/or modify it under the
++// terms of the GNU General Public License as published by the
++// Free Software Foundation; either version 2, or (at your option)
++// any later version.
++
++// This library is distributed in the hope that it will be useful,
++// but WITHOUT ANY WARRANTY; without even the implied warranty of
++// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++// GNU General Public License for more details.
++
++// You should have received a copy of the GNU General Public License along
++// with this library; see the file COPYING. If not, write to the Free
++// Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307,
++// USA.
++
++// As a special exception, you may use this file as part of a free software
++// library without restriction. Specifically, if other files instantiate
++// templates or use macros or inline functions from this file, or you compile
++// this file and link it with other files to produce an executable, this
++// file does not by itself cause the resulting executable to be covered by
++// the GNU General Public License. This exception does not however
++// invalidate any other reasons why the executable file might be covered by
++// the GNU General Public License.
++
++//
++// ISO C++ 14882: 22.1 Locales
++//
++
++// Information as gleaned from /usr/include/ctype.h
++
++ const ctype_base::mask*
++ ctype<char>::classic_table() throw()
++ { return __C_ctype_b; }
++
++ ctype<char>::ctype(__c_locale, const mask* __table, bool __del,
++ size_t __refs)
++ : facet(__refs), _M_c_locale_ctype(_S_get_c_locale()),
++ _M_del(__table != 0 && __del), _M_widen_ok(0), _M_narrow_ok(0)
++ {
++ _M_toupper = __C_ctype_toupper;
++ _M_tolower = __C_ctype_tolower;
++ _M_table = __table ? __table : __C_ctype_b;
++ memset(_M_widen, 0, sizeof(_M_widen));
++ memset(_M_narrow, 0, sizeof(_M_narrow));
++ }
++
++ ctype<char>::ctype(const mask* __table, bool __del, size_t __refs)
++ : facet(__refs), _M_c_locale_ctype(_S_get_c_locale()),
++ _M_del(__table != 0 && __del), _M_widen_ok(0), _M_narrow_ok(0)
++ {
++ _M_toupper = __C_ctype_toupper;
++ _M_tolower = __C_ctype_tolower;
++ _M_table = __table ? __table : __C_ctype_b;
++ memset(_M_widen, 0, sizeof(_M_widen));
++ memset(_M_narrow, 0, sizeof(_M_narrow));
++ }
++
++ char
++ ctype<char>::do_toupper(char __c) const
++ { return _M_toupper[static_cast<unsigned char>(__c)]; }
++
++ const char*
++ ctype<char>::do_toupper(char* __low, const char* __high) const
++ {
++ while (__low < __high)
++ {
++ *__low = _M_toupper[static_cast<unsigned char>(*__low)];
++ ++__low;
++ }
++ return __high;
++ }
++
++ char
++ ctype<char>::do_tolower(char __c) const
++ { return _M_tolower[static_cast<unsigned char>(__c)]; }
++
++ const char*
++ ctype<char>::do_tolower(char* __low, const char* __high) const
++ {
++ while (__low < __high)
++ {
++ *__low = _M_tolower[static_cast<unsigned char>(*__low)];
++ ++__low;
++ }
++ return __high;
++ }
+diff -urN gcc-3.4.1-dist/libstdc++-v3/config/os/uclibc/os_defines.h gcc-3.4.1/libstdc++-v3/config/os/uclibc/os_defines.h
+--- gcc-3.4.1-dist/libstdc++-v3/config/os/uclibc/os_defines.h 1969-12-31 18:00:00.000000000 -0600
++++ gcc-3.4.1/libstdc++-v3/config/os/uclibc/os_defines.h 2004-07-28 13:00:52.000000000 -0500
+@@ -0,0 +1,44 @@
++// Specific definitions for GNU/Linux -*- C++ -*-
++
++// Copyright (C) 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
++//
++// This file is part of the GNU ISO C++ Library. This library is free
++// software; you can redistribute it and/or modify it under the
++// terms of the GNU General Public License as published by the
++// Free Software Foundation; either version 2, or (at your option)
++// any later version.
++
++// This library is distributed in the hope that it will be useful,
++// but WITHOUT ANY WARRANTY; without even the implied warranty of
++// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++// GNU General Public License for more details.
++
++// You should have received a copy of the GNU General Public License along
++// with this library; see the file COPYING. If not, write to the Free
++// Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307,
++// USA.
++
++// As a special exception, you may use this file as part of a free software
++// library without restriction. Specifically, if other files instantiate
++// templates or use macros or inline functions from this file, or you compile
++// this file and link it with other files to produce an executable, this
++// file does not by itself cause the resulting executable to be covered by
++// the GNU General Public License. This exception does not however
++// invalidate any other reasons why the executable file might be covered by
++// the GNU General Public License.
++
++#ifndef _GLIBCXX_OS_DEFINES
++#define _GLIBCXX_OS_DEFINES 1
++
++// System-specific #define, typedefs, corrections, etc, go here. This
++// file will come before all others.
++
++// This keeps isanum, et al from being propagated as macros.
++#define __NO_CTYPE 1
++
++#include <features.h>
++
++// We must not see the optimized string functions GNU libc defines.
++#define __NO_STRING_INLINES
++
++#endif
+diff -urN gcc-3.4.1-dist/libstdc++-v3/configure gcc-3.4.1/libstdc++-v3/configure
+--- gcc-3.4.1-dist/libstdc++-v3/configure 2004-06-25 17:02:47.000000000 -0500
++++ gcc-3.4.1/libstdc++-v3/configure 2004-07-28 13:00:52.000000000 -0500
+@@ -5545,6 +5545,9 @@
+ # Default to "generic".
+ if test $enable_clocale_flag = auto; then
+ case x${target_os} in
++ xlinux-uclibc*)
++ enable_clocale_flag=uclibc
++ ;;
+ xlinux* | xgnu* | xkfreebsd*-gnu | xknetbsd*-gnu)
+ cat >conftest.$ac_ext <<_ACEOF
+ #line $LINENO "configure"
+@@ -5759,6 +5762,77 @@
+ CTIME_CC=config/locale/generic/time_members.cc
+ CLOCALE_INTERNAL_H=config/locale/generic/c++locale_internal.h
+ ;;
++ uclibc)
++ echo "$as_me:$LINENO: result: uclibc" >&5
++echo "${ECHO_T}uclibc" >&6
++
++ # Declare intention to use gettext, and add support for specific
++ # languages.
++ # For some reason, ALL_LINGUAS has to be before AM-GNU-GETTEXT
++ ALL_LINGUAS="de fr"
++
++ # Don't call AM-GNU-GETTEXT here. Instead, assume glibc.
++ # Extract the first word of "msgfmt", so it can be a program name with args.
++set dummy msgfmt; ac_word=$2
++echo "$as_me:$LINENO: checking for $ac_word" >&5
++echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
++if test "${ac_cv_prog_check_msgfmt+set}" = set; then
++ echo $ECHO_N "(cached) $ECHO_C" >&6
++else
++ if test -n "$check_msgfmt"; then
++ ac_cv_prog_check_msgfmt="$check_msgfmt" # Let the user override the test.
++else
++as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
++for as_dir in $PATH
++do
++ IFS=$as_save_IFS
++ test -z "$as_dir" && as_dir=.
++ for ac_exec_ext in '' $ac_executable_extensions; do
++ if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
++ ac_cv_prog_check_msgfmt="yes"
++ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
++ break 2
++ fi
++done
++done
++
++ test -z "$ac_cv_prog_check_msgfmt" && ac_cv_prog_check_msgfmt="no"
++fi
++fi
++check_msgfmt=$ac_cv_prog_check_msgfmt
++if test -n "$check_msgfmt"; then
++ echo "$as_me:$LINENO: result: $check_msgfmt" >&5
++echo "${ECHO_T}$check_msgfmt" >&6
++else
++ echo "$as_me:$LINENO: result: no" >&5
++echo "${ECHO_T}no" >&6
++fi
++
++ if test x"$check_msgfmt" = x"yes" && test x"$enable_nls" = x"yes"; then
++ USE_NLS=yes
++ fi
++ # Export the build objects.
++ for ling in $ALL_LINGUAS; do \
++ glibcxx_MOFILES="$glibcxx_MOFILES $ling.mo"; \
++ glibcxx_POFILES="$glibcxx_POFILES $ling.po"; \
++ done
++
++
++
++ CLOCALE_H=config/locale/uclibc/c_locale.h
++ CLOCALE_CC=config/locale/uclibc/c_locale.cc
++ CCODECVT_H=config/locale/ieee_1003.1-2001/codecvt_specializations.h
++ CCODECVT_CC=config/locale/uclibc/codecvt_members.cc
++ CCOLLATE_CC=config/locale/uclibc/collate_members.cc
++ CCTYPE_CC=config/locale/uclibc/ctype_members.cc
++ CMESSAGES_H=config/locale/uclibc/messages_members.h
++ CMESSAGES_CC=config/locale/uclibc/messages_members.cc
++ CMONEY_CC=config/locale/uclibc/monetary_members.cc
++ CNUMERIC_CC=config/locale/uclibc/numeric_members.cc
++ CTIME_H=config/locale/uclibc/time_members.h
++ CTIME_CC=config/locale/uclibc/time_members.cc
++ CLOCALE_INTERNAL_H=config/locale/uclibc/c++locale_internal.h
++ ;;
+ esac
+
+ # This is where the testsuite looks for locale catalogs, using the
+diff -urN gcc-3.4.1-dist/libstdc++-v3/configure.host gcc-3.4.1/libstdc++-v3/configure.host
+--- gcc-3.4.1-dist/libstdc++-v3/configure.host 2004-03-18 11:36:12.000000000 -0600
++++ gcc-3.4.1/libstdc++-v3/configure.host 2004-07-28 13:00:52.000000000 -0500
+@@ -217,6 +217,12 @@
+ ;;
+ esac
+
++# Override for uClibc since linux-uclibc gets mishandled above.
++case "${host_os}" in
++ *-uclibc*)
++ os_include_dir="os/uclibc"
++ ;;
++esac
+
+ # Set any OS-dependent and CPU-dependent bits.
+ # THIS TABLE IS SORTED. KEEP IT THAT WAY.
+diff -urN gcc-3.4.1-dist/libstdc++-v3/crossconfig.m4 gcc-3.4.1/libstdc++-v3/crossconfig.m4
+--- gcc-3.4.1-dist/libstdc++-v3/crossconfig.m4 2004-06-10 12:12:14.000000000 -0500
++++ gcc-3.4.1/libstdc++-v3/crossconfig.m4 2004-07-29 16:37:34.000000000 -0500
+@@ -122,6 +122,99 @@
+ ;;
+ esac
+ ;;
++ *-uclibc*)
++# Temporary hack until we implement the float versions of the libm funcs
++ AC_CHECK_HEADERS([nan.h ieeefp.h endian.h sys/isa_defs.h \
++ machine/endian.h machine/param.h sys/machine.h sys/types.h \
++ fp.h float.h endian.h inttypes.h locale.h float.h stdint.h])
++ SECTION_FLAGS='-ffunction-sections -fdata-sections'
++ AC_SUBST(SECTION_FLAGS)
++ GLIBCXX_CHECK_LINKER_FEATURES
++ GLIBCXX_CHECK_COMPLEX_MATH_SUPPORT
++ GLIBCXX_CHECK_WCHAR_T_SUPPORT
++
++ # For LFS.
++ AC_DEFINE(HAVE_INT64_T)
++ case "$target" in
++ *-uclinux*)
++ # Don't enable LFS with uClinux
++ ;;
++ *)
++ AC_DEFINE(_GLIBCXX_USE_LFS)
++ esac
++
++ # For showmanyc_helper().
++ AC_CHECK_HEADERS(sys/ioctl.h sys/filio.h)
++ GLIBCXX_CHECK_POLL
++ GLIBCXX_CHECK_S_ISREG_OR_S_IFREG
++
++ # For xsputn_2().
++ AC_CHECK_HEADERS(sys/uio.h)
++ GLIBCXX_CHECK_WRITEV
++
++# AC_DEFINE(HAVE_ACOSF)
++# AC_DEFINE(HAVE_ASINF)
++# AC_DEFINE(HAVE_ATANF)
++# AC_DEFINE(HAVE_ATAN2F)
++ AC_DEFINE(HAVE_CEILF)
++ AC_DEFINE(HAVE_COPYSIGN)
++# AC_DEFINE(HAVE_COPYSIGNF)
++# AC_DEFINE(HAVE_COSF)
++# AC_DEFINE(HAVE_COSHF)
++# AC_DEFINE(HAVE_EXPF)
++# AC_DEFINE(HAVE_FABSF)
++ AC_DEFINE(HAVE_FINITE)
++ AC_DEFINE(HAVE_FINITEF)
++ AC_DEFINE(HAVE_FLOORF)
++# AC_DEFINE(HAVE_FMODF)
++# AC_DEFINE(HAVE_FREXPF)
++ AC_DEFINE(HAVE_HYPOT)
++# AC_DEFINE(HAVE_HYPOTF)
++ AC_DEFINE(HAVE_ISINF)
++ AC_DEFINE(HAVE_ISINFF)
++ AC_DEFINE(HAVE_ISNAN)
++ AC_DEFINE(HAVE_ISNANF)
++# AC_DEFINE(HAVE_LOGF)
++# AC_DEFINE(HAVE_LOG10F)
++# AC_DEFINE(HAVE_MODFF)
++# AC_DEFINE(HAVE_SINF)
++# AC_DEFINE(HAVE_SINHF)
++# AC_DEFINE(HAVE_SINCOS)
++# AC_DEFINE(HAVE_SINCOSF)
++ AC_DEFINE(HAVE_SQRTF)
++# AC_DEFINE(HAVE_TANF)
++# AC_DEFINE(HAVE_TANHF)
++ if test x"long_double_math_on_this_cpu" = x"yes"; then
++ AC_MSG_ERROR([long_double_math_on_this_cpu is yes!])
++# AC_DEFINE(HAVE_ACOSL)
++# AC_DEFINE(HAVE_ASINL)
++# AC_DEFINE(HAVE_ATANL)
++# AC_DEFINE(HAVE_ATAN2L)
++# AC_DEFINE(HAVE_CEILL)
++# AC_DEFINE(HAVE_COPYSIGNL)
++# AC_DEFINE(HAVE_COSL)
++# AC_DEFINE(HAVE_COSHL)
++# AC_DEFINE(HAVE_EXPL)
++# AC_DEFINE(HAVE_FABSL)
++# AC_DEFINE(HAVE_FINITEL)
++# AC_DEFINE(HAVE_FLOORL)
++# AC_DEFINE(HAVE_FMODL)
++# AC_DEFINE(HAVE_FREXPL)
++# AC_DEFINE(HAVE_HYPOTL)
++# AC_DEFINE(HAVE_ISINFL)
++# AC_DEFINE(HAVE_ISNANL)
++# AC_DEFINE(HAVE_LOGL)
++# AC_DEFINE(HAVE_LOG10L)
++# AC_DEFINE(HAVE_MODFL)
++# AC_DEFINE(HAVE_POWL)
++# AC_DEFINE(HAVE_SINL)
++# AC_DEFINE(HAVE_SINHL)
++# AC_DEFINE(HAVE_SINCOSL)
++# AC_DEFINE(HAVE_SQRTL)
++# AC_DEFINE(HAVE_TANL)
++# AC_DEFINE(HAVE_TANHL)
++ fi
++ ;;
+ *-linux* | *-uclinux* | *-gnu* | *-kfreebsd*-gnu | *-knetbsd*-gnu)
+ AC_CHECK_HEADERS([nan.h ieeefp.h endian.h sys/isa_defs.h \
+ machine/endian.h machine/param.h sys/machine.h sys/types.h \
+@@ -136,7 +229,7 @@
+ AC_DEFINE(HAVE_INT64_T)
+ case "$target" in
+ *-uclinux*)
+- # Don't enable LFS with uClibc
++ # Don't enable LFS with uClinux
+ ;;
+ *)
+ AC_DEFINE(_GLIBCXX_USE_LFS)
+diff -urN gcc-3.4.1-dist/libstdc++-v3/include/c_std/std_cwchar.h gcc-3.4.1/libstdc++-v3/include/c_std/std_cwchar.h
+--- gcc-3.4.1-dist/libstdc++-v3/include/c_std/std_cwchar.h 2003-12-08 21:44:35.000000000 -0600
++++ gcc-3.4.1/libstdc++-v3/include/c_std/std_cwchar.h 2004-07-28 13:47:30.000000000 -0500
+@@ -179,7 +179,9 @@
+ using ::wcscoll;
+ using ::wcscpy;
+ using ::wcscspn;
++#if _GLIBCXX_HAVE_WCSFTIME
+ using ::wcsftime;
++#endif
+ using ::wcslen;
+ using ::wcsncat;
+ using ::wcsncmp;
diff --git a/gcc/gcc-3.4.3/gcc-uclibc-3.4.0-120-softfloat.patch b/gcc/gcc-3.4.3/gcc-uclibc-3.4.0-120-softfloat.patch
index e69de29bb2..f2431896cf 100644
--- a/gcc/gcc-3.4.3/gcc-uclibc-3.4.0-120-softfloat.patch
+++ b/gcc/gcc-3.4.3/gcc-uclibc-3.4.0-120-softfloat.patch
@@ -0,0 +1,14 @@
+--- gcc-3.3.2-old/configure.in 2003-08-09 01:57:21.000000000 -0500
++++ gcc-3.3.2/configure.in 2004-01-15 12:46:29.000000000 -0600
+@@ -1418,6 +1418,11 @@
+ fi
+
+ FLAGS_FOR_TARGET=
++case " $targargs " in
++ *" --nfp "* | *" --without-float "*)
++ FLAGS_FOR_TARGET=$FLAGS_FOR_TARGET' -msoft-float'
++ ;;
++esac
+ case " $target_configdirs " in
+ *" newlib "*)
+ case " $targargs " in
diff --git a/gcc/gcc-3.4.3/gcc34-arm-ldm-peephole.patch b/gcc/gcc-3.4.3/gcc34-arm-ldm-peephole.patch
index e69de29bb2..fb317e1537 100644
--- a/gcc/gcc-3.4.3/gcc34-arm-ldm-peephole.patch
+++ b/gcc/gcc-3.4.3/gcc34-arm-ldm-peephole.patch
@@ -0,0 +1,79 @@
+--- gcc-3.4.0/gcc/config/arm/arm.md.arm-ldm-peephole 2004-01-13 08:24:37.000000000 -0500
++++ gcc-3.4.0/gcc/config/arm/arm.md 2004-04-24 18:18:04.000000000 -0400
+@@ -8810,13 +8810,16 @@
+ (set_attr "length" "4,8,8")]
+ )
+
++; Try to convert LDR+LDR+arith into [add+]LDM+arith
++; On XScale, LDM is always slower than two LDRs, so only do this if
++; optimising for size.
+ (define_insn "*arith_adjacentmem"
+ [(set (match_operand:SI 0 "s_register_operand" "=r")
+ (match_operator:SI 1 "shiftable_operator"
+ [(match_operand:SI 2 "memory_operand" "m")
+ (match_operand:SI 3 "memory_operand" "m")]))
+ (clobber (match_scratch:SI 4 "=r"))]
+- "TARGET_ARM && adjacent_mem_locations (operands[2], operands[3])"
++ "TARGET_ARM && (!arm_tune_xscale || optimize_size) && adjacent_mem_locations (operands[2], operands[3])"
+ "*
+ {
+ rtx ldm[3];
+@@ -8851,6 +8854,8 @@
+ }
+ if (val1 && val2)
+ {
++ /* This would be a loss on a Harvard core, but adjacent_mem_locations()
++ will prevent it from happening. */
+ rtx ops[3];
+ ldm[0] = ops[0] = operands[4];
+ ops[1] = XEXP (XEXP (operands[2], 0), 0);
+--- gcc-3.4.0/gcc/config/arm/arm.c.arm-ldm-peephole 2004-04-24 18:16:25.000000000 -0400
++++ gcc-3.4.0/gcc/config/arm/arm.c 2004-04-24 18:18:04.000000000 -0400
+@@ -4593,8 +4593,11 @@
+ arith_adjacentmem pattern to output an overlong sequence. */
+ if (!const_ok_for_op (PLUS, val0) || !const_ok_for_op (PLUS, val1))
+ return 0;
+-
+- return (reg0 == reg1) && ((val1 - val0) == 4 || (val0 - val1) == 4);
++
++ /* For Harvard cores, only accept pairs where one offset is zero.
++ See comment in load_multiple_sequence. */
++ return (reg0 == reg1) && ((val1 - val0) == 4 || (val0 - val1) == 4)
++ && (!arm_ld_sched || val0 == 0 || val1 == 0);
+ }
+ return 0;
+ }
+@@ -4838,6 +4841,11 @@
+ *load_offset = unsorted_offsets[order[0]];
+ }
+
++ /* For XScale a two-word LDM is a performance loss, so only do this if
++ size is more important. See comments in arm_gen_load_multiple. */
++ if (nops == 2 && arm_tune_xscale && !optimize_size)
++ return 0;
++
+ if (unsorted_offsets[order[0]] == 0)
+ return 1; /* ldmia */
+
+@@ -5064,6 +5072,11 @@
+ *load_offset = unsorted_offsets[order[0]];
+ }
+
++ /* For XScale a two-word LDM is a performance loss, so only do this if
++ size is more important. See comments in arm_gen_load_multiple. */
++ if (nops == 2 && arm_tune_xscale && !optimize_size)
++ return 0;
++
+ if (unsorted_offsets[order[0]] == 0)
+ return 1; /* stmia */
+
+--- gcc-3.4.0/gcc/genpeep.c.arm-ldm-peephole 2003-07-05 01:27:22.000000000 -0400
++++ gcc-3.4.0/gcc/genpeep.c 2004-04-24 18:18:04.000000000 -0400
+@@ -381,6 +381,7 @@
+ printf ("#include \"recog.h\"\n");
+ printf ("#include \"except.h\"\n\n");
+ printf ("#include \"function.h\"\n\n");
++ printf ("#include \"flags.h\"\n\n");
+
+ printf ("#ifdef HAVE_peephole\n");
+ printf ("extern rtx peep_operand[];\n\n");
diff --git a/gcc/gcc-3.4.3/gcc34-arm-ldm.patch b/gcc/gcc-3.4.3/gcc34-arm-ldm.patch
index e69de29bb2..142052fdf0 100644
--- a/gcc/gcc-3.4.3/gcc34-arm-ldm.patch
+++ b/gcc/gcc-3.4.3/gcc34-arm-ldm.patch
@@ -0,0 +1,119 @@
+--- gcc-3.4.0/gcc/config/arm/arm.c.arm-ldm 2004-02-27 09:51:05.000000000 -0500
++++ gcc-3.4.0/gcc/config/arm/arm.c 2004-04-24 18:16:25.000000000 -0400
+@@ -8520,6 +8520,26 @@
+ return_used_this_function = 0;
+ }
+
++/* Return the number (counting from 0) of
++ the least significant set bit in MASK. */
++
++#ifdef __GNUC__
++inline
++#endif
++static int
++number_of_first_bit_set (mask)
++ int mask;
++{
++ int bit;
++
++ for (bit = 0;
++ (mask & (1 << bit)) == 0;
++ ++bit)
++ continue;
++
++ return bit;
++}
++
+ const char *
+ arm_output_epilogue (rtx sibling)
+ {
+@@ -8753,27 +8773,47 @@
+ saved_regs_mask |= (1 << PC_REGNUM);
+ }
+
+- /* Load the registers off the stack. If we only have one register
+- to load use the LDR instruction - it is faster. */
+- if (saved_regs_mask == (1 << LR_REGNUM))
+- {
+- /* The exception handler ignores the LR, so we do
+- not really need to load it off the stack. */
+- if (eh_ofs)
+- asm_fprintf (f, "\tadd\t%r, %r, #4\n", SP_REGNUM, SP_REGNUM);
+- else
+- asm_fprintf (f, "\tldr\t%r, [%r], #4\n", LR_REGNUM, SP_REGNUM);
+- }
+- else if (saved_regs_mask)
++ if (saved_regs_mask)
+ {
+- if (saved_regs_mask & (1 << SP_REGNUM))
+- /* Note - write back to the stack register is not enabled
+- (ie "ldmfd sp!..."). We know that the stack pointer is
+- in the list of registers and if we add writeback the
+- instruction becomes UNPREDICTABLE. */
+- print_multi_reg (f, "ldmfd\t%r", SP_REGNUM, saved_regs_mask);
++ /* Load the registers off the stack. If we only have one register
++ to load use the LDR instruction - it is faster. */
++ if (bit_count (saved_regs_mask) == 1)
++ {
++ int reg = number_of_first_bit_set (saved_regs_mask);
++
++ switch (reg)
++ {
++ case SP_REGNUM:
++ /* Mustn't use base writeback when loading SP. */
++ asm_fprintf (f, "\tldr\t%r, [%r]\n", SP_REGNUM, SP_REGNUM);
++ break;
++
++ case LR_REGNUM:
++ if (eh_ofs)
++ {
++ /* The exception handler ignores the LR, so we do
++ not really need to load it off the stack. */
++ asm_fprintf (f, "\tadd\t%r, %r, #4\n", SP_REGNUM, SP_REGNUM);
++ break;
++ }
++ /* else fall through */
++
++ default:
++ asm_fprintf (f, "\tldr\t%r, [%r], #4\n", reg, SP_REGNUM);
++ break;
++ }
++ }
+ else
+- print_multi_reg (f, "ldmfd\t%r!", SP_REGNUM, saved_regs_mask);
++ {
++ if (saved_regs_mask & (1 << SP_REGNUM))
++ /* Note - write back to the stack register is not enabled
++ (ie "ldmfd sp!..."). We know that the stack pointer is
++ in the list of registers and if we add writeback the
++ instruction becomes UNPREDICTABLE. */
++ print_multi_reg (f, "ldmfd\t%r", SP_REGNUM, saved_regs_mask);
++ else
++ print_multi_reg (f, "ldmfd\t%r!", SP_REGNUM, saved_regs_mask);
++ }
+ }
+
+ if (current_function_pretend_args_size)
+@@ -11401,22 +11441,6 @@
+ }
+ }
+
+-/* Return the number (counting from 0) of
+- the least significant set bit in MASK. */
+-
+-inline static int
+-number_of_first_bit_set (int mask)
+-{
+- int bit;
+-
+- for (bit = 0;
+- (mask & (1 << bit)) == 0;
+- ++bit)
+- continue;
+-
+- return bit;
+-}
+-
+ /* Generate code to return from a thumb function.
+ If 'reg_containing_return_addr' is -1, then the return address is
+ actually on the stack, at the stack pointer. */
diff --git a/gcc/gcc-3.4.3/gcc34-arm-tune.patch b/gcc/gcc-3.4.3/gcc34-arm-tune.patch
index e69de29bb2..cdb20bef9b 100644
--- a/gcc/gcc-3.4.3/gcc34-arm-tune.patch
+++ b/gcc/gcc-3.4.3/gcc34-arm-tune.patch
@@ -0,0 +1,9 @@
+--- gcc-3.4.0/gcc/config/arm/linux-elf.h.arm-tune 2004-01-31 01:18:11.000000000 -0500
++++ gcc-3.4.0/gcc/config/arm/linux-elf.h 2004-04-24 18:19:10.000000000 -0400
+@@ -126,3 +126,6 @@
+
+ #define LINK_GCC_C_SEQUENCE_SPEC \
+ "%{static:--start-group} %G %L %{static:--end-group}%{!static:%G}"
++
++/* Tune for XScale. */
++#define TARGET_TUNE_DEFAULT TARGET_CPU_xscale
diff --git a/gcc/gcc-3.4.3/gcc34-reverse-compare.patch b/gcc/gcc-3.4.3/gcc34-reverse-compare.patch
index e69de29bb2..c3c40dd183 100644
--- a/gcc/gcc-3.4.3/gcc34-reverse-compare.patch
+++ b/gcc/gcc-3.4.3/gcc34-reverse-compare.patch
@@ -0,0 +1,32 @@
+--- gcc-3.4.0/gcc/flow.c.reverse-compare 2004-02-27 22:39:19.000000000 -0500
++++ gcc-3.4.0/gcc/flow.c 2004-04-24 16:36:00.000000000 -0400
+@@ -1843,6 +1843,7 @@
+ regset_head diff_head;
+ regset diff = INITIALIZE_REG_SET (diff_head);
+ basic_block bb_true, bb_false;
++ enum rtx_code reversed_code;
+ int i;
+
+ /* Identify the successor blocks. */
+@@ -1889,8 +1890,11 @@
+ if (GET_CODE (reg) == REG
+ && XEXP (cond_true, 1) == const0_rtx)
+ {
+- rtx cond_false
+- = gen_rtx_fmt_ee (reverse_condition (GET_CODE (cond_true)),
++ rtx cond_false;
++ reversed_code = reverse_condition (GET_CODE (cond_true));
++ if (reversed_code == UNKNOWN)
++ goto skip;
++ cond_false = gen_rtx_fmt_ee (reversed_code,
+ GET_MODE (cond_true), XEXP (cond_true, 0),
+ XEXP (cond_true, 1));
+ if (GET_CODE (XEXP (set_src, 1)) == PC)
+@@ -1925,6 +1929,7 @@
+ }
+ }
+
++ skip:
+ FREE_REG_SET (diff);
+ }
+ #endif
diff --git a/gcc/gcc-3.4.3/sdk-libstdc++-includes.patch b/gcc/gcc-3.4.3/sdk-libstdc++-includes.patch
index e69de29bb2..4377c2143b 100644
--- a/gcc/gcc-3.4.3/sdk-libstdc++-includes.patch
+++ b/gcc/gcc-3.4.3/sdk-libstdc++-includes.patch
@@ -0,0 +1,22 @@
+--- gcc-3.4.1/libstdc++-v3/libmath/Makefile.am~ 2003-08-27 22:29:42.000000000 +0100
++++ gcc-3.4.1/libstdc++-v3/libmath/Makefile.am 2004-07-22 16:41:45.152130128 +0100
+@@ -32,7 +32,7 @@
+
+ libmath_la_SOURCES = stubs.c
+
+-AM_CPPFLAGS = $(CANADIAN_INCLUDES)
++AM_CPPFLAGS = $(CANADIAN_INCLUDES) -I$(toplevel_srcdir)/include
+
+ # Only compiling "C" sources in this directory.
+ LIBTOOL = @LIBTOOL@ --tag CC
+--- gcc-3.4.1/libstdc++-v3/fragment.am.old 2004-07-22 18:24:58.024083656 +0100
++++ gcc-3.4.1/libstdc++-v3/fragment.am 2004-07-22 18:24:59.019932264 +0100
+@@ -18,7 +18,7 @@
+ $(WARN_FLAGS) $(WERROR) -fdiagnostics-show-location=once
+
+ # -I/-D flags to pass when compiling.
+-AM_CPPFLAGS = $(GLIBCXX_INCLUDES)
++AM_CPPFLAGS = $(GLIBCXX_INCLUDES) -I$(toplevel_srcdir)/include
+
+
+
diff --git a/gettext/gettext-0.14.1/fixchicken.patch b/gettext/gettext-0.14.1/fixchicken.patch
index e69de29bb2..4174f6d747 100644
--- a/gettext/gettext-0.14.1/fixchicken.patch
+++ b/gettext/gettext-0.14.1/fixchicken.patch
@@ -0,0 +1,26 @@
+Index: gettext-tools/m4/csharpcomp.m4
+===================================================================
+RCS file: /cvs/gettext/gettext/gettext-tools/m4/csharpcomp.m4,v
+retrieving revision 1.4
+retrieving revision 1.6
+diff -u -r1.4 -r1.6
+--- a/gettext-tools/m4/csharpcomp.m4 30 Jan 2004 11:01:18 -0000 1.4
++++ a/gettext-tools/m4/csharpcomp.m4 26 May 2004 12:15:23 -0000 1.6
+@@ -1,4 +1,4 @@
+-# csharpcomp.m4 serial 2 (gettext-0.14.1)
++# csharpcomp.m4 serial 4 (gettext-0.15)
+ dnl Copyright (C) 2003-2004 Free Software Foundation, Inc.
+ dnl This file is free software, distributed under the terms of the GNU
+ dnl General Public License. As a special exception to the GNU General
+@@ -43,7 +43,8 @@
+ ;;
+ sscli)
+ if test -n "$HAVE_CSC_IN_PATH" \
+- && csc -help >/dev/null 2>/dev/null; then
++ && csc -help >/dev/null 2>/dev/null \
++ && { if csc -help 2>/dev/null | grep -i chicken > /dev/null; then false; else true; fi; }; then
+ HAVE_CSC=1
+ ac_result="csc"
+ break
+
+
diff --git a/gpe-beam/gpe-beam-0.2.6/vcard-send.patch b/gpe-beam/gpe-beam-0.2.6/vcard-send.patch
index e69de29bb2..1a2c66f821 100644
--- a/gpe-beam/gpe-beam-0.2.6/vcard-send.patch
+++ b/gpe-beam/gpe-beam-0.2.6/vcard-send.patch
@@ -0,0 +1,33 @@
+Index: main.c
+===================================================================
+RCS file: /cvs/gpe/base/gpe-beam/main.c,v
+retrieving revision 1.26
+diff -u -r1.26 main.c
+--- main.c 16 Oct 2004 12:17:51 -0000 1.26
++++ main.c 17 Oct 2004 12:43:16 -0000
+@@ -632,7 +632,7 @@
+
+
+ static void
+-send_vcard (void)
++do_send_vcard (void)
+ {
+ if (!radio_is_on)
+ radio_on ();
+@@ -651,6 +651,16 @@
+ }
+ }
+
++static void
++send_vcard (void)
++{
++ scan_thread =
++ g_thread_create ((GThreadFunc)
++ do_send_vcard, NULL, FALSE, NULL);
++ if (scan_thread == NULL)
++ gpe_perror_box (_("Unable to start sender."));
++}
++
+
+ static gboolean
+ get_irstatus (void)
diff --git a/gpe-bluetooth/gpe-bluetooth-0.37/hciattach-path.patch b/gpe-bluetooth/gpe-bluetooth-0.37/hciattach-path.patch
index e69de29bb2..4b122668a6 100644
--- a/gpe-bluetooth/gpe-bluetooth-0.37/hciattach-path.patch
+++ b/gpe-bluetooth/gpe-bluetooth-0.37/hciattach-path.patch
@@ -0,0 +1,11 @@
+--- gpe-bluetooth-0.37/hciattach.sh.old 2004-11-23 14:11:16.000000000 +0000
++++ gpe-bluetooth-0.37/hciattach.sh 2004-11-23 14:11:23.000000000 +0000
+@@ -3,7 +3,7 @@
+ if [ -f /etc/sysconfig/bluetooth ]; then
+ . /etc/sysconfig/bluetooth
+
+- exec hciattach -n $BLUETOOTH_PORT $BLUETOOTH_PROTOCOL $BLUETOOTH_SPEED
++ exec /sbin/hciattach -n $BLUETOOTH_PORT $BLUETOOTH_PROTOCOL $BLUETOOTH_SPEED
+ else
+ echo "Bluetooth not configured"
+ exit 1
diff --git a/gpe-conf/gpe-conf-0.1.22/gpe-conf-0.1.22-20041124.patch b/gpe-conf/gpe-conf-0.1.22/gpe-conf-0.1.22-20041124.patch
index e69de29bb2..daa0be2888 100644
--- a/gpe-conf/gpe-conf-0.1.22/gpe-conf-0.1.22-20041124.patch
+++ b/gpe-conf/gpe-conf-0.1.22/gpe-conf-0.1.22-20041124.patch
@@ -0,0 +1,227 @@
+? rgpe-conf-0.1.22-20041124.patch
+Index: ChangeLog
+===================================================================
+RCS file: /cvs/gpe/base/gpe-conf/ChangeLog,v
+retrieving revision 1.268
+retrieving revision 1.270
+diff -u -r1.268 -r1.270
+--- ChangeLog 23 Nov 2004 21:34:38 -0000 1.268
++++ ChangeLog 24 Nov 2004 15:44:10 -0000 1.270
+@@ -1,5 +1,15 @@
+ 2004-11-23 Florian Boor <florian.boor@kernelconcepts.de>
+
++ * cfgfile.*, network.c: Hide all network interfaces that are not available.
++
++2004-11-19 Florian Boor <florian.boor@kernelconcepts.de>
++
++ * users/interface.*, users/callbacks.c: Prevent user from deleting last
++ user account.
++ * timeanddate.c: Make some entries activate OK button.
++
++2004-11-23 Florian Boor <florian.boor@kernelconcepts.de>
++
+ * Released Version 0.1.22
+
+ 2004-11-17 Florian Boor <florian.boor@kernelconcepts.de>
+Index: cfgfile.c
+===================================================================
+RCS file: /cvs/gpe/base/gpe-conf/cfgfile.c,v
+retrieving revision 1.14
+retrieving revision 1.15
+diff -u -r1.14 -r1.15
+--- cfgfile.c 16 Oct 2004 16:03:42 -0000 1.14
++++ cfgfile.c 24 Nov 2004 15:43:10 -0000 1.15
+@@ -19,9 +19,12 @@
+ #include <stdlib.h>
+ #include <libintl.h>
+ #include <gpe/errorbox.h>
++#include <net/if.h>
++#include <sys/socket.h>
+
+ #include "cfgfile.h"
+ #include "network.h"
++#include "tools/interface.h"
+
+ #define _(x) gettext(x)
+
+@@ -280,6 +283,22 @@
+ }
+ }
+
++static gboolean
++is_present_interface(gchar *ifname)
++{
++ struct interface *int_list, *ife;
++
++ int_list = if_getlist ();
++ g_strstrip(ifname);
++
++ for (ife = int_list; ife; ife = ife->next)
++ {
++ if (g_str_has_prefix(ifname, ife->name))
++ return TRUE;
++ }
++ return FALSE;
++}
++
+ gint get_scheme_list()
+ {
+ gchar ifname[255] = {0};
+@@ -339,6 +358,7 @@
+ memset(&iflist[l-1],'\0',sizeof(NWInterface_t));
+
+ strcpy(iflist[l-1].name,ifname);
++ iflist[l-1].ispresent = is_present_interface(ifname);
+
+ iflist[l-1].isstatic = FALSE;
+ iflist[l-1].isinet = FALSE;
+Index: cfgfile.h
+===================================================================
+RCS file: /cvs/gpe/base/gpe-conf/cfgfile.h,v
+retrieving revision 1.9
+retrieving revision 1.10
+diff -u -r1.9 -r1.10
+--- cfgfile.h 25 Aug 2004 10:17:31 -0000 1.9
++++ cfgfile.h 24 Nov 2004 15:43:10 -0000 1.10
+@@ -66,6 +66,7 @@
+ gint firstline;
+ gint lastline;
+ gint status;
++ gboolean ispresent;
+ } NWInterface_t;
+
+ gint set_file_open(gint openon);
+Index: network.c
+===================================================================
+RCS file: /cvs/gpe/base/gpe-conf/network.c,v
+retrieving revision 1.42
+retrieving revision 1.43
+diff -u -r1.42 -r1.43
+--- network.c 17 Nov 2004 18:25:59 -0000 1.42
++++ network.c 24 Nov 2004 15:43:10 -0000 1.43
+@@ -1648,12 +1648,15 @@
+ for (row = 0; row < num_int; row++)
+ {
+ ctable = NULL;
+- if (iflist[row].isstatic)
+- ctable = create_nwstatic_widgets (iflist[row]);
+- if (iflist[row].isdhcp)
+- ctable = create_nwdhcp_widgets (iflist[row]);
+- if (iflist[row].isppp)
+- ctable = create_nwppp_widgets (iflist[row]);
++ if (iflist[row].ispresent)
++ {
++ if (iflist[row].isstatic)
++ ctable = create_nwstatic_widgets (iflist[row]);
++ if (iflist[row].isdhcp)
++ ctable = create_nwdhcp_widgets (iflist[row]);
++ if (iflist[row].isppp)
++ ctable = create_nwppp_widgets (iflist[row]);
++ }
+ if (ctable)
+ {
+ if (!have_access)
+Index: timeanddate.c
+===================================================================
+RCS file: /cvs/gpe/base/gpe-conf/timeanddate.c,v
+retrieving revision 1.39
+retrieving revision 1.40
+diff -u -r1.39 -r1.40
+--- timeanddate.c 6 Nov 2004 08:22:52 -0000 1.39
++++ timeanddate.c 24 Nov 2004 15:43:10 -0000 1.40
+@@ -528,6 +528,7 @@
+ gtk_misc_set_alignment (GTK_MISC (self.catlabel1), 0.0, 0.9);
+
+ self.cal = gtk_date_combo_new ();
++ gtk_entry_set_activates_default(GTK_ENTRY(GTK_DATE_COMBO(self.cal)->entry), TRUE);
+ gtk_calendar_select_month (GTK_CALENDAR (GTK_DATE_COMBO(self.cal)->cal), ts.tm_mon, ts.tm_year);
+ gtk_calendar_select_day (GTK_CALENDAR (GTK_DATE_COMBO(self.cal)->cal), ts.tm_mday);
+ gtk_table_attach (GTK_TABLE (table), self.cal, 0, 3, 1, 2,
+@@ -549,6 +550,8 @@
+ gpe_time_sel_set_time(GPE_TIME_SEL(self.tsel),(guint)ts.tm_hour, (guint)ts.tm_min);
+ gtk_table_attach (GTK_TABLE (table), self.tsel, 0, 3, 3, 4,
+ GTK_FILL,0,3,0);
++ gtk_entry_set_activates_default(GTK_ENTRY(GPE_TIME_SEL(self.tsel)->hour_spin), TRUE);
++ gtk_entry_set_activates_default(GTK_ENTRY(GPE_TIME_SEL(self.tsel)->minute_spin), TRUE);
+ /* -------------------------------------------------------------------------- */
+
+ self.catlabel3 = gtk_label_new (NULL);
+@@ -569,7 +572,7 @@
+ gtk_combo_set_popdown_strings (GTK_COMBO (self.ntpserver), ntpsrv);
+ gtk_table_attach (GTK_TABLE (table), self.ntpserver, 0, 3, 5, 6,
+ GTK_FILL,0,3,0);
+-
++
+ gtk_tooltips_set_tip (tooltips, self.ntpserver, _("Select the timeserver to use to set the clock."), NULL);
+
+ self.internet = gtk_button_new_with_label(_("Get time from network"));
+Index: users/callbacks.c
+===================================================================
+RCS file: /cvs/gpe/base/gpe-conf/users/callbacks.c,v
+retrieving revision 1.17
+retrieving revision 1.18
+diff -u -r1.17 -r1.18
+--- users/callbacks.c 17 Nov 2004 18:26:00 -0000 1.17
++++ users/callbacks.c 24 Nov 2004 15:43:10 -0000 1.18
+@@ -93,6 +93,22 @@
+ pwlist *cur = pwroot;
+ pwlist **prec = &pwroot;
+ uint i=GPOINTER_TO_UINT(tmp->data);
++ uint usercount = 0;
++
++ while (cur)
++ {
++ if ((cur->pw.pw_uid >= MINUSERUID) && (cur->pw.pw_uid < MAXUSERID))
++ usercount++;
++ cur = cur->next;
++ }
++
++ if (usercount < 2)
++ {
++ gpe_error_box(_("You need at least one user account!"));
++ return;
++ }
++ cur = pwroot;
++
+ while(IsHidden(cur))
+ {
+ prec = &cur->next;
+@@ -109,6 +125,13 @@
+ }
+ i--;
+ }
++
++ if (!strcmp(cur->pw.pw_name, "lx"))
++ {
++ gpe_error_box(_("You can't remove this user!"));
++ return;
++ }
++
+ if(cur->pw.pw_uid < MINUSERUID)
+ gpe_error_box(_("You can't remove\n system users!"));
+ else
+@@ -122,7 +145,6 @@
+ }
+ }
+ ReloadList();
+-
+ }
+
+
+Index: users/interface.h
+===================================================================
+RCS file: /cvs/gpe/base/gpe-conf/users/interface.h,v
+retrieving revision 1.8
+retrieving revision 1.9
+diff -u -r1.8 -r1.9
+--- users/interface.h 14 Jun 2004 10:15:16 -0000 1.8
++++ users/interface.h 24 Nov 2004 15:43:10 -0000 1.9
+@@ -6,6 +6,8 @@
+ #else
+ #define MINUSERUID 100
+ #endif
++#define MAXUSERID 65500
++
+ typedef struct pwlist_s
+ {
+ struct passwd pw;
diff --git a/gpe-gallery/files/mcheck.patch b/gpe-gallery/files/mcheck.patch
index e69de29bb2..a2f2376eba 100644
--- a/gpe-gallery/files/mcheck.patch
+++ b/gpe-gallery/files/mcheck.patch
@@ -0,0 +1,24 @@
+Index: gpe-gallery-0.95/main.c
+===================================================================
+--- gpe-gallery-0.95.orig/main.c 2004-09-07 09:01:39.000000000 -0500
++++ gpe-gallery-0.95/main.c 2004-11-18 15:55:18.856050416 -0600
+@@ -19,7 +19,9 @@
+ #include <fcntl.h>
+ #include <math.h>
+ #include <time.h>
++#ifdef USE_MCHECK
+ #include <mcheck.h>
++#endif
+
+ #include <gtk/gtk.h>
+ #include <gdk/gdk.h>
+@@ -1115,7 +1117,9 @@
+ GdkBitmap *bmap;
+ struct stat arg_stat;
+
++#ifdef USE_MCHECK
+ mtrace ();
++#endif
+
+ if (gpe_application_init (&argc, &argv) == FALSE)
+ exit (1);
diff --git a/hostap/files/hostap_cs.modconf b/hostap/files/hostap_cs.modconf
index e69de29bb2..275b22246d 100644
--- a/hostap/files/hostap_cs.modconf
+++ b/hostap/files/hostap_cs.modconf
@@ -0,0 +1,2 @@
+options hostap_cs ignore_cis_vcc=1
+
diff --git a/hotplug/files/collie/usbd b/hotplug/files/collie/usbd
index e69de29bb2..38aff0adcb 100644
--- a/hotplug/files/collie/usbd
+++ b/hotplug/files/collie/usbd
@@ -0,0 +1 @@
+usbdmodule="sa1100_bi"
diff --git a/hotplug/files/sysconfig-hotplug b/hotplug/files/sysconfig-hotplug
index e69de29bb2..7ee5a20c31 100644
--- a/hotplug/files/sysconfig-hotplug
+++ b/hotplug/files/sysconfig-hotplug
@@ -0,0 +1,12 @@
+##
+## Each of these may be assigned to the number of nanoseconds by which to
+## increase the timing of pcmcia bus transactions
+##
+# COMPACTFLASH_TIMING_INCREMENT=0
+## the single pcmcia sleeve seems to be a bit slow, but 25ns makes my
+## orinoco card detectable. -Jamey 4/2/2002
+PCMCIA_TIMING_INCREMENT=25
+# DUAL_PCMCIA_TIMING_INCREMENT=0
+
+## mount options used by MMC
+MMC_MOUNT_OPTIONS="-t auto -o sync,noatime"
diff --git a/hotplug/files/tosa/sysconfig-usb b/hotplug/files/tosa/sysconfig-usb
index e69de29bb2..cbd18c6566 100644
--- a/hotplug/files/tosa/sysconfig-usb
+++ b/hotplug/files/tosa/sysconfig-usb
@@ -0,0 +1,2 @@
+## Specify the USB host controller driver
+HCD="usb_ohci_tc6393"
diff --git a/hotplug/files/usbd b/hotplug/files/usbd
index e69de29bb2..aa4e6d2de7 100644
--- a/hotplug/files/usbd
+++ b/hotplug/files/usbd
@@ -0,0 +1,2 @@
+usbdmodule=""
+
diff --git a/hotplug/hotplug-20040920/userspecified_hcd.patch b/hotplug/hotplug-20040920/userspecified_hcd.patch
index e69de29bb2..c2ceeb686e 100644
--- a/hotplug/hotplug-20040920/userspecified_hcd.patch
+++ b/hotplug/hotplug-20040920/userspecified_hcd.patch
@@ -0,0 +1,55 @@
+
+#
+# Patch managed by http://www.holgerschurig.de/patcher.html
+#
+
+--- hotplug-2004_09_20/etc/hotplug/usb.rc~userspecified_hcd.patch
++++ hotplug-2004_09_20/etc/hotplug/usb.rc
+@@ -24,6 +24,7 @@
+ unset I_WANT_A_BROKEN_PS
+ PS_PERSONALITY=linux
+
++HCD=
+ STATIC_MODULE_LIST=
+ X11_USBMICE_HACK=false
+
+@@ -166,20 +167,25 @@
+ # FIXME: some of this should be driven by PCI hotplugging, and have
+ # the blacklist control which uhci driver gets used (before 2.5).
+
+- # "new style" HCDs ... more common code
+- modprobe -q ehci-hcd >/dev/null 2>&1
+- modprobe -q ohci-hcd >/dev/null 2>&1
+- modprobe -q uhci-hcd >/dev/null 2>&1
+-
+- # "old style" HCDs ... more driver-specific bugs
+- modprobe -q usb-ohci >/dev/null 2>&1
+- # NOTE: this prefers "uhci"; you may prefer "usb-uhci".
+- # modprobe -q usb-uhci >/dev/null 2>&1 || modprobe -q uhci >/dev/null 2>&1
+- modprobe -q uhci >/dev/null 2>&1 || modprobe -q usb-uhci >/dev/null 2>&1
+-
+- # ... add any non-PCI HCDS here. Examples include the
+- # CRIS usb-host, Philips ISP-1161, Symlogic 811HS, and so on.
+- # ohci-hcd can handle some non-pci variants.
++ # If the user specified which HCD they want, trust them.
++ if [ -n "$HCD" ]; then
++ modprobe -q $HCD >/dev/null 2>&1
++ else
++ # "new style" HCDs ... more common code
++ modprobe -q ehci-hcd >/dev/null 2>&1
++ modprobe -q ohci-hcd >/dev/null 2>&1
++ modprobe -q uhci-hcd >/dev/null 2>&1
++
++ # "old style" HCDs ... more driver-specific bugs
++ modprobe -q usb-ohci >/dev/null 2>&1
++ # NOTE: this prefers "uhci"; you may prefer "usb-uhci".
++ # modprobe -q usb-uhci >/dev/null 2>&1 || modprobe -q uhci >/dev/null 2>&1
++ modprobe -q uhci >/dev/null 2>&1 || modprobe -q usb-uhci >/dev/null 2>&1
++
++ # ... add any non-PCI HCDS here. Examples include the
++ # CRIS usb-host, Philips ISP-1161, Symlogic 811HS, and so on.
++ # ohci-hcd can handle some non-pci variants.
++ fi
+
+ if [ -d /proc/bus/usb ]; then
+ # If we see there are no busses, we "failed" and
diff --git a/insttrain/files/gcc3.patch b/insttrain/files/gcc3.patch
index e69de29bb2..878146ee3a 100644
--- a/insttrain/files/gcc3.patch
+++ b/insttrain/files/gcc3.patch
@@ -0,0 +1,33 @@
+
+#
+# Patch managed by http://www.holgerschurig.de/patcher.html
+#
+
+--- src/top.cpp~gcc3
++++ src/top.cpp
+@@ -18,6 +18,7 @@
+ //static char rcsid[] = "$Id$";
+
+ #include "top.h"
++#include <qapplication.h>
+ #include <qmenubar.h>
+ #include <qpopupmenu.h>
+ #include <qframe.h>
+@@ -90,7 +91,7 @@
+ }
+
+
+-Top::Top(QWidget *parent = 0, const char *name = 0, WFlags f = WType_TopLevel)
++Top::Top(QWidget *parent, const char *name, WFlags f)
+ : QMainWindow(parent, name, f)
+ {
+ setCaption("Instrument Trainer");
+@@ -172,7 +173,7 @@
+ void
+ Top::Exit()
+ {
+- exit(0);
++ qApp->quit();
+ }
+
+ void
diff --git a/irk/files/install-default-conf.patch b/irk/files/install-default-conf.patch
index e69de29bb2..a55992a737 100644
--- a/irk/files/install-default-conf.patch
+++ b/irk/files/install-default-conf.patch
@@ -0,0 +1,18 @@
+
+#
+# Patch managed by http://www.holgerschurig.de/patcher.html
+#
+
+--- irk/irk.cpp~install-default-config
++++ irk/irk.cpp
+@@ -470,6 +470,10 @@
+
+ void CGIRK::readConfig()
+ {
++ // check if the config is there, if not copy it from default location
++ // FIXME: Do it properly once confirmed that it works :D
++ ::system( "if [ ! -e \"$HOME/Settings/IRK.conf\" ]; then cat $OPIEDIR/etc/skel/IRK.conf >$HOME/Settings/IRK.conf; fi" );
++
+ // clear the keyboard list ready for the new one to be loaded in
+ m_keyboardList.clear();
+ #if !POCKETOP_BUILD
diff --git a/ixp425-eth/files/ethhdr.patch b/ixp425-eth/files/ethhdr.patch
index e69de29bb2..79c9fef34f 100644
--- a/ixp425-eth/files/ethhdr.patch
+++ b/ixp425-eth/files/ethhdr.patch
@@ -0,0 +1,25 @@
+
+#
+# Patch managed by http://www.holgerschurig.de/patcher.html
+#
+
+--- ixp425-eth-1.1-r0/ixp425_eth.c~ethhdr
++++ ixp425-eth-1.1-r0/ixp425_eth.c
+@@ -1307,7 +1307,7 @@
+ * and its constants are taken from the eth_type_trans()
+ * function.
+ */
+- struct ethhdr *eth = skb->mac.ethernet;
++ struct ethhdr *eth = eth_hdr(skb);
+ unsigned short hproto = ntohs(eth->h_proto);
+
+ if (hproto >= 1536)
+@@ -1349,7 +1349,7 @@
+ * mode is set This costs
+ * a lookup inside the packet payload.
+ */
+- struct ethhdr *eth = skb->mac.ethernet;
++ struct ethhdr *eth = eth_hdr(skb);
+ unsigned char *hdest = eth->h_dest;
+
+ if (memcmp(hdest, dev->dev_addr, ETH_ALEN)!=0)
diff --git a/ixp425-eth/files/makefile.patch b/ixp425-eth/files/makefile.patch
index e69de29bb2..2e47972995 100644
--- a/ixp425-eth/files/makefile.patch
+++ b/ixp425-eth/files/makefile.patch
@@ -0,0 +1,18 @@
+
+#
+# Patch managed by http://www.holgerschurig.de/patcher.html
+#
+
+--- /dev/null
++++ ixp425-eth-1.1-r0/Makefile
+@@ -0,0 +1,10 @@
++ifneq ($(KERNELRELEASE),)
++obj-m := ixp425_eth.o
++
++else
++KDIR := /lib/modules/$(shell uname -r)/build
++PWD := $(shell pwd)
++
++default modules:
++ $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules
++endif
diff --git a/ixp4xx/ixp4xx-csr/badpaths.patch b/ixp4xx/ixp4xx-csr/badpaths.patch
index e69de29bb2..c5981aa3b7 100644
--- a/ixp4xx/ixp4xx-csr/badpaths.patch
+++ b/ixp4xx/ixp4xx-csr/badpaths.patch
@@ -0,0 +1,18 @@
+
+#
+# Patch managed by http://www.mn-logistik.de/unsupported/pxa250/patcher
+#
+
+--- ixp400_xscale_sw/src/linux/vx.c~ugh 2003-12-12 14:58:34.000000000 -0500
++++ ixp400_xscale_sw/src/linux/vx.c 2004-11-14 17:50:07.610969440 -0500
+@@ -31,8 +31,8 @@
+ *
+ * -- End Intel Copyright Notice --
+ */
+-#include <asm-arm/system.h>
+-#include <asm-arm/semaphore.h>
++#include <asm/system.h>
++#include <asm/semaphore.h>
+ #include <linux/kernel.h>
+ #include <linux/time.h>
+ #include <linux/sched.h>
diff --git a/ixp4xx/ixp4xx-csr/build-timing-annoyance.patch b/ixp4xx/ixp4xx-csr/build-timing-annoyance.patch
index e69de29bb2..0c040ec2df 100644
--- a/ixp4xx/ixp4xx-csr/build-timing-annoyance.patch
+++ b/ixp4xx/ixp4xx-csr/build-timing-annoyance.patch
@@ -0,0 +1,10 @@
+--- ixp400_xscale_sw/Makefile~ 2004-11-18 21:26:43.000000000 +1030
++++ ixp400_xscale_sw/Makefile 2004-11-18 21:31:20.000000000 +1030
+@@ -1015,6 +1015,7 @@
+ cp Makefile.kmod26 $(OBJ_DIR)/Makefile
+ make -C $(OBJ_DIR)
+ $(LD) $(LDFLAGS) $^ -o $@
++ sleep 2
+ make -C $(OBJ_DIR)
+
+
diff --git a/lftp/lftp-3.0.11/pty.patch b/lftp/lftp-3.0.11/pty.patch
index e69de29bb2..166b665f3f 100644
--- a/lftp/lftp-3.0.11/pty.patch
+++ b/lftp/lftp-3.0.11/pty.patch
@@ -0,0 +1,31 @@
+
+#
+# Patch managed by http://www.mn-logistik.de/unsupported/pxa250/patcher
+#
+
+--- lftp-2.6.11/m4/pty.m4~pty
++++ lftp-2.6.11/m4/pty.m4
+@@ -4,14 +4,20 @@
+ *-*-sco3.2v4*) no_dev_ptmx=1;;
+ *-*-sco3.2v5*) no_dev_ptmx=1;;
+ esac
++AC_SEARCH_LIBS(openpty,util,
++ [
++ AC_DEFINE(HAVE_OPENPTY,1,[define if you have openpty function])
++ no_dev_ptc=yes
++ no_dev_ptmx=yes
++ ])
++AC_CHECK_FUNCS([_getpty])
+ if test -z "$no_dev_ptmx" ; then
+ AC_CHECK_FILE("/dev/ptmx", [
+ AC_DEFINE(HAVE_DEV_PTMX,1,[define if you have /dev/ptmx device])])
+ fi
++if test -z "$no_dev_ptc" ; then
+ AC_CHECK_FILE("/dev/ptc", [
+ AC_DEFINE(HAVE_DEV_PTS_AND_PTC,1,[define if you have /dev/ptc device])])
++fi
+ AC_CHECK_HEADERS([util.h sys/stropts.h pty.h])
+-AC_SEARCH_LIBS(openpty,util,
+- [AC_DEFINE(HAVE_OPENPTY,1,[define if you have openpty function])])
+-AC_CHECK_FUNCS([_getpty])
+ ])
diff --git a/lftp/lftp-3.0.11/readline.patch b/lftp/lftp-3.0.11/readline.patch
index e69de29bb2..eb90e21539 100644
--- a/lftp/lftp-3.0.11/readline.patch
+++ b/lftp/lftp-3.0.11/readline.patch
@@ -0,0 +1,24 @@
+
+#
+# Patch managed by http://www.mn-logistik.de/unsupported/pxa250/patcher
+#
+
+--- lftp-3.0.1/configure.ac~readline.patch 2004-04-06 10:27:36.000000000 +0200
++++ lftp-3.0.1/configure.ac 2004-04-06 23:39:01.000000000 +0200
+@@ -380,5 +378,5 @@
+ sysconfdir=/etc
+ fi
+
+-AC_CONFIG_FILES([Makefile src/Makefile lib/Makefile include/Makefile doc/Makefile readline-4.3/Makefile po/Makefile.in m4/Makefile contrib/Makefile])
++AC_CONFIG_FILES([Makefile src/Makefile lib/Makefile include/Makefile doc/Makefile po/Makefile.in m4/Makefile contrib/Makefile])
+ AC_OUTPUT
+--- lftp-3.0.1/Makefile.am~readline.patch 2003-12-19 15:16:31.000000000 +0100
++++ lftp-3.0.1/Makefile.am 2004-04-06 23:40:17.000000000 +0200
+@@ -1,6 +1,6 @@
+ EXTRA_DIST = config.rpath lftp.conf lftp.lsm FAQ acconfig.h README README.debug-levels\
+ README.modules MIRRORS BUGS FEATURES
+-SUBDIRS = include readline-4.3 lib doc src po m4 contrib
++SUBDIRS = include lib doc src po m4 contrib
+ AUTOMAKE_OPTIONS = foreign
+ ACLOCAL_AMFLAGS = -I m4
+
diff --git a/libmikmod/libmikmod/autofoo.patch b/libmikmod/libmikmod/autofoo.patch
index e69de29bb2..22d8170aa8 100644
--- a/libmikmod/libmikmod/autofoo.patch
+++ b/libmikmod/libmikmod/autofoo.patch
@@ -0,0 +1,16 @@
+
+#
+# Patch managed by http://www.holgerschurig.de/patcher.html
+#
+
+--- libmikmod-3.2.0-beta2/configure.in~autofoo
++++ libmikmod-3.2.0-beta2/configure.in
+@@ -415,8 +415,6 @@
+
+ AC_CHECK_FUNCS(setenv snprintf srandom)
+ AC_REPLACE_FUNCS(strcasecmp strdup strstr)
+-# Change extension, as we use libtool
+-LIBOBJS="`echo $LIBOBJS|sed -e 's/\.o/\.lo/g'`"
+
+ AC_HEADER_EGREP(srandom,math.h,AC_DEFINE(SRANDOM_IN_MATH_H))
+
diff --git a/libtool/libtool-1.5.10/autotools.patch b/libtool/libtool-1.5.10/autotools.patch
index e69de29bb2..5df441e945 100644
--- a/libtool/libtool-1.5.10/autotools.patch
+++ b/libtool/libtool-1.5.10/autotools.patch
@@ -0,0 +1,127 @@
+diff -urNd -urNd libtool-1.5/cdemo/configure.ac libtool-1.5.ac/cdemo/configure.ac
+--- libtool-1.5/cdemo/configure.ac 2001-10-06 11:35:17.000000000 -0400
++++ libtool-1.5.ac/cdemo/configure.ac 2004-05-05 17:16:17.000000000 -0400
+@@ -24,6 +24,7 @@
+ ## ------------------------ ##
+ AC_INIT([cdemo], [0.1], [bug-libtool@gnu.org])
+ AC_CONFIG_SRCDIR([main.c])
++AC_CONFIG_AUX_DIR([..])
+
+
+ ## ------------------------ ##
+diff -urNd -urNd libtool-1.5/demo/configure.ac libtool-1.5.ac/demo/configure.ac
+--- libtool-1.5/demo/configure.ac 2002-03-02 22:19:55.000000000 -0500
++++ libtool-1.5.ac/demo/configure.ac 2004-05-05 17:16:17.000000000 -0400
+@@ -24,6 +24,7 @@
+ ## ------------------------ ##
+ AC_INIT([demo], [1.0], [bug-libtool@gnu.org])
+ AC_CONFIG_SRCDIR([hello.c])
++AC_CONFIG_AUX_DIR([..])
+
+
+ ## ------------------------ ##
+diff -urNd -urNd libtool-1.5/demo/Makefile.am libtool-1.5.ac/demo/Makefile.am
+--- libtool-1.5/demo/Makefile.am 2002-11-19 04:42:39.000000000 -0500
++++ libtool-1.5.ac/demo/Makefile.am 2004-05-05 17:17:34.000000000 -0400
+@@ -121,17 +121,16 @@
+
+ # This is one of the essential tests for deplibs_check_method=pass_all.
+ # If this one passes with pass_all, it is likely that pass_all works
+-EXTRA_LIBRARIES = libhell0.a
+-libhell0_a_SOURCES = hello.c foo.c
+-EXTRA_LTLIBRARIES = libhell1.la libhell2.la
++EXTRA_LTLIBRARIES = libhell0.la libhell1.la libhell2.la
++libhell0_la_SOURCES = hello.c foo.c
+ libhell1_la_SOURCES = hell1.c
+ libhell1_la_LIBADD = -L. -lhell0
+ libhell1_la_LDFLAGS = -no-undefined -rpath $(libdir)
+-libhell1_la_DEPENDENCIES = libhell0.a
++libhell1_la_DEPENDENCIES = libhell0.la
+ libhell2_la_SOURCES = hell2.c
+ libhell2_la_LIBADD = -L. -lhell0
+ libhell2_la_LDFLAGS = -no-undefined -rpath $(libdir)
+-libhell2_la_DEPENDENCIES = libhell0.a
++libhell2_la_DEPENDENCIES = libhell0.la
+ EXTRA_PROGRAMS = hell0
+ hell0_SOURCES = main.c
+ hell0_LDADD = libhell1.la libhell2.la $(LIBM)
+diff -urNd -urNd libtool-1.5/depdemo/configure.ac libtool-1.5.ac/depdemo/configure.ac
+--- libtool-1.5/depdemo/configure.ac 2002-10-22 15:29:28.000000000 -0400
++++ libtool-1.5.ac/depdemo/configure.ac 2004-05-05 17:16:17.000000000 -0400
+@@ -24,6 +24,7 @@
+ ## ------------------------ ##
+ AC_INIT([depdemo], [0.1], [bug-libtool@gnu.org])
+ AC_CONFIG_SRCDIR([main.c])
++AC_CONFIG_AUX_DIR([..])
+
+
+ ## ------------------------ ##
+diff -urNd -urNd libtool-1.5/f77demo/configure.ac libtool-1.5.ac/f77demo/configure.ac
+--- libtool-1.5/f77demo/configure.ac 2003-03-22 01:34:27.000000000 -0500
++++ libtool-1.5.ac/f77demo/configure.ac 2004-05-05 17:16:17.000000000 -0400
+@@ -23,6 +23,7 @@
+ ## ------------------------ ##
+ AC_INIT([f77demo], [0.1], [bug-libtool@gnu.org])
+ AC_CONFIG_SRCDIR([foof.f])dnl
++AC_CONFIG_AUX_DIR([..])
+
+ ## ------------------------ ##
+ ## Automake Initialisation. ##
+diff -urNd -urNd libtool-1.5/mdemo/configure.ac libtool-1.5.ac/mdemo/configure.ac
+--- libtool-1.5/mdemo/configure.ac 2001-10-06 11:35:17.000000000 -0400
++++ libtool-1.5.ac/mdemo/configure.ac 2004-05-05 17:16:17.000000000 -0400
+@@ -24,6 +24,7 @@
+ ## ------------------------ ##
+ AC_INIT([mdemo], [0.1], [bug-libtool@gnu.org])
+ AC_CONFIG_SRCDIR([main.c])
++AC_CONFIG_AUX_DIR([..])
+
+
+ ## ------------------------ ##
+diff -urNd -urNd libtool-1.5/pdemo/configure.ac libtool-1.5.ac/pdemo/configure.ac
+--- libtool-1.5/pdemo/configure.ac 2001-10-06 11:35:17.000000000 -0400
++++ libtool-1.5.ac/pdemo/configure.ac 2004-05-05 17:16:17.000000000 -0400
+@@ -24,6 +24,7 @@
+ ## ------------------------ ##
+ AC_INIT([pdemo], [0.1], [bug-libtool@gnu.org])
+ AC_CONFIG_SRCDIR([longer_file_name_hello.c])
++AC_CONFIG_AUX_DIR([..])
+
+
+ ## ------------------------ ##
+diff -urNd -urNd libtool-1.5/pdemo/Makefile.am libtool-1.5.ac/pdemo/Makefile.am
+--- libtool-1.5/pdemo/Makefile.am 2002-11-19 04:42:39.000000000 -0500
++++ libtool-1.5.ac/pdemo/Makefile.am 2004-05-05 17:17:59.000000000 -0400
+@@ -121,17 +121,16 @@
+
+ # This is one of the essential tests for deplibs_check_method=pass_all.
+ # If this one passes with pass_all, it is likely that pass_all works
+-EXTRA_LIBRARIES = libhell0.a
+-libhell0_a_SOURCES = longer_file_name_hello.c longer_file_name_foo.c
+-EXTRA_LTLIBRARIES = libhell1.la libhell2.la
++libhell0_la_SOURCES = longer_file_name_hello.c longer_file_name_foo.c
++EXTRA_LTLIBRARIES = libhell0.la libhell1.la libhell2.la
+ libhell1_la_SOURCES = longer_file_name_hell1.c
+ libhell1_la_LIBADD = -L. -lhell0
+ libhell1_la_LDFLAGS = -no-undefined -rpath $(libdir)
+-libhell1_la_DEPENDENCIES = libhell0.a
++libhell1_la_DEPENDENCIES = libhell0.la
+ libhell2_la_SOURCES = longer_file_name_hell2.c
+ libhell2_la_LIBADD = -L. -lhell0
+ libhell2_la_LDFLAGS = -no-undefined -rpath $(libdir)
+-libhell2_la_DEPENDENCIES = libhell0.a
++libhell2_la_DEPENDENCIES = libhell0.la
+ EXTRA_PROGRAMS = hell0
+ hell0_SOURCES = longer_file_name_main.c
+ hell0_LDADD = libhell1.la libhell2.la $(LIBM)
+diff -urNd -urNd libtool-1.5/tagdemo/configure.ac libtool-1.5.ac/tagdemo/configure.ac
+--- libtool-1.5/tagdemo/configure.ac 2001-10-06 11:35:17.000000000 -0400
++++ libtool-1.5.ac/tagdemo/configure.ac 2004-05-05 17:16:17.000000000 -0400
+@@ -24,6 +24,7 @@
+ ## ------------------------ ##
+ AC_INIT([tagdemo], [0.1], [bug-libtool@gnu.org])
+ AC_CONFIG_SRCDIR([foo.cpp])
++AC_CONFIG_AUX_DIR([..])
+
+ AC_CANONICAL_TARGET
+
diff --git a/libtool/libtool-1.5.10/install-path-check.patch b/libtool/libtool-1.5.10/install-path-check.patch
index e69de29bb2..46cc960986 100644
--- a/libtool/libtool-1.5.10/install-path-check.patch
+++ b/libtool/libtool-1.5.10/install-path-check.patch
@@ -0,0 +1,25 @@
+
+#
+# Patch managed by http://www.mn-logistik.de/unsupported/pxa250/patcher
+#
+
+--- libtool-1.5.6/ltmain.in~install-path-check 2004-05-14 22:56:19.000000000 -0500
++++ libtool-1.5.6/ltmain.in 2004-05-14 22:57:48.000000000 -0500
+@@ -5503,10 +5503,13 @@
+ # At present, this check doesn't affect windows .dll's that
+ # are installed into $libdir/../bin (currently, that works fine)
+ # but it's something to keep an eye on.
+- if test "$inst_prefix_dir" = "$destdir"; then
+- $echo "$modename: error: cannot install \`$file' to a directory not ending in $libdir" 1>&2
+- exit $EXIT_FAILURE
+- fi
++ #
++ # This breaks install into our staging area. -PB
++ #
++ # if test "$inst_prefix_dir" = "$destdir"; then
++ # $echo "$modename: error: cannot install \`$file' to a directory not ending in $libdir" 1>&2
++ # exit $EXIT_FAILURE
++ # fi
+
+ if test -n "$inst_prefix_dir"; then
+ # Stick the inst_prefix_dir data into the link command.
diff --git a/libtool/libtool-1.5.10/libdir-la.patch b/libtool/libtool-1.5.10/libdir-la.patch
index e69de29bb2..47aae4c7af 100644
--- a/libtool/libtool-1.5.10/libdir-la.patch
+++ b/libtool/libtool-1.5.10/libdir-la.patch
@@ -0,0 +1,52 @@
+
+#
+# Patch managed by http://www.mn-logistik.de/unsupported/pxa250/patcher
+#
+
+--- libtool-1.5.6/ltmain.in~libdir-la 2004-04-01 21:46:15.000000000 -0600
++++ libtool-1.5.6/ltmain.in 2004-05-14 22:53:51.000000000 -0500
+@@ -2147,8 +2147,14 @@
+ absdir="$abs_ladir"
+ libdir="$abs_ladir"
+ else
+- dir="$libdir"
+- absdir="$libdir"
++ # Adding 'libdir' from the .la file to our library search paths
++ # breaks crosscompilation horribly. We cheat here and don't add
++ # it, instead adding the path where we found the .la. -CL
++ dir="$abs_ladir"
++ absdir="$abs_ladir"
++ libdir="$abs_ladir"
++ #dir="$libdir"
++ #absdir="$libdir"
+ fi
+ else
+ dir="$ladir/$objdir"
+@@ -2615,6 +2621,16 @@
+ esac
+ if grep "^installed=no" $deplib > /dev/null; then
+ path="$absdir/$objdir"
++# This interferes with crosscompilation. -CL
++# else
++# eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $deplib`
++# if test -z "$libdir"; then
++# $echo "$modename: \`$deplib' is not a valid libtool archive" 1>&2
++# exit 1
++# fi
++# if test "$absdir" != "$libdir"; then
++# $echo "$modename: warning: \`$deplib' seems to be moved" 1>&2
++# fi
+ else
+ eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $deplib`
+ if test -z "$libdir"; then
+@@ -5165,6 +5181,10 @@
+ # Replace all uninstalled libtool libraries with the installed ones
+ newdependency_libs=
+ for deplib in $dependency_libs; do
++ # Replacing uninstalled with installed can easily break crosscompilation,
++ # since the installed path is generally the wrong architecture. -CL
++ newdependency_libs="$newdependency_libs $deplib"
++ continue
+ case $deplib in
+ *.la)
+ name=`$echo "X$deplib" | $Xsed -e 's%^.*/%%'`
diff --git a/libtool/libtool-1.5.10/prefix.patch b/libtool/libtool-1.5.10/prefix.patch
index e69de29bb2..fa30d02f26 100644
--- a/libtool/libtool-1.5.10/prefix.patch
+++ b/libtool/libtool-1.5.10/prefix.patch
@@ -0,0 +1,40 @@
+
+#
+# Patch managed by http://www.mn-logistik.de/unsupported/pxa250/patcher
+#
+
+--- libtool-1.5.6/libtool.m4~prefix.patch 2004-05-14 23:02:28.000000000 -0500
++++ libtool-1.5.6/libtool.m4 2004-05-14 23:02:34.000000000 -0500
+@@ -82,7 +82,8 @@
+ LIBTOOL_DEPS="$ac_aux_dir/ltmain.sh"
+
+ # Always use our own libtool.
+-LIBTOOL='$(SHELL) $(top_builddir)/libtool'
++LIBTOOL='$(SHELL) $(top_builddir)'
++LIBTOOL="$LIBTOOL/$host_alias-libtool"
+ AC_SUBST(LIBTOOL)dnl
+
+ # Prevent multiple expansion
+@@ -149,7 +150,7 @@
+ rm="rm -f"
+
+ # Global variables:
+-default_ofile=libtool
++default_ofile=${host_alias}-libtool
+ can_build_shared=yes
+
+ # All known linkers require a `.a' archive for static linking (except M$VC,
+--- libtool-1.5.6/Makefile.am~prefix.patch 2004-04-11 00:07:06.000000000 -0500
++++ libtool-1.5.6/Makefile.am 2004-05-14 23:02:46.000000000 -0500
+@@ -30,9 +30,9 @@
+ aclocal_DATA = $(aclocal_macros)
+
+ # The standalone libtool script, and the libtool distributor.
+-bin_SCRIPTS = libtool libtoolize
++bin_SCRIPTS = $(host_alias)-libtool libtoolize
+
+-libtool: $(srcdir)/ltmain.sh $(top_builddir)/configure.ac
++$(host_alias)-libtool: $(srcdir)/ltmain.sh $(top_builddir)/configure.ac
+ $(SHELL) $(top_builddir)/config.status --recheck
+ chmod +x $@
+
diff --git a/libtool/libtool-1.5.10/sedvar.patch b/libtool/libtool-1.5.10/sedvar.patch
index e69de29bb2..d505edae24 100644
--- a/libtool/libtool-1.5.10/sedvar.patch
+++ b/libtool/libtool-1.5.10/sedvar.patch
@@ -0,0 +1,16 @@
+
+#
+# Made by http://www.mn-logistik.de/unsupported/pxa250/patcher
+#
+
+--- libtool-1.5/ltmain.in~sedvar 2003-04-14 16:58:24.000000000 -0500
++++ libtool-1.5/ltmain.in 2003-09-24 14:18:50.175518400 -0500
+@@ -48,6 +48,8 @@
+ exit 0
+ fi
+
++[ -z "${SED}" ] && SED=sed
++
+ # The name of this program.
+ progname=`$echo "$0" | ${SED} 's%^.*/%%'`
+ modename="$progname"
diff --git a/libtool/libtool-1.5.10/tag.patch b/libtool/libtool-1.5.10/tag.patch
index e69de29bb2..8921a3efb7 100644
--- a/libtool/libtool-1.5.10/tag.patch
+++ b/libtool/libtool-1.5.10/tag.patch
@@ -0,0 +1,19 @@
+
+#
+# Patch managed by http://www.mn-logistik.de/unsupported/pxa250/patcher
+#
+
+--- libtool-1.5.6/ltmain.in~tag 2004-05-15 05:14:32.000000000 -0400
++++ libtool-1.5.6/ltmain.in 2004-05-15 05:18:01.000000000 -0400
+@@ -226,8 +226,9 @@
+ # line option must be used.
+ if test -z "$tagname"; then
+ $echo "$modename: unable to infer tagged configuration"
+- $echo "$modename: specify a tag with \`--tag'" 1>&2
+- exit $EXIT_FAILURE
++ $echo "$modename: defaulting to \`CC'"
++ $echo "$modename: if this is not correct, specify a tag with \`--tag'"
++# exit $EXIT_FAILURE
+ # else
+ # $echo "$modename: using $tagname tagged configuration"
+ fi
diff --git a/libtool/libtool-1.5.10/uclibc.patch b/libtool/libtool-1.5.10/uclibc.patch
index e69de29bb2..2c8cbabbb3 100644
--- a/libtool/libtool-1.5.10/uclibc.patch
+++ b/libtool/libtool-1.5.10/uclibc.patch
@@ -0,0 +1,19 @@
+
+#
+# Patch managed by http://www.holgerschurig.de/patcher.html
+#
+
+--- libtool-1.5.8/libtool.m4~uclibc
++++ libtool-1.5.8/libtool.m4
+@@ -2221,6 +2221,11 @@
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
++linux-uclibc*)
++ lt_cv_deplibs_check_method=pass_all
++ lt_cv_file_magic_test_file=`echo /lib/libuClibc-*.so`
++ ;;
++
+ netbsd*)
+ if echo __ELF__ | $CC -E - | grep __ELF__ > /dev/null; then
+ lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$'
diff --git a/libusb/libusb-0.1.8/amd64-fPIC.patch b/libusb/libusb-0.1.8/amd64-fPIC.patch
index e69de29bb2..b8cfdbe697 100644
--- a/libusb/libusb-0.1.8/amd64-fPIC.patch
+++ b/libusb/libusb-0.1.8/amd64-fPIC.patch
@@ -0,0 +1,11 @@
+--- Makefile.in.orig 2004-03-27 16:34:18.371955584 +0100
++++ Makefile.in 2004-03-27 16:34:07.796563288 +0100
+@@ -54,7 +54,7 @@
+ CC = @CC@
+ CCDEPMODE = @CCDEPMODE@
+
+-CFLAGS = $(CFLAGS_EXT)
++CFLAGS = $(CFLAGS_EXT) -fPIC
+ CPP = @CPP@
+ CPPFLAGS = @CPPFLAGS@
+ CXX = @CXX@
diff --git a/libusb/libusb-0.1.8/configure.patch b/libusb/libusb-0.1.8/configure.patch
index e69de29bb2..6f014a9dcd 100644
--- a/libusb/libusb-0.1.8/configure.patch
+++ b/libusb/libusb-0.1.8/configure.patch
@@ -0,0 +1,99 @@
+--- libusb-0.1.8/configure.in~configure.patch 2004-02-12 03:27:18.000000000 +1100
++++ libusb-0.1.8/configure.in 2004-11-03 20:17:45.949802575 +1100
+@@ -1,10 +1,11 @@
+ # This configure.in is based off of the one that glib uses
+
+ # require atleast autoconf 2.13
+-AC_PREREQ(2.13)
++AC_PREREQ(2.57)
+
+ # init autoconf (and check for presence of usb.c)
+-AC_INIT(usb.c)
++AC_INIT
++AC_CONFIG_SRCDIR([usb.c])
+
+ AH_TOP(
+ #undef LINUX_API
+@@ -29,8 +30,8 @@
+
+ dnl we need to AC_DIVERT_PUSH/AC_DIVERT_POP these variable definitions so they
+ dnl are available for $ac_help expansion (don't we all *love* autoconf?)
+-ifdef([AC_DIVERSION_NOTICE], [AC_DIVERT_PUSH(AC_DIVERSION_NOTICE)],
+- [AC_DIVERT_PUSH(NOTICE)])
++dnl ifdef([AC_DIVERSION_NOTICE], [AC_DIVERT_PUSH(AC_DIVERSION_NOTICE)],
++dnl [AC_DIVERT_PUSH(NOTICE)])
+
+ #
+ # The following version number definitions apply to libusb
+@@ -52,7 +53,7 @@
+ LIBUSB_BINARY_AGE=4
+ LIBUSB_VERSION=$LIBUSB_MAJOR_VERSION.$LIBUSB_MINOR_VERSION.$LIBUSB_MICRO_VERSION
+ dnl
+-AC_DIVERT_POP()dnl
++dnl AC_DIVERT_POP()dnl
+
+ AC_SUBST(LIBUSB_MAJOR_VERSION)
+ AC_SUBST(LIBUSB_MINOR_VERSION)
+@@ -81,7 +82,7 @@
+ AM_INIT_AUTOMAKE($PACKAGE, $VERSION, no-define)
+
+ # Specify a configuration file
+-AM_CONFIG_HEADER(config.h)
++AC_CONFIG_HEADERS([config.h])
+
+ AC_DEFINE_UNQUOTED(LIBUSB_MAJOR_VERSION, $LIBUSB_MAJOR_VERSION)
+ AC_DEFINE_UNQUOTED(LIBUSB_MINOR_VERSION, $LIBUSB_MINOR_VERSION)
+@@ -149,14 +150,14 @@
+
+ dnl figure debugging default, prior to $ac_help setup
+ dnl
+-ifdef([AC_DIVERSION_NOTICE], [AC_DIVERT_PUSH(AC_DIVERSION_NOTICE)],
+- [AC_DIVERT_PUSH(NOTICE)])
++dnl ifdef([AC_DIVERSION_NOTICE], [AC_DIVERT_PUSH(AC_DIVERSION_NOTICE)],
++dnl [AC_DIVERT_PUSH(NOTICE)])
+ if test `expr $LIBUSB_MINOR_VERSION \% 2` = 1 ; then
+ debug_default=yes
+ else
+ debug_default=minimum
+ fi
+-AC_DIVERT_POP()dnl
++dnl AC_DIVERT_POP()dnl
+
+ dnl declare --enable-* args and collect ac_help strings
+ AC_ARG_ENABLE(debug, [ --enable-debug=[no/minimum/yes] turn on debugging [default=$debug_default]],,enable_debug=$debug_default)
+@@ -207,15 +208,11 @@
+
+ if test "$os_support" = "bsd"; then
+ AC_MSG_CHECKING(if dev/usb/usb.h uses new naming convention)
+- AC_TRY_COMPILE(
+- [#include <dev/usb/usb.h>],
+- [int main(void)
++ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <dev/usb/usb.h>]], [[int main(void)
+ {
+ int a = ((struct usb_ctl_request *)0L)->ucr_addr;
+ return 0;
+- }],
+- [AC_MSG_RESULT(yes)],
+- [AC_MSG_RESULT(no)
++ }]])],[AC_MSG_RESULT(yes)],[AC_MSG_RESULT(no)
+ AC_DEFINE_UNQUOTED(HAVE_OLD_DEV_USB_USB_H)])
+ fi
+
+@@ -246,7 +243,7 @@
+
+ AM_CONDITIONAL(BUILD_DOCS, test "$build_docs" != "no")
+
+-AC_OUTPUT([
++AC_CONFIG_FILES([
+ Makefile
+ doc/Makefile
+ libusb.spec
+@@ -255,5 +252,6 @@
+ README
+ INSTALL.libusb
+ usb.h
+-],[chmod +x libusb-config])
+-
++])
++AC_CONFIG_COMMANDS([default],[[chmod +x libusb-config]],[[]])
++AC_OUTPUT
diff --git a/libusb/libusb-0.1.8/pkgconfig.patch b/libusb/libusb-0.1.8/pkgconfig.patch
index e69de29bb2..737c89edb0 100644
--- a/libusb/libusb-0.1.8/pkgconfig.patch
+++ b/libusb/libusb-0.1.8/pkgconfig.patch
@@ -0,0 +1,68 @@
+--- libusb-0.1.8.orig/libusb.pc.in
++++ libusb-0.1.8/libusb.pc.in
+@@ -0,0 +1,11 @@
++prefix=@prefix@
++exec_prefix=@exec_prefix@
++libdir=@libdir@
++includedir=@includedir@
++
++Name: libusb
++Description: USB access library
++Version: @VERSION@
++Libs: -L${libdir} -lusb
++Cflags: -I${includedir}
++
+--- libusb-0.1.8.orig/configure.in
++++ libusb-0.1.8/configure.in
+@@ -219,6 +219,26 @@
+ AC_DEFINE_UNQUOTED(HAVE_OLD_DEV_USB_USB_H)])
+ fi
+
++#pkg-config: pkg-config is a tool to simplify checks for libraries and versions.
++pkgconfig_msg="no (http://www.freedesktop.org/software/pkgconfig)"
++try_pkgconfig=true
++have_pkgconfig=false
++AC_ARG_WITH(pkgconfig, [ --without-pkgconfig don't use pkg-config],[
++ if test x$withval = xno; then
++ try_pkgconfig=false
++ pkgconfig_msg="no (not requested)"
++ fi])
++if $try_pkgconfig; then
++ AC_PATH_PROG(PKG_CONFIG,pkg-config)
++ if test -n "${PKG_CONFIG}"; then
++ have_pkgconfig=true
++ pkgconfig_msg="yes"
++ fi
++fi
++# guessing directory to install *.pc into
++pkgconfigdir='${libdir}/pkgconfig'
++AC_SUBST(pkgconfigdir)
++
+ # Check if the user wants to enable documentation
+ AC_MSG_CHECKING(if we should build the documentation)
+ AC_ARG_ENABLE(build-docs, [ --disable-build-docs turn off building documentation],
+@@ -249,6 +269,7 @@
+ AC_OUTPUT([
+ Makefile
+ doc/Makefile
++ libusb.pc
+ libusb.spec
+ libusb-config
+ tests/Makefile
+--- libusb-0.1.8.orig/Makefile.am
++++ libusb-0.1.8/Makefile.am
+@@ -12,11 +12,13 @@
+
+ bin_SCRIPTS = libusb-config
+
+-EXTRA_DIST = LICENSE libusb.spec.in libusb.spec libusb-config.in README.in README INSTALL.libusb.in INSTALL.libusb
++EXTRA_DIST = LICENSE libusb.spec.in libusb.spec libusb-config.in README.in README INSTALL.libusb.in INSTALL.libusb libusb.pc.in
+ EXTRA_libusb_la_SOURCE = linux.c linux.h bsd.c darwin.c
+
+ lib_LTLIBRARIES = libusb.la
+
++pkgconfig_DATA= libusb.pc
++
+ if LINUX_API
+ OS_SUPPORT = linux.c linux.h
+ else
diff --git a/libxine/files/configure-1.0.0-rc7.patch b/libxine/files/configure-1.0.0-rc7.patch
index e69de29bb2..c25d41988d 100644
--- a/libxine/files/configure-1.0.0-rc7.patch
+++ b/libxine/files/configure-1.0.0-rc7.patch
@@ -0,0 +1,177 @@
+--- xine-lib-1-rc6a/configure.ac.orig 2004-09-16 22:28:33.762843408 -0400
++++ xine-lib-1-rc6a/configure.ac 2004-09-16 22:29:00.969707336 -0400
+@@ -622,7 +622,7 @@
+ dnl ---------------------------------------------
+
+ AM_PATH_AALIB(1.4,, AC_MSG_RESULT([*** All of AALIB dependent parts will be disabled ***]))
+-AM_CONDITIONAL(HAVE_AA, test x$no_aalib != "xyes")
++AM_CONDITIONAL(HAVE_AA, [test x"$enable_aalib" = "xyes" && test x$no_aalib != "xyes"])
+
+
+ dnl ---------------------------------------------
+@@ -735,10 +735,7 @@
+ dnl check for SDL
+ dnl ---------------------------------------------
+
+-AM_PATH_SDL(1.1.5,
+- AC_DEFINE(HAVE_SDL,1,[Define this if you have SDL library installed]),
+- [])
+-AM_CONDITIONAL(HAVE_SDL, [test x"$no_sdl" != x"yes"])
++AM_CONDITIONAL(HAVE_SDL, 0)
+
+
+ dnl ---------------------------------------------
+@@ -868,61 +865,29 @@
+ dnl Ogg/Theora libs.
+ dnl ---------------------------------------------
+
+-AM_PATH_OGG(
+- [ AM_PATH_THEORA(AC_DEFINE(HAVE_THEORA,1,[Define this if you have theora]),
+- AC_MSG_RESULT([*** All OGG/THEORA dependent parts will be disabled ***]))
+- ],
+- AC_MSG_RESULT([*** All of OGG/Theora dependent parts will be disabled ***]))
+-AM_CONDITIONAL(HAVE_THEORA, [test x"$no_ogg" != "xyes" -a x"$no_theora" != "xyes"])
++AM_CONDITIONAL(HAVE_THEORA, 0)
+
+
+ dnl ---------------------------------------------
+ dnl Ogg/Speex libs.
+ dnl ---------------------------------------------
+
+-PKG_CHECK_MODULES(SPEEX, speex, no_speex="no", no_speex="yes")
+-
+-if test "x$no_speex" = "xyes" ; then
+- AM_PATH_OGG([ AM_PATH_SPEEX(no_speex="no")])
+-fi
+-
+-if test "x$no_speex" = "xyes" ; then
+- AC_MSG_RESULT([*** All OGG/SPEEX dependent parts will be disabled ***])
+-else
+- AC_DEFINE(HAVE_SPEEX,1,[Define this if you have speex])
+-
+- dnl Test whether Speex headers are eg. <speex.h> or <speex/speex.h>
+- dnl Speex headers were moved in mid-2004; 1.0.x has backwards compatible headers, 1.1.x does not
+- AC_CHECK_HEADER([speex/speex.h],
+- AC_DEFINE(HAVE_SPEEX_SUBDIR, [1], [Define to 1 if speex headers are eg. <speex/speex.h>])
+- )
+-
+- AC_SUBST(SPEEX_CFLAGS)
+- AC_SUBST(SPEEX_LIBS)
+-fi
+-AM_CONDITIONAL(HAVE_SPEEX, [test x"$no_ogg" != "xyes" -a x"$no_speex" != "xyes"])
++AM_CONDITIONAL(HAVE_SPEEX, 0)
+
+
+ dnl ---------------------------------------------
+ dnl check for libFLAC
+ dnl ---------------------------------------------
+
+-AM_PATH_LIBFLAC([],
+- AC_MSG_RESULT([*** All FLAC dependent parts will be disabled ***]))
+-AM_CONDITIONAL(HAVE_FLAC, [test x"$no_libFLAC" != "xyes"])
++AM_CONDITIONAL(HAVE_FLAC, 0)
+
+
+ dnl ---------------------------------------------
+ dnl MNG libs.
+ dnl ---------------------------------------------
+
+-AC_CHECK_LIB(mng, mng_initialize,
+- [ AC_CHECK_HEADER(libmng.h,
+- [ have_libmng=yes
+- MNG_LIBS="-lmng" ],
+- AC_MSG_RESULT([*** All libmng dependent parts will be disabled ***]))],
+- AC_MSG_RESULT([*** All libmng dependent parts will be disabled ***]))
+-AM_CONDITIONAL(HAVE_LIBMNG, test x"$have_libmng" = "xyes")
++MNG_LIBS=""
++AM_CONDITIONAL(HAVE_LIBMNG, 0)
+ AC_SUBST(MNG_LIBS)
+
+
+@@ -952,7 +917,7 @@
+ dnl freetype2 lib.
+ dnl ---------------------------------------------
+
+-AM_PATH_FREETYPE2()
++AM_CONDITIONAL(HAVE_FT2, 0)
+
+
+ dnl ---------------------------------------------
+@@ -995,37 +960,21 @@
+ dnl ESD support
+ dnl ---------------------------------------------
+
+-AM_PATH_ESD(0.2.8,
+- AC_DEFINE(HAVE_ESD,1,[Define this if you have ESD (libesd) installed]),
+- AC_MSG_RESULT(*** All of ESD dependent parts will be disabled ***))
+-AM_CONDITIONAL(HAVE_ESD, test x"$no_esd" != "xyes")
++AM_CONDITIONAL(HAVE_ESD, 0)
+
+
+ dnl ---------------------------------------------
+ dnl ARTS support
+ dnl ---------------------------------------------
+
+-AM_PATH_ARTS(0.9.5,
+- AC_DEFINE(HAVE_ARTS,1,[Define this if you have ARTS (libartsc) installed]),
+- AC_MSG_RESULT(*** All of ARTS dependent parts will be disabled ***))
+-AM_CONDITIONAL(HAVE_ARTS, test x"$no_arts" != "xyes")
++AM_CONDITIONAL(HAVE_ARTS, 0)
+
+
+ dnl ---------------------------------------------
+ dnl gnome-vfs support
+ dnl ---------------------------------------------
+
+-PKG_CHECK_MODULES(GNOME_VFS, gnome-vfs-2.0,
+- no_gnome_vfs=no,
+- no_gnome_vfs=yes)
+-AC_SUBST(GNOME_VFS_CFLAGS)
+-AC_SUBST(GNOME_VFS_LIBS)
+-if test x"$no_gnome_vfs" != "xyes"; then
+-AC_DEFINE(HAVE_GNOME_VFS,1,[Define this if you have gnome-vfs installed])
+-else
+-AC_MSG_RESULT(*** All of the gnome-vfs dependent parts will be disabled ***)
+-fi
+-AM_CONDITIONAL(HAVE_GNOME_VFS, test x"$no_gnome_vfs" != "xyes")
++AM_CONDITIONAL(HAVE_GNOME_VFS, 0)
+
+
+ dnl ---------------------------------------------
+@@ -1188,35 +1137,7 @@
+ ],ISOC99_PRAGMA=no)
+ AC_MSG_RESULT($ISOC99_PRAGMA)
+
+- dnl bitfield order
+- AC_MSG_CHECKING(bitfield ordering in structs)
+- AC_TRY_RUN([
+-int
+- main() {
+- struct { char bit_0:1, bit_12:2, bit_345:3, bit_67:2; }
+-#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4)
+- __attribute__((packed))
+-#endif
+- bf = { 1,1,1,1 };
+- if (sizeof (bf) != 1) return 1;
+- return *((unsigned char*) &bf) != 0x4b; }
+-], bf_lsbf=1, AC_TRY_RUN([
+-int
+-main() {
+- struct { char bit_0:1, bit_12:2, bit_345:3, bit_67:2; }
+-#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4)
+- __attribute__((packed))
+-#endif
+- bf = { 1,1,1,1 };
+- if (sizeof (bf) != 1) return 1;
+- return *((unsigned char*) &bf) != 0xa5; }
+-], bf_lsbf=0, AC_MSG_ERROR([unsupported bitfield ordering])))
+- if test "x$bf_lsbf" = "x1"; then
+- AC_MSG_RESULT(LSBF)
+- AC_DEFINE(BITFIELD_LSBF, [], [compiler does lsbf in struct bitfields])
+- else
+- AC_MSG_RESULT(MSBF)
+- fi
++ AC_DEFINE(BITFIELD_LSBF, [], [compiler does lsbf in struct bitfields])
+
+ AC_HAVE_HEADERS( errno.h fcntl.h \
+ stdbool.h stdlib.h stdint.h stdio.h string.h \
diff --git a/libxine/files/cpu-1.0.0-rc7.patch b/libxine/files/cpu-1.0.0-rc7.patch
index e69de29bb2..a7ccdbbfa9 100644
--- a/libxine/files/cpu-1.0.0-rc7.patch
+++ b/libxine/files/cpu-1.0.0-rc7.patch
@@ -0,0 +1,112 @@
+--- xine-lib-1-rc6a/src/post/goom/xmmx.c.orig 2004-09-16 21:53:34.040049792 -0400
++++ xine-lib-1-rc6a/src/post/goom/xmmx.c 2004-09-16 21:59:24.407785800 -0400
+@@ -112,23 +112,23 @@
+ * post : mm3 & mm4 : coefs for this position
+ * mm1 : X vector [0|X]
+ *
+- * modif : eax,ebx
++ * modif : eax,ecx
+ */
+ __asm__ __volatile__ (
+- "movd %%mm0,%%ebx\n"
++ "movd %%mm0,%%ecx\n"
+ "movq %%mm0,%%mm1\n"
+
+- "andl $15,%%ebx\n"
++ "andl $15,%%ecx\n"
+ "psrlq $32,%%mm1\n"
+
+- "shll $6,%%ebx\n"
++ "shll $6,%%ecx\n"
+ "movd %%mm1,%%eax\n"
+
+- "addl %0,%%ebx\n"
++ "addl %0,%%ecx\n"
+ "andl $15,%%eax\n"
+
+- "movd (%%ebx,%%eax,4),%%mm3\n"
+- ::"g"(precalCoef):"eax","ebx");
++ "movd (%%ecx,%%eax,4),%%mm3\n"
++ ::"g"(precalCoef):"eax","ecx");
+
+ /*
+ * extraction des coefficients...
+@@ -158,7 +158,7 @@
+ * post : mm0 : expix1[position]
+ * mm2 : expix1[position+largeur]
+ *
+- * modif : eax,ebx
++ * modif : eax,ecx
+ */
+ psrld_i2r (PERTEDEC,mm0);
+ psrld_i2r (PERTEDEC,mm1);
+@@ -167,23 +167,23 @@
+ /*^*/ "movq %%mm3,%%mm5\n" /*^*/
+
+ "mull %1\n"
+- "movd %%mm0,%%ebx\n"
++ "movd %%mm0,%%ecx\n"
+ /*^*/ "punpcklbw %%mm5, %%mm3\n" /*^*/
+
+- "addl %%ebx,%%eax\n"
++ "addl %%ecx,%%eax\n"
+ /*^*/ "movq %%mm3,%%mm4\n" /*^*/
+ /*^*/ "movq %%mm3,%%mm5\n" /*^*/
+
+- "movl %0,%%ebx\n"
++ "movl %0,%%ecx\n"
+ /*^*/ "punpcklbw %%mm5,%%mm3\n" /*^*/
+
+- "movq (%%ebx,%%eax,4),%%mm0\n"
++ "movq (%%ecx,%%eax,4),%%mm0\n"
+ /*^*/ "punpckhbw %%mm5,%%mm4\n" /*^*/
+
+ "addl %1,%%eax\n"
+- "movq (%%ebx,%%eax,4),%%mm2\n"
++ "movq (%%ecx,%%eax,4),%%mm2\n"
+
+- : : "X"(expix1), "X"(prevX):"eax","ebx"
++ : : "X"(expix1), "X"(prevX):"eax","ecx"
+ );
+
+ /*
+--- xine-lib-1-rc7/src/post/goom/mmx.h 2004-09-12 05:17:26.000000000 -0700
++++ xine-lib-1-rc7.new/src/post/goom/mmx.h 2004-11-09 16:08:17.777667856 -0700
+@@ -71,13 +71,15 @@
+ */
+ register int rval = 0;
+
++ /* Maybe if I yell it will help: DON'T CLOBBER EBX! */
++
+ __asm__ __volatile__ (
+ /* See if CPUID instruction is supported ... */
+ /* ... Get copies of EFLAGS into eax and ecx */
+ "pushf\n\t"
+ "popl %%eax\n\t"
+ "movl %%eax, %%ecx\n\t"
+-
++
+ /* ... Toggle the ID bit in one copy and store */
+ /* to the EFLAGS reg */
+ "xorl $0x200000, %%eax\n\t"
+@@ -97,6 +99,8 @@
+ /* Get standard CPUID information, and
+ go to a specific vendor section */
+ "movl $0, %%eax\n\t"
++
++ "pushl %%ebx\n\t"
+ "cpuid\n\t"
+
+ /* Check for Intel */
+@@ -214,9 +218,10 @@
+ "movl $0, %0\n\n\t"
+
+ "Return:\n\t"
++ "popl %%ebx\n\t"
+ : "=X" (rval)
+ : /* no input */
+- : "eax", "ebx", "ecx", "edx"
++ : "eax", "ecx", "edx"
+ );
+
+ /* Return */
diff --git a/libxine/libxine-opie-1.0.0-rc7/demuxogg.patch b/libxine/libxine-opie-1.0.0-rc7/demuxogg.patch
index e69de29bb2..55f239cfad 100644
--- a/libxine/libxine-opie-1.0.0-rc7/demuxogg.patch
+++ b/libxine/libxine-opie-1.0.0-rc7/demuxogg.patch
@@ -0,0 +1,26 @@
+
+#
+# Patch managed by http://www.holgerschurig.de/patcher.html
+#
+
+--- xine-lib-1-rc7/src/demuxers/demux_ogg.c~demuxogg
++++ xine-lib-1-rc7/src/demuxers/demux_ogg.c
+@@ -40,7 +40,7 @@
+ #include <inttypes.h>
+
+ #include <ogg/ogg.h>
+-#include <vorbis/codec.h>
++#include <tremor/ivorbiscodec.h>
+
+ #ifdef HAVE_SPEEX
+ #ifdef HAVE_SPEEX_SUBDIR
+--- xine-lib-1-rc7/src/demuxers/Makefile.am~demuxogg
++++ xine-lib-1-rc7/src/demuxers/Makefile.am
+@@ -1,6 +1,6 @@
+ include $(top_srcdir)/misc/Makefile.common
+
+-AM_CFLAGS = $(THEORA_CFLAGS) $(OGG_CFLAGS) $(SPEEX_CFLAGS) $(LIBMODPLUG_CFLAGS)
++AM_CFLAGS = $(THEORA_CFLAGS) $(OGG_CFLAGS) $(VORBIS_CFLAGS) $(SPEEX_CFLAGS) $(LIBMODPLUG_CFLAGS)
+
+ libdir = $(XINE_PLUGINDIR)
+
diff --git a/libxine/libxine-opie-1.0.0-rc7/fix-syntax.patch b/libxine/libxine-opie-1.0.0-rc7/fix-syntax.patch
index e69de29bb2..b129dec36c 100644
--- a/libxine/libxine-opie-1.0.0-rc7/fix-syntax.patch
+++ b/libxine/libxine-opie-1.0.0-rc7/fix-syntax.patch
@@ -0,0 +1,16 @@
+
+#
+# Patch managed by http://www.holgerschurig.de/patcher.html
+#
+
+--- xine-lib-1-rc7/src/libvorbis/xine_decoder.c~fix-syntax
++++ xine-lib-1-rc7/src/libvorbis/xine_decoder.c
+@@ -218,7 +218,7 @@
+ float **pcm;
+ int samples;
+
+- if(vorbis_synthesis(&this->vb,&this->op)==0)
++ if(vorbis_synthesis(&this->vb,&this->op,1)==0)
+ vorbis_synthesis_blockin(&this->vd,&this->vb);
+
+ if (buf->pts!=0)
diff --git a/libxine/libxine-opie-1.0.0-rc7/fix-x.patch b/libxine/libxine-opie-1.0.0-rc7/fix-x.patch
index e69de29bb2..1de163e9e3 100644
--- a/libxine/libxine-opie-1.0.0-rc7/fix-x.patch
+++ b/libxine/libxine-opie-1.0.0-rc7/fix-x.patch
@@ -0,0 +1,37 @@
+HAVE_VLDXVMC was only defined when 'X' was enabled leading to an
+error when compiling without-x
+
+Disable XVmc and VldXvmc
+
+#
+# Patch managed by http://www.holgerschurig.de/patcher.html
+#
+
+--- xine-lib-1-rc7/configure.ac~fix-x
++++ xine-lib-1-rc7/configure.ac
+@@ -618,7 +618,7 @@
+ [Define 1 if you have vldXvMC.h])
+ fi
+ fi
+-AM_CONDITIONAL(HAVE_VLDXVMC, test x$ac_have_vldxvmc_h = "xyes")
++
+ dnl Try fallback to standard XvMC if vld failed
+ if test x$ac_have_xxmc = "xno"; then
+ if test x$xxmc_stub == "xXvMCW"; then
+@@ -705,10 +705,16 @@
+
+ LIBS="$saved_libs"
+ fi
++
++ac_have_xvmc="no"
++ac_have_vldxvmc_h="no"
+ AM_CONDITIONAL(HAVE_XVMC, test x$ac_have_xvmc = "xyes")
+ AC_SUBST(XVMC_LIB)
+
+
++
++AM_CONDITIONAL(HAVE_VLDXVMC, test x$ac_have_vldxvmc_h = "xyes")
++
+ dnl ---------------------------------------------
+ dnl Checks for Xinerama extension
+ dnl ---------------------------------------------
diff --git a/libxine/libxine-opie-1.0.0-rc7/libxine-arm-configure.patch b/libxine/libxine-opie-1.0.0-rc7/libxine-arm-configure.patch
index e69de29bb2..fa80ea6b13 100644
--- a/libxine/libxine-opie-1.0.0-rc7/libxine-arm-configure.patch
+++ b/libxine/libxine-opie-1.0.0-rc7/libxine-arm-configure.patch
@@ -0,0 +1,18 @@
+
+#
+# Patch managed by http://www.holgerschurig.de/patcher.html
+#
+
+--- xine-lib-1-rc7/configure.ac~libxine-arm-configure.patch
++++ xine-lib-1-rc7/configure.ac
+@@ -1810,8 +1810,8 @@
+ AC_DEFINE_UNQUOTED(FPM_DEFAULT,,[Define to select libmad fixed point arithmetic implementation])
+ ;;
+
+- armv4l-*-linux*)
+- CFLAGS="-O2 -fsigned-char -ffast-math -mcpu=strongarm1100 -fomit-frame-pointer -fthread-jumps -fregmove $CFLAGS"
++ arm-*-linux*)
++ CFLAGS="-O2 -fsigned-char -ffast-math -march=armv4 -mtune=xscale -fomit-frame-pointer -fthread-jumps -fregmove $CFLAGS"
+ dnl CFLAGS="-O1 -fforce-mem -fforce-addr -fthread-jumps -fcse-follow-jumps -fcse-skip-blocks -fexpensive-optimizations -fregmove -fschedule-insns2 -finline-functions -fsigned-char -fomit-frame-pointer -march=armv4 -mtune=strongarm $CFLAGS"
+ DEBUG_CFLAGS="-O2 $DEBUG_CFLAGS"
+ AC_DEFINE_UNQUOTED(FPM_ARM,,[Define to select libmad fixed point arithmetic implementation])
diff --git a/libxine/libxine-opie-1.0.0-rc7/libxine-cut-memusage.patch b/libxine/libxine-opie-1.0.0-rc7/libxine-cut-memusage.patch
index e69de29bb2..762ceaa3ef 100644
--- a/libxine/libxine-opie-1.0.0-rc7/libxine-cut-memusage.patch
+++ b/libxine/libxine-opie-1.0.0-rc7/libxine-cut-memusage.patch
@@ -0,0 +1,63 @@
+
+#
+# Patch managed by http://www.holgerschurig.de/patcher.html
+#
+
+--- xine-lib-1-rc7/src/xine-engine/audio_out.c~libxine-cut-memusage
++++ xine-lib-1-rc7/src/xine-engine/audio_out.c
+@@ -95,10 +95,10 @@
+ #include "metronom.h"
+
+
+-#define NUM_AUDIO_BUFFERS 32
+-#define AUDIO_BUF_SIZE 32768
++#define NUM_AUDIO_BUFFERS 16
++#define AUDIO_BUF_SIZE 8192
+
+-#define ZERO_BUF_SIZE 5000
++#define ZERO_BUF_SIZE 2048
+
+ /* By adding gap errors (difference between reported and expected
+ * sound card clock) into metronom's vpts_offset we can use its
+--- xine-lib-1-rc7/src/xine-engine/video_decoder.c~libxine-cut-memusage
++++ xine-lib-1-rc7/src/xine-engine/video_decoder.c
+@@ -479,7 +479,7 @@
+
+ num_buffers = stream->xine->config->register_num (stream->xine->config,
+ "video.num_buffers",
+- 500,
++ 250,
+ _("number of video buffers"),
+ _("The number of video buffers (each is 8k in size) "
+ "xine uses in its internal queue. Higher values "
+@@ -487,7 +487,7 @@
+ "also increased latency and memory comsumption."),
+ 20, NULL, NULL);
+
+- stream->video_fifo = _x_fifo_buffer_new (num_buffers, 8192);
++ stream->video_fifo = _x_fifo_buffer_new (num_buffers, 4096);
+ stream->spu_track_map_entries = 0;
+
+ pthread_attr_init(&pth_attrs);
+--- xine-lib-1-rc7/src/xine-engine/video_overlay.h~libxine-cut-memusage
++++ xine-lib-1-rc7/src/xine-engine/video_overlay.h
+@@ -36,7 +36,7 @@
+ #define CLUT_Y_CR_CB_INIT(_y,_cr,_cb) { (_cb), (_cr), (_y) }
+ #endif
+
+-#define MAX_OBJECTS 50
++#define MAX_OBJECTS 5
+ #define MAX_EVENTS 50
+ #define MAX_SHOWING 5
+
+--- xine-lib-1-rc7/src/xine-engine/audio_decoder.c~libxine-cut-memusage
++++ xine-lib-1-rc7/src/xine-engine/audio_decoder.c
+@@ -462,7 +462,7 @@
+
+ num_buffers = stream->xine->config->register_num (stream->xine->config,
+ "audio.num_buffers",
+- 230,
++ 100,
+ _("number of audio buffers"),
+ _("The number of audio buffers (each is 8k in size) "
+ "xine uses in its internal queue. Higher values "
diff --git a/libxine/libxine-opie-1.0.0-rc7/libxine-ffmpeg-enable-arm.patch b/libxine/libxine-opie-1.0.0-rc7/libxine-ffmpeg-enable-arm.patch
index e69de29bb2..f89d2baa4e 100644
--- a/libxine/libxine-opie-1.0.0-rc7/libxine-ffmpeg-enable-arm.patch
+++ b/libxine/libxine-opie-1.0.0-rc7/libxine-ffmpeg-enable-arm.patch
@@ -0,0 +1,20 @@
+
+#
+# Patch managed by http://www.holgerschurig.de/patcher.html
+#
+
+--- xine-lib-1-rc7/src/libffmpeg/libavcodec/armv4l/Makefile.am~libxine-ffmpeg-enable-arm
++++ xine-lib-1-rc7/src/libffmpeg/libavcodec/armv4l/Makefile.am
+@@ -9,9 +9,8 @@
+ libavcodec_armv4l_dummy = libavcodec_armv4l_dummy.c
+ EXTRA_DIST = $(libavcodec_armv4l_src) $(libavcodec_armv4l_dummy)
+
+-#if HAVE_ARMV4L
+-#armv4l_modules = $(libavcodec_armv4l_src)
+-#endif
+-armv4l_modules =
++if HAVE_ARMV4L
++armv4l_modules = $(libavcodec_armv4l_src)
++endif
+
+ libavcodec_armv4l_la_SOURCES = $(armv4l_modules) $(libavcodec_armv4l_dummy)
diff --git a/libxine/libxine-opie-1.0.0-rc7/libxine-libavcodec.patch b/libxine/libxine-opie-1.0.0-rc7/libxine-libavcodec.patch
index e69de29bb2..6dde9913c8 100644
--- a/libxine/libxine-opie-1.0.0-rc7/libxine-libavcodec.patch
+++ b/libxine/libxine-opie-1.0.0-rc7/libxine-libavcodec.patch
@@ -0,0 +1,926 @@
+TO be MERGED!!!
+
+
+#
+# Patch managed by http://www.holgerschurig.de/patcher.html
+#
+
+--- xine-lib-1-rc7/src/libffmpeg/libavcodec/Makefile.am~libxine-libavcodec
++++ xine-lib-1-rc7/src/libffmpeg/libavcodec/Makefile.am
+@@ -9,9 +9,14 @@
+ # we need to compile everything in debug mode, including the encoders,
+ # otherwise we get unresolved symbols, because some unsatisfied function calls
+ # are not optimized away with debug optimization
+-AM_CFLAGS = $(LIBFFMPEG_CFLAGS) `test "$(CFLAGS)" = "$(DEBUG_CFLAGS)" && echo -DCONFIG_ENCODERS`
++#AM_CFLAGS = $(LIBFFMPEG_CFLAGS) `test "$(CFLAGS)" = "$(DEBUG_CFLAGS)" && echo -DCONFIG_ENCODERS`
+ ASFLAGS =
+
++if HAVE_ARMV4L
++AM_CFLAGS = -DARCH_ARMV4L $(LIBFFMPEG_CFLAGS)
++else
++AM_CFLAGS = $(LIBFFMPEG_CFLAGS)
++endif
+ noinst_LTLIBRARIES = libavcodec.la
+
+ libavcodec_la_SOURCES = \
+--- xine-lib-1-rc7/src/libffmpeg/libavcodec/dsputil.h~libxine-libavcodec
++++ xine-lib-1-rc7/src/libffmpeg/libavcodec/dsputil.h
+@@ -45,6 +45,7 @@
+ void ff_fdct248_islow (DCTELEM *data);
+
+ void j_rev_dct (DCTELEM *data);
++void j_rev_dct_ARM ( DCTELEM *data );
+
+ void ff_fdct_mmx(DCTELEM *block);
+ void ff_fdct_mmx2(DCTELEM *block);
+--- xine-lib-1-rc7/src/libffmpeg/libavcodec/mpegvideo.c~libxine-libavcodec
++++ xine-lib-1-rc7/src/libffmpeg/libavcodec/mpegvideo.c
+@@ -218,6 +218,25 @@
+ }
+ #endif //CONFIG_ENCODERS
+
++
++#ifdef ARCH_ARMV4L
++
++static void ff_jref_idct_put_armv4l(UINT8 *dest, int line_size, DCTELEM *block)
++{
++// fprintf(stderr, "src/libffmpeg/libavcodec/mpegvideo.c : ff_jref_idct_put_armv4l utilisant l'iDCT ARMv4l en ASM\n");
++ j_rev_dct_ARM (block);
++ ff_put_pixels_clamped(block, dest, line_size);
++}
++static void ff_jref_idct_add_armv4l(UINT8 *dest, int line_size, DCTELEM *block)
++{
++// fprintf(stderr, "src/libffmpeg/libavcodec/mpegvideo.c : ff_jref_idct_add_armv4l utilisant l'iDCT ARMv4l en ASM\n");
++ j_rev_dct_ARM (block);
++ ff_add_pixels_clamped(block, dest, line_size);
++}
++
++#endif
++
++
+ /* init common dct for both encoder and decoder */
+ int DCT_common_init(MpegEncContext *s)
+ {
+@@ -246,7 +265,11 @@
+ MPV_common_init_mmi(s);
+ #endif
+ #ifdef ARCH_ARMV4L
+- MPV_common_init_armv4l(s);
++/* MPV_common_init_armv4l(s); */
++/* Methode bourrine, mais bon, c'est pour pas trop se prendre la tete a tout changer ce soir, a ameliorer plus tard*/
++ s->idct_put= ff_jref_idct_put_armv4l;
++ s->idct_add= ff_jref_idct_add_armv4l;
++ s->idct_permutation_type= FF_NO_IDCT_PERM;
+ #endif
+ #ifdef ARCH_POWERPC
+ MPV_common_init_ppc(s);
+--- xine-lib-1-rc7/src/libffmpeg/libavcodec/armv4l/jrevdct_arm.S~libxine-libavcodec
++++ xine-lib-1-rc7/src/libffmpeg/libavcodec/armv4l/jrevdct_arm.S
+@@ -1,386 +1,491 @@
+ /*
+- C-like prototype :
+- void j_rev_dct_ARM(DCTBLOCK data)
++ * jrevdct_arm.S
++ * Copyright (C) 2002 Frederic 'dilb' Boulay.
++ * All Rights Reserved.
++ *
++ * Author: Frederic Boulay <dilb@handhelds.org>
++ *
++ * you can redistribute this file and/or modify
++ * it under the terms of the GNU General Public License (version 2)
++ * as published by the Free Software Foundation.
++ *
++ * This file is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ *
++ *
++ * The function defined in this file, is derived from the simple_idct function from
++ * the libavcodec library part of the ffmpeg project.
++ */
+
+- With DCTBLOCK being a pointer to an array of 64 'signed shorts'
+
+- Copyright (c) 2001 Lionel Ulmer (lionel.ulmer@free.fr / bbrox@bbrox.org)
+
+- Permission is hereby granted, free of charge, to any person obtaining a copy
+- of this software and associated documentation files (the "Software"), to deal
+- in the Software without restriction, including without limitation the rights
+- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+- copies of the Software, and to permit persons to whom the Software is
+- furnished to do so, subject to the following conditions:
+
+- The above copyright notice and this permission notice shall be included in
+- all copies or substantial portions of the Software.
++/* useful constants for the algorithm, they are save in __constant_ptr__ at the end of the source code.*/
++#define W1 22725
++#define W2 21407
++#define W3 19266
++#define W4 16383
++#define W5 12873
++#define W6 8867
++#define W7 4520
++#define MASK_MSHW 0xFFFF0000
++
++/* offsets of the constants in the vector*/
++#define offW1 0
++#define offW2 4
++#define offW3 8
++#define offW4 12
++#define offW5 16
++#define offW6 20
++#define offW7 24
++#define offMASK_MSHW 28
++
++#define ROW_SHIFT 11
++#define ROW_SHIFT2MSHW (16-11)
++#define COL_SHIFT 20
++#define ROW_SHIFTED_1 1024 /* 1<< (ROW_SHIFT-1)*/
++#define COL_SHIFTED_1 524288 /* 1<< (COL_SHIFT-1)*/
++
+
+- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+- COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+- IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+- CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+-
+-*/
+-#define FIX_0_298631336 2446
+-#define FIX_0_541196100 4433
+-#define FIX_0_765366865 6270
+-#define FIX_1_175875602 9633
+-#define FIX_1_501321110 12299
+-#define FIX_2_053119869 16819
+-#define FIX_3_072711026 25172
+-#define FIX_M_0_390180644 -3196
+-#define FIX_M_0_899976223 -7373
+-#define FIX_M_1_847759065 -15137
+-#define FIX_M_1_961570560 -16069
+-#define FIX_M_2_562915447 -20995
+-#define FIX_0xFFFF 0xFFFF
+-
+-#define FIX_0_298631336_ID 0
+-#define FIX_0_541196100_ID 4
+-#define FIX_0_765366865_ID 8
+-#define FIX_1_175875602_ID 12
+-#define FIX_1_501321110_ID 16
+-#define FIX_2_053119869_ID 20
+-#define FIX_3_072711026_ID 24
+-#define FIX_M_0_390180644_ID 28
+-#define FIX_M_0_899976223_ID 32
+-#define FIX_M_1_847759065_ID 36
+-#define FIX_M_1_961570560_ID 40
+-#define FIX_M_2_562915447_ID 44
+-#define FIX_0xFFFF_ID 48
+ .text
+ .align
+-
+ .global j_rev_dct_ARM
++
+ j_rev_dct_ARM:
+- stmdb sp!, { r4 - r12, lr } @ all callee saved regs
++simple_idct3:
++simple_idct_ARM:
++ @@ void simple_idct_ARM(int16_t *block)
++ @@ save stack for reg needed (take all of them),
++ @@ R0-R3 are scratch regs, so no need to save them, but R0 contains the pointer to block
++ @@ so it must not be overwritten, if it is not saved!!
++ @@ R12 is another scratch register, so it should not be saved too
++ @@ save all registers
++ stmfd sp!, {r4-r11, r14} @ R14 is also called LR
++ @@ at this point, R0=block, other registers are free.
++ add r14, r0, #112 @ R14=&block[8*7], better start from the last row, and decrease the value until row=0, i.e. R12=block.
++ add r12, pc, #(__constant_ptr__-.-8) @ R12=__constant_ptr__, the vector containing the constants, probably not necessary to reserve a register for it
++ @@ add 2 temporary variables in the stack: R0 and R14
++ sub sp, sp, #8 @ allow 2 local variables
++ str r0, [sp, #0] @ save block in sp[0]
++ @@ stack status
++ @@ sp+4 free
++ @@ sp+0 R0 (block)
+
+- sub sp, sp, #4 @ reserve some space on the stack
+- str r0, [ sp ] @ save the DCT pointer to the stack
+
+- mov lr, r0 @ lr = pointer to the current row
+- mov r12, #8 @ r12 = row-counter
+- add r11, pc, #(const_array-.-8) @ r11 = base pointer to the constants array
+-row_loop:
+- ldrsh r0, [lr, # 0] @ r0 = 'd0'
+- ldrsh r1, [lr, # 8] @ r1 = 'd1'
++ @@ at this point, R0=block, R14=&block[56], R12=__const_ptr_, R1-R11 free
+
+- @ Optimization for row that have all items except the first set to 0
+- @ (this works as the DCTELEMS are always 4-byte aligned)
+- ldr r5, [lr, # 0]
+- ldr r2, [lr, # 4]
+- ldr r3, [lr, # 8]
+- ldr r4, [lr, #12]
+- orr r3, r3, r4
+- orr r3, r3, r2
+- orrs r5, r3, r5
+- beq end_of_row_loop @ nothing to be done as ALL of them are '0'
+- orrs r2, r3, r1
+- beq empty_row
+-
+- ldrsh r2, [lr, # 2] @ r2 = 'd2'
+- ldrsh r4, [lr, # 4] @ r4 = 'd4'
+- ldrsh r6, [lr, # 6] @ r6 = 'd6'
+-
+- ldr r3, [r11, #FIX_0_541196100_ID]
+- add r7, r2, r6
+- ldr r5, [r11, #FIX_M_1_847759065_ID]
+- mul r7, r3, r7 @ r7 = z1
+- ldr r3, [r11, #FIX_0_765366865_ID]
+- mla r6, r5, r6, r7 @ r6 = tmp2
+- add r5, r0, r4 @ r5 = tmp0
+- mla r2, r3, r2, r7 @ r2 = tmp3
+- sub r3, r0, r4 @ r3 = tmp1
+
+- add r0, r2, r5, lsl #13 @ r0 = tmp10
+- rsb r2, r2, r5, lsl #13 @ r2 = tmp13
+- add r4, r6, r3, lsl #13 @ r4 = tmp11
+- rsb r3, r6, r3, lsl #13 @ r3 = tmp12
++__row_loop:
++ @@ read the row and check if it is null, almost null, or not, according to strongarm specs, it is not necessary to optimise ldr accesses (i.e. split 32bits in 2 16bits words), at least it gives more usable registers :)
++ ldr r1, [r14, #0] @ R1=(int32)(R12)[0]=ROWr32[0] (relative row cast to a 32b pointer)
++ ldr r2, [r14, #4] @ R2=(int32)(R12)[1]=ROWr32[1]
++ ldr r3, [r14, #8] @ R3=ROWr32[2]
++ ldr r4, [r14, #12] @ R4=ROWr32[3]
++ @@ check if the words are null, if all of them are null, then proceed with next row (branch __end_row_loop),
++ @@ if ROWr16[0] is the only one not null, then proceed with this special case (branch __almost_empty_row)
++ @@ else follow the complete algorithm.
++ @@ at this point, R0=block, R14=&block[n], R12=__const_ptr_, R1=ROWr32[0], R2=ROWr32[1],
++ @@ R3=ROWr32[2], R4=ROWr32[3], R5-R11 free
++ orr r5, r4, r3 @ R5=R4 | R3
++ orr r5, r5, r2 @ R5=R4 | R3 | R2
++ orrs r6, r5, r1 @ Test R5 | R1 (the aim is to check if everything is null)
++ beq __end_row_loop
++ mov r7, r1, asr #16 @ R7=R1>>16=ROWr16[1] (evaluate it now, as it could be useful later)
++ ldrsh r6, [r14, #0] @ R6=ROWr16[0]
++ orrs r5, r5, r7 @ R5=R4 | R3 | R2 | R7
++ beq __almost_empty_row
+
+- stmdb sp!, { r0, r2, r3, r4 } @ save on the stack tmp10, tmp13, tmp12, tmp11
+-
+- ldrsh r3, [lr, #10] @ r3 = 'd3'
+- ldrsh r5, [lr, #12] @ r5 = 'd5'
+- ldrsh r7, [lr, #14] @ r7 = 'd7'
++__b_evaluation:
++ @@ at this point, R0=block (temp), R1(free), R2=ROWr32[1], R3=ROWr32[2], R4=ROWr32[3],
++ @@ R5=(temp), R6=ROWr16[0], R7=ROWr16[1], R8-R11 free,
++ @@ R12=__const_ptr_, R14=&block[n]
++ @@ to save some registers/calls, proceed with b0-b3 first, followed by a0-a3
+
+- add r0, r3, r5 @ r0 = 'z2'
+- add r2, r1, r7 @ r2 = 'z1'
+- add r4, r3, r7 @ r4 = 'z3'
+- add r6, r1, r5 @ r6 = 'z4'
+- ldr r9, [r11, #FIX_1_175875602_ID]
+- add r8, r4, r6 @ r8 = z3 + z4
+- ldr r10, [r11, #FIX_M_0_899976223_ID]
+- mul r8, r9, r8 @ r8 = 'z5'
+- ldr r9, [r11, #FIX_M_2_562915447_ID]
+- mul r2, r10, r2 @ r2 = 'z1'
+- ldr r10, [r11, #FIX_M_1_961570560_ID]
+- mul r0, r9, r0 @ r0 = 'z2'
+- ldr r9, [r11, #FIX_M_0_390180644_ID]
+- mla r4, r10, r4, r8 @ r4 = 'z3'
+- ldr r10, [r11, #FIX_0_298631336_ID]
+- mla r6, r9, r6, r8 @ r6 = 'z4'
+- ldr r9, [r11, #FIX_2_053119869_ID]
+- mla r7, r10, r7, r2 @ r7 = tmp0 + z1
+- ldr r10, [r11, #FIX_3_072711026_ID]
+- mla r5, r9, r5, r0 @ r5 = tmp1 + z2
+- ldr r9, [r11, #FIX_1_501321110_ID]
+- mla r3, r10, r3, r0 @ r3 = tmp2 + z2
+- add r7, r7, r4 @ r7 = tmp0
+- mla r1, r9, r1, r2 @ r1 = tmp3 + z1
+- add r5, r5, r6 @ r5 = tmp1
+- add r3, r3, r4 @ r3 = tmp2
+- add r1, r1, r6 @ r1 = tmp3
++ @@ MUL16(b0, W1, row[1]);
++ @@ MUL16(b1, W3, row[1]);
++ @@ MUL16(b2, W5, row[1]);
++ @@ MUL16(b3, W7, row[1]);
++ @@ MAC16(b0, W3, row[3]);
++ @@ MAC16(b1, -W7, row[3]);
++ @@ MAC16(b2, -W1, row[3]);
++ @@ MAC16(b3, -W5, row[3]);
++ ldr r8, [r12, #offW1] @ R8=W1
++ mov r2, r2, asr #16 @ R2=ROWr16[3]
++ mul r0, r8, r7 @ R0=W1*ROWr16[1]=b0 (ROWr16[1] must be the second arg, to have the possibility to save 1 cycle)
++ ldr r9, [r12, #offW3] @ R9=W3
++ ldr r10, [r12, #offW5] @ R10=W5
++ mul r1, r9, r7 @ R1=W3*ROWr16[1]=b1 (ROWr16[1] must be the second arg, to have the possibility to save 1 cycle)
++ ldr r11, [r12, #offW7] @ R11=W7
++ mul r5, r10, r7 @ R5=W5*ROWr16[1]=b2 (ROWr16[1] must be the second arg, to have the possibility to save 1 cycle)
++ mul r7, r11, r7 @ R7=W7*ROWr16[1]=b3 (ROWr16[1] must be the second arg, to have the possibility to save 1 cycle)
++ teq r2, #0 @ if null avoid muls
++ mlane r0, r9, r2, r0 @ R0+=W3*ROWr16[3]=b0 (ROWr16[3] must be the second arg, to have the possibility to save 1 cycle)
++ rsbne r2, r2, #0 @ R2=-ROWr16[3]
++ mlane r1, r11, r2, r1 @ R1-=W7*ROWr16[3]=b1 (ROWr16[3] must be the second arg, to have the possibility to save 1 cycle)
++ mlane r5, r8, r2, r5 @ R5-=W1*ROWr16[3]=b2 (ROWr16[3] must be the second arg, to have the possibility to save 1 cycle)
++ mlane r7, r10, r2, r7 @ R7-=W5*ROWr16[3]=b3 (ROWr16[3] must be the second arg, to have the possibility to save 1 cycle)
+
+- ldmia sp!, { r0, r2, r4, r6 } @ r0 = tmp10 / r2 = tmp13 / r4 = tmp12 / r6 = tmp11
+- @ r1 = tmp3 / r3 = tmp2 / r5 = tmp1 / r7 = tmp0
+-
+- @ Compute DESCALE(tmp10 + tmp3, CONST_BITS-PASS1_BITS)
+- add r8, r0, r1
+- add r8, r8, #(1<<10)
+- mov r8, r8, asr #11
+- strh r8, [lr, # 0]
+-
+- @ Compute DESCALE(tmp10 - tmp3, CONST_BITS-PASS1_BITS)
+- sub r8, r0, r1
+- add r8, r8, #(1<<10)
+- mov r8, r8, asr #11
+- strh r8, [lr, #14]
+-
+- @ Compute DESCALE(tmp11 + tmp2, CONST_BITS-PASS1_BITS)
+- add r8, r6, r3
+- add r8, r8, #(1<<10)
+- mov r8, r8, asr #11
+- strh r8, [lr, # 2]
+-
+- @ Compute DESCALE(tmp11 - tmp2, CONST_BITS-PASS1_BITS)
+- sub r8, r6, r3
+- add r8, r8, #(1<<10)
+- mov r8, r8, asr #11
+- strh r8, [lr, #12]
+-
+- @ Compute DESCALE(tmp12 + tmp1, CONST_BITS-PASS1_BITS)
+- add r8, r4, r5
+- add r8, r8, #(1<<10)
+- mov r8, r8, asr #11
+- strh r8, [lr, # 4]
+-
+- @ Compute DESCALE(tmp12 - tmp1, CONST_BITS-PASS1_BITS)
+- sub r8, r4, r5
+- add r8, r8, #(1<<10)
+- mov r8, r8, asr #11
+- strh r8, [lr, #10]
+-
+- @ Compute DESCALE(tmp13 + tmp0, CONST_BITS-PASS1_BITS)
+- add r8, r2, r7
+- add r8, r8, #(1<<10)
+- mov r8, r8, asr #11
+- strh r8, [lr, # 6]
+-
+- @ Compute DESCALE(tmp13 - tmp0, CONST_BITS-PASS1_BITS)
+- sub r8, r2, r7
+- add r8, r8, #(1<<10)
+- mov r8, r8, asr #11
+- strh r8, [lr, # 8]
++ @@ at this point, R0=b0, R1=b1, R2 (free), R3=ROWr32[2], R4=ROWr32[3],
++ @@ R5=b2, R6=ROWr16[0], R7=b3, R8=W1, R9=W3, R10=W5, R11=W7,
++ @@ R12=__const_ptr_, R14=&block[n]
++ @@ temp = ((uint32_t*)row)[2] | ((uint32_t*)row)[3];
++ @@ if (temp != 0) {}
++ orrs r2, r3, r4 @ R2=ROWr32[2] | ROWr32[3]
++ beq __end_b_evaluation
+
+- @ End of row loop
+- add lr, lr, #16
+- subs r12, r12, #1
+- bne row_loop
+- beq start_column_loop
+-
+-empty_row:
+- ldr r1, [r11, #FIX_0xFFFF_ID]
+- mov r0, r0, lsl #2
+- and r0, r0, r1
+- add r0, r0, r0, lsl #16
+- str r0, [lr, # 0]
+- str r0, [lr, # 4]
+- str r0, [lr, # 8]
+- str r0, [lr, #12]
++ @@ at this point, R0=b0, R1=b1, R2 (free), R3=ROWr32[2], R4=ROWr32[3],
++ @@ R5=b2, R6=ROWr16[0], R7=b3, R8=W1, R9=W3, R10=W5, R11=W7,
++ @@ R12=__const_ptr_, R14=&block[n]
++ @@ MAC16(b0, W5, row[5]);
++ @@ MAC16(b2, W7, row[5]);
++ @@ MAC16(b3, W3, row[5]);
++ @@ MAC16(b1, -W1, row[5]);
++ @@ MAC16(b0, W7, row[7]);
++ @@ MAC16(b2, W3, row[7]);
++ @@ MAC16(b3, -W1, row[7]);
++ @@ MAC16(b1, -W5, row[7]);
++ mov r3, r3, asr #16 @ R3=ROWr16[5]
++ teq r3, #0 @ if null avoid muls
++ mlane r0, r10, r3, r0 @ R0+=W5*ROWr16[5]=b0
++ mov r4, r4, asr #16 @ R4=ROWr16[7]
++ mlane r5, r11, r3, r5 @ R5+=W7*ROWr16[5]=b2
++ mlane r7, r9, r3, r7 @ R7+=W3*ROWr16[5]=b3
++ rsbne r3, r3, #0 @ R3=-ROWr16[5]
++ mlane r1, r8, r3, r1 @ R7-=W1*ROWr16[5]=b1
++ @@ R3 is free now
++ teq r4, #0 @ if null avoid muls
++ mlane r0, r11, r4, r0 @ R0+=W7*ROWr16[7]=b0
++ mlane r5, r9, r4, r5 @ R5+=W3*ROWr16[7]=b2
++ rsbne r4, r4, #0 @ R4=-ROWr16[7]
++ mlane r7, r8, r4, r7 @ R7-=W1*ROWr16[7]=b3
++ mlane r1, r10, r4, r1 @ R1-=W5*ROWr16[7]=b1
++ @@ R4 is free now
++__end_b_evaluation:
++ @@ at this point, R0=b0, R1=b1, R2=ROWr32[2] | ROWr32[3] (tmp), R3 (free), R4 (free),
++ @@ R5=b2, R6=ROWr16[0], R7=b3, R8 (free), R9 (free), R10 (free), R11 (free),
++ @@ R12=__const_ptr_, R14=&block[n]
+
+-end_of_row_loop:
+- @ End of loop
+- add lr, lr, #16
+- subs r12, r12, #1
+- bne row_loop
++__a_evaluation:
++ @@ a0 = (W4 * row[0]) + (1 << (ROW_SHIFT - 1));
++ @@ a1 = a0 + W6 * row[2];
++ @@ a2 = a0 - W6 * row[2];
++ @@ a3 = a0 - W2 * row[2];
++ @@ a0 = a0 + W2 * row[2];
++ ldr r9, [r12, #offW4] @ R9=W4
++ mul r6, r9, r6 @ R6=W4*ROWr16[0]
++ ldr r10, [r12, #offW6] @ R10=W6
++ ldrsh r4, [r14, #4] @ R4=ROWr16[2] (a3 not defined yet)
++ add r6, r6, #ROW_SHIFTED_1 @ R6=W4*ROWr16[0] + 1<<(ROW_SHIFT-1) (a0)
+
+-start_column_loop:
+- @ Start of column loop
+- ldr lr, [ sp ]
+- mov r12, #8
+-column_loop:
+- ldrsh r0, [lr, #( 0*8)] @ r0 = 'd0'
+- ldrsh r2, [lr, #( 4*8)] @ r2 = 'd2'
+- ldrsh r4, [lr, #( 8*8)] @ r4 = 'd4'
+- ldrsh r6, [lr, #(12*8)] @ r6 = 'd6'
++ mul r11, r10, r4 @ R11=W6*ROWr16[2]
++ ldr r8, [r12, #offW2] @ R8=W2
++ sub r3, r6, r11 @ R3=a0-W6*ROWr16[2] (a2)
++ @@ temp = ((uint32_t*)row)[2] | ((uint32_t*)row)[3];
++ @@ if (temp != 0) {}
++ teq r2, #0
++ beq __end_bef_a_evaluation
+
+- ldr r3, [r11, #FIX_0_541196100_ID]
+- add r1, r2, r6
+- ldr r5, [r11, #FIX_M_1_847759065_ID]
+- mul r1, r3, r1 @ r1 = z1
+- ldr r3, [r11, #FIX_0_765366865_ID]
+- mla r6, r5, r6, r1 @ r6 = tmp2
+- add r5, r0, r4 @ r5 = tmp0
+- mla r2, r3, r2, r1 @ r2 = tmp3
+- sub r3, r0, r4 @ r3 = tmp1
++ add r2, r6, r11 @ R2=a0+W6*ROWr16[2] (a1)
++ mul r11, r8, r4 @ R11=W2*ROWr16[2]
++ sub r4, r6, r11 @ R4=a0-W2*ROWr16[2] (a3)
++ add r6, r6, r11 @ R6=a0+W2*ROWr16[2] (a0)
+
+- add r0, r2, r5, lsl #13 @ r0 = tmp10
+- rsb r2, r2, r5, lsl #13 @ r2 = tmp13
+- add r4, r6, r3, lsl #13 @ r4 = tmp11
+- rsb r6, r6, r3, lsl #13 @ r6 = tmp12
+
+- ldrsh r1, [lr, #( 2*8)] @ r1 = 'd1'
+- ldrsh r3, [lr, #( 6*8)] @ r3 = 'd3'
+- ldrsh r5, [lr, #(10*8)] @ r5 = 'd5'
+- ldrsh r7, [lr, #(14*8)] @ r7 = 'd7'
++ @@ at this point, R0=b0, R1=b1, R2=a1, R3=a2, R4=a3,
++ @@ R5=b2, R6=a0, R7=b3, R8=W2, R9=W4, R10=W6, R11 (free),
++ @@ R12=__const_ptr_, R14=&block[n]
+
+- @ Check for empty odd column (happens about 20 to 25 % of the time according to my stats)
+- orr r9, r1, r3
+- orr r10, r5, r7
+- orrs r10, r9, r10
+- beq empty_odd_column
+
+- stmdb sp!, { r0, r2, r4, r6 } @ save on the stack tmp10, tmp13, tmp12, tmp11
+-
+- add r0, r3, r5 @ r0 = 'z2'
+- add r2, r1, r7 @ r2 = 'z1'
+- add r4, r3, r7 @ r4 = 'z3'
+- add r6, r1, r5 @ r6 = 'z4'
+- ldr r9, [r11, #FIX_1_175875602_ID]
+- add r8, r4, r6
+- ldr r10, [r11, #FIX_M_0_899976223_ID]
+- mul r8, r9, r8 @ r8 = 'z5'
+- ldr r9, [r11, #FIX_M_2_562915447_ID]
+- mul r2, r10, r2 @ r2 = 'z1'
+- ldr r10, [r11, #FIX_M_1_961570560_ID]
+- mul r0, r9, r0 @ r0 = 'z2'
+- ldr r9, [r11, #FIX_M_0_390180644_ID]
+- mla r4, r10, r4, r8 @ r4 = 'z3'
+- ldr r10, [r11, #FIX_0_298631336_ID]
+- mla r6, r9, r6, r8 @ r6 = 'z4'
+- ldr r9, [r11, #FIX_2_053119869_ID]
+- mla r7, r10, r7, r2 @ r7 = tmp0 + z1
+- ldr r10, [r11, #FIX_3_072711026_ID]
+- mla r5, r9, r5, r0 @ r5 = tmp1 + z2
+- ldr r9, [r11, #FIX_1_501321110_ID]
+- mla r3, r10, r3, r0 @ r3 = tmp2 + z2
+- add r7, r7, r4 @ r7 = tmp0
+- mla r1, r9, r1, r2 @ r1 = tmp3 + z1
+- add r5, r5, r6 @ r5 = tmp1
+- add r3, r3, r4 @ r3 = tmp2
+- add r1, r1, r6 @ r1 = tmp3
+-
+- ldmia sp!, { r0, r2, r4, r6 } @ r0 = tmp10 / r2 = tmp13 / r4 = tmp11 / r6 = tmp12
+- @ r1 = tmp3 / r3 = tmp2 / r5 = tmp1 / r7 = tmp0
++ @@ a0 += W4*row[4]
++ @@ a1 -= W4*row[4]
++ @@ a2 -= W4*row[4]
++ @@ a3 += W4*row[4]
++ ldrsh r11, [r14, #8] @ R11=ROWr16[4]
++ teq r11, #0 @ if null avoid muls
++ mulne r11, r9, r11 @ R11=W4*ROWr16[4]
++ @@ R9 is free now
++ ldrsh r9, [r14, #12] @ R9=ROWr16[6]
++ addne r6, r6, r11 @ R6+=W4*ROWr16[4] (a0)
++ subne r2, r2, r11 @ R2-=W4*ROWr16[4] (a1)
++ subne r3, r3, r11 @ R3-=W4*ROWr16[4] (a2)
++ addne r4, r4, r11 @ R4+=W4*ROWr16[4] (a3)
++ @@ W6 alone is no more useful, save W2*ROWr16[6] in it instead
++ teq r9, #0 @ if null avoid muls
++ mulne r11, r10, r9 @ R11=W6*ROWr16[6]
++ addne r6, r6, r11 @ R6+=W6*ROWr16[6] (a0)
++ mulne r10, r8, r9 @ R10=W2*ROWr16[6]
++ @@ a0 += W6*row[6];
++ @@ a3 -= W6*row[6];
++ @@ a1 -= W2*row[6];
++ @@ a2 += W2*row[6];
++ subne r4, r4, r11 @ R4-=W6*ROWr16[6] (a3)
++ subne r2, r2, r10 @ R2-=W2*ROWr16[6] (a1)
++ addne r3, r3, r10 @ R3+=W2*ROWr16[6] (a2)
+
+- @ Compute DESCALE(tmp10 + tmp3, CONST_BITS+PASS1_BITS+3)
+- add r8, r0, r1
+- add r8, r8, #(1<<17)
+- mov r8, r8, asr #18
+- strh r8, [lr, #( 0*8)]
+-
+- @ Compute DESCALE(tmp10 - tmp3, CONST_BITS+PASS1_BITS+3)
+- sub r8, r0, r1
+- add r8, r8, #(1<<17)
+- mov r8, r8, asr #18
+- strh r8, [lr, #(14*8)]
+-
+- @ Compute DESCALE(tmp11 + tmp2, CONST_BITS+PASS1_BITS+3)
+- add r8, r4, r3
+- add r8, r8, #(1<<17)
+- mov r8, r8, asr #18
+- strh r8, [lr, #( 2*8)]
+-
+- @ Compute DESCALE(tmp11 - tmp2, CONST_BITS+PASS1_BITS+3)
+- sub r8, r4, r3
+- add r8, r8, #(1<<17)
+- mov r8, r8, asr #18
+- strh r8, [lr, #(12*8)]
+-
+- @ Compute DESCALE(tmp12 + tmp1, CONST_BITS+PASS1_BITS+3)
+- add r8, r6, r5
+- add r8, r8, #(1<<17)
+- mov r8, r8, asr #18
+- strh r8, [lr, #( 4*8)]
+-
+- @ Compute DESCALE(tmp12 - tmp1, CONST_BITS+PASS1_BITS+3)
+- sub r8, r6, r5
+- add r8, r8, #(1<<17)
+- mov r8, r8, asr #18
+- strh r8, [lr, #(10*8)]
+-
+- @ Compute DESCALE(tmp13 + tmp0, CONST_BITS+PASS1_BITS+3)
+- add r8, r2, r7
+- add r8, r8, #(1<<17)
+- mov r8, r8, asr #18
+- strh r8, [lr, #( 6*8)]
+-
+- @ Compute DESCALE(tmp13 - tmp0, CONST_BITS+PASS1_BITS+3)
+- sub r8, r2, r7
+- add r8, r8, #(1<<17)
+- mov r8, r8, asr #18
+- strh r8, [lr, #( 8*8)]
++__end_a_evaluation:
++ @@ at this point, R0=b0, R1=b1, R2=a1, R3=a2, R4=a3,
++ @@ R5=b2, R6=a0, R7=b3, R8 (free), R9 (free), R10 (free), R11 (free),
++ @@ R12=__const_ptr_, R14=&block[n]
++ @@ row[0] = (a0 + b0) >> ROW_SHIFT;
++ @@ row[1] = (a1 + b1) >> ROW_SHIFT;
++ @@ row[2] = (a2 + b2) >> ROW_SHIFT;
++ @@ row[3] = (a3 + b3) >> ROW_SHIFT;
++ @@ row[4] = (a3 - b3) >> ROW_SHIFT;
++ @@ row[5] = (a2 - b2) >> ROW_SHIFT;
++ @@ row[6] = (a1 - b1) >> ROW_SHIFT;
++ @@ row[7] = (a0 - b0) >> ROW_SHIFT;
++ add r8, r6, r0 @ R8=a0+b0
++ add r9, r2, r1 @ R9=a1+b1
++ @@ put 2 16 bits half-words in a 32bits word
++ @@ ROWr32[0]=ROWr16[0] | (ROWr16[1]<<16) (only Little Endian compliant then!!!)
++ ldr r10, [r12, #offMASK_MSHW] @ R10=0xFFFF0000
++ and r9, r10, r9, lsl #ROW_SHIFT2MSHW @ R9=0xFFFF0000 & ((a1+b1)<<5)
++ mvn r11, r10 @ R11= NOT R10= 0x0000FFFF
++ and r8, r11, r8, asr #ROW_SHIFT @ R8=0x0000FFFF & ((a0+b0)>>11)
++ orr r8, r8, r9
++ str r8, [r14, #0]
+
+- @ End of row loop
+- add lr, lr, #2
+- subs r12, r12, #1
+- bne column_loop
+- beq the_end
+-
+-empty_odd_column:
+- @ Compute DESCALE(tmp10 + tmp3, CONST_BITS+PASS1_BITS+3)
+- @ Compute DESCALE(tmp10 - tmp3, CONST_BITS+PASS1_BITS+3)
+- add r0, r0, #(1<<17)
+- mov r0, r0, asr #18
+- strh r0, [lr, #( 0*8)]
+- strh r0, [lr, #(14*8)]
+-
+- @ Compute DESCALE(tmp11 + tmp2, CONST_BITS+PASS1_BITS+3)
+- @ Compute DESCALE(tmp11 - tmp2, CONST_BITS+PASS1_BITS+3)
+- add r4, r4, #(1<<17)
+- mov r4, r4, asr #18
+- strh r4, [lr, #( 2*8)]
+- strh r4, [lr, #(12*8)]
+-
+- @ Compute DESCALE(tmp12 + tmp1, CONST_BITS+PASS1_BITS+3)
+- @ Compute DESCALE(tmp12 - tmp1, CONST_BITS+PASS1_BITS+3)
+- add r6, r6, #(1<<17)
+- mov r6, r6, asr #18
+- strh r6, [lr, #( 4*8)]
+- strh r6, [lr, #(10*8)]
+-
+- @ Compute DESCALE(tmp13 + tmp0, CONST_BITS+PASS1_BITS+3)
+- @ Compute DESCALE(tmp13 - tmp0, CONST_BITS+PASS1_BITS+3)
+- add r2, r2, #(1<<17)
+- mov r2, r2, asr #18
+- strh r2, [lr, #( 6*8)]
+- strh r2, [lr, #( 8*8)]
++ add r8, r3, r5 @ R8=a2+b2
++ add r9, r4, r7 @ R9=a3+b3
++ and r9, r10, r9, lsl #ROW_SHIFT2MSHW @ R9=0xFFFF0000 & ((a3+b3)<<5)
++ and r8, r11, r8, asr #ROW_SHIFT @ R8=0x0000FFFF & ((a2+b2)>>11)
++ orr r8, r8, r9
++ str r8, [r14, #4]
+
+- @ End of row loop
+- add lr, lr, #2
+- subs r12, r12, #1
+- bne column_loop
+-
+-the_end:
+- @ The end....
+- add sp, sp, #4
+- ldmia sp!, { r4 - r12, pc } @ restore callee saved regs and return
++ sub r8, r4, r7 @ R8=a3-b3
++ sub r9, r3, r5 @ R9=a2-b2
++ and r9, r10, r9, lsl #ROW_SHIFT2MSHW @ R9=0xFFFF0000 & ((a2-b2)<<5)
++ and r8, r11, r8, asr #ROW_SHIFT @ R8=0x0000FFFF & ((a3-b3)>>11)
++ orr r8, r8, r9
++ str r8, [r14, #8]
+
+-const_array:
++ sub r8, r2, r1 @ R8=a1-b1
++ sub r9, r6, r0 @ R9=a0-b0
++ and r9, r10, r9, lsl #ROW_SHIFT2MSHW @ R9=0xFFFF0000 & ((a0-b0)<<5)
++ and r8, r11, r8, asr #ROW_SHIFT @ R8=0x0000FFFF & ((a1-b1)>>11)
++ orr r8, r8, r9
++ str r8, [r14, #12]
++
++ bal __end_row_loop
++
++__almost_empty_row:
++ @@ the row was empty, except ROWr16[0], now, management of this special case
++ @@ at this point, R0=block, R14=&block[n], R12=__const_ptr_, R1=ROWr32[0], R2=ROWr32[1],
++ @@ R3=ROWr32[2], R4=ROWr32[3], R5=(temp), R6=ROWr16[0], R7=ROWr16[1],
++ @@ R8=0xFFFF (temp), R9-R11 free
++ mov r8, #0x10000 @ R8=0xFFFF (2 steps needed!) it saves a ldr call (because of delay run).
++ sub r8, r8, #1 @ R8 is now ready.
++ and r5, r8, r6, lsl #3 @ R5=R8 & (R6<<3)= (ROWr16[0]<<3) & 0xFFFF
++ orr r5, r5, r5, lsl #16 @ R5=R5 | (R5<<16)
++ str r5, [r14, #0] @ R14[0]=ROWr32[0]=R5
++ str r5, [r14, #4] @ R14[4]=ROWr32[1]=R5
++ str r5, [r14, #8] @ R14[8]=ROWr32[2]=R5
++ str r5, [r14, #12] @ R14[12]=ROWr32[3]=R5
++
++__end_row_loop:
++ @@ at this point, R0-R11 (free)
++ @@ R12=__const_ptr_, R14=&block[n]
++ ldr r0, [sp, #0] @ R0=block
++ teq r0, r14 @ compare current &block[8*n] to block, when block is reached, the loop is finished.
++ sub r14, r14, #16
++ bne __row_loop
++
++
++
++ @@ at this point, R0=block, R1-R11 (free)
++ @@ R12=__const_ptr_, R14=&block[n]
++ add r14, r0, #14 @ R14=&block[7], better start from the last col, and decrease the value until col=0, i.e. R14=block.
++__col_loop:
++
++__b_evaluation2:
++ @@ at this point, R0=block (temp), R1-R11 (free)
++ @@ R12=__const_ptr_, R14=&block[n]
++ @@ proceed with b0-b3 first, followed by a0-a3
++ @@ MUL16(b0, W1, col[8x1]);
++ @@ MUL16(b1, W3, col[8x1]);
++ @@ MUL16(b2, W5, col[8x1]);
++ @@ MUL16(b3, W7, col[8x1]);
++ @@ MAC16(b0, W3, col[8x3]);
++ @@ MAC16(b1, -W7, col[8x3]);
++ @@ MAC16(b2, -W1, col[8x3]);
++ @@ MAC16(b3, -W5, col[8x3]);
++ ldr r8, [r12, #offW1] @ R8=W1
++ ldrsh r7, [r14, #16]
++ mul r0, r8, r7 @ R0=W1*ROWr16[1]=b0 (ROWr16[1] must be the second arg, to have the possibility to save 1 cycle)
++ ldr r9, [r12, #offW3] @ R9=W3
++ ldr r10, [r12, #offW5] @ R10=W5
++ mul r1, r9, r7 @ R1=W3*ROWr16[1]=b1 (ROWr16[1] must be the second arg, to have the possibility to save 1 cycle)
++ ldr r11, [r12, #offW7] @ R11=W7
++ mul r5, r10, r7 @ R5=W5*ROWr16[1]=b2 (ROWr16[1] must be the second arg, to have the possibility to save 1 cycle)
++ ldrsh r2, [r14, #48]
++ mul r7, r11, r7 @ R7=W7*ROWr16[1]=b3 (ROWr16[1] must be the second arg, to have the possibility to save 1 cycle)
++ teq r2, #0 @ if 0, then avoid muls
++ mlane r0, r9, r2, r0 @ R0+=W3*ROWr16[3]=b0 (ROWr16[3] must be the second arg, to have the possibility to save 1 cycle)
++ rsbne r2, r2, #0 @ R2=-ROWr16[3]
++ mlane r1, r11, r2, r1 @ R1-=W7*ROWr16[3]=b1 (ROWr16[3] must be the second arg, to have the possibility to save 1 cycle)
++ mlane r5, r8, r2, r5 @ R5-=W1*ROWr16[3]=b2 (ROWr16[3] must be the second arg, to have the possibility to save 1 cycle)
++ mlane r7, r10, r2, r7 @ R7-=W5*ROWr16[3]=b3 (ROWr16[3] must be the second arg, to have the possibility to save 1 cycle)
++
++ @@ at this point, R0=b0, R1=b1, R2 (free), R3 (free), R4 (free),
++ @@ R5=b2, R6 (free), R7=b3, R8=W1, R9=W3, R10=W5, R11=W7,
++ @@ R12=__const_ptr_, R14=&block[n]
++ @@ MAC16(b0, W5, col[5x8]);
++ @@ MAC16(b2, W7, col[5x8]);
++ @@ MAC16(b3, W3, col[5x8]);
++ @@ MAC16(b1, -W1, col[5x8]);
++ @@ MAC16(b0, W7, col[7x8]);
++ @@ MAC16(b2, W3, col[7x8]);
++ @@ MAC16(b3, -W1, col[7x8]);
++ @@ MAC16(b1, -W5, col[7x8]);
++ ldrsh r3, [r14, #80] @ R3=COLr16[5x8]
++ teq r3, #0 @ if 0 then avoid muls
++ mlane r0, r10, r3, r0 @ R0+=W5*ROWr16[5x8]=b0
++ mlane r5, r11, r3, r5 @ R5+=W7*ROWr16[5x8]=b2
++ mlane r7, r9, r3, r7 @ R7+=W3*ROWr16[5x8]=b3
++ rsbne r3, r3, #0 @ R3=-ROWr16[5x8]
++ ldrsh r4, [r14, #112] @ R4=COLr16[7x8]
++ mlane r1, r8, r3, r1 @ R7-=W1*ROWr16[5x8]=b1
++ @@ R3 is free now
++ teq r4, #0 @ if 0 then avoid muls
++ mlane r0, r11, r4, r0 @ R0+=W7*ROWr16[7x8]=b0
++ mlane r5, r9, r4, r5 @ R5+=W3*ROWr16[7x8]=b2
++ rsbne r4, r4, #0 @ R4=-ROWr16[7x8]
++ mlane r7, r8, r4, r7 @ R7-=W1*ROWr16[7x8]=b3
++ mlane r1, r10, r4, r1 @ R1-=W5*ROWr16[7x8]=b1
++ @@ R4 is free now
++__end_b_evaluation2:
++ @@ at this point, R0=b0, R1=b1, R2 (free), R3 (free), R4 (free),
++ @@ R5=b2, R6 (free), R7=b3, R8 (free), R9 (free), R10 (free), R11 (free),
++ @@ R12=__const_ptr_, R14=&block[n]
++
++__a_evaluation2:
++ @@ a0 = (W4 * col[8x0]) + (1 << (COL_SHIFT - 1));
++ @@ a1 = a0 + W6 * row[2];
++ @@ a2 = a0 - W6 * row[2];
++ @@ a3 = a0 - W2 * row[2];
++ @@ a0 = a0 + W2 * row[2];
++ ldrsh r6, [r14, #0]
++ ldr r9, [r12, #offW4] @ R9=W4
++ mul r6, r9, r6 @ R6=W4*ROWr16[0]
++ ldr r10, [r12, #offW6] @ R10=W6
++ ldrsh r4, [r14, #32] @ R4=ROWr16[2] (a3 not defined yet)
++ add r6, r6, #COL_SHIFTED_1 @ R6=W4*ROWr16[0] + 1<<(COL_SHIFT-1) (a0)
++ mul r11, r10, r4 @ R11=W6*ROWr16[2]
++ ldr r8, [r12, #offW2] @ R8=W2
++ add r2, r6, r11 @ R2=a0+W6*ROWr16[2] (a1)
++ sub r3, r6, r11 @ R3=a0-W6*ROWr16[2] (a2)
++ mul r11, r8, r4 @ R11=W2*ROWr16[2]
++ sub r4, r6, r11 @ R4=a0-W2*ROWr16[2] (a3)
++ add r6, r6, r11 @ R6=a0+W2*ROWr16[2] (a0)
++
++ @@ at this point, R0=b0, R1=b1, R2=a1, R3=a2, R4=a3,
++ @@ R5=b2, R6=a0, R7=b3, R8=W2, R9=W4, R10=W6, R11 (free),
++ @@ R12=__const_ptr_, R14=&block[n]
++ @@ a0 += W4*row[4]
++ @@ a1 -= W4*row[4]
++ @@ a2 -= W4*row[4]
++ @@ a3 += W4*row[4]
++ ldrsh r11, [r14, #64] @ R11=ROWr16[4]
++ teq r11, #0 @ if null avoid muls
++ mulne r11, r9, r11 @ R11=W4*ROWr16[4]
++ @@ R9 is free now
++ addne r6, r6, r11 @ R6+=W4*ROWr16[4] (a0)
++ subne r2, r2, r11 @ R2-=W4*ROWr16[4] (a1)
++ subne r3, r3, r11 @ R3-=W4*ROWr16[4] (a2)
++ ldrsh r9, [r14, #96] @ R9=ROWr16[6]
++ addne r4, r4, r11 @ R4+=W4*ROWr16[4] (a3)
++ @@ W6 alone is no more useful, save W2*ROWr16[6] in it instead
++ teq r9, #0 @ if null avoid muls
++ mulne r11, r10, r9 @ R11=W6*ROWr16[6]
++ addne r6, r6, r11 @ R6+=W6*ROWr16[6] (a0)
++ mulne r10, r8, r9 @ R10=W2*ROWr16[6]
++ @@ a0 += W6*row[6];
++ @@ a3 -= W6*row[6];
++ @@ a1 -= W2*row[6];
++ @@ a2 += W2*row[6];
++ subne r4, r4, r11 @ R4-=W6*ROWr16[6] (a3)
++ subne r2, r2, r10 @ R2-=W2*ROWr16[6] (a1)
++ addne r3, r3, r10 @ R3+=W2*ROWr16[6] (a2)
++__end_a_evaluation2:
++ @@ at this point, R0=b0, R1=b1, R2=a1, R3=a2, R4=a3,
++ @@ R5=b2, R6=a0, R7=b3, R8 (free), R9 (free), R10 (free), R11 (free),
++ @@ R12=__const_ptr_, R14=&block[n]
++ @@ col[0 ] = ((a0 + b0) >> COL_SHIFT);
++ @@ col[8 ] = ((a1 + b1) >> COL_SHIFT);
++ @@ col[16] = ((a2 + b2) >> COL_SHIFT);
++ @@ col[24] = ((a3 + b3) >> COL_SHIFT);
++ @@ col[32] = ((a3 - b3) >> COL_SHIFT);
++ @@ col[40] = ((a2 - b2) >> COL_SHIFT);
++ @@ col[48] = ((a1 - b1) >> COL_SHIFT);
++ @@ col[56] = ((a0 - b0) >> COL_SHIFT);
++ @@@@@ no optimisation here @@@@@
++ add r8, r6, r0 @ R8=a0+b0
++ add r9, r2, r1 @ R9=a1+b1
++ mov r8, r8, asr #COL_SHIFT
++ mov r9, r9, asr #COL_SHIFT
++ strh r8, [r14, #0]
++ strh r9, [r14, #16]
++ add r8, r3, r5 @ R8=a2+b2
++ add r9, r4, r7 @ R9=a3+b3
++ mov r8, r8, asr #COL_SHIFT
++ mov r9, r9, asr #COL_SHIFT
++ strh r8, [r14, #32]
++ strh r9, [r14, #48]
++ sub r8, r4, r7 @ R8=a3-b3
++ sub r9, r3, r5 @ R9=a2-b2
++ mov r8, r8, asr #COL_SHIFT
++ mov r9, r9, asr #COL_SHIFT
++ strh r8, [r14, #64]
++ strh r9, [r14, #80]
++ sub r8, r2, r1 @ R8=a1-b1
++ sub r9, r6, r0 @ R9=a0-b0
++ mov r8, r8, asr #COL_SHIFT
++ mov r9, r9, asr #COL_SHIFT
++ strh r8, [r14, #96]
++ strh r9, [r14, #112]
++
++__end_col_loop:
++ @@ at this point, R0-R11 (free)
++ @@ R12=__const_ptr_, R14=&block[n]
++ ldr r0, [sp, #0] @ R0=block
++ teq r0, r14 @ compare current &block[n] to block, when block is reached, the loop is finished.
++ sub r14, r14, #2
++ bne __col_loop
++
++
++
++
++__end_simple_idct_ARM:
++ @@ restore registers to previous status!
++ add sp, sp, #8 @@ the local variables!
++ ldmfd sp!, {r4-r11, r15} @@ update PC with LR content.
++
++
++
++@@ kind of sub-function, here not to overload the common case.
++__end_bef_a_evaluation:
++ add r2, r6, r11 @ R2=a0+W6*ROWr16[2] (a1)
++ mul r11, r8, r4 @ R11=W2*ROWr16[2]
++ sub r4, r6, r11 @ R4=a0-W2*ROWr16[2] (a3)
++ add r6, r6, r11 @ R6=a0+W2*ROWr16[2] (a0)
++ bal __end_a_evaluation
++
++
++__constant_ptr__: @@ see #defines at the beginning of the source code for values.
+ .align
+- .word FIX_0_298631336
+- .word FIX_0_541196100
+- .word FIX_0_765366865
+- .word FIX_1_175875602
+- .word FIX_1_501321110
+- .word FIX_2_053119869
+- .word FIX_3_072711026
+- .word FIX_M_0_390180644
+- .word FIX_M_0_899976223
+- .word FIX_M_1_847759065
+- .word FIX_M_1_961570560
+- .word FIX_M_2_562915447
+- .word FIX_0xFFFF
++ .word W1
++ .word W2
++ .word W3
++ .word W4
++ .word W5
++ .word W6
++ .word W7
++ .word MASK_MSHW
++
++
diff --git a/libxine/libxine-opie-1.0.0-rc7/libxine-libvorbis.patch b/libxine/libxine-opie-1.0.0-rc7/libxine-libvorbis.patch
index e69de29bb2..159f3f9eb2 100644
--- a/libxine/libxine-opie-1.0.0-rc7/libxine-libvorbis.patch
+++ b/libxine/libxine-opie-1.0.0-rc7/libxine-libvorbis.patch
@@ -0,0 +1,37 @@
+
+#
+# Patch managed by http://www.holgerschurig.de/patcher.html
+#
+
+--- xine-lib-1-rc7/src/libvorbis/xine_decoder.c~libxine-libvorbis.patch
++++ xine-lib-1-rc7/src/libvorbis/xine_decoder.c
+@@ -40,7 +40,7 @@
+ #include "buffer.h"
+
+ #include <ogg/ogg.h>
+-#include <vorbis/codec.h>
++#include <tremor/ivorbiscodec.h>
+
+ #define MAX_NUM_SAMPLES 4096
+
+@@ -215,7 +215,7 @@
+
+ } else if (this->output_open) {
+
+- float **pcm;
++ int **pcm;
+ int samples;
+
+ if(vorbis_synthesis(&this->vb,&this->op,1)==0)
+@@ -244,9 +244,9 @@
+ interleave */
+ for(i=0;i<this->vi.channels;i++){
+ ogg_int16_t *ptr=audio_buffer->mem+i;
+- float *mono=pcm[i];
++ int *mono=pcm[i];
+ for(j=0;j<bout;j++){
+- int val=mono[j]*32767.f;
++ int val=mono[j]>>9;
+ /* might as well guard against clipping */
+ if(val>32767){
+ val=32767;
diff --git a/libxine/libxine-opie-1.0.0-rc7/libxine-tremor-autoconf.patch b/libxine/libxine-opie-1.0.0-rc7/libxine-tremor-autoconf.patch
index e69de29bb2..bbedc81de3 100644
--- a/libxine/libxine-opie-1.0.0-rc7/libxine-tremor-autoconf.patch
+++ b/libxine/libxine-opie-1.0.0-rc7/libxine-tremor-autoconf.patch
@@ -0,0 +1,16 @@
+
+#
+# Patch managed by http://www.holgerschurig.de/patcher.html
+#
+
+--- xine-lib-1-rc7/m4/vorbis.m4~libxine-tremor-autoconf
++++ xine-lib-1-rc7/m4/vorbis.m4
+@@ -18,7 +18,7 @@
+ VORBIS_LIBDIR="-L$vorbis_prefix/$XINE_LIBNAME"
+ fi
+
+- VORBIS_LIBS="$VORBIS_LIBDIR -lvorbis -lm"
++ VORBIS_LIBS="$VORBIS_LIBDIR -lvorbisidec -lm"
+ VORBISFILE_LIBS="-lvorbisfile"
+ VORBISENC_LIBS="-lvorbisenc"
+
diff --git a/libxine/libxine-opie-1.0.0-rc7/mpegvideo-static-inlinine.patch b/libxine/libxine-opie-1.0.0-rc7/mpegvideo-static-inlinine.patch
index e69de29bb2..81160b1c81 100644
--- a/libxine/libxine-opie-1.0.0-rc7/mpegvideo-static-inlinine.patch
+++ b/libxine/libxine-opie-1.0.0-rc7/mpegvideo-static-inlinine.patch
@@ -0,0 +1,16 @@
+
+#
+# Patch managed by http://www.holgerschurig.de/patcher.html
+#
+
+--- xine-lib-1-rc7/src/libffmpeg/libavcodec/mpegvideo.c~mpegvideo-static-inlininf.patch
++++ xine-lib-1-rc7/src/libffmpeg/libavcodec/mpegvideo.c
+@@ -2757,7 +2757,7 @@
+ }
+ }
+
+-inline int ff_h263_round_chroma(int x){
++static inline int ff_h263_round_chroma(int x){
+ if (x >= 0)
+ return (h263_chroma_roundtab[x & 0xf] + ((x >> 3) & ~1));
+ else {
diff --git a/libxrender/files/autofoo.patch b/libxrender/files/autofoo.patch
index e69de29bb2..eb21c766f8 100644
--- a/libxrender/files/autofoo.patch
+++ b/libxrender/files/autofoo.patch
@@ -0,0 +1,20 @@
+
+#
+# Patch managed by http://www.holgerschurig.de/patcher.html
+#
+
+--- libXrender-0.8.4/configure.ac~autofoo
++++ libXrender-0.8.4/configure.ac
+@@ -33,11 +33,10 @@
+ dnl protocol, so Xrender version l.n.m corresponds to protocol version l.n
+ dnl
+ AC_INIT(libXrender, 0.8.4, [keithp@keithp.com], libXrender)
++AC_CONFIG_AUX_DIR(.)
+ AM_INIT_AUTOMAKE([dist-bzip2])
+ AM_MAINTAINER_MODE
+-
+ AM_CONFIG_HEADER(config.h)
+-AC_CONFIG_AUX_DIR(.)
+
+ # Check for progs
+ AC_PROG_CC
diff --git a/linux/linux-mtx-1-2.4.24/01-mtd-2004-01-27.diff b/linux/linux-mtx-1-2.4.24/01-mtd-2004-01-27.diff
index e69de29bb2..1a31cb79fb 100644
--- a/linux/linux-mtx-1-2.4.24/01-mtd-2004-01-27.diff
+++ b/linux/linux-mtx-1-2.4.24/01-mtd-2004-01-27.diff
@@ -0,0 +1,51462 @@
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/Config.in linux/drivers/mtd/Config.in
+--- linux-mips-2.4.24-pre2/drivers/mtd/Config.in 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/Config.in 2004-11-17 18:17:58.809348880 +0100
+@@ -1,5 +1,5 @@
+
+-# $Id$
++# $Id$
+
+ mainmenu_option next_comment
+ comment 'Memory Technology Devices (MTD)'
+@@ -30,6 +30,7 @@
+ if [ "$CONFIG_NFTL" = "y" -o "$CONFIG_NFTL" = "m" ]; then
+ bool ' Write support for NFTL (BETA)' CONFIG_NFTL_RW
+ fi
++ dep_tristate ' INFTL (Inverse NAND Flash Translation Layer) support' CONFIG_INFTL $CONFIG_MTD
+
+ source drivers/mtd/chips/Config.in
+
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/Makefile linux/drivers/mtd/Makefile
+--- linux-mips-2.4.24-pre2/drivers/mtd/Makefile 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/Makefile 2004-11-17 18:17:58.826346296 +0100
+@@ -1,30 +1,7 @@
+ #
+ # Makefile for the memory technology device drivers.
+ #
+-# Note! Dependencies are done automagically by 'make dep', which also
+-# removes any old dependencies. DON'T put your own dependencies here
+-# unless it's something special (ie not a .c file).
+-#
+-# Note 2! The CFLAGS definitions are now inherited from the
+-# parent makes..
+-#
+-# $Id$
+-
+-
+-obj-y += chips/chipslink.o maps/mapslink.o \
+- devices/devlink.o nand/nandlink.o
+-obj-m :=
+-obj-n :=
+-obj- :=
+-
+-O_TARGET := mtdlink.o
+-
+-export-objs := mtdcore.o mtdpart.o redboot.o cmdlinepart.o afs.o mtdconcat.o
+-list-multi := nftl.o
+-
+-mod-subdirs :=
+-subdir-y := chips maps devices nand
+-subdir-m := $(subdir-y)
++# $Id$
+
+ # *** BIG UGLY NOTE ***
+ #
+@@ -52,15 +29,44 @@
+
+ # 'Users' - code which presents functionality to userspace.
+ obj-$(CONFIG_MTD_CHAR) += mtdchar.o
+-obj-$(CONFIG_MTD_BLOCK) += mtdblock.o
+-obj-$(CONFIG_MTD_BLOCK_RO) += mtdblock_ro.o
+-obj-$(CONFIG_FTL) += ftl.o
+-obj-$(CONFIG_NFTL) += nftl.o
++obj-$(CONFIG_MTD_BLOCK) += mtdblock.o mtd_blkdevs.o
++obj-$(CONFIG_MTD_BLOCK_RO) += mtdblock_ro.o mtd_blkdevs.o
++obj-$(CONFIG_FTL) += ftl.o mtd_blkdevs.o
++obj-$(CONFIG_NFTL) += nftl.o mtd_blkdevs.o
++obj-$(CONFIG_INFTL) += inftl.o mtd_blkdevs.o
+
+ nftl-objs := nftlcore.o nftlmount.o
++inftl-objs := inftlcore.o inftlmount.o
++
++ifeq ($(PATCHLEVEL),4)
++
++export-objs := mtdcore.o mtdpart.o redboot.o cmdlinepart.o afs.o \
++ mtdconcat.o mtd_blkdevs-24.o
++
++mtd_blkdevs-objs := mtd_blkdevs-24.o
++
++obj-y += chips/chipslink.o maps/mapslink.o \
++ devices/devlink.o nand/nandlink.o
++
++O_TARGET := mtdlink.o
++
++list-multi := nftl.o inftl.o mtd_blkdevs.o
++
++mod-subdirs :=
++subdir-y := chips maps devices nand
++subdir-m := $(subdir-y)
+
+ include $(TOPDIR)/Rules.make
+
+ nftl.o: $(nftl-objs)
+ $(LD) -r -o $@ $(nftl-objs)
+
++inftl.o: $(inftl-objs)
++ $(LD) -r -o $@ $(inftl-objs)
++
++mtd_blkdevs.o: $(mtd_blkdevs-objs)
++ $(LD) -r -o $@ $(mtd_blkdevs-objs)
++
++else
++obj-y += chips/ maps/ devices/ nand/
++endif
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/afs.c linux/drivers/mtd/afs.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/afs.c 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/afs.c 2004-11-17 18:17:58.827346144 +0100
+@@ -21,7 +21,7 @@
+ This is access code for flashes using ARM's flash partitioning
+ standards.
+
+- $Id$
++ $Id$
+
+ ======================================================================*/
+
+@@ -76,17 +76,19 @@
+ return ret;
+ }
+
++ ret = 1;
++
+ /*
+ * Does it contain the magic number?
+ */
+ if (fs.signature != 0xa0ffff9f)
+- ret = 1;
++ ret = 0;
+
+ /*
+ * Don't touch the SIB.
+ */
+ if (fs.type == 2)
+- ret = 1;
++ ret = 0;
+
+ *iis_start = fs.image_info_base & mask;
+ *img_start = fs.image_start & mask;
+@@ -96,14 +98,14 @@
+ * be located after the footer structure.
+ */
+ if (*iis_start >= ptr)
+- ret = 1;
++ ret = 0;
+
+ /*
+ * Check the start of this image. The image
+ * data can not be located after this block.
+ */
+ if (*img_start > off)
+- ret = 1;
++ ret = 0;
+
+ return ret;
+ }
+@@ -125,7 +127,9 @@
+ return ret;
+ }
+
+-int parse_afs_partitions(struct mtd_info *mtd, struct mtd_partition **pparts)
++static int parse_afs_partitions(struct mtd_info *mtd,
++ struct mtd_partition **pparts,
++ unsigned long origin)
+ {
+ struct mtd_partition *parts;
+ u_int mask, off, idx, sz;
+@@ -150,7 +154,7 @@
+ ret = afs_read_footer(mtd, &img_ptr, &iis_ptr, off, mask);
+ if (ret < 0)
+ break;
+- if (ret == 1)
++ if (ret == 0)
+ continue;
+
+ ret = afs_read_iis(mtd, &iis, iis_ptr);
+@@ -183,7 +187,7 @@
+ ret = afs_read_footer(mtd, &img_ptr, &iis_ptr, off, mask);
+ if (ret < 0)
+ break;
+- if (ret == 1)
++ if (ret == 0)
+ continue;
+
+ /* Read the image info block */
+@@ -227,7 +231,25 @@
+ return idx ? idx : ret;
+ }
+
+-EXPORT_SYMBOL(parse_afs_partitions);
++static struct mtd_part_parser afs_parser = {
++ .owner = THIS_MODULE,
++ .parse_fn = parse_afs_partitions,
++ .name = "afs",
++};
++
++static int __init afs_parser_init(void)
++{
++ return register_mtd_parser(&afs_parser);
++}
++
++static void __exit afs_parser_exit(void)
++{
++ deregister_mtd_parser(&afs_parser);
++}
++
++module_init(afs_parser_init);
++module_exit(afs_parser_exit);
++
+
+ MODULE_AUTHOR("ARM Ltd");
+ MODULE_DESCRIPTION("ARM Firmware Suite partition parser");
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/chips/Config.in linux/drivers/mtd/chips/Config.in
+--- linux-mips-2.4.24-pre2/drivers/mtd/chips/Config.in 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/chips/Config.in 2004-11-17 18:17:58.905334288 +0100
+@@ -1,6 +1,6 @@
+ # drivers/mtd/chips/Config.in
+
+-# $Id$
++# $Id$
+
+ mainmenu_option next_comment
+
+@@ -11,13 +11,12 @@
+
+ if [ "$CONFIG_MTD_CFI" = "y" -o "$CONFIG_MTD_JEDECPROBE" = "y" ]; then
+ define_bool CONFIG_MTD_GEN_PROBE y
+-else
+- if [ "$CONFIG_MTD_CFI" = "m" -o "$CONFIG_MTD_JEDECPROBE" = "m" ]; then
++elif [ "$CONFIG_MTD_CFI" = "m" -o "$CONFIG_MTD_JEDECPROBE" = "m" ]; then
+ define_bool CONFIG_MTD_GEN_PROBE m
+- else
++else
+ define_bool CONFIG_MTD_GEN_PROBE n
+- fi
+ fi
++
+ if [ "$CONFIG_MTD_GEN_PROBE" = "y" -o "$CONFIG_MTD_GEN_PROBE" = "m" ]; then
+ bool ' Flash chip driver advanced configuration options' CONFIG_MTD_CFI_ADV_OPTIONS
+ if [ "$CONFIG_MTD_CFI_ADV_OPTIONS" = "y" ]; then
+@@ -44,8 +43,27 @@
+ fi
+ dep_tristate ' Support for Intel/Sharp flash chips' CONFIG_MTD_CFI_INTELEXT $CONFIG_MTD_GEN_PROBE
+ dep_tristate ' Support for AMD/Fujitsu flash chips' CONFIG_MTD_CFI_AMDSTD $CONFIG_MTD_GEN_PROBE
++if [ "$CONFIG_MTD_CFI_AMDSTD" = "y" -o "$CONFIG_MTD_CFI_AMDSTD" = "m" ]; then
++ bool ' Retry failed commands (erase/program)' CONFIG_MTD_CFI_AMDSTD_RETRY n
++ if [ "$CONFIG_MTD_CFI_AMDSTD_RETRY" = "y" ]; then
++ int ' Max retries of failed commands (erase/program)' CONFIG_MTD_CFI_AMDSTD_RETRY_MAX 0
++ fi
++fi
++
+ dep_tristate ' Support for ST (Advanced Architecture) flash chips' CONFIG_MTD_CFI_STAA $CONFIG_MTD_GEN_PROBE
+
++if [ "$CONFIG_MTD_CFI_INTELEXT" = "y" \
++ -o "$CONFIG_MTD_CFI_AMDSTD" = "y" \
++ -o "$CONFIG_MTD_CFI_STAA" = "y" ]; then
++ define_bool CONFIG_MTD_CFI_UTIL y
++elif [ "$CONFIG_MTD_CFI_INTELEXT" = "m" \
++ -o "$CONFIG_MTD_CFI_AMDSTD" = "m" \
++ -o "$CONFIG_MTD_CFI_STAA" = "m" ]; then
++ define_bool CONFIG_MTD_CFI_UTIL m
++else
++ define_bool CONFIG_MTD_CFI_UTIL n
++fi
++
+ dep_tristate ' Support for RAM chips in bus mapping' CONFIG_MTD_RAM $CONFIG_MTD
+ dep_tristate ' Support for ROM chips in bus mapping' CONFIG_MTD_ROM $CONFIG_MTD
+ dep_tristate ' Support for absent chips in bus mapping' CONFIG_MTD_ABSENT $CONFIG_MTD
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/chips/Makefile linux/drivers/mtd/chips/Makefile
+--- linux-mips-2.4.24-pre2/drivers/mtd/chips/Makefile 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/chips/Makefile 2004-11-17 18:17:58.907333984 +0100
+@@ -1,11 +1,12 @@
+ #
+ # linux/drivers/chips/Makefile
+ #
+-# $Id$
++# $Id$
+
++ifeq ($(PATCHLEVEL),4)
+ O_TARGET := chipslink.o
+-
+-export-objs := chipreg.o gen_probe.o
++export-objs := chipreg.o gen_probe.o cfi_util.o
++endif
+
+ # *** BIG UGLY NOTE ***
+ #
+@@ -17,6 +18,7 @@
+ obj-$(CONFIG_MTD) += chipreg.o
+ obj-$(CONFIG_MTD_AMDSTD) += amd_flash.o
+ obj-$(CONFIG_MTD_CFI) += cfi_probe.o
++obj-$(CONFIG_MTD_CFI_UTIL) += cfi_util.o
+ obj-$(CONFIG_MTD_CFI_STAA) += cfi_cmdset_0020.o
+ obj-$(CONFIG_MTD_CFI_AMDSTD) += cfi_cmdset_0002.o
+ obj-$(CONFIG_MTD_CFI_INTELEXT) += cfi_cmdset_0001.o
+@@ -28,4 +30,4 @@
+ obj-$(CONFIG_MTD_SHARP) += sharp.o
+ obj-$(CONFIG_MTD_ABSENT) += map_absent.o
+
+-include $(TOPDIR)/Rules.make
++-include $(TOPDIR)/Rules.make
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/chips/amd_flash.c linux/drivers/mtd/chips/amd_flash.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/chips/amd_flash.c 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/chips/amd_flash.c 2004-11-17 18:17:58.909333680 +0100
+@@ -3,7 +3,7 @@
+ *
+ * Author: Jonas Holmberg <jonas.holmberg@axis.com>
+ *
+- * $Id$
++ * $Id$
+ *
+ * Copyright (c) 2001 Axis Communications AB
+ *
+@@ -19,6 +19,7 @@
+ #include <linux/slab.h>
+ #include <linux/delay.h>
+ #include <linux/interrupt.h>
++#include <linux/init.h>
+ #include <linux/mtd/map.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/flashchip.h>
+@@ -125,10 +126,10 @@
+
+
+ static struct mtd_chip_driver amd_flash_chipdrv = {
+- probe: amd_flash_probe,
+- destroy: amd_flash_destroy,
+- name: "amd_flash",
+- module: THIS_MODULE
++ .probe = amd_flash_probe,
++ .destroy = amd_flash_destroy,
++ .name = "amd_flash",
++ .module = THIS_MODULE
+ };
+
+
+@@ -140,11 +141,11 @@
+ static inline __u32 wide_read(struct map_info *map, __u32 addr)
+ {
+ if (map->buswidth == 1) {
+- return map->read8(map, addr);
++ return map_read8(map, addr);
+ } else if (map->buswidth == 2) {
+- return map->read16(map, addr);
++ return map_read16(map, addr);
+ } else if (map->buswidth == 4) {
+- return map->read32(map, addr);
++ return map_read32(map, addr);
+ }
+
+ return 0;
+@@ -153,11 +154,11 @@
+ static inline void wide_write(struct map_info *map, __u32 val, __u32 addr)
+ {
+ if (map->buswidth == 1) {
+- map->write8(map, val, addr);
++ map_write8(map, val, addr);
+ } else if (map->buswidth == 2) {
+- map->write16(map, val, addr);
++ map_write16(map, val, addr);
+ } else if (map->buswidth == 4) {
+- map->write32(map, val, addr);
++ map_write32(map, val, addr);
+ }
+ }
+
+@@ -424,231 +425,228 @@
+
+ static struct mtd_info *amd_flash_probe(struct map_info *map)
+ {
+- /* Keep this table on the stack so that it gets deallocated after the
+- * probe is done.
+- */
+- const struct amd_flash_info table[] = {
++ static const struct amd_flash_info table[] = {
+ {
+- mfr_id: MANUFACTURER_AMD,
+- dev_id: AM29LV160DT,
+- name: "AMD AM29LV160DT",
+- size: 0x00200000,
+- numeraseregions: 4,
+- regions: {
+- { offset: 0x000000, erasesize: 0x10000, numblocks: 31 },
+- { offset: 0x1F0000, erasesize: 0x08000, numblocks: 1 },
+- { offset: 0x1F8000, erasesize: 0x02000, numblocks: 2 },
+- { offset: 0x1FC000, erasesize: 0x04000, numblocks: 1 }
++ .mfr_id = MANUFACTURER_AMD,
++ .dev_id = AM29LV160DT,
++ .name = "AMD AM29LV160DT",
++ .size = 0x00200000,
++ .numeraseregions = 4,
++ .regions = {
++ { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 31 },
++ { .offset = 0x1F0000, .erasesize = 0x08000, .numblocks = 1 },
++ { .offset = 0x1F8000, .erasesize = 0x02000, .numblocks = 2 },
++ { .offset = 0x1FC000, .erasesize = 0x04000, .numblocks = 1 }
+ }
+ }, {
+- mfr_id: MANUFACTURER_AMD,
+- dev_id: AM29LV160DB,
+- name: "AMD AM29LV160DB",
+- size: 0x00200000,
+- numeraseregions: 4,
+- regions: {
+- { offset: 0x000000, erasesize: 0x04000, numblocks: 1 },
+- { offset: 0x004000, erasesize: 0x02000, numblocks: 2 },
+- { offset: 0x008000, erasesize: 0x08000, numblocks: 1 },
+- { offset: 0x010000, erasesize: 0x10000, numblocks: 31 }
++ .mfr_id = MANUFACTURER_AMD,
++ .dev_id = AM29LV160DB,
++ .name = "AMD AM29LV160DB",
++ .size = 0x00200000,
++ .numeraseregions = 4,
++ .regions = {
++ { .offset = 0x000000, .erasesize = 0x04000, .numblocks = 1 },
++ { .offset = 0x004000, .erasesize = 0x02000, .numblocks = 2 },
++ { .offset = 0x008000, .erasesize = 0x08000, .numblocks = 1 },
++ { .offset = 0x010000, .erasesize = 0x10000, .numblocks = 31 }
+ }
+ }, {
+- mfr_id: MANUFACTURER_TOSHIBA,
+- dev_id: TC58FVT160,
+- name: "Toshiba TC58FVT160",
+- size: 0x00200000,
+- numeraseregions: 4,
+- regions: {
+- { offset: 0x000000, erasesize: 0x10000, numblocks: 31 },
+- { offset: 0x1F0000, erasesize: 0x08000, numblocks: 1 },
+- { offset: 0x1F8000, erasesize: 0x02000, numblocks: 2 },
+- { offset: 0x1FC000, erasesize: 0x04000, numblocks: 1 }
++ .mfr_id = MANUFACTURER_TOSHIBA,
++ .dev_id = TC58FVT160,
++ .name = "Toshiba TC58FVT160",
++ .size = 0x00200000,
++ .numeraseregions = 4,
++ .regions = {
++ { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 31 },
++ { .offset = 0x1F0000, .erasesize = 0x08000, .numblocks = 1 },
++ { .offset = 0x1F8000, .erasesize = 0x02000, .numblocks = 2 },
++ { .offset = 0x1FC000, .erasesize = 0x04000, .numblocks = 1 }
+ }
+ }, {
+- mfr_id: MANUFACTURER_FUJITSU,
+- dev_id: MBM29LV160TE,
+- name: "Fujitsu MBM29LV160TE",
+- size: 0x00200000,
+- numeraseregions: 4,
+- regions: {
+- { offset: 0x000000, erasesize: 0x10000, numblocks: 31 },
+- { offset: 0x1F0000, erasesize: 0x08000, numblocks: 1 },
+- { offset: 0x1F8000, erasesize: 0x02000, numblocks: 2 },
+- { offset: 0x1FC000, erasesize: 0x04000, numblocks: 1 }
++ .mfr_id = MANUFACTURER_FUJITSU,
++ .dev_id = MBM29LV160TE,
++ .name = "Fujitsu MBM29LV160TE",
++ .size = 0x00200000,
++ .numeraseregions = 4,
++ .regions = {
++ { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 31 },
++ { .offset = 0x1F0000, .erasesize = 0x08000, .numblocks = 1 },
++ { .offset = 0x1F8000, .erasesize = 0x02000, .numblocks = 2 },
++ { .offset = 0x1FC000, .erasesize = 0x04000, .numblocks = 1 }
+ }
+ }, {
+- mfr_id: MANUFACTURER_TOSHIBA,
+- dev_id: TC58FVB160,
+- name: "Toshiba TC58FVB160",
+- size: 0x00200000,
+- numeraseregions: 4,
+- regions: {
+- { offset: 0x000000, erasesize: 0x04000, numblocks: 1 },
+- { offset: 0x004000, erasesize: 0x02000, numblocks: 2 },
+- { offset: 0x008000, erasesize: 0x08000, numblocks: 1 },
+- { offset: 0x010000, erasesize: 0x10000, numblocks: 31 }
++ .mfr_id = MANUFACTURER_TOSHIBA,
++ .dev_id = TC58FVB160,
++ .name = "Toshiba TC58FVB160",
++ .size = 0x00200000,
++ .numeraseregions = 4,
++ .regions = {
++ { .offset = 0x000000, .erasesize = 0x04000, .numblocks = 1 },
++ { .offset = 0x004000, .erasesize = 0x02000, .numblocks = 2 },
++ { .offset = 0x008000, .erasesize = 0x08000, .numblocks = 1 },
++ { .offset = 0x010000, .erasesize = 0x10000, .numblocks = 31 }
+ }
+ }, {
+- mfr_id: MANUFACTURER_FUJITSU,
+- dev_id: MBM29LV160BE,
+- name: "Fujitsu MBM29LV160BE",
+- size: 0x00200000,
+- numeraseregions: 4,
+- regions: {
+- { offset: 0x000000, erasesize: 0x04000, numblocks: 1 },
+- { offset: 0x004000, erasesize: 0x02000, numblocks: 2 },
+- { offset: 0x008000, erasesize: 0x08000, numblocks: 1 },
+- { offset: 0x010000, erasesize: 0x10000, numblocks: 31 }
++ .mfr_id = MANUFACTURER_FUJITSU,
++ .dev_id = MBM29LV160BE,
++ .name = "Fujitsu MBM29LV160BE",
++ .size = 0x00200000,
++ .numeraseregions = 4,
++ .regions = {
++ { .offset = 0x000000, .erasesize = 0x04000, .numblocks = 1 },
++ { .offset = 0x004000, .erasesize = 0x02000, .numblocks = 2 },
++ { .offset = 0x008000, .erasesize = 0x08000, .numblocks = 1 },
++ { .offset = 0x010000, .erasesize = 0x10000, .numblocks = 31 }
+ }
+ }, {
+- mfr_id: MANUFACTURER_AMD,
+- dev_id: AM29LV800BB,
+- name: "AMD AM29LV800BB",
+- size: 0x00100000,
+- numeraseregions: 4,
+- regions: {
+- { offset: 0x000000, erasesize: 0x04000, numblocks: 1 },
+- { offset: 0x004000, erasesize: 0x02000, numblocks: 2 },
+- { offset: 0x008000, erasesize: 0x08000, numblocks: 1 },
+- { offset: 0x010000, erasesize: 0x10000, numblocks: 15 }
++ .mfr_id = MANUFACTURER_AMD,
++ .dev_id = AM29LV800BB,
++ .name = "AMD AM29LV800BB",
++ .size = 0x00100000,
++ .numeraseregions = 4,
++ .regions = {
++ { .offset = 0x000000, .erasesize = 0x04000, .numblocks = 1 },
++ { .offset = 0x004000, .erasesize = 0x02000, .numblocks = 2 },
++ { .offset = 0x008000, .erasesize = 0x08000, .numblocks = 1 },
++ { .offset = 0x010000, .erasesize = 0x10000, .numblocks = 15 }
+ }
+ }, {
+- mfr_id: MANUFACTURER_AMD,
+- dev_id: AM29F800BB,
+- name: "AMD AM29F800BB",
+- size: 0x00100000,
+- numeraseregions: 4,
+- regions: {
+- { offset: 0x000000, erasesize: 0x04000, numblocks: 1 },
+- { offset: 0x004000, erasesize: 0x02000, numblocks: 2 },
+- { offset: 0x008000, erasesize: 0x08000, numblocks: 1 },
+- { offset: 0x010000, erasesize: 0x10000, numblocks: 15 }
++ .mfr_id = MANUFACTURER_AMD,
++ .dev_id = AM29F800BB,
++ .name = "AMD AM29F800BB",
++ .size = 0x00100000,
++ .numeraseregions = 4,
++ .regions = {
++ { .offset = 0x000000, .erasesize = 0x04000, .numblocks = 1 },
++ { .offset = 0x004000, .erasesize = 0x02000, .numblocks = 2 },
++ { .offset = 0x008000, .erasesize = 0x08000, .numblocks = 1 },
++ { .offset = 0x010000, .erasesize = 0x10000, .numblocks = 15 }
+ }
+ }, {
+- mfr_id: MANUFACTURER_AMD,
+- dev_id: AM29LV800BT,
+- name: "AMD AM29LV800BT",
+- size: 0x00100000,
+- numeraseregions: 4,
+- regions: {
+- { offset: 0x000000, erasesize: 0x10000, numblocks: 15 },
+- { offset: 0x0F0000, erasesize: 0x08000, numblocks: 1 },
+- { offset: 0x0F8000, erasesize: 0x02000, numblocks: 2 },
+- { offset: 0x0FC000, erasesize: 0x04000, numblocks: 1 }
++ .mfr_id = MANUFACTURER_AMD,
++ .dev_id = AM29LV800BT,
++ .name = "AMD AM29LV800BT",
++ .size = 0x00100000,
++ .numeraseregions = 4,
++ .regions = {
++ { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 15 },
++ { .offset = 0x0F0000, .erasesize = 0x08000, .numblocks = 1 },
++ { .offset = 0x0F8000, .erasesize = 0x02000, .numblocks = 2 },
++ { .offset = 0x0FC000, .erasesize = 0x04000, .numblocks = 1 }
+ }
+ }, {
+- mfr_id: MANUFACTURER_AMD,
+- dev_id: AM29F800BT,
+- name: "AMD AM29F800BT",
+- size: 0x00100000,
+- numeraseregions: 4,
+- regions: {
+- { offset: 0x000000, erasesize: 0x10000, numblocks: 15 },
+- { offset: 0x0F0000, erasesize: 0x08000, numblocks: 1 },
+- { offset: 0x0F8000, erasesize: 0x02000, numblocks: 2 },
+- { offset: 0x0FC000, erasesize: 0x04000, numblocks: 1 }
++ .mfr_id = MANUFACTURER_AMD,
++ .dev_id = AM29F800BT,
++ .name = "AMD AM29F800BT",
++ .size = 0x00100000,
++ .numeraseregions = 4,
++ .regions = {
++ { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 15 },
++ { .offset = 0x0F0000, .erasesize = 0x08000, .numblocks = 1 },
++ { .offset = 0x0F8000, .erasesize = 0x02000, .numblocks = 2 },
++ { .offset = 0x0FC000, .erasesize = 0x04000, .numblocks = 1 }
+ }
+ }, {
+- mfr_id: MANUFACTURER_AMD,
+- dev_id: AM29LV800BB,
+- name: "AMD AM29LV800BB",
+- size: 0x00100000,
+- numeraseregions: 4,
+- regions: {
+- { offset: 0x000000, erasesize: 0x10000, numblocks: 15 },
+- { offset: 0x0F0000, erasesize: 0x08000, numblocks: 1 },
+- { offset: 0x0F8000, erasesize: 0x02000, numblocks: 2 },
+- { offset: 0x0FC000, erasesize: 0x04000, numblocks: 1 }
++ .mfr_id = MANUFACTURER_AMD,
++ .dev_id = AM29LV800BB,
++ .name = "AMD AM29LV800BB",
++ .size = 0x00100000,
++ .numeraseregions = 4,
++ .regions = {
++ { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 15 },
++ { .offset = 0x0F0000, .erasesize = 0x08000, .numblocks = 1 },
++ { .offset = 0x0F8000, .erasesize = 0x02000, .numblocks = 2 },
++ { .offset = 0x0FC000, .erasesize = 0x04000, .numblocks = 1 }
+ }
+ }, {
+- mfr_id: MANUFACTURER_FUJITSU,
+- dev_id: MBM29LV800BB,
+- name: "Fujitsu MBM29LV800BB",
+- size: 0x00100000,
+- numeraseregions: 4,
+- regions: {
+- { offset: 0x000000, erasesize: 0x04000, numblocks: 1 },
+- { offset: 0x004000, erasesize: 0x02000, numblocks: 2 },
+- { offset: 0x008000, erasesize: 0x08000, numblocks: 1 },
+- { offset: 0x010000, erasesize: 0x10000, numblocks: 15 }
++ .mfr_id = MANUFACTURER_FUJITSU,
++ .dev_id = MBM29LV800BB,
++ .name = "Fujitsu MBM29LV800BB",
++ .size = 0x00100000,
++ .numeraseregions = 4,
++ .regions = {
++ { .offset = 0x000000, .erasesize = 0x04000, .numblocks = 1 },
++ { .offset = 0x004000, .erasesize = 0x02000, .numblocks = 2 },
++ { .offset = 0x008000, .erasesize = 0x08000, .numblocks = 1 },
++ { .offset = 0x010000, .erasesize = 0x10000, .numblocks = 15 }
+ }
+ }, {
+- mfr_id: MANUFACTURER_ST,
+- dev_id: M29W800T,
+- name: "ST M29W800T",
+- size: 0x00100000,
+- numeraseregions: 4,
+- regions: {
+- { offset: 0x000000, erasesize: 0x10000, numblocks: 15 },
+- { offset: 0x0F0000, erasesize: 0x08000, numblocks: 1 },
+- { offset: 0x0F8000, erasesize: 0x02000, numblocks: 2 },
+- { offset: 0x0FC000, erasesize: 0x04000, numblocks: 1 }
++ .mfr_id = MANUFACTURER_ST,
++ .dev_id = M29W800T,
++ .name = "ST M29W800T",
++ .size = 0x00100000,
++ .numeraseregions = 4,
++ .regions = {
++ { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 15 },
++ { .offset = 0x0F0000, .erasesize = 0x08000, .numblocks = 1 },
++ { .offset = 0x0F8000, .erasesize = 0x02000, .numblocks = 2 },
++ { .offset = 0x0FC000, .erasesize = 0x04000, .numblocks = 1 }
+ }
+ }, {
+- mfr_id: MANUFACTURER_ST,
+- dev_id: M29W160DT,
+- name: "ST M29W160DT",
+- size: 0x00200000,
+- numeraseregions: 4,
+- regions: {
+- { offset: 0x000000, erasesize: 0x10000, numblocks: 31 },
+- { offset: 0x1F0000, erasesize: 0x08000, numblocks: 1 },
+- { offset: 0x1F8000, erasesize: 0x02000, numblocks: 2 },
+- { offset: 0x1FC000, erasesize: 0x04000, numblocks: 1 }
++ .mfr_id = MANUFACTURER_ST,
++ .dev_id = M29W160DT,
++ .name = "ST M29W160DT",
++ .size = 0x00200000,
++ .numeraseregions = 4,
++ .regions = {
++ { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 31 },
++ { .offset = 0x1F0000, .erasesize = 0x08000, .numblocks = 1 },
++ { .offset = 0x1F8000, .erasesize = 0x02000, .numblocks = 2 },
++ { .offset = 0x1FC000, .erasesize = 0x04000, .numblocks = 1 }
+ }
+ }, {
+- mfr_id: MANUFACTURER_ST,
+- dev_id: M29W160DB,
+- name: "ST M29W160DB",
+- size: 0x00200000,
+- numeraseregions: 4,
+- regions: {
+- { offset: 0x000000, erasesize: 0x04000, numblocks: 1 },
+- { offset: 0x004000, erasesize: 0x02000, numblocks: 2 },
+- { offset: 0x008000, erasesize: 0x08000, numblocks: 1 },
+- { offset: 0x010000, erasesize: 0x10000, numblocks: 31 }
++ .mfr_id = MANUFACTURER_ST,
++ .dev_id = M29W160DB,
++ .name = "ST M29W160DB",
++ .size = 0x00200000,
++ .numeraseregions = 4,
++ .regions = {
++ { .offset = 0x000000, .erasesize = 0x04000, .numblocks = 1 },
++ { .offset = 0x004000, .erasesize = 0x02000, .numblocks = 2 },
++ { .offset = 0x008000, .erasesize = 0x08000, .numblocks = 1 },
++ { .offset = 0x010000, .erasesize = 0x10000, .numblocks = 31 }
+ }
+ }, {
+- mfr_id: MANUFACTURER_AMD,
+- dev_id: AM29BDS323D,
+- name: "AMD AM29BDS323D",
+- size: 0x00400000,
+- numeraseregions: 3,
+- regions: {
+- { offset: 0x000000, erasesize: 0x10000, numblocks: 48 },
+- { offset: 0x300000, erasesize: 0x10000, numblocks: 15 },
+- { offset: 0x3f0000, erasesize: 0x02000, numblocks: 8 },
++ .mfr_id = MANUFACTURER_AMD,
++ .dev_id = AM29BDS323D,
++ .name = "AMD AM29BDS323D",
++ .size = 0x00400000,
++ .numeraseregions = 3,
++ .regions = {
++ { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 48 },
++ { .offset = 0x300000, .erasesize = 0x10000, .numblocks = 15 },
++ { .offset = 0x3f0000, .erasesize = 0x02000, .numblocks = 8 },
+ }
+ }, {
+- mfr_id: MANUFACTURER_AMD,
+- dev_id: AM29BDS643D,
+- name: "AMD AM29BDS643D",
+- size: 0x00800000,
+- numeraseregions: 3,
+- regions: {
+- { offset: 0x000000, erasesize: 0x10000, numblocks: 96 },
+- { offset: 0x600000, erasesize: 0x10000, numblocks: 31 },
+- { offset: 0x7f0000, erasesize: 0x02000, numblocks: 8 },
++ .mfr_id = MANUFACTURER_AMD,
++ .dev_id = AM29BDS643D,
++ .name = "AMD AM29BDS643D",
++ .size = 0x00800000,
++ .numeraseregions = 3,
++ .regions = {
++ { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 96 },
++ { .offset = 0x600000, .erasesize = 0x10000, .numblocks = 31 },
++ { .offset = 0x7f0000, .erasesize = 0x02000, .numblocks = 8 },
+ }
+ }, {
+- mfr_id: MANUFACTURER_ATMEL,
+- dev_id: AT49xV16x,
+- name: "Atmel AT49xV16x",
+- size: 0x00200000,
+- numeraseregions: 2,
+- regions: {
+- { offset: 0x000000, erasesize: 0x02000, numblocks: 8 },
+- { offset: 0x010000, erasesize: 0x10000, numblocks: 31 }
++ .mfr_id = MANUFACTURER_ATMEL,
++ .dev_id = AT49xV16x,
++ .name = "Atmel AT49xV16x",
++ .size = 0x00200000,
++ .numeraseregions = 2,
++ .regions = {
++ { .offset = 0x000000, .erasesize = 0x02000, .numblocks = 8 },
++ { .offset = 0x010000, .erasesize = 0x10000, .numblocks = 31 }
+ }
+ }, {
+- mfr_id: MANUFACTURER_ATMEL,
+- dev_id: AT49xV16xT,
+- name: "Atmel AT49xV16xT",
+- size: 0x00200000,
+- numeraseregions: 2,
+- regions: {
+- { offset: 0x000000, erasesize: 0x10000, numblocks: 31 },
+- { offset: 0x1F0000, erasesize: 0x02000, numblocks: 8 }
++ .mfr_id = MANUFACTURER_ATMEL,
++ .dev_id = AT49xV16xT,
++ .name = "Atmel AT49xV16xT",
++ .size = 0x00200000,
++ .numeraseregions = 2,
++ .regions = {
++ { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 31 },
++ { .offset = 0x1F0000, .erasesize = 0x02000, .numblocks = 8 }
+ }
+ }
+ };
+@@ -822,7 +820,7 @@
+
+ chip->state = FL_READY;
+
+- map->copy_from(map, buf, adr, len);
++ map_copy_from(map, buf, adr, len);
+
+ wake_up(&chip->wq);
+ spin_unlock_bh(chip->mutex);
+@@ -984,7 +982,7 @@
+ u_char tmp_buf[4];
+ __u32 datum;
+
+- map->copy_from(map, tmp_buf,
++ map_copy_from(map, tmp_buf,
+ bus_ofs + private->chips[chipnum].start,
+ map->buswidth);
+ while (len && i < map->buswidth)
+@@ -1057,7 +1055,7 @@
+ u_char tmp_buf[2];
+ __u32 datum;
+
+- map->copy_from(map, tmp_buf,
++ map_copy_from(map, tmp_buf,
+ ofs + private->chips[chipnum].start,
+ map->buswidth);
+ while (len--) {
+@@ -1178,7 +1176,7 @@
+ __u8 verify;
+
+ for (address = adr; address < (adr + size); address++) {
+- if ((verify = map->read8(map, address)) != 0xFF) {
++ if ((verify = map_read8(map, address)) != 0xFF) {
+ error = 1;
+ break;
+ }
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/chips/cfi_cmdset_0001.c linux/drivers/mtd/chips/cfi_cmdset_0001.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/chips/cfi_cmdset_0001.c 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/chips/cfi_cmdset_0001.c 2004-11-17 18:17:58.910333528 +0100
+@@ -4,7 +4,7 @@
+ *
+ * (C) 2000 Red Hat. GPL'd
+ *
+- * $Id$
++ * $Id$
+ *
+ *
+ * 10/10/2000 Nicolas Pitre <nico@cam.org>
+@@ -21,6 +21,7 @@
+ #include <linux/types.h>
+ #include <linux/kernel.h>
+ #include <linux/sched.h>
++#include <linux/init.h>
+ #include <asm/io.h>
+ #include <asm/byteorder.h>
+
+@@ -29,10 +30,14 @@
+ #include <linux/delay.h>
+ #include <linux/interrupt.h>
+ #include <linux/mtd/map.h>
+-#include <linux/mtd/cfi.h>
++#include <linux/mtd/mtd.h>
+ #include <linux/mtd/compatmac.h>
++#include <linux/mtd/cfi.h>
++
++/* #define CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE */
+
+-// debugging, turns off buffer write mode #define FORCE_WORD_WRITE
++// debugging, turns off buffer write mode if set to 1
++#define FORCE_WORD_WRITE 0
+
+ static int cfi_intelext_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
+ static int cfi_intelext_read_user_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
+@@ -52,16 +57,21 @@
+
+ static struct mtd_info *cfi_intelext_setup (struct map_info *);
+
+-static int do_point (struct mtd_info *mtd, loff_t from, size_t len,
++static int cfi_intelext_point (struct mtd_info *mtd, loff_t from, size_t len,
+ size_t *retlen, u_char **mtdbuf);
+-static void do_unpoint (struct mtd_info *mtd, u_char *addr, loff_t from,
++static void cfi_intelext_unpoint (struct mtd_info *mtd, u_char *addr, loff_t from,
+ size_t len);
+
++
++/*
++ * *********** SETUP AND PROBE BITS ***********
++ */
++
+ static struct mtd_chip_driver cfi_intelext_chipdrv = {
+- probe: NULL, /* Not usable directly */
+- destroy: cfi_intelext_destroy,
+- name: "cfi_cmdset_0001",
+- module: THIS_MODULE
++ .probe = NULL, /* Not usable directly */
++ .destroy = cfi_intelext_destroy,
++ .name = "cfi_cmdset_0001",
++ .module = THIS_MODULE
+ };
+
+ /* #define DEBUG_LOCK_BITS */
+@@ -102,13 +112,63 @@
+ }
+
+ printk(" Vcc Logic Supply Optimum Program/Erase Voltage: %d.%d V\n",
+- extp->VccOptimal >> 8, extp->VccOptimal & 0xf);
++ extp->VccOptimal >> 4, extp->VccOptimal & 0xf);
+ if (extp->VppOptimal)
+ printk(" Vpp Programming Supply Optimum Program/Erase Voltage: %d.%d V\n",
+- extp->VppOptimal >> 8, extp->VppOptimal & 0xf);
++ extp->VppOptimal >> 4, extp->VppOptimal & 0xf);
+ }
+ #endif
+
++#ifdef CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE
++/* Some Intel Strata Flash prior to FPO revision C has bugs in this area */
++static void fixup_intel_strataflash(struct map_info *map, void* param)
++{
++ struct cfi_private *cfi = map->fldrv_priv;
++ struct cfi_pri_amdstd *extp = cfi->cmdset_priv;
++
++ printk(KERN_WARNING "cfi_cmdset_0001: Suspend "
++ "erase on write disabled.\n");
++ extp->SuspendCmdSupport &= ~1;
++}
++#endif
++
++static void fixup_st_m28w320ct(struct map_info *map, void* param)
++{
++ struct cfi_private *cfi = map->fldrv_priv;
++
++ cfi->cfiq->BufWriteTimeoutTyp = 0; /* Not supported */
++ cfi->cfiq->BufWriteTimeoutMax = 0; /* Not supported */
++}
++
++static void fixup_st_m28w320cb(struct map_info *map, void* param)
++{
++ struct cfi_private *cfi = map->fldrv_priv;
++
++ /* Note this is done after the region info is endian swapped */
++ cfi->cfiq->EraseRegionInfo[1] =
++ (cfi->cfiq->EraseRegionInfo[1] & 0xffff0000) | 0x3e;
++};
++
++static struct cfi_fixup fixup_table[] = {
++#ifdef CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE
++ {
++ CFI_MFR_ANY, CFI_ID_ANY,
++ fixup_intel_strataflash, NULL
++ },
++#endif
++ {
++ 0x0020, /* STMicroelectronics */
++ 0x00ba, /* M28W320CT */
++ fixup_st_m28w320ct, NULL
++ }, {
++ 0x0020, /* STMicroelectronics */
++ 0x00bb, /* M28W320CB */
++ fixup_st_m28w320cb, NULL
++ }, {
++ 0, 0, NULL, NULL
++ }
++};
++
+ /* This routine is made available to other mtd code via
+ * inter_module_register. It must only be accessed through
+ * inter_module_get which will bump the use count of this module. The
+@@ -120,7 +180,6 @@
+ {
+ struct cfi_private *cfi = map->fldrv_priv;
+ int i;
+- __u32 base = cfi->chips[0].start;
+
+ if (cfi->cfi_mode == CFI_MODE_CFI) {
+ /*
+@@ -130,59 +189,29 @@
+ */
+ __u16 adr = primary?cfi->cfiq->P_ADR:cfi->cfiq->A_ADR;
+ struct cfi_pri_intelext *extp;
+- int ofs_factor = cfi->interleave * cfi->device_type;
+-
+- //printk(" Intel/Sharp Extended Query Table at 0x%4.4X\n", adr);
+- if (!adr)
+- return NULL;
+
+- /* Switch it into Query Mode */
+- cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL);
+-
+- extp = kmalloc(sizeof(*extp), GFP_KERNEL);
+- if (!extp) {
+- printk(KERN_ERR "Failed to allocate memory\n");
++ extp = (struct cfi_pri_intelext*)cfi_read_pri(map, adr, sizeof(*extp), "Intel/Sharp");
++ if (!extp)
+ return NULL;
+- }
+-
+- /* Read in the Extended Query Table */
+- for (i=0; i<sizeof(*extp); i++) {
+- ((unsigned char *)extp)[i] =
+- cfi_read_query(map, (base+((adr+i)*ofs_factor)));
+- }
+-
+- if (extp->MajorVersion != '1' ||
+- (extp->MinorVersion < '0' || extp->MinorVersion > '3')) {
+- printk(KERN_WARNING " Unknown IntelExt Extended Query "
+- "version %c.%c.\n", extp->MajorVersion,
+- extp->MinorVersion);
+- kfree(extp);
+- return NULL;
+- }
+
+ /* Do some byteswapping if necessary */
+ extp->FeatureSupport = le32_to_cpu(extp->FeatureSupport);
+ extp->BlkStatusRegMask = le16_to_cpu(extp->BlkStatusRegMask);
+ extp->ProtRegAddr = le16_to_cpu(extp->ProtRegAddr);
+
++ /* Install our own private info structure */
++ cfi->cmdset_priv = extp;
++
++ cfi_fixup(map, fixup_table);
++
+ #ifdef DEBUG_CFI_FEATURES
+ /* Tell the user about it in lots of lovely detail */
+ cfi_tell_features(extp);
+ #endif
+
+ if(extp->SuspendCmdSupport & 1) {
+-//#define CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE
+-#ifdef CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE
+-/* Some Intel Strata Flash prior to FPO revision C has bugs in this area */
+- printk(KERN_WARNING "cfi_cmdset_0001: Suspend "
+- "erase on write disabled.\n");
+- extp->SuspendCmdSupport &= ~1;
+-#else
+ printk(KERN_NOTICE "cfi_cmdset_0001: Erase suspend on write enabled\n");
+-#endif
+ }
+- /* Install our own private info structure */
+- cfi->cmdset_priv = extp;
+ }
+
+ for (i=0; i< cfi->numchips; i++) {
+@@ -194,8 +223,6 @@
+
+ map->fldrv = &cfi_intelext_chipdrv;
+
+- /* Make sure it's in read mode */
+- cfi_send_gen_cmd(0xff, 0x55, base, map, cfi, cfi->device_type, NULL);
+ return cfi_intelext_setup(map);
+ }
+
+@@ -261,20 +288,16 @@
+ mtd->erase = cfi_intelext_erase_varsize;
+ mtd->read = cfi_intelext_read;
+
+- if(map->point && map->unpoint){
+- mtd->point = do_point;
+- mtd->unpoint = do_unpoint;
++ if (map_is_linear(map)) {
++ mtd->point = cfi_intelext_point;
++ mtd->unpoint = cfi_intelext_unpoint;
+ }
+
+-#ifndef FORCE_WORD_WRITE
+- if ( cfi->cfiq->BufWriteTimeoutTyp ) {
+- printk("Using buffer write method\n" );
++ if ( cfi->cfiq->BufWriteTimeoutTyp && !FORCE_WORD_WRITE) {
++ printk(KERN_INFO "Using buffer write method\n" );
+ mtd->write = cfi_intelext_write_buffers;
+ } else {
+-#else
+- {
+-#endif
+- printk("Using word write method\n" );
++ printk(KERN_INFO "Using word write method\n" );
+ mtd->write = cfi_intelext_write_words;
+ }
+ mtd->read_user_prot_reg = cfi_intelext_read_user_prot_reg;
+@@ -286,8 +309,8 @@
+ mtd->resume = cfi_intelext_resume;
+ mtd->flags = MTD_CAP_NORFLASH;
+ map->fldrv = &cfi_intelext_chipdrv;
+- MOD_INC_USE_COUNT;
+ mtd->name = map->name;
++ __module_get(THIS_MODULE);
+ return mtd;
+
+ setup_err:
+@@ -301,78 +324,170 @@
+ return NULL;
+ }
+
+-static int do_point_onechip (struct map_info *map, struct flchip *chip, loff_t adr, size_t len)
++/*
++ * *********** CHIP ACCESS FUNCTIONS ***********
++ */
++
++static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr, int mode)
+ {
+- cfi_word status, status_OK;
+- unsigned long timeo;
+ DECLARE_WAITQUEUE(wait, current);
+- unsigned long cmd_addr;
+ struct cfi_private *cfi = map->fldrv_priv;
++ cfi_word status, status_OK = CMD(0x80);
++ unsigned long timeo;
++ struct cfi_pri_intelext *cfip = (struct cfi_pri_intelext *)cfi->cmdset_priv;
+
+- adr += chip->start;
+-
+- /* Ensure cmd read/writes are aligned. */
+- cmd_addr = adr & ~(CFIDEV_BUSWIDTH-1);
+-
+- /* Let's determine this according to the interleave only once */
+- status_OK = CMD(0x80);
+-
++ resettime:
+ timeo = jiffies + HZ;
+ retry:
+- spin_lock(chip->mutex);
+-
+- /* Check that the chip's ready to talk to us.
+- * If it's in FL_ERASING state, suspend it and make it talk now.
+- */
+ switch (chip->state) {
+
+- case FL_READY:
+- case FL_POINT:
++ case FL_STATUS:
++ for (;;) {
++ status = cfi_read(map, adr);
++ if ((status & status_OK) == status_OK)
+ break;
+
++ if (time_after(jiffies, timeo)) {
++ printk(KERN_ERR "Waiting for chip to be ready timed out. Status %llx\n",
++ (long long)status);
++ spin_unlock(chip->mutex);
++ return -EIO;
++ }
++ spin_unlock(chip->mutex);
++ cfi_udelay(1);
++ spin_lock(chip->mutex);
++ /* Someone else might have been playing with it. */
++ goto retry;
++ }
++
++ case FL_READY:
+ case FL_CFI_QUERY:
+ case FL_JEDEC_QUERY:
+- cfi_write(map, CMD(0x70), cmd_addr);
+- chip->state = FL_STATUS;
++ return 0;
+
+- case FL_STATUS:
+- status = cfi_read(map, cmd_addr);
+- if ((status & status_OK) == status_OK) {
+- cfi_write(map, CMD(0xff), cmd_addr);
+- chip->state = FL_READY;
++ case FL_ERASING:
++ if (!(cfip->FeatureSupport & 2) ||
++ !(mode == FL_READY || mode == FL_POINT ||
++ (mode == FL_WRITING && (cfip->SuspendCmdSupport & 1))))
++ goto sleep;
++
++
++ /* Erase suspend */
++ cfi_write(map, CMD(0xB0), adr);
++
++ /* If the flash has finished erasing, then 'erase suspend'
++ * appears to make some (28F320) flash devices switch to
++ * 'read' mode. Make sure that we switch to 'read status'
++ * mode so we get the right data. --rmk
++ */
++ cfi_write(map, CMD(0x70), adr);
++ chip->oldstate = FL_ERASING;
++ chip->state = FL_ERASE_SUSPENDING;
++ chip->erase_suspended = 1;
++ for (;;) {
++ status = cfi_read(map, adr);
++ if ((status & status_OK) == status_OK)
+ break;
+- }
+
+- /* Urgh. Chip not yet ready to talk to us. */
+ if (time_after(jiffies, timeo)) {
+- spin_unlock(chip->mutex);
+- printk(KERN_ERR "waiting for chip to be ready timed out in read. WSM status = %llx\n", (__u64)status);
++ /* Urgh. Resume and pretend we weren't here. */
++ cfi_write(map, CMD(0xd0), adr);
++ /* Make sure we're in 'read status' mode if it had finished */
++ cfi_write(map, CMD(0x70), adr);
++ chip->state = FL_ERASING;
++ chip->oldstate = FL_READY;
++ printk(KERN_ERR "Chip not ready after erase "
++ "suspended: status = 0x%x\n", status);
+ return -EIO;
+ }
+
+- /* Latency issues. Drop the lock, wait a while and retry */
+ spin_unlock(chip->mutex);
+ cfi_udelay(1);
+- goto retry;
++ spin_lock(chip->mutex);
++ /* Nobody will touch it while it's in state FL_ERASE_SUSPENDING.
++ So we can just loop here. */
++ }
++ chip->state = FL_STATUS;
++ return 0;
++
++ case FL_POINT:
++ /* Only if there's no operation suspended... */
++ if (mode == FL_READY && chip->oldstate == FL_READY)
++ return 0;
+
+ default:
+- /* Stick ourselves on a wait queue to be woken when
+- someone changes the status */
++ sleep:
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ add_wait_queue(&chip->wq, &wait);
+ spin_unlock(chip->mutex);
+ schedule();
+ remove_wait_queue(&chip->wq, &wait);
+- timeo = jiffies + HZ;
+- goto retry;
++ spin_lock(chip->mutex);
++ goto resettime;
+ }
++}
++
++static void put_chip(struct map_info *map, struct flchip *chip, unsigned long adr)
++{
++ struct cfi_private *cfi = map->fldrv_priv;
++
++ switch(chip->oldstate) {
++ case FL_ERASING:
++ chip->state = chip->oldstate;
++ /* What if one interleaved chip has finished and the
++ other hasn't? The old code would leave the finished
++ one in READY mode. That's bad, and caused -EROFS
++ errors to be returned from do_erase_oneblock because
++ that's the only bit it checked for at the time.
++ As the state machine appears to explicitly allow
++ sending the 0x70 (Read Status) command to an erasing
++ chip and expecting it to be ignored, that's what we
++ do. */
++ cfi_write(map, CMD(0xd0), adr);
++ cfi_write(map, CMD(0x70), adr);
++ chip->oldstate = FL_READY;
++ chip->state = FL_ERASING;
++ break;
++
++ case FL_READY:
++ case FL_STATUS:
++ /* We should really make set_vpp() count, rather than doing this */
++ DISABLE_VPP(map);
++ break;
++ default:
++ printk(KERN_ERR "put_chip() called with oldstate %d!!\n", chip->oldstate);
++ }
++ wake_up(&chip->wq);
++}
++
++static int do_point_onechip (struct map_info *map, struct flchip *chip, loff_t adr, size_t len)
++{
++ unsigned long cmd_addr;
++ struct cfi_private *cfi = map->fldrv_priv;
++ int ret = 0;
++
++ adr += chip->start;
++
++ /* Ensure cmd read/writes are aligned. */
++ cmd_addr = adr & ~(CFIDEV_BUSWIDTH-1);
++
++ spin_lock(chip->mutex);
++
++ ret = get_chip(map, chip, cmd_addr, FL_POINT);
++
++ if (!ret) {
++ if (chip->state != FL_POINT && chip->state != FL_READY)
++ cfi_write(map, CMD(0xff), cmd_addr);
+
+ chip->state = FL_POINT;
+ chip->ref_point_counter++;
++ }
+ spin_unlock(chip->mutex);
+- return 0;
++
++ return ret;
+ }
+-static int do_point (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char **mtdbuf)
++
++static int cfi_intelext_point (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char **mtdbuf)
+ {
+ struct map_info *map = mtd->priv;
+ struct cfi_private *cfi = map->fldrv_priv;
+@@ -380,12 +495,10 @@
+ int chipnum;
+ int ret = 0;
+
+- if (from + len > mtd->size)
++ if (!map->virt || (from + len > mtd->size))
+ return -EINVAL;
+
+- *mtdbuf = map->point(map, from, len);
+- if(*mtdbuf == NULL)
+- return -EINVAL; /* can not point this region */
++ *mtdbuf = (void *)map->virt + from;
+ *retlen = 0;
+
+ /* Now lock the chip(s) to POINT state */
+@@ -418,14 +531,13 @@
+ return 0;
+ }
+
+-static void do_unpoint (struct mtd_info *mtd, u_char *addr, loff_t from, size_t len)
++static void cfi_intelext_unpoint (struct mtd_info *mtd, u_char *addr, loff_t from, size_t len)
+ {
+ struct map_info *map = mtd->priv;
+ struct cfi_private *cfi = map->fldrv_priv;
+ unsigned long ofs;
+ int chipnum;
+
+- map->unpoint(map, addr, from, len);
+ /* Now unlock the chip(s) POINT state */
+
+ /* ofs: offset within the first chip that the first read should start */
+@@ -446,13 +558,14 @@
+ thislen = len;
+
+ spin_lock(chip->mutex);
+- if(chip->state == FL_POINT){
++ if (chip->state == FL_POINT) {
+ chip->ref_point_counter--;
+ if(chip->ref_point_counter == 0)
+ chip->state = FL_READY;
+ } else
+- printk("Warning: unpoint called on non pointed region\n"); /* Should this give an error? */
+- wake_up(&chip->wq);
++ printk(KERN_ERR "Warning: unpoint called on non pointed region\n"); /* Should this give an error? */
++
++ put_chip(map, chip, chip->start);
+ spin_unlock(chip->mutex);
+
+ len -= thislen;
+@@ -463,136 +576,32 @@
+
+ static inline int do_read_onechip(struct map_info *map, struct flchip *chip, loff_t adr, size_t len, u_char *buf)
+ {
+- cfi_word status, status_OK;
+- unsigned long timeo;
+- DECLARE_WAITQUEUE(wait, current);
+- int suspended = 0;
+ unsigned long cmd_addr;
+ struct cfi_private *cfi = map->fldrv_priv;
++ int ret;
+
+ adr += chip->start;
+
+ /* Ensure cmd read/writes are aligned. */
+ cmd_addr = adr & ~(CFIDEV_BUSWIDTH-1);
+
+- /* Let's determine this according to the interleave only once */
+- status_OK = CMD(0x80);
+-
+- timeo = jiffies + HZ;
+- retry:
+ spin_lock(chip->mutex);
+-
+- /* Check that the chip's ready to talk to us.
+- * If it's in FL_ERASING state, suspend it and make it talk now.
+- */
+- switch (chip->state) {
+- case FL_ERASING:
+- if (!cfi->cmdset_priv ||
+- !(((struct cfi_pri_intelext *)cfi->cmdset_priv)->FeatureSupport & 2))
+- goto sleep; /* We don't support erase suspend */
+-
+- cfi_write (map, CMD(0xb0), cmd_addr);
+- /* If the flash has finished erasing, then 'erase suspend'
+- * appears to make some (28F320) flash devices switch to
+- * 'read' mode. Make sure that we switch to 'read status'
+- * mode so we get the right data. --rmk
+- */
+- cfi_write(map, CMD(0x70), cmd_addr);
+- chip->oldstate = FL_ERASING;
+- chip->state = FL_ERASE_SUSPENDING;
+- // printk("Erase suspending at 0x%lx\n", cmd_addr);
+- for (;;) {
+- status = cfi_read(map, cmd_addr);
+- if ((status & status_OK) == status_OK)
+- break;
+-
+- if (time_after(jiffies, timeo)) {
+- /* Urgh */
+- cfi_write(map, CMD(0xd0), cmd_addr);
+- /* make sure we're in 'read status' mode */
+- cfi_write(map, CMD(0x70), cmd_addr);
+- chip->state = FL_ERASING;
+- spin_unlock(chip->mutex);
+- printk(KERN_ERR "Chip not ready after erase "
+- "suspended: status = 0x%llx\n", (__u64)status);
+- return -EIO;
+- }
+-
++ ret = get_chip(map, chip, cmd_addr, FL_READY);
++ if (ret) {
+ spin_unlock(chip->mutex);
+- cfi_udelay(1);
+- spin_lock(chip->mutex);
++ return ret;
+ }
+
+- suspended = 1;
++ if (chip->state != FL_POINT && chip->state != FL_READY) {
+ cfi_write(map, CMD(0xff), cmd_addr);
+- chip->state = FL_READY;
+- break;
+-
+-#if 0
+- case FL_WRITING:
+- /* Not quite yet */
+-#endif
+-
+- case FL_READY:
+- case FL_POINT:
+- break;
+-
+- case FL_CFI_QUERY:
+- case FL_JEDEC_QUERY:
+- cfi_write(map, CMD(0x70), cmd_addr);
+- chip->state = FL_STATUS;
+
+- case FL_STATUS:
+- status = cfi_read(map, cmd_addr);
+- if ((status & status_OK) == status_OK) {
+- cfi_write(map, CMD(0xff), cmd_addr);
+ chip->state = FL_READY;
+- break;
+ }
+
+- /* Urgh. Chip not yet ready to talk to us. */
+- if (time_after(jiffies, timeo)) {
+- spin_unlock(chip->mutex);
+- printk(KERN_ERR "waiting for chip to be ready timed out in read. WSM status = %llx\n", (__u64)status);
+- return -EIO;
+- }
++ map_copy_from(map, buf, adr, len);
+
+- /* Latency issues. Drop the lock, wait a while and retry */
+- spin_unlock(chip->mutex);
+- cfi_udelay(1);
+- goto retry;
++ put_chip(map, chip, cmd_addr);
+
+- default:
+- sleep:
+- /* Stick ourselves on a wait queue to be woken when
+- someone changes the status */
+- set_current_state(TASK_UNINTERRUPTIBLE);
+- add_wait_queue(&chip->wq, &wait);
+- spin_unlock(chip->mutex);
+- schedule();
+- remove_wait_queue(&chip->wq, &wait);
+- timeo = jiffies + HZ;
+- goto retry;
+- }
+-
+- map->copy_from(map, buf, adr, len);
+-
+- if (suspended) {
+- chip->state = chip->oldstate;
+- /* What if one interleaved chip has finished and the
+- other hasn't? The old code would leave the finished
+- one in READY mode. That's bad, and caused -EROFS
+- errors to be returned from do_erase_oneblock because
+- that's the only bit it checked for at the time.
+- As the state machine appears to explicitly allow
+- sending the 0x70 (Read Status) command to an erasing
+- chip and expecting it to be ignored, that's what we
+- do. */
+- cfi_write(map, CMD(0xd0), cmd_addr);
+- cfi_write(map, CMD(0x70), cmd_addr);
+- }
+-
+- wake_up(&chip->wq);
+ spin_unlock(chip->mutex);
+ return 0;
+ }
+@@ -640,70 +649,52 @@
+ {
+ struct map_info *map = mtd->priv;
+ struct cfi_private *cfi = map->fldrv_priv;
+- struct cfi_pri_intelext *extp=cfi->cmdset_priv;
+- int ofs_factor = cfi->interleave * cfi->device_type;
+- int count=len;
++ struct cfi_pri_intelext *extp = cfi->cmdset_priv;
+ struct flchip *chip;
+- int chip_num,offst;
+- unsigned long timeo;
+- DECLARE_WAITQUEUE(wait, current);
++ int ofs_factor = cfi->interleave * cfi->device_type;
++ int count = len;
++ int chip_num, offst;
++ int ret;
+
+- chip=0;
+- /* Calculate which chip & protection register offset we need */
+- chip_num=((unsigned int)from/reg_sz);
+- offst=from-(reg_sz*chip_num)+base_offst;
++ chip_num = ((unsigned int)from/reg_sz);
++ offst = from - (reg_sz*chip_num)+base_offst;
+
+- while(count){
++ while (count) {
++ /* Calculate which chip & protection register offset we need */
+
+- if(chip_num>=cfi->numchips)
++ if (chip_num >= cfi->numchips)
+ goto out;
+
+- /* Make sure that the chip is in the right state */
++ chip = &cfi->chips[chip_num];
+
+- timeo = jiffies + HZ;
+- chip=&cfi->chips[chip_num];
+- retry:
+ spin_lock(chip->mutex);
+-
+- switch (chip->state) {
+- case FL_READY:
+- case FL_STATUS:
+- case FL_CFI_QUERY:
+- case FL_JEDEC_QUERY:
+- break;
+-
+- default:
+- /* Stick ourselves on a wait queue to be woken when
+- someone changes the status */
+- set_current_state(TASK_UNINTERRUPTIBLE);
+- add_wait_queue(&chip->wq, &wait);
++ ret = get_chip(map, chip, chip->start, FL_JEDEC_QUERY);
++ if (ret) {
+ spin_unlock(chip->mutex);
+- schedule();
+- remove_wait_queue(&chip->wq, &wait);
+- timeo = jiffies + HZ;
+- goto retry;
++ return (len-count)?:ret;
+ }
+
+- /* Now read the data required from this flash */
++ if (chip->state != FL_JEDEC_QUERY) {
++ cfi_write(map, CMD(0x90), chip->start);
++ chip->state = FL_JEDEC_QUERY;
++ }
+
+- cfi_send_gen_cmd(0x90, 0x55,chip->start, map, cfi, cfi->device_type, NULL);
+- while(count && ((offst-base_offst)<reg_sz)){
+- *buf=map->read8(map,(chip->start+((extp->ProtRegAddr+1)*ofs_factor)+offst));
++ while (count && ((offst-base_offst) < reg_sz)) {
++ *buf = map_read8(map,(chip->start+((extp->ProtRegAddr+1)*ofs_factor)+offst));
+ buf++;
+ offst++;
+ count--;
+ }
+
+- chip->state=FL_CFI_QUERY;
++ put_chip(map, chip, chip->start);
+ spin_unlock(chip->mutex);
++
+ /* Move on to the next chip */
+ chip_num++;
+- offst=base_offst;
+-
++ offst = base_offst;
+ }
+
+ out:
+- wake_up(&chip->wq);
+ return len-count;
+ }
+
+@@ -749,103 +740,20 @@
+ static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned long adr, cfi_word datum)
+ {
+ struct cfi_private *cfi = map->fldrv_priv;
+- struct cfi_pri_intelext *extp = cfi->cmdset_priv;
+- cfi_word status, status_OK;
+- unsigned long timeo;
+- DECLARE_WAITQUEUE(wait, current);
+- int z, suspended=0, ret=0;
+-
+- adr += chip->start;
+-
+- /* Let's determine this according to the interleave only once */
+- status_OK = CMD(0x80);
+-
+- timeo = jiffies + HZ;
+- retry:
+- spin_lock(chip->mutex);
+-
+- /* Check that the chip's ready to talk to us.
+- * Later, we can actually think about interrupting it
+- * if it's in FL_ERASING state.
+- * Not just yet, though.
+- */
+- switch (chip->state) {
+- case FL_READY:
+- break;
+-
+- case FL_CFI_QUERY:
+- case FL_JEDEC_QUERY:
+- cfi_write(map, CMD(0x70), adr);
+- chip->state = FL_STATUS;
+-
+- case FL_STATUS:
+- status = cfi_read(map, adr);
+- if ((status & status_OK) == status_OK)
+- break;
+-
+- /* Urgh. Chip not yet ready to talk to us. */
+- if (time_after(jiffies, timeo)) {
+- spin_unlock(chip->mutex);
+- printk(KERN_ERR "waiting for chip to be ready timed out in read\n");
+- return -EIO;
+- }
+-
+- /* Latency issues. Drop the lock, wait a while and retry */
+- spin_unlock(chip->mutex);
+- cfi_udelay(1);
+- goto retry;
+-
+- case FL_ERASING:
+- if (!extp ||
+- !((extp->FeatureSupport & 2) && (extp->SuspendCmdSupport & 1)))
+- goto sleep; /* We don't support erase suspend */
+-
+- cfi_write (map, CMD(0xb0), adr);
+-
+- /* If the flash has finished erasing, then 'erase suspend'
+- * appears to make some (28F320) flash devices switch to
+- * 'read' mode. Make sure that we switch to 'read status'
+- * mode so we get the right data. --rmk
+- */
+- cfi_write(map, CMD(0x70), adr);
+- chip->oldstate = FL_ERASING;
+- chip->state = FL_ERASE_SUSPENDING;
+- for (;;) {
+- status = cfi_read(map, adr);
+- if ((status & status_OK) == status_OK)
+- break;
++ cfi_word status, status_OK;
++ unsigned long timeo;
++ int z, ret=0;
+
+- if (time_after(jiffies, timeo)) {
+- /* Urgh */
+- cfi_write(map, CMD(0xd0), adr);
+- /* make sure we're in 'read status' mode */
+- cfi_write(map, CMD(0x70), adr);
+- chip->state = FL_ERASING;
+- spin_unlock(chip->mutex);
+- printk(KERN_ERR "Chip not ready after erase "
+- "suspended: status = 0x%x\n", status);
+- return -EIO;
+- }
++ adr += chip->start;
+
+- spin_unlock(chip->mutex);
+- cfi_udelay(1);
+- spin_lock(chip->mutex);
+- }
+- suspended = 1;
+- chip->state = FL_STATUS;
+- break;
++ /* Let's determine this according to the interleave only once */
++ status_OK = CMD(0x80);
+
+- default:
+- sleep:
+- /* Stick ourselves on a wait queue to be woken when
+- someone changes the status */
+- set_current_state(TASK_UNINTERRUPTIBLE);
+- add_wait_queue(&chip->wq, &wait);
++ spin_lock(chip->mutex);
++ ret = get_chip(map, chip, adr, FL_WRITING);
++ if (ret) {
+ spin_unlock(chip->mutex);
+- schedule();
+- remove_wait_queue(&chip->wq, &wait);
+- timeo = jiffies + HZ;
+- goto retry;
++ return ret;
+ }
+
+ ENABLE_VPP(map);
+@@ -862,6 +770,8 @@
+ for (;;) {
+ if (chip->state != FL_WRITING) {
+ /* Someone's suspended the write. Sleep */
++ DECLARE_WAITQUEUE(wait, current);
++
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ add_wait_queue(&chip->wq, &wait);
+ spin_unlock(chip->mutex);
+@@ -879,7 +789,6 @@
+ /* OK Still waiting */
+ if (time_after(jiffies, timeo)) {
+ chip->state = FL_STATUS;
+- DISABLE_VPP(map);
+ printk(KERN_ERR "waiting for chip to be ready timed out in word write\n");
+ ret = -EIO;
+ goto out;
+@@ -908,27 +817,11 @@
+ /* put back into read status register mode */
+ cfi_write(map, CMD(0x70), adr);
+ ret = -EROFS;
+- goto out;
+ }
+ out:
+- if (suspended) {
+- chip->state = chip->oldstate;
+- /* What if one interleaved chip has finished and the
+- other hasn't? The old code would leave the finished
+- one in READY mode. That's bad, and caused -EROFS
+- errors to be returned from do_erase_oneblock because
+- that's the only bit it checked for at the time.
+- As the state machine appears to explicitly allow
+- sending the 0x70 (Read Status) command to an erasing
+- chip and expecting it to be ignored, that's what we
+- do. */
+- cfi_write(map, CMD(0xd0), adr);
+- cfi_write(map, CMD(0x70), adr);
+- } else
+- DISABLE_VPP(map); /* must not clear the VPP if there is a suspended erase to be resumed */
+-
+- wake_up(&chip->wq);
++ put_chip(map, chip, adr);
+ spin_unlock(chip->mutex);
++
+ return ret;
+ }
+
+@@ -1059,11 +952,9 @@
+ unsigned long adr, const u_char *buf, int len)
+ {
+ struct cfi_private *cfi = map->fldrv_priv;
+- struct cfi_pri_intelext *extp = cfi->cmdset_priv;
+ cfi_word status, status_OK;
+ unsigned long cmd_adr, timeo;
+- DECLARE_WAITQUEUE(wait, current);
+- int wbufsize, z, suspended=0, ret=0;
++ int wbufsize, z, ret=0, bytes, words;
+
+ wbufsize = CFIDEV_INTERLEAVE << cfi->cfiq->MaxBufWriteSize;
+ adr += chip->start;
+@@ -1072,91 +963,18 @@
+ /* Let's determine this according to the interleave only once */
+ status_OK = CMD(0x80);
+
+- timeo = jiffies + HZ;
+- retry:
+ spin_lock(chip->mutex);
+-
+- /* Check that the chip's ready to talk to us.
+- * Later, we can actually think about interrupting it
+- * if it's in FL_ERASING state.
+- * Not just yet, though.
+- */
+- switch (chip->state) {
+- case FL_READY:
+- case FL_CFI_QUERY:
+- case FL_JEDEC_QUERY:
+- cfi_write(map, CMD(0x70), cmd_adr);
+- chip->state = FL_STATUS;
+-
+- case FL_STATUS:
+- status = cfi_read(map, cmd_adr);
+- if ((status & status_OK) == status_OK)
+- break;
+- /* Urgh. Chip not yet ready to talk to us. */
+- if (time_after(jiffies, timeo)) {
++ ret = get_chip(map, chip, cmd_adr, FL_WRITING);
++ if (ret) {
+ spin_unlock(chip->mutex);
+- printk(KERN_ERR "waiting for chip to be ready timed out in buffer write\n");
+- return -EIO;
++ return ret;
+ }
+
+- /* Latency issues. Drop the lock, wait a while and retry */
+- spin_unlock(chip->mutex);
+- cfi_udelay(1);
+- goto retry;
+-
+- case FL_ERASING:
+- if (!extp ||
+- !((extp->FeatureSupport & 2) && (extp->SuspendCmdSupport & 1)))
+- goto sleep; /* We don't support erase suspend */
+-
+- cfi_write (map, CMD(0xb0), adr);
+-
+- /* If the flash has finished erasing, then 'erase suspend'
+- * appears to make some (28F320) flash devices switch to
+- * 'read' mode. Make sure that we switch to 'read status'
+- * mode so we get the right data. --rmk
+- */
+- cfi_write(map, CMD(0x70), adr);
+- chip->oldstate = FL_ERASING;
+- chip->state = FL_ERASE_SUSPENDING;
+- for (;;) {
+- status = cfi_read(map, adr);
+- if ((status & status_OK) == status_OK)
+- break;
+-
+- if (time_after(jiffies, timeo)) {
+- /* Urgh */
+- cfi_write(map, CMD(0xd0), adr);
+- /* make sure we're in 'read status' mode */
+- cfi_write(map, CMD(0x70), adr);
+- chip->state = FL_ERASING;
+- spin_unlock(chip->mutex);
+- printk(KERN_ERR "Chip not ready after erase "
+- "suspended: status = 0x%x\n", status);
+- return -EIO;
+- }
++ if (chip->state != FL_STATUS)
++ cfi_write(map, CMD(0x70), cmd_adr);
+
+- spin_unlock(chip->mutex);
+- cfi_udelay(1);
+- spin_lock(chip->mutex);
+- }
+- suspended = 1;
+- chip->state = FL_STATUS;
+- break;
++ status = cfi_read(map, cmd_adr);
+
+- default:
+- sleep:
+- /* Stick ourselves on a wait queue to be woken when
+- someone changes the status */
+- set_current_state(TASK_UNINTERRUPTIBLE);
+- add_wait_queue(&chip->wq, &wait);
+- spin_unlock(chip->mutex);
+- schedule();
+- remove_wait_queue(&chip->wq, &wait);
+- timeo = jiffies + HZ;
+- goto retry;
+- }
+- /* We know we're now in FL_STATUS mode, and 'status' is current */
+ /* §4.8 of the 28FxxxJ3A datasheet says "Any time SR.4 and/or SR.5 is set
+ [...], the device will not accept any more Write to Buffer commands".
+ So we must check here and reset those bits if they're set. Otherwise
+@@ -1185,7 +1003,6 @@
+ /* Argh. Not ready for write to buffer */
+ cfi_write(map, CMD(0x70), cmd_adr);
+ chip->state = FL_STATUS;
+- DISABLE_VPP(map);
+ printk(KERN_ERR "Chip not ready for buffer write. Xstatus = %llx, status = %llx\n", (__u64)status, (__u64)cfi_read(map, cmd_adr));
+ /* Odd. Clear status bits */
+ cfi_write(map, CMD(0x50), cmd_adr);
+@@ -1196,20 +1013,42 @@
+ }
+
+ /* Write length of data to come */
+- cfi_write(map, CMD(len/CFIDEV_BUSWIDTH-1), cmd_adr );
++ bytes = len & (CFIDEV_BUSWIDTH-1);
++ words = len / CFIDEV_BUSWIDTH;
++ cfi_write(map, CMD(words - !bytes), cmd_adr );
+
+ /* Write data */
+- for (z = 0; z < len; z += CFIDEV_BUSWIDTH) {
++ z = 0;
++ while(z < words * CFIDEV_BUSWIDTH) {
+ if (cfi_buswidth_is_1()) {
+- map->write8 (map, *((__u8*)buf)++, adr+z);
++ map_write8 (map, *((__u8*)buf)++, adr+z);
+ } else if (cfi_buswidth_is_2()) {
+- map->write16 (map, *((__u16*)buf)++, adr+z);
++ map_write16 (map, *((__u16*)buf)++, adr+z);
+ } else if (cfi_buswidth_is_4()) {
+- map->write32 (map, *((__u32*)buf)++, adr+z);
++ map_write32 (map, *((__u32*)buf)++, adr+z);
+ } else if (cfi_buswidth_is_8()) {
+- map->write64 (map, *((__u64*)buf)++, adr+z);
++ map_write64 (map, *((__u64*)buf)++, adr+z);
++ } else {
++ ret = -EINVAL;
++ goto out;
++ }
++ z += CFIDEV_BUSWIDTH;
++ }
++ if (bytes) {
++ int i = 0, n = 0;
++ u_char tmp_buf[8], *tmp_p = tmp_buf;
++
++ while (bytes--)
++ tmp_buf[i++] = buf[n++];
++ while (i < CFIDEV_BUSWIDTH)
++ tmp_buf[i++] = 0xff;
++ if (cfi_buswidth_is_2()) {
++ map_write16 (map, *((__u16*)tmp_p)++, adr+z);
++ } else if (cfi_buswidth_is_4()) {
++ map_write32 (map, *((__u32*)tmp_p)++, adr+z);
++ } else if (cfi_buswidth_is_8()) {
++ map_write64 (map, *((__u64*)tmp_p)++, adr+z);
+ } else {
+- DISABLE_VPP(map);
+ ret = -EINVAL;
+ goto out;
+ }
+@@ -1227,6 +1066,7 @@
+ for (;;) {
+ if (chip->state != FL_WRITING) {
+ /* Someone's suspended the write. Sleep */
++ DECLARE_WAITQUEUE(wait, current);
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ add_wait_queue(&chip->wq, &wait);
+ spin_unlock(chip->mutex);
+@@ -1244,7 +1084,6 @@
+ /* OK Still waiting */
+ if (time_after(jiffies, timeo)) {
+ chip->state = FL_STATUS;
+- DISABLE_VPP(map);
+ printk(KERN_ERR "waiting for chip to be ready timed out in bufwrite\n");
+ ret = -EIO;
+ goto out;
+@@ -1266,6 +1105,7 @@
+
+ /* Done and happy. */
+ chip->state = FL_STATUS;
++
+ /* check for lock bit */
+ if (status & CMD(0x02)) {
+ /* clear status */
+@@ -1273,26 +1113,10 @@
+ /* put back into read status register mode */
+ cfi_write(map, CMD(0x70), adr);
+ ret = -EROFS;
+- goto out;
+ }
+- out:
+- if (suspended) {
+- chip->state = chip->oldstate;
+- /* What if one interleaved chip has finished and the
+- other hasn't? The old code would leave the finished
+- one in READY mode. That's bad, and caused -EROFS
+- errors to be returned from do_erase_oneblock because
+- that's the only bit it checked for at the time.
+- As the state machine appears to explicitly allow
+- sending the 0x70 (Read Status) command to an erasing
+- chip and expecting it to be ignored, that's what we
+- do. */
+- cfi_write(map, CMD(0xd0), adr);
+- cfi_write(map, CMD(0x70), adr);
+- } else
+- DISABLE_VPP(map); /* must not clear the VPP if there is a suspended erase to be resumed */
+
+- wake_up(&chip->wq);
++ out:
++ put_chip(map, chip, cmd_adr);
+ spin_unlock(chip->mutex);
+ return ret;
+ }
+@@ -1336,12 +1160,12 @@
+ }
+
+ /* Write buffer is worth it only if more than one word to write... */
+- while(len > CFIDEV_BUSWIDTH) {
++ while(len) {
+ /* We must not cross write block boundaries */
+ int size = wbufsize - (ofs & (wbufsize-1));
+
+ if (size > len)
+- size = len & ~(CFIDEV_BUSWIDTH-1);
++ size = len;
+ ret = do_write_buffer(map, &cfi->chips[chipnum],
+ ofs, buf, size);
+ if (ret)
+@@ -1359,17 +1183,6 @@
+ return 0;
+ }
+ }
+-
+- /* ... and write the remaining bytes */
+- if (len > 0) {
+- size_t local_retlen;
+- ret = cfi_intelext_write_words(mtd, ofs + (chipnum << cfi->chipshift),
+- len, &local_retlen, buf);
+- if (ret)
+- return ret;
+- (*retlen) += local_retlen;
+- }
+-
+ return 0;
+ }
+
+@@ -1479,45 +1292,12 @@
+ /* Let's determine this according to the interleave only once */
+ status_OK = CMD(0x80);
+
+- timeo = jiffies + HZ;
+-retry:
++ retry:
+ spin_lock(chip->mutex);
+-
+- /* Check that the chip's ready to talk to us. */
+- switch (chip->state) {
+- case FL_CFI_QUERY:
+- case FL_JEDEC_QUERY:
+- case FL_READY:
+- cfi_write(map, CMD(0x70), adr);
+- chip->state = FL_STATUS;
+-
+- case FL_STATUS:
+- status = cfi_read(map, adr);
+- if ((status & status_OK) == status_OK)
+- break;
+-
+- /* Urgh. Chip not yet ready to talk to us. */
+- if (time_after(jiffies, timeo)) {
+- spin_unlock(chip->mutex);
+- printk(KERN_ERR "waiting for chip to be ready timed out in erase\n");
+- return -EIO;
+- }
+-
+- /* Latency issues. Drop the lock, wait a while and retry */
+- spin_unlock(chip->mutex);
+- cfi_udelay(1);
+- goto retry;
+-
+- default:
+- /* Stick ourselves on a wait queue to be woken when
+- someone changes the status */
+- set_current_state(TASK_UNINTERRUPTIBLE);
+- add_wait_queue(&chip->wq, &wait);
++ ret = get_chip(map, chip, adr, FL_ERASING);
++ if (ret) {
+ spin_unlock(chip->mutex);
+- schedule();
+- remove_wait_queue(&chip->wq, &wait);
+- timeo = jiffies + HZ;
+- goto retry;
++ return ret;
+ }
+
+ ENABLE_VPP(map);
+@@ -1528,7 +1308,7 @@
+ cfi_write(map, CMD(0x20), adr);
+ cfi_write(map, CMD(0xD0), adr);
+ chip->state = FL_ERASING;
+- chip->oldstate = 0;
++ chip->erase_suspended = 0;
+
+ spin_unlock(chip->mutex);
+ set_current_state(TASK_UNINTERRUPTIBLE);
+@@ -1550,11 +1330,11 @@
+ spin_lock(chip->mutex);
+ continue;
+ }
+- if (chip->oldstate) {
++ if (chip->erase_suspended) {
+ /* This erase was suspended and resumed.
+ Adjust the timeout */
+ timeo = jiffies + (HZ*20); /* FIXME */
+- chip->oldstate = 0;
++ chip->erase_suspended = 0;
+ }
+
+ status = cfi_read(map, adr);
+@@ -1658,39 +1438,22 @@
+ int i;
+ struct flchip *chip;
+ int ret = 0;
+- DECLARE_WAITQUEUE(wait, current);
+
+ for (i=0; !ret && i<cfi->numchips; i++) {
+ chip = &cfi->chips[i];
+
+- retry:
+ spin_lock(chip->mutex);
++ ret = get_chip(map, chip, chip->start, FL_SYNCING);
+
+- switch(chip->state) {
+- case FL_READY:
+- case FL_STATUS:
+- case FL_CFI_QUERY:
+- case FL_JEDEC_QUERY:
++ if (!ret) {
+ chip->oldstate = chip->state;
+ chip->state = FL_SYNCING;
+ /* No need to wake_up() on this state change -
+ * as the whole point is that nobody can do anything
+ * with the chip now anyway.
+ */
+- case FL_SYNCING:
+- spin_unlock(chip->mutex);
+- break;
+-
+- default:
+- /* Not an idle state */
+- add_wait_queue(&chip->wq, &wait);
+-
+- spin_unlock(chip->mutex);
+- schedule();
+- remove_wait_queue(&chip->wq, &wait);
+-
+- goto retry;
+ }
++ spin_unlock(chip->mutex);
+ }
+
+ /* Unlock the chips again */
+@@ -1731,52 +1494,18 @@
+ struct cfi_private *cfi = map->fldrv_priv;
+ cfi_word status, status_OK;
+ unsigned long timeo = jiffies + HZ;
+- DECLARE_WAITQUEUE(wait, current);
++ int ret;
+
+ adr += chip->start;
+
+ /* Let's determine this according to the interleave only once */
+ status_OK = CMD(0x80);
+
+- timeo = jiffies + HZ;
+-retry:
+ spin_lock(chip->mutex);
+-
+- /* Check that the chip's ready to talk to us. */
+- switch (chip->state) {
+- case FL_CFI_QUERY:
+- case FL_JEDEC_QUERY:
+- case FL_READY:
+- cfi_write(map, CMD(0x70), adr);
+- chip->state = FL_STATUS;
+-
+- case FL_STATUS:
+- status = cfi_read(map, adr);
+- if ((status & status_OK) == status_OK)
+- break;
+-
+- /* Urgh. Chip not yet ready to talk to us. */
+- if (time_after(jiffies, timeo)) {
+- spin_unlock(chip->mutex);
+- printk(KERN_ERR "%s: waiting for chip to be ready timed out\n", __FUNCTION__);
+- return -EIO;
+- }
+-
+- /* Latency issues. Drop the lock, wait a while and retry */
+- spin_unlock(chip->mutex);
+- cfi_udelay(1);
+- goto retry;
+-
+- default:
+- /* Stick ourselves on a wait queue to be woken when
+- someone changes the status */
+- set_current_state(TASK_UNINTERRUPTIBLE);
+- add_wait_queue(&chip->wq, &wait);
++ ret = get_chip(map, chip, adr, FL_LOCKING);
++ if (ret) {
+ spin_unlock(chip->mutex);
+- schedule();
+- remove_wait_queue(&chip->wq, &wait);
+- timeo = jiffies + HZ;
+- goto retry;
++ return ret;
+ }
+
+ ENABLE_VPP(map);
+@@ -1823,8 +1552,7 @@
+
+ /* Done and happy. */
+ chip->state = FL_STATUS;
+- DISABLE_VPP(map);
+- wake_up(&chip->wq);
++ put_chip(map, chip, adr);
+ spin_unlock(chip->mutex);
+ return 0;
+ }
+@@ -1889,22 +1617,23 @@
+
+ spin_lock(chip->mutex);
+
+- switch(chip->state) {
++ switch (chip->state) {
+ case FL_READY:
+ case FL_STATUS:
+ case FL_CFI_QUERY:
+ case FL_JEDEC_QUERY:
++ if (chip->oldstate == FL_READY) {
+ chip->oldstate = chip->state;
+ chip->state = FL_PM_SUSPENDED;
+ /* No need to wake_up() on this state change -
+ * as the whole point is that nobody can do anything
+ * with the chip now anyway.
+ */
+- case FL_PM_SUSPENDED:
++ }
+ break;
+-
+ default:
+ ret = -EAGAIN;
++ case FL_PM_SUSPENDED:
+ break;
+ }
+ spin_unlock(chip->mutex);
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/chips/cfi_cmdset_0002.c linux/drivers/mtd/chips/cfi_cmdset_0002.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/chips/cfi_cmdset_0002.c 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/chips/cfi_cmdset_0002.c 2004-11-17 18:17:58.937329424 +0100
+@@ -6,16 +6,22 @@
+ *
+ * 2_by_8 routines added by Simon Munton
+ *
++ * 4_by_16 work by Carolyn J. Smith
++ *
++ * Occasionally maintained by Thayne Harbaugh tharbaugh at lnxi dot com
++ *
+ * This code is GPL
+ *
+- * $Id$
++ * $Id$
+ *
+ */
+
++#include <linux/config.h>
+ #include <linux/module.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
+ #include <linux/sched.h>
++#include <linux/init.h>
+ #include <asm/io.h>
+ #include <asm/byteorder.h>
+
+@@ -23,16 +29,50 @@
+ #include <linux/slab.h>
+ #include <linux/delay.h>
+ #include <linux/interrupt.h>
++#include <linux/mtd/compatmac.h>
+ #include <linux/mtd/map.h>
++#include <linux/mtd/mtd.h>
+ #include <linux/mtd/cfi.h>
+
+ #define AMD_BOOTLOC_BUG
++#define FORCE_WORD_WRITE 0
++
++
++/*
++ * This is an attempt to coalesce the retry logic in one place - that way
++ * there aren't #ifdefs scattered throughout.
++ */
++#ifdef CONFIG_MTD_CFI_AMDSTD_RETRY
++
++#ifndef CONFIG_MTD_CFI_AMDSTD_RETRY_MAX
++#define CONFIG_MTD_CFI_AMDSTD_RETRY_MAX 0
++#endif
++
++#define RETRY_CMD_LABEL retry_cmd: do {} while (0)
++#define HANDLE_WACKY_STATE() handle_wacky_state(__func__, retry_cmd_cnt, adr, datum, prev_oldstatus, prev_status, oldstatus, status)
++static int retry_cmd_max = CONFIG_MTD_CFI_AMDSTD_RETRY_MAX;
++#define DECLARE_RETRY_CMD_CNT() int retry_cmd_cnt = 0
++#ifdef CONFIG_MTD_CFI_AMDSTD_RETRY
++#define CHECK_RETRIES() do { if (++retry_cmd_cnt <= retry_cmd_max) goto retry_cmd; } while (0)
++#endif
++
++#else
++
++#define RETRY_CMD_LABEL do {} while (0)
++#define HANDLE_WACKY_STATE() handle_wacky_state(__func__, adr, datum, prev_oldstatus, prev_status, oldstatus, status)
++#define DECLARE_RETRY_CMD_CNT()
++#define CHECK_RETRIES()
++
++#endif /* !defined(CONFIG_MTD_CFI_AMDSTD_RETRY) */
++
+
+ static int cfi_amdstd_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
+-static int cfi_amdstd_write(struct mtd_info *, loff_t, size_t, size_t *, const u_char *);
++static int cfi_amdstd_write_words(struct mtd_info *, loff_t, size_t, size_t *, const u_char *);
++static int cfi_amdstd_write_buffers(struct mtd_info *, loff_t, size_t, size_t *, const u_char *);
+ static int cfi_amdstd_erase_chip(struct mtd_info *, struct erase_info *);
+-static int cfi_amdstd_erase_onesize(struct mtd_info *, struct erase_info *);
+ static int cfi_amdstd_erase_varsize(struct mtd_info *, struct erase_info *);
++static int cfi_amdstd_lock_varsize(struct mtd_info *, loff_t, size_t);
++static int cfi_amdstd_unlock_varsize(struct mtd_info *, loff_t, size_t);
+ static void cfi_amdstd_sync (struct mtd_info *);
+ static int cfi_amdstd_suspend (struct mtd_info *);
+ static void cfi_amdstd_resume (struct mtd_info *);
+@@ -45,55 +85,136 @@
+
+
+ static struct mtd_chip_driver cfi_amdstd_chipdrv = {
+- probe: NULL, /* Not usable directly */
+- destroy: cfi_amdstd_destroy,
+- name: "cfi_cmdset_0002",
+- module: THIS_MODULE
++ .probe = NULL, /* Not usable directly */
++ .destroy = cfi_amdstd_destroy,
++ .name = "cfi_cmdset_0002",
++ .module = THIS_MODULE
+ };
+
+-struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary)
++
++/* #define DEBUG_LOCK_BITS */
++/* #define DEBUG_CFI_FEATURES */
++
++
++#ifdef DEBUG_CFI_FEATURES
++static void cfi_tell_features(struct cfi_pri_amdstd *extp)
+ {
+- struct cfi_private *cfi = map->fldrv_priv;
+- unsigned char bootloc;
+- int ofs_factor = cfi->interleave * cfi->device_type;
+- int i;
+- __u8 major, minor;
+- __u32 base = cfi->chips[0].start;
++ const char* erase_suspend[3] = {
++ "Not supported", "Read only", "Read/write"
++ };
++ const char* top_bottom[6] = {
++ "No WP", "8x8KiB sectors at top & bottom, no WP",
++ "Bottom boot", "Top boot",
++ "Uniform, Bottom WP", "Uniform, Top WP"
++ };
++
++ printk(" Silicon revision: %d\n", extp->SiliconRevision >> 1);
++ printk(" Address sensitive unlock: %s\n",
++ (extp->SiliconRevision & 1) ? "Not required" : "Required");
+
+- if (cfi->cfi_mode==CFI_MODE_CFI){
+- __u16 adr = primary?cfi->cfiq->P_ADR:cfi->cfiq->A_ADR;
++ if (extp->EraseSuspend < ARRAY_SIZE(erase_suspend))
++ printk(" Erase Suspend: %s\n", erase_suspend[extp->EraseSuspend]);
++ else
++ printk(" Erase Suspend: Unknown value %d\n", extp->EraseSuspend);
+
+- cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL);
++ if (extp->BlkProt == 0)
++ printk(" Block protection: Not supported\n");
++ else
++ printk(" Block protection: %d sectors per group\n", extp->BlkProt);
++
++
++ printk(" Temporary block unprotect: %s\n",
++ extp->TmpBlkUnprotect ? "Supported" : "Not supported");
++ printk(" Block protect/unprotect scheme: %d\n", extp->BlkProtUnprot);
++ printk(" Number of simultaneous operations: %d\n", extp->SimultaneousOps);
++ printk(" Burst mode: %s\n",
++ extp->BurstMode ? "Supported" : "Not supported");
++ if (extp->PageMode == 0)
++ printk(" Page mode: Not supported\n");
++ else
++ printk(" Page mode: %d word page\n", extp->PageMode << 2);
+
+- major = cfi_read_query(map, base + (adr+3)*ofs_factor);
+- minor = cfi_read_query(map, base + (adr+4)*ofs_factor);
++ printk(" Vpp Supply Minimum Program/Erase Voltage: %d.%d V\n",
++ extp->VppMin >> 4, extp->VppMin & 0xf);
++ printk(" Vpp Supply Maximum Program/Erase Voltage: %d.%d V\n",
++ extp->VppMax >> 4, extp->VppMax & 0xf);
+
+- printk(KERN_NOTICE " Amd/Fujitsu Extended Query Table v%c.%c at 0x%4.4X\n",
+- major, minor, adr);
+- cfi_send_gen_cmd(0xf0, 0x55, base, map, cfi, cfi->device_type, NULL);
+-
+- cfi_send_gen_cmd(0xaa, 0x555, base, map, cfi, cfi->device_type, NULL);
+- cfi_send_gen_cmd(0x55, 0x2aa, base, map, cfi, cfi->device_type, NULL);
+- cfi_send_gen_cmd(0x90, 0x555, base, map, cfi, cfi->device_type, NULL);
+- cfi->mfr = cfi_read_query(map, base);
+- cfi->id = cfi_read_query(map, base + ofs_factor);
++ if (extp->TopBottom < ARRAY_SIZE(top_bottom))
++ printk(" Top/Bottom Boot Block: %s\n", top_bottom[extp->TopBottom]);
++ else
++ printk(" Top/Bottom Boot Block: Unknown value %d\n", extp->TopBottom);
++}
++#endif
+
+- /* Wheee. Bring me the head of someone at AMD. */
+ #ifdef AMD_BOOTLOC_BUG
++/* Wheee. Bring me the head of someone at AMD. */
++static void fixup_amd_bootblock(struct map_info *map, void* param)
++{
++ struct cfi_private *cfi = map->fldrv_priv;
++ struct cfi_pri_amdstd *extp = cfi->cmdset_priv;
++ __u8 major = extp->MajorVersion;
++ __u8 minor = extp->MinorVersion;
++
+ if (((major << 8) | minor) < 0x3131) {
+ /* CFI version 1.0 => don't trust bootloc */
+ if (cfi->id & 0x80) {
+ printk(KERN_WARNING "%s: JEDEC Device ID is 0x%02X. Assuming broken CFI table.\n", map->name, cfi->id);
+- bootloc = 3; /* top boot */
++ extp->TopBottom = 3; /* top boot */
+ } else {
+- bootloc = 2; /* bottom boot */
++ extp->TopBottom = 2; /* bottom boot */
+ }
+- } else
++ }
++}
+ #endif
++
++static struct cfi_fixup fixup_table[] = {
++#ifdef AMD_BOOTLOC_BUG
+ {
+- cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL);
+- bootloc = cfi_read_query(map, base + (adr+15)*ofs_factor);
++ 0x0001, /* AMD */
++ CFI_ID_ANY,
++ fixup_amd_bootblock, NULL
++ },
++#endif
++ { 0, 0, NULL, NULL }
++};
++
++
++struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary)
++{
++ struct cfi_private *cfi = map->fldrv_priv;
++ unsigned char bootloc;
++ int i;
++
++ if (cfi->cfi_mode==CFI_MODE_CFI){
++ /*
++ * It's a real CFI chip, not one for which the probe
++ * routine faked a CFI structure. So we read the feature
++ * table from it.
++ */
++ __u16 adr = primary?cfi->cfiq->P_ADR:cfi->cfiq->A_ADR;
++ struct cfi_pri_amdstd *extp;
++
++ extp = (struct cfi_pri_amdstd*)cfi_read_pri(map, adr, sizeof(*extp), "Amd/Fujitsu");
++ if (!extp)
++ return NULL;
++
++ /* Install our own private info structure */
++ cfi->cmdset_priv = extp;
++
++ cfi_fixup(map, fixup_table);
++
++#ifdef DEBUG_CFI_FEATURES
++ /* Tell the user about it in lots of lovely detail */
++ cfi_tell_features(extp);
++#endif
++
++ bootloc = extp->TopBottom;
++ if ((bootloc != 2) && (bootloc != 3)) {
++ printk(KERN_WARNING "%s: CFI does not contain boot "
++ "bank location. Assuming top.\n", map->name);
++ bootloc = 2;
+ }
++
+ if (bootloc == 3 && cfi->cfiq->NumEraseRegions > 1) {
+ printk(KERN_WARNING "%s: Swapping erase regions for broken CFI table.\n", map->name);
+
+@@ -106,6 +227,11 @@
+ cfi->cfiq->EraseRegionInfo[j] = swap;
+ }
+ }
++ /*
++ * These might already be setup (more correctly) by
++ * jedec_probe.c - still need it for cfi_probe.c path.
++ */
++ if ( ! (cfi->addr_unlock1 && cfi->addr_unlock2) ) {
+ switch (cfi->device_type) {
+ case CFI_DEVICETYPE_X8:
+ cfi->addr_unlock1 = 0x555;
+@@ -125,9 +251,13 @@
+ cfi->addr_unlock2 = 0xaaa;
+ break;
+ default:
+- printk(KERN_NOTICE "Eep. Unknown cfi_cmdset_0002 device type %d\n", cfi->device_type);
++ printk(KERN_WARNING
++ "MTD %s(): Unsupported device type %d\n",
++ __func__, cfi->device_type);
+ return NULL;
+ }
++ }
++
+ } /* CFI mode */
+
+ for (i=0; i< cfi->numchips; i++) {
+@@ -138,15 +268,17 @@
+
+ map->fldrv = &cfi_amdstd_chipdrv;
+
+- cfi_send_gen_cmd(0xf0, 0x55, base, map, cfi, cfi->device_type, NULL);
+ return cfi_amdstd_setup(map);
+ }
+
++
+ static struct mtd_info *cfi_amdstd_setup(struct map_info *map)
+ {
+ struct cfi_private *cfi = map->fldrv_priv;
+ struct mtd_info *mtd;
+ unsigned long devsize = (1<<cfi->cfiq->DevSize) * cfi->interleave;
++ unsigned long offset = 0;
++ int i,j;
+
+ mtd = kmalloc(sizeof(*mtd), GFP_KERNEL);
+ printk(KERN_NOTICE "number of %s chips: %d\n",
+@@ -163,15 +295,9 @@
+ /* Also select the correct geometry setup too */
+ mtd->size = devsize * cfi->numchips;
+
+- if (cfi->cfiq->NumEraseRegions == 1) {
+- /* No need to muck about with multiple erase sizes */
+- mtd->erasesize = ((cfi->cfiq->EraseRegionInfo[0] >> 8) & ~0xff) * cfi->interleave;
+- } else {
+- unsigned long offset = 0;
+- int i,j;
+-
+ mtd->numeraseregions = cfi->cfiq->NumEraseRegions * cfi->numchips;
+- mtd->eraseregions = kmalloc(sizeof(struct mtd_erase_region_info) * mtd->numeraseregions, GFP_KERNEL);
++ mtd->eraseregions = kmalloc(sizeof(struct mtd_erase_region_info)
++ * mtd->numeraseregions, GFP_KERNEL);
+ if (!mtd->eraseregions) {
+ printk(KERN_WARNING "Failed to allocate memory for MTD erase region info\n");
+ goto setup_err;
+@@ -206,39 +332,52 @@
+ mtd->eraseregions[i].numblocks);
+ }
+ #endif
+- }
+
+ switch (CFIDEV_BUSWIDTH)
+ {
+ case 1:
+ case 2:
+ case 4:
+-#if 1
+- if (mtd->numeraseregions > 1)
+- mtd->erase = cfi_amdstd_erase_varsize;
+- else
++#ifdef CFI_WORD_64
++ case 8:
+ #endif
+- if (((cfi->cfiq->EraseRegionInfo[0] & 0xffff) + 1) == 1)
++ if (mtd->numeraseregions == 1
++ && ((cfi->cfiq->EraseRegionInfo[0] & 0xffff) + 1) == 1) {
+ mtd->erase = cfi_amdstd_erase_chip;
+- else
+- mtd->erase = cfi_amdstd_erase_onesize;
++ } else {
++ mtd->erase = cfi_amdstd_erase_varsize;
++ mtd->lock = cfi_amdstd_lock_varsize;
++ mtd->unlock = cfi_amdstd_unlock_varsize;
++ }
++
++ if ( cfi->cfiq->BufWriteTimeoutTyp && !FORCE_WORD_WRITE) {
++ DEBUG(MTD_DEBUG_LEVEL1, "Using buffer write method\n" );
++ mtd->write = cfi_amdstd_write_buffers;
++ } else {
++ DEBUG(MTD_DEBUG_LEVEL1, "Using word write method\n" );
++ mtd->write = cfi_amdstd_write_words;
++ }
++
+ mtd->read = cfi_amdstd_read;
+- mtd->write = cfi_amdstd_write;
+ break;
+
+ default:
+- printk(KERN_WARNING "Unsupported buswidth\n");
++ printk(KERN_WARNING "MTD %s(): Unsupported buswidth %d\n",
++ __func__, CFIDEV_BUSWIDTH);
+ goto setup_err;
+ break;
+ }
+ if (cfi->fast_prog) {
+- /* In cfi_amdstd_write() we frob the protection stuff
++ /* In cfi_amdstd_write_words() we frob the protection stuff
+ without paying any attention to the state machine.
+ This upsets in-progress erases. So we turn this flag
+ off for now till the code gets fixed. */
+ printk(KERN_NOTICE "cfi_cmdset_0002: Disabling fast programming due to code brokenness.\n");
+ cfi->fast_prog = 0;
+ }
++ /* FIXME: erase-suspend-program is broken. See
++ http://lists.infradead.org/pipermail/linux-mtd/2003-December/009001.html */
++ printk(KERN_NOTICE "cfi_cmdset_0002: Disabling erase-suspend-program due to code brokenness.\n");
+
+
+ /* does this chip have a secsi area? */
+@@ -266,7 +405,7 @@
+ mtd->flags = MTD_CAP_NORFLASH;
+ map->fldrv = &cfi_amdstd_chipdrv;
+ mtd->name = map->name;
+- MOD_INC_USE_COUNT;
++ __module_get(THIS_MODULE);
+ return mtd;
+
+ setup_err:
+@@ -280,46 +419,210 @@
+ return NULL;
+ }
+
+-static inline int do_read_onechip(struct map_info *map, struct flchip *chip, loff_t adr, size_t len, u_char *buf)
++
++/* This is more work to coalesce the retry #ifdefs in one location */
++static inline void handle_wacky_state(const char *func,
++#ifdef CONFIG_MTD_CFI_AMDSTD_RETRY
++ int retry_cmd_cnt,
++#endif
++ unsigned long adr,
++ cfi_word datum,
++ cfi_word prev_oldstatus,
++ cfi_word prev_status,
++ cfi_word oldstatus,
++ cfi_word status)
++{
++#ifdef CONFIG_MTD_CFI_AMDSTD_RETRY
++ if ( retry_cmd_cnt == retry_cmd_max ) {
++#endif
++ printk(KERN_WARNING
++ "MTD %s(): Wacky! Unable to decode failure status\n"
++ "Possible buggy device - try "
++#ifdef CONFIG_MTD_CFI_AMDSTD_RETRY
++ "increasing retry_cmd_max from %d\n"
++#else
++ "enabling CONFIG_MTD_CFI_AMDSTD_RETRY\n"
++ "in your kernel config and setting driver retry_cmd_max\n"
++#endif
++ , func
++#ifdef CONFIG_MTD_CFI_AMDSTD_RETRY
++ , retry_cmd_max
++#endif
++ );
++
++ printk(KERN_WARNING
++ "MTD %s(): 0x%.8lx(0x%.8x): 0x%.8x 0x%.8x 0x%.8x 0x%.8x\n",
++ func, adr, datum,
++ prev_oldstatus, prev_status,
++ oldstatus, status);
++#ifdef CONFIG_MTD_CFI_AMDSTD_RETRY
++ }
++#endif
++}
++
++
++static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr, int mode)
+ {
+ DECLARE_WAITQUEUE(wait, current);
+- unsigned long timeo = jiffies + HZ;
++ struct cfi_private *cfi = map->fldrv_priv;
++ cfi_word status, oldstatus;
++ cfi_word dq6 = CMD(1<<6);
++ cfi_word dq2 = CMD(1<<2);
++ unsigned long timeo;
++ struct cfi_pri_amdstd *cfip = (struct cfi_pri_amdstd *)cfi->cmdset_priv;
+
++ resettime:
++ timeo = jiffies + HZ;
+ retry:
++ switch (chip->state) {
++
++ case FL_STATUS:
++ for (;;) {
++ oldstatus = cfi_read(map, adr);
++ status = cfi_read(map, adr);
++ if (((oldstatus ^ status) & (dq6 | dq2)) == 0)
++ break;
++
++ if (time_after(jiffies, timeo)) {
++ printk(KERN_ERR "Waiting for chip to be ready timed out. Status %llx\n",
++ (long long)status);
++ cfi_spin_unlock(chip->mutex);
++ return -EIO;
++ }
++ cfi_spin_unlock(chip->mutex);
++ cfi_udelay(1);
+ cfi_spin_lock(chip->mutex);
++ /* Someone else might have been playing with it. */
++ goto retry;
++ }
+
+- if (chip->state != FL_READY){
+-#if 0
+- printk(KERN_DEBUG "Waiting for chip to read, status = %d\n", chip->state);
+-#endif
+- set_current_state(TASK_UNINTERRUPTIBLE);
+- add_wait_queue(&chip->wq, &wait);
++ case FL_READY:
++ case FL_CFI_QUERY:
++ case FL_JEDEC_QUERY:
++ return 0;
++
++ case FL_ERASING:
++ if (mode == FL_WRITING) /* FIXME: Erase-suspend-program appears broken. */
++ goto sleep;
++
++ if (!(mode == FL_READY || mode == FL_POINT
++ || (mode == FL_WRITING && (cfip->EraseSuspend & 0x2))
++ || (mode == FL_WRITING && (cfip->EraseSuspend & 0x1))))
++ goto sleep;
++
++ oldstatus = cfi_read(map, adr);
++ status = cfi_read(map, adr);
++ if ((oldstatus ^ status) & dq2) {
++ printk(KERN_ERR "Can't suspend erase -- block in progress\n");
++ goto sleep;
++ }
++
++ /* Erase suspend */
++ /* FIXME - is there a way to verify suspend? */
++ cfi_write(map, CMD(0xB0), chip->in_progress_block_addr);
++ chip->oldstate = FL_ERASING;
++ chip->state = FL_ERASE_SUSPENDING;
++ chip->erase_suspended = 1;
++ for (;;) {
++ oldstatus = cfi_read(map, chip->in_progress_block_addr);
++ status = cfi_read(map, chip->in_progress_block_addr);
++ if (((oldstatus ^ status) & dq6) == 0)
++ break;
++
++ if (time_after(jiffies, timeo)) {
++ /* Urgh. Resume and pretend we weren't here. */
++ /* FIXME - is there a way to verify resume? */
++ cfi_write(map, CMD(0x30), chip->in_progress_block_addr);
++ chip->state = FL_ERASING;
++ chip->oldstate = FL_READY;
++ printk(KERN_ERR "Chip not ready after erase "
++ "suspended: status = 0x%x\n", status);
++ return -EIO;
++ }
+
+ cfi_spin_unlock(chip->mutex);
++ cfi_udelay(1);
++ cfi_spin_lock(chip->mutex);
++ /* Nobody will touch it while it's in state FL_ERASE_SUSPENDING.
++ So we can just loop here. */
++ }
++ chip->state = FL_READY;
++ return 0;
++
++ case FL_POINT:
++ /* Only if there's no operation suspended... */
++ if (mode == FL_READY && chip->oldstate == FL_READY)
++ return 0;
+
++ default:
++ sleep:
++ set_current_state(TASK_UNINTERRUPTIBLE);
++ add_wait_queue(&chip->wq, &wait);
++ cfi_spin_unlock(chip->mutex);
+ schedule();
+ remove_wait_queue(&chip->wq, &wait);
+-#if 0
+- if(signal_pending(current))
+- return -EINTR;
+-#endif
+- timeo = jiffies + HZ;
++ cfi_spin_lock(chip->mutex);
++ goto resettime;
++ }
++}
+
+- goto retry;
++
++static void put_chip(struct map_info *map, struct flchip *chip, unsigned long adr)
++{
++ struct cfi_private *cfi = map->fldrv_priv;
++
++ switch(chip->oldstate) {
++ case FL_ERASING:
++ chip->state = chip->oldstate;
++ cfi_write(map, CMD(0x30), chip->in_progress_block_addr);
++ chip->oldstate = FL_READY;
++ chip->state = FL_ERASING;
++ break;
++
++ case FL_READY:
++ case FL_STATUS:
++ /* We should really make set_vpp() count, rather than doing this */
++ DISABLE_VPP(map);
++ break;
++ default:
++ printk(KERN_ERR "MTD: put_chip() called with oldstate %d!!\n", chip->oldstate);
+ }
++ wake_up(&chip->wq);
++}
++
++
++static inline int do_read_onechip(struct map_info *map, struct flchip *chip, loff_t adr, size_t len, u_char *buf)
++{
++ unsigned long cmd_addr;
++ struct cfi_private *cfi = map->fldrv_priv;
++ int ret;
+
+ adr += chip->start;
+
++ /* Ensure cmd read/writes are aligned. */
++ cmd_addr = adr & ~(CFIDEV_BUSWIDTH-1);
++
++ cfi_spin_lock(chip->mutex);
++ ret = get_chip(map, chip, cmd_addr, FL_READY);
++ if (ret) {
++ cfi_spin_unlock(chip->mutex);
++ return ret;
++ }
++
++ if (chip->state != FL_POINT && chip->state != FL_READY) {
++ cfi_write(map, CMD(0xf0), cmd_addr);
+ chip->state = FL_READY;
++ }
+
+- map->copy_from(map, buf, adr, len);
++ map_copy_from(map, buf, adr, len);
+
+- wake_up(&chip->wq);
+- cfi_spin_unlock(chip->mutex);
++ put_chip(map, chip, cmd_addr);
+
++ cfi_spin_unlock(chip->mutex);
+ return 0;
+ }
+
++
+ static int cfi_amdstd_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf)
+ {
+ struct map_info *map = mtd->priv;
+@@ -361,6 +664,7 @@
+ return ret;
+ }
+
++
+ static inline int do_read_secsi_onechip(struct map_info *map, struct flchip *chip, loff_t adr, size_t len, u_char *buf)
+ {
+ DECLARE_WAITQUEUE(wait, current);
+@@ -394,12 +698,14 @@
+
+ chip->state = FL_READY;
+
++ /* should these be CFI_DEVICETYPE_X8 instead of cfi->device_type? */
+ cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
+ cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL);
+ cfi_send_gen_cmd(0x88, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
+
+- map->copy_from(map, buf, adr, len);
++ map_copy_from(map, buf, adr, len);
+
++ /* should these be CFI_DEVICETYPE_X8 instead of cfi->device_type? */
+ cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
+ cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL);
+ cfi_send_gen_cmd(0x90, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
+@@ -454,125 +760,241 @@
+ return ret;
+ }
+
+-static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned long adr, __u32 datum, int fast)
++
++static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned long adr, cfi_word datum, int fast)
+ {
+- unsigned long timeo = jiffies + HZ;
+- unsigned int oldstatus, status;
+- unsigned int dq6, dq5;
+ struct cfi_private *cfi = map->fldrv_priv;
+- DECLARE_WAITQUEUE(wait, current);
++ unsigned long timeo = jiffies + HZ;
++ cfi_word oldstatus, status, prev_oldstatus, prev_status;
++ cfi_word dq6 = CMD(1<<6);
++ /*
++ * We use a 1ms + 1 jiffies generic timeout for writes (most devices
++ * have a max write time of a few hundreds usec). However, we should
++ * use the maximum timeout value given by the chip at probe time
++ * instead. Unfortunately, struct flchip does have a field for
++ * maximum timeout, only for typical which can be far too short
++ * depending of the conditions. The ' + 1' is to avoid having a
++ * timeout of 0 jiffies if HZ is smaller than 1000.
++ */
++ unsigned long uWriteTimeout = ( HZ / 1000 ) + 1;
+ int ret = 0;
++ int ta = 0;
++ DECLARE_RETRY_CMD_CNT();
+
+- retry:
+- cfi_spin_lock(chip->mutex);
+-
+- if (chip->state != FL_READY) {
+-#if 0
+- printk(KERN_DEBUG "Waiting for chip to write, status = %d\n", chip->state);
+-#endif
+- set_current_state(TASK_UNINTERRUPTIBLE);
+- add_wait_queue(&chip->wq, &wait);
++ adr += chip->start;
+
++ cfi_spin_lock(chip->mutex);
++ ret = get_chip(map, chip, adr, FL_WRITING);
++ if (ret) {
+ cfi_spin_unlock(chip->mutex);
+-
+- schedule();
+- remove_wait_queue(&chip->wq, &wait);
+-#if 0
+- printk(KERN_DEBUG "Wake up to write:\n");
+- if(signal_pending(current))
+- return -EINTR;
+-#endif
+- timeo = jiffies + HZ;
+-
+- goto retry;
++ return ret;
+ }
+
+- chip->state = FL_WRITING;
++ RETRY_CMD_LABEL;
++ DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): WRITE 0x%.8lx(0x%.8x)\n",
++ __func__, adr, datum );
++
++ /*
++ * Check for a NOP for the case when the datum to write is already
++ * present - it saves time and works around buggy chips that corrupt
++ * data at other locations when 0xff is written to a location that
++ * already contains 0xff.
++ */
++ status = cfi_read(map, adr);
++ if (status == datum) {
++ DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): NOP 0x%.8x == 0x%.8x\n",
++ __func__, status, datum );
++ goto op_done;
++ }
+
+- adr += chip->start;
+ ENABLE_VPP(map);
+ if (fast) { /* Unlock bypass */
+ cfi_send_gen_cmd(0xA0, 0, chip->start, map, cfi, cfi->device_type, NULL);
+- }
+- else {
++ } else {
++ /*
++ * The CFI_DEVICETYPE_X8 argument is needed even when
++ * cfi->device_type != CFI_DEVICETYPE_X8. The addresses for
++ * command sequences don't scale even when the device is
++ * wider. This is the case for many of the cfi_send_gen_cmd()
++ * below. I'm not sure, however, why some use
++ * cfi->device_type.
++ */
+ cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
+ cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
+ cfi_send_gen_cmd(0xA0, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
+ }
+-
+ cfi_write(map, datum, adr);
++ chip->state = FL_WRITING;
+
+ cfi_spin_unlock(chip->mutex);
+ cfi_udelay(chip->word_write_time);
+ cfi_spin_lock(chip->mutex);
+
+- /* Polling toggle bits instead of reading back many times
+- This ensures that write operation is really completed,
+- or tells us why it failed. */
+- dq6 = CMD(1<<6);
+- dq5 = CMD(1<<5);
+- timeo = jiffies + (HZ/1000); /* setting timeout to 1ms for now */
++ /*
++ * Polling toggle bits instead of reading back many times This ensures
++ * that write operation is really completed, or tells us why it
++ * failed.
++ *
++ * It may appear that the polling and decoding of error state might be
++ * simplified. Don't do it unless you really know what you are doing.
++ *
++ * You must remember that JESD21-C 3.5.3 states that the status must
++ * be read back an _additional_ two times before a failure is
++ * determined. This is because these devices have internal state
++ * machines that are asynchronous to the external data bus. During an
++ * erase or write the read-back status of the polling bits might be
++ * transitioning internaly when the external read-back occurs. This
++ * means that the bits aren't in the final state and they might appear
++ * to report an error as they are in a transient state: dq7 is
++ * asynchronous with dq6 and other status bits.
++ *
++ * This asynchronous behaviour can cause infrequent errors that will
++ * usually disappear the next time an erase or write happens (Try
++ * tracking those errors down!). To ensure that the bits are not in
++ * transition, the location must be read-back two more times and
++ * compared against what was written - BOTH reads MUST match what was
++ * written. Don't think this can be simplified to only the last read
++ * matching the datum written: status bits *can* match the datum
++ * written.
++ *
++ * If the final comparison fails, error state can *then* be decoded.
++ *
++ * - Thayne Harbaugh
++ */
++ /* See comment above for timeout value. */
++ timeo = jiffies + uWriteTimeout;
++ for (;;) {
++ if (chip->state != FL_WRITING) {
++ /* Someone's suspended the write. Sleep */
++ DECLARE_WAITQUEUE(wait, current);
++
++ set_current_state(TASK_UNINTERRUPTIBLE);
++ add_wait_queue(&chip->wq, &wait);
++ cfi_spin_unlock(chip->mutex);
++ schedule();
++ remove_wait_queue(&chip->wq, &wait);
++ timeo = jiffies + (HZ / 2); /* FIXME */
++ cfi_spin_lock(chip->mutex);
++ continue;
++ }
+
+ oldstatus = cfi_read(map, adr);
+ status = cfi_read(map, adr);
++ DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): Check 0x%.8x 0x%.8x\n",
++ __func__, oldstatus, status );
+
+- while( (status & dq6) != (oldstatus & dq6) &&
+- (status & dq5) != dq5 &&
+- !time_after(jiffies, timeo) ) {
++ /*
++ * This only checks if dq6 is still toggling and that our
++ * timer hasn't expired. We purposefully ignore the chip's
++ * internal timer that will assert dq5 and leave dq6 toggling.
++ * This is done for a variety of reasons:
++ *
++ * 1) Not all chips support dq5.
++ *
++ * 2) Dealing with asynchronous status bit and data updates
++ * and reading a device two more times creates _messy_ logic
++ * when trying to deal with interleaved devices - some may be
++ * changing while others are still busy.
++ *
++ * 3) Checking dq5 only helps to optimize an error case that
++ * should at worst be infrequent and at best non-existent.
++ *
++ * If our timeout occurs _then_ we will check dq5 to see if
++ * the device also had an internal timeout.
++ */
++ if ( (((status ^ oldstatus) & dq6) == 0)
++ || ( ta = time_after(jiffies, timeo)) )
++ break;
+
+- if (need_resched()) {
++ /* Latency issues. Drop the lock, wait a while and retry */
+ cfi_spin_unlock(chip->mutex);
+- yield();
++ cfi_udelay(1);
+ cfi_spin_lock(chip->mutex);
+- } else
+- udelay(1);
+-
+- oldstatus = cfi_read( map, adr );
+- status = cfi_read( map, adr );
+ }
+
+- if( (status & dq6) != (oldstatus & dq6) ) {
+- /* The erasing didn't stop?? */
+- if( (status & dq5) == dq5 ) {
+- /* When DQ5 raises, we must check once again
+- if DQ6 is toggling. If not, the erase has been
+- completed OK. If not, reset chip. */
++ /*
++ * Something kicked us out of the read-back loop. We'll check success
++ * befor checking failure. Even though dq6 might be true data, it is
++ * unkown if all of the other bits have changed to true data due to
++ * the asynchronous nature of the internal state machine. We will
++ * read two more times and use this to either verify that the write
++ * completed successfully or that something really went wrong. BOTH
++ * reads must match what was written - this certifies that bits aren't
++ * still changing and that the status bits erroneously match the datum
++ * that was written.
++ */
++ prev_oldstatus = oldstatus;
++ prev_status = status;
+ oldstatus = cfi_read(map, adr);
+ status = cfi_read(map, adr);
++ DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): Check 0x%.8x 0x%.8x\n",
++ __func__, oldstatus, status );
+
+- if ( (oldstatus & 0x00FF) == (status & 0x00FF) ) {
+- printk(KERN_WARNING "Warning: DQ5 raised while program operation was in progress, however operation completed OK\n" );
++ if ( oldstatus == datum && status == datum ) {
++ /* success - do nothing */
++ goto op_done;
++ }
++
++ if ( ta ) {
++ /* Only check dq5 on the chips that are still toggling. */
++ cfi_word dq5mask = ( ( status ^ oldstatus ) & dq6 ) >> 1;
++ if ( status & dq5mask ) {
++ /* dq5 asserted - decode interleave chips */
++ printk( KERN_WARNING
++ "MTD %s(): FLASH internal timeout: 0x%.8x 0x%.8x 0x%8x\n",
++ __func__,
++ status & dq5mask, status, datum );
+ } else {
+- /* DQ5 is active so we can do a reset and stop the erase */
+- cfi_write(map, CMD(0xF0), chip->start);
+- printk(KERN_WARNING "Internal flash device timeout occurred or write operation was performed while flash was programming.\n" );
++ printk( KERN_WARNING
++ "MTD %s(): Software timed out during write.\n",
++ __func__ );
++ }
++ goto op_failed;
+ }
+- } else {
+- printk(KERN_WARNING "Waiting for write to complete timed out in do_write_oneword.");
+
++ /*
++ * If we get to here then it means that something
++ * is wrong and it's not a timeout. Something
++ * is seriously wacky! Dump some debug info.
++ */
++ /*
++ * Found a clue about the chips that reach this state.
++ * Some flash chips (SST >cough<)
++ * are horribly broken. They do not ignore traffic that is
++ * destined to other devices. This happens because some solutions
++ * are on shared busses, the erase and program sequences have
++ * have multiple commands, and the sequence is interspersed with
++ * commands destined to other devices. A good flash chip will
++ * examine the command and destination address and will ignore
++ * commands that are for other devices.
++ */
++ HANDLE_WACKY_STATE();
++
++ op_failed:
++ /* reset on all failures. */
++ cfi_write( map, CMD(0xF0), chip->start );
++ /* FIXME - should have reset delay before continuing */
++ CHECK_RETRIES();
++ ret = -EIO;
++
++ op_done:
+ chip->state = FL_READY;
+- wake_up(&chip->wq);
+- cfi_spin_unlock(chip->mutex);
+- DISABLE_VPP(map);
+- ret = -EIO;
+- }
+- }
+-
+- DISABLE_VPP(map);
+- chip->state = FL_READY;
+- wake_up(&chip->wq);
++ put_chip(map, chip, adr);
+ cfi_spin_unlock(chip->mutex);
+
+ return ret;
+ }
+
+-static int cfi_amdstd_write (struct mtd_info *mtd, loff_t to , size_t len, size_t *retlen, const u_char *buf)
++
++static int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len,
++ size_t *retlen, const u_char *buf)
+ {
+ struct map_info *map = mtd->priv;
+ struct cfi_private *cfi = map->fldrv_priv;
+ int ret = 0;
+ int chipnum;
+ unsigned long ofs, chipstart;
++ DECLARE_WAITQUEUE(wait, current);
+
+ *retlen = 0;
+ if (!len)
+@@ -587,19 +1009,52 @@
+ unsigned long bus_ofs = ofs & ~(CFIDEV_BUSWIDTH-1);
+ int i = ofs - bus_ofs;
+ int n = 0;
+- u_char tmp_buf[4];
+- __u32 datum;
++ u_char tmp_buf[8];
++ cfi_word datum;
++
++ retry:
++ cfi_spin_lock(cfi->chips[chipnum].mutex);
++
++ if (cfi->chips[chipnum].state != FL_READY) {
++#if 0
++ printk(KERN_DEBUG "Waiting for chip to write, status = %d\n", cfi->chips[chipnum].state);
++#endif
++ set_current_state(TASK_UNINTERRUPTIBLE);
++ add_wait_queue(&cfi->chips[chipnum].wq, &wait);
++
++ cfi_spin_unlock(cfi->chips[chipnum].mutex);
++
++ schedule();
++ remove_wait_queue(&cfi->chips[chipnum].wq, &wait);
++#if 0
++ if(signal_pending(current))
++ return -EINTR;
++#endif
++ goto retry;
++ }
++
++ map_copy_from(map, tmp_buf, bus_ofs + cfi->chips[chipnum].start, CFIDEV_BUSWIDTH);
+
+- map->copy_from(map, tmp_buf, bus_ofs + cfi->chips[chipnum].start, CFIDEV_BUSWIDTH);
+- while (len && i < CFIDEV_BUSWIDTH)
+- tmp_buf[i++] = buf[n++], len--;
++ cfi_spin_unlock(cfi->chips[chipnum].mutex);
++
++ while (len && i < CFIDEV_BUSWIDTH) {
++ tmp_buf[i++] = buf[n++];
++ len--;
++ }
+
++ /* already know that buswidth > 1 */
+ if (cfi_buswidth_is_2()) {
+ datum = *(__u16*)tmp_buf;
+ } else if (cfi_buswidth_is_4()) {
+ datum = *(__u32*)tmp_buf;
++#ifdef CFI_WORD_64
++ } else if (cfi_buswidth_is_8()) {
++ datum = *(__u64*)tmp_buf;
++#endif
+ } else {
+- return -EINVAL; /* should never happen, but be safe */
++ printk(KERN_WARNING "MTD %s(): Unsupported buswidth %d\n",
++ __func__, CFIDEV_BUSWIDTH);
++ return -EINVAL;
+ }
+
+ ret = do_write_oneword(map, &cfi->chips[chipnum],
+@@ -628,7 +1083,7 @@
+
+ /* We are now aligned, write as much as possible */
+ while(len >= CFIDEV_BUSWIDTH) {
+- __u32 datum;
++ cfi_word datum;
+
+ if (cfi_buswidth_is_1()) {
+ datum = *(__u8*)buf;
+@@ -636,7 +1091,13 @@
+ datum = *(__u16*)buf;
+ } else if (cfi_buswidth_is_4()) {
+ datum = *(__u32*)buf;
++#ifdef CFI_WORD_64
++ } else if (cfi_buswidth_is_8()) {
++ datum = *(__u64*)buf;
++#endif
+ } else {
++ printk(KERN_WARNING "MTD %s(): Unsupported buswidth %d\n",
++ __func__, CFIDEV_BUSWIDTH);
+ return -EINVAL;
+ }
+ ret = do_write_oneword(map, &cfi->chips[chipnum],
+@@ -685,10 +1146,34 @@
+ /* Write the trailing bytes if any */
+ if (len & (CFIDEV_BUSWIDTH-1)) {
+ int i = 0, n = 0;
+- u_char tmp_buf[4];
+- __u32 datum;
++ u_char tmp_buf[8];
++ cfi_word datum;
++
++ retry1:
++ cfi_spin_lock(cfi->chips[chipnum].mutex);
++
++ if (cfi->chips[chipnum].state != FL_READY) {
++#if 0
++ printk(KERN_DEBUG "Waiting for chip to write, status = %d\n", cfi->chips[chipnum].state);
++#endif
++ set_current_state(TASK_UNINTERRUPTIBLE);
++ add_wait_queue(&cfi->chips[chipnum].wq, &wait);
++
++ cfi_spin_unlock(cfi->chips[chipnum].mutex);
++
++ schedule();
++ remove_wait_queue(&cfi->chips[chipnum].wq, &wait);
++#if 0
++ if(signal_pending(current))
++ return -EINTR;
++#endif
++ goto retry1;
++ }
++
++ map_copy_from(map, tmp_buf, ofs + cfi->chips[chipnum].start, CFIDEV_BUSWIDTH);
++
++ cfi_spin_unlock(cfi->chips[chipnum].mutex);
+
+- map->copy_from(map, tmp_buf, ofs + cfi->chips[chipnum].start, CFIDEV_BUSWIDTH);
+ while (len--)
+ tmp_buf[i++] = buf[n++];
+
+@@ -696,8 +1181,14 @@
+ datum = *(__u16*)tmp_buf;
+ } else if (cfi_buswidth_is_4()) {
+ datum = *(__u32*)tmp_buf;
++#ifdef CFI_WORD_64
++ } else if (cfi_buswidth_is_8()) {
++ datum = *(__u64*)tmp_buf;
++#endif
+ } else {
+- return -EINVAL; /* should never happen, but be safe */
++ printk(KERN_WARNING "MTD %s(): Unsupported buswidth %d\n",
++ __func__, CFIDEV_BUSWIDTH);
++ return -EINVAL;
+ }
+
+ ret = do_write_oneword(map, &cfi->chips[chipnum],
+@@ -711,289 +1202,446 @@
+ return 0;
+ }
+
+-static inline int do_erase_chip(struct map_info *map, struct flchip *chip)
++
++/*
++ * FIXME: interleaved mode not tested, and probably not supported!
++ */
++static inline int do_write_buffer(struct map_info *map, struct flchip *chip,
++ unsigned long adr, const u_char *buf, int len)
+ {
+- unsigned int oldstatus, status;
+- unsigned int dq6, dq5;
+- unsigned long timeo = jiffies + HZ;
+- unsigned int adr;
+ struct cfi_private *cfi = map->fldrv_priv;
+- DECLARE_WAITQUEUE(wait, current);
+-
+- retry:
+- cfi_spin_lock(chip->mutex);
++ unsigned long timeo = jiffies + HZ;
++ cfi_word oldstatus, status, prev_oldstatus, prev_status;
++ cfi_word dq6 = CMD(1<<6);
++ /* see comments in do_write_oneword() regarding uWriteTimeo. */
++ static unsigned long uWriteTimeout = ( HZ / 1000 ) + 1;
++ int ret = -EIO;
++ int ta = 0;
++ unsigned long cmd_adr;
++ int z, bytes, words;
++ cfi_word datum;
++ DECLARE_RETRY_CMD_CNT();
+
+- if (chip->state != FL_READY){
+- set_current_state(TASK_UNINTERRUPTIBLE);
+- add_wait_queue(&chip->wq, &wait);
++ adr += chip->start;
++ cmd_adr = adr;
+
++ cfi_spin_lock(chip->mutex);
++ ret = get_chip(map, chip, adr, FL_WRITING);
++ if (ret) {
+ cfi_spin_unlock(chip->mutex);
++ return ret;
++ }
+
+- schedule();
+- remove_wait_queue(&chip->wq, &wait);
+-#if 0
+- if(signal_pending(current))
+- return -EINTR;
++ if (cfi_buswidth_is_1()) {
++ datum = *(__u8*)buf;
++ } else if (cfi_buswidth_is_2()) {
++ datum = *(__u16*)buf;
++ } else if (cfi_buswidth_is_4()) {
++ datum = *(__u32*)buf;
++#ifdef CFI_WORD_64
++ } else if (cfi_buswidth_is_8()) {
++ datum = *(__u64*)buf;
+ #endif
+- timeo = jiffies + HZ;
+-
+- goto retry;
++ } else {
++ printk(KERN_WARNING "MTD %s(): Unsupported buswidth %d\n",
++ __func__, CFIDEV_BUSWIDTH);
++ return -EINVAL;
+ }
+
+- chip->state = FL_ERASING;
++ RETRY_CMD_LABEL;
++ DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): WRITE 0x%.8lx(0x%.8x)\n",
++ __func__, adr, datum );
+
+- /* Handle devices with one erase region, that only implement
+- * the chip erase command.
+- */
+ ENABLE_VPP(map);
+ cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
+ cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
+- cfi_send_gen_cmd(0x80, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
+- cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
+- cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
+- cfi_send_gen_cmd(0x10, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
+- timeo = jiffies + (HZ*20);
+- adr = cfi->addr_unlock1;
++ //cfi_send_gen_cmd(0xA0, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
+
+- /* Wait for the end of programing/erasure by using the toggle method.
+- * As long as there is a programming procedure going on, bit 6 of the last
+- * written byte is toggling it's state with each consectuve read.
+- * The toggling stops as soon as the procedure is completed.
+- *
+- * If the process has gone on for too long on the chip bit 5 gets.
+- * After bit5 is set you can kill the operation by sending a reset
+- * command to the chip.
+- */
+- dq6 = CMD(1<<6);
+- dq5 = CMD(1<<5);
++ /* Write Buffer Load */
++ cfi_write(map, CMD(0x25), cmd_adr);
+
+- oldstatus = cfi_read(map, adr);
+- status = cfi_read(map, adr);
+- while( ((status & dq6) != (oldstatus & dq6)) &&
+- ((status & dq5) != dq5) &&
+- !time_after(jiffies, timeo)) {
+- int wait_reps;
++ chip->state = FL_WRITING_TO_BUFFER;
++
++ /* Write length of data to come */
++ bytes = len & (CFIDEV_BUSWIDTH-1);
++ words = len / CFIDEV_BUSWIDTH;
++ cfi_write(map, CMD(words - !bytes), cmd_adr );
++ /* Write data */
++ z = 0;
++ while(z < words * CFIDEV_BUSWIDTH) {
++ if (cfi_buswidth_is_1()) {
++ datum = *((__u8*)buf);
++ map_write8 (map, *((__u8*)buf)++, adr+z);
++ } else if (cfi_buswidth_is_2()) {
++ datum = *((__u16*)buf);
++ map_write16 (map, *((__u16*)buf)++, adr+z);
++ } else if (cfi_buswidth_is_4()) {
++ datum = *((__u32*)buf);
++ map_write32 (map, *((__u32*)buf)++, adr+z);
++#ifdef CFI_WORD_64
++ } else if (cfi_buswidth_is_8()) {
++ datum = *((__u64*)buf);
++ map_write64 (map, *((__u64*)buf)++, adr+z);
++#endif
++ } else {
++ printk(KERN_WARNING "MTD %s(): Unsupported buswidth %d\n",
++ __func__, CFIDEV_BUSWIDTH);
++ ret = -EINVAL;
++ goto op_failed;
++ }
++ z += CFIDEV_BUSWIDTH;
++ }
++ if (bytes) {
++ int i = 0, n = 0;
++ u_char tmp_buf[8], *tmp_p = tmp_buf;
++
++ while (bytes--)
++ tmp_buf[i++] = buf[n++];
++ while (i < CFIDEV_BUSWIDTH)
++ tmp_buf[i++] = 0xff;
++ if (cfi_buswidth_is_2()) {
++ datum = *((__u16*)tmp_p);
++ map_write16 (map, *((__u16*)tmp_p)++, adr+z);
++ } else if (cfi_buswidth_is_4()) {
++ datum = *((__u32*)tmp_p);
++ map_write32 (map, *((__u32*)tmp_p)++, adr+z);
++#ifdef CFI_WORD_64
++ } else if (cfi_buswidth_is_8()) {
++ datum = *((__u64*)tmp_p);
++ map_write64 (map, *((__u64*)tmp_p)++, adr+z);
++#endif
++ } else {
++ printk(KERN_WARNING "MTD %s(): Unsupported buswidth %d\n",
++ __func__, CFIDEV_BUSWIDTH);
++ ret = -EINVAL;
++ goto op_failed;
++ }
++ } else if (words > 0) {
++ z -= CFIDEV_BUSWIDTH;
++ }
++
++ adr += z;
++
++ /* Write Buffer Program Confirm: GO GO GO */
++ cfi_write(map, CMD(0x29), cmd_adr);
++ chip->state = FL_WRITING;
+
+- /* an initial short sleep */
+ cfi_spin_unlock(chip->mutex);
+- schedule_timeout(HZ/100);
++ cfi_udelay(chip->buffer_write_time);
+ cfi_spin_lock(chip->mutex);
+
+- if (chip->state != FL_ERASING) {
+- /* Someone's suspended the erase. Sleep */
++ timeo = jiffies + uWriteTimeout;
++
++ for (;;) {
++ if (chip->state != FL_WRITING) {
++ /* Someone's suspended the write. Sleep */
++ DECLARE_WAITQUEUE(wait, current);
++
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ add_wait_queue(&chip->wq, &wait);
+-
+ cfi_spin_unlock(chip->mutex);
+- printk("erase suspended. Sleeping\n");
+-
+ schedule();
+ remove_wait_queue(&chip->wq, &wait);
+-#if 0
+- if (signal_pending(current))
+- return -EINTR;
+-#endif
+- timeo = jiffies + (HZ*2); /* FIXME */
++ timeo = jiffies + (HZ / 2); /* FIXME */
+ cfi_spin_lock(chip->mutex);
+ continue;
+ }
+
+- /* Busy wait for 1/10 of a milisecond */
+- for(wait_reps = 0;
+- (wait_reps < 100) &&
+- ((status & dq6) != (oldstatus & dq6)) &&
+- ((status & dq5) != dq5);
+- wait_reps++) {
++ oldstatus = cfi_read(map, adr);
++ status = cfi_read(map, adr);
++ DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): Check 0x%.8x 0x%.8x\n",
++ __func__, oldstatus, status );
++
++ /* See comments in do_write_oneword() about checking status */
++ if ( (((status ^ oldstatus) & dq6) == 0)
++ || ( ta = time_after(jiffies, timeo)) ) {
++ break;
++ }
+
+ /* Latency issues. Drop the lock, wait a while and retry */
+ cfi_spin_unlock(chip->mutex);
+-
+ cfi_udelay(1);
+-
+ cfi_spin_lock(chip->mutex);
+- oldstatus = cfi_read(map, adr);
+- status = cfi_read(map, adr);
+ }
++
++ /* See comments in do_write_oneword() about "two more checks" */
++ prev_oldstatus = oldstatus;
++ prev_status = status;
+ oldstatus = cfi_read(map, adr);
+ status = cfi_read(map, adr);
++ DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): Check 0x%.8x 0x%.8x\n",
++ __func__, oldstatus, status );
++
++ if ( oldstatus == datum && status == datum ) {
++ /* success - do nothing */
++ goto op_done;
++ }
++
++ if ( ta ) {
++ /* Only check dq5 on the chips that are still toggling. */
++ cfi_word dq5mask = ( ( status ^ oldstatus ) & dq6 ) >> 1;
++ if ( status & dq5mask ) {
++ /* dq5 asserted - decode interleave chips */
++ printk( KERN_WARNING
++ "MTD %s(): FLASH internal timeout: 0x%.8x 0x%.8x 0x%8x\n",
++ __func__,
++ status & dq5mask, status, datum );
++ } else {
++ printk( KERN_WARNING
++ "MTD %s(): Software timed out during write.\n",
++ __func__ );
+ }
+- if ((status & dq6) != (oldstatus & dq6)) {
+- /* The erasing didn't stop?? */
+- if ((status & dq5) == dq5) {
+- /* dq5 is active so we can do a reset and stop the erase */
+- cfi_write(map, CMD(0xF0), chip->start);
++ goto op_failed;
+ }
++
++ HANDLE_WACKY_STATE();
++
++ op_failed:
++ /* reset on all failures. */
++ cfi_write( map, CMD(0xF0), chip->start );
++ /* FIXME - should have reset delay before continuing */
++ CHECK_RETRIES();
++ ret = -EIO;
++
++ op_done:
+ chip->state = FL_READY;
+- wake_up(&chip->wq);
++ put_chip(map, chip, adr);
+ cfi_spin_unlock(chip->mutex);
+- printk("waiting for erase to complete timed out.");
+- DISABLE_VPP(map);
+- return -EIO;
++
++ return ret;
++}
++
++
++static int cfi_amdstd_write_buffers(struct mtd_info *mtd, loff_t to, size_t len,
++ size_t *retlen, const u_char *buf)
++{
++ struct map_info *map = mtd->priv;
++ struct cfi_private *cfi = map->fldrv_priv;
++ int wbufsize = CFIDEV_INTERLEAVE << cfi->cfiq->MaxBufWriteSize;
++ int ret = 0;
++ int chipnum;
++ unsigned long ofs;
++
++ *retlen = 0;
++ if (!len)
++ return 0;
++
++ chipnum = to >> cfi->chipshift;
++ ofs = to - (chipnum << cfi->chipshift);
++
++ /* If it's not bus-aligned, do the first word write */
++ if (ofs & (CFIDEV_BUSWIDTH-1)) {
++ size_t local_len = (-ofs)&(CFIDEV_BUSWIDTH-1);
++ if (local_len > len)
++ local_len = len;
++ ret = cfi_amdstd_write_words(mtd, to, local_len,
++ retlen, buf);
++ if (ret)
++ return ret;
++ ofs += local_len;
++ buf += local_len;
++ len -= local_len;
++
++ if (ofs >> cfi->chipshift) {
++ chipnum ++;
++ ofs = 0;
++ if (chipnum == cfi->numchips)
++ return 0;
++ }
++ }
++
++ /* Write buffer is worth it only if more than one word to write... */
++ while (len) {
++ /* We must not cross write block boundaries */
++ int size = wbufsize - (ofs & (wbufsize-1));
++
++ if (size > len)
++ size = len;
++ ret = do_write_buffer(map, &cfi->chips[chipnum],
++ ofs, buf, size);
++ if (ret)
++ return ret;
++
++ ofs += size;
++ buf += size;
++ (*retlen) += size;
++ len -= size;
++
++ if (ofs >> cfi->chipshift) {
++ chipnum ++;
++ ofs = 0;
++ if (chipnum == cfi->numchips)
++ return 0;
++ }
+ }
+- DISABLE_VPP(map);
+- chip->state = FL_READY;
+- wake_up(&chip->wq);
+- cfi_spin_unlock(chip->mutex);
+
+ return 0;
+ }
+
+-static inline int do_erase_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr)
++
++/*
++ * Handle devices with one erase region, that only implement
++ * the chip erase command.
++ */
++static inline int do_erase_chip(struct map_info *map, struct flchip *chip)
+ {
+- unsigned int oldstatus, status;
+- unsigned int dq6, dq5;
+- unsigned long timeo = jiffies + HZ;
+ struct cfi_private *cfi = map->fldrv_priv;
++ cfi_word oldstatus, status, prev_oldstatus, prev_status;
++ cfi_word dq6 = CMD(1<<6);
++ unsigned long timeo = jiffies + HZ;
++ unsigned long int adr;
+ DECLARE_WAITQUEUE(wait, current);
++ int ret = 0;
++ int ta = 0;
++ cfi_word datum = 0;
++ DECLARE_RETRY_CMD_CNT();
+
+- retry:
+- cfi_spin_lock(chip->mutex);
+-
+- if (chip->state != FL_READY){
+- set_current_state(TASK_UNINTERRUPTIBLE);
+- add_wait_queue(&chip->wq, &wait);
++ adr = cfi->addr_unlock1;
+
++ cfi_spin_lock(chip->mutex);
++ ret = get_chip(map, chip, adr, FL_WRITING);
++ if (ret) {
+ cfi_spin_unlock(chip->mutex);
+-
+- schedule();
+- remove_wait_queue(&chip->wq, &wait);
+-#if 0
+- if(signal_pending(current))
+- return -EINTR;
+-#endif
+- timeo = jiffies + HZ;
+-
+- goto retry;
++ return ret;
+ }
+
+- chip->state = FL_ERASING;
++ RETRY_CMD_LABEL;
++ DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): ERASE 0x%.8lx\n",
++ __func__, chip->start );
+
+- adr += chip->start;
+ ENABLE_VPP(map);
+ cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
+ cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
+ cfi_send_gen_cmd(0x80, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
+ cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
+ cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
+- cfi_write(map, CMD(0x30), adr);
+-
+- timeo = jiffies + (HZ*20);
+-
+- /* Wait for the end of programing/erasure by using the toggle method.
+- * As long as there is a programming procedure going on, bit 6 of the last
+- * written byte is toggling it's state with each consectuve read.
+- * The toggling stops as soon as the procedure is completed.
+- *
+- * If the process has gone on for too long on the chip bit 5 gets.
+- * After bit5 is set you can kill the operation by sending a reset
+- * command to the chip.
+- */
+- dq6 = CMD(1<<6);
+- dq5 = CMD(1<<5);
++ cfi_send_gen_cmd(0x10, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
+
+- oldstatus = cfi_read(map, adr);
+- status = cfi_read(map, adr);
+- while( ((status & dq6) != (oldstatus & dq6)) &&
+- ((status & dq5) != dq5) &&
+- !time_after(jiffies, timeo)) {
+- int wait_reps;
++ chip->state = FL_ERASING;
++ chip->erase_suspended = 0;
++ chip->in_progress_block_addr = adr;
+
+- /* an initial short sleep */
+ cfi_spin_unlock(chip->mutex);
+- schedule_timeout(HZ/100);
++ set_current_state(TASK_UNINTERRUPTIBLE);
++ schedule_timeout((chip->erase_time*HZ)/(2*1000));
+ cfi_spin_lock(chip->mutex);
+
++ timeo = jiffies + (HZ*20);
++
++ for (;;) {
+ if (chip->state != FL_ERASING) {
+ /* Someone's suspended the erase. Sleep */
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ add_wait_queue(&chip->wq, &wait);
+-
+ cfi_spin_unlock(chip->mutex);
+- printk(KERN_DEBUG "erase suspended. Sleeping\n");
+-
+ schedule();
+ remove_wait_queue(&chip->wq, &wait);
+-#if 0
+- if (signal_pending(current))
+- return -EINTR;
+-#endif
+- timeo = jiffies + (HZ*2); /* FIXME */
+ cfi_spin_lock(chip->mutex);
+ continue;
+ }
++ if (chip->erase_suspended) {
++ /* This erase was suspended and resumed.
++ Adjust the timeout */
++ timeo = jiffies + (HZ*20); /* FIXME */
++ chip->erase_suspended = 0;
++ }
+
+- /* Busy wait for 1/10 of a milisecond */
+- for(wait_reps = 0;
+- (wait_reps < 100) &&
+- ((status & dq6) != (oldstatus & dq6)) &&
+- ((status & dq5) != dq5);
+- wait_reps++) {
++ oldstatus = cfi_read(map, adr);
++ status = cfi_read(map, adr);
++ DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): Check 0x%.8x 0x%.8x\n",
++ __func__, oldstatus, status );
++ if ( (((status ^ oldstatus) & dq6) == 0)
++ || ( ta = time_after(jiffies, timeo)) )
++ break;
+
+ /* Latency issues. Drop the lock, wait a while and retry */
+ cfi_spin_unlock(chip->mutex);
+-
+- cfi_udelay(1);
+-
++ set_current_state(TASK_UNINTERRUPTIBLE);
++ schedule_timeout(1);
+ cfi_spin_lock(chip->mutex);
+- oldstatus = cfi_read(map, adr);
+- status = cfi_read(map, adr);
+ }
++
++ prev_oldstatus = oldstatus;
++ prev_status = status;
+ oldstatus = cfi_read(map, adr);
+ status = cfi_read(map, adr);
+- }
+- if( (status & dq6) != (oldstatus & dq6) )
+- {
+- /* The erasing didn't stop?? */
+- if( ( status & dq5 ) == dq5 )
+- {
+- /* When DQ5 raises, we must check once again if DQ6 is toggling.
+- If not, the erase has been completed OK. If not, reset chip. */
+- oldstatus = cfi_read( map, adr );
+- status = cfi_read( map, adr );
++ DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): Check 0x%.8x 0x%.8x\n",
++ __func__, oldstatus, status );
+
+- if( ( oldstatus & 0x00FF ) == ( status & 0x00FF ) )
+- {
+- printk( "Warning: DQ5 raised while erase operation was in progress, but erase completed OK\n" );
+- }
+- else
+- {
+- /* DQ5 is active so we can do a reset and stop the erase */
+- cfi_write(map, CMD(0xF0), chip->start);
+- printk( KERN_WARNING "Internal flash device timeout occured or write operation was performed while flash was erasing\n" );
++ if ( cfi_buswidth_is_1() ) {
++ datum = (__u8)~0;
++ } else if ( cfi_buswidth_is_2() ) {
++ datum = (__u16)~0;
++ } else if ( cfi_buswidth_is_4() ) {
++ datum = (__u32)~0;
++#ifdef CFI_WORD_64
++ } else if ( cfi_buswidth_is_8() ) {
++ datum = (__u64)~0;
++#endif
++ } else {
++ printk(KERN_WARNING "MTD %s(): Unsupported buswidth %d\n",
++ __func__, CFIDEV_BUSWIDTH);
++ goto op_failed;
++ }
++
++ if ( oldstatus == datum && status == datum ) {
++ /* success - do nothing */
++ goto op_done;
++ }
++
++ if ( ta ) {
++ /* Only check dq5 on the chips that are still toggling. */
++ cfi_word dq5mask = ( ( status ^ oldstatus ) & dq6 ) >> 1;
++ if ( status & dq5mask ) {
++ /* dq5 asserted - decode interleave chips */
++ printk( KERN_WARNING
++ "MTD %s(): FLASH internal timeout: 0x%.8x\n",
++ __func__,
++ status & dq5mask );
++ } else {
++ printk( KERN_WARNING
++ "MTD %s(): Software timed out during write.\n",
++ __func__ );
+ }
++ goto op_failed;
+ }
+- else
+- {
+- printk( "Waiting for erase to complete timed out in do_erase_oneblock.");
+
+- chip->state = FL_READY;
+- wake_up(&chip->wq);
+- cfi_spin_unlock(chip->mutex);
+- DISABLE_VPP(map);
+- return -EIO;
+- }
+- }
++ HANDLE_WACKY_STATE();
+
+- DISABLE_VPP(map);
++ op_failed:
++ /* reset on all failures. */
++ cfi_write( map, CMD(0xF0), chip->start );
++ /* FIXME - should have reset delay before continuing */
++ CHECK_RETRIES();
++ ret = -EIO;
++
++ op_done:
+ chip->state = FL_READY;
+- wake_up(&chip->wq);
++ put_chip(map, chip, adr);
+ cfi_spin_unlock(chip->mutex);
+- return 0;
++
++ return ret;
+ }
+
+-static int cfi_amdstd_erase_varsize(struct mtd_info *mtd, struct erase_info *instr)
++
++typedef int (*frob_t)(struct map_info *map, struct flchip *chip,
++ unsigned long adr, void *thunk);
++
++
++static int cfi_amdstd_varsize_frob(struct mtd_info *mtd, frob_t frob,
++ loff_t ofs, size_t len, void *thunk)
+ {
+ struct map_info *map = mtd->priv;
+ struct cfi_private *cfi = map->fldrv_priv;
+- unsigned long adr, len;
++ unsigned long adr;
+ int chipnum, ret = 0;
+ int i, first;
+ struct mtd_erase_region_info *regions = mtd->eraseregions;
+
+- if (instr->addr > mtd->size)
++ if (ofs > mtd->size)
+ return -EINVAL;
+
+- if ((instr->len + instr->addr) > mtd->size)
++ if ((len + ofs) > mtd->size)
+ return -EINVAL;
+
+ /* Check that both start and end of the requested erase are
+@@ -1008,7 +1656,7 @@
+ start of the requested erase, and then go back one.
+ */
+
+- while (i < mtd->numeraseregions && instr->addr >= regions[i].offset)
++ while (i < mtd->numeraseregions && ofs >= regions[i].offset)
+ i++;
+ i--;
+
+@@ -1018,7 +1666,7 @@
+ effect here.
+ */
+
+- if (instr->addr & (regions[i].erasesize-1))
++ if (ofs & (regions[i].erasesize-1))
+ return -EINVAL;
+
+ /* Remember the erase region we start on */
+@@ -1028,7 +1676,7 @@
+ * with the erase region at that address.
+ */
+
+- while (i<mtd->numeraseregions && (instr->addr + instr->len) >= regions[i].offset)
++ while (i<mtd->numeraseregions && (ofs + len) >= regions[i].offset)
+ i++;
+
+ /* As before, drop back one to point at the region in which
+@@ -1036,17 +1684,16 @@
+ */
+ i--;
+
+- if ((instr->addr + instr->len) & (regions[i].erasesize-1))
++ if ((ofs + len) & (regions[i].erasesize-1))
+ return -EINVAL;
+
+- chipnum = instr->addr >> cfi->chipshift;
+- adr = instr->addr - (chipnum << cfi->chipshift);
+- len = instr->len;
++ chipnum = ofs >> cfi->chipshift;
++ adr = ofs - (chipnum << cfi->chipshift);
+
+ i=first;
+
+- while(len) {
+- ret = do_erase_oneblock(map, &cfi->chips[chipnum], adr);
++ while (len) {
++ ret = (*frob)(map, &cfi->chips[chipnum], adr, thunk);
+
+ if (ret)
+ return ret;
+@@ -1066,50 +1713,171 @@
+ }
+ }
+
+- instr->state = MTD_ERASE_DONE;
+- if (instr->callback)
+- instr->callback(instr);
+-
+ return 0;
+ }
+
+-static int cfi_amdstd_erase_onesize(struct mtd_info *mtd, struct erase_info *instr)
++
++static inline int do_erase_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr, void *thunk)
+ {
+- struct map_info *map = mtd->priv;
+ struct cfi_private *cfi = map->fldrv_priv;
+- unsigned long adr, len;
+- int chipnum, ret = 0;
++ cfi_word oldstatus, status, prev_oldstatus, prev_status;
++ cfi_word dq6 = CMD(1<<6);
++ unsigned long timeo = jiffies + HZ;
++ DECLARE_WAITQUEUE(wait, current);
++ int ret = 0;
++ int ta = 0;
++ cfi_word datum = 0;
++ DECLARE_RETRY_CMD_CNT();
+
+- if (instr->addr & (mtd->erasesize - 1))
+- return -EINVAL;
++ adr += chip->start;
+
+- if (instr->len & (mtd->erasesize -1))
+- return -EINVAL;
++ cfi_spin_lock(chip->mutex);
++ ret = get_chip(map, chip, adr, FL_ERASING);
++ if (ret) {
++ cfi_spin_unlock(chip->mutex);
++ return ret;
++ }
+
+- if ((instr->len + instr->addr) > mtd->size)
+- return -EINVAL;
++ RETRY_CMD_LABEL;
++ DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): ERASE 0x%.8lx\n",
++ __func__, adr );
+
+- chipnum = instr->addr >> cfi->chipshift;
+- adr = instr->addr - (chipnum << cfi->chipshift);
+- len = instr->len;
++ ENABLE_VPP(map);
++ cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
++ cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
++ cfi_send_gen_cmd(0x80, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
++ cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
++ cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
++ cfi_write(map, CMD(0x30), adr);
+
+- while(len) {
+- ret = do_erase_oneblock(map, &cfi->chips[chipnum], adr);
++ chip->state = FL_ERASING;
++ chip->erase_suspended = 0;
++ chip->in_progress_block_addr = adr;
+
+- if (ret)
+- return ret;
++ cfi_spin_unlock(chip->mutex);
++ set_current_state(TASK_UNINTERRUPTIBLE);
++ schedule_timeout((chip->erase_time*HZ)/(2*1000));
++ cfi_spin_lock(chip->mutex);
+
+- adr += mtd->erasesize;
+- len -= mtd->erasesize;
++ timeo = jiffies + (HZ*20);
+
+- if (adr >> cfi->chipshift) {
+- adr = 0;
+- chipnum++;
++ /* Wait for the end of programing/erasure by using the toggle method.
++ * As long as there is a programming procedure going on, bit 6 is
++ * toggling its state with each consecutive read. The toggling stops
++ * as soon as the procedure is completed.
++ *
++ * If the process has gone on for too long on the chip, bit 5 gets
++ * set. After bit5 is set you can kill the operation by sending a
++ * reset command to the chip.
++ */
++ /* See comments in do_write_oneword(). */
+
+- if (chipnum >= cfi->numchips)
++ for (;;) {
++ if (chip->state != FL_ERASING) {
++ /* Someone's suspended the erase. Sleep */
++ set_current_state(TASK_UNINTERRUPTIBLE);
++ add_wait_queue(&chip->wq, &wait);
++ cfi_spin_unlock(chip->mutex);
++ schedule();
++ remove_wait_queue(&chip->wq, &wait);
++ cfi_spin_lock(chip->mutex);
++ continue;
++ }
++ if (chip->erase_suspended) {
++ /* This erase was suspended and resumed.
++ Adjust the timeout */
++ timeo = jiffies + (HZ*20); /* FIXME */
++ chip->erase_suspended = 0;
++ }
++
++ oldstatus = cfi_read(map, adr);
++ status = cfi_read(map, adr);
++ DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): Check 0x%.8x 0x%.8x\n",
++ __func__, oldstatus, status );
++ if ( (((status ^ oldstatus) & dq6) == 0)
++ || ( ta = time_after(jiffies, timeo)) )
+ break;
++
++ /* Latency issues. Drop the lock, wait a while and retry */
++ cfi_spin_unlock(chip->mutex);
++ set_current_state(TASK_UNINTERRUPTIBLE);
++ schedule_timeout(1);
++ cfi_spin_lock(chip->mutex);
+ }
++
++ prev_oldstatus = oldstatus;
++ prev_status = status;
++ oldstatus = cfi_read(map, adr);
++ status = cfi_read(map, adr);
++ DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): Check 0x%.8x 0x%.8x\n",
++ __func__, oldstatus, status );
++
++ if ( cfi_buswidth_is_1() ) {
++ datum = (__u8)~0;
++ } else if ( cfi_buswidth_is_2() ) {
++ datum = (__u16)~0;
++ } else if ( cfi_buswidth_is_4() ) {
++ datum = (__u32)~0;
++#ifdef CFI_WORD_64
++ } else if ( cfi_buswidth_is_8() ) {
++ datum = (__u64)~0;
++#endif
++ } else {
++ printk(KERN_WARNING "MTD %s(): Unsupported buswidth %d\n",
++ __func__, CFIDEV_BUSWIDTH);
++ goto op_failed;
++ }
++
++ if ( oldstatus == datum && status == datum ) {
++ /* success - do nothing */
++ goto op_done;
++ }
++
++ if ( ta ) {
++ /* Only check dq5 on the chips that are still toggling. */
++ cfi_word dq5mask = ( ( status ^ oldstatus ) & dq6 ) >> 1;
++ if ( status & dq5mask ) {
++ /* dq5 asserted - decode interleave chips */
++ printk( KERN_WARNING
++ "MTD %s(): FLASH internal timeout: 0x%.8x\n",
++ __func__,
++ status & dq5mask );
++ } else {
++ printk( KERN_WARNING
++ "MTD %s(): Software timed out during write.\n",
++ __func__ );
+ }
++ goto op_failed;
++ }
++
++ HANDLE_WACKY_STATE();
++
++ op_failed:
++ /* reset on all failures. */
++ cfi_write( map, CMD(0xF0), chip->start );
++ /* FIXME - should have reset delay before continuing */
++ CHECK_RETRIES();
++ ret = -EIO;
++
++ op_done:
++ chip->state = FL_READY;
++ put_chip(map, chip, adr);
++ cfi_spin_unlock(chip->mutex);
++ return ret;
++}
++
++
++int cfi_amdstd_erase_varsize(struct mtd_info *mtd, struct erase_info *instr)
++{
++ unsigned long ofs, len;
++ int ret;
++
++ ofs = instr->addr;
++ len = instr->len;
++
++ ret = cfi_amdstd_varsize_frob(mtd, do_erase_oneblock, ofs, len, 0);
++ if (ret)
++ return ret;
+
+ instr->state = MTD_ERASE_DONE;
+ if (instr->callback)
+@@ -1118,6 +1886,7 @@
+ return 0;
+ }
+
++
+ static int cfi_amdstd_erase_chip(struct mtd_info *mtd, struct erase_info *instr)
+ {
+ struct map_info *map = mtd->priv;
+@@ -1141,6 +1910,7 @@
+ return 0;
+ }
+
++
+ static void cfi_amdstd_sync (struct mtd_info *mtd)
+ {
+ struct map_info *map = mtd->priv;
+@@ -1254,6 +2024,7 @@
+ return ret;
+ }
+
++
+ static void cfi_amdstd_resume(struct mtd_info *mtd)
+ {
+ struct map_info *map = mtd->priv;
+@@ -1279,6 +2050,137 @@
+ }
+ }
+
++
++#ifdef DEBUG_LOCK_BITS
++
++static int do_printlockstatus_oneblock(struct map_info *map,
++ struct flchip *chip,
++ unsigned long adr,
++ void *thunk)
++{
++ struct cfi_private *cfi = map->fldrv_priv;
++ int ofs_factor = cfi->interleave * cfi->device_type;
++
++ cfi_send_gen_cmd(0x90, 0x55, 0, map, cfi, cfi->device_type, NULL);
++ printk(KERN_DEBUG "block status register for 0x%08lx is %x\n",
++ adr, cfi_read_query(map, adr+(2*ofs_factor)));
++ cfi_send_gen_cmd(0xff, 0x55, 0, map, cfi, cfi->device_type, NULL);
++
++ return 0;
++}
++
++
++#define debug_dump_locks(mtd, frob, ofs, len, thunk) \
++ cfi_amdstd_varsize_frob((mtd), (frob), (ofs), (len), (thunk))
++
++#else
++
++#define debug_dump_locks(...)
++
++#endif /* DEBUG_LOCK_BITS */
++
++
++struct xxlock_thunk {
++ cfi_word val;
++ flstate_t state;
++};
++
++
++#define DO_XXLOCK_ONEBLOCK_LOCK ((struct xxlock_thunk){0x01, FL_LOCKING})
++#define DO_XXLOCK_ONEBLOCK_UNLOCK ((struct xxlock_thunk){0x00, FL_UNLOCKING})
++
++
++/*
++ * FIXME - this is *very* specific to a particular chip. It likely won't
++ * work for all chips that require unlock. It also hasn't been tested
++ * with interleaved chips.
++ */
++static int do_xxlock_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr, void *thunk)
++{
++ struct cfi_private *cfi = map->fldrv_priv;
++ struct xxlock_thunk *xxlt = (struct xxlock_thunk *)thunk;
++ int ret;
++
++ /*
++ * This is easy because these are writes to registers and not writes
++ * to flash memory - that means that we don't have to check status
++ * and timeout.
++ */
++
++ adr += chip->start;
++ /*
++ * lock block registers:
++ * - on 64k boundariesand
++ * - bit 1 set high
++ * - block lock registers are 4MiB lower - overflow subtract (danger)
++ */
++ adr = ((adr & ~0xffff) | 0x2) + ~0x3fffff;
++
++ cfi_spin_lock(chip->mutex);
++ ret = get_chip(map, chip, adr, FL_LOCKING);
++ if (ret) {
++ cfi_spin_unlock(chip->mutex);
++ return ret;
++ }
++
++ chip->state = xxlt->state;
++ cfi_write(map, CMD(xxlt->val), adr);
++
++ /* Done and happy. */
++ chip->state = FL_READY;
++ put_chip(map, chip, adr);
++ cfi_spin_unlock(chip->mutex);
++ return 0;
++}
++
++
++static int cfi_amdstd_lock_varsize(struct mtd_info *mtd,
++ loff_t ofs,
++ size_t len)
++{
++ int ret;
++
++ DEBUG(MTD_DEBUG_LEVEL3,
++ "%s: lock status before, ofs=0x%08llx, len=0x%08X\n",
++ __func__, ofs, len);
++ debug_dump_locks(mtd, do_printlockstatus_oneblock, ofs, len, 0);
++
++ ret = cfi_amdstd_varsize_frob(mtd, do_xxlock_oneblock, ofs, len,
++ (void *)&DO_XXLOCK_ONEBLOCK_LOCK);
++
++ DEBUG(MTD_DEBUG_LEVEL3,
++ "%s: lock status after, ret=%d\n",
++ __func__, ret);
++
++ debug_dump_locks(mtd, do_printlockstatus_oneblock, ofs, len, 0);
++
++ return ret;
++}
++
++
++static int cfi_amdstd_unlock_varsize(struct mtd_info *mtd,
++ loff_t ofs,
++ size_t len)
++{
++ int ret;
++
++ DEBUG(MTD_DEBUG_LEVEL3,
++ "%s: lock status before, ofs=0x%08llx, len=0x%08X\n",
++ __func__, ofs, len);
++ debug_dump_locks(mtd, do_printlockstatus_oneblock, ofs, len, 0);
++
++ ret = cfi_amdstd_varsize_frob(mtd, do_xxlock_oneblock, ofs, len,
++ (void *)&DO_XXLOCK_ONEBLOCK_UNLOCK);
++
++ DEBUG(MTD_DEBUG_LEVEL3,
++ "%s: lock status after, ret=%d\n",
++ __func__, ret);
++ debug_dump_locks(mtd, do_printlockstatus_oneblock, ofs, len, 0);
++
++ return ret;
++}
++
++
+ static void cfi_amdstd_destroy(struct mtd_info *mtd)
+ {
+ struct map_info *map = mtd->priv;
+@@ -1291,17 +2193,20 @@
+
+ static char im_name[]="cfi_cmdset_0002";
+
++
+ int __init cfi_amdstd_init(void)
+ {
+ inter_module_register(im_name, THIS_MODULE, &cfi_cmdset_0002);
+ return 0;
+ }
+
++
+ static void __exit cfi_amdstd_exit(void)
+ {
+ inter_module_unregister(im_name);
+ }
+
++
+ module_init(cfi_amdstd_init);
+ module_exit(cfi_amdstd_exit);
+
+@@ -1309,3 +2214,7 @@
+ MODULE_AUTHOR("Crossnet Co. <info@crossnet.co.jp> et al.");
+ MODULE_DESCRIPTION("MTD chip driver for AMD/Fujitsu flash chips");
+
++#ifdef CONFIG_MTD_CFI_AMDSTD_RETRY
++MODULE_PARM(retry_cmd_max, "i");
++MODULE_PARM_DESC(retry_cmd_max, "Number of times to retry an erase or program command if it fails - should only be needed by buggy hardware: default 0");
++#endif
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/chips/cfi_cmdset_0020.c linux/drivers/mtd/chips/cfi_cmdset_0020.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/chips/cfi_cmdset_0020.c 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/chips/cfi_cmdset_0020.c 2004-11-17 18:17:58.945328208 +0100
+@@ -21,16 +21,19 @@
+ #include <linux/types.h>
+ #include <linux/kernel.h>
+ #include <linux/sched.h>
++#include <linux/init.h>
+ #include <asm/io.h>
+ #include <asm/byteorder.h>
+
+ #include <linux/errno.h>
++#include <linux/init.h>
+ #include <linux/slab.h>
+ #include <linux/delay.h>
+ #include <linux/interrupt.h>
++#include <linux/mtd/compatmac.h>
+ #include <linux/mtd/map.h>
+ #include <linux/mtd/cfi.h>
+-#include <linux/mtd/compatmac.h>
++#include <linux/mtd/mtd.h>
+
+
+ static int cfi_staa_read(struct mtd_info *, loff_t, size_t, size_t *, u_char *);
+@@ -51,10 +54,10 @@
+ static struct mtd_info *cfi_staa_setup (struct map_info *);
+
+ static struct mtd_chip_driver cfi_staa_chipdrv = {
+- probe: NULL, /* Not usable directly */
+- destroy: cfi_staa_destroy,
+- name: "cfi_cmdset_0020",
+- module: THIS_MODULE
++ .probe = NULL, /* Not usable directly */
++ .destroy = cfi_staa_destroy,
++ .name = "cfi_cmdset_0020",
++ .module = THIS_MODULE
+ };
+
+ /* #define DEBUG_LOCK_BITS */
+@@ -113,7 +116,6 @@
+ {
+ struct cfi_private *cfi = map->fldrv_priv;
+ int i;
+- __u32 base = cfi->chips[0].start;
+
+ if (cfi->cfi_mode) {
+ /*
+@@ -123,35 +125,10 @@
+ */
+ __u16 adr = primary?cfi->cfiq->P_ADR:cfi->cfiq->A_ADR;
+ struct cfi_pri_intelext *extp;
+- int ofs_factor = cfi->interleave * cfi->device_type;
+-
+- printk(" ST Microelectronics Extended Query Table at 0x%4.4X\n", adr);
+- if (!adr)
+- return NULL;
+-
+- /* Switch it into Query Mode */
+- cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL);
+-
+- extp = kmalloc(sizeof(*extp), GFP_KERNEL);
+- if (!extp) {
+- printk(KERN_ERR "Failed to allocate memory\n");
+- return NULL;
+- }
+-
+- /* Read in the Extended Query Table */
+- for (i=0; i<sizeof(*extp); i++) {
+- ((unsigned char *)extp)[i] =
+- cfi_read_query(map, (base+((adr+i)*ofs_factor)));
+- }
+
+- if (extp->MajorVersion != '1' ||
+- (extp->MinorVersion < '0' || extp->MinorVersion > '2')) {
+- printk(KERN_WARNING " Unknown staa Extended Query "
+- "version %c.%c.\n", extp->MajorVersion,
+- extp->MinorVersion);
+- kfree(extp);
++ extp = (struct cfi_pri_intelext*)cfi_read_pri(map, adr, sizeof(*extp), "ST Microelectronics");
++ if (!extp)
+ return NULL;
+- }
+
+ /* Do some byteswapping if necessary */
+ extp->FeatureSupport = cfi32_to_cpu(extp->FeatureSupport);
+@@ -172,11 +149,6 @@
+ cfi->chips[i].erase_time = 1024;
+ }
+
+- map->fldrv = &cfi_staa_chipdrv;
+- MOD_INC_USE_COUNT;
+-
+- /* Make sure it's in read mode */
+- cfi_send_gen_cmd(0xff, 0x55, base, map, cfi, cfi->device_type, NULL);
+ return cfi_staa_setup(map);
+ }
+
+@@ -208,6 +180,7 @@
+ if (!mtd->eraseregions) {
+ printk(KERN_ERR "Failed to allocate memory for MTD erase region info\n");
+ kfree(cfi->cmdset_priv);
++ kfree(mtd);
+ return NULL;
+ }
+
+@@ -232,6 +205,7 @@
+ printk(KERN_WARNING "Sum of regions (%lx) != total size of set of interleaved chips (%lx)\n", offset, devsize);
+ kfree(mtd->eraseregions);
+ kfree(cfi->cmdset_priv);
++ kfree(mtd);
+ return NULL;
+ }
+
+@@ -256,7 +230,7 @@
+ mtd->flags |= MTD_ECC; /* FIXME: Not all STMicro flashes have this */
+ mtd->eccsize = 8; /* FIXME: Should be 0 for STMicro flashes w/out ECC */
+ map->fldrv = &cfi_staa_chipdrv;
+- MOD_INC_USE_COUNT;
++ __module_get(THIS_MODULE);
+ mtd->name = map->name;
+ return mtd;
+ }
+@@ -288,7 +262,7 @@
+ */
+ switch (chip->state) {
+ case FL_ERASING:
+- if (!((struct cfi_pri_intelext *)cfi->cmdset_priv)->FeatureSupport & 2)
++ if (!(((struct cfi_pri_intelext *)cfi->cmdset_priv)->FeatureSupport & 2))
+ goto sleep; /* We don't support erase suspend */
+
+ cfi_write (map, CMD(0xb0), cmd_addr);
+@@ -374,7 +348,7 @@
+ goto retry;
+ }
+
+- map->copy_from(map, buf, adr, len);
++ map_copy_from(map, buf, adr, len);
+
+ if (suspended) {
+ chip->state = chip->oldstate;
+@@ -540,11 +514,11 @@
+ /* Write data */
+ for (z = 0; z < len; z += CFIDEV_BUSWIDTH) {
+ if (cfi_buswidth_is_1()) {
+- map->write8 (map, *((__u8*)buf)++, adr+z);
++ map_write8 (map, *((__u8*)buf)++, adr+z);
+ } else if (cfi_buswidth_is_2()) {
+- map->write16 (map, *((__u16*)buf)++, adr+z);
++ map_write16 (map, *((__u16*)buf)++, adr+z);
+ } else if (cfi_buswidth_is_4()) {
+- map->write32 (map, *((__u32*)buf)++, adr+z);
++ map_write32 (map, *((__u32*)buf)++, adr+z);
+ } else {
+ DISABLE_VPP(map);
+ return -EINVAL;
+@@ -1436,13 +1410,13 @@
+
+ static char im_name[]="cfi_cmdset_0020";
+
+-mod_init_t cfi_staa_init(void)
++int __init cfi_staa_init(void)
+ {
+ inter_module_register(im_name, THIS_MODULE, &cfi_cmdset_0020);
+ return 0;
+ }
+
+-mod_exit_t cfi_staa_exit(void)
++static void __exit cfi_staa_exit(void)
+ {
+ inter_module_unregister(im_name);
+ }
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/chips/cfi_probe.c linux/drivers/mtd/chips/cfi_probe.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/chips/cfi_probe.c 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/chips/cfi_probe.c 2004-11-17 18:17:58.947327904 +0100
+@@ -1,13 +1,14 @@
+ /*
+ Common Flash Interface probe code.
+ (C) 2000 Red Hat. GPL'd.
+- $Id$
++ $Id$
+ */
+
+ #include <linux/config.h>
+ #include <linux/module.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
+ #include <asm/io.h>
+ #include <asm/byteorder.h>
+ #include <linux/errno.h>
+@@ -25,7 +26,7 @@
+ #endif
+
+ static int cfi_probe_chip(struct map_info *map, __u32 base,
+- struct flchip *chips, struct cfi_private *cfi);
++ unsigned long *chip_map, struct cfi_private *cfi);
+ static int cfi_chip_setup(struct map_info *map, struct cfi_private *cfi);
+
+ struct mtd_info *cfi_probe(struct map_info *map);
+@@ -48,7 +49,7 @@
+ }
+
+ static int cfi_probe_chip(struct map_info *map, __u32 base,
+- struct flchip *chips, struct cfi_private *cfi)
++ unsigned long *chip_map, struct cfi_private *cfi)
+ {
+ int i;
+
+@@ -77,18 +78,24 @@
+ }
+
+ /* Check each previous chip to see if it's an alias */
+- for (i=0; i<cfi->numchips; i++) {
++ for (i=0; i < (base >> cfi->chipshift); i++) {
++ unsigned long start;
++ if(!test_bit(i, chip_map)) {
++ /* Skip location; no valid chip at this address */
++ continue;
++ }
++ start = i << cfi->chipshift;
+ /* This chip should be in read mode if it's one
+ we've already touched. */
+- if (qry_present(map,chips[i].start,cfi)) {
++ if (qry_present(map, start, cfi)) {
+ /* Eep. This chip also had the QRY marker.
+ * Is it an alias for the new one? */
+- cfi_send_gen_cmd(0xF0, 0, chips[i].start, map, cfi, cfi->device_type, NULL);
++ cfi_send_gen_cmd(0xF0, 0, start, map, cfi, cfi->device_type, NULL);
+
+ /* If the QRY marker goes away, it's an alias */
+- if (!qry_present(map, chips[i].start, cfi)) {
++ if (!qry_present(map, start, cfi)) {
+ printk(KERN_DEBUG "%s: Found an alias at 0x%x for the chip at 0x%lx\n",
+- map->name, base, chips[i].start);
++ map->name, base, start);
+ return 0;
+ }
+ /* Yes, it's actually got QRY for data. Most
+@@ -99,7 +106,7 @@
+
+ if (qry_present(map, base, cfi)) {
+ printk(KERN_DEBUG "%s: Found an alias at 0x%x for the chip at 0x%lx\n",
+- map->name, base, chips[i].start);
++ map->name, base, start);
+ return 0;
+ }
+ }
+@@ -107,13 +114,7 @@
+
+ /* OK, if we got to here, then none of the previous chips appear to
+ be aliases for the current one. */
+- if (cfi->numchips == MAX_CFI_CHIPS) {
+- printk(KERN_WARNING"%s: Too many flash chips detected. Increase MAX_CFI_CHIPS from %d.\n", map->name, MAX_CFI_CHIPS);
+- /* Doesn't matter about resetting it to Read Mode - we're not going to talk to it anyway */
+- return -1;
+- }
+- chips[cfi->numchips].start = base;
+- chips[cfi->numchips].state = FL_READY;
++ set_bit((base >> cfi->chipshift), chip_map); /* Update chip map */
+ cfi->numchips++;
+
+ /* Put it back into Read Mode */
+@@ -179,9 +180,28 @@
+ (cfi->cfiq->EraseRegionInfo[i] & 0xffff) + 1);
+ #endif
+ }
++
++ /* Note we put the device back into Read Mode BEFORE going into Auto
++ * Select Mode, as some devices support nesting of modes, others
++ * don't. This way should always work.
++ * On cmdset 0001 the writes of 0xaa and 0x55 are not needed, and
++ * so should be treated as nops or illegal (and so put the device
++ * back into Read Mode, which is a nop in this case).
++ */
++ cfi_send_gen_cmd(0xf0, 0, base, map, cfi, cfi->device_type, NULL);
++ cfi_send_gen_cmd(0xaa, 0x555, base, map, cfi, cfi->device_type, NULL);
++ cfi_send_gen_cmd(0x55, 0x2aa, base, map, cfi, cfi->device_type, NULL);
++ cfi_send_gen_cmd(0x90, 0x555, base, map, cfi, cfi->device_type, NULL);
++ cfi->mfr = cfi_read_query(map, base);
++ cfi->id = cfi_read_query(map, base + ofs_factor);
++
+ /* Put it back into Read Mode */
+ cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL);
+
++ printk(KERN_INFO "%s: Found %d x%d devices at 0x%x in %d-bit mode\n",
++ map->name, cfi->interleave, cfi->device_type*8, base,
++ map->buswidth*8);
++
+ return 1;
+ }
+
+@@ -240,11 +260,11 @@
+ printk("No Alternate Algorithm Table\n");
+
+
+- printk("Vcc Minimum: %x.%x V\n", cfip->VccMin >> 4, cfip->VccMin & 0xf);
+- printk("Vcc Maximum: %x.%x V\n", cfip->VccMax >> 4, cfip->VccMax & 0xf);
++ printk("Vcc Minimum: %2d.%d V\n", cfip->VccMin >> 4, cfip->VccMin & 0xf);
++ printk("Vcc Maximum: %2d.%d V\n", cfip->VccMax >> 4, cfip->VccMax & 0xf);
+ if (cfip->VppMin) {
+- printk("Vpp Minimum: %x.%x V\n", cfip->VppMin >> 4, cfip->VppMin & 0xf);
+- printk("Vpp Maximum: %x.%x V\n", cfip->VppMax >> 4, cfip->VppMax & 0xf);
++ printk("Vpp Minimum: %2d.%d V\n", cfip->VppMin >> 4, cfip->VppMin & 0xf);
++ printk("Vpp Maximum: %2d.%d V\n", cfip->VppMax >> 4, cfip->VppMax & 0xf);
+ }
+ else
+ printk("No Vpp line\n");
+@@ -303,8 +323,8 @@
+ #endif /* DEBUG_CFI */
+
+ static struct chip_probe cfi_chip_probe = {
+- name: "CFI",
+- probe_chip: cfi_probe_chip
++ .name = "CFI",
++ .probe_chip = cfi_probe_chip
+ };
+
+ struct mtd_info *cfi_probe(struct map_info *map)
+@@ -317,9 +337,9 @@
+ }
+
+ static struct mtd_chip_driver cfi_chipdrv = {
+- probe: cfi_probe,
+- name: "cfi_probe",
+- module: THIS_MODULE
++ .probe = cfi_probe,
++ .name = "cfi_probe",
++ .module = THIS_MODULE
+ };
+
+ int __init cfi_probe_init(void)
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/chips/cfi_util.c linux/drivers/mtd/chips/cfi_util.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/chips/cfi_util.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/mtd/chips/cfi_util.c 2004-11-17 18:17:58.000000000 +0100
+@@ -0,0 +1,91 @@
++/*
++ * Common Flash Interface support:
++ * Generic utility functions not dependant on command set
++ *
++ * Copyright (C) 2002 Red Hat
++ * Copyright (C) 2003 STMicroelectronics Limited
++ *
++ * This code is covered by the GPL.
++ *
++ * $Id$
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/kernel.h>
++#include <linux/sched.h>
++#include <asm/io.h>
++#include <asm/byteorder.h>
++
++#include <linux/errno.h>
++#include <linux/slab.h>
++#include <linux/delay.h>
++#include <linux/interrupt.h>
++#include <linux/mtd/map.h>
++#include <linux/mtd/cfi.h>
++#include <linux/mtd/compatmac.h>
++
++struct cfi_extquery *
++cfi_read_pri(struct map_info *map, __u16 adr, __u16 size, const char* name)
++{
++ struct cfi_private *cfi = map->fldrv_priv;
++ __u32 base = 0; // cfi->chips[0].start;
++ int ofs_factor = cfi->interleave * cfi->device_type;
++ int i;
++ struct cfi_extquery *extp = NULL;
++
++ printk(" %s Extended Query Table at 0x%4.4X\n", name, adr);
++ if (!adr)
++ goto out;
++
++ /* Switch it into Query Mode */
++ cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL);
++
++ extp = kmalloc(size, GFP_KERNEL);
++ if (!extp) {
++ printk(KERN_ERR "Failed to allocate memory\n");
++ goto out;
++ }
++
++ /* Read in the Extended Query Table */
++ for (i=0; i<size; i++) {
++ ((unsigned char *)extp)[i] =
++ cfi_read_query(map, base+((adr+i)*ofs_factor));
++ }
++
++ if (extp->MajorVersion != '1' ||
++ (extp->MinorVersion < '0' || extp->MinorVersion > '3')) {
++ printk(KERN_WARNING " Unknown %s Extended Query "
++ "version %c.%c.\n", name, extp->MajorVersion,
++ extp->MinorVersion);
++ kfree(extp);
++ extp = NULL;
++ goto out;
++ }
++
++out:
++ /* Make sure it's in read mode */
++ cfi_send_gen_cmd(0xf0, 0, base, map, cfi, cfi->device_type, NULL);
++
++ return extp;
++}
++
++EXPORT_SYMBOL(cfi_read_pri);
++
++void cfi_fixup(struct map_info *map, struct cfi_fixup* fixups)
++{
++ struct cfi_private *cfi = map->fldrv_priv;
++ struct cfi_fixup *f;
++
++ for (f=fixups; f->fixup; f++) {
++ if (((f->mfr == CFI_MFR_ANY) || (f->mfr == cfi->mfr)) &&
++ ((f->id == CFI_ID_ANY) || (f->id == cfi->id))) {
++ f->fixup(map, f->param);
++ }
++ }
++}
++
++EXPORT_SYMBOL(cfi_fixup);
++
++MODULE_LICENSE("GPL");
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/chips/chipreg.c linux/drivers/mtd/chips/chipreg.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/chips/chipreg.c 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/chips/chipreg.c 2004-11-17 18:17:58.949327600 +0100
+@@ -1,5 +1,5 @@
+ /*
+- * $Id$
++ * $Id$
+ *
+ * Registration for chip drivers
+ *
+@@ -7,10 +7,13 @@
+
+ #include <linux/kernel.h>
+ #include <linux/config.h>
++#include <linux/module.h>
+ #include <linux/kmod.h>
+ #include <linux/spinlock.h>
+-#include <linux/mtd/compatmac.h>
++#include <linux/slab.h>
+ #include <linux/mtd/map.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/compatmac.h>
+
+ spinlock_t chip_drvs_lock = SPIN_LOCK_UNLOCKED;
+ static LIST_HEAD(chip_drvs_list);
+@@ -44,10 +47,8 @@
+ break;
+ }
+ }
+- if (ret && !try_inc_mod_count(ret->module)) {
+- /* Eep. Failed. */
++ if (ret && !try_module_get(ret->module))
+ ret = NULL;
+- }
+
+ spin_unlock(&chip_drvs_lock);
+
+@@ -64,32 +65,46 @@
+
+ drv = get_mtd_chip_driver(name);
+
+- if (!drv && !request_module(name))
++ if (!drv && !request_module("%s", name))
+ drv = get_mtd_chip_driver(name);
+
+ if (!drv)
+ return NULL;
+
+ ret = drv->probe(map);
+-#ifdef CONFIG_MODULES
++
+ /* We decrease the use count here. It may have been a
+ probe-only module, which is no longer required from this
+ point, having given us a handle on (and increased the use
+ count of) the actual driver code.
+ */
+- if(drv->module)
+- __MOD_DEC_USE_COUNT(drv->module);
+-#endif
++ module_put(drv->module);
+
+ if (ret)
+ return ret;
+
+ return NULL;
+ }
++/*
++ * Destroy an MTD device which was created for a map device.
++ * Make sure the MTD device is already unregistered before calling this
++ */
++void map_destroy(struct mtd_info *mtd)
++{
++ struct map_info *map = mtd->priv;
++
++ if (map->fldrv->destroy)
++ map->fldrv->destroy(mtd);
++
++ module_put(map->fldrv->module);
++
++ kfree(mtd);
++}
+
+ EXPORT_SYMBOL(register_mtd_chip_driver);
+ EXPORT_SYMBOL(unregister_mtd_chip_driver);
+ EXPORT_SYMBOL(do_map_probe);
++EXPORT_SYMBOL(map_destroy);
+
+ MODULE_LICENSE("GPL");
+ MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/chips/gen_probe.c linux/drivers/mtd/chips/gen_probe.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/chips/gen_probe.c 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/chips/gen_probe.c 2004-11-17 18:17:58.951327296 +0100
+@@ -1,14 +1,17 @@
+ /*
+ * Routines common to all CFI-type probes.
+- * (C) 2001, 2001 Red Hat, Inc.
++ * (C) 2001-2003 Red Hat, Inc.
+ * GPL'd
+- * $Id$
++ * $Id$
+ */
+
+ #include <linux/kernel.h>
++#include <linux/slab.h>
++#include <linux/module.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+ #include <linux/mtd/cfi.h>
++#include <linux/mtd/mtd.h>
+ #include <linux/mtd/gen_probe.h>
+
+ static struct mtd_info *check_cmd_set(struct map_info *, int);
+@@ -50,11 +53,11 @@
+
+ struct cfi_private *genprobe_ident_chips(struct map_info *map, struct chip_probe *cp)
+ {
+- unsigned long base=0;
+ struct cfi_private cfi;
+ struct cfi_private *retcfi;
+- struct flchip chip[MAX_CFI_CHIPS];
+- int i;
++ unsigned long *chip_map;
++ int i, j;
++ int max_chips;
+
+ memset(&cfi, 0, sizeof(cfi));
+
+@@ -77,8 +80,6 @@
+ return NULL;
+ }
+ #endif
+- chip[0].start = 0;
+- chip[0].state = FL_READY;
+ cfi.chipshift = cfi.cfiq->DevSize;
+
+ switch(cfi.interleave) {
+@@ -103,20 +104,28 @@
+ cfi.numchips = 1;
+
+ /*
++ * Allocate memory for bitmap of valid chips.
++ * Align bitmap storage size to full byte.
++ */
++ max_chips = map->size >> cfi.chipshift;
++ chip_map = kmalloc((max_chips / 8) + ((max_chips % 8) ? 1 : 0), GFP_KERNEL);
++ if (!chip_map) {
++ printk(KERN_WARNING "%s: kmalloc failed for CFI chip map\n", map->name);
++ kfree(cfi.cfiq);
++ return NULL;
++ }
++
++ set_bit(0, chip_map); /* Mark first chip valid */
++
++ /*
+ * Now probe for other chips, checking sensibly for aliases while
+ * we're at it. The new_chip probe above should have let the first
+ * chip in read mode.
+- *
+- * NOTE: Here, we're checking if there is room for another chip
+- * the same size within the mapping. Therefore,
+- * base + chipsize <= map->size is the correct thing to do,
+- * because, base + chipsize would be the _first_ byte of the
+- * next chip, not the one we're currently pondering.
+ */
+
+- for (base = (1<<cfi.chipshift); base + (1<<cfi.chipshift) <= map->size;
+- base += (1<<cfi.chipshift))
+- cp->probe_chip(map, base, &chip[0], &cfi);
++ for (i = 1; i < max_chips; i++) {
++ cp->probe_chip(map, i << cfi.chipshift, chip_map, &cfi);
++ }
+
+ /*
+ * Now allocate the space for the structures we need to return to
+@@ -128,19 +137,26 @@
+ if (!retcfi) {
+ printk(KERN_WARNING "%s: kmalloc failed for CFI private structure\n", map->name);
+ kfree(cfi.cfiq);
++ kfree(chip_map);
+ return NULL;
+ }
+
+ memcpy(retcfi, &cfi, sizeof(cfi));
+- memcpy(&retcfi->chips[0], chip, sizeof(struct flchip) * cfi.numchips);
++ memset(&retcfi->chips[0], 0, sizeof(struct flchip) * cfi.numchips);
+
+- /* Fix up the stuff that breaks when you move it */
+- for (i=0; i< retcfi->numchips; i++) {
+- init_waitqueue_head(&retcfi->chips[i].wq);
+- spin_lock_init(&retcfi->chips[i]._spinlock);
+- retcfi->chips[i].mutex = &retcfi->chips[i]._spinlock;
++ for (i = 0, j = 0; (j < cfi.numchips) && (i < max_chips); i++) {
++ if(test_bit(i, chip_map)) {
++ struct flchip *pchip = &retcfi->chips[j++];
++
++ pchip->start = (i << cfi.chipshift);
++ pchip->state = FL_READY;
++ init_waitqueue_head(&pchip->wq);
++ spin_lock_init(&pchip->_spinlock);
++ pchip->mutex = &pchip->_spinlock;
++ }
+ }
+
++ kfree(chip_map);
+ return retcfi;
+ }
+
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/chips/jedec.c linux/drivers/mtd/chips/jedec.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/chips/jedec.c 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/chips/jedec.c 2004-11-17 18:17:58.953326992 +0100
+@@ -11,10 +11,16 @@
+ * not going to guess how to send commands to them, plus I expect they will
+ * all speak CFI..
+ *
+- * $Id$
++ * $Id$
+ */
+
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/kernel.h>
+ #include <linux/mtd/jedec.h>
++#include <linux/mtd/map.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/compatmac.h>
+
+ static struct mtd_info *jedec_probe(struct map_info *);
+ static int jedec_probe8(struct map_info *map,unsigned long base,
+@@ -33,14 +39,51 @@
+
+ /* Listing of parts and sizes. We need this table to learn the sector
+ size of the chip and the total length */
+-static const struct JEDECTable JEDEC_table[] =
+- {{0x013D,"AMD Am29F017D",2*1024*1024,64*1024,MTD_CAP_NORFLASH},
+- {0x01AD,"AMD Am29F016",2*1024*1024,64*1024,MTD_CAP_NORFLASH},
+- {0x01D5,"AMD Am29F080",1*1024*1024,64*1024,MTD_CAP_NORFLASH},
+- {0x01A4,"AMD Am29F040",512*1024,64*1024,MTD_CAP_NORFLASH},
+- {0x20E3,"AMD Am29W040B",512*1024,64*1024,MTD_CAP_NORFLASH},
+- {0xC2AD,"Macronix MX29F016",2*1024*1024,64*1024,MTD_CAP_NORFLASH},
+- {}};
++static const struct JEDECTable JEDEC_table[] = {
++ {
++ .jedec = 0x013D,
++ .name = "AMD Am29F017D",
++ .size = 2*1024*1024,
++ .sectorsize = 64*1024,
++ .capabilities = MTD_CAP_NORFLASH
++ },
++ {
++ .jedec = 0x01AD,
++ .name = "AMD Am29F016",
++ .size = 2*1024*1024,
++ .sectorsize = 64*1024,
++ .capabilities = MTD_CAP_NORFLASH
++ },
++ {
++ .jedec = 0x01D5,
++ .name = "AMD Am29F080",
++ .size = 1*1024*1024,
++ .sectorsize = 64*1024,
++ .capabilities = MTD_CAP_NORFLASH
++ },
++ {
++ .jedec = 0x01A4,
++ .name = "AMD Am29F040",
++ .size = 512*1024,
++ .sectorsize = 64*1024,
++ .capabilities = MTD_CAP_NORFLASH
++ },
++ {
++ .jedec = 0x20E3,
++ .name = "AMD Am29W040B",
++ .size = 512*1024,
++ .sectorsize = 64*1024,
++ .capabilities = MTD_CAP_NORFLASH
++ },
++ {
++ .jedec = 0xC2AD,
++ .name = "Macronix MX29F016",
++ .size = 2*1024*1024,
++ .sectorsize = 64*1024,
++ .capabilities = MTD_CAP_NORFLASH
++ },
++ { .jedec = 0x0 }
++};
+
+ static const struct JEDECTable *jedec_idtoinf(__u8 mfr,__u8 id);
+ static void jedec_sync(struct mtd_info *mtd) {};
+@@ -54,9 +97,9 @@
+
+
+ static struct mtd_chip_driver jedec_chipdrv = {
+- probe: jedec_probe,
+- name: "jedec",
+- module: THIS_MODULE
++ .probe = jedec_probe,
++ .name = "jedec",
++ .module = THIS_MODULE
+ };
+
+ /* Probe entry point */
+@@ -131,8 +174,7 @@
+ /* Generate a part name that includes the number of different chips and
+ other configuration information */
+ count = 1;
+- strncpy(Part,map->name,sizeof(Part)-10);
+- Part[sizeof(Part)-11] = 0;
++ strlcpy(Part,map->name,sizeof(Part)-10);
+ strcat(Part," ");
+ Uniq = 0;
+ for (I = 0; priv->chips[I].jedec != 0 && I < MAX_JEDEC_CHIPS; I++)
+@@ -209,8 +251,7 @@
+ // printk("Part: '%s'\n",Part);
+
+ memset(MTD,0,sizeof(*MTD));
+- // strncpy(MTD->name,Part,sizeof(MTD->name));
+- // MTD->name[sizeof(MTD->name)-1] = 0;
++ // strlcpy(MTD->name,Part,sizeof(MTD->name));
+ MTD->name = map->name;
+ MTD->type = MTD_NORFLASH;
+ MTD->flags = MTD_CAP_NORFLASH;
+@@ -229,7 +270,7 @@
+ MTD->priv = map;
+ map->fldrv_priv = priv;
+ map->fldrv = &jedec_chipdrv;
+- MOD_INC_USE_COUNT;
++ __module_get(THIS_MODULE);
+ return MTD;
+ }
+
+@@ -351,8 +392,8 @@
+ static int jedec_probe8(struct map_info *map,unsigned long base,
+ struct jedec_private *priv)
+ {
+- #define flread(x) map->read8(map,base+x)
+- #define flwrite(v,x) map->write8(map,v,base+x)
++ #define flread(x) map_read8(map,base+x)
++ #define flwrite(v,x) map_write8(map,v,base+x)
+
+ const unsigned long AutoSel1 = 0xAA;
+ const unsigned long AutoSel2 = 0x55;
+@@ -411,8 +452,8 @@
+ static int jedec_probe32(struct map_info *map,unsigned long base,
+ struct jedec_private *priv)
+ {
+- #define flread(x) map->read32(map,base+((x)<<2))
+- #define flwrite(v,x) map->write32(map,v,base+((x)<<2))
++ #define flread(x) map_read32(map,base+((x)<<2))
++ #define flwrite(v,x) map_write32(map,v,base+((x)<<2))
+
+ const unsigned long AutoSel1 = 0xAAAAAAAA;
+ const unsigned long AutoSel2 = 0x55555555;
+@@ -490,7 +531,7 @@
+ {
+ struct map_info *map = (struct map_info *)mtd->priv;
+
+- map->copy_from(map, buf, from, len);
++ map_copy_from(map, buf, from, len);
+ *retlen = len;
+ return 0;
+ }
+@@ -514,7 +555,7 @@
+ get = priv->bank_fill[0] - offset;
+
+ bank /= priv->bank_fill[0];
+- map->copy_from(map,buf + *retlen,bank*my_bank_size + offset,get);
++ map_copy_from(map,buf + *retlen,bank*my_bank_size + offset,get);
+
+ len -= get;
+ *retlen += get;
+@@ -545,8 +586,8 @@
+ static int flash_erase(struct mtd_info *mtd, struct erase_info *instr)
+ {
+ // Does IO to the currently selected chip
+- #define flread(x) map->read8(map,chip->base+((x)<<chip->addrshift))
+- #define flwrite(v,x) map->write8(map,v,chip->base+((x)<<chip->addrshift))
++ #define flread(x) map_read8(map,chip->base+((x)<<chip->addrshift))
++ #define flwrite(v,x) map_write8(map,v,chip->base+((x)<<chip->addrshift))
+
+ unsigned long Time = 0;
+ unsigned long NoTime = 0;
+@@ -608,7 +649,7 @@
+
+ /* Poll the flash for erasure completion, specs say this can take as long
+ as 480 seconds to do all the sectors (for a 2 meg flash).
+- Erasure time is dependant on chip age, temp and wear.. */
++ Erasure time is dependent on chip age, temp and wear.. */
+
+ /* This being a generic routine assumes a 32 bit bus. It does read32s
+ and bundles interleved chips into the same grouping. This will work
+@@ -651,19 +692,19 @@
+ or this is not really flash ;> */
+ switch (map->buswidth) {
+ case 1:
+- Last[0] = map->read8(map,(chip->base >> chip->addrshift) + chip->start + off);
+- Last[1] = map->read8(map,(chip->base >> chip->addrshift) + chip->start + off);
+- Last[2] = map->read8(map,(chip->base >> chip->addrshift) + chip->start + off);
++ Last[0] = map_read8(map,(chip->base >> chip->addrshift) + chip->start + off);
++ Last[1] = map_read8(map,(chip->base >> chip->addrshift) + chip->start + off);
++ Last[2] = map_read8(map,(chip->base >> chip->addrshift) + chip->start + off);
+ break;
+ case 2:
+- Last[0] = map->read16(map,(chip->base >> chip->addrshift) + chip->start + off);
+- Last[1] = map->read16(map,(chip->base >> chip->addrshift) + chip->start + off);
+- Last[2] = map->read16(map,(chip->base >> chip->addrshift) + chip->start + off);
++ Last[0] = map_read16(map,(chip->base >> chip->addrshift) + chip->start + off);
++ Last[1] = map_read16(map,(chip->base >> chip->addrshift) + chip->start + off);
++ Last[2] = map_read16(map,(chip->base >> chip->addrshift) + chip->start + off);
+ break;
+ case 3:
+- Last[0] = map->read32(map,(chip->base >> chip->addrshift) + chip->start + off);
+- Last[1] = map->read32(map,(chip->base >> chip->addrshift) + chip->start + off);
+- Last[2] = map->read32(map,(chip->base >> chip->addrshift) + chip->start + off);
++ Last[0] = map_read32(map,(chip->base >> chip->addrshift) + chip->start + off);
++ Last[1] = map_read32(map,(chip->base >> chip->addrshift) + chip->start + off);
++ Last[2] = map_read32(map,(chip->base >> chip->addrshift) + chip->start + off);
+ break;
+ }
+ Count = 3;
+@@ -699,13 +740,13 @@
+
+ switch (map->buswidth) {
+ case 1:
+- Last[Count % 4] = map->read8(map,(chip->base >> chip->addrshift) + chip->start + off);
++ Last[Count % 4] = map_read8(map,(chip->base >> chip->addrshift) + chip->start + off);
+ break;
+ case 2:
+- Last[Count % 4] = map->read16(map,(chip->base >> chip->addrshift) + chip->start + off);
++ Last[Count % 4] = map_read16(map,(chip->base >> chip->addrshift) + chip->start + off);
+ break;
+ case 4:
+- Last[Count % 4] = map->read32(map,(chip->base >> chip->addrshift) + chip->start + off);
++ Last[Count % 4] = map_read32(map,(chip->base >> chip->addrshift) + chip->start + off);
+ break;
+ }
+ Count++;
+@@ -755,10 +796,10 @@
+ size_t *retlen, const u_char *buf)
+ {
+ /* Does IO to the currently selected chip. It takes the bank addressing
+- base (which is divisable by the chip size) adds the necesary lower bits
+- of addrshift (interleve index) and then adds the control register index. */
+- #define flread(x) map->read8(map,base+(off&((1<<chip->addrshift)-1))+((x)<<chip->addrshift))
+- #define flwrite(v,x) map->write8(map,v,base+(off&((1<<chip->addrshift)-1))+((x)<<chip->addrshift))
++ base (which is divisible by the chip size) adds the necessary lower bits
++ of addrshift (interleave index) and then adds the control register index. */
++ #define flread(x) map_read8(map,base+(off&((1<<chip->addrshift)-1))+((x)<<chip->addrshift))
++ #define flwrite(v,x) map_write8(map,v,base+(off&((1<<chip->addrshift)-1))+((x)<<chip->addrshift))
+
+ struct map_info *map = (struct map_info *)mtd->priv;
+ struct jedec_private *priv = (struct jedec_private *)map->fldrv_priv;
+@@ -794,7 +835,7 @@
+ // Loop over this page
+ for (; off != (chip->size << chip->addrshift) && len != 0; start++, len--, off++,buf++)
+ {
+- unsigned char oldbyte = map->read8(map,base+off);
++ unsigned char oldbyte = map_read8(map,base+off);
+ unsigned char Last[4];
+ unsigned long Count = 0;
+
+@@ -809,10 +850,10 @@
+ flwrite(0xAA,0x555);
+ flwrite(0x55,0x2AA);
+ flwrite(0xA0,0x555);
+- map->write8(map,*buf,base + off);
+- Last[0] = map->read8(map,base + off);
+- Last[1] = map->read8(map,base + off);
+- Last[2] = map->read8(map,base + off);
++ map_write8(map,*buf,base + off);
++ Last[0] = map_read8(map,base + off);
++ Last[1] = map_read8(map,base + off);
++ Last[2] = map_read8(map,base + off);
+
+ /* Wait for the flash to finish the operation. We store the last 4
+ status bytes that have been retrieved so we can determine why
+@@ -820,7 +861,7 @@
+ failure */
+ for (Count = 3; Last[(Count - 1) % 4] != Last[(Count - 2) % 4] &&
+ Count < 10000; Count++)
+- Last[Count % 4] = map->read8(map,base + off);
++ Last[Count % 4] = map_read8(map,base + off);
+ if (Last[(Count - 1) % 4] != *buf)
+ {
+ jedec_flash_failed(Last[(Count - 3) % 4]);
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/chips/jedec_probe.c linux/drivers/mtd/chips/jedec_probe.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/chips/jedec_probe.c 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/chips/jedec_probe.c 2004-11-17 18:17:58.954326840 +0100
+@@ -1,9 +1,11 @@
+ /*
+ Common Flash Interface probe code.
+ (C) 2000 Red Hat. GPL'd.
+- $Id$
++ $Id$
+ See JEDEC (http://www.jedec.org/) standard JESD21C (section 3.5)
+ for the standard this probe goes back to.
++
++ Occasionally maintained by Thayne Harbaugh tharbaugh at lnxi dot com
+ */
+
+ #include <linux/config.h>
+@@ -15,7 +17,9 @@
+ #include <linux/errno.h>
+ #include <linux/slab.h>
+ #include <linux/interrupt.h>
++#include <linux/init.h>
+
++#include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+ #include <linux/mtd/cfi.h>
+ #include <linux/mtd/gen_probe.h>
+@@ -26,20 +30,24 @@
+ #define MANUFACTURER_FUJITSU 0x0004
+ #define MANUFACTURER_INTEL 0x0089
+ #define MANUFACTURER_MACRONIX 0x00C2
+-#define MANUFACTURER_ST 0x0020
++#define MANUFACTURER_PMC 0x009D
+ #define MANUFACTURER_SST 0x00BF
++#define MANUFACTURER_ST 0x0020
+ #define MANUFACTURER_TOSHIBA 0x0098
++#define MANUFACTURER_WINBOND 0x00da
+
+
+ /* AMD */
+ #define AM29F800BB 0x2258
+ #define AM29F800BT 0x22D6
++#define AM29LV400BB 0x22BA
++#define AM29LV400BT 0x22B9
+ #define AM29LV800BB 0x225B
+ #define AM29LV800BT 0x22DA
+ #define AM29LV160DT 0x22C4
+ #define AM29LV160DB 0x2249
+ #define AM29F017D 0x003D
+-#define AM29F016 0x00AD
++#define AM29F016D 0x00AD
+ #define AM29F080 0x00D5
+ #define AM29F040 0x00A4
+ #define AM29LV040B 0x004F
+@@ -54,6 +62,7 @@
+ #define AT49BV32XT 0x00C9
+
+ /* Fujitsu */
++#define MBM29F040C 0x00A4
+ #define MBM29LV650UE 0x22D7
+ #define MBM29LV320TE 0x22F6
+ #define MBM29LV320BE 0x22F9
+@@ -61,6 +70,9 @@
+ #define MBM29LV160BE 0x2249
+ #define MBM29LV800BA 0x225B
+ #define MBM29LV800TA 0x22DA
++#define MBM29LV400TC 0x22B9
++#define MBM29LV400BC 0x22BA
++
+
+ /* Intel */
+ #define I28F004B3T 0x00d4
+@@ -93,8 +105,14 @@
+ #define MX29F004T 0x0045
+ #define MX29F004B 0x0046
+
++/* PMC */
++#define PM49FL002 0x006D
++#define PM49FL004 0x006E
++#define PM49FL008 0x006A
++
+ /* ST - www.st.com */
+-#define M29W800T 0x00D7
++#define M29W800DT 0x00D7
++#define M29W800DB 0x005B
+ #define M29W160DT 0x22C4
+ #define M29W160DB 0x2249
+ #define M29W040B 0x00E3
+@@ -110,6 +128,7 @@
+ #define SST39LF040 0x00D7
+ #define SST39SF010A 0x00B5
+ #define SST39SF020A 0x00B6
++#define SST49LF004B 0x0060
+ #define SST49LF030A 0x001C
+ #define SST49LF040A 0x0051
+ #define SST49LF080A 0x005B
+@@ -122,15 +141,87 @@
+ #define TC58FVT641 0x0093
+ #define TC58FVB641 0x0095
+
++/* Winbond */
++#define W49V002A 0x00b0
++
++
++/*
++ * Unlock address sets for AMD command sets.
++ * Intel command sets use the MTD_UADDR_UNNECESSARY.
++ * Each identifier, except MTD_UADDR_UNNECESSARY, and
++ * MTD_UADDR_NO_SUPPORT must be defined below in unlock_addrs[].
++ * MTD_UADDR_NOT_SUPPORTED must be 0 so that structure
++ * initialization need not require initializing all of the
++ * unlock addresses for all bit widths.
++ */
++enum uaddr {
++ MTD_UADDR_NOT_SUPPORTED = 0, /* data width not supported */
++ MTD_UADDR_0x0555_0x02AA,
++ MTD_UADDR_0x0555_0x0AAA,
++ MTD_UADDR_0x5555_0x2AAA,
++ MTD_UADDR_0x0AAA_0x0555,
++ MTD_UADDR_DONT_CARE, /* Requires an arbitrary address */
++ MTD_UADDR_UNNECESSARY, /* Does not require any address */
++};
++
++
++struct unlock_addr {
++ int addr1;
++ int addr2;
++};
++
++
++/*
++ * I don't like the fact that the first entry in unlock_addrs[]
++ * exists, but is for MTD_UADDR_NOT_SUPPORTED - and, therefore,
++ * should not be used. The problem is that structures with
++ * initializers have extra fields initialized to 0. It is _very_
++ * desireable to have the unlock address entries for unsupported
++ * data widths automatically initialized - that means that
++ * MTD_UADDR_NOT_SUPPORTED must be 0 and the first entry here
++ * must go unused.
++ */
++static const struct unlock_addr unlock_addrs[] = {
++ [MTD_UADDR_NOT_SUPPORTED] = {
++ .addr1 = 0xffff,
++ .addr2 = 0xffff
++ },
++
++ [MTD_UADDR_0x0555_0x02AA] = {
++ .addr1 = 0x0555,
++ .addr2 = 0x02aa
++ },
++
++ [MTD_UADDR_0x0555_0x0AAA] = {
++ .addr1 = 0x0555,
++ .addr2 = 0x0aaa
++ },
++
++ [MTD_UADDR_0x5555_0x2AAA] = {
++ .addr1 = 0x5555,
++ .addr2 = 0x2aaa
++ },
++
++ [MTD_UADDR_0x0AAA_0x0555] = {
++ .addr1 = 0x0AAA,
++ .addr2 = 0x0555
++ },
++
++ [MTD_UADDR_DONT_CARE] = {
++ .addr1 = 0x0000, /* Doesn't matter which address */
++ .addr2 = 0x0000 /* is used - must be last entry */
++ }
++};
++
+
+ struct amd_flash_info {
+ const __u16 mfr_id;
+ const __u16 dev_id;
+ const char *name;
+ const int DevSize;
+- const int InterfaceDesc;
+ const int NumEraseRegions;
+ const int CmdSet;
++ const __u8 uaddr[4]; /* unlock addrs for 8, 16, 32, 64 */
+ const ulong regions[4];
+ };
+
+@@ -145,760 +236,1214 @@
+ #define SIZE_4MiB 22
+ #define SIZE_8MiB 23
+
++
++/*
++ * Please keep this list ordered by manufacturer!
++ * Fortunately, the list isn't searched often and so a
++ * slow, linear search isn't so bad.
++ */
+ static const struct amd_flash_info jedec_table[] = {
+ {
+- mfr_id: MANUFACTURER_AMD,
+- dev_id: AM29F032B,
+- name: "AMD AM29F032B",
+- DevSize: SIZE_4MiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 1,
+- regions: {ERASEINFO(0x10000,64)
+- }
+- }, {
+- mfr_id: MANUFACTURER_AMD,
+- dev_id: AM29LV160DT,
+- name: "AMD AM29LV160DT",
+- DevSize: SIZE_2MiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 4,
+- regions: {ERASEINFO(0x10000,31),
++ .mfr_id = MANUFACTURER_AMD,
++ .dev_id = AM29F032B,
++ .name = "AMD AM29F032B",
++ .uaddr = {
++ [0] = MTD_UADDR_0x0555_0x02AA /* x8 */
++ },
++ .DevSize = SIZE_4MiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 1,
++ .regions = {
++ ERASEINFO(0x10000,64)
++ }
++ }, {
++ .mfr_id = MANUFACTURER_AMD,
++ .dev_id = AM29LV160DT,
++ .name = "AMD AM29LV160DT",
++ .uaddr = {
++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */
++ [1] = MTD_UADDR_0x0555_0x02AA /* x16 */
++ },
++ .DevSize = SIZE_2MiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 4,
++ .regions = {
++ ERASEINFO(0x10000,31),
+ ERASEINFO(0x08000,1),
+ ERASEINFO(0x02000,2),
+ ERASEINFO(0x04000,1)
+ }
+ }, {
+- mfr_id: MANUFACTURER_AMD,
+- dev_id: AM29LV160DB,
+- name: "AMD AM29LV160DB",
+- DevSize: SIZE_2MiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 4,
+- regions: {ERASEINFO(0x04000,1),
++ .mfr_id = MANUFACTURER_AMD,
++ .dev_id = AM29LV160DB,
++ .name = "AMD AM29LV160DB",
++ .uaddr = {
++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */
++ [1] = MTD_UADDR_0x0555_0x02AA /* x16 */
++ },
++ .DevSize = SIZE_2MiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 4,
++ .regions = {
++ ERASEINFO(0x04000,1),
+ ERASEINFO(0x02000,2),
+ ERASEINFO(0x08000,1),
+ ERASEINFO(0x10000,31)
+ }
+ }, {
+- mfr_id: MANUFACTURER_TOSHIBA,
+- dev_id: TC58FVT160,
+- name: "Toshiba TC58FVT160",
+- DevSize: SIZE_2MiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 4,
+- regions: {ERASEINFO(0x10000,31),
++ .mfr_id = MANUFACTURER_AMD,
++ .dev_id = AM29LV400BB,
++ .name = "AMD AM29LV400BB",
++ .uaddr = {
++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */
++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */
++ },
++ .DevSize = SIZE_512KiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 4,
++ .regions = {
++ ERASEINFO(0x04000,1),
++ ERASEINFO(0x02000,2),
++ ERASEINFO(0x08000,1),
++ ERASEINFO(0x10000,7)
++ }
++ }, {
++ .mfr_id = MANUFACTURER_AMD,
++ .dev_id = AM29LV400BT,
++ .name = "AMD AM29LV400BT",
++ .uaddr = {
++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */
++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */
++ },
++ .DevSize = SIZE_512KiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 4,
++ .regions = {
++ ERASEINFO(0x10000,7),
+ ERASEINFO(0x08000,1),
+ ERASEINFO(0x02000,2),
+ ERASEINFO(0x04000,1)
+ }
+ }, {
+- mfr_id: MANUFACTURER_TOSHIBA,
+- dev_id: TC58FVB160,
+- name: "Toshiba TC58FVB160",
+- DevSize: SIZE_2MiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 4,
+- regions: {ERASEINFO(0x04000,1),
++ .mfr_id = MANUFACTURER_AMD,
++ .dev_id = AM29LV800BB,
++ .name = "AMD AM29LV800BB",
++ .uaddr = {
++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */
++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */
++ },
++ .DevSize = SIZE_1MiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 4,
++ .regions = {
++ ERASEINFO(0x04000,1),
+ ERASEINFO(0x02000,2),
+ ERASEINFO(0x08000,1),
+- ERASEINFO(0x10000,31)
++ ERASEINFO(0x10000,15),
+ }
+ }, {
+- mfr_id: MANUFACTURER_TOSHIBA,
+- dev_id: TC58FVB321,
+- name: "Toshiba TC58FVB321",
+- DevSize: SIZE_4MiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 2,
+- regions: {ERASEINFO(0x02000,8),
+- ERASEINFO(0x10000,63)
++ .mfr_id = MANUFACTURER_AMD,
++ .dev_id = AM29F800BB,
++ .name = "AMD AM29F800BB",
++ .uaddr = {
++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */
++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */
++ },
++ .DevSize = SIZE_1MiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 4,
++ .regions = {
++ ERASEINFO(0x04000,1),
++ ERASEINFO(0x02000,2),
++ ERASEINFO(0x08000,1),
++ ERASEINFO(0x10000,15),
+ }
+ }, {
+- mfr_id: MANUFACTURER_TOSHIBA,
+- dev_id: TC58FVT321,
+- name: "Toshiba TC58FVT321",
+- DevSize: SIZE_4MiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 2,
+- regions: {ERASEINFO(0x10000,63),
+- ERASEINFO(0x02000,8)
++ .mfr_id = MANUFACTURER_AMD,
++ .dev_id = AM29LV800BT,
++ .name = "AMD AM29LV800BT",
++ .uaddr = {
++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */
++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */
++ },
++ .DevSize = SIZE_1MiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 4,
++ .regions = {
++ ERASEINFO(0x10000,15),
++ ERASEINFO(0x08000,1),
++ ERASEINFO(0x02000,2),
++ ERASEINFO(0x04000,1)
+ }
+ }, {
+- mfr_id: MANUFACTURER_TOSHIBA,
+- dev_id: TC58FVB641,
+- name: "Toshiba TC58FVB641",
+- DevSize: SIZE_8MiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 2,
+- regions: {ERASEINFO(0x02000,8),
+- ERASEINFO(0x10000,127)
++ .mfr_id = MANUFACTURER_AMD,
++ .dev_id = AM29F800BT,
++ .name = "AMD AM29F800BT",
++ .uaddr = {
++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */
++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */
++ },
++ .DevSize = SIZE_1MiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 4,
++ .regions = {
++ ERASEINFO(0x10000,15),
++ ERASEINFO(0x08000,1),
++ ERASEINFO(0x02000,2),
++ ERASEINFO(0x04000,1)
+ }
+ }, {
+- mfr_id: MANUFACTURER_TOSHIBA,
+- dev_id: TC58FVT641,
+- name: "Toshiba TC58FVT641",
+- DevSize: SIZE_8MiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 2,
+- regions: {ERASEINFO(0x10000,127),
+- ERASEINFO(0x02000,8)
++ .mfr_id = MANUFACTURER_AMD,
++ .dev_id = AM29F017D,
++ .name = "AMD AM29F017D",
++ .uaddr = {
++ [0] = MTD_UADDR_DONT_CARE /* x8 */
++ },
++ .DevSize = SIZE_2MiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 1,
++ .regions = {
++ ERASEINFO(0x10000,32),
++ }
++ }, {
++ .mfr_id = MANUFACTURER_AMD,
++ .dev_id = AM29F016D,
++ .name = "AMD AM29F016D",
++ .uaddr = {
++ [0] = MTD_UADDR_0x0555_0x02AA /* x8 */
++ },
++ .DevSize = SIZE_2MiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 1,
++ .regions = {
++ ERASEINFO(0x10000,32),
++ }
++ }, {
++ .mfr_id = MANUFACTURER_AMD,
++ .dev_id = AM29F080,
++ .name = "AMD AM29F080",
++ .uaddr = {
++ [0] = MTD_UADDR_0x0555_0x02AA /* x8 */
++ },
++ .DevSize = SIZE_1MiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 1,
++ .regions = {
++ ERASEINFO(0x10000,16),
++ }
++ }, {
++ .mfr_id = MANUFACTURER_AMD,
++ .dev_id = AM29F040,
++ .name = "AMD AM29F040",
++ .uaddr = {
++ [0] = MTD_UADDR_0x0555_0x02AA /* x8 */
++ },
++ .DevSize = SIZE_512KiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 1,
++ .regions = {
++ ERASEINFO(0x10000,8),
++ }
++ }, {
++ .mfr_id = MANUFACTURER_AMD,
++ .dev_id = AM29LV040B,
++ .name = "AMD AM29LV040B",
++ .uaddr = {
++ [0] = MTD_UADDR_0x0555_0x02AA /* x8 */
++ },
++ .DevSize = SIZE_512KiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 1,
++ .regions = {
++ ERASEINFO(0x10000,8),
++ }
++ }, {
++ .mfr_id = MANUFACTURER_ATMEL,
++ .dev_id = AT49BV512,
++ .name = "Atmel AT49BV512",
++ .uaddr = {
++ [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */
++ },
++ .DevSize = SIZE_64KiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 1,
++ .regions = {
++ ERASEINFO(0x10000,1)
++ }
++ }, {
++ .mfr_id = MANUFACTURER_ATMEL,
++ .dev_id = AT29LV512,
++ .name = "Atmel AT29LV512",
++ .uaddr = {
++ [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */
++ },
++ .DevSize = SIZE_64KiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 1,
++ .regions = {
++ ERASEINFO(0x80,256),
++ ERASEINFO(0x80,256)
++ }
++ }, {
++ .mfr_id = MANUFACTURER_ATMEL,
++ .dev_id = AT49BV16X,
++ .name = "Atmel AT49BV16X",
++ .uaddr = {
++ [0] = MTD_UADDR_0x0555_0x0AAA, /* x8 */
++ [1] = MTD_UADDR_0x0555_0x0AAA /* x16 */
++ },
++ .DevSize = SIZE_2MiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 2,
++ .regions = {
++ ERASEINFO(0x02000,8),
++ ERASEINFO(0x10000,31)
+ }
+ }, {
+- mfr_id: MANUFACTURER_FUJITSU,
+- dev_id: MBM29LV650UE,
+- name: "Fujitsu MBM29LV650UE",
+- DevSize: SIZE_8MiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 1,
+- regions: {ERASEINFO(0x10000,128)
+- }
+- }, {
+- mfr_id: MANUFACTURER_FUJITSU,
+- dev_id: MBM29LV320TE,
+- name: "Fujitsu MBM29LV320TE",
+- DevSize: SIZE_4MiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 2,
+- regions: {ERASEINFO(0x10000,63),
++ .mfr_id = MANUFACTURER_ATMEL,
++ .dev_id = AT49BV16XT,
++ .name = "Atmel AT49BV16XT",
++ .uaddr = {
++ [0] = MTD_UADDR_0x0555_0x0AAA, /* x8 */
++ [1] = MTD_UADDR_0x0555_0x0AAA /* x16 */
++ },
++ .DevSize = SIZE_2MiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 2,
++ .regions = {
++ ERASEINFO(0x10000,31),
+ ERASEINFO(0x02000,8)
+ }
+ }, {
+- mfr_id: MANUFACTURER_FUJITSU,
+- dev_id: MBM29LV320BE,
+- name: "Fujitsu MBM29LV320BE",
+- DevSize: SIZE_4MiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 2,
+- regions: {ERASEINFO(0x02000,8),
++ .mfr_id = MANUFACTURER_ATMEL,
++ .dev_id = AT49BV32X,
++ .name = "Atmel AT49BV32X",
++ .uaddr = {
++ [0] = MTD_UADDR_0x0555_0x0AAA, /* x8 */
++ [1] = MTD_UADDR_0x0555_0x0AAA /* x16 */
++ },
++ .DevSize = SIZE_4MiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 2,
++ .regions = {
++ ERASEINFO(0x02000,8),
+ ERASEINFO(0x10000,63)
+ }
+ }, {
+- mfr_id: MANUFACTURER_FUJITSU,
+- dev_id: MBM29LV160TE,
+- name: "Fujitsu MBM29LV160TE",
+- DevSize: SIZE_2MiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 4,
+- regions: {ERASEINFO(0x10000,31),
+- ERASEINFO(0x08000,1),
+- ERASEINFO(0x02000,2),
+- ERASEINFO(0x04000,1)
++ .mfr_id = MANUFACTURER_ATMEL,
++ .dev_id = AT49BV32XT,
++ .name = "Atmel AT49BV32XT",
++ .uaddr = {
++ [0] = MTD_UADDR_0x0555_0x0AAA, /* x8 */
++ [1] = MTD_UADDR_0x0555_0x0AAA /* x16 */
++ },
++ .DevSize = SIZE_4MiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 2,
++ .regions = {
++ ERASEINFO(0x10000,63),
++ ERASEINFO(0x02000,8)
+ }
+ }, {
+- mfr_id: MANUFACTURER_FUJITSU,
+- dev_id: MBM29LV160BE,
+- name: "Fujitsu MBM29LV160BE",
+- DevSize: SIZE_2MiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 4,
+- regions: {ERASEINFO(0x04000,1),
+- ERASEINFO(0x02000,2),
+- ERASEINFO(0x08000,1),
+- ERASEINFO(0x10000,31)
++ .mfr_id = MANUFACTURER_FUJITSU,
++ .dev_id = MBM29F040C,
++ .name = "Fujitsu MBM29F040C",
++ .uaddr = {
++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */
++ },
++ .DevSize = SIZE_512KiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 1,
++ .regions = {
++ ERASEINFO(0x10000,8)
++ }
++ }, {
++ .mfr_id = MANUFACTURER_FUJITSU,
++ .dev_id = MBM29LV650UE,
++ .name = "Fujitsu MBM29LV650UE",
++ .uaddr = {
++ [0] = MTD_UADDR_DONT_CARE /* x16 */
++ },
++ .DevSize = SIZE_8MiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 1,
++ .regions = {
++ ERASEINFO(0x10000,128)
++ }
++ }, {
++ .mfr_id = MANUFACTURER_FUJITSU,
++ .dev_id = MBM29LV320TE,
++ .name = "Fujitsu MBM29LV320TE",
++ .uaddr = {
++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */
++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */
++ },
++ .DevSize = SIZE_4MiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 2,
++ .regions = {
++ ERASEINFO(0x10000,63),
++ ERASEINFO(0x02000,8)
+ }
+ }, {
+- mfr_id: MANUFACTURER_FUJITSU,
+- dev_id: MBM29LV800BA,
+- name: "Fujitsu MBM29LV800BA",
+- DevSize: SIZE_1MiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 4,
+- regions: {ERASEINFO(0x04000,1),
+- ERASEINFO(0x02000,2),
+- ERASEINFO(0x08000,1),
+- ERASEINFO(0x10000,15)
++ .mfr_id = MANUFACTURER_FUJITSU,
++ .dev_id = MBM29LV320BE,
++ .name = "Fujitsu MBM29LV320BE",
++ .uaddr = {
++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */
++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */
++ },
++ .DevSize = SIZE_4MiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 2,
++ .regions = {
++ ERASEINFO(0x02000,8),
++ ERASEINFO(0x10000,63)
+ }
+ }, {
+- mfr_id: MANUFACTURER_FUJITSU,
+- dev_id: MBM29LV800TA,
+- name: "Fujitsu MBM29LV800TA",
+- DevSize: SIZE_1MiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 4,
+- regions: {ERASEINFO(0x10000,15),
++ .mfr_id = MANUFACTURER_FUJITSU,
++ .dev_id = MBM29LV160TE,
++ .name = "Fujitsu MBM29LV160TE",
++ .uaddr = {
++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */
++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */
++ },
++ .DevSize = SIZE_2MiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 4,
++ .regions = {
++ ERASEINFO(0x10000,31),
+ ERASEINFO(0x08000,1),
+ ERASEINFO(0x02000,2),
+ ERASEINFO(0x04000,1)
+ }
+ }, {
+- mfr_id: MANUFACTURER_AMD,
+- dev_id: AM29LV800BB,
+- name: "AMD AM29LV800BB",
+- DevSize: SIZE_1MiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 4,
+- regions: {ERASEINFO(0x04000,1),
++ .mfr_id = MANUFACTURER_FUJITSU,
++ .dev_id = MBM29LV160BE,
++ .name = "Fujitsu MBM29LV160BE",
++ .uaddr = {
++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */
++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */
++ },
++ .DevSize = SIZE_2MiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 4,
++ .regions = {
++ ERASEINFO(0x04000,1),
+ ERASEINFO(0x02000,2),
+ ERASEINFO(0x08000,1),
+- ERASEINFO(0x10000,15),
++ ERASEINFO(0x10000,31)
+ }
+ }, {
+- mfr_id: MANUFACTURER_AMD,
+- dev_id: AM29F800BB,
+- name: "AMD AM29F800BB",
+- DevSize: SIZE_1MiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 4,
+- regions: {ERASEINFO(0x04000,1),
++ .mfr_id = MANUFACTURER_FUJITSU,
++ .dev_id = MBM29LV800BA,
++ .name = "Fujitsu MBM29LV800BA",
++ .uaddr = {
++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */
++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */
++ },
++ .DevSize = SIZE_1MiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 4,
++ .regions = {
++ ERASEINFO(0x04000,1),
+ ERASEINFO(0x02000,2),
+ ERASEINFO(0x08000,1),
+- ERASEINFO(0x10000,15),
++ ERASEINFO(0x10000,15)
+ }
+ }, {
+- mfr_id: MANUFACTURER_AMD,
+- dev_id: AM29LV800BT,
+- name: "AMD AM29LV800BT",
+- DevSize: SIZE_1MiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 4,
+- regions: {ERASEINFO(0x10000,15),
++ .mfr_id = MANUFACTURER_FUJITSU,
++ .dev_id = MBM29LV800TA,
++ .name = "Fujitsu MBM29LV800TA",
++ .uaddr = {
++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */
++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */
++ },
++ .DevSize = SIZE_1MiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 4,
++ .regions = {
++ ERASEINFO(0x10000,15),
+ ERASEINFO(0x08000,1),
+ ERASEINFO(0x02000,2),
+ ERASEINFO(0x04000,1)
+ }
+ }, {
+- mfr_id: MANUFACTURER_AMD,
+- dev_id: AM29F800BT,
+- name: "AMD AM29F800BT",
+- DevSize: SIZE_1MiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 4,
+- regions: {ERASEINFO(0x10000,15),
+- ERASEINFO(0x08000,1),
++ .mfr_id = MANUFACTURER_FUJITSU,
++ .dev_id = MBM29LV400BC,
++ .name = "Fujitsu MBM29LV400BC",
++ .uaddr = {
++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */
++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */
++ },
++ .DevSize = SIZE_512KiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 4,
++ .regions = {
++ ERASEINFO(0x04000,1),
+ ERASEINFO(0x02000,2),
+- ERASEINFO(0x04000,1)
++ ERASEINFO(0x08000,1),
++ ERASEINFO(0x10000,7)
+ }
+ }, {
+- mfr_id: MANUFACTURER_AMD,
+- dev_id: AM29LV800BB,
+- name: "AMD AM29LV800BB",
+- DevSize: SIZE_1MiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 4,
+- regions: {ERASEINFO(0x10000,15),
++ .mfr_id = MANUFACTURER_FUJITSU,
++ .dev_id = MBM29LV400TC,
++ .name = "Fujitsu MBM29LV400TC",
++ .uaddr = {
++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */
++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */
++ },
++ .DevSize = SIZE_512KiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 4,
++ .regions = {
++ ERASEINFO(0x10000,7),
+ ERASEINFO(0x08000,1),
+ ERASEINFO(0x02000,2),
+ ERASEINFO(0x04000,1)
+ }
+ }, {
+- mfr_id: MANUFACTURER_INTEL,
+- dev_id: I28F004B3B,
+- name: "Intel 28F004B3B",
+- DevSize: SIZE_512KiB,
+- CmdSet: P_ID_INTEL_STD,
+- NumEraseRegions: 2,
+- regions: {
++ .mfr_id = MANUFACTURER_INTEL,
++ .dev_id = I28F004B3B,
++ .name = "Intel 28F004B3B",
++ .uaddr = {
++ [0] = MTD_UADDR_UNNECESSARY, /* x8 */
++ },
++ .DevSize = SIZE_512KiB,
++ .CmdSet = P_ID_INTEL_STD,
++ .NumEraseRegions= 2,
++ .regions = {
+ ERASEINFO(0x02000, 8),
+ ERASEINFO(0x10000, 7),
+ }
+ }, {
+- mfr_id: MANUFACTURER_INTEL,
+- dev_id: I28F004B3T,
+- name: "Intel 28F004B3T",
+- DevSize: SIZE_512KiB,
+- CmdSet: P_ID_INTEL_STD,
+- NumEraseRegions: 2,
+- regions: {
++ .mfr_id = MANUFACTURER_INTEL,
++ .dev_id = I28F004B3T,
++ .name = "Intel 28F004B3T",
++ .uaddr = {
++ [0] = MTD_UADDR_UNNECESSARY, /* x8 */
++ },
++ .DevSize = SIZE_512KiB,
++ .CmdSet = P_ID_INTEL_STD,
++ .NumEraseRegions= 2,
++ .regions = {
+ ERASEINFO(0x10000, 7),
+ ERASEINFO(0x02000, 8),
+ }
+ }, {
+- mfr_id: MANUFACTURER_INTEL,
+- dev_id: I28F400B3B,
+- name: "Intel 28F400B3B",
+- DevSize: SIZE_512KiB,
+- CmdSet: P_ID_INTEL_STD,
+- NumEraseRegions: 2,
+- regions: {
++ .mfr_id = MANUFACTURER_INTEL,
++ .dev_id = I28F400B3B,
++ .name = "Intel 28F400B3B",
++ .uaddr = {
++ [0] = MTD_UADDR_UNNECESSARY, /* x8 */
++ [1] = MTD_UADDR_UNNECESSARY, /* x16 */
++ },
++ .DevSize = SIZE_512KiB,
++ .CmdSet = P_ID_INTEL_STD,
++ .NumEraseRegions= 2,
++ .regions = {
+ ERASEINFO(0x02000, 8),
+ ERASEINFO(0x10000, 7),
+ }
+ }, {
+- mfr_id: MANUFACTURER_INTEL,
+- dev_id: I28F400B3T,
+- name: "Intel 28F400B3T",
+- DevSize: SIZE_512KiB,
+- CmdSet: P_ID_INTEL_STD,
+- NumEraseRegions: 2,
+- regions: {
++ .mfr_id = MANUFACTURER_INTEL,
++ .dev_id = I28F400B3T,
++ .name = "Intel 28F400B3T",
++ .uaddr = {
++ [0] = MTD_UADDR_UNNECESSARY, /* x8 */
++ [1] = MTD_UADDR_UNNECESSARY, /* x16 */
++ },
++ .DevSize = SIZE_512KiB,
++ .CmdSet = P_ID_INTEL_STD,
++ .NumEraseRegions= 2,
++ .regions = {
+ ERASEINFO(0x10000, 7),
+ ERASEINFO(0x02000, 8),
+ }
+ }, {
+- mfr_id: MANUFACTURER_INTEL,
+- dev_id: I28F008B3B,
+- name: "Intel 28F008B3B",
+- DevSize: SIZE_1MiB,
+- CmdSet: P_ID_INTEL_STD,
+- NumEraseRegions: 2,
+- regions: {
++ .mfr_id = MANUFACTURER_INTEL,
++ .dev_id = I28F008B3B,
++ .name = "Intel 28F008B3B",
++ .uaddr = {
++ [0] = MTD_UADDR_UNNECESSARY, /* x8 */
++ },
++ .DevSize = SIZE_1MiB,
++ .CmdSet = P_ID_INTEL_STD,
++ .NumEraseRegions= 2,
++ .regions = {
+ ERASEINFO(0x02000, 8),
+ ERASEINFO(0x10000, 15),
+ }
+ }, {
+- mfr_id: MANUFACTURER_INTEL,
+- dev_id: I28F008B3T,
+- name: "Intel 28F008B3T",
+- DevSize: SIZE_1MiB,
+- CmdSet: P_ID_INTEL_STD,
+- NumEraseRegions: 2,
+- regions: {
++ .mfr_id = MANUFACTURER_INTEL,
++ .dev_id = I28F008B3T,
++ .name = "Intel 28F008B3T",
++ .uaddr = {
++ [0] = MTD_UADDR_UNNECESSARY, /* x8 */
++ },
++ .DevSize = SIZE_1MiB,
++ .CmdSet = P_ID_INTEL_STD,
++ .NumEraseRegions= 2,
++ .regions = {
+ ERASEINFO(0x10000, 15),
+ ERASEINFO(0x02000, 8),
+ }
+ }, {
+- mfr_id: MANUFACTURER_INTEL,
+- dev_id: I28F008S5,
+- name: "Intel 28F008S5",
+- DevSize: SIZE_1MiB,
+- CmdSet: P_ID_INTEL_EXT,
+- NumEraseRegions: 1,
+- regions: {ERASEINFO(0x10000,16),
+- }
+- }, {
+- mfr_id: MANUFACTURER_INTEL,
+- dev_id: I28F016S5,
+- name: "Intel 28F016S5",
+- DevSize: SIZE_2MiB,
+- CmdSet: P_ID_INTEL_EXT,
+- NumEraseRegions: 1,
+- regions: {ERASEINFO(0x10000,32),
+- }
+- }, {
+- mfr_id: MANUFACTURER_INTEL,
+- dev_id: I28F008SA,
+- name: "Intel 28F008SA",
+- DevSize: SIZE_1MiB,
+- CmdSet: P_ID_INTEL_STD,
+- NumEraseRegions: 1,
+- regions: {
++ .mfr_id = MANUFACTURER_INTEL,
++ .dev_id = I28F008S5,
++ .name = "Intel 28F008S5",
++ .uaddr = {
++ [0] = MTD_UADDR_UNNECESSARY, /* x8 */
++ },
++ .DevSize = SIZE_1MiB,
++ .CmdSet = P_ID_INTEL_EXT,
++ .NumEraseRegions= 1,
++ .regions = {
++ ERASEINFO(0x10000,16),
++ }
++ }, {
++ .mfr_id = MANUFACTURER_INTEL,
++ .dev_id = I28F016S5,
++ .name = "Intel 28F016S5",
++ .uaddr = {
++ [0] = MTD_UADDR_UNNECESSARY, /* x8 */
++ },
++ .DevSize = SIZE_2MiB,
++ .CmdSet = P_ID_INTEL_EXT,
++ .NumEraseRegions= 1,
++ .regions = {
++ ERASEINFO(0x10000,32),
++ }
++ }, {
++ .mfr_id = MANUFACTURER_INTEL,
++ .dev_id = I28F008SA,
++ .name = "Intel 28F008SA",
++ .uaddr = {
++ [0] = MTD_UADDR_UNNECESSARY, /* x8 */
++ },
++ .DevSize = SIZE_1MiB,
++ .CmdSet = P_ID_INTEL_STD,
++ .NumEraseRegions= 1,
++ .regions = {
+ ERASEINFO(0x10000, 16),
+ }
+ }, {
+- mfr_id: MANUFACTURER_INTEL,
+- dev_id: I28F800B3B,
+- name: "Intel 28F800B3B",
+- DevSize: SIZE_1MiB,
+- CmdSet: P_ID_INTEL_STD,
+- NumEraseRegions: 2,
+- regions: {
++ .mfr_id = MANUFACTURER_INTEL,
++ .dev_id = I28F800B3B,
++ .name = "Intel 28F800B3B",
++ .uaddr = {
++ [1] = MTD_UADDR_UNNECESSARY, /* x16 */
++ },
++ .DevSize = SIZE_1MiB,
++ .CmdSet = P_ID_INTEL_STD,
++ .NumEraseRegions= 2,
++ .regions = {
+ ERASEINFO(0x02000, 8),
+ ERASEINFO(0x10000, 15),
+ }
+ }, {
+- mfr_id: MANUFACTURER_INTEL,
+- dev_id: I28F800B3T,
+- name: "Intel 28F800B3T",
+- DevSize: SIZE_1MiB,
+- CmdSet: P_ID_INTEL_STD,
+- NumEraseRegions: 2,
+- regions: {
++ .mfr_id = MANUFACTURER_INTEL,
++ .dev_id = I28F800B3T,
++ .name = "Intel 28F800B3T",
++ .uaddr = {
++ [1] = MTD_UADDR_UNNECESSARY, /* x16 */
++ },
++ .DevSize = SIZE_1MiB,
++ .CmdSet = P_ID_INTEL_STD,
++ .NumEraseRegions= 2,
++ .regions = {
+ ERASEINFO(0x10000, 15),
+ ERASEINFO(0x02000, 8),
+ }
+ }, {
+- mfr_id: MANUFACTURER_INTEL,
+- dev_id: I28F016B3B,
+- name: "Intel 28F016B3B",
+- DevSize: SIZE_2MiB,
+- CmdSet: P_ID_INTEL_STD,
+- NumEraseRegions: 2,
+- regions: {
++ .mfr_id = MANUFACTURER_INTEL,
++ .dev_id = I28F016B3B,
++ .name = "Intel 28F016B3B",
++ .uaddr = {
++ [0] = MTD_UADDR_UNNECESSARY, /* x8 */
++ },
++ .DevSize = SIZE_2MiB,
++ .CmdSet = P_ID_INTEL_STD,
++ .NumEraseRegions= 2,
++ .regions = {
+ ERASEINFO(0x02000, 8),
+ ERASEINFO(0x10000, 31),
+ }
+ }, {
+- mfr_id: MANUFACTURER_INTEL,
+- dev_id: I28F016S3,
+- name: "Intel I28F016S3",
+- DevSize: SIZE_2MiB,
+- CmdSet: P_ID_INTEL_STD,
+- NumEraseRegions: 1,
+- regions: {
++ .mfr_id = MANUFACTURER_INTEL,
++ .dev_id = I28F016S3,
++ .name = "Intel I28F016S3",
++ .uaddr = {
++ [0] = MTD_UADDR_UNNECESSARY, /* x8 */
++ },
++ .DevSize = SIZE_2MiB,
++ .CmdSet = P_ID_INTEL_STD,
++ .NumEraseRegions= 1,
++ .regions = {
+ ERASEINFO(0x10000, 32),
+ }
+ }, {
+- mfr_id: MANUFACTURER_INTEL,
+- dev_id: I28F016B3T,
+- name: "Intel 28F016B3T",
+- DevSize: SIZE_2MiB,
+- CmdSet: P_ID_INTEL_STD,
+- NumEraseRegions: 2,
+- regions: {
++ .mfr_id = MANUFACTURER_INTEL,
++ .dev_id = I28F016B3T,
++ .name = "Intel 28F016B3T",
++ .uaddr = {
++ [0] = MTD_UADDR_UNNECESSARY, /* x8 */
++ },
++ .DevSize = SIZE_2MiB,
++ .CmdSet = P_ID_INTEL_STD,
++ .NumEraseRegions= 2,
++ .regions = {
+ ERASEINFO(0x10000, 31),
+ ERASEINFO(0x02000, 8),
+ }
+ }, {
+- mfr_id: MANUFACTURER_INTEL,
+- dev_id: I28F160B3B,
+- name: "Intel 28F160B3B",
+- DevSize: SIZE_2MiB,
+- CmdSet: P_ID_INTEL_STD,
+- NumEraseRegions: 2,
+- regions: {
++ .mfr_id = MANUFACTURER_INTEL,
++ .dev_id = I28F160B3B,
++ .name = "Intel 28F160B3B",
++ .uaddr = {
++ [1] = MTD_UADDR_UNNECESSARY, /* x16 */
++ },
++ .DevSize = SIZE_2MiB,
++ .CmdSet = P_ID_INTEL_STD,
++ .NumEraseRegions= 2,
++ .regions = {
+ ERASEINFO(0x02000, 8),
+ ERASEINFO(0x10000, 31),
+ }
+ }, {
+- mfr_id: MANUFACTURER_INTEL,
+- dev_id: I28F160B3T,
+- name: "Intel 28F160B3T",
+- DevSize: SIZE_2MiB,
+- CmdSet: P_ID_INTEL_STD,
+- NumEraseRegions: 2,
+- regions: {
++ .mfr_id = MANUFACTURER_INTEL,
++ .dev_id = I28F160B3T,
++ .name = "Intel 28F160B3T",
++ .uaddr = {
++ [1] = MTD_UADDR_UNNECESSARY, /* x16 */
++ },
++ .DevSize = SIZE_2MiB,
++ .CmdSet = P_ID_INTEL_STD,
++ .NumEraseRegions= 2,
++ .regions = {
+ ERASEINFO(0x10000, 31),
+ ERASEINFO(0x02000, 8),
+ }
+ }, {
+- mfr_id: MANUFACTURER_INTEL,
+- dev_id: I28F320B3B,
+- name: "Intel 28F320B3B",
+- DevSize: SIZE_4MiB,
+- CmdSet: P_ID_INTEL_STD,
+- NumEraseRegions: 2,
+- regions: {
++ .mfr_id = MANUFACTURER_INTEL,
++ .dev_id = I28F320B3B,
++ .name = "Intel 28F320B3B",
++ .uaddr = {
++ [1] = MTD_UADDR_UNNECESSARY, /* x16 */
++ },
++ .DevSize = SIZE_4MiB,
++ .CmdSet = P_ID_INTEL_STD,
++ .NumEraseRegions= 2,
++ .regions = {
+ ERASEINFO(0x02000, 8),
+ ERASEINFO(0x10000, 63),
+ }
+ }, {
+- mfr_id: MANUFACTURER_INTEL,
+- dev_id: I28F320B3T,
+- name: "Intel 28F320B3T",
+- DevSize: SIZE_4MiB,
+- CmdSet: P_ID_INTEL_STD,
+- NumEraseRegions: 2,
+- regions: {
++ .mfr_id = MANUFACTURER_INTEL,
++ .dev_id = I28F320B3T,
++ .name = "Intel 28F320B3T",
++ .uaddr = {
++ [1] = MTD_UADDR_UNNECESSARY, /* x16 */
++ },
++ .DevSize = SIZE_4MiB,
++ .CmdSet = P_ID_INTEL_STD,
++ .NumEraseRegions= 2,
++ .regions = {
+ ERASEINFO(0x10000, 63),
+ ERASEINFO(0x02000, 8),
+ }
+ }, {
+- mfr_id: MANUFACTURER_INTEL,
+- dev_id: I28F640B3B,
+- name: "Intel 28F640B3B",
+- DevSize: SIZE_8MiB,
+- CmdSet: P_ID_INTEL_STD,
+- NumEraseRegions: 2,
+- regions: {
++ .mfr_id = MANUFACTURER_INTEL,
++ .dev_id = I28F640B3B,
++ .name = "Intel 28F640B3B",
++ .uaddr = {
++ [1] = MTD_UADDR_UNNECESSARY, /* x16 */
++ },
++ .DevSize = SIZE_8MiB,
++ .CmdSet = P_ID_INTEL_STD,
++ .NumEraseRegions= 2,
++ .regions = {
+ ERASEINFO(0x02000, 8),
+ ERASEINFO(0x10000, 127),
+ }
+ }, {
+- mfr_id: MANUFACTURER_INTEL,
+- dev_id: I28F640B3T,
+- name: "Intel 28F640B3T",
+- DevSize: SIZE_8MiB,
+- CmdSet: P_ID_INTEL_STD,
+- NumEraseRegions: 2,
+- regions: {
++ .mfr_id = MANUFACTURER_INTEL,
++ .dev_id = I28F640B3T,
++ .name = "Intel 28F640B3T",
++ .uaddr = {
++ [1] = MTD_UADDR_UNNECESSARY, /* x16 */
++ },
++ .DevSize = SIZE_8MiB,
++ .CmdSet = P_ID_INTEL_STD,
++ .NumEraseRegions= 2,
++ .regions = {
+ ERASEINFO(0x10000, 127),
+ ERASEINFO(0x02000, 8),
+ }
+ }, {
+- mfr_id: MANUFACTURER_INTEL,
+- dev_id: I82802AB,
+- name: "Intel 82802AB",
+- DevSize: SIZE_512KiB,
+- CmdSet: P_ID_INTEL_EXT,
+- NumEraseRegions: 1,
+- regions: {ERASEINFO(0x10000,8),
+- }
+- }, {
+- mfr_id: MANUFACTURER_INTEL,
+- dev_id: I82802AC,
+- name: "Intel 82802AC",
+- DevSize: SIZE_1MiB,
+- CmdSet: P_ID_INTEL_EXT,
+- NumEraseRegions: 1,
+- regions: {ERASEINFO(0x10000,16),
+- }
+- }, {
+- mfr_id: MANUFACTURER_ST,
+- dev_id: M29W800T,
+- name: "ST M29W800T",
+- DevSize: SIZE_1MiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 4,
+- regions: {ERASEINFO(0x10000,15),
++ .mfr_id = MANUFACTURER_INTEL,
++ .dev_id = I82802AB,
++ .name = "Intel 82802AB",
++ .uaddr = {
++ [0] = MTD_UADDR_UNNECESSARY, /* x8 */
++ },
++ .DevSize = SIZE_512KiB,
++ .CmdSet = P_ID_INTEL_EXT,
++ .NumEraseRegions= 1,
++ .regions = {
++ ERASEINFO(0x10000,8),
++ }
++ }, {
++ .mfr_id = MANUFACTURER_INTEL,
++ .dev_id = I82802AC,
++ .name = "Intel 82802AC",
++ .uaddr = {
++ [0] = MTD_UADDR_UNNECESSARY, /* x8 */
++ },
++ .DevSize = SIZE_1MiB,
++ .CmdSet = P_ID_INTEL_EXT,
++ .NumEraseRegions= 1,
++ .regions = {
++ ERASEINFO(0x10000,16),
++ }
++ }, {
++ .mfr_id = MANUFACTURER_MACRONIX,
++ .dev_id = MX29LV160T,
++ .name = "MXIC MX29LV160T",
++ .uaddr = {
++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */
++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */
++ },
++ .DevSize = SIZE_2MiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 4,
++ .regions = {
++ ERASEINFO(0x10000,31),
+ ERASEINFO(0x08000,1),
+ ERASEINFO(0x02000,2),
+ ERASEINFO(0x04000,1)
+ }
+ }, {
+- mfr_id: MANUFACTURER_ST,
+- dev_id: M29W160DT,
+- name: "ST M29W160DT",
+- DevSize: SIZE_2MiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 4,
+- regions: {ERASEINFO(0x10000,31),
+- ERASEINFO(0x08000,1),
+- ERASEINFO(0x02000,2),
+- ERASEINFO(0x04000,1)
+- }
+- }, {
+- mfr_id: MANUFACTURER_ST,
+- dev_id: M29W160DB,
+- name: "ST M29W160DB",
+- DevSize: SIZE_2MiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 4,
+- regions: {ERASEINFO(0x04000,1),
++ .mfr_id = MANUFACTURER_MACRONIX,
++ .dev_id = MX29LV160B,
++ .name = "MXIC MX29LV160B",
++ .uaddr = {
++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */
++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */
++ },
++ .DevSize = SIZE_2MiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 4,
++ .regions = {
++ ERASEINFO(0x04000,1),
+ ERASEINFO(0x02000,2),
+ ERASEINFO(0x08000,1),
+ ERASEINFO(0x10000,31)
+ }
+ }, {
+- mfr_id: MANUFACTURER_ATMEL,
+- dev_id: AT49BV512,
+- name: "Atmel AT49BV512",
+- DevSize: SIZE_64KiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 1,
+- regions: {ERASEINFO(0x10000,1)
+- }
+- }, {
+- mfr_id: MANUFACTURER_ATMEL,
+- dev_id: AT29LV512,
+- name: "Atmel AT29LV512",
+- DevSize: SIZE_64KiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 1,
+- regions: {
+- ERASEINFO(0x80,256),
+- ERASEINFO(0x80,256)
+- }
+- }, {
+- mfr_id: MANUFACTURER_ATMEL,
+- dev_id: AT49BV16X,
+- name: "Atmel AT49BV16X",
+- DevSize: SIZE_2MiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 2,
+- regions: {ERASEINFO(0x02000,8),
+- ERASEINFO(0x10000,31)
++ .mfr_id = MANUFACTURER_MACRONIX,
++ .dev_id = MX29F016,
++ .name = "Macronix MX29F016",
++ .uaddr = {
++ [0] = MTD_UADDR_0x0555_0x02AA /* x8 */
++ },
++ .DevSize = SIZE_2MiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 1,
++ .regions = {
++ ERASEINFO(0x10000,32),
++ }
++ }, {
++ .mfr_id = MANUFACTURER_MACRONIX,
++ .dev_id = MX29F004T,
++ .name = "Macronix MX29F004T",
++ .uaddr = {
++ [0] = MTD_UADDR_0x0555_0x02AA /* x8 */
++ },
++ .DevSize = SIZE_512KiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 4,
++ .regions = {
++ ERASEINFO(0x10000,7),
++ ERASEINFO(0x08000,1),
++ ERASEINFO(0x02000,2),
++ ERASEINFO(0x04000,1),
+ }
+ }, {
+- mfr_id: MANUFACTURER_ATMEL,
+- dev_id: AT49BV16XT,
+- name: "Atmel AT49BV16XT",
+- DevSize: SIZE_2MiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 2,
+- regions: {ERASEINFO(0x10000,31),
+- ERASEINFO(0x02000,8)
++ .mfr_id = MANUFACTURER_MACRONIX,
++ .dev_id = MX29F004B,
++ .name = "Macronix MX29F004B",
++ .uaddr = {
++ [0] = MTD_UADDR_0x0555_0x02AA /* x8 */
++ },
++ .DevSize = SIZE_512KiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 4,
++ .regions = {
++ ERASEINFO(0x04000,1),
++ ERASEINFO(0x02000,2),
++ ERASEINFO(0x08000,1),
++ ERASEINFO(0x10000,7),
+ }
+ }, {
+- mfr_id: MANUFACTURER_ATMEL,
+- dev_id: AT49BV32X,
+- name: "Atmel AT49BV32X",
+- DevSize: SIZE_4MiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 2,
+- regions: {ERASEINFO(0x02000,8),
+- ERASEINFO(0x10000,63)
++ .mfr_id = MANUFACTURER_PMC,
++ .dev_id = PM49FL002,
++ .name = "PMC Pm49FL002",
++ .uaddr = {
++ [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */
++ },
++ .DevSize = SIZE_256KiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 1,
++ .regions = {
++ ERASEINFO( 0x01000, 64 )
++ }
++ }, {
++ .mfr_id = MANUFACTURER_PMC,
++ .dev_id = PM49FL004,
++ .name = "PMC Pm49FL004",
++ .uaddr = {
++ [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */
++ },
++ .DevSize = SIZE_512KiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 1,
++ .regions = {
++ ERASEINFO( 0x01000, 128 )
++ }
++ }, {
++ .mfr_id = MANUFACTURER_PMC,
++ .dev_id = PM49FL008,
++ .name = "PMC Pm49FL008",
++ .uaddr = {
++ [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */
++ },
++ .DevSize = SIZE_1MiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 1,
++ .regions = {
++ ERASEINFO( 0x01000, 256 )
++ }
++ }, {
++ .mfr_id = MANUFACTURER_SST,
++ .dev_id = SST39LF512,
++ .name = "SST 39LF512",
++ .uaddr = {
++ [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */
++ },
++ .DevSize = SIZE_64KiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 1,
++ .regions = {
++ ERASEINFO(0x01000,16),
++ }
++ }, {
++ .mfr_id = MANUFACTURER_SST,
++ .dev_id = SST39LF010,
++ .name = "SST 39LF010",
++ .uaddr = {
++ [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */
++ },
++ .DevSize = SIZE_128KiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 1,
++ .regions = {
++ ERASEINFO(0x01000,32),
++ }
++ }, {
++ .mfr_id = MANUFACTURER_SST,
++ .dev_id = SST39LF020,
++ .name = "SST 39LF020",
++ .uaddr = {
++ [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */
++ },
++ .DevSize = SIZE_256KiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 1,
++ .regions = {
++ ERASEINFO(0x01000,64),
++ }
++ }, {
++ .mfr_id = MANUFACTURER_SST,
++ .dev_id = SST39LF040,
++ .name = "SST 39LF040",
++ .uaddr = {
++ [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */
++ },
++ .DevSize = SIZE_512KiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 1,
++ .regions = {
++ ERASEINFO(0x01000,128),
++ }
++ }, {
++ .mfr_id = MANUFACTURER_SST,
++ .dev_id = SST39SF010A,
++ .name = "SST 39SF010A",
++ .uaddr = {
++ [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */
++ },
++ .DevSize = SIZE_128KiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 1,
++ .regions = {
++ ERASEINFO(0x01000,32),
++ }
++ }, {
++ .mfr_id = MANUFACTURER_SST,
++ .dev_id = SST39SF020A,
++ .name = "SST 39SF020A",
++ .uaddr = {
++ [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */
++ },
++ .DevSize = SIZE_256KiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 1,
++ .regions = {
++ ERASEINFO(0x01000,64),
++ }
++ }, {
++ .mfr_id = MANUFACTURER_SST,
++ .dev_id = SST49LF004B,
++ .name = "SST 49LF004B",
++ .uaddr = {
++ [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */
++ },
++ .DevSize = SIZE_512KiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 1,
++ .regions = {
++ ERASEINFO(0x01000,128),
++ }
++ }, {
++ .mfr_id = MANUFACTURER_SST,
++ .dev_id = SST49LF030A,
++ .name = "SST 49LF030A",
++ .uaddr = {
++ [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */
++ },
++ .DevSize = SIZE_512KiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 1,
++ .regions = {
++ ERASEINFO(0x01000,96),
++ }
++ }, {
++ .mfr_id = MANUFACTURER_SST,
++ .dev_id = SST49LF040A,
++ .name = "SST 49LF040A",
++ .uaddr = {
++ [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */
++ },
++ .DevSize = SIZE_512KiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 1,
++ .regions = {
++ ERASEINFO(0x01000,128),
++ }
++ }, {
++ .mfr_id = MANUFACTURER_SST,
++ .dev_id = SST49LF080A,
++ .name = "SST 49LF080A",
++ .uaddr = {
++ [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */
++ },
++ .DevSize = SIZE_1MiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 1,
++ .regions = {
++ ERASEINFO(0x01000,256),
++ }
++ }, {
++ .mfr_id = MANUFACTURER_ST, /* FIXME - CFI device? */
++ .dev_id = M29W800DT,
++ .name = "ST M29W800DT",
++ .uaddr = {
++ [0] = MTD_UADDR_0x5555_0x2AAA, /* x8 */
++ [1] = MTD_UADDR_0x5555_0x2AAA /* x16 */
++ },
++ .DevSize = SIZE_1MiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 4,
++ .regions = {
++ ERASEINFO(0x10000,15),
++ ERASEINFO(0x08000,1),
++ ERASEINFO(0x02000,2),
++ ERASEINFO(0x04000,1)
+ }
+ }, {
+- mfr_id: MANUFACTURER_ATMEL,
+- dev_id: AT49BV32XT,
+- name: "Atmel AT49BV32XT",
+- DevSize: SIZE_4MiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 2,
+- regions: {ERASEINFO(0x10000,63),
+- ERASEINFO(0x02000,8)
++ .mfr_id = MANUFACTURER_ST, /* FIXME - CFI device? */
++ .dev_id = M29W800DB,
++ .name = "ST M29W800DB",
++ .uaddr = {
++ [0] = MTD_UADDR_0x5555_0x2AAA, /* x8 */
++ [1] = MTD_UADDR_0x5555_0x2AAA /* x16 */
++ },
++ .DevSize = SIZE_1MiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 4,
++ .regions = {
++ ERASEINFO(0x04000,1),
++ ERASEINFO(0x02000,2),
++ ERASEINFO(0x08000,1),
++ ERASEINFO(0x10000,15)
+ }
+ }, {
+- mfr_id: MANUFACTURER_AMD,
+- dev_id: AM29F017D,
+- name: "AMD AM29F017D",
+- DevSize: SIZE_2MiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 1,
+- regions: {ERASEINFO(0x10000,32),
+- }
+- }, {
+- mfr_id: MANUFACTURER_AMD,
+- dev_id: AM29F016,
+- name: "AMD AM29F016",
+- DevSize: SIZE_2MiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 1,
+- regions: {ERASEINFO(0x10000,32),
+- }
+- }, {
+- mfr_id: MANUFACTURER_AMD,
+- dev_id: AM29F080,
+- name: "AMD AM29F080",
+- DevSize: SIZE_1MiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 1,
+- regions: {ERASEINFO(0x10000,16),
+- }
+- }, {
+- mfr_id: MANUFACTURER_AMD,
+- dev_id: AM29F040,
+- name: "AMD AM29F040",
+- DevSize: SIZE_512KiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 1,
+- regions: {ERASEINFO(0x10000,8),
+- }
+- }, {
+- mfr_id: MANUFACTURER_AMD,
+- dev_id: AM29LV040B,
+- name: "AMD AM29LV040B",
+- DevSize: SIZE_512KiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 1,
+- regions: {ERASEINFO(0x10000,8),
+- }
+- }, {
+- mfr_id: MANUFACTURER_ST,
+- dev_id: M29W040B,
+- name: "ST M29W040B",
+- DevSize: SIZE_512KiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 1,
+- regions: {ERASEINFO(0x10000,8),
+- }
+- }, {
+- mfr_id: MANUFACTURER_MACRONIX,
+- dev_id: MX29LV160T,
+- name: "MXIC MX29LV160T",
+- DevSize: SIZE_2MiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 4,
+- regions: {ERASEINFO(0x10000,31),
++ .mfr_id = MANUFACTURER_ST, /* FIXME - CFI device? */
++ .dev_id = M29W160DT,
++ .name = "ST M29W160DT",
++ .uaddr = {
++ [0] = MTD_UADDR_0x0555_0x02AA, /* x8 */
++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */
++ },
++ .DevSize = SIZE_2MiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 4,
++ .regions = {
++ ERASEINFO(0x10000,31),
+ ERASEINFO(0x08000,1),
+ ERASEINFO(0x02000,2),
+ ERASEINFO(0x04000,1)
+ }
+ }, {
+- mfr_id: MANUFACTURER_MACRONIX,
+- dev_id: MX29LV160B,
+- name: "MXIC MX29LV160B",
+- DevSize: SIZE_2MiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 4,
+- regions: {ERASEINFO(0x04000,1),
++ .mfr_id = MANUFACTURER_ST, /* FIXME - CFI device? */
++ .dev_id = M29W160DB,
++ .name = "ST M29W160DB",
++ .uaddr = {
++ [0] = MTD_UADDR_0x0555_0x02AA, /* x8 */
++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */
++ },
++ .DevSize = SIZE_2MiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 4,
++ .regions = {
++ ERASEINFO(0x04000,1),
+ ERASEINFO(0x02000,2),
+ ERASEINFO(0x08000,1),
+ ERASEINFO(0x10000,31)
+ }
+ }, {
+- mfr_id: MANUFACTURER_MACRONIX,
+- dev_id: MX29F016,
+- name: "Macronix MX29F016",
+- DevSize: SIZE_2MiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 1,
+- regions: {ERASEINFO(0x10000,32),
+- }
+- }, {
+- mfr_id: MANUFACTURER_MACRONIX,
+- dev_id: MX29F004T,
+- name: "Macronix MX29F004T",
+- DevSize: SIZE_512KiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 4,
+- regions: {ERASEINFO(0x10000,7),
++ .mfr_id = MANUFACTURER_ST,
++ .dev_id = M29W040B,
++ .name = "ST M29W040B",
++ .uaddr = {
++ [0] = MTD_UADDR_0x0555_0x02AA /* x8 */
++ },
++ .DevSize = SIZE_512KiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 1,
++ .regions = {
++ ERASEINFO(0x10000,8),
++ }
++ }, {
++ .mfr_id = MANUFACTURER_TOSHIBA,
++ .dev_id = TC58FVT160,
++ .name = "Toshiba TC58FVT160",
++ .uaddr = {
++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */
++ [1] = MTD_UADDR_0x0555_0x02AA /* x16 */
++ },
++ .DevSize = SIZE_2MiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 4,
++ .regions = {
++ ERASEINFO(0x10000,31),
+ ERASEINFO(0x08000,1),
+ ERASEINFO(0x02000,2),
+- ERASEINFO(0x04000,1),
++ ERASEINFO(0x04000,1)
+ }
+ }, {
+- mfr_id: MANUFACTURER_MACRONIX,
+- dev_id: MX29F004B,
+- name: "Macronix MX29F004B",
+- DevSize: SIZE_512KiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 4,
+- regions: {ERASEINFO(0x04000,1),
++ .mfr_id = MANUFACTURER_TOSHIBA,
++ .dev_id = TC58FVB160,
++ .name = "Toshiba TC58FVB160",
++ .uaddr = {
++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */
++ [1] = MTD_UADDR_0x0555_0x02AA /* x16 */
++ },
++ .DevSize = SIZE_2MiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 4,
++ .regions = {
++ ERASEINFO(0x04000,1),
+ ERASEINFO(0x02000,2),
+ ERASEINFO(0x08000,1),
+- ERASEINFO(0x10000,7),
++ ERASEINFO(0x10000,31)
++ }
++ }, {
++ .mfr_id = MANUFACTURER_TOSHIBA,
++ .dev_id = TC58FVB321,
++ .name = "Toshiba TC58FVB321",
++ .uaddr = {
++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */
++ [1] = MTD_UADDR_0x0555_0x02AA /* x16 */
++ },
++ .DevSize = SIZE_4MiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 2,
++ .regions = {
++ ERASEINFO(0x02000,8),
++ ERASEINFO(0x10000,63)
++ }
++ }, {
++ .mfr_id = MANUFACTURER_TOSHIBA,
++ .dev_id = TC58FVT321,
++ .name = "Toshiba TC58FVT321",
++ .uaddr = {
++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */
++ [1] = MTD_UADDR_0x0555_0x02AA /* x16 */
++ },
++ .DevSize = SIZE_4MiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 2,
++ .regions = {
++ ERASEINFO(0x10000,63),
++ ERASEINFO(0x02000,8)
++ }
++ }, {
++ .mfr_id = MANUFACTURER_TOSHIBA,
++ .dev_id = TC58FVB641,
++ .name = "Toshiba TC58FVB641",
++ .uaddr = {
++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */
++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */
++ },
++ .DevSize = SIZE_8MiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 2,
++ .regions = {
++ ERASEINFO(0x02000,8),
++ ERASEINFO(0x10000,127)
++ }
++ }, {
++ .mfr_id = MANUFACTURER_TOSHIBA,
++ .dev_id = TC58FVT641,
++ .name = "Toshiba TC58FVT641",
++ .uaddr = {
++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */
++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */
++ },
++ .DevSize = SIZE_8MiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 2,
++ .regions = {
++ ERASEINFO(0x10000,127),
++ ERASEINFO(0x02000,8)
+ }
+ }, {
+- mfr_id: MANUFACTURER_SST,
+- dev_id: SST39LF512,
+- name: "SST 39LF512",
+- DevSize: SIZE_64KiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 1,
+- regions: {ERASEINFO(0x01000,16),
+- }
+- }, {
+- mfr_id: MANUFACTURER_SST,
+- dev_id: SST39LF010,
+- name: "SST 39LF010",
+- DevSize: SIZE_128KiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 1,
+- regions: {ERASEINFO(0x01000,32),
+- }
+- }, {
+- mfr_id: MANUFACTURER_SST,
+- dev_id: SST39LF020,
+- name: "SST 39LF020",
+- DevSize: SIZE_256KiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 1,
+- regions: {ERASEINFO(0x01000,64),
+- }
+- }, {
+- mfr_id: MANUFACTURER_SST,
+- dev_id: SST39LF040,
+- name: "SST 39LF040",
+- DevSize: SIZE_512KiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 1,
+- regions: {ERASEINFO(0x01000,128),
+- }
+- }, {
+- mfr_id: MANUFACTURER_SST,
+- dev_id: SST39SF010A,
+- name: "SST 39SF010A",
+- DevSize: SIZE_128KiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 1,
+- regions: {ERASEINFO(0x01000,32),
+- }
+- }, {
+- mfr_id: MANUFACTURER_SST,
+- dev_id: SST39SF020A,
+- name: "SST 39SF020A",
+- DevSize: SIZE_256KiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 1,
+- regions: {ERASEINFO(0x01000,64),
+- }
+- }, {
+- mfr_id: MANUFACTURER_SST,
+- dev_id: SST49LF030A,
+- name: "SST 49LF030A",
+- DevSize: SIZE_512KiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 1,
+- regions: {ERASEINFO(0x01000,96),
+- }
+- }, {
+- mfr_id: MANUFACTURER_SST,
+- dev_id: SST49LF040A,
+- name: "SST 49LF040A",
+- DevSize: SIZE_512KiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 1,
+- regions: {ERASEINFO(0x01000,128),
+- }
+- }, {
+- mfr_id: MANUFACTURER_SST,
+- dev_id: SST49LF080A,
+- name: "SST 49LF080A",
+- DevSize: SIZE_1MiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 1,
+- regions: {ERASEINFO(0x01000,256),
++ .mfr_id = MANUFACTURER_WINBOND,
++ .dev_id = W49V002A,
++ .name = "Winbond W49V002A",
++ .uaddr = {
++ [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */
++ },
++ .DevSize = SIZE_256KiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 4,
++ .regions = {
++ ERASEINFO(0x10000, 3),
++ ERASEINFO(0x08000, 1),
++ ERASEINFO(0x02000, 2),
++ ERASEINFO(0x04000, 1),
+ }
+ }
+ };
+@@ -907,7 +1452,7 @@
+ static int cfi_jedec_setup(struct cfi_private *p_cfi, int index);
+
+ static int jedec_probe_chip(struct map_info *map, __u32 base,
+- struct flchip *chips, struct cfi_private *cfi);
++ unsigned long *chip_map, struct cfi_private *cfi);
+
+ struct mtd_info *jedec_probe(struct map_info *map);
+
+@@ -944,11 +1489,43 @@
+ * this should be safe.
+ */
+ cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL);
++ /* FIXME - should have reset delay before continuing */
++}
+
++
++static inline __u8 finfo_uaddr(const struct amd_flash_info *finfo, int device_type)
++{
++ int uaddr_idx;
++ __u8 uaddr = MTD_UADDR_NOT_SUPPORTED;
++
++ switch ( device_type ) {
++ case CFI_DEVICETYPE_X8: uaddr_idx = 0; break;
++ case CFI_DEVICETYPE_X16: uaddr_idx = 1; break;
++ case CFI_DEVICETYPE_X32: uaddr_idx = 2; break;
++ default:
++ printk(KERN_NOTICE "MTD: %s(): unknown device_type %d\n",
++ __func__, device_type);
++ goto uaddr_done;
++ }
++
++ uaddr = finfo->uaddr[uaddr_idx];
++
++ if (uaddr != MTD_UADDR_NOT_SUPPORTED ) {
++ /* ASSERT("The unlock addresses for non-8-bit mode
++ are bollocks. We don't really need an array."); */
++ uaddr = finfo->uaddr[0];
++ }
++
++ uaddr_done:
++ return uaddr;
+ }
++
++
+ static int cfi_jedec_setup(struct cfi_private *p_cfi, int index)
+ {
+ int i,num_erase_regions;
++ unsigned long mask;
++ __u8 uaddr;
+
+ printk("Found: %s\n",jedec_table[index].name);
+
+@@ -971,41 +1548,170 @@
+ p_cfi->cfiq->EraseRegionInfo[i] = jedec_table[index].regions[i];
+ }
+ p_cfi->cmdset_priv = 0;
++
++ /* This may be redundant for some cases, but it doesn't hurt */
++ p_cfi->mfr = jedec_table[index].mfr_id;
++ p_cfi->id = jedec_table[index].dev_id;
++
++ uaddr = finfo_uaddr(&jedec_table[index], p_cfi->device_type);
++ if ( uaddr == MTD_UADDR_NOT_SUPPORTED ) {
++ kfree( p_cfi->cfiq );
++ return 0;
++ }
++
++ /* Mask out address bits which are smaller than the device type */
++ mask = ~(p_cfi->device_type-1);
++ p_cfi->addr_unlock1 = unlock_addrs[uaddr].addr1 & mask;
++ p_cfi->addr_unlock2 = unlock_addrs[uaddr].addr2 & mask;
++
+ return 1; /* ok */
+ }
+
+-static int jedec_probe_chip(struct map_info *map, __u32 base,
+- struct flchip *chips, struct cfi_private *cfi)
++
++/*
++ * There is a BIG problem properly ID'ing the JEDEC devic and guaranteeing
++ * the mapped address, unlock addresses, and proper chip ID. This function
++ * attempts to minimize errors. It is doubtfull that this probe will ever
++ * be perfect - consequently there should be some module parameters that
++ * could be manually specified to force the chip info.
++ */
++static inline int jedec_match( __u32 base,
++ struct map_info *map,
++ struct cfi_private *cfi,
++ const struct amd_flash_info *finfo )
+ {
+- int i;
+- int unlockpass = 0;
++ int rc = 0; /* failure until all tests pass */
++ u32 mfr, id;
++ __u8 uaddr;
++ unsigned long mask;
+
+- if (!cfi->numchips) {
++ /*
++ * The IDs must match. For X16 and X32 devices operating in
++ * a lower width ( X8 or X16 ), the device ID's are usually just
++ * the lower byte(s) of the larger device ID for wider mode. If
++ * a part is found that doesn't fit this assumption (device id for
++ * smaller width mode is completely unrealated to full-width mode)
++ * then the jedec_table[] will have to be augmented with the IDs
++ * for different widths.
++ */
+ switch (cfi->device_type) {
+ case CFI_DEVICETYPE_X8:
+- cfi->addr_unlock1 = 0x555;
+- cfi->addr_unlock2 = 0x2aa;
++ mfr = (__u8)finfo->mfr_id;
++ id = (__u8)finfo->dev_id;
+ break;
+ case CFI_DEVICETYPE_X16:
+- cfi->addr_unlock1 = 0xaaa;
+- if (map->buswidth == cfi->interleave) {
+- /* X16 chip(s) in X8 mode */
+- cfi->addr_unlock2 = 0x555;
+- } else {
+- cfi->addr_unlock2 = 0x554;
+- }
++ mfr = (__u16)finfo->mfr_id;
++ id = (__u16)finfo->dev_id;
+ break;
+ case CFI_DEVICETYPE_X32:
+- cfi->addr_unlock1 = 0x1555;
+- cfi->addr_unlock2 = 0xaaa;
++ mfr = (__u16)finfo->mfr_id;
++ id = (__u32)finfo->dev_id;
+ break;
+ default:
+- printk(KERN_NOTICE "Eep. Unknown jedec_probe device type %d\n", cfi->device_type);
+- return 0;
++ printk(KERN_WARNING
++ "MTD %s(): Unsupported device type %d\n",
++ __func__, cfi->device_type);
++ goto match_done;
++ }
++ if ( cfi->mfr != mfr || cfi->id != id ) {
++ goto match_done;
++ }
++
++ /* the part size must fit in the memory window */
++ DEBUG( MTD_DEBUG_LEVEL3,
++ "MTD %s(): Check fit 0x%.8x + 0x%.8x = 0x%.8x\n",
++ __func__, base, 1 << finfo->DevSize, base + (1 << finfo->DevSize) );
++ if ( base + cfi->interleave * ( 1 << finfo->DevSize ) > map->size ) {
++ DEBUG( MTD_DEBUG_LEVEL3,
++ "MTD %s(): 0x%.4x 0x%.4x %dKiB doesn't fit\n",
++ __func__, finfo->mfr_id, finfo->dev_id,
++ 1 << finfo->DevSize );
++ goto match_done;
++ }
++
++ uaddr = finfo_uaddr(finfo, cfi->device_type);
++ if ( uaddr == MTD_UADDR_NOT_SUPPORTED ) {
++ goto match_done;
++ }
++
++ mask = ~(cfi->device_type-1);
++
++ DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): check unlock addrs 0x%.4x 0x%.4x\n",
++ __func__, cfi->addr_unlock1, cfi->addr_unlock2 );
++ if ( MTD_UADDR_UNNECESSARY != uaddr && MTD_UADDR_DONT_CARE != uaddr
++ && ( (unlock_addrs[uaddr].addr1 & mask) != cfi->addr_unlock1 ||
++ (unlock_addrs[uaddr].addr2 & mask) != cfi->addr_unlock2 ) ) {
++ DEBUG( MTD_DEBUG_LEVEL3,
++ "MTD %s(): 0x%.4x 0x%.4x did not match\n",
++ __func__,
++ unlock_addrs[uaddr].addr1 & mask,
++ unlock_addrs[uaddr].addr2 & mask);
++ goto match_done;
+ }
++
++ /*
++ * Make sure the ID's dissappear when the device is taken out of
++ * ID mode. The only time this should fail when it should succeed
++ * is when the ID's are written as data to the same
++ * addresses. For this rare and unfortunate case the chip
++ * cannot be probed correctly.
++ * FIXME - write a driver that takes all of the chip info as
++ * module parameters, doesn't probe but forces a load.
++ */
++ DEBUG( MTD_DEBUG_LEVEL3,
++ "MTD %s(): check ID's disappear when not in ID mode\n",
++ __func__ );
++ jedec_reset( base, map, cfi );
++ mfr = jedec_read_mfr( map, base, cfi );
++ id = jedec_read_id( map, base, cfi );
++ if ( mfr == cfi->mfr && id == cfi->id ) {
++ DEBUG( MTD_DEBUG_LEVEL3,
++ "MTD %s(): ID 0x%.2x:0x%.2x did not change after reset:\n"
++ "You might need to manually specify JEDEC parameters.\n",
++ __func__, cfi->mfr, cfi->id );
++ goto match_done;
++ }
++
++ /* all tests passed - mark as success */
++ rc = 1;
++
++ /*
++ * Put the device back in ID mode - only need to do this if we
++ * were truly frobbing a real device.
++ */
++ DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): return to ID mode\n", __func__ );
++ if(cfi->addr_unlock1) {
++ cfi_send_gen_cmd(0xaa, cfi->addr_unlock1, base, map, cfi, CFI_DEVICETYPE_X8, NULL);
++ cfi_send_gen_cmd(0x55, cfi->addr_unlock2, base, map, cfi, CFI_DEVICETYPE_X8, NULL);
+ }
++ cfi_send_gen_cmd(0x90, cfi->addr_unlock1, base, map, cfi, CFI_DEVICETYPE_X8, NULL);
++ /* FIXME - should have a delay before continuing */
++
++ match_done:
++ return rc;
++}
++
++
++static int jedec_probe_chip(struct map_info *map, __u32 base,
++ unsigned long *chip_map, struct cfi_private *cfi)
++{
++ int i;
++ enum uaddr uaddr_idx = MTD_UADDR_NOT_SUPPORTED;
+
+ retry:
++ if (!cfi->numchips) {
++ unsigned long mask = ~(cfi->device_type-1);
++
++ uaddr_idx++;
++
++ if (MTD_UADDR_UNNECESSARY == uaddr_idx)
++ return 0;
++
++ /* Mask out address bits which are smaller than the device type */
++ cfi->addr_unlock1 = unlock_addrs[uaddr_idx].addr1 & mask;
++ cfi->addr_unlock2 = unlock_addrs[uaddr_idx].addr2 & mask;
++ }
++
+ /* Make certain we aren't probing past the end of map */
+ if (base >= map->size) {
+ printk(KERN_NOTICE
+@@ -1038,6 +1744,7 @@
+ cfi_send_gen_cmd(0x55, cfi->addr_unlock2, base, map, cfi, CFI_DEVICETYPE_X8, NULL);
+ }
+ cfi_send_gen_cmd(0x90, cfi->addr_unlock1, base, map, cfi, CFI_DEVICETYPE_X8, NULL);
++ /* FIXME - should have a delay before continuing */
+
+ if (!cfi->numchips) {
+ /* This is the first time we're called. Set up the CFI
+@@ -1045,26 +1752,21 @@
+
+ cfi->mfr = jedec_read_mfr(map, base, cfi);
+ cfi->id = jedec_read_id(map, base, cfi);
+- printk(KERN_INFO "Search for id:(%02x %02x) interleave(%d) type(%d)\n",
++ DEBUG(MTD_DEBUG_LEVEL3,
++ "Search for id:(%02x %02x) interleave(%d) type(%d)\n",
+ cfi->mfr, cfi->id, cfi->interleave, cfi->device_type);
+ for (i=0; i<sizeof(jedec_table)/sizeof(jedec_table[0]); i++) {
+- if (cfi->mfr == jedec_table[i].mfr_id &&
+- cfi->id == jedec_table[i].dev_id) {
++ if ( jedec_match( base, map, cfi, &jedec_table[i] ) ) {
++ DEBUG( MTD_DEBUG_LEVEL3,
++ "MTD %s(): matched device 0x%x,0x%x unlock_addrs: 0x%.4x 0x%.4x\n",
++ __func__, cfi->mfr, cfi->id,
++ cfi->addr_unlock1, cfi->addr_unlock2 );
+ if (!cfi_jedec_setup(cfi, i))
+ return 0;
+ goto ok_out;
+ }
+ }
+- switch(unlockpass++) {
+- case 0:
+- cfi->addr_unlock1 |= cfi->addr_unlock1 << 4;
+- cfi->addr_unlock2 |= cfi->addr_unlock2 << 4;
+- goto retry;
+- case 1:
+- cfi->addr_unlock1 = cfi->addr_unlock2 = 0;
+ goto retry;
+- }
+- return 0;
+ } else {
+ __u16 mfr;
+ __u16 id;
+@@ -1081,21 +1783,24 @@
+ }
+ }
+
+- /* Check each previous chip to see if it's an alias */
+- for (i=0; i<cfi->numchips; i++) {
+- /* This chip should be in read mode if it's one
+- we've already touched. */
+- if (jedec_read_mfr(map, chips[i].start, cfi) == cfi->mfr &&
+- jedec_read_id(map, chips[i].start, cfi) == cfi->id) {
++ /* Check each previous chip locations to see if it's an alias */
++ for (i=0; i < (base >> cfi->chipshift); i++) {
++ unsigned long start;
++ if(!test_bit(i, chip_map)) {
++ continue; /* Skip location; no valid chip at this address */
++ }
++ start = i << cfi->chipshift;
++ if (jedec_read_mfr(map, start, cfi) == cfi->mfr &&
++ jedec_read_id(map, start, cfi) == cfi->id) {
+ /* Eep. This chip also looks like it's in autoselect mode.
+ Is it an alias for the new one? */
+- jedec_reset(chips[i].start, map, cfi);
++ jedec_reset(start, map, cfi);
+
+ /* If the device IDs go away, it's an alias */
+ if (jedec_read_mfr(map, base, cfi) != cfi->mfr ||
+ jedec_read_id(map, base, cfi) != cfi->id) {
+ printk(KERN_DEBUG "%s: Found an alias at 0x%x for the chip at 0x%lx\n",
+- map->name, base, chips[i].start);
++ map->name, base, start);
+ return 0;
+ }
+
+@@ -1107,7 +1812,7 @@
+ if (jedec_read_mfr(map, base, cfi) == cfi->mfr &&
+ jedec_read_id(map, base, cfi) == cfi->id) {
+ printk(KERN_DEBUG "%s: Found an alias at 0x%x for the chip at 0x%lx\n",
+- map->name, base, chips[i].start);
++ map->name, base, start);
+ return 0;
+ }
+ }
+@@ -1115,13 +1820,7 @@
+
+ /* OK, if we got to here, then none of the previous chips appear to
+ be aliases for the current one. */
+- if (cfi->numchips == MAX_CFI_CHIPS) {
+- printk(KERN_WARNING"%s: Too many flash chips detected. Increase MAX_CFI_CHIPS from %d.\n", map->name, MAX_CFI_CHIPS);
+- /* Doesn't matter about resetting it to Read Mode - we're not going to talk to it anyway */
+- return -1;
+- }
+- chips[cfi->numchips].start = base;
+- chips[cfi->numchips].state = FL_READY;
++ set_bit((base >> cfi->chipshift), chip_map); /* Update chip map */
+ cfi->numchips++;
+
+ ok_out:
+@@ -1136,8 +1835,8 @@
+ }
+
+ static struct chip_probe jedec_chip_probe = {
+- name: "JEDEC",
+- probe_chip: jedec_probe_chip
++ .name = "JEDEC",
++ .probe_chip = jedec_probe_chip
+ };
+
+ struct mtd_info *jedec_probe(struct map_info *map)
+@@ -1150,9 +1849,9 @@
+ }
+
+ static struct mtd_chip_driver jedec_chipdrv = {
+- probe: jedec_probe,
+- name: "jedec_probe",
+- module: THIS_MODULE
++ .probe = jedec_probe,
++ .name = "jedec_probe",
++ .module = THIS_MODULE
+ };
+
+ int __init jedec_probe_init(void)
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/chips/map_absent.c linux/drivers/mtd/chips/map_absent.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/chips/map_absent.c 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/chips/map_absent.c 2004-11-17 18:17:58.963325472 +0100
+@@ -1,7 +1,7 @@
+ /*
+ * Common code to handle absent "placeholder" devices
+ * Copyright 2001 Resilience Corporation <ebrower@resilience.com>
+- * $Id$
++ * $Id$
+ *
+ * This map driver is used to allocate "placeholder" MTD
+ * devices on systems that have socketed/removable media.
+@@ -23,9 +23,10 @@
+ #include <linux/kernel.h>
+ #include <linux/errno.h>
+ #include <linux/slab.h>
+-
++#include <linux/init.h>
++#include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+-
++#include <linux/mtd/compatmac.h>
+
+ static int map_absent_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
+ static int map_absent_write (struct mtd_info *, loff_t, size_t, size_t *, const u_char *);
+@@ -36,10 +37,10 @@
+
+
+ static struct mtd_chip_driver map_absent_chipdrv = {
+- probe: map_absent_probe,
+- destroy: map_absent_destroy,
+- name: "map_absent",
+- module: THIS_MODULE
++ .probe = map_absent_probe,
++ .destroy = map_absent_destroy,
++ .name = "map_absent",
++ .module = THIS_MODULE
+ };
+
+ static struct mtd_info *map_absent_probe(struct map_info *map)
+@@ -65,7 +66,7 @@
+ mtd->flags = 0;
+ mtd->erasesize = PAGE_SIZE;
+
+- MOD_INC_USE_COUNT;
++ __module_get(THIS_MODULE);
+ return mtd;
+ }
+
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/chips/map_ram.c linux/drivers/mtd/chips/map_ram.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/chips/map_ram.c 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/chips/map_ram.c 2004-11-17 18:17:58.965325168 +0100
+@@ -1,7 +1,7 @@
+ /*
+ * Common code to handle map devices which are simple RAM
+ * (C) 2000 Red Hat. GPL'd.
+- * $Id$
++ * $Id$
+ */
+
+ #include <linux/module.h>
+@@ -11,8 +11,10 @@
+ #include <asm/byteorder.h>
+ #include <linux/errno.h>
+ #include <linux/slab.h>
+-
++#include <linux/init.h>
++#include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
++#include <linux/mtd/compatmac.h>
+
+
+ static int mapram_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
+@@ -23,9 +25,9 @@
+
+
+ static struct mtd_chip_driver mapram_chipdrv = {
+- probe: map_ram_probe,
+- name: "map_ram",
+- module: THIS_MODULE
++ .probe = map_ram_probe,
++ .name = "map_ram",
++ .module = THIS_MODULE
+ };
+
+ static struct mtd_info *map_ram_probe(struct map_info *map)
+@@ -34,21 +36,21 @@
+
+ /* Check the first byte is RAM */
+ #if 0
+- map->write8(map, 0x55, 0);
+- if (map->read8(map, 0) != 0x55)
++ map_write8(map, 0x55, 0);
++ if (map_read8(map, 0) != 0x55)
+ return NULL;
+
+- map->write8(map, 0xAA, 0);
+- if (map->read8(map, 0) != 0xAA)
++ map_write8(map, 0xAA, 0);
++ if (map_read8(map, 0) != 0xAA)
+ return NULL;
+
+ /* Check the last byte is RAM */
+- map->write8(map, 0x55, map->size-1);
+- if (map->read8(map, map->size-1) != 0x55)
++ map_write8(map, 0x55, map->size-1);
++ if (map_read8(map, map->size-1) != 0x55)
+ return NULL;
+
+- map->write8(map, 0xAA, map->size-1);
+- if (map->read8(map, map->size-1) != 0xAA)
++ map_write8(map, 0xAA, map->size-1);
++ if (map_read8(map, map->size-1) != 0xAA)
+ return NULL;
+ #endif
+ /* OK. It seems to be RAM. */
+@@ -74,7 +76,7 @@
+ while(mtd->size & (mtd->erasesize - 1))
+ mtd->erasesize >>= 1;
+
+- MOD_INC_USE_COUNT;
++ __module_get(THIS_MODULE);
+ return mtd;
+ }
+
+@@ -83,7 +85,7 @@
+ {
+ struct map_info *map = (struct map_info *)mtd->priv;
+
+- map->copy_from(map, buf, from, len);
++ map_copy_from(map, buf, from, len);
+ *retlen = len;
+ return 0;
+ }
+@@ -92,7 +94,7 @@
+ {
+ struct map_info *map = (struct map_info *)mtd->priv;
+
+- map->copy_to(map, to, buf, len);
++ map_copy_to(map, to, buf, len);
+ *retlen = len;
+ return 0;
+ }
+@@ -105,7 +107,7 @@
+ unsigned long i;
+
+ for (i=0; i<instr->len; i++)
+- map->write8(map, 0xFF, instr->addr + i);
++ map_write8(map, 0xFF, instr->addr + i);
+
+ if (instr->callback)
+ instr->callback(instr);
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/chips/map_rom.c linux/drivers/mtd/chips/map_rom.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/chips/map_rom.c 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/chips/map_rom.c 2004-11-17 18:17:58.966325016 +0100
+@@ -1,7 +1,7 @@
+ /*
+ * Common code to handle map devices which are simple ROM
+ * (C) 2000 Red Hat. GPL'd.
+- * $Id$
++ * $Id$
+ */
+
+ #include <linux/version.h>
+@@ -12,8 +12,10 @@
+ #include <asm/byteorder.h>
+ #include <linux/errno.h>
+ #include <linux/slab.h>
+-
++#include <linux/init.h>
++#include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
++#include <linux/mtd/compatmac.h>
+
+ static int maprom_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
+ static int maprom_write (struct mtd_info *, loff_t, size_t, size_t *, const u_char *);
+@@ -21,9 +23,9 @@
+ struct mtd_info *map_rom_probe(struct map_info *map);
+
+ static struct mtd_chip_driver maprom_chipdrv = {
+- probe: map_rom_probe,
+- name: "map_rom",
+- module: THIS_MODULE
++ .probe = map_rom_probe,
++ .name = "map_rom",
++ .module = THIS_MODULE
+ };
+
+ struct mtd_info *map_rom_probe(struct map_info *map)
+@@ -49,7 +51,7 @@
+ while(mtd->size & (mtd->erasesize - 1))
+ mtd->erasesize >>= 1;
+
+- MOD_INC_USE_COUNT;
++ __module_get(THIS_MODULE);
+ return mtd;
+ }
+
+@@ -58,7 +60,7 @@
+ {
+ struct map_info *map = (struct map_info *)mtd->priv;
+
+- map->copy_from(map, buf, from, len);
++ map_copy_from(map, buf, from, len);
+ *retlen = len;
+ return 0;
+ }
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/chips/sharp.c linux/drivers/mtd/chips/sharp.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/chips/sharp.c 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/chips/sharp.c 2004-11-17 18:17:58.968324712 +0100
+@@ -4,7 +4,7 @@
+ * Copyright 2000,2001 David A. Schleef <ds@schleef.org>
+ * 2000,2001 Lineo, Inc.
+ *
+- * $Id$
++ * $Id$
+ *
+ * Devices supported:
+ * LH28F016SCT Symmetrical block flash memory, 2Mx8
+@@ -28,6 +28,7 @@
+ #include <linux/errno.h>
+ #include <linux/interrupt.h>
+ #include <linux/mtd/map.h>
++#include <linux/mtd/mtd.h>
+ #include <linux/mtd/cfi.h>
+ #include <linux/delay.h>
+
+@@ -98,10 +99,10 @@
+ static void sharp_destroy(struct mtd_info *mtd);
+
+ static struct mtd_chip_driver sharp_chipdrv = {
+- probe: sharp_probe,
+- destroy: sharp_destroy,
+- name: "sharp",
+- module: THIS_MODULE
++ .probe = sharp_probe,
++ .destroy = sharp_destroy,
++ .name = "sharp",
++ .module = THIS_MODULE
+ };
+
+
+@@ -116,8 +117,10 @@
+ return NULL;
+
+ sharp = kmalloc(sizeof(*sharp), GFP_KERNEL);
+- if(!sharp)
++ if(!sharp) {
++ kfree(mtd);
+ return NULL;
++ }
+
+ memset(mtd, 0, sizeof(*mtd));
+
+@@ -163,12 +166,12 @@
+ u32 read0, read4;
+ int width = 4;
+
+- tmp = map->read32(map, base+0);
++ tmp = map_read32(map, base+0);
+
+- map->write32(map, CMD_READ_ID, base+0);
++ map_write32(map, CMD_READ_ID, base+0);
+
+- read0=map->read32(map, base+0);
+- read4=map->read32(map, base+4);
++ read0=map_read32(map, base+0);
++ read4=map_read32(map, base+4);
+ if(read0 == 0x89898989){
+ printk("Looks like sharp flash\n");
+ switch(read4){
+@@ -196,10 +199,10 @@
+ printk("Sort-of looks like sharp flash, 0x%08x 0x%08x\n",
+ read0,read4);
+ }
+- }else if((map->read32(map, base+0) == CMD_READ_ID)){
++ }else if((map_read32(map, base+0) == CMD_READ_ID)){
+ /* RAM, probably */
+ printk("Looks like RAM\n");
+- map->write32(map, tmp, base+0);
++ map_write32(map, tmp, base+0);
+ }else{
+ printk("Doesn't look like sharp flash, 0x%08x 0x%08x\n",
+ read0,read4);
+@@ -221,10 +224,10 @@
+
+ switch(chip->state){
+ case FL_READY:
+- map->write32(map,CMD_READ_STATUS,adr);
++ map_write32(map,CMD_READ_STATUS,adr);
+ chip->state = FL_STATUS;
+ case FL_STATUS:
+- status = map->read32(map,adr);
++ status = map_read32(map,adr);
+ //printk("status=%08x\n",status);
+
+ udelay(100);
+@@ -252,7 +255,7 @@
+ goto retry;
+ }
+
+- map->write32(map,CMD_RESET, adr);
++ map_write32(map,CMD_RESET, adr);
+
+ chip->state = FL_READY;
+
+@@ -293,7 +296,7 @@
+ if(ret<0)
+ break;
+
+- map->copy_from(map,buf,ofs,thislen);
++ map_copy_from(map,buf,ofs,thislen);
+
+ sharp_release(&sharp->chips[chipnum]);
+
+@@ -354,17 +357,17 @@
+ ret = sharp_wait(map,chip);
+
+ for(try=0;try<10;try++){
+- map->write32(map,CMD_BYTE_WRITE,adr);
++ map_write32(map,CMD_BYTE_WRITE,adr);
+ /* cpu_to_le32 -> hack to fix the writel be->le conversion */
+- map->write32(map,cpu_to_le32(datum),adr);
++ map_write32(map,cpu_to_le32(datum),adr);
+
+ chip->state = FL_WRITING;
+
+ timeo = jiffies + (HZ/2);
+
+- map->write32(map,CMD_READ_STATUS,adr);
++ map_write32(map,CMD_READ_STATUS,adr);
+ for(i=0;i<100;i++){
+- status = map->read32(map,adr);
++ status = map_read32(map,adr);
+ if((status & SR_READY)==SR_READY)
+ break;
+ }
+@@ -377,9 +380,9 @@
+
+ printk("sharp: error writing byte at addr=%08lx status=%08x\n",adr,status);
+
+- map->write32(map,CMD_CLEAR_STATUS,adr);
++ map_write32(map,CMD_CLEAR_STATUS,adr);
+ }
+- map->write32(map,CMD_RESET,adr);
++ map_write32(map,CMD_RESET,adr);
+ chip->state = FL_READY;
+
+ wake_up(&chip->wq);
+@@ -436,14 +439,14 @@
+ int status;
+ DECLARE_WAITQUEUE(wait, current);
+
+- map->write32(map,CMD_READ_STATUS,adr);
+- status = map->read32(map,adr);
++ map_write32(map,CMD_READ_STATUS,adr);
++ status = map_read32(map,adr);
+
+ timeo = jiffies + HZ;
+
+ while(time_before(jiffies, timeo)){
+- map->write32(map,CMD_READ_STATUS,adr);
+- status = map->read32(map,adr);
++ map_write32(map,CMD_READ_STATUS,adr);
++ status = map_read32(map,adr);
+ if((status & SR_READY)==SR_READY){
+ ret = 0;
+ goto out;
+@@ -485,26 +488,26 @@
+ sharp_unlock_oneblock(map,chip,adr);
+ #endif
+
+- map->write32(map,CMD_BLOCK_ERASE_1,adr);
+- map->write32(map,CMD_BLOCK_ERASE_2,adr);
++ map_write32(map,CMD_BLOCK_ERASE_1,adr);
++ map_write32(map,CMD_BLOCK_ERASE_2,adr);
+
+ chip->state = FL_ERASING;
+
+ ret = sharp_do_wait_for_ready(map,chip,adr);
+ if(ret<0)return ret;
+
+- map->write32(map,CMD_READ_STATUS,adr);
+- status = map->read32(map,adr);
++ map_write32(map,CMD_READ_STATUS,adr);
++ status = map_read32(map,adr);
+
+ if(!(status&SR_ERRORS)){
+- map->write32(map,CMD_RESET,adr);
++ map_write32(map,CMD_RESET,adr);
+ chip->state = FL_READY;
+ //spin_unlock_bh(chip->mutex);
+ return 0;
+ }
+
+ printk("sharp: error erasing block at addr=%08lx status=%08x\n",adr,status);
+- map->write32(map,CMD_CLEAR_STATUS,adr);
++ map_write32(map,CMD_CLEAR_STATUS,adr);
+
+ //spin_unlock_bh(chip->mutex);
+
+@@ -518,17 +521,17 @@
+ int i;
+ int status;
+
+- map->write32(map,CMD_CLEAR_BLOCK_LOCKS_1,adr);
+- map->write32(map,CMD_CLEAR_BLOCK_LOCKS_2,adr);
++ map_write32(map,CMD_CLEAR_BLOCK_LOCKS_1,adr);
++ map_write32(map,CMD_CLEAR_BLOCK_LOCKS_2,adr);
+
+ udelay(100);
+
+- status = map->read32(map,adr);
++ status = map_read32(map,adr);
+ printk("status=%08x\n",status);
+
+ for(i=0;i<1000;i++){
+- //map->write32(map,CMD_READ_STATUS,adr);
+- status = map->read32(map,adr);
++ //map_write32(map,CMD_READ_STATUS,adr);
++ status = map_read32(map,adr);
+ if((status & SR_READY)==SR_READY)
+ break;
+ udelay(100);
+@@ -538,13 +541,13 @@
+ }
+
+ if(!(status&SR_ERRORS)){
+- map->write32(map,CMD_RESET,adr);
++ map_write32(map,CMD_RESET,adr);
+ chip->state = FL_READY;
+ return;
+ }
+
+ printk("sharp: error unlocking block at addr=%08lx status=%08x\n",adr,status);
+- map->write32(map,CMD_CLEAR_STATUS,adr);
++ map_write32(map,CMD_CLEAR_STATUS,adr);
+ }
+ #endif
+
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/cmdlinepart.c linux/drivers/mtd/cmdlinepart.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/cmdlinepart.c 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/cmdlinepart.c 2004-11-17 18:17:58.829345840 +0100
+@@ -1,5 +1,5 @@
+ /*
+- * $Id$
++ * $Id$
+ *
+ * Read flash partition table from command line
+ *
+@@ -28,7 +28,7 @@
+
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/partitions.h>
+-#include <asm/setup.h>
++#include <linux/mtd/compatmac.h>
+ #include <linux/bootmem.h>
+
+ /* error message prefix */
+@@ -178,8 +178,7 @@
+ parts[this_part].mask_flags = mask_flags;
+ if (name)
+ {
+- strncpy(extra_mem, name, name_len);
+- extra_mem[name_len] = 0;
++ strlcpy(extra_mem, name, name_len + 1);
+ }
+ else
+ {
+@@ -258,8 +257,7 @@
+ this_mtd->parts = parts;
+ this_mtd->num_parts = num_parts;
+ this_mtd->mtd_id = (char*)(this_mtd + 1);
+- strncpy(this_mtd->mtd_id, mtd_id, mtd_id_len);
+- this_mtd->mtd_id[mtd_id_len] = 0;
++ strlcpy(this_mtd->mtd_id, mtd_id, mtd_id_len + 1);
+
+ /* link into chain */
+ this_mtd->next = partitions;
+@@ -291,13 +289,14 @@
+ * information. It returns partitions for the requested mtd device, or
+ * the first one in the chain if a NULL mtd_id is passed in.
+ */
+-int parse_cmdline_partitions(struct mtd_info *master,
++static int parse_cmdline_partitions(struct mtd_info *master,
+ struct mtd_partition **pparts,
+- const char *mtd_id)
++ unsigned long origin)
+ {
+ unsigned long offset;
+ int i;
+ struct cmdline_mtd_partition *part;
++ char *mtd_id = master->name;
+
+ if(!cmdline)
+ return -EINVAL;
+@@ -349,7 +348,25 @@
+
+ __setup("mtdparts=", mtdpart_setup);
+
+-EXPORT_SYMBOL(parse_cmdline_partitions);
++static struct mtd_part_parser cmdline_parser = {
++ .owner = THIS_MODULE,
++ .parse_fn = parse_cmdline_partitions,
++ .name = "cmdlinepart",
++};
++
++static int __init cmdline_parser_init(void)
++{
++ return register_mtd_parser(&cmdline_parser);
++}
++
++static void __exit cmdline_parser_exit(void)
++{
++ deregister_mtd_parser(&cmdline_parser);
++}
++
++module_init(cmdline_parser_init);
++module_exit(cmdline_parser_exit);
++
+
+ MODULE_LICENSE("GPL");
+ MODULE_AUTHOR("Marius Groeger <mag@sysgo.de>");
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/devices/Config.in linux/drivers/mtd/devices/Config.in
+--- linux-mips-2.4.24-pre2/drivers/mtd/devices/Config.in 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/devices/Config.in 2004-11-17 18:17:58.972324104 +0100
+@@ -1,6 +1,6 @@
+-# drivers/mtd/maps/Config.in
++# drivers/mtd/devices/Config.in
+
+-# $Id$
++# $Id$
+
+ mainmenu_option next_comment
+
+@@ -28,13 +28,13 @@
+ dep_tristate ' MTD emulation using block device' CONFIG_MTD_BLKMTD $CONFIG_MTD
+
+ comment 'Disk-On-Chip Device Drivers'
+- dep_tristate ' M-Systems Disk-On-Chip 1000' CONFIG_MTD_DOC1000 $CONFIG_MTD
+ dep_tristate ' M-Systems Disk-On-Chip 2000 and Millennium' CONFIG_MTD_DOC2000 $CONFIG_MTD
+ dep_tristate ' M-Systems Disk-On-Chip Millennium-only alternative driver (see help)' CONFIG_MTD_DOC2001 $CONFIG_MTD
+- if [ "$CONFIG_MTD_DOC2001" = "y" -o "$CONFIG_MTD_DOC2000" = "y" ]; then
++ dep_tristate ' M-Systems Disk-On-Chip Millennium Plus driver (see help)' CONFIG_MTD_DOC2001PLUS $CONFIG_MTD
++ if [ "$CONFIG_MTD_DOC2001PLUS" = "y" -o "$CONFIG_MTD_DOC2001" = "y" -o "$CONFIG_MTD_DOC2000" = "y" ]; then
+ define_bool CONFIG_MTD_DOCPROBE y
+ else
+- if [ "$CONFIG_MTD_DOC2001" = "m" -o "$CONFIG_MTD_DOC2000" = "m" ]; then
++ if [ "$CONFIG_MTD_DOC2001PLUS" = "m" -o "$CONFIG_MTD_DOC2001" = "m" -o "$CONFIG_MTD_DOC2000" = "m" ]; then
+ define_bool CONFIG_MTD_DOCPROBE m
+ else
+ define_bool CONFIG_MTD_DOCPROBE n
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/devices/Makefile linux/drivers/mtd/devices/Makefile
+--- linux-mips-2.4.24-pre2/drivers/mtd/devices/Makefile 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/devices/Makefile 2004-11-17 18:17:58.974323800 +0100
+@@ -1,9 +1,12 @@
+ #
+ # linux/drivers/devices/Makefile
+ #
+-# $Id$
++# $Id$
+
++ifeq ($(PATCHLEVEL),4)
+ O_TARGET := devlink.o
++export-objs := docecc.o
++endif
+
+ # *** BIG UGLY NOTE ***
+ #
+@@ -12,15 +15,16 @@
+ # here where previously there was none. We now have to ensure that
+ # doc200[01].o are linked before docprobe.o
+
+-obj-$(CONFIG_MTD_DOC1000) += doc1000.o
+ obj-$(CONFIG_MTD_DOC2000) += doc2000.o
+ obj-$(CONFIG_MTD_DOC2001) += doc2001.o
++obj-$(CONFIG_MTD_DOC2001PLUS) += doc2001plus.o
+ obj-$(CONFIG_MTD_DOCPROBE) += docprobe.o docecc.o
+ obj-$(CONFIG_MTD_SLRAM) += slram.o
++obj-$(CONFIG_MTD_PHRAM) += phram.o
+ obj-$(CONFIG_MTD_PMC551) += pmc551.o
+ obj-$(CONFIG_MTD_MS02NV) += ms02-nv.o
+ obj-$(CONFIG_MTD_MTDRAM) += mtdram.o
+ obj-$(CONFIG_MTD_LART) += lart.o
+ obj-$(CONFIG_MTD_BLKMTD) += blkmtd.o
+
+-include $(TOPDIR)/Rules.make
++-include $(TOPDIR)/Rules.make
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/devices/blkmtd-25.c linux/drivers/mtd/devices/blkmtd-25.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/devices/blkmtd-25.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/mtd/devices/blkmtd-25.c 2004-11-17 18:17:58.000000000 +0100
+@@ -0,0 +1,827 @@
++/*
++ * $Id$
++ *
++ * blkmtd.c - use a block device as a fake MTD
++ *
++ * Author: Simon Evans <spse@secret.org.uk>
++ *
++ * Copyright (C) 2001,2002 Simon Evans
++ *
++ * Licence: GPL
++ *
++ * How it works:
++ * The driver uses raw/io to read/write the device and the page
++ * cache to cache access. Writes update the page cache with the
++ * new data and mark it dirty and add the page into a BIO which
++ * is then written out.
++ *
++ * It can be loaded Read-Only to prevent erases and writes to the
++ * medium.
++ *
++ */
++
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/fs.h>
++#include <linux/blkdev.h>
++#include <linux/bio.h>
++#include <linux/pagemap.h>
++#include <linux/list.h>
++#include <linux/init.h>
++#include <linux/mtd/mtd.h>
++
++
++#define err(format, arg...) printk(KERN_ERR "blkmtd: " format "\n" , ## arg)
++#define info(format, arg...) printk(KERN_INFO "blkmtd: " format "\n" , ## arg)
++#define warn(format, arg...) printk(KERN_WARNING "blkmtd: " format "\n" , ## arg)
++#define crit(format, arg...) printk(KERN_CRIT "blkmtd: " format "\n" , ## arg)
++
++
++/* Default erase size in K, always make it a multiple of PAGE_SIZE */
++#define CONFIG_MTD_BLKDEV_ERASESIZE (128 << 10) /* 128KiB */
++#define VERSION "$Revision$"
++
++/* Info for the block device */
++struct blkmtd_dev {
++ struct list_head list;
++ struct block_device *blkdev;
++ struct mtd_info mtd_info;
++ struct semaphore wrbuf_mutex;
++};
++
++
++/* Static info about the MTD, used in cleanup_module */
++static LIST_HEAD(blkmtd_device_list);
++
++
++static void blkmtd_sync(struct mtd_info *mtd);
++
++#define MAX_DEVICES 4
++
++/* Module parameters passed by insmod/modprobe */
++char *device[MAX_DEVICES]; /* the block device to use */
++int erasesz[MAX_DEVICES]; /* optional default erase size */
++int ro[MAX_DEVICES]; /* optional read only flag */
++int sync;
++
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Simon Evans <spse@secret.org.uk>");
++MODULE_DESCRIPTION("Emulate an MTD using a block device");
++MODULE_PARM(device, "1-4s");
++MODULE_PARM_DESC(device, "block device to use");
++MODULE_PARM(erasesz, "1-4i");
++MODULE_PARM_DESC(erasesz, "optional erase size to use in KiB. eg 4=4KiB.");
++MODULE_PARM(ro, "1-4i");
++MODULE_PARM_DESC(ro, "1=Read only, writes and erases cause errors");
++MODULE_PARM(sync, "i");
++MODULE_PARM_DESC(sync, "1=Synchronous writes");
++
++
++/* completion handler for BIO reads */
++static int bi_read_complete(struct bio *bio, unsigned int bytes_done, int error)
++{
++ if (bio->bi_size)
++ return 1;
++
++ complete((struct completion*)bio->bi_private);
++ return 0;
++}
++
++
++/* completion handler for BIO writes */
++static int bi_write_complete(struct bio *bio, unsigned int bytes_done, int error)
++{
++ const int uptodate = test_bit(BIO_UPTODATE, &bio->bi_flags);
++ struct bio_vec *bvec = bio->bi_io_vec + bio->bi_vcnt - 1;
++
++ if (bio->bi_size)
++ return 1;
++
++ if(!uptodate)
++ err("bi_write_complete: not uptodate\n");
++
++ do {
++ struct page *page = bvec->bv_page;
++ DEBUG(3, "Cleaning up page %ld\n", page->index);
++ if (--bvec >= bio->bi_io_vec)
++ prefetchw(&bvec->bv_page->flags);
++
++ if (uptodate) {
++ SetPageUptodate(page);
++ } else {
++ ClearPageUptodate(page);
++ SetPageError(page);
++ }
++ ClearPageDirty(page);
++ unlock_page(page);
++ page_cache_release(page);
++ } while (bvec >= bio->bi_io_vec);
++
++ complete((struct completion*)bio->bi_private);
++ return 0;
++}
++
++
++/* read one page from the block device */
++static int blkmtd_readpage(struct blkmtd_dev *dev, struct page *page)
++{
++ struct bio *bio;
++ struct completion event;
++ int err = -ENOMEM;
++
++ if(PageUptodate(page)) {
++ DEBUG(2, "blkmtd: readpage page %ld is already upto date\n", page->index);
++ unlock_page(page);
++ return 0;
++ }
++
++ ClearPageUptodate(page);
++ ClearPageError(page);
++
++ bio = bio_alloc(GFP_KERNEL, 1);
++ if(bio) {
++ init_completion(&event);
++ bio->bi_bdev = dev->blkdev;
++ bio->bi_sector = page->index << (PAGE_SHIFT-9);
++ bio->bi_private = &event;
++ bio->bi_end_io = bi_read_complete;
++ if(bio_add_page(bio, page, PAGE_SIZE, 0) == PAGE_SIZE) {
++ submit_bio(READ, bio);
++ blk_run_queues();
++ wait_for_completion(&event);
++ err = test_bit(BIO_UPTODATE, &bio->bi_flags) ? 0 : -EIO;
++ bio_put(bio);
++ }
++ }
++
++ if(err)
++ SetPageError(page);
++ else
++ SetPageUptodate(page);
++ flush_dcache_page(page);
++ unlock_page(page);
++ return err;
++}
++
++
++/* write out the current BIO and wait for it to finish */
++static int blkmtd_write_out(struct bio *bio)
++{
++ struct completion event;
++ int err;
++
++ if(!bio->bi_vcnt) {
++ bio_put(bio);
++ return 0;
++ }
++
++ init_completion(&event);
++ bio->bi_private = &event;
++ bio->bi_end_io = bi_write_complete;
++ submit_bio(WRITE, bio);
++ blk_run_queues();
++ wait_for_completion(&event);
++ DEBUG(3, "submit_bio completed, bi_vcnt = %d\n", bio->bi_vcnt);
++ err = test_bit(BIO_UPTODATE, &bio->bi_flags) ? 0 : -EIO;
++ bio_put(bio);
++ return err;
++}
++
++
++/**
++ * blkmtd_add_page - add a page to the current BIO
++ * @bio: bio to add to (NULL to alloc initial bio)
++ * @blkdev: block device
++ * @page: page to add
++ * @pagecnt: pages left to add
++ *
++ * Adds a page to the current bio, allocating it if necessary. If it cannot be
++ * added, the current bio is written out and a new one is allocated. Returns
++ * the new bio to add or NULL on error
++ */
++static struct bio *blkmtd_add_page(struct bio *bio, struct block_device *blkdev,
++ struct page *page, int pagecnt)
++{
++
++ retry:
++ if(!bio) {
++ bio = bio_alloc(GFP_KERNEL, pagecnt);
++ if(!bio)
++ return NULL;
++ bio->bi_sector = page->index << (PAGE_SHIFT-9);
++ bio->bi_bdev = blkdev;
++ }
++
++ if(bio_add_page(bio, page, PAGE_SIZE, 0) != PAGE_SIZE) {
++ blkmtd_write_out(bio);
++ bio = NULL;
++ goto retry;
++ }
++ return bio;
++}
++
++
++/**
++ * write_pages - write block of data to device via the page cache
++ * @dev: device to write to
++ * @buf: data source or NULL if erase (output is set to 0xff)
++ * @to: offset into output device
++ * @len: amount to data to write
++ * @retlen: amount of data written
++ *
++ * Grab pages from the page cache and fill them with the source data.
++ * Non page aligned start and end result in a readin of the page and
++ * part of the page being modified. Pages are added to the bio and then written
++ * out.
++ */
++static int write_pages(struct blkmtd_dev *dev, const u_char *buf, loff_t to,
++ size_t len, size_t *retlen)
++{
++ int pagenr, offset;
++ size_t start_len = 0, end_len;
++ int pagecnt = 0;
++ int err = 0;
++ struct bio *bio = NULL;
++ size_t thislen = 0;
++
++ pagenr = to >> PAGE_SHIFT;
++ offset = to & ~PAGE_MASK;
++
++ DEBUG(2, "blkmtd: write_pages: buf = %p to = %ld len = %d pagenr = %d offset = %d\n",
++ buf, (long)to, len, pagenr, offset);
++
++ /* see if we have to do a partial write at the start */
++ if(offset) {
++ start_len = ((offset + len) > PAGE_SIZE) ? PAGE_SIZE - offset : len;
++ len -= start_len;
++ }
++
++ /* calculate the length of the other two regions */
++ end_len = len & ~PAGE_MASK;
++ len -= end_len;
++
++ if(start_len)
++ pagecnt++;
++
++ if(len)
++ pagecnt += len >> PAGE_SHIFT;
++
++ if(end_len)
++ pagecnt++;
++
++ down(&dev->wrbuf_mutex);
++
++ DEBUG(3, "blkmtd: write: start_len = %d len = %d end_len = %d pagecnt = %d\n",
++ start_len, len, end_len, pagecnt);
++
++ if(start_len) {
++ /* do partial start region */
++ struct page *page;
++
++ DEBUG(3, "blkmtd: write: doing partial start, page = %d len = %d offset = %d\n",
++ pagenr, start_len, offset);
++
++ BUG_ON(!buf);
++ page = read_cache_page(dev->blkdev->bd_inode->i_mapping, pagenr, (filler_t *)blkmtd_readpage, dev);
++ lock_page(page);
++ if(PageDirty(page)) {
++ err("to = %lld start_len = %d len = %d end_len = %d pagenr = %d\n",
++ to, start_len, len, end_len, pagenr);
++ BUG();
++ }
++ memcpy(page_address(page)+offset, buf, start_len);
++ SetPageDirty(page);
++ SetPageUptodate(page);
++ buf += start_len;
++ thislen = start_len;
++ bio = blkmtd_add_page(bio, dev->blkdev, page, pagecnt);
++ if(!bio) {
++ err = -ENOMEM;
++ err("bio_add_page failed\n");
++ goto write_err;
++ }
++ pagecnt--;
++ pagenr++;
++ }
++
++ /* Now do the main loop to a page aligned, n page sized output */
++ if(len) {
++ int pagesc = len >> PAGE_SHIFT;
++ DEBUG(3, "blkmtd: write: whole pages start = %d, count = %d\n",
++ pagenr, pagesc);
++ while(pagesc) {
++ struct page *page;
++
++ /* see if page is in the page cache */
++ DEBUG(3, "blkmtd: write: grabbing page %d from page cache\n", pagenr);
++ page = grab_cache_page(dev->blkdev->bd_inode->i_mapping, pagenr);
++ if(PageDirty(page)) {
++ BUG();
++ }
++ if(!page) {
++ warn("write: cannot grab cache page %d", pagenr);
++ err = -ENOMEM;
++ goto write_err;
++ }
++ if(!buf) {
++ memset(page_address(page), 0xff, PAGE_SIZE);
++ } else {
++ memcpy(page_address(page), buf, PAGE_SIZE);
++ buf += PAGE_SIZE;
++ }
++ bio = blkmtd_add_page(bio, dev->blkdev, page, pagecnt);
++ if(!bio) {
++ err = -ENOMEM;
++ err("bio_add_page failed\n");
++ goto write_err;
++ }
++ pagenr++;
++ pagecnt--;
++ SetPageDirty(page);
++ SetPageUptodate(page);
++ pagesc--;
++ thislen += PAGE_SIZE;
++ }
++ }
++
++ if(end_len) {
++ /* do the third region */
++ struct page *page;
++ DEBUG(3, "blkmtd: write: doing partial end, page = %d len = %d\n",
++ pagenr, end_len);
++ BUG_ON(!buf);
++ page = read_cache_page(dev->blkdev->bd_inode->i_mapping, pagenr, (filler_t *)blkmtd_readpage, dev);
++ lock_page(page);
++ if(PageDirty(page)) {
++ err("to = %lld start_len = %d len = %d end_len = %d pagenr = %d\n",
++ to, start_len, len, end_len, pagenr);
++ BUG();
++ }
++ memcpy(page_address(page), buf, end_len);
++ SetPageDirty(page);
++ SetPageUptodate(page);
++ DEBUG(3, "blkmtd: write: writing out partial end\n");
++ thislen += end_len;
++ bio = blkmtd_add_page(bio, dev->blkdev, page, pagecnt);
++ if(!bio) {
++ err = -ENOMEM;
++ err("bio_add_page failed\n");
++ goto write_err;
++ }
++ pagenr++;
++ }
++
++ DEBUG(3, "blkmtd: write: got %d vectors to write\n", bio->bi_vcnt);
++ write_err:
++ if(bio)
++ blkmtd_write_out(bio);
++
++ DEBUG(2, "blkmtd: write: end, retlen = %d, err = %d\n", *retlen, err);
++ up(&dev->wrbuf_mutex);
++
++ if(retlen)
++ *retlen = thislen;
++ return err;
++}
++
++
++/* erase a specified part of the device */
++static int blkmtd_erase(struct mtd_info *mtd, struct erase_info *instr)
++{
++ struct blkmtd_dev *dev = mtd->priv;
++ struct mtd_erase_region_info *einfo = mtd->eraseregions;
++ int numregions = mtd->numeraseregions;
++ size_t from;
++ u_long len;
++ int err = -EIO;
++ int retlen;
++
++ instr->state = MTD_ERASING;
++ from = instr->addr;
++ len = instr->len;
++
++ /* check erase region has valid start and length */
++ DEBUG(2, "blkmtd: erase: dev = `%s' from = 0x%x len = 0x%lx\n",
++ mtd->name+9, from, len);
++ while(numregions) {
++ DEBUG(3, "blkmtd: checking erase region = 0x%08X size = 0x%X num = 0x%x\n",
++ einfo->offset, einfo->erasesize, einfo->numblocks);
++ if(from >= einfo->offset
++ && from < einfo->offset + (einfo->erasesize * einfo->numblocks)) {
++ if(len == einfo->erasesize
++ && ( (from - einfo->offset) % einfo->erasesize == 0))
++ break;
++ }
++ numregions--;
++ einfo++;
++ }
++
++ if(!numregions) {
++ /* Not a valid erase block */
++ err("erase: invalid erase request 0x%lX @ 0x%08X", len, from);
++ instr->state = MTD_ERASE_FAILED;
++ err = -EIO;
++ }
++
++ if(instr->state != MTD_ERASE_FAILED) {
++ /* do the erase */
++ DEBUG(3, "Doing erase from = %d len = %ld\n", from, len);
++ err = write_pages(dev, NULL, from, len, &retlen);
++ if(err || retlen != len) {
++ err("erase failed err = %d", err);
++ instr->state = MTD_ERASE_FAILED;
++ } else {
++ instr->state = MTD_ERASE_DONE;
++ }
++ }
++
++ DEBUG(3, "blkmtd: erase: checking callback\n");
++ if (instr->callback) {
++ (*(instr->callback))(instr);
++ }
++ DEBUG(2, "blkmtd: erase: finished (err = %d)\n", err);
++ return err;
++}
++
++
++/* read a range of the data via the page cache */
++static int blkmtd_read(struct mtd_info *mtd, loff_t from, size_t len,
++ size_t *retlen, u_char *buf)
++{
++ struct blkmtd_dev *dev = mtd->priv;
++ int err = 0;
++ int offset;
++ int pagenr, pages;
++ size_t thislen = 0;
++
++ DEBUG(2, "blkmtd: read: dev = `%s' from = %ld len = %d buf = %p\n",
++ mtd->name+9, (long int)from, len, buf);
++
++ if(from > mtd->size)
++ return -EINVAL;
++ if(from + len > mtd->size)
++ len = mtd->size - from;
++
++ pagenr = from >> PAGE_SHIFT;
++ offset = from - (pagenr << PAGE_SHIFT);
++
++ pages = (offset+len+PAGE_SIZE-1) >> PAGE_SHIFT;
++ DEBUG(3, "blkmtd: read: pagenr = %d offset = %d, pages = %d\n",
++ pagenr, offset, pages);
++
++ while(pages) {
++ struct page *page;
++ int cpylen;
++
++ DEBUG(3, "blkmtd: read: looking for page: %d\n", pagenr);
++ page = read_cache_page(dev->blkdev->bd_inode->i_mapping, pagenr, (filler_t *)blkmtd_readpage, dev);
++ if(IS_ERR(page)) {
++ err = -EIO;
++ goto readerr;
++ }
++
++ cpylen = (PAGE_SIZE > len) ? len : PAGE_SIZE;
++ if(offset+cpylen > PAGE_SIZE)
++ cpylen = PAGE_SIZE-offset;
++
++ memcpy(buf + thislen, page_address(page) + offset, cpylen);
++ offset = 0;
++ len -= cpylen;
++ thislen += cpylen;
++ pagenr++;
++ pages--;
++ if(!PageDirty(page))
++ page_cache_release(page);
++ }
++
++ readerr:
++ if(retlen)
++ *retlen = thislen;
++ DEBUG(2, "blkmtd: end read: retlen = %d, err = %d\n", thislen, err);
++ return err;
++}
++
++
++/* write data to the underlying device */
++static int blkmtd_write(struct mtd_info *mtd, loff_t to, size_t len,
++ size_t *retlen, const u_char *buf)
++{
++ struct blkmtd_dev *dev = mtd->priv;
++ int err;
++
++ if(!len)
++ return 0;
++
++ DEBUG(2, "blkmtd: write: dev = `%s' to = %ld len = %d buf = %p\n",
++ mtd->name+9, (long int)to, len, buf);
++
++ if(to >= mtd->size) {
++ return -ENOSPC;
++ }
++
++ if(to + len > mtd->size) {
++ len = mtd->size - to;
++ }
++
++ err = write_pages(dev, buf, to, len, retlen);
++ if(err > 0)
++ err = 0;
++ DEBUG(2, "blkmtd: write: end, err = %d\n", err);
++ return err;
++}
++
++
++/* sync the device - wait until the write queue is empty */
++static void blkmtd_sync(struct mtd_info *mtd)
++{
++ /* Currently all writes are synchronous */
++}
++
++
++static void free_device(struct blkmtd_dev *dev)
++{
++ DEBUG(2, "blkmtd: free_device() dev = %p\n", dev);
++ if(dev) {
++ if(dev->mtd_info.eraseregions)
++ kfree(dev->mtd_info.eraseregions);
++ if(dev->mtd_info.name)
++ kfree(dev->mtd_info.name);
++
++ if(dev->blkdev) {
++ invalidate_inode_pages(dev->blkdev->bd_inode->i_mapping);
++ close_bdev_excl(dev->blkdev, BDEV_RAW);
++ }
++ kfree(dev);
++ }
++}
++
++
++/* For a given size and initial erase size, calculate the number
++ * and size of each erase region. Goes round the loop twice,
++ * once to find out how many regions, then allocates space,
++ * then round the loop again to fill it in.
++ */
++static struct mtd_erase_region_info *calc_erase_regions(
++ size_t erase_size, size_t total_size, int *regions)
++{
++ struct mtd_erase_region_info *info = NULL;
++
++ DEBUG(2, "calc_erase_regions, es = %d size = %d regions = %d\n",
++ erase_size, total_size, *regions);
++ /* Make any user specified erasesize be a power of 2
++ and at least PAGE_SIZE */
++ if(erase_size) {
++ int es = erase_size;
++ erase_size = 1;
++ while(es != 1) {
++ es >>= 1;
++ erase_size <<= 1;
++ }
++ if(erase_size < PAGE_SIZE)
++ erase_size = PAGE_SIZE;
++ } else {
++ erase_size = CONFIG_MTD_BLKDEV_ERASESIZE;
++ }
++
++ *regions = 0;
++
++ do {
++ int tot_size = total_size;
++ int er_size = erase_size;
++ int count = 0, offset = 0, regcnt = 0;
++
++ while(tot_size) {
++ count = tot_size / er_size;
++ if(count) {
++ tot_size = tot_size % er_size;
++ if(info) {
++ DEBUG(2, "adding to erase info off=%d er=%d cnt=%d\n",
++ offset, er_size, count);
++ (info+regcnt)->offset = offset;
++ (info+regcnt)->erasesize = er_size;
++ (info+regcnt)->numblocks = count;
++ (*regions)++;
++ }
++ regcnt++;
++ offset += (count * er_size);
++ }
++ while(er_size > tot_size)
++ er_size >>= 1;
++ }
++ if(info == NULL) {
++ info = kmalloc(regcnt * sizeof(struct mtd_erase_region_info), GFP_KERNEL);
++ if(!info)
++ break;
++ }
++ } while(!(*regions));
++ DEBUG(2, "calc_erase_regions done, es = %d size = %d regions = %d\n",
++ erase_size, total_size, *regions);
++ return info;
++}
++
++
++extern dev_t __init name_to_dev_t(const char *line);
++
++static struct blkmtd_dev *add_device(char *devname, int readonly, int erase_size)
++{
++ struct block_device *bdev;
++ int mode;
++ struct blkmtd_dev *dev;
++
++ if(!devname)
++ return NULL;
++
++ /* Get a handle on the device */
++
++
++#ifdef MODULE
++ mode = (readonly) ? O_RDONLY : O_RDWR;
++ bdev = open_bdev_excl(devname, mode, BDEV_RAW, NULL);
++#else
++ mode = (readonly) ? FMODE_READ : FMODE_WRITE;
++ bdev = open_by_devnum(name_to_dev_t(devname), mode, BDEV_RAW);
++#endif
++ if(IS_ERR(bdev)) {
++ err("error: cannot open device %s", devname);
++ DEBUG(2, "blkmtd: opening bdev returned %ld\n", PTR_ERR(bdev));
++ return NULL;
++ }
++
++ DEBUG(1, "blkmtd: found a block device major = %d, minor = %d\n",
++ MAJOR(bdev->bd_dev), MINOR(bdev->bd_dev));
++
++ if(MAJOR(bdev->bd_dev) == MTD_BLOCK_MAJOR) {
++ err("attempting to use an MTD device as a block device");
++ blkdev_put(bdev, BDEV_RAW);
++ return NULL;
++ }
++
++ dev = kmalloc(sizeof(struct blkmtd_dev), GFP_KERNEL);
++ if(dev == NULL) {
++ blkdev_put(bdev, BDEV_RAW);
++ return NULL;
++ }
++
++ memset(dev, 0, sizeof(struct blkmtd_dev));
++ if(!readonly) {
++ init_MUTEX(&dev->wrbuf_mutex);
++ }
++
++ dev->blkdev = bdev;
++ dev->mtd_info.size = dev->blkdev->bd_inode->i_size & PAGE_MASK;
++
++ /* Setup the MTD structure */
++ /* make the name contain the block device in */
++ dev->mtd_info.name = kmalloc(sizeof("blkmtd: ") + strlen(devname), GFP_KERNEL);
++ if(dev->mtd_info.name == NULL)
++ goto devinit_err;
++
++ sprintf(dev->mtd_info.name, "blkmtd: %s", devname);
++ dev->mtd_info.eraseregions = calc_erase_regions(erase_size, dev->mtd_info.size,
++ &dev->mtd_info.numeraseregions);
++ if(dev->mtd_info.eraseregions == NULL)
++ goto devinit_err;
++
++ dev->mtd_info.erasesize = dev->mtd_info.eraseregions->erasesize;
++ DEBUG(1, "blkmtd: init: found %d erase regions\n",
++ dev->mtd_info.numeraseregions);
++
++ if(readonly) {
++ dev->mtd_info.type = MTD_ROM;
++ dev->mtd_info.flags = MTD_CAP_ROM;
++ } else {
++ dev->mtd_info.type = MTD_RAM;
++ dev->mtd_info.flags = MTD_CAP_RAM;
++ dev->mtd_info.erase = blkmtd_erase;
++ dev->mtd_info.write = blkmtd_write;
++ dev->mtd_info.writev = default_mtd_writev;
++ dev->mtd_info.sync = blkmtd_sync;
++ }
++ dev->mtd_info.read = blkmtd_read;
++ dev->mtd_info.readv = default_mtd_readv;
++ dev->mtd_info.priv = dev;
++ dev->mtd_info.owner = THIS_MODULE;
++
++ list_add(&dev->list, &blkmtd_device_list);
++ if (add_mtd_device(&dev->mtd_info)) {
++ /* Device didnt get added, so free the entry */
++ list_del(&dev->list);
++ goto devinit_err;
++ } else {
++ info("mtd%d: [%s] erase_size = %dKiB %s",
++ dev->mtd_info.index, dev->mtd_info.name + strlen("blkmtd: "),
++ dev->mtd_info.erasesize >> 10,
++ readonly ? "(read-only)" : "");
++ }
++
++ return dev;
++
++ devinit_err:
++ free_device(dev);
++ return NULL;
++}
++
++
++/* Cleanup and exit - sync the device and kill of the kernel thread */
++static void __devexit cleanup_blkmtd(void)
++{
++ struct list_head *temp1, *temp2;
++
++ /* Remove the MTD devices */
++ list_for_each_safe(temp1, temp2, &blkmtd_device_list) {
++ struct blkmtd_dev *dev = list_entry(temp1, struct blkmtd_dev,
++ list);
++ blkmtd_sync(&dev->mtd_info);
++ del_mtd_device(&dev->mtd_info);
++ info("mtd%d: [%s] removed", dev->mtd_info.index,
++ dev->mtd_info.name + strlen("blkmtd: "));
++ list_del(&dev->list);
++ free_device(dev);
++ }
++}
++
++#ifndef MODULE
++
++/* Handle kernel boot params */
++
++
++static int __init param_blkmtd_device(char *str)
++{
++ int i;
++
++ for(i = 0; i < MAX_DEVICES; i++) {
++ device[i] = str;
++ DEBUG(2, "blkmtd: device setup: %d = %s\n", i, device[i]);
++ strsep(&str, ",");
++ }
++ return 1;
++}
++
++
++static int __init param_blkmtd_erasesz(char *str)
++{
++ int i;
++ for(i = 0; i < MAX_DEVICES; i++) {
++ char *val = strsep(&str, ",");
++ if(val)
++ erasesz[i] = simple_strtoul(val, NULL, 0);
++ DEBUG(2, "blkmtd: erasesz setup: %d = %d\n", i, erasesz[i]);
++ }
++
++ return 1;
++}
++
++
++static int __init param_blkmtd_ro(char *str)
++{
++ int i;
++ for(i = 0; i < MAX_DEVICES; i++) {
++ char *val = strsep(&str, ",");
++ if(val)
++ ro[i] = simple_strtoul(val, NULL, 0);
++ DEBUG(2, "blkmtd: ro setup: %d = %d\n", i, ro[i]);
++ }
++
++ return 1;
++}
++
++
++static int __init param_blkmtd_sync(char *str)
++{
++ if(str[0] == '1')
++ sync = 1;
++ return 1;
++}
++
++__setup("blkmtd_device=", param_blkmtd_device);
++__setup("blkmtd_erasesz=", param_blkmtd_erasesz);
++__setup("blkmtd_ro=", param_blkmtd_ro);
++__setup("blkmtd_sync=", param_blkmtd_sync);
++
++#endif
++
++
++/* Startup */
++static int __init init_blkmtd(void)
++{
++ int i;
++
++ info("version " VERSION);
++ /* Check args - device[0] is the bare minimum*/
++ if(!device[0]) {
++ err("error: missing `device' name\n");
++ return -EINVAL;
++ }
++
++ for(i = 0; i < MAX_DEVICES; i++)
++ add_device(device[i], ro[i], erasesz[i] << 10);
++
++ if(list_empty(&blkmtd_device_list))
++ return -EINVAL;
++
++ return 0;
++}
++
++module_init(init_blkmtd);
++module_exit(cleanup_blkmtd);
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/devices/blkmtd.c linux/drivers/mtd/devices/blkmtd.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/devices/blkmtd.c 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/devices/blkmtd.c 2004-11-17 18:17:58.991321216 +0100
+@@ -1,5 +1,5 @@
+ /*
+- * $Id$
++ * $Id$
+ *
+ * blkmtd.c - use a block device as a fake MTD
+ *
+@@ -143,7 +143,7 @@
+ for(cnt = 0; cnt < pages; cnt++) {
+ page = grab_cache_page(dev->binding->bd_inode->i_mapping, pagenrs[cnt]);
+ pagelst[cnt] = page;
+- if(!PageUptodate(page)) {
++ if(!Page_Uptodate(page)) {
+ iobuf->blocks[iobuf->nr_pages] = pagenrs[cnt];
+ iobuf->maplist[iobuf->nr_pages++] = page;
+ }
+@@ -912,7 +912,7 @@
+ dev->mtd_info.point = 0;
+ dev->mtd_info.unpoint = 0;
+ dev->mtd_info.priv = dev;
+- dev->mtd_info.module = THIS_MODULE;
++ dev->mtd_info.owner = THIS_MODULE;
+
+ list_add(&dev->list, &blkmtd_device_list);
+ if (add_mtd_device(&dev->mtd_info)) {
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/devices/doc2000.c linux/drivers/mtd/devices/doc2000.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/devices/doc2000.c 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/devices/doc2000.c 2004-11-17 18:17:58.994320760 +0100
+@@ -4,7 +4,7 @@
+ * (c) 1999 Machine Vision Holdings, Inc.
+ * (c) 1999, 2000 David Woodhouse <dwmw2@infradead.org>
+ *
+- * $Id$
++ * $Id$
+ */
+
+ #include <linux/kernel.h>
+@@ -25,6 +25,7 @@
+ #include <linux/mtd/doc2000.h>
+
+ #define DOC_SUPPORT_2000
++#define DOC_SUPPORT_2000TSOP
+ #define DOC_SUPPORT_MILLENNIUM
+
+ #ifdef DOC_SUPPORT_2000
+@@ -33,7 +34,7 @@
+ #define DoC_is_2000(doc) (0)
+ #endif
+
+-#ifdef DOC_SUPPORT_MILLENNIUM
++#if defined(DOC_SUPPORT_2000TSOP) || defined(DOC_SUPPORT_MILLENNIUM)
+ #define DoC_is_Millennium(doc) (doc->ChipID == DOC_ChipID_DocMil)
+ #else
+ #define DoC_is_Millennium(doc) (0)
+@@ -56,6 +57,9 @@
+ size_t *retlen, u_char *buf, u_char *eccbuf, int oobsel);
+ static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len,
+ size_t *retlen, const u_char *buf, u_char *eccbuf, int oobsel);
++static int doc_writev_ecc(struct mtd_info *mtd, const struct iovec *vecs,
++ unsigned long count, loff_t to, size_t *retlen,
++ u_char *eccbuf, struct nand_oobinfo *oobsel);
+ static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
+ size_t *retlen, u_char *buf);
+ static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
+@@ -92,6 +96,10 @@
+
+ /* Out-of-line routine to wait for chip response */
+ while (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B)) {
++ /* issue 2 read from NOP register after reading from CDSNControl register
++ see Software Requirement 11.4 item 2. */
++ DoC_Delay(doc, 2);
++
+ if (time_after(jiffies, timeo)) {
+ DEBUG(MTD_DEBUG_LEVEL2, "_DoC_WaitReady timed out.\n");
+ return -EIO;
+@@ -145,6 +153,8 @@
+
+ /* Send the command */
+ WriteDOC_(command, docptr, doc->ioreg);
++ if (DoC_is_Millennium(doc))
++ WriteDOC(command, docptr, WritePipeTerm);
+
+ /* Lower the CLE line */
+ WriteDOC(xtraflags | CDSN_CTRL_CE, docptr, CDSNControl);
+@@ -206,6 +216,9 @@
+ }
+ }
+
++ if (DoC_is_Millennium(doc))
++ WriteDOC(ofs & 0xff, docptr, WritePipeTerm);
++
+ DoC_Delay(doc, 2); /* Needed for some slow flash chips. mf. */
+
+ /* FIXME: The SlowIO's for millennium could be replaced by
+@@ -344,15 +357,25 @@
+
+ /* Read the manufacturer and device id codes from the device */
+
+- /* CDSN Slow IO register see Software Requirement 11.4 item 5. */
++ if (DoC_is_Millennium(doc)) {
++ DoC_Delay(doc, 2);
++ dummy = ReadDOC(doc->virtadr, ReadPipeInit);
++ mfr = ReadDOC(doc->virtadr, LastDataRead);
++
++ DoC_Delay(doc, 2);
++ dummy = ReadDOC(doc->virtadr, ReadPipeInit);
++ id = ReadDOC(doc->virtadr, LastDataRead);
++ } else {
++ /* CDSN Slow IO register see Software Req 11.4 item 5. */
+ dummy = ReadDOC(doc->virtadr, CDSNSlowIO);
+ DoC_Delay(doc, 2);
+ mfr = ReadDOC_(doc->virtadr, doc->ioreg);
+
+- /* CDSN Slow IO register see Software Requirement 11.4 item 5. */
++ /* CDSN Slow IO register see Software Req 11.4 item 5. */
+ dummy = ReadDOC(doc->virtadr, CDSNSlowIO);
+ DoC_Delay(doc, 2);
+ id = ReadDOC_(doc->virtadr, doc->ioreg);
++ }
+
+ /* No response - return failure */
+ if (mfr == 0xff || mfr == 0)
+@@ -410,20 +433,16 @@
+
+ /* DoC_ScanChips: Find all NAND chips present in a DiskOnChip, and identify them */
+
+-static void DoC_ScanChips(struct DiskOnChip *this)
++static void DoC_ScanChips(struct DiskOnChip *this, int maxchips)
+ {
+ int floor, chip;
+ int numchips[MAX_FLOORS];
+- int maxchips = MAX_CHIPS;
+ int ret = 1;
+
+ this->numchips = 0;
+ this->mfr = 0;
+ this->id = 0;
+
+- if (DoC_is_Millennium(this))
+- maxchips = MAX_CHIPS_MIL;
+-
+ /* For each floor, find the number of valid chips it contains */
+ for (floor = 0; floor < MAX_FLOORS; floor++) {
+ ret = 1;
+@@ -515,6 +534,7 @@
+ {
+ struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv;
+ struct DiskOnChip *old = NULL;
++ int maxchips;
+
+ /* We must avoid being called twice for the same device. */
+
+@@ -538,14 +558,28 @@
+
+
+ switch (this->ChipID) {
++ case DOC_ChipID_Doc2kTSOP:
++ mtd->name = "DiskOnChip 2000 TSOP";
++ this->ioreg = DoC_Mil_CDSN_IO;
++ /* Pretend it's a Millennium */
++ this->ChipID = DOC_ChipID_DocMil;
++ maxchips = MAX_CHIPS;
++ break;
+ case DOC_ChipID_Doc2k:
+ mtd->name = "DiskOnChip 2000";
+ this->ioreg = DoC_2k_CDSN_IO;
++ maxchips = MAX_CHIPS;
+ break;
+ case DOC_ChipID_DocMil:
+ mtd->name = "DiskOnChip Millennium";
+ this->ioreg = DoC_Mil_CDSN_IO;
++ maxchips = MAX_CHIPS_MIL;
+ break;
++ default:
++ printk("Unknown ChipID 0x%02x\n", this->ChipID);
++ kfree(mtd);
++ iounmap((void *) this->virtadr);
++ return;
+ }
+
+ printk(KERN_NOTICE "%s found at address 0x%lX\n", mtd->name,
+@@ -553,11 +587,12 @@
+
+ mtd->type = MTD_NANDFLASH;
+ mtd->flags = MTD_CAP_NANDFLASH;
++ mtd->ecctype = MTD_ECC_RS_DiskOnChip;
+ mtd->size = 0;
+ mtd->erasesize = 0;
+ mtd->oobblock = 512;
+ mtd->oobsize = 16;
+- mtd->module = THIS_MODULE;
++ mtd->owner = THIS_MODULE;
+ mtd->erase = doc_erase;
+ mtd->point = NULL;
+ mtd->unpoint = NULL;
+@@ -565,6 +600,7 @@
+ mtd->write = doc_write;
+ mtd->read_ecc = doc_read_ecc;
+ mtd->write_ecc = doc_write_ecc;
++ mtd->writev_ecc = doc_writev_ecc;
+ mtd->read_oob = doc_read_oob;
+ mtd->write_oob = doc_write_oob;
+ mtd->sync = NULL;
+@@ -577,7 +613,7 @@
+ init_MUTEX(&this->lock);
+
+ /* Ident all the chips present. */
+- DoC_ScanChips(this);
++ DoC_ScanChips(this, maxchips);
+
+ if (!this->totlen) {
+ kfree(mtd);
+@@ -608,6 +644,7 @@
+ unsigned char syndrome[6];
+ volatile char dummy;
+ int i, len256 = 0, ret=0;
++ size_t left = len;
+
+ docptr = this->virtadr;
+
+@@ -617,6 +654,10 @@
+
+ down(&this->lock);
+
++ *retlen = 0;
++ while (left) {
++ len = left;
++
+ /* Don't allow a single read to cross a 512-byte block boundary */
+ if (from + len > ((from | 0x1ff) + 1))
+ len = ((from | 0x1ff) + 1) - from;
+@@ -673,7 +714,7 @@
+ DoC_ReadBuf(this, &buf[len256], len - len256);
+
+ /* Let the caller know we completed it */
+- *retlen = len;
++ *retlen += len;
+
+ if (eccbuf) {
+ /* Read the ECC data through the DiskOnChip ECC logic */
+@@ -730,11 +771,16 @@
+
+ /* according to 11.4.1, we need to wait for the busy line
+ * drop if we read to the end of the page. */
+- if(0 == ((from + *retlen) & 0x1ff))
++ if(0 == ((from + len) & 0x1ff))
+ {
+ DoC_WaitReady(this);
+ }
+
++ from += len;
++ left -= len;
++ buf += len;
++ }
++
+ up(&this->lock);
+
+ return ret;
+@@ -757,6 +803,8 @@
+ volatile char dummy;
+ int len256 = 0;
+ struct Nand *mychip;
++ size_t left = len;
++ int status;
+
+ docptr = this->virtadr;
+
+@@ -766,15 +814,21 @@
+
+ down(&this->lock);
+
++ *retlen = 0;
++ while (left) {
++ len = left;
++
+ /* Don't allow a single write to cross a 512-byte block boundary */
+ if (to + len > ((to | 0x1ff) + 1))
+ len = ((to | 0x1ff) + 1) - to;
+
+ /* The ECC will not be calculated correctly if less than 512 is written */
++/* DBB-
+ if (len != 0x200 && eccbuf)
+ printk(KERN_WARNING
+ "ECC needs a full sector write (adr: %lx size %lx)\n",
+ (long) to, (long) len);
++ -DBB */
+
+ /* printk("DoC_Write (adr: %lx size %lx)\n", (long) to, (long) len); */
+
+@@ -853,6 +907,9 @@
+ WriteDOC_(0, docptr, this->ioreg);
+ }
+
++ WriteDOC(CDSN_CTRL_ECC_IO | CDSN_CTRL_FLASH_IO | CDSN_CTRL_CE, docptr,
++ CDSNControl);
++
+ /* Read the ECC data through the DiskOnChip ECC logic */
+ for (di = 0; di < 6; di++) {
+ eccbuf[di] = ReadDOC(docptr, ECCSyndrome0 + di);
+@@ -874,10 +931,16 @@
+ DoC_Command(this, NAND_CMD_STATUS, CDSN_CTRL_WP);
+ /* There's an implicit DoC_WaitReady() in DoC_Command */
+
++ if (DoC_is_Millennium(this)) {
++ ReadDOC(docptr, ReadPipeInit);
++ status = ReadDOC(docptr, LastDataRead);
++ } else {
+ dummy = ReadDOC(docptr, CDSNSlowIO);
+ DoC_Delay(this, 2);
++ status = ReadDOC_(docptr, this->ioreg);
++ }
+
+- if (ReadDOC_(docptr, this->ioreg) & 1) {
++ if (status & 1) {
+ printk(KERN_ERR "Error programming flash\n");
+ /* Error in programming */
+ *retlen = 0;
+@@ -886,7 +949,7 @@
+ }
+
+ /* Let the caller know we completed it */
+- *retlen = len;
++ *retlen += len;
+
+ if (eccbuf) {
+ unsigned char x[8];
+@@ -901,13 +964,81 @@
+ x[7]=0x55;
+
+ ret = doc_write_oob_nolock(mtd, to, 8, &dummy, x);
++ if (ret) {
+ up(&this->lock);
+ return ret;
+ }
++ }
++
++ to += len;
++ left -= len;
++ buf += len;
++ }
++
+ up(&this->lock);
+ return 0;
+ }
+
++static int doc_writev_ecc(struct mtd_info *mtd, const struct iovec *vecs,
++ unsigned long count, loff_t to, size_t *retlen,
++ u_char *eccbuf, struct nand_oobinfo *oobsel)
++{
++ static char static_buf[512];
++ static DECLARE_MUTEX(writev_buf_sem);
++
++ size_t totretlen = 0;
++ size_t thisvecofs = 0;
++ int ret= 0;
++
++ down(&writev_buf_sem);
++
++ while(count) {
++ size_t thislen, thisretlen;
++ unsigned char *buf;
++
++ buf = vecs->iov_base + thisvecofs;
++ thislen = vecs->iov_len - thisvecofs;
++
++
++ if (thislen >= 512) {
++ thislen = thislen & ~(512-1);
++ thisvecofs += thislen;
++ } else {
++ /* Not enough to fill a page. Copy into buf */
++ memcpy(static_buf, buf, thislen);
++ buf = &static_buf[thislen];
++
++ while(count && thislen < 512) {
++ vecs++;
++ count--;
++ thisvecofs = min((512-thislen), vecs->iov_len);
++ memcpy(buf, vecs->iov_base, thisvecofs);
++ thislen += thisvecofs;
++ buf += thisvecofs;
++ }
++ buf = static_buf;
++ }
++ if (count && thisvecofs == vecs->iov_len) {
++ thisvecofs = 0;
++ vecs++;
++ count--;
++ }
++ ret = doc_write_ecc(mtd, to, thislen, &thisretlen, buf, eccbuf, oobsel);
++
++ totretlen += thisretlen;
++
++ if (ret || thisretlen != thislen)
++ break;
++
++ to += thislen;
++ }
++
++ up(&writev_buf_sem);
++ *retlen = totretlen;
++ return ret;
++}
++
++
+ static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
+ size_t * retlen, u_char * buf)
+ {
+@@ -977,6 +1108,7 @@
+ unsigned long docptr = this->virtadr;
+ struct Nand *mychip = &this->chips[ofs >> this->chipshift];
+ volatile int dummy;
++ int status;
+
+ // printk("doc_write_oob(%lx, %d): %2.2X %2.2X %2.2X %2.2X ... %2.2X %2.2X .. %2.2X %2.2X\n",(long)ofs, len,
+ // buf[0], buf[1], buf[2], buf[3], buf[8], buf[9], buf[14],buf[15]);
+@@ -1025,10 +1157,16 @@
+ DoC_Command(this, NAND_CMD_STATUS, 0);
+ /* DoC_WaitReady() is implicit in DoC_Command */
+
++ if (DoC_is_Millennium(this)) {
++ ReadDOC(docptr, ReadPipeInit);
++ status = ReadDOC(docptr, LastDataRead);
++ } else {
+ dummy = ReadDOC(docptr, CDSNSlowIO);
+ DoC_Delay(this, 2);
++ status = ReadDOC_(docptr, this->ioreg);
++ }
+
+- if (ReadDOC_(docptr, this->ioreg) & 1) {
++ if (status & 1) {
+ printk(KERN_ERR "Error programming oob data\n");
+ /* There was an error */
+ *retlen = 0;
+@@ -1044,10 +1182,16 @@
+ DoC_Command(this, NAND_CMD_STATUS, 0);
+ /* DoC_WaitReady() is implicit in DoC_Command */
+
++ if (DoC_is_Millennium(this)) {
++ ReadDOC(docptr, ReadPipeInit);
++ status = ReadDOC(docptr, LastDataRead);
++ } else {
+ dummy = ReadDOC(docptr, CDSNSlowIO);
+ DoC_Delay(this, 2);
++ status = ReadDOC_(docptr, this->ioreg);
++ }
+
+- if (ReadDOC_(docptr, this->ioreg) & 1) {
++ if (status & 1) {
+ printk(KERN_ERR "Error programming oob data\n");
+ /* There was an error */
+ *retlen = 0;
+@@ -1080,6 +1224,7 @@
+ volatile int dummy;
+ unsigned long docptr;
+ struct Nand *mychip;
++ int status;
+
+ down(&this->lock);
+
+@@ -1111,10 +1256,16 @@
+
+ DoC_Command(this, NAND_CMD_STATUS, CDSN_CTRL_WP);
+
++ if (DoC_is_Millennium(this)) {
++ ReadDOC(docptr, ReadPipeInit);
++ status = ReadDOC(docptr, LastDataRead);
++ } else {
+ dummy = ReadDOC(docptr, CDSNSlowIO);
+ DoC_Delay(this, 2);
++ status = ReadDOC_(docptr, this->ioreg);
++ }
+
+- if (ReadDOC_(docptr, this->ioreg) & 1) {
++ if (status & 1) {
+ printk(KERN_ERR "Error erasing at 0x%x\n", ofs);
+ /* There was an error */
+ instr->state = MTD_ERASE_FAILED;
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/devices/doc2001.c linux/drivers/mtd/devices/doc2001.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/devices/doc2001.c 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/devices/doc2001.c 2004-11-17 18:17:58.995320608 +0100
+@@ -4,7 +4,7 @@
+ * (c) 1999 Machine Vision Holdings, Inc.
+ * (c) 1999, 2000 David Woodhouse <dwmw2@infradead.org>
+ *
+- * $Id$
++ * $Id$
+ */
+
+ #include <linux/kernel.h>
+@@ -359,14 +359,15 @@
+
+ mtd->type = MTD_NANDFLASH;
+ mtd->flags = MTD_CAP_NANDFLASH;
++ mtd->ecctype = MTD_ECC_RS_DiskOnChip;
+ mtd->size = 0;
+
+- /* FIXME: erase size is not always 8kB */
++ /* FIXME: erase size is not always 8KiB */
+ mtd->erasesize = 0x2000;
+
+ mtd->oobblock = 512;
+ mtd->oobsize = 16;
+- mtd->module = THIS_MODULE;
++ mtd->owner = THIS_MODULE;
+ mtd->erase = doc_erase;
+ mtd->point = NULL;
+ mtd->unpoint = NULL;
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/devices/doc2001plus.c linux/drivers/mtd/devices/doc2001plus.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/devices/doc2001plus.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/mtd/devices/doc2001plus.c 2004-11-17 18:17:59.000000000 +0100
+@@ -0,0 +1,1154 @@
++/*
++ * Linux driver for Disk-On-Chip Millennium Plus
++ *
++ * (c) 2002-2003 Greg Ungerer <gerg@snapgear.com>
++ * (c) 2002-2003 SnapGear Inc
++ * (c) 1999 Machine Vision Holdings, Inc.
++ * (c) 1999, 2000 David Woodhouse <dwmw2@infradead.org>
++ *
++ * $Id$
++ *
++ * Released under GPL
++ */
++
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <asm/errno.h>
++#include <asm/io.h>
++#include <asm/uaccess.h>
++#include <linux/miscdevice.h>
++#include <linux/pci.h>
++#include <linux/delay.h>
++#include <linux/slab.h>
++#include <linux/sched.h>
++#include <linux/init.h>
++#include <linux/types.h>
++
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/nand.h>
++#include <linux/mtd/doc2000.h>
++
++/* #define ECC_DEBUG */
++
++/* I have no idea why some DoC chips can not use memcop_form|to_io().
++ * This may be due to the different revisions of the ASIC controller built-in or
++ * simplily a QA/Bug issue. Who knows ?? If you have trouble, please uncomment
++ * this:*/
++#undef USE_MEMCPY
++
++static int doc_read(struct mtd_info *mtd, loff_t from, size_t len,
++ size_t *retlen, u_char *buf);
++static int doc_write(struct mtd_info *mtd, loff_t to, size_t len,
++ size_t *retlen, const u_char *buf);
++static int doc_read_ecc(struct mtd_info *mtd, loff_t from, size_t len,
++ size_t *retlen, u_char *buf, u_char *eccbuf,
++ struct nand_oobinfo *oobsel);
++static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len,
++ size_t *retlen, const u_char *buf, u_char *eccbuf,
++ struct nand_oobinfo *oobsel);
++static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
++ size_t *retlen, u_char *buf);
++static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
++ size_t *retlen, const u_char *buf);
++static int doc_erase (struct mtd_info *mtd, struct erase_info *instr);
++
++static struct mtd_info *docmilpluslist = NULL;
++
++
++/* Perform the required delay cycles by writing to the NOP register */
++static void DoC_Delay(unsigned long docptr, int cycles)
++{
++ int i;
++
++ for (i = 0; (i < cycles); i++)
++ WriteDOC(0, docptr, Mplus_NOP);
++}
++
++#define CDSN_CTRL_FR_B_MASK (CDSN_CTRL_FR_B0 | CDSN_CTRL_FR_B1)
++
++/* DOC_WaitReady: Wait for RDY line to be asserted by the flash chip */
++static int _DoC_WaitReady(unsigned long docptr)
++{
++ unsigned int c = 0xffff;
++
++ DEBUG(MTD_DEBUG_LEVEL3,
++ "_DoC_WaitReady called for out-of-line wait\n");
++
++ /* Out-of-line routine to wait for chip response */
++ while (((ReadDOC(docptr, Mplus_FlashControl) & CDSN_CTRL_FR_B_MASK) != CDSN_CTRL_FR_B_MASK) && --c)
++ ;
++
++ if (c == 0)
++ DEBUG(MTD_DEBUG_LEVEL2, "_DoC_WaitReady timed out.\n");
++
++ return (c == 0);
++}
++
++static inline int DoC_WaitReady(unsigned long docptr)
++{
++ /* This is inline, to optimise the common case, where it's ready instantly */
++ int ret = 0;
++
++ /* read form NOP register should be issued prior to the read from CDSNControl
++ see Software Requirement 11.4 item 2. */
++ DoC_Delay(docptr, 4);
++
++ if ((ReadDOC(docptr, Mplus_FlashControl) & CDSN_CTRL_FR_B_MASK) != CDSN_CTRL_FR_B_MASK)
++ /* Call the out-of-line routine to wait */
++ ret = _DoC_WaitReady(docptr);
++
++ return ret;
++}
++
++/* For some reason the Millennium Plus seems to occassionally put itself
++ * into reset mode. For me this happens randomly, with no pattern that I
++ * can detect. M-systems suggest always check this on any block level
++ * operation and setting to normal mode if in reset mode.
++ */
++static inline void DoC_CheckASIC(unsigned long docptr)
++{
++ /* Make sure the DoC is in normal mode */
++ if ((ReadDOC(docptr, Mplus_DOCControl) & DOC_MODE_NORMAL) == 0) {
++ WriteDOC((DOC_MODE_NORMAL | DOC_MODE_MDWREN), docptr, Mplus_DOCControl);
++ WriteDOC(~(DOC_MODE_NORMAL | DOC_MODE_MDWREN), docptr, Mplus_CtrlConfirm);
++ }
++}
++
++/* DoC_Command: Send a flash command to the flash chip through the Flash
++ * command register. Need 2 Write Pipeline Terminates to complete send.
++ */
++static inline void DoC_Command(unsigned long docptr, unsigned char command,
++ unsigned char xtraflags)
++{
++ WriteDOC(command, docptr, Mplus_FlashCmd);
++ WriteDOC(command, docptr, Mplus_WritePipeTerm);
++ WriteDOC(command, docptr, Mplus_WritePipeTerm);
++}
++
++/* DoC_Address: Set the current address for the flash chip through the Flash
++ * Address register. Need 2 Write Pipeline Terminates to complete send.
++ */
++static inline void DoC_Address(struct DiskOnChip *doc, int numbytes,
++ unsigned long ofs, unsigned char xtraflags1,
++ unsigned char xtraflags2)
++{
++ unsigned long docptr = doc->virtadr;
++
++ /* Allow for possible Mill Plus internal flash interleaving */
++ ofs >>= doc->interleave;
++
++ switch (numbytes) {
++ case 1:
++ /* Send single byte, bits 0-7. */
++ WriteDOC(ofs & 0xff, docptr, Mplus_FlashAddress);
++ break;
++ case 2:
++ /* Send bits 9-16 followed by 17-23 */
++ WriteDOC((ofs >> 9) & 0xff, docptr, Mplus_FlashAddress);
++ WriteDOC((ofs >> 17) & 0xff, docptr, Mplus_FlashAddress);
++ break;
++ case 3:
++ /* Send 0-7, 9-16, then 17-23 */
++ WriteDOC(ofs & 0xff, docptr, Mplus_FlashAddress);
++ WriteDOC((ofs >> 9) & 0xff, docptr, Mplus_FlashAddress);
++ WriteDOC((ofs >> 17) & 0xff, docptr, Mplus_FlashAddress);
++ break;
++ default:
++ return;
++ }
++
++ WriteDOC(0x00, docptr, Mplus_WritePipeTerm);
++ WriteDOC(0x00, docptr, Mplus_WritePipeTerm);
++}
++
++/* DoC_SelectChip: Select a given flash chip within the current floor */
++static int DoC_SelectChip(unsigned long docptr, int chip)
++{
++ /* No choice for flash chip on Millennium Plus */
++ return 0;
++}
++
++/* DoC_SelectFloor: Select a given floor (bank of flash chips) */
++static int DoC_SelectFloor(unsigned long docptr, int floor)
++{
++ WriteDOC((floor & 0x3), docptr, Mplus_DeviceSelect);
++ return 0;
++}
++
++/*
++ * Translate the given offset into the appropriate command and offset.
++ * This does the mapping using the 16bit interleave layout defined by
++ * M-Systems, and looks like this for a sector pair:
++ * +-----------+-------+-------+-------+--------------+---------+-----------+
++ * | 0 --- 511 |512-517|518-519|520-521| 522 --- 1033 |1034-1039|1040 - 1055|
++ * +-----------+-------+-------+-------+--------------+---------+-----------+
++ * | Data 0 | ECC 0 |Flags0 |Flags1 | Data 1 |ECC 1 | OOB 1 + 2 |
++ * +-----------+-------+-------+-------+--------------+---------+-----------+
++ */
++/* FIXME: This lives in INFTL not here. Other users of flash devices
++ may not want it */
++static unsigned int DoC_GetDataOffset(struct mtd_info *mtd, loff_t *from)
++{
++ struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv;
++
++ if (this->interleave) {
++ unsigned int ofs = *from & 0x3ff;
++ unsigned int cmd;
++
++ if (ofs < 512) {
++ cmd = NAND_CMD_READ0;
++ ofs &= 0x1ff;
++ } else if (ofs < 1014) {
++ cmd = NAND_CMD_READ1;
++ ofs = (ofs & 0x1ff) + 10;
++ } else {
++ cmd = NAND_CMD_READOOB;
++ ofs = ofs - 1014;
++ }
++
++ *from = (*from & ~0x3ff) | ofs;
++ return cmd;
++ } else {
++ /* No interleave */
++ if ((*from) & 0x100)
++ return NAND_CMD_READ1;
++ return NAND_CMD_READ0;
++ }
++}
++
++static unsigned int DoC_GetECCOffset(struct mtd_info *mtd, loff_t *from)
++{
++ unsigned int ofs, cmd;
++
++ if (*from & 0x200) {
++ cmd = NAND_CMD_READOOB;
++ ofs = 10 + (*from & 0xf);
++ } else {
++ cmd = NAND_CMD_READ1;
++ ofs = (*from & 0xf);
++ }
++
++ *from = (*from & ~0x3ff) | ofs;
++ return cmd;
++}
++
++static unsigned int DoC_GetFlagsOffset(struct mtd_info *mtd, loff_t *from)
++{
++ unsigned int ofs, cmd;
++
++ cmd = NAND_CMD_READ1;
++ ofs = (*from & 0x200) ? 8 : 6;
++ *from = (*from & ~0x3ff) | ofs;
++ return cmd;
++}
++
++static unsigned int DoC_GetHdrOffset(struct mtd_info *mtd, loff_t *from)
++{
++ unsigned int ofs, cmd;
++
++ cmd = NAND_CMD_READOOB;
++ ofs = (*from & 0x200) ? 24 : 16;
++ *from = (*from & ~0x3ff) | ofs;
++ return cmd;
++}
++
++static inline void MemReadDOC(unsigned long docptr, unsigned char *buf, int len)
++{
++#ifndef USE_MEMCPY
++ int i;
++ for (i = 0; i < len; i++)
++ buf[i] = ReadDOC(docptr, Mil_CDSN_IO + i);
++#else
++ memcpy_fromio(buf, docptr + DoC_Mil_CDSN_IO, len);
++#endif
++}
++
++static inline void MemWriteDOC(unsigned long docptr, unsigned char *buf, int len)
++{
++#ifndef USE_MEMCPY
++ int i;
++ for (i = 0; i < len; i++)
++ WriteDOC(buf[i], docptr, Mil_CDSN_IO + i);
++#else
++ memcpy_toio(docptr + DoC_Mil_CDSN_IO, buf, len);
++#endif
++}
++
++/* DoC_IdentChip: Identify a given NAND chip given {floor,chip} */
++static int DoC_IdentChip(struct DiskOnChip *doc, int floor, int chip)
++{
++ int mfr, id, i, j;
++ volatile char dummy;
++ unsigned long docptr = doc->virtadr;
++
++ /* Page in the required floor/chip */
++ DoC_SelectFloor(docptr, floor);
++ DoC_SelectChip(docptr, chip);
++
++ /* Millennium Plus bus cycle sequence as per figure 2, section 2.4 */
++ WriteDOC((DOC_FLASH_CE | DOC_FLASH_WP), docptr, Mplus_FlashSelect);
++
++ /* Reset the chip, see Software Requirement 11.4 item 1. */
++ DoC_Command(docptr, NAND_CMD_RESET, 0);
++ DoC_WaitReady(docptr);
++
++ /* Read the NAND chip ID: 1. Send ReadID command */
++ DoC_Command(docptr, NAND_CMD_READID, 0);
++
++ /* Read the NAND chip ID: 2. Send address byte zero */
++ DoC_Address(doc, 1, 0x00, 0, 0x00);
++
++ WriteDOC(0, docptr, Mplus_FlashControl);
++ DoC_WaitReady(docptr);
++
++ /* Read the manufacturer and device id codes of the flash device through
++ CDSN IO register see Software Requirement 11.4 item 5.*/
++ dummy = ReadDOC(docptr, Mplus_ReadPipeInit);
++ dummy = ReadDOC(docptr, Mplus_ReadPipeInit);
++
++ mfr = ReadDOC(docptr, Mil_CDSN_IO);
++ if (doc->interleave)
++ dummy = ReadDOC(docptr, Mil_CDSN_IO); /* 2 way interleave */
++
++ id = ReadDOC(docptr, Mil_CDSN_IO);
++ if (doc->interleave)
++ dummy = ReadDOC(docptr, Mil_CDSN_IO); /* 2 way interleave */
++
++ dummy = ReadDOC(docptr, Mplus_LastDataRead);
++ dummy = ReadDOC(docptr, Mplus_LastDataRead);
++
++ /* Disable flash internally */
++ WriteDOC(0, docptr, Mplus_FlashSelect);
++
++ /* No response - return failure */
++ if (mfr == 0xff || mfr == 0)
++ return 0;
++
++ for (i = 0; nand_flash_ids[i].name != NULL; i++) {
++ if (id == nand_flash_ids[i].id) {
++ /* Try to identify manufacturer */
++ for (j = 0; nand_manuf_ids[j].id != 0x0; j++) {
++ if (nand_manuf_ids[j].id == mfr)
++ break;
++ }
++ printk(KERN_INFO "Flash chip found: Manufacturer ID: %2.2X, "
++ "Chip ID: %2.2X (%s:%s)\n", mfr, id,
++ nand_manuf_ids[j].name, nand_flash_ids[i].name);
++ doc->mfr = mfr;
++ doc->id = id;
++ doc->chipshift = nand_flash_ids[i].chipshift;
++ doc->erasesize = nand_flash_ids[i].erasesize << doc->interleave;
++ break;
++ }
++ }
++
++ if (nand_flash_ids[i].name == NULL)
++ return 0;
++ return 1;
++}
++
++/* DoC_ScanChips: Find all NAND chips present in a DiskOnChip, and identify them */
++static void DoC_ScanChips(struct DiskOnChip *this)
++{
++ int floor, chip;
++ int numchips[MAX_FLOORS_MPLUS];
++ int ret;
++
++ this->numchips = 0;
++ this->mfr = 0;
++ this->id = 0;
++
++ /* Work out the intended interleave setting */
++ this->interleave = 0;
++ if (this->ChipID == DOC_ChipID_DocMilPlus32)
++ this->interleave = 1;
++
++ /* Check the ASIC agrees */
++ if ( (this->interleave << 2) !=
++ (ReadDOC(this->virtadr, Mplus_Configuration) & 4)) {
++ u_char conf = ReadDOC(this->virtadr, Mplus_Configuration);
++ printk(KERN_NOTICE "Setting DiskOnChip Millennium Plus interleave to %s\n",
++ this->interleave?"on (16-bit)":"off (8-bit)");
++ conf ^= 4;
++ WriteDOC(this->virtadr, conf, Mplus_Configuration);
++ }
++
++ /* For each floor, find the number of valid chips it contains */
++ for (floor = 0,ret = 1; floor < MAX_FLOORS_MPLUS; floor++) {
++ numchips[floor] = 0;
++ for (chip = 0; chip < MAX_CHIPS_MPLUS && ret != 0; chip++) {
++ ret = DoC_IdentChip(this, floor, chip);
++ if (ret) {
++ numchips[floor]++;
++ this->numchips++;
++ }
++ }
++ }
++ /* If there are none at all that we recognise, bail */
++ if (!this->numchips) {
++ printk("No flash chips recognised.\n");
++ return;
++ }
++
++ /* Allocate an array to hold the information for each chip */
++ this->chips = kmalloc(sizeof(struct Nand) * this->numchips, GFP_KERNEL);
++ if (!this->chips){
++ printk("MTD: No memory for allocating chip info structures\n");
++ return;
++ }
++
++ /* Fill out the chip array with {floor, chipno} for each
++ * detected chip in the device. */
++ for (floor = 0, ret = 0; floor < MAX_FLOORS_MPLUS; floor++) {
++ for (chip = 0 ; chip < numchips[floor] ; chip++) {
++ this->chips[ret].floor = floor;
++ this->chips[ret].chip = chip;
++ this->chips[ret].curadr = 0;
++ this->chips[ret].curmode = 0x50;
++ ret++;
++ }
++ }
++
++ /* Calculate and print the total size of the device */
++ this->totlen = this->numchips * (1 << this->chipshift);
++ printk(KERN_INFO "%d flash chips found. Total DiskOnChip size: %ld MiB\n",
++ this->numchips ,this->totlen >> 20);
++}
++
++static int DoCMilPlus_is_alias(struct DiskOnChip *doc1, struct DiskOnChip *doc2)
++{
++ int tmp1, tmp2, retval;
++
++ if (doc1->physadr == doc2->physadr)
++ return 1;
++
++ /* Use the alias resolution register which was set aside for this
++ * purpose. If it's value is the same on both chips, they might
++ * be the same chip, and we write to one and check for a change in
++ * the other. It's unclear if this register is usuable in the
++ * DoC 2000 (it's in the Millennium docs), but it seems to work. */
++ tmp1 = ReadDOC(doc1->virtadr, Mplus_AliasResolution);
++ tmp2 = ReadDOC(doc2->virtadr, Mplus_AliasResolution);
++ if (tmp1 != tmp2)
++ return 0;
++
++ WriteDOC((tmp1+1) % 0xff, doc1->virtadr, Mplus_AliasResolution);
++ tmp2 = ReadDOC(doc2->virtadr, Mplus_AliasResolution);
++ if (tmp2 == (tmp1+1) % 0xff)
++ retval = 1;
++ else
++ retval = 0;
++
++ /* Restore register contents. May not be necessary, but do it just to
++ * be safe. */
++ WriteDOC(tmp1, doc1->virtadr, Mplus_AliasResolution);
++
++ return retval;
++}
++
++static const char im_name[] = "DoCMilPlus_init";
++
++/* This routine is made available to other mtd code via
++ * inter_module_register. It must only be accessed through
++ * inter_module_get which will bump the use count of this module. The
++ * addresses passed back in mtd are valid as long as the use count of
++ * this module is non-zero, i.e. between inter_module_get and
++ * inter_module_put. Keith Owens <kaos@ocs.com.au> 29 Oct 2000.
++ */
++static void DoCMilPlus_init(struct mtd_info *mtd)
++{
++ struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv;
++ struct DiskOnChip *old = NULL;
++
++ /* We must avoid being called twice for the same device. */
++ if (docmilpluslist)
++ old = (struct DiskOnChip *)docmilpluslist->priv;
++
++ while (old) {
++ if (DoCMilPlus_is_alias(this, old)) {
++ printk(KERN_NOTICE "Ignoring DiskOnChip Millennium "
++ "Plus at 0x%lX - already configured\n",
++ this->physadr);
++ iounmap((void *)this->virtadr);
++ kfree(mtd);
++ return;
++ }
++ if (old->nextdoc)
++ old = (struct DiskOnChip *)old->nextdoc->priv;
++ else
++ old = NULL;
++ }
++
++ mtd->name = "DiskOnChip Millennium Plus";
++ printk(KERN_NOTICE "DiskOnChip Millennium Plus found at "
++ "address 0x%lX\n", this->physadr);
++
++ mtd->type = MTD_NANDFLASH;
++ mtd->flags = MTD_CAP_NANDFLASH;
++ mtd->ecctype = MTD_ECC_RS_DiskOnChip;
++ mtd->size = 0;
++
++ mtd->erasesize = 0;
++ mtd->oobblock = 512;
++ mtd->oobsize = 16;
++ mtd->owner = THIS_MODULE;
++ mtd->erase = doc_erase;
++ mtd->point = NULL;
++ mtd->unpoint = NULL;
++ mtd->read = doc_read;
++ mtd->write = doc_write;
++ mtd->read_ecc = doc_read_ecc;
++ mtd->write_ecc = doc_write_ecc;
++ mtd->read_oob = doc_read_oob;
++ mtd->write_oob = doc_write_oob;
++ mtd->sync = NULL;
++
++ this->totlen = 0;
++ this->numchips = 0;
++ this->curfloor = -1;
++ this->curchip = -1;
++
++ /* Ident all the chips present. */
++ DoC_ScanChips(this);
++
++ if (!this->totlen) {
++ kfree(mtd);
++ iounmap((void *)this->virtadr);
++ } else {
++ this->nextdoc = docmilpluslist;
++ docmilpluslist = mtd;
++ mtd->size = this->totlen;
++ mtd->erasesize = this->erasesize;
++ add_mtd_device(mtd);
++ return;
++ }
++}
++
++#if 0
++static int doc_dumpblk(struct mtd_info *mtd, loff_t from)
++{
++ int i;
++ loff_t fofs;
++ struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv;
++ unsigned long docptr = this->virtadr;
++ struct Nand *mychip = &this->chips[from >> (this->chipshift)];
++ unsigned char *bp, buf[1056];
++ char c[32];
++
++ from &= ~0x3ff;
++
++ /* Don't allow read past end of device */
++ if (from >= this->totlen)
++ return -EINVAL;
++
++ DoC_CheckASIC(docptr);
++
++ /* Find the chip which is to be used and select it */
++ if (this->curfloor != mychip->floor) {
++ DoC_SelectFloor(docptr, mychip->floor);
++ DoC_SelectChip(docptr, mychip->chip);
++ } else if (this->curchip != mychip->chip) {
++ DoC_SelectChip(docptr, mychip->chip);
++ }
++ this->curfloor = mychip->floor;
++ this->curchip = mychip->chip;
++
++ /* Millennium Plus bus cycle sequence as per figure 2, section 2.4 */
++ WriteDOC((DOC_FLASH_CE | DOC_FLASH_WP), docptr, Mplus_FlashSelect);
++
++ /* Reset the chip, see Software Requirement 11.4 item 1. */
++ DoC_Command(docptr, NAND_CMD_RESET, 0);
++ DoC_WaitReady(docptr);
++
++ fofs = from;
++ DoC_Command(docptr, DoC_GetDataOffset(mtd, &fofs), 0);
++ DoC_Address(this, 3, fofs, 0, 0x00);
++ WriteDOC(0, docptr, Mplus_FlashControl);
++ DoC_WaitReady(docptr);
++
++ /* disable the ECC engine */
++ WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf);
++
++ ReadDOC(docptr, Mplus_ReadPipeInit);
++ ReadDOC(docptr, Mplus_ReadPipeInit);
++
++ /* Read the data via the internal pipeline through CDSN IO
++ register, see Pipelined Read Operations 11.3 */
++ MemReadDOC(docptr, buf, 1054);
++ buf[1054] = ReadDOC(docptr, Mplus_LastDataRead);
++ buf[1055] = ReadDOC(docptr, Mplus_LastDataRead);
++
++ memset(&c[0], 0, sizeof(c));
++ printk("DUMP OFFSET=%x:\n", (int)from);
++
++ for (i = 0, bp = &buf[0]; (i < 1056); i++) {
++ if ((i % 16) == 0)
++ printk("%08x: ", i);
++ printk(" %02x", *bp);
++ c[(i & 0xf)] = ((*bp >= 0x20) && (*bp <= 0x7f)) ? *bp : '.';
++ bp++;
++ if (((i + 1) % 16) == 0)
++ printk(" %s\n", c);
++ }
++ printk("\n");
++
++ /* Disable flash internally */
++ WriteDOC(0, docptr, Mplus_FlashSelect);
++
++ return 0;
++}
++#endif
++
++static int doc_read(struct mtd_info *mtd, loff_t from, size_t len,
++ size_t *retlen, u_char *buf)
++{
++ /* Just a special case of doc_read_ecc */
++ return doc_read_ecc(mtd, from, len, retlen, buf, NULL, NULL);
++}
++
++static int doc_read_ecc(struct mtd_info *mtd, loff_t from, size_t len,
++ size_t *retlen, u_char *buf, u_char *eccbuf,
++ struct nand_oobinfo *oobsel)
++{
++ int ret, i;
++ volatile char dummy;
++ loff_t fofs;
++ unsigned char syndrome[6];
++ struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv;
++ unsigned long docptr = this->virtadr;
++ struct Nand *mychip = &this->chips[from >> (this->chipshift)];
++
++ /* Don't allow read past end of device */
++ if (from >= this->totlen)
++ return -EINVAL;
++
++ /* Don't allow a single read to cross a 512-byte block boundary */
++ if (from + len > ((from | 0x1ff) + 1))
++ len = ((from | 0x1ff) + 1) - from;
++
++ DoC_CheckASIC(docptr);
++
++ /* Find the chip which is to be used and select it */
++ if (this->curfloor != mychip->floor) {
++ DoC_SelectFloor(docptr, mychip->floor);
++ DoC_SelectChip(docptr, mychip->chip);
++ } else if (this->curchip != mychip->chip) {
++ DoC_SelectChip(docptr, mychip->chip);
++ }
++ this->curfloor = mychip->floor;
++ this->curchip = mychip->chip;
++
++ /* Millennium Plus bus cycle sequence as per figure 2, section 2.4 */
++ WriteDOC((DOC_FLASH_CE | DOC_FLASH_WP), docptr, Mplus_FlashSelect);
++
++ /* Reset the chip, see Software Requirement 11.4 item 1. */
++ DoC_Command(docptr, NAND_CMD_RESET, 0);
++ DoC_WaitReady(docptr);
++
++ fofs = from;
++ DoC_Command(docptr, DoC_GetDataOffset(mtd, &fofs), 0);
++ DoC_Address(this, 3, fofs, 0, 0x00);
++ WriteDOC(0, docptr, Mplus_FlashControl);
++ DoC_WaitReady(docptr);
++
++ if (eccbuf) {
++ /* init the ECC engine, see Reed-Solomon EDC/ECC 11.1 .*/
++ WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf);
++ WriteDOC(DOC_ECC_EN, docptr, Mplus_ECCConf);
++ } else {
++ /* disable the ECC engine */
++ WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf);
++ }
++
++ /* Let the caller know we completed it */
++ *retlen = len;
++ ret = 0;
++
++ ReadDOC(docptr, Mplus_ReadPipeInit);
++ ReadDOC(docptr, Mplus_ReadPipeInit);
++
++ if (eccbuf) {
++ /* Read the data via the internal pipeline through CDSN IO
++ register, see Pipelined Read Operations 11.3 */
++ MemReadDOC(docptr, buf, len);
++
++ /* Read the ECC data following raw data */
++ MemReadDOC(docptr, eccbuf, 4);
++ eccbuf[4] = ReadDOC(docptr, Mplus_LastDataRead);
++ eccbuf[5] = ReadDOC(docptr, Mplus_LastDataRead);
++
++ /* Flush the pipeline */
++ dummy = ReadDOC(docptr, Mplus_ECCConf);
++ dummy = ReadDOC(docptr, Mplus_ECCConf);
++
++ /* Check the ECC Status */
++ if (ReadDOC(docptr, Mplus_ECCConf) & 0x80) {
++ int nb_errors;
++ /* There was an ECC error */
++#ifdef ECC_DEBUG
++ printk("DiskOnChip ECC Error: Read at %lx\n", (long)from);
++#endif
++ /* Read the ECC syndrom through the DiskOnChip ECC logic.
++ These syndrome will be all ZERO when there is no error */
++ for (i = 0; i < 6; i++)
++ syndrome[i] = ReadDOC(docptr, Mplus_ECCSyndrome0 + i);
++
++ nb_errors = doc_decode_ecc(buf, syndrome);
++#ifdef ECC_DEBUG
++ printk("ECC Errors corrected: %x\n", nb_errors);
++#endif
++ if (nb_errors < 0) {
++ /* We return error, but have actually done the read. Not that
++ this can be told to user-space, via sys_read(), but at least
++ MTD-aware stuff can know about it by checking *retlen */
++#ifdef ECC_DEBUG
++ printk("%s(%d): Millennium Plus ECC error (from=0x%x:\n",
++ __FILE__, __LINE__, (int)from);
++ printk(" syndrome= %02x:%02x:%02x:%02x:%02x:"
++ "%02x\n",
++ syndrome[0], syndrome[1], syndrome[2],
++ syndrome[3], syndrome[4], syndrome[5]);
++ printk(" eccbuf= %02x:%02x:%02x:%02x:%02x:"
++ "%02x\n",
++ eccbuf[0], eccbuf[1], eccbuf[2],
++ eccbuf[3], eccbuf[4], eccbuf[5]);
++#endif
++ ret = -EIO;
++ }
++ }
++
++#ifdef PSYCHO_DEBUG
++ printk("ECC DATA at %lx: %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n",
++ (long)from, eccbuf[0], eccbuf[1], eccbuf[2], eccbuf[3],
++ eccbuf[4], eccbuf[5]);
++#endif
++
++ /* disable the ECC engine */
++ WriteDOC(DOC_ECC_DIS, docptr , Mplus_ECCConf);
++ } else {
++ /* Read the data via the internal pipeline through CDSN IO
++ register, see Pipelined Read Operations 11.3 */
++ MemReadDOC(docptr, buf, len-2);
++ buf[len-2] = ReadDOC(docptr, Mplus_LastDataRead);
++ buf[len-1] = ReadDOC(docptr, Mplus_LastDataRead);
++ }
++
++ /* Disable flash internally */
++ WriteDOC(0, docptr, Mplus_FlashSelect);
++
++ return ret;
++}
++
++static int doc_write(struct mtd_info *mtd, loff_t to, size_t len,
++ size_t *retlen, const u_char *buf)
++{
++ char eccbuf[6];
++ return doc_write_ecc(mtd, to, len, retlen, buf, eccbuf, NULL);
++}
++
++static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len,
++ size_t *retlen, const u_char *buf, u_char *eccbuf,
++ struct nand_oobinfo *oobsel)
++{
++ int i, before, ret = 0;
++ loff_t fto;
++ volatile char dummy;
++ struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv;
++ unsigned long docptr = this->virtadr;
++ struct Nand *mychip = &this->chips[to >> (this->chipshift)];
++
++ /* Don't allow write past end of device */
++ if (to >= this->totlen)
++ return -EINVAL;
++
++ /* Don't allow writes which aren't exactly one block (512 bytes) */
++ if ((to & 0x1ff) || (len != 0x200))
++ return -EINVAL;
++
++ /* Determine position of OOB flags, before or after data */
++ before = (this->interleave && (to & 0x200));
++
++ DoC_CheckASIC(docptr);
++
++ /* Find the chip which is to be used and select it */
++ if (this->curfloor != mychip->floor) {
++ DoC_SelectFloor(docptr, mychip->floor);
++ DoC_SelectChip(docptr, mychip->chip);
++ } else if (this->curchip != mychip->chip) {
++ DoC_SelectChip(docptr, mychip->chip);
++ }
++ this->curfloor = mychip->floor;
++ this->curchip = mychip->chip;
++
++ /* Millennium Plus bus cycle sequence as per figure 2, section 2.4 */
++ WriteDOC(DOC_FLASH_CE, docptr, Mplus_FlashSelect);
++
++ /* Reset the chip, see Software Requirement 11.4 item 1. */
++ DoC_Command(docptr, NAND_CMD_RESET, 0);
++ DoC_WaitReady(docptr);
++
++ /* Set device to appropriate plane of flash */
++ fto = to;
++ WriteDOC(DoC_GetDataOffset(mtd, &fto), docptr, Mplus_FlashCmd);
++
++ /* On interleaved devices the flags for 2nd half 512 are before data */
++ if (eccbuf && before)
++ fto -= 2;
++
++ /* issue the Serial Data In command to initial the Page Program process */
++ DoC_Command(docptr, NAND_CMD_SEQIN, 0x00);
++ DoC_Address(this, 3, fto, 0x00, 0x00);
++
++ /* Disable the ECC engine */
++ WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf);
++
++ if (eccbuf) {
++ if (before) {
++ /* Write the block status BLOCK_USED (0x5555) */
++ WriteDOC(0x55, docptr, Mil_CDSN_IO);
++ WriteDOC(0x55, docptr, Mil_CDSN_IO);
++ }
++
++ /* init the ECC engine, see Reed-Solomon EDC/ECC 11.1 .*/
++ WriteDOC(DOC_ECC_EN | DOC_ECC_RW, docptr, Mplus_ECCConf);
++ }
++
++ MemWriteDOC(docptr, (unsigned char *) buf, len);
++
++ if (eccbuf) {
++ /* Write ECC data to flash, the ECC info is generated by
++ the DiskOnChip ECC logic see Reed-Solomon EDC/ECC 11.1 */
++ DoC_Delay(docptr, 3);
++
++ /* Read the ECC data through the DiskOnChip ECC logic */
++ for (i = 0; i < 6; i++)
++ eccbuf[i] = ReadDOC(docptr, Mplus_ECCSyndrome0 + i);
++
++ /* disable the ECC engine */
++ WriteDOC(DOC_ECC_DIS, docptr, Mplus_ECCConf);
++
++ /* Write the ECC data to flash */
++ MemWriteDOC(docptr, eccbuf, 6);
++
++ if (!before) {
++ /* Write the block status BLOCK_USED (0x5555) */
++ WriteDOC(0x55, docptr, Mil_CDSN_IO+6);
++ WriteDOC(0x55, docptr, Mil_CDSN_IO+7);
++ }
++
++#ifdef PSYCHO_DEBUG
++ printk("OOB data at %lx is %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n",
++ (long) to, eccbuf[0], eccbuf[1], eccbuf[2], eccbuf[3],
++ eccbuf[4], eccbuf[5]);
++#endif
++ }
++
++ WriteDOC(0x00, docptr, Mplus_WritePipeTerm);
++ WriteDOC(0x00, docptr, Mplus_WritePipeTerm);
++
++ /* Commit the Page Program command and wait for ready
++ see Software Requirement 11.4 item 1.*/
++ DoC_Command(docptr, NAND_CMD_PAGEPROG, 0x00);
++ DoC_WaitReady(docptr);
++
++ /* Read the status of the flash device through CDSN IO register
++ see Software Requirement 11.4 item 5.*/
++ DoC_Command(docptr, NAND_CMD_STATUS, 0);
++ dummy = ReadDOC(docptr, Mplus_ReadPipeInit);
++ dummy = ReadDOC(docptr, Mplus_ReadPipeInit);
++ DoC_Delay(docptr, 2);
++ if ((dummy = ReadDOC(docptr, Mplus_LastDataRead)) & 1) {
++ printk("MTD: Error 0x%x programming at 0x%x\n", dummy, (int)to);
++ /* Error in programming
++ FIXME: implement Bad Block Replacement (in nftl.c ??) */
++ *retlen = 0;
++ ret = -EIO;
++ }
++ dummy = ReadDOC(docptr, Mplus_LastDataRead);
++
++ /* Disable flash internally */
++ WriteDOC(0, docptr, Mplus_FlashSelect);
++
++ /* Let the caller know we completed it */
++ *retlen = len;
++
++ return ret;
++}
++
++static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
++ size_t *retlen, u_char *buf)
++{
++ loff_t fofs, base;
++ struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv;
++ unsigned long docptr = this->virtadr;
++ struct Nand *mychip = &this->chips[ofs >> this->chipshift];
++ size_t i, size, got, want;
++
++ DoC_CheckASIC(docptr);
++
++ /* Find the chip which is to be used and select it */
++ if (this->curfloor != mychip->floor) {
++ DoC_SelectFloor(docptr, mychip->floor);
++ DoC_SelectChip(docptr, mychip->chip);
++ } else if (this->curchip != mychip->chip) {
++ DoC_SelectChip(docptr, mychip->chip);
++ }
++ this->curfloor = mychip->floor;
++ this->curchip = mychip->chip;
++
++ /* Millennium Plus bus cycle sequence as per figure 2, section 2.4 */
++ WriteDOC((DOC_FLASH_CE | DOC_FLASH_WP), docptr, Mplus_FlashSelect);
++
++ /* disable the ECC engine */
++ WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf);
++ DoC_WaitReady(docptr);
++
++ /* Maximum of 16 bytes in the OOB region, so limit read to that */
++ if (len > 16)
++ len = 16;
++ got = 0;
++ want = len;
++
++ for (i = 0; ((i < 3) && (want > 0)); i++) {
++ /* Figure out which region we are accessing... */
++ fofs = ofs;
++ base = ofs & 0xf;
++ if (!this->interleave) {
++ DoC_Command(docptr, NAND_CMD_READOOB, 0);
++ size = 16 - base;
++ } else if (base < 6) {
++ DoC_Command(docptr, DoC_GetECCOffset(mtd, &fofs), 0);
++ size = 6 - base;
++ } else if (base < 8) {
++ DoC_Command(docptr, DoC_GetFlagsOffset(mtd, &fofs), 0);
++ size = 8 - base;
++ } else {
++ DoC_Command(docptr, DoC_GetHdrOffset(mtd, &fofs), 0);
++ size = 16 - base;
++ }
++ if (size > want)
++ size = want;
++
++ /* Issue read command */
++ DoC_Address(this, 3, fofs, 0, 0x00);
++ WriteDOC(0, docptr, Mplus_FlashControl);
++ DoC_WaitReady(docptr);
++
++ ReadDOC(docptr, Mplus_ReadPipeInit);
++ ReadDOC(docptr, Mplus_ReadPipeInit);
++ MemReadDOC(docptr, &buf[got], size - 2);
++ buf[got + size - 2] = ReadDOC(docptr, Mplus_LastDataRead);
++ buf[got + size - 1] = ReadDOC(docptr, Mplus_LastDataRead);
++
++ ofs += size;
++ got += size;
++ want -= size;
++ }
++
++ /* Disable flash internally */
++ WriteDOC(0, docptr, Mplus_FlashSelect);
++
++ *retlen = len;
++ return 0;
++}
++
++static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
++ size_t *retlen, const u_char *buf)
++{
++ volatile char dummy;
++ loff_t fofs, base;
++ struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv;
++ unsigned long docptr = this->virtadr;
++ struct Nand *mychip = &this->chips[ofs >> this->chipshift];
++ size_t i, size, got, want;
++ int ret = 0;
++
++ DoC_CheckASIC(docptr);
++
++ /* Find the chip which is to be used and select it */
++ if (this->curfloor != mychip->floor) {
++ DoC_SelectFloor(docptr, mychip->floor);
++ DoC_SelectChip(docptr, mychip->chip);
++ } else if (this->curchip != mychip->chip) {
++ DoC_SelectChip(docptr, mychip->chip);
++ }
++ this->curfloor = mychip->floor;
++ this->curchip = mychip->chip;
++
++ /* Millennium Plus bus cycle sequence as per figure 2, section 2.4 */
++ WriteDOC(DOC_FLASH_CE, docptr, Mplus_FlashSelect);
++
++
++ /* Maximum of 16 bytes in the OOB region, so limit write to that */
++ if (len > 16)
++ len = 16;
++ got = 0;
++ want = len;
++
++ for (i = 0; ((i < 3) && (want > 0)); i++) {
++ /* Reset the chip, see Software Requirement 11.4 item 1. */
++ DoC_Command(docptr, NAND_CMD_RESET, 0);
++ DoC_WaitReady(docptr);
++
++ /* Figure out which region we are accessing... */
++ fofs = ofs;
++ base = ofs & 0x0f;
++ if (!this->interleave) {
++ WriteDOC(NAND_CMD_READOOB, docptr, Mplus_FlashCmd);
++ size = 16 - base;
++ } else if (base < 6) {
++ WriteDOC(DoC_GetECCOffset(mtd, &fofs), docptr, Mplus_FlashCmd);
++ size = 6 - base;
++ } else if (base < 8) {
++ WriteDOC(DoC_GetFlagsOffset(mtd, &fofs), docptr, Mplus_FlashCmd);
++ size = 8 - base;
++ } else {
++ WriteDOC(DoC_GetHdrOffset(mtd, &fofs), docptr, Mplus_FlashCmd);
++ size = 16 - base;
++ }
++ if (size > want)
++ size = want;
++
++ /* Issue the Serial Data In command to initial the Page Program process */
++ DoC_Command(docptr, NAND_CMD_SEQIN, 0x00);
++ DoC_Address(this, 3, fofs, 0, 0x00);
++
++ /* Disable the ECC engine */
++ WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf);
++
++ /* Write the data via the internal pipeline through CDSN IO
++ register, see Pipelined Write Operations 11.2 */
++ MemWriteDOC(docptr, (unsigned char *) &buf[got], size);
++ WriteDOC(0x00, docptr, Mplus_WritePipeTerm);
++ WriteDOC(0x00, docptr, Mplus_WritePipeTerm);
++
++ /* Commit the Page Program command and wait for ready
++ see Software Requirement 11.4 item 1.*/
++ DoC_Command(docptr, NAND_CMD_PAGEPROG, 0x00);
++ DoC_WaitReady(docptr);
++
++ /* Read the status of the flash device through CDSN IO register
++ see Software Requirement 11.4 item 5.*/
++ DoC_Command(docptr, NAND_CMD_STATUS, 0x00);
++ dummy = ReadDOC(docptr, Mplus_ReadPipeInit);
++ dummy = ReadDOC(docptr, Mplus_ReadPipeInit);
++ DoC_Delay(docptr, 2);
++ if ((dummy = ReadDOC(docptr, Mplus_LastDataRead)) & 1) {
++ printk("MTD: Error 0x%x programming oob at 0x%x\n",
++ dummy, (int)ofs);
++ /* FIXME: implement Bad Block Replacement */
++ *retlen = 0;
++ ret = -EIO;
++ }
++ dummy = ReadDOC(docptr, Mplus_LastDataRead);
++
++ ofs += size;
++ got += size;
++ want -= size;
++ }
++
++ /* Disable flash internally */
++ WriteDOC(0, docptr, Mplus_FlashSelect);
++
++ *retlen = len;
++ return ret;
++}
++
++int doc_erase(struct mtd_info *mtd, struct erase_info *instr)
++{
++ volatile char dummy;
++ struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv;
++ __u32 ofs = instr->addr;
++ __u32 len = instr->len;
++ unsigned long docptr = this->virtadr;
++ struct Nand *mychip = &this->chips[ofs >> this->chipshift];
++
++ DoC_CheckASIC(docptr);
++
++ if (len != mtd->erasesize)
++ printk(KERN_WARNING "MTD: Erase not right size (%x != %x)n",
++ len, mtd->erasesize);
++
++ /* Find the chip which is to be used and select it */
++ if (this->curfloor != mychip->floor) {
++ DoC_SelectFloor(docptr, mychip->floor);
++ DoC_SelectChip(docptr, mychip->chip);
++ } else if (this->curchip != mychip->chip) {
++ DoC_SelectChip(docptr, mychip->chip);
++ }
++ this->curfloor = mychip->floor;
++ this->curchip = mychip->chip;
++
++ instr->state = MTD_ERASE_PENDING;
++
++ /* Millennium Plus bus cycle sequence as per figure 2, section 2.4 */
++ WriteDOC(DOC_FLASH_CE, docptr, Mplus_FlashSelect);
++
++ DoC_Command(docptr, NAND_CMD_RESET, 0x00);
++ DoC_WaitReady(docptr);
++
++ DoC_Command(docptr, NAND_CMD_ERASE1, 0);
++ DoC_Address(this, 2, ofs, 0, 0x00);
++ DoC_Command(docptr, NAND_CMD_ERASE2, 0);
++ DoC_WaitReady(docptr);
++ instr->state = MTD_ERASING;
++
++ /* Read the status of the flash device through CDSN IO register
++ see Software Requirement 11.4 item 5. */
++ DoC_Command(docptr, NAND_CMD_STATUS, 0);
++ dummy = ReadDOC(docptr, Mplus_ReadPipeInit);
++ dummy = ReadDOC(docptr, Mplus_ReadPipeInit);
++ if ((dummy = ReadDOC(docptr, Mplus_LastDataRead)) & 1) {
++ printk("MTD: Error 0x%x erasing at 0x%x\n", dummy, ofs);
++ /* FIXME: implement Bad Block Replacement (in nftl.c ??) */
++ instr->state = MTD_ERASE_FAILED;
++ } else {
++ instr->state = MTD_ERASE_DONE;
++ }
++ dummy = ReadDOC(docptr, Mplus_LastDataRead);
++
++ /* Disable flash internally */
++ WriteDOC(0, docptr, Mplus_FlashSelect);
++
++ if (instr->callback)
++ instr->callback(instr);
++
++ return 0;
++}
++
++/****************************************************************************
++ *
++ * Module stuff
++ *
++ ****************************************************************************/
++
++int __init init_doc2001plus(void)
++{
++ inter_module_register(im_name, THIS_MODULE, &DoCMilPlus_init);
++ return 0;
++}
++
++static void __exit cleanup_doc2001plus(void)
++{
++ struct mtd_info *mtd;
++ struct DiskOnChip *this;
++
++ while ((mtd=docmilpluslist)) {
++ this = (struct DiskOnChip *)mtd->priv;
++ docmilpluslist = this->nextdoc;
++
++ del_mtd_device(mtd);
++
++ iounmap((void *)this->virtadr);
++ kfree(this->chips);
++ kfree(mtd);
++ }
++ inter_module_unregister(im_name);
++}
++
++module_exit(cleanup_doc2001plus);
++module_init(init_doc2001plus);
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Greg Ungerer <gerg@snapgear.com> et al.");
++MODULE_DESCRIPTION("Driver for DiskOnChip Millennium Plus");
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/devices/docecc.c linux/drivers/mtd/devices/docecc.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/devices/docecc.c 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/devices/docecc.c 2004-11-17 18:17:59.019316960 +0100
+@@ -7,7 +7,7 @@
+ * Author: Fabrice Bellard (fabrice.bellard@netgem.com)
+ * Copyright (C) 2000 Netgem S.A.
+ *
+- * $Id$
++ * $Id$
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -519,6 +519,8 @@
+ return nb_errors;
+ }
+
++EXPORT_SYMBOL_GPL(doc_decode_ecc);
++
+ MODULE_LICENSE("GPL");
+ MODULE_AUTHOR("Fabrice Bellard <fabrice.bellard@netgem.com>");
+ MODULE_DESCRIPTION("ECC code for correcting errors detected by DiskOnChip 2000 and Millennium ECC hardware");
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/devices/docprobe.c linux/drivers/mtd/devices/docprobe.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/devices/docprobe.c 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/devices/docprobe.c 2004-11-17 18:17:59.020316808 +0100
+@@ -4,7 +4,7 @@
+ /* (C) 1999 Machine Vision Holdings, Inc. */
+ /* (C) 1999-2003 David Woodhouse <dwmw2@infradead.org> */
+
+-/* $Id$ */
++/* $Id$ */
+
+
+
+@@ -31,14 +31,12 @@
+ /* DOC_SINGLE_DRIVER:
+ Millennium driver has been merged into DOC2000 driver.
+
+- The newly-merged driver doesn't appear to work for writing. It's the
+- same with the DiskOnChip 2000 and the Millennium. If you have a
+- Millennium and you want write support to work, remove the definition
+- of DOC_SINGLE_DRIVER below to use the old doc2001-specific driver.
+-
+- Otherwise, it's left on in the hope that it'll annoy someone with
+- a Millennium enough that they go through and work out what the
+- difference is :)
++ The old Millennium-only driver has been retained just in case there
++ are problems with the new code. If the combined driver doesn't work
++ for you, you can try the old one by undefining DOC_SINGLE_DRIVER
++ below and also enabling it in your configuration. If this fixes the
++ problems, please send a report to the MTD mailing list at
++ <linux-mtd@lists.infradead.org>.
+ */
+ #define DOC_SINGLE_DRIVER
+
+@@ -47,18 +45,15 @@
+ #include <linux/module.h>
+ #include <asm/errno.h>
+ #include <asm/io.h>
+-#include <asm/uaccess.h>
+-#include <linux/miscdevice.h>
+-#include <linux/pci.h>
+ #include <linux/delay.h>
+ #include <linux/slab.h>
+-#include <linux/sched.h>
+ #include <linux/init.h>
+ #include <linux/types.h>
+
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/nand.h>
+ #include <linux/mtd/doc2000.h>
++#include <linux/mtd/compatmac.h>
+
+ /* Where to look for the devices? */
+ #ifndef CONFIG_MTD_DOCPROBE_ADDRESS
+@@ -92,17 +87,17 @@
+ 0xff000000,
+ #elif defined(CONFIG_MOMENCO_OCELOT_G) || defined (CONFIG_MOMENCO_OCELOT_C)
+ 0xff000000,
+-#else
++##else
+ #warning Unknown architecture for DiskOnChip. No default probe locations defined
+ #endif
+- 0 };
++ 0xffffffff };
+
+ /* doccheck: Probe a given memory window to see if there's a DiskOnChip present */
+
+ static inline int __init doccheck(unsigned long potential, unsigned long physadr)
+ {
+ unsigned long window=potential;
+- unsigned char tmp, ChipID;
++ unsigned char tmp, tmpb, tmpc, ChipID;
+ #ifndef DOC_PASSIVE_PROBE
+ unsigned char tmp2;
+ #endif
+@@ -140,26 +135,80 @@
+ window, DOCControl);
+ #endif /* !DOC_PASSIVE_PROBE */
+
++ /* We need to read the ChipID register four times. For some
++ newer DiskOnChip 2000 units, the first three reads will
++ return the DiskOnChip Millennium ident. Don't ask. */
+ ChipID = ReadDOC(window, ChipID);
+
+ switch (ChipID) {
+ case DOC_ChipID_Doc2k:
+ /* Check the TOGGLE bit in the ECC register */
+ tmp = ReadDOC(window, 2k_ECCStatus) & DOC_TOGGLE_BIT;
+- if ((ReadDOC(window, 2k_ECCStatus) & DOC_TOGGLE_BIT) != tmp)
++ tmpb = ReadDOC(window, 2k_ECCStatus) & DOC_TOGGLE_BIT;
++ tmpc = ReadDOC(window, 2k_ECCStatus) & DOC_TOGGLE_BIT;
++ if (tmp != tmpb && tmp == tmpc)
+ return ChipID;
+ break;
+
+ case DOC_ChipID_DocMil:
++ /* Check for the new 2000 with Millennium ASIC */
++ ReadDOC(window, ChipID);
++ ReadDOC(window, ChipID);
++ if (ReadDOC(window, ChipID) != DOC_ChipID_DocMil)
++ ChipID = DOC_ChipID_Doc2kTSOP;
++
+ /* Check the TOGGLE bit in the ECC register */
+ tmp = ReadDOC(window, ECCConf) & DOC_TOGGLE_BIT;
+- if ((ReadDOC(window, ECCConf) & DOC_TOGGLE_BIT) != tmp)
++ tmpb = ReadDOC(window, ECCConf) & DOC_TOGGLE_BIT;
++ tmpc = ReadDOC(window, ECCConf) & DOC_TOGGLE_BIT;
++ if (tmp != tmpb && tmp == tmpc)
++ return ChipID;
++ break;
++
++ case DOC_ChipID_DocMilPlus16:
++ case DOC_ChipID_DocMilPlus32:
++ case 0:
++ /* Possible Millennium+, need to do more checks */
++#ifndef DOC_PASSIVE_PROBE
++ /* Possibly release from power down mode */
++ for (tmp = 0; (tmp < 4); tmp++)
++ ReadDOC(window, Mplus_Power);
++
++ /* Reset the DiskOnChip ASIC */
++ tmp = DOC_MODE_RESET | DOC_MODE_MDWREN | DOC_MODE_RST_LAT |
++ DOC_MODE_BDECT;
++ WriteDOC(tmp, window, Mplus_DOCControl);
++ WriteDOC(~tmp, window, Mplus_CtrlConfirm);
++
++ mdelay(1);
++ /* Enable the DiskOnChip ASIC */
++ tmp = DOC_MODE_NORMAL | DOC_MODE_MDWREN | DOC_MODE_RST_LAT |
++ DOC_MODE_BDECT;
++ WriteDOC(tmp, window, Mplus_DOCControl);
++ WriteDOC(~tmp, window, Mplus_CtrlConfirm);
++ mdelay(1);
++#endif /* !DOC_PASSIVE_PROBE */
++
++ ChipID = ReadDOC(window, ChipID);
++
++ switch (ChipID) {
++ case DOC_ChipID_DocMilPlus16:
++ case DOC_ChipID_DocMilPlus32:
++ /* Check the TOGGLE bit in the toggle register */
++ tmp = ReadDOC(window, Mplus_Toggle) & DOC_TOGGLE_BIT;
++ tmpb = ReadDOC(window, Mplus_Toggle) & DOC_TOGGLE_BIT;
++ tmpc = ReadDOC(window, Mplus_Toggle) & DOC_TOGGLE_BIT;
++ if (tmp != tmpb && tmp == tmpc)
+ return ChipID;
++ default:
+ break;
++ }
++ /* FALL TRHU */
+
+ default:
+-#ifndef CONFIG_MTD_DOCPROBE_55AA
+- printk(KERN_WARNING "Possible DiskOnChip with unknown ChipID %2.2X found at 0x%lx\n",
++
++#ifdef CONFIG_MTD_DOCPROBE_55AA
++ printk(KERN_DEBUG "Possible DiskOnChip with unknown ChipID %2.2X found at 0x%lx\n",
+ ChipID, physadr);
+ #endif
+ #ifndef DOC_PASSIVE_PROBE
+@@ -200,6 +249,12 @@
+ return;
+
+ if ((ChipID = doccheck(docptr, physadr))) {
++ if (ChipID == DOC_ChipID_Doc2kTSOP) {
++ /* Remove this at your own peril. The hardware driver works but nothing prevents you from erasing bad blocks */
++ printk(KERN_NOTICE "Refusing to drive DiskOnChip 2000 TSOP until Bad Block Table is correctly supported by INFTL\n");
++ iounmap((void *)docptr);
++ return;
++ }
+ docfound = 1;
+ mtd = kmalloc(sizeof(struct DiskOnChip) + sizeof(struct mtd_info), GFP_KERNEL);
+
+@@ -221,6 +276,12 @@
+ sprintf(namebuf, "with ChipID %2.2X", ChipID);
+
+ switch(ChipID) {
++ case DOC_ChipID_Doc2kTSOP:
++ name="2000 TSOP";
++ im_funcname = "DoC2k_init";
++ im_modname = "doc2000";
++ break;
++
+ case DOC_ChipID_Doc2k:
+ name="2000";
+ im_funcname = "DoC2k_init";
+@@ -237,6 +298,13 @@
+ im_modname = "doc2001";
+ #endif /* DOC_SINGLE_DRIVER */
+ break;
++
++ case DOC_ChipID_DocMilPlus16:
++ case DOC_ChipID_DocMilPlus32:
++ name="MillenniumPlus";
++ im_funcname = "DoCMilPlus_init";
++ im_modname = "doc2001plus";
++ break;
+ }
+
+ if (im_funcname)
+@@ -248,6 +316,7 @@
+ return;
+ }
+ printk(KERN_NOTICE "Cannot find driver for DiskOnChip %s at 0x%lX\n", name, physadr);
++ kfree(mtd);
+ }
+ iounmap((void *)docptr);
+ }
+@@ -267,7 +336,7 @@
+ printk(KERN_INFO "Using configured DiskOnChip probe address 0x%lx\n", doc_config_location);
+ DoC_Probe(doc_config_location);
+ } else {
+- for (i=0; doc_locations[i]; i++) {
++ for (i=0; (doc_locations[i] != 0xffffffff); i++) {
+ DoC_Probe(doc_locations[i]);
+ }
+ }
+@@ -275,11 +344,7 @@
+ found, so the user knows we at least tried. */
+ if (!docfound)
+ printk(KERN_INFO "No recognised DiskOnChip devices found\n");
+- /* So it looks like we've been used and we get unloaded */
+- MOD_INC_USE_COUNT;
+- MOD_DEC_USE_COUNT;
+- return 0;
+-
++ return -EAGAIN;
+ }
+
+ module_init(init_doc);
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/devices/lart.c linux/drivers/mtd/devices/lart.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/devices/lart.c 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/devices/lart.c 2004-11-17 18:17:59.022316504 +0100
+@@ -2,7 +2,7 @@
+ /*
+ * MTD driver for the 28F160F3 Flash Memory (non-CFI) on LART.
+ *
+- * $Id$
++ * $Id$
+ *
+ * Author: Abraham vd Merwe <abraham@2d3d.co.za>
+ *
+@@ -584,45 +584,40 @@
+
+ static struct mtd_info mtd;
+
+-static struct mtd_erase_region_info erase_regions[] =
+-{
++static struct mtd_erase_region_info erase_regions[] = {
+ /* parameter blocks */
+ {
+- offset: 0x00000000,
+- erasesize: FLASH_BLOCKSIZE_PARAM,
+- numblocks: FLASH_NUMBLOCKS_16m_PARAM
++ .offset = 0x00000000,
++ .erasesize = FLASH_BLOCKSIZE_PARAM,
++ .numblocks = FLASH_NUMBLOCKS_16m_PARAM,
+ },
+ /* main blocks */
+ {
+- offset: FLASH_BLOCKSIZE_PARAM * FLASH_NUMBLOCKS_16m_PARAM,
+- erasesize: FLASH_BLOCKSIZE_MAIN,
+- numblocks: FLASH_NUMBLOCKS_16m_MAIN
++ .offset = FLASH_BLOCKSIZE_PARAM * FLASH_NUMBLOCKS_16m_PARAM,
++ .erasesize = FLASH_BLOCKSIZE_MAIN,
++ .numblocks = FLASH_NUMBLOCKS_16m_MAIN,
+ }
+ };
+
+ #ifdef HAVE_PARTITIONS
+-static struct mtd_partition lart_partitions[] =
+-{
++static struct mtd_partition lart_partitions[] = {
+ /* blob */
+ {
+- name: "blob",
+- offset: BLOB_START,
+- size: BLOB_LEN,
+- mask_flags: 0
++ .name = "blob",
++ .offset = BLOB_START,
++ .size = BLOB_LEN,
+ },
+ /* kernel */
+ {
+- name: "kernel",
+- offset: KERNEL_START, /* MTDPART_OFS_APPEND */
+- size: KERNEL_LEN,
+- mask_flags: 0
++ .name = "kernel",
++ .offset = KERNEL_START, /* MTDPART_OFS_APPEND */
++ .size = KERNEL_LEN,
+ },
+ /* initial ramdisk / file system */
+ {
+- name: "file system",
+- offset: INITRD_START, /* MTDPART_OFS_APPEND */
+- size: INITRD_LEN, /* MTDPART_SIZ_FULL */
+- mask_flags: 0
++ .name = "file system",
++ .offset = INITRD_START, /* MTDPART_OFS_APPEND */
++ .size = INITRD_LEN, /* MTDPART_SIZ_FULL */
+ }
+ };
+ #endif
+@@ -646,10 +641,10 @@
+ mtd.erasesize = FLASH_BLOCKSIZE_MAIN;
+ mtd.numeraseregions = NB_OF (erase_regions);
+ mtd.eraseregions = erase_regions;
+- mtd.module = THIS_MODULE;
+ mtd.erase = flash_erase;
+ mtd.read = flash_read;
+ mtd.write = flash_write;
++ mtd.owner = THIS_MODULE;
+
+ #ifdef LART_DEBUG
+ printk (KERN_DEBUG
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/devices/ms02-nv.c linux/drivers/mtd/devices/ms02-nv.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/devices/ms02-nv.c 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/devices/ms02-nv.c 2004-11-17 18:17:59.023316352 +0100
+@@ -6,7 +6,7 @@
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+- * $Id$
++ * $Id$
+ */
+
+ #include <linux/init.h>
+@@ -38,9 +38,9 @@
+
+ /*
+ * Addresses we probe for an MS02-NV at. Modules may be located
+- * at any 8MB boundary within a 0MB up to 112MB range or at any 32MB
+- * boundary within a 0MB up to 448MB range. We don't support a module
+- * at 0MB, though.
++ * at any 8MiB boundary within a 0MiB up to 112MiB range or at any 32MiB
++ * boundary within a 0MiB up to 448MiB range. We don't support a module
++ * at 0MiB, though.
+ */
+ static ulong ms02nv_addrs[] __initdata = {
+ 0x07000000, 0x06800000, 0x06000000, 0x05800000, 0x05000000,
+@@ -130,7 +130,7 @@
+
+ int ret = -ENODEV;
+
+- /* The module decodes 8MB of address space. */
++ /* The module decodes 8MiB of address space. */
+ mod_res = kmalloc(sizeof(*mod_res), GFP_KERNEL);
+ if (!mod_res)
+ return -ENOMEM;
+@@ -222,7 +222,7 @@
+ mtd->flags = MTD_CAP_RAM | MTD_XIP;
+ mtd->size = fixsize;
+ mtd->name = (char *)ms02nv_name;
+- mtd->module = THIS_MODULE;
++ mtd->owner = THIS_MODULE;
+ mtd->read = ms02nv_read;
+ mtd->write = ms02nv_write;
+
+@@ -233,7 +233,7 @@
+ goto err_out_csr_res;
+ }
+
+- printk(KERN_INFO "mtd%d: %s at 0x%08lx, size %uMB.\n",
++ printk(KERN_INFO "mtd%d: %s at 0x%08lx, size %uMiB.\n",
+ mtd->index, ms02nv_name, addr, size >> 20);
+
+ mp->next = root_ms02nv_mtd;
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/devices/ms02-nv.h linux/drivers/mtd/devices/ms02-nv.h
+--- linux-mips-2.4.24-pre2/drivers/mtd/devices/ms02-nv.h 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/devices/ms02-nv.h 2004-11-17 18:17:59.025316048 +0100
+@@ -9,6 +9,8 @@
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
++ *
++ * $Id$
+ */
+
+ #include <linux/ioport.h>
+@@ -39,8 +41,8 @@
+ * The diagnostic area defines two status words to be read by an
+ * operating system, a magic ID to distinguish a MS02-NV board from
+ * anything else and a status information providing results of tests
+- * as well as the size of SRAM available, which can be 1MB or 2MB
+- * (that's what the firmware handles; no idea if 2MB modules ever
++ * as well as the size of SRAM available, which can be 1MiB or 2MiB
++ * (that's what the firmware handles; no idea if 2MiB modules ever
+ * existed).
+ *
+ * The firmware only handles the MS02-NV board if installed in the
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/devices/mtdram.c linux/drivers/mtd/devices/mtdram.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/devices/mtdram.c 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/devices/mtdram.c 2004-11-17 18:17:59.040313768 +0100
+@@ -1,6 +1,6 @@
+ /*
+ * mtdram - a test mtd device
+- * $Id$
++ * $Id$
+ * Author: Alexander Larsson <alex@cendio.se>
+ *
+ * Copyright (c) 1999 Alexander Larsson <alex@cendio.se>
+@@ -13,6 +13,8 @@
+ #include <linux/module.h>
+ #include <linux/slab.h>
+ #include <linux/ioport.h>
++#include <linux/vmalloc.h>
++#include <linux/init.h>
+ #include <linux/mtd/compatmac.h>
+ #include <linux/mtd/mtd.h>
+
+@@ -136,7 +138,7 @@
+ mtd->erasesize = MTDRAM_ERASE_SIZE;
+ mtd->priv = mapped_address;
+
+- mtd->module = THIS_MODULE;
++ mtd->owner = THIS_MODULE;
+ mtd->erase = ram_erase;
+ mtd->point = ram_point;
+ mtd->unpoint = ram_unpoint;
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/devices/phram.c linux/drivers/mtd/devices/phram.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/devices/phram.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/mtd/devices/phram.c 2004-11-17 18:17:59.000000000 +0100
+@@ -0,0 +1,362 @@
++/**
++ *
++ * $Id$
++ *
++ * Copyright (c) Jochen Schaeuble <psionic@psionic.de>
++ * 07/2003 rewritten by Joern Engel <joern@wh.fh-wedel.de>
++ *
++ * DISCLAIMER: This driver makes use of Rusty's excellent module code,
++ * so it will not work for 2.4 without changes and it wont work for 2.4
++ * as a module without major changes. Oh well!
++ *
++ * Usage:
++ *
++ * one commend line parameter per device, each in the form:
++ * phram=<name>,<start>,<len>
++ * <name> may be up to 63 characters.
++ * <start> and <len> can be octal, decimal or hexadecimal. If followed
++ * by "k", "M" or "G", the numbers will be interpreted as kilo, mega or
++ * gigabytes.
++ *
++ */
++
++#include <asm/io.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/list.h>
++#include <linux/module.h>
++#include <linux/moduleparam.h>
++#include <linux/mtd/mtd.h>
++
++#define ERROR(fmt, args...) printk(KERN_ERR "phram: " fmt , ## args)
++
++struct phram_mtd_list {
++ struct list_head list;
++ struct mtd_info *mtdinfo;
++};
++
++static LIST_HEAD(phram_list);
++
++
++
++int phram_erase(struct mtd_info *mtd, struct erase_info *instr)
++{
++ u_char *start = (u_char *)mtd->priv;
++
++ if (instr->addr + instr->len > mtd->size)
++ return -EINVAL;
++
++ memset(start + instr->addr, 0xff, instr->len);
++
++ /* This'll catch a few races. Free the thing before returning :)
++ * I don't feel at all ashamed. This kind of thing is possible anyway
++ * with flash, but unlikely.
++ */
++
++ instr->state = MTD_ERASE_DONE;
++
++ if (instr->callback)
++ (*(instr->callback))(instr);
++ else
++ kfree(instr);
++
++ return 0;
++}
++
++int phram_point(struct mtd_info *mtd, loff_t from, size_t len,
++ size_t *retlen, u_char **mtdbuf)
++{
++ u_char *start = (u_char *)mtd->priv;
++
++ if (from + len > mtd->size)
++ return -EINVAL;
++
++ *mtdbuf = start + from;
++ *retlen = len;
++ return 0;
++}
++
++void phram_unpoint(struct mtd_info *mtd, u_char *addr, loff_t from, size_t len)
++{
++}
++
++int phram_read(struct mtd_info *mtd, loff_t from, size_t len,
++ size_t *retlen, u_char *buf)
++{
++ u_char *start = (u_char *)mtd->priv;
++
++ if (from + len > mtd->size)
++ return -EINVAL;
++
++ memcpy(buf, start + from, len);
++
++ *retlen = len;
++ return 0;
++}
++
++int phram_write(struct mtd_info *mtd, loff_t to, size_t len,
++ size_t *retlen, const u_char *buf)
++{
++ u_char *start = (u_char *)mtd->priv;
++
++ if (to + len > mtd->size)
++ return -EINVAL;
++
++ memcpy(start + to, buf, len);
++
++ *retlen = len;
++ return 0;
++}
++
++
++
++static void unregister_devices(void)
++{
++ struct phram_mtd_list *this;
++
++ list_for_each_entry(this, &phram_list, list) {
++ del_mtd_device(this->mtdinfo);
++ iounmap(this->mtdinfo->priv);
++ kfree(this->mtdinfo);
++ kfree(this);
++ }
++}
++
++static int register_device(char *name, unsigned long start, unsigned long len)
++{
++ struct phram_mtd_list *new;
++ int ret = -ENOMEM;
++
++ new = kmalloc(sizeof(*new), GFP_KERNEL);
++ if (!new)
++ goto out0;
++
++ new->mtdinfo = kmalloc(sizeof(struct mtd_info), GFP_KERNEL);
++ if (!new->mtdinfo)
++ goto out1;
++
++ memset(new->mtdinfo, 0, sizeof(struct mtd_info));
++
++ ret = -EIO;
++ new->mtdinfo->priv = ioremap(start, len);
++ if (!new->mtdinfo->priv) {
++ ERROR("ioremap failed\n");
++ goto out2;
++ }
++
++
++ new->mtdinfo->name = name;
++ new->mtdinfo->size = len;
++ new->mtdinfo->flags = MTD_CAP_RAM | MTD_ERASEABLE | MTD_VOLATILE;
++ new->mtdinfo->erase = phram_erase;
++ new->mtdinfo->point = phram_point;
++ new->mtdinfo->unpoint = phram_unpoint;
++ new->mtdinfo->read = phram_read;
++ new->mtdinfo->write = phram_write;
++ new->mtdinfo->owner = THIS_MODULE;
++ new->mtdinfo->type = MTD_RAM;
++ new->mtdinfo->erasesize = 0x0;
++
++ ret = -EAGAIN;
++ if (add_mtd_device(new->mtdinfo)) {
++ ERROR("Failed to register new device\n");
++ goto out3;
++ }
++
++ list_add_tail(&new->list, &phram_list);
++ return 0;
++
++out3:
++ iounmap(new->mtdinfo->priv);
++out2:
++ kfree(new->mtdinfo);
++out1:
++ kfree(new);
++out0:
++ return ret;
++}
++
++static int ustrtoul(const char *cp, char **endp, unsigned int base)
++{
++ unsigned long result = simple_strtoul(cp, endp, base);
++
++ switch (**endp) {
++ case 'G':
++ result *= 1024;
++ case 'M':
++ result *= 1024;
++ case 'k':
++ result *= 1024;
++ endp++;
++ }
++ return result;
++}
++
++static int parse_num32(uint32_t *num32, const char *token)
++{
++ char *endp;
++ unsigned long n;
++
++ n = ustrtoul(token, &endp, 0);
++ if (*endp)
++ return -EINVAL;
++
++ *num32 = n;
++ return 0;
++}
++
++static int parse_name(char **pname, const char *token)
++{
++ size_t len;
++ char *name;
++
++ len = strlen(token) + 1;
++ if (len > 64)
++ return -ENOSPC;
++
++ name = kmalloc(len, GFP_KERNEL);
++ if (!name)
++ return -ENOMEM;
++
++ strcpy(name, token);
++
++ *pname = name;
++ return 0;
++}
++
++#define parse_err(fmt, args...) do { \
++ ERROR(fmt , ## args); \
++ return 0; \
++} while (0)
++
++static int phram_setup(const char *val, struct kernel_param *kp)
++{
++ char buf[64+12+12], *str = buf;
++ char *token[3];
++ char *name;
++ uint32_t start;
++ uint32_t len;
++ int i, ret;
++
++ if (strnlen(val, sizeof(str)) >= sizeof(str))
++ parse_err("parameter too long\n");
++
++ strcpy(str, val);
++
++ for (i=0; i<3; i++)
++ token[i] = strsep(&str, ",");
++
++ if (str)
++ parse_err("too many arguments\n");
++
++ if (!token[2])
++ parse_err("not enough arguments\n");
++
++ ret = parse_name(&name, token[0]);
++ if (ret == -ENOMEM)
++ parse_err("out of memory\n");
++ if (ret == -ENOSPC)
++ parse_err("name too long\n");
++ if (ret)
++ return 0;
++
++ ret = parse_num32(&start, token[1]);
++ if (ret)
++ parse_err("illegal start address\n");
++
++ ret = parse_num32(&len, token[2]);
++ if (ret)
++ parse_err("illegal device length\n");
++
++ register_device(name, start, len);
++
++ return 0;
++}
++
++module_param_call(phram, phram_setup, NULL, NULL, 000);
++MODULE_PARM_DESC(phram, "Memory region to map. \"map=<name>,<start><length>\"");
++
++/*
++ * Just for compatibility with slram, this is horrible and should go someday.
++ */
++static int __init slram_setup(const char *val, struct kernel_param *kp)
++{
++ char buf[256], *str = buf;
++
++ if (!val || !val[0])
++ parse_err("no arguments to \"slram=\"\n");
++
++ if (strnlen(val, sizeof(str)) >= sizeof(str))
++ parse_err("parameter too long\n");
++
++ strcpy(str, val);
++
++ while (str) {
++ char *token[3];
++ char *name;
++ uint32_t start;
++ uint32_t len;
++ int i, ret;
++
++ for (i=0; i<3; i++) {
++ token[i] = strsep(&str, ",");
++ if (token[i])
++ continue;
++ parse_err("wrong number of arguments to \"slram=\"\n");
++ }
++
++ /* name */
++ ret = parse_name(&name, token[0]);
++ if (ret == -ENOMEM)
++ parse_err("of memory\n");
++ if (ret == -ENOSPC)
++ parse_err("too long\n");
++ if (ret)
++ return 1;
++
++ /* start */
++ ret = parse_num32(&start, token[1]);
++ if (ret)
++ parse_err("illegal start address\n");
++
++ /* len */
++ if (token[2][0] == '+')
++ ret = parse_num32(&len, token[2] + 1);
++ else
++ ret = parse_num32(&len, token[2]);
++
++ if (ret)
++ parse_err("illegal device length\n");
++
++ if (token[2][0] != '+') {
++ if (len < start)
++ parse_err("end < start\n");
++ len -= start;
++ }
++
++ register_device(name, start, len);
++ }
++ return 1;
++}
++
++module_param_call(slram, slram_setup, NULL, NULL, 000);
++MODULE_PARM_DESC(slram, "List of memory regions to map. \"map=<name>,<start><length/end>\"");
++
++
++int __init init_phram(void)
++{
++ printk(KERN_ERR "phram loaded\n");
++ return 0;
++}
++
++static void __exit cleanup_phram(void)
++{
++ unregister_devices();
++}
++
++module_init(init_phram);
++module_exit(cleanup_phram);
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Jörn Engel <joern@wh.fh-wedel.de>");
++MODULE_DESCRIPTION("MTD driver for physical RAM");
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/devices/pmc551.c linux/drivers/mtd/devices/pmc551.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/devices/pmc551.c 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/devices/pmc551.c 2004-11-17 18:17:59.043313312 +0100
+@@ -1,5 +1,5 @@
+ /*
+- * $Id$
++ * $Id$
+ *
+ * PMC551 PCI Mezzanine Ram Device
+ *
+@@ -681,11 +681,6 @@
+
+ printk(KERN_INFO PMC551_VERSION);
+
+- if(!pci_present()) {
+- printk(KERN_NOTICE "pmc551: PCI not enabled.\n");
+- return -ENODEV;
+- }
+-
+ /*
+ * PCU-bus chipset probe.
+ */
+@@ -787,10 +782,10 @@
+ mtd->write = pmc551_write;
+ mtd->point = pmc551_point;
+ mtd->unpoint = pmc551_unpoint;
+- mtd->module = THIS_MODULE;
+ mtd->type = MTD_RAM;
+ mtd->name = "PMC551 RAM board";
+ mtd->erasesize = 0x10000;
++ mtd->owner = THIS_MODULE;
+
+ if (add_mtd_device(mtd)) {
+ printk(KERN_NOTICE "pmc551: Failed to register new device\n");
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/devices/slram.c linux/drivers/mtd/devices/slram.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/devices/slram.c 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/devices/slram.c 2004-11-17 18:17:59.044313160 +0100
+@@ -1,6 +1,6 @@
+ /*======================================================================
+
+- $Id$
++ $Id$
+
+ This driver provides a method to access memory not used by the kernel
+ itself (i.e. if the kernel commandline mem=xxx is used). To actually
+@@ -199,7 +199,7 @@
+ (*curmtd)->mtdinfo->unpoint = slram_unpoint;
+ (*curmtd)->mtdinfo->read = slram_read;
+ (*curmtd)->mtdinfo->write = slram_write;
+- (*curmtd)->mtdinfo->module = THIS_MODULE;
++ (*curmtd)->mtdinfo->owner = THIS_MODULE;
+ (*curmtd)->mtdinfo->type = MTD_RAM;
+ (*curmtd)->mtdinfo->erasesize = 0x0;
+
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/ftl.c linux/drivers/mtd/ftl.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/ftl.c 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/ftl.c 2004-11-17 18:17:58.837344624 +0100
+@@ -1,5 +1,5 @@
+ /* This version ported to the Linux-MTD system by dwmw2@infradead.org
+- * $Id$
++ * $Id$
+ *
+ * Fixes: Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+ * - fixes some leaks on failure in build_maps and ftl_notify_add, cleanups
+@@ -55,8 +55,8 @@
+ contact M-Systems (http://www.m-sys.com) directly.
+
+ ======================================================================*/
++#include <linux/mtd/blktrans.h>
+ #include <linux/module.h>
+-#include <linux/mtd/compatmac.h>
+ #include <linux/mtd/mtd.h>
+ /*#define PSYCHO_DEBUG */
+
+@@ -68,43 +68,13 @@
+ #include <linux/timer.h>
+ #include <linux/major.h>
+ #include <linux/fs.h>
+-#include <linux/ioctl.h>
++#include <linux/init.h>
+ #include <linux/hdreg.h>
+-
+-#if (LINUX_VERSION_CODE >= 0x20100)
+ #include <linux/vmalloc.h>
+-#endif
+-#if (LINUX_VERSION_CODE >= 0x20303)
+ #include <linux/blkpg.h>
+-#endif
++#include <asm/uaccess.h>
+
+ #include <linux/mtd/ftl.h>
+-/*====================================================================*/
+-/* Stuff which really ought to be in compatmac.h */
+-
+-#if (LINUX_VERSION_CODE < 0x20328)
+-#define register_disk(dev, drive, minors, ops, size) \
+- do { (dev)->part[(drive)*(minors)].nr_sects = size; \
+- if (size == 0) (dev)->part[(drive)*(minors)].start_sect = -1; \
+- resetup_one_dev(dev, drive); } while (0)
+-#endif
+-
+-#if (LINUX_VERSION_CODE < 0x20320)
+-#define BLK_DEFAULT_QUEUE(n) blk_dev[n].request_fn
+-#define blk_init_queue(q, req) q = (req)
+-#define blk_cleanup_queue(q) q = NULL
+-#define request_arg_t void
+-#else
+-#define request_arg_t request_queue_t *q
+-#endif
+-
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,14)
+-#define BLK_INC_USE_COUNT MOD_INC_USE_COUNT
+-#define BLK_DEC_USE_COUNT MOD_DEC_USE_COUNT
+-#else
+-#define BLK_INC_USE_COUNT do {} while(0)
+-#define BLK_DEC_USE_COUNT do {} while(0)
+-#endif
+
+ /*====================================================================*/
+
+@@ -119,19 +89,6 @@
+ #define FTL_MAJOR 44
+ #endif
+
+-/* Funky stuff for setting up a block device */
+-#define MAJOR_NR FTL_MAJOR
+-#define DEVICE_NAME "ftl"
+-#define DEVICE_REQUEST do_ftl_request
+-#define DEVICE_ON(device)
+-#define DEVICE_OFF(device)
+-
+-#define DEVICE_NR(minor) ((minor)>>5)
+-#define REGION_NR(minor) (((minor)>>3)&3)
+-#define PART_NR(minor) ((minor)&7)
+-#define MINOR_NR(dev,reg,part) (((dev)<<5)+((reg)<<3)+(part))
+-
+-#include <linux/blk.h>
+
+ /*====================================================================*/
+
+@@ -142,8 +99,7 @@
+ #define MAX_REGION 4
+
+ /* Maximum number of partitions in an FTL region */
+-#define PART_BITS 3
+-#define MAX_PART 8
++#define PART_BITS 4
+
+ /* Maximum number of outstanding erase requests per socket */
+ #define MAX_ERASE 8
+@@ -154,7 +110,7 @@
+
+ /* Each memory region corresponds to a minor device */
+ typedef struct partition_t {
+- struct mtd_info *mtd;
++ struct mtd_blktrans_dev mbd;
+ u_int32_t state;
+ u_int32_t *VirtualBlockMap;
+ u_int32_t *VirtualPageMap;
+@@ -179,21 +135,10 @@
+ region_info_t region;
+ memory_handle_t handle;
+ #endif
+- atomic_t open;
+ } partition_t;
+
+-partition_t *myparts[MAX_MTD_DEVICES];
+-
+-static void ftl_notify_add(struct mtd_info *mtd);
+-static void ftl_notify_remove(struct mtd_info *mtd);
+-
+ void ftl_freepart(partition_t *part);
+
+-static struct mtd_notifier ftl_notifier = {
+- add: ftl_notify_add,
+- remove: ftl_notify_remove,
+-};
+-
+ /* Partition state flags */
+ #define FTL_FORMATTED 0x01
+
+@@ -204,51 +149,11 @@
+ #define XFER_PREPARED 0x03
+ #define XFER_FAILED 0x04
+
+-static struct hd_struct ftl_hd[MINOR_NR(MAX_DEV, 0, 0)];
+-static int ftl_sizes[MINOR_NR(MAX_DEV, 0, 0)];
+-static int ftl_blocksizes[MINOR_NR(MAX_DEV, 0, 0)];
+-
+-static struct gendisk ftl_gendisk = {
+- major: FTL_MAJOR,
+- major_name: "ftl",
+- minor_shift: PART_BITS,
+- max_p: MAX_PART,
+-#if (LINUX_VERSION_CODE < 0x20328)
+- max_nr: MAX_DEV*MAX_PART,
+-#endif
+- part: ftl_hd,
+- sizes: ftl_sizes,
+-};
+-
+ /*====================================================================*/
+
+-static int ftl_ioctl(struct inode *inode, struct file *file,
+- u_int cmd, u_long arg);
+-static int ftl_open(struct inode *inode, struct file *file);
+-static release_t ftl_close(struct inode *inode, struct file *file);
+-static int ftl_reread_partitions(int minor);
+
+ static void ftl_erase_callback(struct erase_info *done);
+
+-#if LINUX_VERSION_CODE < 0x20326
+-static struct file_operations ftl_blk_fops = {
+- open: ftl_open,
+- release: ftl_close,
+- ioctl: ftl_ioctl,
+- read: block_read,
+- write: block_write,
+- fsync: block_fsync
+-};
+-#else
+-static struct block_device_operations ftl_blk_fops = {
+-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,14)
+- owner: THIS_MODULE,
+-#endif
+- open: ftl_open,
+- release: ftl_close,
+- ioctl: ftl_ioctl,
+-};
+-#endif
+
+ /*======================================================================
+
+@@ -264,13 +169,13 @@
+ loff_t offset, max_offset;
+ int ret;
+ part->header.FormattedSize = 0;
+- max_offset = (0x100000<part->mtd->size)?0x100000:part->mtd->size;
++ max_offset = (0x100000<part->mbd.mtd->size)?0x100000:part->mbd.mtd->size;
+ /* Search first megabyte for a valid FTL header */
+ for (offset = 0;
+ (offset + sizeof(header)) < max_offset;
+- offset += part->mtd->erasesize ? : 0x2000) {
++ offset += part->mbd.mtd->erasesize ? : 0x2000) {
+
+- ret = part->mtd->read(part->mtd, offset, sizeof(header), &ret,
++ ret = part->mbd.mtd->read(part->mbd.mtd, offset, sizeof(header), &ret,
+ (unsigned char *)&header);
+
+ if (ret)
+@@ -283,15 +188,15 @@
+ printk(KERN_NOTICE "ftl_cs: FTL header not found.\n");
+ return -ENOENT;
+ }
+- if ((le16_to_cpu(header.NumEraseUnits) > 65536) || header.BlockSize != 9 ||
++ if (header.BlockSize != 9 ||
+ (header.EraseUnitSize < 10) || (header.EraseUnitSize > 31) ||
+ (header.NumTransferUnits >= le16_to_cpu(header.NumEraseUnits))) {
+ printk(KERN_NOTICE "ftl_cs: FTL header corrupt!\n");
+ return -1;
+ }
+- if ((1 << header.EraseUnitSize) != part->mtd->erasesize) {
++ if ((1 << header.EraseUnitSize) != part->mbd.mtd->erasesize) {
+ printk(KERN_NOTICE "ftl: FTL EraseUnitSize %x != MTD erasesize %x\n",
+- 1 << header.EraseUnitSize,part->mtd->erasesize);
++ 1 << header.EraseUnitSize,part->mbd.mtd->erasesize);
+ return -1;
+ }
+ part->header = header;
+@@ -326,7 +231,7 @@
+ for (i = 0; i < le16_to_cpu(part->header.NumEraseUnits); i++) {
+ offset = ((i + le16_to_cpu(part->header.FirstPhysicalEUN))
+ << part->header.EraseUnitSize);
+- ret = part->mtd->read(part->mtd, offset, sizeof(header), &retval,
++ ret = part->mbd.mtd->read(part->mbd.mtd, offset, sizeof(header), &retval,
+ (unsigned char *)&header);
+
+ if (ret)
+@@ -391,7 +296,7 @@
+ part->EUNInfo[i].Deleted = 0;
+ offset = part->EUNInfo[i].Offset + le32_to_cpu(header.BAMOffset);
+
+- ret = part->mtd->read(part->mtd, offset,
++ ret = part->mbd.mtd->read(part->mbd.mtd, offset,
+ part->BlocksPerUnit * sizeof(u_int32_t), &retval,
+ (unsigned char *)part->bam_cache);
+
+@@ -456,7 +361,7 @@
+ erase->len = 1 << part->header.EraseUnitSize;
+ erase->priv = (u_long)part;
+
+- ret = part->mtd->erase(part->mtd, erase);
++ ret = part->mbd.mtd->erase(part->mbd.mtd, erase);
+
+ if (!ret)
+ xfer->EraseCount++;
+@@ -523,7 +428,7 @@
+ header.LogicalEUN = cpu_to_le16(0xffff);
+ header.EraseCount = cpu_to_le32(xfer->EraseCount);
+
+- ret = part->mtd->write(part->mtd, xfer->Offset, sizeof(header),
++ ret = part->mbd.mtd->write(part->mbd.mtd, xfer->Offset, sizeof(header),
+ &retlen, (u_char *)&header);
+
+ if (ret) {
+@@ -539,7 +444,7 @@
+
+ for (i = 0; i < nbam; i++, offset += sizeof(u_int32_t)) {
+
+- ret = part->mtd->write(part->mtd, offset, sizeof(u_int32_t),
++ ret = part->mbd.mtd->write(part->mbd.mtd, offset, sizeof(u_int32_t),
+ &retlen, (u_char *)&ctl);
+
+ if (ret)
+@@ -586,7 +491,7 @@
+
+ offset = eun->Offset + le32_to_cpu(part->header.BAMOffset);
+
+- ret = part->mtd->read(part->mtd, offset,
++ ret = part->mbd.mtd->read(part->mbd.mtd, offset,
+ part->BlocksPerUnit * sizeof(u_int32_t),
+ &retlen, (u_char *) (part->bam_cache));
+
+@@ -604,7 +509,7 @@
+ offset = xfer->Offset + 20; /* Bad! */
+ unit = cpu_to_le16(0x7fff);
+
+- ret = part->mtd->write(part->mtd, offset, sizeof(u_int16_t),
++ ret = part->mbd.mtd->write(part->mbd.mtd, offset, sizeof(u_int16_t),
+ &retlen, (u_char *) &unit);
+
+ if (ret) {
+@@ -624,7 +529,7 @@
+ break;
+ case BLOCK_DATA:
+ case BLOCK_REPLACEMENT:
+- ret = part->mtd->read(part->mtd, src, SECTOR_SIZE,
++ ret = part->mbd.mtd->read(part->mbd.mtd, src, SECTOR_SIZE,
+ &retlen, (u_char *) buf);
+ if (ret) {
+ printk(KERN_WARNING "ftl: Error reading old xfer unit in copy_erase_unit\n");
+@@ -632,7 +537,7 @@
+ }
+
+
+- ret = part->mtd->write(part->mtd, dest, SECTOR_SIZE,
++ ret = part->mbd.mtd->write(part->mbd.mtd, dest, SECTOR_SIZE,
+ &retlen, (u_char *) buf);
+ if (ret) {
+ printk(KERN_WARNING "ftl: Error writing new xfer unit in copy_erase_unit\n");
+@@ -651,7 +556,7 @@
+ }
+
+ /* Write the BAM to the transfer unit */
+- ret = part->mtd->write(part->mtd, xfer->Offset + le32_to_cpu(part->header.BAMOffset),
++ ret = part->mbd.mtd->write(part->mbd.mtd, xfer->Offset + le32_to_cpu(part->header.BAMOffset),
+ part->BlocksPerUnit * sizeof(int32_t), &retlen,
+ (u_char *)part->bam_cache);
+ if (ret) {
+@@ -661,7 +566,7 @@
+
+
+ /* All clear? Then update the LogicalEUN again */
+- ret = part->mtd->write(part->mtd, xfer->Offset + 20, sizeof(u_int16_t),
++ ret = part->mbd.mtd->write(part->mbd.mtd, xfer->Offset + 20, sizeof(u_int16_t),
+ &retlen, (u_char *)&srcunitswap);
+
+ if (ret) {
+@@ -749,8 +654,8 @@
+ if (queued) {
+ DEBUG(1, "ftl_cs: waiting for transfer "
+ "unit to be prepared...\n");
+- if (part->mtd->sync)
+- part->mtd->sync(part->mtd);
++ if (part->mbd.mtd->sync)
++ part->mbd.mtd->sync(part->mbd.mtd);
+ } else {
+ static int ne = 0;
+ if (++ne < 5)
+@@ -848,7 +753,7 @@
+ /* Invalidate cache */
+ part->bam_index = 0xffff;
+
+- ret = part->mtd->read(part->mtd,
++ ret = part->mbd.mtd->read(part->mbd.mtd,
+ part->EUNInfo[eun].Offset + le32_to_cpu(part->header.BAMOffset),
+ part->BlocksPerUnit * sizeof(u_int32_t),
+ &retlen, (u_char *) (part->bam_cache));
+@@ -877,78 +782,6 @@
+
+ } /* find_free */
+
+-/*======================================================================
+-
+- This gets a memory handle for the region corresponding to the
+- minor device number.
+-
+-======================================================================*/
+-
+-static int ftl_open(struct inode *inode, struct file *file)
+-{
+- int minor = MINOR(inode->i_rdev);
+- partition_t *partition;
+-
+- if (minor>>4 >= MAX_MTD_DEVICES)
+- return -ENODEV;
+-
+- partition = myparts[minor>>4];
+-
+- if (!partition)
+- return -ENODEV;
+-
+- if (partition->state != FTL_FORMATTED)
+- return -ENXIO;
+-
+- if (ftl_gendisk.part[minor].nr_sects == 0)
+- return -ENXIO;
+-
+- BLK_INC_USE_COUNT;
+-
+- if (!get_mtd_device(partition->mtd, -1)) {
+- BLK_DEC_USE_COUNT;
+- return -ENXIO;
+- }
+-
+- if ((file->f_mode & 2) && !(partition->mtd->flags & MTD_CLEAR_BITS) ) {
+- put_mtd_device(partition->mtd);
+- BLK_DEC_USE_COUNT;
+- return -EROFS;
+- }
+-
+- DEBUG(0, "ftl_cs: ftl_open(%d)\n", minor);
+-
+- atomic_inc(&partition->open);
+-
+- return 0;
+-}
+-
+-/*====================================================================*/
+-
+-static release_t ftl_close(struct inode *inode, struct file *file)
+-{
+- int minor = MINOR(inode->i_rdev);
+- partition_t *part = myparts[minor >> 4];
+- int i;
+-
+- DEBUG(0, "ftl_cs: ftl_close(%d)\n", minor);
+-
+- /* Wait for any pending erase operations to complete */
+- if (part->mtd->sync)
+- part->mtd->sync(part->mtd);
+-
+- for (i = 0; i < part->header.NumTransferUnits; i++) {
+- if (part->XferInfo[i].state == XFER_ERASED)
+- prepare_xfer(part, i);
+- }
+-
+- atomic_dec(&part->open);
+-
+- put_mtd_device(part->mtd);
+- BLK_DEC_USE_COUNT;
+- release_return(0);
+-} /* ftl_close */
+-
+
+ /*======================================================================
+
+@@ -983,7 +816,7 @@
+ else {
+ offset = (part->EUNInfo[log_addr / bsize].Offset
+ + (log_addr % bsize));
+- ret = part->mtd->read(part->mtd, offset, SECTOR_SIZE,
++ ret = part->mbd.mtd->read(part->mbd.mtd, offset, SECTOR_SIZE,
+ &retlen, (u_char *) buffer);
+
+ if (ret) {
+@@ -1022,7 +855,7 @@
+ le32_to_cpu(part->header.BAMOffset));
+
+ #ifdef PSYCHO_DEBUG
+- ret = part->mtd->read(part->mtd, offset, sizeof(u_int32_t),
++ ret = part->mbd.mtd->read(part->mbd.mtd, offset, sizeof(u_int32_t),
+ &retlen, (u_char *)&old_addr);
+ if (ret) {
+ printk(KERN_WARNING"ftl: Error reading old_addr in set_bam_entry: %d\n",ret);
+@@ -1059,7 +892,7 @@
+ #endif
+ part->bam_cache[blk] = le_virt_addr;
+ }
+- ret = part->mtd->write(part->mtd, offset, sizeof(u_int32_t),
++ ret = part->mbd.mtd->write(part->mbd.mtd, offset, sizeof(u_int32_t),
+ &retlen, (u_char *)&le_virt_addr);
+
+ if (ret) {
+@@ -1119,7 +952,7 @@
+ part->EUNInfo[part->bam_index].Deleted++;
+ offset = (part->EUNInfo[part->bam_index].Offset +
+ blk * SECTOR_SIZE);
+- ret = part->mtd->write(part->mtd, offset, SECTOR_SIZE, &retlen,
++ ret = part->mbd.mtd->write(part->mbd.mtd, offset, SECTOR_SIZE, &retlen,
+ buffer);
+
+ if (ret) {
+@@ -1151,164 +984,32 @@
+ return 0;
+ } /* ftl_write */
+
+-/*======================================================================
+-
+- IOCTL calls for getting device parameters.
+-
+-======================================================================*/
+-
+-static int ftl_ioctl(struct inode *inode, struct file *file,
+- u_int cmd, u_long arg)
++static int ftl_getgeo(struct mtd_blktrans_dev *dev, struct hd_geometry *geo)
+ {
+- struct hd_geometry *geo = (struct hd_geometry *)arg;
+- int ret = 0, minor = MINOR(inode->i_rdev);
+- partition_t *part= myparts[minor >> 4];
++ partition_t *part = (void *)dev;
+ u_long sect;
+
+- if (!part)
+- return -ENODEV; /* How? */
+-
+- switch (cmd) {
+- case HDIO_GETGEO:
+- ret = verify_area(VERIFY_WRITE, (long *)arg, sizeof(*geo));
+- if (ret) return ret;
+- /* Sort of arbitrary: round size down to 4K boundary */
++ /* Sort of arbitrary: round size down to 4KiB boundary */
+ sect = le32_to_cpu(part->header.FormattedSize)/SECTOR_SIZE;
+- put_user(1, (char *)&geo->heads);
+- put_user(8, (char *)&geo->sectors);
+- put_user((sect>>3), (short *)&geo->cylinders);
+- put_user(ftl_hd[minor].start_sect, (u_long *)&geo->start);
+- break;
+- case BLKGETSIZE:
+- ret = put_user(ftl_hd[minor].nr_sects, (unsigned long *)arg);
+- break;
+-#ifdef BLKGETSIZE64
+- case BLKGETSIZE64:
+- ret = put_user((u64)ftl_hd[minor].nr_sects << 9, (u64 *)arg);
+- break;
+-#endif
+- case BLKRRPART:
+- ret = ftl_reread_partitions(minor);
+- break;
+-#if (LINUX_VERSION_CODE < 0x20303)
+- case BLKFLSBUF:
+-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
+- if (!capable(CAP_SYS_ADMIN)) return -EACCES;
+-#endif
+- fsync_dev(inode->i_rdev);
+- invalidate_buffers(inode->i_rdev);
+- break;
+- RO_IOCTLS(inode->i_rdev, arg);
+-#else
+- case BLKROSET:
+- case BLKROGET:
+- case BLKFLSBUF:
+- ret = blk_ioctl(inode->i_rdev, cmd, arg);
+- break;
+-#endif
+- default:
+- ret = -EINVAL;
+- }
+-
+- return ret;
+-} /* ftl_ioctl */
+-
+-/*======================================================================
+
+- Handler for block device requests
++ geo->heads = 1;
++ geo->sectors = 8;
++ geo->cylinders = sect >> 3;
+
+-======================================================================*/
+-
+-static int ftl_reread_partitions(int minor)
+-{
+- partition_t *part = myparts[minor >> 4];
+- int i, whole;
+-
+- DEBUG(0, "ftl_cs: ftl_reread_partition(%d)\n", minor);
+- if ((atomic_read(&part->open) > 1)) {
+- return -EBUSY;
+- }
+- whole = minor & ~(MAX_PART-1);
+-
+- i = MAX_PART - 1;
+- while (i-- > 0) {
+- if (ftl_hd[whole+i].nr_sects > 0) {
+- kdev_t rdev = MKDEV(FTL_MAJOR, whole+i);
+-
+- invalidate_device(rdev, 1);
+- }
+- ftl_hd[whole+i].start_sect = 0;
+- ftl_hd[whole+i].nr_sects = 0;
+- }
+-
+- scan_header(part);
+-
+- register_disk(&ftl_gendisk, whole >> PART_BITS, MAX_PART,
+- &ftl_blk_fops, le32_to_cpu(part->header.FormattedSize)/SECTOR_SIZE);
+-
+-#ifdef PCMCIA_DEBUG
+- for (i = 0; i < MAX_PART; i++) {
+- if (ftl_hd[whole+i].nr_sects > 0)
+- printk(KERN_INFO " %d: start %ld size %ld\n", i,
+- ftl_hd[whole+i].start_sect,
+- ftl_hd[whole+i].nr_sects);
+- }
+-#endif
+ return 0;
+ }
+
+-/*======================================================================
+-
+- Handler for block device requests
+-
+-======================================================================*/
+-
+-static void do_ftl_request(request_arg_t)
++static int ftl_readsect(struct mtd_blktrans_dev *dev,
++ unsigned long block, char *buf)
+ {
+- int ret, minor;
+- partition_t *part;
+-
+- do {
+- // sti();
+- INIT_REQUEST;
+-
+- minor = MINOR(CURRENT->rq_dev);
+-
+- part = myparts[minor >> 4];
+- if (part) {
+- ret = 0;
+-
+- switch (CURRENT->cmd) {
+- case READ:
+- ret = ftl_read(part, CURRENT->buffer,
+- CURRENT->sector+ftl_hd[minor].start_sect,
+- CURRENT->current_nr_sectors);
+- if (ret) printk("ftl_read returned %d\n", ret);
+- break;
+-
+- case WRITE:
+- ret = ftl_write(part, CURRENT->buffer,
+- CURRENT->sector+ftl_hd[minor].start_sect,
+- CURRENT->current_nr_sectors);
+- if (ret) printk("ftl_write returned %d\n", ret);
+- break;
+-
+- default:
+- panic("ftl_cs: unknown block command!\n");
+-
+- }
+- } else {
+- ret = 1;
+- printk("NULL part in ftl_request\n");
+- }
+-
+- if (!ret) {
+- CURRENT->sector += CURRENT->current_nr_sectors;
+- }
++ return ftl_read((void *)dev, buf, block, 1);
++}
+
+- end_request((ret == 0) ? 1 : 0);
+- } while (1);
+-} /* do_ftl_request */
++static int ftl_writesect(struct mtd_blktrans_dev *dev,
++ unsigned long block, char *buf)
++{
++ return ftl_write((void *)dev, buf, block, 1);
++}
+
+ /*====================================================================*/
+
+@@ -1337,19 +1038,9 @@
+
+ } /* ftl_freepart */
+
+-static void ftl_notify_add(struct mtd_info *mtd)
++static void ftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
+ {
+ partition_t *partition;
+- int device;
+-
+- for (device=0; device < MAX_MTD_DEVICES && myparts[device]; device++)
+- ;
+-
+- if (device == MAX_MTD_DEVICES) {
+- printk(KERN_NOTICE "Maximum number of FTL partitions reached\n"
+- "Not scanning <%s>\n", mtd->name);
+- return;
+- }
+
+ partition = kmalloc(sizeof(partition_t), GFP_KERNEL);
+
+@@ -1361,92 +1052,55 @@
+
+ memset(partition, 0, sizeof(partition_t));
+
+- partition->mtd = mtd;
++ partition->mbd.mtd = mtd;
+
+ if ((scan_header(partition) == 0) &&
+ (build_maps(partition) == 0)) {
+
+ partition->state = FTL_FORMATTED;
+- atomic_set(&partition->open, 0);
+- myparts[device] = partition;
+- ftl_reread_partitions(device << 4);
+ #ifdef PCMCIA_DEBUG
+- printk(KERN_INFO "ftl_cs: opening %d kb FTL partition\n",
++ printk(KERN_INFO "ftl_cs: opening %d KiB FTL partition\n",
+ le32_to_cpu(partition->header.FormattedSize) >> 10);
+ #endif
++ partition->mbd.size = le32_to_cpu(partition->header.FormattedSize) >> 9;
++ partition->mbd.blksize = SECTOR_SIZE;
++ partition->mbd.tr = tr;
++ partition->mbd.devnum = -1;
++ if (add_mtd_blktrans_dev((void *)partition))
++ kfree(partition);
++
+ } else
+ kfree(partition);
+ }
+
+-static void ftl_notify_remove(struct mtd_info *mtd)
++static void ftl_remove_dev(struct mtd_blktrans_dev *dev)
+ {
+- int i,j;
+-
+- /* Q: What happens if you try to remove a device which has
+- * a currently-open FTL partition on it?
+- *
+- * A: You don't. The ftl_open routine is responsible for
+- * increasing the use count of the driver module which
+- * it uses.
+- */
+-
+- /* That's the theory, anyway :) */
+-
+- for (i=0; i< MAX_MTD_DEVICES; i++)
+- if (myparts[i] && myparts[i]->mtd == mtd) {
+-
+- if (myparts[i]->state == FTL_FORMATTED)
+- ftl_freepart(myparts[i]);
+-
+- myparts[i]->state = 0;
+- for (j=0; j<16; j++) {
+- ftl_gendisk.part[j].nr_sects=0;
+- ftl_gendisk.part[j].start_sect=0;
+- }
+- kfree(myparts[i]);
+- myparts[i] = NULL;
+- }
++ del_mtd_blktrans_dev(dev);
++ kfree(dev);
+ }
+
++struct mtd_blktrans_ops ftl_tr = {
++ .name = "ftl",
++ .major = FTL_MAJOR,
++ .part_bits = PART_BITS,
++ .readsect = ftl_readsect,
++ .writesect = ftl_writesect,
++ .getgeo = ftl_getgeo,
++ .add_mtd = ftl_add_mtd,
++ .remove_dev = ftl_remove_dev,
++ .owner = THIS_MODULE,
++};
++
+ int init_ftl(void)
+ {
+- int i;
+-
+- memset(myparts, 0, sizeof(myparts));
++ DEBUG(0, "$Id$\n");
+
+- DEBUG(0, "$Id$\n");
+-
+- if (register_blkdev(FTL_MAJOR, "ftl", &ftl_blk_fops)) {
+- printk(KERN_NOTICE "ftl_cs: unable to grab major "
+- "device number!\n");
+- return -EAGAIN;
+- }
+-
+- for (i = 0; i < MINOR_NR(MAX_DEV, 0, 0); i++)
+- ftl_blocksizes[i] = 1024;
+- for (i = 0; i < MAX_DEV*MAX_PART; i++) {
+- ftl_hd[i].nr_sects = 0;
+- ftl_hd[i].start_sect = 0;
+- }
+- blksize_size[FTL_MAJOR] = ftl_blocksizes;
+- ftl_gendisk.major = FTL_MAJOR;
+- blk_init_queue(BLK_DEFAULT_QUEUE(FTL_MAJOR), &do_ftl_request);
+- add_gendisk(&ftl_gendisk);
+-
+- register_mtd_user(&ftl_notifier);
+-
+- return 0;
++ return register_mtd_blktrans(&ftl_tr);
+ }
+
+ static void __exit cleanup_ftl(void)
+ {
+- unregister_mtd_user(&ftl_notifier);
+-
+- unregister_blkdev(FTL_MAJOR, "ftl");
+- blk_cleanup_queue(BLK_DEFAULT_QUEUE(FTL_MAJOR));
+- blksize_size[FTL_MAJOR] = NULL;
+-
+- del_gendisk(&ftl_gendisk);
++ deregister_mtd_blktrans(&ftl_tr);
+ }
+
+ module_init(init_ftl);
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/inftlcore.c linux/drivers/mtd/inftlcore.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/inftlcore.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/mtd/inftlcore.c 2004-11-17 18:17:58.000000000 +0100
+@@ -0,0 +1,900 @@
++/*
++ * inftlcore.c -- Linux driver for Inverse Flash Translation Layer (INFTL)
++ *
++ * (C) Copyright 2002, Greg Ungerer (gerg@snapgear.com)
++ *
++ * Based heavily on the nftlcore.c code which is:
++ * (c) 1999 Machine Vision Holdings, Inc.
++ * Author: David Woodhouse <dwmw2@infradead.org>
++ *
++ * $Id$
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ */
++
++#include <linux/config.h>
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/delay.h>
++#include <linux/slab.h>
++#include <linux/sched.h>
++#include <linux/init.h>
++#include <linux/kmod.h>
++#include <linux/hdreg.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/nftl.h>
++#include <linux/mtd/inftl.h>
++#include <asm/uaccess.h>
++#include <asm/errno.h>
++#include <asm/io.h>
++
++/*
++ * Maximum number of loops while examining next block, to have a
++ * chance to detect consistency problems (they should never happen
++ * because of the checks done in the mounting.
++ */
++#define MAX_LOOPS 10000
++
++extern void INFTL_dumptables(struct INFTLrecord *inftl);
++extern void INFTL_dumpVUchains(struct INFTLrecord *inftl);
++
++static void inftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
++{
++ struct INFTLrecord *inftl;
++ unsigned long temp;
++
++ if (mtd->ecctype != MTD_ECC_RS_DiskOnChip)
++ return;
++
++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: add_mtd for %s\n", mtd->name);
++
++ inftl = kmalloc(sizeof(*inftl), GFP_KERNEL);
++
++ if (!inftl) {
++ printk(KERN_WARNING "INFTL: Out of memory for data structures\n");
++ return;
++ }
++ memset(inftl, 0, sizeof(*inftl));
++
++ inftl->mbd.mtd = mtd;
++ inftl->mbd.devnum = -1;
++ inftl->mbd.blksize = 512;
++ inftl->mbd.tr = tr;
++
++ if (INFTL_mount(inftl) < 0) {
++ printk(KERN_WARNING "INFTL: could not mount device\n");
++ kfree(inftl);
++ return;
++ }
++
++ /* OK, it's a new one. Set up all the data structures. */
++
++ /* Calculate geometry */
++ inftl->cylinders = 1024;
++ inftl->heads = 16;
++
++ temp = inftl->cylinders * inftl->heads;
++ inftl->sectors = inftl->mbd.size / temp;
++ if (inftl->mbd.size % temp) {
++ inftl->sectors++;
++ temp = inftl->cylinders * inftl->sectors;
++ inftl->heads = inftl->mbd.size / temp;
++
++ if (inftl->mbd.size % temp) {
++ inftl->heads++;
++ temp = inftl->heads * inftl->sectors;
++ inftl->cylinders = inftl->mbd.size / temp;
++ }
++ }
++
++ if (inftl->mbd.size != inftl->heads * inftl->cylinders * inftl->sectors) {
++ /*
++ Oh no we don't have
++ mbd.size == heads * cylinders * sectors
++ */
++ printk(KERN_WARNING "INFTL: cannot calculate a geometry to "
++ "match size of 0x%lx.\n", inftl->mbd.size);
++ printk(KERN_WARNING "INFTL: using C:%d H:%d S:%d "
++ "(== 0x%lx sects)\n",
++ inftl->cylinders, inftl->heads , inftl->sectors,
++ (long)inftl->cylinders * (long)inftl->heads *
++ (long)inftl->sectors );
++ }
++
++ if (add_mtd_blktrans_dev(&inftl->mbd)) {
++ if (inftl->PUtable)
++ kfree(inftl->PUtable);
++ if (inftl->VUtable)
++ kfree(inftl->VUtable);
++ kfree(inftl);
++ return;
++ }
++#ifdef PSYCHO_DEBUG
++ printk(KERN_INFO "INFTL: Found new nftl%c\n", nftl->mbd.devnum + 'a');
++#endif
++ return;
++}
++
++static void inftl_remove_dev(struct mtd_blktrans_dev *dev)
++{
++ struct INFTLrecord *inftl = (void *)dev;
++
++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: remove_dev (i=%d)\n", dev->devnum);
++
++ del_mtd_blktrans_dev(dev);
++
++ if (inftl->PUtable)
++ kfree(inftl->PUtable);
++ if (inftl->VUtable)
++ kfree(inftl->VUtable);
++ kfree(inftl);
++}
++
++/*
++ * Actual INFTL access routines.
++ */
++
++/*
++ * INFTL_findfreeblock: Find a free Erase Unit on the INFTL partition.
++ * This function is used when the give Virtual Unit Chain.
++ */
++static u16 INFTL_findfreeblock(struct INFTLrecord *inftl, int desperate)
++{
++ u16 pot = inftl->LastFreeEUN;
++ int silly = inftl->nb_blocks;
++
++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_findfreeblock(inftl=0x%x,"
++ "desperate=%d)\n", (int)inftl, desperate);
++
++ /*
++ * Normally, we force a fold to happen before we run out of free
++ * blocks completely.
++ */
++ if (!desperate && inftl->numfreeEUNs < 2) {
++ DEBUG(MTD_DEBUG_LEVEL1, "INFTL: there are too few free "
++ "EUNs (%d)\n", inftl->numfreeEUNs);
++ return 0xffff;
++ }
++
++ /* Scan for a free block */
++ do {
++ if (inftl->PUtable[pot] == BLOCK_FREE) {
++ inftl->LastFreeEUN = pot;
++ return pot;
++ }
++
++ if (++pot > inftl->lastEUN)
++ pot = 0;
++
++ if (!silly--) {
++ printk(KERN_WARNING "INFTL: no free blocks found! "
++ "EUN range = %d - %d\n", 0, inftl->LastFreeEUN);
++ return BLOCK_NIL;
++ }
++ } while (pot != inftl->LastFreeEUN);
++
++ return BLOCK_NIL;
++}
++
++static u16 INFTL_foldchain(struct INFTLrecord *inftl, unsigned thisVUC, unsigned pendingblock)
++{
++ u16 BlockMap[MAX_SECTORS_PER_UNIT];
++ unsigned char BlockDeleted[MAX_SECTORS_PER_UNIT];
++ unsigned int thisEUN, prevEUN, status;
++ int block, silly;
++ unsigned int targetEUN;
++ struct inftl_oob oob;
++ size_t retlen;
++
++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_foldchain(inftl=0x%x,thisVUC=%d,"
++ "pending=%d)\n", (int)inftl, thisVUC, pendingblock);
++
++ memset(BlockMap, 0xff, sizeof(BlockMap));
++ memset(BlockDeleted, 0, sizeof(BlockDeleted));
++
++ thisEUN = targetEUN = inftl->VUtable[thisVUC];
++
++ if (thisEUN == BLOCK_NIL) {
++ printk(KERN_WARNING "INFTL: trying to fold non-existent "
++ "Virtual Unit Chain %d!\n", thisVUC);
++ return BLOCK_NIL;
++ }
++
++ /*
++ * Scan to find the Erase Unit which holds the actual data for each
++ * 512-byte block within the Chain.
++ */
++ silly = MAX_LOOPS;
++ while (thisEUN < inftl->nb_blocks) {
++ for (block = 0; block < inftl->EraseSize/SECTORSIZE; block ++) {
++ if ((BlockMap[block] != 0xffff) || BlockDeleted[block])
++ continue;
++
++ if (MTD_READOOB(inftl->mbd.mtd, (thisEUN * inftl->EraseSize)
++ + (block * SECTORSIZE), 16 , &retlen,
++ (char *)&oob) < 0)
++ status = SECTOR_IGNORE;
++ else
++ status = oob.b.Status | oob.b.Status1;
++
++ switch(status) {
++ case SECTOR_FREE:
++ case SECTOR_IGNORE:
++ break;
++ case SECTOR_USED:
++ BlockMap[block] = thisEUN;
++ continue;
++ case SECTOR_DELETED:
++ BlockDeleted[block] = 1;
++ continue;
++ default:
++ printk(KERN_WARNING "INFTL: unknown status "
++ "for block %d in EUN %d: %x\n",
++ block, thisEUN, status);
++ break;
++ }
++ }
++
++ if (!silly--) {
++ printk(KERN_WARNING "INFTL: infinite loop in Virtual "
++ "Unit Chain 0x%x\n", thisVUC);
++ return BLOCK_NIL;
++ }
++
++ thisEUN = inftl->PUtable[thisEUN];
++ }
++
++ /*
++ * OK. We now know the location of every block in the Virtual Unit
++ * Chain, and the Erase Unit into which we are supposed to be copying.
++ * Go for it.
++ */
++ DEBUG(MTD_DEBUG_LEVEL1, "INFTL: folding chain %d into unit %d\n",
++ thisVUC, targetEUN);
++
++ for (block = 0; block < inftl->EraseSize/SECTORSIZE ; block++) {
++ unsigned char movebuf[SECTORSIZE];
++ int ret;
++
++ /*
++ * If it's in the target EUN already, or if it's pending write,
++ * do nothing.
++ */
++ if (BlockMap[block] == targetEUN || (pendingblock ==
++ (thisVUC * (inftl->EraseSize / SECTORSIZE) + block))) {
++ continue;
++ }
++
++ /*
++ * Copy only in non free block (free blocks can only
++ * happen in case of media errors or deleted blocks).
++ */
++ if (BlockMap[block] == BLOCK_NIL)
++ continue;
++
++ ret = MTD_READECC(inftl->mbd.mtd, (inftl->EraseSize *
++ BlockMap[block]) + (block * SECTORSIZE), SECTORSIZE,
++ &retlen, movebuf, (char *)&oob, NULL);
++ if (ret < 0) {
++ ret = MTD_READECC(inftl->mbd.mtd, (inftl->EraseSize *
++ BlockMap[block]) + (block * SECTORSIZE),
++ SECTORSIZE, &retlen, movebuf, (char *)&oob,
++ NULL);
++ if (ret != -EIO)
++ DEBUG(MTD_DEBUG_LEVEL1, "INFTL: error went "
++ "away on retry?\n");
++ }
++ MTD_WRITEECC(inftl->mbd.mtd, (inftl->EraseSize * targetEUN) +
++ (block * SECTORSIZE), SECTORSIZE, &retlen,
++ movebuf, (char *)&oob, NULL);
++ }
++
++ /*
++ * Newest unit in chain now contains data from _all_ older units.
++ * So go through and erase each unit in chain, oldest first. (This
++ * is important, by doing oldest first if we crash/reboot then it
++ * it is relatively simple to clean up the mess).
++ */
++ DEBUG(MTD_DEBUG_LEVEL1, "INFTL: want to erase virtual chain %d\n",
++ thisVUC);
++
++ for (;;) {
++ /* Find oldest unit in chain. */
++ thisEUN = inftl->VUtable[thisVUC];
++ prevEUN = BLOCK_NIL;
++ while (inftl->PUtable[thisEUN] != BLOCK_NIL) {
++ prevEUN = thisEUN;
++ thisEUN = inftl->PUtable[thisEUN];
++ }
++
++ /* Check if we are all done */
++ if (thisEUN == targetEUN)
++ break;
++
++ if (INFTL_formatblock(inftl, thisEUN) < 0) {
++ /*
++ * Could not erase : mark block as reserved.
++ * FixMe: Update Bad Unit Table on disk.
++ */
++ inftl->PUtable[thisEUN] = BLOCK_RESERVED;
++ } else {
++ /* Correctly erased : mark it as free */
++ inftl->PUtable[thisEUN] = BLOCK_FREE;
++ inftl->PUtable[prevEUN] = BLOCK_NIL;
++ inftl->numfreeEUNs++;
++ }
++ }
++
++ return targetEUN;
++}
++
++u16 INFTL_makefreeblock(struct INFTLrecord *inftl, unsigned pendingblock)
++{
++ /*
++ * This is the part that needs some cleverness applied.
++ * For now, I'm doing the minimum applicable to actually
++ * get the thing to work.
++ * Wear-levelling and other clever stuff needs to be implemented
++ * and we also need to do some assessment of the results when
++ * the system loses power half-way through the routine.
++ */
++ u16 LongestChain = 0;
++ u16 ChainLength = 0, thislen;
++ u16 chain, EUN;
++
++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_makefreeblock(inftl=0x%x,"
++ "pending=%d)\n", (int)inftl, pendingblock);
++
++ for (chain = 0; chain < inftl->nb_blocks; chain++) {
++ EUN = inftl->VUtable[chain];
++ thislen = 0;
++
++ while (EUN <= inftl->lastEUN) {
++ thislen++;
++ EUN = inftl->PUtable[EUN];
++ if (thislen > 0xff00) {
++ printk(KERN_WARNING "INFTL: endless loop in "
++ "Virtual Chain %d: Unit %x\n",
++ chain, EUN);
++ /*
++ * Actually, don't return failure.
++ * Just ignore this chain and get on with it.
++ */
++ thislen = 0;
++ break;
++ }
++ }
++
++ if (thislen > ChainLength) {
++ ChainLength = thislen;
++ LongestChain = chain;
++ }
++ }
++
++ if (ChainLength < 2) {
++ printk(KERN_WARNING "INFTL: no Virtual Unit Chains available "
++ "for folding. Failing request\n");
++ return BLOCK_NIL;
++ }
++
++ return INFTL_foldchain(inftl, LongestChain, pendingblock);
++}
++
++static int nrbits(unsigned int val, int bitcount)
++{
++ int i, total = 0;
++
++ for (i = 0; (i < bitcount); i++)
++ total += (((0x1 << i) & val) ? 1 : 0);
++ return total;
++}
++
++/*
++ * INFTL_findwriteunit: Return the unit number into which we can write
++ * for this block. Make it available if it isn't already.
++ */
++static inline u16 INFTL_findwriteunit(struct INFTLrecord *inftl, unsigned block)
++{
++ unsigned int thisVUC = block / (inftl->EraseSize / SECTORSIZE);
++ unsigned int thisEUN, writeEUN, prev_block, status;
++ unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize -1);
++ struct inftl_oob oob;
++ struct inftl_bci bci;
++ unsigned char anac, nacs, parity;
++ size_t retlen;
++ int silly, silly2 = 3;
++
++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_findwriteunit(inftl=0x%x,"
++ "block=%d)\n", (int)inftl, block);
++
++ do {
++ /*
++ * Scan the media to find a unit in the VUC which has
++ * a free space for the block in question.
++ */
++ writeEUN = BLOCK_NIL;
++ thisEUN = inftl->VUtable[thisVUC];
++ silly = MAX_LOOPS;
++
++ while (thisEUN <= inftl->lastEUN) {
++ MTD_READOOB(inftl->mbd.mtd, (thisEUN * inftl->EraseSize) +
++ blockofs, 8, &retlen, (char *)&bci);
++
++ status = bci.Status | bci.Status1;
++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: status of block %d in "
++ "EUN %d is %x\n", block , writeEUN, status);
++
++ switch(status) {
++ case SECTOR_FREE:
++ writeEUN = thisEUN;
++ break;
++ case SECTOR_DELETED:
++ case SECTOR_USED:
++ /* Can't go any further */
++ goto hitused;
++ case SECTOR_IGNORE:
++ break;
++ default:
++ /*
++ * Invalid block. Don't use it any more.
++ * Must implement.
++ */
++ break;
++ }
++
++ if (!silly--) {
++ printk(KERN_WARNING "INFTL: infinite loop in "
++ "Virtual Unit Chain 0x%x\n", thisVUC);
++ return 0xffff;
++ }
++
++ /* Skip to next block in chain */
++ thisEUN = inftl->PUtable[thisEUN];
++ }
++
++hitused:
++ if (writeEUN != BLOCK_NIL)
++ return writeEUN;
++
++
++ /*
++ * OK. We didn't find one in the existing chain, or there
++ * is no existing chain. Allocate a new one.
++ */
++ writeEUN = INFTL_findfreeblock(inftl, 0);
++
++ if (writeEUN == BLOCK_NIL) {
++ /*
++ * That didn't work - there were no free blocks just
++ * waiting to be picked up. We're going to have to fold
++ * a chain to make room.
++ */
++ thisEUN = INFTL_makefreeblock(inftl, 0xffff);
++
++ /*
++ * Hopefully we free something, lets try again.
++ * This time we are desperate...
++ */
++ DEBUG(MTD_DEBUG_LEVEL1, "INFTL: using desperate==1 "
++ "to find free EUN to accommodate write to "
++ "VUC %d\n", thisVUC);
++ writeEUN = INFTL_findfreeblock(inftl, 1);
++ if (writeEUN == BLOCK_NIL) {
++ /*
++ * Ouch. This should never happen - we should
++ * always be able to make some room somehow.
++ * If we get here, we've allocated more storage
++ * space than actual media, or our makefreeblock
++ * routine is missing something.
++ */
++ printk(KERN_WARNING "INFTL: cannot make free "
++ "space.\n");
++#ifdef DEBUG
++ INFTL_dumptables(inftl);
++ INFTL_dumpVUchains(inftl);
++#endif
++ return BLOCK_NIL;
++ }
++ }
++
++ /*
++ * Insert new block into virtual chain. Firstly update the
++ * block headers in flash...
++ */
++ anac = 0;
++ nacs = 0;
++ thisEUN = inftl->VUtable[thisVUC];
++ if (thisEUN != BLOCK_NIL) {
++ MTD_READOOB(inftl->mbd.mtd, thisEUN * inftl->EraseSize
++ + 8, 8, &retlen, (char *)&oob.u);
++ anac = oob.u.a.ANAC + 1;
++ nacs = oob.u.a.NACs + 1;
++ }
++
++ prev_block = inftl->VUtable[thisVUC];
++ if (prev_block < inftl->nb_blocks)
++ prev_block -= inftl->firstEUN;
++
++ parity = (nrbits(thisVUC, 16) & 0x1) ? 0x1 : 0;
++ parity |= (nrbits(prev_block, 16) & 0x1) ? 0x2 : 0;
++ parity |= (nrbits(anac, 8) & 0x1) ? 0x4 : 0;
++ parity |= (nrbits(nacs, 8) & 0x1) ? 0x8 : 0;
++
++ oob.u.a.virtualUnitNo = cpu_to_le16(thisVUC);
++ oob.u.a.prevUnitNo = cpu_to_le16(prev_block);
++ oob.u.a.ANAC = anac;
++ oob.u.a.NACs = nacs;
++ oob.u.a.parityPerField = parity;
++ oob.u.a.discarded = 0xaa;
++
++ MTD_WRITEOOB(inftl->mbd.mtd, writeEUN * inftl->EraseSize + 8, 8,
++ &retlen, (char *)&oob.u);
++
++ /* Also back up header... */
++ oob.u.b.virtualUnitNo = cpu_to_le16(thisVUC);
++ oob.u.b.prevUnitNo = cpu_to_le16(prev_block);
++ oob.u.b.ANAC = anac;
++ oob.u.b.NACs = nacs;
++ oob.u.b.parityPerField = parity;
++ oob.u.b.discarded = 0xaa;
++
++ MTD_WRITEOOB(inftl->mbd.mtd, writeEUN * inftl->EraseSize +
++ SECTORSIZE * 4 + 8, 8, &retlen, (char *)&oob.u);
++
++ inftl->PUtable[writeEUN] = inftl->VUtable[thisVUC];
++ inftl->VUtable[thisVUC] = writeEUN;
++
++ inftl->numfreeEUNs--;
++ return writeEUN;
++
++ } while (silly2--);
++
++ printk(KERN_WARNING "INFTL: error folding to make room for Virtual "
++ "Unit Chain 0x%x\n", thisVUC);
++ return 0xffff;
++}
++
++/*
++ * Given a Virtual Unit Chain, see if it can be deleted, and if so do it.
++ */
++static void INFTL_trydeletechain(struct INFTLrecord *inftl, unsigned thisVUC)
++{
++ unsigned char BlockUsed[MAX_SECTORS_PER_UNIT];
++ unsigned char BlockDeleted[MAX_SECTORS_PER_UNIT];
++ unsigned int thisEUN, status;
++ int block, silly;
++ struct inftl_bci bci;
++ size_t retlen;
++
++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_trydeletechain(inftl=0x%x,"
++ "thisVUC=%d)\n", (int)inftl, thisVUC);
++
++ memset(BlockUsed, 0, sizeof(BlockUsed));
++ memset(BlockDeleted, 0, sizeof(BlockDeleted));
++
++ thisEUN = inftl->VUtable[thisVUC];
++ if (thisEUN == BLOCK_NIL) {
++ printk(KERN_WARNING "INFTL: trying to delete non-existent "
++ "Virtual Unit Chain %d!\n", thisVUC);
++ return;
++ }
++
++ /*
++ * Scan through the Erase Units to determine whether any data is in
++ * each of the 512-byte blocks within the Chain.
++ */
++ silly = MAX_LOOPS;
++ while (thisEUN < inftl->nb_blocks) {
++ for (block = 0; block < inftl->EraseSize/SECTORSIZE; block++) {
++ if (BlockUsed[block] || BlockDeleted[block])
++ continue;
++
++ if (MTD_READOOB(inftl->mbd.mtd, (thisEUN * inftl->EraseSize)
++ + (block * SECTORSIZE), 8 , &retlen,
++ (char *)&bci) < 0)
++ status = SECTOR_IGNORE;
++ else
++ status = bci.Status | bci.Status1;
++
++ switch(status) {
++ case SECTOR_FREE:
++ case SECTOR_IGNORE:
++ break;
++ case SECTOR_USED:
++ BlockUsed[block] = 1;
++ continue;
++ case SECTOR_DELETED:
++ BlockDeleted[block] = 1;
++ continue;
++ default:
++ printk(KERN_WARNING "INFTL: unknown status "
++ "for block %d in EUN %d: 0x%x\n",
++ block, thisEUN, status);
++ }
++ }
++
++ if (!silly--) {
++ printk(KERN_WARNING "INFTL: infinite loop in Virtual "
++ "Unit Chain 0x%x\n", thisVUC);
++ return;
++ }
++
++ thisEUN = inftl->PUtable[thisEUN];
++ }
++
++ for (block = 0; block < inftl->EraseSize/SECTORSIZE; block++)
++ if (BlockUsed[block])
++ return;
++
++ /*
++ * For each block in the chain free it and make it available
++ * for future use. Erase from the oldest unit first.
++ */
++ DEBUG(MTD_DEBUG_LEVEL1, "INFTL: deleting empty VUC %d\n", thisVUC);
++
++ for (;;) {
++ u16 *prevEUN = &inftl->VUtable[thisVUC];
++ thisEUN = *prevEUN;
++
++ /* If the chain is all gone already, we're done */
++ if (thisEUN == BLOCK_NIL) {
++ DEBUG(MTD_DEBUG_LEVEL2, "INFTL: Empty VUC %d for deletion was already absent\n", thisEUN);
++ return;
++ }
++
++ /* Find oldest unit in chain. */
++ while (inftl->PUtable[thisEUN] != BLOCK_NIL) {
++ BUG_ON(thisEUN >= inftl->nb_blocks);
++
++ prevEUN = &inftl->PUtable[thisEUN];
++ thisEUN = *prevEUN;
++ }
++
++ DEBUG(MTD_DEBUG_LEVEL3, "Deleting EUN %d from VUC %d\n",
++ thisEUN, thisVUC);
++
++ if (INFTL_formatblock(inftl, thisEUN) < 0) {
++ /*
++ * Could not erase : mark block as reserved.
++ * FixMe: Update Bad Unit Table on medium.
++ */
++ inftl->PUtable[thisEUN] = BLOCK_RESERVED;
++ } else {
++ /* Correctly erased : mark it as free */
++ inftl->PUtable[thisEUN] = BLOCK_FREE;
++ inftl->numfreeEUNs++;
++ }
++
++ /* Now sort out whatever was pointing to it... */
++ *prevEUN = BLOCK_NIL;
++
++ /* Ideally we'd actually be responsive to new
++ requests while we're doing this -- if there's
++ free space why should others be made to wait? */
++ cond_resched();
++ }
++
++ inftl->VUtable[thisVUC] = BLOCK_NIL;
++}
++
++static int INFTL_deleteblock(struct INFTLrecord *inftl, unsigned block)
++{
++ unsigned int thisEUN = inftl->VUtable[block / (inftl->EraseSize / SECTORSIZE)];
++ unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize - 1);
++ unsigned int status;
++ int silly = MAX_LOOPS;
++ size_t retlen;
++ struct inftl_bci bci;
++
++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_deleteblock(inftl=0x%x,"
++ "block=%d)\n", (int)inftl, block);
++
++ while (thisEUN < inftl->nb_blocks) {
++ if (MTD_READOOB(inftl->mbd.mtd, (thisEUN * inftl->EraseSize) +
++ blockofs, 8, &retlen, (char *)&bci) < 0)
++ status = SECTOR_IGNORE;
++ else
++ status = bci.Status | bci.Status1;
++
++ switch (status) {
++ case SECTOR_FREE:
++ case SECTOR_IGNORE:
++ break;
++ case SECTOR_DELETED:
++ thisEUN = BLOCK_NIL;
++ goto foundit;
++ case SECTOR_USED:
++ goto foundit;
++ default:
++ printk(KERN_WARNING "INFTL: unknown status for "
++ "block %d in EUN %d: 0x%x\n",
++ block, thisEUN, status);
++ break;
++ }
++
++ if (!silly--) {
++ printk(KERN_WARNING "INFTL: infinite loop in Virtual "
++ "Unit Chain 0x%x\n",
++ block / (inftl->EraseSize / SECTORSIZE));
++ return 1;
++ }
++ thisEUN = inftl->PUtable[thisEUN];
++ }
++
++foundit:
++ if (thisEUN != BLOCK_NIL) {
++ loff_t ptr = (thisEUN * inftl->EraseSize) + blockofs;
++
++ if (MTD_READOOB(inftl->mbd.mtd, ptr, 8, &retlen, (char *)&bci) < 0)
++ return -EIO;
++ bci.Status = bci.Status1 = SECTOR_DELETED;
++ if (MTD_WRITEOOB(inftl->mbd.mtd, ptr, 8, &retlen, (char *)&bci) < 0)
++ return -EIO;
++ INFTL_trydeletechain(inftl, block / (inftl->EraseSize / SECTORSIZE));
++ }
++ return 0;
++}
++
++static int inftl_writeblock(struct mtd_blktrans_dev *mbd, unsigned long block,
++ char *buffer)
++{
++ struct INFTLrecord *inftl = (void *)mbd;
++ unsigned int writeEUN;
++ unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize - 1);
++ size_t retlen;
++ u8 eccbuf[6];
++ char *p, *pend;
++
++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: inftl_writeblock(inftl=0x%x,block=%d,"
++ "buffer=0x%x)\n", (int)inftl, block, (int)buffer);
++
++ /* Is block all zero? */
++ pend = buffer + SECTORSIZE;
++ for (p = buffer; p < pend && !*p; p++)
++ ;
++
++ if (p < pend) {
++ writeEUN = INFTL_findwriteunit(inftl, block);
++
++ if (writeEUN == BLOCK_NIL) {
++ printk(KERN_WARNING "inftl_writeblock(): cannot find "
++ "block to write to\n");
++ /*
++ * If we _still_ haven't got a block to use,
++ * we're screwed.
++ */
++ return 1;
++ }
++
++ MTD_WRITEECC(inftl->mbd.mtd, (writeEUN * inftl->EraseSize) +
++ blockofs, SECTORSIZE, &retlen, (char *)buffer,
++ (char *)eccbuf, NULL);
++ /*
++ * No need to write SECTOR_USED flags since they are written
++ * in mtd_writeecc
++ */
++ } else {
++ INFTL_deleteblock(inftl, block);
++ }
++
++ return 0;
++}
++
++static int inftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block,
++ char *buffer)
++{
++ struct INFTLrecord *inftl = (void *)mbd;
++ unsigned int thisEUN = inftl->VUtable[block / (inftl->EraseSize / SECTORSIZE)];
++ unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize - 1);
++ unsigned int status;
++ int silly = MAX_LOOPS;
++ struct inftl_bci bci;
++ size_t retlen;
++
++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: inftl_readblock(inftl=0x%x,block=%d,"
++ "buffer=0x%x)\n", (int)inftl, block, (int)buffer);
++
++ while (thisEUN < inftl->nb_blocks) {
++ if (MTD_READOOB(inftl->mbd.mtd, (thisEUN * inftl->EraseSize) +
++ blockofs, 8, &retlen, (char *)&bci) < 0)
++ status = SECTOR_IGNORE;
++ else
++ status = bci.Status | bci.Status1;
++
++ switch (status) {
++ case SECTOR_DELETED:
++ thisEUN = BLOCK_NIL;
++ goto foundit;
++ case SECTOR_USED:
++ goto foundit;
++ case SECTOR_FREE:
++ case SECTOR_IGNORE:
++ break;
++ default:
++ printk(KERN_WARNING "INFTL: unknown status for "
++ "block %ld in EUN %d: 0x%04x\n",
++ block, thisEUN, status);
++ break;
++ }
++
++ if (!silly--) {
++ printk(KERN_WARNING "INFTL: infinite loop in "
++ "Virtual Unit Chain 0x%lx\n",
++ block / (inftl->EraseSize / SECTORSIZE));
++ return 1;
++ }
++
++ thisEUN = inftl->PUtable[thisEUN];
++ }
++
++foundit:
++ if (thisEUN == BLOCK_NIL) {
++ /* The requested block is not on the media, return all 0x00 */
++ memset(buffer, 0, SECTORSIZE);
++ } else {
++ size_t retlen;
++ loff_t ptr = (thisEUN * inftl->EraseSize) + blockofs;
++ u_char eccbuf[6];
++ if (MTD_READECC(inftl->mbd.mtd, ptr, SECTORSIZE, &retlen,
++ buffer, eccbuf, NULL))
++ return -EIO;
++ }
++ return 0;
++}
++
++static int inftl_getgeo(struct mtd_blktrans_dev *dev, struct hd_geometry *geo)
++{
++ struct INFTLrecord *inftl = (void *)dev;
++
++ geo->heads = inftl->heads;
++ geo->sectors = inftl->sectors;
++ geo->cylinders = inftl->cylinders;
++
++ return 0;
++}
++
++struct mtd_blktrans_ops inftl_tr = {
++ .name = "inftl",
++ .major = INFTL_MAJOR,
++ .part_bits = INFTL_PARTN_BITS,
++ .getgeo = inftl_getgeo,
++ .readsect = inftl_readblock,
++ .writesect = inftl_writeblock,
++ .add_mtd = inftl_add_mtd,
++ .remove_dev = inftl_remove_dev,
++ .owner = THIS_MODULE,
++};
++
++extern char inftlmountrev[];
++
++int __init init_inftl(void)
++{
++ printk(KERN_INFO "INFTL: inftlcore.c $Revision$, "
++ "inftlmount.c %s\n", inftlmountrev);
++
++ return register_mtd_blktrans(&inftl_tr);
++}
++
++static void __exit cleanup_inftl(void)
++{
++ deregister_mtd_blktrans(&inftl_tr);
++}
++
++module_init(init_inftl);
++module_exit(cleanup_inftl);
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Greg Ungerer <gerg@snapgear.com>, David Woodhouse <dwmw2@infradead.org>, Fabrice Bellard <fabrice.bellard@netgem.com> et al.");
++MODULE_DESCRIPTION("Support code for Inverse Flash Translation Layer, used on M-Systems DiskOnChip 2000, Millennium and Millennium Plus");
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/inftlmount.c linux/drivers/mtd/inftlmount.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/inftlmount.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/mtd/inftlmount.c 2004-11-17 18:17:58.000000000 +0100
+@@ -0,0 +1,817 @@
++/*
++ * inftlmount.c -- INFTL mount code with extensive checks.
++ *
++ * Author: Greg Ungerer (gerg@snapgear.com)
++ * (C) Copyright 2002-2003, Greg Ungerer (gerg@snapgear.com)
++ *
++ * Based heavily on the nftlmount.c code which is:
++ * Author: Fabrice Bellard (fabrice.bellard@netgem.com)
++ * Copyright (C) 2000 Netgem S.A.
++ *
++ * $Id$
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ */
++
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <asm/errno.h>
++#include <asm/io.h>
++#include <asm/uaccess.h>
++#include <linux/miscdevice.h>
++#include <linux/pci.h>
++#include <linux/delay.h>
++#include <linux/slab.h>
++#include <linux/sched.h>
++#include <linux/init.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/nftl.h>
++#include <linux/mtd/inftl.h>
++#include <linux/mtd/compatmac.h>
++
++char inftlmountrev[]="$Revision$";
++
++/*
++ * find_boot_record: Find the INFTL Media Header and its Spare copy which
++ * contains the various device information of the INFTL partition and
++ * Bad Unit Table. Update the PUtable[] table according to the Bad
++ * Unit Table. PUtable[] is used for management of Erase Unit in
++ * other routines in inftlcore.c and inftlmount.c.
++ */
++static int find_boot_record(struct INFTLrecord *inftl)
++{
++ struct inftl_unittail h1;
++ //struct inftl_oob oob;
++ unsigned int i, block, boot_record_count = 0;
++ u8 buf[SECTORSIZE];
++ struct INFTLMediaHeader *mh = &inftl->MediaHdr;
++ struct INFTLPartition *ip;
++ int retlen;
++
++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: find_boot_record(inftl=0x%x)\n",
++ (int)inftl);
++
++ /*
++ * Assume logical EraseSize == physical erasesize for starting the
++ * scan. We'll sort it out later if we find a MediaHeader which says
++ * otherwise.
++ */
++ inftl->EraseSize = inftl->mbd.mtd->erasesize;
++ inftl->nb_blocks = inftl->mbd.mtd->size / inftl->EraseSize;
++
++ inftl->MediaUnit = BLOCK_NIL;
++ inftl->SpareMediaUnit = BLOCK_NIL;
++
++ /* Search for a valid boot record */
++ for (block = 0; block < inftl->nb_blocks; block++) {
++ int ret;
++
++ /*
++ * Check for BNAND header first. Then whinge if it's found
++ * but later checks fail.
++ */
++ if ((ret = MTD_READ(inftl->mbd.mtd, block * inftl->EraseSize,
++ SECTORSIZE, &retlen, buf))) {
++ static int warncount = 5;
++
++ if (warncount) {
++ printk(KERN_WARNING "INFTL: block read at 0x%x "
++ "of mtd%d failed: %d\n",
++ block * inftl->EraseSize,
++ inftl->mbd.mtd->index, ret);
++ if (!--warncount)
++ printk(KERN_WARNING "INFTL: further "
++ "failures for this block will "
++ "not be printed\n");
++ }
++ continue;
++ }
++
++ if (retlen < 6 || memcmp(buf, "BNAND", 6)) {
++ /* BNAND\0 not found. Continue */
++ continue;
++ }
++
++ /* To be safer with BIOS, also use erase mark as discriminant */
++ if ((ret = MTD_READOOB(inftl->mbd.mtd, block * inftl->EraseSize +
++ SECTORSIZE + 8, 8, &retlen, (char *)&h1) < 0)) {
++ printk(KERN_WARNING "INFTL: ANAND header found at "
++ "0x%x in mtd%d, but OOB data read failed "
++ "(err %d)\n", block * inftl->EraseSize,
++ inftl->mbd.mtd->index, ret);
++ continue;
++ }
++
++ if (boot_record_count) {
++ /*
++ * We've already processed one. So we just check if
++ * this one is the same as the first one we found.
++ */
++ if (memcmp(mh, buf, sizeof(struct INFTLMediaHeader))) {
++ printk(KERN_WARNING "INFTL: Media Headers at "
++ "0x%x and 0x%x disagree.\n",
++ inftl->MediaUnit * inftl->EraseSize,
++ block * inftl->EraseSize);
++ return -1;
++ }
++ if (boot_record_count == 1)
++ inftl->SpareMediaUnit = block;
++
++ /*
++ * Mark this boot record (INFTL MediaHeader) block as
++ * reserved.
++ */
++ inftl->PUtable[block] = BLOCK_RESERVED;
++
++ boot_record_count++;
++ continue;
++ }
++
++ /*
++ * This is the first we've seen.
++ * Copy the media header structure into place.
++ */
++ memcpy(mh, buf, sizeof(struct INFTLMediaHeader));
++ mh->NoOfBootImageBlocks = le32_to_cpu(mh->NoOfBootImageBlocks);
++ mh->NoOfBinaryPartitions = le32_to_cpu(mh->NoOfBinaryPartitions);
++ mh->NoOfBDTLPartitions = le32_to_cpu(mh->NoOfBDTLPartitions);
++ mh->BlockMultiplierBits = le32_to_cpu(mh->BlockMultiplierBits);
++ mh->FormatFlags = le32_to_cpu(mh->FormatFlags);
++ mh->PercentUsed = le32_to_cpu(mh->PercentUsed);
++
++#ifdef CONFIG_MTD_DEBUG_VERBOSE
++ if (CONFIG_MTD_DEBUG_VERBOSE >= 2) {
++ printk("INFTL: Media Header ->\n"
++ " bootRecordID = %s\n"
++ " NoOfBootImageBlocks = %d\n"
++ " NoOfBinaryPartitions = %d\n"
++ " NoOfBDTLPartitions = %d\n"
++ " BlockMultiplerBits = %d\n"
++ " FormatFlgs = %d\n"
++ " OsakVersion = 0x%x\n"
++ " PercentUsed = %d\n",
++ mh->bootRecordID, mh->NoOfBootImageBlocks,
++ mh->NoOfBinaryPartitions,
++ mh->NoOfBDTLPartitions,
++ mh->BlockMultiplierBits, mh->FormatFlags,
++ mh->OsakVersion, mh->PercentUsed);
++ }
++#endif
++
++ if (mh->NoOfBDTLPartitions == 0) {
++ printk(KERN_WARNING "INFTL: Media Header sanity check "
++ "failed: NoOfBDTLPartitions (%d) == 0, "
++ "must be at least 1\n", mh->NoOfBDTLPartitions);
++ return -1;
++ }
++
++ if ((mh->NoOfBDTLPartitions + mh->NoOfBinaryPartitions) > 4) {
++ printk(KERN_WARNING "INFTL: Media Header sanity check "
++ "failed: Total Partitions (%d) > 4, "
++ "BDTL=%d Binary=%d\n", mh->NoOfBDTLPartitions +
++ mh->NoOfBinaryPartitions,
++ mh->NoOfBDTLPartitions,
++ mh->NoOfBinaryPartitions);
++ return -1;
++ }
++
++ if (mh->BlockMultiplierBits > 1) {
++ printk(KERN_WARNING "INFTL: sorry, we don't support "
++ "UnitSizeFactor 0x%02x\n",
++ mh->BlockMultiplierBits);
++ return -1;
++ } else if (mh->BlockMultiplierBits == 1) {
++ printk(KERN_WARNING "INFTL: support for INFTL with "
++ "UnitSizeFactor 0x%02x is experimental\n",
++ mh->BlockMultiplierBits);
++ inftl->EraseSize = inftl->mbd.mtd->erasesize <<
++ (0xff - mh->BlockMultiplierBits);
++ inftl->nb_blocks = inftl->mbd.mtd->size / inftl->EraseSize;
++ }
++
++ /* Scan the partitions */
++ for (i = 0; (i < 4); i++) {
++ ip = &mh->Partitions[i];
++ ip->virtualUnits = le32_to_cpu(ip->virtualUnits);
++ ip->firstUnit = le32_to_cpu(ip->firstUnit);
++ ip->lastUnit = le32_to_cpu(ip->lastUnit);
++ ip->flags = le32_to_cpu(ip->flags);
++ ip->spareUnits = le32_to_cpu(ip->spareUnits);
++ ip->Reserved0 = le32_to_cpu(ip->Reserved0);
++
++#ifdef CONFIG_MTD_DEBUG_VERBOSE
++ if (CONFIG_MTD_DEBUG_VERBOSE >= 2) {
++ printk(" PARTITION[%d] ->\n"
++ " virtualUnits = %d\n"
++ " firstUnit = %d\n"
++ " lastUnit = %d\n"
++ " flags = 0x%x\n"
++ " spareUnits = %d\n",
++ i, ip->virtualUnits, ip->firstUnit,
++ ip->lastUnit, ip->flags,
++ ip->spareUnits);
++ }
++#endif
++
++ if (ip->Reserved0 != ip->firstUnit) {
++ struct erase_info *instr = &inftl->instr;
++
++ /*
++ * Most likely this is using the
++ * undocumented qiuck mount feature.
++ * We don't support that, we will need
++ * to erase the hidden block for full
++ * compatibility.
++ */
++ instr->addr = ip->Reserved0 * inftl->EraseSize;
++ instr->len = inftl->EraseSize;
++ MTD_ERASE(inftl->mbd.mtd, instr);
++ }
++ if ((ip->lastUnit - ip->firstUnit + 1) < ip->virtualUnits) {
++ printk(KERN_WARNING "INFTL: Media Header "
++ "Partition %d sanity check failed\n"
++ " firstUnit %d : lastUnit %d > "
++ "virtualUnits %d\n", i, ip->lastUnit,
++ ip->firstUnit, ip->Reserved0);
++ return -1;
++ }
++ if (ip->Reserved1 != 0) {
++ printk(KERN_WARNING "INFTL: Media Header "
++ "Partition %d sanity check failed: "
++ "Reserved1 %d != 0\n",
++ i, ip->Reserved1);
++ return -1;
++ }
++
++ if (ip->flags & INFTL_BDTL)
++ break;
++ }
++
++ if (i >= 4) {
++ printk(KERN_WARNING "INFTL: Media Header Partition "
++ "sanity check failed:\n No partition "
++ "marked as Disk Partition\n");
++ return -1;
++ }
++
++ inftl->nb_boot_blocks = ip->firstUnit;
++ inftl->numvunits = ip->virtualUnits;
++ if (inftl->numvunits > (inftl->nb_blocks -
++ inftl->nb_boot_blocks - 2)) {
++ printk(KERN_WARNING "INFTL: Media Header sanity check "
++ "failed:\n numvunits (%d) > nb_blocks "
++ "(%d) - nb_boot_blocks(%d) - 2\n",
++ inftl->numvunits, inftl->nb_blocks,
++ inftl->nb_boot_blocks);
++ return -1;
++ }
++
++ inftl->mbd.size = inftl->numvunits *
++ (inftl->EraseSize / SECTORSIZE);
++
++ /*
++ * Block count is set to last used EUN (we won't need to keep
++ * any meta-data past that point).
++ */
++ inftl->firstEUN = ip->firstUnit;
++ inftl->lastEUN = ip->lastUnit;
++ inftl->nb_blocks = ip->lastUnit + 1;
++
++ /* Memory alloc */
++ inftl->PUtable = kmalloc(inftl->nb_blocks * sizeof(u16), GFP_KERNEL);
++ if (!inftl->PUtable) {
++ printk(KERN_WARNING "INFTL: allocation of PUtable "
++ "failed (%d bytes)\n",
++ inftl->nb_blocks * sizeof(u16));
++ return -ENOMEM;
++ }
++
++ inftl->VUtable = kmalloc(inftl->nb_blocks * sizeof(u16), GFP_KERNEL);
++ if (!inftl->VUtable) {
++ kfree(inftl->PUtable);
++ printk(KERN_WARNING "INFTL: allocation of VUtable "
++ "failed (%d bytes)\n",
++ inftl->nb_blocks * sizeof(u16));
++ return -ENOMEM;
++ }
++
++ /* Mark the blocks before INFTL MediaHeader as reserved */
++ for (i = 0; i < inftl->nb_boot_blocks; i++)
++ inftl->PUtable[i] = BLOCK_RESERVED;
++ /* Mark all remaining blocks as potentially containing data */
++ for (; i < inftl->nb_blocks; i++)
++ inftl->PUtable[i] = BLOCK_NOTEXPLORED;
++
++ /* Mark this boot record (NFTL MediaHeader) block as reserved */
++ inftl->PUtable[block] = BLOCK_RESERVED;
++
++#if 0
++ /* Read Bad Erase Unit Table and modify PUtable[] accordingly */
++ for (i = 0; i < inftl->nb_blocks; i++) {
++ if ((i & (SECTORSIZE - 1)) == 0) {
++ /* read one sector for every SECTORSIZE of blocks */
++ if ((ret = MTD_READECC(inftl->mbd.mtd,
++ block * inftl->EraseSize + i + SECTORSIZE,
++ SECTORSIZE, &retlen, buf,
++ (char *)&oob, NULL)) < 0) {
++ printk(KERN_WARNING "INFTL: read of "
++ "bad sector table failed "
++ "(err %d)\n", ret);
++ kfree(inftl->VUtable);
++ kfree(inftl->PUtable);
++ return -1;
++ }
++ }
++ /* Mark the Bad Erase Unit as RESERVED in PUtable */
++ if (buf[i & (SECTORSIZE - 1)] != 0xff)
++ inftl->PUtable[i] = BLOCK_RESERVED;
++ }
++#endif
++
++ inftl->MediaUnit = block;
++ boot_record_count++;
++ }
++
++ return boot_record_count ? 0 : -1;
++}
++
++static int memcmpb(void *a, int c, int n)
++{
++ int i;
++ for (i = 0; i < n; i++) {
++ if (c != ((unsigned char *)a)[i])
++ return 1;
++ }
++ return 0;
++}
++
++/*
++ * check_free_sector: check if a free sector is actually FREE,
++ * i.e. All 0xff in data and oob area.
++ */
++static int check_free_sectors(struct INFTLrecord *inftl, unsigned int address,
++ int len, int check_oob)
++{
++ int i, retlen;
++ u8 buf[SECTORSIZE];
++
++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: check_free_sectors(inftl=0x%x,"
++ "address=0x%x,len=%d,check_oob=%d)\n", (int)inftl,
++ address, len, check_oob);
++
++ for (i = 0; i < len; i += SECTORSIZE) {
++ /*
++ * We want to read the sector without ECC check here since a
++ * free sector does not have ECC syndrome on it yet.
++ */
++ if (MTD_READ(inftl->mbd.mtd, address, SECTORSIZE, &retlen, buf) < 0)
++ return -1;
++ if (memcmpb(buf, 0xff, SECTORSIZE) != 0)
++ return -1;
++
++ if (check_oob) {
++ if (MTD_READOOB(inftl->mbd.mtd, address,
++ inftl->mbd.mtd->oobsize, &retlen, buf) < 0)
++ return -1;
++ if (memcmpb(buf, 0xff, inftl->mbd.mtd->oobsize) != 0)
++ return -1;
++ }
++ address += SECTORSIZE;
++ }
++
++ return 0;
++}
++
++/*
++ * INFTL_format: format a Erase Unit by erasing ALL Erase Zones in the Erase
++ * Unit and Update INFTL metadata. Each erase operation is
++ * checked with check_free_sectors.
++ *
++ * Return: 0 when succeed, -1 on error.
++ *
++ * ToDo: 1. Is it neceressary to check_free_sector after erasing ??
++ * 2. UnitSizeFactor != 0xFF
++ */
++int INFTL_formatblock(struct INFTLrecord *inftl, int block)
++{
++ int retlen;
++ struct inftl_unittail uci;
++ struct erase_info *instr = &inftl->instr;
++
++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_formatblock(inftl=0x%x,"
++ "block=%d)\n", (int)inftl, block);
++
++ memset(instr, 0, sizeof(struct erase_info));
++
++ /* FIXME: Shouldn't we be setting the 'discarded' flag to zero
++ _first_? */
++
++ /* Use async erase interface, test return code */
++ instr->addr = block * inftl->EraseSize;
++ instr->len = inftl->EraseSize;
++ MTD_ERASE(inftl->mbd.mtd, instr);
++
++ if (instr->state == MTD_ERASE_FAILED) {
++ /*
++ * Could not format, FixMe: We should update the BadUnitTable
++ * both in memory and on disk.
++ */
++ printk(KERN_WARNING "INFTL: error while formatting block %d\n",
++ block);
++ return -1;
++ }
++
++ /*
++ * Check the "freeness" of Erase Unit before updating metadata.
++ * FixMe: is this check really necessary? Since we have check the
++ * return code after the erase operation.
++ */
++ if (check_free_sectors(inftl, instr->addr, inftl->EraseSize, 1) != 0)
++ return -1;
++
++ uci.EraseMark = cpu_to_le16(ERASE_MARK);
++ uci.EraseMark1 = cpu_to_le16(ERASE_MARK);
++ uci.Reserved[0] = 0;
++ uci.Reserved[1] = 0;
++ uci.Reserved[2] = 0;
++ uci.Reserved[3] = 0;
++ if (MTD_WRITEOOB(inftl->mbd.mtd, block * inftl->EraseSize + SECTORSIZE * 2 +
++ 8, 8, &retlen, (char *)&uci) < 0)
++ return -1;
++ return 0;
++}
++
++/*
++ * format_chain: Format an invalid Virtual Unit chain. It frees all the Erase
++ * Units in a Virtual Unit Chain, i.e. all the units are disconnected.
++ *
++ * Since the chain is invalid then we will have to erase it from its
++ * head (normally for INFTL we go from the oldest). But if it has a
++ * loop then there is no oldest...
++ */
++static void format_chain(struct INFTLrecord *inftl, unsigned int first_block)
++{
++ unsigned int block = first_block, block1;
++
++ printk(KERN_WARNING "INFTL: formatting chain at block %d\n",
++ first_block);
++
++ for (;;) {
++ block1 = inftl->PUtable[block];
++
++ printk(KERN_WARNING "INFTL: formatting block %d\n", block);
++ if (INFTL_formatblock(inftl, block) < 0) {
++ /*
++ * Cannot format !!!! Mark it as Bad Unit,
++ * FixMe: update the BadUnitTable on disk.
++ */
++ inftl->PUtable[block] = BLOCK_RESERVED;
++ } else {
++ inftl->PUtable[block] = BLOCK_FREE;
++ }
++
++ /* Goto next block on the chain */
++ block = block1;
++
++ if (block == BLOCK_NIL || block >= inftl->lastEUN)
++ break;
++ }
++}
++
++void INFTL_dumptables(struct INFTLrecord *s)
++{
++ int i;
++
++ printk("-------------------------------------------"
++ "----------------------------------\n");
++
++ printk("VUtable[%d] ->", s->nb_blocks);
++ for (i = 0; i < s->nb_blocks; i++) {
++ if ((i % 8) == 0)
++ printk("\n%04x: ", i);
++ printk("%04x ", s->VUtable[i]);
++ }
++
++ printk("\n-------------------------------------------"
++ "----------------------------------\n");
++
++ printk("PUtable[%d-%d=%d] ->", s->firstEUN, s->lastEUN, s->nb_blocks);
++ for (i = 0; i <= s->lastEUN; i++) {
++ if ((i % 8) == 0)
++ printk("\n%04x: ", i);
++ printk("%04x ", s->PUtable[i]);
++ }
++
++ printk("\n-------------------------------------------"
++ "----------------------------------\n");
++
++ printk("INFTL ->\n"
++ " EraseSize = %d\n"
++ " h/s/c = %d/%d/%d\n"
++ " numvunits = %d\n"
++ " firstEUN = %d\n"
++ " lastEUN = %d\n"
++ " numfreeEUNs = %d\n"
++ " LastFreeEUN = %d\n"
++ " nb_blocks = %d\n"
++ " nb_boot_blocks = %d",
++ s->EraseSize, s->heads, s->sectors, s->cylinders,
++ s->numvunits, s->firstEUN, s->lastEUN, s->numfreeEUNs,
++ s->LastFreeEUN, s->nb_blocks, s->nb_boot_blocks);
++
++ printk("\n-------------------------------------------"
++ "----------------------------------\n");
++}
++
++void INFTL_dumpVUchains(struct INFTLrecord *s)
++{
++ int logical, block, i;
++
++ printk("-------------------------------------------"
++ "----------------------------------\n");
++
++ printk("INFTL Virtual Unit Chains:\n");
++ for (logical = 0; logical < s->nb_blocks; logical++) {
++ block = s->VUtable[logical];
++ if (block > s->nb_blocks)
++ continue;
++ printk(" LOGICAL %d --> %d ", logical, block);
++ for (i = 0; i < s->nb_blocks; i++) {
++ if (s->PUtable[block] == BLOCK_NIL)
++ break;
++ block = s->PUtable[block];
++ printk("%d ", block);
++ }
++ printk("\n");
++ }
++
++ printk("-------------------------------------------"
++ "----------------------------------\n");
++}
++
++int INFTL_mount(struct INFTLrecord *s)
++{
++ unsigned int block, first_block, prev_block, last_block;
++ unsigned int first_logical_block, logical_block, erase_mark;
++ int chain_length, do_format_chain;
++ struct inftl_unithead1 h0;
++ struct inftl_unittail h1;
++ int i, retlen;
++ u8 *ANACtable, ANAC;
++
++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_mount(inftl=0x%x)\n", (int)s);
++
++ /* Search for INFTL MediaHeader and Spare INFTL Media Header */
++ if (find_boot_record(s) < 0) {
++ printk(KERN_WARNING "INFTL: could not find valid boot record?\n");
++ return -1;
++ }
++
++ /* Init the logical to physical table */
++ for (i = 0; i < s->nb_blocks; i++)
++ s->VUtable[i] = BLOCK_NIL;
++
++ logical_block = block = BLOCK_NIL;
++
++ /* Temporary buffer to store ANAC numbers. */
++ ANACtable = kmalloc(s->nb_blocks * sizeof(u8), GFP_KERNEL);
++ memset(ANACtable, 0, s->nb_blocks);
++
++ /*
++ * First pass is to explore each physical unit, and construct the
++ * virtual chains that exist (newest physical unit goes into VUtable).
++ * Any block that is in any way invalid will be left in the
++ * NOTEXPLORED state. Then at the end we will try to format it and
++ * mark it as free.
++ */
++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: pass 1, explore each unit\n");
++ for (first_block = s->firstEUN; first_block <= s->lastEUN; first_block++) {
++ if (s->PUtable[first_block] != BLOCK_NOTEXPLORED)
++ continue;
++
++ do_format_chain = 0;
++ first_logical_block = BLOCK_NIL;
++ last_block = BLOCK_NIL;
++ block = first_block;
++
++ for (chain_length = 0; ; chain_length++) {
++
++ if ((chain_length == 0) &&
++ (s->PUtable[block] != BLOCK_NOTEXPLORED)) {
++ /* Nothing to do here, onto next block */
++ break;
++ }
++
++ if (MTD_READOOB(s->mbd.mtd, block * s->EraseSize + 8,
++ 8, &retlen, (char *)&h0) < 0 ||
++ MTD_READOOB(s->mbd.mtd, block * s->EraseSize +
++ 2 * SECTORSIZE + 8, 8, &retlen, (char *)&h1) < 0) {
++ /* Should never happen? */
++ do_format_chain++;
++ break;
++ }
++
++ logical_block = le16_to_cpu(h0.virtualUnitNo);
++ prev_block = le16_to_cpu(h0.prevUnitNo);
++ erase_mark = le16_to_cpu((h1.EraseMark | h1.EraseMark1));
++ ANACtable[block] = h0.ANAC;
++
++ /* Previous block is relative to start of Partition */
++ if (prev_block < s->nb_blocks)
++ prev_block += s->firstEUN;
++
++ /* Already explored partial chain? */
++ if (s->PUtable[block] != BLOCK_NOTEXPLORED) {
++ /* Check if chain for this logical */
++ if (logical_block == first_logical_block) {
++ if (last_block != BLOCK_NIL)
++ s->PUtable[last_block] = block;
++ }
++ break;
++ }
++
++ /* Check for invalid block */
++ if (erase_mark != ERASE_MARK) {
++ printk(KERN_WARNING "INFTL: corrupt block %d "
++ "in chain %d, chain length %d, erase "
++ "mark 0x%x?\n", block, first_block,
++ chain_length, erase_mark);
++ /*
++ * Assume end of chain, probably incomplete
++ * fold/erase...
++ */
++ if (chain_length == 0)
++ do_format_chain++;
++ break;
++ }
++
++ /* Check for it being free already then... */
++ if ((logical_block == BLOCK_FREE) ||
++ (logical_block == BLOCK_NIL)) {
++ s->PUtable[block] = BLOCK_FREE;
++ break;
++ }
++
++ /* Sanity checks on block numbers */
++ if ((logical_block >= s->nb_blocks) ||
++ ((prev_block >= s->nb_blocks) &&
++ (prev_block != BLOCK_NIL))) {
++ if (chain_length > 0) {
++ printk(KERN_WARNING "INFTL: corrupt "
++ "block %d in chain %d?\n",
++ block, first_block);
++ do_format_chain++;
++ }
++ break;
++ }
++
++ if (first_logical_block == BLOCK_NIL) {
++ first_logical_block = logical_block;
++ } else {
++ if (first_logical_block != logical_block) {
++ /* Normal for folded chain... */
++ break;
++ }
++ }
++
++ /*
++ * Current block is valid, so if we followed a virtual
++ * chain to get here then we can set the previous
++ * block pointer in our PUtable now. Then move onto
++ * the previous block in the chain.
++ */
++ s->PUtable[block] = BLOCK_NIL;
++ if (last_block != BLOCK_NIL)
++ s->PUtable[last_block] = block;
++ last_block = block;
++ block = prev_block;
++
++ /* Check for end of chain */
++ if (block == BLOCK_NIL)
++ break;
++
++ /* Validate next block before following it... */
++ if (block > s->lastEUN) {
++ printk(KERN_WARNING "INFTL: invalid previous "
++ "block %d in chain %d?\n", block,
++ first_block);
++ do_format_chain++;
++ break;
++ }
++ }
++
++ if (do_format_chain) {
++ format_chain(s, first_block);
++ continue;
++ }
++
++ /*
++ * Looks like a valid chain then. It may not really be the
++ * newest block in the chain, but it is the newest we have
++ * found so far. We might update it in later iterations of
++ * this loop if we find something newer.
++ */
++ s->VUtable[first_logical_block] = first_block;
++ logical_block = BLOCK_NIL;
++ }
++
++#ifdef CONFIG_MTD_DEBUG_VERBOSE
++ if (CONFIG_MTD_DEBUG_VERBOSE >= 2)
++ INFTL_dumptables(s);
++#endif
++
++ /*
++ * Second pass, check for infinite loops in chains. These are
++ * possible because we don't update the previous pointers when
++ * we fold chains. No big deal, just fix them up in PUtable.
++ */
++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: pass 2, validate virtual chains\n");
++ for (logical_block = 0; logical_block < s->numvunits; logical_block++) {
++ block = s->VUtable[logical_block];
++ last_block = BLOCK_NIL;
++
++ /* Check for free/reserved/nil */
++ if (block >= BLOCK_RESERVED)
++ continue;
++
++ ANAC = ANACtable[block];
++ for (i = 0; i < s->numvunits; i++) {
++ if (s->PUtable[block] == BLOCK_NIL)
++ break;
++ if (s->PUtable[block] > s->lastEUN) {
++ printk(KERN_WARNING "INFTL: invalid prev %d, "
++ "in virtual chain %d\n",
++ s->PUtable[block], logical_block);
++ s->PUtable[block] = BLOCK_NIL;
++
++ }
++ if (ANACtable[block] != ANAC) {
++ /*
++ * Chain must point back to itself. This is ok,
++ * but we will need adjust the tables with this
++ * newest block and oldest block.
++ */
++ s->VUtable[logical_block] = block;
++ s->PUtable[last_block] = BLOCK_NIL;
++ break;
++ }
++
++ ANAC--;
++ last_block = block;
++ block = s->PUtable[block];
++ }
++
++ if (i >= s->nb_blocks) {
++ /*
++ * Uhoo, infinite chain with valid ANACS!
++ * Format whole chain...
++ */
++ format_chain(s, first_block);
++ }
++ }
++
++#ifdef CONFIG_MTD_DEBUG_VERBOSE
++ if (CONFIG_MTD_DEBUG_VERBOSE >= 2)
++ INFTL_dumptables(s);
++ if (CONFIG_MTD_DEBUG_VERBOSE >= 2)
++ INFTL_dumpVUchains(s);
++#endif
++
++ /*
++ * Third pass, format unreferenced blocks and init free block count.
++ */
++ s->numfreeEUNs = 0;
++ s->LastFreeEUN = BLOCK_NIL;
++
++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: pass 3, format unused blocks\n");
++ for (block = s->firstEUN; block <= s->lastEUN; block++) {
++ if (s->PUtable[block] == BLOCK_NOTEXPLORED) {
++ printk("INFTL: unreferenced block %d, formatting it\n",
++ block);
++ if (INFTL_formatblock(s, block) < 0)
++ s->PUtable[block] = BLOCK_RESERVED;
++ else
++ s->PUtable[block] = BLOCK_FREE;
++ }
++ if (s->PUtable[block] == BLOCK_FREE) {
++ s->numfreeEUNs++;
++ if (s->LastFreeEUN == BLOCK_NIL)
++ s->LastFreeEUN = block;
++ }
++ }
++
++ kfree(ANACtable);
++ return 0;
++}
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/maps/Config.in linux/drivers/mtd/maps/Config.in
+--- linux-mips-2.4.24-pre2/drivers/mtd/maps/Config.in 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/maps/Config.in 2004-11-17 18:17:59.049312400 +0100
+@@ -1,17 +1,14 @@
+ # drivers/mtd/maps/Config.in
+
+-# $Id$
++# $Id$
+
+ mainmenu_option next_comment
+
+ comment 'Mapping drivers for chip access'
+
+-dep_tristate ' CFI Flash device in physical memory map' CONFIG_MTD_PHYSMAP $CONFIG_MTD_GEN_PROBE
+-if [ "$CONFIG_MTD_PHYSMAP" = "y" -o "$CONFIG_MTD_PHYSMAP" = "m" ]; then
+- hex ' Physical start address of flash mapping' CONFIG_MTD_PHYSMAP_START 0x8000000
+- hex ' Physical length of flash mapping' CONFIG_MTD_PHYSMAP_LEN 0x4000000
+- int ' Bus width in octets' CONFIG_MTD_PHYSMAP_BUSWIDTH 2
+-fi
++bool ' Support for non-linear mappings of flash chips' CONFIG_MTD_COMPLEX_MAPPINGS
++
++bool ' CFI Flash device in physical memory map' CONFIG_MTD_PHYSMAP $CONFIG_MTD_GEN_PROBE
+
+ if [ "$CONFIG_SPARC" = "y" -o "$CONFIG_SPARC64" = "y" ]; then
+ dep_tristate ' Sun Microsystems userflash support' CONFIG_MTD_SUN_UFLASH $CONFIG_MTD_CFI
+@@ -21,51 +18,68 @@
+ dep_tristate ' CFI Flash device mapped on Photron PNC-2000' CONFIG_MTD_PNC2000 $CONFIG_MTD_CFI $CONFIG_MTD_PARTITIONS
+ dep_tristate ' CFI Flash device mapped on AMD SC520 CDP' CONFIG_MTD_SC520CDP $CONFIG_MTD_CFI
+ dep_tristate ' CFI Flash device mapped on AMD NetSc520' CONFIG_MTD_NETSC520 $CONFIG_MTD_CFI $CONFIG_MTD_PARTITIONS
+- dep_tristate ' CFI Flash device mapped on Arcom SBC-GXx boards' CONFIG_MTD_SBC_GXX $CONFIG_MTD_CFI_INTELEXT $CONFIG_MTD_PARTITIONS
+- dep_tristate ' CFI Flash device mapped on Arcom ELAN-104NC' CONFIG_MTD_ELAN_104NC $CONFIG_MTD_CFI_INTELEXT $CONFIG_MTD_PARTITIONS
++ dep_tristate ' CFI Flash device mapped on Arcom SBC-GXx boards' CONFIG_MTD_SBC_GXX $CONFIG_MTD_CFI_INTELEXT $CONFIG_MTD_PARTITIONS $CONFIG_MTD_COMPLEX_MAPPINGS
++ dep_tristate ' CFI Flash device mapped on Arcom ELAN-104NC' CONFIG_MTD_ELAN_104NC $CONFIG_MTD_CFI_INTELEXT $CONFIG_MTD_PARTITIONS $CONFIG_MTD_COMPLEX_MAPPINGS
+ dep_tristate ' CFI Flash device mapped on DIL/Net PC' CONFIG_MTD_DILNETPC $CONFIG_MTD_CFI_INTELEXT $CONFIG_MTD_PARTITIONS $CONFIG_MTD_CONCAT
+ if [ "$CONFIG_MTD_DILNETPC" = "y" -o "$CONFIG_MTD_DILNETPC" = "m" ]; then
+ hex ' Size of boot partition' CONFIG_MTD_DILNETPC_BOOTSIZE 0x80000
+ fi
+- dep_tristate ' JEDEC Flash device mapped on Mixcom piggyback card' CONFIG_MTD_MIXMEM $CONFIG_MTD_JEDEC
+- dep_tristate ' JEDEC Flash device mapped on Octagon 5066 SBC' CONFIG_MTD_OCTAGON $CONFIG_MTD_JEDEC
+- dep_tristate ' JEDEC Flash device mapped on Tempustech VMAX SBC301' CONFIG_MTD_VMAX $CONFIG_MTD_JEDEC
++ dep_tristate ' JEDEC Flash device mapped on Octagon 5066 SBC' CONFIG_MTD_OCTAGON $CONFIG_MTD_JEDEC $CONFIG_MTD_COMPLEX_MAPPINGS
++ dep_tristate ' JEDEC Flash device mapped on Tempustech VMAX SBC301' CONFIG_MTD_VMAX $CONFIG_MTD_JEDEC $CONFIG_MTD_COMPLEX_MAPPINGS
+ dep_tristate ' Flash device mapped with DOCCS on NatSemi SCx200' CONFIG_MTD_SCx200_DOCFLASH $CONFIG_MTD_CFI
+ dep_tristate ' BIOS flash chip on Intel L440GX boards' CONFIG_MTD_L440GX $CONFIG_MTD_JEDECPROBE
+ dep_tristate ' ROM connected to AMD76X southbridge' CONFIG_MTD_AMD76XROM $CONFIG_MTD_GEN_PROBE
+- dep_tristate ' ROM connected to Intel Hub Controller 2' CONFIG_MTD_ICH2ROM $CONFIG_MTD_JEDECPROBE
++ dep_tristate ' ROM connected to Intel Hub Controller 2/3/4/5' CONFIG_MTD_ICHXROM $CONFIG_MTD_JEDECPROBE $CONFIG_MTD_COMPLEX_MAPPINGS
+ dep_tristate ' CFI Flash device mapped on SnapGear/SecureEdge' CONFIG_MTD_NETtel $CONFIG_MTD_PARTITIONS
+ dep_tristate ' BIOS flash chip on Intel SCB2 boards' CONFIG_MTD_SCB2_FLASH $CONFIG_MTD_GEN_PROBE
+ fi
+
+-if [ "$CONFIG_PPC" = "y" ]; then
+- dep_tristate ' CFI Flash device mapped on TQM8XXL' CONFIG_MTD_TQM8XXL $CONFIG_MTD_CFI $CONFIG_TQM8xxL
++if [ "$CONFIG_PPC32" = "y" ]; then
++ if [ "$CONFIG_6xx" = "y" -a "$CONFIG_8260" = "y" ]; then
++ dep_tristate ' Flash device on SBC8240' CONFIG_MTD_SBC8240 $CONFIG_MTD_JEDECPROBE
++ fi
++ if [ "$CONFIG_8xx" = "y" ]; then
++ if [ "$CONFIG_TQM8xxL" = "y" ]; then
++ dep_tristate ' CFI Flash device mapped on TQM8XXL' CONFIG_MTD_TQM8XXL $CONFIG_MTD_CFI
++ fi
++ if [ "$CONFIG_RPXLITE" = "y" -o "$CONFIG_RPXCLASSIC" = "y" ]; then
+ dep_tristate ' CFI Flash device mapped on RPX Lite or CLLF' CONFIG_MTD_RPXLITE $CONFIG_MTD_CFI
++ fi
++ if [ "$CONFIG_MBX" = "y" ]; then
+ dep_tristate ' System flash on MBX860 board' CONFIG_MTD_MBX860 $CONFIG_MTD_CFI
++ fi
++ if [ "$CONFIG_DBOX2" = "y" ]; then
+ dep_tristate ' CFI Flash device mapped on D-Box2' CONFIG_MTD_DBOX2 $CONFIG_MTD_CFI
++ fi
+ dep_tristate ' CFI Flash device mapping on FlagaDM' CONFIG_MTD_CFI_FLAGADM $CONFIG_MTD_CFI
+- dep_tristate ' CFI Flash device mapped on IBM Redwood-4/5' CONFIG_MTD_REDWOOD $CONFIG_MTD_CFI
++ fi
++ if [ "$CONFIG_4xx" = "y" ]; then
++ if [ "$CONFIG_40x" = "y" ]; then
++ if [ "$CONFIG_REDWOOD_4" = "y" -o "$CONFIG_REDWOOD_5" = "y" -o "$CONFIG_REDWOOD_6" = "y" ]; then
++ dep_tristate ' CFI Flash device mapped on IBM Redwood' CONFIG_MTD_REDWOOD $CONFIG_MTD_CFI
++ fi
++ dep_tristate ' CFI Flash device mapped on IBM Beech' CONFIG_MTD_BEECH $CONFIG_MTD_CFI $CONFIG_BEECH
++ dep_tristate ' CFI Flash device mapped on IBM Arctic' CONFIG_MTD_ARCTIC $CONFIG_MTD_CFI $CONFIG_ARCTIC2
++ fi
++ if [ "$CONFIG_440" = "y" ]; then
++ dep_tristate ' Flash devices mapped on IBM Ebony' CONFIG_MTD_EBONY $CONFIG_MTD_CFI $CONFIG_EBONY
++ fi
++ fi
+ fi
+
+-if [ "$CONFIG_MIPS" = "y" ]; then
+- dep_tristate ' Pb1000 MTD support' CONFIG_MTD_PB1000 $CONFIG_MIPS_PB1000
+- dep_tristate ' Pb1500 MTD support' CONFIG_MTD_PB1500 $CONFIG_MIPS_PB1500
+- dep_tristate ' Pb1100 MTD support' CONFIG_MTD_PB1100 $CONFIG_MIPS_PB1100
+- dep_tristate ' Bosporus MTD support' CONFIG_MTD_BOSPORUS $CONFIG_MIPS_BOSPORUS
+- dep_tristate ' XXS1500 boot flash device' CONFIG_MTD_XXS1500 $CONFIG_MIPS_XXS1500
+- dep_tristate ' MTX-1 flash device' CONFIG_MTD_MTX1 $CONFIG_MIPS_MTX1
+- if [ "$CONFIG_MTD_PB1500" = "y" -o "$CONFIG_MTD_PB1500" = "m" \
+- -o "$CONFIG_MTD_PB1100" = "y" -o "$CONFIG_MTD_PB1100" = "m" ]; then
+- bool ' Pb[15]00 boot flash device' CONFIG_MTD_PB1500_BOOT
+- bool ' Pb[15]00 user flash device (2nd 32MiB bank)' CONFIG_MTD_PB1500_USER
++if [ "$CONFIG_MIPS" = "y" -o "$CONFIG_MIPS64" = "y" ]; then
++ if [ "$CONFIG_MIPS_PB1000" = "y" -o "$CONFIG_MIPS_PB1100" = "y" -o "$CONFIG_MIPS_PB1500" = "y" ]; then
++ tristate ' Pb1x00 MTD support' CONFIG_MTD_PB1XXX
++ if [ "$CONFIG_MIPS_PB1500" = "y" -o "$CONFIG_MIPS_PB1100" = "m" ]; then
++ bool ' Pb1x00 boot flash device' CONFIG_MTD_PB1500_BOOT
++ bool ' Pb1x00 user flash device (2nd 32MiB bank)' CONFIG_MTD_PB1500_USER
++ fi
+ fi
+ tristate ' Db1x00 MTD support' CONFIG_MTD_DB1X00
+ if [ "$CONFIG_MTD_DB1X00" = "y" -o "$CONFIG_MTD_DB1X00" = "m" ]; then
+ bool ' Db1x00 boot flash device' CONFIG_MTD_DB1X00_BOOT
+ bool ' Db1x00 user flash device (2nd bank)' CONFIG_MTD_DB1X00_USER
+ fi
+- dep_tristate ' Hydrogen 3 MTD support' CONFIG_MTD_HYDIII $CONFIG_MIPS_HYDROGEN3
+- dep_tristate ' Mirage MTD support' CONFIG_MTD_MIRAGE $CONFIG_MIPS_MIRAGE
+ dep_tristate ' Flash chip mapping on ITE QED-4N-S01B, Globespan IVR or custom board' CONFIG_MTD_CSTM_MIPS_IXX $CONFIG_MTD_CFI $CONFIG_MTD_JEDEC $CONFIG_MTD_PARTITIONS
+ if [ "$CONFIG_MTD_CSTM_MIPS_IXX" = "y" -o "$CONFIG_MTD_CSTM_MIPS_IXX" = "m" ]; then
+ hex ' Physical start address of flash mapping' CONFIG_MTD_CSTM_MIPS_IXX_START 0x8000000
+@@ -73,7 +87,7 @@
+ int ' Bus width in octets' CONFIG_MTD_CSTM_MIPS_IXX_BUSWIDTH 2
+ fi
+ dep_tristate ' Momenco Ocelot boot flash device' CONFIG_MTD_OCELOT $CONFIG_MOMENCO_OCELOT
+- dep_tristate ' LASAT flash device' CONFIG_MTD_LASAT $CONFIG_MTD_CFI $CONFIG_LASAT
++ dep_tristate ' LASAT flash device' CONFIG_MTD_LASAT $CONFIG_LASAT
+ fi
+
+ if [ "$CONFIG_SUPERH" = "y" ]; then
+@@ -85,21 +99,24 @@
+ fi
+
+ if [ "$CONFIG_ARM" = "y" ]; then
+- dep_tristate ' CFI Flash device mapped on Nora' CONFIG_MTD_NORA $CONFIG_MTD_CFI
+ dep_tristate ' CFI Flash device mapped on ARM Integrator/P720T' CONFIG_MTD_ARM_INTEGRATOR $CONFIG_MTD_CFI
+ dep_tristate ' Cirrus CDB89712 evaluation board mappings' CONFIG_MTD_CDB89712 $CONFIG_MTD_CFI $CONFIG_ARCH_CDB89712
+ dep_tristate ' CFI Flash device mapped on StrongARM SA11x0' CONFIG_MTD_SA1100 $CONFIG_MTD_CFI $CONFIG_ARCH_SA1100 $CONFIG_MTD_PARTITIONS
+- dep_tristate ' CFI Flash device mapped on DC21285 Footbridge' CONFIG_MTD_DC21285 $CONFIG_MTD_CFI $CONFIG_ARCH_FOOTBRIDGE
++ dep_tristate ' CFI Flash device mapped on DC21285 Footbridge' CONFIG_MTD_DC21285 $CONFIG_MTD_CFI $CONFIG_ARCH_FOOTBRIDGE $CONFIG_MTD_COMPLEX_MAPPINGS
+ dep_tristate ' CFI Flash device mapped on the XScale IQ80310 board' CONFIG_MTD_IQ80310 $CONFIG_MTD_CFI $CONFIG_ARCH_IQ80310
++ dep_tristate ' CFI Flash device mapped on the XScale Lubbock board' CONFIG_MTD_LUBBOCK $CONFIG_MTD_CFI $CONFIG_ARCH_LUBBOCK
++ dep_tristate ' CFI Flash device mapped on XScale IXP425 systems' CONFIG_MTD_IXP425 $CONFIG_MTD_CFI $CONFIG_MTD_COMPLEX_MAPPINGS
+ dep_tristate ' CFI Flash device mapped on Epxa10db' CONFIG_MTD_EPXA10DB $CONFIG_MTD_CFI $CONFIG_MTD_PARTITIONS $CONFIG_ARCH_CAMELOT
+ dep_tristate ' CFI Flash device mapped on the FortuNet board' CONFIG_MTD_FORTUNET $CONFIG_MTD_CFI $CONFIG_MTD_PARTITIONS $CONFIG_SA1100_FORTUNET
+ dep_tristate ' NV-RAM mapping AUTCPU12 board' CONFIG_MTD_AUTCPU12 $CONFIG_ARCH_AUTCPU12
+ dep_tristate ' CFI Flash device mapped on EDB7312' CONFIG_MTD_EDB7312 $CONFIG_MTD_CFI
++ dep_tristate ' CFI Flash device mapped on Hynix evaluation boards' CONFIG_MTD_H720X $CONFIG_MTD_CFI
+ dep_tristate ' JEDEC Flash device mapped on impA7' CONFIG_MTD_IMPA7 $CONFIG_MTD_JEDECPROBE
+ dep_tristate ' JEDEC Flash device mapped on Ceiva/Polaroid PhotoMax Digital Picture Frame' CONFIG_MTD_CEIVA $CONFIG_MTD_JEDECPROBE $CONFIG_ARCH_CEIVA
++ dep_tristate ' NOR Flash device on TOTO board' CONFIG_MTD_NOR_TOTO $CONFIG_MTD $CONFIG_OMAP_TOTO
+ fi
+ if [ "$CONFIG_ALPHA" = "y" ]; then
+- dep_tristate ' Flash chip mapping on TSUNAMI' CONFIG_MTD_TSUNAMI $CONFIG_MTD_GENPROBE
++ dep_tristate ' Flash chip mapping on TSUNAMI' CONFIG_MTD_TSUNAMI $CONFIG_MTD_GENPROBE $CONFIG_MTD_COMPLEX_MAPPINGS
+ fi
+
+ if [ "$CONFIG_UCLINUX" = "y" ]; then
+@@ -107,7 +124,7 @@
+ fi
+
+ # This needs CFI or JEDEC, depending on the cards found.
+-dep_tristate ' PCI MTD driver' CONFIG_MTD_PCI $CONFIG_MTD $CONFIG_PCI
+-dep_tristate ' PCMCIA MTD driver' CONFIG_MTD_PCMCIA $CONFIG_MTD $CONFIG_PCMCIA
++dep_tristate ' PCI MTD driver' CONFIG_MTD_PCI $CONFIG_MTD $CONFIG_PCI $CONFIG_MTD_COMPLEX_MAPPINGS
++dep_tristate ' PCMCIA MTD driver' CONFIG_MTD_PCMCIA $CONFIG_MTD $CONFIG_PCMCIA $CONFIG_MTD_COMPLEX_MAPPINGS
+
+ endmenu
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/maps/Makefile linux/drivers/mtd/maps/Makefile
+--- linux-mips-2.4.24-pre2/drivers/mtd/maps/Makefile 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/maps/Makefile 2004-11-17 18:17:59.051312096 +0100
+@@ -1,12 +1,16 @@
+ #
+ # linux/drivers/maps/Makefile
+ #
+-# $Id$
++# $Id$
+
+-BELOW25 := $(shell echo $(PATCHLEVEL) | sed s/[1234]/y/)
+-
+-ifeq ($(BELOW25),y)
++ifeq ($(PATCHLEVEL),4)
+ O_TARGET := mapslink.o
++export-objs := map_funcs.o
++endif
++
++
++ifeq ($(CONFIG_MTD_COMPLEX_MAPPINGS),y)
++obj-$(CONFIG_MTD) += map_funcs.o
+ endif
+
+ # Chip mappings
+@@ -21,19 +25,13 @@
+ obj-$(CONFIG_MTD_IQ80310) += iq80310.o
+ obj-$(CONFIG_MTD_L440GX) += l440gx.o
+ obj-$(CONFIG_MTD_AMD76XROM) += amd76xrom.o
+-obj-$(CONFIG_MTD_ICH2ROM) += ich2rom.o
++obj-$(CONFIG_MTD_ICHXROM) += ichxrom.o
+ obj-$(CONFIG_MTD_TSUNAMI) += tsunami_flash.o
++obj-$(CONFIG_MTD_LUBBOCK) += lubbock-flash.o
+ obj-$(CONFIG_MTD_MBX860) += mbx860.o
+-obj-$(CONFIG_MTD_NORA) += nora.o
+ obj-$(CONFIG_MTD_CEIVA) += ceiva.o
+ obj-$(CONFIG_MTD_OCTAGON) += octagon-5066.o
+-ifneq ($(CONFIG_MTD_PHYSMAP),n)
+- ifeq ($(CONFIG_MTD_PHYSMAP_BUSWIDTH),8)
+- obj-$(CONFIG_MTD_PHYSMAP) += physmap64.o
+- else
+- obj-$(CONFIG_MTD_PHYSMAP) += physmap.o
+- endif
+-endif
++obj-$(CONFIG_MTD_PHYSMAP) += physmap.o
+ obj-$(CONFIG_MTD_PNC2000) += pnc2000.o
+ obj-$(CONFIG_MTD_PCMCIA) += pcmciamtd.o
+ obj-$(CONFIG_MTD_RPXLITE) += rpxlite.o
+@@ -49,15 +47,9 @@
+ obj-$(CONFIG_MTD_OCELOT) += ocelot.o
+ obj-$(CONFIG_MTD_SOLUTIONENGINE)+= solutionengine.o
+ obj-$(CONFIG_MTD_PCI) += pci.o
+-obj-$(CONFIG_MTD_PB1000) += pb1xxx-flash.o
+-obj-$(CONFIG_MTD_PB1100) += pb1xxx-flash.o
+-obj-$(CONFIG_MTD_PB1500) += pb1xxx-flash.o
+-obj-$(CONFIG_MTD_XXS1500) += xxs1500.o
+-obj-$(CONFIG_MTD_MTX1) += mtx-1.o
+-obj-$(CONFIG_MTD_LASAT) += lasat.o
++obj-$(CONFIG_MTD_PB1XXX) += pb1xxx-flash.o
+ obj-$(CONFIG_MTD_DB1X00) += db1x00-flash.o
+-obj-$(CONFIG_MTD_HYDIII) += hydrogen3-flash.o
+-obj-$(CONFIG_MTD_BOSPORUS) += pb1xxx-flash.o
++obj-$(CONFIG_MTD_LASAT) += lasat.o
+ obj-$(CONFIG_MTD_AUTCPU12) += autcpu12-nvram.o
+ obj-$(CONFIG_MTD_EDB7312) += edb7312.o
+ obj-$(CONFIG_MTD_IMPA7) += impa7.o
+@@ -66,6 +58,13 @@
+ obj-$(CONFIG_MTD_UCLINUX) += uclinux.o
+ obj-$(CONFIG_MTD_NETtel) += nettel.o
+ obj-$(CONFIG_MTD_SCB2_FLASH) += scb2_flash.o
+-obj-$(CONFIG_MTD_MIRAGE) += mirage-flash.o
++obj-$(CONFIG_MTD_EBONY) += ebony.o
++obj-$(CONFIG_MTD_BEECH) += beech-mtd.o
++obj-$(CONFIG_MTD_ARCTIC) += arctic-mtd.o
++obj-$(CONFIG_MTD_H720X) += h720x-flash.o
++obj-$(CONFIG_MTD_SBC8240) += sbc8240.o
++obj-$(CONFIG_MTD_NOR_TOTO) += omap-toto-flash.o
++obj-$(CONFIG_MTD_MPC1211) += mpc1211.o
++obj-$(CONFIG_MTD_IXP425) += ixp425.o
+
+-include $(TOPDIR)/Rules.make
++-include $(TOPDIR)/Rules.make
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/maps/amd76xrom.c linux/drivers/mtd/maps/amd76xrom.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/maps/amd76xrom.c 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/maps/amd76xrom.c 2004-11-17 18:17:59.052311944 +0100
+@@ -2,12 +2,13 @@
+ * amd76xrom.c
+ *
+ * Normal mappings of chips in physical memory
+- * $Id$
++ * $Id$
+ */
+
+ #include <linux/module.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
+ #include <asm/io.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+@@ -16,77 +17,59 @@
+ #include <linux/pci_ids.h>
+
+
++#define xstr(s) str(s)
++#define str(s) #s
++#define MOD_NAME xstr(KBUILD_BASENAME)
++
++#define MTD_DEV_NAME_LENGTH 16
++
+ struct amd76xrom_map_info {
+ struct map_info map;
+ struct mtd_info *mtd;
+ unsigned long window_addr;
+ u32 window_start, window_size;
+ struct pci_dev *pdev;
++ struct resource window_rsrc;
++ struct resource rom_rsrc;
++ char mtd_name[MTD_DEV_NAME_LENGTH];
+ };
+
+-static __u8 amd76xrom_read8(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readb(map->map_priv_1 + ofs);
+-}
+-
+-static __u16 amd76xrom_read16(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readw(map->map_priv_1 + ofs);
+-}
+
+-static __u32 amd76xrom_read32(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readl(map->map_priv_1 + ofs);
+-}
++static struct amd76xrom_map_info amd76xrom_map = {
++ .map = {
++ .name = MOD_NAME,
++ .size = 0,
++ .buswidth = 1,
++ }
++ /* remaining fields of structure are initialized to 0 */
++};
+
+-static void amd76xrom_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+-{
+- memcpy_fromio(to, map->map_priv_1 + from, len);
+-}
+
+-static void amd76xrom_write8(struct map_info *map, __u8 d, unsigned long adr)
++static void amd76xrom_cleanup(struct amd76xrom_map_info *info)
+ {
+- __raw_writeb(d, map->map_priv_1 + adr);
+- mb();
+-}
++ u8 byte;
+
+-static void amd76xrom_write16(struct map_info *map, __u16 d, unsigned long adr)
+-{
+- __raw_writew(d, map->map_priv_1 + adr);
+- mb();
+-}
++ /* Disable writes through the rom window */
++ pci_read_config_byte(info->pdev, 0x40, &byte);
++ pci_write_config_byte(info->pdev, 0x40, byte & ~1);
+
+-static void amd76xrom_write32(struct map_info *map, __u32 d, unsigned long adr)
+-{
+- __raw_writel(d, map->map_priv_1 + adr);
+- mb();
+-}
++ if (info->mtd) {
++ del_mtd_device(info->mtd);
++ map_destroy(info->mtd);
++ info->mtd = NULL;
++ info->map.virt = 0;
++ }
++ if (info->rom_rsrc.parent)
++ release_resource(&info->rom_rsrc);
++ if (info->window_rsrc.parent)
++ release_resource(&info->window_rsrc);
+
+-static void amd76xrom_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+-{
+- memcpy_toio(map->map_priv_1 + to, from, len);
++ if (info->window_addr) {
++ iounmap((void *)(info->window_addr));
++ info->window_addr = 0;
++ }
+ }
+
+-static struct amd76xrom_map_info amd76xrom_map = {
+- map: {
+- name: "AMD76X rom",
+- size: 0,
+- buswidth: 1,
+- read8: amd76xrom_read8,
+- read16: amd76xrom_read16,
+- read32: amd76xrom_read32,
+- copy_from: amd76xrom_copy_from,
+- write8: amd76xrom_write8,
+- write16: amd76xrom_write16,
+- write32: amd76xrom_write32,
+- copy_to: amd76xrom_copy_to,
+- /* The standard rom socket is for single power supply chips
+- * that don't have an extra vpp.
+- */
+- },
+- mtd: 0,
+- window_addr: 0,
+-};
+
+ static int __devinit amd76xrom_init_one (struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+@@ -97,6 +80,10 @@
+ u8 segen_bits;
+ };
+ static struct rom_window rom_window[] = {
++ /*
++ * Need the 5MiB window for chips that have block lock/unlock
++ * registers located below 4MiB window.
++ */
+ { 0xffb00000, 5*1024*1024, (1<<7) | (1<<6), },
+ { 0xffc00000, 4*1024*1024, (1<<7), },
+ { 0xffff0000, 64*1024, 0 },
+@@ -112,19 +99,29 @@
+ int i;
+ u32 rom_size;
+
++ info->pdev = pdev;
+ window = &rom_window[0];
+-#if 0
+- while(window->size) {
+- if (request_mem_region(window->start, window->size, "amd76xrom")) {
+- break;
+- }
+- window++;
+- }
+- if (!window->size) {
+- printk(KERN_ERR "amd76xrom: cannot reserve rom window\n");
+- goto err_out_none;
++
++ while (window->size) {
++ /*
++ * Try to reserve the window mem region. If this fails then
++ * it is likely due to a fragment of the window being
++ * "reseved" by the BIOS. In the case that the
++ * request_mem_region() fails then once the rom size is
++ * discovered we will try to reserve the unreserved fragment.
++ */
++ info->window_rsrc.name = MOD_NAME;
++ info->window_rsrc.start = window->start;
++ info->window_rsrc.end = window->start + window->size - 1;
++ info->window_rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
++ if (request_resource(&iomem_resource, &info->window_rsrc)) {
++ info->window_rsrc.parent = NULL;
++ printk(KERN_ERR MOD_NAME
++ " %s(): Unable to register resource"
++ " 0x%.08lx-0x%.08lx - kernel bug?\n",
++ __func__,
++ info->window_rsrc.start, info->window_rsrc.end);
+ }
+-#endif
+
+ /* Enable the selected rom window */
+ pci_read_config_byte(pdev, 0x43, &byte);
+@@ -136,49 +133,94 @@
+
+ /* FIXME handle registers 0x80 - 0x8C the bios region locks */
+
+- printk(KERN_NOTICE "amd76xrom window : %x at %x\n",
++ printk(KERN_NOTICE MOD_NAME " window : %x at %x\n",
+ window->size, window->start);
+ /* For write accesses caches are useless */
+- info->window_addr = (unsigned long)ioremap_nocache(window->start, window->size);
++ info->window_addr =
++ (unsigned long)ioremap_nocache(window->start,
++ window->size);
+
+ if (!info->window_addr) {
+ printk(KERN_ERR "Failed to ioremap\n");
+- goto err_out_free_mmio_region;
++ continue;
+ }
+- info->mtd = 0;
++
++ info->mtd = NULL;
++
+ for(i = 0; (rom_size = rom_probe_sizes[i]); i++) {
+ char **chip_type;
+ if (rom_size > window->size) {
+ continue;
+ }
+- info->map.map_priv_1 =
++ info->map.phys = window->start + window->size - rom_size;
++ info->map.virt =
+ info->window_addr + window->size - rom_size;
+ info->map.size = rom_size;
++ simple_map_init(&info->map);
+ chip_type = rom_probe_types;
+ for(; !info->mtd && *chip_type; chip_type++) {
+ info->mtd = do_map_probe(*chip_type, &amd76xrom_map.map);
+ }
+- if (info->mtd) {
+- break;
+- }
++ if (info->mtd) goto found_mtd;
+ }
+- if (!info->mtd) {
+- goto err_out_iounmap;
++ iounmap((void *)(info->window_addr));
++ info->window_addr = 0;
++
++ /* Disable writes through the rom window */
++ pci_read_config_byte(pdev, 0x40, &byte);
++ pci_write_config_byte(pdev, 0x40, byte & ~1);
++
++ window++;
+ }
+- printk(KERN_NOTICE "amd76xrom chip at offset: %x\n",
++ goto failed;
++
++ found_mtd:
++ printk(KERN_NOTICE MOD_NAME " chip at offset: 0x%x\n",
+ window->size - rom_size);
+
+- info->mtd->module = THIS_MODULE;
++ info->mtd->owner = THIS_MODULE;
++
++ if (!info->window_rsrc.parent) {
++ /* failed to reserve entire window - try fragments */
++ info->window_rsrc.name = MOD_NAME;
++ info->window_rsrc.start = window->start;
++ info->window_rsrc.end = window->start + window->size - rom_size - 1;
++ info->window_rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
++ if (request_resource(&iomem_resource, &info->window_rsrc)) {
++ printk(KERN_ERR MOD_NAME
++ ": cannot reserve window resource fragment\n");
++ goto failed;
++ }
++ }
++
+ add_mtd_device(info->mtd);
+ info->window_start = window->start;
+ info->window_size = window->size;
++
++ if (info->window_rsrc.parent) {
++ /*
++ * Registering the MTD device in iomem may not be possible
++ * if there is a BIOS "reserved" and BUSY range. If this
++ * fails then continue anyway.
++ */
++ snprintf(info->mtd_name, MTD_DEV_NAME_LENGTH,
++ "mtd%d", info->mtd->index);
++
++ info->rom_rsrc.name = info->mtd_name;
++ info->rom_rsrc.start = window->start + window->size - rom_size;
++ info->rom_rsrc.end = window->start + window->size - 1;
++ info->rom_rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
++ if (request_resource(&info->window_rsrc, &info->rom_rsrc)) {
++ printk(KERN_ERR MOD_NAME
++ ": cannot reserve MTD resource\n");
++ info->rom_rsrc.parent = NULL;
++ }
++ }
++
+ return 0;
+
+-err_out_iounmap:
+- iounmap((void *)(info->window_addr));
+-err_out_free_mmio_region:
+- release_mem_region(window->start, window->size);
+-err_out_none:
++ failed:
++ amd76xrom_cleanup(info);
+ return -ENODEV;
+ }
+
+@@ -186,21 +228,8 @@
+ static void __devexit amd76xrom_remove_one (struct pci_dev *pdev)
+ {
+ struct amd76xrom_map_info *info = &amd76xrom_map;
+- u8 byte;
+-
+- del_mtd_device(info->mtd);
+- map_destroy(info->mtd);
+- info->mtd = 0;
+- info->map.map_priv_1 = 0;
+-
+- iounmap((void *)(info->window_addr));
+- info->window_addr = 0;
+-
+- /* Disable writes through the rom window */
+- pci_read_config_byte(pdev, 0x40, &byte);
+- pci_write_config_byte(pdev, 0x40, byte & ~1);
+
+- release_mem_region(info->window_start, info->window_size);
++ amd76xrom_cleanup(info);
+ }
+
+ static struct pci_device_id amd76xrom_pci_tbl[] __devinitdata = {
+@@ -208,6 +237,7 @@
+ PCI_ANY_ID, PCI_ANY_ID, },
+ { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7440,
+ PCI_ANY_ID, PCI_ANY_ID, },
++ { PCI_VENDOR_ID_AMD, 0x7468 }, /* amd8111 support */
+ { 0, }
+ };
+
+@@ -215,10 +245,10 @@
+
+ #if 0
+ static struct pci_driver amd76xrom_driver = {
+- name: "amd76xrom",
+- id_table: amd76xrom_pci_tbl,
+- probe: amd76xrom_init_one,
+- remove: amd76xrom_remove_one,
++ .name = MOD_NAME,
++ .id_table = amd76xrom_pci_tbl,
++ .probe = amd76xrom_init_one,
++ .remove = amd76xrom_remove_one,
+ };
+ #endif
+
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/maps/arctic-mtd.c linux/drivers/mtd/maps/arctic-mtd.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/maps/arctic-mtd.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/mtd/maps/arctic-mtd.c 2004-11-17 18:17:59.000000000 +0100
+@@ -0,0 +1,135 @@
++/*
++ * $Id$
++ *
++ * drivers/mtd/maps/arctic-mtd.c MTD mappings and partition tables for
++ * IBM 405LP Arctic boards.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ *
++ * Copyright (C) 2002, International Business Machines Corporation
++ * All Rights Reserved.
++ *
++ * Bishop Brock
++ * IBM Research, Austin Center for Low-Power Computing
++ * bcbrock@us.ibm.com
++ * March 2002
++ *
++ * modified for Arctic by,
++ * David Gibson
++ * IBM OzLabs, Canberra, Australia
++ * <arctic@gibson.dropbear.id.au>
++ */
++
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/init.h>
++
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/map.h>
++#include <linux/mtd/partitions.h>
++
++#include <asm/io.h>
++#include <asm/ibm4xx.h>
++
++/*
++ * 0 : 0xFE00 0000 - 0xFEFF FFFF : Filesystem 1 (16MiB)
++ * 1 : 0xFF00 0000 - 0xFF4F FFFF : kernel (5.12MiB)
++ * 2 : 0xFF50 0000 - 0xFFF5 FFFF : Filesystem 2 (10.624MiB) (if non-XIP)
++ * 3 : 0xFFF6 0000 - 0xFFFF FFFF : PIBS Firmware (640KiB)
++ */
++
++#define FFS1_SIZE 0x01000000 /* 16MiB */
++#define KERNEL_SIZE 0x00500000 /* 5.12MiB */
++#define FFS2_SIZE 0x00a60000 /* 10.624MiB */
++#define FIRMWARE_SIZE 0x000a0000 /* 640KiB */
++
++
++#define NAME "Arctic Linux Flash"
++#define PADDR SUBZERO_BOOTFLASH_PADDR
++#define BUSWIDTH 2
++#define SIZE SUBZERO_BOOTFLASH_SIZE
++#define PARTITIONS 4
++
++/* Flash memories on these boards are memory resources, accessed big-endian. */
++
++{
++ /* do nothing for now */
++}
++
++static struct map_info arctic_mtd_map = {
++ .name = NAME,
++ .size = SIZE,
++ .buswidth = BUSWIDTH,
++ .phys = PADDR,
++};
++
++static struct mtd_info *arctic_mtd;
++
++static struct mtd_partition arctic_partitions[PARTITIONS] = {
++ { .name = "Filesystem",
++ .size = FFS1_SIZE,
++ .offset = 0,},
++ { .name = "Kernel",
++ .size = KERNEL_SIZE,
++ .offset = FFS1_SIZE,},
++ { .name = "Filesystem",
++ .size = FFS2_SIZE,
++ .offset = FFS1_SIZE + KERNEL_SIZE,},
++ { .name = "Firmware",
++ .size = FIRMWARE_SIZE,
++ .offset = SUBZERO_BOOTFLASH_SIZE - FIRMWARE_SIZE,},
++};
++
++static int __init
++init_arctic_mtd(void)
++{
++ printk("%s: 0x%08x at 0x%08x\n", NAME, SIZE, PADDR);
++
++ arctic_mtd_map.virt = (unsigned long) ioremap(PADDR, SIZE);
++
++ if (!arctic_mtd_map.virt) {
++ printk("%s: failed to ioremap 0x%x\n", NAME, PADDR);
++ return -EIO;
++ }
++ simple_map_init(&arctic_mtd_map);
++
++ printk("%s: probing %d-bit flash bus\n", NAME, BUSWIDTH * 8);
++ arctic_mtd = do_map_probe("cfi_probe", &arctic_mtd_map);
++
++ if (!arctic_mtd)
++ return -ENXIO;
++
++ arctic_mtd->owner = THIS_MODULE;
++
++ return add_mtd_partitions(arctic_mtd, arctic_partitions, PARTITIONS);
++}
++
++static void __exit
++cleanup_arctic_mtd(void)
++{
++ if (arctic_mtd) {
++ del_mtd_partitions(arctic_mtd);
++ map_destroy(arctic_mtd);
++ iounmap((void *) arctic_mtd_map.virt);
++ }
++}
++
++module_init(init_arctic_mtd);
++module_exit(cleanup_arctic_mtd);
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("David Gibson <arctic@gibson.dropbear.id.au>");
++MODULE_DESCRIPTION("MTD map and partitions for IBM 405LP Arctic boards");
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/maps/autcpu12-nvram.c linux/drivers/mtd/maps/autcpu12-nvram.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/maps/autcpu12-nvram.c 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/maps/autcpu12-nvram.c 2004-11-17 18:17:59.059310880 +0100
+@@ -2,7 +2,7 @@
+ * NV-RAM memory access on autcpu12
+ * (C) 2002 Thomas Gleixner (gleixner@autronix.de)
+ *
+- * $Id$
++ * $Id$
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -24,6 +24,7 @@
+ #include <linux/types.h>
+ #include <linux/kernel.h>
+ #include <linux/ioport.h>
++#include <linux/init.h>
+ #include <asm/io.h>
+ #include <asm/sizes.h>
+ #include <asm/hardware.h>
+@@ -32,80 +33,27 @@
+ #include <linux/mtd/map.h>
+ #include <linux/mtd/partitions.h>
+
+-__u8 autcpu12_read8(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readb(map->map_priv_1 + ofs);
+-}
+-
+-__u16 autcpu12_read16(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readw(map->map_priv_1 + ofs);
+-}
+-
+-__u32 autcpu12_read32(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readl(map->map_priv_1 + ofs);
+-}
+-
+-void autcpu12_write8(struct map_info *map, __u8 d, unsigned long adr)
+-{
+- __raw_writeb(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-void autcpu12_write16(struct map_info *map, __u16 d, unsigned long adr)
+-{
+- __raw_writew(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-void autcpu12_write32(struct map_info *map, __u32 d, unsigned long adr)
+-{
+- __raw_writel(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-void autcpu12_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+-{
+- memcpy_fromio(to, map->map_priv_1 + from, len);
+-}
+-
+-void autcpu12_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+-{
+- while(len) {
+- __raw_writeb(*(unsigned char *) from, map->map_priv_1 + to);
+- from++;
+- to++;
+- len--;
+- }
+-}
+
+ static struct mtd_info *sram_mtd;
+
+ struct map_info autcpu12_sram_map = {
+- name: "SRAM",
+- size: 32768,
+- buswidth: 8,
+- read8: autcpu12_read8,
+- read16: autcpu12_read16,
+- read32: autcpu12_read32,
+- copy_from: autcpu12_copy_from,
+- write8: autcpu12_write8,
+- write16: autcpu12_write16,
+- write32: autcpu12_write32,
+- copy_to: autcpu12_copy_to
++ .name = "SRAM",
++ .size = 32768,
++ .buswidth = 4,
++ .phys = 0x12000000,
+ };
+
+ static int __init init_autcpu12_sram (void)
+ {
+ int err, save0, save1;
+
+- autcpu12_sram_map.map_priv_1 = (unsigned long)ioremap(0x12000000, SZ_128K);
+- if (!autcpu12_sram_map.map_priv_1) {
++ autcpu12_sram_map.virt = (unsigned long)ioremap(0x12000000, SZ_128K);
++ if (!autcpu12_sram_map.virt) {
+ printk("Failed to ioremap autcpu12 NV-RAM space\n");
+ err = -EIO;
+ goto out;
+ }
++ simple_map_init(&autcpu_sram_map);
+
+ /*
+ * Check for 32K/128K
+@@ -115,20 +63,20 @@
+ * Read and check result on ofs 0x0
+ * Restore contents
+ */
+- save0 = autcpu12_read32(&autcpu12_sram_map,0);
+- save1 = autcpu12_read32(&autcpu12_sram_map,0x10000);
+- autcpu12_write32(&autcpu12_sram_map,~save0,0x10000);
++ save0 = map_read32(&autcpu12_sram_map,0);
++ save1 = map_read32(&autcpu12_sram_map,0x10000);
++ map_write32(&autcpu12_sram_map,~save0,0x10000);
+ /* if we find this pattern on 0x0, we have 32K size
+ * restore contents and exit
+ */
+- if ( autcpu12_read32(&autcpu12_sram_map,0) != save0) {
+- autcpu12_write32(&autcpu12_sram_map,save0,0x0);
++ if ( map_read32(&autcpu12_sram_map,0) != save0) {
++ map_write32(&autcpu12_sram_map,save0,0x0);
+ goto map;
+ }
+ /* We have a 128K found, restore 0x10000 and set size
+ * to 128K
+ */
+- autcpu12_write32(&autcpu12_sram_map,save1,0x10000);
++ ma[_write32(&autcpu12_sram_map,save1,0x10000);
+ autcpu12_sram_map.size = SZ_128K;
+
+ map:
+@@ -139,7 +87,7 @@
+ goto out_ioremap;
+ }
+
+- sram_mtd->module = THIS_MODULE;
++ sram_mtd->owner = THIS_MODULE;
+ sram_mtd->erasesize = 16;
+
+ if (add_mtd_device(sram_mtd)) {
+@@ -148,7 +96,7 @@
+ goto out_probe;
+ }
+
+- printk("NV-RAM device size %ldK registered on AUTCPU12\n",autcpu12_sram_map.size/SZ_1K);
++ printk("NV-RAM device size %ldKiB registered on AUTCPU12\n",autcpu12_sram_map.size/SZ_1K);
+
+ return 0;
+
+@@ -157,7 +105,7 @@
+ sram_mtd = 0;
+
+ out_ioremap:
+- iounmap((void *)autcpu12_sram_map.map_priv_1);
++ iounmap((void *)autcpu12_sram_map.virt);
+ out:
+ return err;
+ }
+@@ -167,7 +115,7 @@
+ if (sram_mtd) {
+ del_mtd_device(sram_mtd);
+ map_destroy(sram_mtd);
+- iounmap((void *)autcpu12_sram_map.map_priv_1);
++ iounmap((void *)autcpu12_sram_map.virt);
+ }
+ }
+
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/maps/beech-mtd.c linux/drivers/mtd/maps/beech-mtd.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/maps/beech-mtd.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/mtd/maps/beech-mtd.c 2004-11-17 18:17:59.000000000 +0100
+@@ -0,0 +1,112 @@
++/*
++ * $Id$
++ *
++ * drivers/mtd/maps/beech-mtd.c MTD mappings and partition tables for
++ * IBM 405LP Beech boards.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ *
++ * Copyright (C) 2002, International Business Machines Corporation
++ * All Rights Reserved.
++ *
++ * Bishop Brock
++ * IBM Research, Austin Center for Low-Power Computing
++ * bcbrock@us.ibm.com
++ * March 2002
++ *
++ */
++
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/init.h>
++
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/map.h>
++#include <linux/mtd/partitions.h>
++
++#include <asm/io.h>
++#include <asm/ibm4xx.h>
++
++#define NAME "Beech Linux Flash"
++#define PADDR BEECH_BIGFLASH_PADDR
++#define SIZE BEECH_BIGFLASH_SIZE
++#define BUSWIDTH 1
++
++/* Flash memories on these boards are memory resources, accessed big-endian. */
++
++
++static struct map_info beech_mtd_map = {
++ .name = NAME,
++ .size = SIZE,
++ .buswidth = BUSWIDTH,
++ .phys = PADDR
++};
++
++static struct mtd_info *beech_mtd;
++
++static struct mtd_partition beech_partitions[2] = {
++ {
++ .name = "Linux Kernel",
++ .size = BEECH_KERNEL_SIZE,
++ .offset = BEECH_KERNEL_OFFSET
++ }, {
++ .name = "Free Area",
++ .size = BEECH_FREE_AREA_SIZE,
++ .offset = BEECH_FREE_AREA_OFFSET
++ }
++};
++
++static int __init
++init_beech_mtd(void)
++{
++ printk("%s: 0x%08x at 0x%08x\n", NAME, SIZE, PADDR);
++
++ beech_mtd_map.virt = (unsigned long) ioremap(PADDR, SIZE);
++
++ if (!beech_mtd_map.virt) {
++ printk("%s: failed to ioremap 0x%x\n", NAME, PADDR);
++ return -EIO;
++ }
++
++ simple_map_init(&beech_mtd_map);
++
++ printk("%s: probing %d-bit flash bus\n", NAME, BUSWIDTH * 8);
++ beech_mtd = do_map_probe("cfi_probe", &beech_mtd_map);
++
++ if (!beech_mtd)
++ return -ENXIO;
++
++ beech_mtd->owner = THIS_MODULE;
++
++ return add_mtd_partitions(beech_mtd, beech_partitions, 2);
++}
++
++static void __exit
++cleanup_beech_mtd(void)
++{
++ if (beech_mtd) {
++ del_mtd_partitions(beech_mtd);
++ map_destroy(beech_mtd);
++ iounmap((void *) beech_mtd_map.virt);
++ }
++}
++
++module_init(init_beech_mtd);
++module_exit(cleanup_beech_mtd);
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Bishop Brock <bcbrock@us.ibm.com>");
++MODULE_DESCRIPTION("MTD map and partitions for IBM 405LP Beech boards");
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/maps/cdb89712.c linux/drivers/mtd/maps/cdb89712.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/maps/cdb89712.c 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/maps/cdb89712.c 2004-11-17 18:17:59.061310576 +0100
+@@ -1,13 +1,14 @@
+ /*
+ * Flash on Cirrus CDB89712
+ *
+- * $Id$
++ * $Id$
+ */
+
+ #include <linux/module.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
+ #include <linux/ioport.h>
++#include <linux/init.h>
+ #include <asm/io.h>
+ #include <asm/arch/hardware.h>
+ #include <linux/mtd/mtd.h>
+@@ -16,77 +17,21 @@
+
+
+
+-__u8 cdb89712_read8(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readb(map->map_priv_1 + ofs);
+-}
+-
+-__u16 cdb89712_read16(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readw(map->map_priv_1 + ofs);
+-}
+-
+-__u32 cdb89712_read32(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readl(map->map_priv_1 + ofs);
+-}
+-
+-void cdb89712_write8(struct map_info *map, __u8 d, unsigned long adr)
+-{
+- __raw_writeb(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-void cdb89712_write16(struct map_info *map, __u16 d, unsigned long adr)
+-{
+- __raw_writew(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-void cdb89712_write32(struct map_info *map, __u32 d, unsigned long adr)
+-{
+- __raw_writel(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-void cdb89712_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+-{
+- // printk ("cdb89712_copy_from: 0x%x@0x%x -> 0x%x\n", len, from, to);
+- memcpy_fromio(to, map->map_priv_1 + from, len);
+-}
+-
+-void cdb89712_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+-{
+- while(len) {
+- __raw_writeb(*(unsigned char *) from, map->map_priv_1 + to);
+- from++;
+- to++;
+- len--;
+- }
+-}
+-
+
+ static struct mtd_info *flash_mtd;
+
+ struct map_info cdb89712_flash_map = {
+- name: "flash",
+- size: FLASH_SIZE,
+- buswidth: FLASH_WIDTH,
+- read8: cdb89712_read8,
+- read16: cdb89712_read16,
+- read32: cdb89712_read32,
+- copy_from: cdb89712_copy_from,
+- write8: cdb89712_write8,
+- write16: cdb89712_write16,
+- write32: cdb89712_write32,
+- copy_to: cdb89712_copy_to
++ .name = "flash",
++ .size = FLASH_SIZE,
++ .buswidth = FLASH_WIDTH,
++ .phys = FLASH_START,
+ };
+
+ struct resource cdb89712_flash_resource = {
+- name: "Flash",
+- start: FLASH_START,
+- end: FLASH_START + FLASH_SIZE - 1,
+- flags: IORESOURCE_IO | IORESOURCE_BUSY,
++ .name = "Flash",
++ .start = FLASH_START,
++ .end = FLASH_START + FLASH_SIZE - 1,
++ .flags = IORESOURCE_IO | IORESOURCE_BUSY,
+ };
+
+ static int __init init_cdb89712_flash (void)
+@@ -99,13 +44,13 @@
+ goto out;
+ }
+
+- cdb89712_flash_map.map_priv_1 = (unsigned long)ioremap(FLASH_START, FLASH_SIZE);
+- if (!cdb89712_flash_map.map_priv_1) {
++ cdb89712_flash_map.virt = (unsigned long)ioremap(FLASH_START, FLASH_SIZE);
++ if (!cdb89712_flash_map.virt) {
+ printk(KERN_NOTICE "Failed to ioremap Cdb89712 FLASH space\n");
+ err = -EIO;
+ goto out_resource;
+ }
+-
++ simple_map_init(&cdb89712_flash_map);
+ flash_mtd = do_map_probe("cfi_probe", &cdb89712_flash_map);
+ if (!flash_mtd) {
+ flash_mtd = do_map_probe("map_rom", &cdb89712_flash_map);
+@@ -118,7 +63,7 @@
+ goto out_ioremap;
+ }
+
+- flash_mtd->module = THIS_MODULE;
++ flash_mtd->owner = THIS_MODULE;
+
+ if (add_mtd_device(flash_mtd)) {
+ printk("FLASH device addition failed\n");
+@@ -132,7 +77,7 @@
+ map_destroy(flash_mtd);
+ flash_mtd = 0;
+ out_ioremap:
+- iounmap((void *)cdb89712_flash_map.map_priv_1);
++ iounmap((void *)cdb89712_flash_map.virt);
+ out_resource:
+ release_resource (&cdb89712_flash_resource);
+ out:
+@@ -146,24 +91,17 @@
+ static struct mtd_info *sram_mtd;
+
+ struct map_info cdb89712_sram_map = {
+- name: "SRAM",
+- size: SRAM_SIZE,
+- buswidth: SRAM_WIDTH,
+- read8: cdb89712_read8,
+- read16: cdb89712_read16,
+- read32: cdb89712_read32,
+- copy_from: cdb89712_copy_from,
+- write8: cdb89712_write8,
+- write16: cdb89712_write16,
+- write32: cdb89712_write32,
+- copy_to: cdb89712_copy_to
++ .name = "SRAM",
++ .size = SRAM_SIZE,
++ .buswidth = SRAM_WIDTH,
++ .phys = SRAM_START,
+ };
+
+ struct resource cdb89712_sram_resource = {
+- name: "SRAM",
+- start: SRAM_START,
+- end: SRAM_START + SRAM_SIZE - 1,
+- flags: IORESOURCE_IO | IORESOURCE_BUSY,
++ .name = "SRAM",
++ .start = SRAM_START,
++ .end = SRAM_START + SRAM_SIZE - 1,
++ .flags = IORESOURCE_IO | IORESOURCE_BUSY,
+ };
+
+ static int __init init_cdb89712_sram (void)
+@@ -176,13 +114,13 @@
+ goto out;
+ }
+
+- cdb89712_sram_map.map_priv_1 = (unsigned long)ioremap(SRAM_START, SRAM_SIZE);
+- if (!cdb89712_sram_map.map_priv_1) {
++ cdb89712_sram_map.virt = (unsigned long)ioremap(SRAM_START, SRAM_SIZE);
++ if (!cdb89712_sram_map.virt) {
+ printk(KERN_NOTICE "Failed to ioremap Cdb89712 SRAM space\n");
+ err = -EIO;
+ goto out_resource;
+ }
+-
++ simple_map_init(&cdb89712_sram_map);
+ sram_mtd = do_map_probe("map_ram", &cdb89712_sram_map);
+ if (!sram_mtd) {
+ printk("SRAM probe failed\n");
+@@ -190,7 +128,7 @@
+ goto out_ioremap;
+ }
+
+- sram_mtd->module = THIS_MODULE;
++ sram_mtd->owner = THIS_MODULE;
+ sram_mtd->erasesize = 16;
+
+ if (add_mtd_device(sram_mtd)) {
+@@ -205,7 +143,7 @@
+ map_destroy(sram_mtd);
+ sram_mtd = 0;
+ out_ioremap:
+- iounmap((void *)cdb89712_sram_map.map_priv_1);
++ iounmap((void *)cdb89712_sram_map.virt);
+ out_resource:
+ release_resource (&cdb89712_sram_resource);
+ out:
+@@ -221,20 +159,17 @@
+ static struct mtd_info *bootrom_mtd;
+
+ struct map_info cdb89712_bootrom_map = {
+- name: "BootROM",
+- size: BOOTROM_SIZE,
+- buswidth: BOOTROM_WIDTH,
+- read8: cdb89712_read8,
+- read16: cdb89712_read16,
+- read32: cdb89712_read32,
+- copy_from: cdb89712_copy_from,
++ .name = "BootROM",
++ .size = BOOTROM_SIZE,
++ .buswidth = BOOTROM_WIDTH,
++ .phys = BOOTROM_START,
+ };
+
+ struct resource cdb89712_bootrom_resource = {
+- name: "BootROM",
+- start: BOOTROM_START,
+- end: BOOTROM_START + BOOTROM_SIZE - 1,
+- flags: IORESOURCE_IO | IORESOURCE_BUSY,
++ .name = "BootROM",
++ .start = BOOTROM_START,
++ .end = BOOTROM_START + BOOTROM_SIZE - 1,
++ .flags = IORESOURCE_IO | IORESOURCE_BUSY,
+ };
+
+ static int __init init_cdb89712_bootrom (void)
+@@ -247,13 +182,13 @@
+ goto out;
+ }
+
+- cdb89712_bootrom_map.map_priv_1 = (unsigned long)ioremap(BOOTROM_START, BOOTROM_SIZE);
+- if (!cdb89712_bootrom_map.map_priv_1) {
++ cdb89712_bootrom_map.virt = (unsigned long)ioremap(BOOTROM_START, BOOTROM_SIZE);
++ if (!cdb89712_bootrom_map.virt) {
+ printk(KERN_NOTICE "Failed to ioremap Cdb89712 BootROM space\n");
+ err = -EIO;
+ goto out_resource;
+ }
+-
++ simple_map_init(&cdb89712_bootrom_map);
+ bootrom_mtd = do_map_probe("map_rom", &cdb89712_bootrom_map);
+ if (!bootrom_mtd) {
+ printk("BootROM probe failed\n");
+@@ -261,7 +196,7 @@
+ goto out_ioremap;
+ }
+
+- bootrom_mtd->module = THIS_MODULE;
++ bootrom_mtd->owner = THIS_MODULE;
+ bootrom_mtd->erasesize = 0x10000;
+
+ if (add_mtd_device(bootrom_mtd)) {
+@@ -276,7 +211,7 @@
+ map_destroy(bootrom_mtd);
+ bootrom_mtd = 0;
+ out_ioremap:
+- iounmap((void *)cdb89712_bootrom_map.map_priv_1);
++ iounmap((void *)cdb89712_bootrom_map.virt);
+ out_resource:
+ release_resource (&cdb89712_bootrom_resource);
+ out:
+@@ -306,21 +241,21 @@
+ if (sram_mtd) {
+ del_mtd_device(sram_mtd);
+ map_destroy(sram_mtd);
+- iounmap((void *)cdb89712_sram_map.map_priv_1);
++ iounmap((void *)cdb89712_sram_map.virt);
+ release_resource (&cdb89712_sram_resource);
+ }
+
+ if (flash_mtd) {
+ del_mtd_device(flash_mtd);
+ map_destroy(flash_mtd);
+- iounmap((void *)cdb89712_flash_map.map_priv_1);
++ iounmap((void *)cdb89712_flash_map.virt);
+ release_resource (&cdb89712_flash_resource);
+ }
+
+ if (bootrom_mtd) {
+ del_mtd_device(bootrom_mtd);
+ map_destroy(bootrom_mtd);
+- iounmap((void *)cdb89712_bootrom_map.map_priv_1);
++ iounmap((void *)cdb89712_bootrom_map.virt);
+ release_resource (&cdb89712_bootrom_resource);
+ }
+ }
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/maps/ceiva.c linux/drivers/mtd/maps/ceiva.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/maps/ceiva.c 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/maps/ceiva.c 2004-11-17 18:17:59.063310272 +0100
+@@ -11,7 +11,7 @@
+ *
+ * (C) 2000 Nicolas Pitre <nico@cam.org>
+ *
+- * $Id$
++ * $Id$
+ */
+
+ #include <linux/config.h>
+@@ -19,6 +19,7 @@
+ #include <linux/types.h>
+ #include <linux/ioport.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
+
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+@@ -31,62 +32,10 @@
+ #include <asm/sizes.h>
+
+ /*
+- * This isnt complete yet, so...
++ * This isn't complete yet, so...
+ */
+ #define CONFIG_MTD_CEIVA_STATICMAP
+
+-static __u8 clps_read8(struct map_info *map, unsigned long ofs)
+-{
+- return readb(map->map_priv_1 + ofs);
+-}
+-
+-static __u16 clps_read16(struct map_info *map, unsigned long ofs)
+-{
+- return readw(map->map_priv_1 + ofs);
+-}
+-
+-static __u32 clps_read32(struct map_info *map, unsigned long ofs)
+-{
+- return readl(map->map_priv_1 + ofs);
+-}
+-
+-static void clps_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+-{
+- memcpy(to, (void *)(map->map_priv_1 + from), len);
+-}
+-
+-static void clps_write8(struct map_info *map, __u8 d, unsigned long adr)
+-{
+- writeb(d, map->map_priv_1 + adr);
+-}
+-
+-static void clps_write16(struct map_info *map, __u16 d, unsigned long adr)
+-{
+- writew(d, map->map_priv_1 + adr);
+-}
+-
+-static void clps_write32(struct map_info *map, __u32 d, unsigned long adr)
+-{
+- writel(d, map->map_priv_1 + adr);
+-}
+-
+-static void clps_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+-{
+- memcpy((void *)(map->map_priv_1 + to), from, len);
+-}
+-
+-static struct map_info clps_map __initdata = {
+- name: "clps flash",
+- read8: clps_read8,
+- read16: clps_read16,
+- read32: clps_read32,
+- copy_from: clps_copy_from,
+- write8: clps_write8,
+- write16: clps_write16,
+- write32: clps_write32,
+- copy_to: clps_copy_to,
+-};
+-
+ #ifdef CONFIG_MTD_CEIVA_STATICMAP
+ /*
+ * See include/linux/mtd/partitions.h for definition of the mtd_partition
+@@ -176,7 +125,7 @@
+ maps = kmalloc(sizeof(struct map_info) * nr, GFP_KERNEL);
+ if (!maps)
+ return -ENOMEM;
+-
++ memset(maps, 0, sizeof(struct map_info) * nr);
+ /*
+ * Claim and then map the memory regions.
+ */
+@@ -191,7 +140,9 @@
+ }
+
+ clps[i].map = maps + i;
+- memcpy(clps[i].map, &clps_map, sizeof(struct map_info));
++
++ clps[i].map->name = "clps flash";
++ clps[i].map->phys = clps[i].base;
+
+ clps[i].vbase = ioremap(clps[i].base, clps[i].size);
+ if (!clps[i].vbase) {
+@@ -199,16 +150,18 @@
+ break;
+ }
+
+- clps[i].map->map_priv_1 = (unsigned long)clps[i].vbase;
++ clps[i].map->virt = (unsigned long)clps[i].vbase;
+ clps[i].map->buswidth = clps[i].width;
+ clps[i].map->size = clps[i].size;
+
++ simple_map_init(&clps[i].map);
++
+ clps[i].mtd = do_map_probe("jedec_probe", clps[i].map);
+ if (clps[i].mtd == NULL) {
+ ret = -ENXIO;
+ break;
+ }
+- clps[i].mtd->module = THIS_MODULE;
++ clps[i].mtd->owner = THIS_MODULE;
+ subdev[i] = clps[i].mtd;
+
+ printk(KERN_INFO "clps flash: JEDEC device at 0x%08lx, %dMiB, "
+@@ -318,10 +271,8 @@
+ return nr;
+ }
+
+-extern int parse_redboot_partitions(struct mtd_info *master, struct mtd_partition **pparts);
+-extern int parse_cmdline_partitions(struct mtd_info *master, struct mtd_partition **pparts, char *);
+-
+ static struct mtd_partition *parsed_parts;
++static const char *probes[] = { "cmdlinepart", "RedBoot", NULL };
+
+ static void __init clps_locate_partitions(struct mtd_info *mtd)
+ {
+@@ -331,20 +282,11 @@
+ /*
+ * Partition selection stuff.
+ */
+-#ifdef CONFIG_MTD_CMDLINE_PARTS
+- nr_parts = parse_cmdline_partitions(mtd, &parsed_parts, "clps");
++ nr_parts = parse_mtd_partitions(mtd, probes, &parsed_parts, 0);
+ if (nr_parts > 0) {
+ part_type = "command line";
+ break;
+ }
+-#endif
+-#ifdef CONFIG_MTD_REDBOOT_PARTS
+- nr_parts = parse_redboot_partitions(mtd, &parsed_parts);
+- if (nr_parts > 0) {
+- part_type = "RedBoot";
+- break;
+- }
+-#endif
+ #ifdef CONFIG_MTD_CEIVA_STATICMAP
+ nr_parts = clps_static_partitions(&parsed_parts);
+ if (nr_parts > 0) {
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/maps/cfi_flagadm.c linux/drivers/mtd/maps/cfi_flagadm.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/maps/cfi_flagadm.c 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/maps/cfi_flagadm.c 2004-11-17 18:17:59.064310120 +0100
+@@ -1,7 +1,7 @@
+ /*
+ * Copyright © 2001 Flaga hf. Medical Devices, Kári Davíðsson <kd@flaga.is>
+ *
+- * $Id$
++ * $Id$
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+@@ -27,6 +27,7 @@
+ #include <linux/module.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
+ #include <asm/io.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+@@ -55,83 +56,33 @@
+ #define FLASH_PARTITION3_ADDR 0x00240000
+ #define FLASH_PARTITION3_SIZE 0x001C0000
+
+-__u8 flagadm_read8(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readb(map->map_priv_1 + ofs);
+-}
+-
+-__u16 flagadm_read16(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readw(map->map_priv_1 + ofs);
+-}
+-
+-__u32 flagadm_read32(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readl(map->map_priv_1 + ofs);
+-}
+-
+-void flagadm_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+-{
+- memcpy_fromio(to, map->map_priv_1 + from, len);
+-}
+-
+-void flagadm_write8(struct map_info *map, __u8 d, unsigned long adr)
+-{
+- __raw_writeb(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-void flagadm_write16(struct map_info *map, __u16 d, unsigned long adr)
+-{
+- __raw_writew(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-void flagadm_write32(struct map_info *map, __u32 d, unsigned long adr)
+-{
+- __raw_writel(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-void flagadm_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+-{
+- memcpy_toio(map->map_priv_1 + to, from, len);
+-}
+
+ struct map_info flagadm_map = {
+- name: "FlagaDM flash device",
+- size: FLASH_SIZE,
+- buswidth: 2,
+- read8: flagadm_read8,
+- read16: flagadm_read16,
+- read32: flagadm_read32,
+- copy_from: flagadm_copy_from,
+- write8: flagadm_write8,
+- write16: flagadm_write16,
+- write32: flagadm_write32,
+- copy_to: flagadm_copy_to
++ .name = "FlagaDM flash device",
++ .size = FLASH_SIZE,
++ .buswidth = 2,
+ };
+
+ struct mtd_partition flagadm_parts[] = {
+ {
+- name : "Bootloader",
+- offset : FLASH_PARTITION0_ADDR,
+- size : FLASH_PARTITION0_SIZE
++ .name = "Bootloader",
++ .offset = FLASH_PARTITION0_ADDR,
++ .size = FLASH_PARTITION0_SIZE
+ },
+ {
+- name : "Kernel image",
+- offset : FLASH_PARTITION1_ADDR,
+- size : FLASH_PARTITION1_SIZE
++ .name = "Kernel image",
++ .offset = FLASH_PARTITION1_ADDR,
++ .size = FLASH_PARTITION1_SIZE
+ },
+ {
+- name : "Initial ramdisk image",
+- offset : FLASH_PARTITION2_ADDR,
+- size : FLASH_PARTITION2_SIZE
++ .name = "Initial ramdisk image",
++ .offset = FLASH_PARTITION2_ADDR,
++ .size = FLASH_PARTITION2_SIZE
+ },
+ {
+- name : "Persistant storage",
+- offset : FLASH_PARTITION3_ADDR,
+- size : FLASH_PARTITION3_SIZE
++ .name = "Persistant storage",
++ .offset = FLASH_PARTITION3_ADDR,
++ .size = FLASH_PARTITION3_SIZE
+ }
+ };
+
+@@ -144,22 +95,26 @@
+ printk(KERN_NOTICE "FlagaDM flash device: %x at %x\n",
+ FLASH_SIZE, FLASH_PHYS_ADDR);
+
+- flagadm_map.map_priv_1 = (unsigned long)ioremap(FLASH_PHYS_ADDR,
++ flagadm_map.phys = FLASH_PHYS_ADDR;
++ flagadm_map.virt = (unsigned long)ioremap(FLASH_PHYS_ADDR,
+ FLASH_SIZE);
+
+- if (!flagadm_map.map_priv_1) {
++ if (!flagadm_map.virt) {
+ printk("Failed to ioremap\n");
+ return -EIO;
+ }
++
++ simple_map_init(&flagadm_map);
++
+ mymtd = do_map_probe("cfi_probe", &flagadm_map);
+ if (mymtd) {
+- mymtd->module = THIS_MODULE;
++ mymtd->owner = THIS_MODULE;
+ add_mtd_partitions(mymtd, flagadm_parts, PARTITION_COUNT);
+ printk(KERN_NOTICE "FlagaDM flash device initialized\n");
+ return 0;
+ }
+
+- iounmap((void *)flagadm_map.map_priv_1);
++ iounmap((void *)flagadm_map.virt);
+ return -ENXIO;
+ }
+
+@@ -169,9 +124,9 @@
+ del_mtd_partitions(mymtd);
+ map_destroy(mymtd);
+ }
+- if (flagadm_map.map_priv_1) {
+- iounmap((void *)flagadm_map.map_priv_1);
+- flagadm_map.map_priv_1 = 0;
++ if (flagadm_map.virt) {
++ iounmap((void *)flagadm_map.virt);
++ flagadm_map.virt = 0;
+ }
+ }
+
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/maps/cstm_mips_ixx.c linux/drivers/mtd/maps/cstm_mips_ixx.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/maps/cstm_mips_ixx.c 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/maps/cstm_mips_ixx.c 2004-11-17 18:17:59.065309968 +0100
+@@ -1,5 +1,5 @@
+ /*
+- * $Id$
++ * $Id$
+ *
+ * Mapping of a custom board with both AMD CFI and JEDEC flash in partitions.
+ * Config with both CFI and JEDEC device support.
+@@ -33,55 +33,13 @@
+ #include <linux/module.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
+ #include <asm/io.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+ #include <linux/mtd/partitions.h>
+ #include <linux/config.h>
+-
+-#if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR)
+ #include <linux/delay.h>
+-#endif
+-
+-__u8 cstm_mips_ixx_read8(struct map_info *map, unsigned long ofs)
+-{
+- return *(__u8 *)(map->map_priv_1 + ofs);
+-}
+-
+-__u16 cstm_mips_ixx_read16(struct map_info *map, unsigned long ofs)
+-{
+- return *(__u16 *)(map->map_priv_1 + ofs);
+-}
+-
+-__u32 cstm_mips_ixx_read32(struct map_info *map, unsigned long ofs)
+-{
+- return *(__u32 *)(map->map_priv_1 + ofs);
+-}
+-
+-void cstm_mips_ixx_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+-{
+- memcpy_fromio(to, map->map_priv_1 + from, len);
+-}
+-
+-void cstm_mips_ixx_write8(struct map_info *map, __u8 d, unsigned long adr)
+-{
+- *(__u8 *)(map->map_priv_1 + adr) = d;
+-}
+-
+-void cstm_mips_ixx_write16(struct map_info *map, __u16 d, unsigned long adr)
+-{
+- *(__u16 *)(map->map_priv_1 + adr) = d;
+-}
+-
+-void cstm_mips_ixx_write32(struct map_info *map, __u32 d, unsigned long adr)
+-{
+- *(__u32 *)(map->map_priv_1 + adr) = d;
+-}
+-
+-void cstm_mips_ixx_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+-{
+- memcpy_toio(map->map_priv_1 + to, from, len);
+-}
+
+ #if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR)
+ #define CC_GCR 0xB4013818
+@@ -97,10 +55,17 @@
+ #define CC_GPAICR 0xB4013804
+ #endif /* defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR) */
+
++#if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR)
+ void cstm_mips_ixx_set_vpp(struct map_info *map,int vpp)
+ {
++ static spinlock_t vpp_lock = SPIN_LOCK_UNLOCKED;
++ static int vpp_count = 0;
++ unsigned long flags;
++
++ spin_lock_irqsave(&vpp_lock, flags);
++
+ if (vpp) {
+-#if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR)
++ if (!vpp_count++) {
+ __u16 data;
+ __u8 data1;
+ static u8 first = 1;
+@@ -116,10 +81,9 @@
+ enabling vpp after powerup */
+ udelay(40);
+ }
+-#endif /* CONFIG_MIPS_ITE8172 */
+ }
+- else {
+-#if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR)
++ } else {
++ if (!--vpp_count) {
+ __u16 data;
+
+ // Set GPIO port B pin3 to high
+@@ -127,26 +91,11 @@
+ data = (data & 0xff3f) | 0x0040;
+ *(__u16 *)CC_GPBCR = data;
+ *(__u8 *)CC_GPBDR = (*(__u8*)CC_GPBDR) & 0xf7;
+-#endif /* CONFIG_MIPS_ITE8172 */
+ }
++ }
++ spin_unlock_irqrestore(&vpp_lock, flags);
+ }
+-
+-const struct map_info basic_cstm_mips_ixx_map = {
+- NULL,
+- 0,
+- 0,
+- cstm_mips_ixx_read8,
+- cstm_mips_ixx_read16,
+- cstm_mips_ixx_read32,
+- cstm_mips_ixx_copy_from,
+- cstm_mips_ixx_write8,
+- cstm_mips_ixx_write16,
+- cstm_mips_ixx_write32,
+- cstm_mips_ixx_copy_to,
+- cstm_mips_ixx_set_vpp,
+- 0,
+- 0
+-};
++#endif
+
+ /* board and partition description */
+
+@@ -175,9 +124,9 @@
+ static struct mtd_partition cstm_mips_ixx_partitions[PHYSMAP_NUMBER][MAX_PHYSMAP_PARTITIONS] = {
+ { // 28F128J3A in 2x16 configuration
+ {
+- name: "main partition ",
+- size: 0x02000000, // 128 x 2 x 128k byte sectors
+- offset: 0,
++ .name = "main partition ",
++ .size = 0x02000000, // 128 x 2 x 128k byte sectors
++ .offset = 0,
+ },
+ },
+ };
+@@ -197,9 +146,9 @@
+ static struct mtd_partition cstm_mips_ixx_partitions[PHYSMAP_NUMBER][MAX_PHYSMAP_PARTITIONS] = {
+ {
+ {
+- name: "main partition",
+- size: CONFIG_MTD_CSTM_MIPS_IXX_LEN,
+- offset: 0,
++ .name = "main partition",
++ .size = CONFIG_MTD_CSTM_MIPS_IXX_LEN,
++ .offset = 0,
+ },
+ },
+ };
+@@ -216,17 +165,24 @@
+
+ /* Initialize mapping */
+ for (i=0;i<PHYSMAP_NUMBER;i++) {
+- printk(KERN_NOTICE "cstm_mips_ixx flash device: %lx at %lx\n", cstm_mips_ixx_board_desc[i].window_size, cstm_mips_ixx_board_desc[i].window_addr);
+- memcpy((char *)&cstm_mips_ixx_map[i],(char *)&basic_cstm_mips_ixx_map,sizeof(struct map_info));
+- cstm_mips_ixx_map[i].map_priv_1 = (unsigned long)ioremap(cstm_mips_ixx_board_desc[i].window_addr, cstm_mips_ixx_board_desc[i].window_size);
+- if (!cstm_mips_ixx_map[i].map_priv_1) {
++ printk(KERN_NOTICE "cstm_mips_ixx flash device: 0x%lx at 0x%lx\n",
++ cstm_mips_ixx_board_desc[i].window_size, cstm_mips_ixx_board_desc[i].window_addr);
++
++
++ cstm_mips_ixx_map[i].phys = cstm_mips_ixx_board_desc[i].window_addr;
++ cstm_mips_ixx_map[i].virt = (unsigned long)ioremap(cstm_mips_ixx_board_desc[i].window_addr, cstm_mips_ixx_board_desc[i].window_size);
++ if (!cstm_mips_ixx_map[i].virt) {
+ printk(KERN_WARNING "Failed to ioremap\n");
+ return -EIO;
+ }
+ cstm_mips_ixx_map[i].name = cstm_mips_ixx_board_desc[i].name;
+ cstm_mips_ixx_map[i].size = cstm_mips_ixx_board_desc[i].window_size;
+ cstm_mips_ixx_map[i].buswidth = cstm_mips_ixx_board_desc[i].buswidth;
+- //printk(KERN_NOTICE "cstm_mips_ixx: ioremap is %x\n",(unsigned int)(cstm_mips_ixx_map[i].map_priv_1));
++#if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR)
++ cstm_mips_ixx_map[i].set_vpp = cstm_mips_ixx_set_vpp;
++#endif
++ simple_map_init(&cstm_mips_ixx_map[i]);
++ //printk(KERN_NOTICE "cstm_mips_ixx: ioremap is %x\n",(unsigned int)(cstm_mips_ixx_map[i].virt));
+ }
+
+ #if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR)
+@@ -244,7 +200,7 @@
+ printk(KERN_NOTICE "cstm_mips_ixx %d jedec: mymtd is %x\n",i,(unsigned int)mymtd);
+ }
+ if (mymtd) {
+- mymtd->module = THIS_MODULE;
++ mymtd->owner = THIS_MODULE;
+
+ cstm_mips_ixx_map[i].map_priv_2 = (unsigned long)mymtd;
+ add_mtd_partitions(mymtd, parts, cstm_mips_ixx_board_desc[i].num_partitions);
+@@ -266,9 +222,9 @@
+ del_mtd_partitions(mymtd);
+ map_destroy(mymtd);
+ }
+- if (cstm_mips_ixx_map[i].map_priv_1) {
+- iounmap((void *)cstm_mips_ixx_map[i].map_priv_1);
+- cstm_mips_ixx_map[i].map_priv_1 = 0;
++ if (cstm_mips_ixx_map[i].virt) {
++ iounmap((void *)cstm_mips_ixx_map[i].virt);
++ cstm_mips_ixx_map[i].virt = 0;
+ }
+ }
+ }
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/maps/db1x00-flash.c linux/drivers/mtd/maps/db1x00-flash.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/maps/db1x00-flash.c 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/maps/db1x00-flash.c 2004-11-17 18:17:59.067309664 +0100
+@@ -8,6 +8,7 @@
+ #include <linux/config.h>
+ #include <linux/module.h>
+ #include <linux/types.h>
++#include <linux/init.h>
+ #include <linux/kernel.h>
+
+ #include <linux/mtd/mtd.h>
+@@ -29,76 +30,6 @@
+ static unsigned long flash_size;
+
+ static BCSR * const bcsr = (BCSR *)0xAE000000;
+-
+-__u8 physmap_read8(struct map_info *map, unsigned long ofs)
+-{
+- __u8 ret;
+- ret = __raw_readb(map->map_priv_1 + ofs);
+- DBG("read8 from %x, %x\n", (unsigned)(map->map_priv_1 + ofs), ret);
+- return ret;
+-}
+-
+-__u16 physmap_read16(struct map_info *map, unsigned long ofs)
+-{
+- __u16 ret;
+- ret = __raw_readw(map->map_priv_1 + ofs);
+- DBG("read16 from %x, %x\n", (unsigned)(map->map_priv_1 + ofs), ret);
+- return ret;
+-}
+-
+-__u32 physmap_read32(struct map_info *map, unsigned long ofs)
+-{
+- __u32 ret;
+- ret = __raw_readl(map->map_priv_1 + ofs);
+- DBG("read32 from %x, %x\n", (unsigned)(map->map_priv_1 + ofs), ret);
+- return ret;
+-}
+-
+-void physmap_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+-{
+- DBG("physmap_copy from %x to %x\n", (unsigned)from, (unsigned)to);
+- memcpy_fromio(to, map->map_priv_1 + from, len);
+-}
+-
+-void physmap_write8(struct map_info *map, __u8 d, unsigned long adr)
+-{
+- DBG("write8 at %x, %x\n", (unsigned)(map->map_priv_1 + adr), d);
+- __raw_writeb(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-void physmap_write16(struct map_info *map, __u16 d, unsigned long adr)
+-{
+- DBG("write16 at %x, %x\n", (unsigned)(map->map_priv_1 + adr), d);
+- __raw_writew(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-void physmap_write32(struct map_info *map, __u32 d, unsigned long adr)
+-{
+- DBG("write32 at %x, %x\n", (unsigned)(map->map_priv_1 + adr), d);
+- __raw_writel(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-void physmap_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+-{
+- DBG("physmap_copy_to %x from %x\n", (unsigned)to, (unsigned)from);
+- memcpy_toio(map->map_priv_1 + to, from, len);
+-}
+-
+-static struct map_info db1x00_map = {
+- name: "Db1x00 flash",
+- read8: physmap_read8,
+- read16: physmap_read16,
+- read32: physmap_read32,
+- copy_from: physmap_copy_from,
+- write8: physmap_write8,
+- write16: physmap_write16,
+- write32: physmap_write32,
+- copy_to: physmap_copy_to,
+-};
+-
+ static unsigned char flash_buswidth = 4;
+
+ /*
+@@ -115,58 +46,62 @@
+ */
+ static struct mtd_partition db1x00_partitions[] = {
+ {
+- name: "User FS",
+- size: 0x1c00000,
+- offset: 0x0000000
++ .name = "User FS",
++ .size = 0x1c00000,
++ .offset = 0x0000000
+ },{
+- name: "yamon",
+- size: 0x0100000,
+- offset: MTDPART_OFS_APPEND,
+- mask_flags: MTD_WRITEABLE
++ .name = "yamon",
++ .size = 0x0100000,
++ .offset = MTDPART_OFS_APPEND,
++ .mask_flags = MTD_WRITEABLE
+ },{
+- name: "raw kernel",
+- size: (0x300000-0x40000), /* last 256KB is yamon env */
+- offset: MTDPART_OFS_APPEND,
++ .name = "raw kernel",
++ .size = (0x300000-0x40000), /* last 256KB is env */
++ .offset = MTDPART_OFS_APPEND,
+ }
+ };
+ #elif defined(DB1X00_BOOT_ONLY)
+ static struct mtd_partition db1x00_partitions[] = {
+ {
+- name: "User FS",
+- size: 0x00c00000,
+- offset: 0x0000000
++ .name = "User FS",
++ .size = 0x00c00000,
++ .offset = 0x0000000
+ },{
+- name: "yamon",
+- size: 0x0100000,
+- offset: MTDPART_OFS_APPEND,
+- mask_flags: MTD_WRITEABLE
++ .name = "yamon",
++ .size = 0x0100000,
++ .offset = MTDPART_OFS_APPEND,
++ .mask_flags = MTD_WRITEABLE
+ },{
+- name: "raw kernel",
+- size: (0x300000-0x40000), /* last 256KB is yamon env */
+- offset: MTDPART_OFS_APPEND,
++ .name = "raw kernel",
++ .size = (0x300000-0x40000), /* last 256KB is env */
++ .offset = MTDPART_OFS_APPEND,
+ }
+ };
+ #elif defined(DB1X00_USER_ONLY)
+ static struct mtd_partition db1x00_partitions[] = {
+ {
+- name: "User FS",
+- size: 0x0e00000,
+- offset: 0x0000000
++ .name = "User FS",
++ .size = 0x0e00000,
++ .offset = 0x0000000
+ },{
+- name: "raw kernel",
+- size: MTDPART_SIZ_FULL,
+- offset: MTDPART_OFS_APPEND,
++ .name = "raw kernel",
++ .size = MTDPART_SIZ_FULL,
++ .offset = MTDPART_OFS_APPEND,
+ }
+ };
+ #else
+ #error MTD_DB1X00 define combo error /* should never happen */
+ #endif
++#define NB_OF(x) (sizeof(x)/sizeof(x[0]))
+
++#define NAME "Db1x00 Linux Flash"
+
+-#define NB_OF(x) (sizeof(x)/sizeof(x[0]))
++static struct map_info db1xxx_mtd_map = {
++ .name = NAME,
++};
+
+ static struct mtd_partition *parsed_parts;
+-static struct mtd_info *mymtd;
++static struct mtd_info *db1xxx_mtd;
+
+ /*
+ * Probe the flash density and setup window address and size
+@@ -174,7 +109,7 @@
+ * want the MTD driver to be probing the boot or user flash,
+ * so having the option to enable only one bank is important.
+ */
+-int setup_flash_params()
++int setup_flash_params(void)
+ {
+ switch ((bcsr->status >> 14) & 0x3) {
+ case 0: /* 64Mbit devices */
+@@ -228,6 +163,10 @@
+ default:
+ return 1;
+ }
++ db1xxx_mtd_map.size = window_size;
++ db1xxx_mtd_map.buswidth = flash_buswidth;
++ db1xxx_mtd_map.phys = window_addr;
++ db1xxx_mtd_map.buswidth = flash_buswidth;
+ return 0;
+ }
+
+@@ -235,10 +174,6 @@
+ {
+ struct mtd_partition *parts;
+ int nb_parts = 0;
+- char *part_type;
+-
+- /* Default flash buswidth */
+- db1x00_map.buswidth = flash_buswidth;
+
+ if (setup_flash_params())
+ return -ENXIO;
+@@ -246,32 +181,29 @@
+ /*
+ * Static partition definition selection
+ */
+- part_type = "static";
+ parts = db1x00_partitions;
+ nb_parts = NB_OF(db1x00_partitions);
+- db1x00_map.size = window_size;
+
+ /*
+ * Now let's probe for the actual flash. Do it here since
+ * specific machine settings might have been set above.
+ */
+ printk(KERN_NOTICE "Db1xxx flash: probing %d-bit flash bus\n",
+- db1x00_map.buswidth*8);
+- db1x00_map.map_priv_1 =
+- (unsigned long)ioremap(window_addr, window_size);
+- mymtd = do_map_probe("cfi_probe", &db1x00_map);
+- if (!mymtd) return -ENXIO;
+- mymtd->module = THIS_MODULE;
++ db1xxx_mtd_map.buswidth*8);
++ db1xxx_mtd_map.virt = (unsigned long)ioremap(window_addr, window_size);
++ db1xxx_mtd = do_map_probe("cfi_probe", &db1xxx_mtd_map);
++ if (!db1xxx_mtd) return -ENXIO;
++ db1xxx_mtd->owner = THIS_MODULE;
+
+- add_mtd_partitions(mymtd, parts, nb_parts);
++ add_mtd_partitions(db1xxx_mtd, parts, nb_parts);
+ return 0;
+ }
+
+ static void __exit db1x00_mtd_cleanup(void)
+ {
+- if (mymtd) {
+- del_mtd_partitions(mymtd);
+- map_destroy(mymtd);
++ if (db1xxx_mtd) {
++ del_mtd_partitions(db1xxx_mtd);
++ map_destroy(db1xxx_mtd);
+ if (parsed_parts)
+ kfree(parsed_parts);
+ }
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/maps/dbox2-flash.c linux/drivers/mtd/maps/dbox2-flash.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/maps/dbox2-flash.c 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/maps/dbox2-flash.c 2004-11-17 18:17:59.068309512 +0100
+@@ -1,12 +1,13 @@
+ /*
+- * $Id$
++ * $Id$
+ *
+- * Nokia / Sagem D-Box 2 flash driver
++ * D-Box 2 flash driver
+ */
+
+ #include <linux/module.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
+ #include <asm/io.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+@@ -16,22 +17,44 @@
+ /* partition_info gives details on the logical partitions that the split the
+ * single flash device into. If the size if zero we use up to the end of the
+ * device. */
+-static struct mtd_partition partition_info[]= {{name: "BR bootloader", // raw
+- size: 128 * 1024,
+- offset: 0,
+- mask_flags: MTD_WRITEABLE},
+- {name: "PPC bootloader", // flfs
+- size: 128 * 1024,
+- offset: MTDPART_OFS_APPEND,
+- mask_flags: 0},
+- {name: "Kernel", // idxfs
+- size: 768 * 1024,
+- offset: MTDPART_OFS_APPEND,
+- mask_flags: 0},
+- {name: "System", // jffs
+- size: MTDPART_SIZ_FULL,
+- offset: MTDPART_OFS_APPEND,
+- mask_flags: 0}};
++static struct mtd_partition partition_info[]= {
++ {
++ .name = "BR bootloader",
++ .size = 128 * 1024,
++ .offset = 0,
++ .mask_flags = MTD_WRITEABLE
++ },
++ {
++ .name = "flfs (ppcboot)",
++ .size = 128 * 1024,
++ .offset = MTDPART_OFS_APPEND,
++ .mask_flags = 0
++ },
++ {
++ .name = "root (cramfs)",
++ .size = 7040 * 1024,
++ .offset = MTDPART_OFS_APPEND,
++ .mask_flags = 0
++ },
++ {
++ .name = "var (jffs2)",
++ .size = 896 * 1024,
++ .offset = MTDPART_OFS_APPEND,
++ .mask_flags = 0
++ },
++ {
++ .name = "flash without bootloader",
++ .size = MTDPART_SIZ_FULL,
++ .offset = 128 * 1024,
++ .mask_flags = 0
++ },
++ {
++ .name = "complete flash",
++ .size = MTDPART_SIZ_FULL,
++ .offset = 0,
++ .mask_flags = MTD_WRITEABLE
++ }
++};
+
+ #define NUM_PARTITIONS (sizeof(partition_info) / sizeof(partition_info[0]))
+
+@@ -40,72 +63,24 @@
+
+ static struct mtd_info *mymtd;
+
+-__u8 dbox2_flash_read8(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readb(map->map_priv_1 + ofs);
+-}
+-
+-__u16 dbox2_flash_read16(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readw(map->map_priv_1 + ofs);
+-}
+-
+-__u32 dbox2_flash_read32(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readl(map->map_priv_1 + ofs);
+-}
+-
+-void dbox2_flash_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+-{
+- memcpy_fromio(to, map->map_priv_1 + from, len);
+-}
+-
+-void dbox2_flash_write8(struct map_info *map, __u8 d, unsigned long adr)
+-{
+- __raw_writeb(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-void dbox2_flash_write16(struct map_info *map, __u16 d, unsigned long adr)
+-{
+- __raw_writew(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-void dbox2_flash_write32(struct map_info *map, __u32 d, unsigned long adr)
+-{
+- __raw_writel(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-void dbox2_flash_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+-{
+- memcpy_toio(map->map_priv_1 + to, from, len);
+-}
+
+ struct map_info dbox2_flash_map = {
+- name: "D-Box 2 flash memory",
+- size: WINDOW_SIZE,
+- buswidth: 4,
+- read8: dbox2_flash_read8,
+- read16: dbox2_flash_read16,
+- read32: dbox2_flash_read32,
+- copy_from: dbox2_flash_copy_from,
+- write8: dbox2_flash_write8,
+- write16: dbox2_flash_write16,
+- write32: dbox2_flash_write32,
+- copy_to: dbox2_flash_copy_to
++ .name = "D-Box 2 flash memory",
++ .size = WINDOW_SIZE,
++ .buswidth = 4,
++ .phys = WINDOW_ADDR,
+ };
+
+ int __init init_dbox2_flash(void)
+ {
+ printk(KERN_NOTICE "D-Box 2 flash driver (size->0x%X mem->0x%X)\n", WINDOW_SIZE, WINDOW_ADDR);
+- dbox2_flash_map.map_priv_1 = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE);
++ dbox2_flash_map.virt = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE);
+
+- if (!dbox2_flash_map.map_priv_1) {
++ if (!dbox2_flash_map.virt) {
+ printk("Failed to ioremap\n");
+ return -EIO;
+ }
++ simple_map_init(&dbox2_flash_map);
+
+ // Probe for dual Intel 28F320 or dual AMD
+ mymtd = do_map_probe("cfi_probe", &dbox2_flash_map);
+@@ -117,7 +92,7 @@
+ }
+
+ if (mymtd) {
+- mymtd->module = THIS_MODULE;
++ mymtd->owner = THIS_MODULE;
+
+ /* Create MTD devices for each partition. */
+ add_mtd_partitions(mymtd, partition_info, NUM_PARTITIONS);
+@@ -125,7 +100,7 @@
+ return 0;
+ }
+
+- iounmap((void *)dbox2_flash_map.map_priv_1);
++ iounmap((void *)dbox2_flash_map.virt);
+ return -ENXIO;
+ }
+
+@@ -135,9 +110,9 @@
+ del_mtd_partitions(mymtd);
+ map_destroy(mymtd);
+ }
+- if (dbox2_flash_map.map_priv_1) {
+- iounmap((void *)dbox2_flash_map.map_priv_1);
+- dbox2_flash_map.map_priv_1 = 0;
++ if (dbox2_flash_map.virt) {
++ iounmap((void *)dbox2_flash_map.virt);
++ dbox2_flash_map.virt = 0;
+ }
+ }
+
+@@ -146,5 +121,5 @@
+
+
+ MODULE_LICENSE("GPL");
+-MODULE_AUTHOR("Kári Davíðsson <kd@flaga.is>");
+-MODULE_DESCRIPTION("MTD map driver for Nokia/Sagem D-Box 2 board");
++MODULE_AUTHOR("Kári Davíðsson <kd@flaga.is>, Bastian Blank <waldi@tuxbox.org>, Alexander Wild <wild@te-elektronik.com>");
++MODULE_DESCRIPTION("MTD map driver for D-Box 2 board");
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/maps/dc21285.c linux/drivers/mtd/maps/dc21285.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/maps/dc21285.c 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/maps/dc21285.c 2004-11-17 18:17:59.069309360 +0100
+@@ -5,12 +5,13 @@
+ *
+ * This code is GPL
+ *
+- * $Id$
++ * $Id$
+ */
+ #include <linux/config.h>
+ #include <linux/module.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
+
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+@@ -92,26 +93,42 @@
+ }
+
+ struct map_info dc21285_map = {
+- name: "DC21285 flash",
+- size: 16*1024*1024,
+- read8: dc21285_read8,
+- read16: dc21285_read16,
+- read32: dc21285_read32,
+- copy_from: dc21285_copy_from,
+- write8: dc21285_write8,
+- write16: dc21285_write16,
+- write32: dc21285_write32,
+- copy_to: dc21285_copy_to
++ .name = "DC21285 flash",
++ .phys = NO_XIP,
++ .size = 16*1024*1024,
++ .read8 = dc21285_read8,
++ .read16 = dc21285_read16,
++ .read32 = dc21285_read32,
++ .copy_from = dc21285_copy_from,
++ .write8 = dc21285_write8,
++ .write16 = dc21285_write16,
++ .write32 = dc21285_write32,
++ .copy_to = dc21285_copy_to
+ };
+
+
+ /* Partition stuff */
+ static struct mtd_partition *dc21285_parts;
+-
+-extern int parse_redboot_partitions(struct mtd_info *, struct mtd_partition **);
++#ifdef CONFIG_MTD_PARTITIONS
++static const char *probes[] = { "RedBoot", "cmdlinepart", NULL };
++#endif
+
+ int __init init_dc21285(void)
+ {
++
++ /*
++ * Flash timing is determined with bits 19-16 of the
++ * CSR_SA110_CNTL. The value is the number of wait cycles, or
++ * 0 for 16 cycles (the default). Cycles are 20 ns.
++ * Here we use 7 for 140 ns flash chips.
++ */
++ /* access time */
++ *CSR_SA110_CNTL = ((*CSR_SA110_CNTL & ~0x000f0000) | (7 << 16));
++ /* burst time */
++ *CSR_SA110_CNTL = ((*CSR_SA110_CNTL & ~0x00f00000) | (7 << 20));
++ /* tristate time */
++ *CSR_SA110_CNTL = ((*CSR_SA110_CNTL & ~0x0f000000) | (7 << 24));
++
+ /* Determine buswidth */
+ switch (*CSR_SA110_CNTL & (3<<14)) {
+ case SA110_CNTL_ROMWIDTH_8:
+@@ -141,33 +158,18 @@
+ if (mymtd) {
+ int nrparts = 0;
+
+- mymtd->module = THIS_MODULE;
++ mymtd->owner = THIS_MODULE;
+
+ /* partition fixup */
+
+-#ifdef CONFIG_MTD_REDBOOT_PARTS
+- nrparts = parse_redboot_partitions(mymtd, &dc21285_parts);
+-#endif
++#ifdef CONFIG_MTD_PARTITIONS
++ nrparts = parse_mtd_partitions(mymtd, probes, &dc21285_parts, (void *)0);
+ if (nrparts > 0) {
+ add_mtd_partitions(mymtd, dc21285_parts, nrparts);
+- } else if (nrparts == 0) {
+- printk(KERN_NOTICE "RedBoot partition table failed\n");
+- add_mtd_device(mymtd);
++ return 0;
+ }
+-
+- /*
+- * Flash timing is determined with bits 19-16 of the
+- * CSR_SA110_CNTL. The value is the number of wait cycles, or
+- * 0 for 16 cycles (the default). Cycles are 20 ns.
+- * Here we use 7 for 140 ns flash chips.
+- */
+- /* access time */
+- *CSR_SA110_CNTL = ((*CSR_SA110_CNTL & ~0x000f0000) | (7 << 16));
+- /* burst time */
+- *CSR_SA110_CNTL = ((*CSR_SA110_CNTL & ~0x00f00000) | (7 << 20));
+- /* tristate time */
+- *CSR_SA110_CNTL = ((*CSR_SA110_CNTL & ~0x0f000000) | (7 << 24));
+-
++#endif
++ add_mtd_device(mymtd);
+ return 0;
+ }
+
+@@ -177,17 +179,16 @@
+
+ static void __exit cleanup_dc21285(void)
+ {
+- if (mymtd) {
++#ifdef CONFIG_MTD_PARTITIONS
++ if (dc21285_parts) {
++ del_mtd_partitions(mymtd);
++ kfree(dc21285_parts);
++ } else
++#endif
+ del_mtd_device(mymtd);
++
+ map_destroy(mymtd);
+- mymtd = NULL;
+- }
+- if (dc21285_map.map_priv_1) {
+ iounmap((void *)dc21285_map.map_priv_1);
+- dc21285_map.map_priv_1 = 0;
+- }
+- if(dc21285_parts)
+- kfree(dc21285_parts);
+ }
+
+ module_init(init_dc21285);
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/maps/dilnetpc.c linux/drivers/mtd/maps/dilnetpc.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/maps/dilnetpc.c 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/maps/dilnetpc.c 2004-11-17 18:17:59.071309056 +0100
+@@ -14,7 +14,7 @@
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+- * $Id$
++ * $Id$
+ *
+ * The DIL/Net PC is a tiny embedded PC board made by SSV Embedded Systems
+ * featuring the AMD Elan SC410 processor. There are two variants of this
+@@ -29,6 +29,7 @@
+ #include <linux/module.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
+ #include <asm/io.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+@@ -36,7 +37,7 @@
+ #include <linux/mtd/concat.h>
+
+ /*
+-** The DIL/NetPC keeps it's BIOS in two distinct flash blocks.
++** The DIL/NetPC keeps its BIOS in two distinct flash blocks.
+ ** Destroying any of these blocks transforms the DNPC into
+ ** a paperweight (albeit not a very useful one, considering
+ ** it only weighs a few grams).
+@@ -189,45 +190,6 @@
+ }
+
+
+-static __u8 dnpc_read8(struct map_info *map, unsigned long ofs)
+-{
+- return readb(map->map_priv_1 + ofs);
+-}
+-
+-static __u16 dnpc_read16(struct map_info *map, unsigned long ofs)
+-{
+- return readw(map->map_priv_1 + ofs);
+-}
+-
+-static __u32 dnpc_read32(struct map_info *map, unsigned long ofs)
+-{
+- return readl(map->map_priv_1 + ofs);
+-}
+-
+-static void dnpc_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+-{
+- memcpy_fromio(to, (void *)(map->map_priv_1 + from), len);
+-}
+-
+-static void dnpc_write8(struct map_info *map, __u8 d, unsigned long adr)
+-{
+- writeb(d, map->map_priv_1 + adr);
+-}
+-
+-static void dnpc_write16(struct map_info *map, __u16 d, unsigned long adr)
+-{
+- writew(d, map->map_priv_1 + adr);
+-}
+-
+-static void dnpc_write32(struct map_info *map, __u32 d, unsigned long adr)
+-{
+- writel(d, map->map_priv_1 + adr);
+-}
+-
+-static void dnpc_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+-{
+- memcpy_toio((void *)(map->map_priv_1 + to), from, len);
+-}
+
+ /*
+ ************************************************************
+@@ -288,19 +250,11 @@
+ #define WINDOW_ADDR FLASH_BASE
+
+ static struct map_info dnpc_map = {
+- name: "ADNP Flash Bank",
+- size: ADNP_WINDOW_SIZE,
+- buswidth: 1,
+- read8: dnpc_read8,
+- read16: dnpc_read16,
+- read32: dnpc_read32,
+- copy_from: dnpc_copy_from,
+- write8: dnpc_write8,
+- write16: dnpc_write16,
+- write32: dnpc_write32,
+- copy_to: dnpc_copy_to,
+- set_vpp: adnp_set_vpp,
+- map_priv_2: WINDOW_ADDR
++ .name = "ADNP Flash Bank",
++ .size = ADNP_WINDOW_SIZE,
++ .buswidth = 1,
++ .set_vpp = adnp_set_vpp,
++ .phys = WINDOW_ADDR
+ };
+
+ /*
+@@ -316,29 +270,29 @@
+ static struct mtd_partition partition_info[]=
+ {
+ {
+- name: "ADNP boot",
+- offset: 0,
+- size: 0xf0000,
++ .name = "ADNP boot",
++ .offset = 0,
++ .size = 0xf0000,
+ },
+ {
+- name: "ADNP system BIOS",
+- offset: MTDPART_OFS_NXTBLK,
+- size: 0x10000,
++ .name = "ADNP system BIOS",
++ .offset = MTDPART_OFS_NXTBLK,
++ .size = 0x10000,
+ #ifdef DNPC_BIOS_BLOCKS_WRITEPROTECTED
+- mask_flags: MTD_WRITEABLE,
++ .mask_flags = MTD_WRITEABLE,
+ #endif
+ },
+ {
+- name: "ADNP file system",
+- offset: MTDPART_OFS_NXTBLK,
+- size: 0x2f0000,
++ .name = "ADNP file system",
++ .offset = MTDPART_OFS_NXTBLK,
++ .size = 0x2f0000,
+ },
+ {
+- name: "ADNP system BIOS entry",
+- offset: MTDPART_OFS_NXTBLK,
+- size: MTDPART_SIZ_FULL,
++ .name = "ADNP system BIOS entry",
++ .offset = MTDPART_OFS_NXTBLK,
++ .size = MTDPART_SIZ_FULL,
+ #ifdef DNPC_BIOS_BLOCKS_WRITEPROTECTED
+- mask_flags: MTD_WRITEABLE,
++ .mask_flags = MTD_WRITEABLE,
+ #endif
+ },
+ };
+@@ -369,21 +323,21 @@
+ static struct mtd_partition higlvl_partition_info[]=
+ {
+ {
+- name: "ADNP boot block",
+- offset: 0,
+- size: CONFIG_MTD_DILNETPC_BOOTSIZE,
++ .name = "ADNP boot block",
++ .offset = 0,
++ .size = CONFIG_MTD_DILNETPC_BOOTSIZE,
+ },
+ {
+- name: "ADNP file system space",
+- offset: MTDPART_OFS_NXTBLK,
+- size: ADNP_WINDOW_SIZE-CONFIG_MTD_DILNETPC_BOOTSIZE-0x20000,
++ .name = "ADNP file system space",
++ .offset = MTDPART_OFS_NXTBLK,
++ .size = ADNP_WINDOW_SIZE-CONFIG_MTD_DILNETPC_BOOTSIZE-0x20000,
+ },
+ {
+- name: "ADNP system BIOS + BIOS Entry",
+- offset: MTDPART_OFS_NXTBLK,
+- size: MTDPART_SIZ_FULL,
++ .name = "ADNP system BIOS + BIOS Entry",
++ .offset = MTDPART_OFS_NXTBLK,
++ .size = MTDPART_SIZ_FULL,
+ #ifdef DNPC_BIOS_BLOCKS_WRITEPROTECTED
+- mask_flags: MTD_WRITEABLE,
++ .mask_flags = MTD_WRITEABLE,
+ #endif
+ },
+ };
+@@ -447,18 +401,19 @@
+ }
+
+ printk(KERN_NOTICE "DIL/Net %s flash: 0x%lx at 0x%lx\n",
+- is_dnp ? "DNPC" : "ADNP", dnpc_map.size, dnpc_map.map_priv_2);
++ is_dnp ? "DNPC" : "ADNP", dnpc_map.size, dnpc_map.phys);
+
+- dnpc_map.map_priv_1 = (unsigned long)ioremap_nocache(dnpc_map.map_priv_2, dnpc_map.size);
++ dnpc_map.virt = (unsigned long)ioremap_nocache(dnpc_map.phys, dnpc_map.size);
+
+- dnpc_map_flash(dnpc_map.map_priv_2, dnpc_map.size);
++ dnpc_map_flash(dnpc_map.phys, dnpc_map.size);
+
+- if (!dnpc_map.map_priv_1) {
++ if (!dnpc_map.virt) {
+ printk("Failed to ioremap_nocache\n");
+ return -EIO;
+ }
++ simple_map_init(&dnpc_map);
+
+- printk("FLASH virtual address: 0x%lx\n", dnpc_map.map_priv_1);
++ printk("FLASH virtual address: 0x%lx\n", dnpc_map.virt);
+
+ mymtd = do_map_probe("jedec_probe", &dnpc_map);
+
+@@ -475,11 +430,11 @@
+ mymtd->erasesize = 0x10000;
+
+ if (!mymtd) {
+- iounmap((void *)dnpc_map.map_priv_1);
++ iounmap((void *)dnpc_map.virt);
+ return -ENXIO;
+ }
+
+- mymtd->module = THIS_MODULE;
++ mymtd->owner = THIS_MODULE;
+
+ /*
+ ** Supply pointers to lowlvl_parts[] array to add_mtd_partitions()
+@@ -525,10 +480,10 @@
+ del_mtd_partitions(mymtd);
+ map_destroy(mymtd);
+ }
+- if (dnpc_map.map_priv_1) {
+- iounmap((void *)dnpc_map.map_priv_1);
++ if (dnpc_map.virt) {
++ iounmap((void *)dnpc_map.virt);
+ dnpc_unmap_flash();
+- dnpc_map.map_priv_1 = 0;
++ dnpc_map.virt = 0;
+ }
+ }
+
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/maps/ebony.c linux/drivers/mtd/maps/ebony.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/maps/ebony.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/mtd/maps/ebony.c 2004-11-17 18:17:59.000000000 +0100
+@@ -0,0 +1,164 @@
++/*
++ * $Id$
++ *
++ * Mapping for Ebony user flash
++ *
++ * Matt Porter <mporter@mvista.com>
++ *
++ * Copyright 2002 MontaVista Software Inc.
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version.
++ */
++
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/map.h>
++#include <linux/mtd/partitions.h>
++#include <linux/config.h>
++#include <asm/io.h>
++#include <asm/ibm440.h>
++#include <platforms/ebony.h>
++
++static struct mtd_info *flash;
++
++static struct map_info ebony_small_map = {
++ .name = "Ebony small flash",
++ .size = EBONY_SMALL_FLASH_SIZE,
++ .buswidth = 1,
++};
++
++static struct map_info ebony_large_map = {
++ .name = "Ebony large flash",
++ .size = EBONY_LARGE_FLASH_SIZE,
++ .buswidth = 1,
++};
++
++static struct mtd_partition ebony_small_partitions[] = {
++ {
++ .name = "OpenBIOS",
++ .offset = 0x0,
++ .size = 0x80000,
++ }
++};
++
++static struct mtd_partition ebony_large_partitions[] = {
++ {
++ .name = "fs",
++ .offset = 0,
++ .size = 0x380000,
++ },
++ {
++ .name = "firmware",
++ .offset = 0x380000,
++ .size = 0x80000,
++ }
++};
++
++int __init init_ebony(void)
++{
++ u8 fpga0_reg;
++ unsigned long fpga0_adr;
++ unsigned long long small_flash_base, large_flash_base;
++
++ fpga0_adr = ioremap64(EBONY_FPGA_ADDR, 16);
++ if (!fpga0_adr)
++ return -ENOMEM;
++
++ fpga0_reg = readb(fpga0_adr);
++ iounmap64(fpga0_adr);
++
++ if (EBONY_BOOT_SMALL_FLASH(fpga0_reg) &&
++ !EBONY_FLASH_SEL(fpga0_reg))
++ small_flash_base = EBONY_SMALL_FLASH_HIGH2;
++ else if (EBONY_BOOT_SMALL_FLASH(fpga0_reg) &&
++ EBONY_FLASH_SEL(fpga0_reg))
++ small_flash_base = EBONY_SMALL_FLASH_HIGH1;
++ else if (!EBONY_BOOT_SMALL_FLASH(fpga0_reg) &&
++ !EBONY_FLASH_SEL(fpga0_reg))
++ small_flash_base = EBONY_SMALL_FLASH_LOW2;
++ else
++ small_flash_base = EBONY_SMALL_FLASH_LOW1;
++
++ if (EBONY_BOOT_SMALL_FLASH(fpga0_reg) &&
++ !EBONY_ONBRD_FLASH_EN(fpga0_reg))
++ large_flash_base = EBONY_LARGE_FLASH_LOW;
++ else
++ large_flash_base = EBONY_LARGE_FLASH_HIGH;
++
++ ebony_small_map.phys = small_flash_base;
++ ebony_small_map.virt =
++ (unsigned long)ioremap64(small_flash_base,
++ ebony_small_map.size);
++
++ if (!ebony_small_map.virt) {
++ printk("Failed to ioremap flash\n");
++ return -EIO;
++ }
++
++ simple_map_init(&ebony_small_map);
++
++ flash = do_map_probe("map_rom", &ebony_small_map);
++ if (flash) {
++ flash->owner = THIS_MODULE;
++ add_mtd_partitions(flash, ebony_small_partitions,
++ ARRAY_SIZE(ebony_small_partitions));
++ } else {
++ printk("map probe failed for flash\n");
++ return -ENXIO;
++ }
++
++ ebony_large_map.phys = large_flash_base;
++ ebony_large_map.virt =
++ (unsigned long)ioremap64(large_flash_base,
++ ebony_large_map.size);
++
++ if (!ebony_large_map.virt) {
++ printk("Failed to ioremap flash\n");
++ return -EIO;
++ }
++
++ simple_map_init(&ebony_large_map);
++
++ flash = do_map_probe("cfi_probe", &ebony_large_map);
++ if (flash) {
++ flash->owner = THIS_MODULE;
++ add_mtd_partitions(flash, ebony_large_partitions,
++ ARRAY_SIZE(ebony_large_partitions));
++ } else {
++ printk("map probe failed for flash\n");
++ return -ENXIO;
++ }
++
++ return 0;
++}
++
++static void __exit cleanup_ebony(void)
++{
++ if (flash) {
++ del_mtd_partitions(flash);
++ map_destroy(flash);
++ }
++
++ if (ebony_small_map.virt) {
++ iounmap((void *)ebony_small_map.virt);
++ ebony_small_map.virt = 0;
++ }
++
++ if (ebony_large_map.virt) {
++ iounmap((void *)ebony_large_map.virt);
++ ebony_large_map.virt = 0;
++ }
++}
++
++module_init(init_ebony);
++module_exit(cleanup_ebony);
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Matt Porter <mporter@mvista.com>");
++MODULE_DESCRIPTION("MTD map and partitions for IBM 440GP Ebony boards");
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/maps/edb7312.c linux/drivers/mtd/maps/edb7312.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/maps/edb7312.c 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/maps/edb7312.c 2004-11-17 18:17:59.073308752 +0100
+@@ -1,5 +1,5 @@
+ /*
+- * $Id$
++ * $Id$
+ *
+ * Handle mapping of the NOR flash on Cogent EDB7312 boards
+ *
+@@ -13,6 +13,7 @@
+ #include <linux/module.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
+ #include <asm/io.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+@@ -35,61 +36,11 @@
+
+ static struct mtd_info *mymtd;
+
+-__u8 edb7312nor_read8(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readb(map->map_priv_1 + ofs);
+-}
+-
+-__u16 edb7312nor_read16(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readw(map->map_priv_1 + ofs);
+-}
+-
+-__u32 edb7312nor_read32(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readl(map->map_priv_1 + ofs);
+-}
+-
+-void edb7312nor_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+-{
+- memcpy_fromio(to, map->map_priv_1 + from, len);
+-}
+-
+-void edb7312nor_write8(struct map_info *map, __u8 d, unsigned long adr)
+-{
+- __raw_writeb(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-void edb7312nor_write16(struct map_info *map, __u16 d, unsigned long adr)
+-{
+- __raw_writew(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-void edb7312nor_write32(struct map_info *map, __u32 d, unsigned long adr)
+-{
+- __raw_writel(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-void edb7312nor_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+-{
+- memcpy_toio(map->map_priv_1 + to, from, len);
+-}
+-
+ struct map_info edb7312nor_map = {
+- name: "NOR flash on EDB7312",
+- size: WINDOW_SIZE,
+- buswidth: BUSWIDTH,
+- read8: edb7312nor_read8,
+- read16: edb7312nor_read16,
+- read32: edb7312nor_read32,
+- copy_from: edb7312nor_copy_from,
+- write8: edb7312nor_write8,
+- write16: edb7312nor_write16,
+- write32: edb7312nor_write32,
+- copy_to: edb7312nor_copy_to
++ .name = "NOR flash on EDB7312",
++ .size = WINDOW_SIZE,
++ .buswidth = BUSWIDTH,
++ .phys = WINDOW_ADDR,
+ };
+
+ #ifdef CONFIG_MTD_PARTITIONS
+@@ -100,29 +51,23 @@
+ static struct mtd_partition static_partitions[3] =
+ {
+ {
+- name: "ARMboot",
+- size: 0x40000,
+- offset: 0
++ .name = "ARMboot",
++ .size = 0x40000,
++ .offset = 0
+ },
+ {
+- name: "Kernel",
+- size: 0x200000,
+- offset: 0x40000
++ .name = "Kernel",
++ .size = 0x200000,
++ .offset = 0x40000
+ },
+ {
+- name: "RootFS",
+- size: 0xDC0000,
+- offset: 0x240000
++ .name = "RootFS",
++ .size = 0xDC0000,
++ .offset = 0x240000
+ },
+ };
+
+-#define NB_OF(x) (sizeof (x) / sizeof (x[0]))
+-
+-#ifdef CONFIG_MTD_CMDLINE_PARTS
+-int parse_cmdline_partitions(struct mtd_info *master,
+- struct mtd_partition **pparts,
+- const char *mtd_id);
+-#endif
++static const char *probes[] = { "RedBoot", "cmdlinepart", NULL };
+
+ #endif
+
+@@ -137,32 +82,33 @@
+
+ printk(KERN_NOTICE MSG_PREFIX "0x%08x at 0x%08x\n",
+ WINDOW_SIZE, WINDOW_ADDR);
+- edb7312nor_map.map_priv_1 = (unsigned long)
++ edb7312nor_map.virt = (unsigned long)
+ ioremap(WINDOW_ADDR, WINDOW_SIZE);
+
+- if (!edb7312nor_map.map_priv_1) {
++ if (!edb7312nor_map.virt) {
+ printk(MSG_PREFIX "failed to ioremap\n");
+ return -EIO;
+ }
+
++ simple_map_init(&edb7312nor_map);
++
+ mymtd = 0;
+ type = rom_probe_types;
+ for(; !mymtd && *type; type++) {
+ mymtd = do_map_probe(*type, &edb7312nor_map);
+ }
+ if (mymtd) {
+- mymtd->module = THIS_MODULE;
++ mymtd->owner = THIS_MODULE;
+
+ #ifdef CONFIG_MTD_PARTITIONS
+-#ifdef CONFIG_MTD_CMDLINE_PARTS
+- mtd_parts_nb = parse_cmdline_partitions(mymtd, &mtd_parts, MTDID);
++ mtd_parts_nb = parse_mtd_partitions(mymtd, probes, &mtd_parts, MTDID);
+ if (mtd_parts_nb > 0)
+- part_type = "command line";
+-#endif
++ part_type = "detected";
++
+ if (mtd_parts_nb == 0)
+ {
+ mtd_parts = static_partitions;
+- mtd_parts_nb = NB_OF(static_partitions);
++ mtd_parts_nb = ARRAY_SIZE(static_partitions);
+ part_type = "static";
+ }
+ #endif
+@@ -178,7 +124,7 @@
+ return 0;
+ }
+
+- iounmap((void *)edb7312nor_map.map_priv_1);
++ iounmap((void *)edb7312nor_map.virt);
+ return -ENXIO;
+ }
+
+@@ -188,9 +134,9 @@
+ del_mtd_device(mymtd);
+ map_destroy(mymtd);
+ }
+- if (edb7312nor_map.map_priv_1) {
+- iounmap((void *)edb7312nor_map.map_priv_1);
+- edb7312nor_map.map_priv_1 = 0;
++ if (edb7312nor_map.virt) {
++ iounmap((void *)edb7312nor_map.virt);
++ edb7312nor_map.virt = 0;
+ }
+ }
+
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/maps/elan-104nc.c linux/drivers/mtd/maps/elan-104nc.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/maps/elan-104nc.c 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/maps/elan-104nc.c 2004-11-17 18:17:59.074308600 +0100
+@@ -16,7 +16,7 @@
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+
+- $Id$
++ $Id$
+
+ The ELAN-104NC has up to 8 Mibyte of Intel StrataFlash (28F320/28F640) in x16
+ mode. This drivers uses the CFI probe and Intel Extended Command Set drivers.
+@@ -40,6 +40,7 @@
+ #include <asm/io.h>
+
+ #include <linux/mtd/map.h>
++#include <linux/mtd/mtd.h>
+ #include <linux/mtd/partitions.h>
+
+ #define WINDOW_START 0xb0000
+@@ -59,14 +60,14 @@
+ * single flash device into. If the size if zero we use up to the end of the
+ * device. */
+ static struct mtd_partition partition_info[]={
+- { name: "ELAN-104NC flash boot partition",
+- offset: 0,
+- size: 640*1024 },
+- { name: "ELAN-104NC flash partition 1",
+- offset: 640*1024,
+- size: 896*1024 },
+- { name: "ELAN-104NC flash partition 2",
+- offset: (640+896)*1024 }
++ { .name = "ELAN-104NC flash boot partition",
++ .offset = 0,
++ .size = 640*1024 },
++ { .name = "ELAN-104NC flash partition 1",
++ .offset = 640*1024,
++ .size = 896*1024 },
++ { .name = "ELAN-104NC flash partition 2",
++ .offset = (640+896)*1024 }
+ };
+ #define NUM_PARTITIONS (sizeof(partition_info)/sizeof(partition_info[0]))
+
+@@ -195,19 +196,20 @@
+ }
+
+ static struct map_info elan_104nc_map = {
+- name: "ELAN-104NC flash",
+- size: 8*1024*1024, /* this must be set to a maximum possible amount
++ .name = "ELAN-104NC flash",
++ .phys = NO_XIP,
++ .size = 8*1024*1024, /* this must be set to a maximum possible amount
+ of flash so the cfi probe routines find all
+ the chips */
+- buswidth: 2,
+- read8: elan_104nc_read8,
+- read16: elan_104nc_read16,
+- read32: elan_104nc_read32,
+- copy_from: elan_104nc_copy_from,
+- write8: elan_104nc_write8,
+- write16: elan_104nc_write16,
+- write32: elan_104nc_write32,
+- copy_to: elan_104nc_copy_to
++ .buswidth = 2,
++ .read8 = elan_104nc_read8,
++ .read16 = elan_104nc_read16,
++ .read32 = elan_104nc_read32,
++ .copy_from = elan_104nc_copy_from,
++ .write8 = elan_104nc_write8,
++ .write16 = elan_104nc_write16,
++ .write32 = elan_104nc_write32,
++ .copy_to = elan_104nc_copy_to
+ };
+
+ /* MTD device for all of the flash. */
+@@ -221,20 +223,13 @@
+ }
+
+ iounmap((void *)iomapadr);
+- release_region(PAGE_IO,PAGE_IO_SIZE);
+ }
+
+ int __init init_elan_104nc(void)
+ {
+- /* Urg! We use I/O port 0x22 without request_region()ing it */
+- /*
+- if (check_region(PAGE_IO,PAGE_IO_SIZE) != 0) {
+- printk( KERN_ERR"%s: IO ports 0x%x-0x%x in use\n",
+- elan_104nc_map.name,
+- PAGE_IO, PAGE_IO+PAGE_IO_SIZE-1 );
+- return -EAGAIN;
+- }
+- */
++ /* Urg! We use I/O port 0x22 without request_region()ing it,
++ because it's already allocated to the PIC. */
++
+ iomapadr = (unsigned long)ioremap(WINDOW_START, WINDOW_LENGTH);
+ if (!iomapadr) {
+ printk( KERN_ERR"%s: failed to ioremap memory region\n",
+@@ -242,10 +237,6 @@
+ return -EIO;
+ }
+
+- /*
+- request_region( PAGE_IO, PAGE_IO_SIZE, "ELAN-104NC flash" );
+- */
+-
+ printk( KERN_INFO"%s: IO:0x%x-0x%x MEM:0x%x-0x%x\n",
+ elan_104nc_map.name,
+ PAGE_IO, PAGE_IO+PAGE_IO_SIZE-1,
+@@ -260,7 +251,7 @@
+ return -ENXIO;
+ }
+
+- all_mtd->module=THIS_MODULE;
++ all_mtd->owner = THIS_MODULE;
+
+ /* Create MTD devices for each partition. */
+ add_mtd_partitions( all_mtd, partition_info, NUM_PARTITIONS );
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/maps/epxa10db-flash.c linux/drivers/mtd/maps/epxa10db-flash.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/maps/epxa10db-flash.c 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/maps/epxa10db-flash.c 2004-11-17 18:17:59.079307840 +0100
+@@ -5,7 +5,7 @@
+ * Copyright (C) 2001 Altera Corporation
+ * Copyright (C) 2001 Red Hat, Inc.
+ *
+- * $Id$
++ * $Id$
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -26,6 +26,7 @@
+ #include <linux/module.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
+ #include <asm/io.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+@@ -43,87 +44,38 @@
+
+ static struct mtd_info *mymtd;
+
+-extern int parse_redboot_partitions(struct mtd_info *, struct mtd_partition **);
+ static int epxa_default_partitions(struct mtd_info *master, struct mtd_partition **pparts);
+
+-static __u8 epxa_read8(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readb(map->map_priv_1 + ofs);
+-}
+-
+-static __u16 epxa_read16(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readw(map->map_priv_1 + ofs);
+-}
+-
+-static __u32 epxa_read32(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readl(map->map_priv_1 + ofs);
+-}
+-
+-static void epxa_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+-{
+- memcpy_fromio(to, (void *)(map->map_priv_1 + from), len);
+-}
+-
+-static void epxa_write8(struct map_info *map, __u8 d, unsigned long adr)
+-{
+- __raw_writeb(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-static void epxa_write16(struct map_info *map, __u16 d, unsigned long adr)
+-{
+- __raw_writew(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-static void epxa_write32(struct map_info *map, __u32 d, unsigned long adr)
+-{
+- __raw_writel(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-static void epxa_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+-{
+- memcpy_toio((void *)(map->map_priv_1 + to), from, len);
+-}
+-
+-
+
+ static struct map_info epxa_map = {
+- name: "EPXA flash",
+- size: FLASH_SIZE,
+- buswidth: 2,
+- read8: epxa_read8,
+- read16: epxa_read16,
+- read32: epxa_read32,
+- copy_from: epxa_copy_from,
+- write8: epxa_write8,
+- write16: epxa_write16,
+- write32: epxa_write32,
+- copy_to: epxa_copy_to
++ .name = "EPXA flash",
++ .size = FLASH_SIZE,
++ .buswidth = 2,
++ .phys = FLASH_START,
+ };
+
++static const char *probes[] = { "RedBoot", "afs", NULL };
+
+ static int __init epxa_mtd_init(void)
+ {
+ int i;
+
+- printk(KERN_NOTICE "%s flash device: %x at %x\n", BOARD_NAME, FLASH_SIZE, FLASH_START);
+- epxa_map.map_priv_1 = (unsigned long)ioremap(FLASH_START, FLASH_SIZE);
+- if (!epxa_map.map_priv_1) {
++ printk(KERN_NOTICE "%s flash device: 0x%x at 0x%x\n", BOARD_NAME, FLASH_SIZE, FLASH_START);
++
++ epxa_map.virt = (unsigned long)ioremap(FLASH_START, FLASH_SIZE);
++ if (!epxa_map.virt) {
+ printk("Failed to ioremap %s flash\n",BOARD_NAME);
+ return -EIO;
+ }
++ simple_map_init(&epxa_map);
+
+ mymtd = do_map_probe("cfi_probe", &epxa_map);
+ if (!mymtd) {
+- iounmap((void *)epxa_map.map_priv_1);
++ iounmap((void *)epxa_map.virt);
+ return -ENXIO;
+ }
+
+- mymtd->module = THIS_MODULE;
++ mymtd->owner = THIS_MODULE;
+
+ /* Unlock the flash device. */
+ if(mymtd->unlock){
+@@ -135,23 +87,14 @@
+ }
+ }
+
+-#ifdef CONFIG_MTD_REDBOOT_PARTS
+- nr_parts = parse_redboot_partitions(mymtd, &parts);
+-
+- if (nr_parts > 0) {
+- add_mtd_partitions(mymtd, parts, nr_parts);
+- return 0;
+- }
+-#endif
+-#ifdef CONFIG_MTD_AFS_PARTS
+- nr_parts = parse_afs_partitions(mymtd, &parts);
++#ifdef CONFIG_MTD_PARTITIONS
++ nr_parts = parse_mtd_partitions(mymtd, probes, &parts, 0);
+
+ if (nr_parts > 0) {
+ add_mtd_partitions(mymtd, parts, nr_parts);
+ return 0;
+ }
+ #endif
+-
+ /* No recognised partitioning schemes found - use defaults */
+ nr_parts = epxa_default_partitions(mymtd, &parts);
+ if (nr_parts > 0) {
+@@ -173,9 +116,9 @@
+ del_mtd_device(mymtd);
+ map_destroy(mymtd);
+ }
+- if (epxa_map.map_priv_1) {
+- iounmap((void *)epxa_map.map_priv_1);
+- epxa_map.map_priv_1 = 0;
++ if (epxa_map.virt) {
++ iounmap((void *)epxa_map.virt);
++ epxa_map.virt = 0;
+ }
+ }
+
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/maps/fortunet.c linux/drivers/mtd/maps/fortunet.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/maps/fortunet.c 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/maps/fortunet.c 2004-11-17 18:17:59.080307688 +0100
+@@ -1,11 +1,12 @@
+ /* fortunet.c memory map
+ *
+- * $Id$
++ * $Id$
+ */
+
+ #include <linux/module.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
+ #include <asm/io.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+@@ -23,7 +24,7 @@
+
+ struct map_region
+ {
+- int window_addr_phyical;
++ int window_addr_physical;
+ int altbuswidth;
+ struct map_info map_info;
+ struct mtd_info *mymtd;
+@@ -37,57 +38,10 @@
+ static int map_regions_parts[MAX_NUM_REGIONS] = {0,0,0,0};
+
+
+-__u8 fortunet_read8(struct map_info *map, unsigned long ofs)
+-{
+- return *(__u8 *)(map->map_priv_1 + ofs);
+-}
+-
+-__u16 fortunet_read16(struct map_info *map, unsigned long ofs)
+-{
+- return *(__u16 *)(map->map_priv_1 + ofs);
+-}
+-
+-__u32 fortunet_read32(struct map_info *map, unsigned long ofs)
+-{
+- return *(__u32 *)(map->map_priv_1 + ofs);
+-}
+-
+-void fortunet_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+-{
+- memcpy(to, (void *)(map->map_priv_1 + from), len);
+-}
+-
+-void fortunet_write8(struct map_info *map, __u8 d, unsigned long adr)
+-{
+- *(__u8 *)(map->map_priv_1 + adr) = d;
+-}
+-
+-void fortunet_write16(struct map_info *map, __u16 d, unsigned long adr)
+-{
+- *(__u16 *)(map->map_priv_1 + adr) = d;
+-}
+-
+-void fortunet_write32(struct map_info *map, __u32 d, unsigned long adr)
+-{
+- *(__u32 *)(map->map_priv_1 + adr) = d;
+-}
+-
+-void fortunet_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+-{
+- memcpy((void *)(map->map_priv_1 + to), from, len);
+-}
+
+ struct map_info default_map = {
+- size: DEF_WINDOW_SIZE,
+- buswidth: 4,
+- read8: fortunet_read8,
+- read16: fortunet_read16,
+- read32: fortunet_read32,
+- copy_from: fortunet_copy_from,
+- write8: fortunet_write8,
+- write16: fortunet_write16,
+- write32: fortunet_write32,
+- copy_to: fortunet_copy_to
++ .size = DEF_WINDOW_SIZE,
++ .buswidth = 4,
+ };
+
+ static char * __init get_string_option(char *dest,int dest_size,char *sor)
+@@ -147,7 +101,7 @@
+ get_options (get_string_option(string,sizeof(string),line),6,params);
+ if(params[0]<1)
+ {
+- printk(MTD_FORTUNET_PK "Bad paramters for MTD Region "
++ printk(MTD_FORTUNET_PK "Bad parameters for MTD Region "
+ " name,region-number[,base,size,buswidth,altbuswidth]\n");
+ return 1;
+ }
+@@ -161,14 +115,14 @@
+ memcpy(&map_regions[params[1]].map_info,
+ &default_map,sizeof(map_regions[params[1]].map_info));
+ map_regions_set[params[1]] = 1;
+- map_regions[params[1]].window_addr_phyical = DEF_WINDOW_ADDR_PHY;
++ map_regions[params[1]].window_addr_physical = DEF_WINDOW_ADDR_PHY;
+ map_regions[params[1]].altbuswidth = 2;
+ map_regions[params[1]].mymtd = NULL;
+ map_regions[params[1]].map_info.name = map_regions[params[1]].map_name;
+ strcpy(map_regions[params[1]].map_info.name,string);
+ if(params[0]>1)
+ {
+- map_regions[params[1]].window_addr_phyical = params[2];
++ map_regions[params[1]].window_addr_physical = params[2];
+ }
+ if(params[0]>2)
+ {
+@@ -185,14 +139,14 @@
+ return 1;
+ }
+
+-static int __init MTD_New_Partion(char *line)
++static int __init MTD_New_Partition(char *line)
+ {
+ char string[MAX_NAME_SIZE];
+ int params[4];
+ get_options (get_string_option(string,sizeof(string),line),4,params);
+ if(params[0]<3)
+ {
+- printk(MTD_FORTUNET_PK "Bad paramters for MTD Partion "
++ printk(MTD_FORTUNET_PK "Bad parameters for MTD Partition "
+ " name,region-number,size,offset\n");
+ return 1;
+ }
+@@ -204,7 +158,7 @@
+ }
+ if(map_regions_parts[params[1]]>=MAX_NUM_PARTITIONS)
+ {
+- printk(MTD_FORTUNET_PK "Out of space for partion in this region\n");
++ printk(MTD_FORTUNET_PK "Out of space for partition in this region\n");
+ return 1;
+ }
+ map_regions[params[1]].parts[map_regions_parts[params[1]]].name =
+@@ -220,7 +174,10 @@
+ }
+
+ __setup("MTD_Region=", MTD_New_Region);
+-__setup("MTD_Partion=", MTD_New_Partion);
++__setup("MTD_Partition=", MTD_New_Partition);
++
++/* Backwards-spelling-compatibility */
++__setup("MTD_Partion=", MTD_New_Partition);
+
+ int __init init_fortunet(void)
+ {
+@@ -229,13 +186,13 @@
+ {
+ if(map_regions_parts[ix]&&(!map_regions_set[ix]))
+ {
+- printk(MTD_FORTUNET_PK "Region %d is not setup (Seting to default)\n",
++ printk(MTD_FORTUNET_PK "Region %d is not setup (Setting to default)\n",
+ ix);
+ memset(&map_regions[ix],0,sizeof(map_regions[ix]));
+ memcpy(&map_regions[ix].map_info,&default_map,
+ sizeof(map_regions[ix].map_info));
+ map_regions_set[ix] = 1;
+- map_regions[ix].window_addr_phyical = DEF_WINDOW_ADDR_PHY;
++ map_regions[ix].window_addr_physical = DEF_WINDOW_ADDR_PHY;
+ map_regions[ix].altbuswidth = 2;
+ map_regions[ix].mymtd = NULL;
+ map_regions[ix].map_info.name = map_regions[ix].map_name;
+@@ -244,30 +201,35 @@
+ if(map_regions_set[ix])
+ {
+ iy++;
+- printk(KERN_NOTICE MTD_FORTUNET_PK "%s flash device at phyicaly "
++ printk(KERN_NOTICE MTD_FORTUNET_PK "%s flash device at physically "
+ " address %x size %x\n",
+ map_regions[ix].map_info.name,
+- map_regions[ix].window_addr_phyical,
++ map_regions[ix].window_addr_physical,
+ map_regions[ix].map_info.size);
+- map_regions[ix].map_info.map_priv_1 =
++
++ map_regions[ix].map_info.phys = map_regions[ix].window_addr_physical,
++
++ map_regions[ix].map_info.virt =
+ (int)ioremap_nocache(
+- map_regions[ix].window_addr_phyical,
++ map_regions[ix].window_addr_physical,
+ map_regions[ix].map_info.size);
+- if(!map_regions[ix].map_info.map_priv_1)
++ if(!map_regions[ix].map_info.virt)
+ {
+ printk(MTD_FORTUNET_PK "%s flash failed to ioremap!\n",
+ map_regions[ix].map_info.name);
+ return -ENXIO;
+ }
+- printk(KERN_NOTICE MTD_FORTUNET_PK "%s flash is veritualy at: %x\n",
++ simple_map_init(&map_regions[ix].map_info);
++
++ printk(KERN_NOTICE MTD_FORTUNET_PK "%s flash is virtually at: %x\n",
+ map_regions[ix].map_info.name,
+- map_regions[ix].map_info.map_priv_1);
++ map_regions[ix].map_info.virt);
+ map_regions[ix].mymtd = do_map_probe("cfi_probe",
+ &map_regions[ix].map_info);
+ if((!map_regions[ix].mymtd)&&(
+ map_regions[ix].altbuswidth!=map_regions[ix].map_info.buswidth))
+ {
+- printk(KERN_NOTICE MTD_FORTUNET_PK "Trying alternet buswidth "
++ printk(KERN_NOTICE MTD_FORTUNET_PK "Trying alternate buswidth "
+ "for %s flash.\n",
+ map_regions[ix].map_info.name);
+ map_regions[ix].map_info.buswidth =
+@@ -275,7 +237,7 @@
+ map_regions[ix].mymtd = do_map_probe("cfi_probe",
+ &map_regions[ix].map_info);
+ }
+- map_regions[ix].mymtd->module = THIS_MODULE;
++ map_regions[ix].mymtd->owner = THIS_MODULE;
+ add_mtd_partitions(map_regions[ix].mymtd,
+ map_regions[ix].parts,map_regions_parts[ix]);
+ }
+@@ -297,7 +259,7 @@
+ del_mtd_partitions( map_regions[ix].mymtd );
+ map_destroy( map_regions[ix].mymtd );
+ }
+- iounmap((void *)map_regions[ix].map_info.map_priv_1);
++ iounmap((void *)map_regions[ix].map_info.virt);
+ }
+ }
+ }
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/maps/h720x-flash.c linux/drivers/mtd/maps/h720x-flash.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/maps/h720x-flash.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/mtd/maps/h720x-flash.c 2004-11-17 18:17:59.000000000 +0100
+@@ -0,0 +1,142 @@
++/*
++ * Flash memory access on Hynix GMS30C7201/HMS30C7202 based
++ * evaluation boards
++ *
++ * (C) 2002 Jungjun Kim <jungjun.kim@hynix.com>
++ * 2003 Thomas Gleixner <tglx@linutronix.de>
++*/
++
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/errno.h>
++#include <linux/slab.h>
++
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/map.h>
++#include <linux/mtd/partitions.h>
++#include <asm/hardware.h>
++#include <asm/io.h>
++
++static struct mtd_info *mymtd;
++
++static struct map_info h720x_map = {
++ .name = "H720X",
++ .buswidth = 4,
++ .size = FLASH_SIZE,
++ .phys = FLASH_PHYS,
++};
++
++static struct mtd_partition h720x_partitions[] = {
++ {
++ .name = "ArMon",
++ .size = 0x00080000,
++ .offset = 0,
++ .mask_flags = MTD_WRITEABLE
++ },{
++ .name = "Env",
++ .size = 0x00040000,
++ .offset = 0x00080000,
++ .mask_flags = MTD_WRITEABLE
++ },{
++ .name = "Kernel",
++ .size = 0x00180000,
++ .offset = 0x000c0000,
++ .mask_flags = MTD_WRITEABLE
++ },{
++ .name = "Ramdisk",
++ .size = 0x00400000,
++ .offset = 0x00240000,
++ .mask_flags = MTD_WRITEABLE
++ },{
++ .name = "jffs2",
++ .size = MTDPART_SIZ_FULL,
++ .offset = MTDPART_OFS_APPEND
++ }
++};
++
++#define NUM_PARTITIONS (sizeof(h720x_partitions)/sizeof(h720x_partitions[0]))
++
++static int nr_mtd_parts;
++static struct mtd_partition *mtd_parts;
++static const char *probes[] = { "cmdlinepart", NULL };
++
++/*
++ * Initialize FLASH support
++ */
++int __init h720x_mtd_init(void)
++{
++
++ char *part_type = NULL;
++
++ h720x_map.virt = (unsigned long)ioremap(FLASH_PHYS, FLASH_SIZE);
++
++ if (!h720x_map.virt) {
++ printk(KERN_ERR "H720x-MTD: ioremap failed\n");
++ return -EIO;
++ }
++
++ simple_map_init(&h720x_map);
++
++ // Probe for flash buswidth 4
++ printk (KERN_INFO "H720x-MTD probing 32bit FLASH\n");
++ mymtd = do_map_probe("cfi_probe", &h720x_map);
++ if (!mymtd) {
++ printk (KERN_INFO "H720x-MTD probing 16bit FLASH\n");
++ // Probe for buswidth 2
++ h720x_map.buswidth = 2;
++ mymtd = do_map_probe("cfi_probe", &h720x_map);
++ }
++
++ if (mymtd) {
++ mymtd->owner = THIS_MODULE;
++
++#ifdef CONFIG_MTD_PARTITIONS
++ nr_mtd_parts = parse_mtd_partitions(mymtd, probes, &mtd_parts, 0);
++ if (nr_mtd_parts > 0)
++ part_type = "command line";
++#endif
++ if (nr_mtd_parts <= 0) {
++ mtd_parts = h720x_partitions;
++ nr_mtd_parts = NUM_PARTITIONS;
++ part_type = "builtin";
++ }
++ printk(KERN_INFO "Using %s partition table\n", part_type);
++ add_mtd_partitions(mymtd, mtd_parts, nr_mtd_parts);
++ return 0;
++ }
++
++ iounmap((void *)h720x_map.virt);
++ return -ENXIO;
++}
++
++/*
++ * Cleanup
++ */
++static void __exit h720x_mtd_cleanup(void)
++{
++
++ if (mymtd) {
++ del_mtd_partitions(mymtd);
++ map_destroy(mymtd);
++ }
++
++ /* Free partition info, if commandline partition was used */
++ if (mtd_parts && (mtd_parts != h720x_partitions))
++ kfree (mtd_parts);
++
++ if (h720x_map.virt) {
++ iounmap((void *)h720x_map.virt);
++ h720x_map.virt = 0;
++ }
++}
++
++
++module_init(h720x_mtd_init);
++module_exit(h720x_mtd_cleanup);
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Thomas Gleixner <tglx@linutronix.de>");
++MODULE_DESCRIPTION("MTD map driver for Hynix evaluation boards");
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/maps/ichxrom.c linux/drivers/mtd/maps/ichxrom.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/maps/ichxrom.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/mtd/maps/ichxrom.c 2004-11-17 18:17:59.000000000 +0100
+@@ -0,0 +1,380 @@
++/*
++ * ichxrom.c
++ *
++ * Normal mappings of chips in physical memory
++ * $Id$
++ */
++
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <asm/io.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/map.h>
++#include <linux/config.h>
++#include <linux/pci.h>
++#include <linux/pci_ids.h>
++
++#define xstr(s) str(s)
++#define str(s) #s
++#define MOD_NAME xstr(KBUILD_BASENAME)
++
++#define MTD_DEV_NAME_LENGTH 16
++
++#define RESERVE_MEM_REGION 0
++
++#define ICHX_FWH_REGION_START 0xFF000000UL
++#define ICHX_FWH_REGION_SIZE 0x01000000UL
++#define BIOS_CNTL 0x4e
++#define FWH_DEC_EN1 0xE3
++#define FWH_DEC_EN2 0xF0
++#define FWH_SEL1 0xE8
++#define FWH_SEL2 0xEE
++
++struct ichxrom_map_info {
++ struct map_info map;
++ struct mtd_info *mtd;
++ unsigned long window_addr;
++ struct pci_dev *pdev;
++ struct resource window_rsrc;
++ struct resource rom_rsrc;
++ char mtd_name[MTD_DEV_NAME_LENGTH];
++};
++
++static inline unsigned long addr(struct map_info *map, unsigned long ofs)
++{
++ unsigned long offset;
++ offset = ((8*1024*1024) - map->size) + ofs;
++ if (offset >= (4*1024*1024)) {
++ offset += 0x400000;
++ }
++ return map->map_priv_1 + 0x400000 + offset;
++}
++
++static inline unsigned long dbg_addr(struct map_info *map, unsigned long addr)
++{
++ return addr - map->map_priv_1 + ICHX_FWH_REGION_START;
++}
++
++static __u8 ichxrom_read8(struct map_info *map, unsigned long ofs)
++{
++ return __raw_readb(addr(map, ofs));
++}
++
++static __u16 ichxrom_read16(struct map_info *map, unsigned long ofs)
++{
++ return __raw_readw(addr(map, ofs));
++}
++
++static __u32 ichxrom_read32(struct map_info *map, unsigned long ofs)
++{
++ return __raw_readl(addr(map, ofs));
++}
++
++static void ichxrom_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
++{
++ memcpy_fromio(to, addr(map, from), len);
++}
++
++static void ichxrom_write8(struct map_info *map, __u8 d, unsigned long ofs)
++{
++ __raw_writeb(d, addr(map,ofs));
++ mb();
++}
++
++static void ichxrom_write16(struct map_info *map, __u16 d, unsigned long ofs)
++{
++ __raw_writew(d, addr(map, ofs));
++ mb();
++}
++
++static void ichxrom_write32(struct map_info *map, __u32 d, unsigned long ofs)
++{
++ __raw_writel(d, addr(map, ofs));
++ mb();
++}
++
++static void ichxrom_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
++{
++ memcpy_toio(addr(map, to), from, len);
++}
++
++static struct ichxrom_map_info ichxrom_map = {
++ .map = {
++ .name = MOD_NAME,
++ .phys = NO_XIP,
++ .size = 0,
++ .buswidth = 1,
++ .read8 = ichxrom_read8,
++ .read16 = ichxrom_read16,
++ .read32 = ichxrom_read32,
++ .copy_from = ichxrom_copy_from,
++ .write8 = ichxrom_write8,
++ .write16 = ichxrom_write16,
++ .write32 = ichxrom_write32,
++ .copy_to = ichxrom_copy_to,
++ /* Firmware hubs only use vpp when being programmed
++ * in a factory setting. So in-place programming
++ * needs to use a different method.
++ */
++ },
++ /* remaining fields of structure are initialized to 0 */
++};
++
++enum fwh_lock_state {
++ FWH_DENY_WRITE = 1,
++ FWH_IMMUTABLE = 2,
++ FWH_DENY_READ = 4,
++};
++
++static void ichxrom_cleanup(struct ichxrom_map_info *info)
++{
++ u16 word;
++
++ /* Disable writes through the rom window */
++ pci_read_config_word(info->pdev, BIOS_CNTL, &word);
++ pci_write_config_word(info->pdev, BIOS_CNTL, word & ~1);
++
++ if (info->mtd) {
++ del_mtd_device(info->mtd);
++ map_destroy(info->mtd);
++ info->mtd = NULL;
++ info->map.virt = 0;
++ }
++ if (info->rom_rsrc.parent)
++ release_resource(&info->rom_rsrc);
++ if (info->window_rsrc.parent)
++ release_resource(&info->window_rsrc);
++
++ if (info->window_addr) {
++ iounmap((void *)(info->window_addr));
++ info->window_addr = 0;
++ }
++}
++
++
++static int ichxrom_set_lock_state(struct mtd_info *mtd, loff_t ofs, size_t len,
++ enum fwh_lock_state state)
++{
++ struct map_info *map = mtd->priv;
++ unsigned long start = ofs;
++ unsigned long end = start + len -1;
++
++ /* FIXME do I need to guard against concurrency here? */
++ /* round down to 64K boundaries */
++ start = start & ~0xFFFF;
++ end = end & ~0xFFFF;
++ while (start <= end) {
++ unsigned long ctrl_addr;
++ ctrl_addr = addr(map, start) - 0x400000 + 2;
++ writeb(state, ctrl_addr);
++ start = start + 0x10000;
++ }
++ return 0;
++}
++
++static int ichxrom_lock(struct mtd_info *mtd, loff_t ofs, size_t len)
++{
++ return ichxrom_set_lock_state(mtd, ofs, len, FWH_DENY_WRITE);
++}
++
++static int ichxrom_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
++{
++ return ichxrom_set_lock_state(mtd, ofs, len, 0);
++}
++
++static int __devinit ichxrom_init_one (struct pci_dev *pdev,
++ const struct pci_device_id *ent)
++{
++ u16 word;
++ struct ichxrom_map_info *info = &ichxrom_map;
++ unsigned long map_size;
++
++ /* For now I just handle the ichx and I assume there
++ * are not a lot of resources up at the top of the address
++ * space. It is possible to handle other devices in the
++ * top 16MB but it is very painful. Also since
++ * you can only really attach a FWH to an ICHX there
++ * a number of simplifications you can make.
++ *
++ * Also you can page firmware hubs if an 8MB window isn't enough
++ * but don't currently handle that case either.
++ */
++
++ info->pdev = pdev;
++
++ /*
++ * Try to reserve the window mem region. If this fails then
++ * it is likely due to the window being "reseved" by the BIOS.
++ */
++ info->window_rsrc.name = MOD_NAME;
++ info->window_rsrc.start = ICHX_FWH_REGION_START;
++ info->window_rsrc.end = ICHX_FWH_REGION_START + ICHX_FWH_REGION_SIZE - 1;
++ info->window_rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
++ if (request_resource(&iomem_resource, &info->window_rsrc)) {
++ info->window_rsrc.parent = NULL;
++ printk(KERN_ERR MOD_NAME
++ " %s(): Unable to register resource"
++ " 0x%.08lx-0x%.08lx - kernel bug?\n",
++ __func__,
++ info->window_rsrc.start, info->window_rsrc.end);
++ }
++
++ /* Enable writes through the rom window */
++ pci_read_config_word(pdev, BIOS_CNTL, &word);
++ if (!(word & 1) && (word & (1<<1))) {
++ /* The BIOS will generate an error if I enable
++ * this device, so don't even try.
++ */
++ printk(KERN_ERR MOD_NAME ": firmware access control, I can't enable writes\n");
++ goto failed;
++ }
++ pci_write_config_word(pdev, BIOS_CNTL, word | 1);
++
++
++ /* Map the firmware hub into my address space. */
++ /* Does this use too much virtual address space? */
++ info->window_addr = (unsigned long)ioremap(
++ ICHX_FWH_REGION_START, ICHX_FWH_REGION_SIZE);
++ if (!info->window_addr) {
++ printk(KERN_ERR "Failed to ioremap\n");
++ goto failed;
++ }
++
++ /* For now assume the firmware has setup all relevant firmware
++ * windows. We don't have enough information to handle this case
++ * intelligently.
++ */
++
++ /* FIXME select the firmware hub and enable a window to it. */
++
++ info->mtd = 0;
++ info->map.map_priv_1 = info->window_addr;
++
++ map_size = ICHX_FWH_REGION_SIZE;
++ while(!info->mtd && (map_size > 0)) {
++ info->map.size = map_size;
++ info->mtd = do_map_probe("jedec_probe", &ichxrom_map.map);
++ map_size -= 512*1024;
++ }
++ if (!info->mtd) {
++ goto failed;
++ }
++ /* I know I can only be a firmware hub here so put
++ * in the special lock and unlock routines.
++ */
++ info->mtd->lock = ichxrom_lock;
++ info->mtd->unlock = ichxrom_unlock;
++
++ info->mtd->owner = THIS_MODULE;
++ add_mtd_device(info->mtd);
++
++ if (info->window_rsrc.parent) {
++ /*
++ * Registering the MTD device in iomem may not be possible
++ * if there is a BIOS "reserved" and BUSY range. If this
++ * fails then continue anyway.
++ */
++ snprintf(info->mtd_name, MTD_DEV_NAME_LENGTH,
++ "mtd%d", info->mtd->index);
++
++ info->rom_rsrc.name = info->mtd_name;
++ info->rom_rsrc.start = ICHX_FWH_REGION_START
++ + ICHX_FWH_REGION_SIZE - map_size;
++ info->rom_rsrc.end = ICHX_FWH_REGION_START
++ + ICHX_FWH_REGION_SIZE;
++ info->rom_rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
++ if (request_resource(&info->window_rsrc, &info->rom_rsrc)) {
++ printk(KERN_ERR MOD_NAME
++ ": cannot reserve MTD resource\n");
++ info->rom_rsrc.parent = NULL;
++ }
++ }
++
++ return 0;
++
++ failed:
++ ichxrom_cleanup(info);
++ return -ENODEV;
++}
++
++
++static void __devexit ichxrom_remove_one (struct pci_dev *pdev)
++{
++ struct ichxrom_map_info *info = &ichxrom_map;
++ u16 word;
++
++ del_mtd_device(info->mtd);
++ map_destroy(info->mtd);
++ info->mtd = 0;
++ info->map.map_priv_1 = 0;
++
++ iounmap((void *)(info->window_addr));
++ info->window_addr = 0;
++
++ /* Disable writes through the rom window */
++ pci_read_config_word(pdev, BIOS_CNTL, &word);
++ pci_write_config_word(pdev, BIOS_CNTL, word & ~1);
++
++#if RESERVE_MEM_REGION
++ release_mem_region(ICHX_FWH_REGION_START, ICHX_FWH_REGION_SIZE);
++#endif
++}
++
++static struct pci_device_id ichxrom_pci_tbl[] __devinitdata = {
++ { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0,
++ PCI_ANY_ID, PCI_ANY_ID, },
++ { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_0,
++ PCI_ANY_ID, PCI_ANY_ID, },
++ { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_0,
++ PCI_ANY_ID, PCI_ANY_ID, },
++ { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_0,
++ PCI_ANY_ID, PCI_ANY_ID, },
++ { 0, },
++};
++
++MODULE_DEVICE_TABLE(pci, ichxrom_pci_tbl);
++
++#if 0
++static struct pci_driver ichxrom_driver = {
++ .name = MOD_NAME,
++ .id_table = ichxrom_pci_tbl,
++ .probe = ichxrom_init_one,
++ .remove = ichxrom_remove_one,
++};
++#endif
++
++static struct pci_dev *mydev;
++int __init init_ichxrom(void)
++{
++ struct pci_dev *pdev;
++ struct pci_device_id *id;
++ pdev = 0;
++ for(id = ichxrom_pci_tbl; id->vendor; id++) {
++ pdev = pci_find_device(id->vendor, id->device, 0);
++ if (pdev) {
++ break;
++ }
++ }
++ if (pdev) {
++ mydev = pdev;
++ return ichxrom_init_one(pdev, &ichxrom_pci_tbl[0]);
++ }
++ return -ENXIO;
++#if 0
++ return pci_module_init(&ichxrom_driver);
++#endif
++}
++
++static void __exit cleanup_ichxrom(void)
++{
++ ichxrom_remove_one(mydev);
++}
++
++module_init(init_ichxrom);
++module_exit(cleanup_ichxrom);
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Eric Biederman <ebiederman@lnxi.com>");
++MODULE_DESCRIPTION("MTD map driver for BIOS chips on the ICHX southbridge");
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/maps/impa7.c linux/drivers/mtd/maps/impa7.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/maps/impa7.c 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/maps/impa7.c 2004-11-17 18:17:59.084307080 +0100
+@@ -1,5 +1,5 @@
+ /*
+- * $Id$
++ * $Id$
+ *
+ * Handle mapping of the NOR flash on implementa A7 boards
+ *
+@@ -13,6 +13,7 @@
+ #include <linux/module.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
+ #include <asm/io.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+@@ -37,75 +38,17 @@
+
+ static struct mtd_info *impa7_mtd[NUM_FLASHBANKS] = { 0 };
+
+-__u8 impa7_read8(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readb(map->map_priv_1 + ofs);
+-}
+-
+-__u16 impa7_read16(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readw(map->map_priv_1 + ofs);
+-}
+-
+-__u32 impa7_read32(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readl(map->map_priv_1 + ofs);
+-}
+-
+-void impa7_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+-{
+- memcpy_fromio(to, map->map_priv_1 + from, len);
+-}
+-
+-void impa7_write8(struct map_info *map, __u8 d, unsigned long adr)
+-{
+- __raw_writeb(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-void impa7_write16(struct map_info *map, __u16 d, unsigned long adr)
+-{
+- __raw_writew(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-void impa7_write32(struct map_info *map, __u32 d, unsigned long adr)
+-{
+- __raw_writel(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-void impa7_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+-{
+- memcpy_toio(map->map_priv_1 + to, from, len);
+-}
+
+ static struct map_info impa7_map[NUM_FLASHBANKS] = {
+ {
+- name: "impA7 NOR Flash Bank #0",
+- size: WINDOW_SIZE0,
+- buswidth: BUSWIDTH,
+- read8: impa7_read8,
+- read16: impa7_read16,
+- read32: impa7_read32,
+- copy_from: impa7_copy_from,
+- write8: impa7_write8,
+- write16: impa7_write16,
+- write32: impa7_write32,
+- copy_to: impa7_copy_to
++ .name = "impA7 NOR Flash Bank #0",
++ .size = WINDOW_SIZE0,
++ .buswidth = BUSWIDTH,
+ },
+ {
+- name: "impA7 NOR Flash Bank #1",
+- size: WINDOW_SIZE1,
+- buswidth: BUSWIDTH,
+- read8: impa7_read8,
+- read16: impa7_read16,
+- read32: impa7_read32,
+- copy_from: impa7_copy_from,
+- write8: impa7_write8,
+- write16: impa7_write16,
+- write32: impa7_write32,
+- copy_to: impa7_copy_to
++ .name = "impA7 NOR Flash Bank #1",
++ .size = WINDOW_SIZE1,
++ .buswidth = BUSWIDTH,
+ },
+ };
+
+@@ -117,24 +60,18 @@
+ static struct mtd_partition static_partitions[] =
+ {
+ {
+- name: "FileSystem",
+- size: 0x800000,
+- offset: 0x00000000
++ .name = "FileSystem",
++ .size = 0x800000,
++ .offset = 0x00000000
+ },
+ };
+
+-#define NB_OF(x) (sizeof (x) / sizeof (x[0]))
++static int mtd_parts_nb[NUM_FLASHBANKS];
++static struct mtd_partition *mtd_parts[NUM_FLASHBANKS];
+
+-#ifdef CONFIG_MTD_CMDLINE_PARTS
+-int parse_cmdline_partitions(struct mtd_info *master,
+- struct mtd_partition **pparts,
+- const char *mtd_id);
+ #endif
+
+-#endif
+-
+-static int mtd_parts_nb = 0;
+-static struct mtd_partition *mtd_parts = 0;
++static const char *probes[] = { "cmdlinepart", NULL };
+
+ int __init init_impa7(void)
+ {
+@@ -146,20 +83,21 @@
+ { WINDOW_ADDR0, WINDOW_SIZE0 },
+ { WINDOW_ADDR1, WINDOW_SIZE1 },
+ };
+- char mtdid[10];
+ int devicesfound = 0;
+
+ for(i=0; i<NUM_FLASHBANKS; i++)
+ {
+ printk(KERN_NOTICE MSG_PREFIX "probing 0x%08lx at 0x%08lx\n",
+ pt[i].size, pt[i].addr);
+- impa7_map[i].map_priv_1 = (unsigned long)
+- ioremap(pt[i].addr, pt[i].size);
+
+- if (!impa7_map[i].map_priv_1) {
++ impa7_map[i].phys = pt[i].addr;
++ impa7_map[i].virt = (unsigned long)
++ ioremap(pt[i].addr, pt[i].size);
++ if (!impa7_map[i].virt) {
+ printk(MSG_PREFIX "failed to ioremap\n");
+ return -EIO;
+ }
++ simple_map_init(&impa7_map[i]);
+
+ impa7_mtd[i] = 0;
+ type = rom_probe_types;
+@@ -167,43 +105,34 @@
+ impa7_mtd[i] = do_map_probe(*type, &impa7_map[i]);
+ }
+
+- if (impa7_mtd[i])
+- {
+- impa7_mtd[i]->module = THIS_MODULE;
+- add_mtd_device(impa7_mtd[i]);
++ if (impa7_mtd[i]) {
++ impa7_mtd[i]->owner = THIS_MODULE;
+ devicesfound++;
+ #ifdef CONFIG_MTD_PARTITIONS
+-#ifdef CONFIG_MTD_CMDLINE_PARTS
+- sprintf(mtdid, MTDID, i);
+- mtd_parts_nb = parse_cmdline_partitions(impa7_mtd[i],
+- &mtd_parts,
+- mtdid);
+- if (mtd_parts_nb > 0)
++ mtd_parts_nb[i] = parse_mtd_partitions(impa7_mtd[i],
++ probes,
++ &mtd_parts[i],
++ 0);
++ if (mtd_parts_nb[i] > 0) {
+ part_type = "command line";
+-#endif
+- if (mtd_parts_nb <= 0)
+- {
+- mtd_parts = static_partitions;
+- mtd_parts_nb = NB_OF(static_partitions);
++ } else {
++ mtd_parts[i] = static_partitions;
++ mtd_parts_nb[i] = ARRAY_SIZE(static_partitions);
+ part_type = "static";
+ }
+- if (mtd_parts_nb <= 0)
+- {
+- printk(KERN_NOTICE MSG_PREFIX
+- "no partition info available\n");
+- }
+- else
+- {
++
+ printk(KERN_NOTICE MSG_PREFIX
+ "using %s partition definition\n",
+ part_type);
+ add_mtd_partitions(impa7_mtd[i],
+- mtd_parts, mtd_parts_nb);
+- }
++ mtd_parts[i], mtd_parts_nb[i]);
++#else
++ add_mtd_device(impa7_mtd[i]);
++
+ #endif
+ }
+ else
+- iounmap((void *)impa7_map[i].map_priv_1);
++ iounmap((void *)impa7_map[i].virt);
+ }
+ return devicesfound == 0 ? -ENXIO : 0;
+ }
+@@ -211,17 +140,16 @@
+ static void __exit cleanup_impa7(void)
+ {
+ int i;
+- for (i=0; i<NUM_FLASHBANKS; i++)
+- {
+- if (impa7_mtd[i])
+- {
++ for (i=0; i<NUM_FLASHBANKS; i++) {
++ if (impa7_mtd[i]) {
++#ifdef CONFIG_MTD_PARTITIONS
++ del_mtd_partitions(impa7_mtd[i]);
++#else
+ del_mtd_device(impa7_mtd[i]);
++#endif
+ map_destroy(impa7_mtd[i]);
+- }
+- if (impa7_map[i].map_priv_1)
+- {
+- iounmap((void *)impa7_map[i].map_priv_1);
+- impa7_map[i].map_priv_1 = 0;
++ iounmap((void *)impa7_map[i].virt);
++ impa7_map[i].virt = 0;
+ }
+ }
+ }
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/maps/integrator-flash-v24.c linux/drivers/mtd/maps/integrator-flash-v24.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/maps/integrator-flash-v24.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/mtd/maps/integrator-flash-v24.c 2004-11-17 18:17:59.000000000 +0100
+@@ -0,0 +1,258 @@
++/*======================================================================
++
++ drivers/mtd/maps/armflash.c: ARM Flash Layout/Partitioning
++
++ Copyright (C) 2000 ARM Limited
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License as published by
++ the Free Software Foundation; either version 2 of the License, or
++ (at your option) any later version.
++
++ This program is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ GNU General Public License for more details.
++
++ You should have received a copy of the GNU General Public License
++ along with this program; if not, write to the Free Software
++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++
++ This is access code for flashes using ARM's flash partitioning
++ standards.
++
++ $Id$
++
++======================================================================*/
++
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/kernel.h>
++#include <linux/slab.h>
++#include <linux/ioport.h>
++#include <linux/init.h>
++
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/map.h>
++#include <linux/mtd/partitions.h>
++
++#include <asm/hardware.h>
++#include <asm/io.h>
++#include <asm/system.h>
++
++// board specific stuff - sorry, it should be in arch/arm/mach-*.
++#ifdef CONFIG_ARCH_INTEGRATOR
++
++#define FLASH_BASE INTEGRATOR_FLASH_BASE
++#define FLASH_SIZE INTEGRATOR_FLASH_SIZE
++
++#define FLASH_PART_SIZE 0x400000
++
++#define SC_CTRLC (IO_ADDRESS(INTEGRATOR_SC_BASE) + INTEGRATOR_SC_CTRLC_OFFSET)
++#define SC_CTRLS (IO_ADDRESS(INTEGRATOR_SC_BASE) + INTEGRATOR_SC_CTRLS_OFFSET)
++#define EBI_CSR1 (IO_ADDRESS(INTEGRATOR_EBI_BASE) + INTEGRATOR_EBI_CSR1_OFFSET)
++#define EBI_LOCK (IO_ADDRESS(INTEGRATOR_EBI_BASE) + INTEGRATOR_EBI_LOCK_OFFSET)
++
++/*
++ * Initialise the flash access systems:
++ * - Disable VPP
++ * - Assert WP
++ * - Set write enable bit in EBI reg
++ */
++static void armflash_flash_init(void)
++{
++ unsigned int tmp;
++
++ __raw_writel(INTEGRATOR_SC_CTRL_nFLVPPEN | INTEGRATOR_SC_CTRL_nFLWP, SC_CTRLC);
++
++ tmp = __raw_readl(EBI_CSR1) | INTEGRATOR_EBI_WRITE_ENABLE;
++ __raw_writel(tmp, EBI_CSR1);
++
++ if (!(__raw_readl(EBI_CSR1) & INTEGRATOR_EBI_WRITE_ENABLE)) {
++ __raw_writel(0xa05f, EBI_LOCK);
++ __raw_writel(tmp, EBI_CSR1);
++ __raw_writel(0, EBI_LOCK);
++ }
++}
++
++/*
++ * Shutdown the flash access systems:
++ * - Disable VPP
++ * - Assert WP
++ * - Clear write enable bit in EBI reg
++ */
++static void armflash_flash_exit(void)
++{
++ unsigned int tmp;
++
++ __raw_writel(INTEGRATOR_SC_CTRL_nFLVPPEN | INTEGRATOR_SC_CTRL_nFLWP, SC_CTRLC);
++
++ /*
++ * Clear the write enable bit in system controller EBI register.
++ */
++ tmp = __raw_readl(EBI_CSR1) & ~INTEGRATOR_EBI_WRITE_ENABLE;
++ __raw_writel(tmp, EBI_CSR1);
++
++ if (__raw_readl(EBI_CSR1) & INTEGRATOR_EBI_WRITE_ENABLE) {
++ __raw_writel(0xa05f, EBI_LOCK);
++ __raw_writel(tmp, EBI_CSR1);
++ __raw_writel(0, EBI_LOCK);
++ }
++}
++
++static void armflash_flash_wp(int on)
++{
++ unsigned int reg;
++
++ if (on)
++ reg = SC_CTRLC;
++ else
++ reg = SC_CTRLS;
++
++ __raw_writel(INTEGRATOR_SC_CTRL_nFLWP, reg);
++}
++
++static void armflash_set_vpp(struct map_info *map, int on)
++{
++ unsigned int reg;
++
++ if (on)
++ reg = SC_CTRLS;
++ else
++ reg = SC_CTRLC;
++
++ __raw_writel(INTEGRATOR_SC_CTRL_nFLVPPEN, reg);
++}
++#endif
++
++#ifdef CONFIG_ARCH_P720T
++
++#define FLASH_BASE (0x04000000)
++#define FLASH_SIZE (64*1024*1024)
++
++#define FLASH_PART_SIZE (4*1024*1024)
++#define FLASH_BLOCK_SIZE (128*1024)
++
++static void armflash_flash_init(void)
++{
++}
++
++static void armflash_flash_exit(void)
++{
++}
++
++static void armflash_flash_wp(int on)
++{
++}
++
++static void armflash_set_vpp(struct map_info *map, int on)
++{
++}
++#endif
++
++
++static struct map_info armflash_map =
++{
++ .name = "AFS",
++ .set_vpp = armflash_set_vpp,
++ .phys = FLASH_BASE,
++};
++
++static struct mtd_info *mtd;
++static struct mtd_partition *parts;
++static const char *probes[] = { "RedBoot", "afs", NULL };
++
++static int __init armflash_cfi_init(void *base, u_int size)
++{
++ int ret;
++
++ armflash_flash_init();
++ armflash_flash_wp(1);
++
++ /*
++ * look for CFI based flash parts fitted to this board
++ */
++ armflash_map.size = size;
++ armflash_map.buswidth = 4;
++ armflash_map.virt = (unsigned long) base;
++
++ simple_map_init(&armflash_map);
++
++ /*
++ * Also, the CFI layer automatically works out what size
++ * of chips we have, and does the necessary identification
++ * for us automatically.
++ */
++ mtd = do_map_probe("cfi_probe", &armflash_map);
++ if (!mtd)
++ return -ENXIO;
++
++ mtd->owner = THIS_MODULE;
++
++ ret = parse_mtd_partitions(mtd, probes, &parts, (void *)0);
++ if (ret > 0) {
++ ret = add_mtd_partitions(mtd, parts, ret);
++ if (ret)
++ printk(KERN_ERR "mtd partition registration "
++ "failed: %d\n", ret);
++ }
++
++ /*
++ * If we got an error, free all resources.
++ */
++ if (ret < 0) {
++ del_mtd_partitions(mtd);
++ map_destroy(mtd);
++ }
++
++ return ret;
++}
++
++static void armflash_cfi_exit(void)
++{
++ if (mtd) {
++ del_mtd_partitions(mtd);
++ map_destroy(mtd);
++ }
++ if (parts)
++ kfree(parts);
++}
++
++static int __init armflash_init(void)
++{
++ int err = -EBUSY;
++ void *base;
++
++ if (request_mem_region(FLASH_BASE, FLASH_SIZE, "flash") == NULL)
++ goto out;
++
++ base = ioremap(FLASH_BASE, FLASH_SIZE);
++ err = -ENOMEM;
++ if (base == NULL)
++ goto release;
++
++ err = armflash_cfi_init(base, FLASH_SIZE);
++ if (err) {
++ iounmap(base);
++release:
++ release_mem_region(FLASH_BASE, FLASH_SIZE);
++ }
++out:
++ return err;
++}
++
++static void __exit armflash_exit(void)
++{
++ armflash_cfi_exit();
++ iounmap((void *)armflash_map.virt);
++ release_mem_region(FLASH_BASE, FLASH_SIZE);
++ armflash_flash_exit();
++}
++
++module_init(armflash_init);
++module_exit(armflash_exit);
++
++MODULE_AUTHOR("ARM Ltd");
++MODULE_DESCRIPTION("ARM Integrator CFI map driver");
++MODULE_LICENSE("GPL");
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/maps/integrator-flash.c linux/drivers/mtd/maps/integrator-flash.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/maps/integrator-flash.c 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/maps/integrator-flash.c 2004-11-17 18:17:59.101304496 +0100
+@@ -1,8 +1,9 @@
+ /*======================================================================
+
+- drivers/mtd/maps/armflash.c: ARM Flash Layout/Partitioning
++ drivers/mtd/maps/integrator-flash.c: ARM Integrator flash map driver
+
+ Copyright (C) 2000 ARM Limited
++ Copyright (C) 2003 Deep Blue Solutions Ltd.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+@@ -21,7 +22,7 @@
+ This is access code for flashes using ARM's flash partitioning
+ standards.
+
+- $Id$
++ $Id$
+
+ ======================================================================*/
+
+@@ -31,268 +32,181 @@
+ #include <linux/kernel.h>
+ #include <linux/slab.h>
+ #include <linux/ioport.h>
++#include <linux/device.h>
+ #include <linux/init.h>
+
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+ #include <linux/mtd/partitions.h>
+
++#include <asm/mach/flash.h>
+ #include <asm/hardware.h>
+ #include <asm/io.h>
+ #include <asm/system.h>
+
+-extern int parse_afs_partitions(struct mtd_info *, struct mtd_partition **);
+-
+-// board specific stuff - sorry, it should be in arch/arm/mach-*.
+-#ifdef CONFIG_ARCH_INTEGRATOR
+-
+-#define FLASH_BASE INTEGRATOR_FLASH_BASE
+-#define FLASH_SIZE INTEGRATOR_FLASH_SIZE
+-
+-#define FLASH_PART_SIZE 0x400000
+-
+-#define SC_CTRLC (IO_ADDRESS(INTEGRATOR_SC_BASE) + INTEGRATOR_SC_CTRLC_OFFSET)
+-#define SC_CTRLS (IO_ADDRESS(INTEGRATOR_SC_BASE) + INTEGRATOR_SC_CTRLS_OFFSET)
+-#define EBI_CSR1 (IO_ADDRESS(INTEGRATOR_EBI_BASE) + INTEGRATOR_EBI_CSR1_OFFSET)
+-#define EBI_LOCK (IO_ADDRESS(INTEGRATOR_EBI_BASE) + INTEGRATOR_EBI_LOCK_OFFSET)
+-
+-/*
+- * Initialise the flash access systems:
+- * - Disable VPP
+- * - Assert WP
+- * - Set write enable bit in EBI reg
+- */
+-static void armflash_flash_init(void)
+-{
+- unsigned int tmp;
+-
+- __raw_writel(INTEGRATOR_SC_CTRL_nFLVPPEN | INTEGRATOR_SC_CTRL_nFLWP, SC_CTRLC);
+-
+- tmp = __raw_readl(EBI_CSR1) | INTEGRATOR_EBI_WRITE_ENABLE;
+- __raw_writel(tmp, EBI_CSR1);
+-
+- if (!(__raw_readl(EBI_CSR1) & INTEGRATOR_EBI_WRITE_ENABLE)) {
+- __raw_writel(0xa05f, EBI_LOCK);
+- __raw_writel(tmp, EBI_CSR1);
+- __raw_writel(0, EBI_LOCK);
+- }
+-}
+-
+-/*
+- * Shutdown the flash access systems:
+- * - Disable VPP
+- * - Assert WP
+- * - Clear write enable bit in EBI reg
+- */
+-static void armflash_flash_exit(void)
+-{
+- unsigned int tmp;
+-
+- __raw_writel(INTEGRATOR_SC_CTRL_nFLVPPEN | INTEGRATOR_SC_CTRL_nFLWP, SC_CTRLC);
+-
+- /*
+- * Clear the write enable bit in system controller EBI register.
+- */
+- tmp = __raw_readl(EBI_CSR1) & ~INTEGRATOR_EBI_WRITE_ENABLE;
+- __raw_writel(tmp, EBI_CSR1);
+-
+- if (__raw_readl(EBI_CSR1) & INTEGRATOR_EBI_WRITE_ENABLE) {
+- __raw_writel(0xa05f, EBI_LOCK);
+- __raw_writel(tmp, EBI_CSR1);
+- __raw_writel(0, EBI_LOCK);
+- }
+-}
+-
+-static void armflash_flash_wp(int on)
+-{
+- unsigned int reg;
+-
+- if (on)
+- reg = SC_CTRLC;
+- else
+- reg = SC_CTRLS;
+-
+- __raw_writel(INTEGRATOR_SC_CTRL_nFLWP, reg);
+-}
+-
+-static void armflash_set_vpp(struct map_info *map, int on)
+-{
+- unsigned int reg;
+-
+- if (on)
+- reg = SC_CTRLS;
+- else
+- reg = SC_CTRLC;
+-
+- __raw_writel(INTEGRATOR_SC_CTRL_nFLVPPEN, reg);
+-}
+-#endif
+-
+ #ifdef CONFIG_ARCH_P720T
+-
+ #define FLASH_BASE (0x04000000)
+ #define FLASH_SIZE (64*1024*1024)
+-
+-#define FLASH_PART_SIZE (4*1024*1024)
+-#define FLASH_BLOCK_SIZE (128*1024)
+-
+-static void armflash_flash_init(void)
+-{
+-}
+-
+-static void armflash_flash_exit(void)
+-{
+-}
+-
+-static void armflash_flash_wp(int on)
+-{
+-}
+-
+-static void armflash_set_vpp(struct map_info *map, int on)
+-{
+-}
+ #endif
+
+-static __u8 armflash_read8(struct map_info *map, unsigned long ofs)
+-{
+- return readb(ofs + map->map_priv_2);
+-}
+-
+-static __u16 armflash_read16(struct map_info *map, unsigned long ofs)
+-{
+- return readw(ofs + map->map_priv_2);
+-}
++struct armflash_info {
++ struct flash_platform_data *plat;
++ struct resource *res;
++ struct mtd_partition *parts;
++ struct mtd_info *mtd;
++ struct map_info map;
++};
+
+-static __u32 armflash_read32(struct map_info *map, unsigned long ofs)
++static void armflash_set_vpp(struct map_info *map, int on)
+ {
+- return readl(ofs + map->map_priv_2);
+-}
++ struct armflash_info *info = container_of(map, struct armflash_info, map);
+
+-static void armflash_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+-{
+- memcpy(to, (void *) (from + map->map_priv_2), len);
++ if (info->plat && info->plat->set_vpp)
++ info->plat->set_vpp(on);
+ }
+
+-static void armflash_write8(struct map_info *map, __u8 d, unsigned long adr)
+-{
+- writeb(d, adr + map->map_priv_2);
+-}
+-
+-static void armflash_write16(struct map_info *map, __u16 d, unsigned long adr)
+-{
+- writew(d, adr + map->map_priv_2);
+-}
++static const char *probes[] = { "RedBoot", "afs", NULL };
+
+-static void armflash_write32(struct map_info *map, __u32 d, unsigned long adr)
++static int armflash_probe(struct device *_dev)
+ {
+- writel(d, adr + map->map_priv_2);
+-}
++ struct platform_device *dev = to_platform_device(_dev);
++ struct flash_platform_data *plat = dev->dev.platform_data;
++ struct resource *res = dev->resource;
++ unsigned int size = res->end - res->start + 1;
++ struct armflash_info *info;
++ int err;
++ void *base;
+
+-static void armflash_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+-{
+- memcpy((void *) (to + map->map_priv_2), from, len);
+-}
++ info = kmalloc(sizeof(struct armflash_info), GFP_KERNEL);
++ if (!info) {
++ err = -ENOMEM;
++ goto out;
++ }
+
+-static struct map_info armflash_map =
+-{
+- name: "AFS",
+- read8: armflash_read8,
+- read16: armflash_read16,
+- read32: armflash_read32,
+- copy_from: armflash_copy_from,
+- write8: armflash_write8,
+- write16: armflash_write16,
+- write32: armflash_write32,
+- copy_to: armflash_copy_to,
+- set_vpp: armflash_set_vpp,
+-};
++ memset(info, 0, sizeof(struct armflash_info));
+
+-static struct mtd_info *mtd;
+-static struct mtd_partition *parts;
++ info->plat = plat;
++ if (plat && plat->init) {
++ err = plat->init();
++ if (err)
++ goto no_resource;
++ }
+
+-static int __init armflash_cfi_init(void *base, u_int size)
+-{
+- int ret;
++ info->res = request_mem_region(res->start, size, "armflash");
++ if (!info->res) {
++ err = -EBUSY;
++ goto no_resource;
++ }
+
+- armflash_flash_init();
+- armflash_flash_wp(1);
++ base = ioremap(res->start, size);
++ if (!base) {
++ err = -ENOMEM;
++ goto no_mem;
++ }
+
+ /*
+ * look for CFI based flash parts fitted to this board
+ */
+- armflash_map.size = size;
+- armflash_map.buswidth = 4;
+- armflash_map.map_priv_2 = (unsigned long) base;
++ info->map.size = size;
++ info->map.buswidth = plat->width;
++ info->map.phys = res->start;
++ info->map.virt = (unsigned long) base;
++ info->map.name = dev->dev.bus_id;
++ info->map.set_vpp = armflash_set_vpp;
++
++ simple_map_init(&info->map);
+
+ /*
+ * Also, the CFI layer automatically works out what size
+ * of chips we have, and does the necessary identification
+ * for us automatically.
+ */
+- mtd = do_map_probe("cfi_probe", &armflash_map);
+- if (!mtd)
+- return -ENXIO;
+-
+- mtd->module = THIS_MODULE;
+-
+- ret = parse_afs_partitions(mtd, &parts);
+- if (ret > 0) {
+- ret = add_mtd_partitions(mtd, parts, ret);
+- if (ret)
+- printk(KERN_ERR "mtd partition registration "
+- "failed: %d\n", ret);
++ info->mtd = do_map_probe(plat->map_name, &info->map);
++ if (!info->mtd) {
++ err = -ENXIO;
++ goto no_device;
+ }
+
++ info->mtd->owner = THIS_MODULE;
++
++ err = parse_mtd_partitions(info->mtd, probes, &info->parts, 0);
++ if (err > 0) {
++ err = add_mtd_partitions(info->mtd, info->parts, err);
++ if (err)
++ printk(KERN_ERR
++ "mtd partition registration failed: %d\n", err);
++ }
++
++ if (err == 0)
++ dev_set_drvdata(&dev->dev, info);
++
+ /*
+ * If we got an error, free all resources.
+ */
+- if (ret < 0) {
+- del_mtd_partitions(mtd);
+- map_destroy(mtd);
++ if (err < 0) {
++ if (info->mtd) {
++ del_mtd_partitions(info->mtd);
++ map_destroy(info->mtd);
+ }
++ if (info->parts)
++ kfree(info->parts);
+
+- return ret;
+-}
+-
+-static void armflash_cfi_exit(void)
+-{
+- if (mtd) {
+- del_mtd_partitions(mtd);
+- map_destroy(mtd);
++ no_device:
++ iounmap(base);
++ no_mem:
++ release_mem_region(res->start, size);
++ no_resource:
++ if (plat && plat->exit)
++ plat->exit();
++ kfree(info);
+ }
+- if (parts)
+- kfree(parts);
++ out:
++ return err;
+ }
+
+-static int __init armflash_init(void)
++static int armflash_remove(struct device *_dev)
+ {
+- int err = -EBUSY;
+- void *base;
++ struct platform_device *dev = to_platform_device(_dev);
++ struct armflash_info *info = dev_get_drvdata(&dev->dev);
+
+- if (request_mem_region(FLASH_BASE, FLASH_SIZE, "flash") == NULL)
+- goto out;
++ dev_set_drvdata(&dev->dev, NULL);
+
+- base = ioremap(FLASH_BASE, FLASH_SIZE);
+- err = -ENOMEM;
+- if (base == NULL)
+- goto release;
++ if (info) {
++ if (info->mtd) {
++ del_mtd_partitions(info->mtd);
++ map_destroy(info->mtd);
++ }
++ if (info->parts)
++ kfree(info->parts);
+
+- err = armflash_cfi_init(base, FLASH_SIZE);
+- if (err) {
+- iounmap(base);
+-release:
+- release_mem_region(FLASH_BASE, FLASH_SIZE);
++ iounmap((void *)info->map.virt);
++ release_resource(info->res);
++ kfree(info->res);
++
++ if (info->plat && info->plat->exit)
++ info->plat->exit();
++
++ kfree(info);
+ }
+-out:
+- return err;
++
++ return 0;
++}
++
++static struct device_driver armflash_driver = {
++ .name = "armflash",
++ .bus = &platform_bus_type,
++ .probe = armflash_probe,
++ .remove = armflash_remove,
++};
++
++static int __init armflash_init(void)
++{
++ return driver_register(&armflash_driver);
+ }
+
+ static void __exit armflash_exit(void)
+ {
+- armflash_cfi_exit();
+- iounmap((void *)armflash_map.map_priv_2);
+- release_mem_region(FLASH_BASE, FLASH_SIZE);
+- armflash_flash_exit();
++ driver_unregister(&armflash_driver);
+ }
+
+ module_init(armflash_init);
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/maps/iq80310.c linux/drivers/mtd/maps/iq80310.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/maps/iq80310.c 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/maps/iq80310.c 2004-11-17 18:17:59.103304192 +0100
+@@ -1,5 +1,5 @@
+ /*
+- * $Id$
++ * $Id$
+ *
+ * Mapping for the Intel XScale IQ80310 evaluation board
+ *
+@@ -14,6 +14,8 @@
+ #include <linux/module.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/slab.h>
+ #include <asm/io.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+@@ -26,127 +28,72 @@
+
+ static struct mtd_info *mymtd;
+
+-static __u8 iq80310_read8(struct map_info *map, unsigned long ofs)
+-{
+- return *(__u8 *)(map->map_priv_1 + ofs);
+-}
+-
+-static __u16 iq80310_read16(struct map_info *map, unsigned long ofs)
+-{
+- return *(__u16 *)(map->map_priv_1 + ofs);
+-}
+-
+-static __u32 iq80310_read32(struct map_info *map, unsigned long ofs)
+-{
+- return *(__u32 *)(map->map_priv_1 + ofs);
+-}
+-
+-static void iq80310_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+-{
+- memcpy(to, (void *)(map->map_priv_1 + from), len);
+-}
+-
+-static void iq80310_write8(struct map_info *map, __u8 d, unsigned long adr)
+-{
+- *(__u8 *)(map->map_priv_1 + adr) = d;
+-}
+-
+-static void iq80310_write16(struct map_info *map, __u16 d, unsigned long adr)
+-{
+- *(__u16 *)(map->map_priv_1 + adr) = d;
+-}
+-
+-static void iq80310_write32(struct map_info *map, __u32 d, unsigned long adr)
+-{
+- *(__u32 *)(map->map_priv_1 + adr) = d;
+-}
+-
+-static void iq80310_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+-{
+- memcpy((void *)(map->map_priv_1 + to), from, len);
+-}
+-
+ static struct map_info iq80310_map = {
+- name: "IQ80310 flash",
+- size: WINDOW_SIZE,
+- buswidth: BUSWIDTH,
+- read8: iq80310_read8,
+- read16: iq80310_read16,
+- read32: iq80310_read32,
+- copy_from: iq80310_copy_from,
+- write8: iq80310_write8,
+- write16: iq80310_write16,
+- write32: iq80310_write32,
+- copy_to: iq80310_copy_to
++ .name = "IQ80310 flash",
++ .size = WINDOW_SIZE,
++ .buswidth = BUSWIDTH,
++ .phys = WINDOW_ADDR
+ };
+
+ static struct mtd_partition iq80310_partitions[4] = {
+ {
+- name: "Firmware",
+- size: 0x00080000,
+- offset: 0,
+- mask_flags: MTD_WRITEABLE /* force read-only */
++ .name = "Firmware",
++ .size = 0x00080000,
++ .offset = 0,
++ .mask_flags = MTD_WRITEABLE /* force read-only */
+ },{
+- name: "Kernel",
+- size: 0x000a0000,
+- offset: 0x00080000,
++ .name = "Kernel",
++ .size = 0x000a0000,
++ .offset = 0x00080000,
+ },{
+- name: "Filesystem",
+- size: 0x00600000,
+- offset: 0x00120000
++ .name = "Filesystem",
++ .size = 0x00600000,
++ .offset = 0x00120000
+ },{
+- name: "RedBoot",
+- size: 0x000e0000,
+- offset: 0x00720000,
+- mask_flags: MTD_WRITEABLE
++ .name = "RedBoot",
++ .size = 0x000e0000,
++ .offset = 0x00720000,
++ .mask_flags = MTD_WRITEABLE
+ }
+ };
+
+-#define NB_OF(x) (sizeof(x)/sizeof(x[0]))
+-
+ static struct mtd_info *mymtd;
+ static struct mtd_partition *parsed_parts;
+-
+-extern int parse_redboot_partitions(struct mtd_info *master, struct mtd_partition **pparts);
++static const char *probes[] = { "RedBoot", "cmdlinepart", NULL };
+
+ static int __init init_iq80310(void)
+ {
+ struct mtd_partition *parts;
+ int nb_parts = 0;
+ int parsed_nr_parts = 0;
+- char *part_type = "static";
++ int ret;
+
+- iq80310_map.map_priv_1 = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE);
+- if (!iq80310_map.map_priv_1) {
++ iq80310_map.virt = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE);
++ if (!iq80310_map.virt) {
+ printk("Failed to ioremap\n");
+ return -EIO;
+ }
++ simple_map_init(&iq80310_map);
++
+ mymtd = do_map_probe("cfi_probe", &iq80310_map);
+ if (!mymtd) {
+- iounmap((void *)iq80310_map.map_priv_1);
++ iounmap((void *)iq80310_map.virt);
+ return -ENXIO;
+ }
+- mymtd->module = THIS_MODULE;
++ mymtd->owner = THIS_MODULE;
+
+-#ifdef CONFIG_MTD_REDBOOT_PARTS
+- if (parsed_nr_parts == 0) {
+- int ret = parse_redboot_partitions(mymtd, &parsed_parts);
++ ret = parse_mtd_partitions(mymtd, probes, &parsed_parts, 0);
+
+- if (ret > 0) {
+- part_type = "RedBoot";
++ if (ret > 0)
+ parsed_nr_parts = ret;
+- }
+- }
+-#endif
+
+ if (parsed_nr_parts > 0) {
+ parts = parsed_parts;
+ nb_parts = parsed_nr_parts;
+ } else {
+ parts = iq80310_partitions;
+- nb_parts = NB_OF(iq80310_partitions);
++ nb_parts = ARRAY_SIZE(iq80310_partitions);
+ }
+- printk(KERN_NOTICE "Using %s partition definition\n", part_type);
+ add_mtd_partitions(mymtd, parts, nb_parts);
+ return 0;
+ }
+@@ -159,8 +106,8 @@
+ if (parsed_parts)
+ kfree(parsed_parts);
+ }
+- if (iq80310_map.map_priv_1)
+- iounmap((void *)iq80310_map.map_priv_1);
++ if (iq80310_map.virt)
++ iounmap((void *)iq80310_map.virt);
+ }
+
+ module_init(init_iq80310);
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/maps/ixp425.c linux/drivers/mtd/maps/ixp425.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/maps/ixp425.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/mtd/maps/ixp425.c 2004-11-17 18:17:59.000000000 +0100
+@@ -0,0 +1,220 @@
++/*
++ * $Id$
++ *
++ * drivers/mtd/maps/ixp425.c
++ *
++ * MTD Map file for IXP425 based systems. Please do not make per-board
++ * map driver as the code will be 90% identical. For now just add
++ * if(machine_is_XXX()) checks to the code. I'll clean this stuff to
++ * use platform_data in the the future so we can get rid of that too.
++ *
++ * Original Author: Intel Corporation
++ * Maintainer: Deepak Saxena <dsaxena@mvista.com>
++ *
++ * Copyright (C) 2002 Intel Corporation
++ * Copyright (C) 2003 MontaVista Software, Inc.
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/string.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/map.h>
++#include <linux/mtd/partitions.h>
++#include <linux/ioport.h>
++#include <asm/io.h>
++#include <asm/mach-types.h>
++
++#include <linux/reboot.h>
++
++#define WINDOW_ADDR 0x50000000
++#define BUSWIDTH 2
++
++#ifndef __ARMEB__
++#define BYTE0(h) ((h) & 0xFF)
++#define BYTE1(h) (((h) >> 8) & 0xFF)
++#else
++#define BYTE0(h) (((h) >> 8) & 0xFF)
++#define BYTE1(h) ((h) & 0xFF)
++#endif
++
++static __u16
++ixp425_read16(struct map_info *map, unsigned long ofs)
++{
++ return *(__u16 *) (map->map_priv_1 + ofs);
++}
++
++/*
++ * The IXP425 expansion bus only allows 16-bit wide acceses
++ * when attached to a 16-bit wide device (such as the 28F128J3A),
++ * so we can't just memcpy_fromio().
++ */
++static void
++ixp425_copy_from(struct map_info *map, void *to,
++ unsigned long from, ssize_t len)
++{
++ int i;
++ u8 *dest = (u8 *) to;
++ u16 *src = (u16 *) (map->map_priv_1 + from);
++ u16 data;
++
++ for (i = 0; i < (len / 2); i++) {
++ data = src[i];
++ dest[i * 2] = BYTE0(data);
++ dest[i * 2 + 1] = BYTE1(data);
++ }
++
++ if (len & 1)
++ dest[len - 1] = BYTE0(src[i]);
++}
++
++static void
++ixp425_write16(struct map_info *map, __u16 d, unsigned long adr)
++{
++ *(__u16 *) (map->map_priv_1 + adr) = d;
++}
++
++static struct map_info ixp425_map = {
++ .name = "IXP425 Flash",
++ .buswidth = BUSWIDTH,
++ .read16 = ixp425_read16,
++ .copy_from = ixp425_copy_from,
++ .write16 = ixp425_write16,
++};
++
++/*
++ * Put flash back in read mode so RedBoot can boot properly.
++ */
++int ixp425_mtd_reboot(struct notifier_block *n, unsigned long code, void *p)
++{
++ if (code != SYS_RESTART)
++ return NOTIFY_DONE;
++
++ ixp425_write16(&ixp425_map, 0xff, 0x55 * 0x2);
++ return NOTIFY_DONE;
++}
++
++static struct notifier_block ixp425_mtd_notifier = {
++ notifier_call:ixp425_mtd_reboot,
++ next:NULL,
++ priority:0
++};
++
++static struct mtd_partition *parsed_parts;
++static const char *probes[] = { "RedBoot", "cmdlinepart", NULL };
++
++static struct mtd_partition ixp425_partitions[] = {
++ {
++ .name = "image",
++ .offset = 0x00040000,
++ .size = 0x00400000,
++ }, {
++ .name = "user",
++ .offset = 0x00440000,
++ .size = MTDPART_SIZ_FULL
++ }
++};
++
++#define NB_OF(x) (sizeof(x)/sizeof(x[0]))
++
++static struct mtd_info *ixp425_mtd;
++static struct resource *mtd_resource;
++
++static void
++ixp425_exit(void)
++{
++ if (ixp425_mtd) {
++ del_mtd_partitions(ixp425_mtd);
++ map_destroy(ixp425_mtd);
++ }
++ if (ixp425_map.map_priv_1)
++ iounmap((void *) ixp425_map.map_priv_1);
++ if (mtd_resource)
++ release_mem_region(WINDOW_ADDR, ixp425_map.size);
++
++ if (parsed_parts)
++ kfree(parsed_parts);
++
++ unregister_reboot_notifier(&ixp425_mtd_notifier);
++
++ /* Disable flash write */
++ *IXP425_EXP_CS0 &= ~IXP425_FLASH_WRITABLE;
++
++ if(machine_is_adi_coyote())
++ *IXP425_EXP_CS1 &= ~IXP425_FLASH_WRITABLE;
++}
++
++static int __init
++ixp425_init(void)
++{
++ int res = -1, npart;
++
++ /* Enable flash write */
++ *IXP425_EXP_CS0 |= IXP425_FLASH_WRITABLE;
++
++ /*
++ * Coyote requires CS1 write to be enabled and has 32MB flash.
++ * This will move to the platform init code in 2.6
++ */
++ if(machine_is_adi_coyote()) {
++ *IXP425_EXP_CS1 |= IXP425_FLASH_WRITABLE;
++ ixp425_map.size = 0x02000000;
++ } else
++ ixp425_map.size = 0x01000000;
++
++ ixp425_map.map_priv_1 = 0;
++ mtd_resource =
++ request_mem_region(WINDOW_ADDR, ixp425_map.size, "IXP425 Flash");
++ if (!mtd_resource) {
++ printk(KERN_ERR
++ "ixp425 flash: Could not request mem region.\n");
++ res = -ENOMEM;
++ goto Error;
++ }
++
++ ixp425_map.map_priv_1 =
++ (unsigned long) ioremap(WINDOW_ADDR, ixp425_map.size);
++ if (!ixp425_map.map_priv_1) {
++ printk("ixp425 Flash: Failed to map IO region. (ioremap)\n");
++ res = -EIO;
++ goto Error;
++ }
++
++ ixp425_mtd = do_map_probe("cfi_probe", &ixp425_map);
++ if (!ixp425_mtd) {
++ res = -ENXIO;
++ goto Error;
++ }
++ ixp425_mtd->owner = THIS_MODULE;
++
++ /* Try to parse RedBoot partitions */
++ npart = parse_mtd_partitions(ixp425_mtd, probes, &parsed_parts, 0);
++ if (npart > 0)
++ res = add_mtd_partitions(ixp425_mtd, parsed_parts, npart);
++ else {
++ printk("IXP425 Flash: Using static MTD partitions.\n");
++ res = add_mtd_partitions(ixp425_mtd, ixp425_partitions,
++ NB_OF(ixp425_partitions));
++ }
++
++ if (res)
++ goto Error;
++
++ register_reboot_notifier(&ixp425_mtd_notifier);
++
++ return res;
++
++Error:
++ ixp425_exit();
++ return res;
++}
++
++module_init(ixp425_init);
++module_exit(ixp425_exit);
++
++MODULE_LICENSE("GPL");
++MODULE_DESCRIPTION("MTD map driver for ixp425 evaluation board");
++MODULE_AUTHOR("Deepak Saxena");
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/maps/l440gx.c linux/drivers/mtd/maps/l440gx.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/maps/l440gx.c 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/maps/l440gx.c 2004-11-17 18:17:59.105303888 +0100
+@@ -1,5 +1,5 @@
+ /*
+- * $Id$
++ * $Id$
+ *
+ * BIOS Flash chip on Intel 440GX board.
+ *
+@@ -9,6 +9,7 @@
+ #include <linux/module.h>
+ #include <linux/pci.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
+ #include <asm/io.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+@@ -27,48 +28,6 @@
+
+ static struct mtd_info *mymtd;
+
+-__u8 l440gx_read8(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readb(map->map_priv_1 + ofs);
+-}
+-
+-__u16 l440gx_read16(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readw(map->map_priv_1 + ofs);
+-}
+-
+-__u32 l440gx_read32(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readl(map->map_priv_1 + ofs);
+-}
+-
+-void l440gx_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+-{
+- memcpy_fromio(to, map->map_priv_1 + from, len);
+-}
+-
+-void l440gx_write8(struct map_info *map, __u8 d, unsigned long adr)
+-{
+- __raw_writeb(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-void l440gx_write16(struct map_info *map, __u16 d, unsigned long adr)
+-{
+- __raw_writew(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-void l440gx_write32(struct map_info *map, __u32 d, unsigned long adr)
+-{
+- __raw_writel(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-void l440gx_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+-{
+- memcpy_toio(map->map_priv_1 + to, from, len);
+-}
+
+ /* Is this really the vpp port? */
+ void l440gx_set_vpp(struct map_info *map, int vpp)
+@@ -85,22 +44,15 @@
+ }
+
+ struct map_info l440gx_map = {
+- name: "L440GX BIOS",
+- size: WINDOW_SIZE,
+- buswidth: BUSWIDTH,
+- read8: l440gx_read8,
+- read16: l440gx_read16,
+- read32: l440gx_read32,
+- copy_from: l440gx_copy_from,
+- write8: l440gx_write8,
+- write16: l440gx_write16,
+- write32: l440gx_write32,
+- copy_to: l440gx_copy_to,
++ .name = "L440GX BIOS",
++ .size = WINDOW_SIZE,
++ .buswidth = BUSWIDTH,
++ .phys = WINDOW_ADDR,
+ #if 0
+ /* FIXME verify that this is the
+ * appripriate code for vpp enable/disable
+ */
+- set_vpp: l440gx_set_vpp
++ .set_vpp = l440gx_set_vpp
+ #endif
+ };
+
+@@ -113,7 +65,6 @@
+ dev = pci_find_device(PCI_VENDOR_ID_INTEL,
+ PCI_DEVICE_ID_INTEL_82371AB_0, NULL);
+
+-
+ pm_dev = pci_find_device(PCI_VENDOR_ID_INTEL,
+ PCI_DEVICE_ID_INTEL_82371AB_3, NULL);
+
+@@ -122,15 +73,14 @@
+ return -ENODEV;
+ }
+
++ l440gx_map.virt = (unsigned long)ioremap_nocache(WINDOW_ADDR, WINDOW_SIZE);
+
+- l440gx_map.map_priv_1 = (unsigned long)ioremap_nocache(WINDOW_ADDR, WINDOW_SIZE);
+-
+- if (!l440gx_map.map_priv_1) {
++ if (!l440gx_map.virt) {
+ printk(KERN_WARNING "Failed to ioremap L440GX flash region\n");
+ return -ENOMEM;
+ }
+-
+- printk(KERN_NOTICE "window_addr = 0x%08lx\n", (unsigned long)l440gx_map.map_priv_1);
++ simple_map_init(&l440gx_map);
++ printk(KERN_NOTICE "window_addr = 0x%08lx\n", (unsigned long)l440gx_map.virt);
+
+ /* Setup the pm iobase resource
+ * This code should move into some kind of generic bridge
+@@ -153,7 +103,7 @@
+ /* Allocate the resource region */
+ if (pci_assign_resource(pm_dev, PIIXE_IOBASE_RESOURCE) != 0) {
+ printk(KERN_WARNING "Could not allocate pm iobase resource\n");
+- iounmap((void *)l440gx_map.map_priv_1);
++ iounmap((void *)l440gx_map.virt);
+ return -ENXIO;
+ }
+ }
+@@ -181,13 +131,13 @@
+ mymtd = do_map_probe("map_rom", &l440gx_map);
+ }
+ if (mymtd) {
+- mymtd->module = THIS_MODULE;
++ mymtd->owner = THIS_MODULE;
+
+ add_mtd_device(mymtd);
+ return 0;
+ }
+
+- iounmap((void *)l440gx_map.map_priv_1);
++ iounmap((void *)l440gx_map.virt);
+ return -ENXIO;
+ }
+
+@@ -196,7 +146,7 @@
+ del_mtd_device(mymtd);
+ map_destroy(mymtd);
+
+- iounmap((void *)l440gx_map.map_priv_1);
++ iounmap((void *)l440gx_map.virt);
+ }
+
+ module_init(init_l440gx);
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/maps/lasat.c linux/drivers/mtd/maps/lasat.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/maps/lasat.c 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/maps/lasat.c 2004-11-17 18:17:59.106303736 +0100
+@@ -1,11 +1,20 @@
+ /*
+- * Flash device on lasat 100 and 200 boards
++ * Flash device on Lasat 100 and 200 boards
++ *
++ * (C) 2002 Brian Murphy <brian@murphy.dk>
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License version
++ * 2 as published by the Free Software Foundation.
++ *
++ * $Id$
+ *
+ */
+
+ #include <linux/module.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
+ #include <asm/io.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+@@ -13,123 +22,80 @@
+ #include <linux/config.h>
+ #include <asm/lasat/lasat.h>
+
+-static struct mtd_info *mymtd;
+-
+-static __u8 sp_read8(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readb(map->map_priv_1 + ofs);
+-}
+-
+-static __u16 sp_read16(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readw(map->map_priv_1 + ofs);
+-}
+-
+-static __u32 sp_read32(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readl(map->map_priv_1 + ofs);
+-}
+-
+-static void sp_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+-{
+- memcpy_fromio(to, map->map_priv_1 + from, len);
+-}
++static struct mtd_info *lasat_mtd;
+
+-static void sp_write8(struct map_info *map, __u8 d, unsigned long adr)
+-{
+- __raw_writeb(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-static void sp_write16(struct map_info *map, __u16 d, unsigned long adr)
+-{
+- __raw_writew(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-static void sp_write32(struct map_info *map, __u32 d, unsigned long adr)
+-{
+- __raw_writel(d, map->map_priv_1 + adr);
+- mb();
+-}
++static struct mtd_partition partition_info[LASAT_MTD_LAST];
++static char *lasat_mtd_partnames[] = {"Bootloader", "Service", "Normal", "Filesystem", "Config"};
+
+-static void sp_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
++static void lasat_set_vpp(struct map_info *map, int vpp)
+ {
+- memcpy_toio(map->map_priv_1 + to, from, len);
++ if (vpp)
++ *lasat_misc->flash_wp_reg |= 1 << lasat_misc->flash_wp_bit;
++ else
++ *lasat_misc->flash_wp_reg &= ~(1 << lasat_misc->flash_wp_bit);
+ }
+
+-static struct map_info sp_map = {
+- name: "SP flash",
+- buswidth: 4,
+- read8: sp_read8,
+- read16: sp_read16,
+- read32: sp_read32,
+- copy_from: sp_copy_from,
+- write8: sp_write8,
+- write16: sp_write16,
+- write32: sp_write32,
+- copy_to: sp_copy_to
++static struct map_info lasat_map = {
++ .name = "LASAT flash",
++ .buswidth = 4,
++ .set_vpp = lasat_set_vpp
+ };
+
+-static struct mtd_partition partition_info[LASAT_MTD_LAST];
+-static char *lasat_mtd_partnames[] = {"Bootloader", "Service", "Normal", "Config", "Filesystem"};
+-
+-static int __init init_sp(void)
++static int __init init_lasat(void)
+ {
+ int i;
+- int nparts = 0;
+- /* this does not play well with the old flash code which
+- * protects and uprotects the flash when necessary */
++ /* since we use AMD chips and set_vpp is not implimented
++ * for these (yet) we still have to permanently enable flash write */
+ printk(KERN_NOTICE "Unprotecting flash\n");
+- *lasat_misc->flash_wp_reg |= 1 << lasat_misc->flash_wp_bit;
++ ENABLE_VPP((&lasat_map));
+
+- sp_map.map_priv_1 = ioremap_nocache(
+- lasat_flash_partition_start(LASAT_MTD_BOOTLOADER),
+- lasat_board_info.li_flash_size);
+- sp_map.size = lasat_board_info.li_flash_size;
++ lasat_map.phys = lasat_flash_partition_start(LASAT_MTD_BOOTLOADER);
++ lasat_map.virt = (unsigned long)ioremap_nocache(
++ lasat_map.phys, lasat_board_info.li_flash_size);
++ lasat_map.size = lasat_board_info.li_flash_size;
+
+- printk(KERN_NOTICE "sp flash device: %lx at %lx\n",
+- sp_map.size, sp_map.map_priv_1);
++ simple_map_init(&lasat_map);
+
+ for (i=0; i < LASAT_MTD_LAST; i++)
+ partition_info[i].name = lasat_mtd_partnames[i];
+
+- mymtd = do_map_probe("cfi_probe", &sp_map);
+- if (mymtd) {
++ lasat_mtd = do_map_probe("cfi_probe", &lasat_map);
++
++ if (!lasat_mtd)
++ lasat_mtd = do_map_probe("jedec_probe", &lasat_map);
++
++ if (lasat_mtd) {
+ u32 size, offset = 0;
+
+- mymtd->module = THIS_MODULE;
++ lasat_mtd->owner = THIS_MODULE;
+
+ for (i=0; i < LASAT_MTD_LAST; i++) {
+ size = lasat_flash_partition_size(i);
+- if (size != 0) {
+- nparts++;
+ partition_info[i].size = size;
+ partition_info[i].offset = offset;
+ offset += size;
+ }
+- }
+
+- add_mtd_partitions( mymtd, partition_info, nparts );
++ add_mtd_partitions( lasat_mtd, partition_info, LASAT_MTD_LAST );
+ return 0;
+ }
+
+ return -ENXIO;
+ }
+
+-static void __exit cleanup_sp(void)
++static void __exit cleanup_lasat(void)
+ {
+- if (mymtd) {
+- del_mtd_partitions(mymtd);
+- map_destroy(mymtd);
++ if (lasat_mtd) {
++ del_mtd_partitions(lasat_mtd);
++ map_destroy(lasat_mtd);
+ }
+- if (sp_map.map_priv_1) {
+- sp_map.map_priv_1 = 0;
++ if (lasat_map.virt) {
++ lasat_map.virt = 0;
+ }
+ }
+
+-module_init(init_sp);
+-module_exit(cleanup_sp);
++module_init(init_lasat);
++module_exit(cleanup_lasat);
+
+ MODULE_LICENSE("GPL");
+ MODULE_AUTHOR("Brian Murphy <brian@murphy.dk>");
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/maps/lubbock-flash.c linux/drivers/mtd/maps/lubbock-flash.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/maps/lubbock-flash.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/mtd/maps/lubbock-flash.c 2004-11-17 18:17:59.000000000 +0100
+@@ -0,0 +1,151 @@
++/*
++ * $Id$
++ *
++ * Map driver for the Lubbock developer platform.
++ *
++ * Author: Nicolas Pitre
++ * Copyright: (C) 2001 MontaVista Software Inc.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ */
++
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <asm/io.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/map.h>
++#include <linux/mtd/partitions.h>
++#include <asm/hardware.h>
++
++
++#define ROM_ADDR 0x00000000
++#define FLASH_ADDR 0x04000000
++
++#define WINDOW_SIZE 64*1024*1024
++
++static struct map_info lubbock_maps[2] = { {
++ .size = WINDOW_SIZE,
++ .phys = 0x00000000,
++}, {
++ .size = WINDOW_SIZE,
++ .phys = 0x04000000,
++} };
++
++static struct mtd_partition lubbock_partitions[] = {
++ {
++ .name = "Bootloader",
++ .size = 0x00040000,
++ .offset = 0,
++ .mask_flags = MTD_WRITEABLE /* force read-only */
++ },{
++ .name = "Kernel",
++ .size = 0x00100000,
++ .offset = 0x00040000,
++ },{
++ .name = "Filesystem",
++ .size = MTDPART_SIZ_FULL,
++ .offset = 0x00140000
++ }
++};
++
++static struct mtd_info *mymtds[2];
++static struct mtd_partition *parsed_parts[2];
++static int nr_parsed_parts[2];
++
++static const char *probes[] = { "RedBoot", "cmdlinepart", NULL };
++
++static int __init init_lubbock(void)
++{
++ int flashboot = (CONF_SWITCHES & 1);
++ int ret = 0, i;
++
++ lubbock_maps[0].buswidth = lubbock_maps[1].buswidth =
++ (BOOT_DEF & 1) ? 2 : 4;
++
++ /* Compensate for the nROMBT switch which swaps the flash banks */
++ printk(KERN_NOTICE "Lubbock configured to boot from %s (bank %d)\n",
++ flashboot?"Flash":"ROM", flashboot);
++
++ lubbock_maps[flashboot^1].name = "Lubbock Application Flash";
++ lubbock_maps[flashboot].name = "Lubbock Boot ROM";
++
++ for (i = 0; i < 2; i++) {
++ lubbock_maps[i].virt = (unsigned long)__ioremap(lubbock_maps[i].phys, WINDOW_SIZE, 0);
++ if (!lubbock_maps[i].virt) {
++ printk(KERN_WARNING "Failed to ioremap %s\n", lubbock_maps[i].name);
++ if (!ret)
++ ret = -ENOMEM;
++ continue;
++ }
++ simple_map_init(&lubbock_maps[i]);
++
++ printk(KERN_NOTICE "Probing %s at physical address 0x%08lx (%d-bit buswidth)\n",
++ lubbock_maps[i].name, lubbock_maps[i].phys,
++ lubbock_maps[i].buswidth * 8);
++
++ mymtds[i] = do_map_probe("cfi_probe", &lubbock_maps[i]);
++
++ if (!mymtds[i]) {
++ iounmap((void *)lubbock_maps[i].virt);
++ if (!ret)
++ ret = -EIO;
++ continue;
++ }
++ mymtds[i]->owner = THIS_MODULE;
++
++ int ret = parse_mtd_partitions(mymtds[i], probes,
++ &parsed_parts[i], 0);
++
++ if (ret > 0)
++ nr_parsed_parts[i] = ret;
++ }
++
++ if (!mymtds[0] && !mymtds[1])
++ return ret;
++
++ for (i = 0; i < 2; i++) {
++ if (!mymtds[i]) {
++ printk(KERN_WARNING "%s is absent. Skipping\n", lubbock_maps[i].name);
++ } else if (nr_parsed_parts[i]) {
++ add_mtd_partitions(mymtds[i], parsed_parts[i], nr_parsed_parts[i]);
++ } else if (!i) {
++ printk("Using static partitions on %s\n", lubbock_maps[i].name);
++ add_mtd_partitions(mymtds[i], lubbock_partitions, ARRAY_SIZE(lubbock_partitions));
++ } else {
++ printk("Registering %s as whole device\n", lubbock_maps[i].name);
++ add_mtd_device(mymtds[i]);
++ }
++ }
++ return 0;
++}
++
++static void __exit cleanup_lubbock(void)
++{
++ int i;
++ for (i = 0; i < 2; i++) {
++ if (!mymtds[i])
++ continue;
++
++ if (nr_parsed_parts[i] || !i)
++ del_mtd_partitions(mymtds[i]);
++ else
++ del_mtd_device(mymtds[i]);
++
++ map_destroy(mymtds[i]);
++ iounmap((void *)lubbock_maps[i].virt);
++
++ if (parsed_parts[i])
++ kfree(parsed_parts[i]);
++ }
++}
++
++module_init(init_lubbock);
++module_exit(cleanup_lubbock);
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Nicolas Pitre <nico@cam.org>");
++MODULE_DESCRIPTION("MTD map driver for Intel Lubbock");
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/maps/map_funcs.c linux/drivers/mtd/maps/map_funcs.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/maps/map_funcs.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/mtd/maps/map_funcs.c 2004-11-17 18:17:59.000000000 +0100
+@@ -0,0 +1,96 @@
++/*
++ * $Id$
++ *
++ * Out-of-line map I/O functions for simple maps when CONFIG_COMPLEX_MAPPINGS
++ * is enabled.
++ */
++
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/config.h>
++#include <linux/types.h>
++#include <linux/string.h>
++#include <asm/io.h>
++
++#include <linux/mtd/map.h>
++
++static u8 simple_map_read8(struct map_info *map, unsigned long ofs)
++{
++ return __raw_readb(map->virt + ofs);
++}
++
++static u16 simple_map_read16(struct map_info *map, unsigned long ofs)
++{
++ return __raw_readw(map->virt + ofs);
++}
++
++static u32 simple_map_read32(struct map_info *map, unsigned long ofs)
++{
++ return __raw_readl(map->virt + ofs);
++}
++
++static u64 simple_map_read64(struct map_info *map, unsigned long ofs)
++{
++#ifndef CONFIG_MTD_CFI_B8 /* 64-bit mappings */
++ BUG();
++ return 0;
++#else
++ return __raw_readll(map->virt + ofs);
++#endif
++}
++
++static void simple_map_write8(struct map_info *map, u8 datum, unsigned long ofs)
++{
++ __raw_writeb(datum, map->virt + ofs);
++ mb();
++}
++
++static void simple_map_write16(struct map_info *map, u16 datum, unsigned long ofs)
++{
++ __raw_writew(datum, map->virt + ofs);
++ mb();
++}
++
++static void simple_map_write32(struct map_info *map, u32 datum, unsigned long ofs)
++{
++ __raw_writel(datum, map->virt + ofs);
++ mb();
++}
++
++static void simple_map_write64(struct map_info *map, u64 datum, unsigned long ofs)
++{
++#ifndef CONFIG_MTD_CFI_B8 /* 64-bit mappings */
++ BUG();
++#else
++ __raw_writell(datum, map->virt + ofs);
++ mb();
++#endif /* CFI_B8 */
++}
++
++static void simple_map_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
++{
++ memcpy_fromio(to, map->virt + from, len);
++}
++
++static void simple_map_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
++{
++ memcpy_toio(map->virt + to, from, len);
++}
++
++void simple_map_init(struct map_info *map)
++{
++ map->read8 = simple_map_read8;
++ map->read16 = simple_map_read16;
++ map->read32 = simple_map_read32;
++ map->read64 = simple_map_read64;
++ map->write8 = simple_map_write8;
++ map->write16 = simple_map_write16;
++ map->write32 = simple_map_write32;
++ map->write64 = simple_map_write64;
++ map->copy_from = simple_map_copy_from;
++ map->copy_to = simple_map_copy_to;
++}
++
++EXPORT_SYMBOL(simple_map_init);
++
++MODULE_LICENSE("GPL");
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/maps/mbx860.c linux/drivers/mtd/maps/mbx860.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/maps/mbx860.c 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/maps/mbx860.c 2004-11-17 18:17:59.110303128 +0100
+@@ -1,5 +1,5 @@
+ /*
+- * $Id$
++ * $Id$
+ *
+ * Handle mapping of the flash on MBX860 boards
+ *
+@@ -15,6 +15,7 @@
+ #include <linux/module.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
+ #include <asm/io.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+@@ -36,91 +37,46 @@
+ * single flash device into. If the size if zero we use up to the end of the
+ * device. */
+ static struct mtd_partition partition_info[]={
+- { name: "MBX flash BOOT partition",
+- offset: 0,
+- size: BOOT_PARTITION_SIZE_KiB*1024 },
+- { name: "MBX flash DATA partition",
+- offset: BOOT_PARTITION_SIZE_KiB*1024,
+- size: (KERNEL_PARTITION_SIZE_KiB)*1024 },
+- { name: "MBX flash APPLICATION partition",
+- offset: (BOOT_PARTITION_SIZE_KiB+KERNEL_PARTITION_SIZE_KiB)*1024 }
++ { .name = "MBX flash BOOT partition",
++ .offset = 0,
++ .size = BOOT_PARTITION_SIZE_KiB*1024 },
++ { .name = "MBX flash DATA partition",
++ .offset = BOOT_PARTITION_SIZE_KiB*1024,
++ .size = (KERNEL_PARTITION_SIZE_KiB)*1024 },
++ { .name = "MBX flash APPLICATION partition",
++ .offset = (BOOT_PARTITION_SIZE_KiB+KERNEL_PARTITION_SIZE_KiB)*1024 }
+ };
+
+
+ static struct mtd_info *mymtd;
+
+-__u8 mbx_read8(struct map_info *map, unsigned long ofs)
+-{
+- return readb(map->map_priv_1 + ofs);
+-}
+-
+-__u16 mbx_read16(struct map_info *map, unsigned long ofs)
+-{
+- return readw(map->map_priv_1 + ofs);
+-}
+-
+-__u32 mbx_read32(struct map_info *map, unsigned long ofs)
+-{
+- return readl(map->map_priv_1 + ofs);
+-}
+-
+-void mbx_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+-{
+- memcpy_fromio(to, (void *)(map->map_priv_1 + from), len);
+-}
+-
+-void mbx_write8(struct map_info *map, __u8 d, unsigned long adr)
+-{
+- writeb(d, map->map_priv_1 + adr);
+-}
+-
+-void mbx_write16(struct map_info *map, __u16 d, unsigned long adr)
+-{
+- writew(d, map->map_priv_1 + adr);
+-}
+-
+-void mbx_write32(struct map_info *map, __u32 d, unsigned long adr)
+-{
+- writel(d, map->map_priv_1 + adr);
+-}
+-
+-void mbx_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+-{
+- memcpy_toio((void *)(map->map_priv_1 + to), from, len);
+-}
+-
+ struct map_info mbx_map = {
+- name: "MBX flash",
+- size: WINDOW_SIZE,
+- buswidth: 4,
+- read8: mbx_read8,
+- read16: mbx_read16,
+- read32: mbx_read32,
+- copy_from: mbx_copy_from,
+- write8: mbx_write8,
+- write16: mbx_write16,
+- write32: mbx_write32,
+- copy_to: mbx_copy_to
++ .name = "MBX flash",
++ .size = WINDOW_SIZE,
++ .phys = WINDOW_ADDR,
++ .buswidth = 4,
+ };
+
+ int __init init_mbx(void)
+ {
+- printk(KERN_NOTICE "Motorola MBX flash device: %x at %x\n", WINDOW_SIZE*4, WINDOW_ADDR);
+- mbx_map.map_priv_1 = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE * 4);
++ printk(KERN_NOTICE "Motorola MBX flash device: 0x%x at 0x%x\n", WINDOW_SIZE*4, WINDOW_ADDR);
++ mbx_map.virt = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE * 4);
+
+- if (!mbx_map.map_priv_1) {
++ if (!mbx_map.virt) {
+ printk("Failed to ioremap\n");
+ return -EIO;
+ }
++ simple_map_init(&mbx_map);
++
+ mymtd = do_map_probe("jedec_probe", &mbx_map);
+ if (mymtd) {
+- mymtd->module = THIS_MODULE;
++ mymtd->owner = THIS_MODULE;
+ add_mtd_device(mymtd);
+ add_mtd_partitions(mymtd, partition_info, NUM_PARTITIONS);
+ return 0;
+ }
+
+- iounmap((void *)mbx_map.map_priv_1);
++ iounmap((void *)mbx_map.virt);
+ return -ENXIO;
+ }
+
+@@ -130,9 +86,9 @@
+ del_mtd_device(mymtd);
+ map_destroy(mymtd);
+ }
+- if (mbx_map.map_priv_1) {
+- iounmap((void *)mbx_map.map_priv_1);
+- mbx_map.map_priv_1 = 0;
++ if (mbx_map.virt) {
++ iounmap((void *)mbx_map.virt);
++ mbx_map.virt = 0;
+ }
+ }
+
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/maps/mpc1211.c linux/drivers/mtd/maps/mpc1211.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/maps/mpc1211.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/mtd/maps/mpc1211.c 2004-11-17 18:17:59.000000000 +0100
+@@ -0,0 +1,79 @@
++/*
++ * Flash on MPC-1211
++ *
++ * (C) 2002 Interface, Saito.K & Jeanne
++ *
++ * GPL'd
++ */
++
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/kernel.h>
++#include <asm/io.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/map.h>
++#include <linux/mtd/partitions.h>
++#include <linux/config.h>
++
++static struct mtd_info *flash_mtd;
++static struct mtd_partition *parsed_parts;
++
++struct map_info mpc1211_flash_map = {
++ .name = "MPC-1211 FLASH",
++ .size = 0x80000,
++ .buswidth = 1,
++};
++
++static struct mtd_partition mpc1211_partitions[] = {
++ {
++ .name = "IPL & ETH-BOOT",
++ .offset = 0x00000000,
++ .size = 0x10000,
++ },
++ {
++ .name = "Flash FS",
++ .offset = 0x00010000,
++ .size = MTDPART_SIZ_FULL,
++ }
++};
++
++static int __init init_mpc1211_maps(void)
++{
++ int nr_parts;
++
++ mpc1211_flash_map.phys = 0;
++ mpc1211_flash_map.virt = P2SEGADDR(0);
++
++ simple_map_init(&mpc1211_flash_map);
++
++ printk(KERN_NOTICE "Probing for flash chips at 0x00000000:\n");
++ flash_mtd = do_map_probe("jedec_probe", &mpc1211_flash_map);
++ if (!flash_mtd) {
++ printk(KERN_NOTICE "Flash chips not detected at either possible location.\n");
++ return -ENXIO;
++ }
++ printk(KERN_NOTICE "MPC-1211: Flash at 0x%08lx\n", mpc1211_flash_map.virt & 0x1fffffff);
++ flash_mtd->module = THIS_MODULE;
++
++ parsed_parts = mpc1211_partitions;
++ nr_parts = ARRAY_SIZE(mpc1211_partitions);
++
++ add_mtd_partitions(flash_mtd, parsed_parts, nr_parts);
++ return 0;
++}
++
++static void __exit cleanup_mpc1211_maps(void)
++{
++ if (parsed_parts)
++ del_mtd_partitions(flash_mtd);
++ else
++ del_mtd_device(flash_mtd);
++ map_destroy(flash_mtd);
++}
++
++module_init(init_mpc1211_maps);
++module_exit(cleanup_mpc1211_maps);
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Saito.K & Jeanne <ksaito@interface.co.jp>");
++MODULE_DESCRIPTION("MTD map driver for MPC-1211 boards. Interface");
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/maps/netsc520.c linux/drivers/mtd/maps/netsc520.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/maps/netsc520.c 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/maps/netsc520.c 2004-11-17 18:17:59.113302672 +0100
+@@ -3,7 +3,7 @@
+ * Copyright (C) 2001 Mark Langsdorf (mark.langsdorf@amd.com)
+ * based on sc520cdp.c by Sysgo Real-Time Solutions GmbH
+ *
+- * $Id$
++ * $Id$
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -27,6 +27,7 @@
+ #include <linux/module.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
+ #include <asm/io.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+@@ -50,95 +51,41 @@
+ ** recoverable afterwards.
+ */
+
+-static __u8 netsc520_read8(struct map_info *map, unsigned long ofs)
+-{
+- return readb(map->map_priv_1 + ofs);
+-}
+-
+-static __u16 netsc520_read16(struct map_info *map, unsigned long ofs)
+-{
+- return readw(map->map_priv_1 + ofs);
+-}
+-
+-static __u32 netsc520_read32(struct map_info *map, unsigned long ofs)
+-{
+- return readl(map->map_priv_1 + ofs);
+-}
+-
+-static void netsc520_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+-{
+- memcpy_fromio(to, (void *)(map->map_priv_1 + from), len);
+-}
+-
+-static void netsc520_write8(struct map_info *map, __u8 d, unsigned long adr)
+-{
+- writeb(d, map->map_priv_1 + adr);
+-}
+-
+-static void netsc520_write16(struct map_info *map, __u16 d, unsigned long adr)
+-{
+- writew(d, map->map_priv_1 + adr);
+-}
+-
+-static void netsc520_write32(struct map_info *map, __u32 d, unsigned long adr)
+-{
+- writel(d, map->map_priv_1 + adr);
+-}
+-
+-static void netsc520_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+-{
+- memcpy_toio((void *)(map->map_priv_1 + to), from, len);
+-}
+-
+ /* partition_info gives details on the logical partitions that the split the
+ * single flash device into. If the size if zero we use up to the end of the
+ * device. */
+ static struct mtd_partition partition_info[]={
+ {
+- name: "NetSc520 boot kernel",
+- offset: 0,
+- size: 0xc0000
++ .name = "NetSc520 boot kernel",
++ .offset = 0,
++ .size = 0xc0000
+ },
+ {
+- name: "NetSc520 Low BIOS",
+- offset: 0xc0000,
+- size: 0x40000
++ .name = "NetSc520 Low BIOS",
++ .offset = 0xc0000,
++ .size = 0x40000
+ },
+ {
+- name: "NetSc520 file system",
+- offset: 0x100000,
+- size: 0xe80000
++ .name = "NetSc520 file system",
++ .offset = 0x100000,
++ .size = 0xe80000
+ },
+ {
+- name: "NetSc520 High BIOS",
+- offset: 0xf80000,
+- size: 0x80000
++ .name = "NetSc520 High BIOS",
++ .offset = 0xf80000,
++ .size = 0x80000
+ },
+ };
+ #define NUM_PARTITIONS (sizeof(partition_info)/sizeof(partition_info[0]))
+
+-/*
+- * If no idea what is going on here. This is taken from the FlashFX stuff.
+- */
+-#define ROMCS 1
+-
+-
+ #define WINDOW_SIZE 0x00100000
+ #define WINDOW_ADDR 0x00200000
+
+ static struct map_info netsc520_map = {
+- name: "netsc520 Flash Bank",
+- size: WINDOW_SIZE,
+- buswidth: 4,
+- read8: netsc520_read8,
+- read16: netsc520_read16,
+- read32: netsc520_read32,
+- copy_from: netsc520_copy_from,
+- write8: netsc520_write8,
+- write16: netsc520_write16,
+- write32: netsc520_write32,
+- copy_to: netsc520_copy_to,
+- map_priv_2: WINDOW_ADDR
++ .name = "netsc520 Flash Bank",
++ .size = WINDOW_SIZE,
++ .buswidth = 4,
++ .phys = WINDOW_ADDR,
+ };
+
+ #define NUM_FLASH_BANKS (sizeof(netsc520_map)/sizeof(struct map_info))
+@@ -147,13 +94,16 @@
+
+ static int __init init_netsc520(void)
+ {
+- printk(KERN_NOTICE "NetSc520 flash device: %lx at %lx\n", netsc520_map.size, netsc520_map.map_priv_2);
+- netsc520_map.map_priv_1 = (unsigned long)ioremap_nocache(netsc520_map.map_priv_2, netsc520_map.size);
++ printk(KERN_NOTICE "NetSc520 flash device: 0x%lx at 0x%lx\n", netsc520_map.size, netsc520_map.phys);
++ netsc520_map.virt = (unsigned long)ioremap_nocache(netsc520_map.phys, netsc520_map.size);
+
+- if (!netsc520_map.map_priv_1) {
++ if (!netsc520_map.virt) {
+ printk("Failed to ioremap_nocache\n");
+ return -EIO;
+ }
++
++ simple_map_init(&netsc520_map);
++
+ mymtd = do_map_probe("cfi_probe", &netsc520_map);
+ if(!mymtd)
+ mymtd = do_map_probe("map_ram", &netsc520_map);
+@@ -161,11 +111,11 @@
+ mymtd = do_map_probe("map_rom", &netsc520_map);
+
+ if (!mymtd) {
+- iounmap((void *)netsc520_map.map_priv_1);
++ iounmap((void *)netsc520_map.virt);
+ return -ENXIO;
+ }
+
+- mymtd->module = THIS_MODULE;
++ mymtd->owner = THIS_MODULE;
+ add_mtd_partitions( mymtd, partition_info, NUM_PARTITIONS );
+ return 0;
+ }
+@@ -176,9 +126,9 @@
+ del_mtd_partitions(mymtd);
+ map_destroy(mymtd);
+ }
+- if (netsc520_map.map_priv_1) {
+- iounmap((void *)netsc520_map.map_priv_1);
+- netsc520_map.map_priv_1 = 0;
++ if (netsc520_map.virt) {
++ iounmap((void *)netsc520_map.virt);
++ netsc520_map.virt = 0;
+ }
+ }
+
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/maps/nettel.c linux/drivers/mtd/maps/nettel.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/maps/nettel.c 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/maps/nettel.c 2004-11-17 18:17:59.114302520 +0100
+@@ -6,7 +6,7 @@
+ * (C) Copyright 2000-2001, Greg Ungerer (gerg@snapgear.com)
+ * (C) Copyright 2001-2002, SnapGear (www.snapgear.com)
+ *
+- * $Id$
++ * $Id$
+ */
+
+ /****************************************************************************/
+@@ -59,128 +59,72 @@
+
+ /****************************************************************************/
+
+-static __u8 nettel_read8(struct map_info *map, unsigned long ofs)
+-{
+- return(readb(map->map_priv_1 + ofs));
+-}
+-
+-static __u16 nettel_read16(struct map_info *map, unsigned long ofs)
+-{
+- return(readw(map->map_priv_1 + ofs));
+-}
+-
+-static __u32 nettel_read32(struct map_info *map, unsigned long ofs)
+-{
+- return(readl(map->map_priv_1 + ofs));
+-}
+-
+-static void nettel_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+-{
+- memcpy_fromio(to, map->map_priv_1 + from, len);
+-}
+-
+-static void nettel_write8(struct map_info *map, __u8 d, unsigned long adr)
+-{
+- writeb(d, map->map_priv_1 + adr);
+-}
+-
+-static void nettel_write16(struct map_info *map, __u16 d, unsigned long adr)
+-{
+- writew(d, map->map_priv_1 + adr);
+-}
+-
+-static void nettel_write32(struct map_info *map, __u32 d, unsigned long adr)
+-{
+- writel(d, map->map_priv_1 + adr);
+-}
+-
+-static void nettel_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+-{
+- memcpy_toio(map->map_priv_1 + to, from, len);
+-}
+-
+ /****************************************************************************/
+
+ #ifdef CONFIG_MTD_CFI_INTELEXT
+ static struct map_info nettel_intel_map = {
+- name: "SnapGear Intel",
+- size: 0,
+- buswidth: INTEL_BUSWIDTH,
+- read8: nettel_read8,
+- read16: nettel_read16,
+- read32: nettel_read32,
+- copy_from: nettel_copy_from,
+- write8: nettel_write8,
+- write16: nettel_write16,
+- write32: nettel_write32,
+- copy_to: nettel_copy_to
++ .name = "SnapGear Intel",
++ .size = 0,
++ .buswidth = INTEL_BUSWIDTH,
+ };
+
+ static struct mtd_partition nettel_intel_partitions[] = {
+ {
+- name: "SnapGear kernel",
+- offset: 0,
+- size: 0x000e0000
++ .name = "SnapGear kernel",
++ .offset = 0,
++ .size = 0x000e0000
+ },
+ {
+- name: "SnapGear filesystem",
+- offset: 0x00100000,
++ .name = "SnapGear filesystem",
++ .offset = 0x00100000,
+ },
+ {
+- name: "SnapGear config",
+- offset: 0x000e0000,
+- size: 0x00020000
++ .name = "SnapGear config",
++ .offset = 0x000e0000,
++ .size = 0x00020000
+ },
+ {
+- name: "SnapGear Intel",
+- offset: 0
++ .name = "SnapGear Intel",
++ .offset = 0
+ },
+ {
+- name: "SnapGear BIOS Config",
+- offset: 0x007e0000,
+- size: 0x00020000
++ .name = "SnapGear BIOS Config",
++ .offset = 0x007e0000,
++ .size = 0x00020000
+ },
+ {
+- name: "SnapGear BIOS",
+- offset: 0x007e0000,
+- size: 0x00020000
++ .name = "SnapGear BIOS",
++ .offset = 0x007e0000,
++ .size = 0x00020000
+ },
+ };
+ #endif
+
+ static struct map_info nettel_amd_map = {
+- name: "SnapGear AMD",
+- size: AMD_WINDOW_MAXSIZE,
+- buswidth: AMD_BUSWIDTH,
+- read8: nettel_read8,
+- read16: nettel_read16,
+- read32: nettel_read32,
+- copy_from: nettel_copy_from,
+- write8: nettel_write8,
+- write16: nettel_write16,
+- write32: nettel_write32,
+- copy_to: nettel_copy_to
++ .name = "SnapGear AMD",
++ .size = AMD_WINDOW_MAXSIZE,
++ .buswidth = AMD_BUSWIDTH,
+ };
+
+ static struct mtd_partition nettel_amd_partitions[] = {
+ {
+- name: "SnapGear BIOS config",
+- offset: 0x000e0000,
+- size: 0x00010000
++ .name = "SnapGear BIOS config",
++ .offset = 0x000e0000,
++ .size = 0x00010000
+ },
+ {
+- name: "SnapGear BIOS",
+- offset: 0x000f0000,
+- size: 0x00010000
++ .name = "SnapGear BIOS",
++ .offset = 0x000f0000,
++ .size = 0x00010000
+ },
+ {
+- name: "SnapGear AMD",
+- offset: 0
++ .name = "SnapGear AMD",
++ .offset = 0
+ },
+ {
+- name: "SnapGear high BIOS",
+- offset: 0x001f0000,
+- size: 0x00010000
++ .name = "SnapGear high BIOS",
++ .offset = 0x001f0000,
++ .size = 0x00010000
+ }
+ };
+
+@@ -328,18 +272,20 @@
+ *amdpar = SC520_PAR(SC520_PAR_BOOTCS, amdaddr, maxsize);
+ __asm__ ("wbinvd");
+
+- nettel_amd_map.map_priv_1 = (unsigned long)
++ nettel_amd_map.phys = amdaddr;
++ nettel_amd_map.virt = (unsigned long)
+ ioremap_nocache(amdaddr, maxsize);
+- if (!nettel_amd_map.map_priv_1) {
++ if (!nettel_amd_map.virt) {
+ printk("SNAPGEAR: failed to ioremap() BOOTCS\n");
+ return(-EIO);
+ }
++ simple_map_init(&nettel_amd_map);
+
+ if ((amd_mtd = do_map_probe("jedec_probe", &nettel_amd_map))) {
+ printk(KERN_NOTICE "SNAPGEAR: AMD flash device size = %dK\n",
+ amd_mtd->size>>10);
+
+- amd_mtd->module = THIS_MODULE;
++ amd_mtd->owner = THIS_MODULE;
+
+ /* The high BIOS partition is only present for 2MB units */
+ num_amd_partitions = NUM_AMD_PARTITIONS;
+@@ -387,8 +333,8 @@
+
+ /* Destroy useless AMD MTD mapping */
+ amd_mtd = NULL;
+- iounmap((void *) nettel_amd_map.map_priv_1);
+- nettel_amd_map.map_priv_1 = (unsigned long) NULL;
++ iounmap((void *) nettel_amd_map.virt);
++ nettel_amd_map.virt = (unsigned long) NULL;
+ #else
+ /* Only AMD flash supported */
+ return(-ENXIO);
+@@ -411,16 +357,18 @@
+
+ /* Probe for the the size of the first Intel flash */
+ nettel_intel_map.size = maxsize;
+- nettel_intel_map.map_priv_1 = (unsigned long)
++ nettel_intel_map.phys = intel0addr;
++ nettel_intel_map.virt = (unsigned long)
+ ioremap_nocache(intel0addr, maxsize);
+- if (!nettel_intel_map.map_priv_1) {
++ if (!nettel_intel_map.virt) {
+ printk("SNAPGEAR: failed to ioremap() ROMCS1\n");
+ return(-EIO);
+ }
++ simple_map_init(&nettel_intel_map);
+
+ intel_mtd = do_map_probe("cfi_probe", &nettel_intel_map);
+ if (! intel_mtd) {
+- iounmap((void *) nettel_intel_map.map_priv_1);
++ iounmap((void *) nettel_intel_map.virt);
+ return(-ENXIO);
+ }
+
+@@ -441,19 +389,19 @@
+ /* Delete the old map and probe again to do both chips */
+ map_destroy(intel_mtd);
+ intel_mtd = NULL;
+- iounmap((void *) nettel_intel_map.map_priv_1);
++ iounmap((void *) nettel_intel_map.virt);
+
+ nettel_intel_map.size = maxsize;
+- nettel_intel_map.map_priv_1 = (unsigned long)
++ nettel_intel_map.virt = (unsigned long)
+ ioremap_nocache(intel0addr, maxsize);
+- if (!nettel_intel_map.map_priv_1) {
++ if (!nettel_intel_map.virt) {
+ printk("SNAPGEAR: failed to ioremap() ROMCS1/2\n");
+ return(-EIO);
+ }
+
+ intel_mtd = do_map_probe("cfi_probe", &nettel_intel_map);
+ if (! intel_mtd) {
+- iounmap((void *) nettel_intel_map.map_priv_1);
++ iounmap((void *) nettel_intel_map.virt);
+ return(-ENXIO);
+ }
+
+@@ -468,7 +416,7 @@
+ printk(KERN_NOTICE "SNAPGEAR: Intel flash device size = %dK\n",
+ (intel_mtd->size >> 10));
+
+- intel_mtd->module = THIS_MODULE;
++ intel_mtd->owner = THIS_MODULE;
+
+ #ifndef CONFIG_BLK_DEV_INITRD
+ ROOT_DEV = MKDEV(MTD_BLOCK_MAJOR, 1);
+@@ -523,18 +471,18 @@
+ del_mtd_partitions(amd_mtd);
+ map_destroy(amd_mtd);
+ }
+- if (nettel_amd_map.map_priv_1) {
+- iounmap((void *)nettel_amd_map.map_priv_1);
+- nettel_amd_map.map_priv_1 = 0;
++ if (nettel_amd_map.virt) {
++ iounmap((void *)nettel_amd_map.virt);
++ nettel_amd_map.virt = 0;
+ }
+ #ifdef CONFIG_MTD_CFI_INTELEXT
+ if (intel_mtd) {
+ del_mtd_partitions(intel_mtd);
+ map_destroy(intel_mtd);
+ }
+- if (nettel_intel_map.map_priv_1) {
+- iounmap((void *)nettel_intel_map.map_priv_1);
+- nettel_intel_map.map_priv_1 = 0;
++ if (nettel_intel_map.virt) {
++ iounmap((void *)nettel_intel_map.virt);
++ nettel_intel_map.virt = 0;
+ }
+ #endif
+ }
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/maps/ocelot.c linux/drivers/mtd/maps/ocelot.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/maps/ocelot.c 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/maps/ocelot.c 2004-11-17 18:17:59.115302368 +0100
+@@ -1,5 +1,5 @@
+ /*
+- * $Id$
++ * $Id$
+ *
+ * Flash on Momenco Ocelot
+ */
+@@ -7,6 +7,7 @@
+ #include <linux/module.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
+ #include <asm/io.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+@@ -20,47 +21,23 @@
+ #define NVRAM_WINDOW_SIZE 0x00007FF0
+ #define NVRAM_BUSWIDTH 1
+
+-extern int parse_redboot_partitions(struct mtd_info *master, struct mtd_partition **pparts);
+-
+ static unsigned int cacheflush = 0;
+
+ static struct mtd_info *flash_mtd;
+ static struct mtd_info *nvram_mtd;
+
+-__u8 ocelot_read8(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readb(map->map_priv_1 + ofs);
+-}
+-
+-void ocelot_write8(struct map_info *map, __u8 d, unsigned long adr)
+-{
+- cacheflush = 1;
+- __raw_writeb(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-void ocelot_copy_from_cache(struct map_info *map, void *to, unsigned long from, ssize_t len)
+-{
+- if (cacheflush) {
+- dma_cache_inv(map->map_priv_2, map->size);
+- cacheflush = 0;
+- }
+- memcpy_fromio(to, map->map_priv_1 + from, len);
+-}
+-
+-void ocelot_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
++static void ocelot_ram_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf)
+ {
+- memcpy_fromio(to, map->map_priv_1 + from, len);
+-}
++ struct map_info *map = (struct map_info *)mtd->priv;
++ size_t done = 0;
+
+-void ocelot_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+-{
+ /* If we use memcpy, it does word-wide writes. Even though we told the
+ GT64120A that it's an 8-bit wide region, word-wide writes don't work.
+ We end up just writing the first byte of the four to all four bytes.
+ So we have this loop instead */
++ *retlen = len;
+ while(len) {
+- __raw_writeb(*(unsigned char *) from, map->map_priv_1 + to);
++ __raw_writeb(*(unsigned char *) from, map->virt + to);
+ from++;
+ to++;
+ len--;
+@@ -70,24 +47,21 @@
+ static struct mtd_partition *parsed_parts;
+
+ struct map_info ocelot_flash_map = {
+- name: "Ocelot boot flash",
+- size: FLASH_WINDOW_SIZE,
+- buswidth: FLASH_BUSWIDTH,
+- read8: ocelot_read8,
+- copy_from: ocelot_copy_from_cache,
+- write8: ocelot_write8,
++ .name = "Ocelot boot flash",
++ .size = FLASH_WINDOW_SIZE,
++ .buswidth = FLASH_BUSWIDTH,
++ .phys = FLASH_WINDOW_ADDR,
+ };
+
+ struct map_info ocelot_nvram_map = {
+- name: "Ocelot NVRAM",
+- size: NVRAM_WINDOW_SIZE,
+- buswidth: NVRAM_BUSWIDTH,
+- read8: ocelot_read8,
+- copy_from: ocelot_copy_from,
+- write8: ocelot_write8,
+- copy_to: ocelot_copy_to
++ .name = "Ocelot NVRAM",
++ .size = NVRAM_WINDOW_SIZE,
++ .buswidth = NVRAM_BUSWIDTH,
++ .phys = NVRAM_WINDOW_ADDR,
+ };
+
++static const char *probes[] = { "RedBoot", NULL };
++
+ static int __init init_ocelot_maps(void)
+ {
+ void *pld;
+@@ -107,12 +81,13 @@
+ iounmap(pld);
+
+ /* Now ioremap the NVRAM space */
+- ocelot_nvram_map.map_priv_1 = (unsigned long)ioremap_nocache(NVRAM_WINDOW_ADDR, NVRAM_WINDOW_SIZE);
+- if (!ocelot_nvram_map.map_priv_1) {
++ ocelot_nvram_map.virt = (unsigned long)ioremap_nocache(NVRAM_WINDOW_ADDR, NVRAM_WINDOW_SIZE);
++ if (!ocelot_nvram_map.virt) {
+ printk(KERN_NOTICE "Failed to ioremap Ocelot NVRAM space\n");
+ return -EIO;
+ }
+- // ocelot_nvram_map.map_priv_2 = ocelot_nvram_map.map_priv_1;
++
++ simple_map_init(&ocelot_nvram_map);
+
+ /* And do the RAM probe on it to get an MTD device */
+ nvram_mtd = do_map_probe("map_ram", &ocelot_nvram_map);
+@@ -120,22 +95,21 @@
+ printk("NVRAM probe failed\n");
+ goto fail_1;
+ }
+- nvram_mtd->module = THIS_MODULE;
++ nvram_mtd->owner = THIS_MODULE;
+ nvram_mtd->erasesize = 16;
++ /* Override the write() method */
++ nvram_mtd->write = ocelot_ram_write;
+
+ /* Now map the flash space */
+- ocelot_flash_map.map_priv_1 = (unsigned long)ioremap_nocache(FLASH_WINDOW_ADDR, FLASH_WINDOW_SIZE);
+- if (!ocelot_flash_map.map_priv_1) {
++ ocelot_flash_map.virt = (unsigned long)ioremap_nocache(FLASH_WINDOW_ADDR, FLASH_WINDOW_SIZE);
++ if (!ocelot_flash_map.virt) {
+ printk(KERN_NOTICE "Failed to ioremap Ocelot flash space\n");
+ goto fail_2;
+ }
+ /* Now the cached version */
+- ocelot_flash_map.map_priv_2 = (unsigned long)__ioremap(FLASH_WINDOW_ADDR, FLASH_WINDOW_SIZE, 0);
++ ocelot_flash_map.cached = (unsigned long)__ioremap(FLASH_WINDOW_ADDR, FLASH_WINDOW_SIZE, 0);
+
+- if (!ocelot_flash_map.map_priv_2) {
+- /* Doesn't matter if it failed. Just use the uncached version */
+- ocelot_flash_map.map_priv_2 = ocelot_flash_map.map_priv_1;
+- }
++ simple_map_init(&ocelot_flash_map);
+
+ /* Only probe for flash if the write jumper is present */
+ if (brd_status & 0x40) {
+@@ -155,10 +129,10 @@
+
+ add_mtd_device(nvram_mtd);
+
+- flash_mtd->module = THIS_MODULE;
+- nr_parts = parse_redboot_partitions(flash_mtd, &parsed_parts);
++ flash_mtd->owner = THIS_MODULE;
++ nr_parts = parse_mtd_partitions(flash_mtd, probes, &parsed_parts, 0);
+
+- if (nr_parts)
++ if (nr_parts > 0)
+ add_mtd_partitions(flash_mtd, parsed_parts, nr_parts);
+ else
+ add_mtd_device(flash_mtd);
+@@ -166,14 +140,13 @@
+ return 0;
+
+ fail3:
+- iounmap((void *)ocelot_flash_map.map_priv_1);
+- if (ocelot_flash_map.map_priv_2 &&
+- ocelot_flash_map.map_priv_2 != ocelot_flash_map.map_priv_1)
+- iounmap((void *)ocelot_flash_map.map_priv_2);
++ iounmap((void *)ocelot_flash_map.virt);
++ if (ocelot_flash_map.cached)
++ iounmap((void *)ocelot_flash_map.cached);
+ fail_2:
+ map_destroy(nvram_mtd);
+ fail_1:
+- iounmap((void *)ocelot_nvram_map.map_priv_1);
++ iounmap((void *)ocelot_nvram_map.virt);
+
+ return -ENXIO;
+ }
+@@ -182,16 +155,16 @@
+ {
+ del_mtd_device(nvram_mtd);
+ map_destroy(nvram_mtd);
+- iounmap((void *)ocelot_nvram_map.map_priv_1);
++ iounmap((void *)ocelot_nvram_map.virt);
+
+ if (parsed_parts)
+ del_mtd_partitions(flash_mtd);
+ else
+ del_mtd_device(flash_mtd);
+ map_destroy(flash_mtd);
+- iounmap((void *)ocelot_flash_map.map_priv_1);
+- if (ocelot_flash_map.map_priv_2 != ocelot_flash_map.map_priv_1)
+- iounmap((void *)ocelot_flash_map.map_priv_2);
++ iounmap((void *)ocelot_flash_map.virt);
++ if (ocelot_flash_map.cached)
++ iounmap((void *)ocelot_flash_map.cached);
+ }
+
+ module_init(init_ocelot_maps);
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/maps/octagon-5066.c linux/drivers/mtd/maps/octagon-5066.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/maps/octagon-5066.c 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/maps/octagon-5066.c 2004-11-17 18:17:59.117302064 +0100
+@@ -1,4 +1,4 @@
+-// $Id$
++// $Id$
+ /* ######################################################################
+
+ Octagon 5066 MTD Driver.
+@@ -31,6 +31,7 @@
+ #include <asm/io.h>
+
+ #include <linux/mtd/map.h>
++#include <linux/mtd/mtd.h>
+
+ #define WINDOW_START 0xe8000
+ #define WINDOW_LENGTH 0x8000
+@@ -151,32 +152,34 @@
+
+ static struct map_info oct5066_map[2] = {
+ {
+- name: "Octagon 5066 Socket",
+- size: 512 * 1024,
+- buswidth: 1,
+- read8: oct5066_read8,
+- read16: oct5066_read16,
+- read32: oct5066_read32,
+- copy_from: oct5066_copy_from,
+- write8: oct5066_write8,
+- write16: oct5066_write16,
+- write32: oct5066_write32,
+- copy_to: oct5066_copy_to,
+- map_priv_1: 1<<6
++ .name = "Octagon 5066 Socket",
++ .phys = NO_XIP,
++ .size = 512 * 1024,
++ .buswidth = 1,
++ .read8 = oct5066_read8,
++ .read16 = oct5066_read16,
++ .read32 = oct5066_read32,
++ .copy_from = oct5066_copy_from,
++ .write8 = oct5066_write8,
++ .write16 = oct5066_write16,
++ .write32 = oct5066_write32,
++ .copy_to = oct5066_copy_to,
++ .map_priv_1 = 1<<6
+ },
+ {
+- name: "Octagon 5066 Internal Flash",
+- size: 2 * 1024 * 1024,
+- buswidth: 1,
+- read8: oct5066_read8,
+- read16: oct5066_read16,
+- read32: oct5066_read32,
+- copy_from: oct5066_copy_from,
+- write8: oct5066_write8,
+- write16: oct5066_write16,
+- write32: oct5066_write32,
+- copy_to: oct5066_copy_to,
+- map_priv_1: 2<<6
++ .name = "Octagon 5066 Internal Flash",
++ .phys = NO_XIP,
++ .size = 2 * 1024 * 1024,
++ .buswidth = 1,
++ .read8 = oct5066_read8,
++ .read16 = oct5066_read16,
++ .read32 = oct5066_read32,
++ .copy_from = oct5066_copy_from,
++ .write8 = oct5066_write8,
++ .write16 = oct5066_write16,
++ .write32 = oct5066_write32,
++ .copy_to = oct5066_copy_to,
++ .map_priv_1 = 2<<6
+ }
+ };
+
+@@ -244,6 +247,7 @@
+ }
+ if (OctProbe() != 0) {
+ printk(KERN_NOTICE "5066: Octagon Probe Failed, is this an Octagon 5066 SBC?\n");
++ iounmap((void *)iomapadr);
+ ret = -EAGAIN;
+ goto out_unmap;
+ }
+@@ -261,7 +265,7 @@
+ if (!oct5066_mtd[i])
+ oct5066_mtd[i] = do_map_probe("map_rom", &oct5066_map[i]);
+ if (oct5066_mtd[i]) {
+- oct5066_mtd[i]->module = THIS_MODULE;
++ oct5066_mtd[i]->owner = THIS_MODULE;
+ add_mtd_device(oct5066_mtd[i]);
+ }
+ }
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/maps/omap-toto-flash.c linux/drivers/mtd/maps/omap-toto-flash.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/maps/omap-toto-flash.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/mtd/maps/omap-toto-flash.c 2004-11-17 18:17:59.000000000 +0100
+@@ -0,0 +1,137 @@
++/*
++ * NOR Flash memory access on TI Toto board
++ *
++ * jzhang@ti.com (C) 2003 Texas Instruments.
++ *
++ * (C) 2002 MontVista Software, Inc.
++ *
++ * $Id$
++ */
++
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/kernel.h>
++
++#include <linux/errno.h>
++#include <linux/init.h>
++
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/map.h>
++#include <linux/mtd/partitions.h>
++
++#include <asm/hardware.h>
++#include <asm/io.h>
++
++
++#ifndef CONFIG_ARCH_OMAP
++#error This is for OMAP architecture only
++#endif
++
++//these lines need be moved to a hardware header file
++#define OMAP_TOTO_FLASH_BASE 0xd8000000
++#define OMAP_TOTO_FLASH_SIZE 0x80000
++
++static struct map_info omap_toto_map_flash = {
++ .name = "OMAP Toto flash",
++ .buswidth = 2,
++ .virt = OMAP_TOTO_FLASH_BASE,
++};
++
++
++static struct mtd_partition toto_flash_partitions[] = {
++ {
++ .name = "BootLoader",
++ .size = 0x00040000, /* hopefully u-boot will stay 128k + 128*/
++ .offset = 0,
++ .mask_flags = MTD_WRITEABLE, /* force read-only */
++ }, {
++ .name = "ReservedSpace",
++ .size = 0x00030000,
++ .offset = MTDPART_OFS_APPEND,
++ //mask_flags: MTD_WRITEABLE, /* force read-only */
++ }, {
++ .name = "EnvArea", /* bottom 64KiB for env vars */
++ .size = MTDPART_SIZ_FULL,
++ .offset = MTDPART_OFS_APPEND,
++ }
++};
++
++static struct mtd_partition *parsed_parts;
++
++static struct mtd_info *flash_mtd;
++
++static int __init init_flash (void)
++{
++
++ struct mtd_partition *parts;
++ int nb_parts = 0;
++ int parsed_nr_parts = 0;
++ const char *part_type;
++
++ /*
++ * Static partition definition selection
++ */
++ part_type = "static";
++
++ parts = toto_flash_partitions;
++ nb_parts = ARRAY_SIZE(toto_flash_partitions);
++ omap_toto_map_flash.size = OMAP_TOTO_FLASH_SIZE;
++ omap_toto_map_flash.phys = virt_to_phys(OMAP_TOTO_FLASH_BASE);
++
++ simple_map_init(&omap_toto_map_flash);
++ /*
++ * Now let's probe for the actual flash. Do it here since
++ * specific machine settings might have been set above.
++ */
++ printk(KERN_NOTICE "OMAP toto flash: probing %d-bit flash bus\n",
++ omap_toto_map_flash.buswidth*8);
++ flash_mtd = do_map_probe("jedec_probe", &omap_toto_map_flash);
++ if (!flash_mtd)
++ return -ENXIO;
++
++ if (parsed_nr_parts > 0) {
++ parts = parsed_parts;
++ nb_parts = parsed_nr_parts;
++ }
++
++ if (nb_parts == 0) {
++ printk(KERN_NOTICE "OMAP toto flash: no partition info available,"
++ "registering whole flash at once\n");
++ if (add_mtd_device(flash_mtd)){
++ return -ENXIO;
++ }
++ } else {
++ printk(KERN_NOTICE "Using %s partition definition\n",
++ part_type);
++ return add_mtd_partitions(flash_mtd, parts, nb_parts);
++ }
++ return 0;
++}
++
++int __init omap_toto_mtd_init(void)
++{
++ int status;
++
++ if (status = init_flash()) {
++ printk(KERN_ERR "OMAP Toto Flash: unable to init map for toto flash\n");
++ }
++ return status;
++}
++
++static void __exit omap_toto_mtd_cleanup(void)
++{
++ if (flash_mtd) {
++ del_mtd_partitions(flash_mtd);
++ map_destroy(flash_mtd);
++ if (parsed_parts)
++ kfree(parsed_parts);
++ }
++}
++
++module_init(omap_toto_mtd_init);
++module_exit(omap_toto_mtd_cleanup);
++
++MODULE_AUTHOR("Jian Zhang");
++MODULE_DESCRIPTION("OMAP Toto board map driver");
++MODULE_LICENSE("GPL");
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/maps/pb1xxx-flash.c linux/drivers/mtd/maps/pb1xxx-flash.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/maps/pb1xxx-flash.c 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/maps/pb1xxx-flash.c 2004-11-17 18:17:59.119301760 +0100
+@@ -3,12 +3,13 @@
+ *
+ * (C) 2001 Pete Popov <ppopov@mvista.com>
+ *
+- * $Id$
++ * $Id$
+ */
+
+ #include <linux/config.h>
+ #include <linux/module.h>
+ #include <linux/types.h>
++#include <linux/init.h>
+ #include <linux/kernel.h>
+
+ #include <linux/mtd/mtd.h>
+@@ -25,210 +26,110 @@
+ #endif
+
+ #ifdef CONFIG_MIPS_PB1000
++
+ #define WINDOW_ADDR 0x1F800000
+ #define WINDOW_SIZE 0x800000
+-#endif
+-
+-__u8 physmap_read8(struct map_info *map, unsigned long ofs)
+-{
+- __u8 ret;
+- ret = __raw_readb(map->map_priv_1 + ofs);
+- DBG("read8 from %x, %x\n", (unsigned)(map->map_priv_1 + ofs), ret);
+- return ret;
+-}
+-
+-__u16 physmap_read16(struct map_info *map, unsigned long ofs)
+-{
+- __u16 ret;
+- ret = __raw_readw(map->map_priv_1 + ofs);
+- DBG("read16 from %x, %x\n", (unsigned)(map->map_priv_1 + ofs), ret);
+- return ret;
+-}
+-
+-__u32 physmap_read32(struct map_info *map, unsigned long ofs)
+-{
+- __u32 ret;
+- ret = __raw_readl(map->map_priv_1 + ofs);
+- DBG("read32 from %x, %x\n", (unsigned)(map->map_priv_1 + ofs), ret);
+- return ret;
+-}
+-
+-void physmap_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+-{
+- DBG("physmap_copy from %x to %x\n", (unsigned)from, (unsigned)to);
+- memcpy_fromio(to, map->map_priv_1 + from, len);
+-}
+-
+-void physmap_write8(struct map_info *map, __u8 d, unsigned long adr)
+-{
+- DBG("write8 at %x, %x\n", (unsigned)(map->map_priv_1 + adr), d);
+- __raw_writeb(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-void physmap_write16(struct map_info *map, __u16 d, unsigned long adr)
+-{
+- DBG("write16 at %x, %x\n", (unsigned)(map->map_priv_1 + adr), d);
+- __raw_writew(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-void physmap_write32(struct map_info *map, __u32 d, unsigned long adr)
+-{
+- DBG("write32 at %x, %x\n", (unsigned)(map->map_priv_1 + adr), d);
+- __raw_writel(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-void physmap_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+-{
+- DBG("physmap_copy_to %x from %x\n", (unsigned)to, (unsigned)from);
+- memcpy_toio(map->map_priv_1 + to, from, len);
+-}
+-
+-
+-
+-static struct map_info pb1xxx_map = {
+- name: "Pb1xxx flash",
+- read8: physmap_read8,
+- read16: physmap_read16,
+- read32: physmap_read32,
+- copy_from: physmap_copy_from,
+- write8: physmap_write8,
+- write16: physmap_write16,
+- write32: physmap_write32,
+- copy_to: physmap_copy_to,
+-};
+
+-
+-#ifdef CONFIG_MIPS_PB1000
+-
+-static unsigned long flash_size = 0x00800000;
+-static unsigned char flash_buswidth = 4;
+ static struct mtd_partition pb1xxx_partitions[] = {
+ {
+- name: "yamon env",
+- size: 0x00020000,
+- offset: 0,
+- mask_flags: MTD_WRITEABLE
+- },{
+- name: "User FS",
+- size: 0x003e0000,
+- offset: 0x20000,
+- },{
+- name: "boot code",
+- size: 0x100000,
+- offset: 0x400000,
+- mask_flags: MTD_WRITEABLE
+- },{
+- name: "raw/kernel",
+- size: 0x300000,
+- offset: 0x500000
+- }
++ .name = "yamon env",
++ .size = 0x00020000,
++ .offset = 0,
++ .mask_flags = MTD_WRITEABLE},
++ {
++ .name = "User FS",
++ .size = 0x003e0000,
++ .offset = 0x20000,},
++ {
++ .name = "boot code",
++ .size = 0x100000,
++ .offset = 0x400000,
++ .mask_flags = MTD_WRITEABLE},
++ {
++ .name = "raw/kernel",
++ .size = 0x300000,
++ .offset = 0x500000}
+ };
+
+ #elif defined(CONFIG_MIPS_PB1500) || defined(CONFIG_MIPS_PB1100)
+
+-static unsigned char flash_buswidth = 4;
+ #if defined(CONFIG_MTD_PB1500_BOOT) && defined(CONFIG_MTD_PB1500_USER)
+-/* both 32MiB banks will be used. Combine the first 32MiB bank and the
+- * first 28MiB of the second bank together into a single jffs/jffs2
++/* both 32MB banks will be used. Combine the first 32MB bank and the
++ * first 28MB of the second bank together into a single jffs/jffs2
+ * partition.
+ */
+-static unsigned long flash_size = 0x04000000;
+ #define WINDOW_ADDR 0x1C000000
+ #define WINDOW_SIZE 0x4000000
+ static struct mtd_partition pb1xxx_partitions[] = {
+ {
+- name: "User FS",
+- size: 0x3c00000,
+- offset: 0x0000000
+- },{
+- name: "yamon",
+- size: 0x0100000,
+- offset: 0x3c00000,
+- mask_flags: MTD_WRITEABLE
+- },{
+- name: "raw kernel",
+- size: 0x02c0000,
+- offset: 0x3d00000
++ .name = "User FS",
++ .size = 0x3c00000,
++ .offset = 0x0000000
++ },{
++ .name = "yamon",
++ .size = 0x0100000,
++ .offset = 0x3c00000,
++ .mask_flags = MTD_WRITEABLE
++ },{
++ .name = "raw kernel",
++ .size = 0x02c0000,
++ .offset = 0x3d00000
+ }
+ };
+ #elif defined(CONFIG_MTD_PB1500_BOOT) && !defined(CONFIG_MTD_PB1500_USER)
+-static unsigned long flash_size = 0x02000000;
+ #define WINDOW_ADDR 0x1E000000
+ #define WINDOW_SIZE 0x2000000
+ static struct mtd_partition pb1xxx_partitions[] = {
+ {
+- name: "User FS",
+- size: 0x1c00000,
+- offset: 0x0000000
+- },{
+- name: "yamon",
+- size: 0x0100000,
+- offset: 0x1c00000,
+- mask_flags: MTD_WRITEABLE
+- },{
+- name: "raw kernel",
+- size: 0x02c0000,
+- offset: 0x1d00000
++ .name = "User FS",
++ .size = 0x1c00000,
++ .offset = 0x0000000
++ },{
++ .name = "yamon",
++ .size = 0x0100000,
++ .offset = 0x1c00000,
++ .mask_flags = MTD_WRITEABLE
++ },{
++ .name = "raw kernel",
++ .size = 0x02c0000,
++ .offset = 0x1d00000
+ }
+ };
+ #elif !defined(CONFIG_MTD_PB1500_BOOT) && defined(CONFIG_MTD_PB1500_USER)
+-static unsigned long flash_size = 0x02000000;
+ #define WINDOW_ADDR 0x1C000000
+ #define WINDOW_SIZE 0x2000000
+ static struct mtd_partition pb1xxx_partitions[] = {
+ {
+- name: "User FS",
+- size: 0x1e00000,
+- offset: 0x0000000
+- },{
+- name: "raw kernel",
+- size: 0x0200000,
+- offset: 0x1e00000,
++ .name = "User FS",
++ .size = 0x1e00000,
++ .offset = 0x0000000
++ },{
++ .name = "raw kernel",
++ .size = 0x0200000,
++ .offset = 0x1e00000,
+ }
+ };
+ #else
+ #error MTD_PB1500 define combo error /* should never happen */
+ #endif
+-#elif defined(CONFIG_MTD_BOSPORUS)
+-static unsigned char flash_buswidth = 2;
+-static unsigned long flash_size = 0x02000000;
+-#define WINDOW_ADDR 0x1F000000
+-#define WINDOW_SIZE 0x2000000
+-static struct mtd_partition pb1xxx_partitions[] = {
+- {
+- name: "User FS",
+- size: 0x00400000,
+- offset: 0x00000000,
+- },{
+- name: "Yamon-2",
+- size: 0x00100000,
+- offset: 0x00400000,
+- },{
+- name: "Root FS",
+- size: 0x00700000,
+- offset: 0x00500000,
+- },{
+- name: "Yamon-1",
+- size: 0x00100000,
+- offset: 0x00C00000,
+- },{
+- name: "Kernel",
+- size: 0x00300000,
+- offset: 0x00D00000,
+- }
+-};
+ #else
+ #error Unsupported board
+ #endif
+
++#define NAME "Pb1x00 Linux Flash"
++#define PADDR WINDOW_ADDR
++#define BUSWIDTH 4
++#define SIZE WINDOW_SIZE
++#define PARTITIONS 4
++
++static struct map_info pb1xxx_mtd_map = {
++ .name = NAME,
++ .size = SIZE,
++ .buswidth = BUSWIDTH,
++ .phys = PADDR,
++};
+
+-#define NB_OF(x) (sizeof(x)/sizeof(x[0]))
+-
+-static struct mtd_partition *parsed_parts;
+-static struct mtd_info *mymtd;
++static struct mtd_info *pb1xxx_mtd;
+
+ int __init pb1xxx_mtd_init(void)
+ {
+@@ -236,40 +137,37 @@
+ int nb_parts = 0;
+ char *part_type;
+
+- /* Default flash buswidth */
+- pb1xxx_map.buswidth = flash_buswidth;
+-
+ /*
+ * Static partition definition selection
+ */
+ part_type = "static";
+ parts = pb1xxx_partitions;
+- nb_parts = NB_OF(pb1xxx_partitions);
+- pb1xxx_map.size = flash_size;
++ nb_parts = ARRAY_SIZE(pb1xxx_partitions);
+
+ /*
+ * Now let's probe for the actual flash. Do it here since
+ * specific machine settings might have been set above.
+ */
+ printk(KERN_NOTICE "Pb1xxx flash: probing %d-bit flash bus\n",
+- pb1xxx_map.buswidth*8);
+- pb1xxx_map.map_priv_1 =
+- (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE);
+- mymtd = do_map_probe("cfi_probe", &pb1xxx_map);
+- if (!mymtd) return -ENXIO;
+- mymtd->module = THIS_MODULE;
++ BUSWIDTH*8);
++ pb1xxx_mtd_map.virt = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE);
++
++ simple_map_init(&pb1xxx_mtd_map);
++
++ pb1xxx_mtd = do_map_probe("cfi_probe", &pb1xxx_mtd_map);
++ if (!pb1xxx_mtd) return -ENXIO;
++ pb1xxx_mtd->owner = THIS_MODULE;
+
+- add_mtd_partitions(mymtd, parts, nb_parts);
++ add_mtd_partitions(pb1xxx_mtd, parts, nb_parts);
+ return 0;
+ }
+
+ static void __exit pb1xxx_mtd_cleanup(void)
+ {
+- if (mymtd) {
+- del_mtd_partitions(mymtd);
+- map_destroy(mymtd);
+- if (parsed_parts)
+- kfree(parsed_parts);
++ if (pb1xxx_mtd) {
++ del_mtd_partitions(pb1xxx_mtd);
++ map_destroy(pb1xxx_mtd);
++ iounmap((void *) pb1xxx_mtd_map.virt);
+ }
+ }
+
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/maps/pci.c linux/drivers/mtd/maps/pci.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/maps/pci.c 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/maps/pci.c 2004-11-17 18:17:59.121301456 +0100
+@@ -7,7 +7,7 @@
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+- * $Id$
++ * $Id$
+ *
+ * Generic PCI memory map driver. We support the following boards:
+ * - Intel IQ80310 ATU.
+@@ -98,10 +98,10 @@
+ }
+
+ static struct mtd_pci_info intel_iq80310_info = {
+- init: intel_iq80310_init,
+- exit: intel_iq80310_exit,
+- translate: intel_iq80310_translate,
+- map_name: "cfi_probe",
++ .init = intel_iq80310_init,
++ .exit = intel_iq80310_exit,
++ .translate = intel_iq80310_translate,
++ .map_name = "cfi_probe",
+ };
+
+ /*
+@@ -181,10 +181,10 @@
+ }
+
+ static struct mtd_pci_info intel_dc21285_info = {
+- init: intel_dc21285_init,
+- exit: intel_dc21285_exit,
+- translate: intel_dc21285_translate,
+- map_name: "jedec_probe",
++ .init = intel_dc21285_init,
++ .exit = intel_dc21285_exit,
++ .translate = intel_dc21285_translate,
++ .map_name = "jedec_probe",
+ };
+
+ /*
+@@ -193,22 +193,20 @@
+
+ static struct pci_device_id mtd_pci_ids[] __devinitdata = {
+ {
+- vendor: PCI_VENDOR_ID_INTEL,
+- device: 0x530d,
+- subvendor: PCI_ANY_ID,
+- subdevice: PCI_ANY_ID,
+- class: PCI_CLASS_MEMORY_OTHER << 8,
+- class_mask: 0xffff00,
+- driver_data: (unsigned long)&intel_iq80310_info,
++ .vendor = PCI_VENDOR_ID_INTEL,
++ .device = 0x530d,
++ .subvendor = PCI_ANY_ID,
++ .subdevice = PCI_ANY_ID,
++ .class = PCI_CLASS_MEMORY_OTHER << 8,
++ .class_mask = 0xffff00,
++ .driver_data = (unsigned long)&intel_iq80310_info,
+ },
+ {
+- vendor: PCI_VENDOR_ID_DEC,
+- device: PCI_DEVICE_ID_DEC_21285,
+- subvendor: 0, /* DC21285 defaults to 0 on reset */
+- subdevice: 0, /* DC21285 defaults to 0 on reset */
+- class: 0,
+- class_mask: 0,
+- driver_data: (unsigned long)&intel_dc21285_info,
++ .vendor = PCI_VENDOR_ID_DEC,
++ .device = PCI_DEVICE_ID_DEC_21285,
++ .subvendor = 0, /* DC21285 defaults to 0 on reset */
++ .subdevice = 0, /* DC21285 defaults to 0 on reset */
++ .driver_data = (unsigned long)&intel_dc21285_info,
+ },
+ { 0, }
+ };
+@@ -275,14 +273,15 @@
+ }
+
+ static struct map_info mtd_pci_map = {
+- read8: mtd_pci_read8,
+- read16: mtd_pci_read16,
+- read32: mtd_pci_read32,
+- copy_from: mtd_pci_copyfrom,
+- write8: mtd_pci_write8,
+- write16: mtd_pci_write16,
+- write32: mtd_pci_write32,
+- copy_to: mtd_pci_copyto,
++ .phys = NO_XIP,
++ .read8 = mtd_pci_read8,
++ .read16 = mtd_pci_read16,
++ .read32 = mtd_pci_read32,
++ .copy_from = mtd_pci_copyfrom,
++ .write8 = mtd_pci_write8,
++ .write16 = mtd_pci_write16,
++ .write32 = mtd_pci_write32,
++ .copy_to = mtd_pci_copyto,
+ };
+
+ static int __devinit
+@@ -322,7 +321,7 @@
+ if (!mtd)
+ goto release;
+
+- mtd->module = THIS_MODULE;
++ mtd->owner = THIS_MODULE;
+ add_mtd_device(mtd);
+
+ pci_set_drvdata(dev, mtd);
+@@ -359,10 +358,10 @@
+ }
+
+ static struct pci_driver mtd_pci_driver = {
+- name: "MTD PCI",
+- probe: mtd_pci_probe,
+- remove: __devexit_p(mtd_pci_remove),
+- id_table: mtd_pci_ids,
++ .name = "MTD PCI",
++ .probe = mtd_pci_probe,
++ .remove = __devexit_p(mtd_pci_remove),
++ .id_table = mtd_pci_ids,
+ };
+
+ static int __init mtd_pci_maps_init(void)
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/maps/pcmciamtd.c linux/drivers/mtd/maps/pcmciamtd.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/maps/pcmciamtd.c 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/maps/pcmciamtd.c 2004-11-17 18:17:59.122301304 +0100
+@@ -1,5 +1,5 @@
+ /*
+- * $Id$
++ * $Id$
+ *
+ * pcmciamtd.c - MTD driver for PCMCIA flash memory cards
+ *
+@@ -14,6 +14,7 @@
+ #include <linux/module.h>
+ #include <linux/slab.h>
+ #include <linux/timer.h>
++#include <linux/init.h>
+ #include <asm/io.h>
+ #include <asm/system.h>
+
+@@ -24,6 +25,7 @@
+ #include <pcmcia/ds.h>
+
+ #include <linux/mtd/map.h>
++#include <linux/mtd/mtd.h>
+
+ #ifdef CONFIG_MTD_DEBUG
+ static int debug = CONFIG_MTD_DEBUG_VERBOSE;
+@@ -47,7 +49,7 @@
+
+
+ #define DRIVER_DESC "PCMCIA Flash memory card driver"
+-#define DRIVER_VERSION "$Revision$"
++#define DRIVER_VERSION "$Revision$"
+
+ /* Size of the PCMCIA address space: 26 bits = 64 MB */
+ #define MAX_PCMCIA_ADDR 0x4000000
+@@ -96,7 +98,7 @@
+ MODULE_PARM(mem_speed, "i");
+ MODULE_PARM_DESC(mem_speed, "Set memory access speed in ns");
+ MODULE_PARM(force_size, "i");
+-MODULE_PARM_DESC(force_size, "Force size of card in MB (1-64)");
++MODULE_PARM_DESC(force_size, "Force size of card in MiB (1-64)");
+ MODULE_PARM(setvpp, "i");
+ MODULE_PARM_DESC(setvpp, "Set Vpp (0=Never, 1=On writes, 2=Always on, default=0)");
+ MODULE_PARM(vpp, "i");
+@@ -106,11 +108,13 @@
+
+
+
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,69)
+ static inline void cs_error(client_handle_t handle, int func, int ret)
+ {
+ error_info_t err = { func, ret };
+ CardServices(ReportError, handle, &err);
+ }
++#endif
+
+
+ /* read/write{8,16} copy_{from,to} routines with window remapping to access whole card */
+@@ -529,6 +533,7 @@
+
+ card_settings(dev, link, &new_name);
+
++ dev->pcmcia_map.phys = NO_XIP;
+ dev->pcmcia_map.read8 = pcmcia_read8_remap;
+ dev->pcmcia_map.read16 = pcmcia_read16_remap;
+ dev->pcmcia_map.copy_from = pcmcia_copy_from_remap;
+@@ -539,7 +544,7 @@
+ dev->pcmcia_map.set_vpp = pcmciamtd_set_vpp;
+
+ /* Request a memory window for PCMCIA. Some architeures can map windows upto the maximum
+- that PCMCIA can support (64Mb) - this is ideal and we aim for a window the size of the
++ that PCMCIA can support (64MiB) - this is ideal and we aim for a window the size of the
+ whole card - otherwise we try smaller windows until we succeed */
+
+ req.Attributes = WIN_MEMORY_TYPE_CM | WIN_ENABLE;
+@@ -552,7 +557,7 @@
+
+ do {
+ int ret;
+- DEBUG(2, "requesting window with size = %dKB memspeed = %d",
++ DEBUG(2, "requesting window with size = %dKiB memspeed = %d",
+ req.Size >> 10, req.AccessSpeed);
+ link->win = (window_handle_t)link->handle;
+ ret = CardServices(RequestWindow, &link->win, &req);
+@@ -560,7 +565,7 @@
+ if(ret) {
+ req.Size >>= 1;
+ } else {
+- DEBUG(2, "Got window of size %dKB", req.Size >> 10);
++ DEBUG(2, "Got window of size %dKiB", req.Size >> 10);
+ dev->win_size = req.Size;
+ break;
+ }
+@@ -573,7 +578,7 @@
+ pcmciamtd_release((u_long)link);
+ return;
+ }
+- DEBUG(1, "Allocated a window of %dKB", dev->win_size >> 10);
++ DEBUG(1, "Allocated a window of %dKiB", dev->win_size >> 10);
+
+ /* Get write protect status */
+ CS_CHECK(GetStatus, link->handle, &status);
+@@ -642,21 +647,21 @@
+ }
+
+ dev->mtd_info = mtd;
+- mtd->module = THIS_MODULE;
++ mtd->owner = THIS_MODULE;
+
+ if(new_name) {
+ int size = 0;
+ char unit = ' ';
+ /* Since we are using a default name, make it better by adding in the
+ size */
+- if(mtd->size < 1048576) { /* <1MB in size, show size in K */
++ if(mtd->size < 1048576) { /* <1MiB in size, show size in KiB */
+ size = mtd->size >> 10;
+ unit = 'K';
+ } else {
+ size = mtd->size >> 20;
+ unit = 'M';
+ }
+- snprintf(dev->mtd_name, sizeof(dev->mtd_name), "%d%cB %s", size, unit, "PCMCIA Memory card");
++ snprintf(dev->mtd_name, sizeof(dev->mtd_name), "%d%ciB %s", size, unit, "PCMCIA Memory card");
+ }
+
+ /* If the memory found is fits completely into the mapped PCMCIA window,
+@@ -828,16 +833,20 @@
+ }
+
+
++#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,68)
++static struct pcmcia_driver pcmciamtd_driver = {
++ .drv = {
++ .name = "pcmciamtd"
++ },
++ .attach = pcmciamtd_attach,
++ .detach = pcmciamtd_detach,
++ .owner = THIS_MODULE
++};
++#endif
++
++
+ static int __init init_pcmciamtd(void)
+ {
+- servinfo_t serv;
+-
+- info(DRIVER_DESC " " DRIVER_VERSION);
+- CardServices(GetCardServicesInfo, &serv);
+- if (serv.Revision != CS_RELEASE_CODE) {
+- err("Card Services release does not match!");
+- return -1;
+- }
+
+ if(buswidth && buswidth != 1 && buswidth != 2) {
+ info("bad buswidth (%d), using default", buswidth);
+@@ -851,15 +860,24 @@
+ info("bad mem_type (%d), using default", mem_type);
+ mem_type = 0;
+ }
++
++#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,68)
++ return pcmcia_register_driver(&pcmciamtd_driver);
++#else
+ register_pccard_driver(&dev_info, &pcmciamtd_attach, &pcmciamtd_detach);
+ return 0;
++#endif
+ }
+
+
+ static void __exit exit_pcmciamtd(void)
+ {
+ DEBUG(1, DRIVER_DESC " unloading");
++#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,68)
++ pcmcia_unregister_driver(&pcmciamtd_driver);
++#else
+ unregister_pccard_driver(&dev_info);
++#endif
+
+ while(dev_list) {
+ dev_link_t *link = dev_list;
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/maps/physmap.c linux/drivers/mtd/maps/physmap.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/maps/physmap.c 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/maps/physmap.c 2004-11-17 18:17:59.124301000 +0100
+@@ -1,179 +1,114 @@
+ /*
+- * $Id$
++ * $Id$
+ *
+ * Normal mappings of chips in physical memory
++ *
++ * Copyright (C) 2003 MontaVista Software Inc.
++ * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net
++ *
++ * 031022 - [jsun] add run-time configure and partition setup
+ */
+
+ #include <linux/module.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/slab.h>
+ #include <asm/io.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+ #include <linux/config.h>
+-
+-#ifdef CONFIG_MTD_PARTITIONS
+ #include <linux/mtd/partitions.h>
+-#endif
+-
+-#define WINDOW_ADDR CONFIG_MTD_PHYSMAP_START
+-#define WINDOW_SIZE CONFIG_MTD_PHYSMAP_LEN
+-#define BUSWIDTH CONFIG_MTD_PHYSMAP_BUSWIDTH
+
+ static struct mtd_info *mymtd;
+
+-__u8 physmap_read8(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readb(map->map_priv_1 + ofs);
+-}
+-
+-__u16 physmap_read16(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readw(map->map_priv_1 + ofs);
+-}
+-
+-__u32 physmap_read32(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readl(map->map_priv_1 + ofs);
+-}
+-
+-void physmap_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+-{
+- memcpy_fromio(to, map->map_priv_1 + from, len);
+-}
++struct map_info physmap_map = {.name = "phys_mapped_flash"};
+
+-void physmap_write8(struct map_info *map, __u8 d, unsigned long adr)
+-{
+- __raw_writeb(d, map->map_priv_1 + adr);
+- mb();
+-}
++#ifdef CONFIG_MTD_PARTITIONS
++static struct mtd_partition *mtd_parts;
++static int mtd_parts_nb;
+
+-void physmap_write16(struct map_info *map, __u16 d, unsigned long adr)
+-{
+- __raw_writew(d, map->map_priv_1 + adr);
+- mb();
+-}
++static int num_physmap_partitions;
++static struct mtd_partition *physmap_partitions;
+
+-void physmap_write32(struct map_info *map, __u32 d, unsigned long adr)
+-{
+- __raw_writel(d, map->map_priv_1 + adr);
+- mb();
+-}
++char *part_probes[] __initdata = {"cmdlinepart", "RedBoot", NULL};
+
+-void physmap_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
++void physmap_set_partitions(struct mtd_partition *parts, int num_parts)
+ {
+- memcpy_toio(map->map_priv_1 + to, from, len);
++ physmap_partitions=parts;
++ num_physmap_partitions=num_parts;
+ }
+-
+-struct map_info physmap_map = {
+- name: "Physically mapped flash",
+- size: WINDOW_SIZE,
+- buswidth: BUSWIDTH,
+- read8: physmap_read8,
+- read16: physmap_read16,
+- read32: physmap_read32,
+- copy_from: physmap_copy_from,
+- write8: physmap_write8,
+- write16: physmap_write16,
+- write32: physmap_write32,
+- copy_to: physmap_copy_to
+-};
+-
+-#ifdef CONFIG_MTD_PARTITIONS
+-#ifdef CONFIG_MTD_CMDLINE_PARTS
+-static struct mtd_partition *mtd_parts = 0;
+-static int mtd_parts_nb = 0;
+-#else
+-static struct mtd_partition physmap_partitions[] = {
+-/* Put your own partition definitions here */
+-#if 0
+- {
+- name: "bootROM",
+- size: 0x80000,
+- offset: 0,
+- mask_flags: MTD_WRITEABLE, /* force read-only */
+- }, {
+- name: "zImage",
+- size: 0x100000,
+- offset: MTDPART_OFS_APPEND,
+- mask_flags: MTD_WRITEABLE, /* force read-only */
+- }, {
+- name: "ramdisk.gz",
+- size: 0x300000,
+- offset: MTDPART_OFS_APPEND,
+- mask_flags: MTD_WRITEABLE, /* force read-only */
+- }, {
+- name: "User FS",
+- size: MTDPART_SIZ_FULL,
+- offset: MTDPART_OFS_APPEND,
+- }
+-#endif
+-};
+-
+-#define NUM_PARTITIONS (sizeof(physmap_partitions)/sizeof(struct mtd_partition))
+-
+-#endif
+-#endif
++#endif /* CONFIG_MTD_PARTITIONS */
+
+ int __init init_physmap(void)
+ {
+ static const char *rom_probe_types[] = { "cfi_probe", "jedec_probe", "map_rom", 0 };
+ const char **type;
+
+- printk(KERN_NOTICE "physmap flash device: %x at %x\n", WINDOW_SIZE, WINDOW_ADDR);
+- physmap_map.map_priv_1 = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE);
++ printk(KERN_NOTICE "physmap flash device: %lx at %lx\n", physmap_map.size, physmap_map.phys);
++ physmap_map.virt = (unsigned long)ioremap(physmap_map.phys, physmap_map.size);
+
+- if (!physmap_map.map_priv_1) {
++ if (!physmap_map.virt) {
+ printk("Failed to ioremap\n");
+ return -EIO;
+ }
+
++ simple_map_init(&physmap_map);
++
+ mymtd = 0;
+ type = rom_probe_types;
+ for(; !mymtd && *type; type++) {
+ mymtd = do_map_probe(*type, &physmap_map);
+ }
+ if (mymtd) {
+- mymtd->module = THIS_MODULE;
++ mymtd->owner = THIS_MODULE;
+
+- add_mtd_device(mymtd);
+ #ifdef CONFIG_MTD_PARTITIONS
+-#ifdef CONFIG_MTD_CMDLINE_PARTS
+- mtd_parts_nb = parse_cmdline_partitions(mymtd, &mtd_parts,
+- "phys");
++ mtd_parts_nb = parse_mtd_partitions(mymtd, part_probes,
++ &mtd_parts, 0);
++
+ if (mtd_parts_nb > 0)
+ {
+- printk(KERN_NOTICE
+- "Using command line partition definition\n");
+ add_mtd_partitions (mymtd, mtd_parts, mtd_parts_nb);
++ return 0;
+ }
+-#else
+- if (NUM_PARTITIONS != 0)
++
++ if (num_physmap_partitions != 0)
+ {
+ printk(KERN_NOTICE
+ "Using physmap partition definition\n");
+- add_mtd_partitions (mymtd, physmap_partitions, NUM_PARTITIONS);
++ add_mtd_partitions (mymtd, physmap_partitions, num_physmap_partitions);
++ return 0;
+ }
+
+ #endif
+-#endif
++ add_mtd_device(mymtd);
++
+ return 0;
+ }
+
+- iounmap((void *)physmap_map.map_priv_1);
++ iounmap((void *)physmap_map.virt);
+ return -ENXIO;
+ }
+
+ static void __exit cleanup_physmap(void)
+ {
+- if (mymtd) {
++#ifdef CONFIG_MTD_PARTITIONS
++ if (mtd_parts_nb) {
++ del_mtd_partitions(mymtd);
++ kfree(mtd_parts);
++ } else if (num_physmap_partitions) {
++ del_mtd_partitions(mymtd);
++ } else {
+ del_mtd_device(mymtd);
+- map_destroy(mymtd);
+- }
+- if (physmap_map.map_priv_1) {
+- iounmap((void *)physmap_map.map_priv_1);
+- physmap_map.map_priv_1 = 0;
+ }
++#else
++ del_mtd_device(mymtd);
++#endif
++ map_destroy(mymtd);
++
++ iounmap((void *)physmap_map.virt);
++ physmap_map.virt = 0;
+ }
+
+ module_init(init_physmap);
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/maps/pnc2000.c linux/drivers/mtd/maps/pnc2000.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/maps/pnc2000.c 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/maps/pnc2000.c 2004-11-17 18:17:59.125300848 +0100
+@@ -5,12 +5,13 @@
+ *
+ * This code is GPL
+ *
+- * $Id$
++ * $Id$
+ */
+
+ #include <linux/module.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
+
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+@@ -24,58 +25,13 @@
+ * MAP DRIVER STUFF
+ */
+
+-__u8 pnc_read8(struct map_info *map, unsigned long ofs)
+-{
+- return *(__u8 *)(WINDOW_ADDR + ofs);
+-}
+-
+-__u16 pnc_read16(struct map_info *map, unsigned long ofs)
+-{
+- return *(__u16 *)(WINDOW_ADDR + ofs);
+-}
+-
+-__u32 pnc_read32(struct map_info *map, unsigned long ofs)
+-{
+- return *(volatile unsigned int *)(WINDOW_ADDR + ofs);
+-}
+-
+-void pnc_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+-{
+- memcpy(to, (void *)(WINDOW_ADDR + from), len);
+-}
+-
+-void pnc_write8(struct map_info *map, __u8 d, unsigned long adr)
+-{
+- *(__u8 *)(WINDOW_ADDR + adr) = d;
+-}
+-
+-void pnc_write16(struct map_info *map, __u16 d, unsigned long adr)
+-{
+- *(__u16 *)(WINDOW_ADDR + adr) = d;
+-}
+-
+-void pnc_write32(struct map_info *map, __u32 d, unsigned long adr)
+-{
+- *(__u32 *)(WINDOW_ADDR + adr) = d;
+-}
+-
+-void pnc_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+-{
+- memcpy((void *)(WINDOW_ADDR + to), from, len);
+-}
+
+ struct map_info pnc_map = {
+- name: "PNC-2000",
+- size: WINDOW_SIZE,
+- buswidth: 4,
+- read8: pnc_read8,
+- read16: pnc_read16,
+- read32: pnc_read32,
+- copy_from: pnc_copy_from,
+- write8: pnc_write8,
+- write16: pnc_write16,
+- write32: pnc_write32,
+- copy_to: pnc_copy_to
++ .name = "PNC-2000",
++ .size = WINDOW_SIZE,
++ .buswidth = 4,
++ .phys = 0xFFFFFFFF,
++ .virt = WINDOW_ADDR,
+ };
+
+
+@@ -84,19 +40,19 @@
+ */
+ static struct mtd_partition pnc_partitions[3] = {
+ {
+- name: "PNC-2000 boot firmware",
+- size: 0x20000,
+- offset: 0
++ .name = "PNC-2000 boot firmware",
++ .size = 0x20000,
++ .offset = 0
+ },
+ {
+- name: "PNC-2000 kernel",
+- size: 0x1a0000,
+- offset: 0x20000
++ .name = "PNC-2000 kernel",
++ .size = 0x1a0000,
++ .offset = 0x20000
+ },
+ {
+- name: "PNC-2000 filesystem",
+- size: 0x240000,
+- offset: 0x1c0000
++ .name = "PNC-2000 filesystem",
++ .size = 0x240000,
++ .offset = 0x1c0000
+ }
+ };
+
+@@ -110,9 +66,11 @@
+ {
+ printk(KERN_NOTICE "Photron PNC-2000 flash mapping: %x at %x\n", WINDOW_SIZE, WINDOW_ADDR);
+
++ simple_map_init(&pnc_map);
++
+ mymtd = do_map_probe("cfi_probe", &pnc_map);
+ if (mymtd) {
+- mymtd->module = THIS_MODULE;
++ mymtd->owner = THIS_MODULE;
+ return add_mtd_partitions(mymtd, pnc_partitions, 3);
+ }
+
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/maps/redwood.c linux/drivers/mtd/maps/redwood.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/maps/redwood.c 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/maps/redwood.c 2004-11-17 18:17:59.126300696 +0100
+@@ -1,38 +1,23 @@
+ /*
+- * $Id:
++ * $Id$
+ *
+- * redwood.c - mapper for IBM Redwood-4/5 board.
++ * drivers/mtd/maps/redwood.c
+ *
+- * Copyright 2001 MontaVista Softare Inc.
++ * FLASH map for the IBM Redwood 4/5/6 boards.
+ *
+- * This program is free software; you can redistribute it and/or modify it
+- * under the terms of the GNU General Public License as published by the
+- * Free Software Foundation; either version 2 of the License, or (at your
+- * option) any later version.
+- *
+- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+- * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+- * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+- *
+- * You should have received a copy of the GNU General Public License along
+- * with this program; if not, write to the Free Software Foundation, Inc.,
+- * 675 Mass Ave, Cambridge, MA 02139, USA.
+- *
+- * History: 12/17/2001 - Armin
+- * migrated to use do_map_probe
++ * Author: MontaVista Software, Inc. <source@mvista.com>
+ *
++ * 2001-2003 (c) MontaVista, Software, Inc. This file is licensed under
++ * the terms of the GNU General Public License version 2. This program
++ * is licensed "as is" without any warranty of any kind, whether express
++ * or implied.
+ */
+
++#include <linux/config.h>
+ #include <linux/module.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
+
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+@@ -40,96 +25,102 @@
+
+ #include <asm/io.h>
+
++#if !defined (CONFIG_REDWOOD_6)
++
+ #define WINDOW_ADDR 0xffc00000
+ #define WINDOW_SIZE 0x00400000
+
+-__u8 redwood_flash_read8(struct map_info *map, unsigned long ofs)
+-{
+- return *(__u8 *)(map->map_priv_1 + ofs);
+-}
+-
+-__u16 redwood_flash_read16(struct map_info *map, unsigned long ofs)
+-{
+- return *(__u16 *)(map->map_priv_1 + ofs);
+-}
+-
+-__u32 redwood_flash_read32(struct map_info *map, unsigned long ofs)
+-{
+- return *(volatile unsigned int *)(map->map_priv_1 + ofs);
+-}
+-
+-void redwood_flash_copy_from(struct map_info *map, void *to,
+- unsigned long from, ssize_t len)
+-{
+- memcpy(to, (void *)(map->map_priv_1 + from), len);
+-}
+-
+-void redwood_flash_write8(struct map_info *map, __u8 d, unsigned long adr)
+-{
+- *(__u8 *)(map->map_priv_1 + adr) = d;
+-}
+-
+-void redwood_flash_write16(struct map_info *map, __u16 d, unsigned long adr)
+-{
+- *(__u16 *)(map->map_priv_1 + adr) = d;
+-}
+-
+-void redwood_flash_write32(struct map_info *map, __u32 d, unsigned long adr)
+-{
+- *(__u32 *)(map->map_priv_1 + adr) = d;
+-}
+-
+-void redwood_flash_copy_to(struct map_info *map, unsigned long to,
+- const void *from, ssize_t len)
+-{
+- memcpy((void *)(map->map_priv_1 + to), from, len);
+-}
++#define RW_PART0_OF 0
++#define RW_PART0_SZ 0x10000
++#define RW_PART1_OF RW_PART0_SZ
++#define RW_PART1_SZ 0x200000 - 0x10000
++#define RW_PART2_OF 0x200000
++#define RW_PART2_SZ 0x10000
++#define RW_PART3_OF 0x210000
++#define RW_PART3_SZ 0x200000 - (0x10000 + 0x20000)
++#define RW_PART4_OF 0x3e0000
++#define RW_PART4_SZ 0x20000
+
+-struct map_info redwood_flash_map = {
+- name: "IBM Redwood",
+- size: WINDOW_SIZE,
+- buswidth: 2,
+- read8: redwood_flash_read8,
+- read16: redwood_flash_read16,
+- read32: redwood_flash_read32,
+- copy_from: redwood_flash_copy_from,
+- write8: redwood_flash_write8,
+- write16: redwood_flash_write16,
+- write32: redwood_flash_write32,
+- copy_to: redwood_flash_copy_to
++static struct mtd_partition redwood_flash_partitions[] = {
++ {
++ .name = "Redwood OpenBIOS Vital Product Data",
++ .offset = RW_PART0_OF,
++ .size = RW_PART0_SZ,
++ .mask_flags = MTD_WRITEABLE /* force read-only */
++ },
++ {
++ .name = "Redwood kernel",
++ .offset = RW_PART1_OF,
++ .size = RW_PART1_SZ
++ },
++ {
++ .name = "Redwood OpenBIOS non-volatile storage",
++ .offset = RW_PART2_OF,
++ .size = RW_PART2_SZ,
++ .mask_flags = MTD_WRITEABLE /* force read-only */
++ },
++ {
++ .name = "Redwood filesystem",
++ .offset = RW_PART3_OF,
++ .size = RW_PART3_SZ
++ },
++ {
++ .name = "Redwood OpenBIOS",
++ .offset = RW_PART4_OF,
++ .size = RW_PART4_SZ,
++ .mask_flags = MTD_WRITEABLE /* force read-only */
++ }
+ };
+
++#else /* CONFIG_REDWOOD_6 */
++/* FIXME: the window is bigger - armin */
++#define WINDOW_ADDR 0xff800000
++#define WINDOW_SIZE 0x00800000
++
++#define RW_PART0_OF 0
++#define RW_PART0_SZ 0x400000 /* 4 MiB data */
++#define RW_PART1_OF RW_PART0_OF + RW_PART0_SZ
++#define RW_PART1_SZ 0x10000 /* 64K VPD */
++#define RW_PART2_OF RW_PART1_OF + RW_PART1_SZ
++#define RW_PART2_SZ 0x400000 - (0x10000 + 0x20000)
++#define RW_PART3_OF RW_PART2_OF + RW_PART2_SZ
++#define RW_PART3_SZ 0x20000
+
+ static struct mtd_partition redwood_flash_partitions[] = {
+ {
+- name: "Redwood OpenBIOS Vital Product Data",
+- offset: 0,
+- size: 0x10000,
+- mask_flags: MTD_WRITEABLE /* force read-only */
++ .name = "Redwood filesystem",
++ .offset = RW_PART0_OF,
++ .size = RW_PART0_SZ
+ },
+ {
+- name: "Redwood kernel",
+- offset: 0x10000,
+- size: 0x200000 - 0x10000
++ .name = "Redwood OpenBIOS Vital Product Data",
++ .offset = RW_PART1_OF,
++ .size = RW_PART1_SZ,
++ .mask_flags = MTD_WRITEABLE /* force read-only */
+ },
+ {
+- name: "Redwood OpenBIOS non-volatile storage",
+- offset: 0x200000,
+- size: 0x10000,
+- mask_flags: MTD_WRITEABLE /* force read-only */
++ .name = "Redwood kernel",
++ .offset = RW_PART2_OF,
++ .size = RW_PART2_SZ
+ },
+ {
+- name: "Redwood filesystem",
+- offset: 0x210000,
+- size: 0x200000 - (0x10000 + 0x20000)
+- },
+- {
+- name: "Redwood OpenBIOS",
+- offset: 0x3e0000,
+- size: 0x20000,
+- mask_flags: MTD_WRITEABLE /* force read-only */
++ .name = "Redwood OpenBIOS",
++ .offset = RW_PART3_OF,
++ .size = RW_PART3_SZ,
++ .mask_flags = MTD_WRITEABLE /* force read-only */
+ }
+ };
++
++#endif /* CONFIG_REDWOOD_6 */
++
++struct map_info redwood_flash_map = {
++ .name = "IBM Redwood",
++ .size = WINDOW_SIZE,
++ .buswidth = 2,
++ .phys = WINDOW_ADDR,
++};
++
++
+ #define NUM_REDWOOD_FLASH_PARTITIONS \
+ (sizeof(redwood_flash_partitions)/sizeof(redwood_flash_partitions[0]))
+
+@@ -140,18 +131,19 @@
+ printk(KERN_NOTICE "redwood: flash mapping: %x at %x\n",
+ WINDOW_SIZE, WINDOW_ADDR);
+
+- redwood_flash_map.map_priv_1 =
++ redwood_flash_map.virt =
+ (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE);
+
+- if (!redwood_flash_map.map_priv_1) {
++ if (!redwood_flash_map.virt) {
+ printk("init_redwood_flash: failed to ioremap\n");
+ return -EIO;
+ }
++ simple_map_init(&redwood_flash_map);
+
+ redwood_mtd = do_map_probe("cfi_probe",&redwood_flash_map);
+
+ if (redwood_mtd) {
+- redwood_mtd->module = THIS_MODULE;
++ redwood_mtd->owner = THIS_MODULE;
+ return add_mtd_partitions(redwood_mtd,
+ redwood_flash_partitions,
+ NUM_REDWOOD_FLASH_PARTITIONS);
+@@ -164,10 +156,15 @@
+ {
+ if (redwood_mtd) {
+ del_mtd_partitions(redwood_mtd);
+- iounmap((void *)redwood_flash_map.map_priv_1);
++ /* moved iounmap after map_destroy - armin */
+ map_destroy(redwood_mtd);
++ iounmap((void *)redwood_flash_map.virt);
+ }
+ }
+
+ module_init(init_redwood_flash);
+ module_exit(cleanup_redwood_flash);
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("MontaVista Software <source@mvista.com>");
++MODULE_DESCRIPTION("MTD map driver for the IBM Redwood reference boards");
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/maps/rpxlite.c linux/drivers/mtd/maps/rpxlite.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/maps/rpxlite.c 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/maps/rpxlite.c 2004-11-17 18:17:59.127300544 +0100
+@@ -1,5 +1,5 @@
+ /*
+- * $Id$
++ * $Id$
+ *
+ * Handle mapping of the flash on the RPX Lite and CLLF boards
+ */
+@@ -7,6 +7,7 @@
+ #include <linux/module.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
+ #include <asm/io.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+@@ -17,80 +18,31 @@
+
+ static struct mtd_info *mymtd;
+
+-__u8 rpxlite_read8(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readb(map->map_priv_1 + ofs);
+-}
+-
+-__u16 rpxlite_read16(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readw(map->map_priv_1 + ofs);
+-}
+-
+-__u32 rpxlite_read32(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readl(map->map_priv_1 + ofs);
+-}
+-
+-void rpxlite_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+-{
+- memcpy_fromio(to, (void *)(map->map_priv_1 + from), len);
+-}
+-
+-void rpxlite_write8(struct map_info *map, __u8 d, unsigned long adr)
+-{
+- __raw_writeb(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-void rpxlite_write16(struct map_info *map, __u16 d, unsigned long adr)
+-{
+- __raw_writew(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-void rpxlite_write32(struct map_info *map, __u32 d, unsigned long adr)
+-{
+- __raw_writel(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-void rpxlite_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+-{
+- memcpy_toio((void *)(map->map_priv_1 + to), from, len);
+-}
+-
+-struct map_info rpxlite_map = {
+- name: "RPX",
+- size: WINDOW_SIZE,
+- buswidth: 4,
+- read8: rpxlite_read8,
+- read16: rpxlite_read16,
+- read32: rpxlite_read32,
+- copy_from: rpxlite_copy_from,
+- write8: rpxlite_write8,
+- write16: rpxlite_write16,
+- write32: rpxlite_write32,
+- copy_to: rpxlite_copy_to
++static struct map_info rpxlite_map = {
++ .name = "RPX",
++ .size = WINDOW_SIZE,
++ .buswidth = 4,
++ .phys = WINDOW_ADDR,
+ };
+
+ int __init init_rpxlite(void)
+ {
+ printk(KERN_NOTICE "RPX Lite or CLLF flash device: %x at %x\n", WINDOW_SIZE*4, WINDOW_ADDR);
+- rpxlite_map.map_priv_1 = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE * 4);
++ rpxlite_map.virt = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE * 4);
+
+- if (!rpxlite_map.map_priv_1) {
++ if (!rpxlite_map.virt) {
+ printk("Failed to ioremap\n");
+ return -EIO;
+ }
++ simple_map_init(&rpxlite_map);
+ mymtd = do_map_probe("cfi_probe", &rpxlite_map);
+ if (mymtd) {
+- mymtd->module = THIS_MODULE;
++ mymtd->owner = THIS_MODULE;
+ add_mtd_device(mymtd);
+ return 0;
+ }
+
+- iounmap((void *)rpxlite_map.map_priv_1);
++ iounmap((void *)rpxlite_map.virt);
+ return -ENXIO;
+ }
+
+@@ -100,9 +52,9 @@
+ del_mtd_device(mymtd);
+ map_destroy(mymtd);
+ }
+- if (rpxlite_map.map_priv_1) {
+- iounmap((void *)rpxlite_map.map_priv_1);
+- rpxlite_map.map_priv_1 = 0;
++ if (rpxlite_map.virt) {
++ iounmap((void *)rpxlite_map.virt);
++ rpxlite_map.virt = 0;
+ }
+ }
+
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/maps/sa1100-flash.c linux/drivers/mtd/maps/sa1100-flash.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/maps/sa1100-flash.c 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/maps/sa1100-flash.c 2004-11-17 18:17:59.129300240 +0100
+@@ -3,7 +3,7 @@
+ *
+ * (C) 2000 Nicolas Pitre <nico@cam.org>
+ *
+- * $Id$
++ * $Id$
+ */
+
+ #include <linux/config.h>
+@@ -11,278 +11,212 @@
+ #include <linux/types.h>
+ #include <linux/ioport.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/errno.h>
++#include <linux/slab.h>
+
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+ #include <linux/mtd/partitions.h>
++#include <linux/mtd/concat.h>
+
+ #include <asm/hardware.h>
++#include <asm/mach-types.h>
+ #include <asm/io.h>
++#include <asm/sizes.h>
+
++#include <asm/arch/h3600.h>
+
+ #ifndef CONFIG_ARCH_SA1100
+ #error This is for SA1100 architecture only
+ #endif
+
++/*
++ * This isnt complete yet, so...
++ */
++#define CONFIG_MTD_SA1100_STATICMAP 1
+
+-#define WINDOW_ADDR 0xe8000000
+-
+-static __u8 sa1100_read8(struct map_info *map, unsigned long ofs)
+-{
+- return readb(map->map_priv_1 + ofs);
+-}
+-
+-static __u16 sa1100_read16(struct map_info *map, unsigned long ofs)
+-{
+- return readw(map->map_priv_1 + ofs);
+-}
+-
+-static __u32 sa1100_read32(struct map_info *map, unsigned long ofs)
+-{
+- return readl(map->map_priv_1 + ofs);
+-}
+-
+-static void sa1100_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+-{
+- memcpy(to, (void *)(map->map_priv_1 + from), len);
+-}
+-
+-static void sa1100_write8(struct map_info *map, __u8 d, unsigned long adr)
+-{
+- writeb(d, map->map_priv_1 + adr);
+-}
+-
+-static void sa1100_write16(struct map_info *map, __u16 d, unsigned long adr)
+-{
+- writew(d, map->map_priv_1 + adr);
+-}
+-
+-static void sa1100_write32(struct map_info *map, __u32 d, unsigned long adr)
+-{
+- writel(d, map->map_priv_1 + adr);
+-}
+-
+-static void sa1100_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+-{
+- memcpy((void *)(map->map_priv_1 + to), from, len);
+-}
+-
+-static struct map_info sa1100_map = {
+- name: "SA1100 flash",
+- read8: sa1100_read8,
+- read16: sa1100_read16,
+- read32: sa1100_read32,
+- copy_from: sa1100_copy_from,
+- write8: sa1100_write8,
+- write16: sa1100_write16,
+- write32: sa1100_write32,
+- copy_to: sa1100_copy_to,
+-
+- map_priv_1: WINDOW_ADDR,
+- map_priv_2: -1,
+-};
+-
+-
++#ifdef CONFIG_MTD_SA1100_STATICMAP
+ /*
+ * Here are partition information for all known SA1100-based devices.
+ * See include/linux/mtd/partitions.h for definition of the mtd_partition
+ * structure.
+ *
+- * The *_max_flash_size is the maximum possible mapped flash size which
+- * is not necessarily the actual flash size. It must be no more than
+- * the value specified in the "struct map_desc *_io_desc" mapping
+- * definition for the corresponding machine.
++ * Please note:
++ * 1. We no longer support static flash mappings via the machine io_desc
++ * structure.
++ * 2. The flash size given should be the largest flash size that can
++ * be accommodated.
++ *
++ * The MTD layer will detect flash chip aliasing and reduce the size of
++ * the map accordingly.
+ *
+ * Please keep these in alphabetical order, and formatted as per existing
+ * entries. Thanks.
+ */
+
+ #ifdef CONFIG_SA1100_ADSBITSY
+-#define ADSBITSY_FLASH_SIZE 0x02000000
+ static struct mtd_partition adsbitsy_partitions[] = {
+ {
+- name: "bootROM",
+- size: 0x80000,
+- offset: 0,
+- mask_flags: MTD_WRITEABLE, /* force read-only */
+- }, {
+- name: "zImage",
+- size: 0x100000,
+- offset: MTDPART_OFS_APPEND,
+- mask_flags: MTD_WRITEABLE, /* force read-only */
+- }, {
+- name: "ramdisk.gz",
+- size: 0x300000,
+- offset: MTDPART_OFS_APPEND,
+- mask_flags: MTD_WRITEABLE, /* force read-only */
+- }, {
+- name: "User FS",
+- size: MTDPART_SIZ_FULL,
+- offset: MTDPART_OFS_APPEND,
++ .name = "bootROM",
++ .size = 0x80000,
++ .offset = 0,
++ .mask_flags = MTD_WRITEABLE, /* force read-only */
++ }, {
++ .name = "zImage",
++ .size = 0x100000,
++ .offset = MTDPART_OFS_APPEND,
++ .mask_flags = MTD_WRITEABLE, /* force read-only */
++ }, {
++ .name = "ramdisk.gz",
++ .size = 0x300000,
++ .offset = MTDPART_OFS_APPEND,
++ .mask_flags = MTD_WRITEABLE, /* force read-only */
++ }, {
++ .name = "User FS",
++ .size = MTDPART_SIZ_FULL,
++ .offset = MTDPART_OFS_APPEND,
+ }
+ };
+ #endif
+
+ #ifdef CONFIG_SA1100_ASSABET
+ /* Phase 4 Assabet has two 28F160B3 flash parts in bank 0: */
+-#define ASSABET4_FLASH_SIZE 0x00400000
+ static struct mtd_partition assabet4_partitions[] = {
+ {
+- name: "bootloader",
+- size: 0x00020000,
+- offset: 0,
+- mask_flags: MTD_WRITEABLE,
+- }, {
+- name: "bootloader params",
+- size: 0x00020000,
+- offset: MTDPART_OFS_APPEND,
+- mask_flags: MTD_WRITEABLE,
+- }, {
+- name: "jffs",
+- size: MTDPART_SIZ_FULL,
+- offset: MTDPART_OFS_APPEND,
++ .name = "bootloader",
++ .size = 0x00020000,
++ .offset = 0,
++ .mask_flags = MTD_WRITEABLE,
++ }, {
++ .name = "bootloader params",
++ .size = 0x00020000,
++ .offset = MTDPART_OFS_APPEND,
++ .mask_flags = MTD_WRITEABLE,
++ }, {
++ .name = "jffs",
++ .size = MTDPART_SIZ_FULL,
++ .offset = MTDPART_OFS_APPEND,
+ }
+ };
+
+ /* Phase 5 Assabet has two 28F128J3A flash parts in bank 0: */
+-#define ASSABET5_FLASH_SIZE 0x02000000
+ static struct mtd_partition assabet5_partitions[] = {
+ {
+- name: "bootloader",
+- size: 0x00040000,
+- offset: 0,
+- mask_flags: MTD_WRITEABLE,
+- }, {
+- name: "bootloader params",
+- size: 0x00040000,
+- offset: MTDPART_OFS_APPEND,
+- mask_flags: MTD_WRITEABLE,
+- }, {
+- name: "jffs",
+- size: MTDPART_SIZ_FULL,
+- offset: MTDPART_OFS_APPEND,
++ .name = "bootloader",
++ .size = 0x00040000,
++ .offset = 0,
++ .mask_flags = MTD_WRITEABLE,
++ }, {
++ .name = "bootloader params",
++ .size = 0x00040000,
++ .offset = MTDPART_OFS_APPEND,
++ .mask_flags = MTD_WRITEABLE,
++ }, {
++ .name = "jffs",
++ .size = MTDPART_SIZ_FULL,
++ .offset = MTDPART_OFS_APPEND,
+ }
+ };
+
+-#define ASSABET_FLASH_SIZE ASSABET5_FLASH_SIZE
+ #define assabet_partitions assabet5_partitions
+ #endif
+
+ #ifdef CONFIG_SA1100_BADGE4
+-
+ /*
+- * 1 x Intel 28F320C3BA100 Advanced+ Boot Block Flash (32 Mi bit)
++ * 1 x Intel 28F320C3 Advanced+ Boot Block Flash (32 Mi bit)
+ * Eight 4 KiW Parameter Bottom Blocks (64 KiB)
+ * Sixty-three 32 KiW Main Blocks (4032 Ki b)
++ *
++ * <or>
++ *
++ * 1 x Intel 28F640C3 Advanced+ Boot Block Flash (64 Mi bit)
++ * Eight 4 KiW Parameter Bottom Blocks (64 KiB)
++ * One-hundred-twenty-seven 32 KiW Main Blocks (8128 Ki b)
+ */
+-#define BADGE4_FLASH_SIZE 0x00400000
+ static struct mtd_partition badge4_partitions[] = {
+ {
+- name: "BLOB boot loader",
+- offset: 0,
+- size: 0x0000A000
+- }, {
+- name: "params",
+- offset: MTDPART_OFS_APPEND,
+- size: 0x00006000
+- }, {
+- name: "kernel",
+- offset: MTDPART_OFS_APPEND,
+- size: 0x00100000
+- }, {
+- name: "root",
+- offset: MTDPART_OFS_APPEND,
+- size: MTDPART_SIZ_FULL
++ .name = "BLOB boot loader",
++ .offset = 0,
++ .size = 0x0000A000
++ }, {
++ .name = "params",
++ .offset = MTDPART_OFS_APPEND,
++ .size = 0x00006000
++ }, {
++ .name = "root",
++ .offset = MTDPART_OFS_APPEND,
++ .size = MTDPART_SIZ_FULL
+ }
+ };
+-
+ #endif
+
+
+ #ifdef CONFIG_SA1100_CERF
+ #ifdef CONFIG_SA1100_CERF_FLASH_32MB
+-#define CERF_FLASH_SIZE 0x02000000
+-static struct mtd_partition cerf_partitions[] = {
+- {
+- name: "firmware",
+- size: 0x00040000,
+- offset: 0,
+- }, {
+- name: "params",
+- size: 0x00040000,
+- offset: 0x00040000,
+- }, {
+- name: "kernel",
+- size: 0x00100000,
+- offset: 0x00080000,
+- }, {
+- name: "rootdisk",
+- size: 0x01E80000,
+- offset: 0x00180000,
+- }
+-};
++# define CERF_FLASH_SIZE 0x02000000
+ #elif defined CONFIG_SA1100_CERF_FLASH_16MB
+-#define CERF_FLASH_SIZE 0x01000000
++# define CERF_FLASH_SIZE 0x01000000
++#elif defined CONFIG_SA1100_CERF_FLASH_8MB
++# define CERF_FLASH_SIZE 0x00800000
++#else
++# error "Undefined flash size for CERF in sa1100-flash.c"
++#endif
++
+ static struct mtd_partition cerf_partitions[] = {
+ {
+- name: "firmware",
+- size: 0x00020000,
+- offset: 0,
+- }, {
+- name: "params",
+- size: 0x00020000,
+- offset: 0x00020000,
+- }, {
+- name: "kernel",
+- size: 0x00100000,
+- offset: 0x00040000,
+- }, {
+- name: "rootdisk",
+- size: 0x00EC0000,
+- offset: 0x00140000,
++ .name = "Bootloader",
++ .size = 0x00020000,
++ .offset = 0x00000000,
++ }, {
++ .name = "Params",
++ .size = 0x00040000,
++ .offset = 0x00020000,
++ }, {
++ .name = "Kernel",
++ .size = 0x00100000,
++ .offset = 0x00060000,
++ }, {
++ .name = "Filesystem",
++ .size = CERF_FLASH_SIZE-0x00160000,
++ .offset = 0x00160000,
+ }
+ };
+-#elif defined CONFIG_SA1100_CERF_FLASH_8MB
+-# error "Unwritten type definition"
+-#else
+-# error "Undefined memory orientation for CERF in sa1100-flash.c"
+-#endif
+ #endif
+
+ #ifdef CONFIG_SA1100_CONSUS
+-#define CONSUS_FLASH_SIZE 0x02000000
+ static struct mtd_partition consus_partitions[] = {
+ {
+- name: "Consus boot firmware",
+- offset: 0,
+- size: 0x00040000,
+- mask_flags: MTD_WRITABLE, /* force read-only */
+- }, {
+- name: "Consus kernel",
+- offset: 0x00040000,
+- size: 0x00100000,
+- mask_flags: 0,
++ .name = "Consus boot firmware",
++ .offset = 0,
++ .size = 0x00040000,
++ .mask_flags = MTD_WRITABLE, /* force read-only */
++ }, {
++ .name = "Consus kernel",
++ .offset = 0x00040000,
++ .size = 0x00100000,
++ .mask_flags = 0,
+ }, {
+- name: "Consus disk",
+- offset: 0x00140000,
++ .name = "Consus disk",
++ .offset = 0x00140000,
+ /* The rest (up to 16M) for jffs. We could put 0 and
+ make it find the size automatically, but right now
+ i have 32 megs. jffs will use all 32 megs if given
+ the chance, and this leads to horrible problems
+ when you try to re-flash the image because blob
+ won't erase the whole partition. */
+- size: 0x01000000 - 0x00140000,
+- mask_flags: 0,
++ .size = 0x01000000 - 0x00140000,
++ .mask_flags = 0,
+ }, {
+ /* this disk is a secondary disk, which can be used as
+ needed, for simplicity, make it the size of the other
+ consus partition, although realistically it could be
+ the remainder of the disk (depending on the file
+ system used) */
+- name: "Consus disk2",
+- offset: 0x01000000,
+- size: 0x01000000 - 0x00140000,
+- mask_flags: 0,
++ .name = "Consus disk2",
++ .offset = 0x01000000,
++ .size = 0x01000000 - 0x00140000,
++ .mask_flags = 0,
+ }
+ };
+ #endif
+@@ -292,96 +226,95 @@
+ #define FLEXANET_FLASH_SIZE 0x02000000
+ static struct mtd_partition flexanet_partitions[] = {
+ {
+- name: "bootloader",
+- size: 0x00040000,
+- offset: 0,
+- mask_flags: MTD_WRITEABLE,
+- }, {
+- name: "bootloader params",
+- size: 0x00040000,
+- offset: MTDPART_OFS_APPEND,
+- mask_flags: MTD_WRITEABLE,
+- }, {
+- name: "kernel",
+- size: 0x000C0000,
+- offset: MTDPART_OFS_APPEND,
+- mask_flags: MTD_WRITEABLE,
+- }, {
+- name: "altkernel",
+- size: 0x000C0000,
+- offset: MTDPART_OFS_APPEND,
+- mask_flags: MTD_WRITEABLE,
+- }, {
+- name: "root",
+- size: 0x00400000,
+- offset: MTDPART_OFS_APPEND,
+- mask_flags: MTD_WRITEABLE,
+- }, {
+- name: "free1",
+- size: 0x00300000,
+- offset: MTDPART_OFS_APPEND,
+- mask_flags: MTD_WRITEABLE,
+- }, {
+- name: "free2",
+- size: 0x00300000,
+- offset: MTDPART_OFS_APPEND,
+- mask_flags: MTD_WRITEABLE,
+- }, {
+- name: "free3",
+- size: MTDPART_SIZ_FULL,
+- offset: MTDPART_OFS_APPEND,
+- mask_flags: MTD_WRITEABLE,
++ .name = "bootloader",
++ .size = 0x00040000,
++ .offset = 0,
++ .mask_flags = MTD_WRITEABLE,
++ }, {
++ .name = "bootloader params",
++ .size = 0x00040000,
++ .offset = MTDPART_OFS_APPEND,
++ .mask_flags = MTD_WRITEABLE,
++ }, {
++ .name = "kernel",
++ .size = 0x000C0000,
++ .offset = MTDPART_OFS_APPEND,
++ .mask_flags = MTD_WRITEABLE,
++ }, {
++ .name = "altkernel",
++ .size = 0x000C0000,
++ .offset = MTDPART_OFS_APPEND,
++ .mask_flags = MTD_WRITEABLE,
++ }, {
++ .name = "root",
++ .size = 0x00400000,
++ .offset = MTDPART_OFS_APPEND,
++ .mask_flags = MTD_WRITEABLE,
++ }, {
++ .name = "free1",
++ .size = 0x00300000,
++ .offset = MTDPART_OFS_APPEND,
++ .mask_flags = MTD_WRITEABLE,
++ }, {
++ .name = "free2",
++ .size = 0x00300000,
++ .offset = MTDPART_OFS_APPEND,
++ .mask_flags = MTD_WRITEABLE,
++ }, {
++ .name = "free3",
++ .size = MTDPART_SIZ_FULL,
++ .offset = MTDPART_OFS_APPEND,
++ .mask_flags = MTD_WRITEABLE,
+ }
+ };
+ #endif
+
+ #ifdef CONFIG_SA1100_FREEBIRD
+-#define FREEBIRD_FLASH_SIZE 0x02000000
+ static struct mtd_partition freebird_partitions[] = {
+-#if CONFIG_SA1100_FREEBIRD_NEW
++#ifdef CONFIG_SA1100_FREEBIRD_NEW
+ {
+- name: "firmware",
+- size: 0x00040000,
+- offset: 0,
+- mask_flags: MTD_WRITEABLE, /* force read-only */
+- }, {
+- name: "kernel",
+- size: 0x00080000,
+- offset: 0x00040000,
+- }, {
+- name: "params",
+- size: 0x00040000,
+- offset: 0x000C0000,
+- }, {
+- name: "initrd",
+- size: 0x00100000,
+- offset: 0x00100000,
+- }, {
+- name: "root cramfs",
+- size: 0x00300000,
+- offset: 0x00200000,
+- }, {
+- name: "usr cramfs",
+- size: 0x00C00000,
+- offset: 0x00500000,
+- }, {
+- name: "local",
+- size: MTDPART_SIZ_FULL,
+- offset: 0x01100000,
++ .name = "firmware",
++ .size = 0x00040000,
++ .offset = 0,
++ .mask_flags = MTD_WRITEABLE, /* force read-only */
++ }, {
++ .name = "kernel",
++ .size = 0x00080000,
++ .offset = 0x00040000,
++ }, {
++ .name = "params",
++ .size = 0x00040000,
++ .offset = 0x000C0000,
++ }, {
++ .name = "initrd",
++ .size = 0x00100000,
++ .offset = 0x00100000,
++ }, {
++ .name = "root cramfs",
++ .size = 0x00300000,
++ .offset = 0x00200000,
++ }, {
++ .name = "usr cramfs",
++ .size = 0x00C00000,
++ .offset = 0x00500000,
++ }, {
++ .name = "local",
++ .size = MTDPART_SIZ_FULL,
++ .offset = 0x01100000,
+ }
+ #else
+ {
+- size: 0x00040000,
+- offset: 0,
++ .size = 0x00040000,
++ .offset = 0,
+ }, {
+- size: 0x000c0000,
+- offset: MTDPART_OFS_APPEND,
++ .size = 0x000c0000,
++ .offset = MTDPART_OFS_APPEND,
+ }, {
+- size: 0x00400000,
+- offset: MTDPART_OFS_APPEND,
++ .size = 0x00400000,
++ .offset = MTDPART_OFS_APPEND,
+ }, {
+- size: MTDPART_SIZ_FULL,
+- offset: MTDPART_OFS_APPEND,
++ .size = MTDPART_SIZ_FULL,
++ .offset = MTDPART_OFS_APPEND,
+ }
+ #endif
+ };
+@@ -389,178 +322,215 @@
+
+ #ifdef CONFIG_SA1100_FRODO
+ /* Frodo has 2 x 16M 28F128J3A flash chips in bank 0: */
+-#define FRODO_FLASH_SIZE 0x02000000
+ static struct mtd_partition frodo_partitions[] =
+ {
+ {
+- name: "Boot Loader",
+- size: 0x00040000,
+- offset: 0x00000000
+- }, {
+- name: "Parameter Block",
+- size: 0x00040000,
+- offset: MTDPART_OFS_APPEND
+- }, {
+- name: "Linux Kernel",
+- size: 0x00100000,
+- offset: MTDPART_OFS_APPEND
+- }, {
+- name: "Ramdisk",
+- size: 0x00680000,
+- offset: MTDPART_OFS_APPEND
+- }, {
+- name: "Flash File System",
+- size: MTDPART_SIZ_FULL,
+- offset: MTDPART_OFS_APPEND
++ .name = "bootloader",
++ .size = 0x00040000,
++ .offset = 0x00000000,
++ .mask_flags = MTD_WRITEABLE
++ }, {
++ .name = "bootloader params",
++ .size = 0x00040000,
++ .offset = MTDPART_OFS_APPEND,
++ .mask_flags = MTD_WRITEABLE
++ }, {
++ .name = "kernel",
++ .size = 0x00100000,
++ .offset = MTDPART_OFS_APPEND,
++ .mask_flags = MTD_WRITEABLE
++ }, {
++ .name = "ramdisk",
++ .size = 0x00400000,
++ .offset = MTDPART_OFS_APPEND,
++ .mask_flags = MTD_WRITEABLE
++ }, {
++ .name = "file system",
++ .size = MTDPART_SIZ_FULL,
++ .offset = MTDPART_OFS_APPEND
+ }
+ };
+ #endif
+
+ #ifdef CONFIG_SA1100_GRAPHICSCLIENT
+-#define GRAPHICSCLIENT_FLASH_SIZE 0x02000000
+ static struct mtd_partition graphicsclient_partitions[] = {
+ {
+- name: "zImage",
+- size: 0x100000,
+- offset: 0,
+- mask_flags: MTD_WRITEABLE, /* force read-only */
+- }, {
+- name: "ramdisk.gz",
+- size: 0x300000,
+- offset: MTDPART_OFS_APPEND,
+- mask_flags: MTD_WRITEABLE, /* force read-only */
+- }, {
+- name: "User FS",
+- size: MTDPART_SIZ_FULL,
+- offset: MTDPART_OFS_APPEND,
++ .name = "zImage",
++ .size = 0x100000,
++ .offset = 0,
++ .mask_flags = MTD_WRITEABLE, /* force read-only */
++ }, {
++ .name = "ramdisk.gz",
++ .size = 0x300000,
++ .offset = MTDPART_OFS_APPEND,
++ .mask_flags = MTD_WRITEABLE, /* force read-only */
++ }, {
++ .name = "User FS",
++ .size = MTDPART_SIZ_FULL,
++ .offset = MTDPART_OFS_APPEND,
+ }
+ };
+ #endif
+
+ #ifdef CONFIG_SA1100_GRAPHICSMASTER
+-#define GRAPHICSMASTER_FLASH_SIZE 0x01000000
+ static struct mtd_partition graphicsmaster_partitions[] = {
+ {
+- name: "zImage",
+- size: 0x100000,
+- offset: 0,
+- mask_flags: MTD_WRITEABLE, /* force read-only */
++ .name = "zImage",
++ .size = 0x100000,
++ .offset = 0,
++ .mask_flags = MTD_WRITEABLE, /* force read-only */
+ },
+ {
+- name: "ramdisk.gz",
+- size: 0x300000,
+- offset: MTDPART_OFS_APPEND,
+- mask_flags: MTD_WRITEABLE, /* force read-only */
++ .name = "ramdisk.gz",
++ .size = 0x300000,
++ .offset = MTDPART_OFS_APPEND,
++ .mask_flags = MTD_WRITEABLE, /* force read-only */
+ },
+ {
+- name: "User FS",
+- size: MTDPART_SIZ_FULL,
+- offset: MTDPART_OFS_APPEND,
++ .name = "User FS",
++ .size = MTDPART_SIZ_FULL,
++ .offset = MTDPART_OFS_APPEND,
+ }
+ };
+ #endif
+
+-#ifdef CONFIG_SA1100_H3600
+-#define H3600_FLASH_SIZE 0x02000000
+-static struct mtd_partition h3600_partitions[] = {
++#ifdef CONFIG_SA1100_H3XXX
++static struct mtd_partition h3xxx_partitions[] = {
+ {
+- name: "H3600 boot firmware",
+- size: 0x00040000,
+- offset: 0,
+- mask_flags: MTD_WRITEABLE, /* force read-only */
+- }, {
+- name: "H3600 kernel",
+- size: 0x00080000,
+- offset: 0x00040000,
++ .name = "H3XXX boot firmware",
++ .size = 0x00040000,
++ .offset = 0,
++ .mask_flags = MTD_WRITEABLE, /* force read-only */
+ }, {
+- name: "H3600 params",
+- size: 0x00040000,
+- offset: 0x000C0000,
++#ifdef CONFIG_MTD_2PARTS_IPAQ
++ .name = "H3XXX root jffs2",
++ .size = MTDPART_SIZ_FULL,
++ .offset = 0x00040000,
++#else
++ .name = "H3XXX kernel",
++ .size = 0x00080000,
++ .offset = 0x00040000,
++ }, {
++ .name = "H3XXX params",
++ .size = 0x00040000,
++ .offset = 0x000C0000,
+ }, {
+ #ifdef CONFIG_JFFS2_FS
+- name: "H3600 root jffs2",
+- size: MTDPART_SIZ_FULL,
+- offset: 0x00100000,
++ .name = "H3XXX root jffs2",
++ .size = MTDPART_SIZ_FULL,
++ .offset = 0x00100000,
+ #else
+- name: "H3600 initrd",
+- size: 0x00100000,
+- offset: 0x00100000,
++ .name = "H3XXX initrd",
++ .size = 0x00100000,
++ .offset = 0x00100000,
+ }, {
+- name: "H3600 root cramfs",
+- size: 0x00300000,
+- offset: 0x00200000,
++ .name = "H3XXX root cramfs",
++ .size = 0x00300000,
++ .offset = 0x00200000,
+ }, {
+- name: "H3600 usr cramfs",
+- size: 0x00800000,
+- offset: 0x00500000,
++ .name = "H3XXX usr cramfs",
++ .size = 0x00800000,
++ .offset = 0x00500000,
+ }, {
+- name: "H3600 usr local",
+- size: MTDPART_SIZ_FULL,
+- offset: 0x00d00000,
++ .name = "H3XXX usr local",
++ .size = MTDPART_SIZ_FULL,
++ .offset = 0x00d00000,
++#endif
+ #endif
+ }
+ };
+
+-static void h3600_set_vpp(struct map_info *map, int vpp)
++static void h3xxx_set_vpp(struct map_info *map, int vpp)
+ {
+ assign_h3600_egpio(IPAQ_EGPIO_VPP_ON, vpp);
+ }
++#else
++#define h3xxx_set_vpp NULL
++#endif
++
++#ifdef CONFIG_SA1100_HACKKIT
++static struct mtd_partition hackkit_partitions[] = {
++ {
++ .name = "BLOB",
++ .size = 0x00040000,
++ .offset = 0x00000000,
++ .mask_flags = MTD_WRITEABLE, /* force read-only */
++ }, {
++ .name = "config",
++ .size = 0x00040000,
++ .offset = MTDPART_OFS_APPEND,
++ }, {
++ .name = "kernel",
++ .size = 0x00100000,
++ .offset = MTDPART_OFS_APPEND,
++ }, {
++ .name = "initrd",
++ .size = 0x00180000,
++ .offset = MTDPART_OFS_APPEND,
++ }, {
++ .name = "rootfs",
++ .size = 0x700000,
++ .offset = MTDPART_OFS_APPEND,
++ }, {
++ .name = "data",
++ .size = MTDPART_SIZ_FULL,
++ .offset = MTDPART_OFS_APPEND,
++ }
++};
+ #endif
+
+ #ifdef CONFIG_SA1100_HUW_WEBPANEL
+-#define HUW_WEBPANEL_FLASH_SIZE 0x01000000
+ static struct mtd_partition huw_webpanel_partitions[] = {
+ {
+- name: "Loader",
+- size: 0x00040000,
+- offset: 0,
+- }, {
+- name: "Sector 1",
+- size: 0x00040000,
+- offset: MTDPART_OFS_APPEND,
++ .name = "Loader",
++ .size = 0x00040000,
++ .offset = 0,
++ }, {
++ .name = "Sector 1",
++ .size = 0x00040000,
++ .offset = MTDPART_OFS_APPEND,
+ }, {
+- size: MTDPART_SIZ_FULL,
+- offset: MTDPART_OFS_APPEND,
++ .size = MTDPART_SIZ_FULL,
++ .offset = MTDPART_OFS_APPEND,
+ }
+ };
+ #endif
+
+ #ifdef CONFIG_SA1100_JORNADA720
+-#define JORNADA720_FLASH_SIZE 0x02000000
+ static struct mtd_partition jornada720_partitions[] = {
+ {
+- name: "JORNADA720 boot firmware",
+- size: 0x00040000,
+- offset: 0,
+- mask_flags: MTD_WRITEABLE, /* force read-only */
++ .name = "JORNADA720 boot firmware",
++ .size = 0x00040000,
++ .offset = 0,
++ .mask_flags = MTD_WRITEABLE, /* force read-only */
+ }, {
+- name: "JORNADA720 kernel",
+- size: 0x000c0000,
+- offset: 0x00040000,
++ .name = "JORNADA720 kernel",
++ .size = 0x000c0000,
++ .offset = 0x00040000,
+ }, {
+- name: "JORNADA720 params",
+- size: 0x00040000,
+- offset: 0x00100000,
++ .name = "JORNADA720 params",
++ .size = 0x00040000,
++ .offset = 0x00100000,
+ }, {
+- name: "JORNADA720 initrd",
+- size: 0x00100000,
+- offset: 0x00140000,
++ .name = "JORNADA720 initrd",
++ .size = 0x00100000,
++ .offset = 0x00140000,
+ }, {
+- name: "JORNADA720 root cramfs",
+- size: 0x00300000,
+- offset: 0x00240000,
++ .name = "JORNADA720 root cramfs",
++ .size = 0x00300000,
++ .offset = 0x00240000,
+ }, {
+- name: "JORNADA720 usr cramfs",
+- size: 0x00800000,
+- offset: 0x00540000,
++ .name = "JORNADA720 usr cramfs",
++ .size = 0x00800000,
++ .offset = 0x00540000,
+ }, {
+- name: "JORNADA720 usr local",
+- size: 0 /* will expand to the end of the flash */
+- offset: 0x00d00000,
++ .name = "JORNADA720 usr local",
++ .size = 0, /* will expand to the end of the flash */
++ .offset = 0x00d00000,
+ }
+ };
+
+-static void jornada720_set_vpp(int vpp)
++static void jornada720_set_vpp(struct map_info *map, int vpp)
+ {
+ if (vpp)
+ PPSR |= 0x80;
+@@ -568,454 +538,811 @@
+ PPSR &= ~0x80;
+ PPDR |= 0x80;
+ }
+-
++#else
++#define jornada720_set_vpp NULL
+ #endif
+
+ #ifdef CONFIG_SA1100_PANGOLIN
+-#define PANGOLIN_FLASH_SIZE 0x04000000
+ static struct mtd_partition pangolin_partitions[] = {
+ {
+- name: "boot firmware",
+- size: 0x00080000,
+- offset: 0x00000000,
+- mask_flags: MTD_WRITEABLE, /* force read-only */
+- }, {
+- name: "kernel",
+- size: 0x00100000,
+- offset: 0x00080000,
+- }, {
+- name: "initrd",
+- size: 0x00280000,
+- offset: 0x00180000,
+- }, {
+- name: "initrd-test",
+- size: 0x03C00000,
+- offset: 0x00400000,
++ .name = "boot firmware",
++ .size = 0x00080000,
++ .offset = 0x00000000,
++ .mask_flags = MTD_WRITEABLE, /* force read-only */
++ }, {
++ .name = "kernel",
++ .size = 0x00100000,
++ .offset = 0x00080000,
++ }, {
++ .name = "initrd",
++ .size = 0x00280000,
++ .offset = 0x00180000,
++ }, {
++ .name = "initrd-test",
++ .size = 0x03C00000,
++ .offset = 0x00400000,
+ }
+ };
+ #endif
+
+ #ifdef CONFIG_SA1100_PT_SYSTEM3
+ /* erase size is 0x40000 == 256k partitions have to have this boundary */
+-#define SYSTEM3_FLASH_SIZE 0x01000000
+ static struct mtd_partition system3_partitions[] = {
+ {
+- name: "BLOB",
+- size: 0x00040000,
+- offset: 0x00000000,
+- mask_flags: MTD_WRITEABLE, /* force read-only */
+- }, {
+- name: "config",
+- size: 0x00040000,
+- offset: MTDPART_OFS_APPEND,
+- }, {
+- name: "kernel",
+- size: 0x00100000,
+- offset: MTDPART_OFS_APPEND,
+- }, {
+- name: "root",
+- size: MTDPART_SIZ_FULL,
+- offset: MTDPART_OFS_APPEND,
++ .name = "BLOB",
++ .size = 0x00040000,
++ .offset = 0x00000000,
++ .mask_flags = MTD_WRITEABLE, /* force read-only */
++ }, {
++ .name = "config",
++ .size = 0x00040000,
++ .offset = MTDPART_OFS_APPEND,
++ }, {
++ .name = "kernel",
++ .size = 0x00100000,
++ .offset = MTDPART_OFS_APPEND,
++ }, {
++ .name = "root",
++ .size = MTDPART_SIZ_FULL,
++ .offset = MTDPART_OFS_APPEND,
+ }
+ };
+ #endif
+
+ #ifdef CONFIG_SA1100_SHANNON
+-#define SHANNON_FLASH_SIZE 0x00400000
+ static struct mtd_partition shannon_partitions[] = {
+ {
+- name: "BLOB boot loader",
+- offset: 0,
+- size: 0x20000
++ .name = "BLOB boot loader",
++ .offset = 0,
++ .size = 0x20000
+ },
+ {
+- name: "kernel",
+- offset: MTDPART_OFS_APPEND,
+- size: 0xe0000
++ .name = "kernel",
++ .offset = MTDPART_OFS_APPEND,
++ .size = 0xe0000
+ },
+ {
+- name: "initrd",
+- offset: MTDPART_OFS_APPEND,
+- size: MTDPART_SIZ_FULL
++ .name = "initrd",
++ .offset = MTDPART_OFS_APPEND,
++ .size = MTDPART_SIZ_FULL
+ }
+ };
+
+ #endif
+
+ #ifdef CONFIG_SA1100_SHERMAN
+-#define SHERMAN_FLASH_SIZE 0x02000000
+ static struct mtd_partition sherman_partitions[] = {
+ {
+- size: 0x50000,
+- offset: 0,
++ .size = 0x50000,
++ .offset = 0,
+ }, {
+- size: 0x70000,
+- offset: MTDPART_OFS_APPEND,
++ .size = 0x70000,
++ .offset = MTDPART_OFS_APPEND,
+ }, {
+- size: 0x600000,
+- offset: MTDPART_OFS_APPEND,
++ .size = 0x600000,
++ .offset = MTDPART_OFS_APPEND,
+ }, {
+- size: 0xA0000,
+- offset: MTDPART_OFS_APPEND,
++ .size = 0xA0000,
++ .offset = MTDPART_OFS_APPEND,
+ }
+ };
+ #endif
+
+ #ifdef CONFIG_SA1100_SIMPAD
+-#define SIMPAD_FLASH_SIZE 0x02000000
+ static struct mtd_partition simpad_partitions[] = {
+ {
+- name: "SIMpad boot firmware",
+- size: 0x00080000,
+- offset: 0,
+- mask_flags: MTD_WRITEABLE, /* force read-only */
+- }, {
+- name: "SIMpad kernel",
+- size: 0x00100000,
+- offset: 0x00080000,
+- }, {
+-#ifdef CONFIG_JFFS2_FS
+- name: "SIMpad root jffs2",
+- size: MTDPART_SIZ_FULL,
+- offset: 0x00180000,
++ .name = "SIMpad boot firmware",
++ .size = 0x00080000,
++ .offset = 0,
++ .mask_flags = MTD_WRITEABLE, /* force read-only */
++ }, {
++ .name = "SIMpad kernel",
++ .size = 0x00100000,
++ .offset = MTDPART_OFS_APPEND,
++ }, {
++#ifdef CONFIG_ROOT_CRAMFS
++ .name = "SIMpad root cramfs",
++ .size =0x00D80000,
++ .offset = MTDPART_OFS_APPEND
++
++ }, {
++ .name = "SIMpad local jffs2",
++ .size = MTDPART_SIZ_FULL,
++ .offset = MTDPART_OFS_APPEND
+ #else
+- name: "SIMpad initrd",
+- size: 0x00300000,
+- offset: 0x00180000,
+- }, {
+- name: "SIMpad root cramfs",
+- size: 0x00300000,
+- offset: 0x00480000,
+- }, {
+- name: "SIMpad usr cramfs",
+- size: 0x005c0000,
+- offset: 0x00780000,
+- }, {
+- name: "SIMpad usr local",
+- size: MTDPART_SIZ_FULL,
+- offset: 0x00d40000,
++ .name = "SIMpad root jffs2",
++ .size = MTDPART_SIZ_FULL,
++ .offset = MTDPART_OFS_APPEND
+ #endif
+ }
+ };
+ #endif /* CONFIG_SA1100_SIMPAD */
+
+ #ifdef CONFIG_SA1100_STORK
+-#define STORK_FLASH_SIZE 0x02000000
+ static struct mtd_partition stork_partitions[] = {
+ {
+- name: "STORK boot firmware",
+- size: 0x00040000,
+- offset: 0,
+- mask_flags: MTD_WRITEABLE, /* force read-only */
+- }, {
+- name: "STORK params",
+- size: 0x00040000,
+- offset: 0x00040000,
+- }, {
+- name: "STORK kernel",
+- size: 0x00100000,
+- offset: 0x00080000,
++ .name = "STORK boot firmware",
++ .size = 0x00040000,
++ .offset = 0,
++ .mask_flags = MTD_WRITEABLE, /* force read-only */
++ }, {
++ .name = "STORK params",
++ .size = 0x00040000,
++ .offset = 0x00040000,
++ }, {
++ .name = "STORK kernel",
++ .size = 0x00100000,
++ .offset = 0x00080000,
+ }, {
+ #ifdef CONFIG_JFFS2_FS
+- name: "STORK root jffs2",
+- offset: 0x00180000,
+- size: MTDPART_SIZ_FULL,
++ .name = "STORK root jffs2",
++ .offset = 0x00180000,
++ .size = MTDPART_SIZ_FULL,
+ #else
+- name: "STORK initrd",
+- size: 0x00100000,
+- offset: 0x00180000,
+- }, {
+- name: "STORK root cramfs",
+- size: 0x00300000,
+- offset: 0x00280000,
+- }, {
+- name: "STORK usr cramfs",
+- size: 0x00800000,
+- offset: 0x00580000,
+- }, {
+- name: "STORK usr local",
+- offset: 0x00d80000,
+- size: MTDPART_SIZ_FULL,
++ .name = "STORK initrd",
++ .size = 0x00100000,
++ .offset = 0x00180000,
++ }, {
++ .name = "STORK root cramfs",
++ .size = 0x00300000,
++ .offset = 0x00280000,
++ }, {
++ .name = "STORK usr cramfs",
++ .size = 0x00800000,
++ .offset = 0x00580000,
++ }, {
++ .name = "STORK usr local",
++ .offset = 0x00d80000,
++ .size = MTDPART_SIZ_FULL,
++#endif
++ }
++};
+ #endif
++
++#ifdef CONFIG_SA1100_TRIZEPS
++static struct mtd_partition trizeps_partitions[] = {
++ {
++ .name = "Bootloader",
++ .size = 0x00100000,
++ .offset = 0,
++ }, {
++ .name = "Kernel",
++ .size = 0x00100000,
++ .offset = MTDPART_OFS_APPEND,
++ }, {
++ .name = "root",
++ .size = MTDPART_SIZ_FULL,
++ .offset = MTDPART_OFS_APPEND,
+ }
+ };
+ #endif
+
+ #ifdef CONFIG_SA1100_YOPY
+-#define YOPY_FLASH_SIZE 0x08000000
+ static struct mtd_partition yopy_partitions[] = {
+ {
+- name: "boot firmware",
+- size: 0x00040000,
+- offset: 0x00000000,
+- mask_flags: MTD_WRITEABLE, /* force read-only */
++ .name = "boot firmware",
++ .size = 0x00040000,
++ .offset = 0x00000000,
++ .mask_flags = MTD_WRITEABLE, /* force read-only */
+ }, {
+- name: "kernel",
+- size: 0x00080000,
+- offset: 0x00080000,
++ .name = "kernel",
++ .size = 0x00080000,
++ .offset = 0x00080000,
+ }, {
+- name: "initrd",
+- size: 0x00300000,
+- offset: 0x00100000,
++ .name = "initrd",
++ .size = 0x00300000,
++ .offset = 0x00100000,
+ }, {
+- name: "root",
+- size: 0x01000000,
+- offset: 0x00400000,
++ .name = "root",
++ .size = 0x01000000,
++ .offset = 0x00400000,
+ }
+ };
+ #endif
+
+-extern int parse_redboot_partitions(struct mtd_info *master, struct mtd_partition **pparts);
+-extern int parse_bootldr_partitions(struct mtd_info *master, struct mtd_partition **pparts);
+-
+-static struct mtd_partition *parsed_parts;
+-static struct mtd_info *mymtd;
+-
+-int __init sa1100_mtd_init(void)
++static int __init sa1100_static_partitions(struct mtd_partition **parts)
+ {
+- struct mtd_partition *parts;
+- int nb_parts = 0, ret;
+- int parsed_nr_parts = 0;
+- const char *part_type;
+- unsigned long base = -1UL;
+-
+- /* Default flash buswidth */
+- sa1100_map.buswidth = (MSC0 & MSC_RBW) ? 2 : 4;
+-
+- /*
+- * Static partition definition selection
+- */
+- part_type = "static";
++ int nb_parts = 0;
+
+ #ifdef CONFIG_SA1100_ADSBITSY
+ if (machine_is_adsbitsy()) {
+- parts = adsbitsy_partitions;
++ *parts = adsbitsy_partitions;
+ nb_parts = ARRAY_SIZE(adsbitsy_partitions);
+- sa1100_map.size = ADSBITSY_FLASH_SIZE;
+- sa1100_map.buswidth = (MSC1 & MSC_RBW) ? 2 : 4;
+ }
+ #endif
+ #ifdef CONFIG_SA1100_ASSABET
+ if (machine_is_assabet()) {
+- parts = assabet_partitions;
++ *parts = assabet_partitions;
+ nb_parts = ARRAY_SIZE(assabet_partitions);
+- sa1100_map.size = ASSABET_FLASH_SIZE;
+ }
+ #endif
+ #ifdef CONFIG_SA1100_BADGE4
+ if (machine_is_badge4()) {
+- parts = badge4_partitions;
++ *parts = badge4_partitions;
+ nb_parts = ARRAY_SIZE(badge4_partitions);
+- sa1100_map.size = BADGE4_FLASH_SIZE;
+ }
+ #endif
+ #ifdef CONFIG_SA1100_CERF
+ if (machine_is_cerf()) {
+- parts = cerf_partitions;
++ *parts = cerf_partitions;
+ nb_parts = ARRAY_SIZE(cerf_partitions);
+- sa1100_map.size = CERF_FLASH_SIZE;
+ }
+ #endif
+ #ifdef CONFIG_SA1100_CONSUS
+ if (machine_is_consus()) {
+- parts = consus_partitions;
++ *parts = consus_partitions;
+ nb_parts = ARRAY_SIZE(consus_partitions);
+- sa1100_map.size = CONSUS_FLASH_SIZE;
+ }
+ #endif
+ #ifdef CONFIG_SA1100_FLEXANET
+ if (machine_is_flexanet()) {
+- parts = flexanet_partitions;
++ *parts = flexanet_partitions;
+ nb_parts = ARRAY_SIZE(flexanet_partitions);
+- sa1100_map.size = FLEXANET_FLASH_SIZE;
+ }
+ #endif
+ #ifdef CONFIG_SA1100_FREEBIRD
+ if (machine_is_freebird()) {
+- parts = freebird_partitions;
++ *parts = freebird_partitions;
+ nb_parts = ARRAY_SIZE(freebird_partitions);
+- sa1100_map.size = FREEBIRD_FLASH_SIZE;
+ }
+ #endif
+ #ifdef CONFIG_SA1100_FRODO
+ if (machine_is_frodo()) {
+- parts = frodo_partitions;
++ *parts = frodo_partitions;
+ nb_parts = ARRAY_SIZE(frodo_partitions);
+- sa1100_map.size = FRODO_FLASH_SIZE;
+- base = 0x00000000;
+ }
+ #endif
+ #ifdef CONFIG_SA1100_GRAPHICSCLIENT
+ if (machine_is_graphicsclient()) {
+- parts = graphicsclient_partitions;
++ *parts = graphicsclient_partitions;
+ nb_parts = ARRAY_SIZE(graphicsclient_partitions);
+- sa1100_map.size = GRAPHICSCLIENT_FLASH_SIZE;
+- sa1100_map.buswidth = (MSC1 & MSC_RBW) ? 2:4;
+ }
+ #endif
+ #ifdef CONFIG_SA1100_GRAPHICSMASTER
+ if (machine_is_graphicsmaster()) {
+- parts = graphicsmaster_partitions;
++ *parts = graphicsmaster_partitions;
+ nb_parts = ARRAY_SIZE(graphicsmaster_partitions);
+- sa1100_map.size = GRAPHICSMASTER_FLASH_SIZE;
+- sa1100_map.buswidth = (MSC1 & MSC_RBW) ? 2:4;
+ }
+ #endif
+-#ifdef CONFIG_SA1100_H3600
+- if (machine_is_h3600()) {
+- parts = h3600_partitions;
+- nb_parts = ARRAY_SIZE(h3600_partitions);
+- sa1100_map.size = H3600_FLASH_SIZE;
+- sa1100_map.set_vpp = h3600_set_vpp;
++#ifdef CONFIG_SA1100_H3XXX
++ if (machine_is_h3xxx()) {
++ *parts = h3xxx_partitions;
++ nb_parts = ARRAY_SIZE(h3xxx_partitions);
++ }
++#endif
++#ifdef CONFIG_SA1100_HACKKIT
++ if (machine_is_hackkit()) {
++ *parts = hackkit_partitions;
++ nb_parts = ARRAY_SIZE(hackkit_partitions);
+ }
+ #endif
+ #ifdef CONFIG_SA1100_HUW_WEBPANEL
+ if (machine_is_huw_webpanel()) {
+- parts = huw_webpanel_partitions;
++ *parts = huw_webpanel_partitions;
+ nb_parts = ARRAY_SIZE(huw_webpanel_partitions);
+- sa1100_map.size = HUW_WEBPANEL_FLASH_SIZE;
+ }
+ #endif
+ #ifdef CONFIG_SA1100_JORNADA720
+ if (machine_is_jornada720()) {
+- parts = jornada720_partitions;
++ *parts = jornada720_partitions;
+ nb_parts = ARRAY_SIZE(jornada720_partitions);
+- sa1100_map.size = JORNADA720_FLASH_SIZE;
+- sa1100_map.set_vpp = jornada720_set_vpp;
+ }
+ #endif
+ #ifdef CONFIG_SA1100_PANGOLIN
+ if (machine_is_pangolin()) {
+- parts = pangolin_partitions;
++ *parts = pangolin_partitions;
+ nb_parts = ARRAY_SIZE(pangolin_partitions);
+- sa1100_map.size = PANGOLIN_FLASH_SIZE;
+ }
+ #endif
+ #ifdef CONFIG_SA1100_PT_SYSTEM3
+ if (machine_is_pt_system3()) {
+- parts = system3_partitions;
++ *parts = system3_partitions;
+ nb_parts = ARRAY_SIZE(system3_partitions);
+- sa1100_map.size = SYSTEM3_FLASH_SIZE;
+ }
+ #endif
+ #ifdef CONFIG_SA1100_SHANNON
+ if (machine_is_shannon()) {
+- parts = shannon_partitions;
++ *parts = shannon_partitions;
+ nb_parts = ARRAY_SIZE(shannon_partitions);
+- sa1100_map.size = SHANNON_FLASH_SIZE;
+ }
+ #endif
+ #ifdef CONFIG_SA1100_SHERMAN
+ if (machine_is_sherman()) {
+- parts = sherman_partitions;
++ *parts = sherman_partitions;
+ nb_parts = ARRAY_SIZE(sherman_partitions);
+- sa1100_map.size = SHERMAN_FLASH_SIZE;
+ }
+ #endif
+ #ifdef CONFIG_SA1100_SIMPAD
+ if (machine_is_simpad()) {
+- parts = simpad_partitions;
++ *parts = simpad_partitions;
+ nb_parts = ARRAY_SIZE(simpad_partitions);
+- sa1100_map.size = SIMPAD_FLASH_SIZE;
+ }
+ #endif
+ #ifdef CONFIG_SA1100_STORK
+ if (machine_is_stork()) {
+- parts = stork_partitions;
++ *parts = stork_partitions;
+ nb_parts = ARRAY_SIZE(stork_partitions);
+- sa1100_map.size = STORK_FLASH_SIZE;
++ }
++#endif
++#ifdef CONFIG_SA1100_TRIZEPS
++ if (machine_is_trizeps()) {
++ *parts = trizeps_partitions;
++ nb_parts = ARRAY_SIZE(trizeps_partitions);
+ }
+ #endif
+ #ifdef CONFIG_SA1100_YOPY
+ if (machine_is_yopy()) {
+- parts = yopy_partitions;
++ *parts = yopy_partitions;
+ nb_parts = ARRAY_SIZE(yopy_partitions);
+- sa1100_map.size = YOPY_FLASH_SIZE;
+ }
+ #endif
+
++ return nb_parts;
++}
++#endif
++
++struct sa_info {
++ unsigned long base;
++ unsigned long size;
++ int width;
++ void *vbase;
++ void (*set_vpp)(struct map_info *, int);
++ struct map_info *map;
++ struct mtd_info *mtd;
++ struct resource *res;
++};
++
++#define NR_SUBMTD 4
++
++static struct sa_info info[NR_SUBMTD];
++
++static int __init sa1100_setup_mtd(struct sa_info *sa, int nr, struct mtd_info **rmtd)
++{
++ struct mtd_info *subdev[nr];
++ struct map_info *maps;
++ int i, found = 0, ret = 0;
++
+ /*
+- * For simple flash devices, use ioremap to map the flash.
++ * Allocate the map_info structs in one go.
+ */
+- if (base != (unsigned long)-1) {
+- if (!request_mem_region(base, sa1100_map.size, "flash"))
+- return -EBUSY;
+- sa1100_map.map_priv_2 = base;
+- sa1100_map.map_priv_1 = (unsigned long)
+- ioremap(base, sa1100_map.size);
++ maps = kmalloc(sizeof(struct map_info) * nr, GFP_KERNEL);
++ if (!maps)
++ return -ENOMEM;
++
++ memset(maps, 0, sizeof(struct map_info) * nr);
++
++ /*
++ * Claim and then map the memory regions.
++ */
++ for (i = 0; i < nr; i++) {
++ if (sa[i].base == (unsigned long)-1)
++ break;
++
++ sa[i].res = request_mem_region(sa[i].base, sa[i].size, "sa1100 flash");
++ if (!sa[i].res) {
++ ret = -EBUSY;
++ break;
++ }
++
++ sa[i].map = maps + i;
++
++ sa[i].vbase = ioremap(sa[i].base, sa[i].size);
++ if (!sa[i].vbase) {
+ ret = -ENOMEM;
+- if (!sa1100_map.map_priv_1)
+- goto out_err;
++ break;
+ }
+
++ sa[i].map->virt = (unsigned long)sa[i].vbase;
++ sa[i].map->phys = sa[i].base;
++ sa[i].map->set_vpp = sa[i].set_vpp;
++ sa[i].map->buswidth = sa[i].width;
++ sa[i].map->size = sa[i].size;
++
++ simple_map_init(sa[i].map);
++
+ /*
+ * Now let's probe for the actual flash. Do it here since
+ * specific machine settings might have been set above.
+ */
+- printk(KERN_NOTICE "SA1100 flash: probing %d-bit flash bus\n", sa1100_map.buswidth*8);
+- mymtd = do_map_probe("cfi_probe", &sa1100_map);
++ sa[i].mtd = do_map_probe("cfi_probe", sa[i].map);
++ if (sa[i].mtd == NULL) {
+ ret = -ENXIO;
+- if (!mymtd)
+- goto out_err;
+- mymtd->module = THIS_MODULE;
++ break;
++ }
++ sa[i].mtd->owner = THIS_MODULE;
++ subdev[i] = sa[i].mtd;
++
++ printk(KERN_INFO "SA1100 flash: CFI device at 0x%08lx, %dMiB, "
++ "%d-bit\n", sa[i].base, sa[i].mtd->size >> 20,
++ sa[i].width * 8);
++ found += 1;
++ }
+
+ /*
+- * Dynamic partition selection stuff (might override the static ones)
++ * ENXIO is special. It means we didn't find a chip when
++ * we probed. We need to tear down the mapping, free the
++ * resource and mark it as such.
+ */
+-#ifdef CONFIG_MTD_REDBOOT_PARTS
+- if (parsed_nr_parts == 0) {
+- int ret = parse_redboot_partitions(mymtd, &parsed_parts);
++ if (ret == -ENXIO) {
++ iounmap(sa[i].vbase);
++ sa[i].vbase = NULL;
++ release_resource(sa[i].res);
++ sa[i].res = NULL;
++ }
+
+- if (ret > 0) {
+- part_type = "RedBoot";
+- parsed_nr_parts = ret;
++ /*
++ * If we found one device, don't bother with concat support.
++ * If we found multiple devices, use concat if we have it
++ * available, otherwise fail.
++ */
++ if (ret == 0 || ret == -ENXIO) {
++ if (found == 1) {
++ *rmtd = subdev[0];
++ ret = 0;
++ } else if (found > 1) {
++ /*
++ * We detected multiple devices. Concatenate
++ * them together.
++ */
++#ifdef CONFIG_MTD_CONCAT
++ *rmtd = mtd_concat_create(subdev, found,
++ "sa1100 flash");
++ if (*rmtd == NULL)
++ ret = -ENXIO;
++#else
++ printk(KERN_ERR "SA1100 flash: multiple devices "
++ "found but MTD concat support disabled.\n");
++ ret = -ENXIO;
++#endif
+ }
+ }
++
++ /*
++ * If we failed, clean up.
++ */
++ if (ret) {
++ do {
++ if (sa[i].mtd)
++ map_destroy(sa[i].mtd);
++ if (sa[i].vbase)
++ iounmap(sa[i].vbase);
++ if (sa[i].res)
++ release_resource(sa[i].res);
++ } while (i--);
++
++ kfree(maps);
++ }
++
++ return ret;
++}
++
++static void __exit sa1100_destroy_mtd(struct sa_info *sa, struct mtd_info *mtd)
++{
++ int i;
++
++ del_mtd_partitions(mtd);
++
++#ifdef CONFIG_MTD_CONCAT
++ if (mtd != sa[0].mtd)
++ mtd_concat_destroy(mtd);
+ #endif
+-#ifdef CONFIG_MTD_CMDLINE_PARTS
+- if (parsed_nr_parts == 0) {
+- int ret = parse_cmdline_partitions(mymtd, &parsed_parts, "sa1100");
+- if (ret > 0) {
+- part_type = "Command Line";
+- parsed_nr_parts = ret;
++
++ for (i = NR_SUBMTD; i >= 0; i--) {
++ if (sa[i].mtd)
++ map_destroy(sa[i].mtd);
++ if (sa[i].vbase)
++ iounmap(sa[i].vbase);
++ if (sa[i].res)
++ release_resource(sa[i].res);
+ }
++ kfree(sa[0].map);
++}
++
++/*
++ * A Thought: can we automatically detect the flash?
++ * - Check to see if the region is busy (yes -> failure)
++ * - Is the MSC setup for flash (no -> failure)
++ * - Probe for flash
++ */
++
++static struct map_info sa1100_probe_map __initdata = {
++ .name = "SA1100-flash",
++};
++
++static void __init sa1100_probe_one_cs(unsigned int msc, unsigned long phys)
++{
++ struct mtd_info *mtd;
++
++ printk(KERN_INFO "* Probing 0x%08lx: MSC = 0x%04x %d bit ",
++ phys, msc & 0xffff, msc & MSC_RBW ? 16 : 32);
++
++ if (check_mem_region(phys, 0x08000000)) {
++ printk("busy\n");
++ return;
+ }
+-#endif
+
+- if (parsed_nr_parts > 0) {
+- parts = parsed_parts;
+- nb_parts = parsed_nr_parts;
++ if ((msc & 3) == 1) {
++ printk("wrong type\n");
++ return;
+ }
+
+- if (nb_parts == 0) {
+- printk(KERN_NOTICE "SA1100 flash: no partition info available, registering whole flash at once\n");
+- add_mtd_device(mymtd);
+- } else {
+- printk(KERN_NOTICE "Using %s partition definition\n", part_type);
+- add_mtd_partitions(mymtd, parts, nb_parts);
++ sa1100_probe_map.buswidth = msc & MSC_RBW ? 2 : 4;
++ sa1100_probe_map.size = SZ_1M;
++ sa1100_probe_map.phys = phys;
++ sa1100_probe_map.virt = (unsigned long)ioremap(phys, SZ_1M);
++ if (sa1100_probe_map.virt == 0)
++ goto fail;
++ simple_map_init(&sa1100_probe_map);
++
++ /* Shame cfi_probe blurts out kernel messages... */
++ mtd = do_map_probe("cfi_probe", &sa1100_probe_map);
++ if (mtd)
++ map_destroy(mtd);
++ iounmap((void *)sa1100_probe_map.virt);
++
++ if (!mtd)
++ goto fail;
++
++ printk("pass\n");
++ return;
++
++ fail:
++ printk("failed\n");
++}
++
++static void __init sa1100_probe_flash(void)
++{
++ printk(KERN_INFO "-- SA11xx Flash probe. Please report results.\n");
++ sa1100_probe_one_cs(MSC0, SA1100_CS0_PHYS);
++ sa1100_probe_one_cs(MSC0 >> 16, SA1100_CS1_PHYS);
++ sa1100_probe_one_cs(MSC1, SA1100_CS2_PHYS);
++ sa1100_probe_one_cs(MSC1 >> 16, SA1100_CS3_PHYS);
++ sa1100_probe_one_cs(MSC2, SA1100_CS4_PHYS);
++ sa1100_probe_one_cs(MSC2 >> 16, SA1100_CS5_PHYS);
++ printk(KERN_INFO "-- SA11xx Flash probe complete.\n");
++}
++
++static int __init sa1100_locate_flash(void)
++{
++ int i, nr = -ENODEV;
++
++ sa1100_probe_flash();
++
++ if (machine_is_adsbitsy()) {
++ info[0].base = SA1100_CS1_PHYS;
++ info[0].size = SZ_32M;
++ nr = 1;
++ }
++ if (machine_is_assabet()) {
++ info[0].base = SA1100_CS0_PHYS;
++ info[0].size = SZ_32M;
++ info[1].base = SA1100_CS1_PHYS; /* neponset */
++ info[1].size = SZ_32M;
++ nr = 2;
++ }
++ if (machine_is_badge4()) {
++ info[0].base = SA1100_CS0_PHYS;
++ info[0].size = SZ_64M;
++ nr = 1;
++ }
++ if (machine_is_cerf()) {
++ info[0].base = SA1100_CS0_PHYS;
++ info[0].size = SZ_32M;
++ nr = 1;
++ }
++ if (machine_is_consus()) {
++ info[0].base = SA1100_CS0_PHYS;
++ info[0].size = SZ_32M;
++ nr = 1;
++ }
++ if (machine_is_flexanet()) {
++ info[0].base = SA1100_CS0_PHYS;
++ info[0].size = SZ_32M;
++ nr = 1;
++ }
++ if (machine_is_freebird()) {
++ info[0].base = SA1100_CS0_PHYS;
++ info[0].size = SZ_32M;
++ nr = 1;
++ }
++ if (machine_is_frodo()) {
++ info[0].base = SA1100_CS0_PHYS;
++ info[0].size = SZ_32M;
++ nr = 1;
++ }
++ if (machine_is_graphicsclient()) {
++ info[0].base = SA1100_CS1_PHYS;
++ info[0].size = SZ_32M;
++ nr = 1;
+ }
+- return 0;
++ if (machine_is_graphicsmaster()) {
++ info[0].base = SA1100_CS1_PHYS;
++ info[0].size = SZ_16M;
++ nr = 1;
++ }
++ if (machine_is_h3xxx()) {
++ info[0].set_vpp = h3xxx_set_vpp;
++ info[0].base = SA1100_CS0_PHYS;
++ info[0].size = SZ_32M;
++ nr = 1;
++ }
++ if (machine_is_huw_webpanel()) {
++ info[0].base = SA1100_CS0_PHYS;
++ info[0].size = SZ_16M;
++ nr = 1;
++ }
++ if (machine_is_itsy()) {
++ info[0].base = SA1100_CS0_PHYS;
++ info[0].size = SZ_32M;
++ nr = 1;
++ }
++ if (machine_is_jornada720()) {
++ info[0].set_vpp = jornada720_set_vpp;
++ info[0].base = SA1100_CS0_PHYS;
++ info[0].size = SZ_32M;
++ nr = 1;
++ }
++ if (machine_is_nanoengine()) {
++ info[0].base = SA1100_CS0_PHYS;
++ info[1].size = SZ_32M;
++ nr = 1;
++ }
++ if (machine_is_pangolin()) {
++ info[0].base = SA1100_CS0_PHYS;
++ info[0].size = SZ_64M;
++ nr = 1;
++ }
++ if (machine_is_pfs168()) {
++ info[0].base = SA1100_CS0_PHYS;
++ info[0].size = SZ_32M;
++ nr = 1;
++ }
++ if (machine_is_pleb()) {
++ info[0].base = SA1100_CS0_PHYS;
++ info[0].size = SZ_4M;
++ info[1].base = SA1100_CS1_PHYS;
++ info[1].size = SZ_4M;
++ nr = 2;
++ }
++ if (machine_is_pt_system3()) {
++ info[0].base = SA1100_CS0_PHYS;
++ info[0].size = SZ_16M;
++ nr = 1;
++ }
++ if (machine_is_shannon()) {
++ info[0].base = SA1100_CS0_PHYS;
++ info[0].size = SZ_4M;
++ nr = 1;
++ }
++ if (machine_is_sherman()) {
++ info[0].base = SA1100_CS0_PHYS;
++ info[0].size = SZ_32M;
++ nr = 1;
++ }
++ if (machine_is_simpad()) {
++ info[0].base = SA1100_CS0_PHYS;
++ info[0].size = SZ_16M;
++ info[1].base = SA1100_CS1_PHYS;
++ info[1].size = SZ_16M;
++ nr = 2;
++ }
++ if (machine_is_stork()) {
++ info[0].base = SA1100_CS0_PHYS;
++ info[0].size = SZ_32M;
++ nr = 1;
++ }
++ if (machine_is_trizeps()) {
++ info[0].base = SA1100_CS0_PHYS;
++ info[0].size = SZ_16M;
++ nr = 1;
++ }
++ if (machine_is_victor()) {
++ info[0].base = SA1100_CS0_PHYS;
++ info[0].size = SZ_2M;
++ nr = 1;
++ }
++ if (machine_is_yopy()) {
++ info[0].base = SA1100_CS0_PHYS;
++ info[0].size = SZ_64M;
++ info[1].base = SA1100_CS1_PHYS;
++ info[1].size = SZ_64M;
++ nr = 2;
++ }
++
++ if (nr < 0)
++ return nr;
+
+- out_err:
+- if (sa1100_map.map_priv_2 != -1) {
+- iounmap((void *)sa1100_map.map_priv_1);
+- release_mem_region(sa1100_map.map_priv_2, sa1100_map.size);
++ /*
++ * Retrieve the buswidth from the MSC registers.
++ * We currently only implement CS0 and CS1 here.
++ */
++ for (i = 0; i < nr; i++) {
++ switch (info[i].base) {
++ default:
++ printk(KERN_WARNING "SA1100 flash: unknown base address "
++ "0x%08lx, assuming CS0\n", info[i].base);
++ case SA1100_CS0_PHYS:
++ info[i].width = (MSC0 & MSC_RBW) ? 2 : 4;
++ break;
++
++ case SA1100_CS1_PHYS:
++ info[i].width = ((MSC0 >> 16) & MSC_RBW) ? 2 : 4;
++ break;
+ }
+- return ret;
++ }
++
++ return nr;
+ }
+
+-static void __exit sa1100_mtd_cleanup(void)
++static struct mtd_partition *parsed_parts;
++const char *part_probes[] = { "cmdlinepart", "RedBoot", NULL };
++
++static void __init sa1100_locate_partitions(struct mtd_info *mtd)
+ {
+- if (mymtd) {
+- del_mtd_partitions(mymtd);
+- map_destroy(mymtd);
+- if (parsed_parts)
+- kfree(parsed_parts);
++ const char *part_type = NULL;
++ int nr_parts = 0;
++
++ do {
++ /*
++ * Partition selection stuff.
++ */
++#ifdef CONFIG_MTD_PARTITIONS
++ nr_parts = parse_mtd_partitions(mtd, part_probes, &parsed_parts, 0);
++ if (nr_parts > 0) {
++ part_type = "dynamic";
++ break;
+ }
+- if (sa1100_map.map_priv_2 != -1) {
+- iounmap((void *)sa1100_map.map_priv_1);
+- release_mem_region(sa1100_map.map_priv_2, sa1100_map.size);
++#endif
++#ifdef CONFIG_MTD_SA1100_STATICMAP
++ nr_parts = sa1100_static_partitions(&parsed_parts);
++ if (nr_parts > 0) {
++ part_type = "static";
++ break;
+ }
++#endif
++ } while (0);
++
++ if (nr_parts == 0) {
++ printk(KERN_NOTICE "SA1100 flash: no partition info "
++ "available, registering whole flash\n");
++ add_mtd_device(mtd);
++ } else {
++ printk(KERN_NOTICE "SA1100 flash: using %s partition "
++ "definition\n", part_type);
++ add_mtd_partitions(mtd, parsed_parts, nr_parts);
++ }
++
++ /* Always succeeds. */
++}
++
++static void __exit sa1100_destroy_partitions(void)
++{
++ if (parsed_parts)
++ kfree(parsed_parts);
++}
++
++static struct mtd_info *mymtd;
++
++static int __init sa1100_mtd_init(void)
++{
++ int ret;
++ int nr;
++
++ nr = sa1100_locate_flash();
++ if (nr < 0)
++ return nr;
++
++ ret = sa1100_setup_mtd(info, nr, &mymtd);
++ if (ret == 0)
++ sa1100_locate_partitions(mymtd);
++
++ return ret;
++}
++
++static void __exit sa1100_mtd_cleanup(void)
++{
++ sa1100_destroy_mtd(info, mymtd);
++ sa1100_destroy_partitions();
+ }
+
+ module_init(sa1100_mtd_init);
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/maps/sbc8240.c linux/drivers/mtd/maps/sbc8240.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/maps/sbc8240.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/mtd/maps/sbc8240.c 2004-11-17 18:17:59.000000000 +0100
+@@ -0,0 +1,417 @@
++/*
++ * Handle mapping of the flash memory access routines on the SBC8240 board.
++ *
++ * Carolyn Smith, Tektronix, Inc.
++ *
++ * This code is GPLed
++ *
++ * $Id$
++ *
++ */
++
++/*
++ * The SBC8240 has 2 flash banks.
++ * Bank 0 is a 512 KiB AMD AM29F040B; 8 x 64 KiB sectors.
++ * It contains the U-Boot code (7 sectors) and the environment (1 sector).
++ * Bank 1 is 4 x 1 MiB AMD AM29LV800BT; 15 x 64 KiB sectors, 1 x 32 KiB sector,
++ * 2 x 8 KiB sectors, 1 x 16 KiB sectors.
++ * Both parts are JEDEC compatible.
++ */
++
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/kernel.h>
++#include <asm/io.h>
++
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/map.h>
++#include <linux/mtd/cfi.h>
++
++#ifdef CONFIG_MTD_PARTITIONS
++#include <linux/mtd/partitions.h>
++#endif
++
++#define DEBUG
++
++#ifdef DEBUG
++# define debugk(fmt,args...) printk(fmt ,##args)
++#else
++# define debugk(fmt,args...)
++#endif
++
++
++#define WINDOW_ADDR0 0xFFF00000 /* 512 KiB */
++#define WINDOW_SIZE0 0x00080000
++#define BUSWIDTH0 1
++
++#define WINDOW_ADDR1 0xFF000000 /* 4 MiB */
++#define WINDOW_SIZE1 0x00400000
++#define BUSWIDTH1 8
++
++#define MSG_PREFIX "sbc8240:" /* prefix for our printk()'s */
++#define MTDID "sbc8240-%d" /* for mtdparts= partitioning */
++
++
++static __u8 sbc8240_read8 (struct map_info *map, unsigned long ofs)
++{
++ return __raw_readb(map->map_priv_1 + ofs);
++}
++
++static __u16 sbc8240_read16 (struct map_info *map, unsigned long ofs)
++{
++ return __raw_readw(map->map_priv_1 + ofs);
++}
++
++static __u32 sbc8240_read32 (struct map_info *map, unsigned long ofs)
++{
++ return __raw_readl(map->map_priv_1 + ofs);
++}
++
++unsigned long long result64;
++
++static __u64 sbc8240_read64 (struct map_info *map, unsigned long ofs)
++{
++ unsigned long flags, msr, saved_msr;
++ volatile long saved_fr[2];
++ volatile unsigned long long result;
++ volatile unsigned long *p;
++
++ save_flags(flags);
++ cli();
++
++ /* turn off floating point unavailable exceptions */
++
++ __asm__ __volatile__ (
++ "mfmsr %0"
++ : "=r" (msr) :);
++
++ saved_msr = msr;
++ msr |= MSR_FP;
++ msr &= ~(MSR_FE0 | MSR_FE1);
++
++ __asm__ __volatile__ (
++ "mtmsr %0\n"
++ "isync\n"
++ : : "r" (msr));
++
++ /* read the data via a floating point register */
++
++ ofs = map->map_priv_1 + ofs;
++ p = (unsigned long *) &result64;
++
++ __asm__ __volatile__ (
++ "lfd 1,0(%1)\n"
++ "stfd 1,0(%0)\n"
++ : : "r" (p), "r" (ofs)
++ );
++
++ /* restore state */
++
++ __asm__ __volatile__ (
++ "mtmsr %0\n"
++ "isync\n"
++ : : "r" (saved_msr));
++
++ restore_flags(flags);
++
++ p = (unsigned long *) &result64;
++ debugk("sbc8240_read64 ofs 0x%x result 0x%08x%08x\n", ofs, *p, *(p+1));
++
++ return result64;
++}
++
++static void sbc8240_copy_from (struct map_info *map,
++ void *to, unsigned long from, ssize_t len)
++{
++ memcpy_fromio (to, (void *) (map->map_priv_1 + from), len);
++}
++
++static void sbc8240_write8 (struct map_info *map, __u8 d, unsigned long adr)
++{
++ __raw_writeb(d, map->map_priv_1 + adr);
++ mb();
++}
++
++static void sbc8240_write16 (struct map_info *map, __u16 d,
++ unsigned long adr)
++{
++ __raw_writew(d, map->map_priv_1 + adr);
++ mb();
++}
++
++static void sbc8240_write32 (struct map_info *map, __u32 d,
++ unsigned long adr)
++{
++ __raw_writel(d, map->map_priv_1 + adr);
++ mb();
++}
++
++static void sbc8240_write64 (struct map_info *map, __u64 data,
++ unsigned long adr)
++{
++ unsigned long long tmp;
++ unsigned long flags, msr, saved_msr, *p;
++ volatile long saved_fr[2];
++
++ save_flags(flags);
++ cli();
++
++ /* turn off floating point unavailable exceptions */
++
++ __asm__ __volatile__ (
++ "mfmsr %0"
++ : "=r" (msr) :);
++
++ saved_msr = msr;
++ msr |= MSR_FP;
++ msr &= ~(MSR_FE0 | MSR_FE1);
++
++ __asm__ __volatile__ (
++ "mtmsr %0\n"
++ "isync\n"
++ : : "r" (msr));
++
++
++ /* write the data via a floating point register */
++
++ tmp = data;
++ p = (unsigned long *) &tmp;
++ adr = map->map_priv_1 + adr;
++ debugk("sbc8240_write64 adr 0x%x data 0x%08x%08x\n", adr, *p, *(p+1));
++
++ __asm__ __volatile__ (
++ "stfd 1,0(%2)\n"
++ "lfd 1,0(%0)\n"
++ "stfd 1,0(%1)\n"
++ "lfd 1,0(%2)\n"
++ : : "r" (p), "r" (adr), "b" (saved_fr)
++ );
++
++ /* restore state */
++
++ __asm__ __volatile__ (
++ "mtmsr %0\n"
++ "isync\n"
++ : : "r" (saved_msr));
++
++ restore_flags(flags);
++}
++
++static void sbc8240_copy_to (struct map_info *map,
++ unsigned long to, const void *from, ssize_t len)
++{
++ memcpy_toio ((void *) (map->map_priv_1 + to), from, len);
++}
++
++static struct map_info sbc8240_map[2] = {
++ {
++ .name = "sbc8240 Flash Bank #0",
++ .size = WINDOW_SIZE0,
++ .buswidth = BUSWIDTH0,
++ .read8 = sbc8240_read8,
++ .read16 = sbc8240_read16,
++ .read32 = sbc8240_read32,
++ .read64 = sbc8240_read64,
++ .copy_from = sbc8240_copy_from,
++ .write8 = sbc8240_write8,
++ .write16 = sbc8240_write16,
++ .write32 = sbc8240_write32,
++ .write64 = sbc8240_write64,
++ .copy_to = sbc8240_copy_to
++ },
++ {
++ .name = "sbc8240 Flash Bank #1",
++ .size = WINDOW_SIZE1,
++ .buswidth = BUSWIDTH1,
++ .read8 = sbc8240_read8,
++ .read16 = sbc8240_read16,
++ .read32 = sbc8240_read32,
++ .read64 = sbc8240_read64,
++ .copy_from = sbc8240_copy_from,
++ .write8 = sbc8240_write8,
++ .write16 = sbc8240_write16,
++ .write32 = sbc8240_write32,
++ .write64 = sbc8240_write64,
++ .copy_to = sbc8240_copy_to
++ }
++};
++
++#define NUM_FLASH_BANKS (sizeof(sbc8240_map) / sizeof(struct map_info))
++
++/*
++ * The following defines the partition layout of SBC8240 boards.
++ *
++ * See include/linux/mtd/partitions.h for definition of the
++ * mtd_partition structure.
++ *
++ * The *_max_flash_size is the maximum possible mapped flash size
++ * which is not necessarily the actual flash size. It must correspond
++ * to the value specified in the mapping definition defined by the
++ * "struct map_desc *_io_desc" for the corresponding machine.
++ */
++
++#ifdef CONFIG_MTD_PARTITIONS
++
++static struct mtd_partition sbc8240_uboot_partitions [] = {
++ /* Bank 0 */
++ {
++ .name = "U-boot", /* U-Boot Firmware */
++ .offset = 0,
++ .size = 0x00070000, /* 7 x 64 KiB sectors */
++ .mask_flags = MTD_WRITEABLE, /* force read-only */
++ },
++ {
++ .name = "environment", /* U-Boot environment */
++ .offset = 0x00070000,
++ .size = 0x00010000, /* 1 x 64 KiB sector */
++ },
++};
++
++static struct mtd_partition sbc8240_fs_partitions [] = {
++ {
++ .name = "jffs", /* JFFS filesystem */
++ .offset = 0,
++ .size = 0x003C0000, /* 4 * 15 * 64KiB */
++ },
++ {
++ .name = "tmp32",
++ .offset = 0x003C0000,
++ .size = 0x00020000, /* 4 * 32KiB */
++ },
++ {
++ .name = "tmp8a",
++ .offset = 0x003E0000,
++ .size = 0x00008000, /* 4 * 8KiB */
++ },
++ {
++ .name = "tmp8b",
++ .offset = 0x003E8000,
++ .size = 0x00008000, /* 4 * 8KiB */
++ },
++ {
++ .name = "tmp16",
++ .offset = 0x003F0000,
++ .size = 0x00010000, /* 4 * 16KiB */
++ }
++};
++
++#define NB_OF(x) (sizeof (x) / sizeof (x[0]))
++
++/* trivial struct to describe partition information */
++struct mtd_part_def
++{
++ int nums;
++ unsigned char *type;
++ struct mtd_partition* mtd_part;
++};
++
++static struct mtd_info *sbc8240_mtd[NUM_FLASH_BANKS];
++static struct mtd_part_def sbc8240_part_banks[NUM_FLASH_BANKS];
++
++
++#endif /* CONFIG_MTD_PARTITIONS */
++
++
++int __init init_sbc8240_mtd (void)
++{
++ static struct _cjs {
++ u_long addr;
++ u_long size;
++ } pt[NUM_FLASH_BANKS] = {
++ {
++ .addr = WINDOW_ADDR0,
++ .size = WINDOW_SIZE0
++ },
++ {
++ .addr = WINDOW_ADDR1,
++ .size = WINDOW_SIZE1
++ },
++ };
++
++ int devicesfound = 0;
++ int i;
++
++ for (i = 0; i < NUM_FLASH_BANKS; i++) {
++ printk (KERN_NOTICE MSG_PREFIX
++ "Probing 0x%08lx at 0x%08lx\n", pt[i].size, pt[i].addr);
++
++ sbc8240_map[i].map_priv_1 =
++ (unsigned long) ioremap (pt[i].addr, pt[i].size);
++ if (!sbc8240_map[i].map_priv_1) {
++ printk (MSG_PREFIX "failed to ioremap\n");
++ return -EIO;
++ }
++
++ sbc8240_mtd[i] = do_map_probe("jedec_probe", &sbc8240_map[i]);
++
++ if (sbc8240_mtd[i]) {
++ sbc8240_mtd[i]->module = THIS_MODULE;
++ devicesfound++;
++ }
++ }
++
++ if (!devicesfound) {
++ printk(KERN_NOTICE MSG_PREFIX
++ "No suppported flash chips found!\n");
++ return -ENXIO;
++ }
++
++#ifdef CONFIG_MTD_PARTITIONS
++ sbc8240_part_banks[0].mtd_part = sbc8240_uboot_partitions;
++ sbc8240_part_banks[0].type = "static image";
++ sbc8240_part_banks[0].nums = NB_OF(sbc8240_uboot_partitions);
++ sbc8240_part_banks[1].mtd_part = sbc8240_fs_partitions;
++ sbc8240_part_banks[1].type = "static file system";
++ sbc8240_part_banks[1].nums = NB_OF(sbc8240_fs_partitions);
++
++ for (i = 0; i < NUM_FLASH_BANKS; i++) {
++
++ if (!sbc8240_mtd[i]) continue;
++ if (sbc8240_part_banks[i].nums == 0) {
++ printk (KERN_NOTICE MSG_PREFIX
++ "No partition info available, registering whole device\n");
++ add_mtd_device(sbc8240_mtd[i]);
++ } else {
++ printk (KERN_NOTICE MSG_PREFIX
++ "Using %s partition definition\n", sbc8240_part_banks[i].mtd_part->name);
++ add_mtd_partitions (sbc8240_mtd[i],
++ sbc8240_part_banks[i].mtd_part,
++ sbc8240_part_banks[i].nums);
++ }
++ }
++#else
++ printk(KERN_NOTICE MSG_PREFIX
++ "Registering %d flash banks at once\n", devicesfound);
++
++ for (i = 0; i < devicesfound; i++) {
++ add_mtd_device(sbc8240_mtd[i]);
++ }
++#endif /* CONFIG_MTD_PARTITIONS */
++
++ return devicesfound == 0 ? -ENXIO : 0;
++}
++
++static void __exit cleanup_sbc8240_mtd (void)
++{
++ int i;
++
++ for (i = 0; i < NUM_FLASH_BANKS; i++) {
++ if (sbc8240_mtd[i]) {
++ del_mtd_device (sbc8240_mtd[i]);
++ map_destroy (sbc8240_mtd[i]);
++ }
++ if (sbc8240_map[i].map_priv_1) {
++ iounmap ((void *) sbc8240_map[i].map_priv_1);
++ sbc8240_map[i].map_priv_1 = 0;
++ }
++ }
++}
++
++module_init (init_sbc8240_mtd);
++module_exit (cleanup_sbc8240_mtd);
++
++MODULE_LICENSE ("GPL");
++MODULE_AUTHOR ("Carolyn Smith <carolyn.smith@tektronix.com>");
++MODULE_DESCRIPTION ("MTD map driver for SBC8240 boards");
++
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/maps/sbc_gxx.c linux/drivers/mtd/maps/sbc_gxx.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/maps/sbc_gxx.c 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/maps/sbc_gxx.c 2004-11-17 18:17:59.132299784 +0100
+@@ -17,7 +17,7 @@
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+
+- $Id$
++ $Id$
+
+ The SBC-MediaGX / SBC-GXx has up to 16 MiB of
+ Intel StrataFlash (28F320/28F640) in x8 mode.
+@@ -91,14 +91,14 @@
+ * single flash device into. If the size if zero we use up to the end of the
+ * device. */
+ static struct mtd_partition partition_info[]={
+- { name: "SBC-GXx flash boot partition",
+- offset: 0,
+- size: BOOT_PARTITION_SIZE_KiB*1024 },
+- { name: "SBC-GXx flash data partition",
+- offset: BOOT_PARTITION_SIZE_KiB*1024,
+- size: (DATA_PARTITION_SIZE_KiB)*1024 },
+- { name: "SBC-GXx flash application partition",
+- offset: (BOOT_PARTITION_SIZE_KiB+DATA_PARTITION_SIZE_KiB)*1024 }
++ { .name = "SBC-GXx flash boot partition",
++ .offset = 0,
++ .size = BOOT_PARTITION_SIZE_KiB*1024 },
++ { .name = "SBC-GXx flash data partition",
++ .offset = BOOT_PARTITION_SIZE_KiB*1024,
++ .size = (DATA_PARTITION_SIZE_KiB)*1024 },
++ { .name = "SBC-GXx flash application partition",
++ .offset = (BOOT_PARTITION_SIZE_KiB+DATA_PARTITION_SIZE_KiB)*1024 }
+ };
+
+ #define NUM_PARTITIONS 3
+@@ -203,19 +203,20 @@
+ }
+
+ static struct map_info sbc_gxx_map = {
+- name: "SBC-GXx flash",
+- size: MAX_SIZE_KiB*1024, /* this must be set to a maximum possible amount
++ .name = "SBC-GXx flash",
++ .phys = NO_XIP,
++ .size = MAX_SIZE_KiB*1024, /* this must be set to a maximum possible amount
+ of flash so the cfi probe routines find all
+ the chips */
+- buswidth: 1,
+- read8: sbc_gxx_read8,
+- read16: sbc_gxx_read16,
+- read32: sbc_gxx_read32,
+- copy_from: sbc_gxx_copy_from,
+- write8: sbc_gxx_write8,
+- write16: sbc_gxx_write16,
+- write32: sbc_gxx_write32,
+- copy_to: sbc_gxx_copy_to
++ .buswidth = 1,
++ .read8 = sbc_gxx_read8,
++ .read16 = sbc_gxx_read16,
++ .read32 = sbc_gxx_read32,
++ .copy_from = sbc_gxx_copy_from,
++ .write8 = sbc_gxx_write8,
++ .write16 = sbc_gxx_write16,
++ .write32 = sbc_gxx_write32,
++ .copy_to = sbc_gxx_copy_to
+ };
+
+ /* MTD device for all of the flash. */
+@@ -234,12 +235,6 @@
+
+ int __init init_sbc_gxx(void)
+ {
+- if (check_region(PAGE_IO,PAGE_IO_SIZE) != 0) {
+- printk( KERN_ERR"%s: IO ports 0x%x-0x%x in use\n",
+- sbc_gxx_map.name,
+- PAGE_IO, PAGE_IO+PAGE_IO_SIZE-1 );
+- return -EAGAIN;
+- }
+ iomapadr = (unsigned long)ioremap(WINDOW_START, WINDOW_LENGTH);
+ if (!iomapadr) {
+ printk( KERN_ERR"%s: failed to ioremap memory region\n",
+@@ -247,7 +242,14 @@
+ return -EIO;
+ }
+
+- request_region( PAGE_IO, PAGE_IO_SIZE, "SBC-GXx flash" );
++ if (!request_region( PAGE_IO, PAGE_IO_SIZE, "SBC-GXx flash")) {
++ printk( KERN_ERR"%s: IO ports 0x%x-0x%x in use\n",
++ sbc_gxx_map.name,
++ PAGE_IO, PAGE_IO+PAGE_IO_SIZE-1 );
++ iounmap((void *)iomapadr);
++ return -EAGAIN;
++ }
++
+
+ printk( KERN_INFO"%s: IO:0x%x-0x%x MEM:0x%x-0x%x\n",
+ sbc_gxx_map.name,
+@@ -261,7 +263,7 @@
+ return -ENXIO;
+ }
+
+- all_mtd->module=THIS_MODULE;
++ all_mtd->owner = THIS_MODULE;
+
+ /* Create MTD devices for each partition. */
+ add_mtd_partitions(all_mtd, partition_info, NUM_PARTITIONS );
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/maps/sc520cdp.c linux/drivers/mtd/maps/sc520cdp.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/maps/sc520cdp.c 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/maps/sc520cdp.c 2004-11-17 18:17:59.133299632 +0100
+@@ -16,7 +16,7 @@
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+- * $Id$
++ * $Id$
+ *
+ *
+ * The SC520CDP is an evaluation board for the Elan SC520 processor available
+@@ -29,6 +29,7 @@
+ #include <linux/module.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
+ #include <asm/io.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+@@ -84,88 +85,25 @@
+ #define WINDOW_SIZE_1 0x00800000
+ #define WINDOW_SIZE_2 0x00080000
+
+-static __u8 sc520cdp_read8(struct map_info *map, unsigned long ofs)
+-{
+- return readb(map->map_priv_1 + ofs);
+-}
+-
+-static __u16 sc520cdp_read16(struct map_info *map, unsigned long ofs)
+-{
+- return readw(map->map_priv_1 + ofs);
+-}
+-
+-static __u32 sc520cdp_read32(struct map_info *map, unsigned long ofs)
+-{
+- return readl(map->map_priv_1 + ofs);
+-}
+-
+-static void sc520cdp_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+-{
+- memcpy_fromio(to, (void *)(map->map_priv_1 + from), len);
+-}
+-
+-static void sc520cdp_write8(struct map_info *map, __u8 d, unsigned long adr)
+-{
+- writeb(d, map->map_priv_1 + adr);
+-}
+-
+-static void sc520cdp_write16(struct map_info *map, __u16 d, unsigned long adr)
+-{
+- writew(d, map->map_priv_1 + adr);
+-}
+-
+-static void sc520cdp_write32(struct map_info *map, __u32 d, unsigned long adr)
+-{
+- writel(d, map->map_priv_1 + adr);
+-}
+-
+-static void sc520cdp_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+-{
+- memcpy_toio((void *)(map->map_priv_1 + to), from, len);
+-}
+
+ static struct map_info sc520cdp_map[] = {
+ {
+- name: "SC520CDP Flash Bank #0",
+- size: WINDOW_SIZE_0,
+- buswidth: 4,
+- read8: sc520cdp_read8,
+- read16: sc520cdp_read16,
+- read32: sc520cdp_read32,
+- copy_from: sc520cdp_copy_from,
+- write8: sc520cdp_write8,
+- write16: sc520cdp_write16,
+- write32: sc520cdp_write32,
+- copy_to: sc520cdp_copy_to,
+- map_priv_2: WINDOW_ADDR_0
++ .name = "SC520CDP Flash Bank #0",
++ .size = WINDOW_SIZE_0,
++ .buswidth = 4,
++ .phys = WINDOW_ADDR_0
+ },
+ {
+- name: "SC520CDP Flash Bank #1",
+- size: WINDOW_SIZE_1,
+- buswidth: 4,
+- read8: sc520cdp_read8,
+- read16: sc520cdp_read16,
+- read32: sc520cdp_read32,
+- copy_from: sc520cdp_copy_from,
+- write8: sc520cdp_write8,
+- write16: sc520cdp_write16,
+- write32: sc520cdp_write32,
+- copy_to: sc520cdp_copy_to,
+- map_priv_2: WINDOW_ADDR_1
++ .name = "SC520CDP Flash Bank #1",
++ .size = WINDOW_SIZE_1,
++ .buswidth = 4,
++ .phys = WINDOW_ADDR_1
+ },
+ {
+- name: "SC520CDP DIL Flash",
+- size: WINDOW_SIZE_2,
+- buswidth: 1,
+- read8: sc520cdp_read8,
+- read16: sc520cdp_read16,
+- read32: sc520cdp_read32,
+- copy_from: sc520cdp_copy_from,
+- write8: sc520cdp_write8,
+- write16: sc520cdp_write16,
+- write32: sc520cdp_write32,
+- copy_to: sc520cdp_copy_to,
+- map_priv_2: WINDOW_ADDR_2
++ .name = "SC520CDP DIL Flash",
++ .size = WINDOW_SIZE_2,
++ .buswidth = 1,
++ .phys = WINDOW_ADDR_2
+ },
+ };
+
+@@ -255,9 +193,9 @@
+ /* map in SC520's MMCR area */
+ mmcr = (unsigned long *)ioremap_nocache(SC520_MMCR_BASE, SC520_MMCR_EXTENT);
+ if(!mmcr) { /* ioremap_nocache failed: skip the PAR reprogramming */
+- /* force map_priv_2 fields to BIOS defaults: */
++ /* force physical address fields to BIOS defaults: */
+ for(i = 0; i < NUM_FLASH_BANKS; i++)
+- sc520cdp_map[i].map_priv_2 = par_table[i].default_address;
++ sc520cdp_map[i].phys = par_table[i].default_address;
+ return;
+ }
+
+@@ -282,7 +220,7 @@
+ sc520cdp_map[i].name);
+ printk(KERN_NOTICE "Trying default address 0x%lx\n",
+ par_table[i].default_address);
+- sc520cdp_map[i].map_priv_2 = par_table[i].default_address;
++ sc520cdp_map[i].phys = par_table[i].default_address;
+ }
+ }
+ iounmap((void *)mmcr);
+@@ -300,13 +238,18 @@
+ #endif
+
+ for (i = 0; i < NUM_FLASH_BANKS; i++) {
+- printk(KERN_NOTICE "SC520 CDP flash device: %lx at %lx\n", sc520cdp_map[i].size, sc520cdp_map[i].map_priv_2);
+- sc520cdp_map[i].map_priv_1 = (unsigned long)ioremap_nocache(sc520cdp_map[i].map_priv_2, sc520cdp_map[i].size);
++ printk(KERN_NOTICE "SC520 CDP flash device: 0x%lx at 0x%lx\n",
++ sc520cdp_map[i].size, sc520cdp_map[i].phys);
+
+- if (!sc520cdp_map[i].map_priv_1) {
++ sc520cdp_map[i].virt = (unsigned long)ioremap_nocache(sc520cdp_map[i].phys, sc520cdp_map[i].size);
++
++ if (!sc520cdp_map[i].virt) {
+ printk("Failed to ioremap_nocache\n");
+ return -EIO;
+ }
++
++ simple_map_init(&sc520cdp_map[i]);
++
+ mymtd[i] = do_map_probe("cfi_probe", &sc520cdp_map[i]);
+ if(!mymtd[i])
+ mymtd[i] = do_map_probe("jedec_probe", &sc520cdp_map[i]);
+@@ -314,11 +257,11 @@
+ mymtd[i] = do_map_probe("map_rom", &sc520cdp_map[i]);
+
+ if (mymtd[i]) {
+- mymtd[i]->module = THIS_MODULE;
++ mymtd[i]->owner = THIS_MODULE;
+ ++devices_found;
+ }
+ else {
+- iounmap((void *)sc520cdp_map[i].map_priv_1);
++ iounmap((void *)sc520cdp_map[i].virt);
+ }
+ }
+ if(devices_found >= 2) {
+@@ -346,9 +289,9 @@
+ for (i = 0; i < NUM_FLASH_BANKS; i++) {
+ if (mymtd[i])
+ map_destroy(mymtd[i]);
+- if (sc520cdp_map[i].map_priv_1) {
+- iounmap((void *)sc520cdp_map[i].map_priv_1);
+- sc520cdp_map[i].map_priv_1 = 0;
++ if (sc520cdp_map[i].virt) {
++ iounmap((void *)sc520cdp_map[i].virt);
++ sc520cdp_map[i].virt = 0;
+ }
+ }
+ }
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/maps/scb2_flash.c linux/drivers/mtd/maps/scb2_flash.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/maps/scb2_flash.c 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/maps/scb2_flash.c 2004-11-17 18:17:59.135299328 +0100
+@@ -1,6 +1,6 @@
+ /*
+ * MTD map driver for BIOS Flash on Intel SCB2 boards
+- * $Id$
++ * $Id$
+ * Copyright (C) 2002 Sun Microsystems, Inc.
+ * Tim Hockin <thockin@sun.com>
+ *
+@@ -14,7 +14,7 @@
+ * try to request it here, but if it fails, we carry on anyway.
+ *
+ * This is how the chip is attached, so said the schematic:
+- * * a 4 MiB (32 Mb) 16 bit chip
++ * * a 4 MiB (32 Mib) 16 bit chip
+ * * a 1 MiB memory region
+ * * A20 and A21 pulled up
+ * * D8-D15 ignored
+@@ -48,6 +48,7 @@
+ #include <linux/module.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
+ #include <asm/io.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+@@ -60,65 +61,13 @@
+ #define SCB2_ADDR 0xfff00000
+ #define SCB2_WINDOW 0x00100000
+
+-static __u8 scb2_read8(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readb(map->map_priv_1 + ofs);
+-}
+-
+-static __u16 scb2_read16(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readw(map->map_priv_1 + ofs);
+-}
+-
+-static __u32 scb2_read32(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readl(map->map_priv_1 + ofs);
+-}
+-
+-static void scb2_copy_from(struct map_info *map, void *to,
+- unsigned long from, ssize_t len)
+-{
+- memcpy_fromio(to, map->map_priv_1 + from, len);
+-}
+-
+-static void scb2_write8(struct map_info *map, __u8 d, unsigned long adr)
+-{
+- __raw_writeb(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-static void scb2_write16(struct map_info *map, __u16 d, unsigned long adr)
+-{
+- __raw_writew(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-static void scb2_write32(struct map_info *map, __u32 d, unsigned long adr)
+-{
+- __raw_writel(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-static void scb2_copy_to(struct map_info *map, unsigned long to,
+- const void *from, ssize_t len)
+-{
+- memcpy_toio(map->map_priv_1 + to, from, len);
+-}
+
+ static void *scb2_ioaddr;
+ static struct mtd_info *scb2_mtd;
+ struct map_info scb2_map = {
+- name: "SCB2 BIOS Flash",
+- size: 0,
+- buswidth: 1,
+- read8: scb2_read8,
+- read16: scb2_read16,
+- read32: scb2_read32,
+- copy_from: scb2_copy_from,
+- write8: scb2_write8,
+- write16: scb2_write16,
+- write32: scb2_write32,
+- copy_to: scb2_copy_to,
++ .name = "SCB2 BIOS Flash",
++ .size = 0,
++ .buswidth = 1,
+ };
+ static int region_fail;
+
+@@ -137,6 +86,8 @@
+ return -1;
+ }
+
++ /* I wasn't here. I didn't see. dwmw2. */
++
+ /* the chip is sometimes bigger than the map - what a waste */
+ mtd->size = map->size;
+
+@@ -211,9 +162,12 @@
+ return -ENOMEM;
+ }
+
+- scb2_map.map_priv_1 = (unsigned long)scb2_ioaddr;
++ scb2_map.phys = SCB2_ADDR;
++ scb2_map.virt = (unsigned long)scb2_ioaddr;
+ scb2_map.size = SCB2_WINDOW;
+
++ simple_map_init(&scb2_map);
++
+ /* try to find a chip */
+ scb2_mtd = do_map_probe("cfi_probe", &scb2_map);
+
+@@ -225,7 +179,7 @@
+ return -ENODEV;
+ }
+
+- scb2_mtd->module = THIS_MODULE;
++ scb2_mtd->owner = THIS_MODULE;
+ if (scb2_fixup_mtd(scb2_mtd) < 0) {
+ del_mtd_device(scb2_mtd);
+ map_destroy(scb2_mtd);
+@@ -235,7 +189,7 @@
+ return -ENODEV;
+ }
+
+- printk(KERN_NOTICE MODNAME ": chip size %x at offset %x\n",
++ printk(KERN_NOTICE MODNAME ": chip size 0x%x at offset 0x%x\n",
+ scb2_mtd->size, SCB2_WINDOW - scb2_mtd->size);
+
+ add_mtd_device(scb2_mtd);
+@@ -266,19 +220,19 @@
+
+ static struct pci_device_id scb2_flash_pci_ids[] __devinitdata = {
+ {
+- vendor: PCI_VENDOR_ID_SERVERWORKS,
+- device: PCI_DEVICE_ID_SERVERWORKS_CSB5,
+- subvendor: PCI_ANY_ID,
+- subdevice: PCI_ANY_ID
++ .vendor = PCI_VENDOR_ID_SERVERWORKS,
++ .device = PCI_DEVICE_ID_SERVERWORKS_CSB5,
++ .subvendor = PCI_ANY_ID,
++ .subdevice = PCI_ANY_ID
+ },
+ { 0, }
+ };
+
+ static struct pci_driver scb2_flash_driver = {
+- name: "Intel SCB2 BIOS Flash",
+- id_table: scb2_flash_pci_ids,
+- probe: scb2_flash_probe,
+- remove: __devexit_p(scb2_flash_remove),
++ .name = "Intel SCB2 BIOS Flash",
++ .id_table = scb2_flash_pci_ids,
++ .probe = scb2_flash_probe,
++ .remove = __devexit_p(scb2_flash_remove),
+ };
+
+ static int __init
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/maps/scx200_docflash.c linux/drivers/mtd/maps/scx200_docflash.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/maps/scx200_docflash.c 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/maps/scx200_docflash.c 2004-11-17 18:17:59.136299176 +0100
+@@ -2,7 +2,7 @@
+
+ Copyright (c) 2001,2002 Christer Weinigel <wingel@nano-system.com>
+
+- $Id$
++ $Id$
+
+ National Semiconductor SCx200 flash mapped with DOCCS
+ */
+@@ -11,6 +11,7 @@
+ #include <linux/config.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
+ #include <asm/io.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+@@ -75,46 +76,9 @@
+ #define NUM_PARTITIONS (sizeof(partition_info)/sizeof(partition_info[0]))
+ #endif
+
+-static __u8 scx200_docflash_read8(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readb(map->map_priv_1 + ofs);
+-}
+-
+-static __u16 scx200_docflash_read16(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readw(map->map_priv_1 + ofs);
+-}
+-
+-static void scx200_docflash_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+-{
+- memcpy_fromio(to, map->map_priv_1 + from, len);
+-}
+-
+-static void scx200_docflash_write8(struct map_info *map, __u8 d, unsigned long adr)
+-{
+- __raw_writeb(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-static void scx200_docflash_write16(struct map_info *map, __u16 d, unsigned long adr)
+-{
+- __raw_writew(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-static void scx200_docflash_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+-{
+- memcpy_toio(map->map_priv_1 + to, from, len);
+-}
+
+ static struct map_info scx200_docflash_map = {
+ .name = "NatSemi SCx200 DOCCS Flash",
+- .read8 = scx200_docflash_read8,
+- .read16 = scx200_docflash_read16,
+- .copy_from = scx200_docflash_copy_from,
+- .write8 = scx200_docflash_write8,
+- .write16 = scx200_docflash_write16,
+- .copy_to = scx200_docflash_copy_to
+ };
+
+ int __init init_scx200_docflash(void)
+@@ -213,8 +177,11 @@
+ else
+ scx200_docflash_map.buswidth = 2;
+
+- scx200_docflash_map.map_priv_1 = (unsigned long)ioremap(docmem.start, scx200_docflash_map.size);
+- if (!scx200_docflash_map.map_priv_1) {
++ simple_map_init(&scx200_docflash_map);
++
++ scx200_docflash_map.phys = docmem.start;
++ scx200_docflash_map.virt = (unsigned long)ioremap(docmem.start, scx200_docflash_map.size);
++ if (!scx200_docflash_map.virt) {
+ printk(KERN_ERR NAME ": failed to ioremap the flash\n");
+ release_resource(&docmem);
+ return -EIO;
+@@ -223,7 +190,7 @@
+ mymtd = do_map_probe(flashtype, &scx200_docflash_map);
+ if (!mymtd) {
+ printk(KERN_ERR NAME ": unable to detect flash\n");
+- iounmap((void *)scx200_docflash_map.map_priv_1);
++ iounmap((void *)scx200_docflash_map.virt);
+ release_resource(&docmem);
+ return -ENXIO;
+ }
+@@ -231,7 +198,7 @@
+ if (size < mymtd->size)
+ printk(KERN_WARNING NAME ": warning, flash mapping is smaller than flash size\n");
+
+- mymtd->module = THIS_MODULE;
++ mymtd->owner = THIS_MODULE;
+
+ #if PARTITION
+ partition_info[3].offset = mymtd->size-partition_info[3].size;
+@@ -253,8 +220,8 @@
+ #endif
+ map_destroy(mymtd);
+ }
+- if (scx200_docflash_map.map_priv_1) {
+- iounmap((void *)scx200_docflash_map.map_priv_1);
++ if (scx200_docflash_map.virt) {
++ iounmap((void *)scx200_docflash_map.virt);
+ release_resource(&docmem);
+ }
+ }
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/maps/solutionengine.c linux/drivers/mtd/maps/solutionengine.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/maps/solutionengine.c 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/maps/solutionengine.c 2004-11-17 18:17:59.137299024 +0100
+@@ -1,5 +1,5 @@
+ /*
+- * $Id$
++ * $Id$
+ *
+ * Flash and EPROM on Hitachi Solution Engine and similar boards.
+ *
+@@ -11,6 +11,7 @@
+ #include <linux/module.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
+ #include <asm/io.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+@@ -18,60 +19,39 @@
+ #include <linux/config.h>
+
+
+-extern int parse_redboot_partitions(struct mtd_info *master, struct mtd_partition **pparts);
+-
+-__u32 soleng_read32(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readl(map->map_priv_1 + ofs);
+-}
+-
+-void soleng_write32(struct map_info *map, __u32 d, unsigned long adr)
+-{
+- __raw_writel(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-void soleng_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+-{
+- memcpy_fromio(to, map->map_priv_1 + from, len);
+-}
+-
+-
+ static struct mtd_info *flash_mtd;
+ static struct mtd_info *eprom_mtd;
+
+ static struct mtd_partition *parsed_parts;
+
+ struct map_info soleng_eprom_map = {
+- name: "Solution Engine EPROM",
+- size: 0x400000,
+- buswidth: 4,
+- copy_from: soleng_copy_from,
++ .name = "Solution Engine EPROM",
++ .size = 0x400000,
++ .buswidth = 4,
+ };
+
+ struct map_info soleng_flash_map = {
+- name: "Solution Engine FLASH",
+- size: 0x400000,
+- buswidth: 4,
+- read32: soleng_read32,
+- copy_from: soleng_copy_from,
+- write32: soleng_write32,
++ .name = "Solution Engine FLASH",
++ .size = 0x400000,
++ .buswidth = 4,
+ };
+
++static const char *probes[] = { "RedBoot", "cmdlinepart", NULL };
++
+ #ifdef CONFIG_MTD_SUPERH_RESERVE
+ static struct mtd_partition superh_se_partitions[] = {
+ /* Reserved for boot code, read-only */
+ {
+- name: "flash_boot",
+- offset: 0x00000000,
+- size: CONFIG_MTD_SUPERH_RESERVE,
+- mask_flags: MTD_WRITEABLE,
++ .name = "flash_boot",
++ .offset = 0x00000000,
++ .size = CONFIG_MTD_SUPERH_RESERVE,
++ .mask_flags = MTD_WRITEABLE,
+ },
+ /* All else is writable (e.g. JFFS) */
+ {
+- name: "Flash FS",
+- offset: MTDPART_OFS_NXTBLK,
+- size: MTDPART_SIZ_FULL,
++ .name = "Flash FS",
++ .offset = MTDPART_OFS_NXTBLK,
++ .size = MTDPART_SIZ_FULL,
+ }
+ };
+ #endif /* CONFIG_MTD_SUPERH_RESERVE */
+@@ -81,16 +61,22 @@
+ int nr_parts = 0;
+
+ /* First probe at offset 0 */
+- soleng_flash_map.map_priv_1 = P2SEGADDR(0);
+- soleng_eprom_map.map_priv_1 = P1SEGADDR(0x01000000);
++ soleng_flash_map.phys = 0;
++ soleng_flash_map.virt = P2SEGADDR(0);
++ soleng_eprom_map.phys = 0x01000000;
++ soleng_eprom_map.virt = P1SEGADDR(0x01000000);
++ simple_map_init(&soleng_eprom_map);
++ simple_map_init(&soleng_flash_map);
+
+ printk(KERN_NOTICE "Probing for flash chips at 0x00000000:\n");
+ flash_mtd = do_map_probe("cfi_probe", &soleng_flash_map);
+ if (!flash_mtd) {
+ /* Not there. Try swapping */
+ printk(KERN_NOTICE "Probing for flash chips at 0x01000000:\n");
+- soleng_flash_map.map_priv_1 = P2SEGADDR(0x01000000);
+- soleng_eprom_map.map_priv_1 = P1SEGADDR(0);
++ soleng_flash_map.phys = 0x01000000;
++ soleng_flash_map.virt = P2SEGADDR(0x01000000);
++ soleng_eprom_map.phys = 0;
++ soleng_eprom_map.virt = P1SEGADDR(0);
+ flash_mtd = do_map_probe("cfi_probe", &soleng_flash_map);
+ if (!flash_mtd) {
+ /* Eep. */
+@@ -99,25 +85,20 @@
+ }
+ }
+ printk(KERN_NOTICE "Solution Engine: Flash at 0x%08lx, EPROM at 0x%08lx\n",
+- soleng_flash_map.map_priv_1 & 0x1fffffff,
+- soleng_eprom_map.map_priv_1 & 0x1fffffff);
+- flash_mtd->module = THIS_MODULE;
++ soleng_flash_map.phys & 0x1fffffff,
++ soleng_eprom_map.phys & 0x1fffffff);
++ flash_mtd->owner = THIS_MODULE;
+
+ eprom_mtd = do_map_probe("map_rom", &soleng_eprom_map);
+ if (eprom_mtd) {
+- eprom_mtd->module = THIS_MODULE;
++ eprom_mtd->owner = THIS_MODULE;
+ add_mtd_device(eprom_mtd);
+ }
+
+-#ifdef CONFIG_MTD_REDBOOT_PARTS
+- nr_parts = parse_redboot_partitions(flash_mtd, &parsed_parts);
+- if (nr_parts > 0)
+- printk(KERN_NOTICE "Found RedBoot partition table.\n");
+- else if (nr_parts < 0)
+- printk(KERN_NOTICE "Error looking for RedBoot partitions.\n");
+-#endif /* CONFIG_MTD_REDBOOT_PARTS */
++ nr_parts = parse_mtd_partitions(flash_mtd, probes, &parsed_parts, 0);
++
+ #if CONFIG_MTD_SUPERH_RESERVE
+- if (nr_parts == 0) {
++ if (nr_parts <= 0) {
+ printk(KERN_NOTICE "Using configured partition at 0x%08x.\n",
+ CONFIG_MTD_SUPERH_RESERVE);
+ parsed_parts = superh_se_partitions;
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/maps/sun_uflash.c linux/drivers/mtd/maps/sun_uflash.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/maps/sun_uflash.c 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/maps/sun_uflash.c 2004-11-17 18:17:59.139298720 +0100
+@@ -1,4 +1,4 @@
+-/* $Id$
++/* $Id$
+ *
+ * sun_uflash - Driver implementation for user-programmable flash
+ * present on many Sun Microsystems SME boardsets.
+@@ -48,60 +48,11 @@
+ struct list_head list;
+ };
+
+-__u8 uflash_read8(struct map_info *map, unsigned long ofs)
+-{
+- return(__raw_readb(map->map_priv_1 + ofs));
+-}
+-
+-__u16 uflash_read16(struct map_info *map, unsigned long ofs)
+-{
+- return(__raw_readw(map->map_priv_1 + ofs));
+-}
+-
+-__u32 uflash_read32(struct map_info *map, unsigned long ofs)
+-{
+- return(__raw_readl(map->map_priv_1 + ofs));
+-}
+-
+-void uflash_copy_from(struct map_info *map, void *to, unsigned long from,
+- ssize_t len)
+-{
+- memcpy_fromio(to, map->map_priv_1 + from, len);
+-}
+-
+-void uflash_write8(struct map_info *map, __u8 d, unsigned long adr)
+-{
+- __raw_writeb(d, map->map_priv_1 + adr);
+-}
+-
+-void uflash_write16(struct map_info *map, __u16 d, unsigned long adr)
+-{
+- __raw_writew(d, map->map_priv_1 + adr);
+-}
+-
+-void uflash_write32(struct map_info *map, __u32 d, unsigned long adr)
+-{
+- __raw_writel(d, map->map_priv_1 + adr);
+-}
+-
+-void uflash_copy_to(struct map_info *map, unsigned long to, const void *from,
+- ssize_t len)
+-{
+- memcpy_toio(map->map_priv_1 + to, from, len);
+-}
+
+ struct map_info uflash_map_templ = {
+- name: "SUNW,???-????",
+- size: UFLASH_WINDOW_SIZE,
+- buswidth: UFLASH_BUSWIDTH,
+- read8: uflash_read8,
+- read16: uflash_read16,
+- read32: uflash_read32,
+- copy_from: uflash_copy_from,
+- write8: uflash_write8,
+- write16: uflash_write16,
+- write32: uflash_write32,
+- copy_to: uflash_copy_to
++ .name = "SUNW,???-????",
++ .size = UFLASH_WINDOW_SIZE,
++ .buswidth = UFLASH_BUSWIDTH,
+ };
+
+ int uflash_devinit(struct linux_ebus_device* edev)
+@@ -145,20 +96,22 @@
+ if(0 != pdev->name && 0 < strlen(pdev->name)) {
+ pdev->map.name = pdev->name;
+ }
+-
+- pdev->map.map_priv_1 =
++ pdev->phys = edev->resource[0].start;
++ pdev->virt =
+ (unsigned long)ioremap_nocache(edev->resource[0].start, pdev->map.size);
+- if(0 == pdev->map.map_priv_1) {
++ if(0 == pdev->map.virt) {
+ printk("%s: failed to map device\n", __FUNCTION__);
+ kfree(pdev->name);
+ kfree(pdev);
+ return(-1);
+ }
+
++ simple_map_init(&pdev->map);
++
+ /* MTD registration */
+ pdev->mtd = do_map_probe("cfi_probe", &pdev->map);
+ if(0 == pdev->mtd) {
+- iounmap((void *)pdev->map.map_priv_1);
++ iounmap((void *)pdev->map.virt);
+ kfree(pdev->name);
+ kfree(pdev);
+ return(-ENXIO);
+@@ -166,7 +119,7 @@
+
+ list_add(&pdev->list, &device_list);
+
+- pdev->mtd->module = THIS_MODULE;
++ pdev->mtd->owner = THIS_MODULE;
+
+ add_mtd_device(pdev->mtd);
+ return(0);
+@@ -211,9 +164,9 @@
+ del_mtd_device(udev->mtd);
+ map_destroy(udev->mtd);
+ }
+- if(0 != udev->map.map_priv_1) {
+- iounmap((void*)udev->map.map_priv_1);
+- udev->map.map_priv_1 = 0;
++ if(0 != udev->map.virt) {
++ iounmap((void*)udev->map.virt);
++ udev->map.virt = 0;
+ }
+ if(0 != udev->name) {
+ kfree(udev->name);
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/maps/tqm8xxl.c linux/drivers/mtd/maps/tqm8xxl.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/maps/tqm8xxl.c 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/maps/tqm8xxl.c 2004-11-17 18:17:59.140298568 +0100
+@@ -2,7 +2,7 @@
+ * Handle mapping of the flash memory access routines
+ * on TQM8xxL based devices.
+ *
+- * $Id$
++ * $Id$
+ *
+ * based on rpxlite.c
+ *
+@@ -26,6 +26,7 @@
+ #include <linux/module.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
+ #include <asm/io.h>
+
+ #include <linux/mtd/mtd.h>
+@@ -51,46 +52,6 @@
+ static unsigned long num_banks;
+ static unsigned long start_scan_addr;
+
+-__u8 tqm8xxl_read8(struct map_info *map, unsigned long ofs)
+-{
+- return *((__u8 *)(map->map_priv_1 + ofs));
+-}
+-
+-__u16 tqm8xxl_read16(struct map_info *map, unsigned long ofs)
+-{
+- return *((__u16 *)(map->map_priv_1 + ofs));
+-}
+-
+-__u32 tqm8xxl_read32(struct map_info *map, unsigned long ofs)
+-{
+- return *((__u32 *)(map->map_priv_1 + ofs));
+-}
+-
+-void tqm8xxl_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+-{
+- memcpy_fromio(to, (void *)(map->map_priv_1 + from), len);
+-}
+-
+-void tqm8xxl_write8(struct map_info *map, __u8 d, unsigned long adr)
+-{
+- *((__u8 *)(map->map_priv_1 + adr)) = d;
+-}
+-
+-void tqm8xxl_write16(struct map_info *map, __u16 d, unsigned long adr)
+-{
+- *((__u16 *)( map->map_priv_1 + adr)) = d;
+-}
+-
+-void tqm8xxl_write32(struct map_info *map, __u32 d, unsigned long adr)
+-{
+- *((__u32 *)(map->map_priv_1 + adr)) = d;
+-}
+-
+-void tqm8xxl_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+-{
+- memcpy_toio((void *)(map->map_priv_1 + to), from, len);
+-}
+-
+ /*
+ * Here are partition information for all known TQM8xxL series devices.
+ * See include/linux/mtd/partitions.h for definition of the mtd_partition
+@@ -107,50 +68,48 @@
+ static unsigned long tqm8xxl_max_flash_size = 0x00800000;
+
+ /* partition definition for first flash bank
+- * also ref. to "drivers\char\flash_config.c"
++ * (cf. "drivers/char/flash_config.c")
+ */
+ static struct mtd_partition tqm8xxl_partitions[] = {
+ {
+- name: "ppcboot",
+- offset: 0x00000000,
+- size: 0x00020000, /* 128KB */
+- mask_flags: MTD_WRITEABLE, /* force read-only */
++ .name = "ppcboot",
++ .offset = 0x00000000,
++ .size = 0x00020000, /* 128KB */
++ .mask_flags = MTD_WRITEABLE, /* force read-only */
+ },
+ {
+- name: "kernel", /* default kernel image */
+- offset: 0x00020000,
+- size: 0x000e0000,
+- mask_flags: MTD_WRITEABLE, /* force read-only */
++ .name = "kernel", /* default kernel image */
++ .offset = 0x00020000,
++ .size = 0x000e0000,
++ .mask_flags = MTD_WRITEABLE, /* force read-only */
+ },
+ {
+- name: "user",
+- offset: 0x00100000,
+- size: 0x00100000,
++ .name = "user",
++ .offset = 0x00100000,
++ .size = 0x00100000,
+ },
+ {
+- name: "initrd",
+- offset: 0x00200000,
+- size: 0x00200000,
++ .name = "initrd",
++ .offset = 0x00200000,
++ .size = 0x00200000,
+ }
+ };
+-/* partition definition for second flahs bank */
++/* partition definition for second flash bank */
+ static struct mtd_partition tqm8xxl_fs_partitions[] = {
+ {
+- name: "cramfs",
+- offset: 0x00000000,
+- size: 0x00200000,
++ .name = "cramfs",
++ .offset = 0x00000000,
++ .size = 0x00200000,
+ },
+ {
+- name: "jffs",
+- offset: 0x00200000,
+- size: 0x00200000,
+- //size: MTDPART_SIZ_FULL,
++ .name = "jffs",
++ .offset = 0x00200000,
++ .size = 0x00200000,
++ .//size = MTDPART_SIZ_FULL,
+ }
+ };
+ #endif
+
+-#define NB_OF(x) (sizeof(x)/sizeof(x[0]))
+-
+ int __init init_tqm_mtd(void)
+ {
+ int idx = 0, ret = 0;
+@@ -160,67 +119,73 @@
+
+ flash_addr = bd->bi_flashstart;
+ flash_size = bd->bi_flashsize;
+- //request maximum flash size address spzce
++
++ //request maximum flash size address space
+ start_scan_addr = (unsigned long)ioremap(flash_addr, flash_size);
+ if (!start_scan_addr) {
+- //printk("%s:Failed to ioremap address:0x%x\n", __FUNCTION__, FLASH_ADDR);
+- printk("%s:Failed to ioremap address:0x%x\n", __FUNCTION__, flash_addr);
++ printk(KERN_WARNING "%s:Failed to ioremap address:0x%x\n", __FUNCTION__, flash_addr);
+ return -EIO;
+ }
+- for(idx = 0 ; idx < FLASH_BANK_MAX ; idx++)
+- {
++
++ for (idx = 0 ; idx < FLASH_BANK_MAX ; idx++) {
+ if(mtd_size >= flash_size)
+ break;
+
+- printk("%s: chip probing count %d\n", __FUNCTION__, idx);
++ printk(KERN_INFO "%s: chip probing count %d\n", __FUNCTION__, idx);
+
+ map_banks[idx] = (struct map_info *)kmalloc(sizeof(struct map_info), GFP_KERNEL);
+- if(map_banks[idx] == NULL)
+- {
+- //return -ENOMEM;
++ if(map_banks[idx] == NULL) {
+ ret = -ENOMEM;
++ /* FIXME: What if some MTD devices were probed already? */
+ goto error_mem;
+ }
++
+ memset((void *)map_banks[idx], 0, sizeof(struct map_info));
+ map_banks[idx]->name = (char *)kmalloc(16, GFP_KERNEL);
+- if(map_banks[idx]->name == NULL)
+- {
+- //return -ENOMEM;
++
++ if (!map_banks[idx]->name) {
+ ret = -ENOMEM;
++ /* FIXME: What if some MTD devices were probed already? */
+ goto error_mem;
+ }
+- memset((void *)map_banks[idx]->name, 0, 16);
+-
+ sprintf(map_banks[idx]->name, "TQM8xxL%d", idx);
++
+ map_banks[idx]->size = flash_size;
+ map_banks[idx]->buswidth = 4;
+- map_banks[idx]->read8 = tqm8xxl_read8;
+- map_banks[idx]->read16 = tqm8xxl_read16;
+- map_banks[idx]->read32 = tqm8xxl_read32;
+- map_banks[idx]->copy_from = tqm8xxl_copy_from;
+- map_banks[idx]->write8 = tqm8xxl_write8;
+- map_banks[idx]->write16 = tqm8xxl_write16;
+- map_banks[idx]->write32 = tqm8xxl_write32;
+- map_banks[idx]->copy_to = tqm8xxl_copy_to;
++
++ simple_map_init(map_banks[idx]);
++
++ map_banks[idx]->virt = start_scan_addr;
++ map_banks[idx]->phys = flash_addr;
++ /* FIXME: This looks utterly bogus, but I'm trying to
++ preserve the behaviour of the original (shown here)...
++
+ map_banks[idx]->map_priv_1 =
+ start_scan_addr + ((idx > 0) ?
+ (mtd_banks[idx-1] ? mtd_banks[idx-1]->size : 0) : 0);
++ */
++
++ if (idx && mtd_banks[idx-1]) {
++ map_banks[idx]->virt += mtd_banks[idx-1]->size;
++ map_banks[idx]->phys += mtd_banks[idx-1]->size;
++ }
++
+ //start to probe flash chips
+ mtd_banks[idx] = do_map_probe("cfi_probe", map_banks[idx]);
+- if(mtd_banks[idx])
+- {
+- mtd_banks[idx]->module = THIS_MODULE;
++
++ if (mtd_banks[idx]) {
++ mtd_banks[idx]->owner = THIS_MODULE;
+ mtd_size += mtd_banks[idx]->size;
+ num_banks++;
+- printk("%s: bank%d, name:%s, size:%dbytes \n", __FUNCTION__, num_banks,
++
++ printk(KERN_INFO "%s: bank%d, name:%s, size:%dbytes \n", __FUNCTION__, num_banks,
+ mtd_banks[idx]->name, mtd_banks[idx]->size);
+ }
+ }
+
+ /* no supported flash chips found */
+- if(!num_banks)
+- {
+- printk("TQM8xxL: No support flash chips found!\n");
++ if (!num_banks) {
++ printk(KERN_NOTICE "TQM8xxL: No support flash chips found!\n");
+ ret = -ENXIO;
+ goto error_mem;
+ }
+@@ -231,12 +196,13 @@
+ */
+ part_banks[0].mtd_part = tqm8xxl_partitions;
+ part_banks[0].type = "Static image";
+- part_banks[0].nums = NB_OF(tqm8xxl_partitions);
++ part_banks[0].nums = ARRAY_SIZE(tqm8xxl_partitions);
++
+ part_banks[1].mtd_part = tqm8xxl_fs_partitions;
+ part_banks[1].type = "Static file system";
+- part_banks[1].nums = NB_OF(tqm8xxl_fs_partitions);
+- for(idx = 0; idx < num_banks ; idx++)
+- {
++ part_banks[1].nums = ARRAY_SIZE(tqm8xxl_fs_partitions);
++
++ for(idx = 0; idx < num_banks ; idx++) {
+ if (part_banks[idx].nums == 0) {
+ printk(KERN_NOTICE "TQM flash%d: no partition info available, registering whole flash at once\n", idx);
+ add_mtd_device(mtd_banks[idx]);
+@@ -254,12 +220,9 @@
+ #endif
+ return 0;
+ error_mem:
+- for(idx = 0 ; idx < FLASH_BANK_MAX ; idx++)
+- {
+- if(map_banks[idx] != NULL)
+- {
+- if(map_banks[idx]->name != NULL)
+- {
++ for(idx = 0 ; idx < FLASH_BANK_MAX ; idx++) {
++ if(map_banks[idx] != NULL) {
++ if(map_banks[idx]->name != NULL) {
+ kfree(map_banks[idx]->name);
+ map_banks[idx]->name = NULL;
+ }
+@@ -267,18 +230,15 @@
+ map_banks[idx] = NULL;
+ }
+ }
+- //return -ENOMEM;
+ error:
+ iounmap((void *)start_scan_addr);
+- //return -ENXIO;
+ return ret;
+ }
+
+ static void __exit cleanup_tqm_mtd(void)
+ {
+ unsigned int idx = 0;
+- for(idx = 0 ; idx < num_banks ; idx++)
+- {
++ for(idx = 0 ; idx < num_banks ; idx++) {
+ /* destroy mtd_info previously allocated */
+ if (mtd_banks[idx]) {
+ del_mtd_partitions(mtd_banks[idx]);
+@@ -288,6 +248,7 @@
+ kfree(map_banks[idx]->name);
+ kfree(map_banks[idx]);
+ }
++
+ if (start_scan_addr) {
+ iounmap((void *)start_scan_addr);
+ start_scan_addr = 0;
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/maps/tsunami_flash.c linux/drivers/mtd/maps/tsunami_flash.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/maps/tsunami_flash.c 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/maps/tsunami_flash.c 2004-11-17 18:17:59.141298416 +0100
+@@ -2,11 +2,13 @@
+ * tsunami_flash.c
+ *
+ * flash chip on alpha ds10...
+- * $Id$
++ * $Id$
+ */
+ #include <asm/io.h>
+ #include <asm/core_tsunami.h>
++#include <linux/init.h>
+ #include <linux/mtd/map.h>
++#include <linux/mtd/mtd.h>
+
+ #define FLASH_ENABLE_PORT 0x00C00001
+ #define FLASH_ENABLE_BYTE 0x01
+@@ -58,18 +60,12 @@
+ static struct map_info tsunami_flash_map = {
+ .name = "flash chip on the Tsunami TIG bus",
+ .size = MAX_TIG_FLASH_SIZE,
++ .phys = NO_XIP;
+ .buswidth = 1,
+ .read8 = tsunami_flash_read8,
+- .read16 = 0,
+- .read32 = 0,
+ .copy_from = tsunami_flash_copy_from,
+ .write8 = tsunami_flash_write8,
+- .write16 = 0,
+- .write32 = 0,
+ .copy_to = tsunami_flash_copy_to,
+- .set_vpp = 0,
+- .map_priv_1 = 0,
+-
+ };
+
+ static struct mtd_info *tsunami_flash_mtd;
+@@ -99,7 +95,7 @@
+ tsunami_flash_mtd = do_map_probe(*type, &tsunami_flash_map);
+ }
+ if (tsunami_flash_mtd) {
+- tsunami_flash_mtd->module = THIS_MODULE;
++ tsunami_flash_mtd->owner = THIS_MODULE;
+ add_mtd_device(tsunami_flash_mtd);
+ return 0;
+ }
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/maps/uclinux.c linux/drivers/mtd/maps/uclinux.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/maps/uclinux.c 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/maps/uclinux.c 2004-11-17 18:17:59.142298264 +0100
+@@ -5,7 +5,7 @@
+ *
+ * (C) Copyright 2002, Greg Ungerer (gerg@snapgear.com)
+ *
+- * $Id$
++ * $Id$
+ */
+
+ /****************************************************************************/
+@@ -24,58 +24,11 @@
+
+ /****************************************************************************/
+
+-__u8 uclinux_read8(struct map_info *map, unsigned long ofs)
+-{
+- return(*((__u8 *) (map->map_priv_1 + ofs)));
+-}
+-
+-__u16 uclinux_read16(struct map_info *map, unsigned long ofs)
+-{
+- return(*((__u16 *) (map->map_priv_1 + ofs)));
+-}
+-
+-__u32 uclinux_read32(struct map_info *map, unsigned long ofs)
+-{
+- return(*((__u32 *) (map->map_priv_1 + ofs)));
+-}
+-
+-void uclinux_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+-{
+- memcpy(to, (void *)(map->map_priv_1 + from), len);
+-}
+-
+-void uclinux_write8(struct map_info *map, __u8 d, unsigned long adr)
+-{
+- *((__u8 *) (map->map_priv_1 + adr)) = d;
+-}
+-
+-void uclinux_write16(struct map_info *map, __u16 d, unsigned long adr)
+-{
+- *((__u16 *) (map->map_priv_1 + adr)) = d;
+-}
+-
+-void uclinux_write32(struct map_info *map, __u32 d, unsigned long adr)
+-{
+- *((__u32 *) (map->map_priv_1 + adr)) = d;
+-}
+-
+-void uclinux_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+-{
+- memcpy((void *) (map->map_priv_1 + to), from, len);
+-}
+
+ /****************************************************************************/
+
+ struct map_info uclinux_ram_map = {
+- name: "RAM",
+- read8: uclinux_read8,
+- read16: uclinux_read16,
+- read32: uclinux_read32,
+- copy_from: uclinux_copy_from,
+- write8: uclinux_write8,
+- write16: uclinux_write16,
+- write32: uclinux_write32,
+- copy_to: uclinux_copy_to,
++ .name = "RAM",
+ };
+
+ struct mtd_info *uclinux_ram_mtdinfo;
+@@ -83,7 +36,7 @@
+ /****************************************************************************/
+
+ struct mtd_partition uclinux_romfs[] = {
+- { name: "ROMfs", offset: 0 }
++ { .name = "ROMfs" }
+ };
+
+ #define NUM_PARTITIONS (sizeof(uclinux_romfs) / sizeof(uclinux_romfs[0]))
+@@ -94,7 +47,7 @@
+ size_t *retlen, u_char **mtdbuf)
+ {
+ struct map_info *map = (struct map_info *) mtd->priv;
+- *mtdbuf = (u_char *) (map->map_priv_1 + ((int) from));
++ *mtdbuf = (u_char *) (map->virt + ((int) from));
+ *retlen = len;
+ return(0);
+ }
+@@ -108,29 +61,31 @@
+ extern char _ebss;
+
+ mapp = &uclinux_ram_map;
+- mapp->map_priv_2 = (unsigned long) &_ebss;
++ mapp->phys = (unsigned long) &_ebss;
+ mapp->size = PAGE_ALIGN(*((unsigned long *)((&_ebss) + 8)));
+ mapp->buswidth = 4;
+
+ printk("uclinux[mtd]: RAM probe address=0x%x size=0x%x\n",
+ (int) mapp->map_priv_2, (int) mapp->size);
+
+- mapp->map_priv_1 = (unsigned long)
+- ioremap_nocache(mapp->map_priv_2, mapp->size);
++ mapp->virt = (unsigned long)
++ ioremap_nocache(mapp->phys, mapp->size);
+
+- if (mapp->map_priv_1 == 0) {
++ if (mapp->virt == 0) {
+ printk("uclinux[mtd]: ioremap_nocache() failed\n");
+ return(-EIO);
+ }
+
++ simple_map_init(mapp);
++
+ mtd = do_map_probe("map_ram", mapp);
+ if (!mtd) {
+ printk("uclinux[mtd]: failed to find a mapping?\n");
+- iounmap((void *) mapp->map_priv_1);
++ iounmap((void *) mapp->virt);
+ return(-ENXIO);
+ }
+
+- mtd->module = THIS_MODULE;
++ mtd->owner = THIS_MODULE;
+ mtd->point = uclinux_point;
+ mtd->priv = mapp;
+
+@@ -155,8 +110,8 @@
+ uclinux_ram_mtdinfo = NULL;
+ }
+ if (uclinux_ram_map.map_priv_1) {
+- iounmap((void *) uclinux_ram_map.map_priv_1);
+- uclinux_ram_map.map_priv_1 = 0;
++ iounmap((void *) uclinux_ram_map.virt);
++ uclinux_ram_map.virt = 0;
+ }
+ }
+
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/maps/vmax301.c linux/drivers/mtd/maps/vmax301.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/maps/vmax301.c 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/maps/vmax301.c 2004-11-17 18:17:59.144297960 +0100
+@@ -1,4 +1,4 @@
+-// $Id$
++// $Id$
+ /* ######################################################################
+
+ Tempustech VMAX SBC301 MTD Driver.
+@@ -24,6 +24,7 @@
+ #include <asm/io.h>
+
+ #include <linux/mtd/map.h>
++#include <linux/mtd/mtd.h>
+
+
+ #define WINDOW_START 0xd8000
+@@ -142,34 +143,36 @@
+
+ static struct map_info vmax_map[2] = {
+ {
+- name: "VMAX301 Internal Flash",
+- size: 3*2*1024*1024,
+- buswidth: 1,
+- read8: vmax301_read8,
+- read16: vmax301_read16,
+- read32: vmax301_read32,
+- copy_from: vmax301_copy_from,
+- write8: vmax301_write8,
+- write16: vmax301_write16,
+- write32: vmax301_write32,
+- copy_to: vmax301_copy_to,
+- map_priv_1: WINDOW_START + WINDOW_LENGTH,
+- map_priv_2: 0xFFFFFFFF
++ .name = "VMAX301 Internal Flash",
++ .phys = NO_XIP,
++ .size = 3*2*1024*1024,
++ .buswidth = 1,
++ .read8 = vmax301_read8,
++ .read16 = vmax301_read16,
++ .read32 = vmax301_read32,
++ .copy_from = vmax301_copy_from,
++ .write8 = vmax301_write8,
++ .write16 = vmax301_write16,
++ .write32 = vmax301_write32,
++ .copy_to = vmax301_copy_to,
++ .map_priv_1 = WINDOW_START + WINDOW_LENGTH,
++ .map_priv_2 = 0xFFFFFFFF
+ },
+ {
+- name: "VMAX301 Socket",
+- size: 0,
+- buswidth: 1,
+- read8: vmax301_read8,
+- read16: vmax301_read16,
+- read32: vmax301_read32,
+- copy_from: vmax301_copy_from,
+- write8: vmax301_write8,
+- write16: vmax301_write16,
+- write32: vmax301_write32,
+- copy_to: vmax301_copy_to,
+- map_priv_1: WINDOW_START + (3*WINDOW_LENGTH),
+- map_priv_2: 0xFFFFFFFF
++ .name = "VMAX301 Socket",
++ .phys = NO_XIP,
++ .size = 0,
++ .buswidth = 1,
++ .read8 = vmax301_read8,
++ .read16 = vmax301_read16,
++ .read32 = vmax301_read32,
++ .copy_from = vmax301_copy_from,
++ .write8 = vmax301_write8,
++ .write16 = vmax301_write16,
++ .write32 = vmax301_write32,
++ .copy_to = vmax301_copy_to,
++ .map_priv_1 = WINDOW_START + (3*WINDOW_LENGTH),
++ .map_priv_2 = 0xFFFFFFFF
+ }
+ };
+
+@@ -206,8 +209,8 @@
+ address of the first half, because it's used more
+ often.
+ */
+- vmax_map[0].map_priv_1 = iomapadr + WINDOW_START;
+- vmax_map[1].map_priv_1 = iomapadr + (3*WINDOW_START);
++ vmax_map[0].map_priv_2 = iomapadr + WINDOW_START;
++ vmax_map[1].map_priv_2 = iomapadr + (3*WINDOW_START);
+
+ for (i=0; i<2; i++) {
+ vmax_mtd[i] = do_map_probe("cfi_probe", &vmax_map[i]);
+@@ -218,7 +221,7 @@
+ if (!vmax_mtd[i])
+ vmax_mtd[i] = do_map_probe("map_rom", &vmax_map[i]);
+ if (vmax_mtd[i]) {
+- vmax_mtd[i]->module = THIS_MODULE;
++ vmax_mtd[i]->owner = THIS_MODULE;
+ add_mtd_device(vmax_mtd[i]);
+ }
+ }
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/mtd_blkdevs-24.c linux/drivers/mtd/mtd_blkdevs-24.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/mtd_blkdevs-24.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/mtd/mtd_blkdevs-24.c 2004-11-17 18:17:58.000000000 +0100
+@@ -0,0 +1,699 @@
++/*
++ * $Id$
++ *
++ * (C) 2003 David Woodhouse <dwmw2@infradead.org>
++ *
++ * Interface to Linux 2.4 block layer for MTD 'translation layers'.
++ *
++ */
++
++#include <linux/kernel.h>
++#include <linux/slab.h>
++#include <linux/module.h>
++#include <linux/list.h>
++#include <linux/fs.h>
++#include <linux/mtd/blktrans.h>
++#include <linux/mtd/mtd.h>
++#include <linux/blkdev.h>
++#include <linux/blk.h>
++#include <linux/blkpg.h>
++#include <linux/spinlock.h>
++#include <linux/hdreg.h>
++#include <linux/init.h>
++#include <asm/semaphore.h>
++#include <asm/uaccess.h>
++
++static LIST_HEAD(blktrans_majors);
++
++extern struct semaphore mtd_table_mutex;
++extern struct mtd_info *mtd_table[];
++
++struct mtd_blkcore_priv {
++ devfs_handle_t devfs_dir_handle;
++ int blksizes[256];
++ int sizes[256];
++ struct hd_struct part_table[256];
++ struct gendisk gd;
++ spinlock_t devs_lock; /* See comment in _request function */
++ struct completion thread_dead;
++ int exiting;
++ wait_queue_head_t thread_wq;
++};
++
++static inline struct mtd_blktrans_dev *tr_get_dev(struct mtd_blktrans_ops *tr,
++ int devnum)
++{
++ struct list_head *this;
++ struct mtd_blktrans_dev *d;
++
++ list_for_each(this, &tr->devs) {
++ d = list_entry(this, struct mtd_blktrans_dev, list);
++
++ if (d->devnum == devnum)
++ return d;
++ }
++ return NULL;
++}
++
++static inline struct mtd_blktrans_ops *get_tr(int major)
++{
++ struct list_head *this;
++ struct mtd_blktrans_ops *t;
++
++ list_for_each(this, &blktrans_majors) {
++ t = list_entry(this, struct mtd_blktrans_ops, list);
++
++ if (t->major == major)
++ return t;
++ }
++ return NULL;
++}
++
++static int do_blktrans_request(struct mtd_blktrans_ops *tr,
++ struct mtd_blktrans_dev *dev,
++ struct request *req)
++{
++ unsigned long block, nsect;
++ char *buf;
++ int minor;
++
++ minor = MINOR(req->rq_dev);
++ block = req->sector;
++ nsect = req->current_nr_sectors;
++ buf = req->buffer;
++
++ if (block + nsect > tr->blkcore_priv->part_table[minor].nr_sects) {
++ printk(KERN_WARNING "Access beyond end of device.\n");
++ return 0;
++ }
++ block += tr->blkcore_priv->part_table[minor].start_sect;
++
++ switch(req->cmd) {
++ case READ:
++ for (; nsect > 0; nsect--, block++, buf += 512)
++ if (tr->readsect(dev, block, buf))
++ return 0;
++ return 1;
++
++ case WRITE:
++ if (!tr->writesect)
++ return 0;
++
++ for (; nsect > 0; nsect--, block++, buf += 512)
++ if (tr->writesect(dev, block, buf))
++ return 0;
++ return 1;
++
++ default:
++ printk(KERN_NOTICE "Unknown request cmd %d\n", req->cmd);
++ return 0;
++ }
++}
++
++static int mtd_blktrans_thread(void *arg)
++{
++ struct mtd_blktrans_ops *tr = arg;
++ struct request_queue *rq = BLK_DEFAULT_QUEUE(tr->major);
++
++ /* we might get involved when memory gets low, so use PF_MEMALLOC */
++ current->flags |= PF_MEMALLOC;
++
++ snprintf(current->comm, sizeof(current->comm), "%sd", tr->name);
++
++ /* daemonize() doesn't do this for us since some kernel threads
++ actually want to deal with signals. We can't just call
++ exit_sighand() since that'll cause an oops when we finally
++ do exit. */
++
++#ifndef __rh_config_h__ /* HAVE_NPTL */
++ spin_lock_irq(&current->sigmask_lock);
++ sigfillset(&current->blocked);
++ recalc_sigpending(current);
++ spin_unlock_irq(&current->sigmask_lock);
++#else
++ spin_lock_irq(&current->sighand->siglock);
++ sigfillset(&current->blocked);
++ recalc_sigpending();
++ spin_unlock_irq(&current->sighand->siglock);
++#endif
++ daemonize();
++
++ while (!tr->blkcore_priv->exiting) {
++ struct request *req;
++ struct mtd_blktrans_dev *dev;
++ int devnum;
++ int res = 0;
++ DECLARE_WAITQUEUE(wait, current);
++
++ spin_lock_irq(&io_request_lock);
++
++ if (list_empty(&rq->queue_head)) {
++
++ add_wait_queue(&tr->blkcore_priv->thread_wq, &wait);
++ set_current_state(TASK_INTERRUPTIBLE);
++
++ spin_unlock_irq(&io_request_lock);
++
++ schedule();
++ remove_wait_queue(&tr->blkcore_priv->thread_wq, &wait);
++
++ continue;
++ }
++
++ req = blkdev_entry_next_request(&rq->queue_head);
++
++ devnum = MINOR(req->rq_dev) >> tr->part_bits;
++
++ /* The ll_rw_blk code knows not to touch the request
++ at the head of the queue */
++ spin_unlock_irq(&io_request_lock);
++
++ /* FIXME: Where can we store the dev, on which
++ we already have a refcount anyway? We need to
++ lock against concurrent addition/removal of devices,
++ but if we use the mtd_table_mutex we deadlock when
++ grok_partitions is called from the registration
++ callbacks. */
++ spin_lock(&tr->blkcore_priv->devs_lock);
++ dev = tr_get_dev(tr, devnum);
++ spin_unlock(&tr->blkcore_priv->devs_lock);
++
++ BUG_ON(!dev);
++
++ /* Ensure serialisation of requests */
++ down(&dev->sem);
++
++ res = do_blktrans_request(tr, dev, req);
++ up(&dev->sem);
++
++ if (!end_that_request_first(req, res, tr->name)) {
++ spin_lock_irq(&io_request_lock);
++ blkdev_dequeue_request(req);
++ end_that_request_last(req);
++ spin_unlock_irq(&io_request_lock);
++ }
++ }
++ complete_and_exit(&tr->blkcore_priv->thread_dead, 0);
++}
++
++static void mtd_blktrans_request(struct request_queue *rq)
++{
++ struct mtd_blktrans_ops *tr = rq->queuedata;
++ wake_up(&tr->blkcore_priv->thread_wq);
++}
++
++int blktrans_open(struct inode *i, struct file *f)
++{
++ struct mtd_blktrans_ops *tr = NULL;
++ struct mtd_blktrans_dev *dev = NULL;
++ int major_nr = MAJOR(i->i_rdev);
++ int minor_nr = MINOR(i->i_rdev);
++ int devnum;
++ int ret = -ENODEV;
++
++ if (is_read_only(i->i_rdev) && (f->f_mode & FMODE_WRITE))
++ return -EROFS;
++
++ down(&mtd_table_mutex);
++
++ tr = get_tr(major_nr);
++
++ if (!tr)
++ goto out;
++
++ devnum = minor_nr >> tr->part_bits;
++
++ dev = tr_get_dev(tr, devnum);
++
++ if (!dev)
++ goto out;
++
++ if (!tr->blkcore_priv->part_table[minor_nr].nr_sects) {
++ ret = -ENODEV;
++ goto out;
++ }
++
++ if (!try_inc_mod_count(dev->mtd->owner))
++ goto out;
++
++ if (!try_inc_mod_count(tr->owner))
++ goto out_tr;
++
++ dev->mtd->usecount++;
++
++ ret = 0;
++ if (tr->open && (ret = tr->open(dev))) {
++ dev->mtd->usecount--;
++ if (dev->mtd->owner)
++ __MOD_DEC_USE_COUNT(dev->mtd->owner);
++ out_tr:
++ if (tr->owner)
++ __MOD_DEC_USE_COUNT(tr->owner);
++ }
++ out:
++ up(&mtd_table_mutex);
++
++ return ret;
++}
++
++int blktrans_release(struct inode *i, struct file *f)
++{
++ struct mtd_blktrans_dev *dev;
++ struct mtd_blktrans_ops *tr;
++ int ret = 0;
++ int devnum;
++
++ down(&mtd_table_mutex);
++
++ tr = get_tr(MAJOR(i->i_rdev));
++ if (!tr) {
++ up(&mtd_table_mutex);
++ return -ENODEV;
++ }
++
++ devnum = MINOR(i->i_rdev) >> tr->part_bits;
++ dev = tr_get_dev(tr, devnum);
++
++ if (!dev) {
++ up(&mtd_table_mutex);
++ return -ENODEV;
++ }
++
++ if (tr->release)
++ ret = tr->release(dev);
++
++ if (!ret) {
++ dev->mtd->usecount--;
++ if (dev->mtd->owner)
++ __MOD_DEC_USE_COUNT(dev->mtd->owner);
++ if (tr->owner)
++ __MOD_DEC_USE_COUNT(tr->owner);
++ }
++
++ up(&mtd_table_mutex);
++
++ return ret;
++}
++
++static int mtd_blktrans_rrpart(kdev_t rdev, struct mtd_blktrans_ops *tr,
++ struct mtd_blktrans_dev *dev)
++{
++ struct gendisk *gd = &(tr->blkcore_priv->gd);
++ int i;
++ int minor = MINOR(rdev);
++
++ if (minor & ((1<<tr->part_bits)-1) || !tr->part_bits) {
++ /* BLKRRPART on a partition. Go away. */
++ return -ENOTTY;
++ }
++
++ if (!capable(CAP_SYS_ADMIN))
++ return -EACCES;
++
++ /* We are required to prevent simultaneous open() ourselves.
++ The core doesn't do that for us. Did I ever mention how
++ much the Linux block layer sucks? Sledgehammer approach... */
++ down(&mtd_table_mutex);
++
++ for (i=0; i < (1<<tr->part_bits); i++) {
++ invalidate_device(MKDEV(tr->major, minor+i), 1);
++ gd->part[minor + i].start_sect = 0;
++ gd->part[minor + i].nr_sects = 0;
++ }
++
++ grok_partitions(gd, minor, 1 << tr->part_bits,
++ tr->blkcore_priv->sizes[minor]);
++ up(&mtd_table_mutex);
++
++ return 0;
++}
++
++static int blktrans_ioctl(struct inode *inode, struct file *file,
++ unsigned int cmd, unsigned long arg)
++{
++ struct mtd_blktrans_dev *dev;
++ struct mtd_blktrans_ops *tr;
++ int devnum;
++
++ switch(cmd) {
++ case BLKGETSIZE:
++ case BLKGETSIZE64:
++ case BLKBSZSET:
++ case BLKBSZGET:
++ case BLKROSET:
++ case BLKROGET:
++ case BLKRASET:
++ case BLKRAGET:
++ case BLKPG:
++ case BLKELVGET:
++ case BLKELVSET:
++ return blk_ioctl(inode->i_rdev, cmd, arg);
++ }
++
++ down(&mtd_table_mutex);
++
++ tr = get_tr(MAJOR(inode->i_rdev));
++ if (!tr) {
++ up(&mtd_table_mutex);
++ return -ENODEV;
++ }
++
++ devnum = MINOR(inode->i_rdev) >> tr->part_bits;
++ dev = tr_get_dev(tr, devnum);
++
++ up(&mtd_table_mutex);
++
++ if (!dev)
++ return -ENODEV;
++
++ switch(cmd) {
++ case BLKRRPART:
++ return mtd_blktrans_rrpart(inode->i_rdev, tr, dev);
++
++ case BLKFLSBUF:
++ blk_ioctl(inode->i_rdev, cmd, arg);
++ if (tr->flush)
++ return tr->flush(dev);
++ /* The core code did the work, we had nothing to do. */
++ return 0;
++
++ case HDIO_GETGEO:
++ if (tr->getgeo) {
++ struct hd_geometry g;
++ struct gendisk *gd = &(tr->blkcore_priv->gd);
++ int ret;
++
++ memset(&g, 0, sizeof(g));
++ ret = tr->getgeo(dev, &g);
++ if (ret)
++ return ret;
++
++ g.start = gd->part[MINOR(inode->i_rdev)].start_sect;
++ if (copy_to_user((void *)arg, &g, sizeof(g)))
++ return -EFAULT;
++ return 0;
++ } /* else */
++ default:
++ return -ENOTTY;
++ }
++}
++
++struct block_device_operations mtd_blktrans_ops = {
++ .owner = THIS_MODULE,
++ .open = blktrans_open,
++ .release = blktrans_release,
++ .ioctl = blktrans_ioctl,
++};
++
++int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new)
++{
++ struct mtd_blktrans_ops *tr = new->tr;
++ struct list_head *this;
++ int last_devnum = -1;
++ int i;
++
++ if (!down_trylock(&mtd_table_mutex)) {
++ up(&mtd_table_mutex);
++ BUG();
++ }
++
++ spin_lock(&tr->blkcore_priv->devs_lock);
++
++ list_for_each(this, &tr->devs) {
++ struct mtd_blktrans_dev *d = list_entry(this, struct mtd_blktrans_dev, list);
++ if (new->devnum == -1) {
++ /* Use first free number */
++ if (d->devnum != last_devnum+1) {
++ /* Found a free devnum. Plug it in here */
++ new->devnum = last_devnum+1;
++ list_add_tail(&new->list, &d->list);
++ goto added;
++ }
++ } else if (d->devnum == new->devnum) {
++ /* Required number taken */
++ spin_unlock(&tr->blkcore_priv->devs_lock);
++ return -EBUSY;
++ } else if (d->devnum > new->devnum) {
++ /* Required number was free */
++ list_add_tail(&new->list, &d->list);
++ goto added;
++ }
++ last_devnum = d->devnum;
++ }
++ if (new->devnum == -1)
++ new->devnum = last_devnum+1;
++
++ if ((new->devnum << tr->part_bits) > 256) {
++ spin_unlock(&tr->blkcore_priv->devs_lock);
++ return -EBUSY;
++ }
++
++ init_MUTEX(&new->sem);
++ list_add_tail(&new->list, &tr->devs);
++ added:
++ spin_unlock(&tr->blkcore_priv->devs_lock);
++
++ if (!tr->writesect)
++ new->readonly = 1;
++
++ for (i = new->devnum << tr->part_bits;
++ i < (new->devnum+1) << tr->part_bits;
++ i++) {
++ set_device_ro(MKDEV(tr->major, i), new->readonly);
++ tr->blkcore_priv->blksizes[i] = new->blksize;
++ tr->blkcore_priv->sizes[i] = 0;
++ tr->blkcore_priv->part_table[i].nr_sects = 0;
++ tr->blkcore_priv->part_table[i].start_sect = 0;
++ }
++
++ /*
++ <viro_zzz> dwmw2: BLOCK_SIZE_BITS has nothing to do with block devices
++ <viro> dwmw2: any code which sets blk_size[][] should be
++ size >> 10 /+ 2.4 and its dumb units */
++
++ tr->blkcore_priv->sizes[new->devnum << tr->part_bits] =
++ (new->size * new->blksize) >> 10; /* 2.4 and its dumb units */
++
++ /* But this is still in device's sectors? $DEITY knows */
++ tr->blkcore_priv->part_table[new->devnum << tr->part_bits].nr_sects = new->size;
++
++ if (tr->part_bits) {
++ grok_partitions(&tr->blkcore_priv->gd, new->devnum,
++ 1 << tr->part_bits, new->size);
++ }
++#ifdef CONFIG_DEVFS_FS
++ if (!tr->part_bits) {
++ char name[2];
++
++ name[0] = '0' + new->devnum;
++ name[1] = 0;
++
++ new->blkcore_priv =
++ devfs_register(tr->blkcore_priv->devfs_dir_handle,
++ name, DEVFS_FL_DEFAULT, tr->major,
++ new->devnum, S_IFBLK|S_IRUGO|S_IWUGO,
++ &mtd_blktrans_ops, NULL);
++ }
++#endif
++ return 0;
++}
++
++int del_mtd_blktrans_dev(struct mtd_blktrans_dev *old)
++{
++ struct mtd_blktrans_ops *tr = old->tr;
++ int i;
++
++ if (!down_trylock(&mtd_table_mutex)) {
++ up(&mtd_table_mutex);
++ BUG();
++ }
++
++#ifdef CONFIG_DEVFS_FS
++ if (!tr->part_bits) {
++ devfs_unregister(old->blkcore_priv);
++ old->blkcore_priv = NULL;
++ } else {
++ devfs_register_partitions(&tr->blkcore_priv->gd,
++ old->devnum << tr->part_bits, 1);
++ }
++#endif
++ spin_lock(&tr->blkcore_priv->devs_lock);
++ list_del(&old->list);
++ spin_unlock(&tr->blkcore_priv->devs_lock);
++
++ for (i = (old->devnum << tr->part_bits);
++ i < ((old->devnum+1) << tr->part_bits); i++) {
++ tr->blkcore_priv->sizes[i] = 0;
++ tr->blkcore_priv->part_table[i].nr_sects = 0;
++ tr->blkcore_priv->part_table[i].start_sect = 0;
++ }
++
++ return 0;
++}
++
++void blktrans_notify_remove(struct mtd_info *mtd)
++{
++ struct list_head *this, *this2, *next;
++
++ list_for_each(this, &blktrans_majors) {
++ struct mtd_blktrans_ops *tr = list_entry(this, struct mtd_blktrans_ops, list);
++
++ list_for_each_safe(this2, next, &tr->devs) {
++ struct mtd_blktrans_dev *dev = list_entry(this2, struct mtd_blktrans_dev, list);
++
++ if (dev->mtd == mtd)
++ tr->remove_dev(dev);
++ }
++ }
++}
++
++void blktrans_notify_add(struct mtd_info *mtd)
++{
++ struct list_head *this;
++
++ if (mtd->type == MTD_ABSENT)
++ return;
++
++ list_for_each(this, &blktrans_majors) {
++ struct mtd_blktrans_ops *tr = list_entry(this, struct mtd_blktrans_ops, list);
++
++ tr->add_mtd(tr, mtd);
++ }
++
++}
++
++static struct mtd_notifier blktrans_notifier = {
++ .add = blktrans_notify_add,
++ .remove = blktrans_notify_remove,
++};
++
++int register_mtd_blktrans(struct mtd_blktrans_ops *tr)
++{
++ int ret, i;
++
++ /* Register the notifier if/when the first device type is
++ registered, to prevent the link/init ordering from fucking
++ us over. */
++ if (!blktrans_notifier.list.next)
++ register_mtd_user(&blktrans_notifier);
++
++ tr->blkcore_priv = kmalloc(sizeof(*tr->blkcore_priv), GFP_KERNEL);
++ if (!tr->blkcore_priv)
++ return -ENOMEM;
++
++ memset(tr->blkcore_priv, 0, sizeof(*tr->blkcore_priv));
++
++ down(&mtd_table_mutex);
++
++ ret = devfs_register_blkdev(tr->major, tr->name, &mtd_blktrans_ops);
++ if (ret) {
++ printk(KERN_WARNING "Unable to register %s block device on major %d: %d\n",
++ tr->name, tr->major, ret);
++ kfree(tr->blkcore_priv);
++ up(&mtd_table_mutex);
++ return ret;
++ }
++
++ blk_init_queue(BLK_DEFAULT_QUEUE(tr->major), &mtd_blktrans_request);
++ (BLK_DEFAULT_QUEUE(tr->major))->queuedata = tr;
++
++ init_completion(&tr->blkcore_priv->thread_dead);
++ init_waitqueue_head(&tr->blkcore_priv->thread_wq);
++
++ ret = kernel_thread(mtd_blktrans_thread, tr,
++ CLONE_FS|CLONE_FILES|CLONE_SIGHAND);
++ if (ret < 0) {
++ blk_cleanup_queue(BLK_DEFAULT_QUEUE(tr->major));
++ devfs_unregister_blkdev(tr->major, tr->name);
++ kfree(tr->blkcore_priv);
++ up(&mtd_table_mutex);
++ return ret;
++ }
++
++ tr->blkcore_priv->devfs_dir_handle =
++ devfs_mk_dir(NULL, tr->name, NULL);
++
++ blksize_size[tr->major] = tr->blkcore_priv->blksizes;
++ blk_size[tr->major] = tr->blkcore_priv->sizes;
++
++ tr->blkcore_priv->gd.major = tr->major;
++ tr->blkcore_priv->gd.major_name = tr->name;
++ tr->blkcore_priv->gd.minor_shift = tr->part_bits;
++ tr->blkcore_priv->gd.max_p = (1<<tr->part_bits) - 1;
++ tr->blkcore_priv->gd.part = tr->blkcore_priv->part_table;
++ tr->blkcore_priv->gd.sizes = tr->blkcore_priv->sizes;
++ tr->blkcore_priv->gd.nr_real = 256 >> tr->part_bits;
++
++ spin_lock_init(&tr->blkcore_priv->devs_lock);
++
++ add_gendisk(&tr->blkcore_priv->gd);
++
++ INIT_LIST_HEAD(&tr->devs);
++ list_add(&tr->list, &blktrans_majors);
++
++ for (i=0; i<MAX_MTD_DEVICES; i++) {
++ if (mtd_table[i] && mtd_table[i]->type != MTD_ABSENT)
++ tr->add_mtd(tr, mtd_table[i]);
++ }
++ up(&mtd_table_mutex);
++
++ return 0;
++}
++
++int deregister_mtd_blktrans(struct mtd_blktrans_ops *tr)
++{
++ struct list_head *this, *next;
++
++ down(&mtd_table_mutex);
++
++ /* Clean up the kernel thread */
++ tr->blkcore_priv->exiting = 1;
++ wake_up(&tr->blkcore_priv->thread_wq);
++ wait_for_completion(&tr->blkcore_priv->thread_dead);
++
++ /* Remove it from the list of active majors */
++ list_del(&tr->list);
++
++ /* Remove each of its devices */
++ list_for_each_safe(this, next, &tr->devs) {
++ struct mtd_blktrans_dev *dev = list_entry(this, struct mtd_blktrans_dev, list);
++ tr->remove_dev(dev);
++ }
++
++ blksize_size[tr->major] = NULL;
++ blk_size[tr->major] = NULL;
++
++ del_gendisk(&tr->blkcore_priv->gd);
++
++ blk_cleanup_queue(BLK_DEFAULT_QUEUE(tr->major));
++ devfs_unregister_blkdev(tr->major, tr->name);
++
++ devfs_unregister(tr->blkcore_priv->devfs_dir_handle);
++
++ up(&mtd_table_mutex);
++
++ kfree(tr->blkcore_priv);
++
++ if (!list_empty(&tr->devs))
++ BUG();
++ return 0;
++}
++
++static void __exit mtd_blktrans_exit(void)
++{
++ /* No race here -- if someone's currently in register_mtd_blktrans
++ we're screwed anyway. */
++ if (blktrans_notifier.list.next)
++ unregister_mtd_user(&blktrans_notifier);
++}
++
++module_exit(mtd_blktrans_exit);
++
++EXPORT_SYMBOL_GPL(register_mtd_blktrans);
++EXPORT_SYMBOL_GPL(deregister_mtd_blktrans);
++EXPORT_SYMBOL_GPL(add_mtd_blktrans_dev);
++EXPORT_SYMBOL_GPL(del_mtd_blktrans_dev);
++
++MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
++MODULE_LICENSE("GPL");
++MODULE_DESCRIPTION("Common interface to block layer for MTD 'translation layers'");
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/mtd_blkdevs.c linux/drivers/mtd/mtd_blkdevs.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/mtd_blkdevs.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/mtd/mtd_blkdevs.c 2004-11-17 18:17:58.000000000 +0100
+@@ -0,0 +1,479 @@
++/*
++ * $Id$
++ *
++ * (C) 2003 David Woodhouse <dwmw2@infradead.org>
++ *
++ * Interface to Linux 2.5 block layer for MTD 'translation layers'.
++ *
++ */
++
++#include <linux/kernel.h>
++#include <linux/slab.h>
++#include <linux/module.h>
++#include <linux/list.h>
++#include <linux/fs.h>
++#include <linux/mtd/blktrans.h>
++#include <linux/mtd/mtd.h>
++#include <linux/blkdev.h>
++#include <linux/blk.h>
++#include <linux/blkpg.h>
++#include <linux/spinlock.h>
++#include <linux/hdreg.h>
++#include <linux/init.h>
++#include <asm/semaphore.h>
++#include <asm/uaccess.h>
++#include <linux/devfs_fs_kernel.h>
++
++static LIST_HEAD(blktrans_majors);
++
++extern struct semaphore mtd_table_mutex;
++extern struct mtd_info *mtd_table[];
++
++struct mtd_blkcore_priv {
++ struct completion thread_dead;
++ int exiting;
++ wait_queue_head_t thread_wq;
++ struct request_queue *rq;
++ spinlock_t queue_lock;
++};
++
++static int do_blktrans_request(struct mtd_blktrans_ops *tr,
++ struct mtd_blktrans_dev *dev,
++ struct request *req)
++{
++ unsigned long block, nsect;
++ char *buf;
++
++ block = req->sector;
++ nsect = req->current_nr_sectors;
++ buf = req->buffer;
++
++ if (!(req->flags & REQ_CMD))
++ return 0;
++
++ if (block + nsect > get_capacity(req->rq_disk))
++ return 0;
++
++ switch(rq_data_dir(req)) {
++ case READ:
++ for (; nsect > 0; nsect--, block++, buf += 512)
++ if (tr->readsect(dev, block, buf))
++ return 0;
++ return 1;
++
++ case WRITE:
++ if (!tr->writesect)
++ return 0;
++
++ for (; nsect > 0; nsect--, block++, buf += 512)
++ if (tr->writesect(dev, block, buf))
++ return 0;
++ return 1;
++
++ default:
++ printk(KERN_NOTICE "Unknown request %ld\n", rq_data_dir(req));
++ return 0;
++ }
++}
++
++static int mtd_blktrans_thread(void *arg)
++{
++ struct mtd_blktrans_ops *tr = arg;
++ struct request_queue *rq = tr->blkcore_priv->rq;
++
++ /* we might get involved when memory gets low, so use PF_MEMALLOC */
++ current->flags |= PF_MEMALLOC;
++
++ daemonize("%sd", tr->name);
++
++ /* daemonize() doesn't do this for us since some kernel threads
++ actually want to deal with signals. We can't just call
++ exit_sighand() since that'll cause an oops when we finally
++ do exit. */
++ spin_lock_irq(&current->sighand->siglock);
++ sigfillset(&current->blocked);
++ recalc_sigpending();
++ spin_unlock_irq(&current->sighand->siglock);
++
++ spin_lock_irq(rq->queue_lock);
++
++ while (!tr->blkcore_priv->exiting) {
++ struct request *req;
++ struct mtd_blktrans_dev *dev;
++ int res = 0;
++ DECLARE_WAITQUEUE(wait, current);
++
++ req = elv_next_request(rq);
++
++ if (!req) {
++ add_wait_queue(&tr->blkcore_priv->thread_wq, &wait);
++ set_current_state(TASK_INTERRUPTIBLE);
++
++ spin_unlock_irq(rq->queue_lock);
++
++ schedule();
++ remove_wait_queue(&tr->blkcore_priv->thread_wq, &wait);
++
++ spin_lock_irq(rq->queue_lock);
++
++ continue;
++ }
++
++ dev = req->rq_disk->private_data;
++ tr = dev->tr;
++
++ spin_unlock_irq(rq->queue_lock);
++
++ down(&dev->sem);
++ res = do_blktrans_request(tr, dev, req);
++ up(&dev->sem);
++
++ spin_lock_irq(rq->queue_lock);
++
++ end_request(req, res);
++ }
++ complete_and_exit(&tr->blkcore_priv->thread_dead, 0);
++}
++
++static void mtd_blktrans_request(struct request_queue *rq)
++{
++ struct mtd_blktrans_ops *tr = rq->queuedata;
++ wake_up(&tr->blkcore_priv->thread_wq);
++}
++
++
++int blktrans_open(struct inode *i, struct file *f)
++{
++ struct mtd_blktrans_dev *dev;
++ struct mtd_blktrans_ops *tr;
++ int ret = -ENODEV;
++
++ dev = i->i_bdev->bd_disk->private_data;
++ tr = dev->tr;
++
++ if (!try_module_get(dev->mtd->owner))
++ goto out;
++
++ if (!try_module_get(tr->owner))
++ goto out_tr;
++
++ /* FIXME: Locking. A hot pluggable device can go away
++ (del_mtd_device can be called for it) without its module
++ being unloaded. */
++ dev->mtd->usecount++;
++
++ ret = 0;
++ if (tr->open && (ret = tr->open(dev))) {
++ dev->mtd->usecount--;
++ module_put(dev->mtd->owner);
++ out_tr:
++ module_put(tr->owner);
++ }
++ out:
++ return ret;
++}
++
++int blktrans_release(struct inode *i, struct file *f)
++{
++ struct mtd_blktrans_dev *dev;
++ struct mtd_blktrans_ops *tr;
++ int ret = 0;
++
++ dev = i->i_bdev->bd_disk->private_data;
++ tr = dev->tr;
++
++ if (tr->release)
++ ret = tr->release(dev);
++
++ if (!ret) {
++ dev->mtd->usecount--;
++ module_put(dev->mtd->owner);
++ module_put(tr->owner);
++ }
++
++ return ret;
++}
++
++
++static int blktrans_ioctl(struct inode *inode, struct file *file,
++ unsigned int cmd, unsigned long arg)
++{
++ struct mtd_blktrans_dev *dev = inode->i_bdev->bd_disk->private_data;
++ struct mtd_blktrans_ops *tr = dev->tr;
++
++ switch (cmd) {
++ case BLKFLSBUF:
++ if (tr->flush)
++ return tr->flush(dev);
++ /* The core code did the work, we had nothing to do. */
++ return 0;
++
++ case HDIO_GETGEO:
++ if (tr->getgeo) {
++ struct hd_geometry g;
++ int ret;
++
++ memset(&g, 0, sizeof(g));
++ ret = tr->getgeo(dev, &g);
++
++ if (ret)
++ return ret;
++
++ g.start = get_start_sect(inode->i_bdev);
++ if (copy_to_user((void *)arg, &g, sizeof(g)))
++ return -EFAULT;
++ return 0;
++ } /* else */
++ default:
++ return -ENOTTY;
++ }
++}
++
++struct block_device_operations mtd_blktrans_ops = {
++ .owner = THIS_MODULE,
++ .open = blktrans_open,
++ .release = blktrans_release,
++ .ioctl = blktrans_ioctl,
++};
++
++int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new)
++{
++ struct mtd_blktrans_ops *tr = new->tr;
++ struct list_head *this;
++ int last_devnum = -1;
++ struct gendisk *gd;
++
++ if (!down_trylock(&mtd_table_mutex)) {
++ up(&mtd_table_mutex);
++ BUG();
++ }
++
++ list_for_each(this, &tr->devs) {
++ struct mtd_blktrans_dev *d = list_entry(this, struct mtd_blktrans_dev, list);
++ if (new->devnum == -1) {
++ /* Use first free number */
++ if (d->devnum != last_devnum+1) {
++ /* Found a free devnum. Plug it in here */
++ new->devnum = last_devnum+1;
++ list_add_tail(&new->list, &d->list);
++ goto added;
++ }
++ } else if (d->devnum == new->devnum) {
++ /* Required number taken */
++ return -EBUSY;
++ } else if (d->devnum > new->devnum) {
++ /* Required number was free */
++ list_add_tail(&new->list, &d->list);
++ goto added;
++ }
++ last_devnum = d->devnum;
++ }
++ if (new->devnum == -1)
++ new->devnum = last_devnum+1;
++
++ if ((new->devnum << tr->part_bits) > 256) {
++ return -EBUSY;
++ }
++
++ init_MUTEX(&new->sem);
++ list_add_tail(&new->list, &tr->devs);
++ added:
++ if (!tr->writesect)
++ new->readonly = 1;
++
++ gd = alloc_disk(1 << tr->part_bits);
++ if (!gd) {
++ list_del(&new->list);
++ return -ENOMEM;
++ }
++ gd->major = tr->major;
++ gd->first_minor = (new->devnum) << tr->part_bits;
++ gd->fops = &mtd_blktrans_ops;
++
++ snprintf(gd->disk_name, sizeof(gd->disk_name),
++ "%s%c", tr->name, (tr->part_bits?'a':'0') + new->devnum);
++ snprintf(gd->devfs_name, sizeof(gd->devfs_name),
++ "%s/%c", tr->name, (tr->part_bits?'a':'0') + new->devnum);
++
++ /* 2.5 has capacity in units of 512 bytes while still
++ having BLOCK_SIZE_BITS set to 10. Just to keep us amused. */
++ set_capacity(gd, (new->size * new->blksize) >> 9);
++
++ gd->private_data = new;
++ new->blkcore_priv = gd;
++ gd->queue = tr->blkcore_priv->rq;
++
++ if (new->readonly)
++ set_disk_ro(gd, 1);
++
++ add_disk(gd);
++
++ return 0;
++}
++
++int del_mtd_blktrans_dev(struct mtd_blktrans_dev *old)
++{
++ if (!down_trylock(&mtd_table_mutex)) {
++ up(&mtd_table_mutex);
++ BUG();
++ }
++
++ list_del(&old->list);
++
++ del_gendisk(old->blkcore_priv);
++ put_disk(old->blkcore_priv);
++
++ return 0;
++}
++
++void blktrans_notify_remove(struct mtd_info *mtd)
++{
++ struct list_head *this, *this2, *next;
++
++ list_for_each(this, &blktrans_majors) {
++ struct mtd_blktrans_ops *tr = list_entry(this, struct mtd_blktrans_ops, list);
++
++ list_for_each_safe(this2, next, &tr->devs) {
++ struct mtd_blktrans_dev *dev = list_entry(this2, struct mtd_blktrans_dev, list);
++
++ if (dev->mtd == mtd)
++ tr->remove_dev(dev);
++ }
++ }
++}
++
++void blktrans_notify_add(struct mtd_info *mtd)
++{
++ struct list_head *this;
++
++ if (mtd->type == MTD_ABSENT)
++ return;
++
++ list_for_each(this, &blktrans_majors) {
++ struct mtd_blktrans_ops *tr = list_entry(this, struct mtd_blktrans_ops, list);
++
++ tr->add_mtd(tr, mtd);
++ }
++
++}
++
++static struct mtd_notifier blktrans_notifier = {
++ .add = blktrans_notify_add,
++ .remove = blktrans_notify_remove,
++};
++
++int register_mtd_blktrans(struct mtd_blktrans_ops *tr)
++{
++ int ret, i;
++
++ /* Register the notifier if/when the first device type is
++ registered, to prevent the link/init ordering from fucking
++ us over. */
++ if (!blktrans_notifier.list.next)
++ register_mtd_user(&blktrans_notifier);
++
++ tr->blkcore_priv = kmalloc(sizeof(*tr->blkcore_priv), GFP_KERNEL);
++ if (!tr->blkcore_priv)
++ return -ENOMEM;
++
++ memset(tr->blkcore_priv, 0, sizeof(*tr->blkcore_priv));
++
++ down(&mtd_table_mutex);
++
++ ret = register_blkdev(tr->major, tr->name);
++ if (ret) {
++ printk(KERN_WARNING "Unable to register %s block device on major %d: %d\n",
++ tr->name, tr->major, ret);
++ kfree(tr->blkcore_priv);
++ up(&mtd_table_mutex);
++ return ret;
++ }
++ spin_lock_init(&tr->blkcore_priv->queue_lock);
++ init_completion(&tr->blkcore_priv->thread_dead);
++ init_waitqueue_head(&tr->blkcore_priv->thread_wq);
++
++ tr->blkcore_priv->rq = blk_init_queue(mtd_blktrans_request, &tr->blkcore_priv->queue_lock);
++ if (!tr->blkcore_priv->rq) {
++ unregister_blkdev(tr->major, tr->name);
++ kfree(tr->blkcore_priv);
++ up(&mtd_table_mutex);
++ return -ENOMEM;
++ }
++
++ tr->blkcore_priv->rq->queuedata = tr;
++
++ ret = kernel_thread(mtd_blktrans_thread, tr,
++ CLONE_FS|CLONE_FILES|CLONE_SIGHAND);
++ if (ret < 0) {
++ blk_cleanup_queue(tr->blkcore_priv->rq);
++ unregister_blkdev(tr->major, tr->name);
++ kfree(tr->blkcore_priv);
++ up(&mtd_table_mutex);
++ return ret;
++ }
++
++ devfs_mk_dir(tr->name);
++
++ INIT_LIST_HEAD(&tr->devs);
++ list_add(&tr->list, &blktrans_majors);
++
++ for (i=0; i<MAX_MTD_DEVICES; i++) {
++ if (mtd_table[i] && mtd_table[i]->type != MTD_ABSENT)
++ tr->add_mtd(tr, mtd_table[i]);
++ }
++
++ up(&mtd_table_mutex);
++
++ return 0;
++}
++
++int deregister_mtd_blktrans(struct mtd_blktrans_ops *tr)
++{
++ struct list_head *this, *next;
++
++ down(&mtd_table_mutex);
++
++ /* Clean up the kernel thread */
++ tr->blkcore_priv->exiting = 1;
++ wake_up(&tr->blkcore_priv->thread_wq);
++ wait_for_completion(&tr->blkcore_priv->thread_dead);
++
++ /* Remove it from the list of active majors */
++ list_del(&tr->list);
++
++ list_for_each_safe(this, next, &tr->devs) {
++ struct mtd_blktrans_dev *dev = list_entry(this, struct mtd_blktrans_dev, list);
++ tr->remove_dev(dev);
++ }
++
++ devfs_remove(tr->name);
++ blk_cleanup_queue(tr->blkcore_priv->rq);
++ unregister_blkdev(tr->major, tr->name);
++
++ up(&mtd_table_mutex);
++
++ kfree(tr->blkcore_priv);
++
++ if (!list_empty(&tr->devs))
++ BUG();
++ return 0;
++}
++
++static void __exit mtd_blktrans_exit(void)
++{
++ /* No race here -- if someone's currently in register_mtd_blktrans
++ we're screwed anyway. */
++ if (blktrans_notifier.list.next)
++ unregister_mtd_user(&blktrans_notifier);
++}
++
++module_exit(mtd_blktrans_exit);
++
++EXPORT_SYMBOL_GPL(register_mtd_blktrans);
++EXPORT_SYMBOL_GPL(deregister_mtd_blktrans);
++EXPORT_SYMBOL_GPL(add_mtd_blktrans_dev);
++EXPORT_SYMBOL_GPL(del_mtd_blktrans_dev);
++
++MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
++MODULE_LICENSE("GPL");
++MODULE_DESCRIPTION("Common interface to block layer for MTD 'translation layers'");
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/mtdblock.c linux/drivers/mtd/mtdblock.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/mtdblock.c 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/mtdblock.c 2004-11-17 18:17:58.856341736 +0100
+@@ -1,52 +1,25 @@
+ /*
+ * Direct MTD block device access
+ *
+- * $Id$
++ * $Id$
+ *
+- * 02-nov-2000 Nicolas Pitre Added read-modify-write with cache
++ * (C) 2000-2003 Nicolas Pitre <nico@cam.org>
++ * (C) 1999-2003 David Woodhouse <dwmw2@infradead.org>
+ */
+
+ #include <linux/config.h>
+ #include <linux/types.h>
+ #include <linux/module.h>
+ #include <linux/kernel.h>
++#include <linux/fs.h>
++#include <linux/init.h>
+ #include <linux/slab.h>
++#include <linux/vmalloc.h>
+ #include <linux/mtd/mtd.h>
+-#include <linux/mtd/compatmac.h>
+-
+-#define MAJOR_NR MTD_BLOCK_MAJOR
+-#define DEVICE_NAME "mtdblock"
+-#define DEVICE_REQUEST mtdblock_request
+-#define DEVICE_NR(device) (device)
+-#define DEVICE_ON(device)
+-#define DEVICE_OFF(device)
+-#define DEVICE_NO_RANDOM
+-#include <linux/blk.h>
+-/* for old kernels... */
+-#ifndef QUEUE_EMPTY
+-#define QUEUE_EMPTY (!CURRENT)
+-#endif
+-#if LINUX_VERSION_CODE < 0x20300
+-#define QUEUE_PLUGGED (blk_dev[MAJOR_NR].plug_tq.sync)
+-#else
+-#define QUEUE_PLUGGED (blk_dev[MAJOR_NR].request_queue.plugged)
+-#endif
+-
+-#ifdef CONFIG_DEVFS_FS
+-#include <linux/devfs_fs_kernel.h>
+-static void mtd_notify_add(struct mtd_info* mtd);
+-static void mtd_notify_remove(struct mtd_info* mtd);
+-static struct mtd_notifier notifier = {
+- mtd_notify_add,
+- mtd_notify_remove,
+- NULL
+-};
+-static devfs_handle_t devfs_dir_handle = NULL;
+-static devfs_handle_t devfs_rw_handle[MAX_MTD_DEVICES];
+-#endif
++#include <linux/mtd/blktrans.h>
+
+ static struct mtdblk_dev {
+- struct mtd_info *mtd; /* Locked */
++ struct mtd_info *mtd;
+ int count;
+ struct semaphore cache_sem;
+ unsigned char *cache_data;
+@@ -55,19 +28,6 @@
+ enum { STATE_EMPTY, STATE_CLEAN, STATE_DIRTY } cache_state;
+ } *mtdblks[MAX_MTD_DEVICES];
+
+-static spinlock_t mtdblks_lock;
+-
+-static int mtd_sizes[MAX_MTD_DEVICES];
+-static int mtd_blksizes[MAX_MTD_DEVICES];
+-
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,14)
+-#define BLK_INC_USE_COUNT MOD_INC_USE_COUNT
+-#define BLK_DEC_USE_COUNT MOD_DEC_USE_COUNT
+-#else
+-#define BLK_INC_USE_COUNT do {} while(0)
+-#define BLK_DEC_USE_COUNT do {} while(0)
+-#endif
+-
+ /*
+ * Cache stuff...
+ *
+@@ -151,7 +111,7 @@
+ return ret;
+
+ /*
+- * Here we could argably set the cache state to STATE_CLEAN.
++ * Here we could argubly set the cache state to STATE_CLEAN.
+ * However this could lead to inconsistency since we will not
+ * be notified if this content is altered on the flash by other
+ * means. Let's declare it empty and leave buffering tasks to
+@@ -277,57 +237,47 @@
+ return 0;
+ }
+
++static int mtdblock_readsect(struct mtd_blktrans_dev *dev,
++ unsigned long block, char *buf)
++{
++ struct mtdblk_dev *mtdblk = mtdblks[dev->devnum];
++ return do_cached_read(mtdblk, block<<9, 512, buf);
++}
+
++static int mtdblock_writesect(struct mtd_blktrans_dev *dev,
++ unsigned long block, char *buf)
++{
++ struct mtdblk_dev *mtdblk = mtdblks[dev->devnum];
++ if (unlikely(!mtdblk->cache_data)) {
++ mtdblk->cache_data = vmalloc(mtdblk->mtd->erasesize);
++ if (!mtdblk->cache_data)
++ return -EINTR;
++ /* -EINTR is not really correct, but it is the best match
++ * documented in man 2 write for all cases. We could also
++ * return -EAGAIN sometimes, but why bother?
++ */
++ }
++ return do_cached_write(mtdblk, block<<9, 512, buf);
++}
+
+-static int mtdblock_open(struct inode *inode, struct file *file)
++static int mtdblock_open(struct mtd_blktrans_dev *mbd)
+ {
+ struct mtdblk_dev *mtdblk;
+- struct mtd_info *mtd;
+- int dev;
++ struct mtd_info *mtd = mbd->mtd;
++ int dev = mbd->devnum;
+
+ DEBUG(MTD_DEBUG_LEVEL1,"mtdblock_open\n");
+
+- if (!inode)
+- return -EINVAL;
+-
+- dev = MINOR(inode->i_rdev);
+- if (dev >= MAX_MTD_DEVICES)
+- return -EINVAL;
+-
+- BLK_INC_USE_COUNT;
+-
+- mtd = get_mtd_device(NULL, dev);
+- if (!mtd)
+- return -ENODEV;
+- if (MTD_ABSENT == mtd->type) {
+- put_mtd_device(mtd);
+- BLK_DEC_USE_COUNT;
+- return -ENODEV;
+- }
+-
+- spin_lock(&mtdblks_lock);
+-
+- /* If it's already open, no need to piss about. */
+ if (mtdblks[dev]) {
+ mtdblks[dev]->count++;
+- spin_unlock(&mtdblks_lock);
+- put_mtd_device(mtd);
+ return 0;
+ }
+
+- /* OK, it's not open. Try to find it */
+-
+- /* First we have to drop the lock, because we have to
+- to things which might sleep.
+- */
+- spin_unlock(&mtdblks_lock);
+-
++ /* OK, it's not open. Create cache info for it */
+ mtdblk = kmalloc(sizeof(struct mtdblk_dev), GFP_KERNEL);
+- if (!mtdblk) {
+- put_mtd_device(mtd);
+- BLK_DEC_USE_COUNT;
++ if (!mtdblk)
+ return -ENOMEM;
+- }
++
+ memset(mtdblk, 0, sizeof(*mtdblk));
+ mtdblk->count = 1;
+ mtdblk->mtd = mtd;
+@@ -337,336 +287,102 @@
+ if ((mtdblk->mtd->flags & MTD_CAP_RAM) != MTD_CAP_RAM &&
+ mtdblk->mtd->erasesize) {
+ mtdblk->cache_size = mtdblk->mtd->erasesize;
+- mtdblk->cache_data = vmalloc(mtdblk->mtd->erasesize);
+- if (!mtdblk->cache_data) {
+- put_mtd_device(mtdblk->mtd);
+- kfree(mtdblk);
+- BLK_DEC_USE_COUNT;
+- return -ENOMEM;
+- }
+- }
+-
+- /* OK, we've created a new one. Add it to the list. */
+-
+- spin_lock(&mtdblks_lock);
+-
+- if (mtdblks[dev]) {
+- /* Another CPU made one at the same time as us. */
+- mtdblks[dev]->count++;
+- spin_unlock(&mtdblks_lock);
+- put_mtd_device(mtdblk->mtd);
+- vfree(mtdblk->cache_data);
+- kfree(mtdblk);
+- return 0;
++ mtdblk->cache_data = NULL;
+ }
+
+ mtdblks[dev] = mtdblk;
+- mtd_sizes[dev] = mtdblk->mtd->size/1024;
+- if (mtdblk->mtd->erasesize)
+- mtd_blksizes[dev] = mtdblk->mtd->erasesize;
+- if (mtd_blksizes[dev] > PAGE_SIZE)
+- mtd_blksizes[dev] = PAGE_SIZE;
+- set_device_ro (inode->i_rdev, !(mtdblk->mtd->flags & MTD_WRITEABLE));
+-
+- spin_unlock(&mtdblks_lock);
+
+ DEBUG(MTD_DEBUG_LEVEL1, "ok\n");
+
+ return 0;
+ }
+
+-static release_t mtdblock_release(struct inode *inode, struct file *file)
++static int mtdblock_release(struct mtd_blktrans_dev *mbd)
+ {
+- int dev;
+- struct mtdblk_dev *mtdblk;
+- DEBUG(MTD_DEBUG_LEVEL1, "mtdblock_release\n");
++ int dev = mbd->devnum;
++ struct mtdblk_dev *mtdblk = mtdblks[dev];
+
+- if (inode == NULL)
+- release_return(-ENODEV);
+-
+- dev = MINOR(inode->i_rdev);
+- mtdblk = mtdblks[dev];
++ DEBUG(MTD_DEBUG_LEVEL1, "mtdblock_release\n");
+
+ down(&mtdblk->cache_sem);
+ write_cached_data(mtdblk);
+ up(&mtdblk->cache_sem);
+
+- spin_lock(&mtdblks_lock);
+ if (!--mtdblk->count) {
+ /* It was the last usage. Free the device */
+ mtdblks[dev] = NULL;
+- spin_unlock(&mtdblks_lock);
+ if (mtdblk->mtd->sync)
+ mtdblk->mtd->sync(mtdblk->mtd);
+- put_mtd_device(mtdblk->mtd);
+ vfree(mtdblk->cache_data);
+ kfree(mtdblk);
+- } else {
+- spin_unlock(&mtdblks_lock);
+ }
+-
+ DEBUG(MTD_DEBUG_LEVEL1, "ok\n");
+
+- BLK_DEC_USE_COUNT;
+- release_return(0);
+-}
+-
+-
+-/*
+- * This is a special request_fn because it is executed in a process context
+- * to be able to sleep independently of the caller. The io_request_lock
+- * is held upon entry and exit.
+- * The head of our request queue is considered active so there is no need
+- * to dequeue requests before we are done.
+- */
+-static void handle_mtdblock_request(void)
+-{
+- struct request *req;
+- struct mtdblk_dev *mtdblk;
+- unsigned int res;
+-
+- for (;;) {
+- INIT_REQUEST;
+- req = CURRENT;
+- spin_unlock_irq(&io_request_lock);
+- mtdblk = mtdblks[MINOR(req->rq_dev)];
+- res = 0;
+-
+- if (MINOR(req->rq_dev) >= MAX_MTD_DEVICES)
+- panic("%s: minor out of bounds", __FUNCTION__);
+-
+- if ((req->sector + req->current_nr_sectors) > (mtdblk->mtd->size >> 9))
+- goto end_req;
+-
+- // Handle the request
+- switch (req->cmd)
+- {
+- int err;
+-
+- case READ:
+- down(&mtdblk->cache_sem);
+- err = do_cached_read (mtdblk, req->sector << 9,
+- req->current_nr_sectors << 9,
+- req->buffer);
+- up(&mtdblk->cache_sem);
+- if (!err)
+- res = 1;
+- break;
+-
+- case WRITE:
+- // Read only device
+- if ( !(mtdblk->mtd->flags & MTD_WRITEABLE) )
+- break;
+-
+- // Do the write
+- down(&mtdblk->cache_sem);
+- err = do_cached_write (mtdblk, req->sector << 9,
+- req->current_nr_sectors << 9,
+- req->buffer);
+- up(&mtdblk->cache_sem);
+- if (!err)
+- res = 1;
+- break;
+- }
+-
+-end_req:
+- spin_lock_irq(&io_request_lock);
+- end_request(res);
+- }
+-}
+-
+-static volatile int leaving = 0;
+-static DECLARE_MUTEX_LOCKED(thread_sem);
+-static DECLARE_WAIT_QUEUE_HEAD(thr_wq);
+-
+-int mtdblock_thread(void *dummy)
+-{
+- struct task_struct *tsk = current;
+- DECLARE_WAITQUEUE(wait, tsk);
+-
+- /* we might get involved when memory gets low, so use PF_MEMALLOC */
+- tsk->flags |= PF_MEMALLOC;
+- strcpy(tsk->comm, "mtdblockd");
+- spin_lock_irq(&tsk->sigmask_lock);
+- sigfillset(&tsk->blocked);
+- recalc_sigpending(tsk);
+- spin_unlock_irq(&tsk->sigmask_lock);
+- daemonize();
+-
+- while (!leaving) {
+- add_wait_queue(&thr_wq, &wait);
+- set_current_state(TASK_INTERRUPTIBLE);
+- spin_lock_irq(&io_request_lock);
+- if (QUEUE_EMPTY || QUEUE_PLUGGED) {
+- spin_unlock_irq(&io_request_lock);
+- schedule();
+- remove_wait_queue(&thr_wq, &wait);
+- } else {
+- remove_wait_queue(&thr_wq, &wait);
+- set_current_state(TASK_RUNNING);
+- handle_mtdblock_request();
+- spin_unlock_irq(&io_request_lock);
+- }
+- }
+-
+- up(&thread_sem);
+ return 0;
+ }
+
+-#if LINUX_VERSION_CODE < 0x20300
+-#define RQFUNC_ARG void
+-#else
+-#define RQFUNC_ARG request_queue_t *q
+-#endif
+-
+-static void mtdblock_request(RQFUNC_ARG)
++static int mtdblock_flush(struct mtd_blktrans_dev *dev)
+ {
+- /* Don't do anything, except wake the thread if necessary */
+- wake_up(&thr_wq);
+-}
++ struct mtdblk_dev *mtdblk = mtdblks[dev->devnum];
+
+-
+-static int mtdblock_ioctl(struct inode * inode, struct file * file,
+- unsigned int cmd, unsigned long arg)
+-{
+- struct mtdblk_dev *mtdblk;
+-
+- mtdblk = mtdblks[MINOR(inode->i_rdev)];
+-
+-#ifdef PARANOIA
+- if (!mtdblk)
+- BUG();
+-#endif
+-
+- switch (cmd) {
+- case BLKGETSIZE: /* Return device size */
+- return put_user((mtdblk->mtd->size >> 9), (unsigned long *) arg);
+-
+-#ifdef BLKGETSIZE64
+- case BLKGETSIZE64:
+- return put_user((u64)mtdblk->mtd->size, (u64 *)arg);
+-#endif
+-
+- case BLKFLSBUF:
+-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
+- if(!capable(CAP_SYS_ADMIN))
+- return -EACCES;
+-#endif
+- fsync_dev(inode->i_rdev);
+- invalidate_buffers(inode->i_rdev);
+ down(&mtdblk->cache_sem);
+ write_cached_data(mtdblk);
+ up(&mtdblk->cache_sem);
++
+ if (mtdblk->mtd->sync)
+ mtdblk->mtd->sync(mtdblk->mtd);
+ return 0;
+-
+- default:
+- return -EINVAL;
+- }
+ }
+
+-#if LINUX_VERSION_CODE < 0x20326
+-static struct file_operations mtd_fops =
++static void mtdblock_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
+ {
+- open: mtdblock_open,
+- ioctl: mtdblock_ioctl,
+- release: mtdblock_release,
+- read: block_read,
+- write: block_write
+-};
+-#else
+-static struct block_device_operations mtd_fops =
+-{
+-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,14)
+- owner: THIS_MODULE,
+-#endif
+- open: mtdblock_open,
+- release: mtdblock_release,
+- ioctl: mtdblock_ioctl
+-};
+-#endif
++ struct mtd_blktrans_dev *dev = kmalloc(sizeof(*dev), GFP_KERNEL);
+
+-#ifdef CONFIG_DEVFS_FS
+-/* Notification that a new device has been added. Create the devfs entry for
+- * it. */
+-
+-static void mtd_notify_add(struct mtd_info* mtd)
+-{
+- char name[8];
+-
+- if (!mtd || mtd->type == MTD_ABSENT)
++ if (!dev)
+ return;
+
+- sprintf(name, "%d", mtd->index);
+- devfs_rw_handle[mtd->index] = devfs_register(devfs_dir_handle, name,
+- DEVFS_FL_DEFAULT, MTD_BLOCK_MAJOR, mtd->index,
+- S_IFBLK | S_IRUGO | S_IWUGO,
+- &mtd_fops, NULL);
+-}
+-
+-static void mtd_notify_remove(struct mtd_info* mtd)
+-{
+- if (!mtd || mtd->type == MTD_ABSENT)
+- return;
++ memset(dev, 0, sizeof(*dev));
+
+- devfs_unregister(devfs_rw_handle[mtd->index]);
+-}
+-#endif
++ dev->mtd = mtd;
++ dev->devnum = mtd->index;
++ dev->blksize = 512;
++ dev->size = mtd->size >> 9;
++ dev->tr = tr;
++
++ if (!(mtd->flags & MTD_WRITEABLE))
++ dev->readonly = 1;
++
++ add_mtd_blktrans_dev(dev);
++}
++
++static void mtdblock_remove_dev(struct mtd_blktrans_dev *dev)
++{
++ del_mtd_blktrans_dev(dev);
++ kfree(dev);
++}
++
++struct mtd_blktrans_ops mtdblock_tr = {
++ .name = "mtdblock",
++ .major = 31,
++ .part_bits = 0,
++ .open = mtdblock_open,
++ .flush = mtdblock_flush,
++ .release = mtdblock_release,
++ .readsect = mtdblock_readsect,
++ .writesect = mtdblock_writesect,
++ .add_mtd = mtdblock_add_mtd,
++ .remove_dev = mtdblock_remove_dev,
++ .owner = THIS_MODULE,
++};
+
+ int __init init_mtdblock(void)
+ {
+- int i;
+-
+- spin_lock_init(&mtdblks_lock);
+-#ifdef CONFIG_DEVFS_FS
+- if (devfs_register_blkdev(MTD_BLOCK_MAJOR, DEVICE_NAME, &mtd_fops))
+- {
+- printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n",
+- MTD_BLOCK_MAJOR);
+- return -EAGAIN;
+- }
+-
+- devfs_dir_handle = devfs_mk_dir(NULL, DEVICE_NAME, NULL);
+- register_mtd_user(&notifier);
+-#else
+- if (register_blkdev(MAJOR_NR,DEVICE_NAME,&mtd_fops)) {
+- printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n",
+- MTD_BLOCK_MAJOR);
+- return -EAGAIN;
+- }
+-#endif
+-
+- /* We fill it in at open() time. */
+- for (i=0; i< MAX_MTD_DEVICES; i++) {
+- mtd_sizes[i] = 0;
+- mtd_blksizes[i] = BLOCK_SIZE;
+- }
+- init_waitqueue_head(&thr_wq);
+- /* Allow the block size to default to BLOCK_SIZE. */
+- blksize_size[MAJOR_NR] = mtd_blksizes;
+- blk_size[MAJOR_NR] = mtd_sizes;
+-
+- blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), &mtdblock_request);
+- kernel_thread (mtdblock_thread, NULL, CLONE_FS|CLONE_FILES|CLONE_SIGHAND);
+- return 0;
++ return register_mtd_blktrans(&mtdblock_tr);
+ }
+
+ static void __exit cleanup_mtdblock(void)
+ {
+- leaving = 1;
+- wake_up(&thr_wq);
+- down(&thread_sem);
+-#ifdef CONFIG_DEVFS_FS
+- unregister_mtd_user(&notifier);
+- devfs_unregister(devfs_dir_handle);
+- devfs_unregister_blkdev(MTD_BLOCK_MAJOR, DEVICE_NAME);
+-#else
+- unregister_blkdev(MAJOR_NR,DEVICE_NAME);
+-#endif
+- blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
+- blksize_size[MAJOR_NR] = NULL;
+- blk_size[MAJOR_NR] = NULL;
++ deregister_mtd_blktrans(&mtdblock_tr);
+ }
+
+ module_init(init_mtdblock);
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/mtdblock_ro.c linux/drivers/mtd/mtdblock_ro.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/mtdblock_ro.c 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/mtdblock_ro.c 2004-11-17 18:17:58.857341584 +0100
+@@ -1,301 +1,87 @@
+ /*
+- * $Id$
++ * $Id$
+ *
+- * Read-only version of the mtdblock device, without the
+- * read/erase/modify/writeback stuff
++ * (C) 2003 David Woodhouse <dwmw2@infradead.org>
++ *
++ * Simple read-only (writable only for RAM) mtdblock driver
+ */
+
+-#ifdef MTDBLOCK_DEBUG
+-#define DEBUGLVL debug
+-#endif
+-
+-
+-#include <linux/module.h>
+-#include <linux/types.h>
+-
++#include <linux/init.h>
++#include <linux/slab.h>
+ #include <linux/mtd/mtd.h>
+-#include <linux/mtd/compatmac.h>
+-
+-#define MAJOR_NR MTD_BLOCK_MAJOR
+-#define DEVICE_NAME "mtdblock"
+-#define DEVICE_REQUEST mtdblock_request
+-#define DEVICE_NR(device) (device)
+-#define DEVICE_ON(device)
+-#define DEVICE_OFF(device)
+-#define DEVICE_NO_RANDOM
+-#include <linux/blk.h>
+-
+-#if LINUX_VERSION_CODE < 0x20300
+-#define RQFUNC_ARG void
+-#define blkdev_dequeue_request(req) do {CURRENT = req->next;} while (0)
+-#else
+-#define RQFUNC_ARG request_queue_t *q
+-#endif
+-
+-#ifdef MTDBLOCK_DEBUG
+-static int debug = MTDBLOCK_DEBUG;
+-MODULE_PARM(debug, "i");
+-#endif
+-
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,14)
+-#define BLK_INC_USE_COUNT MOD_INC_USE_COUNT
+-#define BLK_DEC_USE_COUNT MOD_DEC_USE_COUNT
+-#else
+-#define BLK_INC_USE_COUNT do {} while(0)
+-#define BLK_DEC_USE_COUNT do {} while(0)
+-#endif
+-
+-static int mtd_sizes[MAX_MTD_DEVICES];
++#include <linux/mtd/blktrans.h>
+
+-
+-static int mtdblock_open(struct inode *inode, struct file *file)
++static int mtdblock_readsect(struct mtd_blktrans_dev *dev,
++ unsigned long block, char *buf)
+ {
+- struct mtd_info *mtd = NULL;
+-
+- int dev;
+-
+- DEBUG(1,"mtdblock_open\n");
+-
+- if (inode == 0)
+- return -EINVAL;
+-
+- dev = MINOR(inode->i_rdev);
+-
+- mtd = get_mtd_device(NULL, dev);
+- if (!mtd)
+- return -EINVAL;
+- if (MTD_ABSENT == mtd->type) {
+- put_mtd_device(mtd);
+- return -EINVAL;
+- }
+-
+- BLK_INC_USE_COUNT;
+-
+- mtd_sizes[dev] = mtd->size>>9;
+-
+- DEBUG(1, "ok\n");
++ size_t retlen;
+
++ if (dev->mtd->read(dev->mtd, (block * 512), 512, &retlen, buf))
++ return 1;
+ return 0;
+ }
+
+-static release_t mtdblock_release(struct inode *inode, struct file *file)
++static int mtdblock_writesect(struct mtd_blktrans_dev *dev,
++ unsigned long block, char *buf)
+ {
+- int dev;
+- struct mtd_info *mtd;
+-
+- DEBUG(1, "mtdblock_release\n");
+-
+- if (inode == NULL)
+- release_return(-ENODEV);
+-
+- dev = MINOR(inode->i_rdev);
+- mtd = __get_mtd_device(NULL, dev);
+-
+- if (!mtd) {
+- printk(KERN_WARNING "MTD device is absent on mtd_release!\n");
+- BLK_DEC_USE_COUNT;
+- release_return(-ENODEV);
+- }
+-
+- if (mtd->sync)
+- mtd->sync(mtd);
+-
+- put_mtd_device(mtd);
+-
+- DEBUG(1, "ok\n");
++ size_t retlen;
+
+- BLK_DEC_USE_COUNT;
+- release_return(0);
++ if (dev->mtd->write(dev->mtd, (block * 512), 512, &retlen, buf))
++ return 1;
++ return 0;
+ }
+
+-
+-static void mtdblock_request(RQFUNC_ARG)
++static void mtdblock_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
+ {
+- struct request *current_request;
+- unsigned int res = 0;
+- struct mtd_info *mtd;
+-
+- while (1)
+- {
+- /* Grab the Request and unlink it from the request list, INIT_REQUEST
+- will execute a return if we are done. */
+- INIT_REQUEST;
+- current_request = CURRENT;
+-
+- if (MINOR(current_request->rq_dev) >= MAX_MTD_DEVICES)
+- {
+- printk("mtd: Unsupported device!\n");
+- end_request(0);
+- continue;
+- }
+-
+- // Grab our MTD structure
+-
+- mtd = __get_mtd_device(NULL, MINOR(current_request->rq_dev));
+- if (!mtd) {
+- printk("MTD device %d doesn't appear to exist any more\n", CURRENT_DEV);
+- end_request(0);
+- }
+-
+- if (current_request->sector << 9 > mtd->size ||
+- (current_request->sector + current_request->current_nr_sectors) << 9 > mtd->size)
+- {
+- printk("mtd: Attempt to read past end of device!\n");
+- printk("size: %x, sector: %lx, nr_sectors %lx\n", mtd->size,
+- current_request->sector, current_request->current_nr_sectors);
+- end_request(0);
+- continue;
+- }
+-
+- /* Remove the request we are handling from the request list so nobody messes
+- with it */
+-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
+- /* Now drop the lock that the ll_rw_blk functions grabbed for us
+- and process the request. This is necessary due to the extreme time
+- we spend processing it. */
+- spin_unlock_irq(&io_request_lock);
+-#endif
+-
+- // Handle the request
+- switch (current_request->cmd)
+- {
+- size_t retlen;
++ struct mtd_blktrans_dev *dev = kmalloc(sizeof(*dev), GFP_KERNEL);
+
+- case READ:
+- if (MTD_READ(mtd,current_request->sector<<9,
+- current_request->current_nr_sectors << 9,
+- &retlen, current_request->buffer) == 0)
+- res = 1;
+- else
+- res = 0;
+- break;
++ if (!dev)
++ return;
+
+- case WRITE:
++ memset(dev, 0, sizeof(*dev));
+
+- /* printk("mtdblock_request WRITE sector=%d(%d)\n",current_request->sector,
+- current_request->current_nr_sectors);
+- */
++ dev->mtd = mtd;
++ dev->devnum = mtd->index;
++ dev->blksize = 512;
++ dev->size = mtd->size >> 9;
++ dev->tr = tr;
++ if ((mtd->flags & (MTD_CLEAR_BITS|MTD_SET_BITS|MTD_WRITEABLE)) !=
++ (MTD_CLEAR_BITS|MTD_SET_BITS|MTD_WRITEABLE))
++ dev->readonly = 1;
+
+- // Read only device
+- if ((mtd->flags & MTD_CAP_RAM) == 0)
+- {
+- res = 0;
+- break;
+- }
+-
+- // Do the write
+- if (MTD_WRITE(mtd,current_request->sector<<9,
+- current_request->current_nr_sectors << 9,
+- &retlen, current_request->buffer) == 0)
+- res = 1;
+- else
+- res = 0;
+- break;
+-
+- // Shouldn't happen
+- default:
+- printk("mtd: unknown request\n");
+- break;
+- }
+-
+- // Grab the lock and re-thread the item onto the linked list
+-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
+- spin_lock_irq(&io_request_lock);
+-#endif
+- end_request(res);
+- }
++ add_mtd_blktrans_dev(dev);
+ }
+
+-
+-
+-static int mtdblock_ioctl(struct inode * inode, struct file * file,
+- unsigned int cmd, unsigned long arg)
++static void mtdblock_remove_dev(struct mtd_blktrans_dev *dev)
+ {
+- struct mtd_info *mtd;
+-
+- mtd = __get_mtd_device(NULL, MINOR(inode->i_rdev));
+-
+- if (!mtd) return -EINVAL;
+-
+- switch (cmd) {
+- case BLKGETSIZE: /* Return device size */
+- return put_user((mtd->size >> 9), (unsigned long *) arg);
+-
+-#ifdef BLKGETSIZE64
+- case BLKGETSIZE64:
+- return put_user((u64)mtd->size, (u64 *)arg);
+-#endif
+-
+- case BLKFLSBUF:
+-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
+- if(!capable(CAP_SYS_ADMIN)) return -EACCES;
+-#endif
+- fsync_dev(inode->i_rdev);
+- invalidate_buffers(inode->i_rdev);
+- if (mtd->sync)
+- mtd->sync(mtd);
+- return 0;
+-
+- default:
+- return -ENOTTY;
+- }
++ del_mtd_blktrans_dev(dev);
++ kfree(dev);
+ }
+
+-#if LINUX_VERSION_CODE < 0x20326
+-static struct file_operations mtd_fops =
+-{
+- open: mtdblock_open,
+- ioctl: mtdblock_ioctl,
+- release: mtdblock_release,
+- read: block_read,
+- write: block_write
+-};
+-#else
+-static struct block_device_operations mtd_fops =
+-{
+-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,14)
+- owner: THIS_MODULE,
+-#endif
+- open: mtdblock_open,
+- release: mtdblock_release,
+- ioctl: mtdblock_ioctl
++struct mtd_blktrans_ops mtdblock_tr = {
++ .name = "mtdblock",
++ .major = 31,
++ .part_bits = 0,
++ .readsect = mtdblock_readsect,
++ .writesect = mtdblock_writesect,
++ .add_mtd = mtdblock_add_mtd,
++ .remove_dev = mtdblock_remove_dev,
++ .owner = THIS_MODULE,
+ };
+-#endif
+
+-int __init init_mtdblock(void)
++static int __init mtdblock_init(void)
+ {
+- int i;
+-
+- if (register_blkdev(MAJOR_NR,DEVICE_NAME,&mtd_fops)) {
+- printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n",
+- MTD_BLOCK_MAJOR);
+- return -EAGAIN;
+- }
+-
+- /* We fill it in at open() time. */
+- for (i=0; i< MAX_MTD_DEVICES; i++) {
+- mtd_sizes[i] = 0;
+- }
+-
+- /* Allow the block size to default to BLOCK_SIZE. */
+- blksize_size[MAJOR_NR] = NULL;
+- blk_size[MAJOR_NR] = mtd_sizes;
+-
+- blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), &mtdblock_request);
+- return 0;
++ return register_mtd_blktrans(&mtdblock_tr);
+ }
+
+-static void __exit cleanup_mtdblock(void)
++static void __exit mtdblock_exit(void)
+ {
+- unregister_blkdev(MAJOR_NR,DEVICE_NAME);
+- blk_size[MAJOR_NR] = NULL;
+- blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
++ deregister_mtd_blktrans(&mtdblock_tr);
+ }
+
+-module_init(init_mtdblock);
+-module_exit(cleanup_mtdblock);
+-
++module_init(mtdblock_init);
++module_exit(mtdblock_exit);
+
+ MODULE_LICENSE("GPL");
+-MODULE_AUTHOR("Erwin Authried <eauth@softsys.co.at> et al.");
++MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
+ MODULE_DESCRIPTION("Simple read-only block device emulation access to MTD devices");
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/mtdchar.c linux/drivers/mtd/mtdchar.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/mtdchar.c 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/mtdchar.c 2004-11-17 18:17:58.859341280 +0100
+@@ -1,8 +1,7 @@
+ /*
+- * $Id$
++ * $Id$
+ *
+ * Character-device access to raw MTD devices.
+- * Pure 2.4 version - compatibility cruft removed to mtdchar-compat.c
+ *
+ */
+
+@@ -10,7 +9,11 @@
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+ #include <linux/mtd/mtd.h>
++#include <linux/mtd/compatmac.h>
+ #include <linux/slab.h>
++#include <linux/init.h>
++#include <linux/fs.h>
++#include <asm/uaccess.h>
+
+ #ifdef CONFIG_DEVFS_FS
+ #include <linux/devfs_fs_kernel.h>
+@@ -18,8 +21,8 @@
+ static void mtd_notify_remove(struct mtd_info* mtd);
+
+ static struct mtd_notifier notifier = {
+- add: mtd_notify_add,
+- remove: mtd_notify_remove,
++ .add = mtd_notify_add,
++ .remove = mtd_notify_remove,
+ };
+
+ static devfs_handle_t devfs_dir_handle;
+@@ -60,7 +63,7 @@
+
+ static int mtd_open(struct inode *inode, struct file *file)
+ {
+- int minor = minor(inode->i_rdev);
++ int minor = iminor(inode);
+ int devnum = minor >> 1;
+ struct mtd_info *mtd;
+
+@@ -442,81 +445,13 @@
+ break;
+ }
+
+- case MEMWRITEDATA:
++ case MEMSETOOBSEL:
+ {
+- struct mtd_oob_buf buf;
+- void *databuf;
+- ssize_t retlen;
+-
+- if (copy_from_user(&buf, (struct mtd_oob_buf *)arg, sizeof(struct mtd_oob_buf)))
+- return -EFAULT;
+-
+- if (buf.length > 0x4096)
+- return -EINVAL;
+-
+- if (!mtd->write_ecc)
+- ret = -EOPNOTSUPP;
+- else
+- ret = verify_area(VERIFY_READ, (char *)buf.ptr, buf.length);
+-
+- if (ret)
+- return ret;
+-
+- databuf = kmalloc(buf.length, GFP_KERNEL);
+- if (!databuf)
+- return -ENOMEM;
+-
+- if (copy_from_user(databuf, buf.ptr, buf.length)) {
+- kfree(databuf);
++ if (copy_from_user(&mtd->oobinfo ,(void *)arg, sizeof(struct nand_oobinfo)))
+ return -EFAULT;
+- }
+-
+- ret = (mtd->write_ecc)(mtd, buf.start, buf.length, &retlen, databuf, NULL, 0);
+-
+- if (copy_to_user((void *)arg + sizeof(u_int32_t), &retlen, sizeof(u_int32_t)))
+- ret = -EFAULT;
+-
+- kfree(databuf);
+ break;
+-
+ }
+
+- case MEMREADDATA:
+- {
+- struct mtd_oob_buf buf;
+- void *databuf;
+- ssize_t retlen = 0;
+-
+- if (copy_from_user(&buf, (struct mtd_oob_buf *)arg, sizeof(struct mtd_oob_buf)))
+- return -EFAULT;
+-
+- if (buf.length > 0x4096)
+- return -EINVAL;
+-
+- if (!mtd->read_ecc)
+- ret = -EOPNOTSUPP;
+- else
+- ret = verify_area(VERIFY_WRITE, (char *)buf.ptr, buf.length);
+-
+- if (ret)
+- return ret;
+-
+- databuf = kmalloc(buf.length, GFP_KERNEL);
+- if (!databuf)
+- return -ENOMEM;
+-
+- ret = (mtd->read_ecc)(mtd, buf.start, buf.length, &retlen, databuf, NULL, 0);
+-
+- if (copy_to_user((void *)arg + sizeof(u_int32_t), &retlen, sizeof(u_int32_t)))
+- ret = -EFAULT;
+- else if (retlen && copy_to_user(buf.ptr, databuf, retlen))
+- ret = -EFAULT;
+-
+- kfree(databuf);
+- break;
+- }
+-
+-
+ default:
+ DEBUG(MTD_DEBUG_LEVEL0, "Invalid ioctl %x (MEMGETINFO = %x)\n", cmd, MEMGETINFO);
+ ret = -ENOTTY;
+@@ -526,13 +461,13 @@
+ } /* memory_ioctl */
+
+ static struct file_operations mtd_fops = {
+- owner: THIS_MODULE,
+- llseek: mtd_lseek, /* lseek */
+- read: mtd_read, /* read */
+- write: mtd_write, /* write */
+- ioctl: mtd_ioctl, /* ioctl */
+- open: mtd_open, /* open */
+- release: mtd_close, /* release */
++ .owner = THIS_MODULE,
++ .llseek = mtd_lseek,
++ .read = mtd_read,
++ .write = mtd_write,
++ .ioctl = mtd_ioctl,
++ .open = mtd_open,
++ .release = mtd_close,
+ };
+
+
+@@ -572,26 +507,18 @@
+
+ static int __init init_mtdchar(void)
+ {
+-#ifdef CONFIG_DEVFS_FS
+- if (devfs_register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops))
++ if (register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops))
+ {
+ printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n",
+ MTD_CHAR_MAJOR);
+ return -EAGAIN;
+ }
+
++#ifdef CONFIG_DEVFS_FS
+ devfs_dir_handle = devfs_mk_dir(NULL, "mtd", NULL);
+
+ register_mtd_user(&notifier);
+-#else
+- if (register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops))
+- {
+- printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n",
+- MTD_CHAR_MAJOR);
+- return -EAGAIN;
+- }
+ #endif
+-
+ return 0;
+ }
+
+@@ -600,10 +527,8 @@
+ #ifdef CONFIG_DEVFS_FS
+ unregister_mtd_user(&notifier);
+ devfs_unregister(devfs_dir_handle);
+- devfs_unregister_chrdev(MTD_CHAR_MAJOR, "mtd");
+-#else
+- unregister_chrdev(MTD_CHAR_MAJOR, "mtd");
+ #endif
++ unregister_chrdev(MTD_CHAR_MAJOR, "mtd");
+ }
+
+ module_init(init_mtdchar);
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/mtdconcat.c linux/drivers/mtd/mtdconcat.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/mtdconcat.c 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/mtdconcat.c 2004-11-17 18:17:58.860341128 +0100
+@@ -3,9 +3,11 @@
+ *
+ * (C) 2002 Robert Kaiser <rkaiser@sysgo.de>
+ *
++ * NAND support by Christian Gan <cgan@iders.ca>
++ *
+ * This code is GPL
+ *
+- * $Id$
++ * $Id$
+ */
+
+ #include <linux/module.h>
+@@ -35,21 +37,20 @@
+ #define SIZEOF_STRUCT_MTD_CONCAT(num_subdev) \
+ ((sizeof(struct mtd_concat) + (num_subdev) * sizeof(struct mtd_info *)))
+
+-
+ /*
+ * Given a pointer to the MTD object in the mtd_concat structure,
+ * we can retrieve the pointer to that structure with this macro.
+ */
+ #define CONCAT(x) ((struct mtd_concat *)(x))
+
+-
+ /*
+ * MTD methods which look up the relevant subdevice, translate the
+ * effective address and pass through to the subdevice.
+ */
+
+-static int concat_read (struct mtd_info *mtd, loff_t from, size_t len,
+- size_t *retlen, u_char *buf)
++static int
++concat_read(struct mtd_info *mtd, loff_t from, size_t len,
++ size_t * retlen, u_char * buf)
+ {
+ struct mtd_concat *concat = CONCAT(mtd);
+ int err = -EINVAL;
+@@ -57,43 +58,43 @@
+
+ *retlen = 0;
+
+- for(i = 0; i < concat->num_subdev; i++)
+- {
++ for (i = 0; i < concat->num_subdev; i++) {
+ struct mtd_info *subdev = concat->subdev[i];
+ size_t size, retsize;
+
+- if (from >= subdev->size)
+- {
++ if (from >= subdev->size) {
++ /* Not destined for this subdev */
+ size = 0;
+ from -= subdev->size;
++ continue;
+ }
+- else
+- {
+ if (from + len > subdev->size)
++ /* First part goes into this subdev */
+ size = subdev->size - from;
+ else
++ /* Entire transaction goes into this subdev */
+ size = len;
+
+ err = subdev->read(subdev, from, size, &retsize, buf);
+
+- if(err)
++ if (err)
+ break;
+
+ *retlen += retsize;
+ len -= size;
+- if(len == 0)
++ if (len == 0)
+ break;
+
+ err = -EINVAL;
+ buf += size;
+ from = 0;
+ }
+- }
+ return err;
+ }
+
+-static int concat_write (struct mtd_info *mtd, loff_t to, size_t len,
+- size_t *retlen, const u_char *buf)
++static int
++concat_write(struct mtd_info *mtd, loff_t to, size_t len,
++ size_t * retlen, const u_char * buf)
+ {
+ struct mtd_concat *concat = CONCAT(mtd);
+ int err = -EINVAL;
+@@ -104,18 +105,15 @@
+
+ *retlen = 0;
+
+- for(i = 0; i < concat->num_subdev; i++)
+- {
++ for (i = 0; i < concat->num_subdev; i++) {
+ struct mtd_info *subdev = concat->subdev[i];
+ size_t size, retsize;
+
+- if (to >= subdev->size)
+- {
++ if (to >= subdev->size) {
+ size = 0;
+ to -= subdev->size;
++ continue;
+ }
+- else
+- {
+ if (to + len > subdev->size)
+ size = subdev->size - to;
+ else
+@@ -126,25 +124,232 @@
+ else
+ err = subdev->write(subdev, to, size, &retsize, buf);
+
+- if(err)
++ if (err)
+ break;
+
+ *retlen += retsize;
+ len -= size;
+- if(len == 0)
++ if (len == 0)
+ break;
+
+ err = -EINVAL;
+ buf += size;
+ to = 0;
+ }
++ return err;
++}
++
++static int
++concat_read_ecc(struct mtd_info *mtd, loff_t from, size_t len,
++ size_t * retlen, u_char * buf, u_char * eccbuf,
++ struct nand_oobinfo *oobsel)
++{
++ struct mtd_concat *concat = CONCAT(mtd);
++ int err = -EINVAL;
++ int i;
++
++ *retlen = 0;
++
++ for (i = 0; i < concat->num_subdev; i++) {
++ struct mtd_info *subdev = concat->subdev[i];
++ size_t size, retsize;
++
++ if (from >= subdev->size) {
++ /* Not destined for this subdev */
++ size = 0;
++ from -= subdev->size;
++ continue;
++ }
++
++ if (from + len > subdev->size)
++ /* First part goes into this subdev */
++ size = subdev->size - from;
++ else
++ /* Entire transaction goes into this subdev */
++ size = len;
++
++ if (subdev->read_ecc)
++ err = subdev->read_ecc(subdev, from, size,
++ &retsize, buf, eccbuf, oobsel);
++ else
++ err = -EINVAL;
++
++ if (err)
++ break;
++
++ *retlen += retsize;
++ len -= size;
++ if (len == 0)
++ break;
++
++ err = -EINVAL;
++ buf += size;
++ if (eccbuf) {
++ eccbuf += subdev->oobsize;
++ /* in nand.c at least, eccbufs are
++ tagged with 2 (int)eccstatus'; we
++ must account for these */
++ eccbuf += 2 * (sizeof (int));
++ }
++ from = 0;
+ }
+ return err;
+ }
+
+-static void concat_erase_callback (struct erase_info *instr)
++static int
++concat_write_ecc(struct mtd_info *mtd, loff_t to, size_t len,
++ size_t * retlen, const u_char * buf, u_char * eccbuf,
++ struct nand_oobinfo *oobsel)
+ {
+- wake_up((wait_queue_head_t *)instr->priv);
++ struct mtd_concat *concat = CONCAT(mtd);
++ int err = -EINVAL;
++ int i;
++
++ if (!(mtd->flags & MTD_WRITEABLE))
++ return -EROFS;
++
++ *retlen = 0;
++
++ for (i = 0; i < concat->num_subdev; i++) {
++ struct mtd_info *subdev = concat->subdev[i];
++ size_t size, retsize;
++
++ if (to >= subdev->size) {
++ size = 0;
++ to -= subdev->size;
++ continue;
++ }
++ if (to + len > subdev->size)
++ size = subdev->size - to;
++ else
++ size = len;
++
++ if (!(subdev->flags & MTD_WRITEABLE))
++ err = -EROFS;
++ else if (subdev->write_ecc)
++ err = subdev->write_ecc(subdev, to, size,
++ &retsize, buf, eccbuf, oobsel);
++ else
++ err = -EINVAL;
++
++ if (err)
++ break;
++
++ *retlen += retsize;
++ len -= size;
++ if (len == 0)
++ break;
++
++ err = -EINVAL;
++ buf += size;
++ if (eccbuf)
++ eccbuf += subdev->oobsize;
++ to = 0;
++ }
++ return err;
++}
++
++static int
++concat_read_oob(struct mtd_info *mtd, loff_t from, size_t len,
++ size_t * retlen, u_char * buf)
++{
++ struct mtd_concat *concat = CONCAT(mtd);
++ int err = -EINVAL;
++ int i;
++
++ *retlen = 0;
++
++ for (i = 0; i < concat->num_subdev; i++) {
++ struct mtd_info *subdev = concat->subdev[i];
++ size_t size, retsize;
++
++ if (from >= subdev->size) {
++ /* Not destined for this subdev */
++ size = 0;
++ from -= subdev->size;
++ continue;
++ }
++ if (from + len > subdev->size)
++ /* First part goes into this subdev */
++ size = subdev->size - from;
++ else
++ /* Entire transaction goes into this subdev */
++ size = len;
++
++ if (subdev->read_oob)
++ err = subdev->read_oob(subdev, from, size,
++ &retsize, buf);
++ else
++ err = -EINVAL;
++
++ if (err)
++ break;
++
++ *retlen += retsize;
++ len -= size;
++ if (len == 0)
++ break;
++
++ err = -EINVAL;
++ buf += size;
++ from = 0;
++ }
++ return err;
++}
++
++static int
++concat_write_oob(struct mtd_info *mtd, loff_t to, size_t len,
++ size_t * retlen, const u_char * buf)
++{
++ struct mtd_concat *concat = CONCAT(mtd);
++ int err = -EINVAL;
++ int i;
++
++ if (!(mtd->flags & MTD_WRITEABLE))
++ return -EROFS;
++
++ *retlen = 0;
++
++ for (i = 0; i < concat->num_subdev; i++) {
++ struct mtd_info *subdev = concat->subdev[i];
++ size_t size, retsize;
++
++ if (to >= subdev->size) {
++ size = 0;
++ to -= subdev->size;
++ continue;
++ }
++ if (to + len > subdev->size)
++ size = subdev->size - to;
++ else
++ size = len;
++
++ if (!(subdev->flags & MTD_WRITEABLE))
++ err = -EROFS;
++ else if (subdev->write_oob)
++ err = subdev->write_oob(subdev, to, size, &retsize,
++ buf);
++ else
++ err = -EINVAL;
++
++ if (err)
++ break;
++
++ *retlen += retsize;
++ len -= size;
++ if (len == 0)
++ break;
++
++ err = -EINVAL;
++ buf += size;
++ to = 0;
++ }
++ return err;
++}
++
++static void concat_erase_callback(struct erase_info *instr)
++{
++ wake_up((wait_queue_head_t *) instr->priv);
+ }
+
+ static int concat_dev_erase(struct mtd_info *mtd, struct erase_info *erase)
+@@ -160,18 +365,18 @@
+
+ erase->mtd = mtd;
+ erase->callback = concat_erase_callback;
+- erase->priv = (unsigned long)&waitq;
++ erase->priv = (unsigned long) &waitq;
+
+ /*
+ * FIXME: Allow INTERRUPTIBLE. Which means
+ * not having the wait_queue head on the stack.
+ */
+ err = mtd->erase(mtd, erase);
+- if (!err)
+- {
++ if (!err) {
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ add_wait_queue(&waitq, &wait);
+- if (erase->state != MTD_ERASE_DONE && erase->state != MTD_ERASE_FAILED)
++ if (erase->state != MTD_ERASE_DONE
++ && erase->state != MTD_ERASE_FAILED)
+ schedule();
+ remove_wait_queue(&waitq, &wait);
+ set_current_state(TASK_RUNNING);
+@@ -181,7 +386,7 @@
+ return err;
+ }
+
+-static int concat_erase (struct mtd_info *mtd, struct erase_info *instr)
++static int concat_erase(struct mtd_info *mtd, struct erase_info *instr)
+ {
+ struct mtd_concat *concat = CONCAT(mtd);
+ struct mtd_info *subdev;
+@@ -192,10 +397,10 @@
+ if (!(mtd->flags & MTD_WRITEABLE))
+ return -EROFS;
+
+- if(instr->addr > concat->mtd.size)
++ if (instr->addr > concat->mtd.size)
+ return -EINVAL;
+
+- if(instr->len + instr->addr > concat->mtd.size)
++ if (instr->len + instr->addr > concat->mtd.size)
+ return -EINVAL;
+
+ /*
+@@ -204,23 +409,22 @@
+ * region info rather than looking at each particular sub-device
+ * in turn.
+ */
+- if (!concat->mtd.numeraseregions)
+- { /* the easy case: device has uniform erase block size */
+- if(instr->addr & (concat->mtd.erasesize - 1))
++ if (!concat->mtd.numeraseregions) {
++ /* the easy case: device has uniform erase block size */
++ if (instr->addr & (concat->mtd.erasesize - 1))
+ return -EINVAL;
+- if(instr->len & (concat->mtd.erasesize - 1))
++ if (instr->len & (concat->mtd.erasesize - 1))
+ return -EINVAL;
+- }
+- else
+- { /* device has variable erase size */
+- struct mtd_erase_region_info *erase_regions = concat->mtd.eraseregions;
++ } else {
++ /* device has variable erase size */
++ struct mtd_erase_region_info *erase_regions =
++ concat->mtd.eraseregions;
+
+ /*
+ * Find the erase region where the to-be-erased area begins:
+ */
+- for(i = 0; i < concat->mtd.numeraseregions &&
+- instr->addr >= erase_regions[i].offset; i++)
+- ;
++ for (i = 0; i < concat->mtd.numeraseregions &&
++ instr->addr >= erase_regions[i].offset; i++) ;
+ --i;
+
+ /*
+@@ -228,25 +432,26 @@
+ * to-be-erased area begins. Verify that the starting
+ * offset is aligned to this region's erase size:
+ */
+- if (instr->addr & (erase_regions[i].erasesize-1))
++ if (instr->addr & (erase_regions[i].erasesize - 1))
+ return -EINVAL;
+
+ /*
+ * now find the erase region where the to-be-erased area ends:
+ */
+- for(; i < concat->mtd.numeraseregions &&
+- (instr->addr + instr->len) >= erase_regions[i].offset ; ++i)
+- ;
++ for (; i < concat->mtd.numeraseregions &&
++ (instr->addr + instr->len) >= erase_regions[i].offset;
++ ++i) ;
+ --i;
+ /*
+ * check if the ending offset is aligned to this region's erase size
+ */
+- if ((instr->addr + instr->len) & (erase_regions[i].erasesize-1))
++ if ((instr->addr + instr->len) & (erase_regions[i].erasesize -
++ 1))
+ return -EINVAL;
+ }
+
+ /* make a local copy of instr to avoid modifying the caller's struct */
+- erase = kmalloc(sizeof(struct erase_info),GFP_KERNEL);
++ erase = kmalloc(sizeof (struct erase_info), GFP_KERNEL);
+
+ if (!erase)
+ return -ENOMEM;
+@@ -258,39 +463,40 @@
+ * find the subdevice where the to-be-erased area begins, adjust
+ * starting offset to be relative to the subdevice start
+ */
+- for(i = 0; i < concat->num_subdev; i++)
+- {
++ for (i = 0; i < concat->num_subdev; i++) {
+ subdev = concat->subdev[i];
+- if(subdev->size <= erase->addr)
++ if (subdev->size <= erase->addr)
+ erase->addr -= subdev->size;
+ else
+ break;
+ }
+- if(i >= concat->num_subdev) /* must never happen since size */
+- BUG(); /* limit has been verified above */
++
++ /* must never happen since size limit has been verified above */
++ if (i >= concat->num_subdev)
++ BUG();
+
+ /* now do the erase: */
+ err = 0;
+- for(;length > 0; i++) /* loop for all subevices affected by this request */
+- {
++ for (; length > 0; i++) {
++ /* loop for all subdevices affected by this request */
+ subdev = concat->subdev[i]; /* get current subdevice */
+
+ /* limit length to subdevice's size: */
+- if(erase->addr + length > subdev->size)
++ if (erase->addr + length > subdev->size)
+ erase->len = subdev->size - erase->addr;
+ else
+ erase->len = length;
+
+- if (!(subdev->flags & MTD_WRITEABLE))
+- {
++ if (!(subdev->flags & MTD_WRITEABLE)) {
+ err = -EROFS;
+ break;
+ }
+ length -= erase->len;
+- if ((err = concat_dev_erase(subdev, erase)))
+- {
+- if(err == -EINVAL) /* sanity check: must never happen since */
+- BUG(); /* block alignment has been checked above */
++ if ((err = concat_dev_erase(subdev, erase))) {
++ /* sanity check: should never happen since
++ * block alignment has been checked above */
++ if (err == -EINVAL)
++ BUG();
+ break;
+ }
+ /*
+@@ -313,7 +519,7 @@
+ return 0;
+ }
+
+-static int concat_lock (struct mtd_info *mtd, loff_t ofs, size_t len)
++static int concat_lock(struct mtd_info *mtd, loff_t ofs, size_t len)
+ {
+ struct mtd_concat *concat = CONCAT(mtd);
+ int i, err = -EINVAL;
+@@ -321,18 +527,15 @@
+ if ((len + ofs) > mtd->size)
+ return -EINVAL;
+
+- for(i = 0; i < concat->num_subdev; i++)
+- {
++ for (i = 0; i < concat->num_subdev; i++) {
+ struct mtd_info *subdev = concat->subdev[i];
+ size_t size;
+
+- if (ofs >= subdev->size)
+- {
++ if (ofs >= subdev->size) {
+ size = 0;
+ ofs -= subdev->size;
++ continue;
+ }
+- else
+- {
+ if (ofs + len > subdev->size)
+ size = subdev->size - ofs;
+ else
+@@ -340,21 +543,21 @@
+
+ err = subdev->lock(subdev, ofs, size);
+
+- if(err)
++ if (err)
+ break;
+
+ len -= size;
+- if(len == 0)
++ if (len == 0)
+ break;
+
+ err = -EINVAL;
+ ofs = 0;
+ }
+- }
++
+ return err;
+ }
+
+-static int concat_unlock (struct mtd_info *mtd, loff_t ofs, size_t len)
++static int concat_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
+ {
+ struct mtd_concat *concat = CONCAT(mtd);
+ int i, err = 0;
+@@ -362,18 +565,15 @@
+ if ((len + ofs) > mtd->size)
+ return -EINVAL;
+
+- for(i = 0; i < concat->num_subdev; i++)
+- {
++ for (i = 0; i < concat->num_subdev; i++) {
+ struct mtd_info *subdev = concat->subdev[i];
+ size_t size;
+
+- if (ofs >= subdev->size)
+- {
++ if (ofs >= subdev->size) {
+ size = 0;
+ ofs -= subdev->size;
++ continue;
+ }
+- else
+- {
+ if (ofs + len > subdev->size)
+ size = subdev->size - ofs;
+ else
+@@ -381,17 +581,17 @@
+
+ err = subdev->unlock(subdev, ofs, size);
+
+- if(err)
++ if (err)
+ break;
+
+ len -= size;
+- if(len == 0)
++ if (len == 0)
+ break;
+
+ err = -EINVAL;
+ ofs = 0;
+ }
+- }
++
+ return err;
+ }
+
+@@ -400,8 +600,7 @@
+ struct mtd_concat *concat = CONCAT(mtd);
+ int i;
+
+- for(i = 0; i < concat->num_subdev; i++)
+- {
++ for (i = 0; i < concat->num_subdev; i++) {
+ struct mtd_info *subdev = concat->subdev[i];
+ subdev->sync(subdev);
+ }
+@@ -412,10 +611,9 @@
+ struct mtd_concat *concat = CONCAT(mtd);
+ int i, rc = 0;
+
+- for(i = 0; i < concat->num_subdev; i++)
+- {
++ for (i = 0; i < concat->num_subdev; i++) {
+ struct mtd_info *subdev = concat->subdev[i];
+- if((rc = subdev->suspend(subdev)) < 0)
++ if ((rc = subdev->suspend(subdev)) < 0)
+ return rc;
+ }
+ return rc;
+@@ -426,8 +624,7 @@
+ struct mtd_concat *concat = CONCAT(mtd);
+ int i;
+
+- for(i = 0; i < concat->num_subdev; i++)
+- {
++ for (i = 0; i < concat->num_subdev; i++) {
+ struct mtd_info *subdev = concat->subdev[i];
+ subdev->resume(subdev);
+ }
+@@ -439,11 +636,10 @@
+ * stored to *new_dev upon success. This function does _not_
+ * register any devices: this is the caller's responsibility.
+ */
+-struct mtd_info *mtd_concat_create(
+- struct mtd_info *subdev[], /* subdevices to concatenate */
++struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to concatenate */
+ int num_devs, /* number of subdevices */
+- char *name) /* name for the new device */
+-{
++ char *name)
++{ /* name for the new device */
+ int i;
+ size_t size;
+ struct mtd_concat *concat;
+@@ -451,21 +647,21 @@
+ int num_erase_region;
+
+ printk(KERN_NOTICE "Concatenating MTD devices:\n");
+- for(i = 0; i < num_devs; i++)
++ for (i = 0; i < num_devs; i++)
+ printk(KERN_NOTICE "(%d): \"%s\"\n", i, subdev[i]->name);
+ printk(KERN_NOTICE "into device \"%s\"\n", name);
+
+ /* allocate the device structure */
+ size = SIZEOF_STRUCT_MTD_CONCAT(num_devs);
+- concat = kmalloc (size, GFP_KERNEL);
+- if(!concat)
+- {
+- printk ("memory allocation error while creating concatenated device \"%s\"\n",
++ concat = kmalloc(size, GFP_KERNEL);
++ if (!concat) {
++ printk
++ ("memory allocation error while creating concatenated device \"%s\"\n",
+ name);
+ return NULL;
+ }
+ memset(concat, 0, size);
+- concat->subdev = (struct mtd_info **)(concat + 1);
++ concat->subdev = (struct mtd_info **) (concat + 1);
+
+ /*
+ * Set up the new "super" device's MTD object structure, check for
+@@ -479,39 +675,53 @@
+ concat->mtd.oobsize = subdev[0]->oobsize;
+ concat->mtd.ecctype = subdev[0]->ecctype;
+ concat->mtd.eccsize = subdev[0]->eccsize;
++ if (subdev[0]->read_ecc)
++ concat->mtd.read_ecc = concat_read_ecc;
++ if (subdev[0]->write_ecc)
++ concat->mtd.write_ecc = concat_write_ecc;
++ if (subdev[0]->read_oob)
++ concat->mtd.read_oob = concat_read_oob;
++ if (subdev[0]->write_oob)
++ concat->mtd.write_oob = concat_write_oob;
+
+ concat->subdev[0] = subdev[0];
+
+- for(i = 1; i < num_devs; i++)
+- {
+- if(concat->mtd.type != subdev[i]->type)
+- {
++ for (i = 1; i < num_devs; i++) {
++ if (concat->mtd.type != subdev[i]->type) {
+ kfree(concat);
+- printk ("Incompatible device type on \"%s\"\n", subdev[i]->name);
++ printk("Incompatible device type on \"%s\"\n",
++ subdev[i]->name);
+ return NULL;
+ }
+- if(concat->mtd.flags != subdev[i]->flags)
+- { /*
+- * Expect all flags except MTD_WRITEABLE to be equal on
+- * all subdevices.
++ if (concat->mtd.flags != subdev[i]->flags) {
++ /*
++ * Expect all flags except MTD_WRITEABLE to be
++ * equal on all subdevices.
+ */
+- if((concat->mtd.flags ^ subdev[i]->flags) & ~MTD_WRITEABLE)
+- {
++ if ((concat->mtd.flags ^ subdev[i]->
++ flags) & ~MTD_WRITEABLE) {
+ kfree(concat);
+- printk ("Incompatible device flags on \"%s\"\n", subdev[i]->name);
++ printk("Incompatible device flags on \"%s\"\n",
++ subdev[i]->name);
+ return NULL;
+- }
+- else /* if writeable attribute differs, make super device writeable */
+- concat->mtd.flags |= subdev[i]->flags & MTD_WRITEABLE;
++ } else
++ /* if writeable attribute differs,
++ make super device writeable */
++ concat->mtd.flags |=
++ subdev[i]->flags & MTD_WRITEABLE;
+ }
+ concat->mtd.size += subdev[i]->size;
+- if(concat->mtd.oobblock != subdev[i]->oobblock ||
++ if (concat->mtd.oobblock != subdev[i]->oobblock ||
+ concat->mtd.oobsize != subdev[i]->oobsize ||
+ concat->mtd.ecctype != subdev[i]->ecctype ||
+- concat->mtd.eccsize != subdev[i]->eccsize)
+- {
++ concat->mtd.eccsize != subdev[i]->eccsize ||
++ !concat->mtd.read_ecc != !subdev[i]->read_ecc ||
++ !concat->mtd.write_ecc != !subdev[i]->write_ecc ||
++ !concat->mtd.read_oob != !subdev[i]->read_oob ||
++ !concat->mtd.write_oob != !subdev[i]->write_oob) {
+ kfree(concat);
+- printk ("Incompatible OOB or ECC data on \"%s\"\n", subdev[i]->name);
++ printk("Incompatible OOB or ECC data on \"%s\"\n",
++ subdev[i]->name);
+ return NULL;
+ }
+ concat->subdev[i] = subdev[i];
+@@ -535,7 +745,6 @@
+ concat->mtd.suspend = concat_suspend;
+ concat->mtd.resume = concat_resume;
+
+-
+ /*
+ * Combine the erase block size info of the subdevices:
+ *
+@@ -544,44 +753,44 @@
+ */
+ max_erasesize = curr_erasesize = subdev[0]->erasesize;
+ num_erase_region = 1;
+- for(i = 0; i < num_devs; i++)
+- {
+- if(subdev[i]->numeraseregions == 0)
+- { /* current subdevice has uniform erase size */
+- if(subdev[i]->erasesize != curr_erasesize)
+- { /* if it differs from the last subdevice's erase size, count it */
++ for (i = 0; i < num_devs; i++) {
++ if (subdev[i]->numeraseregions == 0) {
++ /* current subdevice has uniform erase size */
++ if (subdev[i]->erasesize != curr_erasesize) {
++ /* if it differs from the last subdevice's erase size, count it */
+ ++num_erase_region;
+ curr_erasesize = subdev[i]->erasesize;
+- if(curr_erasesize > max_erasesize)
++ if (curr_erasesize > max_erasesize)
+ max_erasesize = curr_erasesize;
+ }
+- }
+- else
+- { /* current subdevice has variable erase size */
++ } else {
++ /* current subdevice has variable erase size */
+ int j;
+- for(j = 0; j < subdev[i]->numeraseregions; j++)
+- { /* walk the list of erase regions, count any changes */
+- if(subdev[i]->eraseregions[j].erasesize != curr_erasesize)
+- {
++ for (j = 0; j < subdev[i]->numeraseregions; j++) {
++
++ /* walk the list of erase regions, count any changes */
++ if (subdev[i]->eraseregions[j].erasesize !=
++ curr_erasesize) {
+ ++num_erase_region;
+- curr_erasesize = subdev[i]->eraseregions[j].erasesize;
+- if(curr_erasesize > max_erasesize)
++ curr_erasesize =
++ subdev[i]->eraseregions[j].
++ erasesize;
++ if (curr_erasesize > max_erasesize)
+ max_erasesize = curr_erasesize;
+ }
+ }
+ }
+ }
+
+- if(num_erase_region == 1)
+- { /*
++ if (num_erase_region == 1) {
++ /*
+ * All subdevices have the same uniform erase size.
+ * This is easy:
+ */
+ concat->mtd.erasesize = curr_erasesize;
+ concat->mtd.numeraseregions = 0;
+- }
+- else
+- { /*
++ } else {
++ /*
+ * erase block size varies across the subdevices: allocate
+ * space to store the data describing the variable erase regions
+ */
+@@ -590,12 +799,13 @@
+
+ concat->mtd.erasesize = max_erasesize;
+ concat->mtd.numeraseregions = num_erase_region;
+- concat->mtd.eraseregions = erase_region_p = kmalloc (
+- num_erase_region * sizeof(struct mtd_erase_region_info), GFP_KERNEL);
+- if(!erase_region_p)
+- {
++ concat->mtd.eraseregions = erase_region_p =
++ kmalloc(num_erase_region *
++ sizeof (struct mtd_erase_region_info), GFP_KERNEL);
++ if (!erase_region_p) {
+ kfree(concat);
+- printk ("memory allocation error while creating erase region list"
++ printk
++ ("memory allocation error while creating erase region list"
+ " for device \"%s\"\n", name);
+ return NULL;
+ }
+@@ -606,41 +816,48 @@
+ */
+ curr_erasesize = subdev[0]->erasesize;
+ begin = position = 0;
+- for(i = 0; i < num_devs; i++)
+- {
+- if(subdev[i]->numeraseregions == 0)
+- { /* current subdevice has uniform erase size */
+- if(subdev[i]->erasesize != curr_erasesize)
+- { /*
++ for (i = 0; i < num_devs; i++) {
++ if (subdev[i]->numeraseregions == 0) {
++ /* current subdevice has uniform erase size */
++ if (subdev[i]->erasesize != curr_erasesize) {
++ /*
+ * fill in an mtd_erase_region_info structure for the area
+ * we have walked so far:
+ */
+ erase_region_p->offset = begin;
+- erase_region_p->erasesize = curr_erasesize;
+- erase_region_p->numblocks = (position - begin) / curr_erasesize;
++ erase_region_p->erasesize =
++ curr_erasesize;
++ erase_region_p->numblocks =
++ (position - begin) / curr_erasesize;
+ begin = position;
+
+ curr_erasesize = subdev[i]->erasesize;
+ ++erase_region_p;
+ }
+ position += subdev[i]->size;
+- }
+- else
+- { /* current subdevice has variable erase size */
++ } else {
++ /* current subdevice has variable erase size */
+ int j;
+- for(j = 0; j < subdev[i]->numeraseregions; j++)
+- { /* walk the list of erase regions, count any changes */
+- if(subdev[i]->eraseregions[j].erasesize != curr_erasesize)
+- {
++ for (j = 0; j < subdev[i]->numeraseregions; j++) {
++ /* walk the list of erase regions, count any changes */
++ if (subdev[i]->eraseregions[j].
++ erasesize != curr_erasesize) {
+ erase_region_p->offset = begin;
+- erase_region_p->erasesize = curr_erasesize;
+- erase_region_p->numblocks = (position - begin) / curr_erasesize;
++ erase_region_p->erasesize =
++ curr_erasesize;
++ erase_region_p->numblocks =
++ (position -
++ begin) / curr_erasesize;
+ begin = position;
+
+- curr_erasesize = subdev[i]->eraseregions[j].erasesize;
++ curr_erasesize =
++ subdev[i]->eraseregions[j].
++ erasesize;
+ ++erase_region_p;
+ }
+- position += subdev[i]->eraseregions[j].numblocks * curr_erasesize;
++ position +=
++ subdev[i]->eraseregions[j].
++ numblocks * curr_erasesize;
+ }
+ }
+ }
+@@ -660,16 +877,14 @@
+ void mtd_concat_destroy(struct mtd_info *mtd)
+ {
+ struct mtd_concat *concat = CONCAT(mtd);
+- if(concat->mtd.numeraseregions)
++ if (concat->mtd.numeraseregions)
+ kfree(concat->mtd.eraseregions);
+ kfree(concat);
+ }
+
+-
+ EXPORT_SYMBOL(mtd_concat_create);
+ EXPORT_SYMBOL(mtd_concat_destroy);
+
+-
+ MODULE_LICENSE("GPL");
+ MODULE_AUTHOR("Robert Kaiser <rkaiser@sysgo.de>");
+ MODULE_DESCRIPTION("Generic support for concatenating of MTD devices");
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/mtdcore.c linux/drivers/mtd/mtdcore.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/mtdcore.c 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/mtdcore.c 2004-11-17 18:17:58.862340824 +0100
+@@ -1,5 +1,5 @@
+ /*
+- * $Id$
++ * $Id$
+ *
+ * Core registration and callback routines for MTD
+ * drivers and users.
+@@ -17,6 +17,7 @@
+ #include <linux/major.h>
+ #include <linux/fs.h>
+ #include <linux/ioctl.h>
++#include <linux/init.h>
+ #include <linux/mtd/compatmac.h>
+ #ifdef CONFIG_PROC_FS
+ #include <linux/proc_fs.h>
+@@ -24,9 +25,15 @@
+
+ #include <linux/mtd/mtd.h>
+
+-static DECLARE_MUTEX(mtd_table_mutex);
+-static struct mtd_info *mtd_table[MAX_MTD_DEVICES];
+-static struct mtd_notifier *mtd_notifiers = NULL;
++/* These are exported solely for the purpose of mtd_blkdevs.c. You
++ should not use them for _anything_ else */
++DECLARE_MUTEX(mtd_table_mutex);
++struct mtd_info *mtd_table[MAX_MTD_DEVICES];
++
++EXPORT_SYMBOL_GPL(mtd_table_mutex);
++EXPORT_SYMBOL_GPL(mtd_table);
++
++static LIST_HEAD(mtd_notifiers);
+
+ /**
+ * add_mtd_device - register an MTD device
+@@ -44,21 +51,28 @@
+
+ down(&mtd_table_mutex);
+
+- for (i=0; i< MAX_MTD_DEVICES; i++)
+- if (!mtd_table[i])
+- {
+- struct mtd_notifier *not=mtd_notifiers;
++ for (i=0; i < MAX_MTD_DEVICES; i++)
++ if (!mtd_table[i]) {
++ struct list_head *this;
+
+ mtd_table[i] = mtd;
+ mtd->index = i;
++ mtd->usecount = 0;
++
+ DEBUG(0, "mtd: Giving out device %d to %s\n",i, mtd->name);
+- while (not)
+- {
+- (*(not->add))(mtd);
+- not = not->next;
++ /* No need to get a refcount on the module containing
++ the notifier, since we hold the mtd_table_mutex */
++ list_for_each(this, &mtd_notifiers) {
++ struct mtd_notifier *not = list_entry(this, struct mtd_notifier, list);
++ not->add(mtd);
+ }
++
+ up(&mtd_table_mutex);
+- MOD_INC_USE_COUNT;
++ /* We _know_ we aren't being removed, because
++ our caller is still holding us here. So none
++ of this try_ nonsense, and no bitching about it
++ either. :) */
++ __module_get(THIS_MODULE);
+ return 0;
+ }
+
+@@ -78,29 +92,34 @@
+
+ int del_mtd_device (struct mtd_info *mtd)
+ {
+- struct mtd_notifier *not=mtd_notifiers;
+- int i;
++ int ret;
+
+ down(&mtd_table_mutex);
+
+- for (i=0; i < MAX_MTD_DEVICES; i++)
+- {
+- if (mtd_table[i] == mtd)
+- {
+- while (not)
+- {
+- (*(not->remove))(mtd);
+- not = not->next;
+- }
+- mtd_table[i] = NULL;
+- up (&mtd_table_mutex);
+- MOD_DEC_USE_COUNT;
+- return 0;
++ if (mtd_table[mtd->index] != mtd) {
++ ret = -ENODEV;
++ } else if (mtd->usecount) {
++ printk(KERN_NOTICE "Removing MTD device #%d (%s) with use count %d\n",
++ mtd->index, mtd->name, mtd->usecount);
++ ret = -EBUSY;
++ } else {
++ struct list_head *this;
++
++ /* No need to get a refcount on the module containing
++ the notifier, since we hold the mtd_table_mutex */
++ list_for_each(this, &mtd_notifiers) {
++ struct mtd_notifier *not = list_entry(this, struct mtd_notifier, list);
++ not->remove(mtd);
+ }
++
++ mtd_table[mtd->index] = NULL;
++
++ module_put(THIS_MODULE);
++ ret = 0;
+ }
+
+ up(&mtd_table_mutex);
+- return 1;
++ return ret;
+ }
+
+ /**
+@@ -118,10 +137,9 @@
+
+ down(&mtd_table_mutex);
+
+- new->next = mtd_notifiers;
+- mtd_notifiers = new;
++ list_add(&new->list, &mtd_notifiers);
+
+- MOD_INC_USE_COUNT;
++ __module_get(THIS_MODULE);
+
+ for (i=0; i< MAX_MTD_DEVICES; i++)
+ if (mtd_table[i])
+@@ -142,34 +160,24 @@
+
+ int unregister_mtd_user (struct mtd_notifier *old)
+ {
+- struct mtd_notifier **prev = &mtd_notifiers;
+- struct mtd_notifier *cur;
+ int i;
+
+ down(&mtd_table_mutex);
+
+- while ((cur = *prev)) {
+- if (cur == old) {
+- *prev = cur->next;
+-
+- MOD_DEC_USE_COUNT;
++ module_put(THIS_MODULE);
+
+ for (i=0; i< MAX_MTD_DEVICES; i++)
+ if (mtd_table[i])
+ old->remove(mtd_table[i]);
+
++ list_del(&old->list);
+ up(&mtd_table_mutex);
+ return 0;
+- }
+- prev = &cur->next;
+- }
+- up(&mtd_table_mutex);
+- return 1;
+ }
+
+
+ /**
+- * __get_mtd_device - obtain a validated handle for an MTD device
++ * get_mtd_device - obtain a validated handle for an MTD device
+ * @mtd: last known address of the required MTD device
+ * @num: internal device number of the required MTD device
+ *
+@@ -177,11 +185,10 @@
+ * table, if any. Given an address and num == -1, search the device table
+ * for a device with that address and return if it's still present. Given
+ * both, return the num'th driver only if its address matches. Return NULL
+- * if not. get_mtd_device() increases the use count, but
+- * __get_mtd_device() doesn't - you should generally use get_mtd_device().
++ * if not.
+ */
+
+-struct mtd_info *__get_mtd_device(struct mtd_info *mtd, int num)
++struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num)
+ {
+ struct mtd_info *ret = NULL;
+ int i;
+@@ -198,10 +205,27 @@
+ ret = NULL;
+ }
+
++ if (ret && !try_module_get(ret->owner))
++ ret = NULL;
++
++ if (ret)
++ ret->usecount++;
++
+ up(&mtd_table_mutex);
+ return ret;
+ }
+
++void put_mtd_device(struct mtd_info *mtd)
++{
++ int c;
++
++ down(&mtd_table_mutex);
++ c = --mtd->usecount;
++ up(&mtd_table_mutex);
++ BUG_ON(c < 0);
++
++ module_put(mtd->owner);
++}
+
+ /* default_mtd_writev - default mtd writev method for MTD devices that
+ * dont implement their own
+@@ -265,7 +289,8 @@
+
+ EXPORT_SYMBOL(add_mtd_device);
+ EXPORT_SYMBOL(del_mtd_device);
+-EXPORT_SYMBOL(__get_mtd_device);
++EXPORT_SYMBOL(get_mtd_device);
++EXPORT_SYMBOL(put_mtd_device);
+ EXPORT_SYMBOL(register_mtd_user);
+ EXPORT_SYMBOL(unregister_mtd_user);
+ EXPORT_SYMBOL(default_mtd_writev);
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/mtdpart.c linux/drivers/mtd/mtdpart.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/mtdpart.c 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/mtdpart.c 2004-11-17 18:17:58.863340672 +0100
+@@ -5,7 +5,7 @@
+ *
+ * This code is GPL
+ *
+- * $Id$
++ * $Id$
+ *
+ * 02-21-2002 Thomas Gleixner <gleixner@autronix.de>
+ * added support for read_oob, write_oob
+@@ -16,10 +16,11 @@
+ #include <linux/kernel.h>
+ #include <linux/slab.h>
+ #include <linux/list.h>
+-
++#include <linux/config.h>
++#include <linux/kmod.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/partitions.h>
+-
++#include <linux/mtd/compatmac.h>
+
+ /* Our partition linked list */
+ static LIST_HEAD(mtd_partitions);
+@@ -54,8 +55,12 @@
+ len = 0;
+ else if (from + len > mtd->size)
+ len = mtd->size - from;
++ if (part->master->read_ecc == NULL)
+ return part->master->read (part->master, from + part->offset,
+ len, retlen, buf);
++ else
++ return part->master->read_ecc (part->master, from + part->offset,
++ len, retlen, buf, NULL, &mtd->oobinfo);
+ }
+
+ static int part_point (struct mtd_info *mtd, loff_t from, size_t len,
+@@ -78,9 +83,11 @@
+
+
+ static int part_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
+- size_t *retlen, u_char *buf, u_char *eccbuf, int oobsel)
++ size_t *retlen, u_char *buf, u_char *eccbuf, struct nand_oobinfo *oobsel)
+ {
+ struct mtd_part *part = PART(mtd);
++ if (oobsel == NULL)
++ oobsel = &mtd->oobinfo;
+ if (from >= mtd->size)
+ len = 0;
+ else if (from + len > mtd->size)
+@@ -113,7 +120,7 @@
+ size_t *retlen, u_char *buf)
+ {
+ struct mtd_part *part = PART(mtd);
+- return part->master->read_user_prot_reg (part->master, from,
++ return part->master->read_fact_prot_reg (part->master, from,
+ len, retlen, buf);
+ }
+
+@@ -127,17 +134,24 @@
+ len = 0;
+ else if (to + len > mtd->size)
+ len = mtd->size - to;
++ if (part->master->write_ecc == NULL)
+ return part->master->write (part->master, to + part->offset,
+ len, retlen, buf);
++ else
++ return part->master->write_ecc (part->master, to + part->offset,
++ len, retlen, buf, NULL, &mtd->oobinfo);
++
+ }
+
+ static int part_write_ecc (struct mtd_info *mtd, loff_t to, size_t len,
+ size_t *retlen, const u_char *buf,
+- u_char *eccbuf, int oobsel)
++ u_char *eccbuf, struct nand_oobinfo *oobsel)
+ {
+ struct mtd_part *part = PART(mtd);
+ if (!(mtd->flags & MTD_WRITEABLE))
+ return -EROFS;
++ if (oobsel == NULL)
++ oobsel = &mtd->oobinfo;
+ if (to >= mtd->size)
+ len = 0;
+ else if (to + len > mtd->size)
+@@ -174,25 +188,37 @@
+ struct mtd_part *part = PART(mtd);
+ if (!(mtd->flags & MTD_WRITEABLE))
+ return -EROFS;
++ if (part->master->writev_ecc == NULL)
+ return part->master->writev (part->master, vecs, count,
+ to + part->offset, retlen);
++ else
++ return part->master->writev_ecc (part->master, vecs, count,
++ to + part->offset, retlen,
++ NULL, &mtd->oobinfo);
+ }
+
+ static int part_readv (struct mtd_info *mtd, struct iovec *vecs,
+ unsigned long count, loff_t from, size_t *retlen)
+ {
+ struct mtd_part *part = PART(mtd);
++ if (part->master->readv_ecc == NULL)
+ return part->master->readv (part->master, vecs, count,
+ from + part->offset, retlen);
++ else
++ return part->master->readv_ecc (part->master, vecs, count,
++ from + part->offset, retlen,
++ NULL, &mtd->oobinfo);
+ }
+
+ static int part_writev_ecc (struct mtd_info *mtd, const struct iovec *vecs,
+ unsigned long count, loff_t to, size_t *retlen,
+- u_char *eccbuf, int oobsel)
++ u_char *eccbuf, struct nand_oobinfo *oobsel)
+ {
+ struct mtd_part *part = PART(mtd);
+ if (!(mtd->flags & MTD_WRITEABLE))
+ return -EROFS;
++ if (oobsel == NULL)
++ oobsel = &mtd->oobinfo;
+ return part->master->writev_ecc (part->master, vecs, count,
+ to + part->offset, retlen,
+ eccbuf, oobsel);
+@@ -200,9 +226,11 @@
+
+ static int part_readv_ecc (struct mtd_info *mtd, struct iovec *vecs,
+ unsigned long count, loff_t from, size_t *retlen,
+- u_char *eccbuf, int oobsel)
++ u_char *eccbuf, struct nand_oobinfo *oobsel)
+ {
+ struct mtd_part *part = PART(mtd);
++ if (oobsel == NULL)
++ oobsel = &mtd->oobinfo;
+ return part->master->readv_ecc (part->master, vecs, count,
+ from + part->offset, retlen,
+ eccbuf, oobsel);
+@@ -288,7 +316,7 @@
+ */
+
+ int add_mtd_partitions(struct mtd_info *master,
+- struct mtd_partition *parts,
++ const struct mtd_partition *parts,
+ int nbparts)
+ {
+ struct mtd_part *slave;
+@@ -321,7 +349,7 @@
+
+ slave->mtd.name = parts[i].name;
+ slave->mtd.bank_size = master->bank_size;
+- slave->mtd.module = master->module;
++ slave->mtd.owner = master->owner;
+
+ slave->mtd.read = part_read;
+ slave->mtd.write = part_write;
+@@ -452,6 +480,75 @@
+ EXPORT_SYMBOL(add_mtd_partitions);
+ EXPORT_SYMBOL(del_mtd_partitions);
+
++static spinlock_t part_parser_lock = SPIN_LOCK_UNLOCKED;
++static LIST_HEAD(part_parsers);
++
++struct mtd_part_parser *get_partition_parser(const char *name)
++{
++ struct list_head *this;
++ void *ret = NULL;
++ spin_lock(&part_parser_lock);
++
++ list_for_each(this, &part_parsers) {
++ struct mtd_part_parser *p = list_entry(this, struct mtd_part_parser, list);
++
++ if (!strcmp(p->name, name) && try_module_get(p->owner)) {
++ ret = p;
++ break;
++ }
++ }
++ spin_unlock(&part_parser_lock);
++
++ return ret;
++}
++
++int register_mtd_parser(struct mtd_part_parser *p)
++{
++ spin_lock(&part_parser_lock);
++ list_add(&p->list, &part_parsers);
++ spin_unlock(&part_parser_lock);
++
++ return 0;
++}
++
++int deregister_mtd_parser(struct mtd_part_parser *p)
++{
++ spin_lock(&part_parser_lock);
++ list_del(&p->list);
++ spin_unlock(&part_parser_lock);
++ return 0;
++}
++
++int parse_mtd_partitions(struct mtd_info *master, const char **types,
++ struct mtd_partition **pparts, unsigned long origin)
++{
++ struct mtd_part_parser *parser;
++ int ret = 0;
++
++ for ( ; ret <= 0 && *types; types++) {
++ parser = get_partition_parser(*types);
++#ifdef CONFIG_KMOD
++ if (!parser && !request_module("%s", *types))
++ parser = get_partition_parser(*types);
++#endif
++ if (!parser) {
++ printk(KERN_NOTICE "%s partition parsing not available\n",
++ *types);
++ continue;
++ }
++ ret = (*parser->parse_fn)(master, pparts, origin);
++ if (ret > 0) {
++ printk(KERN_NOTICE "%d %s partitions found on MTD device %s\n",
++ ret, parser->name, master->name);
++ }
++ put_partition_parser(parser);
++ }
++ return ret;
++}
++
++EXPORT_SYMBOL_GPL(parse_mtd_partitions);
++EXPORT_SYMBOL_GPL(register_mtd_parser);
++EXPORT_SYMBOL_GPL(deregister_mtd_parser);
+
+ MODULE_LICENSE("GPL");
+ MODULE_AUTHOR("Nicolas Pitre <nico@cam.org>");
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/nand/Config.in linux/drivers/mtd/nand/Config.in
+--- linux-mips-2.4.24-pre2/drivers/mtd/nand/Config.in 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/nand/Config.in 2004-11-17 18:17:59.159295680 +0100
+@@ -1,6 +1,6 @@
+ # drivers/mtd/nand/Config.in
+
+-# $Id$
++# $Id$
+
+ mainmenu_option next_comment
+
+@@ -11,26 +11,27 @@
+ bool ' Verify NAND page writes' CONFIG_MTD_NAND_VERIFY_WRITE
+ fi
+
+-if [ "$CONFIG_ARM" = "y" -a "$CONFIG_ARCH_P720T" = "y" ]; then
+- dep_tristate ' NAND Flash device on SPIA board' CONFIG_MTD_NAND_SPIA $CONFIG_MTD_NAND
++if [ "$CONFIG_ARM" = "y" ]; then
++ dep_tristate ' NAND Flash device on SPIA board' CONFIG_MTD_NAND_SPIA $CONFIG_MTD_NAND $CONFIG_ARCH_P720T
++ dep_tristate ' NAND Flash device on TOTO board' CONFIG_MTD_NAND_TOTO $CONFIG_MTD_NAND $CONFIG_ARCH_OMAP
++ dep_tristate ' SmartMedia Card on AUTCPU12 board' CONFIG_MTD_NAND_AUTCPU12 $CONFIG_MTD_NAND $CONFIG_ARCH_AUTCPU12
++ dep_tristate ' NAND Flash device on EDP7312 board' CONFIG_MTD_NAND_EDB7312 $CONFIG_MTD_NAND $CONFIG_ARCH_EDB7312
+ fi
+
+-if [ "$CONFIG_ARCH_AUTCPU12" = "y" ]; then
+- dep_tristate ' SmartMedia Card on AUTCPU12 board' CONFIG_MTD_NAND_AUTCPU12 $CONFIG_MTD_NAND
+-fi
+-
+-if [ "$CONFIG_ARCH_EDB7312" = "y" ]; then
+- dep_tristate ' NAND Flash device on EDP7312 board' CONFIG_MTD_NAND_EDB7312 $CONFIG_MTD_NAND
+-fi
+-
+-if [ "$CONFIG_MTD_DOC2001" = "y" -o "$CONFIG_MTD_DOC2000" = "y" -o "$CONFIG_MTD_NAND" = "y" ]; then
++if [ "$CONFIG_MTD_DOC2001PLUS" = "y" -o "$CONFIG_MTD_DOC2001" = "y" -o "$CONFIG_MTD_DOC2000" = "y" -o "$CONFIG_MTD_NAND" = "y" ]; then
+ define_bool CONFIG_MTD_NAND_IDS y
++else
++ if [ "$CONFIG_MTD_DOC2001PLUS" = "m" -o "$CONFIG_MTD_DOC2001" = "m" -o "$CONFIG_MTD_DOC2000" = "m" -o "$CONFIG_MTD_NAND" = "m" ]; then
++ define_bool CONFIG_MTD_NAND_IDS m
++ fi
+ fi
+
+-if [ "$CONFIG_MTD_NAND_IDS" != "y" ]; then
+-if [ "$CONFIG_MTD_DOC2001" = "m" -o "$CONFIG_MTD_DOC2000" = "m" -o "$CONFIG_MTD_NAND" = "m" ]; then
+- define_bool CONFIG_MTD_NAND_IDS m
++if [ "$CONFIG_TOSHIBA_RBTX4925" = "y" ]; then
++ dep_tristate ' SmartMedia Card on Toshiba RBTX4925 reference board' CONFIG_MTD_NAND_TX4925NDFMC $CONFIG_MTD_NAND $CONFIG_TOSHIBA_RBTX4925_MPLEX_NAND
+ fi
++
++if [ "$CONFIG_TOSHIBA_RBTX4938" = "y" ]; then
++ dep_tristate ' NAND Flash device on Toshiba RBTX4938 reference board' CONFIG_MTD_NAND_TX4938NDFMC $CONFIG_MTD_NAND $CONFIG_TOSHIBA_RBTX4938_MPLEX_NAND
+ fi
+
+ endmenu
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/nand/Makefile linux/drivers/mtd/nand/Makefile
+--- linux-mips-2.4.24-pre2/drivers/mtd/nand/Makefile 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/nand/Makefile 2004-11-17 18:17:59.161295376 +0100
+@@ -1,16 +1,20 @@
+ #
+ # linux/drivers/nand/Makefile
+ #
+-# $Id$
++# $Id$
+
++ifeq ($(PATCHLEVEL),4)
+ O_TARGET := nandlink.o
+-
+ export-objs := nand.o nand_ecc.o nand_ids.o
++endif
+
+ obj-$(CONFIG_MTD_NAND) += nand.o nand_ecc.o
+ obj-$(CONFIG_MTD_NAND_SPIA) += spia.o
++obj-$(CONFIG_MTD_NAND_TOTO) += toto.o
+ obj-$(CONFIG_MTD_NAND_AUTCPU12) += autcpu12.o
+ obj-$(CONFIG_MTD_NAND_EDB7312) += edb7312.o
++obj-$(CONFIG_MTD_NAND_TX4925NDFMC) += tx4925ndfmc.o
++obj-$(CONFIG_MTD_NAND_TX4938NDFMC) += tx4938ndfmc.o
+ obj-$(CONFIG_MTD_NAND_IDS) += nand_ids.o
+
+-include $(TOPDIR)/Rules.make
++-include $(TOPDIR)/Rules.make
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/nand/autcpu12.c linux/drivers/mtd/nand/autcpu12.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/nand/autcpu12.c 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/nand/autcpu12.c 2004-11-17 18:17:59.162295224 +0100
+@@ -4,9 +4,9 @@
+ * Copyright (c) 2002 Thomas Gleixner <tgxl@linutronix.de>
+ *
+ * Derived from drivers/mtd/spia.c
+- * Copyright (C) 2000 Steven J. Hill (sjhill@cotw.com)
++ * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
+ *
+- * $Id$
++ * $Id$
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+@@ -25,10 +25,10 @@
+ * added page_cache
+ *
+ * 10-06-2002 TG 128K card support added
+- *
+ */
+
+ #include <linux/slab.h>
++#include <linux/init.h>
+ #include <linux/module.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/nand.h>
+@@ -70,6 +70,7 @@
+ /*
+ * Define partitions for flash devices
+ */
++extern struct nand_oobinfo jffs2_oobinfo;
+
+ static struct mtd_partition partition_info16k[] = {
+ { name: "AUTCPU12 flash partition 1",
+@@ -95,7 +96,7 @@
+ size: 16 * SZ_1M },
+ { name: "AUTCPU12 flash partition 2",
+ offset: 16 * SZ_1M,
+- size: 48 * SZ_1M},
++ size: 48 * SZ_1M },
+ };
+
+ static struct mtd_partition partition_info128k[] = {
+@@ -104,7 +105,7 @@
+ size: 16 * SZ_1M },
+ { name: "AUTCPU12 flash partition 2",
+ offset: 16 * SZ_1M,
+- size: 112 * SZ_1M},
++ size: 112 * SZ_1M },
+ };
+
+ #define NUM_PARTITIONS16K 2
+@@ -114,7 +115,7 @@
+ /*
+ * hardware specific access to control-lines
+ */
+-void autcpu12_hwcontrol(int cmd)
++static void autcpu12_hwcontrol(struct mtd_info *mtd, int cmd)
+ {
+
+ switch(cmd){
+@@ -133,7 +134,7 @@
+ /*
+ * read device ready pin
+ */
+-int autcpu12_device_ready(void)
++int autcpu12_device_ready(struct mtd_info *mtd)
+ {
+
+ return ( (*(volatile unsigned char *) (autcpu12_io_base + autcpu12_pedr)) & AUTCPU12_SMC_RDY) ? 1 : 0;
+@@ -184,7 +185,7 @@
+ this->eccmode = NAND_ECC_SOFT;
+
+ /* Scan to find existance of the device */
+- if (nand_scan (autcpu12_mtd)) {
++ if (nand_scan (autcpu12_mtd, 1)) {
+ err = -ENXIO;
+ goto out_ior;
+ }
+@@ -197,15 +198,6 @@
+ goto out_ior;
+ }
+
+- /* Allocate memory for internal data buffer */
+- this->data_cache = kmalloc (sizeof(u_char) * (autcpu12_mtd->oobblock + autcpu12_mtd->oobsize), GFP_KERNEL);
+- if (!this->data_cache) {
+- printk ("Unable to allocate NAND data cache for AUTCPU12.\n");
+- err = -ENOMEM;
+- goto out_buf;
+- }
+- this->cache_page = -1;
+-
+ /* Register the partitions */
+ switch(autcpu12_mtd->size){
+ case SZ_16M: add_mtd_partitions(autcpu12_mtd, partition_info16k, NUM_PARTITIONS16K); break;
+@@ -215,13 +207,11 @@
+ default: {
+ printk ("Unsupported SmartMedia device\n");
+ err = -ENXIO;
+- goto out_cac;
++ goto out_buf;
+ }
+ }
+ goto out;
+
+-out_cac:
+- kfree (this->data_cache);
+ out_buf:
+ kfree (this->data_buf);
+ out_ior:
+@@ -250,7 +240,6 @@
+
+ /* Free internal data buffers */
+ kfree (this->data_buf);
+- kfree (this->data_cache);
+
+ /* unmap physical adress */
+ iounmap((void *)autcpu12_fio_base);
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/nand/diskonchip.c linux/drivers/mtd/nand/diskonchip.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/nand/diskonchip.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/mtd/nand/diskonchip.c 2004-11-17 18:17:59.000000000 +0100
+@@ -0,0 +1,534 @@
++/*
++ * drivers/mtd/nand/diskonchip.c
++ *
++ * (C) 2003 Red Hat, Inc.
++ *
++ * Author: David Woodhouse <dwmw2@infradead.org>
++ *
++ * Interface to generic NAND code for M-Systems DiskOnChip devices
++ *
++ * $Id$
++ */
++
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/sched.h>
++#include <linux/delay.h>
++#include <asm/io.h>
++
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/nand.h>
++#include <linux/mtd/doc2000.h>
++#include <linux/mtd/compatmac.h>
++
++struct doc_priv {
++ unsigned long virtadr;
++ unsigned long physadr;
++ u_char ChipID;
++ u_char CDSNControl;
++ int chips_per_floor; /* The number of chips detected on each floor */
++ int curfloor;
++ int curchip;
++};
++
++#define DoC_is_Millennium(doc) ((doc)->ChipID == DOC_ChipID_DocMil)
++#define DoC_is_2000(doc) ((doc)->ChipID == DOC_ChipID_Doc2k)
++
++static void doc200x_hwcontrol(struct mtd_info *mtd, int cmd);
++static void doc200x_select_chip(struct mtd_info *mtd, int chip);
++
++static int debug=0;
++MODULE_PARM(debug, "i");
++
++static int try_dword=1;
++MODULE_PARM(try_dword, "i");
++
++static void DoC_Delay(struct doc_priv *doc, unsigned short cycles)
++{
++ volatile char dummy;
++ int i;
++
++ for (i = 0; i < cycles; i++) {
++ if (DoC_is_Millennium(doc))
++ dummy = ReadDOC(doc->virtadr, NOP);
++ else
++ dummy = ReadDOC(doc->virtadr, DOCStatus);
++ }
++
++}
++/* DOC_WaitReady: Wait for RDY line to be asserted by the flash chip */
++static int _DoC_WaitReady(struct doc_priv *doc)
++{
++ unsigned long docptr = doc->virtadr;
++ unsigned long timeo = jiffies + (HZ * 10);
++
++ if(debug) printk("_DoC_WaitReady...\n");
++ /* Out-of-line routine to wait for chip response */
++ while (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B)) {
++ if (time_after(jiffies, timeo)) {
++ printk("_DoC_WaitReady timed out.\n");
++ return -EIO;
++ }
++ udelay(1);
++ cond_resched();
++ }
++
++ return 0;
++}
++
++static inline int DoC_WaitReady(struct doc_priv *doc)
++{
++ unsigned long docptr = doc->virtadr;
++ int ret = 0;
++
++ DoC_Delay(doc, 4);
++
++ if (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B))
++ /* Call the out-of-line routine to wait */
++ ret = _DoC_WaitReady(doc);
++
++ DoC_Delay(doc, 2);
++ if(debug) printk("DoC_WaitReady OK\n");
++ return ret;
++}
++
++static void doc2000_write_byte(struct mtd_info *mtd, u_char datum)
++{
++ struct nand_chip *this = mtd->priv;
++ struct doc_priv *doc = (void *)this->priv;
++ unsigned long docptr = doc->virtadr;
++
++ if(debug)printk("write_byte %02x\n", datum);
++ WriteDOC(datum, docptr, CDSNSlowIO);
++ WriteDOC(datum, docptr, 2k_CDSN_IO);
++}
++
++static u_char doc2000_read_byte(struct mtd_info *mtd)
++{
++ struct nand_chip *this = mtd->priv;
++ struct doc_priv *doc = (void *)this->priv;
++ unsigned long docptr = doc->virtadr;
++
++ ReadDOC(docptr, CDSNSlowIO);
++ u_char ret = ReadDOC(docptr, 2k_CDSN_IO);
++ if (debug) printk("read_byte returns %02x\n", ret);
++ return ret;
++}
++static void doc2000_writebuf(struct mtd_info *mtd,
++ const u_char *buf, int len)
++{
++ struct nand_chip *this = mtd->priv;
++ struct doc_priv *doc = (void *)this->priv;
++ unsigned long docptr = doc->virtadr;
++ int i;
++ if (debug)printk("writebuf of %d bytes: ", len);
++ for (i=0; i < len; i++) {
++ WriteDOC_(buf[i], docptr, DoC_2k_CDSN_IO + i);
++ if (debug && i < 16)
++ printk("%02x ", buf[i]);
++ }
++ if (debug) printk("\n");
++}
++
++static void doc2000_readbuf(struct mtd_info *mtd,
++ u_char *buf, int len)
++{
++ struct nand_chip *this = mtd->priv;
++ struct doc_priv *doc = (void *)this->priv;
++ unsigned long docptr = doc->virtadr;
++ int i;
++
++ if (debug)printk("readbuf of %d bytes: ", len);
++
++ for (i=0; i < len; i++) {
++ buf[i] = ReadDOC(docptr, 2k_CDSN_IO + i);
++ }
++}
++
++static void doc2000_readbuf_dword(struct mtd_info *mtd,
++ u_char *buf, int len)
++{
++ struct nand_chip *this = mtd->priv;
++ struct doc_priv *doc = (void *)this->priv;
++ unsigned long docptr = doc->virtadr;
++ int i;
++
++ if (debug) printk("readbuf_dword of %d bytes: ", len);
++
++ if (unlikely((((unsigned long)buf)|len) & 3)) {
++ for (i=0; i < len; i++) {
++ *(uint8_t *)(&buf[i]) = ReadDOC(docptr, 2k_CDSN_IO + i);
++ }
++ } else {
++ for (i=0; i < len; i+=4) {
++ *(uint32_t*)(&buf[i]) = readl(docptr + DoC_2k_CDSN_IO + i);
++ }
++ }
++}
++
++static int doc2000_verifybuf(struct mtd_info *mtd,
++ const u_char *buf, int len)
++{
++ struct nand_chip *this = mtd->priv;
++ struct doc_priv *doc = (void *)this->priv;
++ unsigned long docptr = doc->virtadr;
++ int i;
++
++ for (i=0; i < len; i++)
++ if (buf[i] != ReadDOC(docptr, 2k_CDSN_IO))
++ return i;
++ return 0;
++}
++
++static uint16_t doc200x_ident_chip(struct mtd_info *mtd, int nr)
++{
++ struct nand_chip *this = mtd->priv;
++ struct doc_priv *doc = (void *)this->priv;
++ uint16_t ret;
++
++ doc200x_select_chip(mtd, nr);
++ doc200x_hwcontrol(mtd, NAND_CTL_SETCLE);
++ this->write_byte(mtd, NAND_CMD_READID);
++ doc200x_hwcontrol(mtd, NAND_CTL_CLRCLE);
++ doc200x_hwcontrol(mtd, NAND_CTL_SETALE);
++ this->write_byte(mtd, 0);
++ doc200x_hwcontrol(mtd, NAND_CTL_CLRALE);
++
++ ret = this->read_byte(mtd) << 8;
++ ret |= this->read_byte(mtd);
++
++ if (doc->ChipID == DOC_ChipID_Doc2k && try_dword && !nr) {
++ /* First chip probe. See if we get same results by 32-bit access */
++ union {
++ uint32_t dword;
++ uint8_t byte[4];
++ } ident;
++ struct nand_chip *this = mtd->priv;
++ struct doc_priv *doc = (void *)this->priv;
++ unsigned long docptr = doc->virtadr;
++
++ doc200x_hwcontrol(mtd, NAND_CTL_SETCLE);
++ doc2000_write_byte(mtd, NAND_CMD_READID);
++ doc200x_hwcontrol(mtd, NAND_CTL_CLRCLE);
++ doc200x_hwcontrol(mtd, NAND_CTL_SETALE);
++ doc2000_write_byte(mtd, 0);
++ doc200x_hwcontrol(mtd, NAND_CTL_CLRALE);
++
++ ident.dword = readl(docptr + DoC_2k_CDSN_IO);
++ if (((ident.byte[0] << 8) | ident.byte[1]) == ret) {
++ printk(KERN_INFO "DiskOnChip 2000 responds to DWORD access\n");
++ this->read_buf = &doc2000_readbuf_dword;
++ }
++ }
++
++ return ret;
++}
++
++static void doc2000_count_chips(struct mtd_info *mtd)
++{
++ struct nand_chip *this = mtd->priv;
++ struct doc_priv *doc = (void *)this->priv;
++ uint16_t mfrid;
++ int i;
++
++ /* Max 4 chips per floor on DiskOnChip 2000 */
++ doc->chips_per_floor = 4;
++
++ /* Find out what the first chip is */
++ mfrid = doc200x_ident_chip(mtd, 0);
++
++ /* Find how many chips in each floor. */
++ for (i = 1; i < 4; i++) {
++ if (doc200x_ident_chip(mtd, i) != mfrid)
++ break;
++ }
++ doc->chips_per_floor = i;
++}
++
++static int doc200x_wait(struct mtd_info *mtd, struct nand_chip *this, int state)
++{
++ struct doc_priv *doc = (void *)this->priv;
++
++ int status;
++
++ DoC_WaitReady(doc);
++ this->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1);
++ DoC_WaitReady(doc);
++ status = (int)this->read_byte(mtd);
++
++ return status;
++}
++
++static void doc2001_write_byte(struct mtd_info *mtd, u_char datum)
++{
++ struct nand_chip *this = mtd->priv;
++ struct doc_priv *doc = (void *)this->priv;
++ unsigned long docptr = doc->virtadr;
++
++ WriteDOC(datum, docptr, CDSNSlowIO);
++ WriteDOC(datum, docptr, Mil_CDSN_IO);
++ WriteDOC(datum, docptr, WritePipeTerm);
++}
++
++static u_char doc2001_read_byte(struct mtd_info *mtd)
++{
++ struct nand_chip *this = mtd->priv;
++ struct doc_priv *doc = (void *)this->priv;
++ unsigned long docptr = doc->virtadr;
++
++ ReadDOC(docptr, CDSNSlowIO);
++ /* 11.4.5 -- delay twice to allow extended length cycle */
++ DoC_Delay(doc, 2);
++ ReadDOC(docptr, ReadPipeInit);
++ return ReadDOC(docptr, Mil_CDSN_IO);
++}
++
++static void doc2001_writebuf(struct mtd_info *mtd,
++ const u_char *buf, int len)
++{
++ struct nand_chip *this = mtd->priv;
++ struct doc_priv *doc = (void *)this->priv;
++ unsigned long docptr = doc->virtadr;
++ int i;
++
++ for (i=0; i < len; i++)
++ WriteDOC_(buf[i], docptr, DoC_Mil_CDSN_IO + i);
++ /* Terminate write pipeline */
++ WriteDOC(0x00, docptr, WritePipeTerm);
++}
++
++static void doc2001_readbuf(struct mtd_info *mtd,
++ u_char *buf, int len)
++{
++ struct nand_chip *this = mtd->priv;
++ struct doc_priv *doc = (void *)this->priv;
++ unsigned long docptr = doc->virtadr;
++ int i;
++
++ /* Start read pipeline */
++ ReadDOC(docptr, ReadPipeInit);
++
++ for (i=0; i < len-1; i++)
++ buf[i] = ReadDOC(docptr, Mil_CDSN_IO);
++
++ /* Terminate read pipeline */
++ buf[i] = ReadDOC(docptr, LastDataRead);
++}
++static int doc2001_verifybuf(struct mtd_info *mtd,
++ const u_char *buf, int len)
++{
++ struct nand_chip *this = mtd->priv;
++ struct doc_priv *doc = (void *)this->priv;
++ unsigned long docptr = doc->virtadr;
++ int i;
++
++ /* Start read pipeline */
++ ReadDOC(docptr, ReadPipeInit);
++
++ for (i=0; i < len-1; i++)
++ if (buf[i] != ReadDOC(docptr, Mil_CDSN_IO)) {
++ ReadDOC(docptr, LastDataRead);
++ return i;
++ }
++ if (buf[i] != ReadDOC(docptr, LastDataRead))
++ return i;
++ return 0;
++}
++
++static void doc200x_select_chip(struct mtd_info *mtd, int chip)
++{
++ struct nand_chip *this = mtd->priv;
++ struct doc_priv *doc = (void *)this->priv;
++ unsigned long docptr = doc->virtadr;
++ int floor = 0;
++
++ /* 11.4.4 -- deassert CE before changing chip */
++ doc200x_hwcontrol(mtd, NAND_CTL_CLRNCE);
++
++ if(debug)printk("select chip (%d)\n", chip);
++
++ if (chip == -1)
++ return;
++
++ floor = chip / doc->chips_per_floor;
++ chip -= (floor * doc->chips_per_floor);
++
++ WriteDOC(floor, docptr, FloorSelect);
++ WriteDOC(chip, docptr, CDSNDeviceSelect);
++
++ doc200x_hwcontrol(mtd, NAND_CTL_SETNCE);
++
++ doc->curchip = chip;
++ doc->curfloor = floor;
++}
++
++static void doc200x_hwcontrol(struct mtd_info *mtd, int cmd)
++{
++ struct nand_chip *this = mtd->priv;
++ struct doc_priv *doc = (void *)this->priv;
++ unsigned long docptr = doc->virtadr;
++
++ switch(cmd) {
++ case NAND_CTL_SETNCE:
++ doc->CDSNControl |= CDSN_CTRL_CE;
++ break;
++ case NAND_CTL_CLRNCE:
++ doc->CDSNControl &= ~CDSN_CTRL_CE;
++ break;
++ case NAND_CTL_SETCLE:
++ doc->CDSNControl |= CDSN_CTRL_CLE;
++ break;
++ case NAND_CTL_CLRCLE:
++ doc->CDSNControl &= ~CDSN_CTRL_CLE;
++ break;
++ case NAND_CTL_SETALE:
++ doc->CDSNControl |= CDSN_CTRL_ALE;
++ break;
++ case NAND_CTL_CLRALE:
++ doc->CDSNControl &= ~CDSN_CTRL_ALE;
++ break;
++ case NAND_CTL_SETWP:
++ doc->CDSNControl |= CDSN_CTRL_WP;
++ break;
++ case NAND_CTL_CLRWP:
++ doc->CDSNControl &= ~CDSN_CTRL_WP;
++ break;
++ }
++ if (debug)printk("hwcontrol(%d): %02x\n", cmd, doc->CDSNControl);
++ WriteDOC(doc->CDSNControl, docptr, CDSNControl);
++ /* 11.4.3 -- 4 NOPs after CSDNControl write */
++ DoC_Delay(doc, 4);
++}
++
++static int doc200x_dev_ready(struct mtd_info *mtd)
++{
++ struct nand_chip *this = mtd->priv;
++ struct doc_priv *doc = (void *)this->priv;
++ unsigned long docptr = doc->virtadr;
++
++ /* 11.4.2 -- must NOP four times before checking FR/B# */
++ DoC_Delay(doc, 4);
++ if (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B)) {
++ if(debug)
++ printk("not ready\n");
++ return 0;
++ }
++ /* 11.4.2 -- Must NOP twice if it's ready */
++ DoC_Delay(doc, 2);
++ if (debug)printk("was ready\n");
++ return 1;
++}
++
++static int doc200x_block_bad(struct mtd_info *mtd, unsigned long block)
++{
++ /* FIXME: Look it up in the BBT */
++ return 0;
++}
++
++struct doc_priv mydoc = {
++ .physadr = 0xd4000,
++ .curfloor = -1,
++ .curchip = -1,
++};
++
++u_char mydatabuf[528];
++
++struct nand_chip mynand = {
++ .priv = (void *)&mydoc,
++ .select_chip = doc200x_select_chip,
++ .hwcontrol = doc200x_hwcontrol,
++ .dev_ready = doc200x_dev_ready,
++ .waitfunc = doc200x_wait,
++ .block_bad = doc200x_block_bad,
++ .eccmode = NAND_ECC_SOFT,
++ .data_buf = mydatabuf,
++};
++
++struct mtd_info mymtd = {
++ .priv = (void *)&mynand,
++ .owner = THIS_MODULE,
++};
++
++int __init init_nanddoc(void)
++{
++ mydoc.virtadr = (unsigned long)ioremap(mydoc.physadr, DOC_IOREMAP_LEN);
++ int nrchips = 1;
++ char *name;
++
++ WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_RESET,
++ mydoc.virtadr, DOCControl);
++ WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_RESET,
++ mydoc.virtadr, DOCControl);
++
++ WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_NORMAL,
++ mydoc.virtadr, DOCControl);
++ WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_NORMAL,
++ mydoc.virtadr, DOCControl);
++
++ mydoc.ChipID = ReadDOC(mydoc.virtadr, ChipID);
++
++ switch(mydoc.ChipID) {
++ case DOC_ChipID_DocMil:
++ mynand.write_byte = doc2001_write_byte;
++ mynand.read_byte = doc2001_read_byte;
++ mynand.write_buf = doc2001_writebuf;
++ mynand.read_buf = doc2001_readbuf;
++ mynand.verify_buf = doc2001_verifybuf;
++
++ ReadDOC(mydoc.virtadr, ChipID);
++ ReadDOC(mydoc.virtadr, ChipID);
++ if (ReadDOC(mydoc.virtadr, ChipID) != DOC_ChipID_DocMil) {
++ /* It's not a Millennium; it's one of the newer
++ DiskOnChip 2000 units with a similar ASIC.
++ Treat it like a Millennium, except that it
++ can have multiple chips. */
++ doc2000_count_chips(&mymtd);
++ nrchips = 4 * mydoc.chips_per_floor;
++ name = "DiskOnChip 2000 (INFTL Model)";
++ } else {
++ /* Bog-standard Millennium */
++ mydoc.chips_per_floor = 1;
++ nrchips = 1;
++ name = "DiskOnChip Millennium";
++ }
++ break;
++
++ case DOC_ChipID_Doc2k:
++ mynand.write_byte = doc2000_write_byte;
++ mynand.read_byte = doc2000_read_byte;
++ mynand.write_buf = doc2000_writebuf;
++ mynand.read_buf = doc2000_readbuf;
++ mynand.verify_buf = doc2000_verifybuf;
++
++ doc2000_count_chips(&mymtd);
++ nrchips = 4 * mydoc.chips_per_floor;
++ name = "DiskOnChip 2000 (NFTL Model)";
++ mydoc.CDSNControl |= CDSN_CTRL_FLASH_IO;
++
++ break;
++
++ default:
++ return -EIO;
++ }
++ if (nand_scan(&mymtd, nrchips)) {
++ iounmap((void *)mydoc.virtadr);
++ return -EIO;
++ }
++ mymtd.name = name;
++ add_mtd_device(&mymtd);
++
++ return 0;
++}
++
++void __exit cleanup_nanddoc(void)
++{
++ del_mtd_device(&mymtd);
++ iounmap((void *)mydoc.virtadr);
++}
++
++module_init(init_nanddoc);
++module_exit(cleanup_nanddoc);
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
++MODULE_DESCRIPTION("M-Systems DiskOnChip 2000 and Millennium device driver\n");
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/nand/edb7312.c linux/drivers/mtd/nand/edb7312.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/nand/edb7312.c 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/nand/edb7312.c 2004-11-17 18:17:59.165294768 +0100
+@@ -6,7 +6,7 @@
+ * Derived from drivers/mtd/nand/autcpu12.c
+ * Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de)
+ *
+- * $Id$
++ * $Id$
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+@@ -20,6 +20,7 @@
+
+ #include <linux/slab.h>
+ #include <linux/module.h>
++#include <linux/init.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/nand.h>
+ #include <linux/mtd/partitions.h>
+@@ -77,16 +78,13 @@
+ };
+ #define NUM_PARTITIONS 1
+
+-extern int parse_cmdline_partitions(struct mtd_info *master,
+- struct mtd_partition **pparts,
+- const char *mtd_id);
+ #endif
+
+
+ /*
+ * hardware specific access to control-lines
+ */
+-static void ep7312_hwcontrol(int cmd)
++static void ep7312_hwcontrol(struct mtd_info *mtd, int cmd)
+ {
+ switch(cmd) {
+
+@@ -116,10 +114,13 @@
+ /*
+ * read device ready pin
+ */
+-static int ep7312_device_ready(void)
++static int ep7312_device_ready(struct mtd_info *mtd)
+ {
+ return 1;
+ }
++#ifdef CONFIG_MTD_PARTITIONS
++const char *part_probes[] = { "cmdlinepart", NULL };
++#endif
+
+ /*
+ * Main initialization routine
+@@ -174,7 +175,7 @@
+ this->chip_delay = 15;
+
+ /* Scan to find existence of the device */
+- if (nand_scan (ep7312_mtd)) {
++ if (nand_scan (ep7312_mtd, 1)) {
+ iounmap((void *)ep7312_fio_base);
+ kfree (ep7312_mtd);
+ return -ENXIO;
+@@ -189,27 +190,16 @@
+ return -ENOMEM;
+ }
+
+- /* Allocate memory for internal data buffer */
+- this->data_cache = kmalloc (sizeof(u_char) * (ep7312_mtd->oobblock + ep7312_mtd->oobsize), GFP_KERNEL);
+- if (!this->data_cache) {
+- printk("Unable to allocate NAND data cache for EDB7312.\n");
+- kfree (this->data_buf);
+- iounmap((void *)ep7312_fio_base);
+- kfree (ep7312_mtd);
+- return -ENOMEM;
+- }
+- this->cache_page = -1;
+-
+-#ifdef CONFIG_MTD_CMDLINE_PARTS
+- mtd_parts_nb = parse_cmdline_partitions(ep7312_mtd, &mtd_parts,
+- "edb7312-nand");
++#ifdef CONFIG_PARTITIONS
++ ep7312_mtd->name = "edb7312-nand";
++ mtd_parts_nb = parse_mtd_partitions(ep7312_mtd, part_probes,
++ &mtd_parts, 0);
+ if (mtd_parts_nb > 0)
+ part_type = "command line";
+ else
+ mtd_parts_nb = 0;
+ #endif
+- if (mtd_parts_nb == 0)
+- {
++ if (mtd_parts_nb == 0) {
+ mtd_parts = partition_info;
+ mtd_parts_nb = NUM_PARTITIONS;
+ part_type = "static";
+@@ -236,7 +226,6 @@
+
+ /* Free internal data buffer */
+ kfree (this->data_buf);
+- kfree (this->data_cache);
+
+ /* Free the MTD device structure */
+ kfree (ep7312_mtd);
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/nand/nand.c linux/drivers/mtd/nand/nand.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/nand/nand.c 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/nand/nand.c 2004-11-17 18:17:59.167294464 +0100
+@@ -8,7 +8,7 @@
+ * Additional technical information is available on
+ * http://www.linux-mtd.infradead.org/tech/nand.html
+ *
+- * Copyright (C) 2000 Steven J. Hill (sjhill@cotw.com)
++ * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
+ * 2002 Thomas Gleixner (tglx@linutronix.de)
+ *
+ * 10-29-2001 Thomas Gleixner (tglx@linutronix.de)
+@@ -112,10 +112,27 @@
+ * for mtd->read_ecc / mtd->write_ecc
+ * some minor cleanups
+ *
+- * 12-05-2000 tglx: Dave Ellis (DGE@sixnetio) provided the fix for
++ * 12-05-2002 tglx: Dave Ellis (DGE@sixnetio) provided the fix for
+ * WRITE_VERIFY long time ago. Thanks for remembering me.
+ *
+- * $Id$
++ * 02-14-2003 tglx: Reject non page aligned writes
++ * Fixed ecc select in nand_write_page to match semantics.
++ *
++ * 02-18-2003 tglx: Changed oobsel to pointer. Added a default oob-selector
++ *
++ * 02-18-2003 tglx: Implemented oobsel again. Now it uses a pointer to
++ + a structure, which will be supplied by a filesystem driver
++ * If NULL is given, then the defaults (none or defaults
++ * supplied by ioctl (MEMSETOOBSEL) are used.
++ * For partitions the partition defaults are used (mtdpart.c)
++ *
++ * 06-04-2003 tglx: fix compile errors and fix write verify problem for
++ * some chips, which need either a delay between the readback
++ * and the next write command or have the CE removed. The
++ * CE disable/enable is much faster than a 20us delay and
++ * it should work on all available chips.
++ *
++ * $Id$
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+@@ -130,102 +147,151 @@
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/nand.h>
+ #include <linux/mtd/nand_ecc.h>
++#include <linux/mtd/compatmac.h>
+ #include <linux/interrupt.h>
+ #include <asm/io.h>
+
+ /*
+- * Macros for low-level register control
+- */
+-#define nand_select() this->hwcontrol(NAND_CTL_SETNCE);
+-
+-#define nand_deselect() this->hwcontrol(NAND_CTL_CLRNCE);
+-
+-/*
+- * out of band configuration for different filesystems
+- */
+-static int oobconfigs[][6] = {
+- { 0,0,0,0,0,0},
+-
+- { NAND_JFFS2_OOB_ECCPOS0, NAND_JFFS2_OOB_ECCPOS1, NAND_JFFS2_OOB_ECCPOS2,
+- NAND_JFFS2_OOB_ECCPOS3, NAND_JFFS2_OOB_ECCPOS4, NAND_JFFS2_OOB_ECCPOS5 },
+-
+- { NAND_YAFFS_OOB_ECCPOS0, NAND_YAFFS_OOB_ECCPOS1, NAND_YAFFS_OOB_ECCPOS2,
+- NAND_YAFFS_OOB_ECCPOS3, NAND_YAFFS_OOB_ECCPOS4, NAND_YAFFS_OOB_ECCPOS5 }
+-};
+-
+-/*
+ * NAND low-level MTD interface functions
+ */
++static void nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len);
++static void nand_read_buf(struct mtd_info *mtd, u_char *buf, int len);
++static int nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len);
++
+ static int nand_read (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf);
+ static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
+- size_t * retlen, u_char * buf, u_char * eccbuf, int oobsel);
++ size_t * retlen, u_char * buf, u_char * eccbuf, struct nand_oobinfo *oobsel);
+ static int nand_read_oob (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf);
+ static int nand_write (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char * buf);
+ static int nand_write_ecc (struct mtd_info *mtd, loff_t to, size_t len,
+- size_t * retlen, const u_char * buf, u_char * eccbuf, int oobsel);
++ size_t * retlen, const u_char * buf, u_char * eccbuf, struct nand_oobinfo *oobsel);
+ static int nand_write_oob (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char *buf);
+ static int nand_writev (struct mtd_info *mtd, const struct iovec *vecs,
+ unsigned long count, loff_t to, size_t * retlen);
+ static int nand_writev_ecc (struct mtd_info *mtd, const struct iovec *vecs,
+- unsigned long count, loff_t to, size_t * retlen, u_char *eccbuf, int oobsel);
++ unsigned long count, loff_t to, size_t * retlen, u_char *eccbuf, struct nand_oobinfo *oobsel);
+ static int nand_erase (struct mtd_info *mtd, struct erase_info *instr);
+ static void nand_sync (struct mtd_info *mtd);
+-static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int page, int col,
+- int last, u_char *oob_buf, int oobsel);
++static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int page, u_char *oob_buf, struct nand_oobinfo *oobsel);
++
++static u_char nand_read_byte(struct mtd_info *mtd)
++{
++ struct nand_chip *this = mtd->priv;
++ return readb(this->IO_ADDR_R);
++}
++
++static void nand_write_byte(struct mtd_info *mtd, u_char byte)
++{
++ struct nand_chip *this = mtd->priv;
++ writeb(byte, this->IO_ADDR_W);
++}
++
++static void nand_select_chip(struct mtd_info *mtd, int chip)
++{
++ struct nand_chip *this = mtd->priv;
++ switch(chip) {
++ case -1:
++ this->hwcontrol(mtd, NAND_CTL_CLRNCE);
++ break;
++ case 0:
++ this->hwcontrol(mtd, NAND_CTL_SETNCE);
++ break;
++
++ default:
++ BUG();
++ }
++}
++
++static void nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
++{
++ int i;
++ struct nand_chip *this = mtd->priv;
++
++ for (i=0; i<len; i++)
++ writeb(buf[i], this->IO_ADDR_W);
++}
++
++static void nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
++{
++ int i;
++ struct nand_chip *this = mtd->priv;
++
++ for (i=0; i<len; i++)
++ buf[i] = readb(this->IO_ADDR_R);
++}
++
++static int nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len)
++{
++ int i;
++ struct nand_chip *this = mtd->priv;
++
++ for (i=0; i<len; i++)
++ if (buf[i] != readb(this->IO_ADDR_R))
++ return i;
++
++ return 0;
++}
++
++/* Appropriate chip should already be selected */
++static int nand_block_bad(struct mtd_info *mtd, unsigned long page)
++{
++ struct nand_chip *this = mtd->priv;
++
++ this->cmdfunc (mtd, NAND_CMD_READOOB, NAND_BADBLOCK_POS, page);
++ if (this->read_byte(mtd) != 0xff)
++ return 1;
++
++ return 0;
++}
++
+ /*
+ * Send command to NAND device
+ */
+ static void nand_command (struct mtd_info *mtd, unsigned command, int column, int page_addr)
+ {
+ register struct nand_chip *this = mtd->priv;
+- register unsigned long NAND_IO_ADDR = this->IO_ADDR_W;
+
+ /* Begin command latch cycle */
+- this->hwcontrol (NAND_CTL_SETCLE);
++ this->hwcontrol(mtd, NAND_CTL_SETCLE);
+ /*
+ * Write out the command to the device.
+ */
+- if (command != NAND_CMD_SEQIN)
+- writeb (command, NAND_IO_ADDR);
+- else {
+- if (mtd->oobblock == 256 && column >= 256) {
+- column -= 256;
+- writeb (NAND_CMD_READOOB, NAND_IO_ADDR);
+- writeb (NAND_CMD_SEQIN, NAND_IO_ADDR);
+- } else if (mtd->oobblock == 512 && column >= 256) {
+- if (column < 512) {
+- column -= 256;
+- writeb (NAND_CMD_READ1, NAND_IO_ADDR);
+- writeb (NAND_CMD_SEQIN, NAND_IO_ADDR);
+- } else {
+- column -= 512;
+- writeb (NAND_CMD_READOOB, NAND_IO_ADDR);
+- writeb (NAND_CMD_SEQIN, NAND_IO_ADDR);
+- }
++ if (command == NAND_CMD_SEQIN) {
++ int readcmd;
++
++ if (column >= mtd->oobblock) {
++ /* OOB area */
++ column -= mtd->oobblock;
++ readcmd = NAND_CMD_READOOB;
++ } else if (column < 256) {
++ /* First 256 bytes --> READ0 */
++ readcmd = NAND_CMD_READ0;
+ } else {
+- writeb (NAND_CMD_READ0, NAND_IO_ADDR);
+- writeb (NAND_CMD_SEQIN, NAND_IO_ADDR);
++ column -= 256;
++ readcmd = NAND_CMD_READ1;
+ }
++ this->write_byte(mtd, readcmd);
+ }
++ this->write_byte(mtd, command);
+
+ /* Set ALE and clear CLE to start address cycle */
+- this->hwcontrol (NAND_CTL_CLRCLE);
++ this->hwcontrol(mtd, NAND_CTL_CLRCLE);
+
+ if (column != -1 || page_addr != -1) {
+- this->hwcontrol (NAND_CTL_SETALE);
++ this->hwcontrol(mtd, NAND_CTL_SETALE);
+
+ /* Serially input address */
+ if (column != -1)
+- writeb (column, NAND_IO_ADDR);
++ this->write_byte(mtd, column);
+ if (page_addr != -1) {
+- writeb ((unsigned char) (page_addr & 0xff), NAND_IO_ADDR);
+- writeb ((unsigned char) ((page_addr >> 8) & 0xff), NAND_IO_ADDR);
++ this->write_byte(mtd, (unsigned char) (page_addr & 0xff));
++ this->write_byte(mtd, (unsigned char) ((page_addr >> 8) & 0xff));
+ /* One more address cycle for higher density devices */
+ if (mtd->size & 0x0c000000)
+- writeb ((unsigned char) ((page_addr >> 16) & 0x0f), NAND_IO_ADDR);
++ this->write_byte(mtd, (unsigned char) ((page_addr >> 16) & 0x0f));
+ }
+ /* Latch in address */
+- this->hwcontrol (NAND_CTL_CLRALE);
++ this->hwcontrol(mtd, NAND_CTL_CLRALE);
+ }
+
+ /*
+@@ -244,10 +310,11 @@
+ case NAND_CMD_RESET:
+ if (this->dev_ready)
+ break;
+- this->hwcontrol (NAND_CTL_SETCLE);
+- writeb (NAND_CMD_STATUS, NAND_IO_ADDR);
+- this->hwcontrol (NAND_CTL_CLRCLE);
+- while ( !(readb (this->IO_ADDR_R) & 0x40));
++ udelay(this->chip_delay);
++ this->hwcontrol(mtd, NAND_CTL_SETCLE);
++ this->write_byte(mtd, NAND_CMD_STATUS);
++ this->hwcontrol(mtd, NAND_CTL_CLRCLE);
++ while ( !(this->read_byte(mtd) & 0x40));
+ return;
+
+ /* This applies to read commands */
+@@ -263,7 +330,7 @@
+ }
+
+ /* wait until command is processed */
+- while (!this->dev_ready());
++ while (!this->dev_ready(mtd));
+ }
+
+ /*
+@@ -288,17 +355,17 @@
+ spin_unlock_bh (&this->chip_lock);
+ return;
+ }
+-
++#if 0 /* This was broken. And of dubious utility */
+ if (this->state == FL_ERASING) {
+ if (new_state != FL_ERASING) {
+ this->state = new_state;
+ spin_unlock_bh (&this->chip_lock);
+- nand_select (); /* select in any case */
++ this->select_chip(mtd, 0); /* select in any case */
+ this->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
+ return;
+ }
+ }
+-
++#endif
+ set_current_state (TASK_UNINTERRUPTIBLE);
+ add_wait_queue (&this->wq, &wait);
+ spin_unlock_bh (&this->chip_lock);
+@@ -334,17 +401,17 @@
+ return 0;
+ }
+ if (this->dev_ready) {
+- if (this->dev_ready ())
++ if (this->dev_ready(mtd))
+ break;
+ }
+- if (readb (this->IO_ADDR_R) & 0x40)
++ if (this->read_byte(mtd) & 0x40)
+ break;
+
+ spin_unlock_bh (&this->chip_lock);
+ yield ();
+ spin_lock_bh (&this->chip_lock);
+ }
+- status = (int) readb (this->IO_ADDR_R);
++ status = (int) this->read_byte(mtd);
+ spin_unlock_bh (&this->chip_lock);
+
+ return status;
+@@ -352,14 +419,15 @@
+
+ /*
+ * Nand_page_program function is used for write and writev !
++ * This function will always program a full page of data
++ * If you call it with a non page aligned buffer, you're lost :)
+ */
+-static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this,
+- int page, int col, int last, u_char *oob_buf, int oobsel)
++static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int page, u_char *oob_buf, struct nand_oobinfo *oobsel)
+ {
+ int i, status;
+ u_char ecc_code[6], *oob_data;
+- int eccmode = oobsel ? this->eccmode : NAND_ECC_NONE;
+- int *oob_config = oobconfigs[oobsel];
++ int eccmode = oobsel->useecc ? this->eccmode : NAND_ECC_NONE;
++ int *oob_config = oobsel->eccpos;
+
+ /* pad oob area, if we have no oob buffer from fs-driver */
+ if (!oob_buf) {
+@@ -369,66 +437,42 @@
+ } else
+ oob_data = oob_buf;
+
+- /* software ecc 3 Bytes ECC / 256 Byte Data ? */
+- if (eccmode == NAND_ECC_SOFT) {
+- /* Read back previous written data, if col > 0 */
+- if (col) {
+- this->cmdfunc (mtd, NAND_CMD_READ0, 0, page);
+- for (i = 0; i < col; i++)
+- this->data_poi[i] = readb (this->IO_ADDR_R);
+- }
+- if ((col < this->eccsize) && (last >= this->eccsize)) {
+- this->calculate_ecc (&this->data_poi[0], &(ecc_code[0]));
+- for (i = 0; i < 3; i++)
+- oob_data[oob_config[i]] = ecc_code[i];
+- }
+- /* Calculate and write the second ECC if we have enough data */
+- if ((mtd->oobblock == 512) && (last == 512)) {
+- this->calculate_ecc (&this->data_poi[256], &(ecc_code[3]));
+- for (i = 3; i < 6; i++)
+- oob_data[oob_config[i]] = ecc_code[i];
+- }
+- } else {
+- /* For hardware ECC skip ECC, if we have no full page write */
+- if (eccmode != NAND_ECC_NONE && (col || last != mtd->oobblock))
+- eccmode = NAND_ECC_NONE;
+- }
+-
+- /* Prepad for partial page programming !!! */
+- for (i = 0; i < col; i++)
+- this->data_poi[i] = 0xff;
+-
+- /* Postpad for partial page programming !!! oob is already padded */
+- for (i = last; i < mtd->oobblock; i++)
+- this->data_poi[i] = 0xff;
+-
+ /* Send command to begin auto page programming */
+ this->cmdfunc (mtd, NAND_CMD_SEQIN, 0x00, page);
+
+ /* Write out complete page of data, take care of eccmode */
+- switch (this->eccmode) {
++ switch (eccmode) {
+ /* No ecc and software ecc 3/256, write all */
+ case NAND_ECC_NONE:
++ printk (KERN_WARNING "Writing data without ECC to NAND-FLASH is not recommended\n");
++ this->write_buf(mtd, this->data_poi, mtd->oobblock);
++ break;
+ case NAND_ECC_SOFT:
+- for (i = 0; i < mtd->oobblock; i++)
+- writeb ( this->data_poi[i] , this->IO_ADDR_W);
++ this->calculate_ecc(mtd, &this->data_poi[0], &(ecc_code[0]));
++ for (i = 0; i < 3; i++)
++ oob_data[oob_config[i]] = ecc_code[i];
++ /* Calculate and write the second ECC for 512 Byte page size */
++ if (mtd->oobblock == 512) {
++ this->calculate_ecc(mtd, &this->data_poi[256], &(ecc_code[3]));
++ for (i = 3; i < 6; i++)
++ oob_data[oob_config[i]] = ecc_code[i];
++ }
++ this->write_buf(mtd, this->data_poi, mtd->oobblock);
+ break;
+
+ /* Hardware ecc 3 byte / 256 data, write first half, get ecc, then second, if 512 byte pagesize */
+ case NAND_ECC_HW3_256:
+- this->enable_hwecc (NAND_ECC_WRITE); /* enable hardware ecc logic for write */
+- for (i = 0; i < mtd->eccsize; i++)
+- writeb ( this->data_poi[i] , this->IO_ADDR_W);
++ this->enable_hwecc(mtd, NAND_ECC_WRITE); /* enable hardware ecc logic for write */
++ this->write_buf(mtd, this->data_poi, mtd->eccsize);
+
+- this->calculate_ecc (NULL, &(ecc_code[0]));
++ this->calculate_ecc(mtd, NULL, &(ecc_code[0]));
+ for (i = 0; i < 3; i++)
+ oob_data[oob_config[i]] = ecc_code[i];
+
+ if (mtd->oobblock == 512) {
+- this->enable_hwecc (NAND_ECC_WRITE); /* enable hardware ecc logic for write*/
+- for (i = mtd->eccsize; i < mtd->oobblock; i++)
+- writeb ( this->data_poi[i] , this->IO_ADDR_W);
+- this->calculate_ecc (NULL, &(ecc_code[3]));
++ this->enable_hwecc(mtd, NAND_ECC_WRITE); /* enable hardware ecc logic for write*/
++ this->write_buf(mtd, &this->data_poi[mtd->eccsize], mtd->oobblock - mtd->eccsize);
++ this->calculate_ecc(mtd, NULL, &(ecc_code[3]));
+ for (i = 3; i < 6; i++)
+ oob_data[oob_config[i]] = ecc_code[i];
+ }
+@@ -436,20 +480,18 @@
+
+ /* Hardware ecc 3 byte / 512 byte data, write full page */
+ case NAND_ECC_HW3_512:
+- this->enable_hwecc (NAND_ECC_WRITE); /* enable hardware ecc logic */
+- for (i = 0; i < mtd->oobblock; i++)
+- writeb ( this->data_poi[i] , this->IO_ADDR_W);
+- this->calculate_ecc (NULL, &(ecc_code[0]));
++ this->enable_hwecc(mtd, NAND_ECC_WRITE); /* enable hardware ecc logic */
++ this->write_buf(mtd, this->data_poi, mtd->oobblock);
++ this->calculate_ecc(mtd, NULL, &(ecc_code[0]));
+ for (i = 0; i < 3; i++)
+ oob_data[oob_config[i]] = ecc_code[i];
+ break;
+
+ /* Hardware ecc 6 byte / 512 byte data, write full page */
+ case NAND_ECC_HW6_512:
+- this->enable_hwecc (NAND_ECC_WRITE); /* enable hardware ecc logic */
+- for (i = 0; i < mtd->oobblock; i++)
+- writeb ( this->data_poi[i] , this->IO_ADDR_W);
+- this->calculate_ecc (NULL, &(ecc_code[0]));
++ this->enable_hwecc(mtd, NAND_ECC_WRITE); /* enable hardware ecc logic */
++ this->write_buf(mtd, this->data_poi, mtd->oobblock);
++ this->calculate_ecc(mtd, NULL, &(ecc_code[0]));
+ for (i = 0; i < 6; i++)
+ oob_data[oob_config[i]] = ecc_code[i];
+ break;
+@@ -460,8 +502,7 @@
+ }
+
+ /* Write out OOB data */
+- for (i = 0; i < mtd->oobsize; i++)
+- writeb ( oob_data[i] , this->IO_ADDR_W);
++ this->write_buf(mtd, oob_data, mtd->oobsize);
+
+ /* Send command to actually program the data */
+ this->cmdfunc (mtd, NAND_CMD_PAGEPROG, -1, -1);
+@@ -490,25 +531,21 @@
+ */
+
+ /* Send command to read back the page */
+- this->cmdfunc (mtd, NAND_CMD_READ0, col, page);
++ this->cmdfunc (mtd, NAND_CMD_READ0, 0, page);
+ /* Loop through and verify the data */
+- for (i = col; i < last; i++) {
+- if (this->data_poi[i] != readb (this->IO_ADDR_R)) {
++ if (this->verify_buf(mtd, this->data_poi, mtd->oobblock)) {
+ DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write verify, page 0x%08x ", __FUNCTION__, page);
+ return -EIO;
+ }
+- }
+
+ /* check, if we have a fs-supplied oob-buffer */
+ if (oob_buf) {
+- for (i = 0; i < mtd->oobsize; i++) {
+- if (oob_data[i] != readb (this->IO_ADDR_R)) {
++ if (this->verify_buf(mtd, oob_data, mtd->oobsize)) {
+ DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write verify, page 0x%08x ", __FUNCTION__, page);
+ return -EIO;
+ }
+- }
+ } else {
+- if (eccmode != NAND_ECC_NONE && !col && last == mtd->oobblock) {
++ if (eccmode != NAND_ECC_NONE) {
+ int ecc_bytes = 0;
+
+ switch (this->eccmode) {
+@@ -518,8 +555,7 @@
+ case NAND_ECC_HW6_512: ecc_bytes = 6; break;
+ }
+
+- for (i = 0; i < mtd->oobsize; i++)
+- oob_data[i] = readb (this->IO_ADDR_R);
++ this->read_buf(mtd, oob_data, mtd->oobsize);
+
+ for (i = 0; i < ecc_bytes; i++) {
+ if (oob_data[oob_config[i]] != ecc_code[i]) {
+@@ -531,6 +567,13 @@
+ }
+ }
+ }
++ /*
++ * Terminate the read command. This is faster than sending a reset command or
++ * applying a 20us delay before issuing the next programm sequence.
++ * This is not a problem for all chips, but I have found a bunch of them.
++ */
++ this->select_chip(mtd, -1);
++ this->select_chip(mtd, 0);
+ #endif
+ return 0;
+ }
+@@ -540,7 +583,7 @@
+ */
+ static int nand_read (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf)
+ {
+- return (nand_read_ecc (mtd, from, len, retlen, buf, NULL, 0));
++ return nand_read_ecc (mtd, from, len, retlen, buf, NULL, NULL);
+ }
+
+
+@@ -548,7 +591,7 @@
+ * NAND read with ECC
+ */
+ static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
+- size_t * retlen, u_char * buf, u_char * oob_buf, int oobsel)
++ size_t * retlen, u_char * buf, u_char * oob_buf, struct nand_oobinfo *oobsel)
+ {
+ int j, col, page, end, ecc;
+ int erase_state = 0;
+@@ -557,9 +600,15 @@
+ u_char *data_poi, *oob_data = oob_buf;
+ u_char ecc_calc[6];
+ u_char ecc_code[6];
+- int eccmode = oobsel ? this->eccmode : NAND_ECC_NONE;
++ int eccmode;
++ int *oob_config;
++
++ // use chip default if zero
++ if (oobsel == NULL)
++ oobsel = &mtd->oobinfo;
+
+- int *oob_config = oobconfigs[oobsel];
++ eccmode = oobsel->useecc ? this->eccmode : NAND_ECC_NONE;
++ oob_config = oobsel->eccpos;
+
+ DEBUG (MTD_DEBUG_LEVEL3, "nand_read_ecc: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len);
+
+@@ -574,7 +623,7 @@
+ nand_get_chip (this, mtd ,FL_READING, &erase_state);
+
+ /* Select the NAND device */
+- nand_select ();
++ this->select_chip(mtd, 0);
+
+ /* First we calculate the starting page */
+ page = from >> this->page_shift;
+@@ -596,7 +645,7 @@
+ if (!this->dev_ready)
+ udelay (this->chip_delay);
+ else
+- while (!this->dev_ready());
++ while (!this->dev_ready(mtd));
+ }
+
+ /*
+@@ -616,39 +665,40 @@
+
+ j = 0;
+ switch (eccmode) {
+- case NAND_ECC_NONE: /* No ECC, Read in a page */
+- while (j < end)
+- data_poi[j++] = readb (this->IO_ADDR_R);
++ case NAND_ECC_NONE: { /* No ECC, Read in a page */
++ static unsigned long lastwhinge = 0;
++ if ((lastwhinge / HZ) != (jiffies / HZ)) {
++ printk (KERN_WARNING "Reading data from NAND FLASH without ECC is not recommended\n");
++ lastwhinge = jiffies;
++ }
++ this->read_buf(mtd, data_poi, end);
+ break;
++ }
+
+ case NAND_ECC_SOFT: /* Software ECC 3/256: Read in a page + oob data */
+- while (j < end)
+- data_poi[j++] = readb (this->IO_ADDR_R);
+- this->calculate_ecc (&data_poi[0], &ecc_calc[0]);
++ this->read_buf(mtd, data_poi, end);
++ this->calculate_ecc(mtd, &data_poi[0], &ecc_calc[0]);
+ if (mtd->oobblock == 512)
+- this->calculate_ecc (&data_poi[256], &ecc_calc[3]);
++ this->calculate_ecc(mtd, &data_poi[256], &ecc_calc[3]);
+ break;
+
+ case NAND_ECC_HW3_256: /* Hardware ECC 3 byte /256 byte data: Read in first 256 byte, get ecc, */
+- this->enable_hwecc (NAND_ECC_READ);
+- while (j < ecc)
+- data_poi[j++] = readb (this->IO_ADDR_R);
+- this->calculate_ecc (&data_poi[0], &ecc_calc[0]); /* read from hardware */
++ this->enable_hwecc(mtd, NAND_ECC_READ);
++ this->read_buf(mtd, data_poi, ecc);
++ this->calculate_ecc(mtd, &data_poi[0], &ecc_calc[0]); /* read from hardware */
+
+ if (mtd->oobblock == 512) { /* read second, if pagesize = 512 */
+- this->enable_hwecc (NAND_ECC_READ);
+- while (j < end)
+- data_poi[j++] = readb (this->IO_ADDR_R);
+- this->calculate_ecc (&data_poi[256], &ecc_calc[3]); /* read from hardware */
++ this->enable_hwecc(mtd, NAND_ECC_READ);
++ this->read_buf(mtd, &data_poi[ecc], end-ecc);
++ this->calculate_ecc(mtd, &data_poi[256], &ecc_calc[3]); /* read from hardware */
+ }
+ break;
+
+ case NAND_ECC_HW3_512:
+ case NAND_ECC_HW6_512: /* Hardware ECC 3/6 byte / 512 byte data : Read in a page */
+- this->enable_hwecc (NAND_ECC_READ);
+- while (j < end)
+- data_poi[j++] = readb (this->IO_ADDR_R);
+- this->calculate_ecc (&data_poi[0], &ecc_calc[0]); /* read from hardware */
++ this->enable_hwecc(mtd, NAND_ECC_READ);
++ this->read_buf(mtd, data_poi, end);
++ this->calculate_ecc(mtd, &data_poi[0], &ecc_calc[0]); /* read from hardware */
+ break;
+
+ default:
+@@ -658,7 +708,7 @@
+
+ /* read oobdata */
+ for (j = 0; j < mtd->oobsize; j++)
+- oob_data[oob + j] = readb (this->IO_ADDR_R);
++ oob_data[oob + j] = this->read_byte(mtd);
+
+ /* Skip ECC, if not active */
+ if (eccmode == NAND_ECC_NONE)
+@@ -669,7 +719,7 @@
+ ecc_code[j] = oob_data[oob + oob_config[j]];
+
+ /* correct data, if neccecary */
+- ecc_status = this->correct_data (&data_poi[0], &ecc_code[0], &ecc_calc[0]);
++ ecc_status = this->correct_data(mtd, &data_poi[0], &ecc_code[0], &ecc_calc[0]);
+ /* check, if we have a fs supplied oob-buffer */
+ if (oob_buf) {
+ oob += mtd->oobsize;
+@@ -682,7 +732,7 @@
+ }
+
+ if (mtd->oobblock == 512 && eccmode != NAND_ECC_HW3_512) {
+- ecc_status = this->correct_data (&data_poi[256], &ecc_code[3], &ecc_calc[3]);
++ ecc_status = this->correct_data(mtd, &data_poi[256], &ecc_code[3], &ecc_calc[3]);
+ if (oob_buf) {
+ *((int *)&oob_data[oob]) = ecc_status;
+ oob += sizeof(int);
+@@ -705,7 +755,7 @@
+ }
+
+ /* De-select the NAND device */
+- nand_deselect ();
++ this->select_chip(mtd, -1);
+
+ /* Wake up anyone waiting on the device */
+ spin_lock_bh (&this->chip_lock);
+@@ -753,7 +803,7 @@
+ nand_get_chip (this, mtd , FL_READING, &erase_state);
+
+ /* Select the NAND device */
+- nand_select ();
++ this->select_chip(mtd, 0);
+
+ /* Send the read command */
+ this->cmdfunc (mtd, NAND_CMD_READOOB, col, page);
+@@ -761,13 +811,20 @@
+ * Read the data, if we read more than one page
+ * oob data, let the device transfer the data !
+ */
+- for (i = 0; i < len; i++) {
+- buf[i] = readb (this->IO_ADDR_R);
+- if ((col++ & (mtd->oobsize - 1)) == (mtd->oobsize - 1))
++ i = 0;
++ while (i < len) {
++ int thislen = (mtd->oobsize - col) & (mtd->oobsize - 1);
++ if (!thislen)
++ thislen = mtd->oobsize;
++ thislen = min_t(int, thislen, len);
++ this->read_buf(mtd, &buf[i], thislen);
++ i += thislen;
++ col += thislen;
++ /* Delay between pages */
+ udelay (this->chip_delay);
+ }
+ /* De-select the NAND device */
+- nand_deselect ();
++ this->select_chip(mtd, -1);
+
+ /* Wake up anyone waiting on the device */
+ spin_lock_bh (&this->chip_lock);
+@@ -780,45 +837,54 @@
+ return 0;
+ }
+
++#define NOTALIGNED(x) (x & (mtd->oobblock-1)) != 0
++
+ /*
+ * Use NAND write ECC
+ */
+ static int nand_write (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char * buf)
+ {
+- return (nand_write_ecc (mtd, to, len, retlen, buf, NULL, 0));
++ return (nand_write_ecc (mtd, to, len, retlen, buf, NULL, NULL));
+ }
+ /*
+ * NAND write with ECC
+ */
+ static int nand_write_ecc (struct mtd_info *mtd, loff_t to, size_t len,
+- size_t * retlen, const u_char * buf, u_char * eccbuf, int oobsel)
++ size_t * retlen, const u_char * buf, u_char * eccbuf, struct nand_oobinfo *oobsel)
+ {
+- int i, page, col, cnt, ret = 0, oob = 0, written = 0;
++ int page, ret = 0, oob = 0, written = 0;
+ struct nand_chip *this = mtd->priv;
+
+ DEBUG (MTD_DEBUG_LEVEL3, "nand_write_ecc: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len);
+
+ /* Do not allow write past end of device */
+ if ((to + len) > mtd->size) {
+- DEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: Attempt to write past end of page\n");
++ DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: Attempt to write past end of page\n");
++ return -EINVAL;
++ }
++
++ /* reject writes, which are not page aligned */
++ if (NOTALIGNED (to) || NOTALIGNED(len)) {
++ printk (KERN_NOTICE "nand_write_ecc: Attempt to write not page aligned data\n");
+ return -EINVAL;
+ }
+
++ // if oobsel is NULL, use chip defaults
++ if (oobsel == NULL)
++ oobsel = &mtd->oobinfo;
++
+ /* Shift to get page */
+ page = ((int) to) >> this->page_shift;
+
+- /* Get the starting column */
+- col = to & (mtd->oobblock - 1);
+-
+ /* Grab the lock and see if the device is available */
+ nand_get_chip (this, mtd, FL_WRITING, NULL);
+
+ /* Select the NAND device */
+- nand_select ();
++ this->select_chip(mtd, 0);
+
+ /* Check the WP bit */
+ this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1);
+- if (!(readb (this->IO_ADDR_R) & 0x80)) {
++ if (!(this->read_byte(mtd) & 0x80)) {
+ DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: Device is write protected!!!\n");
+ ret = -EIO;
+ goto out;
+@@ -826,42 +892,27 @@
+
+ /* Loop until all data is written */
+ while (written < len) {
+- /*
+- * Check, if we have a full page write, then we can
+- * use the given buffer, else we have to copy
+- */
+- if (!col && (len - written) >= mtd->oobblock) {
++ int cnt = mtd->oobblock;
+ this->data_poi = (u_char*) &buf[written];
+- cnt = mtd->oobblock;
+- } else {
+- cnt = 0;
+- for (i = col; i < len && i < mtd->oobblock; i++) {
+- this->data_buf[i] = buf[written + i];
+- cnt++;
+- }
+- this->data_poi = this->data_buf;
+- }
+- /* We use the same function for write and writev !) */
++ /* We use the same function for write and writev */
+ if (eccbuf) {
+- ret = nand_write_page (mtd, this, page, col, cnt ,&eccbuf[oob], oobsel);
++ ret = nand_write_page (mtd, this, page, &eccbuf[oob], oobsel);
+ oob += mtd->oobsize;
+ } else
+- ret = nand_write_page (mtd, this, page, col, cnt, NULL, oobsel);
++ ret = nand_write_page (mtd, this, page, NULL, oobsel);
+
+ if (ret)
+ goto out;
+
+ /* Update written bytes count */
+ written += cnt;
+- /* Next write is aligned */
+- col = 0;
+ /* Increment page address */
+ page++;
+ }
+
+ out:
+ /* De-select the NAND device */
+- nand_deselect ();
++ this->select_chip(mtd, -1);
+
+ /* Wake up anyone waiting on the device */
+ spin_lock_bh (&this->chip_lock);
+@@ -873,13 +924,21 @@
+ return ret;
+ }
+
++static u_char ffchars[] = {
++ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
++ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
++};
++
+ /*
+ * NAND write out-of-band
+ */
+ static int nand_write_oob (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char * buf)
+ {
+- int i, column, page, status, ret = 0;
++ int column, page, status, ret = 0;
+ struct nand_chip *this = mtd->priv;
++#ifdef CONFIG_MTD_NAND_VERIFY_WRITE
++ int i;
++#endif
+
+ DEBUG (MTD_DEBUG_LEVEL3, "nand_write_oob: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len);
+
+@@ -902,27 +961,31 @@
+ nand_get_chip (this, mtd, FL_WRITING, NULL);
+
+ /* Select the NAND device */
+- nand_select ();
++ this->select_chip(mtd, 0);
++
++ /* Reset the chip. Some chips (like the Toshiba TC5832DC found
++ in one of my DiskOnChip 2000 test units) will clear the whole
++ data page too if we don't do this. I have no clue why, but
++ I seem to have 'fixed' it in the doc2000 driver in
++ August 1999. dwmw2. */
++ this->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
+
+ /* Check the WP bit */
+ this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1);
+- if (!(readb (this->IO_ADDR_R) & 0x80)) {
++ if (!(this->read_byte(mtd) & 0x80)) {
+ DEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: Device is write protected!!!\n");
+ ret = -EIO;
+ goto out;
+ }
+-
+ /* Write out desired data */
+ this->cmdfunc (mtd, NAND_CMD_SEQIN, mtd->oobblock, page);
++
+ /* prepad 0xff for partial programming */
+- for (i = 0; i < column; i++)
+- writeb (0xff, this->IO_ADDR_W);
++ this->write_buf(mtd, ffchars, column);
+ /* write data */
+- for (i = 0; i < len; i++)
+- writeb (buf[i], this->IO_ADDR_W);
++ this->write_buf(mtd, buf, len);
+ /* postpad 0xff for partial programming */
+- for (i = len + column; i < mtd->oobsize; i++)
+- writeb (0xff, this->IO_ADDR_W);
++ this->write_buf(mtd, ffchars, mtd->oobsize - (len+column));
+
+ /* Send command to program the OOB data */
+ this->cmdfunc (mtd, NAND_CMD_PAGEPROG, -1, -1);
+@@ -944,7 +1007,7 @@
+
+ /* Loop through and verify the data */
+ for (i = 0; i < len; i++) {
+- if (buf[i] != readb (this->IO_ADDR_R)) {
++ if (buf[i] != this->read_byte(mtd)) {
+ DEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: " "Failed write verify, page 0x%08x\n", page);
+ ret = -EIO;
+ goto out;
+@@ -954,7 +1017,7 @@
+
+ out:
+ /* De-select the NAND device */
+- nand_deselect ();
++ this->select_chip(mtd, -1);
+
+ /* Wake up anyone waiting on the device */
+ spin_lock_bh (&this->chip_lock);
+@@ -976,9 +1039,9 @@
+ }
+
+ static int nand_writev_ecc (struct mtd_info *mtd, const struct iovec *vecs, unsigned long count,
+- loff_t to, size_t * retlen, u_char *eccbuf, int oobsel)
++ loff_t to, size_t * retlen, u_char *eccbuf, struct nand_oobinfo *oobsel)
+ {
+- int i, page, col, cnt, len, total_len, ret = 0, written = 0;
++ int i, page, len, total_len, ret = 0, written = 0;
+ struct nand_chip *this = mtd->priv;
+
+ /* Calculate total length of data */
+@@ -995,39 +1058,42 @@
+ return -EINVAL;
+ }
+
++ /* reject writes, which are not page aligned */
++ if (NOTALIGNED (to) || NOTALIGNED(total_len)) {
++ printk (KERN_NOTICE "nand_write_ecc: Attempt to write not page aligned data\n");
++ return -EINVAL;
++ }
++
++ // if oobsel is NULL, use chip defaults
++ if (oobsel == NULL)
++ oobsel = &mtd->oobinfo;
++
+ /* Shift to get page */
+ page = ((int) to) >> this->page_shift;
+
+- /* Get the starting column */
+- col = to & (mtd->oobblock - 1);
+-
+ /* Grab the lock and see if the device is available */
+ nand_get_chip (this, mtd, FL_WRITING, NULL);
+
+ /* Select the NAND device */
+- nand_select ();
++ this->select_chip(mtd, 0);
+
+ /* Check the WP bit */
+ this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1);
+- if (!(readb (this->IO_ADDR_R) & 0x80)) {
++ if (!(this->read_byte(mtd) & 0x80)) {
+ DEBUG (MTD_DEBUG_LEVEL0, "nand_writev: Device is write protected!!!\n");
+ ret = -EIO;
+ goto out;
+ }
+
+ /* Loop until all iovecs' data has been written */
+- cnt = col;
+ len = 0;
+-
+ while (count) {
+ /*
+- * Check, if we write from offset 0 and if the tuple
+- * gives us not enough data for a full page write. Then we
+- * can use the iov direct, else we have to copy into
+- * data_buf.
++ * Check, if the tuple gives us not enough data for a
++ * full page write. Then we can use the iov direct,
++ * else we have to copy into data_buf.
+ */
+- if (!cnt && (vecs->iov_len - len) >= mtd->oobblock) {
+- cnt = mtd->oobblock;
++ if ((vecs->iov_len - len) >= mtd->oobblock) {
+ this->data_poi = (u_char *) vecs->iov_base;
+ this->data_poi += len;
+ len += mtd->oobblock;
+@@ -1042,6 +1108,7 @@
+ * Read data out of each tuple until we have a full page
+ * to write or we've read all the tuples.
+ */
++ int cnt = 0;
+ while ((cnt < mtd->oobblock) && count) {
+ if (vecs->iov_base != NULL && vecs->iov_len) {
+ this->data_buf[cnt++] = ((u_char *) vecs->iov_base)[len++];
+@@ -1057,15 +1124,12 @@
+ }
+
+ /* We use the same function for write and writev !) */
+- ret = nand_write_page (mtd, this, page, col, cnt, NULL, oobsel);
++ ret = nand_write_page (mtd, this, page, NULL, oobsel);
+ if (ret)
+ goto out;
+
+ /* Update written bytes count */
+- written += (cnt - col);
+-
+- /* Reset written byte counter and column */
+- col = cnt = 0;
++ written += mtd->oobblock;;
+
+ /* Increment page address */
+ page++;
+@@ -1073,7 +1137,7 @@
+
+ out:
+ /* De-select the NAND device */
+- nand_deselect ();
++ this->select_chip(mtd, -1);
+
+ /* Wake up anyone waiting on the device */
+ spin_lock_bh (&this->chip_lock);
+@@ -1125,11 +1189,11 @@
+ pages_per_block = mtd->erasesize / mtd->oobblock;
+
+ /* Select the NAND device */
+- nand_select ();
++ this->select_chip(mtd, 0);
+
+ /* Check the WP bit */
+ this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1);
+- if (!(readb (this->IO_ADDR_R) & 0x80)) {
++ if (!(this->read_byte(mtd) & 0x80)) {
+ DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Device is write protected!!!\n");
+ instr->state = MTD_ERASE_FAILED;
+ goto erase_exit;
+@@ -1142,8 +1206,7 @@
+
+ while (len) {
+ /* Check if we have a bad block, we do not erase bad blocks ! */
+- this->cmdfunc (mtd, NAND_CMD_READOOB, NAND_BADBLOCK_POS, page);
+- if (readb (this->IO_ADDR_R) != 0xff) {
++ if (this->block_bad(mtd, page)) {
+ printk (KERN_WARNING "nand_erase: attempt to erase a bad block at page 0x%08x\n", page);
+ instr->state = MTD_ERASE_FAILED;
+ goto erase_exit;
+@@ -1179,7 +1242,7 @@
+ if (this->state == FL_ERASING || this->state == FL_READY) {
+ /* Select the NAND device again, if we were interrupted */
+ this->state = FL_ERASING;
+- nand_select ();
++ this->select_chip(mtd, 0);
+ continue;
+ } else {
+ set_current_state (TASK_UNINTERRUPTIBLE);
+@@ -1194,7 +1257,7 @@
+
+ erase_exit:
+ /* De-select the NAND device */
+- nand_deselect ();
++ this->select_chip(mtd, -1);
+ spin_unlock_bh (&this->chip_lock);
+
+ ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO;;
+@@ -1205,6 +1268,7 @@
+ /* The device is ready */
+ spin_lock_bh (&this->chip_lock);
+ this->state = FL_READY;
++ wake_up (&this->wq);
+ spin_unlock_bh (&this->chip_lock);
+
+ /* Return more or less happy */
+@@ -1259,7 +1323,7 @@
+ /*
+ * Scan for the NAND device
+ */
+-int nand_scan (struct mtd_info *mtd)
++int nand_scan (struct mtd_info *mtd, int maxchips)
+ {
+ int i, nand_maf_id, nand_dev_id;
+ struct nand_chip *this = mtd->priv;
+@@ -1276,23 +1340,38 @@
+ if (this->waitfunc == NULL)
+ this->waitfunc = nand_wait;
+
++ if (!this->block_bad)
++ this->block_bad = nand_block_bad;
++ if (!this->select_chip)
++ this->select_chip = nand_select_chip;
++ if (!this->write_byte)
++ this->write_byte = nand_write_byte;
++ if (!this->read_byte)
++ this->read_byte = nand_read_byte;
++ if (!this->write_buf)
++ this->write_buf = nand_write_buf;
++ if (!this->read_buf)
++ this->read_buf = nand_read_buf;
++ if (!this->verify_buf)
++ this->verify_buf = nand_verify_buf;
++
+ /* Select the device */
+- nand_select ();
++ this->select_chip(mtd, 0);
+
+ /* Send the command for reading device ID */
+ this->cmdfunc (mtd, NAND_CMD_READID, 0x00, -1);
+
+ /* Read manufacturer and device IDs */
+- nand_maf_id = readb (this->IO_ADDR_R);
+- nand_dev_id = readb (this->IO_ADDR_R);
++ nand_maf_id = this->read_byte(mtd);
++ nand_dev_id = this->read_byte(mtd);
+
+ /* Print and store flash device information */
+ for (i = 0; nand_flash_ids[i].name != NULL; i++) {
+ if (nand_dev_id == nand_flash_ids[i].id && !mtd->size) {
+ mtd->name = nand_flash_ids[i].name;
+ mtd->erasesize = nand_flash_ids[i].erasesize;
+- mtd->size = (1 << nand_flash_ids[i].chipshift);
+ mtd->eccsize = 256;
++ this->chipshift = nand_flash_ids[i].chipshift;
+ if (nand_flash_ids[i].page256) {
+ mtd->oobblock = 256;
+ mtd->oobsize = 8;
+@@ -1307,13 +1386,34 @@
+ if (nand_manuf_ids[i].id == nand_maf_id)
+ break;
+ }
+- printk (KERN_INFO "NAND device: Manufacture ID:"
++ printk (KERN_INFO "NAND device: Manufacturer ID:"
+ " 0x%02x, Chip ID: 0x%02x (%s %s)\n", nand_maf_id, nand_dev_id,
+ nand_manuf_ids[i].name , mtd->name);
+ break;
+ }
+ }
+
++ if (!mtd->name) {
++ printk (KERN_WARNING "No NAND device found!!!\n");
++ return 1;
++ }
++
++ for (i=1; i < maxchips; i++) {
++ this->select_chip(mtd, i);
++
++ /* Send the command for reading device ID */
++ this->cmdfunc (mtd, NAND_CMD_READID, 0x00, -1);
++
++ /* Read manufacturer and device IDs */
++ if (nand_maf_id != this->read_byte(mtd) ||
++ nand_dev_id != this->read_byte(mtd))
++ break;
++ }
++ if (i > 1)
++ printk(KERN_INFO "%d NAND chips detected\n", i);
++
++ mtd->size = (1 << this->chipshift) /* * i when we fix the rest of the code */;
++
+ /*
+ * check ECC mode, default to software
+ * if 3byte/512byte hardware ECC is selected and we have 256 byte pagesize
+@@ -1324,6 +1424,7 @@
+ switch (this->eccmode) {
+
+ case NAND_ECC_HW3_512:
++ case NAND_ECC_HW6_512:
+ if (mtd->oobblock == 256) {
+ printk (KERN_WARNING "512 byte HW ECC not possible on 256 Byte pagesize, fallback to SW ECC \n");
+ this->eccmode = NAND_ECC_SOFT;
+@@ -1340,6 +1441,7 @@
+ BUG();
+
+ case NAND_ECC_NONE:
++ printk (KERN_WARNING "NAND_ECC_NONE selected by board driver. This is not recommended !!\n");
+ this->eccmode = NAND_ECC_NONE;
+ break;
+
+@@ -1359,18 +1461,11 @@
+ spin_lock_init (&this->chip_lock);
+
+ /* De-select the device */
+- nand_deselect ();
+-
+- /* Print warning message for no device */
+- if (!mtd->size) {
+- printk (KERN_WARNING "No NAND device found!!!\n");
+- return 1;
+- }
++ this->select_chip(mtd, -1);
+
+ /* Fill in remaining MTD driver data */
+ mtd->type = MTD_NANDFLASH;
+ mtd->flags = MTD_CAP_NANDFLASH | MTD_ECC;
+- mtd->module = THIS_MODULE;
+ mtd->ecctype = MTD_ECC_SW;
+ mtd->erase = nand_erase;
+ mtd->point = NULL;
+@@ -1389,6 +1484,7 @@
+ mtd->unlock = NULL;
+ mtd->suspend = NULL;
+ mtd->resume = NULL;
++ mtd->owner = THIS_MODULE;
+
+ /* Return happy */
+ return 0;
+@@ -1397,5 +1493,5 @@
+ EXPORT_SYMBOL (nand_scan);
+
+ MODULE_LICENSE ("GPL");
+-MODULE_AUTHOR ("Steven J. Hill <sjhill@cotw.com>, Thomas Gleixner <tglx@linutronix.de>");
++MODULE_AUTHOR ("Steven J. Hill <sjhill@realitydiluted.com>, Thomas Gleixner <tglx@linutronix.de>");
+ MODULE_DESCRIPTION ("Generic NAND flash driver code");
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/nand/nand_ecc.c linux/drivers/mtd/nand/nand_ecc.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/nand/nand_ecc.c 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/nand/nand_ecc.c 2004-11-17 18:17:59.168294312 +0100
+@@ -1,10 +1,10 @@
+ /*
+ * drivers/mtd/nand_ecc.c
+ *
+- * Copyright (C) 2000 Steven J. Hill (sjhill@cotw.com)
++ * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
+ * Toshiba America Electronics Components, Inc.
+ *
+- * $Id$
++ * $Id$
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+@@ -17,6 +17,7 @@
+ #include <linux/types.h>
+ #include <linux/kernel.h>
+ #include <linux/module.h>
++#include <linux/mtd/nand_ecc.h>
+
+ /*
+ * Pre-calculated 256-way 1 byte column parity
+@@ -84,7 +85,7 @@
+ /*
+ * Calculate 3 byte ECC code for 256 byte block
+ */
+-void nand_calculate_ecc (const u_char *dat, u_char *ecc_code)
++void nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code)
+ {
+ u_char idx, reg1, reg2, reg3;
+ int j;
+@@ -119,7 +120,7 @@
+ /*
+ * Detect and correct a 1 bit error for 256 byte block
+ */
+-int nand_correct_data (u_char *dat, u_char *read_ecc, u_char *calc_ecc)
++int nand_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc)
+ {
+ u_char a, b, c, d1, d2, d3, add, bit, i;
+
+@@ -209,5 +210,5 @@
+ EXPORT_SYMBOL(nand_correct_data);
+
+ MODULE_LICENSE("GPL");
+-MODULE_AUTHOR("Steven J. Hill <sjhill@cotw.com>");
++MODULE_AUTHOR("Steven J. Hill <sjhill@realitydiluted.com>");
+ MODULE_DESCRIPTION("Generic NAND ECC support");
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/nand/nand_ids.c linux/drivers/mtd/nand/nand_ids.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/nand/nand_ids.c 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/nand/nand_ids.c 2004-11-17 18:17:59.169294160 +0100
+@@ -4,7 +4,7 @@
+ * Copyright (C) 2002 Thomas Gleixner (tglx@linutronix.de)
+ *
+ *
+- * $Id$
++ * $Id$
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+@@ -18,21 +18,21 @@
+ * Chip ID list
+ */
+ struct nand_flash_dev nand_flash_ids[] = {
+- {"NAND 1MB 5V", 0x6e, 20, 0x1000, 1}, // 1Mb 5V
+- {"NAND 2MB 5V", 0x64, 21, 0x1000, 1}, // 2Mb 5V
+- {"NAND 4MB 5V", 0x6b, 22, 0x2000, 0}, // 4Mb 5V
+- {"NAND 1MB 3,3V", 0xe8, 20, 0x1000, 1}, // 1Mb 3.3V
+- {"NAND 1MB 3,3V", 0xec, 20, 0x1000, 1}, // 1Mb 3.3V
+- {"NAND 2MB 3,3V", 0xea, 21, 0x1000, 1}, // 2Mb 3.3V
+- {"NAND 4MB 3,3V", 0xd5, 22, 0x2000, 0}, // 4Mb 3.3V
+- {"NAND 4MB 3,3V", 0xe3, 22, 0x2000, 0}, // 4Mb 3.3V
+- {"NAND 4MB 3,3V", 0xe5, 22, 0x2000, 0}, // 4Mb 3.3V
+- {"NAND 8MB 3,3V", 0xd6, 23, 0x2000, 0}, // 8Mb 3.3V
+- {"NAND 8MB 3,3V", 0xe6, 23, 0x2000, 0}, // 8Mb 3.3V
+- {"NAND 16MB 3,3V", 0x73, 24, 0x4000, 0},// 16Mb 3,3V
+- {"NAND 32MB 3,3V", 0x75, 25, 0x4000, 0}, // 32Mb 3,3V
+- {"NAND 64MB 3,3V", 0x76, 26, 0x4000, 0}, // 64Mb 3,3V
+- {"NAND 128MB 3,3V", 0x79, 27, 0x4000, 0}, // 128Mb 3,3V
++ {"NAND 1MiB 5V", 0x6e, 20, 0x1000, 1},
++ {"NAND 2MiB 5V", 0x64, 21, 0x1000, 1},
++ {"NAND 4MiB 5V", 0x6b, 22, 0x2000, 0},
++ {"NAND 1MiB 3,3V", 0xe8, 20, 0x1000, 1},
++ {"NAND 1MiB 3,3V", 0xec, 20, 0x1000, 1},
++ {"NAND 2MiB 3,3V", 0xea, 21, 0x1000, 1},
++ {"NAND 4MiB 3,3V", 0xd5, 22, 0x2000, 0},
++ {"NAND 4MiB 3,3V", 0xe3, 22, 0x2000, 0},
++ {"NAND 4MiB 3,3V", 0xe5, 22, 0x2000, 0},
++ {"NAND 8MiB 3,3V", 0xd6, 23, 0x2000, 0},
++ {"NAND 8MiB 3,3V", 0xe6, 23, 0x2000, 0},
++ {"NAND 16MiB 3,3V", 0x73, 24, 0x4000, 0},
++ {"NAND 32MiB 3,3V", 0x75, 25, 0x4000, 0},
++ {"NAND 64MiB 3,3V", 0x76, 26, 0x4000, 0},
++ {"NAND 128MiB 3,3V", 0x79, 27, 0x4000, 0},
+ {NULL,}
+ };
+
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/nand/spia.c linux/drivers/mtd/nand/spia.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/nand/spia.c 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/nand/spia.c 2004-11-17 18:17:59.171293856 +0100
+@@ -1,14 +1,14 @@
+ /*
+ * drivers/mtd/nand/spia.c
+ *
+- * Copyright (C) 2000 Steven J. Hill (sjhill@cotw.com)
++ * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
+ *
+ *
+ * 10-29-2001 TG change to support hardwarespecific access
+ * to controllines (due to change in nand.c)
+ * page_cache added
+ *
+- * $Id$
++ * $Id$
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+@@ -20,6 +20,8 @@
+ * a 64Mibit (8MiB x 8 bits) NAND flash device.
+ */
+
++#include <linux/kernel.h>
++#include <linux/init.h>
+ #include <linux/slab.h>
+ #include <linux/module.h>
+ #include <linux/mtd/mtd.h>
+@@ -35,14 +37,14 @@
+ /*
+ * Values specific to the SPIA board (used with EP7212 processor)
+ */
+-#define SPIA_IO_ADDR = 0xd0000000 /* Start of EP7212 IO address space */
+-#define SPIA_FIO_ADDR = 0xf0000000 /* Address where flash is mapped */
+-#define SPIA_PEDR = 0x0080 /*
++#define SPIA_IO_BASE 0xd0000000 /* Start of EP7212 IO address space */
++#define SPIA_FIO_BASE 0xf0000000 /* Address where flash is mapped */
++#define SPIA_PEDR 0x0080 /*
+ * IO offset to Port E data register
+ * where the CLE, ALE and NCE pins
+ * are wired to.
+ */
+-#define SPIA_PEDDR = 0x00c0 /*
++#define SPIA_PEDDR 0x00c0 /*
+ * IO offset to Port E data direction
+ * register so we can control the IO
+ * lines.
+@@ -62,21 +64,20 @@
+ MODULE_PARM(spia_pedr, "i");
+ MODULE_PARM(spia_peddr, "i");
+
+-__setup("spia_io_base=",spia_io_base);
+-__setup("spia_fio_base=",spia_fio_base);
+-__setup("spia_pedr=",spia_pedr);
+-__setup("spia_peddr=",spia_peddr);
+-
+ /*
+ * Define partitions for flash device
+ */
+ const static struct mtd_partition partition_info[] = {
+- { name: "SPIA flash partition 1",
+- offset: 0,
+- size: 2*1024*1024 },
+- { name: "SPIA flash partition 2",
+- offset: 2*1024*1024,
+- size: 6*1024*1024 }
++ {
++ .name = "SPIA flash partition 1",
++ .offset = 0,
++ .size = 2*1024*1024
++ },
++ {
++ .name = "SPIA flash partition 2",
++ .offset = 2*1024*1024,
++ .size = 6*1024*1024
++ }
+ };
+ #define NUM_PARTITIONS 2
+
+@@ -84,7 +85,7 @@
+ /*
+ * hardware specific access to control-lines
+ */
+-void spia_hwcontrol(int cmd){
++static void spia_hwcontrol(struct mtd_info *mtd, int cmd){
+
+ switch(cmd){
+
+@@ -139,7 +140,7 @@
+ this->chip_delay = 15;
+
+ /* Scan to find existence of the device */
+- if (nand_scan (spia_mtd)) {
++ if (nand_scan (spia_mtd, 1)) {
+ kfree (spia_mtd);
+ return -ENXIO;
+ }
+@@ -152,16 +153,6 @@
+ return -ENOMEM;
+ }
+
+- /* Allocate memory for internal data buffer */
+- this->data_cache = kmalloc (sizeof(u_char) * (spia_mtd->oobblock + spia_mtd->oobsize), GFP_KERNEL);
+- if (!this->data_cache) {
+- printk ("Unable to allocate NAND data cache for SPIA.\n");
+- kfree (this->data_buf);
+- kfree (spia_mtd);
+- return = -ENOMEM;
+- }
+- this->cache_page = -1;
+-
+ /* Register the partitions */
+ add_mtd_partitions(spia_mtd, partition_info, NUM_PARTITIONS);
+
+@@ -183,7 +174,6 @@
+
+ /* Free internal data buffer */
+ kfree (this->data_buf);
+- kfree (this->page_cache);
+
+ /* Free the MTD device structure */
+ kfree (spia_mtd);
+@@ -192,5 +182,5 @@
+ #endif
+
+ MODULE_LICENSE("GPL");
+-MODULE_AUTHOR("Steven J. Hill <sjhill@cotw.com");
++MODULE_AUTHOR("Steven J. Hill <sjhill@realitydiluted.com");
+ MODULE_DESCRIPTION("Board-specific glue layer for NAND flash on SPIA board");
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/nand/toto.c linux/drivers/mtd/nand/toto.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/nand/toto.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/mtd/nand/toto.c 2004-11-17 18:17:59.000000000 +0100
+@@ -0,0 +1,221 @@
++/*
++ * drivers/mtd/nand/toto.c
++ *
++ * Copyright (c) 2003 Texas Instruments
++ *
++ * Derived from drivers/mtd/autcpu12.c
++ *
++ * Copyright (c) 2002 Thomas Gleixner <tgxl@linutronix.de>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ *
++ * Overview:
++ * This is a device driver for the NAND flash device found on the
++ * TI fido board. It supports 32MiB and 64MiB cards
++ *
++ * $Id$
++ */
++
++#include <linux/slab.h>
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/delay.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/nand.h>
++#include <linux/mtd/partitions.h>
++#include <asm/io.h>
++#include <asm/arch/hardware.h>
++#include <asm/sizes.h>
++#include <asm/arch/toto.h>
++#include <asm/arch-omap1510/hardware.h>
++#include <asm/arch/gpio.h>
++
++/*
++ * MTD structure for TOTO board
++ */
++static struct mtd_info *toto_mtd = NULL;
++
++static int toto_io_base = OMAP_FLASH_1_BASE;
++
++#define CONFIG_NAND_WORKAROUND 1
++
++#define NAND_NCE 0x4000
++#define NAND_CLE 0x1000
++#define NAND_ALE 0x0002
++#define NAND_MASK (NAND_CLE | NAND_ALE | NAND_NCE)
++
++#define T_NAND_CTL_CLRALE(iob) gpiosetout(NAND_ALE, 0)
++#define T_NAND_CTL_SETALE(iob) gpiosetout(NAND_ALE, NAND_ALE)
++#ifdef CONFIG_NAND_WORKAROUND /* "some" dev boards busted, blue wired to rts2 :( */
++#define T_NAND_CTL_CLRCLE(iob) gpiosetout(NAND_CLE, 0); rts2setout(2, 2)
++#define T_NAND_CTL_SETCLE(iob) gpiosetout(NAND_CLE, NAND_CLE); rts2setout(2, 0)
++#else
++#define T_NAND_CTL_CLRCLE(iob) gpiosetout(NAND_CLE, 0)
++#define T_NAND_CTL_SETCLE(iob) gpiosetout(NAND_CLE, NAND_CLE)
++#endif
++#define T_NAND_CTL_SETNCE(iob) gpiosetout(NAND_NCE, 0)
++#define T_NAND_CTL_CLRNCE(iob) gpiosetout(NAND_NCE, NAND_NCE)
++
++/*
++ * Define partitions for flash devices
++ */
++
++static struct mtd_partition partition_info64M[] = {
++ { .name = "toto kernel partition 1",
++ .offset = 0,
++ .size = 2 * SZ_1M },
++ { .name = "toto file sys partition 2",
++ .offset = 2 * SZ_1M,
++ .size = 14 * SZ_1M },
++ { .name = "toto user partition 3",
++ .offset = 16 * SZ_1M,
++ .size = 16 * SZ_1M },
++ { .name = "toto devboard extra partition 4",
++ .offset = 32 * SZ_1M,
++ .size = 32 * SZ_1M },
++};
++
++static struct mtd_partition partition_info32M[] = {
++ { .name = "toto kernel partition 1",
++ .offset = 0,
++ .size = 2 * SZ_1M },
++ { .name = "toto file sys partition 2",
++ .offset = 2 * SZ_1M,
++ .size = 14 * SZ_1M },
++ { .name = "toto user partition 3",
++ .offset = 16 * SZ_1M,
++ .size = 16 * SZ_1M },
++};
++
++#define NUM_PARTITIONS32M 3
++#define NUM_PARTITIONS64M 4
++/*
++ * hardware specific access to control-lines
++*/
++
++static void toto_hwcontrol(struct mtd_info *mtd, int cmd)
++{
++
++ udelay(1); /* hopefully enough time for tc make proceding write to clear */
++ switch(cmd){
++
++ case NAND_CTL_SETCLE: T_NAND_CTL_SETCLE(cmd); break;
++ case NAND_CTL_CLRCLE: T_NAND_CTL_CLRCLE(cmd); break;
++
++ case NAND_CTL_SETALE: T_NAND_CTL_SETALE(cmd); break;
++ case NAND_CTL_CLRALE: T_NAND_CTL_CLRALE(cmd); break;
++
++ case NAND_CTL_SETNCE: T_NAND_CTL_SETNCE(cmd); break;
++ case NAND_CTL_CLRNCE: T_NAND_CTL_CLRNCE(cmd); break;
++ }
++ udelay(1); /* allow time to ensure gpio state to over take memory write */
++}
++
++/*
++ * Main initialization routine
++ */
++int __init toto_init (void)
++{
++ struct nand_chip *this;
++ int err = 0;
++
++ /* Allocate memory for MTD device structure and private data */
++ toto_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip),
++ GFP_KERNEL);
++ if (!toto_mtd) {
++ printk (KERN_WARNING "Unable to allocate toto NAND MTD device structure.\n");
++ err = -ENOMEM;
++ goto out;
++ }
++
++ /* Get pointer to private data */
++ this = (struct nand_chip *) (&toto_mtd[1]);
++
++ /* Initialize structures */
++ memset((char *) toto_mtd, 0, sizeof(struct mtd_info));
++ memset((char *) this, 0, sizeof(struct nand_chip));
++
++ /* Link the private data with the MTD structure */
++ toto_mtd->priv = this;
++
++ /* Set address of NAND IO lines */
++ this->IO_ADDR_R = toto_io_base;
++ this->IO_ADDR_W = toto_io_base;
++ this->hwcontrol = toto_hwcontrol;
++ this->dev_ready = NULL;
++ /* 25 us command delay time */
++ this->chip_delay = 30;
++ this->eccmode = NAND_ECC_SOFT;
++
++ /* Scan to find existance of the device */
++ if (nand_scan (toto_mtd, 1)) {
++ err = -ENXIO;
++ goto out_mtd;
++ }
++
++ /* Allocate memory for internal data buffer */
++ this->data_buf = kmalloc (sizeof(u_char) * (toto_mtd->oobblock + toto_mtd->oobsize), GFP_KERNEL);
++ if (!this->data_buf) {
++ printk (KERN_WARNING "Unable to allocate NAND data buffer for toto.\n");
++ err = -ENOMEM;
++ goto out_mtd;
++ }
++
++ /* Register the partitions */
++ switch(toto_mtd->size){
++ case SZ_64M: add_mtd_partitions(toto_mtd, partition_info64M, NUM_PARTITIONS64M); break;
++ case SZ_32M: add_mtd_partitions(toto_mtd, partition_info32M, NUM_PARTITIONS32M); break;
++ default: {
++ printk (KERN_WARNING "Unsupported Nand device\n");
++ err = -ENXIO;
++ goto out_buf;
++ }
++ }
++
++ gpioreserve(NAND_MASK); /* claim our gpios */
++ archflashwp(0,0); /* open up flash for writing */
++
++ goto out;
++
++out_buf:
++ kfree (this->data_buf);
++out_mtd:
++ kfree (toto_mtd);
++out:
++ return err;
++}
++
++module_init(toto_init);
++
++/*
++ * Clean up routine
++ */
++static void __exit toto_cleanup (void)
++{
++ struct nand_chip *this = (struct nand_chip *) &toto_mtd[1];
++
++ /* Unregister partitions */
++ del_mtd_partitions(toto_mtd);
++
++ /* Unregister the device */
++ del_mtd_device (toto_mtd);
++
++ /* Free internal data buffers */
++ kfree (this->data_buf);
++
++ /* Free the MTD device structure */
++ kfree (toto_mtd);
++
++ /* stop flash writes */
++ archflashwp(0,1);
++
++ /* release gpios to system */
++ gpiorelease(NAND_MASK);
++}
++module_exit(toto_cleanup);
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Richard Woodruff <r-woodruff2@ti.com>");
++MODULE_DESCRIPTION("Glue layer for NAND flash on toto board");
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/nand/tx4925ndfmc.c linux/drivers/mtd/nand/tx4925ndfmc.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/nand/tx4925ndfmc.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/mtd/nand/tx4925ndfmc.c 2004-11-17 18:17:59.000000000 +0100
+@@ -0,0 +1,442 @@
++/*
++ * drivers/mtd/tx4925ndfmc.c
++ *
++ * Overview:
++ * This is a device driver for the NAND flash device found on the
++ * Toshiba RBTX4925 reference board, which is a SmartMediaCard. It supports
++ * 16MiB, 32MiB and 64MiB cards.
++ *
++ * Author: MontaVista Software, Inc. source@mvista.com
++ *
++ * Derived from drivers/mtd/autcpu12.c
++ * Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de)
++ *
++ * $Id$
++ *
++ * Copyright (C) 2001 Toshiba Corporation
++ *
++ * 2003 (c) MontaVista Software, Inc. This file is licensed under
++ * the terms of the GNU General Public License version 2. This program
++ * is licensed "as is" without any warranty of any kind, whether express
++ * or implied.
++ *
++ */
++
++#include <linux/slab.h>
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/nand.h>
++#include <linux/mtd/partitions.h>
++#include <linux/delay.h>
++#include <asm/io.h>
++#include <asm/tx4925/tx4925_nand.h>
++
++extern struct nand_oobinfo jffs2_oobinfo;
++
++/*
++ * MTD structure for RBTX4925 board
++ */
++static struct mtd_info *tx4925ndfmc_mtd = NULL;
++
++/*
++ * Module stuff
++ */
++#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
++#define tx4925ndfmc_init init_module
++#define tx4925ndfmc_cleanup cleanup_module
++#endif
++
++/*
++ * Define partitions for flash devices
++ */
++
++static struct mtd_partition partition_info16k[] = {
++ { .name = "RBTX4925 flash partition 1",
++ .offset = 0,
++ .size = 8 * 0x00100000 },
++ { .name = "RBTX4925 flash partition 2",
++ .offset = 8 * 0x00100000,
++ .size = 8 * 0x00100000 },
++};
++
++static struct mtd_partition partition_info32k[] = {
++ { .name = "RBTX4925 flash partition 1",
++ .offset = 0,
++ .size = 8 * 0x00100000 },
++ { .name = "RBTX4925 flash partition 2",
++ .offset = 8 * 0x00100000,
++ .size = 24 * 0x00100000 },
++};
++
++static struct mtd_partition partition_info64k[] = {
++ { .name = "User FS",
++ .offset = 0,
++ .size = 16 * 0x00100000 },
++ { .name = "RBTX4925 flash partition 2",
++ .offset = 16 * 0x00100000,
++ .size = 48 * 0x00100000},
++};
++
++static struct mtd_partition partition_info128k[] = {
++ { .name = "Skip bad section",
++ .offset = 0,
++ .size = 16 * 0x00100000 },
++ { .name = "User FS",
++ .offset = 16 * 0x00100000,
++ .size = 112 * 0x00100000 },
++};
++#define NUM_PARTITIONS16K 2
++#define NUM_PARTITIONS32K 2
++#define NUM_PARTITIONS64K 2
++#define NUM_PARTITIONS128K 2
++
++/*
++ * hardware specific access to control-lines
++*/
++static void tx4925ndfmc_hwcontrol(struct mtd_info *mtd, int cmd)
++{
++
++ switch(cmd){
++
++ case NAND_CTL_SETCLE:
++ tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_CLE;
++ break;
++ case NAND_CTL_CLRCLE:
++ tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_CLE;
++ break;
++ case NAND_CTL_SETALE:
++ tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_ALE;
++ break;
++ case NAND_CTL_CLRALE:
++ tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_ALE;
++ break;
++ case NAND_CTL_SETNCE:
++ tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_CE;
++ break;
++ case NAND_CTL_CLRNCE:
++ tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_CE;
++ break;
++ case NAND_CTL_SETWP:
++ tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_WE;
++ break;
++ case NAND_CTL_CLRWP:
++ tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_WE;
++ break;
++ }
++}
++
++/*
++* read device ready pin
++*/
++static int tx4925ndfmc_device_ready(struct mtd_info *mtd)
++{
++ int ready;
++ ready = (tx4925_ndfmcptr->sr & TX4925_NDSFR_BUSY) ? 0 : 1;
++ return ready;
++}
++void tx4925ndfmc_enable_hwecc(struct mtd_info *mtd, int mode)
++{
++ /* reset first */
++ tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_ECC_CNTL_MASK;
++ tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_ECC_CNTL_MASK;
++ tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_ECC_CNTL_ENAB;
++}
++static void tx4925ndfmc_disable_ecc(void)
++{
++ tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_ECC_CNTL_MASK;
++}
++static void tx4925ndfmc_enable_read_ecc(void)
++{
++ tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_ECC_CNTL_MASK;
++ tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_ECC_CNTL_READ;
++}
++void tx4925ndfmc_readecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code){
++ int i;
++ u_char *ecc = ecc_code;
++ tx4925ndfmc_enable_read_ecc();
++ for (i = 0;i < 6;i++,ecc++)
++ *ecc = tx4925_read_nfmc(&(tx4925_ndfmcptr->dtr));
++ tx4925ndfmc_disable_ecc();
++}
++void tx4925ndfmc_device_setup(void)
++{
++
++ *(unsigned char *)0xbb005000 &= ~0x08;
++
++ /* reset NDFMC */
++ tx4925_ndfmcptr->rstr |= TX4925_NDFRSTR_RST;
++ while (tx4925_ndfmcptr->rstr & TX4925_NDFRSTR_RST);
++
++ /* setup BusSeparete, Hold Time, Strobe Pulse Width */
++ tx4925_ndfmcptr->mcr = TX4925_BSPRT ? TX4925_NDFMCR_BSPRT : 0;
++ tx4925_ndfmcptr->spr = TX4925_HOLD << 4 | TX4925_SPW;
++}
++static u_char tx4925ndfmc_nand_read_byte(struct mtd_info *mtd)
++{
++ struct nand_chip *this = mtd->priv;
++ return tx4925_read_nfmc(this->IO_ADDR_R);
++}
++
++static void tx4925ndfmc_nand_write_byte(struct mtd_info *mtd, u_char byte)
++{
++ struct nand_chip *this = mtd->priv;
++ tx4925_write_nfmc(byte, this->IO_ADDR_W);
++}
++
++static void tx4925ndfmc_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
++{
++ int i;
++ struct nand_chip *this = mtd->priv;
++
++ for (i=0; i<len; i++)
++ tx4925_write_nfmc(buf[i], this->IO_ADDR_W);
++}
++
++static void tx4925ndfmc_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
++{
++ int i;
++ struct nand_chip *this = mtd->priv;
++
++ for (i=0; i<len; i++)
++ buf[i] = tx4925_read_nfmc(this->IO_ADDR_R);
++}
++
++static int tx4925ndfmc_nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len)
++{
++ int i;
++ struct nand_chip *this = mtd->priv;
++
++ for (i=0; i<len; i++)
++ if (buf[i] != tx4925_read_nfmc(this->IO_ADDR_R))
++ return i;
++
++ return 0;
++}
++
++/*
++ * Send command to NAND device
++ */
++static void tx4925ndfmc_nand_command (struct mtd_info *mtd, unsigned command, int column, int page_addr)
++{
++ register struct nand_chip *this = mtd->priv;
++
++ /* Begin command latch cycle */
++ this->hwcontrol(mtd, NAND_CTL_SETCLE);
++ /*
++ * Write out the command to the device.
++ */
++ if (command == NAND_CMD_SEQIN) {
++ int readcmd;
++
++ if (column >= mtd->oobblock) {
++ /* OOB area */
++ column -= mtd->oobblock;
++ readcmd = NAND_CMD_READOOB;
++ } else if (column < 256) {
++ /* First 256 bytes --> READ0 */
++ readcmd = NAND_CMD_READ0;
++ } else {
++ column -= 256;
++ readcmd = NAND_CMD_READ1;
++ }
++ this->write_byte(mtd, readcmd);
++ }
++ this->write_byte(mtd, command);
++
++ /* Set ALE and clear CLE to start address cycle */
++ this->hwcontrol(mtd, NAND_CTL_CLRCLE);
++
++ if (column != -1 || page_addr != -1) {
++ this->hwcontrol(mtd, NAND_CTL_SETALE);
++
++ /* Serially input address */
++ if (column != -1)
++ this->write_byte(mtd, column);
++ if (page_addr != -1) {
++ this->write_byte(mtd, (unsigned char) (page_addr & 0xff));
++ this->write_byte(mtd, (unsigned char) ((page_addr >> 8) & 0xff));
++ /* One more address cycle for higher density devices */
++ if (mtd->size & 0x0c000000)
++ this->write_byte(mtd, (unsigned char) ((page_addr >> 16) & 0x0f));
++ }
++ /* Latch in address */
++ this->hwcontrol(mtd, NAND_CTL_CLRALE);
++ }
++
++ /*
++ * program and erase have their own busy handlers
++ * status and sequential in needs no delay
++ */
++ switch (command) {
++
++ case NAND_CMD_PAGEPROG:
++ /* Turn off WE */
++ this->hwcontrol (mtd, NAND_CTL_CLRWP);
++ return;
++
++ case NAND_CMD_SEQIN:
++ /* Turn on WE */
++ this->hwcontrol (mtd, NAND_CTL_SETWP);
++ return;
++
++ case NAND_CMD_ERASE1:
++ case NAND_CMD_ERASE2:
++ case NAND_CMD_STATUS:
++ return;
++
++ case NAND_CMD_RESET:
++ if (this->dev_ready)
++ break;
++ this->hwcontrol(mtd, NAND_CTL_SETCLE);
++ this->write_byte(mtd, NAND_CMD_STATUS);
++ this->hwcontrol(mtd, NAND_CTL_CLRCLE);
++ while ( !(this->read_byte(mtd) & 0x40));
++ return;
++
++ /* This applies to read commands */
++ default:
++ /*
++ * If we don't have access to the busy pin, we apply the given
++ * command delay
++ */
++ if (!this->dev_ready) {
++ udelay (this->chip_delay);
++ return;
++ }
++ }
++
++ /* wait until command is processed */
++ while (!this->dev_ready(mtd));
++}
++
++#ifdef CONFIG_MTD_CMDLINE_PARTS
++extern int parse_cmdline_partitions(struct mtd_info *master, struct mtd_partitio
++n **pparts, char *);
++#endif
++
++/*
++ * Main initialization routine
++ */
++extern int nand_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc);
++int __init tx4925ndfmc_init (void)
++{
++ struct nand_chip *this;
++ int err = 0;
++
++ /* Allocate memory for MTD device structure and private data */
++ tx4925ndfmc_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip),
++ GFP_KERNEL);
++ if (!tx4925ndfmc_mtd) {
++ printk ("Unable to allocate RBTX4925 NAND MTD device structure.\n");
++ err = -ENOMEM;
++ goto out;
++ }
++
++ tx4925ndfmc_device_setup();
++
++ /* io is indirect via a register so don't need to ioremap address */
++
++ /* Get pointer to private data */
++ this = (struct nand_chip *) (&tx4925ndfmc_mtd[1]);
++
++ /* Initialize structures */
++ memset((char *) tx4925ndfmc_mtd, 0, sizeof(struct mtd_info));
++ memset((char *) this, 0, sizeof(struct nand_chip));
++
++ /* Link the private data with the MTD structure */
++ tx4925ndfmc_mtd->priv = this;
++
++ /* Set address of NAND IO lines */
++ this->IO_ADDR_R = (unsigned long)&(tx4925_ndfmcptr->dtr);
++ this->IO_ADDR_W = (unsigned long)&(tx4925_ndfmcptr->dtr);
++ this->hwcontrol = tx4925ndfmc_hwcontrol;
++ this->enable_hwecc = tx4925ndfmc_enable_hwecc;
++ this->calculate_ecc = tx4925ndfmc_readecc;
++ this->correct_data = nand_correct_data;
++ this->eccmode = NAND_ECC_HW6_512;
++ this->dev_ready = tx4925ndfmc_device_ready;
++ /* 20 us command delay time */
++ this->chip_delay = 20;
++ this->read_byte = tx4925ndfmc_nand_read_byte;
++ this->write_byte = tx4925ndfmc_nand_write_byte;
++ this->cmdfunc = tx4925ndfmc_nand_command;
++ this->write_buf = tx4925ndfmc_nand_write_buf;
++ this->read_buf = tx4925ndfmc_nand_read_buf;
++ this->verify_buf = tx4925ndfmc_nand_verify_buf;
++
++ /* Scan to find existance of the device */
++ if (nand_scan (tx4925ndfmc_mtd, 1)) {
++ err = -ENXIO;
++ goto out_ior;
++ }
++
++ /* Allocate memory for internal data buffer */
++ this->data_buf = kmalloc (sizeof(u_char) * (tx4925ndfmc_mtd->oobblock + tx4925ndfmc_mtd->oobsize), GFP_KERNEL);
++ if (!this->data_buf) {
++ printk ("Unable to allocate NAND data buffer for RBTX4925.\n");
++ err = -ENOMEM;
++ goto out_ior;
++ }
++
++ /* Register the partitions */
++#ifdef CONFIG_MTD_CMDLINE_PARTS
++ {
++ int mtd_parts_nb = 0;
++ struct mtd_partition *mtd_parts = 0;
++ mtd_parts_nb = parse_cmdline_partitions(tx4925ndfmc_mtd, &mtd_parts, "tx4925ndfmc");
++ if (mtd_parts_nb > 0)
++ add_mtd_partitions(tx4925ndfmc_mtd, mtd_parts, mtd_parts_nb);
++ else
++ add_mtd_device(tx4925ndfmc_mtd);
++ }
++#else /* ifdef CONFIG_MTD_CMDLINE_PARTS */
++ switch(tx4925ndfmc_mtd->size){
++ case 0x01000000: add_mtd_partitions(tx4925ndfmc_mtd, partition_info16k, NUM_PARTITIONS16K); break;
++ case 0x02000000: add_mtd_partitions(tx4925ndfmc_mtd, partition_info32k, NUM_PARTITIONS32K); break;
++ case 0x04000000: add_mtd_partitions(tx4925ndfmc_mtd, partition_info64k, NUM_PARTITIONS64K); break;
++ case 0x08000000: add_mtd_partitions(tx4925ndfmc_mtd, partition_info128k, NUM_PARTITIONS128K); break;
++ default: {
++ printk ("Unsupported SmartMedia device\n");
++ err = -ENXIO;
++ goto out_buf;
++ }
++ }
++#endif /* ifdef CONFIG_MTD_CMDLINE_PARTS */
++ goto out;
++
++out_buf:
++ kfree (this->data_buf);
++out_ior:
++out:
++ return err;
++}
++
++module_init(tx4925ndfmc_init);
++
++/*
++ * Clean up routine
++ */
++#ifdef MODULE
++static void __exit tx4925ndfmc_cleanup (void)
++{
++ struct nand_chip *this = (struct nand_chip *) &tx4925ndfmc_mtd[1];
++
++ /* Unregister partitions */
++ del_mtd_partitions(tx4925ndfmc_mtd);
++
++ /* Unregister the device */
++ del_mtd_device (tx4925ndfmc_mtd);
++
++ /* Free internal data buffers */
++ kfree (this->data_buf);
++
++ /* Free the MTD device structure */
++ kfree (tx4925ndfmc_mtd);
++}
++module_exit(tx4925ndfmc_cleanup);
++#endif
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Alice Hennessy <ahennessy@mvista.com>");
++MODULE_DESCRIPTION("Glue layer for SmartMediaCard on Toshiba RBTX4925");
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/nand/tx4938ndfmc.c linux/drivers/mtd/nand/tx4938ndfmc.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/nand/tx4938ndfmc.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/mtd/nand/tx4938ndfmc.c 2004-11-17 18:17:59.000000000 +0100
+@@ -0,0 +1,422 @@
++/*
++ * drivers/mtd/nand/tx4938ndfmc.c
++ *
++ * Overview:
++ * This is a device driver for the NAND flash device connected to
++ * TX4938 internal NAND Memory Controller.
++ * TX4938 NDFMC is almost same as TX4925 NDFMC, but register size are 64 bit.
++ *
++ * Author: source@mvista.com
++ *
++ * Based on spia.c by Steven J. Hill
++ *
++ * $Id$
++ *
++ * Copyright (C) 2000-2001 Toshiba Corporation
++ *
++ * 2003 (c) MontaVista Software, Inc. This file is licensed under the
++ * terms of the GNU General Public License version 2. This program is
++ * licensed "as is" without any warranty of any kind, whether express
++ * or implied.
++ */
++#include <linux/config.h>
++#include <linux/slab.h>
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/nand.h>
++#include <linux/mtd/nand_ecc.h>
++#include <linux/mtd/partitions.h>
++#include <asm/io.h>
++#include <asm/bootinfo.h>
++#include <linux/delay.h>
++#include <asm/tx4938/rbtx4938.h>
++
++extern struct nand_oobinfo jffs2_oobinfo;
++
++/*
++ * MTD structure for TX4938 NDFMC
++ */
++static struct mtd_info *tx4938ndfmc_mtd;
++
++/*
++ * Define partitions for flash device
++ */
++#define flush_wb() (void)tx4938_ndfmcptr->mcr;
++
++#define NUM_PARTITIONS 3
++#define NUMBER_OF_CIS_BLOCKS 24
++#define SIZE_OF_BLOCK 0x00004000
++#define NUMBER_OF_BLOCK_PER_ZONE 1024
++#define SIZE_OF_ZONE (NUMBER_OF_BLOCK_PER_ZONE * SIZE_OF_BLOCK)
++#ifndef CONFIG_MTD_CMDLINE_PARTS
++/*
++ * You can use the following sample of MTD partitions
++ * on the NAND Flash Memory 32MB or more.
++ *
++ * The following figure shows the image of the sample partition on
++ * the 32MB NAND Flash Memory.
++ *
++ * Block No.
++ * 0 +-----------------------------+ ------
++ * | CIS | ^
++ * 24 +-----------------------------+ |
++ * | kernel image | | Zone 0
++ * | | |
++ * +-----------------------------+ |
++ * 1023 | unused area | v
++ * +-----------------------------+ ------
++ * 1024 | JFFS2 | ^
++ * | | |
++ * | | | Zone 1
++ * | | |
++ * | | |
++ * | | v
++ * 2047 +-----------------------------+ ------
++ *
++ */
++static struct mtd_partition partition_info[NUM_PARTITIONS] = {
++ {
++ .name = "RBTX4938 CIS Area",
++ .offset = 0,
++ .size = (NUMBER_OF_CIS_BLOCKS * SIZE_OF_BLOCK),
++ .mask_flags = MTD_WRITEABLE /* This partition is NOT writable */
++ },
++ {
++ .name = "RBTX4938 kernel image",
++ .offset = MTDPART_OFS_APPEND,
++ .size = 8 * 0x00100000, /* 8MB (Depends on size of kernel image) */
++ .mask_flags = MTD_WRITEABLE /* This partition is NOT writable */
++ },
++ {
++ .name = "Root FS (JFFS2)",
++ .offset = (0 + SIZE_OF_ZONE), /* start address of next zone */
++ .size = MTDPART_SIZ_FULL
++ },
++};
++#endif
++
++static void tx4938ndfmc_hwcontrol(struct mtd_info *mtd, int cmd)
++{
++ switch (cmd) {
++ case NAND_CTL_SETCLE:
++ tx4938_ndfmcptr->mcr |= TX4938_NDFMCR_CLE;
++ break;
++ case NAND_CTL_CLRCLE:
++ tx4938_ndfmcptr->mcr &= ~TX4938_NDFMCR_CLE;
++ break;
++ case NAND_CTL_SETALE:
++ tx4938_ndfmcptr->mcr |= TX4938_NDFMCR_ALE;
++ break;
++ case NAND_CTL_CLRALE:
++ tx4938_ndfmcptr->mcr &= ~TX4938_NDFMCR_ALE;
++ break;
++ /* TX4938_NDFMCR_CE bit is 0:high 1:low */
++ case NAND_CTL_SETNCE:
++ tx4938_ndfmcptr->mcr |= TX4938_NDFMCR_CE;
++ break;
++ case NAND_CTL_CLRNCE:
++ tx4938_ndfmcptr->mcr &= ~TX4938_NDFMCR_CE;
++ break;
++ case NAND_CTL_SETWP:
++ tx4938_ndfmcptr->mcr |= TX4938_NDFMCR_WE;
++ break;
++ case NAND_CTL_CLRWP:
++ tx4938_ndfmcptr->mcr &= ~TX4938_NDFMCR_WE;
++ break;
++ }
++}
++static int tx4938ndfmc_dev_ready(struct mtd_info *mtd)
++{
++ flush_wb();
++ return !(tx4938_ndfmcptr->sr & TX4938_NDFSR_BUSY);
++}
++static void tx4938ndfmc_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code)
++{
++ u32 mcr = tx4938_ndfmcptr->mcr;
++ mcr &= ~TX4938_NDFMCR_ECC_ALL;
++ tx4938_ndfmcptr->mcr = mcr | TX4938_NDFMCR_ECC_OFF;
++ tx4938_ndfmcptr->mcr = mcr | TX4938_NDFMCR_ECC_READ;
++ ecc_code[1] = tx4938_ndfmcptr->dtr;
++ ecc_code[0] = tx4938_ndfmcptr->dtr;
++ ecc_code[2] = tx4938_ndfmcptr->dtr;
++ tx4938_ndfmcptr->mcr = mcr | TX4938_NDFMCR_ECC_OFF;
++}
++static void tx4938ndfmc_enable_hwecc(struct mtd_info *mtd, int mode)
++{
++ u32 mcr = tx4938_ndfmcptr->mcr;
++ mcr &= ~TX4938_NDFMCR_ECC_ALL;
++ tx4938_ndfmcptr->mcr = mcr | TX4938_NDFMCR_ECC_RESET;
++ tx4938_ndfmcptr->mcr = mcr | TX4938_NDFMCR_ECC_OFF;
++ tx4938_ndfmcptr->mcr = mcr | TX4938_NDFMCR_ECC_ON;
++}
++
++static u_char tx4938ndfmc_nand_read_byte(struct mtd_info *mtd)
++{
++ struct nand_chip *this = mtd->priv;
++ return tx4938_read_nfmc(this->IO_ADDR_R);
++}
++
++static void tx4938ndfmc_nand_write_byte(struct mtd_info *mtd, u_char byte)
++{
++ struct nand_chip *this = mtd->priv;
++ tx4938_write_nfmc(byte, this->IO_ADDR_W);
++}
++
++static void tx4938ndfmc_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
++{
++ int i;
++ struct nand_chip *this = mtd->priv;
++
++ for (i=0; i<len; i++)
++ tx4938_write_nfmc(buf[i], this->IO_ADDR_W);
++}
++
++static void tx4938ndfmc_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
++{
++ int i;
++ struct nand_chip *this = mtd->priv;
++
++ for (i=0; i<len; i++)
++ buf[i] = tx4938_read_nfmc(this->IO_ADDR_R);
++}
++
++static int tx4938ndfmc_nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len)
++{
++ int i;
++ struct nand_chip *this = mtd->priv;
++
++ for (i=0; i<len; i++)
++ if (buf[i] != tx4938_read_nfmc(this->IO_ADDR_R))
++ return i;
++
++ return 0;
++}
++
++/*
++ * Send command to NAND device
++ */
++static void tx4938ndfmc_nand_command (struct mtd_info *mtd, unsigned command, int column, int page_addr)
++{
++ register struct nand_chip *this = mtd->priv;
++
++ /* Begin command latch cycle */
++ this->hwcontrol(mtd, NAND_CTL_SETCLE);
++ /*
++ * Write out the command to the device.
++ */
++ if (command == NAND_CMD_SEQIN) {
++ int readcmd;
++
++ if (column >= mtd->oobblock) {
++ /* OOB area */
++ column -= mtd->oobblock;
++ readcmd = NAND_CMD_READOOB;
++ } else if (column < 256) {
++ /* First 256 bytes --> READ0 */
++ readcmd = NAND_CMD_READ0;
++ } else {
++ column -= 256;
++ readcmd = NAND_CMD_READ1;
++ }
++ this->write_byte(mtd, readcmd);
++ }
++ this->write_byte(mtd, command);
++
++ /* Set ALE and clear CLE to start address cycle */
++ this->hwcontrol(mtd, NAND_CTL_CLRCLE);
++
++ if (column != -1 || page_addr != -1) {
++ this->hwcontrol(mtd, NAND_CTL_SETALE);
++
++ /* Serially input address */
++ if (column != -1)
++ this->write_byte(mtd, column);
++ if (page_addr != -1) {
++ this->write_byte(mtd, (unsigned char) (page_addr & 0xff));
++ this->write_byte(mtd, (unsigned char) ((page_addr >> 8) & 0xff));
++ /* One more address cycle for higher density devices */
++ if (mtd->size & 0x0c000000)
++ this->write_byte(mtd, (unsigned char) ((page_addr >> 16) & 0x0f));
++ }
++ /* Latch in address */
++ this->hwcontrol(mtd, NAND_CTL_CLRALE);
++ }
++
++ /*
++ * program and erase have their own busy handlers
++ * status and sequential in needs no delay
++ */
++ switch (command) {
++
++ case NAND_CMD_PAGEPROG:
++ /* Turn off WE */
++ this->hwcontrol (mtd, NAND_CTL_CLRWP);
++ return;
++
++ case NAND_CMD_SEQIN:
++ /* Turn on WE */
++ this->hwcontrol (mtd, NAND_CTL_SETWP);
++ return;
++
++ case NAND_CMD_ERASE1:
++ case NAND_CMD_ERASE2:
++ case NAND_CMD_STATUS:
++ return;
++
++ case NAND_CMD_RESET:
++ if (this->dev_ready)
++ break;
++ this->hwcontrol(mtd, NAND_CTL_SETCLE);
++ this->write_byte(mtd, NAND_CMD_STATUS);
++ this->hwcontrol(mtd, NAND_CTL_CLRCLE);
++ while ( !(this->read_byte(mtd) & 0x40));
++ return;
++
++ /* This applies to read commands */
++ default:
++ /*
++ * If we don't have access to the busy pin, we apply the given
++ * command delay
++ */
++ if (!this->dev_ready) {
++ udelay (this->chip_delay);
++ return;
++ }
++ }
++
++ /* wait until command is processed */
++ while (!this->dev_ready(mtd));
++}
++
++#ifdef CONFIG_MTD_CMDLINE_PARTS
++extern int parse_cmdline_partitions(struct mtd_info *master, struct mtd_partition **pparts, char *);
++#endif
++/*
++ * Main initialization routine
++ */
++int __init tx4938ndfmc_init (void)
++{
++ struct nand_chip *this;
++ int bsprt = 0, hold = 0xf, spw = 0xf;
++ int protected = 0;
++
++ if ((*rbtx4938_piosel_ptr & 0x0c) != 0x08) {
++ printk("TX4938 NDFMC: disabled by IOC PIOSEL\n");
++ return -ENODEV;
++ }
++ bsprt = 1;
++ hold = 2;
++ spw = 9 - 1; /* 8 GBUSCLK = 80ns (@ GBUSCLK 100MHz) */
++
++ if ((tx4938_ccfgptr->pcfg &
++ (TX4938_PCFG_ATA_SEL|TX4938_PCFG_ISA_SEL|TX4938_PCFG_NDF_SEL))
++ != TX4938_PCFG_NDF_SEL) {
++ printk("TX4938 NDFMC: disabled by PCFG.\n");
++ return -ENODEV;
++ }
++
++ /* reset NDFMC */
++ tx4938_ndfmcptr->rstr |= TX4938_NDFRSTR_RST;
++ while (tx4938_ndfmcptr->rstr & TX4938_NDFRSTR_RST)
++ ;
++ /* setup BusSeparete, Hold Time, Strobe Pulse Width */
++ tx4938_ndfmcptr->mcr = bsprt ? TX4938_NDFMCR_BSPRT : 0;
++ tx4938_ndfmcptr->spr = hold << 4 | spw;
++
++ /* Allocate memory for MTD device structure and private data */
++ tx4938ndfmc_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip),
++ GFP_KERNEL);
++ if (!tx4938ndfmc_mtd) {
++ printk ("Unable to allocate TX4938 NDFMC MTD device structure.\n");
++ return -ENOMEM;
++ }
++
++ /* Get pointer to private data */
++ this = (struct nand_chip *) (&tx4938ndfmc_mtd[1]);
++
++ /* Initialize structures */
++ memset((char *) tx4938ndfmc_mtd, 0, sizeof(struct mtd_info));
++ memset((char *) this, 0, sizeof(struct nand_chip));
++
++ /* Link the private data with the MTD structure */
++ tx4938ndfmc_mtd->priv = this;
++
++ /* Set address of NAND IO lines */
++ this->IO_ADDR_R = (unsigned long)&tx4938_ndfmcptr->dtr;
++ this->IO_ADDR_W = (unsigned long)&tx4938_ndfmcptr->dtr;
++ this->hwcontrol = tx4938ndfmc_hwcontrol;
++ this->dev_ready = tx4938ndfmc_dev_ready;
++ this->calculate_ecc = tx4938ndfmc_calculate_ecc;
++ this->correct_data = nand_correct_data;
++ this->enable_hwecc = tx4938ndfmc_enable_hwecc;
++ this->eccmode = NAND_ECC_HW3_256;
++ this->chip_delay = 100;
++ this->read_byte = tx4938ndfmc_nand_read_byte;
++ this->write_byte = tx4938ndfmc_nand_write_byte;
++ this->cmdfunc = tx4938ndfmc_nand_command;
++ this->write_buf = tx4938ndfmc_nand_write_buf;
++ this->read_buf = tx4938ndfmc_nand_read_buf;
++ this->verify_buf = tx4938ndfmc_nand_verify_buf;
++
++ /* Scan to find existance of the device */
++ if (nand_scan (tx4938ndfmc_mtd, 1)) {
++ kfree (tx4938ndfmc_mtd);
++ return -ENXIO;
++ }
++
++ /* Allocate memory for internal data buffer */
++ this->data_buf = kmalloc (sizeof(u_char) * (tx4938ndfmc_mtd->oobblock + tx4938ndfmc_mtd->oobsize), GFP_KERNEL);
++ if (!this->data_buf) {
++ printk ("Unable to allocate NAND data buffer for TX4938.\n");
++ kfree (tx4938ndfmc_mtd);
++ return -ENOMEM;
++ }
++
++ if (protected) {
++ printk(KERN_INFO "TX4938 NDFMC: write protected.\n");
++ tx4938ndfmc_mtd->flags &= ~(MTD_WRITEABLE | MTD_ERASEABLE);
++ }
++
++#ifdef CONFIG_MTD_CMDLINE_PARTS
++ {
++ int mtd_parts_nb = 0;
++ struct mtd_partition *mtd_parts = 0;
++ mtd_parts_nb = parse_cmdline_partitions(tx4938ndfmc_mtd, &mtd_parts, "tx4938ndfmc");
++ if (mtd_parts_nb > 0)
++ add_mtd_partitions(tx4938ndfmc_mtd, mtd_parts, mtd_parts_nb);
++ else
++ add_mtd_device(tx4938ndfmc_mtd);
++ }
++#else
++ add_mtd_partitions(tx4938ndfmc_mtd, partition_info, NUM_PARTITIONS );
++#endif
++
++ return 0;
++}
++module_init(tx4938ndfmc_init);
++
++/*
++ * Clean up routine
++ */
++static void __exit tx4938ndfmc_cleanup (void)
++{
++ struct nand_chip *this = (struct nand_chip *) tx4938ndfmc_mtd->priv;
++
++ /* Unregister the device */
++#ifdef CONFIG_MTD_CMDLINE_PARTS
++ del_mtd_partitions(tx4938ndfmc_mtd);
++#endif
++ del_mtd_device (tx4938ndfmc_mtd);
++
++ /* Free the MTD device structure */
++ kfree (tx4938ndfmc_mtd);
++
++ /* Free internal data buffer */
++ kfree (this->data_buf);
++}
++module_exit(tx4938ndfmc_cleanup);
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Alice Hennessy <ahennessy@mvista.com>");
++MODULE_DESCRIPTION("Board-specific glue layer for NAND flash on TX4938 NDFMC");
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/nftlcore.c linux/drivers/mtd/nftlcore.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/nftlcore.c 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/nftlcore.c 2004-11-17 18:17:58.881337936 +0100
+@@ -1,7 +1,7 @@
+ /* Linux driver for NAND Flash Translation Layer */
+ /* (c) 1999 Machine Vision Holdings, Inc. */
+ /* Author: David Woodhouse <dwmw2@infradead.org> */
+-/* $Id$ */
++/* $Id$ */
+
+ /*
+ The contents of this file are distributed under the GNU General
+@@ -23,15 +23,13 @@
+ #include <linux/slab.h>
+ #include <linux/sched.h>
+ #include <linux/init.h>
+-#include <linux/blkpg.h>
++#include <linux/hdreg.h>
+
+-#ifdef CONFIG_KMOD
+ #include <linux/kmod.h>
+-#endif
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/nand.h>
+ #include <linux/mtd/nftl.h>
+-#include <linux/mtd/compatmac.h>
++#include <linux/mtd/blktrans.h>
+
+ /* maximum number of loops while examining next block, to have a
+ chance to detect consistency problems (they should never happen
+@@ -39,187 +37,95 @@
+
+ #define MAX_LOOPS 10000
+
+-/* NFTL block device stuff */
+-#define MAJOR_NR NFTL_MAJOR
+-#define DEVICE_REQUEST nftl_request
+-#define DEVICE_OFF(device)
+-
+-
+-#include <linux/blk.h>
+-#include <linux/hdreg.h>
+-
+-/* Linux-specific block device functions */
+-
+-/* I _HATE_ the Linux block device setup more than anything else I've ever
+- * encountered, except ...
+- */
+-
+-static int nftl_sizes[256];
+-static int nftl_blocksizes[256];
+-
+-/* .. for the Linux partition table handling. */
+-struct hd_struct part_table[256];
+-
+-#if LINUX_VERSION_CODE < 0x20328
+-static void dummy_init (struct gendisk *crap)
+-{}
+-#endif
+-
+-static struct gendisk nftl_gendisk = {
+- major: MAJOR_NR,
+- major_name: "nftl",
+- minor_shift: NFTL_PARTN_BITS, /* Bits to shift to get real from partition */
+- max_p: (1<<NFTL_PARTN_BITS)-1, /* Number of partitions per real */
+-#if LINUX_VERSION_CODE < 0x20328
+- max_nr: MAX_NFTLS, /* maximum number of real */
+- init: dummy_init, /* init function */
+-#endif
+- part: part_table, /* hd struct */
+- sizes: nftl_sizes, /* block sizes */
+-};
+-
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,14)
+-#define BLK_INC_USE_COUNT MOD_INC_USE_COUNT
+-#define BLK_DEC_USE_COUNT MOD_DEC_USE_COUNT
+-#else
+-#define BLK_INC_USE_COUNT do {} while(0)
+-#define BLK_DEC_USE_COUNT do {} while(0)
+-#endif
+-
+-struct NFTLrecord *NFTLs[MAX_NFTLS];
+
+-static void NFTL_setup(struct mtd_info *mtd)
++static void nftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
+ {
+- int i;
+ struct NFTLrecord *nftl;
+ unsigned long temp;
+- int firstfree = -1;
+-
+- DEBUG(MTD_DEBUG_LEVEL1,"NFTL_setup\n");
+
+- for (i = 0; i < MAX_NFTLS; i++) {
+- if (!NFTLs[i] && firstfree == -1)
+- firstfree = i;
+- else if (NFTLs[i] && NFTLs[i]->mtd == mtd) {
+- /* This is a Spare Media Header for an NFTL we've already found */
+- DEBUG(MTD_DEBUG_LEVEL1, "MTD already mounted as NFTL\n");
++ if (mtd->ecctype != MTD_ECC_RS_DiskOnChip)
+ return;
+- }
+- }
+- if (firstfree == -1) {
+- printk(KERN_WARNING "No more NFTL slot available\n");
+- return;
+- }
++
++ DEBUG(MTD_DEBUG_LEVEL1, "NFTL: add_mtd for %s\n", mtd->name);
+
+ nftl = kmalloc(sizeof(struct NFTLrecord), GFP_KERNEL);
++
+ if (!nftl) {
+- printk(KERN_WARNING "Out of memory for NFTL data structures\n");
++ printk(KERN_WARNING "NFTL: out of memory for data structures\n");
+ return;
+ }
++ memset(nftl, 0, sizeof(*nftl));
+
+- init_MUTEX(&nftl->mutex);
+-
+- nftl->mtd = mtd;
++ nftl->mbd.mtd = mtd;
++ nftl->mbd.devnum = -1;
++ nftl->mbd.blksize = 512;
++ nftl->mbd.tr = tr;
+
+ if (NFTL_mount(nftl) < 0) {
+- printk(KERN_WARNING "Could not mount NFTL device\n");
++ printk(KERN_WARNING "NFTL: could not mount device\n");
+ kfree(nftl);
+ return;
+ }
+
+ /* OK, it's a new one. Set up all the data structures. */
+-#ifdef PSYCHO_DEBUG
+- printk("Found new NFTL nftl%c\n", firstfree + 'a');
+-#endif
+
+- /* linux stuff */
+- nftl->usecount = 0;
++ /* Calculate geometry */
+ nftl->cylinders = 1024;
+ nftl->heads = 16;
+
+ temp = nftl->cylinders * nftl->heads;
+- nftl->sectors = nftl->nr_sects / temp;
+- if (nftl->nr_sects % temp) {
++ nftl->sectors = nftl->mbd.size / temp;
++ if (nftl->mbd.size % temp) {
+ nftl->sectors++;
+ temp = nftl->cylinders * nftl->sectors;
+- nftl->heads = nftl->nr_sects / temp;
++ nftl->heads = nftl->mbd.size / temp;
+
+- if (nftl->nr_sects % temp) {
++ if (nftl->mbd.size % temp) {
+ nftl->heads++;
+ temp = nftl->heads * nftl->sectors;
+- nftl->cylinders = nftl->nr_sects / temp;
++ nftl->cylinders = nftl->mbd.size / temp;
+ }
+ }
+
+- if (nftl->nr_sects != nftl->heads * nftl->cylinders * nftl->sectors) {
+- printk(KERN_WARNING "Cannot calculate an NFTL geometry to "
+- "match size of 0x%x.\n", nftl->nr_sects);
+- printk(KERN_WARNING "Using C:%d H:%d S:%d (== 0x%lx sects)\n",
++ if (nftl->mbd.size != nftl->heads * nftl->cylinders * nftl->sectors) {
++ /*
++ Oh no we don't have
++ mbd.size == heads * cylinders * sectors
++ */
++ printk(KERN_WARNING "NFTL: cannot calculate a geometry to "
++ "match size of 0x%lx.\n", nftl->mbd.size);
++ printk(KERN_WARNING "NFTL: using C:%d H:%d S:%d "
++ "(== 0x%lx sects)\n",
+ nftl->cylinders, nftl->heads , nftl->sectors,
+- (long)nftl->cylinders * (long)nftl->heads * (long)nftl->sectors );
+-
+- /* Oh no we don't have nftl->nr_sects = nftl->heads * nftl->cylinders * nftl->sectors; */
++ (long)nftl->cylinders * (long)nftl->heads *
++ (long)nftl->sectors );
+ }
+- NFTLs[firstfree] = nftl;
+- /* Finally, set up the block device sizes */
+- nftl_sizes[firstfree * 16] = nftl->nr_sects;
+- //nftl_blocksizes[firstfree*16] = 512;
+- part_table[firstfree * 16].nr_sects = nftl->nr_sects;
+-
+- nftl_gendisk.nr_real++;
+-
+- /* partition check ... */
+-#if LINUX_VERSION_CODE < 0x20328
+- resetup_one_dev(&nftl_gendisk, firstfree);
+-#else
+- grok_partitions(&nftl_gendisk, firstfree, 1<<NFTL_PARTN_BITS, nftl->nr_sects);
+-#endif
+-}
+-
+-static void NFTL_unsetup(int i)
+-{
+- struct NFTLrecord *nftl = NFTLs[i];
+-
+- DEBUG(MTD_DEBUG_LEVEL1, "NFTL_unsetup %d\n", i);
+-
+- NFTLs[i] = NULL;
+
++ if (add_mtd_blktrans_dev(&nftl->mbd)) {
+ if (nftl->ReplUnitTable)
+ kfree(nftl->ReplUnitTable);
+ if (nftl->EUNtable)
+ kfree(nftl->EUNtable);
+-
+- nftl_gendisk.nr_real--;
+ kfree(nftl);
+-}
+-
+-/* Search the MTD device for NFTL partitions */
+-static void NFTL_notify_add(struct mtd_info *mtd)
+-{
+- DEBUG(MTD_DEBUG_LEVEL1, "NFTL_notify_add for %s\n", mtd->name);
+-
+- if (mtd) {
+- if (!mtd->read_oob) {
+- /* If this MTD doesn't have out-of-band data,
+- then there's no point continuing */
+- DEBUG(MTD_DEBUG_LEVEL1, "No OOB data, quitting\n");
+ return;
+ }
+- DEBUG(MTD_DEBUG_LEVEL3, "mtd->read = %p, size = %d, erasesize = %d\n",
+- mtd->read, mtd->size, mtd->erasesize);
+-
+- NFTL_setup(mtd);
+- }
++#ifdef PSYCHO_DEBUG
++ printk(KERN_INFO "NFTL: Found new nftl%c\n", nftl->mbd.devnum + 'a');
++#endif
+ }
+
+-static void NFTL_notify_remove(struct mtd_info *mtd)
++static void nftl_remove_dev(struct mtd_blktrans_dev *dev)
+ {
+- int i;
++ struct NFTLrecord *nftl = (void *)dev;
+
+- for (i = 0; i < MAX_NFTLS; i++) {
+- if (NFTLs[i] && NFTLs[i]->mtd == mtd)
+- NFTL_unsetup(i);
+- }
++ DEBUG(MTD_DEBUG_LEVEL1, "NFTL: remove_dev (i=%d)\n", dev->devnum);
++
++ del_mtd_blktrans_dev(dev);
++ if (nftl->ReplUnitTable)
++ kfree(nftl->ReplUnitTable);
++ if (nftl->EUNtable)
++ kfree(nftl->EUNtable);
++ kfree(nftl);
+ }
+
+ #ifdef CONFIG_NFTL_RW
+@@ -303,7 +209,7 @@
+
+ targetEUN = thisEUN;
+ for (block = 0; block < nftl->EraseSize / 512; block ++) {
+- MTD_READOOB(nftl->mtd,
++ MTD_READOOB(nftl->mbd.mtd,
+ (thisEUN * nftl->EraseSize) + (block * 512),
+ 16 , &retlen, (char *)&oob);
+ if (block == 2) {
+@@ -420,7 +326,7 @@
+ chain by selecting the longer one */
+ oob.u.c.FoldMark = oob.u.c.FoldMark1 = cpu_to_le16(FOLD_MARK_IN_PROGRESS);
+ oob.u.c.unused = 0xffffffff;
+- MTD_WRITEOOB(nftl->mtd, (nftl->EraseSize * targetEUN) + 2 * 512 + 8,
++ MTD_WRITEOOB(nftl->mbd.mtd, (nftl->EraseSize * targetEUN) + 2 * 512 + 8,
+ 8, &retlen, (char *)&oob.u);
+ }
+
+@@ -444,16 +350,16 @@
+ if (BlockMap[block] == BLOCK_NIL)
+ continue;
+
+- ret = MTD_READECC(nftl->mtd, (nftl->EraseSize * BlockMap[block]) + (block * 512),
++ ret = MTD_READECC(nftl->mbd.mtd, (nftl->EraseSize * BlockMap[block]) + (block * 512),
+ 512, &retlen, movebuf, (char *)&oob, NAND_ECC_DISKONCHIP);
+ if (ret < 0) {
+- ret = MTD_READECC(nftl->mtd, (nftl->EraseSize * BlockMap[block])
++ ret = MTD_READECC(nftl->mbd.mtd, (nftl->EraseSize * BlockMap[block])
+ + (block * 512), 512, &retlen,
+ movebuf, (char *)&oob, NAND_ECC_DISKONCHIP);
+ if (ret != -EIO)
+ printk("Error went away on retry.\n");
+ }
+- MTD_WRITEECC(nftl->mtd, (nftl->EraseSize * targetEUN) + (block * 512),
++ MTD_WRITEECC(nftl->mbd.mtd, (nftl->EraseSize * targetEUN) + (block * 512),
+ 512, &retlen, movebuf, (char *)&oob, NAND_ECC_DISKONCHIP);
+ }
+
+@@ -462,7 +368,7 @@
+ = cpu_to_le16(thisVUC);
+ oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum = 0xffff;
+
+- MTD_WRITEOOB(nftl->mtd, (nftl->EraseSize * targetEUN) + 8,
++ MTD_WRITEOOB(nftl->mbd.mtd, (nftl->EraseSize * targetEUN) + 8,
+ 8, &retlen, (char *)&oob.u);
+
+ /* OK. We've moved the whole lot into the new block. Now we have to free the original blocks. */
+@@ -582,7 +488,7 @@
+
+ lastEUN = writeEUN;
+
+- MTD_READOOB(nftl->mtd, (writeEUN * nftl->EraseSize) + blockofs,
++ MTD_READOOB(nftl->mbd.mtd, (writeEUN * nftl->EraseSize) + blockofs,
+ 8, &retlen, (char *)&bci);
+
+ DEBUG(MTD_DEBUG_LEVEL2, "Status of block %d in EUN %d is %x\n",
+@@ -670,12 +576,12 @@
+ nftl->ReplUnitTable[writeEUN] = BLOCK_NIL;
+
+ /* ... and on the flash itself */
+- MTD_READOOB(nftl->mtd, writeEUN * nftl->EraseSize + 8, 8,
++ MTD_READOOB(nftl->mbd.mtd, writeEUN * nftl->EraseSize + 8, 8,
+ &retlen, (char *)&oob.u);
+
+ oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC);
+
+- MTD_WRITEOOB(nftl->mtd, writeEUN * nftl->EraseSize + 8, 8,
++ MTD_WRITEOOB(nftl->mbd.mtd, writeEUN * nftl->EraseSize + 8, 8,
+ &retlen, (char *)&oob.u);
+
+ /* we link the new block to the chain only after the
+@@ -685,13 +591,13 @@
+ /* Both in our cache... */
+ nftl->ReplUnitTable[lastEUN] = writeEUN;
+ /* ... and on the flash itself */
+- MTD_READOOB(nftl->mtd, (lastEUN * nftl->EraseSize) + 8,
++ MTD_READOOB(nftl->mbd.mtd, (lastEUN * nftl->EraseSize) + 8,
+ 8, &retlen, (char *)&oob.u);
+
+ oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum
+ = cpu_to_le16(writeEUN);
+
+- MTD_WRITEOOB(nftl->mtd, (lastEUN * nftl->EraseSize) + 8,
++ MTD_WRITEOOB(nftl->mbd.mtd, (lastEUN * nftl->EraseSize) + 8,
+ 8, &retlen, (char *)&oob.u);
+ }
+
+@@ -704,8 +610,10 @@
+ return 0xffff;
+ }
+
+-static int NFTL_writeblock(struct NFTLrecord *nftl, unsigned block, char *buffer)
++static int nftl_writeblock(struct mtd_blktrans_dev *mbd, unsigned long block,
++ char *buffer)
+ {
++ struct NFTLrecord *nftl = (void *)mbd;
+ u16 writeEUN;
+ unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1);
+ size_t retlen;
+@@ -720,7 +628,7 @@
+ return 1;
+ }
+
+- MTD_WRITEECC(nftl->mtd, (writeEUN * nftl->EraseSize) + blockofs,
++ MTD_WRITEECC(nftl->mbd.mtd, (writeEUN * nftl->EraseSize) + blockofs,
+ 512, &retlen, (char *)buffer, (char *)eccbuf, NAND_ECC_DISKONCHIP);
+ /* no need to write SECTOR_USED flags since they are written in mtd_writeecc */
+
+@@ -728,8 +636,10 @@
+ }
+ #endif /* CONFIG_NFTL_RW */
+
+-static int NFTL_readblock(struct NFTLrecord *nftl, unsigned block, char *buffer)
++static int nftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block,
++ char *buffer)
+ {
++ struct NFTLrecord *nftl = (void *)mbd;
+ u16 lastgoodEUN;
+ u16 thisEUN = nftl->EUNtable[block / (nftl->EraseSize / 512)];
+ unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1);
+@@ -742,7 +652,7 @@
+
+ if (thisEUN != BLOCK_NIL) {
+ while (thisEUN < nftl->nb_blocks) {
+- if (MTD_READOOB(nftl->mtd, (thisEUN * nftl->EraseSize) + blockofs,
++ if (MTD_READOOB(nftl->mbd.mtd, (thisEUN * nftl->EraseSize) + blockofs,
+ 8, &retlen, (char *)&bci) < 0)
+ status = SECTOR_IGNORE;
+ else
+@@ -761,13 +671,13 @@
+ case SECTOR_IGNORE:
+ break;
+ default:
+- printk("Unknown status for block %d in EUN %d: %x\n",
++ printk("Unknown status for block %ld in EUN %d: %x\n",
+ block, thisEUN, status);
+ break;
+ }
+
+ if (!silly--) {
+- printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%x\n",
++ printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%lx\n",
+ block / (nftl->EraseSize / 512));
+ return 1;
+ }
+@@ -783,264 +693,22 @@
+ loff_t ptr = (lastgoodEUN * nftl->EraseSize) + blockofs;
+ size_t retlen;
+ u_char eccbuf[6];
+- if (MTD_READECC(nftl->mtd, ptr, 512, &retlen, buffer, eccbuf, NAND_ECC_DISKONCHIP))
++ if (MTD_READECC(nftl->mbd.mtd, ptr, 512, &retlen, buffer, eccbuf, NAND_ECC_DISKONCHIP))
+ return -EIO;
+ }
+ return 0;
+ }
+
+-static int nftl_ioctl(struct inode * inode, struct file * file, unsigned int cmd, unsigned long arg)
+-{
+- struct NFTLrecord *nftl;
+- int p;
+-
+- nftl = NFTLs[MINOR(inode->i_rdev) >> NFTL_PARTN_BITS];
+-
+- if (!nftl) return -EINVAL;
+-
+- switch (cmd) {
+- case HDIO_GETGEO: {
+- struct hd_geometry g;
+-
+- g.heads = nftl->heads;
+- g.sectors = nftl->sectors;
+- g.cylinders = nftl->cylinders;
+- g.start = part_table[MINOR(inode->i_rdev)].start_sect;
+- return copy_to_user((void *)arg, &g, sizeof g) ? -EFAULT : 0;
+- }
+- case BLKGETSIZE: /* Return device size */
+- return put_user(part_table[MINOR(inode->i_rdev)].nr_sects,
+- (unsigned long *) arg);
+-
+-#ifdef BLKGETSIZE64
+- case BLKGETSIZE64:
+- return put_user((u64)part_table[MINOR(inode->i_rdev)].nr_sects << 9,
+- (u64 *)arg);
+-#endif
+-
+- case BLKFLSBUF:
+- if (!capable(CAP_SYS_ADMIN)) return -EACCES;
+- fsync_dev(inode->i_rdev);
+- invalidate_buffers(inode->i_rdev);
+- if (nftl->mtd->sync)
+- nftl->mtd->sync(nftl->mtd);
+- return 0;
+-
+- case BLKRRPART:
+- if (!capable(CAP_SYS_ADMIN)) return -EACCES;
+- if (nftl->usecount > 1) return -EBUSY;
+- /*
+- * We have to flush all buffers and invalidate caches,
+- * or we won't be able to re-use the partitions,
+- * if there was a change and we don't want to reboot
+- */
+- p = (1<<NFTL_PARTN_BITS) - 1;
+- while (p-- > 0) {
+- kdev_t devp = MKDEV(MAJOR(inode->i_dev), MINOR(inode->i_dev)+p);
+- if (part_table[p].nr_sects > 0)
+- invalidate_device (devp, 1);
+-
+- part_table[MINOR(inode->i_dev)+p].start_sect = 0;
+- part_table[MINOR(inode->i_dev)+p].nr_sects = 0;
+- }
+-
+-#if LINUX_VERSION_CODE < 0x20328
+- resetup_one_dev(&nftl_gendisk, MINOR(inode->i_rdev) >> NFTL_PARTN_BITS);
+-#else
+- grok_partitions(&nftl_gendisk, MINOR(inode->i_rdev) >> NFTL_PARTN_BITS,
+- 1<<NFTL_PARTN_BITS, nftl->nr_sects);
+-#endif
+- return 0;
+-
+-#if (LINUX_VERSION_CODE < 0x20303)
+- RO_IOCTLS(inode->i_rdev, arg); /* ref. linux/blk.h */
+-#else
+- case BLKROSET:
+- case BLKROGET:
+- case BLKSSZGET:
+- return blk_ioctl(inode->i_rdev, cmd, arg);
+-#endif
+-
+- default:
+- return -EINVAL;
+- }
+-}
+-
+-void nftl_request(RQFUNC_ARG)
+-{
+- unsigned int dev, block, nsect;
+- struct NFTLrecord *nftl;
+- char *buffer;
+- struct request *req;
+- int res;
+-
+- while (1) {
+- INIT_REQUEST; /* blk.h */
+- req = CURRENT;
+-
+- /* We can do this because the generic code knows not to
+- touch the request at the head of the queue */
+- spin_unlock_irq(&io_request_lock);
+-
+- DEBUG(MTD_DEBUG_LEVEL2, "NFTL_request\n");
+- DEBUG(MTD_DEBUG_LEVEL3, "NFTL %s request, from sector 0x%04lx for 0x%04lx sectors\n",
+- (req->cmd == READ) ? "Read " : "Write",
+- req->sector, req->current_nr_sectors);
+-
+- dev = MINOR(req->rq_dev);
+- block = req->sector;
+- nsect = req->current_nr_sectors;
+- buffer = req->buffer;
+- res = 1; /* succeed */
+-
+- if (dev >= MAX_NFTLS * (1<<NFTL_PARTN_BITS)) {
+- /* there is no such partition */
+- printk("nftl: bad minor number: device = %s\n",
+- kdevname(req->rq_dev));
+- res = 0; /* fail */
+- goto repeat;
+- }
+-
+- nftl = NFTLs[dev / (1<<NFTL_PARTN_BITS)];
+- DEBUG(MTD_DEBUG_LEVEL3, "Waiting for mutex\n");
+- down(&nftl->mutex);
+- DEBUG(MTD_DEBUG_LEVEL3, "Got mutex\n");
+-
+- if (block + nsect > part_table[dev].nr_sects) {
+- /* access past the end of device */
+- printk("nftl%c%d: bad access: block = %d, count = %d\n",
+- (MINOR(req->rq_dev)>>6)+'a', dev & 0xf, block, nsect);
+- up(&nftl->mutex);
+- res = 0; /* fail */
+- goto repeat;
+- }
+-
+- block += part_table[dev].start_sect;
+-
+- if (req->cmd == READ) {
+- DEBUG(MTD_DEBUG_LEVEL2, "NFTL read request of 0x%x sectors @ %x "
+- "(req->nr_sectors == %lx)\n", nsect, block, req->nr_sectors);
+-
+- for ( ; nsect > 0; nsect-- , block++, buffer += 512) {
+- /* Read a single sector to req->buffer + (512 * i) */
+- if (NFTL_readblock(nftl, block, buffer)) {
+- DEBUG(MTD_DEBUG_LEVEL2, "NFTL read request failed\n");
+- up(&nftl->mutex);
+- res = 0;
+- goto repeat;
+- }
+- }
+-
+- DEBUG(MTD_DEBUG_LEVEL2,"NFTL read request completed OK\n");
+- up(&nftl->mutex);
+- goto repeat;
+- } else if (req->cmd == WRITE) {
+- DEBUG(MTD_DEBUG_LEVEL2, "NFTL write request of 0x%x sectors @ %x "
+- "(req->nr_sectors == %lx)\n", nsect, block,
+- req->nr_sectors);
+-#ifdef CONFIG_NFTL_RW
+- for ( ; nsect > 0; nsect-- , block++, buffer += 512) {
+- /* Read a single sector to req->buffer + (512 * i) */
+- if (NFTL_writeblock(nftl, block, buffer)) {
+- DEBUG(MTD_DEBUG_LEVEL1,"NFTL write request failed\n");
+- up(&nftl->mutex);
+- res = 0;
+- goto repeat;
+- }
+- }
+- DEBUG(MTD_DEBUG_LEVEL2,"NFTL write request completed OK\n");
+-#else
+- res = 0; /* Writes always fail */
+-#endif /* CONFIG_NFTL_RW */
+- up(&nftl->mutex);
+- goto repeat;
+- } else {
+- DEBUG(MTD_DEBUG_LEVEL0, "NFTL unknown request\n");
+- up(&nftl->mutex);
+- res = 0;
+- goto repeat;
+- }
+- repeat:
+- DEBUG(MTD_DEBUG_LEVEL3, "end_request(%d)\n", res);
+- spin_lock_irq(&io_request_lock);
+- end_request(res);
+- }
+-}
+-
+-static int nftl_open(struct inode *ip, struct file *fp)
+-{
+- int nftlnum = MINOR(ip->i_rdev) >> NFTL_PARTN_BITS;
+- struct NFTLrecord *thisNFTL;
+- thisNFTL = NFTLs[nftlnum];
+-
+- DEBUG(MTD_DEBUG_LEVEL2,"NFTL_open\n");
+-
+-#ifdef CONFIG_KMOD
+- if (!thisNFTL && nftlnum == 0) {
+- request_module("docprobe");
+- thisNFTL = NFTLs[nftlnum];
+- }
+-#endif
+- if (!thisNFTL) {
+- DEBUG(MTD_DEBUG_LEVEL2,"ENODEV: thisNFTL = %d, minor = %d, ip = %p, fp = %p\n",
+- nftlnum, ip->i_rdev, ip, fp);
+- return -ENODEV;
+- }
+-
+-#ifndef CONFIG_NFTL_RW
+- if (fp->f_mode & FMODE_WRITE)
+- return -EROFS;
+-#endif /* !CONFIG_NFTL_RW */
+-
+- thisNFTL->usecount++;
+- BLK_INC_USE_COUNT;
+- if (!get_mtd_device(thisNFTL->mtd, -1)) {
+- BLK_DEC_USE_COUNT;
+- return -ENXIO;
+- }
+-
+- return 0;
+-}
+-
+-static int nftl_release(struct inode *inode, struct file *fp)
++static int nftl_getgeo(struct mtd_blktrans_dev *dev, struct hd_geometry *geo)
+ {
+- struct NFTLrecord *thisNFTL;
+-
+- thisNFTL = NFTLs[MINOR(inode->i_rdev) / 16];
+-
+- DEBUG(MTD_DEBUG_LEVEL2, "NFTL_release\n");
+-
+- if (thisNFTL->mtd->sync)
+- thisNFTL->mtd->sync(thisNFTL->mtd);
+- thisNFTL->usecount--;
+- BLK_DEC_USE_COUNT;
++ struct NFTLrecord *nftl = (void *)dev;
+
+- put_mtd_device(thisNFTL->mtd);
++ geo->heads = nftl->heads;
++ geo->sectors = nftl->sectors;
++ geo->cylinders = nftl->cylinders;
+
+ return 0;
+ }
+-#if LINUX_VERSION_CODE < 0x20326
+-static struct file_operations nftl_fops = {
+- read: block_read,
+- write: block_write,
+- ioctl: nftl_ioctl,
+- open: nftl_open,
+- release: nftl_release,
+- fsync: block_fsync,
+-};
+-#else
+-static struct block_device_operations nftl_fops =
+-{
+-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,14)
+- owner: THIS_MODULE,
+-#endif
+- open: nftl_open,
+- release: nftl_release,
+- ioctl: nftl_ioctl
+-};
+-#endif
+-
+-
+
+ /****************************************************************************
+ *
+@@ -1048,49 +716,33 @@
+ *
+ ****************************************************************************/
+
+-static struct mtd_notifier nftl_notifier = {
+- add: NFTL_notify_add,
+- remove: NFTL_notify_remove
++
++struct mtd_blktrans_ops nftl_tr = {
++ .name = "nftl",
++ .major = NFTL_MAJOR,
++ .part_bits = NFTL_PARTN_BITS,
++ .getgeo = nftl_getgeo,
++ .readsect = nftl_readblock,
++#ifdef CONFIG_NFTL_RW
++ .writesect = nftl_writeblock,
++#endif
++ .add_mtd = nftl_add_mtd,
++ .remove_dev = nftl_remove_dev,
++ .owner = THIS_MODULE,
+ };
+
+ extern char nftlmountrev[];
+
+ int __init init_nftl(void)
+ {
+- int i;
+-
+-#ifdef PRERELEASE
+- printk(KERN_INFO "NFTL driver: nftlcore.c $Revision$, nftlmount.c %s\n", nftlmountrev);
+-#endif
+-
+- if (register_blkdev(MAJOR_NR, "nftl", &nftl_fops)){
+- printk("unable to register NFTL block device on major %d\n", MAJOR_NR);
+- return -EBUSY;
+- } else {
+- blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), &nftl_request);
++ printk(KERN_INFO "NFTL driver: nftlcore.c $Revision$, nftlmount.c %s\n", nftlmountrev);
+
+- /* set block size to 1kB each */
+- for (i = 0; i < 256; i++) {
+- nftl_blocksizes[i] = 1024;
+- }
+- blksize_size[MAJOR_NR] = nftl_blocksizes;
+-
+- add_gendisk(&nftl_gendisk);
+- }
+-
+- register_mtd_user(&nftl_notifier);
+-
+- return 0;
++ return register_mtd_blktrans(&nftl_tr);
+ }
+
+ static void __exit cleanup_nftl(void)
+ {
+- unregister_mtd_user(&nftl_notifier);
+- unregister_blkdev(MAJOR_NR, "nftl");
+-
+- blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
+-
+- del_gendisk(&nftl_gendisk);
++ deregister_mtd_blktrans(&nftl_tr);
+ }
+
+ module_init(init_nftl);
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/nftlmount.c linux/drivers/mtd/nftlmount.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/nftlmount.c 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/nftlmount.c 2004-11-17 18:17:58.899335200 +0100
+@@ -4,7 +4,7 @@
+ * Author: Fabrice Bellard (fabrice.bellard@netgem.com)
+ * Copyright (C) 2000 Netgem S.A.
+ *
+- * $Id$
++ * $Id$
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -21,26 +21,17 @@
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+-#define __NO_VERSION__
+ #include <linux/kernel.h>
+-#include <linux/module.h>
+ #include <asm/errno.h>
+-#include <asm/io.h>
+-#include <asm/uaccess.h>
+-#include <linux/miscdevice.h>
+-#include <linux/pci.h>
+ #include <linux/delay.h>
+ #include <linux/slab.h>
+-#include <linux/sched.h>
+-#include <linux/init.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/nand.h>
+ #include <linux/mtd/nftl.h>
+-#include <linux/mtd/compatmac.h>
+
+ #define SECTORSIZE 512
+
+-char nftlmountrev[]="$Revision$";
++char nftlmountrev[]="$Revision$";
+
+ /* find_boot_record: Find the NFTL Media Header and its Spare copy which contains the
+ * various device information of the NFTL partition and Bad Unit Table. Update
+@@ -59,8 +50,8 @@
+
+ /* Assume logical EraseSize == physical erasesize for starting the scan.
+ We'll sort it out later if we find a MediaHeader which says otherwise */
+- nftl->EraseSize = nftl->mtd->erasesize;
+- nftl->nb_blocks = nftl->mtd->size / nftl->EraseSize;
++ nftl->EraseSize = nftl->mbd.mtd->erasesize;
++ nftl->nb_blocks = nftl->mbd.mtd->size / nftl->EraseSize;
+
+ nftl->MediaUnit = BLOCK_NIL;
+ nftl->SpareMediaUnit = BLOCK_NIL;
+@@ -71,12 +62,12 @@
+
+ /* Check for ANAND header first. Then can whinge if it's found but later
+ checks fail */
+- if ((ret = MTD_READ(nftl->mtd, block * nftl->EraseSize, SECTORSIZE, &retlen, buf))) {
++ if ((ret = MTD_READ(nftl->mbd.mtd, block * nftl->EraseSize, SECTORSIZE, &retlen, buf))) {
+ static int warncount = 5;
+
+ if (warncount) {
+ printk(KERN_WARNING "Block read at 0x%x of mtd%d failed: %d\n",
+- block * nftl->EraseSize, nftl->mtd->index, ret);
++ block * nftl->EraseSize, nftl->mbd.mtd->index, ret);
+ if (!--warncount)
+ printk(KERN_WARNING "Further failures for this block will not be printed\n");
+ }
+@@ -87,16 +78,16 @@
+ /* ANAND\0 not found. Continue */
+ #if 0
+ printk(KERN_DEBUG "ANAND header not found at 0x%x in mtd%d\n",
+- block * nftl->EraseSize, nftl->mtd->index);
++ block * nftl->EraseSize, nftl->mbd.mtd->index);
+ #endif
+ continue;
+ }
+
+ /* To be safer with BIOS, also use erase mark as discriminant */
+- if ((ret = MTD_READOOB(nftl->mtd, block * nftl->EraseSize + SECTORSIZE + 8,
+- 8, &retlen, (char *)&h1)) < 0) {
++ if ((ret = MTD_READOOB(nftl->mbd.mtd, block * nftl->EraseSize + SECTORSIZE + 8,
++ 8, &retlen, (char *)&h1) < 0)) {
+ printk(KERN_WARNING "ANAND header found at 0x%x in mtd%d, but OOB data read failed (err %d)\n",
+- block * nftl->EraseSize, nftl->mtd->index, ret);
++ block * nftl->EraseSize, nftl->mbd.mtd->index, ret);
+ continue;
+ }
+
+@@ -106,23 +97,23 @@
+ */
+ if (le16_to_cpu(h1.EraseMark | h1.EraseMark1) != ERASE_MARK) {
+ printk(KERN_NOTICE "ANAND header found at 0x%x in mtd%d, but erase mark not present (0x%04x,0x%04x instead)\n",
+- block * nftl->EraseSize, nftl->mtd->index,
++ block * nftl->EraseSize, nftl->mbd.mtd->index,
+ le16_to_cpu(h1.EraseMark), le16_to_cpu(h1.EraseMark1));
+ continue;
+ }
+
+ /* Finally reread to check ECC */
+- if ((ret = MTD_READECC(nftl->mtd, block * nftl->EraseSize, SECTORSIZE,
+- &retlen, buf, (char *)&oob, NAND_ECC_DISKONCHIP)) < 0) {
++ if ((ret = MTD_READECC(nftl->mbd.mtd, block * nftl->EraseSize, SECTORSIZE,
++ &retlen, buf, (char *)&oob, NAND_ECC_DISKONCHIP) < 0)) {
+ printk(KERN_NOTICE "ANAND header found at 0x%x in mtd%d, but ECC read failed (err %d)\n",
+- block * nftl->EraseSize, nftl->mtd->index, ret);
++ block * nftl->EraseSize, nftl->mbd.mtd->index, ret);
+ continue;
+ }
+
+ /* Paranoia. Check the ANAND header is still there after the ECC read */
+ if (memcmp(buf, "ANAND", 6)) {
+ printk(KERN_NOTICE "ANAND header found at 0x%x in mtd%d, but went away on reread!\n",
+- block * nftl->EraseSize, nftl->mtd->index);
++ block * nftl->EraseSize, nftl->mbd.mtd->index);
+ printk(KERN_NOTICE "New data are: %02x %02x %02x %02x %02x %02x\n",
+ buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]);
+ continue;
+@@ -137,8 +128,12 @@
+ printk(KERN_NOTICE "NFTL Media Headers at 0x%x and 0x%x disagree.\n",
+ nftl->MediaUnit * nftl->EraseSize, block * nftl->EraseSize);
+ /* if (debug) Print both side by side */
++ if (boot_record_count < 2) {
++ /* We haven't yet seen two real ones */
+ return -1;
+ }
++ continue;
++ }
+ if (boot_record_count == 1)
+ nftl->SpareMediaUnit = block;
+
+@@ -163,8 +158,8 @@
+ } else if (mh->UnitSizeFactor != 0xff) {
+ printk(KERN_NOTICE "WARNING: Support for NFTL with UnitSizeFactor 0x%02x is experimental\n",
+ mh->UnitSizeFactor);
+- nftl->EraseSize = nftl->mtd->erasesize << (0xff - mh->UnitSizeFactor);
+- nftl->nb_blocks = nftl->mtd->size / nftl->EraseSize;
++ nftl->EraseSize = nftl->mbd.mtd->erasesize << (0xff - mh->UnitSizeFactor);
++ nftl->nb_blocks = nftl->mbd.mtd->size / nftl->EraseSize;
+ }
+ nftl->nb_boot_blocks = le16_to_cpu(mh->FirstPhysicalEUN);
+ if ((nftl->nb_boot_blocks + 2) >= nftl->nb_blocks) {
+@@ -182,7 +177,7 @@
+ return -1;
+ }
+
+- nftl->nr_sects = nftl->numvunits * (nftl->EraseSize / SECTORSIZE);
++ nftl->mbd.size = nftl->numvunits * (nftl->EraseSize / SECTORSIZE);
+
+ /* If we're not using the last sectors in the device for some reason,
+ reduce nb_blocks accordingly so we forget they're there */
+@@ -220,7 +215,7 @@
+ for (i = 0; i < nftl->nb_blocks; i++) {
+ if ((i & (SECTORSIZE - 1)) == 0) {
+ /* read one sector for every SECTORSIZE of blocks */
+- if ((ret = MTD_READECC(nftl->mtd, block * nftl->EraseSize +
++ if ((ret = MTD_READECC(nftl->mbd.mtd, block * nftl->EraseSize +
+ i + SECTORSIZE, SECTORSIZE, &retlen, buf,
+ (char *)&oob, NAND_ECC_DISKONCHIP)) < 0) {
+ printk(KERN_NOTICE "Read of bad sector table failed (err %d)\n",
+@@ -263,16 +258,16 @@
+ for (i = 0; i < len; i += SECTORSIZE) {
+ /* we want to read the sector without ECC check here since a free
+ sector does not have ECC syndrome on it yet */
+- if (MTD_READ(nftl->mtd, address, SECTORSIZE, &retlen, buf) < 0)
++ if (MTD_READ(nftl->mbd.mtd, address, SECTORSIZE, &retlen, buf) < 0)
+ return -1;
+ if (memcmpb(buf, 0xff, SECTORSIZE) != 0)
+ return -1;
+
+ if (check_oob) {
+- if (MTD_READOOB(nftl->mtd, address, nftl->mtd->oobsize,
++ if (MTD_READOOB(nftl->mbd.mtd, address, nftl->mbd.mtd->oobsize,
+ &retlen, buf) < 0)
+ return -1;
+- if (memcmpb(buf, 0xff, nftl->mtd->oobsize) != 0)
++ if (memcmpb(buf, 0xff, nftl->mbd.mtd->oobsize) != 0)
+ return -1;
+ }
+ address += SECTORSIZE;
+@@ -297,7 +292,7 @@
+ struct erase_info *instr = &nftl->instr;
+
+ /* Read the Unit Control Information #1 for Wear-Leveling */
+- if (MTD_READOOB(nftl->mtd, block * nftl->EraseSize + SECTORSIZE + 8,
++ if (MTD_READOOB(nftl->mbd.mtd, block * nftl->EraseSize + SECTORSIZE + 8,
+ 8, &retlen, (char *)&uci) < 0)
+ goto default_uci1;
+
+@@ -314,7 +309,7 @@
+ /* XXX: use async erase interface, XXX: test return code */
+ instr->addr = block * nftl->EraseSize;
+ instr->len = nftl->EraseSize;
+- MTD_ERASE(nftl->mtd, instr);
++ MTD_ERASE(nftl->mbd.mtd, instr);
+
+ if (instr->state == MTD_ERASE_FAILED) {
+ /* could not format, FixMe: We should update the BadUnitTable
+@@ -337,7 +332,7 @@
+ return -1;
+
+ uci.WearInfo = le32_to_cpu(nb_erases);
+- if (MTD_WRITEOOB(nftl->mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8,
++ if (MTD_WRITEOOB(nftl->mbd.mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8,
+ &retlen, (char *)&uci) < 0)
+ return -1;
+ return 0;
+@@ -363,7 +358,7 @@
+ block = first_block;
+ for (;;) {
+ for (i = 0; i < sectors_per_block; i++) {
+- if (MTD_READOOB(nftl->mtd, block * nftl->EraseSize + i * SECTORSIZE,
++ if (MTD_READOOB(nftl->mbd.mtd, block * nftl->EraseSize + i * SECTORSIZE,
+ 8, &retlen, (char *)&bci) < 0)
+ status = SECTOR_IGNORE;
+ else
+@@ -383,7 +378,7 @@
+ /* sector not free actually : mark it as SECTOR_IGNORE */
+ bci.Status = SECTOR_IGNORE;
+ bci.Status1 = SECTOR_IGNORE;
+- MTD_WRITEOOB(nftl->mtd,
++ MTD_WRITEOOB(nftl->mbd.mtd,
+ block * nftl->EraseSize + i * SECTORSIZE,
+ 8, &retlen, (char *)&bci);
+ }
+@@ -476,7 +471,7 @@
+ size_t retlen;
+
+ /* check erase mark. */
+- if (MTD_READOOB(nftl->mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8,
++ if (MTD_READOOB(nftl->mbd.mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8,
+ &retlen, (char *)&h1) < 0)
+ return -1;
+
+@@ -491,7 +486,7 @@
+ h1.EraseMark = cpu_to_le16(ERASE_MARK);
+ h1.EraseMark1 = cpu_to_le16(ERASE_MARK);
+ h1.WearInfo = cpu_to_le32(0);
+- if (MTD_WRITEOOB(nftl->mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8,
++ if (MTD_WRITEOOB(nftl->mbd.mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8,
+ &retlen, (char *)&h1) < 0)
+ return -1;
+ } else {
+@@ -503,7 +498,7 @@
+ SECTORSIZE, 0) != 0)
+ return -1;
+
+- if (MTD_READOOB(nftl->mtd, block * nftl->EraseSize + i,
++ if (MTD_READOOB(nftl->mbd.mtd, block * nftl->EraseSize + i,
+ 16, &retlen, buf) < 0)
+ return -1;
+ if (i == SECTORSIZE) {
+@@ -533,7 +528,7 @@
+ struct nftl_uci2 uci;
+ size_t retlen;
+
+- if (MTD_READOOB(nftl->mtd, block * nftl->EraseSize + 2 * SECTORSIZE + 8,
++ if (MTD_READOOB(nftl->mbd.mtd, block * nftl->EraseSize + 2 * SECTORSIZE + 8,
+ 8, &retlen, (char *)&uci) < 0)
+ return 0;
+
+@@ -572,9 +567,9 @@
+
+ for (;;) {
+ /* read the block header. If error, we format the chain */
+- if (MTD_READOOB(s->mtd, block * s->EraseSize + 8, 8,
++ if (MTD_READOOB(s->mbd.mtd, block * s->EraseSize + 8, 8,
+ &retlen, (char *)&h0) < 0 ||
+- MTD_READOOB(s->mtd, block * s->EraseSize + SECTORSIZE + 8, 8,
++ MTD_READOOB(s->mbd.mtd, block * s->EraseSize + SECTORSIZE + 8, 8,
+ &retlen, (char *)&h1) < 0) {
+ s->ReplUnitTable[block] = BLOCK_NIL;
+ do_format_chain = 1;
+diff -Nurb linux-mips-2.4.24-pre2/drivers/mtd/redboot.c linux/drivers/mtd/redboot.c
+--- linux-mips-2.4.24-pre2/drivers/mtd/redboot.c 2004-11-17 18:04:53.000000000 +0100
++++ linux/drivers/mtd/redboot.c 2004-11-17 18:17:58.901334896 +0100
+@@ -1,5 +1,5 @@
+ /*
+- * $Id$
++ * $Id$
+ *
+ * Parse RedBoot-style Flash Image System (FIS) tables and
+ * produce a Linux partition array to match.
+@@ -7,6 +7,7 @@
+
+ #include <linux/kernel.h>
+ #include <linux/slab.h>
++#include <linux/init.h>
+
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/partitions.h>
+@@ -34,7 +35,9 @@
+ return 1;
+ }
+
+-int parse_redboot_partitions(struct mtd_info *master, struct mtd_partition **pparts)
++static int parse_redboot_partitions(struct mtd_info *master,
++ struct mtd_partition **pparts,
++ unsigned long fis_origin)
+ {
+ int nrparts = 0;
+ struct fis_image_desc *buf;
+@@ -43,7 +46,9 @@
+ int ret, i;
+ size_t retlen;
+ char *names;
++ char *nullname;
+ int namelen = 0;
++ static char nullstring[] = "unallocated";
+
+ buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
+
+@@ -90,7 +95,11 @@
+ goto out;
+ }
+ new_fl->img = &buf[i];
++ if (fis_origin) {
++ buf[i].flash_base -= fis_origin;
++ } else {
+ buf[i].flash_base &= master->size-1;
++ }
+
+ /* I'm sure the JFFS2 code has done me permanent damage.
+ * I now think the following is _normal_
+@@ -110,18 +119,24 @@
+ if (tmp_fl->img->flash_base + tmp_fl->img->size + master->erasesize < tmp_fl->next->img->flash_base)
+ nrparts++;
+ }
+- parts = kmalloc(sizeof(*parts)*nrparts + namelen, GFP_KERNEL);
++ parts = kmalloc(sizeof(*parts)*nrparts + sizeof(nullstring) + namelen, GFP_KERNEL);
+
+ if (!parts) {
+ ret = -ENOMEM;
+ goto out;
+ }
+- names = (char *)&parts[nrparts];
++
+ memset(parts, 0, sizeof(*parts)*nrparts + namelen);
++
++ /* FIXME: Include nullname only if it's used */
++ nullname = (char *)&parts[nrparts];
++ sprintf(nullname, nullstring);
++ names = nullname + sizeof(nullstring);
++
+ i=0;
+
+ if (fl->img->flash_base) {
+- parts[0].name = "unallocated space";
++ parts[0].name = nullname;
+ parts[0].size = fl->img->flash_base;
+ parts[0].offset = 0;
+ }
+@@ -133,11 +148,11 @@
+ strcpy(names, fl->img->name);
+ names += strlen(names)+1;
+
+- if(fl->next && fl->img->flash_base + fl->img->size + master->erasesize < fl->next->img->flash_base) {
++ if(fl->next && fl->img->flash_base + fl->img->size + master->erasesize <= fl->next->img->flash_base) {
+ i++;
+ parts[i].offset = parts[i-1].size + parts[i-1].offset;
+ parts[i].size = fl->next->img->flash_base - parts[i].offset;
+- parts[i].name = "unallocated space";
++ parts[i].name = nullname;
+ }
+ tmp_fl = fl;
+ fl = fl->next;
+@@ -155,7 +170,24 @@
+ return ret;
+ }
+
+-EXPORT_SYMBOL(parse_redboot_partitions);
++static struct mtd_part_parser redboot_parser = {
++ .owner = THIS_MODULE,
++ .parse_fn = parse_redboot_partitions,
++ .name = "RedBoot",
++};
++
++static int __init redboot_parser_init(void)
++{
++ return register_mtd_parser(&redboot_parser);
++}
++
++static void __exit redboot_parser_exit(void)
++{
++ deregister_mtd_parser(&redboot_parser);
++}
++
++module_init(redboot_parser_init);
++module_exit(redboot_parser_exit);
+
+ MODULE_LICENSE("GPL");
+ MODULE_AUTHOR("Red Hat, Inc. - David Woodhouse <dwmw2@cambridge.redhat.com>");
+diff -Nurb linux-mips-2.4.24-pre2/fs/Config.in linux/fs/Config.in
+--- linux-mips-2.4.24-pre2/fs/Config.in 2004-11-17 18:05:04.000000000 +0100
++++ linux/fs/Config.in 2004-11-17 18:17:59.489245520 +0100
+@@ -49,6 +49,7 @@
+ dep_tristate 'Journalling Flash File System v2 (JFFS2) support' CONFIG_JFFS2_FS $CONFIG_MTD
+ if [ "$CONFIG_JFFS2_FS" = "y" -o "$CONFIG_JFFS2_FS" = "m" ] ; then
+ int 'JFFS2 debugging verbosity (0 = quiet, 2 = noisy)' CONFIG_JFFS2_FS_DEBUG 0
++ bool 'JFFS2 support for NAND chips' CONFIG_JFFS2_FS_NAND
+ fi
+ tristate 'Compressed ROM file system support' CONFIG_CRAMFS
+ bool 'Virtual memory file system support (former shm fs)' CONFIG_TMPFS
+diff -Nurb linux-mips-2.4.24-pre2/fs/jffs2/Makefile linux/fs/jffs2/Makefile
+--- linux-mips-2.4.24-pre2/fs/jffs2/Makefile 2004-11-17 18:05:04.000000000 +0100
++++ linux/fs/jffs2/Makefile 2004-11-17 18:17:59.000000000 +0100
+@@ -1,7 +1,7 @@
+ #
+ # Makefile for the linux Journalling Flash FileSystem (JFFS) routines.
+ #
+-# $Id$
++# $Id$
+ #
+ # Note! Dependencies are done automagically by 'make dep', which also
+ # removes any old dependencies. DON'T put your own dependencies here
+@@ -10,16 +10,31 @@
+ # Note 2! The CFLAGS definitions are now in the main makefile...
+
+
+-COMPR_OBJS := compr.o compr_rubin.o compr_rtime.o pushpull.o \
+- compr_zlib.o
++obj-$(CONFIG_JFFS2_FS) := jffs2.o
++
++COMPR_OBJS := compr.o compr_rubin.o compr_rtime.o compr_zlib.o
+ JFFS2_OBJS := dir.o file.o ioctl.o nodelist.o malloc.o \
+- read.o nodemgmt.o readinode.o super.o write.o scan.o gc.o \
+- symlink.o build.o erase.o background.o
++ read.o nodemgmt.o readinode.o write.o scan.o gc.o \
++ symlink.o build.o erase.o background.o fs.o writev.o
+
+-O_TARGET := jffs2.o
++BELOW25 := $(shell echo $(PATCHLEVEL) | sed s/[1234]/y/)
++
++ifeq ($(BELOW25),y)
++LINUX_OBJS := super-v24.o crc32.o rbtree.o
++else
++LINUX_OBJS := super.o
++endif
+
+-obj-y := $(COMPR_OBJS) $(JFFS2_OBJS)
+-obj-m := $(O_TARGET)
++NAND_OBJS-$(CONFIG_JFFS2_FS_NAND) := wbuf.o
+
++jffs2-objs := $(COMPR_OBJS) $(JFFS2_OBJS) $(VERS_OBJS) $(NAND_OBJS-y) \
++ $(LINUX_OBJS)
++
++
++# 2.4 build compatibility
++ifeq ($(BELOW25),y)
++obj-y := $(jffs2-objs)
++O_TARGET := jffs2.o
+ include $(TOPDIR)/Rules.make
++endif
+
+diff -Nurb linux-mips-2.4.24-pre2/fs/jffs2/background.c linux/fs/jffs2/background.c
+--- linux-mips-2.4.24-pre2/fs/jffs2/background.c 2004-11-17 18:05:04.000000000 +0100
++++ linux/fs/jffs2/background.c 2004-11-17 18:17:59.000000000 +0100
+@@ -1,61 +1,36 @@
+ /*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+- * Copyright (C) 2001 Red Hat, Inc.
++ * Copyright (C) 2001-2003 Red Hat, Inc.
+ *
+- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
++ * Created by David Woodhouse <dwmw2@redhat.com>
+ *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
++ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+- * The contents of this file are subject to the Red Hat eCos Public
+- * License Version 1.1 (the "Licence"); you may not use this file
+- * except in compliance with the Licence. You may obtain a copy of
+- * the Licence at http://www.redhat.com/
+- *
+- * Software distributed under the Licence is distributed on an "AS IS"
+- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+- * See the Licence for the specific language governing rights and
+- * limitations under the Licence.
+- *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
+- *
+- * Alternatively, the contents of this file may be used under the
+- * terms of the GNU General Public License version 2 (the "GPL"), in
+- * which case the provisions of the GPL are applicable instead of the
+- * above. If you wish to allow the use of your version of this file
+- * only under the terms of the GPL and not to allow others to use your
+- * version of this file under the RHEPL, indicate your decision by
+- * deleting the provisions above and replace them with the notice and
+- * other provisions required by the GPL. If you do not delete the
+- * provisions above, a recipient may use your version of this file
+- * under either the RHEPL or the GPL.
+- *
+- * $Id$
++ * $Id$
+ *
+ */
+
+ #define __KERNEL_SYSCALLS__
+
+ #include <linux/kernel.h>
+-#include <linux/sched.h>
+-#include <linux/unistd.h>
+ #include <linux/jffs2.h>
+ #include <linux/mtd/mtd.h>
+-#include <linux/interrupt.h>
+ #include <linux/completion.h>
++#include <linux/sched.h>
++#include <linux/unistd.h>
++#include <linux/suspend.h>
+ #include "nodelist.h"
+
+
+ static int jffs2_garbage_collect_thread(void *);
+-static int thread_should_wake(struct jffs2_sb_info *c);
+
+ void jffs2_garbage_collect_trigger(struct jffs2_sb_info *c)
+ {
+- spin_lock_bh(&c->erase_completion_lock);
+- if (c->gc_task && thread_should_wake(c))
++ spin_lock(&c->erase_completion_lock);
++ if (c->gc_task && jffs2_thread_should_wake(c))
+ send_sig(SIGHUP, c->gc_task, 1);
+- spin_unlock_bh(&c->erase_completion_lock);
++ spin_unlock(&c->erase_completion_lock);
+ }
+
+ /* This must only ever be called when no GC thread is currently running */
+@@ -86,12 +61,12 @@
+
+ void jffs2_stop_garbage_collect_thread(struct jffs2_sb_info *c)
+ {
+- spin_lock_bh(&c->erase_completion_lock);
++ spin_lock(&c->erase_completion_lock);
+ if (c->gc_task) {
+ D1(printk(KERN_DEBUG "jffs2: Killing GC task %d\n", c->gc_task->pid));
+ send_sig(SIGKILL, c->gc_task, 1);
+ }
+- spin_unlock_bh(&c->erase_completion_lock);
++ spin_unlock(&c->erase_completion_lock);
+ wait_for_completion(&c->gc_thread_exit);
+ }
+
+@@ -99,34 +74,37 @@
+ {
+ struct jffs2_sb_info *c = _c;
+
+- daemonize();
+- current->tty = NULL;
++ daemonize("jffs2_gcd_mtd%d", c->mtd->index);
++ allow_signal(SIGKILL);
++ allow_signal(SIGSTOP);
++ allow_signal(SIGCONT);
++
+ c->gc_task = current;
+ up(&c->gc_thread_start);
+
+- sprintf(current->comm, "jffs2_gcd_mtd%d", c->mtd->index);
+-
+- /* FIXME in the 2.2 backport */
+- current->nice = 10;
++ set_user_nice(current, 10);
+
+ for (;;) {
+- spin_lock_irq(&current->sigmask_lock);
+- siginitsetinv (&current->blocked, sigmask(SIGHUP) | sigmask(SIGKILL) | sigmask(SIGSTOP) | sigmask(SIGCONT));
+- recalc_sigpending(current);
+- spin_unlock_irq(&current->sigmask_lock);
++ allow_signal(SIGHUP);
+
+- if (!thread_should_wake(c)) {
++ if (!jffs2_thread_should_wake(c)) {
+ set_current_state (TASK_INTERRUPTIBLE);
+ D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread sleeping...\n"));
+- /* Yes, there's a race here; we checked thread_should_wake() before
+- setting current->state to TASK_INTERRUPTIBLE. But it doesn't
++ /* Yes, there's a race here; we checked jffs2_thread_should_wake()
++ before setting current->state to TASK_INTERRUPTIBLE. But it doesn't
+ matter - We don't care if we miss a wakeup, because the GC thread
+ is only an optimisation anyway. */
+ schedule();
+ }
+
+- if (current->need_resched)
+- schedule();
++ if (current->flags & PF_FREEZE) {
++ refrigerator(0);
++ /* refrigerator() should recalc sigpending for us
++ but doesn't. No matter - allow_signal() will. */
++ continue;
++ }
++
++ cond_resched();
+
+ /* Put_super will send a SIGKILL and then wait on the sem.
+ */
+@@ -134,9 +112,7 @@
+ siginfo_t info;
+ unsigned long signr;
+
+- spin_lock_irq(&current->sigmask_lock);
+- signr = dequeue_signal(&current->blocked, &info);
+- spin_unlock_irq(&current->sigmask_lock);
++ signr = dequeue_signal_lock(current, &current->blocked, &info);
+
+ switch(signr) {
+ case SIGSTOP:
+@@ -147,9 +123,10 @@
+
+ case SIGKILL:
+ D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): SIGKILL received.\n"));
+- spin_lock_bh(&c->erase_completion_lock);
++ die:
++ spin_lock(&c->erase_completion_lock);
+ c->gc_task = NULL;
+- spin_unlock_bh(&c->erase_completion_lock);
++ spin_unlock(&c->erase_completion_lock);
+ complete_and_exit(&c->gc_thread_exit, 0);
+
+ case SIGHUP:
+@@ -157,27 +134,15 @@
+ break;
+ default:
+ D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): signal %ld received\n", signr));
+-
+ }
+ }
+ /* We don't want SIGHUP to interrupt us. STOP and KILL are OK though. */
+- spin_lock_irq(&current->sigmask_lock);
+- siginitsetinv (&current->blocked, sigmask(SIGKILL) | sigmask(SIGSTOP) | sigmask(SIGCONT));
+- recalc_sigpending(current);
+- spin_unlock_irq(&current->sigmask_lock);
++ disallow_signal(SIGHUP);
+
+ D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): pass\n"));
+- jffs2_garbage_collect_pass(c);
++ if (jffs2_garbage_collect_pass(c) == -ENOSPC) {
++ printk(KERN_NOTICE "No space for garbage collection. Aborting GC thread\n");
++ goto die;
++ }
+ }
+-}
+-
+-static int thread_should_wake(struct jffs2_sb_info *c)
+-{
+- D1(printk(KERN_DEBUG "thread_should_wake(): nr_free_blocks %d, nr_erasing_blocks %d, dirty_size 0x%x\n",
+- c->nr_free_blocks, c->nr_erasing_blocks, c->dirty_size));
+- if (c->nr_free_blocks + c->nr_erasing_blocks < JFFS2_RESERVED_BLOCKS_GCTRIGGER &&
+- c->dirty_size > c->sector_size)
+- return 1;
+- else
+- return 0;
+ }
+diff -Nurb linux-mips-2.4.24-pre2/fs/jffs2/build.c linux/fs/jffs2/build.c
+--- linux-mips-2.4.24-pre2/fs/jffs2/build.c 2004-11-17 18:05:04.000000000 +0100
++++ linux/fs/jffs2/build.c 2004-11-17 18:17:59.000000000 +0100
+@@ -1,47 +1,22 @@
+ /*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+- * Copyright (C) 2001 Red Hat, Inc.
++ * Copyright (C) 2001-2003 Red Hat, Inc.
+ *
+- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
++ * Created by David Woodhouse <dwmw2@redhat.com>
+ *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
++ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+- * The contents of this file are subject to the Red Hat eCos Public
+- * License Version 1.1 (the "Licence"); you may not use this file
+- * except in compliance with the Licence. You may obtain a copy of
+- * the Licence at http://www.redhat.com/
+- *
+- * Software distributed under the Licence is distributed on an "AS IS"
+- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+- * See the Licence for the specific language governing rights and
+- * limitations under the Licence.
+- *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
+- *
+- * Alternatively, the contents of this file may be used under the
+- * terms of the GNU General Public License version 2 (the "GPL"), in
+- * which case the provisions of the GPL are applicable instead of the
+- * above. If you wish to allow the use of your version of this file
+- * only under the terms of the GPL and not to allow others to use your
+- * version of this file under the RHEPL, indicate your decision by
+- * deleting the provisions above and replace them with the notice and
+- * other provisions required by the GPL. If you do not delete the
+- * provisions above, a recipient may use your version of this file
+- * under either the RHEPL or the GPL.
+- *
+- * $Id$
++ * $Id$
+ *
+ */
+
+ #include <linux/kernel.h>
+-#include <linux/jffs2.h>
++#include <linux/sched.h>
+ #include <linux/slab.h>
+ #include "nodelist.h"
+
+-int jffs2_build_inode_pass1(struct jffs2_sb_info *, struct jffs2_inode_cache *);
+-int jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *, struct jffs2_inode_cache *);
++static void jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *, struct jffs2_inode_cache *, struct jffs2_full_dirent **);
+
+ static inline struct jffs2_inode_cache *
+ first_inode_chain(int *i, struct jffs2_sb_info *c)
+@@ -68,16 +43,52 @@
+ ic; \
+ ic = next_inode(&i, ic, (c)))
+
++
++static inline void jffs2_build_inode_pass1(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic)
++{
++ struct jffs2_full_dirent *fd;
++
++ D1(printk(KERN_DEBUG "jffs2_build_inode building directory inode #%u\n", ic->ino));
++
++ /* For each child, increase nlink */
++ for(fd = ic->scan_dents; fd; fd = fd->next) {
++ struct jffs2_inode_cache *child_ic;
++ if (!fd->ino)
++ continue;
++
++ /* XXX: Can get high latency here with huge directories */
++
++ child_ic = jffs2_get_ino_cache(c, fd->ino);
++ if (!child_ic) {
++ printk(KERN_NOTICE "Eep. Child \"%s\" (ino #%u) of dir ino #%u doesn't exist!\n",
++ fd->name, fd->ino, ic->ino);
++ continue;
++ }
++
++ if (child_ic->nlink++ && fd->type == DT_DIR) {
++ printk(KERN_NOTICE "Child dir \"%s\" (ino #%u) of dir ino #%u appears to be a hard link\n", fd->name, fd->ino, ic->ino);
++ if (fd->ino == 1 && ic->ino == 1) {
++ printk(KERN_NOTICE "This is mostly harmless, and probably caused by creating a JFFS2 image\n");
++ printk(KERN_NOTICE "using a buggy version of mkfs.jffs2. Use at least v1.17.\n");
++ }
++ /* What do we do about it? */
++ }
++ D1(printk(KERN_DEBUG "Increased nlink for child \"%s\" (ino #%u)\n", fd->name, fd->ino));
++ /* Can't free them. We might need them in pass 2 */
++ }
++}
++
+ /* Scan plan:
+ - Scan physical nodes. Build map of inodes/dirents. Allocate inocaches as we go
+ - Scan directory tree from top down, setting nlink in inocaches
+ - Scan inocaches for inodes with nlink==0
+ */
+-int jffs2_build_filesystem(struct jffs2_sb_info *c)
++static int jffs2_build_filesystem(struct jffs2_sb_info *c)
+ {
+ int ret;
+ int i;
+ struct jffs2_inode_cache *ic;
++ struct jffs2_full_dirent *dead_fds = NULL;
+
+ /* First, scan the medium and build all the inode caches with
+ lists of physical nodes */
+@@ -90,14 +101,17 @@
+ return ret;
+
+ D1(printk(KERN_DEBUG "Scanned flash completely\n"));
+- /* Now build the data map for each inode, marking obsoleted nodes
+- as such, and also increase nlink of any children. */
++ D1(jffs2_dump_block_lists(c));
++
++ /* Now scan the directory tree, increasing nlink according to every dirent found. */
+ for_each_inode(i, c, ic) {
+ D1(printk(KERN_DEBUG "Pass 1: ino #%u\n", ic->ino));
+- ret = jffs2_build_inode_pass1(c, ic);
+- if (ret) {
+- D1(printk(KERN_WARNING "Eep. jffs2_build_inode_pass1 for ino %d returned %d\n", ic->ino, ret));
+- return ret;
++
++ D1(BUG_ON(ic->ino > c->highest_ino));
++
++ if (ic->scan_dents) {
++ jffs2_build_inode_pass1(c, ic);
++ cond_resched();
+ }
+ }
+ D1(printk(KERN_DEBUG "Pass 1 complete\n"));
+@@ -107,181 +121,226 @@
+ children too, and repeat the scan. As that's going to be
+ a fairly uncommon occurrence, it's not so evil to do it this
+ way. Recursion bad. */
+- do {
+- D1(printk(KERN_DEBUG "Pass 2 (re)starting\n"));
+- ret = 0;
++ D1(printk(KERN_DEBUG "Pass 2 starting\n"));
++
+ for_each_inode(i, c, ic) {
+ D1(printk(KERN_DEBUG "Pass 2: ino #%u, nlink %d, ic %p, nodes %p\n", ic->ino, ic->nlink, ic, ic->nodes));
+ if (ic->nlink)
+ continue;
+
+- ret = jffs2_build_remove_unlinked_inode(c, ic);
+- if (ret)
+- break;
+- /* -EAGAIN means the inode's nlink was zero, so we deleted it,
+- and furthermore that it had children and their nlink has now
+- gone to zero too. So we have to restart the scan. */
++ jffs2_build_remove_unlinked_inode(c, ic, &dead_fds);
++ cond_resched();
++ }
++
++ D1(printk(KERN_DEBUG "Pass 2a starting\n"));
++
++ while (dead_fds) {
++ struct jffs2_inode_cache *ic;
++ struct jffs2_full_dirent *fd = dead_fds;
++
++ dead_fds = fd->next;
++
++ ic = jffs2_get_ino_cache(c, fd->ino);
++ D1(printk(KERN_DEBUG "Removing dead_fd ino #%u (\"%s\"), ic at %p\n", fd->ino, fd->name, ic));
++
++ if (ic)
++ jffs2_build_remove_unlinked_inode(c, ic, &dead_fds);
++ jffs2_free_full_dirent(fd);
+ }
+- } while(ret == -EAGAIN);
+
+ D1(printk(KERN_DEBUG "Pass 2 complete\n"));
+
+- /* Finally, we can scan again and free the dirent nodes and scan_info structs */
++ /* Finally, we can scan again and free the dirent structs */
+ for_each_inode(i, c, ic) {
+- struct jffs2_scan_info *scan = ic->scan;
+ struct jffs2_full_dirent *fd;
+ D1(printk(KERN_DEBUG "Pass 3: ino #%u, ic %p, nodes %p\n", ic->ino, ic, ic->nodes));
+- if (!scan) {
+- if (ic->nlink) {
+- D1(printk(KERN_WARNING "Why no scan struct for ino #%u which has nlink %d?\n", ic->ino, ic->nlink));
+- }
+- continue;
+- }
+- ic->scan = NULL;
+- while(scan->dents) {
+- fd = scan->dents;
+- scan->dents = fd->next;
++
++ while(ic->scan_dents) {
++ fd = ic->scan_dents;
++ ic->scan_dents = fd->next;
+ jffs2_free_full_dirent(fd);
+ }
+- kfree(scan);
++ ic->scan_dents = NULL;
++ cond_resched();
+ }
+ D1(printk(KERN_DEBUG "Pass 3 complete\n"));
++ D1(jffs2_dump_block_lists(c));
++
++ /* Rotate the lists by some number to ensure wear levelling */
++ jffs2_rotate_lists(c);
+
+ return ret;
+ }
+
+-int jffs2_build_inode_pass1(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic)
++static void jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic, struct jffs2_full_dirent **dead_fds)
+ {
+- struct jffs2_tmp_dnode_info *tn;
++ struct jffs2_raw_node_ref *raw;
+ struct jffs2_full_dirent *fd;
+- struct jffs2_node_frag *fraglist = NULL;
+- struct jffs2_tmp_dnode_info *metadata = NULL;
+-
+- D1(printk(KERN_DEBUG "jffs2_build_inode building inode #%u\n", ic->ino));
+- if (ic->ino > c->highest_ino)
+- c->highest_ino = ic->ino;
+
+- if (!ic->scan->tmpnodes && ic->ino != 1) {
+- D1(printk(KERN_DEBUG "jffs2_build_inode: ino #%u has no data nodes!\n", ic->ino));
+- }
+- /* Build the list to make sure any obsolete nodes are marked as such */
+- while(ic->scan->tmpnodes) {
+- tn = ic->scan->tmpnodes;
+- ic->scan->tmpnodes = tn->next;
+-
+- if (metadata && tn->version > metadata->version) {
+- D1(printk(KERN_DEBUG "jffs2_build_inode_pass1 ignoring old metadata at 0x%08x\n",
+- metadata->fn->raw->flash_offset &~3));
++ D1(printk(KERN_DEBUG "JFFS2: Removing ino #%u with nlink == zero.\n", ic->ino));
+
+- jffs2_free_full_dnode(metadata->fn);
+- jffs2_free_tmp_dnode_info(metadata);
+- metadata = NULL;
++ for (raw = ic->nodes; raw != (void *)ic; raw = raw->next_in_ino) {
++ D1(printk(KERN_DEBUG "obsoleting node at 0x%08x\n", ref_offset(raw)));
++ jffs2_mark_node_obsolete(c, raw);
+ }
+
+- if (tn->fn->size) {
+- jffs2_add_full_dnode_to_fraglist (c, &fraglist, tn->fn);
+- jffs2_free_tmp_dnode_info(tn);
+- } else {
+- if (!metadata) {
+- metadata = tn;
+- } else {
+- D1(printk(KERN_DEBUG "jffs2_build_inode_pass1 ignoring new metadata at 0x%08x\n",
+- tn->fn->raw->flash_offset &~3));
+-
+- jffs2_free_full_dnode(tn->fn);
+- jffs2_free_tmp_dnode_info(tn);
+- }
+- }
+- }
++ if (ic->scan_dents) {
++ int whinged = 0;
++ D1(printk(KERN_DEBUG "Inode #%u was a directory which may have children...\n", ic->ino));
+
+- /* OK. Now clear up */
+- if (metadata) {
+- jffs2_free_full_dnode(metadata->fn);
+- jffs2_free_tmp_dnode_info(metadata);
+- }
+- metadata = NULL;
++ while(ic->scan_dents) {
++ struct jffs2_inode_cache *child_ic;
+
+- while (fraglist) {
+- struct jffs2_node_frag *frag;
+- frag = fraglist;
+- fraglist = fraglist->next;
++ fd = ic->scan_dents;
++ ic->scan_dents = fd->next;
+
+- if (frag->node && !(--frag->node->frags)) {
+- jffs2_free_full_dnode(frag->node);
++ if (!fd->ino) {
++ /* It's a deletion dirent. Ignore it */
++ D1(printk(KERN_DEBUG "Child \"%s\" is a deletion dirent, skipping...\n", fd->name));
++ jffs2_free_full_dirent(fd);
++ continue;
+ }
+- jffs2_free_node_frag(frag);
++ if (!whinged) {
++ whinged = 1;
++ printk(KERN_NOTICE "Inode #%u was a directory with children - removing those too...\n", ic->ino);
+ }
+
+- /* Now for each child, increase nlink */
+- for(fd=ic->scan->dents; fd; fd = fd->next) {
+- struct jffs2_inode_cache *child_ic;
+- if (!fd->ino)
+- continue;
++ D1(printk(KERN_DEBUG "Removing child \"%s\", ino #%u\n",
++ fd->name, fd->ino));
+
+ child_ic = jffs2_get_ino_cache(c, fd->ino);
+ if (!child_ic) {
+- printk(KERN_NOTICE "Eep. Child \"%s\" (ino #%u) of dir ino #%u doesn't exist!\n",
+- fd->name, fd->ino, ic->ino);
++ printk(KERN_NOTICE "Cannot remove child \"%s\", ino #%u, because it doesn't exist\n", fd->name, fd->ino);
++ jffs2_free_full_dirent(fd);
+ continue;
+ }
+
+- if (child_ic->nlink++ && fd->type == DT_DIR) {
+- printk(KERN_NOTICE "Child dir \"%s\" (ino #%u) of dir ino #%u appears to be a hard link\n", fd->name, fd->ino, ic->ino);
+- if (fd->ino == 1 && ic->ino == 1) {
+- printk(KERN_NOTICE "This is mostly harmless, and probably caused by creating a JFFS2 image\n");
+- printk(KERN_NOTICE "using a buggy version of mkfs.jffs2. Use at least v1.17.\n");
++ /* Reduce nlink of the child. If it's now zero, stick it on the
++ dead_fds list to be cleaned up later. Else just free the fd */
++
++ child_ic->nlink--;
++
++ if (!child_ic->nlink) {
++ D1(printk(KERN_DEBUG "Inode #%u (\"%s\") has now got zero nlink. Adding to dead_fds list.\n",
++ fd->ino, fd->name));
++ fd->next = *dead_fds;
++ *dead_fds = fd;
++ } else {
++ D1(printk(KERN_DEBUG "Inode #%u (\"%s\") has now got nlink %d. Ignoring.\n",
++ fd->ino, fd->name, child_ic->nlink));
++ jffs2_free_full_dirent(fd);
+ }
+- /* What do we do about it? */
+ }
+- D1(printk(KERN_DEBUG "Increased nlink for child \"%s\" (ino #%u)\n", fd->name, fd->ino));
+- /* Can't free them. We might need them in pass 2 */
+ }
+- return 0;
++
++ /*
++ We don't delete the inocache from the hash list and free it yet.
++ The erase code will do that, when all the nodes are completely gone.
++ */
+ }
+
+-int jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic)
++static void jffs2_calc_trigger_levels(struct jffs2_sb_info *c)
+ {
+- struct jffs2_raw_node_ref *raw;
+- struct jffs2_full_dirent *fd;
+- int ret = 0;
++ uint32_t size;
+
+- if(!ic->scan) {
+- D1(printk(KERN_DEBUG "ino #%u was already removed\n", ic->ino));
+- return 0;
+- }
++ /* Deletion should almost _always_ be allowed. We're fairly
++ buggered once we stop allowing people to delete stuff
++ because there's not enough free space... */
++ c->resv_blocks_deletion = 2;
++
++ /* Be conservative about how much space we need before we allow writes.
++ On top of that which is required for deletia, require an extra 2%
++ of the medium to be available, for overhead caused by nodes being
++ split across blocks, etc. */
++
++ size = c->flash_size / 50; /* 2% of flash size */
++ size += c->nr_blocks * 100; /* And 100 bytes per eraseblock */
++ size += c->sector_size - 1; /* ... and round up */
++
++ c->resv_blocks_write = c->resv_blocks_deletion + (size / c->sector_size);
++
++ /* When do we let the GC thread run in the background */
++
++ c->resv_blocks_gctrigger = c->resv_blocks_write + 1;
++
++ /* When do we allow garbage collection to merge nodes to make
++ long-term progress at the expense of short-term space exhaustion? */
++ c->resv_blocks_gcmerge = c->resv_blocks_deletion + 1;
++
++ /* When do we allow garbage collection to eat from bad blocks rather
++ than actually making progress? */
++ c->resv_blocks_gcbad = 0;//c->resv_blocks_deletion + 2;
++
++ /* If there's less than this amount of dirty space, don't bother
++ trying to GC to make more space. It'll be a fruitless task */
++ c->nospc_dirty_size = c->sector_size + (c->flash_size / 100);
++
++ D1(printk(KERN_DEBUG "JFFS2 trigger levels (size %d KiB, block size %d KiB, %d blocks)\n",
++ c->flash_size / 1024, c->sector_size / 1024, c->nr_blocks));
++ D1(printk(KERN_DEBUG "Blocks required to allow deletion: %d (%d KiB)\n",
++ c->resv_blocks_deletion, c->resv_blocks_deletion*c->sector_size/1024));
++ D1(printk(KERN_DEBUG "Blocks required to allow writes: %d (%d KiB)\n",
++ c->resv_blocks_write, c->resv_blocks_write*c->sector_size/1024));
++ D1(printk(KERN_DEBUG "Blocks required to quiesce GC thread: %d (%d KiB)\n",
++ c->resv_blocks_gctrigger, c->resv_blocks_gctrigger*c->sector_size/1024));
++ D1(printk(KERN_DEBUG "Blocks required to allow GC merges: %d (%d KiB)\n",
++ c->resv_blocks_gcmerge, c->resv_blocks_gcmerge*c->sector_size/1024));
++ D1(printk(KERN_DEBUG "Blocks required to GC bad blocks: %d (%d KiB)\n",
++ c->resv_blocks_gcbad, c->resv_blocks_gcbad*c->sector_size/1024));
++ D1(printk(KERN_DEBUG "Amount of dirty space required to GC: %d bytes\n",
++ c->nospc_dirty_size));
++}
+
+- D1(printk(KERN_DEBUG "JFFS2: Removing ino #%u with nlink == zero.\n", ic->ino));
++int jffs2_do_mount_fs(struct jffs2_sb_info *c)
++{
++ int i;
+
+- for (raw = ic->nodes; raw != (void *)ic; raw = raw->next_in_ino) {
+- D1(printk(KERN_DEBUG "obsoleting node at 0x%08x\n", raw->flash_offset&~3));
+- jffs2_mark_node_obsolete(c, raw);
++ c->free_size = c->flash_size;
++ c->nr_blocks = c->flash_size / c->sector_size;
++ c->blocks = kmalloc(sizeof(struct jffs2_eraseblock) * c->nr_blocks, GFP_KERNEL);
++ if (!c->blocks)
++ return -ENOMEM;
++ for (i=0; i<c->nr_blocks; i++) {
++ INIT_LIST_HEAD(&c->blocks[i].list);
++ c->blocks[i].offset = i * c->sector_size;
++ c->blocks[i].free_size = c->sector_size;
++ c->blocks[i].dirty_size = 0;
++ c->blocks[i].wasted_size = 0;
++ c->blocks[i].unchecked_size = 0;
++ c->blocks[i].used_size = 0;
++ c->blocks[i].first_node = NULL;
++ c->blocks[i].last_node = NULL;
++ }
++
++ init_MUTEX(&c->alloc_sem);
++ init_MUTEX(&c->erase_free_sem);
++ init_waitqueue_head(&c->erase_wait);
++ init_waitqueue_head(&c->inocache_wq);
++ spin_lock_init(&c->erase_completion_lock);
++ spin_lock_init(&c->inocache_lock);
++
++ INIT_LIST_HEAD(&c->clean_list);
++ INIT_LIST_HEAD(&c->very_dirty_list);
++ INIT_LIST_HEAD(&c->dirty_list);
++ INIT_LIST_HEAD(&c->erasable_list);
++ INIT_LIST_HEAD(&c->erasing_list);
++ INIT_LIST_HEAD(&c->erase_pending_list);
++ INIT_LIST_HEAD(&c->erasable_pending_wbuf_list);
++ INIT_LIST_HEAD(&c->erase_complete_list);
++ INIT_LIST_HEAD(&c->free_list);
++ INIT_LIST_HEAD(&c->bad_list);
++ INIT_LIST_HEAD(&c->bad_used_list);
++ c->highest_ino = 1;
++
++ if (jffs2_build_filesystem(c)) {
++ D1(printk(KERN_DEBUG "build_fs failed\n"));
++ jffs2_free_ino_caches(c);
++ jffs2_free_raw_node_refs(c);
++ kfree(c->blocks);
++ return -EIO;
+ }
+
+- if (ic->scan->dents) {
+- printk(KERN_NOTICE "Inode #%u was a directory with children - removing those too...\n", ic->ino);
+-
+- while(ic->scan->dents) {
+- struct jffs2_inode_cache *child_ic;
++ jffs2_calc_trigger_levels(c);
+
+- fd = ic->scan->dents;
+- ic->scan->dents = fd->next;
+-
+- D1(printk(KERN_DEBUG "Removing child \"%s\", ino #%u\n",
+- fd->name, fd->ino));
+-
+- child_ic = jffs2_get_ino_cache(c, fd->ino);
+- if (!child_ic) {
+- printk(KERN_NOTICE "Cannot remove child \"%s\", ino #%u, because it doesn't exist\n", fd->name, fd->ino);
+- continue;
+- }
+- jffs2_free_full_dirent(fd);
+- child_ic->nlink--;
+- }
+- ret = -EAGAIN;
+- }
+- kfree(ic->scan);
+- ic->scan = NULL;
+- // jffs2_del_ino_cache(c, ic);
+- // jffs2_free_inode_cache(ic);
+- return ret;
++ return 0;
+ }
+diff -Nurb linux-mips-2.4.24-pre2/fs/jffs2/compr.c linux/fs/jffs2/compr.c
+--- linux-mips-2.4.24-pre2/fs/jffs2/compr.c 2004-11-17 18:05:04.000000000 +0100
++++ linux/fs/jffs2/compr.c 2004-11-17 18:17:59.000000000 +0100
+@@ -1,59 +1,37 @@
+ /*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+- * Copyright (C) 2001 Red Hat, Inc.
++ * Copyright (C) 2001-2003 Red Hat, Inc.
+ *
+ * Created by Arjan van de Ven <arjanv@redhat.com>
+ *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
++ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+- * The contents of this file are subject to the Red Hat eCos Public
+- * License Version 1.1 (the "Licence"); you may not use this file
+- * except in compliance with the Licence. You may obtain a copy of
+- * the Licence at http://www.redhat.com/
+- *
+- * Software distributed under the Licence is distributed on an "AS IS"
+- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+- * See the Licence for the specific language governing rights and
+- * limitations under the Licence.
+- *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
+- *
+- * Alternatively, the contents of this file may be used under the
+- * terms of the GNU General Public License version 2 (the "GPL"), in
+- * which case the provisions of the GPL are applicable instead of the
+- * above. If you wish to allow the use of your version of this file
+- * only under the terms of the GPL and not to allow others to use your
+- * version of this file under the RHEPL, indicate your decision by
+- * deleting the provisions above and replace them with the notice and
+- * other provisions required by the GPL. If you do not delete the
+- * provisions above, a recipient may use your version of this file
+- * under either the RHEPL or the GPL.
+- *
+- * $Id$
++ * $Id$
+ *
+ */
+
+ #include <linux/kernel.h>
+ #include <linux/string.h>
+-#include <linux/types.h>
+ #include <linux/errno.h>
++#include <linux/types.h>
++#include <linux/slab.h>
+ #include <linux/jffs2.h>
++#include "nodelist.h"
+
+-int zlib_compress(unsigned char *data_in, unsigned char *cpage_out, __u32 *sourcelen, __u32 *dstlen);
+-void zlib_decompress(unsigned char *data_in, unsigned char *cpage_out, __u32 srclen, __u32 destlen);
+-int rtime_compress(unsigned char *data_in, unsigned char *cpage_out, __u32 *sourcelen, __u32 *dstlen);
+-void rtime_decompress(unsigned char *data_in, unsigned char *cpage_out, __u32 srclen, __u32 destlen);
+-int rubinmips_compress(unsigned char *data_in, unsigned char *cpage_out, __u32 *sourcelen, __u32 *dstlen);
+-void rubinmips_decompress(unsigned char *data_in, unsigned char *cpage_out, __u32 srclen, __u32 destlen);
+-int dynrubin_compress(unsigned char *data_in, unsigned char *cpage_out, __u32 *sourcelen, __u32 *dstlen);
+-void dynrubin_decompress(unsigned char *data_in, unsigned char *cpage_out, __u32 srclen, __u32 destlen);
++int jffs2_zlib_compress(unsigned char *data_in, unsigned char *cpage_out, uint32_t *sourcelen, uint32_t *dstlen);
++void jffs2_zlib_decompress(unsigned char *data_in, unsigned char *cpage_out, uint32_t srclen, uint32_t destlen);
++int jffs2_rtime_compress(unsigned char *data_in, unsigned char *cpage_out, uint32_t *sourcelen, uint32_t *dstlen);
++void jffs2_rtime_decompress(unsigned char *data_in, unsigned char *cpage_out, uint32_t srclen, uint32_t destlen);
++int jffs2_rubinmips_compress(unsigned char *data_in, unsigned char *cpage_out, uint32_t *sourcelen, uint32_t *dstlen);
++void jffs2_rubinmips_decompress(unsigned char *data_in, unsigned char *cpage_out, uint32_t srclen, uint32_t destlen);
++int jffs2_dynrubin_compress(unsigned char *data_in, unsigned char *cpage_out, uint32_t *sourcelen, uint32_t *dstlen);
++void jffs2_dynrubin_decompress(unsigned char *data_in, unsigned char *cpage_out, uint32_t srclen, uint32_t destlen);
+
+
+ /* jffs2_compress:
+ * @data: Pointer to uncompressed data
+- * @cdata: Pointer to buffer for compressed data
++ * @cdata: Pointer to returned pointer to buffer for compressed data
+ * @datalen: On entry, holds the amount of data available for compression.
+ * On exit, expected to hold the amount of data actually compressed.
+ * @cdatalen: On entry, holds the amount of space available for compressed
+@@ -68,47 +46,59 @@
+ * jffs2_compress should compress as much as will fit, and should set
+ * *datalen accordingly to show the amount of data which were compressed.
+ */
+-unsigned char jffs2_compress(unsigned char *data_in, unsigned char *cpage_out,
+- __u32 *datalen, __u32 *cdatalen)
++unsigned char jffs2_compress(unsigned char *data_in, unsigned char **cpage_out,
++ uint32_t *datalen, uint32_t *cdatalen)
+ {
++#ifdef JFFS2_COMPRESSION
+ int ret;
+
+- ret = zlib_compress(data_in, cpage_out, datalen, cdatalen);
++ *cpage_out = kmalloc(*cdatalen, GFP_KERNEL);
++ if (!*cpage_out) {
++ printk(KERN_WARNING "No memory for compressor allocation. Compression failed\n");
++ goto out;
++ }
++
++#ifdef JFFS2_USE_ZLIB
++ ret = jffs2_zlib_compress(data_in, *cpage_out, datalen, cdatalen);
+ if (!ret) {
+ return JFFS2_COMPR_ZLIB;
+ }
+-#if 0 /* Disabled 23/9/1. With zlib it hardly ever gets a look in */
+- ret = dynrubin_compress(data_in, cpage_out, datalen, cdatalen);
++#endif
++#ifdef JFFS2_USE_DYNRUBIN
++ ret = jffs2_dynrubin_compress(data_in, *cpage_out, datalen, cdatalen);
+ if (!ret) {
+ return JFFS2_COMPR_DYNRUBIN;
+ }
+ #endif
+-#if 0 /* Disabled 26/2/1. Obsoleted by dynrubin */
+- ret = rubinmips_compress(data_in, cpage_out, datalen, cdatalen);
++#ifdef JFFS2_USE_RUBINMIPS
++ ret = jffs2_rubinmips_compress(data_in, *cpage_out, datalen, cdatalen);
+ if (!ret) {
+ return JFFS2_COMPR_RUBINMIPS;
+ }
+ #endif
++#ifdef JFFS2_USE_RTIME
+ /* rtime does manage to recompress already-compressed data */
+- ret = rtime_compress(data_in, cpage_out, datalen, cdatalen);
++ ret = jffs2_rtime_compress(data_in, *cpage_out, datalen, cdatalen);
+ if (!ret) {
+ return JFFS2_COMPR_RTIME;
+ }
+-#if 0
+- /* We don't need to copy. Let the caller special-case the COMPR_NONE case. */
+- /* If we get here, no compression is going to work */
+- /* But we might want to use the fragmentation part -- Arjan */
+- memcpy(cpage_out,data_in,min(*datalen,*cdatalen));
+- if (*datalen > *cdatalen)
+- *datalen = *cdatalen;
+ #endif
++ kfree(*cpage_out);
++#endif /* Compression */
++ out:
++ *cpage_out = data_in;
++ *datalen = *cdatalen;
+ return JFFS2_COMPR_NONE; /* We failed to compress */
+-
+ }
+
++void jffs2_free_comprbuf(unsigned char *comprbuf, unsigned char *orig)
++{
++ if (orig != comprbuf)
++ kfree(comprbuf);
++}
+
+ int jffs2_decompress(unsigned char comprtype, unsigned char *cdata_in,
+- unsigned char *data_out, __u32 cdatalen, __u32 datalen)
++ unsigned char *data_out, uint32_t cdatalen, uint32_t datalen)
+ {
+ switch (comprtype) {
+ case JFFS2_COMPR_NONE:
+@@ -119,30 +109,27 @@
+ case JFFS2_COMPR_ZERO:
+ memset(data_out, 0, datalen);
+ break;
+-
++#ifdef JFFS2_USE_ZLIB
+ case JFFS2_COMPR_ZLIB:
+- zlib_decompress(cdata_in, data_out, cdatalen, datalen);
++ jffs2_zlib_decompress(cdata_in, data_out, cdatalen, datalen);
+ break;
+-
++#endif
++#ifdef JFFS2_USE_RTIME
+ case JFFS2_COMPR_RTIME:
+- rtime_decompress(cdata_in, data_out, cdatalen, datalen);
++ jffs2_rtime_decompress(cdata_in, data_out, cdatalen, datalen);
+ break;
+-
+- case JFFS2_COMPR_RUBINMIPS:
+-#if 0 /* Disabled 23/9/1 */
+- rubinmips_decompress(cdata_in, data_out, cdatalen, datalen);
+-#else
+- printk(KERN_WARNING "JFFS2: Rubinmips compression encountered but support not compiled in!\n");
+ #endif
++#ifdef JFFS2_USE_RUBINMIPS
++ case JFFS2_COMPR_RUBINMIPS:
++ jffs2_rubinmips_decompress(cdata_in, data_out, cdatalen, datalen);
+ break;
+- case JFFS2_COMPR_DYNRUBIN:
+-#if 1 /* Phase this one out */
+- dynrubin_decompress(cdata_in, data_out, cdatalen, datalen);
+-#else
+- printk(KERN_WARNING "JFFS2: Dynrubin compression encountered but support not compiled in!\n");
+ #endif
+- break;
++#ifdef JFFS2_USE_DYNRUBIN
++ case JFFS2_COMPR_DYNRUBIN:
+
++ jffs2_dynrubin_decompress(cdata_in, data_out, cdatalen, datalen);
++ break;
++#endif
+ default:
+ printk(KERN_NOTICE "Unknown JFFS2 compression type 0x%02x\n", comprtype);
+ return -EIO;
+diff -Nurb linux-mips-2.4.24-pre2/fs/jffs2/compr_rtime.c linux/fs/jffs2/compr_rtime.c
+--- linux-mips-2.4.24-pre2/fs/jffs2/compr_rtime.c 2004-11-17 18:05:04.000000000 +0100
++++ linux/fs/jffs2/compr_rtime.c 2004-11-17 18:17:59.000000000 +0100
+@@ -1,43 +1,19 @@
+ /*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+- * Copyright (C) 2001 Red Hat, Inc.
++ * Copyright (C) 2001-2003 Red Hat, Inc.
+ *
+ * Created by Arjan van de Ven <arjanv@redhat.com>
+ *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
++ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+- * The contents of this file are subject to the Red Hat eCos Public
+- * License Version 1.1 (the "Licence"); you may not use this file
+- * except in compliance with the Licence. You may obtain a copy of
+- * the Licence at http://www.redhat.com/
+- *
+- * Software distributed under the Licence is distributed on an "AS IS"
+- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+- * See the Licence for the specific language governing rights and
+- * limitations under the Licence.
+- *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
+- *
+- * Alternatively, the contents of this file may be used under the
+- * terms of the GNU General Public License version 2 (the "GPL"), in
+- * which case the provisions of the GPL are applicable instead of the
+- * above. If you wish to allow the use of your version of this file
+- * only under the terms of the GPL and not to allow others to use your
+- * version of this file under the RHEPL, indicate your decision by
+- * deleting the provisions above and replace them with the notice and
+- * other provisions required by the GPL. If you do not delete the
+- * provisions above, a recipient may use your version of this file
+- * under either the RHEPL or the GPL.
+- *
+- * $Id$
++ * $Id$
+ *
+ *
+ * Very simple lz77-ish encoder.
+ *
+ * Theory of operation: Both encoder and decoder have a list of "last
+- * occurances" for every possible source-value; after sending the
++ * occurrences" for every possible source-value; after sending the
+ * first source-byte, the second byte indicated the "run" length of
+ * matches
+ *
+@@ -51,10 +27,10 @@
+ #include <linux/string.h>
+
+ /* _compress returns the compressed size, -1 if bigger */
+-int rtime_compress(unsigned char *data_in, unsigned char *cpage_out,
+- __u32 *sourcelen, __u32 *dstlen)
++int jffs2_rtime_compress(unsigned char *data_in, unsigned char *cpage_out,
++ uint32_t *sourcelen, uint32_t *dstlen)
+ {
+- int positions[256];
++ short positions[256];
+ int outpos = 0;
+ int pos=0;
+
+@@ -91,10 +67,10 @@
+ }
+
+
+-void rtime_decompress(unsigned char *data_in, unsigned char *cpage_out,
+- __u32 srclen, __u32 destlen)
++void jffs2_rtime_decompress(unsigned char *data_in, unsigned char *cpage_out,
++ uint32_t srclen, uint32_t destlen)
+ {
+- int positions[256];
++ short positions[256];
+ int outpos = 0;
+ int pos=0;
+
+diff -Nurb linux-mips-2.4.24-pre2/fs/jffs2/compr_rubin.c linux/fs/jffs2/compr_rubin.c
+--- linux-mips-2.4.24-pre2/fs/jffs2/compr_rubin.c 2004-11-17 18:05:04.000000000 +0100
++++ linux/fs/jffs2/compr_rubin.c 2004-11-17 18:17:59.000000000 +0100
+@@ -1,37 +1,13 @@
+ /*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+- * Copyright (C) 2001 Red Hat, Inc.
++ * Copyright (C) 2001, 2002 Red Hat, Inc.
+ *
+ * Created by Arjan van de Ven <arjanv@redhat.com>
+ *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
++ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+- * The contents of this file are subject to the Red Hat eCos Public
+- * License Version 1.1 (the "Licence"); you may not use this file
+- * except in compliance with the Licence. You may obtain a copy of
+- * the Licence at http://www.redhat.com/
+- *
+- * Software distributed under the Licence is distributed on an "AS IS"
+- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+- * See the Licence for the specific language governing rights and
+- * limitations under the Licence.
+- *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
+- *
+- * Alternatively, the contents of this file may be used under the
+- * terms of the GNU General Public License version 2 (the "GPL"), in
+- * which case the provisions of the GPL are applicable instead of the
+- * above. If you wish to allow the use of your version of this file
+- * only under the terms of the GPL and not to allow others to use your
+- * version of this file under the RHEPL, indicate your decision by
+- * deleting the provisions above and replace them with the notice and
+- * other provisions required by the GPL. If you do not delete the
+- * provisions above, a recipient may use your version of this file
+- * under either the RHEPL or the GPL.
+- *
+- * $Id$
++ * $Id$
+ *
+ */
+
+@@ -43,7 +19,7 @@
+
+
+
+-void init_rubin(struct rubin_state *rs, int div, int *bits)
++static void init_rubin(struct rubin_state *rs, int div, int *bits)
+ {
+ int c;
+
+@@ -56,7 +32,7 @@
+ }
+
+
+-int encode(struct rubin_state *rs, long A, long B, int symbol)
++static int encode(struct rubin_state *rs, long A, long B, int symbol)
+ {
+
+ long i0, i1;
+@@ -91,7 +67,7 @@
+ }
+
+
+-void end_rubin(struct rubin_state *rs)
++static void end_rubin(struct rubin_state *rs)
+ {
+
+ int i;
+@@ -104,7 +80,7 @@
+ }
+
+
+-void init_decode(struct rubin_state *rs, int div, int *bits)
++static void init_decode(struct rubin_state *rs, int div, int *bits)
+ {
+ init_rubin(rs, div, bits);
+
+@@ -151,7 +127,7 @@
+ rs->rec_q = rec_q;
+ }
+
+-int decode(struct rubin_state *rs, long A, long B)
++static int decode(struct rubin_state *rs, long A, long B)
+ {
+ unsigned long p = rs->p, q = rs->q;
+ long i0, threshold;
+@@ -212,8 +188,8 @@
+
+
+
+-int rubin_do_compress(int bit_divider, int *bits, unsigned char *data_in,
+- unsigned char *cpage_out, __u32 *sourcelen, __u32 *dstlen)
++static int rubin_do_compress(int bit_divider, int *bits, unsigned char *data_in,
++ unsigned char *cpage_out, uint32_t *sourcelen, uint32_t *dstlen)
+ {
+ int outpos = 0;
+ int pos=0;
+@@ -246,20 +222,20 @@
+ }
+ #if 0
+ /* _compress returns the compressed size, -1 if bigger */
+-int rubinmips_compress(unsigned char *data_in, unsigned char *cpage_out,
+- __u32 *sourcelen, __u32 *dstlen)
++int jffs2_rubinmips_compress(unsigned char *data_in, unsigned char *cpage_out,
++ uint32_t *sourcelen, uint32_t *dstlen)
+ {
+ return rubin_do_compress(BIT_DIVIDER_MIPS, bits_mips, data_in, cpage_out, sourcelen, dstlen);
+ }
+ #endif
+-int dynrubin_compress(unsigned char *data_in, unsigned char *cpage_out,
+- __u32 *sourcelen, __u32 *dstlen)
++int jffs2_dynrubin_compress(unsigned char *data_in, unsigned char *cpage_out,
++ uint32_t *sourcelen, uint32_t *dstlen)
+ {
+ int bits[8];
+ unsigned char histo[256];
+ int i;
+ int ret;
+- __u32 mysrclen, mydstlen;
++ uint32_t mysrclen, mydstlen;
+
+ mysrclen = *sourcelen;
+ mydstlen = *dstlen - 8;
+@@ -315,8 +291,8 @@
+ return 0;
+ }
+
+-void rubin_do_decompress(int bit_divider, int *bits, unsigned char *cdata_in,
+- unsigned char *page_out, __u32 srclen, __u32 destlen)
++static void rubin_do_decompress(int bit_divider, int *bits, unsigned char *cdata_in,
++ unsigned char *page_out, uint32_t srclen, uint32_t destlen)
+ {
+ int outpos = 0;
+ struct rubin_state rs;
+@@ -330,14 +306,14 @@
+ }
+
+
+-void rubinmips_decompress(unsigned char *data_in, unsigned char *cpage_out,
+- __u32 sourcelen, __u32 dstlen)
++void jffs2_rubinmips_decompress(unsigned char *data_in, unsigned char *cpage_out,
++ uint32_t sourcelen, uint32_t dstlen)
+ {
+ rubin_do_decompress(BIT_DIVIDER_MIPS, bits_mips, data_in, cpage_out, sourcelen, dstlen);
+ }
+
+-void dynrubin_decompress(unsigned char *data_in, unsigned char *cpage_out,
+- __u32 sourcelen, __u32 dstlen)
++void jffs2_dynrubin_decompress(unsigned char *data_in, unsigned char *cpage_out,
++ uint32_t sourcelen, uint32_t dstlen)
+ {
+ int bits[8];
+ int c;
+diff -Nurb linux-mips-2.4.24-pre2/fs/jffs2/compr_rubin.h linux/fs/jffs2/compr_rubin.h
+--- linux-mips-2.4.24-pre2/fs/jffs2/compr_rubin.h 2004-11-17 18:05:04.000000000 +0100
++++ linux/fs/jffs2/compr_rubin.h 2004-11-17 18:17:59.000000000 +0100
+@@ -1,7 +1,7 @@
+ /* Rubin encoder/decoder header */
+ /* work started at : aug 3, 1994 */
+ /* last modification : aug 15, 1994 */
+-/* $Id$ */
++/* $Id$ */
+
+ #include "pushpull.h"
+
+@@ -19,10 +19,3 @@
+ int bit_divider;
+ int bits[8];
+ };
+-
+-
+-void init_rubin (struct rubin_state *rs, int div, int *bits);
+-int encode (struct rubin_state *, long, long, int);
+-void end_rubin (struct rubin_state *);
+-void init_decode (struct rubin_state *, int div, int *bits);
+-int decode (struct rubin_state *, long, long);
+diff -Nurb linux-mips-2.4.24-pre2/fs/jffs2/compr_zlib.c linux/fs/jffs2/compr_zlib.c
+--- linux-mips-2.4.24-pre2/fs/jffs2/compr_zlib.c 2004-11-17 18:05:04.000000000 +0100
++++ linux/fs/jffs2/compr_zlib.c 2004-11-17 18:17:59.000000000 +0100
+@@ -1,50 +1,26 @@
+ /*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+- * Copyright (C) 2001, 2002 Red Hat, Inc.
++ * Copyright (C) 2001-2003 Red Hat, Inc.
+ *
+- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
++ * Created by David Woodhouse <dwmw2@redhat.com>
+ *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
++ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+- * The contents of this file are subject to the Red Hat eCos Public
+- * License Version 1.1 (the "Licence"); you may not use this file
+- * except in compliance with the Licence. You may obtain a copy of
+- * the Licence at http://www.redhat.com/
+- *
+- * Software distributed under the Licence is distributed on an "AS IS"
+- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+- * See the Licence for the specific language governing rights and
+- * limitations under the Licence.
+- *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
+- *
+- * Alternatively, the contents of this file may be used under the
+- * terms of the GNU General Public License version 2 (the "GPL"), in
+- * which case the provisions of the GPL are applicable instead of the
+- * above. If you wish to allow the use of your version of this file
+- * only under the terms of the GPL and not to allow others to use your
+- * version of this file under the RHEPL, indicate your decision by
+- * deleting the provisions above and replace them with the notice and
+- * other provisions required by the GPL. If you do not delete the
+- * provisions above, a recipient may use your version of this file
+- * under either the RHEPL or the GPL.
+- *
+- * $Id$
++ * $Id$
+ *
+ */
+
+-#ifndef __KERNEL__
++#if !defined(__KERNEL__) && !defined(__ECOS)
+ #error "The userspace support got too messy and was removed. Update your mkfs.jffs2"
+ #endif
+
+ #include <linux/config.h>
+ #include <linux/kernel.h>
+-#include <linux/mtd/compatmac.h> /* for min() */
+ #include <linux/slab.h>
+-#include <linux/jffs2.h>
+ #include <linux/zlib.h>
++#include <linux/zutil.h>
++#include <asm/semaphore.h>
+ #include "nodelist.h"
+
+ /* Plan: call deflate() with avail_in == *sourcelen,
+@@ -58,21 +34,24 @@
+
+ static DECLARE_MUTEX(deflate_sem);
+ static DECLARE_MUTEX(inflate_sem);
+-static void *deflate_workspace;
+-static void *inflate_workspace;
++static z_stream inf_strm, def_strm;
++
++#ifdef __KERNEL__ /* Linux-only */
++#include <linux/vmalloc.h>
++#include <linux/init.h>
+
+ int __init jffs2_zlib_init(void)
+ {
+- deflate_workspace = vmalloc(zlib_deflate_workspacesize());
+- if (!deflate_workspace) {
++ def_strm.workspace = vmalloc(zlib_deflate_workspacesize());
++ if (!def_strm.workspace) {
+ printk(KERN_WARNING "Failed to allocate %d bytes for deflate workspace\n", zlib_deflate_workspacesize());
+ return -ENOMEM;
+ }
+ D1(printk(KERN_DEBUG "Allocated %d bytes for deflate workspace\n", zlib_deflate_workspacesize()));
+- inflate_workspace = vmalloc(zlib_inflate_workspacesize());
+- if (!inflate_workspace) {
++ inf_strm.workspace = vmalloc(zlib_inflate_workspacesize());
++ if (!inf_strm.workspace) {
+ printk(KERN_WARNING "Failed to allocate %d bytes for inflate workspace\n", zlib_inflate_workspacesize());
+- vfree(deflate_workspace);
++ vfree(def_strm.workspace);
+ return -ENOMEM;
+ }
+ D1(printk(KERN_DEBUG "Allocated %d bytes for inflate workspace\n", zlib_inflate_workspacesize()));
+@@ -81,97 +60,120 @@
+
+ void jffs2_zlib_exit(void)
+ {
+- vfree(deflate_workspace);
+- vfree(inflate_workspace);
++ vfree(def_strm.workspace);
++ vfree(inf_strm.workspace);
+ }
++#endif /* __KERNEL__ */
+
+-int zlib_compress(unsigned char *data_in, unsigned char *cpage_out,
+- __u32 *sourcelen, __u32 *dstlen)
++int jffs2_zlib_compress(unsigned char *data_in, unsigned char *cpage_out,
++ uint32_t *sourcelen, uint32_t *dstlen)
+ {
+- z_stream strm;
+ int ret;
+
+ if (*dstlen <= STREAM_END_SPACE)
+ return -1;
+
+ down(&deflate_sem);
+- strm.workspace = deflate_workspace;
+
+- if (Z_OK != zlib_deflateInit(&strm, 3)) {
++ if (Z_OK != zlib_deflateInit(&def_strm, 3)) {
+ printk(KERN_WARNING "deflateInit failed\n");
+ up(&deflate_sem);
+ return -1;
+ }
+
+- strm.next_in = data_in;
+- strm.total_in = 0;
++ def_strm.next_in = data_in;
++ def_strm.total_in = 0;
+
+- strm.next_out = cpage_out;
+- strm.total_out = 0;
++ def_strm.next_out = cpage_out;
++ def_strm.total_out = 0;
+
+- while (strm.total_out < *dstlen - STREAM_END_SPACE && strm.total_in < *sourcelen) {
+- strm.avail_out = *dstlen - (strm.total_out + STREAM_END_SPACE);
+- strm.avail_in = min((unsigned)(*sourcelen-strm.total_in), strm.avail_out);
++ while (def_strm.total_out < *dstlen - STREAM_END_SPACE && def_strm.total_in < *sourcelen) {
++ def_strm.avail_out = *dstlen - (def_strm.total_out + STREAM_END_SPACE);
++ def_strm.avail_in = min((unsigned)(*sourcelen-def_strm.total_in), def_strm.avail_out);
+ D1(printk(KERN_DEBUG "calling deflate with avail_in %d, avail_out %d\n",
+- strm.avail_in, strm.avail_out));
+- ret = zlib_deflate(&strm, Z_PARTIAL_FLUSH);
++ def_strm.avail_in, def_strm.avail_out));
++ ret = zlib_deflate(&def_strm, Z_PARTIAL_FLUSH);
+ D1(printk(KERN_DEBUG "deflate returned with avail_in %d, avail_out %d, total_in %ld, total_out %ld\n",
+- strm.avail_in, strm.avail_out, strm.total_in, strm.total_out));
++ def_strm.avail_in, def_strm.avail_out, def_strm.total_in, def_strm.total_out));
+ if (ret != Z_OK) {
+ D1(printk(KERN_DEBUG "deflate in loop returned %d\n", ret));
+- zlib_deflateEnd(&strm);
++ zlib_deflateEnd(&def_strm);
+ up(&deflate_sem);
+ return -1;
+ }
+ }
+- strm.avail_out += STREAM_END_SPACE;
+- strm.avail_in = 0;
+- ret = zlib_deflate(&strm, Z_FINISH);
+- zlib_deflateEnd(&strm);
+- up(&deflate_sem);
++ def_strm.avail_out += STREAM_END_SPACE;
++ def_strm.avail_in = 0;
++ ret = zlib_deflate(&def_strm, Z_FINISH);
++ zlib_deflateEnd(&def_strm);
++
+ if (ret != Z_STREAM_END) {
+ D1(printk(KERN_DEBUG "final deflate returned %d\n", ret));
+- return -1;
++ ret = -1;
++ goto out;
+ }
+
+- D1(printk(KERN_DEBUG "zlib compressed %ld bytes into %ld\n",
+- strm.total_in, strm.total_out));
++ if (def_strm.total_out >= def_strm.total_in) {
++ D1(printk(KERN_DEBUG "zlib compressed %ld bytes into %ld; failing\n",
++ def_strm.total_in, def_strm.total_out));
++ ret = -1;
++ goto out;
++ }
+
+- if (strm.total_out >= strm.total_in)
+- return -1;
++ D1(printk(KERN_DEBUG "zlib compressed %ld bytes into %ld\n",
++ def_strm.total_in, def_strm.total_out));
+
+- *dstlen = strm.total_out;
+- *sourcelen = strm.total_in;
+- return 0;
++ *dstlen = def_strm.total_out;
++ *sourcelen = def_strm.total_in;
++ ret = 0;
++ out:
++ up(&deflate_sem);
++ return ret;
+ }
+
+-void zlib_decompress(unsigned char *data_in, unsigned char *cpage_out,
+- __u32 srclen, __u32 destlen)
++void jffs2_zlib_decompress(unsigned char *data_in, unsigned char *cpage_out,
++ uint32_t srclen, uint32_t destlen)
+ {
+- z_stream strm;
+ int ret;
++ int wbits = MAX_WBITS;
+
+ down(&inflate_sem);
+- strm.workspace = inflate_workspace;
+
+- if (Z_OK != zlib_inflateInit(&strm)) {
++ inf_strm.next_in = data_in;
++ inf_strm.avail_in = srclen;
++ inf_strm.total_in = 0;
++
++ inf_strm.next_out = cpage_out;
++ inf_strm.avail_out = destlen;
++ inf_strm.total_out = 0;
++
++ /* If it's deflate, and it's got no preset dictionary, then
++ we can tell zlib to skip the adler32 check. */
++ if (srclen > 2 && !(data_in[1] & PRESET_DICT) &&
++ ((data_in[0] & 0x0f) == Z_DEFLATED) &&
++ !(((data_in[0]<<8) + data_in[1]) % 31)) {
++
++ D2(printk(KERN_DEBUG "inflate skipping adler32\n"));
++ wbits = -((data_in[0] >> 4) + 8);
++ inf_strm.next_in += 2;
++ inf_strm.avail_in -= 2;
++ } else {
++ /* Let this remain D1 for now -- it should never happen */
++ D1(printk(KERN_DEBUG "inflate not skipping adler32\n"));
++ }
++
++
++ if (Z_OK != zlib_inflateInit2(&inf_strm, wbits)) {
+ printk(KERN_WARNING "inflateInit failed\n");
+ up(&inflate_sem);
+ return;
+ }
+- strm.next_in = data_in;
+- strm.avail_in = srclen;
+- strm.total_in = 0;
+-
+- strm.next_out = cpage_out;
+- strm.avail_out = destlen;
+- strm.total_out = 0;
+
+- while((ret = zlib_inflate(&strm, Z_FINISH)) == Z_OK)
++ while((ret = zlib_inflate(&inf_strm, Z_FINISH)) == Z_OK)
+ ;
+ if (ret != Z_STREAM_END) {
+ printk(KERN_NOTICE "inflate returned %d\n", ret);
+ }
+- zlib_inflateEnd(&strm);
++ zlib_inflateEnd(&inf_strm);
+ up(&inflate_sem);
+ }
+diff -Nurb linux-mips-2.4.24-pre2/fs/jffs2/comprtest.c linux/fs/jffs2/comprtest.c
+--- linux-mips-2.4.24-pre2/fs/jffs2/comprtest.c 2004-11-17 18:05:04.000000000 +0100
++++ linux/fs/jffs2/comprtest.c 2004-11-17 18:17:59.000000000 +0100
+@@ -1,4 +1,4 @@
+-/* $Id$ */
++/* $Id$ */
+
+ #include <linux/kernel.h>
+ #include <linux/string.h>
+@@ -266,13 +266,13 @@
+ static unsigned char decomprbuf[TESTDATA_LEN];
+
+ int jffs2_decompress(unsigned char comprtype, unsigned char *cdata_in,
+- unsigned char *data_out, __u32 cdatalen, __u32 datalen);
++ unsigned char *data_out, uint32_t cdatalen, uint32_t datalen);
+ unsigned char jffs2_compress(unsigned char *data_in, unsigned char *cpage_out,
+- __u32 *datalen, __u32 *cdatalen);
++ uint32_t *datalen, uint32_t *cdatalen);
+
+ int init_module(void ) {
+ unsigned char comprtype;
+- __u32 c, d;
++ uint32_t c, d;
+ int ret;
+
+ printk("Original data: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
+diff -Nurb linux-mips-2.4.24-pre2/fs/jffs2/crc32.c linux/fs/jffs2/crc32.c
+--- linux-mips-2.4.24-pre2/fs/jffs2/crc32.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/fs/jffs2/crc32.c 2004-11-17 18:17:59.283276832 +0100
+@@ -0,0 +1,97 @@
++/*
++ * COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or
++ * code or tables extracted from it, as desired without restriction.
++ *
++ * First, the polynomial itself and its table of feedback terms. The
++ * polynomial is
++ * X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0
++ *
++ * Note that we take it "backwards" and put the highest-order term in
++ * the lowest-order bit. The X^32 term is "implied"; the LSB is the
++ * X^31 term, etc. The X^0 term (usually shown as "+1") results in
++ * the MSB being 1
++ *
++ * Note that the usual hardware shift register implementation, which
++ * is what we're using (we're merely optimizing it by doing eight-bit
++ * chunks at a time) shifts bits into the lowest-order term. In our
++ * implementation, that means shifting towards the right. Why do we
++ * do it this way? Because the calculated CRC must be transmitted in
++ * order from highest-order term to lowest-order term. UARTs transmit
++ * characters in order from LSB to MSB. By storing the CRC this way
++ * we hand it to the UART in the order low-byte to high-byte; the UART
++ * sends each low-bit to hight-bit; and the result is transmission bit
++ * by bit from highest- to lowest-order term without requiring any bit
++ * shuffling on our part. Reception works similarly
++ *
++ * The feedback terms table consists of 256, 32-bit entries. Notes
++ *
++ * The table can be generated at runtime if desired; code to do so
++ * is shown later. It might not be obvious, but the feedback
++ * terms simply represent the results of eight shift/xor opera
++ * tions for all combinations of data and CRC register values
++ *
++ * The values must be right-shifted by eight bits by the "updcrc
++ * logic; the shift must be unsigned (bring in zeroes). On some
++ * hardware you could probably optimize the shift in assembler by
++ * using byte-swap instructions
++ * polynomial $edb88320
++ */
++
++/* $Id$ */
++
++#include "crc32.h"
++
++const uint32_t crc32_table[256] = {
++ 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L,
++ 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L,
++ 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
++ 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL,
++ 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L,
++ 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L,
++ 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L,
++ 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL,
++ 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L,
++ 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL,
++ 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L,
++ 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L,
++ 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L,
++ 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL,
++ 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL,
++ 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L,
++ 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL,
++ 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L,
++ 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L,
++ 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L,
++ 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL,
++ 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L,
++ 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L,
++ 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,
++ 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L,
++ 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L,
++ 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L,
++ 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L,
++ 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L,
++ 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL,
++ 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL,
++ 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L,
++ 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L,
++ 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL,
++ 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL,
++ 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L,
++ 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL,
++ 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L,
++ 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL,
++ 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L,
++ 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL,
++ 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L,
++ 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L,
++ 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL,
++ 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L,
++ 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L,
++ 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L,
++ 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,
++ 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L,
++ 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L,
++ 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL,
++ 0x2d02ef8dL
++};
+diff -Nurb linux-mips-2.4.24-pre2/fs/jffs2/crc32.h linux/fs/jffs2/crc32.h
+--- linux-mips-2.4.24-pre2/fs/jffs2/crc32.h 1970-01-01 01:00:00.000000000 +0100
++++ linux/fs/jffs2/crc32.h 2004-11-17 18:17:59.284276680 +0100
+@@ -0,0 +1,21 @@
++#ifndef CRC32_H
++#define CRC32_H
++
++/* $Id$ */
++
++#include <linux/types.h>
++
++extern const uint32_t crc32_table[256];
++
++/* Return a 32-bit CRC of the contents of the buffer. */
++
++static inline uint32_t
++crc32(uint32_t val, const void *ss, int len)
++{
++ const unsigned char *s = ss;
++ while (--len >= 0)
++ val = crc32_table[(val ^ *s++) & 0xff] ^ (val >> 8);
++ return val;
++}
++
++#endif
+diff -Nurb linux-mips-2.4.24-pre2/fs/jffs2/dir.c linux/fs/jffs2/dir.c
+--- linux-mips-2.4.24-pre2/fs/jffs2/dir.c 2004-11-17 18:05:04.000000000 +0100
++++ linux/fs/jffs2/dir.c 2004-11-17 18:17:59.000000000 +0100
+@@ -1,84 +1,73 @@
+ /*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+- * Copyright (C) 2001 Red Hat, Inc.
++ * Copyright (C) 2001-2003 Red Hat, Inc.
+ *
+- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
++ * Created by David Woodhouse <dwmw2@redhat.com>
+ *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
++ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+- * The contents of this file are subject to the Red Hat eCos Public
+- * License Version 1.1 (the "Licence"); you may not use this file
+- * except in compliance with the Licence. You may obtain a copy of
+- * the Licence at http://www.redhat.com/
+- *
+- * Software distributed under the Licence is distributed on an "AS IS"
+- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+- * See the Licence for the specific language governing rights and
+- * limitations under the Licence.
+- *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
+- *
+- * Alternatively, the contents of this file may be used under the
+- * terms of the GNU General Public License version 2 (the "GPL"), in
+- * which case the provisions of the GPL are applicable instead of the
+- * above. If you wish to allow the use of your version of this file
+- * only under the terms of the GPL and not to allow others to use your
+- * version of this file under the RHEPL, indicate your decision by
+- * deleting the provisions above and replace them with the notice and
+- * other provisions required by the GPL. If you do not delete the
+- * provisions above, a recipient may use your version of this file
+- * under either the RHEPL or the GPL.
+- *
+- * $Id$
++ * $Id$
+ *
+ */
+
+ #include <linux/kernel.h>
+ #include <linux/slab.h>
++#include <linux/sched.h>
+ #include <linux/fs.h>
+-#include <linux/mtd/compatmac.h> /* For completion */
++#include <linux/crc32.h>
+ #include <linux/jffs2.h>
+ #include <linux/jffs2_fs_i.h>
+ #include <linux/jffs2_fs_sb.h>
++#include <linux/time.h>
+ #include "nodelist.h"
+-#include <linux/crc32.h>
++
++/* Urgh. Please tell me there's a nicer way of doing these. */
++#include <linux/version.h>
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,48)
++typedef int mknod_arg_t;
++#define NAMEI_COMPAT(x) ((void *)x)
++#else
++typedef dev_t mknod_arg_t;
++#define NAMEI_COMPAT(x) (x)
++#endif
+
+ static int jffs2_readdir (struct file *, void *, filldir_t);
+
+-static int jffs2_create (struct inode *,struct dentry *,int);
+-static struct dentry *jffs2_lookup (struct inode *,struct dentry *);
++static int jffs2_create (struct inode *,struct dentry *,int,
++ struct nameidata *);
++static struct dentry *jffs2_lookup (struct inode *,struct dentry *,
++ struct nameidata *);
+ static int jffs2_link (struct dentry *,struct inode *,struct dentry *);
+ static int jffs2_unlink (struct inode *,struct dentry *);
+ static int jffs2_symlink (struct inode *,struct dentry *,const char *);
+ static int jffs2_mkdir (struct inode *,struct dentry *,int);
+ static int jffs2_rmdir (struct inode *,struct dentry *);
+-static int jffs2_mknod (struct inode *,struct dentry *,int,int);
++static int jffs2_mknod (struct inode *,struct dentry *,int,mknod_arg_t);
+ static int jffs2_rename (struct inode *, struct dentry *,
+ struct inode *, struct dentry *);
+
+ struct file_operations jffs2_dir_operations =
+ {
+- read: generic_read_dir,
+- readdir: jffs2_readdir,
+- ioctl: jffs2_ioctl,
+- fsync: jffs2_null_fsync
++ .read = generic_read_dir,
++ .readdir = jffs2_readdir,
++ .ioctl = jffs2_ioctl,
++ .fsync = jffs2_fsync
+ };
+
+
+ struct inode_operations jffs2_dir_inode_operations =
+ {
+- create: jffs2_create,
+- lookup: jffs2_lookup,
+- link: jffs2_link,
+- unlink: jffs2_unlink,
+- symlink: jffs2_symlink,
+- mkdir: jffs2_mkdir,
+- rmdir: jffs2_rmdir,
+- mknod: jffs2_mknod,
+- rename: jffs2_rename,
+- setattr: jffs2_setattr,
++ .create = NAMEI_COMPAT(jffs2_create),
++ .lookup = NAMEI_COMPAT(jffs2_lookup),
++ .link = jffs2_link,
++ .unlink = jffs2_unlink,
++ .symlink = jffs2_symlink,
++ .mkdir = jffs2_mkdir,
++ .rmdir = jffs2_rmdir,
++ .mknod = jffs2_mknod,
++ .rename = jffs2_rename,
++ .setattr = jffs2_setattr,
+ };
+
+ /***********************************************************************/
+@@ -88,12 +77,13 @@
+ and we use the same hash function as the dentries. Makes this
+ nice and simple
+ */
+-static struct dentry *jffs2_lookup(struct inode *dir_i, struct dentry *target)
++static struct dentry *jffs2_lookup(struct inode *dir_i, struct dentry *target,
++ struct nameidata *nd)
+ {
+ struct jffs2_inode_info *dir_f;
+ struct jffs2_sb_info *c;
+ struct jffs2_full_dirent *fd = NULL, *fd_list;
+- __u32 ino = 0;
++ uint32_t ino = 0;
+ struct inode *inode = NULL;
+
+ D1(printk(KERN_DEBUG "jffs2_lookup()\n"));
+@@ -153,8 +143,9 @@
+ offset++;
+ }
+ if (offset == 1) {
+- D1(printk(KERN_DEBUG "Dirent 1: \"..\", ino #%lu\n", filp->f_dentry->d_parent->d_inode->i_ino));
+- if (filldir(dirent, "..", 2, 1, filp->f_dentry->d_parent->d_inode->i_ino, DT_DIR) < 0)
++ unsigned long pino = parent_ino(filp->f_dentry);
++ D1(printk(KERN_DEBUG "Dirent 1: \"..\", ino #%lu\n", pino));
++ if (filldir(dirent, "..", 2, 1, pino, DT_DIR) < 0)
+ goto out;
+ offset++;
+ }
+@@ -188,18 +179,14 @@
+
+ /***********************************************************************/
+
+-static int jffs2_create(struct inode *dir_i, struct dentry *dentry, int mode)
++
++static int jffs2_create(struct inode *dir_i, struct dentry *dentry, int mode,
++ struct nameidata *nd)
+ {
++ struct jffs2_raw_inode *ri;
+ struct jffs2_inode_info *f, *dir_f;
+ struct jffs2_sb_info *c;
+ struct inode *inode;
+- struct jffs2_raw_inode *ri;
+- struct jffs2_raw_dirent *rd;
+- struct jffs2_full_dnode *fn;
+- struct jffs2_full_dirent *fd;
+- int namelen;
+- __u32 alloclen, phys_ofs;
+- __u32 writtenlen;
+ int ret;
+
+ ri = jffs2_alloc_raw_inode();
+@@ -210,23 +197,11 @@
+
+ D1(printk(KERN_DEBUG "jffs2_create()\n"));
+
+- /* Try to reserve enough space for both node and dirent.
+- * Just the node will do for now, though
+- */
+- namelen = dentry->d_name.len;
+- ret = jffs2_reserve_space(c, sizeof(*ri), &phys_ofs, &alloclen, ALLOC_NORMAL);
+- D1(printk(KERN_DEBUG "jffs2_create(): reserved 0x%x bytes\n", alloclen));
+- if (ret) {
+- jffs2_free_raw_inode(ri);
+- return ret;
+- }
+-
+ inode = jffs2_new_inode(dir_i, mode, ri);
+
+ if (IS_ERR(inode)) {
+ D1(printk(KERN_DEBUG "jffs2_new_inode() failed\n"));
+ jffs2_free_raw_inode(ri);
+- jffs2_complete_reservation(c);
+ return PTR_ERR(inode);
+ }
+
+@@ -236,93 +211,22 @@
+ inode->i_mapping->nrpages = 0;
+
+ f = JFFS2_INODE_INFO(inode);
++ dir_f = JFFS2_INODE_INFO(dir_i);
+
+- ri->data_crc = 0;
+- ri->node_crc = crc32(0, ri, sizeof(*ri)-8);
+-
+- fn = jffs2_write_dnode(inode, ri, NULL, 0, phys_ofs, &writtenlen);
+- D1(printk(KERN_DEBUG "jffs2_create created file with mode 0x%x\n", ri->mode));
+- jffs2_free_raw_inode(ri);
+-
+- if (IS_ERR(fn)) {
+- D1(printk(KERN_DEBUG "jffs2_write_dnode() failed\n"));
+- /* Eeek. Wave bye bye */
+- up(&f->sem);
+- jffs2_complete_reservation(c);
+- jffs2_clear_inode(inode);
+- return PTR_ERR(fn);
+- }
+- /* No data here. Only a metadata node, which will be
+- obsoleted by the first data write
+- */
+- f->metadata = fn;
+-
+- /* Work out where to put the dirent node now. */
+- writtenlen = PAD(writtenlen);
+- phys_ofs += writtenlen;
+- alloclen -= writtenlen;
+- up(&f->sem);
+-
+- if (alloclen < sizeof(*rd)+namelen) {
+- /* Not enough space left in this chunk. Get some more */
+- jffs2_complete_reservation(c);
+- ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL);
++ ret = jffs2_do_create(c, dir_f, f, ri,
++ dentry->d_name.name, dentry->d_name.len);
+
+ if (ret) {
+- /* Eep. */
+- D1(printk(KERN_DEBUG "jffs2_reserve_space() for dirent failed\n"));
+ jffs2_clear_inode(inode);
++ make_bad_inode(inode);
++ iput(inode);
++ jffs2_free_raw_inode(ri);
+ return ret;
+ }
+- }
+
+- rd = jffs2_alloc_raw_dirent();
+- if (!rd) {
+- /* Argh. Now we treat it like a normal delete */
+- jffs2_complete_reservation(c);
+- jffs2_clear_inode(inode);
+- return -ENOMEM;
+- }
+-
+- dir_f = JFFS2_INODE_INFO(dir_i);
+- down(&dir_f->sem);
+-
+- rd->magic = JFFS2_MAGIC_BITMASK;
+- rd->nodetype = JFFS2_NODETYPE_DIRENT;
+- rd->totlen = sizeof(*rd) + namelen;
+- rd->hdr_crc = crc32(0, rd, sizeof(struct jffs2_unknown_node)-4);
+-
+- rd->pino = dir_i->i_ino;
+- rd->version = ++dir_f->highest_version;
+- rd->ino = inode->i_ino;
+- rd->mctime = CURRENT_TIME;
+- rd->nsize = namelen;
+- rd->type = DT_REG;
+- rd->node_crc = crc32(0, rd, sizeof(*rd)-8);
+- rd->name_crc = crc32(0, dentry->d_name.name, namelen);
+-
+- fd = jffs2_write_dirent(dir_i, rd, dentry->d_name.name, namelen, phys_ofs, &writtenlen);
+-
+- jffs2_complete_reservation(c);
+-
+- if (IS_ERR(fd)) {
+- /* dirent failed to write. Delete the inode normally
+- as if it were the final unlink() */
+- jffs2_free_raw_dirent(rd);
+- up(&dir_f->sem);
+- jffs2_clear_inode(inode);
+- return PTR_ERR(fd);
+- }
+-
+- dir_i->i_mtime = dir_i->i_ctime = rd->mctime;
+-
+- jffs2_free_raw_dirent(rd);
+-
+- /* Link the fd into the inode's list, obsoleting an old
+- one if necessary. */
+- jffs2_add_fd_to_list(c, fd, &dir_f->dents);
+- up(&dir_f->sem);
++ dir_i->i_mtime = dir_i->i_ctime = ITIME(je32_to_cpu(ri->ctime));
+
++ jffs2_free_raw_inode(ri);
+ d_instantiate(dentry, inode);
+
+ D1(printk(KERN_DEBUG "jffs2_create: Created ino #%lu with mode %o, nlink %d(%d). nrpages %ld\n",
+@@ -332,173 +236,48 @@
+
+ /***********************************************************************/
+
+-static int jffs2_do_unlink(struct inode *dir_i, struct dentry *dentry, int rename)
+-{
+- struct jffs2_inode_info *dir_f, *f;
+- struct jffs2_sb_info *c;
+- struct jffs2_raw_dirent *rd;
+- struct jffs2_full_dirent *fd;
+- __u32 alloclen, phys_ofs;
+- int ret;
+-
+- c = JFFS2_SB_INFO(dir_i->i_sb);
+-
+- rd = jffs2_alloc_raw_dirent();
+- if (!rd)
+- return -ENOMEM;
+-
+- ret = jffs2_reserve_space(c, sizeof(*rd)+dentry->d_name.len, &phys_ofs, &alloclen, ALLOC_DELETION);
+- if (ret) {
+- jffs2_free_raw_dirent(rd);
+- return ret;
+- }
+-
+- dir_f = JFFS2_INODE_INFO(dir_i);
+- down(&dir_f->sem);
+-
+- /* Build a deletion node */
+- rd->magic = JFFS2_MAGIC_BITMASK;
+- rd->nodetype = JFFS2_NODETYPE_DIRENT;
+- rd->totlen = sizeof(*rd) + dentry->d_name.len;
+- rd->hdr_crc = crc32(0, rd, sizeof(struct jffs2_unknown_node)-4);
+-
+- rd->pino = dir_i->i_ino;
+- rd->version = ++dir_f->highest_version;
+- rd->ino = 0;
+- rd->mctime = CURRENT_TIME;
+- rd->nsize = dentry->d_name.len;
+- rd->type = DT_UNKNOWN;
+- rd->node_crc = crc32(0, rd, sizeof(*rd)-8);
+- rd->name_crc = crc32(0, dentry->d_name.name, dentry->d_name.len);
+-
+- fd = jffs2_write_dirent(dir_i, rd, dentry->d_name.name, dentry->d_name.len, phys_ofs, NULL);
+-
+- jffs2_complete_reservation(c);
+- jffs2_free_raw_dirent(rd);
+-
+- if (IS_ERR(fd)) {
+- up(&dir_f->sem);
+- return PTR_ERR(fd);
+- }
+-
+- /* File it. This will mark the old one obsolete. */
+- jffs2_add_fd_to_list(c, fd, &dir_f->dents);
+- up(&dir_f->sem);
+-
+- if (!rename) {
+- f = JFFS2_INODE_INFO(dentry->d_inode);
+- down(&f->sem);
+-
+- while (f->dents) {
+- /* There can be only deleted ones */
+- fd = f->dents;
+-
+- f->dents = fd->next;
+-
+- if (fd->ino) {
+- printk(KERN_WARNING "Deleting inode #%u with active dentry \"%s\"->ino #%u\n",
+- f->inocache->ino, fd->name, fd->ino);
+- } else {
+- D1(printk(KERN_DEBUG "Removing deletion dirent for \"%s\" from dir ino #%u\n", fd->name, f->inocache->ino));
+- }
+- jffs2_mark_node_obsolete(c, fd->raw);
+- jffs2_free_full_dirent(fd);
+- }
+- /* Don't oops on unlinking a bad inode */
+- if (f->inocache)
+- f->inocache->nlink--;
+- dentry->d_inode->i_nlink--;
+- up(&f->sem);
+- }
+-
+- return 0;
+-}
+
+ static int jffs2_unlink(struct inode *dir_i, struct dentry *dentry)
+ {
+- return jffs2_do_unlink(dir_i, dentry, 0);
+-}
+-/***********************************************************************/
+-
+-static int jffs2_do_link (struct dentry *old_dentry, struct inode *dir_i, struct dentry *dentry, int rename)
+-{
+- struct jffs2_inode_info *dir_f, *f;
+- struct jffs2_sb_info *c;
+- struct jffs2_raw_dirent *rd;
+- struct jffs2_full_dirent *fd;
+- __u32 alloclen, phys_ofs;
++ struct jffs2_sb_info *c = JFFS2_SB_INFO(dir_i->i_sb);
++ struct jffs2_inode_info *dir_f = JFFS2_INODE_INFO(dir_i);
++ struct jffs2_inode_info *dead_f = JFFS2_INODE_INFO(dentry->d_inode);
+ int ret;
+
+- c = JFFS2_SB_INFO(dir_i->i_sb);
+-
+- rd = jffs2_alloc_raw_dirent();
+- if (!rd)
+- return -ENOMEM;
+-
+- ret = jffs2_reserve_space(c, sizeof(*rd)+dentry->d_name.len, &phys_ofs, &alloclen, ALLOC_NORMAL);
+- if (ret) {
+- jffs2_free_raw_dirent(rd);
++ ret = jffs2_do_unlink(c, dir_f, dentry->d_name.name,
++ dentry->d_name.len, dead_f);
++ if (dead_f->inocache)
++ dentry->d_inode->i_nlink = dead_f->inocache->nlink;
+ return ret;
+- }
+-
+- dir_f = JFFS2_INODE_INFO(dir_i);
+- down(&dir_f->sem);
+-
+- /* Build a deletion node */
+- rd->magic = JFFS2_MAGIC_BITMASK;
+- rd->nodetype = JFFS2_NODETYPE_DIRENT;
+- rd->totlen = sizeof(*rd) + dentry->d_name.len;
+- rd->hdr_crc = crc32(0, rd, sizeof(struct jffs2_unknown_node)-4);
+-
+- rd->pino = dir_i->i_ino;
+- rd->version = ++dir_f->highest_version;
+- rd->ino = old_dentry->d_inode->i_ino;
+- rd->mctime = CURRENT_TIME;
+- rd->nsize = dentry->d_name.len;
+-
+- /* XXX: This is ugly. */
+- rd->type = (old_dentry->d_inode->i_mode & S_IFMT) >> 12;
+- if (!rd->type) rd->type = DT_REG;
+-
+- rd->node_crc = crc32(0, rd, sizeof(*rd)-8);
+- rd->name_crc = crc32(0, dentry->d_name.name, dentry->d_name.len);
+-
+- fd = jffs2_write_dirent(dir_i, rd, dentry->d_name.name, dentry->d_name.len, phys_ofs, NULL);
+-
+- jffs2_complete_reservation(c);
+- jffs2_free_raw_dirent(rd);
+-
+- if (IS_ERR(fd)) {
+- up(&dir_f->sem);
+- return PTR_ERR(fd);
+- }
+-
+- /* File it. This will mark the old one obsolete. */
+- jffs2_add_fd_to_list(c, fd, &dir_f->dents);
+- up(&dir_f->sem);
+-
+- if (!rename) {
+- f = JFFS2_INODE_INFO(old_dentry->d_inode);
+- down(&f->sem);
+- old_dentry->d_inode->i_nlink = ++f->inocache->nlink;
+- up(&f->sem);
+- }
+- return 0;
+ }
++/***********************************************************************/
++
+
+ static int jffs2_link (struct dentry *old_dentry, struct inode *dir_i, struct dentry *dentry)
+ {
++ struct jffs2_sb_info *c = JFFS2_SB_INFO(old_dentry->d_inode->i_sb);
++ struct jffs2_inode_info *f = JFFS2_INODE_INFO(old_dentry->d_inode);
++ struct jffs2_inode_info *dir_f = JFFS2_INODE_INFO(dir_i);
+ int ret;
++ uint8_t type;
+
+- /* Can't link a bad inode. */
+- if (!JFFS2_INODE_INFO(old_dentry->d_inode)->inocache)
++ /* Don't let people make hard links to bad inodes. */
++ if (!f->inocache)
+ return -EIO;
+
+ if (S_ISDIR(old_dentry->d_inode->i_mode))
+ return -EPERM;
+
+- ret = jffs2_do_link(old_dentry, dir_i, dentry, 0);
++ /* XXX: This is ugly */
++ type = (old_dentry->d_inode->i_mode & S_IFMT) >> 12;
++ if (!type) type = DT_REG;
++
++ ret = jffs2_do_link(c, dir_f, f->inocache->ino, type, dentry->d_name.name, dentry->d_name.len);
++
+ if (!ret) {
++ down(&f->sem);
++ old_dentry->d_inode->i_nlink = ++f->inocache->nlink;
++ up(&f->sem);
+ d_instantiate(dentry, old_dentry->d_inode);
+ atomic_inc(&old_dentry->d_inode->i_count);
+ }
+@@ -517,8 +296,7 @@
+ struct jffs2_full_dnode *fn;
+ struct jffs2_full_dirent *fd;
+ int namelen;
+- __u32 alloclen, phys_ofs;
+- __u32 writtenlen;
++ uint32_t alloclen, phys_ofs;
+ int ret;
+
+ /* FIXME: If you care. We'd need to use frags for the target
+@@ -556,15 +334,16 @@
+
+ f = JFFS2_INODE_INFO(inode);
+
+- inode->i_size = ri->isize = ri->dsize = ri->csize = strlen(target);
+- ri->totlen = sizeof(*ri) + ri->dsize;
+- ri->hdr_crc = crc32(0, ri, sizeof(struct jffs2_unknown_node)-4);
++ inode->i_size = strlen(target);
++ ri->isize = ri->dsize = ri->csize = cpu_to_je32(inode->i_size);
++ ri->totlen = cpu_to_je32(sizeof(*ri) + inode->i_size);
++ ri->hdr_crc = cpu_to_je32(crc32(0, ri, sizeof(struct jffs2_unknown_node)-4));
+
+ ri->compr = JFFS2_COMPR_NONE;
+- ri->data_crc = crc32(0, target, strlen(target));
+- ri->node_crc = crc32(0, ri, sizeof(*ri)-8);
++ ri->data_crc = cpu_to_je32(crc32(0, target, strlen(target)));
++ ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
+
+- fn = jffs2_write_dnode(inode, ri, target, strlen(target), phys_ofs, &writtenlen);
++ fn = jffs2_write_dnode(c, f, ri, target, strlen(target), phys_ofs, ALLOC_NORMAL);
+
+ jffs2_free_raw_inode(ri);
+
+@@ -581,13 +360,6 @@
+ f->metadata = fn;
+ up(&f->sem);
+
+- /* Work out where to put the dirent node now. */
+- writtenlen = (writtenlen+3)&~3;
+- phys_ofs += writtenlen;
+- alloclen -= writtenlen;
+-
+- if (alloclen < sizeof(*rd)+namelen) {
+- /* Not enough space left in this chunk. Get some more */
+ jffs2_complete_reservation(c);
+ ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL);
+ if (ret) {
+@@ -595,7 +367,6 @@
+ jffs2_clear_inode(inode);
+ return ret;
+ }
+- }
+
+ rd = jffs2_alloc_raw_dirent();
+ if (!rd) {
+@@ -608,41 +379,42 @@
+ dir_f = JFFS2_INODE_INFO(dir_i);
+ down(&dir_f->sem);
+
+- rd->magic = JFFS2_MAGIC_BITMASK;
+- rd->nodetype = JFFS2_NODETYPE_DIRENT;
+- rd->totlen = sizeof(*rd) + namelen;
+- rd->hdr_crc = crc32(0, rd, sizeof(struct jffs2_unknown_node)-4);
+-
+- rd->pino = dir_i->i_ino;
+- rd->version = ++dir_f->highest_version;
+- rd->ino = inode->i_ino;
+- rd->mctime = CURRENT_TIME;
++ rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
++ rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT);
++ rd->totlen = cpu_to_je32(sizeof(*rd) + namelen);
++ rd->hdr_crc = cpu_to_je32(crc32(0, rd, sizeof(struct jffs2_unknown_node)-4));
++
++ rd->pino = cpu_to_je32(dir_i->i_ino);
++ rd->version = cpu_to_je32(++dir_f->highest_version);
++ rd->ino = cpu_to_je32(inode->i_ino);
++ rd->mctime = cpu_to_je32(get_seconds());
+ rd->nsize = namelen;
+ rd->type = DT_LNK;
+- rd->node_crc = crc32(0, rd, sizeof(*rd)-8);
+- rd->name_crc = crc32(0, dentry->d_name.name, namelen);
++ rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8));
++ rd->name_crc = cpu_to_je32(crc32(0, dentry->d_name.name, namelen));
+
+- fd = jffs2_write_dirent(dir_i, rd, dentry->d_name.name, namelen, phys_ofs, &writtenlen);
+-
+- jffs2_complete_reservation(c);
++ fd = jffs2_write_dirent(c, dir_f, rd, dentry->d_name.name, namelen, phys_ofs, ALLOC_NORMAL);
+
+ if (IS_ERR(fd)) {
+ /* dirent failed to write. Delete the inode normally
+ as if it were the final unlink() */
++ jffs2_complete_reservation(c);
+ jffs2_free_raw_dirent(rd);
+ up(&dir_f->sem);
+ jffs2_clear_inode(inode);
+ return PTR_ERR(fd);
+ }
+
+- dir_i->i_mtime = dir_i->i_ctime = rd->mctime;
++ dir_i->i_mtime = dir_i->i_ctime = ITIME(je32_to_cpu(rd->mctime));
+
+ jffs2_free_raw_dirent(rd);
+
+ /* Link the fd into the inode's list, obsoleting an old
+ one if necessary. */
+ jffs2_add_fd_to_list(c, fd, &dir_f->dents);
++
+ up(&dir_f->sem);
++ jffs2_complete_reservation(c);
+
+ d_instantiate(dentry, inode);
+ return 0;
+@@ -659,8 +431,7 @@
+ struct jffs2_full_dnode *fn;
+ struct jffs2_full_dirent *fd;
+ int namelen;
+- __u32 alloclen, phys_ofs;
+- __u32 writtenlen;
++ uint32_t alloclen, phys_ofs;
+ int ret;
+
+ mode |= S_IFDIR;
+@@ -692,13 +463,15 @@
+
+ inode->i_op = &jffs2_dir_inode_operations;
+ inode->i_fop = &jffs2_dir_operations;
++ /* Directories get nlink 2 at start */
++ inode->i_nlink = 2;
+
+ f = JFFS2_INODE_INFO(inode);
+
+- ri->data_crc = 0;
+- ri->node_crc = crc32(0, ri, sizeof(*ri)-8);
++ ri->data_crc = cpu_to_je32(0);
++ ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
+
+- fn = jffs2_write_dnode(inode, ri, NULL, 0, phys_ofs, &writtenlen);
++ fn = jffs2_write_dnode(c, f, ri, NULL, 0, phys_ofs, ALLOC_NORMAL);
+
+ jffs2_free_raw_inode(ri);
+
+@@ -715,13 +488,6 @@
+ f->metadata = fn;
+ up(&f->sem);
+
+- /* Work out where to put the dirent node now. */
+- writtenlen = PAD(writtenlen);
+- phys_ofs += writtenlen;
+- alloclen -= writtenlen;
+-
+- if (alloclen < sizeof(*rd)+namelen) {
+- /* Not enough space left in this chunk. Get some more */
+ jffs2_complete_reservation(c);
+ ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL);
+ if (ret) {
+@@ -729,7 +495,6 @@
+ jffs2_clear_inode(inode);
+ return ret;
+ }
+- }
+
+ rd = jffs2_alloc_raw_dirent();
+ if (!rd) {
+@@ -742,41 +507,43 @@
+ dir_f = JFFS2_INODE_INFO(dir_i);
+ down(&dir_f->sem);
+
+- rd->magic = JFFS2_MAGIC_BITMASK;
+- rd->nodetype = JFFS2_NODETYPE_DIRENT;
+- rd->totlen = sizeof(*rd) + namelen;
+- rd->hdr_crc = crc32(0, rd, sizeof(struct jffs2_unknown_node)-4);
+-
+- rd->pino = dir_i->i_ino;
+- rd->version = ++dir_f->highest_version;
+- rd->ino = inode->i_ino;
+- rd->mctime = CURRENT_TIME;
++ rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
++ rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT);
++ rd->totlen = cpu_to_je32(sizeof(*rd) + namelen);
++ rd->hdr_crc = cpu_to_je32(crc32(0, rd, sizeof(struct jffs2_unknown_node)-4));
++
++ rd->pino = cpu_to_je32(dir_i->i_ino);
++ rd->version = cpu_to_je32(++dir_f->highest_version);
++ rd->ino = cpu_to_je32(inode->i_ino);
++ rd->mctime = cpu_to_je32(get_seconds());
+ rd->nsize = namelen;
+ rd->type = DT_DIR;
+- rd->node_crc = crc32(0, rd, sizeof(*rd)-8);
+- rd->name_crc = crc32(0, dentry->d_name.name, namelen);
++ rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8));
++ rd->name_crc = cpu_to_je32(crc32(0, dentry->d_name.name, namelen));
+
+- fd = jffs2_write_dirent(dir_i, rd, dentry->d_name.name, namelen, phys_ofs, &writtenlen);
+-
+- jffs2_complete_reservation(c);
++ fd = jffs2_write_dirent(c, dir_f, rd, dentry->d_name.name, namelen, phys_ofs, ALLOC_NORMAL);
+
+ if (IS_ERR(fd)) {
+ /* dirent failed to write. Delete the inode normally
+ as if it were the final unlink() */
++ jffs2_complete_reservation(c);
+ jffs2_free_raw_dirent(rd);
+ up(&dir_f->sem);
+ jffs2_clear_inode(inode);
+ return PTR_ERR(fd);
+ }
+
+- dir_i->i_mtime = dir_i->i_ctime = rd->mctime;
++ dir_i->i_mtime = dir_i->i_ctime = ITIME(je32_to_cpu(rd->mctime));
++ dir_i->i_nlink++;
+
+ jffs2_free_raw_dirent(rd);
+
+ /* Link the fd into the inode's list, obsoleting an old
+ one if necessary. */
+ jffs2_add_fd_to_list(c, fd, &dir_f->dents);
++
+ up(&dir_f->sem);
++ jffs2_complete_reservation(c);
+
+ d_instantiate(dentry, inode);
+ return 0;
+@@ -786,15 +553,19 @@
+ {
+ struct jffs2_inode_info *f = JFFS2_INODE_INFO(dentry->d_inode);
+ struct jffs2_full_dirent *fd;
++ int ret;
+
+ for (fd = f->dents ; fd; fd = fd->next) {
+ if (fd->ino)
+ return -ENOTEMPTY;
+ }
+- return jffs2_unlink(dir_i, dentry);
++ ret = jffs2_unlink(dir_i, dentry);
++ if (!ret)
++ dir_i->i_nlink--;
++ return ret;
+ }
+
+-static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, int rdev)
++static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, mknod_arg_t rdev)
+ {
+ struct jffs2_inode_info *f, *dir_f;
+ struct jffs2_sb_info *c;
+@@ -804,12 +575,14 @@
+ struct jffs2_full_dnode *fn;
+ struct jffs2_full_dirent *fd;
+ int namelen;
+- unsigned short dev;
++ jint16_t dev;
+ int devlen = 0;
+- __u32 alloclen, phys_ofs;
+- __u32 writtenlen;
++ uint32_t alloclen, phys_ofs;
+ int ret;
+
++ if (!old_valid_dev(rdev))
++ return -EINVAL;
++
+ ri = jffs2_alloc_raw_inode();
+ if (!ri)
+ return -ENOMEM;
+@@ -817,7 +590,7 @@
+ c = JFFS2_SB_INFO(dir_i->i_sb);
+
+ if (S_ISBLK(mode) || S_ISCHR(mode)) {
+- dev = (MAJOR(to_kdev_t(rdev)) << 8) | MINOR(to_kdev_t(rdev));
++ dev = cpu_to_je16(old_encode_dev(rdev));
+ devlen = sizeof(dev);
+ }
+
+@@ -844,15 +617,15 @@
+
+ f = JFFS2_INODE_INFO(inode);
+
+- ri->dsize = ri->csize = devlen;
+- ri->totlen = sizeof(*ri) + ri->csize;
+- ri->hdr_crc = crc32(0, ri, sizeof(struct jffs2_unknown_node)-4);
++ ri->dsize = ri->csize = cpu_to_je32(devlen);
++ ri->totlen = cpu_to_je32(sizeof(*ri) + devlen);
++ ri->hdr_crc = cpu_to_je32(crc32(0, ri, sizeof(struct jffs2_unknown_node)-4));
+
+ ri->compr = JFFS2_COMPR_NONE;
+- ri->data_crc = crc32(0, &dev, devlen);
+- ri->node_crc = crc32(0, ri, sizeof(*ri)-8);
++ ri->data_crc = cpu_to_je32(crc32(0, &dev, devlen));
++ ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
+
+- fn = jffs2_write_dnode(inode, ri, (char *)&dev, devlen, phys_ofs, &writtenlen);
++ fn = jffs2_write_dnode(c, f, ri, (char *)&dev, devlen, phys_ofs, ALLOC_NORMAL);
+
+ jffs2_free_raw_inode(ri);
+
+@@ -869,13 +642,6 @@
+ f->metadata = fn;
+ up(&f->sem);
+
+- /* Work out where to put the dirent node now. */
+- writtenlen = (writtenlen+3)&~3;
+- phys_ofs += writtenlen;
+- alloclen -= writtenlen;
+-
+- if (alloclen < sizeof(*rd)+namelen) {
+- /* Not enough space left in this chunk. Get some more */
+ jffs2_complete_reservation(c);
+ ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL);
+ if (ret) {
+@@ -883,7 +649,6 @@
+ jffs2_clear_inode(inode);
+ return ret;
+ }
+- }
+
+ rd = jffs2_alloc_raw_dirent();
+ if (!rd) {
+@@ -896,44 +661,45 @@
+ dir_f = JFFS2_INODE_INFO(dir_i);
+ down(&dir_f->sem);
+
+- rd->magic = JFFS2_MAGIC_BITMASK;
+- rd->nodetype = JFFS2_NODETYPE_DIRENT;
+- rd->totlen = sizeof(*rd) + namelen;
+- rd->hdr_crc = crc32(0, rd, sizeof(struct jffs2_unknown_node)-4);
+-
+- rd->pino = dir_i->i_ino;
+- rd->version = ++dir_f->highest_version;
+- rd->ino = inode->i_ino;
+- rd->mctime = CURRENT_TIME;
++ rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
++ rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT);
++ rd->totlen = cpu_to_je32(sizeof(*rd) + namelen);
++ rd->hdr_crc = cpu_to_je32(crc32(0, rd, sizeof(struct jffs2_unknown_node)-4));
++
++ rd->pino = cpu_to_je32(dir_i->i_ino);
++ rd->version = cpu_to_je32(++dir_f->highest_version);
++ rd->ino = cpu_to_je32(inode->i_ino);
++ rd->mctime = cpu_to_je32(get_seconds());
+ rd->nsize = namelen;
+
+ /* XXX: This is ugly. */
+ rd->type = (mode & S_IFMT) >> 12;
+
+- rd->node_crc = crc32(0, rd, sizeof(*rd)-8);
+- rd->name_crc = crc32(0, dentry->d_name.name, namelen);
+-
+- fd = jffs2_write_dirent(dir_i, rd, dentry->d_name.name, namelen, phys_ofs, &writtenlen);
++ rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8));
++ rd->name_crc = cpu_to_je32(crc32(0, dentry->d_name.name, namelen));
+
+- jffs2_complete_reservation(c);
++ fd = jffs2_write_dirent(c, dir_f, rd, dentry->d_name.name, namelen, phys_ofs, ALLOC_NORMAL);
+
+ if (IS_ERR(fd)) {
+ /* dirent failed to write. Delete the inode normally
+ as if it were the final unlink() */
++ jffs2_complete_reservation(c);
+ jffs2_free_raw_dirent(rd);
+ up(&dir_f->sem);
+ jffs2_clear_inode(inode);
+ return PTR_ERR(fd);
+ }
+
+- dir_i->i_mtime = dir_i->i_ctime = rd->mctime;
++ dir_i->i_mtime = dir_i->i_ctime = ITIME(je32_to_cpu(rd->mctime));
+
+ jffs2_free_raw_dirent(rd);
+
+ /* Link the fd into the inode's list, obsoleting an old
+ one if necessary. */
+ jffs2_add_fd_to_list(c, fd, &dir_f->dents);
++
+ up(&dir_f->sem);
++ jffs2_complete_reservation(c);
+
+ d_instantiate(dentry, inode);
+
+@@ -944,7 +710,9 @@
+ struct inode *new_dir_i, struct dentry *new_dentry)
+ {
+ int ret;
++ struct jffs2_sb_info *c = JFFS2_SB_INFO(old_dir_i->i_sb);
+ struct jffs2_inode_info *victim_f = NULL;
++ uint8_t type;
+
+ /* The VFS will check for us and prevent trying to rename a
+ * file over a directory and vice versa, but if it's a directory,
+@@ -973,7 +741,15 @@
+ */
+
+ /* Make a hard link */
+- ret = jffs2_do_link(old_dentry, new_dir_i, new_dentry, 1);
++
++ /* XXX: This is ugly */
++ type = (old_dentry->d_inode->i_mode & S_IFMT) >> 12;
++ if (!type) type = DT_REG;
++
++ ret = jffs2_do_link(c, JFFS2_INODE_INFO(new_dir_i),
++ old_dentry->d_inode->i_ino, type,
++ new_dentry->d_name.name, new_dentry->d_name.len);
++
+ if (ret)
+ return ret;
+
+@@ -989,22 +765,36 @@
+ }
+ }
+
++ /* If it was a directory we moved, and there was no victim,
++ increase i_nlink on its new parent */
++ if (S_ISDIR(old_dentry->d_inode->i_mode) && !victim_f)
++ new_dir_i->i_nlink++;
++
+ /* Unlink the original */
+- ret = jffs2_do_unlink(old_dir_i, old_dentry, 1);
++ ret = jffs2_do_unlink(c, JFFS2_INODE_INFO(old_dir_i),
++ old_dentry->d_name.name, old_dentry->d_name.len, NULL);
++
++ /* We don't touch inode->i_nlink */
+
+ if (ret) {
+ /* Oh shit. We really ought to make a single node which can do both atomically */
+ struct jffs2_inode_info *f = JFFS2_INODE_INFO(old_dentry->d_inode);
+ down(&f->sem);
++ old_dentry->d_inode->i_nlink++;
+ if (f->inocache)
+- old_dentry->d_inode->i_nlink = f->inocache->nlink++;
++ f->inocache->nlink++;
+ up(&f->sem);
+
+ printk(KERN_NOTICE "jffs2_rename(): Link succeeded, unlink failed (err %d). You now have a hard link\n", ret);
+ /* Might as well let the VFS know */
+ d_instantiate(new_dentry, old_dentry->d_inode);
+ atomic_inc(&old_dentry->d_inode->i_count);
+- }
+ return ret;
++ }
++
++ if (S_ISDIR(old_dentry->d_inode->i_mode))
++ old_dir_i->i_nlink--;
++
++ return 0;
+ }
+
+diff -Nurb linux-mips-2.4.24-pre2/fs/jffs2/erase.c linux/fs/jffs2/erase.c
+--- linux-mips-2.4.24-pre2/fs/jffs2/erase.c 2004-11-17 18:05:04.000000000 +0100
++++ linux/fs/jffs2/erase.c 2004-11-17 18:17:59.000000000 +0100
+@@ -1,68 +1,60 @@
+ /*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+- * Copyright (C) 2001 Red Hat, Inc.
++ * Copyright (C) 2001-2003 Red Hat, Inc.
+ *
+- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
++ * Created by David Woodhouse <dwmw2@redhat.com>
+ *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
++ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+- * The contents of this file are subject to the Red Hat eCos Public
+- * License Version 1.1 (the "Licence"); you may not use this file
+- * except in compliance with the Licence. You may obtain a copy of
+- * the Licence at http://www.redhat.com/
+- *
+- * Software distributed under the Licence is distributed on an "AS IS"
+- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+- * See the Licence for the specific language governing rights and
+- * limitations under the Licence.
+- *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
+- *
+- * Alternatively, the contents of this file may be used under the
+- * terms of the GNU General Public License version 2 (the "GPL"), in
+- * which case the provisions of the GPL are applicable instead of the
+- * above. If you wish to allow the use of your version of this file
+- * only under the terms of the GPL and not to allow others to use your
+- * version of this file under the RHEPL, indicate your decision by
+- * deleting the provisions above and replace them with the notice and
+- * other provisions required by the GPL. If you do not delete the
+- * provisions above, a recipient may use your version of this file
+- * under either the RHEPL or the GPL.
+- *
+- * $Id$
++ * $Id$
+ *
+ */
++
+ #include <linux/kernel.h>
+ #include <linux/slab.h>
+ #include <linux/mtd/mtd.h>
+-#include <linux/jffs2.h>
+-#include <linux/interrupt.h>
+-#include "nodelist.h"
++#include <linux/compiler.h>
+ #include <linux/crc32.h>
++#include <linux/sched.h>
++#include <linux/pagemap.h>
++#include "nodelist.h"
+
+ struct erase_priv_struct {
+ struct jffs2_eraseblock *jeb;
+ struct jffs2_sb_info *c;
+ };
+
++#ifndef __ECOS
+ static void jffs2_erase_callback(struct erase_info *);
++#endif
++static void jffs2_erase_failed(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
++static void jffs2_erase_succeeded(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
+ static void jffs2_free_all_node_refs(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
++static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
+
+ void jffs2_erase_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
+ {
+- struct erase_info *instr;
+ int ret;
++#ifdef __ECOS
++ ret = jffs2_flash_erase(c, jeb);
++ if (!ret) {
++ jffs2_erase_succeeded(c, jeb);
++ return;
++ }
++#else /* Linux */
++ struct erase_info *instr;
+
+ instr = kmalloc(sizeof(struct erase_info) + sizeof(struct erase_priv_struct), GFP_KERNEL);
+ if (!instr) {
+ printk(KERN_WARNING "kmalloc for struct erase_info in jffs2_erase_block failed. Refiling block for later\n");
+- spin_lock_bh(&c->erase_completion_lock);
++ spin_lock(&c->erase_completion_lock);
+ list_del(&jeb->list);
+ list_add(&jeb->list, &c->erase_pending_list);
+ c->erasing_size -= c->sector_size;
+- spin_unlock_bh(&c->erase_completion_lock);
++ c->dirty_size += c->sector_size;
++ jeb->dirty_size = c->sector_size;
++ spin_unlock(&c->erase_completion_lock);
+ return;
+ }
+
+@@ -77,19 +69,27 @@
+ ((struct erase_priv_struct *)instr->priv)->jeb = jeb;
+ ((struct erase_priv_struct *)instr->priv)->c = c;
+
++ /* NAND , read out the fail counter, if possible */
++ if (!jffs2_can_mark_obsolete(c))
++ jffs2_nand_read_failcnt(c,jeb);
++
+ ret = c->mtd->erase(c->mtd, instr);
+- if (!ret) {
++ if (!ret)
+ return;
+- }
++
++ kfree(instr);
++#endif /* __ECOS */
++
+ if (ret == -ENOMEM || ret == -EAGAIN) {
+ /* Erase failed immediately. Refile it on the list */
+ D1(printk(KERN_DEBUG "Erase at 0x%08x failed: %d. Refiling on erase_pending_list\n", jeb->offset, ret));
+- spin_lock_bh(&c->erase_completion_lock);
++ spin_lock(&c->erase_completion_lock);
+ list_del(&jeb->list);
+ list_add(&jeb->list, &c->erase_pending_list);
+ c->erasing_size -= c->sector_size;
+- spin_unlock_bh(&c->erase_completion_lock);
+- kfree(instr);
++ c->dirty_size += c->sector_size;
++ jeb->dirty_size = c->sector_size;
++ spin_unlock(&c->erase_completion_lock);
+ return;
+ }
+
+@@ -97,74 +97,101 @@
+ printk(KERN_WARNING "Erase at 0x%08x failed immediately: -EROFS. Is the sector locked?\n", jeb->offset);
+ else
+ printk(KERN_WARNING "Erase at 0x%08x failed immediately: errno %d\n", jeb->offset, ret);
+- spin_lock_bh(&c->erase_completion_lock);
+- list_del(&jeb->list);
+- list_add(&jeb->list, &c->bad_list);
+- c->nr_erasing_blocks--;
+- c->bad_size += c->sector_size;
+- c->erasing_size -= c->sector_size;
+- spin_unlock_bh(&c->erase_completion_lock);
+- wake_up(&c->erase_wait);
+- kfree(instr);
++
++ jffs2_erase_failed(c, jeb);
+ }
+
+-void jffs2_erase_pending_blocks(struct jffs2_sb_info *c)
++void jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count)
+ {
+ struct jffs2_eraseblock *jeb;
+
+- spin_lock_bh(&c->erase_completion_lock);
+- while (!list_empty(&c->erase_pending_list)) {
++ down(&c->erase_free_sem);
+
+- jeb = list_entry(c->erase_pending_list.next, struct jffs2_eraseblock, list);
++ spin_lock(&c->erase_completion_lock);
+
+- D1(printk(KERN_DEBUG "Starting erase of pending block 0x%08x\n", jeb->offset));
++ while (!list_empty(&c->erase_complete_list) ||
++ !list_empty(&c->erase_pending_list)) {
+
++ if (!list_empty(&c->erase_complete_list)) {
++ jeb = list_entry(c->erase_complete_list.next, struct jffs2_eraseblock, list);
++ list_del(&jeb->list);
++ spin_unlock(&c->erase_completion_lock);
++ jffs2_mark_erased_block(c, jeb);
++
++ if (!--count) {
++ D1(printk(KERN_DEBUG "Count reached. jffs2_erase_pending_blocks leaving\n"));
++ goto done;
++ }
++
++ } else if (!list_empty(&c->erase_pending_list)) {
++ jeb = list_entry(c->erase_pending_list.next, struct jffs2_eraseblock, list);
++ D1(printk(KERN_DEBUG "Starting erase of pending block 0x%08x\n", jeb->offset));
+ list_del(&jeb->list);
+ c->erasing_size += c->sector_size;
++ c->wasted_size -= jeb->wasted_size;
+ c->free_size -= jeb->free_size;
+ c->used_size -= jeb->used_size;
+ c->dirty_size -= jeb->dirty_size;
+- jeb->used_size = jeb->dirty_size = jeb->free_size = 0;
++ jeb->wasted_size = jeb->used_size = jeb->dirty_size = jeb->free_size = 0;
+ jffs2_free_all_node_refs(c, jeb);
+ list_add(&jeb->list, &c->erasing_list);
+- spin_unlock_bh(&c->erase_completion_lock);
++ spin_unlock(&c->erase_completion_lock);
+
+ jffs2_erase_block(c, jeb);
++
++ } else {
++ BUG();
++ }
++
+ /* Be nice */
+- if (current->need_resched)
+- schedule();
+- spin_lock_bh(&c->erase_completion_lock);
++ cond_resched();
++ spin_lock(&c->erase_completion_lock);
+ }
+- spin_unlock_bh(&c->erase_completion_lock);
++
++ spin_unlock(&c->erase_completion_lock);
++ done:
+ D1(printk(KERN_DEBUG "jffs2_erase_pending_blocks completed\n"));
++
++ up(&c->erase_free_sem);
++}
++
++static void jffs2_erase_succeeded(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
++{
++ D1(printk(KERN_DEBUG "Erase completed successfully at 0x%08x\n", jeb->offset));
++ spin_lock(&c->erase_completion_lock);
++ list_del(&jeb->list);
++ list_add_tail(&jeb->list, &c->erase_complete_list);
++ spin_unlock(&c->erase_completion_lock);
++ /* Ensure that kupdated calls us again to mark them clean */
++ jffs2_erase_pending_trigger(c);
+ }
+
++static void jffs2_erase_failed(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
++{
++ spin_lock(&c->erase_completion_lock);
++ c->erasing_size -= c->sector_size;
++ c->bad_size += c->sector_size;
++ list_del(&jeb->list);
++ list_add(&jeb->list, &c->bad_list);
++ c->nr_erasing_blocks--;
++ spin_unlock(&c->erase_completion_lock);
++ wake_up(&c->erase_wait);
++}
+
++#ifndef __ECOS
+ static void jffs2_erase_callback(struct erase_info *instr)
+ {
+ struct erase_priv_struct *priv = (void *)instr->priv;
+
+ if(instr->state != MTD_ERASE_DONE) {
+ printk(KERN_WARNING "Erase at 0x%08x finished, but state != MTD_ERASE_DONE. State is 0x%x instead.\n", instr->addr, instr->state);
+- spin_lock(&priv->c->erase_completion_lock);
+- priv->c->erasing_size -= priv->c->sector_size;
+- priv->c->bad_size += priv->c->sector_size;
+- list_del(&priv->jeb->list);
+- list_add(&priv->jeb->list, &priv->c->bad_list);
+- priv->c->nr_erasing_blocks--;
+- spin_unlock(&priv->c->erase_completion_lock);
+- wake_up(&priv->c->erase_wait);
++ jffs2_erase_failed(priv->c, priv->jeb);
+ } else {
+- D1(printk(KERN_DEBUG "Erase completed successfully at 0x%08x\n", instr->addr));
+- spin_lock(&priv->c->erase_completion_lock);
+- list_del(&priv->jeb->list);
+- list_add_tail(&priv->jeb->list, &priv->c->erase_complete_list);
+- spin_unlock(&priv->c->erase_completion_lock);
++ jffs2_erase_succeeded(priv->c, priv->jeb);
+ }
+- /* Make sure someone picks up the block off the erase_complete list */
+- OFNI_BS_2SFFJ(priv->c)->s_dirt = 1;
+ kfree(instr);
+ }
++#endif /* !__ECOS */
+
+ /* Hmmm. Maybe we should accept the extra space it takes and make
+ this a standard doubly-linked list? */
+@@ -221,7 +248,7 @@
+ this = ic->nodes;
+
+ while(this) {
+- printk( "0x%08x(%d)->", this->flash_offset & ~3, this->flash_offset &3);
++ printk( "0x%08x(%d)->", ref_offset(this), ref_flags(this));
+ if (++i == 5) {
+ printk("\n" KERN_DEBUG);
+ i=0;
+@@ -256,54 +283,43 @@
+ jeb->last_node = NULL;
+ }
+
+-void jffs2_erase_pending_trigger(struct jffs2_sb_info *c)
++static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
+ {
+- OFNI_BS_2SFFJ(c)->s_dirt = 1;
+-}
+-
+-void jffs2_mark_erased_blocks(struct jffs2_sb_info *c)
+-{
+- static struct jffs2_unknown_node marker = {JFFS2_MAGIC_BITMASK, JFFS2_NODETYPE_CLEANMARKER, sizeof(struct jffs2_unknown_node)};
+- struct jffs2_eraseblock *jeb;
+- struct jffs2_raw_node_ref *marker_ref;
++ struct jffs2_raw_node_ref *marker_ref = NULL;
+ unsigned char *ebuf;
+- ssize_t retlen;
++ size_t retlen;
+ int ret;
+
+- marker.hdr_crc = crc32(0, &marker, sizeof(struct jffs2_unknown_node)-4);
+-
+- spin_lock_bh(&c->erase_completion_lock);
+- while (!list_empty(&c->erase_complete_list)) {
+- jeb = list_entry(c->erase_complete_list.next, struct jffs2_eraseblock, list);
+- list_del(&jeb->list);
+- spin_unlock_bh(&c->erase_completion_lock);
+-
++ if (!jffs2_cleanmarker_oob(c)) {
+ marker_ref = jffs2_alloc_raw_node_ref();
+ if (!marker_ref) {
+ printk(KERN_WARNING "Failed to allocate raw node ref for clean marker\n");
+- /* Come back later */
++ /* Stick it back on the list from whence it came and come back later */
+ jffs2_erase_pending_trigger(c);
++ spin_lock(&c->erase_completion_lock);
++ list_add(&jeb->list, &c->erase_complete_list);
++ spin_unlock(&c->erase_completion_lock);
+ return;
+ }
+-
++ }
+ ebuf = kmalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!ebuf) {
+ printk(KERN_WARNING "Failed to allocate page buffer for verifying erase at 0x%08x. Assuming it worked\n", jeb->offset);
+ } else {
+- __u32 ofs = jeb->offset;
++ uint32_t ofs = jeb->offset;
+
+ D1(printk(KERN_DEBUG "Verifying erase at 0x%08x\n", jeb->offset));
+ while(ofs < jeb->offset + c->sector_size) {
+- __u32 readlen = min((__u32)PAGE_SIZE, jeb->offset + c->sector_size - ofs);
++ uint32_t readlen = min((uint32_t)PAGE_SIZE, jeb->offset + c->sector_size - ofs);
+ int i;
+
+- ret = c->mtd->read(c->mtd, ofs, readlen, &retlen, ebuf);
+- if (ret < 0) {
++ ret = jffs2_flash_read(c, ofs, readlen, &retlen, ebuf);
++ if (ret) {
+ printk(KERN_WARNING "Read of newly-erased block at 0x%08x failed: %d. Putting on bad_list\n", ofs, ret);
+ goto bad;
+ }
+ if (retlen != readlen) {
+- printk(KERN_WARNING "Short read from newly-erased block at 0x%08x. Wanted %d, got %d\n", ofs, readlen, retlen);
++ printk(KERN_WARNING "Short read from newly-erased block at 0x%08x. Wanted %d, got %zd\n", ofs, readlen, retlen);
+ goto bad;
+ }
+ for (i=0; i<readlen; i += sizeof(unsigned long)) {
+@@ -312,62 +328,89 @@
+ if (datum + 1) {
+ printk(KERN_WARNING "Newly-erased block contained word 0x%lx at offset 0x%08x\n", datum, ofs + i);
+ bad:
++ if (!jffs2_cleanmarker_oob(c))
+ jffs2_free_raw_node_ref(marker_ref);
++ else
++ jffs2_write_nand_badblock( c ,jeb );
+ kfree(ebuf);
+ bad2:
+- spin_lock_bh(&c->erase_completion_lock);
++ spin_lock(&c->erase_completion_lock);
+ c->erasing_size -= c->sector_size;
+ c->bad_size += c->sector_size;
+
+ list_add_tail(&jeb->list, &c->bad_list);
+ c->nr_erasing_blocks--;
+- spin_unlock_bh(&c->erase_completion_lock);
++ spin_unlock(&c->erase_completion_lock);
+ wake_up(&c->erase_wait);
+ return;
+ }
+ }
+ ofs += readlen;
++ cond_resched();
+ }
+ kfree(ebuf);
+ }
+
+ /* Write the erase complete marker */
+ D1(printk(KERN_DEBUG "Writing erased marker to block at 0x%08x\n", jeb->offset));
+- ret = c->mtd->write(c->mtd, jeb->offset, sizeof(marker), &retlen, (char *)&marker);
++ if (jffs2_cleanmarker_oob(c)) {
++
++ if (jffs2_write_nand_cleanmarker(c, jeb))
++ goto bad2;
++
++ jeb->first_node = jeb->last_node = NULL;
++
++ jeb->free_size = c->sector_size;
++ jeb->used_size = 0;
++ jeb->dirty_size = 0;
++ jeb->wasted_size = 0;
++ } else {
++ struct jffs2_unknown_node marker = {
++ .magic = cpu_to_je16(JFFS2_MAGIC_BITMASK),
++ .nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER),
++ .totlen = cpu_to_je32(c->cleanmarker_size)
++ };
++
++ marker.hdr_crc = cpu_to_je32(crc32(0, &marker, sizeof(struct jffs2_unknown_node)-4));
++
++ /* We only write the header; the rest was noise or padding anyway */
++ ret = jffs2_flash_write(c, jeb->offset, sizeof(marker), &retlen, (char *)&marker);
+ if (ret) {
+ printk(KERN_WARNING "Write clean marker to block at 0x%08x failed: %d\n",
+ jeb->offset, ret);
+ goto bad2;
+ }
+ if (retlen != sizeof(marker)) {
+- printk(KERN_WARNING "Short write to newly-erased block at 0x%08x: Wanted %d, got %d\n",
++ printk(KERN_WARNING "Short write to newly-erased block at 0x%08x: Wanted %d, got %zd\n",
+ jeb->offset, sizeof(marker), retlen);
+ goto bad2;
+ }
+
+ marker_ref->next_in_ino = NULL;
+ marker_ref->next_phys = NULL;
+- marker_ref->flash_offset = jeb->offset;
+- marker_ref->totlen = PAD(sizeof(marker));
++ marker_ref->flash_offset = jeb->offset | REF_NORMAL;
++ marker_ref->__totlen = c->cleanmarker_size;
+
+ jeb->first_node = jeb->last_node = marker_ref;
+
+- jeb->free_size = c->sector_size - marker_ref->totlen;
+- jeb->used_size = marker_ref->totlen;
++ jeb->free_size = c->sector_size - c->cleanmarker_size;
++ jeb->used_size = c->cleanmarker_size;
+ jeb->dirty_size = 0;
++ jeb->wasted_size = 0;
++ }
+
+- spin_lock_bh(&c->erase_completion_lock);
++ spin_lock(&c->erase_completion_lock);
+ c->erasing_size -= c->sector_size;
+ c->free_size += jeb->free_size;
+ c->used_size += jeb->used_size;
+
+ ACCT_SANITY_CHECK(c,jeb);
+- ACCT_PARANOIA_CHECK(jeb);
++ D1(ACCT_PARANOIA_CHECK(jeb));
+
+ list_add_tail(&jeb->list, &c->free_list);
+ c->nr_erasing_blocks--;
+ c->nr_free_blocks++;
++ spin_unlock(&c->erase_completion_lock);
+ wake_up(&c->erase_wait);
+- }
+- spin_unlock_bh(&c->erase_completion_lock);
+ }
++
+diff -Nurb linux-mips-2.4.24-pre2/fs/jffs2/file.c linux/fs/jffs2/file.c
+--- linux-mips-2.4.24-pre2/fs/jffs2/file.c 2004-11-17 18:05:04.000000000 +0100
++++ linux/fs/jffs2/file.c 2004-11-17 18:17:59.000000000 +0100
+@@ -1,319 +1,106 @@
+ /*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+- * Copyright (C) 2001 Red Hat, Inc.
++ * Copyright (C) 2001-2003 Red Hat, Inc.
+ *
+- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
++ * Created by David Woodhouse <dwmw2@redhat.com>
+ *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
++ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+- * The contents of this file are subject to the Red Hat eCos Public
+- * License Version 1.1 (the "Licence"); you may not use this file
+- * except in compliance with the Licence. You may obtain a copy of
+- * the Licence at http://www.redhat.com/
+- *
+- * Software distributed under the Licence is distributed on an "AS IS"
+- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+- * See the Licence for the specific language governing rights and
+- * limitations under the Licence.
+- *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
+- *
+- * Alternatively, the contents of this file may be used under the
+- * terms of the GNU General Public License version 2 (the "GPL"), in
+- * which case the provisions of the GPL are applicable instead of the
+- * above. If you wish to allow the use of your version of this file
+- * only under the terms of the GPL and not to allow others to use your
+- * version of this file under the RHEPL, indicate your decision by
+- * deleting the provisions above and replace them with the notice and
+- * other provisions required by the GPL. If you do not delete the
+- * provisions above, a recipient may use your version of this file
+- * under either the RHEPL or the GPL.
+- *
+- * $Id$
++ * $Id$
+ *
+ */
+
++#include <linux/version.h>
+ #include <linux/kernel.h>
+-#include <linux/mtd/compatmac.h> /* for min() */
+ #include <linux/slab.h>
+ #include <linux/fs.h>
++#include <linux/time.h>
+ #include <linux/pagemap.h>
++#include <linux/highmem.h>
++#include <linux/crc32.h>
+ #include <linux/jffs2.h>
+ #include "nodelist.h"
+-#include <linux/crc32.h>
+
+ extern int generic_file_open(struct inode *, struct file *) __attribute__((weak));
+ extern loff_t generic_file_llseek(struct file *file, loff_t offset, int origin) __attribute__((weak));
+
+
+-int jffs2_null_fsync(struct file *filp, struct dentry *dentry, int datasync)
++int jffs2_fsync(struct file *filp, struct dentry *dentry, int datasync)
+ {
+- /* Move along. Nothing to see here */
++ struct inode *inode = dentry->d_inode;
++ struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
++
++ /* Trigger GC to flush any pending writes for this inode */
++ jffs2_flush_wbuf_gc(c, inode->i_ino);
++
+ return 0;
+ }
+
+ struct file_operations jffs2_file_operations =
+ {
+- llseek: generic_file_llseek,
+- open: generic_file_open,
+- read: generic_file_read,
+- write: generic_file_write,
+- ioctl: jffs2_ioctl,
+- mmap: generic_file_mmap,
+- fsync: jffs2_null_fsync
++ .llseek = generic_file_llseek,
++ .open = generic_file_open,
++ .read = generic_file_read,
++ .write = generic_file_write,
++ .ioctl = jffs2_ioctl,
++ .mmap = generic_file_readonly_mmap,
++ .fsync = jffs2_fsync,
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,29)
++ .sendfile = generic_file_sendfile
++#endif
+ };
+
+ /* jffs2_file_inode_operations */
+
+ struct inode_operations jffs2_file_inode_operations =
+ {
+- setattr: jffs2_setattr
++ .setattr = jffs2_setattr
+ };
+
+ struct address_space_operations jffs2_file_address_operations =
+ {
+- readpage: jffs2_readpage,
+- prepare_write: jffs2_prepare_write,
+- commit_write: jffs2_commit_write
++ .readpage = jffs2_readpage,
++ .prepare_write =jffs2_prepare_write,
++ .commit_write = jffs2_commit_write
+ };
+
+-int jffs2_setattr (struct dentry *dentry, struct iattr *iattr)
+-{
+- struct jffs2_full_dnode *old_metadata, *new_metadata;
+- struct inode *inode = dentry->d_inode;
+- struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
+- struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
+- struct jffs2_raw_inode *ri;
+- unsigned short dev;
+- unsigned char *mdata = NULL;
+- int mdatalen = 0;
+- unsigned int ivalid;
+- __u32 phys_ofs, alloclen;
+- int ret;
+- D1(printk(KERN_DEBUG "jffs2_setattr(): ino #%lu\n", inode->i_ino));
+- ret = inode_change_ok(inode, iattr);
+- if (ret)
+- return ret;
+-
+- /* Special cases - we don't want more than one data node
+- for these types on the medium at any time. So setattr
+- must read the original data associated with the node
+- (i.e. the device numbers or the target name) and write
+- it out again with the appropriate data attached */
+- if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode)) {
+- /* For these, we don't actually need to read the old node */
+- dev = (MAJOR(to_kdev_t(dentry->d_inode->i_rdev)) << 8) |
+- MINOR(to_kdev_t(dentry->d_inode->i_rdev));
+- mdata = (char *)&dev;
+- mdatalen = sizeof(dev);
+- D1(printk(KERN_DEBUG "jffs2_setattr(): Writing %d bytes of kdev_t\n", mdatalen));
+- } else if (S_ISLNK(inode->i_mode)) {
+- mdatalen = f->metadata->size;
+- mdata = kmalloc(f->metadata->size, GFP_USER);
+- if (!mdata)
+- return -ENOMEM;
+- ret = jffs2_read_dnode(c, f->metadata, mdata, 0, mdatalen);
+- if (ret) {
+- kfree(mdata);
+- return ret;
+- }
+- D1(printk(KERN_DEBUG "jffs2_setattr(): Writing %d bytes of symlink target\n", mdatalen));
+- }
+-
+- ri = jffs2_alloc_raw_inode();
+- if (!ri) {
+- if (S_ISLNK(inode->i_mode))
+- kfree(mdata);
+- return -ENOMEM;
+- }
+-
+- ret = jffs2_reserve_space(c, sizeof(*ri) + mdatalen, &phys_ofs, &alloclen, ALLOC_NORMAL);
+- if (ret) {
+- jffs2_free_raw_inode(ri);
+- if (S_ISLNK(inode->i_mode))
+- kfree(mdata);
+- return ret;
+- }
+- down(&f->sem);
+- ivalid = iattr->ia_valid;
+-
+- ri->magic = JFFS2_MAGIC_BITMASK;
+- ri->nodetype = JFFS2_NODETYPE_INODE;
+- ri->totlen = sizeof(*ri) + mdatalen;
+- ri->hdr_crc = crc32(0, ri, sizeof(struct jffs2_unknown_node)-4);
+-
+- ri->ino = inode->i_ino;
+- ri->version = ++f->highest_version;
+-
+- ri->mode = (ivalid & ATTR_MODE)?iattr->ia_mode:inode->i_mode;
+- ri->uid = (ivalid & ATTR_UID)?iattr->ia_uid:inode->i_uid;
+- ri->gid = (ivalid & ATTR_GID)?iattr->ia_gid:inode->i_gid;
+-
+- if (ivalid & ATTR_MODE && ri->mode & S_ISGID &&
+- !in_group_p(ri->gid) && !capable(CAP_FSETID))
+- ri->mode &= ~S_ISGID;
+-
+- ri->isize = (ivalid & ATTR_SIZE)?iattr->ia_size:inode->i_size;
+- ri->atime = (ivalid & ATTR_ATIME)?iattr->ia_atime:inode->i_atime;
+- ri->mtime = (ivalid & ATTR_MTIME)?iattr->ia_mtime:inode->i_mtime;
+- ri->ctime = (ivalid & ATTR_CTIME)?iattr->ia_ctime:inode->i_ctime;
+-
+- ri->offset = 0;
+- ri->csize = ri->dsize = mdatalen;
+- ri->compr = JFFS2_COMPR_NONE;
+- if (inode->i_size < ri->isize) {
+- /* It's an extension. Make it a hole node */
+- ri->compr = JFFS2_COMPR_ZERO;
+- ri->dsize = ri->isize - inode->i_size;
+- ri->offset = inode->i_size;
+- }
+- ri->node_crc = crc32(0, ri, sizeof(*ri)-8);
+- if (mdatalen)
+- ri->data_crc = crc32(0, mdata, mdatalen);
+- else
+- ri->data_crc = 0;
+-
+- new_metadata = jffs2_write_dnode(inode, ri, mdata, mdatalen, phys_ofs, NULL);
+- if (S_ISLNK(inode->i_mode))
+- kfree(mdata);
+-
+- jffs2_complete_reservation(c);
+-
+- if (IS_ERR(new_metadata)) {
+- jffs2_free_raw_inode(ri);
+- up(&f->sem);
+- return PTR_ERR(new_metadata);
+- }
+- /* It worked. Update the inode */
+- inode->i_atime = ri->atime;
+- inode->i_ctime = ri->ctime;
+- inode->i_mtime = ri->mtime;
+- inode->i_mode = ri->mode;
+- inode->i_uid = ri->uid;
+- inode->i_gid = ri->gid;
+-
+-
+- old_metadata = f->metadata;
+-
+- if (inode->i_size > ri->isize) {
+- vmtruncate(inode, ri->isize);
+- jffs2_truncate_fraglist (c, &f->fraglist, ri->isize);
+- }
+-
+- if (inode->i_size < ri->isize) {
+- jffs2_add_full_dnode_to_inode(c, f, new_metadata);
+- inode->i_size = ri->isize;
+- f->metadata = NULL;
+- } else {
+- f->metadata = new_metadata;
+- }
+- if (old_metadata) {
+- jffs2_mark_node_obsolete(c, old_metadata->raw);
+- jffs2_free_full_dnode(old_metadata);
+- }
+- jffs2_free_raw_inode(ri);
+- up(&f->sem);
+- return 0;
+-}
+-
+ int jffs2_do_readpage_nolock (struct inode *inode, struct page *pg)
+ {
+ struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
+ struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
+- struct jffs2_node_frag *frag = f->fraglist;
+- __u32 offset = pg->index << PAGE_CACHE_SHIFT;
+- __u32 end = offset + PAGE_CACHE_SIZE;
+ unsigned char *pg_buf;
+ int ret;
+
+- D1(printk(KERN_DEBUG "jffs2_do_readpage_nolock(): ino #%lu, page at offset 0x%x\n", inode->i_ino, offset));
++ D2(printk(KERN_DEBUG "jffs2_do_readpage_nolock(): ino #%lu, page at offset 0x%lx\n", inode->i_ino, pg->index << PAGE_CACHE_SHIFT));
+
+ if (!PageLocked(pg))
+ PAGE_BUG(pg);
+
+- while(frag && frag->ofs + frag->size <= offset) {
+- // D1(printk(KERN_DEBUG "skipping frag %d-%d; before the region we care about\n", frag->ofs, frag->ofs + frag->size));
+- frag = frag->next;
+- }
+-
+ pg_buf = kmap(pg);
++ /* FIXME: Can kmap fail? */
+
+- /* XXX FIXME: Where a single physical node actually shows up in two
+- frags, we read it twice. Don't do that. */
+- /* Now we're pointing at the first frag which overlaps our page */
+- while(offset < end) {
+- D2(printk(KERN_DEBUG "jffs2_readpage: offset %d, end %d\n", offset, end));
+- if (!frag || frag->ofs > offset) {
+- __u32 holesize = end - offset;
+- if (frag) {
+- D1(printk(KERN_NOTICE "Eep. Hole in ino %ld fraglist. frag->ofs = 0x%08x, offset = 0x%08x\n", inode->i_ino, frag->ofs, offset));
+- holesize = min(holesize, frag->ofs - offset);
+- D1(jffs2_print_frag_list(f));
+- }
+- D1(printk(KERN_DEBUG "Filling non-frag hole from %d-%d\n", offset, offset+holesize));
+- memset(pg_buf, 0, holesize);
+- pg_buf += holesize;
+- offset += holesize;
+- continue;
+- } else if (frag->ofs < offset && (offset & (PAGE_CACHE_SIZE-1)) != 0) {
+- D1(printk(KERN_NOTICE "Eep. Overlap in ino #%ld fraglist. frag->ofs = 0x%08x, offset = 0x%08x\n",
+- inode->i_ino, frag->ofs, offset));
+- D1(jffs2_print_frag_list(f));
+- memset(pg_buf, 0, end - offset);
+- ClearPageUptodate(pg);
+- SetPageError(pg);
+- kunmap(pg);
+- return -EIO;
+- } else if (!frag->node) {
+- __u32 holeend = min(end, frag->ofs + frag->size);
+- D1(printk(KERN_DEBUG "Filling frag hole from %d-%d (frag 0x%x 0x%x)\n", offset, holeend, frag->ofs, frag->ofs + frag->size));
+- memset(pg_buf, 0, holeend - offset);
+- pg_buf += holeend - offset;
+- offset = holeend;
+- frag = frag->next;
+- continue;
+- } else {
+- __u32 readlen;
+- __u32 fragofs; /* offset within the frag to start reading */
++ ret = jffs2_read_inode_range(c, f, pg_buf, pg->index << PAGE_CACHE_SHIFT, PAGE_CACHE_SIZE);
+
+- fragofs = offset - frag->ofs;
+- readlen = min(frag->size - fragofs, end - offset);
+- D1(printk(KERN_DEBUG "Reading %d-%d from node at 0x%x\n", frag->ofs+fragofs,
+- fragofs+frag->ofs+readlen, frag->node->raw->flash_offset & ~3));
+- ret = jffs2_read_dnode(c, frag->node, pg_buf, fragofs + frag->ofs - frag->node->ofs, readlen);
+- D2(printk(KERN_DEBUG "node read done\n"));
+ if (ret) {
+- D1(printk(KERN_DEBUG"jffs2_readpage error %d\n",ret));
+- memset(pg_buf, 0, readlen);
+ ClearPageUptodate(pg);
+ SetPageError(pg);
+- kunmap(pg);
+- return ret;
+- }
+-
+- pg_buf += readlen;
+- offset += readlen;
+- frag = frag->next;
+- D2(printk(KERN_DEBUG "node read was OK. Looping\n"));
+- }
+- }
+- D2(printk(KERN_DEBUG "readpage finishing\n"));
++ } else {
+ SetPageUptodate(pg);
+ ClearPageError(pg);
++ }
+
+ flush_dcache_page(pg);
+-
+ kunmap(pg);
+- D1(printk(KERN_DEBUG "readpage finished\n"));
++
++ D2(printk(KERN_DEBUG "readpage finished\n"));
+ return 0;
+ }
+
+ int jffs2_do_readpage_unlock(struct inode *inode, struct page *pg)
+ {
+ int ret = jffs2_do_readpage_nolock(inode, pg);
+- UnlockPage(pg);
++ unlock_page(pg);
+ return ret;
+ }
+
+@@ -333,17 +120,17 @@
+ {
+ struct inode *inode = pg->mapping->host;
+ struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
+- __u32 pageofs = pg->index << PAGE_CACHE_SHIFT;
++ uint32_t pageofs = pg->index << PAGE_CACHE_SHIFT;
+ int ret = 0;
+
+- D1(printk(KERN_DEBUG "jffs2_prepare_write() nrpages %ld\n", inode->i_mapping->nrpages));
++ D1(printk(KERN_DEBUG "jffs2_prepare_write()\n"));
+
+ if (pageofs > inode->i_size) {
+ /* Make new hole frag from old EOF to new page */
+ struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
+ struct jffs2_raw_inode ri;
+ struct jffs2_full_dnode *fn;
+- __u32 phys_ofs, alloc_len;
++ uint32_t phys_ofs, alloc_len;
+
+ D1(printk(KERN_DEBUG "Writing new hole frag 0x%x-0x%x between current EOF and new page\n",
+ (unsigned int)inode->i_size, pageofs));
+@@ -355,29 +142,30 @@
+ down(&f->sem);
+ memset(&ri, 0, sizeof(ri));
+
+- ri.magic = JFFS2_MAGIC_BITMASK;
+- ri.nodetype = JFFS2_NODETYPE_INODE;
+- ri.totlen = sizeof(ri);
+- ri.hdr_crc = crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4);
+-
+- ri.ino = f->inocache->ino;
+- ri.version = ++f->highest_version;
+- ri.mode = inode->i_mode;
+- ri.uid = inode->i_uid;
+- ri.gid = inode->i_gid;
+- ri.isize = max((__u32)inode->i_size, pageofs);
+- ri.atime = ri.ctime = ri.mtime = CURRENT_TIME;
+- ri.offset = inode->i_size;
+- ri.dsize = pageofs - inode->i_size;
+- ri.csize = 0;
++ ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
++ ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE);
++ ri.totlen = cpu_to_je32(sizeof(ri));
++ ri.hdr_crc = cpu_to_je32(crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4));
++
++ ri.ino = cpu_to_je32(f->inocache->ino);
++ ri.version = cpu_to_je32(++f->highest_version);
++ ri.mode = cpu_to_jemode(inode->i_mode);
++ ri.uid = cpu_to_je16(inode->i_uid);
++ ri.gid = cpu_to_je16(inode->i_gid);
++ ri.isize = cpu_to_je32(max((uint32_t)inode->i_size, pageofs));
++ ri.atime = ri.ctime = ri.mtime = cpu_to_je32(get_seconds());
++ ri.offset = cpu_to_je32(inode->i_size);
++ ri.dsize = cpu_to_je32(pageofs - inode->i_size);
++ ri.csize = cpu_to_je32(0);
+ ri.compr = JFFS2_COMPR_ZERO;
+- ri.node_crc = crc32(0, &ri, sizeof(ri)-8);
+- ri.data_crc = 0;
++ ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8));
++ ri.data_crc = cpu_to_je32(0);
++
++ fn = jffs2_write_dnode(c, f, &ri, NULL, 0, phys_ofs, ALLOC_NORMAL);
+
+- fn = jffs2_write_dnode(inode, &ri, NULL, 0, phys_ofs, NULL);
+- jffs2_complete_reservation(c);
+ if (IS_ERR(fn)) {
+ ret = PTR_ERR(fn);
++ jffs2_complete_reservation(c);
+ up(&f->sem);
+ return ret;
+ }
+@@ -391,16 +179,17 @@
+ D1(printk(KERN_DEBUG "Eep. add_full_dnode_to_inode() failed in prepare_write, returned %d\n", ret));
+ jffs2_mark_node_obsolete(c, fn->raw);
+ jffs2_free_full_dnode(fn);
++ jffs2_complete_reservation(c);
+ up(&f->sem);
+ return ret;
+ }
++ jffs2_complete_reservation(c);
+ inode->i_size = pageofs;
+ up(&f->sem);
+ }
+
+-
+ /* Read in the page if it wasn't already present, unless it's a whole page */
+- if (!Page_Uptodate(pg) && (start || end < PAGE_CACHE_SIZE)) {
++ if (!PageUptodate(pg) && (start || end < PAGE_CACHE_SIZE)) {
+ down(&f->sem);
+ ret = jffs2_do_readpage_nolock(inode, pg);
+ up(&f->sem);
+@@ -417,14 +206,12 @@
+ struct inode *inode = pg->mapping->host;
+ struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
+ struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
+- __u32 newsize = max_t(__u32, filp->f_dentry->d_inode->i_size, (pg->index << PAGE_CACHE_SHIFT) + end);
+- __u32 file_ofs = (pg->index << PAGE_CACHE_SHIFT);
+- __u32 writelen = min((__u32)PAGE_CACHE_SIZE, newsize - file_ofs);
+ struct jffs2_raw_inode *ri;
+ int ret = 0;
+- ssize_t writtenlen = 0;
++ uint32_t writtenlen = 0;
+
+- D1(printk(KERN_DEBUG "jffs2_commit_write(): ino #%lu, page at 0x%lx, range %d-%d, flags %lx\n", inode->i_ino, pg->index << PAGE_CACHE_SHIFT, start, end, pg->flags));
++ D1(printk(KERN_DEBUG "jffs2_commit_write(): ino #%lu, page at 0x%lx, range %d-%d, flags %lx\n",
++ inode->i_ino, pg->index << PAGE_CACHE_SHIFT, start, end, pg->flags));
+
+ if (!start && end == PAGE_CACHE_SIZE) {
+ /* We need to avoid deadlock with page_cache_read() in
+@@ -435,109 +222,47 @@
+ }
+
+ ri = jffs2_alloc_raw_inode();
+- if (!ri)
+- return -ENOMEM;
+-
+- while(writelen) {
+- struct jffs2_full_dnode *fn;
+- unsigned char *comprbuf = NULL;
+- unsigned char comprtype = JFFS2_COMPR_NONE;
+- __u32 phys_ofs, alloclen;
+- __u32 datalen, cdatalen;
+
+- D2(printk(KERN_DEBUG "jffs2_commit_write() loop: 0x%x to write to 0x%x\n", writelen, file_ofs));
+-
+- ret = jffs2_reserve_space(c, sizeof(*ri) + JFFS2_MIN_DATA_LEN, &phys_ofs, &alloclen, ALLOC_NORMAL);
+- if (ret) {
+- SetPageError(pg);
+- D1(printk(KERN_DEBUG "jffs2_reserve_space returned %d\n", ret));
+- break;
++ if (!ri) {
++ D1(printk(KERN_DEBUG "jffs2_commit_write(): Allocation of raw inode failed\n"));
++ return -ENOMEM;
+ }
+- down(&f->sem);
+- datalen = writelen;
+- cdatalen = min(alloclen - sizeof(*ri), writelen);
+-
+- comprbuf = kmalloc(cdatalen, GFP_KERNEL);
+- if (comprbuf) {
+- comprtype = jffs2_compress(page_address(pg)+ (file_ofs & (PAGE_CACHE_SIZE-1)), comprbuf, &datalen, &cdatalen);
+- }
+- if (comprtype == JFFS2_COMPR_NONE) {
+- /* Either compression failed, or the allocation of comprbuf failed */
+- if (comprbuf)
+- kfree(comprbuf);
+- comprbuf = page_address(pg) + (file_ofs & (PAGE_CACHE_SIZE -1));
+- datalen = cdatalen;
+- }
+- /* Now comprbuf points to the data to be written, be it compressed or not.
+- comprtype holds the compression type, and comprtype == JFFS2_COMPR_NONE means
+- that the comprbuf doesn't need to be kfree()d.
+- */
+-
+- ri->magic = JFFS2_MAGIC_BITMASK;
+- ri->nodetype = JFFS2_NODETYPE_INODE;
+- ri->totlen = sizeof(*ri) + cdatalen;
+- ri->hdr_crc = crc32(0, ri, sizeof(struct jffs2_unknown_node)-4);
+-
+- ri->ino = inode->i_ino;
+- ri->version = ++f->highest_version;
+- ri->mode = inode->i_mode;
+- ri->uid = inode->i_uid;
+- ri->gid = inode->i_gid;
+- ri->isize = max((__u32)inode->i_size, file_ofs + datalen);
+- ri->atime = ri->ctime = ri->mtime = CURRENT_TIME;
+- ri->offset = file_ofs;
+- ri->csize = cdatalen;
+- ri->dsize = datalen;
+- ri->compr = comprtype;
+- ri->node_crc = crc32(0, ri, sizeof(*ri)-8);
+- ri->data_crc = crc32(0, comprbuf, cdatalen);
+-
+- fn = jffs2_write_dnode(inode, ri, comprbuf, cdatalen, phys_ofs, NULL);
+
+- jffs2_complete_reservation(c);
++ /* Set the fields that the generic jffs2_write_inode_range() code can't find */
++ ri->ino = cpu_to_je32(inode->i_ino);
++ ri->mode = cpu_to_jemode(inode->i_mode);
++ ri->uid = cpu_to_je16(inode->i_uid);
++ ri->gid = cpu_to_je16(inode->i_gid);
++ ri->isize = cpu_to_je32((uint32_t)inode->i_size);
++ ri->atime = ri->ctime = ri->mtime = cpu_to_je32(get_seconds());
++
++ /* In 2.4, it was already kmapped by generic_file_write(). Doesn't
++ hurt to do it again. The alternative is ifdefs, which are ugly. */
++ kmap(pg);
++
++ ret = jffs2_write_inode_range(c, f, ri, page_address(pg) + start,
++ (pg->index << PAGE_CACHE_SHIFT) + start,
++ end - start, &writtenlen);
+
+- if (comprtype != JFFS2_COMPR_NONE)
+- kfree(comprbuf);
++ kunmap(pg);
+
+- if (IS_ERR(fn)) {
+- ret = PTR_ERR(fn);
+- up(&f->sem);
+- SetPageError(pg);
+- break;
+- }
+- ret = jffs2_add_full_dnode_to_inode(c, f, fn);
+- if (f->metadata) {
+- jffs2_mark_node_obsolete(c, f->metadata->raw);
+- jffs2_free_full_dnode(f->metadata);
+- f->metadata = NULL;
+- }
+- up(&f->sem);
+ if (ret) {
+- /* Eep */
+- D1(printk(KERN_DEBUG "Eep. add_full_dnode_to_inode() failed in commit_write, returned %d\n", ret));
+- jffs2_mark_node_obsolete(c, fn->raw);
+- jffs2_free_full_dnode(fn);
++ /* There was an error writing. */
+ SetPageError(pg);
+- break;
+ }
+- inode->i_size = ri->isize;
++
++ if (writtenlen) {
++ if (inode->i_size < (pg->index << PAGE_CACHE_SHIFT) + start + writtenlen) {
++ inode->i_size = (pg->index << PAGE_CACHE_SHIFT) + start + writtenlen;
+ inode->i_blocks = (inode->i_size + 511) >> 9;
+- inode->i_ctime = inode->i_mtime = ri->ctime;
+- if (!datalen) {
+- printk(KERN_WARNING "Eep. We didn't actually write any bloody data\n");
+- ret = -EIO;
+- SetPageError(pg);
+- break;
++
++ inode->i_ctime = inode->i_mtime = ITIME(je32_to_cpu(ri->ctime));
+ }
+- D1(printk(KERN_DEBUG "increasing writtenlen by %d\n", datalen));
+- writtenlen += datalen;
+- file_ofs += datalen;
+- writelen -= datalen;
+ }
+
+ jffs2_free_raw_inode(ri);
+
+- if (writtenlen < end) {
++ if (start+writtenlen < end) {
+ /* generic_file_write has written more to the page cache than we've
+ actually written to the medium. Mark the page !Uptodate so that
+ it gets reread */
+@@ -545,13 +270,7 @@
+ SetPageError(pg);
+ ClearPageUptodate(pg);
+ }
+- if (writtenlen <= start) {
+- /* We didn't even get to the start of the affected part */
+- ret = ret?ret:-ENOSPC;
+- D1(printk(KERN_DEBUG "jffs2_commit_write(): Only %x bytes written to page. start (%x) not reached, returning %d\n", writtenlen, start, ret));
+- }
+- writtenlen = min(end-start, writtenlen-start);
+
+- D1(printk(KERN_DEBUG "jffs2_commit_write() returning %d. nrpages is %ld\n",writtenlen?writtenlen:ret, inode->i_mapping->nrpages));
++ D1(printk(KERN_DEBUG "jffs2_commit_write() returning %d\n",writtenlen?writtenlen:ret));
+ return writtenlen?writtenlen:ret;
+ }
+diff -Nurb linux-mips-2.4.24-pre2/fs/jffs2/fs.c linux/fs/jffs2/fs.c
+--- linux-mips-2.4.24-pre2/fs/jffs2/fs.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/fs/jffs2/fs.c 2004-11-17 18:17:59.301274096 +0100
+@@ -0,0 +1,618 @@
++/*
++ * JFFS2 -- Journalling Flash File System, Version 2.
++ *
++ * Copyright (C) 2001-2003 Red Hat, Inc.
++ *
++ * Created by David Woodhouse <dwmw2@redhat.com>
++ *
++ * For licensing information, see the file 'LICENCE' in this directory.
++ *
++ * $Id$
++ *
++ */
++
++#include <linux/version.h>
++#include <linux/config.h>
++#include <linux/kernel.h>
++#include <linux/sched.h>
++#include <linux/fs.h>
++#include <linux/list.h>
++#include <linux/mtd/mtd.h>
++#include <linux/pagemap.h>
++#include <linux/slab.h>
++#include <linux/vfs.h>
++#include <linux/crc32.h>
++#include "nodelist.h"
++
++
++static int jffs2_do_setattr (struct inode *inode, struct iattr *iattr)
++{
++ struct jffs2_full_dnode *old_metadata, *new_metadata;
++ struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
++ struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
++ struct jffs2_raw_inode *ri;
++ unsigned short dev;
++ unsigned char *mdata = NULL;
++ int mdatalen = 0;
++ unsigned int ivalid;
++ uint32_t phys_ofs, alloclen;
++ int ret;
++ D1(printk(KERN_DEBUG "jffs2_setattr(): ino #%lu\n", inode->i_ino));
++ ret = inode_change_ok(inode, iattr);
++ if (ret)
++ return ret;
++
++ /* Special cases - we don't want more than one data node
++ for these types on the medium at any time. So setattr
++ must read the original data associated with the node
++ (i.e. the device numbers or the target name) and write
++ it out again with the appropriate data attached */
++ if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode)) {
++ /* For these, we don't actually need to read the old node */
++ dev = old_encode_dev(inode->i_rdev);
++ mdata = (char *)&dev;
++ mdatalen = sizeof(dev);
++ D1(printk(KERN_DEBUG "jffs2_setattr(): Writing %d bytes of kdev_t\n", mdatalen));
++ } else if (S_ISLNK(inode->i_mode)) {
++ mdatalen = f->metadata->size;
++ mdata = kmalloc(f->metadata->size, GFP_USER);
++ if (!mdata)
++ return -ENOMEM;
++ ret = jffs2_read_dnode(c, f->metadata, mdata, 0, mdatalen);
++ if (ret) {
++ kfree(mdata);
++ return ret;
++ }
++ D1(printk(KERN_DEBUG "jffs2_setattr(): Writing %d bytes of symlink target\n", mdatalen));
++ }
++
++ ri = jffs2_alloc_raw_inode();
++ if (!ri) {
++ if (S_ISLNK(inode->i_mode))
++ kfree(mdata);
++ return -ENOMEM;
++ }
++
++ ret = jffs2_reserve_space(c, sizeof(*ri) + mdatalen, &phys_ofs, &alloclen, ALLOC_NORMAL);
++ if (ret) {
++ jffs2_free_raw_inode(ri);
++ if (S_ISLNK(inode->i_mode & S_IFMT))
++ kfree(mdata);
++ return ret;
++ }
++ down(&f->sem);
++ ivalid = iattr->ia_valid;
++
++ ri->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
++ ri->nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE);
++ ri->totlen = cpu_to_je32(sizeof(*ri) + mdatalen);
++ ri->hdr_crc = cpu_to_je32(crc32(0, ri, sizeof(struct jffs2_unknown_node)-4));
++
++ ri->ino = cpu_to_je32(inode->i_ino);
++ ri->version = cpu_to_je32(++f->highest_version);
++
++ ri->uid = cpu_to_je16((ivalid & ATTR_UID)?iattr->ia_uid:inode->i_uid);
++ ri->gid = cpu_to_je16((ivalid & ATTR_GID)?iattr->ia_gid:inode->i_gid);
++
++ if (ivalid & ATTR_MODE)
++ if (iattr->ia_mode & S_ISGID &&
++ !in_group_p(je16_to_cpu(ri->gid)) && !capable(CAP_FSETID))
++ ri->mode = cpu_to_jemode(iattr->ia_mode & ~S_ISGID);
++ else
++ ri->mode = cpu_to_jemode(iattr->ia_mode);
++ else
++ ri->mode = cpu_to_jemode(inode->i_mode);
++
++
++ ri->isize = cpu_to_je32((ivalid & ATTR_SIZE)?iattr->ia_size:inode->i_size);
++ ri->atime = cpu_to_je32(I_SEC((ivalid & ATTR_ATIME)?iattr->ia_atime:inode->i_atime));
++ ri->mtime = cpu_to_je32(I_SEC((ivalid & ATTR_MTIME)?iattr->ia_mtime:inode->i_mtime));
++ ri->ctime = cpu_to_je32(I_SEC((ivalid & ATTR_CTIME)?iattr->ia_ctime:inode->i_ctime));
++
++ ri->offset = cpu_to_je32(0);
++ ri->csize = ri->dsize = cpu_to_je32(mdatalen);
++ ri->compr = JFFS2_COMPR_NONE;
++ if (ivalid & ATTR_SIZE && inode->i_size < iattr->ia_size) {
++ /* It's an extension. Make it a hole node */
++ ri->compr = JFFS2_COMPR_ZERO;
++ ri->dsize = cpu_to_je32(iattr->ia_size - inode->i_size);
++ ri->offset = cpu_to_je32(inode->i_size);
++ }
++ ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
++ if (mdatalen)
++ ri->data_crc = cpu_to_je32(crc32(0, mdata, mdatalen));
++ else
++ ri->data_crc = cpu_to_je32(0);
++
++ new_metadata = jffs2_write_dnode(c, f, ri, mdata, mdatalen, phys_ofs, ALLOC_NORMAL);
++ if (S_ISLNK(inode->i_mode))
++ kfree(mdata);
++
++ if (IS_ERR(new_metadata)) {
++ jffs2_complete_reservation(c);
++ jffs2_free_raw_inode(ri);
++ up(&f->sem);
++ return PTR_ERR(new_metadata);
++ }
++ /* It worked. Update the inode */
++ inode->i_atime = ITIME(je32_to_cpu(ri->atime));
++ inode->i_ctime = ITIME(je32_to_cpu(ri->ctime));
++ inode->i_mtime = ITIME(je32_to_cpu(ri->mtime));
++ inode->i_mode = jemode_to_cpu(ri->mode);
++ inode->i_uid = je16_to_cpu(ri->uid);
++ inode->i_gid = je16_to_cpu(ri->gid);
++
++
++ old_metadata = f->metadata;
++
++ if (ivalid & ATTR_SIZE && inode->i_size > iattr->ia_size)
++ jffs2_truncate_fraglist (c, &f->fragtree, iattr->ia_size);
++
++ if (ivalid & ATTR_SIZE && inode->i_size < iattr->ia_size) {
++ jffs2_add_full_dnode_to_inode(c, f, new_metadata);
++ inode->i_size = iattr->ia_size;
++ f->metadata = NULL;
++ } else {
++ f->metadata = new_metadata;
++ }
++ if (old_metadata) {
++ jffs2_mark_node_obsolete(c, old_metadata->raw);
++ jffs2_free_full_dnode(old_metadata);
++ }
++ jffs2_free_raw_inode(ri);
++
++ up(&f->sem);
++ jffs2_complete_reservation(c);
++
++ /* We have to do the vmtruncate() without f->sem held, since
++ some pages may be locked and waiting for it in readpage().
++ We are protected from a simultaneous write() extending i_size
++ back past iattr->ia_size, because do_truncate() holds the
++ generic inode semaphore. */
++ if (ivalid & ATTR_SIZE && inode->i_size > iattr->ia_size)
++ vmtruncate(inode, iattr->ia_size);
++
++ return 0;
++}
++
++int jffs2_setattr(struct dentry *dentry, struct iattr *iattr)
++{
++ return jffs2_do_setattr(dentry->d_inode, iattr);
++}
++
++int jffs2_statfs(struct super_block *sb, struct kstatfs *buf)
++{
++ struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
++ unsigned long avail;
++
++ buf->f_type = JFFS2_SUPER_MAGIC;
++ buf->f_bsize = 1 << PAGE_SHIFT;
++ buf->f_blocks = c->flash_size >> PAGE_SHIFT;
++ buf->f_files = 0;
++ buf->f_ffree = 0;
++ buf->f_namelen = JFFS2_MAX_NAME_LEN;
++
++ spin_lock(&c->erase_completion_lock);
++
++ avail = c->dirty_size + c->free_size;
++ if (avail > c->sector_size * c->resv_blocks_write)
++ avail -= c->sector_size * c->resv_blocks_write;
++ else
++ avail = 0;
++
++ buf->f_bavail = buf->f_bfree = avail >> PAGE_SHIFT;
++
++ D1(jffs2_dump_block_lists(c));
++
++ spin_unlock(&c->erase_completion_lock);
++
++ return 0;
++}
++
++
++void jffs2_clear_inode (struct inode *inode)
++{
++ /* We can forget about this inode for now - drop all
++ * the nodelists associated with it, etc.
++ */
++ struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
++ struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
++
++ D1(printk(KERN_DEBUG "jffs2_clear_inode(): ino #%lu mode %o\n", inode->i_ino, inode->i_mode));
++
++ jffs2_do_clear_inode(c, f);
++}
++
++void jffs2_read_inode (struct inode *inode)
++{
++ struct jffs2_inode_info *f;
++ struct jffs2_sb_info *c;
++ struct jffs2_raw_inode latest_node;
++ int ret;
++
++ D1(printk(KERN_DEBUG "jffs2_read_inode(): inode->i_ino == %lu\n", inode->i_ino));
++
++ f = JFFS2_INODE_INFO(inode);
++ c = JFFS2_SB_INFO(inode->i_sb);
++
++ jffs2_init_inode_info(f);
++
++ ret = jffs2_do_read_inode(c, f, inode->i_ino, &latest_node);
++
++ if (ret) {
++ make_bad_inode(inode);
++ up(&f->sem);
++ return;
++ }
++ inode->i_mode = jemode_to_cpu(latest_node.mode);
++ inode->i_uid = je16_to_cpu(latest_node.uid);
++ inode->i_gid = je16_to_cpu(latest_node.gid);
++ inode->i_size = je32_to_cpu(latest_node.isize);
++ inode->i_atime = ITIME(je32_to_cpu(latest_node.atime));
++ inode->i_mtime = ITIME(je32_to_cpu(latest_node.mtime));
++ inode->i_ctime = ITIME(je32_to_cpu(latest_node.ctime));
++
++ inode->i_nlink = f->inocache->nlink;
++
++ inode->i_blksize = PAGE_SIZE;
++ inode->i_blocks = (inode->i_size + 511) >> 9;
++
++ switch (inode->i_mode & S_IFMT) {
++ jint16_t rdev;
++
++ case S_IFLNK:
++ inode->i_op = &jffs2_symlink_inode_operations;
++ break;
++
++ case S_IFDIR:
++ {
++ struct jffs2_full_dirent *fd;
++
++ for (fd=f->dents; fd; fd = fd->next) {
++ if (fd->type == DT_DIR && fd->ino)
++ inode->i_nlink++;
++ }
++ /* and '..' */
++ inode->i_nlink++;
++ /* Root dir gets i_nlink 3 for some reason */
++ if (inode->i_ino == 1)
++ inode->i_nlink++;
++
++ inode->i_op = &jffs2_dir_inode_operations;
++ inode->i_fop = &jffs2_dir_operations;
++ break;
++ }
++ case S_IFREG:
++ inode->i_op = &jffs2_file_inode_operations;
++ inode->i_fop = &jffs2_file_operations;
++ inode->i_mapping->a_ops = &jffs2_file_address_operations;
++ inode->i_mapping->nrpages = 0;
++ break;
++
++ case S_IFBLK:
++ case S_IFCHR:
++ /* Read the device numbers from the media */
++ D1(printk(KERN_DEBUG "Reading device numbers from flash\n"));
++ if (jffs2_read_dnode(c, f->metadata, (char *)&rdev, 0, sizeof(rdev)) < 0) {
++ /* Eep */
++ printk(KERN_NOTICE "Read device numbers for inode %lu failed\n", (unsigned long)inode->i_ino);
++ up(&f->sem);
++ jffs2_do_clear_inode(c, f);
++ make_bad_inode(inode);
++ return;
++ }
++
++ case S_IFSOCK:
++ case S_IFIFO:
++ inode->i_op = &jffs2_file_inode_operations;
++ init_special_inode(inode, inode->i_mode,
++ old_decode_dev((je16_to_cpu(rdev))));
++ break;
++
++ default:
++ printk(KERN_WARNING "jffs2_read_inode(): Bogus imode %o for ino %lu\n", inode->i_mode, (unsigned long)inode->i_ino);
++ }
++
++ up(&f->sem);
++
++ D1(printk(KERN_DEBUG "jffs2_read_inode() returning\n"));
++}
++
++void jffs2_dirty_inode(struct inode *inode)
++{
++ struct iattr iattr;
++
++ if (!(inode->i_state & I_DIRTY_DATASYNC)) {
++ D2(printk(KERN_DEBUG "jffs2_dirty_inode() not calling setattr() for ino #%lu\n", inode->i_ino));
++ return;
++ }
++
++ D1(printk(KERN_DEBUG "jffs2_dirty_inode() calling setattr() for ino #%lu\n", inode->i_ino));
++
++ iattr.ia_valid = ATTR_MODE|ATTR_UID|ATTR_GID|ATTR_ATIME|ATTR_MTIME|ATTR_CTIME;
++ iattr.ia_mode = inode->i_mode;
++ iattr.ia_uid = inode->i_uid;
++ iattr.ia_gid = inode->i_gid;
++ iattr.ia_atime = inode->i_atime;
++ iattr.ia_mtime = inode->i_mtime;
++ iattr.ia_ctime = inode->i_ctime;
++
++ jffs2_do_setattr(inode, &iattr);
++}
++
++int jffs2_remount_fs (struct super_block *sb, int *flags, char *data)
++{
++ struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
++
++ if (c->flags & JFFS2_SB_FLAG_RO && !(sb->s_flags & MS_RDONLY))
++ return -EROFS;
++
++ /* We stop if it was running, then restart if it needs to.
++ This also catches the case where it was stopped and this
++ is just a remount to restart it */
++ if (!(sb->s_flags & MS_RDONLY))
++ jffs2_stop_garbage_collect_thread(c);
++
++ if (!(*flags & MS_RDONLY))
++ jffs2_start_garbage_collect_thread(c);
++
++ sb->s_flags = (sb->s_flags & ~MS_RDONLY)|(*flags & MS_RDONLY);
++
++ return 0;
++}
++
++void jffs2_write_super (struct super_block *sb)
++{
++ struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
++ sb->s_dirt = 0;
++
++ if (sb->s_flags & MS_RDONLY)
++ return;
++
++ D1(printk(KERN_DEBUG "jffs2_write_super()\n"));
++ jffs2_garbage_collect_trigger(c);
++ jffs2_erase_pending_blocks(c, 0);
++ jffs2_flush_wbuf_gc(c, 0);
++}
++
++
++/* jffs2_new_inode: allocate a new inode and inocache, add it to the hash,
++ fill in the raw_inode while you're at it. */
++struct inode *jffs2_new_inode (struct inode *dir_i, int mode, struct jffs2_raw_inode *ri)
++{
++ struct inode *inode;
++ struct super_block *sb = dir_i->i_sb;
++ struct jffs2_sb_info *c;
++ struct jffs2_inode_info *f;
++ int ret;
++
++ D1(printk(KERN_DEBUG "jffs2_new_inode(): dir_i %ld, mode 0x%x\n", dir_i->i_ino, mode));
++
++ c = JFFS2_SB_INFO(sb);
++
++ inode = new_inode(sb);
++
++ if (!inode)
++ return ERR_PTR(-ENOMEM);
++
++ f = JFFS2_INODE_INFO(inode);
++ jffs2_init_inode_info(f);
++
++ memset(ri, 0, sizeof(*ri));
++ /* Set OS-specific defaults for new inodes */
++ ri->uid = cpu_to_je16(current->fsuid);
++
++ if (dir_i->i_mode & S_ISGID) {
++ ri->gid = cpu_to_je16(dir_i->i_gid);
++ if (S_ISDIR(mode))
++ mode |= S_ISGID;
++ } else {
++ ri->gid = cpu_to_je16(current->fsgid);
++ }
++ ri->mode = cpu_to_jemode(mode);
++ ret = jffs2_do_new_inode (c, f, mode, ri);
++ if (ret) {
++ make_bad_inode(inode);
++ iput(inode);
++ return ERR_PTR(ret);
++ }
++ inode->i_nlink = 1;
++ inode->i_ino = je32_to_cpu(ri->ino);
++ inode->i_mode = jemode_to_cpu(ri->mode);
++ inode->i_gid = je16_to_cpu(ri->gid);
++ inode->i_uid = je16_to_cpu(ri->uid);
++ inode->i_atime = inode->i_ctime = inode->i_mtime = CURRENT_TIME;
++ ri->atime = ri->mtime = ri->ctime = cpu_to_je32(I_SEC(inode->i_mtime));
++
++ inode->i_blksize = PAGE_SIZE;
++ inode->i_blocks = 0;
++ inode->i_size = 0;
++
++ insert_inode_hash(inode);
++
++ return inode;
++}
++
++
++int jffs2_do_fill_super(struct super_block *sb, void *data, int silent)
++{
++ struct jffs2_sb_info *c;
++ struct inode *root_i;
++ int ret;
++ size_t blocks;
++
++ c = JFFS2_SB_INFO(sb);
++
++ c->flash_size = c->mtd->size;
++
++ /*
++ * Check, if we have to concatenate physical blocks to larger virtual blocks
++ * to reduce the memorysize for c->blocks. (kmalloc allows max. 128K allocation)
++ */
++ blocks = c->flash_size / c->mtd->erasesize;
++ while ((blocks * sizeof (struct jffs2_eraseblock)) > (128 * 1024))
++ blocks >>= 1;
++
++ c->sector_size = c->flash_size / blocks;
++ if (c->sector_size != c->mtd->erasesize)
++ printk(KERN_INFO "jffs2: Erase block size too small (%dKiB). Using virtual blocks size (%dKiB) instead\n",
++ c->mtd->erasesize / 1024, c->sector_size / 1024);
++
++ if (c->flash_size < 5*c->sector_size) {
++ printk(KERN_ERR "jffs2: Too few erase blocks (%d)\n", c->flash_size / c->sector_size);
++ return -EINVAL;
++ }
++
++ c->cleanmarker_size = sizeof(struct jffs2_unknown_node);
++ /* Joern -- stick alignment for weird 8-byte-page flash here */
++
++ if (jffs2_cleanmarker_oob(c)) {
++ /* NAND (or other bizarre) flash... do setup accordingly */
++ ret = jffs2_nand_flash_setup(c);
++ if (ret)
++ return ret;
++ }
++
++ c->inocache_list = kmalloc(INOCACHE_HASHSIZE * sizeof(struct jffs2_inode_cache *), GFP_KERNEL);
++ if (!c->inocache_list) {
++ ret = -ENOMEM;
++ goto out_wbuf;
++ }
++ memset(c->inocache_list, 0, INOCACHE_HASHSIZE * sizeof(struct jffs2_inode_cache *));
++
++ if ((ret = jffs2_do_mount_fs(c)))
++ goto out_inohash;
++
++ ret = -EINVAL;
++
++ D1(printk(KERN_DEBUG "jffs2_do_fill_super(): Getting root inode\n"));
++ root_i = iget(sb, 1);
++ if (is_bad_inode(root_i)) {
++ D1(printk(KERN_WARNING "get root inode failed\n"));
++ goto out_nodes;
++ }
++
++ D1(printk(KERN_DEBUG "jffs2_do_fill_super(): d_alloc_root()\n"));
++ sb->s_root = d_alloc_root(root_i);
++ if (!sb->s_root)
++ goto out_root_i;
++
++#if LINUX_VERSION_CODE >= 0x20403
++ sb->s_maxbytes = 0xFFFFFFFF;
++#endif
++ sb->s_blocksize = PAGE_CACHE_SIZE;
++ sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
++ sb->s_magic = JFFS2_SUPER_MAGIC;
++ if (!(sb->s_flags & MS_RDONLY))
++ jffs2_start_garbage_collect_thread(c);
++ return 0;
++
++ out_root_i:
++ iput(root_i);
++ out_nodes:
++ jffs2_free_ino_caches(c);
++ jffs2_free_raw_node_refs(c);
++ kfree(c->blocks);
++ out_inohash:
++ kfree(c->inocache_list);
++ out_wbuf:
++ jffs2_nand_flash_cleanup(c);
++
++ return ret;
++}
++
++void jffs2_gc_release_inode(struct jffs2_sb_info *c,
++ struct jffs2_inode_info *f)
++{
++ iput(OFNI_EDONI_2SFFJ(f));
++}
++
++struct jffs2_inode_info *jffs2_gc_fetch_inode(struct jffs2_sb_info *c,
++ int inum, int nlink)
++{
++ struct inode *inode;
++ struct jffs2_inode_cache *ic;
++ if (!nlink) {
++ /* The inode has zero nlink but its nodes weren't yet marked
++ obsolete. This has to be because we're still waiting for
++ the final (close() and) iput() to happen.
++
++ There's a possibility that the final iput() could have
++ happened while we were contemplating. In order to ensure
++ that we don't cause a new read_inode() (which would fail)
++ for the inode in question, we use ilookup() in this case
++ instead of iget().
++
++ The nlink can't _become_ zero at this point because we're
++ holding the alloc_sem, and jffs2_do_unlink() would also
++ need that while decrementing nlink on any inode.
++ */
++ inode = ilookup(OFNI_BS_2SFFJ(c), inum);
++ if (!inode) {
++ D1(printk(KERN_DEBUG "ilookup() failed for ino #%u; inode is probably deleted.\n",
++ inum));
++
++ spin_lock(&c->inocache_lock);
++ ic = jffs2_get_ino_cache(c, inum);
++ if (!ic) {
++ D1(printk(KERN_DEBUG "Inode cache for ino #%u is gone.\n", inum));
++ spin_unlock(&c->inocache_lock);
++ return NULL;
++ }
++ if (ic->state != INO_STATE_CHECKEDABSENT) {
++ /* Wait for progress. Don't just loop */
++ D1(printk(KERN_DEBUG "Waiting for ino #%u in state %d\n",
++ ic->ino, ic->state));
++ sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock);
++ } else {
++ spin_unlock(&c->inocache_lock);
++ }
++
++ return NULL;
++ }
++ } else {
++ /* Inode has links to it still; they're not going away because
++ jffs2_do_unlink() would need the alloc_sem and we have it.
++ Just iget() it, and if read_inode() is necessary that's OK.
++ */
++ inode = iget(OFNI_BS_2SFFJ(c), inum);
++ if (!inode)
++ return ERR_PTR(-ENOMEM);
++ }
++ if (is_bad_inode(inode)) {
++ printk(KERN_NOTICE "Eep. read_inode() failed for ino #%u. nlink %d\n",
++ inum, nlink);
++ /* NB. This will happen again. We need to do something appropriate here. */
++ iput(inode);
++ return ERR_PTR(-EIO);
++ }
++
++ return JFFS2_INODE_INFO(inode);
++}
++
++unsigned char *jffs2_gc_fetch_page(struct jffs2_sb_info *c,
++ struct jffs2_inode_info *f,
++ unsigned long offset,
++ unsigned long *priv)
++{
++ struct inode *inode = OFNI_EDONI_2SFFJ(f);
++ struct page *pg;
++
++ pg = read_cache_page(inode->i_mapping, offset >> PAGE_CACHE_SHIFT,
++ (void *)jffs2_do_readpage_unlock, inode);
++ if (IS_ERR(pg))
++ return (void *)pg;
++
++ *priv = (unsigned long)pg;
++ return kmap(pg);
++}
++
++void jffs2_gc_release_page(struct jffs2_sb_info *c,
++ unsigned char *ptr,
++ unsigned long *priv)
++{
++ struct page *pg = (void *)*priv;
++
++ kunmap(pg);
++ page_cache_release(pg);
++}
+diff -Nurb linux-mips-2.4.24-pre2/fs/jffs2/gc.c linux/fs/jffs2/gc.c
+--- linux-mips-2.4.24-pre2/fs/jffs2/gc.c 2004-11-17 18:05:04.000000000 +0100
++++ linux/fs/jffs2/gc.c 2004-11-17 18:17:59.000000000 +0100
+@@ -1,76 +1,67 @@
+ /*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+- * Copyright (C) 2001 Red Hat, Inc.
++ * Copyright (C) 2001-2003 Red Hat, Inc.
+ *
+- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
++ * Created by David Woodhouse <dwmw2@redhat.com>
+ *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
++ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+- * The contents of this file are subject to the Red Hat eCos Public
+- * License Version 1.1 (the "Licence"); you may not use this file
+- * except in compliance with the Licence. You may obtain a copy of
+- * the Licence at http://www.redhat.com/
+- *
+- * Software distributed under the Licence is distributed on an "AS IS"
+- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+- * See the Licence for the specific language governing rights and
+- * limitations under the Licence.
+- *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
+- *
+- * Alternatively, the contents of this file may be used under the
+- * terms of the GNU General Public License version 2 (the "GPL"), in
+- * which case the provisions of the GPL are applicable instead of the
+- * above. If you wish to allow the use of your version of this file
+- * only under the terms of the GPL and not to allow others to use your
+- * version of this file under the RHEPL, indicate your decision by
+- * deleting the provisions above and replace them with the notice and
+- * other provisions required by the GPL. If you do not delete the
+- * provisions above, a recipient may use your version of this file
+- * under either the RHEPL or the GPL.
+- *
+- * $Id$
++ * $Id$
+ *
+ */
+
+ #include <linux/kernel.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/slab.h>
+-#include <linux/jffs2.h>
+-#include <linux/sched.h>
+-#include <linux/interrupt.h>
+ #include <linux/pagemap.h>
+-#include "nodelist.h"
+ #include <linux/crc32.h>
++#include <linux/compiler.h>
++#include <linux/stat.h>
++#include "nodelist.h"
+
++static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c,
++ struct jffs2_inode_cache *ic,
++ struct jffs2_raw_node_ref *raw);
+ static int jffs2_garbage_collect_metadata(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
+- struct inode *inode, struct jffs2_full_dnode *fd);
++ struct jffs2_inode_info *f, struct jffs2_full_dnode *fd);
+ static int jffs2_garbage_collect_dirent(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
+- struct inode *inode, struct jffs2_full_dirent *fd);
++ struct jffs2_inode_info *f, struct jffs2_full_dirent *fd);
+ static int jffs2_garbage_collect_deletion_dirent(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
+- struct inode *inode, struct jffs2_full_dirent *fd);
++ struct jffs2_inode_info *f, struct jffs2_full_dirent *fd);
+ static int jffs2_garbage_collect_hole(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
+- struct inode *indeo, struct jffs2_full_dnode *fn,
+- __u32 start, __u32 end);
++ struct jffs2_inode_info *f, struct jffs2_full_dnode *fn,
++ uint32_t start, uint32_t end);
+ static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
+- struct inode *inode, struct jffs2_full_dnode *fn,
+- __u32 start, __u32 end);
++ struct jffs2_inode_info *f, struct jffs2_full_dnode *fn,
++ uint32_t start, uint32_t end);
++static int jffs2_garbage_collect_live(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
++ struct jffs2_raw_node_ref *raw, struct jffs2_inode_info *f);
+
+ /* Called with erase_completion_lock held */
+ static struct jffs2_eraseblock *jffs2_find_gc_block(struct jffs2_sb_info *c)
+ {
+ struct jffs2_eraseblock *ret;
+ struct list_head *nextlist = NULL;
++ int n = jiffies % 128;
+
+ /* Pick an eraseblock to garbage collect next. This is where we'll
+ put the clever wear-levelling algorithms. Eventually. */
+- if (!list_empty(&c->bad_used_list) && c->nr_free_blocks > JFFS2_RESERVED_BLOCKS_GCBAD) {
++ /* We possibly want to favour the dirtier blocks more when the
++ number of free blocks is low. */
++ if (!list_empty(&c->bad_used_list) && c->nr_free_blocks > c->resv_blocks_gcbad) {
+ D1(printk(KERN_DEBUG "Picking block from bad_used_list to GC next\n"));
+ nextlist = &c->bad_used_list;
+- } else if (jiffies % 100 && !list_empty(&c->dirty_list)) {
+- /* Most of the time, pick one off the dirty list */
++ } else if (n < 50 && !list_empty(&c->erasable_list)) {
++ /* Note that most of them will have gone directly to be erased.
++ So don't favour the erasable_list _too_ much. */
++ D1(printk(KERN_DEBUG "Picking block from erasable_list to GC next\n"));
++ nextlist = &c->erasable_list;
++ } else if (n < 110 && !list_empty(&c->very_dirty_list)) {
++ /* Most of the time, pick one off the very_dirty list */
++ D1(printk(KERN_DEBUG "Picking block from very_dirty_list to GC next\n"));
++ nextlist = &c->very_dirty_list;
++ } else if (n < 126 && !list_empty(&c->dirty_list)) {
+ D1(printk(KERN_DEBUG "Picking block from dirty_list to GC next\n"));
+ nextlist = &c->dirty_list;
+ } else if (!list_empty(&c->clean_list)) {
+@@ -80,9 +71,16 @@
+ D1(printk(KERN_DEBUG "Picking block from dirty_list to GC next (clean_list was empty)\n"));
+
+ nextlist = &c->dirty_list;
++ } else if (!list_empty(&c->very_dirty_list)) {
++ D1(printk(KERN_DEBUG "Picking block from very_dirty_list to GC next (clean_list and dirty_list were empty)\n"));
++ nextlist = &c->very_dirty_list;
++ } else if (!list_empty(&c->erasable_list)) {
++ D1(printk(KERN_DEBUG "Picking block from erasable_list to GC next (clean_list and {very_,}dirty_list were empty)\n"));
++
++ nextlist = &c->erasable_list;
+ } else {
+- /* Eep. Both were empty */
+- printk(KERN_NOTICE "jffs2: No clean _or_ dirty blocks to GC from! Where are they all?\n");
++ /* Eep. All were empty */
++ printk(KERN_NOTICE "jffs2: No clean, dirty _or_ erasable blocks to GC from! Where are they all?\n");
+ return NULL;
+ }
+
+@@ -94,6 +92,17 @@
+ printk(KERN_WARNING "Eep. ret->gc_node for block at 0x%08x is NULL\n", ret->offset);
+ BUG();
+ }
++
++ /* Have we accidentally picked a clean block with wasted space ? */
++ if (ret->wasted_size) {
++ D1(printk(KERN_DEBUG "Converting wasted_size %08x to dirty_size\n", ret->wasted_size));
++ ret->dirty_size += ret->wasted_size;
++ c->wasted_size -= ret->wasted_size;
++ c->dirty_size += ret->wasted_size;
++ ret->wasted_size = 0;
++ }
++
++ D1(jffs2_dump_block_lists(c));
+ return ret;
+ }
+
+@@ -103,21 +112,90 @@
+ */
+ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
+ {
+- struct jffs2_eraseblock *jeb;
+ struct jffs2_inode_info *f;
+- struct jffs2_raw_node_ref *raw;
+- struct jffs2_node_frag *frag;
+- struct jffs2_full_dnode *fn = NULL;
+- struct jffs2_full_dirent *fd;
+ struct jffs2_inode_cache *ic;
+- __u32 start = 0, end = 0, nrfrags = 0;
+- struct inode *inode;
+- int ret = 0;
++ struct jffs2_eraseblock *jeb;
++ struct jffs2_raw_node_ref *raw;
++ int ret = 0, inum, nlink;
+
+ if (down_interruptible(&c->alloc_sem))
+ return -EINTR;
+
+- spin_lock_bh(&c->erase_completion_lock);
++ for (;;) {
++ spin_lock(&c->erase_completion_lock);
++ if (!c->unchecked_size)
++ break;
++
++ /* We can't start doing GC yet. We haven't finished checking
++ the node CRCs etc. Do it now. */
++
++ /* checked_ino is protected by the alloc_sem */
++ if (c->checked_ino > c->highest_ino) {
++ printk(KERN_CRIT "Checked all inodes but still 0x%x bytes of unchecked space?\n",
++ c->unchecked_size);
++ D1(jffs2_dump_block_lists(c));
++ spin_unlock(&c->erase_completion_lock);
++ BUG();
++ }
++
++ spin_unlock(&c->erase_completion_lock);
++
++ spin_lock(&c->inocache_lock);
++
++ ic = jffs2_get_ino_cache(c, c->checked_ino++);
++
++ if (!ic) {
++ spin_unlock(&c->inocache_lock);
++ continue;
++ }
++
++ if (!ic->nlink) {
++ D1(printk(KERN_DEBUG "Skipping check of ino #%d with nlink zero\n",
++ ic->ino));
++ spin_unlock(&c->inocache_lock);
++ continue;
++ }
++ switch(ic->state) {
++ case INO_STATE_CHECKEDABSENT:
++ case INO_STATE_PRESENT:
++ D1(printk(KERN_DEBUG "Skipping ino #%u already checked\n", ic->ino));
++ spin_unlock(&c->inocache_lock);
++ continue;
++
++ case INO_STATE_GC:
++ case INO_STATE_CHECKING:
++ printk(KERN_WARNING "Inode #%u is in state %d during CRC check phase!\n", ic->ino, ic->state);
++ spin_unlock(&c->inocache_lock);
++ BUG();
++
++ case INO_STATE_READING:
++ /* We need to wait for it to finish, lest we move on
++ and trigger the BUG() above while we haven't yet
++ finished checking all its nodes */
++ D1(printk(KERN_DEBUG "Waiting for ino #%u to finish reading\n", ic->ino));
++ up(&c->alloc_sem);
++ sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock);
++ return 0;
++
++ default:
++ BUG();
++
++ case INO_STATE_UNCHECKED:
++ ;
++ }
++ ic->state = INO_STATE_CHECKING;
++ spin_unlock(&c->inocache_lock);
++
++ D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass() triggering inode scan of ino#%u\n", ic->ino));
++
++ ret = jffs2_do_crccheck_inode(c, ic);
++ if (ret)
++ printk(KERN_WARNING "Returned error for crccheck of ino #%u. Expect badness...\n", ic->ino);
++
++ jffs2_set_inocache_state(c, ic, INO_STATE_CHECKEDABSENT);
++ up(&c->alloc_sem);
++ return ret;
++ }
+
+ /* First, work out which block we're garbage-collecting */
+ jeb = c->gcblock;
+@@ -127,12 +205,14 @@
+
+ if (!jeb) {
+ printk(KERN_NOTICE "jffs2: Couldn't find erase block to garbage collect!\n");
+- spin_unlock_bh(&c->erase_completion_lock);
++ spin_unlock(&c->erase_completion_lock);
+ up(&c->alloc_sem);
+ return -EIO;
+ }
+
+- D1(printk(KERN_DEBUG "garbage collect from block at phys 0x%08x\n", jeb->offset));
++ D1(printk(KERN_DEBUG "GC from block %08x, used_size %08x, dirty_size %08x, free_size %08x\n", jeb->offset, jeb->used_size, jeb->dirty_size, jeb->free_size));
++ D1(if (c->nextblock)
++ printk(KERN_DEBUG "Nextblock at %08x, used_size %08x, dirty_size %08x, wasted_size %08x, free_size %08x\n", c->nextblock->offset, c->nextblock->used_size, c->nextblock->dirty_size, c->nextblock->wasted_size, c->nextblock->free_size));
+
+ if (!jeb->used_size) {
+ up(&c->alloc_sem);
+@@ -141,92 +221,211 @@
+
+ raw = jeb->gc_node;
+
+- while(raw->flash_offset & 1) {
+- D1(printk(KERN_DEBUG "Node at 0x%08x is obsolete... skipping\n", raw->flash_offset &~3));
+- jeb->gc_node = raw = raw->next_phys;
+- if (!raw) {
++ while(ref_obsolete(raw)) {
++ D1(printk(KERN_DEBUG "Node at 0x%08x is obsolete... skipping\n", ref_offset(raw)));
++ raw = raw->next_phys;
++ if (unlikely(!raw)) {
+ printk(KERN_WARNING "eep. End of raw list while still supposedly nodes to GC\n");
+ printk(KERN_WARNING "erase block at 0x%08x. free_size 0x%08x, dirty_size 0x%08x, used_size 0x%08x\n",
+ jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size);
+- spin_unlock_bh(&c->erase_completion_lock);
++ jeb->gc_node = raw;
++ spin_unlock(&c->erase_completion_lock);
+ up(&c->alloc_sem);
+ BUG();
+ }
+ }
+- D1(printk(KERN_DEBUG "Going to garbage collect node at 0x%08x\n", raw->flash_offset &~3));
++ jeb->gc_node = raw;
++
++ D1(printk(KERN_DEBUG "Going to garbage collect node at 0x%08x\n", ref_offset(raw)));
++
+ if (!raw->next_in_ino) {
+ /* Inode-less node. Clean marker, snapshot or something like that */
+- spin_unlock_bh(&c->erase_completion_lock);
++ /* FIXME: If it's something that needs to be copied, including something
++ we don't grok that has JFFS2_NODETYPE_RWCOMPAT_COPY, we should do so */
++ spin_unlock(&c->erase_completion_lock);
+ jffs2_mark_node_obsolete(c, raw);
+ up(&c->alloc_sem);
+ goto eraseit_lock;
+ }
+
+ ic = jffs2_raw_ref_to_ic(raw);
+- D1(printk(KERN_DEBUG "Inode number is #%u\n", ic->ino));
+-
+- spin_unlock_bh(&c->erase_completion_lock);
+
+- D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass collecting from block @0x%08x. Node @0x%08x, ino #%u\n", jeb->offset, raw->flash_offset&~3, ic->ino));
+- if (!ic->nlink) {
+- /* The inode has zero nlink but its nodes weren't yet marked
+- obsolete. This has to be because we're still waiting for
+- the final (close() and) iput() to happen.
+-
+- There's a possibility that the final iput() could have
+- happened while we were contemplating. In order to ensure
+- that we don't cause a new read_inode() (which would fail)
+- for the inode in question, we use ilookup() in this case
+- instead of iget().
+-
+- The nlink can't _become_ zero at this point because we're
+- holding the alloc_sem, and jffs2_do_unlink() would also
+- need that while decrementing nlink on any inode.
++ /* We need to hold the inocache. Either the erase_completion_lock or
++ the inocache_lock are sufficient; we trade down since the inocache_lock
++ causes less contention. */
++ spin_lock(&c->inocache_lock);
++
++ spin_unlock(&c->erase_completion_lock);
++
++ D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass collecting from block @0x%08x. Node @0x%08x(%d), ino #%u\n", jeb->offset, ref_offset(raw), ref_flags(raw), ic->ino));
++
++ /* Three possibilities:
++ 1. Inode is already in-core. We must iget it and do proper
++ updating to its fragtree, etc.
++ 2. Inode is not in-core, node is REF_PRISTINE. We lock the
++ inocache to prevent a read_inode(), copy the node intact.
++ 3. Inode is not in-core, node is not pristine. We must iget()
++ and take the slow path.
+ */
+- inode = ilookup(OFNI_BS_2SFFJ(c), ic->ino);
+- if (!inode) {
+- D1(printk(KERN_DEBUG "ilookup() failed for ino #%u; inode is probably deleted.\n",
++
++ switch(ic->state) {
++ case INO_STATE_CHECKEDABSENT:
++ /* It's been checked, but it's not currently in-core.
++ We can just copy any pristine nodes, but have
++ to prevent anyone else from doing read_inode() while
++ we're at it, so we set the state accordingly */
++ if (ref_flags(raw) == REF_PRISTINE)
++ ic->state = INO_STATE_GC;
++ else {
++ D1(printk(KERN_DEBUG "Ino #%u is absent but node not REF_PRISTINE. Reading.\n",
+ ic->ino));
+- up(&c->alloc_sem);
+- return 0;
+ }
+- } else {
+- /* Inode has links to it still; they're not going away because
+- jffs2_do_unlink() would need the alloc_sem and we have it.
+- Just iget() it, and if read_inode() is necessary that's OK.
++ break;
++
++ case INO_STATE_PRESENT:
++ /* It's in-core. GC must iget() it. */
++ break;
++
++ case INO_STATE_UNCHECKED:
++ case INO_STATE_CHECKING:
++ case INO_STATE_GC:
++ /* Should never happen. We should have finished checking
++ by the time we actually start doing any GC, and since
++ we're holding the alloc_sem, no other garbage collection
++ can happen.
+ */
+- inode = iget(OFNI_BS_2SFFJ(c), ic->ino);
+- if (!inode) {
++ printk(KERN_CRIT "Inode #%u already in state %d in jffs2_garbage_collect_pass()!\n",
++ ic->ino, ic->state);
+ up(&c->alloc_sem);
+- return -ENOMEM;
++ spin_unlock(&c->inocache_lock);
++ BUG();
++
++ case INO_STATE_READING:
++ /* Someone's currently trying to read it. We must wait for
++ them to finish and then go through the full iget() route
++ to do the GC. However, sometimes read_inode() needs to get
++ the alloc_sem() (for marking nodes invalid) so we must
++ drop the alloc_sem before sleeping. */
++
++ up(&c->alloc_sem);
++ D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass() waiting for ino #%u in state %d\n",
++ ic->ino, ic->state));
++ sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock);
++ /* And because we dropped the alloc_sem we must start again from the
++ beginning. Ponder chance of livelock here -- we're returning success
++ without actually making any progress.
++
++ Q: What are the chances that the inode is back in INO_STATE_READING
++ again by the time we next enter this function? And that this happens
++ enough times to cause a real delay?
++
++ A: Small enough that I don't care :)
++ */
++ return 0;
+ }
++
++ /* OK. Now if the inode is in state INO_STATE_GC, we are going to copy the
++ node intact, and we don't have to muck about with the fragtree etc.
++ because we know it's not in-core. If it _was_ in-core, we go through
++ all the iget() crap anyway */
++
++ if (ic->state == INO_STATE_GC) {
++ spin_unlock(&c->inocache_lock);
++
++ ret = jffs2_garbage_collect_pristine(c, ic, raw);
++
++ spin_lock(&c->inocache_lock);
++ ic->state = INO_STATE_CHECKEDABSENT;
++ wake_up(&c->inocache_wq);
++
++ if (ret != -EBADFD) {
++ spin_unlock(&c->inocache_lock);
++ goto release_sem;
+ }
+- if (is_bad_inode(inode)) {
+- printk(KERN_NOTICE "Eep. read_inode() failed for ino #%u\n", ic->ino);
+- /* NB. This will happen again. We need to do something appropriate here. */
++
++ /* Fall through if it wanted us to, with inocache_lock held */
++ }
++
++ /* Prevent the fairly unlikely race where the gcblock is
++ entirely obsoleted by the final close of a file which had
++ the only valid nodes in the block, followed by erasure,
++ followed by freeing of the ic because the erased block(s)
++ held _all_ the nodes of that inode.... never been seen but
++ it's vaguely possible. */
++
++ inum = ic->ino;
++ nlink = ic->nlink;
++ spin_unlock(&c->inocache_lock);
++
++ f = jffs2_gc_fetch_inode(c, inum, nlink);
++ if (IS_ERR(f))
++ return PTR_ERR(f);
++ if (!f)
++ return 0;
++
++ ret = jffs2_garbage_collect_live(c, jeb, raw, f);
++
++ jffs2_gc_release_inode(c, f);
++
++ release_sem:
+ up(&c->alloc_sem);
+- iput(inode);
+- return -EIO;
++
++ eraseit_lock:
++ /* If we've finished this block, start it erasing */
++ spin_lock(&c->erase_completion_lock);
++
++ eraseit:
++ if (c->gcblock && !c->gcblock->used_size) {
++ D1(printk(KERN_DEBUG "Block at 0x%08x completely obsoleted by GC. Moving to erase_pending_list\n", c->gcblock->offset));
++ /* We're GC'ing an empty block? */
++ list_add_tail(&c->gcblock->list, &c->erase_pending_list);
++ c->gcblock = NULL;
++ c->nr_erasing_blocks++;
++ jffs2_erase_pending_trigger(c);
+ }
++ spin_unlock(&c->erase_completion_lock);
++
++ return ret;
++}
++
++static int jffs2_garbage_collect_live(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
++ struct jffs2_raw_node_ref *raw, struct jffs2_inode_info *f)
++{
++ struct jffs2_node_frag *frag;
++ struct jffs2_full_dnode *fn = NULL;
++ struct jffs2_full_dirent *fd;
++ uint32_t start = 0, end = 0, nrfrags = 0;
++ int ret = 0;
+
+- f = JFFS2_INODE_INFO(inode);
+ down(&f->sem);
++
+ /* Now we have the lock for this inode. Check that it's still the one at the head
+ of the list. */
+
+- if (raw->flash_offset & 1) {
++ spin_lock(&c->erase_completion_lock);
++
++ if (c->gcblock != jeb) {
++ spin_unlock(&c->erase_completion_lock);
++ D1(printk(KERN_DEBUG "GC block is no longer gcblock. Restart\n"));
++ goto upnout;
++ }
++ if (ref_obsolete(raw)) {
++ spin_unlock(&c->erase_completion_lock);
+ D1(printk(KERN_DEBUG "node to be GC'd was obsoleted in the meantime.\n"));
+ /* They'll call again */
+ goto upnout;
+ }
++ spin_unlock(&c->erase_completion_lock);
++
+ /* OK. Looks safe. And nobody can get us now because we have the semaphore. Move the block */
+ if (f->metadata && f->metadata->raw == raw) {
+ fn = f->metadata;
+- ret = jffs2_garbage_collect_metadata(c, jeb, inode, fn);
++ ret = jffs2_garbage_collect_metadata(c, jeb, f, fn);
+ goto upnout;
+ }
+
+- for (frag = f->fraglist; frag; frag = frag->next) {
++ /* FIXME. Read node and do lookup? */
++ for (frag = frag_first(&f->fragtree); frag; frag = frag_next(frag)) {
+ if (frag->node && frag->node->raw == raw) {
+ fn = frag->node;
+ end = frag->ofs + frag->size;
+@@ -237,13 +436,22 @@
+ }
+ }
+ if (fn) {
++ if (ref_flags(raw) == REF_PRISTINE) {
++ ret = jffs2_garbage_collect_pristine(c, f->inocache, raw);
++ if (!ret) {
++ /* Urgh. Return it sensibly. */
++ frag->node->raw = f->inocache->nodes;
++ }
++ if (ret != -EBADFD)
++ goto upnout;
++ }
+ /* We found a datanode. Do the GC */
+ if((start >> PAGE_CACHE_SHIFT) < ((end-1) >> PAGE_CACHE_SHIFT)) {
+ /* It crosses a page boundary. Therefore, it must be a hole. */
+- ret = jffs2_garbage_collect_hole(c, jeb, inode, fn, start, end);
++ ret = jffs2_garbage_collect_hole(c, jeb, f, fn, start, end);
+ } else {
+ /* It could still be a hole. But we GC the page this way anyway */
+- ret = jffs2_garbage_collect_dnode(c, jeb, inode, fn, start, end);
++ ret = jffs2_garbage_collect_dnode(c, jeb, f, fn, start, end);
+ }
+ goto upnout;
+ }
+@@ -255,12 +463,13 @@
+ }
+
+ if (fd && fd->ino) {
+- ret = jffs2_garbage_collect_dirent(c, jeb, inode, fd);
++ ret = jffs2_garbage_collect_dirent(c, jeb, f, fd);
+ } else if (fd) {
+- ret = jffs2_garbage_collect_deletion_dirent(c, jeb, inode, fd);
++ ret = jffs2_garbage_collect_deletion_dirent(c, jeb, f, fd);
+ } else {
+- printk(KERN_WARNING "Raw node at 0x%08x wasn't in node lists for ino #%lu\n", raw->flash_offset&~3, inode->i_ino);
+- if (raw->flash_offset & 1) {
++ printk(KERN_WARNING "Raw node at 0x%08x wasn't in node lists for ino #%u\n",
++ ref_offset(raw), f->inocache->ino);
++ if (ref_obsolete(raw)) {
+ printk(KERN_WARNING "But it's obsolete so we don't mind too much\n");
+ } else {
+ ret = -EIO;
+@@ -268,46 +477,197 @@
+ }
+ upnout:
+ up(&f->sem);
+- up(&c->alloc_sem);
+- iput(inode);
+
+- eraseit_lock:
+- /* If we've finished this block, start it erasing */
+- spin_lock_bh(&c->erase_completion_lock);
++ return ret;
++}
+
+- eraseit:
+- if (c->gcblock && !c->gcblock->used_size) {
+- D1(printk(KERN_DEBUG "Block at 0x%08x completely obsoleted by GC. Moving to erase_pending_list\n", c->gcblock->offset));
+- /* We're GC'ing an empty block? */
+- list_add_tail(&c->gcblock->list, &c->erase_pending_list);
+- c->gcblock = NULL;
+- c->nr_erasing_blocks++;
+- jffs2_erase_pending_trigger(c);
++static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c,
++ struct jffs2_inode_cache *ic,
++ struct jffs2_raw_node_ref *raw)
++{
++ union jffs2_node_union *node;
++ struct jffs2_raw_node_ref *nraw;
++ size_t retlen;
++ int ret;
++ uint32_t phys_ofs, alloclen;
++ uint32_t crc, rawlen;
++ int retried = 0;
++
++ D1(printk(KERN_DEBUG "Going to GC REF_PRISTINE node at 0x%08x\n", ref_offset(raw)));
++
++ rawlen = ref_totlen(c, c->gcblock, raw);
++
++ /* Ask for a small amount of space (or the totlen if smaller) because we
++ don't want to force wastage of the end of a block if splitting would
++ work. */
++ ret = jffs2_reserve_space_gc(c, min_t(uint32_t, sizeof(struct jffs2_raw_inode) + JFFS2_MIN_DATA_LEN,
++ rawlen), &phys_ofs, &alloclen);
++ if (ret)
++ return ret;
++
++ if (alloclen < rawlen) {
++ /* Doesn't fit untouched. We'll go the old route and split it */
++ return -EBADFD;
++ }
++
++ node = kmalloc(rawlen, GFP_KERNEL);
++ if (!node)
++ return -ENOMEM;
++
++ ret = jffs2_flash_read(c, ref_offset(raw), rawlen, &retlen, (char *)node);
++ if (!ret && retlen != rawlen)
++ ret = -EIO;
++ if (ret)
++ goto out_node;
++
++ crc = crc32(0, node, sizeof(struct jffs2_unknown_node)-4);
++ if (je32_to_cpu(node->u.hdr_crc) != crc) {
++ printk(KERN_WARNING "Header CRC failed on REF_PRISTINE node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
++ ref_offset(raw), je32_to_cpu(node->u.hdr_crc), crc);
++ goto bail;
++ }
++
++ switch(je16_to_cpu(node->u.nodetype)) {
++ case JFFS2_NODETYPE_INODE:
++ crc = crc32(0, node, sizeof(node->i)-8);
++ if (je32_to_cpu(node->i.node_crc) != crc) {
++ printk(KERN_WARNING "Node CRC failed on REF_PRISTINE data node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
++ ref_offset(raw), je32_to_cpu(node->i.node_crc), crc);
++ goto bail;
++ }
++
++ if (je32_to_cpu(node->i.dsize)) {
++ crc = crc32(0, node->i.data, je32_to_cpu(node->i.csize));
++ if (je32_to_cpu(node->i.data_crc) != crc) {
++ printk(KERN_WARNING "Data CRC failed on REF_PRISTINE data node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
++ ref_offset(raw), je32_to_cpu(node->i.data_crc), crc);
++ goto bail;
+ }
+- spin_unlock_bh(&c->erase_completion_lock);
++ }
++ break;
++
++ case JFFS2_NODETYPE_DIRENT:
++ crc = crc32(0, node, sizeof(node->d)-8);
++ if (je32_to_cpu(node->d.node_crc) != crc) {
++ printk(KERN_WARNING "Node CRC failed on REF_PRISTINE dirent node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
++ ref_offset(raw), je32_to_cpu(node->d.node_crc), crc);
++ goto bail;
++ }
++
++ if (node->d.nsize) {
++ crc = crc32(0, node->d.name, node->d.nsize);
++ if (je32_to_cpu(node->d.name_crc) != crc) {
++ printk(KERN_WARNING "Name CRC failed on REF_PRISTINE dirent ode at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
++ ref_offset(raw), je32_to_cpu(node->d.name_crc), crc);
++ goto bail;
++ }
++ }
++ break;
++ default:
++ printk(KERN_WARNING "Unknown node type for REF_PRISTINE node at 0x%08x: 0x%04x\n",
++ ref_offset(raw), je16_to_cpu(node->u.nodetype));
++ goto bail;
++ }
++
++ nraw = jffs2_alloc_raw_node_ref();
++ if (!nraw) {
++ ret = -ENOMEM;
++ goto out_node;
++ }
++
++ /* OK, all the CRCs are good; this node can just be copied as-is. */
++ retry:
++ nraw->flash_offset = phys_ofs;
++ nraw->__totlen = rawlen;
++ nraw->next_phys = NULL;
++
++ ret = jffs2_flash_write(c, phys_ofs, rawlen, &retlen, (char *)node);
++
++ if (ret || (retlen != rawlen)) {
++ printk(KERN_NOTICE "Write of %d bytes at 0x%08x failed. returned %d, retlen %zd\n",
++ rawlen, phys_ofs, ret, retlen);
++ if (retlen) {
++ /* Doesn't belong to any inode */
++ nraw->next_in_ino = NULL;
++
++ nraw->flash_offset |= REF_OBSOLETE;
++ jffs2_add_physical_node_ref(c, nraw);
++ jffs2_mark_node_obsolete(c, nraw);
++ } else {
++ printk(KERN_NOTICE "Not marking the space at 0x%08x as dirty because the flash driver returned retlen zero\n", nraw->flash_offset);
++ jffs2_free_raw_node_ref(nraw);
++ }
++ if (!retried && (nraw == jffs2_alloc_raw_node_ref())) {
++ /* Try to reallocate space and retry */
++ uint32_t dummy;
++ struct jffs2_eraseblock *jeb = &c->blocks[phys_ofs / c->sector_size];
++
++ retried = 1;
++
++ D1(printk(KERN_DEBUG "Retrying failed write of REF_PRISTINE node.\n"));
++
++ ACCT_SANITY_CHECK(c,jeb);
++ D1(ACCT_PARANOIA_CHECK(jeb));
++
++ ret = jffs2_reserve_space_gc(c, rawlen, &phys_ofs, &dummy);
++
++ if (!ret) {
++ D1(printk(KERN_DEBUG "Allocated space at 0x%08x to retry failed write.\n", phys_ofs));
+
++ ACCT_SANITY_CHECK(c,jeb);
++ D1(ACCT_PARANOIA_CHECK(jeb));
++
++ goto retry;
++ }
++ D1(printk(KERN_DEBUG "Failed to allocate space to retry failed write: %d!\n", ret));
++ jffs2_free_raw_node_ref(nraw);
++ }
++
++ if (!ret)
++ ret = -EIO;
++ goto out_node;
++ }
++ nraw->flash_offset |= REF_PRISTINE;
++ jffs2_add_physical_node_ref(c, nraw);
++
++ /* Link into per-inode list. This is safe because of the ic
++ state being INO_STATE_GC. Note that if we're doing this
++ for an inode which is in-code, the 'nraw' pointer is then
++ going to be fetched from ic->nodes by our caller. */
++ nraw->next_in_ino = ic->nodes;
++ ic->nodes = nraw;
++
++ jffs2_mark_node_obsolete(c, raw);
++ D1(printk(KERN_DEBUG "WHEEE! GC REF_PRISTINE node at 0x%08x succeeded\n", ref_offset(raw)));
++
++ out_node:
++ kfree(node);
+ return ret;
++ bail:
++ ret = -EBADFD;
++ goto out_node;
+ }
+
+ static int jffs2_garbage_collect_metadata(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
+- struct inode *inode, struct jffs2_full_dnode *fn)
++ struct jffs2_inode_info *f, struct jffs2_full_dnode *fn)
+ {
+- struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
+ struct jffs2_full_dnode *new_fn;
+ struct jffs2_raw_inode ri;
+- unsigned short dev;
++ jint16_t dev;
+ char *mdata = NULL, mdatalen = 0;
+- __u32 alloclen, phys_ofs;
++ uint32_t alloclen, phys_ofs;
+ int ret;
+
+- if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode)) {
++ if (S_ISBLK(JFFS2_F_I_MODE(f)) ||
++ S_ISCHR(JFFS2_F_I_MODE(f)) ) {
+ /* For these, we don't actually need to read the old node */
+- dev = (MAJOR(to_kdev_t(inode->i_rdev)) << 8) |
+- MINOR(to_kdev_t(inode->i_rdev));
++ /* FIXME: for minor or major > 255. */
++ dev = cpu_to_je16(((JFFS2_F_I_RDEV_MAJ(f) << 8) |
++ JFFS2_F_I_RDEV_MIN(f)));
+ mdata = (char *)&dev;
+ mdatalen = sizeof(dev);
+ D1(printk(KERN_DEBUG "jffs2_garbage_collect_metadata(): Writing %d bytes of kdev_t\n", mdatalen));
+- } else if (S_ISLNK(inode->i_mode)) {
++ } else if (S_ISLNK(JFFS2_F_I_MODE(f))) {
+ mdatalen = fn->size;
+ mdata = kmalloc(fn->size, GFP_KERNEL);
+ if (!mdata) {
+@@ -326,34 +686,34 @@
+
+ ret = jffs2_reserve_space_gc(c, sizeof(ri) + mdatalen, &phys_ofs, &alloclen);
+ if (ret) {
+- printk(KERN_WARNING "jffs2_reserve_space_gc of %d bytes for garbage_collect_metadata failed: %d\n",
++ printk(KERN_WARNING "jffs2_reserve_space_gc of %zd bytes for garbage_collect_metadata failed: %d\n",
+ sizeof(ri)+ mdatalen, ret);
+ goto out;
+ }
+
+ memset(&ri, 0, sizeof(ri));
+- ri.magic = JFFS2_MAGIC_BITMASK;
+- ri.nodetype = JFFS2_NODETYPE_INODE;
+- ri.totlen = sizeof(ri) + mdatalen;
+- ri.hdr_crc = crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4);
+-
+- ri.ino = inode->i_ino;
+- ri.version = ++f->highest_version;
+- ri.mode = inode->i_mode;
+- ri.uid = inode->i_uid;
+- ri.gid = inode->i_gid;
+- ri.isize = inode->i_size;
+- ri.atime = inode->i_atime;
+- ri.ctime = inode->i_ctime;
+- ri.mtime = inode->i_mtime;
+- ri.offset = 0;
+- ri.csize = mdatalen;
+- ri.dsize = mdatalen;
++ ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
++ ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE);
++ ri.totlen = cpu_to_je32(sizeof(ri) + mdatalen);
++ ri.hdr_crc = cpu_to_je32(crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4));
++
++ ri.ino = cpu_to_je32(f->inocache->ino);
++ ri.version = cpu_to_je32(++f->highest_version);
++ ri.mode = cpu_to_jemode(JFFS2_F_I_MODE(f));
++ ri.uid = cpu_to_je16(JFFS2_F_I_UID(f));
++ ri.gid = cpu_to_je16(JFFS2_F_I_GID(f));
++ ri.isize = cpu_to_je32(JFFS2_F_I_SIZE(f));
++ ri.atime = cpu_to_je32(JFFS2_F_I_ATIME(f));
++ ri.ctime = cpu_to_je32(JFFS2_F_I_CTIME(f));
++ ri.mtime = cpu_to_je32(JFFS2_F_I_MTIME(f));
++ ri.offset = cpu_to_je32(0);
++ ri.csize = cpu_to_je32(mdatalen);
++ ri.dsize = cpu_to_je32(mdatalen);
+ ri.compr = JFFS2_COMPR_NONE;
+- ri.node_crc = crc32(0, &ri, sizeof(ri)-8);
+- ri.data_crc = crc32(0, mdata, mdatalen);
++ ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8));
++ ri.data_crc = cpu_to_je32(crc32(0, mdata, mdatalen));
+
+- new_fn = jffs2_write_dnode(inode, &ri, mdata, mdatalen, phys_ofs, NULL);
++ new_fn = jffs2_write_dnode(c, f, &ri, mdata, mdatalen, phys_ofs, ALLOC_GC);
+
+ if (IS_ERR(new_fn)) {
+ printk(KERN_WARNING "Error writing new dnode: %ld\n", PTR_ERR(new_fn));
+@@ -364,41 +724,40 @@
+ jffs2_free_full_dnode(fn);
+ f->metadata = new_fn;
+ out:
+- if (S_ISLNK(inode->i_mode))
++ if (S_ISLNK(JFFS2_F_I_MODE(f)))
+ kfree(mdata);
+ return ret;
+ }
+
+ static int jffs2_garbage_collect_dirent(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
+- struct inode *inode, struct jffs2_full_dirent *fd)
++ struct jffs2_inode_info *f, struct jffs2_full_dirent *fd)
+ {
+- struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
+ struct jffs2_full_dirent *new_fd;
+ struct jffs2_raw_dirent rd;
+- __u32 alloclen, phys_ofs;
++ uint32_t alloclen, phys_ofs;
+ int ret;
+
+- rd.magic = JFFS2_MAGIC_BITMASK;
+- rd.nodetype = JFFS2_NODETYPE_DIRENT;
++ rd.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
++ rd.nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT);
+ rd.nsize = strlen(fd->name);
+- rd.totlen = sizeof(rd) + rd.nsize;
+- rd.hdr_crc = crc32(0, &rd, sizeof(struct jffs2_unknown_node)-4);
++ rd.totlen = cpu_to_je32(sizeof(rd) + rd.nsize);
++ rd.hdr_crc = cpu_to_je32(crc32(0, &rd, sizeof(struct jffs2_unknown_node)-4));
+
+- rd.pino = inode->i_ino;
+- rd.version = ++f->highest_version;
+- rd.ino = fd->ino;
+- rd.mctime = max(inode->i_mtime, inode->i_ctime);
++ rd.pino = cpu_to_je32(f->inocache->ino);
++ rd.version = cpu_to_je32(++f->highest_version);
++ rd.ino = cpu_to_je32(fd->ino);
++ rd.mctime = cpu_to_je32(max(JFFS2_F_I_MTIME(f), JFFS2_F_I_CTIME(f)));
+ rd.type = fd->type;
+- rd.node_crc = crc32(0, &rd, sizeof(rd)-8);
+- rd.name_crc = crc32(0, fd->name, rd.nsize);
++ rd.node_crc = cpu_to_je32(crc32(0, &rd, sizeof(rd)-8));
++ rd.name_crc = cpu_to_je32(crc32(0, fd->name, rd.nsize));
+
+ ret = jffs2_reserve_space_gc(c, sizeof(rd)+rd.nsize, &phys_ofs, &alloclen);
+ if (ret) {
+- printk(KERN_WARNING "jffs2_reserve_space_gc of %d bytes for garbage_collect_dirent failed: %d\n",
++ printk(KERN_WARNING "jffs2_reserve_space_gc of %zd bytes for garbage_collect_dirent failed: %d\n",
+ sizeof(rd)+rd.nsize, ret);
+ return ret;
+ }
+- new_fd = jffs2_write_dirent(inode, &rd, fd->name, rd.nsize, phys_ofs, NULL);
++ new_fd = jffs2_write_dirent(c, f, &rd, fd->name, rd.nsize, phys_ofs, ALLOC_GC);
+
+ if (IS_ERR(new_fd)) {
+ printk(KERN_WARNING "jffs2_write_dirent in garbage_collect_dirent failed: %ld\n", PTR_ERR(new_fd));
+@@ -409,19 +768,98 @@
+ }
+
+ static int jffs2_garbage_collect_deletion_dirent(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
+- struct inode *inode, struct jffs2_full_dirent *fd)
++ struct jffs2_inode_info *f, struct jffs2_full_dirent *fd)
+ {
+- struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
+ struct jffs2_full_dirent **fdp = &f->dents;
+ int found = 0;
+
+- /* FIXME: When we run on NAND flash, we need to work out whether
+- this deletion dirent is still needed to actively delete a
+- 'real' dirent with the same name that's still somewhere else
+- on the flash. For now, we know that we've actually obliterated
+- all the older dirents when they became obsolete, so we didn't
+- really need to write the deletion to flash in the first place.
+- */
++ /* On a medium where we can't actually mark nodes obsolete
++ pernamently, such as NAND flash, we need to work out
++ whether this deletion dirent is still needed to actively
++ delete a 'real' dirent with the same name that's still
++ somewhere else on the flash. */
++ if (!jffs2_can_mark_obsolete(c)) {
++ struct jffs2_raw_dirent *rd;
++ struct jffs2_raw_node_ref *raw;
++ int ret;
++ size_t retlen;
++ int name_len = strlen(fd->name);
++ uint32_t name_crc = crc32(0, fd->name, name_len);
++ uint32_t rawlen = ref_totlen(c, jeb, fd->raw);
++
++ rd = kmalloc(rawlen, GFP_KERNEL);
++ if (!rd)
++ return -ENOMEM;
++
++ /* Prevent the erase code from nicking the obsolete node refs while
++ we're looking at them. I really don't like this extra lock but
++ can't see any alternative. Suggestions on a postcard to... */
++ down(&c->erase_free_sem);
++
++ for (raw = f->inocache->nodes; raw != (void *)f->inocache; raw = raw->next_in_ino) {
++
++ /* We only care about obsolete ones */
++ if (!(ref_obsolete(raw)))
++ continue;
++
++ /* Any dirent with the same name is going to have the same length... */
++ if (ref_totlen(c, NULL, raw) != rawlen)
++ continue;
++
++ /* Doesn't matter if there's one in the same erase block. We're going to
++ delete it too at the same time. */
++ if ((raw->flash_offset & ~(c->sector_size-1)) ==
++ (fd->raw->flash_offset & ~(c->sector_size-1)))
++ continue;
++
++ D1(printk(KERN_DEBUG "Check potential deletion dirent at %08x\n", ref_offset(raw)));
++
++ /* This is an obsolete node belonging to the same directory, and it's of the right
++ length. We need to take a closer look...*/
++ ret = jffs2_flash_read(c, ref_offset(raw), rawlen, &retlen, (char *)rd);
++ if (ret) {
++ printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Read error (%d) reading obsolete node at %08x\n", ret, ref_offset(raw));
++ /* If we can't read it, we don't need to continue to obsolete it. Continue */
++ continue;
++ }
++ if (retlen != rawlen) {
++ printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Short read (%zd not %zd) reading header from obsolete node at %08x\n",
++ retlen, rawlen, ref_offset(raw));
++ continue;
++ }
++
++ if (je16_to_cpu(rd->nodetype) != JFFS2_NODETYPE_DIRENT)
++ continue;
++
++ /* If the name CRC doesn't match, skip */
++ if (je32_to_cpu(rd->name_crc) != name_crc)
++ continue;
++
++ /* If the name length doesn't match, or it's another deletion dirent, skip */
++ if (rd->nsize != name_len || !je32_to_cpu(rd->ino))
++ continue;
++
++ /* OK, check the actual name now */
++ if (memcmp(rd->name, fd->name, name_len))
++ continue;
++
++ /* OK. The name really does match. There really is still an older node on
++ the flash which our deletion dirent obsoletes. So we have to write out
++ a new deletion dirent to replace it */
++ up(&c->erase_free_sem);
++
++ D1(printk(KERN_DEBUG "Deletion dirent at %08x still obsoletes real dirent \"%s\" at %08x for ino #%u\n",
++ ref_offset(fd->raw), fd->name, ref_offset(raw), je32_to_cpu(rd->ino)));
++ kfree(rd);
++
++ return jffs2_garbage_collect_dirent(c, jeb, f, fd);
++ }
++
++ up(&c->erase_free_sem);
++ kfree(rd);
++ }
++
++ /* No need for it any more. Just mark it obsolete and remove it from the list */
+ while (*fdp) {
+ if ((*fdp) == fd) {
+ found = 1;
+@@ -431,7 +869,7 @@
+ fdp = &(*fdp)->next;
+ }
+ if (!found) {
+- printk(KERN_WARNING "Deletion dirent \"%s\" not found in list for ino #%lu\n", fd->name, inode->i_ino);
++ printk(KERN_WARNING "Deletion dirent \"%s\" not found in list for ino #%u\n", fd->name, f->inocache->ino);
+ }
+ jffs2_mark_node_obsolete(c, fd->raw);
+ jffs2_free_full_dirent(fd);
+@@ -439,93 +877,95 @@
+ }
+
+ static int jffs2_garbage_collect_hole(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
+- struct inode *inode, struct jffs2_full_dnode *fn,
+- __u32 start, __u32 end)
++ struct jffs2_inode_info *f, struct jffs2_full_dnode *fn,
++ uint32_t start, uint32_t end)
+ {
+- struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
+ struct jffs2_raw_inode ri;
+ struct jffs2_node_frag *frag;
+ struct jffs2_full_dnode *new_fn;
+- __u32 alloclen, phys_ofs;
++ uint32_t alloclen, phys_ofs;
+ int ret;
+
+- D1(printk(KERN_DEBUG "Writing replacement hole node for ino #%lu from offset 0x%x to 0x%x\n",
+- inode->i_ino, start, end));
++ D1(printk(KERN_DEBUG "Writing replacement hole node for ino #%u from offset 0x%x to 0x%x\n",
++ f->inocache->ino, start, end));
+
+ memset(&ri, 0, sizeof(ri));
+
+ if(fn->frags > 1) {
+ size_t readlen;
+- __u32 crc;
++ uint32_t crc;
+ /* It's partially obsoleted by a later write. So we have to
+ write it out again with the _same_ version as before */
+- ret = c->mtd->read(c->mtd, fn->raw->flash_offset & ~3, sizeof(ri), &readlen, (char *)&ri);
++ ret = jffs2_flash_read(c, ref_offset(fn->raw), sizeof(ri), &readlen, (char *)&ri);
+ if (readlen != sizeof(ri) || ret) {
+- printk(KERN_WARNING "Node read failed in jffs2_garbage_collect_hole. Ret %d, retlen %d. Data will be lost by writing new hold node\n", ret, readlen);
++ printk(KERN_WARNING "Node read failed in jffs2_garbage_collect_hole. Ret %d, retlen %zd. Data will be lost by writing new hole node\n", ret, readlen);
+ goto fill;
+ }
+- if (ri.nodetype != JFFS2_NODETYPE_INODE) {
++ if (je16_to_cpu(ri.nodetype) != JFFS2_NODETYPE_INODE) {
+ printk(KERN_WARNING "jffs2_garbage_collect_hole: Node at 0x%08x had node type 0x%04x instead of JFFS2_NODETYPE_INODE(0x%04x)\n",
+- fn->raw->flash_offset & ~3, ri.nodetype, JFFS2_NODETYPE_INODE);
++ ref_offset(fn->raw),
++ je16_to_cpu(ri.nodetype), JFFS2_NODETYPE_INODE);
+ return -EIO;
+ }
+- if (ri.totlen != sizeof(ri)) {
+- printk(KERN_WARNING "jffs2_garbage_collect_hole: Node at 0x%08x had totlen 0x%x instead of expected 0x%x\n",
+- fn->raw->flash_offset & ~3, ri.totlen, sizeof(ri));
++ if (je32_to_cpu(ri.totlen) != sizeof(ri)) {
++ printk(KERN_WARNING "jffs2_garbage_collect_hole: Node at 0x%08x had totlen 0x%x instead of expected 0x%zx\n",
++ ref_offset(fn->raw),
++ je32_to_cpu(ri.totlen), sizeof(ri));
+ return -EIO;
+ }
+ crc = crc32(0, &ri, sizeof(ri)-8);
+- if (crc != ri.node_crc) {
++ if (crc != je32_to_cpu(ri.node_crc)) {
+ printk(KERN_WARNING "jffs2_garbage_collect_hole: Node at 0x%08x had CRC 0x%08x which doesn't match calculated CRC 0x%08x\n",
+- fn->raw->flash_offset & ~3, ri.node_crc, crc);
++ ref_offset(fn->raw),
++ je32_to_cpu(ri.node_crc), crc);
+ /* FIXME: We could possibly deal with this by writing new holes for each frag */
+- printk(KERN_WARNING "Data in the range 0x%08x to 0x%08x of inode #%lu will be lost\n",
+- start, end, inode->i_ino);
++ printk(KERN_WARNING "Data in the range 0x%08x to 0x%08x of inode #%u will be lost\n",
++ start, end, f->inocache->ino);
+ goto fill;
+ }
+ if (ri.compr != JFFS2_COMPR_ZERO) {
+- printk(KERN_WARNING "jffs2_garbage_collect_hole: Node 0x%08x wasn't a hole node!\n", fn->raw->flash_offset & ~3);
+- printk(KERN_WARNING "Data in the range 0x%08x to 0x%08x of inode #%lu will be lost\n",
+- start, end, inode->i_ino);
++ printk(KERN_WARNING "jffs2_garbage_collect_hole: Node 0x%08x wasn't a hole node!\n", ref_offset(fn->raw));
++ printk(KERN_WARNING "Data in the range 0x%08x to 0x%08x of inode #%u will be lost\n",
++ start, end, f->inocache->ino);
+ goto fill;
+ }
+ } else {
+ fill:
+- ri.magic = JFFS2_MAGIC_BITMASK;
+- ri.nodetype = JFFS2_NODETYPE_INODE;
+- ri.totlen = sizeof(ri);
+- ri.hdr_crc = crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4);
+-
+- ri.ino = inode->i_ino;
+- ri.version = ++f->highest_version;
+- ri.offset = start;
+- ri.dsize = end - start;
+- ri.csize = 0;
++ ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
++ ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE);
++ ri.totlen = cpu_to_je32(sizeof(ri));
++ ri.hdr_crc = cpu_to_je32(crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4));
++
++ ri.ino = cpu_to_je32(f->inocache->ino);
++ ri.version = cpu_to_je32(++f->highest_version);
++ ri.offset = cpu_to_je32(start);
++ ri.dsize = cpu_to_je32(end - start);
++ ri.csize = cpu_to_je32(0);
+ ri.compr = JFFS2_COMPR_ZERO;
+ }
+- ri.mode = inode->i_mode;
+- ri.uid = inode->i_uid;
+- ri.gid = inode->i_gid;
+- ri.isize = inode->i_size;
+- ri.atime = inode->i_atime;
+- ri.ctime = inode->i_ctime;
+- ri.mtime = inode->i_mtime;
+- ri.data_crc = 0;
+- ri.node_crc = crc32(0, &ri, sizeof(ri)-8);
++ ri.mode = cpu_to_jemode(JFFS2_F_I_MODE(f));
++ ri.uid = cpu_to_je16(JFFS2_F_I_UID(f));
++ ri.gid = cpu_to_je16(JFFS2_F_I_GID(f));
++ ri.isize = cpu_to_je32(JFFS2_F_I_SIZE(f));
++ ri.atime = cpu_to_je32(JFFS2_F_I_ATIME(f));
++ ri.ctime = cpu_to_je32(JFFS2_F_I_CTIME(f));
++ ri.mtime = cpu_to_je32(JFFS2_F_I_MTIME(f));
++ ri.data_crc = cpu_to_je32(0);
++ ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8));
+
+ ret = jffs2_reserve_space_gc(c, sizeof(ri), &phys_ofs, &alloclen);
+ if (ret) {
+- printk(KERN_WARNING "jffs2_reserve_space_gc of %d bytes for garbage_collect_hole failed: %d\n",
++ printk(KERN_WARNING "jffs2_reserve_space_gc of %zd bytes for garbage_collect_hole failed: %d\n",
+ sizeof(ri), ret);
+ return ret;
+ }
+- new_fn = jffs2_write_dnode(inode, &ri, NULL, 0, phys_ofs, NULL);
++ new_fn = jffs2_write_dnode(c, f, &ri, NULL, 0, phys_ofs, ALLOC_GC);
+
+ if (IS_ERR(new_fn)) {
+ printk(KERN_WARNING "Error writing new hole node: %ld\n", PTR_ERR(new_fn));
+ return PTR_ERR(new_fn);
+ }
+- if (ri.version == f->highest_version) {
++ if (je32_to_cpu(ri.version) == f->highest_version) {
+ jffs2_add_full_dnode_to_inode(c, f, new_fn);
+ if (f->metadata) {
+ jffs2_mark_node_obsolete(c, f->metadata->raw);
+@@ -541,12 +981,17 @@
+ * number as before. (Except in case of error -- see 'goto fill;'
+ * above.)
+ */
+- D1(if(fn->frags <= 1) {
++ D1(if(unlikely(fn->frags <= 1)) {
+ printk(KERN_WARNING "jffs2_garbage_collect_hole: Replacing fn with %d frag(s) but new ver %d != highest_version %d of ino #%d\n",
+- fn->frags, ri.version, f->highest_version, ri.ino);
++ fn->frags, je32_to_cpu(ri.version), f->highest_version,
++ je32_to_cpu(ri.ino));
+ });
+
+- for (frag = f->fraglist; frag; frag = frag->next) {
++ /* This is a partially-overlapped hole node. Mark it REF_NORMAL not REF_PRISTINE */
++ mark_ref_normal(new_fn->raw);
++
++ for (frag = jffs2_lookup_node_frag(&f->fragtree, fn->ofs);
++ frag; frag = frag_next(frag)) {
+ if (frag->ofs > fn->size + fn->ofs)
+ break;
+ if (frag->node == fn) {
+@@ -571,49 +1016,146 @@
+ }
+
+ static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
+- struct inode *inode, struct jffs2_full_dnode *fn,
+- __u32 start, __u32 end)
++ struct jffs2_inode_info *f, struct jffs2_full_dnode *fn,
++ uint32_t start, uint32_t end)
+ {
+- struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
+ struct jffs2_full_dnode *new_fn;
+ struct jffs2_raw_inode ri;
+- __u32 alloclen, phys_ofs, offset, orig_end;
++ uint32_t alloclen, phys_ofs, offset, orig_end, orig_start;
+ int ret = 0;
+ unsigned char *comprbuf = NULL, *writebuf;
+- struct page *pg;
++ unsigned long pg;
+ unsigned char *pg_ptr;
+
+-
+ memset(&ri, 0, sizeof(ri));
+
+- D1(printk(KERN_DEBUG "Writing replacement dnode for ino #%lu from offset 0x%x to 0x%x\n",
+- inode->i_ino, start, end));
++ D1(printk(KERN_DEBUG "Writing replacement dnode for ino #%u from offset 0x%x to 0x%x\n",
++ f->inocache->ino, start, end));
+
+ orig_end = end;
++ orig_start = start;
+
++ if (c->nr_free_blocks + c->nr_erasing_blocks > c->resv_blocks_gcmerge) {
++ /* Attempt to do some merging. But only expand to cover logically
++ adjacent frags if the block containing them is already considered
++ to be dirty. Otherwise we end up with GC just going round in
++ circles dirtying the nodes it already wrote out, especially
++ on NAND where we have small eraseblocks and hence a much higher
++ chance of nodes having to be split to cross boundaries. */
+
+- /* If we're looking at the last node in the block we're
+- garbage-collecting, we allow ourselves to merge as if the
+- block was already erasing. We're likely to be GC'ing a
+- partial page, and the next block we GC is likely to have
+- the other half of this page right at the beginning, which
+- means we'd expand it _then_, as nr_erasing_blocks would have
+- increased since we checked, and in doing so would obsolete
+- the partial node which we'd have written here. Meaning that
+- the GC would churn and churn, and just leave dirty blocks in
+- it's wake.
+- */
+- if(c->nr_free_blocks + c->nr_erasing_blocks > JFFS2_RESERVED_BLOCKS_GCMERGE - (fn->raw->next_phys?0:1)) {
+- /* Shitloads of space */
+- /* FIXME: Integrate this properly with GC calculations */
+- start &= ~(PAGE_CACHE_SIZE-1);
+- end = min_t(__u32, start + PAGE_CACHE_SIZE, inode->i_size);
+- D1(printk(KERN_DEBUG "Plenty of free space, so expanding to write from offset 0x%x to 0x%x\n",
+- start, end));
+- if (end < orig_end) {
+- printk(KERN_WARNING "Eep. jffs2_garbage_collect_dnode extended node to write, but it got smaller: start 0x%x, orig_end 0x%x, end 0x%x\n", start, orig_end, end);
+- end = orig_end;
++ struct jffs2_node_frag *frag;
++ uint32_t min, max;
++
++ min = start & ~(PAGE_CACHE_SIZE-1);
++ max = min + PAGE_CACHE_SIZE;
++
++ frag = jffs2_lookup_node_frag(&f->fragtree, start);
++
++ /* BUG_ON(!frag) but that'll happen anyway... */
++
++ BUG_ON(frag->ofs != start);
++
++ /* First grow down... */
++ while((frag = frag_prev(frag)) && frag->ofs >= min) {
++
++ /* If the previous frag doesn't even reach the beginning, there's
++ excessive fragmentation. Just merge. */
++ if (frag->ofs > min) {
++ D1(printk(KERN_DEBUG "Expanding down to cover partial frag (0x%x-0x%x)\n",
++ frag->ofs, frag->ofs+frag->size));
++ start = frag->ofs;
++ continue;
++ }
++ /* OK. This frag holds the first byte of the page. */
++ if (!frag->node || !frag->node->raw) {
++ D1(printk(KERN_DEBUG "First frag in page is hole (0x%x-0x%x). Not expanding down.\n",
++ frag->ofs, frag->ofs+frag->size));
++ break;
++ } else {
++
++ /* OK, it's a frag which extends to the beginning of the page. Does it live
++ in a block which is still considered clean? If so, don't obsolete it.
++ If not, cover it anyway. */
++
++ struct jffs2_raw_node_ref *raw = frag->node->raw;
++ struct jffs2_eraseblock *jeb;
++
++ jeb = &c->blocks[raw->flash_offset / c->sector_size];
++
++ if (jeb == c->gcblock) {
++ D1(printk(KERN_DEBUG "Expanding down to cover frag (0x%x-0x%x) in gcblock at %08x\n",
++ frag->ofs, frag->ofs+frag->size, ref_offset(raw)));
++ start = frag->ofs;
++ break;
+ }
++ if (!ISDIRTY(jeb->dirty_size + jeb->wasted_size)) {
++ D1(printk(KERN_DEBUG "Not expanding down to cover frag (0x%x-0x%x) in clean block %08x\n",
++ frag->ofs, frag->ofs+frag->size, jeb->offset));
++ break;
++ }
++
++ D1(printk(KERN_DEBUG "Expanding down to cover frag (0x%x-0x%x) in dirty block %08x\n",
++ frag->ofs, frag->ofs+frag->size, jeb->offset));
++ start = frag->ofs;
++ break;
++ }
++ }
++
++ /* ... then up */
++
++ /* Find last frag which is actually part of the node we're to GC. */
++ frag = jffs2_lookup_node_frag(&f->fragtree, end-1);
++
++ while((frag = frag_next(frag)) && frag->ofs+frag->size <= max) {
++
++ /* If the previous frag doesn't even reach the beginning, there's lots
++ of fragmentation. Just merge. */
++ if (frag->ofs+frag->size < max) {
++ D1(printk(KERN_DEBUG "Expanding up to cover partial frag (0x%x-0x%x)\n",
++ frag->ofs, frag->ofs+frag->size));
++ end = frag->ofs + frag->size;
++ continue;
++ }
++
++ if (!frag->node || !frag->node->raw) {
++ D1(printk(KERN_DEBUG "Last frag in page is hole (0x%x-0x%x). Not expanding up.\n",
++ frag->ofs, frag->ofs+frag->size));
++ break;
++ } else {
++
++ /* OK, it's a frag which extends to the beginning of the page. Does it live
++ in a block which is still considered clean? If so, don't obsolete it.
++ If not, cover it anyway. */
++
++ struct jffs2_raw_node_ref *raw = frag->node->raw;
++ struct jffs2_eraseblock *jeb;
++
++ jeb = &c->blocks[raw->flash_offset / c->sector_size];
++
++ if (jeb == c->gcblock) {
++ D1(printk(KERN_DEBUG "Expanding up to cover frag (0x%x-0x%x) in gcblock at %08x\n",
++ frag->ofs, frag->ofs+frag->size, ref_offset(raw)));
++ end = frag->ofs + frag->size;
++ break;
++ }
++ if (!ISDIRTY(jeb->dirty_size + jeb->wasted_size)) {
++ D1(printk(KERN_DEBUG "Not expanding up to cover frag (0x%x-0x%x) in clean block %08x\n",
++ frag->ofs, frag->ofs+frag->size, jeb->offset));
++ break;
++ }
++
++ D1(printk(KERN_DEBUG "Expanding up to cover frag (0x%x-0x%x) in dirty block %08x\n",
++ frag->ofs, frag->ofs+frag->size, jeb->offset));
++ end = frag->ofs + frag->size;
++ break;
++ }
++ }
++ D1(printk(KERN_DEBUG "Expanded dnode to write from (0x%x-0x%x) to (0x%x-0x%x)\n",
++ orig_start, orig_end, start, end));
++
++ BUG_ON(end > JFFS2_F_I_SIZE(f));
++ BUG_ON(end < orig_end);
++ BUG_ON(start > orig_start);
+ }
+
+ /* First, use readpage() to read the appropriate page into the page cache */
+@@ -623,63 +1165,57 @@
+ * page OK. We'll actually write it out again in commit_write, which is a little
+ * suboptimal, but at least we're correct.
+ */
+- pg = read_cache_page(inode->i_mapping, start >> PAGE_CACHE_SHIFT, (void *)jffs2_do_readpage_unlock, inode);
++ pg_ptr = jffs2_gc_fetch_page(c, f, start, &pg);
+
+- if (IS_ERR(pg)) {
+- printk(KERN_WARNING "read_cache_page() returned error: %ld\n", PTR_ERR(pg));
+- return PTR_ERR(pg);
++ if (IS_ERR(pg_ptr)) {
++ printk(KERN_WARNING "read_cache_page() returned error: %ld\n", PTR_ERR(pg_ptr));
++ return PTR_ERR(pg_ptr);
+ }
+- pg_ptr = (char *)kmap(pg);
+- comprbuf = kmalloc(end - start, GFP_KERNEL);
+
+ offset = start;
+ while(offset < orig_end) {
+- __u32 datalen;
+- __u32 cdatalen;
++ uint32_t datalen;
++ uint32_t cdatalen;
+ char comprtype = JFFS2_COMPR_NONE;
+
+ ret = jffs2_reserve_space_gc(c, sizeof(ri) + JFFS2_MIN_DATA_LEN, &phys_ofs, &alloclen);
+
+ if (ret) {
+- printk(KERN_WARNING "jffs2_reserve_space_gc of %d bytes for garbage_collect_dnode failed: %d\n",
++ printk(KERN_WARNING "jffs2_reserve_space_gc of %zd bytes for garbage_collect_dnode failed: %d\n",
+ sizeof(ri)+ JFFS2_MIN_DATA_LEN, ret);
+ break;
+ }
+- cdatalen = min(alloclen - sizeof(ri), end - offset);
++ cdatalen = min_t(uint32_t, alloclen - sizeof(ri), end - offset);
+ datalen = end - offset;
+
+ writebuf = pg_ptr + (offset & (PAGE_CACHE_SIZE -1));
+
+- if (comprbuf) {
+- comprtype = jffs2_compress(writebuf, comprbuf, &datalen, &cdatalen);
+- }
+- if (comprtype) {
+- writebuf = comprbuf;
+- } else {
+- datalen = cdatalen;
+- }
+- ri.magic = JFFS2_MAGIC_BITMASK;
+- ri.nodetype = JFFS2_NODETYPE_INODE;
+- ri.totlen = sizeof(ri) + cdatalen;
+- ri.hdr_crc = crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4);
+-
+- ri.ino = inode->i_ino;
+- ri.version = ++f->highest_version;
+- ri.mode = inode->i_mode;
+- ri.uid = inode->i_uid;
+- ri.gid = inode->i_gid;
+- ri.isize = inode->i_size;
+- ri.atime = inode->i_atime;
+- ri.ctime = inode->i_ctime;
+- ri.mtime = inode->i_mtime;
+- ri.offset = offset;
+- ri.csize = cdatalen;
+- ri.dsize = datalen;
++ comprtype = jffs2_compress(writebuf, &comprbuf, &datalen, &cdatalen);
++
++ ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
++ ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE);
++ ri.totlen = cpu_to_je32(sizeof(ri) + cdatalen);
++ ri.hdr_crc = cpu_to_je32(crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4));
++
++ ri.ino = cpu_to_je32(f->inocache->ino);
++ ri.version = cpu_to_je32(++f->highest_version);
++ ri.mode = cpu_to_jemode(JFFS2_F_I_MODE(f));
++ ri.uid = cpu_to_je16(JFFS2_F_I_UID(f));
++ ri.gid = cpu_to_je16(JFFS2_F_I_GID(f));
++ ri.isize = cpu_to_je32(JFFS2_F_I_SIZE(f));
++ ri.atime = cpu_to_je32(JFFS2_F_I_ATIME(f));
++ ri.ctime = cpu_to_je32(JFFS2_F_I_CTIME(f));
++ ri.mtime = cpu_to_je32(JFFS2_F_I_MTIME(f));
++ ri.offset = cpu_to_je32(offset);
++ ri.csize = cpu_to_je32(cdatalen);
++ ri.dsize = cpu_to_je32(datalen);
+ ri.compr = comprtype;
+- ri.node_crc = crc32(0, &ri, sizeof(ri)-8);
+- ri.data_crc = crc32(0, writebuf, cdatalen);
++ ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8));
++ ri.data_crc = cpu_to_je32(crc32(0, comprbuf, cdatalen));
++
++ new_fn = jffs2_write_dnode(c, f, &ri, comprbuf, cdatalen, phys_ofs, ALLOC_GC);
+
+- new_fn = jffs2_write_dnode(inode, &ri, writebuf, cdatalen, phys_ofs, NULL);
++ jffs2_free_comprbuf(comprbuf, writebuf);
+
+ if (IS_ERR(new_fn)) {
+ printk(KERN_WARNING "Error writing new dnode: %ld\n", PTR_ERR(new_fn));
+@@ -694,12 +1230,8 @@
+ f->metadata = NULL;
+ }
+ }
+- if (comprbuf) kfree(comprbuf);
+
+- kunmap(pg);
+- /* XXX: Does the page get freed automatically? */
+- /* AAA: Judging by the unmount getting stuck in __wait_on_page, nope. */
+- page_cache_release(pg);
++ jffs2_gc_release_page(c, pg_ptr, &pg);
+ return ret;
+ }
+
+diff -Nurb linux-mips-2.4.24-pre2/fs/jffs2/ioctl.c linux/fs/jffs2/ioctl.c
+--- linux-mips-2.4.24-pre2/fs/jffs2/ioctl.c 2004-11-17 18:05:04.000000000 +0100
++++ linux/fs/jffs2/ioctl.c 2004-11-17 18:17:59.000000000 +0100
+@@ -1,37 +1,13 @@
+ /*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+- * Copyright (C) 2001 Red Hat, Inc.
++ * Copyright (C) 2001-2003 Red Hat, Inc.
+ *
+- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
++ * Created by David Woodhouse <dwmw2@redhat.com>
+ *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
++ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+- * The contents of this file are subject to the Red Hat eCos Public
+- * License Version 1.1 (the "Licence"); you may not use this file
+- * except in compliance with the Licence. You may obtain a copy of
+- * the Licence at http://www.redhat.com/
+- *
+- * Software distributed under the Licence is distributed on an "AS IS"
+- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+- * See the Licence for the specific language governing rights and
+- * limitations under the Licence.
+- *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
+- *
+- * Alternatively, the contents of this file may be used under the
+- * terms of the GNU General Public License version 2 (the "GPL"), in
+- * which case the provisions of the GPL are applicable instead of the
+- * above. If you wish to allow the use of your version of this file
+- * only under the terms of the GPL and not to allow others to use your
+- * version of this file under the RHEPL, indicate your decision by
+- * deleting the provisions above and replace them with the notice and
+- * other provisions required by the GPL. If you do not delete the
+- * provisions above, a recipient may use your version of this file
+- * under either the RHEPL or the GPL.
+- *
+- * $Id$
++ * $Id$
+ *
+ */
+
+@@ -42,6 +18,6 @@
+ {
+ /* Later, this will provide for lsattr.jffs2 and chattr.jffs2, which
+ will include compression support etc. */
+- return -EINVAL;
++ return -ENOTTY;
+ }
+
+diff -Nurb linux-mips-2.4.24-pre2/fs/jffs2/malloc.c linux/fs/jffs2/malloc.c
+--- linux-mips-2.4.24-pre2/fs/jffs2/malloc.c 2004-11-17 18:05:04.000000000 +0100
++++ linux/fs/jffs2/malloc.c 2004-11-17 18:17:59.000000000 +0100
+@@ -1,37 +1,13 @@
+ /*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+- * Copyright (C) 2001 Red Hat, Inc.
++ * Copyright (C) 2001-2003 Red Hat, Inc.
+ *
+- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
++ * Created by David Woodhouse <dwmw2@redhat.com>
+ *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
++ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+- * The contents of this file are subject to the Red Hat eCos Public
+- * License Version 1.1 (the "Licence"); you may not use this file
+- * except in compliance with the Licence. You may obtain a copy of
+- * the Licence at http://www.redhat.com/
+- *
+- * Software distributed under the Licence is distributed on an "AS IS"
+- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+- * See the Licence for the specific language governing rights and
+- * limitations under the Licence.
+- *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
+- *
+- * Alternatively, the contents of this file may be used under the
+- * terms of the GNU General Public License version 2 (the "GPL"), in
+- * which case the provisions of the GPL are applicable instead of the
+- * above. If you wish to allow the use of your version of this file
+- * only under the terms of the GPL and not to allow others to use your
+- * version of this file under the RHEPL, indicate your decision by
+- * deleting the provisions above and replace them with the notice and
+- * other provisions required by the GPL. If you do not delete the
+- * provisions above, a recipient may use your version of this file
+- * under either the RHEPL or the GPL.
+- *
+- * $Id$
++ * $Id$
+ *
+ */
+
+@@ -47,6 +23,9 @@
+ #define JFFS2_SLAB_POISON 0
+ #endif
+
++// replace this by #define D3 (x) x for cache debugging
++#define D3(x)
++
+ /* These are initialised to NULL in the kernel startup code.
+ If you're porting to other operating systems, beware */
+ static kmem_cache_t *full_dnode_slab;
+@@ -57,57 +36,47 @@
+ static kmem_cache_t *node_frag_slab;
+ static kmem_cache_t *inode_cache_slab;
+
+-void jffs2_free_tmp_dnode_info_list(struct jffs2_tmp_dnode_info *tn)
+-{
+- struct jffs2_tmp_dnode_info *next;
+-
+- while (tn) {
+- next = tn;
+- tn = tn->next;
+- jffs2_free_full_dnode(next->fn);
+- jffs2_free_tmp_dnode_info(next);
+- }
+-}
+-
+-void jffs2_free_full_dirent_list(struct jffs2_full_dirent *fd)
+-{
+- struct jffs2_full_dirent *next;
+-
+- while (fd) {
+- next = fd->next;
+- jffs2_free_full_dirent(fd);
+- fd = next;
+- }
+-}
+-
+ int __init jffs2_create_slab_caches(void)
+ {
+- full_dnode_slab = kmem_cache_create("jffs2_full_dnode", sizeof(struct jffs2_full_dnode), 0, JFFS2_SLAB_POISON, NULL, NULL);
++ full_dnode_slab = kmem_cache_create("jffs2_full_dnode",
++ sizeof(struct jffs2_full_dnode),
++ 0, JFFS2_SLAB_POISON, NULL, NULL);
+ if (!full_dnode_slab)
+ goto err;
+
+- raw_dirent_slab = kmem_cache_create("jffs2_raw_dirent", sizeof(struct jffs2_raw_dirent), 0, JFFS2_SLAB_POISON, NULL, NULL);
++ raw_dirent_slab = kmem_cache_create("jffs2_raw_dirent",
++ sizeof(struct jffs2_raw_dirent),
++ 0, JFFS2_SLAB_POISON, NULL, NULL);
+ if (!raw_dirent_slab)
+ goto err;
+
+- raw_inode_slab = kmem_cache_create("jffs2_raw_inode", sizeof(struct jffs2_raw_inode), 0, JFFS2_SLAB_POISON, NULL, NULL);
++ raw_inode_slab = kmem_cache_create("jffs2_raw_inode",
++ sizeof(struct jffs2_raw_inode),
++ 0, JFFS2_SLAB_POISON, NULL, NULL);
+ if (!raw_inode_slab)
+ goto err;
+
+- tmp_dnode_info_slab = kmem_cache_create("jffs2_tmp_dnode", sizeof(struct jffs2_tmp_dnode_info), 0, JFFS2_SLAB_POISON, NULL, NULL);
++ tmp_dnode_info_slab = kmem_cache_create("jffs2_tmp_dnode",
++ sizeof(struct jffs2_tmp_dnode_info),
++ 0, JFFS2_SLAB_POISON, NULL, NULL);
+ if (!tmp_dnode_info_slab)
+ goto err;
+
+- raw_node_ref_slab = kmem_cache_create("jffs2_raw_node_ref", sizeof(struct jffs2_raw_node_ref), 0, JFFS2_SLAB_POISON, NULL, NULL);
++ raw_node_ref_slab = kmem_cache_create("jffs2_raw_node_ref",
++ sizeof(struct jffs2_raw_node_ref),
++ 0, JFFS2_SLAB_POISON, NULL, NULL);
+ if (!raw_node_ref_slab)
+ goto err;
+
+- node_frag_slab = kmem_cache_create("jffs2_node_frag", sizeof(struct jffs2_node_frag), 0, JFFS2_SLAB_POISON, NULL, NULL);
++ node_frag_slab = kmem_cache_create("jffs2_node_frag",
++ sizeof(struct jffs2_node_frag),
++ 0, JFFS2_SLAB_POISON, NULL, NULL);
+ if (!node_frag_slab)
+ goto err;
+
+- inode_cache_slab = kmem_cache_create("jffs2_inode_cache", sizeof(struct jffs2_inode_cache), 0, JFFS2_SLAB_POISON, NULL, NULL);
+-
++ inode_cache_slab = kmem_cache_create("jffs2_inode_cache",
++ sizeof(struct jffs2_inode_cache),
++ 0, JFFS2_SLAB_POISON, NULL, NULL);
+ if (inode_cache_slab)
+ return 0;
+ err:
+@@ -131,7 +100,6 @@
+ kmem_cache_destroy(node_frag_slab);
+ if(inode_cache_slab)
+ kmem_cache_destroy(inode_cache_slab);
+-
+ }
+
+ struct jffs2_full_dirent *jffs2_alloc_full_dirent(int namesize)
+@@ -146,75 +114,92 @@
+
+ struct jffs2_full_dnode *jffs2_alloc_full_dnode(void)
+ {
+- void *ret = kmem_cache_alloc(full_dnode_slab, GFP_KERNEL);
++ struct jffs2_full_dnode *ret = kmem_cache_alloc(full_dnode_slab, GFP_KERNEL);
++ D3 (printk (KERN_DEBUG "alloc_full_dnode at %p\n", ret));
+ return ret;
+ }
+
+ void jffs2_free_full_dnode(struct jffs2_full_dnode *x)
+ {
++ D3 (printk (KERN_DEBUG "free full_dnode at %p\n", x));
+ kmem_cache_free(full_dnode_slab, x);
+ }
+
+ struct jffs2_raw_dirent *jffs2_alloc_raw_dirent(void)
+ {
+- return kmem_cache_alloc(raw_dirent_slab, GFP_KERNEL);
++ struct jffs2_raw_dirent *ret = kmem_cache_alloc(raw_dirent_slab, GFP_KERNEL);
++ D3 (printk (KERN_DEBUG "alloc_raw_dirent\n", ret));
++ return ret;
+ }
+
+ void jffs2_free_raw_dirent(struct jffs2_raw_dirent *x)
+ {
++ D3 (printk (KERN_DEBUG "free_raw_dirent at %p\n", x));
+ kmem_cache_free(raw_dirent_slab, x);
+ }
+
+ struct jffs2_raw_inode *jffs2_alloc_raw_inode(void)
+ {
+- return kmem_cache_alloc(raw_inode_slab, GFP_KERNEL);
++ struct jffs2_raw_inode *ret = kmem_cache_alloc(raw_inode_slab, GFP_KERNEL);
++ D3 (printk (KERN_DEBUG "alloc_raw_inode at %p\n", ret));
++ return ret;
+ }
+
+ void jffs2_free_raw_inode(struct jffs2_raw_inode *x)
+ {
++ D3 (printk (KERN_DEBUG "free_raw_inode at %p\n", x));
+ kmem_cache_free(raw_inode_slab, x);
+ }
+
+ struct jffs2_tmp_dnode_info *jffs2_alloc_tmp_dnode_info(void)
+ {
+- return kmem_cache_alloc(tmp_dnode_info_slab, GFP_KERNEL);
++ struct jffs2_tmp_dnode_info *ret = kmem_cache_alloc(tmp_dnode_info_slab, GFP_KERNEL);
++ D3 (printk (KERN_DEBUG "alloc_tmp_dnode_info at %p\n", ret));
++ return ret;
+ }
+
+ void jffs2_free_tmp_dnode_info(struct jffs2_tmp_dnode_info *x)
+ {
++ D3 (printk (KERN_DEBUG "free_tmp_dnode_info at %p\n", x));
+ kmem_cache_free(tmp_dnode_info_slab, x);
+ }
+
+ struct jffs2_raw_node_ref *jffs2_alloc_raw_node_ref(void)
+ {
+- return kmem_cache_alloc(raw_node_ref_slab, GFP_KERNEL);
++ struct jffs2_raw_node_ref *ret = kmem_cache_alloc(raw_node_ref_slab, GFP_KERNEL);
++ D3 (printk (KERN_DEBUG "alloc_raw_node_ref at %p\n", ret));
++ return ret;
+ }
+
+ void jffs2_free_raw_node_ref(struct jffs2_raw_node_ref *x)
+ {
++ D3 (printk (KERN_DEBUG "free_raw_node_ref at %p\n", x));
+ kmem_cache_free(raw_node_ref_slab, x);
+ }
+
+ struct jffs2_node_frag *jffs2_alloc_node_frag(void)
+ {
+- return kmem_cache_alloc(node_frag_slab, GFP_KERNEL);
++ struct jffs2_node_frag *ret = kmem_cache_alloc(node_frag_slab, GFP_KERNEL);
++ D3 (printk (KERN_DEBUG "alloc_node_frag at %p\n", ret));
++ return ret;
+ }
+
+ void jffs2_free_node_frag(struct jffs2_node_frag *x)
+ {
++ D3 (printk (KERN_DEBUG "free_node_frag at %p\n", x));
+ kmem_cache_free(node_frag_slab, x);
+ }
+
+ struct jffs2_inode_cache *jffs2_alloc_inode_cache(void)
+ {
+ struct jffs2_inode_cache *ret = kmem_cache_alloc(inode_cache_slab, GFP_KERNEL);
+- D1(printk(KERN_DEBUG "Allocated inocache at %p\n", ret));
++ D3 (printk(KERN_DEBUG "Allocated inocache at %p\n", ret));
+ return ret;
+ }
+
+ void jffs2_free_inode_cache(struct jffs2_inode_cache *x)
+ {
+- D1(printk(KERN_DEBUG "Freeing inocache at %p\n", x));
++ D3 (printk(KERN_DEBUG "Freeing inocache at %p\n", x));
+ kmem_cache_free(inode_cache_slab, x);
+ }
+
+diff -Nurb linux-mips-2.4.24-pre2/fs/jffs2/nodelist.c linux/fs/jffs2/nodelist.c
+--- linux-mips-2.4.24-pre2/fs/jffs2/nodelist.c 2004-11-17 18:05:04.000000000 +0100
++++ linux/fs/jffs2/nodelist.c 2004-11-17 18:17:59.000000000 +0100
+@@ -1,44 +1,24 @@
+ /*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+- * Copyright (C) 2001, 2002 Red Hat, Inc.
++ * Copyright (C) 2001-2003 Red Hat, Inc.
+ *
+- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
++ * Created by David Woodhouse <dwmw2@redhat.com>
+ *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
++ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+- * The contents of this file are subject to the Red Hat eCos Public
+- * License Version 1.1 (the "Licence"); you may not use this file
+- * except in compliance with the Licence. You may obtain a copy of
+- * the Licence at http://www.redhat.com/
+- *
+- * Software distributed under the Licence is distributed on an "AS IS"
+- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+- * See the Licence for the specific language governing rights and
+- * limitations under the Licence.
+- *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
+- *
+- * Alternatively, the contents of this file may be used under the
+- * terms of the GNU General Public License version 2 (the "GPL"), in
+- * which case the provisions of the GPL are applicable instead of the
+- * above. If you wish to allow the use of your version of this file
+- * only under the terms of the GPL and not to allow others to use your
+- * version of this file under the RHEPL, indicate your decision by
+- * deleting the provisions above and replace them with the notice and
+- * other provisions required by the GPL. If you do not delete the
+- * provisions above, a recipient may use your version of this file
+- * under either the RHEPL or the GPL.
+- *
+- * $Id$
++ * $Id$
+ *
+ */
+
+ #include <linux/kernel.h>
+-#include <linux/jffs2.h>
++#include <linux/sched.h>
+ #include <linux/fs.h>
+ #include <linux/mtd/mtd.h>
++#include <linux/rbtree.h>
++#include <linux/crc32.h>
++#include <linux/slab.h>
++#include <linux/pagemap.h>
+ #include "nodelist.h"
+
+ void jffs2_add_fd_to_list(struct jffs2_sb_info *c, struct jffs2_full_dirent *new, struct jffs2_full_dirent **list)
+@@ -78,7 +58,7 @@
+ /* Put a new tmp_dnode_info into the list, keeping the list in
+ order of increasing version
+ */
+-void jffs2_add_tn_to_list(struct jffs2_tmp_dnode_info *tn, struct jffs2_tmp_dnode_info **list)
++static void jffs2_add_tn_to_list(struct jffs2_tmp_dnode_info *tn, struct jffs2_tmp_dnode_info **list)
+ {
+ struct jffs2_tmp_dnode_info **prev = list;
+
+@@ -89,13 +69,37 @@
+ *prev = tn;
+ }
+
++static void jffs2_free_tmp_dnode_info_list(struct jffs2_tmp_dnode_info *tn)
++{
++ struct jffs2_tmp_dnode_info *next;
++
++ while (tn) {
++ next = tn;
++ tn = tn->next;
++ jffs2_free_full_dnode(next->fn);
++ jffs2_free_tmp_dnode_info(next);
++ }
++}
++
++static void jffs2_free_full_dirent_list(struct jffs2_full_dirent *fd)
++{
++ struct jffs2_full_dirent *next;
++
++ while (fd) {
++ next = fd->next;
++ jffs2_free_full_dirent(fd);
++ fd = next;
++ }
++}
++
++
+ /* Get tmp_dnode_info and full_dirent for all non-obsolete nodes associated
+ with this ino, returning the former in order of version */
+
+ int jffs2_get_inode_nodes(struct jffs2_sb_info *c, ino_t ino, struct jffs2_inode_info *f,
+ struct jffs2_tmp_dnode_info **tnp, struct jffs2_full_dirent **fdp,
+- __u32 *highest_version, __u32 *latest_mctime,
+- __u32 *mctime_ver)
++ uint32_t *highest_version, uint32_t *latest_mctime,
++ uint32_t *mctime_ver)
+ {
+ struct jffs2_raw_node_ref *ref = f->inocache->nodes;
+ struct jffs2_tmp_dnode_info *tn, *ret_tn = NULL;
+@@ -109,43 +113,71 @@
+
+ D1(printk(KERN_DEBUG "jffs2_get_inode_nodes(): ino #%lu\n", ino));
+ if (!f->inocache->nodes) {
+- printk(KERN_WARNING "Eep. no nodes for ino #%lu\n", ino);
++ printk(KERN_WARNING "Eep. no nodes for ino #%lu\n", (unsigned long)ino);
+ }
++
++ spin_lock(&c->erase_completion_lock);
++
+ for (ref = f->inocache->nodes; ref && ref->next_in_ino; ref = ref->next_in_ino) {
+ /* Work out whether it's a data node or a dirent node */
+- if (ref->flash_offset & 1) {
++ if (ref_obsolete(ref)) {
+ /* FIXME: On NAND flash we may need to read these */
+- D1(printk(KERN_DEBUG "node at 0x%08x is obsoleted. Ignoring.\n", ref->flash_offset &~3));
++ D1(printk(KERN_DEBUG "node at 0x%08x is obsoleted. Ignoring.\n", ref_offset(ref)));
+ continue;
+ }
+- err = c->mtd->read(c->mtd, (ref->flash_offset & ~3), min(ref->totlen, sizeof(node)), &retlen, (void *)&node);
++ /* We can hold a pointer to a non-obsolete node without the spinlock,
++ but _obsolete_ nodes may disappear at any time, if the block
++ they're in gets erased */
++ spin_unlock(&c->erase_completion_lock);
++
++ cond_resched();
++
++ /* FIXME: point() */
++ err = jffs2_flash_read(c, (ref_offset(ref)),
++ min_t(uint32_t, ref_totlen(c, NULL, ref), sizeof(node)),
++ &retlen, (void *)&node);
+ if (err) {
+- printk(KERN_WARNING "error %d reading node at 0x%08x in get_inode_nodes()\n", err, (ref->flash_offset) & ~3);
++ printk(KERN_WARNING "error %d reading node at 0x%08x in get_inode_nodes()\n", err, ref_offset(ref));
+ goto free_out;
+ }
+
+
+ /* Check we've managed to read at least the common node header */
+- if (retlen < min(ref->totlen, sizeof(node.u))) {
++ if (retlen < min_t(uint32_t, ref_totlen(c, NULL, ref), sizeof(node.u))) {
+ printk(KERN_WARNING "short read in get_inode_nodes()\n");
+ err = -EIO;
+ goto free_out;
+ }
+
+- switch (node.u.nodetype) {
++ switch (je16_to_cpu(node.u.nodetype)) {
+ case JFFS2_NODETYPE_DIRENT:
+- D1(printk(KERN_DEBUG "Node at %08x is a dirent node\n", ref->flash_offset &~3));
++ D1(printk(KERN_DEBUG "Node at %08x (%d) is a dirent node\n", ref_offset(ref), ref_flags(ref)));
++ if (ref_flags(ref) == REF_UNCHECKED) {
++ printk(KERN_WARNING "BUG: Dirent node at 0x%08x never got checked? How?\n", ref_offset(ref));
++ BUG();
++ }
+ if (retlen < sizeof(node.d)) {
+ printk(KERN_WARNING "short read in get_inode_nodes()\n");
+ err = -EIO;
+ goto free_out;
+ }
+- if (node.d.version > *highest_version)
+- *highest_version = node.d.version;
+- if (ref->flash_offset & 1) {
+- /* Obsoleted */
++ /* sanity check */
++ if (PAD((node.d.nsize + sizeof (node.d))) != PAD(je32_to_cpu (node.d.totlen))) {
++ printk(KERN_NOTICE "jffs2_get_inode_nodes(): Illegal nsize in node at 0x%08x: nsize 0x%02x, totlen %04x\n",
++ ref_offset(ref), node.d.nsize, je32_to_cpu(node.d.totlen));
++ jffs2_mark_node_obsolete(c, ref);
++ spin_lock(&c->erase_completion_lock);
+ continue;
+ }
++ if (je32_to_cpu(node.d.version) > *highest_version)
++ *highest_version = je32_to_cpu(node.d.version);
++ if (ref_obsolete(ref)) {
++ /* Obsoleted. This cannot happen, surely? dwmw2 20020308 */
++ printk(KERN_ERR "Dirent node at 0x%08x became obsolete while we weren't looking\n",
++ ref_offset(ref));
++ BUG();
++ }
++
+ fd = jffs2_alloc_full_dirent(node.d.nsize+1);
+ if (!fd) {
+ err = -ENOMEM;
+@@ -153,29 +185,30 @@
+ }
+ memset(fd,0,sizeof(struct jffs2_full_dirent) + node.d.nsize+1);
+ fd->raw = ref;
+- fd->version = node.d.version;
+- fd->ino = node.d.ino;
++ fd->version = je32_to_cpu(node.d.version);
++ fd->ino = je32_to_cpu(node.d.ino);
+ fd->type = node.d.type;
+
+ /* Pick out the mctime of the latest dirent */
+ if(fd->version > *mctime_ver) {
+ *mctime_ver = fd->version;
+- *latest_mctime = node.d.mctime;
++ *latest_mctime = je32_to_cpu(node.d.mctime);
+ }
+
+ /* memcpy as much of the name as possible from the raw
+ dirent we've already read from the flash
+ */
+ if (retlen > sizeof(struct jffs2_raw_dirent))
+- memcpy(&fd->name[0], &node.d.name[0], min((__u32)node.d.nsize, (retlen-sizeof(struct jffs2_raw_dirent))));
++ memcpy(&fd->name[0], &node.d.name[0], min_t(uint32_t, node.d.nsize, (retlen-sizeof(struct jffs2_raw_dirent))));
+
+ /* Do we need to copy any more of the name directly
+ from the flash?
+ */
+ if (node.d.nsize + sizeof(struct jffs2_raw_dirent) > retlen) {
++ /* FIXME: point() */
+ int already = retlen - sizeof(struct jffs2_raw_dirent);
+
+- err = c->mtd->read(c->mtd, (ref->flash_offset & ~3) + retlen,
++ err = jffs2_flash_read(c, (ref_offset(ref)) + retlen,
+ node.d.nsize - already, &retlen, &fd->name[already]);
+ if (!err && retlen != node.d.nsize - already)
+ err = -EIO;
+@@ -196,21 +229,126 @@
+ break;
+
+ case JFFS2_NODETYPE_INODE:
+- D1(printk(KERN_DEBUG "Node at %08x is a data node\n", ref->flash_offset &~3));
++ D1(printk(KERN_DEBUG "Node at %08x (%d) is a data node\n", ref_offset(ref), ref_flags(ref)));
+ if (retlen < sizeof(node.i)) {
+ printk(KERN_WARNING "read too short for dnode\n");
+ err = -EIO;
+ goto free_out;
+ }
+- if (node.i.version > *highest_version)
+- *highest_version = node.i.version;
+- D1(printk(KERN_DEBUG "version %d, highest_version now %d\n", node.i.version, *highest_version));
+-
+- if (ref->flash_offset & 1) {
+- D1(printk(KERN_DEBUG "obsoleted\n"));
+- /* Obsoleted */
++ if (je32_to_cpu(node.i.version) > *highest_version)
++ *highest_version = je32_to_cpu(node.i.version);
++ D1(printk(KERN_DEBUG "version %d, highest_version now %d\n", je32_to_cpu(node.i.version), *highest_version));
++
++ if (ref_obsolete(ref)) {
++ /* Obsoleted. This cannot happen, surely? dwmw2 20020308 */
++ printk(KERN_ERR "Inode node at 0x%08x became obsolete while we weren't looking\n",
++ ref_offset(ref));
++ BUG();
++ }
++
++ /* If we've never checked the CRCs on this node, check them now. */
++ if (ref_flags(ref) == REF_UNCHECKED) {
++ uint32_t crc, len;
++ struct jffs2_eraseblock *jeb;
++
++ crc = crc32(0, &node, sizeof(node.i)-8);
++ if (crc != je32_to_cpu(node.i.node_crc)) {
++ printk(KERN_NOTICE "jffs2_get_inode_nodes(): CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
++ ref_offset(ref), je32_to_cpu(node.i.node_crc), crc);
++ jffs2_mark_node_obsolete(c, ref);
++ spin_lock(&c->erase_completion_lock);
+ continue;
+ }
++
++ /* sanity checks */
++ if ( je32_to_cpu(node.i.offset) > je32_to_cpu(node.i.isize) ||
++ PAD(je32_to_cpu(node.i.csize) + sizeof (node.i)) != PAD(je32_to_cpu(node.i.totlen))) {
++ printk(KERN_NOTICE "jffs2_get_inode_nodes(): Inode corrupted at 0x%08x, totlen %d, #ino %d, version %d, isize %d, csize %d, dsize %d \n",
++ ref_offset(ref), je32_to_cpu(node.i.totlen), je32_to_cpu(node.i.ino),
++ je32_to_cpu(node.i.version), je32_to_cpu(node.i.isize),
++ je32_to_cpu(node.i.csize), je32_to_cpu(node.i.dsize));
++ jffs2_mark_node_obsolete(c, ref);
++ spin_lock(&c->erase_completion_lock);
++ continue;
++ }
++
++ if (node.i.compr != JFFS2_COMPR_ZERO && je32_to_cpu(node.i.csize)) {
++ unsigned char *buf=NULL;
++ uint32_t pointed = 0;
++#ifndef __ECOS
++ if (c->mtd->point) {
++ err = c->mtd->point (c->mtd, ref_offset(ref) + sizeof(node.i), je32_to_cpu(node.i.csize),
++ &retlen, &buf);
++ if (!err && retlen < je32_to_cpu(node.i.csize)) {
++ D1(printk(KERN_DEBUG "MTD point returned len too short: 0x%zx\n", retlen));
++ c->mtd->unpoint(c->mtd, buf, ref_offset(ref) + sizeof(node.i), je32_to_cpu(node.i.csize));
++ } else if (err){
++ D1(printk(KERN_DEBUG "MTD point failed %d\n", err));
++ } else
++ pointed = 1; /* succefully pointed to device */
++ }
++#endif
++ if(!pointed){
++ buf = kmalloc(je32_to_cpu(node.i.csize), GFP_KERNEL);
++ if (!buf)
++ return -ENOMEM;
++
++ err = jffs2_flash_read(c, ref_offset(ref) + sizeof(node.i), je32_to_cpu(node.i.csize),
++ &retlen, buf);
++ if (!err && retlen != je32_to_cpu(node.i.csize))
++ err = -EIO;
++ if (err) {
++ kfree(buf);
++ return err;
++ }
++ }
++ crc = crc32(0, buf, je32_to_cpu(node.i.csize));
++ if(!pointed)
++ kfree(buf);
++#ifndef __ECOS
++ else
++ c->mtd->unpoint(c->mtd, buf, ref_offset(ref) + sizeof(node.i), je32_to_cpu(node.i.csize));
++#endif
++
++ if (crc != je32_to_cpu(node.i.data_crc)) {
++ printk(KERN_NOTICE "jffs2_get_inode_nodes(): Data CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
++ ref_offset(ref), je32_to_cpu(node.i.data_crc), crc);
++ jffs2_mark_node_obsolete(c, ref);
++ spin_lock(&c->erase_completion_lock);
++ continue;
++ }
++
++ }
++
++ /* Mark the node as having been checked and fix the accounting accordingly */
++ spin_lock(&c->erase_completion_lock);
++ jeb = &c->blocks[ref->flash_offset / c->sector_size];
++ len = ref_totlen(c, jeb, ref);
++
++ jeb->used_size += len;
++ jeb->unchecked_size -= len;
++ c->used_size += len;
++ c->unchecked_size -= len;
++
++ /* If node covers at least a whole page, or if it starts at the
++ beginning of a page and runs to the end of the file, or if
++ it's a hole node, mark it REF_PRISTINE, else REF_NORMAL.
++
++ If it's actually overlapped, it'll get made NORMAL (or OBSOLETE)
++ when the overlapping node(s) get added to the tree anyway.
++ */
++ if ((je32_to_cpu(node.i.dsize) >= PAGE_CACHE_SIZE) ||
++ ( ((je32_to_cpu(node.i.offset)&(PAGE_CACHE_SIZE-1))==0) &&
++ (je32_to_cpu(node.i.dsize)+je32_to_cpu(node.i.offset) == je32_to_cpu(node.i.isize)))) {
++ D1(printk(KERN_DEBUG "Marking node at 0x%08x REF_PRISTINE\n", ref_offset(ref)));
++ ref->flash_offset = ref_offset(ref) | REF_PRISTINE;
++ } else {
++ D1(printk(KERN_DEBUG "Marking node at 0x%08x REF_NORMAL\n", ref_offset(ref)));
++ ref->flash_offset = ref_offset(ref) | REF_NORMAL;
++ }
++ spin_unlock(&c->erase_completion_lock);
++ }
++
+ tn = jffs2_alloc_tmp_dnode_info();
+ if (!tn) {
+ D1(printk(KERN_DEBUG "alloc tn failed\n"));
+@@ -225,36 +363,76 @@
+ jffs2_free_tmp_dnode_info(tn);
+ goto free_out;
+ }
+- tn->version = node.i.version;
+- tn->fn->ofs = node.i.offset;
++ tn->version = je32_to_cpu(node.i.version);
++ tn->fn->ofs = je32_to_cpu(node.i.offset);
+ /* There was a bug where we wrote hole nodes out with
+ csize/dsize swapped. Deal with it */
+- if (node.i.compr == JFFS2_COMPR_ZERO && !node.i.dsize && node.i.csize)
+- tn->fn->size = node.i.csize;
++ if (node.i.compr == JFFS2_COMPR_ZERO && !je32_to_cpu(node.i.dsize) && je32_to_cpu(node.i.csize))
++ tn->fn->size = je32_to_cpu(node.i.csize);
+ else // normal case...
+- tn->fn->size = node.i.dsize;
++ tn->fn->size = je32_to_cpu(node.i.dsize);
+ tn->fn->raw = ref;
+- D1(printk(KERN_DEBUG "dnode @%08x: ver %u, offset %04x, dsize %04x\n", ref->flash_offset &~3, node.i.version, node.i.offset, node.i.dsize));
++ D1(printk(KERN_DEBUG "dnode @%08x: ver %u, offset %04x, dsize %04x\n",
++ ref_offset(ref), je32_to_cpu(node.i.version),
++ je32_to_cpu(node.i.offset), je32_to_cpu(node.i.dsize)));
+ jffs2_add_tn_to_list(tn, &ret_tn);
+ break;
+
+ default:
+- switch(node.u.nodetype & JFFS2_COMPAT_MASK) {
++ if (ref_flags(ref) == REF_UNCHECKED) {
++ struct jffs2_eraseblock *jeb;
++ uint32_t len;
++
++ printk(KERN_ERR "Eep. Unknown node type %04x at %08x was marked REF_UNCHECKED\n",
++ je16_to_cpu(node.u.nodetype), ref_offset(ref));
++
++ /* Mark the node as having been checked and fix the accounting accordingly */
++ spin_lock(&c->erase_completion_lock);
++ jeb = &c->blocks[ref->flash_offset / c->sector_size];
++ len = ref_totlen(c, jeb, ref);
++
++ jeb->used_size += len;
++ jeb->unchecked_size -= len;
++ c->used_size += len;
++ c->unchecked_size -= len;
++
++ mark_ref_normal(ref);
++ spin_unlock(&c->erase_completion_lock);
++ }
++ node.u.nodetype = cpu_to_je16(JFFS2_NODE_ACCURATE | je16_to_cpu(node.u.nodetype));
++ if (crc32(0, &node, sizeof(struct jffs2_unknown_node)-4) != je32_to_cpu(node.u.hdr_crc)) {
++ /* Hmmm. This should have been caught at scan time. */
++ printk(KERN_ERR "Node header CRC failed at %08x. But it must have been OK earlier.\n",
++ ref_offset(ref));
++ printk(KERN_ERR "Node was: { %04x, %04x, %08x, %08x }\n",
++ je16_to_cpu(node.u.magic), je16_to_cpu(node.u.nodetype), je32_to_cpu(node.u.totlen),
++ je32_to_cpu(node.u.hdr_crc));
++ jffs2_mark_node_obsolete(c, ref);
++ } else switch(je16_to_cpu(node.u.nodetype) & JFFS2_COMPAT_MASK) {
+ case JFFS2_FEATURE_INCOMPAT:
+- printk(KERN_NOTICE "Unknown INCOMPAT nodetype %04X at %08X\n", node.u.nodetype, ref->flash_offset & ~3);
++ printk(KERN_NOTICE "Unknown INCOMPAT nodetype %04X at %08x\n", je16_to_cpu(node.u.nodetype), ref_offset(ref));
++ /* EEP */
++ BUG();
+ break;
+ case JFFS2_FEATURE_ROCOMPAT:
+- printk(KERN_NOTICE "Unknown ROCOMPAT nodetype %04X at %08X\n", node.u.nodetype, ref->flash_offset & ~3);
++ printk(KERN_NOTICE "Unknown ROCOMPAT nodetype %04X at %08x\n", je16_to_cpu(node.u.nodetype), ref_offset(ref));
++ if (!(c->flags & JFFS2_SB_FLAG_RO))
++ BUG();
+ break;
+ case JFFS2_FEATURE_RWCOMPAT_COPY:
+- printk(KERN_NOTICE "Unknown RWCOMPAT_COPY nodetype %04X at %08X\n", node.u.nodetype, ref->flash_offset & ~3);
++ printk(KERN_NOTICE "Unknown RWCOMPAT_COPY nodetype %04X at %08x\n", je16_to_cpu(node.u.nodetype), ref_offset(ref));
+ break;
+ case JFFS2_FEATURE_RWCOMPAT_DELETE:
+- printk(KERN_NOTICE "Unknown RWCOMPAT_DELETE nodetype %04X at %08X\n", node.u.nodetype, ref->flash_offset & ~3);
++ printk(KERN_NOTICE "Unknown RWCOMPAT_DELETE nodetype %04X at %08x\n", je16_to_cpu(node.u.nodetype), ref_offset(ref));
++ jffs2_mark_node_obsolete(c, ref);
+ break;
+ }
++
+ }
++ spin_lock(&c->erase_completion_lock);
++
+ }
++ spin_unlock(&c->erase_completion_lock);
+ *tnp = ret_tn;
+ *fdp = ret_fd;
+
+@@ -266,19 +444,30 @@
+ return err;
+ }
+
++void jffs2_set_inocache_state(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic, int state)
++{
++ spin_lock(&c->inocache_lock);
++ ic->state = state;
++ wake_up(&c->inocache_wq);
++ spin_unlock(&c->inocache_lock);
++}
++
++/* During mount, this needs no locking. During normal operation, its
++ callers want to do other stuff while still holding the inocache_lock.
++ Rather than introducing special case get_ino_cache functions or
++ callbacks, we just let the caller do the locking itself. */
++
+ struct jffs2_inode_cache *jffs2_get_ino_cache(struct jffs2_sb_info *c, uint32_t ino)
+ {
+ struct jffs2_inode_cache *ret;
+
+ D2(printk(KERN_DEBUG "jffs2_get_ino_cache(): ino %u\n", ino));
+- spin_lock (&c->inocache_lock);
++
+ ret = c->inocache_list[ino % INOCACHE_HASHSIZE];
+ while (ret && ret->ino < ino) {
+ ret = ret->next;
+ }
+
+- spin_unlock(&c->inocache_lock);
+-
+ if (ret && ret->ino != ino)
+ ret = NULL;
+
+@@ -299,6 +488,7 @@
+ }
+ new->next = *prev;
+ *prev = new;
++
+ spin_unlock(&c->inocache_lock);
+ }
+
+@@ -316,6 +506,7 @@
+ if ((*prev) == old) {
+ *prev = old->next;
+ }
++
+ spin_unlock(&c->inocache_lock);
+ }
+
+@@ -352,3 +543,128 @@
+ }
+ }
+
++struct jffs2_node_frag *jffs2_lookup_node_frag(struct rb_root *fragtree, uint32_t offset)
++{
++ /* The common case in lookup is that there will be a node
++ which precisely matches. So we go looking for that first */
++ struct rb_node *next;
++ struct jffs2_node_frag *prev = NULL;
++ struct jffs2_node_frag *frag = NULL;
++
++ D2(printk(KERN_DEBUG "jffs2_lookup_node_frag(%p, %d)\n", fragtree, offset));
++
++ next = fragtree->rb_node;
++
++ while(next) {
++ frag = rb_entry(next, struct jffs2_node_frag, rb);
++
++ D2(printk(KERN_DEBUG "Considering frag %d-%d (%p). left %p, right %p\n",
++ frag->ofs, frag->ofs+frag->size, frag, frag->rb.rb_left, frag->rb.rb_right));
++ if (frag->ofs + frag->size <= offset) {
++ D2(printk(KERN_DEBUG "Going right from frag %d-%d, before the region we care about\n",
++ frag->ofs, frag->ofs+frag->size));
++ /* Remember the closest smaller match on the way down */
++ if (!prev || frag->ofs > prev->ofs)
++ prev = frag;
++ next = frag->rb.rb_right;
++ } else if (frag->ofs > offset) {
++ D2(printk(KERN_DEBUG "Going left from frag %d-%d, after the region we care about\n",
++ frag->ofs, frag->ofs+frag->size));
++ next = frag->rb.rb_left;
++ } else {
++ D2(printk(KERN_DEBUG "Returning frag %d,%d, matched\n",
++ frag->ofs, frag->ofs+frag->size));
++ return frag;
++ }
++ }
++
++ /* Exact match not found. Go back up looking at each parent,
++ and return the closest smaller one */
++
++ if (prev)
++ D2(printk(KERN_DEBUG "No match. Returning frag %d,%d, closest previous\n",
++ prev->ofs, prev->ofs+prev->size));
++ else
++ D2(printk(KERN_DEBUG "Returning NULL, empty fragtree\n"));
++
++ return prev;
++}
++
++/* Pass 'c' argument to indicate that nodes should be marked obsolete as
++ they're killed. */
++void jffs2_kill_fragtree(struct rb_root *root, struct jffs2_sb_info *c)
++{
++ struct jffs2_node_frag *frag;
++ struct jffs2_node_frag *parent;
++
++ if (!root->rb_node)
++ return;
++
++ frag = (rb_entry(root->rb_node, struct jffs2_node_frag, rb));
++
++ while(frag) {
++ if (frag->rb.rb_left) {
++ D2(printk(KERN_DEBUG "Going left from frag (%p) %d-%d\n",
++ frag, frag->ofs, frag->ofs+frag->size));
++ frag = frag_left(frag);
++ continue;
++ }
++ if (frag->rb.rb_right) {
++ D2(printk(KERN_DEBUG "Going right from frag (%p) %d-%d\n",
++ frag, frag->ofs, frag->ofs+frag->size));
++ frag = frag_right(frag);
++ continue;
++ }
++
++ D2(printk(KERN_DEBUG "jffs2_kill_fragtree: frag at 0x%x-0x%x: node %p, frags %d--\n",
++ frag->ofs, frag->ofs+frag->size, frag->node,
++ frag->node?frag->node->frags:0));
++
++ if (frag->node && !(--frag->node->frags)) {
++ /* Not a hole, and it's the final remaining frag
++ of this node. Free the node */
++ if (c)
++ jffs2_mark_node_obsolete(c, frag->node->raw);
++
++ jffs2_free_full_dnode(frag->node);
++ }
++ parent = frag_parent(frag);
++ if (parent) {
++ if (frag_left(parent) == frag)
++ parent->rb.rb_left = NULL;
++ else
++ parent->rb.rb_right = NULL;
++ }
++
++ jffs2_free_node_frag(frag);
++ frag = parent;
++
++ cond_resched();
++ }
++}
++
++void jffs2_fragtree_insert(struct jffs2_node_frag *newfrag, struct jffs2_node_frag *base)
++{
++ struct rb_node *parent = &base->rb;
++ struct rb_node **link = &parent;
++
++ D2(printk(KERN_DEBUG "jffs2_fragtree_insert(%p; %d-%d, %p)\n", newfrag,
++ newfrag->ofs, newfrag->ofs+newfrag->size, base));
++
++ while (*link) {
++ parent = *link;
++ base = rb_entry(parent, struct jffs2_node_frag, rb);
++
++ D2(printk(KERN_DEBUG "fragtree_insert considering frag at 0x%x\n", base->ofs));
++ if (newfrag->ofs > base->ofs)
++ link = &base->rb.rb_right;
++ else if (newfrag->ofs < base->ofs)
++ link = &base->rb.rb_left;
++ else {
++ printk(KERN_CRIT "Duplicate frag at %08x (%p,%p)\n", newfrag->ofs, newfrag, base);
++ BUG();
++ }
++ }
++
++ rb_link_node(&newfrag->rb, &base->rb, link);
++}
+diff -Nurb linux-mips-2.4.24-pre2/fs/jffs2/nodelist.h linux/fs/jffs2/nodelist.h
+--- linux-mips-2.4.24-pre2/fs/jffs2/nodelist.h 2004-11-17 18:05:04.000000000 +0100
++++ linux/fs/jffs2/nodelist.h 2004-11-17 18:17:59.000000000 +0100
+@@ -1,48 +1,35 @@
+ /*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+- * Copyright (C) 2001 Red Hat, Inc.
++ * Copyright (C) 2001-2003 Red Hat, Inc.
+ *
+- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
++ * Created by David Woodhouse <dwmw2@redhat.com>
+ *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
++ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+- * The contents of this file are subject to the Red Hat eCos Public
+- * License Version 1.1 (the "Licence"); you may not use this file
+- * except in compliance with the Licence. You may obtain a copy of
+- * the Licence at http://www.redhat.com/
+- *
+- * Software distributed under the Licence is distributed on an "AS IS"
+- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+- * See the Licence for the specific language governing rights and
+- * limitations under the Licence.
+- *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
+- *
+- * Alternatively, the contents of this file may be used under the
+- * terms of the GNU General Public License version 2 (the "GPL"), in
+- * which case the provisions of the GPL are applicable instead of the
+- * above. If you wish to allow the use of your version of this file
+- * only under the terms of the GPL and not to allow others to use your
+- * version of this file under the RHEPL, indicate your decision by
+- * deleting the provisions above and replace them with the notice and
+- * other provisions required by the GPL. If you do not delete the
+- * provisions above, a recipient may use your version of this file
+- * under either the RHEPL or the GPL.
+- *
+- * $Id$
++ * $Id$
+ *
+ */
+
++#ifndef __JFFS2_NODELIST_H__
++#define __JFFS2_NODELIST_H__
++
+ #include <linux/config.h>
+ #include <linux/fs.h>
+-
++#include <linux/types.h>
++#include <linux/jffs2.h>
+ #include <linux/jffs2_fs_sb.h>
+ #include <linux/jffs2_fs_i.h>
+
++#ifdef __ECOS
++#include "os-ecos.h"
++#else
++#include <linux/mtd/compatmac.h> /* For min/max in older kernels */
++#include "os-linux.h"
++#endif
++
+ #ifndef CONFIG_JFFS2_FS_DEBUG
+-#define CONFIG_JFFS2_FS_DEBUG 2
++#define CONFIG_JFFS2_FS_DEBUG 1
+ #endif
+
+ #if CONFIG_JFFS2_FS_DEBUG > 0
+@@ -71,17 +58,21 @@
+ for this inode instead. The inode_cache will have NULL in the first
+ word so you know when you've got there :) */
+ struct jffs2_raw_node_ref *next_phys;
+- // __u32 ino;
+- __u32 flash_offset;
+- __u32 totlen;
+-// __u16 nodetype;
++ uint32_t flash_offset;
++ uint32_t __totlen; /* This may die; use ref_totlen(c, jeb, ) below */
++};
+
+ /* flash_offset & 3 always has to be zero, because nodes are
+ always aligned at 4 bytes. So we have a couple of extra bits
+- to play with. So we set the least significant bit to 1 to
+- signify that the node is obsoleted by later nodes.
+- */
+-};
++ to play with, which indicate the node's status; see below: */
++#define REF_UNCHECKED 0 /* We haven't yet checked the CRC or built its inode */
++#define REF_OBSOLETE 1 /* Obsolete, can be completely ignored */
++#define REF_PRISTINE 2 /* Completely clean. GC without looking */
++#define REF_NORMAL 3 /* Possibly overlapped. Read the page and write again on GC */
++#define ref_flags(ref) ((ref)->flash_offset & 3)
++#define ref_offset(ref) ((ref)->flash_offset & ~3)
++#define ref_obsolete(ref) (((ref)->flash_offset & 3) == REF_OBSOLETE)
++#define mark_ref_normal(ref) do { (ref)->flash_offset = ref_offset(ref) | REF_NORMAL; } while(0)
+
+ /*
+ Used for keeping track of deletion nodes &c, which can only be marked
+@@ -101,19 +92,35 @@
+ a pointer to the first physical node which is part of this inode, too.
+ */
+ struct jffs2_inode_cache {
+- struct jffs2_scan_info *scan; /* Used during scan to hold
+- temporary lists of nodes, and later must be set to
++ struct jffs2_full_dirent *scan_dents; /* Used during scan to hold
++ temporary lists of dirents, and later must be set to
+ NULL to mark the end of the raw_node_ref->next_in_ino
+ chain. */
+ struct jffs2_inode_cache *next;
+ struct jffs2_raw_node_ref *nodes;
+- __u32 ino;
++ uint32_t ino;
+ int nlink;
++ int state;
+ };
+
++/* Inode states for 'state' above. We need the 'GC' state to prevent
++ someone from doing a read_inode() while we're moving a 'REF_PRISTINE'
++ node without going through all the iget() nonsense */
++#define INO_STATE_UNCHECKED 0 /* CRC checks not yet done */
++#define INO_STATE_CHECKING 1 /* CRC checks in progress */
++#define INO_STATE_PRESENT 2 /* In core */
++#define INO_STATE_CHECKEDABSENT 3 /* Checked, cleared again */
++#define INO_STATE_GC 4 /* GCing a 'pristine' node */
++#define INO_STATE_READING 5 /* In read_inode() */
++
++#define INOCACHE_HASHSIZE 128
++
+ struct jffs2_scan_info {
+ struct jffs2_full_dirent *dents;
+ struct jffs2_tmp_dnode_info *tmpnodes;
++ /* Latest i_size info */
++ uint32_t version;
++ uint32_t isize;
+ };
+ /*
+ Larger representation of a raw node, kept in-core only when the
+@@ -123,9 +130,9 @@
+ struct jffs2_full_dnode
+ {
+ struct jffs2_raw_node_ref *raw;
+- __u32 ofs; /* Don't really need this, but optimisation */
+- __u32 size;
+- __u32 frags; /* Number of fragments which currently refer
++ uint32_t ofs; /* Don't really need this, but optimisation */
++ uint32_t size;
++ uint32_t frags; /* Number of fragments which currently refer
+ to this node. When this reaches zero,
+ the node is obsolete.
+ */
+@@ -140,15 +147,15 @@
+ {
+ struct jffs2_tmp_dnode_info *next;
+ struct jffs2_full_dnode *fn;
+- __u32 version;
++ uint32_t version;
+ };
+
+ struct jffs2_full_dirent
+ {
+ struct jffs2_raw_node_ref *raw;
+ struct jffs2_full_dirent *next;
+- __u32 version;
+- __u32 ino; /* == zero for unlink */
++ uint32_t version;
++ uint32_t ino; /* == zero for unlink */
+ unsigned int nhash;
+ unsigned char type;
+ unsigned char name[0];
+@@ -159,21 +166,23 @@
+ */
+ struct jffs2_node_frag
+ {
+- struct jffs2_node_frag *next;
++ struct rb_node rb;
+ struct jffs2_full_dnode *node; /* NULL for holes */
+- __u32 size;
+- __u32 ofs; /* Don't really need this, but optimisation */
++ uint32_t size;
++ uint32_t ofs; /* Don't really need this, but optimisation */
+ };
+
+ struct jffs2_eraseblock
+ {
+ struct list_head list;
+ int bad_count;
+- __u32 offset; /* of this block in the MTD */
++ uint32_t offset; /* of this block in the MTD */
+
+- __u32 used_size;
+- __u32 dirty_size;
+- __u32 free_size; /* Note that sector_size - free_size
++ uint32_t unchecked_size;
++ uint32_t used_size;
++ uint32_t dirty_size;
++ uint32_t wasted_size;
++ uint32_t free_size; /* Note that sector_size - free_size
+ is the address of the first free space */
+ struct jffs2_raw_node_ref *first_node;
+ struct jffs2_raw_node_ref *last_node;
+@@ -190,45 +199,134 @@
+ };
+
+ #define ACCT_SANITY_CHECK(c, jeb) do { \
+- if (jeb->used_size + jeb->dirty_size + jeb->free_size != c->sector_size) { \
+- printk(KERN_NOTICE "Eeep. Space accounting for block at 0x%08x is screwed\n", jeb->offset); \
+- printk(KERN_NOTICE "free 0x%08x + dirty 0x%08x + used %08x != total %08x\n", \
+- jeb->free_size, jeb->dirty_size, jeb->used_size, c->sector_size); \
++ struct jffs2_eraseblock *___j = jeb; \
++ if ((___j) && ___j->used_size + ___j->dirty_size + ___j->free_size + ___j->wasted_size + ___j->unchecked_size != c->sector_size) { \
++ printk(KERN_NOTICE "Eeep. Space accounting for block at 0x%08x is screwed\n", ___j->offset); \
++ printk(KERN_NOTICE "free 0x%08x + dirty 0x%08x + used %08x + wasted %08x + unchecked %08x != total %08x\n", \
++ ___j->free_size, ___j->dirty_size, ___j->used_size, ___j->wasted_size, ___j->unchecked_size, c->sector_size); \
+ BUG(); \
+ } \
+- if (c->used_size + c->dirty_size + c->free_size + c->erasing_size + c->bad_size != c->flash_size) { \
++ if (c->used_size + c->dirty_size + c->free_size + c->erasing_size + c->bad_size + c->wasted_size + c->unchecked_size != c->flash_size) { \
+ printk(KERN_NOTICE "Eeep. Space accounting superblock info is screwed\n"); \
+- printk(KERN_NOTICE "free 0x%08x + dirty 0x%08x + used %08x + erasing %08x + bad %08x != total %08x\n", \
+- c->free_size, c->dirty_size, c->used_size, c->erasing_size, c->bad_size, c->flash_size); \
++ printk(KERN_NOTICE "free 0x%08x + dirty 0x%08x + used %08x + erasing %08x + bad %08x + wasted %08x + unchecked %08x != total %08x\n", \
++ c->free_size, c->dirty_size, c->used_size, c->erasing_size, c->bad_size, c->wasted_size, c->unchecked_size, c->flash_size); \
+ BUG(); \
+ } \
+ } while(0)
+
++static inline void paranoia_failed_dump(struct jffs2_eraseblock *jeb)
++{
++ struct jffs2_raw_node_ref *ref;
++ int i=0;
++
++ printk(KERN_NOTICE);
++ for (ref = jeb->first_node; ref; ref = ref->next_phys) {
++ printk("%08x->", ref_offset(ref));
++ if (++i == 8) {
++ i = 0;
++ printk("\n" KERN_NOTICE);
++ }
++ }
++ printk("\n");
++}
++
++
+ #define ACCT_PARANOIA_CHECK(jeb) do { \
+- __u32 my_used_size = 0; \
++ uint32_t my_used_size = 0; \
++ uint32_t my_unchecked_size = 0; \
+ struct jffs2_raw_node_ref *ref2 = jeb->first_node; \
+ while (ref2) { \
+- if (!(ref2->flash_offset & 1)) \
+- my_used_size += ref2->totlen; \
++ if (unlikely(ref2->flash_offset < jeb->offset || \
++ ref2->flash_offset > jeb->offset + c->sector_size)) { \
++ printk(KERN_NOTICE "Node %08x shouldn't be in block at %08x!\n", \
++ ref_offset(ref2), jeb->offset); \
++ paranoia_failed_dump(jeb); \
++ BUG(); \
++ } \
++ if (ref_flags(ref2) == REF_UNCHECKED) \
++ my_unchecked_size += ref_totlen(c, jeb, ref2); \
++ else if (!ref_obsolete(ref2)) \
++ my_used_size += ref_totlen(c, jeb, ref2); \
++ if (unlikely((!ref2->next_phys) != (ref2 == jeb->last_node))) { \
++ printk("ref for node at %p (phys %08x) has next_phys->%p (%08x), last_node->%p (phys %08x)\n", \
++ ref2, ref_offset(ref2), ref2->next_phys, ref_offset(ref2->next_phys), \
++ jeb->last_node, ref_offset(jeb->last_node)); \
++ paranoia_failed_dump(jeb); \
++ BUG(); \
++ } \
+ ref2 = ref2->next_phys; \
+ } \
+ if (my_used_size != jeb->used_size) { \
+ printk(KERN_NOTICE "Calculated used size %08x != stored used size %08x\n", my_used_size, jeb->used_size); \
+ BUG(); \
+ } \
++ if (my_unchecked_size != jeb->unchecked_size) { \
++ printk(KERN_NOTICE "Calculated unchecked size %08x != stored unchecked size %08x\n", my_unchecked_size, jeb->unchecked_size); \
++ BUG(); \
++ } \
+ } while(0)
+
++/* Calculate totlen from surrounding nodes or eraseblock */
++static inline uint32_t __ref_totlen(struct jffs2_sb_info *c,
++ struct jffs2_eraseblock *jeb,
++ struct jffs2_raw_node_ref *ref)
++{
++ uint32_t ref_end;
++
++ if (ref->next_phys)
++ ref_end = ref_offset(ref->next_phys);
++ else {
++ if (!jeb)
++ jeb = &c->blocks[ref->flash_offset / c->sector_size];
++
++ /* Last node in block. Use free_space */
++ BUG_ON(ref != jeb->last_node);
++ ref_end = jeb->offset + c->sector_size - jeb->free_size;
++ }
++ return ref_end - ref_offset(ref);
++}
++
++static inline uint32_t ref_totlen(struct jffs2_sb_info *c,
++ struct jffs2_eraseblock *jeb,
++ struct jffs2_raw_node_ref *ref)
++{
++ uint32_t ret;
++
++ D1(if (jeb && jeb != &c->blocks[ref->flash_offset / c->sector_size]) {
++ printk(KERN_CRIT "ref_totlen called with wrong block -- at 0x%08x instead of 0x%08x; ref 0x%08x\n",
++ jeb->offset, c->blocks[ref->flash_offset / c->sector_size].offset, ref_offset(ref));
++ BUG();
++ })
++
++#if 1
++ ret = ref->__totlen;
++#else
++ /* This doesn't actually work yet */
++ ret = __ref_totlen(c, jeb, ref);
++ if (ret != ref->__totlen) {
++ printk(KERN_CRIT "Totlen for ref at %p (0x%08x-0x%08x) miscalculated as 0x%x instead of %x\n",
++ ref, ref_offset(ref), ref_offset(ref)+ref->__totlen,
++ ret, ref->__totlen);
++ if (!jeb)
++ jeb = &c->blocks[ref->flash_offset / c->sector_size];
++ paranoia_failed_dump(jeb);
++ BUG();
++ }
++#endif
++ return ret;
++}
++
++
+ #define ALLOC_NORMAL 0 /* Normal allocation */
+ #define ALLOC_DELETION 1 /* Deletion node. Best to allow it */
+ #define ALLOC_GC 2 /* Space requested for GC. Give it or die */
++#define ALLOC_NORETRY 3 /* For jffs2_write_dnode: On failure, return -EAGAIN instead of retrying */
+
+-#define JFFS2_RESERVED_BLOCKS_BASE 3 /* Number of free blocks there must be before we... */
+-#define JFFS2_RESERVED_BLOCKS_WRITE (JFFS2_RESERVED_BLOCKS_BASE + 2) /* ... allow a normal filesystem write */
+-#define JFFS2_RESERVED_BLOCKS_DELETION (JFFS2_RESERVED_BLOCKS_BASE + 1) /* ... allow a normal filesystem deletion */
+-#define JFFS2_RESERVED_BLOCKS_GCTRIGGER (JFFS2_RESERVED_BLOCKS_BASE + 3) /* ... wake up the GC thread */
+-#define JFFS2_RESERVED_BLOCKS_GCBAD (JFFS2_RESERVED_BLOCKS_BASE + 1) /* ... pick a block from the bad_list to GC */
+-#define JFFS2_RESERVED_BLOCKS_GCMERGE (JFFS2_RESERVED_BLOCKS_BASE) /* ... merge pages when garbage collecting */
++/* How much dirty space before it goes on the very_dirty_list */
++#define VERYDIRTY(c, size) ((size) >= ((c)->sector_size / 2))
+
++/* check if dirty space is more than 255 Byte */
++#define ISDIRTY(size) ((size) > sizeof (struct jffs2_raw_inode) + JFFS2_MIN_DATA_LEN)
+
+ #define PAD(x) (((x)+3)&~3)
+
+@@ -241,43 +339,75 @@
+ return ((struct jffs2_inode_cache *)raw);
+ }
+
++static inline struct jffs2_node_frag *frag_first(struct rb_root *root)
++{
++ struct rb_node *node = root->rb_node;
++
++ if (!node)
++ return NULL;
++ while(node->rb_left)
++ node = node->rb_left;
++ return rb_entry(node, struct jffs2_node_frag, rb);
++}
++#define rb_parent(rb) ((rb)->rb_parent)
++#define frag_next(frag) rb_entry(rb_next(&(frag)->rb), struct jffs2_node_frag, rb)
++#define frag_prev(frag) rb_entry(rb_prev(&(frag)->rb), struct jffs2_node_frag, rb)
++#define frag_parent(frag) rb_entry(rb_parent(&(frag)->rb), struct jffs2_node_frag, rb)
++#define frag_left(frag) rb_entry((frag)->rb.rb_left, struct jffs2_node_frag, rb)
++#define frag_right(frag) rb_entry((frag)->rb.rb_right, struct jffs2_node_frag, rb)
++#define frag_erase(frag, list) rb_erase(&frag->rb, list);
++
+ /* nodelist.c */
+ D1(void jffs2_print_frag_list(struct jffs2_inode_info *f));
+ void jffs2_add_fd_to_list(struct jffs2_sb_info *c, struct jffs2_full_dirent *new, struct jffs2_full_dirent **list);
+-void jffs2_add_tn_to_list(struct jffs2_tmp_dnode_info *tn, struct jffs2_tmp_dnode_info **list);
+ int jffs2_get_inode_nodes(struct jffs2_sb_info *c, ino_t ino, struct jffs2_inode_info *f,
+ struct jffs2_tmp_dnode_info **tnp, struct jffs2_full_dirent **fdp,
+- __u32 *highest_version, __u32 *latest_mctime,
+- __u32 *mctime_ver);
++ uint32_t *highest_version, uint32_t *latest_mctime,
++ uint32_t *mctime_ver);
++void jffs2_set_inocache_state(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic, int state);
+ struct jffs2_inode_cache *jffs2_get_ino_cache(struct jffs2_sb_info *c, uint32_t ino);
+ void jffs2_add_ino_cache (struct jffs2_sb_info *c, struct jffs2_inode_cache *new);
+ void jffs2_del_ino_cache(struct jffs2_sb_info *c, struct jffs2_inode_cache *old);
+ void jffs2_free_ino_caches(struct jffs2_sb_info *c);
+ void jffs2_free_raw_node_refs(struct jffs2_sb_info *c);
++struct jffs2_node_frag *jffs2_lookup_node_frag(struct rb_root *fragtree, uint32_t offset);
++void jffs2_kill_fragtree(struct rb_root *root, struct jffs2_sb_info *c_delete);
++void jffs2_fragtree_insert(struct jffs2_node_frag *newfrag, struct jffs2_node_frag *base);
++struct rb_node *rb_next(struct rb_node *);
++struct rb_node *rb_prev(struct rb_node *);
++void rb_replace_node(struct rb_node *victim, struct rb_node *new, struct rb_root *root);
+
+ /* nodemgmt.c */
+-int jffs2_reserve_space(struct jffs2_sb_info *c, __u32 minsize, __u32 *ofs, __u32 *len, int prio);
+-int jffs2_reserve_space_gc(struct jffs2_sb_info *c, __u32 minsize, __u32 *ofs, __u32 *len);
+-int jffs2_add_physical_node_ref(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *new, __u32 len, int dirty);
++int jffs2_thread_should_wake(struct jffs2_sb_info *c);
++int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len, int prio);
++int jffs2_reserve_space_gc(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len);
++int jffs2_add_physical_node_ref(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *new);
+ void jffs2_complete_reservation(struct jffs2_sb_info *c);
+ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *raw);
++void jffs2_dump_block_lists(struct jffs2_sb_info *c);
+
+ /* write.c */
+-struct inode *jffs2_new_inode (struct inode *dir_i, int mode, struct jffs2_raw_inode *ri);
+-struct jffs2_full_dnode *jffs2_write_dnode(struct inode *inode, struct jffs2_raw_inode *ri, const unsigned char *data, __u32 datalen, __u32 flash_ofs, __u32 *writelen);
+-struct jffs2_full_dirent *jffs2_write_dirent(struct inode *inode, struct jffs2_raw_dirent *rd, const unsigned char *name, __u32 namelen, __u32 flash_ofs, __u32 *writelen);
++int jffs2_do_new_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, uint32_t mode, struct jffs2_raw_inode *ri);
++
++struct jffs2_full_dnode *jffs2_write_dnode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_inode *ri, const unsigned char *data, uint32_t datalen, uint32_t flash_ofs, int alloc_mode);
++struct jffs2_full_dirent *jffs2_write_dirent(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_dirent *rd, const unsigned char *name, uint32_t namelen, uint32_t flash_ofs, int alloc_mode);
++int jffs2_write_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
++ struct jffs2_raw_inode *ri, unsigned char *buf,
++ uint32_t offset, uint32_t writelen, uint32_t *retlen);
++int jffs2_do_create(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, struct jffs2_inode_info *f, struct jffs2_raw_inode *ri, const char *name, int namelen);
++int jffs2_do_unlink(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, const char *name, int namelen, struct jffs2_inode_info *dead_f);
++int jffs2_do_link (struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, uint32_t ino, uint8_t type, const char *name, int namelen);
++
+
+ /* readinode.c */
+-void jffs2_truncate_fraglist (struct jffs2_sb_info *c, struct jffs2_node_frag **list, __u32 size);
+-int jffs2_add_full_dnode_to_fraglist(struct jffs2_sb_info *c, struct jffs2_node_frag **list, struct jffs2_full_dnode *fn);
++void jffs2_truncate_fraglist (struct jffs2_sb_info *c, struct rb_root *list, uint32_t size);
+ int jffs2_add_full_dnode_to_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_full_dnode *fn);
+-void jffs2_read_inode (struct inode *);
+-void jffs2_clear_inode (struct inode *);
++int jffs2_do_read_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
++ uint32_t ino, struct jffs2_raw_inode *latest_node);
++int jffs2_do_crccheck_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic);
++void jffs2_do_clear_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f);
+
+ /* malloc.c */
+-void jffs2_free_tmp_dnode_info_list(struct jffs2_tmp_dnode_info *tn);
+-void jffs2_free_full_dirent_list(struct jffs2_full_dirent *fd);
+-
+ int jffs2_create_slab_caches(void);
+ void jffs2_destroy_slab_caches(void);
+
+@@ -301,54 +431,41 @@
+ /* gc.c */
+ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c);
+
+-/* background.c */
+-int jffs2_start_garbage_collect_thread(struct jffs2_sb_info *c);
+-void jffs2_stop_garbage_collect_thread(struct jffs2_sb_info *c);
+-void jffs2_garbage_collect_trigger(struct jffs2_sb_info *c);
+-
+-/* dir.c */
+-extern struct file_operations jffs2_dir_operations;
+-extern struct inode_operations jffs2_dir_inode_operations;
+-
+-/* file.c */
+-extern struct file_operations jffs2_file_operations;
+-extern struct inode_operations jffs2_file_inode_operations;
+-extern struct address_space_operations jffs2_file_address_operations;
+-int jffs2_null_fsync(struct file *, struct dentry *, int);
+-int jffs2_setattr (struct dentry *dentry, struct iattr *iattr);
+-int jffs2_do_readpage_nolock (struct inode *inode, struct page *pg);
+-int jffs2_do_readpage_unlock (struct inode *inode, struct page *pg);
+-int jffs2_readpage (struct file *, struct page *);
+-int jffs2_prepare_write (struct file *, struct page *, unsigned, unsigned);
+-int jffs2_commit_write (struct file *, struct page *, unsigned, unsigned);
+-
+-/* ioctl.c */
+-int jffs2_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
+-
+ /* read.c */
+ int jffs2_read_dnode(struct jffs2_sb_info *c, struct jffs2_full_dnode *fd, unsigned char *buf, int ofs, int len);
++int jffs2_read_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
++ unsigned char *buf, uint32_t offset, uint32_t len);
++char *jffs2_getlink(struct jffs2_sb_info *c, struct jffs2_inode_info *f);
+
+ /* compr.c */
+-unsigned char jffs2_compress(unsigned char *data_in, unsigned char *cpage_out,
+- __u32 *datalen, __u32 *cdatalen);
++unsigned char jffs2_compress(unsigned char *data_in, unsigned char **cpage_out,
++ uint32_t *datalen, uint32_t *cdatalen);
++void jffs2_free_comprbuf(unsigned char *comprbuf, unsigned char *orig);
+ int jffs2_decompress(unsigned char comprtype, unsigned char *cdata_in,
+- unsigned char *data_out, __u32 cdatalen, __u32 datalen);
++ unsigned char *data_out, uint32_t cdatalen, uint32_t datalen);
+
+ /* scan.c */
+ int jffs2_scan_medium(struct jffs2_sb_info *c);
++void jffs2_rotate_lists(struct jffs2_sb_info *c);
+
+ /* build.c */
+-int jffs2_build_filesystem(struct jffs2_sb_info *c);
+-
+-/* symlink.c */
+-extern struct inode_operations jffs2_symlink_inode_operations;
++int jffs2_do_mount_fs(struct jffs2_sb_info *c);
+
+ /* erase.c */
+ void jffs2_erase_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
+-void jffs2_erase_pending_blocks(struct jffs2_sb_info *c);
+-void jffs2_mark_erased_blocks(struct jffs2_sb_info *c);
+-void jffs2_erase_pending_trigger(struct jffs2_sb_info *c);
++void jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count);
++
++#ifdef CONFIG_JFFS2_FS_NAND
++/* wbuf.c */
++int jffs2_flush_wbuf_gc(struct jffs2_sb_info *c, uint32_t ino);
++int jffs2_flush_wbuf_pad(struct jffs2_sb_info *c);
++int jffs2_check_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
++int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
++int jffs2_nand_read_failcnt(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
++#endif
+
+ /* compr_zlib.c */
+ int jffs2_zlib_init(void);
+ void jffs2_zlib_exit(void);
++
++#endif /* __JFFS2_NODELIST_H__ */
+diff -Nurb linux-mips-2.4.24-pre2/fs/jffs2/nodemgmt.c linux/fs/jffs2/nodemgmt.c
+--- linux-mips-2.4.24-pre2/fs/jffs2/nodemgmt.c 2004-11-17 18:05:04.000000000 +0100
++++ linux/fs/jffs2/nodemgmt.c 2004-11-17 18:17:59.000000000 +0100
+@@ -1,45 +1,21 @@
+ /*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+- * Copyright (C) 2001 Red Hat, Inc.
++ * Copyright (C) 2001-2003 Red Hat, Inc.
+ *
+- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
++ * Created by David Woodhouse <dwmw2@redhat.com>
+ *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
++ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+- * The contents of this file are subject to the Red Hat eCos Public
+- * License Version 1.1 (the "Licence"); you may not use this file
+- * except in compliance with the Licence. You may obtain a copy of
+- * the Licence at http://www.redhat.com/
+- *
+- * Software distributed under the Licence is distributed on an "AS IS"
+- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+- * See the Licence for the specific language governing rights and
+- * limitations under the Licence.
+- *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
+- *
+- * Alternatively, the contents of this file may be used under the
+- * terms of the GNU General Public License version 2 (the "GPL"), in
+- * which case the provisions of the GPL are applicable instead of the
+- * above. If you wish to allow the use of your version of this file
+- * only under the terms of the GPL and not to allow others to use your
+- * version of this file under the RHEPL, indicate your decision by
+- * deleting the provisions above and replace them with the notice and
+- * other provisions required by the GPL. If you do not delete the
+- * provisions above, a recipient may use your version of this file
+- * under either the RHEPL or the GPL.
+- *
+- * $Id$
++ * $Id$
+ *
+ */
+
+ #include <linux/kernel.h>
+ #include <linux/slab.h>
+-#include <linux/jffs2.h>
+ #include <linux/mtd/mtd.h>
+-#include <linux/interrupt.h>
++#include <linux/compiler.h>
++#include <linux/sched.h> /* For cond_resched() */
+ #include "nodelist.h"
+
+ /**
+@@ -62,53 +38,95 @@
+ * for the requested allocation.
+ */
+
+-static int jffs2_do_reserve_space(struct jffs2_sb_info *c, __u32 minsize, __u32 *ofs, __u32 *len);
++static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len);
+
+-int jffs2_reserve_space(struct jffs2_sb_info *c, __u32 minsize, __u32 *ofs, __u32 *len, int prio)
++int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len, int prio)
+ {
+ int ret = -EAGAIN;
+- int blocksneeded = JFFS2_RESERVED_BLOCKS_WRITE;
++ int blocksneeded = c->resv_blocks_write;
+ /* align it */
+ minsize = PAD(minsize);
+
+- if (prio == ALLOC_DELETION)
+- blocksneeded = JFFS2_RESERVED_BLOCKS_DELETION;
+-
+ D1(printk(KERN_DEBUG "jffs2_reserve_space(): Requested 0x%x bytes\n", minsize));
+ down(&c->alloc_sem);
+
+ D1(printk(KERN_DEBUG "jffs2_reserve_space(): alloc sem got\n"));
+
+- spin_lock_bh(&c->erase_completion_lock);
++ spin_lock(&c->erase_completion_lock);
+
+- /* this needs a little more thought */
++ /* this needs a little more thought (true <tglx> :)) */
+ while(ret == -EAGAIN) {
+ while(c->nr_free_blocks + c->nr_erasing_blocks < blocksneeded) {
+ int ret;
++ uint32_t dirty, avail;
++
++ /* calculate real dirty size
++ * dirty_size contains blocks on erase_pending_list
++ * those blocks are counted in c->nr_erasing_blocks.
++ * If one block is actually erased, it is not longer counted as dirty_space
++ * but it is counted in c->nr_erasing_blocks, so we add it and subtract it
++ * with c->nr_erasing_blocks * c->sector_size again.
++ * Blocks on erasable_list are counted as dirty_size, but not in c->nr_erasing_blocks
++ * This helps us to force gc and pick eventually a clean block to spread the load.
++ * We add unchecked_size here, as we hopefully will find some space to use.
++ * This will affect the sum only once, as gc first finishes checking
++ * of nodes.
++ */
++ dirty = c->dirty_size + c->erasing_size - c->nr_erasing_blocks * c->sector_size + c->unchecked_size;
++ if (dirty < c->nospc_dirty_size) {
++ if (prio == ALLOC_DELETION && c->nr_free_blocks + c->nr_erasing_blocks >= c->resv_blocks_deletion) {
++ printk(KERN_NOTICE "jffs2_reserve_space(): Low on dirty space to GC, but it's a deletion. Allowing...\n");
++ break;
++ }
++ D1(printk(KERN_DEBUG "dirty size 0x%08x + unchecked_size 0x%08x < nospc_dirty_size 0x%08x, returning -ENOSPC\n",
++ dirty, c->unchecked_size, c->sector_size));
++
++ spin_unlock(&c->erase_completion_lock);
++ up(&c->alloc_sem);
++ return -ENOSPC;
++ }
+
++ /* Calc possibly available space. Possibly available means that we
++ * don't know, if unchecked size contains obsoleted nodes, which could give us some
++ * more usable space. This will affect the sum only once, as gc first finishes checking
++ * of nodes.
++ + Return -ENOSPC, if the maximum possibly available space is less or equal than
++ * blocksneeded * sector_size.
++ * This blocks endless gc looping on a filesystem, which is nearly full, even if
++ * the check above passes.
++ */
++ avail = c->free_size + c->dirty_size + c->erasing_size + c->unchecked_size;
++ if ( (avail / c->sector_size) <= blocksneeded) {
++ if (prio == ALLOC_DELETION && c->nr_free_blocks + c->nr_erasing_blocks >= c->resv_blocks_deletion) {
++ printk(KERN_NOTICE "jffs2_reserve_space(): Low on possibly available space, but it's a deletion. Allowing...\n");
++ break;
++ }
++
++ D1(printk(KERN_DEBUG "max. available size 0x%08x < blocksneeded * sector_size 0x%08x, returning -ENOSPC\n",
++ avail, blocksneeded * c->sector_size));
++ spin_unlock(&c->erase_completion_lock);
+ up(&c->alloc_sem);
+- if (c->dirty_size < c->sector_size) {
+- D1(printk(KERN_DEBUG "Short on space, but total dirty size 0x%08x < sector size 0x%08x, so -ENOSPC\n", c->dirty_size, c->sector_size));
+- spin_unlock_bh(&c->erase_completion_lock);
+ return -ENOSPC;
+ }
+- D1(printk(KERN_DEBUG "Triggering GC pass. nr_free_blocks %d, nr_erasing_blocks %d, free_size 0x%08x, dirty_size 0x%08x, used_size 0x%08x, erasing_size 0x%08x, bad_size 0x%08x (total 0x%08x of 0x%08x)\n",
+- c->nr_free_blocks, c->nr_erasing_blocks, c->free_size, c->dirty_size, c->used_size, c->erasing_size, c->bad_size,
+- c->free_size + c->dirty_size + c->used_size + c->erasing_size + c->bad_size, c->flash_size));
+- spin_unlock_bh(&c->erase_completion_lock);
++
++ up(&c->alloc_sem);
++
++ D1(printk(KERN_DEBUG "Triggering GC pass. nr_free_blocks %d, nr_erasing_blocks %d, free_size 0x%08x, dirty_size 0x%08x, wasted_size 0x%08x, used_size 0x%08x, erasing_size 0x%08x, bad_size 0x%08x (total 0x%08x of 0x%08x)\n",
++ c->nr_free_blocks, c->nr_erasing_blocks, c->free_size, c->dirty_size, c->wasted_size, c->used_size, c->erasing_size, c->bad_size,
++ c->free_size + c->dirty_size + c->wasted_size + c->used_size + c->erasing_size + c->bad_size, c->flash_size));
++ spin_unlock(&c->erase_completion_lock);
+
+ ret = jffs2_garbage_collect_pass(c);
+ if (ret)
+ return ret;
+
+- if (current->need_resched)
+- schedule();
++ cond_resched();
+
+ if (signal_pending(current))
+ return -EINTR;
+
+ down(&c->alloc_sem);
+- spin_lock_bh(&c->erase_completion_lock);
++ spin_lock(&c->erase_completion_lock);
+ }
+
+ ret = jffs2_do_reserve_space(c, minsize, ofs, len);
+@@ -116,45 +134,72 @@
+ D1(printk(KERN_DEBUG "jffs2_reserve_space: ret is %d\n", ret));
+ }
+ }
+- spin_unlock_bh(&c->erase_completion_lock);
++ spin_unlock(&c->erase_completion_lock);
+ if (ret)
+ up(&c->alloc_sem);
+ return ret;
+ }
+
+-int jffs2_reserve_space_gc(struct jffs2_sb_info *c, __u32 minsize, __u32 *ofs, __u32 *len)
++int jffs2_reserve_space_gc(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len)
+ {
+ int ret = -EAGAIN;
+ minsize = PAD(minsize);
+
+ D1(printk(KERN_DEBUG "jffs2_reserve_space_gc(): Requested 0x%x bytes\n", minsize));
+
+- spin_lock_bh(&c->erase_completion_lock);
++ spin_lock(&c->erase_completion_lock);
+ while(ret == -EAGAIN) {
+ ret = jffs2_do_reserve_space(c, minsize, ofs, len);
+ if (ret) {
+ D1(printk(KERN_DEBUG "jffs2_reserve_space_gc: looping, ret is %d\n", ret));
+ }
+ }
+- spin_unlock_bh(&c->erase_completion_lock);
++ spin_unlock(&c->erase_completion_lock);
+ return ret;
+ }
+
+ /* Called with alloc sem _and_ erase_completion_lock */
+-static int jffs2_do_reserve_space(struct jffs2_sb_info *c, __u32 minsize, __u32 *ofs, __u32 *len)
++static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len)
+ {
+ struct jffs2_eraseblock *jeb = c->nextblock;
+
+ restart:
+ if (jeb && minsize > jeb->free_size) {
+ /* Skip the end of this block and file it as having some dirty space */
+- c->dirty_size += jeb->free_size;
++ /* If there's a pending write to it, flush now */
++ if (jffs2_wbuf_dirty(c)) {
++ spin_unlock(&c->erase_completion_lock);
++ D1(printk(KERN_DEBUG "jffs2_do_reserve_space: Flushing write buffer\n"));
++ jffs2_flush_wbuf_pad(c);
++ spin_lock(&c->erase_completion_lock);
++ jeb = c->nextblock;
++ goto restart;
++ }
++ c->wasted_size += jeb->free_size;
+ c->free_size -= jeb->free_size;
+- jeb->dirty_size += jeb->free_size;
++ jeb->wasted_size += jeb->free_size;
+ jeb->free_size = 0;
++
++ /* Check, if we have a dirty block now, or if it was dirty already */
++ if (ISDIRTY (jeb->wasted_size + jeb->dirty_size)) {
++ c->dirty_size += jeb->wasted_size;
++ c->wasted_size -= jeb->wasted_size;
++ jeb->dirty_size += jeb->wasted_size;
++ jeb->wasted_size = 0;
++ if (VERYDIRTY(c, jeb->dirty_size)) {
++ D1(printk(KERN_DEBUG "Adding full erase block at 0x%08x to very_dirty_list (free 0x%08x, dirty 0x%08x, used 0x%08x\n",
++ jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size));
++ list_add_tail(&jeb->list, &c->very_dirty_list);
++ } else {
+ D1(printk(KERN_DEBUG "Adding full erase block at 0x%08x to dirty_list (free 0x%08x, dirty 0x%08x, used 0x%08x\n",
+ jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size));
+ list_add_tail(&jeb->list, &c->dirty_list);
++ }
++ } else {
++ D1(printk(KERN_DEBUG "Adding full erase block at 0x%08x to clean_list (free 0x%08x, dirty 0x%08x, used 0x%08x\n",
++ jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size));
++ list_add_tail(&jeb->list, &c->clean_list);
++ }
+ c->nextblock = jeb = NULL;
+ }
+
+@@ -164,33 +209,44 @@
+
+ if (list_empty(&c->free_list)) {
+
+- DECLARE_WAITQUEUE(wait, current);
++ if (!c->nr_erasing_blocks &&
++ !list_empty(&c->erasable_list)) {
++ struct jffs2_eraseblock *ejeb;
++
++ ejeb = list_entry(c->erasable_list.next, struct jffs2_eraseblock, list);
++ list_del(&ejeb->list);
++ list_add_tail(&ejeb->list, &c->erase_pending_list);
++ c->nr_erasing_blocks++;
++ jffs2_erase_pending_trigger(c);
++ D1(printk(KERN_DEBUG "jffs2_do_reserve_space: Triggering erase of erasable block at 0x%08x\n",
++ ejeb->offset));
++ }
++
++ if (!c->nr_erasing_blocks &&
++ !list_empty(&c->erasable_pending_wbuf_list)) {
++ D1(printk(KERN_DEBUG "jffs2_do_reserve_space: Flushing write buffer\n"));
++ /* c->nextblock is NULL, no update to c->nextblock allowed */
++ spin_unlock(&c->erase_completion_lock);
++ jffs2_flush_wbuf_pad(c);
++ spin_lock(&c->erase_completion_lock);
++ /* Have another go. It'll be on the erasable_list now */
++ return -EAGAIN;
++ }
+
+ if (!c->nr_erasing_blocks) {
+-// if (list_empty(&c->erasing_list) && list_empty(&c->erase_pending_list) && list_empty(c->erase_complete_list)) {
+ /* Ouch. We're in GC, or we wouldn't have got here.
+ And there's no space left. At all. */
+- printk(KERN_CRIT "Argh. No free space left for GC. nr_erasing_blocks is %d. nr_free_blocks is %d. (erasingempty: %s, erasependingempty: %s)\n",
+- c->nr_erasing_blocks, c->nr_free_blocks, list_empty(&c->erasing_list)?"yes":"no", list_empty(&c->erase_pending_list)?"yes":"no");
++ printk(KERN_CRIT "Argh. No free space left for GC. nr_erasing_blocks is %d. nr_free_blocks is %d. (erasableempty: %s, erasingempty: %s, erasependingempty: %s)\n",
++ c->nr_erasing_blocks, c->nr_free_blocks, list_empty(&c->erasable_list)?"yes":"no",
++ list_empty(&c->erasing_list)?"yes":"no", list_empty(&c->erase_pending_list)?"yes":"no");
+ return -ENOSPC;
+ }
+- /* Make sure this can't deadlock. Someone has to start the erases
+- of erase_pending blocks */
+- set_current_state(TASK_INTERRUPTIBLE);
+- add_wait_queue(&c->erase_wait, &wait);
+- D1(printk(KERN_DEBUG "Waiting for erases to complete. erasing_blocks is %d. (erasingempty: %s, erasependingempty: %s)\n",
+- c->nr_erasing_blocks, list_empty(&c->erasing_list)?"yes":"no", list_empty(&c->erase_pending_list)?"yes":"no"));
+- if (!list_empty(&c->erase_pending_list)) {
+- D1(printk(KERN_DEBUG "Triggering pending erases\n"));
+- jffs2_erase_pending_trigger(c);
+- }
+- spin_unlock_bh(&c->erase_completion_lock);
+- schedule();
+- remove_wait_queue(&c->erase_wait, &wait);
+- spin_lock_bh(&c->erase_completion_lock);
+- if (signal_pending(current)) {
+- return -EINTR;
+- }
++
++ spin_unlock(&c->erase_completion_lock);
++ /* Don't wait for it; just erase one right now */
++ jffs2_erase_pending_blocks(c, 1);
++ spin_lock(&c->erase_completion_lock);
++
+ /* An erase may have failed, decreasing the
+ amount of free space available. So we must
+ restart from the beginning */
+@@ -201,7 +257,8 @@
+ list_del(next);
+ c->nextblock = jeb = list_entry(next, struct jffs2_eraseblock, list);
+ c->nr_free_blocks--;
+- if (jeb->free_size != c->sector_size - sizeof(struct jffs2_unknown_node)) {
++
++ if (jeb->free_size != c->sector_size - c->cleanmarker_size) {
+ printk(KERN_WARNING "Eep. Block 0x%08x taken from free_list had free_size of 0x%08x!!\n", jeb->offset, jeb->free_size);
+ goto restart;
+ }
+@@ -210,6 +267,20 @@
+ enough space */
+ *ofs = jeb->offset + (c->sector_size - jeb->free_size);
+ *len = jeb->free_size;
++
++ if (c->cleanmarker_size && jeb->used_size == c->cleanmarker_size &&
++ !jeb->first_node->next_in_ino) {
++ /* Only node in it beforehand was a CLEANMARKER node (we think).
++ So mark it obsolete now that there's going to be another node
++ in the block. This will reduce used_size to zero but We've
++ already set c->nextblock so that jffs2_mark_node_obsolete()
++ won't try to refile it to the dirty_list.
++ */
++ spin_unlock(&c->erase_completion_lock);
++ jffs2_mark_node_obsolete(c, jeb->first_node);
++ spin_lock(&c->erase_completion_lock);
++ }
++
+ D1(printk(KERN_DEBUG "jffs2_do_reserve_space(): Giving 0x%x bytes at 0x%x\n", *len, *ofs));
+ return 0;
+ }
+@@ -217,9 +288,9 @@
+ /**
+ * jffs2_add_physical_node_ref - add a physical node reference to the list
+ * @c: superblock info
+- * @ofs: physical location of this physical node
++ * @new: new node reference to add
+ * @len: length of this physical node
+- * @ino: inode number with which this physical node is associated
++ * @dirty: dirty flag for new node
+ *
+ * Should only be used to report nodes for which space has been allocated
+ * by jffs2_reserve_space.
+@@ -227,47 +298,58 @@
+ * Must be called with the alloc_sem held.
+ */
+
+-int jffs2_add_physical_node_ref(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *new, __u32 len, int dirty)
++int jffs2_add_physical_node_ref(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *new)
+ {
+ struct jffs2_eraseblock *jeb;
++ uint32_t len;
++
++ jeb = &c->blocks[new->flash_offset / c->sector_size];
++ len = ref_totlen(c, jeb, new);
+
+- len = PAD(len);
+- jeb = &c->blocks[(new->flash_offset & ~3) / c->sector_size];
+- D1(printk(KERN_DEBUG "jffs2_add_physical_node_ref(): Node at 0x%x, size 0x%x\n", new->flash_offset & ~3, len));
++ D1(printk(KERN_DEBUG "jffs2_add_physical_node_ref(): Node at 0x%x(%d), size 0x%x\n", ref_offset(new), ref_flags(new), len));
+ #if 1
+- if (jeb != c->nextblock || (new->flash_offset & ~3) != jeb->offset + (c->sector_size - jeb->free_size)) {
++ if (jeb != c->nextblock || (ref_offset(new)) != jeb->offset + (c->sector_size - jeb->free_size)) {
+ printk(KERN_WARNING "argh. node added in wrong place\n");
+ jffs2_free_raw_node_ref(new);
+ return -EINVAL;
+ }
+ #endif
++ spin_lock(&c->erase_completion_lock);
++
+ if (!jeb->first_node)
+ jeb->first_node = new;
+ if (jeb->last_node)
+ jeb->last_node->next_phys = new;
+ jeb->last_node = new;
+
+- spin_lock_bh(&c->erase_completion_lock);
+ jeb->free_size -= len;
+ c->free_size -= len;
+- if (dirty) {
+- new->flash_offset |= 1;
++ if (ref_obsolete(new)) {
+ jeb->dirty_size += len;
+ c->dirty_size += len;
+ } else {
+ jeb->used_size += len;
+ c->used_size += len;
+ }
+- spin_unlock_bh(&c->erase_completion_lock);
++
+ if (!jeb->free_size && !jeb->dirty_size) {
+ /* If it lives on the dirty_list, jffs2_reserve_space will put it there */
+ D1(printk(KERN_DEBUG "Adding full erase block at 0x%08x to clean_list (free 0x%08x, dirty 0x%08x, used 0x%08x\n",
+ jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size));
++ if (jffs2_wbuf_dirty(c)) {
++ /* Flush the last write in the block if it's outstanding */
++ spin_unlock(&c->erase_completion_lock);
++ jffs2_flush_wbuf_pad(c);
++ spin_lock(&c->erase_completion_lock);
++ }
++
+ list_add_tail(&jeb->list, &c->clean_list);
+ c->nextblock = NULL;
+ }
+ ACCT_SANITY_CHECK(c,jeb);
+- ACCT_PARANOIA_CHECK(jeb);
++ D1(ACCT_PARANOIA_CHECK(jeb));
++
++ spin_unlock(&c->erase_completion_lock);
+
+ return 0;
+ }
+@@ -280,20 +362,34 @@
+ up(&c->alloc_sem);
+ }
+
++static inline int on_list(struct list_head *obj, struct list_head *head)
++{
++ struct list_head *this;
++
++ list_for_each(this, head) {
++ if (this == obj) {
++ D1(printk("%p is on list at %p\n", obj, head));
++ return 1;
++
++ }
++ }
++ return 0;
++}
++
+ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *ref)
+ {
+ struct jffs2_eraseblock *jeb;
+ int blocknr;
+ struct jffs2_unknown_node n;
+- int ret;
+- ssize_t retlen;
++ int ret, addedsize;
++ size_t retlen;
+
+ if(!ref) {
+ printk(KERN_NOTICE "EEEEEK. jffs2_mark_node_obsolete called with NULL node\n");
+ return;
+ }
+- if (ref->flash_offset & 1) {
+- D1(printk(KERN_DEBUG "jffs2_mark_node_obsolete called with already obsolete node at 0x%08x\n", ref->flash_offset &~3));
++ if (ref_obsolete(ref)) {
++ D1(printk(KERN_DEBUG "jffs2_mark_node_obsolete called with already obsolete node at 0x%08x\n", ref_offset(ref)));
+ return;
+ }
+ blocknr = ref->flash_offset / c->sector_size;
+@@ -302,22 +398,63 @@
+ BUG();
+ }
+ jeb = &c->blocks[blocknr];
+- if (jeb->used_size < ref->totlen) {
++
++ spin_lock(&c->erase_completion_lock);
++
++ if (ref_flags(ref) == REF_UNCHECKED) {
++ D1(if (unlikely(jeb->unchecked_size < ref_totlen(c, jeb, ref))) {
++ printk(KERN_NOTICE "raw unchecked node of size 0x%08x freed from erase block %d at 0x%08x, but unchecked_size was already 0x%08x\n",
++ ref_totlen(c, jeb, ref), blocknr, ref->flash_offset, jeb->used_size);
++ BUG();
++ })
++ D1(printk(KERN_DEBUG "Obsoleting previously unchecked node at 0x%08x of len %x: ", ref_offset(ref), ref_totlen(c, jeb, ref)));
++ jeb->unchecked_size -= ref_totlen(c, jeb, ref);
++ c->unchecked_size -= ref_totlen(c, jeb, ref);
++ } else {
++ D1(if (unlikely(jeb->used_size < ref_totlen(c, jeb, ref))) {
+ printk(KERN_NOTICE "raw node of size 0x%08x freed from erase block %d at 0x%08x, but used_size was already 0x%08x\n",
+- ref->totlen, blocknr, ref->flash_offset, jeb->used_size);
++ ref_totlen(c, jeb, ref), blocknr, ref->flash_offset, jeb->used_size);
+ BUG();
++ })
++ D1(printk(KERN_DEBUG "Obsoleting node at 0x%08x of len %x: ", ref_offset(ref), ref_totlen(c, jeb, ref)));
++ jeb->used_size -= ref_totlen(c, jeb, ref);
++ c->used_size -= ref_totlen(c, jeb, ref);
++ }
++
++ // Take care, that wasted size is taken into concern
++ if ((jeb->dirty_size || ISDIRTY(jeb->wasted_size + ref_totlen(c, jeb, ref))) && jeb != c->nextblock) {
++ D1(printk("Dirtying\n"));
++ addedsize = ref_totlen(c, jeb, ref);
++ jeb->dirty_size += ref_totlen(c, jeb, ref);
++ c->dirty_size += ref_totlen(c, jeb, ref);
++
++ /* Convert wasted space to dirty, if not a bad block */
++ if (jeb->wasted_size) {
++ if (on_list(&jeb->list, &c->bad_used_list)) {
++ D1(printk(KERN_DEBUG "Leaving block at %08x on the bad_used_list\n",
++ jeb->offset));
++ addedsize = 0; /* To fool the refiling code later */
++ } else {
++ D1(printk(KERN_DEBUG "Converting %d bytes of wasted space to dirty in block at %08x\n",
++ jeb->wasted_size, jeb->offset));
++ addedsize += jeb->wasted_size;
++ jeb->dirty_size += jeb->wasted_size;
++ c->dirty_size += jeb->wasted_size;
++ c->wasted_size -= jeb->wasted_size;
++ jeb->wasted_size = 0;
+ }
+-
+- spin_lock_bh(&c->erase_completion_lock);
+- jeb->used_size -= ref->totlen;
+- jeb->dirty_size += ref->totlen;
+- c->used_size -= ref->totlen;
+- c->dirty_size += ref->totlen;
+- ref->flash_offset |= 1;
++ }
++ } else {
++ D1(printk("Wasting\n"));
++ addedsize = 0;
++ jeb->wasted_size += ref_totlen(c, jeb, ref);
++ c->wasted_size += ref_totlen(c, jeb, ref);
++ }
++ ref->flash_offset = ref_offset(ref) | REF_OBSOLETE;
+
+ ACCT_SANITY_CHECK(c, jeb);
+
+- ACCT_PARANOIA_CHECK(jeb);
++ D1(ACCT_PARANOIA_CHECK(jeb));
+
+ if (c->flags & JFFS2_SB_FLAG_MOUNTING) {
+ /* Mount in progress. Don't muck about with the block
+@@ -325,68 +462,280 @@
+ obliterate nodes that look obsolete. If they weren't
+ marked obsolete on the flash at the time they _became_
+ obsolete, there was probably a reason for that. */
+- spin_unlock_bh(&c->erase_completion_lock);
++ spin_unlock(&c->erase_completion_lock);
+ return;
+ }
++
+ if (jeb == c->nextblock) {
+ D2(printk(KERN_DEBUG "Not moving nextblock 0x%08x to dirty/erase_pending list\n", jeb->offset));
+- } else if (jeb == c->gcblock) {
+- D2(printk(KERN_DEBUG "Not moving gcblock 0x%08x to dirty/erase_pending list\n", jeb->offset));
+-#if 0 /* We no longer do this here. It can screw the wear levelling. If you have a lot of static
+- data and a few blocks free, and you just create new files and keep deleting/overwriting
+- them, then you'd keep erasing and reusing those blocks without ever moving stuff around.
+- So we leave completely obsoleted blocks on the dirty_list and let the GC delete them
+- when it finds them there. That way, we still get the 'once in a while, take a clean block'
+- to spread out the flash usage */
+- } else if (!jeb->used_size) {
++ } else if (!jeb->used_size && !jeb->unchecked_size) {
++ if (jeb == c->gcblock) {
++ D1(printk(KERN_DEBUG "gcblock at 0x%08x completely dirtied. Clearing gcblock...\n", jeb->offset));
++ c->gcblock = NULL;
++ } else {
+ D1(printk(KERN_DEBUG "Eraseblock at 0x%08x completely dirtied. Removing from (dirty?) list...\n", jeb->offset));
+ list_del(&jeb->list);
++ }
++ if (jffs2_wbuf_dirty(c)) {
++ D1(printk(KERN_DEBUG "...and adding to erasable_pending_wbuf_list\n"));
++ list_add_tail(&jeb->list, &c->erasable_pending_wbuf_list);
++ } else {
++ if (jiffies & 127) {
++ /* Most of the time, we just erase it immediately. Otherwise we
++ spend ages scanning it on mount, etc. */
+ D1(printk(KERN_DEBUG "...and adding to erase_pending_list\n"));
+ list_add_tail(&jeb->list, &c->erase_pending_list);
+ c->nr_erasing_blocks++;
+ jffs2_erase_pending_trigger(c);
+- // OFNI_BS_2SFFJ(c)->s_dirt = 1;
++ } else {
++ /* Sometimes, however, we leave it elsewhere so it doesn't get
++ immediately reused, and we spread the load a bit. */
++ D1(printk(KERN_DEBUG "...and adding to erasable_list\n"));
++ list_add_tail(&jeb->list, &c->erasable_list);
++ }
++ }
+ D1(printk(KERN_DEBUG "Done OK\n"));
+-#endif
+- } else if (jeb->dirty_size == ref->totlen) {
++ } else if (jeb == c->gcblock) {
++ D2(printk(KERN_DEBUG "Not moving gcblock 0x%08x to dirty_list\n", jeb->offset));
++ } else if (ISDIRTY(jeb->dirty_size) && !ISDIRTY(jeb->dirty_size - addedsize)) {
+ D1(printk(KERN_DEBUG "Eraseblock at 0x%08x is freshly dirtied. Removing from clean list...\n", jeb->offset));
+ list_del(&jeb->list);
+ D1(printk(KERN_DEBUG "...and adding to dirty_list\n"));
+ list_add_tail(&jeb->list, &c->dirty_list);
++ } else if (VERYDIRTY(c, jeb->dirty_size) &&
++ !VERYDIRTY(c, jeb->dirty_size - addedsize)) {
++ D1(printk(KERN_DEBUG "Eraseblock at 0x%08x is now very dirty. Removing from dirty list...\n", jeb->offset));
++ list_del(&jeb->list);
++ D1(printk(KERN_DEBUG "...and adding to very_dirty_list\n"));
++ list_add_tail(&jeb->list, &c->very_dirty_list);
++ } else {
++ D1(printk(KERN_DEBUG "Eraseblock at 0x%08x not moved anywhere. (free 0x%08x, dirty 0x%08x, used 0x%08x)\n",
++ jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size));
+ }
+- spin_unlock_bh(&c->erase_completion_lock);
+
+- if (c->mtd->type != MTD_NORFLASH && c->mtd->type != MTD_RAM)
++ spin_unlock(&c->erase_completion_lock);
++
++ if (!jffs2_can_mark_obsolete(c))
+ return;
+- if (OFNI_BS_2SFFJ(c)->s_flags & MS_RDONLY)
++ if (jffs2_is_readonly(c))
+ return;
+
+- D1(printk(KERN_DEBUG "obliterating obsoleted node at 0x%08x\n", ref->flash_offset &~3));
+- ret = c->mtd->read(c->mtd, ref->flash_offset &~3, sizeof(n), &retlen, (char *)&n);
++ D1(printk(KERN_DEBUG "obliterating obsoleted node at 0x%08x\n", ref_offset(ref)));
++ ret = jffs2_flash_read(c, ref_offset(ref), sizeof(n), &retlen, (char *)&n);
+ if (ret) {
+- printk(KERN_WARNING "Read error reading from obsoleted node at 0x%08x: %d\n", ref->flash_offset &~3, ret);
++ printk(KERN_WARNING "Read error reading from obsoleted node at 0x%08x: %d\n", ref_offset(ref), ret);
+ return;
+ }
+ if (retlen != sizeof(n)) {
+- printk(KERN_WARNING "Short read from obsoleted node at 0x%08x: %d\n", ref->flash_offset &~3, retlen);
++ printk(KERN_WARNING "Short read from obsoleted node at 0x%08x: %zd\n", ref_offset(ref), retlen);
+ return;
+ }
+- if (PAD(n.totlen) != PAD(ref->totlen)) {
+- printk(KERN_WARNING "Node totlen on flash (0x%08x) != totlen in node ref (0x%08x)\n", n.totlen, ref->totlen);
++ if (PAD(je32_to_cpu(n.totlen)) != PAD(ref_totlen(c, jeb, ref))) {
++ printk(KERN_WARNING "Node totlen on flash (0x%08x) != totlen from node ref (0x%08x)\n", je32_to_cpu(n.totlen), ref_totlen(c, jeb, ref));
+ return;
+ }
+- if (!(n.nodetype & JFFS2_NODE_ACCURATE)) {
+- D1(printk(KERN_DEBUG "Node at 0x%08x was already marked obsolete (nodetype 0x%04x\n", ref->flash_offset &~3, n.nodetype));
++ if (!(je16_to_cpu(n.nodetype) & JFFS2_NODE_ACCURATE)) {
++ D1(printk(KERN_DEBUG "Node at 0x%08x was already marked obsolete (nodetype 0x%04x)\n", ref_offset(ref), je16_to_cpu(n.nodetype)));
+ return;
+ }
+- n.nodetype &= ~JFFS2_NODE_ACCURATE;
+- ret = c->mtd->write(c->mtd, ref->flash_offset&~3, sizeof(n), &retlen, (char *)&n);
++ /* XXX FIXME: This is ugly now */
++ n.nodetype = cpu_to_je16(je16_to_cpu(n.nodetype) & ~JFFS2_NODE_ACCURATE);
++ ret = jffs2_flash_write(c, ref_offset(ref), sizeof(n), &retlen, (char *)&n);
+ if (ret) {
+- printk(KERN_WARNING "Write error in obliterating obsoleted node at 0x%08x: %d\n", ref->flash_offset &~3, ret);
++ printk(KERN_WARNING "Write error in obliterating obsoleted node at 0x%08x: %d\n", ref_offset(ref), ret);
+ return;
+ }
+ if (retlen != sizeof(n)) {
+- printk(KERN_WARNING "Short write in obliterating obsoleted node at 0x%08x: %d\n", ref->flash_offset &~3, retlen);
++ printk(KERN_WARNING "Short write in obliterating obsoleted node at 0x%08x: %zd\n", ref_offset(ref), retlen);
+ return;
+ }
+ }
++
++#if CONFIG_JFFS2_FS_DEBUG > 0
++void jffs2_dump_block_lists(struct jffs2_sb_info *c)
++{
++
++
++ printk(KERN_DEBUG "jffs2_dump_block_lists:\n");
++ printk(KERN_DEBUG "flash_size: %08x\n", c->flash_size);
++ printk(KERN_DEBUG "used_size: %08x\n", c->used_size);
++ printk(KERN_DEBUG "dirty_size: %08x\n", c->dirty_size);
++ printk(KERN_DEBUG "wasted_size: %08x\n", c->wasted_size);
++ printk(KERN_DEBUG "unchecked_size: %08x\n", c->unchecked_size);
++ printk(KERN_DEBUG "free_size: %08x\n", c->free_size);
++ printk(KERN_DEBUG "erasing_size: %08x\n", c->erasing_size);
++ printk(KERN_DEBUG "bad_size: %08x\n", c->bad_size);
++ printk(KERN_DEBUG "sector_size: %08x\n", c->sector_size);
++ printk(KERN_DEBUG "jffs2_reserved_blocks size: %08x\n",c->sector_size * c->resv_blocks_write);
++
++ if (c->nextblock) {
++ printk(KERN_DEBUG "nextblock: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n",
++ c->nextblock->offset, c->nextblock->used_size, c->nextblock->dirty_size, c->nextblock->wasted_size, c->nextblock->unchecked_size, c->nextblock->free_size);
++ } else {
++ printk(KERN_DEBUG "nextblock: NULL\n");
++ }
++ if (c->gcblock) {
++ printk(KERN_DEBUG "gcblock: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n",
++ c->gcblock->offset, c->gcblock->used_size, c->gcblock->dirty_size, c->gcblock->wasted_size, c->gcblock->unchecked_size, c->gcblock->free_size);
++ } else {
++ printk(KERN_DEBUG "gcblock: NULL\n");
++ }
++ if (list_empty(&c->clean_list)) {
++ printk(KERN_DEBUG "clean_list: empty\n");
++ } else {
++ struct list_head *this;
++ int numblocks = 0;
++ uint32_t dirty = 0;
++
++ list_for_each(this, &c->clean_list) {
++ struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
++ numblocks ++;
++ dirty += jeb->wasted_size;
++ printk(KERN_DEBUG "clean_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n", jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size);
++ }
++ printk (KERN_DEBUG "Contains %d blocks with total wasted size %u, average wasted size: %u\n", numblocks, dirty, dirty / numblocks);
++ }
++ if (list_empty(&c->very_dirty_list)) {
++ printk(KERN_DEBUG "very_dirty_list: empty\n");
++ } else {
++ struct list_head *this;
++ int numblocks = 0;
++ uint32_t dirty = 0;
++
++ list_for_each(this, &c->very_dirty_list) {
++ struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
++ numblocks ++;
++ dirty += jeb->dirty_size;
++ printk(KERN_DEBUG "very_dirty_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n",
++ jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size);
++ }
++ printk (KERN_DEBUG "Contains %d blocks with total dirty size %u, average dirty size: %u\n",
++ numblocks, dirty, dirty / numblocks);
++ }
++ if (list_empty(&c->dirty_list)) {
++ printk(KERN_DEBUG "dirty_list: empty\n");
++ } else {
++ struct list_head *this;
++ int numblocks = 0;
++ uint32_t dirty = 0;
++
++ list_for_each(this, &c->dirty_list) {
++ struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
++ numblocks ++;
++ dirty += jeb->dirty_size;
++ printk(KERN_DEBUG "dirty_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n",
++ jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size);
++ }
++ printk (KERN_DEBUG "Contains %d blocks with total dirty size %u, average dirty size: %u\n",
++ numblocks, dirty, dirty / numblocks);
++ }
++ if (list_empty(&c->erasable_list)) {
++ printk(KERN_DEBUG "erasable_list: empty\n");
++ } else {
++ struct list_head *this;
++
++ list_for_each(this, &c->erasable_list) {
++ struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
++ printk(KERN_DEBUG "erasable_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n",
++ jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size);
++ }
++ }
++ if (list_empty(&c->erasing_list)) {
++ printk(KERN_DEBUG "erasing_list: empty\n");
++ } else {
++ struct list_head *this;
++
++ list_for_each(this, &c->erasing_list) {
++ struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
++ printk(KERN_DEBUG "erasing_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n",
++ jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size);
++ }
++ }
++ if (list_empty(&c->erase_pending_list)) {
++ printk(KERN_DEBUG "erase_pending_list: empty\n");
++ } else {
++ struct list_head *this;
++
++ list_for_each(this, &c->erase_pending_list) {
++ struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
++ printk(KERN_DEBUG "erase_pending_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n",
++ jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size);
++ }
++ }
++ if (list_empty(&c->erasable_pending_wbuf_list)) {
++ printk(KERN_DEBUG "erasable_pending_wbuf_list: empty\n");
++ } else {
++ struct list_head *this;
++
++ list_for_each(this, &c->erasable_pending_wbuf_list) {
++ struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
++ printk(KERN_DEBUG "erasable_pending_wbuf_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n",
++ jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size);
++ }
++ }
++ if (list_empty(&c->free_list)) {
++ printk(KERN_DEBUG "free_list: empty\n");
++ } else {
++ struct list_head *this;
++
++ list_for_each(this, &c->free_list) {
++ struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
++ printk(KERN_DEBUG "free_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n",
++ jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size);
++ }
++ }
++ if (list_empty(&c->bad_list)) {
++ printk(KERN_DEBUG "bad_list: empty\n");
++ } else {
++ struct list_head *this;
++
++ list_for_each(this, &c->bad_list) {
++ struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
++ printk(KERN_DEBUG "bad_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n",
++ jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size);
++ }
++ }
++ if (list_empty(&c->bad_used_list)) {
++ printk(KERN_DEBUG "bad_used_list: empty\n");
++ } else {
++ struct list_head *this;
++
++ list_for_each(this, &c->bad_used_list) {
++ struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
++ printk(KERN_DEBUG "bad_used_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n",
++ jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size);
++ }
++ }
++}
++#endif /* CONFIG_JFFS2_FS_DEBUG */
++
++int jffs2_thread_should_wake(struct jffs2_sb_info *c)
++{
++ int ret = 0;
++ uint32_t dirty;
++
++ if (c->unchecked_size) {
++ D1(printk(KERN_DEBUG "jffs2_thread_should_wake(): unchecked_size %d, checked_ino #%d\n",
++ c->unchecked_size, c->checked_ino));
++ return 1;
++ }
++
++ /* dirty_size contains blocks on erase_pending_list
++ * those blocks are counted in c->nr_erasing_blocks.
++ * If one block is actually erased, it is not longer counted as dirty_space
++ * but it is counted in c->nr_erasing_blocks, so we add it and subtract it
++ * with c->nr_erasing_blocks * c->sector_size again.
++ * Blocks on erasable_list are counted as dirty_size, but not in c->nr_erasing_blocks
++ * This helps us to force gc and pick eventually a clean block to spread the load.
++ */
++ dirty = c->dirty_size + c->erasing_size - c->nr_erasing_blocks * c->sector_size;
++
++ if (c->nr_free_blocks + c->nr_erasing_blocks < c->resv_blocks_gctrigger &&
++ (dirty > c->nospc_dirty_size))
++ ret = 1;
++
++ D1(printk(KERN_DEBUG "jffs2_thread_should_wake(): nr_free_blocks %d, nr_erasing_blocks %d, dirty_size 0x%x: %s\n",
++ c->nr_free_blocks, c->nr_erasing_blocks, c->dirty_size, ret?"yes":"no"));
++
++ return ret;
++}
+diff -Nurb linux-mips-2.4.24-pre2/fs/jffs2/os-linux.h linux/fs/jffs2/os-linux.h
+--- linux-mips-2.4.24-pre2/fs/jffs2/os-linux.h 1970-01-01 01:00:00.000000000 +0100
++++ linux/fs/jffs2/os-linux.h 2004-11-17 18:17:59.365264368 +0100
+@@ -0,0 +1,212 @@
++/*
++ * JFFS2 -- Journalling Flash File System, Version 2.
++ *
++ * Copyright (C) 2002-2003 Red Hat, Inc.
++ *
++ * Created by David Woodhouse <dwmw2@redhat.com>
++ *
++ * For licensing information, see the file 'LICENCE' in this directory.
++ *
++ * $Id$
++ *
++ */
++
++#ifndef __JFFS2_OS_LINUX_H__
++#define __JFFS2_OS_LINUX_H__
++#include <linux/version.h>
++
++/* JFFS2 uses Linux mode bits natively -- no need for conversion */
++#define os_to_jffs2_mode(x) (x)
++#define jffs2_to_os_mode(x) (x)
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,73)
++#define kstatfs statfs
++#endif
++
++#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,2)
++#define JFFS2_INODE_INFO(i) (list_entry(i, struct jffs2_inode_info, vfs_inode))
++#define OFNI_EDONI_2SFFJ(f) (&(f)->vfs_inode)
++#define JFFS2_SB_INFO(sb) (sb->s_fs_info)
++#define OFNI_BS_2SFFJ(c) ((struct super_block *)c->os_priv)
++#elif defined(JFFS2_OUT_OF_KERNEL)
++#define JFFS2_INODE_INFO(i) ((struct jffs2_inode_info *) &(i)->u)
++#define OFNI_EDONI_2SFFJ(f) ((struct inode *) ( ((char *)f) - ((char *)(&((struct inode *)NULL)->u)) ) )
++#define JFFS2_SB_INFO(sb) ((struct jffs2_sb_info *) &(sb)->u)
++#define OFNI_BS_2SFFJ(c) ((struct super_block *) ( ((char *)c) - ((char *)(&((struct super_block *)NULL)->u)) ) )
++#else
++#define JFFS2_INODE_INFO(i) (&i->u.jffs2_i)
++#define OFNI_EDONI_2SFFJ(f) ((struct inode *) ( ((char *)f) - ((char *)(&((struct inode *)NULL)->u)) ) )
++#define JFFS2_SB_INFO(sb) (&sb->u.jffs2_sb)
++#define OFNI_BS_2SFFJ(c) ((struct super_block *) ( ((char *)c) - ((char *)(&((struct super_block *)NULL)->u)) ) )
++#endif
++
++
++#define JFFS2_F_I_SIZE(f) (OFNI_EDONI_2SFFJ(f)->i_size)
++#define JFFS2_F_I_MODE(f) (OFNI_EDONI_2SFFJ(f)->i_mode)
++#define JFFS2_F_I_UID(f) (OFNI_EDONI_2SFFJ(f)->i_uid)
++#define JFFS2_F_I_GID(f) (OFNI_EDONI_2SFFJ(f)->i_gid)
++
++#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,1)
++#define JFFS2_F_I_RDEV_MIN(f) (iminor(OFNI_EDONI_2SFFJ(f)))
++#define JFFS2_F_I_RDEV_MAJ(f) (imajor(OFNI_EDONI_2SFFJ(f)))
++#else
++#define JFFS2_F_I_RDEV_MIN(f) (MINOR(to_kdev_t(OFNI_EDONI_2SFFJ(f)->i_rdev)))
++#define JFFS2_F_I_RDEV_MAJ(f) (MAJOR(to_kdev_t(OFNI_EDONI_2SFFJ(f)->i_rdev)))
++#endif
++
++/* Urgh. The things we do to keep the 2.4 build working */
++#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,47)
++#define ITIME(sec) ((struct timespec){sec, 0})
++#define I_SEC(tv) ((tv).tv_sec)
++#define JFFS2_F_I_CTIME(f) (OFNI_EDONI_2SFFJ(f)->i_ctime.tv_sec)
++#define JFFS2_F_I_MTIME(f) (OFNI_EDONI_2SFFJ(f)->i_mtime.tv_sec)
++#define JFFS2_F_I_ATIME(f) (OFNI_EDONI_2SFFJ(f)->i_atime.tv_sec)
++#else
++#define ITIME(x) (x)
++#define I_SEC(x) (x)
++#define JFFS2_F_I_CTIME(f) (OFNI_EDONI_2SFFJ(f)->i_ctime)
++#define JFFS2_F_I_MTIME(f) (OFNI_EDONI_2SFFJ(f)->i_mtime)
++#define JFFS2_F_I_ATIME(f) (OFNI_EDONI_2SFFJ(f)->i_atime)
++#endif
++
++#define sleep_on_spinunlock(wq, s) \
++ do { \
++ DECLARE_WAITQUEUE(__wait, current); \
++ add_wait_queue((wq), &__wait); \
++ set_current_state(TASK_UNINTERRUPTIBLE); \
++ spin_unlock(s); \
++ schedule(); \
++ remove_wait_queue((wq), &__wait); \
++ } while(0)
++
++static inline void jffs2_init_inode_info(struct jffs2_inode_info *f)
++{
++#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,2)
++ f->highest_version = 0;
++ f->fragtree = RB_ROOT;
++ f->metadata = NULL;
++ f->dents = NULL;
++ f->flags = 0;
++ f->usercompr = 0;
++#else
++ memset(f, 0, sizeof(*f));
++ init_MUTEX_LOCKED(&f->sem);
++#endif
++}
++
++#define jffs2_is_readonly(c) (OFNI_BS_2SFFJ(c)->s_flags & MS_RDONLY)
++
++#ifndef CONFIG_JFFS2_FS_NAND
++#define jffs2_can_mark_obsolete(c) (1)
++#define jffs2_cleanmarker_oob(c) (0)
++#define jffs2_write_nand_cleanmarker(c,jeb) (-EIO)
++
++#define jffs2_flash_write(c, ofs, len, retlen, buf) ((c)->mtd->write((c)->mtd, ofs, len, retlen, buf))
++#define jffs2_flash_read(c, ofs, len, retlen, buf) ((c)->mtd->read((c)->mtd, ofs, len, retlen, buf))
++#define jffs2_flush_wbuf_pad(c) ({ (void)(c), 0; })
++#define jffs2_flush_wbuf_gc(c, i) ({ (void)(c), (void) i, 0; })
++#define jffs2_nand_read_failcnt(c,jeb) do { ; } while(0)
++#define jffs2_write_nand_badblock(c,jeb) do { ; } while(0)
++#define jffs2_nand_flash_setup(c) (0)
++#define jffs2_nand_flash_cleanup(c) do {} while(0)
++#define jffs2_wbuf_dirty(c) (0)
++#define jffs2_flash_writev(a,b,c,d,e,f) jffs2_flash_direct_writev(a,b,c,d,e)
++#define jffs2_wbuf_timeout NULL
++#define jffs2_wbuf_process NULL
++
++#else /* NAND support present */
++
++#define jffs2_can_mark_obsolete(c) (c->mtd->type == MTD_NORFLASH || c->mtd->type == MTD_RAM)
++#define jffs2_cleanmarker_oob(c) (c->mtd->type == MTD_NANDFLASH)
++
++#define jffs2_flash_write_oob(c, ofs, len, retlen, buf) ((c)->mtd->write_oob((c)->mtd, ofs, len, retlen, buf))
++#define jffs2_flash_read_oob(c, ofs, len, retlen, buf) ((c)->mtd->read_oob((c)->mtd, ofs, len, retlen, buf))
++#define jffs2_wbuf_dirty(c) (!!(c)->wbuf_len)
++struct kstatfs;
++
++/* wbuf.c */
++int jffs2_flash_writev(struct jffs2_sb_info *c, const struct iovec *vecs, unsigned long count, loff_t to, size_t *retlen, uint32_t ino);
++int jffs2_flash_write(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *retlen, const u_char *buf);
++int jffs2_flash_read(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *retlen, u_char *buf);
++int jffs2_check_oob_empty(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,int mode);
++int jffs2_check_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
++int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
++int jffs2_write_nand_badblock(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
++void jffs2_wbuf_timeout(unsigned long data);
++void jffs2_wbuf_process(void *data);
++int jffs2_nand_flash_setup(struct jffs2_sb_info *c);
++void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c);
++#endif /* NAND */
++
++/* erase.c */
++static inline void jffs2_erase_pending_trigger(struct jffs2_sb_info *c)
++{
++ OFNI_BS_2SFFJ(c)->s_dirt = 1;
++}
++
++/* background.c */
++int jffs2_start_garbage_collect_thread(struct jffs2_sb_info *c);
++void jffs2_stop_garbage_collect_thread(struct jffs2_sb_info *c);
++void jffs2_garbage_collect_trigger(struct jffs2_sb_info *c);
++
++/* dir.c */
++extern struct file_operations jffs2_dir_operations;
++extern struct inode_operations jffs2_dir_inode_operations;
++
++/* file.c */
++extern struct file_operations jffs2_file_operations;
++extern struct inode_operations jffs2_file_inode_operations;
++extern struct address_space_operations jffs2_file_address_operations;
++int jffs2_fsync(struct file *, struct dentry *, int);
++int jffs2_do_readpage_nolock (struct inode *inode, struct page *pg);
++int jffs2_do_readpage_unlock (struct inode *inode, struct page *pg);
++int jffs2_readpage (struct file *, struct page *);
++int jffs2_prepare_write (struct file *, struct page *, unsigned, unsigned);
++int jffs2_commit_write (struct file *, struct page *, unsigned, unsigned);
++
++/* ioctl.c */
++int jffs2_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
++
++/* symlink.c */
++extern struct inode_operations jffs2_symlink_inode_operations;
++
++/* fs.c */
++int jffs2_setattr (struct dentry *, struct iattr *);
++void jffs2_read_inode (struct inode *);
++void jffs2_clear_inode (struct inode *);
++void jffs2_dirty_inode(struct inode *inode);
++struct inode *jffs2_new_inode (struct inode *dir_i, int mode,
++ struct jffs2_raw_inode *ri);
++int jffs2_statfs (struct super_block *, struct kstatfs *);
++void jffs2_write_super (struct super_block *);
++int jffs2_remount_fs (struct super_block *, int *, char *);
++int jffs2_do_fill_super(struct super_block *sb, void *data, int silent);
++void jffs2_gc_release_inode(struct jffs2_sb_info *c,
++ struct jffs2_inode_info *f);
++struct jffs2_inode_info *jffs2_gc_fetch_inode(struct jffs2_sb_info *c,
++ int inum, int nlink);
++
++unsigned char *jffs2_gc_fetch_page(struct jffs2_sb_info *c,
++ struct jffs2_inode_info *f,
++ unsigned long offset,
++ unsigned long *priv);
++void jffs2_gc_release_page(struct jffs2_sb_info *c,
++ unsigned char *pg,
++ unsigned long *priv);
++
++
++/* writev.c */
++int jffs2_flash_direct_writev(struct jffs2_sb_info *c, const struct iovec *vecs,
++ unsigned long count, loff_t to, size_t *retlen);
++
++/* Compression config */
++#define JFFS2_COMPRESSION
++#undef JFFS2_USE_DYNRUBIN /* Disabled 23/9/1. With zlib it hardly ever gets a look in */
++#undef JFFS2_USE_RUBINMIPS /* Disabled 26/2/1. Obsoleted by dynrubin */
++#define JFFS2_USE_ZLIB
++#define JFFS2_USE_RTIME /* rtime does manage to recompress already-compressed data */
++
++
++#endif /* __JFFS2_OS_LINUX_H__ */
++
++
+diff -Nurb linux-mips-2.4.24-pre2/fs/jffs2/pushpull.h linux/fs/jffs2/pushpull.h
+--- linux-mips-2.4.24-pre2/fs/jffs2/pushpull.h 2004-11-17 18:05:04.000000000 +0100
++++ linux/fs/jffs2/pushpull.h 2004-11-17 18:17:59.000000000 +0100
+@@ -1,42 +1,21 @@
+ /*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+- * Copyright (C) 2001 Red Hat, Inc.
++ * Copyright (C) 2001, 2002 Red Hat, Inc.
+ *
+- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
++ * Created by David Woodhouse <dwmw2@redhat.com>
+ *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
++ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+- * The contents of this file are subject to the Red Hat eCos Public
+- * License Version 1.1 (the "Licence"); you may not use this file
+- * except in compliance with the Licence. You may obtain a copy of
+- * the Licence at http://www.redhat.com/
+- *
+- * Software distributed under the Licence is distributed on an "AS IS"
+- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+- * See the Licence for the specific language governing rights and
+- * limitations under the Licence.
+- *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
+- *
+- * Alternatively, the contents of this file may be used under the
+- * terms of the GNU General Public License version 2 (the "GPL"), in
+- * which case the provisions of the GPL are applicable instead of the
+- * above. If you wish to allow the use of your version of this file
+- * only under the terms of the GPL and not to allow others to use your
+- * version of this file under the RHEPL, indicate your decision by
+- * deleting the provisions above and replace them with the notice and
+- * other provisions required by the GPL. If you do not delete the
+- * provisions above, a recipient may use your version of this file
+- * under either the RHEPL or the GPL.
+- *
+- * $Id$
++ * $Id$
+ *
+ */
+
+ #ifndef __PUSHPULL_H__
+ #define __PUSHPULL_H__
++
++#include <linux/errno.h>
++
+ struct pushpull {
+ unsigned char *buf;
+ unsigned int buflen;
+@@ -44,9 +23,36 @@
+ unsigned int reserve;
+ };
+
+-void init_pushpull(struct pushpull *, char *, unsigned, unsigned, unsigned);
+-int pushbit(struct pushpull *pp, int bit, int use_reserved);
+-int pushedbits(struct pushpull *pp);
++
++static inline void init_pushpull(struct pushpull *pp, char *buf, unsigned buflen, unsigned ofs, unsigned reserve)
++{
++ pp->buf = buf;
++ pp->buflen = buflen;
++ pp->ofs = ofs;
++ pp->reserve = reserve;
++}
++
++static inline int pushbit(struct pushpull *pp, int bit, int use_reserved)
++{
++ if (pp->ofs >= pp->buflen - (use_reserved?0:pp->reserve)) {
++ return -ENOSPC;
++ }
++
++ if (bit) {
++ pp->buf[pp->ofs >> 3] |= (1<<(7-(pp->ofs &7)));
++ }
++ else {
++ pp->buf[pp->ofs >> 3] &= ~(1<<(7-(pp->ofs &7)));
++ }
++ pp->ofs++;
++
++ return 0;
++}
++
++static inline int pushedbits(struct pushpull *pp)
++{
++ return pp->ofs;
++}
+
+ static inline int pullbit(struct pushpull *pp)
+ {
+diff -Nurb linux-mips-2.4.24-pre2/fs/jffs2/rbtree.c linux/fs/jffs2/rbtree.c
+--- linux-mips-2.4.24-pre2/fs/jffs2/rbtree.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/fs/jffs2/rbtree.c 2004-11-17 18:17:59.368263912 +0100
+@@ -0,0 +1,363 @@
++/*
++ Red Black Trees
++ (C) 1999 Andrea Arcangeli <andrea@suse.de>
++ (C) 2002 David Woodhouse <dwmw2@infradead.org>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License as published by
++ the Free Software Foundation; either version 2 of the License, or
++ (at your option) any later version.
++
++ This program is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ GNU General Public License for more details.
++
++ You should have received a copy of the GNU General Public License
++ along with this program; if not, write to the Free Software
++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++
++ $Id$
++*/
++
++#ifdef __ECOS /* This file is _not_ under the eCos licence; it is pure GPL. */
++#error "Licence problem. eCos has its own rbtree code."
++#endif
++
++#include <linux/version.h>
++#include <linux/rbtree.h>
++
++/* This wasn't present till 2.4.11, wasn't exported till 2.4.19 */
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,11) || \
++ (LINUX_VERSION_CODE < KERNEL_VERSION(2,4,19) && defined(MODULE))
++static void __rb_rotate_left(struct rb_node * node, struct rb_root * root)
++{
++ struct rb_node * right = node->rb_right;
++
++ if ((node->rb_right = right->rb_left))
++ right->rb_left->rb_parent = node;
++ right->rb_left = node;
++
++ if ((right->rb_parent = node->rb_parent))
++ {
++ if (node == node->rb_parent->rb_left)
++ node->rb_parent->rb_left = right;
++ else
++ node->rb_parent->rb_right = right;
++ }
++ else
++ root->rb_node = right;
++ node->rb_parent = right;
++}
++
++static void __rb_rotate_right(struct rb_node * node, struct rb_root * root)
++{
++ struct rb_node * left = node->rb_left;
++
++ if ((node->rb_left = left->rb_right))
++ left->rb_right->rb_parent = node;
++ left->rb_right = node;
++
++ if ((left->rb_parent = node->rb_parent))
++ {
++ if (node == node->rb_parent->rb_right)
++ node->rb_parent->rb_right = left;
++ else
++ node->rb_parent->rb_left = left;
++ }
++ else
++ root->rb_node = left;
++ node->rb_parent = left;
++}
++
++void rb_insert_color(struct rb_node * node, struct rb_root * root)
++{
++ struct rb_node * parent, * gparent;
++
++ while ((parent = node->rb_parent) && parent->rb_color == RB_RED)
++ {
++ gparent = parent->rb_parent;
++
++ if (parent == gparent->rb_left)
++ {
++ {
++ register struct rb_node * uncle = gparent->rb_right;
++ if (uncle && uncle->rb_color == RB_RED)
++ {
++ uncle->rb_color = RB_BLACK;
++ parent->rb_color = RB_BLACK;
++ gparent->rb_color = RB_RED;
++ node = gparent;
++ continue;
++ }
++ }
++
++ if (parent->rb_right == node)
++ {
++ register struct rb_node * tmp;
++ __rb_rotate_left(parent, root);
++ tmp = parent;
++ parent = node;
++ node = tmp;
++ }
++
++ parent->rb_color = RB_BLACK;
++ gparent->rb_color = RB_RED;
++ __rb_rotate_right(gparent, root);
++ } else {
++ {
++ register struct rb_node * uncle = gparent->rb_left;
++ if (uncle && uncle->rb_color == RB_RED)
++ {
++ uncle->rb_color = RB_BLACK;
++ parent->rb_color = RB_BLACK;
++ gparent->rb_color = RB_RED;
++ node = gparent;
++ continue;
++ }
++ }
++
++ if (parent->rb_left == node)
++ {
++ register struct rb_node * tmp;
++ __rb_rotate_right(parent, root);
++ tmp = parent;
++ parent = node;
++ node = tmp;
++ }
++
++ parent->rb_color = RB_BLACK;
++ gparent->rb_color = RB_RED;
++ __rb_rotate_left(gparent, root);
++ }
++ }
++
++ root->rb_node->rb_color = RB_BLACK;
++}
++
++static void __rb_erase_color(struct rb_node * node, struct rb_node * parent,
++ struct rb_root * root)
++{
++ struct rb_node * other;
++
++ while ((!node || node->rb_color == RB_BLACK) && node != root->rb_node)
++ {
++ if (parent->rb_left == node)
++ {
++ other = parent->rb_right;
++ if (other->rb_color == RB_RED)
++ {
++ other->rb_color = RB_BLACK;
++ parent->rb_color = RB_RED;
++ __rb_rotate_left(parent, root);
++ other = parent->rb_right;
++ }
++ if ((!other->rb_left ||
++ other->rb_left->rb_color == RB_BLACK)
++ && (!other->rb_right ||
++ other->rb_right->rb_color == RB_BLACK))
++ {
++ other->rb_color = RB_RED;
++ node = parent;
++ parent = node->rb_parent;
++ }
++ else
++ {
++ if (!other->rb_right ||
++ other->rb_right->rb_color == RB_BLACK)
++ {
++ register struct rb_node * o_left;
++ if ((o_left = other->rb_left))
++ o_left->rb_color = RB_BLACK;
++ other->rb_color = RB_RED;
++ __rb_rotate_right(other, root);
++ other = parent->rb_right;
++ }
++ other->rb_color = parent->rb_color;
++ parent->rb_color = RB_BLACK;
++ if (other->rb_right)
++ other->rb_right->rb_color = RB_BLACK;
++ __rb_rotate_left(parent, root);
++ node = root->rb_node;
++ break;
++ }
++ }
++ else
++ {
++ other = parent->rb_left;
++ if (other->rb_color == RB_RED)
++ {
++ other->rb_color = RB_BLACK;
++ parent->rb_color = RB_RED;
++ __rb_rotate_right(parent, root);
++ other = parent->rb_left;
++ }
++ if ((!other->rb_left ||
++ other->rb_left->rb_color == RB_BLACK)
++ && (!other->rb_right ||
++ other->rb_right->rb_color == RB_BLACK))
++ {
++ other->rb_color = RB_RED;
++ node = parent;
++ parent = node->rb_parent;
++ }
++ else
++ {
++ if (!other->rb_left ||
++ other->rb_left->rb_color == RB_BLACK)
++ {
++ register struct rb_node * o_right;
++ if ((o_right = other->rb_right))
++ o_right->rb_color = RB_BLACK;
++ other->rb_color = RB_RED;
++ __rb_rotate_left(other, root);
++ other = parent->rb_left;
++ }
++ other->rb_color = parent->rb_color;
++ parent->rb_color = RB_BLACK;
++ if (other->rb_left)
++ other->rb_left->rb_color = RB_BLACK;
++ __rb_rotate_right(parent, root);
++ node = root->rb_node;
++ break;
++ }
++ }
++ }
++ if (node)
++ node->rb_color = RB_BLACK;
++}
++
++void rb_erase(struct rb_node * node, struct rb_root * root)
++{
++ struct rb_node * child, * parent;
++ int color;
++
++ if (!node->rb_left)
++ child = node->rb_right;
++ else if (!node->rb_right)
++ child = node->rb_left;
++ else
++ {
++ struct rb_node * old = node, * left;
++
++ node = node->rb_right;
++ while ((left = node->rb_left))
++ node = left;
++ child = node->rb_right;
++ parent = node->rb_parent;
++ color = node->rb_color;
++
++ if (child)
++ child->rb_parent = parent;
++ if (parent)
++ {
++ if (parent->rb_left == node)
++ parent->rb_left = child;
++ else
++ parent->rb_right = child;
++ }
++ else
++ root->rb_node = child;
++
++ if (node->rb_parent == old)
++ parent = node;
++ node->rb_parent = old->rb_parent;
++ node->rb_color = old->rb_color;
++ node->rb_right = old->rb_right;
++ node->rb_left = old->rb_left;
++
++ if (old->rb_parent)
++ {
++ if (old->rb_parent->rb_left == old)
++ old->rb_parent->rb_left = node;
++ else
++ old->rb_parent->rb_right = node;
++ } else
++ root->rb_node = node;
++
++ old->rb_left->rb_parent = node;
++ if (old->rb_right)
++ old->rb_right->rb_parent = node;
++ goto color;
++ }
++
++ parent = node->rb_parent;
++ color = node->rb_color;
++
++ if (child)
++ child->rb_parent = parent;
++ if (parent)
++ {
++ if (parent->rb_left == node)
++ parent->rb_left = child;
++ else
++ parent->rb_right = child;
++ }
++ else
++ root->rb_node = child;
++
++ color:
++ if (color == RB_BLACK)
++ __rb_erase_color(child, parent, root);
++}
++#endif /* Before 2.4.11 */
++
++ /* These routines haven't made it into 2.4 (yet) */
++struct rb_node *rb_next(struct rb_node *node)
++{
++ /* If we have a right-hand child, go down and then left as far
++ as we can. */
++ if (node->rb_right) {
++ node = node->rb_right;
++ while (node->rb_left)
++ node=node->rb_left;
++ return node;
++ }
++
++ /* No right-hand children. Everything down and left is
++ smaller than us, so any 'next' node must be in the general
++ direction of our parent. Go up the tree; any time the
++ ancestor is a right-hand child of its parent, keep going
++ up. First time it's a left-hand child of its parent, said
++ parent is our 'next' node. */
++ while (node->rb_parent && node == node->rb_parent->rb_right)
++ node = node->rb_parent;
++
++ return node->rb_parent;
++}
++
++struct rb_node *rb_prev(struct rb_node *node)
++{
++ if (node->rb_left) {
++ node = node->rb_left;
++ while (node->rb_right)
++ node=node->rb_right;
++ return node;
++ }
++ while (node->rb_parent && node == node->rb_parent->rb_left)
++ node = node->rb_parent;
++
++ return node->rb_parent;
++}
++
++void rb_replace_node(struct rb_node *victim, struct rb_node *new, struct rb_root *root)
++{
++ struct rb_node *parent = victim->rb_parent;
++
++ /* Set the surrounding nodes to point to the replacement */
++ if (parent) {
++ if (victim == parent->rb_left)
++ parent->rb_left = new;
++ else
++ parent->rb_right = new;
++ } else {
++ root->rb_node = new;
++ }
++ if (victim->rb_left)
++ victim->rb_left->rb_parent = new;
++ if (victim->rb_right)
++ victim->rb_right->rb_parent = new;
++
++ /* Copy the pointers/colour from the victim to the replacement */
++ *new = *victim;
++}
+diff -Nurb linux-mips-2.4.24-pre2/fs/jffs2/read.c linux/fs/jffs2/read.c
+--- linux-mips-2.4.24-pre2/fs/jffs2/read.c 2004-11-17 18:05:04.000000000 +0100
++++ linux/fs/jffs2/read.c 2004-11-17 18:17:59.000000000 +0100
+@@ -1,52 +1,29 @@
+ /*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+- * Copyright (C) 2001 Red Hat, Inc.
++ * Copyright (C) 2001-2003 Red Hat, Inc.
+ *
+- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
++ * Created by David Woodhouse <dwmw2@redhat.com>
+ *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
++ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+- * The contents of this file are subject to the Red Hat eCos Public
+- * License Version 1.1 (the "Licence"); you may not use this file
+- * except in compliance with the Licence. You may obtain a copy of
+- * the Licence at http://www.redhat.com/
+- *
+- * Software distributed under the Licence is distributed on an "AS IS"
+- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+- * See the Licence for the specific language governing rights and
+- * limitations under the Licence.
+- *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
+- *
+- * Alternatively, the contents of this file may be used under the
+- * terms of the GNU General Public License version 2 (the "GPL"), in
+- * which case the provisions of the GPL are applicable instead of the
+- * above. If you wish to allow the use of your version of this file
+- * only under the terms of the GPL and not to allow others to use your
+- * version of this file under the RHEPL, indicate your decision by
+- * deleting the provisions above and replace them with the notice and
+- * other provisions required by the GPL. If you do not delete the
+- * provisions above, a recipient may use your version of this file
+- * under either the RHEPL or the GPL.
+- *
+- * $Id$
++ * $Id$
+ *
+ */
+
+ #include <linux/kernel.h>
+ #include <linux/slab.h>
+-#include <linux/jffs2.h>
++#include <linux/crc32.h>
++#include <linux/pagemap.h>
+ #include <linux/mtd/mtd.h>
++#include <linux/compiler.h>
+ #include "nodelist.h"
+-#include <linux/crc32.h>
+
+ int jffs2_read_dnode(struct jffs2_sb_info *c, struct jffs2_full_dnode *fd, unsigned char *buf, int ofs, int len)
+ {
+ struct jffs2_raw_inode *ri;
+ size_t readlen;
+- __u32 crc;
++ uint32_t crc;
+ unsigned char *decomprbuf = NULL;
+ unsigned char *readbuf = NULL;
+ int ret = 0;
+@@ -55,35 +32,41 @@
+ if (!ri)
+ return -ENOMEM;
+
+- ret = c->mtd->read(c->mtd, fd->raw->flash_offset & ~3, sizeof(*ri), &readlen, (char *)ri);
++ ret = jffs2_flash_read(c, ref_offset(fd->raw), sizeof(*ri), &readlen, (char *)ri);
+ if (ret) {
+ jffs2_free_raw_inode(ri);
+- printk(KERN_WARNING "Error reading node from 0x%08x: %d\n", fd->raw->flash_offset & ~3, ret);
++ printk(KERN_WARNING "Error reading node from 0x%08x: %d\n", ref_offset(fd->raw), ret);
+ return ret;
+ }
+ if (readlen != sizeof(*ri)) {
+ jffs2_free_raw_inode(ri);
+- printk(KERN_WARNING "Short read from 0x%08x: wanted 0x%x bytes, got 0x%x\n",
+- fd->raw->flash_offset & ~3, sizeof(*ri), readlen);
++ printk(KERN_WARNING "Short read from 0x%08x: wanted 0x%zx bytes, got 0x%zx\n",
++ ref_offset(fd->raw), sizeof(*ri), readlen);
+ return -EIO;
+ }
+ crc = crc32(0, ri, sizeof(*ri)-8);
+
+- D1(printk(KERN_DEBUG "Node read from %08x: node_crc %08x, calculated CRC %08x. dsize %x, csize %x, offset %x, buf %p\n", fd->raw->flash_offset & ~3, ri->node_crc, crc, ri->dsize, ri->csize, ri->offset, buf));
+- if (crc != ri->node_crc) {
+- printk(KERN_WARNING "Node CRC %08x != calculated CRC %08x for node at %08x\n", ri->node_crc, crc, fd->raw->flash_offset & ~3);
++ D1(printk(KERN_DEBUG "Node read from %08x: node_crc %08x, calculated CRC %08x. dsize %x, csize %x, offset %x, buf %p\n",
++ ref_offset(fd->raw), je32_to_cpu(ri->node_crc),
++ crc, je32_to_cpu(ri->dsize), je32_to_cpu(ri->csize),
++ je32_to_cpu(ri->offset), buf));
++ if (crc != je32_to_cpu(ri->node_crc)) {
++ printk(KERN_WARNING "Node CRC %08x != calculated CRC %08x for node at %08x\n",
++ je32_to_cpu(ri->node_crc), crc, ref_offset(fd->raw));
+ ret = -EIO;
+ goto out_ri;
+ }
+ /* There was a bug where we wrote hole nodes out with csize/dsize
+ swapped. Deal with it */
+- if (ri->compr == JFFS2_COMPR_ZERO && !ri->dsize && ri->csize) {
++ if (ri->compr == JFFS2_COMPR_ZERO && !je32_to_cpu(ri->dsize) &&
++ je32_to_cpu(ri->csize)) {
+ ri->dsize = ri->csize;
+- ri->csize = 0;
++ ri->csize = cpu_to_je32(0);
+ }
+
+- D1(if(ofs + len > ri->dsize) {
+- printk(KERN_WARNING "jffs2_read_dnode() asked for %d bytes at %d from %d-byte node\n", len, ofs, ri->dsize);
++ D1(if(ofs + len > je32_to_cpu(ri->dsize)) {
++ printk(KERN_WARNING "jffs2_read_dnode() asked for %d bytes at %d from %d-byte node\n",
++ len, ofs, je32_to_cpu(ri->dsize));
+ ret = -EINVAL;
+ goto out_ri;
+ });
+@@ -100,18 +83,18 @@
+ Reading partial node and it's uncompressed - read into readbuf, check CRC, and copy
+ Reading partial node and it's compressed - read into readbuf, check checksum, decompress to decomprbuf and copy
+ */
+- if (ri->compr == JFFS2_COMPR_NONE && len == ri->dsize) {
++ if (ri->compr == JFFS2_COMPR_NONE && len == je32_to_cpu(ri->dsize)) {
+ readbuf = buf;
+ } else {
+- readbuf = kmalloc(ri->csize, GFP_KERNEL);
++ readbuf = kmalloc(je32_to_cpu(ri->csize), GFP_KERNEL);
+ if (!readbuf) {
+ ret = -ENOMEM;
+ goto out_ri;
+ }
+ }
+ if (ri->compr != JFFS2_COMPR_NONE) {
+- if (len < ri->dsize) {
+- decomprbuf = kmalloc(ri->dsize, GFP_KERNEL);
++ if (len < je32_to_cpu(ri->dsize)) {
++ decomprbuf = kmalloc(je32_to_cpu(ri->dsize), GFP_KERNEL);
+ if (!decomprbuf) {
+ ret = -ENOMEM;
+ goto out_readbuf;
+@@ -123,31 +106,35 @@
+ decomprbuf = readbuf;
+ }
+
+- D2(printk(KERN_DEBUG "Read %d bytes to %p\n", ri->csize, readbuf));
+- ret = c->mtd->read(c->mtd, (fd->raw->flash_offset &~3) + sizeof(*ri), ri->csize, &readlen, readbuf);
++ D2(printk(KERN_DEBUG "Read %d bytes to %p\n", je32_to_cpu(ri->csize),
++ readbuf));
++ ret = jffs2_flash_read(c, (ref_offset(fd->raw)) + sizeof(*ri),
++ je32_to_cpu(ri->csize), &readlen, readbuf);
+
+- if (!ret && readlen != ri->csize)
++ if (!ret && readlen != je32_to_cpu(ri->csize))
+ ret = -EIO;
+ if (ret)
+ goto out_decomprbuf;
+
+- crc = crc32(0, readbuf, ri->csize);
+- if (crc != ri->data_crc) {
+- printk(KERN_WARNING "Data CRC %08x != calculated CRC %08x for node at %08x\n", ri->data_crc, crc, fd->raw->flash_offset & ~3);
++ crc = crc32(0, readbuf, je32_to_cpu(ri->csize));
++ if (crc != je32_to_cpu(ri->data_crc)) {
++ printk(KERN_WARNING "Data CRC %08x != calculated CRC %08x for node at %08x\n",
++ je32_to_cpu(ri->data_crc), crc, ref_offset(fd->raw));
+ ret = -EIO;
+ goto out_decomprbuf;
+ }
+ D2(printk(KERN_DEBUG "Data CRC matches calculated CRC %08x\n", crc));
+ if (ri->compr != JFFS2_COMPR_NONE) {
+- D2(printk(KERN_DEBUG "Decompress %d bytes from %p to %d bytes at %p\n", ri->csize, readbuf, ri->dsize, decomprbuf));
+- ret = jffs2_decompress(ri->compr, readbuf, decomprbuf, ri->csize, ri->dsize);
++ D2(printk(KERN_DEBUG "Decompress %d bytes from %p to %d bytes at %p\n",
++ je32_to_cpu(ri->csize), readbuf, je32_to_cpu(ri->dsize), decomprbuf));
++ ret = jffs2_decompress(ri->compr, readbuf, decomprbuf, je32_to_cpu(ri->csize), je32_to_cpu(ri->dsize));
+ if (ret) {
+ printk(KERN_WARNING "Error: jffs2_decompress returned %d\n", ret);
+ goto out_decomprbuf;
+ }
+ }
+
+- if (len < ri->dsize) {
++ if (len < je32_to_cpu(ri->dsize)) {
+ memcpy(buf, decomprbuf+ofs, len);
+ }
+ out_decomprbuf:
+@@ -161,3 +148,96 @@
+
+ return ret;
+ }
++
++int jffs2_read_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
++ unsigned char *buf, uint32_t offset, uint32_t len)
++{
++ uint32_t end = offset + len;
++ struct jffs2_node_frag *frag;
++ int ret;
++
++ D1(printk(KERN_DEBUG "jffs2_read_inode_range: ino #%u, range 0x%08x-0x%08x\n",
++ f->inocache->ino, offset, offset+len));
++
++ frag = jffs2_lookup_node_frag(&f->fragtree, offset);
++
++ /* XXX FIXME: Where a single physical node actually shows up in two
++ frags, we read it twice. Don't do that. */
++ /* Now we're pointing at the first frag which overlaps our page */
++ while(offset < end) {
++ D2(printk(KERN_DEBUG "jffs2_read_inode_range: offset %d, end %d\n", offset, end));
++ if (unlikely(!frag || frag->ofs > offset)) {
++ uint32_t holesize = end - offset;
++ if (frag) {
++ D1(printk(KERN_NOTICE "Eep. Hole in ino #%u fraglist. frag->ofs = 0x%08x, offset = 0x%08x\n", f->inocache->ino, frag->ofs, offset));
++ holesize = min(holesize, frag->ofs - offset);
++ D1(jffs2_print_frag_list(f));
++ }
++ D1(printk(KERN_DEBUG "Filling non-frag hole from %d-%d\n", offset, offset+holesize));
++ memset(buf, 0, holesize);
++ buf += holesize;
++ offset += holesize;
++ continue;
++ } else if (unlikely(!frag->node)) {
++ uint32_t holeend = min(end, frag->ofs + frag->size);
++ D1(printk(KERN_DEBUG "Filling frag hole from %d-%d (frag 0x%x 0x%x)\n", offset, holeend, frag->ofs, frag->ofs + frag->size));
++ memset(buf, 0, holeend - offset);
++ buf += holeend - offset;
++ offset = holeend;
++ frag = frag_next(frag);
++ continue;
++ } else {
++ uint32_t readlen;
++ uint32_t fragofs; /* offset within the frag to start reading */
++
++ fragofs = offset - frag->ofs;
++ readlen = min(frag->size - fragofs, end - offset);
++ D1(printk(KERN_DEBUG "Reading %d-%d from node at 0x%08x (%d)\n",
++ frag->ofs+fragofs, frag->ofs+fragofs+readlen,
++ ref_offset(frag->node->raw), ref_flags(frag->node->raw)));
++ ret = jffs2_read_dnode(c, frag->node, buf, fragofs + frag->ofs - frag->node->ofs, readlen);
++ D2(printk(KERN_DEBUG "node read done\n"));
++ if (ret) {
++ D1(printk(KERN_DEBUG"jffs2_read_inode_range error %d\n",ret));
++ memset(buf, 0, readlen);
++ return ret;
++ }
++ buf += readlen;
++ offset += readlen;
++ frag = frag_next(frag);
++ D2(printk(KERN_DEBUG "node read was OK. Looping\n"));
++ }
++ }
++ return 0;
++}
++
++/* Core function to read symlink target. */
++char *jffs2_getlink(struct jffs2_sb_info *c, struct jffs2_inode_info *f)
++{
++ char *buf;
++ int ret;
++
++ down(&f->sem);
++
++ if (!f->metadata) {
++ printk(KERN_NOTICE "No metadata for symlink inode #%u\n", f->inocache->ino);
++ up(&f->sem);
++ return ERR_PTR(-EINVAL);
++ }
++ buf = kmalloc(f->metadata->size+1, GFP_USER);
++ if (!buf) {
++ up(&f->sem);
++ return ERR_PTR(-ENOMEM);
++ }
++ buf[f->metadata->size]=0;
++
++ ret = jffs2_read_dnode(c, f->metadata, buf, 0, f->metadata->size);
++
++ up(&f->sem);
++
++ if (ret) {
++ kfree(buf);
++ return ERR_PTR(ret);
++ }
++ return buf;
++}
+diff -Nurb linux-mips-2.4.24-pre2/fs/jffs2/readinode.c linux/fs/jffs2/readinode.c
+--- linux-mips-2.4.24-pre2/fs/jffs2/readinode.c 2004-11-17 18:05:04.000000000 +0100
++++ linux/fs/jffs2/readinode.c 2004-11-17 18:17:59.000000000 +0100
+@@ -1,79 +1,122 @@
+ /*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+- * Copyright (C) 2001 Red Hat, Inc.
++ * Copyright (C) 2001-2003 Red Hat, Inc.
+ *
+- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
++ * Created by David Woodhouse <dwmw2@redhat.com>
+ *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
++ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+- * The contents of this file are subject to the Red Hat eCos Public
+- * License Version 1.1 (the "Licence"); you may not use this file
+- * except in compliance with the Licence. You may obtain a copy of
+- * the Licence at http://www.redhat.com/
+- *
+- * Software distributed under the Licence is distributed on an "AS IS"
+- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+- * See the Licence for the specific language governing rights and
+- * limitations under the Licence.
+- *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
+- *
+- * Alternatively, the contents of this file may be used under the
+- * terms of the GNU General Public License version 2 (the "GPL"), in
+- * which case the provisions of the GPL are applicable instead of the
+- * above. If you wish to allow the use of your version of this file
+- * only under the terms of the GPL and not to allow others to use your
+- * version of this file under the RHEPL, indicate your decision by
+- * deleting the provisions above and replace them with the notice and
+- * other provisions required by the GPL. If you do not delete the
+- * provisions above, a recipient may use your version of this file
+- * under either the RHEPL or the GPL.
+- *
+- * $Id$
++ * $Id$
+ *
+ */
+
+-/* Given an inode, probably with existing list of fragments, add the new node
+- * to the fragment list.
+- */
+ #include <linux/kernel.h>
+ #include <linux/slab.h>
+ #include <linux/fs.h>
++#include <linux/crc32.h>
++#include <linux/pagemap.h>
+ #include <linux/mtd/mtd.h>
+-#include <linux/jffs2.h>
++#include <linux/compiler.h>
+ #include "nodelist.h"
+-#include <linux/crc32.h>
+
++static int jffs2_add_frag_to_fragtree(struct jffs2_sb_info *c, struct rb_root *list, struct jffs2_node_frag *newfrag);
+
+-D1(void jffs2_print_frag_list(struct jffs2_inode_info *f)
++#if CONFIG_JFFS2_FS_DEBUG >= 1
++static void jffs2_print_fragtree(struct rb_root *list, int permitbug)
+ {
+- struct jffs2_node_frag *this = f->fraglist;
++ struct jffs2_node_frag *this = frag_first(list);
++ uint32_t lastofs = 0;
++ int buggy = 0;
+
+ while(this) {
+ if (this->node)
+- printk(KERN_DEBUG "frag %04x-%04x: 0x%08x on flash (*%p->%p)\n", this->ofs, this->ofs+this->size, this->node->raw->flash_offset &~3, this, this->next);
++ printk(KERN_DEBUG "frag %04x-%04x: 0x%08x(%d) on flash (*%p). left (%p), right (%p), parent (%p)\n",
++ this->ofs, this->ofs+this->size, ref_offset(this->node->raw), ref_flags(this->node->raw),
++ this, frag_left(this), frag_right(this), frag_parent(this));
+ else
+- printk(KERN_DEBUG "frag %04x-%04x: hole (*%p->%p)\n", this->ofs, this->ofs+this->size, this, this->next);
+- this = this->next;
++ printk(KERN_DEBUG "frag %04x-%04x: hole (*%p). left (%p} right (%p), parent (%p)\n", this->ofs,
++ this->ofs+this->size, this, frag_left(this), frag_right(this), frag_parent(this));
++ if (this->ofs != lastofs)
++ buggy = 1;
++ lastofs = this->ofs+this->size;
++ this = frag_next(this);
++ }
++ if (buggy && !permitbug) {
++ printk(KERN_CRIT "Frag tree got a hole in it\n");
++ BUG();
+ }
++}
++
++void jffs2_print_frag_list(struct jffs2_inode_info *f)
++{
++ jffs2_print_fragtree(&f->fragtree, 0);
++
+ if (f->metadata) {
+- printk(KERN_DEBUG "metadata at 0x%08x\n", f->metadata->raw->flash_offset &~3);
++ printk(KERN_DEBUG "metadata at 0x%08x\n", ref_offset(f->metadata->raw));
+ }
+-})
+-
++}
+
+-int jffs2_add_full_dnode_to_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_full_dnode *fn)
++static int jffs2_sanitycheck_fragtree(struct jffs2_inode_info *f)
+ {
+- int ret;
+- D1(printk(KERN_DEBUG "jffs2_add_full_dnode_to_inode(ino #%u, f %p, fn %p)\n", f->inocache->ino, f, fn));
++ struct jffs2_node_frag *frag;
++ int bitched = 0;
+
+- ret = jffs2_add_full_dnode_to_fraglist(c, &f->fraglist, fn);
++ for (frag = frag_first(&f->fragtree); frag; frag = frag_next(frag)) {
+
+- D2(jffs2_print_frag_list(f));
+- return ret;
++ struct jffs2_full_dnode *fn = frag->node;
++ if (!fn || !fn->raw)
++ continue;
++
++ if (ref_flags(fn->raw) == REF_PRISTINE) {
++
++ if (fn->frags > 1) {
++ printk(KERN_WARNING "REF_PRISTINE node at 0x%08x had %d frags. Tell dwmw2\n", ref_offset(fn->raw), fn->frags);
++ bitched = 1;
++ }
++ /* A hole node which isn't multi-page should be garbage-collected
++ and merged anyway, so we just check for the frag size here,
++ rather than mucking around with actually reading the node
++ and checking the compression type, which is the real way
++ to tell a hole node. */
++ if (frag->ofs & (PAGE_CACHE_SIZE-1) && frag_prev(frag) && frag_prev(frag)->size < PAGE_CACHE_SIZE && frag_prev(frag)->node) {
++ printk(KERN_WARNING "REF_PRISTINE node at 0x%08x had a previous non-hole frag in the same page. Tell dwmw2\n",
++ ref_offset(fn->raw));
++ bitched = 1;
++ }
++
++ if ((frag->ofs+frag->size) & (PAGE_CACHE_SIZE-1) && frag_next(frag) && frag_next(frag)->size < PAGE_CACHE_SIZE && frag_next(frag)->node) {
++ printk(KERN_WARNING "REF_PRISTINE node at 0x%08x (%08x-%08x) had a following non-hole frag in the same page. Tell dwmw2\n",
++ ref_offset(fn->raw), frag->ofs, frag->ofs+frag->size);
++ bitched = 1;
++ }
++ }
++ }
++
++ if (bitched) {
++ struct jffs2_node_frag *thisfrag;
++
++ printk(KERN_WARNING "Inode is #%u\n", f->inocache->ino);
++ thisfrag = frag_first(&f->fragtree);
++ while (thisfrag) {
++ if (!thisfrag->node) {
++ printk("Frag @0x%x-0x%x; node-less hole\n",
++ thisfrag->ofs, thisfrag->size + thisfrag->ofs);
++ } else if (!thisfrag->node->raw) {
++ printk("Frag @0x%x-0x%x; raw-less hole\n",
++ thisfrag->ofs, thisfrag->size + thisfrag->ofs);
++ } else {
++ printk("Frag @0x%x-0x%x; raw at 0x%08x(%d) (0x%x-0x%x)\n",
++ thisfrag->ofs, thisfrag->size + thisfrag->ofs,
++ ref_offset(thisfrag->node->raw), ref_flags(thisfrag->node->raw),
++ thisfrag->node->ofs, thisfrag->node->ofs+thisfrag->node->size);
++ }
++ thisfrag = frag_next(thisfrag);
++ }
++ }
++ return bitched;
+ }
++#endif /* D1 */
+
+ static void jffs2_obsolete_node_frag(struct jffs2_sb_info *c, struct jffs2_node_frag *this)
+ {
+@@ -82,42 +125,38 @@
+ if (!this->node->frags) {
+ /* The node has no valid frags left. It's totally obsoleted */
+ D2(printk(KERN_DEBUG "Marking old node @0x%08x (0x%04x-0x%04x) obsolete\n",
+- this->node->raw->flash_offset &~3, this->node->ofs, this->node->ofs+this->node->size));
++ ref_offset(this->node->raw), this->node->ofs, this->node->ofs+this->node->size));
+ jffs2_mark_node_obsolete(c, this->node->raw);
+ jffs2_free_full_dnode(this->node);
+ } else {
+- D2(printk(KERN_DEBUG "Not marking old node @0x%08x (0x%04x-0x%04x) obsolete. frags is %d\n",
+- this->node->raw->flash_offset &~3, this->node->ofs, this->node->ofs+this->node->size,
++ D2(printk(KERN_DEBUG "Marking old node @0x%08x (0x%04x-0x%04x) REF_NORMAL. frags is %d\n",
++ ref_offset(this->node->raw), this->node->ofs, this->node->ofs+this->node->size,
+ this->node->frags));
++ mark_ref_normal(this->node->raw);
+ }
+
+ }
+ jffs2_free_node_frag(this);
+ }
+
+-/* Doesn't set inode->i_size */
+-int jffs2_add_full_dnode_to_fraglist(struct jffs2_sb_info *c, struct jffs2_node_frag **list, struct jffs2_full_dnode *fn)
++/* Given an inode, probably with existing list of fragments, add the new node
++ * to the fragment list.
++ */
++int jffs2_add_full_dnode_to_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_full_dnode *fn)
+ {
++ int ret;
++ struct jffs2_node_frag *newfrag;
+
+- struct jffs2_node_frag *this, **prev, *old;
+- struct jffs2_node_frag *newfrag, *newfrag2;
+- __u32 lastend = 0;
+-
++ D1(printk(KERN_DEBUG "jffs2_add_full_dnode_to_inode(ino #%u, f %p, fn %p)\n", f->inocache->ino, f, fn));
+
+ newfrag = jffs2_alloc_node_frag();
+- if (!newfrag) {
++ if (unlikely(!newfrag))
+ return -ENOMEM;
+- }
+-
+- D2(if (fn->raw)
+- printk(KERN_DEBUG "adding node %04x-%04x @0x%08x on flash, newfrag *%p\n", fn->ofs, fn->ofs+fn->size, fn->raw->flash_offset &~3, newfrag);
+- else
+- printk(KERN_DEBUG "adding hole node %04x-%04x on flash, newfrag *%p\n", fn->ofs, fn->ofs+fn->size, newfrag));
+
+- prev = list;
+- this = *list;
++ D2(printk(KERN_DEBUG "adding node %04x-%04x @0x%08x on flash, newfrag *%p\n",
++ fn->ofs, fn->ofs+fn->size, ref_offset(fn->raw), newfrag));
+
+- if (!fn->size) {
++ if (unlikely(!fn->size)) {
+ jffs2_free_node_frag(newfrag);
+ return 0;
+ }
+@@ -126,176 +165,358 @@
+ newfrag->size = fn->size;
+ newfrag->node = fn;
+ newfrag->node->frags = 1;
+- newfrag->next = (void *)0xdeadbeef;
++
++ ret = jffs2_add_frag_to_fragtree(c, &f->fragtree, newfrag);
++ if (ret)
++ return ret;
++
++ /* If we now share a page with other nodes, mark either previous
++ or next node REF_NORMAL, as appropriate. */
++ if (newfrag->ofs & (PAGE_CACHE_SIZE-1)) {
++ struct jffs2_node_frag *prev = frag_prev(newfrag);
++
++ mark_ref_normal(fn->raw);
++ /* If we don't start at zero there's _always_ a previous */
++ if (prev->node)
++ mark_ref_normal(prev->node->raw);
++ }
++
++ if ((newfrag->ofs+newfrag->size) & (PAGE_CACHE_SIZE-1)) {
++ struct jffs2_node_frag *next = frag_next(newfrag);
++
++ if (next) {
++ mark_ref_normal(fn->raw);
++ if (next->node)
++ mark_ref_normal(next->node->raw);
++ }
++ }
++ D2(if (jffs2_sanitycheck_fragtree(f)) {
++ printk(KERN_WARNING "Just added node %04x-%04x @0x%08x on flash, newfrag *%p\n",
++ fn->ofs, fn->ofs+fn->size, ref_offset(fn->raw), newfrag);
++ return 0;
++ })
++ D2(jffs2_print_frag_list(f));
++ return 0;
++}
++
++/* Doesn't set inode->i_size */
++static int jffs2_add_frag_to_fragtree(struct jffs2_sb_info *c, struct rb_root *list, struct jffs2_node_frag *newfrag)
++{
++ struct jffs2_node_frag *this;
++ uint32_t lastend;
+
+ /* Skip all the nodes which are completed before this one starts */
+- while(this && fn->ofs >= this->ofs+this->size) {
+- lastend = this->ofs + this->size;
++ this = jffs2_lookup_node_frag(list, newfrag->node->ofs);
+
+- D2(printk(KERN_DEBUG "j_a_f_d_t_f: skipping frag 0x%04x-0x%04x; phys 0x%08x (*%p->%p)\n",
+- this->ofs, this->ofs+this->size, this->node?(this->node->raw->flash_offset &~3):0xffffffff, this, this->next));
+- prev = &this->next;
+- this = this->next;
++ if (this) {
++ D2(printk(KERN_DEBUG "j_a_f_d_t_f: Lookup gave frag 0x%04x-0x%04x; phys 0x%08x (*%p)\n",
++ this->ofs, this->ofs+this->size, this->node?(ref_offset(this->node->raw)):0xffffffff, this));
++ lastend = this->ofs + this->size;
++ } else {
++ D2(printk(KERN_DEBUG "j_a_f_d_t_f: Lookup gave no frag\n"));
++ lastend = 0;
+ }
+
+ /* See if we ran off the end of the list */
+- if (!this) {
++ if (lastend <= newfrag->ofs) {
+ /* We did */
+- if (lastend < fn->ofs) {
++
++ /* Check if 'this' node was on the same page as the new node.
++ If so, both 'this' and the new node get marked REF_NORMAL so
++ the GC can take a look.
++ */
++ if ((lastend-1) >> PAGE_CACHE_SHIFT == newfrag->ofs >> PAGE_CACHE_SHIFT) {
++ if (this->node)
++ mark_ref_normal(this->node->raw);
++ mark_ref_normal(newfrag->node->raw);
++ }
++
++ if (lastend < newfrag->node->ofs) {
+ /* ... and we need to put a hole in before the new node */
+ struct jffs2_node_frag *holefrag = jffs2_alloc_node_frag();
+- if (!holefrag)
++ if (!holefrag) {
++ jffs2_free_node_frag(newfrag);
+ return -ENOMEM;
++ }
+ holefrag->ofs = lastend;
+- holefrag->size = fn->ofs - lastend;
+- holefrag->next = NULL;
++ holefrag->size = newfrag->node->ofs - lastend;
+ holefrag->node = NULL;
+- *prev = holefrag;
+- prev = &holefrag->next;
++ if (this) {
++ /* By definition, the 'this' node has no right-hand child,
++ because there are no frags with offset greater than it.
++ So that's where we want to put the hole */
++ D2(printk(KERN_DEBUG "Adding hole frag (%p) on right of node at (%p)\n", holefrag, this));
++ rb_link_node(&holefrag->rb, &this->rb, &this->rb.rb_right);
++ } else {
++ D2(printk(KERN_DEBUG "Adding hole frag (%p) at root of tree\n", holefrag));
++ rb_link_node(&holefrag->rb, NULL, &list->rb_node);
++ }
++ rb_insert_color(&holefrag->rb, list);
++ this = holefrag;
+ }
+- newfrag->next = NULL;
+- *prev = newfrag;
++ if (this) {
++ /* By definition, the 'this' node has no right-hand child,
++ because there are no frags with offset greater than it.
++ So that's where we want to put the hole */
++ D2(printk(KERN_DEBUG "Adding new frag (%p) on right of node at (%p)\n", newfrag, this));
++ rb_link_node(&newfrag->rb, &this->rb, &this->rb.rb_right);
++ } else {
++ D2(printk(KERN_DEBUG "Adding new frag (%p) at root of tree\n", newfrag));
++ rb_link_node(&newfrag->rb, NULL, &list->rb_node);
++ }
++ rb_insert_color(&newfrag->rb, list);
+ return 0;
+ }
+
+- D2(printk(KERN_DEBUG "j_a_f_d_t_f: dealing with frag 0x%04x-0x%04x; phys 0x%08x (*%p->%p)\n",
+- this->ofs, this->ofs+this->size, this->node?(this->node->raw->flash_offset &~3):0xffffffff, this, this->next));
++ D2(printk(KERN_DEBUG "j_a_f_d_t_f: dealing with frag 0x%04x-0x%04x; phys 0x%08x (*%p)\n",
++ this->ofs, this->ofs+this->size, this->node?(ref_offset(this->node->raw)):0xffffffff, this));
+
+- /* OK. 'this' is pointing at the first frag that fn->ofs at least partially obsoletes,
+- * - i.e. fn->ofs < this->ofs+this->size && fn->ofs >= this->ofs
++ /* OK. 'this' is pointing at the first frag that newfrag->ofs at least partially obsoletes,
++ * - i.e. newfrag->ofs < this->ofs+this->size && newfrag->ofs >= this->ofs
+ */
+- if (fn->ofs > this->ofs) {
++ if (newfrag->ofs > this->ofs) {
+ /* This node isn't completely obsoleted. The start of it remains valid */
+- if (this->ofs + this->size > fn->ofs + fn->size) {
++
++ /* Mark the new node and the partially covered node REF_NORMAL -- let
++ the GC take a look at them */
++ mark_ref_normal(newfrag->node->raw);
++ if (this->node)
++ mark_ref_normal(this->node->raw);
++
++ if (this->ofs + this->size > newfrag->ofs + newfrag->size) {
+ /* The new node splits 'this' frag into two */
+- newfrag2 = jffs2_alloc_node_frag();
++ struct jffs2_node_frag *newfrag2 = jffs2_alloc_node_frag();
+ if (!newfrag2) {
+ jffs2_free_node_frag(newfrag);
+ return -ENOMEM;
+ }
+- D1(printk(KERN_DEBUG "split old frag 0x%04x-0x%04x -->", this->ofs, this->ofs+this->size);
++ D2(printk(KERN_DEBUG "split old frag 0x%04x-0x%04x -->", this->ofs, this->ofs+this->size);
+ if (this->node)
+- printk("phys 0x%08x\n", this->node->raw->flash_offset &~3);
++ printk("phys 0x%08x\n", ref_offset(this->node->raw));
+ else
+ printk("hole\n");
+ )
+- newfrag2->ofs = fn->ofs + fn->size;
++
++ /* New second frag pointing to this's node */
++ newfrag2->ofs = newfrag->ofs + newfrag->size;
+ newfrag2->size = (this->ofs+this->size) - newfrag2->ofs;
+- newfrag2->next = this->next;
+ newfrag2->node = this->node;
+ if (this->node)
+ this->node->frags++;
+- newfrag->next = newfrag2;
+- this->next = newfrag;
++
++ /* Adjust size of original 'this' */
+ this->size = newfrag->ofs - this->ofs;
++
++ /* Now, we know there's no node with offset
++ greater than this->ofs but smaller than
++ newfrag2->ofs or newfrag->ofs, for obvious
++ reasons. So we can do a tree insert from
++ 'this' to insert newfrag, and a tree insert
++ from newfrag to insert newfrag2. */
++ jffs2_fragtree_insert(newfrag, this);
++ rb_insert_color(&newfrag->rb, list);
++
++ jffs2_fragtree_insert(newfrag2, newfrag);
++ rb_insert_color(&newfrag2->rb, list);
++
+ return 0;
+ }
+ /* New node just reduces 'this' frag in size, doesn't split it */
+- this->size = fn->ofs - this->ofs;
+- newfrag->next = this->next;
+- this->next = newfrag;
+- this = newfrag->next;
++ this->size = newfrag->ofs - this->ofs;
++
++ /* Again, we know it lives down here in the tree */
++ jffs2_fragtree_insert(newfrag, this);
++ rb_insert_color(&newfrag->rb, list);
+ } else {
+- D2(printk(KERN_DEBUG "Inserting newfrag (*%p) in before 'this' (*%p)\n", newfrag, this));
+- *prev = newfrag;
+- newfrag->next = this;
++ /* New frag starts at the same point as 'this' used to. Replace
++ it in the tree without doing a delete and insertion */
++ D2(printk(KERN_DEBUG "Inserting newfrag (*%p),%d-%d in before 'this' (*%p),%d-%d\n",
++ newfrag, newfrag->ofs, newfrag->ofs+newfrag->size,
++ this, this->ofs, this->ofs+this->size));
++
++ rb_replace_node(&this->rb, &newfrag->rb, list);
++
++ if (newfrag->ofs + newfrag->size >= this->ofs+this->size) {
++ D2(printk(KERN_DEBUG "Obsoleting node frag %p (%x-%x)\n", this, this->ofs, this->ofs+this->size));
++ jffs2_obsolete_node_frag(c, this);
++ } else {
++ this->ofs += newfrag->size;
++ this->size -= newfrag->size;
++
++ jffs2_fragtree_insert(this, newfrag);
++ rb_insert_color(&this->rb, list);
++ return 0;
+ }
+- /* OK, now we have newfrag added in the correct place in the list, but
+- newfrag->next points to a fragment which may be overlapping it
++ }
++ /* OK, now we have newfrag added in the correct place in the tree, but
++ frag_next(newfrag) may be a fragment which is overlapped by it
+ */
+- while (this && newfrag->ofs + newfrag->size >= this->ofs + this->size) {
+- /* 'this' frag is obsoleted. */
+- old = this;
+- this = old->next;
+- jffs2_obsolete_node_frag(c, old);
++ while ((this = frag_next(newfrag)) && newfrag->ofs + newfrag->size >= this->ofs + this->size) {
++ /* 'this' frag is obsoleted completely. */
++ D2(printk(KERN_DEBUG "Obsoleting node frag %p (%x-%x) and removing from tree\n", this, this->ofs, this->ofs+this->size));
++ rb_erase(&this->rb, list);
++ jffs2_obsolete_node_frag(c, this);
+ }
+ /* Now we're pointing at the first frag which isn't totally obsoleted by
+ the new frag */
+- newfrag->next = this;
+
+ if (!this || newfrag->ofs + newfrag->size == this->ofs) {
+ return 0;
+ }
+- /* Still some overlap */
++ /* Still some overlap but we don't need to move it in the tree */
+ this->size = (this->ofs + this->size) - (newfrag->ofs + newfrag->size);
+ this->ofs = newfrag->ofs + newfrag->size;
++
++ /* And mark them REF_NORMAL so the GC takes a look at them */
++ if (this->node)
++ mark_ref_normal(this->node->raw);
++ mark_ref_normal(newfrag->node->raw);
++
+ return 0;
+ }
+
+-void jffs2_truncate_fraglist (struct jffs2_sb_info *c, struct jffs2_node_frag **list, __u32 size)
++void jffs2_truncate_fraglist (struct jffs2_sb_info *c, struct rb_root *list, uint32_t size)
+ {
++ struct jffs2_node_frag *frag = jffs2_lookup_node_frag(list, size);
++
+ D1(printk(KERN_DEBUG "Truncating fraglist to 0x%08x bytes\n", size));
+
+- while (*list) {
+- if ((*list)->ofs >= size) {
+- struct jffs2_node_frag *this = *list;
+- *list = this->next;
+- D1(printk(KERN_DEBUG "Removing frag 0x%08x-0x%08x\n", this->ofs, this->ofs+this->size));
+- jffs2_obsolete_node_frag(c, this);
+- continue;
+- } else if ((*list)->ofs + (*list)->size > size) {
+- D1(printk(KERN_DEBUG "Truncating frag 0x%08x-0x%08x\n", (*list)->ofs, (*list)->ofs + (*list)->size));
+- (*list)->size = size - (*list)->ofs;
+- }
+- list = &(*list)->next;
++ /* We know frag->ofs <= size. That's what lookup does for us */
++ if (frag && frag->ofs != size) {
++ if (frag->ofs+frag->size >= size) {
++ D1(printk(KERN_DEBUG "Truncating frag 0x%08x-0x%08x\n", frag->ofs, frag->ofs+frag->size));
++ frag->size = size - frag->ofs;
++ }
++ frag = frag_next(frag);
++ }
++ while (frag && frag->ofs >= size) {
++ struct jffs2_node_frag *next = frag_next(frag);
++
++ D1(printk(KERN_DEBUG "Removing frag 0x%08x-0x%08x\n", frag->ofs, frag->ofs+frag->size));
++ frag_erase(frag, list);
++ jffs2_obsolete_node_frag(c, frag);
++ frag = next;
+ }
+ }
+
+ /* Scan the list of all nodes present for this ino, build map of versions, etc. */
+
+-void jffs2_read_inode (struct inode *inode)
++static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c,
++ struct jffs2_inode_info *f,
++ struct jffs2_raw_inode *latest_node);
++
++int jffs2_do_read_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
++ uint32_t ino, struct jffs2_raw_inode *latest_node)
+ {
+- struct jffs2_tmp_dnode_info *tn_list, *tn;
+- struct jffs2_full_dirent *fd_list;
+- struct jffs2_inode_info *f;
+- struct jffs2_full_dnode *fn = NULL;
+- struct jffs2_sb_info *c;
+- struct jffs2_raw_inode latest_node;
+- __u32 latest_mctime, mctime_ver;
+- __u32 mdata_ver = 0;
+- int ret;
+- ssize_t retlen;
++ D2(printk(KERN_DEBUG "jffs2_do_read_inode(): getting inocache\n"));
+
+- D1(printk(KERN_DEBUG "jffs2_read_inode(): inode->i_ino == %lu\n", inode->i_ino));
++ retry_inocache:
++ spin_lock(&c->inocache_lock);
++ f->inocache = jffs2_get_ino_cache(c, ino);
++
++ D2(printk(KERN_DEBUG "jffs2_do_read_inode(): Got inocache at %p\n", f->inocache));
++
++ if (f->inocache) {
++ /* Check its state. We may need to wait before we can use it */
++ switch(f->inocache->state) {
++ case INO_STATE_UNCHECKED:
++ case INO_STATE_CHECKEDABSENT:
++ f->inocache->state = INO_STATE_READING;
++ break;
+
+- f = JFFS2_INODE_INFO(inode);
+- c = JFFS2_SB_INFO(inode->i_sb);
++ case INO_STATE_CHECKING:
++ case INO_STATE_GC:
++ /* If it's in either of these states, we need
++ to wait for whoever's got it to finish and
++ put it back. */
++ D1(printk(KERN_DEBUG "jffs2_get_ino_cache_read waiting for ino #%u in state %d\n",
++ ino, f->inocache->state));
++ sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock);
++ goto retry_inocache;
++
++ case INO_STATE_READING:
++ case INO_STATE_PRESENT:
++ /* Eep. This should never happen. It can
++ happen if Linux calls read_inode() again
++ before clear_inode() has finished though. */
++ printk(KERN_WARNING "Eep. Trying to read_inode #%u when it's already in state %d!\n", ino, f->inocache->state);
++ /* Fail. That's probably better than allowing it to succeed */
++ f->inocache = NULL;
++ break;
+
+- memset(f, 0, sizeof(*f));
+- D2(printk(KERN_DEBUG "getting inocache\n"));
+- init_MUTEX(&f->sem);
+- f->inocache = jffs2_get_ino_cache(c, inode->i_ino);
+- D2(printk(KERN_DEBUG "jffs2_read_inode(): Got inocache at %p\n", f->inocache));
++ default:
++ BUG();
++ }
++ }
++ spin_unlock(&c->inocache_lock);
+
+- if (!f->inocache && inode->i_ino == 1) {
++ if (!f->inocache && ino == 1) {
+ /* Special case - no root inode on medium */
+ f->inocache = jffs2_alloc_inode_cache();
+ if (!f->inocache) {
+- printk(KERN_CRIT "jffs2_read_inode(): Cannot allocate inocache for root inode\n");
+- make_bad_inode(inode);
+- return;
++ printk(KERN_CRIT "jffs2_do_read_inode(): Cannot allocate inocache for root inode\n");
++ return -ENOMEM;
+ }
+- D1(printk(KERN_DEBUG "jffs2_read_inode(): Creating inocache for root inode\n"));
++ D1(printk(KERN_DEBUG "jffs2_do_read_inode(): Creating inocache for root inode\n"));
+ memset(f->inocache, 0, sizeof(struct jffs2_inode_cache));
+ f->inocache->ino = f->inocache->nlink = 1;
+ f->inocache->nodes = (struct jffs2_raw_node_ref *)f->inocache;
++ f->inocache->state = INO_STATE_READING;
+ jffs2_add_ino_cache(c, f->inocache);
+ }
+ if (!f->inocache) {
+- printk(KERN_WARNING "jffs2_read_inode() on nonexistent ino %lu\n", (unsigned long)inode->i_ino);
+- make_bad_inode(inode);
+- return;
++ printk(KERN_WARNING "jffs2_do_read_inode() on nonexistent ino %u\n", ino);
++ return -ENOENT;
+ }
+- D1(printk(KERN_DEBUG "jffs2_read_inode(): ino #%lu nlink is %d\n", (unsigned long)inode->i_ino, f->inocache->nlink));
+- inode->i_nlink = f->inocache->nlink;
++
++ return jffs2_do_read_inode_internal(c, f, latest_node);
++}
++
++int jffs2_do_crccheck_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic)
++{
++ struct jffs2_raw_inode n;
++ struct jffs2_inode_info *f = kmalloc(sizeof(*f), GFP_KERNEL);
++ int ret;
++
++ if (!f)
++ return -ENOMEM;
++
++ memset(f, 0, sizeof(*f));
++ init_MUTEX_LOCKED(&f->sem);
++ f->inocache = ic;
++
++ ret = jffs2_do_read_inode_internal(c, f, &n);
++ if (!ret) {
++ up(&f->sem);
++ jffs2_do_clear_inode(c, f);
++ }
++ kfree (f);
++ return ret;
++}
++
++static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c,
++ struct jffs2_inode_info *f,
++ struct jffs2_raw_inode *latest_node)
++{
++ struct jffs2_tmp_dnode_info *tn_list, *tn;
++ struct jffs2_full_dirent *fd_list;
++ struct jffs2_full_dnode *fn = NULL;
++ uint32_t crc;
++ uint32_t latest_mctime, mctime_ver;
++ uint32_t mdata_ver = 0;
++ size_t retlen;
++ int ret;
++
++ D1(printk(KERN_DEBUG "jffs2_do_read_inode_internal(): ino #%u nlink is %d\n", f->inocache->ino, f->inocache->nlink));
+
+ /* Grab all nodes relevant to this ino */
+- ret = jffs2_get_inode_nodes(c, inode->i_ino, f, &tn_list, &fd_list, &f->highest_version, &latest_mctime, &mctime_ver);
++ ret = jffs2_get_inode_nodes(c, f->inocache->ino, f, &tn_list, &fd_list, &f->highest_version, &latest_mctime, &mctime_ver);
+
+ if (ret) {
+- printk(KERN_CRIT "jffs2_get_inode_nodes() for ino %lu returned %d\n", inode->i_ino, ret);
+- make_bad_inode(inode);
+- return;
++ printk(KERN_CRIT "jffs2_get_inode_nodes() for ino %u returned %d\n", f->inocache->ino, ret);
++ if (f->inocache->state == INO_STATE_READING)
++ jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT);
++ return ret;
+ }
+ f->dents = fd_list;
+
+@@ -304,205 +525,169 @@
+
+ fn = tn->fn;
+
+- if (f->metadata && tn->version > mdata_ver) {
+- D1(printk(KERN_DEBUG "Obsoleting old metadata at 0x%08x\n", f->metadata->raw->flash_offset &~3));
++ if (f->metadata) {
++ if (likely(tn->version >= mdata_ver)) {
++ D1(printk(KERN_DEBUG "Obsoleting old metadata at 0x%08x\n", ref_offset(f->metadata->raw)));
+ jffs2_mark_node_obsolete(c, f->metadata->raw);
+ jffs2_free_full_dnode(f->metadata);
+ f->metadata = NULL;
+
+ mdata_ver = 0;
++ } else {
++ /* This should never happen. */
++ printk(KERN_WARNING "Er. New metadata at 0x%08x with ver %d is actually older than previous ver %d at 0x%08x\n",
++ ref_offset(fn->raw), tn->version, mdata_ver, ref_offset(f->metadata->raw));
++ jffs2_mark_node_obsolete(c, fn->raw);
++ jffs2_free_full_dnode(fn);
++ /* Fill in latest_node from the metadata, not this one we're about to free... */
++ fn = f->metadata;
++ goto next_tn;
++ }
+ }
+
+ if (fn->size) {
+ jffs2_add_full_dnode_to_inode(c, f, fn);
+ } else {
+ /* Zero-sized node at end of version list. Just a metadata update */
+- D1(printk(KERN_DEBUG "metadata @%08x: ver %d\n", fn->raw->flash_offset &~3, tn->version));
++ D1(printk(KERN_DEBUG "metadata @%08x: ver %d\n", ref_offset(fn->raw), tn->version));
+ f->metadata = fn;
+ mdata_ver = tn->version;
+ }
++ next_tn:
+ tn_list = tn->next;
+ jffs2_free_tmp_dnode_info(tn);
+ }
++ D1(jffs2_sanitycheck_fragtree(f));
++
+ if (!fn) {
+ /* No data nodes for this inode. */
+- if (inode->i_ino != 1) {
+- printk(KERN_WARNING "jffs2_read_inode(): No data nodes found for ino #%lu\n", inode->i_ino);
++ if (f->inocache->ino != 1) {
++ printk(KERN_WARNING "jffs2_do_read_inode(): No data nodes found for ino #%u\n", f->inocache->ino);
+ if (!fd_list) {
+- make_bad_inode(inode);
+- return;
+- }
+- printk(KERN_WARNING "jffs2_read_inode(): But it has children so we fake some modes for it\n");
++ if (f->inocache->state == INO_STATE_READING)
++ jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT);
++ return -EIO;
++ }
++ printk(KERN_WARNING "jffs2_do_read_inode(): But it has children so we fake some modes for it\n");
++ }
++ latest_node->mode = cpu_to_jemode(S_IFDIR|S_IRUGO|S_IWUSR|S_IXUGO);
++ latest_node->version = cpu_to_je32(0);
++ latest_node->atime = latest_node->ctime = latest_node->mtime = cpu_to_je32(0);
++ latest_node->isize = cpu_to_je32(0);
++ latest_node->gid = cpu_to_je16(0);
++ latest_node->uid = cpu_to_je16(0);
++ if (f->inocache->state == INO_STATE_READING)
++ jffs2_set_inocache_state(c, f->inocache, INO_STATE_PRESENT);
++ return 0;
+ }
+- inode->i_mode = S_IFDIR | S_IRUGO | S_IWUSR | S_IXUGO;
+- latest_node.version = 0;
+- inode->i_atime = inode->i_ctime = inode->i_mtime = CURRENT_TIME;
+- inode->i_nlink = f->inocache->nlink;
+- inode->i_size = 0;
+- } else {
+- __u32 crc;
+
+- ret = c->mtd->read(c->mtd, fn->raw->flash_offset & ~3, sizeof(latest_node), &retlen, (void *)&latest_node);
+- if (ret || retlen != sizeof(latest_node)) {
+- printk(KERN_NOTICE "MTD read in jffs2_read_inode() failed: Returned %d, %ld of %d bytes read\n",
+- ret, (long)retlen, sizeof(latest_node));
+- jffs2_clear_inode(inode);
+- make_bad_inode(inode);
+- return;
+- }
+-
+- crc = crc32(0, &latest_node, sizeof(latest_node)-8);
+- if (crc != latest_node.node_crc) {
+- printk(KERN_NOTICE "CRC failed for read_inode of inode %ld at physical location 0x%x\n", inode->i_ino, fn->raw->flash_offset & ~3);
+- jffs2_clear_inode(inode);
+- make_bad_inode(inode);
+- return;
+- }
+-
+- inode->i_mode = latest_node.mode;
+- inode->i_uid = latest_node.uid;
+- inode->i_gid = latest_node.gid;
+- inode->i_size = latest_node.isize;
+- if (S_ISREG(inode->i_mode))
+- jffs2_truncate_fraglist(c, &f->fraglist, latest_node.isize);
+- inode->i_atime = latest_node.atime;
+- inode->i_mtime = latest_node.mtime;
+- inode->i_ctime = latest_node.ctime;
+- }
+-
+- /* OK, now the special cases. Certain inode types should
+- have only one data node, and it's kept as the metadata
+- node */
+- if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode) ||
+- S_ISLNK(inode->i_mode)) {
+- if (f->metadata) {
+- printk(KERN_WARNING "Argh. Special inode #%lu with mode 0%o had metadata node\n", inode->i_ino, inode->i_mode);
+- jffs2_clear_inode(inode);
+- make_bad_inode(inode);
+- return;
+- }
+- if (!f->fraglist) {
+- printk(KERN_WARNING "Argh. Special inode #%lu with mode 0%o has no fragments\n", inode->i_ino, inode->i_mode);
+- jffs2_clear_inode(inode);
+- make_bad_inode(inode);
+- return;
+- }
+- /* ASSERT: f->fraglist != NULL */
+- if (f->fraglist->next) {
+- printk(KERN_WARNING "Argh. Special inode #%lu with mode 0%o had more than one node\n", inode->i_ino, inode->i_mode);
+- /* FIXME: Deal with it - check crc32, check for duplicate node, check times and discard the older one */
+- jffs2_clear_inode(inode);
+- make_bad_inode(inode);
+- return;
+- }
+- /* OK. We're happy */
+- f->metadata = f->fraglist->node;
+- jffs2_free_node_frag(f->fraglist);
+- f->fraglist = NULL;
++ ret = jffs2_flash_read(c, ref_offset(fn->raw), sizeof(*latest_node), &retlen, (void *)latest_node);
++ if (ret || retlen != sizeof(*latest_node)) {
++ printk(KERN_NOTICE "MTD read in jffs2_do_read_inode() failed: Returned %d, %zd of %zd bytes read\n",
++ ret, retlen, sizeof(*latest_node));
++ /* FIXME: If this fails, there seems to be a memory leak. Find it. */
++ up(&f->sem);
++ jffs2_do_clear_inode(c, f);
++ return ret?ret:-EIO;
+ }
+
+- inode->i_blksize = PAGE_SIZE;
+- inode->i_blocks = (inode->i_size + 511) >> 9;
+-
+- switch (inode->i_mode & S_IFMT) {
+- unsigned short rdev;
+-
+- case S_IFLNK:
+- inode->i_op = &jffs2_symlink_inode_operations;
+- /* Hack to work around broken isize in old symlink code.
+- Remove this when dwmw2 comes to his senses and stops
+- symlinks from being an entirely gratuitous special
+- case. */
+- if (!inode->i_size)
+- inode->i_size = latest_node.dsize;
+- break;
++ crc = crc32(0, latest_node, sizeof(*latest_node)-8);
++ if (crc != je32_to_cpu(latest_node->node_crc)) {
++ printk(KERN_NOTICE "CRC failed for read_inode of inode %u at physical location 0x%x\n", f->inocache->ino, ref_offset(fn->raw));
++ up(&f->sem);
++ jffs2_do_clear_inode(c, f);
++ return -EIO;
++ }
+
++ switch(jemode_to_cpu(latest_node->mode) & S_IFMT) {
+ case S_IFDIR:
+- if (mctime_ver > latest_node.version) {
++ if (mctime_ver > je32_to_cpu(latest_node->version)) {
+ /* The times in the latest_node are actually older than
+ mctime in the latest dirent. Cheat. */
+- inode->i_mtime = inode->i_ctime = inode->i_atime =
+- latest_mctime;
++ latest_node->ctime = latest_node->mtime = cpu_to_je32(latest_mctime);
+ }
+- inode->i_op = &jffs2_dir_inode_operations;
+- inode->i_fop = &jffs2_dir_operations;
+ break;
+
++
+ case S_IFREG:
+- inode->i_op = &jffs2_file_inode_operations;
+- inode->i_fop = &jffs2_file_operations;
+- inode->i_mapping->a_ops = &jffs2_file_address_operations;
+- inode->i_mapping->nrpages = 0;
++ /* If it was a regular file, truncate it to the latest node's isize */
++ jffs2_truncate_fraglist(c, &f->fragtree, je32_to_cpu(latest_node->isize));
+ break;
+
++ case S_IFLNK:
++ /* Hack to work around broken isize in old symlink code.
++ Remove this when dwmw2 comes to his senses and stops
++ symlinks from being an entirely gratuitous special
++ case. */
++ if (!je32_to_cpu(latest_node->isize))
++ latest_node->isize = latest_node->dsize;
++ /* fall through... */
++
+ case S_IFBLK:
+ case S_IFCHR:
+- /* Read the device numbers from the media */
+- D1(printk(KERN_DEBUG "Reading device numbers from flash\n"));
+- if (jffs2_read_dnode(c, f->metadata, (char *)&rdev, 0, sizeof(rdev)) < 0) {
+- /* Eep */
+- printk(KERN_NOTICE "Read device numbers for inode %lu failed\n", (unsigned long)inode->i_ino);
+- jffs2_clear_inode(inode);
+- make_bad_inode(inode);
+- return;
++ /* Certain inode types should have only one data node, and it's
++ kept as the metadata node */
++ if (f->metadata) {
++ printk(KERN_WARNING "Argh. Special inode #%u with mode 0%o had metadata node\n",
++ f->inocache->ino, jemode_to_cpu(latest_node->mode));
++ up(&f->sem);
++ jffs2_do_clear_inode(c, f);
++ return -EIO;
+ }
+-
+- case S_IFSOCK:
+- case S_IFIFO:
+- inode->i_op = &jffs2_file_inode_operations;
+- init_special_inode(inode, inode->i_mode, kdev_t_to_nr(MKDEV(rdev>>8, rdev&0xff)));
++ if (!frag_first(&f->fragtree)) {
++ printk(KERN_WARNING "Argh. Special inode #%u with mode 0%o has no fragments\n",
++ f->inocache->ino, jemode_to_cpu(latest_node->mode));
++ up(&f->sem);
++ jffs2_do_clear_inode(c, f);
++ return -EIO;
++ }
++ /* ASSERT: f->fraglist != NULL */
++ if (frag_next(frag_first(&f->fragtree))) {
++ printk(KERN_WARNING "Argh. Special inode #%u with mode 0x%x had more than one node\n",
++ f->inocache->ino, jemode_to_cpu(latest_node->mode));
++ /* FIXME: Deal with it - check crc32, check for duplicate node, check times and discard the older one */
++ up(&f->sem);
++ jffs2_do_clear_inode(c, f);
++ return -EIO;
++ }
++ /* OK. We're happy */
++ f->metadata = frag_first(&f->fragtree)->node;
++ jffs2_free_node_frag(frag_first(&f->fragtree));
++ f->fragtree = RB_ROOT;
+ break;
+-
+- default:
+- printk(KERN_WARNING "jffs2_read_inode(): Bogus imode %o for ino %lu", inode->i_mode, (unsigned long)inode->i_ino);
+ }
+- D1(printk(KERN_DEBUG "jffs2_read_inode() returning\n"));
++ if (f->inocache->state == INO_STATE_READING)
++ jffs2_set_inocache_state(c, f->inocache, INO_STATE_PRESENT);
++
++ return 0;
+ }
+
+-void jffs2_clear_inode (struct inode *inode)
++void jffs2_do_clear_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f)
+ {
+- /* We can forget about this inode for now - drop all
+- * the nodelists associated with it, etc.
+- */
+- struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
+- struct jffs2_node_frag *frag, *frags;
+ struct jffs2_full_dirent *fd, *fds;
+- struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
+ int deleted;
+
+- D1(printk(KERN_DEBUG "jffs2_clear_inode(): ino #%lu mode %o\n", inode->i_ino, inode->i_mode));
+-
+ down(&f->sem);
+ deleted = f->inocache && !f->inocache->nlink;
+
+- frags = f->fraglist;
+- fds = f->dents;
+ if (f->metadata) {
+ if (deleted)
+ jffs2_mark_node_obsolete(c, f->metadata->raw);
+ jffs2_free_full_dnode(f->metadata);
+ }
+
+- while (frags) {
+- frag = frags;
+- frags = frag->next;
+- D2(printk(KERN_DEBUG "jffs2_clear_inode: frag at 0x%x-0x%x: node %p, frags %d--\n", frag->ofs, frag->ofs+frag->size, frag->node, frag->node?frag->node->frags:0));
++ jffs2_kill_fragtree(&f->fragtree, deleted?c:NULL);
+
+- if (frag->node && !(--frag->node->frags)) {
+- /* Not a hole, and it's the final remaining frag of this node. Free the node */
+- if (deleted)
+- jffs2_mark_node_obsolete(c, frag->node->raw);
++ fds = f->dents;
+
+- jffs2_free_full_dnode(frag->node);
+- }
+- jffs2_free_node_frag(frag);
+- }
+ while(fds) {
+ fd = fds;
+ fds = fd->next;
+ jffs2_free_full_dirent(fd);
+ }
+
+- up(&f->sem);
+-};
++ if (f->inocache && f->inocache->state != INO_STATE_CHECKING)
++ jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT);
+
++ up(&f->sem);
++}
+diff -Nurb linux-mips-2.4.24-pre2/fs/jffs2/scan.c linux/fs/jffs2/scan.c
+--- linux-mips-2.4.24-pre2/fs/jffs2/scan.c 2004-11-17 18:05:04.000000000 +0100
++++ linux/fs/jffs2/scan.c 2004-11-17 18:17:59.000000000 +0100
+@@ -1,47 +1,25 @@
+ /*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+- * Copyright (C) 2001 Red Hat, Inc.
++ * Copyright (C) 2001-2003 Red Hat, Inc.
+ *
+- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
++ * Created by David Woodhouse <dwmw2@redhat.com>
+ *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
++ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+- * The contents of this file are subject to the Red Hat eCos Public
+- * License Version 1.1 (the "Licence"); you may not use this file
+- * except in compliance with the Licence. You may obtain a copy of
+- * the Licence at http://www.redhat.com/
+- *
+- * Software distributed under the Licence is distributed on an "AS IS"
+- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+- * See the Licence for the specific language governing rights and
+- * limitations under the Licence.
+- *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
+- *
+- * Alternatively, the contents of this file may be used under the
+- * terms of the GNU General Public License version 2 (the "GPL"), in
+- * which case the provisions of the GPL are applicable instead of the
+- * above. If you wish to allow the use of your version of this file
+- * only under the terms of the GPL and not to allow others to use your
+- * version of this file under the RHEPL, indicate your decision by
+- * deleting the provisions above and replace them with the notice and
+- * other provisions required by the GPL. If you do not delete the
+- * provisions above, a recipient may use your version of this file
+- * under either the RHEPL or the GPL.
+- *
+- * $Id$
++ * $Id$
+ *
+ */
+ #include <linux/kernel.h>
++#include <linux/sched.h>
+ #include <linux/slab.h>
+-#include <linux/jffs2.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/pagemap.h>
+-#include "nodelist.h"
+ #include <linux/crc32.h>
++#include <linux/compiler.h>
++#include "nodelist.h"
+
++#define EMPTY_SCAN_SIZE 1024
+
+ #define DIRTY_SPACE(x) do { typeof(x) _x = (x); \
+ c->free_size -= _x; c->dirty_size += _x; \
+@@ -51,6 +29,10 @@
+ c->free_size -= _x; c->used_size += _x; \
+ jeb->free_size -= _x ; jeb->used_size += _x; \
+ }while(0)
++#define UNCHECKED_SPACE(x) do { typeof(x) _x = (x); \
++ c->free_size -= _x; c->unchecked_size += _x; \
++ jeb->free_size -= _x ; jeb->unchecked_size += _x; \
++ }while(0)
+
+ #define noisy_printk(noise, args...) do { \
+ if (*(noise)) { \
+@@ -63,39 +45,84 @@
+ } while(0)
+
+ static uint32_t pseudo_random;
+-static void jffs2_rotate_lists(struct jffs2_sb_info *c);
+
+-static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
++static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
++ unsigned char *buf, uint32_t buf_size);
+
+ /* These helper functions _must_ increase ofs and also do the dirty/used space accounting.
+ * Returning an error will abort the mount - bad checksums etc. should just mark the space
+ * as dirty.
+ */
+-static int jffs2_scan_empty(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, __u32 *ofs, int *noise);
+-static int jffs2_scan_inode_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, __u32 *ofs);
+-static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, __u32 *ofs);
++static int jffs2_scan_inode_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
++ struct jffs2_raw_inode *ri, uint32_t ofs);
++static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
++ struct jffs2_raw_dirent *rd, uint32_t ofs);
++
++#define BLK_STATE_ALLFF 0
++#define BLK_STATE_CLEAN 1
++#define BLK_STATE_PARTDIRTY 2
++#define BLK_STATE_CLEANMARKER 3
++#define BLK_STATE_ALLDIRTY 4
++#define BLK_STATE_BADBLOCK 5
+
++static inline int min_free(struct jffs2_sb_info *c)
++{
++ uint32_t min = 2 * sizeof(struct jffs2_raw_inode);
++#ifdef CONFIG_JFFS2_FS_NAND
++ if (!jffs2_can_mark_obsolete(c) && min < c->wbuf_pagesize)
++ return c->wbuf_pagesize;
++#endif
++ return min;
+
++}
+ int jffs2_scan_medium(struct jffs2_sb_info *c)
+ {
+ int i, ret;
+- __u32 empty_blocks = 0;
+-
+- if (!c->blocks) {
+- printk(KERN_WARNING "EEEK! c->blocks is NULL!\n");
+- return -EINVAL;
++ uint32_t empty_blocks = 0, bad_blocks = 0;
++ unsigned char *flashbuf = NULL;
++ uint32_t buf_size = 0;
++#ifndef __ECOS
++ size_t pointlen;
++
++ if (c->mtd->point) {
++ ret = c->mtd->point (c->mtd, 0, c->mtd->size, &pointlen, &flashbuf);
++ if (!ret && pointlen < c->mtd->size) {
++ /* Don't muck about if it won't let us point to the whole flash */
++ D1(printk(KERN_DEBUG "MTD point returned len too short: 0x%zx\n", pointlen));
++ c->mtd->unpoint(c->mtd, flashbuf, 0, c->mtd->size);
++ flashbuf = NULL;
++ }
++ if (ret)
++ D1(printk(KERN_DEBUG "MTD point failed %d\n", ret));
++ }
++#endif
++ if (!flashbuf) {
++ /* For NAND it's quicker to read a whole eraseblock at a time,
++ apparently */
++ if (jffs2_cleanmarker_oob(c))
++ buf_size = c->sector_size;
++ else
++ buf_size = PAGE_SIZE;
++
++ D1(printk(KERN_DEBUG "Allocating readbuf of %d bytes\n", buf_size));
++ flashbuf = kmalloc(buf_size, GFP_KERNEL);
++ if (!flashbuf)
++ return -ENOMEM;
+ }
++
+ for (i=0; i<c->nr_blocks; i++) {
+ struct jffs2_eraseblock *jeb = &c->blocks[i];
+
+- ret = jffs2_scan_eraseblock(c, jeb);
++ ret = jffs2_scan_eraseblock(c, jeb, buf_size?flashbuf:(flashbuf+jeb->offset), buf_size);
++
+ if (ret < 0)
+- return ret;
++ goto out;
+
+ ACCT_PARANOIA_CHECK(jeb);
+
+ /* Now decide which list to put it on */
+- if (ret == 1) {
++ switch(ret) {
++ case BLK_STATE_ALLFF:
+ /*
+ * Empty block. Since we can't be sure it
+ * was entirely erased, we just queue it for erase
+@@ -103,10 +130,12 @@
+ * is complete. Meanwhile we still count it as empty
+ * for later checks.
+ */
+- list_add(&jeb->list, &c->erase_pending_list);
+ empty_blocks++;
++ list_add(&jeb->list, &c->erase_pending_list);
+ c->nr_erasing_blocks++;
+- } else if (jeb->used_size == PAD(sizeof(struct jffs2_unknown_node)) && !jeb->first_node->next_in_ino) {
++ break;
++
++ case BLK_STATE_CLEANMARKER:
+ /* Only a CLEANMARKER node is valid */
+ if (!jeb->dirty_size) {
+ /* It's actually free */
+@@ -118,74 +147,227 @@
+ list_add(&jeb->list, &c->erase_pending_list);
+ c->nr_erasing_blocks++;
+ }
+- } else if (jeb->used_size > c->sector_size - (2*sizeof(struct jffs2_raw_inode))) {
++ break;
++
++ case BLK_STATE_CLEAN:
+ /* Full (or almost full) of clean data. Clean list */
+ list_add(&jeb->list, &c->clean_list);
+- } else if (jeb->used_size) {
++ break;
++
++ case BLK_STATE_PARTDIRTY:
+ /* Some data, but not full. Dirty list. */
+ /* Except that we want to remember the block with most free space,
+ and stick it in the 'nextblock' position to start writing to it.
+ Later when we do snapshots, this must be the most recent block,
+ not the one with most free space.
+ */
+- if (jeb->free_size > 2*sizeof(struct jffs2_raw_inode) &&
++ if (jeb->free_size > min_free(c) &&
+ (!c->nextblock || c->nextblock->free_size < jeb->free_size)) {
+ /* Better candidate for the next writes to go to */
+- if (c->nextblock)
++ if (c->nextblock) {
++ c->nextblock->dirty_size += c->nextblock->free_size + c->nextblock->wasted_size;
++ c->dirty_size += c->nextblock->free_size + c->nextblock->wasted_size;
++ c->free_size -= c->nextblock->free_size;
++ c->wasted_size -= c->nextblock->wasted_size;
++ c->nextblock->free_size = c->nextblock->wasted_size = 0;
++ if (VERYDIRTY(c, c->nextblock->dirty_size)) {
++ list_add(&c->nextblock->list, &c->very_dirty_list);
++ } else {
+ list_add(&c->nextblock->list, &c->dirty_list);
++ }
++ }
+ c->nextblock = jeb;
+ } else {
++ jeb->dirty_size += jeb->free_size + jeb->wasted_size;
++ c->dirty_size += jeb->free_size + jeb->wasted_size;
++ c->free_size -= jeb->free_size;
++ c->wasted_size -= jeb->wasted_size;
++ jeb->free_size = jeb->wasted_size = 0;
++ if (VERYDIRTY(c, jeb->dirty_size)) {
++ list_add(&jeb->list, &c->very_dirty_list);
++ } else {
+ list_add(&jeb->list, &c->dirty_list);
+ }
+- } else {
++ }
++ break;
++
++ case BLK_STATE_ALLDIRTY:
+ /* Nothing valid - not even a clean marker. Needs erasing. */
+ /* For now we just put it on the erasing list. We'll start the erases later */
+- printk(KERN_NOTICE "JFFS2: Erase block at 0x%08x is not formatted. It will be erased\n", jeb->offset);
++ D1(printk(KERN_NOTICE "JFFS2: Erase block at 0x%08x is not formatted. It will be erased\n", jeb->offset));
+ list_add(&jeb->list, &c->erase_pending_list);
+ c->nr_erasing_blocks++;
++ break;
++
++ case BLK_STATE_BADBLOCK:
++ D1(printk(KERN_NOTICE "JFFS2: Block at 0x%08x is bad\n", jeb->offset));
++ list_add(&jeb->list, &c->bad_list);
++ c->bad_size += c->sector_size;
++ c->free_size -= c->sector_size;
++ bad_blocks++;
++ break;
++ default:
++ printk(KERN_WARNING "jffs2_scan_medium(): unknown block state\n");
++ BUG();
+ }
+ }
+- /* Rotate the lists by some number to ensure wear levelling */
+- jffs2_rotate_lists(c);
+
++ /* Nextblock dirty is always seen as wasted, because we cannot recycle it now */
++ if (c->nextblock && (c->nextblock->dirty_size)) {
++ c->nextblock->wasted_size += c->nextblock->dirty_size;
++ c->wasted_size += c->nextblock->dirty_size;
++ c->dirty_size -= c->nextblock->dirty_size;
++ c->nextblock->dirty_size = 0;
++ }
++#ifdef CONFIG_JFFS2_FS_NAND
++ if (!jffs2_can_mark_obsolete(c) && c->nextblock && (c->nextblock->free_size & (c->wbuf_pagesize-1))) {
++ /* If we're going to start writing into a block which already
++ contains data, and the end of the data isn't page-aligned,
++ skip a little and align it. */
++
++ uint32_t skip = c->nextblock->free_size & (c->wbuf_pagesize-1);
++
++ D1(printk(KERN_DEBUG "jffs2_scan_medium(): Skipping %d bytes in nextblock to ensure page alignment\n",
++ skip));
++ c->nextblock->wasted_size += skip;
++ c->wasted_size += skip;
++
++ c->nextblock->free_size -= skip;
++ c->free_size -= skip;
++ }
++#endif
+ if (c->nr_erasing_blocks) {
+- if (!c->used_size && empty_blocks != c->nr_blocks) {
++ if ( !c->used_size && ((empty_blocks+bad_blocks)!= c->nr_blocks || bad_blocks == c->nr_blocks) ) {
+ printk(KERN_NOTICE "Cowardly refusing to erase blocks on filesystem with no valid JFFS2 nodes\n");
+- return -EIO;
++ printk(KERN_NOTICE "empty_blocks %d, bad_blocks %d, c->nr_blocks %d\n",empty_blocks,bad_blocks,c->nr_blocks);
++ ret = -EIO;
++ goto out;
+ }
+ jffs2_erase_pending_trigger(c);
+ }
++ ret = 0;
++ out:
++ if (buf_size)
++ kfree(flashbuf);
++#ifndef __ECOS
++ else
++ c->mtd->unpoint(c->mtd, flashbuf, 0, c->mtd->size);
++#endif
++ return ret;
++}
++
++static int jffs2_fill_scan_buf (struct jffs2_sb_info *c, unsigned char *buf,
++ uint32_t ofs, uint32_t len)
++{
++ int ret;
++ size_t retlen;
++
++ ret = jffs2_flash_read(c, ofs, len, &retlen, buf);
++ if (ret) {
++ D1(printk(KERN_WARNING "mtd->read(0x%x bytes from 0x%x) returned %d\n", len, ofs, ret));
++ return ret;
++ }
++ if (retlen < len) {
++ D1(printk(KERN_WARNING "Read at 0x%x gave only 0x%zx bytes\n", ofs, retlen));
++ return -EIO;
++ }
++ D2(printk(KERN_DEBUG "Read 0x%x bytes from 0x%08x into buf\n", len, ofs));
++ D2(printk(KERN_DEBUG "000: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
++ buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]));
+ return 0;
+ }
+
+-static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) {
+- struct jffs2_unknown_node node;
+- __u32 ofs, prevofs;
+- __u32 hdr_crc, nodetype;
++static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
++ unsigned char *buf, uint32_t buf_size) {
++ struct jffs2_unknown_node *node;
++ struct jffs2_unknown_node crcnode;
++ uint32_t ofs, prevofs;
++ uint32_t hdr_crc, buf_ofs, buf_len;
+ int err;
+ int noise = 0;
++ int wasempty = 0;
++ uint32_t empty_start = 0;
++#ifdef CONFIG_JFFS2_FS_NAND
++ int cleanmarkerfound = 0;
++#endif
+
+ ofs = jeb->offset;
+ prevofs = jeb->offset - 1;
+
+ D1(printk(KERN_DEBUG "jffs2_scan_eraseblock(): Scanning block at 0x%x\n", ofs));
+
+- err = jffs2_scan_empty(c, jeb, &ofs, &noise);
+- if (err) return err;
+- if (ofs == jeb->offset + c->sector_size) {
++#ifdef CONFIG_JFFS2_FS_NAND
++ if (jffs2_cleanmarker_oob(c)) {
++ int ret = jffs2_check_nand_cleanmarker(c, jeb);
++ D2(printk(KERN_NOTICE "jffs_check_nand_cleanmarker returned %d\n",ret));
++ /* Even if it's not found, we still scan to see
++ if the block is empty. We use this information
++ to decide whether to erase it or not. */
++ switch (ret) {
++ case 0: cleanmarkerfound = 1; break;
++ case 1: break;
++ case 2: return BLK_STATE_BADBLOCK;
++ case 3: return BLK_STATE_ALLDIRTY; /* Block has failed to erase min. once */
++ default: return ret;
++ }
++ }
++#endif
++ buf_ofs = jeb->offset;
++
++ if (!buf_size) {
++ buf_len = c->sector_size;
++ } else {
++ buf_len = EMPTY_SCAN_SIZE;
++ err = jffs2_fill_scan_buf(c, buf, buf_ofs, buf_len);
++ if (err)
++ return err;
++ }
++
++ /* We temporarily use 'ofs' as a pointer into the buffer/jeb */
++ ofs = 0;
++
++ /* Scan only 4KiB of 0xFF before declaring it's empty */
++ while(ofs < EMPTY_SCAN_SIZE && *(uint32_t *)(&buf[ofs]) == 0xFFFFFFFF)
++ ofs += 4;
++
++ if (ofs == EMPTY_SCAN_SIZE) {
++#ifdef CONFIG_JFFS2_FS_NAND
++ if (jffs2_cleanmarker_oob(c)) {
++ /* scan oob, take care of cleanmarker */
++ int ret = jffs2_check_oob_empty(c, jeb, cleanmarkerfound);
++ D2(printk(KERN_NOTICE "jffs2_check_oob_empty returned %d\n",ret));
++ switch (ret) {
++ case 0: return cleanmarkerfound ? BLK_STATE_CLEANMARKER : BLK_STATE_ALLFF;
++ case 1: return BLK_STATE_ALLDIRTY;
++ case 2: return BLK_STATE_BADBLOCK; /* case 2/3 are paranoia checks */
++ case 3: return BLK_STATE_ALLDIRTY; /* Block has failed to erase min. once */
++ default: return ret;
++ }
++ }
++#endif
+ D1(printk(KERN_DEBUG "Block at 0x%08x is empty (erased)\n", jeb->offset));
+- return 1; /* special return code */
++ return BLK_STATE_ALLFF; /* OK to erase if all blocks are like this */
++ }
++ if (ofs) {
++ D1(printk(KERN_DEBUG "Free space at %08x ends at %08x\n", jeb->offset,
++ jeb->offset + ofs));
++ DIRTY_SPACE(ofs);
+ }
+
++ /* Now ofs is a complete physical flash offset as it always was... */
++ ofs += jeb->offset;
++
+ noise = 10;
+
+ while(ofs < jeb->offset + c->sector_size) {
+- ssize_t retlen;
+- ACCT_PARANOIA_CHECK(jeb);
++
++ D1(ACCT_PARANOIA_CHECK(jeb));
++
++ cond_resched();
+
+ if (ofs & 3) {
+ printk(KERN_WARNING "Eep. ofs 0x%08x not word-aligned!\n", ofs);
+- ofs = (ofs+3)&~3;
++ ofs = PAD(ofs);
+ continue;
+ }
+ if (ofs == prevofs) {
+@@ -196,102 +378,173 @@
+ }
+ prevofs = ofs;
+
+- if (jeb->offset + c->sector_size < ofs + sizeof(node)) {
+- D1(printk(KERN_DEBUG "Fewer than %d bytes left to end of block. Not reading\n", sizeof(struct jffs2_unknown_node)));
++ if (jeb->offset + c->sector_size < ofs + sizeof(*node)) {
++ D1(printk(KERN_DEBUG "Fewer than %zd bytes left to end of block. (%x+%x<%x+%zx) Not reading\n", sizeof(struct jffs2_unknown_node),
++ jeb->offset, c->sector_size, ofs, sizeof(*node)));
+ DIRTY_SPACE((jeb->offset + c->sector_size)-ofs);
+ break;
+ }
+
+- err = c->mtd->read(c->mtd, ofs, sizeof(node), &retlen, (char *)&node);
+-
+- if (err) {
+- D1(printk(KERN_WARNING "mtd->read(0x%x bytes from 0x%x) returned %d\n", sizeof(node), ofs, err));
++ if (buf_ofs + buf_len < ofs + sizeof(*node)) {
++ buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs);
++ D1(printk(KERN_DEBUG "Fewer than %zd bytes (node header) left to end of buf. Reading 0x%x at 0x%08x\n",
++ sizeof(struct jffs2_unknown_node), buf_len, ofs));
++ err = jffs2_fill_scan_buf(c, buf, ofs, buf_len);
++ if (err)
+ return err;
++ buf_ofs = ofs;
+ }
+- if (retlen < sizeof(node)) {
+- D1(printk(KERN_WARNING "Read at 0x%x gave only 0x%x bytes\n", ofs, retlen));
+- DIRTY_SPACE(retlen);
+- ofs += retlen;
+- continue;
++
++ node = (struct jffs2_unknown_node *)&buf[ofs-buf_ofs];
++
++ if (*(uint32_t *)(&buf[ofs-buf_ofs]) == 0xffffffff) {
++ uint32_t inbuf_ofs = ofs - buf_ofs + 4;
++ uint32_t scanend;
++
++ empty_start = ofs;
++ ofs += 4;
++
++ /* If scanning empty space after only a cleanmarker, don't
++ bother scanning the whole block */
++ if (unlikely(empty_start == jeb->offset + c->cleanmarker_size &&
++ jeb->offset + EMPTY_SCAN_SIZE < buf_ofs + buf_len))
++ scanend = jeb->offset + EMPTY_SCAN_SIZE - buf_ofs;
++ else
++ scanend = buf_len;
++
++ D1(printk(KERN_DEBUG "Found empty flash at 0x%08x\n", ofs));
++ while (inbuf_ofs < scanend) {
++ if (*(uint32_t *)(&buf[inbuf_ofs]) != 0xffffffff)
++ goto emptyends;
++
++ inbuf_ofs+=4;
++ ofs += 4;
+ }
++ /* Ran off end. */
++ D1(printk(KERN_DEBUG "Empty flash ends normally at 0x%08x\n", ofs));
+
+- if (node.magic == JFFS2_EMPTY_BITMASK && node.nodetype == JFFS2_EMPTY_BITMASK) {
+- D1(printk(KERN_DEBUG "Found empty flash at 0x%x\n", ofs));
+- err = jffs2_scan_empty(c, jeb, &ofs, &noise);
+- if (err) return err;
++ if (buf_ofs == jeb->offset && jeb->used_size == PAD(c->cleanmarker_size) &&
++ c->cleanmarker_size && !jeb->first_node->next_in_ino && !jeb->dirty_size)
++ return BLK_STATE_CLEANMARKER;
++ wasempty = 1;
++ continue;
++ } else if (wasempty) {
++ emptyends:
++ printk(KERN_WARNING "Empty flash at 0x%08x ends at 0x%08x\n", empty_start, ofs);
++ DIRTY_SPACE(ofs-empty_start);
++ wasempty = 0;
+ continue;
+ }
+
+- if (ofs == jeb->offset && node.magic == KSAMTIB_CIGAM_2SFFJ) {
++ if (ofs == jeb->offset && je16_to_cpu(node->magic) == KSAMTIB_CIGAM_2SFFJ) {
+ printk(KERN_WARNING "Magic bitmask is backwards at offset 0x%08x. Wrong endian filesystem?\n", ofs);
+ DIRTY_SPACE(4);
+ ofs += 4;
+ continue;
+ }
+- if (node.magic == JFFS2_DIRTY_BITMASK) {
+- D1(printk(KERN_DEBUG "Empty bitmask at 0x%08x\n", ofs));
++ if (je16_to_cpu(node->magic) == JFFS2_DIRTY_BITMASK) {
++ D1(printk(KERN_DEBUG "Dirty bitmask at 0x%08x\n", ofs));
+ DIRTY_SPACE(4);
+ ofs += 4;
+ continue;
+ }
+- if (node.magic == JFFS2_OLD_MAGIC_BITMASK) {
++ if (je16_to_cpu(node->magic) == JFFS2_OLD_MAGIC_BITMASK) {
+ printk(KERN_WARNING "Old JFFS2 bitmask found at 0x%08x\n", ofs);
+ printk(KERN_WARNING "You cannot use older JFFS2 filesystems with newer kernels\n");
+ DIRTY_SPACE(4);
+ ofs += 4;
+ continue;
+ }
+- if (node.magic != JFFS2_MAGIC_BITMASK) {
++ if (je16_to_cpu(node->magic) != JFFS2_MAGIC_BITMASK) {
+ /* OK. We're out of possibilities. Whinge and move on */
+- noisy_printk(&noise, "jffs2_scan_eraseblock(): Magic bitmask 0x%04x not found at 0x%08x: 0x%04x instead\n", JFFS2_MAGIC_BITMASK, ofs, node.magic);
++ noisy_printk(&noise, "jffs2_scan_eraseblock(): Magic bitmask 0x%04x not found at 0x%08x: 0x%04x instead\n",
++ JFFS2_MAGIC_BITMASK, ofs,
++ je16_to_cpu(node->magic));
+ DIRTY_SPACE(4);
+ ofs += 4;
+ continue;
+ }
+ /* We seem to have a node of sorts. Check the CRC */
+- nodetype = node.nodetype;
+- node.nodetype |= JFFS2_NODE_ACCURATE;
+- hdr_crc = crc32(0, &node, sizeof(node)-4);
+- node.nodetype = nodetype;
+- if (hdr_crc != node.hdr_crc) {
++ crcnode.magic = node->magic;
++ crcnode.nodetype = cpu_to_je16( je16_to_cpu(node->nodetype) | JFFS2_NODE_ACCURATE);
++ crcnode.totlen = node->totlen;
++ hdr_crc = crc32(0, &crcnode, sizeof(crcnode)-4);
++
++ if (hdr_crc != je32_to_cpu(node->hdr_crc)) {
+ noisy_printk(&noise, "jffs2_scan_eraseblock(): Node at 0x%08x {0x%04x, 0x%04x, 0x%08x) has invalid CRC 0x%08x (calculated 0x%08x)\n",
+- ofs, node.magic, node.nodetype, node.totlen, node.hdr_crc, hdr_crc);
++ ofs, je16_to_cpu(node->magic),
++ je16_to_cpu(node->nodetype),
++ je32_to_cpu(node->totlen),
++ je32_to_cpu(node->hdr_crc),
++ hdr_crc);
+ DIRTY_SPACE(4);
+ ofs += 4;
+ continue;
+ }
+
+- if (ofs + node.totlen > jeb->offset + c->sector_size) {
++ if (ofs + je32_to_cpu(node->totlen) >
++ jeb->offset + c->sector_size) {
+ /* Eep. Node goes over the end of the erase block. */
+ printk(KERN_WARNING "Node at 0x%08x with length 0x%08x would run over the end of the erase block\n",
+- ofs, node.totlen);
++ ofs, je32_to_cpu(node->totlen));
+ printk(KERN_WARNING "Perhaps the file system was created with the wrong erase size?\n");
+ DIRTY_SPACE(4);
+ ofs += 4;
+ continue;
+ }
+
+- switch(node.nodetype | JFFS2_NODE_ACCURATE) {
++ if (!(je16_to_cpu(node->nodetype) & JFFS2_NODE_ACCURATE)) {
++ /* Wheee. This is an obsoleted node */
++ D2(printk(KERN_DEBUG "Node at 0x%08x is obsolete. Skipping\n", ofs));
++ DIRTY_SPACE(PAD(je32_to_cpu(node->totlen)));
++ ofs += PAD(je32_to_cpu(node->totlen));
++ continue;
++ }
++
++ switch(je16_to_cpu(node->nodetype)) {
+ case JFFS2_NODETYPE_INODE:
+- err = jffs2_scan_inode_node(c, jeb, &ofs);
++ if (buf_ofs + buf_len < ofs + sizeof(struct jffs2_raw_inode)) {
++ buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs);
++ D1(printk(KERN_DEBUG "Fewer than %zd bytes (inode node) left to end of buf. Reading 0x%x at 0x%08x\n",
++ sizeof(struct jffs2_raw_inode), buf_len, ofs));
++ err = jffs2_fill_scan_buf(c, buf, ofs, buf_len);
++ if (err)
++ return err;
++ buf_ofs = ofs;
++ node = (void *)buf;
++ }
++ err = jffs2_scan_inode_node(c, jeb, (void *)node, ofs);
+ if (err) return err;
++ ofs += PAD(je32_to_cpu(node->totlen));
+ break;
+
+ case JFFS2_NODETYPE_DIRENT:
+- err = jffs2_scan_dirent_node(c, jeb, &ofs);
++ if (buf_ofs + buf_len < ofs + je32_to_cpu(node->totlen)) {
++ buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs);
++ D1(printk(KERN_DEBUG "Fewer than %d bytes (dirent node) left to end of buf. Reading 0x%x at 0x%08x\n",
++ je32_to_cpu(node->totlen), buf_len, ofs));
++ err = jffs2_fill_scan_buf(c, buf, ofs, buf_len);
++ if (err)
++ return err;
++ buf_ofs = ofs;
++ node = (void *)buf;
++ }
++ err = jffs2_scan_dirent_node(c, jeb, (void *)node, ofs);
+ if (err) return err;
++ ofs += PAD(je32_to_cpu(node->totlen));
+ break;
+
+ case JFFS2_NODETYPE_CLEANMARKER:
+- if (node.totlen != sizeof(struct jffs2_unknown_node)) {
++ D1(printk(KERN_DEBUG "CLEANMARKER node found at 0x%08x\n", ofs));
++ if (je32_to_cpu(node->totlen) != c->cleanmarker_size) {
+ printk(KERN_NOTICE "CLEANMARKER node found at 0x%08x has totlen 0x%x != normal 0x%x\n",
+- ofs, node.totlen, sizeof(struct jffs2_unknown_node));
++ ofs, je32_to_cpu(node->totlen), c->cleanmarker_size);
+ DIRTY_SPACE(PAD(sizeof(struct jffs2_unknown_node)));
++ ofs += PAD(sizeof(struct jffs2_unknown_node));
+ } else if (jeb->first_node) {
+ printk(KERN_NOTICE "CLEANMARKER node found at 0x%08x, not first node in block (0x%08x)\n", ofs, jeb->offset);
+ DIRTY_SPACE(PAD(sizeof(struct jffs2_unknown_node)));
+ ofs += PAD(sizeof(struct jffs2_unknown_node));
+- continue;
+ } else {
+ struct jffs2_raw_node_ref *marker_ref = jffs2_alloc_raw_node_ref();
+ if (!marker_ref) {
+@@ -300,98 +553,80 @@
+ }
+ marker_ref->next_in_ino = NULL;
+ marker_ref->next_phys = NULL;
+- marker_ref->flash_offset = ofs;
+- marker_ref->totlen = sizeof(struct jffs2_unknown_node);
++ marker_ref->flash_offset = ofs | REF_NORMAL;
++ marker_ref->__totlen = c->cleanmarker_size;
+ jeb->first_node = jeb->last_node = marker_ref;
+
+- USED_SPACE(PAD(sizeof(struct jffs2_unknown_node)));
++ USED_SPACE(PAD(c->cleanmarker_size));
++ ofs += PAD(c->cleanmarker_size);
+ }
+- ofs += PAD(sizeof(struct jffs2_unknown_node));
++ break;
++
++ case JFFS2_NODETYPE_PADDING:
++ DIRTY_SPACE(PAD(je32_to_cpu(node->totlen)));
++ ofs += PAD(je32_to_cpu(node->totlen));
+ break;
+
+ default:
+- switch (node.nodetype & JFFS2_COMPAT_MASK) {
++ switch (je16_to_cpu(node->nodetype) & JFFS2_COMPAT_MASK) {
+ case JFFS2_FEATURE_ROCOMPAT:
+- printk(KERN_NOTICE "Read-only compatible feature node (0x%04x) found at offset 0x%08x\n", node.nodetype, ofs);
++ printk(KERN_NOTICE "Read-only compatible feature node (0x%04x) found at offset 0x%08x\n", je16_to_cpu(node->nodetype), ofs);
+ c->flags |= JFFS2_SB_FLAG_RO;
+- if (!(OFNI_BS_2SFFJ(c)->s_flags & MS_RDONLY))
++ if (!(jffs2_is_readonly(c)))
+ return -EROFS;
+- DIRTY_SPACE(PAD(node.totlen));
+- ofs += PAD(node.totlen);
+- continue;
++ DIRTY_SPACE(PAD(je32_to_cpu(node->totlen)));
++ ofs += PAD(je32_to_cpu(node->totlen));
++ break;
+
+ case JFFS2_FEATURE_INCOMPAT:
+- printk(KERN_NOTICE "Incompatible feature node (0x%04x) found at offset 0x%08x\n", node.nodetype, ofs);
++ printk(KERN_NOTICE "Incompatible feature node (0x%04x) found at offset 0x%08x\n", je16_to_cpu(node->nodetype), ofs);
+ return -EINVAL;
+
+ case JFFS2_FEATURE_RWCOMPAT_DELETE:
+- printk(KERN_NOTICE "Unknown but compatible feature node (0x%04x) found at offset 0x%08x\n", node.nodetype, ofs);
+- DIRTY_SPACE(PAD(node.totlen));
+- ofs += PAD(node.totlen);
++ D1(printk(KERN_NOTICE "Unknown but compatible feature node (0x%04x) found at offset 0x%08x\n", je16_to_cpu(node->nodetype), ofs));
++ DIRTY_SPACE(PAD(je32_to_cpu(node->totlen)));
++ ofs += PAD(je32_to_cpu(node->totlen));
+ break;
+
+ case JFFS2_FEATURE_RWCOMPAT_COPY:
+- printk(KERN_NOTICE "Unknown but compatible feature node (0x%04x) found at offset 0x%08x\n", node.nodetype, ofs);
+- USED_SPACE(PAD(node.totlen));
+- ofs += PAD(node.totlen);
++ D1(printk(KERN_NOTICE "Unknown but compatible feature node (0x%04x) found at offset 0x%08x\n", je16_to_cpu(node->nodetype), ofs));
++ USED_SPACE(PAD(je32_to_cpu(node->totlen)));
++ ofs += PAD(je32_to_cpu(node->totlen));
+ break;
+ }
+ }
+ }
+- D1(printk(KERN_DEBUG "Block at 0x%08x: free 0x%08x, dirty 0x%08x, used 0x%08x\n", jeb->offset,
+- jeb->free_size, jeb->dirty_size, jeb->used_size));
+- return 0;
+-}
+
+-/* We're pointing at the first empty word on the flash. Scan and account for the whole dirty region */
+-static int jffs2_scan_empty(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, __u32 *startofs, int *noise)
+-{
+- __u32 *buf;
+- __u32 scanlen = (jeb->offset + c->sector_size) - *startofs;
+- __u32 curofs = *startofs;
+-
+- buf = kmalloc(min((__u32)PAGE_SIZE, scanlen), GFP_KERNEL);
+- if (!buf) {
+- printk(KERN_WARNING "Scan buffer allocation failed\n");
+- return -ENOMEM;
+- }
+- while(scanlen) {
+- ssize_t retlen;
+- int ret, i;
+-
+- ret = c->mtd->read(c->mtd, curofs, min((__u32)PAGE_SIZE, scanlen), &retlen, (char *)buf);
+- if(ret) {
+- D1(printk(KERN_WARNING "jffs2_scan_empty(): Read 0x%x bytes at 0x%08x returned %d\n", min((__u32)PAGE_SIZE, scanlen), curofs, ret));
+- kfree(buf);
+- return ret;
+- }
+- if (retlen < 4) {
+- D1(printk(KERN_WARNING "Eep. too few bytes read in scan_empty()\n"));
+- kfree(buf);
+- return -EIO;
+- }
+- for (i=0; i<(retlen / 4); i++) {
+- if (buf[i] != 0xffffffff) {
+- curofs += i*4;
+-
+- noisy_printk(noise, "jffs2_scan_empty(): Empty block at 0x%08x ends at 0x%08x (with 0x%08x)! Marking dirty\n", *startofs, curofs, buf[i]);
+- DIRTY_SPACE(curofs - (*startofs));
+- *startofs = curofs;
+- kfree(buf);
+- return 0;
+- }
+- }
+- scanlen -= retlen&~3;
+- curofs += retlen&~3;
+- }
+
+- D1(printk(KERN_DEBUG "Empty flash detected from 0x%08x to 0x%08x\n", *startofs, curofs));
+- kfree(buf);
+- *startofs = curofs;
+- return 0;
++ D1(printk(KERN_DEBUG "Block at 0x%08x: free 0x%08x, dirty 0x%08x, unchecked 0x%08x, used 0x%08x\n", jeb->offset,
++ jeb->free_size, jeb->dirty_size, jeb->unchecked_size, jeb->used_size));
++
++ /* mark_node_obsolete can add to wasted !! */
++ if (jeb->wasted_size) {
++ jeb->dirty_size += jeb->wasted_size;
++ c->dirty_size += jeb->wasted_size;
++ c->wasted_size -= jeb->wasted_size;
++ jeb->wasted_size = 0;
++ }
++
++ if ((jeb->used_size + jeb->unchecked_size) == PAD(c->cleanmarker_size) && !jeb->dirty_size
++ && (!jeb->first_node || jeb->first_node->next_in_ino) )
++ return BLK_STATE_CLEANMARKER;
++
++ /* move blocks with max 4 byte dirty space to cleanlist */
++ else if (!ISDIRTY(c->sector_size - (jeb->used_size + jeb->unchecked_size))) {
++ c->dirty_size -= jeb->dirty_size;
++ c->wasted_size += jeb->dirty_size;
++ jeb->wasted_size += jeb->dirty_size;
++ jeb->dirty_size = 0;
++ return BLK_STATE_CLEAN;
++ } else if (jeb->used_size || jeb->unchecked_size)
++ return BLK_STATE_PARTDIRTY;
++ else
++ return BLK_STATE_ALLDIRTY;
+ }
+
+-static struct jffs2_inode_cache *jffs2_scan_make_ino_cache(struct jffs2_sb_info *c, __u32 ino)
++static struct jffs2_inode_cache *jffs2_scan_make_ino_cache(struct jffs2_sb_info *c, uint32_t ino)
+ {
+ struct jffs2_inode_cache *ic;
+
+@@ -399,137 +634,77 @@
+ if (ic)
+ return ic;
+
++ if (ino > c->highest_ino)
++ c->highest_ino = ino;
++
+ ic = jffs2_alloc_inode_cache();
+ if (!ic) {
+ printk(KERN_NOTICE "jffs2_scan_make_inode_cache(): allocation of inode cache failed\n");
+ return NULL;
+ }
+ memset(ic, 0, sizeof(*ic));
+- ic->scan = kmalloc(sizeof(struct jffs2_scan_info), GFP_KERNEL);
+- if (!ic->scan) {
+- printk(KERN_NOTICE "jffs2_scan_make_inode_cache(): allocation of scan info for inode cache failed\n");
+- jffs2_free_inode_cache(ic);
+- return NULL;
+- }
+- memset(ic->scan, 0, sizeof(*ic->scan));
++
+ ic->ino = ino;
+ ic->nodes = (void *)ic;
+ jffs2_add_ino_cache(c, ic);
+ if (ino == 1)
+- ic->nlink=1;
++ ic->nlink = 1;
+ return ic;
+ }
+
+-static int jffs2_scan_inode_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, __u32 *ofs)
++static int jffs2_scan_inode_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
++ struct jffs2_raw_inode *ri, uint32_t ofs)
+ {
+ struct jffs2_raw_node_ref *raw;
+- struct jffs2_full_dnode *fn;
+- struct jffs2_tmp_dnode_info *tn, **tn_list;
+ struct jffs2_inode_cache *ic;
+- struct jffs2_raw_inode ri;
+- __u32 crc;
+- __u16 oldnodetype;
+- int ret;
+- ssize_t retlen;
+-
+- D1(printk(KERN_DEBUG "jffs2_scan_inode_node(): Node at 0x%08x\n", *ofs));
+-
+- ret = c->mtd->read(c->mtd, *ofs, sizeof(ri), &retlen, (char *)&ri);
+- if (ret) {
+- printk(KERN_NOTICE "jffs2_scan_inode_node(): Read error at 0x%08x: %d\n", *ofs, ret);
+- return ret;
+- }
+- if (retlen != sizeof(ri)) {
+- printk(KERN_NOTICE "Short read: 0x%x bytes at 0x%08x instead of requested %x\n",
+- retlen, *ofs, sizeof(ri));
+- return -EIO;
+- }
++ uint32_t ino = je32_to_cpu(ri->ino);
+
+- /* We sort of assume that the node was accurate when it was
+- first written to the medium :) */
+- oldnodetype = ri.nodetype;
+- ri.nodetype |= JFFS2_NODE_ACCURATE;
+- crc = crc32(0, &ri, sizeof(ri)-8);
+- ri.nodetype = oldnodetype;
++ D1(printk(KERN_DEBUG "jffs2_scan_inode_node(): Node at 0x%08x\n", ofs));
+
+- if(crc != ri.node_crc) {
+- printk(KERN_NOTICE "jffs2_scan_inode_node(): CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
+- *ofs, ri.node_crc, crc);
+- /* FIXME: Why do we believe totlen? */
+- DIRTY_SPACE(4);
+- *ofs += 4;
+- return 0;
+- }
+- /* There was a bug where we wrote hole nodes out with csize/dsize
+- swapped. Deal with it */
+- if (ri.compr == JFFS2_COMPR_ZERO && !ri.dsize && ri.csize) {
+- ri.dsize = ri.csize;
+- ri.csize = 0;
+- }
++ /* We do very little here now. Just check the ino# to which we should attribute
++ this node; we can do all the CRC checking etc. later. There's a tradeoff here --
++ we used to scan the flash once only, reading everything we want from it into
++ memory, then building all our in-core data structures and freeing the extra
++ information. Now we allow the first part of the mount to complete a lot quicker,
++ but we have to go _back_ to the flash in order to finish the CRC checking, etc.
++ Which means that the _full_ amount of time to get to proper write mode with GC
++ operational may actually be _longer_ than before. Sucks to be me. */
+
+- if (ri.csize) {
+- /* Check data CRC too */
+- unsigned char *dbuf;
+- __u32 crc;
+-
+- dbuf = kmalloc(PAGE_CACHE_SIZE, GFP_KERNEL);
+- if (!dbuf) {
+- printk(KERN_NOTICE "jffs2_scan_inode_node(): allocation of temporary data buffer for CRC check failed\n");
+- return -ENOMEM;
+- }
+- ret = c->mtd->read(c->mtd, *ofs+sizeof(ri), ri.csize, &retlen, dbuf);
+- if (ret) {
+- printk(KERN_NOTICE "jffs2_scan_inode_node(): Read error at 0x%08x: %d\n", *ofs+sizeof(ri), ret);
+- kfree(dbuf);
+- return ret;
+- }
+- if (retlen != ri.csize) {
+- printk(KERN_NOTICE "Short read: 0x%x bytes at 0x%08x instead of requested %x\n",
+- retlen, *ofs+ sizeof(ri), ri.csize);
+- kfree(dbuf);
+- return -EIO;
+- }
+- crc = crc32(0, dbuf, ri.csize);
+- kfree(dbuf);
+- if (crc != ri.data_crc) {
+- printk(KERN_NOTICE "jffs2_scan_inode_node(): Data CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
+- *ofs, ri.data_crc, crc);
+- DIRTY_SPACE(PAD(ri.totlen));
+- *ofs += PAD(ri.totlen);
+- return 0;
+- }
+- }
+-
+- /* Wheee. It worked */
+ raw = jffs2_alloc_raw_node_ref();
+ if (!raw) {
+ printk(KERN_NOTICE "jffs2_scan_inode_node(): allocation of node reference failed\n");
+ return -ENOMEM;
+ }
+- tn = jffs2_alloc_tmp_dnode_info();
+- if (!tn) {
+- jffs2_free_raw_node_ref(raw);
+- return -ENOMEM;
+- }
+- fn = jffs2_alloc_full_dnode();
+- if (!fn) {
+- jffs2_free_tmp_dnode_info(tn);
++
++ ic = jffs2_get_ino_cache(c, ino);
++ if (!ic) {
++ /* Inocache get failed. Either we read a bogus ino# or it's just genuinely the
++ first node we found for this inode. Do a CRC check to protect against the former
++ case */
++ uint32_t crc = crc32(0, ri, sizeof(*ri)-8);
++
++ if (crc != je32_to_cpu(ri->node_crc)) {
++ printk(KERN_NOTICE "jffs2_scan_inode_node(): CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
++ ofs, je32_to_cpu(ri->node_crc), crc);
++ /* We believe totlen because the CRC on the node _header_ was OK, just the node itself failed. */
++ DIRTY_SPACE(PAD(je32_to_cpu(ri->totlen)));
+ jffs2_free_raw_node_ref(raw);
+- return -ENOMEM;
++ return 0;
+ }
+- ic = jffs2_scan_make_ino_cache(c, ri.ino);
++ ic = jffs2_scan_make_ino_cache(c, ino);
+ if (!ic) {
+- jffs2_free_full_dnode(fn);
+- jffs2_free_tmp_dnode_info(tn);
+ jffs2_free_raw_node_ref(raw);
+ return -ENOMEM;
+ }
++ }
+
+- /* Build the data structures and file them for later */
+- raw->flash_offset = *ofs;
+- raw->totlen = PAD(ri.totlen);
++ /* Wheee. It worked */
++
++ raw->flash_offset = ofs | REF_UNCHECKED;
++ raw->__totlen = PAD(je32_to_cpu(ri->totlen));
+ raw->next_phys = NULL;
+ raw->next_in_ino = ic->nodes;
++
+ ic->nodes = raw;
+ if (!jeb->first_node)
+ jeb->first_node = raw;
+@@ -538,134 +713,56 @@
+ jeb->last_node = raw;
+
+ D1(printk(KERN_DEBUG "Node is ino #%u, version %d. Range 0x%x-0x%x\n",
+- ri.ino, ri.version, ri.offset, ri.offset+ri.dsize));
+-
+- pseudo_random += ri.version;
+-
+- for (tn_list = &ic->scan->tmpnodes; *tn_list; tn_list = &((*tn_list)->next)) {
+- if ((*tn_list)->version < ri.version)
+- continue;
+- if ((*tn_list)->version > ri.version)
+- break;
+- /* Wheee. We've found another instance of the same version number.
+- We should obsolete one of them.
+- */
+- D1(printk(KERN_DEBUG "Duplicate version %d found in ino #%u. Previous one is at 0x%08x\n", ri.version, ic->ino, (*tn_list)->fn->raw->flash_offset &~3));
+- if (!jeb->used_size) {
+- D1(printk(KERN_DEBUG "No valid nodes yet found in this eraseblock 0x%08x, so obsoleting the new instance at 0x%08x\n",
+- jeb->offset, raw->flash_offset & ~3));
+- ri.nodetype &= ~JFFS2_NODE_ACCURATE;
+- /* Perhaps we could also mark it as such on the medium. Maybe later */
+- }
+- break;
+- }
+-
+- if (ri.nodetype & JFFS2_NODE_ACCURATE) {
+- memset(fn,0,sizeof(*fn));
+-
+- fn->ofs = ri.offset;
+- fn->size = ri.dsize;
+- fn->frags = 0;
+- fn->raw = raw;
+-
+- tn->next = NULL;
+- tn->fn = fn;
+- tn->version = ri.version;
++ je32_to_cpu(ri->ino), je32_to_cpu(ri->version),
++ je32_to_cpu(ri->offset),
++ je32_to_cpu(ri->offset)+je32_to_cpu(ri->dsize)));
+
+- USED_SPACE(PAD(ri.totlen));
+- jffs2_add_tn_to_list(tn, &ic->scan->tmpnodes);
+- /* Make sure the one we just added is the _last_ in the list
+- with this version number, so the older ones get obsoleted */
+- while (tn->next && tn->next->version == tn->version) {
++ pseudo_random += je32_to_cpu(ri->version);
+
+- D1(printk(KERN_DEBUG "Shifting new node at 0x%08x after other node at 0x%08x for version %d in list\n",
+- fn->raw->flash_offset&~3, tn->next->fn->raw->flash_offset &~3, ri.version));
+-
+- if(tn->fn != fn)
+- BUG();
+- tn->fn = tn->next->fn;
+- tn->next->fn = fn;
+- tn = tn->next;
+- }
+- } else {
+- jffs2_free_full_dnode(fn);
+- jffs2_free_tmp_dnode_info(tn);
+- raw->flash_offset |= 1;
+- DIRTY_SPACE(PAD(ri.totlen));
+- }
+- *ofs += PAD(ri.totlen);
++ UNCHECKED_SPACE(PAD(je32_to_cpu(ri->totlen)));
+ return 0;
+ }
+
+-static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, __u32 *ofs)
++static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
++ struct jffs2_raw_dirent *rd, uint32_t ofs)
+ {
+ struct jffs2_raw_node_ref *raw;
+ struct jffs2_full_dirent *fd;
+ struct jffs2_inode_cache *ic;
+- struct jffs2_raw_dirent rd;
+- __u16 oldnodetype;
+- int ret;
+- __u32 crc;
+- ssize_t retlen;
+-
+- D1(printk(KERN_DEBUG "jffs2_scan_dirent_node(): Node at 0x%08x\n", *ofs));
++ uint32_t crc;
+
+- ret = c->mtd->read(c->mtd, *ofs, sizeof(rd), &retlen, (char *)&rd);
+- if (ret) {
+- printk(KERN_NOTICE "jffs2_scan_dirent_node(): Read error at 0x%08x: %d\n", *ofs, ret);
+- return ret;
+- }
+- if (retlen != sizeof(rd)) {
+- printk(KERN_NOTICE "Short read: 0x%x bytes at 0x%08x instead of requested %x\n",
+- retlen, *ofs, sizeof(rd));
+- return -EIO;
+- }
++ D1(printk(KERN_DEBUG "jffs2_scan_dirent_node(): Node at 0x%08x\n", ofs));
+
+- /* We sort of assume that the node was accurate when it was
+- first written to the medium :) */
+- oldnodetype = rd.nodetype;
+- rd.nodetype |= JFFS2_NODE_ACCURATE;
+- crc = crc32(0, &rd, sizeof(rd)-8);
+- rd.nodetype = oldnodetype;
++ /* We don't get here unless the node is still valid, so we don't have to
++ mask in the ACCURATE bit any more. */
++ crc = crc32(0, rd, sizeof(*rd)-8);
+
+- if (crc != rd.node_crc) {
++ if (crc != je32_to_cpu(rd->node_crc)) {
+ printk(KERN_NOTICE "jffs2_scan_dirent_node(): Node CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
+- *ofs, rd.node_crc, crc);
+- /* FIXME: Why do we believe totlen? */
+- DIRTY_SPACE(4);
+- *ofs += 4;
++ ofs, je32_to_cpu(rd->node_crc), crc);
++ /* We believe totlen because the CRC on the node _header_ was OK, just the node itself failed. */
++ DIRTY_SPACE(PAD(je32_to_cpu(rd->totlen)));
+ return 0;
+ }
+
+- pseudo_random += rd.version;
++ pseudo_random += je32_to_cpu(rd->version);
+
+- fd = jffs2_alloc_full_dirent(rd.nsize+1);
++ fd = jffs2_alloc_full_dirent(rd->nsize+1);
+ if (!fd) {
+ return -ENOMEM;
+-}
+- ret = c->mtd->read(c->mtd, *ofs + sizeof(rd), rd.nsize, &retlen, &fd->name[0]);
+- if (ret) {
+- jffs2_free_full_dirent(fd);
+- printk(KERN_NOTICE "jffs2_scan_dirent_node(): Read error at 0x%08x: %d\n",
+- *ofs + sizeof(rd), ret);
+- return ret;
+- }
+- if (retlen != rd.nsize) {
+- jffs2_free_full_dirent(fd);
+- printk(KERN_NOTICE "Short read: 0x%x bytes at 0x%08x instead of requested %x\n",
+- retlen, *ofs + sizeof(rd), rd.nsize);
+- return -EIO;
+ }
+- crc = crc32(0, fd->name, rd.nsize);
+- if (crc != rd.name_crc) {
++ memcpy(&fd->name, rd->name, rd->nsize);
++ fd->name[rd->nsize] = 0;
++
++ crc = crc32(0, fd->name, rd->nsize);
++ if (crc != je32_to_cpu(rd->name_crc)) {
+ printk(KERN_NOTICE "jffs2_scan_dirent_node(): Name CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
+- *ofs, rd.name_crc, crc);
+- fd->name[rd.nsize]=0;
+- D1(printk(KERN_NOTICE "Name for which CRC failed is (now) '%s', ino #%d\n", fd->name, rd.ino));
++ ofs, je32_to_cpu(rd->name_crc), crc);
++ D1(printk(KERN_NOTICE "Name for which CRC failed is (now) '%s', ino #%d\n", fd->name, je32_to_cpu(rd->ino)));
+ jffs2_free_full_dirent(fd);
+ /* FIXME: Why do we believe totlen? */
+- DIRTY_SPACE(PAD(rd.totlen));
+- *ofs += PAD(rd.totlen);
++ /* We believe totlen because the CRC on the node _header_ was OK, just the name failed. */
++ DIRTY_SPACE(PAD(je32_to_cpu(rd->totlen)));
+ return 0;
+ }
+ raw = jffs2_alloc_raw_node_ref();
+@@ -674,15 +771,15 @@
+ printk(KERN_NOTICE "jffs2_scan_dirent_node(): allocation of node reference failed\n");
+ return -ENOMEM;
+ }
+- ic = jffs2_scan_make_ino_cache(c, rd.pino);
++ ic = jffs2_scan_make_ino_cache(c, je32_to_cpu(rd->pino));
+ if (!ic) {
+ jffs2_free_full_dirent(fd);
+ jffs2_free_raw_node_ref(raw);
+ return -ENOMEM;
+ }
+
+- raw->totlen = PAD(rd.totlen);
+- raw->flash_offset = *ofs;
++ raw->__totlen = PAD(je32_to_cpu(rd->totlen));
++ raw->flash_offset = ofs | REF_PRISTINE;
+ raw->next_phys = NULL;
+ raw->next_in_ino = ic->nodes;
+ ic->nodes = raw;
+@@ -692,24 +789,15 @@
+ jeb->last_node->next_phys = raw;
+ jeb->last_node = raw;
+
+- if (rd.nodetype & JFFS2_NODE_ACCURATE) {
+ fd->raw = raw;
+ fd->next = NULL;
+- fd->version = rd.version;
+- fd->ino = rd.ino;
+- fd->name[rd.nsize]=0;
+- fd->nhash = full_name_hash(fd->name, rd.nsize);
+- fd->type = rd.type;
+-
+- USED_SPACE(PAD(rd.totlen));
+- jffs2_add_fd_to_list(c, fd, &ic->scan->dents);
+- } else {
+- raw->flash_offset |= 1;
+- jffs2_free_full_dirent(fd);
++ fd->version = je32_to_cpu(rd->version);
++ fd->ino = je32_to_cpu(rd->ino);
++ fd->nhash = full_name_hash(fd->name, rd->nsize);
++ fd->type = rd->type;
++ USED_SPACE(PAD(je32_to_cpu(rd->totlen)));
++ jffs2_add_fd_to_list(c, fd, &ic->scan_dents);
+
+- DIRTY_SPACE(PAD(rd.totlen));
+- }
+- *ofs += PAD(rd.totlen);
+ return 0;
+ }
+
+@@ -731,26 +819,90 @@
+ struct list_head *n = head->next;
+
+ list_del(head);
+- while(count--)
++ while(count--) {
+ n = n->next;
++ }
+ list_add(head, n);
+ }
+
+-static void jffs2_rotate_lists(struct jffs2_sb_info *c)
++void jffs2_rotate_lists(struct jffs2_sb_info *c)
+ {
+ uint32_t x;
++ uint32_t rotateby;
+
+ x = count_list(&c->clean_list);
+- if (x)
+- rotate_list((&c->clean_list), pseudo_random % x);
++ if (x) {
++ rotateby = pseudo_random % x;
++ D1(printk(KERN_DEBUG "Rotating clean_list by %d\n", rotateby));
++
++ rotate_list((&c->clean_list), rotateby);
++
++ D1(printk(KERN_DEBUG "Erase block at front of clean_list is at %08x\n",
++ list_entry(c->clean_list.next, struct jffs2_eraseblock, list)->offset));
++ } else {
++ D1(printk(KERN_DEBUG "Not rotating empty clean_list\n"));
++ }
++
++ x = count_list(&c->very_dirty_list);
++ if (x) {
++ rotateby = pseudo_random % x;
++ D1(printk(KERN_DEBUG "Rotating very_dirty_list by %d\n", rotateby));
++
++ rotate_list((&c->very_dirty_list), rotateby);
++
++ D1(printk(KERN_DEBUG "Erase block at front of very_dirty_list is at %08x\n",
++ list_entry(c->very_dirty_list.next, struct jffs2_eraseblock, list)->offset));
++ } else {
++ D1(printk(KERN_DEBUG "Not rotating empty very_dirty_list\n"));
++ }
+
+ x = count_list(&c->dirty_list);
+- if (x)
+- rotate_list((&c->dirty_list), pseudo_random % x);
++ if (x) {
++ rotateby = pseudo_random % x;
++ D1(printk(KERN_DEBUG "Rotating dirty_list by %d\n", rotateby));
++
++ rotate_list((&c->dirty_list), rotateby);
++
++ D1(printk(KERN_DEBUG "Erase block at front of dirty_list is at %08x\n",
++ list_entry(c->dirty_list.next, struct jffs2_eraseblock, list)->offset));
++ } else {
++ D1(printk(KERN_DEBUG "Not rotating empty dirty_list\n"));
++ }
++
++ x = count_list(&c->erasable_list);
++ if (x) {
++ rotateby = pseudo_random % x;
++ D1(printk(KERN_DEBUG "Rotating erasable_list by %d\n", rotateby));
+
+- if (c->nr_erasing_blocks)
+- rotate_list((&c->erase_pending_list), pseudo_random % c->nr_erasing_blocks);
++ rotate_list((&c->erasable_list), rotateby);
+
+- if (c->nr_free_blocks) /* Not that it should ever be zero */
+- rotate_list((&c->free_list), pseudo_random % c->nr_free_blocks);
++ D1(printk(KERN_DEBUG "Erase block at front of erasable_list is at %08x\n",
++ list_entry(c->erasable_list.next, struct jffs2_eraseblock, list)->offset));
++ } else {
++ D1(printk(KERN_DEBUG "Not rotating empty erasable_list\n"));
++ }
++
++ if (c->nr_erasing_blocks) {
++ rotateby = pseudo_random % c->nr_erasing_blocks;
++ D1(printk(KERN_DEBUG "Rotating erase_pending_list by %d\n", rotateby));
++
++ rotate_list((&c->erase_pending_list), rotateby);
++
++ D1(printk(KERN_DEBUG "Erase block at front of erase_pending_list is at %08x\n",
++ list_entry(c->erase_pending_list.next, struct jffs2_eraseblock, list)->offset));
++ } else {
++ D1(printk(KERN_DEBUG "Not rotating empty erase_pending_list\n"));
++ }
++
++ if (c->nr_free_blocks) {
++ rotateby = pseudo_random % c->nr_free_blocks;
++ D1(printk(KERN_DEBUG "Rotating free_list by %d\n", rotateby));
++
++ rotate_list((&c->free_list), rotateby);
++
++ D1(printk(KERN_DEBUG "Erase block at front of free_list is at %08x\n",
++ list_entry(c->free_list.next, struct jffs2_eraseblock, list)->offset));
++ } else {
++ D1(printk(KERN_DEBUG "Not rotating empty free_list\n"));
++ }
+ }
+diff -Nurb linux-mips-2.4.24-pre2/fs/jffs2/super-v24.c linux/fs/jffs2/super-v24.c
+--- linux-mips-2.4.24-pre2/fs/jffs2/super-v24.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/fs/jffs2/super-v24.c 2004-11-17 18:17:59.374263000 +0100
+@@ -0,0 +1,167 @@
++/*
++ * JFFS2 -- Journalling Flash File System, Version 2.
++ *
++ * Copyright (C) 2001-2003 Red Hat, Inc.
++ *
++ * Created by David Woodhouse <dwmw2@redhat.com>
++ *
++ * For licensing information, see the file 'LICENCE' in this directory.
++ *
++ * $Id$
++ *
++ */
++
++#include <linux/config.h>
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/version.h>
++#include <linux/slab.h>
++#include <linux/init.h>
++#include <linux/list.h>
++#include <linux/fs.h>
++#include <linux/jffs2.h>
++#include <linux/pagemap.h>
++#include <linux/mtd/mtd.h>
++#include "nodelist.h"
++
++#ifndef MTD_BLOCK_MAJOR
++#define MTD_BLOCK_MAJOR 31
++#endif
++
++static void jffs2_put_super (struct super_block *);
++
++static struct super_operations jffs2_super_operations =
++{
++ .read_inode = jffs2_read_inode,
++ .put_super = jffs2_put_super,
++ .write_super = jffs2_write_super,
++ .statfs = jffs2_statfs,
++ .remount_fs = jffs2_remount_fs,
++ .clear_inode = jffs2_clear_inode,
++ .dirty_inode = jffs2_dirty_inode,
++};
++
++
++static struct super_block *jffs2_read_super(struct super_block *sb, void *data, int silent)
++{
++ struct jffs2_sb_info *c;
++ int ret;
++
++ D1(printk(KERN_DEBUG "jffs2: read_super for device %s\n", kdevname(sb->s_dev)));
++
++ if (major(sb->s_dev) != MTD_BLOCK_MAJOR) {
++ if (!silent)
++ printk(KERN_DEBUG "jffs2: attempt to mount non-MTD device %s\n", kdevname(sb->s_dev));
++ return NULL;
++ }
++
++ c = JFFS2_SB_INFO(sb);
++ memset(c, 0, sizeof(*c));
++
++ sb->s_op = &jffs2_super_operations;
++
++ c->mtd = get_mtd_device(NULL, minor(sb->s_dev));
++ if (!c->mtd) {
++ D1(printk(KERN_DEBUG "jffs2: MTD device #%u doesn't appear to exist\n", minor(sb->s_dev)));
++ return NULL;
++ }
++
++ ret = jffs2_do_fill_super(sb, data, silent);
++ if (ret) {
++ put_mtd_device(c->mtd);
++ return NULL;
++ }
++
++ return sb;
++}
++
++static void jffs2_put_super (struct super_block *sb)
++{
++ struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
++
++ D2(printk(KERN_DEBUG "jffs2: jffs2_put_super()\n"));
++
++
++ if (!(sb->s_flags & MS_RDONLY))
++ jffs2_stop_garbage_collect_thread(c);
++ down(&c->alloc_sem);
++ jffs2_flush_wbuf_pad(c);
++ up(&c->alloc_sem);
++ jffs2_free_ino_caches(c);
++ jffs2_free_raw_node_refs(c);
++ kfree(c->blocks);
++ jffs2_nand_flash_cleanup(c);
++ kfree(c->inocache_list);
++ if (c->mtd->sync)
++ c->mtd->sync(c->mtd);
++ put_mtd_device(c->mtd);
++
++ D1(printk(KERN_DEBUG "jffs2_put_super returning\n"));
++}
++
++static DECLARE_FSTYPE_DEV(jffs2_fs_type, "jffs2", jffs2_read_super);
++
++static int __init init_jffs2_fs(void)
++{
++ int ret;
++
++ printk(KERN_INFO "JFFS2 version 2.2."
++#ifdef CONFIG_FS_JFFS2_NAND
++ " (NAND)"
++#endif
++ " (C) 2001-2003 Red Hat, Inc.\n");
++
++#ifdef JFFS2_OUT_OF_KERNEL
++ /* sanity checks. Could we do these at compile time? */
++ if (sizeof(struct jffs2_sb_info) > sizeof (((struct super_block *)NULL)->u)) {
++ printk(KERN_ERR "JFFS2 error: struct jffs2_sb_info (%d bytes) doesn't fit in the super_block union (%d bytes)\n",
++ sizeof(struct jffs2_sb_info), sizeof (((struct super_block *)NULL)->u));
++ return -EIO;
++ }
++
++ if (sizeof(struct jffs2_inode_info) > sizeof (((struct inode *)NULL)->u)) {
++ printk(KERN_ERR "JFFS2 error: struct jffs2_inode_info (%d bytes) doesn't fit in the inode union (%d bytes)\n",
++ sizeof(struct jffs2_inode_info), sizeof (((struct inode *)NULL)->u));
++ return -EIO;
++ }
++#endif
++ ret = jffs2_zlib_init();
++ if (ret) {
++ printk(KERN_ERR "JFFS2 error: Failed to initialise zlib workspaces\n");
++ goto out;
++ }
++ ret = jffs2_create_slab_caches();
++ if (ret) {
++ printk(KERN_ERR "JFFS2 error: Failed to initialise slab caches\n");
++ goto out_zlib;
++ }
++ ret = register_filesystem(&jffs2_fs_type);
++ if (ret) {
++ printk(KERN_ERR "JFFS2 error: Failed to register filesystem\n");
++ goto out_slab;
++ }
++ return 0;
++
++ out_slab:
++ jffs2_destroy_slab_caches();
++ out_zlib:
++ jffs2_zlib_exit();
++ out:
++
++ return ret;
++}
++
++static void __exit exit_jffs2_fs(void)
++{
++ jffs2_destroy_slab_caches();
++ jffs2_zlib_exit();
++ unregister_filesystem(&jffs2_fs_type);
++}
++
++module_init(init_jffs2_fs);
++module_exit(exit_jffs2_fs);
++
++MODULE_DESCRIPTION("The Journalling Flash File System, v2");
++MODULE_AUTHOR("Red Hat, Inc.");
++MODULE_LICENSE("GPL"); // Actually dual-licensed, but it doesn't matter for
++ // the sake of this tag. It's Free Software.
+diff -Nurb linux-mips-2.4.24-pre2/fs/jffs2/super.c linux/fs/jffs2/super.c
+--- linux-mips-2.4.24-pre2/fs/jffs2/super.c 2004-11-17 18:05:04.000000000 +0100
++++ linux/fs/jffs2/super.c 2004-11-17 18:17:59.000000000 +0100
+@@ -1,291 +1,257 @@
+ /*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+- * Copyright (C) 2001 Red Hat, Inc.
++ * Copyright (C) 2001-2003 Red Hat, Inc.
+ *
+- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
++ * Created by David Woodhouse <dwmw2@redhat.com>
+ *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
++ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+- * The contents of this file are subject to the Red Hat eCos Public
+- * License Version 1.1 (the "Licence"); you may not use this file
+- * except in compliance with the Licence. You may obtain a copy of
+- * the Licence at http://www.redhat.com/
+- *
+- * Software distributed under the Licence is distributed on an "AS IS"
+- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+- * See the Licence for the specific language governing rights and
+- * limitations under the Licence.
+- *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
+- *
+- * Alternatively, the contents of this file may be used under the
+- * terms of the GNU General Public License version 2 (the "GPL"), in
+- * which case the provisions of the GPL are applicable instead of the
+- * above. If you wish to allow the use of your version of this file
+- * only under the terms of the GPL and not to allow others to use your
+- * version of this file under the RHEPL, indicate your decision by
+- * deleting the provisions above and replace them with the notice and
+- * other provisions required by the GPL. If you do not delete the
+- * provisions above, a recipient may use your version of this file
+- * under either the RHEPL or the GPL.
+- *
+- * $Id$
++ * $Id$
+ *
+ */
+
+ #include <linux/config.h>
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+-#include <linux/version.h>
+ #include <linux/slab.h>
+ #include <linux/init.h>
+ #include <linux/list.h>
+ #include <linux/fs.h>
++#include <linux/mount.h>
+ #include <linux/jffs2.h>
+ #include <linux/pagemap.h>
+ #include <linux/mtd/mtd.h>
+-#include <linux/interrupt.h>
++#include <linux/ctype.h>
++#include <linux/namei.h>
+ #include "nodelist.h"
+
+-#ifndef MTD_BLOCK_MAJOR
+-#define MTD_BLOCK_MAJOR 31
+-#endif
++static void jffs2_put_super(struct super_block *);
++
++static kmem_cache_t *jffs2_inode_cachep;
++
++static struct inode *jffs2_alloc_inode(struct super_block *sb)
++{
++ struct jffs2_inode_info *ei;
++ ei = (struct jffs2_inode_info *)kmem_cache_alloc(jffs2_inode_cachep, SLAB_KERNEL);
++ if (!ei)
++ return NULL;
++ return &ei->vfs_inode;
++}
++
++static void jffs2_destroy_inode(struct inode *inode)
++{
++ kmem_cache_free(jffs2_inode_cachep, JFFS2_INODE_INFO(inode));
++}
++
++static void jffs2_i_init_once(void * foo, kmem_cache_t * cachep, unsigned long flags)
++{
++ struct jffs2_inode_info *ei = (struct jffs2_inode_info *) foo;
+
+-extern void jffs2_read_inode (struct inode *);
+-void jffs2_put_super (struct super_block *);
+-void jffs2_write_super (struct super_block *);
+-static int jffs2_statfs (struct super_block *, struct statfs *);
+-int jffs2_remount_fs (struct super_block *, int *, char *);
+-extern void jffs2_clear_inode (struct inode *);
++ if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
++ SLAB_CTOR_CONSTRUCTOR) {
++ init_MUTEX_LOCKED(&ei->sem);
++ inode_init_once(&ei->vfs_inode);
++ }
++}
+
+ static struct super_operations jffs2_super_operations =
+ {
+- read_inode: jffs2_read_inode,
+-// delete_inode: jffs2_delete_inode,
+- put_super: jffs2_put_super,
+- write_super: jffs2_write_super,
+- statfs: jffs2_statfs,
+- remount_fs: jffs2_remount_fs,
+- clear_inode: jffs2_clear_inode
++ .alloc_inode = jffs2_alloc_inode,
++ .destroy_inode =jffs2_destroy_inode,
++ .read_inode = jffs2_read_inode,
++ .put_super = jffs2_put_super,
++ .write_super = jffs2_write_super,
++ .statfs = jffs2_statfs,
++ .remount_fs = jffs2_remount_fs,
++ .clear_inode = jffs2_clear_inode,
++ .dirty_inode = jffs2_dirty_inode,
+ };
+
+-static int jffs2_statfs(struct super_block *sb, struct statfs *buf)
++static int jffs2_sb_compare(struct super_block *sb, void *data)
+ {
++ struct jffs2_sb_info *p = data;
+ struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
+- unsigned long avail;
+
+- buf->f_type = JFFS2_SUPER_MAGIC;
+- buf->f_bsize = 1 << PAGE_SHIFT;
+- buf->f_blocks = c->flash_size >> PAGE_SHIFT;
+- buf->f_files = 0;
+- buf->f_ffree = 0;
+- buf->f_namelen = JFFS2_MAX_NAME_LEN;
+-
+- spin_lock_bh(&c->erase_completion_lock);
+-
+- avail = c->dirty_size + c->free_size;
+- if (avail > c->sector_size * JFFS2_RESERVED_BLOCKS_WRITE)
+- avail -= c->sector_size * JFFS2_RESERVED_BLOCKS_WRITE;
+- else
+- avail = 0;
+-
+- buf->f_bavail = buf->f_bfree = avail >> PAGE_SHIFT;
+-
+-#if CONFIG_JFFS2_FS_DEBUG > 0
+- printk(KERN_DEBUG "STATFS:\n");
+- printk(KERN_DEBUG "flash_size: %08x\n", c->flash_size);
+- printk(KERN_DEBUG "used_size: %08x\n", c->used_size);
+- printk(KERN_DEBUG "dirty_size: %08x\n", c->dirty_size);
+- printk(KERN_DEBUG "free_size: %08x\n", c->free_size);
+- printk(KERN_DEBUG "erasing_size: %08x\n", c->erasing_size);
+- printk(KERN_DEBUG "bad_size: %08x\n", c->bad_size);
+- printk(KERN_DEBUG "sector_size: %08x\n", c->sector_size);
+-
+- if (c->nextblock) {
+- printk(KERN_DEBUG "nextblock: 0x%08x\n", c->nextblock->offset);
+- } else {
+- printk(KERN_DEBUG "nextblock: NULL\n");
+- }
+- if (c->gcblock) {
+- printk(KERN_DEBUG "gcblock: 0x%08x\n", c->gcblock->offset);
++ /* The superblocks are considered to be equivalent if the underlying MTD
++ device is the same one */
++ if (c->mtd == p->mtd) {
++ D1(printk(KERN_DEBUG "jffs2_sb_compare: match on device %d (\"%s\")\n", p->mtd->index, p->mtd->name));
++ return 1;
+ } else {
+- printk(KERN_DEBUG "gcblock: NULL\n");
++ D1(printk(KERN_DEBUG "jffs2_sb_compare: No match, device %d (\"%s\"), device %d (\"%s\")\n",
++ c->mtd->index, c->mtd->name, p->mtd->index, p->mtd->name));
++ return 0;
+ }
+- if (list_empty(&c->clean_list)) {
+- printk(KERN_DEBUG "clean_list: empty\n");
+- } else {
+- struct list_head *this;
++}
+
+- list_for_each(this, &c->clean_list) {
+- struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
+- printk(KERN_DEBUG "clean_list: %08x\n", jeb->offset);
+- }
+- }
+- if (list_empty(&c->dirty_list)) {
+- printk(KERN_DEBUG "dirty_list: empty\n");
+- } else {
+- struct list_head *this;
++static int jffs2_sb_set(struct super_block *sb, void *data)
++{
++ struct jffs2_sb_info *p = data;
+
+- list_for_each(this, &c->dirty_list) {
+- struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
+- printk(KERN_DEBUG "dirty_list: %08x\n", jeb->offset);
+- }
+- }
+- if (list_empty(&c->erasing_list)) {
+- printk(KERN_DEBUG "erasing_list: empty\n");
+- } else {
+- struct list_head *this;
++ /* For persistence of NFS exports etc. we use the same s_dev
++ each time we mount the device, don't just use an anonymous
++ device */
++ sb->s_fs_info = p;
++ p->os_priv = sb;
++ sb->s_dev = MKDEV(MTD_BLOCK_MAJOR, p->mtd->index);
+
+- list_for_each(this, &c->erasing_list) {
+- struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
+- printk(KERN_DEBUG "erasing_list: %08x\n", jeb->offset);
+- }
+- }
+- if (list_empty(&c->erase_pending_list)) {
+- printk(KERN_DEBUG "erase_pending_list: empty\n");
+- } else {
+- struct list_head *this;
++ return 0;
++}
+
+- list_for_each(this, &c->erase_pending_list) {
+- struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
+- printk(KERN_DEBUG "erase_pending_list: %08x\n", jeb->offset);
+- }
+- }
+- if (list_empty(&c->free_list)) {
+- printk(KERN_DEBUG "free_list: empty\n");
+- } else {
+- struct list_head *this;
++static struct super_block *jffs2_get_sb_mtd(struct file_system_type *fs_type,
++ int flags, const char *dev_name,
++ void *data, struct mtd_info *mtd)
++{
++ struct super_block *sb;
++ struct jffs2_sb_info *c;
++ int ret;
+
+- list_for_each(this, &c->free_list) {
+- struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
+- printk(KERN_DEBUG "free_list: %08x\n", jeb->offset);
+- }
+- }
+- if (list_empty(&c->bad_list)) {
+- printk(KERN_DEBUG "bad_list: empty\n");
+- } else {
+- struct list_head *this;
++ c = kmalloc(sizeof(*c), GFP_KERNEL);
++ if (!c)
++ return ERR_PTR(-ENOMEM);
++ memset(c, 0, sizeof(*c));
++ c->mtd = mtd;
+
+- list_for_each(this, &c->bad_list) {
+- struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
+- printk(KERN_DEBUG "bad_list: %08x\n", jeb->offset);
+- }
+- }
+- if (list_empty(&c->bad_used_list)) {
+- printk(KERN_DEBUG "bad_used_list: empty\n");
+- } else {
+- struct list_head *this;
++ sb = sget(fs_type, jffs2_sb_compare, jffs2_sb_set, c);
++
++ if (IS_ERR(sb))
++ goto out_put;
+
+- list_for_each(this, &c->bad_used_list) {
+- struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
+- printk(KERN_DEBUG "bad_used_list: %08x\n", jeb->offset);
++ if (sb->s_root) {
++ /* New mountpoint for JFFS2 which is already mounted */
++ D1(printk(KERN_DEBUG "jffs2_get_sb_mtd(): Device %d (\"%s\") is already mounted\n",
++ mtd->index, mtd->name));
++ goto out_put;
+ }
++
++ D1(printk(KERN_DEBUG "jffs2_get_sb_mtd(): New superblock for device %d (\"%s\")\n",
++ mtd->index, mtd->name));
++
++ sb->s_op = &jffs2_super_operations;
++
++ ret = jffs2_do_fill_super(sb, data, (flags&MS_VERBOSE)?1:0);
++
++ if (ret) {
++ /* Failure case... */
++ up_write(&sb->s_umount);
++ deactivate_super(sb);
++ return ERR_PTR(ret);
+ }
+-#endif /* CONFIG_JFFS2_FS_DEBUG */
+
+- spin_unlock_bh(&c->erase_completion_lock);
++ sb->s_flags |= MS_ACTIVE;
++ return sb;
+
++ out_put:
++ kfree(c);
++ put_mtd_device(mtd);
+
+- return 0;
++ return sb;
+ }
+
+-static struct super_block *jffs2_read_super(struct super_block *sb, void *data, int silent)
++static struct super_block *jffs2_get_sb_mtdnr(struct file_system_type *fs_type,
++ int flags, const char *dev_name,
++ void *data, int mtdnr)
+ {
+- struct jffs2_sb_info *c;
+- struct inode *root_i;
+- int i;
+-
+- D1(printk(KERN_DEBUG "jffs2: read_super for device %s\n", kdevname(sb->s_dev)));
++ struct mtd_info *mtd;
+
+- if (MAJOR(sb->s_dev) != MTD_BLOCK_MAJOR) {
+- if (!silent)
+- printk(KERN_DEBUG "jffs2: attempt to mount non-MTD device %s\n", kdevname(sb->s_dev));
+- return NULL;
++ mtd = get_mtd_device(NULL, mtdnr);
++ if (!mtd) {
++ D1(printk(KERN_DEBUG "jffs2: MTD device #%u doesn't appear to exist\n", mtdnr));
++ return ERR_PTR(-EINVAL);
+ }
+
+- c = JFFS2_SB_INFO(sb);
+- memset(c, 0, sizeof(*c));
++ return jffs2_get_sb_mtd(fs_type, flags, dev_name, data, mtd);
++}
+
+- c->mtd = get_mtd_device(NULL, MINOR(sb->s_dev));
+- if (!c->mtd) {
+- D1(printk(KERN_DEBUG "jffs2: MTD device #%u doesn't appear to exist\n", MINOR(sb->s_dev)));
+- return NULL;
++static struct super_block *jffs2_get_sb(struct file_system_type *fs_type,
++ int flags, const char *dev_name,
++ void *data)
++{
++ int err;
++ struct nameidata nd;
++ int mtdnr;
++
++ if (!dev_name)
++ return ERR_PTR(-EINVAL);
++
++ D1(printk(KERN_DEBUG "jffs2_get_sb(): dev_name \"%s\"\n", dev_name));
++
++ /* The preferred way of mounting in future; especially when
++ CONFIG_BLK_DEV is implemented - we specify the underlying
++ MTD device by number or by name, so that we don't require
++ block device support to be present in the kernel. */
++
++ /* FIXME: How to do the root fs this way? */
++
++ if (dev_name[0] == 'm' && dev_name[1] == 't' && dev_name[2] == 'd') {
++ /* Probably mounting without the blkdev crap */
++ if (dev_name[3] == ':') {
++ struct mtd_info *mtd;
++
++ /* Mount by MTD device name */
++ D1(printk(KERN_DEBUG "jffs2_get_sb(): mtd:%%s, name \"%s\"\n", dev_name+4));
++ for (mtdnr = 0; mtdnr < MAX_MTD_DEVICES; mtdnr++) {
++ mtd = get_mtd_device(NULL, mtdnr);
++ if (mtd) {
++ if (!strcmp(mtd->name, dev_name+4))
++ return jffs2_get_sb_mtd(fs_type, flags, dev_name, data, mtd);
++ put_mtd_device(mtd);
+ }
+- c->sector_size = c->mtd->erasesize;
+- c->free_size = c->flash_size = c->mtd->size;
+- c->nr_blocks = c->mtd->size / c->mtd->erasesize;
+- c->blocks = kmalloc(sizeof(struct jffs2_eraseblock) * c->nr_blocks, GFP_KERNEL);
+- if (!c->blocks)
+- goto out_mtd;
+- for (i=0; i<c->nr_blocks; i++) {
+- INIT_LIST_HEAD(&c->blocks[i].list);
+- c->blocks[i].offset = i * c->sector_size;
+- c->blocks[i].free_size = c->sector_size;
+- c->blocks[i].dirty_size = 0;
+- c->blocks[i].used_size = 0;
+- c->blocks[i].first_node = NULL;
+- c->blocks[i].last_node = NULL;
+- }
+-
+- spin_lock_init(&c->nodelist_lock);
+- init_MUTEX(&c->alloc_sem);
+- init_waitqueue_head(&c->erase_wait);
+- spin_lock_init(&c->erase_completion_lock);
+- spin_lock_init(&c->inocache_lock);
+-
+- INIT_LIST_HEAD(&c->clean_list);
+- INIT_LIST_HEAD(&c->dirty_list);
+- INIT_LIST_HEAD(&c->erasing_list);
+- INIT_LIST_HEAD(&c->erase_pending_list);
+- INIT_LIST_HEAD(&c->erase_complete_list);
+- INIT_LIST_HEAD(&c->free_list);
+- INIT_LIST_HEAD(&c->bad_list);
+- INIT_LIST_HEAD(&c->bad_used_list);
+- c->highest_ino = 1;
+-
+- if (jffs2_build_filesystem(c)) {
+- D1(printk(KERN_DEBUG "build_fs failed\n"));
+- goto out_nodes;
+ }
++ printk(KERN_NOTICE "jffs2_get_sb(): MTD device with name \"%s\" not found.\n", dev_name+4);
++ } else if (isdigit(dev_name[3])) {
++ /* Mount by MTD device number name */
++ char *endptr;
+
+- sb->s_op = &jffs2_super_operations;
++ mtdnr = simple_strtoul(dev_name+3, &endptr, 0);
++ if (!*endptr) {
++ /* It was a valid number */
++ D1(printk(KERN_DEBUG "jffs2_get_sb(): mtd%%d, mtdnr %d\n", mtdnr));
++ return jffs2_get_sb_mtdnr(fs_type, flags, dev_name, data, mtdnr);
++ }
++ }
++ }
+
+- D1(printk(KERN_DEBUG "jffs2_read_super(): Getting root inode\n"));
+- root_i = iget(sb, 1);
+- if (is_bad_inode(root_i)) {
+- D1(printk(KERN_WARNING "get root inode failed\n"));
+- goto out_nodes;
++ /* Try the old way - the hack where we allowed users to mount
++ /dev/mtdblock$(n) but didn't actually _use_ the blkdev */
++
++ err = path_lookup(dev_name, LOOKUP_FOLLOW, &nd);
++
++ D1(printk(KERN_DEBUG "jffs2_get_sb(): path_lookup() returned %d, inode %p\n",
++ err, nd.dentry->d_inode));
++
++ if (err)
++ return ERR_PTR(err);
++
++ err = -EINVAL;
++
++ if (!S_ISBLK(nd.dentry->d_inode->i_mode))
++ goto out;
++
++ if (nd.mnt->mnt_flags & MNT_NODEV) {
++ err = -EACCES;
++ goto out;
+ }
+
+- D1(printk(KERN_DEBUG "jffs2_read_super(): d_alloc_root()\n"));
+- sb->s_root = d_alloc_root(root_i);
+- if (!sb->s_root)
+- goto out_root_i;
++ if (imajor(nd.dentry->d_inode) != MTD_BLOCK_MAJOR) {
++ if (!(flags & MS_VERBOSE)) /* Yes I mean this. Strangely */
++ printk(KERN_NOTICE "Attempt to mount non-MTD device \"%s\" as JFFS2\n",
++ dev_name);
++ goto out;
++ }
+
+-#if LINUX_VERSION_CODE >= 0x20403
+- sb->s_maxbytes = 0xFFFFFFFF;
+-#endif
+- sb->s_blocksize = PAGE_CACHE_SIZE;
+- sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
+- sb->s_magic = JFFS2_SUPER_MAGIC;
+- if (!(sb->s_flags & MS_RDONLY))
+- jffs2_start_garbage_collect_thread(c);
+- return sb;
++ mtdnr = iminor(nd.dentry->d_inode);
++ path_release(&nd);
+
+- out_root_i:
+- iput(root_i);
+- out_nodes:
+- jffs2_free_ino_caches(c);
+- jffs2_free_raw_node_refs(c);
+- kfree(c->blocks);
+- out_mtd:
+- put_mtd_device(c->mtd);
+- return NULL;
++ return jffs2_get_sb_mtdnr(fs_type, flags, dev_name, data, mtdnr);
++
++out:
++ path_release(&nd);
++ return ERR_PTR(err);
+ }
+
+-void jffs2_put_super (struct super_block *sb)
++static void jffs2_put_super (struct super_block *sb)
+ {
+ struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
+
+@@ -293,74 +259,53 @@
+
+ if (!(sb->s_flags & MS_RDONLY))
+ jffs2_stop_garbage_collect_thread(c);
++ down(&c->alloc_sem);
++ jffs2_flush_wbuf_pad(c);
++ up(&c->alloc_sem);
+ jffs2_free_ino_caches(c);
+ jffs2_free_raw_node_refs(c);
+ kfree(c->blocks);
++ jffs2_nand_flash_cleanup(c);
++ kfree(c->inocache_list);
+ if (c->mtd->sync)
+ c->mtd->sync(c->mtd);
+- put_mtd_device(c->mtd);
+
+ D1(printk(KERN_DEBUG "jffs2_put_super returning\n"));
+ }
+
+-int jffs2_remount_fs (struct super_block *sb, int *flags, char *data)
++static void jffs2_kill_sb(struct super_block *sb)
+ {
+ struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
+-
+- if (c->flags & JFFS2_SB_FLAG_RO && !(sb->s_flags & MS_RDONLY))
+- return -EROFS;
+-
+- /* We stop if it was running, then restart if it needs to.
+- This also catches the case where it was stopped and this
+- is just a remount to restart it */
+- if (!(sb->s_flags & MS_RDONLY))
+- jffs2_stop_garbage_collect_thread(c);
+-
+- if (!(*flags & MS_RDONLY))
+- jffs2_start_garbage_collect_thread(c);
+-
+- sb->s_flags = (sb->s_flags & ~MS_RDONLY)|(*flags & MS_RDONLY);
+-
+- return 0;
+-}
+-
+-void jffs2_write_super (struct super_block *sb)
+-{
+- struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
+- sb->s_dirt = 0;
+-
+- if (sb->s_flags & MS_RDONLY)
+- return;
+-
+- jffs2_garbage_collect_trigger(c);
+- jffs2_erase_pending_blocks(c);
+- jffs2_mark_erased_blocks(c);
++ generic_shutdown_super(sb);
++ put_mtd_device(c->mtd);
++ kfree(c);
+ }
+
+-
+-static DECLARE_FSTYPE_DEV(jffs2_fs_type, "jffs2", jffs2_read_super);
++static struct file_system_type jffs2_fs_type = {
++ .owner = THIS_MODULE,
++ .name = "jffs2",
++ .get_sb = jffs2_get_sb,
++ .kill_sb = jffs2_kill_sb,
++};
+
+ static int __init init_jffs2_fs(void)
+ {
+ int ret;
+
+- printk(KERN_NOTICE "JFFS2 version 2.1. (C) 2001 Red Hat, Inc., designed by Axis Communications AB.\n");
+-
+-#ifdef JFFS2_OUT_OF_KERNEL
+- /* sanity checks. Could we do these at compile time? */
+- if (sizeof(struct jffs2_sb_info) > sizeof (((struct super_block *)NULL)->u)) {
+- printk(KERN_ERR "JFFS2 error: struct jffs2_sb_info (%d bytes) doesn't fit in the super_block union (%d bytes)\n",
+- sizeof(struct jffs2_sb_info), sizeof (((struct super_block *)NULL)->u));
+- return -EIO;
+- }
+-
+- if (sizeof(struct jffs2_inode_info) > sizeof (((struct inode *)NULL)->u)) {
+- printk(KERN_ERR "JFFS2 error: struct jffs2_inode_info (%d bytes) doesn't fit in the inode union (%d bytes)\n",
+- sizeof(struct jffs2_inode_info), sizeof (((struct inode *)NULL)->u));
+- return -EIO;
+- }
++ printk(KERN_INFO "JFFS2 version 2.2."
++#ifdef CONFIG_FS_JFFS2_NAND
++ " (NAND)"
+ #endif
++ " (C) 2001-2003 Red Hat, Inc.\n");
+
++ jffs2_inode_cachep = kmem_cache_create("jffs2_i",
++ sizeof(struct jffs2_inode_info),
++ 0, SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT,
++ jffs2_i_init_once, NULL);
++ if (!jffs2_inode_cachep) {
++ printk(KERN_ERR "JFFS2 error: Failed to initialise inode cache\n");
++ return -ENOMEM;
++ }
+ ret = jffs2_zlib_init();
+ if (ret) {
+ printk(KERN_ERR "JFFS2 error: Failed to initialise zlib workspaces\n");
+@@ -388,9 +333,10 @@
+
+ static void __exit exit_jffs2_fs(void)
+ {
++ unregister_filesystem(&jffs2_fs_type);
+ jffs2_destroy_slab_caches();
+ jffs2_zlib_exit();
+- unregister_filesystem(&jffs2_fs_type);
++ kmem_cache_destroy(jffs2_inode_cachep);
+ }
+
+ module_init(init_jffs2_fs);
+diff -Nurb linux-mips-2.4.24-pre2/fs/jffs2/symlink.c linux/fs/jffs2/symlink.c
+--- linux-mips-2.4.24-pre2/fs/jffs2/symlink.c 2004-11-17 18:05:04.000000000 +0100
++++ linux/fs/jffs2/symlink.c 2004-11-17 18:17:59.000000000 +0100
+@@ -3,35 +3,11 @@
+ *
+ * Copyright (C) 2001, 2002 Red Hat, Inc.
+ *
+- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
++ * Created by David Woodhouse <dwmw2@redhat.com>
+ *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
++ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+- * The contents of this file are subject to the Red Hat eCos Public
+- * License Version 1.1 (the "Licence"); you may not use this file
+- * except in compliance with the Licence. You may obtain a copy of
+- * the Licence at http://www.redhat.com/
+- *
+- * Software distributed under the Licence is distributed on an "AS IS"
+- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+- * See the Licence for the specific language governing rights and
+- * limitations under the Licence.
+- *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
+- *
+- * Alternatively, the contents of this file may be used under the
+- * terms of the GNU General Public License version 2 (the "GPL"), in
+- * which case the provisions of the GPL are applicable instead of the
+- * above. If you wish to allow the use of your version of this file
+- * only under the terms of the GPL and not to allow others to use your
+- * version of this file under the RHEPL, indicate your decision by
+- * deleting the provisions above and replace them with the notice and
+- * other provisions required by the GPL. If you do not delete the
+- * provisions above, a recipient may use your version of this file
+- * under either the RHEPL or the GPL.
+- *
+- * $Id$
++ * $Id$
+ *
+ */
+
+@@ -39,7 +15,6 @@
+ #include <linux/kernel.h>
+ #include <linux/slab.h>
+ #include <linux/fs.h>
+-#include <linux/jffs2.h>
+ #include "nodelist.h"
+
+ int jffs2_readlink(struct dentry *dentry, char *buffer, int buflen);
+@@ -47,45 +22,17 @@
+
+ struct inode_operations jffs2_symlink_inode_operations =
+ {
+- readlink: jffs2_readlink,
+- follow_link: jffs2_follow_link,
+- setattr: jffs2_setattr
++ .readlink = jffs2_readlink,
++ .follow_link = jffs2_follow_link,
++ .setattr = jffs2_setattr
+ };
+
+-static char *jffs2_getlink(struct dentry *dentry)
+-{
+- struct jffs2_inode_info *f = JFFS2_INODE_INFO(dentry->d_inode);
+- char *buf;
+- int ret;
+-
+- down(&f->sem);
+- if (!f->metadata) {
+- up(&f->sem);
+- printk(KERN_NOTICE "No metadata for symlink inode #%lu\n", dentry->d_inode->i_ino);
+- return ERR_PTR(-EINVAL);
+- }
+- buf = kmalloc(f->metadata->size+1, GFP_USER);
+- if (!buf) {
+- up(&f->sem);
+- return ERR_PTR(-ENOMEM);
+- }
+- buf[f->metadata->size]=0;
+-
+- ret = jffs2_read_dnode(JFFS2_SB_INFO(dentry->d_inode->i_sb), f->metadata, buf, 0, f->metadata->size);
+- up(&f->sem);
+- if (ret) {
+- kfree(buf);
+- return ERR_PTR(ret);
+- }
+- return buf;
+-
+-}
+ int jffs2_readlink(struct dentry *dentry, char *buffer, int buflen)
+ {
+ unsigned char *kbuf;
+ int ret;
+
+- kbuf = jffs2_getlink(dentry);
++ kbuf = jffs2_getlink(JFFS2_SB_INFO(dentry->d_inode->i_sb), JFFS2_INODE_INFO(dentry->d_inode));
+ if (IS_ERR(kbuf))
+ return PTR_ERR(kbuf);
+
+@@ -99,7 +46,7 @@
+ unsigned char *buf;
+ int ret;
+
+- buf = jffs2_getlink(dentry);
++ buf = jffs2_getlink(JFFS2_SB_INFO(dentry->d_inode->i_sb), JFFS2_INODE_INFO(dentry->d_inode));
+
+ if (IS_ERR(buf))
+ return PTR_ERR(buf);
+diff -Nurb linux-mips-2.4.24-pre2/fs/jffs2/wbuf.c linux/fs/jffs2/wbuf.c
+--- linux-mips-2.4.24-pre2/fs/jffs2/wbuf.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/fs/jffs2/wbuf.c 2004-11-17 18:17:59.409257680 +0100
+@@ -0,0 +1,1156 @@
++/*
++ * JFFS2 -- Journalling Flash File System, Version 2.
++ *
++ * Copyright (C) 2001-2003 Red Hat, Inc.
++ *
++ * Created by David Woodhouse <dwmw2@redhat.com>
++ *
++ * For licensing information, see the file 'LICENCE' in this directory.
++ *
++ * $Id$
++ *
++ */
++
++#include <linux/kernel.h>
++#include <linux/slab.h>
++#include <linux/mtd/mtd.h>
++#include <linux/crc32.h>
++#include <linux/mtd/nand.h>
++#include "nodelist.h"
++
++/* For testing write failures */
++#undef BREAKME
++#undef BREAKMEHEADER
++
++#ifdef BREAKME
++static unsigned char *brokenbuf;
++#endif
++
++/* max. erase failures before we mark a block bad */
++#define MAX_ERASE_FAILURES 5
++
++/* two seconds timeout for timed wbuf-flushing */
++#define WBUF_FLUSH_TIMEOUT 2 * HZ
++
++struct jffs2_inodirty {
++ uint32_t ino;
++ struct jffs2_inodirty *next;
++};
++
++static struct jffs2_inodirty inodirty_nomem;
++
++static int jffs2_wbuf_pending_for_ino(struct jffs2_sb_info *c, uint32_t ino)
++{
++ struct jffs2_inodirty *this = c->wbuf_inodes;
++
++ /* If a malloc failed, consider _everything_ dirty */
++ if (this == &inodirty_nomem)
++ return 1;
++
++ /* If ino == 0, _any_ non-GC writes mean 'yes' */
++ if (this && !ino)
++ return 1;
++
++ /* Look to see if the inode in question is pending in the wbuf */
++ while (this) {
++ if (this->ino == ino)
++ return 1;
++ this = this->next;
++ }
++ return 0;
++}
++
++static void jffs2_clear_wbuf_ino_list(struct jffs2_sb_info *c)
++{
++ struct jffs2_inodirty *this;
++
++ this = c->wbuf_inodes;
++
++ if (this != &inodirty_nomem) {
++ while (this) {
++ struct jffs2_inodirty *next = this->next;
++ kfree(this);
++ this = next;
++ }
++ }
++ c->wbuf_inodes = NULL;
++}
++
++static void jffs2_wbuf_dirties_inode(struct jffs2_sb_info *c, uint32_t ino)
++{
++ struct jffs2_inodirty *new;
++
++ /* Mark the superblock dirty so that kupdated will flush... */
++ OFNI_BS_2SFFJ(c)->s_dirt = 1;
++
++ if (jffs2_wbuf_pending_for_ino(c, ino))
++ return;
++
++ new = kmalloc(sizeof(*new), GFP_KERNEL);
++ if (!new) {
++ D1(printk(KERN_DEBUG "No memory to allocate inodirty. Fallback to all considered dirty\n"));
++ jffs2_clear_wbuf_ino_list(c);
++ c->wbuf_inodes = &inodirty_nomem;
++ return;
++ }
++ new->ino = ino;
++ new->next = c->wbuf_inodes;
++ c->wbuf_inodes = new;
++ return;
++}
++
++static inline void jffs2_refile_wbuf_blocks(struct jffs2_sb_info *c)
++{
++ struct list_head *this, *next;
++ static int n;
++
++ if (list_empty(&c->erasable_pending_wbuf_list))
++ return;
++
++ list_for_each_safe(this, next, &c->erasable_pending_wbuf_list) {
++ struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
++
++ D1(printk(KERN_DEBUG "Removing eraseblock at 0x%08x from erasable_pending_wbuf_list...\n", jeb->offset));
++ list_del(this);
++ if ((jiffies + (n++)) & 127) {
++ /* Most of the time, we just erase it immediately. Otherwise we
++ spend ages scanning it on mount, etc. */
++ D1(printk(KERN_DEBUG "...and adding to erase_pending_list\n"));
++ list_add_tail(&jeb->list, &c->erase_pending_list);
++ c->nr_erasing_blocks++;
++ jffs2_erase_pending_trigger(c);
++ } else {
++ /* Sometimes, however, we leave it elsewhere so it doesn't get
++ immediately reused, and we spread the load a bit. */
++ D1(printk(KERN_DEBUG "...and adding to erasable_list\n"));
++ list_add_tail(&jeb->list, &c->erasable_list);
++ }
++ }
++}
++
++/* Recover from failure to write wbuf. Recover the nodes up to the
++ * wbuf, not the one which we were starting to try to write. */
++
++static void jffs2_wbuf_recover(struct jffs2_sb_info *c)
++{
++ struct jffs2_eraseblock *jeb, *new_jeb;
++ struct jffs2_raw_node_ref **first_raw, **raw;
++ size_t retlen;
++ int ret;
++ unsigned char *buf;
++ uint32_t start, end, ofs, len;
++
++ spin_lock(&c->erase_completion_lock);
++
++ jeb = &c->blocks[c->wbuf_ofs / c->sector_size];
++
++ D1(printk("About to refile bad block at %08x\n", jeb->offset));
++
++ D2(jffs2_dump_block_lists(c));
++ /* File the existing block on the bad_used_list.... */
++ if (c->nextblock == jeb)
++ c->nextblock = NULL;
++ else /* Not sure this should ever happen... need more coffee */
++ list_del(&jeb->list);
++ if (jeb->first_node) {
++ D1(printk("Refiling block at %08x to bad_used_list\n", jeb->offset));
++ list_add(&jeb->list, &c->bad_used_list);
++ } else {
++ BUG();
++ /* It has to have had some nodes or we couldn't be here */
++ D1(printk("Refiling block at %08x to erase_pending_list\n", jeb->offset));
++ list_add(&jeb->list, &c->erase_pending_list);
++ c->nr_erasing_blocks++;
++ jffs2_erase_pending_trigger(c);
++ }
++ D2(jffs2_dump_block_lists(c));
++
++ /* Adjust its size counts accordingly */
++ c->wasted_size += jeb->free_size;
++ c->free_size -= jeb->free_size;
++ jeb->wasted_size += jeb->free_size;
++ jeb->free_size = 0;
++
++ ACCT_SANITY_CHECK(c,jeb);
++ D1(ACCT_PARANOIA_CHECK(jeb));
++
++ /* Find the first node to be recovered, by skipping over every
++ node which ends before the wbuf starts, or which is obsolete. */
++ first_raw = &jeb->first_node;
++ while (*first_raw &&
++ (ref_obsolete(*first_raw) ||
++ (ref_offset(*first_raw)+ref_totlen(c, jeb, *first_raw)) < c->wbuf_ofs)) {
++ D1(printk(KERN_DEBUG "Skipping node at 0x%08x(%d)-0x%08x which is either before 0x%08x or obsolete\n",
++ ref_offset(*first_raw), ref_flags(*first_raw),
++ (ref_offset(*first_raw) + ref_totlen(c, jeb, *first_raw)),
++ c->wbuf_ofs));
++ first_raw = &(*first_raw)->next_phys;
++ }
++
++ if (!*first_raw) {
++ /* All nodes were obsolete. Nothing to recover. */
++ D1(printk(KERN_DEBUG "No non-obsolete nodes to be recovered. Just filing block bad\n"));
++ spin_unlock(&c->erase_completion_lock);
++ return;
++ }
++
++ start = ref_offset(*first_raw);
++ end = ref_offset(*first_raw) + ref_totlen(c, jeb, *first_raw);
++
++ /* Find the last node to be recovered */
++ raw = first_raw;
++ while ((*raw)) {
++ if (!ref_obsolete(*raw))
++ end = ref_offset(*raw) + ref_totlen(c, jeb, *raw);
++
++ raw = &(*raw)->next_phys;
++ }
++ spin_unlock(&c->erase_completion_lock);
++
++ D1(printk(KERN_DEBUG "wbuf recover %08x-%08x\n", start, end));
++
++ buf = NULL;
++ if (start < c->wbuf_ofs) {
++ /* First affected node was already partially written.
++ * Attempt to reread the old data into our buffer. */
++
++ buf = kmalloc(end - start, GFP_KERNEL);
++ if (!buf) {
++ printk(KERN_CRIT "Malloc failure in wbuf recovery. Data loss ensues.\n");
++
++ goto read_failed;
++ }
++
++ /* Do the read... */
++ ret = c->mtd->read_ecc(c->mtd, start, c->wbuf_ofs - start, &retlen, buf, NULL, c->oobinfo);
++ if (ret == -EIO && retlen == c->wbuf_ofs - start) {
++ /* ECC recovered */
++ ret = 0;
++ }
++ if (ret || retlen != c->wbuf_ofs - start) {
++ printk(KERN_CRIT "Old data are already lost in wbuf recovery. Data loss ensues.\n");
++
++ kfree(buf);
++ buf = NULL;
++ read_failed:
++ first_raw = &(*first_raw)->next_phys;
++ /* If this was the only node to be recovered, give up */
++ if (!(*first_raw))
++ return;
++
++ /* It wasn't. Go on and try to recover nodes complete in the wbuf */
++ start = ref_offset(*first_raw);
++ } else {
++ /* Read succeeded. Copy the remaining data from the wbuf */
++ memcpy(buf + (c->wbuf_ofs - start), c->wbuf, end - c->wbuf_ofs);
++ }
++ }
++ /* OK... we're to rewrite (end-start) bytes of data from first_raw onwards.
++ Either 'buf' contains the data, or we find it in the wbuf */
++
++
++ /* ... and get an allocation of space from a shiny new block instead */
++ ret = jffs2_reserve_space_gc(c, end-start, &ofs, &len);
++ if (ret) {
++ printk(KERN_WARNING "Failed to allocate space for wbuf recovery. Data loss ensues.\n");
++ if (buf)
++ kfree(buf);
++ return;
++ }
++ if (end-start >= c->wbuf_pagesize) {
++ /* Need to do another write immediately. This, btw,
++ means that we'll be writing from 'buf' and not from
++ the wbuf. Since if we're writing from the wbuf there
++ won't be more than a wbuf full of data, now will
++ there? :) */
++
++ uint32_t towrite = (end-start) - ((end-start)%c->wbuf_pagesize);
++
++ D1(printk(KERN_DEBUG "Write 0x%x bytes at 0x%08x in wbuf recover\n",
++ towrite, ofs));
++
++#ifdef BREAKMEHEADER
++ static int breakme;
++ if (breakme++ == 20) {
++ printk(KERN_NOTICE "Faking write error at 0x%08x\n", ofs);
++ breakme = 0;
++ c->mtd->write_ecc(c->mtd, ofs, towrite, &retlen,
++ brokenbuf, NULL, c->oobinfo);
++ ret = -EIO;
++ } else
++#endif
++ ret = c->mtd->write_ecc(c->mtd, ofs, towrite, &retlen,
++ buf, NULL, c->oobinfo);
++
++ if (ret || retlen != towrite) {
++ /* Argh. We tried. Really we did. */
++ printk(KERN_CRIT "Recovery of wbuf failed due to a second write error\n");
++ kfree(buf);
++
++ if (retlen) {
++ struct jffs2_raw_node_ref *raw2;
++
++ raw2 = jffs2_alloc_raw_node_ref();
++ if (!raw2)
++ return;
++
++ raw2->flash_offset = ofs | REF_OBSOLETE;
++ raw2->__totlen = ref_totlen(c, jeb, *first_raw);
++ raw2->next_phys = NULL;
++ raw2->next_in_ino = NULL;
++
++ jffs2_add_physical_node_ref(c, raw2);
++ }
++ return;
++ }
++ printk(KERN_NOTICE "Recovery of wbuf succeeded to %08x\n", ofs);
++
++ c->wbuf_len = (end - start) - towrite;
++ c->wbuf_ofs = ofs + towrite;
++ memcpy(c->wbuf, buf + towrite, c->wbuf_len);
++ /* Don't muck about with c->wbuf_inodes. False positives are harmless. */
++
++ kfree(buf);
++ } else {
++ /* OK, now we're left with the dregs in whichever buffer we're using */
++ if (buf) {
++ memcpy(c->wbuf, buf, end-start);
++ kfree(buf);
++ } else {
++ memmove(c->wbuf, c->wbuf + (start - c->wbuf_ofs), end - start);
++ }
++ c->wbuf_ofs = ofs;
++ c->wbuf_len = end - start;
++ }
++
++ /* Now sort out the jffs2_raw_node_refs, moving them from the old to the next block */
++ new_jeb = &c->blocks[ofs / c->sector_size];
++
++ spin_lock(&c->erase_completion_lock);
++ if (new_jeb->first_node) {
++ /* Odd, but possible with ST flash later maybe */
++ new_jeb->last_node->next_phys = *first_raw;
++ } else {
++ new_jeb->first_node = *first_raw;
++ }
++
++ raw = first_raw;
++ while (*raw) {
++ uint32_t rawlen = ref_totlen(c, jeb, *raw);
++
++ D1(printk(KERN_DEBUG "Refiling block of %08x at %08x(%d) to %08x\n",
++ rawlen, ref_offset(*raw), ref_flags(*raw), ofs));
++
++ if (ref_obsolete(*raw)) {
++ /* Shouldn't really happen much */
++ new_jeb->dirty_size += rawlen;
++ new_jeb->free_size -= rawlen;
++ c->dirty_size += rawlen;
++ } else {
++ new_jeb->used_size += rawlen;
++ new_jeb->free_size -= rawlen;
++ jeb->dirty_size += rawlen;
++ jeb->used_size -= rawlen;
++ c->dirty_size += rawlen;
++ }
++ c->free_size -= rawlen;
++ (*raw)->flash_offset = ofs | ref_flags(*raw);
++ ofs += rawlen;
++ new_jeb->last_node = *raw;
++
++ raw = &(*raw)->next_phys;
++ }
++
++ /* Fix up the original jeb now it's on the bad_list */
++ *first_raw = NULL;
++ if (first_raw == &jeb->first_node) {
++ jeb->last_node = NULL;
++ D1(printk(KERN_DEBUG "Failing block at %08x is now empty. Moving to erase_pending_list\n", jeb->offset));
++ list_del(&jeb->list);
++ list_add(&jeb->list, &c->erase_pending_list);
++ c->nr_erasing_blocks++;
++ jffs2_erase_pending_trigger(c);
++ }
++ else
++ jeb->last_node = container_of(first_raw, struct jffs2_raw_node_ref, next_phys);
++
++ ACCT_SANITY_CHECK(c,jeb);
++ D1(ACCT_PARANOIA_CHECK(jeb));
++
++ ACCT_SANITY_CHECK(c,new_jeb);
++ D1(ACCT_PARANOIA_CHECK(new_jeb));
++
++ spin_unlock(&c->erase_completion_lock);
++
++ D1(printk(KERN_DEBUG "wbuf recovery completed OK\n"));
++}
++
++/* Meaning of pad argument:
++ 0: Do not pad. Probably pointless - we only ever use this when we can't pad anyway.
++ 1: Pad, do not adjust nextblock free_size
++ 2: Pad, adjust nextblock free_size
++*/
++static int __jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad)
++{
++ int ret;
++ size_t retlen;
++
++ /* Nothing to do if not NAND flash. In particular, we shouldn't
++ del_timer() the timer we never initialised. */
++ if (jffs2_can_mark_obsolete(c))
++ return 0;
++
++ if (!down_trylock(&c->alloc_sem)) {
++ up(&c->alloc_sem);
++ printk(KERN_CRIT "jffs2_flush_wbuf() called with alloc_sem not locked!\n");
++ BUG();
++ }
++
++ if(!c->wbuf || !c->wbuf_len)
++ return 0;
++
++ /* claim remaining space on the page
++ this happens, if we have a change to a new block,
++ or if fsync forces us to flush the writebuffer.
++ if we have a switch to next page, we will not have
++ enough remaining space for this.
++ */
++ if (pad) {
++ c->wbuf_len = PAD(c->wbuf_len);
++
++ if ( c->wbuf_len + sizeof(struct jffs2_unknown_node) < c->wbuf_pagesize) {
++ struct jffs2_unknown_node *padnode = (void *)(c->wbuf + c->wbuf_len);
++ padnode->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
++ padnode->nodetype = cpu_to_je16(JFFS2_NODETYPE_PADDING);
++ padnode->totlen = cpu_to_je32(c->wbuf_pagesize - c->wbuf_len);
++ padnode->hdr_crc = cpu_to_je32(crc32(0, padnode, sizeof(*padnode)-4));
++ }
++ }
++ /* else jffs2_flash_writev has actually filled in the rest of the
++ buffer for us, and will deal with the node refs etc. later. */
++
++#ifdef BREAKME
++ static int breakme;
++ if (breakme++ == 20) {
++ printk(KERN_NOTICE "Faking write error at 0x%08x\n", c->wbuf_ofs);
++ breakme = 0;
++ c->mtd->write_ecc(c->mtd, c->wbuf_ofs, c->wbuf_pagesize,
++ &retlen, brokenbuf, NULL, c->oobinfo);
++ ret = -EIO;
++ } else
++#endif
++ ret = c->mtd->write_ecc(c->mtd, c->wbuf_ofs, c->wbuf_pagesize, &retlen, c->wbuf, NULL, c->oobinfo);
++
++
++ if (ret || retlen != c->wbuf_pagesize) {
++ if (ret)
++ printk(KERN_WARNING "jffs2_flush_wbuf(): Write failed with %d\n",ret);
++ else {
++ printk(KERN_WARNING "jffs2_flush_wbuf(): Write was short: %zd instead of %d\n",
++ retlen, c->wbuf_pagesize);
++ ret = -EIO;
++ }
++
++ jffs2_wbuf_recover(c);
++
++ return ret;
++ }
++
++ /* Adjusting free size of next block only, if it's called from fsync ! */
++ if (pad == 2) {
++ D1(printk(KERN_DEBUG "jffs2_flush_wbuf() adjusting free_size of c->nextblock\n"));
++ spin_lock(&c->erase_completion_lock);
++ if (!c->nextblock)
++ BUG();
++ /* wbuf_pagesize - wbuf_len is the amount of space that's to be
++ padded. If there is less free space in the block than that,
++ something screwed up */
++ if (c->nextblock->free_size < (c->wbuf_pagesize - c->wbuf_len)) {
++ printk(KERN_CRIT "jffs2_flush_wbuf(): Accounting error. wbuf at 0x%08x has 0x%03x bytes, 0x%03x left.\n",
++ c->wbuf_ofs, c->wbuf_len, c->wbuf_pagesize-c->wbuf_len);
++ printk(KERN_CRIT "jffs2_flush_wbuf(): But free_size for block at 0x%08x is only 0x%08x\n",
++ c->nextblock->offset, c->nextblock->free_size);
++ BUG();
++ }
++ c->nextblock->free_size -= (c->wbuf_pagesize - c->wbuf_len);
++ c->free_size -= (c->wbuf_pagesize - c->wbuf_len);
++ c->nextblock->wasted_size += (c->wbuf_pagesize - c->wbuf_len);
++ c->wasted_size += (c->wbuf_pagesize - c->wbuf_len);
++ spin_unlock(&c->erase_completion_lock);
++ }
++
++ /* Stick any now-obsoleted blocks on the erase_pending_list */
++ spin_lock(&c->erase_completion_lock);
++ jffs2_refile_wbuf_blocks(c);
++ jffs2_clear_wbuf_ino_list(c);
++ spin_unlock(&c->erase_completion_lock);
++
++ memset(c->wbuf,0xff,c->wbuf_pagesize);
++ /* adjust write buffer offset, else we get a non contiguous write bug */
++ c->wbuf_ofs += c->wbuf_pagesize;
++ c->wbuf_len = 0;
++ return 0;
++}
++
++/* Trigger garbage collection to flush the write-buffer.
++ If ino arg is zero, do it if _any_ real (i.e. not GC) writes are
++ outstanding. If ino arg non-zero, do it only if a write for the
++ given inode is outstanding. */
++int jffs2_flush_wbuf_gc(struct jffs2_sb_info *c, uint32_t ino)
++{
++ uint32_t old_wbuf_ofs;
++ uint32_t old_wbuf_len;
++ int ret = 0;
++
++ D1(printk(KERN_DEBUG "jffs2_flush_wbuf_gc() called for ino #%u...\n", ino));
++
++ down(&c->alloc_sem);
++ if (!jffs2_wbuf_pending_for_ino(c, ino)) {
++ D1(printk(KERN_DEBUG "Ino #%d not pending in wbuf. Returning\n", ino));
++ up(&c->alloc_sem);
++ return 0;
++ }
++
++ old_wbuf_ofs = c->wbuf_ofs;
++ old_wbuf_len = c->wbuf_len;
++
++ if (c->unchecked_size) {
++ /* GC won't make any progress for a while */
++ D1(printk(KERN_DEBUG "jffs2_flush_wbuf_gc() padding. Not finished checking\n"));
++ ret = __jffs2_flush_wbuf(c, 2);
++ } else while (old_wbuf_len &&
++ old_wbuf_ofs == c->wbuf_ofs) {
++
++ up(&c->alloc_sem);
++
++ D1(printk(KERN_DEBUG "jffs2_flush_wbuf_gc() calls gc pass\n"));
++
++ ret = jffs2_garbage_collect_pass(c);
++ if (ret) {
++ /* GC failed. Flush it with padding instead */
++ down(&c->alloc_sem);
++ ret = __jffs2_flush_wbuf(c, 2);
++ break;
++ }
++ down(&c->alloc_sem);
++ }
++
++ D1(printk(KERN_DEBUG "jffs2_flush_wbuf_gc() ends...\n"));
++
++ up(&c->alloc_sem);
++ return ret;
++}
++
++/* Pad write-buffer to end and write it, wasting space. */
++int jffs2_flush_wbuf_pad(struct jffs2_sb_info *c)
++{
++ return __jffs2_flush_wbuf(c, 1);
++}
++
++
++#define PAGE_DIV(x) ( (x) & (~(c->wbuf_pagesize - 1)) )
++#define PAGE_MOD(x) ( (x) & (c->wbuf_pagesize - 1) )
++int jffs2_flash_writev(struct jffs2_sb_info *c, const struct iovec *invecs, unsigned long count, loff_t to, size_t *retlen, uint32_t ino)
++{
++ struct iovec outvecs[3];
++ uint32_t totlen = 0;
++ uint32_t split_ofs = 0;
++ uint32_t old_totlen;
++ int ret, splitvec = -1;
++ int invec, outvec;
++ size_t wbuf_retlen;
++ unsigned char *wbuf_ptr;
++ size_t donelen = 0;
++ uint32_t outvec_to = to;
++
++ /* If not NAND flash, don't bother */
++ if (!c->wbuf)
++ return jffs2_flash_direct_writev(c, invecs, count, to, retlen);
++
++ /* If wbuf_ofs is not initialized, set it to target address */
++ if (c->wbuf_ofs == 0xFFFFFFFF) {
++ c->wbuf_ofs = PAGE_DIV(to);
++ c->wbuf_len = PAGE_MOD(to);
++ memset(c->wbuf,0xff,c->wbuf_pagesize);
++ }
++
++ /* Sanity checks on target address.
++ It's permitted to write at PAD(c->wbuf_len+c->wbuf_ofs),
++ and it's permitted to write at the beginning of a new
++ erase block. Anything else, and you die.
++ New block starts at xxx000c (0-b = block header)
++ */
++ if ( (to & ~(c->sector_size-1)) != (c->wbuf_ofs & ~(c->sector_size-1)) ) {
++ /* It's a write to a new block */
++ if (c->wbuf_len) {
++ D1(printk(KERN_DEBUG "jffs2_flash_writev() to 0x%lx causes flush of wbuf at 0x%08x\n", (unsigned long)to, c->wbuf_ofs));
++ ret = jffs2_flush_wbuf_pad(c);
++ if (ret) {
++ /* the underlying layer has to check wbuf_len to do the cleanup */
++ D1(printk(KERN_WARNING "jffs2_flush_wbuf() called from jffs2_flash_writev() failed %d\n", ret));
++ *retlen = 0;
++ return ret;
++ }
++ }
++ /* set pointer to new block */
++ c->wbuf_ofs = PAGE_DIV(to);
++ c->wbuf_len = PAGE_MOD(to);
++ }
++
++ if (to != PAD(c->wbuf_ofs + c->wbuf_len)) {
++ /* We're not writing immediately after the writebuffer. Bad. */
++ printk(KERN_CRIT "jffs2_flash_writev(): Non-contiguous write to %08lx\n", (unsigned long)to);
++ if (c->wbuf_len)
++ printk(KERN_CRIT "wbuf was previously %08x-%08x\n",
++ c->wbuf_ofs, c->wbuf_ofs+c->wbuf_len);
++ BUG();
++ }
++
++ /* Note outvecs[3] above. We know count is never greater than 2 */
++ if (count > 2) {
++ printk(KERN_CRIT "jffs2_flash_writev(): count is %ld\n", count);
++ BUG();
++ }
++
++ invec = 0;
++ outvec = 0;
++
++
++ /* Fill writebuffer first, if already in use */
++ if (c->wbuf_len) {
++ uint32_t invec_ofs = 0;
++
++ /* adjust alignment offset */
++ if (c->wbuf_len != PAGE_MOD(to)) {
++ c->wbuf_len = PAGE_MOD(to);
++ /* take care of alignment to next page */
++ if (!c->wbuf_len)
++ c->wbuf_len = c->wbuf_pagesize;
++ }
++
++ while(c->wbuf_len < c->wbuf_pagesize) {
++ uint32_t thislen;
++
++ if (invec == count)
++ goto alldone;
++
++ thislen = c->wbuf_pagesize - c->wbuf_len;
++
++ if (thislen >= invecs[invec].iov_len)
++ thislen = invecs[invec].iov_len;
++
++ invec_ofs = thislen;
++
++ memcpy(c->wbuf + c->wbuf_len, invecs[invec].iov_base, thislen);
++ c->wbuf_len += thislen;
++ donelen += thislen;
++ /* Get next invec, if actual did not fill the buffer */
++ if (c->wbuf_len < c->wbuf_pagesize)
++ invec++;
++ }
++
++ /* write buffer is full, flush buffer */
++ ret = __jffs2_flush_wbuf(c, 0);
++ if (ret) {
++ /* the underlying layer has to check wbuf_len to do the cleanup */
++ D1(printk(KERN_WARNING "jffs2_flush_wbuf() called from jffs2_flash_writev() failed %d\n", ret));
++ /* Retlen zero to make sure our caller doesn't mark the space dirty.
++ We've already done everything that's necessary */
++ *retlen = 0;
++ return ret;
++ }
++ outvec_to += donelen;
++ c->wbuf_ofs = outvec_to;
++
++ /* All invecs done ? */
++ if (invec == count)
++ goto alldone;
++
++ /* Set up the first outvec, containing the remainder of the
++ invec we partially used */
++ if (invecs[invec].iov_len > invec_ofs) {
++ outvecs[0].iov_base = invecs[invec].iov_base+invec_ofs;
++ totlen = outvecs[0].iov_len = invecs[invec].iov_len-invec_ofs;
++ if (totlen > c->wbuf_pagesize) {
++ splitvec = outvec;
++ split_ofs = outvecs[0].iov_len - PAGE_MOD(totlen);
++ }
++ outvec++;
++ }
++ invec++;
++ }
++
++ /* OK, now we've flushed the wbuf and the start of the bits
++ we have been asked to write, now to write the rest.... */
++
++ /* totlen holds the amount of data still to be written */
++ old_totlen = totlen;
++ for ( ; invec < count; invec++,outvec++ ) {
++ outvecs[outvec].iov_base = invecs[invec].iov_base;
++ totlen += outvecs[outvec].iov_len = invecs[invec].iov_len;
++ if (PAGE_DIV(totlen) != PAGE_DIV(old_totlen)) {
++ splitvec = outvec;
++ split_ofs = outvecs[outvec].iov_len - PAGE_MOD(totlen);
++ old_totlen = totlen;
++ }
++ }
++
++ /* Now the outvecs array holds all the remaining data to write */
++ /* Up to splitvec,split_ofs is to be written immediately. The rest
++ goes into the (now-empty) wbuf */
++
++ if (splitvec != -1) {
++ uint32_t remainder;
++ int ret;
++
++ remainder = outvecs[splitvec].iov_len - split_ofs;
++ outvecs[splitvec].iov_len = split_ofs;
++
++ /* We did cross a page boundary, so we write some now */
++ ret = c->mtd->writev_ecc(c->mtd, outvecs, splitvec+1, outvec_to, &wbuf_retlen, NULL, c->oobinfo);
++ if (ret < 0 || wbuf_retlen != PAGE_DIV(totlen)) {
++ /* At this point we have no problem,
++ c->wbuf is empty.
++ */
++ *retlen = donelen;
++ return ret;
++ }
++
++ donelen += wbuf_retlen;
++ c->wbuf_ofs = PAGE_DIV(outvec_to) + PAGE_DIV(totlen);
++
++ if (remainder) {
++ outvecs[splitvec].iov_base += split_ofs;
++ outvecs[splitvec].iov_len = remainder;
++ } else {
++ splitvec++;
++ }
++
++ } else {
++ splitvec = 0;
++ }
++
++ /* Now splitvec points to the start of the bits we have to copy
++ into the wbuf */
++ wbuf_ptr = c->wbuf;
++
++ for ( ; splitvec < outvec; splitvec++) {
++ /* Don't copy the wbuf into itself */
++ if (outvecs[splitvec].iov_base == c->wbuf)
++ continue;
++ memcpy(wbuf_ptr, outvecs[splitvec].iov_base, outvecs[splitvec].iov_len);
++ wbuf_ptr += outvecs[splitvec].iov_len;
++ donelen += outvecs[splitvec].iov_len;
++ }
++ c->wbuf_len = wbuf_ptr - c->wbuf;
++
++ /* If there's a remainder in the wbuf and it's a non-GC write,
++ remember that the wbuf affects this ino */
++alldone:
++ *retlen = donelen;
++
++ if (c->wbuf_len && ino)
++ jffs2_wbuf_dirties_inode(c, ino);
++
++ return 0;
++}
++
++/*
++ * This is the entry for flash write.
++ * Check, if we work on NAND FLASH, if so build an iovec and write it via vritev
++*/
++int jffs2_flash_write(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *retlen, const u_char *buf)
++{
++ struct iovec vecs[1];
++
++ if (jffs2_can_mark_obsolete(c))
++ return c->mtd->write(c->mtd, ofs, len, retlen, buf);
++
++ vecs[0].iov_base = (unsigned char *) buf;
++ vecs[0].iov_len = len;
++ return jffs2_flash_writev(c, vecs, 1, ofs, retlen, 0);
++}
++
++/*
++ Handle readback from writebuffer and ECC failure return
++*/
++int jffs2_flash_read(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *retlen, u_char *buf)
++{
++ loff_t orbf = 0, owbf = 0, lwbf = 0;
++ int ret;
++
++ /* Read flash */
++ if (!jffs2_can_mark_obsolete(c)) {
++ ret = c->mtd->read_ecc(c->mtd, ofs, len, retlen, buf, NULL, c->oobinfo);
++
++ if ( (ret == -EIO) && (*retlen == len) ) {
++ printk(KERN_WARNING "mtd->read(0x%zx bytes from 0x%llx) returned ECC error\n",
++ len, ofs);
++ /*
++ * We have the raw data without ECC correction in the buffer, maybe
++ * we are lucky and all data or parts are correct. We check the node.
++ * If data are corrupted node check will sort it out.
++ * We keep this block, it will fail on write or erase and the we
++ * mark it bad. Or should we do that now? But we should give him a chance.
++ * Maybe we had a system crash or power loss before the ecc write or
++ * a erase was completed.
++ * So we return success. :)
++ */
++ ret = 0;
++ }
++ } else
++ return c->mtd->read(c->mtd, ofs, len, retlen, buf);
++
++ /* if no writebuffer available or write buffer empty, return */
++ if (!c->wbuf_pagesize || !c->wbuf_len)
++ return ret;
++
++ /* if we read in a different block, return */
++ if ( (ofs & ~(c->sector_size-1)) != (c->wbuf_ofs & ~(c->sector_size-1)) )
++ return ret;
++
++ if (ofs >= c->wbuf_ofs) {
++ owbf = (ofs - c->wbuf_ofs); /* offset in write buffer */
++ if (owbf > c->wbuf_len) /* is read beyond write buffer ? */
++ return ret;
++ lwbf = c->wbuf_len - owbf; /* number of bytes to copy */
++ if (lwbf > len)
++ lwbf = len;
++ } else {
++ orbf = (c->wbuf_ofs - ofs); /* offset in read buffer */
++ if (orbf > len) /* is write beyond write buffer ? */
++ return ret;
++ lwbf = len - orbf; /* number of bytes to copy */
++ if (lwbf > c->wbuf_len)
++ lwbf = c->wbuf_len;
++ }
++ if (lwbf > 0)
++ memcpy(buf+orbf,c->wbuf+owbf,lwbf);
++
++ return ret;
++}
++
++/*
++ * Check, if the out of band area is empty
++ */
++int jffs2_check_oob_empty( struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, int mode)
++{
++ unsigned char *buf;
++ int ret = 0;
++ int i,len,page;
++ size_t retlen;
++ int oob_size;
++
++ oob_size = c->mtd->oobsize;
++
++ /* allocate a buffer for all oob data in this sector */
++ len = 4 * oob_size;
++ buf = kmalloc(len, GFP_KERNEL);
++ if (!buf) {
++ printk(KERN_NOTICE "jffs2_check_oob_empty(): allocation of temporary data buffer for oob check failed\n");
++ return -ENOMEM;
++ }
++ /*
++ * if mode = 0, we scan for a total empty oob area, else we have
++ * to take care of the cleanmarker in the first page of the block
++ */
++ ret = jffs2_flash_read_oob(c, jeb->offset, len , &retlen, buf);
++ if (ret) {
++ D1(printk(KERN_WARNING "jffs2_check_oob_empty(): Read OOB failed %d for block at %08x\n", ret, jeb->offset));
++ goto out;
++ }
++
++ if (retlen < len) {
++ D1(printk(KERN_WARNING "jffs2_check_oob_empty(): Read OOB return short read "
++ "(%zd bytes not %d) for block at %08x\n", retlen, len, jeb->offset));
++ ret = -EIO;
++ goto out;
++ }
++
++ /* Special check for first two pages */
++ for (page = 0; page < 2 * oob_size; page += oob_size) {
++ /* Check for bad block marker */
++ if (buf[page+c->badblock_pos] != 0xff) {
++ D1(printk(KERN_WARNING "jffs2_check_oob_empty(): Bad or failed block at %08x\n",jeb->offset));
++ /* Return 2 for bad and 3 for failed block
++ bad goes to list_bad and failed to list_erase */
++ ret = (!page) ? 2 : 3;
++ goto out;
++ }
++ for(i = 0; i < oob_size ; i++) {
++ /* Yeah, we know about the cleanmarker. */
++ if (mode && i >= c->fsdata_pos &&
++ i < c->fsdata_pos+c->fsdata_len)
++ continue;
++
++ if (buf[page+i] != 0xFF) {
++ D2(printk(KERN_DEBUG "Found %02x at %x in OOB for %08x\n",
++ buf[page+i], page+i, jeb->offset));
++ ret = 1;
++ goto out;
++ }
++ }
++ /* only the first page can contain a cleanmarker !*/
++ mode = 0;
++ }
++
++ /* we know, we are aligned :) */
++ for (; page < len; page += sizeof(long)) {
++ unsigned long dat = *(unsigned long *)(&buf[page]);
++ if(dat != -1) {
++ ret = 1;
++ goto out;
++ }
++ }
++
++out:
++ kfree(buf);
++
++ return ret;
++}
++
++/*
++* Scan for a valid cleanmarker and for bad blocks
++* For virtual blocks (concatenated physical blocks) check the cleanmarker
++* only in the first page of the first physical block, but scan for bad blocks in all
++* physical blocks
++*/
++int jffs2_check_nand_cleanmarker (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
++{
++ struct jffs2_unknown_node n;
++ unsigned char buf[32];
++ unsigned char *p;
++ int ret, i, cnt, retval = 0;
++ size_t retlen, offset;
++ int oob_size;
++
++ offset = jeb->offset;
++ oob_size = c->mtd->oobsize;
++
++ /* Loop through the physical blocks */
++ for (cnt = 0; cnt < (c->sector_size / c->mtd->erasesize); cnt++) {
++ /*
++ * We read oob data from page 0 and 1 of the block.
++ * page 0 contains cleanmarker and badblock info
++ * page 1 contains failure count of this block
++ */
++ ret = c->mtd->read_oob (c->mtd, offset, oob_size << 1, &retlen, buf);
++
++ if (ret) {
++ D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): Read OOB failed %d for block at %08x\n", ret, jeb->offset));
++ return ret;
++ }
++ if (retlen < (oob_size << 1)) {
++ D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): Read OOB return short read (%zd bytes not %d) for block at %08x\n", retlen, oob_size << 1, jeb->offset));
++ return -EIO;
++ }
++
++ /* Check for bad block marker */
++ if (buf[c->badblock_pos] != 0xff) {
++ D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): Bad block at %08x (has %02x %02x in badblock_pos %d\n",
++ jeb->offset, buf[c->badblock_pos], buf[c->badblock_pos + oob_size], c->badblock_pos));
++ return 2;
++ }
++
++ /* Check for failure counter in the second page */
++ if (buf[c->badblock_pos + oob_size] != 0xff) {
++ D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): Block marked as failed at %08x, fail count:%d\n", jeb->offset, buf[c->badblock_pos + oob_size]));
++ return 3;
++ }
++
++ /* Check cleanmarker only on the first physical block */
++ if (!cnt) {
++ n.magic = cpu_to_je16 (JFFS2_MAGIC_BITMASK);
++ n.nodetype = cpu_to_je16 (JFFS2_NODETYPE_CLEANMARKER);
++ n.totlen = cpu_to_je32 (8);
++ p = (unsigned char *) &n;
++
++ for (i = 0; i < c->fsdata_len; i++) {
++ if (buf[c->fsdata_pos + i] != p[i]) {
++ retval = 1;
++ }
++ }
++ D1(if (retval == 1) {
++ printk(KERN_WARNING "jffs2_check_nand_cleanmarker(): Cleanmarker node not detected in block at %08x\n", jeb->offset);
++ printk(KERN_WARNING "OOB at %08x was ", offset);
++ for (i=0; i < oob_size; i++) {
++ printk("%02x ", buf[i]);
++ }
++ printk("\n");
++ })
++ }
++ offset += c->mtd->erasesize;
++ }
++ return retval;
++}
++
++int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
++{
++ struct jffs2_unknown_node n;
++ int ret;
++ size_t retlen;
++
++ n.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
++ n.nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER);
++ n.totlen = cpu_to_je32(8);
++
++ ret = jffs2_flash_write_oob(c, jeb->offset + c->fsdata_pos, c->fsdata_len, &retlen, (unsigned char *)&n);
++
++ if (ret) {
++ D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): Write failed for block at %08x: error %d\n", jeb->offset, ret));
++ return ret;
++ }
++ if (retlen != c->fsdata_len) {
++ D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): Short write for block at %08x: %zd not %d\n", jeb->offset, retlen, c->fsdata_len));
++ return ret;
++ }
++ return 0;
++}
++
++/*
++ * We try to get the failure count of this block.
++ */
++int jffs2_nand_read_failcnt(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) {
++
++ unsigned char buf[16];
++ int ret;
++ size_t retlen;
++ int oob_size;
++
++ oob_size = c->mtd->oobsize;
++
++ ret = c->mtd->read_oob(c->mtd, jeb->offset + c->mtd->oobblock, oob_size , &retlen, buf);
++
++ if (ret) {
++ D1(printk(KERN_WARNING "jffs2_nand_read_failcnt(): Read OOB failed %d for block at %08x\n", ret, jeb->offset));
++ return ret;
++ }
++
++ if (retlen < oob_size) {
++ D1(printk(KERN_WARNING "jffs2_nand_read_failcnt(): Read OOB return short read (%zd bytes not %d) for block at %08x\n", retlen, oob_size, jeb->offset));
++ return -EIO;
++ }
++
++ jeb->bad_count = buf[c->badblock_pos];
++ return 0;
++}
++
++/*
++ * On NAND we try to mark this block bad. We try to write how often
++ * the block was erased and mark it finaly bad, if the count
++ * is > MAX_ERASE_FAILURES. We read this information on mount !
++ * jeb->bad_count contains the count before this erase.
++ * Don't care about failures. This block remains on the erase-pending
++ * or badblock list as long as nobody manipulates the flash with
++ * a bootloader or something like that.
++ */
++
++int jffs2_write_nand_badblock(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
++{
++ unsigned char buf = 0x0;
++ int ret;
++ size_t retlen;
++
++ /* if the count is < max, we try to write the counter to the 2nd page oob area */
++ if( ++jeb->bad_count < MAX_ERASE_FAILURES) {
++ buf = (unsigned char)jeb->bad_count;
++ c->badblock_pos += c->mtd->oobblock;
++ }
++
++ ret = jffs2_flash_write_oob(c, jeb->offset + c->badblock_pos, 1, &retlen, &buf);
++
++ if (ret) {
++ D1(printk(KERN_WARNING "jffs2_write_nand_badblock(): Write failed for block at %08x: error %d\n", jeb->offset, ret));
++ return ret;
++ }
++ if (retlen != 1) {
++ D1(printk(KERN_WARNING "jffs2_write_nand_badblock(): Short write for block at %08x: %zd not 1\n", jeb->offset, retlen));
++ return ret;
++ }
++ return 0;
++}
++
++#define JFFS2_OOB_ECCPOS0 0
++#define JFFS2_OOB_ECCPOS1 1
++#define JFFS2_OOB_ECCPOS2 2
++#define JFFS2_OOB_ECCPOS3 3
++#define JFFS2_OOB_ECCPOS4 6
++#define JFFS2_OOB_ECCPOS5 7
++
++#define NAND_JFFS2_OOB8_FSDAPOS 6
++#define NAND_JFFS2_OOB16_FSDAPOS 8
++#define NAND_JFFS2_OOB8_FSDALEN 2
++#define NAND_JFFS2_OOB16_FSDALEN 8
++
++static struct nand_oobinfo jffs2_oobinfo_swecc = {
++ .useecc = 1,
++ .eccpos = {JFFS2_OOB_ECCPOS0, JFFS2_OOB_ECCPOS1, JFFS2_OOB_ECCPOS2,
++ JFFS2_OOB_ECCPOS3, JFFS2_OOB_ECCPOS4, JFFS2_OOB_ECCPOS5}
++};
++
++static struct nand_oobinfo jffs2_oobinfo_docecc = {
++ .useecc = 1,
++ .eccpos = {0,1,2,3,4,5}
++};
++
++
++
++int jffs2_nand_flash_setup(struct jffs2_sb_info *c)
++{
++ /* Cleanmarker is out-of-band, so inline size zero */
++ c->cleanmarker_size = 0;
++
++ /* Initialise write buffer */
++ c->wbuf_pagesize = c->mtd->oobblock;
++ c->wbuf_ofs = 0xFFFFFFFF;
++
++ /* FIXME: If we had a generic way of describing the hardware's
++ use of OOB area, we could perhaps make this generic too. */
++ switch(c->mtd->ecctype) {
++ case MTD_ECC_SW:
++ D1(printk(KERN_DEBUG "JFFS2 using software ECC\n"));
++ c->oobinfo = &jffs2_oobinfo_swecc;
++ if (c->mtd->oobsize == 8) {
++ c->fsdata_pos = NAND_JFFS2_OOB8_FSDAPOS;
++ c->fsdata_len = NAND_JFFS2_OOB8_FSDALEN;
++ } else {
++ c->fsdata_pos = NAND_JFFS2_OOB16_FSDAPOS;
++ c->fsdata_len = NAND_JFFS2_OOB16_FSDALEN;
++ }
++ c->badblock_pos = NAND_BADBLOCK_POS;
++ break;
++
++ case MTD_ECC_RS_DiskOnChip:
++ D1(printk(KERN_DEBUG "JFFS2 using DiskOnChip hardware ECC\n"));
++ c->oobinfo = &jffs2_oobinfo_docecc;
++ c->fsdata_pos = 6;
++ c->fsdata_len = NAND_JFFS2_OOB16_FSDALEN;
++ c->badblock_pos = 15;
++ break;
++
++ default:
++ printk("JFFS2 doesn't yet know how to handle ECC type %d\n",
++ c->mtd->ecctype);
++ return -EINVAL;
++ }
++
++ c->wbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL);
++ if (!c->wbuf)
++ return -ENOMEM;
++
++#ifdef BREAKME
++ if (!brokenbuf)
++ brokenbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL);
++ if (!brokenbuf) {
++ kfree(c->wbuf);
++ return -ENOMEM;
++ }
++ memset(brokenbuf, 0xdb, c->wbuf_pagesize);
++#endif
++ return 0;
++}
++
++void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c)
++{
++ kfree(c->wbuf);
++}
+diff -Nurb linux-mips-2.4.24-pre2/fs/jffs2/write.c linux/fs/jffs2/write.c
+--- linux-mips-2.4.24-pre2/fs/jffs2/write.c 2004-11-17 18:05:04.000000000 +0100
++++ linux/fs/jffs2/write.c 2004-11-17 18:17:59.000000000 +0100
+@@ -1,154 +1,70 @@
+ /*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+- * Copyright (C) 2001 Red Hat, Inc.
++ * Copyright (C) 2001-2003 Red Hat, Inc.
+ *
+- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
++ * Created by David Woodhouse <dwmw2@redhat.com>
+ *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
++ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+- * The contents of this file are subject to the Red Hat eCos Public
+- * License Version 1.1 (the "Licence"); you may not use this file
+- * except in compliance with the Licence. You may obtain a copy of
+- * the Licence at http://www.redhat.com/
+- *
+- * Software distributed under the Licence is distributed on an "AS IS"
+- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+- * See the Licence for the specific language governing rights and
+- * limitations under the Licence.
+- *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
+- *
+- * Alternatively, the contents of this file may be used under the
+- * terms of the GNU General Public License version 2 (the "GPL"), in
+- * which case the provisions of the GPL are applicable instead of the
+- * above. If you wish to allow the use of your version of this file
+- * only under the terms of the GPL and not to allow others to use your
+- * version of this file under the RHEPL, indicate your decision by
+- * deleting the provisions above and replace them with the notice and
+- * other provisions required by the GPL. If you do not delete the
+- * provisions above, a recipient may use your version of this file
+- * under either the RHEPL or the GPL.
+- *
+- * $Id$
++ * $Id$
+ *
+ */
+
+ #include <linux/kernel.h>
+ #include <linux/fs.h>
+-#include <linux/jffs2.h>
++#include <linux/crc32.h>
++#include <linux/slab.h>
++#include <linux/pagemap.h>
+ #include <linux/mtd/mtd.h>
+ #include "nodelist.h"
+-#include <linux/crc32.h>
+
+-/* jffs2_new_inode: allocate a new inode and inocache, add it to the hash,
+- fill in the raw_inode while you're at it. */
+-struct inode *jffs2_new_inode (struct inode *dir_i, int mode, struct jffs2_raw_inode *ri)
++
++int jffs2_do_new_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, uint32_t mode, struct jffs2_raw_inode *ri)
+ {
+- struct inode *inode;
+- struct super_block *sb = dir_i->i_sb;
+ struct jffs2_inode_cache *ic;
+- struct jffs2_sb_info *c;
+- struct jffs2_inode_info *f;
+-
+- D1(printk(KERN_DEBUG "jffs2_new_inode(): dir_i %ld, mode 0x%x\n", dir_i->i_ino, mode));
+-
+- c = JFFS2_SB_INFO(sb);
+- memset(ri, 0, sizeof(*ri));
+
+ ic = jffs2_alloc_inode_cache();
+ if (!ic) {
+- return ERR_PTR(-ENOMEM);
++ return -ENOMEM;
+ }
+- memset(ic, 0, sizeof(*ic));
+-
+- inode = new_inode(sb);
+
+- if (!inode) {
+- jffs2_free_inode_cache(ic);
+- return ERR_PTR(-ENOMEM);
+- }
+-
+- /* Alloc jffs2_inode_info when that's split in 2.5 */
++ memset(ic, 0, sizeof(*ic));
+
+- f = JFFS2_INODE_INFO(inode);
+- memset(f, 0, sizeof(*f));
+ init_MUTEX_LOCKED(&f->sem);
+ f->inocache = ic;
+- inode->i_nlink = f->inocache->nlink = 1;
++ f->inocache->nlink = 1;
+ f->inocache->nodes = (struct jffs2_raw_node_ref *)f->inocache;
+- f->inocache->ino = ri->ino = inode->i_ino = ++c->highest_ino;
+- D1(printk(KERN_DEBUG "jffs2_new_inode(): Assigned ino# %d\n", ri->ino));
+- jffs2_add_ino_cache(c, f->inocache);
++ f->inocache->ino = ++c->highest_ino;
++ f->inocache->state = INO_STATE_PRESENT;
+
+- ri->magic = JFFS2_MAGIC_BITMASK;
+- ri->nodetype = JFFS2_NODETYPE_INODE;
+- ri->totlen = PAD(sizeof(*ri));
+- ri->hdr_crc = crc32(0, ri, sizeof(struct jffs2_unknown_node)-4);
+- ri->mode = mode;
+- f->highest_version = ri->version = 1;
+- ri->uid = current->fsuid;
+- if (dir_i->i_mode & S_ISGID) {
+- ri->gid = dir_i->i_gid;
+- if (S_ISDIR(mode))
+- ri->mode |= S_ISGID;
+- } else {
+- ri->gid = current->fsgid;
+- }
+- inode->i_mode = ri->mode;
+- inode->i_gid = ri->gid;
+- inode->i_uid = ri->uid;
+- inode->i_atime = inode->i_ctime = inode->i_mtime =
+- ri->atime = ri->mtime = ri->ctime = CURRENT_TIME;
+- inode->i_blksize = PAGE_SIZE;
+- inode->i_blocks = 0;
+- inode->i_size = 0;
++ ri->ino = cpu_to_je32(f->inocache->ino);
+
+- insert_inode_hash(inode);
++ D1(printk(KERN_DEBUG "jffs2_do_new_inode(): Assigned ino# %d\n", f->inocache->ino));
++ jffs2_add_ino_cache(c, f->inocache);
+
+- return inode;
+-}
++ ri->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
++ ri->nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE);
++ ri->totlen = cpu_to_je32(PAD(sizeof(*ri)));
++ ri->hdr_crc = cpu_to_je32(crc32(0, ri, sizeof(struct jffs2_unknown_node)-4));
++ ri->mode = cpu_to_jemode(mode);
+
+-/* This ought to be in core MTD code. All registered MTD devices
+- without writev should have this put in place. Bug the MTD
+- maintainer */
+-static int mtd_fake_writev(struct mtd_info *mtd, const struct iovec *vecs, unsigned long count, loff_t to, size_t *retlen)
+-{
+- unsigned long i;
+- size_t totlen = 0, thislen;
+- int ret = 0;
++ f->highest_version = 1;
++ ri->version = cpu_to_je32(f->highest_version);
+
+- for (i=0; i<count; i++) {
+- ret = mtd->write(mtd, to, vecs[i].iov_len, &thislen, vecs[i].iov_base);
+- totlen += thislen;
+- if (ret || thislen != vecs[i].iov_len)
+- break;
+- to += vecs[i].iov_len;
+- }
+- if (retlen)
+- *retlen = totlen;
+- return ret;
+-}
+-
+-
+-static inline int mtd_writev(struct mtd_info *mtd, const struct iovec *vecs, unsigned long count, loff_t to, size_t *retlen)
+-{
+- if (mtd->writev)
+- return mtd->writev(mtd,vecs,count,to,retlen);
+- else
+- return mtd_fake_writev(mtd, vecs, count, to, retlen);
++ return 0;
+ }
+
+-static void writecheck(struct mtd_info *mtd, __u32 ofs)
++#if CONFIG_JFFS2_FS_DEBUG > 0
++static void writecheck(struct jffs2_sb_info *c, uint32_t ofs)
+ {
+ unsigned char buf[16];
+- ssize_t retlen;
++ size_t retlen;
+ int ret, i;
+
+- ret = mtd->read(mtd, ofs, 16, &retlen, buf);
+- if (ret && retlen != 16) {
+- D1(printk(KERN_DEBUG "read failed or short in writecheck(). ret %d, retlen %d\n", ret, retlen));
++ ret = jffs2_flash_read(c, ofs, 16, &retlen, buf);
++ if (ret || (retlen != 16)) {
++ D1(printk(KERN_DEBUG "read failed or short in writecheck(). ret %d, retlen %zd\n", ret, retlen));
+ return;
+ }
+ ret = 0;
+@@ -157,32 +73,31 @@
+ ret = 1;
+ }
+ if (ret) {
+- printk(KERN_WARNING "ARGH. About to write node to 0x%08x on flash, but there's data already there:\n", ofs);
++ printk(KERN_WARNING "ARGH. About to write node to 0x%08x on flash, but there are data already there:\n", ofs);
+ printk(KERN_WARNING "0x%08x: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
+ ofs,
+ buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7],
+ buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]);
+ }
+ }
+-
+-
++#endif
+
+
+ /* jffs2_write_dnode - given a raw_inode, allocate a full_dnode for it,
+ write it to the flash, link it into the existing inode/fragment list */
+
+-struct jffs2_full_dnode *jffs2_write_dnode(struct inode *inode, struct jffs2_raw_inode *ri, const unsigned char *data, __u32 datalen, __u32 flash_ofs, __u32 *writelen)
++struct jffs2_full_dnode *jffs2_write_dnode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_inode *ri, const unsigned char *data, uint32_t datalen, uint32_t flash_ofs, int alloc_mode)
+
+ {
+- struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
+- struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
+ struct jffs2_raw_node_ref *raw;
+ struct jffs2_full_dnode *fn;
+- ssize_t retlen;
++ size_t retlen;
+ struct iovec vecs[2];
+ int ret;
++ int retried = 0;
++ unsigned long cnt = 2;
+
+- D1(if(ri->hdr_crc != crc32(0, ri, sizeof(struct jffs2_unknown_node)-4)) {
++ D1(if(je32_to_cpu(ri->hdr_crc) != crc32(0, ri, sizeof(struct jffs2_unknown_node)-4)) {
+ printk(KERN_CRIT "Eep. CRC not correct in jffs2_write_dnode()\n");
+ BUG();
+ }
+@@ -192,10 +107,10 @@
+ vecs[1].iov_base = (unsigned char *)data;
+ vecs[1].iov_len = datalen;
+
+- writecheck(c->mtd, flash_ofs);
++ D1(writecheck(c, flash_ofs));
+
+- if (ri->totlen != sizeof(*ri) + datalen) {
+- printk(KERN_WARNING "jffs2_write_dnode: ri->totlen (0x%08x) != sizeof(*ri) (0x%08x) + datalen (0x%08x)\n", ri->totlen, sizeof(*ri), datalen);
++ if (je32_to_cpu(ri->totlen) != sizeof(*ri) + datalen) {
++ printk(KERN_WARNING "jffs2_write_dnode: ri->totlen (0x%08x) != sizeof(*ri) (0x%08zx) + datalen (0x%08x)\n", je32_to_cpu(ri->totlen), sizeof(*ri), datalen);
+ }
+ raw = jffs2_alloc_raw_node_ref();
+ if (!raw)
+@@ -206,19 +121,28 @@
+ jffs2_free_raw_node_ref(raw);
+ return ERR_PTR(-ENOMEM);
+ }
+- raw->flash_offset = flash_ofs;
+- raw->totlen = PAD(ri->totlen);
+- raw->next_phys = NULL;
+
+- fn->ofs = ri->offset;
+- fn->size = ri->dsize;
++ fn->ofs = je32_to_cpu(ri->offset);
++ fn->size = je32_to_cpu(ri->dsize);
+ fn->frags = 0;
++
++ /* check number of valid vecs */
++ if (!datalen || !data)
++ cnt = 1;
++ retry:
+ fn->raw = raw;
+
+- ret = mtd_writev(c->mtd, vecs, 2, flash_ofs, &retlen);
++ raw->flash_offset = flash_ofs;
++ raw->__totlen = PAD(sizeof(*ri)+datalen);
++ raw->next_phys = NULL;
++
++ ret = jffs2_flash_writev(c, vecs, cnt, flash_ofs, &retlen,
++ (alloc_mode==ALLOC_GC)?0:f->inocache->ino);
++
+ if (ret || (retlen != sizeof(*ri) + datalen)) {
+- printk(KERN_NOTICE "Write of %d bytes at 0x%08x failed. returned %d, retlen %d\n",
++ printk(KERN_NOTICE "Write of %zd bytes at 0x%08x failed. returned %d, retlen %zd\n",
+ sizeof(*ri)+datalen, flash_ofs, ret, retlen);
++
+ /* Mark the space as dirtied */
+ if (retlen) {
+ /* Doesn't belong to any inode */
+@@ -229,48 +153,96 @@
+ seem corrupted, in which case the scan would skip over
+ any node we write before the original intended end of
+ this node */
+- jffs2_add_physical_node_ref(c, raw, sizeof(*ri)+datalen, 1);
++ raw->flash_offset |= REF_OBSOLETE;
++ jffs2_add_physical_node_ref(c, raw);
+ jffs2_mark_node_obsolete(c, raw);
+ } else {
+ printk(KERN_NOTICE "Not marking the space at 0x%08x as dirty because the flash driver returned retlen zero\n", raw->flash_offset);
+ jffs2_free_raw_node_ref(raw);
+ }
++ if (!retried && alloc_mode != ALLOC_NORETRY && (raw = jffs2_alloc_raw_node_ref())) {
++ /* Try to reallocate space and retry */
++ uint32_t dummy;
++ struct jffs2_eraseblock *jeb = &c->blocks[flash_ofs / c->sector_size];
++
++ retried = 1;
++
++ D1(printk(KERN_DEBUG "Retrying failed write.\n"));
++
++ ACCT_SANITY_CHECK(c,jeb);
++ D1(ACCT_PARANOIA_CHECK(jeb));
++
++ if (alloc_mode == ALLOC_GC) {
++ ret = jffs2_reserve_space_gc(c, sizeof(*ri) + datalen, &flash_ofs, &dummy);
++ } else {
++ /* Locking pain */
++ up(&f->sem);
++ jffs2_complete_reservation(c);
++
++ ret = jffs2_reserve_space(c, sizeof(*ri) + datalen, &flash_ofs, &dummy, alloc_mode);
++ down(&f->sem);
++ }
++
++ if (!ret) {
++ D1(printk(KERN_DEBUG "Allocated space at 0x%08x to retry failed write.\n", flash_ofs));
++
++ ACCT_SANITY_CHECK(c,jeb);
++ D1(ACCT_PARANOIA_CHECK(jeb));
+
++ goto retry;
++ }
++ D1(printk(KERN_DEBUG "Failed to allocate space to retry failed write: %d!\n", ret));
++ jffs2_free_raw_node_ref(raw);
++ }
+ /* Release the full_dnode which is now useless, and return */
+ jffs2_free_full_dnode(fn);
+- if (writelen)
+- *writelen = retlen;
+ return ERR_PTR(ret?ret:-EIO);
+ }
+ /* Mark the space used */
+- jffs2_add_physical_node_ref(c, raw, retlen, 0);
++ /* If node covers at least a whole page, or if it starts at the
++ beginning of a page and runs to the end of the file, or if
++ it's a hole node, mark it REF_PRISTINE, else REF_NORMAL.
++ */
++ if ((je32_to_cpu(ri->dsize) >= PAGE_CACHE_SIZE) ||
++ ( ((je32_to_cpu(ri->offset)&(PAGE_CACHE_SIZE-1))==0) &&
++ (je32_to_cpu(ri->dsize)+je32_to_cpu(ri->offset) == je32_to_cpu(ri->isize)))) {
++ raw->flash_offset |= REF_PRISTINE;
++ } else {
++ raw->flash_offset |= REF_NORMAL;
++ }
++ jffs2_add_physical_node_ref(c, raw);
+
+ /* Link into per-inode list */
+ raw->next_in_ino = f->inocache->nodes;
+ f->inocache->nodes = raw;
+
+- D1(printk(KERN_DEBUG "jffs2_write_dnode wrote node at 0x%08x with dsize 0x%x, csize 0x%x, node_crc 0x%08x, data_crc 0x%08x, totlen 0x%08x\n", flash_ofs, ri->dsize, ri->csize, ri->node_crc, ri->data_crc, ri->totlen));
+- if (writelen)
+- *writelen = retlen;
++ D1(printk(KERN_DEBUG "jffs2_write_dnode wrote node at 0x%08x(%d) with dsize 0x%x, csize 0x%x, node_crc 0x%08x, data_crc 0x%08x, totlen 0x%08x\n",
++ flash_ofs, ref_flags(raw), je32_to_cpu(ri->dsize),
++ je32_to_cpu(ri->csize), je32_to_cpu(ri->node_crc),
++ je32_to_cpu(ri->data_crc), je32_to_cpu(ri->totlen)));
++
++ if (retried) {
++ ACCT_SANITY_CHECK(c,NULL);
++ }
+
+- f->inocache->nodes = raw;
+ return fn;
+ }
+
+-struct jffs2_full_dirent *jffs2_write_dirent(struct inode *inode, struct jffs2_raw_dirent *rd, const unsigned char *name, __u32 namelen, __u32 flash_ofs, __u32 *writelen)
++struct jffs2_full_dirent *jffs2_write_dirent(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_dirent *rd, const unsigned char *name, uint32_t namelen, uint32_t flash_ofs, int alloc_mode)
+ {
+- struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
+- struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
+ struct jffs2_raw_node_ref *raw;
+ struct jffs2_full_dirent *fd;
+- ssize_t retlen;
++ size_t retlen;
+ struct iovec vecs[2];
++ int retried = 0;
+ int ret;
+
+- D1(printk(KERN_DEBUG "jffs2_write_dirent(ino #%u, name at *0x%p \"%s\"->ino #%u, name_crc 0x%08x)\n", rd->pino, name, name, rd->ino, rd->name_crc));
+- writecheck(c->mtd, flash_ofs);
++ D1(printk(KERN_DEBUG "jffs2_write_dirent(ino #%u, name at *0x%p \"%s\"->ino #%u, name_crc 0x%08x)\n",
++ je32_to_cpu(rd->pino), name, name, je32_to_cpu(rd->ino),
++ je32_to_cpu(rd->name_crc)));
++ D1(writecheck(c, flash_ofs));
+
+- D1(if(rd->hdr_crc != crc32(0, rd, sizeof(struct jffs2_unknown_node)-4)) {
++ D1(if(je32_to_cpu(rd->hdr_crc) != crc32(0, rd, sizeof(struct jffs2_unknown_node)-4)) {
+ printk(KERN_CRIT "Eep. CRC not correct in jffs2_write_dirent()\n");
+ BUG();
+ }
+@@ -291,44 +263,414 @@
+ jffs2_free_raw_node_ref(raw);
+ return ERR_PTR(-ENOMEM);
+ }
+- raw->flash_offset = flash_ofs;
+- raw->totlen = PAD(rd->totlen);
+- raw->next_in_ino = f->inocache->nodes;
+- f->inocache->nodes = raw;
+- raw->next_phys = NULL;
+
+- fd->version = rd->version;
+- fd->ino = rd->ino;
++ fd->version = je32_to_cpu(rd->version);
++ fd->ino = je32_to_cpu(rd->ino);
+ fd->nhash = full_name_hash(name, strlen(name));
+ fd->type = rd->type;
+ memcpy(fd->name, name, namelen);
+ fd->name[namelen]=0;
++
++ retry:
+ fd->raw = raw;
+
+- ret = mtd_writev(c->mtd, vecs, 2, flash_ofs, &retlen);
++ raw->flash_offset = flash_ofs;
++ raw->__totlen = PAD(sizeof(*rd)+namelen);
++ raw->next_phys = NULL;
++
++ ret = jffs2_flash_writev(c, vecs, 2, flash_ofs, &retlen,
++ (alloc_mode==ALLOC_GC)?0:je32_to_cpu(rd->pino));
+ if (ret || (retlen != sizeof(*rd) + namelen)) {
+- printk(KERN_NOTICE "Write of %d bytes at 0x%08x failed. returned %d, retlen %d\n",
++ printk(KERN_NOTICE "Write of %zd bytes at 0x%08x failed. returned %d, retlen %zd\n",
+ sizeof(*rd)+namelen, flash_ofs, ret, retlen);
+ /* Mark the space as dirtied */
+ if (retlen) {
+- jffs2_add_physical_node_ref(c, raw, sizeof(*rd)+namelen, 1);
++ raw->next_in_ino = NULL;
++ raw->flash_offset |= REF_OBSOLETE;
++ jffs2_add_physical_node_ref(c, raw);
+ jffs2_mark_node_obsolete(c, raw);
+ } else {
+ printk(KERN_NOTICE "Not marking the space at 0x%08x as dirty because the flash driver returned retlen zero\n", raw->flash_offset);
+ jffs2_free_raw_node_ref(raw);
+ }
++ if (!retried && (raw = jffs2_alloc_raw_node_ref())) {
++ /* Try to reallocate space and retry */
++ uint32_t dummy;
++ struct jffs2_eraseblock *jeb = &c->blocks[flash_ofs / c->sector_size];
++
++ retried = 1;
++
++ D1(printk(KERN_DEBUG "Retrying failed write.\n"));
++
++ ACCT_SANITY_CHECK(c,jeb);
++ D1(ACCT_PARANOIA_CHECK(jeb));
++
++ if (alloc_mode == ALLOC_GC) {
++ ret = jffs2_reserve_space_gc(c, sizeof(*rd) + namelen, &flash_ofs, &dummy);
++ } else {
++ /* Locking pain */
++ up(&f->sem);
++ jffs2_complete_reservation(c);
++
++ ret = jffs2_reserve_space(c, sizeof(*rd) + namelen, &flash_ofs, &dummy, alloc_mode);
++ down(&f->sem);
++ }
+
++ if (!ret) {
++ D1(printk(KERN_DEBUG "Allocated space at 0x%08x to retry failed write.\n", flash_ofs));
++ ACCT_SANITY_CHECK(c,jeb);
++ D1(ACCT_PARANOIA_CHECK(jeb));
++ goto retry;
++ }
++ D1(printk(KERN_DEBUG "Failed to allocate space to retry failed write: %d!\n", ret));
++ jffs2_free_raw_node_ref(raw);
++ }
+ /* Release the full_dnode which is now useless, and return */
+ jffs2_free_full_dirent(fd);
+- if (writelen)
+- *writelen = retlen;
+ return ERR_PTR(ret?ret:-EIO);
+ }
+ /* Mark the space used */
+- jffs2_add_physical_node_ref(c, raw, retlen, 0);
+- if (writelen)
+- *writelen = retlen;
++ raw->flash_offset |= REF_PRISTINE;
++ jffs2_add_physical_node_ref(c, raw);
+
++ raw->next_in_ino = f->inocache->nodes;
+ f->inocache->nodes = raw;
++
++ if (retried) {
++ ACCT_SANITY_CHECK(c,NULL);
++ }
++
+ return fd;
+ }
++
++/* The OS-specific code fills in the metadata in the jffs2_raw_inode for us, so that
++ we don't have to go digging in struct inode or its equivalent. It should set:
++ mode, uid, gid, (starting)isize, atime, ctime, mtime */
++int jffs2_write_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
++ struct jffs2_raw_inode *ri, unsigned char *buf,
++ uint32_t offset, uint32_t writelen, uint32_t *retlen)
++{
++ int ret = 0;
++ uint32_t writtenlen = 0;
++
++ D1(printk(KERN_DEBUG "jffs2_write_inode_range(): Ino #%u, ofs 0x%x, len 0x%x\n",
++ f->inocache->ino, offset, writelen));
++
++ while(writelen) {
++ struct jffs2_full_dnode *fn;
++ unsigned char *comprbuf = NULL;
++ unsigned char comprtype = JFFS2_COMPR_NONE;
++ uint32_t phys_ofs, alloclen;
++ uint32_t datalen, cdatalen;
++ int retried = 0;
++
++ retry:
++ D2(printk(KERN_DEBUG "jffs2_commit_write() loop: 0x%x to write to 0x%x\n", writelen, offset));
++
++ ret = jffs2_reserve_space(c, sizeof(*ri) + JFFS2_MIN_DATA_LEN, &phys_ofs, &alloclen, ALLOC_NORMAL);
++ if (ret) {
++ D1(printk(KERN_DEBUG "jffs2_reserve_space returned %d\n", ret));
++ break;
++ }
++ down(&f->sem);
++ datalen = min_t(uint32_t, writelen, PAGE_CACHE_SIZE - (offset & (PAGE_CACHE_SIZE-1)));
++ cdatalen = min_t(uint32_t, alloclen - sizeof(*ri), datalen);
++
++ comprtype = jffs2_compress(buf, &comprbuf, &datalen, &cdatalen);
++
++ ri->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
++ ri->nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE);
++ ri->totlen = cpu_to_je32(sizeof(*ri) + cdatalen);
++ ri->hdr_crc = cpu_to_je32(crc32(0, ri, sizeof(struct jffs2_unknown_node)-4));
++
++ ri->ino = cpu_to_je32(f->inocache->ino);
++ ri->version = cpu_to_je32(++f->highest_version);
++ ri->isize = cpu_to_je32(max(je32_to_cpu(ri->isize), offset + datalen));
++ ri->offset = cpu_to_je32(offset);
++ ri->csize = cpu_to_je32(cdatalen);
++ ri->dsize = cpu_to_je32(datalen);
++ ri->compr = comprtype;
++ ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
++ ri->data_crc = cpu_to_je32(crc32(0, comprbuf, cdatalen));
++
++ fn = jffs2_write_dnode(c, f, ri, comprbuf, cdatalen, phys_ofs, ALLOC_NORETRY);
++
++ jffs2_free_comprbuf(comprbuf, buf);
++
++ if (IS_ERR(fn)) {
++ ret = PTR_ERR(fn);
++ up(&f->sem);
++ jffs2_complete_reservation(c);
++ if (!retried) {
++ /* Write error to be retried */
++ retried = 1;
++ D1(printk(KERN_DEBUG "Retrying node write in jffs2_write_inode_range()\n"));
++ goto retry;
++ }
++ break;
++ }
++ ret = jffs2_add_full_dnode_to_inode(c, f, fn);
++ if (f->metadata) {
++ jffs2_mark_node_obsolete(c, f->metadata->raw);
++ jffs2_free_full_dnode(f->metadata);
++ f->metadata = NULL;
++ }
++ if (ret) {
++ /* Eep */
++ D1(printk(KERN_DEBUG "Eep. add_full_dnode_to_inode() failed in commit_write, returned %d\n", ret));
++ jffs2_mark_node_obsolete(c, fn->raw);
++ jffs2_free_full_dnode(fn);
++
++ up(&f->sem);
++ jffs2_complete_reservation(c);
++ break;
++ }
++ up(&f->sem);
++ jffs2_complete_reservation(c);
++ if (!datalen) {
++ printk(KERN_WARNING "Eep. We didn't actually write any data in jffs2_write_inode_range()\n");
++ ret = -EIO;
++ break;
++ }
++ D1(printk(KERN_DEBUG "increasing writtenlen by %d\n", datalen));
++ writtenlen += datalen;
++ offset += datalen;
++ writelen -= datalen;
++ buf += datalen;
++ }
++ *retlen = writtenlen;
++ return ret;
++}
++
++int jffs2_do_create(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, struct jffs2_inode_info *f, struct jffs2_raw_inode *ri, const char *name, int namelen)
++{
++ struct jffs2_raw_dirent *rd;
++ struct jffs2_full_dnode *fn;
++ struct jffs2_full_dirent *fd;
++ uint32_t alloclen, phys_ofs;
++ int ret;
++
++ /* Try to reserve enough space for both node and dirent.
++ * Just the node will do for now, though
++ */
++ ret = jffs2_reserve_space(c, sizeof(*ri), &phys_ofs, &alloclen, ALLOC_NORMAL);
++ D1(printk(KERN_DEBUG "jffs2_do_create(): reserved 0x%x bytes\n", alloclen));
++ if (ret) {
++ up(&f->sem);
++ return ret;
++ }
++
++ ri->data_crc = cpu_to_je32(0);
++ ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
++
++ fn = jffs2_write_dnode(c, f, ri, NULL, 0, phys_ofs, ALLOC_NORMAL);
++
++ D1(printk(KERN_DEBUG "jffs2_do_create created file with mode 0x%x\n",
++ jemode_to_cpu(ri->mode)));
++
++ if (IS_ERR(fn)) {
++ D1(printk(KERN_DEBUG "jffs2_write_dnode() failed\n"));
++ /* Eeek. Wave bye bye */
++ up(&f->sem);
++ jffs2_complete_reservation(c);
++ return PTR_ERR(fn);
++ }
++ /* No data here. Only a metadata node, which will be
++ obsoleted by the first data write
++ */
++ f->metadata = fn;
++
++ up(&f->sem);
++ jffs2_complete_reservation(c);
++ ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL);
++
++ if (ret) {
++ /* Eep. */
++ D1(printk(KERN_DEBUG "jffs2_reserve_space() for dirent failed\n"));
++ return ret;
++ }
++
++ rd = jffs2_alloc_raw_dirent();
++ if (!rd) {
++ /* Argh. Now we treat it like a normal delete */
++ jffs2_complete_reservation(c);
++ return -ENOMEM;
++ }
++
++ down(&dir_f->sem);
++
++ rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
++ rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT);
++ rd->totlen = cpu_to_je32(sizeof(*rd) + namelen);
++ rd->hdr_crc = cpu_to_je32(crc32(0, rd, sizeof(struct jffs2_unknown_node)-4));
++
++ rd->pino = cpu_to_je32(dir_f->inocache->ino);
++ rd->version = cpu_to_je32(++dir_f->highest_version);
++ rd->ino = ri->ino;
++ rd->mctime = ri->ctime;
++ rd->nsize = namelen;
++ rd->type = DT_REG;
++ rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8));
++ rd->name_crc = cpu_to_je32(crc32(0, name, namelen));
++
++ fd = jffs2_write_dirent(c, dir_f, rd, name, namelen, phys_ofs, ALLOC_NORMAL);
++
++ jffs2_free_raw_dirent(rd);
++
++ if (IS_ERR(fd)) {
++ /* dirent failed to write. Delete the inode normally
++ as if it were the final unlink() */
++ jffs2_complete_reservation(c);
++ up(&dir_f->sem);
++ return PTR_ERR(fd);
++ }
++
++ /* Link the fd into the inode's list, obsoleting an old
++ one if necessary. */
++ jffs2_add_fd_to_list(c, fd, &dir_f->dents);
++
++ jffs2_complete_reservation(c);
++ up(&dir_f->sem);
++
++ return 0;
++}
++
++
++int jffs2_do_unlink(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f,
++ const char *name, int namelen, struct jffs2_inode_info *dead_f)
++{
++ struct jffs2_raw_dirent *rd;
++ struct jffs2_full_dirent *fd;
++ uint32_t alloclen, phys_ofs;
++ int ret;
++
++ rd = jffs2_alloc_raw_dirent();
++ if (!rd)
++ return -ENOMEM;
++
++ ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_DELETION);
++ if (ret) {
++ jffs2_free_raw_dirent(rd);
++ return ret;
++ }
++
++ down(&dir_f->sem);
++
++ /* Build a deletion node */
++ rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
++ rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT);
++ rd->totlen = cpu_to_je32(sizeof(*rd) + namelen);
++ rd->hdr_crc = cpu_to_je32(crc32(0, rd, sizeof(struct jffs2_unknown_node)-4));
++
++ rd->pino = cpu_to_je32(dir_f->inocache->ino);
++ rd->version = cpu_to_je32(++dir_f->highest_version);
++ rd->ino = cpu_to_je32(0);
++ rd->mctime = cpu_to_je32(get_seconds());
++ rd->nsize = namelen;
++ rd->type = DT_UNKNOWN;
++ rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8));
++ rd->name_crc = cpu_to_je32(crc32(0, name, namelen));
++
++ fd = jffs2_write_dirent(c, dir_f, rd, name, namelen, phys_ofs, ALLOC_DELETION);
++
++ jffs2_free_raw_dirent(rd);
++
++ if (IS_ERR(fd)) {
++ jffs2_complete_reservation(c);
++ up(&dir_f->sem);
++ return PTR_ERR(fd);
++ }
++
++ /* File it. This will mark the old one obsolete. */
++ jffs2_add_fd_to_list(c, fd, &dir_f->dents);
++
++ up(&dir_f->sem);
++
++ /* dead_f is NULL if this was a rename not a real unlink */
++ /* Also catch the !f->inocache case, where there was a dirent
++ pointing to an inode which didn't exist. */
++ if (dead_f && dead_f->inocache) {
++
++ down(&dead_f->sem);
++
++ while (dead_f->dents) {
++ /* There can be only deleted ones */
++ fd = dead_f->dents;
++
++ dead_f->dents = fd->next;
++
++ if (fd->ino) {
++ printk(KERN_WARNING "Deleting inode #%u with active dentry \"%s\"->ino #%u\n",
++ dead_f->inocache->ino, fd->name, fd->ino);
++ } else {
++ D1(printk(KERN_DEBUG "Removing deletion dirent for \"%s\" from dir ino #%u\n", fd->name, dead_f->inocache->ino));
++ }
++ jffs2_mark_node_obsolete(c, fd->raw);
++ jffs2_free_full_dirent(fd);
++ }
++
++ dead_f->inocache->nlink--;
++ /* NB: Caller must set inode nlink if appropriate */
++ up(&dead_f->sem);
++ }
++
++ jffs2_complete_reservation(c);
++
++ return 0;
++}
++
++
++int jffs2_do_link (struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, uint32_t ino, uint8_t type, const char *name, int namelen)
++{
++ struct jffs2_raw_dirent *rd;
++ struct jffs2_full_dirent *fd;
++ uint32_t alloclen, phys_ofs;
++ int ret;
++
++ rd = jffs2_alloc_raw_dirent();
++ if (!rd)
++ return -ENOMEM;
++
++ ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL);
++ if (ret) {
++ jffs2_free_raw_dirent(rd);
++ return ret;
++ }
++
++ down(&dir_f->sem);
++
++ /* Build a deletion node */
++ rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
++ rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT);
++ rd->totlen = cpu_to_je32(sizeof(*rd) + namelen);
++ rd->hdr_crc = cpu_to_je32(crc32(0, rd, sizeof(struct jffs2_unknown_node)-4));
++
++ rd->pino = cpu_to_je32(dir_f->inocache->ino);
++ rd->version = cpu_to_je32(++dir_f->highest_version);
++ rd->ino = cpu_to_je32(ino);
++ rd->mctime = cpu_to_je32(get_seconds());
++ rd->nsize = namelen;
++
++ rd->type = type;
++
++ rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8));
++ rd->name_crc = cpu_to_je32(crc32(0, name, namelen));
++
++ fd = jffs2_write_dirent(c, dir_f, rd, name, namelen, phys_ofs, ALLOC_NORMAL);
++
++ jffs2_free_raw_dirent(rd);
++
++ if (IS_ERR(fd)) {
++ jffs2_complete_reservation(c);
++ up(&dir_f->sem);
++ return PTR_ERR(fd);
++ }
++
++ /* File it. This will mark the old one obsolete. */
++ jffs2_add_fd_to_list(c, fd, &dir_f->dents);
++
++ jffs2_complete_reservation(c);
++ up(&dir_f->sem);
++
++ return 0;
++}
+diff -Nurb linux-mips-2.4.24-pre2/fs/jffs2/writev.c linux/fs/jffs2/writev.c
+--- linux-mips-2.4.24-pre2/fs/jffs2/writev.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/fs/jffs2/writev.c 2004-11-17 18:17:59.413257072 +0100
+@@ -0,0 +1,50 @@
++/*
++ * JFFS2 -- Journalling Flash File System, Version 2.
++ *
++ * Copyright (C) 2001, 2002 Red Hat, Inc.
++ *
++ * Created by David Woodhouse <dwmw2@redhat.com>
++ *
++ * For licensing information, see the file 'LICENCE' in this directory.
++ *
++ * $Id$
++ *
++ */
++
++#include <linux/kernel.h>
++#include <linux/mtd/mtd.h>
++#include "nodelist.h"
++
++/* This ought to be in core MTD code. All registered MTD devices
++ without writev should have this put in place. Bug the MTD
++ maintainer */
++static inline int mtd_fake_writev(struct mtd_info *mtd, const struct iovec *vecs,
++ unsigned long count, loff_t to, size_t *retlen)
++{
++ unsigned long i;
++ size_t totlen = 0, thislen;
++ int ret = 0;
++
++ for (i=0; i<count; i++) {
++ if (!vecs[i].iov_len)
++ continue;
++ ret = mtd->write(mtd, to, vecs[i].iov_len, &thislen, vecs[i].iov_base);
++ totlen += thislen;
++ if (ret || thislen != vecs[i].iov_len)
++ break;
++ to += vecs[i].iov_len;
++ }
++ if (retlen)
++ *retlen = totlen;
++ return ret;
++}
++
++int jffs2_flash_direct_writev(struct jffs2_sb_info *c, const struct iovec *vecs,
++ unsigned long count, loff_t to, size_t *retlen)
++{
++ if (c->mtd->writev)
++ return c->mtd->writev(c->mtd, vecs, count, to, retlen);
++ else
++ return mtd_fake_writev(c->mtd, vecs, count, to, retlen);
++}
++
+diff -Nurb linux-mips-2.4.24-pre2/include/linux/jffs2.h linux/include/linux/jffs2.h
+--- linux-mips-2.4.24-pre2/include/linux/jffs2.h 2004-11-17 18:05:09.000000000 +0100
++++ linux/include/linux/jffs2.h 2004-11-17 18:17:59.000000000 +0100
+@@ -1,50 +1,30 @@
+ /*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+- * Copyright (C) 2001 Red Hat, Inc.
++ * Copyright (C) 2001-2003 Red Hat, Inc.
+ *
+- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
++ * Created by David Woodhouse <dwmw2@redhat.com>
+ *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
++ * For licensing information, see the file 'LICENCE' in the
++ * jffs2 directory.
+ *
+- * The contents of this file are subject to the Red Hat eCos Public
+- * License Version 1.1 (the "Licence"); you may not use this file
+- * except in compliance with the Licence. You may obtain a copy of
+- * the Licence at http://www.redhat.com/
+- *
+- * Software distributed under the Licence is distributed on an "AS IS"
+- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+- * See the Licence for the specific language governing rights and
+- * limitations under the Licence.
+- *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
+- *
+- * Alternatively, the contents of this file may be used under the
+- * terms of the GNU General Public License version 2 (the "GPL"), in
+- * which case the provisions of the GPL are applicable instead of the
+- * above. If you wish to allow the use of your version of this file
+- * only under the terms of the GPL and not to allow others to use your
+- * version of this file under the RHEPL, indicate your decision by
+- * deleting the provisions above and replace them with the notice and
+- * other provisions required by the GPL. If you do not delete the
+- * provisions above, a recipient may use your version of this file
+- * under either the RHEPL or the GPL.
+- *
+- * $Id$
++ * $Id$
+ *
+ */
+
+ #ifndef __LINUX_JFFS2_H__
+ #define __LINUX_JFFS2_H__
+
+-#include <asm/types.h>
++/* You must include something which defines the C99 uintXX_t types.
++ We don't do it from here because this file is used in too many
++ different environments. */
++
+ #define JFFS2_SUPER_MAGIC 0x72b6
+
+ /* Values we may expect to find in the 'magic' field */
+ #define JFFS2_OLD_MAGIC_BITMASK 0x1984
+ #define JFFS2_MAGIC_BITMASK 0x1985
+-#define KSAMTIB_CIGAM_2SFFJ 0x5981 /* For detecting wrong-endian fs */
++#define KSAMTIB_CIGAM_2SFFJ 0x8519 /* For detecting wrong-endian fs */
+ #define JFFS2_EMPTY_BITMASK 0xffff
+ #define JFFS2_DIRTY_BITMASK 0x0000
+
+@@ -78,16 +58,12 @@
+ #define JFFS2_NODETYPE_DIRENT (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 1)
+ #define JFFS2_NODETYPE_INODE (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 2)
+ #define JFFS2_NODETYPE_CLEANMARKER (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 3)
++#define JFFS2_NODETYPE_PADDING (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 4)
+
+ // Maybe later...
+ //#define JFFS2_NODETYPE_CHECKPOINT (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 3)
+ //#define JFFS2_NODETYPE_OPTIONS (JFFS2_FEATURE_RWCOMPAT_COPY | JFFS2_NODE_ACCURATE | 4)
+
+-/* Same as the non_ECC versions, but with extra space for real
+- * ECC instead of just the checksum. For use on NAND flash
+- */
+-//#define JFFS2_NODETYPE_DIRENT_ECC (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 5)
+-//#define JFFS2_NODETYPE_INODE_ECC (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 6)
+
+ #define JFFS2_INO_FLAG_PREREAD 1 /* Do read_inode() for this one at
+ mount time, don't wait for it to
+@@ -96,31 +72,79 @@
+ compression type */
+
+
++/* These can go once we've made sure we've caught all uses without
++ byteswapping */
++
++typedef struct {
++ uint32_t v32;
++} __attribute__((packed)) jint32_t;
++
++typedef struct {
++ uint32_t m;
++} __attribute__((packed)) jmode_t;
++
++typedef struct {
++ uint16_t v16;
++} __attribute__((packed)) jint16_t;
++
++#define JFFS2_NATIVE_ENDIAN
++
++/* Note we handle mode bits conversion from JFFS2 (i.e. Linux) to/from
++ whatever OS we're actually running on here too. */
++
++#if defined(JFFS2_NATIVE_ENDIAN)
++#define cpu_to_je16(x) ((jint16_t){x})
++#define cpu_to_je32(x) ((jint32_t){x})
++#define cpu_to_jemode(x) ((jmode_t){os_to_jffs2_mode(x)})
++
++#define je16_to_cpu(x) ((x).v16)
++#define je32_to_cpu(x) ((x).v32)
++#define jemode_to_cpu(x) (jffs2_to_os_mode((x).m))
++#elif defined(JFFS2_BIG_ENDIAN)
++#define cpu_to_je16(x) ((jint16_t){cpu_to_be16(x)})
++#define cpu_to_je32(x) ((jint32_t){cpu_to_be32(x)})
++#define cpu_to_jemode(x) ((jmode_t){cpu_to_be32(os_to_jffs2_mode(x))})
++
++#define je16_to_cpu(x) (be16_to_cpu(x.v16))
++#define je32_to_cpu(x) (be32_to_cpu(x.v32))
++#define jemode_to_cpu(x) (be32_to_cpu(jffs2_to_os_mode((x).m)))
++#elif defined(JFFS2_LITTLE_ENDIAN)
++#define cpu_to_je16(x) ((jint16_t){cpu_to_le16(x)})
++#define cpu_to_je32(x) ((jint32_t){cpu_to_le32(x)})
++#define cpu_to_jemode(x) ((jmode_t){cpu_to_le32(os_to_jffs2_mode(x))})
++
++#define je16_to_cpu(x) (le16_to_cpu(x.v16))
++#define je32_to_cpu(x) (le32_to_cpu(x.v32))
++#define jemode_to_cpu(x) (le32_to_cpu(jffs2_to_os_mode((x).m)))
++#else
++#error wibble
++#endif
++
+ struct jffs2_unknown_node
+ {
+ /* All start like this */
+- __u16 magic;
+- __u16 nodetype;
+- __u32 totlen; /* So we can skip over nodes we don't grok */
+- __u32 hdr_crc;
++ jint16_t magic;
++ jint16_t nodetype;
++ jint32_t totlen; /* So we can skip over nodes we don't grok */
++ jint32_t hdr_crc;
+ } __attribute__((packed));
+
+ struct jffs2_raw_dirent
+ {
+- __u16 magic;
+- __u16 nodetype; /* == JFFS_NODETYPE_DIRENT */
+- __u32 totlen;
+- __u32 hdr_crc;
+- __u32 pino;
+- __u32 version;
+- __u32 ino; /* == zero for unlink */
+- __u32 mctime;
+- __u8 nsize;
+- __u8 type;
+- __u8 unused[2];
+- __u32 node_crc;
+- __u32 name_crc;
+- __u8 name[0];
++ jint16_t magic;
++ jint16_t nodetype; /* == JFFS_NODETYPE_DIRENT */
++ jint32_t totlen;
++ jint32_t hdr_crc;
++ jint32_t pino;
++ jint32_t version;
++ jint32_t ino; /* == zero for unlink */
++ jint32_t mctime;
++ uint8_t nsize;
++ uint8_t type;
++ uint8_t unused[2];
++ jint32_t node_crc;
++ jint32_t name_crc;
++ uint8_t name[0];
+ } __attribute__((packed));
+
+ /* The JFFS2 raw inode structure: Used for storage on physical media. */
+@@ -131,28 +155,28 @@
+ */
+ struct jffs2_raw_inode
+ {
+- __u16 magic; /* A constant magic number. */
+- __u16 nodetype; /* == JFFS_NODETYPE_INODE */
+- __u32 totlen; /* Total length of this node (inc data, etc.) */
+- __u32 hdr_crc;
+- __u32 ino; /* Inode number. */
+- __u32 version; /* Version number. */
+- __u32 mode; /* The file's type or mode. */
+- __u16 uid; /* The file's owner. */
+- __u16 gid; /* The file's group. */
+- __u32 isize; /* Total resultant size of this inode (used for truncations) */
+- __u32 atime; /* Last access time. */
+- __u32 mtime; /* Last modification time. */
+- __u32 ctime; /* Change time. */
+- __u32 offset; /* Where to begin to write. */
+- __u32 csize; /* (Compressed) data size */
+- __u32 dsize; /* Size of the node's data. (after decompression) */
+- __u8 compr; /* Compression algorithm used */
+- __u8 usercompr; /* Compression algorithm requested by the user */
+- __u16 flags; /* See JFFS2_INO_FLAG_* */
+- __u32 data_crc; /* CRC for the (compressed) data. */
+- __u32 node_crc; /* CRC for the raw inode (excluding data) */
+-// __u8 data[dsize];
++ jint16_t magic; /* A constant magic number. */
++ jint16_t nodetype; /* == JFFS_NODETYPE_INODE */
++ jint32_t totlen; /* Total length of this node (inc data, etc.) */
++ jint32_t hdr_crc;
++ jint32_t ino; /* Inode number. */
++ jint32_t version; /* Version number. */
++ jmode_t mode; /* The file's type or mode. */
++ jint16_t uid; /* The file's owner. */
++ jint16_t gid; /* The file's group. */
++ jint32_t isize; /* Total resultant size of this inode (used for truncations) */
++ jint32_t atime; /* Last access time. */
++ jint32_t mtime; /* Last modification time. */
++ jint32_t ctime; /* Change time. */
++ jint32_t offset; /* Where to begin to write. */
++ jint32_t csize; /* (Compressed) data size */
++ jint32_t dsize; /* Size of the node's data. (after decompression) */
++ uint8_t compr; /* Compression algorithm used */
++ uint8_t usercompr; /* Compression algorithm requested by the user */
++ jint16_t flags; /* See JFFS2_INO_FLAG_* */
++ jint32_t data_crc; /* CRC for the (compressed) data. */
++ jint32_t node_crc; /* CRC for the raw inode (excluding data) */
++ uint8_t data[0];
+ } __attribute__((packed));
+
+ union jffs2_node_union {
+diff -Nurb linux-mips-2.4.24-pre2/include/linux/jffs2_fs_i.h linux/include/linux/jffs2_fs_i.h
+--- linux-mips-2.4.24-pre2/include/linux/jffs2_fs_i.h 2004-11-17 18:05:09.000000000 +0100
++++ linux/include/linux/jffs2_fs_i.h 2004-11-17 18:17:59.000000000 +0100
+@@ -1,22 +1,12 @@
+-/* $Id$ */
++/* $Id$ */
+
+ #ifndef _JFFS2_FS_I
+ #define _JFFS2_FS_I
+
+-/* Include the pipe_inode_info at the beginning so that we can still
+- use the storage space in the inode when we have a pipe inode.
+- This sucks.
+-*/
+-
+-#undef THISSUCKS /* Only for 2.2 */
+-#ifdef THISSUCKS
+-#include <linux/pipe_fs_i.h>
+-#endif
++#include <linux/version.h>
++#include <linux/rbtree.h>
+
+ struct jffs2_inode_info {
+-#ifdef THISSUCKS
+- struct pipe_inode_info pipecrap;
+-#endif
+ /* We need an internal semaphore similar to inode->i_sem.
+ Unfortunately, we can't used the existing one, because
+ either the GC would deadlock, or we'd have to release it
+@@ -26,10 +16,10 @@
+ struct semaphore sem;
+
+ /* The highest (datanode) version number used for this ino */
+- __u32 highest_version;
++ uint32_t highest_version;
+
+ /* List of data fragments which make up the file */
+- struct jffs2_node_frag *fraglist;
++ struct rb_root fragtree;
+
+ /* There may be one datanode which isn't referenced by any of the
+ above fragments, if it contains a metadata update but no actual
+@@ -44,19 +34,13 @@
+ /* Some stuff we just have to keep in-core at all times, for each inode. */
+ struct jffs2_inode_cache *inocache;
+
+- /* Keep a pointer to the last physical node in the list. We don't
+- use the doubly-linked lists because we don't want to increase
+- the memory usage that much. This is simpler */
+- // struct jffs2_raw_node_ref *lastnode;
+- __u16 flags;
+- __u8 usercompr;
+-};
+-
+-#ifdef JFFS2_OUT_OF_KERNEL
+-#define JFFS2_INODE_INFO(i) ((struct jffs2_inode_info *) &(i)->u)
+-#else
+-#define JFFS2_INODE_INFO(i) (&i->u.jffs2_i)
++ uint16_t flags;
++ uint8_t usercompr;
++#if !defined (__ECOS)
++#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,2)
++ struct inode vfs_inode;
+ #endif
++#endif
++};
+
+ #endif /* _JFFS2_FS_I */
+-
+diff -Nurb linux-mips-2.4.24-pre2/include/linux/jffs2_fs_sb.h linux/include/linux/jffs2_fs_sb.h
+--- linux-mips-2.4.24-pre2/include/linux/jffs2_fs_sb.h 2004-11-17 18:05:09.000000000 +0100
++++ linux/include/linux/jffs2_fs_sb.h 2004-11-17 18:17:59.000000000 +0100
+@@ -1,19 +1,22 @@
+-/* $Id$ */
++/* $Id$ */
+
+ #ifndef _JFFS2_FS_SB
+ #define _JFFS2_FS_SB
+
+ #include <linux/types.h>
+ #include <linux/spinlock.h>
++#include <linux/workqueue.h>
+ #include <linux/completion.h>
+ #include <asm/semaphore.h>
++#include <linux/timer.h>
++#include <linux/wait.h>
+ #include <linux/list.h>
+
+-#define INOCACHE_HASHSIZE 1
+-
+ #define JFFS2_SB_FLAG_RO 1
+ #define JFFS2_SB_FLAG_MOUNTING 2
+
++struct jffs2_inodirty;
++
+ /* A struct for the overall file system control. Pointers to
+ jffs2_sb_info structs are named `c' in the source code.
+ Nee jffs_control
+@@ -21,36 +24,46 @@
+ struct jffs2_sb_info {
+ struct mtd_info *mtd;
+
+- __u32 highest_ino;
++ uint32_t highest_ino;
++ uint32_t checked_ino;
++
+ unsigned int flags;
+- spinlock_t nodelist_lock;
+
+- // pid_t thread_pid; /* GC thread's PID */
+ struct task_struct *gc_task; /* GC task struct */
+ struct semaphore gc_thread_start; /* GC thread start mutex */
+ struct completion gc_thread_exit; /* GC thread exit completion port */
+- // __u32 gc_minfree_threshold; /* GC trigger thresholds */
+- // __u32 gc_maxdirty_threshold;
+
+ struct semaphore alloc_sem; /* Used to protect all the following
+ fields, and also to protect against
+ out-of-order writing of nodes.
+ And GC.
+ */
+- __u32 flash_size;
+- __u32 used_size;
+- __u32 dirty_size;
+- __u32 free_size;
+- __u32 erasing_size;
+- __u32 bad_size;
+- __u32 sector_size;
+- // __u32 min_free_size;
+- // __u32 max_chunk_size;
++ uint32_t cleanmarker_size; /* Size of an _inline_ CLEANMARKER
++ (i.e. zero for OOB CLEANMARKER */
++
++ uint32_t flash_size;
++ uint32_t used_size;
++ uint32_t dirty_size;
++ uint32_t wasted_size;
++ uint32_t free_size;
++ uint32_t erasing_size;
++ uint32_t bad_size;
++ uint32_t sector_size;
++ uint32_t unchecked_size;
++
++ uint32_t nr_free_blocks;
++ uint32_t nr_erasing_blocks;
++
++ /* Number of free blocks there must be before we... */
++ uint8_t resv_blocks_write; /* ... allow a normal filesystem write */
++ uint8_t resv_blocks_deletion; /* ... allow a normal filesystem deletion */
++ uint8_t resv_blocks_gctrigger; /* ... wake up the GC thread */
++ uint8_t resv_blocks_gcbad; /* ... pick a block from the bad_list to GC */
++ uint8_t resv_blocks_gcmerge; /* ... merge pages when garbage collecting */
+
+- __u32 nr_free_blocks;
+- __u32 nr_erasing_blocks;
++ uint32_t nospc_dirty_size;
+
+- __u32 nr_blocks;
++ uint32_t nr_blocks;
+ struct jffs2_eraseblock *blocks; /* The whole array of blocks. Used for getting blocks
+ * from the offset (blocks[ofs / sector_size]) */
+ struct jffs2_eraseblock *nextblock; /* The block we're currently filling */
+@@ -58,9 +71,12 @@
+ struct jffs2_eraseblock *gcblock; /* The block we're currently garbage-collecting */
+
+ struct list_head clean_list; /* Blocks 100% full of clean data */
++ struct list_head very_dirty_list; /* Blocks with lots of dirty space */
+ struct list_head dirty_list; /* Blocks with some dirty space */
++ struct list_head erasable_list; /* Blocks which are completely dirty, and need erasing */
++ struct list_head erasable_pending_wbuf_list; /* Blocks which need erasing but only after the current wbuf is flushed */
+ struct list_head erasing_list; /* Blocks which are currently erasing */
+- struct list_head erase_pending_list; /* Blocks which need erasing */
++ struct list_head erase_pending_list; /* Blocks which need erasing now */
+ struct list_head erase_complete_list; /* Blocks which are erased and need the clean marker written to them */
+ struct list_head free_list; /* Blocks which are free and ready to be used */
+ struct list_head bad_list; /* Bad blocks. */
+@@ -69,16 +85,33 @@
+ spinlock_t erase_completion_lock; /* Protect free_list and erasing_list
+ against erase completion handler */
+ wait_queue_head_t erase_wait; /* For waiting for erases to complete */
+- struct jffs2_inode_cache *inocache_list[INOCACHE_HASHSIZE];
++
++ wait_queue_head_t inocache_wq;
++ struct jffs2_inode_cache **inocache_list;
+ spinlock_t inocache_lock;
+-};
+
+-#ifdef JFFS2_OUT_OF_KERNEL
+-#define JFFS2_SB_INFO(sb) ((struct jffs2_sb_info *) &(sb)->u)
+-#else
+-#define JFFS2_SB_INFO(sb) (&sb->u.jffs2_sb)
++ /* Sem to allow jffs2_garbage_collect_deletion_dirent to
++ drop the erase_completion_lock while it's holding a pointer
++ to an obsoleted node. I don't like this. Alternatives welcomed. */
++ struct semaphore erase_free_sem;
++
++#ifdef CONFIG_JFFS2_FS_NAND
++ /* Write-behind buffer for NAND flash */
++ unsigned char *wbuf;
++ uint32_t wbuf_ofs;
++ uint32_t wbuf_len;
++ uint32_t wbuf_pagesize;
++ struct jffs2_inodirty *wbuf_inodes;
++
++ /* Information about out-of-band area usage... */
++ struct nand_oobinfo *oobinfo;
++ uint32_t badblock_pos;
++ uint32_t fsdata_pos;
++ uint32_t fsdata_len;
+ #endif
+
+-#define OFNI_BS_2SFFJ(c) ((struct super_block *) ( ((char *)c) - ((char *)(&((struct super_block *)NULL)->u)) ) )
++ /* OS-private pointer for getting back to master superblock info */
++ void *os_priv;
++};
+
+ #endif /* _JFFS2_FB_SB */
+diff -Nurb linux-mips-2.4.24-pre2/include/linux/mtd/blktrans.h linux/include/linux/mtd/blktrans.h
+--- linux-mips-2.4.24-pre2/include/linux/mtd/blktrans.h 1970-01-01 01:00:00.000000000 +0100
++++ linux/include/linux/mtd/blktrans.h 2004-11-17 18:17:59.205288688 +0100
+@@ -0,0 +1,72 @@
++/*
++ * $Id$
++ *
++ * (C) 2003 David Woodhouse <dwmw2@infradead.org>
++ *
++ * Interface to Linux block layer for MTD 'translation layers'.
++ *
++ */
++
++#ifndef __MTD_TRANS_H__
++#define __MTD_TRANS_H__
++
++#include <asm/semaphore.h>
++
++struct hd_geometry;
++struct mtd_info;
++struct mtd_blktrans_ops;
++struct file;
++struct inode;
++
++struct mtd_blktrans_dev {
++ struct mtd_blktrans_ops *tr;
++ struct list_head list;
++ struct mtd_info *mtd;
++ struct semaphore sem;
++ int devnum;
++ int blksize;
++ unsigned long size;
++ int readonly;
++ void *blkcore_priv; /* gendisk in 2.5, devfs_handle in 2.4 */
++};
++
++struct blkcore_priv; /* Differs for 2.4 and 2.5 kernels; private */
++
++struct mtd_blktrans_ops {
++ char *name;
++ int major;
++ int part_bits;
++
++ /* Access functions */
++ int (*readsect)(struct mtd_blktrans_dev *dev,
++ unsigned long block, char *buffer);
++ int (*writesect)(struct mtd_blktrans_dev *dev,
++ unsigned long block, char *buffer);
++
++ /* Block layer ioctls */
++ int (*getgeo)(struct mtd_blktrans_dev *dev, struct hd_geometry *geo);
++ int (*flush)(struct mtd_blktrans_dev *dev);
++
++ /* Called with mtd_table_mutex held; no race with add/remove */
++ int (*open)(struct mtd_blktrans_dev *dev);
++ int (*release)(struct mtd_blktrans_dev *dev);
++
++ /* Called on {de,}registration and on subsequent addition/removal
++ of devices, with mtd_table_mutex held. */
++ void (*add_mtd)(struct mtd_blktrans_ops *tr, struct mtd_info *mtd);
++ void (*remove_dev)(struct mtd_blktrans_dev *dev);
++
++ struct list_head devs;
++ struct list_head list;
++ struct module *owner;
++
++ struct mtd_blkcore_priv *blkcore_priv;
++};
++
++extern int register_mtd_blktrans(struct mtd_blktrans_ops *tr);
++extern int deregister_mtd_blktrans(struct mtd_blktrans_ops *tr);
++extern int add_mtd_blktrans_dev(struct mtd_blktrans_dev *dev);
++extern int del_mtd_blktrans_dev(struct mtd_blktrans_dev *dev);
++
++
++#endif /* __MTD_TRANS_H__ */
+diff -Nurb linux-mips-2.4.24-pre2/include/linux/mtd/cfi.h linux/include/linux/mtd/cfi.h
+--- linux-mips-2.4.24-pre2/include/linux/mtd/cfi.h 2004-11-17 18:05:09.000000000 +0100
++++ linux/include/linux/mtd/cfi.h 2004-11-17 18:17:59.207288384 +0100
+@@ -1,13 +1,14 @@
+
+ /* Common Flash Interface structures
+ * See http://support.intel.com/design/flash/technote/index.htm
+- * $Id$
++ * $Id$
+ */
+
+ #ifndef __MTD_CFI_H__
+ #define __MTD_CFI_H__
+
+ #include <linux/config.h>
++#include <linux/version.h>
+ #include <linux/delay.h>
+ #include <linux/types.h>
+ #include <linux/interrupt.h>
+@@ -260,7 +261,8 @@
+ __u8 pri[3];
+ __u8 MajorVersion;
+ __u8 MinorVersion;
+- __u32 FeatureSupport;
++ __u32 FeatureSupport; /* if bit 31 is set then an additional __u32 feature
++ block follows - FIXME - not currently supported */
+ __u8 SuspendCmdSupport;
+ __u16 BlkStatusRegMask;
+ __u8 VccOptimal;
+@@ -271,6 +273,25 @@
+ __u8 UserProtRegSize;
+ } __attribute__((packed));
+
++/* Vendor-Specific PRI for AMD/Fujitsu Extended Command Set (0x0002) */
++
++struct cfi_pri_amdstd {
++ __u8 pri[3];
++ __u8 MajorVersion;
++ __u8 MinorVersion;
++ __u8 SiliconRevision; /* bits 1-0: Address Sensitive Unlock */
++ __u8 EraseSuspend;
++ __u8 BlkProt;
++ __u8 TmpBlkUnprotect;
++ __u8 BlkProtUnprot;
++ __u8 SimultaneousOps;
++ __u8 BurstMode;
++ __u8 PageMode;
++ __u8 VppMin;
++ __u8 VppMax;
++ __u8 TopBottom;
++} __attribute__((packed));
++
+ struct cfi_pri_query {
+ __u8 NumFields;
+ __u32 ProtField[1]; /* Not host ordered */
+@@ -314,8 +335,6 @@
+ struct flchip chips[0]; /* per-chip data structure for each chip */
+ };
+
+-#define MAX_CFI_CHIPS 8 /* Entirely arbitrary to avoid realloc() */
+-
+ /*
+ * Returns the command address according to the given geometry.
+ */
+@@ -387,13 +406,13 @@
+ static inline cfi_word cfi_read(struct map_info *map, __u32 addr)
+ {
+ if (cfi_buswidth_is_1()) {
+- return map->read8(map, addr);
++ return map_read8(map, addr);
+ } else if (cfi_buswidth_is_2()) {
+- return map->read16(map, addr);
++ return map_read16(map, addr);
+ } else if (cfi_buswidth_is_4()) {
+- return map->read32(map, addr);
++ return map_read32(map, addr);
+ } else if (cfi_buswidth_is_8()) {
+- return map->read64(map, addr);
++ return map_read64(map, addr);
+ } else {
+ return 0;
+ }
+@@ -406,13 +425,13 @@
+ static inline void cfi_write(struct map_info *map, cfi_word val, __u32 addr)
+ {
+ if (cfi_buswidth_is_1()) {
+- map->write8(map, val, addr);
++ map_write8(map, val, addr);
+ } else if (cfi_buswidth_is_2()) {
+- map->write16(map, val, addr);
++ map_write16(map, val, addr);
+ } else if (cfi_buswidth_is_4()) {
+- map->write32(map, val, addr);
++ map_write32(map, val, addr);
+ } else if (cfi_buswidth_is_8()) {
+- map->write64(map, val, addr);
++ map_write64(map, val, addr);
+ }
+ }
+
+@@ -443,13 +462,13 @@
+ static inline __u8 cfi_read_query(struct map_info *map, __u32 addr)
+ {
+ if (cfi_buswidth_is_1()) {
+- return map->read8(map, addr);
++ return map_read8(map, addr);
+ } else if (cfi_buswidth_is_2()) {
+- return cfi16_to_cpu(map->read16(map, addr));
++ return cfi16_to_cpu(map_read16(map, addr));
+ } else if (cfi_buswidth_is_4()) {
+- return cfi32_to_cpu(map->read32(map, addr));
++ return cfi32_to_cpu(map_read32(map, addr));
+ } else if (cfi_buswidth_is_8()) {
+- return cfi64_to_cpu(map->read64(map, addr));
++ return cfi64_to_cpu(map_read64(map, addr));
+ } else {
+ return 0;
+ }
+@@ -479,5 +498,19 @@
+ spin_unlock_bh(mutex);
+ }
+
++struct cfi_extquery *cfi_read_pri(struct map_info *map, __u16 adr, __u16 size,
++ const char* name);
++
++struct cfi_fixup {
++ __u16 mfr;
++ __u16 id;
++ void (*fixup)(struct map_info *map, void* param);
++ void* param;
++};
++
++#define CFI_MFR_ANY 0xffff
++#define CFI_ID_ANY 0xffff
++
++void cfi_fixup(struct map_info *map, struct cfi_fixup* fixups);
+
+ #endif /* __MTD_CFI_H__ */
+diff -Nurb linux-mips-2.4.24-pre2/include/linux/mtd/compatmac.h linux/include/linux/mtd/compatmac.h
+--- linux-mips-2.4.24-pre2/include/linux/mtd/compatmac.h 2004-11-17 18:05:09.000000000 +0100
++++ linux/include/linux/mtd/compatmac.h 2004-11-17 18:17:59.210287928 +0100
+@@ -1,573 +1,152 @@
+-
+ /*
+- * mtd/include/compatmac.h
+- *
+- * $Id$
++ * $Id$
+ *
+ * Extensions and omissions from the normal 'linux/compatmac.h'
+ * files. hopefully this will end up empty as the 'real' one
+ * becomes fully-featured.
+ */
+
+-
+-/* First, include the parts which the kernel is good enough to provide
+- * to us
+- */
+-
+ #ifndef __LINUX_MTD_COMPATMAC_H__
+ #define __LINUX_MTD_COMPATMAC_H__
+
+-#include <linux/config.h>
+-#include <linux/module.h>
+-#ifndef LINUX_VERSION_CODE
+ #include <linux/version.h>
+-#endif
+-
+-#ifndef VERSION_CODE
+-# define VERSION_CODE(vers,rel,seq) ( ((vers)<<16) | ((rel)<<8) | (seq) )
+-#endif
+-#ifndef KERNEL_VERSION
+-# define KERNEL_VERSION(a,b,c) VERSION_CODE(a,b,c)
+-#endif
+
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,0,0)
+-# error "This kernel is too old: not supported by this file"
+-#endif
+-
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0)
+-#include <linux/types.h> /* used later in this header */
+-
+-#define memcpy_fromio(a,b,c) memcpy((a),(void *)(b),(c))
+-#define memcpy_toio(a,b,c) memcpy((void *)(a),(b),(c))
+-
+-typedef struct wait_queue * wait_queue_head_t;
+-
+-#define DECLARE_WAITQUEUE(x,y) struct wait_queue x = {y,NULL}
+-#define DECLARE_WAIT_QUEUE_HEAD(x) struct wait_queue *x = NULL
+-#define init_waitqueue_head init_waitqueue
+-#define DECLARE_MUTEX(x) struct semaphore x = MUTEX
+-#define DECLARE_MUTEX_LOCKED(x) struct semaphore x = MUTEX_LOCKED
+-
+-/* from sysdep-2.1.h */
+-# include <asm/segment.h>
+-# define access_ok(t,a,sz) (verify_area((t),(a),(sz)) ? 0 : 1)
+-# define verify_area_20 verify_area
+-# define copy_to_user(t,f,n) (memcpy_tofs(t,f,n), 0)
+-# define __copy_to_user(t,f,n) copy_to_user((t),(f),(n))
+-# define copy_to_user_ret(t,f,n,r) copy_to_user((t),(f),(n))
+-# define copy_from_user(t,f,n) (memcpy_fromfs((t),(f),(n)), 0)
+-# define __copy_from_user(t,f,n) copy_from_user((t),(f),(n))
+-# define copy_from_user_ret(t,f,n,r) copy_from_user((t),(f),(n))
+-//xxx # define PUT_USER(val,add) (put_user((val),(add)), 0)
+-# define Put_user(val,add) (put_user((val),(add)), 0)
+-# define __PUT_USER(val,add) PUT_USER((val),(add))
+-# define PUT_USER_RET(val,add,ret) PUT_USER((val),(add))
+-# define GET_USER(dest,add) ((dest)=get_user((add)), 0)
+-# define __GET_USER(dest,add) GET_USER((dest),(add))
+-# define GET_USER_RET(dest,add,ret) GET_USER((dest),(add))
+-
+-#define ioremap(offset,size) vremap(offset,size)
+-#define iounmap(adr) /* */
+-
+-#define EXPORT_SYMBOL(s) /* */
+-#define EXPORT_SYMBOL_NOVERS(s) /* */
+-
+-/* 2.1.10 and 2.1.43 introduced new functions. They are worth using */
+-
+-#if LINUX_VERSION_CODE < VERSION_CODE(2,1,10)
+-
+-# include <asm/byteorder.h>
+-# ifdef __LITTLE_ENDIAN
+-# define cpu_to_le16(x) (x)
+-# define cpu_to_le32(x) (x)
+-# define cpu_to_be16(x) htons((x))
+-# define cpu_to_be32(x) htonl((x))
+-# else
+-# define cpu_to_be16(x) (x)
+-# define cpu_to_be32(x) (x)
+- extern inline __u16 cpu_to_le16(__u16 x) { return (x<<8) | (x>>8);}
+- extern inline __u32 cpu_to_le32(__u32 x) { return((x>>24) |
+- ((x>>8)&0xff00) | ((x<<8)&0xff0000) | (x<<24));}
+-# endif
+-
+-# define le16_to_cpu(x) cpu_to_le16(x)
+-# define le32_to_cpu(x) cpu_to_le32(x)
+-# define be16_to_cpu(x) cpu_to_be16(x)
+-# define be32_to_cpu(x) cpu_to_be32(x)
+-
+-#endif
+-
+-#if LINUX_VERSION_CODE < VERSION_CODE(2,1,43)
+-# define cpu_to_le16p(addr) (cpu_to_le16(*(addr)))
+-# define cpu_to_le32p(addr) (cpu_to_le32(*(addr)))
+-# define cpu_to_be16p(addr) (cpu_to_be16(*(addr)))
+-# define cpu_to_be32p(addr) (cpu_to_be32(*(addr)))
+-
+- extern inline void cpu_to_le16s(__u16 *a) {*a = cpu_to_le16(*a);}
+- extern inline void cpu_to_le32s(__u16 *a) {*a = cpu_to_le32(*a);}
+- extern inline void cpu_to_be16s(__u16 *a) {*a = cpu_to_be16(*a);}
+- extern inline void cpu_to_be32s(__u16 *a) {*a = cpu_to_be32(*a);}
+-
+-# define le16_to_cpup(x) cpu_to_le16p(x)
+-# define le32_to_cpup(x) cpu_to_le32p(x)
+-# define be16_to_cpup(x) cpu_to_be16p(x)
+-# define be32_to_cpup(x) cpu_to_be32p(x)
+-
+-# define le16_to_cpus(x) cpu_to_le16s(x)
+-# define le32_to_cpus(x) cpu_to_le32s(x)
+-# define be16_to_cpus(x) cpu_to_be16s(x)
+-# define be32_to_cpus(x) cpu_to_be32s(x)
+-#endif
+-
+-// from 2.2, linux/types.h
+-#ifndef __BIT_TYPES_DEFINED__
+-#define __BIT_TYPES_DEFINED__
+-
+-typedef __u8 u_int8_t;
+-typedef __s8 int8_t;
+-typedef __u16 u_int16_t;
+-typedef __s16 int16_t;
+-typedef __u32 u_int32_t;
+-typedef __s32 int32_t;
+-
+-#endif /* !(__BIT_TYPES_DEFINED__) */
+-
+-#if (__GNUC__ > 2) || (__GNUC__ == 2 && __GNUC_MINOR__ >= 8)
+- typedef struct { } spinlock_t;
+- #define SPIN_LOCK_UNLOCKED (spinlock_t) { }
+-#else
+- typedef struct { int gcc_is_buggy; } spinlock_t;
+- #define SPIN_LOCK_UNLOCKED (spinlock_t) { 0 }
+-#endif
+-
+-#define spin_lock_init(lock) do { } while(0)
+-#define spin_lock(lock) (void)(lock) /* Not "unused variable". */
+-#define spin_trylock(lock) (1)
+-#define spin_unlock_wait(lock) do { } while(0)
+-#define spin_unlock(lock) do { } while(0)
+-#define spin_lock_irq(lock) cli()
+-#define spin_unlock_irq(lock) sti()
+-
+-#define spin_lock_irqsave(lock, flags) \
+- do { save_flags(flags); cli(); } while (0)
+-#define spin_unlock_irqrestore(lock, flags) \
+- restore_flags(flags)
+-
+-// Doesn't work when tqueue.h is included.
+-// #define queue_task queue_task_irq_off
+-#define tty_flip_buffer_push(tty) queue_task_irq_off(&tty->flip.tqueue, &tq_timer)
+-#define signal_pending(current) (current->signal & ~current->blocked)
+-#define schedule_timeout(to) do {current->timeout = jiffies + (to);schedule ();} while (0)
+-#define time_after(t1,t2) (((long)t1-t2) > 0)
+-
+-#else
+- #include <linux/compatmac.h>
+-#endif // LINUX_VERSION_CODE < 0x020100
+-
+-
+-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
+-#include <linux/vmalloc.h>
+-#endif
+-
+-/* Modularization issues */
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,18)
+-# define __USE_OLD_SYMTAB__
+-# define EXPORT_NO_SYMBOLS register_symtab(NULL);
+-# define REGISTER_SYMTAB(tab) register_symtab(tab)
+-#else
+-# define REGISTER_SYMTAB(tab) /* nothing */
+-#endif
+-
+-#ifdef __USE_OLD_SYMTAB__
+-# define __MODULE_STRING(s) /* nothing */
+-# define MODULE_PARM(v,t) /* nothing */
+-# define MODULE_PARM_DESC(v,t) /* nothing */
+-# define MODULE_AUTHOR(n) /* nothing */
+-# define MODULE_DESCRIPTION(d) /* nothing */
+-# define MODULE_SUPPORTED_DEVICE(n) /* nothing */
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,10)
++#error "This kernel is too old: not supported by this file"
+ #endif
+
+-/*
+- * "select" changed in 2.1.23. The implementation is twin, but this
+- * header is new
+- */
+-#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,22)
+-# include <linux/poll.h>
+-#else
+-# define __USE_OLD_SELECT__
+-#endif
++ /* O(1) scheduler stuff. */
+
+-/* Other change in the fops are solved using pseudo-types */
+-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
+-# define lseek_t long long
+-# define lseek_off_t long long
+-#else
+-# define lseek_t int
+-# define lseek_off_t off_t
+-#endif
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,5) && !defined(__rh_config_h__)
++#include <linux/sched.h>
++static inline void __recalc_sigpending(void)
++{
++ recalc_sigpending(current);
++}
++#undef recalc_sigpending
++#define recalc_sigpending() __recalc_sigpending ()
+
+-/* changed the prototype of read/write */
++#define set_user_nice(tsk, n) do { (tsk)->nice = n; } while(0)
+
+-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) || defined(__alpha__)
+-# define count_t unsigned long
+-# define read_write_t long
+-#else
+-# define count_t int
+-# define read_write_t int
+ #endif
+
+
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,31)
+-# define release_t void
+-# define release_return(x) return
+-#else
+-# define release_t int
+-# define release_return(x) return (x)
+-#endif
+-
+-#if LINUX_VERSION_CODE < 0x20300
+-#define __exit
+-#endif
+-#if LINUX_VERSION_CODE < 0x20200
+-#define __init
+-#else
+-#include <linux/init.h>
+-#endif
+
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)
+-#define init_MUTEX(x) do {*(x) = MUTEX;} while (0)
+-#define init_MUTEX_LOCKED(x) do {*(x) = MUTEX_LOCKED;} while (0)
+-#endif
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,20)
+
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
+-#define RQFUNC_ARG void
+-#define blkdev_dequeue_request(req) do {CURRENT = req->next;} while (0)
+-#else
+-#define RQFUNC_ARG request_queue_t *q
++#ifndef yield
++#define yield() do { set_current_state(TASK_RUNNING); schedule(); } while(0)
+ #endif
+
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,32)
+-#define blk_cleanup_queue(nr) do {blk_dev[nr].request_fn = 0;} while(0)
+-#define BLK_DEFAULT_QUEUE(nr) (blk_dev[nr].request_fn)
+-#define blk_init_queue(q, rq) do {q = rq;} while(0)
++#ifndef minor
++#define major(d) (MAJOR(to_kdev_t(d)))
++#define minor(d) (MINOR(to_kdev_t(d)))
+ #endif
+
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,0)
+-#ifdef CONFIG_MODULES
+-#define __MOD_INC_USE_COUNT(mod) \
+- (atomic_inc(&(mod)->uc.usecount), (mod)->flags |= MOD_VISITED|MOD_USED_ONCE)
+-#define __MOD_DEC_USE_COUNT(mod) \
+- (atomic_dec(&(mod)->uc.usecount), (mod)->flags |= MOD_VISITED)
+-#else
+-#define __MOD_INC_USE_COUNT(mod)
+-#define __MOD_DEC_USE_COUNT(mod)
+-#endif
++#ifndef mk_kdev
++#define mk_kdev(ma,mi) MKDEV(ma,mi)
++#define kdev_t_to_nr(x) (x)
+ #endif
+
++#define need_resched() (current->need_resched)
++#define cond_resched() do { if need_resched() { yield(); } } while(0)
+
+-#ifndef HAVE_INTER_MODULE
+-static inline void *inter_module_get(char *x) {return NULL;}
+-static inline void *inter_module_get_request(char *x, char *y) {return NULL;}
+-static inline void inter_module_put(const char *x) {}
+-static inline void inter_module_register(const char *x, struct module *y, const void *z) {}
+-static inline void inter_module_unregister(const char *x) {}
++#endif /* < 2.4.20 */
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,73)
++#define iminor(i) minor((i)->i_rdev)
++#define imajor(i) major((i)->i_rdev)
++#define old_encode_dev(d) ( (major(d)<<8) | minor(d) )
++#define old_decode_dev(rdev) (kdev_t_to_nr(mk_kdev((rdev)>>8, (rdev)&0xff)))
++#define old_valid_dev(d) (1)
+ #endif
+
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,61)
+
+-#define DECLARE_WAIT_QUEUE_HEAD(x) struct wait_queue *x = NULL
+-#define init_waitqueue_head init_waitqueue
++#include <linux/sched.h>
+
++#ifdef __rh_config_h__
++#define sigmask_lock sighand->siglock
++#define sig sighand
+ #endif
+
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
+-
+-static inline int try_inc_mod_count(struct module *mod)
++static inline void __daemonize_modvers(void)
+ {
+-#ifdef CONFIG_MODULES
+- if (mod)
+- __MOD_INC_USE_COUNT(mod);
+-#endif
+- return 1;
+-}
+-#endif
++ daemonize();
+
++ spin_lock_irq(&current->sigmask_lock);
++ sigfillset(&current->blocked);
++ recalc_sigpending();
++ spin_unlock_irq(&current->sigmask_lock);
++}
++#undef daemonize
++#define daemonize(fmt, ...) do { \
++ snprintf(current->comm, sizeof(current->comm), fmt ,##__VA_ARGS__); \
++ __daemonize_modvers(); \
++ } while(0)
+
+-/* Yes, I'm aware that it's a fairly ugly hack.
+- Until the __constant_* macros appear in Linus' own kernels, this is
+- the way it has to be done.
+- DW 19/1/00
+- */
+-
+-#include <asm/byteorder.h>
+-
+-#ifndef __constant_cpu_to_le16
+-
+-#ifdef __BIG_ENDIAN
+-#define __constant_cpu_to_le64(x) ___swab64((x))
+-#define __constant_le64_to_cpu(x) ___swab64((x))
+-#define __constant_cpu_to_le32(x) ___swab32((x))
+-#define __constant_le32_to_cpu(x) ___swab32((x))
+-#define __constant_cpu_to_le16(x) ___swab16((x))
+-#define __constant_le16_to_cpu(x) ___swab16((x))
+-#define __constant_cpu_to_be64(x) ((__u64)(x))
+-#define __constant_be64_to_cpu(x) ((__u64)(x))
+-#define __constant_cpu_to_be32(x) ((__u32)(x))
+-#define __constant_be32_to_cpu(x) ((__u32)(x))
+-#define __constant_cpu_to_be16(x) ((__u16)(x))
+-#define __constant_be16_to_cpu(x) ((__u16)(x))
+-#else
+-#ifdef __LITTLE_ENDIAN
+-#define __constant_cpu_to_le64(x) ((__u64)(x))
+-#define __constant_le64_to_cpu(x) ((__u64)(x))
+-#define __constant_cpu_to_le32(x) ((__u32)(x))
+-#define __constant_le32_to_cpu(x) ((__u32)(x))
+-#define __constant_cpu_to_le16(x) ((__u16)(x))
+-#define __constant_le16_to_cpu(x) ((__u16)(x))
+-#define __constant_cpu_to_be64(x) ___swab64((x))
+-#define __constant_be64_to_cpu(x) ___swab64((x))
+-#define __constant_cpu_to_be32(x) ___swab32((x))
+-#define __constant_be32_to_cpu(x) ___swab32((x))
+-#define __constant_cpu_to_be16(x) ___swab16((x))
+-#define __constant_be16_to_cpu(x) ___swab16((x))
+-#else
+-#error No (recognised) endianness defined (unless it,s PDP)
+-#endif /* __LITTLE_ENDIAN */
+-#endif /* __BIG_ENDIAN */
+-
+-#endif /* ifndef __constant_cpu_to_le16 */
+-
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
+- #define mod_init_t int __init
+- #define mod_exit_t void
+-#else
+- #define mod_init_t static int __init
+- #define mod_exit_t static void __exit
+-#endif
+-
+-#ifndef THIS_MODULE
+-#ifdef MODULE
+-#define THIS_MODULE (&__this_module)
+-#else
+-#define THIS_MODULE (NULL)
+-#endif
+-#endif
+-
+-#if LINUX_VERSION_CODE < 0x20300
+-#include <linux/interrupt.h>
+-#define spin_lock_bh(lock) do {start_bh_atomic();spin_lock(lock);}while(0)
+-#define spin_unlock_bh(lock) do {spin_unlock(lock);end_bh_atomic();}while(0)
+-#else
+-#include <asm/softirq.h>
+-#include <linux/spinlock.h>
+-#endif
+-
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)
+-#define set_current_state(state_value) \
+- do { current->state = (state_value); } while (0)
+-#endif
+-
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,0)
+-static inline int invalidate_device(kdev_t dev, int do_sync) {
++static inline int dequeue_signal_lock(struct task_struct *tsk, sigset_t *mask, siginfo_t *info)
++{
++ unsigned long flags;
++ unsigned long ret;
+
+- if (do_sync)
+- fsync_dev(dev);
++ spin_lock_irqsave(&current->sigmask_lock, flags);
++ ret = dequeue_signal(mask, info);
++ spin_unlock_irqrestore(&current->sigmask_lock, flags);
+
+- invalidate_buffers(dev);
+- return 0;
++ return ret;
+ }
+-#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,4,5)
+-static inline int invalidate_device(kdev_t dev, int do_sync) {
+- struct super_block *sb = get_super(dev);
+- int res = 0;
+
+- if (do_sync)
+- fsync_dev(dev);
+-
+- if (sb)
+- res = invalidate_inodes(sb);
++static inline int allow_signal(int sig)
++{
++ if (sig < 1 || sig > _NSIG)
++ return -EINVAL;
+
+- invalidate_buffers(dev);
+- return res;
++ spin_lock_irq(&current->sigmask_lock);
++ sigdelset(&current->blocked, sig);
++ recalc_sigpending();
++ /* Make sure the kernel neither eats it now converts to SIGKILL */
++ current->sig->action[sig-1].sa.sa_handler = (void *)2;
++ spin_unlock_irq(&current->sigmask_lock);
++ return 0;
+ }
+-#endif
+-
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,10)
+-#undef min
+-#undef max
+-#undef min_t
+-#undef max_t
+-/*
+- * min()/max() macros that also do
+- * strict type-checking.. See the
+- * "unnecessary" pointer comparison.
+- */
+-#define min(x,y) ({ \
+- const typeof(x) _x = (x); \
+- const typeof(y) _y = (y); \
+- (void) (&_x == &_y); \
+- _x < _y ? _x : _y; })
+-
+-#define max(x,y) ({ \
+- const typeof(x) _x = (x); \
+- const typeof(y) _y = (y); \
+- (void) (&_x == &_y); \
+- _x > _y ? _x : _y; })
+-
+-/*
+- * ..and if you can't take the strict
+- * types, you can specify one yourself.
+- *
+- * Or not use min/max at all, of course.
+- */
+-#define min_t(type,x,y) \
+- ({ type __x = (x); type __y = (y); __x < __y ? __x: __y; })
+-#define max_t(type,x,y) \
+- ({ type __x = (x); type __y = (y); __x > __y ? __x: __y; })
+-#endif
+-
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,7)
+-struct completion {
+- struct semaphore s;
+-};
+-
+-#define complete(c) up(&(c)->s)
+-#define wait_for_completion(c) down(&(c)->s)
+-#define init_completion(c) init_MUTEX_LOCKED(&(c)->s);
+-
+-#endif
+-
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,9)
+-/* This came later */
+-#define complete_and_exit(c, r) do { complete(c); do_exit(r); } while(0)
+-#endif
+-
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,9) || \
+- (LINUX_VERSION_CODE < KERNEL_VERSION(2,4,10) && !defined(__rh_config_h__))
++static inline int disallow_signal(int sig)
++{
++ if (sig < 1 || sig > _NSIG)
++ return -EINVAL;
+
+-#include <linux/genhd.h>
++ spin_lock_irq(&current->sigmask_lock);
++ sigaddset(&current->blocked, sig);
++ recalc_sigpending();
+
+-static inline void add_gendisk(struct gendisk *gp)
+-{
+- gp->next = gendisk_head;
+- gendisk_head = gp;
++ current->sig->action[sig-1].sa.sa_handler = SIG_DFL;
++ spin_unlock_irq(&current->sigmask_lock);
++ return 0;
+ }
+
+-static inline void del_gendisk(struct gendisk *gp)
+-{
+- struct gendisk *gd, **gdp;
++#undef sighand
++#undef sigmask_lock
+
+- for (gdp = &gendisk_head; *gdp; gdp = &((*gdp)->next))
+- if (*gdp == gp) {
+- gd = *gdp; *gdp = gd->next;
+- break;
+- }
+-}
++#define PF_FREEZE 0
++#define refrigerator(x) do { ; } while(0)
+ #endif
+
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18) && defined(MODULE)
++ /* Module bits */
+
+-#define module_init(func) \
+-mod_init_t init_module(void) { \
+- return func(); \
+-}
+
+-#define module_exit(func) \
+-mod_exit_t cleanup_module(void) { \
+- return func(); \
+-}
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,60)
++#define try_module_get(m) try_inc_mod_count(m)
++#define __module_get(m) do { if (!try_inc_mod_count(m)) BUG(); } while(0)
++#define module_put(m) do { if (m) __MOD_DEC_USE_COUNT((struct module *)(m)); } while(0)
++#define set_module_owner(x) do { x->owner = THIS_MODULE; } while(0)
+ #endif
+
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,9) || \
+- (LINUX_VERSION_CODE < KERNEL_VERSION(2,4,10) && !defined(__rh_config_h__))
+-#define MODULE_LICENSE(x) /* */
+-#endif
+
+-/* Removed for 2.4.21 kernel. This really should have been renamed
+- when it was changed -- this is a PITA */
+-#if 0 && LINUX_VERSION_CODE < KERNEL_VERSION(2,5,5)
+-#include <linux/sched.h>
+-static inline void __recalc_sigpending(void)
+-{
+- recalc_sigpending(current);
+-}
+-#undef recalc_sigpending
+-#define recalc_sigpending() __recalc_sigpending ()
+-#endif
++ /* Random filesystem stuff, only for JFFS2 really */
+
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,5)
+ #define parent_ino(d) ((d)->d_parent->d_inode->i_ino)
+ #endif
+
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,3)
+-#define need_resched() (current->need_resched)
+-#define cond_resched() do { if need_resched() schedule(); } while(0)
+-#endif
+-
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,19)
+-#ifndef yield
+-#define yield() do { set_current_state(TASK_RUNNING); schedule(); } while(0)
+-#endif
+-#ifndef minor
+-#define major(d) (MAJOR(to_kdev_t(d)))
+-#define minor(d) (MINOR(to_kdev_t(d)))
+-#endif
+-#ifndef mk_kdev
+-#define mk_kdev(ma,mi) MKDEV(ma,mi)
+-#define kdev_t_to_nr(x) (x)
+-#endif
+-#endif
+-
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
+- /* Is this right? */
+-#define set_user_nice(tsk, n) do { (tsk)->priority = 20-(n); } while(0)
+-#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,4,21) && !defined(RED_HAT_LINUX_KERNEL)
+-#define set_user_nice(tsk, n) do { (tsk)->nice = n; } while(0)
+-#endif
+-
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,21)
+-#define rq_data_dir(x) ((x)->cmd)
+-#endif
+-
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+-
+-#define IS_REQ_CMD(req) (1)
+-
+-#define QUEUE_LOCK(q) (&io_request_lock)
+-
+-#define BLK_INIT_QUEUE(q, req, lock) blk_init_queue((q), (req))
+-
+-#else /* > 2.5.0 */
+-
+-#define IS_REQ_CMD(req) ((req)->flags & REQ_CMD)
+-
+-#define QUEUE_LOCK(q) ((q)->queue_lock)
+-
+-#define BLK_INIT_QUEUE(q, req, lock) blk_init_queue((q), (req), (lock))
+-
+-#endif
+-
+-/* Removed cos it broke stuff. Where is this required anyway?
+- * #ifndef QUEUE_EMPTY
+- * #define QUEUE_EMPTY (!CURRENT)
+- * #endif
+- */
+-#if LINUX_VERSION_CODE < 0x20300
+-#define QUEUE_PLUGGED (blk_dev[MAJOR_NR].plug_tq.sync)
+-#elif LINUX_VERSION_CODE < 0x20500 //FIXME (Si)
+-#define QUEUE_PLUGGED (blk_dev[MAJOR_NR].request_queue.plugged)
+-#else
+-#define QUEUE_PLUGGED (blk_queue_plugged(QUEUE))
+-#endif
+-
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,14)
+-#define BLK_INC_USE_COUNT MOD_INC_USE_COUNT
+-#define BLK_DEC_USE_COUNT MOD_DEC_USE_COUNT
+-#else
+-#define BLK_INC_USE_COUNT do {} while(0)
+-#define BLK_DEC_USE_COUNT do {} while(0)
+-#endif
+-
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,12)
+ #define PageUptodate(x) Page_Uptodate(x)
+ #endif
+@@ -580,4 +159,31 @@
+ #define generic_file_readonly_mmap generic_file_mmap
+ #endif
+
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,70)
++
++#include <linux/kmod.h>
++#include <linux/string.h>
++
++static inline char *strlcpy(char *dest, const char *src, int len)
++{
++ dest[len-1] = 0;
++ return strncpy(dest, src, len-1);
++}
++
++static inline int do_old_request_module(const char *mod)
++{
++ return request_module(mod);
++}
++#undef request_module
++#define request_module(fmt, ...) \
++ ({ char modname[32]; snprintf(modname, 31, fmt ,##__VA_ARGS__); do_old_request_module(modname); })
++
++#endif /* 2.5.70 */
++
++#ifndef container_of
++#define container_of(ptr, type, member) ({ \
++ const typeof( ((type *)0)->member ) *__mptr = (ptr); \
++ (type *)( (char *)__mptr - offsetof(type,member) );})
++#endif
++
+ #endif /* __LINUX_MTD_COMPATMAC_H__ */
+diff -Nurb linux-mips-2.4.24-pre2/include/linux/mtd/doc2000.h linux/include/linux/mtd/doc2000.h
+--- linux-mips-2.4.24-pre2/include/linux/mtd/doc2000.h 2004-11-17 18:05:09.000000000 +0100
++++ linux/include/linux/mtd/doc2000.h 2004-11-17 18:17:59.212287624 +0100
+@@ -1,13 +1,21 @@
+-
+-/* Linux driver for Disk-On-Chip 2000 */
+-/* (c) 1999 Machine Vision Holdings, Inc. */
+-/* Author: David Woodhouse <dwmw2@mvhi.com> */
+-/* $Id$ */
++/*
++ * Linux driver for Disk-On-Chip devices
++ *
++ * Copyright (C) 1999 Machine Vision Holdings, Inc.
++ * Copyright (C) 2001-2003 David Woodhouse <dwmw2@infradead.org>
++ * Copyright (C) 2002-2003 Greg Ungerer <gerg@snapgear.com>
++ * Copyright (C) 2002-2003 SnapGear Inc
++ *
++ * $Id$
++ *
++ * Released under GPL
++ */
+
+ #ifndef __MTD_DOC2000_H__
+ #define __MTD_DOC2000_H__
+
+ #include <linux/mtd/mtd.h>
++#include <asm/semaphore.h>
+
+ #define DoC_Sig1 0
+ #define DoC_Sig2 1
+@@ -38,18 +46,47 @@
+ #define DoC_Mil_CDSN_IO 0x0800
+ #define DoC_2k_CDSN_IO 0x1800
+
++#define DoC_Mplus_NOP 0x1002
++#define DoC_Mplus_AliasResolution 0x1004
++#define DoC_Mplus_DOCControl 0x1006
++#define DoC_Mplus_AccessStatus 0x1008
++#define DoC_Mplus_DeviceSelect 0x1008
++#define DoC_Mplus_Configuration 0x100a
++#define DoC_Mplus_OutputControl 0x100c
++#define DoC_Mplus_FlashControl 0x1020
++#define DoC_Mplus_FlashSelect 0x1022
++#define DoC_Mplus_FlashCmd 0x1024
++#define DoC_Mplus_FlashAddress 0x1026
++#define DoC_Mplus_FlashData0 0x1028
++#define DoC_Mplus_FlashData1 0x1029
++#define DoC_Mplus_ReadPipeInit 0x102a
++#define DoC_Mplus_LastDataRead 0x102c
++#define DoC_Mplus_LastDataRead1 0x102d
++#define DoC_Mplus_WritePipeTerm 0x102e
++#define DoC_Mplus_ECCSyndrome0 0x1040
++#define DoC_Mplus_ECCSyndrome1 0x1041
++#define DoC_Mplus_ECCSyndrome2 0x1042
++#define DoC_Mplus_ECCSyndrome3 0x1043
++#define DoC_Mplus_ECCSyndrome4 0x1044
++#define DoC_Mplus_ECCSyndrome5 0x1045
++#define DoC_Mplus_ECCConf 0x1046
++#define DoC_Mplus_Toggle 0x1046
++#define DoC_Mplus_DownloadStatus 0x1074
++#define DoC_Mplus_CtrlConfirm 0x1076
++#define DoC_Mplus_Power 0x1fff
++
+ /* How to access the device?
+ * On ARM, it'll be mmap'd directly with 32-bit wide accesses.
+ * On PPC, it's mmap'd and 16-bit wide.
+ * Others use readb/writeb
+ */
+ #if defined(__arm__)
+-#define ReadDOC_(adr, reg) ((unsigned char)(*(__u32 *)(((unsigned long)adr)+((reg)<<2))))
+-#define WriteDOC_(d, adr, reg) do{ *(__u32 *)(((unsigned long)adr)+((reg)<<2)) = (__u32)d; wmb();} while(0)
++#define ReadDOC_(adr, reg) ((unsigned char)(*(volatile __u32 *)(((unsigned long)adr)+((reg)<<2))))
++#define WriteDOC_(d, adr, reg) do{ *(volatile __u32 *)(((unsigned long)adr)+((reg)<<2)) = (__u32)d; wmb();} while(0)
+ #define DOC_IOREMAP_LEN 0x8000
+ #elif defined(__ppc__)
+-#define ReadDOC_(adr, reg) ((unsigned char)(*(__u16 *)(((unsigned long)adr)+((reg)<<1))))
+-#define WriteDOC_(d, adr, reg) do{ *(__u16 *)(((unsigned long)adr)+((reg)<<1)) = (__u16)d; wmb();} while(0)
++#define ReadDOC_(adr, reg) ((unsigned char)(*(volatile __u16 *)(((unsigned long)adr)+((reg)<<1))))
++#define WriteDOC_(d, adr, reg) do{ *(volatile __u16 *)(((unsigned long)adr)+((reg)<<1)) = (__u16)d; wmb();} while(0)
+ #define DOC_IOREMAP_LEN 0x4000
+ #else
+ #define ReadDOC_(adr, reg) readb(((unsigned long)adr) + (reg))
+@@ -71,13 +108,21 @@
+ #define DOC_MODE_RESERVED1 2
+ #define DOC_MODE_RESERVED2 3
+
+-#define DOC_MODE_MDWREN 4
+ #define DOC_MODE_CLR_ERR 0x80
++#define DOC_MODE_RST_LAT 0x10
++#define DOC_MODE_BDECT 0x08
++#define DOC_MODE_MDWREN 0x04
+
+ #define DOC_ChipID_Doc2k 0x20
++#define DOC_ChipID_Doc2kTSOP 0x21 /* internal number for MTD */
+ #define DOC_ChipID_DocMil 0x30
++#define DOC_ChipID_DocMilPlus32 0x40
++#define DOC_ChipID_DocMilPlus16 0x41
+
+ #define CDSN_CTRL_FR_B 0x80
++#define CDSN_CTRL_FR_B0 0x40
++#define CDSN_CTRL_FR_B1 0x80
++
+ #define CDSN_CTRL_ECC_IO 0x20
+ #define CDSN_CTRL_FLASH_IO 0x10
+ #define CDSN_CTRL_WP 0x08
+@@ -93,6 +138,10 @@
+ #define DOC_ECC_RESV 0x02
+ #define DOC_ECC_IGNORE 0x01
+
++#define DOC_FLASH_CE 0x80
++#define DOC_FLASH_WP 0x40
++#define DOC_FLASH_BANK 0x02
++
+ /* We have to also set the reserved bit 1 for enable */
+ #define DOC_ECC_EN (DOC_ECC__EN | DOC_ECC_RESV)
+ #define DOC_ECC_DIS (DOC_ECC_RESV)
+@@ -107,9 +156,12 @@
+ #define MAX_FLOORS 4
+ #define MAX_CHIPS 4
+
+-#define MAX_FLOORS_MIL 4
++#define MAX_FLOORS_MIL 1
+ #define MAX_CHIPS_MIL 1
+
++#define MAX_FLOORS_MPLUS 2
++#define MAX_CHIPS_MPLUS 1
++
+ #define ADDR_COLUMN 1
+ #define ADDR_PAGE 2
+ #define ADDR_COLUMN_PAGE 3
+@@ -118,7 +170,7 @@
+ unsigned long physadr;
+ unsigned long virtadr;
+ unsigned long totlen;
+- char ChipID; /* Type of DiskOnChip */
++ unsigned char ChipID; /* Type of DiskOnChip */
+ int ioreg;
+
+ unsigned long mfr; /* Flash IDs - only one type of flash per device */
+@@ -126,6 +178,7 @@
+ int chipshift;
+ char page256;
+ char pageadrlen;
++ char interleave; /* Internal interleaving - Millennium Plus style */
+ unsigned long erasesize;
+
+ int curfloor;
+diff -Nurb linux-mips-2.4.24-pre2/include/linux/mtd/flashchip.h linux/include/linux/mtd/flashchip.h
+--- linux-mips-2.4.24-pre2/include/linux/mtd/flashchip.h 2004-11-17 18:05:09.000000000 +0100
++++ linux/include/linux/mtd/flashchip.h 2004-11-17 18:17:59.214287320 +0100
+@@ -6,7 +6,7 @@
+ *
+ * (C) 2000 Red Hat. GPLd.
+ *
+- * $Id$
++ * $Id$
+ *
+ */
+
+@@ -58,6 +58,11 @@
+ int ref_point_counter;
+ flstate_t state;
+ flstate_t oldstate;
++
++ int write_suspended:1;
++ int erase_suspended:1;
++ unsigned long in_progress_block_addr;
++
+ spinlock_t *mutex;
+ spinlock_t _spinlock; /* We do it like this because sometimes they'll be shared. */
+ wait_queue_head_t wq; /* Wait on here when we're waiting for the chip
+diff -Nurb linux-mips-2.4.24-pre2/include/linux/mtd/gen_probe.h linux/include/linux/mtd/gen_probe.h
+--- linux-mips-2.4.24-pre2/include/linux/mtd/gen_probe.h 2004-11-17 18:05:09.000000000 +0100
++++ linux/include/linux/mtd/gen_probe.h 2004-11-17 18:17:59.216287016 +0100
+@@ -1,7 +1,7 @@
+ /*
+ * (C) 2001, 2001 Red Hat, Inc.
+ * GPL'd
+- * $Id$
++ * $Id$
+ */
+
+ #ifndef __LINUX_MTD_GEN_PROBE_H__
+@@ -10,12 +10,12 @@
+ #include <linux/mtd/flashchip.h>
+ #include <linux/mtd/map.h>
+ #include <linux/mtd/cfi.h>
++#include <asm/bitops.h>
+
+ struct chip_probe {
+ char *name;
+ int (*probe_chip)(struct map_info *map, __u32 base,
+- struct flchip *chips, struct cfi_private *cfi);
+-
++ unsigned long *chip_map, struct cfi_private *cfi);
+ };
+
+ struct mtd_info *mtd_do_chip_probe(struct map_info *map, struct chip_probe *cp);
+diff -Nurb linux-mips-2.4.24-pre2/include/linux/mtd/inftl.h linux/include/linux/mtd/inftl.h
+--- linux-mips-2.4.24-pre2/include/linux/mtd/inftl.h 1970-01-01 01:00:00.000000000 +0100
++++ linux/include/linux/mtd/inftl.h 2004-11-17 18:17:59.219286560 +0100
+@@ -0,0 +1,129 @@
++/*
++ * inftl.h -- defines to support the Inverse NAND Flash Translation Layer
++ *
++ * (C) Copyright 2002, Greg Ungerer (gerg@snapgear.com)
++ *
++ * $Id$
++ */
++
++#ifndef __MTD_INFTL_H__
++#define __MTD_INFTL_H__
++
++#include <linux/mtd/blktrans.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/nftl.h>
++
++#define OSAK_VERSION 0x5120
++#define PERCENTUSED 98
++
++#define SECTORSIZE 512
++
++#ifndef INFTL_MAJOR
++#define INFTL_MAJOR 94
++#endif
++#define INFTL_PARTN_BITS 4
++
++/* Block Control Information */
++
++struct inftl_bci {
++ __u8 ECCsig[6];
++ __u8 Status;
++ __u8 Status1;
++} __attribute__((packed));
++
++struct inftl_unithead1 {
++ __u16 virtualUnitNo;
++ __u16 prevUnitNo;
++ __u8 ANAC;
++ __u8 NACs;
++ __u8 parityPerField;
++ __u8 discarded;
++} __attribute__((packed));
++
++struct inftl_unithead2 {
++ __u8 parityPerField;
++ __u8 ANAC;
++ __u16 prevUnitNo;
++ __u16 virtualUnitNo;
++ __u8 NACs;
++ __u8 discarded;
++} __attribute__((packed));
++
++struct inftl_unittail {
++ __u8 Reserved[4];
++ __u16 EraseMark;
++ __u16 EraseMark1;
++} __attribute__((packed));
++
++union inftl_uci {
++ struct inftl_unithead1 a;
++ struct inftl_unithead2 b;
++ struct inftl_unittail c;
++};
++
++struct inftl_oob {
++ struct inftl_bci b;
++ union inftl_uci u;
++};
++
++
++/* INFTL Media Header */
++
++struct INFTLPartition {
++ __u32 virtualUnits;
++ __u32 firstUnit;
++ __u32 lastUnit;
++ __u32 flags;
++ __u32 spareUnits;
++ __u32 Reserved0;
++ __u32 Reserved1;
++} __attribute__((packed));
++
++struct INFTLMediaHeader {
++ char bootRecordID[8];
++ __u32 NoOfBootImageBlocks;
++ __u32 NoOfBinaryPartitions;
++ __u32 NoOfBDTLPartitions;
++ __u32 BlockMultiplierBits;
++ __u32 FormatFlags;
++ __u32 OsakVersion;
++ __u32 PercentUsed;
++ struct INFTLPartition Partitions[4];
++} __attribute__((packed));
++
++/* Partition flag types */
++#define INFTL_BINARY 0x20000000
++#define INFTL_BDTL 0x40000000
++#define INFTL_LAST 0x80000000
++
++
++#ifdef __KERNEL__
++
++struct INFTLrecord {
++ struct mtd_blktrans_dev mbd;
++ __u16 MediaUnit, SpareMediaUnit;
++ __u32 EraseSize;
++ struct INFTLMediaHeader MediaHdr;
++ int usecount;
++ unsigned char heads;
++ unsigned char sectors;
++ unsigned short cylinders;
++ __u16 numvunits;
++ __u16 firstEUN;
++ __u16 lastEUN;
++ __u16 numfreeEUNs;
++ __u16 LastFreeEUN; /* To speed up finding a free EUN */
++ int head,sect,cyl;
++ __u16 *PUtable; /* Physical Unit Table */
++ __u16 *VUtable; /* Virtual Unit Table */
++ unsigned int nb_blocks; /* number of physical blocks */
++ unsigned int nb_boot_blocks; /* number of blocks used by the bios */
++ struct erase_info instr;
++};
++
++int INFTL_mount(struct INFTLrecord *s);
++int INFTL_formatblock(struct INFTLrecord *s, int block);
++
++#endif /* __KERNEL__ */
++
++#endif /* __MTD_INFTL_H__ */
+diff -Nurb linux-mips-2.4.24-pre2/include/linux/mtd/jedec.h linux/include/linux/mtd/jedec.h
+--- linux-mips-2.4.24-pre2/include/linux/mtd/jedec.h 2004-11-17 18:05:09.000000000 +0100
++++ linux/include/linux/mtd/jedec.h 2004-11-17 18:17:59.220286408 +0100
+@@ -7,14 +7,13 @@
+ *
+ * See the AMD flash databook for information on how to operate the interface.
+ *
+- * $Id$
++ * $Id$
+ */
+
+ #ifndef __LINUX_MTD_JEDEC_H__
+ #define __LINUX_MTD_JEDEC_H__
+
+ #include <linux/types.h>
+-#include <linux/mtd/map.h>
+
+ #define MAX_JEDEC_CHIPS 16
+
+diff -Nurb linux-mips-2.4.24-pre2/include/linux/mtd/map.h linux/include/linux/mtd/map.h
+--- linux-mips-2.4.24-pre2/include/linux/mtd/map.h 2004-11-17 18:05:09.000000000 +0100
++++ linux/include/linux/mtd/map.h 2004-11-17 18:17:59.221286256 +0100
+@@ -1,14 +1,15 @@
+
+ /* Overhauled routines for dealing with different mmap regions of flash */
+-/* $Id$ */
++/* $Id$ */
+
+ #ifndef __LINUX_MTD_MAP_H__
+ #define __LINUX_MTD_MAP_H__
+
+ #include <linux/config.h>
+ #include <linux/types.h>
+-#include <linux/mtd/mtd.h>
+-#include <linux/slab.h>
++#include <linux/list.h>
++#include <asm/system.h>
++#include <asm/io.h>
+
+ /* The map stuff is very simple. You fill in your struct map_info with
+ a handful of routines for accessing the device, making sure they handle
+@@ -29,39 +30,44 @@
+ struct map_info {
+ char *name;
+ unsigned long size;
++ unsigned long phys;
++#define NO_XIP (-1UL)
++
++ unsigned long virt;
++ void *cached;
++
+ int buswidth; /* in octets */
+- __u8 (*read8)(struct map_info *, unsigned long);
+- __u16 (*read16)(struct map_info *, unsigned long);
+- __u32 (*read32)(struct map_info *, unsigned long);
+- __u64 (*read64)(struct map_info *, unsigned long);
++
++#ifdef CONFIG_MTD_COMPLEX_MAPPINGS
++ u8 (*read8)(struct map_info *, unsigned long);
++ u16 (*read16)(struct map_info *, unsigned long);
++ u32 (*read32)(struct map_info *, unsigned long);
++ u64 (*read64)(struct map_info *, unsigned long);
+ /* If it returned a 'long' I'd call it readl.
+ * It doesn't.
+ * I won't.
+ * dwmw2 */
+
+ void (*copy_from)(struct map_info *, void *, unsigned long, ssize_t);
+- void (*write8)(struct map_info *, __u8, unsigned long);
+- void (*write16)(struct map_info *, __u16, unsigned long);
+- void (*write32)(struct map_info *, __u32, unsigned long);
+- void (*write64)(struct map_info *, __u64, unsigned long);
++ void (*write8)(struct map_info *, u8, unsigned long);
++ void (*write16)(struct map_info *, u16, unsigned long);
++ void (*write32)(struct map_info *, u32, unsigned long);
++ void (*write64)(struct map_info *, u64, unsigned long);
+ void (*copy_to)(struct map_info *, unsigned long, const void *, ssize_t);
+
+- u_char * (*point) (struct map_info *, loff_t, size_t);
+- void (*unpoint) (struct map_info *, u_char *, loff_t, size_t);
+-
++ /* We can perhaps put in 'point' and 'unpoint' methods, if we really
++ want to enable XIP for non-linear mappings. Not yet though. */
++#endif
++ /* set_vpp() must handle being reentered -- enable, enable, disable
++ must leave it enabled. */
+ void (*set_vpp)(struct map_info *, int);
+- /* We put these two here rather than a single void *map_priv,
+- because we want mappers to be able to have quickly-accessible
+- cache for the 'currently-mapped page' without the _extra_
+- redirection that would be necessary. If you need more than
+- two longs, turn the second into a pointer. dwmw2 */
++
+ unsigned long map_priv_1;
+ unsigned long map_priv_2;
+ void *fldrv_priv;
+ struct mtd_chip_driver *fldrv;
+ };
+
+-
+ struct mtd_chip_driver {
+ struct mtd_info *(*probe)(struct map_info *map);
+ void (*destroy)(struct mtd_info *);
+@@ -74,26 +80,93 @@
+ void unregister_mtd_chip_driver(struct mtd_chip_driver *);
+
+ struct mtd_info *do_map_probe(const char *name, struct map_info *map);
++void map_destroy(struct mtd_info *mtd);
++
++#define ENABLE_VPP(map) do { if(map->set_vpp) map->set_vpp(map, 1); } while(0)
++#define DISABLE_VPP(map) do { if(map->set_vpp) map->set_vpp(map, 0); } while(0)
++
++#ifdef CONFIG_MTD_COMPLEX_MAPPINGS
++#define map_read8(map, ofs) (map)->read8(map, ofs)
++#define map_read16(map, ofs) (map)->read16(map, ofs)
++#define map_read32(map, ofs) (map)->read32(map, ofs)
++#define map_read64(map, ofs) (map)->read64(map, ofs)
++#define map_copy_from(map, to, from, len) (map)->copy_from(map, to, from, len)
++#define map_write8(map, datum, ofs) (map)->write8(map, datum, ofs)
++#define map_write16(map, datum, ofs) (map)->write16(map, datum, ofs)
++#define map_write32(map, datum, ofs) (map)->write32(map, datum, ofs)
++#define map_write64(map, datum, ofs) (map)->write64(map, datum, ofs)
++#define map_copy_to(map, to, from, len) (map)->copy_to(map, to, from, len)
+
++extern void simple_map_init(struct map_info *);
++#define map_is_linear(map) (map->phys != NO_XIP)
+
+-/*
+- * Destroy an MTD device which was created for a map device.
+- * Make sure the MTD device is already unregistered before calling this
+- */
+-static inline void map_destroy(struct mtd_info *mtd)
+-{
+- struct map_info *map = mtd->priv;
+-
+- if (map->fldrv->destroy)
+- map->fldrv->destroy(mtd);
+-#ifdef CONFIG_MODULES
+- if (map->fldrv->module)
+- __MOD_DEC_USE_COUNT(map->fldrv->module);
++#else
++static inline u8 map_read8(struct map_info *map, unsigned long ofs)
++{
++ return __raw_readb(map->virt + ofs);
++}
++
++static inline u16 map_read16(struct map_info *map, unsigned long ofs)
++{
++ return __raw_readw(map->virt + ofs);
++}
++
++static inline u32 map_read32(struct map_info *map, unsigned long ofs)
++{
++ return __raw_readl(map->virt + ofs);
++}
++
++static inline u64 map_read64(struct map_info *map, unsigned long ofs)
++{
++#ifndef CONFIG_MTD_CFI_B8 /* 64-bit mappings */
++ BUG();
++ return 0;
++#else
++ return __raw_readll(map->virt + ofs);
+ #endif
+- kfree(mtd);
+ }
+
+-#define ENABLE_VPP(map) do { if(map->set_vpp) map->set_vpp(map, 1); } while(0)
+-#define DISABLE_VPP(map) do { if(map->set_vpp) map->set_vpp(map, 0); } while(0)
++static inline void map_write8(struct map_info *map, u8 datum, unsigned long ofs)
++{
++ __raw_writeb(datum, map->virt + ofs);
++ mb();
++}
++
++static inline void map_write16(struct map_info *map, u16 datum, unsigned long ofs)
++{
++ __raw_writew(datum, map->virt + ofs);
++ mb();
++}
++
++static inline void map_write32(struct map_info *map, u32 datum, unsigned long ofs)
++{
++ __raw_writel(datum, map->virt + ofs);
++ mb();
++}
++
++static inline void map_write64(struct map_info *map, u64 datum, unsigned long ofs)
++{
++#ifndef CONFIG_MTD_CFI_B8 /* 64-bit mappings */
++ BUG();
++#else
++ __raw_writell(datum, map->virt + ofs);
++ mb();
++#endif /* CFI_B8 */
++}
++
++static inline void map_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
++{
++ memcpy_fromio(to, map->virt + from, len);
++}
++
++static inline void map_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
++{
++ memcpy_toio(map->virt + to, from, len);
++}
++
++#define simple_map_init(map) do { } while (0)
++#define map_is_linear(map) (1)
++
++#endif /* !CONFIG_MTD_COMPLEX_MAPPINGS */
+
+ #endif /* __LINUX_MTD_MAP_H__ */
+diff -Nurb linux-mips-2.4.24-pre2/include/linux/mtd/mtd.h linux/include/linux/mtd/mtd.h
+--- linux-mips-2.4.24-pre2/include/linux/mtd/mtd.h 2004-11-17 18:05:09.000000000 +0100
++++ linux/include/linux/mtd/mtd.h 2004-11-17 18:17:59.234284280 +0100
+@@ -1,5 +1,10 @@
+-
+-/* $Id$ */
++/*
++ * $Id$
++ *
++ * Copyright (C) 1999-2003 David Woodhouse <dwmw2@infradead.org> et al.
++ *
++ * Released under GPL
++ */
+
+ #ifndef __MTD_MTD_H__
+ #define __MTD_MTD_H__
+@@ -9,7 +14,6 @@
+ #include <linux/config.h>
+ #include <linux/version.h>
+ #include <linux/types.h>
+-#include <linux/mtd/compatmac.h>
+ #include <linux/module.h>
+ #include <linux/uio.h>
+
+@@ -26,7 +30,6 @@
+ unsigned char *ptr;
+ };
+
+-
+ #define MTD_CHAR_MAJOR 90
+ #define MTD_BLOCK_MAJOR 31
+ #define MAX_MTD_DEVICES 16
+@@ -93,18 +96,23 @@
+ #define MEMUNLOCK _IOW('M', 6, struct erase_info_user)
+ #define MEMGETREGIONCOUNT _IOR('M', 7, int)
+ #define MEMGETREGIONINFO _IOWR('M', 8, struct region_info_user)
+-#define MEMREADDATA _IOWR('M', 9, struct mtd_oob_buf)
+-#define MEMWRITEDATA _IOWR('M', 10, struct mtd_oob_buf)
++#define MEMSETOOBSEL _IOW('M', 9, struct nand_oobinfo)
++
++struct nand_oobinfo {
++ int useecc;
++ int eccpos[6];
++};
++
+
+ #ifndef __KERNEL__
+
+ typedef struct mtd_info_user mtd_info_t;
+ typedef struct erase_info_user erase_info_t;
+ typedef struct region_info_user region_info_t;
++typedef struct nand_oobinfo nand_oobinfo_t;
+
+ /* User-space ioctl definitions */
+
+-
+ #else /* __KERNEL__ */
+
+
+@@ -150,10 +158,14 @@
+ u_int32_t ecctype;
+ u_int32_t eccsize;
+
++
+ // Kernel-only stuff starts here.
+ char *name;
+ int index;
+
++ // oobinfo is a nand_oobinfo structure, which can be set by iotcl (MEMSETOOBINFO)
++ struct nand_oobinfo oobinfo;
++
+ /* Data for variable erase regions. If numeraseregions is zero,
+ * it means that the whole device has erasesize as given above.
+ */
+@@ -163,7 +175,6 @@
+ /* This really shouldn't be here. It can go away in 2.5 */
+ u_int32_t bank_size;
+
+- struct module *module;
+ int (*erase) (struct mtd_info *mtd, struct erase_info *instr);
+
+ /* This stuff for eXecute-In-Place */
+@@ -176,8 +187,8 @@
+ int (*read) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
+ int (*write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);
+
+- int (*read_ecc) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf, u_char *eccbuf, int oobsel);
+- int (*write_ecc) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf, u_char *eccbuf, int oobsel);
++ int (*read_ecc) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf, u_char *eccbuf, struct nand_oobinfo *oobsel);
++ int (*write_ecc) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf, u_char *eccbuf, struct nand_oobinfo *oobsel);
+
+ int (*read_oob) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
+ int (*write_oob) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);
+@@ -201,10 +212,10 @@
+ */
+ int (*readv) (struct mtd_info *mtd, struct iovec *vecs, unsigned long count, loff_t from, size_t *retlen);
+ int (*readv_ecc) (struct mtd_info *mtd, struct iovec *vecs, unsigned long count, loff_t from,
+- size_t *retlen, u_char *eccbuf, int oobsel);
++ size_t *retlen, u_char *eccbuf, struct nand_oobinfo *oobsel);
+ int (*writev) (struct mtd_info *mtd, const struct iovec *vecs, unsigned long count, loff_t to, size_t *retlen);
+ int (*writev_ecc) (struct mtd_info *mtd, const struct iovec *vecs, unsigned long count, loff_t to,
+- size_t *retlen, u_char *eccbuf, int oobsel);
++ size_t *retlen, u_char *eccbuf, struct nand_oobinfo *oobsel);
+
+ /* Sync */
+ void (*sync) (struct mtd_info *mtd);
+@@ -218,6 +229,9 @@
+ void (*resume) (struct mtd_info *mtd);
+
+ void *priv;
++
++ struct module *owner;
++ int usecount;
+ };
+
+
+@@ -226,31 +240,15 @@
+ extern int add_mtd_device(struct mtd_info *mtd);
+ extern int del_mtd_device (struct mtd_info *mtd);
+
+-extern struct mtd_info *__get_mtd_device(struct mtd_info *mtd, int num);
+-
+-static inline struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num)
+-{
+- struct mtd_info *ret;
++extern struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num);
+
+- ret = __get_mtd_device(mtd, num);
+-
+- if (ret && ret->module && !try_inc_mod_count(ret->module))
+- return NULL;
+-
+- return ret;
+-}
+-
+-static inline void put_mtd_device(struct mtd_info *mtd)
+-{
+- if (mtd->module)
+- __MOD_DEC_USE_COUNT(mtd->module);
+-}
++extern void put_mtd_device(struct mtd_info *mtd);
+
+
+ struct mtd_notifier {
+ void (*add)(struct mtd_info *mtd);
+ void (*remove)(struct mtd_info *mtd);
+- struct mtd_notifier *next;
++ struct list_head list;
+ };
+
+
+@@ -263,7 +261,6 @@
+ int default_mtd_readv(struct mtd_info *mtd, struct iovec *vecs,
+ unsigned long count, loff_t from, size_t *retlen);
+
+-#ifndef MTDC
+ #define MTD_ERASE(mtd, args...) (*(mtd->erase))(mtd, args)
+ #define MTD_POINT(mtd, a,b,c,d) (*(mtd->point))(mtd, a,b,c, (u_char **)(d))
+ #define MTD_UNPOINT(mtd, arg) (*(mtd->unpoint))(mtd, (u_char *)arg)
+@@ -276,7 +273,6 @@
+ #define MTD_READOOB(mtd, args...) (*(mtd->read_oob))(mtd, args)
+ #define MTD_WRITEOOB(mtd, args...) (*(mtd->write_oob))(mtd, args)
+ #define MTD_SYNC(mtd) do { if (mtd->sync) (*(mtd->sync))(mtd); } while (0)
+-#endif /* MTDC */
+
+ /*
+ * Debugging macro and defines
+@@ -293,7 +289,8 @@
+ printk(KERN_INFO args); \
+ } while(0)
+ #else /* CONFIG_MTD_DEBUG */
+-#define DEBUG(n, args...)
++#define DEBUG(n, args...) do { } while(0)
++
+ #endif /* CONFIG_MTD_DEBUG */
+
+ #endif /* __KERNEL__ */
+diff -Nurb linux-mips-2.4.24-pre2/include/linux/mtd/nand.h linux/include/linux/mtd/nand.h
+--- linux-mips-2.4.24-pre2/include/linux/mtd/nand.h 2004-11-17 18:05:09.000000000 +0100
++++ linux/include/linux/mtd/nand.h 2004-11-17 18:17:59.236283976 +0100
+@@ -2,10 +2,10 @@
+ * linux/include/linux/mtd/nand.h
+ *
+ * Copyright (c) 2000 David Woodhouse <dwmw2@mvhi.com>
+- * Steven J. Hill <sjhill@cotw.com>
++ * Steven J. Hill <sjhill@realitydiluted.com>
+ * Thomas Gleixner <tglx@linutronix.de>
+ *
+- * $Id$
++ * $Id$
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+@@ -49,12 +49,14 @@
+ #define __LINUX_MTD_NAND_H
+
+ #include <linux/config.h>
+-#include <linux/sched.h>
++#include <linux/wait.h>
++#include <linux/spinlock.h>
+
++struct mtd_info;
+ /*
+ * Searches for a NAND device
+ */
+-extern int nand_scan (struct mtd_info *mtd);
++extern int nand_scan (struct mtd_info *mtd, int max_chips);
+
+ /*
+ * Constants for hardware specific CLE/ALE/NCE function
+@@ -65,6 +67,8 @@
+ #define NAND_CTL_CLRCLE 4
+ #define NAND_CTL_SETALE 5
+ #define NAND_CTL_CLRALE 6
++#define NAND_CTL_SETWP 7
++#define NAND_CTL_CLRWP 8
+
+ /*
+ * Standard NAND flash commands
+@@ -160,24 +164,33 @@
+ struct nand_chip {
+ unsigned long IO_ADDR_R;
+ unsigned long IO_ADDR_W;
+- void (*hwcontrol)(int cmd);
+- int (*dev_ready)(void);
++
++ u_char (*read_byte)(struct mtd_info *mtd);
++ void (*write_byte)(struct mtd_info *mtd, u_char byte);
++
++ void (*write_buf)(struct mtd_info *mtd, const u_char *buf, int len);
++ void (*read_buf)(struct mtd_info *mtd, u_char *buf, int len);
++ int (*verify_buf)(struct mtd_info *mtd, const u_char *buf, int len);
++ void (*select_chip)(struct mtd_info *mtd, int chip);
++ int (*block_bad)(struct mtd_info *mtd, unsigned long pos);
++ void (*hwcontrol)(struct mtd_info *mtd, int cmd);
++ int (*dev_ready)(struct mtd_info *mtd);
+ void (*cmdfunc)(struct mtd_info *mtd, unsigned command, int column, int page_addr);
+ int (*waitfunc)(struct mtd_info *mtd, struct nand_chip *this, int state);
+- void (*calculate_ecc)(const u_char *dat, u_char *ecc_code);
+- int (*correct_data)(u_char *dat, u_char *read_ecc, u_char *calc_ecc);
+- void (*enable_hwecc)(int mode);
++ void (*calculate_ecc)(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code);
++ int (*correct_data)(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc);
++ void (*enable_hwecc)(struct mtd_info *mtd, int mode);
+ int eccmode;
+ int eccsize;
+ int chip_delay;
++ int chipshift;
+ spinlock_t chip_lock;
+ wait_queue_head_t wq;
+ nand_state_t state;
+ int page_shift;
+ u_char *data_buf;
+ u_char *data_poi;
+- u_char *data_cache;
+- int cache_page;
++ void *priv;
+ };
+
+ /*
+@@ -241,34 +254,4 @@
+ */
+ #define NAND_BADBLOCK_POS 5
+
+-#define NAND_NONE_OOB 0
+-#define NAND_JFFS2_OOB 1
+-#define NAND_YAFFS_OOB 2
+-
+-#define NAND_NOOB_ECCPOS0 0
+-#define NAND_NOOB_ECCPOS1 1
+-#define NAND_NOOB_ECCPOS2 2
+-#define NAND_NOOB_ECCPOS3 3
+-#define NAND_NOOB_ECCPOS4 6
+-#define NAND_NOOB_ECCPOS5 7
+-
+-#define NAND_JFFS2_OOB_ECCPOS0 0
+-#define NAND_JFFS2_OOB_ECCPOS1 1
+-#define NAND_JFFS2_OOB_ECCPOS2 2
+-#define NAND_JFFS2_OOB_ECCPOS3 3
+-#define NAND_JFFS2_OOB_ECCPOS4 6
+-#define NAND_JFFS2_OOB_ECCPOS5 7
+-
+-#define NAND_YAFFS_OOB_ECCPOS0 8
+-#define NAND_YAFFS_OOB_ECCPOS1 9
+-#define NAND_YAFFS_OOB_ECCPOS2 10
+-#define NAND_YAFFS_OOB_ECCPOS3 13
+-#define NAND_YAFFS_OOB_ECCPOS4 14
+-#define NAND_YAFFS_OOB_ECCPOS5 15
+-
+-#define NAND_JFFS2_OOB8_FSDAPOS 6
+-#define NAND_JFFS2_OOB16_FSDAPOS 8
+-#define NAND_JFFS2_OOB8_FSDALEN 2
+-#define NAND_JFFS2_OOB16_FSDALEN 8
+-
+ #endif /* __LINUX_MTD_NAND_H */
+diff -Nurb linux-mips-2.4.24-pre2/include/linux/mtd/nand_ecc.h linux/include/linux/mtd/nand_ecc.h
+--- linux-mips-2.4.24-pre2/include/linux/mtd/nand_ecc.h 2004-11-17 18:05:09.000000000 +0100
++++ linux/include/linux/mtd/nand_ecc.h 2004-11-17 18:17:59.237283824 +0100
+@@ -1,9 +1,9 @@
+ /*
+ * drivers/mtd/nand_ecc.h
+ *
+- * Copyright (C) 2000 Steven J. Hill (sjhill@cotw.com)
++ * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
+ *
+- * $Id$
++ * $Id$
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+@@ -12,17 +12,19 @@
+ * This file is the header for the ECC algorithm.
+ */
+
+-/*
+- * Creates non-inverted ECC code from line parity
+- */
+-void nand_trans_result(u_char reg2, u_char reg3, u_char *ecc_code);
++#ifndef __MTD_NAND_ECC_H__
++#define __MTD_NAND_ECC_H__
++
++struct mtd_info;
+
+ /*
+ * Calculate 3 byte ECC code for 256 byte block
+ */
+-void nand_calculate_ecc (const u_char *dat, u_char *ecc_code);
++void nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code);
+
+ /*
+ * Detect and correct a 1 bit error for 256 byte block
+ */
+-int nand_correct_data (u_char *dat, u_char *read_ecc, u_char *calc_ecc);
++int nand_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc);
++
++#endif /* __MTD_NAND_ECC_H__ */
+diff -Nurb linux-mips-2.4.24-pre2/include/linux/mtd/nftl.h linux/include/linux/mtd/nftl.h
+--- linux-mips-2.4.24-pre2/include/linux/mtd/nftl.h 2004-11-17 18:05:09.000000000 +0100
++++ linux/include/linux/mtd/nftl.h 2004-11-17 18:17:59.238283672 +0100
+@@ -1,15 +1,14 @@
+-
+-/* Defines for NAND Flash Translation Layer */
+-/* (c) 1999 Machine Vision Holdings, Inc. */
+-/* Author: David Woodhouse <dwmw2@mvhi.com> */
+-/* $Id$ */
++/*
++ * $Id$
++ *
++ * (C) 1999-2003 David Woodhouse <dwmw2@infradead.org>
++ */
+
+ #ifndef __MTD_NFTL_H__
+ #define __MTD_NFTL_H__
+
+-#ifndef __BOOT__
+ #include <linux/mtd/mtd.h>
+-#endif
++#include <linux/mtd/blktrans.h>
+
+ /* Block Control Information */
+
+@@ -84,8 +83,7 @@
+ #define BLOCK_RESERVED 0xfffc /* bios block or bad block */
+
+ struct NFTLrecord {
+- struct mtd_info *mtd;
+- struct semaphore mutex;
++ struct mtd_blktrans_dev mbd;
+ __u16 MediaUnit, SpareMediaUnit;
+ __u32 EraseSize;
+ struct NFTLMediaHeader MediaHdr;
+@@ -97,7 +95,6 @@
+ __u16 lastEUN; /* should be suppressed */
+ __u16 numfreeEUNs;
+ __u16 LastFreeEUN; /* To speed up finding a free EUN */
+- __u32 nr_sects;
+ int head,sect,cyl;
+ __u16 *EUNtable; /* [numvunits]: First EUN for each virtual unit */
+ __u16 *ReplUnitTable; /* [numEUNs]: ReplUnitNumber for each */
+@@ -114,7 +111,7 @@
+ #endif
+
+ #define MAX_NFTLS 16
+-#define MAX_SECTORS_PER_UNIT 32
++#define MAX_SECTORS_PER_UNIT 64
+ #define NFTL_PARTN_BITS 4
+
+ #endif /* __KERNEL__ */
+diff -Nurb linux-mips-2.4.24-pre2/include/linux/mtd/partitions.h linux/include/linux/mtd/partitions.h
+--- linux-mips-2.4.24-pre2/include/linux/mtd/partitions.h 2004-11-17 18:05:09.000000000 +0100
++++ linux/include/linux/mtd/partitions.h 2004-11-17 18:17:59.240283368 +0100
+@@ -5,7 +5,7 @@
+ *
+ * This code is GPL
+ *
+- * $Id$
++ * $Id$
+ */
+
+ #ifndef MTD_PARTITIONS_H
+@@ -41,6 +41,7 @@
+ u_int32_t size; /* partition size */
+ u_int32_t offset; /* offset within the master MTD space */
+ u_int32_t mask_flags; /* master MTD flags to mask out for this partition */
++ struct nand_oobinfo *oobsel; /* out of band layout for this partition (NAND only)*/
+ struct mtd_info **mtdp; /* pointer to store the MTD object */
+ };
+
+@@ -49,8 +50,27 @@
+ #define MTDPART_SIZ_FULL (0)
+
+
+-int add_mtd_partitions(struct mtd_info *, struct mtd_partition *, int);
++int add_mtd_partitions(struct mtd_info *, const struct mtd_partition *, int);
+ int del_mtd_partitions(struct mtd_info *);
+
++/*
++ * Functions dealing with the various ways of partitioning the space
++ */
++
++struct mtd_part_parser {
++ struct list_head list;
++ struct module *owner;
++ const char *name;
++ int (*parse_fn)(struct mtd_info *, struct mtd_partition **, unsigned long);
++};
++
++extern struct mtd_part_parser *get_partition_parser(const char *name);
++extern int register_mtd_parser(struct mtd_part_parser *parser);
++extern int deregister_mtd_parser(struct mtd_part_parser *parser);
++extern int parse_mtd_partitions(struct mtd_info *master, const char **types,
++ struct mtd_partition **pparts, unsigned long origin);
++
++#define put_partition_parser(p) do { module_put((p)->owner); } while(0)
++
+ #endif
+
+diff -Nurb linux-mips-2.4.24-pre2/include/linux/mtd/physmap.h linux/include/linux/mtd/physmap.h
+--- linux-mips-2.4.24-pre2/include/linux/mtd/physmap.h 1970-01-01 01:00:00.000000000 +0100
++++ linux/include/linux/mtd/physmap.h 2004-11-17 18:17:59.241283216 +0100
+@@ -0,0 +1,59 @@
++/*
++ * For boards with physically mapped flash and using
++ * drivers/mtd/maps/physmap.c mapping driver.
++ *
++ * Copyright (C) 2003 MontaVista Software Inc.
++ * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version.
++ *
++ */
++
++#ifndef __LINUX_MTD_PHYSMAP__
++
++#include <linux/config.h>
++
++#if defined(CONFIG_MTD_PHYSMAP)
++
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/map.h>
++#include <linux/mtd/partitions.h>
++
++/*
++ * The map_info for physmap. Board can override size, buswidth, phys,
++ * (*set_vpp)(), etc in their initial setup routine.
++ */
++extern struct map_info physmap_map;
++
++/*
++ * Board needs to specify the exact mapping during their setup time.
++ */
++static inline void physmap_configure(unsigned long addr, unsigned long size, int buswidth, void (*set_vpp)(struct map_info *, int) )
++{
++ physmap_map.phys = addr;
++ physmap_map.size = size;
++ physmap_map.buswidth = buswidth;
++ physmap_map.set_vpp = set_vpp;
++}
++
++#if defined(CONFIG_MTD_PARTITIONS)
++
++/*
++ * Machines that wish to do flash partition may want to call this function in
++ * their setup routine.
++ *
++ * physmap_set_partitions(mypartitions, num_parts);
++ *
++ * Note that one can always override this hard-coded partition with
++ * command line partition (you need to enable CONFIG_MTD_CMDLINE_PARTS).
++ */
++void physmap_set_partitions(struct mtd_partition *parts, int num_parts);
++
++#endif /* defined(CONFIG_MTD_PARTITIONS) */
++#endif /* defined(CONFIG_MTD) */
++
++#endif /* __LINUX_MTD_PHYSMAP__ */
++
+diff -Nurb linux-mips-2.4.24-pre2/include/linux/rbtree-24.h linux/include/linux/rbtree-24.h
+--- linux-mips-2.4.24-pre2/include/linux/rbtree-24.h 1970-01-01 01:00:00.000000000 +0100
++++ linux/include/linux/rbtree-24.h 2004-11-17 18:17:59.443252512 +0100
+@@ -0,0 +1,133 @@
++/*
++ Red Black Trees
++ (C) 1999 Andrea Arcangeli <andrea@suse.de>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License as published by
++ the Free Software Foundation; either version 2 of the License, or
++ (at your option) any later version.
++
++ This program is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ GNU General Public License for more details.
++
++ You should have received a copy of the GNU General Public License
++ along with this program; if not, write to the Free Software
++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++
++ linux/include/linux/rbtree.h
++
++ To use rbtrees you'll have to implement your own insert and search cores.
++ This will avoid us to use callbacks and to drop drammatically performances.
++ I know it's not the cleaner way, but in C (not in C++) to get
++ performances and genericity...
++
++ Some example of insert and search follows here. The search is a plain
++ normal search over an ordered tree. The insert instead must be implemented
++ int two steps: as first thing the code must insert the element in
++ order as a red leaf in the tree, then the support library function
++ rb_insert_color() must be called. Such function will do the
++ not trivial work to rebalance the rbtree if necessary.
++
++-----------------------------------------------------------------------
++static inline struct page * rb_search_page_cache(struct inode * inode,
++ unsigned long offset)
++{
++ rb_node_t * n = inode->i_rb_page_cache.rb_node;
++ struct page * page;
++
++ while (n)
++ {
++ page = rb_entry(n, struct page, rb_page_cache);
++
++ if (offset < page->offset)
++ n = n->rb_left;
++ else if (offset > page->offset)
++ n = n->rb_right;
++ else
++ return page;
++ }
++ return NULL;
++}
++
++static inline struct page * __rb_insert_page_cache(struct inode * inode,
++ unsigned long offset,
++ rb_node_t * node)
++{
++ rb_node_t ** p = &inode->i_rb_page_cache.rb_node;
++ rb_node_t * parent = NULL;
++ struct page * page;
++
++ while (*p)
++ {
++ parent = *p;
++ page = rb_entry(parent, struct page, rb_page_cache);
++
++ if (offset < page->offset)
++ p = &(*p)->rb_left;
++ else if (offset > page->offset)
++ p = &(*p)->rb_right;
++ else
++ return page;
++ }
++
++ rb_link_node(node, parent, p);
++
++ return NULL;
++}
++
++static inline struct page * rb_insert_page_cache(struct inode * inode,
++ unsigned long offset,
++ rb_node_t * node)
++{
++ struct page * ret;
++ if ((ret = __rb_insert_page_cache(inode, offset, node)))
++ goto out;
++ rb_insert_color(node, &inode->i_rb_page_cache);
++ out:
++ return ret;
++}
++-----------------------------------------------------------------------
++*/
++
++#ifndef _LINUX_RBTREE_H
++#define _LINUX_RBTREE_H
++
++#include <linux/kernel.h>
++#include <linux/stddef.h>
++
++typedef struct rb_node_s
++{
++ struct rb_node_s * rb_parent;
++ int rb_color;
++#define RB_RED 0
++#define RB_BLACK 1
++ struct rb_node_s * rb_right;
++ struct rb_node_s * rb_left;
++}
++rb_node_t;
++
++typedef struct rb_root_s
++{
++ struct rb_node_s * rb_node;
++}
++rb_root_t;
++
++#define RB_ROOT (rb_root_t) { NULL, }
++#define rb_entry(ptr, type, member) \
++ ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
++
++extern void rb_insert_color(rb_node_t *, rb_root_t *);
++extern void rb_erase(rb_node_t *, rb_root_t *);
++
++static inline void rb_link_node(rb_node_t * node, rb_node_t * parent, rb_node_t ** rb_link)
++{
++ node->rb_parent = parent;
++ node->rb_color = RB_RED;
++ node->rb_left = node->rb_right = NULL;
++
++ *rb_link = node;
++}
++
++#endif /* _LINUX_RBTREE_H */
+diff -Nurb linux-mips-2.4.24-pre2/include/linux/rbtree.h linux/include/linux/rbtree.h
+--- linux-mips-2.4.24-pre2/include/linux/rbtree.h 2004-11-17 18:05:09.000000000 +0100
++++ linux/include/linux/rbtree.h 2004-11-17 18:17:59.000000000 +0100
+@@ -1,133 +1,25 @@
+ /*
+- Red Black Trees
+- (C) 1999 Andrea Arcangeli <andrea@suse.de>
++ * 2.5 compatibility
++ * $Id$
++ */
++
++#ifndef __MTD_COMPAT_RBTREE_H__
++#define __MTD_COMPAT_RBTREE_H__
++
++#include <linux/version.h>
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,40)
++#include_next <linux/rbtree.h>
++#else
++#define rb_node_s rb_node
++#define rb_root_s rb_root
++
++#include <linux/rbtree-24.h>
++
++/* Find logical next and previous nodes in a tree */
++extern struct rb_node *rb_next(struct rb_node *);
++extern struct rb_node *rb_prev(struct rb_node *);
++extern struct rb_node *rb_first(struct rb_root *);
++#endif
+
+- This program is free software; you can redistribute it and/or modify
+- it under the terms of the GNU General Public License as published by
+- the Free Software Foundation; either version 2 of the License, or
+- (at your option) any later version.
+-
+- This program is distributed in the hope that it will be useful,
+- but WITHOUT ANY WARRANTY; without even the implied warranty of
+- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+- GNU General Public License for more details.
+-
+- You should have received a copy of the GNU General Public License
+- along with this program; if not, write to the Free Software
+- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+-
+- linux/include/linux/rbtree.h
+-
+- To use rbtrees you'll have to implement your own insert and search cores.
+- This will avoid us to use callbacks and to drop drammatically performances.
+- I know it's not the cleaner way, but in C (not in C++) to get
+- performances and genericity...
+-
+- Some example of insert and search follows here. The search is a plain
+- normal search over an ordered tree. The insert instead must be implemented
+- int two steps: as first thing the code must insert the element in
+- order as a red leaf in the tree, then the support library function
+- rb_insert_color() must be called. Such function will do the
+- not trivial work to rebalance the rbtree if necessary.
+-
+------------------------------------------------------------------------
+-static inline struct page * rb_search_page_cache(struct inode * inode,
+- unsigned long offset)
+-{
+- rb_node_t * n = inode->i_rb_page_cache.rb_node;
+- struct page * page;
+-
+- while (n)
+- {
+- page = rb_entry(n, struct page, rb_page_cache);
+-
+- if (offset < page->offset)
+- n = n->rb_left;
+- else if (offset > page->offset)
+- n = n->rb_right;
+- else
+- return page;
+- }
+- return NULL;
+-}
+-
+-static inline struct page * __rb_insert_page_cache(struct inode * inode,
+- unsigned long offset,
+- rb_node_t * node)
+-{
+- rb_node_t ** p = &inode->i_rb_page_cache.rb_node;
+- rb_node_t * parent = NULL;
+- struct page * page;
+-
+- while (*p)
+- {
+- parent = *p;
+- page = rb_entry(parent, struct page, rb_page_cache);
+-
+- if (offset < page->offset)
+- p = &(*p)->rb_left;
+- else if (offset > page->offset)
+- p = &(*p)->rb_right;
+- else
+- return page;
+- }
+-
+- rb_link_node(node, parent, p);
+-
+- return NULL;
+-}
+-
+-static inline struct page * rb_insert_page_cache(struct inode * inode,
+- unsigned long offset,
+- rb_node_t * node)
+-{
+- struct page * ret;
+- if ((ret = __rb_insert_page_cache(inode, offset, node)))
+- goto out;
+- rb_insert_color(node, &inode->i_rb_page_cache);
+- out:
+- return ret;
+-}
+------------------------------------------------------------------------
+-*/
+-
+-#ifndef _LINUX_RBTREE_H
+-#define _LINUX_RBTREE_H
+-
+-#include <linux/kernel.h>
+-#include <linux/stddef.h>
+-
+-typedef struct rb_node_s
+-{
+- struct rb_node_s * rb_parent;
+- int rb_color;
+-#define RB_RED 0
+-#define RB_BLACK 1
+- struct rb_node_s * rb_right;
+- struct rb_node_s * rb_left;
+-}
+-rb_node_t;
+-
+-typedef struct rb_root_s
+-{
+- struct rb_node_s * rb_node;
+-}
+-rb_root_t;
+-
+-#define RB_ROOT (rb_root_t) { NULL, }
+-#define rb_entry(ptr, type, member) \
+- ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
+-
+-extern void rb_insert_color(rb_node_t *, rb_root_t *);
+-extern void rb_erase(rb_node_t *, rb_root_t *);
+-
+-static inline void rb_link_node(rb_node_t * node, rb_node_t * parent, rb_node_t ** rb_link)
+-{
+- node->rb_parent = parent;
+- node->rb_color = RB_RED;
+- node->rb_left = node->rb_right = NULL;
+-
+- *rb_link = node;
+-}
+-
+-#endif /* _LINUX_RBTREE_H */
++#endif /* __MTD_COMPAT_RBTREE_H__ */
+diff -Nurb linux-mips-2.4.24-pre2/include/linux/suspend.h linux/include/linux/suspend.h
+--- linux-mips-2.4.24-pre2/include/linux/suspend.h 1970-01-01 01:00:00.000000000 +0100
++++ linux/include/linux/suspend.h 2004-11-17 18:17:59.450251448 +0100
+@@ -0,0 +1,10 @@
++/* $Id$ */
++
++#ifndef __MTD_COMPAT_VERSION_H__
++#include <linux/version.h>
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
++#include_next <linux/suspend.h>
++#endif
++
++#endif /* __MTD_COMPAT_VERSION_H__ */
+diff -Nurb linux-mips-2.4.24-pre2/include/linux/workqueue.h linux/include/linux/workqueue.h
+--- linux-mips-2.4.24-pre2/include/linux/workqueue.h 1970-01-01 01:00:00.000000000 +0100
++++ linux/include/linux/workqueue.h 2004-11-17 18:17:59.451251296 +0100
+@@ -0,0 +1,21 @@
++/*
++ * 2.5 compatibility
++ * $Id$
++ */
++
++#ifndef __MTD_COMPAT_WORKQUEUE_H__
++#define __MTD_COMPAT_WORKQUEUE_H__
++
++#include <linux/version.h>
++
++#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,40)
++#include_next <linux/workqueue.h>
++#else
++#include <linux/tqueue.h>
++#define work_struct tq_struct
++#define schedule_work(x) schedule_task(x)
++#define flush_scheduled_work flush_scheduled_tasks
++#define INIT_WORK(x,y,z) INIT_TQUEUE(x,y,z)
++#endif
++
++#endif /* __MTD_COMPAT_WORKQUEUE_H__ */
diff --git a/linux/linux-mtx-1-2.4.24/02-mtd-mtx-1-map.diff b/linux/linux-mtx-1-2.4.24/02-mtd-mtx-1-map.diff
index e69de29bb2..ba24bb1381 100644
--- a/linux/linux-mtx-1-2.4.24/02-mtd-mtx-1-map.diff
+++ b/linux/linux-mtx-1-2.4.24/02-mtd-mtx-1-map.diff
@@ -0,0 +1,248 @@
+diff -Nurb linux/drivers/mtd/maps/Config.in linux-mips-2.4.24-pre2+mtd-2004-01-27+mtx-map/drivers/mtd/maps/Config.in
+--- linux/drivers/mtd/maps/Config.in 2004-11-17 18:17:59.049312400 +0100
++++ linux-mips-2.4.24-pre2+mtd-2004-01-27+mtx-map/drivers/mtd/maps/Config.in 2004-11-17 18:12:26.000000000 +0100
+@@ -80,6 +80,7 @@
+ bool ' Db1x00 boot flash device' CONFIG_MTD_DB1X00_BOOT
+ bool ' Db1x00 user flash device (2nd bank)' CONFIG_MTD_DB1X00_USER
+ fi
++ dep_tristate ' MTX-1 flash device' CONFIG_MTD_MTX1 $CONFIG_MIPS_MTX1
+ dep_tristate ' Flash chip mapping on ITE QED-4N-S01B, Globespan IVR or custom board' CONFIG_MTD_CSTM_MIPS_IXX $CONFIG_MTD_CFI $CONFIG_MTD_JEDEC $CONFIG_MTD_PARTITIONS
+ if [ "$CONFIG_MTD_CSTM_MIPS_IXX" = "y" -o "$CONFIG_MTD_CSTM_MIPS_IXX" = "m" ]; then
+ hex ' Physical start address of flash mapping' CONFIG_MTD_CSTM_MIPS_IXX_START 0x8000000
+diff -Nurb linux/drivers/mtd/maps/Makefile linux-mips-2.4.24-pre2+mtd-2004-01-27+mtx-map/drivers/mtd/maps/Makefile
+--- linux/drivers/mtd/maps/Makefile 2004-11-17 18:17:59.051312096 +0100
++++ linux-mips-2.4.24-pre2+mtd-2004-01-27+mtx-map/drivers/mtd/maps/Makefile 2004-11-17 18:12:26.000000000 +0100
+@@ -49,6 +49,7 @@
+ obj-$(CONFIG_MTD_PCI) += pci.o
+ obj-$(CONFIG_MTD_PB1XXX) += pb1xxx-flash.o
+ obj-$(CONFIG_MTD_DB1X00) += db1x00-flash.o
++obj-$(CONFIG_MTD_MTX1) += mtx-1.o
+ obj-$(CONFIG_MTD_LASAT) += lasat.o
+ obj-$(CONFIG_MTD_AUTCPU12) += autcpu12-nvram.o
+ obj-$(CONFIG_MTD_EDB7312) += edb7312.o
+diff -Nurb linux/drivers/mtd/maps/mtx-1.c linux-mips-2.4.24-pre2+mtd-2004-01-27+mtx-map/drivers/mtd/maps/mtx-1.c
+--- linux/drivers/mtd/maps/mtx-1.c 2004-11-17 18:17:02.689880336 +0100
++++ linux-mips-2.4.24-pre2+mtd-2004-01-27+mtx-map/drivers/mtd/maps/mtx-1.c 2004-11-17 18:12:26.000000000 +0100
+@@ -1,166 +1,78 @@
+ /*
+ * Flash memory access on 4G Systems MTX-1 board
+ *
+- * (C) 2003 Pete Popov <ppopov@mvista.com>
+- * Bruno Randolf <bruno.randolf@4g-systems.de>
++ * (C) Bruno Randolf (4G Systeme GmbH) <bruno.randolf@4g-systems.biz>
+ */
+
+ #include <linux/config.h>
+ #include <linux/module.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
+
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+ #include <linux/mtd/partitions.h>
+
+ #include <asm/io.h>
+-#include <asm/au1000.h>
+
+-#ifdef DEBUG_RW
+-#define DBG(x...) printk(x)
+-#else
+-#define DBG(x...)
+-#endif
+-
+-#ifdef CONFIG_MIPS_MTX1
+ #define WINDOW_ADDR 0x1E000000
+ #define WINDOW_SIZE 0x2000000
+-#endif
+-
+-__u8 physmap_read8(struct map_info *map, unsigned long ofs)
+-{
+- __u8 ret;
+- ret = __raw_readb(map->map_priv_1 + ofs);
+- DBG("read8 from %x, %x\n", (unsigned)(map->map_priv_1 + ofs), ret);
+- return ret;
+-}
+-
+-__u16 physmap_read16(struct map_info *map, unsigned long ofs)
+-{
+- __u16 ret;
+- ret = __raw_readw(map->map_priv_1 + ofs);
+- DBG("read16 from %x, %x\n", (unsigned)(map->map_priv_1 + ofs), ret);
+- return ret;
+-}
+-
+-__u32 physmap_read32(struct map_info *map, unsigned long ofs)
+-{
+- __u32 ret;
+- ret = __raw_readl(map->map_priv_1 + ofs);
+- DBG("read32 from %x, %x\n", (unsigned)(map->map_priv_1 + ofs), ret);
+- return ret;
+-}
+-
+-void physmap_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+-{
+- DBG("physmap_copy from %x to %x\n", (unsigned)from, (unsigned)to);
+- memcpy_fromio(to, map->map_priv_1 + from, len);
+-}
+-
+-void physmap_write8(struct map_info *map, __u8 d, unsigned long adr)
+-{
+- DBG("write8 at %x, %x\n", (unsigned)(map->map_priv_1 + adr), d);
+- __raw_writeb(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-void physmap_write16(struct map_info *map, __u16 d, unsigned long adr)
+-{
+- DBG("write16 at %x, %x\n", (unsigned)(map->map_priv_1 + adr), d);
+- __raw_writew(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-void physmap_write32(struct map_info *map, __u32 d, unsigned long adr)
+-{
+- DBG("write32 at %x, %x\n", (unsigned)(map->map_priv_1 + adr), d);
+- __raw_writel(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-void physmap_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+-{
+- DBG("physmap_copy_to %x from %x\n", (unsigned)to, (unsigned)from);
+- memcpy_toio(map->map_priv_1 + to, from, len);
+-}
+-
+-
++#define BUSWIDTH 4
+
+ static struct map_info mtx1_map = {
+- name: "MTX-1 flash",
+- read8: physmap_read8,
+- read16: physmap_read16,
+- read32: physmap_read32,
+- copy_from: physmap_copy_from,
+- write8: physmap_write8,
+- write16: physmap_write16,
+- write32: physmap_write32,
+- copy_to: physmap_copy_to,
++ .name = "MTX-1 flash",
++ .size = WINDOW_SIZE,
++ .buswidth = BUSWIDTH,
++ .phys = WINDOW_ADDR
+ };
+
+-
+-static unsigned long flash_size = 0x01000000;
+-static unsigned char flash_buswidth = 4;
+ static struct mtd_partition mtx1_partitions[] = {
+ {
+- name: "user fs",
+- size: 0x1c00000,
+- offset: 0,
++ .name = "user fs",
++ .size = 0x1c00000,
++ .offset = 0,
+ },{
+- name: "yamon",
+- size: 0x0100000,
+- offset: MTDPART_OFS_APPEND,
+- mask_flags: MTD_WRITEABLE
++ .name = "yamon",
++ .size = 0x0100000,
++ .offset = MTDPART_OFS_APPEND,
++ .mask_flags = MTD_WRITEABLE /* force read-only */
+ },{
+- name: "raw kernel",
+- size: 0x02c0000,
+- offset: MTDPART_OFS_APPEND,
++ .name = "raw kernel",
++ .size = 0x02c0000,
++ .offset = MTDPART_OFS_APPEND,
+ },{
+- name: "yamon env vars",
+- size: 0x0040000,
+- offset: MTDPART_OFS_APPEND,
+- mask_flags: MTD_WRITEABLE
++ .name = "yamon environment",
++ .size = 0x0040000,
++ .offset = MTDPART_OFS_APPEND,
++ .mask_flags = MTD_WRITEABLE /* force read-only */
+ }
+ };
+
+-
+-#define NB_OF(x) (sizeof(x)/sizeof(x[0]))
+-
+-static struct mtd_partition *parsed_parts;
+ static struct mtd_info *mymtd;
+
+ int __init mtx1_mtd_init(void)
+ {
+- struct mtd_partition *parts;
+- int nb_parts = 0;
+- char *part_type;
++ printk(KERN_NOTICE "MTX-1 flash: probing %d-bit flash bus at %x\n",
++ mtx1_map.buswidth*8, WINDOW_ADDR);
+
+- /* Default flash buswidth */
+- mtx1_map.buswidth = flash_buswidth;
++ mtx1_map.virt = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE);
++ if (!mtx1_map.virt) {
++ printk("mtx_mtd_init: failed to ioremap\n");
++ return -EIO;
++ }
++
++ simple_map_init(&mtx1_map);
+
+- /*
+- * Static partition definition selection
+- */
+- part_type = "static";
+- parts = mtx1_partitions;
+- nb_parts = NB_OF(mtx1_partitions);
+- mtx1_map.size = flash_size;
+-
+- /*
+- * Now let's probe for the actual flash. Do it here since
+- * specific machine settings might have been set above.
+- */
+- printk(KERN_NOTICE "MTX-1 flash: probing %d-bit flash bus\n",
+- mtx1_map.buswidth*8);
+- mtx1_map.map_priv_1 =
+- (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE);
+ mymtd = do_map_probe("cfi_probe", &mtx1_map);
+- if (!mymtd) return -ENXIO;
+- mymtd->module = THIS_MODULE;
++ if (!mymtd) {
++ iounmap(mtx1_map.virt);
++ return -ENXIO;
++ }
++
++ mymtd->owner = THIS_MODULE;
+
+- add_mtd_partitions(mymtd, parts, nb_parts);
+- return 0;
++ return add_mtd_partitions(mymtd, mtx1_partitions, ARRAY_SIZE(mtx1_partitions));
+ }
+
+ static void __exit mtx1_mtd_cleanup(void)
+@@ -168,14 +80,14 @@
+ if (mymtd) {
+ del_mtd_partitions(mymtd);
+ map_destroy(mymtd);
+- if (parsed_parts)
+- kfree(parsed_parts);
+ }
++ if (mtx1_map.virt)
++ iounmap(mtx1_map.virt);
+ }
+
+ module_init(mtx1_mtd_init);
+ module_exit(mtx1_mtd_cleanup);
+
+-MODULE_AUTHOR("Pete Popov");
++MODULE_AUTHOR("Bruno Randolf <bruno.randolf@4g-systems.biz>");
+ MODULE_DESCRIPTION("MTX-1 CFI map driver");
+ MODULE_LICENSE("GPL");
+
diff --git a/linux/linux-mtx-1-2.4.24/03-mtd-erase-compiler-bug.diff b/linux/linux-mtx-1-2.4.24/03-mtd-erase-compiler-bug.diff
index e69de29bb2..9e310bf327 100644
--- a/linux/linux-mtx-1-2.4.24/03-mtd-erase-compiler-bug.diff
+++ b/linux/linux-mtx-1-2.4.24/03-mtd-erase-compiler-bug.diff
@@ -0,0 +1,21 @@
+--- linux-mips-2.4.24-pre2+mtd-2004-01-27/fs/jffs2/erase.c 2004-11-17 18:17:59.000000000 +0100
++++ linux/fs/jffs2/erase.c 2004-11-17 18:44:52.260067088 +0100
+@@ -365,11 +365,13 @@
+ jeb->dirty_size = 0;
+ jeb->wasted_size = 0;
+ } else {
+- struct jffs2_unknown_node marker = {
+- .magic = cpu_to_je16(JFFS2_MAGIC_BITMASK),
+- .nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER),
+- .totlen = cpu_to_je32(c->cleanmarker_size)
+- };
++ /* compiler workaround, structure was not initialized before
++ on mipsel cross compilers
++ fix by Eugene.Wisor@flukenetworks.com */
++ struct jffs2_unknown_node marker;
++ marker.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
++ marker.nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER);
++ marker.totlen = cpu_to_je32(c->cleanmarker_size);
+
+ marker.hdr_crc = cpu_to_je32(crc32(0, &marker, sizeof(struct jffs2_unknown_node)-4));
+
diff --git a/linux/linux-mtx-1-2.4.24/04-zboot-2.4.24.patch b/linux/linux-mtx-1-2.4.24/04-zboot-2.4.24.patch
index e69de29bb2..85f5fc3aa3 100644
--- a/linux/linux-mtx-1-2.4.24/04-zboot-2.4.24.patch
+++ b/linux/linux-mtx-1-2.4.24/04-zboot-2.4.24.patch
@@ -0,0 +1,5286 @@
+diff -Naru linux/arch/mips/Makefile linux-new/arch/mips/Makefile
+--- linux/arch/mips/Makefile 2003-10-22 02:58:37.000000000 -0400
++++ linux-new/arch/mips/Makefile 2003-12-18 14:26:20.000000000 -0500
+@@ -724,15 +724,21 @@
+ endif
+
+ MAKEBOOT = $(MAKE) -C arch/$(ARCH)/boot
++MAKEZBOOT = $(MAKE) -C arch/$(ARCH)/zboot
++BOOT_TARGETS = zImage zImage.initrd zImage.flash
+
+ vmlinux.ecoff: vmlinux
+ @$(MAKEBOOT) $@
+
++ $(BOOT_TARGETS): vmlinux
++ @$(MAKEZBOOT) $@
++
+ vmlinux.srec: vmlinux
+ @$(MAKEBOOT) $@
+
+ archclean:
+ @$(MAKEBOOT) clean
++ @$(MAKEZBOOT) clean
+ rm -f arch/$(ARCH)/ld.script
+ $(MAKE) -C arch/$(ARCH)/tools clean
+ $(MAKE) -C arch/mips/baget clean
+diff -Naru linux/arch/mips/zboot/common/au1k_uart.c linux-new/arch/mips/zboot/common/au1k_uart.c
+--- linux/arch/mips/zboot/common/au1k_uart.c 1969-12-31 19:00:00.000000000 -0500
++++ linux-new/arch/mips/zboot/common/au1k_uart.c 2003-12-18 14:26:20.000000000 -0500
+@@ -0,0 +1,103 @@
++/*
++ * BRIEF MODULE DESCRIPTION
++ * Simple Au1000 uart routines.
++ *
++ * Copyright 2001 MontaVista Software Inc.
++ * Author: MontaVista Software, Inc.
++ * ppopov@mvista.com or source@mvista.com
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version.
++ *
++ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
++ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
++ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
++ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
++ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
++ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
++ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
++ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
++ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++ *
++ * You should have received a copy of the GNU General Public License along
++ * with this program; if not, write to the Free Software Foundation, Inc.,
++ * 675 Mass Ave, Cambridge, MA 02139, USA.
++ */
++#include <linux/config.h>
++#include <asm/io.h>
++#include <asm/au1000.h>
++#include "ns16550.h"
++
++typedef unsigned char uint8;
++typedef unsigned int uint32;
++
++#define UART16550_BAUD_2400 2400
++#define UART16550_BAUD_4800 4800
++#define UART16550_BAUD_9600 9600
++#define UART16550_BAUD_19200 19200
++#define UART16550_BAUD_38400 38400
++#define UART16550_BAUD_57600 57600
++#define UART16550_BAUD_115200 115200
++
++#define UART16550_PARITY_NONE 0
++#define UART16550_PARITY_ODD 0x08
++#define UART16550_PARITY_EVEN 0x18
++#define UART16550_PARITY_MARK 0x28
++#define UART16550_PARITY_SPACE 0x38
++
++#define UART16550_DATA_5BIT 0x0
++#define UART16550_DATA_6BIT 0x1
++#define UART16550_DATA_7BIT 0x2
++#define UART16550_DATA_8BIT 0x3
++
++#define UART16550_STOP_1BIT 0x0
++#define UART16550_STOP_2BIT 0x4
++
++/* It would be nice if we had a better way to do this.
++ * It could be a variable defined in one of the board specific files.
++ */
++#undef UART_BASE
++#ifdef CONFIG_COGENT_CSB250
++#define UART_BASE UART3_ADDR
++#else
++#define UART_BASE UART0_ADDR
++#endif
++
++/* memory-mapped read/write of the port */
++#define UART16550_READ(y) (readl(UART_BASE + y) & 0xff)
++#define UART16550_WRITE(y,z) (writel(z&0xff, UART_BASE + y))
++
++/*
++ * We use uart 0, which is already initialized by
++ * yamon.
++ */
++volatile struct NS16550 *
++serial_init(int chan)
++{
++ volatile struct NS16550 *com_port;
++ com_port = (struct NS16550 *) UART_BASE;
++ return (com_port);
++}
++
++void
++serial_putc(volatile struct NS16550 *com_port, unsigned char c)
++{
++ while ((UART16550_READ(UART_LSR)&0x40) == 0);
++ UART16550_WRITE(UART_TX, c);
++}
++
++unsigned char
++serial_getc(volatile struct NS16550 *com_port)
++{
++ while((UART16550_READ(UART_LSR) & 0x1) == 0);
++ return UART16550_READ(UART_RX);
++}
++
++int
++serial_tstc(volatile struct NS16550 *com_port)
++{
++ return((UART16550_READ(UART_LSR) & LSR_DR) != 0);
++}
+diff -Naru linux/arch/mips/zboot/common/ctype.c linux-new/arch/mips/zboot/common/ctype.c
+--- linux/arch/mips/zboot/common/ctype.c 1969-12-31 19:00:00.000000000 -0500
++++ linux-new/arch/mips/zboot/common/ctype.c 2003-12-18 14:26:20.000000000 -0500
+@@ -0,0 +1,35 @@
++/*
++ * linux/lib/ctype.c
++ *
++ * Copyright (C) 1991, 1992 Linus Torvalds
++ */
++
++#include <linux/ctype.h>
++
++unsigned char _ctype[] = {
++_C,_C,_C,_C,_C,_C,_C,_C, /* 0-7 */
++_C,_C|_S,_C|_S,_C|_S,_C|_S,_C|_S,_C,_C, /* 8-15 */
++_C,_C,_C,_C,_C,_C,_C,_C, /* 16-23 */
++_C,_C,_C,_C,_C,_C,_C,_C, /* 24-31 */
++_S|_SP,_P,_P,_P,_P,_P,_P,_P, /* 32-39 */
++_P,_P,_P,_P,_P,_P,_P,_P, /* 40-47 */
++_D,_D,_D,_D,_D,_D,_D,_D, /* 48-55 */
++_D,_D,_P,_P,_P,_P,_P,_P, /* 56-63 */
++_P,_U|_X,_U|_X,_U|_X,_U|_X,_U|_X,_U|_X,_U, /* 64-71 */
++_U,_U,_U,_U,_U,_U,_U,_U, /* 72-79 */
++_U,_U,_U,_U,_U,_U,_U,_U, /* 80-87 */
++_U,_U,_U,_P,_P,_P,_P,_P, /* 88-95 */
++_P,_L|_X,_L|_X,_L|_X,_L|_X,_L|_X,_L|_X,_L, /* 96-103 */
++_L,_L,_L,_L,_L,_L,_L,_L, /* 104-111 */
++_L,_L,_L,_L,_L,_L,_L,_L, /* 112-119 */
++_L,_L,_L,_P,_P,_P,_P,_C, /* 120-127 */
++0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 128-143 */
++0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 144-159 */
++_S|_SP,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P, /* 160-175 */
++_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P, /* 176-191 */
++_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U, /* 192-207 */
++_U,_U,_U,_U,_U,_U,_U,_P,_U,_U,_U,_U,_U,_U,_U,_L, /* 208-223 */
++_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L, /* 224-239 */
++_L,_L,_L,_L,_L,_L,_L,_P,_L,_L,_L,_L,_L,_L,_L,_L}; /* 240-255 */
++
++
+diff -Naru linux/arch/mips/zboot/common/dummy.c linux-new/arch/mips/zboot/common/dummy.c
+--- linux/arch/mips/zboot/common/dummy.c 1969-12-31 19:00:00.000000000 -0500
++++ linux-new/arch/mips/zboot/common/dummy.c 2003-12-18 14:26:20.000000000 -0500
+@@ -0,0 +1,4 @@
++int main(void)
++{
++ return 0;
++}
+diff -Naru linux/arch/mips/zboot/common/Makefile linux-new/arch/mips/zboot/common/Makefile
+--- linux/arch/mips/zboot/common/Makefile 1969-12-31 19:00:00.000000000 -0500
++++ linux-new/arch/mips/zboot/common/Makefile 2003-12-18 14:26:20.000000000 -0500
+@@ -0,0 +1,27 @@
++#
++# arch/mips/zboot/common/Makefile
++#
++# This file is subject to the terms and conditions of the GNU General Public
++# License. See the file "COPYING" in the main directory of this archive
++# for more details.
++#
++# Tom Rini January 2001
++#
++
++.c.s:
++ $(CC) $(CFLAGS) -S -o $*.s $<
++.s.o:
++ $(AS) -o $*.o $<
++.c.o:
++ $(CC) $(CFLAGS) -c -o $*.o $<
++.S.s:
++ $(CPP) $(AFLAGS) -o $*.o $<
++.S.o:
++ $(CC) $(AFLAGS) -c -o $*.o $<
++
++clean:
++ rm -rf *.o
++
++OBJCOPY_ARGS = -O elf32-tradlittlemips
++
++include $(TOPDIR)/Rules.make
+diff -Naru linux/arch/mips/zboot/common/misc-common.c linux-new/arch/mips/zboot/common/misc-common.c
+--- linux/arch/mips/zboot/common/misc-common.c 1969-12-31 19:00:00.000000000 -0500
++++ linux-new/arch/mips/zboot/common/misc-common.c 2003-12-18 14:26:20.000000000 -0500
+@@ -0,0 +1,437 @@
++/*
++ * arch/mips/zboot/common/misc-common.c
++ *
++ * Misc. bootloader code (almost) all platforms can use
++ *
++ * Author: Johnnie Peters <jpeters@mvista.com>
++ * Editor: Tom Rini <trini@mvista.com>
++ *
++ * Derived from arch/ppc/boot/prep/misc.c
++ *
++ * Ported by Pete Popov <ppopov@mvista.com> to
++ * support mips board(s). I also got rid of the vga console
++ * code.
++ *
++ * Copyright 2000-2001 MontaVista Software Inc.
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version.
++ *
++ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
++ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
++ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
++ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
++ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
++ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
++ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
++ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
++ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++ *
++ * You should have received a copy of the GNU General Public License along
++ * with this program; if not, write to the Free Software Foundation, Inc.,
++ * 675 Mass Ave, Cambridge, MA 02139, USA.
++ */
++
++#include <linux/config.h>
++#include "zlib.h"
++#include <stdarg.h>
++
++extern char *avail_ram;
++extern char *end_avail;
++extern char _end[];
++
++void puts(const char *);
++void putc(const char c);
++void puthex(unsigned long val);
++void _bcopy(char *src, char *dst, int len);
++void gunzip(void *, int, unsigned char *, int *);
++static int _cvt(unsigned long val, char *buf, long radix, char *digits);
++
++void _vprintk(void(*)(const char), const char *, va_list ap);
++
++struct NS16550 *com_port;
++
++int serial_tstc(volatile struct NS16550 *);
++unsigned char serial_getc(volatile struct NS16550 *);
++void serial_putc(volatile struct NS16550 *, unsigned char);
++
++void pause(void)
++{
++ puts("pause\n");
++}
++
++void exit(void)
++{
++ puts("exit\n");
++ while(1);
++}
++
++int tstc(void)
++{
++ return (serial_tstc(com_port));
++}
++
++int getc(void)
++{
++ while (1) {
++ if (serial_tstc(com_port))
++ return (serial_getc(com_port));
++ }
++}
++
++void
++putc(const char c)
++{
++ int x,y;
++
++ serial_putc(com_port, c);
++ if ( c == '\n' )
++ serial_putc(com_port, '\r');
++}
++
++void puts(const char *s)
++{
++ char c;
++ while ( ( c = *s++ ) != '\0' ) {
++ serial_putc(com_port, c);
++ if ( c == '\n' ) serial_putc(com_port, '\r');
++ }
++}
++
++void error(char *x)
++{
++ puts("\n\n");
++ puts(x);
++ puts("\n\n -- System halted");
++
++ while(1); /* Halt */
++}
++
++void *zalloc(void *x, unsigned items, unsigned size)
++{
++ void *p = avail_ram;
++
++ size *= items;
++ size = (size + 7) & -8;
++ avail_ram += size;
++ if (avail_ram > end_avail) {
++ puts("oops... out of memory\n");
++ pause();
++ }
++ return p;
++}
++
++void zfree(void *x, void *addr, unsigned nb)
++{
++}
++
++#define HEAD_CRC 2
++#define EXTRA_FIELD 4
++#define ORIG_NAME 8
++#define COMMENT 0x10
++#define RESERVED 0xe0
++
++#define DEFLATED 8
++
++void gunzip(void *dst, int dstlen, unsigned char *src, int *lenp)
++{
++ z_stream s;
++ int r, i, flags;
++
++ /* skip header */
++ i = 10;
++ flags = src[3];
++ if (src[2] != DEFLATED || (flags & RESERVED) != 0) {
++ puts("bad gzipped data\n");
++ exit();
++ }
++ if ((flags & EXTRA_FIELD) != 0)
++ i = 12 + src[10] + (src[11] << 8);
++ if ((flags & ORIG_NAME) != 0)
++ while (src[i++] != 0)
++ ;
++ if ((flags & COMMENT) != 0)
++ while (src[i++] != 0)
++ ;
++ if ((flags & HEAD_CRC) != 0)
++ i += 2;
++ if (i >= *lenp) {
++ puts("gunzip: ran out of data in header\n");
++ exit();
++ }
++
++ s.zalloc = zalloc;
++ s.zfree = zfree;
++ r = inflateInit2(&s, -MAX_WBITS);
++ if (r != Z_OK) {
++ puts("inflateInit2 returned %d\n");
++ exit();
++ }
++ s.next_in = src + i;
++ s.avail_in = *lenp - i;
++ s.next_out = dst;
++ s.avail_out = dstlen;
++ r = inflate(&s, Z_FINISH);
++ if (r != Z_OK && r != Z_STREAM_END) {
++ puts("inflate returned %d\n");
++ exit();
++ }
++ *lenp = s.next_out - (unsigned char *) dst;
++ inflateEnd(&s);
++}
++
++void
++puthex(unsigned long val)
++{
++
++ unsigned char buf[10];
++ int i;
++ for (i = 7; i >= 0; i--)
++ {
++ buf[i] = "0123456789ABCDEF"[val & 0x0F];
++ val >>= 4;
++ }
++ buf[8] = '\0';
++ puts(buf);
++}
++
++#define FALSE 0
++#define TRUE 1
++
++void
++_printk(char const *fmt, ...)
++{
++ va_list ap;
++
++ va_start(ap, fmt);
++ _vprintk(putc, fmt, ap);
++ va_end(ap);
++ return;
++}
++
++#define is_digit(c) ((c >= '0') && (c <= '9'))
++
++void
++_vprintk(void(*putc)(const char), const char *fmt0, va_list ap)
++{
++ char c, sign, *cp = 0;
++ int left_prec, right_prec, zero_fill, length = 0, pad, pad_on_right;
++ char buf[32];
++ long val;
++ while ((c = *fmt0++))
++ {
++ if (c == '%')
++ {
++ c = *fmt0++;
++ left_prec = right_prec = pad_on_right = 0;
++ if (c == '-')
++ {
++ c = *fmt0++;
++ pad_on_right++;
++ }
++ if (c == '0')
++ {
++ zero_fill = TRUE;
++ c = *fmt0++;
++ } else
++ {
++ zero_fill = FALSE;
++ }
++ while (is_digit(c))
++ {
++ left_prec = (left_prec * 10) + (c - '0');
++ c = *fmt0++;
++ }
++ if (c == '.')
++ {
++ c = *fmt0++;
++ zero_fill++;
++ while (is_digit(c))
++ {
++ right_prec = (right_prec * 10) + (c - '0');
++ c = *fmt0++;
++ }
++ } else
++ {
++ right_prec = left_prec;
++ }
++ sign = '\0';
++ switch (c)
++ {
++ case 'd':
++ case 'x':
++ case 'X':
++ val = va_arg(ap, long);
++ switch (c)
++ {
++ case 'd':
++ if (val < 0)
++ {
++ sign = '-';
++ val = -val;
++ }
++ length = _cvt(val, buf, 10, "0123456789");
++ break;
++ case 'x':
++ length = _cvt(val, buf, 16, "0123456789abcdef");
++ break;
++ case 'X':
++ length = _cvt(val, buf, 16, "0123456789ABCDEF");
++ break;
++ }
++ cp = buf;
++ break;
++ case 's':
++ cp = va_arg(ap, char *);
++ length = strlen(cp);
++ break;
++ case 'c':
++ c = va_arg(ap, long /*char*/);
++ (*putc)(c);
++ continue;
++ default:
++ (*putc)('?');
++ }
++ pad = left_prec - length;
++ if (sign != '\0')
++ {
++ pad--;
++ }
++ if (zero_fill)
++ {
++ c = '0';
++ if (sign != '\0')
++ {
++ (*putc)(sign);
++ sign = '\0';
++ }
++ } else
++ {
++ c = ' ';
++ }
++ if (!pad_on_right)
++ {
++ while (pad-- > 0)
++ {
++ (*putc)(c);
++ }
++ }
++ if (sign != '\0')
++ {
++ (*putc)(sign);
++ }
++ while (length-- > 0)
++ {
++ (*putc)(c = *cp++);
++ if (c == '\n')
++ {
++ (*putc)('\r');
++ }
++ }
++ if (pad_on_right)
++ {
++ while (pad-- > 0)
++ {
++ (*putc)(c);
++ }
++ }
++ } else
++ {
++ (*putc)(c);
++ if (c == '\n')
++ {
++ (*putc)('\r');
++ }
++ }
++ }
++}
++
++int
++_cvt(unsigned long val, char *buf, long radix, char *digits)
++{
++ char temp[80];
++ char *cp = temp;
++ int length = 0;
++ if (val == 0)
++ { /* Special case */
++ *cp++ = '0';
++ } else
++ while (val)
++ {
++ *cp++ = digits[val % radix];
++ val /= radix;
++ }
++ while (cp != temp)
++ {
++ *buf++ = *--cp;
++ length++;
++ }
++ *buf = '\0';
++ return (length);
++}
++
++void
++_dump_buf_with_offset(unsigned char *p, int s, unsigned char *base)
++{
++ int i, c;
++ if ((unsigned int)s > (unsigned int)p)
++ {
++ s = (unsigned int)s - (unsigned int)p;
++ }
++ while (s > 0)
++ {
++ if (base)
++ {
++ _printk("%06X: ", (int)p - (int)base);
++ } else
++ {
++ _printk("%06X: ", p);
++ }
++ for (i = 0; i < 16; i++)
++ {
++ if (i < s)
++ {
++ _printk("%02X", p[i] & 0xFF);
++ } else
++ {
++ _printk(" ");
++ }
++ if ((i % 2) == 1) _printk(" ");
++ if ((i % 8) == 7) _printk(" ");
++ }
++ _printk(" |");
++ for (i = 0; i < 16; i++)
++ {
++ if (i < s)
++ {
++ c = p[i] & 0xFF;
++ if ((c < 0x20) || (c >= 0x7F)) c = '.';
++ } else
++ {
++ c = ' ';
++ }
++ _printk("%c", c);
++ }
++ _printk("|\n");
++ s -= 16;
++ p += 16;
++ }
++}
++
++void
++_dump_buf(unsigned char *p, int s)
++{
++ _printk("\n");
++ _dump_buf_with_offset(p, s, 0);
++}
++
++/*
++ * Local variables:
++ * c-indent-level: 8
++ * c-basic-offset: 8
++ * tab-width: 8
++ * End:
++ */
+diff -Naru linux/arch/mips/zboot/common/misc-simple.c linux-new/arch/mips/zboot/common/misc-simple.c
+--- linux/arch/mips/zboot/common/misc-simple.c 1969-12-31 19:00:00.000000000 -0500
++++ linux-new/arch/mips/zboot/common/misc-simple.c 2003-12-18 14:26:20.000000000 -0500
+@@ -0,0 +1,127 @@
++/*
++ * arch/mips/zboot/common/misc-simple.c
++ *
++ * Misc. bootloader code for many machines. This assumes you have are using
++ * a 6xx/7xx/74xx CPU in your machine. This assumes the chunk of memory
++ * below 8MB is free. Finally, it assumes you have a NS16550-style uart for
++ * your serial console. If a machine meets these requirements, it can quite
++ * likely use this code during boot.
++ *
++ * Author: Matt Porter <mporter@mvista.com>
++ * Derived from arch/ppc/boot/prep/misc.c
++ *
++ * Copyright 2001 MontaVista Software Inc.
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version.
++ */
++
++#include <linux/types.h>
++#include <linux/elf.h>
++#include <linux/config.h>
++
++#include <asm/page.h>
++#include <asm/processor.h>
++#include <asm/mmu.h>
++
++#include "zlib.h"
++
++extern struct NS16550 *com_port;
++
++char *avail_ram;
++char *end_avail;
++extern char _end[];
++char *zimage_start;
++
++#ifdef CONFIG_CMDLINE
++#define CMDLINE CONFIG_CMDLINE
++#else
++#define CMDLINE ""
++#endif
++char cmd_preset[] = CMDLINE;
++char cmd_buf[256];
++char *cmd_line = cmd_buf;
++
++/* The linker tells us where the image is.
++*/
++extern unsigned char __image_begin, __image_end;
++extern unsigned char __ramdisk_begin, __ramdisk_end;
++unsigned long initrd_size;
++
++extern void puts(const char *);
++extern void putc(const char c);
++extern void puthex(unsigned long val);
++extern void *memcpy(void * __dest, __const void * __src,
++ __kernel_size_t __n);
++extern void gunzip(void *, int, unsigned char *, int *);
++extern void udelay(long delay);
++extern int tstc(void);
++extern int getc(void);
++extern volatile struct NS16550 *serial_init(int chan);
++
++void
++decompress_kernel(unsigned long load_addr, int num_words,
++ unsigned long cksum, unsigned long *sp)
++{
++ int timer = 0;
++ extern unsigned long start;
++ char *cp, ch;
++ int i;
++ int zimage_size;
++
++ com_port = (struct NS16550 *)serial_init(0);
++
++ initrd_size = (unsigned long)(&__ramdisk_end) -
++ (unsigned long)(&__ramdisk_begin);
++
++ /*
++ * Reveal where we were loaded at and where we
++ * were relocated to.
++ */
++ puts("loaded at: "); puthex(load_addr);
++ puts(" "); puthex((unsigned long)(load_addr + (4*num_words))); puts("\n");
++ if ( (unsigned long)load_addr != (unsigned long)&start )
++ {
++ puts("relocated to: "); puthex((unsigned long)&start);
++ puts(" ");
++ puthex((unsigned long)((unsigned long)&start + (4*num_words)));
++ puts("\n");
++ }
++
++ /*
++ * We link ourself to an arbitrary low address. When we run, we
++ * relocate outself to that address. __image_being points to
++ * the part of the image where the zImage is. -- Tom
++ */
++ zimage_start = (char *)(unsigned long)(&__image_begin);
++ zimage_size = (unsigned long)(&__image_end) -
++ (unsigned long)(&__image_begin);
++
++ /*
++ * The zImage and initrd will be between start and _end, so they've
++ * already been moved once. We're good to go now. -- Tom
++ */
++ puts("zimage at: "); puthex((unsigned long)zimage_start);
++ puts(" "); puthex((unsigned long)(zimage_size+zimage_start));
++ puts("\n");
++
++ if ( initrd_size ) {
++ puts("initrd at: ");
++ puthex((unsigned long)(&__ramdisk_begin));
++ puts(" "); puthex((unsigned long)(&__ramdisk_end));puts("\n");
++ }
++
++ /* assume the chunk below 8M is free */
++ avail_ram = (char *)AVAIL_RAM_START;
++ end_avail = (char *)AVAIL_RAM_END;
++
++ /* Display standard Linux/MIPS boot prompt for kernel args */
++ puts("Uncompressing Linux at load address ");
++ puthex(LOADADDR);
++ puts("\n");
++ /* I don't like this hard coded gunzip size (fixme) */
++ gunzip((void *)LOADADDR, 0x400000, zimage_start, &zimage_size);
++ puts("Now booting the kernel\n");
++}
+diff -Naru linux/arch/mips/zboot/common/no_initrd.c linux-new/arch/mips/zboot/common/no_initrd.c
+--- linux/arch/mips/zboot/common/no_initrd.c 1969-12-31 19:00:00.000000000 -0500
++++ linux-new/arch/mips/zboot/common/no_initrd.c 2003-12-18 14:26:20.000000000 -0500
+@@ -0,0 +1,2 @@
++char initrd_data[1];
++int initrd_len = 0;
+diff -Naru linux/arch/mips/zboot/common/ns16550.c linux-new/arch/mips/zboot/common/ns16550.c
+--- linux/arch/mips/zboot/common/ns16550.c 1969-12-31 19:00:00.000000000 -0500
++++ linux-new/arch/mips/zboot/common/ns16550.c 2003-12-18 14:26:20.000000000 -0500
+@@ -0,0 +1,57 @@
++/*
++ * NS16550 support
++ */
++
++#include <linux/config.h>
++#include <asm/serial.h>
++#include "ns16550.h"
++
++typedef struct NS16550 *NS16550_t;
++
++const NS16550_t COM_PORTS[] = { (NS16550_t) COM1,
++ (NS16550_t) COM2,
++ (NS16550_t) COM3,
++ (NS16550_t) COM4 };
++
++volatile struct NS16550 *
++serial_init(int chan)
++{
++ volatile struct NS16550 *com_port;
++ com_port = (struct NS16550 *) COM_PORTS[chan];
++ /* See if port is present */
++ com_port->lcr = 0x00;
++ com_port->ier = 0xFF;
++#if 0
++ if (com_port->ier != 0x0F) return ((struct NS16550 *)0);
++#endif
++ com_port->ier = 0x00;
++ com_port->lcr = 0x80; /* Access baud rate */
++#ifdef CONFIG_SERIAL_CONSOLE_NONSTD
++ com_port->dll = (BASE_BAUD / CONFIG_SERIAL_CONSOLE_BAUD);
++ com_port->dlm = (BASE_BAUD / CONFIG_SERIAL_CONSOLE_BAUD) >> 8;
++#endif
++ com_port->lcr = 0x03; /* 8 data, 1 stop, no parity */
++ com_port->mcr = 0x03; /* RTS/DTR */
++ com_port->fcr = 0x07; /* Clear & enable FIFOs */
++ return (com_port);
++}
++
++void
++serial_putc(volatile struct NS16550 *com_port, unsigned char c)
++{
++ while ((com_port->lsr & LSR_THRE) == 0) ;
++ com_port->thr = c;
++}
++
++unsigned char
++serial_getc(volatile struct NS16550 *com_port)
++{
++ while ((com_port->lsr & LSR_DR) == 0) ;
++ return (com_port->rbr);
++}
++
++int
++serial_tstc(volatile struct NS16550 *com_port)
++{
++ return ((com_port->lsr & LSR_DR) != 0);
++}
+--- linux/arch/mips/zboot/common/string.c 1969-12-31 19:00:00.000000000 -0500
++++ linux-new/arch/mips/zboot/common/string.c 2003-12-18 17:11:10.000000000 -0500
+@@ -0,0 +1,497 @@
++/*
++ * linux/lib/string.c
++ *
++ * Copyright (C) 1991, 1992 Linus Torvalds
++ */
++
++/*
++ * stupid library routines.. The optimized versions should generally be found
++ * as inline code in <asm-xx/string.h>
++ *
++ * These are buggy as well..
++ *
++ * * Fri Jun 25 1999, Ingo Oeser <ioe@informatik.tu-chemnitz.de>
++ * - Added strsep() which will replace strtok() soon (because strsep() is
++ * reentrant and should be faster). Use only strsep() in new code, please.
++ */
++
++#include <linux/types.h>
++#include <linux/string.h>
++#include <linux/ctype.h>
++
++/**
++ * strnicmp - Case insensitive, length-limited string comparison
++ * @s1: One string
++ * @s2: The other string
++ * @len: the maximum number of characters to compare
++ */
++int strnicmp(const char *s1, const char *s2, size_t len)
++{
++ /* Yes, Virginia, it had better be unsigned */
++ unsigned char c1, c2;
++
++ c1 = 0; c2 = 0;
++ if (len) {
++ do {
++ c1 = *s1; c2 = *s2;
++ s1++; s2++;
++ if (!c1)
++ break;
++ if (!c2)
++ break;
++ if (c1 == c2)
++ continue;
++ c1 = tolower(c1);
++ c2 = tolower(c2);
++ if (c1 != c2)
++ break;
++ } while (--len);
++ }
++ return (int)c1 - (int)c2;
++}
++
++char * ___strtok;
++
++#ifndef __HAVE_ARCH_STRCPY
++/**
++ * strcpy - Copy a %NUL terminated string
++ * @dest: Where to copy the string to
++ * @src: Where to copy the string from
++ */
++char * strcpy(char * dest,const char *src)
++{
++ char *tmp = dest;
++
++ while ((*dest++ = *src++) != '\0')
++ /* nothing */;
++ return tmp;
++}
++#endif
++
++#ifndef __HAVE_ARCH_STRNCPY
++/**
++ * strncpy - Copy a length-limited, %NUL-terminated string
++ * @dest: Where to copy the string to
++ * @src: Where to copy the string from
++ * @count: The maximum number of bytes to copy
++ *
++ * Note that unlike userspace strncpy, this does not %NUL-pad the buffer.
++ * However, the result is not %NUL-terminated if the source exceeds
++ * @count bytes.
++ */
++char * strncpy(char * dest,const char *src,size_t count)
++{
++ char *tmp = dest;
++
++ while (count-- && (*dest++ = *src++) != '\0')
++ /* nothing */;
++
++ return tmp;
++}
++#endif
++
++/**
++ * strcat - Append one %NUL-terminated string to another
++ * @dest: The string to be appended to
++ * @src: The string to append to it
++ */
++char * strcat(char * dest, const char * src)
++{
++ char *tmp = dest;
++
++ while (*dest)
++ dest++;
++ while ((*dest++ = *src++) != '\0')
++ ;
++
++ return tmp;
++}
++
++/**
++ * strncat - Append a length-limited, %NUL-terminated string to another
++ * @dest: The string to be appended to
++ * @src: The string to append to it
++ * @count: The maximum numbers of bytes to copy
++ *
++ * Note that in contrast to strncpy, strncat ensures the result is
++ * terminated.
++ */
++char * strncat(char *dest, const char *src, size_t count)
++{
++ char *tmp = dest;
++
++ if (count) {
++ while (*dest)
++ dest++;
++ while ((*dest++ = *src++)) {
++ if (--count == 0) {
++ *dest = '\0';
++ break;
++ }
++ }
++ }
++
++ return tmp;
++}
++
++#ifndef __HAVE_ARCH_STRCMP
++/**
++ * strcmp - Compare two strings
++ * @cs: One string
++ * @ct: Another string
++ */
++int strcmp(const char * cs,const char * ct)
++{
++ register signed char __res;
++
++ while (1) {
++ if ((__res = *cs - *ct++) != 0 || !*cs++)
++ break;
++ }
++
++ return __res;
++}
++#endif
++
++#ifndef __HAVE_ARCH_STRNCMP
++/**
++ * strncmp - Compare two length-limited strings
++ * @cs: One string
++ * @ct: Another string
++ * @count: The maximum number of bytes to compare
++ */
++int strncmp(const char * cs,const char * ct,size_t count)
++{
++ register signed char __res = 0;
++
++ while (count) {
++ if ((__res = *cs - *ct++) != 0 || !*cs++)
++ break;
++ count--;
++ }
++
++ return __res;
++}
++#endif
++
++/**
++ * strchr - Find the first occurrence of a character in a string
++ * @s: The string to be searched
++ * @c: The character to search for
++ */
++char * strchr(const char * s, int c)
++{
++ for(; *s != (char) c; ++s)
++ if (*s == '\0')
++ return NULL;
++ return (char *) s;
++}
++
++/**
++ * strrchr - Find the last occurrence of a character in a string
++ * @s: The string to be searched
++ * @c: The character to search for
++ */
++char * strrchr(const char * s, int c)
++{
++ const char *p = s + strlen(s);
++ do {
++ if (*p == (char)c)
++ return (char *)p;
++ } while (--p >= s);
++ return NULL;
++}
++
++/**
++ * strlen - Find the length of a string
++ * @s: The string to be sized
++ */
++size_t strlen(const char * s)
++{
++ const char *sc;
++
++ for (sc = s; *sc != '\0'; ++sc)
++ /* nothing */;
++ return sc - s;
++}
++
++/**
++ * strnlen - Find the length of a length-limited string
++ * @s: The string to be sized
++ * @count: The maximum number of bytes to search
++ */
++size_t strnlen(const char * s, size_t count)
++{
++ const char *sc;
++
++ for (sc = s; count-- && *sc != '\0'; ++sc)
++ /* nothing */;
++ return sc - s;
++}
++
++/**
++ * strspn - Calculate the length of the initial substring of @s which only
++ * contain letters in @accept
++ * @s: The string to be searched
++ * @accept: The string to search for
++ */
++size_t strspn(const char *s, const char *accept)
++{
++ const char *p;
++ const char *a;
++ size_t count = 0;
++
++ for (p = s; *p != '\0'; ++p) {
++ for (a = accept; *a != '\0'; ++a) {
++ if (*p == *a)
++ break;
++ }
++ if (*a == '\0')
++ return count;
++ ++count;
++ }
++
++ return count;
++}
++
++/**
++ * strpbrk - Find the first occurrence of a set of characters
++ * @cs: The string to be searched
++ * @ct: The characters to search for
++ */
++char * strpbrk(const char * cs,const char * ct)
++{
++ const char *sc1,*sc2;
++
++ for( sc1 = cs; *sc1 != '\0'; ++sc1) {
++ for( sc2 = ct; *sc2 != '\0'; ++sc2) {
++ if (*sc1 == *sc2)
++ return (char *) sc1;
++ }
++ }
++ return NULL;
++}
++
++/**
++ * strtok - Split a string into tokens
++ * @s: The string to be searched
++ * @ct: The characters to search for
++ *
++ * WARNING: strtok is deprecated, use strsep instead.
++ */
++char * strtok(char * s,const char * ct)
++{
++ char *sbegin, *send;
++
++ sbegin = s ? s : ___strtok;
++ if (!sbegin) {
++ return NULL;
++ }
++ sbegin += strspn(sbegin,ct);
++ if (*sbegin == '\0') {
++ ___strtok = NULL;
++ return( NULL );
++ }
++ send = strpbrk( sbegin, ct);
++ if (send && *send != '\0')
++ *send++ = '\0';
++ ___strtok = send;
++ return (sbegin);
++}
++
++/**
++ * strsep - Split a string into tokens
++ * @s: The string to be searched
++ * @ct: The characters to search for
++ *
++ * strsep() updates @s to point after the token, ready for the next call.
++ *
++ * It returns empty tokens, too, behaving exactly like the libc function
++ * of that name. In fact, it was stolen from glibc2 and de-fancy-fied.
++ * Same semantics, slimmer shape. ;)
++ */
++char * strsep(char **s, const char *ct)
++{
++ char *sbegin = *s, *end;
++
++ if (sbegin == NULL)
++ return NULL;
++
++ end = strpbrk(sbegin, ct);
++ if (end)
++ *end++ = '\0';
++ *s = end;
++
++ return sbegin;
++}
++
++/**
++ * memset - Fill a region of memory with the given value
++ * @s: Pointer to the start of the area.
++ * @c: The byte to fill the area with
++ * @count: The size of the area.
++ *
++ * Do not use memset() to access IO space, use memset_io() instead.
++ */
++void * memset(void * s,int c, size_t count)
++{
++ char *xs = (char *) s;
++
++ while (count--)
++ *xs++ = c;
++
++ return s;
++}
++
++/**
++ * bcopy - Copy one area of memory to another
++ * @src: Where to copy from
++ * @dest: Where to copy to
++ * @count: The size of the area.
++ *
++ * Note that this is the same as memcpy(), with the arguments reversed.
++ * memcpy() is the standard, bcopy() is a legacy BSD function.
++ *
++ * You should not use this function to access IO space, use memcpy_toio()
++ * or memcpy_fromio() instead.
++ */
++char * bcopy(const char * src, char * dest, int count)
++{
++ char *tmp = dest;
++
++ while (count--)
++ *tmp++ = *src++;
++
++ return dest;
++}
++
++/**
++ * memcpy - Copy one area of memory to another
++ * @dest: Where to copy to
++ * @src: Where to copy from
++ * @count: The size of the area.
++ *
++ * You should not use this function to access IO space, use memcpy_toio()
++ * or memcpy_fromio() instead.
++ */
++void * memcpy(void * dest,const void *src,size_t count)
++{
++ char *tmp = (char *) dest, *s = (char *) src;
++
++ while (count--)
++ *tmp++ = *s++;
++
++ return dest;
++}
++
++/**
++ * memmove - Copy one area of memory to another
++ * @dest: Where to copy to
++ * @src: Where to copy from
++ * @count: The size of the area.
++ *
++ * Unlike memcpy(), memmove() copes with overlapping areas.
++ */
++void * memmove(void * dest,const void *src,size_t count)
++{
++ char *tmp, *s;
++
++ if (dest <= src) {
++ tmp = (char *) dest;
++ s = (char *) src;
++ while (count--)
++ *tmp++ = *s++;
++ }
++ else {
++ tmp = (char *) dest + count;
++ s = (char *) src + count;
++ while (count--)
++ *--tmp = *--s;
++ }
++
++ return dest;
++}
++
++/**
++ * memcmp - Compare two areas of memory
++ * @cs: One area of memory
++ * @ct: Another area of memory
++ * @count: The size of the area.
++ */
++int memcmp(const void * cs,const void * ct,size_t count)
++{
++ const unsigned char *su1, *su2;
++ signed char res = 0;
++
++ for( su1 = cs, su2 = ct; 0 < count; ++su1, ++su2, count--)
++ if ((res = *su1 - *su2) != 0)
++ break;
++ return res;
++}
++
++#ifndef __HAVE_ARCH_MEMSCAN
++/**
++ * memscan - Find a character in an area of memory.
++ * @addr: The memory area
++ * @c: The byte to search for
++ * @size: The size of the area.
++ *
++ * returns the address of the first occurrence of @c, or 1 byte past
++ * the area if @c is not found
++ */
++void * memscan(void * addr, int c, size_t size)
++{
++ unsigned char * p = (unsigned char *) addr;
++ unsigned char * e = p + size;
++
++ while (p != e) {
++ if (*p == c)
++ return (void *) p;
++ p++;
++ }
++
++ return (void *) p;
++}
++#endif
++
++/**
++ * strstr - Find the first substring in a %NUL terminated string
++ * @s1: The string to be searched
++ * @s2: The string to search for
++ */
++char * strstr(const char * s1,const char * s2)
++{
++ int l1, l2;
++
++ l2 = strlen(s2);
++ if (!l2)
++ return (char *) s1;
++ l1 = strlen(s1);
++ while (l1 >= l2) {
++ l1--;
++ if (!memcmp(s1,s2,l2))
++ return (char *) s1;
++ s1++;
++ }
++ return NULL;
++}
++
++/**
++ * memchr - Find a character in an area of memory.
++ * @s: The memory area
++ * @c: The byte to search for
++ * @n: The size of the area.
++ *
++ * returns the address of the first occurrence of @c, or %NULL
++ * if @c is not found
++ */
++void *memchr(const void *s, int c, size_t n)
++{
++ const unsigned char *p = s;
++ while (n-- != 0) {
++ if ((unsigned char)c == *p++) {
++ return (void *)(p-1);
++ }
++ }
++ return NULL;
++}
+diff -Naru linux/arch/mips/zboot/csb250/head.S linux-new/arch/mips/zboot/csb250/head.S
+--- linux/arch/mips/zboot/csb250/head.S 1969-12-31 19:00:00.000000000 -0500
++++ linux-new/arch/mips/zboot/csb250/head.S 2003-12-18 14:26:20.000000000 -0500
+@@ -0,0 +1,157 @@
++/*
++ * arch/mips/kernel/head.S
++ *
++ * This file is subject to the terms and conditions of the GNU General Public
++ * License. See the file "COPYING" in the main directory of this archive
++ * for more details.
++ *
++ * Copyright (C) 1994, 1995 Waldorf Electronics
++ * Written by Ralf Baechle and Andreas Busse
++ * Copyright (C) 1995 - 1999 Ralf Baechle
++ * Copyright (C) 1996 Paul M. Antoine
++ * Modified for DECStation and hence R3000 support by Paul M. Antoine
++ * Further modifications by David S. Miller and Harald Koerfgen
++ * Copyright (C) 1999 Silicon Graphics, Inc.
++ *
++ * Head.S contains the MIPS exception handler and startup code.
++ *
++ **************************************************************************
++ * 9 Nov, 2000.
++ * Added Cache Error exception handler and SBDDP EJTAG debug exception.
++ *
++ * Kevin Kissell, kevink@mips.com and Carsten Langgaard, carstenl@mips.com
++ * Copyright (C) 2000 MIPS Technologies, Inc. All rights reserved.
++ **************************************************************************
++ */
++#include <linux/config.h>
++#include <linux/threads.h>
++
++#include <asm/asm.h>
++#include <asm/cacheops.h>
++#include <asm/mipsregs.h>
++#include <asm/offset.h>
++#include <asm/cachectl.h>
++#include <asm/regdef.h>
++
++#define IndexInvalidate_I 0x00
++#define IndexWriteBack_D 0x01
++
++ .set noreorder
++ .cprestore
++ LEAF(start)
++start:
++ bal locate
++ nop
++
++ .globl asize /* Someday we'll put the initrd info here. */
++asize: .word 0
++ .word 0
++ .word 0
++ .word 0
++
++locate:
++ subu s8, ra, 8 /* Where we were loaded */
++ la sp, (.stack + 8192)
++
++ move s0, a0 /* Save boot rom start args */
++ move s1, a1
++ move s2, a2
++ move s3, a3
++
++ la a0, start /* Where we were linked to run */
++
++ move a1, s8
++ la a2, _edata
++ subu t1, a2, a0
++ srl t1, t1, 2
++
++ /* copy text section */
++ li t0, 0
++1: lw v0, 0(a1)
++ nop
++ sw v0, 0(a0)
++ xor t0, t0, v0
++ addu a0, 4
++ bne a2, a0, 1b
++ addu a1, 4
++
++ /* Clear BSS */
++ la a0, _edata
++ la a2, _end
++2: sw zero, 0(a0)
++ bne a2, a0, 2b
++ addu a0, 4
++
++ /* push the D-Cache and invalidate I-Cache */
++ li k0, 0x80000000 # start address
++ li k1, 0x80004000 # end address (16KB I-Cache)
++ subu k1, 128
++
++1:
++ .set mips3
++ cache IndexWriteBack_D, 0(k0)
++ cache IndexWriteBack_D, 32(k0)
++ cache IndexWriteBack_D, 64(k0)
++ cache IndexWriteBack_D, 96(k0)
++ cache IndexInvalidate_I, 0(k0)
++ cache IndexInvalidate_I, 32(k0)
++ cache IndexInvalidate_I, 64(k0)
++ cache IndexInvalidate_I, 96(k0)
++ .set mips0
++
++ bne k0, k1, 1b
++ addu k0, k0, 128
++ /* done */
++
++/* move a0, s8 /* load address */
++ subu a0, s8, 0x1000 /* load address */
++ move a1, t1 /* length in words */
++ move a2, t0 /* checksum */
++ move a3, sp
++
++ la ra, 1f
++ la k0, decompress_kernel
++ jr k0
++ nop
++1:
++
++ la a2, __ramdisk_begin
++ la a3, initrd_size
++ lw a0, 0(a2)
++ lw a1, 0(a3)
++ li k0, KERNEL_ENTRY
++ jr k0
++ nop
++3:
++ b 3b
++ END(start)
++
++ LEAF(udelay)
++udelay:
++ END(udelay)
++
++
++ LEAF(FlushCache)
++ li k0, 0x80000000 # start address
++ li k1, 0x80004000 # end address (16KB I-Cache)
++ subu k1, 128
++
++1:
++ .set mips3
++ cache IndexWriteBack_D, 0(k0)
++ cache IndexWriteBack_D, 32(k0)
++ cache IndexWriteBack_D, 64(k0)
++ cache IndexWriteBack_D, 96(k0)
++ cache IndexInvalidate_I, 0(k0)
++ cache IndexInvalidate_I, 32(k0)
++ cache IndexInvalidate_I, 64(k0)
++ cache IndexInvalidate_I, 96(k0)
++ .set mips0
++
++ bne k0, k1, 1b
++ addu k0, k0, 128
++ jr ra
++ nop
++ END(FlushCache)
++
++ .comm .stack,4096*2,4
+diff -Naru linux/arch/mips/zboot/csb250/Makefile linux-new/arch/mips/zboot/csb250/Makefile
+--- linux/arch/mips/zboot/csb250/Makefile 1969-12-31 19:00:00.000000000 -0500
++++ linux-new/arch/mips/zboot/csb250/Makefile 2003-12-18 14:26:20.000000000 -0500
+@@ -0,0 +1,90 @@
++# arch/mips/zboot/pb1xxx/Makefile
++#
++# Makefile for Cogent CSB250 Au1500 board.
++# All of the boot loader code was derived from the ppc
++# boot code.
++#
++# This program is free software; you can redistribute it and/or modify it
++# under the terms of the GNU General Public License as published by the
++# Free Software Foundation; either version 2 of the License, or (at your
++# option) any later version.
++
++.c.s:
++ $(CC) $(CFLAGS) -S -o $*.s $<
++.s.o:
++ $(AS) -o $*.o $<
++.c.o:
++ $(CC) $(CFLAGS) -D__BOOTER__ -c -o $*.o $<
++.S.s:
++ $(CPP) $(AFLAGS) -o $*.o $<
++.S.o:
++ $(CC) $(AFLAGS) -c -o $*.o $<
++
++#########################################################################
++# START BOARD SPECIFIC VARIABLES
++BNAME=csb250
++
++# These two variables control where the zImage is stored
++# in flash and loaded in memory. It only controls how the srec
++# file is generated, the code is the same.
++RAM_RUN_ADDR = 0x80a00000
++FLASH_LOAD_ADDR = 0xBFD00000
++
++# These two variables specify the free ram region
++# that can be used for temporary malloc area
++AVAIL_RAM_START=0x80400000
++AVAIL_RAM_END=0x80800000
++
++# This one must match the LOADADDR in arch/mips/Makefile!
++LOADADDR=0x80100000
++# END BOARD SPECIFIC VARIABLES
++#########################################################################
++
++OBJECTS := head.o ../common/misc-common.o ../common/misc-simple.o \
++ ../common/au1k_uart.o ../common/string.o ../common/ctype.o
++LIBS := ../lib/zlib.a
++
++ENTRY := ../utils/entry
++OFFSET := ../utils/offset
++SIZE := ../utils/size
++
++LD_ARGS := -T ../ld.script -Ttext $(RAM_RUN_ADDR) -Bstatic
++OBJCOPY_ARGS = -O elf32-tradbigmips
++
++all: zImage
++
++clean:
++ rm -rf *.o vmlinux* zvmlinux.* ../images/*.srec
++
++head.o: head.S $(TOPDIR)/vmlinux
++ $(CC) $(AFLAGS) \
++ -DKERNEL_ENTRY=$(shell sh $(ENTRY) $(NM) $(TOPDIR)/vmlinux ) \
++ -c -o $*.o $<
++
++../common/misc-simple.o:
++ $(CC) $(CFLAGS) -DINITRD_OFFSET=0 -DINITRD_SIZE=0 -DZIMAGE_OFFSET=0 \
++ -DAVAIL_RAM_START=$(AVAIL_RAM_START) \
++ -DAVAIL_RAM_END=$(AVAIL_RAM_END) \
++ -DLOADADDR=$(LOADADDR) \
++ -DZIMAGE_SIZE=0 -c -o $@ $*.c
++
++zvmlinux: $(OBJECTS) $(LIBS) ../ld.script ../images/vmlinux.gz ../common/dummy.o
++ $(OBJCOPY) \
++ --add-section=.image=../images/vmlinux.gz \
++ --set-section-flags=.image=contents,alloc,load,readonly,data \
++ ../common/dummy.o image.o
++ $(LD) $(LD_ARGS) -o $@ $(OBJECTS) image.o $(LIBS)
++ $(OBJCOPY) $(OBJCOPY_ARGS) $@ $@ -R .comment -R .stab -R .stabstr \
++ -R .initrd -R .sysmap
++
++# Here we manipulate the image in order to get it the necessary
++# srecord file we need.
++zImage: zvmlinux
++ mv zvmlinux ../images/zImage.$(BNAME)
++ $(OBJCOPY) -O binary ../images/zImage.$(BNAME) ../images/$(BNAME).bin
++
++zImage.flash: zImage
++ $(OBJCOPY) -O srec --adjust-vma 0x3ed00000 \
++ ../images/zImage.$(BNAME) ../images/$(BNAME).flash.srec
++
++include $(TOPDIR)/Rules.make
+diff -Naru linux/arch/mips/zboot/images/Makefile linux-new/arch/mips/zboot/images/Makefile
+--- linux/arch/mips/zboot/images/Makefile 1969-12-31 19:00:00.000000000 -0500
++++ linux-new/arch/mips/zboot/images/Makefile 2003-12-18 14:26:20.000000000 -0500
+@@ -0,0 +1,10 @@
++
++include $(TOPDIR)/Rules.make
++
++vmlinux.gz: $(TOPDIR)/vmlinux
++ $(OBJCOPY) -S -O binary $(TOPDIR)/vmlinux vmlinux
++ gzip -vf vmlinux
++
++clean:
++ rm -f vmlinux.* zImage.*
++
+diff -Naru linux/arch/mips/zboot/include/nonstdio.h linux-new/arch/mips/zboot/include/nonstdio.h
+--- linux/arch/mips/zboot/include/nonstdio.h 1969-12-31 19:00:00.000000000 -0500
++++ linux-new/arch/mips/zboot/include/nonstdio.h 2003-12-18 14:26:20.000000000 -0500
+@@ -0,0 +1,18 @@
++/*
++ * Copyright (C) Paul Mackerras 1997.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version
++ * 2 of the License, or (at your option) any later version.
++ */
++typedef int FILE;
++extern FILE *stdin, *stdout;
++#define NULL ((void *)0)
++#define EOF (-1)
++#define fopen(n, m) NULL
++#define fflush(f) 0
++#define fclose(f) 0
++extern char *fgets();
++
++#define perror(s) printf("%s: no files!\n", (s))
+diff -Naru linux/arch/mips/zboot/include/ns16550.h linux-new/arch/mips/zboot/include/ns16550.h
+--- linux/arch/mips/zboot/include/ns16550.h 1969-12-31 19:00:00.000000000 -0500
++++ linux-new/arch/mips/zboot/include/ns16550.h 2003-12-18 14:26:20.000000000 -0500
+@@ -0,0 +1,49 @@
++/*
++ * NS16550 Serial Port
++ */
++
++/*
++ * Figure out which file will have the definitons of COMx
++ */
++#if !defined(CONFIG_AU1X00_UART)
++#error no serial.h
++#endif
++
++/* Some machines have their uart registers 16 bytes apart. Most don't.
++ * TODO: Make this work like drivers/char/serial does - Tom */
++#if !defined(UART_REG_PAD)
++#define UART_REG_PAD(x)
++#endif
++
++struct NS16550
++ {
++ unsigned char rbr; /* 0 */
++ UART_REG_PAD(rbr)
++ unsigned char ier; /* 1 */
++ UART_REG_PAD(ier)
++ unsigned char fcr; /* 2 */
++ UART_REG_PAD(fcr)
++ unsigned char lcr; /* 3 */
++ UART_REG_PAD(lcr)
++ unsigned char mcr; /* 4 */
++ UART_REG_PAD(mcr)
++ unsigned char lsr; /* 5 */
++ UART_REG_PAD(lsr)
++ unsigned char msr; /* 6 */
++ UART_REG_PAD(msr)
++ unsigned char scr; /* 7 */
++ };
++
++#define thr rbr
++#define iir fcr
++#define dll rbr
++#define dlm ier
++
++#define LSR_DR 0x01 /* Data ready */
++#define LSR_OE 0x02 /* Overrun */
++#define LSR_PE 0x04 /* Parity error */
++#define LSR_FE 0x08 /* Framing error */
++#define LSR_BI 0x10 /* Break */
++#define LSR_THRE 0x20 /* Xmit holding register empty */
++#define LSR_TEMT 0x40 /* Xmitter empty */
++#define LSR_ERR 0x80 /* Error */
+diff -Naru linux/arch/mips/zboot/include/pb1000_serial.h linux-new/arch/mips/zboot/include/pb1000_serial.h
+--- linux/arch/mips/zboot/include/pb1000_serial.h 1969-12-31 19:00:00.000000000 -0500
++++ linux-new/arch/mips/zboot/include/pb1000_serial.h 2003-12-18 14:26:20.000000000 -0500
+@@ -0,0 +1,20 @@
++/*
++ * arch/ppc/boot/include/sandpoint_serial.h
++ *
++ * Location of the COM ports on Motorola SPS Sandpoint machines
++ *
++ * Author: Mark A. Greer
++ * mgreer@mvista.com
++ *
++ * Copyright 2001 MontaVista Software Inc.
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version.
++ */
++
++#define COM1 0xfe0003f8
++#define COM2 0xfe0002f8
++#define COM3 0x00000000 /* No COM3 */
++#define COM4 0x00000000 /* No COM4 */
+diff -Naru linux/arch/mips/zboot/include/zlib.h linux-new/arch/mips/zboot/include/zlib.h
+--- linux/arch/mips/zboot/include/zlib.h 1969-12-31 19:00:00.000000000 -0500
++++ linux-new/arch/mips/zboot/include/zlib.h 2003-12-18 14:26:21.000000000 -0500
+@@ -0,0 +1,432 @@
++/* $Id$ */
++
++/*
++ * This file is derived from zlib.h and zconf.h from the zlib-0.95
++ * distribution by Jean-loup Gailly and Mark Adler, with some additions
++ * by Paul Mackerras to aid in implementing Deflate compression and
++ * decompression for PPP packets.
++ */
++
++/*
++ * ==FILEVERSION 960122==
++ *
++ * This marker is used by the Linux installation script to determine
++ * whether an up-to-date version of this file is already installed.
++ */
++
++/* zlib.h -- interface of the 'zlib' general purpose compression library
++ version 0.95, Aug 16th, 1995.
++
++ Copyright (C) 1995 Jean-loup Gailly and Mark Adler
++
++ This software is provided 'as-is', without any express or implied
++ warranty. In no event will the authors be held liable for any damages
++ arising from the use of this software.
++
++ Permission is granted to anyone to use this software for any purpose,
++ including commercial applications, and to alter it and redistribute it
++ freely, subject to the following restrictions:
++
++ 1. The origin of this software must not be misrepresented; you must not
++ claim that you wrote the original software. If you use this software
++ in a product, an acknowledgment in the product documentation would be
++ appreciated but is not required.
++ 2. Altered source versions must be plainly marked as such, and must not be
++ misrepresented as being the original software.
++ 3. This notice may not be removed or altered from any source distribution.
++
++ Jean-loup Gailly Mark Adler
++ gzip@prep.ai.mit.edu madler@alumni.caltech.edu
++ */
++
++#ifndef _ZLIB_H
++#define _ZLIB_H
++
++/* #include "zconf.h" */ /* included directly here */
++
++/* zconf.h -- configuration of the zlib compression library
++ * Copyright (C) 1995 Jean-loup Gailly.
++ * For conditions of distribution and use, see copyright notice in zlib.h
++ */
++
++/* From: zconf.h,v 1.12 1995/05/03 17:27:12 jloup Exp */
++
++/*
++ The library does not install any signal handler. It is recommended to
++ add at least a handler for SIGSEGV when decompressing; the library checks
++ the consistency of the input data whenever possible but may go nuts
++ for some forms of corrupted input.
++ */
++
++/*
++ * Compile with -DMAXSEG_64K if the alloc function cannot allocate more
++ * than 64k bytes at a time (needed on systems with 16-bit int).
++ * Compile with -DUNALIGNED_OK if it is OK to access shorts or ints
++ * at addresses which are not a multiple of their size.
++ * Under DOS, -DFAR=far or -DFAR=__far may be needed.
++ */
++
++#ifndef STDC
++# if defined(MSDOS) || defined(__STDC__) || defined(__cplusplus)
++# define STDC
++# endif
++#endif
++
++#ifdef __MWERKS__ /* Metrowerks CodeWarrior declares fileno() in unix.h */
++# include <unix.h>
++#endif
++
++/* Maximum value for memLevel in deflateInit2 */
++#ifndef MAX_MEM_LEVEL
++# ifdef MAXSEG_64K
++# define MAX_MEM_LEVEL 8
++# else
++# define MAX_MEM_LEVEL 9
++# endif
++#endif
++
++#ifndef FAR
++# define FAR
++#endif
++
++/* Maximum value for windowBits in deflateInit2 and inflateInit2 */
++#ifndef MAX_WBITS
++# define MAX_WBITS 15 /* 32K LZ77 window */
++#endif
++
++/* The memory requirements for deflate are (in bytes):
++ 1 << (windowBits+2) + 1 << (memLevel+9)
++ that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values)
++ plus a few kilobytes for small objects. For example, if you want to reduce
++ the default memory requirements from 256K to 128K, compile with
++ make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7"
++ Of course this will generally degrade compression (there's no free lunch).
++
++ The memory requirements for inflate are (in bytes) 1 << windowBits
++ that is, 32K for windowBits=15 (default value) plus a few kilobytes
++ for small objects.
++*/
++
++ /* Type declarations */
++
++#ifndef OF /* function prototypes */
++# ifdef STDC
++# define OF(args) args
++# else
++# define OF(args) ()
++# endif
++#endif
++
++typedef unsigned char Byte; /* 8 bits */
++typedef unsigned int uInt; /* 16 bits or more */
++typedef unsigned long uLong; /* 32 bits or more */
++
++typedef Byte FAR Bytef;
++typedef char FAR charf;
++typedef int FAR intf;
++typedef uInt FAR uIntf;
++typedef uLong FAR uLongf;
++
++#ifdef STDC
++ typedef void FAR *voidpf;
++ typedef void *voidp;
++#else
++ typedef Byte FAR *voidpf;
++ typedef Byte *voidp;
++#endif
++
++/* end of original zconf.h */
++
++#define ZLIB_VERSION "0.95P"
++
++/*
++ The 'zlib' compression library provides in-memory compression and
++ decompression functions, including integrity checks of the uncompressed
++ data. This version of the library supports only one compression method
++ (deflation) but other algorithms may be added later and will have the same
++ stream interface.
++
++ For compression the application must provide the output buffer and
++ may optionally provide the input buffer for optimization. For decompression,
++ the application must provide the input buffer and may optionally provide
++ the output buffer for optimization.
++
++ Compression can be done in a single step if the buffers are large
++ enough (for example if an input file is mmap'ed), or can be done by
++ repeated calls of the compression function. In the latter case, the
++ application must provide more input and/or consume the output
++ (providing more output space) before each call.
++*/
++
++typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size));
++typedef void (*free_func) OF((voidpf opaque, voidpf address, uInt nbytes));
++
++struct internal_state;
++
++typedef struct z_stream_s {
++ Bytef *next_in; /* next input byte */
++ uInt avail_in; /* number of bytes available at next_in */
++ uLong total_in; /* total nb of input bytes read so far */
++
++ Bytef *next_out; /* next output byte should be put there */
++ uInt avail_out; /* remaining free space at next_out */
++ uLong total_out; /* total nb of bytes output so far */
++
++ char *msg; /* last error message, NULL if no error */
++ struct internal_state FAR *state; /* not visible by applications */
++
++ alloc_func zalloc; /* used to allocate the internal state */
++ free_func zfree; /* used to free the internal state */
++ voidp opaque; /* private data object passed to zalloc and zfree */
++
++ Byte data_type; /* best guess about the data type: ascii or binary */
++
++} z_stream;
++
++/*
++ The application must update next_in and avail_in when avail_in has
++ dropped to zero. It must update next_out and avail_out when avail_out
++ has dropped to zero. The application must initialize zalloc, zfree and
++ opaque before calling the init function. All other fields are set by the
++ compression library and must not be updated by the application.
++
++ The opaque value provided by the application will be passed as the first
++ parameter for calls of zalloc and zfree. This can be useful for custom
++ memory management. The compression library attaches no meaning to the
++ opaque value.
++
++ zalloc must return Z_NULL if there is not enough memory for the object.
++ On 16-bit systems, the functions zalloc and zfree must be able to allocate
++ exactly 65536 bytes, but will not be required to allocate more than this
++ if the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS,
++ pointers returned by zalloc for objects of exactly 65536 bytes *must*
++ have their offset normalized to zero. The default allocation function
++ provided by this library ensures this (see zutil.c). To reduce memory
++ requirements and avoid any allocation of 64K objects, at the expense of
++ compression ratio, compile the library with -DMAX_WBITS=14 (see zconf.h).
++
++ The fields total_in and total_out can be used for statistics or
++ progress reports. After compression, total_in holds the total size of
++ the uncompressed data and may be saved for use in the decompressor
++ (particularly if the decompressor wants to decompress everything in
++ a single step).
++*/
++
++ /* constants */
++
++#define Z_NO_FLUSH 0
++#define Z_PARTIAL_FLUSH 1
++#define Z_FULL_FLUSH 2
++#define Z_SYNC_FLUSH 3 /* experimental: partial_flush + byte align */
++#define Z_FINISH 4
++#define Z_PACKET_FLUSH 5
++/* See deflate() below for the usage of these constants */
++
++#define Z_OK 0
++#define Z_STREAM_END 1
++#define Z_ERRNO (-1)
++#define Z_STREAM_ERROR (-2)
++#define Z_DATA_ERROR (-3)
++#define Z_MEM_ERROR (-4)
++#define Z_BUF_ERROR (-5)
++/* error codes for the compression/decompression functions */
++
++#define Z_BEST_SPEED 1
++#define Z_BEST_COMPRESSION 9
++#define Z_DEFAULT_COMPRESSION (-1)
++/* compression levels */
++
++#define Z_FILTERED 1
++#define Z_HUFFMAN_ONLY 2
++#define Z_DEFAULT_STRATEGY 0
++
++#define Z_BINARY 0
++#define Z_ASCII 1
++#define Z_UNKNOWN 2
++/* Used to set the data_type field */
++
++#define Z_NULL 0 /* for initializing zalloc, zfree, opaque */
++
++extern char *zlib_version;
++/* The application can compare zlib_version and ZLIB_VERSION for consistency.
++ If the first character differs, the library code actually used is
++ not compatible with the zlib.h header file used by the application.
++ */
++
++ /* basic functions */
++
++extern int inflateInit OF((z_stream *strm));
++/*
++ Initializes the internal stream state for decompression. The fields
++ zalloc and zfree must be initialized before by the caller. If zalloc and
++ zfree are set to Z_NULL, inflateInit updates them to use default allocation
++ functions.
++
++ inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not
++ enough memory. msg is set to null if there is no error message.
++ inflateInit does not perform any decompression: this will be done by
++ inflate().
++*/
++
++
++extern int inflate OF((z_stream *strm, int flush));
++/*
++ Performs one or both of the following actions:
++
++ - Decompress more input starting at next_in and update next_in and avail_in
++ accordingly. If not all input can be processed (because there is not
++ enough room in the output buffer), next_in is updated and processing
++ will resume at this point for the next call of inflate().
++
++ - Provide more output starting at next_out and update next_out and avail_out
++ accordingly. inflate() always provides as much output as possible
++ (until there is no more input data or no more space in the output buffer).
++
++ Before the call of inflate(), the application should ensure that at least
++ one of the actions is possible, by providing more input and/or consuming
++ more output, and updating the next_* and avail_* values accordingly.
++ The application can consume the uncompressed output when it wants, for
++ example when the output buffer is full (avail_out == 0), or after each
++ call of inflate().
++
++ If the parameter flush is set to Z_PARTIAL_FLUSH or Z_PACKET_FLUSH,
++ inflate flushes as much output as possible to the output buffer. The
++ flushing behavior of inflate is not specified for values of the flush
++ parameter other than Z_PARTIAL_FLUSH, Z_PACKET_FLUSH or Z_FINISH, but the
++ current implementation actually flushes as much output as possible
++ anyway. For Z_PACKET_FLUSH, inflate checks that once all the input data
++ has been consumed, it is expecting to see the length field of a stored
++ block; if not, it returns Z_DATA_ERROR.
++
++ inflate() should normally be called until it returns Z_STREAM_END or an
++ error. However if all decompression is to be performed in a single step
++ (a single call of inflate), the parameter flush should be set to
++ Z_FINISH. In this case all pending input is processed and all pending
++ output is flushed; avail_out must be large enough to hold all the
++ uncompressed data. (The size of the uncompressed data may have been saved
++ by the compressor for this purpose.) The next operation on this stream must
++ be inflateEnd to deallocate the decompression state. The use of Z_FINISH
++ is never required, but can be used to inform inflate that a faster routine
++ may be used for the single inflate() call.
++
++ inflate() returns Z_OK if some progress has been made (more input
++ processed or more output produced), Z_STREAM_END if the end of the
++ compressed data has been reached and all uncompressed output has been
++ produced, Z_DATA_ERROR if the input data was corrupted, Z_STREAM_ERROR if
++ the stream structure was inconsistent (for example if next_in or next_out
++ was NULL), Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR if no
++ progress is possible or if there was not enough room in the output buffer
++ when Z_FINISH is used. In the Z_DATA_ERROR case, the application may then
++ call inflateSync to look for a good compression block. */
++
++
++extern int inflateEnd OF((z_stream *strm));
++/*
++ All dynamically allocated data structures for this stream are freed.
++ This function discards any unprocessed input and does not flush any
++ pending output.
++
++ inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state
++ was inconsistent. In the error case, msg may be set but then points to a
++ static string (which must not be deallocated).
++*/
++
++ /* advanced functions */
++
++extern int inflateInit2 OF((z_stream *strm,
++ int windowBits));
++/*
++ This is another version of inflateInit with more compression options. The
++ fields next_out, zalloc and zfree must be initialized before by the caller.
++
++ The windowBits parameter is the base two logarithm of the maximum window
++ size (the size of the history buffer). It should be in the range 8..15 for
++ this version of the library (the value 16 will be allowed soon). The
++ default value is 15 if inflateInit is used instead. If a compressed stream
++ with a larger window size is given as input, inflate() will return with
++ the error code Z_DATA_ERROR instead of trying to allocate a larger window.
++
++ If next_out is not null, the library will use this buffer for the history
++ buffer; the buffer must either be large enough to hold the entire output
++ data, or have at least 1<<windowBits bytes. If next_out is null, the
++ library will allocate its own buffer (and leave next_out null). next_in
++ need not be provided here but must be provided by the application for the
++ next call of inflate().
++
++ If the history buffer is provided by the application, next_out must
++ never be changed by the application since the decompressor maintains
++ history information inside this buffer from call to call; the application
++ can only reset next_out to the beginning of the history buffer when
++ avail_out is zero and all output has been consumed.
++
++ inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was
++ not enough memory, Z_STREAM_ERROR if a parameter is invalid (such as
++ windowBits < 8). msg is set to null if there is no error message.
++ inflateInit2 does not perform any decompression: this will be done by
++ inflate().
++*/
++
++extern int inflateSync OF((z_stream *strm));
++/*
++ Skips invalid compressed data until the special marker (see deflate()
++ above) can be found, or until all available input is skipped. No output
++ is provided.
++
++ inflateSync returns Z_OK if the special marker has been found, Z_BUF_ERROR
++ if no more input was provided, Z_DATA_ERROR if no marker has been found,
++ or Z_STREAM_ERROR if the stream structure was inconsistent. In the success
++ case, the application may save the current current value of total_in which
++ indicates where valid compressed data was found. In the error case, the
++ application may repeatedly call inflateSync, providing more input each time,
++ until success or end of the input data.
++*/
++
++extern int inflateReset OF((z_stream *strm));
++/*
++ This function is equivalent to inflateEnd followed by inflateInit,
++ but does not free and reallocate all the internal decompression state.
++ The stream will keep attributes that may have been set by inflateInit2.
++
++ inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source
++ stream state was inconsistent (such as zalloc or state being NULL).
++*/
++
++extern int inflateIncomp OF((z_stream *strm));
++/*
++ This function adds the data at next_in (avail_in bytes) to the output
++ history without performing any output. There must be no pending output,
++ and the decompressor must be expecting to see the start of a block.
++ Calling this function is equivalent to decompressing a stored block
++ containing the data at next_in (except that the data is not output).
++*/
++
++ /* checksum functions */
++
++/*
++ This function is not related to compression but is exported
++ anyway because it might be useful in applications using the
++ compression library.
++*/
++
++extern uLong adler32 OF((uLong adler, Bytef *buf, uInt len));
++
++/*
++ Update a running Adler-32 checksum with the bytes buf[0..len-1] and
++ return the updated checksum. If buf is NULL, this function returns
++ the required initial value for the checksum.
++ An Adler-32 checksum is almost as reliable as a CRC32 but can be computed
++ much faster. Usage example:
++
++ uLong adler = adler32(0L, Z_NULL, 0);
++
++ while (read_buffer(buffer, length) != EOF) {
++ adler = adler32(adler, buffer, length);
++ }
++ if (adler != original_adler) error();
++*/
++
++#ifndef _Z_UTIL_H
++ struct internal_state {int dummy;}; /* hack for buggy compilers */
++#endif
++
++#endif /* _ZLIB_H */
+diff -Naru linux/arch/mips/zboot/ld.script linux-new/arch/mips/zboot/ld.script
+--- linux/arch/mips/zboot/ld.script 1969-12-31 19:00:00.000000000 -0500
++++ linux-new/arch/mips/zboot/ld.script 2003-12-18 14:26:21.000000000 -0500
+@@ -0,0 +1,151 @@
++OUTPUT_ARCH(mips)
++ENTRY(start)
++SECTIONS
++{
++ /* Read-only sections, merged into text segment: */
++ /* . = 0x81000000; */
++ .init : { *(.init) } =0
++ .text :
++ {
++ _ftext = . ;
++ *(.text)
++ *(.rodata)
++ *(.rodata1)
++ /* .gnu.warning sections are handled specially by elf32.em. */
++ *(.gnu.warning)
++ } =0
++ .kstrtab : { *(.kstrtab) }
++
++ . = ALIGN(16); /* Exception table */
++ __start___ex_table = .;
++ __ex_table : { *(__ex_table) }
++ __stop___ex_table = .;
++
++ __start___dbe_table = .; /* Exception table for data bus errors */
++ __dbe_table : { *(__dbe_table) }
++ __stop___dbe_table = .;
++
++ __start___ksymtab = .; /* Kernel symbol table */
++ __ksymtab : { *(__ksymtab) }
++ __stop___ksymtab = .;
++
++ _etext = .;
++
++ . = ALIGN(8192);
++ .data.init_task : { *(.data.init_task) }
++
++ /* Startup code */
++ . = ALIGN(4096);
++ __init_begin = .;
++ .text.init : { *(.text.init) }
++ .data.init : { *(.data.init) }
++ . = ALIGN(16);
++ __setup_start = .;
++ .setup.init : { *(.setup.init) }
++ __setup_end = .;
++ __initcall_start = .;
++ .initcall.init : { *(.initcall.init) }
++ __initcall_end = .;
++ . = ALIGN(4096); /* Align double page for init_task_union */
++ __init_end = .;
++
++ . = ALIGN(4096);
++ .data.page_aligned : { *(.data.idt) }
++
++ . = ALIGN(32);
++ .data.cacheline_aligned : { *(.data.cacheline_aligned) }
++
++ .fini : { *(.fini) } =0
++ .reginfo : { *(.reginfo) }
++ /* Adjust the address for the data segment. We want to adjust up to
++ the same address within the page on the next page up. It would
++ be more correct to do this:
++ . = .;
++ The current expression does not correctly handle the case of a
++ text segment ending precisely at the end of a page; it causes the
++ data segment to skip a page. The above expression does not have
++ this problem, but it will currently (2/95) cause BFD to allocate
++ a single segment, combining both text and data, for this case.
++ This will prevent the text segment from being shared among
++ multiple executions of the program; I think that is more
++ important than losing a page of the virtual address space (note
++ that no actual memory is lost; the page which is skipped can not
++ be referenced). */
++ . = .;
++ .data :
++ {
++ _fdata = . ;
++ *(.data)
++
++ /* Put the compressed image here, so bss is on the end. */
++ __image_begin = .;
++ *(.image)
++ __image_end = .;
++ /* Align the initial ramdisk image (INITRD) on page boundaries. */
++ . = ALIGN(4096);
++ __ramdisk_begin = .;
++ *(.initrd)
++ __ramdisk_end = .;
++ . = ALIGN(4096);
++
++ CONSTRUCTORS
++ }
++ .data1 : { *(.data1) }
++ _gp = . + 0x8000;
++ .lit8 : { *(.lit8) }
++ .lit4 : { *(.lit4) }
++ .ctors : { *(.ctors) }
++ .dtors : { *(.dtors) }
++ .got : { *(.got.plt) *(.got) }
++ .dynamic : { *(.dynamic) }
++ /* We want the small data sections together, so single-instruction offsets
++ can access them all, and initialized data all before uninitialized, so
++ we can shorten the on-disk segment size. */
++ .sdata : { *(.sdata) }
++ . = ALIGN(4);
++ _edata = .;
++ PROVIDE (edata = .);
++
++ __bss_start = .;
++ _fbss = .;
++ .sbss : { *(.sbss) *(.scommon) }
++ .bss :
++ {
++ *(.dynbss)
++ *(.bss)
++ *(COMMON)
++ . = ALIGN(4);
++ _end = . ;
++ PROVIDE (end = .);
++ }
++
++ /* Sections to be discarded */
++ /DISCARD/ :
++ {
++ *(.text.exit)
++ *(.data.exit)
++ *(.exitcall.exit)
++ }
++
++ /* This is the MIPS specific mdebug section. */
++ .mdebug : { *(.mdebug) }
++ /* These are needed for ELF backends which have not yet been
++ converted to the new style linker. */
++ .stab 0 : { *(.stab) }
++ .stabstr 0 : { *(.stabstr) }
++ /* DWARF debug sections.
++ Symbols in the .debug DWARF section are relative to the beginning of the
++ section so we begin .debug at 0. It's not clear yet what needs to happen
++ for the others. */
++ .debug 0 : { *(.debug) }
++ .debug_srcinfo 0 : { *(.debug_srcinfo) }
++ .debug_aranges 0 : { *(.debug_aranges) }
++ .debug_pubnames 0 : { *(.debug_pubnames) }
++ .debug_sfnames 0 : { *(.debug_sfnames) }
++ .line 0 : { *(.line) }
++ /* These must appear regardless of . */
++ .gptab.sdata : { *(.gptab.data) *(.gptab.sdata) }
++ .gptab.sbss : { *(.gptab.bss) *(.gptab.sbss) }
++ .comment : { *(.comment) }
++ .note : { *(.note) }
++}
+diff -Naru linux/arch/mips/zboot/lib/Makefile linux-new/arch/mips/zboot/lib/Makefile
+--- linux/arch/mips/zboot/lib/Makefile 1969-12-31 19:00:00.000000000 -0500
++++ linux-new/arch/mips/zboot/lib/Makefile 2003-12-18 14:26:21.000000000 -0500
+@@ -0,0 +1,9 @@
++#
++# Makefile for some libs needed by zImage.
++#
++
++L_TARGET := zlib.a
++
++obj-y := zlib.o
++
++include $(TOPDIR)/Rules.make
+diff -Naru linux/arch/mips/zboot/lib/zlib.c linux-new/arch/mips/zboot/lib/zlib.c
+--- linux/arch/mips/zboot/lib/zlib.c 1969-12-31 19:00:00.000000000 -0500
++++ linux-new/arch/mips/zboot/lib/zlib.c 2003-12-18 14:26:21.000000000 -0500
+@@ -0,0 +1,2148 @@
++/*
++ * This file is derived from various .h and .c files from the zlib-0.95
++ * distribution by Jean-loup Gailly and Mark Adler, with some additions
++ * by Paul Mackerras to aid in implementing Deflate compression and
++ * decompression for PPP packets. See zlib.h for conditions of
++ * distribution and use.
++ *
++ * Changes that have been made include:
++ * - changed functions not used outside this file to "local"
++ * - added minCompression parameter to deflateInit2
++ * - added Z_PACKET_FLUSH (see zlib.h for details)
++ * - added inflateIncomp
++ *
++ * $Id$
++ */
++
++/*+++++*/
++/* zutil.h -- internal interface and configuration of the compression library
++ * Copyright (C) 1995 Jean-loup Gailly.
++ * For conditions of distribution and use, see copyright notice in zlib.h
++ */
++
++/* WARNING: this file should *not* be used by applications. It is
++ part of the implementation of the compression library and is
++ subject to change. Applications should only use zlib.h.
++ */
++
++/* From: zutil.h,v 1.9 1995/05/03 17:27:12 jloup Exp */
++
++#define _Z_UTIL_H
++
++#include "zlib.h"
++
++#ifndef local
++# define local static
++#endif
++/* compile with -Dlocal if your debugger can't find static symbols */
++
++#define FAR
++
++typedef unsigned char uch;
++typedef uch FAR uchf;
++typedef unsigned short ush;
++typedef ush FAR ushf;
++typedef unsigned long ulg;
++
++extern char *z_errmsg[]; /* indexed by 1-zlib_error */
++
++#define ERR_RETURN(strm,err) return (strm->msg=z_errmsg[1-err], err)
++/* To be used only when the state is known to be valid */
++
++#ifndef NULL
++#define NULL ((void *) 0)
++#endif
++
++ /* common constants */
++
++#define DEFLATED 8
++
++#ifndef DEF_WBITS
++# define DEF_WBITS MAX_WBITS
++#endif
++/* default windowBits for decompression. MAX_WBITS is for compression only */
++
++#if MAX_MEM_LEVEL >= 8
++# define DEF_MEM_LEVEL 8
++#else
++# define DEF_MEM_LEVEL MAX_MEM_LEVEL
++#endif
++/* default memLevel */
++
++#define STORED_BLOCK 0
++#define STATIC_TREES 1
++#define DYN_TREES 2
++/* The three kinds of block type */
++
++#define MIN_MATCH 3
++#define MAX_MATCH 258
++/* The minimum and maximum match lengths */
++
++ /* functions */
++
++#include <linux/string.h>
++#define zmemcpy memcpy
++#define zmemzero(dest, len) memset(dest, 0, len)
++
++/* Diagnostic functions */
++#ifdef DEBUG_ZLIB
++# include <stdio.h>
++# ifndef verbose
++# define verbose 0
++# endif
++# define Assert(cond,msg) {if(!(cond)) z_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
++
++
++typedef uLong (*check_func) OF((uLong check, Bytef *buf, uInt len));
++
++/* voidpf zcalloc OF((voidpf opaque, unsigned items, unsigned size)); */
++/* void zcfree OF((voidpf opaque, voidpf ptr)); */
++
++#define ZALLOC(strm, items, size) \
++ (*((strm)->zalloc))((strm)->opaque, (items), (size))
++#define ZFREE(strm, addr, size) \
++ (*((strm)->zfree))((strm)->opaque, (voidpf)(addr), (size))
++#define TRY_FREE(s, p, n) {if (p) ZFREE(s, p, n);}
++
++/* deflate.h -- internal compression state
++ * Copyright (C) 1995 Jean-loup Gailly
++ * For conditions of distribution and use, see copyright notice in zlib.h
++ */
++
++/* WARNING: this file should *not* be used by applications. It is
++ part of the implementation of the compression library and is
++ subject to change. Applications should only use zlib.h.
++ */
++
++/*+++++*/
++/* infblock.h -- header to use infblock.c
++ * Copyright (C) 1995 Mark Adler
++ * For conditions of distribution and use, see copyright notice in zlib.h
++ */
++
++/* WARNING: this file should *not* be used by applications. It is
++ part of the implementation of the compression library and is
++ subject to change. Applications should only use zlib.h.
++ */
++
++struct inflate_blocks_state;
++typedef struct inflate_blocks_state FAR inflate_blocks_statef;
++
++local inflate_blocks_statef * inflate_blocks_new OF((
++ z_stream *z,
++ check_func c, /* check function */
++ uInt w)); /* window size */
++
++local int inflate_blocks OF((
++ inflate_blocks_statef *,
++ z_stream *,
++ int)); /* initial return code */
++
++local void inflate_blocks_reset OF((
++ inflate_blocks_statef *,
++ z_stream *,
++ uLongf *)); /* check value on output */
++
++local int inflate_blocks_free OF((
++ inflate_blocks_statef *,
++ z_stream *,
++ uLongf *)); /* check value on output */
++
++local int inflate_addhistory OF((
++ inflate_blocks_statef *,
++ z_stream *));
++
++local int inflate_packet_flush OF((
++ inflate_blocks_statef *));
++
++/*+++++*/
++/* inftrees.h -- header to use inftrees.c
++ * Copyright (C) 1995 Mark Adler
++ * For conditions of distribution and use, see copyright notice in zlib.h
++ */
++
++/* WARNING: this file should *not* be used by applications. It is
++ part of the implementation of the compression library and is
++ subject to change. Applications should only use zlib.h.
++ */
++
++/* Huffman code lookup table entry--this entry is four bytes for machines
++ that have 16-bit pointers (e.g. PC's in the small or medium model). */
++
++typedef struct inflate_huft_s FAR inflate_huft;
++
++struct inflate_huft_s {
++ union {
++ struct {
++ Byte Exop; /* number of extra bits or operation */
++ Byte Bits; /* number of bits in this code or subcode */
++ } what;
++ uInt Nalloc; /* number of these allocated here */
++ Bytef *pad; /* pad structure to a power of 2 (4 bytes for */
++ } word; /* 16-bit, 8 bytes for 32-bit machines) */
++ union {
++ uInt Base; /* literal, length base, or distance base */
++ inflate_huft *Next; /* pointer to next level of table */
++ } more;
++};
++
++#ifdef DEBUG_ZLIB
++ local uInt inflate_hufts;
++#endif
++
++local int inflate_trees_bits OF((
++ uIntf *, /* 19 code lengths */
++ uIntf *, /* bits tree desired/actual depth */
++ inflate_huft * FAR *, /* bits tree result */
++ z_stream *)); /* for zalloc, zfree functions */
++
++local int inflate_trees_dynamic OF((
++ uInt, /* number of literal/length codes */
++ uInt, /* number of distance codes */
++ uIntf *, /* that many (total) code lengths */
++ uIntf *, /* literal desired/actual bit depth */
++ uIntf *, /* distance desired/actual bit depth */
++ inflate_huft * FAR *, /* literal/length tree result */
++ inflate_huft * FAR *, /* distance tree result */
++ z_stream *)); /* for zalloc, zfree functions */
++
++local int inflate_trees_fixed OF((
++ uIntf *, /* literal desired/actual bit depth */
++ uIntf *, /* distance desired/actual bit depth */
++ inflate_huft * FAR *, /* literal/length tree result */
++ inflate_huft * FAR *)); /* distance tree result */
++
++local int inflate_trees_free OF((
++ inflate_huft *, /* tables to free */
++ z_stream *)); /* for zfree function */
++
++
++/*+++++*/
++/* infcodes.h -- header to use infcodes.c
++ * Copyright (C) 1995 Mark Adler
++ * For conditions of distribution and use, see copyright notice in zlib.h
++ */
++
++/* WARNING: this file should *not* be used by applications. It is
++ part of the implementation of the compression library and is
++ subject to change. Applications should only use zlib.h.
++ */
++
++struct inflate_codes_state;
++typedef struct inflate_codes_state FAR inflate_codes_statef;
++
++local inflate_codes_statef *inflate_codes_new OF((
++ uInt, uInt,
++ inflate_huft *, inflate_huft *,
++ z_stream *));
++
++local int inflate_codes OF((
++ inflate_blocks_statef *,
++ z_stream *,
++ int));
++
++local void inflate_codes_free OF((
++ inflate_codes_statef *,
++ z_stream *));
++
++
++/*+++++*/
++/* inflate.c -- zlib interface to inflate modules
++ * Copyright (C) 1995 Mark Adler
++ * For conditions of distribution and use, see copyright notice in zlib.h
++ */
++
++/* inflate private state */
++struct internal_state {
++
++ /* mode */
++ enum {
++ METHOD, /* waiting for method byte */
++ FLAG, /* waiting for flag byte */
++ BLOCKS, /* decompressing blocks */
++ CHECK4, /* four check bytes to go */
++ CHECK3, /* three check bytes to go */
++ CHECK2, /* two check bytes to go */
++ CHECK1, /* one check byte to go */
++ DONE, /* finished check, done */
++ BAD} /* got an error--stay here */
++ mode; /* current inflate mode */
++
++ /* mode dependent information */
++ union {
++ uInt method; /* if FLAGS, method byte */
++ struct {
++ uLong was; /* computed check value */
++ uLong need; /* stream check value */
++ } check; /* if CHECK, check values to compare */
++ uInt marker; /* if BAD, inflateSync's marker bytes count */
++ } sub; /* submode */
++
++ /* mode independent information */
++ int nowrap; /* flag for no wrapper */
++ uInt wbits; /* log2(window size) (8..15, defaults to 15) */
++ inflate_blocks_statef
++ *blocks; /* current inflate_blocks state */
++
++};
++
++
++int inflateReset(z)
++z_stream *z;
++{
++ uLong c;
++
++ if (z == Z_NULL || z->state == Z_NULL)
++ return Z_STREAM_ERROR;
++ z->total_in = z->total_out = 0;
++ z->msg = Z_NULL;
++ z->state->mode = z->state->nowrap ? BLOCKS : METHOD;
++ inflate_blocks_reset(z->state->blocks, z, &c);
++ Trace((stderr, "inflate: reset\n"));
++ return Z_OK;
++}
++
++
++int inflateEnd(z)
++z_stream *z;
++{
++ uLong c;
++
++ if (z == Z_NULL || z->state == Z_NULL || z->zfree == Z_NULL)
++ return Z_STREAM_ERROR;
++ if (z->state->blocks != Z_NULL)
++ inflate_blocks_free(z->state->blocks, z, &c);
++ ZFREE(z, z->state, sizeof(struct internal_state));
++ z->state = Z_NULL;
++ Trace((stderr, "inflate: end\n"));
++ return Z_OK;
++}
++
++
++int inflateInit2(z, w)
++z_stream *z;
++int w;
++{
++ /* initialize state */
++ if (z == Z_NULL)
++ return Z_STREAM_ERROR;
++/* if (z->zalloc == Z_NULL) z->zalloc = zcalloc; */
++/* if (z->zfree == Z_NULL) z->zfree = zcfree; */
++ if ((z->state = (struct internal_state FAR *)
++ ZALLOC(z,1,sizeof(struct internal_state))) == Z_NULL)
++ return Z_MEM_ERROR;
++ z->state->blocks = Z_NULL;
++
++ /* handle undocumented nowrap option (no zlib header or check) */
++ z->state->nowrap = 0;
++ if (w < 0)
++ {
++ w = - w;
++ z->state->nowrap = 1;
++ }
++
++ /* set window size */
++ if (w < 8 || w > 15)
++ {
++ inflateEnd(z);
++ return Z_STREAM_ERROR;
++ }
++ z->state->wbits = (uInt)w;
++
++ /* create inflate_blocks state */
++ if ((z->state->blocks =
++ inflate_blocks_new(z, z->state->nowrap ? Z_NULL : adler32, 1 << w))
++ == Z_NULL)
++ {
++ inflateEnd(z);
++ return Z_MEM_ERROR;
++ }
++ Trace((stderr, "inflate: allocated\n"));
++
++ /* reset state */
++ inflateReset(z);
++ return Z_OK;
++}
++
++
++int inflateInit(z)
++z_stream *z;
++{
++ return inflateInit2(z, DEF_WBITS);
++}
++
++
++#define NEEDBYTE {if(z->avail_in==0)goto empty;r=Z_OK;}
++#define NEXTBYTE (z->avail_in--,z->total_in++,*z->next_in++)
++
++int inflate(z, f)
++z_stream *z;
++int f;
++{
++ int r;
++ uInt b;
++
++ if (z == Z_NULL || z->next_in == Z_NULL)
++ return Z_STREAM_ERROR;
++ r = Z_BUF_ERROR;
++ while (1) switch (z->state->mode)
++ {
++ case METHOD:
++ NEEDBYTE
++ if (((z->state->sub.method = NEXTBYTE) & 0xf) != DEFLATED)
++ {
++ z->state->mode = BAD;
++ z->msg = "unknown compression method";
++ z->state->sub.marker = 5; /* can't try inflateSync */
++ break;
++ }
++ if ((z->state->sub.method >> 4) + 8 > z->state->wbits)
++ {
++ z->state->mode = BAD;
++ z->msg = "invalid window size";
++ z->state->sub.marker = 5; /* can't try inflateSync */
++ break;
++ }
++ z->state->mode = FLAG;
++ case FLAG:
++ NEEDBYTE
++ if ((b = NEXTBYTE) & 0x20)
++ {
++ z->state->mode = BAD;
++ z->msg = "invalid reserved bit";
++ z->state->sub.marker = 5; /* can't try inflateSync */
++ break;
++ }
++ if (((z->state->sub.method << 8) + b) % 31)
++ {
++ z->state->mode = BAD;
++ z->msg = "incorrect header check";
++ z->state->sub.marker = 5; /* can't try inflateSync */
++ break;
++ }
++ Trace((stderr, "inflate: zlib header ok\n"));
++ z->state->mode = BLOCKS;
++ case BLOCKS:
++ r = inflate_blocks(z->state->blocks, z, r);
++ if (f == Z_PACKET_FLUSH && z->avail_in == 0 && z->avail_out != 0)
++ r = inflate_packet_flush(z->state->blocks);
++ if (r == Z_DATA_ERROR)
++ {
++ z->state->mode = BAD;
++ z->state->sub.marker = 0; /* can try inflateSync */
++ break;
++ }
++ if (r != Z_STREAM_END)
++ return r;
++ r = Z_OK;
++ inflate_blocks_reset(z->state->blocks, z, &z->state->sub.check.was);
++ if (z->state->nowrap)
++ {
++ z->state->mode = DONE;
++ break;
++ }
++ z->state->mode = CHECK4;
++ case CHECK4:
++ NEEDBYTE
++ z->state->sub.check.need = (uLong)NEXTBYTE << 24;
++ z->state->mode = CHECK3;
++ case CHECK3:
++ NEEDBYTE
++ z->state->sub.check.need += (uLong)NEXTBYTE << 16;
++ z->state->mode = CHECK2;
++ case CHECK2:
++ NEEDBYTE
++ z->state->sub.check.need += (uLong)NEXTBYTE << 8;
++ z->state->mode = CHECK1;
++ case CHECK1:
++ NEEDBYTE
++ z->state->sub.check.need += (uLong)NEXTBYTE;
++
++ if (z->state->sub.check.was != z->state->sub.check.need)
++ {
++ z->state->mode = BAD;
++ z->msg = "incorrect data check";
++ z->state->sub.marker = 5; /* can't try inflateSync */
++ break;
++ }
++ Trace((stderr, "inflate: zlib check ok\n"));
++ z->state->mode = DONE;
++ case DONE:
++ return Z_STREAM_END;
++ case BAD:
++ return Z_DATA_ERROR;
++ default:
++ return Z_STREAM_ERROR;
++ }
++
++ empty:
++ if (f != Z_PACKET_FLUSH)
++ return r;
++ z->state->mode = BAD;
++ z->state->sub.marker = 0; /* can try inflateSync */
++ return Z_DATA_ERROR;
++}
++
++/*
++ * This subroutine adds the data at next_in/avail_in to the output history
++ * without performing any output. The output buffer must be "caught up";
++ * i.e. no pending output (hence s->read equals s->write), and the state must
++ * be BLOCKS (i.e. we should be willing to see the start of a series of
++ * BLOCKS). On exit, the output will also be caught up, and the checksum
++ * will have been updated if need be.
++ */
++
++int inflateIncomp(z)
++z_stream *z;
++{
++ if (z->state->mode != BLOCKS)
++ return Z_DATA_ERROR;
++ return inflate_addhistory(z->state->blocks, z);
++}
++
++
++int inflateSync(z)
++z_stream *z;
++{
++ uInt n; /* number of bytes to look at */
++ Bytef *p; /* pointer to bytes */
++ uInt m; /* number of marker bytes found in a row */
++ uLong r, w; /* temporaries to save total_in and total_out */
++
++ /* set up */
++ if (z == Z_NULL || z->state == Z_NULL)
++ return Z_STREAM_ERROR;
++ if (z->state->mode != BAD)
++ {
++ z->state->mode = BAD;
++ z->state->sub.marker = 0;
++ }
++ if ((n = z->avail_in) == 0)
++ return Z_BUF_ERROR;
++ p = z->next_in;
++ m = z->state->sub.marker;
++
++ /* search */
++ while (n && m < 4)
++ {
++ if (*p == (Byte)(m < 2 ? 0 : 0xff))
++ m++;
++ else if (*p)
++ m = 0;
++ else
++ m = 4 - m;
++ p++, n--;
++ }
++
++ /* restore */
++ z->total_in += p - z->next_in;
++ z->next_in = p;
++ z->avail_in = n;
++ z->state->sub.marker = m;
++
++ /* return no joy or set up to restart on a new block */
++ if (m != 4)
++ return Z_DATA_ERROR;
++ r = z->total_in; w = z->total_out;
++ inflateReset(z);
++ z->total_in = r; z->total_out = w;
++ z->state->mode = BLOCKS;
++ return Z_OK;
++}
++
++#undef NEEDBYTE
++#undef NEXTBYTE
++
++/*+++++*/
++/* infutil.h -- types and macros common to blocks and codes
++ * Copyright (C) 1995 Mark Adler
++ * For conditions of distribution and use, see copyright notice in zlib.h
++ */
++
++/* WARNING: this file should *not* be used by applications. It is
++ part of the implementation of the compression library and is
++ subject to change. Applications should only use zlib.h.
++ */
++
++/* inflate blocks semi-private state */
++struct inflate_blocks_state {
++
++ /* mode */
++ enum {
++ TYPE, /* get type bits (3, including end bit) */
++ LENS, /* get lengths for stored */
++ STORED, /* processing stored block */
++ TABLE, /* get table lengths */
++ BTREE, /* get bit lengths tree for a dynamic block */
++ DTREE, /* get length, distance trees for a dynamic block */
++ CODES, /* processing fixed or dynamic block */
++ DRY, /* output remaining window bytes */
++ DONEB, /* finished last block, done */
++ BADB} /* got a data error--stuck here */
++ mode; /* current inflate_block mode */
++
++ /* mode dependent information */
++ union {
++ uInt left; /* if STORED, bytes left to copy */
++ struct {
++ uInt table; /* table lengths (14 bits) */
++ uInt index; /* index into blens (or border) */
++ uIntf *blens; /* bit lengths of codes */
++ uInt bb; /* bit length tree depth */
++ inflate_huft *tb; /* bit length decoding tree */
++ int nblens; /* # elements allocated at blens */
++ } trees; /* if DTREE, decoding info for trees */
++ struct {
++ inflate_huft *tl, *td; /* trees to free */
++ inflate_codes_statef
++ *codes;
++ } decode; /* if CODES, current state */
++ } sub; /* submode */
++ uInt last; /* true if this block is the last block */
++
++ /* mode independent information */
++ uInt bitk; /* bits in bit buffer */
++ uLong bitb; /* bit buffer */
++ Bytef *window; /* sliding window */
++ Bytef *end; /* one byte after sliding window */
++ Bytef *read; /* window read pointer */
++ Bytef *write; /* window write pointer */
++ check_func checkfn; /* check function */
++ uLong check; /* check on output */
++
++};
++
++
++/* defines for inflate input/output */
++/* update pointers and return */
++#define UPDBITS {s->bitb=b;s->bitk=k;}
++#define UPDIN {z->avail_in=n;z->total_in+=p-z->next_in;z->next_in=p;}
++#define UPDOUT {s->write=q;}
++#define UPDATE {UPDBITS UPDIN UPDOUT}
++#define LEAVE {UPDATE return inflate_flush(s,z,r);}
++/* get bytes and bits */
++#define LOADIN {p=z->next_in;n=z->avail_in;b=s->bitb;k=s->bitk;}
++#define NEEDBYTE {if(n)r=Z_OK;else LEAVE}
++#define NEXTBYTE (n--,*p++)
++#define NEEDBITS(j) {while(k<(j)){NEEDBYTE;b|=((uLong)NEXTBYTE)<<k;k+=8;}}
++#define DUMPBITS(j) {b>>=(j);k-=(j);}
++/* output bytes */
++#define WAVAIL (q<s->read?s->read-q-1:s->end-q)
++#define LOADOUT {q=s->write;m=WAVAIL;}
++#define WRAP {if(q==s->end&&s->read!=s->window){q=s->window;m=WAVAIL;}}
++#define FLUSH {UPDOUT r=inflate_flush(s,z,r); LOADOUT}
++#define NEEDOUT {if(m==0){WRAP if(m==0){FLUSH WRAP if(m==0) LEAVE}}r=Z_OK;}
++#define OUTBYTE(a) {*q++=(Byte)(a);m--;}
++/* load local pointers */
++#define LOAD {LOADIN LOADOUT}
++
++/*
++ * The IBM 150 firmware munges the data right after _etext[]. This
++ * protects it. -- Cort
++ */
++local uInt protect_mask[] = {0, 0, 0, 0, 0, 0, 0, 0, 0 ,0 ,0 ,0};
++/* And'ing with mask[n] masks the lower n bits */
++local uInt inflate_mask[] = {
++ 0x0000,
++ 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff,
++ 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff
++};
++
++/* copy as much as possible from the sliding window to the output area */
++local int inflate_flush OF((
++ inflate_blocks_statef *,
++ z_stream *,
++ int));
++
++/*+++++*/
++/* inffast.h -- header to use inffast.c
++ * Copyright (C) 1995 Mark Adler
++ * For conditions of distribution and use, see copyright notice in zlib.h
++ */
++
++/* WARNING: this file should *not* be used by applications. It is
++ part of the implementation of the compression library and is
++ subject to change. Applications should only use zlib.h.
++ */
++
++local int inflate_fast OF((
++ uInt,
++ uInt,
++ inflate_huft *,
++ inflate_huft *,
++ inflate_blocks_statef *,
++ z_stream *));
++
++
++/*+++++*/
++/* infblock.c -- interpret and process block types to last block
++ * Copyright (C) 1995 Mark Adler
++ * For conditions of distribution and use, see copyright notice in zlib.h
++ */
++
++/* Table for deflate from PKZIP's appnote.txt. */
++local uInt border[] = { /* Order of the bit length code lengths */
++ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15};
++
++/*
++ Notes beyond the 1.93a appnote.txt:
++
++ 1. Distance pointers never point before the beginning of the output
++ stream.
++ 2. Distance pointers can point back across blocks, up to 32k away.
++ 3. There is an implied maximum of 7 bits for the bit length table and
++ 15 bits for the actual data.
++ 4. If only one code exists, then it is encoded using one bit. (Zero
++ would be more efficient, but perhaps a little confusing.) If two
++ codes exist, they are coded using one bit each (0 and 1).
++ 5. There is no way of sending zero distance codes--a dummy must be
++ sent if there are none. (History: a pre 2.0 version of PKZIP would
++ store blocks with no distance codes, but this was discovered to be
++ too harsh a criterion.) Valid only for 1.93a. 2.04c does allow
++ zero distance codes, which is sent as one code of zero bits in
++ length.
++ 6. There are up to 286 literal/length codes. Code 256 represents the
++ end-of-block. Note however that the static length tree defines
++ 288 codes just to fill out the Huffman codes. Codes 286 and 287
++ cannot be used though, since there is no length base or extra bits
++ defined for them. Similarily, there are up to 30 distance codes.
++ However, static trees define 32 codes (all 5 bits) to fill out the
++ Huffman codes, but the last two had better not show up in the data.
++ 7. Unzip can check dynamic Huffman blocks for complete code sets.
++ The exception is that a single code would not be complete (see #4).
++ 8. The five bits following the block type is really the number of
++ literal codes sent minus 257.
++ 9. Length codes 8,16,16 are interpreted as 13 length codes of 8 bits
++ (1+6+6). Therefore, to output three times the length, you output
++ three codes (1+1+1), whereas to output four times the same length,
++ you only need two codes (1+3). Hmm.
++ 10. In the tree reconstruction algorithm, Code = Code + Increment
++ only if BitLength(i) is not zero. (Pretty obvious.)
++ 11. Correction: 4 Bits: # of Bit Length codes - 4 (4 - 19)
++ 12. Note: length code 284 can represent 227-258, but length code 285
++ really is 258. The last length deserves its own, short code
++ since it gets used a lot in very redundant files. The length
++ 258 is special since 258 - 3 (the min match length) is 255.
++ 13. The literal/length and distance code bit lengths are read as a
++ single stream of lengths. It is possible (and advantageous) for
++ a repeat code (16, 17, or 18) to go across the boundary between
++ the two sets of lengths.
++ */
++
++
++local void inflate_blocks_reset(s, z, c)
++inflate_blocks_statef *s;
++z_stream *z;
++uLongf *c;
++{
++ if (s->checkfn != Z_NULL)
++ *c = s->check;
++ if (s->mode == BTREE || s->mode == DTREE)
++ ZFREE(z, s->sub.trees.blens, s->sub.trees.nblens * sizeof(uInt));
++ if (s->mode == CODES)
++ {
++ inflate_codes_free(s->sub.decode.codes, z);
++ inflate_trees_free(s->sub.decode.td, z);
++ inflate_trees_free(s->sub.decode.tl, z);
++ }
++ s->mode = TYPE;
++ s->bitk = 0;
++ s->bitb = 0;
++ s->read = s->write = s->window;
++ if (s->checkfn != Z_NULL)
++ s->check = (*s->checkfn)(0L, Z_NULL, 0);
++ Trace((stderr, "inflate: blocks reset\n"));
++}
++
++
++local inflate_blocks_statef *inflate_blocks_new(z, c, w)
++z_stream *z;
++check_func c;
++uInt w;
++{
++ inflate_blocks_statef *s;
++
++ if ((s = (inflate_blocks_statef *)ZALLOC
++ (z,1,sizeof(struct inflate_blocks_state))) == Z_NULL)
++ return s;
++ if ((s->window = (Bytef *)ZALLOC(z, 1, w)) == Z_NULL)
++ {
++ ZFREE(z, s, sizeof(struct inflate_blocks_state));
++ return Z_NULL;
++ }
++ s->end = s->window + w;
++ s->checkfn = c;
++ s->mode = TYPE;
++ Trace((stderr, "inflate: blocks allocated\n"));
++ inflate_blocks_reset(s, z, &s->check);
++ return s;
++}
++
++
++local int inflate_blocks(s, z, r)
++inflate_blocks_statef *s;
++z_stream *z;
++int r;
++{
++ uInt t; /* temporary storage */
++ uLong b; /* bit buffer */
++ uInt k; /* bits in bit buffer */
++ Bytef *p; /* input data pointer */
++ uInt n; /* bytes available there */
++ Bytef *q; /* output window write pointer */
++ uInt m; /* bytes to end of window or read pointer */
++
++ /* copy input/output information to locals (UPDATE macro restores) */
++ LOAD
++
++ /* process input based on current state */
++ while (1) switch (s->mode)
++ {
++ case TYPE:
++ NEEDBITS(3)
++ t = (uInt)b & 7;
++ s->last = t & 1;
++ switch (t >> 1)
++ {
++ case 0: /* stored */
++ Trace((stderr, "inflate: stored block%s\n",
++ s->last ? " (last)" : ""));
++ DUMPBITS(3)
++ t = k & 7; /* go to byte boundary */
++ DUMPBITS(t)
++ s->mode = LENS; /* get length of stored block */
++ break;
++ case 1: /* fixed */
++ Trace((stderr, "inflate: fixed codes block%s\n",
++ s->last ? " (last)" : ""));
++ {
++ uInt bl, bd;
++ inflate_huft *tl, *td;
++
++ inflate_trees_fixed(&bl, &bd, &tl, &td);
++ s->sub.decode.codes = inflate_codes_new(bl, bd, tl, td, z);
++ if (s->sub.decode.codes == Z_NULL)
++ {
++ r = Z_MEM_ERROR;
++ LEAVE
++ }
++ s->sub.decode.tl = Z_NULL; /* don't try to free these */
++ s->sub.decode.td = Z_NULL;
++ }
++ DUMPBITS(3)
++ s->mode = CODES;
++ break;
++ case 2: /* dynamic */
++ Trace((stderr, "inflate: dynamic codes block%s\n",
++ s->last ? " (last)" : ""));
++ DUMPBITS(3)
++ s->mode = TABLE;
++ break;
++ case 3: /* illegal */
++ DUMPBITS(3)
++ s->mode = BADB;
++ z->msg = "invalid block type";
++ r = Z_DATA_ERROR;
++ LEAVE
++ }
++ break;
++ case LENS:
++ NEEDBITS(32)
++ if (((~b) >> 16) != (b & 0xffff))
++ {
++ s->mode = BADB;
++ z->msg = "invalid stored block lengths";
++ r = Z_DATA_ERROR;
++ LEAVE
++ }
++ s->sub.left = (uInt)b & 0xffff;
++ b = k = 0; /* dump bits */
++ Tracev((stderr, "inflate: stored length %u\n", s->sub.left));
++ s->mode = s->sub.left ? STORED : TYPE;
++ break;
++ case STORED:
++ if (n == 0)
++ LEAVE
++ NEEDOUT
++ t = s->sub.left;
++ if (t > n) t = n;
++ if (t > m) t = m;
++ zmemcpy(q, p, t);
++ p += t; n -= t;
++ q += t; m -= t;
++ if ((s->sub.left -= t) != 0)
++ break;
++ Tracev((stderr, "inflate: stored end, %lu total out\n",
++ z->total_out + (q >= s->read ? q - s->read :
++ (s->end - s->read) + (q - s->window))));
++ s->mode = s->last ? DRY : TYPE;
++ break;
++ case TABLE:
++ NEEDBITS(14)
++ s->sub.trees.table = t = (uInt)b & 0x3fff;
++#ifndef PKZIP_BUG_WORKAROUND
++ if ((t & 0x1f) > 29 || ((t >> 5) & 0x1f) > 29)
++ {
++ s->mode = BADB;
++ z->msg = "too many length or distance symbols";
++ r = Z_DATA_ERROR;
++ LEAVE
++ }
++#endif
++ t = 258 + (t & 0x1f) + ((t >> 5) & 0x1f);
++ if (t < 19)
++ t = 19;
++ if ((s->sub.trees.blens = (uIntf*)ZALLOC(z, t, sizeof(uInt))) == Z_NULL)
++ {
++ r = Z_MEM_ERROR;
++ LEAVE
++ }
++ s->sub.trees.nblens = t;
++ DUMPBITS(14)
++ s->sub.trees.index = 0;
++ Tracev((stderr, "inflate: table sizes ok\n"));
++ s->mode = BTREE;
++ case BTREE:
++ while (s->sub.trees.index < 4 + (s->sub.trees.table >> 10))
++ {
++ NEEDBITS(3)
++ s->sub.trees.blens[border[s->sub.trees.index++]] = (uInt)b & 7;
++ DUMPBITS(3)
++ }
++ while (s->sub.trees.index < 19)
++ s->sub.trees.blens[border[s->sub.trees.index++]] = 0;
++ s->sub.trees.bb = 7;
++ t = inflate_trees_bits(s->sub.trees.blens, &s->sub.trees.bb,
++ &s->sub.trees.tb, z);
++ if (t != Z_OK)
++ {
++ r = t;
++ if (r == Z_DATA_ERROR)
++ s->mode = BADB;
++ LEAVE
++ }
++ s->sub.trees.index = 0;
++ Tracev((stderr, "inflate: bits tree ok\n"));
++ s->mode = DTREE;
++ case DTREE:
++ while (t = s->sub.trees.table,
++ s->sub.trees.index < 258 + (t & 0x1f) + ((t >> 5) & 0x1f))
++ {
++ inflate_huft *h;
++ uInt i, j, c;
++
++ t = s->sub.trees.bb;
++ NEEDBITS(t)
++ h = s->sub.trees.tb + ((uInt)b & inflate_mask[t]);
++ t = h->word.what.Bits;
++ c = h->more.Base;
++ if (c < 16)
++ {
++ DUMPBITS(t)
++ s->sub.trees.blens[s->sub.trees.index++] = c;
++ }
++ else /* c == 16..18 */
++ {
++ i = c == 18 ? 7 : c - 14;
++ j = c == 18 ? 11 : 3;
++ NEEDBITS(t + i)
++ DUMPBITS(t)
++ j += (uInt)b & inflate_mask[i];
++ DUMPBITS(i)
++ i = s->sub.trees.index;
++ t = s->sub.trees.table;
++ if (i + j > 258 + (t & 0x1f) + ((t >> 5) & 0x1f) ||
++ (c == 16 && i < 1))
++ {
++ s->mode = BADB;
++ z->msg = "invalid bit length repeat";
++ r = Z_DATA_ERROR;
++ LEAVE
++ }
++ c = c == 16 ? s->sub.trees.blens[i - 1] : 0;
++ do {
++ s->sub.trees.blens[i++] = c;
++ } while (--j);
++ s->sub.trees.index = i;
++ }
++ }
++ inflate_trees_free(s->sub.trees.tb, z);
++ s->sub.trees.tb = Z_NULL;
++ {
++ uInt bl, bd;
++ inflate_huft *tl, *td;
++ inflate_codes_statef *c;
++
++ bl = 9; /* must be <= 9 for lookahead assumptions */
++ bd = 6; /* must be <= 9 for lookahead assumptions */
++ t = s->sub.trees.table;
++ t = inflate_trees_dynamic(257 + (t & 0x1f), 1 + ((t >> 5) & 0x1f),
++ s->sub.trees.blens, &bl, &bd, &tl, &td, z);
++ if (t != Z_OK)
++ {
++ if (t == (uInt)Z_DATA_ERROR)
++ s->mode = BADB;
++ r = t;
++ LEAVE
++ }
++ Tracev((stderr, "inflate: trees ok\n"));
++ if ((c = inflate_codes_new(bl, bd, tl, td, z)) == Z_NULL)
++ {
++ inflate_trees_free(td, z);
++ inflate_trees_free(tl, z);
++ r = Z_MEM_ERROR;
++ LEAVE
++ }
++ ZFREE(z, s->sub.trees.blens, s->sub.trees.nblens * sizeof(uInt));
++ s->sub.decode.codes = c;
++ s->sub.decode.tl = tl;
++ s->sub.decode.td = td;
++ }
++ s->mode = CODES;
++ case CODES:
++ UPDATE
++ if ((r = inflate_codes(s, z, r)) != Z_STREAM_END)
++ return inflate_flush(s, z, r);
++ r = Z_OK;
++ inflate_codes_free(s->sub.decode.codes, z);
++ inflate_trees_free(s->sub.decode.td, z);
++ inflate_trees_free(s->sub.decode.tl, z);
++ LOAD
++ Tracev((stderr, "inflate: codes end, %lu total out\n",
++ z->total_out + (q >= s->read ? q - s->read :
++ (s->end - s->read) + (q - s->window))));
++ if (!s->last)
++ {
++ s->mode = TYPE;
++ break;
++ }
++ if (k > 7) /* return unused byte, if any */
++ {
++ Assert(k < 16, "inflate_codes grabbed too many bytes")
++ k -= 8;
++ n++;
++ p--; /* can always return one */
++ }
++ s->mode = DRY;
++ case DRY:
++ FLUSH
++ if (s->read != s->write)
++ LEAVE
++ s->mode = DONEB;
++ case DONEB:
++ r = Z_STREAM_END;
++ LEAVE
++ case BADB:
++ r = Z_DATA_ERROR;
++ LEAVE
++ default:
++ r = Z_STREAM_ERROR;
++ LEAVE
++ }
++}
++
++
++local int inflate_blocks_free(s, z, c)
++inflate_blocks_statef *s;
++z_stream *z;
++uLongf *c;
++{
++ inflate_blocks_reset(s, z, c);
++ ZFREE(z, s->window, s->end - s->window);
++ ZFREE(z, s, sizeof(struct inflate_blocks_state));
++ Trace((stderr, "inflate: blocks freed\n"));
++ return Z_OK;
++}
++
++/*
++ * This subroutine adds the data at next_in/avail_in to the output history
++ * without performing any output. The output buffer must be "caught up";
++ * i.e. no pending output (hence s->read equals s->write), and the state must
++ * be BLOCKS (i.e. we should be willing to see the start of a series of
++ * BLOCKS). On exit, the output will also be caught up, and the checksum
++ * will have been updated if need be.
++ */
++local int inflate_addhistory(s, z)
++inflate_blocks_statef *s;
++z_stream *z;
++{
++ uLong b; /* bit buffer */ /* NOT USED HERE */
++ uInt k; /* bits in bit buffer */ /* NOT USED HERE */
++ uInt t; /* temporary storage */
++ Bytef *p; /* input data pointer */
++ uInt n; /* bytes available there */
++ Bytef *q; /* output window write pointer */
++ uInt m; /* bytes to end of window or read pointer */
++
++ if (s->read != s->write)
++ return Z_STREAM_ERROR;
++ if (s->mode != TYPE)
++ return Z_DATA_ERROR;
++
++ /* we're ready to rock */
++ LOAD
++ /* while there is input ready, copy to output buffer, moving
++ * pointers as needed.
++ */
++ while (n) {
++ t = n; /* how many to do */
++ /* is there room until end of buffer? */
++ if (t > m) t = m;
++ /* update check information */
++ if (s->checkfn != Z_NULL)
++ s->check = (*s->checkfn)(s->check, q, t);
++ zmemcpy(q, p, t);
++ q += t;
++ p += t;
++ n -= t;
++ z->total_out += t;
++ s->read = q; /* drag read pointer forward */
++/* WRAP */ /* expand WRAP macro by hand to handle s->read */
++ if (q == s->end) {
++ s->read = q = s->window;
++ m = WAVAIL;
++ }
++ }
++ UPDATE
++ return Z_OK;
++}
++
++
++/*
++ * At the end of a Deflate-compressed PPP packet, we expect to have seen
++ * a `stored' block type value but not the (zero) length bytes.
++ */
++local int inflate_packet_flush(s)
++ inflate_blocks_statef *s;
++{
++ if (s->mode != LENS)
++ return Z_DATA_ERROR;
++ s->mode = TYPE;
++ return Z_OK;
++}
++
++
++/*+++++*/
++/* inftrees.c -- generate Huffman trees for efficient decoding
++ * Copyright (C) 1995 Mark Adler
++ * For conditions of distribution and use, see copyright notice in zlib.h
++ */
++
++/* simplify the use of the inflate_huft type with some defines */
++#define base more.Base
++#define next more.Next
++#define exop word.what.Exop
++#define bits word.what.Bits
++
++
++local int huft_build OF((
++ uIntf *, /* code lengths in bits */
++ uInt, /* number of codes */
++ uInt, /* number of "simple" codes */
++ uIntf *, /* list of base values for non-simple codes */
++ uIntf *, /* list of extra bits for non-simple codes */
++ inflate_huft * FAR*,/* result: starting table */
++ uIntf *, /* maximum lookup bits (returns actual) */
++ z_stream *)); /* for zalloc function */
++
++local voidpf falloc OF((
++ voidpf, /* opaque pointer (not used) */
++ uInt, /* number of items */
++ uInt)); /* size of item */
++
++local void ffree OF((
++ voidpf q, /* opaque pointer (not used) */
++ voidpf p, /* what to free (not used) */
++ uInt n)); /* number of bytes (not used) */
++
++/* Tables for deflate from PKZIP's appnote.txt. */
++local uInt cplens[] = { /* Copy lengths for literal codes 257..285 */
++ 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
++ 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0};
++ /* actually lengths - 2; also see note #13 above about 258 */
++local uInt cplext[] = { /* Extra bits for literal codes 257..285 */
++ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2,
++ 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 192, 192}; /* 192==invalid */
++local uInt cpdist[] = { /* Copy offsets for distance codes 0..29 */
++ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
++ 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
++ 8193, 12289, 16385, 24577};
++local uInt cpdext[] = { /* Extra bits for distance codes */
++ 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6,
++ 7, 7, 8, 8, 9, 9, 10, 10, 11, 11,
++ 12, 12, 13, 13};
++
++/*
++ Huffman code decoding is performed using a multi-level table lookup.
++ The fastest way to decode is to simply build a lookup table whose
++ size is determined by the longest code. However, the time it takes
++ to build this table can also be a factor if the data being decoded
++ is not very long. The most common codes are necessarily the
++ shortest codes, so those codes dominate the decoding time, and hence
++ the speed. The idea is you can have a shorter table that decodes the
++ shorter, more probable codes, and then point to subsidiary tables for
++ the longer codes. The time it costs to decode the longer codes is
++ then traded against the time it takes to make longer tables.
++
++ This results of this trade are in the variables lbits and dbits
++ below. lbits is the number of bits the first level table for literal/
++ length codes can decode in one step, and dbits is the same thing for
++ the distance codes. Subsequent tables are also less than or equal to
++ those sizes. These values may be adjusted either when all of the
++ codes are shorter than that, in which case the longest code length in
++ bits is used, or when the shortest code is *longer* than the requested
++ table size, in which case the length of the shortest code in bits is
++ used.
++
++ There are two different values for the two tables, since they code a
++ different number of possibilities each. The literal/length table
++ codes 286 possible values, or in a flat code, a little over eight
++ bits. The distance table codes 30 possible values, or a little less
++ than five bits, flat. The optimum values for speed end up being
++ about one bit more than those, so lbits is 8+1 and dbits is 5+1.
++ The optimum values may differ though from machine to machine, and
++ possibly even between compilers. Your mileage may vary.
++ */
++
++
++/* If BMAX needs to be larger than 16, then h and x[] should be uLong. */
++#define BMAX 15 /* maximum bit length of any code */
++#define N_MAX 288 /* maximum number of codes in any set */
++
++#ifdef DEBUG_ZLIB
++ uInt inflate_hufts;
++#endif
++
++local int huft_build(b, n, s, d, e, t, m, zs)
++uIntf *b; /* code lengths in bits (all assumed <= BMAX) */
++uInt n; /* number of codes (assumed <= N_MAX) */
++uInt s; /* number of simple-valued codes (0..s-1) */
++uIntf *d; /* list of base values for non-simple codes */
++uIntf *e; /* list of extra bits for non-simple codes */
++inflate_huft * FAR *t; /* result: starting table */
++uIntf *m; /* maximum lookup bits, returns actual */
++z_stream *zs; /* for zalloc function */
++/* Given a list of code lengths and a maximum table size, make a set of
++ tables to decode that set of codes. Return Z_OK on success, Z_BUF_ERROR
++ if the given code set is incomplete (the tables are still built in this
++ case), Z_DATA_ERROR if the input is invalid (all zero length codes or an
++ over-subscribed set of lengths), or Z_MEM_ERROR if not enough memory. */
++{
++
++ uInt a; /* counter for codes of length k */
++ uInt c[BMAX+1]; /* bit length count table */
++ uInt f; /* i repeats in table every f entries */
++ int g; /* maximum code length */
++ int h; /* table level */
++ register uInt i; /* counter, current code */
++ register uInt j; /* counter */
++ register int k; /* number of bits in current code */
++ int l; /* bits per table (returned in m) */
++ register uIntf *p; /* pointer into c[], b[], or v[] */
++ inflate_huft *q; /* points to current table */
++ struct inflate_huft_s r; /* table entry for structure assignment */
++ inflate_huft *u[BMAX]; /* table stack */
++ uInt v[N_MAX]; /* values in order of bit length */
++ register int w; /* bits before this table == (l * h) */
++ uInt x[BMAX+1]; /* bit offsets, then code stack */
++ uIntf *xp; /* pointer into x */
++ int y; /* number of dummy codes added */
++ uInt z; /* number of entries in current table */
++
++
++ /* Generate counts for each bit length */
++ p = c;
++#define C0 *p++ = 0;
++#define C2 C0 C0 C0 C0
++#define C4 C2 C2 C2 C2
++ C4 /* clear c[]--assume BMAX+1 is 16 */
++ p = b; i = n;
++ do {
++ c[*p++]++; /* assume all entries <= BMAX */
++ } while (--i);
++ if (c[0] == n) /* null input--all zero length codes */
++ {
++ *t = (inflate_huft *)Z_NULL;
++ *m = 0;
++ return Z_OK;
++ }
++
++
++ /* Find minimum and maximum length, bound *m by those */
++ l = *m;
++ for (j = 1; j <= BMAX; j++)
++ if (c[j])
++ break;
++ k = j; /* minimum code length */
++ if ((uInt)l < j)
++ l = j;
++ for (i = BMAX; i; i--)
++ if (c[i])
++ break;
++ g = i; /* maximum code length */
++ if ((uInt)l > i)
++ l = i;
++ *m = l;
++
++
++ /* Adjust last length count to fill out codes, if needed */
++ for (y = 1 << j; j < i; j++, y <<= 1)
++ if ((y -= c[j]) < 0)
++ return Z_DATA_ERROR;
++ if ((y -= c[i]) < 0)
++ return Z_DATA_ERROR;
++ c[i] += y;
++
++
++ /* Generate starting offsets into the value table for each length */
++ x[1] = j = 0;
++ p = c + 1; xp = x + 2;
++ while (--i) { /* note that i == g from above */
++ *xp++ = (j += *p++);
++ }
++
++
++ /* Make a table of values in order of bit lengths */
++ p = b; i = 0;
++ do {
++ if ((j = *p++) != 0)
++ v[x[j]++] = i;
++ } while (++i < n);
++
++
++ /* Generate the Huffman codes and for each, make the table entries */
++ x[0] = i = 0; /* first Huffman code is zero */
++ p = v; /* grab values in bit order */
++ h = -1; /* no tables yet--level -1 */
++ w = -l; /* bits decoded == (l * h) */
++ u[0] = (inflate_huft *)Z_NULL; /* just to keep compilers happy */
++ q = (inflate_huft *)Z_NULL; /* ditto */
++ z = 0; /* ditto */
++
++ /* go through the bit lengths (k already is bits in shortest code) */
++ for (; k <= g; k++)
++ {
++ a = c[k];
++ while (a--)
++ {
++ /* here i is the Huffman code of length k bits for value *p */
++ /* make tables up to required level */
++ while (k > w + l)
++ {
++ h++;
++ w += l; /* previous table always l bits */
++
++ /* compute minimum size table less than or equal to l bits */
++ z = (z = g - w) > (uInt)l ? l : z; /* table size upper limit */
++ if ((f = 1 << (j = k - w)) > a + 1) /* try a k-w bit table */
++ { /* too few codes for k-w bit table */
++ f -= a + 1; /* deduct codes from patterns left */
++ xp = c + k;
++ if (j < z)
++ while (++j < z) /* try smaller tables up to z bits */
++ {
++ if ((f <<= 1) <= *++xp)
++ break; /* enough codes to use up j bits */
++ f -= *xp; /* else deduct codes from patterns */
++ }
++ }
++ z = 1 << j; /* table entries for j-bit table */
++
++ /* allocate and link in new table */
++ if ((q = (inflate_huft *)ZALLOC
++ (zs,z + 1,sizeof(inflate_huft))) == Z_NULL)
++ {
++ if (h)
++ inflate_trees_free(u[0], zs);
++ return Z_MEM_ERROR; /* not enough memory */
++ }
++ q->word.Nalloc = z + 1;
++#ifdef DEBUG_ZLIB
++ inflate_hufts += z + 1;
++#endif
++ *t = q + 1; /* link to list for huft_free() */
++ *(t = &(q->next)) = Z_NULL;
++ u[h] = ++q; /* table starts after link */
++
++ /* connect to last table, if there is one */
++ if (h)
++ {
++ x[h] = i; /* save pattern for backing up */
++ r.bits = (Byte)l; /* bits to dump before this table */
++ r.exop = (Byte)j; /* bits in this table */
++ r.next = q; /* pointer to this table */
++ j = i >> (w - l); /* (get around Turbo C bug) */
++ u[h-1][j] = r; /* connect to last table */
++ }
++ }
++
++ /* set up table entry in r */
++ r.bits = (Byte)(k - w);
++ if (p >= v + n)
++ r.exop = 128 + 64; /* out of values--invalid code */
++ else if (*p < s)
++ {
++ r.exop = (Byte)(*p < 256 ? 0 : 32 + 64); /* 256 is end-of-block */
++ r.base = *p++; /* simple code is just the value */
++ }
++ else
++ {
++ r.exop = (Byte)e[*p - s] + 16 + 64; /* non-simple--look up in lists */
++ r.base = d[*p++ - s];
++ }
++
++ /* fill code-like entries with r */
++ f = 1 << (k - w);
++ for (j = i >> w; j < z; j += f)
++ q[j] = r;
++
++ /* backwards increment the k-bit code i */
++ for (j = 1 << (k - 1); i & j; j >>= 1)
++ i ^= j;
++ i ^= j;
++
++ /* backup over finished tables */
++ while ((i & ((1 << w) - 1)) != x[h])
++ {
++ h--; /* don't need to update q */
++ w -= l;
++ }
++ }
++ }
++
++
++ /* Return Z_BUF_ERROR if we were given an incomplete table */
++ return y != 0 && g != 1 ? Z_BUF_ERROR : Z_OK;
++}
++
++
++local int inflate_trees_bits(c, bb, tb, z)
++uIntf *c; /* 19 code lengths */
++uIntf *bb; /* bits tree desired/actual depth */
++inflate_huft * FAR *tb; /* bits tree result */
++z_stream *z; /* for zfree function */
++{
++ int r;
++
++ r = huft_build(c, 19, 19, (uIntf*)Z_NULL, (uIntf*)Z_NULL, tb, bb, z);
++ if (r == Z_DATA_ERROR)
++ z->msg = "oversubscribed dynamic bit lengths tree";
++ else if (r == Z_BUF_ERROR)
++ {
++ inflate_trees_free(*tb, z);
++ z->msg = "incomplete dynamic bit lengths tree";
++ r = Z_DATA_ERROR;
++ }
++ return r;
++}
++
++
++local int inflate_trees_dynamic(nl, nd, c, bl, bd, tl, td, z)
++uInt nl; /* number of literal/length codes */
++uInt nd; /* number of distance codes */
++uIntf *c; /* that many (total) code lengths */
++uIntf *bl; /* literal desired/actual bit depth */
++uIntf *bd; /* distance desired/actual bit depth */
++inflate_huft * FAR *tl; /* literal/length tree result */
++inflate_huft * FAR *td; /* distance tree result */
++z_stream *z; /* for zfree function */
++{
++ int r;
++
++ /* build literal/length tree */
++ if ((r = huft_build(c, nl, 257, cplens, cplext, tl, bl, z)) != Z_OK)
++ {
++ if (r == Z_DATA_ERROR)
++ z->msg = "oversubscribed literal/length tree";
++ else if (r == Z_BUF_ERROR)
++ {
++ inflate_trees_free(*tl, z);
++ z->msg = "incomplete literal/length tree";
++ r = Z_DATA_ERROR;
++ }
++ return r;
++ }
++
++ /* build distance tree */
++ if ((r = huft_build(c + nl, nd, 0, cpdist, cpdext, td, bd, z)) != Z_OK)
++ {
++ if (r == Z_DATA_ERROR)
++ z->msg = "oversubscribed literal/length tree";
++ else if (r == Z_BUF_ERROR) {
++#ifdef PKZIP_BUG_WORKAROUND
++ r = Z_OK;
++ }
++#else
++ inflate_trees_free(*td, z);
++ z->msg = "incomplete literal/length tree";
++ r = Z_DATA_ERROR;
++ }
++ inflate_trees_free(*tl, z);
++ return r;
++#endif
++ }
++
++ /* done */
++ return Z_OK;
++}
++
++
++/* build fixed tables only once--keep them here */
++local int fixed_lock = 0;
++local int fixed_built = 0;
++#define FIXEDH 530 /* number of hufts used by fixed tables */
++local uInt fixed_left = FIXEDH;
++local inflate_huft fixed_mem[FIXEDH];
++local uInt fixed_bl;
++local uInt fixed_bd;
++local inflate_huft *fixed_tl;
++local inflate_huft *fixed_td;
++
++
++local voidpf falloc(q, n, s)
++voidpf q; /* opaque pointer (not used) */
++uInt n; /* number of items */
++uInt s; /* size of item */
++{
++ Assert(s == sizeof(inflate_huft) && n <= fixed_left,
++ "inflate_trees falloc overflow");
++ if (q) s++; /* to make some compilers happy */
++ fixed_left -= n;
++ return (voidpf)(fixed_mem + fixed_left);
++}
++
++
++local void ffree(q, p, n)
++voidpf q;
++voidpf p;
++uInt n;
++{
++ Assert(0, "inflate_trees ffree called!");
++ if (q) q = p; /* to make some compilers happy */
++}
++
++
++local int inflate_trees_fixed(bl, bd, tl, td)
++uIntf *bl; /* literal desired/actual bit depth */
++uIntf *bd; /* distance desired/actual bit depth */
++inflate_huft * FAR *tl; /* literal/length tree result */
++inflate_huft * FAR *td; /* distance tree result */
++{
++ /* build fixed tables if not built already--lock out other instances */
++ while (++fixed_lock > 1)
++ fixed_lock--;
++ if (!fixed_built)
++ {
++ int k; /* temporary variable */
++ unsigned c[288]; /* length list for huft_build */
++ z_stream z; /* for falloc function */
++
++ /* set up fake z_stream for memory routines */
++ z.zalloc = falloc;
++ z.zfree = ffree;
++ z.opaque = Z_NULL;
++
++ /* literal table */
++ for (k = 0; k < 144; k++)
++ c[k] = 8;
++ for (; k < 256; k++)
++ c[k] = 9;
++ for (; k < 280; k++)
++ c[k] = 7;
++ for (; k < 288; k++)
++ c[k] = 8;
++ fixed_bl = 7;
++ huft_build(c, 288, 257, cplens, cplext, &fixed_tl, &fixed_bl, &z);
++
++ /* distance table */
++ for (k = 0; k < 30; k++)
++ c[k] = 5;
++ fixed_bd = 5;
++ huft_build(c, 30, 0, cpdist, cpdext, &fixed_td, &fixed_bd, &z);
++
++ /* done */
++ fixed_built = 1;
++ }
++ fixed_lock--;
++ *bl = fixed_bl;
++ *bd = fixed_bd;
++ *tl = fixed_tl;
++ *td = fixed_td;
++ return Z_OK;
++}
++
++
++local int inflate_trees_free(t, z)
++inflate_huft *t; /* table to free */
++z_stream *z; /* for zfree function */
++/* Free the malloc'ed tables built by huft_build(), which makes a linked
++ list of the tables it made, with the links in a dummy first entry of
++ each table. */
++{
++ register inflate_huft *p, *q;
++
++ /* Go through linked list, freeing from the malloced (t[-1]) address. */
++ p = t;
++ while (p != Z_NULL)
++ {
++ q = (--p)->next;
++ ZFREE(z, p, p->word.Nalloc * sizeof(inflate_huft));
++ p = q;
++ }
++ return Z_OK;
++}
++
++/*+++++*/
++/* infcodes.c -- process literals and length/distance pairs
++ * Copyright (C) 1995 Mark Adler
++ * For conditions of distribution and use, see copyright notice in zlib.h
++ */
++
++/* simplify the use of the inflate_huft type with some defines */
++#define base more.Base
++#define next more.Next
++#define exop word.what.Exop
++#define bits word.what.Bits
++
++/* inflate codes private state */
++struct inflate_codes_state {
++
++ /* mode */
++ enum { /* waiting for "i:"=input, "o:"=output, "x:"=nothing */
++ START, /* x: set up for LEN */
++ LEN, /* i: get length/literal/eob next */
++ LENEXT, /* i: getting length extra (have base) */
++ DIST, /* i: get distance next */
++ DISTEXT, /* i: getting distance extra */
++ COPY, /* o: copying bytes in window, waiting for space */
++ LIT, /* o: got literal, waiting for output space */
++ WASH, /* o: got eob, possibly still output waiting */
++ END, /* x: got eob and all data flushed */
++ BADCODE} /* x: got error */
++ mode; /* current inflate_codes mode */
++
++ /* mode dependent information */
++ uInt len;
++ union {
++ struct {
++ inflate_huft *tree; /* pointer into tree */
++ uInt need; /* bits needed */
++ } code; /* if LEN or DIST, where in tree */
++ uInt lit; /* if LIT, literal */
++ struct {
++ uInt get; /* bits to get for extra */
++ uInt dist; /* distance back to copy from */
++ } copy; /* if EXT or COPY, where and how much */
++ } sub; /* submode */
++
++ /* mode independent information */
++ Byte lbits; /* ltree bits decoded per branch */
++ Byte dbits; /* dtree bits decoder per branch */
++ inflate_huft *ltree; /* literal/length/eob tree */
++ inflate_huft *dtree; /* distance tree */
++
++};
++
++
++local inflate_codes_statef *inflate_codes_new(bl, bd, tl, td, z)
++uInt bl, bd;
++inflate_huft *tl, *td;
++z_stream *z;
++{
++ inflate_codes_statef *c;
++
++ if ((c = (inflate_codes_statef *)
++ ZALLOC(z,1,sizeof(struct inflate_codes_state))) != Z_NULL)
++ {
++ c->mode = START;
++ c->lbits = (Byte)bl;
++ c->dbits = (Byte)bd;
++ c->ltree = tl;
++ c->dtree = td;
++ Tracev((stderr, "inflate: codes new\n"));
++ }
++ return c;
++}
++
++
++local int inflate_codes(s, z, r)
++inflate_blocks_statef *s;
++z_stream *z;
++int r;
++{
++ uInt j; /* temporary storage */
++ inflate_huft *t; /* temporary pointer */
++ uInt e; /* extra bits or operation */
++ uLong b; /* bit buffer */
++ uInt k; /* bits in bit buffer */
++ Bytef *p; /* input data pointer */
++ uInt n; /* bytes available there */
++ Bytef *q; /* output window write pointer */
++ uInt m; /* bytes to end of window or read pointer */
++ Bytef *f; /* pointer to copy strings from */
++ inflate_codes_statef *c = s->sub.decode.codes; /* codes state */
++
++ /* copy input/output information to locals (UPDATE macro restores) */
++ LOAD
++
++ /* process input and output based on current state */
++ while (1) switch (c->mode)
++ { /* waiting for "i:"=input, "o:"=output, "x:"=nothing */
++ case START: /* x: set up for LEN */
++#ifndef SLOW
++ if (m >= 258 && n >= 10)
++ {
++ UPDATE
++ r = inflate_fast(c->lbits, c->dbits, c->ltree, c->dtree, s, z);
++ LOAD
++ if (r != Z_OK)
++ {
++ c->mode = r == Z_STREAM_END ? WASH : BADCODE;
++ break;
++ }
++ }
++#endif /* !SLOW */
++ c->sub.code.need = c->lbits;
++ c->sub.code.tree = c->ltree;
++ c->mode = LEN;
++ case LEN: /* i: get length/literal/eob next */
++ j = c->sub.code.need;
++ NEEDBITS(j)
++ t = c->sub.code.tree + ((uInt)b & inflate_mask[j]);
++ DUMPBITS(t->bits)
++ e = (uInt)(t->exop);
++ if (e == 0) /* literal */
++ {
++ c->sub.lit = t->base;
++ Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ?
++ "inflate: literal '%c'\n" :
++ "inflate: literal 0x%02x\n", t->base));
++ c->mode = LIT;
++ break;
++ }
++ if (e & 16) /* length */
++ {
++ c->sub.copy.get = e & 15;
++ c->len = t->base;
++ c->mode = LENEXT;
++ break;
++ }
++ if ((e & 64) == 0) /* next table */
++ {
++ c->sub.code.need = e;
++ c->sub.code.tree = t->next;
++ break;
++ }
++ if (e & 32) /* end of block */
++ {
++ Tracevv((stderr, "inflate: end of block\n"));
++ c->mode = WASH;
++ break;
++ }
++ c->mode = BADCODE; /* invalid code */
++ z->msg = "invalid literal/length code";
++ r = Z_DATA_ERROR;
++ LEAVE
++ case LENEXT: /* i: getting length extra (have base) */
++ j = c->sub.copy.get;
++ NEEDBITS(j)
++ c->len += (uInt)b & inflate_mask[j];
++ DUMPBITS(j)
++ c->sub.code.need = c->dbits;
++ c->sub.code.tree = c->dtree;
++ Tracevv((stderr, "inflate: length %u\n", c->len));
++ c->mode = DIST;
++ case DIST: /* i: get distance next */
++ j = c->sub.code.need;
++ NEEDBITS(j)
++ t = c->sub.code.tree + ((uInt)b & inflate_mask[j]);
++ DUMPBITS(t->bits)
++ e = (uInt)(t->exop);
++ if (e & 16) /* distance */
++ {
++ c->sub.copy.get = e & 15;
++ c->sub.copy.dist = t->base;
++ c->mode = DISTEXT;
++ break;
++ }
++ if ((e & 64) == 0) /* next table */
++ {
++ c->sub.code.need = e;
++ c->sub.code.tree = t->next;
++ break;
++ }
++ c->mode = BADCODE; /* invalid code */
++ z->msg = "invalid distance code";
++ r = Z_DATA_ERROR;
++ LEAVE
++ case DISTEXT: /* i: getting distance extra */
++ j = c->sub.copy.get;
++ NEEDBITS(j)
++ c->sub.copy.dist += (uInt)b & inflate_mask[j];
++ DUMPBITS(j)
++ Tracevv((stderr, "inflate: distance %u\n", c->sub.copy.dist));
++ c->mode = COPY;
++ case COPY: /* o: copying bytes in window, waiting for space */
++#ifndef __TURBOC__ /* Turbo C bug for following expression */
++ f = (uInt)(q - s->window) < c->sub.copy.dist ?
++ s->end - (c->sub.copy.dist - (q - s->window)) :
++ q - c->sub.copy.dist;
++#else
++ f = q - c->sub.copy.dist;
++ if ((uInt)(q - s->window) < c->sub.copy.dist)
++ f = s->end - (c->sub.copy.dist - (q - s->window));
++#endif
++ while (c->len)
++ {
++ NEEDOUT
++ OUTBYTE(*f++)
++ if (f == s->end)
++ f = s->window;
++ c->len--;
++ }
++ c->mode = START;
++ break;
++ case LIT: /* o: got literal, waiting for output space */
++ NEEDOUT
++ OUTBYTE(c->sub.lit)
++ c->mode = START;
++ break;
++ case WASH: /* o: got eob, possibly more output */
++ FLUSH
++ if (s->read != s->write)
++ LEAVE
++ c->mode = END;
++ case END:
++ r = Z_STREAM_END;
++ LEAVE
++ case BADCODE: /* x: got error */
++ r = Z_DATA_ERROR;
++ LEAVE
++ default:
++ r = Z_STREAM_ERROR;
++ LEAVE
++ }
++}
++
++
++local void inflate_codes_free(c, z)
++inflate_codes_statef *c;
++z_stream *z;
++{
++ ZFREE(z, c, sizeof(struct inflate_codes_state));
++ Tracev((stderr, "inflate: codes free\n"));
++}
++
++/*+++++*/
++/* inflate_util.c -- data and routines common to blocks and codes
++ * Copyright (C) 1995 Mark Adler
++ * For conditions of distribution and use, see copyright notice in zlib.h
++ */
++
++/* copy as much as possible from the sliding window to the output area */
++local int inflate_flush(s, z, r)
++inflate_blocks_statef *s;
++z_stream *z;
++int r;
++{
++ uInt n;
++ Bytef *p, *q;
++
++ /* local copies of source and destination pointers */
++ p = z->next_out;
++ q = s->read;
++
++ /* compute number of bytes to copy as far as end of window */
++ n = (uInt)((q <= s->write ? s->write : s->end) - q);
++ if (n > z->avail_out) n = z->avail_out;
++ if (n && r == Z_BUF_ERROR) r = Z_OK;
++
++ /* update counters */
++ z->avail_out -= n;
++ z->total_out += n;
++
++ /* update check information */
++ if (s->checkfn != Z_NULL)
++ s->check = (*s->checkfn)(s->check, q, n);
++
++ /* copy as far as end of window */
++ zmemcpy(p, q, n);
++ p += n;
++ q += n;
++
++ /* see if more to copy at beginning of window */
++ if (q == s->end)
++ {
++ /* wrap pointers */
++ q = s->window;
++ if (s->write == s->end)
++ s->write = s->window;
++
++ /* compute bytes to copy */
++ n = (uInt)(s->write - q);
++ if (n > z->avail_out) n = z->avail_out;
++ if (n && r == Z_BUF_ERROR) r = Z_OK;
++
++ /* update counters */
++ z->avail_out -= n;
++ z->total_out += n;
++
++ /* update check information */
++ if (s->checkfn != Z_NULL)
++ s->check = (*s->checkfn)(s->check, q, n);
++
++ /* copy */
++ zmemcpy(p, q, n);
++ p += n;
++ q += n;
++ }
++
++ /* update pointers */
++ z->next_out = p;
++ s->read = q;
++
++ /* done */
++ return r;
++}
++
++
++/*+++++*/
++/* inffast.c -- process literals and length/distance pairs fast
++ * Copyright (C) 1995 Mark Adler
++ * For conditions of distribution and use, see copyright notice in zlib.h
++ */
++
++/* simplify the use of the inflate_huft type with some defines */
++#define base more.Base
++#define next more.Next
++#define exop word.what.Exop
++#define bits word.what.Bits
++
++/* macros for bit input with no checking and for returning unused bytes */
++#define GRABBITS(j) {while(k<(j)){b|=((uLong)NEXTBYTE)<<k;k+=8;}}
++#define UNGRAB {n+=(c=k>>3);p-=c;k&=7;}
++
++/* Called with number of bytes left to write in window at least 258
++ (the maximum string length) and number of input bytes available
++ at least ten. The ten bytes are six bytes for the longest length/
++ distance pair plus four bytes for overloading the bit buffer. */
++
++local int inflate_fast(bl, bd, tl, td, s, z)
++uInt bl, bd;
++inflate_huft *tl, *td;
++inflate_blocks_statef *s;
++z_stream *z;
++{
++ inflate_huft *t; /* temporary pointer */
++ uInt e; /* extra bits or operation */
++ uLong b; /* bit buffer */
++ uInt k; /* bits in bit buffer */
++ Bytef *p; /* input data pointer */
++ uInt n; /* bytes available there */
++ Bytef *q; /* output window write pointer */
++ uInt m; /* bytes to end of window or read pointer */
++ uInt ml; /* mask for literal/length tree */
++ uInt md; /* mask for distance tree */
++ uInt c; /* bytes to copy */
++ uInt d; /* distance back to copy from */
++ Bytef *r; /* copy source pointer */
++
++ /* load input, output, bit values */
++ LOAD
++
++ /* initialize masks */
++ ml = inflate_mask[bl];
++ md = inflate_mask[bd];
++
++ /* do until not enough input or output space for fast loop */
++ do { /* assume called with m >= 258 && n >= 10 */
++ /* get literal/length code */
++ GRABBITS(20) /* max bits for literal/length code */
++ if ((e = (t = tl + ((uInt)b & ml))->exop) == 0)
++ {
++ DUMPBITS(t->bits)
++ Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ?
++ "inflate: * literal '%c'\n" :
++ "inflate: * literal 0x%02x\n", t->base));
++ *q++ = (Byte)t->base;
++ m--;
++ continue;
++ }
++ do {
++ DUMPBITS(t->bits)
++ if (e & 16)
++ {
++ /* get extra bits for length */
++ e &= 15;
++ c = t->base + ((uInt)b & inflate_mask[e]);
++ DUMPBITS(e)
++ Tracevv((stderr, "inflate: * length %u\n", c));
++
++ /* decode distance base of block to copy */
++ GRABBITS(15); /* max bits for distance code */
++ e = (t = td + ((uInt)b & md))->exop;
++ do {
++ DUMPBITS(t->bits)
++ if (e & 16)
++ {
++ /* get extra bits to add to distance base */
++ e &= 15;
++ GRABBITS(e) /* get extra bits (up to 13) */
++ d = t->base + ((uInt)b & inflate_mask[e]);
++ DUMPBITS(e)
++ Tracevv((stderr, "inflate: * distance %u\n", d));
++
++ /* do the copy */
++ m -= c;
++ if ((uInt)(q - s->window) >= d) /* offset before dest */
++ { /* just copy */
++ r = q - d;
++ *q++ = *r++; c--; /* minimum count is three, */
++ *q++ = *r++; c--; /* so unroll loop a little */
++ }
++ else /* else offset after destination */
++ {
++ e = d - (q - s->window); /* bytes from offset to end */
++ r = s->end - e; /* pointer to offset */
++ if (c > e) /* if source crosses, */
++ {
++ c -= e; /* copy to end of window */
++ do {
++ *q++ = *r++;
++ } while (--e);
++ r = s->window; /* copy rest from start of window */
++ }
++ }
++ do { /* copy all or what's left */
++ *q++ = *r++;
++ } while (--c);
++ break;
++ }
++ else if ((e & 64) == 0)
++ e = (t = t->next + ((uInt)b & inflate_mask[e]))->exop;
++ else
++ {
++ z->msg = "invalid distance code";
++ UNGRAB
++ UPDATE
++ return Z_DATA_ERROR;
++ }
++ } while (1);
++ break;
++ }
++ if ((e & 64) == 0)
++ {
++ if ((e = (t = t->next + ((uInt)b & inflate_mask[e]))->exop) == 0)
++ {
++ DUMPBITS(t->bits)
++ Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ?
++ "inflate: * literal '%c'\n" :
++ "inflate: * literal 0x%02x\n", t->base));
++ *q++ = (Byte)t->base;
++ m--;
++ break;
++ }
++ }
++ else if (e & 32)
++ {
++ Tracevv((stderr, "inflate: * end of block\n"));
++ UNGRAB
++ UPDATE
++ return Z_STREAM_END;
++ }
++ else
++ {
++ z->msg = "invalid literal/length code";
++ UNGRAB
++ UPDATE
++ return Z_DATA_ERROR;
++ }
++ } while (1);
++ } while (m >= 258 && n >= 10);
++
++ /* not enough input or output--restore pointers and return */
++ UNGRAB
++ UPDATE
++ return Z_OK;
++}
++
++
++/*+++++*/
++/* zutil.c -- target dependent utility functions for the compression library
++ * Copyright (C) 1995 Jean-loup Gailly.
++ * For conditions of distribution and use, see copyright notice in zlib.h
++ */
++
++/* From: zutil.c,v 1.8 1995/05/03 17:27:12 jloup Exp */
++
++char *zlib_version = ZLIB_VERSION;
++
++char *z_errmsg[] = {
++"stream end", /* Z_STREAM_END 1 */
++"", /* Z_OK 0 */
++"file error", /* Z_ERRNO (-1) */
++"stream error", /* Z_STREAM_ERROR (-2) */
++"data error", /* Z_DATA_ERROR (-3) */
++"insufficient memory", /* Z_MEM_ERROR (-4) */
++"buffer error", /* Z_BUF_ERROR (-5) */
++""};
++
++
++/*+++++*/
++/* adler32.c -- compute the Adler-32 checksum of a data stream
++ * Copyright (C) 1995 Mark Adler
++ * For conditions of distribution and use, see copyright notice in zlib.h
++ */
++
++/* From: adler32.c,v 1.6 1995/05/03 17:27:08 jloup Exp */
++
++#define BASE 65521L /* largest prime smaller than 65536 */
++#define NMAX 5552
++/* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */
++
++#define DO1(buf) {s1 += *buf++; s2 += s1;}
++#define DO2(buf) DO1(buf); DO1(buf);
++#define DO4(buf) DO2(buf); DO2(buf);
++#define DO8(buf) DO4(buf); DO4(buf);
++#define DO16(buf) DO8(buf); DO8(buf);
++
++/* ========================================================================= */
++uLong adler32(adler, buf, len)
++ uLong adler;
++ Bytef *buf;
++ uInt len;
++{
++ unsigned long s1 = adler & 0xffff;
++ unsigned long s2 = (adler >> 16) & 0xffff;
++ int k;
++
++ if (buf == Z_NULL) return 1L;
++
++ while (len > 0) {
++ k = len < NMAX ? len : NMAX;
++ len -= k;
++ while (k >= 16) {
++ DO16(buf);
++ k -= 16;
++ }
++ if (k != 0) do {
++ DO1(buf);
++ } while (--k);
++ s1 %= BASE;
++ s2 %= BASE;
++ }
++ return (s2 << 16) | s1;
++}
+diff -Naru linux/arch/mips/zboot/Makefile linux-new/arch/mips/zboot/Makefile
+--- linux/arch/mips/zboot/Makefile 1969-12-31 19:00:00.000000000 -0500
++++ linux-new/arch/mips/zboot/Makefile 2003-12-18 14:26:21.000000000 -0500
+@@ -0,0 +1,79 @@
++#
++# arch/mips/zboot/Makefile
++#
++# This file is subject to the terms and conditions of the GNU General Public
++# License. See the file "COPYING" in the main directory of this archive
++# for more details.
++
++# Adapted for MIPS Pete Popov, Dan Malek
++#
++# Copyright (C) 1994 by Linus Torvalds
++# Adapted for PowerPC by Gary Thomas
++# modified by Cort (cort@cs.nmt.edu)
++#
++
++.c.s:
++ $(CC) $(CFLAGS) -S -o $*.s $<
++.s.o:
++ $(AS) -o $*.o $<
++.c.o:
++ $(CC) $(CFLAGS) -c -o $*.o $<
++.S.s:
++ $(CPP) $(AFLAGS) -o $*.o $<
++.S.o:
++ $(CC) $(AFLAGS) -c -o $*.o $<
++
++GZIP_FLAGS = -v9f
++
++CFLAGS := $(CPPFLAGS) -O2 -D__BOOTER__ \
++ -fomit-frame-pointer -fno-strict-aliasing -fno-common \
++ -G 0 -mno-abicalls -fno-pic -mcpu=r4600 -mips2 \
++ -I$(TOPDIR)/arch/$(ARCH)/zboot/include \
++ -I$(TOPDIR)/include/asm
++AFLAGS += -D__BOOTER__
++
++BOOT_TARGETS = zImage zImage.initrd zImage.flash zImage.initrd.flash
++
++lib/zlib.a:
++ $(MAKE) -C lib
++
++images/vmlinux.gz: $(TOPDIR)/vmlinux
++ $(MAKE) -C images vmlinux.gz
++
++$(BOOT_TARGETS): lib/zlib.a images/vmlinux.gz
++ifdef CONFIG_MIPS_PB1000
++ $(MAKE) -C pb1xxx $@
++endif
++ifdef CONFIG_MIPS_PB1500
++ $(MAKE) -C pb1xxx $@
++endif
++ifdef CONFIG_MIPS_PB1100
++ $(MAKE) -C pb1xxx $@
++endif
++ifdef CONFIG_MIPS_DB1000
++ $(MAKE) -C pb1xxx $@
++endif
++ifdef CONFIG_MIPS_DB1100
++ $(MAKE) -C pb1xxx $@
++endif
++ifdef CONFIG_MIPS_DB1500
++ $(MAKE) -C pb1xxx $@
++endif
++ifdef CONFIG_MIPS_BOSPORUS
++ $(MAKE) -C pb1xxx $@
++endif
++ifdef CONFIG_COGENT_CSB250
++ $(MAKE) -C csb250 $@
++endif
++ifdef CONFIG_MIPS_XXS1500
++BOOT_DIR = xxs1500
++endif
++
++# Do the dirs
++clean:
++ $(MAKE) -C common clean
++ $(MAKE) -C images clean
++ $(MAKE) -C pb1xxx clean
++ $(MAKE) -C xxs1500 clean
++
++include $(TOPDIR)/Rules.make
+diff -Naru linux/arch/mips/zboot/pb1xxx/head.S linux-new/arch/mips/zboot/pb1xxx/head.S
+--- linux/arch/mips/zboot/pb1xxx/head.S 1969-12-31 19:00:00.000000000 -0500
++++ linux-new/arch/mips/zboot/pb1xxx/head.S 2003-12-18 14:26:21.000000000 -0500
+@@ -0,0 +1,149 @@
++/*
++ * arch/mips/kernel/head.S
++ *
++ * This file is subject to the terms and conditions of the GNU General Public
++ * License. See the file "COPYING" in the main directory of this archive
++ * for more details.
++ *
++ * Copyright (C) 1994, 1995 Waldorf Electronics
++ * Written by Ralf Baechle and Andreas Busse
++ * Copyright (C) 1995 - 1999 Ralf Baechle
++ * Copyright (C) 1996 Paul M. Antoine
++ * Modified for DECStation and hence R3000 support by Paul M. Antoine
++ * Further modifications by David S. Miller and Harald Koerfgen
++ * Copyright (C) 1999 Silicon Graphics, Inc.
++ *
++ * Head.S contains the MIPS exception handler and startup code.
++ *
++ **************************************************************************
++ * 9 Nov, 2000.
++ * Added Cache Error exception handler and SBDDP EJTAG debug exception.
++ *
++ * Kevin Kissell, kevink@mips.com and Carsten Langgaard, carstenl@mips.com
++ * Copyright (C) 2000 MIPS Technologies, Inc. All rights reserved.
++ **************************************************************************
++ */
++#include <linux/config.h>
++#include <linux/threads.h>
++
++#include <asm/asm.h>
++#include <asm/cacheops.h>
++#include <asm/mipsregs.h>
++#include <asm/offset.h>
++#include <asm/cachectl.h>
++#include <asm/regdef.h>
++
++#define IndexInvalidate_I 0x00
++#define IndexWriteBack_D 0x01
++
++ .set noreorder
++ .cprestore
++ LEAF(start)
++start:
++ bal locate
++ nop
++locate:
++ subu s8, ra, 8 /* Where we were loaded */
++ la sp, (.stack + 8192)
++
++ move s0, a0 /* Save boot rom start args */
++ move s1, a1
++ move s2, a2
++ move s3, a3
++
++ la a0, start /* Where we were linked to run */
++
++ move a1, s8
++ la a2, _edata
++ subu t1, a2, a0
++ srl t1, t1, 2
++
++ /* copy text section */
++ li t0, 0
++1: lw v0, 0(a1)
++ nop
++ sw v0, 0(a0)
++ xor t0, t0, v0
++ addu a0, 4
++ bne a2, a0, 1b
++ addu a1, 4
++
++ /* Clear BSS */
++ la a0, _edata
++ la a2, _end
++2: sw zero, 0(a0)
++ bne a2, a0, 2b
++ addu a0, 4
++
++ /* push the D-Cache and invalidate I-Cache */
++ li k0, 0x80000000 # start address
++ li k1, 0x80004000 # end address (16KB I-Cache)
++ subu k1, 128
++
++1:
++ .set mips3
++ cache IndexWriteBack_D, 0(k0)
++ cache IndexWriteBack_D, 32(k0)
++ cache IndexWriteBack_D, 64(k0)
++ cache IndexWriteBack_D, 96(k0)
++ cache IndexInvalidate_I, 0(k0)
++ cache IndexInvalidate_I, 32(k0)
++ cache IndexInvalidate_I, 64(k0)
++ cache IndexInvalidate_I, 96(k0)
++ .set mips0
++
++ bne k0, k1, 1b
++ addu k0, k0, 128
++ /* done */
++
++ move a0, s8 /* load address */
++ move a1, t1 /* length in words */
++ move a2, t0 /* checksum */
++ move a3, sp
++
++ la ra, 1f
++ la k0, decompress_kernel
++ jr k0
++ nop
++1:
++
++ move a0, s0
++ move a1, s1
++ move a2, s2
++ move a3, s3
++ li k0, KERNEL_ENTRY
++ jr k0
++ nop
++3:
++ b 3b
++ END(start)
++
++ LEAF(udelay)
++udelay:
++ END(udelay)
++
++
++ LEAF(FlushCache)
++ li k0, 0x80000000 # start address
++ li k1, 0x80004000 # end address (16KB I-Cache)
++ subu k1, 128
++
++1:
++ .set mips3
++ cache IndexWriteBack_D, 0(k0)
++ cache IndexWriteBack_D, 32(k0)
++ cache IndexWriteBack_D, 64(k0)
++ cache IndexWriteBack_D, 96(k0)
++ cache IndexInvalidate_I, 0(k0)
++ cache IndexInvalidate_I, 32(k0)
++ cache IndexInvalidate_I, 64(k0)
++ cache IndexInvalidate_I, 96(k0)
++ .set mips0
++
++ bne k0, k1, 1b
++ addu k0, k0, 128
++ jr ra
++ nop
++ END(FlushCache)
++
++ .comm .stack,4096*2,4
+diff -Naru linux/arch/mips/zboot/pb1xxx/Makefile linux-new/arch/mips/zboot/pb1xxx/Makefile
+--- linux/arch/mips/zboot/pb1xxx/Makefile 1969-12-31 19:00:00.000000000 -0500
++++ linux-new/arch/mips/zboot/pb1xxx/Makefile 2003-12-18 14:26:21.000000000 -0500
+@@ -0,0 +1,123 @@
++# arch/mips/zboot/pb1xxx/Makefile
++#
++# Makefile for Alchemy Semiconductor Pb1[015]00 boards.
++# All of the boot loader code was derived from the ppc
++# boot code.
++#
++# Copyright 2001,2002 MontaVista Software Inc.
++#
++# Author: Mark A. Greer
++# mgreer@mvista.com
++# Ported and modified for mips support by
++# Pete Popov <ppopov@mvista.com>
++#
++# This program is free software; you can redistribute it and/or modify it
++# under the terms of the GNU General Public License as published by the
++# Free Software Foundation; either version 2 of the License, or (at your
++# option) any later version.
++
++.c.s:
++ $(CC) $(CFLAGS) -S -o $*.s $<
++.s.o:
++ $(AS) -o $*.o $<
++.c.o:
++ $(CC) $(CFLAGS) -D__BOOTER__ -c -o $*.o $<
++.S.s:
++ $(CPP) $(AFLAGS) -o $*.o $<
++.S.o:
++ $(CC) $(AFLAGS) -c -o $*.o $<
++
++#########################################################################
++# START BOARD SPECIFIC VARIABLES
++ifdef CONFIG_MIPS_PB1000
++BNAME=pb1000
++endif
++
++ifdef CONFIG_MIPS_PB1100
++BNAME=pb1100
++endif
++
++ifdef CONFIG_MIPS_PB1500
++BNAME=pb1500
++endif
++
++ifdef CONFIG_MIPS_DB1000
++BNAME=db1000
++endif
++
++ifdef CONFIG_MIPS_DB1100
++BNAME=db1100
++endif
++
++ifdef CONFIG_MIPS_DB1500
++BNAME=db1500
++endif
++
++ifdef CONFIG_MIPS_BOSPORUS
++BNAME=bosporus
++endif
++
++# These two variables control where the zImage is stored
++# in flash and loaded in memory. It only controls how the srec
++# file is generated, the code is the same.
++RAM_RUN_ADDR = 0x81000000
++FLASH_LOAD_ADDR = 0xBFD00000
++
++# These two variables specify the free ram region
++# that can be used for temporary malloc area
++AVAIL_RAM_START=0x80400000
++AVAIL_RAM_END=0x80800000
++
++# This one must match the LOADADDR in arch/mips/Makefile!
++LOADADDR=0x80100000
++# END BOARD SPECIFIC VARIABLES
++#########################################################################
++
++OBJECTS := head.o ../common/misc-common.o ../common/misc-simple.o \
++ ../common/au1k_uart.o ../common/string.o ../common/ctype.o
++LIBS := ../lib/zlib.a
++
++ENTRY := ../utils/entry
++OFFSET := ../utils/offset
++SIZE := ../utils/size
++
++LD_ARGS := -T ../ld.script -Ttext $(RAM_RUN_ADDR) -Bstatic
++OBJCOPY_ARGS = -O elf32-tradlittlemips
++
++all: zImage
++
++clean:
++ rm -rf *.o vmlinux* zvmlinux.* ../images/*.srec
++
++head.o: head.S $(TOPDIR)/vmlinux
++ $(CC) $(AFLAGS) \
++ -DKERNEL_ENTRY=$(shell sh $(ENTRY) $(NM) $(TOPDIR)/vmlinux ) \
++ -c -o $*.o $<
++
++../common/misc-simple.o:
++ $(CC) $(CFLAGS) -DINITRD_OFFSET=0 -DINITRD_SIZE=0 -DZIMAGE_OFFSET=0 \
++ -DAVAIL_RAM_START=$(AVAIL_RAM_START) \
++ -DAVAIL_RAM_END=$(AVAIL_RAM_END) \
++ -DLOADADDR=$(LOADADDR) \
++ -DZIMAGE_SIZE=0 -c -o $@ $*.c
++
++zvmlinux: $(OBJECTS) $(LIBS) ../ld.script ../images/vmlinux.gz ../common/dummy.o
++ $(OBJCOPY) \
++ --add-section=.image=../images/vmlinux.gz \
++ --set-section-flags=.image=contents,alloc,load,readonly,data \
++ ../common/dummy.o image.o
++ $(LD) $(LD_ARGS) -o $@ $(OBJECTS) image.o $(LIBS)
++ $(OBJCOPY) $(OBJCOPY_ARGS) $@ $@ -R .comment -R .stab -R .stabstr \
++ -R .initrd -R .sysmap
++
++# Here we manipulate the image in order to get it the necessary
++# srecord file we need.
++zImage: zvmlinux
++ mv zvmlinux ../images/zImage.$(BNAME)
++ $(OBJCOPY) -O srec ../images/zImage.$(BNAME) ../images/$(BNAME).srec
++
++zImage.flash: zImage
++ $(OBJCOPY) -O srec --adjust-vma 0x3ed00000 \
++ ../images/zImage.$(BNAME) ../images/$(BNAME).flash.srec
++
++include $(TOPDIR)/Rules.make
+diff -Naru linux/arch/mips/zboot/utils/entry linux-new/arch/mips/zboot/utils/entry
+--- linux/arch/mips/zboot/utils/entry 1969-12-31 19:00:00.000000000 -0500
++++ linux-new/arch/mips/zboot/utils/entry 2003-12-18 14:26:21.000000000 -0500
+@@ -0,0 +1,12 @@
++#!/bin/sh
++
++# grab the kernel_entry address from the vmlinux elf image
++entry=`$1 $2 | grep kernel_entry`
++
++fs=`echo $entry | grep ffffffff` # check toolchain output
++
++if [ -n "$fs" ]; then
++ echo "0x"`$1 $2 | grep kernel_entry | cut -c9- | awk '{print $1}'`
++else
++ echo "0x"`$1 $2 | grep kernel_entry | cut -c1- | awk '{print $1}'`
++fi
+diff -Naru linux/arch/mips/zboot/utils/offset linux-new/arch/mips/zboot/utils/offset
+--- linux/arch/mips/zboot/utils/offset 1969-12-31 19:00:00.000000000 -0500
++++ linux-new/arch/mips/zboot/utils/offset 2003-12-18 14:26:21.000000000 -0500
+@@ -0,0 +1,3 @@
++#!/bin/sh
++
++echo "0x"`$1 -h $2 | grep $3 | grep -v zvmlinux| awk '{print $6}'`
+diff -Naru linux/arch/mips/zboot/utils/size linux-new/arch/mips/zboot/utils/size
+--- linux/arch/mips/zboot/utils/size 1969-12-31 19:00:00.000000000 -0500
++++ linux-new/arch/mips/zboot/utils/size 2003-12-18 14:26:21.000000000 -0500
+@@ -0,0 +1,4 @@
++#!/bin/sh
++
++OFFSET=`$1 -h $2 | grep $3 | grep -v zvmlinux | awk '{print $3}'`
++echo "0x"$OFFSET
+diff -Naru linux/arch/mips/zboot/xxs1500/head.S linux-new/arch/mips/zboot/xxs1500/head.S
+--- linux/arch/mips/zboot/xxs1500/head.S 1969-12-31 19:00:00.000000000 -0500
++++ linux-new/arch/mips/zboot/xxs1500/head.S 2003-12-18 14:26:21.000000000 -0500
+@@ -0,0 +1,137 @@
++/*
++ * arch/mips/kernel/head.S
++ *
++ * This file is subject to the terms and conditions of the GNU General Public
++ * License. See the file "COPYING" in the main directory of this archive
++ * for more details.
++ *
++ * Copyright (C) 1994, 1995 Waldorf Electronics
++ * Written by Ralf Baechle and Andreas Busse
++ * Copyright (C) 1995 - 1999 Ralf Baechle
++ * Copyright (C) 1996 Paul M. Antoine
++ * Modified for DECStation and hence R3000 support by Paul M. Antoine
++ * Further modifications by David S. Miller and Harald Koerfgen
++ * Copyright (C) 1999 Silicon Graphics, Inc.
++ *
++ * Head.S contains the MIPS exception handler and startup code.
++ *
++ **************************************************************************
++ * 9 Nov, 2000.
++ * Added Cache Error exception handler and SBDDP EJTAG debug exception.
++ *
++ * Kevin Kissell, kevink@mips.com and Carsten Langgaard, carstenl@mips.com
++ * Copyright (C) 2000 MIPS Technologies, Inc. All rights reserved.
++ **************************************************************************
++ */
++#include <linux/config.h>
++#include <linux/threads.h>
++
++#include <asm/asm.h>
++#include <asm/cacheops.h>
++#include <asm/mipsregs.h>
++#include <asm/offset.h>
++#include <asm/cachectl.h>
++#include <asm/regdef.h>
++
++#define IndexInvalidate_I 0x00
++
++ .set noreorder
++ .cprestore
++ LEAF(start)
++start:
++
++locate:
++ la sp, .stack
++ move s0, a0
++ move s1, a1
++ move s2, a2
++ move s3, a3
++
++ la a0, start
++
++ li a1, FLASH_LOAD_ADDR
++ la a2, _edata
++ subu t1, a2, a0
++ srl t1, t1, 2
++
++ /* copy text section */
++ li t0, 0
++1: lw v0, 0(a1)
++ nop
++ sw v0, 0(a0)
++ xor t0, t0, v0
++ addu a0, 4
++ bne a2, a0, 1b
++ addu a1, 4
++
++ /* Clear BSS */
++ la a0, _edata
++ la a2, _end
++2: sw zero, 0(a0)
++ bne a2, a0, 2b
++ addu a0, 4
++
++ /* flush the I-Cache */
++ li k0, 0x80000000 # start address
++ li k1, 0x80004000 # end address (16KB I-Cache)
++ subu k1, 128
++
++1:
++ .set mips3
++ cache IndexInvalidate_I, 0(k0)
++ cache IndexInvalidate_I, 32(k0)
++ cache IndexInvalidate_I, 64(k0)
++ cache IndexInvalidate_I, 96(k0)
++ .set mips0
++
++ bne k0, k1, 1b
++ addu k0, k0, 128
++ /* done */
++
++ li a0, FLASH_LOAD_ADDR /* load address */
++ move a1, t1 /* length in words */
++ move a2, t0 /* checksum */
++ move a3, sp
++
++ la ra, 1f
++ la k0, decompress_kernel
++ jr k0
++ nop
++1:
++
++ move a0, s0
++ move a1, s1
++ move a2, s2
++ move a3, s3
++ li k0, KERNEL_ENTRY
++ jr k0
++ nop
++3:
++ b 3b
++ END(start)
++
++ LEAF(udelay)
++udelay:
++ END(udelay)
++
++
++ LEAF(FlushCache)
++ li k0, 0x80000000 # start address
++ li k1, 0x80004000 # end address (16KB I-Cache)
++ subu k1, 128
++
++1:
++ .set mips3
++ cache IndexInvalidate_I, 0(k0)
++ cache IndexInvalidate_I, 32(k0)
++ cache IndexInvalidate_I, 64(k0)
++ cache IndexInvalidate_I, 96(k0)
++ .set mips0
++
++ bne k0, k1, 1b
++ addu k0, k0, 128
++ jr ra
++ nop
++ END(FlushCache)
++
++ .comm .stack,4096*2,4
+diff -Naru linux/arch/mips/zboot/xxs1500/ld.script linux-new/arch/mips/zboot/xxs1500/ld.script
+--- linux/arch/mips/zboot/xxs1500/ld.script 1969-12-31 19:00:00.000000000 -0500
++++ linux-new/arch/mips/zboot/xxs1500/ld.script 2003-12-18 14:26:21.000000000 -0500
+@@ -0,0 +1,147 @@
++OUTPUT_ARCH(mips)
++ENTRY(start)
++SECTIONS
++{
++ /* Read-only sections, merged into text segment: */
++ /* . = 0x81000000; */
++ .init : { *(.init) } =0
++ .text :
++ {
++ _ftext = . ;
++ *(.text)
++ *(.rodata)
++ *(.rodata1)
++ /* .gnu.warning sections are handled specially by elf32.em. */
++ *(.gnu.warning)
++ } =0
++ .kstrtab : { *(.kstrtab) }
++
++ . = ALIGN(16); /* Exception table */
++ __start___ex_table = .;
++ __ex_table : { *(__ex_table) }
++ __stop___ex_table = .;
++
++ __start___dbe_table = .; /* Exception table for data bus errors */
++ __dbe_table : { *(__dbe_table) }
++ __stop___dbe_table = .;
++
++ __start___ksymtab = .; /* Kernel symbol table */
++ __ksymtab : { *(__ksymtab) }
++ __stop___ksymtab = .;
++
++ _etext = .;
++
++ . = ALIGN(8192);
++ .data.init_task : { *(.data.init_task) }
++
++ /* Startup code */
++ . = ALIGN(4096);
++ __init_begin = .;
++ .text.init : { *(.text.init) }
++ .data.init : { *(.data.init) }
++ . = ALIGN(16);
++ __setup_start = .;
++ .setup.init : { *(.setup.init) }
++ __setup_end = .;
++ __initcall_start = .;
++ .initcall.init : { *(.initcall.init) }
++ __initcall_end = .;
++ . = ALIGN(4096); /* Align double page for init_task_union */
++ __init_end = .;
++
++ . = ALIGN(4096);
++ .data.page_aligned : { *(.data.idt) }
++
++ . = ALIGN(32);
++ .data.cacheline_aligned : { *(.data.cacheline_aligned) }
++
++ .fini : { *(.fini) } =0
++ .reginfo : { *(.reginfo) }
++ /* Adjust the address for the data segment. We want to adjust up to
++ the same address within the page on the next page up. It would
++ be more correct to do this:
++ . = .;
++ The current expression does not correctly handle the case of a
++ text segment ending precisely at the end of a page; it causes the
++ data segment to skip a page. The above expression does not have
++ this problem, but it will currently (2/95) cause BFD to allocate
++ a single segment, combining both text and data, for this case.
++ This will prevent the text segment from being shared among
++ multiple executions of the program; I think that is more
++ important than losing a page of the virtual address space (note
++ that no actual memory is lost; the page which is skipped can not
++ be referenced). */
++ . = .;
++ .data :
++ {
++ _fdata = . ;
++ *(.data)
++
++ /* Align the initial ramdisk image (INITRD) on page boundaries. */
++ . = ALIGN(4096);
++ __rd_start = .;
++ *(.initrd)
++ __rd_end = .;
++ . = ALIGN(4096);
++
++ CONSTRUCTORS
++ }
++ .data1 : { *(.data1) }
++ _gp = . + 0x8000;
++ .lit8 : { *(.lit8) }
++ .lit4 : { *(.lit4) }
++ .ctors : { *(.ctors) }
++ .dtors : { *(.dtors) }
++ .got : { *(.got.plt) *(.got) }
++ .dynamic : { *(.dynamic) }
++ /* We want the small data sections together, so single-instruction offsets
++ can access them all, and initialized data all before uninitialized, so
++ we can shorten the on-disk segment size. */
++ .sdata : { *(.sdata) }
++ . = ALIGN(4);
++ _edata = .;
++ PROVIDE (edata = .);
++
++ __bss_start = .;
++ _fbss = .;
++ .sbss : { *(.sbss) *(.scommon) }
++ .bss :
++ {
++ *(.dynbss)
++ *(.bss)
++ *(COMMON)
++ . = ALIGN(4);
++ _end = . ;
++ PROVIDE (end = .);
++ }
++
++ /* Sections to be discarded */
++ /DISCARD/ :
++ {
++ *(.text.exit)
++ *(.data.exit)
++ *(.exitcall.exit)
++ }
++
++ /* This is the MIPS specific mdebug section. */
++ .mdebug : { *(.mdebug) }
++ /* These are needed for ELF backends which have not yet been
++ converted to the new style linker. */
++ .stab 0 : { *(.stab) }
++ .stabstr 0 : { *(.stabstr) }
++ /* DWARF debug sections.
++ Symbols in the .debug DWARF section are relative to the beginning of the
++ section so we begin .debug at 0. It's not clear yet what needs to happen
++ for the others. */
++ .debug 0 : { *(.debug) }
++ .debug_srcinfo 0 : { *(.debug_srcinfo) }
++ .debug_aranges 0 : { *(.debug_aranges) }
++ .debug_pubnames 0 : { *(.debug_pubnames) }
++ .debug_sfnames 0 : { *(.debug_sfnames) }
++ .line 0 : { *(.line) }
++ /* These must appear regardless of . */
++ .gptab.sdata : { *(.gptab.data) *(.gptab.sdata) }
++ .gptab.sbss : { *(.gptab.bss) *(.gptab.sbss) }
++ .comment : { *(.comment) }
++ .note : { *(.note) }
++}
+diff -Naru linux/arch/mips/zboot/xxs1500/Makefile linux-new/arch/mips/zboot/xxs1500/Makefile
+--- linux/arch/mips/zboot/xxs1500/Makefile 1969-12-31 19:00:00.000000000 -0500
++++ linux-new/arch/mips/zboot/xxs1500/Makefile 2003-12-18 14:26:21.000000000 -0500
+@@ -0,0 +1,123 @@
++# arch/mips/compressed/alchemy/Makefile
++#
++# Makefile for Alchemy Semiconductor Pb1[015]00 boards.
++# All of the boot loader code was derived from the ppc
++# boot code.
++#
++# Copyright 2001,2002 MontaVista Software Inc.
++#
++# Author: Mark A. Greer
++# mgreer@mvista.com
++# Ported and modified for mips support by
++# Pete Popov <ppopov@mvista.com>
++#
++# This program is free software; you can redistribute it and/or modify it
++# under the terms of the GNU General Public License as published by the
++# Free Software Foundation; either version 2 of the License, or (at your
++# option) any later version.
++
++.c.s:
++ $(CC) $(CFLAGS) -S -o $*.s $<
++.s.o:
++ $(AS) -o $*.o $<
++.c.o:
++ $(CC) $(CFLAGS) -D__BOOTER__ -c -o $*.o $<
++.S.s:
++ $(CPP) $(AFLAGS) -o $*.o $<
++.S.o:
++ $(CC) $(AFLAGS) -c -o $*.o $<
++
++#########################################################################
++# START BOARD SPECIFIC VARIABLES
++BNAME=xxs1500
++
++
++# These two variables control where the zImage is stored
++# in flash and loaded in memory. If you change either one,
++# be sure to make the appropriate change to the zImage
++# rule.
++RAM_LOAD_ADDR = 0x81000000
++FLASH_LOAD_ADDR = 0xBF000000
++
++# These two variables specify the free ram region
++# that can be used for temporary malloc area
++AVAIL_RAM_START=0x80400000
++AVAIL_RAM_END=0x80800000
++
++# This one must match the LOADADDR in arch/mips/Makefile!
++LOADADDR=0x80100000
++# END BOARD SPECIFIC VARIABLES
++#########################################################################
++
++ZLINKFLAGS = -T ld.script -Ttext $(RAM_LOAD_ADDR)
++
++OBJECTS := head.o ../common/misc-common.o ../common/misc-simple.o \
++ ../common/au1k_uart.o ../common/string.o ../common/ctype.o
++LIBS := ../lib/zlib.a
++
++ENTRY := ../utils/entry
++OFFSET := ../utils/offset
++SIZE := ../utils/size
++
++all: zImage
++
++clean:
++ rm -rf *.o vmlinux* zvmlinux.*
++
++head.o: head.S $(TOPDIR)/vmlinux
++ $(CC) -DFLASH_LOAD_ADDR=$(FLASH_LOAD_ADDR) $(AFLAGS) \
++ -DKERNEL_ENTRY=$(shell sh $(ENTRY) $(NM) $(TOPDIR)/vmlinux ) \
++ -c -o $*.o $<
++
++../common/misc-simple.o:
++ $(CC) $(CFLAGS) -DINITRD_OFFSET=0 -DINITRD_SIZE=0 -DZIMAGE_OFFSET=0 \
++ -DAVAIL_RAM_START=$(AVAIL_RAM_START) \
++ -DAVAIL_RAM_END=$(AVAIL_RAM_END) \
++ -DLOADADDR=$(LOADADDR) \
++ -DZIMAGE_SIZE=0 -c -o $@ $*.c
++
++# This is the first pass at building the boot loader image,
++# without knowing the file offset where the vmlinuz.gz
++# kernel will end up. We build this image, check the offset,
++# and then rebuild it with the correct offset and size
++# passed to mips-simple.c
++zvmlinux.no: $(OBJECTS) $(LIBS) ../images/vmlinux.gz
++ $(LD) $(ZLINKFLAGS) -o $@.tmp $(OBJECTS) $(LIBS)
++ $(OBJCOPY) -R .comment \
++ --add-section=image=../images/vmlinux.gz \
++ $@.tmp $@
++ # rm -f $@.tmp
++
++
++# This is the final image we build, now that we know what
++# the vmlinuz.gz offset is.
++zvmlinux: $(OBJECTS) $(LIBS) ../images/vmlinux.gz zvmlinux.no
++ $(CC) $(CFLAGS) -DINITRD_OFFSET=0 -DINITRD_SIZE=0 \
++ -DZIMAGE_OFFSET=$(shell sh $(OFFSET) $(OBJDUMP) $@.no image) \
++ -DZIMAGE_SIZE=$(shell sh $(SIZE) $(OBJDUMP) $@.no image) \
++ -D__BOOTER__ \
++ -DAVAIL_RAM_START=$(AVAIL_RAM_START) \
++ -DAVAIL_RAM_END=$(AVAIL_RAM_END) \
++ -DLOADADDR=$(LOADADDR) \
++ -c -o ../common/misc-simple.o ../common/misc-simple.c
++ $(LD) $(ZLINKFLAGS) -o $@.tmp $(OBJECTS) $(LIBS)
++ $(OBJCOPY) -R .comment \
++ --add-section=image=../images/vmlinux.gz \
++ $@.tmp $@
++ $(OBJCOPY) --adjust-section-vma=image+$(RAM_LOAD_ADDR) $@
++ $(OBJCOPY) --adjust-section-vma=image+$(shell sh $(OFFSET) \
++ $(OBJDUMP) $@.no image ) $@
++ # rm -f $@.tmp
++ # rm -f $@.no
++
++
++# Here we manipulate the image in order to get it the necessary
++# srecord file we need.
++zImage: zvmlinux
++ mv zvmlinux ../images/$@.$(BNAME)
++ $(OBJCOPY) --set-section-flags=image=alloc,load,code ../images/$@.$(BNAME)
++ $(OBJCOPY) -O srec --adjust-vma 0x3e000000 \
++ ../images/$@.$(BNAME) ../images/$@.$(BNAME).srec
++ # rm ../images/vmlinux.gz
++
++include $(TOPDIR)/Rules.make
diff --git a/linux/linux-mtx-1-2.4.24/05-zboot-cflags.diff b/linux/linux-mtx-1-2.4.24/05-zboot-cflags.diff
index e69de29bb2..9ac0c31b45 100644
--- a/linux/linux-mtx-1-2.4.24/05-zboot-cflags.diff
+++ b/linux/linux-mtx-1-2.4.24/05-zboot-cflags.diff
@@ -0,0 +1,15 @@
+--- linux-mips-2.4.24-pre2+zboot/arch/mips/zboot/Makefile 2004-11-17 19:25:04.538345048 +0100
++++ linux/arch/mips/zboot/Makefile 2004-11-17 19:21:05.304714088 +0100
+@@ -25,11 +25,7 @@
+
+ GZIP_FLAGS = -v9f
+
+-CFLAGS := $(CPPFLAGS) -O2 -D__BOOTER__ \
+- -fomit-frame-pointer -fno-strict-aliasing -fno-common \
+- -G 0 -mno-abicalls -fno-pic -mcpu=r4600 -mips2 \
+- -I$(TOPDIR)/arch/$(ARCH)/zboot/include \
+- -I$(TOPDIR)/include/asm
++CFLAGS += -D__BOOTER__ -I$(TOPDIR)/arch/$(ARCH)/zboot/include
+ AFLAGS += -D__BOOTER__
+
+ BOOT_TARGETS = zImage zImage.initrd zImage.flash zImage.initrd.flash
diff --git a/linux/linux-mtx-1-2.4.24/06-zboot-mtx.diff b/linux/linux-mtx-1-2.4.24/06-zboot-mtx.diff
index e69de29bb2..82a3295564 100644
--- a/linux/linux-mtx-1-2.4.24/06-zboot-mtx.diff
+++ b/linux/linux-mtx-1-2.4.24/06-zboot-mtx.diff
@@ -0,0 +1,27 @@
+diff -urb work/arch/mips/zboot/Makefile mtx/arch/mips/zboot/Makefile
+--- work/arch/mips/zboot/Makefile 2003-07-01 20:09:20.000000000 +0200
++++ mtx/arch/mips/zboot/Makefile 2003-07-01 19:20:29.000000000 +0200
+@@ -64,6 +64,9 @@
+ ifdef CONFIG_MIPS_XXS1500
+ BOOT_DIR = xxs1500
+ endif
++ifdef CONFIG_MIPS_MTX1
++ $(MAKE) -C pb1xxx $@
++endif
+
+ # Do the dirs
+ clean:
+diff -urb work/arch/mips/zboot/pb1xxx/Makefile mtx/arch/mips/zboot/pb1xxx/Makefile
+--- work/arch/mips/zboot/pb1xxx/Makefile 2003-07-01 20:09:20.000000000 +0200
++++ mtx/arch/mips/zboot/pb1xxx/Makefile 2003-07-01 19:20:37.000000000 +0200
+@@ -57,6 +57,10 @@
+ BNAME=bosporus
+ endif
+
++ifdef CONFIG_MIPS_MTX1
++BNAME=mtx-1
++endif
++
+ # These two variables control where the zImage is stored
+ # in flash and loaded in memory. It only controls how the srec
+ # file is generated, the code is the same.
diff --git a/linux/linux-mtx-1-2.4.24/07-zimage-flash-bin.patch b/linux/linux-mtx-1-2.4.24/07-zimage-flash-bin.patch
index e69de29bb2..ce13d9add7 100644
--- a/linux/linux-mtx-1-2.4.24/07-zimage-flash-bin.patch
+++ b/linux/linux-mtx-1-2.4.24/07-zimage-flash-bin.patch
@@ -0,0 +1,11 @@
+diff -Nurb oe/tmp/work/linux-mtx-1-2.4.27-r0/linux/arch/mips/zboot/pb1xxx/Makefile linux.m/arch/mips/zboot/pb1xxx/Makefile
+--- oe/tmp/work/linux-mtx-1-2.4.27-r0/linux/arch/mips/zboot/pb1xxx/Makefile 2004-10-13 21:08:49.840408328 +0200
++++ linux.m/arch/mips/zboot/pb1xxx/Makefile 2004-10-13 21:08:29.736464592 +0200
+@@ -123,5 +123,7 @@
+ zImage.flash: zImage
+ $(OBJCOPY) -O srec --adjust-vma 0x3ed00000 \
+ ../images/zImage.$(BNAME) ../images/$(BNAME).flash.srec
++ $(OBJCOPY) -O binary --adjust-vma 0x3ed00000 \
++ ../images/zImage.$(BNAME) ../images/$(BNAME).flash.bin
+
+ include $(TOPDIR)/Rules.make
diff --git a/linux/linux-mtx-1-2.4.24/08-usb-nonpci-2.4.24.patch b/linux/linux-mtx-1-2.4.24/08-usb-nonpci-2.4.24.patch
index e69de29bb2..89ed24f214 100644
--- a/linux/linux-mtx-1-2.4.24/08-usb-nonpci-2.4.24.patch
+++ b/linux/linux-mtx-1-2.4.24/08-usb-nonpci-2.4.24.patch
@@ -0,0 +1,173 @@
+diff -Naru linux/drivers/usb/host/Config.in linux-new/drivers/usb/host/Config.in
+--- linux/drivers/usb/host/Config.in 2003-11-16 20:07:42.000000000 -0500
++++ linux-new/drivers/usb/host/Config.in 2003-12-18 14:19:37.000000000 -0500
+@@ -17,3 +17,4 @@
+ dep_tristate ' SL811HS Alternate (x86, StrongARM, isosynchronous mode)' CONFIG_USB_SL811HS_ALT $CONFIG_USB $CONFIG_EXPERIMENTAL
+ dep_tristate ' SL811HS (x86, StrongARM) support, old driver' CONFIG_USB_SL811HS $CONFIG_USB $CONFIG_EXPERIMENTAL
+ fi
++dep_tristate ' Non-PCI OHCI support' CONFIG_USB_NON_PCI_OHCI $CONFIG_USB_OHCI
+diff -Naru linux/drivers/usb/host/usb-ohci.c linux-new/drivers/usb/host/usb-ohci.c
+--- linux/drivers/usb/host/usb-ohci.c 2003-08-13 13:19:23.000000000 -0400
++++ linux-new/drivers/usb/host/usb-ohci.c 2003-12-18 14:19:53.000000000 -0500
+@@ -2517,6 +2517,7 @@
+ hc_release_ohci (ohci);
+ return ret;
+ }
++#ifndef CONFIG_USB_NON_PCI_OHCI
+ ohci->flags = id->driver_data;
+
+ /* Check for NSC87560. We have to look at the bridge (fn1) to identify
+@@ -2535,6 +2536,7 @@
+ printk (KERN_INFO __FILE__ ": Using NSC SuperIO setup\n");
+ if (ohci->flags & OHCI_QUIRK_AMD756)
+ printk (KERN_INFO __FILE__ ": AMD756 erratum 4 workaround\n");
++#endif
+
+ if (hc_reset (ohci) < 0) {
+ hc_release_ohci (ohci);
+@@ -2580,8 +2582,10 @@
+ int temp;
+ int i;
+
++#ifndef CONFIG_USB_NON_PCI_OHCI
+ if (ohci->pci_latency)
+ pci_write_config_byte (ohci->ohci_dev, PCI_LATENCY_TIMER, ohci->pci_latency);
++#endif
+
+ ohci->disabled = 1;
+ ohci->sleeping = 0;
+@@ -2611,6 +2615,7 @@
+
+ /*-------------------------------------------------------------------------*/
+
++#ifndef CONFIG_USB_NON_PCI_OHCI
+ /* configured so that an OHCI device is always provided */
+ /* always called with process context; sleeping is OK */
+
+@@ -2658,6 +2663,88 @@
+ }
+ return status;
+ }
++#else /* CONFIG_USB_NON_PCI_OHCI */
++
++// Boot options
++static int ohci_base=0, ohci_len=0;
++static int ohci_irq=-1;
++
++MODULE_PARM(ohci_base, "i");
++MODULE_PARM(ohci_len, "i");
++MODULE_PARM(ohci_irq, "i");
++MODULE_PARM_DESC(ohci_base, "IO Base address of OHCI Oper. registers");
++MODULE_PARM_DESC(ohci_len, "IO length of OHCI Oper. registers");
++MODULE_PARM_DESC(ohci_irq, "IRQ for OHCI interrupts");
++
++// bogus pci_dev
++static struct pci_dev bogus_pcidev;
++
++static struct pci_driver ohci_pci_driver = {
++ name: "usb-ohci",
++};
++
++static int __devinit
++ohci_non_pci_init (void)
++{
++ void *mem_base;
++
++ if (!ohci_base || !ohci_len || (ohci_irq < 0))
++ return -ENODEV;
++
++ if (!request_mem_region (ohci_base, ohci_len, ohci_pci_driver.name)) {
++ dbg ("controller already in use");
++ return -EBUSY;
++ }
++
++ mem_base = ioremap_nocache (ohci_base, ohci_len);
++ if (!mem_base) {
++ err("Error mapping OHCI memory");
++ return -EFAULT;
++ }
++
++ /*
++ * Fill in the bogus pci_dev. Only those members actually
++ * dereferenced in this driver are initialized.
++ */
++ memset(&bogus_pcidev, 0, sizeof(struct pci_dev));
++ strcpy(bogus_pcidev.name, "non-PCI OHCI");
++ strcpy(bogus_pcidev.slot_name, "builtin");
++ bogus_pcidev.resource[0].name = "OHCI Operational Registers";
++ bogus_pcidev.resource[0].start = ohci_base;
++ bogus_pcidev.resource[0].end = ohci_base + ohci_len;
++ bogus_pcidev.resource[0].flags = 0;
++ bogus_pcidev.irq = ohci_irq;
++
++ return hc_found_ohci (&bogus_pcidev, bogus_pcidev.irq, mem_base, NULL);
++}
++
++#ifndef MODULE
++
++static int __init
++ohci_setup (char* options)
++{
++ char* this_opt;
++
++ if (!options || !*options)
++ return 0;
++
++ for(this_opt=strtok(options,",");this_opt;this_opt=strtok(NULL,",")) {
++ if (!strncmp(this_opt, "base:", 5)) {
++ ohci_base = simple_strtoul(this_opt+5, NULL, 0);
++ } else if (!strncmp(this_opt, "len:", 4)) {
++ ohci_len = simple_strtoul(this_opt+4, NULL, 0);
++ } else if (!strncmp(this_opt, "irq:", 4)) {
++ ohci_irq = simple_strtoul(this_opt+4, NULL, 0);
++ }
++ }
++ return 0;
++}
++
++__setup("usb_ohci=", ohci_setup);
++
++#endif /* !MODULE */
++
++#endif /* CONFIG_USB_NON_PCI_OHCI */
+
+ /*-------------------------------------------------------------------------*/
+
+@@ -2698,6 +2785,7 @@
+ }
+
+
++#ifndef CONFIG_USB_NON_PCI_OHCI
+ #ifdef CONFIG_PM
+
+ /*-------------------------------------------------------------------------*/
+@@ -2936,20 +3024,29 @@
+ resume: ohci_pci_resume,
+ #endif /* PM */
+ };
++#endif /* CONFIG_USB_NON_PCI_OHCI */
+
+
+ /*-------------------------------------------------------------------------*/
+
+ static int __init ohci_hcd_init (void)
+ {
++#ifndef CONFIG_USB_NON_PCI_OHCI
+ return pci_module_init (&ohci_pci_driver);
++#else
++ return ohci_non_pci_init();
++#endif
+ }
+
+ /*-------------------------------------------------------------------------*/
+
+ static void __exit ohci_hcd_cleanup (void)
+ {
++#ifndef CONFIG_USB_NON_PCI_OHCI
+ pci_unregister_driver (&ohci_pci_driver);
++#else
++ ohci_pci_remove(&bogus_pcidev);
++#endif
+ }
+
+ module_init (ohci_hcd_init);
diff --git a/linux/linux-mtx-1-2.4.24/09-iw-max-spy-32.diff b/linux/linux-mtx-1-2.4.24/09-iw-max-spy-32.diff
index e69de29bb2..e782d3d3dc 100644
--- a/linux/linux-mtx-1-2.4.24/09-iw-max-spy-32.diff
+++ b/linux/linux-mtx-1-2.4.24/09-iw-max-spy-32.diff
@@ -0,0 +1,11 @@
+--- linux-mips-2.4.24-pre2/include/linux/wireless.h 2004-11-17 18:05:09.000000000 +0100
++++ linux/include/linux/wireless.h 2004-11-17 19:02:44.370081592 +0100
+@@ -334,7 +334,7 @@
+ * a few of them in the struct iw_range. */
+
+ /* Maximum of address that you may set with SPY */
+-#define IW_MAX_SPY 8
++#define IW_MAX_SPY 32
+
+ /* Maximum of address that you may get in the
+ list of access points in range */
diff --git a/linux/linux-mtx-1-2.4.24/10-mtx-pci-slots.diff b/linux/linux-mtx-1-2.4.24/10-mtx-pci-slots.diff
index e69de29bb2..8cdde20440 100644
--- a/linux/linux-mtx-1-2.4.24/10-mtx-pci-slots.diff
+++ b/linux/linux-mtx-1-2.4.24/10-mtx-pci-slots.diff
@@ -0,0 +1,23 @@
+--- linux-mips-2.4.24-pre2/arch/mips/au1000/common/pci_fixup.c 2004-11-17 18:04:41.000000000 +0100
++++ linux/arch/mips/au1000/common/pci_fixup.c 2004-11-18 11:22:03.176289496 +0100
+@@ -118,6 +118,20 @@
+ slot = PCI_SLOT(dev->devfn);
+ #if defined( CONFIG_SOC_AU1500 )
+ switch (slot) {
++#if defined( CONFIG_MIPS_MTX1 )
++ case 0:
++ dev->irq = AU1000_PCI_INTA;
++ break;
++ case 1:
++ dev->irq = AU1000_PCI_INTB;
++ break;
++ case 2:
++ dev->irq = AU1000_PCI_INTC;
++ break;
++ case 3:
++ dev->irq = AU1000_PCI_INTD;
++ break;
++#endif
+ case 12:
+ case 13:
+ default:
diff --git a/linux/linux-mtx-1-2.4.24/11-mtx-extraversion.diff b/linux/linux-mtx-1-2.4.24/11-mtx-extraversion.diff
index e69de29bb2..c57296981c 100644
--- a/linux/linux-mtx-1-2.4.24/11-mtx-extraversion.diff
+++ b/linux/linux-mtx-1-2.4.24/11-mtx-extraversion.diff
@@ -0,0 +1,11 @@
+--- linux-mips-2.4.24-pre2/Makefile 2004-11-17 18:04:38.000000000 +0100
++++ linux/Makefile 2004-11-17 18:57:54.314176792 +0100
+@@ -1,7 +1,7 @@
+ VERSION = 2
+ PATCHLEVEL = 4
+ SUBLEVEL = 24
+-EXTRAVERSION = -pre2
++EXTRAVERSION = mtx
+
+ KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
+
diff --git a/linux/linux-mtx-1-2.4.24/12-openswan-2.2.0-nat-t.diff b/linux/linux-mtx-1-2.4.24/12-openswan-2.2.0-nat-t.diff
index e69de29bb2..31bcbd8a5a 100644
--- a/linux/linux-mtx-1-2.4.24/12-openswan-2.2.0-nat-t.diff
+++ b/linux/linux-mtx-1-2.4.24/12-openswan-2.2.0-nat-t.diff
@@ -0,0 +1,143 @@
+diff -Nurb linux-1-11/include/net/sock.h linux/include/net/sock.h
+--- linux-1-11/include/net/sock.h 2004-11-18 09:55:07.377211480 +0100
++++ linux/include/net/sock.h 2004-11-18 09:55:43.605703912 +0100
+@@ -256,6 +256,13 @@
+ __u32 end_seq;
+ };
+
++#if 1
++#define UDP_OPT_IN_SOCK 1
++struct udp_opt {
++ __u32 esp_in_udp;
++};
++#endif
++
+ struct tcp_opt {
+ int tcp_header_len; /* Bytes of tcp header to send */
+
+@@ -601,6 +608,9 @@
+ #if defined(CONFIG_SPX) || defined (CONFIG_SPX_MODULE)
+ struct spx_opt af_spx;
+ #endif /* CONFIG_SPX */
++#if 1
++ struct udp_opt af_udp;
++#endif
+
+ } tp_pinfo;
+
+diff -Nurb linux-1-11/net/Config.in linux/net/Config.in
+--- linux-1-11/net/Config.in 2004-11-18 09:55:07.545185944 +0100
++++ linux/net/Config.in 2004-11-18 09:55:43.627700568 +0100
+@@ -102,4 +102,6 @@
+ tristate 'Packet Generator (USE WITH CAUTION)' CONFIG_NET_PKTGEN
+ endmenu
+
++bool 'IPSEC NAT-Traversal' CONFIG_IPSEC_NAT_TRAVERSAL
++
+ endmenu
+diff -Nurb linux-1-11/net/ipv4/udp.c linux/net/ipv4/udp.c
+--- linux-1-11/net/ipv4/udp.c 2004-11-18 09:55:07.958123168 +0100
++++ linux/net/ipv4/udp.c 2004-11-18 09:55:43.638698896 +0100
+@@ -804,6 +804,9 @@
+
+ static int udp_queue_rcv_skb(struct sock * sk, struct sk_buff *skb)
+ {
++#ifdef CONFIG_IPSEC_NAT_TRAVERSAL
++ struct udp_opt *tp = &(sk->tp_pinfo.af_udp);
++#endif
+ /*
+ * Charge it to the socket, dropping if the queue is full.
+ */
+@@ -821,6 +824,40 @@
+ }
+ #endif
+
++#ifdef CONFIG_IPSEC_NAT_TRAVERSAL
++ if (tp->esp_in_udp) {
++ /*
++ * Set skb->sk and xmit packet to ipsec_rcv.
++ *
++ * If ret != 0, ipsec_rcv refused the packet (not ESPinUDP),
++ * restore skb->sk and fall back to sock_queue_rcv_skb
++ */
++ struct inet_protocol *esp = NULL;
++
++#if defined(CONFIG_IPSEC) && !defined(CONFIG_IPSEC_MODULE)
++ /* optomize only when we know it is statically linked */
++ extern struct inet_protocol esp_protocol;
++ esp = &esp_protocol;
++#else
++ for (esp = (struct inet_protocol *)inet_protos[IPPROTO_ESP & (MAX_INET_PROTOS - 1)];
++ (esp) && (esp->protocol != IPPROTO_ESP);
++ esp = esp->next);
++#endif
++
++ if (esp && esp->handler) {
++ struct sock *sav_sk = skb->sk;
++ skb->sk = sk;
++ if (esp->handler(skb) == 0) {
++ skb->sk = sav_sk;
++ /*not sure we might count ESPinUDP as UDP...*/
++ UDP_INC_STATS_BH(UdpInDatagrams);
++ return 0;
++ }
++ skb->sk = sav_sk;
++ }
++ }
++#endif
++
+ if (sock_queue_rcv_skb(sk,skb)<0) {
+ UDP_INC_STATS_BH(UdpInErrors);
+ IP_INC_STATS_BH(IpInDiscards);
+@@ -1044,13 +1081,49 @@
+ return len;
+ }
+
++static int udp_setsockopt(struct sock *sk, int level, int optname,
++ char *optval, int optlen)
++{
++ struct udp_opt *tp = &(sk->tp_pinfo.af_udp);
++ int val;
++ int err = 0;
++
++ if (level != SOL_UDP)
++ return ip_setsockopt(sk, level, optname, optval, optlen);
++
++ if(optlen<sizeof(int))
++ return -EINVAL;
++
++ if (get_user(val, (int *)optval))
++ return -EFAULT;
++
++ lock_sock(sk);
++
++ switch(optname) {
++#ifdef CONFIG_IPSEC_NAT_TRAVERSAL
++#ifndef UDP_ESPINUDP
++#define UDP_ESPINUDP 100
++#endif
++ case UDP_ESPINUDP:
++ tp->esp_in_udp = val;
++ break;
++#endif
++ default:
++ err = -ENOPROTOOPT;
++ break;
++ }
++
++ release_sock(sk);
++ return err;
++}
++
+ struct proto udp_prot = {
+ name: "UDP",
+ close: udp_close,
+ connect: udp_connect,
+ disconnect: udp_disconnect,
+ ioctl: udp_ioctl,
+- setsockopt: ip_setsockopt,
++ setsockopt: udp_setsockopt,
+ getsockopt: ip_getsockopt,
+ sendmsg: udp_sendmsg,
+ recvmsg: udp_recvmsg,
+
diff --git a/linux/linux-mtx-1-2.4.24/13-openswan-2.2.0.patch b/linux/linux-mtx-1-2.4.24/13-openswan-2.2.0.patch
index e69de29bb2..db64219eda 100644
--- a/linux/linux-mtx-1-2.4.24/13-openswan-2.2.0.patch
+++ b/linux/linux-mtx-1-2.4.24/13-openswan-2.2.0.patch
@@ -0,0 +1,61573 @@
+make[1]: Entering directory `/data/mtx/oe/tmp/work/openswan-2.2.0-r0/openswan-2.2.0'
+packaging/utils/kernelpatch 2.4
+--- linux/Documentation/Configure.help.orig Fri Dec 21 12:41:53 2001
++++ linux/Documentation/Configure.help Mon Jul 29 16:35:32 2002
+@@ -24237,5 +24237,65 @@
+
+-#
++IP Security Protocol (IPSEC) (EXPERIMENTAL)
++CONFIG_IPSEC
++ This unit is experimental code.
++ Pick 'y' for static linking, 'm' for module support or 'n' for none.
++ This option adds support for network layer packet encryption and/or
++ authentication with participating hosts. The standards start with:
++ RFCs 2411, 2407 and 2401. Others are mentioned where they refer to
++ specific features below. There are more pending which can be found
++ at: ftp://ftp.ietf.org/internet-drafts/draft-ietf-ipsec-*.
++ A description of each document can also be found at:
++ http://ietf.org/ids.by.wg/ipsec.html.
++ Their charter can be found at:
++ http://www.ietf.org/html.charters/ipsec-charter.html
++ Snapshots and releases of the current work can be found at:
++ http://www.freeswan.org/
++
++IPSEC: IP-in-IP encapsulation
++CONFIG_IPSEC_IPIP
++ This option provides support for tunnel mode IPSEC. It is recommended
++ to enable this.
++
++IPSEC: Authentication Header
++CONFIG_IPSEC_AH
++ This option provides support for the IPSEC Authentication Header
++ (IP protocol 51) which provides packet layer sender and content
++ authentication. It is recommended to enable this. RFC2402
++
++HMAC-MD5 algorithm
++CONFIG_IPSEC_AUTH_HMAC_MD5
++ Provides support for authentication using the HMAC MD5
++ algorithm with 96 bits of hash used as the authenticator. RFC2403
++
++HMAC-SHA1 algorithm
++CONFIG_IPSEC_AUTH_HMAC_SHA1
++ Provides support for Authentication Header using the HMAC SHA1
++ algorithm with 96 bits of hash used as the authenticator. RFC2404
++
++IPSEC: Encapsulating Security Payload
++CONFIG_IPSEC_ESP
++ This option provides support for the IPSEC Encapsulation Security
++ Payload (IP protocol 50) which provides packet layer content
++ hiding. It is recommended to enable this. RFC2406
++
++3DES algorithm
++CONFIG_IPSEC_ENC_3DES
++ Provides support for Encapsulation Security Payload protocol, using
++ the triple DES encryption algorithm. RFC2451
++
++IPSEC Debugging Option
++CONFIG_IPSEC_DEBUG
++ Enables IPSEC kernel debugging. It is further controlled by the
++ user space utility 'klipsdebug'.
++
++IPSEC Regression Testing option
++CONFIG_IPSEC_REGRESS
++ Enables IPSEC regression testing. Creates a number of switches in
++ /proc/sys/net/ipsec which cause various failure modes in KLIPS.
++ For more details see FreeSWAN source under
++ testing/doc/regression_options.txt.
++
++#
+ # A couple of things I keep forgetting:
+ # capitalize: AppleTalk, Ethernet, DOS, DMA, FAT, FTP, Internet,
+ # Intel, IRQ, ISDN, Linux, MSDOS, NetWare, NetWinder,
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/README.openswan-2 Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,112 @@
++*
++* RCSID $Id$
++*
++
++ ****************************************
++ * IPSEC for Linux, Release 2.xx series *
++ ****************************************
++
++
++
++1. Files
++
++The contents of linux/net/ipsec/ (see below) join the linux kernel source tree.
++as provided for higher up.
++
++The programs/ directory contains the user-level utilities which you need
++to run IPSEC. See the top-level top/INSTALL to compile and install them.
++
++The testing/ directory contains test scripts.
++
++The doc/ directory contains -- what else -- documentation.
++
++1.1. Kernel files
++
++The following are found in net/ipsec/:
++
++Makefile The Makefile
++Config.in The configuration script for make menuconfig
++defconfig Configuration defaults for first time.
++
++radij.c General-purpose radix-tree operations
++
++ipsec_ipcomp.c IPCOMP encapsulate/decapsulate code.
++ipsec_ah.c Authentication Header (AH) encapsulate/decapsulate code.
++ipsec_esp.c Encapsulated Security Payload (ESP) encap/decap code.
++
++pfkey_v2.c PF_KEYv2 socket interface code.
++pfkey_v2_parser.c PF_KEYv2 message parsing and processing code.
++
++ipsec_init.c Initialization code, /proc interface.
++ipsec_radij.c Interface with the radix tree code.
++ipsec_netlink.c Interface with the netlink code.
++ipsec_xform.c Routines and structures common to transforms.
++ipsec_tunnel.c The outgoing packet processing code.
++ipsec_rcv.c The incoming packet processing code.
++ipsec_md5c.c Somewhat modified RSADSI MD5 C code.
++ipsec_sha1.c Somewhat modified Steve Reid SHA-1 C code.
++
++sysctl_net_ipsec.c /proc/sys/net/ipsec/* variable definitions.
++
++version.c symbolic link to project version.
++
++radij.h Headers for radij.c
++
++ipcomp.h Headers used by IPCOMP code.
++
++ipsec_radij.h Interface with the radix tree code.
++ipsec_netlink.h Headers used by the netlink interface.
++ipsec_encap.h Headers defining encapsulation structures.
++ipsec_xform.h Transform headers.
++ipsec_tunnel.h Headers used by tunneling code.
++ipsec_ipe4.h Headers for the IP-in-IP code.
++ipsec_ah.h Headers common to AH transforms.
++ipsec_md5h.h RSADSI MD5 headers.
++ipsec_sha1.h SHA-1 headers.
++ipsec_esp.h Headers common to ESP transfroms.
++ipsec_rcv.h Headers for incoming packet processing code.
++
++1.2. User-level files.
++
++The following are found in utils/:
++
++eroute.c Create an "extended route" source code
++spi.c Set up Security Associations source code
++spigrp.c Link SPIs together source code.
++tncfg.c Configure the tunneling features of the virtual interface
++ source code
++klipsdebug.c Set/reset klips debugging features source code.
++version.c symbolic link to project version.
++
++eroute.8 Create an "extended route" manual page
++spi.8 Set up Security Associations manual page
++spigrp.8 Link SPIs together manual page
++tncfg.8 Configure the tunneling features of the virtual interface
++ manual page
++klipsdebug.8 Set/reset klips debugging features manual page
++
++eroute.5 /proc/net/ipsec_eroute format manual page
++spi.5 /proc/net/ipsec_spi format manual page
++spigrp.5 /proc/net/ipsec_spigrp format manual page
++tncfg.5 /proc/net/ipsec_tncfg format manual page
++klipsdebug.5 /proc/net/ipsec_klipsdebug format manual page
++version.5 /proc/net/ipsec_version format manual page
++pf_key.5 /proc/net/pf_key format manual page
++
++Makefile Utilities makefile.
++
++*.8 Manpages for the respective utils.
++
++
++1.3. Test files
++
++The test scripts are locate in testing/ and and documentation is found
++at doc/src/umltesting.html. Automated testing via "make check" is available
++provided that the User-Mode-Linux patches are available.
++
++*
++* $Log$
++* Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++* Turn off EOLN_NATIVE flag
++*
++* (Logical change 1.5010)
++*
++* Revision 1.1 2003/12/10 01:07:49 mcr
++* documentation for additions.
++*
++*
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/crypto/ciphers/aes/Makefile.objs Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,13 @@
++
++ASM-$(ARCH_ASM):=1
++ASM_X86:=$(ASM-i586)$(ASM-i686)
++
++ifneq ($(strip $(ASM_X86)),)
++obj-$(CONFIG_IPSEC_ENC_AES) += aes-i586.o
++else
++obj-$(CONFIG_IPSEC_ENC_AES) += aes.o
++endif
++
++obj-$(CONFIG_IPSEC_ENC_AES) += aes_cbc.o
++obj-$(CONFIG_IPSEC_ENC_AES) += aes_xcbc_mac.o
++
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/crypto/ciphers/aes/aes-i586.S Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,892 @@
++//
++// Copyright (c) 2001, Dr Brian Gladman <brg@gladman.uk.net>, Worcester, UK.
++// All rights reserved.
++//
++// TERMS
++//
++// Redistribution and use in source and binary forms, with or without
++// modification, are permitted subject to the following conditions:
++//
++// 1. Redistributions of source code must retain the above copyright
++// notice, this list of conditions and the following disclaimer.
++//
++// 2. Redistributions in binary form must reproduce the above copyright
++// notice, this list of conditions and the following disclaimer in the
++// documentation and/or other materials provided with the distribution.
++//
++// 3. The copyright holder's name must not be used to endorse or promote
++// any products derived from this software without his specific prior
++// written permission.
++//
++// This software is provided 'as is' with no express or implied warranties
++// of correctness or fitness for purpose.
++
++// Modified by Jari Ruusu, December 24 2001
++// - Converted syntax to GNU CPP/assembler syntax
++// - C programming interface converted back to "old" API
++// - Minor portability cleanups and speed optimizations
++
++// An AES (Rijndael) implementation for the Pentium. This version only
++// implements the standard AES block length (128 bits, 16 bytes). This code
++// does not preserve the eax, ecx or edx registers or the artihmetic status
++// flags. However, the ebx, esi, edi, and ebp registers are preserved across
++// calls.
++
++// void aes_set_key(aes_context *cx, const unsigned char key[], const int key_len, const int f)
++// void aes_encrypt(const aes_context *cx, const unsigned char in_blk[], unsigned char out_blk[])
++// void aes_decrypt(const aes_context *cx, const unsigned char in_blk[], unsigned char out_blk[])
++
++#if defined(USE_UNDERLINE)
++# define aes_set_key _aes_set_key
++# define aes_encrypt _aes_encrypt
++# define aes_decrypt _aes_decrypt
++#endif
++#if !defined(ALIGN32BYTES)
++# define ALIGN32BYTES 32
++#endif
++
++ .file "aes-i586.S"
++ .globl aes_set_key
++ .globl aes_encrypt
++ .globl aes_decrypt
++
++#define tlen 1024 // length of each of 4 'xor' arrays (256 32-bit words)
++
++// offsets to parameters with one register pushed onto stack
++
++#define ctx 8 // AES context structure
++#define in_blk 12 // input byte array address parameter
++#define out_blk 16 // output byte array address parameter
++
++// offsets in context structure
++
++#define nkey 0 // key length, size 4
++#define nrnd 4 // number of rounds, size 4
++#define ekey 8 // encryption key schedule base address, size 256
++#define dkey 264 // decryption key schedule base address, size 256
++
++// This macro performs a forward encryption cycle. It is entered with
++// the first previous round column values in %eax, %ebx, %esi and %edi and
++// exits with the final values in the same registers.
++
++#define fwd_rnd(p1,p2) \
++ mov %ebx,(%esp) ;\
++ movzbl %al,%edx ;\
++ mov %eax,%ecx ;\
++ mov p2(%ebp),%eax ;\
++ mov %edi,4(%esp) ;\
++ mov p2+12(%ebp),%edi ;\
++ xor p1(,%edx,4),%eax ;\
++ movzbl %ch,%edx ;\
++ shr $16,%ecx ;\
++ mov p2+4(%ebp),%ebx ;\
++ xor p1+tlen(,%edx,4),%edi ;\
++ movzbl %cl,%edx ;\
++ movzbl %ch,%ecx ;\
++ xor p1+3*tlen(,%ecx,4),%ebx ;\
++ mov %esi,%ecx ;\
++ mov p1+2*tlen(,%edx,4),%esi ;\
++ movzbl %cl,%edx ;\
++ xor p1(,%edx,4),%esi ;\
++ movzbl %ch,%edx ;\
++ shr $16,%ecx ;\
++ xor p1+tlen(,%edx,4),%ebx ;\
++ movzbl %cl,%edx ;\
++ movzbl %ch,%ecx ;\
++ xor p1+2*tlen(,%edx,4),%eax ;\
++ mov (%esp),%edx ;\
++ xor p1+3*tlen(,%ecx,4),%edi ;\
++ movzbl %dl,%ecx ;\
++ xor p2+8(%ebp),%esi ;\
++ xor p1(,%ecx,4),%ebx ;\
++ movzbl %dh,%ecx ;\
++ shr $16,%edx ;\
++ xor p1+tlen(,%ecx,4),%eax ;\
++ movzbl %dl,%ecx ;\
++ movzbl %dh,%edx ;\
++ xor p1+2*tlen(,%ecx,4),%edi ;\
++ mov 4(%esp),%ecx ;\
++ xor p1+3*tlen(,%edx,4),%esi ;\
++ movzbl %cl,%edx ;\
++ xor p1(,%edx,4),%edi ;\
++ movzbl %ch,%edx ;\
++ shr $16,%ecx ;\
++ xor p1+tlen(,%edx,4),%esi ;\
++ movzbl %cl,%edx ;\
++ movzbl %ch,%ecx ;\
++ xor p1+2*tlen(,%edx,4),%ebx ;\
++ xor p1+3*tlen(,%ecx,4),%eax
++
++// This macro performs an inverse encryption cycle. It is entered with
++// the first previous round column values in %eax, %ebx, %esi and %edi and
++// exits with the final values in the same registers.
++
++#define inv_rnd(p1,p2) \
++ movzbl %al,%edx ;\
++ mov %ebx,(%esp) ;\
++ mov %eax,%ecx ;\
++ mov p2(%ebp),%eax ;\
++ mov %edi,4(%esp) ;\
++ mov p2+4(%ebp),%ebx ;\
++ xor p1(,%edx,4),%eax ;\
++ movzbl %ch,%edx ;\
++ shr $16,%ecx ;\
++ mov p2+12(%ebp),%edi ;\
++ xor p1+tlen(,%edx,4),%ebx ;\
++ movzbl %cl,%edx ;\
++ movzbl %ch,%ecx ;\
++ xor p1+3*tlen(,%ecx,4),%edi ;\
++ mov %esi,%ecx ;\
++ mov p1+2*tlen(,%edx,4),%esi ;\
++ movzbl %cl,%edx ;\
++ xor p1(,%edx,4),%esi ;\
++ movzbl %ch,%edx ;\
++ shr $16,%ecx ;\
++ xor p1+tlen(,%edx,4),%edi ;\
++ movzbl %cl,%edx ;\
++ movzbl %ch,%ecx ;\
++ xor p1+2*tlen(,%edx,4),%eax ;\
++ mov (%esp),%edx ;\
++ xor p1+3*tlen(,%ecx,4),%ebx ;\
++ movzbl %dl,%ecx ;\
++ xor p2+8(%ebp),%esi ;\
++ xor p1(,%ecx,4),%ebx ;\
++ movzbl %dh,%ecx ;\
++ shr $16,%edx ;\
++ xor p1+tlen(,%ecx,4),%esi ;\
++ movzbl %dl,%ecx ;\
++ movzbl %dh,%edx ;\
++ xor p1+2*tlen(,%ecx,4),%edi ;\
++ mov 4(%esp),%ecx ;\
++ xor p1+3*tlen(,%edx,4),%eax ;\
++ movzbl %cl,%edx ;\
++ xor p1(,%edx,4),%edi ;\
++ movzbl %ch,%edx ;\
++ shr $16,%ecx ;\
++ xor p1+tlen(,%edx,4),%eax ;\
++ movzbl %cl,%edx ;\
++ movzbl %ch,%ecx ;\
++ xor p1+2*tlen(,%edx,4),%ebx ;\
++ xor p1+3*tlen(,%ecx,4),%esi
++
++// AES (Rijndael) Encryption Subroutine
++
++ .text
++ .align ALIGN32BYTES
++aes_encrypt:
++ push %ebp
++ mov ctx(%esp),%ebp // pointer to context
++ mov in_blk(%esp),%ecx
++ push %ebx
++ push %esi
++ push %edi
++ mov nrnd(%ebp),%edx // number of rounds
++ lea ekey+16(%ebp),%ebp // key pointer
++
++// input four columns and xor in first round key
++
++ mov (%ecx),%eax
++ mov 4(%ecx),%ebx
++ mov 8(%ecx),%esi
++ mov 12(%ecx),%edi
++ xor -16(%ebp),%eax
++ xor -12(%ebp),%ebx
++ xor -8(%ebp),%esi
++ xor -4(%ebp),%edi
++
++ sub $8,%esp // space for register saves on stack
++
++ sub $10,%edx
++ je aes_15
++ add $32,%ebp
++ sub $2,%edx
++ je aes_13
++ add $32,%ebp
++
++ fwd_rnd(aes_ft_tab,-64) // 14 rounds for 256-bit key
++ fwd_rnd(aes_ft_tab,-48)
++aes_13: fwd_rnd(aes_ft_tab,-32) // 12 rounds for 192-bit key
++ fwd_rnd(aes_ft_tab,-16)
++aes_15: fwd_rnd(aes_ft_tab,0) // 10 rounds for 128-bit key
++ fwd_rnd(aes_ft_tab,16)
++ fwd_rnd(aes_ft_tab,32)
++ fwd_rnd(aes_ft_tab,48)
++ fwd_rnd(aes_ft_tab,64)
++ fwd_rnd(aes_ft_tab,80)
++ fwd_rnd(aes_ft_tab,96)
++ fwd_rnd(aes_ft_tab,112)
++ fwd_rnd(aes_ft_tab,128)
++ fwd_rnd(aes_fl_tab,144) // last round uses a different table
++
++// move final values to the output array.
++
++ mov out_blk+20(%esp),%ebp
++ add $8,%esp
++ mov %eax,(%ebp)
++ mov %ebx,4(%ebp)
++ mov %esi,8(%ebp)
++ mov %edi,12(%ebp)
++ pop %edi
++ pop %esi
++ pop %ebx
++ pop %ebp
++ ret
++
++
++// AES (Rijndael) Decryption Subroutine
++
++ .align ALIGN32BYTES
++aes_decrypt:
++ push %ebp
++ mov ctx(%esp),%ebp // pointer to context
++ mov in_blk(%esp),%ecx
++ push %ebx
++ push %esi
++ push %edi
++ mov nrnd(%ebp),%edx // number of rounds
++ lea dkey+16(%ebp),%ebp // key pointer
++
++// input four columns and xor in first round key
++
++ mov (%ecx),%eax
++ mov 4(%ecx),%ebx
++ mov 8(%ecx),%esi
++ mov 12(%ecx),%edi
++ xor -16(%ebp),%eax
++ xor -12(%ebp),%ebx
++ xor -8(%ebp),%esi
++ xor -4(%ebp),%edi
++
++ sub $8,%esp // space for register saves on stack
++
++ sub $10,%edx
++ je aes_25
++ add $32,%ebp
++ sub $2,%edx
++ je aes_23
++ add $32,%ebp
++
++ inv_rnd(aes_it_tab,-64) // 14 rounds for 256-bit key
++ inv_rnd(aes_it_tab,-48)
++aes_23: inv_rnd(aes_it_tab,-32) // 12 rounds for 192-bit key
++ inv_rnd(aes_it_tab,-16)
++aes_25: inv_rnd(aes_it_tab,0) // 10 rounds for 128-bit key
++ inv_rnd(aes_it_tab,16)
++ inv_rnd(aes_it_tab,32)
++ inv_rnd(aes_it_tab,48)
++ inv_rnd(aes_it_tab,64)
++ inv_rnd(aes_it_tab,80)
++ inv_rnd(aes_it_tab,96)
++ inv_rnd(aes_it_tab,112)
++ inv_rnd(aes_it_tab,128)
++ inv_rnd(aes_il_tab,144) // last round uses a different table
++
++// move final values to the output array.
++
++ mov out_blk+20(%esp),%ebp
++ add $8,%esp
++ mov %eax,(%ebp)
++ mov %ebx,4(%ebp)
++ mov %esi,8(%ebp)
++ mov %edi,12(%ebp)
++ pop %edi
++ pop %esi
++ pop %ebx
++ pop %ebp
++ ret
++
++// AES (Rijndael) Key Schedule Subroutine
++
++// input/output parameters
++
++#define aes_cx 12 // AES context
++#define in_key 16 // key input array address
++#define key_ln 20 // key length, bytes (16,24,32) or bits (128,192,256)
++#define ed_flg 24 // 0=create both encr/decr keys, 1=create encr key only
++
++// offsets for locals
++
++#define cnt -4
++#define kpf -8
++#define slen 8
++
++// This macro performs a column mixing operation on an input 32-bit
++// word to give a 32-bit result. It uses each of the 4 bytes in the
++// the input column to index 4 different tables of 256 32-bit words
++// that are xored together to form the output value.
++
++#define mix_col(p1) \
++ movzbl %bl,%ecx ;\
++ mov p1(,%ecx,4),%eax ;\
++ movzbl %bh,%ecx ;\
++ ror $16,%ebx ;\
++ xor p1+tlen(,%ecx,4),%eax ;\
++ movzbl %bl,%ecx ;\
++ xor p1+2*tlen(,%ecx,4),%eax ;\
++ movzbl %bh,%ecx ;\
++ xor p1+3*tlen(,%ecx,4),%eax
++
++// Key Schedule Macros
++
++#define ksc4(p1) \
++ rol $24,%ebx ;\
++ mix_col(aes_fl_tab) ;\
++ ror $8,%ebx ;\
++ xor 4*p1+aes_rcon_tab,%eax ;\
++ xor %eax,%esi ;\
++ xor %esi,%ebp ;\
++ mov %esi,16*p1(%edi) ;\
++ mov %ebp,16*p1+4(%edi) ;\
++ xor %ebp,%edx ;\
++ xor %edx,%ebx ;\
++ mov %edx,16*p1+8(%edi) ;\
++ mov %ebx,16*p1+12(%edi)
++
++#define ksc6(p1) \
++ rol $24,%ebx ;\
++ mix_col(aes_fl_tab) ;\
++ ror $8,%ebx ;\
++ xor 4*p1+aes_rcon_tab,%eax ;\
++ xor 24*p1-24(%edi),%eax ;\
++ mov %eax,24*p1(%edi) ;\
++ xor 24*p1-20(%edi),%eax ;\
++ mov %eax,24*p1+4(%edi) ;\
++ xor %eax,%esi ;\
++ xor %esi,%ebp ;\
++ mov %esi,24*p1+8(%edi) ;\
++ mov %ebp,24*p1+12(%edi) ;\
++ xor %ebp,%edx ;\
++ xor %edx,%ebx ;\
++ mov %edx,24*p1+16(%edi) ;\
++ mov %ebx,24*p1+20(%edi)
++
++#define ksc8(p1) \
++ rol $24,%ebx ;\
++ mix_col(aes_fl_tab) ;\
++ ror $8,%ebx ;\
++ xor 4*p1+aes_rcon_tab,%eax ;\
++ xor 32*p1-32(%edi),%eax ;\
++ mov %eax,32*p1(%edi) ;\
++ xor 32*p1-28(%edi),%eax ;\
++ mov %eax,32*p1+4(%edi) ;\
++ xor 32*p1-24(%edi),%eax ;\
++ mov %eax,32*p1+8(%edi) ;\
++ xor 32*p1-20(%edi),%eax ;\
++ mov %eax,32*p1+12(%edi) ;\
++ push %ebx ;\
++ mov %eax,%ebx ;\
++ mix_col(aes_fl_tab) ;\
++ pop %ebx ;\
++ xor %eax,%esi ;\
++ xor %esi,%ebp ;\
++ mov %esi,32*p1+16(%edi) ;\
++ mov %ebp,32*p1+20(%edi) ;\
++ xor %ebp,%edx ;\
++ xor %edx,%ebx ;\
++ mov %edx,32*p1+24(%edi) ;\
++ mov %ebx,32*p1+28(%edi)
++
++ .align ALIGN32BYTES
++aes_set_key:
++ pushfl
++ push %ebp
++ mov %esp,%ebp
++ sub $slen,%esp
++ push %ebx
++ push %esi
++ push %edi
++
++ mov aes_cx(%ebp),%edx // edx -> AES context
++
++ mov key_ln(%ebp),%ecx // key length
++ cmpl $128,%ecx
++ jb aes_30
++ shr $3,%ecx
++aes_30: cmpl $32,%ecx
++ je aes_32
++ cmpl $24,%ecx
++ je aes_32
++ mov $16,%ecx
++aes_32: shr $2,%ecx
++ mov %ecx,nkey(%edx)
++
++ lea 6(%ecx),%eax // 10/12/14 for 4/6/8 32-bit key length
++ mov %eax,nrnd(%edx)
++
++ mov in_key(%ebp),%esi // key input array
++ lea ekey(%edx),%edi // key position in AES context
++ cld
++ push %ebp
++ mov %ecx,%eax // save key length in eax
++ rep ; movsl // words in the key schedule
++ mov -4(%esi),%ebx // put some values in registers
++ mov -8(%esi),%edx // to allow faster code
++ mov -12(%esi),%ebp
++ mov -16(%esi),%esi
++
++ cmpl $4,%eax // jump on key size
++ je aes_36
++ cmpl $6,%eax
++ je aes_35
++
++ ksc8(0)
++ ksc8(1)
++ ksc8(2)
++ ksc8(3)
++ ksc8(4)
++ ksc8(5)
++ ksc8(6)
++ jmp aes_37
++aes_35: ksc6(0)
++ ksc6(1)
++ ksc6(2)
++ ksc6(3)
++ ksc6(4)
++ ksc6(5)
++ ksc6(6)
++ ksc6(7)
++ jmp aes_37
++aes_36: ksc4(0)
++ ksc4(1)
++ ksc4(2)
++ ksc4(3)
++ ksc4(4)
++ ksc4(5)
++ ksc4(6)
++ ksc4(7)
++ ksc4(8)
++ ksc4(9)
++aes_37: pop %ebp
++ mov aes_cx(%ebp),%edx // edx -> AES context
++ cmpl $0,ed_flg(%ebp)
++ jne aes_39
++
++// compile decryption key schedule from encryption schedule - reverse
++// order and do mix_column operation on round keys except first and last
++
++ mov nrnd(%edx),%eax // kt = cx->d_key + nc * cx->Nrnd
++ shl $2,%eax
++ lea dkey(%edx,%eax,4),%edi
++ lea ekey(%edx),%esi // kf = cx->e_key
++
++ movsl // copy first round key (unmodified)
++ movsl
++ movsl
++ movsl
++ sub $32,%edi
++ movl $1,cnt(%ebp)
++aes_38: // do mix column on each column of
++ lodsl // each round key
++ mov %eax,%ebx
++ mix_col(aes_im_tab)
++ stosl
++ lodsl
++ mov %eax,%ebx
++ mix_col(aes_im_tab)
++ stosl
++ lodsl
++ mov %eax,%ebx
++ mix_col(aes_im_tab)
++ stosl
++ lodsl
++ mov %eax,%ebx
++ mix_col(aes_im_tab)
++ stosl
++ sub $32,%edi
++
++ incl cnt(%ebp)
++ mov cnt(%ebp),%eax
++ cmp nrnd(%edx),%eax
++ jb aes_38
++
++ movsl // copy last round key (unmodified)
++ movsl
++ movsl
++ movsl
++aes_39: pop %edi
++ pop %esi
++ pop %ebx
++ mov %ebp,%esp
++ pop %ebp
++ popfl
++ ret
++
++
++// finite field multiplies by {02}, {04} and {08}
++
++#define f2(x) ((x<<1)^(((x>>7)&1)*0x11b))
++#define f4(x) ((x<<2)^(((x>>6)&1)*0x11b)^(((x>>6)&2)*0x11b))
++#define f8(x) ((x<<3)^(((x>>5)&1)*0x11b)^(((x>>5)&2)*0x11b)^(((x>>5)&4)*0x11b))
++
++// finite field multiplies required in table generation
++
++#define f3(x) (f2(x) ^ x)
++#define f9(x) (f8(x) ^ x)
++#define fb(x) (f8(x) ^ f2(x) ^ x)
++#define fd(x) (f8(x) ^ f4(x) ^ x)
++#define fe(x) (f8(x) ^ f4(x) ^ f2(x))
++
++// These defines generate the forward table entries
++
++#define u0(x) ((f3(x) << 24) | (x << 16) | (x << 8) | f2(x))
++#define u1(x) ((x << 24) | (x << 16) | (f2(x) << 8) | f3(x))
++#define u2(x) ((x << 24) | (f2(x) << 16) | (f3(x) << 8) | x)
++#define u3(x) ((f2(x) << 24) | (f3(x) << 16) | (x << 8) | x)
++
++// These defines generate the inverse table entries
++
++#define v0(x) ((fb(x) << 24) | (fd(x) << 16) | (f9(x) << 8) | fe(x))
++#define v1(x) ((fd(x) << 24) | (f9(x) << 16) | (fe(x) << 8) | fb(x))
++#define v2(x) ((f9(x) << 24) | (fe(x) << 16) | (fb(x) << 8) | fd(x))
++#define v3(x) ((fe(x) << 24) | (fb(x) << 16) | (fd(x) << 8) | f9(x))
++
++// These defines generate entries for the last round tables
++
++#define w0(x) (x)
++#define w1(x) (x << 8)
++#define w2(x) (x << 16)
++#define w3(x) (x << 24)
++
++// macro to generate inverse mix column tables (needed for the key schedule)
++
++#define im_data0(p1) \
++ .long p1(0x00),p1(0x01),p1(0x02),p1(0x03),p1(0x04),p1(0x05),p1(0x06),p1(0x07) ;\
++ .long p1(0x08),p1(0x09),p1(0x0a),p1(0x0b),p1(0x0c),p1(0x0d),p1(0x0e),p1(0x0f) ;\
++ .long p1(0x10),p1(0x11),p1(0x12),p1(0x13),p1(0x14),p1(0x15),p1(0x16),p1(0x17) ;\
++ .long p1(0x18),p1(0x19),p1(0x1a),p1(0x1b),p1(0x1c),p1(0x1d),p1(0x1e),p1(0x1f)
++#define im_data1(p1) \
++ .long p1(0x20),p1(0x21),p1(0x22),p1(0x23),p1(0x24),p1(0x25),p1(0x26),p1(0x27) ;\
++ .long p1(0x28),p1(0x29),p1(0x2a),p1(0x2b),p1(0x2c),p1(0x2d),p1(0x2e),p1(0x2f) ;\
++ .long p1(0x30),p1(0x31),p1(0x32),p1(0x33),p1(0x34),p1(0x35),p1(0x36),p1(0x37) ;\
++ .long p1(0x38),p1(0x39),p1(0x3a),p1(0x3b),p1(0x3c),p1(0x3d),p1(0x3e),p1(0x3f)
++#define im_data2(p1) \
++ .long p1(0x40),p1(0x41),p1(0x42),p1(0x43),p1(0x44),p1(0x45),p1(0x46),p1(0x47) ;\
++ .long p1(0x48),p1(0x49),p1(0x4a),p1(0x4b),p1(0x4c),p1(0x4d),p1(0x4e),p1(0x4f) ;\
++ .long p1(0x50),p1(0x51),p1(0x52),p1(0x53),p1(0x54),p1(0x55),p1(0x56),p1(0x57) ;\
++ .long p1(0x58),p1(0x59),p1(0x5a),p1(0x5b),p1(0x5c),p1(0x5d),p1(0x5e),p1(0x5f)
++#define im_data3(p1) \
++ .long p1(0x60),p1(0x61),p1(0x62),p1(0x63),p1(0x64),p1(0x65),p1(0x66),p1(0x67) ;\
++ .long p1(0x68),p1(0x69),p1(0x6a),p1(0x6b),p1(0x6c),p1(0x6d),p1(0x6e),p1(0x6f) ;\
++ .long p1(0x70),p1(0x71),p1(0x72),p1(0x73),p1(0x74),p1(0x75),p1(0x76),p1(0x77) ;\
++ .long p1(0x78),p1(0x79),p1(0x7a),p1(0x7b),p1(0x7c),p1(0x7d),p1(0x7e),p1(0x7f)
++#define im_data4(p1) \
++ .long p1(0x80),p1(0x81),p1(0x82),p1(0x83),p1(0x84),p1(0x85),p1(0x86),p1(0x87) ;\
++ .long p1(0x88),p1(0x89),p1(0x8a),p1(0x8b),p1(0x8c),p1(0x8d),p1(0x8e),p1(0x8f) ;\
++ .long p1(0x90),p1(0x91),p1(0x92),p1(0x93),p1(0x94),p1(0x95),p1(0x96),p1(0x97) ;\
++ .long p1(0x98),p1(0x99),p1(0x9a),p1(0x9b),p1(0x9c),p1(0x9d),p1(0x9e),p1(0x9f)
++#define im_data5(p1) \
++ .long p1(0xa0),p1(0xa1),p1(0xa2),p1(0xa3),p1(0xa4),p1(0xa5),p1(0xa6),p1(0xa7) ;\
++ .long p1(0xa8),p1(0xa9),p1(0xaa),p1(0xab),p1(0xac),p1(0xad),p1(0xae),p1(0xaf) ;\
++ .long p1(0xb0),p1(0xb1),p1(0xb2),p1(0xb3),p1(0xb4),p1(0xb5),p1(0xb6),p1(0xb7) ;\
++ .long p1(0xb8),p1(0xb9),p1(0xba),p1(0xbb),p1(0xbc),p1(0xbd),p1(0xbe),p1(0xbf)
++#define im_data6(p1) \
++ .long p1(0xc0),p1(0xc1),p1(0xc2),p1(0xc3),p1(0xc4),p1(0xc5),p1(0xc6),p1(0xc7) ;\
++ .long p1(0xc8),p1(0xc9),p1(0xca),p1(0xcb),p1(0xcc),p1(0xcd),p1(0xce),p1(0xcf) ;\
++ .long p1(0xd0),p1(0xd1),p1(0xd2),p1(0xd3),p1(0xd4),p1(0xd5),p1(0xd6),p1(0xd7) ;\
++ .long p1(0xd8),p1(0xd9),p1(0xda),p1(0xdb),p1(0xdc),p1(0xdd),p1(0xde),p1(0xdf)
++#define im_data7(p1) \
++ .long p1(0xe0),p1(0xe1),p1(0xe2),p1(0xe3),p1(0xe4),p1(0xe5),p1(0xe6),p1(0xe7) ;\
++ .long p1(0xe8),p1(0xe9),p1(0xea),p1(0xeb),p1(0xec),p1(0xed),p1(0xee),p1(0xef) ;\
++ .long p1(0xf0),p1(0xf1),p1(0xf2),p1(0xf3),p1(0xf4),p1(0xf5),p1(0xf6),p1(0xf7) ;\
++ .long p1(0xf8),p1(0xf9),p1(0xfa),p1(0xfb),p1(0xfc),p1(0xfd),p1(0xfe),p1(0xff)
++
++// S-box data - 256 entries
++
++#define sb_data0(p1) \
++ .long p1(0x63),p1(0x7c),p1(0x77),p1(0x7b),p1(0xf2),p1(0x6b),p1(0x6f),p1(0xc5) ;\
++ .long p1(0x30),p1(0x01),p1(0x67),p1(0x2b),p1(0xfe),p1(0xd7),p1(0xab),p1(0x76) ;\
++ .long p1(0xca),p1(0x82),p1(0xc9),p1(0x7d),p1(0xfa),p1(0x59),p1(0x47),p1(0xf0) ;\
++ .long p1(0xad),p1(0xd4),p1(0xa2),p1(0xaf),p1(0x9c),p1(0xa4),p1(0x72),p1(0xc0)
++#define sb_data1(p1) \
++ .long p1(0xb7),p1(0xfd),p1(0x93),p1(0x26),p1(0x36),p1(0x3f),p1(0xf7),p1(0xcc) ;\
++ .long p1(0x34),p1(0xa5),p1(0xe5),p1(0xf1),p1(0x71),p1(0xd8),p1(0x31),p1(0x15) ;\
++ .long p1(0x04),p1(0xc7),p1(0x23),p1(0xc3),p1(0x18),p1(0x96),p1(0x05),p1(0x9a) ;\
++ .long p1(0x07),p1(0x12),p1(0x80),p1(0xe2),p1(0xeb),p1(0x27),p1(0xb2),p1(0x75)
++#define sb_data2(p1) \
++ .long p1(0x09),p1(0x83),p1(0x2c),p1(0x1a),p1(0x1b),p1(0x6e),p1(0x5a),p1(0xa0) ;\
++ .long p1(0x52),p1(0x3b),p1(0xd6),p1(0xb3),p1(0x29),p1(0xe3),p1(0x2f),p1(0x84) ;\
++ .long p1(0x53),p1(0xd1),p1(0x00),p1(0xed),p1(0x20),p1(0xfc),p1(0xb1),p1(0x5b) ;\
++ .long p1(0x6a),p1(0xcb),p1(0xbe),p1(0x39),p1(0x4a),p1(0x4c),p1(0x58),p1(0xcf)
++#define sb_data3(p1) \
++ .long p1(0xd0),p1(0xef),p1(0xaa),p1(0xfb),p1(0x43),p1(0x4d),p1(0x33),p1(0x85) ;\
++ .long p1(0x45),p1(0xf9),p1(0x02),p1(0x7f),p1(0x50),p1(0x3c),p1(0x9f),p1(0xa8) ;\
++ .long p1(0x51),p1(0xa3),p1(0x40),p1(0x8f),p1(0x92),p1(0x9d),p1(0x38),p1(0xf5) ;\
++ .long p1(0xbc),p1(0xb6),p1(0xda),p1(0x21),p1(0x10),p1(0xff),p1(0xf3),p1(0xd2)
++#define sb_data4(p1) \
++ .long p1(0xcd),p1(0x0c),p1(0x13),p1(0xec),p1(0x5f),p1(0x97),p1(0x44),p1(0x17) ;\
++ .long p1(0xc4),p1(0xa7),p1(0x7e),p1(0x3d),p1(0x64),p1(0x5d),p1(0x19),p1(0x73) ;\
++ .long p1(0x60),p1(0x81),p1(0x4f),p1(0xdc),p1(0x22),p1(0x2a),p1(0x90),p1(0x88) ;\
++ .long p1(0x46),p1(0xee),p1(0xb8),p1(0x14),p1(0xde),p1(0x5e),p1(0x0b),p1(0xdb)
++#define sb_data5(p1) \
++ .long p1(0xe0),p1(0x32),p1(0x3a),p1(0x0a),p1(0x49),p1(0x06),p1(0x24),p1(0x5c) ;\
++ .long p1(0xc2),p1(0xd3),p1(0xac),p1(0x62),p1(0x91),p1(0x95),p1(0xe4),p1(0x79) ;\
++ .long p1(0xe7),p1(0xc8),p1(0x37),p1(0x6d),p1(0x8d),p1(0xd5),p1(0x4e),p1(0xa9) ;\
++ .long p1(0x6c),p1(0x56),p1(0xf4),p1(0xea),p1(0x65),p1(0x7a),p1(0xae),p1(0x08)
++#define sb_data6(p1) \
++ .long p1(0xba),p1(0x78),p1(0x25),p1(0x2e),p1(0x1c),p1(0xa6),p1(0xb4),p1(0xc6) ;\
++ .long p1(0xe8),p1(0xdd),p1(0x74),p1(0x1f),p1(0x4b),p1(0xbd),p1(0x8b),p1(0x8a) ;\
++ .long p1(0x70),p1(0x3e),p1(0xb5),p1(0x66),p1(0x48),p1(0x03),p1(0xf6),p1(0x0e) ;\
++ .long p1(0x61),p1(0x35),p1(0x57),p1(0xb9),p1(0x86),p1(0xc1),p1(0x1d),p1(0x9e)
++#define sb_data7(p1) \
++ .long p1(0xe1),p1(0xf8),p1(0x98),p1(0x11),p1(0x69),p1(0xd9),p1(0x8e),p1(0x94) ;\
++ .long p1(0x9b),p1(0x1e),p1(0x87),p1(0xe9),p1(0xce),p1(0x55),p1(0x28),p1(0xdf) ;\
++ .long p1(0x8c),p1(0xa1),p1(0x89),p1(0x0d),p1(0xbf),p1(0xe6),p1(0x42),p1(0x68) ;\
++ .long p1(0x41),p1(0x99),p1(0x2d),p1(0x0f),p1(0xb0),p1(0x54),p1(0xbb),p1(0x16)
++
++// Inverse S-box data - 256 entries
++
++#define ib_data0(p1) \
++ .long p1(0x52),p1(0x09),p1(0x6a),p1(0xd5),p1(0x30),p1(0x36),p1(0xa5),p1(0x38) ;\
++ .long p1(0xbf),p1(0x40),p1(0xa3),p1(0x9e),p1(0x81),p1(0xf3),p1(0xd7),p1(0xfb) ;\
++ .long p1(0x7c),p1(0xe3),p1(0x39),p1(0x82),p1(0x9b),p1(0x2f),p1(0xff),p1(0x87) ;\
++ .long p1(0x34),p1(0x8e),p1(0x43),p1(0x44),p1(0xc4),p1(0xde),p1(0xe9),p1(0xcb)
++#define ib_data1(p1) \
++ .long p1(0x54),p1(0x7b),p1(0x94),p1(0x32),p1(0xa6),p1(0xc2),p1(0x23),p1(0x3d) ;\
++ .long p1(0xee),p1(0x4c),p1(0x95),p1(0x0b),p1(0x42),p1(0xfa),p1(0xc3),p1(0x4e) ;\
++ .long p1(0x08),p1(0x2e),p1(0xa1),p1(0x66),p1(0x28),p1(0xd9),p1(0x24),p1(0xb2) ;\
++ .long p1(0x76),p1(0x5b),p1(0xa2),p1(0x49),p1(0x6d),p1(0x8b),p1(0xd1),p1(0x25)
++#define ib_data2(p1) \
++ .long p1(0x72),p1(0xf8),p1(0xf6),p1(0x64),p1(0x86),p1(0x68),p1(0x98),p1(0x16) ;\
++ .long p1(0xd4),p1(0xa4),p1(0x5c),p1(0xcc),p1(0x5d),p1(0x65),p1(0xb6),p1(0x92) ;\
++ .long p1(0x6c),p1(0x70),p1(0x48),p1(0x50),p1(0xfd),p1(0xed),p1(0xb9),p1(0xda) ;\
++ .long p1(0x5e),p1(0x15),p1(0x46),p1(0x57),p1(0xa7),p1(0x8d),p1(0x9d),p1(0x84)
++#define ib_data3(p1) \
++ .long p1(0x90),p1(0xd8),p1(0xab),p1(0x00),p1(0x8c),p1(0xbc),p1(0xd3),p1(0x0a) ;\
++ .long p1(0xf7),p1(0xe4),p1(0x58),p1(0x05),p1(0xb8),p1(0xb3),p1(0x45),p1(0x06) ;\
++ .long p1(0xd0),p1(0x2c),p1(0x1e),p1(0x8f),p1(0xca),p1(0x3f),p1(0x0f),p1(0x02) ;\
++ .long p1(0xc1),p1(0xaf),p1(0xbd),p1(0x03),p1(0x01),p1(0x13),p1(0x8a),p1(0x6b)
++#define ib_data4(p1) \
++ .long p1(0x3a),p1(0x91),p1(0x11),p1(0x41),p1(0x4f),p1(0x67),p1(0xdc),p1(0xea) ;\
++ .long p1(0x97),p1(0xf2),p1(0xcf),p1(0xce),p1(0xf0),p1(0xb4),p1(0xe6),p1(0x73) ;\
++ .long p1(0x96),p1(0xac),p1(0x74),p1(0x22),p1(0xe7),p1(0xad),p1(0x35),p1(0x85) ;\
++ .long p1(0xe2),p1(0xf9),p1(0x37),p1(0xe8),p1(0x1c),p1(0x75),p1(0xdf),p1(0x6e)
++#define ib_data5(p1) \
++ .long p1(0x47),p1(0xf1),p1(0x1a),p1(0x71),p1(0x1d),p1(0x29),p1(0xc5),p1(0x89) ;\
++ .long p1(0x6f),p1(0xb7),p1(0x62),p1(0x0e),p1(0xaa),p1(0x18),p1(0xbe),p1(0x1b) ;\
++ .long p1(0xfc),p1(0x56),p1(0x3e),p1(0x4b),p1(0xc6),p1(0xd2),p1(0x79),p1(0x20) ;\
++ .long p1(0x9a),p1(0xdb),p1(0xc0),p1(0xfe),p1(0x78),p1(0xcd),p1(0x5a),p1(0xf4)
++#define ib_data6(p1) \
++ .long p1(0x1f),p1(0xdd),p1(0xa8),p1(0x33),p1(0x88),p1(0x07),p1(0xc7),p1(0x31) ;\
++ .long p1(0xb1),p1(0x12),p1(0x10),p1(0x59),p1(0x27),p1(0x80),p1(0xec),p1(0x5f) ;\
++ .long p1(0x60),p1(0x51),p1(0x7f),p1(0xa9),p1(0x19),p1(0xb5),p1(0x4a),p1(0x0d) ;\
++ .long p1(0x2d),p1(0xe5),p1(0x7a),p1(0x9f),p1(0x93),p1(0xc9),p1(0x9c),p1(0xef)
++#define ib_data7(p1) \
++ .long p1(0xa0),p1(0xe0),p1(0x3b),p1(0x4d),p1(0xae),p1(0x2a),p1(0xf5),p1(0xb0) ;\
++ .long p1(0xc8),p1(0xeb),p1(0xbb),p1(0x3c),p1(0x83),p1(0x53),p1(0x99),p1(0x61) ;\
++ .long p1(0x17),p1(0x2b),p1(0x04),p1(0x7e),p1(0xba),p1(0x77),p1(0xd6),p1(0x26) ;\
++ .long p1(0xe1),p1(0x69),p1(0x14),p1(0x63),p1(0x55),p1(0x21),p1(0x0c),p1(0x7d)
++
++// The rcon_table (needed for the key schedule)
++//
++// Here is original Dr Brian Gladman's source code:
++// _rcon_tab:
++// %assign x 1
++// %rep 29
++// dd x
++// %assign x f2(x)
++// %endrep
++//
++// Here is precomputed output (it's more portable this way):
++
++ .align ALIGN32BYTES
++aes_rcon_tab:
++ .long 0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80
++ .long 0x1b,0x36,0x6c,0xd8,0xab,0x4d,0x9a,0x2f
++ .long 0x5e,0xbc,0x63,0xc6,0x97,0x35,0x6a,0xd4
++ .long 0xb3,0x7d,0xfa,0xef,0xc5
++
++// The forward xor tables
++
++ .align ALIGN32BYTES
++aes_ft_tab:
++ sb_data0(u0)
++ sb_data1(u0)
++ sb_data2(u0)
++ sb_data3(u0)
++ sb_data4(u0)
++ sb_data5(u0)
++ sb_data6(u0)
++ sb_data7(u0)
++
++ sb_data0(u1)
++ sb_data1(u1)
++ sb_data2(u1)
++ sb_data3(u1)
++ sb_data4(u1)
++ sb_data5(u1)
++ sb_data6(u1)
++ sb_data7(u1)
++
++ sb_data0(u2)
++ sb_data1(u2)
++ sb_data2(u2)
++ sb_data3(u2)
++ sb_data4(u2)
++ sb_data5(u2)
++ sb_data6(u2)
++ sb_data7(u2)
++
++ sb_data0(u3)
++ sb_data1(u3)
++ sb_data2(u3)
++ sb_data3(u3)
++ sb_data4(u3)
++ sb_data5(u3)
++ sb_data6(u3)
++ sb_data7(u3)
++
++ .align ALIGN32BYTES
++aes_fl_tab:
++ sb_data0(w0)
++ sb_data1(w0)
++ sb_data2(w0)
++ sb_data3(w0)
++ sb_data4(w0)
++ sb_data5(w0)
++ sb_data6(w0)
++ sb_data7(w0)
++
++ sb_data0(w1)
++ sb_data1(w1)
++ sb_data2(w1)
++ sb_data3(w1)
++ sb_data4(w1)
++ sb_data5(w1)
++ sb_data6(w1)
++ sb_data7(w1)
++
++ sb_data0(w2)
++ sb_data1(w2)
++ sb_data2(w2)
++ sb_data3(w2)
++ sb_data4(w2)
++ sb_data5(w2)
++ sb_data6(w2)
++ sb_data7(w2)
++
++ sb_data0(w3)
++ sb_data1(w3)
++ sb_data2(w3)
++ sb_data3(w3)
++ sb_data4(w3)
++ sb_data5(w3)
++ sb_data6(w3)
++ sb_data7(w3)
++
++// The inverse xor tables
++
++ .align ALIGN32BYTES
++aes_it_tab:
++ ib_data0(v0)
++ ib_data1(v0)
++ ib_data2(v0)
++ ib_data3(v0)
++ ib_data4(v0)
++ ib_data5(v0)
++ ib_data6(v0)
++ ib_data7(v0)
++
++ ib_data0(v1)
++ ib_data1(v1)
++ ib_data2(v1)
++ ib_data3(v1)
++ ib_data4(v1)
++ ib_data5(v1)
++ ib_data6(v1)
++ ib_data7(v1)
++
++ ib_data0(v2)
++ ib_data1(v2)
++ ib_data2(v2)
++ ib_data3(v2)
++ ib_data4(v2)
++ ib_data5(v2)
++ ib_data6(v2)
++ ib_data7(v2)
++
++ ib_data0(v3)
++ ib_data1(v3)
++ ib_data2(v3)
++ ib_data3(v3)
++ ib_data4(v3)
++ ib_data5(v3)
++ ib_data6(v3)
++ ib_data7(v3)
++
++ .align ALIGN32BYTES
++aes_il_tab:
++ ib_data0(w0)
++ ib_data1(w0)
++ ib_data2(w0)
++ ib_data3(w0)
++ ib_data4(w0)
++ ib_data5(w0)
++ ib_data6(w0)
++ ib_data7(w0)
++
++ ib_data0(w1)
++ ib_data1(w1)
++ ib_data2(w1)
++ ib_data3(w1)
++ ib_data4(w1)
++ ib_data5(w1)
++ ib_data6(w1)
++ ib_data7(w1)
++
++ ib_data0(w2)
++ ib_data1(w2)
++ ib_data2(w2)
++ ib_data3(w2)
++ ib_data4(w2)
++ ib_data5(w2)
++ ib_data6(w2)
++ ib_data7(w2)
++
++ ib_data0(w3)
++ ib_data1(w3)
++ ib_data2(w3)
++ ib_data3(w3)
++ ib_data4(w3)
++ ib_data5(w3)
++ ib_data6(w3)
++ ib_data7(w3)
++
++// The inverse mix column tables
++
++ .align ALIGN32BYTES
++aes_im_tab:
++ im_data0(v0)
++ im_data1(v0)
++ im_data2(v0)
++ im_data3(v0)
++ im_data4(v0)
++ im_data5(v0)
++ im_data6(v0)
++ im_data7(v0)
++
++ im_data0(v1)
++ im_data1(v1)
++ im_data2(v1)
++ im_data3(v1)
++ im_data4(v1)
++ im_data5(v1)
++ im_data6(v1)
++ im_data7(v1)
++
++ im_data0(v2)
++ im_data1(v2)
++ im_data2(v2)
++ im_data3(v2)
++ im_data4(v2)
++ im_data5(v2)
++ im_data6(v2)
++ im_data7(v2)
++
++ im_data0(v3)
++ im_data1(v3)
++ im_data2(v3)
++ im_data3(v3)
++ im_data4(v3)
++ im_data5(v3)
++ im_data6(v3)
++ im_data7(v3)
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/crypto/ciphers/aes/aes.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,1415 @@
++// I retain copyright in this code but I encourage its free use provided
++// that I don't carry any responsibility for the results. I am especially
++// happy to see it used in free and open source software. If you do use
++// it I would appreciate an acknowledgement of its origin in the code or
++// the product that results and I would also appreciate knowing a little
++// about the use to which it is being put. I am grateful to Frank Yellin
++// for some ideas that are used in this implementation.
++//
++// Dr B. R. Gladman <brg@gladman.uk.net> 6th April 2001.
++//
++// This is an implementation of the AES encryption algorithm (Rijndael)
++// designed by Joan Daemen and Vincent Rijmen. This version is designed
++// to provide both fixed and dynamic block and key lengths and can also
++// run with either big or little endian internal byte order (see aes.h).
++// It inputs block and key lengths in bytes with the legal values being
++// 16, 24 and 32.
++
++/*
++ * Modified by Jari Ruusu, May 1 2001
++ * - Fixed some compile warnings, code was ok but gcc warned anyway.
++ * - Changed basic types: byte -> unsigned char, word -> u_int32_t
++ * - Major name space cleanup: Names visible to outside now begin
++ * with "aes_" or "AES_". A lot of stuff moved from aes.h to aes.c
++ * - Removed C++ and DLL support as part of name space cleanup.
++ * - Eliminated unnecessary recomputation of tables. (actual bug fix)
++ * - Merged precomputed constant tables to aes.c file.
++ * - Removed data alignment restrictions for portability reasons.
++ * - Made block and key lengths accept bit count (128/192/256)
++ * as well byte count (16/24/32).
++ * - Removed all error checks. This change also eliminated the need
++ * to preinitialize the context struct to zero.
++ * - Removed some totally unused constants.
++ */
++
++#include "crypto/aes.h"
++
++// CONFIGURATION OPTIONS (see also aes.h)
++//
++// 1. Define UNROLL for full loop unrolling in encryption and decryption.
++// 2. Define PARTIAL_UNROLL to unroll two loops in encryption and decryption.
++// 3. Define FIXED_TABLES for compiled rather than dynamic tables.
++// 4. Define FF_TABLES to use tables for field multiplies and inverses.
++// Do not enable this without understanding stack space requirements.
++// 5. Define ARRAYS to use arrays to hold the local state block. If this
++// is not defined, individually declared 32-bit words are used.
++// 6. Define FAST_VARIABLE if a high speed variable block implementation
++// is needed (essentially three separate fixed block size code sequences)
++// 7. Define either ONE_TABLE or FOUR_TABLES for a fast table driven
++// version using 1 table (2 kbytes of table space) or 4 tables (8
++// kbytes of table space) for higher speed.
++// 8. Define either ONE_LR_TABLE or FOUR_LR_TABLES for a further speed
++// increase by using tables for the last rounds but with more table
++// space (2 or 8 kbytes extra).
++// 9. If neither ONE_TABLE nor FOUR_TABLES is defined, a compact but
++// slower version is provided.
++// 10. If fast decryption key scheduling is needed define ONE_IM_TABLE
++// or FOUR_IM_TABLES for higher speed (2 or 8 kbytes extra).
++
++#define UNROLL
++//#define PARTIAL_UNROLL
++
++#define FIXED_TABLES
++//#define FF_TABLES
++//#define ARRAYS
++#define FAST_VARIABLE
++
++//#define ONE_TABLE
++#define FOUR_TABLES
++
++//#define ONE_LR_TABLE
++#define FOUR_LR_TABLES
++
++//#define ONE_IM_TABLE
++#define FOUR_IM_TABLES
++
++#if defined(UNROLL) && defined (PARTIAL_UNROLL)
++#error both UNROLL and PARTIAL_UNROLL are defined
++#endif
++
++#if defined(ONE_TABLE) && defined (FOUR_TABLES)
++#error both ONE_TABLE and FOUR_TABLES are defined
++#endif
++
++#if defined(ONE_LR_TABLE) && defined (FOUR_LR_TABLES)
++#error both ONE_LR_TABLE and FOUR_LR_TABLES are defined
++#endif
++
++#if defined(ONE_IM_TABLE) && defined (FOUR_IM_TABLES)
++#error both ONE_IM_TABLE and FOUR_IM_TABLES are defined
++#endif
++
++#if defined(AES_BLOCK_SIZE) && AES_BLOCK_SIZE != 16 && AES_BLOCK_SIZE != 24 && AES_BLOCK_SIZE != 32
++#error an illegal block size has been specified
++#endif
++
++// upr(x,n): rotates bytes within words by n positions, moving bytes
++// to higher index positions with wrap around into low positions
++// ups(x,n): moves bytes by n positions to higher index positions in
++// words but without wrap around
++// bval(x,n): extracts a byte from a word
++
++#define upr(x,n) (((x) << 8 * (n)) | ((x) >> (32 - 8 * (n))))
++#define ups(x,n) ((x) << 8 * (n))
++#define bval(x,n) ((unsigned char)((x) >> 8 * (n)))
++#define bytes2word(b0, b1, b2, b3) \
++ ((u_int32_t)(b3) << 24 | (u_int32_t)(b2) << 16 | (u_int32_t)(b1) << 8 | (b0))
++
++
++/* little endian processor without data alignment restrictions: AES_LE_OK */
++/* original code: i386 */
++#if defined(i386) || defined(_I386) || defined(__i386__) || defined(__i386)
++#define AES_LE_OK 1
++/* added (tested): alpha --jjo */
++#elif defined(__alpha__)|| defined (__alpha)
++#define AES_LE_OK 1
++/* added (tested): ia64 --jjo */
++#elif defined(__ia64__)|| defined (__ia64)
++#define AES_LE_OK 1
++#endif
++
++#ifdef AES_LE_OK
++/* little endian processor without data alignment restrictions */
++#define word_in(x) *(u_int32_t*)(x)
++#define const_word_in(x) *(const u_int32_t*)(x)
++#define word_out(x,v) *(u_int32_t*)(x) = (v)
++#define const_word_out(x,v) *(const u_int32_t*)(x) = (v)
++#else
++/* slower but generic big endian or with data alignment restrictions */
++/* some additional "const" touches to stop "gcc -Wcast-qual" complains --jjo */
++#define word_in(x) ((u_int32_t)(((unsigned char *)(x))[0])|((u_int32_t)(((unsigned char *)(x))[1])<<8)|((u_int32_t)(((unsigned char *)(x))[2])<<16)|((u_int32_t)(((unsigned char *)(x))[3])<<24))
++#define const_word_in(x) ((const u_int32_t)(((const unsigned char *)(x))[0])|((const u_int32_t)(((const unsigned char *)(x))[1])<<8)|((const u_int32_t)(((const unsigned char *)(x))[2])<<16)|((const u_int32_t)(((const unsigned char *)(x))[3])<<24))
++#define word_out(x,v) ((unsigned char *)(x))[0]=(v),((unsigned char *)(x))[1]=((v)>>8),((unsigned char *)(x))[2]=((v)>>16),((unsigned char *)(x))[3]=((v)>>24)
++#define const_word_out(x,v) ((const unsigned char *)(x))[0]=(v),((const unsigned char *)(x))[1]=((v)>>8),((const unsigned char *)(x))[2]=((v)>>16),((const unsigned char *)(x))[3]=((v)>>24)
++#endif
++
++// Disable at least some poor combinations of options
++
++#if !defined(ONE_TABLE) && !defined(FOUR_TABLES)
++#define FIXED_TABLES
++#undef UNROLL
++#undef ONE_LR_TABLE
++#undef FOUR_LR_TABLES
++#undef ONE_IM_TABLE
++#undef FOUR_IM_TABLES
++#elif !defined(FOUR_TABLES)
++#ifdef FOUR_LR_TABLES
++#undef FOUR_LR_TABLES
++#define ONE_LR_TABLE
++#endif
++#ifdef FOUR_IM_TABLES
++#undef FOUR_IM_TABLES
++#define ONE_IM_TABLE
++#endif
++#elif !defined(AES_BLOCK_SIZE)
++#if defined(UNROLL)
++#define PARTIAL_UNROLL
++#undef UNROLL
++#endif
++#endif
++
++// the finite field modular polynomial and elements
++
++#define ff_poly 0x011b
++#define ff_hi 0x80
++
++// multiply four bytes in GF(2^8) by 'x' {02} in parallel
++
++#define m1 0x80808080
++#define m2 0x7f7f7f7f
++#define m3 0x0000001b
++#define FFmulX(x) ((((x) & m2) << 1) ^ ((((x) & m1) >> 7) * m3))
++
++// The following defines provide alternative definitions of FFmulX that might
++// give improved performance if a fast 32-bit multiply is not available. Note
++// that a temporary variable u needs to be defined where FFmulX is used.
++
++// #define FFmulX(x) (u = (x) & m1, u |= (u >> 1), ((x) & m2) << 1) ^ ((u >> 3) | (u >> 6))
++// #define m4 0x1b1b1b1b
++// #define FFmulX(x) (u = (x) & m1, ((x) & m2) << 1) ^ ((u - (u >> 7)) & m4)
++
++// perform column mix operation on four bytes in parallel
++
++#define fwd_mcol(x) (f2 = FFmulX(x), f2 ^ upr(x ^ f2,3) ^ upr(x,2) ^ upr(x,1))
++
++#if defined(FIXED_TABLES)
++
++// the S-Box table
++
++static const unsigned char s_box[256] =
++{
++ 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5,
++ 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
++ 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0,
++ 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
++ 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc,
++ 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
++ 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a,
++ 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
++ 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0,
++ 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
++ 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b,
++ 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
++ 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85,
++ 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
++ 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5,
++ 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
++ 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17,
++ 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
++ 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88,
++ 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
++ 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c,
++ 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
++ 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9,
++ 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
++ 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6,
++ 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
++ 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e,
++ 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
++ 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94,
++ 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
++ 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68,
++ 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16
++};
++
++// the inverse S-Box table
++
++static const unsigned char inv_s_box[256] =
++{
++ 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38,
++ 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
++ 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87,
++ 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
++ 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d,
++ 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
++ 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2,
++ 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
++ 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16,
++ 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
++ 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda,
++ 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
++ 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a,
++ 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
++ 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02,
++ 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
++ 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea,
++ 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
++ 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85,
++ 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
++ 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89,
++ 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
++ 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20,
++ 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
++ 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31,
++ 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
++ 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d,
++ 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
++ 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0,
++ 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
++ 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26,
++ 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d
++};
++
++#define w0(p) 0x000000##p
++
++// Number of elements required in this table for different
++// block and key lengths is:
++//
++// Nk = 4 6 8
++// ----------
++// Nb = 4 | 10 8 7
++// 6 | 19 12 11
++// 8 | 29 19 14
++//
++// this table can be a table of bytes if the key schedule
++// code is adjusted accordingly
++
++static const u_int32_t rcon_tab[29] =
++{
++ w0(01), w0(02), w0(04), w0(08),
++ w0(10), w0(20), w0(40), w0(80),
++ w0(1b), w0(36), w0(6c), w0(d8),
++ w0(ab), w0(4d), w0(9a), w0(2f),
++ w0(5e), w0(bc), w0(63), w0(c6),
++ w0(97), w0(35), w0(6a), w0(d4),
++ w0(b3), w0(7d), w0(fa), w0(ef),
++ w0(c5)
++};
++
++#undef w0
++
++#define r0(p,q,r,s) 0x##p##q##r##s
++#define r1(p,q,r,s) 0x##q##r##s##p
++#define r2(p,q,r,s) 0x##r##s##p##q
++#define r3(p,q,r,s) 0x##s##p##q##r
++#define w0(p) 0x000000##p
++#define w1(p) 0x0000##p##00
++#define w2(p) 0x00##p##0000
++#define w3(p) 0x##p##000000
++
++#if defined(FIXED_TABLES) && (defined(ONE_TABLE) || defined(FOUR_TABLES))
++
++// data for forward tables (other than last round)
++
++#define f_table \
++ r(a5,63,63,c6), r(84,7c,7c,f8), r(99,77,77,ee), r(8d,7b,7b,f6),\
++ r(0d,f2,f2,ff), r(bd,6b,6b,d6), r(b1,6f,6f,de), r(54,c5,c5,91),\
++ r(50,30,30,60), r(03,01,01,02), r(a9,67,67,ce), r(7d,2b,2b,56),\
++ r(19,fe,fe,e7), r(62,d7,d7,b5), r(e6,ab,ab,4d), r(9a,76,76,ec),\
++ r(45,ca,ca,8f), r(9d,82,82,1f), r(40,c9,c9,89), r(87,7d,7d,fa),\
++ r(15,fa,fa,ef), r(eb,59,59,b2), r(c9,47,47,8e), r(0b,f0,f0,fb),\
++ r(ec,ad,ad,41), r(67,d4,d4,b3), r(fd,a2,a2,5f), r(ea,af,af,45),\
++ r(bf,9c,9c,23), r(f7,a4,a4,53), r(96,72,72,e4), r(5b,c0,c0,9b),\
++ r(c2,b7,b7,75), r(1c,fd,fd,e1), r(ae,93,93,3d), r(6a,26,26,4c),\
++ r(5a,36,36,6c), r(41,3f,3f,7e), r(02,f7,f7,f5), r(4f,cc,cc,83),\
++ r(5c,34,34,68), r(f4,a5,a5,51), r(34,e5,e5,d1), r(08,f1,f1,f9),\
++ r(93,71,71,e2), r(73,d8,d8,ab), r(53,31,31,62), r(3f,15,15,2a),\
++ r(0c,04,04,08), r(52,c7,c7,95), r(65,23,23,46), r(5e,c3,c3,9d),\
++ r(28,18,18,30), r(a1,96,96,37), r(0f,05,05,0a), r(b5,9a,9a,2f),\
++ r(09,07,07,0e), r(36,12,12,24), r(9b,80,80,1b), r(3d,e2,e2,df),\
++ r(26,eb,eb,cd), r(69,27,27,4e), r(cd,b2,b2,7f), r(9f,75,75,ea),\
++ r(1b,09,09,12), r(9e,83,83,1d), r(74,2c,2c,58), r(2e,1a,1a,34),\
++ r(2d,1b,1b,36), r(b2,6e,6e,dc), r(ee,5a,5a,b4), r(fb,a0,a0,5b),\
++ r(f6,52,52,a4), r(4d,3b,3b,76), r(61,d6,d6,b7), r(ce,b3,b3,7d),\
++ r(7b,29,29,52), r(3e,e3,e3,dd), r(71,2f,2f,5e), r(97,84,84,13),\
++ r(f5,53,53,a6), r(68,d1,d1,b9), r(00,00,00,00), r(2c,ed,ed,c1),\
++ r(60,20,20,40), r(1f,fc,fc,e3), r(c8,b1,b1,79), r(ed,5b,5b,b6),\
++ r(be,6a,6a,d4), r(46,cb,cb,8d), r(d9,be,be,67), r(4b,39,39,72),\
++ r(de,4a,4a,94), r(d4,4c,4c,98), r(e8,58,58,b0), r(4a,cf,cf,85),\
++ r(6b,d0,d0,bb), r(2a,ef,ef,c5), r(e5,aa,aa,4f), r(16,fb,fb,ed),\
++ r(c5,43,43,86), r(d7,4d,4d,9a), r(55,33,33,66), r(94,85,85,11),\
++ r(cf,45,45,8a), r(10,f9,f9,e9), r(06,02,02,04), r(81,7f,7f,fe),\
++ r(f0,50,50,a0), r(44,3c,3c,78), r(ba,9f,9f,25), r(e3,a8,a8,4b),\
++ r(f3,51,51,a2), r(fe,a3,a3,5d), r(c0,40,40,80), r(8a,8f,8f,05),\
++ r(ad,92,92,3f), r(bc,9d,9d,21), r(48,38,38,70), r(04,f5,f5,f1),\
++ r(df,bc,bc,63), r(c1,b6,b6,77), r(75,da,da,af), r(63,21,21,42),\
++ r(30,10,10,20), r(1a,ff,ff,e5), r(0e,f3,f3,fd), r(6d,d2,d2,bf),\
++ r(4c,cd,cd,81), r(14,0c,0c,18), r(35,13,13,26), r(2f,ec,ec,c3),\
++ r(e1,5f,5f,be), r(a2,97,97,35), r(cc,44,44,88), r(39,17,17,2e),\
++ r(57,c4,c4,93), r(f2,a7,a7,55), r(82,7e,7e,fc), r(47,3d,3d,7a),\
++ r(ac,64,64,c8), r(e7,5d,5d,ba), r(2b,19,19,32), r(95,73,73,e6),\
++ r(a0,60,60,c0), r(98,81,81,19), r(d1,4f,4f,9e), r(7f,dc,dc,a3),\
++ r(66,22,22,44), r(7e,2a,2a,54), r(ab,90,90,3b), r(83,88,88,0b),\
++ r(ca,46,46,8c), r(29,ee,ee,c7), r(d3,b8,b8,6b), r(3c,14,14,28),\
++ r(79,de,de,a7), r(e2,5e,5e,bc), r(1d,0b,0b,16), r(76,db,db,ad),\
++ r(3b,e0,e0,db), r(56,32,32,64), r(4e,3a,3a,74), r(1e,0a,0a,14),\
++ r(db,49,49,92), r(0a,06,06,0c), r(6c,24,24,48), r(e4,5c,5c,b8),\
++ r(5d,c2,c2,9f), r(6e,d3,d3,bd), r(ef,ac,ac,43), r(a6,62,62,c4),\
++ r(a8,91,91,39), r(a4,95,95,31), r(37,e4,e4,d3), r(8b,79,79,f2),\
++ r(32,e7,e7,d5), r(43,c8,c8,8b), r(59,37,37,6e), r(b7,6d,6d,da),\
++ r(8c,8d,8d,01), r(64,d5,d5,b1), r(d2,4e,4e,9c), r(e0,a9,a9,49),\
++ r(b4,6c,6c,d8), r(fa,56,56,ac), r(07,f4,f4,f3), r(25,ea,ea,cf),\
++ r(af,65,65,ca), r(8e,7a,7a,f4), r(e9,ae,ae,47), r(18,08,08,10),\
++ r(d5,ba,ba,6f), r(88,78,78,f0), r(6f,25,25,4a), r(72,2e,2e,5c),\
++ r(24,1c,1c,38), r(f1,a6,a6,57), r(c7,b4,b4,73), r(51,c6,c6,97),\
++ r(23,e8,e8,cb), r(7c,dd,dd,a1), r(9c,74,74,e8), r(21,1f,1f,3e),\
++ r(dd,4b,4b,96), r(dc,bd,bd,61), r(86,8b,8b,0d), r(85,8a,8a,0f),\
++ r(90,70,70,e0), r(42,3e,3e,7c), r(c4,b5,b5,71), r(aa,66,66,cc),\
++ r(d8,48,48,90), r(05,03,03,06), r(01,f6,f6,f7), r(12,0e,0e,1c),\
++ r(a3,61,61,c2), r(5f,35,35,6a), r(f9,57,57,ae), r(d0,b9,b9,69),\
++ r(91,86,86,17), r(58,c1,c1,99), r(27,1d,1d,3a), r(b9,9e,9e,27),\
++ r(38,e1,e1,d9), r(13,f8,f8,eb), r(b3,98,98,2b), r(33,11,11,22),\
++ r(bb,69,69,d2), r(70,d9,d9,a9), r(89,8e,8e,07), r(a7,94,94,33),\
++ r(b6,9b,9b,2d), r(22,1e,1e,3c), r(92,87,87,15), r(20,e9,e9,c9),\
++ r(49,ce,ce,87), r(ff,55,55,aa), r(78,28,28,50), r(7a,df,df,a5),\
++ r(8f,8c,8c,03), r(f8,a1,a1,59), r(80,89,89,09), r(17,0d,0d,1a),\
++ r(da,bf,bf,65), r(31,e6,e6,d7), r(c6,42,42,84), r(b8,68,68,d0),\
++ r(c3,41,41,82), r(b0,99,99,29), r(77,2d,2d,5a), r(11,0f,0f,1e),\
++ r(cb,b0,b0,7b), r(fc,54,54,a8), r(d6,bb,bb,6d), r(3a,16,16,2c)
++
++// data for inverse tables (other than last round)
++
++#define i_table \
++ r(50,a7,f4,51), r(53,65,41,7e), r(c3,a4,17,1a), r(96,5e,27,3a),\
++ r(cb,6b,ab,3b), r(f1,45,9d,1f), r(ab,58,fa,ac), r(93,03,e3,4b),\
++ r(55,fa,30,20), r(f6,6d,76,ad), r(91,76,cc,88), r(25,4c,02,f5),\
++ r(fc,d7,e5,4f), r(d7,cb,2a,c5), r(80,44,35,26), r(8f,a3,62,b5),\
++ r(49,5a,b1,de), r(67,1b,ba,25), r(98,0e,ea,45), r(e1,c0,fe,5d),\
++ r(02,75,2f,c3), r(12,f0,4c,81), r(a3,97,46,8d), r(c6,f9,d3,6b),\
++ r(e7,5f,8f,03), r(95,9c,92,15), r(eb,7a,6d,bf), r(da,59,52,95),\
++ r(2d,83,be,d4), r(d3,21,74,58), r(29,69,e0,49), r(44,c8,c9,8e),\
++ r(6a,89,c2,75), r(78,79,8e,f4), r(6b,3e,58,99), r(dd,71,b9,27),\
++ r(b6,4f,e1,be), r(17,ad,88,f0), r(66,ac,20,c9), r(b4,3a,ce,7d),\
++ r(18,4a,df,63), r(82,31,1a,e5), r(60,33,51,97), r(45,7f,53,62),\
++ r(e0,77,64,b1), r(84,ae,6b,bb), r(1c,a0,81,fe), r(94,2b,08,f9),\
++ r(58,68,48,70), r(19,fd,45,8f), r(87,6c,de,94), r(b7,f8,7b,52),\
++ r(23,d3,73,ab), r(e2,02,4b,72), r(57,8f,1f,e3), r(2a,ab,55,66),\
++ r(07,28,eb,b2), r(03,c2,b5,2f), r(9a,7b,c5,86), r(a5,08,37,d3),\
++ r(f2,87,28,30), r(b2,a5,bf,23), r(ba,6a,03,02), r(5c,82,16,ed),\
++ r(2b,1c,cf,8a), r(92,b4,79,a7), r(f0,f2,07,f3), r(a1,e2,69,4e),\
++ r(cd,f4,da,65), r(d5,be,05,06), r(1f,62,34,d1), r(8a,fe,a6,c4),\
++ r(9d,53,2e,34), r(a0,55,f3,a2), r(32,e1,8a,05), r(75,eb,f6,a4),\
++ r(39,ec,83,0b), r(aa,ef,60,40), r(06,9f,71,5e), r(51,10,6e,bd),\
++ r(f9,8a,21,3e), r(3d,06,dd,96), r(ae,05,3e,dd), r(46,bd,e6,4d),\
++ r(b5,8d,54,91), r(05,5d,c4,71), r(6f,d4,06,04), r(ff,15,50,60),\
++ r(24,fb,98,19), r(97,e9,bd,d6), r(cc,43,40,89), r(77,9e,d9,67),\
++ r(bd,42,e8,b0), r(88,8b,89,07), r(38,5b,19,e7), r(db,ee,c8,79),\
++ r(47,0a,7c,a1), r(e9,0f,42,7c), r(c9,1e,84,f8), r(00,00,00,00),\
++ r(83,86,80,09), r(48,ed,2b,32), r(ac,70,11,1e), r(4e,72,5a,6c),\
++ r(fb,ff,0e,fd), r(56,38,85,0f), r(1e,d5,ae,3d), r(27,39,2d,36),\
++ r(64,d9,0f,0a), r(21,a6,5c,68), r(d1,54,5b,9b), r(3a,2e,36,24),\
++ r(b1,67,0a,0c), r(0f,e7,57,93), r(d2,96,ee,b4), r(9e,91,9b,1b),\
++ r(4f,c5,c0,80), r(a2,20,dc,61), r(69,4b,77,5a), r(16,1a,12,1c),\
++ r(0a,ba,93,e2), r(e5,2a,a0,c0), r(43,e0,22,3c), r(1d,17,1b,12),\
++ r(0b,0d,09,0e), r(ad,c7,8b,f2), r(b9,a8,b6,2d), r(c8,a9,1e,14),\
++ r(85,19,f1,57), r(4c,07,75,af), r(bb,dd,99,ee), r(fd,60,7f,a3),\
++ r(9f,26,01,f7), r(bc,f5,72,5c), r(c5,3b,66,44), r(34,7e,fb,5b),\
++ r(76,29,43,8b), r(dc,c6,23,cb), r(68,fc,ed,b6), r(63,f1,e4,b8),\
++ r(ca,dc,31,d7), r(10,85,63,42), r(40,22,97,13), r(20,11,c6,84),\
++ r(7d,24,4a,85), r(f8,3d,bb,d2), r(11,32,f9,ae), r(6d,a1,29,c7),\
++ r(4b,2f,9e,1d), r(f3,30,b2,dc), r(ec,52,86,0d), r(d0,e3,c1,77),\
++ r(6c,16,b3,2b), r(99,b9,70,a9), r(fa,48,94,11), r(22,64,e9,47),\
++ r(c4,8c,fc,a8), r(1a,3f,f0,a0), r(d8,2c,7d,56), r(ef,90,33,22),\
++ r(c7,4e,49,87), r(c1,d1,38,d9), r(fe,a2,ca,8c), r(36,0b,d4,98),\
++ r(cf,81,f5,a6), r(28,de,7a,a5), r(26,8e,b7,da), r(a4,bf,ad,3f),\
++ r(e4,9d,3a,2c), r(0d,92,78,50), r(9b,cc,5f,6a), r(62,46,7e,54),\
++ r(c2,13,8d,f6), r(e8,b8,d8,90), r(5e,f7,39,2e), r(f5,af,c3,82),\
++ r(be,80,5d,9f), r(7c,93,d0,69), r(a9,2d,d5,6f), r(b3,12,25,cf),\
++ r(3b,99,ac,c8), r(a7,7d,18,10), r(6e,63,9c,e8), r(7b,bb,3b,db),\
++ r(09,78,26,cd), r(f4,18,59,6e), r(01,b7,9a,ec), r(a8,9a,4f,83),\
++ r(65,6e,95,e6), r(7e,e6,ff,aa), r(08,cf,bc,21), r(e6,e8,15,ef),\
++ r(d9,9b,e7,ba), r(ce,36,6f,4a), r(d4,09,9f,ea), r(d6,7c,b0,29),\
++ r(af,b2,a4,31), r(31,23,3f,2a), r(30,94,a5,c6), r(c0,66,a2,35),\
++ r(37,bc,4e,74), r(a6,ca,82,fc), r(b0,d0,90,e0), r(15,d8,a7,33),\
++ r(4a,98,04,f1), r(f7,da,ec,41), r(0e,50,cd,7f), r(2f,f6,91,17),\
++ r(8d,d6,4d,76), r(4d,b0,ef,43), r(54,4d,aa,cc), r(df,04,96,e4),\
++ r(e3,b5,d1,9e), r(1b,88,6a,4c), r(b8,1f,2c,c1), r(7f,51,65,46),\
++ r(04,ea,5e,9d), r(5d,35,8c,01), r(73,74,87,fa), r(2e,41,0b,fb),\
++ r(5a,1d,67,b3), r(52,d2,db,92), r(33,56,10,e9), r(13,47,d6,6d),\
++ r(8c,61,d7,9a), r(7a,0c,a1,37), r(8e,14,f8,59), r(89,3c,13,eb),\
++ r(ee,27,a9,ce), r(35,c9,61,b7), r(ed,e5,1c,e1), r(3c,b1,47,7a),\
++ r(59,df,d2,9c), r(3f,73,f2,55), r(79,ce,14,18), r(bf,37,c7,73),\
++ r(ea,cd,f7,53), r(5b,aa,fd,5f), r(14,6f,3d,df), r(86,db,44,78),\
++ r(81,f3,af,ca), r(3e,c4,68,b9), r(2c,34,24,38), r(5f,40,a3,c2),\
++ r(72,c3,1d,16), r(0c,25,e2,bc), r(8b,49,3c,28), r(41,95,0d,ff),\
++ r(71,01,a8,39), r(de,b3,0c,08), r(9c,e4,b4,d8), r(90,c1,56,64),\
++ r(61,84,cb,7b), r(70,b6,32,d5), r(74,5c,6c,48), r(42,57,b8,d0)
++
++// generate the required tables in the desired endian format
++
++#undef r
++#define r r0
++
++#if defined(ONE_TABLE)
++static const u_int32_t ft_tab[256] =
++ { f_table };
++#elif defined(FOUR_TABLES)
++static const u_int32_t ft_tab[4][256] =
++{ { f_table },
++#undef r
++#define r r1
++ { f_table },
++#undef r
++#define r r2
++ { f_table },
++#undef r
++#define r r3
++ { f_table }
++};
++#endif
++
++#undef r
++#define r r0
++#if defined(ONE_TABLE)
++static const u_int32_t it_tab[256] =
++ { i_table };
++#elif defined(FOUR_TABLES)
++static const u_int32_t it_tab[4][256] =
++{ { i_table },
++#undef r
++#define r r1
++ { i_table },
++#undef r
++#define r r2
++ { i_table },
++#undef r
++#define r r3
++ { i_table }
++};
++#endif
++
++#endif
++
++#if defined(FIXED_TABLES) && (defined(ONE_LR_TABLE) || defined(FOUR_LR_TABLES))
++
++// data for inverse tables (last round)
++
++#define li_table \
++ w(52), w(09), w(6a), w(d5), w(30), w(36), w(a5), w(38),\
++ w(bf), w(40), w(a3), w(9e), w(81), w(f3), w(d7), w(fb),\
++ w(7c), w(e3), w(39), w(82), w(9b), w(2f), w(ff), w(87),\
++ w(34), w(8e), w(43), w(44), w(c4), w(de), w(e9), w(cb),\
++ w(54), w(7b), w(94), w(32), w(a6), w(c2), w(23), w(3d),\
++ w(ee), w(4c), w(95), w(0b), w(42), w(fa), w(c3), w(4e),\
++ w(08), w(2e), w(a1), w(66), w(28), w(d9), w(24), w(b2),\
++ w(76), w(5b), w(a2), w(49), w(6d), w(8b), w(d1), w(25),\
++ w(72), w(f8), w(f6), w(64), w(86), w(68), w(98), w(16),\
++ w(d4), w(a4), w(5c), w(cc), w(5d), w(65), w(b6), w(92),\
++ w(6c), w(70), w(48), w(50), w(fd), w(ed), w(b9), w(da),\
++ w(5e), w(15), w(46), w(57), w(a7), w(8d), w(9d), w(84),\
++ w(90), w(d8), w(ab), w(00), w(8c), w(bc), w(d3), w(0a),\
++ w(f7), w(e4), w(58), w(05), w(b8), w(b3), w(45), w(06),\
++ w(d0), w(2c), w(1e), w(8f), w(ca), w(3f), w(0f), w(02),\
++ w(c1), w(af), w(bd), w(03), w(01), w(13), w(8a), w(6b),\
++ w(3a), w(91), w(11), w(41), w(4f), w(67), w(dc), w(ea),\
++ w(97), w(f2), w(cf), w(ce), w(f0), w(b4), w(e6), w(73),\
++ w(96), w(ac), w(74), w(22), w(e7), w(ad), w(35), w(85),\
++ w(e2), w(f9), w(37), w(e8), w(1c), w(75), w(df), w(6e),\
++ w(47), w(f1), w(1a), w(71), w(1d), w(29), w(c5), w(89),\
++ w(6f), w(b7), w(62), w(0e), w(aa), w(18), w(be), w(1b),\
++ w(fc), w(56), w(3e), w(4b), w(c6), w(d2), w(79), w(20),\
++ w(9a), w(db), w(c0), w(fe), w(78), w(cd), w(5a), w(f4),\
++ w(1f), w(dd), w(a8), w(33), w(88), w(07), w(c7), w(31),\
++ w(b1), w(12), w(10), w(59), w(27), w(80), w(ec), w(5f),\
++ w(60), w(51), w(7f), w(a9), w(19), w(b5), w(4a), w(0d),\
++ w(2d), w(e5), w(7a), w(9f), w(93), w(c9), w(9c), w(ef),\
++ w(a0), w(e0), w(3b), w(4d), w(ae), w(2a), w(f5), w(b0),\
++ w(c8), w(eb), w(bb), w(3c), w(83), w(53), w(99), w(61),\
++ w(17), w(2b), w(04), w(7e), w(ba), w(77), w(d6), w(26),\
++ w(e1), w(69), w(14), w(63), w(55), w(21), w(0c), w(7d),
++
++// generate the required tables in the desired endian format
++
++#undef r
++#define r(p,q,r,s) w0(q)
++#if defined(ONE_LR_TABLE)
++static const u_int32_t fl_tab[256] =
++ { f_table };
++#elif defined(FOUR_LR_TABLES)
++static const u_int32_t fl_tab[4][256] =
++{ { f_table },
++#undef r
++#define r(p,q,r,s) w1(q)
++ { f_table },
++#undef r
++#define r(p,q,r,s) w2(q)
++ { f_table },
++#undef r
++#define r(p,q,r,s) w3(q)
++ { f_table }
++};
++#endif
++
++#undef w
++#define w w0
++#if defined(ONE_LR_TABLE)
++static const u_int32_t il_tab[256] =
++ { li_table };
++#elif defined(FOUR_LR_TABLES)
++static const u_int32_t il_tab[4][256] =
++{ { li_table },
++#undef w
++#define w w1
++ { li_table },
++#undef w
++#define w w2
++ { li_table },
++#undef w
++#define w w3
++ { li_table }
++};
++#endif
++
++#endif
++
++#if defined(FIXED_TABLES) && (defined(ONE_IM_TABLE) || defined(FOUR_IM_TABLES))
++
++#define m_table \
++ r(00,00,00,00), r(0b,0d,09,0e), r(16,1a,12,1c), r(1d,17,1b,12),\
++ r(2c,34,24,38), r(27,39,2d,36), r(3a,2e,36,24), r(31,23,3f,2a),\
++ r(58,68,48,70), r(53,65,41,7e), r(4e,72,5a,6c), r(45,7f,53,62),\
++ r(74,5c,6c,48), r(7f,51,65,46), r(62,46,7e,54), r(69,4b,77,5a),\
++ r(b0,d0,90,e0), r(bb,dd,99,ee), r(a6,ca,82,fc), r(ad,c7,8b,f2),\
++ r(9c,e4,b4,d8), r(97,e9,bd,d6), r(8a,fe,a6,c4), r(81,f3,af,ca),\
++ r(e8,b8,d8,90), r(e3,b5,d1,9e), r(fe,a2,ca,8c), r(f5,af,c3,82),\
++ r(c4,8c,fc,a8), r(cf,81,f5,a6), r(d2,96,ee,b4), r(d9,9b,e7,ba),\
++ r(7b,bb,3b,db), r(70,b6,32,d5), r(6d,a1,29,c7), r(66,ac,20,c9),\
++ r(57,8f,1f,e3), r(5c,82,16,ed), r(41,95,0d,ff), r(4a,98,04,f1),\
++ r(23,d3,73,ab), r(28,de,7a,a5), r(35,c9,61,b7), r(3e,c4,68,b9),\
++ r(0f,e7,57,93), r(04,ea,5e,9d), r(19,fd,45,8f), r(12,f0,4c,81),\
++ r(cb,6b,ab,3b), r(c0,66,a2,35), r(dd,71,b9,27), r(d6,7c,b0,29),\
++ r(e7,5f,8f,03), r(ec,52,86,0d), r(f1,45,9d,1f), r(fa,48,94,11),\
++ r(93,03,e3,4b), r(98,0e,ea,45), r(85,19,f1,57), r(8e,14,f8,59),\
++ r(bf,37,c7,73), r(b4,3a,ce,7d), r(a9,2d,d5,6f), r(a2,20,dc,61),\
++ r(f6,6d,76,ad), r(fd,60,7f,a3), r(e0,77,64,b1), r(eb,7a,6d,bf),\
++ r(da,59,52,95), r(d1,54,5b,9b), r(cc,43,40,89), r(c7,4e,49,87),\
++ r(ae,05,3e,dd), r(a5,08,37,d3), r(b8,1f,2c,c1), r(b3,12,25,cf),\
++ r(82,31,1a,e5), r(89,3c,13,eb), r(94,2b,08,f9), r(9f,26,01,f7),\
++ r(46,bd,e6,4d), r(4d,b0,ef,43), r(50,a7,f4,51), r(5b,aa,fd,5f),\
++ r(6a,89,c2,75), r(61,84,cb,7b), r(7c,93,d0,69), r(77,9e,d9,67),\
++ r(1e,d5,ae,3d), r(15,d8,a7,33), r(08,cf,bc,21), r(03,c2,b5,2f),\
++ r(32,e1,8a,05), r(39,ec,83,0b), r(24,fb,98,19), r(2f,f6,91,17),\
++ r(8d,d6,4d,76), r(86,db,44,78), r(9b,cc,5f,6a), r(90,c1,56,64),\
++ r(a1,e2,69,4e), r(aa,ef,60,40), r(b7,f8,7b,52), r(bc,f5,72,5c),\
++ r(d5,be,05,06), r(de,b3,0c,08), r(c3,a4,17,1a), r(c8,a9,1e,14),\
++ r(f9,8a,21,3e), r(f2,87,28,30), r(ef,90,33,22), r(e4,9d,3a,2c),\
++ r(3d,06,dd,96), r(36,0b,d4,98), r(2b,1c,cf,8a), r(20,11,c6,84),\
++ r(11,32,f9,ae), r(1a,3f,f0,a0), r(07,28,eb,b2), r(0c,25,e2,bc),\
++ r(65,6e,95,e6), r(6e,63,9c,e8), r(73,74,87,fa), r(78,79,8e,f4),\
++ r(49,5a,b1,de), r(42,57,b8,d0), r(5f,40,a3,c2), r(54,4d,aa,cc),\
++ r(f7,da,ec,41), r(fc,d7,e5,4f), r(e1,c0,fe,5d), r(ea,cd,f7,53),\
++ r(db,ee,c8,79), r(d0,e3,c1,77), r(cd,f4,da,65), r(c6,f9,d3,6b),\
++ r(af,b2,a4,31), r(a4,bf,ad,3f), r(b9,a8,b6,2d), r(b2,a5,bf,23),\
++ r(83,86,80,09), r(88,8b,89,07), r(95,9c,92,15), r(9e,91,9b,1b),\
++ r(47,0a,7c,a1), r(4c,07,75,af), r(51,10,6e,bd), r(5a,1d,67,b3),\
++ r(6b,3e,58,99), r(60,33,51,97), r(7d,24,4a,85), r(76,29,43,8b),\
++ r(1f,62,34,d1), r(14,6f,3d,df), r(09,78,26,cd), r(02,75,2f,c3),\
++ r(33,56,10,e9), r(38,5b,19,e7), r(25,4c,02,f5), r(2e,41,0b,fb),\
++ r(8c,61,d7,9a), r(87,6c,de,94), r(9a,7b,c5,86), r(91,76,cc,88),\
++ r(a0,55,f3,a2), r(ab,58,fa,ac), r(b6,4f,e1,be), r(bd,42,e8,b0),\
++ r(d4,09,9f,ea), r(df,04,96,e4), r(c2,13,8d,f6), r(c9,1e,84,f8),\
++ r(f8,3d,bb,d2), r(f3,30,b2,dc), r(ee,27,a9,ce), r(e5,2a,a0,c0),\
++ r(3c,b1,47,7a), r(37,bc,4e,74), r(2a,ab,55,66), r(21,a6,5c,68),\
++ r(10,85,63,42), r(1b,88,6a,4c), r(06,9f,71,5e), r(0d,92,78,50),\
++ r(64,d9,0f,0a), r(6f,d4,06,04), r(72,c3,1d,16), r(79,ce,14,18),\
++ r(48,ed,2b,32), r(43,e0,22,3c), r(5e,f7,39,2e), r(55,fa,30,20),\
++ r(01,b7,9a,ec), r(0a,ba,93,e2), r(17,ad,88,f0), r(1c,a0,81,fe),\
++ r(2d,83,be,d4), r(26,8e,b7,da), r(3b,99,ac,c8), r(30,94,a5,c6),\
++ r(59,df,d2,9c), r(52,d2,db,92), r(4f,c5,c0,80), r(44,c8,c9,8e),\
++ r(75,eb,f6,a4), r(7e,e6,ff,aa), r(63,f1,e4,b8), r(68,fc,ed,b6),\
++ r(b1,67,0a,0c), r(ba,6a,03,02), r(a7,7d,18,10), r(ac,70,11,1e),\
++ r(9d,53,2e,34), r(96,5e,27,3a), r(8b,49,3c,28), r(80,44,35,26),\
++ r(e9,0f,42,7c), r(e2,02,4b,72), r(ff,15,50,60), r(f4,18,59,6e),\
++ r(c5,3b,66,44), r(ce,36,6f,4a), r(d3,21,74,58), r(d8,2c,7d,56),\
++ r(7a,0c,a1,37), r(71,01,a8,39), r(6c,16,b3,2b), r(67,1b,ba,25),\
++ r(56,38,85,0f), r(5d,35,8c,01), r(40,22,97,13), r(4b,2f,9e,1d),\
++ r(22,64,e9,47), r(29,69,e0,49), r(34,7e,fb,5b), r(3f,73,f2,55),\
++ r(0e,50,cd,7f), r(05,5d,c4,71), r(18,4a,df,63), r(13,47,d6,6d),\
++ r(ca,dc,31,d7), r(c1,d1,38,d9), r(dc,c6,23,cb), r(d7,cb,2a,c5),\
++ r(e6,e8,15,ef), r(ed,e5,1c,e1), r(f0,f2,07,f3), r(fb,ff,0e,fd),\
++ r(92,b4,79,a7), r(99,b9,70,a9), r(84,ae,6b,bb), r(8f,a3,62,b5),\
++ r(be,80,5d,9f), r(b5,8d,54,91), r(a8,9a,4f,83), r(a3,97,46,8d)
++
++#undef r
++#define r r0
++
++#if defined(ONE_IM_TABLE)
++static const u_int32_t im_tab[256] =
++ { m_table };
++#elif defined(FOUR_IM_TABLES)
++static const u_int32_t im_tab[4][256] =
++{ { m_table },
++#undef r
++#define r r1
++ { m_table },
++#undef r
++#define r r2
++ { m_table },
++#undef r
++#define r r3
++ { m_table }
++};
++#endif
++
++#endif
++
++#else
++
++static int tab_gen = 0;
++
++static unsigned char s_box[256]; // the S box
++static unsigned char inv_s_box[256]; // the inverse S box
++static u_int32_t rcon_tab[AES_RC_LENGTH]; // table of round constants
++
++#if defined(ONE_TABLE)
++static u_int32_t ft_tab[256];
++static u_int32_t it_tab[256];
++#elif defined(FOUR_TABLES)
++static u_int32_t ft_tab[4][256];
++static u_int32_t it_tab[4][256];
++#endif
++
++#if defined(ONE_LR_TABLE)
++static u_int32_t fl_tab[256];
++static u_int32_t il_tab[256];
++#elif defined(FOUR_LR_TABLES)
++static u_int32_t fl_tab[4][256];
++static u_int32_t il_tab[4][256];
++#endif
++
++#if defined(ONE_IM_TABLE)
++static u_int32_t im_tab[256];
++#elif defined(FOUR_IM_TABLES)
++static u_int32_t im_tab[4][256];
++#endif
++
++// Generate the tables for the dynamic table option
++
++#if !defined(FF_TABLES)
++
++// It will generally be sensible to use tables to compute finite
++// field multiplies and inverses but where memory is scarse this
++// code might sometimes be better.
++
++// return 2 ^ (n - 1) where n is the bit number of the highest bit
++// set in x with x in the range 1 < x < 0x00000200. This form is
++// used so that locals within FFinv can be bytes rather than words
++
++static unsigned char hibit(const u_int32_t x)
++{ unsigned char r = (unsigned char)((x >> 1) | (x >> 2));
++
++ r |= (r >> 2);
++ r |= (r >> 4);
++ return (r + 1) >> 1;
++}
++
++// return the inverse of the finite field element x
++
++static unsigned char FFinv(const unsigned char x)
++{ unsigned char p1 = x, p2 = 0x1b, n1 = hibit(x), n2 = 0x80, v1 = 1, v2 = 0;
++
++ if(x < 2) return x;
++
++ for(;;)
++ {
++ if(!n1) return v1;
++
++ while(n2 >= n1)
++ {
++ n2 /= n1; p2 ^= p1 * n2; v2 ^= v1 * n2; n2 = hibit(p2);
++ }
++
++ if(!n2) return v2;
++
++ while(n1 >= n2)
++ {
++ n1 /= n2; p1 ^= p2 * n1; v1 ^= v2 * n1; n1 = hibit(p1);
++ }
++ }
++}
++
++// define the finite field multiplies required for Rijndael
++
++#define FFmul02(x) ((((x) & 0x7f) << 1) ^ ((x) & 0x80 ? 0x1b : 0))
++#define FFmul03(x) ((x) ^ FFmul02(x))
++#define FFmul09(x) ((x) ^ FFmul02(FFmul02(FFmul02(x))))
++#define FFmul0b(x) ((x) ^ FFmul02((x) ^ FFmul02(FFmul02(x))))
++#define FFmul0d(x) ((x) ^ FFmul02(FFmul02((x) ^ FFmul02(x))))
++#define FFmul0e(x) FFmul02((x) ^ FFmul02((x) ^ FFmul02(x)))
++
++#else
++
++#define FFinv(x) ((x) ? pow[255 - log[x]]: 0)
++
++#define FFmul02(x) (x ? pow[log[x] + 0x19] : 0)
++#define FFmul03(x) (x ? pow[log[x] + 0x01] : 0)
++#define FFmul09(x) (x ? pow[log[x] + 0xc7] : 0)
++#define FFmul0b(x) (x ? pow[log[x] + 0x68] : 0)
++#define FFmul0d(x) (x ? pow[log[x] + 0xee] : 0)
++#define FFmul0e(x) (x ? pow[log[x] + 0xdf] : 0)
++
++#endif
++
++// The forward and inverse affine transformations used in the S-box
++
++#define fwd_affine(x) \
++ (w = (u_int32_t)x, w ^= (w<<1)^(w<<2)^(w<<3)^(w<<4), 0x63^(unsigned char)(w^(w>>8)))
++
++#define inv_affine(x) \
++ (w = (u_int32_t)x, w = (w<<1)^(w<<3)^(w<<6), 0x05^(unsigned char)(w^(w>>8)))
++
++static void gen_tabs(void)
++{ u_int32_t i, w;
++
++#if defined(FF_TABLES)
++
++ unsigned char pow[512], log[256];
++
++ // log and power tables for GF(2^8) finite field with
++ // 0x011b as modular polynomial - the simplest primitive
++ // root is 0x03, used here to generate the tables
++
++ i = 0; w = 1;
++ do
++ {
++ pow[i] = (unsigned char)w;
++ pow[i + 255] = (unsigned char)w;
++ log[w] = (unsigned char)i++;
++ w ^= (w << 1) ^ (w & ff_hi ? ff_poly : 0);
++ }
++ while (w != 1);
++
++#endif
++
++ for(i = 0, w = 1; i < AES_RC_LENGTH; ++i)
++ {
++ rcon_tab[i] = bytes2word(w, 0, 0, 0);
++ w = (w << 1) ^ (w & ff_hi ? ff_poly : 0);
++ }
++
++ for(i = 0; i < 256; ++i)
++ { unsigned char b;
++
++ s_box[i] = b = fwd_affine(FFinv((unsigned char)i));
++
++ w = bytes2word(b, 0, 0, 0);
++#if defined(ONE_LR_TABLE)
++ fl_tab[i] = w;
++#elif defined(FOUR_LR_TABLES)
++ fl_tab[0][i] = w;
++ fl_tab[1][i] = upr(w,1);
++ fl_tab[2][i] = upr(w,2);
++ fl_tab[3][i] = upr(w,3);
++#endif
++ w = bytes2word(FFmul02(b), b, b, FFmul03(b));
++#if defined(ONE_TABLE)
++ ft_tab[i] = w;
++#elif defined(FOUR_TABLES)
++ ft_tab[0][i] = w;
++ ft_tab[1][i] = upr(w,1);
++ ft_tab[2][i] = upr(w,2);
++ ft_tab[3][i] = upr(w,3);
++#endif
++ inv_s_box[i] = b = FFinv(inv_affine((unsigned char)i));
++
++ w = bytes2word(b, 0, 0, 0);
++#if defined(ONE_LR_TABLE)
++ il_tab[i] = w;
++#elif defined(FOUR_LR_TABLES)
++ il_tab[0][i] = w;
++ il_tab[1][i] = upr(w,1);
++ il_tab[2][i] = upr(w,2);
++ il_tab[3][i] = upr(w,3);
++#endif
++ w = bytes2word(FFmul0e(b), FFmul09(b), FFmul0d(b), FFmul0b(b));
++#if defined(ONE_TABLE)
++ it_tab[i] = w;
++#elif defined(FOUR_TABLES)
++ it_tab[0][i] = w;
++ it_tab[1][i] = upr(w,1);
++ it_tab[2][i] = upr(w,2);
++ it_tab[3][i] = upr(w,3);
++#endif
++#if defined(ONE_IM_TABLE)
++ im_tab[b] = w;
++#elif defined(FOUR_IM_TABLES)
++ im_tab[0][b] = w;
++ im_tab[1][b] = upr(w,1);
++ im_tab[2][b] = upr(w,2);
++ im_tab[3][b] = upr(w,3);
++#endif
++
++ }
++}
++
++#endif
++
++#define no_table(x,box,vf,rf,c) bytes2word( \
++ box[bval(vf(x,0,c),rf(0,c))], \
++ box[bval(vf(x,1,c),rf(1,c))], \
++ box[bval(vf(x,2,c),rf(2,c))], \
++ box[bval(vf(x,3,c),rf(3,c))])
++
++#define one_table(x,op,tab,vf,rf,c) \
++ ( tab[bval(vf(x,0,c),rf(0,c))] \
++ ^ op(tab[bval(vf(x,1,c),rf(1,c))],1) \
++ ^ op(tab[bval(vf(x,2,c),rf(2,c))],2) \
++ ^ op(tab[bval(vf(x,3,c),rf(3,c))],3))
++
++#define four_tables(x,tab,vf,rf,c) \
++ ( tab[0][bval(vf(x,0,c),rf(0,c))] \
++ ^ tab[1][bval(vf(x,1,c),rf(1,c))] \
++ ^ tab[2][bval(vf(x,2,c),rf(2,c))] \
++ ^ tab[3][bval(vf(x,3,c),rf(3,c))])
++
++#define vf1(x,r,c) (x)
++#define rf1(r,c) (r)
++#define rf2(r,c) ((r-c)&3)
++
++#if defined(FOUR_LR_TABLES)
++#define ls_box(x,c) four_tables(x,fl_tab,vf1,rf2,c)
++#elif defined(ONE_LR_TABLE)
++#define ls_box(x,c) one_table(x,upr,fl_tab,vf1,rf2,c)
++#else
++#define ls_box(x,c) no_table(x,s_box,vf1,rf2,c)
++#endif
++
++#if defined(FOUR_IM_TABLES)
++#define inv_mcol(x) four_tables(x,im_tab,vf1,rf1,0)
++#elif defined(ONE_IM_TABLE)
++#define inv_mcol(x) one_table(x,upr,im_tab,vf1,rf1,0)
++#else
++#define inv_mcol(x) \
++ (f9 = (x),f2 = FFmulX(f9), f4 = FFmulX(f2), f8 = FFmulX(f4), f9 ^= f8, \
++ f2 ^= f4 ^ f8 ^ upr(f2 ^ f9,3) ^ upr(f4 ^ f9,2) ^ upr(f9,1))
++#endif
++
++// Subroutine to set the block size (if variable) in bytes, legal
++// values being 16, 24 and 32.
++
++#if defined(AES_BLOCK_SIZE)
++#define nc (AES_BLOCK_SIZE / 4)
++#else
++#define nc (cx->aes_Ncol)
++
++void aes_set_blk(aes_context *cx, int n_bytes)
++{
++#if !defined(FIXED_TABLES)
++ if(!tab_gen) { gen_tabs(); tab_gen = 1; }
++#endif
++
++ switch(n_bytes) {
++ case 32: /* bytes */
++ case 256: /* bits */
++ nc = 8;
++ break;
++ case 24: /* bytes */
++ case 192: /* bits */
++ nc = 6;
++ break;
++ case 16: /* bytes */
++ case 128: /* bits */
++ default:
++ nc = 4;
++ break;
++ }
++}
++
++#endif
++
++// Initialise the key schedule from the user supplied key. The key
++// length is now specified in bytes - 16, 24 or 32 as appropriate.
++// This corresponds to bit lengths of 128, 192 and 256 bits, and
++// to Nk values of 4, 6 and 8 respectively.
++
++#define mx(t,f) (*t++ = inv_mcol(*f),f++)
++#define cp(t,f) *t++ = *f++
++
++#if AES_BLOCK_SIZE == 16
++#define cpy(d,s) cp(d,s); cp(d,s); cp(d,s); cp(d,s)
++#define mix(d,s) mx(d,s); mx(d,s); mx(d,s); mx(d,s)
++#elif AES_BLOCK_SIZE == 24
++#define cpy(d,s) cp(d,s); cp(d,s); cp(d,s); cp(d,s); \
++ cp(d,s); cp(d,s)
++#define mix(d,s) mx(d,s); mx(d,s); mx(d,s); mx(d,s); \
++ mx(d,s); mx(d,s)
++#elif AES_BLOCK_SIZE == 32
++#define cpy(d,s) cp(d,s); cp(d,s); cp(d,s); cp(d,s); \
++ cp(d,s); cp(d,s); cp(d,s); cp(d,s)
++#define mix(d,s) mx(d,s); mx(d,s); mx(d,s); mx(d,s); \
++ mx(d,s); mx(d,s); mx(d,s); mx(d,s)
++#else
++
++#define cpy(d,s) \
++switch(nc) \
++{ case 8: cp(d,s); cp(d,s); \
++ case 6: cp(d,s); cp(d,s); \
++ case 4: cp(d,s); cp(d,s); \
++ cp(d,s); cp(d,s); \
++}
++
++#define mix(d,s) \
++switch(nc) \
++{ case 8: mx(d,s); mx(d,s); \
++ case 6: mx(d,s); mx(d,s); \
++ case 4: mx(d,s); mx(d,s); \
++ mx(d,s); mx(d,s); \
++}
++
++#endif
++
++void aes_set_key(aes_context *cx, const unsigned char in_key[], int n_bytes, const int f)
++{ u_int32_t *kf, *kt, rci;
++
++#if !defined(FIXED_TABLES)
++ if(!tab_gen) { gen_tabs(); tab_gen = 1; }
++#endif
++
++ switch(n_bytes) {
++ case 32: /* bytes */
++ case 256: /* bits */
++ cx->aes_Nkey = 8;
++ break;
++ case 24: /* bytes */
++ case 192: /* bits */
++ cx->aes_Nkey = 6;
++ break;
++ case 16: /* bytes */
++ case 128: /* bits */
++ default:
++ cx->aes_Nkey = 4;
++ break;
++ }
++
++ cx->aes_Nrnd = (cx->aes_Nkey > nc ? cx->aes_Nkey : nc) + 6;
++
++ cx->aes_e_key[0] = const_word_in(in_key );
++ cx->aes_e_key[1] = const_word_in(in_key + 4);
++ cx->aes_e_key[2] = const_word_in(in_key + 8);
++ cx->aes_e_key[3] = const_word_in(in_key + 12);
++
++ kf = cx->aes_e_key;
++ kt = kf + nc * (cx->aes_Nrnd + 1) - cx->aes_Nkey;
++ rci = 0;
++
++ switch(cx->aes_Nkey)
++ {
++ case 4: do
++ { kf[4] = kf[0] ^ ls_box(kf[3],3) ^ rcon_tab[rci++];
++ kf[5] = kf[1] ^ kf[4];
++ kf[6] = kf[2] ^ kf[5];
++ kf[7] = kf[3] ^ kf[6];
++ kf += 4;
++ }
++ while(kf < kt);
++ break;
++
++ case 6: cx->aes_e_key[4] = const_word_in(in_key + 16);
++ cx->aes_e_key[5] = const_word_in(in_key + 20);
++ do
++ { kf[ 6] = kf[0] ^ ls_box(kf[5],3) ^ rcon_tab[rci++];
++ kf[ 7] = kf[1] ^ kf[ 6];
++ kf[ 8] = kf[2] ^ kf[ 7];
++ kf[ 9] = kf[3] ^ kf[ 8];
++ kf[10] = kf[4] ^ kf[ 9];
++ kf[11] = kf[5] ^ kf[10];
++ kf += 6;
++ }
++ while(kf < kt);
++ break;
++
++ case 8: cx->aes_e_key[4] = const_word_in(in_key + 16);
++ cx->aes_e_key[5] = const_word_in(in_key + 20);
++ cx->aes_e_key[6] = const_word_in(in_key + 24);
++ cx->aes_e_key[7] = const_word_in(in_key + 28);
++ do
++ { kf[ 8] = kf[0] ^ ls_box(kf[7],3) ^ rcon_tab[rci++];
++ kf[ 9] = kf[1] ^ kf[ 8];
++ kf[10] = kf[2] ^ kf[ 9];
++ kf[11] = kf[3] ^ kf[10];
++ kf[12] = kf[4] ^ ls_box(kf[11],0);
++ kf[13] = kf[5] ^ kf[12];
++ kf[14] = kf[6] ^ kf[13];
++ kf[15] = kf[7] ^ kf[14];
++ kf += 8;
++ }
++ while (kf < kt);
++ break;
++ }
++
++ if(!f)
++ { u_int32_t i;
++
++ kt = cx->aes_d_key + nc * cx->aes_Nrnd;
++ kf = cx->aes_e_key;
++
++ cpy(kt, kf); kt -= 2 * nc;
++
++ for(i = 1; i < cx->aes_Nrnd; ++i)
++ {
++#if defined(ONE_TABLE) || defined(FOUR_TABLES)
++#if !defined(ONE_IM_TABLE) && !defined(FOUR_IM_TABLES)
++ u_int32_t f2, f4, f8, f9;
++#endif
++ mix(kt, kf);
++#else
++ cpy(kt, kf);
++#endif
++ kt -= 2 * nc;
++ }
++
++ cpy(kt, kf);
++ }
++}
++
++// y = output word, x = input word, r = row, c = column
++// for r = 0, 1, 2 and 3 = column accessed for row r
++
++#if defined(ARRAYS)
++#define s(x,c) x[c]
++#else
++#define s(x,c) x##c
++#endif
++
++// I am grateful to Frank Yellin for the following constructions
++// which, given the column (c) of the output state variable that
++// is being computed, return the input state variables which are
++// needed for each row (r) of the state
++
++// For the fixed block size options, compilers reduce these two
++// expressions to fixed variable references. For variable block
++// size code conditional clauses will sometimes be returned
++
++#define unused 77 // Sunset Strip
++
++#define fwd_var(x,r,c) \
++ ( r==0 ? \
++ ( c==0 ? s(x,0) \
++ : c==1 ? s(x,1) \
++ : c==2 ? s(x,2) \
++ : c==3 ? s(x,3) \
++ : c==4 ? s(x,4) \
++ : c==5 ? s(x,5) \
++ : c==6 ? s(x,6) \
++ : s(x,7)) \
++ : r==1 ? \
++ ( c==0 ? s(x,1) \
++ : c==1 ? s(x,2) \
++ : c==2 ? s(x,3) \
++ : c==3 ? nc==4 ? s(x,0) : s(x,4) \
++ : c==4 ? s(x,5) \
++ : c==5 ? nc==8 ? s(x,6) : s(x,0) \
++ : c==6 ? s(x,7) \
++ : s(x,0)) \
++ : r==2 ? \
++ ( c==0 ? nc==8 ? s(x,3) : s(x,2) \
++ : c==1 ? nc==8 ? s(x,4) : s(x,3) \
++ : c==2 ? nc==4 ? s(x,0) : nc==8 ? s(x,5) : s(x,4) \
++ : c==3 ? nc==4 ? s(x,1) : nc==8 ? s(x,6) : s(x,5) \
++ : c==4 ? nc==8 ? s(x,7) : s(x,0) \
++ : c==5 ? nc==8 ? s(x,0) : s(x,1) \
++ : c==6 ? s(x,1) \
++ : s(x,2)) \
++ : \
++ ( c==0 ? nc==8 ? s(x,4) : s(x,3) \
++ : c==1 ? nc==4 ? s(x,0) : nc==8 ? s(x,5) : s(x,4) \
++ : c==2 ? nc==4 ? s(x,1) : nc==8 ? s(x,6) : s(x,5) \
++ : c==3 ? nc==4 ? s(x,2) : nc==8 ? s(x,7) : s(x,0) \
++ : c==4 ? nc==8 ? s(x,0) : s(x,1) \
++ : c==5 ? nc==8 ? s(x,1) : s(x,2) \
++ : c==6 ? s(x,2) \
++ : s(x,3)))
++
++#define inv_var(x,r,c) \
++ ( r==0 ? \
++ ( c==0 ? s(x,0) \
++ : c==1 ? s(x,1) \
++ : c==2 ? s(x,2) \
++ : c==3 ? s(x,3) \
++ : c==4 ? s(x,4) \
++ : c==5 ? s(x,5) \
++ : c==6 ? s(x,6) \
++ : s(x,7)) \
++ : r==1 ? \
++ ( c==0 ? nc==4 ? s(x,3) : nc==8 ? s(x,7) : s(x,5) \
++ : c==1 ? s(x,0) \
++ : c==2 ? s(x,1) \
++ : c==3 ? s(x,2) \
++ : c==4 ? s(x,3) \
++ : c==5 ? s(x,4) \
++ : c==6 ? s(x,5) \
++ : s(x,6)) \
++ : r==2 ? \
++ ( c==0 ? nc==4 ? s(x,2) : nc==8 ? s(x,5) : s(x,4) \
++ : c==1 ? nc==4 ? s(x,3) : nc==8 ? s(x,6) : s(x,5) \
++ : c==2 ? nc==8 ? s(x,7) : s(x,0) \
++ : c==3 ? nc==8 ? s(x,0) : s(x,1) \
++ : c==4 ? nc==8 ? s(x,1) : s(x,2) \
++ : c==5 ? nc==8 ? s(x,2) : s(x,3) \
++ : c==6 ? s(x,3) \
++ : s(x,4)) \
++ : \
++ ( c==0 ? nc==4 ? s(x,1) : nc==8 ? s(x,4) : s(x,3) \
++ : c==1 ? nc==4 ? s(x,2) : nc==8 ? s(x,5) : s(x,4) \
++ : c==2 ? nc==4 ? s(x,3) : nc==8 ? s(x,6) : s(x,5) \
++ : c==3 ? nc==8 ? s(x,7) : s(x,0) \
++ : c==4 ? nc==8 ? s(x,0) : s(x,1) \
++ : c==5 ? nc==8 ? s(x,1) : s(x,2) \
++ : c==6 ? s(x,2) \
++ : s(x,3)))
++
++#define si(y,x,k,c) s(y,c) = const_word_in(x + 4 * c) ^ k[c]
++#define so(y,x,c) word_out(y + 4 * c, s(x,c))
++
++#if defined(FOUR_TABLES)
++#define fwd_rnd(y,x,k,c) s(y,c)= (k)[c] ^ four_tables(x,ft_tab,fwd_var,rf1,c)
++#define inv_rnd(y,x,k,c) s(y,c)= (k)[c] ^ four_tables(x,it_tab,inv_var,rf1,c)
++#elif defined(ONE_TABLE)
++#define fwd_rnd(y,x,k,c) s(y,c)= (k)[c] ^ one_table(x,upr,ft_tab,fwd_var,rf1,c)
++#define inv_rnd(y,x,k,c) s(y,c)= (k)[c] ^ one_table(x,upr,it_tab,inv_var,rf1,c)
++#else
++#define fwd_rnd(y,x,k,c) s(y,c) = fwd_mcol(no_table(x,s_box,fwd_var,rf1,c)) ^ (k)[c]
++#define inv_rnd(y,x,k,c) s(y,c) = inv_mcol(no_table(x,inv_s_box,inv_var,rf1,c) ^ (k)[c])
++#endif
++
++#if defined(FOUR_LR_TABLES)
++#define fwd_lrnd(y,x,k,c) s(y,c)= (k)[c] ^ four_tables(x,fl_tab,fwd_var,rf1,c)
++#define inv_lrnd(y,x,k,c) s(y,c)= (k)[c] ^ four_tables(x,il_tab,inv_var,rf1,c)
++#elif defined(ONE_LR_TABLE)
++#define fwd_lrnd(y,x,k,c) s(y,c)= (k)[c] ^ one_table(x,ups,fl_tab,fwd_var,rf1,c)
++#define inv_lrnd(y,x,k,c) s(y,c)= (k)[c] ^ one_table(x,ups,il_tab,inv_var,rf1,c)
++#else
++#define fwd_lrnd(y,x,k,c) s(y,c) = no_table(x,s_box,fwd_var,rf1,c) ^ (k)[c]
++#define inv_lrnd(y,x,k,c) s(y,c) = no_table(x,inv_s_box,inv_var,rf1,c) ^ (k)[c]
++#endif
++
++#if AES_BLOCK_SIZE == 16
++
++#if defined(ARRAYS)
++#define locals(y,x) x[4],y[4]
++#else
++#define locals(y,x) x##0,x##1,x##2,x##3,y##0,y##1,y##2,y##3
++// the following defines prevent the compiler requiring the declaration
++// of generated but unused variables in the fwd_var and inv_var macros
++#define b04 unused
++#define b05 unused
++#define b06 unused
++#define b07 unused
++#define b14 unused
++#define b15 unused
++#define b16 unused
++#define b17 unused
++#endif
++#define l_copy(y, x) s(y,0) = s(x,0); s(y,1) = s(x,1); \
++ s(y,2) = s(x,2); s(y,3) = s(x,3);
++#define state_in(y,x,k) si(y,x,k,0); si(y,x,k,1); si(y,x,k,2); si(y,x,k,3)
++#define state_out(y,x) so(y,x,0); so(y,x,1); so(y,x,2); so(y,x,3)
++#define round(rm,y,x,k) rm(y,x,k,0); rm(y,x,k,1); rm(y,x,k,2); rm(y,x,k,3)
++
++#elif AES_BLOCK_SIZE == 24
++
++#if defined(ARRAYS)
++#define locals(y,x) x[6],y[6]
++#else
++#define locals(y,x) x##0,x##1,x##2,x##3,x##4,x##5, \
++ y##0,y##1,y##2,y##3,y##4,y##5
++#define b06 unused
++#define b07 unused
++#define b16 unused
++#define b17 unused
++#endif
++#define l_copy(y, x) s(y,0) = s(x,0); s(y,1) = s(x,1); \
++ s(y,2) = s(x,2); s(y,3) = s(x,3); \
++ s(y,4) = s(x,4); s(y,5) = s(x,5);
++#define state_in(y,x,k) si(y,x,k,0); si(y,x,k,1); si(y,x,k,2); \
++ si(y,x,k,3); si(y,x,k,4); si(y,x,k,5)
++#define state_out(y,x) so(y,x,0); so(y,x,1); so(y,x,2); \
++ so(y,x,3); so(y,x,4); so(y,x,5)
++#define round(rm,y,x,k) rm(y,x,k,0); rm(y,x,k,1); rm(y,x,k,2); \
++ rm(y,x,k,3); rm(y,x,k,4); rm(y,x,k,5)
++#else
++
++#if defined(ARRAYS)
++#define locals(y,x) x[8],y[8]
++#else
++#define locals(y,x) x##0,x##1,x##2,x##3,x##4,x##5,x##6,x##7, \
++ y##0,y##1,y##2,y##3,y##4,y##5,y##6,y##7
++#endif
++#define l_copy(y, x) s(y,0) = s(x,0); s(y,1) = s(x,1); \
++ s(y,2) = s(x,2); s(y,3) = s(x,3); \
++ s(y,4) = s(x,4); s(y,5) = s(x,5); \
++ s(y,6) = s(x,6); s(y,7) = s(x,7);
++
++#if AES_BLOCK_SIZE == 32
++
++#define state_in(y,x,k) si(y,x,k,0); si(y,x,k,1); si(y,x,k,2); si(y,x,k,3); \
++ si(y,x,k,4); si(y,x,k,5); si(y,x,k,6); si(y,x,k,7)
++#define state_out(y,x) so(y,x,0); so(y,x,1); so(y,x,2); so(y,x,3); \
++ so(y,x,4); so(y,x,5); so(y,x,6); so(y,x,7)
++#define round(rm,y,x,k) rm(y,x,k,0); rm(y,x,k,1); rm(y,x,k,2); rm(y,x,k,3); \
++ rm(y,x,k,4); rm(y,x,k,5); rm(y,x,k,6); rm(y,x,k,7)
++#else
++
++#define state_in(y,x,k) \
++switch(nc) \
++{ case 8: si(y,x,k,7); si(y,x,k,6); \
++ case 6: si(y,x,k,5); si(y,x,k,4); \
++ case 4: si(y,x,k,3); si(y,x,k,2); \
++ si(y,x,k,1); si(y,x,k,0); \
++}
++
++#define state_out(y,x) \
++switch(nc) \
++{ case 8: so(y,x,7); so(y,x,6); \
++ case 6: so(y,x,5); so(y,x,4); \
++ case 4: so(y,x,3); so(y,x,2); \
++ so(y,x,1); so(y,x,0); \
++}
++
++#if defined(FAST_VARIABLE)
++
++#define round(rm,y,x,k) \
++switch(nc) \
++{ case 8: rm(y,x,k,7); rm(y,x,k,6); \
++ rm(y,x,k,5); rm(y,x,k,4); \
++ rm(y,x,k,3); rm(y,x,k,2); \
++ rm(y,x,k,1); rm(y,x,k,0); \
++ break; \
++ case 6: rm(y,x,k,5); rm(y,x,k,4); \
++ rm(y,x,k,3); rm(y,x,k,2); \
++ rm(y,x,k,1); rm(y,x,k,0); \
++ break; \
++ case 4: rm(y,x,k,3); rm(y,x,k,2); \
++ rm(y,x,k,1); rm(y,x,k,0); \
++ break; \
++}
++#else
++
++#define round(rm,y,x,k) \
++switch(nc) \
++{ case 8: rm(y,x,k,7); rm(y,x,k,6); \
++ case 6: rm(y,x,k,5); rm(y,x,k,4); \
++ case 4: rm(y,x,k,3); rm(y,x,k,2); \
++ rm(y,x,k,1); rm(y,x,k,0); \
++}
++
++#endif
++
++#endif
++#endif
++
++void aes_encrypt(const aes_context *cx, const unsigned char in_blk[], unsigned char out_blk[])
++{ u_int32_t locals(b0, b1);
++ const u_int32_t *kp = cx->aes_e_key;
++
++#if !defined(ONE_TABLE) && !defined(FOUR_TABLES)
++ u_int32_t f2;
++#endif
++
++ state_in(b0, in_blk, kp); kp += nc;
++
++#if defined(UNROLL)
++
++ switch(cx->aes_Nrnd)
++ {
++ case 14: round(fwd_rnd, b1, b0, kp );
++ round(fwd_rnd, b0, b1, kp + nc ); kp += 2 * nc;
++ case 12: round(fwd_rnd, b1, b0, kp );
++ round(fwd_rnd, b0, b1, kp + nc ); kp += 2 * nc;
++ case 10: round(fwd_rnd, b1, b0, kp );
++ round(fwd_rnd, b0, b1, kp + nc);
++ round(fwd_rnd, b1, b0, kp + 2 * nc);
++ round(fwd_rnd, b0, b1, kp + 3 * nc);
++ round(fwd_rnd, b1, b0, kp + 4 * nc);
++ round(fwd_rnd, b0, b1, kp + 5 * nc);
++ round(fwd_rnd, b1, b0, kp + 6 * nc);
++ round(fwd_rnd, b0, b1, kp + 7 * nc);
++ round(fwd_rnd, b1, b0, kp + 8 * nc);
++ round(fwd_lrnd, b0, b1, kp + 9 * nc);
++ }
++
++#elif defined(PARTIAL_UNROLL)
++ { u_int32_t rnd;
++
++ for(rnd = 0; rnd < (cx->aes_Nrnd >> 1) - 1; ++rnd)
++ {
++ round(fwd_rnd, b1, b0, kp);
++ round(fwd_rnd, b0, b1, kp + nc); kp += 2 * nc;
++ }
++
++ round(fwd_rnd, b1, b0, kp);
++ round(fwd_lrnd, b0, b1, kp + nc);
++ }
++#else
++ { u_int32_t rnd;
++
++ for(rnd = 0; rnd < cx->aes_Nrnd - 1; ++rnd)
++ {
++ round(fwd_rnd, b1, b0, kp);
++ l_copy(b0, b1); kp += nc;
++ }
++
++ round(fwd_lrnd, b0, b1, kp);
++ }
++#endif
++
++ state_out(out_blk, b0);
++}
++
++void aes_decrypt(const aes_context *cx, const unsigned char in_blk[], unsigned char out_blk[])
++{ u_int32_t locals(b0, b1);
++ const u_int32_t *kp = cx->aes_d_key;
++
++#if !defined(ONE_TABLE) && !defined(FOUR_TABLES)
++ u_int32_t f2, f4, f8, f9;
++#endif
++
++ state_in(b0, in_blk, kp); kp += nc;
++
++#if defined(UNROLL)
++
++ switch(cx->aes_Nrnd)
++ {
++ case 14: round(inv_rnd, b1, b0, kp );
++ round(inv_rnd, b0, b1, kp + nc ); kp += 2 * nc;
++ case 12: round(inv_rnd, b1, b0, kp );
++ round(inv_rnd, b0, b1, kp + nc ); kp += 2 * nc;
++ case 10: round(inv_rnd, b1, b0, kp );
++ round(inv_rnd, b0, b1, kp + nc);
++ round(inv_rnd, b1, b0, kp + 2 * nc);
++ round(inv_rnd, b0, b1, kp + 3 * nc);
++ round(inv_rnd, b1, b0, kp + 4 * nc);
++ round(inv_rnd, b0, b1, kp + 5 * nc);
++ round(inv_rnd, b1, b0, kp + 6 * nc);
++ round(inv_rnd, b0, b1, kp + 7 * nc);
++ round(inv_rnd, b1, b0, kp + 8 * nc);
++ round(inv_lrnd, b0, b1, kp + 9 * nc);
++ }
++
++#elif defined(PARTIAL_UNROLL)
++ { u_int32_t rnd;
++
++ for(rnd = 0; rnd < (cx->aes_Nrnd >> 1) - 1; ++rnd)
++ {
++ round(inv_rnd, b1, b0, kp);
++ round(inv_rnd, b0, b1, kp + nc); kp += 2 * nc;
++ }
++
++ round(inv_rnd, b1, b0, kp);
++ round(inv_lrnd, b0, b1, kp + nc);
++ }
++#else
++ { u_int32_t rnd;
++
++ for(rnd = 0; rnd < cx->aes_Nrnd - 1; ++rnd)
++ {
++ round(inv_rnd, b1, b0, kp);
++ l_copy(b0, b1); kp += nc;
++ }
++
++ round(inv_lrnd, b0, b1, kp);
++ }
++#endif
++
++ state_out(out_blk, b0);
++}
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/crypto/ciphers/aes/aes_cbc.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,43 @@
++/*
++// I retain copyright in this code but I encourage its free use provided
++// that I don't carry any responsibility for the results. I am especially
++// happy to see it used in free and open source software. If you do use
++// it I would appreciate an acknowledgement of its origin in the code or
++// the product that results and I would also appreciate knowing a little
++// about the use to which it is being put. I am grateful to Frank Yellin
++// for some ideas that are used in this implementation.
++//
++// Dr B. R. Gladman <brg@gladman.uk.net> 6th April 2001.
++//
++// This is an implementation of the AES encryption algorithm (Rijndael)
++// designed by Joan Daemen and Vincent Rijmen. This version is designed
++// to provide both fixed and dynamic block and key lengths and can also
++// run with either big or little endian internal byte order (see aes.h).
++// It inputs block and key lengths in bytes with the legal values being
++// 16, 24 and 32.
++*
++*/
++
++#ifdef __KERNEL__
++#include <linux/types.h>
++#else
++#include <sys/types.h>
++#endif
++#include "crypto/aes_cbc.h"
++#include "crypto/cbc_generic.h"
++
++/* returns bool success */
++int AES_set_key(aes_context *aes_ctx, const u_int8_t *key, int keysize) {
++ aes_set_key(aes_ctx, key, keysize, 0);
++ return 1;
++}
++CBC_IMPL_BLK16(AES_cbc_encrypt, aes_context, u_int8_t *, aes_encrypt, aes_decrypt);
++
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.1 2004/04/06 02:48:12 mcr
++ * pullup of AES cipher from alg-branch.
++ *
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/crypto/ciphers/aes/aes_xcbc_mac.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,67 @@
++#ifdef __KERNEL__
++#include <linux/types.h>
++#include <linux/kernel.h>
++#define DEBUG(x)
++#else
++#include <stdio.h>
++#include <sys/types.h>
++#define DEBUG(x) x
++#endif
++
++#include "crypto/aes.h"
++#include "crypto/aes_xcbc_mac.h"
++
++int AES_xcbc_mac_set_key(aes_context_mac *ctxm, const u_int8_t *key, int keylen)
++{
++ int ret=1;
++ aes_block kn[3] = {
++ { 0x01010101, 0x01010101, 0x01010101, 0x01010101 },
++ { 0x02020202, 0x02020202, 0x02020202, 0x02020202 },
++ { 0x03030303, 0x03030303, 0x03030303, 0x03030303 },
++ };
++ aes_set_key(&ctxm->ctx_k1, key, keylen, 0);
++ aes_encrypt(&ctxm->ctx_k1, (u_int8_t *) kn[0], (u_int8_t *) kn[0]);
++ aes_encrypt(&ctxm->ctx_k1, (u_int8_t *) kn[1], (u_int8_t *) ctxm->k2);
++ aes_encrypt(&ctxm->ctx_k1, (u_int8_t *) kn[2], (u_int8_t *) ctxm->k3);
++ aes_set_key(&ctxm->ctx_k1, (u_int8_t *) kn[0], 16, 0);
++ return ret;
++}
++static void do_pad_xor(u_int8_t *out, const u_int8_t *in, int len) {
++ int pos=0;
++ for (pos=1; pos <= 16; pos++, in++, out++) {
++ if (pos <= len)
++ *out ^= *in;
++ if (pos > len) {
++ DEBUG(printf("put 0x80 at pos=%d\n", pos));
++ *out ^= 0x80;
++ break;
++ }
++ }
++}
++static void xor_block(aes_block res, const aes_block op) {
++ res[0] ^= op[0];
++ res[1] ^= op[1];
++ res[2] ^= op[2];
++ res[3] ^= op[3];
++}
++int AES_xcbc_mac_hash(const aes_context_mac *ctxm, const u_int8_t * in, int ilen, u_int8_t hash[16]) {
++ int ret=ilen;
++ u_int32_t out[4] = { 0, 0, 0, 0 };
++ for (; ilen > 16 ; ilen-=16) {
++ xor_block(out, (const u_int32_t*) &in[0]);
++ aes_encrypt(&ctxm->ctx_k1, in, (u_int8_t *)&out[0]);
++ in+=16;
++ }
++ do_pad_xor((u_int8_t *)&out, in, ilen);
++ if (ilen==16) {
++ DEBUG(printf("using k3\n"));
++ xor_block(out, ctxm->k3);
++ }
++ else
++ {
++ DEBUG(printf("using k2\n"));
++ xor_block(out, ctxm->k2);
++ }
++ aes_encrypt(&ctxm->ctx_k1, (u_int8_t *)out, hash);
++ return ret;
++}
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/crypto/ciphers/aes/test_main.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,41 @@
++#include <stdio.h>
++#include <string.h>
++#include <sys/types.h>
++#include "aes_cbc.h"
++#define AES_BLOCK_SIZE 16
++#define KEY_SIZE 128 /* bits */
++#define KEY "1234567890123456"
++#define STR "hola guaso como estaisss ... 012"
++#define STRSZ (sizeof(STR)-1)
++
++#define EMT_AESCBC_BLKLEN AES_BLOCK_SIZE
++#define AES_CONTEXT_T aes_context
++#define EMT_ESPAES_KEY_SZ 16
++int pretty_print(const unsigned char *buf, int count) {
++ int i=0;
++ for (;i<count;i++) {
++ if (i%8==0) putchar(' ');
++ if (i%16==0) putchar('\n');
++ printf ("%02hhx ", buf[i]);
++ }
++ putchar('\n');
++ return i;
++}
++//#define SIZE STRSZ/2
++#define SIZE STRSZ
++int main() {
++ int ret;
++ char buf0[SIZE+1], buf1[SIZE+1];
++ char IV[AES_BLOCK_SIZE]="\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0";
++ aes_context ac;
++ AES_set_key(&ac, KEY, KEY_SIZE);
++ //pretty_print((char *)&ac.aes_e_key, sizeof(ac.aes_e_key));
++ memset(buf0, 0, sizeof (buf0));
++ memset(buf1, 0, sizeof (buf1));
++ ret=AES_cbc_encrypt(&ac, STR, buf0, SIZE, IV, 1);
++ pretty_print(buf0, SIZE);
++ printf("size=%d ret=%d\n%s\n", SIZE, ret, buf0);
++ ret=AES_cbc_encrypt(&ac, buf0, buf1, SIZE, IV, 0);
++ printf("size=%d ret=%d\n%s\n", SIZE, ret, buf1);
++ return 0;
++}
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/crypto/ciphers/aes/test_main_mac.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,30 @@
++#include <stdio.h>
++#include <sys/types.h>
++#include <string.h>
++#include "aes.h"
++#include "aes_xcbc_mac.h"
++#define STR "Hola guasssso c|mo estais ...012"
++void print_hash(const __u8 *hash) {
++ printf("%08x %08x %08x %08x\n",
++ *(__u32*)(&hash[0]),
++ *(__u32*)(&hash[4]),
++ *(__u32*)(&hash[8]),
++ *(__u32*)(&hash[12]));
++}
++int main(int argc, char *argv[]) {
++ aes_block key= { 0xdeadbeef, 0xceedcaca, 0xcafebabe, 0xff010204 };
++ __u8 hash[16];
++ char *str = argv[1];
++ aes_context_mac ctx;
++ if (str==NULL) {
++ fprintf(stderr, "pasame el str\n");
++ return 255;
++ }
++ AES_xcbc_mac_set_key(&ctx, (__u8 *)&key, sizeof(key));
++ AES_xcbc_mac_hash(&ctx, str, strlen(str), hash);
++ print_hash(hash);
++ str[2]='x';
++ AES_xcbc_mac_hash(&ctx, str, strlen(str), hash);
++ print_hash(hash);
++ return 0;
++}
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/crypto/ciphers/des/COPYRIGHT Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,50 @@
++Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com)
++All rights reserved.
++
++This package is an DES implementation written by Eric Young (eay@cryptsoft.com).
++The implementation was written so as to conform with MIT's libdes.
++
++This library is free for commercial and non-commercial use as long as
++the following conditions are aheared to. The following conditions
++apply to all code found in this distribution.
++
++Copyright remains Eric Young's, and as such any Copyright notices in
++the code are not to be removed.
++If this package is used in a product, Eric Young should be given attribution
++as the author of that the SSL library. This can be in the form of a textual
++message at program startup or in documentation (online or textual) provided
++with the package.
++
++Redistribution and use in source and binary forms, with or without
++modification, are permitted provided that the following conditions
++are met:
++1. Redistributions of source code must retain the copyright
++ notice, this list of conditions and the following disclaimer.
++2. Redistributions in binary form must reproduce the above copyright
++ notice, this list of conditions and the following disclaimer in the
++ documentation and/or other materials provided with the distribution.
++3. All advertising materials mentioning features or use of this software
++ must display the following acknowledgement:
++ This product includes software developed by Eric Young (eay@cryptsoft.com)
++
++THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
++ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
++FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++SUCH DAMAGE.
++
++The license and distribution terms for any publically available version or
++derivative of this code cannot be changed. i.e. this code cannot simply be
++copied and put under another distrubution license
++[including the GNU Public License.]
++
++The reason behind this being stated in this direct manner is past
++experience in code simply being copied and the attribution removed
++from it and then being distributed as part of other packages. This
++implementation was a non-trivial and unpaid effort.
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/crypto/ciphers/des/INSTALL Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,69 @@
++Check the CC and CFLAGS lines in the makefile
++
++If your C library does not support the times(3) function, change the
++#define TIMES to
++#undef TIMES in speed.c
++If it does, check the HZ value for the times(3) function.
++If your system does not define CLK_TCK it will be assumed to
++be 100.0.
++
++If possible use gcc v 2.7.?
++Turn on the maximum optimising (normally '-O3 -fomit-frame-pointer' for gcc)
++In recent times, some system compilers give better performace.
++
++type 'make'
++
++run './destest' to check things are ok.
++run './rpw' to check the tty code for reading passwords works.
++run './speed' to see how fast those optimisations make the library run :-)
++run './des_opts' to determin the best compile time options.
++
++The output from des_opts should be put in the makefile options and des_enc.c
++should be rebuilt. For 64 bit computers, do not use the DES_PTR option.
++For the DEC Alpha, edit des.h and change DES_LONG to 'unsigned int'
++and then you can use the 'DES_PTR' option.
++
++The file options.txt has the options listed for best speed on quite a
++few systems. Look and the options (UNROLL, PTR, RISC2 etc) and then
++turn on the relevent option in the Makefile
++
++There are some special Makefile targets that make life easier.
++make cc - standard cc build
++make gcc - standard gcc build
++make x86-elf - x86 assembler (elf), linux-elf.
++make x86-out - x86 assembler (a.out), FreeBSD
++make x86-solaris- x86 assembler
++make x86-bsdi - x86 assembler (a.out with primative assembler).
++
++If at all possible use the assembler (for Windows NT/95, use
++asm/win32.obj to link with). The x86 assembler is very very fast.
++
++A make install will by default install
++libdes.a in /usr/local/lib/libdes.a
++des in /usr/local/bin/des
++des_crypt.man in /usr/local/man/man3/des_crypt.3
++des.man in /usr/local/man/man1/des.1
++des.h in /usr/include/des.h
++
++des(1) should be compatible with sunOS's but I have been unable to
++test it.
++
++These routines should compile on MSDOS, most 32bit and 64bit version
++of Unix (BSD and SYSV) and VMS, without modification.
++The only problems should be #include files that are in the wrong places.
++
++These routines can be compiled under MSDOS.
++I have successfully encrypted files using des(1) under MSDOS and then
++decrypted the files on a SparcStation.
++I have been able to compile and test the routines with
++Microsoft C v 5.1 and Turbo C v 2.0.
++The code in this library is in no way optimised for the 16bit
++operation of MSDOS.
++
++When building for glibc, ignore all of the above and just unpack into
++glibc-1.??/des and then gmake as per normal.
++
++As a final note on performace. Certain CPUs like sparcs and Alpha often give
++a %10 speed difference depending on the link order. It is rather anoying
++when one program reports 'x' DES encrypts a second and another reports
++'x*0.9' the speed.
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/crypto/ciphers/des/Makefile.objs Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,20 @@
++obj-$(CONFIG_IPSEC_ENC_3DES) += cbc_enc.o
++#obj-$(CONFIG_IPSEC_ENC_3DES) += des_opts.o
++obj-$(CONFIG_IPSEC_ENC_3DES) += ecb_enc.o
++#obj-$(CONFIG_IPSEC_ENC_3DES) += fcrypt.o
++obj-$(CONFIG_IPSEC_ENC_3DES) += set_key.o
++
++ifeq ($(strip ${SUBARCH}),)
++SUBARCH:=${ARCH}
++endif
++
++ifeq (${SUBARCH},i386)
++obj-$(CONFIG_IPSEC_ENC_3DES) += dx86unix.o
++else
++obj-$(CONFIG_IPSEC_ENC_3DES) += des_enc.o
++endif
++
++
++
++
++
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/crypto/ciphers/des/README Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,54 @@
++
++ libdes, Version 4.01 10-Jan-97
++
++ Copyright (c) 1997, Eric Young
++ All rights reserved.
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms specified in COPYRIGHT.
++
++--
++The primary ftp site for this library is
++ftp://ftp.psy.uq.oz.au/pub/Crypto/DES/libdes-x.xx.tar.gz
++libdes is now also shipped with SSLeay. Primary ftp site of
++ftp://ftp.psy.uq.oz.au/pub/Crypto/SSL/SSLeay-x.x.x.tar.gz
++
++The best way to build this library is to build it as part of SSLeay.
++
++This kit builds a DES encryption library and a DES encryption program.
++It supports ecb, cbc, ofb, cfb, triple ecb, triple cbc, triple ofb,
++triple cfb, desx, and MIT's pcbc encryption modes and also has a fast
++implementation of crypt(3).
++It contains support routines to read keys from a terminal,
++generate a random key, generate a key from an arbitrary length string,
++read/write encrypted data from/to a file descriptor.
++
++The implementation was written so as to conform with the manual entry
++for the des_crypt(3) library routines from MIT's project Athena.
++
++destest should be run after compilation to test the des routines.
++rpw should be run after compilation to test the read password routines.
++The des program is a replacement for the sun des command. I believe it
++conforms to the sun version.
++
++The Imakefile is setup for use in the kerberos distribution.
++
++These routines are best compiled with gcc or any other good
++optimising compiler.
++Just turn you optimiser up to the highest settings and run destest
++after the build to make sure everything works.
++
++I believe these routines are close to the fastest and most portable DES
++routines that use small lookup tables (4.5k) that are publicly available.
++The fcrypt routine is faster than ufc's fcrypt (when compiling with
++gcc2 -O2) on the sparc 2 (1410 vs 1270) but is not so good on other machines
++(on a sun3/260 168 vs 336). It is a function of CPU on chip cache size.
++[ 10-Jan-97 and a function of an incorrect speed testing program in
++ ufc which gave much better test figures that reality ].
++
++It is worth noting that on sparc and Alpha CPUs, performance of the DES
++library can vary by upto %10 due to the positioning of files after application
++linkage.
++
++Eric Young (eay@cryptsoft.com)
++
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/crypto/ciphers/des/README.freeswan Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,33 @@
++The only changes the FreeS/WAN project has made to libdes-lite 4.04b are:
++
++We #ifdef-ed the declaration of DES_LONG in des.h, so it's more efficient
++on the Alpha, instead of just noting the issue in a comment.
++
++We #ifdef-ed out the des_options() function in ecb_enc.c, because we don't
++use it, and its call to sprintf() can cause subtle difficulties when KLIPS
++is built as a module (depending on details of Linux configuration options).
++
++We changed some instances of CC=$(CC) in the Makefile to CC='$(CC)' to make
++it cope better with Linux kernel Makefile stupidities, and took out an
++explicit CC=gcc (unwise on systems with strange compilers).
++
++We deleted some references to <stdio.h> and <stdlib.h>, and a declaration
++of one function found only in the full libdes (not in libdes-lite), to
++avoid dragging in bits of stdio/stdlib unnecessarily. (Our thanks to Hans
++Schultz for spotting this and pointing out the fixes.)
++
++We deleted a couple of .obj files in the asm subdirectory, which appear to
++have been included in the original library by accident.
++
++We have added an include of our Makefile.inc file, to permit overriding
++things like choice of compiler (although the libdes Makefile would
++probably need some work to make this effective).
++
++
++
++Note that Eric Young is no longer at the email address listed in these
++files, and is (alas) no longer working on free crypto software.
++
++
++
++This file is RCSID $Id$
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/crypto/ciphers/des/VERSION Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,406 @@
++Version 4.04
++ Fixed a few tests in destest. Also added x86 assember for
++ des_ncbc_encrypt() which is the standard cbc mode function.
++ This makes a very very large performace difference.
++ Ariel Glenn ariel@columbia.edu reports that the terminal
++ 'turn echo off' can return (errno == EINVAL) under solaris
++ when redirection is used. So I now catch that as well as ENOTTY.
++
++
++Version 4.03
++ Left a static out of enc_write.c, which caused to buffer to be
++ continiously malloc()ed. Does anyone use these functions? I keep
++ on feeling like removing them since I only had these in there
++ for a version of kerberised login. Anyway, this was pointed out
++ by Theo de Raadt <deraadt@cvs.openbsd.org>
++ The 'n' bit ofb code was wrong, it was not shifting the shift
++ register. It worked correctly for n == 64. Thanks to
++ Gigi Ankeny <Gigi.Ankeny@Eng.Sun.COM> for pointing this one out.
++
++Version 4.02
++ I was doing 'if (memcmp(weak_keys[i],key,sizeof(key)) == 0)'
++ when checking for weak keys which is wrong :-(, pointed out by
++ Markus F.X.J. Oberhumer <markus.oberhumer@jk.uni-linz.ac.at>.
++
++Version 4.01
++ Even faster inner loop in the DES assembler for x86 and a modification
++ for IP/FP which is faster on x86. Both of these changes are
++ from Svend Olaf Mikkelsen <svolaf@inet.uni-c.dk>. His
++ changes make the assembler run %40 faster on a pentium. This is just
++ a case of getting the instruction sequence 'just right'.
++ All credit to 'Svend' :-)
++ Quite a few special x86 'make' targets.
++ A libdes-l (lite) distribution.
++
++Version 4.00
++ After a bit of a pause, I'll up the major version number since this
++ is mostly a performace release. I've added x86 assembler and
++ added more options for performance. A %28 speedup for gcc
++ on a pentium and the assembler is a %50 speedup.
++ MIPS CPU's, sparc and Alpha are the main CPU's with speedups.
++ Run des_opts to work out which options should be used.
++ DES_RISC1/DES_RISC2 use alternative inner loops which use
++ more registers but should give speedups on any CPU that does
++ dual issue (pentium). DES_UNROLL unrolls the inner loop,
++ which costs in code size.
++
++Version 3.26
++ I've finally removed one of the shifts in D_ENCRYPT. This
++ meant I've changed the des_SPtrans table (spr.h), the set_key()
++ function and some things in des_enc.c. This has definitly
++ made things faster :-). I've known about this one for some
++ time but I've been too lazy to follow it up :-).
++ Noticed that in the D_ENCRYPT() macro, we can just do L^=(..)^(..)^..
++ instead of L^=((..)|(..)|(..).. This should save a register at
++ least.
++ Assember for x86. The file to replace is des_enc.c, which is replaced
++ by one of the assembler files found in asm. Look at des/asm/readme
++ for more info.
++
++ /* Modification to fcrypt so it can be compiled to support
++ HPUX 10.x's long password format, define -DLONGCRYPT to use this.
++ Thanks to Jens Kupferschmidt <bt1cu@hpboot.rz.uni-leipzig.de>. */
++
++ SIGWINCH case put in des_read_passwd() so the function does not
++ 'exit' if this function is recieved.
++
++Version 3.25 17/07/96
++ Modified read_pwd.c so that stdin can be read if not a tty.
++ Thanks to Jeff Barber <jeffb@issl.atl.hp.com> for the patches.
++ des_init_random_number_generator() shortened due to VMS linker
++ limits.
++ Added RSA's DESX cbc mode. It is a form of cbc encryption, with 2
++ 8 byte quantites xored before and after encryption.
++ des_xcbc_encryption() - the name is funny to preserve the des_
++ prefix on all functions.
++
++Version 3.24 20/04/96
++ The DES_PTR macro option checked and used by SSLeay configuration
++
++Version 3.23 11/04/96
++ Added DES_LONG. If defined to 'unsigned int' on the DEC Alpha,
++ it gives a %20 speedup :-)
++ Fixed the problem with des.pl under perl5. The patches were
++ sent by Ed Kubaitis (ejk@uiuc.edu).
++ if fcrypt.c, changed values to handle illegal salt values the way
++ normal crypt() implementations do. Some programs apparently use
++ them :-(. The patch was sent by Bjorn Gronvall <bg@sics.se>
++
++Version 3.22 29/11/95
++ Bug in des(1), an error with the uuencoding stuff when the
++ 'data' is small, thanks to Geoff Keating <keagchon@mehta.anu.edu.au>
++ for the patch.
++
++Version 3.21 22/11/95
++ After some emailing back and forth with
++ Colin Plumb <colin@nyx10.cs.du.edu>, I've tweaked a few things
++ and in a future version I will probably put in some of the
++ optimisation he suggested for use with the DES_USE_PTR option.
++ Extra routines from Mark Murray <mark@grondar.za> for use in
++ freeBSD. They mostly involve random number generation for use
++ with kerberos. They involve evil machine specific system calls
++ etc so I would normally suggest pushing this stuff into the
++ application and/or using RAND_seed()/RAND_bytes() if you are
++ using this DES library as part of SSLeay.
++ Redone the read_pw() function so that it is cleaner and
++ supports termios, thanks to Sameer Parekh <sameer@c2.org>
++ for the initial patches for this.
++ Renamed 3ecb_encrypt() to ecb3_encrypt(). This has been
++ done just to make things more consistent.
++ I have also now added triple DES versions of cfb and ofb.
++
++Version 3.20
++ Damn, Damn, Damn, as pointed out by Mike_Spreitzer.PARC@xerox.com,
++ my des_random_seed() function was only copying 4 bytes of the
++ passed seed into the init structure. It is now fixed to copy 8.
++ My own suggestion is to used something like MD5 :-)
++
++Version 3.19
++ While looking at my code one day, I though, why do I keep on
++ calling des_encrypt(in,out,ks,enc) when every function that
++ calls it has in and out the same. So I dropped the 'out'
++ parameter, people should not be using this function.
++
++Version 3.18 30/08/95
++ Fixed a few bit with the distribution and the filenames.
++ 3.17 had been munged via a move to DOS and back again.
++ NO CODE CHANGES
++
++Version 3.17 14/07/95
++ Fixed ede3 cbc which I had broken in 3.16. I have also
++ removed some unneeded variables in 7-8 of the routines.
++
++Version 3.16 26/06/95
++ Added des_encrypt2() which does not use IP/FP, used by triple
++ des routines. Tweaked things a bit elsewhere. %13 speedup on
++ sparc and %6 on a R4400 for ede3 cbc mode.
++
++Version 3.15 06/06/95
++ Added des_ncbc_encrypt(), it is des_cbc mode except that it is
++ 'normal' and copies the new iv value back over the top of the
++ passed parameter.
++ CHANGED des_ede3_cbc_encrypt() so that it too now overwrites
++ the iv. THIS WILL BREAK EXISTING CODE, but since this function
++ only new, I feel I can change it, not so with des_cbc_encrypt :-(.
++ I need to update the documentation.
++
++Version 3.14 31/05/95
++ New release upon the world, as part of my SSL implementation.
++ New copyright and usage stuff. Basically free for all to use
++ as long as you say it came from me :-)
++
++Version 3.13 31/05/95
++ A fix in speed.c, if HZ is not defined, I set it to 100.0
++ which is reasonable for most unixes except SunOS 4.x.
++ I now have a #ifdef sun but timing for SunOS 4.x looked very
++ good :-(. At my last job where I used SunOS 4.x, it was
++ defined to be 60.0 (look at the old INSTALL documentation), at
++ the last release had it changed to 100.0 since I now work with
++ Solaris2 and SVR4 boxes.
++ Thanks to Rory Chisholm <rchishol@math.ethz.ch> for pointing this
++ one out.
++
++Version 3.12 08/05/95
++ As pointed out by The Crypt Keeper <tck@bend.UCSD.EDU>,
++ my D_ENCRYPT macro in crypt() had an un-necessary variable.
++ It has been removed.
++
++Version 3.11 03/05/95
++ Added des_ede3_cbc_encrypt() which is cbc mode des with 3 keys
++ and one iv. It is a standard and I needed it for my SSL code.
++ It makes more sense to use this for triple DES than
++ 3cbc_encrypt(). I have also added (or should I say tested :-)
++ cfb64_encrypt() which is cfb64 but it will encrypt a partial
++ number of bytes - 3 bytes in 3 bytes out. Again this is for
++ my SSL library, as a form of encryption to use with SSL
++ telnet.
++
++Version 3.10 22/03/95
++ Fixed a bug in 3cbc_encrypt() :-(. When making repeated calls
++ to cbc3_encrypt, the 2 iv values that were being returned to
++ be used in the next call were reversed :-(.
++ Many thanks to Bill Wade <wade@Stoner.COM> for pointing out
++ this error.
++
++Version 3.09 01/02/95
++ Fixed des_random_key to far more random, it was rather feeble
++ with regards to picking the initial seed. The problem was
++ pointed out by Olaf Kirch <okir@monad.swb.de>.
++
++Version 3.08 14/12/94
++ Added Makefile.PL so libdes can be built into perl5.
++ Changed des_locl.h so RAND is always defined.
++
++Version 3.07 05/12/94
++ Added GNUmake and stuff so the library can be build with
++ glibc.
++
++Version 3.06 30/08/94
++ Added rpc_enc.c which contains _des_crypt. This is for use in
++ secure_rpc v 4.0
++ Finally fixed the cfb_enc problems.
++ Fixed a few parameter parsing bugs in des (-3 and -b), thanks
++ to Rob McMillan <R.McMillan@its.gu.edu.au>
++
++Version 3.05 21/04/94
++ for unsigned long l; gcc does not produce ((l>>34) == 0)
++ This causes bugs in cfb_enc.
++ Thanks to Hadmut Danisch <danisch@ira.uka.de>
++
++Version 3.04 20/04/94
++ Added a version number to des.c and libdes.a
++
++Version 3.03 12/01/94
++ Fixed a bug in non zero iv in 3cbc_enc.
++
++Version 3.02 29/10/93
++ I now work in a place where there are 6+ architectures and 14+
++ OS versions :-).
++ Fixed TERMIO definition so the most sys V boxes will work :-)
++
++Release upon comp.sources.misc
++Version 3.01 08/10/93
++ Added des_3cbc_encrypt()
++
++Version 3.00 07/10/93
++ Fixed up documentation.
++ quad_cksum definitely compatible with MIT's now.
++
++Version 2.30 24/08/93
++ Triple DES now defaults to triple cbc but can do triple ecb
++ with the -b flag.
++ Fixed some MSDOS uuen/uudecoding problems, thanks to
++ Added prototypes.
++
++Version 2.22 29/06/93
++ Fixed a bug in des_is_weak_key() which stopped it working :-(
++ thanks to engineering@MorningStar.Com.
++
++Version 2.21 03/06/93
++ des(1) with no arguments gives quite a bit of help.
++ Added -c (generate ckecksum) flag to des(1).
++ Added -3 (triple DES) flag to des(1).
++ Added cfb and ofb routines to the library.
++
++Version 2.20 11/03/93
++ Added -u (uuencode) flag to des(1).
++ I have been playing with byte order in quad_cksum to make it
++ compatible with MIT's version. All I can say is avid this
++ function if possible since MIT's output is endian dependent.
++
++Version 2.12 14/10/92
++ Added MSDOS specific macro in ecb_encrypt which gives a %70
++ speed up when the code is compiled with turbo C.
++
++Version 2.11 12/10/92
++ Speedup in set_key (recoding of PC-1)
++ I now do it in 47 simple operations, down from 60.
++ Thanks to John Fletcher (john_fletcher@lccmail.ocf.llnl.gov)
++ for motivating me to look for a faster system :-)
++ The speedup is probably less that 1% but it is still 13
++ instructions less :-).
++
++Version 2.10 06/10/92
++ The code now works on the 64bit ETA10 and CRAY without modifications or
++ #defines. I believe the code should work on any machine that
++ defines long, int or short to be 8 bytes long.
++ Thanks to Shabbir J. Safdar (shabby@mentor.cc.purdue.edu)
++ for helping me fix the code to run on 64bit machines (he had
++ access to an ETA10).
++ Thanks also to John Fletcher <john_fletcher@lccmail.ocf.llnl.gov>
++ for testing the routines on a CRAY.
++ read_password.c has been renamed to read_passwd.c
++ string_to_key.c has been renamed to string2key.c
++
++Version 2.00 14/09/92
++ Made mods so that the library should work on 64bit CPU's.
++ Removed all my uchar and ulong defs. To many different
++ versions of unix define them in their header files in too many
++ different combinations :-)
++ IRIX - Sillicon Graphics mods (mostly in read_password.c).
++ Thanks to Andrew Daviel (advax@erich.triumf.ca)
++
++Version 1.99 26/08/92
++ Fixed a bug or 2 in enc_read.c
++ Fixed a bug in enc_write.c
++ Fixed a pseudo bug in fcrypt.c (very obscure).
++
++Version 1.98 31/07/92
++ Support for the ETA10. This is a strange machine that defines
++ longs and ints as 8 bytes and shorts as 4 bytes.
++ Since I do evil things with long * that assume that they are 4
++ bytes. Look in the Makefile for the option to compile for
++ this machine. quad_cksum appears to have problems but I
++ will don't have the time to fix it right now, and this is not
++ a function that uses DES and so will not effect the main uses
++ of the library.
++
++Version 1.97 20/05/92 eay
++ Fixed the Imakefile and made some changes to des.h to fix some
++ problems when building this package with Kerberos v 4.
++
++Version 1.96 18/05/92 eay
++ Fixed a small bug in string_to_key() where problems could
++ occur if des_check_key was set to true and the string
++ generated a weak key.
++
++Patch2 posted to comp.sources.misc
++Version 1.95 13/05/92 eay
++ Added an alternative version of the D_ENCRYPT macro in
++ ecb_encrypt and fcrypt. Depending on the compiler, one version or the
++ other will be faster. This was inspired by
++ Dana How <how@isl.stanford.edu>, and her pointers about doing the
++ *(ulong *)((uchar *)ptr+(value&0xfc))
++ vs
++ ptr[value&0x3f]
++ to stop the C compiler doing a <<2 to convert the long array index.
++
++Version 1.94 05/05/92 eay
++ Fixed an incompatibility between my string_to_key and the MIT
++ version. When the key is longer than 8 chars, I was wrapping
++ with a different method. To use the old version, define
++ OLD_STR_TO_KEY in the makefile. Thanks to
++ viktor@newsu.shearson.com (Viktor Dukhovni).
++
++Version 1.93 28/04/92 eay
++ Fixed the VMS mods so that echo is now turned off in
++ read_password. Thanks again to brennan@coco.cchs.su.oz.AU.
++ MSDOS support added. The routines can be compiled with
++ Turbo C (v2.0) and MSC (v5.1). Make sure MSDOS is defined.
++
++Patch1 posted to comp.sources.misc
++Version 1.92 13/04/92 eay
++ Changed D_ENCRYPT so that the rotation of R occurs outside of
++ the loop. This required rotating all the longs in sp.h (now
++ called spr.h). Thanks to Richard Outerbridge <71755.204@CompuServe.COM>
++ speed.c has been changed so it will work without SIGALRM. If
++ times(3) is not present it will try to use ftime() instead.
++
++Version 1.91 08/04/92 eay
++ Added -E/-D options to des(1) so it can use string_to_key.
++ Added SVR4 mods suggested by witr@rwwa.COM
++ Added VMS mods suggested by brennan@coco.cchs.su.oz.AU. If
++ anyone knows how to turn of tty echo in VMS please tell me or
++ implement it yourself :-).
++ Changed FILE *IN/*OUT to *DES_IN/*DES_OUT since it appears VMS
++ does not like IN/OUT being used.
++
++Libdes posted to comp.sources.misc
++Version 1.9 24/03/92 eay
++ Now contains a fast small crypt replacement.
++ Added des(1) command.
++ Added des_rw_mode so people can use cbc encryption with
++ enc_read and enc_write.
++
++Version 1.8 15/10/91 eay
++ Bug in cbc_cksum.
++ Many thanks to Keith Reynolds (keithr@sco.COM) for pointing this
++ one out.
++
++Version 1.7 24/09/91 eay
++ Fixed set_key :-)
++ set_key is 4 times faster and takes less space.
++ There are a few minor changes that could be made.
++
++Version 1.6 19/09/1991 eay
++ Finally go IP and FP finished.
++ Now I need to fix set_key.
++ This version is quite a bit faster that 1.51
++
++Version 1.52 15/06/1991 eay
++ 20% speedup in ecb_encrypt by changing the E bit selection
++ to use 2 32bit words. This also required modification of the
++ sp table. There is still a way to speedup the IP and IP-1
++ (hints from outer@sq.com) still working on this one :-(.
++
++Version 1.51 07/06/1991 eay
++ Faster des_encrypt by loop unrolling
++ Fixed bug in quad_cksum.c (thanks to hughes@logos.ucs.indiana.edu)
++
++Version 1.50 28/05/1991 eay
++ Optimised the code a bit more for the sparc. I have improved the
++ speed of the inner des_encrypt by speeding up the initial and
++ final permutations.
++
++Version 1.40 23/10/1990 eay
++ Fixed des_random_key, it did not produce a random key :-(
++
++Version 1.30 2/10/1990 eay
++ Have made des_quad_cksum the same as MIT's, the full package
++ should be compatible with MIT's
++ Have tested on a DECstation 3100
++ Still need to fix des_set_key (make it faster).
++ Does des_cbc_encrypts at 70.5k/sec on a 3100.
++
++Version 1.20 18/09/1990 eay
++ Fixed byte order dependencies.
++ Fixed (I hope) all the word alignment problems.
++ Speedup in des_ecb_encrypt.
++
++Version 1.10 11/09/1990 eay
++ Added des_enc_read and des_enc_write.
++ Still need to fix des_quad_cksum.
++ Still need to document des_enc_read and des_enc_write.
++
++Version 1.00 27/08/1990 eay
++
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/crypto/ciphers/des/asm/crypt586.pl Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,204 @@
++#!/usr/bin/perl
++#
++# The inner loop instruction sequence and the IP/FP modifications are from
++# Svend Olaf Mikkelsen <svolaf@inet.uni-c.dk>
++# I've added the stuff needed for crypt() but I've not worried about making
++# things perfect.
++#
++
++push(@INC,"perlasm","../../perlasm");
++require "x86asm.pl";
++
++&asm_init($ARGV[0],"crypt586.pl");
++
++$L="edi";
++$R="esi";
++
++&external_label("des_SPtrans");
++&fcrypt_body("fcrypt_body");
++&asm_finish();
++
++sub fcrypt_body
++ {
++ local($name,$do_ip)=@_;
++
++ &function_begin($name,"EXTRN _des_SPtrans:DWORD");
++
++ &comment("");
++ &comment("Load the 2 words");
++ $ks="ebp";
++
++ &xor( $L, $L);
++ &xor( $R, $R);
++ &mov($ks,&wparam(1));
++
++ &push(25); # add a variable
++
++ &set_label("start");
++ for ($i=0; $i<16; $i+=2)
++ {
++ &comment("");
++ &comment("Round $i");
++ &D_ENCRYPT($i,$L,$R,$i*2,$ks,"des_SPtrans","eax","ebx","ecx","edx");
++
++ &comment("");
++ &comment("Round ".sprintf("%d",$i+1));
++ &D_ENCRYPT($i+1,$R,$L,($i+1)*2,$ks,"des_SPtrans","eax","ebx","ecx","edx");
++ }
++ &mov("ebx", &swtmp(0));
++ &mov("eax", $L);
++ &dec("ebx");
++ &mov($L, $R);
++ &mov($R, "eax");
++ &mov(&swtmp(0), "ebx");
++ &jnz(&label("start"));
++
++ &comment("");
++ &comment("FP");
++ &mov("edx",&wparam(0));
++
++ &FP_new($R,$L,"eax",3);
++ &mov(&DWP(0,"edx","",0),"eax");
++ &mov(&DWP(4,"edx","",0),$L);
++
++ &pop("ecx"); # remove variable
++
++ &function_end($name);
++ }
++
++sub D_ENCRYPT
++ {
++ local($r,$L,$R,$S,$ks,$desSP,$u,$tmp1,$tmp2,$t)=@_;
++
++ &mov( $u, &wparam(2)); # 2
++ &mov( $t, $R);
++ &shr( $t, 16); # 1
++ &mov( $tmp2, &wparam(3)); # 2
++ &xor( $t, $R); # 1
++
++ &and( $u, $t); # 2
++ &and( $t, $tmp2); # 2
++
++ &mov( $tmp1, $u);
++ &shl( $tmp1, 16); # 1
++ &mov( $tmp2, $t);
++ &shl( $tmp2, 16); # 1
++ &xor( $u, $tmp1); # 2
++ &xor( $t, $tmp2); # 2
++ &mov( $tmp1, &DWP(&n2a($S*4),$ks,"",0)); # 2
++ &xor( $u, $tmp1);
++ &mov( $tmp2, &DWP(&n2a(($S+1)*4),$ks,"",0)); # 2
++ &xor( $u, $R);
++ &xor( $t, $R);
++ &xor( $t, $tmp2);
++
++ &and( $u, "0xfcfcfcfc" ); # 2
++ &xor( $tmp1, $tmp1); # 1
++ &and( $t, "0xcfcfcfcf" ); # 2
++ &xor( $tmp2, $tmp2);
++ &movb( &LB($tmp1), &LB($u) );
++ &movb( &LB($tmp2), &HB($u) );
++ &rotr( $t, 4 );
++ &mov( $ks, &DWP(" $desSP",$tmp1,"",0));
++ &movb( &LB($tmp1), &LB($t) );
++ &xor( $L, $ks);
++ &mov( $ks, &DWP("0x200+$desSP",$tmp2,"",0));
++ &xor( $L, $ks);
++ &movb( &LB($tmp2), &HB($t) );
++ &shr( $u, 16);
++ &mov( $ks, &DWP("0x100+$desSP",$tmp1,"",0));
++ &xor( $L, $ks);
++ &movb( &LB($tmp1), &HB($u) );
++ &shr( $t, 16);
++ &mov( $ks, &DWP("0x300+$desSP",$tmp2,"",0));
++ &xor( $L, $ks);
++ &mov( $ks, &wparam(1));
++ &movb( &LB($tmp2), &HB($t) );
++ &and( $u, "0xff" );
++ &and( $t, "0xff" );
++ &mov( $tmp1, &DWP("0x600+$desSP",$tmp1,"",0));
++ &xor( $L, $tmp1);
++ &mov( $tmp1, &DWP("0x700+$desSP",$tmp2,"",0));
++ &xor( $L, $tmp1);
++ &mov( $tmp1, &DWP("0x400+$desSP",$u,"",0));
++ &xor( $L, $tmp1);
++ &mov( $tmp1, &DWP("0x500+$desSP",$t,"",0));
++ &xor( $L, $tmp1);
++ }
++
++sub n2a
++ {
++ sprintf("%d",$_[0]);
++ }
++
++# now has a side affect of rotating $a by $shift
++sub R_PERM_OP
++ {
++ local($a,$b,$tt,$shift,$mask,$last)=@_;
++
++ &rotl( $a, $shift ) if ($shift != 0);
++ &mov( $tt, $a );
++ &xor( $a, $b );
++ &and( $a, $mask );
++ if ($notlast eq $b)
++ {
++ &xor( $b, $a );
++ &xor( $tt, $a );
++ }
++ else
++ {
++ &xor( $tt, $a );
++ &xor( $b, $a );
++ }
++ &comment("");
++ }
++
++sub IP_new
++ {
++ local($l,$r,$tt,$lr)=@_;
++
++ &R_PERM_OP($l,$r,$tt, 4,"0xf0f0f0f0",$l);
++ &R_PERM_OP($r,$tt,$l,20,"0xfff0000f",$l);
++ &R_PERM_OP($l,$tt,$r,14,"0x33333333",$r);
++ &R_PERM_OP($tt,$r,$l,22,"0x03fc03fc",$r);
++ &R_PERM_OP($l,$r,$tt, 9,"0xaaaaaaaa",$r);
++
++ if ($lr != 3)
++ {
++ if (($lr-3) < 0)
++ { &rotr($tt, 3-$lr); }
++ else { &rotl($tt, $lr-3); }
++ }
++ if ($lr != 2)
++ {
++ if (($lr-2) < 0)
++ { &rotr($r, 2-$lr); }
++ else { &rotl($r, $lr-2); }
++ }
++ }
++
++sub FP_new
++ {
++ local($l,$r,$tt,$lr)=@_;
++
++ if ($lr != 2)
++ {
++ if (($lr-2) < 0)
++ { &rotl($r, 2-$lr); }
++ else { &rotr($r, $lr-2); }
++ }
++ if ($lr != 3)
++ {
++ if (($lr-3) < 0)
++ { &rotl($l, 3-$lr); }
++ else { &rotr($l, $lr-3); }
++ }
++
++ &R_PERM_OP($l,$r,$tt, 0,"0xaaaaaaaa",$r);
++ &R_PERM_OP($tt,$r,$l,23,"0x03fc03fc",$r);
++ &R_PERM_OP($l,$r,$tt,10,"0x33333333",$l);
++ &R_PERM_OP($r,$tt,$l,18,"0xfff0000f",$l);
++ &R_PERM_OP($l,$tt,$r,12,"0xf0f0f0f0",$r);
++ &rotr($tt , 4);
++ }
++
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/crypto/ciphers/des/asm/des-586.pl Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,251 @@
++#!/usr/bin/perl
++#
++# The inner loop instruction sequence and the IP/FP modifications are from
++# Svend Olaf Mikkelsen <svolaf@inet.uni-c.dk>
++#
++
++push(@INC,"perlasm","../../perlasm");
++require "x86asm.pl";
++require "cbc.pl";
++require "desboth.pl";
++
++# base code is in microsft
++# op dest, source
++# format.
++#
++
++&asm_init($ARGV[0],"des-586.pl");
++
++$L="edi";
++$R="esi";
++
++&external_label("des_SPtrans");
++&des_encrypt("des_encrypt",1);
++&des_encrypt("des_encrypt2",0);
++&des_encrypt3("des_encrypt3",1);
++&des_encrypt3("des_decrypt3",0);
++&cbc("des_ncbc_encrypt","des_encrypt","des_encrypt",0,4,5,3,5,-1);
++&cbc("des_ede3_cbc_encrypt","des_encrypt3","des_decrypt3",0,6,7,3,4,5);
++
++&asm_finish();
++
++sub des_encrypt
++ {
++ local($name,$do_ip)=@_;
++
++ &function_begin_B($name,"EXTRN _des_SPtrans:DWORD");
++
++ &push("esi");
++ &push("edi");
++
++ &comment("");
++ &comment("Load the 2 words");
++ $ks="ebp";
++
++ if ($do_ip)
++ {
++ &mov($R,&wparam(0));
++ &xor( "ecx", "ecx" );
++
++ &push("ebx");
++ &push("ebp");
++
++ &mov("eax",&DWP(0,$R,"",0));
++ &mov("ebx",&wparam(2)); # get encrypt flag
++ &mov($L,&DWP(4,$R,"",0));
++ &comment("");
++ &comment("IP");
++ &IP_new("eax",$L,$R,3);
++ }
++ else
++ {
++ &mov("eax",&wparam(0));
++ &xor( "ecx", "ecx" );
++
++ &push("ebx");
++ &push("ebp");
++
++ &mov($R,&DWP(0,"eax","",0));
++ &mov("ebx",&wparam(2)); # get encrypt flag
++ &rotl($R,3);
++ &mov($L,&DWP(4,"eax","",0));
++ &rotl($L,3);
++ }
++
++ &mov( $ks, &wparam(1) );
++ &cmp("ebx","0");
++ &je(&label("start_decrypt"));
++
++ for ($i=0; $i<16; $i+=2)
++ {
++ &comment("");
++ &comment("Round $i");
++ &D_ENCRYPT($i,$L,$R,$i*2,$ks,"des_SPtrans","eax","ebx","ecx","edx");
++
++ &comment("");
++ &comment("Round ".sprintf("%d",$i+1));
++ &D_ENCRYPT($i+1,$R,$L,($i+1)*2,$ks,"des_SPtrans","eax","ebx","ecx","edx");
++ }
++ &jmp(&label("end"));
++
++ &set_label("start_decrypt");
++
++ for ($i=15; $i>0; $i-=2)
++ {
++ &comment("");
++ &comment("Round $i");
++ &D_ENCRYPT(15-$i,$L,$R,$i*2,$ks,"des_SPtrans","eax","ebx","ecx","edx");
++ &comment("");
++ &comment("Round ".sprintf("%d",$i-1));
++ &D_ENCRYPT(15-$i+1,$R,$L,($i-1)*2,$ks,"des_SPtrans","eax","ebx","ecx","edx");
++ }
++
++ &set_label("end");
++
++ if ($do_ip)
++ {
++ &comment("");
++ &comment("FP");
++ &mov("edx",&wparam(0));
++ &FP_new($L,$R,"eax",3);
++
++ &mov(&DWP(0,"edx","",0),"eax");
++ &mov(&DWP(4,"edx","",0),$R);
++ }
++ else
++ {
++ &comment("");
++ &comment("Fixup");
++ &rotr($L,3); # r
++ &mov("eax",&wparam(0));
++ &rotr($R,3); # l
++ &mov(&DWP(0,"eax","",0),$L);
++ &mov(&DWP(4,"eax","",0),$R);
++ }
++
++ &pop("ebp");
++ &pop("ebx");
++ &pop("edi");
++ &pop("esi");
++ &ret();
++
++ &function_end_B($name);
++ }
++
++sub D_ENCRYPT
++ {
++ local($r,$L,$R,$S,$ks,$desSP,$u,$tmp1,$tmp2,$t)=@_;
++
++ &mov( $u, &DWP(&n2a($S*4),$ks,"",0));
++ &xor( $tmp1, $tmp1);
++ &mov( $t, &DWP(&n2a(($S+1)*4),$ks,"",0));
++ &xor( $u, $R);
++ &xor( $t, $R);
++ &and( $u, "0xfcfcfcfc" );
++ &and( $t, "0xcfcfcfcf" );
++ &movb( &LB($tmp1), &LB($u) );
++ &movb( &LB($tmp2), &HB($u) );
++ &rotr( $t, 4 );
++ &mov( $ks, &DWP(" $desSP",$tmp1,"",0));
++ &movb( &LB($tmp1), &LB($t) );
++ &xor( $L, $ks);
++ &mov( $ks, &DWP("0x200+$desSP",$tmp2,"",0));
++ &xor( $L, $ks); ######
++ &movb( &LB($tmp2), &HB($t) );
++ &shr( $u, 16);
++ &mov( $ks, &DWP("0x100+$desSP",$tmp1,"",0));
++ &xor( $L, $ks); ######
++ &movb( &LB($tmp1), &HB($u) );
++ &shr( $t, 16);
++ &mov( $ks, &DWP("0x300+$desSP",$tmp2,"",0));
++ &xor( $L, $ks);
++ &mov( $ks, &wparam(1) );
++ &movb( &LB($tmp2), &HB($t) );
++ &and( $u, "0xff" );
++ &and( $t, "0xff" );
++ &mov( $tmp1, &DWP("0x600+$desSP",$tmp1,"",0));
++ &xor( $L, $tmp1);
++ &mov( $tmp1, &DWP("0x700+$desSP",$tmp2,"",0));
++ &xor( $L, $tmp1);
++ &mov( $tmp1, &DWP("0x400+$desSP",$u,"",0));
++ &xor( $L, $tmp1);
++ &mov( $tmp1, &DWP("0x500+$desSP",$t,"",0));
++ &xor( $L, $tmp1);
++ }
++
++sub n2a
++ {
++ sprintf("%d",$_[0]);
++ }
++
++# now has a side affect of rotating $a by $shift
++sub R_PERM_OP
++ {
++ local($a,$b,$tt,$shift,$mask,$last)=@_;
++
++ &rotl( $a, $shift ) if ($shift != 0);
++ &mov( $tt, $a );
++ &xor( $a, $b );
++ &and( $a, $mask );
++ if (!$last eq $b)
++ {
++ &xor( $b, $a );
++ &xor( $tt, $a );
++ }
++ else
++ {
++ &xor( $tt, $a );
++ &xor( $b, $a );
++ }
++ &comment("");
++ }
++
++sub IP_new
++ {
++ local($l,$r,$tt,$lr)=@_;
++
++ &R_PERM_OP($l,$r,$tt, 4,"0xf0f0f0f0",$l);
++ &R_PERM_OP($r,$tt,$l,20,"0xfff0000f",$l);
++ &R_PERM_OP($l,$tt,$r,14,"0x33333333",$r);
++ &R_PERM_OP($tt,$r,$l,22,"0x03fc03fc",$r);
++ &R_PERM_OP($l,$r,$tt, 9,"0xaaaaaaaa",$r);
++
++ if ($lr != 3)
++ {
++ if (($lr-3) < 0)
++ { &rotr($tt, 3-$lr); }
++ else { &rotl($tt, $lr-3); }
++ }
++ if ($lr != 2)
++ {
++ if (($lr-2) < 0)
++ { &rotr($r, 2-$lr); }
++ else { &rotl($r, $lr-2); }
++ }
++ }
++
++sub FP_new
++ {
++ local($l,$r,$tt,$lr)=@_;
++
++ if ($lr != 2)
++ {
++ if (($lr-2) < 0)
++ { &rotl($r, 2-$lr); }
++ else { &rotr($r, $lr-2); }
++ }
++ if ($lr != 3)
++ {
++ if (($lr-3) < 0)
++ { &rotl($l, 3-$lr); }
++ else { &rotr($l, $lr-3); }
++ }
++
++ &R_PERM_OP($l,$r,$tt, 0,"0xaaaaaaaa",$r);
++ &R_PERM_OP($tt,$r,$l,23,"0x03fc03fc",$r);
++ &R_PERM_OP($l,$r,$tt,10,"0x33333333",$l);
++ &R_PERM_OP($r,$tt,$l,18,"0xfff0000f",$l);
++ &R_PERM_OP($l,$tt,$r,12,"0xf0f0f0f0",$r);
++ &rotr($tt , 4);
++ }
++
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/crypto/ciphers/des/asm/des686.pl Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,230 @@
++#!/usr/bin/perl
++
++$prog="des686.pl";
++
++# base code is in microsft
++# op dest, source
++# format.
++#
++
++# WILL NOT WORK ANYMORE WITH desboth.pl
++require "desboth.pl";
++
++if ( ($ARGV[0] eq "elf"))
++ { require "x86unix.pl"; }
++elsif ( ($ARGV[0] eq "a.out"))
++ { $aout=1; require "x86unix.pl"; }
++elsif ( ($ARGV[0] eq "sol"))
++ { $sol=1; require "x86unix.pl"; }
++elsif ( ($ARGV[0] eq "cpp"))
++ { $cpp=1; require "x86unix.pl"; }
++elsif ( ($ARGV[0] eq "win32"))
++ { require "x86ms.pl"; }
++else
++ {
++ print STDERR <<"EOF";
++Pick one target type from
++ elf - linux, FreeBSD etc
++ a.out - old linux
++ sol - x86 solaris
++ cpp - format so x86unix.cpp can be used
++ win32 - Windows 95/Windows NT
++EOF
++ exit(1);
++ }
++
++&comment("Don't even think of reading this code");
++&comment("It was automatically generated by $prog");
++&comment("Which is a perl program used to generate the x86 assember for");
++&comment("any of elf, a.out, Win32, or Solaris");
++&comment("It can be found in SSLeay 0.6.5+ or in libdes 3.26+");
++&comment("eric <eay\@cryptsoft.com>");
++&comment("");
++
++&file("dx86xxxx");
++
++$L="edi";
++$R="esi";
++
++&des_encrypt("des_encrypt",1);
++&des_encrypt("des_encrypt2",0);
++
++&des_encrypt3("des_encrypt3",1);
++&des_encrypt3("des_decrypt3",0);
++
++&file_end();
++
++sub des_encrypt
++ {
++ local($name,$do_ip)=@_;
++
++ &function_begin($name,"EXTRN _des_SPtrans:DWORD");
++
++ &comment("");
++ &comment("Load the 2 words");
++ &mov("eax",&wparam(0));
++ &mov($L,&DWP(0,"eax","",0));
++ &mov($R,&DWP(4,"eax","",0));
++
++ $ksp=&wparam(1);
++
++ if ($do_ip)
++ {
++ &comment("");
++ &comment("IP");
++ &IP_new($L,$R,"eax");
++ }
++
++ &comment("");
++ &comment("fixup rotate");
++ &rotl($R,3);
++ &rotl($L,3);
++ &exch($L,$R);
++
++ &comment("");
++ &comment("load counter, key_schedule and enc flag");
++ &mov("eax",&wparam(2)); # get encrypt flag
++ &mov("ebp",&wparam(1)); # get ks
++ &cmp("eax","0");
++ &je(&label("start_decrypt"));
++
++ # encrypting part
++
++ for ($i=0; $i<16; $i+=2)
++ {
++ &comment("");
++ &comment("Round $i");
++ &D_ENCRYPT($L,$R,$i*2,"ebp","des_SPtrans","ecx","edx","eax","ebx");
++
++ &comment("");
++ &comment("Round ".sprintf("%d",$i+1));
++ &D_ENCRYPT($R,$L,($i+1)*2,"ebp","des_SPtrans","ecx","edx","eax","ebx");
++ }
++ &jmp(&label("end"));
++
++ &set_label("start_decrypt");
++
++ for ($i=15; $i>0; $i-=2)
++ {
++ &comment("");
++ &comment("Round $i");
++ &D_ENCRYPT($L,$R,$i*2,"ebp","des_SPtrans","ecx","edx","eax","ebx");
++ &comment("");
++ &comment("Round ".sprintf("%d",$i-1));
++ &D_ENCRYPT($R,$L,($i-1)*2,"ebp","des_SPtrans","ecx","edx","eax","ebx");
++ }
++
++ &set_label("end");
++
++ &comment("");
++ &comment("Fixup");
++ &rotr($L,3); # r
++ &rotr($R,3); # l
++
++ if ($do_ip)
++ {
++ &comment("");
++ &comment("FP");
++ &FP_new($R,$L,"eax");
++ }
++
++ &mov("eax",&wparam(0));
++ &mov(&DWP(0,"eax","",0),$L);
++ &mov(&DWP(4,"eax","",0),$R);
++
++ &function_end($name);
++ }
++
++
++# The logic is to load R into 2 registers and operate on both at the same time.
++# We also load the 2 R's into 2 more registers so we can do the 'move word down a byte'
++# while also masking the other copy and doing a lookup. We then also accumulate the
++# L value in 2 registers then combine them at the end.
++sub D_ENCRYPT
++ {
++ local($L,$R,$S,$ks,$desSP,$u,$t,$tmp1,$tmp2,$tmp3)=@_;
++
++ &mov( $u, &DWP(&n2a($S*4),$ks,"",0));
++ &mov( $t, &DWP(&n2a(($S+1)*4),$ks,"",0));
++ &xor( $u, $R );
++ &xor( $t, $R );
++ &rotr( $t, 4 );
++
++ # the numbers at the end of the line are origional instruction order
++ &mov( $tmp2, $u ); # 1 2
++ &mov( $tmp1, $t ); # 1 1
++ &and( $tmp2, "0xfc" ); # 1 4
++ &and( $tmp1, "0xfc" ); # 1 3
++ &shr( $t, 8 ); # 1 5
++ &xor( $L, &DWP("0x100+$desSP",$tmp1,"",0)); # 1 7
++ &shr( $u, 8 ); # 1 6
++ &mov( $tmp1, &DWP(" $desSP",$tmp2,"",0)); # 1 8
++
++ &mov( $tmp2, $u ); # 2 2
++ &xor( $L, $tmp1 ); # 1 9
++ &and( $tmp2, "0xfc" ); # 2 4
++ &mov( $tmp1, $t ); # 2 1
++ &and( $tmp1, "0xfc" ); # 2 3
++ &shr( $t, 8 ); # 2 5
++ &xor( $L, &DWP("0x300+$desSP",$tmp1,"",0)); # 2 7
++ &shr( $u, 8 ); # 2 6
++ &mov( $tmp1, &DWP("0x200+$desSP",$tmp2,"",0)); # 2 8
++ &mov( $tmp2, $u ); # 3 2
++
++ &xor( $L, $tmp1 ); # 2 9
++ &and( $tmp2, "0xfc" ); # 3 4
++
++ &mov( $tmp1, $t ); # 3 1
++ &shr( $u, 8 ); # 3 6
++ &and( $tmp1, "0xfc" ); # 3 3
++ &shr( $t, 8 ); # 3 5
++ &xor( $L, &DWP("0x500+$desSP",$tmp1,"",0)); # 3 7
++ &mov( $tmp1, &DWP("0x400+$desSP",$tmp2,"",0)); # 3 8
++
++ &and( $t, "0xfc" ); # 4 1
++ &xor( $L, $tmp1 ); # 3 9
++
++ &and( $u, "0xfc" ); # 4 2
++ &xor( $L, &DWP("0x700+$desSP",$t,"",0)); # 4 3
++ &xor( $L, &DWP("0x600+$desSP",$u,"",0)); # 4 4
++ }
++
++sub PERM_OP
++ {
++ local($a,$b,$tt,$shift,$mask)=@_;
++
++ &mov( $tt, $a );
++ &shr( $tt, $shift );
++ &xor( $tt, $b );
++ &and( $tt, $mask );
++ &xor( $b, $tt );
++ &shl( $tt, $shift );
++ &xor( $a, $tt );
++ }
++
++sub IP_new
++ {
++ local($l,$r,$tt)=@_;
++
++ &PERM_OP($r,$l,$tt, 4,"0x0f0f0f0f");
++ &PERM_OP($l,$r,$tt,16,"0x0000ffff");
++ &PERM_OP($r,$l,$tt, 2,"0x33333333");
++ &PERM_OP($l,$r,$tt, 8,"0x00ff00ff");
++ &PERM_OP($r,$l,$tt, 1,"0x55555555");
++ }
++
++sub FP_new
++ {
++ local($l,$r,$tt)=@_;
++
++ &PERM_OP($l,$r,$tt, 1,"0x55555555");
++ &PERM_OP($r,$l,$tt, 8,"0x00ff00ff");
++ &PERM_OP($l,$r,$tt, 2,"0x33333333");
++ &PERM_OP($r,$l,$tt,16,"0x0000ffff");
++ &PERM_OP($l,$r,$tt, 4,"0x0f0f0f0f");
++ }
++
++sub n2a
++ {
++ sprintf("%d",$_[0]);
++ }
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/crypto/ciphers/des/asm/desboth.pl Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,79 @@
++#!/usr/bin/perl
++
++$L="edi";
++$R="esi";
++
++sub des_encrypt3
++ {
++ local($name,$enc)=@_;
++
++ &function_begin_B($name,"");
++ &push("ebx");
++ &mov("ebx",&wparam(0));
++
++ &push("ebp");
++ &push("esi");
++
++ &push("edi");
++
++ &comment("");
++ &comment("Load the data words");
++ &mov($L,&DWP(0,"ebx","",0));
++ &mov($R,&DWP(4,"ebx","",0));
++ &stack_push(3);
++
++ &comment("");
++ &comment("IP");
++ &IP_new($L,$R,"edx",0);
++
++ # put them back
++
++ if ($enc)
++ {
++ &mov(&DWP(4,"ebx","",0),$R);
++ &mov("eax",&wparam(1));
++ &mov(&DWP(0,"ebx","",0),"edx");
++ &mov("edi",&wparam(2));
++ &mov("esi",&wparam(3));
++ }
++ else
++ {
++ &mov(&DWP(4,"ebx","",0),$R);
++ &mov("esi",&wparam(1));
++ &mov(&DWP(0,"ebx","",0),"edx");
++ &mov("edi",&wparam(2));
++ &mov("eax",&wparam(3));
++ }
++ &mov(&swtmp(2), (($enc)?"1":"0"));
++ &mov(&swtmp(1), "eax");
++ &mov(&swtmp(0), "ebx");
++ &call("des_encrypt2");
++ &mov(&swtmp(2), (($enc)?"0":"1"));
++ &mov(&swtmp(1), "edi");
++ &mov(&swtmp(0), "ebx");
++ &call("des_encrypt2");
++ &mov(&swtmp(2), (($enc)?"1":"0"));
++ &mov(&swtmp(1), "esi");
++ &mov(&swtmp(0), "ebx");
++ &call("des_encrypt2");
++
++ &stack_pop(3);
++ &mov($L,&DWP(0,"ebx","",0));
++ &mov($R,&DWP(4,"ebx","",0));
++
++ &comment("");
++ &comment("FP");
++ &FP_new($L,$R,"eax",0);
++
++ &mov(&DWP(0,"ebx","",0),"eax");
++ &mov(&DWP(4,"ebx","",0),$R);
++
++ &pop("edi");
++ &pop("esi");
++ &pop("ebp");
++ &pop("ebx");
++ &ret();
++ &function_end_B($name);
++ }
++
++
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/crypto/ciphers/des/asm/perlasm/cbc.pl Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,342 @@
++#!/usr/bin/perl
++
++# void des_ncbc_encrypt(input, output, length, schedule, ivec, enc)
++# des_cblock (*input);
++# des_cblock (*output);
++# long length;
++# des_key_schedule schedule;
++# des_cblock (*ivec);
++# int enc;
++#
++# calls
++# des_encrypt((DES_LONG *)tin,schedule,DES_ENCRYPT);
++#
++
++#&cbc("des_ncbc_encrypt","des_encrypt",0);
++#&cbc("BF_cbc_encrypt","BF_encrypt","BF_encrypt",
++# 1,4,5,3,5,-1);
++#&cbc("des_ncbc_encrypt","des_encrypt","des_encrypt",
++# 0,4,5,3,5,-1);
++#&cbc("des_ede3_cbc_encrypt","des_encrypt3","des_decrypt3",
++# 0,6,7,3,4,5);
++#
++# When doing a cipher that needs bigendian order,
++# for encrypt, the iv is kept in bigendian form,
++# while for decrypt, it is kept in little endian.
++sub cbc
++ {
++ local($name,$enc_func,$dec_func,$swap,$iv_off,$enc_off,$p1,$p2,$p3)=@_;
++ # name is the function name
++ # enc_func and dec_func and the functions to call for encrypt/decrypt
++ # swap is true if byte order needs to be reversed
++ # iv_off is parameter number for the iv
++ # enc_off is parameter number for the encrypt/decrypt flag
++ # p1,p2,p3 are the offsets for parameters to be passed to the
++ # underlying calls.
++
++ &function_begin_B($name,"");
++ &comment("");
++
++ $in="esi";
++ $out="edi";
++ $count="ebp";
++
++ &push("ebp");
++ &push("ebx");
++ &push("esi");
++ &push("edi");
++
++ $data_off=4;
++ $data_off+=4 if ($p1 > 0);
++ $data_off+=4 if ($p2 > 0);
++ $data_off+=4 if ($p3 > 0);
++
++ &mov($count, &wparam(2)); # length
++
++ &comment("getting iv ptr from parameter $iv_off");
++ &mov("ebx", &wparam($iv_off)); # Get iv ptr
++
++ &mov($in, &DWP(0,"ebx","",0));# iv[0]
++ &mov($out, &DWP(4,"ebx","",0));# iv[1]
++
++ &push($out);
++ &push($in);
++ &push($out); # used in decrypt for iv[1]
++ &push($in); # used in decrypt for iv[0]
++
++ &mov("ebx", "esp"); # This is the address of tin[2]
++
++ &mov($in, &wparam(0)); # in
++ &mov($out, &wparam(1)); # out
++
++ # We have loaded them all, how lets push things
++ &comment("getting encrypt flag from parameter $enc_off");
++ &mov("ecx", &wparam($enc_off)); # Get enc flag
++ if ($p3 > 0)
++ {
++ &comment("get and push parameter $p3");
++ if ($enc_off != $p3)
++ { &mov("eax", &wparam($p3)); &push("eax"); }
++ else { &push("ecx"); }
++ }
++ if ($p2 > 0)
++ {
++ &comment("get and push parameter $p2");
++ if ($enc_off != $p2)
++ { &mov("eax", &wparam($p2)); &push("eax"); }
++ else { &push("ecx"); }
++ }
++ if ($p1 > 0)
++ {
++ &comment("get and push parameter $p1");
++ if ($enc_off != $p1)
++ { &mov("eax", &wparam($p1)); &push("eax"); }
++ else { &push("ecx"); }
++ }
++ &push("ebx"); # push data/iv
++
++ &cmp("ecx",0);
++ &jz(&label("decrypt"));
++
++ &and($count,0xfffffff8);
++ &mov("eax", &DWP($data_off,"esp","",0)); # load iv[0]
++ &mov("ebx", &DWP($data_off+4,"esp","",0)); # load iv[1]
++
++ &jz(&label("encrypt_finish"));
++
++ #############################################################
++
++ &set_label("encrypt_loop");
++ # encrypt start
++ # "eax" and "ebx" hold iv (or the last cipher text)
++
++ &mov("ecx", &DWP(0,$in,"",0)); # load first 4 bytes
++ &mov("edx", &DWP(4,$in,"",0)); # second 4 bytes
++
++ &xor("eax", "ecx");
++ &xor("ebx", "edx");
++
++ &bswap("eax") if $swap;
++ &bswap("ebx") if $swap;
++
++ &mov(&DWP($data_off,"esp","",0), "eax"); # put in array for call
++ &mov(&DWP($data_off+4,"esp","",0), "ebx"); #
++
++ &call($enc_func);
++
++ &mov("eax", &DWP($data_off,"esp","",0));
++ &mov("ebx", &DWP($data_off+4,"esp","",0));
++
++ &bswap("eax") if $swap;
++ &bswap("ebx") if $swap;
++
++ &mov(&DWP(0,$out,"",0),"eax");
++ &mov(&DWP(4,$out,"",0),"ebx");
++
++ # eax and ebx are the next iv.
++
++ &add($in, 8);
++ &add($out, 8);
++
++ &sub($count, 8);
++ &jnz(&label("encrypt_loop"));
++
++###################################################################3
++ &set_label("encrypt_finish");
++ &mov($count, &wparam(2)); # length
++ &and($count, 7);
++ &jz(&label("finish"));
++ &xor("ecx","ecx");
++ &xor("edx","edx");
++ &mov($count,&DWP(&label("cbc_enc_jmp_table"),"",$count,4));
++ &jmp_ptr($count);
++
++&set_label("ej7");
++ &xor("edx", "edx") if $ppro; # ppro friendly
++ &movb(&HB("edx"), &BP(6,$in,"",0));
++ &shl("edx",8);
++&set_label("ej6");
++ &movb(&HB("edx"), &BP(5,$in,"",0));
++&set_label("ej5");
++ &movb(&LB("edx"), &BP(4,$in,"",0));
++&set_label("ej4");
++ &mov("ecx", &DWP(0,$in,"",0));
++ &jmp(&label("ejend"));
++&set_label("ej3");
++ &movb(&HB("ecx"), &BP(2,$in,"",0));
++ &xor("ecx", "ecx") if $ppro; # ppro friendly
++ &shl("ecx",8);
++&set_label("ej2");
++ &movb(&HB("ecx"), &BP(1,$in,"",0));
++&set_label("ej1");
++ &movb(&LB("ecx"), &BP(0,$in,"",0));
++&set_label("ejend");
++
++ &xor("eax", "ecx");
++ &xor("ebx", "edx");
++
++ &bswap("eax") if $swap;
++ &bswap("ebx") if $swap;
++
++ &mov(&DWP($data_off,"esp","",0), "eax"); # put in array for call
++ &mov(&DWP($data_off+4,"esp","",0), "ebx"); #
++
++ &call($enc_func);
++
++ &mov("eax", &DWP($data_off,"esp","",0));
++ &mov("ebx", &DWP($data_off+4,"esp","",0));
++
++ &bswap("eax") if $swap;
++ &bswap("ebx") if $swap;
++
++ &mov(&DWP(0,$out,"",0),"eax");
++ &mov(&DWP(4,$out,"",0),"ebx");
++
++ &jmp(&label("finish"));
++
++ #############################################################
++ #############################################################
++ &set_label("decrypt",1);
++ # decrypt start
++ &and($count,0xfffffff8);
++ # The next 2 instructions are only for if the jz is taken
++ &mov("eax", &DWP($data_off+8,"esp","",0)); # get iv[0]
++ &mov("ebx", &DWP($data_off+12,"esp","",0)); # get iv[1]
++ &jz(&label("decrypt_finish"));
++
++ &set_label("decrypt_loop");
++ &mov("eax", &DWP(0,$in,"",0)); # load first 4 bytes
++ &mov("ebx", &DWP(4,$in,"",0)); # second 4 bytes
++
++ &bswap("eax") if $swap;
++ &bswap("ebx") if $swap;
++
++ &mov(&DWP($data_off,"esp","",0), "eax"); # put back
++ &mov(&DWP($data_off+4,"esp","",0), "ebx"); #
++
++ &call($dec_func);
++
++ &mov("eax", &DWP($data_off,"esp","",0)); # get return
++ &mov("ebx", &DWP($data_off+4,"esp","",0)); #
++
++ &bswap("eax") if $swap;
++ &bswap("ebx") if $swap;
++
++ &mov("ecx", &DWP($data_off+8,"esp","",0)); # get iv[0]
++ &mov("edx", &DWP($data_off+12,"esp","",0)); # get iv[1]
++
++ &xor("ecx", "eax");
++ &xor("edx", "ebx");
++
++ &mov("eax", &DWP(0,$in,"",0)); # get old cipher text,
++ &mov("ebx", &DWP(4,$in,"",0)); # next iv actually
++
++ &mov(&DWP(0,$out,"",0),"ecx");
++ &mov(&DWP(4,$out,"",0),"edx");
++
++ &mov(&DWP($data_off+8,"esp","",0), "eax"); # save iv
++ &mov(&DWP($data_off+12,"esp","",0), "ebx"); #
++
++ &add($in, 8);
++ &add($out, 8);
++
++ &sub($count, 8);
++ &jnz(&label("decrypt_loop"));
++############################ ENDIT #######################3
++ &set_label("decrypt_finish");
++ &mov($count, &wparam(2)); # length
++ &and($count, 7);
++ &jz(&label("finish"));
++
++ &mov("eax", &DWP(0,$in,"",0)); # load first 4 bytes
++ &mov("ebx", &DWP(4,$in,"",0)); # second 4 bytes
++
++ &bswap("eax") if $swap;
++ &bswap("ebx") if $swap;
++
++ &mov(&DWP($data_off,"esp","",0), "eax"); # put back
++ &mov(&DWP($data_off+4,"esp","",0), "ebx"); #
++
++ &call($dec_func);
++
++ &mov("eax", &DWP($data_off,"esp","",0)); # get return
++ &mov("ebx", &DWP($data_off+4,"esp","",0)); #
++
++ &bswap("eax") if $swap;
++ &bswap("ebx") if $swap;
++
++ &mov("ecx", &DWP($data_off+8,"esp","",0)); # get iv[0]
++ &mov("edx", &DWP($data_off+12,"esp","",0)); # get iv[1]
++
++ &xor("ecx", "eax");
++ &xor("edx", "ebx");
++
++ # this is for when we exit
++ &mov("eax", &DWP(0,$in,"",0)); # get old cipher text,
++ &mov("ebx", &DWP(4,$in,"",0)); # next iv actually
++
++&set_label("dj7");
++ &rotr("edx", 16);
++ &movb(&BP(6,$out,"",0), &LB("edx"));
++ &shr("edx",16);
++&set_label("dj6");
++ &movb(&BP(5,$out,"",0), &HB("edx"));
++&set_label("dj5");
++ &movb(&BP(4,$out,"",0), &LB("edx"));
++&set_label("dj4");
++ &mov(&DWP(0,$out,"",0), "ecx");
++ &jmp(&label("djend"));
++&set_label("dj3");
++ &rotr("ecx", 16);
++ &movb(&BP(2,$out,"",0), &LB("ecx"));
++ &shl("ecx",16);
++&set_label("dj2");
++ &movb(&BP(1,$in,"",0), &HB("ecx"));
++&set_label("dj1");
++ &movb(&BP(0,$in,"",0), &LB("ecx"));
++&set_label("djend");
++
++ # final iv is still in eax:ebx
++ &jmp(&label("finish"));
++
++
++############################ FINISH #######################3
++ &set_label("finish",1);
++ &mov("ecx", &wparam($iv_off)); # Get iv ptr
++
++ #################################################
++ $total=16+4;
++ $total+=4 if ($p1 > 0);
++ $total+=4 if ($p2 > 0);
++ $total+=4 if ($p3 > 0);
++ &add("esp",$total);
++
++ &mov(&DWP(0,"ecx","",0), "eax"); # save iv
++ &mov(&DWP(4,"ecx","",0), "ebx"); # save iv
++
++ &function_end_A($name);
++
++ &set_label("cbc_enc_jmp_table",1);
++ &data_word("0");
++ &data_word(&label("ej1"));
++ &data_word(&label("ej2"));
++ &data_word(&label("ej3"));
++ &data_word(&label("ej4"));
++ &data_word(&label("ej5"));
++ &data_word(&label("ej6"));
++ &data_word(&label("ej7"));
++ &set_label("cbc_dec_jmp_table",1);
++ &data_word("0");
++ &data_word(&label("dj1"));
++ &data_word(&label("dj2"));
++ &data_word(&label("dj3"));
++ &data_word(&label("dj4"));
++ &data_word(&label("dj5"));
++ &data_word(&label("dj6"));
++ &data_word(&label("dj7"));
++
++ &function_end_B($name);
++
++ }
++
++1;
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/crypto/ciphers/des/asm/perlasm/readme Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,124 @@
++The perl scripts in this directory are my 'hack' to generate
++multiple different assembler formats via the one origional script.
++
++The way to use this library is to start with adding the path to this directory
++and then include it.
++
++push(@INC,"perlasm","../../perlasm");
++require "x86asm.pl";
++
++The first thing we do is setup the file and type of assember
++
++&asm_init($ARGV[0],$0);
++
++The first argument is the 'type'. Currently
++'cpp', 'sol', 'a.out', 'elf' or 'win32'.
++Argument 2 is the file name.
++
++The reciprocal function is
++&asm_finish() which should be called at the end.
++
++There are 2 main 'packages'. x86ms.pl, which is the microsoft assembler,
++and x86unix.pl which is the unix (gas) version.
++
++Functions of interest are:
++&external_label("des_SPtrans"); declare and external variable
++&LB(reg); Low byte for a register
++&HB(reg); High byte for a register
++&BP(off,base,index,scale) Byte pointer addressing
++&DWP(off,base,index,scale) Word pointer addressing
++&stack_push(num) Basically a 'sub esp, num*4' with extra
++&stack_pop(num) inverse of stack_push
++&function_begin(name,extra) Start a function with pushing of
++ edi, esi, ebx and ebp. extra is extra win32
++ external info that may be required.
++&function_begin_B(name,extra) Same as norma function_begin but no pushing.
++&function_end(name) Call at end of function.
++&function_end_A(name) Standard pop and ret, for use inside functions
++&function_end_B(name) Call at end but with poping or 'ret'.
++&swtmp(num) Address on stack temp word.
++&wparam(num) Parameter number num, that was push
++ in C convention. This all works over pushes
++ and pops.
++&comment("hello there") Put in a comment.
++&label("loop") Refer to a label, normally a jmp target.
++&set_label("loop") Set a label at this point.
++&data_word(word) Put in a word of data.
++
++So how does this all hold together? Given
++
++int calc(int len, int *data)
++ {
++ int i,j=0;
++
++ for (i=0; i<len; i++)
++ {
++ j+=other(data[i]);
++ }
++ }
++
++So a very simple version of this function could be coded as
++
++ push(@INC,"perlasm","../../perlasm");
++ require "x86asm.pl";
++
++ &asm_init($ARGV[0],"cacl.pl");
++
++ &external_label("other");
++
++ $tmp1= "eax";
++ $j= "edi";
++ $data= "esi";
++ $i= "ebp";
++
++ &comment("a simple function");
++ &function_begin("calc");
++ &mov( $data, &wparam(1)); # data
++ &xor( $j, $j);
++ &xor( $i, $i);
++
++ &set_label("loop");
++ &cmp( $i, &wparam(0));
++ &jge( &label("end"));
++
++ &mov( $tmp1, &DWP(0,$data,$i,4));
++ &push( $tmp1);
++ &call( "other");
++ &add( $j, "eax");
++ &pop( $tmp1);
++ &inc( $i);
++ &jmp( &label("loop"));
++
++ &set_label("end");
++ &mov( "eax", $j);
++
++ &function_end("calc");
++
++ &asm_finish();
++
++The above example is very very unoptimised but gives an idea of how
++things work.
++
++There is also a cbc mode function generator in cbc.pl
++
++&cbc( $name,
++ $encrypt_function_name,
++ $decrypt_function_name,
++ $true_if_byte_swap_needed,
++ $parameter_number_for_iv,
++ $parameter_number_for_encrypt_flag,
++ $first_parameter_to_pass,
++ $second_parameter_to_pass,
++ $third_parameter_to_pass);
++
++So for example, given
++void BF_encrypt(BF_LONG *data,BF_KEY *key);
++void BF_decrypt(BF_LONG *data,BF_KEY *key);
++void BF_cbc_encrypt(unsigned char *in, unsigned char *out, long length,
++ BF_KEY *ks, unsigned char *iv, int enc);
++
++&cbc("BF_cbc_encrypt","BF_encrypt","BF_encrypt",1,4,5,3,-1,-1);
++
++&cbc("des_ncbc_encrypt","des_encrypt","des_encrypt",0,4,5,3,5,-1);
++&cbc("des_ede3_cbc_encrypt","des_encrypt3","des_decrypt3",0,6,7,3,4,5);
++
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/crypto/ciphers/des/asm/perlasm/x86asm.pl Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,111 @@
++#!/usr/bin/perl
++
++# require 'x86asm.pl';
++# &asm_init("cpp","des-586.pl");
++# XXX
++# XXX
++# main'asm_finish
++
++sub main'asm_finish
++ {
++ &file_end();
++ &asm_finish_cpp() if $cpp;
++ print &asm_get_output();
++ }
++
++sub main'asm_init
++ {
++ ($type,$fn)=@_;
++ $filename=$fn;
++
++ $cpp=$sol=$aout=$win32=0;
++ if ( ($type eq "elf"))
++ { require "x86unix.pl"; }
++ elsif ( ($type eq "a.out"))
++ { $aout=1; require "x86unix.pl"; }
++ elsif ( ($type eq "sol"))
++ { $sol=1; require "x86unix.pl"; }
++ elsif ( ($type eq "cpp"))
++ { $cpp=1; require "x86unix.pl"; }
++ elsif ( ($type eq "win32"))
++ { $win32=1; require "x86ms.pl"; }
++ else
++ {
++ print STDERR <<"EOF";
++Pick one target type from
++ elf - linux, FreeBSD etc
++ a.out - old linux
++ sol - x86 solaris
++ cpp - format so x86unix.cpp can be used
++ win32 - Windows 95/Windows NT
++EOF
++ exit(1);
++ }
++
++ &asm_init_output();
++
++&comment("Don't even think of reading this code");
++&comment("It was automatically generated by $filename");
++&comment("Which is a perl program used to generate the x86 assember for");
++&comment("any of elf, a.out, BSDI,Win32, or Solaris");
++&comment("eric <eay\@cryptsoft.com>");
++&comment("");
++
++ $filename =~ s/\.pl$//;
++ &file($filename);
++ }
++
++sub asm_finish_cpp
++ {
++ return unless $cpp;
++
++ local($tmp,$i);
++ foreach $i (&get_labels())
++ {
++ $tmp.="#define $i _$i\n";
++ }
++ print <<"EOF";
++/* Run the C pre-processor over this file with one of the following defined
++ * ELF - elf object files,
++ * OUT - a.out object files,
++ * BSDI - BSDI style a.out object files
++ * SOL - Solaris style elf
++ */
++
++#define TYPE(a,b) .type a,b
++#define SIZE(a,b) .size a,b
++
++#if defined(OUT) || defined(BSDI)
++$tmp
++#endif
++
++#ifdef OUT
++#define OK 1
++#define ALIGN 4
++#endif
++
++#ifdef BSDI
++#define OK 1
++#define ALIGN 4
++#undef SIZE
++#undef TYPE
++#endif
++
++#if defined(ELF) || defined(SOL)
++#define OK 1
++#define ALIGN 16
++#endif
++
++#ifndef OK
++You need to define one of
++ELF - elf systems - linux-elf, NetBSD and DG-UX
++OUT - a.out systems - linux-a.out and FreeBSD
++SOL - solaris systems, which are elf with strange comment lines
++BSDI - a.out with a very primative version of as.
++#endif
++
++/* Let the Assembler begin :-) */
++EOF
++ }
++
++1;
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/crypto/ciphers/des/asm/perlasm/x86ms.pl Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,345 @@
++#!/usr/bin/perl
++
++package x86ms;
++
++$label="L000";
++
++%lb=( 'eax', 'al',
++ 'ebx', 'bl',
++ 'ecx', 'cl',
++ 'edx', 'dl',
++ 'ax', 'al',
++ 'bx', 'bl',
++ 'cx', 'cl',
++ 'dx', 'dl',
++ );
++
++%hb=( 'eax', 'ah',
++ 'ebx', 'bh',
++ 'ecx', 'ch',
++ 'edx', 'dh',
++ 'ax', 'ah',
++ 'bx', 'bh',
++ 'cx', 'ch',
++ 'dx', 'dh',
++ );
++
++sub main'asm_init_output { @out=(); }
++sub main'asm_get_output { return(@out); }
++sub main'get_labels { return(@labels); }
++sub main'external_label { push(@labels,@_); }
++
++sub main'LB
++ {
++ (defined($lb{$_[0]})) || die "$_[0] does not have a 'low byte'\n";
++ return($lb{$_[0]});
++ }
++
++sub main'HB
++ {
++ (defined($hb{$_[0]})) || die "$_[0] does not have a 'high byte'\n";
++ return($hb{$_[0]});
++ }
++
++sub main'BP
++ {
++ &get_mem("BYTE",@_);
++ }
++
++sub main'DWP
++ {
++ &get_mem("DWORD",@_);
++ }
++
++sub main'stack_push
++ {
++ local($num)=@_;
++ $stack+=$num*4;
++ &main'sub("esp",$num*4);
++ }
++
++sub main'stack_pop
++ {
++ local($num)=@_;
++ $stack-=$num*4;
++ &main'add("esp",$num*4);
++ }
++
++sub get_mem
++ {
++ local($size,$addr,$reg1,$reg2,$idx)=@_;
++ local($t,$post);
++ local($ret)="$size PTR ";
++
++ $addr =~ s/^\s+//;
++ if ($addr =~ /^(.+)\+(.+)$/)
++ {
++ $reg2=&conv($1);
++ $addr="_$2";
++ }
++ elsif ($addr =~ /^[_a-zA-Z]/)
++ {
++ $addr="_$addr";
++ }
++
++ $reg1="$regs{$reg1}" if defined($regs{$reg1});
++ $reg2="$regs{$reg2}" if defined($regs{$reg2});
++ if (($addr ne "") && ($addr ne 0))
++ {
++ if ($addr !~ /^-/)
++ { $ret.=$addr; }
++ else { $post=$addr; }
++ }
++ if ($reg2 ne "")
++ {
++ $t="";
++ $t="*$idx" if ($idx != 0);
++ $reg1="+".$reg1 if ("$reg1$post" ne "");
++ $ret.="[$reg2$t$reg1$post]";
++ }
++ else
++ {
++ $ret.="[$reg1$post]"
++ }
++ return($ret);
++ }
++
++sub main'mov { &out2("mov",@_); }
++sub main'movb { &out2("mov",@_); }
++sub main'and { &out2("and",@_); }
++sub main'or { &out2("or",@_); }
++sub main'shl { &out2("shl",@_); }
++sub main'shr { &out2("shr",@_); }
++sub main'xor { &out2("xor",@_); }
++sub main'xorb { &out2("xor",@_); }
++sub main'add { &out2("add",@_); }
++sub main'adc { &out2("adc",@_); }
++sub main'sub { &out2("sub",@_); }
++sub main'rotl { &out2("rol",@_); }
++sub main'rotr { &out2("ror",@_); }
++sub main'exch { &out2("xchg",@_); }
++sub main'cmp { &out2("cmp",@_); }
++sub main'lea { &out2("lea",@_); }
++sub main'mul { &out1("mul",@_); }
++sub main'div { &out1("div",@_); }
++sub main'dec { &out1("dec",@_); }
++sub main'inc { &out1("inc",@_); }
++sub main'jmp { &out1("jmp",@_); }
++sub main'jmp_ptr { &out1p("jmp",@_); }
++sub main'je { &out1("je",@_); }
++sub main'jle { &out1("jle",@_); }
++sub main'jz { &out1("jz",@_); }
++sub main'jge { &out1("jge",@_); }
++sub main'jl { &out1("jl",@_); }
++sub main'jb { &out1("jb",@_); }
++sub main'jnz { &out1("jnz",@_); }
++sub main'jne { &out1("jne",@_); }
++sub main'push { &out1("push",@_); $stack+=4; }
++sub main'pop { &out1("pop",@_); $stack-=4; }
++sub main'bswap { &out1("bswap",@_); &using486(); }
++sub main'not { &out1("not",@_); }
++sub main'call { &out1("call",'_'.$_[0]); }
++sub main'ret { &out0("ret"); }
++sub main'nop { &out0("nop"); }
++
++sub out2
++ {
++ local($name,$p1,$p2)=@_;
++ local($l,$t);
++
++ push(@out,"\t$name\t");
++ $t=&conv($p1).",";
++ $l=length($t);
++ push(@out,$t);
++ $l=4-($l+9)/8;
++ push(@out,"\t" x $l);
++ push(@out,&conv($p2));
++ push(@out,"\n");
++ }
++
++sub out0
++ {
++ local($name)=@_;
++
++ push(@out,"\t$name\n");
++ }
++
++sub out1
++ {
++ local($name,$p1)=@_;
++ local($l,$t);
++
++ push(@out,"\t$name\t".&conv($p1)."\n");
++ }
++
++sub conv
++ {
++ local($p)=@_;
++
++ $p =~ s/0x([0-9A-Fa-f]+)/0$1h/;
++ return $p;
++ }
++
++sub using486
++ {
++ return if $using486;
++ $using486++;
++ grep(s/\.386/\.486/,@out);
++ }
++
++sub main'file
++ {
++ local($file)=@_;
++
++ local($tmp)=<<"EOF";
++ TITLE $file.asm
++ .386
++.model FLAT
++EOF
++ push(@out,$tmp);
++ }
++
++sub main'function_begin
++ {
++ local($func,$extra)=@_;
++
++ push(@labels,$func);
++
++ local($tmp)=<<"EOF";
++_TEXT SEGMENT
++PUBLIC _$func
++$extra
++_$func PROC NEAR
++ push ebp
++ push ebx
++ push esi
++ push edi
++EOF
++ push(@out,$tmp);
++ $stack=20;
++ }
++
++sub main'function_begin_B
++ {
++ local($func,$extra)=@_;
++
++ local($tmp)=<<"EOF";
++_TEXT SEGMENT
++PUBLIC _$func
++$extra
++_$func PROC NEAR
++EOF
++ push(@out,$tmp);
++ $stack=4;
++ }
++
++sub main'function_end
++ {
++ local($func)=@_;
++
++ local($tmp)=<<"EOF";
++ pop edi
++ pop esi
++ pop ebx
++ pop ebp
++ ret
++_$func ENDP
++_TEXT ENDS
++EOF
++ push(@out,$tmp);
++ $stack=0;
++ %label=();
++ }
++
++sub main'function_end_B
++ {
++ local($func)=@_;
++
++ local($tmp)=<<"EOF";
++_$func ENDP
++_TEXT ENDS
++EOF
++ push(@out,$tmp);
++ $stack=0;
++ %label=();
++ }
++
++sub main'function_end_A
++ {
++ local($func)=@_;
++
++ local($tmp)=<<"EOF";
++ pop edi
++ pop esi
++ pop ebx
++ pop ebp
++ ret
++EOF
++ push(@out,$tmp);
++ }
++
++sub main'file_end
++ {
++ push(@out,"END\n");
++ }
++
++sub main'wparam
++ {
++ local($num)=@_;
++
++ return(&main'DWP($stack+$num*4,"esp","",0));
++ }
++
++sub main'swtmp
++ {
++ return(&main'DWP($_[0]*4,"esp","",0));
++ }
++
++# Should use swtmp, which is above esp. Linix can trash the stack above esp
++#sub main'wtmp
++# {
++# local($num)=@_;
++#
++# return(&main'DWP(-(($num+1)*4),"esp","",0));
++# }
++
++sub main'comment
++ {
++ foreach (@_)
++ {
++ push(@out,"\t; $_\n");
++ }
++ }
++
++sub main'label
++ {
++ if (!defined($label{$_[0]}))
++ {
++ $label{$_[0]}="\$${label}${_[0]}";
++ $label++;
++ }
++ return($label{$_[0]});
++ }
++
++sub main'set_label
++ {
++ if (!defined($label{$_[0]}))
++ {
++ $label{$_[0]}="${label}${_[0]}";
++ $label++;
++ }
++ push(@out,"$label{$_[0]}:\n");
++ }
++
++sub main'data_word
++ {
++ push(@out,"\tDD\t$_[0]\n");
++ }
++
++sub out1p
++ {
++ local($name,$p1)=@_;
++ local($l,$t);
++
++ push(@out,"\t$name\t ".&conv($p1)."\n");
++ }
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/crypto/ciphers/des/asm/perlasm/x86unix.pl Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,403 @@
++#!/usr/bin/perl
++
++package x86unix;
++
++$label="L000";
++
++$align=($main'aout)?"4":"16";
++$under=($main'aout)?"_":"";
++$com_start=($main'sol)?"/":"#";
++
++sub main'asm_init_output { @out=(); }
++sub main'asm_get_output { return(@out); }
++sub main'get_labels { return(@labels); }
++sub main'external_label { push(@labels,@_); }
++
++if ($main'cpp)
++ {
++ $align="ALIGN";
++ $under="";
++ $com_start='/*';
++ $com_end='*/';
++ }
++
++%lb=( 'eax', '%al',
++ 'ebx', '%bl',
++ 'ecx', '%cl',
++ 'edx', '%dl',
++ 'ax', '%al',
++ 'bx', '%bl',
++ 'cx', '%cl',
++ 'dx', '%dl',
++ );
++
++%hb=( 'eax', '%ah',
++ 'ebx', '%bh',
++ 'ecx', '%ch',
++ 'edx', '%dh',
++ 'ax', '%ah',
++ 'bx', '%bh',
++ 'cx', '%ch',
++ 'dx', '%dh',
++ );
++
++%regs=( 'eax', '%eax',
++ 'ebx', '%ebx',
++ 'ecx', '%ecx',
++ 'edx', '%edx',
++ 'esi', '%esi',
++ 'edi', '%edi',
++ 'ebp', '%ebp',
++ 'esp', '%esp',
++ );
++
++%reg_val=(
++ 'eax', 0x00,
++ 'ebx', 0x03,
++ 'ecx', 0x01,
++ 'edx', 0x02,
++ 'esi', 0x06,
++ 'edi', 0x07,
++ 'ebp', 0x05,
++ 'esp', 0x04,
++ );
++
++sub main'LB
++ {
++ (defined($lb{$_[0]})) || die "$_[0] does not have a 'low byte'\n";
++ return($lb{$_[0]});
++ }
++
++sub main'HB
++ {
++ (defined($hb{$_[0]})) || die "$_[0] does not have a 'high byte'\n";
++ return($hb{$_[0]});
++ }
++
++sub main'DWP
++ {
++ local($addr,$reg1,$reg2,$idx)=@_;
++
++ $ret="";
++ $addr =~ s/(^|[+ \t])([A-Za-z_]+)($|[+ \t])/$1$under$2$3/;
++ $reg1="$regs{$reg1}" if defined($regs{$reg1});
++ $reg2="$regs{$reg2}" if defined($regs{$reg2});
++ $ret.=$addr if ($addr ne "") && ($addr ne 0);
++ if ($reg2 ne "")
++ { $ret.="($reg1,$reg2,$idx)"; }
++ else
++ { $ret.="($reg1)" }
++ return($ret);
++ }
++
++sub main'BP
++ {
++ return(&main'DWP(@_));
++ }
++
++#sub main'BP
++# {
++# local($addr,$reg1,$reg2,$idx)=@_;
++#
++# $ret="";
++#
++# $addr =~ s/(^|[+ \t])([A-Za-z_]+)($|[+ \t])/$1$under$2$3/;
++# $reg1="$regs{$reg1}" if defined($regs{$reg1});
++# $reg2="$regs{$reg2}" if defined($regs{$reg2});
++# $ret.=$addr if ($addr ne "") && ($addr ne 0);
++# if ($reg2 ne "")
++# { $ret.="($reg1,$reg2,$idx)"; }
++# else
++# { $ret.="($reg1)" }
++# return($ret);
++# }
++
++sub main'mov { &out2("movl",@_); }
++sub main'movb { &out2("movb",@_); }
++sub main'and { &out2("andl",@_); }
++sub main'or { &out2("orl",@_); }
++sub main'shl { &out2("sall",@_); }
++sub main'shr { &out2("shrl",@_); }
++sub main'xor { &out2("xorl",@_); }
++sub main'xorb { &out2("xorb",@_); }
++sub main'add { &out2("addl",@_); }
++sub main'adc { &out2("adcl",@_); }
++sub main'sub { &out2("subl",@_); }
++sub main'rotl { &out2("roll",@_); }
++sub main'rotr { &out2("rorl",@_); }
++sub main'exch { &out2("xchg",@_); }
++sub main'cmp { &out2("cmpl",@_); }
++sub main'lea { &out2("leal",@_); }
++sub main'mul { &out1("mull",@_); }
++sub main'div { &out1("divl",@_); }
++sub main'jmp { &out1("jmp",@_); }
++sub main'jmp_ptr { &out1p("jmp",@_); }
++sub main'je { &out1("je",@_); }
++sub main'jle { &out1("jle",@_); }
++sub main'jne { &out1("jne",@_); }
++sub main'jnz { &out1("jnz",@_); }
++sub main'jz { &out1("jz",@_); }
++sub main'jge { &out1("jge",@_); }
++sub main'jl { &out1("jl",@_); }
++sub main'jb { &out1("jb",@_); }
++sub main'dec { &out1("decl",@_); }
++sub main'inc { &out1("incl",@_); }
++sub main'push { &out1("pushl",@_); $stack+=4; }
++sub main'pop { &out1("popl",@_); $stack-=4; }
++sub main'bswap { &out1("bswapl",@_); }
++sub main'not { &out1("notl",@_); }
++sub main'call { &out1("call",$under.$_[0]); }
++sub main'ret { &out0("ret"); }
++sub main'nop { &out0("nop"); }
++
++sub out2
++ {
++ local($name,$p1,$p2)=@_;
++ local($l,$ll,$t);
++ local(%special)=( "roll",0xD1C0,"rorl",0xD1C8,
++ "rcll",0xD1D0,"rcrl",0xD1D8,
++ "shll",0xD1E0,"shrl",0xD1E8,
++ "sarl",0xD1F8);
++
++ if ((defined($special{$name})) && defined($regs{$p1}) && ($p2 == 1))
++ {
++ $op=$special{$name}|$reg_val{$p1};
++ $tmp1=sprintf ".byte %d\n",($op>>8)&0xff;
++ $tmp2=sprintf ".byte %d\t",$op &0xff;
++ push(@out,$tmp1);
++ push(@out,$tmp2);
++
++ $p2=&conv($p2);
++ $p1=&conv($p1);
++ &main'comment("$name $p2 $p1");
++ return;
++ }
++
++ push(@out,"\t$name\t");
++ $t=&conv($p2).",";
++ $l=length($t);
++ push(@out,$t);
++ $ll=4-($l+9)/8;
++ $tmp1=sprintf "\t" x $ll;
++ push(@out,$tmp1);
++ push(@out,&conv($p1)."\n");
++ }
++
++sub out1
++ {
++ local($name,$p1)=@_;
++ local($l,$t);
++
++ push(@out,"\t$name\t".&conv($p1)."\n");
++ }
++
++sub out1p
++ {
++ local($name,$p1)=@_;
++ local($l,$t);
++
++ push(@out,"\t$name\t*".&conv($p1)."\n");
++ }
++
++sub out0
++ {
++ push(@out,"\t$_[0]\n");
++ }
++
++sub conv
++ {
++ local($p)=@_;
++
++# $p =~ s/0x([0-9A-Fa-f]+)/0$1h/;
++
++ $p=$regs{$p} if (defined($regs{$p}));
++
++ $p =~ s/^(-{0,1}[0-9A-Fa-f]+)$/\$$1/;
++ $p =~ s/^(0x[0-9A-Fa-f]+)$/\$$1/;
++ return $p;
++ }
++
++sub main'file
++ {
++ local($file)=@_;
++
++ local($tmp)=<<"EOF";
++ .file "$file.s"
++ .version "01.01"
++gcc2_compiled.:
++EOF
++ push(@out,$tmp);
++ }
++
++sub main'function_begin
++ {
++ local($func)=@_;
++
++ $func=$under.$func;
++
++ local($tmp)=<<"EOF";
++.text
++ .align $align
++.globl $func
++EOF
++ push(@out,$tmp);
++ if ($main'cpp)
++ { $tmp=push(@out,"\tTYPE($func,\@function)\n"); }
++ else { $tmp=push(@out,"\t.type\t$func,\@function\n"); }
++ push(@out,"$func:\n");
++ $tmp=<<"EOF";
++ pushl %ebp
++ pushl %ebx
++ pushl %esi
++ pushl %edi
++
++EOF
++ push(@out,$tmp);
++ $stack=20;
++ }
++
++sub main'function_begin_B
++ {
++ local($func,$extra)=@_;
++
++ $func=$under.$func;
++
++ local($tmp)=<<"EOF";
++.text
++ .align $align
++.globl $func
++EOF
++ push(@out,$tmp);
++ if ($main'cpp)
++ { push(@out,"\tTYPE($func,\@function)\n"); }
++ else { push(@out,"\t.type $func,\@function\n"); }
++ push(@out,"$func:\n");
++ $stack=4;
++ }
++
++sub main'function_end
++ {
++ local($func)=@_;
++
++ $func=$under.$func;
++
++ local($tmp)=<<"EOF";
++ popl %edi
++ popl %esi
++ popl %ebx
++ popl %ebp
++ ret
++.${func}_end:
++EOF
++ push(@out,$tmp);
++ if ($main'cpp)
++ { push(@out,"\tSIZE($func,.${func}_end-$func)\n"); }
++ else { push(@out,"\t.size\t$func,.${func}_end-$func\n"); }
++ push(@out,".ident \"$func\"\n");
++ $stack=0;
++ %label=();
++ }
++
++sub main'function_end_A
++ {
++ local($func)=@_;
++
++ local($tmp)=<<"EOF";
++ popl %edi
++ popl %esi
++ popl %ebx
++ popl %ebp
++ ret
++EOF
++ push(@out,$tmp);
++ }
++
++sub main'function_end_B
++ {
++ local($func)=@_;
++
++ $func=$under.$func;
++
++ push(@out,".${func}_end:\n");
++ if ($main'cpp)
++ { push(@out,"\tSIZE($func,.${func}_end-$func)\n"); }
++ else { push(@out,"\t.size\t$func,.${func}_end-$func\n"); }
++ push(@out,".ident \"desasm.pl\"\n");
++ $stack=0;
++ %label=();
++ }
++
++sub main'wparam
++ {
++ local($num)=@_;
++
++ return(&main'DWP($stack+$num*4,"esp","",0));
++ }
++
++sub main'stack_push
++ {
++ local($num)=@_;
++ $stack+=$num*4;
++ &main'sub("esp",$num*4);
++ }
++
++sub main'stack_pop
++ {
++ local($num)=@_;
++ $stack-=$num*4;
++ &main'add("esp",$num*4);
++ }
++
++sub main'swtmp
++ {
++ return(&main'DWP($_[0]*4,"esp","",0));
++ }
++
++# Should use swtmp, which is above esp. Linix can trash the stack above esp
++#sub main'wtmp
++# {
++# local($num)=@_;
++#
++# return(&main'DWP(-($num+1)*4,"esp","",0));
++# }
++
++sub main'comment
++ {
++ foreach (@_)
++ {
++ if (/^\s*$/)
++ { push(@out,"\n"); }
++ else
++ { push(@out,"\t$com_start $_ $com_end\n"); }
++ }
++ }
++
++sub main'label
++ {
++ if (!defined($label{$_[0]}))
++ {
++ $label{$_[0]}=".${label}${_[0]}";
++ $label++;
++ }
++ return($label{$_[0]});
++ }
++
++sub main'set_label
++ {
++ if (!defined($label{$_[0]}))
++ {
++ $label{$_[0]}=".${label}${_[0]}";
++ $label++;
++ }
++ push(@out,".align $align\n") if ($_[1] != 0);
++ push(@out,"$label{$_[0]}:\n");
++ }
++
++sub main'file_end
++ {
++ }
++
++sub main'data_word
++ {
++ push(@out,"\t.long $_[0]\n");
++ }
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/crypto/ciphers/des/asm/readme Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,131 @@
++First up, let me say I don't like writing in assembler. It is not portable,
++dependant on the particular CPU architecture release and is generally a pig
++to debug and get right. Having said that, the x86 architecture is probably
++the most important for speed due to number of boxes and since
++it appears to be the worst architecture to to get
++good C compilers for. So due to this, I have lowered myself to do
++assembler for the inner DES routines in libdes :-).
++
++The file to implement in assembler is des_enc.c. Replace the following
++4 functions
++des_encrypt(DES_LONG data[2],des_key_schedule ks, int encrypt);
++des_encrypt2(DES_LONG data[2],des_key_schedule ks, int encrypt);
++des_encrypt3(DES_LONG data[2],des_key_schedule ks1,ks2,ks3);
++des_decrypt3(DES_LONG data[2],des_key_schedule ks1,ks2,ks3);
++
++They encrypt/decrypt the 64 bits held in 'data' using
++the 'ks' key schedules. The only difference between the 4 functions is that
++des_encrypt2() does not perform IP() or FP() on the data (this is an
++optimization for when doing triple DES and des_encrypt3() and des_decrypt3()
++perform triple des. The triple DES routines are in here because it does
++make a big difference to have them located near the des_encrypt2 function
++at link time..
++
++Now as we all know, there are lots of different operating systems running on
++x86 boxes, and unfortunately they normally try to make sure their assembler
++formating is not the same as the other peoples.
++The 4 main formats I know of are
++Microsoft Windows 95/Windows NT
++Elf Includes Linux and FreeBSD(?).
++a.out The older Linux.
++Solaris Same as Elf but different comments :-(.
++
++Now I was not overly keen to write 4 different copies of the same code,
++so I wrote a few perl routines to output the correct assembler, given
++a target assembler type. This code is ugly and is just a hack.
++The libraries are x86unix.pl and x86ms.pl.
++des586.pl, des686.pl and des-som[23].pl are the programs to actually
++generate the assembler.
++
++So to generate elf assembler
++perl des-som3.pl elf >dx86-elf.s
++For Windows 95/NT
++perl des-som2.pl win32 >win32.asm
++
++[ update 4 Jan 1996 ]
++I have added another way to do things.
++perl des-som3.pl cpp >dx86-cpp.s
++generates a file that will be included by dx86unix.cpp when it is compiled.
++To build for elf, a.out, solaris, bsdi etc,
++cc -E -DELF asm/dx86unix.cpp | as -o asm/dx86-elf.o
++cc -E -DSOL asm/dx86unix.cpp | as -o asm/dx86-sol.o
++cc -E -DOUT asm/dx86unix.cpp | as -o asm/dx86-out.o
++cc -E -DBSDI asm/dx86unix.cpp | as -o asm/dx86bsdi.o
++This was done to cut down the number of files in the distribution.
++
++Now the ugly part. I acquired my copy of Intels
++"Optimization's For Intel's 32-Bit Processors" and found a few interesting
++things. First, the aim of the exersize is to 'extract' one byte at a time
++from a word and do an array lookup. This involves getting the byte from
++the 4 locations in the word and moving it to a new word and doing the lookup.
++The most obvious way to do this is
++xor eax, eax # clear word
++movb al, cl # get low byte
++xor edi DWORD PTR 0x100+des_SP[eax] # xor in word
++movb al, ch # get next byte
++xor edi DWORD PTR 0x300+des_SP[eax] # xor in word
++shr ecx 16
++which seems ok. For the pentium, this system appears to be the best.
++One has to do instruction interleaving to keep both functional units
++operating, but it is basically very efficient.
++
++Now the crunch. When a full register is used after a partial write, eg.
++mov al, cl
++xor edi, DWORD PTR 0x100+des_SP[eax]
++386 - 1 cycle stall
++486 - 1 cycle stall
++586 - 0 cycle stall
++686 - at least 7 cycle stall (page 22 of the above mentioned document).
++
++So the technique that produces the best results on a pentium, according to
++the documentation, will produce hideous results on a pentium pro.
++
++To get around this, des686.pl will generate code that is not as fast on
++a pentium, should be very good on a pentium pro.
++mov eax, ecx # copy word
++shr ecx, 8 # line up next byte
++and eax, 0fch # mask byte
++xor edi DWORD PTR 0x100+des_SP[eax] # xor in array lookup
++mov eax, ecx # get word
++shr ecx 8 # line up next byte
++and eax, 0fch # mask byte
++xor edi DWORD PTR 0x300+des_SP[eax] # xor in array lookup
++
++Due to the execution units in the pentium, this actually works quite well.
++For a pentium pro it should be very good. This is the type of output
++Visual C++ generates.
++
++There is a third option. instead of using
++mov al, ch
++which is bad on the pentium pro, one may be able to use
++movzx eax, ch
++which may not incur the partial write penalty. On the pentium,
++this instruction takes 4 cycles so is not worth using but on the
++pentium pro it appears it may be worth while. I need access to one to
++experiment :-).
++
++eric (20 Oct 1996)
++
++22 Nov 1996 - I have asked people to run the 2 different version on pentium
++pros and it appears that the intel documentation is wrong. The
++mov al,bh is still faster on a pentium pro, so just use the des586.pl
++install des686.pl
++
++3 Dec 1996 - I added des_encrypt3/des_decrypt3 because I have moved these
++functions into des_enc.c because it does make a massive performance
++difference on some boxes to have the functions code located close to
++the des_encrypt2() function.
++
++9 Jan 1997 - des-som2.pl is now the correct perl script to use for
++pentiums. It contains an inner loop from
++Svend Olaf Mikkelsen <svolaf@inet.uni-c.dk> which does raw ecb DES calls at
++273,000 per second. He had a previous version at 250,000 and the best
++I was able to get was 203,000. The content has not changed, this is all
++due to instruction sequencing (and actual instructions choice) which is able
++to keep both functional units of the pentium going.
++We may have lost the ugly register usage restrictions when x86 went 32 bit
++but for the pentium it has been replaced by evil instruction ordering tricks.
++
++13 Jan 1997 - des-som3.pl, more optimizations from Svend Olaf.
++raw DES at 281,000 per second on a pentium 100.
++
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/crypto/ciphers/des/cbc_enc.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,135 @@
++/* crypto/des/cbc_enc.c */
++/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com)
++ * All rights reserved.
++ *
++ * This package is an SSL implementation written
++ * by Eric Young (eay@cryptsoft.com).
++ * The implementation was written so as to conform with Netscapes SSL.
++ *
++ * This library is free for commercial and non-commercial use as long as
++ * the following conditions are aheared to. The following conditions
++ * apply to all code found in this distribution, be it the RC4, RSA,
++ * lhash, DES, etc., code; not just the SSL code. The SSL documentation
++ * included with this distribution is covered by the same copyright terms
++ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
++ *
++ * Copyright remains Eric Young's, and as such any Copyright notices in
++ * the code are not to be removed.
++ * If this package is used in a product, Eric Young should be given attribution
++ * as the author of the parts of the library used.
++ * This can be in the form of a textual message at program startup or
++ * in documentation (online or textual) provided with the package.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ * 1. Redistributions of source code must retain the copyright
++ * notice, this list of conditions and the following disclaimer.
++ * 2. Redistributions in binary form must reproduce the above copyright
++ * notice, this list of conditions and the following disclaimer in the
++ * documentation and/or other materials provided with the distribution.
++ * 3. All advertising materials mentioning features or use of this software
++ * must display the following acknowledgement:
++ * "This product includes cryptographic software written by
++ * Eric Young (eay@cryptsoft.com)"
++ * The word 'cryptographic' can be left out if the rouines from the library
++ * being used are not cryptographic related :-).
++ * 4. If you include any Windows specific code (or a derivative thereof) from
++ * the apps directory (application code) you must include an acknowledgement:
++ * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
++ *
++ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ *
++ * The licence and distribution terms for any publically available version or
++ * derivative of this code cannot be changed. i.e. this code cannot simply be
++ * copied and put under another distribution licence
++ * [including the GNU Public Licence.]
++ */
++
++#include "des_locl.h"
++
++void des_cbc_encrypt(input, output, length, schedule, ivec, enc)
++des_cblock (*input);
++des_cblock (*output);
++long length;
++des_key_schedule schedule;
++des_cblock (*ivec);
++int enc;
++ {
++ register DES_LONG tin0,tin1;
++ register DES_LONG tout0,tout1,xor0,xor1;
++ register unsigned char *in,*out;
++ register long l=length;
++ DES_LONG tin[2];
++ unsigned char *iv;
++
++ in=(unsigned char *)input;
++ out=(unsigned char *)output;
++ iv=(unsigned char *)ivec;
++
++ if (enc)
++ {
++ c2l(iv,tout0);
++ c2l(iv,tout1);
++ for (l-=8; l>=0; l-=8)
++ {
++ c2l(in,tin0);
++ c2l(in,tin1);
++ tin0^=tout0; tin[0]=tin0;
++ tin1^=tout1; tin[1]=tin1;
++ des_encrypt((DES_LONG *)tin,schedule,DES_ENCRYPT);
++ tout0=tin[0]; l2c(tout0,out);
++ tout1=tin[1]; l2c(tout1,out);
++ }
++ if (l != -8)
++ {
++ c2ln(in,tin0,tin1,l+8);
++ tin0^=tout0; tin[0]=tin0;
++ tin1^=tout1; tin[1]=tin1;
++ des_encrypt((DES_LONG *)tin,schedule,DES_ENCRYPT);
++ tout0=tin[0]; l2c(tout0,out);
++ tout1=tin[1]; l2c(tout1,out);
++ }
++ }
++ else
++ {
++ c2l(iv,xor0);
++ c2l(iv,xor1);
++ for (l-=8; l>=0; l-=8)
++ {
++ c2l(in,tin0); tin[0]=tin0;
++ c2l(in,tin1); tin[1]=tin1;
++ des_encrypt((DES_LONG *)tin,schedule,DES_DECRYPT);
++ tout0=tin[0]^xor0;
++ tout1=tin[1]^xor1;
++ l2c(tout0,out);
++ l2c(tout1,out);
++ xor0=tin0;
++ xor1=tin1;
++ }
++ if (l != -8)
++ {
++ c2l(in,tin0); tin[0]=tin0;
++ c2l(in,tin1); tin[1]=tin1;
++ des_encrypt((DES_LONG *)tin,schedule,DES_DECRYPT);
++ tout0=tin[0]^xor0;
++ tout1=tin[1]^xor1;
++ l2cn(tout0,tout1,out,l+8);
++ /* xor0=tin0;
++ xor1=tin1; */
++ }
++ }
++ tin0=tin1=tout0=tout1=xor0=xor1=0;
++ tin[0]=tin[1]=0;
++ }
++
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/crypto/ciphers/des/des.doc Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,505 @@
++The DES library.
++
++Please note that this library was originally written to operate with
++eBones, a version of Kerberos that had had encryption removed when it left
++the USA and then put back in. As such there are some routines that I will
++advise not using but they are still in the library for historical reasons.
++For all calls that have an 'input' and 'output' variables, they can be the
++same.
++
++This library requires the inclusion of 'des.h'.
++
++All of the encryption functions take what is called a des_key_schedule as an
++argument. A des_key_schedule is an expanded form of the des key.
++A des_key is 8 bytes of odd parity, the type used to hold the key is a
++des_cblock. A des_cblock is an array of 8 bytes, often in this library
++description I will refer to input bytes when the function specifies
++des_cblock's as input or output, this just means that the variable should
++be a multiple of 8 bytes.
++
++The define DES_ENCRYPT is passed to specify encryption, DES_DECRYPT to
++specify decryption. The functions and global variable are as follows:
++
++int des_check_key;
++ DES keys are supposed to be odd parity. If this variable is set to
++ a non-zero value, des_set_key() will check that the key has odd
++ parity and is not one of the known weak DES keys. By default this
++ variable is turned off;
++
++void des_set_odd_parity(
++des_cblock *key );
++ This function takes a DES key (8 bytes) and sets the parity to odd.
++
++int des_is_weak_key(
++des_cblock *key );
++ This function returns a non-zero value if the DES key passed is a
++ weak, DES key. If it is a weak key, don't use it, try a different
++ one. If you are using 'random' keys, the chances of hitting a weak
++ key are 1/2^52 so it is probably not worth checking for them.
++
++int des_set_key(
++des_cblock *key,
++des_key_schedule schedule);
++ Des_set_key converts an 8 byte DES key into a des_key_schedule.
++ A des_key_schedule is an expanded form of the key which is used to
++ perform actual encryption. It can be regenerated from the DES key
++ so it only needs to be kept when encryption or decryption is about
++ to occur. Don't save or pass around des_key_schedule's since they
++ are CPU architecture dependent, DES keys are not. If des_check_key
++ is non zero, zero is returned if the key has the wrong parity or
++ the key is a weak key, else 1 is returned.
++
++int des_key_sched(
++des_cblock *key,
++des_key_schedule schedule);
++ An alternative name for des_set_key().
++
++int des_rw_mode; /* defaults to DES_PCBC_MODE */
++ This flag holds either DES_CBC_MODE or DES_PCBC_MODE (default).
++ This specifies the function to use in the enc_read() and enc_write()
++ functions.
++
++void des_encrypt(
++unsigned long *data,
++des_key_schedule ks,
++int enc);
++ This is the DES encryption function that gets called by just about
++ every other DES routine in the library. You should not use this
++ function except to implement 'modes' of DES. I say this because the
++ functions that call this routine do the conversion from 'char *' to
++ long, and this needs to be done to make sure 'non-aligned' memory
++ access do not occur. The characters are loaded 'little endian',
++ have a look at my source code for more details on how I use this
++ function.
++ Data is a pointer to 2 unsigned long's and ks is the
++ des_key_schedule to use. enc, is non zero specifies encryption,
++ zero if decryption.
++
++void des_encrypt2(
++unsigned long *data,
++des_key_schedule ks,
++int enc);
++ This functions is the same as des_encrypt() except that the DES
++ initial permutation (IP) and final permutation (FP) have been left
++ out. As for des_encrypt(), you should not use this function.
++ It is used by the routines in my library that implement triple DES.
++ IP() des_encrypt2() des_encrypt2() des_encrypt2() FP() is the same
++ as des_encrypt() des_encrypt() des_encrypt() except faster :-).
++
++void des_ecb_encrypt(
++des_cblock *input,
++des_cblock *output,
++des_key_schedule ks,
++int enc);
++ This is the basic Electronic Code Book form of DES, the most basic
++ form. Input is encrypted into output using the key represented by
++ ks. If enc is non zero (DES_ENCRYPT), encryption occurs, otherwise
++ decryption occurs. Input is 8 bytes long and output is 8 bytes.
++ (the des_cblock structure is 8 chars).
++
++void des_ecb3_encrypt(
++des_cblock *input,
++des_cblock *output,
++des_key_schedule ks1,
++des_key_schedule ks2,
++des_key_schedule ks3,
++int enc);
++ This is the 3 key EDE mode of ECB DES. What this means is that
++ the 8 bytes of input is encrypted with ks1, decrypted with ks2 and
++ then encrypted again with ks3, before being put into output;
++ C=E(ks3,D(ks2,E(ks1,M))). There is a macro, des_ecb2_encrypt()
++ that only takes 2 des_key_schedules that implements,
++ C=E(ks1,D(ks2,E(ks1,M))) in that the final encrypt is done with ks1.
++
++void des_cbc_encrypt(
++des_cblock *input,
++des_cblock *output,
++long length,
++des_key_schedule ks,
++des_cblock *ivec,
++int enc);
++ This routine implements DES in Cipher Block Chaining mode.
++ Input, which should be a multiple of 8 bytes is encrypted
++ (or decrypted) to output which will also be a multiple of 8 bytes.
++ The number of bytes is in length (and from what I've said above,
++ should be a multiple of 8). If length is not a multiple of 8, I'm
++ not being held responsible :-). ivec is the initialisation vector.
++ This function does not modify this variable. To correctly implement
++ cbc mode, you need to do one of 2 things; copy the last 8 bytes of
++ cipher text for use as the next ivec in your application,
++ or use des_ncbc_encrypt().
++ Only this routine has this problem with updating the ivec, all
++ other routines that are implementing cbc mode update ivec.
++
++void des_ncbc_encrypt(
++des_cblock *input,
++des_cblock *output,
++long length,
++des_key_schedule sk,
++des_cblock *ivec,
++int enc);
++ For historical reasons, des_cbc_encrypt() did not update the
++ ivec with the value requires so that subsequent calls to
++ des_cbc_encrypt() would 'chain'. This was needed so that the same
++ 'length' values would not need to be used when decrypting.
++ des_ncbc_encrypt() does the right thing. It is the same as
++ des_cbc_encrypt accept that ivec is updates with the correct value
++ to pass in subsequent calls to des_ncbc_encrypt(). I advise using
++ des_ncbc_encrypt() instead of des_cbc_encrypt();
++
++void des_xcbc_encrypt(
++des_cblock *input,
++des_cblock *output,
++long length,
++des_key_schedule sk,
++des_cblock *ivec,
++des_cblock *inw,
++des_cblock *outw,
++int enc);
++ This is RSA's DESX mode of DES. It uses inw and outw to
++ 'whiten' the encryption. inw and outw are secret (unlike the iv)
++ and are as such, part of the key. So the key is sort of 24 bytes.
++ This is much better than cbc des.
++
++void des_3cbc_encrypt(
++des_cblock *input,
++des_cblock *output,
++long length,
++des_key_schedule sk1,
++des_key_schedule sk2,
++des_cblock *ivec1,
++des_cblock *ivec2,
++int enc);
++ This function is flawed, do not use it. I have left it in the
++ library because it is used in my des(1) program and will function
++ correctly when used by des(1). If I removed the function, people
++ could end up unable to decrypt files.
++ This routine implements outer triple cbc encryption using 2 ks and
++ 2 ivec's. Use des_ede2_cbc_encrypt() instead.
++
++void des_ede3_cbc_encrypt(
++des_cblock *input,
++des_cblock *output,
++long length,
++des_key_schedule ks1,
++des_key_schedule ks2,
++des_key_schedule ks3,
++des_cblock *ivec,
++int enc);
++ This function implements inner triple CBC DES encryption with 3
++ keys. What this means is that each 'DES' operation
++ inside the cbc mode is really an C=E(ks3,D(ks2,E(ks1,M))).
++ Again, this is cbc mode so an ivec is requires.
++ This mode is used by SSL.
++ There is also a des_ede2_cbc_encrypt() that only uses 2
++ des_key_schedule's, the first being reused for the final
++ encryption. C=E(ks1,D(ks2,E(ks1,M))). This form of triple DES
++ is used by the RSAref library.
++
++void des_pcbc_encrypt(
++des_cblock *input,
++des_cblock *output,
++long length,
++des_key_schedule ks,
++des_cblock *ivec,
++int enc);
++ This is Propagating Cipher Block Chaining mode of DES. It is used
++ by Kerberos v4. It's parameters are the same as des_ncbc_encrypt().
++
++void des_cfb_encrypt(
++unsigned char *in,
++unsigned char *out,
++int numbits,
++long length,
++des_key_schedule ks,
++des_cblock *ivec,
++int enc);
++ Cipher Feedback Back mode of DES. This implementation 'feeds back'
++ in numbit blocks. The input (and output) is in multiples of numbits
++ bits. numbits should to be a multiple of 8 bits. Length is the
++ number of bytes input. If numbits is not a multiple of 8 bits,
++ the extra bits in the bytes will be considered padding. So if
++ numbits is 12, for each 2 input bytes, the 4 high bits of the
++ second byte will be ignored. So to encode 72 bits when using
++ a numbits of 12 take 12 bytes. To encode 72 bits when using
++ numbits of 9 will take 16 bytes. To encode 80 bits when using
++ numbits of 16 will take 10 bytes. etc, etc. This padding will
++ apply to both input and output.
++
++
++void des_cfb64_encrypt(
++unsigned char *in,
++unsigned char *out,
++long length,
++des_key_schedule ks,
++des_cblock *ivec,
++int *num,
++int enc);
++ This is one of the more useful functions in this DES library, it
++ implements CFB mode of DES with 64bit feedback. Why is this
++ useful you ask? Because this routine will allow you to encrypt an
++ arbitrary number of bytes, no 8 byte padding. Each call to this
++ routine will encrypt the input bytes to output and then update ivec
++ and num. num contains 'how far' we are though ivec. If this does
++ not make much sense, read more about cfb mode of DES :-).
++
++void des_ede3_cfb64_encrypt(
++unsigned char *in,
++unsigned char *out,
++long length,
++des_key_schedule ks1,
++des_key_schedule ks2,
++des_key_schedule ks3,
++des_cblock *ivec,
++int *num,
++int enc);
++ Same as des_cfb64_encrypt() accept that the DES operation is
++ triple DES. As usual, there is a macro for
++ des_ede2_cfb64_encrypt() which reuses ks1.
++
++void des_ofb_encrypt(
++unsigned char *in,
++unsigned char *out,
++int numbits,
++long length,
++des_key_schedule ks,
++des_cblock *ivec);
++ This is a implementation of Output Feed Back mode of DES. It is
++ the same as des_cfb_encrypt() in that numbits is the size of the
++ units dealt with during input and output (in bits).
++
++void des_ofb64_encrypt(
++unsigned char *in,
++unsigned char *out,
++long length,
++des_key_schedule ks,
++des_cblock *ivec,
++int *num);
++ The same as des_cfb64_encrypt() except that it is Output Feed Back
++ mode.
++
++void des_ede3_ofb64_encrypt(
++unsigned char *in,
++unsigned char *out,
++long length,
++des_key_schedule ks1,
++des_key_schedule ks2,
++des_key_schedule ks3,
++des_cblock *ivec,
++int *num);
++ Same as des_ofb64_encrypt() accept that the DES operation is
++ triple DES. As usual, there is a macro for
++ des_ede2_ofb64_encrypt() which reuses ks1.
++
++int des_read_pw_string(
++char *buf,
++int length,
++char *prompt,
++int verify);
++ This routine is used to get a password from the terminal with echo
++ turned off. Buf is where the string will end up and length is the
++ size of buf. Prompt is a string presented to the 'user' and if
++ verify is set, the key is asked for twice and unless the 2 copies
++ match, an error is returned. A return code of -1 indicates a
++ system error, 1 failure due to use interaction, and 0 is success.
++
++unsigned long des_cbc_cksum(
++des_cblock *input,
++des_cblock *output,
++long length,
++des_key_schedule ks,
++des_cblock *ivec);
++ This function produces an 8 byte checksum from input that it puts in
++ output and returns the last 4 bytes as a long. The checksum is
++ generated via cbc mode of DES in which only the last 8 byes are
++ kept. I would recommend not using this function but instead using
++ the EVP_Digest routines, or at least using MD5 or SHA. This
++ function is used by Kerberos v4 so that is why it stays in the
++ library.
++
++char *des_fcrypt(
++const char *buf,
++const char *salt
++char *ret);
++ This is my fast version of the unix crypt(3) function. This version
++ takes only a small amount of space relative to other fast
++ crypt() implementations. This is different to the normal crypt
++ in that the third parameter is the buffer that the return value
++ is written into. It needs to be at least 14 bytes long. This
++ function is thread safe, unlike the normal crypt.
++
++char *crypt(
++const char *buf,
++const char *salt);
++ This function calls des_fcrypt() with a static array passed as the
++ third parameter. This emulates the normal non-thread safe semantics
++ of crypt(3).
++
++void des_string_to_key(
++char *str,
++des_cblock *key);
++ This function takes str and converts it into a DES key. I would
++ recommend using MD5 instead and use the first 8 bytes of output.
++ When I wrote the first version of these routines back in 1990, MD5
++ did not exist but I feel these routines are still sound. This
++ routines is compatible with the one in MIT's libdes.
++
++void des_string_to_2keys(
++char *str,
++des_cblock *key1,
++des_cblock *key2);
++ This function takes str and converts it into 2 DES keys.
++ I would recommend using MD5 and using the 16 bytes as the 2 keys.
++ I have nothing against these 2 'string_to_key' routines, it's just
++ that if you say that your encryption key is generated by using the
++ 16 bytes of an MD5 hash, every-one knows how you generated your
++ keys.
++
++int des_read_password(
++des_cblock *key,
++char *prompt,
++int verify);
++ This routine combines des_read_pw_string() with des_string_to_key().
++
++int des_read_2passwords(
++des_cblock *key1,
++des_cblock *key2,
++char *prompt,
++int verify);
++ This routine combines des_read_pw_string() with des_string_to_2key().
++
++void des_random_seed(
++des_cblock key);
++ This routine sets a starting point for des_random_key().
++
++void des_random_key(
++des_cblock ret);
++ This function return a random key. Make sure to 'seed' the random
++ number generator (with des_random_seed()) before using this function.
++ I personally now use a MD5 based random number system.
++
++int des_enc_read(
++int fd,
++char *buf,
++int len,
++des_key_schedule ks,
++des_cblock *iv);
++ This function will write to a file descriptor the encrypted data
++ from buf. This data will be preceded by a 4 byte 'byte count' and
++ will be padded out to 8 bytes. The encryption is either CBC of
++ PCBC depending on the value of des_rw_mode. If it is DES_PCBC_MODE,
++ pcbc is used, if DES_CBC_MODE, cbc is used. The default is to use
++ DES_PCBC_MODE.
++
++int des_enc_write(
++int fd,
++char *buf,
++int len,
++des_key_schedule ks,
++des_cblock *iv);
++ This routines read stuff written by des_enc_read() and decrypts it.
++ I have used these routines quite a lot but I don't believe they are
++ suitable for non-blocking io. If you are after a full
++ authentication/encryption over networks, have a look at SSL instead.
++
++unsigned long des_quad_cksum(
++des_cblock *input,
++des_cblock *output,
++long length,
++int out_count,
++des_cblock *seed);
++ This is a function from Kerberos v4 that is not anything to do with
++ DES but was needed. It is a cksum that is quicker to generate than
++ des_cbc_cksum(); I personally would use MD5 routines now.
++=====
++Modes of DES
++Quite a bit of the following information has been taken from
++ AS 2805.5.2
++ Australian Standard
++ Electronic funds transfer - Requirements for interfaces,
++ Part 5.2: Modes of operation for an n-bit block cipher algorithm
++ Appendix A
++
++There are several different modes in which DES can be used, they are
++as follows.
++
++Electronic Codebook Mode (ECB) (des_ecb_encrypt())
++- 64 bits are enciphered at a time.
++- The order of the blocks can be rearranged without detection.
++- The same plaintext block always produces the same ciphertext block
++ (for the same key) making it vulnerable to a 'dictionary attack'.
++- An error will only affect one ciphertext block.
++
++Cipher Block Chaining Mode (CBC) (des_cbc_encrypt())
++- a multiple of 64 bits are enciphered at a time.
++- The CBC mode produces the same ciphertext whenever the same
++ plaintext is encrypted using the same key and starting variable.
++- The chaining operation makes the ciphertext blocks dependent on the
++ current and all preceding plaintext blocks and therefore blocks can not
++ be rearranged.
++- The use of different starting variables prevents the same plaintext
++ enciphering to the same ciphertext.
++- An error will affect the current and the following ciphertext blocks.
++
++Cipher Feedback Mode (CFB) (des_cfb_encrypt())
++- a number of bits (j) <= 64 are enciphered at a time.
++- The CFB mode produces the same ciphertext whenever the same
++ plaintext is encrypted using the same key and starting variable.
++- The chaining operation makes the ciphertext variables dependent on the
++ current and all preceding variables and therefore j-bit variables are
++ chained together and can not be rearranged.
++- The use of different starting variables prevents the same plaintext
++ enciphering to the same ciphertext.
++- The strength of the CFB mode depends on the size of k (maximal if
++ j == k). In my implementation this is always the case.
++- Selection of a small value for j will require more cycles through
++ the encipherment algorithm per unit of plaintext and thus cause
++ greater processing overheads.
++- Only multiples of j bits can be enciphered.
++- An error will affect the current and the following ciphertext variables.
++
++Output Feedback Mode (OFB) (des_ofb_encrypt())
++- a number of bits (j) <= 64 are enciphered at a time.
++- The OFB mode produces the same ciphertext whenever the same
++ plaintext enciphered using the same key and starting variable. More
++ over, in the OFB mode the same key stream is produced when the same
++ key and start variable are used. Consequently, for security reasons
++ a specific start variable should be used only once for a given key.
++- The absence of chaining makes the OFB more vulnerable to specific attacks.
++- The use of different start variables values prevents the same
++ plaintext enciphering to the same ciphertext, by producing different
++ key streams.
++- Selection of a small value for j will require more cycles through
++ the encipherment algorithm per unit of plaintext and thus cause
++ greater processing overheads.
++- Only multiples of j bits can be enciphered.
++- OFB mode of operation does not extend ciphertext errors in the
++ resultant plaintext output. Every bit error in the ciphertext causes
++ only one bit to be in error in the deciphered plaintext.
++- OFB mode is not self-synchronising. If the two operation of
++ encipherment and decipherment get out of synchronism, the system needs
++ to be re-initialised.
++- Each re-initialisation should use a value of the start variable
++ different from the start variable values used before with the same
++ key. The reason for this is that an identical bit stream would be
++ produced each time from the same parameters. This would be
++ susceptible to a ' known plaintext' attack.
++
++Triple ECB Mode (des_ecb3_encrypt())
++- Encrypt with key1, decrypt with key2 and encrypt with key3 again.
++- As for ECB encryption but increases the key length to 168 bits.
++ There are theoretic attacks that can be used that make the effective
++ key length 112 bits, but this attack also requires 2^56 blocks of
++ memory, not very likely, even for the NSA.
++- If both keys are the same it is equivalent to encrypting once with
++ just one key.
++- If the first and last key are the same, the key length is 112 bits.
++ There are attacks that could reduce the key space to 55 bit's but it
++ requires 2^56 blocks of memory.
++- If all 3 keys are the same, this is effectively the same as normal
++ ecb mode.
++
++Triple CBC Mode (des_ede3_cbc_encrypt())
++- Encrypt with key1, decrypt with key2 and then encrypt with key3.
++- As for CBC encryption but increases the key length to 168 bits with
++ the same restrictions as for triple ecb mode.
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/crypto/ciphers/des/des_crypt.man Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,508 @@
++.TH DES_CRYPT 3
++.SH NAME
++des_read_password, des_read_2password,
++des_string_to_key, des_string_to_2key, des_read_pw_string,
++des_random_key, des_set_key,
++des_key_sched, des_ecb_encrypt, des_ecb3_encrypt, des_cbc_encrypt,
++des_3cbc_encrypt,
++des_pcbc_encrypt, des_cfb_encrypt, des_ofb_encrypt,
++des_cbc_cksum, des_quad_cksum,
++des_enc_read, des_enc_write, des_set_odd_parity,
++des_is_weak_key, crypt \- (non USA) DES encryption
++.SH SYNOPSIS
++.nf
++.nj
++.ft B
++#include <des.h>
++.PP
++.B int des_read_password(key,prompt,verify)
++des_cblock *key;
++char *prompt;
++int verify;
++.PP
++.B int des_read_2password(key1,key2,prompt,verify)
++des_cblock *key1,*key2;
++char *prompt;
++int verify;
++.PP
++.B int des_string_to_key(str,key)
++char *str;
++des_cblock *key;
++.PP
++.B int des_string_to_2keys(str,key1,key2)
++char *str;
++des_cblock *key1,*key2;
++.PP
++.B int des_read_pw_string(buf,length,prompt,verify)
++char *buf;
++int length;
++char *prompt;
++int verify;
++.PP
++.B int des_random_key(key)
++des_cblock *key;
++.PP
++.B int des_set_key(key,schedule)
++des_cblock *key;
++des_key_schedule schedule;
++.PP
++.B int des_key_sched(key,schedule)
++des_cblock *key;
++des_key_schedule schedule;
++.PP
++.B int des_ecb_encrypt(input,output,schedule,encrypt)
++des_cblock *input;
++des_cblock *output;
++des_key_schedule schedule;
++int encrypt;
++.PP
++.B int des_ecb3_encrypt(input,output,ks1,ks2,encrypt)
++des_cblock *input;
++des_cblock *output;
++des_key_schedule ks1,ks2;
++int encrypt;
++.PP
++.B int des_cbc_encrypt(input,output,length,schedule,ivec,encrypt)
++des_cblock *input;
++des_cblock *output;
++long length;
++des_key_schedule schedule;
++des_cblock *ivec;
++int encrypt;
++.PP
++.B int des_3cbc_encrypt(input,output,length,sk1,sk2,ivec1,ivec2,encrypt)
++des_cblock *input;
++des_cblock *output;
++long length;
++des_key_schedule sk1;
++des_key_schedule sk2;
++des_cblock *ivec1;
++des_cblock *ivec2;
++int encrypt;
++.PP
++.B int des_pcbc_encrypt(input,output,length,schedule,ivec,encrypt)
++des_cblock *input;
++des_cblock *output;
++long length;
++des_key_schedule schedule;
++des_cblock *ivec;
++int encrypt;
++.PP
++.B int des_cfb_encrypt(input,output,numbits,length,schedule,ivec,encrypt)
++unsigned char *input;
++unsigned char *output;
++int numbits;
++long length;
++des_key_schedule schedule;
++des_cblock *ivec;
++int encrypt;
++.PP
++.B int des_ofb_encrypt(input,output,numbits,length,schedule,ivec)
++unsigned char *input,*output;
++int numbits;
++long length;
++des_key_schedule schedule;
++des_cblock *ivec;
++.PP
++.B unsigned long des_cbc_cksum(input,output,length,schedule,ivec)
++des_cblock *input;
++des_cblock *output;
++long length;
++des_key_schedule schedule;
++des_cblock *ivec;
++.PP
++.B unsigned long des_quad_cksum(input,output,length,out_count,seed)
++des_cblock *input;
++des_cblock *output;
++long length;
++int out_count;
++des_cblock *seed;
++.PP
++.B int des_check_key;
++.PP
++.B int des_enc_read(fd,buf,len,sched,iv)
++int fd;
++char *buf;
++int len;
++des_key_schedule sched;
++des_cblock *iv;
++.PP
++.B int des_enc_write(fd,buf,len,sched,iv)
++int fd;
++char *buf;
++int len;
++des_key_schedule sched;
++des_cblock *iv;
++.PP
++.B extern int des_rw_mode;
++.PP
++.B void des_set_odd_parity(key)
++des_cblock *key;
++.PP
++.B int des_is_weak_key(key)
++des_cblock *key;
++.PP
++.B char *crypt(passwd,salt)
++char *passwd;
++char *salt;
++.PP
++.fi
++.SH DESCRIPTION
++This library contains a fast implementation of the DES encryption
++algorithm.
++.PP
++There are two phases to the use of DES encryption.
++The first is the generation of a
++.I des_key_schedule
++from a key,
++the second is the actual encryption.
++A des key is of type
++.I des_cblock.
++This type is made from 8 characters with odd parity.
++The least significant bit in the character is the parity bit.
++The key schedule is an expanded form of the key; it is used to speed the
++encryption process.
++.PP
++.I des_read_password
++writes the string specified by prompt to the standard output,
++turns off echo and reads an input string from standard input
++until terminated with a newline.
++If verify is non-zero, it prompts and reads the input again and verifies
++that both entered passwords are the same.
++The entered string is converted into a des key by using the
++.I des_string_to_key
++routine.
++The new key is placed in the
++.I des_cblock
++that was passed (by reference) to the routine.
++If there were no errors,
++.I des_read_password
++returns 0,
++-1 is returned if there was a terminal error and 1 is returned for
++any other error.
++.PP
++.I des_read_2password
++operates in the same way as
++.I des_read_password
++except that it generates 2 keys by using the
++.I des_string_to_2key
++function.
++.PP
++.I des_read_pw_string
++is called by
++.I des_read_password
++to read and verify a string from a terminal device.
++The string is returned in
++.I buf.
++The size of
++.I buf
++is passed to the routine via the
++.I length
++parameter.
++.PP
++.I des_string_to_key
++converts a string into a valid des key.
++.PP
++.I des_string_to_2key
++converts a string into 2 valid des keys.
++This routine is best suited for used to generate keys for use with
++.I des_ecb3_encrypt.
++.PP
++.I des_random_key
++returns a random key that is made of a combination of process id,
++time and an increasing counter.
++.PP
++Before a des key can be used it is converted into a
++.I des_key_schedule
++via the
++.I des_set_key
++routine.
++If the
++.I des_check_key
++flag is non-zero,
++.I des_set_key
++will check that the key passed is of odd parity and is not a week or
++semi-weak key.
++If the parity is wrong,
++then -1 is returned.
++If the key is a weak key,
++then -2 is returned.
++If an error is returned,
++the key schedule is not generated.
++.PP
++.I des_key_sched
++is another name for the
++.I des_set_key
++function.
++.PP
++The following routines mostly operate on an input and output stream of
++.I des_cblock's.
++.PP
++.I des_ecb_encrypt
++is the basic DES encryption routine that encrypts or decrypts a single 8-byte
++.I des_cblock
++in
++.I electronic code book
++mode.
++It always transforms the input data, pointed to by
++.I input,
++into the output data,
++pointed to by the
++.I output
++argument.
++If the
++.I encrypt
++argument is non-zero (DES_ENCRYPT),
++the
++.I input
++(cleartext) is encrypted in to the
++.I output
++(ciphertext) using the key_schedule specified by the
++.I schedule
++argument,
++previously set via
++.I des_set_key.
++If
++.I encrypt
++is zero (DES_DECRYPT),
++the
++.I input
++(now ciphertext)
++is decrypted into the
++.I output
++(now cleartext).
++Input and output may overlap.
++No meaningful value is returned.
++.PP
++.I des_ecb3_encrypt
++encrypts/decrypts the
++.I input
++block by using triple ecb DES encryption.
++This involves encrypting the input with
++.I ks1,
++decryption with the key schedule
++.I ks2,
++and then encryption with the first again.
++This routine greatly reduces the chances of brute force breaking of
++DES and has the advantage of if
++.I ks1
++and
++.I ks2
++are the same, it is equivalent to just encryption using ecb mode and
++.I ks1
++as the key.
++.PP
++.I des_cbc_encrypt
++encrypts/decrypts using the
++.I cipher-block-chaining
++mode of DES.
++If the
++.I encrypt
++argument is non-zero,
++the routine cipher-block-chain encrypts the cleartext data pointed to by the
++.I input
++argument into the ciphertext pointed to by the
++.I output
++argument,
++using the key schedule provided by the
++.I schedule
++argument,
++and initialisation vector provided by the
++.I ivec
++argument.
++If the
++.I length
++argument is not an integral multiple of eight bytes,
++the last block is copied to a temporary area and zero filled.
++The output is always
++an integral multiple of eight bytes.
++To make multiple cbc encrypt calls on a large amount of data appear to
++be one
++.I des_cbc_encrypt
++call, the
++.I ivec
++of subsequent calls should be the last 8 bytes of the output.
++.PP
++.I des_3cbc_encrypt
++encrypts/decrypts the
++.I input
++block by using triple cbc DES encryption.
++This involves encrypting the input with key schedule
++.I ks1,
++decryption with the key schedule
++.I ks2,
++and then encryption with the first again.
++2 initialisation vectors are required,
++.I ivec1
++and
++.I ivec2.
++Unlike
++.I des_cbc_encrypt,
++these initialisation vectors are modified by the subroutine.
++This routine greatly reduces the chances of brute force breaking of
++DES and has the advantage of if
++.I ks1
++and
++.I ks2
++are the same, it is equivalent to just encryption using cbc mode and
++.I ks1
++as the key.
++.PP
++.I des_pcbc_encrypt
++encrypt/decrypts using a modified block chaining mode.
++It provides better error propagation characteristics than cbc
++encryption.
++.PP
++.I des_cfb_encrypt
++encrypt/decrypts using cipher feedback mode. This method takes an
++array of characters as input and outputs and array of characters. It
++does not require any padding to 8 character groups. Note: the ivec
++variable is changed and the new changed value needs to be passed to
++the next call to this function. Since this function runs a complete
++DES ecb encryption per numbits, this function is only suggested for
++use when sending small numbers of characters.
++.PP
++.I des_ofb_encrypt
++encrypt using output feedback mode. This method takes an
++array of characters as input and outputs and array of characters. It
++does not require any padding to 8 character groups. Note: the ivec
++variable is changed and the new changed value needs to be passed to
++the next call to this function. Since this function runs a complete
++DES ecb encryption per numbits, this function is only suggested for
++use when sending small numbers of characters.
++.PP
++.I des_cbc_cksum
++produces an 8 byte checksum based on the input stream (via cbc encryption).
++The last 4 bytes of the checksum is returned and the complete 8 bytes is
++placed in
++.I output.
++.PP
++.I des_quad_cksum
++returns a 4 byte checksum from the input bytes.
++The algorithm can be iterated over the input,
++depending on
++.I out_count,
++1, 2, 3 or 4 times.
++If
++.I output
++is non-NULL,
++the 8 bytes generated by each pass are written into
++.I output.
++.PP
++.I des_enc_write
++is used to write
++.I len
++bytes
++to file descriptor
++.I fd
++from buffer
++.I buf.
++The data is encrypted via
++.I pcbc_encrypt
++(default) using
++.I sched
++for the key and
++.I iv
++as a starting vector.
++The actual data send down
++.I fd
++consists of 4 bytes (in network byte order) containing the length of the
++following encrypted data. The encrypted data then follows, padded with random
++data out to a multiple of 8 bytes.
++.PP
++.I des_enc_read
++is used to read
++.I len
++bytes
++from file descriptor
++.I fd
++into buffer
++.I buf.
++The data being read from
++.I fd
++is assumed to have come from
++.I des_enc_write
++and is decrypted using
++.I sched
++for the key schedule and
++.I iv
++for the initial vector.
++The
++.I des_enc_read/des_enc_write
++pair can be used to read/write to files, pipes and sockets.
++I have used them in implementing a version of rlogin in which all
++data is encrypted.
++.PP
++.I des_rw_mode
++is used to specify the encryption mode to use with
++.I des_enc_read
++and
++.I des_end_write.
++If set to
++.I DES_PCBC_MODE
++(the default), des_pcbc_encrypt is used.
++If set to
++.I DES_CBC_MODE
++des_cbc_encrypt is used.
++These two routines and the variable are not part of the normal MIT library.
++.PP
++.I des_set_odd_parity
++sets the parity of the passed
++.I key
++to odd. This routine is not part of the standard MIT library.
++.PP
++.I des_is_weak_key
++returns 1 is the passed key is a weak key (pick again :-),
++0 if it is ok.
++This routine is not part of the standard MIT library.
++.PP
++.I crypt
++is a replacement for the normal system crypt.
++It is much faster than the system crypt.
++.PP
++.SH FILES
++/usr/include/des.h
++.br
++/usr/lib/libdes.a
++.PP
++The encryption routines have been tested on 16bit, 32bit and 64bit
++machines of various endian and even works under VMS.
++.PP
++.SH BUGS
++.PP
++If you think this manual is sparse,
++read the des_crypt(3) manual from the MIT kerberos (or bones outside
++of the USA) distribution.
++.PP
++.I des_cfb_encrypt
++and
++.I des_ofb_encrypt
++operates on input of 8 bits. What this means is that if you set
++numbits to 12, and length to 2, the first 12 bits will come from the 1st
++input byte and the low half of the second input byte. The second 12
++bits will have the low 8 bits taken from the 3rd input byte and the
++top 4 bits taken from the 4th input byte. The same holds for output.
++This function has been implemented this way because most people will
++be using a multiple of 8 and because once you get into pulling bytes input
++bytes apart things get ugly!
++.PP
++.I des_read_pw_string
++is the most machine/OS dependent function and normally generates the
++most problems when porting this code.
++.PP
++.I des_string_to_key
++is probably different from the MIT version since there are lots
++of fun ways to implement one-way encryption of a text string.
++.PP
++The routines are optimised for 32 bit machines and so are not efficient
++on IBM PCs.
++.PP
++NOTE: extensive work has been done on this library since this document
++was origionally written. Please try to read des.doc from the libdes
++distribution since it is far more upto date and documents more of the
++functions. Libdes is now also being shipped as part of SSLeay, a
++general cryptographic library that amonst other things implements
++netscapes SSL protocoll. The most recent version can be found in
++SSLeay distributions.
++.SH AUTHOR
++Eric Young (eay@cryptsoft.com)
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/crypto/ciphers/des/des_enc.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,502 @@
++/* crypto/des/des_enc.c */
++/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com)
++ * All rights reserved.
++ *
++ * This package is an SSL implementation written
++ * by Eric Young (eay@cryptsoft.com).
++ * The implementation was written so as to conform with Netscapes SSL.
++ *
++ * This library is free for commercial and non-commercial use as long as
++ * the following conditions are aheared to. The following conditions
++ * apply to all code found in this distribution, be it the RC4, RSA,
++ * lhash, DES, etc., code; not just the SSL code. The SSL documentation
++ * included with this distribution is covered by the same copyright terms
++ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
++ *
++ * Copyright remains Eric Young's, and as such any Copyright notices in
++ * the code are not to be removed.
++ * If this package is used in a product, Eric Young should be given attribution
++ * as the author of the parts of the library used.
++ * This can be in the form of a textual message at program startup or
++ * in documentation (online or textual) provided with the package.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ * 1. Redistributions of source code must retain the copyright
++ * notice, this list of conditions and the following disclaimer.
++ * 2. Redistributions in binary form must reproduce the above copyright
++ * notice, this list of conditions and the following disclaimer in the
++ * documentation and/or other materials provided with the distribution.
++ * 3. All advertising materials mentioning features or use of this software
++ * must display the following acknowledgement:
++ * "This product includes cryptographic software written by
++ * Eric Young (eay@cryptsoft.com)"
++ * The word 'cryptographic' can be left out if the rouines from the library
++ * being used are not cryptographic related :-).
++ * 4. If you include any Windows specific code (or a derivative thereof) from
++ * the apps directory (application code) you must include an acknowledgement:
++ * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
++ *
++ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ *
++ * The licence and distribution terms for any publically available version or
++ * derivative of this code cannot be changed. i.e. this code cannot simply be
++ * copied and put under another distribution licence
++ * [including the GNU Public Licence.]
++ */
++
++#include "des_locl.h"
++
++void des_encrypt(data, ks, enc)
++DES_LONG *data;
++des_key_schedule ks;
++int enc;
++ {
++ register DES_LONG l,r,t,u;
++#ifdef DES_PTR
++ register unsigned char *des_SP=(unsigned char *)des_SPtrans;
++#endif
++#ifndef DES_UNROLL
++ register int i;
++#endif
++ register DES_LONG *s;
++
++ r=data[0];
++ l=data[1];
++
++ IP(r,l);
++ /* Things have been modified so that the initial rotate is
++ * done outside the loop. This required the
++ * des_SPtrans values in sp.h to be rotated 1 bit to the right.
++ * One perl script later and things have a 5% speed up on a sparc2.
++ * Thanks to Richard Outerbridge <71755.204@CompuServe.COM>
++ * for pointing this out. */
++ /* clear the top bits on machines with 8byte longs */
++ /* shift left by 2 */
++ r=ROTATE(r,29)&0xffffffffL;
++ l=ROTATE(l,29)&0xffffffffL;
++
++ s=(DES_LONG *)ks;
++ /* I don't know if it is worth the effort of loop unrolling the
++ * inner loop */
++ if (enc)
++ {
++#ifdef DES_UNROLL
++ D_ENCRYPT(l,r, 0); /* 1 */
++ D_ENCRYPT(r,l, 2); /* 2 */
++ D_ENCRYPT(l,r, 4); /* 3 */
++ D_ENCRYPT(r,l, 6); /* 4 */
++ D_ENCRYPT(l,r, 8); /* 5 */
++ D_ENCRYPT(r,l,10); /* 6 */
++ D_ENCRYPT(l,r,12); /* 7 */
++ D_ENCRYPT(r,l,14); /* 8 */
++ D_ENCRYPT(l,r,16); /* 9 */
++ D_ENCRYPT(r,l,18); /* 10 */
++ D_ENCRYPT(l,r,20); /* 11 */
++ D_ENCRYPT(r,l,22); /* 12 */
++ D_ENCRYPT(l,r,24); /* 13 */
++ D_ENCRYPT(r,l,26); /* 14 */
++ D_ENCRYPT(l,r,28); /* 15 */
++ D_ENCRYPT(r,l,30); /* 16 */
++#else
++ for (i=0; i<32; i+=8)
++ {
++ D_ENCRYPT(l,r,i+0); /* 1 */
++ D_ENCRYPT(r,l,i+2); /* 2 */
++ D_ENCRYPT(l,r,i+4); /* 3 */
++ D_ENCRYPT(r,l,i+6); /* 4 */
++ }
++#endif
++ }
++ else
++ {
++#ifdef DES_UNROLL
++ D_ENCRYPT(l,r,30); /* 16 */
++ D_ENCRYPT(r,l,28); /* 15 */
++ D_ENCRYPT(l,r,26); /* 14 */
++ D_ENCRYPT(r,l,24); /* 13 */
++ D_ENCRYPT(l,r,22); /* 12 */
++ D_ENCRYPT(r,l,20); /* 11 */
++ D_ENCRYPT(l,r,18); /* 10 */
++ D_ENCRYPT(r,l,16); /* 9 */
++ D_ENCRYPT(l,r,14); /* 8 */
++ D_ENCRYPT(r,l,12); /* 7 */
++ D_ENCRYPT(l,r,10); /* 6 */
++ D_ENCRYPT(r,l, 8); /* 5 */
++ D_ENCRYPT(l,r, 6); /* 4 */
++ D_ENCRYPT(r,l, 4); /* 3 */
++ D_ENCRYPT(l,r, 2); /* 2 */
++ D_ENCRYPT(r,l, 0); /* 1 */
++#else
++ for (i=30; i>0; i-=8)
++ {
++ D_ENCRYPT(l,r,i-0); /* 16 */
++ D_ENCRYPT(r,l,i-2); /* 15 */
++ D_ENCRYPT(l,r,i-4); /* 14 */
++ D_ENCRYPT(r,l,i-6); /* 13 */
++ }
++#endif
++ }
++
++ /* rotate and clear the top bits on machines with 8byte longs */
++ l=ROTATE(l,3)&0xffffffffL;
++ r=ROTATE(r,3)&0xffffffffL;
++
++ FP(r,l);
++ data[0]=l;
++ data[1]=r;
++ l=r=t=u=0;
++ }
++
++void des_encrypt2(data, ks, enc)
++DES_LONG *data;
++des_key_schedule ks;
++int enc;
++ {
++ register DES_LONG l,r,t,u;
++#ifdef DES_PTR
++ register unsigned char *des_SP=(unsigned char *)des_SPtrans;
++#endif
++#ifndef DES_UNROLL
++ register int i;
++#endif
++ register DES_LONG *s;
++
++ r=data[0];
++ l=data[1];
++
++ /* Things have been modified so that the initial rotate is
++ * done outside the loop. This required the
++ * des_SPtrans values in sp.h to be rotated 1 bit to the right.
++ * One perl script later and things have a 5% speed up on a sparc2.
++ * Thanks to Richard Outerbridge <71755.204@CompuServe.COM>
++ * for pointing this out. */
++ /* clear the top bits on machines with 8byte longs */
++ r=ROTATE(r,29)&0xffffffffL;
++ l=ROTATE(l,29)&0xffffffffL;
++
++ s=(DES_LONG *)ks;
++ /* I don't know if it is worth the effort of loop unrolling the
++ * inner loop */
++ if (enc)
++ {
++#ifdef DES_UNROLL
++ D_ENCRYPT(l,r, 0); /* 1 */
++ D_ENCRYPT(r,l, 2); /* 2 */
++ D_ENCRYPT(l,r, 4); /* 3 */
++ D_ENCRYPT(r,l, 6); /* 4 */
++ D_ENCRYPT(l,r, 8); /* 5 */
++ D_ENCRYPT(r,l,10); /* 6 */
++ D_ENCRYPT(l,r,12); /* 7 */
++ D_ENCRYPT(r,l,14); /* 8 */
++ D_ENCRYPT(l,r,16); /* 9 */
++ D_ENCRYPT(r,l,18); /* 10 */
++ D_ENCRYPT(l,r,20); /* 11 */
++ D_ENCRYPT(r,l,22); /* 12 */
++ D_ENCRYPT(l,r,24); /* 13 */
++ D_ENCRYPT(r,l,26); /* 14 */
++ D_ENCRYPT(l,r,28); /* 15 */
++ D_ENCRYPT(r,l,30); /* 16 */
++#else
++ for (i=0; i<32; i+=8)
++ {
++ D_ENCRYPT(l,r,i+0); /* 1 */
++ D_ENCRYPT(r,l,i+2); /* 2 */
++ D_ENCRYPT(l,r,i+4); /* 3 */
++ D_ENCRYPT(r,l,i+6); /* 4 */
++ }
++#endif
++ }
++ else
++ {
++#ifdef DES_UNROLL
++ D_ENCRYPT(l,r,30); /* 16 */
++ D_ENCRYPT(r,l,28); /* 15 */
++ D_ENCRYPT(l,r,26); /* 14 */
++ D_ENCRYPT(r,l,24); /* 13 */
++ D_ENCRYPT(l,r,22); /* 12 */
++ D_ENCRYPT(r,l,20); /* 11 */
++ D_ENCRYPT(l,r,18); /* 10 */
++ D_ENCRYPT(r,l,16); /* 9 */
++ D_ENCRYPT(l,r,14); /* 8 */
++ D_ENCRYPT(r,l,12); /* 7 */
++ D_ENCRYPT(l,r,10); /* 6 */
++ D_ENCRYPT(r,l, 8); /* 5 */
++ D_ENCRYPT(l,r, 6); /* 4 */
++ D_ENCRYPT(r,l, 4); /* 3 */
++ D_ENCRYPT(l,r, 2); /* 2 */
++ D_ENCRYPT(r,l, 0); /* 1 */
++#else
++ for (i=30; i>0; i-=8)
++ {
++ D_ENCRYPT(l,r,i-0); /* 16 */
++ D_ENCRYPT(r,l,i-2); /* 15 */
++ D_ENCRYPT(l,r,i-4); /* 14 */
++ D_ENCRYPT(r,l,i-6); /* 13 */
++ }
++#endif
++ }
++ /* rotate and clear the top bits on machines with 8byte longs */
++ data[0]=ROTATE(l,3)&0xffffffffL;
++ data[1]=ROTATE(r,3)&0xffffffffL;
++ l=r=t=u=0;
++ }
++
++void des_encrypt3(data,ks1,ks2,ks3)
++DES_LONG *data;
++des_key_schedule ks1;
++des_key_schedule ks2;
++des_key_schedule ks3;
++ {
++ register DES_LONG l,r;
++
++ l=data[0];
++ r=data[1];
++ IP(l,r);
++ data[0]=l;
++ data[1]=r;
++ des_encrypt2((DES_LONG *)data,ks1,DES_ENCRYPT);
++ des_encrypt2((DES_LONG *)data,ks2,DES_DECRYPT);
++ des_encrypt2((DES_LONG *)data,ks3,DES_ENCRYPT);
++ l=data[0];
++ r=data[1];
++ FP(r,l);
++ data[0]=l;
++ data[1]=r;
++ }
++
++void des_decrypt3(data,ks1,ks2,ks3)
++DES_LONG *data;
++des_key_schedule ks1;
++des_key_schedule ks2;
++des_key_schedule ks3;
++ {
++ register DES_LONG l,r;
++
++ l=data[0];
++ r=data[1];
++ IP(l,r);
++ data[0]=l;
++ data[1]=r;
++ des_encrypt2((DES_LONG *)data,ks3,DES_DECRYPT);
++ des_encrypt2((DES_LONG *)data,ks2,DES_ENCRYPT);
++ des_encrypt2((DES_LONG *)data,ks1,DES_DECRYPT);
++ l=data[0];
++ r=data[1];
++ FP(r,l);
++ data[0]=l;
++ data[1]=r;
++ }
++
++#ifndef DES_DEFAULT_OPTIONS
++
++void des_ncbc_encrypt(input, output, length, schedule, ivec, enc)
++des_cblock (*input);
++des_cblock (*output);
++long length;
++des_key_schedule schedule;
++des_cblock (*ivec);
++int enc;
++ {
++ register DES_LONG tin0,tin1;
++ register DES_LONG tout0,tout1,xor0,xor1;
++ register unsigned char *in,*out;
++ register long l=length;
++ DES_LONG tin[2];
++ unsigned char *iv;
++
++ in=(unsigned char *)input;
++ out=(unsigned char *)output;
++ iv=(unsigned char *)ivec;
++
++ if (enc)
++ {
++ c2l(iv,tout0);
++ c2l(iv,tout1);
++ for (l-=8; l>=0; l-=8)
++ {
++ c2l(in,tin0);
++ c2l(in,tin1);
++ tin0^=tout0; tin[0]=tin0;
++ tin1^=tout1; tin[1]=tin1;
++ des_encrypt((DES_LONG *)tin,schedule,DES_ENCRYPT);
++ tout0=tin[0]; l2c(tout0,out);
++ tout1=tin[1]; l2c(tout1,out);
++ }
++ if (l != -8)
++ {
++ c2ln(in,tin0,tin1,l+8);
++ tin0^=tout0; tin[0]=tin0;
++ tin1^=tout1; tin[1]=tin1;
++ des_encrypt((DES_LONG *)tin,schedule,DES_ENCRYPT);
++ tout0=tin[0]; l2c(tout0,out);
++ tout1=tin[1]; l2c(tout1,out);
++ }
++ iv=(unsigned char *)ivec;
++ l2c(tout0,iv);
++ l2c(tout1,iv);
++ }
++ else
++ {
++ c2l(iv,xor0);
++ c2l(iv,xor1);
++ for (l-=8; l>=0; l-=8)
++ {
++ c2l(in,tin0); tin[0]=tin0;
++ c2l(in,tin1); tin[1]=tin1;
++ des_encrypt((DES_LONG *)tin,schedule,DES_DECRYPT);
++ tout0=tin[0]^xor0;
++ tout1=tin[1]^xor1;
++ l2c(tout0,out);
++ l2c(tout1,out);
++ xor0=tin0;
++ xor1=tin1;
++ }
++ if (l != -8)
++ {
++ c2l(in,tin0); tin[0]=tin0;
++ c2l(in,tin1); tin[1]=tin1;
++ des_encrypt((DES_LONG *)tin,schedule,DES_DECRYPT);
++ tout0=tin[0]^xor0;
++ tout1=tin[1]^xor1;
++ l2cn(tout0,tout1,out,l+8);
++ xor0=tin0;
++ xor1=tin1;
++ }
++
++ iv=(unsigned char *)ivec;
++ l2c(xor0,iv);
++ l2c(xor1,iv);
++ }
++ tin0=tin1=tout0=tout1=xor0=xor1=0;
++ tin[0]=tin[1]=0;
++ }
++
++void des_ede3_cbc_encrypt(input, output, length, ks1, ks2, ks3, ivec, enc)
++des_cblock (*input);
++des_cblock (*output);
++long length;
++des_key_schedule ks1;
++des_key_schedule ks2;
++des_key_schedule ks3;
++des_cblock (*ivec);
++int enc;
++ {
++ register DES_LONG tin0,tin1;
++ register DES_LONG tout0,tout1,xor0,xor1;
++ register unsigned char *in,*out;
++ register long l=length;
++ DES_LONG tin[2];
++ unsigned char *iv;
++
++ in=(unsigned char *)input;
++ out=(unsigned char *)output;
++ iv=(unsigned char *)ivec;
++
++ if (enc)
++ {
++ c2l(iv,tout0);
++ c2l(iv,tout1);
++ for (l-=8; l>=0; l-=8)
++ {
++ c2l(in,tin0);
++ c2l(in,tin1);
++ tin0^=tout0;
++ tin1^=tout1;
++
++ tin[0]=tin0;
++ tin[1]=tin1;
++ des_encrypt3((DES_LONG *)tin,ks1,ks2,ks3);
++ tout0=tin[0];
++ tout1=tin[1];
++
++ l2c(tout0,out);
++ l2c(tout1,out);
++ }
++ if (l != -8)
++ {
++ c2ln(in,tin0,tin1,l+8);
++ tin0^=tout0;
++ tin1^=tout1;
++
++ tin[0]=tin0;
++ tin[1]=tin1;
++ des_encrypt3((DES_LONG *)tin,ks1,ks2,ks3);
++ tout0=tin[0];
++ tout1=tin[1];
++
++ l2c(tout0,out);
++ l2c(tout1,out);
++ }
++ iv=(unsigned char *)ivec;
++ l2c(tout0,iv);
++ l2c(tout1,iv);
++ }
++ else
++ {
++ register DES_LONG t0,t1;
++
++ c2l(iv,xor0);
++ c2l(iv,xor1);
++ for (l-=8; l>=0; l-=8)
++ {
++ c2l(in,tin0);
++ c2l(in,tin1);
++
++ t0=tin0;
++ t1=tin1;
++
++ tin[0]=tin0;
++ tin[1]=tin1;
++ des_decrypt3((DES_LONG *)tin,ks1,ks2,ks3);
++ tout0=tin[0];
++ tout1=tin[1];
++
++ tout0^=xor0;
++ tout1^=xor1;
++ l2c(tout0,out);
++ l2c(tout1,out);
++ xor0=t0;
++ xor1=t1;
++ }
++ if (l != -8)
++ {
++ c2l(in,tin0);
++ c2l(in,tin1);
++
++ t0=tin0;
++ t1=tin1;
++
++ tin[0]=tin0;
++ tin[1]=tin1;
++ des_decrypt3((DES_LONG *)tin,ks1,ks2,ks3);
++ tout0=tin[0];
++ tout1=tin[1];
++
++ tout0^=xor0;
++ tout1^=xor1;
++ l2cn(tout0,tout1,out,l+8);
++ xor0=t0;
++ xor1=t1;
++ }
++
++ iv=(unsigned char *)ivec;
++ l2c(xor0,iv);
++ l2c(xor1,iv);
++ }
++ tin0=tin1=tout0=tout1=xor0=xor1=0;
++ tin[0]=tin[1]=0;
++ }
++
++#endif /* DES_DEFAULT_OPTIONS */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/crypto/ciphers/des/des_locl.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,515 @@
++/* crypto/des/des_locl.org */
++/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com)
++ * All rights reserved.
++ *
++ * This package is an SSL implementation written
++ * by Eric Young (eay@cryptsoft.com).
++ * The implementation was written so as to conform with Netscapes SSL.
++ *
++ * This library is free for commercial and non-commercial use as long as
++ * the following conditions are aheared to. The following conditions
++ * apply to all code found in this distribution, be it the RC4, RSA,
++ * lhash, DES, etc., code; not just the SSL code. The SSL documentation
++ * included with this distribution is covered by the same copyright terms
++ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
++ *
++ * Copyright remains Eric Young's, and as such any Copyright notices in
++ * the code are not to be removed.
++ * If this package is used in a product, Eric Young should be given attribution
++ * as the author of the parts of the library used.
++ * This can be in the form of a textual message at program startup or
++ * in documentation (online or textual) provided with the package.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ * 1. Redistributions of source code must retain the copyright
++ * notice, this list of conditions and the following disclaimer.
++ * 2. Redistributions in binary form must reproduce the above copyright
++ * notice, this list of conditions and the following disclaimer in the
++ * documentation and/or other materials provided with the distribution.
++ * 3. All advertising materials mentioning features or use of this software
++ * must display the following acknowledgement:
++ * "This product includes cryptographic software written by
++ * Eric Young (eay@cryptsoft.com)"
++ * The word 'cryptographic' can be left out if the rouines from the library
++ * being used are not cryptographic related :-).
++ * 4. If you include any Windows specific code (or a derivative thereof) from
++ * the apps directory (application code) you must include an acknowledgement:
++ * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
++ *
++ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ *
++ * The licence and distribution terms for any publically available version or
++ * derivative of this code cannot be changed. i.e. this code cannot simply be
++ * copied and put under another distribution licence
++ * [including the GNU Public Licence.]
++ */
++
++/* WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
++ *
++ * Always modify des_locl.org since des_locl.h is automatically generated from
++ * it during SSLeay configuration.
++ *
++ * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
++ */
++
++#ifndef HEADER_DES_LOCL_H
++#define HEADER_DES_LOCL_H
++
++#if defined(WIN32) || defined(WIN16)
++#ifndef MSDOS
++#define MSDOS
++#endif
++#endif
++
++#include "crypto/des.h"
++
++#ifndef DES_DEFAULT_OPTIONS
++/* the following is tweaked from a config script, that is why it is a
++ * protected undef/define */
++#ifndef DES_PTR
++#define DES_PTR
++#endif
++
++/* This helps C compiler generate the correct code for multiple functional
++ * units. It reduces register dependancies at the expense of 2 more
++ * registers */
++#ifndef DES_RISC1
++#define DES_RISC1
++#endif
++
++#ifndef DES_RISC2
++#undef DES_RISC2
++#endif
++
++#if defined(DES_RISC1) && defined(DES_RISC2)
++YOU SHOULD NOT HAVE BOTH DES_RISC1 AND DES_RISC2 DEFINED!!!!!
++#endif
++
++/* Unroll the inner loop, this sometimes helps, sometimes hinders.
++ * Very mucy CPU dependant */
++#ifndef DES_UNROLL
++#define DES_UNROLL
++#endif
++
++/* These default values were supplied by
++ * Peter Gutman <pgut001@cs.auckland.ac.nz>
++ * They are only used if nothing else has been defined */
++#if !defined(DES_PTR) && !defined(DES_RISC1) && !defined(DES_RISC2) && !defined(DES_UNROLL)
++/* Special defines which change the way the code is built depending on the
++ CPU and OS. For SGI machines you can use _MIPS_SZLONG (32 or 64) to find
++ even newer MIPS CPU's, but at the moment one size fits all for
++ optimization options. Older Sparc's work better with only UNROLL, but
++ there's no way to tell at compile time what it is you're running on */
++
++#if defined( sun ) /* Newer Sparc's */
++ #define DES_PTR
++ #define DES_RISC1
++ #define DES_UNROLL
++#elif defined( __ultrix ) /* Older MIPS */
++ #define DES_PTR
++ #define DES_RISC2
++ #define DES_UNROLL
++#elif defined( __osf1__ ) /* Alpha */
++ #define DES_PTR
++ #define DES_RISC2
++#elif defined ( _AIX ) /* RS6000 */
++ /* Unknown */
++#elif defined( __hpux ) /* HP-PA */
++ /* Unknown */
++#elif defined( __aux ) /* 68K */
++ /* Unknown */
++#elif defined( __dgux ) /* 88K (but P6 in latest boxes) */
++ #define DES_UNROLL
++#elif defined( __sgi ) /* Newer MIPS */
++ #define DES_PTR
++ #define DES_RISC2
++ #define DES_UNROLL
++#elif defined( i386 ) /* x86 boxes, should be gcc */
++ #define DES_PTR
++ #define DES_RISC1
++ #define DES_UNROLL
++#endif /* Systems-specific speed defines */
++#endif
++
++#endif /* DES_DEFAULT_OPTIONS */
++
++#ifdef MSDOS /* Visual C++ 2.1 (Windows NT/95) */
++#include <stdlib.h>
++#include <errno.h>
++#include <time.h>
++#include <io.h>
++#ifndef RAND
++#define RAND
++#endif
++#undef NOPROTO
++#endif
++
++#if defined(__STDC__) || defined(VMS) || defined(M_XENIX) || defined(MSDOS)
++#ifndef __KERNEL__
++#include <string.h>
++#else
++#include <linux/string.h>
++#endif
++#endif
++
++#ifndef RAND
++#define RAND
++#endif
++
++#ifdef linux
++#undef RAND
++#endif
++
++#ifdef MSDOS
++#define getpid() 2
++#define RAND
++#undef NOPROTO
++#endif
++
++#if defined(NOCONST)
++#define const
++#endif
++
++#ifdef __STDC__
++#undef NOPROTO
++#endif
++
++#ifdef RAND
++#define srandom(s) srand(s)
++#define random rand
++#endif
++
++#define ITERATIONS 16
++#define HALF_ITERATIONS 8
++
++/* used in des_read and des_write */
++#define MAXWRITE (1024*16)
++#define BSIZE (MAXWRITE+4)
++
++#define c2l(c,l) (l =((DES_LONG)(*((c)++))) , \
++ l|=((DES_LONG)(*((c)++)))<< 8L, \
++ l|=((DES_LONG)(*((c)++)))<<16L, \
++ l|=((DES_LONG)(*((c)++)))<<24L)
++
++/* NOTE - c is not incremented as per c2l */
++#define c2ln(c,l1,l2,n) { \
++ c+=n; \
++ l1=l2=0; \
++ switch (n) { \
++ case 8: l2 =((DES_LONG)(*(--(c))))<<24L; \
++ case 7: l2|=((DES_LONG)(*(--(c))))<<16L; \
++ case 6: l2|=((DES_LONG)(*(--(c))))<< 8L; \
++ case 5: l2|=((DES_LONG)(*(--(c)))); \
++ case 4: l1 =((DES_LONG)(*(--(c))))<<24L; \
++ case 3: l1|=((DES_LONG)(*(--(c))))<<16L; \
++ case 2: l1|=((DES_LONG)(*(--(c))))<< 8L; \
++ case 1: l1|=((DES_LONG)(*(--(c)))); \
++ } \
++ }
++
++#define l2c(l,c) (*((c)++)=(unsigned char)(((l) )&0xff), \
++ *((c)++)=(unsigned char)(((l)>> 8L)&0xff), \
++ *((c)++)=(unsigned char)(((l)>>16L)&0xff), \
++ *((c)++)=(unsigned char)(((l)>>24L)&0xff))
++
++/* replacements for htonl and ntohl since I have no idea what to do
++ * when faced with machines with 8 byte longs. */
++#define HDRSIZE 4
++
++#define n2l(c,l) (l =((DES_LONG)(*((c)++)))<<24L, \
++ l|=((DES_LONG)(*((c)++)))<<16L, \
++ l|=((DES_LONG)(*((c)++)))<< 8L, \
++ l|=((DES_LONG)(*((c)++))))
++
++#define l2n(l,c) (*((c)++)=(unsigned char)(((l)>>24L)&0xff), \
++ *((c)++)=(unsigned char)(((l)>>16L)&0xff), \
++ *((c)++)=(unsigned char)(((l)>> 8L)&0xff), \
++ *((c)++)=(unsigned char)(((l) )&0xff))
++
++/* NOTE - c is not incremented as per l2c */
++#define l2cn(l1,l2,c,n) { \
++ c+=n; \
++ switch (n) { \
++ case 8: *(--(c))=(unsigned char)(((l2)>>24L)&0xff); \
++ case 7: *(--(c))=(unsigned char)(((l2)>>16L)&0xff); \
++ case 6: *(--(c))=(unsigned char)(((l2)>> 8L)&0xff); \
++ case 5: *(--(c))=(unsigned char)(((l2) )&0xff); \
++ case 4: *(--(c))=(unsigned char)(((l1)>>24L)&0xff); \
++ case 3: *(--(c))=(unsigned char)(((l1)>>16L)&0xff); \
++ case 2: *(--(c))=(unsigned char)(((l1)>> 8L)&0xff); \
++ case 1: *(--(c))=(unsigned char)(((l1) )&0xff); \
++ } \
++ }
++
++#if defined(WIN32)
++#define ROTATE(a,n) (_lrotr(a,n))
++#else
++#define ROTATE(a,n) (((a)>>(n))+((a)<<(32-(n))))
++#endif
++
++/* Don't worry about the LOAD_DATA() stuff, that is used by
++ * fcrypt() to add it's little bit to the front */
++
++#ifdef DES_FCRYPT
++
++#define LOAD_DATA_tmp(R,S,u,t,E0,E1) \
++ { DES_LONG tmp; LOAD_DATA(R,S,u,t,E0,E1,tmp); }
++
++#define LOAD_DATA(R,S,u,t,E0,E1,tmp) \
++ t=R^(R>>16L); \
++ u=t&E0; t&=E1; \
++ tmp=(u<<16); u^=R^s[S ]; u^=tmp; \
++ tmp=(t<<16); t^=R^s[S+1]; t^=tmp
++#else
++#define LOAD_DATA_tmp(a,b,c,d,e,f) LOAD_DATA(a,b,c,d,e,f,g)
++#define LOAD_DATA(R,S,u,t,E0,E1,tmp) \
++ u=R^s[S ]; \
++ t=R^s[S+1]
++#endif
++
++/* The changes to this macro may help or hinder, depending on the
++ * compiler and the achitecture. gcc2 always seems to do well :-).
++ * Inspired by Dana How <how@isl.stanford.edu>
++ * DO NOT use the alternative version on machines with 8 byte longs.
++ * It does not seem to work on the Alpha, even when DES_LONG is 4
++ * bytes, probably an issue of accessing non-word aligned objects :-( */
++#ifdef DES_PTR
++
++/* It recently occured to me that 0^0^0^0^0^0^0 == 0, so there
++ * is no reason to not xor all the sub items together. This potentially
++ * saves a register since things can be xored directly into L */
++
++#if defined(DES_RISC1) || defined(DES_RISC2)
++#ifdef DES_RISC1
++#define D_ENCRYPT(LL,R,S) { \
++ unsigned int u1,u2,u3; \
++ LOAD_DATA(R,S,u,t,E0,E1,u1); \
++ u2=(int)u>>8L; \
++ u1=(int)u&0xfc; \
++ u2&=0xfc; \
++ t=ROTATE(t,4); \
++ u>>=16L; \
++ LL^= *(DES_LONG *)((unsigned char *)des_SP +u1); \
++ LL^= *(DES_LONG *)((unsigned char *)des_SP+0x200+u2); \
++ u3=(int)(u>>8L); \
++ u1=(int)u&0xfc; \
++ u3&=0xfc; \
++ LL^= *(DES_LONG *)((unsigned char *)des_SP+0x400+u1); \
++ LL^= *(DES_LONG *)((unsigned char *)des_SP+0x600+u3); \
++ u2=(int)t>>8L; \
++ u1=(int)t&0xfc; \
++ u2&=0xfc; \
++ t>>=16L; \
++ LL^= *(DES_LONG *)((unsigned char *)des_SP+0x100+u1); \
++ LL^= *(DES_LONG *)((unsigned char *)des_SP+0x300+u2); \
++ u3=(int)t>>8L; \
++ u1=(int)t&0xfc; \
++ u3&=0xfc; \
++ LL^= *(DES_LONG *)((unsigned char *)des_SP+0x500+u1); \
++ LL^= *(DES_LONG *)((unsigned char *)des_SP+0x700+u3); }
++#endif
++#ifdef DES_RISC2
++#define D_ENCRYPT(LL,R,S) { \
++ unsigned int u1,u2,s1,s2; \
++ LOAD_DATA(R,S,u,t,E0,E1,u1); \
++ u2=(int)u>>8L; \
++ u1=(int)u&0xfc; \
++ u2&=0xfc; \
++ t=ROTATE(t,4); \
++ LL^= *(DES_LONG *)((unsigned char *)des_SP +u1); \
++ LL^= *(DES_LONG *)((unsigned char *)des_SP+0x200+u2); \
++ s1=(int)(u>>16L); \
++ s2=(int)(u>>24L); \
++ s1&=0xfc; \
++ s2&=0xfc; \
++ LL^= *(DES_LONG *)((unsigned char *)des_SP+0x400+s1); \
++ LL^= *(DES_LONG *)((unsigned char *)des_SP+0x600+s2); \
++ u2=(int)t>>8L; \
++ u1=(int)t&0xfc; \
++ u2&=0xfc; \
++ LL^= *(DES_LONG *)((unsigned char *)des_SP+0x100+u1); \
++ LL^= *(DES_LONG *)((unsigned char *)des_SP+0x300+u2); \
++ s1=(int)(t>>16L); \
++ s2=(int)(t>>24L); \
++ s1&=0xfc; \
++ s2&=0xfc; \
++ LL^= *(DES_LONG *)((unsigned char *)des_SP+0x500+s1); \
++ LL^= *(DES_LONG *)((unsigned char *)des_SP+0x700+s2); }
++#endif
++#else
++#define D_ENCRYPT(LL,R,S) { \
++ LOAD_DATA_tmp(R,S,u,t,E0,E1); \
++ t=ROTATE(t,4); \
++ LL^= \
++ *(DES_LONG *)((unsigned char *)des_SP +((u )&0xfc))^ \
++ *(DES_LONG *)((unsigned char *)des_SP+0x200+((u>> 8L)&0xfc))^ \
++ *(DES_LONG *)((unsigned char *)des_SP+0x400+((u>>16L)&0xfc))^ \
++ *(DES_LONG *)((unsigned char *)des_SP+0x600+((u>>24L)&0xfc))^ \
++ *(DES_LONG *)((unsigned char *)des_SP+0x100+((t )&0xfc))^ \
++ *(DES_LONG *)((unsigned char *)des_SP+0x300+((t>> 8L)&0xfc))^ \
++ *(DES_LONG *)((unsigned char *)des_SP+0x500+((t>>16L)&0xfc))^ \
++ *(DES_LONG *)((unsigned char *)des_SP+0x700+((t>>24L)&0xfc)); }
++#endif
++
++#else /* original version */
++
++#if defined(DES_RISC1) || defined(DES_RISC2)
++#ifdef DES_RISC1
++#define D_ENCRYPT(LL,R,S) {\
++ unsigned int u1,u2,u3; \
++ LOAD_DATA(R,S,u,t,E0,E1,u1); \
++ u>>=2L; \
++ t=ROTATE(t,6); \
++ u2=(int)u>>8L; \
++ u1=(int)u&0x3f; \
++ u2&=0x3f; \
++ u>>=16L; \
++ LL^=des_SPtrans[0][u1]; \
++ LL^=des_SPtrans[2][u2]; \
++ u3=(int)u>>8L; \
++ u1=(int)u&0x3f; \
++ u3&=0x3f; \
++ LL^=des_SPtrans[4][u1]; \
++ LL^=des_SPtrans[6][u3]; \
++ u2=(int)t>>8L; \
++ u1=(int)t&0x3f; \
++ u2&=0x3f; \
++ t>>=16L; \
++ LL^=des_SPtrans[1][u1]; \
++ LL^=des_SPtrans[3][u2]; \
++ u3=(int)t>>8L; \
++ u1=(int)t&0x3f; \
++ u3&=0x3f; \
++ LL^=des_SPtrans[5][u1]; \
++ LL^=des_SPtrans[7][u3]; }
++#endif
++#ifdef DES_RISC2
++#define D_ENCRYPT(LL,R,S) {\
++ unsigned int u1,u2,s1,s2; \
++ LOAD_DATA(R,S,u,t,E0,E1,u1); \
++ u>>=2L; \
++ t=ROTATE(t,6); \
++ u2=(int)u>>8L; \
++ u1=(int)u&0x3f; \
++ u2&=0x3f; \
++ LL^=des_SPtrans[0][u1]; \
++ LL^=des_SPtrans[2][u2]; \
++ s1=(int)u>>16L; \
++ s2=(int)u>>24L; \
++ s1&=0x3f; \
++ s2&=0x3f; \
++ LL^=des_SPtrans[4][s1]; \
++ LL^=des_SPtrans[6][s2]; \
++ u2=(int)t>>8L; \
++ u1=(int)t&0x3f; \
++ u2&=0x3f; \
++ LL^=des_SPtrans[1][u1]; \
++ LL^=des_SPtrans[3][u2]; \
++ s1=(int)t>>16; \
++ s2=(int)t>>24L; \
++ s1&=0x3f; \
++ s2&=0x3f; \
++ LL^=des_SPtrans[5][s1]; \
++ LL^=des_SPtrans[7][s2]; }
++#endif
++
++#else
++
++#define D_ENCRYPT(LL,R,S) {\
++ LOAD_DATA_tmp(R,S,u,t,E0,E1); \
++ t=ROTATE(t,4); \
++ LL^=\
++ des_SPtrans[0][(u>> 2L)&0x3f]^ \
++ des_SPtrans[2][(u>>10L)&0x3f]^ \
++ des_SPtrans[4][(u>>18L)&0x3f]^ \
++ des_SPtrans[6][(u>>26L)&0x3f]^ \
++ des_SPtrans[1][(t>> 2L)&0x3f]^ \
++ des_SPtrans[3][(t>>10L)&0x3f]^ \
++ des_SPtrans[5][(t>>18L)&0x3f]^ \
++ des_SPtrans[7][(t>>26L)&0x3f]; }
++#endif
++#endif
++
++ /* IP and FP
++ * The problem is more of a geometric problem that random bit fiddling.
++ 0 1 2 3 4 5 6 7 62 54 46 38 30 22 14 6
++ 8 9 10 11 12 13 14 15 60 52 44 36 28 20 12 4
++ 16 17 18 19 20 21 22 23 58 50 42 34 26 18 10 2
++ 24 25 26 27 28 29 30 31 to 56 48 40 32 24 16 8 0
++
++ 32 33 34 35 36 37 38 39 63 55 47 39 31 23 15 7
++ 40 41 42 43 44 45 46 47 61 53 45 37 29 21 13 5
++ 48 49 50 51 52 53 54 55 59 51 43 35 27 19 11 3
++ 56 57 58 59 60 61 62 63 57 49 41 33 25 17 9 1
++
++ The output has been subject to swaps of the form
++ 0 1 -> 3 1 but the odd and even bits have been put into
++ 2 3 2 0
++ different words. The main trick is to remember that
++ t=((l>>size)^r)&(mask);
++ r^=t;
++ l^=(t<<size);
++ can be used to swap and move bits between words.
++
++ So l = 0 1 2 3 r = 16 17 18 19
++ 4 5 6 7 20 21 22 23
++ 8 9 10 11 24 25 26 27
++ 12 13 14 15 28 29 30 31
++ becomes (for size == 2 and mask == 0x3333)
++ t = 2^16 3^17 -- -- l = 0 1 16 17 r = 2 3 18 19
++ 6^20 7^21 -- -- 4 5 20 21 6 7 22 23
++ 10^24 11^25 -- -- 8 9 24 25 10 11 24 25
++ 14^28 15^29 -- -- 12 13 28 29 14 15 28 29
++
++ Thanks for hints from Richard Outerbridge - he told me IP&FP
++ could be done in 15 xor, 10 shifts and 5 ands.
++ When I finally started to think of the problem in 2D
++ I first got ~42 operations without xors. When I remembered
++ how to use xors :-) I got it to its final state.
++ */
++#define PERM_OP(a,b,t,n,m) ((t)=((((a)>>(n))^(b))&(m)),\
++ (b)^=(t),\
++ (a)^=((t)<<(n)))
++
++#define IP(l,r) \
++ { \
++ register DES_LONG tt; \
++ PERM_OP(r,l,tt, 4,0x0f0f0f0fL); \
++ PERM_OP(l,r,tt,16,0x0000ffffL); \
++ PERM_OP(r,l,tt, 2,0x33333333L); \
++ PERM_OP(l,r,tt, 8,0x00ff00ffL); \
++ PERM_OP(r,l,tt, 1,0x55555555L); \
++ }
++
++#define FP(l,r) \
++ { \
++ register DES_LONG tt; \
++ PERM_OP(l,r,tt, 1,0x55555555L); \
++ PERM_OP(r,l,tt, 8,0x00ff00ffL); \
++ PERM_OP(l,r,tt, 2,0x33333333L); \
++ PERM_OP(r,l,tt,16,0x0000ffffL); \
++ PERM_OP(l,r,tt, 4,0x0f0f0f0fL); \
++ }
++
++extern const DES_LONG des_SPtrans[8][64];
++
++#ifndef NOPROTO
++void fcrypt_body(DES_LONG *out,des_key_schedule ks,
++ DES_LONG Eswap0, DES_LONG Eswap1);
++#else
++void fcrypt_body();
++#endif
++
++#endif
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/crypto/ciphers/des/des_opts.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,620 @@
++/* crypto/des/des_opts.c */
++/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com)
++ * All rights reserved.
++ *
++ * This package is an SSL implementation written
++ * by Eric Young (eay@cryptsoft.com).
++ * The implementation was written so as to conform with Netscapes SSL.
++ *
++ * This library is free for commercial and non-commercial use as long as
++ * the following conditions are aheared to. The following conditions
++ * apply to all code found in this distribution, be it the RC4, RSA,
++ * lhash, DES, etc., code; not just the SSL code. The SSL documentation
++ * included with this distribution is covered by the same copyright terms
++ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
++ *
++ * Copyright remains Eric Young's, and as such any Copyright notices in
++ * the code are not to be removed.
++ * If this package is used in a product, Eric Young should be given attribution
++ * as the author of the parts of the library used.
++ * This can be in the form of a textual message at program startup or
++ * in documentation (online or textual) provided with the package.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ * 1. Redistributions of source code must retain the copyright
++ * notice, this list of conditions and the following disclaimer.
++ * 2. Redistributions in binary form must reproduce the above copyright
++ * notice, this list of conditions and the following disclaimer in the
++ * documentation and/or other materials provided with the distribution.
++ * 3. All advertising materials mentioning features or use of this software
++ * must display the following acknowledgement:
++ * "This product includes cryptographic software written by
++ * Eric Young (eay@cryptsoft.com)"
++ * The word 'cryptographic' can be left out if the rouines from the library
++ * being used are not cryptographic related :-).
++ * 4. If you include any Windows specific code (or a derivative thereof) from
++ * the apps directory (application code) you must include an acknowledgement:
++ * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
++ *
++ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ *
++ * The licence and distribution terms for any publically available version or
++ * derivative of this code cannot be changed. i.e. this code cannot simply be
++ * copied and put under another distribution licence
++ * [including the GNU Public Licence.]
++ */
++
++/* define PART1, PART2, PART3 or PART4 to build only with a few of the options.
++ * This is for machines with 64k code segment size restrictions. */
++
++#ifndef MSDOS
++#define TIMES
++#endif
++
++#include <stdio.h>
++#ifndef MSDOS
++#include <unistd.h>
++#else
++#include <io.h>
++extern void exit();
++#endif
++#include <signal.h>
++#ifndef VMS
++#ifndef _IRIX
++#include <time.h>
++#endif
++#ifdef TIMES
++#include <sys/types.h>
++#include <sys/times.h>
++#endif
++#else /* VMS */
++#include <types.h>
++struct tms {
++ time_t tms_utime;
++ time_t tms_stime;
++ time_t tms_uchild; /* I dunno... */
++ time_t tms_uchildsys; /* so these names are a guess :-) */
++ }
++#endif
++#ifndef TIMES
++#include <sys/timeb.h>
++#endif
++
++#ifdef sun
++#include <limits.h>
++#include <sys/param.h>
++#endif
++
++#include "des_locl.h"
++#include "spr.h"
++
++#define DES_DEFAULT_OPTIONS
++
++#if !defined(PART1) && !defined(PART2) && !defined(PART3) && !defined(PART4)
++#define PART1
++#define PART2
++#define PART3
++#define PART4
++#endif
++
++#ifdef PART1
++
++#undef DES_UNROLL
++#undef DES_RISC1
++#undef DES_RISC2
++#undef DES_PTR
++#undef D_ENCRYPT
++#define des_encrypt des_encrypt_u4_cisc_idx
++#define des_encrypt2 des_encrypt2_u4_cisc_idx
++#define des_encrypt3 des_encrypt3_u4_cisc_idx
++#define des_decrypt3 des_decrypt3_u4_cisc_idx
++#undef HEADER_DES_LOCL_H
++#include "des_enc.c"
++
++#define DES_UNROLL
++#undef DES_RISC1
++#undef DES_RISC2
++#undef DES_PTR
++#undef D_ENCRYPT
++#undef des_encrypt
++#undef des_encrypt2
++#undef des_encrypt3
++#undef des_decrypt3
++#define des_encrypt des_encrypt_u16_cisc_idx
++#define des_encrypt2 des_encrypt2_u16_cisc_idx
++#define des_encrypt3 des_encrypt3_u16_cisc_idx
++#define des_decrypt3 des_decrypt3_u16_cisc_idx
++#undef HEADER_DES_LOCL_H
++#include "des_enc.c"
++
++#undef DES_UNROLL
++#define DES_RISC1
++#undef DES_RISC2
++#undef DES_PTR
++#undef D_ENCRYPT
++#undef des_encrypt
++#undef des_encrypt2
++#undef des_encrypt3
++#undef des_decrypt3
++#define des_encrypt des_encrypt_u4_risc1_idx
++#define des_encrypt2 des_encrypt2_u4_risc1_idx
++#define des_encrypt3 des_encrypt3_u4_risc1_idx
++#define des_decrypt3 des_decrypt3_u4_risc1_idx
++#undef HEADER_DES_LOCL_H
++#include "des_enc.c"
++
++#endif
++
++#ifdef PART2
++
++#undef DES_UNROLL
++#undef DES_RISC1
++#define DES_RISC2
++#undef DES_PTR
++#undef D_ENCRYPT
++#undef des_encrypt
++#undef des_encrypt2
++#undef des_encrypt3
++#undef des_decrypt3
++#define des_encrypt des_encrypt_u4_risc2_idx
++#define des_encrypt2 des_encrypt2_u4_risc2_idx
++#define des_encrypt3 des_encrypt3_u4_risc2_idx
++#define des_decrypt3 des_decrypt3_u4_risc2_idx
++#undef HEADER_DES_LOCL_H
++#include "des_enc.c"
++
++#define DES_UNROLL
++#define DES_RISC1
++#undef DES_RISC2
++#undef DES_PTR
++#undef D_ENCRYPT
++#undef des_encrypt
++#undef des_encrypt2
++#undef des_encrypt3
++#undef des_decrypt3
++#define des_encrypt des_encrypt_u16_risc1_idx
++#define des_encrypt2 des_encrypt2_u16_risc1_idx
++#define des_encrypt3 des_encrypt3_u16_risc1_idx
++#define des_decrypt3 des_decrypt3_u16_risc1_idx
++#undef HEADER_DES_LOCL_H
++#include "des_enc.c"
++
++#define DES_UNROLL
++#undef DES_RISC1
++#define DES_RISC2
++#undef DES_PTR
++#undef D_ENCRYPT
++#undef des_encrypt
++#undef des_encrypt2
++#undef des_encrypt3
++#undef des_decrypt3
++#define des_encrypt des_encrypt_u16_risc2_idx
++#define des_encrypt2 des_encrypt2_u16_risc2_idx
++#define des_encrypt3 des_encrypt3_u16_risc2_idx
++#define des_decrypt3 des_decrypt3_u16_risc2_idx
++#undef HEADER_DES_LOCL_H
++#include "des_enc.c"
++
++#endif
++
++#ifdef PART3
++
++#undef DES_UNROLL
++#undef DES_RISC1
++#undef DES_RISC2
++#define DES_PTR
++#undef D_ENCRYPT
++#undef des_encrypt
++#undef des_encrypt2
++#undef des_encrypt3
++#undef des_decrypt3
++#define des_encrypt des_encrypt_u4_cisc_ptr
++#define des_encrypt2 des_encrypt2_u4_cisc_ptr
++#define des_encrypt3 des_encrypt3_u4_cisc_ptr
++#define des_decrypt3 des_decrypt3_u4_cisc_ptr
++#undef HEADER_DES_LOCL_H
++#include "des_enc.c"
++
++#define DES_UNROLL
++#undef DES_RISC1
++#undef DES_RISC2
++#define DES_PTR
++#undef D_ENCRYPT
++#undef des_encrypt
++#undef des_encrypt2
++#undef des_encrypt3
++#undef des_decrypt3
++#define des_encrypt des_encrypt_u16_cisc_ptr
++#define des_encrypt2 des_encrypt2_u16_cisc_ptr
++#define des_encrypt3 des_encrypt3_u16_cisc_ptr
++#define des_decrypt3 des_decrypt3_u16_cisc_ptr
++#undef HEADER_DES_LOCL_H
++#include "des_enc.c"
++
++#undef DES_UNROLL
++#define DES_RISC1
++#undef DES_RISC2
++#define DES_PTR
++#undef D_ENCRYPT
++#undef des_encrypt
++#undef des_encrypt2
++#undef des_encrypt3
++#undef des_decrypt3
++#define des_encrypt des_encrypt_u4_risc1_ptr
++#define des_encrypt2 des_encrypt2_u4_risc1_ptr
++#define des_encrypt3 des_encrypt3_u4_risc1_ptr
++#define des_decrypt3 des_decrypt3_u4_risc1_ptr
++#undef HEADER_DES_LOCL_H
++#include "des_enc.c"
++
++#endif
++
++#ifdef PART4
++
++#undef DES_UNROLL
++#undef DES_RISC1
++#define DES_RISC2
++#define DES_PTR
++#undef D_ENCRYPT
++#undef des_encrypt
++#undef des_encrypt2
++#undef des_encrypt3
++#undef des_decrypt3
++#define des_encrypt des_encrypt_u4_risc2_ptr
++#define des_encrypt2 des_encrypt2_u4_risc2_ptr
++#define des_encrypt3 des_encrypt3_u4_risc2_ptr
++#define des_decrypt3 des_decrypt3_u4_risc2_ptr
++#undef HEADER_DES_LOCL_H
++#include "des_enc.c"
++
++#define DES_UNROLL
++#define DES_RISC1
++#undef DES_RISC2
++#define DES_PTR
++#undef D_ENCRYPT
++#undef des_encrypt
++#undef des_encrypt2
++#undef des_encrypt3
++#undef des_decrypt3
++#define des_encrypt des_encrypt_u16_risc1_ptr
++#define des_encrypt2 des_encrypt2_u16_risc1_ptr
++#define des_encrypt3 des_encrypt3_u16_risc1_ptr
++#define des_decrypt3 des_decrypt3_u16_risc1_ptr
++#undef HEADER_DES_LOCL_H
++#include "des_enc.c"
++
++#define DES_UNROLL
++#undef DES_RISC1
++#define DES_RISC2
++#define DES_PTR
++#undef D_ENCRYPT
++#undef des_encrypt
++#undef des_encrypt2
++#undef des_encrypt3
++#undef des_decrypt3
++#define des_encrypt des_encrypt_u16_risc2_ptr
++#define des_encrypt2 des_encrypt2_u16_risc2_ptr
++#define des_encrypt3 des_encrypt3_u16_risc2_ptr
++#define des_decrypt3 des_decrypt3_u16_risc2_ptr
++#undef HEADER_DES_LOCL_H
++#include "des_enc.c"
++
++#endif
++
++/* The following if from times(3) man page. It may need to be changed */
++#ifndef HZ
++# ifndef CLK_TCK
++# ifndef _BSD_CLK_TCK_ /* FreeBSD fix */
++# ifndef VMS
++# define HZ 100.0
++# else /* VMS */
++# define HZ 100.0
++# endif
++# else /* _BSD_CLK_TCK_ */
++# define HZ ((double)_BSD_CLK_TCK_)
++# endif
++# else /* CLK_TCK */
++# define HZ ((double)CLK_TCK)
++# endif
++#endif
++
++#define BUFSIZE ((long)1024)
++long run=0;
++
++#ifndef NOPROTO
++double Time_F(int s);
++#else
++double Time_F();
++#endif
++
++#ifdef SIGALRM
++#if defined(__STDC__) || defined(sgi)
++#define SIGRETTYPE void
++#else
++#define SIGRETTYPE int
++#endif
++
++#ifndef NOPROTO
++SIGRETTYPE sig_done(int sig);
++#else
++SIGRETTYPE sig_done();
++#endif
++
++SIGRETTYPE sig_done(sig)
++int sig;
++ {
++ signal(SIGALRM,sig_done);
++ run=0;
++#ifdef LINT
++ sig=sig;
++#endif
++ }
++#endif
++
++#define START 0
++#define STOP 1
++
++double Time_F(s)
++int s;
++ {
++ double ret;
++#ifdef TIMES
++ static struct tms tstart,tend;
++
++ if (s == START)
++ {
++ times(&tstart);
++ return(0);
++ }
++ else
++ {
++ times(&tend);
++ ret=((double)(tend.tms_utime-tstart.tms_utime))/HZ;
++ return((ret == 0.0)?1e-6:ret);
++ }
++#else /* !times() */
++ static struct timeb tstart,tend;
++ long i;
++
++ if (s == START)
++ {
++ ftime(&tstart);
++ return(0);
++ }
++ else
++ {
++ ftime(&tend);
++ i=(long)tend.millitm-(long)tstart.millitm;
++ ret=((double)(tend.time-tstart.time))+((double)i)/1000.0;
++ return((ret == 0.0)?1e-6:ret);
++ }
++#endif
++ }
++
++#ifdef SIGALRM
++#define print_name(name) fprintf(stderr,"Doing %s's for 10 seconds\n",name); alarm(10);
++#else
++#define print_name(name) fprintf(stderr,"Doing %s %ld times\n",name,cb);
++#endif
++
++#define time_it(func,name,index) \
++ print_name(name); \
++ Time_F(START); \
++ for (count=0,run=1; COND(cb); count++) \
++ { \
++ unsigned long d[2]; \
++ func(d,&(sch[0]),DES_ENCRYPT); \
++ } \
++ tm[index]=Time_F(STOP); \
++ fprintf(stderr,"%ld %s's in %.2f second\n",count,name,tm[index]); \
++ tm[index]=((double)COUNT(cb))/tm[index];
++
++#define print_it(name,index) \
++ fprintf(stderr,"%s bytes per sec = %12.2f (%5.1fuS)\n",name, \
++ tm[index]*8,1.0e6/tm[index]);
++
++int main(argc,argv)
++int argc;
++char **argv;
++ {
++ long count;
++ static unsigned char buf[BUFSIZE];
++ static des_cblock key ={0x12,0x34,0x56,0x78,0x9a,0xbc,0xde,0xf0};
++ static des_cblock key2={0x34,0x56,0x78,0x9a,0xbc,0xde,0xf0,0x12};
++ static des_cblock key3={0x56,0x78,0x9a,0xbc,0xde,0xf0,0x12,0x34};
++ des_key_schedule sch,sch2,sch3;
++ double d,tm[16],max=0;
++ int rank[16];
++ char *str[16];
++ int max_idx=0,i,num=0,j;
++#ifndef SIGALARM
++ long ca,cb,cc,cd,ce;
++#endif
++
++ for (i=0; i<12; i++)
++ {
++ tm[i]=0.0;
++ rank[i]=0;
++ }
++
++#ifndef TIMES
++ fprintf(stderr,"To get the most acurate results, try to run this\n");
++ fprintf(stderr,"program when this computer is idle.\n");
++#endif
++
++ des_set_key((C_Block *)key,sch);
++ des_set_key((C_Block *)key2,sch2);
++ des_set_key((C_Block *)key3,sch3);
++
++#ifndef SIGALRM
++ fprintf(stderr,"First we calculate the approximate speed ...\n");
++ des_set_key((C_Block *)key,sch);
++ count=10;
++ do {
++ long i;
++ unsigned long data[2];
++
++ count*=2;
++ Time_F(START);
++ for (i=count; i; i--)
++ des_encrypt(data,&(sch[0]),DES_ENCRYPT);
++ d=Time_F(STOP);
++ } while (d < 3.0);
++ ca=count;
++ cb=count*3;
++ cc=count*3*8/BUFSIZE+1;
++ cd=count*8/BUFSIZE+1;
++
++ ce=count/20+1;
++#define COND(d) (count != (d))
++#define COUNT(d) (d)
++#else
++#define COND(c) (run)
++#define COUNT(d) (count)
++ signal(SIGALRM,sig_done);
++ alarm(10);
++#endif
++
++#ifdef PART1
++ time_it(des_encrypt_u4_cisc_idx, "des_encrypt_u4_cisc_idx ", 0);
++ time_it(des_encrypt_u16_cisc_idx, "des_encrypt_u16_cisc_idx ", 1);
++ time_it(des_encrypt_u4_risc1_idx, "des_encrypt_u4_risc1_idx ", 2);
++ num+=3;
++#endif
++#ifdef PART2
++ time_it(des_encrypt_u16_risc1_idx,"des_encrypt_u16_risc1_idx", 3);
++ time_it(des_encrypt_u4_risc2_idx, "des_encrypt_u4_risc2_idx ", 4);
++ time_it(des_encrypt_u16_risc2_idx,"des_encrypt_u16_risc2_idx", 5);
++ num+=3;
++#endif
++#ifdef PART3
++ time_it(des_encrypt_u4_cisc_ptr, "des_encrypt_u4_cisc_ptr ", 6);
++ time_it(des_encrypt_u16_cisc_ptr, "des_encrypt_u16_cisc_ptr ", 7);
++ time_it(des_encrypt_u4_risc1_ptr, "des_encrypt_u4_risc1_ptr ", 8);
++ num+=3;
++#endif
++#ifdef PART4
++ time_it(des_encrypt_u16_risc1_ptr,"des_encrypt_u16_risc1_ptr", 9);
++ time_it(des_encrypt_u4_risc2_ptr, "des_encrypt_u4_risc2_ptr ",10);
++ time_it(des_encrypt_u16_risc2_ptr,"des_encrypt_u16_risc2_ptr",11);
++ num+=3;
++#endif
++
++#ifdef PART1
++ str[0]=" 4 c i";
++ print_it("des_encrypt_u4_cisc_idx ",0);
++ max=tm[0];
++ max_idx=0;
++ str[1]="16 c i";
++ print_it("des_encrypt_u16_cisc_idx ",1);
++ if (max < tm[1]) { max=tm[1]; max_idx=1; }
++ str[2]=" 4 r1 i";
++ print_it("des_encrypt_u4_risc1_idx ",2);
++ if (max < tm[2]) { max=tm[2]; max_idx=2; }
++#endif
++#ifdef PART2
++ str[3]="16 r1 i";
++ print_it("des_encrypt_u16_risc1_idx",3);
++ if (max < tm[3]) { max=tm[3]; max_idx=3; }
++ str[4]=" 4 r2 i";
++ print_it("des_encrypt_u4_risc2_idx ",4);
++ if (max < tm[4]) { max=tm[4]; max_idx=4; }
++ str[5]="16 r2 i";
++ print_it("des_encrypt_u16_risc2_idx",5);
++ if (max < tm[5]) { max=tm[5]; max_idx=5; }
++#endif
++#ifdef PART3
++ str[6]=" 4 c p";
++ print_it("des_encrypt_u4_cisc_ptr ",6);
++ if (max < tm[6]) { max=tm[6]; max_idx=6; }
++ str[7]="16 c p";
++ print_it("des_encrypt_u16_cisc_ptr ",7);
++ if (max < tm[7]) { max=tm[7]; max_idx=7; }
++ str[8]=" 4 r1 p";
++ print_it("des_encrypt_u4_risc1_ptr ",8);
++ if (max < tm[8]) { max=tm[8]; max_idx=8; }
++#endif
++#ifdef PART4
++ str[9]="16 r1 p";
++ print_it("des_encrypt_u16_risc1_ptr",9);
++ if (max < tm[9]) { max=tm[9]; max_idx=9; }
++ str[10]=" 4 r2 p";
++ print_it("des_encrypt_u4_risc2_ptr ",10);
++ if (max < tm[10]) { max=tm[10]; max_idx=10; }
++ str[11]="16 r2 p";
++ print_it("des_encrypt_u16_risc2_ptr",11);
++ if (max < tm[11]) { max=tm[11]; max_idx=11; }
++#endif
++ printf("options des ecb/s\n");
++ printf("%s %12.2f 100.0%%\n",str[max_idx],tm[max_idx]);
++ d=tm[max_idx];
++ tm[max_idx]= -2.0;
++ max= -1.0;
++ for (;;)
++ {
++ for (i=0; i<12; i++)
++ {
++ if (max < tm[i]) { max=tm[i]; j=i; }
++ }
++ if (max < 0.0) break;
++ printf("%s %12.2f %4.1f%%\n",str[j],tm[j],tm[j]/d*100.0);
++ tm[j]= -2.0;
++ max= -1.0;
++ }
++
++ switch (max_idx)
++ {
++ case 0:
++ printf("-DDES_DEFAULT_OPTIONS\n");
++ break;
++ case 1:
++ printf("-DDES_UNROLL\n");
++ break;
++ case 2:
++ printf("-DDES_RISC1\n");
++ break;
++ case 3:
++ printf("-DDES_UNROLL -DDES_RISC1\n");
++ break;
++ case 4:
++ printf("-DDES_RISC2\n");
++ break;
++ case 5:
++ printf("-DDES_UNROLL -DDES_RISC2\n");
++ break;
++ case 6:
++ printf("-DDES_PTR\n");
++ break;
++ case 7:
++ printf("-DDES_UNROLL -DDES_PTR\n");
++ break;
++ case 8:
++ printf("-DDES_RISC1 -DDES_PTR\n");
++ break;
++ case 9:
++ printf("-DDES_UNROLL -DDES_RISC1 -DDES_PTR\n");
++ break;
++ case 10:
++ printf("-DDES_RISC2 -DDES_PTR\n");
++ break;
++ case 11:
++ printf("-DDES_UNROLL -DDES_RISC2 -DDES_PTR\n");
++ break;
++ }
++ exit(0);
++#if defined(LINT) || defined(MSDOS)
++ return(0);
++#endif
++ }
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/crypto/ciphers/des/des_ver.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,60 @@
++/* crypto/des/des_ver.h */
++/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com)
++ * All rights reserved.
++ *
++ * This package is an SSL implementation written
++ * by Eric Young (eay@cryptsoft.com).
++ * The implementation was written so as to conform with Netscapes SSL.
++ *
++ * This library is free for commercial and non-commercial use as long as
++ * the following conditions are aheared to. The following conditions
++ * apply to all code found in this distribution, be it the RC4, RSA,
++ * lhash, DES, etc., code; not just the SSL code. The SSL documentation
++ * included with this distribution is covered by the same copyright terms
++ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
++ *
++ * Copyright remains Eric Young's, and as such any Copyright notices in
++ * the code are not to be removed.
++ * If this package is used in a product, Eric Young should be given attribution
++ * as the author of the parts of the library used.
++ * This can be in the form of a textual message at program startup or
++ * in documentation (online or textual) provided with the package.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ * 1. Redistributions of source code must retain the copyright
++ * notice, this list of conditions and the following disclaimer.
++ * 2. Redistributions in binary form must reproduce the above copyright
++ * notice, this list of conditions and the following disclaimer in the
++ * documentation and/or other materials provided with the distribution.
++ * 3. All advertising materials mentioning features or use of this software
++ * must display the following acknowledgement:
++ * "This product includes cryptographic software written by
++ * Eric Young (eay@cryptsoft.com)"
++ * The word 'cryptographic' can be left out if the rouines from the library
++ * being used are not cryptographic related :-).
++ * 4. If you include any Windows specific code (or a derivative thereof) from
++ * the apps directory (application code) you must include an acknowledgement:
++ * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
++ *
++ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ *
++ * The licence and distribution terms for any publically available version or
++ * derivative of this code cannot be changed. i.e. this code cannot simply be
++ * copied and put under another distribution licence
++ * [including the GNU Public Licence.]
++ */
++
++extern char *DES_version; /* SSLeay version string */
++extern char *libdes_version; /* old libdes version string */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/crypto/ciphers/des/destest.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,871 @@
++/* crypto/des/destest.c */
++/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com)
++ * All rights reserved.
++ *
++ * This package is an SSL implementation written
++ * by Eric Young (eay@cryptsoft.com).
++ * The implementation was written so as to conform with Netscapes SSL.
++ *
++ * This library is free for commercial and non-commercial use as long as
++ * the following conditions are aheared to. The following conditions
++ * apply to all code found in this distribution, be it the RC4, RSA,
++ * lhash, DES, etc., code; not just the SSL code. The SSL documentation
++ * included with this distribution is covered by the same copyright terms
++ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
++ *
++ * Copyright remains Eric Young's, and as such any Copyright notices in
++ * the code are not to be removed.
++ * If this package is used in a product, Eric Young should be given attribution
++ * as the author of the parts of the library used.
++ * This can be in the form of a textual message at program startup or
++ * in documentation (online or textual) provided with the package.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ * 1. Redistributions of source code must retain the copyright
++ * notice, this list of conditions and the following disclaimer.
++ * 2. Redistributions in binary form must reproduce the above copyright
++ * notice, this list of conditions and the following disclaimer in the
++ * documentation and/or other materials provided with the distribution.
++ * 3. All advertising materials mentioning features or use of this software
++ * must display the following acknowledgement:
++ * "This product includes cryptographic software written by
++ * Eric Young (eay@cryptsoft.com)"
++ * The word 'cryptographic' can be left out if the rouines from the library
++ * being used are not cryptographic related :-).
++ * 4. If you include any Windows specific code (or a derivative thereof) from
++ * the apps directory (application code) you must include an acknowledgement:
++ * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
++ *
++ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ *
++ * The licence and distribution terms for any publically available version or
++ * derivative of this code cannot be changed. i.e. this code cannot simply be
++ * copied and put under another distribution licence
++ * [including the GNU Public Licence.]
++ */
++
++#if defined(WIN32) || defined(WIN16) || defined(WINDOWS)
++#ifndef MSDOS
++#define MSDOS
++#endif
++#endif
++
++#include <stdio.h>
++#include <stdlib.h>
++#ifndef MSDOS
++#include <unistd.h>
++#else
++#include <io.h>
++#endif
++#include <string.h>
++#include "des_locl.h"
++
++/* tisk tisk - the test keys don't all have odd parity :-( */
++/* test data */
++#define NUM_TESTS 34
++static unsigned char key_data[NUM_TESTS][8]={
++ {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
++ {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF},
++ {0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
++ {0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11},
++ {0x01,0x23,0x45,0x67,0x89,0xAB,0xCD,0xEF},
++ {0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11},
++ {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
++ {0xFE,0xDC,0xBA,0x98,0x76,0x54,0x32,0x10},
++ {0x7C,0xA1,0x10,0x45,0x4A,0x1A,0x6E,0x57},
++ {0x01,0x31,0xD9,0x61,0x9D,0xC1,0x37,0x6E},
++ {0x07,0xA1,0x13,0x3E,0x4A,0x0B,0x26,0x86},
++ {0x38,0x49,0x67,0x4C,0x26,0x02,0x31,0x9E},
++ {0x04,0xB9,0x15,0xBA,0x43,0xFE,0xB5,0xB6},
++ {0x01,0x13,0xB9,0x70,0xFD,0x34,0xF2,0xCE},
++ {0x01,0x70,0xF1,0x75,0x46,0x8F,0xB5,0xE6},
++ {0x43,0x29,0x7F,0xAD,0x38,0xE3,0x73,0xFE},
++ {0x07,0xA7,0x13,0x70,0x45,0xDA,0x2A,0x16},
++ {0x04,0x68,0x91,0x04,0xC2,0xFD,0x3B,0x2F},
++ {0x37,0xD0,0x6B,0xB5,0x16,0xCB,0x75,0x46},
++ {0x1F,0x08,0x26,0x0D,0x1A,0xC2,0x46,0x5E},
++ {0x58,0x40,0x23,0x64,0x1A,0xBA,0x61,0x76},
++ {0x02,0x58,0x16,0x16,0x46,0x29,0xB0,0x07},
++ {0x49,0x79,0x3E,0xBC,0x79,0xB3,0x25,0x8F},
++ {0x4F,0xB0,0x5E,0x15,0x15,0xAB,0x73,0xA7},
++ {0x49,0xE9,0x5D,0x6D,0x4C,0xA2,0x29,0xBF},
++ {0x01,0x83,0x10,0xDC,0x40,0x9B,0x26,0xD6},
++ {0x1C,0x58,0x7F,0x1C,0x13,0x92,0x4F,0xEF},
++ {0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01},
++ {0x1F,0x1F,0x1F,0x1F,0x0E,0x0E,0x0E,0x0E},
++ {0xE0,0xFE,0xE0,0xFE,0xF1,0xFE,0xF1,0xFE},
++ {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
++ {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF},
++ {0x01,0x23,0x45,0x67,0x89,0xAB,0xCD,0xEF},
++ {0xFE,0xDC,0xBA,0x98,0x76,0x54,0x32,0x10}};
++
++static unsigned char plain_data[NUM_TESTS][8]={
++ {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
++ {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF},
++ {0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x01},
++ {0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11},
++ {0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11},
++ {0x01,0x23,0x45,0x67,0x89,0xAB,0xCD,0xEF},
++ {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
++ {0x01,0x23,0x45,0x67,0x89,0xAB,0xCD,0xEF},
++ {0x01,0xA1,0xD6,0xD0,0x39,0x77,0x67,0x42},
++ {0x5C,0xD5,0x4C,0xA8,0x3D,0xEF,0x57,0xDA},
++ {0x02,0x48,0xD4,0x38,0x06,0xF6,0x71,0x72},
++ {0x51,0x45,0x4B,0x58,0x2D,0xDF,0x44,0x0A},
++ {0x42,0xFD,0x44,0x30,0x59,0x57,0x7F,0xA2},
++ {0x05,0x9B,0x5E,0x08,0x51,0xCF,0x14,0x3A},
++ {0x07,0x56,0xD8,0xE0,0x77,0x47,0x61,0xD2},
++ {0x76,0x25,0x14,0xB8,0x29,0xBF,0x48,0x6A},
++ {0x3B,0xDD,0x11,0x90,0x49,0x37,0x28,0x02},
++ {0x26,0x95,0x5F,0x68,0x35,0xAF,0x60,0x9A},
++ {0x16,0x4D,0x5E,0x40,0x4F,0x27,0x52,0x32},
++ {0x6B,0x05,0x6E,0x18,0x75,0x9F,0x5C,0xCA},
++ {0x00,0x4B,0xD6,0xEF,0x09,0x17,0x60,0x62},
++ {0x48,0x0D,0x39,0x00,0x6E,0xE7,0x62,0xF2},
++ {0x43,0x75,0x40,0xC8,0x69,0x8F,0x3C,0xFA},
++ {0x07,0x2D,0x43,0xA0,0x77,0x07,0x52,0x92},
++ {0x02,0xFE,0x55,0x77,0x81,0x17,0xF1,0x2A},
++ {0x1D,0x9D,0x5C,0x50,0x18,0xF7,0x28,0xC2},
++ {0x30,0x55,0x32,0x28,0x6D,0x6F,0x29,0x5A},
++ {0x01,0x23,0x45,0x67,0x89,0xAB,0xCD,0xEF},
++ {0x01,0x23,0x45,0x67,0x89,0xAB,0xCD,0xEF},
++ {0x01,0x23,0x45,0x67,0x89,0xAB,0xCD,0xEF},
++ {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF},
++ {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
++ {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
++ {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}};
++
++static unsigned char cipher_data[NUM_TESTS][8]={
++ {0x8C,0xA6,0x4D,0xE9,0xC1,0xB1,0x23,0xA7},
++ {0x73,0x59,0xB2,0x16,0x3E,0x4E,0xDC,0x58},
++ {0x95,0x8E,0x6E,0x62,0x7A,0x05,0x55,0x7B},
++ {0xF4,0x03,0x79,0xAB,0x9E,0x0E,0xC5,0x33},
++ {0x17,0x66,0x8D,0xFC,0x72,0x92,0x53,0x2D},
++ {0x8A,0x5A,0xE1,0xF8,0x1A,0xB8,0xF2,0xDD},
++ {0x8C,0xA6,0x4D,0xE9,0xC1,0xB1,0x23,0xA7},
++ {0xED,0x39,0xD9,0x50,0xFA,0x74,0xBC,0xC4},
++ {0x69,0x0F,0x5B,0x0D,0x9A,0x26,0x93,0x9B},
++ {0x7A,0x38,0x9D,0x10,0x35,0x4B,0xD2,0x71},
++ {0x86,0x8E,0xBB,0x51,0xCA,0xB4,0x59,0x9A},
++ {0x71,0x78,0x87,0x6E,0x01,0xF1,0x9B,0x2A},
++ {0xAF,0x37,0xFB,0x42,0x1F,0x8C,0x40,0x95},
++ {0x86,0xA5,0x60,0xF1,0x0E,0xC6,0xD8,0x5B},
++ {0x0C,0xD3,0xDA,0x02,0x00,0x21,0xDC,0x09},
++ {0xEA,0x67,0x6B,0x2C,0xB7,0xDB,0x2B,0x7A},
++ {0xDF,0xD6,0x4A,0x81,0x5C,0xAF,0x1A,0x0F},
++ {0x5C,0x51,0x3C,0x9C,0x48,0x86,0xC0,0x88},
++ {0x0A,0x2A,0xEE,0xAE,0x3F,0xF4,0xAB,0x77},
++ {0xEF,0x1B,0xF0,0x3E,0x5D,0xFA,0x57,0x5A},
++ {0x88,0xBF,0x0D,0xB6,0xD7,0x0D,0xEE,0x56},
++ {0xA1,0xF9,0x91,0x55,0x41,0x02,0x0B,0x56},
++ {0x6F,0xBF,0x1C,0xAF,0xCF,0xFD,0x05,0x56},
++ {0x2F,0x22,0xE4,0x9B,0xAB,0x7C,0xA1,0xAC},
++ {0x5A,0x6B,0x61,0x2C,0xC2,0x6C,0xCE,0x4A},
++ {0x5F,0x4C,0x03,0x8E,0xD1,0x2B,0x2E,0x41},
++ {0x63,0xFA,0xC0,0xD0,0x34,0xD9,0xF7,0x93},
++ {0x61,0x7B,0x3A,0x0C,0xE8,0xF0,0x71,0x00},
++ {0xDB,0x95,0x86,0x05,0xF8,0xC8,0xC6,0x06},
++ {0xED,0xBF,0xD1,0xC6,0x6C,0x29,0xCC,0xC7},
++ {0x35,0x55,0x50,0xB2,0x15,0x0E,0x24,0x51},
++ {0xCA,0xAA,0xAF,0x4D,0xEA,0xF1,0xDB,0xAE},
++ {0xD5,0xD4,0x4F,0xF7,0x20,0x68,0x3D,0x0D},
++ {0x2A,0x2B,0xB0,0x08,0xDF,0x97,0xC2,0xF2}};
++
++static unsigned char cipher_ecb2[NUM_TESTS-1][8]={
++ {0x92,0x95,0xB5,0x9B,0xB3,0x84,0x73,0x6E},
++ {0x19,0x9E,0x9D,0x6D,0xF3,0x9A,0xA8,0x16},
++ {0x2A,0x4B,0x4D,0x24,0x52,0x43,0x84,0x27},
++ {0x35,0x84,0x3C,0x01,0x9D,0x18,0xC5,0xB6},
++ {0x4A,0x5B,0x2F,0x42,0xAA,0x77,0x19,0x25},
++ {0xA0,0x6B,0xA9,0xB8,0xCA,0x5B,0x17,0x8A},
++ {0xAB,0x9D,0xB7,0xFB,0xED,0x95,0xF2,0x74},
++ {0x3D,0x25,0x6C,0x23,0xA7,0x25,0x2F,0xD6},
++ {0xB7,0x6F,0xAB,0x4F,0xBD,0xBD,0xB7,0x67},
++ {0x8F,0x68,0x27,0xD6,0x9C,0xF4,0x1A,0x10},
++ {0x82,0x57,0xA1,0xD6,0x50,0x5E,0x81,0x85},
++ {0xA2,0x0F,0x0A,0xCD,0x80,0x89,0x7D,0xFA},
++ {0xCD,0x2A,0x53,0x3A,0xDB,0x0D,0x7E,0xF3},
++ {0xD2,0xC2,0xBE,0x27,0xE8,0x1B,0x68,0xE3},
++ {0xE9,0x24,0xCF,0x4F,0x89,0x3C,0x5B,0x0A},
++ {0xA7,0x18,0xC3,0x9F,0xFA,0x9F,0xD7,0x69},
++ {0x77,0x2C,0x79,0xB1,0xD2,0x31,0x7E,0xB1},
++ {0x49,0xAB,0x92,0x7F,0xD0,0x22,0x00,0xB7},
++ {0xCE,0x1C,0x6C,0x7D,0x85,0xE3,0x4A,0x6F},
++ {0xBE,0x91,0xD6,0xE1,0x27,0xB2,0xE9,0x87},
++ {0x70,0x28,0xAE,0x8F,0xD1,0xF5,0x74,0x1A},
++ {0xAA,0x37,0x80,0xBB,0xF3,0x22,0x1D,0xDE},
++ {0xA6,0xC4,0xD2,0x5E,0x28,0x93,0xAC,0xB3},
++ {0x22,0x07,0x81,0x5A,0xE4,0xB7,0x1A,0xAD},
++ {0xDC,0xCE,0x05,0xE7,0x07,0xBD,0xF5,0x84},
++ {0x26,0x1D,0x39,0x2C,0xB3,0xBA,0xA5,0x85},
++ {0xB4,0xF7,0x0F,0x72,0xFB,0x04,0xF0,0xDC},
++ {0x95,0xBA,0xA9,0x4E,0x87,0x36,0xF2,0x89},
++ {0xD4,0x07,0x3A,0xF1,0x5A,0x17,0x82,0x0E},
++ {0xEF,0x6F,0xAF,0xA7,0x66,0x1A,0x7E,0x89},
++ {0xC1,0x97,0xF5,0x58,0x74,0x8A,0x20,0xE7},
++ {0x43,0x34,0xCF,0xDA,0x22,0xC4,0x86,0xC8},
++ {0x08,0xD7,0xB4,0xFB,0x62,0x9D,0x08,0x85}};
++
++static unsigned char cbc_key [8]={0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef};
++static unsigned char cbc2_key[8]={0xf0,0xe1,0xd2,0xc3,0xb4,0xa5,0x96,0x87};
++static unsigned char cbc3_key[8]={0xfe,0xdc,0xba,0x98,0x76,0x54,0x32,0x10};
++static unsigned char cbc_iv [8]={0xfe,0xdc,0xba,0x98,0x76,0x54,0x32,0x10};
++static char cbc_data[40]="7654321 Now is the time for \0001";
++
++static unsigned char cbc_ok[32]={
++ 0xcc,0xd1,0x73,0xff,0xab,0x20,0x39,0xf4,
++ 0xac,0xd8,0xae,0xfd,0xdf,0xd8,0xa1,0xeb,
++ 0x46,0x8e,0x91,0x15,0x78,0x88,0xba,0x68,
++ 0x1d,0x26,0x93,0x97,0xf7,0xfe,0x62,0xb4};
++
++static unsigned char xcbc_ok[32]={
++ 0x86,0x74,0x81,0x0D,0x61,0xA4,0xA5,0x48,
++ 0xB9,0x93,0x03,0xE1,0xB8,0xBB,0xBD,0xBD,
++ 0x64,0x30,0x0B,0xB9,0x06,0x65,0x81,0x76,
++ 0x04,0x1D,0x77,0x62,0x17,0xCA,0x2B,0xD2,
++ };
++
++static unsigned char cbc3_ok[32]={
++ 0x3F,0xE3,0x01,0xC9,0x62,0xAC,0x01,0xD0,
++ 0x22,0x13,0x76,0x3C,0x1C,0xBD,0x4C,0xDC,
++ 0x79,0x96,0x57,0xC0,0x64,0xEC,0xF5,0xD4,
++ 0x1C,0x67,0x38,0x12,0xCF,0xDE,0x96,0x75};
++
++static unsigned char pcbc_ok[32]={
++ 0xcc,0xd1,0x73,0xff,0xab,0x20,0x39,0xf4,
++ 0x6d,0xec,0xb4,0x70,0xa0,0xe5,0x6b,0x15,
++ 0xae,0xa6,0xbf,0x61,0xed,0x7d,0x9c,0x9f,
++ 0xf7,0x17,0x46,0x3b,0x8a,0xb3,0xcc,0x88};
++
++static unsigned char cfb_key[8]={0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef};
++static unsigned char cfb_iv[8]={0x12,0x34,0x56,0x78,0x90,0xab,0xcd,0xef};
++static unsigned char cfb_buf1[40],cfb_buf2[40],cfb_tmp[8];
++static unsigned char plain[24]=
++ {
++ 0x4e,0x6f,0x77,0x20,0x69,0x73,
++ 0x20,0x74,0x68,0x65,0x20,0x74,
++ 0x69,0x6d,0x65,0x20,0x66,0x6f,
++ 0x72,0x20,0x61,0x6c,0x6c,0x20
++ };
++static unsigned char cfb_cipher8[24]= {
++ 0xf3,0x1f,0xda,0x07,0x01,0x14, 0x62,0xee,0x18,0x7f,0x43,0xd8,
++ 0x0a,0x7c,0xd9,0xb5,0xb0,0xd2, 0x90,0xda,0x6e,0x5b,0x9a,0x87 };
++static unsigned char cfb_cipher16[24]={
++ 0xF3,0x09,0x87,0x87,0x7F,0x57, 0xF7,0x3C,0x36,0xB6,0xDB,0x70,
++ 0xD8,0xD5,0x34,0x19,0xD3,0x86, 0xB2,0x23,0xB7,0xB2,0xAD,0x1B };
++static unsigned char cfb_cipher32[24]={
++ 0xF3,0x09,0x62,0x49,0xA4,0xDF, 0xA4,0x9F,0x33,0xDC,0x7B,0xAD,
++ 0x4C,0xC8,0x9F,0x64,0xE4,0x53, 0xE5,0xEC,0x67,0x20,0xDA,0xB6 };
++static unsigned char cfb_cipher48[24]={
++ 0xF3,0x09,0x62,0x49,0xC7,0xF4, 0x30,0xB5,0x15,0xEC,0xBB,0x85,
++ 0x97,0x5A,0x13,0x8C,0x68,0x60, 0xE2,0x38,0x34,0x3C,0xDC,0x1F };
++static unsigned char cfb_cipher64[24]={
++ 0xF3,0x09,0x62,0x49,0xC7,0xF4, 0x6E,0x51,0xA6,0x9E,0x83,0x9B,
++ 0x1A,0x92,0xF7,0x84,0x03,0x46, 0x71,0x33,0x89,0x8E,0xA6,0x22 };
++
++static unsigned char ofb_key[8]={0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef};
++static unsigned char ofb_iv[8]={0x12,0x34,0x56,0x78,0x90,0xab,0xcd,0xef};
++static unsigned char ofb_buf1[24],ofb_buf2[24],ofb_tmp[8];
++static unsigned char ofb_cipher[24]=
++ {
++ 0xf3,0x09,0x62,0x49,0xc7,0xf4,0x6e,0x51,
++ 0x35,0xf2,0x4a,0x24,0x2e,0xeb,0x3d,0x3f,
++ 0x3d,0x6d,0x5b,0xe3,0x25,0x5a,0xf8,0xc3
++ };
++
++DES_LONG cbc_cksum_ret=0xB462FEF7L;
++unsigned char cbc_cksum_data[8]={0x1D,0x26,0x93,0x97,0xf7,0xfe,0x62,0xb4};
++
++#ifndef NOPROTO
++static char *pt(unsigned char *p);
++static int cfb_test(int bits, unsigned char *cfb_cipher);
++static int cfb64_test(unsigned char *cfb_cipher);
++static int ede_cfb64_test(unsigned char *cfb_cipher);
++#else
++static char *pt();
++static int cfb_test();
++static int cfb64_test();
++static int ede_cfb64_test();
++#endif
++
++int main(argc,argv)
++int argc;
++char *argv[];
++ {
++ int i,j,err=0;
++ des_cblock in,out,outin,iv3;
++ des_key_schedule ks,ks2,ks3;
++ unsigned char cbc_in[40];
++ unsigned char cbc_out[40];
++ DES_LONG cs;
++ unsigned char qret[4][4],cret[8];
++ DES_LONG lqret[4];
++ int num;
++ char *str;
++
++ printf("Doing ecb\n");
++ for (i=0; i<NUM_TESTS; i++)
++ {
++ if ((j=des_key_sched((C_Block *)(key_data[i]),ks)) != 0)
++ {
++ printf("Key error %2d:%d\n",i+1,j);
++ err=1;
++ }
++ memcpy(in,plain_data[i],8);
++ memset(out,0,8);
++ memset(outin,0,8);
++ des_ecb_encrypt((C_Block *)in,(C_Block *)out,ks,DES_ENCRYPT);
++ des_ecb_encrypt((C_Block *)out,(C_Block *)outin,ks,DES_DECRYPT);
++
++ if (memcmp(out,cipher_data[i],8) != 0)
++ {
++ printf("Encryption error %2d\nk=%s p=%s o=%s act=%s\n",
++ i+1,pt(key_data[i]),pt(in),pt(cipher_data[i]),
++ pt(out));
++ err=1;
++ }
++ if (memcmp(in,outin,8) != 0)
++ {
++ printf("Decryption error %2d\nk=%s p=%s o=%s act=%s\n",
++ i+1,pt(key_data[i]),pt(out),pt(in),pt(outin));
++ err=1;
++ }
++ }
++
++#ifndef LIBDES_LIT
++ printf("Doing ede ecb\n");
++ for (i=0; i<(NUM_TESTS-1); i++)
++ {
++ if ((j=des_key_sched((C_Block *)(key_data[i]),ks)) != 0)
++ {
++ err=1;
++ printf("Key error %2d:%d\n",i+1,j);
++ }
++ if ((j=des_key_sched((C_Block *)(key_data[i+1]),ks2)) != 0)
++ {
++ printf("Key error %2d:%d\n",i+2,j);
++ err=1;
++ }
++ if ((j=des_key_sched((C_Block *)(key_data[i+2]),ks3)) != 0)
++ {
++ printf("Key error %2d:%d\n",i+3,j);
++ err=1;
++ }
++ memcpy(in,plain_data[i],8);
++ memset(out,0,8);
++ memset(outin,0,8);
++ des_ecb2_encrypt((C_Block *)in,(C_Block *)out,ks,ks2,
++ DES_ENCRYPT);
++ des_ecb2_encrypt((C_Block *)out,(C_Block *)outin,ks,ks2,
++ DES_DECRYPT);
++
++ if (memcmp(out,cipher_ecb2[i],8) != 0)
++ {
++ printf("Encryption error %2d\nk=%s p=%s o=%s act=%s\n",
++ i+1,pt(key_data[i]),pt(in),pt(cipher_ecb2[i]),
++ pt(out));
++ err=1;
++ }
++ if (memcmp(in,outin,8) != 0)
++ {
++ printf("Decryption error %2d\nk=%s p=%s o=%s act=%s\n",
++ i+1,pt(key_data[i]),pt(out),pt(in),pt(outin));
++ err=1;
++ }
++ }
++#endif
++
++ printf("Doing cbc\n");
++ if ((j=des_key_sched((C_Block *)cbc_key,ks)) != 0)
++ {
++ printf("Key error %d\n",j);
++ err=1;
++ }
++ memset(cbc_out,0,40);
++ memset(cbc_in,0,40);
++ memcpy(iv3,cbc_iv,sizeof(cbc_iv));
++ des_ncbc_encrypt((C_Block *)cbc_data,(C_Block *)cbc_out,
++ (long)strlen((char *)cbc_data)+1,ks,
++ (C_Block *)iv3,DES_ENCRYPT);
++ if (memcmp(cbc_out,cbc_ok,32) != 0)
++ printf("cbc_encrypt encrypt error\n");
++
++ memcpy(iv3,cbc_iv,sizeof(cbc_iv));
++ des_ncbc_encrypt((C_Block *)cbc_out,(C_Block *)cbc_in,
++ (long)strlen((char *)cbc_data)+1,ks,
++ (C_Block *)iv3,DES_DECRYPT);
++ if (memcmp(cbc_in,cbc_data,strlen((char *)cbc_data)) != 0)
++ {
++ printf("cbc_encrypt decrypt error\n");
++ err=1;
++ }
++
++#ifndef LIBDES_LIT
++ printf("Doing desx cbc\n");
++ if ((j=des_key_sched((C_Block *)cbc_key,ks)) != 0)
++ {
++ printf("Key error %d\n",j);
++ err=1;
++ }
++ memset(cbc_out,0,40);
++ memset(cbc_in,0,40);
++ memcpy(iv3,cbc_iv,sizeof(cbc_iv));
++ des_xcbc_encrypt((C_Block *)cbc_data,(C_Block *)cbc_out,
++ (long)strlen((char *)cbc_data)+1,ks,
++ (C_Block *)iv3,
++ (C_Block *)cbc2_key, (C_Block *)cbc3_key, DES_ENCRYPT);
++ if (memcmp(cbc_out,xcbc_ok,32) != 0)
++ {
++ printf("des_xcbc_encrypt encrypt error\n");
++ }
++ memcpy(iv3,cbc_iv,sizeof(cbc_iv));
++ des_xcbc_encrypt((C_Block *)cbc_out,(C_Block *)cbc_in,
++ (long)strlen((char *)cbc_data)+1,ks,
++ (C_Block *)iv3,
++ (C_Block *)cbc2_key, (C_Block *)cbc3_key, DES_DECRYPT);
++ if (memcmp(cbc_in,cbc_data,strlen((char *)cbc_data)+1) != 0)
++ {
++ printf("des_xcbc_encrypt decrypt error\n");
++ err=1;
++ }
++#endif
++
++ printf("Doing ede cbc\n");
++ if ((j=des_key_sched((C_Block *)cbc_key,ks)) != 0)
++ {
++ printf("Key error %d\n",j);
++ err=1;
++ }
++ if ((j=des_key_sched((C_Block *)cbc2_key,ks2)) != 0)
++ {
++ printf("Key error %d\n",j);
++ err=1;
++ }
++ if ((j=des_key_sched((C_Block *)cbc3_key,ks3)) != 0)
++ {
++ printf("Key error %d\n",j);
++ err=1;
++ }
++ memset(cbc_out,0,40);
++ memset(cbc_in,0,40);
++ i=strlen((char *)cbc_data)+1;
++ /* i=((i+7)/8)*8; */
++ memcpy(iv3,cbc_iv,sizeof(cbc_iv));
++
++ des_ede3_cbc_encrypt((C_Block *)cbc_data,(C_Block *)cbc_out,
++ 16L,ks,ks2,ks3,(C_Block *)iv3,DES_ENCRYPT);
++ des_ede3_cbc_encrypt((C_Block *)&(cbc_data[16]),
++ (C_Block *)&(cbc_out[16]),
++ (long)i-16,ks,ks2,ks3,(C_Block *)iv3,DES_ENCRYPT);
++ if (memcmp(cbc_out,cbc3_ok,
++ (unsigned int)(strlen((char *)cbc_data)+1+7)/8*8) != 0)
++ {
++ printf("des_ede3_cbc_encrypt encrypt error\n");
++ err=1;
++ }
++
++ memcpy(iv3,cbc_iv,sizeof(cbc_iv));
++ des_ede3_cbc_encrypt((C_Block *)cbc_out,(C_Block *)cbc_in,
++ (long)i,ks,ks2,ks3,(C_Block *)iv3,DES_DECRYPT);
++ if (memcmp(cbc_in,cbc_data,strlen(cbc_data)+1) != 0)
++ {
++ printf("des_ede3_cbc_encrypt decrypt error\n");
++ err=1;
++ }
++
++#ifndef LIBDES_LIT
++ printf("Doing pcbc\n");
++ if ((j=des_key_sched((C_Block *)cbc_key,ks)) != 0)
++ {
++ printf("Key error %d\n",j);
++ err=1;
++ }
++ memset(cbc_out,0,40);
++ memset(cbc_in,0,40);
++ des_pcbc_encrypt((C_Block *)cbc_data,(C_Block *)cbc_out,
++ (long)strlen(cbc_data)+1,ks,(C_Block *)cbc_iv,DES_ENCRYPT);
++ if (memcmp(cbc_out,pcbc_ok,32) != 0)
++ {
++ printf("pcbc_encrypt encrypt error\n");
++ err=1;
++ }
++ des_pcbc_encrypt((C_Block *)cbc_out,(C_Block *)cbc_in,
++ (long)strlen(cbc_data)+1,ks,(C_Block *)cbc_iv,DES_DECRYPT);
++ if (memcmp(cbc_in,cbc_data,strlen(cbc_data)+1) != 0)
++ {
++ printf("pcbc_encrypt decrypt error\n");
++ err=1;
++ }
++
++ printf("Doing ");
++ printf("cfb8 ");
++ err+=cfb_test(8,cfb_cipher8);
++ printf("cfb16 ");
++ err+=cfb_test(16,cfb_cipher16);
++ printf("cfb32 ");
++ err+=cfb_test(32,cfb_cipher32);
++ printf("cfb48 ");
++ err+=cfb_test(48,cfb_cipher48);
++ printf("cfb64 ");
++ err+=cfb_test(64,cfb_cipher64);
++
++ printf("cfb64() ");
++ err+=cfb64_test(cfb_cipher64);
++
++ memcpy(cfb_tmp,cfb_iv,sizeof(cfb_iv));
++ for (i=0; i<sizeof(plain); i++)
++ des_cfb_encrypt(&(plain[i]),&(cfb_buf1[i]),
++ 8,(long)1,ks,(C_Block *)cfb_tmp,DES_ENCRYPT);
++ if (memcmp(cfb_cipher8,cfb_buf1,sizeof(plain)) != 0)
++ {
++ printf("cfb_encrypt small encrypt error\n");
++ err=1;
++ }
++
++ memcpy(cfb_tmp,cfb_iv,sizeof(cfb_iv));
++ for (i=0; i<sizeof(plain); i++)
++ des_cfb_encrypt(&(cfb_buf1[i]),&(cfb_buf2[i]),
++ 8,(long)1,ks,(C_Block *)cfb_tmp,DES_DECRYPT);
++ if (memcmp(plain,cfb_buf2,sizeof(plain)) != 0)
++ {
++ printf("cfb_encrypt small decrypt error\n");
++ err=1;
++ }
++
++ printf("ede_cfb64() ");
++ err+=ede_cfb64_test(cfb_cipher64);
++
++ printf("done\n");
++
++ printf("Doing ofb\n");
++ des_key_sched((C_Block *)ofb_key,ks);
++ memcpy(ofb_tmp,ofb_iv,sizeof(ofb_iv));
++ des_ofb_encrypt(plain,ofb_buf1,64,(long)sizeof(plain)/8,ks,
++ (C_Block *)ofb_tmp);
++ if (memcmp(ofb_cipher,ofb_buf1,sizeof(ofb_buf1)) != 0)
++ {
++ printf("ofb_encrypt encrypt error\n");
++printf("%02X %02X %02X %02X %02X %02X %02X %02X\n",
++ofb_buf1[8+0], ofb_buf1[8+1], ofb_buf1[8+2], ofb_buf1[8+3],
++ofb_buf1[8+4], ofb_buf1[8+5], ofb_buf1[8+6], ofb_buf1[8+7]);
++printf("%02X %02X %02X %02X %02X %02X %02X %02X\n",
++ofb_buf1[8+0], ofb_cipher[8+1], ofb_cipher[8+2], ofb_cipher[8+3],
++ofb_buf1[8+4], ofb_cipher[8+5], ofb_cipher[8+6], ofb_cipher[8+7]);
++ err=1;
++ }
++ memcpy(ofb_tmp,ofb_iv,sizeof(ofb_iv));
++ des_ofb_encrypt(ofb_buf1,ofb_buf2,64,(long)sizeof(ofb_buf1)/8,ks,
++ (C_Block *)ofb_tmp);
++ if (memcmp(plain,ofb_buf2,sizeof(ofb_buf2)) != 0)
++ {
++ printf("ofb_encrypt decrypt error\n");
++printf("%02X %02X %02X %02X %02X %02X %02X %02X\n",
++ofb_buf2[8+0], ofb_buf2[8+1], ofb_buf2[8+2], ofb_buf2[8+3],
++ofb_buf2[8+4], ofb_buf2[8+5], ofb_buf2[8+6], ofb_buf2[8+7]);
++printf("%02X %02X %02X %02X %02X %02X %02X %02X\n",
++plain[8+0], plain[8+1], plain[8+2], plain[8+3],
++plain[8+4], plain[8+5], plain[8+6], plain[8+7]);
++ err=1;
++ }
++
++ printf("Doing ofb64\n");
++ des_key_sched((C_Block *)ofb_key,ks);
++ memcpy(ofb_tmp,ofb_iv,sizeof(ofb_iv));
++ memset(ofb_buf1,0,sizeof(ofb_buf1));
++ memset(ofb_buf2,0,sizeof(ofb_buf1));
++ num=0;
++ for (i=0; i<sizeof(plain); i++)
++ {
++ des_ofb64_encrypt(&(plain[i]),&(ofb_buf1[i]),1,ks,
++ (C_Block *)ofb_tmp,&num);
++ }
++ if (memcmp(ofb_cipher,ofb_buf1,sizeof(ofb_buf1)) != 0)
++ {
++ printf("ofb64_encrypt encrypt error\n");
++ err=1;
++ }
++ memcpy(ofb_tmp,ofb_iv,sizeof(ofb_iv));
++ num=0;
++ des_ofb64_encrypt(ofb_buf1,ofb_buf2,(long)sizeof(ofb_buf1),ks,
++ (C_Block *)ofb_tmp,&num);
++ if (memcmp(plain,ofb_buf2,sizeof(ofb_buf2)) != 0)
++ {
++ printf("ofb64_encrypt decrypt error\n");
++ err=1;
++ }
++
++ printf("Doing ede_ofb64\n");
++ des_key_sched((C_Block *)ofb_key,ks);
++ memcpy(ofb_tmp,ofb_iv,sizeof(ofb_iv));
++ memset(ofb_buf1,0,sizeof(ofb_buf1));
++ memset(ofb_buf2,0,sizeof(ofb_buf1));
++ num=0;
++ for (i=0; i<sizeof(plain); i++)
++ {
++ des_ede3_ofb64_encrypt(&(plain[i]),&(ofb_buf1[i]),1,ks,ks,ks,
++ (C_Block *)ofb_tmp,&num);
++ }
++ if (memcmp(ofb_cipher,ofb_buf1,sizeof(ofb_buf1)) != 0)
++ {
++ printf("ede_ofb64_encrypt encrypt error\n");
++ err=1;
++ }
++ memcpy(ofb_tmp,ofb_iv,sizeof(ofb_iv));
++ num=0;
++ des_ede3_ofb64_encrypt(ofb_buf1,ofb_buf2,(long)sizeof(ofb_buf1),ks,
++ ks,ks,(C_Block *)ofb_tmp,&num);
++ if (memcmp(plain,ofb_buf2,sizeof(ofb_buf2)) != 0)
++ {
++ printf("ede_ofb64_encrypt decrypt error\n");
++ err=1;
++ }
++
++ printf("Doing cbc_cksum\n");
++ des_key_sched((C_Block *)cbc_key,ks);
++ cs=des_cbc_cksum((C_Block *)cbc_data,(C_Block *)cret,
++ (long)strlen(cbc_data),ks,(C_Block *)cbc_iv);
++ if (cs != cbc_cksum_ret)
++ {
++ printf("bad return value (%08lX), should be %08lX\n",
++ (unsigned long)cs,(unsigned long)cbc_cksum_ret);
++ err=1;
++ }
++ if (memcmp(cret,cbc_cksum_data,8) != 0)
++ {
++ printf("bad cbc_cksum block returned\n");
++ err=1;
++ }
++
++ printf("Doing quad_cksum\n");
++ cs=quad_cksum((C_Block *)cbc_data,(C_Block *)qret,
++ (long)strlen(cbc_data),2,(C_Block *)cbc_iv);
++ for (i=0; i<4; i++)
++ {
++ lqret[i]=0;
++ memcpy(&(lqret[i]),&(qret[i][0]),4);
++ }
++ { /* Big-endian fix */
++ static DES_LONG l=1;
++ static unsigned char *c=(unsigned char *)&l;
++ DES_LONG ll;
++
++ if (!c[0])
++ {
++ ll=lqret[0]^lqret[3];
++ lqret[0]^=ll;
++ lqret[3]^=ll;
++ ll=lqret[1]^lqret[2];
++ lqret[1]^=ll;
++ lqret[2]^=ll;
++ }
++ }
++ if (cs != 0x70d7a63aL)
++ {
++ printf("quad_cksum error, ret %08lx should be 70d7a63a\n",
++ (unsigned long)cs);
++ err=1;
++ }
++ if (lqret[0] != 0x327eba8dL)
++ {
++ printf("quad_cksum error, out[0] %08lx is not %08lx\n",
++ (unsigned long)lqret[0],0x327eba8dL);
++ err=1;
++ }
++ if (lqret[1] != 0x201a49ccL)
++ {
++ printf("quad_cksum error, out[1] %08lx is not %08lx\n",
++ (unsigned long)lqret[1],0x201a49ccL);
++ err=1;
++ }
++ if (lqret[2] != 0x70d7a63aL)
++ {
++ printf("quad_cksum error, out[2] %08lx is not %08lx\n",
++ (unsigned long)lqret[2],0x70d7a63aL);
++ err=1;
++ }
++ if (lqret[3] != 0x501c2c26L)
++ {
++ printf("quad_cksum error, out[3] %08lx is not %08lx\n",
++ (unsigned long)lqret[3],0x501c2c26L);
++ err=1;
++ }
++#endif
++
++ printf("input word alignment test");
++ for (i=0; i<4; i++)
++ {
++ printf(" %d",i);
++ des_ncbc_encrypt((C_Block *)&(cbc_out[i]),(C_Block *)cbc_in,
++ (long)strlen(cbc_data)+1,ks,(C_Block *)cbc_iv,
++ DES_ENCRYPT);
++ }
++ printf("\noutput word alignment test");
++ for (i=0; i<4; i++)
++ {
++ printf(" %d",i);
++ des_ncbc_encrypt((C_Block *)cbc_out,(C_Block *)&(cbc_in[i]),
++ (long)strlen(cbc_data)+1,ks,(C_Block *)cbc_iv,
++ DES_ENCRYPT);
++ }
++ printf("\n");
++ printf("fast crypt test ");
++ str=crypt("testing","ef");
++ if (strcmp("efGnQx2725bI2",str) != 0)
++ {
++ printf("fast crypt error, %s should be efGnQx2725bI2\n",str);
++ err=1;
++ }
++ str=crypt("bca76;23","yA");
++ if (strcmp("yA1Rp/1hZXIJk",str) != 0)
++ {
++ printf("fast crypt error, %s should be yA1Rp/1hZXIJk\n",str);
++ err=1;
++ }
++ printf("\n");
++ exit(err);
++ return(0);
++ }
++
++static char *pt(p)
++unsigned char *p;
++ {
++ static char bufs[10][20];
++ static int bnum=0;
++ char *ret;
++ int i;
++ static char *f="0123456789ABCDEF";
++
++ ret= &(bufs[bnum++][0]);
++ bnum%=10;
++ for (i=0; i<8; i++)
++ {
++ ret[i*2]=f[(p[i]>>4)&0xf];
++ ret[i*2+1]=f[p[i]&0xf];
++ }
++ ret[16]='\0';
++ return(ret);
++ }
++
++#ifndef LIBDES_LIT
++
++static int cfb_test(bits, cfb_cipher)
++int bits;
++unsigned char *cfb_cipher;
++ {
++ des_key_schedule ks;
++ int i,err=0;
++
++ des_key_sched((C_Block *)cfb_key,ks);
++ memcpy(cfb_tmp,cfb_iv,sizeof(cfb_iv));
++ des_cfb_encrypt(plain,cfb_buf1,bits,(long)sizeof(plain),ks,
++ (C_Block *)cfb_tmp,DES_ENCRYPT);
++ if (memcmp(cfb_cipher,cfb_buf1,sizeof(plain)) != 0)
++ {
++ err=1;
++ printf("cfb_encrypt encrypt error\n");
++ for (i=0; i<24; i+=8)
++ printf("%s\n",pt(&(cfb_buf1[i])));
++ }
++ memcpy(cfb_tmp,cfb_iv,sizeof(cfb_iv));
++ des_cfb_encrypt(cfb_buf1,cfb_buf2,bits,(long)sizeof(plain),ks,
++ (C_Block *)cfb_tmp,DES_DECRYPT);
++ if (memcmp(plain,cfb_buf2,sizeof(plain)) != 0)
++ {
++ err=1;
++ printf("cfb_encrypt decrypt error\n");
++ for (i=0; i<24; i+=8)
++ printf("%s\n",pt(&(cfb_buf1[i])));
++ }
++ return(err);
++ }
++
++static int cfb64_test(cfb_cipher)
++unsigned char *cfb_cipher;
++ {
++ des_key_schedule ks;
++ int err=0,i,n;
++
++ des_key_sched((C_Block *)cfb_key,ks);
++ memcpy(cfb_tmp,cfb_iv,sizeof(cfb_iv));
++ n=0;
++ des_cfb64_encrypt(plain,cfb_buf1,(long)12,ks,
++ (C_Block *)cfb_tmp,&n,DES_ENCRYPT);
++ des_cfb64_encrypt(&(plain[12]),&(cfb_buf1[12]),
++ (long)sizeof(plain)-12,ks,
++ (C_Block *)cfb_tmp,&n,DES_ENCRYPT);
++ if (memcmp(cfb_cipher,cfb_buf1,sizeof(plain)) != 0)
++ {
++ err=1;
++ printf("cfb_encrypt encrypt error\n");
++ for (i=0; i<24; i+=8)
++ printf("%s\n",pt(&(cfb_buf1[i])));
++ }
++ memcpy(cfb_tmp,cfb_iv,sizeof(cfb_iv));
++ n=0;
++ des_cfb64_encrypt(cfb_buf1,cfb_buf2,(long)17,ks,
++ (C_Block *)cfb_tmp,&n,DES_DECRYPT);
++ des_cfb64_encrypt(&(cfb_buf1[17]),&(cfb_buf2[17]),
++ (long)sizeof(plain)-17,ks,
++ (C_Block *)cfb_tmp,&n,DES_DECRYPT);
++ if (memcmp(plain,cfb_buf2,sizeof(plain)) != 0)
++ {
++ err=1;
++ printf("cfb_encrypt decrypt error\n");
++ for (i=0; i<24; i+=8)
++ printf("%s\n",pt(&(cfb_buf2[i])));
++ }
++ return(err);
++ }
++
++static int ede_cfb64_test(cfb_cipher)
++unsigned char *cfb_cipher;
++ {
++ des_key_schedule ks;
++ int err=0,i,n;
++
++ des_key_sched((C_Block *)cfb_key,ks);
++ memcpy(cfb_tmp,cfb_iv,sizeof(cfb_iv));
++ n=0;
++ des_ede3_cfb64_encrypt(plain,cfb_buf1,(long)12,ks,ks,ks,
++ (C_Block *)cfb_tmp,&n,DES_ENCRYPT);
++ des_ede3_cfb64_encrypt(&(plain[12]),&(cfb_buf1[12]),
++ (long)sizeof(plain)-12,ks,ks,ks,
++ (C_Block *)cfb_tmp,&n,DES_ENCRYPT);
++ if (memcmp(cfb_cipher,cfb_buf1,sizeof(plain)) != 0)
++ {
++ err=1;
++ printf("ede_cfb_encrypt encrypt error\n");
++ for (i=0; i<24; i+=8)
++ printf("%s\n",pt(&(cfb_buf1[i])));
++ }
++ memcpy(cfb_tmp,cfb_iv,sizeof(cfb_iv));
++ n=0;
++ des_ede3_cfb64_encrypt(cfb_buf1,cfb_buf2,(long)17,ks,ks,ks,
++ (C_Block *)cfb_tmp,&n,DES_DECRYPT);
++ des_ede3_cfb64_encrypt(&(cfb_buf1[17]),&(cfb_buf2[17]),
++ (long)sizeof(plain)-17,ks,ks,ks,
++ (C_Block *)cfb_tmp,&n,DES_DECRYPT);
++ if (memcmp(plain,cfb_buf2,sizeof(plain)) != 0)
++ {
++ err=1;
++ printf("ede_cfb_encrypt decrypt error\n");
++ for (i=0; i<24; i+=8)
++ printf("%s\n",pt(&(cfb_buf2[i])));
++ }
++ return(err);
++ }
++
++#endif
++
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/crypto/ciphers/des/dx86unix.S Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,3160 @@
++/*
++ * This file was originally generated by Michael Richardson <mcr@freeswan.org>
++ * via the perl scripts found in the ASM subdir. It remains copyright of
++ * Eric Young, see the file COPYRIGHT.
++ *
++ * This was last done on October 9, 2002.
++ *
++ * While this file does not need to go through cpp, we pass it through
++ * CPP by naming it dx86unix.S instead of dx86unix.s because there is
++ * a bug in Rules.make for .s builds - specifically it references EXTRA_CFLAGS
++ * which may contain stuff that AS doesn't understand instead of
++ * referencing EXTRA_AFLAGS.
++ */
++
++ .file "dx86unix.S"
++ .version "01.01"
++.text
++ .align 16
++.globl des_encrypt
++ .type des_encrypt , @function
++des_encrypt:
++ pushl %esi
++ pushl %edi
++
++
++ movl 12(%esp), %esi
++ xorl %ecx, %ecx
++ pushl %ebx
++ pushl %ebp
++ movl (%esi), %eax
++ movl 28(%esp), %ebx
++ movl 4(%esi), %edi
++
++
++ roll $4, %eax
++ movl %eax, %esi
++ xorl %edi, %eax
++ andl $0xf0f0f0f0, %eax
++ xorl %eax, %esi
++ xorl %eax, %edi
++
++ roll $20, %edi
++ movl %edi, %eax
++ xorl %esi, %edi
++ andl $0xfff0000f, %edi
++ xorl %edi, %eax
++ xorl %edi, %esi
++
++ roll $14, %eax
++ movl %eax, %edi
++ xorl %esi, %eax
++ andl $0x33333333, %eax
++ xorl %eax, %edi
++ xorl %eax, %esi
++
++ roll $22, %esi
++ movl %esi, %eax
++ xorl %edi, %esi
++ andl $0x03fc03fc, %esi
++ xorl %esi, %eax
++ xorl %esi, %edi
++
++ roll $9, %eax
++ movl %eax, %esi
++ xorl %edi, %eax
++ andl $0xaaaaaaaa, %eax
++ xorl %eax, %esi
++ xorl %eax, %edi
++
++.byte 209
++.byte 199
++ movl 24(%esp), %ebp
++ cmpl $0, %ebx
++ je .L000start_decrypt
++
++
++ movl (%ebp), %eax
++ xorl %ebx, %ebx
++ movl 4(%ebp), %edx
++ xorl %esi, %eax
++ xorl %esi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %edi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %edi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %edi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %edi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %edi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %edi
++
++
++ movl 8(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 12(%ebp), %edx
++ xorl %edi, %eax
++ xorl %edi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %esi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %esi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %esi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %esi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %esi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %esi
++
++
++ movl 16(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 20(%ebp), %edx
++ xorl %esi, %eax
++ xorl %esi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %edi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %edi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %edi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %edi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %edi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %edi
++
++
++ movl 24(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 28(%ebp), %edx
++ xorl %edi, %eax
++ xorl %edi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %esi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %esi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %esi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %esi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %esi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %esi
++
++
++ movl 32(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 36(%ebp), %edx
++ xorl %esi, %eax
++ xorl %esi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %edi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %edi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %edi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %edi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %edi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %edi
++
++
++ movl 40(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 44(%ebp), %edx
++ xorl %edi, %eax
++ xorl %edi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %esi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %esi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %esi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %esi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %esi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %esi
++
++
++ movl 48(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 52(%ebp), %edx
++ xorl %esi, %eax
++ xorl %esi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %edi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %edi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %edi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %edi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %edi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %edi
++
++
++ movl 56(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 60(%ebp), %edx
++ xorl %edi, %eax
++ xorl %edi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %esi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %esi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %esi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %esi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %esi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %esi
++
++
++ movl 64(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 68(%ebp), %edx
++ xorl %esi, %eax
++ xorl %esi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %edi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %edi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %edi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %edi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %edi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %edi
++
++
++ movl 72(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 76(%ebp), %edx
++ xorl %edi, %eax
++ xorl %edi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %esi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %esi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %esi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %esi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %esi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %esi
++
++
++ movl 80(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 84(%ebp), %edx
++ xorl %esi, %eax
++ xorl %esi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %edi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %edi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %edi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %edi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %edi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %edi
++
++
++ movl 88(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 92(%ebp), %edx
++ xorl %edi, %eax
++ xorl %edi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %esi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %esi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %esi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %esi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %esi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %esi
++
++
++ movl 96(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 100(%ebp), %edx
++ xorl %esi, %eax
++ xorl %esi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %edi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %edi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %edi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %edi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %edi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %edi
++
++
++ movl 104(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 108(%ebp), %edx
++ xorl %edi, %eax
++ xorl %edi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %esi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %esi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %esi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %esi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %esi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %esi
++
++
++ movl 112(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 116(%ebp), %edx
++ xorl %esi, %eax
++ xorl %esi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %edi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %edi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %edi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %edi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %edi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %edi
++
++
++ movl 120(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 124(%ebp), %edx
++ xorl %edi, %eax
++ xorl %edi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %esi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %esi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %esi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %esi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %esi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %esi
++ jmp .L001end
++.L000start_decrypt:
++
++
++ movl 120(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 124(%ebp), %edx
++ xorl %esi, %eax
++ xorl %esi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %edi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %edi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %edi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %edi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %edi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %edi
++
++
++ movl 112(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 116(%ebp), %edx
++ xorl %edi, %eax
++ xorl %edi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %esi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %esi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %esi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %esi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %esi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %esi
++
++
++ movl 104(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 108(%ebp), %edx
++ xorl %esi, %eax
++ xorl %esi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %edi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %edi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %edi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %edi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %edi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %edi
++
++
++ movl 96(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 100(%ebp), %edx
++ xorl %edi, %eax
++ xorl %edi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %esi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %esi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %esi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %esi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %esi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %esi
++
++
++ movl 88(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 92(%ebp), %edx
++ xorl %esi, %eax
++ xorl %esi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %edi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %edi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %edi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %edi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %edi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %edi
++
++
++ movl 80(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 84(%ebp), %edx
++ xorl %edi, %eax
++ xorl %edi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %esi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %esi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %esi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %esi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %esi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %esi
++
++
++ movl 72(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 76(%ebp), %edx
++ xorl %esi, %eax
++ xorl %esi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %edi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %edi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %edi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %edi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %edi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %edi
++
++
++ movl 64(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 68(%ebp), %edx
++ xorl %edi, %eax
++ xorl %edi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %esi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %esi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %esi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %esi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %esi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %esi
++
++
++ movl 56(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 60(%ebp), %edx
++ xorl %esi, %eax
++ xorl %esi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %edi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %edi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %edi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %edi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %edi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %edi
++
++
++ movl 48(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 52(%ebp), %edx
++ xorl %edi, %eax
++ xorl %edi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %esi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %esi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %esi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %esi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %esi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %esi
++
++
++ movl 40(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 44(%ebp), %edx
++ xorl %esi, %eax
++ xorl %esi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %edi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %edi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %edi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %edi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %edi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %edi
++
++
++ movl 32(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 36(%ebp), %edx
++ xorl %edi, %eax
++ xorl %edi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %esi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %esi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %esi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %esi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %esi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %esi
++
++
++ movl 24(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 28(%ebp), %edx
++ xorl %esi, %eax
++ xorl %esi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %edi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %edi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %edi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %edi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %edi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %edi
++
++
++ movl 16(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 20(%ebp), %edx
++ xorl %edi, %eax
++ xorl %edi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %esi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %esi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %esi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %esi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %esi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %esi
++
++
++ movl 8(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 12(%ebp), %edx
++ xorl %esi, %eax
++ xorl %esi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %edi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %edi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %edi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %edi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %edi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %edi
++
++
++ movl (%ebp), %eax
++ xorl %ebx, %ebx
++ movl 4(%ebp), %edx
++ xorl %edi, %eax
++ xorl %edi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %esi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %esi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %esi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %esi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %esi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %esi
++.L001end:
++
++
++ movl 20(%esp), %edx
++.byte 209
++.byte 206
++ movl %edi, %eax
++ xorl %esi, %edi
++ andl $0xaaaaaaaa, %edi
++ xorl %edi, %eax
++ xorl %edi, %esi
++
++ roll $23, %eax
++ movl %eax, %edi
++ xorl %esi, %eax
++ andl $0x03fc03fc, %eax
++ xorl %eax, %edi
++ xorl %eax, %esi
++
++ roll $10, %edi
++ movl %edi, %eax
++ xorl %esi, %edi
++ andl $0x33333333, %edi
++ xorl %edi, %eax
++ xorl %edi, %esi
++
++ roll $18, %esi
++ movl %esi, %edi
++ xorl %eax, %esi
++ andl $0xfff0000f, %esi
++ xorl %esi, %edi
++ xorl %esi, %eax
++
++ roll $12, %edi
++ movl %edi, %esi
++ xorl %eax, %edi
++ andl $0xf0f0f0f0, %edi
++ xorl %edi, %esi
++ xorl %edi, %eax
++
++ rorl $4, %eax
++ movl %eax, (%edx)
++ movl %esi, 4(%edx)
++ popl %ebp
++ popl %ebx
++ popl %edi
++ popl %esi
++ ret
++.des_encrypt_end:
++ .size des_encrypt , .des_encrypt_end-des_encrypt
++.ident "desasm.pl"
++.text
++ .align 16
++.globl des_encrypt2
++ .type des_encrypt2 , @function
++des_encrypt2:
++ pushl %esi
++ pushl %edi
++
++
++ movl 12(%esp), %eax
++ xorl %ecx, %ecx
++ pushl %ebx
++ pushl %ebp
++ movl (%eax), %esi
++ movl 28(%esp), %ebx
++ roll $3, %esi
++ movl 4(%eax), %edi
++ roll $3, %edi
++ movl 24(%esp), %ebp
++ cmpl $0, %ebx
++ je .L002start_decrypt
++
++
++ movl (%ebp), %eax
++ xorl %ebx, %ebx
++ movl 4(%ebp), %edx
++ xorl %esi, %eax
++ xorl %esi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %edi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %edi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %edi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %edi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %edi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %edi
++
++
++ movl 8(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 12(%ebp), %edx
++ xorl %edi, %eax
++ xorl %edi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %esi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %esi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %esi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %esi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %esi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %esi
++
++
++ movl 16(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 20(%ebp), %edx
++ xorl %esi, %eax
++ xorl %esi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %edi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %edi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %edi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %edi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %edi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %edi
++
++
++ movl 24(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 28(%ebp), %edx
++ xorl %edi, %eax
++ xorl %edi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %esi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %esi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %esi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %esi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %esi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %esi
++
++
++ movl 32(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 36(%ebp), %edx
++ xorl %esi, %eax
++ xorl %esi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %edi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %edi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %edi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %edi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %edi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %edi
++
++
++ movl 40(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 44(%ebp), %edx
++ xorl %edi, %eax
++ xorl %edi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %esi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %esi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %esi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %esi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %esi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %esi
++
++
++ movl 48(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 52(%ebp), %edx
++ xorl %esi, %eax
++ xorl %esi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %edi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %edi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %edi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %edi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %edi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %edi
++
++
++ movl 56(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 60(%ebp), %edx
++ xorl %edi, %eax
++ xorl %edi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %esi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %esi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %esi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %esi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %esi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %esi
++
++
++ movl 64(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 68(%ebp), %edx
++ xorl %esi, %eax
++ xorl %esi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %edi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %edi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %edi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %edi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %edi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %edi
++
++
++ movl 72(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 76(%ebp), %edx
++ xorl %edi, %eax
++ xorl %edi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %esi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %esi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %esi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %esi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %esi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %esi
++
++
++ movl 80(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 84(%ebp), %edx
++ xorl %esi, %eax
++ xorl %esi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %edi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %edi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %edi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %edi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %edi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %edi
++
++
++ movl 88(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 92(%ebp), %edx
++ xorl %edi, %eax
++ xorl %edi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %esi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %esi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %esi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %esi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %esi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %esi
++
++
++ movl 96(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 100(%ebp), %edx
++ xorl %esi, %eax
++ xorl %esi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %edi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %edi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %edi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %edi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %edi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %edi
++
++
++ movl 104(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 108(%ebp), %edx
++ xorl %edi, %eax
++ xorl %edi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %esi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %esi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %esi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %esi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %esi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %esi
++
++
++ movl 112(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 116(%ebp), %edx
++ xorl %esi, %eax
++ xorl %esi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %edi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %edi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %edi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %edi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %edi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %edi
++
++
++ movl 120(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 124(%ebp), %edx
++ xorl %edi, %eax
++ xorl %edi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %esi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %esi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %esi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %esi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %esi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %esi
++ jmp .L003end
++.L002start_decrypt:
++
++
++ movl 120(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 124(%ebp), %edx
++ xorl %esi, %eax
++ xorl %esi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %edi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %edi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %edi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %edi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %edi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %edi
++
++
++ movl 112(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 116(%ebp), %edx
++ xorl %edi, %eax
++ xorl %edi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %esi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %esi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %esi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %esi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %esi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %esi
++
++
++ movl 104(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 108(%ebp), %edx
++ xorl %esi, %eax
++ xorl %esi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %edi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %edi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %edi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %edi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %edi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %edi
++
++
++ movl 96(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 100(%ebp), %edx
++ xorl %edi, %eax
++ xorl %edi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %esi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %esi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %esi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %esi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %esi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %esi
++
++
++ movl 88(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 92(%ebp), %edx
++ xorl %esi, %eax
++ xorl %esi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %edi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %edi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %edi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %edi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %edi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %edi
++
++
++ movl 80(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 84(%ebp), %edx
++ xorl %edi, %eax
++ xorl %edi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %esi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %esi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %esi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %esi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %esi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %esi
++
++
++ movl 72(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 76(%ebp), %edx
++ xorl %esi, %eax
++ xorl %esi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %edi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %edi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %edi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %edi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %edi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %edi
++
++
++ movl 64(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 68(%ebp), %edx
++ xorl %edi, %eax
++ xorl %edi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %esi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %esi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %esi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %esi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %esi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %esi
++
++
++ movl 56(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 60(%ebp), %edx
++ xorl %esi, %eax
++ xorl %esi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %edi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %edi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %edi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %edi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %edi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %edi
++
++
++ movl 48(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 52(%ebp), %edx
++ xorl %edi, %eax
++ xorl %edi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %esi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %esi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %esi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %esi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %esi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %esi
++
++
++ movl 40(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 44(%ebp), %edx
++ xorl %esi, %eax
++ xorl %esi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %edi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %edi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %edi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %edi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %edi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %edi
++
++
++ movl 32(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 36(%ebp), %edx
++ xorl %edi, %eax
++ xorl %edi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %esi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %esi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %esi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %esi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %esi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %esi
++
++
++ movl 24(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 28(%ebp), %edx
++ xorl %esi, %eax
++ xorl %esi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %edi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %edi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %edi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %edi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %edi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %edi
++
++
++ movl 16(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 20(%ebp), %edx
++ xorl %edi, %eax
++ xorl %edi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %esi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %esi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %esi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %esi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %esi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %esi
++
++
++ movl 8(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 12(%ebp), %edx
++ xorl %esi, %eax
++ xorl %esi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %edi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %edi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %edi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %edi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %edi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %edi
++
++
++ movl (%ebp), %eax
++ xorl %ebx, %ebx
++ movl 4(%ebp), %edx
++ xorl %edi, %eax
++ xorl %edi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %esi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %esi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %esi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %esi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %esi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %esi
++.L003end:
++
++
++ rorl $3, %edi
++ movl 20(%esp), %eax
++ rorl $3, %esi
++ movl %edi, (%eax)
++ movl %esi, 4(%eax)
++ popl %ebp
++ popl %ebx
++ popl %edi
++ popl %esi
++ ret
++.des_encrypt2_end:
++ .size des_encrypt2 , .des_encrypt2_end-des_encrypt2
++.ident "desasm.pl"
++.text
++ .align 16
++.globl des_encrypt3
++ .type des_encrypt3 , @function
++des_encrypt3:
++ pushl %ebx
++ movl 8(%esp), %ebx
++ pushl %ebp
++ pushl %esi
++ pushl %edi
++
++
++ movl (%ebx), %edi
++ movl 4(%ebx), %esi
++ subl $12, %esp
++
++
++ roll $4, %edi
++ movl %edi, %edx
++ xorl %esi, %edi
++ andl $0xf0f0f0f0, %edi
++ xorl %edi, %edx
++ xorl %edi, %esi
++
++ roll $20, %esi
++ movl %esi, %edi
++ xorl %edx, %esi
++ andl $0xfff0000f, %esi
++ xorl %esi, %edi
++ xorl %esi, %edx
++
++ roll $14, %edi
++ movl %edi, %esi
++ xorl %edx, %edi
++ andl $0x33333333, %edi
++ xorl %edi, %esi
++ xorl %edi, %edx
++
++ roll $22, %edx
++ movl %edx, %edi
++ xorl %esi, %edx
++ andl $0x03fc03fc, %edx
++ xorl %edx, %edi
++ xorl %edx, %esi
++
++ roll $9, %edi
++ movl %edi, %edx
++ xorl %esi, %edi
++ andl $0xaaaaaaaa, %edi
++ xorl %edi, %edx
++ xorl %edi, %esi
++
++ rorl $3, %edx
++ rorl $2, %esi
++ movl %esi, 4(%ebx)
++ movl 36(%esp), %eax
++ movl %edx, (%ebx)
++ movl 40(%esp), %edi
++ movl 44(%esp), %esi
++ movl $1, 8(%esp)
++ movl %eax, 4(%esp)
++ movl %ebx, (%esp)
++ call des_encrypt2
++ movl $0, 8(%esp)
++ movl %edi, 4(%esp)
++ movl %ebx, (%esp)
++ call des_encrypt2
++ movl $1, 8(%esp)
++ movl %esi, 4(%esp)
++ movl %ebx, (%esp)
++ call des_encrypt2
++ addl $12, %esp
++ movl (%ebx), %edi
++ movl 4(%ebx), %esi
++
++
++ roll $2, %esi
++ roll $3, %edi
++ movl %edi, %eax
++ xorl %esi, %edi
++ andl $0xaaaaaaaa, %edi
++ xorl %edi, %eax
++ xorl %edi, %esi
++
++ roll $23, %eax
++ movl %eax, %edi
++ xorl %esi, %eax
++ andl $0x03fc03fc, %eax
++ xorl %eax, %edi
++ xorl %eax, %esi
++
++ roll $10, %edi
++ movl %edi, %eax
++ xorl %esi, %edi
++ andl $0x33333333, %edi
++ xorl %edi, %eax
++ xorl %edi, %esi
++
++ roll $18, %esi
++ movl %esi, %edi
++ xorl %eax, %esi
++ andl $0xfff0000f, %esi
++ xorl %esi, %edi
++ xorl %esi, %eax
++
++ roll $12, %edi
++ movl %edi, %esi
++ xorl %eax, %edi
++ andl $0xf0f0f0f0, %edi
++ xorl %edi, %esi
++ xorl %edi, %eax
++
++ rorl $4, %eax
++ movl %eax, (%ebx)
++ movl %esi, 4(%ebx)
++ popl %edi
++ popl %esi
++ popl %ebp
++ popl %ebx
++ ret
++.des_encrypt3_end:
++ .size des_encrypt3 , .des_encrypt3_end-des_encrypt3
++.ident "desasm.pl"
++.text
++ .align 16
++.globl des_decrypt3
++ .type des_decrypt3 , @function
++des_decrypt3:
++ pushl %ebx
++ movl 8(%esp), %ebx
++ pushl %ebp
++ pushl %esi
++ pushl %edi
++
++
++ movl (%ebx), %edi
++ movl 4(%ebx), %esi
++ subl $12, %esp
++
++
++ roll $4, %edi
++ movl %edi, %edx
++ xorl %esi, %edi
++ andl $0xf0f0f0f0, %edi
++ xorl %edi, %edx
++ xorl %edi, %esi
++
++ roll $20, %esi
++ movl %esi, %edi
++ xorl %edx, %esi
++ andl $0xfff0000f, %esi
++ xorl %esi, %edi
++ xorl %esi, %edx
++
++ roll $14, %edi
++ movl %edi, %esi
++ xorl %edx, %edi
++ andl $0x33333333, %edi
++ xorl %edi, %esi
++ xorl %edi, %edx
++
++ roll $22, %edx
++ movl %edx, %edi
++ xorl %esi, %edx
++ andl $0x03fc03fc, %edx
++ xorl %edx, %edi
++ xorl %edx, %esi
++
++ roll $9, %edi
++ movl %edi, %edx
++ xorl %esi, %edi
++ andl $0xaaaaaaaa, %edi
++ xorl %edi, %edx
++ xorl %edi, %esi
++
++ rorl $3, %edx
++ rorl $2, %esi
++ movl %esi, 4(%ebx)
++ movl 36(%esp), %esi
++ movl %edx, (%ebx)
++ movl 40(%esp), %edi
++ movl 44(%esp), %eax
++ movl $0, 8(%esp)
++ movl %eax, 4(%esp)
++ movl %ebx, (%esp)
++ call des_encrypt2
++ movl $1, 8(%esp)
++ movl %edi, 4(%esp)
++ movl %ebx, (%esp)
++ call des_encrypt2
++ movl $0, 8(%esp)
++ movl %esi, 4(%esp)
++ movl %ebx, (%esp)
++ call des_encrypt2
++ addl $12, %esp
++ movl (%ebx), %edi
++ movl 4(%ebx), %esi
++
++
++ roll $2, %esi
++ roll $3, %edi
++ movl %edi, %eax
++ xorl %esi, %edi
++ andl $0xaaaaaaaa, %edi
++ xorl %edi, %eax
++ xorl %edi, %esi
++
++ roll $23, %eax
++ movl %eax, %edi
++ xorl %esi, %eax
++ andl $0x03fc03fc, %eax
++ xorl %eax, %edi
++ xorl %eax, %esi
++
++ roll $10, %edi
++ movl %edi, %eax
++ xorl %esi, %edi
++ andl $0x33333333, %edi
++ xorl %edi, %eax
++ xorl %edi, %esi
++
++ roll $18, %esi
++ movl %esi, %edi
++ xorl %eax, %esi
++ andl $0xfff0000f, %esi
++ xorl %esi, %edi
++ xorl %esi, %eax
++
++ roll $12, %edi
++ movl %edi, %esi
++ xorl %eax, %edi
++ andl $0xf0f0f0f0, %edi
++ xorl %edi, %esi
++ xorl %edi, %eax
++
++ rorl $4, %eax
++ movl %eax, (%ebx)
++ movl %esi, 4(%ebx)
++ popl %edi
++ popl %esi
++ popl %ebp
++ popl %ebx
++ ret
++.des_decrypt3_end:
++ .size des_decrypt3 , .des_decrypt3_end-des_decrypt3
++.ident "desasm.pl"
++.text
++ .align 16
++.globl des_ncbc_encrypt
++ .type des_ncbc_encrypt , @function
++des_ncbc_encrypt:
++
++ pushl %ebp
++ pushl %ebx
++ pushl %esi
++ pushl %edi
++ movl 28(%esp), %ebp
++
++ movl 36(%esp), %ebx
++ movl (%ebx), %esi
++ movl 4(%ebx), %edi
++ pushl %edi
++ pushl %esi
++ pushl %edi
++ pushl %esi
++ movl %esp, %ebx
++ movl 36(%esp), %esi
++ movl 40(%esp), %edi
++
++ movl 56(%esp), %ecx
++
++ pushl %ecx
++
++ movl 52(%esp), %eax
++ pushl %eax
++ pushl %ebx
++ cmpl $0, %ecx
++ jz .L004decrypt
++ andl $4294967288, %ebp
++ movl 12(%esp), %eax
++ movl 16(%esp), %ebx
++ jz .L005encrypt_finish
++.L006encrypt_loop:
++ movl (%esi), %ecx
++ movl 4(%esi), %edx
++ xorl %ecx, %eax
++ xorl %edx, %ebx
++ movl %eax, 12(%esp)
++ movl %ebx, 16(%esp)
++ call des_encrypt
++ movl 12(%esp), %eax
++ movl 16(%esp), %ebx
++ movl %eax, (%edi)
++ movl %ebx, 4(%edi)
++ addl $8, %esi
++ addl $8, %edi
++ subl $8, %ebp
++ jnz .L006encrypt_loop
++.L005encrypt_finish:
++ movl 56(%esp), %ebp
++ andl $7, %ebp
++ jz .L007finish
++ xorl %ecx, %ecx
++ xorl %edx, %edx
++ movl .L008cbc_enc_jmp_table(,%ebp,4),%ebp
++ jmp *%ebp
++.L009ej7:
++ movb 6(%esi), %dh
++ sall $8, %edx
++.L010ej6:
++ movb 5(%esi), %dh
++.L011ej5:
++ movb 4(%esi), %dl
++.L012ej4:
++ movl (%esi), %ecx
++ jmp .L013ejend
++.L014ej3:
++ movb 2(%esi), %ch
++ sall $8, %ecx
++.L015ej2:
++ movb 1(%esi), %ch
++.L016ej1:
++ movb (%esi), %cl
++.L013ejend:
++ xorl %ecx, %eax
++ xorl %edx, %ebx
++ movl %eax, 12(%esp)
++ movl %ebx, 16(%esp)
++ call des_encrypt
++ movl 12(%esp), %eax
++ movl 16(%esp), %ebx
++ movl %eax, (%edi)
++ movl %ebx, 4(%edi)
++ jmp .L007finish
++.align 16
++.L004decrypt:
++ andl $4294967288, %ebp
++ movl 20(%esp), %eax
++ movl 24(%esp), %ebx
++ jz .L017decrypt_finish
++.L018decrypt_loop:
++ movl (%esi), %eax
++ movl 4(%esi), %ebx
++ movl %eax, 12(%esp)
++ movl %ebx, 16(%esp)
++ call des_encrypt
++ movl 12(%esp), %eax
++ movl 16(%esp), %ebx
++ movl 20(%esp), %ecx
++ movl 24(%esp), %edx
++ xorl %eax, %ecx
++ xorl %ebx, %edx
++ movl (%esi), %eax
++ movl 4(%esi), %ebx
++ movl %ecx, (%edi)
++ movl %edx, 4(%edi)
++ movl %eax, 20(%esp)
++ movl %ebx, 24(%esp)
++ addl $8, %esi
++ addl $8, %edi
++ subl $8, %ebp
++ jnz .L018decrypt_loop
++.L017decrypt_finish:
++ movl 56(%esp), %ebp
++ andl $7, %ebp
++ jz .L007finish
++ movl (%esi), %eax
++ movl 4(%esi), %ebx
++ movl %eax, 12(%esp)
++ movl %ebx, 16(%esp)
++ call des_encrypt
++ movl 12(%esp), %eax
++ movl 16(%esp), %ebx
++ movl 20(%esp), %ecx
++ movl 24(%esp), %edx
++ xorl %eax, %ecx
++ xorl %ebx, %edx
++ movl (%esi), %eax
++ movl 4(%esi), %ebx
++.L019dj7:
++ rorl $16, %edx
++ movb %dl, 6(%edi)
++ shrl $16, %edx
++.L020dj6:
++ movb %dh, 5(%edi)
++.L021dj5:
++ movb %dl, 4(%edi)
++.L022dj4:
++ movl %ecx, (%edi)
++ jmp .L023djend
++.L024dj3:
++ rorl $16, %ecx
++ movb %cl, 2(%edi)
++ sall $16, %ecx
++.L025dj2:
++ movb %ch, 1(%esi)
++.L026dj1:
++ movb %cl, (%esi)
++.L023djend:
++ jmp .L007finish
++.align 16
++.L007finish:
++ movl 64(%esp), %ecx
++ addl $28, %esp
++ movl %eax, (%ecx)
++ movl %ebx, 4(%ecx)
++ popl %edi
++ popl %esi
++ popl %ebx
++ popl %ebp
++ ret
++.align 16
++.L008cbc_enc_jmp_table:
++ .long 0
++ .long .L016ej1
++ .long .L015ej2
++ .long .L014ej3
++ .long .L012ej4
++ .long .L011ej5
++ .long .L010ej6
++ .long .L009ej7
++.align 16
++.L027cbc_dec_jmp_table:
++ .long 0
++ .long .L026dj1
++ .long .L025dj2
++ .long .L024dj3
++ .long .L022dj4
++ .long .L021dj5
++ .long .L020dj6
++ .long .L019dj7
++.des_ncbc_encrypt_end:
++ .size des_ncbc_encrypt , .des_ncbc_encrypt_end-des_ncbc_encrypt
++.ident "desasm.pl"
++.text
++ .align 16
++.globl des_ede3_cbc_encrypt
++ .type des_ede3_cbc_encrypt , @function
++des_ede3_cbc_encrypt:
++
++ pushl %ebp
++ pushl %ebx
++ pushl %esi
++ pushl %edi
++ movl 28(%esp), %ebp
++
++ movl 44(%esp), %ebx
++ movl (%ebx), %esi
++ movl 4(%ebx), %edi
++ pushl %edi
++ pushl %esi
++ pushl %edi
++ pushl %esi
++ movl %esp, %ebx
++ movl 36(%esp), %esi
++ movl 40(%esp), %edi
++
++ movl 64(%esp), %ecx
++
++ movl 56(%esp), %eax
++ pushl %eax
++
++ movl 56(%esp), %eax
++ pushl %eax
++
++ movl 56(%esp), %eax
++ pushl %eax
++ pushl %ebx
++ cmpl $0, %ecx
++ jz .L028decrypt
++ andl $4294967288, %ebp
++ movl 16(%esp), %eax
++ movl 20(%esp), %ebx
++ jz .L029encrypt_finish
++.L030encrypt_loop:
++ movl (%esi), %ecx
++ movl 4(%esi), %edx
++ xorl %ecx, %eax
++ xorl %edx, %ebx
++ movl %eax, 16(%esp)
++ movl %ebx, 20(%esp)
++ call des_encrypt3
++ movl 16(%esp), %eax
++ movl 20(%esp), %ebx
++ movl %eax, (%edi)
++ movl %ebx, 4(%edi)
++ addl $8, %esi
++ addl $8, %edi
++ subl $8, %ebp
++ jnz .L030encrypt_loop
++.L029encrypt_finish:
++ movl 60(%esp), %ebp
++ andl $7, %ebp
++ jz .L031finish
++ xorl %ecx, %ecx
++ xorl %edx, %edx
++ movl .L032cbc_enc_jmp_table(,%ebp,4),%ebp
++ jmp *%ebp
++.L033ej7:
++ movb 6(%esi), %dh
++ sall $8, %edx
++.L034ej6:
++ movb 5(%esi), %dh
++.L035ej5:
++ movb 4(%esi), %dl
++.L036ej4:
++ movl (%esi), %ecx
++ jmp .L037ejend
++.L038ej3:
++ movb 2(%esi), %ch
++ sall $8, %ecx
++.L039ej2:
++ movb 1(%esi), %ch
++.L040ej1:
++ movb (%esi), %cl
++.L037ejend:
++ xorl %ecx, %eax
++ xorl %edx, %ebx
++ movl %eax, 16(%esp)
++ movl %ebx, 20(%esp)
++ call des_encrypt3
++ movl 16(%esp), %eax
++ movl 20(%esp), %ebx
++ movl %eax, (%edi)
++ movl %ebx, 4(%edi)
++ jmp .L031finish
++.align 16
++.L028decrypt:
++ andl $4294967288, %ebp
++ movl 24(%esp), %eax
++ movl 28(%esp), %ebx
++ jz .L041decrypt_finish
++.L042decrypt_loop:
++ movl (%esi), %eax
++ movl 4(%esi), %ebx
++ movl %eax, 16(%esp)
++ movl %ebx, 20(%esp)
++ call des_decrypt3
++ movl 16(%esp), %eax
++ movl 20(%esp), %ebx
++ movl 24(%esp), %ecx
++ movl 28(%esp), %edx
++ xorl %eax, %ecx
++ xorl %ebx, %edx
++ movl (%esi), %eax
++ movl 4(%esi), %ebx
++ movl %ecx, (%edi)
++ movl %edx, 4(%edi)
++ movl %eax, 24(%esp)
++ movl %ebx, 28(%esp)
++ addl $8, %esi
++ addl $8, %edi
++ subl $8, %ebp
++ jnz .L042decrypt_loop
++.L041decrypt_finish:
++ movl 60(%esp), %ebp
++ andl $7, %ebp
++ jz .L031finish
++ movl (%esi), %eax
++ movl 4(%esi), %ebx
++ movl %eax, 16(%esp)
++ movl %ebx, 20(%esp)
++ call des_decrypt3
++ movl 16(%esp), %eax
++ movl 20(%esp), %ebx
++ movl 24(%esp), %ecx
++ movl 28(%esp), %edx
++ xorl %eax, %ecx
++ xorl %ebx, %edx
++ movl (%esi), %eax
++ movl 4(%esi), %ebx
++.L043dj7:
++ rorl $16, %edx
++ movb %dl, 6(%edi)
++ shrl $16, %edx
++.L044dj6:
++ movb %dh, 5(%edi)
++.L045dj5:
++ movb %dl, 4(%edi)
++.L046dj4:
++ movl %ecx, (%edi)
++ jmp .L047djend
++.L048dj3:
++ rorl $16, %ecx
++ movb %cl, 2(%edi)
++ sall $16, %ecx
++.L049dj2:
++ movb %ch, 1(%esi)
++.L050dj1:
++ movb %cl, (%esi)
++.L047djend:
++ jmp .L031finish
++.align 16
++.L031finish:
++ movl 76(%esp), %ecx
++ addl $32, %esp
++ movl %eax, (%ecx)
++ movl %ebx, 4(%ecx)
++ popl %edi
++ popl %esi
++ popl %ebx
++ popl %ebp
++ ret
++.align 16
++.L032cbc_enc_jmp_table:
++ .long 0
++ .long .L040ej1
++ .long .L039ej2
++ .long .L038ej3
++ .long .L036ej4
++ .long .L035ej5
++ .long .L034ej6
++ .long .L033ej7
++.align 16
++.L051cbc_dec_jmp_table:
++ .long 0
++ .long .L050dj1
++ .long .L049dj2
++ .long .L048dj3
++ .long .L046dj4
++ .long .L045dj5
++ .long .L044dj6
++ .long .L043dj7
++.des_ede3_cbc_encrypt_end:
++ .size des_ede3_cbc_encrypt , .des_ede3_cbc_encrypt_end-des_ede3_cbc_encrypt
++.ident "desasm.pl"
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/crypto/ciphers/des/ecb_enc.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,128 @@
++/* crypto/des/ecb_enc.c */
++/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com)
++ * All rights reserved.
++ *
++ * This package is an SSL implementation written
++ * by Eric Young (eay@cryptsoft.com).
++ * The implementation was written so as to conform with Netscapes SSL.
++ *
++ * This library is free for commercial and non-commercial use as long as
++ * the following conditions are aheared to. The following conditions
++ * apply to all code found in this distribution, be it the RC4, RSA,
++ * lhash, DES, etc., code; not just the SSL code. The SSL documentation
++ * included with this distribution is covered by the same copyright terms
++ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
++ *
++ * Copyright remains Eric Young's, and as such any Copyright notices in
++ * the code are not to be removed.
++ * If this package is used in a product, Eric Young should be given attribution
++ * as the author of the parts of the library used.
++ * This can be in the form of a textual message at program startup or
++ * in documentation (online or textual) provided with the package.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ * 1. Redistributions of source code must retain the copyright
++ * notice, this list of conditions and the following disclaimer.
++ * 2. Redistributions in binary form must reproduce the above copyright
++ * notice, this list of conditions and the following disclaimer in the
++ * documentation and/or other materials provided with the distribution.
++ * 3. All advertising materials mentioning features or use of this software
++ * must display the following acknowledgement:
++ * "This product includes cryptographic software written by
++ * Eric Young (eay@cryptsoft.com)"
++ * The word 'cryptographic' can be left out if the rouines from the library
++ * being used are not cryptographic related :-).
++ * 4. If you include any Windows specific code (or a derivative thereof) from
++ * the apps directory (application code) you must include an acknowledgement:
++ * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
++ *
++ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ *
++ * The licence and distribution terms for any publically available version or
++ * derivative of this code cannot be changed. i.e. this code cannot simply be
++ * copied and put under another distribution licence
++ * [including the GNU Public Licence.]
++ */
++
++#include "des_locl.h"
++#include "spr.h"
++
++char *libdes_version="libdes v 3.24 - 20-Apr-1996 - eay";
++char *DES_version="DES part of SSLeay 0.8.2b 08-Jan-1998";
++
++/* RCSID $Id$ */
++/* This function ifdef'ed out for FreeS/WAN project. */
++#ifdef notdef
++char *des_options()
++ {
++ static int init=1;
++ static char buf[32];
++
++ if (init)
++ {
++ char *ptr,*unroll,*risc,*size;
++
++ init=0;
++#ifdef DES_PTR
++ ptr="ptr";
++#else
++ ptr="idx";
++#endif
++#if defined(DES_RISC1) || defined(DES_RISC2)
++#ifdef DES_RISC1
++ risc="risc1";
++#endif
++#ifdef DES_RISC2
++ risc="risc2";
++#endif
++#else
++ risc="cisc";
++#endif
++#ifdef DES_UNROLL
++ unroll="16";
++#else
++ unroll="4";
++#endif
++ if (sizeof(DES_LONG) != sizeof(long))
++ size="int";
++ else
++ size="long";
++ sprintf(buf,"des(%s,%s,%s,%s)",ptr,risc,unroll,size);
++ }
++ return(buf);
++ }
++#endif
++
++
++void des_ecb_encrypt(input, output, ks, enc)
++des_cblock (*input);
++des_cblock (*output);
++des_key_schedule ks;
++int enc;
++ {
++ register DES_LONG l;
++ register unsigned char *in,*out;
++ DES_LONG ll[2];
++
++ in=(unsigned char *)input;
++ out=(unsigned char *)output;
++ c2l(in,l); ll[0]=l;
++ c2l(in,l); ll[1]=l;
++ des_encrypt(ll,ks,enc);
++ l=ll[0]; l2c(l,out);
++ l=ll[1]; l2c(l,out);
++ l=ll[0]=ll[1]=0;
++ }
++
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/crypto/ciphers/des/fcrypt.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,152 @@
++/* NOCW */
++
++/* This version of crypt has been developed from my MIT compatable
++ * DES library.
++ * The library is available at pub/Crypto/DES at ftp.psy.uq.oz.au
++ * Eric Young (eay@cryptsoft.com)
++ */
++
++/* Modification by Jens Kupferschmidt (Cu)
++ * I have included directive PARA for shared memory computers.
++ * I have included a directive LONGCRYPT to using this routine to cipher
++ * passwords with more then 8 bytes like HP-UX 10.x it used. The MAXPLEN
++ * definition is the maximum of lenght of password and can changed. I have
++ * defined 24.
++ */
++
++#include "des_locl.h"
++
++/* Added more values to handle illegal salt values the way normal
++ * crypt() implementations do. The patch was sent by
++ * Bjorn Gronvall <bg@sics.se>
++ */
++static unsigned const char con_salt[128]={
++0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,
++0xDA,0xDB,0xDC,0xDD,0xDE,0xDF,0xE0,0xE1,
++0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,
++0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,
++0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,
++0xFA,0xFB,0xFC,0xFD,0xFE,0xFF,0x00,0x01,
++0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,
++0x0A,0x0B,0x05,0x06,0x07,0x08,0x09,0x0A,
++0x0B,0x0C,0x0D,0x0E,0x0F,0x10,0x11,0x12,
++0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,
++0x1B,0x1C,0x1D,0x1E,0x1F,0x20,0x21,0x22,
++0x23,0x24,0x25,0x20,0x21,0x22,0x23,0x24,
++0x25,0x26,0x27,0x28,0x29,0x2A,0x2B,0x2C,
++0x2D,0x2E,0x2F,0x30,0x31,0x32,0x33,0x34,
++0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,
++0x3D,0x3E,0x3F,0x40,0x41,0x42,0x43,0x44,
++};
++
++static unsigned const char cov_2char[64]={
++0x2E,0x2F,0x30,0x31,0x32,0x33,0x34,0x35,
++0x36,0x37,0x38,0x39,0x41,0x42,0x43,0x44,
++0x45,0x46,0x47,0x48,0x49,0x4A,0x4B,0x4C,
++0x4D,0x4E,0x4F,0x50,0x51,0x52,0x53,0x54,
++0x55,0x56,0x57,0x58,0x59,0x5A,0x61,0x62,
++0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6A,
++0x6B,0x6C,0x6D,0x6E,0x6F,0x70,0x71,0x72,
++0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7A
++};
++
++#ifndef NOPROTO
++void fcrypt_body(DES_LONG *out,des_key_schedule ks,
++ DES_LONG Eswap0, DES_LONG Eswap1);
++
++#ifdef PERL5
++char *des_crypt(const char *buf,const char *salt);
++#else
++char *crypt(const char *buf,const char *salt);
++#endif
++#else
++void fcrypt_body();
++#ifdef PERL5
++char *des_crypt();
++#else
++char *crypt();
++#endif
++#endif
++
++#ifdef PERL5
++char *des_crypt(buf,salt)
++#else
++char *crypt(buf,salt)
++#endif
++const char *buf;
++const char *salt;
++ {
++ static char buff[14];
++
++ return(des_fcrypt(buf,salt,buff));
++ }
++
++
++char *des_fcrypt(buf,salt,ret)
++const char *buf;
++const char *salt;
++char *ret;
++ {
++ unsigned int i,j,x,y;
++ DES_LONG Eswap0,Eswap1;
++ DES_LONG out[2],ll;
++ des_cblock key;
++ des_key_schedule ks;
++ unsigned char bb[9];
++ unsigned char *b=bb;
++ unsigned char c,u;
++
++ /* eay 25/08/92
++ * If you call crypt("pwd","*") as often happens when you
++ * have * as the pwd field in /etc/passwd, the function
++ * returns *\0XXXXXXXXX
++ * The \0 makes the string look like * so the pwd "*" would
++ * crypt to "*". This was found when replacing the crypt in
++ * our shared libraries. People found that the disbled
++ * accounts effectivly had no passwd :-(. */
++ x=ret[0]=((salt[0] == '\0')?'A':salt[0]);
++ Eswap0=con_salt[x]<<2;
++ x=ret[1]=((salt[1] == '\0')?'A':salt[1]);
++ Eswap1=con_salt[x]<<6;
++
++/* EAY
++r=strlen(buf);
++r=(r+7)/8;
++*/
++ for (i=0; i<8; i++)
++ {
++ c= *(buf++);
++ if (!c) break;
++ key[i]=(c<<1);
++ }
++ for (; i<8; i++)
++ key[i]=0;
++
++ des_set_key((des_cblock *)(key),ks);
++ fcrypt_body(&(out[0]),ks,Eswap0,Eswap1);
++
++ ll=out[0]; l2c(ll,b);
++ ll=out[1]; l2c(ll,b);
++ y=0;
++ u=0x80;
++ bb[8]=0;
++ for (i=2; i<13; i++)
++ {
++ c=0;
++ for (j=0; j<6; j++)
++ {
++ c<<=1;
++ if (bb[y] & u) c|=1;
++ u>>=1;
++ if (!u)
++ {
++ y++;
++ u=0x80;
++ }
++ }
++ ret[i]=cov_2char[c];
++ }
++ ret[13]='\0';
++ return(ret);
++ }
++
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/crypto/ciphers/des/fcrypt_b.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,148 @@
++/* crypto/des/fcrypt_b.c */
++/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com)
++ * All rights reserved.
++ *
++ * This package is an SSL implementation written
++ * by Eric Young (eay@cryptsoft.com).
++ * The implementation was written so as to conform with Netscapes SSL.
++ *
++ * This library is free for commercial and non-commercial use as long as
++ * the following conditions are aheared to. The following conditions
++ * apply to all code found in this distribution, be it the RC4, RSA,
++ * lhash, DES, etc., code; not just the SSL code. The SSL documentation
++ * included with this distribution is covered by the same copyright terms
++ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
++ *
++ * Copyright remains Eric Young's, and as such any Copyright notices in
++ * the code are not to be removed.
++ * If this package is used in a product, Eric Young should be given attribution
++ * as the author of the parts of the library used.
++ * This can be in the form of a textual message at program startup or
++ * in documentation (online or textual) provided with the package.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ * 1. Redistributions of source code must retain the copyright
++ * notice, this list of conditions and the following disclaimer.
++ * 2. Redistributions in binary form must reproduce the above copyright
++ * notice, this list of conditions and the following disclaimer in the
++ * documentation and/or other materials provided with the distribution.
++ * 3. All advertising materials mentioning features or use of this software
++ * must display the following acknowledgement:
++ * "This product includes cryptographic software written by
++ * Eric Young (eay@cryptsoft.com)"
++ * The word 'cryptographic' can be left out if the rouines from the library
++ * being used are not cryptographic related :-).
++ * 4. If you include any Windows specific code (or a derivative thereof) from
++ * the apps directory (application code) you must include an acknowledgement:
++ * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
++ *
++ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ *
++ * The licence and distribution terms for any publically available version or
++ * derivative of this code cannot be changed. i.e. this code cannot simply be
++ * copied and put under another distribution licence
++ * [including the GNU Public Licence.]
++ */
++
++/* #include <stdio.h> */
++
++/* This version of crypt has been developed from my MIT compatable
++ * DES library.
++ * The library is available at pub/Crypto/DES at ftp.psy.uq.oz.au
++ * Eric Young (eay@cryptsoft.com)
++ */
++
++#define DES_FCRYPT
++#include "des_locl.h"
++#undef DES_FCRYPT
++
++#undef PERM_OP
++#define PERM_OP(a,b,t,n,m) ((t)=((((a)>>(n))^(b))&(m)),\
++ (b)^=(t),\
++ (a)^=((t)<<(n)))
++
++#undef HPERM_OP
++#define HPERM_OP(a,t,n,m) ((t)=((((a)<<(16-(n)))^(a))&(m)),\
++ (a)=(a)^(t)^(t>>(16-(n))))\
++
++void fcrypt_body(out, ks, Eswap0, Eswap1)
++DES_LONG *out;
++des_key_schedule ks;
++DES_LONG Eswap0;
++DES_LONG Eswap1;
++ {
++ register DES_LONG l,r,t,u;
++#ifdef DES_PTR
++ register unsigned char *des_SP=(unsigned char *)des_SPtrans;
++#endif
++ register DES_LONG *s;
++ register int j;
++ register DES_LONG E0,E1;
++
++ l=0;
++ r=0;
++
++ s=(DES_LONG *)ks;
++ E0=Eswap0;
++ E1=Eswap1;
++
++ for (j=0; j<25; j++)
++ {
++#ifdef DES_UNROLL
++ register int i;
++
++ for (i=0; i<32; i+=8)
++ {
++ D_ENCRYPT(l,r,i+0); /* 1 */
++ D_ENCRYPT(r,l,i+2); /* 2 */
++ D_ENCRYPT(l,r,i+4); /* 1 */
++ D_ENCRYPT(r,l,i+6); /* 2 */
++ }
++#else
++ D_ENCRYPT(l,r, 0); /* 1 */
++ D_ENCRYPT(r,l, 2); /* 2 */
++ D_ENCRYPT(l,r, 4); /* 3 */
++ D_ENCRYPT(r,l, 6); /* 4 */
++ D_ENCRYPT(l,r, 8); /* 5 */
++ D_ENCRYPT(r,l,10); /* 6 */
++ D_ENCRYPT(l,r,12); /* 7 */
++ D_ENCRYPT(r,l,14); /* 8 */
++ D_ENCRYPT(l,r,16); /* 9 */
++ D_ENCRYPT(r,l,18); /* 10 */
++ D_ENCRYPT(l,r,20); /* 11 */
++ D_ENCRYPT(r,l,22); /* 12 */
++ D_ENCRYPT(l,r,24); /* 13 */
++ D_ENCRYPT(r,l,26); /* 14 */
++ D_ENCRYPT(l,r,28); /* 15 */
++ D_ENCRYPT(r,l,30); /* 16 */
++#endif
++
++ t=l;
++ l=r;
++ r=t;
++ }
++ l=ROTATE(l,3)&0xffffffffL;
++ r=ROTATE(r,3)&0xffffffffL;
++
++ PERM_OP(l,r,t, 1,0x55555555L);
++ PERM_OP(r,l,t, 8,0x00ff00ffL);
++ PERM_OP(l,r,t, 2,0x33333333L);
++ PERM_OP(r,l,t,16,0x0000ffffL);
++ PERM_OP(l,r,t, 4,0x0f0f0f0fL);
++
++ out[0]=r;
++ out[1]=l;
++ }
++
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/crypto/ciphers/des/options.txt Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,39 @@
++Note that the UNROLL option makes the 'inner' des loop unroll all 16 rounds
++instead of the default 4.
++RISC1 and RISC2 are 2 alternatives for the inner loop and
++PTR means to use pointers arithmatic instead of arrays.
++
++FreeBSD - Pentium Pro 200mhz - gcc 2.7.2.2 - assembler 577,000 4620k/s
++IRIX 6.2 - R10000 195mhz - cc (-O3 -n32) - UNROLL RISC2 PTR 496,000 3968k/s
++solaris 2.5.1 usparc 167mhz?? - SC4.0 - UNROLL RISC1 PTR [1] 459,400 3672k/s
++FreeBSD - Pentium Pro 200mhz - gcc 2.7.2.2 - UNROLL RISC1 433,000 3468k/s
++solaris 2.5.1 usparc 167mhz?? - gcc 2.7.2 - UNROLL 380,000 3041k/s
++linux - pentium 100mhz - gcc 2.7.0 - assembler 281,000 2250k/s
++NT 4.0 - pentium 100mhz - VC 4.2 - assembler 281,000 2250k/s
++AIX 4.1? - PPC604 100mhz - cc - UNROLL 275,000 2200k/s
++IRIX 5.3 - R4400 200mhz - gcc 2.6.3 - UNROLL RISC2 PTR 235,300 1882k/s
++IRIX 5.3 - R4400 200mhz - cc - UNROLL RISC2 PTR 233,700 1869k/s
++NT 4.0 - pentium 100mhz - VC 4.2 - UNROLL RISC1 PTR 191,000 1528k/s
++DEC Alpha 165mhz?? - cc - RISC2 PTR [2] 181,000 1448k/s
++linux - pentium 100mhz - gcc 2.7.0 - UNROLL RISC1 PTR 158,500 1268k/s
++HPUX 10 - 9000/887 - cc - UNROLL [3] 148,000 1190k/s
++solaris 2.5.1 - sparc 10 50mhz - gcc 2.7.2 - UNROLL 123,600 989k/s
++IRIX 5.3 - R4000 100mhz - cc - UNROLL RISC2 PTR 101,000 808k/s
++DGUX - 88100 50mhz(?) - gcc 2.6.3 - UNROLL 81,000 648k/s
++solaris 2.4 486 50mhz - gcc 2.6.3 - assembler 65,000 522k/s
++HPUX 10 - 9000/887 - k&r cc (default compiler) - UNROLL PTR 76,000 608k/s
++solaris 2.4 486 50mhz - gcc 2.6.3 - UNROLL RISC2 43,500 344k/s
++AIX - old slow one :-) - cc - 39,000 312k/s
++
++Notes.
++[1] For the ultra sparc, SunC 4.0
++ cc -xtarget=ultra -xarch=v8plus -Xa -xO5, running 'des_opts'
++ gives a speed of 344,000 des/s while 'speed' gives 459,000 des/s.
++ I'll record the higher since it is coming from the library but it
++ is all rather weird.
++[2] Similar to the ultra sparc ([1]), 181,000 for 'des_opts' vs 175,000.
++[3] I was unable to get access to this machine when it was not heavily loaded.
++ As such, my timing program was never able to get more that %30 of the CPU.
++ This would cause the program to give much lower speed numbers because
++ it would be 'fighting' to stay in the cache with the other CPU burning
++ processes.
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/crypto/ciphers/des/podd.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,75 @@
++/* crypto/des/podd.h */
++/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com)
++ * All rights reserved.
++ *
++ * This package is an SSL implementation written
++ * by Eric Young (eay@cryptsoft.com).
++ * The implementation was written so as to conform with Netscapes SSL.
++ *
++ * This library is free for commercial and non-commercial use as long as
++ * the following conditions are aheared to. The following conditions
++ * apply to all code found in this distribution, be it the RC4, RSA,
++ * lhash, DES, etc., code; not just the SSL code. The SSL documentation
++ * included with this distribution is covered by the same copyright terms
++ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
++ *
++ * Copyright remains Eric Young's, and as such any Copyright notices in
++ * the code are not to be removed.
++ * If this package is used in a product, Eric Young should be given attribution
++ * as the author of the parts of the library used.
++ * This can be in the form of a textual message at program startup or
++ * in documentation (online or textual) provided with the package.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ * 1. Redistributions of source code must retain the copyright
++ * notice, this list of conditions and the following disclaimer.
++ * 2. Redistributions in binary form must reproduce the above copyright
++ * notice, this list of conditions and the following disclaimer in the
++ * documentation and/or other materials provided with the distribution.
++ * 3. All advertising materials mentioning features or use of this software
++ * must display the following acknowledgement:
++ * "This product includes cryptographic software written by
++ * Eric Young (eay@cryptsoft.com)"
++ * The word 'cryptographic' can be left out if the rouines from the library
++ * being used are not cryptographic related :-).
++ * 4. If you include any Windows specific code (or a derivative thereof) from
++ * the apps directory (application code) you must include an acknowledgement:
++ * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
++ *
++ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ *
++ * The licence and distribution terms for any publically available version or
++ * derivative of this code cannot be changed. i.e. this code cannot simply be
++ * copied and put under another distribution licence
++ * [including the GNU Public Licence.]
++ */
++
++static const unsigned char odd_parity[256]={
++ 1, 1, 2, 2, 4, 4, 7, 7, 8, 8, 11, 11, 13, 13, 14, 14,
++ 16, 16, 19, 19, 21, 21, 22, 22, 25, 25, 26, 26, 28, 28, 31, 31,
++ 32, 32, 35, 35, 37, 37, 38, 38, 41, 41, 42, 42, 44, 44, 47, 47,
++ 49, 49, 50, 50, 52, 52, 55, 55, 56, 56, 59, 59, 61, 61, 62, 62,
++ 64, 64, 67, 67, 69, 69, 70, 70, 73, 73, 74, 74, 76, 76, 79, 79,
++ 81, 81, 82, 82, 84, 84, 87, 87, 88, 88, 91, 91, 93, 93, 94, 94,
++ 97, 97, 98, 98,100,100,103,103,104,104,107,107,109,109,110,110,
++112,112,115,115,117,117,118,118,121,121,122,122,124,124,127,127,
++128,128,131,131,133,133,134,134,137,137,138,138,140,140,143,143,
++145,145,146,146,148,148,151,151,152,152,155,155,157,157,158,158,
++161,161,162,162,164,164,167,167,168,168,171,171,173,173,174,174,
++176,176,179,179,181,181,182,182,185,185,186,186,188,188,191,191,
++193,193,194,194,196,196,199,199,200,200,203,203,205,205,206,206,
++208,208,211,211,213,213,214,214,217,217,218,218,220,220,223,223,
++224,224,227,227,229,229,230,230,233,233,234,234,236,236,239,239,
++241,241,242,242,244,244,247,247,248,248,251,251,253,253,254,254};
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/crypto/ciphers/des/set_key.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,246 @@
++/* crypto/des/set_key.c */
++/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com)
++ * All rights reserved.
++ *
++ * This package is an SSL implementation written
++ * by Eric Young (eay@cryptsoft.com).
++ * The implementation was written so as to conform with Netscapes SSL.
++ *
++ * This library is free for commercial and non-commercial use as long as
++ * the following conditions are aheared to. The following conditions
++ * apply to all code found in this distribution, be it the RC4, RSA,
++ * lhash, DES, etc., code; not just the SSL code. The SSL documentation
++ * included with this distribution is covered by the same copyright terms
++ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
++ *
++ * Copyright remains Eric Young's, and as such any Copyright notices in
++ * the code are not to be removed.
++ * If this package is used in a product, Eric Young should be given attribution
++ * as the author of the parts of the library used.
++ * This can be in the form of a textual message at program startup or
++ * in documentation (online or textual) provided with the package.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ * 1. Redistributions of source code must retain the copyright
++ * notice, this list of conditions and the following disclaimer.
++ * 2. Redistributions in binary form must reproduce the above copyright
++ * notice, this list of conditions and the following disclaimer in the
++ * documentation and/or other materials provided with the distribution.
++ * 3. All advertising materials mentioning features or use of this software
++ * must display the following acknowledgement:
++ * "This product includes cryptographic software written by
++ * Eric Young (eay@cryptsoft.com)"
++ * The word 'cryptographic' can be left out if the rouines from the library
++ * being used are not cryptographic related :-).
++ * 4. If you include any Windows specific code (or a derivative thereof) from
++ * the apps directory (application code) you must include an acknowledgement:
++ * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
++ *
++ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ *
++ * The licence and distribution terms for any publically available version or
++ * derivative of this code cannot be changed. i.e. this code cannot simply be
++ * copied and put under another distribution licence
++ * [including the GNU Public Licence.]
++ */
++
++/* set_key.c v 1.4 eay 24/9/91
++ * 1.4 Speed up by 400% :-)
++ * 1.3 added register declarations.
++ * 1.2 unrolled make_key_sched a bit more
++ * 1.1 added norm_expand_bits
++ * 1.0 First working version
++ */
++#include "des_locl.h"
++#include "podd.h"
++#include "sk.h"
++
++#ifndef NOPROTO
++static int check_parity(des_cblock (*key));
++#else
++static int check_parity();
++#endif
++
++int des_check_key=0;
++
++void des_set_odd_parity(key)
++des_cblock (*key);
++ {
++ int i;
++
++ for (i=0; i<DES_KEY_SZ; i++)
++ (*key)[i]=odd_parity[(*key)[i]];
++ }
++
++static int check_parity(key)
++des_cblock (*key);
++ {
++ int i;
++
++ for (i=0; i<DES_KEY_SZ; i++)
++ {
++ if ((*key)[i] != odd_parity[(*key)[i]])
++ return(0);
++ }
++ return(1);
++ }
++
++/* Weak and semi week keys as take from
++ * %A D.W. Davies
++ * %A W.L. Price
++ * %T Security for Computer Networks
++ * %I John Wiley & Sons
++ * %D 1984
++ * Many thanks to smb@ulysses.att.com (Steven Bellovin) for the reference
++ * (and actual cblock values).
++ */
++#define NUM_WEAK_KEY 16
++static des_cblock weak_keys[NUM_WEAK_KEY]={
++ /* weak keys */
++ {0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01},
++ {0xFE,0xFE,0xFE,0xFE,0xFE,0xFE,0xFE,0xFE},
++ {0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F},
++ {0xE0,0xE0,0xE0,0xE0,0xE0,0xE0,0xE0,0xE0},
++ /* semi-weak keys */
++ {0x01,0xFE,0x01,0xFE,0x01,0xFE,0x01,0xFE},
++ {0xFE,0x01,0xFE,0x01,0xFE,0x01,0xFE,0x01},
++ {0x1F,0xE0,0x1F,0xE0,0x0E,0xF1,0x0E,0xF1},
++ {0xE0,0x1F,0xE0,0x1F,0xF1,0x0E,0xF1,0x0E},
++ {0x01,0xE0,0x01,0xE0,0x01,0xF1,0x01,0xF1},
++ {0xE0,0x01,0xE0,0x01,0xF1,0x01,0xF1,0x01},
++ {0x1F,0xFE,0x1F,0xFE,0x0E,0xFE,0x0E,0xFE},
++ {0xFE,0x1F,0xFE,0x1F,0xFE,0x0E,0xFE,0x0E},
++ {0x01,0x1F,0x01,0x1F,0x01,0x0E,0x01,0x0E},
++ {0x1F,0x01,0x1F,0x01,0x0E,0x01,0x0E,0x01},
++ {0xE0,0xFE,0xE0,0xFE,0xF1,0xFE,0xF1,0xFE},
++ {0xFE,0xE0,0xFE,0xE0,0xFE,0xF1,0xFE,0xF1}};
++
++int des_is_weak_key(key)
++des_cblock (*key);
++ {
++ int i;
++
++ for (i=0; i<NUM_WEAK_KEY; i++)
++ /* Added == 0 to comparision, I obviously don't run
++ * this section very often :-(, thanks to
++ * engineering@MorningStar.Com for the fix
++ * eay 93/06/29
++ * Another problem, I was comparing only the first 4
++ * bytes, 97/03/18 */
++ if (memcmp(weak_keys[i],key,sizeof(des_cblock)) == 0) return(1);
++ return(0);
++ }
++
++/* NOW DEFINED IN des_local.h
++ * See ecb_encrypt.c for a pseudo description of these macros.
++ * #define PERM_OP(a,b,t,n,m) ((t)=((((a)>>(n))^(b))&(m)),\
++ * (b)^=(t),\
++ * (a)=((a)^((t)<<(n))))
++ */
++
++#define HPERM_OP(a,t,n,m) ((t)=((((a)<<(16-(n)))^(a))&(m)),\
++ (a)=(a)^(t)^(t>>(16-(n))))
++
++/* return 0 if key parity is odd (correct),
++ * return -1 if key parity error,
++ * return -2 if illegal weak key.
++ */
++int des_set_key(key, schedule)
++des_cblock (*key);
++des_key_schedule schedule;
++ {
++ static int shifts2[16]={0,0,1,1,1,1,1,1,0,1,1,1,1,1,1,0};
++ register DES_LONG c,d,t,s,t2;
++ register unsigned char *in;
++ register DES_LONG *k;
++ register int i;
++
++ if (des_check_key)
++ {
++ if (!check_parity(key))
++ return(-1);
++
++ if (des_is_weak_key(key))
++ return(-2);
++ }
++
++ k=(DES_LONG *)schedule;
++ in=(unsigned char *)key;
++
++ c2l(in,c);
++ c2l(in,d);
++
++ /* do PC1 in 60 simple operations */
++/* PERM_OP(d,c,t,4,0x0f0f0f0fL);
++ HPERM_OP(c,t,-2, 0xcccc0000L);
++ HPERM_OP(c,t,-1, 0xaaaa0000L);
++ HPERM_OP(c,t, 8, 0x00ff0000L);
++ HPERM_OP(c,t,-1, 0xaaaa0000L);
++ HPERM_OP(d,t,-8, 0xff000000L);
++ HPERM_OP(d,t, 8, 0x00ff0000L);
++ HPERM_OP(d,t, 2, 0x33330000L);
++ d=((d&0x00aa00aaL)<<7L)|((d&0x55005500L)>>7L)|(d&0xaa55aa55L);
++ d=(d>>8)|((c&0xf0000000L)>>4);
++ c&=0x0fffffffL; */
++
++ /* I now do it in 47 simple operations :-)
++ * Thanks to John Fletcher (john_fletcher@lccmail.ocf.llnl.gov)
++ * for the inspiration. :-) */
++ PERM_OP (d,c,t,4,0x0f0f0f0fL);
++ HPERM_OP(c,t,-2,0xcccc0000L);
++ HPERM_OP(d,t,-2,0xcccc0000L);
++ PERM_OP (d,c,t,1,0x55555555L);
++ PERM_OP (c,d,t,8,0x00ff00ffL);
++ PERM_OP (d,c,t,1,0x55555555L);
++ d= (((d&0x000000ffL)<<16L)| (d&0x0000ff00L) |
++ ((d&0x00ff0000L)>>16L)|((c&0xf0000000L)>>4L));
++ c&=0x0fffffffL;
++
++ for (i=0; i<ITERATIONS; i++)
++ {
++ if (shifts2[i])
++ { c=((c>>2L)|(c<<26L)); d=((d>>2L)|(d<<26L)); }
++ else
++ { c=((c>>1L)|(c<<27L)); d=((d>>1L)|(d<<27L)); }
++ c&=0x0fffffffL;
++ d&=0x0fffffffL;
++ /* could be a few less shifts but I am to lazy at this
++ * point in time to investigate */
++ s= des_skb[0][ (c )&0x3f ]|
++ des_skb[1][((c>> 6)&0x03)|((c>> 7L)&0x3c)]|
++ des_skb[2][((c>>13)&0x0f)|((c>>14L)&0x30)]|
++ des_skb[3][((c>>20)&0x01)|((c>>21L)&0x06) |
++ ((c>>22L)&0x38)];
++ t= des_skb[4][ (d )&0x3f ]|
++ des_skb[5][((d>> 7L)&0x03)|((d>> 8L)&0x3c)]|
++ des_skb[6][ (d>>15L)&0x3f ]|
++ des_skb[7][((d>>21L)&0x0f)|((d>>22L)&0x30)];
++
++ /* table contained 0213 4657 */
++ t2=((t<<16L)|(s&0x0000ffffL))&0xffffffffL;
++ *(k++)=ROTATE(t2,30)&0xffffffffL;
++
++ t2=((s>>16L)|(t&0xffff0000L));
++ *(k++)=ROTATE(t2,26)&0xffffffffL;
++ }
++ return(0);
++ }
++
++int des_key_sched(key, schedule)
++des_cblock (*key);
++des_key_schedule schedule;
++ {
++ return(des_set_key(key,schedule));
++ }
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/crypto/ciphers/des/sk.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,204 @@
++/* crypto/des/sk.h */
++/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com)
++ * All rights reserved.
++ *
++ * This package is an SSL implementation written
++ * by Eric Young (eay@cryptsoft.com).
++ * The implementation was written so as to conform with Netscapes SSL.
++ *
++ * This library is free for commercial and non-commercial use as long as
++ * the following conditions are aheared to. The following conditions
++ * apply to all code found in this distribution, be it the RC4, RSA,
++ * lhash, DES, etc., code; not just the SSL code. The SSL documentation
++ * included with this distribution is covered by the same copyright terms
++ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
++ *
++ * Copyright remains Eric Young's, and as such any Copyright notices in
++ * the code are not to be removed.
++ * If this package is used in a product, Eric Young should be given attribution
++ * as the author of the parts of the library used.
++ * This can be in the form of a textual message at program startup or
++ * in documentation (online or textual) provided with the package.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ * 1. Redistributions of source code must retain the copyright
++ * notice, this list of conditions and the following disclaimer.
++ * 2. Redistributions in binary form must reproduce the above copyright
++ * notice, this list of conditions and the following disclaimer in the
++ * documentation and/or other materials provided with the distribution.
++ * 3. All advertising materials mentioning features or use of this software
++ * must display the following acknowledgement:
++ * "This product includes cryptographic software written by
++ * Eric Young (eay@cryptsoft.com)"
++ * The word 'cryptographic' can be left out if the rouines from the library
++ * being used are not cryptographic related :-).
++ * 4. If you include any Windows specific code (or a derivative thereof) from
++ * the apps directory (application code) you must include an acknowledgement:
++ * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
++ *
++ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ *
++ * The licence and distribution terms for any publically available version or
++ * derivative of this code cannot be changed. i.e. this code cannot simply be
++ * copied and put under another distribution licence
++ * [including the GNU Public Licence.]
++ */
++
++static const DES_LONG des_skb[8][64]={
++{
++/* for C bits (numbered as per FIPS 46) 1 2 3 4 5 6 */
++0x00000000L,0x00000010L,0x20000000L,0x20000010L,
++0x00010000L,0x00010010L,0x20010000L,0x20010010L,
++0x00000800L,0x00000810L,0x20000800L,0x20000810L,
++0x00010800L,0x00010810L,0x20010800L,0x20010810L,
++0x00000020L,0x00000030L,0x20000020L,0x20000030L,
++0x00010020L,0x00010030L,0x20010020L,0x20010030L,
++0x00000820L,0x00000830L,0x20000820L,0x20000830L,
++0x00010820L,0x00010830L,0x20010820L,0x20010830L,
++0x00080000L,0x00080010L,0x20080000L,0x20080010L,
++0x00090000L,0x00090010L,0x20090000L,0x20090010L,
++0x00080800L,0x00080810L,0x20080800L,0x20080810L,
++0x00090800L,0x00090810L,0x20090800L,0x20090810L,
++0x00080020L,0x00080030L,0x20080020L,0x20080030L,
++0x00090020L,0x00090030L,0x20090020L,0x20090030L,
++0x00080820L,0x00080830L,0x20080820L,0x20080830L,
++0x00090820L,0x00090830L,0x20090820L,0x20090830L,
++},{
++/* for C bits (numbered as per FIPS 46) 7 8 10 11 12 13 */
++0x00000000L,0x02000000L,0x00002000L,0x02002000L,
++0x00200000L,0x02200000L,0x00202000L,0x02202000L,
++0x00000004L,0x02000004L,0x00002004L,0x02002004L,
++0x00200004L,0x02200004L,0x00202004L,0x02202004L,
++0x00000400L,0x02000400L,0x00002400L,0x02002400L,
++0x00200400L,0x02200400L,0x00202400L,0x02202400L,
++0x00000404L,0x02000404L,0x00002404L,0x02002404L,
++0x00200404L,0x02200404L,0x00202404L,0x02202404L,
++0x10000000L,0x12000000L,0x10002000L,0x12002000L,
++0x10200000L,0x12200000L,0x10202000L,0x12202000L,
++0x10000004L,0x12000004L,0x10002004L,0x12002004L,
++0x10200004L,0x12200004L,0x10202004L,0x12202004L,
++0x10000400L,0x12000400L,0x10002400L,0x12002400L,
++0x10200400L,0x12200400L,0x10202400L,0x12202400L,
++0x10000404L,0x12000404L,0x10002404L,0x12002404L,
++0x10200404L,0x12200404L,0x10202404L,0x12202404L,
++},{
++/* for C bits (numbered as per FIPS 46) 14 15 16 17 19 20 */
++0x00000000L,0x00000001L,0x00040000L,0x00040001L,
++0x01000000L,0x01000001L,0x01040000L,0x01040001L,
++0x00000002L,0x00000003L,0x00040002L,0x00040003L,
++0x01000002L,0x01000003L,0x01040002L,0x01040003L,
++0x00000200L,0x00000201L,0x00040200L,0x00040201L,
++0x01000200L,0x01000201L,0x01040200L,0x01040201L,
++0x00000202L,0x00000203L,0x00040202L,0x00040203L,
++0x01000202L,0x01000203L,0x01040202L,0x01040203L,
++0x08000000L,0x08000001L,0x08040000L,0x08040001L,
++0x09000000L,0x09000001L,0x09040000L,0x09040001L,
++0x08000002L,0x08000003L,0x08040002L,0x08040003L,
++0x09000002L,0x09000003L,0x09040002L,0x09040003L,
++0x08000200L,0x08000201L,0x08040200L,0x08040201L,
++0x09000200L,0x09000201L,0x09040200L,0x09040201L,
++0x08000202L,0x08000203L,0x08040202L,0x08040203L,
++0x09000202L,0x09000203L,0x09040202L,0x09040203L,
++},{
++/* for C bits (numbered as per FIPS 46) 21 23 24 26 27 28 */
++0x00000000L,0x00100000L,0x00000100L,0x00100100L,
++0x00000008L,0x00100008L,0x00000108L,0x00100108L,
++0x00001000L,0x00101000L,0x00001100L,0x00101100L,
++0x00001008L,0x00101008L,0x00001108L,0x00101108L,
++0x04000000L,0x04100000L,0x04000100L,0x04100100L,
++0x04000008L,0x04100008L,0x04000108L,0x04100108L,
++0x04001000L,0x04101000L,0x04001100L,0x04101100L,
++0x04001008L,0x04101008L,0x04001108L,0x04101108L,
++0x00020000L,0x00120000L,0x00020100L,0x00120100L,
++0x00020008L,0x00120008L,0x00020108L,0x00120108L,
++0x00021000L,0x00121000L,0x00021100L,0x00121100L,
++0x00021008L,0x00121008L,0x00021108L,0x00121108L,
++0x04020000L,0x04120000L,0x04020100L,0x04120100L,
++0x04020008L,0x04120008L,0x04020108L,0x04120108L,
++0x04021000L,0x04121000L,0x04021100L,0x04121100L,
++0x04021008L,0x04121008L,0x04021108L,0x04121108L,
++},{
++/* for D bits (numbered as per FIPS 46) 1 2 3 4 5 6 */
++0x00000000L,0x10000000L,0x00010000L,0x10010000L,
++0x00000004L,0x10000004L,0x00010004L,0x10010004L,
++0x20000000L,0x30000000L,0x20010000L,0x30010000L,
++0x20000004L,0x30000004L,0x20010004L,0x30010004L,
++0x00100000L,0x10100000L,0x00110000L,0x10110000L,
++0x00100004L,0x10100004L,0x00110004L,0x10110004L,
++0x20100000L,0x30100000L,0x20110000L,0x30110000L,
++0x20100004L,0x30100004L,0x20110004L,0x30110004L,
++0x00001000L,0x10001000L,0x00011000L,0x10011000L,
++0x00001004L,0x10001004L,0x00011004L,0x10011004L,
++0x20001000L,0x30001000L,0x20011000L,0x30011000L,
++0x20001004L,0x30001004L,0x20011004L,0x30011004L,
++0x00101000L,0x10101000L,0x00111000L,0x10111000L,
++0x00101004L,0x10101004L,0x00111004L,0x10111004L,
++0x20101000L,0x30101000L,0x20111000L,0x30111000L,
++0x20101004L,0x30101004L,0x20111004L,0x30111004L,
++},{
++/* for D bits (numbered as per FIPS 46) 8 9 11 12 13 14 */
++0x00000000L,0x08000000L,0x00000008L,0x08000008L,
++0x00000400L,0x08000400L,0x00000408L,0x08000408L,
++0x00020000L,0x08020000L,0x00020008L,0x08020008L,
++0x00020400L,0x08020400L,0x00020408L,0x08020408L,
++0x00000001L,0x08000001L,0x00000009L,0x08000009L,
++0x00000401L,0x08000401L,0x00000409L,0x08000409L,
++0x00020001L,0x08020001L,0x00020009L,0x08020009L,
++0x00020401L,0x08020401L,0x00020409L,0x08020409L,
++0x02000000L,0x0A000000L,0x02000008L,0x0A000008L,
++0x02000400L,0x0A000400L,0x02000408L,0x0A000408L,
++0x02020000L,0x0A020000L,0x02020008L,0x0A020008L,
++0x02020400L,0x0A020400L,0x02020408L,0x0A020408L,
++0x02000001L,0x0A000001L,0x02000009L,0x0A000009L,
++0x02000401L,0x0A000401L,0x02000409L,0x0A000409L,
++0x02020001L,0x0A020001L,0x02020009L,0x0A020009L,
++0x02020401L,0x0A020401L,0x02020409L,0x0A020409L,
++},{
++/* for D bits (numbered as per FIPS 46) 16 17 18 19 20 21 */
++0x00000000L,0x00000100L,0x00080000L,0x00080100L,
++0x01000000L,0x01000100L,0x01080000L,0x01080100L,
++0x00000010L,0x00000110L,0x00080010L,0x00080110L,
++0x01000010L,0x01000110L,0x01080010L,0x01080110L,
++0x00200000L,0x00200100L,0x00280000L,0x00280100L,
++0x01200000L,0x01200100L,0x01280000L,0x01280100L,
++0x00200010L,0x00200110L,0x00280010L,0x00280110L,
++0x01200010L,0x01200110L,0x01280010L,0x01280110L,
++0x00000200L,0x00000300L,0x00080200L,0x00080300L,
++0x01000200L,0x01000300L,0x01080200L,0x01080300L,
++0x00000210L,0x00000310L,0x00080210L,0x00080310L,
++0x01000210L,0x01000310L,0x01080210L,0x01080310L,
++0x00200200L,0x00200300L,0x00280200L,0x00280300L,
++0x01200200L,0x01200300L,0x01280200L,0x01280300L,
++0x00200210L,0x00200310L,0x00280210L,0x00280310L,
++0x01200210L,0x01200310L,0x01280210L,0x01280310L,
++},{
++/* for D bits (numbered as per FIPS 46) 22 23 24 25 27 28 */
++0x00000000L,0x04000000L,0x00040000L,0x04040000L,
++0x00000002L,0x04000002L,0x00040002L,0x04040002L,
++0x00002000L,0x04002000L,0x00042000L,0x04042000L,
++0x00002002L,0x04002002L,0x00042002L,0x04042002L,
++0x00000020L,0x04000020L,0x00040020L,0x04040020L,
++0x00000022L,0x04000022L,0x00040022L,0x04040022L,
++0x00002020L,0x04002020L,0x00042020L,0x04042020L,
++0x00002022L,0x04002022L,0x00042022L,0x04042022L,
++0x00000800L,0x04000800L,0x00040800L,0x04040800L,
++0x00000802L,0x04000802L,0x00040802L,0x04040802L,
++0x00002800L,0x04002800L,0x00042800L,0x04042800L,
++0x00002802L,0x04002802L,0x00042802L,0x04042802L,
++0x00000820L,0x04000820L,0x00040820L,0x04040820L,
++0x00000822L,0x04000822L,0x00040822L,0x04040822L,
++0x00002820L,0x04002820L,0x00042820L,0x04042820L,
++0x00002822L,0x04002822L,0x00042822L,0x04042822L,
++}};
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/crypto/ciphers/des/speed.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,329 @@
++/* crypto/des/speed.c */
++/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com)
++ * All rights reserved.
++ *
++ * This package is an SSL implementation written
++ * by Eric Young (eay@cryptsoft.com).
++ * The implementation was written so as to conform with Netscapes SSL.
++ *
++ * This library is free for commercial and non-commercial use as long as
++ * the following conditions are aheared to. The following conditions
++ * apply to all code found in this distribution, be it the RC4, RSA,
++ * lhash, DES, etc., code; not just the SSL code. The SSL documentation
++ * included with this distribution is covered by the same copyright terms
++ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
++ *
++ * Copyright remains Eric Young's, and as such any Copyright notices in
++ * the code are not to be removed.
++ * If this package is used in a product, Eric Young should be given attribution
++ * as the author of the parts of the library used.
++ * This can be in the form of a textual message at program startup or
++ * in documentation (online or textual) provided with the package.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ * 1. Redistributions of source code must retain the copyright
++ * notice, this list of conditions and the following disclaimer.
++ * 2. Redistributions in binary form must reproduce the above copyright
++ * notice, this list of conditions and the following disclaimer in the
++ * documentation and/or other materials provided with the distribution.
++ * 3. All advertising materials mentioning features or use of this software
++ * must display the following acknowledgement:
++ * "This product includes cryptographic software written by
++ * Eric Young (eay@cryptsoft.com)"
++ * The word 'cryptographic' can be left out if the rouines from the library
++ * being used are not cryptographic related :-).
++ * 4. If you include any Windows specific code (or a derivative thereof) from
++ * the apps directory (application code) you must include an acknowledgement:
++ * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
++ *
++ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ *
++ * The licence and distribution terms for any publically available version or
++ * derivative of this code cannot be changed. i.e. this code cannot simply be
++ * copied and put under another distribution licence
++ * [including the GNU Public Licence.]
++ */
++
++/* 11-Sep-92 Andrew Daviel Support for Silicon Graphics IRIX added */
++/* 06-Apr-92 Luke Brennan Support for VMS and add extra signal calls */
++
++#ifndef MSDOS
++#define TIMES
++#endif
++
++#include <stdio.h>
++#ifndef MSDOS
++#include <unistd.h>
++#else
++#include <io.h>
++extern int exit();
++#endif
++#include <signal.h>
++#ifndef VMS
++#ifndef _IRIX
++#include <time.h>
++#endif
++#ifdef TIMES
++#include <sys/types.h>
++#include <sys/times.h>
++#endif
++#else /* VMS */
++#include <types.h>
++struct tms {
++ time_t tms_utime;
++ time_t tms_stime;
++ time_t tms_uchild; /* I dunno... */
++ time_t tms_uchildsys; /* so these names are a guess :-) */
++ }
++#endif
++#ifndef TIMES
++#include <sys/timeb.h>
++#endif
++
++#ifdef sun
++#include <limits.h>
++#include <sys/param.h>
++#endif
++
++#include "des_locl.h"
++
++/* The following if from times(3) man page. It may need to be changed */
++#ifndef HZ
++# ifndef CLK_TCK
++# ifndef _BSD_CLK_TCK_ /* FreeBSD fix */
++# ifndef VMS
++# define HZ 100.0
++# else /* VMS */
++# define HZ 100.0
++# endif
++# else /* _BSD_CLK_TCK_ */
++# define HZ ((double)_BSD_CLK_TCK_)
++# endif
++# else /* CLK_TCK */
++# define HZ ((double)CLK_TCK)
++# endif
++#endif
++
++#define BUFSIZE ((long)1024)
++long run=0;
++
++#ifndef NOPROTO
++double Time_F(int s);
++#else
++double Time_F();
++#endif
++
++#ifdef SIGALRM
++#if defined(__STDC__) || defined(sgi) || defined(_AIX)
++#define SIGRETTYPE void
++#else
++#define SIGRETTYPE int
++#endif
++
++#ifndef NOPROTO
++SIGRETTYPE sig_done(int sig);
++#else
++SIGRETTYPE sig_done();
++#endif
++
++SIGRETTYPE sig_done(sig)
++int sig;
++ {
++ signal(SIGALRM,sig_done);
++ run=0;
++#ifdef LINT
++ sig=sig;
++#endif
++ }
++#endif
++
++#define START 0
++#define STOP 1
++
++double Time_F(s)
++int s;
++ {
++ double ret;
++#ifdef TIMES
++ static struct tms tstart,tend;
++
++ if (s == START)
++ {
++ times(&tstart);
++ return(0);
++ }
++ else
++ {
++ times(&tend);
++ ret=((double)(tend.tms_utime-tstart.tms_utime))/HZ;
++ return((ret == 0.0)?1e-6:ret);
++ }
++#else /* !times() */
++ static struct timeb tstart,tend;
++ long i;
++
++ if (s == START)
++ {
++ ftime(&tstart);
++ return(0);
++ }
++ else
++ {
++ ftime(&tend);
++ i=(long)tend.millitm-(long)tstart.millitm;
++ ret=((double)(tend.time-tstart.time))+((double)i)/1e3;
++ return((ret == 0.0)?1e-6:ret);
++ }
++#endif
++ }
++
++int main(argc,argv)
++int argc;
++char **argv;
++ {
++ long count;
++ static unsigned char buf[BUFSIZE];
++ static des_cblock key ={0x12,0x34,0x56,0x78,0x9a,0xbc,0xde,0xf0};
++ static des_cblock key2={0x34,0x56,0x78,0x9a,0xbc,0xde,0xf0,0x12};
++ static des_cblock key3={0x56,0x78,0x9a,0xbc,0xde,0xf0,0x12,0x34};
++ des_key_schedule sch,sch2,sch3;
++ double a,b,c,d,e;
++#ifndef SIGALRM
++ long ca,cb,cc,cd,ce;
++#endif
++
++#ifndef TIMES
++ printf("To get the most acurate results, try to run this\n");
++ printf("program when this computer is idle.\n");
++#endif
++
++ des_set_key((C_Block *)key2,sch2);
++ des_set_key((C_Block *)key3,sch3);
++
++#ifndef SIGALRM
++ printf("First we calculate the approximate speed ...\n");
++ des_set_key((C_Block *)key,sch);
++ count=10;
++ do {
++ long i;
++ DES_LONG data[2];
++
++ count*=2;
++ Time_F(START);
++ for (i=count; i; i--)
++ des_encrypt(data,&(sch[0]),DES_ENCRYPT);
++ d=Time_F(STOP);
++ } while (d < 3.0);
++ ca=count;
++ cb=count*3;
++ cc=count*3*8/BUFSIZE+1;
++ cd=count*8/BUFSIZE+1;
++ ce=count/20+1;
++ printf("Doing set_key %ld times\n",ca);
++#define COND(d) (count != (d))
++#define COUNT(d) (d)
++#else
++#define COND(c) (run)
++#define COUNT(d) (count)
++ signal(SIGALRM,sig_done);
++ printf("Doing set_key for 10 seconds\n");
++ alarm(10);
++#endif
++
++ Time_F(START);
++ for (count=0,run=1; COND(ca); count++)
++ des_set_key((C_Block *)key,sch);
++ d=Time_F(STOP);
++ printf("%ld set_key's in %.2f seconds\n",count,d);
++ a=((double)COUNT(ca))/d;
++
++#ifdef SIGALRM
++ printf("Doing des_encrypt's for 10 seconds\n");
++ alarm(10);
++#else
++ printf("Doing des_encrypt %ld times\n",cb);
++#endif
++ Time_F(START);
++ for (count=0,run=1; COND(cb); count++)
++ {
++ DES_LONG data[2];
++
++ des_encrypt(data,&(sch[0]),DES_ENCRYPT);
++ }
++ d=Time_F(STOP);
++ printf("%ld des_encrypt's in %.2f second\n",count,d);
++ b=((double)COUNT(cb)*8)/d;
++
++#ifdef SIGALRM
++ printf("Doing des_cbc_encrypt on %ld byte blocks for 10 seconds\n",
++ BUFSIZE);
++ alarm(10);
++#else
++ printf("Doing des_cbc_encrypt %ld times on %ld byte blocks\n",cc,
++ BUFSIZE);
++#endif
++ Time_F(START);
++ for (count=0,run=1; COND(cc); count++)
++ des_ncbc_encrypt((C_Block *)buf,(C_Block *)buf,BUFSIZE,&(sch[0]),
++ (C_Block *)&(key[0]),DES_ENCRYPT);
++ d=Time_F(STOP);
++ printf("%ld des_cbc_encrypt's of %ld byte blocks in %.2f second\n",
++ count,BUFSIZE,d);
++ c=((double)COUNT(cc)*BUFSIZE)/d;
++
++#ifdef SIGALRM
++ printf("Doing des_ede_cbc_encrypt on %ld byte blocks for 10 seconds\n",
++ BUFSIZE);
++ alarm(10);
++#else
++ printf("Doing des_ede_cbc_encrypt %ld times on %ld byte blocks\n",cd,
++ BUFSIZE);
++#endif
++ Time_F(START);
++ for (count=0,run=1; COND(cd); count++)
++ des_ede3_cbc_encrypt((C_Block *)buf,(C_Block *)buf,BUFSIZE,
++ &(sch[0]),
++ &(sch2[0]),
++ &(sch3[0]),
++ (C_Block *)&(key[0]),
++ DES_ENCRYPT);
++ d=Time_F(STOP);
++ printf("%ld des_ede_cbc_encrypt's of %ld byte blocks in %.2f second\n",
++ count,BUFSIZE,d);
++ d=((double)COUNT(cd)*BUFSIZE)/d;
++
++#ifdef SIGALRM
++ printf("Doing crypt for 10 seconds\n");
++ alarm(10);
++#else
++ printf("Doing crypt %ld times\n",ce);
++#endif
++ Time_F(START);
++ for (count=0,run=1; COND(ce); count++)
++ crypt("testing1","ef");
++ e=Time_F(STOP);
++ printf("%ld crypts in %.2f second\n",count,e);
++ e=((double)COUNT(ce))/e;
++
++ printf("set_key per sec = %12.2f (%9.3fuS)\n",a,1.0e6/a);
++ printf("DES raw ecb bytes per sec = %12.2f (%9.3fuS)\n",b,8.0e6/b);
++ printf("DES cbc bytes per sec = %12.2f (%9.3fuS)\n",c,8.0e6/c);
++ printf("DES ede cbc bytes per sec = %12.2f (%9.3fuS)\n",d,8.0e6/d);
++ printf("crypt per sec = %12.2f (%9.3fuS)\n",e,1.0e6/e);
++ exit(0);
++#if defined(LINT) || defined(MSDOS)
++ return(0);
++#endif
++ }
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/crypto/ciphers/des/spr.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,204 @@
++/* crypto/des/spr.h */
++/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com)
++ * All rights reserved.
++ *
++ * This package is an SSL implementation written
++ * by Eric Young (eay@cryptsoft.com).
++ * The implementation was written so as to conform with Netscapes SSL.
++ *
++ * This library is free for commercial and non-commercial use as long as
++ * the following conditions are aheared to. The following conditions
++ * apply to all code found in this distribution, be it the RC4, RSA,
++ * lhash, DES, etc., code; not just the SSL code. The SSL documentation
++ * included with this distribution is covered by the same copyright terms
++ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
++ *
++ * Copyright remains Eric Young's, and as such any Copyright notices in
++ * the code are not to be removed.
++ * If this package is used in a product, Eric Young should be given attribution
++ * as the author of the parts of the library used.
++ * This can be in the form of a textual message at program startup or
++ * in documentation (online or textual) provided with the package.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ * 1. Redistributions of source code must retain the copyright
++ * notice, this list of conditions and the following disclaimer.
++ * 2. Redistributions in binary form must reproduce the above copyright
++ * notice, this list of conditions and the following disclaimer in the
++ * documentation and/or other materials provided with the distribution.
++ * 3. All advertising materials mentioning features or use of this software
++ * must display the following acknowledgement:
++ * "This product includes cryptographic software written by
++ * Eric Young (eay@cryptsoft.com)"
++ * The word 'cryptographic' can be left out if the rouines from the library
++ * being used are not cryptographic related :-).
++ * 4. If you include any Windows specific code (or a derivative thereof) from
++ * the apps directory (application code) you must include an acknowledgement:
++ * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
++ *
++ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ *
++ * The licence and distribution terms for any publically available version or
++ * derivative of this code cannot be changed. i.e. this code cannot simply be
++ * copied and put under another distribution licence
++ * [including the GNU Public Licence.]
++ */
++
++const DES_LONG des_SPtrans[8][64]={
++{
++/* nibble 0 */
++0x02080800L, 0x00080000L, 0x02000002L, 0x02080802L,
++0x02000000L, 0x00080802L, 0x00080002L, 0x02000002L,
++0x00080802L, 0x02080800L, 0x02080000L, 0x00000802L,
++0x02000802L, 0x02000000L, 0x00000000L, 0x00080002L,
++0x00080000L, 0x00000002L, 0x02000800L, 0x00080800L,
++0x02080802L, 0x02080000L, 0x00000802L, 0x02000800L,
++0x00000002L, 0x00000800L, 0x00080800L, 0x02080002L,
++0x00000800L, 0x02000802L, 0x02080002L, 0x00000000L,
++0x00000000L, 0x02080802L, 0x02000800L, 0x00080002L,
++0x02080800L, 0x00080000L, 0x00000802L, 0x02000800L,
++0x02080002L, 0x00000800L, 0x00080800L, 0x02000002L,
++0x00080802L, 0x00000002L, 0x02000002L, 0x02080000L,
++0x02080802L, 0x00080800L, 0x02080000L, 0x02000802L,
++0x02000000L, 0x00000802L, 0x00080002L, 0x00000000L,
++0x00080000L, 0x02000000L, 0x02000802L, 0x02080800L,
++0x00000002L, 0x02080002L, 0x00000800L, 0x00080802L,
++},{
++/* nibble 1 */
++0x40108010L, 0x00000000L, 0x00108000L, 0x40100000L,
++0x40000010L, 0x00008010L, 0x40008000L, 0x00108000L,
++0x00008000L, 0x40100010L, 0x00000010L, 0x40008000L,
++0x00100010L, 0x40108000L, 0x40100000L, 0x00000010L,
++0x00100000L, 0x40008010L, 0x40100010L, 0x00008000L,
++0x00108010L, 0x40000000L, 0x00000000L, 0x00100010L,
++0x40008010L, 0x00108010L, 0x40108000L, 0x40000010L,
++0x40000000L, 0x00100000L, 0x00008010L, 0x40108010L,
++0x00100010L, 0x40108000L, 0x40008000L, 0x00108010L,
++0x40108010L, 0x00100010L, 0x40000010L, 0x00000000L,
++0x40000000L, 0x00008010L, 0x00100000L, 0x40100010L,
++0x00008000L, 0x40000000L, 0x00108010L, 0x40008010L,
++0x40108000L, 0x00008000L, 0x00000000L, 0x40000010L,
++0x00000010L, 0x40108010L, 0x00108000L, 0x40100000L,
++0x40100010L, 0x00100000L, 0x00008010L, 0x40008000L,
++0x40008010L, 0x00000010L, 0x40100000L, 0x00108000L,
++},{
++/* nibble 2 */
++0x04000001L, 0x04040100L, 0x00000100L, 0x04000101L,
++0x00040001L, 0x04000000L, 0x04000101L, 0x00040100L,
++0x04000100L, 0x00040000L, 0x04040000L, 0x00000001L,
++0x04040101L, 0x00000101L, 0x00000001L, 0x04040001L,
++0x00000000L, 0x00040001L, 0x04040100L, 0x00000100L,
++0x00000101L, 0x04040101L, 0x00040000L, 0x04000001L,
++0x04040001L, 0x04000100L, 0x00040101L, 0x04040000L,
++0x00040100L, 0x00000000L, 0x04000000L, 0x00040101L,
++0x04040100L, 0x00000100L, 0x00000001L, 0x00040000L,
++0x00000101L, 0x00040001L, 0x04040000L, 0x04000101L,
++0x00000000L, 0x04040100L, 0x00040100L, 0x04040001L,
++0x00040001L, 0x04000000L, 0x04040101L, 0x00000001L,
++0x00040101L, 0x04000001L, 0x04000000L, 0x04040101L,
++0x00040000L, 0x04000100L, 0x04000101L, 0x00040100L,
++0x04000100L, 0x00000000L, 0x04040001L, 0x00000101L,
++0x04000001L, 0x00040101L, 0x00000100L, 0x04040000L,
++},{
++/* nibble 3 */
++0x00401008L, 0x10001000L, 0x00000008L, 0x10401008L,
++0x00000000L, 0x10400000L, 0x10001008L, 0x00400008L,
++0x10401000L, 0x10000008L, 0x10000000L, 0x00001008L,
++0x10000008L, 0x00401008L, 0x00400000L, 0x10000000L,
++0x10400008L, 0x00401000L, 0x00001000L, 0x00000008L,
++0x00401000L, 0x10001008L, 0x10400000L, 0x00001000L,
++0x00001008L, 0x00000000L, 0x00400008L, 0x10401000L,
++0x10001000L, 0x10400008L, 0x10401008L, 0x00400000L,
++0x10400008L, 0x00001008L, 0x00400000L, 0x10000008L,
++0x00401000L, 0x10001000L, 0x00000008L, 0x10400000L,
++0x10001008L, 0x00000000L, 0x00001000L, 0x00400008L,
++0x00000000L, 0x10400008L, 0x10401000L, 0x00001000L,
++0x10000000L, 0x10401008L, 0x00401008L, 0x00400000L,
++0x10401008L, 0x00000008L, 0x10001000L, 0x00401008L,
++0x00400008L, 0x00401000L, 0x10400000L, 0x10001008L,
++0x00001008L, 0x10000000L, 0x10000008L, 0x10401000L,
++},{
++/* nibble 4 */
++0x08000000L, 0x00010000L, 0x00000400L, 0x08010420L,
++0x08010020L, 0x08000400L, 0x00010420L, 0x08010000L,
++0x00010000L, 0x00000020L, 0x08000020L, 0x00010400L,
++0x08000420L, 0x08010020L, 0x08010400L, 0x00000000L,
++0x00010400L, 0x08000000L, 0x00010020L, 0x00000420L,
++0x08000400L, 0x00010420L, 0x00000000L, 0x08000020L,
++0x00000020L, 0x08000420L, 0x08010420L, 0x00010020L,
++0x08010000L, 0x00000400L, 0x00000420L, 0x08010400L,
++0x08010400L, 0x08000420L, 0x00010020L, 0x08010000L,
++0x00010000L, 0x00000020L, 0x08000020L, 0x08000400L,
++0x08000000L, 0x00010400L, 0x08010420L, 0x00000000L,
++0x00010420L, 0x08000000L, 0x00000400L, 0x00010020L,
++0x08000420L, 0x00000400L, 0x00000000L, 0x08010420L,
++0x08010020L, 0x08010400L, 0x00000420L, 0x00010000L,
++0x00010400L, 0x08010020L, 0x08000400L, 0x00000420L,
++0x00000020L, 0x00010420L, 0x08010000L, 0x08000020L,
++},{
++/* nibble 5 */
++0x80000040L, 0x00200040L, 0x00000000L, 0x80202000L,
++0x00200040L, 0x00002000L, 0x80002040L, 0x00200000L,
++0x00002040L, 0x80202040L, 0x00202000L, 0x80000000L,
++0x80002000L, 0x80000040L, 0x80200000L, 0x00202040L,
++0x00200000L, 0x80002040L, 0x80200040L, 0x00000000L,
++0x00002000L, 0x00000040L, 0x80202000L, 0x80200040L,
++0x80202040L, 0x80200000L, 0x80000000L, 0x00002040L,
++0x00000040L, 0x00202000L, 0x00202040L, 0x80002000L,
++0x00002040L, 0x80000000L, 0x80002000L, 0x00202040L,
++0x80202000L, 0x00200040L, 0x00000000L, 0x80002000L,
++0x80000000L, 0x00002000L, 0x80200040L, 0x00200000L,
++0x00200040L, 0x80202040L, 0x00202000L, 0x00000040L,
++0x80202040L, 0x00202000L, 0x00200000L, 0x80002040L,
++0x80000040L, 0x80200000L, 0x00202040L, 0x00000000L,
++0x00002000L, 0x80000040L, 0x80002040L, 0x80202000L,
++0x80200000L, 0x00002040L, 0x00000040L, 0x80200040L,
++},{
++/* nibble 6 */
++0x00004000L, 0x00000200L, 0x01000200L, 0x01000004L,
++0x01004204L, 0x00004004L, 0x00004200L, 0x00000000L,
++0x01000000L, 0x01000204L, 0x00000204L, 0x01004000L,
++0x00000004L, 0x01004200L, 0x01004000L, 0x00000204L,
++0x01000204L, 0x00004000L, 0x00004004L, 0x01004204L,
++0x00000000L, 0x01000200L, 0x01000004L, 0x00004200L,
++0x01004004L, 0x00004204L, 0x01004200L, 0x00000004L,
++0x00004204L, 0x01004004L, 0x00000200L, 0x01000000L,
++0x00004204L, 0x01004000L, 0x01004004L, 0x00000204L,
++0x00004000L, 0x00000200L, 0x01000000L, 0x01004004L,
++0x01000204L, 0x00004204L, 0x00004200L, 0x00000000L,
++0x00000200L, 0x01000004L, 0x00000004L, 0x01000200L,
++0x00000000L, 0x01000204L, 0x01000200L, 0x00004200L,
++0x00000204L, 0x00004000L, 0x01004204L, 0x01000000L,
++0x01004200L, 0x00000004L, 0x00004004L, 0x01004204L,
++0x01000004L, 0x01004200L, 0x01004000L, 0x00004004L,
++},{
++/* nibble 7 */
++0x20800080L, 0x20820000L, 0x00020080L, 0x00000000L,
++0x20020000L, 0x00800080L, 0x20800000L, 0x20820080L,
++0x00000080L, 0x20000000L, 0x00820000L, 0x00020080L,
++0x00820080L, 0x20020080L, 0x20000080L, 0x20800000L,
++0x00020000L, 0x00820080L, 0x00800080L, 0x20020000L,
++0x20820080L, 0x20000080L, 0x00000000L, 0x00820000L,
++0x20000000L, 0x00800000L, 0x20020080L, 0x20800080L,
++0x00800000L, 0x00020000L, 0x20820000L, 0x00000080L,
++0x00800000L, 0x00020000L, 0x20000080L, 0x20820080L,
++0x00020080L, 0x20000000L, 0x00000000L, 0x00820000L,
++0x20800080L, 0x20020080L, 0x20020000L, 0x00800080L,
++0x20820000L, 0x00000080L, 0x00800080L, 0x20020000L,
++0x20820080L, 0x00800000L, 0x20800000L, 0x20000080L,
++0x00820000L, 0x00020080L, 0x20020080L, 0x20800000L,
++0x00000080L, 0x20820000L, 0x00820080L, 0x00000000L,
++0x20000000L, 0x20800080L, 0x00020000L, 0x00820080L,
++}};
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/include/crypto/aes.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,97 @@
++// I retain copyright in this code but I encourage its free use provided
++// that I don't carry any responsibility for the results. I am especially
++// happy to see it used in free and open source software. If you do use
++// it I would appreciate an acknowledgement of its origin in the code or
++// the product that results and I would also appreciate knowing a little
++// about the use to which it is being put. I am grateful to Frank Yellin
++// for some ideas that are used in this implementation.
++//
++// Dr B. R. Gladman <brg@gladman.uk.net> 6th April 2001.
++//
++// This is an implementation of the AES encryption algorithm (Rijndael)
++// designed by Joan Daemen and Vincent Rijmen. This version is designed
++// to provide both fixed and dynamic block and key lengths and can also
++// run with either big or little endian internal byte order (see aes.h).
++// It inputs block and key lengths in bytes with the legal values being
++// 16, 24 and 32.
++
++/*
++ * Modified by Jari Ruusu, May 1 2001
++ * - Fixed some compile warnings, code was ok but gcc warned anyway.
++ * - Changed basic types: byte -> unsigned char, word -> u_int32_t
++ * - Major name space cleanup: Names visible to outside now begin
++ * with "aes_" or "AES_". A lot of stuff moved from aes.h to aes.c
++ * - Removed C++ and DLL support as part of name space cleanup.
++ * - Eliminated unnecessary recomputation of tables. (actual bug fix)
++ * - Merged precomputed constant tables to aes.c file.
++ * - Removed data alignment restrictions for portability reasons.
++ * - Made block and key lengths accept bit count (128/192/256)
++ * as well byte count (16/24/32).
++ * - Removed all error checks. This change also eliminated the need
++ * to preinitialize the context struct to zero.
++ * - Removed some totally unused constants.
++ */
++
++#ifndef _AES_H
++#define _AES_H
++
++#if defined(__linux__) && defined(__KERNEL__)
++# include <linux/types.h>
++#else
++# include <sys/types.h>
++#endif
++
++// CONFIGURATION OPTIONS (see also aes.c)
++//
++// Define AES_BLOCK_SIZE to set the cipher block size (16, 24 or 32) or
++// leave this undefined for dynamically variable block size (this will
++// result in much slower code).
++// IMPORTANT NOTE: AES_BLOCK_SIZE is in BYTES (16, 24, 32 or undefined). If
++// left undefined a slower version providing variable block length is compiled
++
++#define AES_BLOCK_SIZE 16
++
++// The number of key schedule words for different block and key lengths
++// allowing for method of computation which requires the length to be a
++// multiple of the key length
++//
++// Nk = 4 6 8
++// -------------
++// Nb = 4 | 60 60 64
++// 6 | 96 90 96
++// 8 | 120 120 120
++
++#if !defined(AES_BLOCK_SIZE) || (AES_BLOCK_SIZE == 32)
++#define AES_KS_LENGTH 120
++#define AES_RC_LENGTH 29
++#else
++#define AES_KS_LENGTH 4 * AES_BLOCK_SIZE
++#define AES_RC_LENGTH (9 * AES_BLOCK_SIZE) / 8 - 8
++#endif
++
++typedef struct
++{
++ u_int32_t aes_Nkey; // the number of words in the key input block
++ u_int32_t aes_Nrnd; // the number of cipher rounds
++ u_int32_t aes_e_key[AES_KS_LENGTH]; // the encryption key schedule
++ u_int32_t aes_d_key[AES_KS_LENGTH]; // the decryption key schedule
++#if !defined(AES_BLOCK_SIZE)
++ u_int32_t aes_Ncol; // the number of columns in the cipher state
++#endif
++} aes_context;
++
++// THE CIPHER INTERFACE
++
++#if !defined(AES_BLOCK_SIZE)
++extern void aes_set_blk(aes_context *, const int);
++#endif
++extern void aes_set_key(aes_context *, const unsigned char [], const int, const int);
++extern void aes_encrypt(const aes_context *, const unsigned char [], unsigned char []);
++extern void aes_decrypt(const aes_context *, const unsigned char [], unsigned char []);
++
++// The block length inputs to aes_set_block and aes_set_key are in numbers
++// of bytes or bits. The calls to subroutines must be made in the above
++// order but multiple calls can be made without repeating earlier calls
++// if their parameters have not changed.
++
++#endif // _AES_H
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/include/crypto/aes_cbc.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,4 @@
++/* Glue header */
++#include "aes.h"
++int AES_set_key(aes_context *aes_ctx, const u_int8_t * key, int keysize);
++int AES_cbc_encrypt(aes_context *ctx, const u_int8_t * in, u_int8_t * out, int ilen, const u_int8_t * iv, int encrypt);
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/include/crypto/aes_xcbc_mac.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,12 @@
++#ifndef _AES_XCBC_MAC_H
++#define _AES_XCBC_MAC_H
++
++typedef u_int32_t aes_block[4];
++typedef struct {
++ aes_context ctx_k1;
++ aes_block k2;
++ aes_block k3;
++} aes_context_mac;
++int AES_xcbc_mac_set_key(aes_context_mac *ctxm, const u_int8_t *key, int keylen);
++int AES_xcbc_mac_hash(const aes_context_mac *ctxm, const u_int8_t * in, int ilen, u_int8_t hash[16]);
++#endif /* _AES_XCBC_MAC_H */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/include/crypto/cbc_generic.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,110 @@
++#ifndef _CBC_GENERIC_H
++#define _CBC_GENERIC_H
++/*
++ * CBC macro helpers
++ *
++ * Author: JuanJo Ciarlante <jjo-ipsec@mendoza.gov.ar>
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ */
++
++/*
++ * Heavily inspired in loop_AES
++ */
++#define CBC_IMPL_BLK16(name, ctx_type, addr_type, enc_func, dec_func) \
++int name(ctx_type *ctx, const u_int8_t * in, u_int8_t * out, int ilen, const u_int8_t * iv, int encrypt) { \
++ int ret=ilen, pos; \
++ const u_int32_t *iv_i; \
++ if ((ilen) % 16) return 0; \
++ if (encrypt) { \
++ pos=0; \
++ while(pos<ilen) { \
++ if (pos==0) \
++ iv_i=(const u_int32_t*) iv; \
++ else \
++ iv_i=(const u_int32_t*) (out-16); \
++ *((u_int32_t *)(&out[ 0])) = iv_i[0]^*((const u_int32_t *)(&in[ 0])); \
++ *((u_int32_t *)(&out[ 4])) = iv_i[1]^*((const u_int32_t *)(&in[ 4])); \
++ *((u_int32_t *)(&out[ 8])) = iv_i[2]^*((const u_int32_t *)(&in[ 8])); \
++ *((u_int32_t *)(&out[12])) = iv_i[3]^*((const u_int32_t *)(&in[12])); \
++ enc_func(ctx, (addr_type) out, (addr_type) out); \
++ in+=16; \
++ out+=16; \
++ pos+=16; \
++ } \
++ } else { \
++ pos=ilen-16; \
++ in+=pos; \
++ out+=pos; \
++ while(pos>=0) { \
++ dec_func(ctx, (const addr_type) in, (addr_type) out); \
++ if (pos==0) \
++ iv_i=(const u_int32_t*) (iv); \
++ else \
++ iv_i=(const u_int32_t*) (in-16); \
++ *((u_int32_t *)(&out[ 0])) ^= iv_i[0]; \
++ *((u_int32_t *)(&out[ 4])) ^= iv_i[1]; \
++ *((u_int32_t *)(&out[ 8])) ^= iv_i[2]; \
++ *((u_int32_t *)(&out[12])) ^= iv_i[3]; \
++ in-=16; \
++ out-=16; \
++ pos-=16; \
++ } \
++ } \
++ return ret; \
++}
++#define CBC_IMPL_BLK8(name, ctx_type, addr_type, enc_func, dec_func) \
++int name(ctx_type *ctx, u_int8_t * in, u_int8_t * out, int ilen, const u_int8_t * iv, int encrypt) { \
++ int ret=ilen, pos; \
++ const u_int32_t *iv_i; \
++ if ((ilen) % 8) return 0; \
++ if (encrypt) { \
++ pos=0; \
++ while(pos<ilen) { \
++ if (pos==0) \
++ iv_i=(const u_int32_t*) iv; \
++ else \
++ iv_i=(const u_int32_t*) (out-8); \
++ *((u_int32_t *)(&out[ 0])) = iv_i[0]^*((const u_int32_t *)(&in[ 0])); \
++ *((u_int32_t *)(&out[ 4])) = iv_i[1]^*((const u_int32_t *)(&in[ 4])); \
++ enc_func(ctx, (addr_type)out, (addr_type)out); \
++ in+=8; \
++ out+=8; \
++ pos+=8; \
++ } \
++ } else { \
++ pos=ilen-8; \
++ in+=pos; \
++ out+=pos; \
++ while(pos>=0) { \
++ dec_func(ctx, (const addr_type)in, (addr_type)out); \
++ if (pos==0) \
++ iv_i=(const u_int32_t*) (iv); \
++ else \
++ iv_i=(const u_int32_t*) (in-8); \
++ *((u_int32_t *)(&out[ 0])) ^= iv_i[0]; \
++ *((u_int32_t *)(&out[ 4])) ^= iv_i[1]; \
++ in-=8; \
++ out-=8; \
++ pos-=8; \
++ } \
++ } \
++ return ret; \
++}
++#define CBC_DECL(name, ctx_type) \
++int name(ctx_type *ctx, u_int8_t * in, u_int8_t * out, int ilen, const u_int8_t * iv, int encrypt)
++/*
++Eg.:
++CBC_IMPL_BLK16(AES_cbc_encrypt, aes_context, u_int8_t *, aes_encrypt, aes_decrypt);
++CBC_DECL(AES_cbc_encrypt, aes_context);
++*/
++#endif /* _CBC_GENERIC_H */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/include/crypto/des.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,298 @@
++/* crypto/des/des.org */
++/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com)
++ * All rights reserved.
++ *
++ * This package is an SSL implementation written
++ * by Eric Young (eay@cryptsoft.com).
++ * The implementation was written so as to conform with Netscapes SSL.
++ *
++ * This library is free for commercial and non-commercial use as long as
++ * the following conditions are aheared to. The following conditions
++ * apply to all code found in this distribution, be it the RC4, RSA,
++ * lhash, DES, etc., code; not just the SSL code. The SSL documentation
++ * included with this distribution is covered by the same copyright terms
++ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
++ *
++ * Copyright remains Eric Young's, and as such any Copyright notices in
++ * the code are not to be removed.
++ * If this package is used in a product, Eric Young should be given attribution
++ * as the author of the parts of the library used.
++ * This can be in the form of a textual message at program startup or
++ * in documentation (online or textual) provided with the package.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ * 1. Redistributions of source code must retain the copyright
++ * notice, this list of conditions and the following disclaimer.
++ * 2. Redistributions in binary form must reproduce the above copyright
++ * notice, this list of conditions and the following disclaimer in the
++ * documentation and/or other materials provided with the distribution.
++ * 3. All advertising materials mentioning features or use of this software
++ * must display the following acknowledgement:
++ * "This product includes cryptographic software written by
++ * Eric Young (eay@cryptsoft.com)"
++ * The word 'cryptographic' can be left out if the rouines from the library
++ * being used are not cryptographic related :-).
++ * 4. If you include any Windows specific code (or a derivative thereof) from
++ * the apps directory (application code) you must include an acknowledgement:
++ * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
++ *
++ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ *
++ * The licence and distribution terms for any publically available version or
++ * derivative of this code cannot be changed. i.e. this code cannot simply be
++ * copied and put under another distribution licence
++ * [including the GNU Public Licence.]
++ */
++
++/* WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
++ *
++ * Always modify des.org since des.h is automatically generated from
++ * it during SSLeay configuration.
++ *
++ * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
++ */
++
++#ifndef HEADER_DES_H
++#define HEADER_DES_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++
++/* If this is set to 'unsigned int' on a DEC Alpha, this gives about a
++ * %20 speed up (longs are 8 bytes, int's are 4). */
++/* Must be unsigned int on ia64/Itanium or DES breaks badly */
++
++#ifdef __KERNEL__
++#include <linux/types.h>
++#else
++#include <sys/types.h>
++#endif
++
++#ifndef DES_LONG
++#define DES_LONG u_int32_t
++#endif
++
++typedef unsigned char des_cblock[8];
++typedef struct { des_cblock ks; } des_key_schedule[16];
++
++#define DES_KEY_SZ (sizeof(des_cblock))
++#define DES_SCHEDULE_SZ (sizeof(des_key_schedule))
++
++#define DES_ENCRYPT 1
++#define DES_DECRYPT 0
++
++#define DES_CBC_MODE 0
++#define DES_PCBC_MODE 1
++
++#define des_ecb2_encrypt(i,o,k1,k2,e) \
++ des_ecb3_encrypt((i),(o),(k1),(k2),(k1),(e))
++
++#define des_ede2_cbc_encrypt(i,o,l,k1,k2,iv,e) \
++ des_ede3_cbc_encrypt((i),(o),(l),(k1),(k2),(k1),(iv),(e))
++
++#define des_ede2_cfb64_encrypt(i,o,l,k1,k2,iv,n,e) \
++ des_ede3_cfb64_encrypt((i),(o),(l),(k1),(k2),(k1),(iv),(n),(e))
++
++#define des_ede2_ofb64_encrypt(i,o,l,k1,k2,iv,n) \
++ des_ede3_ofb64_encrypt((i),(o),(l),(k1),(k2),(k1),(iv),(n))
++
++#define C_Block des_cblock
++#define Key_schedule des_key_schedule
++#ifdef KERBEROS
++#define ENCRYPT DES_ENCRYPT
++#define DECRYPT DES_DECRYPT
++#endif
++#define KEY_SZ DES_KEY_SZ
++#define string_to_key des_string_to_key
++#define read_pw_string des_read_pw_string
++#define random_key des_random_key
++#define pcbc_encrypt des_pcbc_encrypt
++#define set_key des_set_key
++#define key_sched des_key_sched
++#define ecb_encrypt des_ecb_encrypt
++#define cbc_encrypt des_cbc_encrypt
++#define ncbc_encrypt des_ncbc_encrypt
++#define xcbc_encrypt des_xcbc_encrypt
++#define cbc_cksum des_cbc_cksum
++#define quad_cksum des_quad_cksum
++
++/* For compatibility with the MIT lib - eay 20/05/92 */
++typedef des_key_schedule bit_64;
++#define des_fixup_key_parity des_set_odd_parity
++#define des_check_key_parity check_parity
++
++extern int des_check_key; /* defaults to false */
++extern int des_rw_mode; /* defaults to DES_PCBC_MODE */
++
++/* The next line is used to disable full ANSI prototypes, if your
++ * compiler has problems with the prototypes, make sure this line always
++ * evaluates to true :-) */
++#if defined(MSDOS) || defined(__STDC__)
++#undef NOPROTO
++#endif
++#ifndef NOPROTO
++char *des_options(void);
++void des_ecb3_encrypt(des_cblock *input,des_cblock *output,
++ des_key_schedule ks1,des_key_schedule ks2,
++ des_key_schedule ks3, int enc);
++DES_LONG des_cbc_cksum(des_cblock *input,des_cblock *output,
++ long length,des_key_schedule schedule,des_cblock *ivec);
++void des_cbc_encrypt(des_cblock *input,des_cblock *output,long length,
++ des_key_schedule schedule,des_cblock *ivec,int enc);
++void des_ncbc_encrypt(des_cblock *input,des_cblock *output,long length,
++ des_key_schedule schedule,des_cblock *ivec,int enc);
++void des_xcbc_encrypt(des_cblock *input,des_cblock *output,long length,
++ des_key_schedule schedule,des_cblock *ivec,
++ des_cblock *inw,des_cblock *outw,int enc);
++void des_cfb_encrypt(unsigned char *in,unsigned char *out,int numbits,
++ long length,des_key_schedule schedule,des_cblock *ivec,int enc);
++void des_ecb_encrypt(des_cblock *input,des_cblock *output,
++ des_key_schedule ks,int enc);
++void des_encrypt(DES_LONG *data,des_key_schedule ks, int enc);
++void des_encrypt2(DES_LONG *data,des_key_schedule ks, int enc);
++void des_encrypt3(DES_LONG *data, des_key_schedule ks1,
++ des_key_schedule ks2, des_key_schedule ks3);
++void des_decrypt3(DES_LONG *data, des_key_schedule ks1,
++ des_key_schedule ks2, des_key_schedule ks3);
++void des_ede3_cbc_encrypt(des_cblock *input, des_cblock *output,
++ long length, des_key_schedule ks1, des_key_schedule ks2,
++ des_key_schedule ks3, des_cblock *ivec, int enc);
++void des_ede3_cfb64_encrypt(unsigned char *in, unsigned char *out,
++ long length, des_key_schedule ks1, des_key_schedule ks2,
++ des_key_schedule ks3, des_cblock *ivec, int *num, int enc);
++void des_ede3_ofb64_encrypt(unsigned char *in, unsigned char *out,
++ long length, des_key_schedule ks1, des_key_schedule ks2,
++ des_key_schedule ks3, des_cblock *ivec, int *num);
++
++void des_xwhite_in2out(des_cblock (*des_key), des_cblock (*in_white),
++ des_cblock (*out_white));
++
++int des_enc_read(int fd,char *buf,int len,des_key_schedule sched,
++ des_cblock *iv);
++int des_enc_write(int fd,char *buf,int len,des_key_schedule sched,
++ des_cblock *iv);
++char *des_fcrypt(const char *buf,const char *salt, char *ret);
++#ifdef PERL5
++char *des_crypt(const char *buf,const char *salt);
++#else
++/* some stupid compilers complain because I have declared char instead
++ * of const char */
++#ifndef __KERNEL__
++#ifdef HEADER_DES_LOCL_H
++char *crypt(const char *buf,const char *salt);
++#else /* HEADER_DES_LOCL_H */
++char *crypt(void);
++#endif /* HEADER_DES_LOCL_H */
++#endif /* __KERNEL__ */
++#endif /* PERL5 */
++void des_ofb_encrypt(unsigned char *in,unsigned char *out,
++ int numbits,long length,des_key_schedule schedule,des_cblock *ivec);
++void des_pcbc_encrypt(des_cblock *input,des_cblock *output,long length,
++ des_key_schedule schedule,des_cblock *ivec,int enc);
++DES_LONG des_quad_cksum(des_cblock *input,des_cblock *output,
++ long length,int out_count,des_cblock *seed);
++void des_random_seed(des_cblock key);
++void des_random_key(des_cblock ret);
++int des_read_password(des_cblock *key,char *prompt,int verify);
++int des_read_2passwords(des_cblock *key1,des_cblock *key2,
++ char *prompt,int verify);
++int des_read_pw_string(char *buf,int length,char *prompt,int verify);
++void des_set_odd_parity(des_cblock *key);
++int des_is_weak_key(des_cblock *key);
++int des_set_key(des_cblock *key,des_key_schedule schedule);
++int des_key_sched(des_cblock *key,des_key_schedule schedule);
++void des_string_to_key(char *str,des_cblock *key);
++void des_string_to_2keys(char *str,des_cblock *key1,des_cblock *key2);
++void des_cfb64_encrypt(unsigned char *in, unsigned char *out, long length,
++ des_key_schedule schedule, des_cblock *ivec, int *num, int enc);
++void des_ofb64_encrypt(unsigned char *in, unsigned char *out, long length,
++ des_key_schedule schedule, des_cblock *ivec, int *num);
++int des_read_pw(char *buf, char *buff, int size, char *prompt, int verify);
++
++/* Extra functions from Mark Murray <mark@grondar.za> */
++/* The following functions are not in the normal unix build or the
++ * SSLeay build. When using the SSLeay build, use RAND_seed()
++ * and RAND_bytes() instead. */
++int des_new_random_key(des_cblock *key);
++void des_init_random_number_generator(des_cblock *key);
++void des_set_random_generator_seed(des_cblock *key);
++void des_set_sequence_number(des_cblock new_sequence_number);
++void des_generate_random_block(des_cblock *block);
++
++#else
++
++char *des_options();
++void des_ecb3_encrypt();
++DES_LONG des_cbc_cksum();
++void des_cbc_encrypt();
++void des_ncbc_encrypt();
++void des_xcbc_encrypt();
++void des_cfb_encrypt();
++void des_ede3_cfb64_encrypt();
++void des_ede3_ofb64_encrypt();
++void des_ecb_encrypt();
++void des_encrypt();
++void des_encrypt2();
++void des_encrypt3();
++void des_decrypt3();
++void des_ede3_cbc_encrypt();
++int des_enc_read();
++int des_enc_write();
++char *des_fcrypt();
++#ifdef PERL5
++char *des_crypt();
++#else
++char *crypt();
++#endif
++void des_ofb_encrypt();
++void des_pcbc_encrypt();
++DES_LONG des_quad_cksum();
++void des_random_seed();
++void des_random_key();
++int des_read_password();
++int des_read_2passwords();
++int des_read_pw_string();
++void des_set_odd_parity();
++int des_is_weak_key();
++int des_set_key();
++int des_key_sched();
++void des_string_to_key();
++void des_string_to_2keys();
++void des_cfb64_encrypt();
++void des_ofb64_encrypt();
++int des_read_pw();
++void des_xwhite_in2out();
++
++/* Extra functions from Mark Murray <mark@grondar.za> */
++/* The following functions are not in the normal unix build or the
++ * SSLeay build. When using the SSLeay build, use RAND_seed()
++ * and RAND_bytes() instead. */
++#ifdef FreeBSD
++int des_new_random_key();
++void des_init_random_number_generator();
++void des_set_random_generator_seed();
++void des_set_sequence_number();
++void des_generate_random_block();
++#endif
++
++#endif
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/include/mast.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,33 @@
++struct mast_callbacks {
++ int (*packet_encap)(struct device *mast, void *context,
++ struct sk_buff *skb, int flowref);
++ int (*link_inquire)(struct device *mast, void *context);
++};
++
++
++struct device *mast_init (int family,
++ struct mast_callbacks *callbacks,
++ unsigned int flags,
++ unsigned int desired_unit,
++ unsigned int max_flowref,
++ void *context);
++
++int mast_destroy(struct device *mast);
++
++int mast_recv(struct device *mast, struct sk_buff *skb, int flowref);
++
++/* free this skb as being useless, increment failure count. */
++int mast_toast(struct device *mast, struct sk_buff *skb, int flowref);
++
++int mast_linkstat (struct device *mast, int flowref,
++ int status);
++
++int mast_setreference (struct device *mast,
++ int defaultSA);
++
++int mast_setneighbor (struct device *mast,
++ struct sockaddr *source,
++ struct sockaddr *destination,
++ int flowref);
++
++
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/include/openswan.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,513 @@
++#ifndef _OPENSWAN_H
++/*
++ * header file for FreeS/WAN library functions
++ * Copyright (C) 1998, 1999, 2000 Henry Spencer.
++ * Copyright (C) 1999, 2000, 2001 Richard Guy Briggs
++ *
++ * This library is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU Library General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/lgpl.txt>.
++ *
++ * This library 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 Library General Public
++ * License for more details.
++ *
++ * RCSID $Id$
++ */
++#define _OPENSWAN_H /* seen it, no need to see it again */
++
++
++
++/*
++ * We've just got to have some datatypes defined... And annoyingly, just
++ * where we get them depends on whether we're in userland or not.
++ */
++/* things that need to come from one place or the other, depending */
++#ifdef __KERNEL__
++#include <linux/types.h>
++#include <linux/socket.h>
++#include <linux/in.h>
++#include <linux/string.h>
++#include <linux/ctype.h>
++#define assert(foo) /* nothing */
++#else
++#include <sys/types.h>
++#include <netinet/in.h>
++#include <string.h>
++#include <ctype.h>
++#include <assert.h>
++#include <stdio.h>
++
++# define uint8_t u_int8_t
++# define uint16_t u_int16_t
++# define uint32_t u_int32_t
++# define uint64_t u_int64_t
++
++
++# define DEBUG_NO_STATIC static
++
++#endif
++
++#include <openswan/ipsec_param.h>
++
++
++/*
++ * Grab the kernel version to see if we have NET_21, and therefore
++ * IPv6. Some of this is repeated from ipsec_kversions.h. Of course,
++ * we aren't really testing if the kernel has IPv6, but rather if the
++ * the include files do.
++ */
++#include <linux/version.h>
++#ifndef KERNEL_VERSION
++#define KERNEL_VERSION(x,y,z) (((x)<<16)+((y)<<8)+(z))
++#endif
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
++#define NET_21
++#endif
++
++#ifndef IPPROTO_COMP
++# define IPPROTO_COMP 108
++#endif /* !IPPROTO_COMP */
++
++#ifndef IPPROTO_INT
++# define IPPROTO_INT 61
++#endif /* !IPPROTO_INT */
++
++#ifdef CONFIG_IPSEC_DEBUG
++#ifndef DEBUG_NO_STATIC
++# define DEBUG_NO_STATIC
++#endif
++#else /* CONFIG_IPSEC_DEBUG */
++#ifndef DEBUG_NO_STATIC
++# define DEBUG_NO_STATIC static
++#endif
++#endif /* CONFIG_IPSEC_DEBUG */
++
++#ifdef CONFIG_IPSEC_NAT_TRAVERSAL /* KERNEL ifdef */
++#ifndef NAT_TRAVERSAL
++#define NAT_TRAVERSAL
++#endif
++#endif
++#ifdef NAT_TRAVERSAL
++#define ESPINUDP_WITH_NON_IKE 1 /* draft-ietf-ipsec-nat-t-ike-00/01 */
++#define ESPINUDP_WITH_NON_ESP 2 /* draft-ietf-ipsec-nat-t-ike-02 */
++#endif
++
++/*
++ * Basic data types for the address-handling functions.
++ * ip_address and ip_subnet are supposed to be opaque types; do not
++ * use their definitions directly, they are subject to change!
++ */
++
++/* first, some quick fakes in case we're on an old system with no IPv6 */
++#ifndef s6_addr16
++struct in6_addr {
++ union
++ {
++ __u8 u6_addr8[16];
++ __u16 u6_addr16[8];
++ __u32 u6_addr32[4];
++ } in6_u;
++#define s6_addr in6_u.u6_addr8
++#define s6_addr16 in6_u.u6_addr16
++#define s6_addr32 in6_u.u6_addr32
++};
++struct sockaddr_in6 {
++ unsigned short int sin6_family; /* AF_INET6 */
++ __u16 sin6_port; /* Transport layer port # */
++ __u32 sin6_flowinfo; /* IPv6 flow information */
++ struct in6_addr sin6_addr; /* IPv6 address */
++ __u32 sin6_scope_id; /* scope id (new in RFC2553) */
++};
++#endif /* !s6_addr16 */
++
++/* then the main types */
++typedef struct {
++ union {
++ struct sockaddr_in v4;
++ struct sockaddr_in6 v6;
++ } u;
++} ip_address;
++typedef struct {
++ ip_address addr;
++ int maskbits;
++} ip_subnet;
++
++/* and the SA ID stuff */
++#ifdef __KERNEL__
++typedef __u32 ipsec_spi_t;
++#else
++typedef u_int32_t ipsec_spi_t;
++#endif
++typedef struct { /* to identify an SA, we need: */
++ ip_address dst; /* A. destination host */
++ ipsec_spi_t spi; /* B. 32-bit SPI, assigned by dest. host */
++# define SPI_PASS 256 /* magic values... */
++# define SPI_DROP 257 /* ...for use... */
++# define SPI_REJECT 258 /* ...with SA_INT */
++# define SPI_HOLD 259
++# define SPI_TRAP 260
++# define SPI_TRAPSUBNET 261
++ int proto; /* C. protocol */
++# define SA_ESP 50 /* IPPROTO_ESP */
++# define SA_AH 51 /* IPPROTO_AH */
++# define SA_IPIP 4 /* IPPROTO_IPIP */
++# define SA_COMP 108 /* IPPROTO_COMP */
++# define SA_INT 61 /* IANA reserved for internal use */
++} ip_said;
++
++/* misc */
++typedef const char *err_t; /* error message, or NULL for success */
++struct prng { /* pseudo-random-number-generator guts */
++ unsigned char sbox[256];
++ int i, j;
++ unsigned long count;
++};
++
++
++/*
++ * definitions for user space, taken from freeswan/ipsec_sa.h
++ */
++typedef uint32_t IPsecSAref_t;
++
++#define IPSEC_SA_REF_FIELD_WIDTH (8 * sizeof(IPsecSAref_t))
++
++#define IPsecSAref2NFmark(x) ((x) << (IPSEC_SA_REF_FIELD_WIDTH - IPSEC_SA_REF_TABLE_IDX_WIDTH))
++#define NFmark2IPsecSAref(x) ((x) >> (IPSEC_SA_REF_FIELD_WIDTH - IPSEC_SA_REF_TABLE_IDX_WIDTH))
++
++#define IPSEC_SAREF_NULL (~((IPsecSAref_t)0))
++
++/* GCC magic for use in function definitions! */
++#ifdef GCC_LINT
++# define PRINTF_LIKE(n) __attribute__ ((format(printf, n, n+1)))
++# define NEVER_RETURNS __attribute__ ((noreturn))
++# define UNUSED __attribute__ ((unused))
++# define BLANK_FORMAT " " /* GCC_LINT whines about empty formats */
++#else
++# define PRINTF_LIKE(n) /* ignore */
++# define NEVER_RETURNS /* ignore */
++# define UNUSED /* ignore */
++# define BLANK_FORMAT ""
++#endif
++
++
++
++
++
++/*
++ * new IPv6-compatible functions
++ */
++
++/* text conversions */
++err_t ttoul(const char *src, size_t srclen, int format, unsigned long *dst);
++size_t ultot(unsigned long src, int format, char *buf, size_t buflen);
++#define ULTOT_BUF (22+1) /* holds 64 bits in octal */
++err_t ttoaddr(const char *src, size_t srclen, int af, ip_address *dst);
++err_t tnatoaddr(const char *src, size_t srclen, int af, ip_address *dst);
++size_t addrtot(const ip_address *src, int format, char *buf, size_t buflen);
++/* RFC 1886 old IPv6 reverse-lookup format is the bulkiest */
++#define ADDRTOT_BUF (32*2 + 3 + 1 + 3 + 1 + 1)
++err_t ttosubnet(const char *src, size_t srclen, int af, ip_subnet *dst);
++size_t subnettot(const ip_subnet *src, int format, char *buf, size_t buflen);
++#define SUBNETTOT_BUF (ADDRTOT_BUF + 1 + 3)
++size_t subnetporttot(const ip_subnet *src, int format, char *buf, size_t buflen);
++#define SUBNETPROTOTOT_BUF (SUBNETTOTO_BUF + ULTOT_BUF)
++err_t ttosa(const char *src, size_t srclen, ip_said *dst);
++size_t satot(const ip_said *src, int format, char *bufptr, size_t buflen);
++#define SATOT_BUF (5 + ULTOA_BUF + 1 + ADDRTOT_BUF)
++err_t ttodata(const char *src, size_t srclen, int base, char *buf,
++ size_t buflen, size_t *needed);
++err_t ttodatav(const char *src, size_t srclen, int base,
++ char *buf, size_t buflen, size_t *needed,
++ char *errp, size_t errlen, unsigned int flags);
++#define TTODATAV_BUF 40 /* ttodatav's largest non-literal message */
++#define TTODATAV_IGNORESPACE (1<<1) /* ignore spaces in base64 encodings*/
++#define TTODATAV_SPACECOUNTS 0 /* do not ignore spaces in base64 */
++
++size_t datatot(const char *src, size_t srclen, int format, char *buf,
++ size_t buflen);
++size_t keyblobtoid(const unsigned char *src, size_t srclen, char *dst,
++ size_t dstlen);
++size_t splitkeytoid(const unsigned char *e, size_t elen, const unsigned char *m,
++ size_t mlen, char *dst, size_t dstlen);
++#define KEYID_BUF 10 /* up to 9 text digits plus NUL */
++err_t ttoprotoport(char *src, size_t src_len, u_int8_t *proto, u_int16_t *port,
++ int *has_port_wildcard);
++
++/* initializations */
++void initsaid(const ip_address *addr, ipsec_spi_t spi, int proto, ip_said *dst);
++err_t loopbackaddr(int af, ip_address *dst);
++err_t unspecaddr(int af, ip_address *dst);
++err_t anyaddr(int af, ip_address *dst);
++err_t initaddr(const unsigned char *src, size_t srclen, int af, ip_address *dst);
++err_t initsubnet(const ip_address *addr, int maskbits, int clash, ip_subnet *dst);
++err_t addrtosubnet(const ip_address *addr, ip_subnet *dst);
++
++/* misc. conversions and related */
++err_t rangetosubnet(const ip_address *from, const ip_address *to, ip_subnet *dst);
++int addrtypeof(const ip_address *src);
++int subnettypeof(const ip_subnet *src);
++size_t addrlenof(const ip_address *src);
++size_t addrbytesptr(const ip_address *src, const unsigned char **dst);
++size_t addrbytesof(const ip_address *src, unsigned char *dst, size_t dstlen);
++int masktocount(const ip_address *src);
++void networkof(const ip_subnet *src, ip_address *dst);
++void maskof(const ip_subnet *src, ip_address *dst);
++
++/* tests */
++int sameaddr(const ip_address *a, const ip_address *b);
++int addrcmp(const ip_address *a, const ip_address *b);
++int samesubnet(const ip_subnet *a, const ip_subnet *b);
++int addrinsubnet(const ip_address *a, const ip_subnet *s);
++int subnetinsubnet(const ip_subnet *a, const ip_subnet *b);
++int subnetishost(const ip_subnet *s);
++int samesaid(const ip_said *a, const ip_said *b);
++int sameaddrtype(const ip_address *a, const ip_address *b);
++int samesubnettype(const ip_subnet *a, const ip_subnet *b);
++int isanyaddr(const ip_address *src);
++int isunspecaddr(const ip_address *src);
++int isloopbackaddr(const ip_address *src);
++
++/* low-level grot */
++int portof(const ip_address *src);
++void setportof(int port, ip_address *dst);
++struct sockaddr *sockaddrof(ip_address *src);
++size_t sockaddrlenof(const ip_address *src);
++
++/* PRNG */
++void prng_init(struct prng *prng, const unsigned char *key, size_t keylen);
++void prng_bytes(struct prng *prng, unsigned char *dst, size_t dstlen);
++unsigned long prng_count(struct prng *prng);
++void prng_final(struct prng *prng);
++
++/* odds and ends */
++const char *ipsec_version_code(void);
++const char *ipsec_version_string(void);
++const char **ipsec_copyright_notice(void);
++
++const char *dns_string_rr(int rr, char *buf, int bufsize);
++const char *dns_string_datetime(time_t seconds,
++ char *buf,
++ int bufsize);
++
++
++/*
++ * old functions, to be deleted eventually
++ */
++
++/* unsigned long */
++const char * /* NULL for success, else string literal */
++atoul(
++ const char *src,
++ size_t srclen, /* 0 means strlen(src) */
++ int base, /* 0 means figure it out */
++ unsigned long *resultp
++);
++size_t /* space needed for full conversion */
++ultoa(
++ unsigned long n,
++ int base,
++ char *dst,
++ size_t dstlen
++);
++#define ULTOA_BUF 21 /* just large enough for largest result, */
++ /* assuming 64-bit unsigned long! */
++
++/* Internet addresses */
++const char * /* NULL for success, else string literal */
++atoaddr(
++ const char *src,
++ size_t srclen, /* 0 means strlen(src) */
++ struct in_addr *addr
++);
++size_t /* space needed for full conversion */
++addrtoa(
++ struct in_addr addr,
++ int format, /* character; 0 means default */
++ char *dst,
++ size_t dstlen
++);
++#define ADDRTOA_BUF 16 /* just large enough for largest result */
++
++/* subnets */
++const char * /* NULL for success, else string literal */
++atosubnet(
++ const char *src,
++ size_t srclen, /* 0 means strlen(src) */
++ struct in_addr *addr,
++ struct in_addr *mask
++);
++size_t /* space needed for full conversion */
++subnettoa(
++ struct in_addr addr,
++ struct in_addr mask,
++ int format, /* character; 0 means default */
++ char *dst,
++ size_t dstlen
++);
++#define SUBNETTOA_BUF 32 /* large enough for worst case result */
++
++/* ranges */
++const char * /* NULL for success, else string literal */
++atoasr(
++ const char *src,
++ size_t srclen, /* 0 means strlen(src) */
++ char *type, /* 'a', 's', 'r' */
++ struct in_addr *addrs /* two-element array */
++);
++size_t /* space needed for full conversion */
++rangetoa(
++ struct in_addr *addrs, /* two-element array */
++ int format, /* character; 0 means default */
++ char *dst,
++ size_t dstlen
++);
++#define RANGETOA_BUF 34 /* large enough for worst case result */
++
++/* data types for SA conversion functions */
++
++/* generic data, e.g. keys */
++const char * /* NULL for success, else string literal */
++atobytes(
++ const char *src,
++ size_t srclen, /* 0 means strlen(src) */
++ char *dst,
++ size_t dstlen,
++ size_t *lenp /* NULL means don't bother telling me */
++);
++size_t /* 0 failure, else true size */
++bytestoa(
++ const char *src,
++ size_t srclen,
++ int format, /* character; 0 means default */
++ char *dst,
++ size_t dstlen
++);
++
++/* old versions of generic-data functions; deprecated */
++size_t /* 0 failure, else true size */
++atodata(
++ const char *src,
++ size_t srclen, /* 0 means strlen(src) */
++ char *dst,
++ size_t dstlen
++);
++size_t /* 0 failure, else true size */
++datatoa(
++ const char *src,
++ size_t srclen,
++ int format, /* character; 0 means default */
++ char *dst,
++ size_t dstlen
++);
++
++/* part extraction and special addresses */
++struct in_addr
++subnetof(
++ struct in_addr addr,
++ struct in_addr mask
++);
++struct in_addr
++hostof(
++ struct in_addr addr,
++ struct in_addr mask
++);
++struct in_addr
++broadcastof(
++ struct in_addr addr,
++ struct in_addr mask
++);
++
++/* mask handling */
++int
++goodmask(
++ struct in_addr mask
++);
++int
++masktobits(
++ struct in_addr mask
++);
++struct in_addr
++bitstomask(
++ int n
++);
++
++
++
++/*
++ * general utilities
++ */
++
++#ifndef __KERNEL__
++/* option pickup from files (userland only because of use of FILE) */
++const char *optionsfrom(const char *filename, int *argcp, char ***argvp,
++ int optind, FILE *errorreport);
++
++/* sanitize a string */
++extern size_t sanitize_string(char *buf, size_t size);
++
++#endif
++
++
++/*
++ * ENUM of klips debugging values. Not currently used in klips.
++ * debug flag is actually 32 -bits, but only one bit is ever used,
++ * so we can actually pack it all into a single 32-bit word.
++ */
++enum klips_debug_flags {
++ KDF_VERBOSE = 0,
++ KDF_XMIT = 1,
++ KDF_NETLINK = 2, /* obsolete */
++ KDF_XFORM = 3,
++ KDF_EROUTE = 4,
++ KDF_SPI = 5,
++ KDF_RADIJ = 6,
++ KDF_ESP = 7,
++ KDF_AH = 8, /* obsolete */
++ KDF_RCV = 9,
++ KDF_TUNNEL = 10,
++ KDF_PFKEY = 11,
++ KDF_COMP = 12
++};
++
++
++/*
++ * Debugging levels for pfkey_lib_debug
++ */
++#define PF_KEY_DEBUG_PARSE_NONE 0
++#define PF_KEY_DEBUG_PARSE_PROBLEM 1
++#define PF_KEY_DEBUG_PARSE_STRUCT 2
++#define PF_KEY_DEBUG_PARSE_FLOW 4
++#define PF_KEY_DEBUG_BUILD 8
++#define PF_KEY_DEBUG_PARSE_MAX 15
++
++extern unsigned int pfkey_lib_debug; /* bits selecting what to report */
++
++/*
++ * pluto and lwdnsq need to know the maximum size of the commands to,
++ * and replies from lwdnsq.
++ */
++
++#define LWDNSQ_CMDBUF_LEN 1024
++#define LWDNSQ_RESULT_LEN_MAX 4096
++
++
++/* syntax for passthrough SA */
++#ifndef PASSTHROUGHNAME
++#define PASSTHROUGHNAME "%passthrough"
++#define PASSTHROUGH4NAME "%passthrough4"
++#define PASSTHROUGH6NAME "%passthrough6"
++#define PASSTHROUGHIS "tun0@0.0.0.0"
++#define PASSTHROUGH4IS "tun0@0.0.0.0"
++#define PASSTHROUGH6IS "tun0@::"
++#define PASSTHROUGHTYPE "tun"
++#define PASSTHROUGHSPI 0
++#define PASSTHROUGHDST 0
++#endif
++
++
++
++#endif /* _OPENSWAN_H */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/include/openswan/ipcomp.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,61 @@
++/*
++ * IPCOMP zlib interface code.
++ * Copyright (C) 2000 Svenning Soerensen <svenning@post5.tele.dk>
++ * Copyright (C) 2000, 2001 Richard Guy Briggs <rgb@conscoop.ottawa.on.ca>
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++
++ RCSID $Id$
++
++ */
++
++/* SSS */
++
++#ifndef _IPCOMP_H
++#define _IPCOMP_H
++
++/* Prefix all global deflate symbols with "ipcomp_" to avoid collisions with ppp_deflate & ext2comp */
++#ifndef IPCOMP_PREFIX
++#define IPCOMP_PREFIX
++#endif /* IPCOMP_PREFIX */
++
++#ifndef IPPROTO_COMP
++#define IPPROTO_COMP 108
++#endif /* IPPROTO_COMP */
++
++#ifdef CONFIG_IPSEC_DEBUG
++extern int sysctl_ipsec_debug_ipcomp;
++#endif /* CONFIG_IPSEC_DEBUG */
++
++struct ipcomphdr { /* IPCOMP header */
++ __u8 ipcomp_nh; /* Next header (protocol) */
++ __u8 ipcomp_flags; /* Reserved, must be 0 */
++ __u16 ipcomp_cpi; /* Compression Parameter Index */
++};
++
++extern struct inet_protocol comp_protocol;
++extern int sysctl_ipsec_debug_ipcomp;
++
++#define IPCOMP_UNCOMPRESSABLE 0x000000001
++#define IPCOMP_COMPRESSIONERROR 0x000000002
++#define IPCOMP_PARMERROR 0x000000004
++#define IPCOMP_DECOMPRESSIONERROR 0x000000008
++
++#define IPCOMP_ADAPT_INITIAL_TRIES 8
++#define IPCOMP_ADAPT_INITIAL_SKIP 4
++#define IPCOMP_ADAPT_SUBSEQ_TRIES 2
++#define IPCOMP_ADAPT_SUBSEQ_SKIP 8
++
++/* Function prototypes */
++struct sk_buff *skb_compress(struct sk_buff *skb, struct ipsec_sa *ips, unsigned int *flags);
++struct sk_buff *skb_decompress(struct sk_buff *skb, struct ipsec_sa *ips, unsigned int *flags);
++
++#endif /* _IPCOMP_H */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/include/openswan/ipsec_ah.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,190 @@
++/*
++ * Authentication Header declarations
++ * Copyright (C) 1996, 1997 John Ioannidis.
++ * Copyright (C) 1998, 1999, 2000, 2001 Richard Guy Briggs.
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ * RCSID $Id$
++ */
++
++#include "ipsec_md5h.h"
++#include "ipsec_sha1.h"
++
++#ifndef IPPROTO_AH
++#define IPPROTO_AH 51
++#endif /* IPPROTO_AH */
++
++#include "ipsec_auth.h"
++
++#ifdef __KERNEL__
++
++extern struct inet_protocol ah_protocol;
++
++struct options;
++
++struct ahhdr /* Generic AH header */
++{
++ __u8 ah_nh; /* Next header (protocol) */
++ __u8 ah_hl; /* AH length, in 32-bit words */
++ __u16 ah_rv; /* reserved, must be 0 */
++ __u32 ah_spi; /* Security Parameters Index */
++ __u32 ah_rpl; /* Replay prevention */
++ __u8 ah_data[AHHMAC_HASHLEN];/* Authentication hash */
++};
++#define AH_BASIC_LEN 8 /* basic AH header is 8 bytes, nh,hl,rv,spi
++ * and the ah_hl, says how many bytes after that
++ * to cover. */
++
++extern struct xform_functions ah_xform_funcs[];
++
++#ifdef CONFIG_IPSEC_DEBUG
++extern int debug_ah;
++#endif /* CONFIG_IPSEC_DEBUG */
++#endif /* __KERNEL__ */
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.23 2004/04/05 19:55:04 mcr
++ * Moved from linux/include/freeswan/ipsec_ah.h,v
++ *
++ * Revision 1.22 2004/04/05 19:41:05 mcr
++ * merged alg-branch code.
++ *
++ * Revision 1.21 2003/12/13 19:10:16 mcr
++ * refactored rcv and xmit code - same as FS 2.05.
++ *
++ * Revision 1.22 2003/12/11 20:14:58 mcr
++ * refactored the xmit code, to move all encapsulation
++ * code into protocol functions. Note that all functions
++ * are essentially done by a single function, which is probably
++ * wrong.
++ * the rcv_functions structures are renamed xform_functions.
++ *
++ * Revision 1.21 2003/12/06 21:21:19 mcr
++ * split up receive path into per-transform files, for
++ * easier later removal.
++ *
++ * Revision 1.20.8.1 2003/12/22 15:25:52 jjo
++ * Merged algo-0.8.1-rc11-test1 into alg-branch
++ *
++ * Revision 1.20 2003/02/06 02:21:34 rgb
++ *
++ * Moved "struct auth_alg" from ipsec_rcv.c to ipsec_ah.h .
++ * Changed "struct ah" to "struct ahhdr" and "struct esp" to "struct esphdr".
++ * Removed "#ifdef INBOUND_POLICY_CHECK_eroute" dead code.
++ *
++ * Revision 1.19 2002/09/16 21:19:13 mcr
++ * fixes for west-ah-icmp-01 - length of AH header must be
++ * calculated properly, and next_header field properly copied.
++ *
++ * Revision 1.18 2002/05/14 02:37:02 rgb
++ * Change reference from _TDB to _IPSA.
++ *
++ * Revision 1.17 2002/04/24 07:36:46 mcr
++ * Moved from ./klips/net/ipsec/ipsec_ah.h,v
++ *
++ * Revision 1.16 2002/02/20 01:27:06 rgb
++ * Ditched a pile of structs only used by the old Netlink interface.
++ *
++ * Revision 1.15 2001/12/11 02:35:57 rgb
++ * Change "struct net_device" to "struct device" for 2.2 compatibility.
++ *
++ * Revision 1.14 2001/11/26 09:23:47 rgb
++ * Merge MCR's ipsec_sa, eroute, proc and struct lifetime changes.
++ *
++ * Revision 1.13.2.1 2001/09/25 02:18:24 mcr
++ * replace "struct device" with "struct netdevice"
++ *
++ * Revision 1.13 2001/06/14 19:35:08 rgb
++ * Update copyright date.
++ *
++ * Revision 1.12 2000/09/12 03:21:20 rgb
++ * Cleared out unused htonq.
++ *
++ * Revision 1.11 2000/09/08 19:12:55 rgb
++ * Change references from DEBUG_IPSEC to CONFIG_IPSEC_DEBUG.
++ *
++ * Revision 1.10 2000/01/21 06:13:10 rgb
++ * Tidied up spacing.
++ * Added macros for HMAC padding magic numbers.(kravietz)
++ *
++ * Revision 1.9 1999/12/07 18:16:23 rgb
++ * Fixed comments at end of #endif lines.
++ *
++ * Revision 1.8 1999/04/11 00:28:56 henry
++ * GPL boilerplate
++ *
++ * Revision 1.7 1999/04/06 04:54:25 rgb
++ * Fix/Add RCSID Id: and Log: bits to make PHMDs happy. This includes
++ * patch shell fixes.
++ *
++ * Revision 1.6 1999/01/26 02:06:01 rgb
++ * Removed CONFIG_IPSEC_ALGO_SWITCH macro.
++ *
++ * Revision 1.5 1999/01/22 06:17:49 rgb
++ * Updated macro comments.
++ * Added context types to support algorithm switch code.
++ * 64-bit clean-up -- converting 'u long long' to __u64.
++ *
++ * Revision 1.4 1998/07/14 15:54:56 rgb
++ * Add #ifdef __KERNEL__ to protect kernel-only structures.
++ *
++ * Revision 1.3 1998/06/30 18:05:16 rgb
++ * Comment out references to htonq.
++ *
++ * Revision 1.2 1998/06/25 19:33:46 rgb
++ * Add prototype for protocol receive function.
++ * Rearrange for more logical layout.
++ *
++ * Revision 1.1 1998/06/18 21:27:43 henry
++ * move sources from klips/src to klips/net/ipsec, to keep stupid
++ * kernel-build scripts happier in the presence of symlinks
++ *
++ * Revision 1.4 1998/05/18 22:28:43 rgb
++ * Disable key printing facilities from /proc/net/ipsec_*.
++ *
++ * Revision 1.3 1998/04/21 21:29:07 rgb
++ * Rearrange debug switches to change on the fly debug output from user
++ * space. Only kernel changes checked in at this time. radij.c was also
++ * changed to temporarily remove buggy debugging code in rj_delete causing
++ * an OOPS and hence, netlink device open errors.
++ *
++ * Revision 1.2 1998/04/12 22:03:17 rgb
++ * Updated ESP-3DES-HMAC-MD5-96,
++ * ESP-DES-HMAC-MD5-96,
++ * AH-HMAC-MD5-96,
++ * AH-HMAC-SHA1-96 since Henry started freeswan cvs repository
++ * from old standards (RFC182[5-9] to new (as of March 1998) drafts.
++ *
++ * Fixed eroute references in /proc/net/ipsec*.
++ *
++ * Started to patch module unloading memory leaks in ipsec_netlink and
++ * radij tree unloading.
++ *
++ * Revision 1.1 1998/04/09 03:05:55 henry
++ * sources moved up from linux/net/ipsec
++ *
++ * Revision 1.1.1.1 1998/04/08 05:35:02 henry
++ * RGB's ipsec-0.8pre2.tar.gz ipsec-0.8
++ *
++ * Revision 0.4 1997/01/15 01:28:15 ji
++ * Added definitions for new AH transforms.
++ *
++ * Revision 0.3 1996/11/20 14:35:48 ji
++ * Minor Cleanup.
++ * Rationalized debugging code.
++ *
++ * Revision 0.2 1996/11/02 00:18:33 ji
++ * First limited release.
++ *
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/include/openswan/ipsec_alg.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,260 @@
++/*
++ * Modular extensions service and registration functions interface
++ *
++ * Author: JuanJo Ciarlante <jjo-ipsec@mendoza.gov.ar>
++ *
++ * ipsec_alg.h,v 1.1.2.1 2003/11/21 18:12:23 jjo Exp
++ *
++ */
++/*
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ */
++#ifndef IPSEC_ALG_H
++#define IPSEC_ALG_H
++
++/*
++ * gcc >= 3.2 has removed __FUNCTION__, replaced by C99 __func__
++ * *BUT* its a compiler variable.
++ */
++#if (__GNUC__ >= 3)
++#ifndef __FUNCTION__
++#define __FUNCTION__ __func__
++#endif
++#endif
++
++/* Version 0.8.1-0 */
++#define IPSEC_ALG_VERSION 0x00080100
++
++#include <linux/types.h>
++#include <linux/list.h>
++#include <asm/atomic.h>
++/*
++ * The following structs are used via pointers in ipsec_alg object to
++ * avoid ipsec_alg.h coupling with freeswan headers, thus simplifying
++ * module development
++ */
++struct ipsec_sa;
++struct esp;
++
++/**************************************
++ *
++ * Main registration object
++ *
++ *************************************/
++#define IPSEC_ALG_VERSION_QUAD(v) \
++ (v>>24),((v>>16)&0xff),((v>>8)&0xff),(v&0xff)
++/*
++ * Main ipsec_alg objects: "OOPrograming wannabe"
++ * Hierachy (carefully handled with _minimal_ cast'ing):
++ *
++ * ipsec_alg+
++ * +->ipsec_alg_enc (ixt_alg_type=SADB_EXT_SUPPORTED_ENCRYPT)
++ * +->ipsec_alg_auth (ixt_alg_type=SADB_EXT_SUPPORTED_AUTH)
++ */
++
++/***************************************************************
++ *
++ * INTERFACE object: struct ipsec_alg
++ *
++ ***************************************************************/
++
++/*
++ * common part for every struct ipsec_alg_*
++ * (sortof poor's man OOP)
++ */
++#define IPSEC_ALG_STRUCT_COMMON \
++ unsigned ixt_version; /* only allow this version (or 'near')*/ \
++ struct list_head ixt_list; /* dlinked list */ \
++ struct module *ixt_module; /* THIS_MODULE */ \
++ unsigned ixt_state; /* state flags */ \
++ atomic_t ixt_refcnt; /* ref. count when pointed from ipsec_sa */ \
++ char ixt_name[16]; /* descriptive short name, eg. "3des" */ \
++ void *ixt_data; /* private for algo implementation */ \
++ uint8_t ixt_blocksize; /* blocksize in bytes */ \
++ \
++ /* THIS IS A COPY of struct supported (lib/pfkey.h) \
++ * please keep in sync until we migrate 'supported' stuff \
++ * to ipsec_alg \
++ */ \
++ uint16_t ixt_alg_type; /* correspond to IPSEC_ALG_{ENCRYPT,AUTH} */ \
++ uint8_t ixt_alg_id; /* enc. alg. number, eg. ESP_3DES */ \
++ uint8_t ixt_ivlen; /* ivlen in bits, expected to be multiple of 8! */ \
++ uint16_t ixt_keyminbits;/* min. keybits (of entropy) */ \
++ uint16_t ixt_keymaxbits;/* max. keybits (of entropy) */
++
++#define ixt_support ixt_alg_type
++
++#define IPSEC_ALG_ST_SUPP 0x01
++#define IPSEC_ALG_ST_REGISTERED 0x02
++#define IPSEC_ALG_ST_EXCL 0x04
++struct ipsec_alg {
++ IPSEC_ALG_STRUCT_COMMON
++};
++/*
++ * Note the const in cbc_encrypt IV arg:
++ * some ciphers like to toast passed IV (eg. 3DES): make a local IV copy
++ */
++struct ipsec_alg_enc {
++ IPSEC_ALG_STRUCT_COMMON
++ unsigned ixt_e_keylen; /* raw key length in bytes */
++ unsigned ixt_e_ctx_size; /* sa_p->key_e_size */
++ int (*ixt_e_set_key)(struct ipsec_alg_enc *alg, __u8 *key_e, const __u8 *key, size_t keysize);
++ __u8 *(*ixt_e_new_key)(struct ipsec_alg_enc *alg, const __u8 *key, size_t keysize);
++ void (*ixt_e_destroy_key)(struct ipsec_alg_enc *alg, __u8 *key_e);
++ int (*ixt_e_cbc_encrypt)(struct ipsec_alg_enc *alg, __u8 *key_e, __u8 *in, int ilen, const __u8 *iv, int encrypt);
++};
++struct ipsec_alg_auth {
++ IPSEC_ALG_STRUCT_COMMON
++ unsigned ixt_a_keylen; /* raw key length in bytes */
++ unsigned ixt_a_ctx_size; /* sa_p->key_a_size */
++ unsigned ixt_a_authlen; /* 'natural' auth. hash len (bytes) */
++ int (*ixt_a_hmac_set_key)(struct ipsec_alg_auth *alg, __u8 *key_a, const __u8 *key, int keylen);
++ int (*ixt_a_hmac_hash)(struct ipsec_alg_auth *alg, __u8 *key_a, const __u8 *dat, int len, __u8 *hash, int hashlen);
++};
++/*
++ * These are _copies_ of SADB_EXT_SUPPORTED_{AUTH,ENCRYPT},
++ * to avoid header coupling for true constants
++ * about headers ... "cp is your friend" --Linus
++ */
++#define IPSEC_ALG_TYPE_AUTH 14
++#define IPSEC_ALG_TYPE_ENCRYPT 15
++
++/***************************************************************
++ *
++ * INTERFACE for module loading,testing, and unloading
++ *
++ ***************************************************************/
++/* - registration calls */
++int register_ipsec_alg(struct ipsec_alg *);
++int unregister_ipsec_alg(struct ipsec_alg *);
++/* - optional (simple test) for algos */
++int ipsec_alg_test(unsigned alg_type, unsigned alg_id, int testparm);
++/* inline wrappers (usefull for type validation */
++static inline int register_ipsec_alg_enc(struct ipsec_alg_enc *ixt) {
++ return register_ipsec_alg((struct ipsec_alg*)ixt);
++}
++static inline int unregister_ipsec_alg_enc(struct ipsec_alg_enc *ixt) {
++ return unregister_ipsec_alg((struct ipsec_alg*)ixt);
++}
++static inline int register_ipsec_alg_auth(struct ipsec_alg_auth *ixt) {
++ return register_ipsec_alg((struct ipsec_alg*)ixt);
++}
++static inline int unregister_ipsec_alg_auth(struct ipsec_alg_auth *ixt) {
++ return unregister_ipsec_alg((struct ipsec_alg*)ixt);
++}
++
++/*****************************************************************
++ *
++ * INTERFACE for ENC services: key creation, encrypt function
++ *
++ *****************************************************************/
++
++#define IPSEC_ALG_ENCRYPT 1
++#define IPSEC_ALG_DECRYPT 0
++
++/* encryption key context creation function */
++int ipsec_alg_enc_key_create(struct ipsec_sa *sa_p);
++/*
++ * ipsec_alg_esp_encrypt(): encrypt ilen bytes in idat returns
++ * 0 or ERR<0
++ */
++int ipsec_alg_esp_encrypt(struct ipsec_sa *sa_p, __u8 *idat, int ilen, const __u8 *iv, int action);
++
++/***************************************************************
++ *
++ * INTERFACE for AUTH services: key creation, hash functions
++ *
++ ***************************************************************/
++int ipsec_alg_auth_key_create(struct ipsec_sa *sa_p);
++int ipsec_alg_sa_esp_hash(const struct ipsec_sa *sa_p, const __u8 *espp, int len, __u8 *hash, int hashlen) ;
++#define ipsec_alg_sa_esp_update(c,k,l) ipsec_alg_sa_esp_hash(c,k,l,NULL,0)
++
++/* only called from ipsec_init.c */
++int ipsec_alg_init(void);
++
++/* algo module glue for static algos */
++void ipsec_alg_static_init(void);
++typedef int (*ipsec_alg_init_func_t) (void);
++
++/**********************************************
++ *
++ * INTERFACE for ipsec_sa init and wipe
++ *
++ **********************************************/
++
++/* returns true if ipsec_sa has ipsec_alg obj attached */
++/*
++ * Initializes ipsec_sa's ipsec_alg object, using already loaded
++ * proto, authalg, encalg.; links ipsec_alg objects (enc, auth)
++ */
++int ipsec_alg_sa_init(struct ipsec_sa *sa_p);
++/*
++ * Destroys ipsec_sa's ipsec_alg object
++ * unlinking ipsec_alg objects
++ */
++int ipsec_alg_sa_wipe(struct ipsec_sa *sa_p);
++
++#define IPSEC_ALG_MODULE_INIT_MOD( func_name ) \
++ static int func_name(void); \
++ module_init(func_name); \
++ static int __init func_name(void)
++#define IPSEC_ALG_MODULE_EXIT_MOD( func_name ) \
++ static void func_name(void); \
++ module_exit(func_name); \
++ static void __exit func_name(void)
++
++#define IPSEC_ALG_MODULE_INIT_STATIC( func_name ) \
++ extern int func_name(void); \
++ int func_name(void)
++#define IPSEC_ALG_MODULE_EXIT_STATIC( func_name ) \
++ extern void func_name(void); \
++ void func_name(void)
++
++/**********************************************
++ *
++ * 2.2 backport for some 2.4 useful module stuff
++ *
++ **********************************************/
++#ifdef MODULE
++#ifndef THIS_MODULE
++#define THIS_MODULE (&__this_module)
++#endif
++#ifndef module_init
++typedef int (*__init_module_func_t)(void);
++typedef void (*__cleanup_module_func_t)(void);
++
++#define module_init(x) \
++ int init_module(void) __attribute__((alias(#x))); \
++ static inline __init_module_func_t __init_module_inline(void) \
++ { return x; }
++#define module_exit(x) \
++ void cleanup_module(void) __attribute__((alias(#x))); \
++ static inline __cleanup_module_func_t __cleanup_module_inline(void) \
++ { return x; }
++#endif
++#define IPSEC_ALG_MODULE_INIT( func_name ) IPSEC_ALG_MODULE_INIT_MOD( func_name )
++#define IPSEC_ALG_MODULE_EXIT( func_name ) IPSEC_ALG_MODULE_EXIT_MOD( func_name )
++
++#else /* not MODULE */
++#ifndef THIS_MODULE
++#define THIS_MODULE NULL
++#endif
++/*
++ * I only want module_init() magic
++ * when algo.c file *is THE MODULE*, in all other
++ * cases, initialization is called explicitely from ipsec_alg_init()
++ */
++#define IPSEC_ALG_MODULE_INIT( func_name ) IPSEC_ALG_MODULE_INIT_STATIC(func_name)
++#define IPSEC_ALG_MODULE_EXIT( func_name ) IPSEC_ALG_MODULE_EXIT_STATIC(func_name)
++#endif
++
++#endif /* IPSEC_ALG_H */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/include/openswan/ipsec_auth.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,100 @@
++/*
++ * Authentication Header declarations
++ * Copyright (C) 2003 Michael Richardson <mcr@sandelman.ottawa.on.ca>
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ * RCSID $Id$
++ */
++
++#include "ipsec_md5h.h"
++#include "ipsec_sha1.h"
++
++#ifndef IPSEC_AUTH_H
++#define IPSEC_AUTH_H
++
++#define AH_FLENGTH 12 /* size of fixed part */
++#define AHMD5_KMAX 64 /* MD5 max 512 bits key */
++#define AHMD5_AMAX 12 /* MD5 96 bits of authenticator */
++
++#define AHMD596_KLEN 16 /* MD5 128 bits key */
++#define AHSHA196_KLEN 20 /* SHA1 160 bits key */
++
++#define AHMD596_ALEN 16 /* MD5 128 bits authentication length */
++#define AHSHA196_ALEN 20 /* SHA1 160 bits authentication length */
++
++#define AHMD596_BLKLEN 64 /* MD5 block length */
++#define AHSHA196_BLKLEN 64 /* SHA1 block length */
++#define AHSHA2_256_BLKLEN 64 /* SHA2-256 block length */
++#define AHSHA2_384_BLKLEN 128 /* SHA2-384 block length (?) */
++#define AHSHA2_512_BLKLEN 128 /* SHA2-512 block length */
++
++#define AH_BLKLEN_MAX 128 /* keep up to date! */
++
++
++#define AH_AMAX AHSHA196_ALEN /* keep up to date! */
++#define AHHMAC_HASHLEN 12 /* authenticator length of 96bits */
++#define AHHMAC_RPLLEN 4 /* 32 bit replay counter */
++
++#define DB_AH_PKTRX 0x0001
++#define DB_AH_PKTRX2 0x0002
++#define DB_AH_DMP 0x0004
++#define DB_AH_IPSA 0x0010
++#define DB_AH_XF 0x0020
++#define DB_AH_INAU 0x0040
++#define DB_AH_REPLAY 0x0100
++
++#ifdef __KERNEL__
++
++/* General HMAC algorithm is described in RFC 2104 */
++
++#define HMAC_IPAD 0x36
++#define HMAC_OPAD 0x5C
++
++struct md5_ctx {
++ MD5_CTX ictx; /* context after H(K XOR ipad) */
++ MD5_CTX octx; /* context after H(K XOR opad) */
++};
++
++struct sha1_ctx {
++ SHA1_CTX ictx; /* context after H(K XOR ipad) */
++ SHA1_CTX octx; /* context after H(K XOR opad) */
++};
++
++struct auth_alg {
++ void (*init)(void *ctx);
++ void (*update)(void *ctx, unsigned char *bytes, __u32 len);
++ void (*final)(unsigned char *hash, void *ctx);
++ int hashlen;
++};
++
++struct options;
++
++#endif /* __KERNEL__ */
++#endif /* IPSEC_AUTH_H */
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.3 2004/04/06 02:49:08 mcr
++ * pullup of algo code from alg-branch.
++ *
++ * Revision 1.2 2004/04/05 19:55:04 mcr
++ * Moved from linux/include/freeswan/ipsec_auth.h,v
++ *
++ * Revision 1.1 2003/12/13 19:10:16 mcr
++ * refactored rcv and xmit code - same as FS 2.05.
++ *
++ * Revision 1.1 2003/12/06 21:21:19 mcr
++ * split up receive path into per-transform files, for
++ * easier later removal.
++ *
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/include/openswan/ipsec_encap.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,149 @@
++/*
++ * declarations relevant to encapsulation-like operations
++ * Copyright (C) 1996, 1997 John Ioannidis.
++ * Copyright (C) 1998, 1999, 2000, 2001 Richard Guy Briggs.
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ * RCSID $Id$
++ */
++
++#ifndef _IPSEC_ENCAP_H_
++
++#define SENT_IP4 16 /* data is two struct in_addr + proto + ports*/
++ /* (2 * sizeof(struct in_addr)) */
++ /* sizeof(struct sockaddr_encap)
++ - offsetof(struct sockaddr_encap, Sen.Sip4.Src) */
++
++struct sockaddr_encap
++{
++ __u8 sen_len; /* length */
++ __u8 sen_family; /* AF_ENCAP */
++ __u16 sen_type; /* see SENT_* */
++ union
++ {
++ struct /* SENT_IP4 */
++ {
++ struct in_addr Src;
++ struct in_addr Dst;
++ __u8 Proto;
++ __u16 Sport;
++ __u16 Dport;
++ } Sip4;
++ } Sen;
++};
++
++#define sen_ip_src Sen.Sip4.Src
++#define sen_ip_dst Sen.Sip4.Dst
++#define sen_proto Sen.Sip4.Proto
++#define sen_sport Sen.Sip4.Sport
++#define sen_dport Sen.Sip4.Dport
++
++#ifndef AF_ENCAP
++#define AF_ENCAP 26
++#endif /* AF_ENCAP */
++
++#define _IPSEC_ENCAP_H_
++#endif /* _IPSEC_ENCAP_H_ */
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.19 2004/04/05 19:55:04 mcr
++ * Moved from linux/include/freeswan/ipsec_encap.h,v
++ *
++ * Revision 1.18 2003/10/31 02:27:05 mcr
++ * pulled up port-selector patches and sa_id elimination.
++ *
++ * Revision 1.17.30.1 2003/09/21 13:59:38 mcr
++ * pre-liminary X.509 patch - does not yet pass tests.
++ *
++ * Revision 1.17 2002/04/24 07:36:46 mcr
++ * Moved from ./klips/net/ipsec/ipsec_encap.h,v
++ *
++ * Revision 1.16 2001/11/26 09:23:47 rgb
++ * Merge MCR's ipsec_sa, eroute, proc and struct lifetime changes.
++ *
++ * Revision 1.15.2.1 2001/09/25 02:18:54 mcr
++ * struct eroute moved to ipsec_eroute.h
++ *
++ * Revision 1.15 2001/09/14 16:58:36 rgb
++ * Added support for storing the first and last packets through a HOLD.
++ *
++ * Revision 1.14 2001/09/08 21:13:31 rgb
++ * Added pfkey ident extension support for ISAKMPd. (NetCelo)
++ *
++ * Revision 1.13 2001/06/14 19:35:08 rgb
++ * Update copyright date.
++ *
++ * Revision 1.12 2001/05/27 06:12:10 rgb
++ * Added structures for pid, packet count and last access time to eroute.
++ * Added packet count to beginning of /proc/net/ipsec_eroute.
++ *
++ * Revision 1.11 2000/09/08 19:12:56 rgb
++ * Change references from DEBUG_IPSEC to CONFIG_IPSEC_DEBUG.
++ *
++ * Revision 1.10 2000/03/22 16:15:36 rgb
++ * Fixed renaming of dev_get (MB).
++ *
++ * Revision 1.9 2000/01/21 06:13:26 rgb
++ * Added a macro for AF_ENCAP
++ *
++ * Revision 1.8 1999/12/31 14:56:55 rgb
++ * MB fix for 2.3 dev-use-count.
++ *
++ * Revision 1.7 1999/11/18 04:09:18 rgb
++ * Replaced all kernel version macros to shorter, readable form.
++ *
++ * Revision 1.6 1999/09/24 00:34:13 rgb
++ * Add Marc Boucher's support for 2.3.xx+.
++ *
++ * Revision 1.5 1999/04/11 00:28:57 henry
++ * GPL boilerplate
++ *
++ * Revision 1.4 1999/04/06 04:54:25 rgb
++ * Fix/Add RCSID Id: and Log: bits to make PHMDs happy. This includes
++ * patch shell fixes.
++ *
++ * Revision 1.3 1998/10/19 14:44:28 rgb
++ * Added inclusion of freeswan.h.
++ * sa_id structure implemented and used: now includes protocol.
++ *
++ * Revision 1.2 1998/07/14 18:19:33 rgb
++ * Added #ifdef __KERNEL__ directives to restrict scope of header.
++ *
++ * Revision 1.1 1998/06/18 21:27:44 henry
++ * move sources from klips/src to klips/net/ipsec, to keep stupid
++ * kernel-build scripts happier in the presence of symlinks
++ *
++ * Revision 1.2 1998/04/21 21:29:10 rgb
++ * Rearrange debug switches to change on the fly debug output from user
++ * space. Only kernel changes checked in at this time. radij.c was also
++ * changed to temporarily remove buggy debugging code in rj_delete causing
++ * an OOPS and hence, netlink device open errors.
++ *
++ * Revision 1.1 1998/04/09 03:05:58 henry
++ * sources moved up from linux/net/ipsec
++ *
++ * Revision 1.1.1.1 1998/04/08 05:35:02 henry
++ * RGB's ipsec-0.8pre2.tar.gz ipsec-0.8
++ *
++ * Revision 0.4 1997/01/15 01:28:15 ji
++ * Minor cosmetic changes.
++ *
++ * Revision 0.3 1996/11/20 14:35:48 ji
++ * Minor Cleanup.
++ * Rationalized debugging code.
++ *
++ * Revision 0.2 1996/11/02 00:18:33 ji
++ * First limited release.
++ *
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/include/openswan/ipsec_eroute.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,112 @@
++/*
++ * @(#) declarations of eroute structures
++ *
++ * Copyright (C) 1996, 1997 John Ioannidis.
++ * Copyright (C) 1998, 1999, 2000, 2001 Richard Guy Briggs <rgb@freeswan.org>
++ * Copyright (C) 2001 Michael Richardson <mcr@freeswan.org>
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ * RCSID $Id$
++ *
++ * derived from ipsec_encap.h 1.15 on 2001/9/18 by mcr.
++ *
++ */
++
++#ifndef _IPSEC_EROUTE_H_
++
++#include "radij.h"
++#include "ipsec_encap.h"
++#include "ipsec_radij.h"
++
++/*
++ * The "type" is really part of the address as far as the routing
++ * system is concerned. By using only one bit in the type field
++ * for each type, we sort-of make sure that different types of
++ * encapsulation addresses won't be matched against the wrong type.
++ */
++
++/*
++ * An entry in the radix tree
++ */
++
++struct rjtentry
++{
++ struct radij_node rd_nodes[2]; /* tree glue, and other values */
++#define rd_key(r) ((struct sockaddr_encap *)((r)->rd_nodes->rj_key))
++#define rd_mask(r) ((struct sockaddr_encap *)((r)->rd_nodes->rj_mask))
++ short rd_flags;
++ short rd_count;
++};
++
++struct ident
++{
++ __u16 type; /* identity type */
++ __u64 id; /* identity id */
++ __u8 len; /* identity len */
++ caddr_t data; /* identity data */
++};
++
++/*
++ * An encapsulation route consists of a pointer to a
++ * radix tree entry and a SAID (a destination_address/SPI/protocol triple).
++ */
++
++struct eroute
++{
++ struct rjtentry er_rjt;
++ ip_said er_said;
++ uint32_t er_pid;
++ uint32_t er_count;
++ uint64_t er_lasttime;
++ struct sockaddr_encap er_eaddr; /* MCR get rid of _encap, it is silly*/
++ struct sockaddr_encap er_emask;
++ struct ident er_ident_s;
++ struct ident er_ident_d;
++ struct sk_buff* er_first;
++ struct sk_buff* er_last;
++};
++
++#define er_dst er_said.dst
++#define er_spi er_said.spi
++
++#define _IPSEC_EROUTE_H_
++#endif /* _IPSEC_EROUTE_H_ */
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.5 2004/04/05 19:55:05 mcr
++ * Moved from linux/include/freeswan/ipsec_eroute.h,v
++ *
++ * Revision 1.4 2003/10/31 02:27:05 mcr
++ * pulled up port-selector patches and sa_id elimination.
++ *
++ * Revision 1.3.30.2 2003/10/29 01:10:19 mcr
++ * elimited "struct sa_id"
++ *
++ * Revision 1.3.30.1 2003/09/21 13:59:38 mcr
++ * pre-liminary X.509 patch - does not yet pass tests.
++ *
++ * Revision 1.3 2002/04/24 07:36:46 mcr
++ * Moved from ./klips/net/ipsec/ipsec_eroute.h,v
++ *
++ * Revision 1.2 2001/11/26 09:16:13 rgb
++ * Merge MCR's ipsec_sa, eroute, proc and struct lifetime changes.
++ *
++ * Revision 1.1.2.1 2001/09/25 02:18:54 mcr
++ * struct eroute moved to ipsec_eroute.h
++ *
++ *
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/include/openswan/ipsec_errs.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,53 @@
++/*
++ * @(#) definition of ipsec_errs structure
++ *
++ * Copyright (C) 2001 Richard Guy Briggs <rgb@freeswan.org>
++ * and Michael Richardson <mcr@freeswan.org>
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ * RCSID $Id$
++ *
++ */
++
++/*
++ * This file describes the errors/statistics that FreeSWAN collects.
++ *
++ */
++
++struct ipsec_errs {
++ __u32 ips_alg_errs; /* number of algorithm errors */
++ __u32 ips_auth_errs; /* # of authentication errors */
++ __u32 ips_encsize_errs; /* # of encryption size errors*/
++ __u32 ips_encpad_errs; /* # of encryption pad errors*/
++ __u32 ips_replaywin_errs; /* # of pkt sequence errors */
++};
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.4 2004/04/05 19:55:05 mcr
++ * Moved from linux/include/freeswan/ipsec_errs.h,v
++ *
++ * Revision 1.3 2002/04/24 07:36:46 mcr
++ * Moved from ./klips/net/ipsec/ipsec_errs.h,v
++ *
++ * Revision 1.2 2001/11/26 09:16:13 rgb
++ * Merge MCR's ipsec_sa, eroute, proc and struct lifetime changes.
++ *
++ * Revision 1.1.2.1 2001/09/25 02:25:57 mcr
++ * lifetime structure created and common functions created.
++ *
++ *
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/include/openswan/ipsec_esp.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,147 @@
++/*
++ * Copyright (C) 1996, 1997 John Ioannidis.
++ * Copyright (C) 1998, 1999, 2000, 2001 Richard Guy Briggs.
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ * RCSID $Id$
++ */
++
++#include "openswan/ipsec_md5h.h"
++#include "openswan/ipsec_sha1.h"
++
++#include "crypto/des.h"
++
++#ifndef IPPROTO_ESP
++#define IPPROTO_ESP 50
++#endif /* IPPROTO_ESP */
++
++#define ESP_HEADER_LEN 8 /* 64 bits header (spi+rpl)*/
++
++#define EMT_ESPDESCBC_ULEN 20 /* coming from user mode */
++#define EMT_ESPDES_KMAX 64 /* 512 bit secret key enough? */
++#define EMT_ESPDES_KEY_SZ 8 /* 56 bit secret key with parity = 64 bits */
++#define EMT_ESP3DES_KEY_SZ 24 /* 168 bit secret key with parity = 192 bits */
++#define EMT_ESPDES_IV_SZ 8 /* IV size */
++#define ESP_DESCBC_BLKLEN 8 /* DES-CBC block size */
++
++#define ESP_IV_MAXSZ 16 /* This is _critical_ */
++#define ESP_IV_MAXSZ_INT (ESP_IV_MAXSZ/sizeof(int))
++
++#define DB_ES_PKTRX 0x0001
++#define DB_ES_PKTRX2 0x0002
++#define DB_ES_IPSA 0x0010
++#define DB_ES_XF 0x0020
++#define DB_ES_IPAD 0x0040
++#define DB_ES_INAU 0x0080
++#define DB_ES_OINFO 0x0100
++#define DB_ES_OINFO2 0x0200
++#define DB_ES_OH 0x0400
++#define DB_ES_REPLAY 0x0800
++
++#ifdef __KERNEL__
++struct des_eks {
++ des_key_schedule ks;
++};
++
++extern struct inet_protocol esp_protocol;
++
++struct options;
++
++struct esphdr
++{
++ __u32 esp_spi; /* Security Parameters Index */
++ __u32 esp_rpl; /* Replay counter */
++ __u8 esp_iv[8]; /* iv */
++};
++
++extern struct xform_functions esp_xform_funcs[];
++
++#ifdef CONFIG_IPSEC_DEBUG
++extern int debug_esp;
++#endif /* CONFIG_IPSEC_DEBUG */
++#endif /* __KERNEL__ */
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.25 2004/04/06 02:49:08 mcr
++ * pullup of algo code from alg-branch.
++ *
++ * Revision 1.24 2004/04/05 19:55:05 mcr
++ * Moved from linux/include/freeswan/ipsec_esp.h,v
++ *
++ * Revision 1.23 2004/04/05 19:41:05 mcr
++ * merged alg-branch code.
++ *
++ * Revision 1.22 2003/12/13 19:10:16 mcr
++ * refactored rcv and xmit code - same as FS 2.05.
++ *
++ * Revision 1.23 2003/12/11 20:14:58 mcr
++ * refactored the xmit code, to move all encapsulation
++ * code into protocol functions. Note that all functions
++ * are essentially done by a single function, which is probably
++ * wrong.
++ * the rcv_functions structures are renamed xform_functions.
++ *
++ * Revision 1.22 2003/12/06 21:21:19 mcr
++ * split up receive path into per-transform files, for
++ * easier later removal.
++ *
++ * Revision 1.21.8.1 2003/12/22 15:25:52 jjo
++ * Merged algo-0.8.1-rc11-test1 into alg-branch
++ *
++ * Revision 1.21 2003/02/06 02:21:34 rgb
++ *
++ * Moved "struct auth_alg" from ipsec_rcv.c to ipsec_ah.h .
++ * Changed "struct ah" to "struct ahhdr" and "struct esp" to "struct esphdr".
++ * Removed "#ifdef INBOUND_POLICY_CHECK_eroute" dead code.
++ *
++ * Revision 1.20 2002/05/14 02:37:02 rgb
++ * Change reference from _TDB to _IPSA.
++ *
++ * Revision 1.19 2002/04/24 07:55:32 mcr
++ * #include patches and Makefiles for post-reorg compilation.
++ *
++ * Revision 1.18 2002/04/24 07:36:46 mcr
++ * Moved from ./klips/net/ipsec/ipsec_esp.h,v
++ *
++ * Revision 1.17 2002/02/20 01:27:07 rgb
++ * Ditched a pile of structs only used by the old Netlink interface.
++ *
++ * Revision 1.16 2001/12/11 02:35:57 rgb
++ * Change "struct net_device" to "struct device" for 2.2 compatibility.
++ *
++ * Revision 1.15 2001/11/26 09:23:48 rgb
++ * Merge MCR's ipsec_sa, eroute, proc and struct lifetime changes.
++ *
++ * Revision 1.14.2.3 2001/10/23 04:16:42 mcr
++ * get definition of des_key_schedule from des.h
++ *
++ * Revision 1.14.2.2 2001/10/22 20:33:13 mcr
++ * use "des_key_schedule" structure instead of cooking our own.
++ *
++ * Revision 1.14.2.1 2001/09/25 02:18:25 mcr
++ * replace "struct device" with "struct netdevice"
++ *
++ * Revision 1.14 2001/06/14 19:35:08 rgb
++ * Update copyright date.
++ *
++ * Revision 1.13 2000/09/08 19:12:56 rgb
++ * Change references from DEBUG_IPSEC to CONFIG_IPSEC_DEBUG.
++ *
++ * Revision 1.12 2000/08/01 14:51:50 rgb
++ * Removed _all_ remaining traces of DES.
++ *
++ * Revision 1.11 2000/01/10 16:36:20 rgb
++ * Ditch last of EME option flags, including initiator.
++ *
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/include/openswan/ipsec_ipcomp.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,91 @@
++/*
++ * IP compression header declations
++ *
++ * Copyright (C) 2003 Michael Richardson <mcr@sandelman.ottawa.on.ca>
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ * RCSID $Id$
++ */
++
++#ifndef IPSEC_IPCOMP_H
++#define IPSEC_IPCOMP_H
++
++#include "openswan/ipsec_auth.h"
++
++/* Prefix all global deflate symbols with "ipcomp_" to avoid collisions with ppp_deflate & ext2comp */
++#ifndef IPCOMP_PREFIX
++#define IPCOMP_PREFIX
++#endif /* IPCOMP_PREFIX */
++
++#ifndef IPPROTO_COMP
++#define IPPROTO_COMP 108
++#endif /* IPPROTO_COMP */
++
++#ifdef CONFIG_IPSEC_DEBUG
++extern int sysctl_ipsec_debug_ipcomp;
++#endif /* CONFIG_IPSEC_DEBUG */
++
++struct ipcomphdr { /* IPCOMP header */
++ __u8 ipcomp_nh; /* Next header (protocol) */
++ __u8 ipcomp_flags; /* Reserved, must be 0 */
++ __u16 ipcomp_cpi; /* Compression Parameter Index */
++};
++
++extern struct inet_protocol comp_protocol;
++extern int sysctl_ipsec_debug_ipcomp;
++
++#define IPCOMP_UNCOMPRESSABLE 0x000000001
++#define IPCOMP_COMPRESSIONERROR 0x000000002
++#define IPCOMP_PARMERROR 0x000000004
++#define IPCOMP_DECOMPRESSIONERROR 0x000000008
++
++#define IPCOMP_ADAPT_INITIAL_TRIES 8
++#define IPCOMP_ADAPT_INITIAL_SKIP 4
++#define IPCOMP_ADAPT_SUBSEQ_TRIES 2
++#define IPCOMP_ADAPT_SUBSEQ_SKIP 8
++
++/* Function prototypes */
++struct sk_buff *skb_compress(struct sk_buff *skb, struct ipsec_sa *ips, unsigned int *flags);
++struct sk_buff *skb_decompress(struct sk_buff *skb, struct ipsec_sa *ips, unsigned int *flags);
++
++extern struct xform_functions ipcomp_xform_funcs[];
++
++#endif /* IPSEC_IPCOMP_H */
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.3 2004/04/06 02:49:08 mcr
++ * pullup of algo code from alg-branch.
++ *
++ * Revision 1.2 2004/04/05 19:55:05 mcr
++ * Moved from linux/include/freeswan/ipsec_ipcomp.h,v
++ *
++ * Revision 1.1 2003/12/13 19:10:16 mcr
++ * refactored rcv and xmit code - same as FS 2.05.
++ *
++ * Revision 1.2 2003/12/11 20:14:58 mcr
++ * refactored the xmit code, to move all encapsulation
++ * code into protocol functions. Note that all functions
++ * are essentially done by a single function, which is probably
++ * wrong.
++ * the rcv_functions structures are renamed xform_functions.
++ *
++ * Revision 1.1 2003/12/06 21:21:19 mcr
++ * split up receive path into per-transform files, for
++ * easier later removal.
++ *
++ *
++ *
++ */
++
++
++
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/include/openswan/ipsec_ipe4.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,68 @@
++/*
++ * IP-in-IP Header declarations
++ * Copyright (C) 1996, 1997 John Ioannidis.
++ * Copyright (C) 1998, 1999, 2000, 2001 Richard Guy Briggs.
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ * RCSID $Id$
++ */
++
++/* The packet header is an IP header! */
++
++struct ipe4_xdata /* transform table data */
++{
++ struct in_addr i4_src;
++ struct in_addr i4_dst;
++};
++
++#define EMT_IPE4_ULEN 8 /* coming from user mode */
++
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.6 2004/04/05 19:55:05 mcr
++ * Moved from linux/include/freeswan/ipsec_ipe4.h,v
++ *
++ * Revision 1.5 2002/04/24 07:36:46 mcr
++ * Moved from ./klips/net/ipsec/ipsec_ipe4.h,v
++ *
++ * Revision 1.4 2001/06/14 19:35:08 rgb
++ * Update copyright date.
++ *
++ * Revision 1.3 1999/04/11 00:28:57 henry
++ * GPL boilerplate
++ *
++ * Revision 1.2 1999/04/06 04:54:25 rgb
++ * Fix/Add RCSID Id: and Log: bits to make PHMDs happy. This includes
++ * patch shell fixes.
++ *
++ * Revision 1.1 1998/06/18 21:27:47 henry
++ * move sources from klips/src to klips/net/ipsec, to keep stupid
++ * kernel-build scripts happier in the presence of symlinks
++ *
++ * Revision 1.1 1998/04/09 03:06:07 henry
++ * sources moved up from linux/net/ipsec
++ *
++ * Revision 1.1.1.1 1998/04/08 05:35:03 henry
++ * RGB's ipsec-0.8pre2.tar.gz ipsec-0.8
++ *
++ * Revision 0.4 1997/01/15 01:28:15 ji
++ * No changes.
++ *
++ * Revision 0.3 1996/11/20 14:48:53 ji
++ * Release update only.
++ *
++ * Revision 0.2 1996/11/02 00:18:33 ji
++ * First limited release.
++ *
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/include/openswan/ipsec_ipip.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,45 @@
++/*
++ * Copyright (C) 2003 Michael Richardson <mcr@sandelman.ottawa.on.ca>
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ * RCSID $Id$
++ */
++
++#ifndef _IPSEC_IPIP_H_
++
++#ifndef IPPROTO_IPIP
++#define IPPROTO_IPIP 4
++#endif /* IPPROTO_ESP */
++
++extern struct xform_functions ipip_xform_funcs[];
++
++#define _IPSEC_IPIP_H_
++
++#endif /* _IPSEC_IPIP_H_ */
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.2 2004/04/05 19:55:05 mcr
++ * Moved from linux/include/freeswan/ipsec_ipip.h,v
++ *
++ * Revision 1.1 2003/12/13 19:10:16 mcr
++ * refactored rcv and xmit code - same as FS 2.05.
++ *
++ * Revision 1.1 2003/12/11 20:14:58 mcr
++ * refactored the xmit code, to move all encapsulation
++ * code into protocol functions. Note that all functions
++ * are essentially done by a single function, which is probably
++ * wrong.
++ * the rcv_functions structures are renamed xform_functions.
++ *
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/include/openswan/ipsec_kversion.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,239 @@
++#ifndef _FREESWAN_KVERSIONS_H
++/*
++ * header file for FreeS/WAN library functions
++ * Copyright (C) 1998, 1999, 2000 Henry Spencer.
++ * Copyright (C) 1999, 2000, 2001 Richard Guy Briggs
++ *
++ * This library is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU Library General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/lgpl.txt>.
++ *
++ * This library 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 Library General Public
++ * License for more details.
++ *
++ * RCSID $Id$
++ */
++#define _FREESWAN_KVERSIONS_H /* seen it, no need to see it again */
++
++/*
++ * this file contains a series of atomic defines that depend upon
++ * kernel version numbers. The kernel versions are arranged
++ * in version-order number (which is often not chronological)
++ * and each clause enables or disables a feature.
++ */
++
++/*
++ * First, assorted kernel-version-dependent trickery.
++ */
++#include <linux/version.h>
++#ifndef KERNEL_VERSION
++#define KERNEL_VERSION(x,y,z) (((x)<<16)+((y)<<8)+(z))
++#endif
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0)
++#define HEADER_CACHE_BIND_21
++#endif
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
++#define SPINLOCK
++#define PROC_FS_21
++#define NETLINK_SOCK
++#define NET_21
++#endif
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,19)
++#define net_device_stats enet_statistics
++#endif
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
++#define SPINLOCK_23
++#define NETDEV_23
++# ifndef CONFIG_IP_ALIAS
++# define CONFIG_IP_ALIAS
++# endif
++#include <linux/socket.h>
++#include <linux/skbuff.h>
++#include <linux/netlink.h>
++# ifdef NETLINK_XFRM
++# define NETDEV_25
++# endif
++#endif
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,25)
++#define PROC_FS_2325
++#undef PROC_FS_21
++#endif
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,30)
++#define PROC_NO_DUMMY
++#endif
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,35)
++#define SKB_COPY_EXPAND
++#endif
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,37)
++#define IP_SELECT_IDENT
++#endif
++
++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,50)) && defined(CONFIG_NETFILTER)
++#define SKB_RESET_NFCT
++#endif
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,2)
++#define IP_SELECT_IDENT_NEW
++#endif
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,4)
++#define IPH_is_SKB_PULLED
++#define SKB_COW_NEW
++#define PROTO_HANDLER_SINGLE_PARM
++#define IP_FRAGMENT_LINEARIZE 1
++#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,4) */
++# ifdef REDHAT_BOGOSITY
++# define IP_SELECT_IDENT_NEW
++# define IPH_is_SKB_PULLED
++# define SKB_COW_NEW
++# define PROTO_HANDLER_SINGLE_PARM
++# endif /* REDHAT_BOGOSITY */
++#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,4) */
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,9)
++#define MALLOC_SLAB
++#define LINUX_KERNEL_HAS_SNPRINTF
++#endif
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
++#define HAVE_NETDEV_PRINTK 1
++#endif
++
++#ifdef NET_21
++# include <linux/in6.h>
++#else
++ /* old kernel in.h has some IPv6 stuff, but not quite enough */
++# define s6_addr16 s6_addr
++# define AF_INET6 10
++# define uint8_t __u8
++# define uint16_t __u16
++# define uint32_t __u32
++# define uint64_t __u64
++#endif
++
++#ifdef NET_21
++# define ipsec_kfree_skb(a) kfree_skb(a)
++#else /* NET_21 */
++# define ipsec_kfree_skb(a) kfree_skb(a, FREE_WRITE)
++#endif /* NET_21 */
++
++#ifdef NETDEV_23
++# define device net_device
++# define ipsec_dev_get dev_get_by_name
++# define __ipsec_dev_get __dev_get_by_name
++# define ipsec_dev_put(x) dev_put(x)
++# define __ipsec_dev_put(x) __dev_put(x)
++# define ipsec_dev_hold(x) dev_hold(x)
++#else /* NETDEV_23 */
++# define ipsec_dev_get dev_get
++# define __ipsec_dev_put(x)
++# define ipsec_dev_put(x)
++# define ipsec_dev_hold(x)
++#endif /* NETDEV_23 */
++
++#ifndef SPINLOCK
++# include <linux/bios32.h>
++ /* simulate spin locks and read/write locks */
++ typedef struct {
++ volatile char lock;
++ } spinlock_t;
++
++ typedef struct {
++ volatile unsigned int lock;
++ } rwlock_t;
++
++# define spin_lock_init(x) { (x)->lock = 0;}
++# define rw_lock_init(x) { (x)->lock = 0; }
++
++# define spin_lock(x) { while ((x)->lock) barrier(); (x)->lock=1;}
++# define spin_lock_irq(x) { cli(); spin_lock(x);}
++# define spin_lock_irqsave(x,flags) { save_flags(flags); spin_lock_irq(x);}
++
++# define spin_unlock(x) { (x)->lock=0;}
++# define spin_unlock_irq(x) { spin_unlock(x); sti();}
++# define spin_unlock_irqrestore(x,flags) { spin_unlock(x); restore_flags(flags);}
++
++# define read_lock(x) spin_lock(x)
++# define read_lock_irq(x) spin_lock_irq(x)
++# define read_lock_irqsave(x,flags) spin_lock_irqsave(x,flags)
++
++# define read_unlock(x) spin_unlock(x)
++# define read_unlock_irq(x) spin_unlock_irq(x)
++# define read_unlock_irqrestore(x,flags) spin_unlock_irqrestore(x,flags)
++
++# define write_lock(x) spin_lock(x)
++# define write_lock_irq(x) spin_lock_irq(x)
++# define write_lock_irqsave(x,flags) spin_lock_irqsave(x,flags)
++
++# define write_unlock(x) spin_unlock(x)
++# define write_unlock_irq(x) spin_unlock_irq(x)
++# define write_unlock_irqrestore(x,flags) spin_unlock_irqrestore(x,flags)
++#endif /* !SPINLOCK */
++
++#ifndef SPINLOCK_23
++# define spin_lock_bh(x) spin_lock_irq(x)
++# define spin_unlock_bh(x) spin_unlock_irq(x)
++
++# define read_lock_bh(x) read_lock_irq(x)
++# define read_unlock_bh(x) read_unlock_irq(x)
++
++# define write_lock_bh(x) write_lock_irq(x)
++# define write_unlock_bh(x) write_unlock_irq(x)
++#endif /* !SPINLOCK_23 */
++
++#ifndef HAVE_NETDEV_PRINTK
++#define netdev_printk(sevlevel, netdev, msglevel, format, arg...) \
++ printk(sevlevel "%s: " format , netdev->name , ## arg)
++#endif
++
++#endif /* _FREESWAN_KVERSIONS_H */
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.9 2004/04/05 19:55:05 mcr
++ * Moved from linux/include/freeswan/ipsec_kversion.h,v
++ *
++ * Revision 1.8 2003/12/13 19:10:16 mcr
++ * refactored rcv and xmit code - same as FS 2.05.
++ *
++ * Revision 1.7 2003/07/31 22:48:08 mcr
++ * derive NET25-ness from presence of NETLINK_XFRM macro.
++ *
++ * Revision 1.6 2003/06/24 20:22:32 mcr
++ * added new global: ipsecdevices[] so that we can keep track of
++ * the ipsecX devices. They will be referenced with dev_hold(),
++ * so 2.2 may need this as well.
++ *
++ * Revision 1.5 2003/04/03 17:38:09 rgb
++ * Centralised ipsec_kfree_skb and ipsec_dev_{get,put}.
++ *
++ * Revision 1.4 2002/04/24 07:36:46 mcr
++ * Moved from ./klips/net/ipsec/ipsec_kversion.h,v
++ *
++ * Revision 1.3 2002/04/12 03:21:17 mcr
++ * three parameter version of ip_select_ident appears first
++ * in 2.4.2 (RH7.1) not 2.4.4.
++ *
++ * Revision 1.2 2002/03/08 21:35:22 rgb
++ * Defined LINUX_KERNEL_HAS_SNPRINTF to shut up compiler warnings after
++ * 2.4.9. (Andreas Piesk).
++ *
++ * Revision 1.1 2002/01/29 02:11:42 mcr
++ * removal of kversions.h - sources that needed it now use ipsec_param.h.
++ * updating of IPv6 structures to match latest in6.h version.
++ * removed dead code from freeswan.h that also duplicated kversions.h
++ * code.
++ *
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/include/openswan/ipsec_life.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,112 @@
++/*
++ * Definitions relevant to IPSEC lifetimes
++ * Copyright (C) 2001 Richard Guy Briggs <rgb@freeswan.org>
++ * and Michael Richardson <mcr@freeswan.org>
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ * RCSID $Id$
++ *
++ * This file derived from ipsec_xform.h on 2001/9/18 by mcr.
++ *
++ */
++
++/*
++ * This file describes the book keeping fields for the
++ * IPsec Security Association Structure. ("ipsec_sa")
++ *
++ * This structure is never allocated directly by kernel code,
++ * (it is always a static/auto or is part of a structure)
++ * so it does not have a reference count.
++ *
++ */
++
++#ifndef _IPSEC_LIFE_H_
++
++/*
++ * _count is total count.
++ * _hard is hard limit (kill SA after this number)
++ * _soft is soft limit (try to renew SA after this number)
++ * _last is used in some special cases.
++ *
++ */
++
++struct ipsec_lifetime64
++{
++ __u64 ipl_count;
++ __u64 ipl_soft;
++ __u64 ipl_hard;
++ __u64 ipl_last;
++};
++
++struct ipsec_lifetimes
++{
++ /* number of bytes processed */
++ struct ipsec_lifetime64 ipl_bytes;
++
++ /* number of packets processed */
++ struct ipsec_lifetime64 ipl_packets;
++
++ /* time since SA was added */
++ struct ipsec_lifetime64 ipl_addtime;
++
++ /* time since SA was first used */
++ struct ipsec_lifetime64 ipl_usetime;
++
++ /* from rfc2367:
++ * For CURRENT, the number of different connections,
++ * endpoints, or flows that the association has been
++ * allocated towards. For HARD and SOFT, the number of
++ * these the association may be allocated towards
++ * before it expires. The concept of a connection,
++ * flow, or endpoint is system specific.
++ *
++ * mcr(2001-9-18) it is unclear what purpose these serve for FreeSWAN.
++ * They are maintained for PF_KEY compatibility.
++ */
++ struct ipsec_lifetime64 ipl_allocations;
++};
++
++enum ipsec_life_alive {
++ ipsec_life_harddied = -1,
++ ipsec_life_softdied = 0,
++ ipsec_life_okay = 1
++};
++
++enum ipsec_life_type {
++ ipsec_life_timebased = 1,
++ ipsec_life_countbased= 0
++};
++
++#define _IPSEC_LIFE_H_
++#endif /* _IPSEC_LIFE_H_ */
++
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.4 2004/04/05 19:55:05 mcr
++ * Moved from linux/include/freeswan/ipsec_life.h,v
++ *
++ * Revision 1.3 2002/04/24 07:36:46 mcr
++ * Moved from ./klips/net/ipsec/ipsec_life.h,v
++ *
++ * Revision 1.2 2001/11/26 09:16:14 rgb
++ * Merge MCR's ipsec_sa, eroute, proc and struct lifetime changes.
++ *
++ * Revision 1.1.2.1 2001/09/25 02:25:58 mcr
++ * lifetime structure created and common functions created.
++ *
++ *
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/include/openswan/ipsec_md5h.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,140 @@
++/*
++ * RCSID $Id$
++ */
++
++/*
++ * The rest of this file is Copyright RSA DSI. See the following comments
++ * for the full Copyright notice.
++ */
++
++#ifndef _IPSEC_MD5H_H_
++#define _IPSEC_MD5H_H_
++
++/* GLOBAL.H - RSAREF types and constants
++ */
++
++/* PROTOTYPES should be set to one if and only if the compiler supports
++ function argument prototyping.
++ The following makes PROTOTYPES default to 0 if it has not already
++ been defined with C compiler flags.
++ */
++#ifndef PROTOTYPES
++#define PROTOTYPES 1
++#endif /* !PROTOTYPES */
++
++/* POINTER defines a generic pointer type */
++typedef __u8 *POINTER;
++
++/* UINT2 defines a two byte word */
++typedef __u16 UINT2;
++
++/* UINT4 defines a four byte word */
++typedef __u32 UINT4;
++
++/* PROTO_LIST is defined depending on how PROTOTYPES is defined above.
++ If using PROTOTYPES, then PROTO_LIST returns the list, otherwise it
++ returns an empty list.
++ */
++
++#if PROTOTYPES
++#define PROTO_LIST(list) list
++#else /* PROTOTYPES */
++#define PROTO_LIST(list) ()
++#endif /* PROTOTYPES */
++
++
++/* MD5.H - header file for MD5C.C
++ */
++
++/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
++rights reserved.
++
++License to copy and use this software is granted provided that it
++is identified as the "RSA Data Security, Inc. MD5 Message-Digest
++Algorithm" in all material mentioning or referencing this software
++or this function.
++
++License is also granted to make and use derivative works provided
++that such works are identified as "derived from the RSA Data
++Security, Inc. MD5 Message-Digest Algorithm" in all material
++mentioning or referencing the derived work.
++
++RSA Data Security, Inc. makes no representations concerning either
++the merchantability of this software or the suitability of this
++software for any particular purpose. It is provided "as is"
++without express or implied warranty of any kind.
++
++These notices must be retained in any copies of any part of this
++documentation and/or software.
++ */
++
++/* MD5 context. */
++typedef struct {
++ UINT4 state[4]; /* state (ABCD) */
++ UINT4 count[2]; /* number of bits, modulo 2^64 (lsb first) */
++ unsigned char buffer[64]; /* input buffer */
++} MD5_CTX;
++
++void MD5Init PROTO_LIST ((void *));
++void MD5Update PROTO_LIST
++ ((void *, unsigned char *, __u32));
++void MD5Final PROTO_LIST ((unsigned char [16], void *));
++
++#endif /* _IPSEC_MD5H_H_ */
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.9 2004/04/05 19:55:05 mcr
++ * Moved from linux/include/freeswan/ipsec_md5h.h,v
++ *
++ * Revision 1.8 2002/09/10 01:45:09 mcr
++ * changed type of MD5_CTX and SHA1_CTX to void * so that
++ * the function prototypes would match, and could be placed
++ * into a pointer to a function.
++ *
++ * Revision 1.7 2002/04/24 07:36:46 mcr
++ * Moved from ./klips/net/ipsec/ipsec_md5h.h,v
++ *
++ * Revision 1.6 1999/12/13 13:59:13 rgb
++ * Quick fix to argument size to Update bugs.
++ *
++ * Revision 1.5 1999/12/07 18:16:23 rgb
++ * Fixed comments at end of #endif lines.
++ *
++ * Revision 1.4 1999/04/06 04:54:26 rgb
++ * Fix/Add RCSID Id: and Log: bits to make PHMDs happy. This includes
++ * patch shell fixes.
++ *
++ * Revision 1.3 1999/01/22 06:19:58 rgb
++ * 64-bit clean-up.
++ *
++ * Revision 1.2 1998/11/30 13:22:54 rgb
++ * Rationalised all the klips kernel file headers. They are much shorter
++ * now and won't conflict under RH5.2.
++ *
++ * Revision 1.1 1998/06/18 21:27:48 henry
++ * move sources from klips/src to klips/net/ipsec, to keep stupid
++ * kernel-build scripts happier in the presence of symlinks
++ *
++ * Revision 1.2 1998/04/23 20:54:03 rgb
++ * Fixed md5 and sha1 include file nesting issues, to be cleaned up when
++ * verified.
++ *
++ * Revision 1.1 1998/04/09 03:04:21 henry
++ * sources moved up from linux/net/ipsec
++ * these two include files modified not to include others except in kernel
++ *
++ * Revision 1.1.1.1 1998/04/08 05:35:03 henry
++ * RGB's ipsec-0.8pre2.tar.gz ipsec-0.8
++ *
++ * Revision 0.4 1997/01/15 01:28:15 ji
++ * No changes.
++ *
++ * Revision 0.3 1996/11/20 14:48:53 ji
++ * Release update only.
++ *
++ * Revision 0.2 1996/11/02 00:18:33 ji
++ * First limited release.
++ *
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/include/openswan/ipsec_param.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,333 @@
++/*
++ * @(#) FreeSWAN tunable paramaters
++ *
++ * Copyright (C) 2001 Richard Guy Briggs <rgb@freeswan.org>
++ * and Michael Richardson <mcr@freeswan.org>
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ * RCSID $Id$
++ *
++ */
++
++/*
++ * This file provides a set of #define's which may be tuned by various
++ * people/configurations. It keeps all compile-time tunables in one place.
++ *
++ * This file should be included before all other IPsec kernel-only files.
++ *
++ */
++
++#ifndef _IPSEC_PARAM_H_
++
++#ifdef __KERNEL__
++#include "ipsec_kversion.h"
++
++/* Set number of ipsecX virtual devices here. */
++/* This must be < exp(field width of IPSEC_DEV_FORMAT) */
++/* It must also be reasonable so as not to overload the memory and CPU */
++/* constraints of the host. */
++#define IPSEC_NUM_IF 4
++/* The field width must be < IF_NAM_SIZ - strlen("ipsec") - 1. */
++/* With "ipsec" being 5 characters, that means 10 is the max field width */
++/* but machine memory and CPU constraints are not likely to tollerate */
++/* more than 3 digits. The default is one digit. */
++/* Update: userland scripts get upset if they can't find "ipsec0", so */
++/* for now, no "0"-padding should be used (which would have been helpful */
++/* to make text-searches work */
++#define IPSEC_DEV_FORMAT "ipsec%d"
++/* For, say, 500 virtual ipsec devices, I would recommend: */
++/* #define IPSEC_NUM_IF 500 */
++/* #define IPSEC_DEV_FORMAT "ipsec%03d" */
++/* Note that the "interfaces=" line in /etc/ipsec.conf would be, um, challenging. */
++
++/* use dynamic ipsecX device allocation */
++#ifndef CONFIG_IPSEC_DYNDEV
++#define CONFIG_IPSEC_DYNDEV 1
++#endif /* CONFIG_IPSEC_DYNDEV */
++
++
++#ifdef CONFIG_IPSEC_BIGGATE
++# define SADB_HASHMOD 8069
++#else /* CONFIG_IPSEC_BIGGATE */
++# define SADB_HASHMOD 257
++#endif /* CONFIG_IPSEC_BIGGATE */
++#endif /* __KERNEL__ */
++
++/*
++ * This is for the SA reference table. This number is related to the
++ * maximum number of SAs that KLIPS can concurrently deal with, plus enough
++ * space for keeping expired SAs around.
++ *
++ * TABLE_MAX_WIDTH is the number of bits that we will use.
++ * MAIN_TABLE_WIDTH is the number of bits used for the primary index table.
++ *
++ */
++#ifndef IPSEC_SA_REF_TABLE_IDX_WIDTH
++# define IPSEC_SA_REF_TABLE_IDX_WIDTH 16
++#endif
++
++#ifndef IPSEC_SA_REF_MAINTABLE_IDX_WIDTH
++# define IPSEC_SA_REF_MAINTABLE_IDX_WIDTH 4
++#endif
++
++#ifndef IPSEC_SA_REF_FREELIST_NUM_ENTRIES
++# define IPSEC_SA_REF_FREELIST_NUM_ENTRIES 256
++#endif
++
++#ifndef IPSEC_SA_REF_CODE
++# define IPSEC_SA_REF_CODE 1
++#endif
++
++#ifdef __KERNEL__
++/* This is defined for 2.4, but not 2.2.... */
++#ifndef ARPHRD_VOID
++# define ARPHRD_VOID 0xFFFF
++#endif
++
++/*
++ * Worry about PROC_FS stuff
++ */
++#if defined(PROC_FS_2325)
++/* kernel 2.4 */
++# define IPSEC_PROC_LAST_ARG ,int *eof,void *data
++# define IPSEC_PROCFS_DEBUG_NO_STATIC
++# define IPSEC_PROC_SUBDIRS
++#else
++/* kernel <2.4 */
++# define IPSEC_PROCFS_DEBUG_NO_STATIC DEBUG_NO_STATIC
++
++# ifndef PROC_NO_DUMMY
++# define IPSEC_PROC_LAST_ARG , int dummy
++# else
++# define IPSEC_PROC_LAST_ARG
++# endif /* !PROC_NO_DUMMY */
++#endif /* PROC_FS_2325 */
++
++#if !defined(LINUX_KERNEL_HAS_SNPRINTF)
++/* GNU CPP specific! */
++# define snprintf(buf, len, fmt...) sprintf(buf, ##fmt)
++#endif /* !LINUX_KERNEL_HAS_SNPRINTF */
++
++#ifdef SPINLOCK
++# ifdef SPINLOCK_23
++# include <linux/spinlock.h> /* *lock* */
++# else /* SPINLOCK_23 */
++# include <asm/spinlock.h> /* *lock* */
++# endif /* SPINLOCK_23 */
++#endif /* SPINLOCK */
++
++#ifndef KLIPS_FIXES_DES_PARITY
++# define KLIPS_FIXES_DES_PARITY 1
++#endif /* !KLIPS_FIXES_DES_PARITY */
++
++/* we don't really want to print these unless there are really big problems */
++#ifndef KLIPS_DIVULGE_CYPHER_KEY
++# define KLIPS_DIVULGE_CYPHER_KEY 0
++#endif /* !KLIPS_DIVULGE_CYPHER_KEY */
++
++#ifndef KLIPS_DIVULGE_HMAC_KEY
++# define KLIPS_DIVULGE_HMAC_KEY 0
++#endif /* !KLIPS_DIVULGE_HMAC_KEY */
++
++#ifndef IPSEC_DISALLOW_IPOPTIONS
++# define IPSEC_DISALLOW_IPOPTIONS 1
++#endif /* !KLIPS_DIVULGE_HMAC_KEY */
++
++/* extra toggles for regression testing */
++#ifdef CONFIG_IPSEC_REGRESS
++
++/*
++ * should pfkey_acquire() become 100% lossy?
++ *
++ */
++extern int sysctl_ipsec_regress_pfkey_lossage;
++#ifndef KLIPS_PFKEY_ACQUIRE_LOSSAGE
++# ifdef CONFIG_IPSEC_PFKEY_ACQUIRE_LOSSAGE
++# define KLIPS_PFKEY_ACQUIRE_LOSSAGE 100
++# else /* CONFIG_IPSEC_PFKEY_ACQUIRE_LOSSAGE */
++/* not by default! */
++# define KLIPS_PFKEY_ACQUIRE_LOSSAGE 0
++# endif /* CONFIG_IPSEC_PFKEY_ACQUIRE_LOSSAGE */
++#endif /* KLIPS_PFKEY_ACQUIRE_LOSSAGE */
++
++#endif /* CONFIG_IPSEC_REGRESS */
++
++
++/*
++ * debugging routines.
++ */
++#ifdef CONFIG_IPSEC_DEBUG
++extern void ipsec_print_ip(struct iphdr *ip);
++
++ #define KLIPS_PRINT(flag, format, args...) \
++ ((flag) ? printk(KERN_INFO format , ## args) : 0)
++ #define KLIPS_PRINTMORE(flag, format, args...) \
++ ((flag) ? printk(format , ## args) : 0)
++ #define KLIPS_IP_PRINT(flag, ip) \
++ ((flag) ? ipsec_print_ip(ip) : 0)
++#else /* CONFIG_IPSEC_DEBUG */
++ #define KLIPS_PRINT(flag, format, args...) do ; while(0)
++ #define KLIPS_PRINTMORE(flag, format, args...) do ; while(0)
++ #define KLIPS_IP_PRINT(flag, ip) do ; while(0)
++#endif /* CONFIG_IPSEC_DEBUG */
++
++
++/*
++ * Stupid kernel API differences in APIs. Not only do some
++ * kernels not have ip_select_ident, but some have differing APIs,
++ * and SuSE has one with one parameter, but no way of checking to
++ * see what is really what.
++ */
++
++#ifdef SUSE_LINUX_2_4_19_IS_STUPID
++#define KLIPS_IP_SELECT_IDENT(iph, skb) ip_select_ident(iph)
++#else
++
++/* simplest case, nothing */
++#if !defined(IP_SELECT_IDENT)
++#define KLIPS_IP_SELECT_IDENT(iph, skb) do { iph->id = htons(ip_id_count++); } while(0)
++#endif
++
++/* kernels > 2.3.37-ish */
++#if defined(IP_SELECT_IDENT) && !defined(IP_SELECT_IDENT_NEW)
++#define KLIPS_IP_SELECT_IDENT(iph, skb) ip_select_ident(iph, skb->dst)
++#endif
++
++/* kernels > 2.4.2 */
++#if defined(IP_SELECT_IDENT) && defined(IP_SELECT_IDENT_NEW)
++#define KLIPS_IP_SELECT_IDENT(iph, skb) ip_select_ident(iph, skb->dst, NULL)
++#endif
++
++#endif /* SUSE_LINUX_2_4_19_IS_STUPID */
++
++/*
++ * make klips fail test:east-espiv-01.
++ * exploit is at testing/attacks/espiv
++ *
++ */
++#define KLIPS_IMPAIRMENT_ESPIV_CBC_ATTACK 0
++
++
++/* IP_FRAGMENT_LINEARIZE is set in freeswan.h if Kernel > 2.4.4 */
++#ifndef IP_FRAGMENT_LINEARIZE
++# define IP_FRAGMENT_LINEARIZE 0
++#endif /* IP_FRAGMENT_LINEARIZE */
++#endif /* __KERNEL__ */
++
++#define _IPSEC_PARAM_H_
++#endif /* _IPSEC_PARAM_H_ */
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.24 2004/04/05 19:55:06 mcr
++ * Moved from linux/include/freeswan/ipsec_param.h,v
++ *
++ * Revision 1.23 2003/12/13 19:10:16 mcr
++ * refactored rcv and xmit code - same as FS 2.05.
++ *
++ * Revision 1.22 2003/10/31 02:27:05 mcr
++ * pulled up port-selector patches and sa_id elimination.
++ *
++ * Revision 1.21.4.1 2003/10/29 01:10:19 mcr
++ * elimited "struct sa_id"
++ *
++ * Revision 1.21 2003/04/03 17:38:18 rgb
++ * Centralised ipsec_kfree_skb and ipsec_dev_{get,put}.
++ * Change indentation for readability.
++ *
++ * Revision 1.20 2003/03/14 08:09:26 rgb
++ * Fixed up CONFIG_IPSEC_DYNDEV definitions.
++ *
++ * Revision 1.19 2003/01/30 02:31:43 rgb
++ *
++ * Rename SAref table macro names for clarity.
++ *
++ * Revision 1.18 2002/09/30 19:06:26 rgb
++ * Reduce default table to 16 bits width.
++ *
++ * Revision 1.17 2002/09/20 15:40:29 rgb
++ * Define switch to activate new SAref code.
++ * Prefix macros with "IPSEC_".
++ * Rework saref freelist.
++ * Restrict some bits to kernel context for use to klips utils.
++ *
++ * Revision 1.16 2002/09/20 05:00:31 rgb
++ * Define switch to divulge hmac keys for debugging.
++ * Added IPOPTIONS switch.
++ *
++ * Revision 1.15 2002/09/19 02:34:24 mcr
++ * define IPSEC_PROC_SUBDIRS if we are 2.4, and use that in ipsec_proc.c
++ * to decide if we are to create /proc/net/ipsec/.
++ *
++ * Revision 1.14 2002/08/30 01:20:54 mcr
++ * reorganized 2.0/2.2/2.4 procfs support macro so match
++ * 2.4 values/typedefs.
++ *
++ * Revision 1.13 2002/07/28 22:03:28 mcr
++ * added some documentation to SA_REF_*
++ * turned on fix for ESPIV attack, now that we have the attack code.
++ *
++ * Revision 1.12 2002/07/26 08:48:31 rgb
++ * Added SA ref table code.
++ *
++ * Revision 1.11 2002/07/23 02:57:45 rgb
++ * Define ARPHRD_VOID for < 2.4 kernels.
++ *
++ * Revision 1.10 2002/05/27 21:37:28 rgb
++ * Set the defaults sanely for those adventurous enough to try more than 1
++ * digit of ipsec devices.
++ *
++ * Revision 1.9 2002/05/27 18:56:07 rgb
++ * Convert to dynamic ipsec device allocation.
++ *
++ * Revision 1.8 2002/04/24 07:36:47 mcr
++ * Moved from ./klips/net/ipsec/ipsec_param.h,v
++ *
++ * Revision 1.7 2002/04/20 00:12:25 rgb
++ * Added esp IV CBC attack fix, disabled.
++ *
++ * Revision 1.6 2002/01/29 02:11:42 mcr
++ * removal of kversions.h - sources that needed it now use ipsec_param.h.
++ * updating of IPv6 structures to match latest in6.h version.
++ * removed dead code from freeswan.h that also duplicated kversions.h
++ * code.
++ *
++ * Revision 1.5 2002/01/28 19:22:01 mcr
++ * by default, turn off LINEARIZE option
++ * (let kversions.h turn it on)
++ *
++ * Revision 1.4 2002/01/20 20:19:36 mcr
++ * renamed option to IP_FRAGMENT_LINEARIZE.
++ *
++ * Revision 1.3 2002/01/12 02:57:25 mcr
++ * first regression test causes acquire messages to be lost
++ * 100% of the time. This is to help testing of pluto.
++ *
++ * Revision 1.2 2001/11/26 09:16:14 rgb
++ * Merge MCR's ipsec_sa, eroute, proc and struct lifetime changes.
++ *
++ * Revision 1.1.2.3 2001/10/23 04:40:16 mcr
++ * added #define for DIVULGING session keys in debug output.
++ *
++ * Revision 1.1.2.2 2001/10/22 20:53:25 mcr
++ * added a define to control forcing of DES parity.
++ *
++ * Revision 1.1.2.1 2001/09/25 02:20:19 mcr
++ * many common kernel configuration questions centralized.
++ * more things remain that should be moved from freeswan.h.
++ *
++ *
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/include/openswan/ipsec_policy.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,216 @@
++#ifndef _IPSEC_POLICY_H
++/*
++ * policy interface file between pluto and applications
++ * Copyright (C) 2003 Michael Richardson <mcr@freeswan.org>
++ *
++ * This library is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU Library General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/lgpl.txt>.
++ *
++ * This library 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 Library General Public
++ * License for more details.
++ *
++ * RCSID $Id$
++ */
++#define _IPSEC_POLICY_H /* seen it, no need to see it again */
++
++
++/*
++ * this file defines an interface between an application (or rather an
++ * application library) and a key/policy daemon. It provides for inquiries
++ * as to the current state of a connected socket, as well as for general
++ * questions.
++ *
++ * In general, the interface is defined as a series of functional interfaces,
++ * and the policy messages should be internal. However, because this is in
++ * fact an ABI between pieces of the system that may get compiled and revised
++ * seperately, this ABI must be public and revision controlled.
++ *
++ * It is expected that the daemon will always support previous versions.
++ */
++
++#define IPSEC_POLICY_MSG_REVISION (unsigned)200305061
++
++enum ipsec_policy_command {
++ IPSEC_CMD_QUERY_FD = 1,
++ IPSEC_CMD_QUERY_HOSTPAIR = 2,
++ IPSEC_CMD_QUERY_DSTONLY = 3,
++};
++
++struct ipsec_policy_msg_head {
++ u_int32_t ipm_version;
++ u_int32_t ipm_msg_len;
++ u_int32_t ipm_msg_type;
++ u_int32_t ipm_msg_seq;
++};
++
++enum ipsec_privacy_quality {
++ IPSEC_PRIVACY_NONE = 0,
++ IPSEC_PRIVACY_INTEGRAL = 4, /* not private at all. AH-like */
++ IPSEC_PRIVACY_UNKNOWN = 8, /* something is claimed, but details unavail */
++ IPSEC_PRIVACY_ROT13 = 12, /* trivially breakable, i.e. 1DES */
++ IPSEC_PRIVACY_GAK = 16, /* known eavesdroppers */
++ IPSEC_PRIVACY_PRIVATE = 32, /* secure for at least a decade */
++ IPSEC_PRIVACY_STRONG = 64, /* ridiculously secure */
++ IPSEC_PRIVACY_TORTOISE = 192, /* even stronger, but very slow */
++ IPSEC_PRIVACY_OTP = 224, /* some kind of *true* one time pad */
++};
++
++enum ipsec_bandwidth_quality {
++ IPSEC_QOS_UNKNOWN = 0, /* unknown bandwidth */
++ IPSEC_QOS_INTERACTIVE = 16, /* reasonably moderate jitter, moderate fast.
++ Good enough for telnet/ssh. */
++ IPSEC_QOS_VOIP = 32, /* faster crypto, predicable jitter */
++ IPSEC_QOS_FTP = 64, /* higher throughput crypto, perhaps hardware
++ offloaded, but latency/jitter may be bad */
++ IPSEC_QOS_WIRESPEED = 128, /* expect to be able to fill your pipe */
++};
++
++/* moved from programs/pluto/constants.h */
++/* IPsec AH transform values
++ * RFC2407 The Internet IP security Domain of Interpretation for ISAKMP 4.4.3
++ * and in http://www.iana.org/assignments/isakmp-registry
++ */
++enum ipsec_authentication_algo {
++ AH_MD5=2,
++ AH_SHA=3,
++ AH_DES=4,
++ AH_SHA2_256=5,
++ AH_SHA2_384=6,
++ AH_SHA2_512=7
++};
++
++/* IPsec ESP transform values
++ * RFC2407 The Internet IP security Domain of Interpretation for ISAKMP 4.4.4
++ * and from http://www.iana.org/assignments/isakmp-registry
++ */
++
++enum ipsec_cipher_algo {
++ ESP_reserved=0,
++ ESP_DES_IV64=1,
++ ESP_DES=2,
++ ESP_3DES=3,
++ ESP_RC5=4,
++ ESP_IDEA=5,
++ ESP_CAST=6,
++ ESP_BLOWFISH=7,
++ ESP_3IDEA=8,
++ ESP_DES_IV32=9,
++ ESP_RC4=10,
++ ESP_NULL=11,
++ ESP_AES=12,
++};
++
++/* IPCOMP transform values
++ * RFC2407 The Internet IP security Domain of Interpretation for ISAKMP 4.4.5
++ */
++
++enum ipsec_comp_algo {
++ IPCOMP_OUI= 1,
++ IPCOMP_DEFLATE= 2,
++ IPCOMP_LZS= 3,
++ IPCOMP_V42BIS= 4
++};
++
++/* Identification type values
++ * RFC 2407 The Internet IP security Domain of Interpretation for ISAKMP 4.6.2.1
++ */
++
++enum ipsec_id_type {
++ ID_IMPOSSIBLE= (-2), /* private to Pluto */
++ ID_MYID= (-1), /* private to Pluto */
++ ID_NONE= 0, /* private to Pluto */
++ ID_IPV4_ADDR= 1,
++ ID_FQDN= 2,
++ ID_USER_FQDN= 3,
++ ID_IPV4_ADDR_SUBNET= 4,
++ ID_IPV6_ADDR= 5,
++ ID_IPV6_ADDR_SUBNET= 6,
++ ID_IPV4_ADDR_RANGE= 7,
++ ID_IPV6_ADDR_RANGE= 8,
++ ID_DER_ASN1_DN= 9,
++ ID_DER_ASN1_GN= 10,
++ ID_KEY_ID= 11
++};
++
++/* Certificate type values
++ * RFC 2408 ISAKMP, chapter 3.9
++ */
++enum ipsec_cert_type {
++ CERT_NONE= 0, /* none, or guess from file contents */
++ CERT_PKCS7_WRAPPED_X509= 1, /* self-signed certificate from disk */
++ CERT_PGP= 2,
++ CERT_DNS_SIGNED_KEY= 3, /* KEY RR from DNS */
++ CERT_X509_SIGNATURE= 4,
++ CERT_X509_KEY_EXCHANGE= 5,
++ CERT_KERBEROS_TOKENS= 6,
++ CERT_CRL= 7,
++ CERT_ARL= 8,
++ CERT_SPKI= 9,
++ CERT_X509_ATTRIBUTE= 10,
++ CERT_RAW_RSA= 11, /* raw RSA from config file */
++};
++
++/* a SIG record in ASCII */
++struct ipsec_dns_sig {
++ char fqdn[256];
++ char dns_sig[768]; /* empty string if not signed */
++};
++
++struct ipsec_raw_key {
++ char id_name[256];
++ char fs_keyid[8];
++};
++
++struct ipsec_identity {
++ enum ipsec_id_type ii_type;
++ enum ipsec_cert_type ii_format;
++ union {
++ struct ipsec_dns_sig ipsec_dns_signed;
++ /* some thing for PGP */
++ /* some thing for PKIX */
++ struct ipsec_raw_key ipsec_raw_key;
++ } ii_credential;
++};
++
++#define IPSEC_MAX_CREDENTIALS 32
++
++struct ipsec_policy_cmd_query {
++ struct ipsec_policy_msg_head head;
++
++ /* Query section */
++ ip_address query_local; /* us */
++ ip_address query_remote; /* them */
++ u_short src_port, dst_port;
++
++ /* Answer section */
++ enum ipsec_privacy_quality strength;
++ enum ipsec_bandwidth_quality bandwidth;
++ enum ipsec_authentication_algo auth_detail;
++ enum ipsec_cipher_algo esp_detail;
++ enum ipsec_comp_algo comp_detail;
++
++ int credential_count;
++
++ struct ipsec_identity credentials[IPSEC_MAX_CREDENTIALS];
++};
++
++#define IPSEC_POLICY_SOCKET "/var/run/pluto.info"
++
++/* prototypes */
++extern err_t ipsec_policy_lookup(int fd, struct ipsec_policy_cmd_query *result);
++extern err_t ipsec_policy_init(void);
++extern err_t ipsec_policy_final(void);
++extern err_t ipsec_policy_readmsg(int policysock,
++ unsigned char *buf, size_t buflen);
++extern err_t ipsec_policy_sendrecv(unsigned char *buf, size_t buflen);
++extern err_t ipsec_policy_cgilookup(struct ipsec_policy_cmd_query *result);
++
++
++extern const char *ipsec_policy_version_code(void);
++extern const char *ipsec_policy_version_string(void);
++
++#endif /* _IPSEC_POLICY_H */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/include/openswan/ipsec_proto.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,151 @@
++/*
++ * @(#) prototypes for FreeSWAN functions
++ *
++ * Copyright (C) 2001 Richard Guy Briggs <rgb@freeswan.org>
++ * and Michael Richardson <mcr@freeswan.org>
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ * RCSID $Id$
++ *
++ */
++
++#ifndef _IPSEC_PROTO_H_
++
++#include "ipsec_param.h"
++
++/*
++ * This file is a kernel only file that declares prototypes for
++ * all intra-module function calls and global data structures.
++ *
++ * Include this file last.
++ *
++ */
++
++/* ipsec_init.c */
++extern struct prng ipsec_prng;
++
++/* ipsec_sa.c */
++extern struct ipsec_sa *ipsec_sadb_hash[SADB_HASHMOD];
++extern spinlock_t tdb_lock;
++extern int ipsec_sadb_init(void);
++
++extern struct ipsec_sa *ipsec_sa_getbyid(ip_said *);
++extern int ipsec_sa_put(struct ipsec_sa *);
++extern /* void */ int ipsec_sa_del(struct ipsec_sa *);
++extern /* void */ int ipsec_sa_delchain(struct ipsec_sa *);
++extern /* void */ int ipsec_sa_add(struct ipsec_sa *);
++
++extern int ipsec_sadb_cleanup(__u8);
++extern int ipsec_sa_wipe(struct ipsec_sa *);
++
++/* debug declarations */
++
++/* ipsec_proc.c */
++extern int ipsec_proc_init(void);
++extern void ipsec_proc_cleanup(void);
++
++/* ipsec_radij.c */
++extern int ipsec_makeroute(struct sockaddr_encap *ea,
++ struct sockaddr_encap *em,
++ ip_said said,
++ uint32_t pid,
++ struct sk_buff *skb,
++ struct ident *ident_s,
++ struct ident *ident_d);
++
++extern int ipsec_breakroute(struct sockaddr_encap *ea,
++ struct sockaddr_encap *em,
++ struct sk_buff **first,
++ struct sk_buff **last);
++
++int ipsec_radijinit(void);
++int ipsec_cleareroutes(void);
++int ipsec_radijcleanup(void);
++
++/* ipsec_life.c */
++extern enum ipsec_life_alive ipsec_lifetime_check(struct ipsec_lifetime64 *il64,
++ const char *lifename,
++ const char *saname,
++ enum ipsec_life_type ilt,
++ enum ipsec_direction idir,
++ struct ipsec_sa *ips);
++
++
++extern int ipsec_lifetime_format(char *buffer,
++ int buflen,
++ char *lifename,
++ enum ipsec_life_type timebaselife,
++ struct ipsec_lifetime64 *lifetime);
++
++extern void ipsec_lifetime_update_hard(struct ipsec_lifetime64 *lifetime,
++ __u64 newvalue);
++
++extern void ipsec_lifetime_update_soft(struct ipsec_lifetime64 *lifetime,
++ __u64 newvalue);
++
++
++
++
++#ifdef CONFIG_IPSEC_DEBUG
++
++extern int debug_xform;
++extern int debug_eroute;
++extern int debug_spi;
++extern int debug_netlink;
++
++#endif /* CONFIG_IPSEC_DEBUG */
++
++
++
++
++#define _IPSEC_PROTO_H
++#endif /* _IPSEC_PROTO_H_ */
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.8 2004/04/05 19:55:06 mcr
++ * Moved from linux/include/freeswan/ipsec_proto.h,v
++ *
++ * Revision 1.7 2003/10/31 02:27:05 mcr
++ * pulled up port-selector patches and sa_id elimination.
++ *
++ * Revision 1.6.30.1 2003/10/29 01:10:19 mcr
++ * elimited "struct sa_id"
++ *
++ * Revision 1.6 2002/05/23 07:13:48 rgb
++ * Added ipsec_sa_put() for releasing an ipsec_sa refcount.
++ *
++ * Revision 1.5 2002/05/14 02:36:40 rgb
++ * Converted reference from ipsec_sa_put to ipsec_sa_add to avoid confusion
++ * with "put" usage in the kernel.
++ *
++ * Revision 1.4 2002/04/24 07:36:47 mcr
++ * Moved from ./klips/net/ipsec/ipsec_proto.h,v
++ *
++ * Revision 1.3 2002/04/20 00:12:25 rgb
++ * Added esp IV CBC attack fix, disabled.
++ *
++ * Revision 1.2 2001/11/26 09:16:15 rgb
++ * Merge MCR's ipsec_sa, eroute, proc and struct lifetime changes.
++ *
++ * Revision 1.1.2.1 2001/09/25 02:21:01 mcr
++ * ipsec_proto.h created to keep prototypes rather than deal with
++ * cyclic dependancies of structures and prototypes in .h files.
++ *
++ *
++ *
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ *
++ */
++
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/include/openswan/ipsec_radij.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,176 @@
++/*
++ * @(#) Definitions relevant to the IPSEC <> radij tree interfacing
++ * Copyright (C) 1996, 1997 John Ioannidis.
++ * Copyright (C) 1998, 1999, 2000, 2001 Richard Guy Briggs.
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ * RCSID $Id$
++ */
++
++#ifndef _IPSEC_RADIJ_H
++
++#include <openswan.h>
++
++int ipsec_walk(char *);
++
++int ipsec_rj_walker_procprint(struct radij_node *, void *);
++int ipsec_rj_walker_delete(struct radij_node *, void *);
++
++/* This structure is used to pass information between
++ * ipsec_eroute_get_info and ipsec_rj_walker_procprint
++ * (through rj_walktree) and between calls of ipsec_rj_walker_procprint.
++ */
++struct wsbuf
++{
++ /* from caller of ipsec_eroute_get_info: */
++ char *const buffer; /* start of buffer provided */
++ const int length; /* length of buffer provided */
++ const off_t offset; /* file position of first character of interest */
++ /* accumulated by ipsec_rj_walker_procprint: */
++ int len; /* number of character filled into buffer */
++ off_t begin; /* file position contained in buffer[0] (<=offset) */
++};
++
++extern struct radij_node_head *rnh;
++extern spinlock_t eroute_lock;
++
++struct eroute * ipsec_findroute(struct sockaddr_encap *);
++
++#define O1(x) (int)(((x)>>24)&0xff)
++#define O2(x) (int)(((x)>>16)&0xff)
++#define O3(x) (int)(((x)>>8)&0xff)
++#define O4(x) (int)(((x))&0xff)
++
++#ifdef CONFIG_IPSEC_DEBUG
++extern int debug_radij;
++void rj_dumptrees(void);
++
++#define DB_RJ_DUMPTREES 0x0001
++#define DB_RJ_FINDROUTE 0x0002
++#endif /* CONFIG_IPSEC_DEBUG */
++
++#define _IPSEC_RADIJ_H
++#endif
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.21 2004/04/29 11:06:42 ken
++ * Last bits from 2.06 procfs updates
++ *
++ * Revision 1.20 2004/04/06 02:49:08 mcr
++ * pullup of algo code from alg-branch.
++ *
++ * Revision 1.19 2004/04/05 19:55:06 mcr
++ * Moved from linux/include/freeswan/ipsec_radij.h,v
++ *
++ * Revision 1.18 2002/04/24 07:36:47 mcr
++ * Moved from ./klips/net/ipsec/ipsec_radij.h,v
++ *
++ * Revision 1.17 2001/11/26 09:23:49 rgb
++ * Merge MCR's ipsec_sa, eroute, proc and struct lifetime changes.
++ *
++ * Revision 1.16.2.1 2001/09/25 02:21:17 mcr
++ * ipsec_proto.h created to keep prototypes rather than deal with
++ * cyclic dependancies of structures and prototypes in .h files.
++ *
++ * Revision 1.16 2001/09/15 16:24:04 rgb
++ * Re-inject first and last HOLD packet when an eroute REPLACE is done.
++ *
++ * Revision 1.15 2001/09/14 16:58:37 rgb
++ * Added support for storing the first and last packets through a HOLD.
++ *
++ * Revision 1.14 2001/09/08 21:13:32 rgb
++ * Added pfkey ident extension support for ISAKMPd. (NetCelo)
++ *
++ * Revision 1.13 2001/06/14 19:35:09 rgb
++ * Update copyright date.
++ *
++ * Revision 1.12 2001/05/27 06:12:11 rgb
++ * Added structures for pid, packet count and last access time to eroute.
++ * Added packet count to beginning of /proc/net/ipsec_eroute.
++ *
++ * Revision 1.11 2000/09/08 19:12:56 rgb
++ * Change references from DEBUG_IPSEC to CONFIG_IPSEC_DEBUG.
++ *
++ * Revision 1.10 1999/11/17 15:53:39 rgb
++ * Changed all occurrences of #include "../../../lib/freeswan.h"
++ * to #include <freeswan.h> which works due to -Ilibfreeswan in the
++ * klips/net/ipsec/Makefile.
++ *
++ * Revision 1.9 1999/10/01 00:01:23 rgb
++ * Added eroute structure locking.
++ *
++ * Revision 1.8 1999/04/11 00:28:59 henry
++ * GPL boilerplate
++ *
++ * Revision 1.7 1999/04/06 04:54:26 rgb
++ * Fix/Add RCSID Id: and Log: bits to make PHMDs happy. This includes
++ * patch shell fixes.
++ *
++ * Revision 1.6 1999/01/22 06:23:26 rgb
++ * Cruft clean-out.
++ *
++ * Revision 1.5 1998/10/25 02:42:08 rgb
++ * Change return type on ipsec_breakroute and ipsec_makeroute and add an
++ * argument to be able to transmit more infomation about errors.
++ *
++ * Revision 1.4 1998/10/19 14:44:29 rgb
++ * Added inclusion of freeswan.h.
++ * sa_id structure implemented and used: now includes protocol.
++ *
++ * Revision 1.3 1998/07/28 00:03:31 rgb
++ * Comment out temporary inet_nto4u() kluge.
++ *
++ * Revision 1.2 1998/07/14 18:22:00 rgb
++ * Add function to clear the eroute table.
++ *
++ * Revision 1.1 1998/06/18 21:27:49 henry
++ * move sources from klips/src to klips/net/ipsec, to keep stupid
++ * kernel-build scripts happier in the presence of symlinks
++ *
++ * Revision 1.5 1998/05/25 20:30:38 rgb
++ * Remove temporary ipsec_walk, rj_deltree and rj_delnodes functions.
++ *
++ * Rename ipsec_rj_walker (ipsec_walk) to ipsec_rj_walker_procprint and
++ * add ipsec_rj_walker_delete.
++ *
++ * Revision 1.4 1998/05/21 13:02:56 rgb
++ * Imported definitions from ipsec_radij.c and radij.c to support /proc 3k
++ * limit fix.
++ *
++ * Revision 1.3 1998/04/21 21:29:09 rgb
++ * Rearrange debug switches to change on the fly debug output from user
++ * space. Only kernel changes checked in at this time. radij.c was also
++ * changed to temporarily remove buggy debugging code in rj_delete causing
++ * an OOPS and hence, netlink device open errors.
++ *
++ * Revision 1.2 1998/04/14 17:30:39 rgb
++ * Fix up compiling errors for radij tree memory reclamation.
++ *
++ * Revision 1.1 1998/04/09 03:06:10 henry
++ * sources moved up from linux/net/ipsec
++ *
++ * Revision 1.1.1.1 1998/04/08 05:35:04 henry
++ * RGB's ipsec-0.8pre2.tar.gz ipsec-0.8
++ *
++ * Revision 0.4 1997/01/15 01:28:15 ji
++ * No changes.
++ *
++ * Revision 0.3 1996/11/20 14:39:04 ji
++ * Minor cleanups.
++ * Rationalized debugging code.
++ *
++ * Revision 0.2 1996/11/02 00:18:33 ji
++ * First limited release.
++ *
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/include/openswan/ipsec_rcv.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,279 @@
++/*
++ *
++ * Copyright (C) 1996, 1997 John Ioannidis.
++ * Copyright (C) 1998, 1999, 2000, 2001 Richard Guy Briggs.
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ * RCSID $Id$
++ */
++
++#ifndef IPSEC_RCV_H
++#define IPSEC_RCV_H
++
++#include "openswan/ipsec_auth.h"
++
++#define DB_RX_PKTRX 0x0001
++#define DB_RX_PKTRX2 0x0002
++#define DB_RX_DMP 0x0004
++#define DB_RX_IPSA 0x0010
++#define DB_RX_XF 0x0020
++#define DB_RX_IPAD 0x0040
++#define DB_RX_INAU 0x0080
++#define DB_RX_OINFO 0x0100
++#define DB_RX_OINFO2 0x0200
++#define DB_RX_OH 0x0400
++#define DB_RX_REPLAY 0x0800
++
++#ifdef __KERNEL__
++/* struct options; */
++
++#define __NO_VERSION__
++#include <linux/module.h>
++#include <linux/config.h> /* for CONFIG_IP_FORWARD */
++#include <linux/version.h>
++#include <openswan.h>
++
++#define IPSEC_BIRTH_TEMPLATE_MAXLEN 256
++
++struct ipsec_birth_reply {
++ int packet_template_len;
++ unsigned char packet_template[IPSEC_BIRTH_TEMPLATE_MAXLEN];
++};
++
++extern struct ipsec_birth_reply ipsec_ipv4_birth_packet;
++extern struct ipsec_birth_reply ipsec_ipv6_birth_packet;
++
++enum ipsec_rcv_value {
++ IPSEC_RCV_LASTPROTO=1,
++ IPSEC_RCV_OK=0,
++ IPSEC_RCV_BADPROTO=-1,
++ IPSEC_RCV_BADLEN=-2,
++ IPSEC_RCV_ESP_BADALG=-3,
++ IPSEC_RCV_3DES_BADBLOCKING=-4,
++ IPSEC_RCV_ESP_DECAPFAIL=-5,
++ IPSEC_RCV_DECAPFAIL=-6,
++ IPSEC_RCV_SAIDNOTFOUND=-7,
++ IPSEC_RCV_IPCOMPALONE=-8,
++ IPSEC_RCV_IPCOMPFAILED=-10,
++ IPSEC_RCV_SAIDNOTLIVE=-11,
++ IPSEC_RCV_FAILEDINBOUND=-12,
++ IPSEC_RCV_LIFETIMEFAILED=-13,
++ IPSEC_RCV_BADAUTH=-14,
++ IPSEC_RCV_REPLAYFAILED=-15,
++ IPSEC_RCV_AUTHFAILED=-16,
++ IPSEC_RCV_REPLAYROLLED=-17,
++ IPSEC_RCV_BAD_DECRYPT=-18
++};
++
++struct ipsec_rcv_state {
++ struct sk_buff *skb;
++ struct net_device_stats *stats;
++ struct iphdr *ipp;
++ struct ipsec_sa *ipsp;
++ int len;
++ int ilen;
++ int authlen;
++ int hard_header_len;
++ int iphlen;
++ struct auth_alg *authfuncs;
++ ip_said said;
++ char sa[SATOT_BUF];
++ size_t sa_len;
++ __u8 next_header;
++ __u8 hash[AH_AMAX];
++ char ipsaddr_txt[ADDRTOA_BUF];
++ char ipdaddr_txt[ADDRTOA_BUF];
++ __u8 *octx;
++ __u8 *ictx;
++ int ictx_len;
++ int octx_len;
++ union {
++ struct {
++ struct esphdr *espp;
++ } espstuff;
++ struct {
++ struct ahhdr *ahp;
++ } ahstuff;
++ struct {
++ struct ipcomphdr *compp;
++ } ipcompstuff;
++ } protostuff;
++#ifdef CONFIG_IPSEC_NAT_TRAVERSAL
++ __u8 natt_type;
++ __u16 natt_sport;
++ __u16 natt_dport;
++ int natt_len;
++#endif
++};
++
++extern int
++#ifdef PROTO_HANDLER_SINGLE_PARM
++ipsec_rcv(struct sk_buff *skb);
++#else /* PROTO_HANDLER_SINGLE_PARM */
++ipsec_rcv(struct sk_buff *skb,
++#ifdef NET_21
++ unsigned short xlen);
++#else /* NET_21 */
++ struct device *dev,
++ struct options *opt,
++ __u32 daddr,
++ unsigned short len,
++ __u32 saddr,
++ int redo,
++ struct inet_protocol *protocol);
++#endif /* NET_21 */
++#endif /* PROTO_HANDLER_SINGLE_PARM */
++
++#ifdef CONFIG_IPSEC_DEBUG
++extern int debug_rcv;
++#endif /* CONFIG_IPSEC_DEBUG */
++#define ipsec_rcv_dmp(_x,_y, _z) if (debug_rcv && sysctl_ipsec_debug_verbose) ipsec_dmp(_x,_y,_z)
++
++extern int sysctl_ipsec_inbound_policy_check;
++#endif /* __KERNEL__ */
++
++#endif /* IPSEC_RCV_H */
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.21 2004/04/06 02:49:08 mcr
++ * pullup of algo code from alg-branch.
++ *
++ * Revision 1.20 2004/04/05 19:55:06 mcr
++ * Moved from linux/include/freeswan/ipsec_rcv.h,v
++ *
++ * Revision 1.19 2003/12/15 18:13:09 mcr
++ * when compiling with NAT traversal, don't assume that the
++ * kernel has been patched, unless CONFIG_IPSEC_NAT_NON_ESP
++ * is set.
++ *
++ * Revision 1.18 2003/12/13 19:10:16 mcr
++ * refactored rcv and xmit code - same as FS 2.05.
++ *
++ * Revision 1.17 2002/09/03 16:32:32 mcr
++ * definitions of ipsec_birth_reply.
++ *
++ * Revision 1.16 2002/05/14 02:36:00 rgb
++ * Change references to _TDB to _IPSA.
++ *
++ * Revision 1.15 2002/04/24 07:36:47 mcr
++ * Moved from ./klips/net/ipsec/ipsec_rcv.h,v
++ *
++ * Revision 1.14 2001/09/07 22:15:48 rgb
++ * Fix for removal of transport layer protocol handler arg in 2.4.4.
++ *
++ * Revision 1.13 2001/06/14 19:35:09 rgb
++ * Update copyright date.
++ *
++ * Revision 1.12 2001/03/16 07:36:44 rgb
++ * Fixed #endif comment to sate compiler.
++ *
++ * Revision 1.11 2000/09/21 04:34:21 rgb
++ * Moved declaration of sysctl_ipsec_inbound_policy_check outside
++ * CONFIG_IPSEC_DEBUG. (MB)
++ *
++ * Revision 1.10 2000/09/18 02:36:10 rgb
++ * Exported sysctl_ipsec_inbound_policy_check for skb_decompress().
++ *
++ * Revision 1.9 2000/09/08 19:12:56 rgb
++ * Change references from DEBUG_IPSEC to CONFIG_IPSEC_DEBUG.
++ *
++ * Revision 1.8 1999/11/18 04:09:19 rgb
++ * Replaced all kernel version macros to shorter, readable form.
++ *
++ * Revision 1.7 1999/05/25 01:45:37 rgb
++ * Fix version macros for 2.0.x as a module.
++ *
++ * Revision 1.6 1999/05/08 21:24:27 rgb
++ * Add includes for 2.2.x include into net/ipv4/protocol.c
++ *
++ * Revision 1.5 1999/05/05 22:02:32 rgb
++ * Add a quick and dirty port to 2.2 kernels by Marc Boucher <marc@mbsi.ca>.
++ *
++ * Revision 1.4 1999/04/11 00:28:59 henry
++ * GPL boilerplate
++ *
++ * Revision 1.3 1999/04/06 04:54:27 rgb
++ * Fix/Add RCSID Id: and Log: bits to make PHMDs happy. This includes
++ * patch shell fixes.
++ *
++ * Revision 1.2 1999/01/22 20:06:59 rgb
++ * Fixed cut-and-paste error from ipsec_esp.h.
++ *
++ * Revision 1.1 1999/01/21 20:29:12 rgb
++ * Converted from transform switching to algorithm switching.
++ *
++ * Log: ipsec_esp.h,v
++ * Revision 1.4 1998/08/12 00:07:32 rgb
++ * Added data structures for new xforms: null, {,3}dessha1.
++ *
++ * Revision 1.3 1998/07/14 15:57:01 rgb
++ * Add #ifdef __KERNEL__ to protect kernel-only structures.
++ *
++ * Revision 1.2 1998/06/25 19:33:46 rgb
++ * Add prototype for protocol receive function.
++ * Rearrange for more logical layout.
++ *
++ * Revision 1.1 1998/06/18 21:27:45 henry
++ * move sources from klips/src to klips/net/ipsec, to keep stupid
++ * kernel-build scripts happier in the presence of symlinks
++ *
++ * Revision 1.6 1998/06/05 02:28:08 rgb
++ * Minor comment fix.
++ *
++ * Revision 1.5 1998/05/27 22:34:00 rgb
++ * Changed structures to accomodate key separation.
++ *
++ * Revision 1.4 1998/05/18 22:28:43 rgb
++ * Disable key printing facilities from /proc/net/ipsec_*.
++ *
++ * Revision 1.3 1998/04/21 21:29:07 rgb
++ * Rearrange debug switches to change on the fly debug output from user
++ * space. Only kernel changes checked in at this time. radij.c was also
++ * changed to temporarily remove buggy debugging code in rj_delete causing
++ * an OOPS and hence, netlink device open errors.
++ *
++ * Revision 1.2 1998/04/12 22:03:20 rgb
++ * Updated ESP-3DES-HMAC-MD5-96,
++ * ESP-DES-HMAC-MD5-96,
++ * AH-HMAC-MD5-96,
++ * AH-HMAC-SHA1-96 since Henry started freeswan cvs repository
++ * from old standards (RFC182[5-9] to new (as of March 1998) drafts.
++ *
++ * Fixed eroute references in /proc/net/ipsec*.
++ *
++ * Started to patch module unloading memory leaks in ipsec_netlink and
++ * radij tree unloading.
++ *
++ * Revision 1.1 1998/04/09 03:06:00 henry
++ * sources moved up from linux/net/ipsec
++ *
++ * Revision 1.1.1.1 1998/04/08 05:35:02 henry
++ * RGB's ipsec-0.8pre2.tar.gz ipsec-0.8
++ *
++ * Revision 0.5 1997/06/03 04:24:48 ji
++ * Added ESP-3DES-MD5-96 transform.
++ *
++ * Revision 0.4 1997/01/15 01:28:15 ji
++ * Added definitions for new ESP transforms.
++ *
++ * Revision 0.3 1996/11/20 14:35:48 ji
++ * Minor Cleanup.
++ * Rationalized debugging code.
++ *
++ * Revision 0.2 1996/11/02 00:18:33 ji
++ * First limited release.
++ *
++ *
++ */
++
++
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/include/openswan/ipsec_sa.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,341 @@
++/*
++ * @(#) Definitions of IPsec Security Association (ipsec_sa)
++ *
++ * Copyright (C) 2001, 2002, 2003
++ * Richard Guy Briggs <rgb@freeswan.org>
++ * and Michael Richardson <mcr@freeswan.org>
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ * RCSID $Id$
++ *
++ * This file derived from ipsec_xform.h on 2001/9/18 by mcr.
++ *
++ */
++
++/*
++ * This file describes the IPsec Security Association Structure.
++ *
++ * This structure keeps track of a single transform that may be done
++ * to a set of packets. It can describe applying the transform or
++ * apply the reverse. (e.g. compression vs expansion). However, it
++ * only describes one at a time. To describe both, two structures would
++ * be used, but since the sides of the transform are performed
++ * on different machines typically it is usual to have only one side
++ * of each association.
++ *
++ */
++
++#ifndef _IPSEC_SA_H_
++
++#ifdef __KERNEL__
++#include "ipsec_stats.h"
++#include "ipsec_life.h"
++#include "ipsec_eroute.h"
++#endif /* __KERNEL__ */
++#include "ipsec_param.h"
++
++
++/* SAs are held in a table.
++ * Entries in this table are referenced by IPsecSAref_t values.
++ * IPsecSAref_t values are conceptually subscripts. Because
++ * we want to allocate the table piece-meal, the subscripting
++ * is implemented with two levels, a bit like paged virtual memory.
++ * This representation mechanism is known as an Iliffe Vector.
++ *
++ * The Main table (AKA the refTable) consists of 2^IPSEC_SA_REF_MAINTABLE_IDX_WIDTH
++ * pointers to subtables.
++ * Each subtable has 2^IPSEC_SA_REF_SUBTABLE_IDX_WIDTH entries, each of which
++ * is a pointer to an SA.
++ *
++ * An IPsecSAref_t contains either an exceptional value (signified by the
++ * high-order bit being on) or a reference to a table entry. A table entry
++ * reference has the subtable subscript in the low-order
++ * IPSEC_SA_REF_SUBTABLE_IDX_WIDTH bits and the Main table subscript
++ * in the next lowest IPSEC_SA_REF_MAINTABLE_IDX_WIDTH bits.
++ *
++ * The Maintable entry for an IPsecSAref_t x, a pointer to its subtable, is
++ * IPsecSAref2table(x). It is of type struct IPsecSArefSubTable *.
++ *
++ * The pointer to the SA for x is IPsecSAref2SA(x). It is of type
++ * struct ipsec_sa*. The macro definition clearly shows the two-level
++ * access needed to find the SA pointer.
++ *
++ * The Maintable is allocated when IPsec is initialized.
++ * Each subtable is allocated when needed, but the first is allocated
++ * when IPsec is initialized.
++ *
++ * IPsecSAref_t is designed to be smaller than an NFmark so that
++ * they can be stored in NFmarks and still leave a few bits for other
++ * purposes. The spare bits are in the low order of the NFmark
++ * but in the high order of the IPsecSAref_t, so conversion is required.
++ * We pick the upper bits of NFmark on the theory that they are less likely to
++ * interfere with more pedestrian uses of nfmark.
++ */
++
++
++typedef unsigned short int IPsecRefTableUnusedCount;
++
++#define IPSEC_SA_REF_TABLE_NUM_ENTRIES (1 << IPSEC_SA_REF_TABLE_IDX_WIDTH)
++
++#ifdef __KERNEL__
++#if ((IPSEC_SA_REF_TABLE_IDX_WIDTH - (1 + IPSEC_SA_REF_MAINTABLE_IDX_WIDTH)) < 0)
++#error "IPSEC_SA_REF_TABLE_IDX_WIDTH("IPSEC_SA_REF_TABLE_IDX_WIDTH") MUST be < 1 + IPSEC_SA_REF_MAINTABLE_IDX_WIDTH("IPSEC_SA_REF_MAINTABLE_IDX_WIDTH")"
++#endif
++
++#define IPSEC_SA_REF_SUBTABLE_IDX_WIDTH (IPSEC_SA_REF_TABLE_IDX_WIDTH - IPSEC_SA_REF_MAINTABLE_IDX_WIDTH)
++
++#define IPSEC_SA_REF_MAINTABLE_NUM_ENTRIES (1 << IPSEC_SA_REF_MAINTABLE_IDX_WIDTH)
++#define IPSEC_SA_REF_SUBTABLE_NUM_ENTRIES (1 << IPSEC_SA_REF_SUBTABLE_IDX_WIDTH)
++
++#ifdef CONFIG_NETFILTER
++#define IPSEC_SA_REF_HOST_FIELD(x) ((struct sk_buff*)(x))->nfmark
++#define IPSEC_SA_REF_HOST_FIELD_TYPE typeof(IPSEC_SA_REF_HOST_FIELD(NULL))
++#else /* CONFIG_NETFILTER */
++/* just make it work for now, it doesn't matter, since there is no nfmark */
++#define IPSEC_SA_REF_HOST_FIELD_TYPE unsigned long
++#endif /* CONFIG_NETFILTER */
++#define IPSEC_SA_REF_HOST_FIELD_WIDTH (8 * sizeof(IPSEC_SA_REF_HOST_FIELD_TYPE))
++#define IPSEC_SA_REF_FIELD_WIDTH (8 * sizeof(IPsecSAref_t))
++
++#define IPSEC_SA_REF_MASK (IPSEC_SAREF_NULL >> (IPSEC_SA_REF_FIELD_WIDTH - IPSEC_SA_REF_TABLE_IDX_WIDTH))
++#define IPSEC_SA_REF_TABLE_MASK ((IPSEC_SAREF_NULL >> (IPSEC_SA_REF_FIELD_WIDTH - IPSEC_SA_REF_MAINTABLE_IDX_WIDTH)) << IPSEC_SA_REF_SUBTABLE_IDX_WIDTH)
++#define IPSEC_SA_REF_ENTRY_MASK (IPSEC_SAREF_NULL >> (IPSEC_SA_REF_FIELD_WIDTH - IPSEC_SA_REF_SUBTABLE_IDX_WIDTH))
++
++#define IPsecSAref2table(x) (((x) & IPSEC_SA_REF_TABLE_MASK) >> IPSEC_SA_REF_SUBTABLE_IDX_WIDTH)
++#define IPsecSAref2entry(x) ((x) & IPSEC_SA_REF_ENTRY_MASK)
++#define IPsecSArefBuild(x,y) (((x) << IPSEC_SA_REF_SUBTABLE_IDX_WIDTH) + (y))
++
++#define IPsecSAref2SA(x) (ipsec_sadb.refTable[IPsecSAref2table(x)]->entry[IPsecSAref2entry(x)])
++#define IPsecSA2SAref(x) ((x)->ips_ref)
++
++#define EMT_INBOUND 0x01 /* SA direction, 1=inbound */
++
++/* 'struct ipsec_sa' should be 64bit aligned when allocated. */
++struct ipsec_sa
++{
++ IPsecSAref_t ips_ref; /* reference table entry number */
++ atomic_t ips_refcount; /* reference count for this struct */
++ struct ipsec_sa *ips_hnext; /* next in hash chain */
++ struct ipsec_sa *ips_inext; /* pointer to next xform */
++ struct ipsec_sa *ips_onext; /* pointer to prev xform */
++
++ struct ifnet *ips_rcvif; /* related rcv encap interface */
++
++ ip_said ips_said; /* SA ID */
++
++ __u32 ips_seq; /* seq num of msg that initiated this SA */
++ __u32 ips_pid; /* PID of process that initiated this SA */
++ __u8 ips_authalg; /* auth algorithm for this SA */
++ __u8 ips_encalg; /* enc algorithm for this SA */
++
++ struct ipsec_stats ips_errs;
++
++ __u8 ips_replaywin; /* replay window size */
++ __u8 ips_state; /* state of SA */
++ __u32 ips_replaywin_lastseq; /* last pkt sequence num */
++ __u64 ips_replaywin_bitmap; /* bitmap of received pkts */
++ __u32 ips_replaywin_maxdiff; /* max pkt sequence difference */
++
++ __u32 ips_flags; /* generic xform flags */
++
++
++ struct ipsec_lifetimes ips_life; /* lifetime records */
++
++ /* selector information */
++ __u8 ips_transport_protocol; /* protocol for this SA, if ports are involved */
++ struct sockaddr*ips_addr_s; /* src sockaddr */
++ struct sockaddr*ips_addr_d; /* dst sockaddr */
++ struct sockaddr*ips_addr_p; /* proxy sockaddr */
++ __u16 ips_addr_s_size;
++ __u16 ips_addr_d_size;
++ __u16 ips_addr_p_size;
++ ip_address ips_flow_s;
++ ip_address ips_flow_d;
++ ip_address ips_mask_s;
++ ip_address ips_mask_d;
++
++ __u16 ips_key_bits_a; /* size of authkey in bits */
++ __u16 ips_auth_bits; /* size of authenticator in bits */
++ __u16 ips_key_bits_e; /* size of enckey in bits */
++ __u16 ips_iv_bits; /* size of IV in bits */
++ __u8 ips_iv_size;
++ __u16 ips_key_a_size;
++ __u16 ips_key_e_size;
++
++ caddr_t ips_key_a; /* authentication key */
++ caddr_t ips_key_e; /* encryption key */
++ caddr_t ips_iv; /* Initialisation Vector */
++
++ struct ident ips_ident_s; /* identity src */
++ struct ident ips_ident_d; /* identity dst */
++
++#ifdef CONFIG_IPSEC_IPCOMP
++ __u16 ips_comp_adapt_tries; /* ipcomp self-adaption tries */
++ __u16 ips_comp_adapt_skip; /* ipcomp self-adaption to-skip */
++ __u64 ips_comp_ratio_cbytes; /* compressed bytes */
++ __u64 ips_comp_ratio_dbytes; /* decompressed (or uncompressed) bytes */
++#endif /* CONFIG_IPSEC_IPCOMP */
++
++#ifdef CONFIG_IPSEC_NAT_TRAVERSAL
++ __u8 ips_natt_type;
++ __u8 ips_natt_reserved[3];
++ __u16 ips_natt_sport;
++ __u16 ips_natt_dport;
++
++ struct sockaddr *ips_natt_oa;
++ __u16 ips_natt_oa_size;
++ __u16 ips_natt_reserved2;
++#endif
++
++#if 0
++ __u32 ips_sens_dpd;
++ __u8 ips_sens_sens_level;
++ __u8 ips_sens_sens_len;
++ __u64* ips_sens_sens_bitmap;
++ __u8 ips_sens_integ_level;
++ __u8 ips_sens_integ_len;
++ __u64* ips_sens_integ_bitmap;
++#endif
++ struct ipsec_alg_enc *ips_alg_enc;
++ struct ipsec_alg_auth *ips_alg_auth;
++ IPsecSAref_t ips_ref_rel;
++};
++
++struct IPsecSArefSubTable
++{
++ struct ipsec_sa* entry[IPSEC_SA_REF_SUBTABLE_NUM_ENTRIES];
++};
++
++struct ipsec_sadb {
++ struct IPsecSArefSubTable* refTable[IPSEC_SA_REF_MAINTABLE_NUM_ENTRIES];
++ IPsecSAref_t refFreeList[IPSEC_SA_REF_FREELIST_NUM_ENTRIES];
++ int refFreeListHead;
++ int refFreeListTail;
++ IPsecSAref_t refFreeListCont;
++ IPsecSAref_t said_hash[SADB_HASHMOD];
++ spinlock_t sadb_lock;
++};
++
++extern struct ipsec_sadb ipsec_sadb;
++
++extern int ipsec_SAref_recycle(void);
++extern int ipsec_SArefSubTable_alloc(unsigned table);
++extern int ipsec_saref_freelist_init(void);
++extern int ipsec_sadb_init(void);
++extern struct ipsec_sa *ipsec_sa_alloc(int*error); /* pass in error var by pointer */
++extern IPsecSAref_t ipsec_SAref_alloc(int*erorr); /* pass in error var by pointer */
++extern int ipsec_sa_free(struct ipsec_sa* ips);
++extern int ipsec_sa_put(struct ipsec_sa *ips);
++extern int ipsec_sa_add(struct ipsec_sa *ips);
++extern int ipsec_sa_del(struct ipsec_sa *ips);
++extern int ipsec_sa_delchain(struct ipsec_sa *ips);
++extern int ipsec_sadb_cleanup(__u8 proto);
++extern int ipsec_sadb_free(void);
++extern int ipsec_sa_wipe(struct ipsec_sa *ips);
++#endif /* __KERNEL__ */
++
++enum ipsec_direction {
++ ipsec_incoming = 1,
++ ipsec_outgoing = 2
++};
++
++#define _IPSEC_SA_H_
++#endif /* _IPSEC_SA_H_ */
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.19 2004/04/05 19:55:06 mcr
++ * Moved from linux/include/freeswan/ipsec_sa.h,v
++ *
++ * Revision 1.18 2004/04/05 19:41:05 mcr
++ * merged alg-branch code.
++ *
++ * Revision 1.17.2.1 2003/12/22 15:25:52 jjo
++ * . Merged algo-0.8.1-rc11-test1 into alg-branch
++ *
++ * Revision 1.17 2003/12/10 01:20:06 mcr
++ * NAT-traversal patches to KLIPS.
++ *
++ * Revision 1.16 2003/10/31 02:27:05 mcr
++ * pulled up port-selector patches and sa_id elimination.
++ *
++ * Revision 1.15.4.1 2003/10/29 01:10:19 mcr
++ * elimited "struct sa_id"
++ *
++ * Revision 1.15 2003/05/11 00:53:09 mcr
++ * IPsecSAref_t and macros were moved to freeswan.h.
++ *
++ * Revision 1.14 2003/02/12 19:31:55 rgb
++ * Fixed bug in "file seen" machinery.
++ * Updated copyright year.
++ *
++ * Revision 1.13 2003/01/30 02:31:52 rgb
++ *
++ * Re-wrote comments describing SAref system for accuracy.
++ * Rename SAref table macro names for clarity.
++ * Convert IPsecSAref_t from signed to unsigned to fix apparent SAref exhaustion bug.
++ * Transmit error code through to caller from callee for better diagnosis of problems.
++ * Enclose all macro arguments in parens to avoid any possible obscrure bugs.
++ *
++ * Revision 1.12 2002/10/07 18:31:19 rgb
++ * Change comment to reflect the flexible nature of the main and sub-table widths.
++ * Added a counter for the number of unused entries in each subtable.
++ * Further break up host field type macro to host field.
++ * Move field width sanity checks to ipsec_sa.c
++ * Define a mask for an entire saref.
++ *
++ * Revision 1.11 2002/09/20 15:40:33 rgb
++ * Re-write most of the SAref macros and types to eliminate any pointer references to Entrys.
++ * Fixed SAref/nfmark macros.
++ * Rework saref freeslist.
++ * Place all ipsec sadb globals into one struct.
++ * Restrict some bits to kernel context for use to klips utils.
++ *
++ * Revision 1.10 2002/09/20 05:00:34 rgb
++ * Update copyright date.
++ *
++ * Revision 1.9 2002/09/17 17:19:29 mcr
++ * make it compile even if there is no netfilter - we lost
++ * functionality, but it works, especially on 2.2.
++ *
++ * Revision 1.8 2002/07/28 22:59:53 mcr
++ * clarified/expanded one comment.
++ *
++ * Revision 1.7 2002/07/26 08:48:31 rgb
++ * Added SA ref table code.
++ *
++ * Revision 1.6 2002/05/31 17:27:48 rgb
++ * Comment fix.
++ *
++ * Revision 1.5 2002/05/27 18:55:03 rgb
++ * Remove final vistiges of tdb references via IPSEC_KLIPS1_COMPAT.
++ *
++ * Revision 1.4 2002/05/23 07:13:36 rgb
++ * Convert "usecount" to "refcount" to remove ambiguity.
++ *
++ * Revision 1.3 2002/04/24 07:36:47 mcr
++ * Moved from ./klips/net/ipsec/ipsec_sa.h,v
++ *
++ * Revision 1.2 2001/11/26 09:16:15 rgb
++ * Merge MCR's ipsec_sa, eroute, proc and struct lifetime changes.
++ *
++ * Revision 1.1.2.1 2001/09/25 02:24:58 mcr
++ * struct tdb -> struct ipsec_sa.
++ * sa(tdb) manipulation functions renamed and moved to ipsec_sa.c
++ * ipsec_xform.c removed. header file still contains useful things.
++ *
++ *
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/include/openswan/ipsec_sha1.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,79 @@
++/*
++ * RCSID $Id$
++ */
++
++/*
++ * Here is the original comment from the distribution:
++
++SHA-1 in C
++By Steve Reid <steve@edmweb.com>
++100% Public Domain
++
++ * Adapted for use by the IPSEC code by John Ioannidis
++ */
++
++
++#ifndef _IPSEC_SHA1_H_
++#define _IPSEC_SHA1_H_
++
++typedef struct
++{
++ __u32 state[5];
++ __u32 count[2];
++ __u8 buffer[64];
++} SHA1_CTX;
++
++void SHA1Transform(__u32 state[5], __u8 buffer[64]);
++void SHA1Init(void *context);
++void SHA1Update(void *context, unsigned char *data, __u32 len);
++void SHA1Final(unsigned char digest[20], void *context);
++
++
++#endif /* _IPSEC_SHA1_H_ */
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.8 2004/04/05 19:55:07 mcr
++ * Moved from linux/include/freeswan/ipsec_sha1.h,v
++ *
++ * Revision 1.7 2002/09/10 01:45:09 mcr
++ * changed type of MD5_CTX and SHA1_CTX to void * so that
++ * the function prototypes would match, and could be placed
++ * into a pointer to a function.
++ *
++ * Revision 1.6 2002/04/24 07:36:47 mcr
++ * Moved from ./klips/net/ipsec/ipsec_sha1.h,v
++ *
++ * Revision 1.5 1999/12/13 13:59:13 rgb
++ * Quick fix to argument size to Update bugs.
++ *
++ * Revision 1.4 1999/12/07 18:16:23 rgb
++ * Fixed comments at end of #endif lines.
++ *
++ * Revision 1.3 1999/04/06 04:54:27 rgb
++ * Fix/Add RCSID Id: and Log: bits to make PHMDs happy. This includes
++ * patch shell fixes.
++ *
++ * Revision 1.2 1998/11/30 13:22:54 rgb
++ * Rationalised all the klips kernel file headers. They are much shorter
++ * now and won't conflict under RH5.2.
++ *
++ * Revision 1.1 1998/06/18 21:27:50 henry
++ * move sources from klips/src to klips/net/ipsec, to keep stupid
++ * kernel-build scripts happier in the presence of symlinks
++ *
++ * Revision 1.2 1998/04/23 20:54:05 rgb
++ * Fixed md5 and sha1 include file nesting issues, to be cleaned up when
++ * verified.
++ *
++ * Revision 1.1 1998/04/09 03:04:21 henry
++ * sources moved up from linux/net/ipsec
++ * these two include files modified not to include others except in kernel
++ *
++ * Revision 1.1.1.1 1998/04/08 05:35:04 henry
++ * RGB's ipsec-0.8pre2.tar.gz ipsec-0.8
++ *
++ * Revision 0.4 1997/01/15 01:28:15 ji
++ * New transform
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/include/openswan/ipsec_stats.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,75 @@
++/*
++ * @(#) definition of ipsec_stats structure
++ *
++ * Copyright (C) 2001 Richard Guy Briggs <rgb@freeswan.org>
++ * and Michael Richardson <mcr@freeswan.org>
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ * RCSID $Id$
++ *
++ */
++
++/*
++ * This file describes the errors/statistics that FreeSWAN collects.
++ */
++
++#ifndef _IPSEC_STATS_H_
++
++struct ipsec_stats {
++ __u32 ips_alg_errs; /* number of algorithm errors */
++ __u32 ips_auth_errs; /* # of authentication errors */
++ __u32 ips_encsize_errs; /* # of encryption size errors*/
++ __u32 ips_encpad_errs; /* # of encryption pad errors*/
++ __u32 ips_replaywin_errs; /* # of pkt sequence errors */
++};
++
++extern int ipsec_snprintf(char * buf, ssize_t size, const char *fmt, ...);
++
++#define _IPSEC_STATS_H_
++#endif /* _IPSEC_STATS_H_ */
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.6 2004/04/05 19:55:07 mcr
++ * Moved from linux/include/freeswan/ipsec_stats.h,v
++ *
++ * Revision 1.5 2004/04/05 19:41:05 mcr
++ * merged alg-branch code.
++ *
++ * Revision 1.4 2004/03/28 20:27:19 paul
++ * Included tested and confirmed fixes mcr made and dhr verified for
++ * snprint statements. Changed one other snprintf to use ipsec_snprintf
++ * so it wouldnt break compatibility with 2.0/2.2 kernels. Verified with
++ * dhr. (thanks dhr!)
++ *
++ * Revision 1.4 2004/03/24 01:58:31 mcr
++ * sprintf->snprintf for formatting into proc buffer.
++ *
++ * Revision 1.3.34.1 2004/04/05 04:30:46 mcr
++ * patches for alg-branch to compile/work with 2.x openswan
++ *
++ * Revision 1.3 2002/04/24 07:36:47 mcr
++ * Moved from ./klips/net/ipsec/ipsec_stats.h,v
++ *
++ * Revision 1.2 2001/11/26 09:16:16 rgb
++ * Merge MCR's ipsec_sa, eroute, proc and struct lifetime changes.
++ *
++ * Revision 1.1.2.1 2001/09/25 02:27:00 mcr
++ * statistics moved to seperate structure.
++ *
++ *
++ *
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/include/openswan/ipsec_tunnel.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,265 @@
++/*
++ * IPSEC tunneling code
++ * Copyright (C) 1996, 1997 John Ioannidis.
++ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 Richard Guy Briggs.
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ * RCSID $Id$
++ */
++
++
++#ifdef NET_21
++# define DEV_QUEUE_XMIT(skb, device, pri) {\
++ skb->dev = device; \
++ neigh_compat_output(skb); \
++ /* skb->dst->output(skb); */ \
++ }
++# define ICMP_SEND(skb_in, type, code, info, dev) \
++ icmp_send(skb_in, type, code, htonl(info))
++# define IP_SEND(skb, dev) \
++ ip_send(skb);
++#else /* NET_21 */
++# define DEV_QUEUE_XMIT(skb, device, pri) {\
++ dev_queue_xmit(skb, device, pri); \
++ }
++# define ICMP_SEND(skb_in, type, code, info, dev) \
++ icmp_send(skb_in, type, code, info, dev)
++# define IP_SEND(skb, dev) \
++ if(ntohs(iph->tot_len) > physmtu) { \
++ ip_fragment(NULL, skb, dev, 0); \
++ ipsec_kfree_skb(skb); \
++ } else { \
++ dev_queue_xmit(skb, dev, SOPRI_NORMAL); \
++ }
++#endif /* NET_21 */
++
++
++/*
++ * Heavily based on drivers/net/new_tunnel.c. Lots
++ * of ideas also taken from the 2.1.x version of drivers/net/shaper.c
++ */
++
++struct ipsectunnelconf
++{
++ __u32 cf_cmd;
++ union
++ {
++ char cfu_name[12];
++ } cf_u;
++#define cf_name cf_u.cfu_name
++};
++
++#define IPSEC_SET_DEV (SIOCDEVPRIVATE)
++#define IPSEC_DEL_DEV (SIOCDEVPRIVATE + 1)
++#define IPSEC_CLR_DEV (SIOCDEVPRIVATE + 2)
++
++#ifdef __KERNEL__
++#include <linux/version.h>
++#ifndef KERNEL_VERSION
++# define KERNEL_VERSION(x,y,z) (((x)<<16)+((y)<<8)+(z))
++#endif
++struct ipsecpriv
++{
++ struct sk_buff_head sendq;
++ struct device *dev;
++ struct wait_queue *wait_queue;
++ char locked;
++ int (*hard_start_xmit) (struct sk_buff *skb,
++ struct device *dev);
++ int (*hard_header) (struct sk_buff *skb,
++ struct device *dev,
++ unsigned short type,
++ void *daddr,
++ void *saddr,
++ unsigned len);
++#ifdef NET_21
++ int (*rebuild_header)(struct sk_buff *skb);
++#else /* NET_21 */
++ int (*rebuild_header)(void *buff, struct device *dev,
++ unsigned long raddr, struct sk_buff *skb);
++#endif /* NET_21 */
++ int (*set_mac_address)(struct device *dev, void *addr);
++#ifndef NET_21
++ void (*header_cache_bind)(struct hh_cache **hhp, struct device *dev,
++ unsigned short htype, __u32 daddr);
++#endif /* !NET_21 */
++ void (*header_cache_update)(struct hh_cache *hh, struct device *dev, unsigned char * haddr);
++ struct net_device_stats *(*get_stats)(struct device *dev);
++ struct net_device_stats mystats;
++ int mtu; /* What is the desired MTU? */
++};
++
++extern char ipsec_tunnel_c_version[];
++
++extern struct device *ipsecdevices[IPSEC_NUM_IF];
++
++int ipsec_tunnel_init_devices(void);
++
++/* void */ int ipsec_tunnel_cleanup_devices(void);
++
++extern /* void */ int ipsec_init(void);
++
++extern int ipsec_tunnel_start_xmit(struct sk_buff *skb, struct device *dev);
++
++#ifdef CONFIG_IPSEC_DEBUG
++extern int debug_tunnel;
++extern int sysctl_ipsec_debug_verbose;
++#endif /* CONFIG_IPSEC_DEBUG */
++#endif /* __KERNEL__ */
++
++#ifdef CONFIG_IPSEC_DEBUG
++#define DB_TN_INIT 0x0001
++#define DB_TN_PROCFS 0x0002
++#define DB_TN_XMIT 0x0010
++#define DB_TN_OHDR 0x0020
++#define DB_TN_CROUT 0x0040
++#define DB_TN_OXFS 0x0080
++#define DB_TN_REVEC 0x0100
++#endif /* CONFIG_IPSEC_DEBUG */
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.29 2004/04/05 19:55:07 mcr
++ * Moved from linux/include/freeswan/ipsec_tunnel.h,v
++ *
++ * Revision 1.28 2003/06/24 20:22:32 mcr
++ * added new global: ipsecdevices[] so that we can keep track of
++ * the ipsecX devices. They will be referenced with dev_hold(),
++ * so 2.2 may need this as well.
++ *
++ * Revision 1.27 2003/04/03 17:38:09 rgb
++ * Centralised ipsec_kfree_skb and ipsec_dev_{get,put}.
++ *
++ * Revision 1.26 2003/02/12 19:32:20 rgb
++ * Updated copyright year.
++ *
++ * Revision 1.25 2002/05/27 18:56:07 rgb
++ * Convert to dynamic ipsec device allocation.
++ *
++ * Revision 1.24 2002/04/24 07:36:48 mcr
++ * Moved from ./klips/net/ipsec/ipsec_tunnel.h,v
++ *
++ * Revision 1.23 2001/11/06 19:50:44 rgb
++ * Moved IP_SEND, ICMP_SEND, DEV_QUEUE_XMIT macros to ipsec_tunnel.h for
++ * use also by pfkey_v2_parser.c
++ *
++ * Revision 1.22 2001/09/15 16:24:05 rgb
++ * Re-inject first and last HOLD packet when an eroute REPLACE is done.
++ *
++ * Revision 1.21 2001/06/14 19:35:10 rgb
++ * Update copyright date.
++ *
++ * Revision 1.20 2000/09/15 11:37:02 rgb
++ * Merge in heavily modified Svenning Soerensen's <svenning@post5.tele.dk>
++ * IPCOMP zlib deflate code.
++ *
++ * Revision 1.19 2000/09/08 19:12:56 rgb
++ * Change references from DEBUG_IPSEC to CONFIG_IPSEC_DEBUG.
++ *
++ * Revision 1.18 2000/07/28 13:50:54 rgb
++ * Changed enet_statistics to net_device_stats and added back compatibility
++ * for pre-2.1.19.
++ *
++ * Revision 1.17 1999/11/19 01:12:15 rgb
++ * Purge unneeded proc_info prototypes, now that static linking uses
++ * dynamic proc_info registration.
++ *
++ * Revision 1.16 1999/11/18 18:51:00 rgb
++ * Changed all device registrations for static linking to
++ * dynamic to reduce the number and size of patches.
++ *
++ * Revision 1.15 1999/11/18 04:14:21 rgb
++ * Replaced all kernel version macros to shorter, readable form.
++ * Added CONFIG_PROC_FS compiler directives in case it is shut off.
++ * Added Marc Boucher's 2.3.25 proc patches.
++ *
++ * Revision 1.14 1999/05/25 02:50:10 rgb
++ * Fix kernel version macros for 2.0.x static linking.
++ *
++ * Revision 1.13 1999/05/25 02:41:06 rgb
++ * Add ipsec_klipsdebug support for static linking.
++ *
++ * Revision 1.12 1999/05/05 22:02:32 rgb
++ * Add a quick and dirty port to 2.2 kernels by Marc Boucher <marc@mbsi.ca>.
++ *
++ * Revision 1.11 1999/04/29 15:19:50 rgb
++ * Add return values to init and cleanup functions.
++ *
++ * Revision 1.10 1999/04/16 16:02:39 rgb
++ * Bump up macro to 4 ipsec I/Fs.
++ *
++ * Revision 1.9 1999/04/15 15:37:25 rgb
++ * Forward check changes from POST1_00 branch.
++ *
++ * Revision 1.5.2.1 1999/04/02 04:26:14 rgb
++ * Backcheck from HEAD, pre1.0.
++ *
++ * Revision 1.8 1999/04/11 00:29:01 henry
++ * GPL boilerplate
++ *
++ * Revision 1.7 1999/04/06 04:54:28 rgb
++ * Fix/Add RCSID Id: and Log: bits to make PHMDs happy. This includes
++ * patch shell fixes.
++ *
++ * Revision 1.6 1999/03/31 05:44:48 rgb
++ * Keep PMTU reduction private.
++ *
++ * Revision 1.5 1999/02/10 22:31:20 rgb
++ * Change rebuild_header member to reflect generality of link layer.
++ *
++ * Revision 1.4 1998/12/01 13:22:04 rgb
++ * Added support for debug printing of version info.
++ *
++ * Revision 1.3 1998/07/29 20:42:46 rgb
++ * Add a macro for clearing all tunnel devices.
++ * Rearrange structures and declarations for sharing with userspace.
++ *
++ * Revision 1.2 1998/06/25 20:01:45 rgb
++ * Make prototypes available for ipsec_init and ipsec proc_dir_entries
++ * for static linking.
++ *
++ * Revision 1.1 1998/06/18 21:27:50 henry
++ * move sources from klips/src to klips/net/ipsec, to keep stupid
++ * kernel-build scripts happier in the presence of symlinks
++ *
++ * Revision 1.3 1998/05/18 21:51:50 rgb
++ * Added macros for num of I/F's and a procfs debug switch.
++ *
++ * Revision 1.2 1998/04/21 21:29:09 rgb
++ * Rearrange debug switches to change on the fly debug output from user
++ * space. Only kernel changes checked in at this time. radij.c was also
++ * changed to temporarily remove buggy debugging code in rj_delete causing
++ * an OOPS and hence, netlink device open errors.
++ *
++ * Revision 1.1 1998/04/09 03:06:13 henry
++ * sources moved up from linux/net/ipsec
++ *
++ * Revision 1.1.1.1 1998/04/08 05:35:05 henry
++ * RGB's ipsec-0.8pre2.tar.gz ipsec-0.8
++ *
++ * Revision 0.5 1997/06/03 04:24:48 ji
++ * Added transport mode.
++ * Changed the way routing is done.
++ * Lots of bug fixes.
++ *
++ * Revision 0.4 1997/01/15 01:28:15 ji
++ * No changes.
++ *
++ * Revision 0.3 1996/11/20 14:39:04 ji
++ * Minor cleanups.
++ * Rationalized debugging code.
++ *
++ * Revision 0.2 1996/11/02 00:18:33 ji
++ * First limited release.
++ *
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/include/openswan/ipsec_xform.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,254 @@
++/*
++ * Definitions relevant to IPSEC transformations
++ * Copyright (C) 1996, 1997 John Ioannidis.
++ * Copyright (C) 1998, 1999, 2000, 2001 Richard Guy Briggs.
++ * COpyright (C) 2003 Michael Richardson <mcr@sandelman.ottawa.on.ca>
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ * RCSID $Id$
++ */
++
++#ifndef _IPSEC_XFORM_H_
++
++#include <openswan.h>
++
++#define XF_NONE 0 /* No transform set */
++#define XF_IP4 1 /* IPv4 inside IPv4 */
++#define XF_AHMD5 2 /* AH MD5 */
++#define XF_AHSHA 3 /* AH SHA */
++#define XF_ESP3DES 5 /* ESP DES3-CBC */
++#define XF_AHHMACMD5 6 /* AH-HMAC-MD5 with opt replay prot */
++#define XF_AHHMACSHA1 7 /* AH-HMAC-SHA1 with opt replay prot */
++#define XF_ESP3DESMD5 9 /* triple DES, HMAC-MD-5, 128-bits of authentication */
++#define XF_ESP3DESMD596 10 /* triple DES, HMAC-MD-5, 96-bits of authentication */
++#define XF_ESPNULLMD596 12 /* NULL, HMAC-MD-5 with 96-bits of authentication */
++#define XF_ESPNULLSHA196 13 /* NULL, HMAC-SHA-1 with 96-bits of authentication */
++#define XF_ESP3DESSHA196 14 /* triple DES, HMAC-SHA-1, 96-bits of authentication */
++#define XF_IP6 15 /* IPv6 inside IPv6 */
++#define XF_COMPDEFLATE 16 /* IPCOMP deflate */
++
++#define XF_CLR 126 /* Clear SA table */
++#define XF_DEL 127 /* Delete SA */
++
++/* IPsec AH transform values
++ * RFC 2407
++ * draft-ietf-ipsec-doi-tc-mib-02.txt
++ */
++
++#define AH_NONE 0
++#define AH_MD5 2
++#define AH_SHA 3
++/* draft-ietf-ipsec-ciph-aes-cbc-03.txt */
++#define AH_SHA2_256 5
++#define AH_SHA2_384 6
++#define AH_SHA2_512 7
++#define AH_RIPEMD 8
++#define AH_MAX 15
++
++/* IPsec ESP transform values */
++
++#define ESP_NONE 0
++#define ESP_DES 2
++#define ESP_3DES 3
++#define ESP_RC5 4
++#define ESP_IDEA 5
++#define ESP_CAST 6
++#define ESP_BLOWFISH 7
++#define ESP_3IDEA 8
++#define ESP_RC4 10
++#define ESP_NULL 11
++#define ESP_AES 12
++
++/* as draft-ietf-ipsec-ciph-aes-cbc-02.txt */
++#define ESP_MARS 249
++#define ESP_RC6 250
++#define ESP_SERPENT 252
++#define ESP_TWOFISH 253
++
++/* IPCOMP transform values */
++
++#define IPCOMP_NONE 0
++#define IPCOMP_OUI 1
++#define IPCOMP_DEFLAT 2
++#define IPCOMP_LZS 3
++#define IPCOMP_V42BIS 4
++
++#define XFT_AUTH 0x0001
++#define XFT_CONF 0x0100
++
++/* available if CONFIG_IPSEC_DEBUG is defined */
++#define DB_XF_INIT 0x0001
++
++#define PROTO2TXT(x) \
++ (x) == IPPROTO_AH ? "AH" : \
++ (x) == IPPROTO_ESP ? "ESP" : \
++ (x) == IPPROTO_IPIP ? "IPIP" : \
++ (x) == IPPROTO_COMP ? "COMP" : \
++ "UNKNOWN_proto"
++static inline const char *enc_name_id (unsigned id) {
++ static char buf[16];
++ snprintf(buf, sizeof(buf), "_ID%d", id);
++ return buf;
++}
++static inline const char *auth_name_id (unsigned id) {
++ static char buf[16];
++ snprintf(buf, sizeof(buf), "_ID%d", id);
++ return buf;
++}
++#define IPS_XFORM_NAME(x) \
++ PROTO2TXT((x)->ips_said.proto), \
++ (x)->ips_said.proto == IPPROTO_COMP ? \
++ ((x)->ips_encalg == SADB_X_CALG_DEFLATE ? \
++ "_DEFLATE" : "_UNKNOWN_comp") : \
++ (x)->ips_encalg == ESP_NONE ? "" : \
++ (x)->ips_encalg == ESP_3DES ? "_3DES" : \
++ (x)->ips_encalg == ESP_AES ? "_AES" : \
++ (x)->ips_encalg == ESP_SERPENT ? "_SERPENT" : \
++ (x)->ips_encalg == ESP_TWOFISH ? "_TWOFISH" : \
++ enc_name_id(x->ips_encalg)/* "_UNKNOWN_encr" */, \
++ (x)->ips_authalg == AH_NONE ? "" : \
++ (x)->ips_authalg == AH_MD5 ? "_HMAC_MD5" : \
++ (x)->ips_authalg == AH_SHA ? "_HMAC_SHA1" : \
++ (x)->ips_authalg == AH_SHA2_256 ? "_HMAC_SHA2_256" : \
++ (x)->ips_authalg == AH_SHA2_384 ? "_HMAC_SHA2_384" : \
++ (x)->ips_authalg == AH_SHA2_512 ? "_HMAC_SHA2_512" : \
++ auth_name_id(x->ips_authalg) /* "_UNKNOWN_auth" */ \
++
++#ifdef __KERNEL__
++struct ipsec_rcv_state;
++struct ipsec_xmit_state;
++
++struct xform_functions {
++ enum ipsec_rcv_value (*rcv_checks)(struct ipsec_rcv_state *irs,
++ struct sk_buff *skb);
++ enum ipsec_rcv_value (*rcv_decrypt)(struct ipsec_rcv_state *irs);
++
++ enum ipsec_rcv_value (*rcv_setup_auth)(struct ipsec_rcv_state *irs,
++ struct sk_buff *skb,
++ __u32 *replay,
++ unsigned char **authenticator);
++ enum ipsec_rcv_value (*rcv_calc_auth)(struct ipsec_rcv_state *irs,
++ struct sk_buff *skb);
++
++ enum ipsec_xmit_value (*xmit_setup)(struct ipsec_xmit_state *ixs);
++ enum ipsec_xmit_value (*xmit_encrypt)(struct ipsec_xmit_state *ixs);
++
++ enum ipsec_xmit_value (*xmit_setup_auth)(struct ipsec_xmit_state *ixs,
++ struct sk_buff *skb,
++ __u32 *replay,
++ unsigned char **authenticator);
++ enum ipsec_xmit_value (*xmit_calc_auth)(struct ipsec_xmit_state *ixs,
++ struct sk_buff *skb);
++ int xmit_headroom;
++ int xmit_needtailroom;
++};
++
++#endif /* __KERNEL__ */
++
++#ifdef CONFIG_IPSEC_DEBUG
++extern void ipsec_dmp(char *s, caddr_t bb, int len);
++#else /* CONFIG_IPSEC_DEBUG */
++#define ipsec_dmp(_x, _y, _z)
++#endif /* CONFIG_IPSEC_DEBUG */
++
++
++#define _IPSEC_XFORM_H_
++#endif /* _IPSEC_XFORM_H_ */
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.40 2004/04/06 02:49:08 mcr
++ * pullup of algo code from alg-branch.
++ *
++ * Revision 1.39 2004/04/05 19:55:07 mcr
++ * Moved from linux/include/freeswan/ipsec_xform.h,v
++ *
++ * Revision 1.38 2004/04/05 19:41:05 mcr
++ * merged alg-branch code.
++ *
++ * Revision 1.37 2003/12/13 19:10:16 mcr
++ * refactored rcv and xmit code - same as FS 2.05.
++ *
++ * Revision 1.36.34.1 2003/12/22 15:25:52 jjo
++ * Merged algo-0.8.1-rc11-test1 into alg-branch
++ *
++ * Revision 1.36 2002/04/24 07:36:48 mcr
++ * Moved from ./klips/net/ipsec/ipsec_xform.h,v
++ *
++ * Revision 1.35 2001/11/26 09:23:51 rgb
++ * Merge MCR's ipsec_sa, eroute, proc and struct lifetime changes.
++ *
++ * Revision 1.33.2.1 2001/09/25 02:24:58 mcr
++ * struct tdb -> struct ipsec_sa.
++ * sa(tdb) manipulation functions renamed and moved to ipsec_sa.c
++ * ipsec_xform.c removed. header file still contains useful things.
++ *
++ * Revision 1.34 2001/11/06 19:47:17 rgb
++ * Changed lifetime_packets to uint32 from uint64.
++ *
++ * Revision 1.33 2001/09/08 21:13:34 rgb
++ * Added pfkey ident extension support for ISAKMPd. (NetCelo)
++ *
++ * Revision 1.32 2001/07/06 07:40:01 rgb
++ * Reformatted for readability.
++ * Added inbound policy checking fields for use with IPIP SAs.
++ *
++ * Revision 1.31 2001/06/14 19:35:11 rgb
++ * Update copyright date.
++ *
++ * Revision 1.30 2001/05/30 08:14:03 rgb
++ * Removed vestiges of esp-null transforms.
++ *
++ * Revision 1.29 2001/01/30 23:42:47 rgb
++ * Allow pfkey msgs from pid other than user context required for ACQUIRE
++ * and subsequent ADD or UDATE.
++ *
++ * Revision 1.28 2000/11/06 04:30:40 rgb
++ * Add Svenning's adaptive content compression.
++ *
++ * Revision 1.27 2000/09/19 00:38:25 rgb
++ * Fixed algorithm name bugs introduced for ipcomp.
++ *
++ * Revision 1.26 2000/09/17 21:36:48 rgb
++ * Added proto2txt macro.
++ *
++ * Revision 1.25 2000/09/17 18:56:47 rgb
++ * Added IPCOMP support.
++ *
++ * Revision 1.24 2000/09/12 19:34:12 rgb
++ * Defined XF_IP6 from Gerhard for ipv6 tunnel support.
++ *
++ * Revision 1.23 2000/09/12 03:23:14 rgb
++ * Cleaned out now unused tdb_xform and tdb_xdata members of struct tdb.
++ *
++ * Revision 1.22 2000/09/08 19:12:56 rgb
++ * Change references from DEBUG_IPSEC to CONFIG_IPSEC_DEBUG.
++ *
++ * Revision 1.21 2000/09/01 18:32:43 rgb
++ * Added (disabled) sensitivity members to tdb struct.
++ *
++ * Revision 1.20 2000/08/30 05:31:01 rgb
++ * Removed all the rest of the references to tdb_spi, tdb_proto, tdb_dst.
++ * Kill remainder of tdb_xform, tdb_xdata, xformsw.
++ *
++ * Revision 1.19 2000/08/01 14:51:52 rgb
++ * Removed _all_ remaining traces of DES.
++ *
++ * Revision 1.18 2000/01/21 06:17:45 rgb
++ * Tidied up spacing.
++ *
++ *
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/include/openswan/ipsec_xmit.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,176 @@
++/*
++ * IPSEC tunneling code
++ * Copyright (C) 1996, 1997 John Ioannidis.
++ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 Richard Guy Briggs.
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ * RCSID $Id$
++ */
++
++#include "openswan/ipsec_sa.h"
++
++enum ipsec_xmit_value
++{
++ IPSEC_XMIT_STOLEN=2,
++ IPSEC_XMIT_PASS=1,
++ IPSEC_XMIT_OK=0,
++ IPSEC_XMIT_ERRMEMALLOC=-1,
++ IPSEC_XMIT_ESP_BADALG=-2,
++ IPSEC_XMIT_BADPROTO=-3,
++ IPSEC_XMIT_ESP_PUSHPULLERR=-4,
++ IPSEC_XMIT_BADLEN=-5,
++ IPSEC_XMIT_AH_BADALG=-6,
++ IPSEC_XMIT_SAIDNOTFOUND=-7,
++ IPSEC_XMIT_SAIDNOTLIVE=-8,
++ IPSEC_XMIT_REPLAYROLLED=-9,
++ IPSEC_XMIT_LIFETIMEFAILED=-10,
++ IPSEC_XMIT_CANNOTFRAG=-11,
++ IPSEC_XMIT_MSSERR=-12,
++ IPSEC_XMIT_ERRSKBALLOC=-13,
++ IPSEC_XMIT_ENCAPFAIL=-14,
++ IPSEC_XMIT_NODEV=-15,
++ IPSEC_XMIT_NOPRIVDEV=-16,
++ IPSEC_XMIT_NOPHYSDEV=-17,
++ IPSEC_XMIT_NOSKB=-18,
++ IPSEC_XMIT_NOIPV6=-19,
++ IPSEC_XMIT_NOIPOPTIONS=-20,
++ IPSEC_XMIT_TTLEXPIRED=-21,
++ IPSEC_XMIT_BADHHLEN=-22,
++ IPSEC_XMIT_PUSHPULLERR=-23,
++ IPSEC_XMIT_ROUTEERR=-24,
++ IPSEC_XMIT_RECURSDETECT=-25,
++ IPSEC_XMIT_IPSENDFAILURE=-26,
++ IPSEC_XMIT_ESPUDP=-27,
++ IPSEC_XMIT_ESPUDP_BADTYPE=-28,
++};
++
++struct ipsec_xmit_state
++{
++ struct sk_buff *skb; /* working skb pointer */
++ struct device *dev; /* working dev pointer */
++ struct ipsecpriv *prv; /* Our device' private space */
++ struct sk_buff *oskb; /* Original skb pointer */
++ struct net_device_stats *stats; /* This device's statistics */
++ struct iphdr *iph; /* Our new IP header */
++ __u32 newdst; /* The other SG's IP address */
++ __u32 orgdst; /* Original IP destination address */
++ __u32 orgedst; /* 1st SG's IP address */
++ __u32 newsrc; /* The new source SG's IP address */
++ __u32 orgsrc; /* Original IP source address */
++ __u32 innersrc; /* Innermost IP source address */
++ int iphlen; /* IP header length */
++ int pyldsz; /* upper protocol payload size */
++ int headroom;
++ int tailroom;
++ int authlen;
++ int max_headroom; /* The extra header space needed */
++ int max_tailroom; /* The extra stuffing needed */
++ int ll_headroom; /* The extra link layer hard_header space needed */
++ int tot_headroom; /* The total header space needed */
++ int tot_tailroom; /* The totalstuffing needed */
++ __u8 *saved_header; /* saved copy of the hard header */
++ unsigned short sport, dport;
++
++ struct sockaddr_encap matcher; /* eroute search key */
++ struct eroute *eroute;
++ struct ipsec_sa *ipsp, *ipsq; /* ipsec_sa pointers */
++ char sa_txt[SATOT_BUF];
++ size_t sa_len;
++ int hard_header_stripped; /* has the hard header been removed yet? */
++ int hard_header_len;
++ struct device *physdev;
++/* struct device *virtdev; */
++ short physmtu;
++ short mtudiff;
++#ifdef NET_21
++ struct rtable *route;
++#endif /* NET_21 */
++ ip_said outgoing_said;
++#ifdef NET_21
++ int pass;
++#endif /* NET_21 */
++ int error;
++ uint32_t eroute_pid;
++ struct ipsec_sa ips;
++#ifdef CONFIG_IPSEC_NAT_TRAVERSAL
++ uint8_t natt_type;
++ uint8_t natt_head;
++ uint16_t natt_sport;
++ uint16_t natt_dport;
++#endif
++};
++
++enum ipsec_xmit_value
++ipsec_xmit_sanity_check_dev(struct ipsec_xmit_state *ixs);
++
++enum ipsec_xmit_value
++ipsec_xmit_sanity_check_skb(struct ipsec_xmit_state *ixs);
++
++enum ipsec_xmit_value
++ipsec_xmit_encap_bundle(struct ipsec_xmit_state *ixs);
++
++extern void ipsec_extract_ports(struct iphdr * iph, struct sockaddr_encap * er);
++
++
++extern int ipsec_xmit_trap_count;
++extern int ipsec_xmit_trap_sendcount;
++
++#ifdef CONFIG_IPSEC_DEBUG
++extern int debug_tunnel;
++extern int sysctl_ipsec_debug_verbose;
++#endif /* CONFIG_IPSEC_DEBUG */
++
++#define debug_xmit debug_tunnel
++
++#define ipsec_xmit_dmp(_x,_y, _z) if (debug_xmit && sysctl_ipsec_debug_verbose) ipsec_dmp(_x,_y,_z)
++
++extern int sysctl_ipsec_icmp;
++extern int sysctl_ipsec_tos;
++
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.9 2004/04/06 02:49:08 mcr
++ * pullup of algo code from alg-branch.
++ *
++ * Revision 1.8 2004/04/05 19:55:07 mcr
++ * Moved from linux/include/freeswan/ipsec_xmit.h,v
++ *
++ * Revision 1.7 2004/02/03 03:11:40 mcr
++ * new xmit type if the UDP encapsulation is wrong.
++ *
++ * Revision 1.6 2003/12/13 19:10:16 mcr
++ * refactored rcv and xmit code - same as FS 2.05.
++ *
++ * Revision 1.5 2003/12/10 01:20:06 mcr
++ * NAT-traversal patches to KLIPS.
++ *
++ * Revision 1.4 2003/12/06 16:37:04 mcr
++ * 1.4.7a X.509 patch applied.
++ *
++ * Revision 1.3 2003/10/31 02:27:05 mcr
++ * pulled up port-selector patches and sa_id elimination.
++ *
++ * Revision 1.2.4.2 2003/10/29 01:10:19 mcr
++ * elimited "struct sa_id"
++ *
++ * Revision 1.2.4.1 2003/09/21 13:59:38 mcr
++ * pre-liminary X.509 patch - does not yet pass tests.
++ *
++ * Revision 1.2 2003/06/20 01:42:13 mcr
++ * added counters to measure how many ACQUIREs we send to pluto,
++ * and how many are successfully sent.
++ *
++ * Revision 1.1 2003/02/12 19:31:03 rgb
++ * Refactored from ipsec_tunnel.c
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/include/openswan/passert.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,64 @@
++/*
++ * sanitize a string into a printable format.
++ *
++ * Copyright (C) 1998-2002 D. Hugh Redelmeier.
++ * Copyright (C) 2003 Michael Richardson <mcr@freeswan.org>
++ *
++ * This library is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU Library General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/lgpl.txt>.
++ *
++ * This library 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 Library General Public
++ * License for more details.
++ *
++ * RCSID $Id$
++ */
++
++#include "openswan.h"
++
++/* our versions of assert: log result */
++
++#ifdef DEBUG
++
++extern void passert_fail(const char *pred_str
++ , const char *file_str, unsigned long line_no) NEVER_RETURNS;
++
++extern void pexpect_log(const char *pred_str
++ , const char *file_str, unsigned long line_no);
++
++# define impossible() passert_fail("impossible", __FILE__, __LINE__)
++
++extern void switch_fail(int n
++ , const char *file_str, unsigned long line_no) NEVER_RETURNS;
++
++# define bad_case(n) switch_fail((int) n, __FILE__, __LINE__)
++
++# define passert(pred) { \
++ if (!(pred)) \
++ passert_fail(#pred, __FILE__, __LINE__); \
++ }
++
++# define pexpect(pred) { \
++ if (!(pred)) \
++ pexpect_log(#pred, __FILE__, __LINE__); \
++ }
++
++/* assert that an err_t is NULL; evaluate exactly once */
++# define happy(x) { \
++ err_t ugh = x; \
++ if (ugh != NULL) \
++ passert_fail(ugh, __FILE__, __LINE__); \
++ }
++
++#else /*!DEBUG*/
++
++# define impossible() abort()
++# define bad_case(n) abort()
++# define passert(pred) { } /* do nothing */
++# define happy(x) { (void) x; } /* evaluate non-judgementally */
++
++#endif /*!DEBUG*/
++
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/include/openswan/pfkey_debug.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,54 @@
++/*
++ * sanitize a string into a printable format.
++ *
++ * Copyright (C) 1998-2002 D. Hugh Redelmeier.
++ * Copyright (C) 2003 Michael Richardson <mcr@freeswan.org>
++ *
++ * This library is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU Library General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/lgpl.txt>.
++ *
++ * This library 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 Library General Public
++ * License for more details.
++ *
++ * RCSID $Id$
++ */
++
++#ifndef _FREESWAN_PFKEY_DEBUG_H
++#define _FREESWAN_PFKEY_DEBUG_H
++
++#ifdef __KERNEL__
++
++/* note, kernel version ignores pfkey levels */
++# define DEBUGGING(level,args...) \
++ KLIPS_PRINT(debug_pfkey, "klips_debug:" args)
++
++# define ERROR(args...) printk(KERN_ERR "klips:" args)
++
++#else
++
++extern unsigned int pfkey_lib_debug;
++
++extern void (*pfkey_debug_func)(const char *message, ...) PRINTF_LIKE(1);
++extern void (*pfkey_error_func)(const char *message, ...) PRINTF_LIKE(1);
++
++#define DEBUGGING(level,args...) if(pfkey_lib_debug & level) { \
++ if(pfkey_debug_func != NULL) { \
++ (*pfkey_debug_func)("pfkey_lib_debug:" args); \
++ } else { \
++ printf("pfkey_lib_debug:" args); \
++ } }
++
++#define ERROR(args...) if(pfkey_error_func != NULL) { \
++ (*pfkey_error_func)("pfkey_lib_debug:" args); \
++ }
++
++# define MALLOC(size) malloc(size)
++# define FREE(obj) free(obj)
++
++#endif
++
++#endif
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/include/openswan/radij.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,280 @@
++/*
++ * RCSID $Id$
++ */
++
++/*
++ * This file is defived from ${SRC}/sys/net/radix.h of BSD 4.4lite
++ *
++ * Variable and procedure names have been modified so that they don't
++ * conflict with the original BSD code, as a small number of modifications
++ * have been introduced and we may want to reuse this code in BSD.
++ *
++ * The `j' in `radij' is pronounced as a voiceless guttural (like a Greek
++ * chi or a German ch sound (as `doch', not as in `milch'), or even a
++ * spanish j as in Juan. It is not as far back in the throat like
++ * the corresponding Hebrew sound, nor is it a soft breath like the English h.
++ * It has nothing to do with the Dutch ij sound.
++ *
++ * Here is the appropriate copyright notice:
++ */
++
++/*
++ * Copyright (c) 1988, 1989, 1993
++ * The Regents of the University of California. All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ * 1. Redistributions of source code must retain the above copyright
++ * notice, this list of conditions and the following disclaimer.
++ * 2. Redistributions in binary form must reproduce the above copyright
++ * notice, this list of conditions and the following disclaimer in the
++ * documentation and/or other materials provided with the distribution.
++ * 3. All advertising materials mentioning features or use of this software
++ * must display the following acknowledgement:
++ * This product includes software developed by the University of
++ * California, Berkeley and its contributors.
++ * 4. Neither the name of the University nor the names of its contributors
++ * may be used to endorse or promote products derived from this software
++ * without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ *
++ * @(#)radix.h 8.1 (Berkeley) 6/10/93
++ */
++
++#ifndef _RADIJ_H_
++#define _RADIJ_H_
++
++/*
++#define RJ_DEBUG
++*/
++
++#ifdef __KERNEL__
++
++#ifndef __P
++#ifdef __STDC__
++#define __P(x) x
++#else
++#define __P(x) ()
++#endif
++#endif
++
++/*
++ * Radix search tree node layout.
++ */
++
++struct radij_node
++{
++ struct radij_mask *rj_mklist; /* list of masks contained in subtree */
++ struct radij_node *rj_p; /* parent */
++ short rj_b; /* bit offset; -1-index(netmask) */
++ char rj_bmask; /* node: mask for bit test*/
++ u_char rj_flags; /* enumerated next */
++#define RJF_NORMAL 1 /* leaf contains normal route */
++#define RJF_ROOT 2 /* leaf is root leaf for tree */
++#define RJF_ACTIVE 4 /* This node is alive (for rtfree) */
++ union {
++ struct { /* leaf only data: */
++ caddr_t rj_Key; /* object of search */
++ caddr_t rj_Mask; /* netmask, if present */
++ struct radij_node *rj_Dupedkey;
++ } rj_leaf;
++ struct { /* node only data: */
++ int rj_Off; /* where to start compare */
++ struct radij_node *rj_L;/* progeny */
++ struct radij_node *rj_R;/* progeny */
++ }rj_node;
++ } rj_u;
++#ifdef RJ_DEBUG
++ int rj_info;
++ struct radij_node *rj_twin;
++ struct radij_node *rj_ybro;
++#endif
++};
++
++#define rj_dupedkey rj_u.rj_leaf.rj_Dupedkey
++#define rj_key rj_u.rj_leaf.rj_Key
++#define rj_mask rj_u.rj_leaf.rj_Mask
++#define rj_off rj_u.rj_node.rj_Off
++#define rj_l rj_u.rj_node.rj_L
++#define rj_r rj_u.rj_node.rj_R
++
++/*
++ * Annotations to tree concerning potential routes applying to subtrees.
++ */
++
++extern struct radij_mask {
++ short rm_b; /* bit offset; -1-index(netmask) */
++ char rm_unused; /* cf. rj_bmask */
++ u_char rm_flags; /* cf. rj_flags */
++ struct radij_mask *rm_mklist; /* more masks to try */
++ caddr_t rm_mask; /* the mask */
++ int rm_refs; /* # of references to this struct */
++} *rj_mkfreelist;
++
++#define MKGet(m) {\
++ if (rj_mkfreelist) {\
++ m = rj_mkfreelist; \
++ rj_mkfreelist = (m)->rm_mklist; \
++ } else \
++ R_Malloc(m, struct radij_mask *, sizeof (*(m))); }\
++
++#define MKFree(m) { (m)->rm_mklist = rj_mkfreelist; rj_mkfreelist = (m);}
++
++struct radij_node_head {
++ struct radij_node *rnh_treetop;
++ int rnh_addrsize; /* permit, but not require fixed keys */
++ int rnh_pktsize; /* permit, but not require fixed keys */
++#if 0
++ struct radij_node *(*rnh_addaddr) /* add based on sockaddr */
++ __P((void *v, void *mask,
++ struct radij_node_head *head, struct radij_node nodes[]));
++#endif
++ int (*rnh_addaddr) /* add based on sockaddr */
++ __P((void *v, void *mask,
++ struct radij_node_head *head, struct radij_node nodes[]));
++ struct radij_node *(*rnh_addpkt) /* add based on packet hdr */
++ __P((void *v, void *mask,
++ struct radij_node_head *head, struct radij_node nodes[]));
++#if 0
++ struct radij_node *(*rnh_deladdr) /* remove based on sockaddr */
++ __P((void *v, void *mask, struct radij_node_head *head));
++#endif
++ int (*rnh_deladdr) /* remove based on sockaddr */
++ __P((void *v, void *mask, struct radij_node_head *head, struct radij_node **node));
++ struct radij_node *(*rnh_delpkt) /* remove based on packet hdr */
++ __P((void *v, void *mask, struct radij_node_head *head));
++ struct radij_node *(*rnh_matchaddr) /* locate based on sockaddr */
++ __P((void *v, struct radij_node_head *head));
++ struct radij_node *(*rnh_matchpkt) /* locate based on packet hdr */
++ __P((void *v, struct radij_node_head *head));
++ int (*rnh_walktree) /* traverse tree */
++ __P((struct radij_node_head *head, int (*f)(struct radij_node *rn, void *w), void *w));
++ struct radij_node rnh_nodes[3]; /* empty tree for common case */
++};
++
++
++#define Bcmp(a, b, n) memcmp(((caddr_t)(b)), ((caddr_t)(a)), (unsigned)(n))
++#define Bcopy(a, b, n) memmove(((caddr_t)(b)), ((caddr_t)(a)), (unsigned)(n))
++#define Bzero(p, n) memset((caddr_t)(p), 0, (unsigned)(n))
++#define R_Malloc(p, t, n) ((p = (t) kmalloc((size_t)(n), GFP_ATOMIC)), Bzero((p),(n)))
++#define Free(p) kfree((caddr_t)p);
++
++void rj_init __P((void));
++int rj_inithead __P((void **, int));
++int rj_refines __P((void *, void *));
++int rj_walktree __P((struct radij_node_head *head, int (*f)(struct radij_node *rn, void *w), void *w));
++struct radij_node
++ *rj_addmask __P((void *, int, int)) /* , rgb */ ;
++int /* * */ rj_addroute __P((void *, void *, struct radij_node_head *,
++ struct radij_node [2])) /* , rgb */ ;
++int /* * */ rj_delete __P((void *, void *, struct radij_node_head *, struct radij_node **)) /* , rgb */ ;
++struct radij_node /* rgb */
++ *rj_insert __P((void *, struct radij_node_head *, int *,
++ struct radij_node [2])),
++ *rj_match __P((void *, struct radij_node_head *)),
++ *rj_newpair __P((void *, int, struct radij_node[2])),
++ *rj_search __P((void *, struct radij_node *)),
++ *rj_search_m __P((void *, struct radij_node *, void *));
++
++void rj_deltree(struct radij_node_head *);
++void rj_delnodes(struct radij_node *);
++void rj_free_mkfreelist(void);
++int radijcleartree(void);
++int radijcleanup(void);
++
++extern struct radij_node_head *mask_rjhead;
++extern int maj_keylen;
++#endif /* __KERNEL__ */
++
++#endif /* _RADIJ_H_ */
++
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.13 2004/04/05 19:55:08 mcr
++ * Moved from linux/include/freeswan/radij.h,v
++ *
++ * Revision 1.12 2002/04/24 07:36:48 mcr
++ * Moved from ./klips/net/ipsec/radij.h,v
++ *
++ * Revision 1.11 2001/09/20 15:33:00 rgb
++ * Min/max cleanup.
++ *
++ * Revision 1.10 1999/11/18 04:09:20 rgb
++ * Replaced all kernel version macros to shorter, readable form.
++ *
++ * Revision 1.9 1999/05/05 22:02:33 rgb
++ * Add a quick and dirty port to 2.2 kernels by Marc Boucher <marc@mbsi.ca>.
++ *
++ * Revision 1.8 1999/04/29 15:24:58 rgb
++ * Add check for existence of macros min/max.
++ *
++ * Revision 1.7 1999/04/11 00:29:02 henry
++ * GPL boilerplate
++ *
++ * Revision 1.6 1999/04/06 04:54:29 rgb
++ * Fix/Add RCSID Id: and Log: bits to make PHMDs happy. This includes
++ * patch shell fixes.
++ *
++ * Revision 1.5 1999/01/22 06:30:32 rgb
++ * 64-bit clean-up.
++ *
++ * Revision 1.4 1998/11/30 13:22:55 rgb
++ * Rationalised all the klips kernel file headers. They are much shorter
++ * now and won't conflict under RH5.2.
++ *
++ * Revision 1.3 1998/10/25 02:43:27 rgb
++ * Change return type on rj_addroute and rj_delete and add and argument
++ * to the latter to be able to transmit more infomation about errors.
++ *
++ * Revision 1.2 1998/07/14 18:09:51 rgb
++ * Add a routine to clear eroute table.
++ * Added #ifdef __KERNEL__ directives to restrict scope of header.
++ *
++ * Revision 1.1 1998/06/18 21:30:22 henry
++ * move sources from klips/src to klips/net/ipsec to keep stupid kernel
++ * build scripts happier about symlinks
++ *
++ * Revision 1.4 1998/05/25 20:34:16 rgb
++ * Remove temporary ipsec_walk, rj_deltree and rj_delnodes functions.
++ *
++ * Rename ipsec_rj_walker (ipsec_walk) to ipsec_rj_walker_procprint and
++ * add ipsec_rj_walker_delete.
++ *
++ * Recover memory for eroute table on unload of module.
++ *
++ * Revision 1.3 1998/04/22 16:51:37 rgb
++ * Tidy up radij debug code from recent rash of modifications to debug code.
++ *
++ * Revision 1.2 1998/04/14 17:30:38 rgb
++ * Fix up compiling errors for radij tree memory reclamation.
++ *
++ * Revision 1.1 1998/04/09 03:06:16 henry
++ * sources moved up from linux/net/ipsec
++ *
++ * Revision 1.1.1.1 1998/04/08 05:35:04 henry
++ * RGB's ipsec-0.8pre2.tar.gz ipsec-0.8
++ *
++ * Revision 0.4 1997/01/15 01:28:15 ji
++ * No changes.
++ *
++ * Revision 0.3 1996/11/20 14:44:45 ji
++ * Release update only.
++ *
++ * Revision 0.2 1996/11/02 00:18:33 ji
++ * First limited release.
++ *
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/include/pfkey.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,509 @@
++/*
++ * FreeS/WAN specific PF_KEY headers
++ * Copyright (C) 1999, 2000, 2001 Richard Guy Briggs.
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ * RCSID $Id$
++ */
++
++#ifndef __NET_IPSEC_PF_KEY_H
++#define __NET_IPSEC_PF_KEY_H
++#ifdef __KERNEL__
++extern struct proto_ops pfkey_proto_ops;
++typedef struct sock pfkey_sock;
++extern int debug_pfkey;
++
++extern /* void */ int pfkey_init(void);
++extern /* void */ int pfkey_cleanup(void);
++
++extern struct sock *pfkey_sock_list;
++struct socket_list
++{
++ struct socket *socketp;
++ struct socket_list *next;
++};
++extern int pfkey_list_insert_socket(struct socket*, struct socket_list**);
++extern int pfkey_list_remove_socket(struct socket*, struct socket_list**);
++extern struct socket_list *pfkey_open_sockets;
++extern struct socket_list *pfkey_registered_sockets[SADB_SATYPE_MAX+1];
++
++struct supported
++{
++ uint16_t supported_alg_exttype;
++ uint8_t supported_alg_id;
++ uint8_t supported_alg_ivlen;
++ uint16_t supported_alg_minbits;
++ uint16_t supported_alg_maxbits;
++};
++
++extern struct supported_list *pfkey_supported_list[SADB_SATYPE_MAX+1];
++struct supported_list
++{
++ struct supported *supportedp;
++ struct supported_list *next;
++};
++extern int pfkey_list_insert_supported(struct supported*, struct supported_list**);
++extern int pfkey_list_remove_supported(struct supported*, struct supported_list**);
++
++struct sockaddr_key
++{
++ uint16_t key_family; /* PF_KEY */
++ uint16_t key_pad; /* not used */
++ uint32_t key_pid; /* process ID */
++};
++
++struct pfkey_extracted_data
++{
++ struct ipsec_sa* ips;
++ struct ipsec_sa* ips2;
++ struct eroute *eroute;
++};
++
++extern int
++pfkey_alloc_eroute(struct eroute** eroute);
++
++extern int
++pfkey_sa_process(struct sadb_ext *pfkey_ext,
++ struct pfkey_extracted_data* extr);
++
++extern int
++pfkey_lifetime_process(struct sadb_ext *pfkey_ext,
++ struct pfkey_extracted_data* extr);
++
++extern int
++pfkey_address_process(struct sadb_ext *pfkey_ext,
++ struct pfkey_extracted_data* extr);
++
++extern int
++pfkey_key_process(struct sadb_ext *pfkey_ext,
++ struct pfkey_extracted_data* extr);
++
++extern int
++pfkey_ident_process(struct sadb_ext *pfkey_ext,
++ struct pfkey_extracted_data* extr);
++
++extern int
++pfkey_sens_process(struct sadb_ext *pfkey_ext,
++ struct pfkey_extracted_data* extr);
++
++extern int
++pfkey_prop_process(struct sadb_ext *pfkey_ext,
++ struct pfkey_extracted_data* extr);
++
++extern int
++pfkey_supported_process(struct sadb_ext *pfkey_ext,
++ struct pfkey_extracted_data* extr);
++
++extern int
++pfkey_spirange_process(struct sadb_ext *pfkey_ext,
++ struct pfkey_extracted_data* extr);
++
++extern int
++pfkey_x_kmprivate_process(struct sadb_ext *pfkey_ext,
++ struct pfkey_extracted_data* extr);
++
++extern int
++pfkey_x_satype_process(struct sadb_ext *pfkey_ext,
++ struct pfkey_extracted_data* extr);
++
++extern int
++pfkey_x_debug_process(struct sadb_ext *pfkey_ext,
++ struct pfkey_extracted_data* extr);
++
++extern int pfkey_upmsg(struct socket *, struct sadb_msg *);
++extern int pfkey_expire(struct ipsec_sa *, int);
++extern int pfkey_acquire(struct ipsec_sa *);
++#else /* ! __KERNEL__ */
++
++extern void (*pfkey_debug_func)(const char *message, ...);
++extern void (*pfkey_error_func)(const char *message, ...);
++extern void pfkey_print(struct sadb_msg *msg, FILE *out);
++
++
++#endif /* __KERNEL__ */
++
++extern uint8_t satype2proto(uint8_t satype);
++extern uint8_t proto2satype(uint8_t proto);
++extern char* satype2name(uint8_t satype);
++extern char* proto2name(uint8_t proto);
++
++struct key_opt
++{
++ uint32_t key_pid; /* process ID */
++ struct sock *sk;
++};
++
++#define key_pid(sk) ((struct key_opt*)&((sk)->protinfo))->key_pid
++
++/* XXX-mcr this is not an alignment, this is because the count is in 64-bit
++ * words.
++ */
++#define IPSEC_PFKEYv2_ALIGN (sizeof(uint64_t)/sizeof(uint8_t))
++#define BITS_PER_OCTET 8
++#define OCTETBITS 8
++#define PFKEYBITS 64
++#define DIVUP(x,y) ((x + y -1) / y) /* divide, rounding upwards */
++#define ALIGN_N(x,y) (DIVUP(x,y) * y) /* align on y boundary */
++
++#define IPSEC_PFKEYv2_LEN(x) ((x) * IPSEC_PFKEYv2_ALIGN)
++#define IPSEC_PFKEYv2_WORDS(x) ((x) / IPSEC_PFKEYv2_ALIGN)
++
++
++#define PFKEYv2_MAX_MSGSIZE 4096
++
++/*
++ * PF_KEYv2 permitted and required extensions in and out bitmaps
++ */
++struct pf_key_ext_parsers_def {
++ int (*parser)(struct sadb_ext*);
++ char *parser_name;
++};
++
++
++extern unsigned int extensions_bitmaps[2/*in/out*/][2/*perm/req*/][SADB_MAX + 1/*ext*/];
++#define EXT_BITS_IN 0
++#define EXT_BITS_OUT 1
++#define EXT_BITS_PERM 0
++#define EXT_BITS_REQ 1
++
++extern void pfkey_extensions_init(struct sadb_ext *extensions[SADB_EXT_MAX + 1]);
++extern void pfkey_extensions_free(struct sadb_ext *extensions[SADB_EXT_MAX + 1]);
++extern void pfkey_msg_free(struct sadb_msg **pfkey_msg);
++
++extern int pfkey_msg_parse(struct sadb_msg *pfkey_msg,
++ struct pf_key_ext_parsers_def *ext_parsers[],
++ struct sadb_ext **extensions,
++ int dir);
++
++extern int pfkey_register_reply(int satype, struct sadb_msg *sadb_msg);
++
++/*
++ * PF_KEYv2 build function prototypes
++ */
++
++int
++pfkey_msg_hdr_build(struct sadb_ext** pfkey_ext,
++ uint8_t msg_type,
++ uint8_t satype,
++ uint8_t msg_errno,
++ uint32_t seq,
++ uint32_t pid);
++
++int
++pfkey_sa_ref_build(struct sadb_ext ** pfkey_ext,
++ uint16_t exttype,
++ uint32_t spi, /* in network order */
++ uint8_t replay_window,
++ uint8_t sa_state,
++ uint8_t auth,
++ uint8_t encrypt,
++ uint32_t flags,
++ uint32_t/*IPsecSAref_t*/ ref);
++
++int
++pfkey_sa_build(struct sadb_ext ** pfkey_ext,
++ uint16_t exttype,
++ uint32_t spi, /* in network order */
++ uint8_t replay_window,
++ uint8_t sa_state,
++ uint8_t auth,
++ uint8_t encrypt,
++ uint32_t flags);
++
++int
++pfkey_lifetime_build(struct sadb_ext ** pfkey_ext,
++ uint16_t exttype,
++ uint32_t allocations,
++ uint64_t bytes,
++ uint64_t addtime,
++ uint64_t usetime,
++ uint32_t packets);
++
++int
++pfkey_address_build(struct sadb_ext** pfkey_ext,
++ uint16_t exttype,
++ uint8_t proto,
++ uint8_t prefixlen,
++ struct sockaddr* address);
++
++int
++pfkey_key_build(struct sadb_ext** pfkey_ext,
++ uint16_t exttype,
++ uint16_t key_bits,
++ char* key);
++
++int
++pfkey_ident_build(struct sadb_ext** pfkey_ext,
++ uint16_t exttype,
++ uint16_t ident_type,
++ uint64_t ident_id,
++ uint8_t ident_len,
++ char* ident_string);
++
++#ifdef NAT_TRAVERSAL
++#ifdef __KERNEL__
++extern int pfkey_nat_t_new_mapping(struct ipsec_sa *, struct sockaddr *, __u16);
++extern int pfkey_x_nat_t_type_process(struct sadb_ext *pfkey_ext, struct pfkey_extracted_data* extr);
++extern int pfkey_x_nat_t_port_process(struct sadb_ext *pfkey_ext, struct pfkey_extracted_data* extr);
++#endif /* __KERNEL__ */
++int
++pfkey_x_nat_t_type_build(struct sadb_ext** pfkey_ext,
++ uint8_t type);
++int
++pfkey_x_nat_t_port_build(struct sadb_ext** pfkey_ext,
++ uint16_t exttype,
++ uint16_t port);
++#endif
++
++int
++pfkey_sens_build(struct sadb_ext** pfkey_ext,
++ uint32_t dpd,
++ uint8_t sens_level,
++ uint8_t sens_len,
++ uint64_t* sens_bitmap,
++ uint8_t integ_level,
++ uint8_t integ_len,
++ uint64_t* integ_bitmap);
++
++int pfkey_x_protocol_build(struct sadb_ext **, uint8_t);
++
++
++int
++pfkey_prop_build(struct sadb_ext** pfkey_ext,
++ uint8_t replay,
++ unsigned int comb_num,
++ struct sadb_comb* comb);
++
++int
++pfkey_supported_build(struct sadb_ext** pfkey_ext,
++ uint16_t exttype,
++ unsigned int alg_num,
++ struct sadb_alg* alg);
++
++int
++pfkey_spirange_build(struct sadb_ext** pfkey_ext,
++ uint16_t exttype,
++ uint32_t min,
++ uint32_t max);
++
++int
++pfkey_x_kmprivate_build(struct sadb_ext** pfkey_ext);
++
++int
++pfkey_x_satype_build(struct sadb_ext** pfkey_ext,
++ uint8_t satype);
++
++int
++pfkey_x_debug_build(struct sadb_ext** pfkey_ext,
++ uint32_t tunnel,
++ uint32_t netlink,
++ uint32_t xform,
++ uint32_t eroute,
++ uint32_t spi,
++ uint32_t radij,
++ uint32_t esp,
++ uint32_t ah,
++ uint32_t rcv,
++ uint32_t pfkey,
++ uint32_t ipcomp,
++ uint32_t verbose);
++
++int
++pfkey_msg_build(struct sadb_msg** pfkey_msg,
++ struct sadb_ext* extensions[],
++ int dir);
++
++/* in pfkey_v2_debug.c - routines to decode numbers -> strings */
++const char *
++pfkey_v2_sadb_ext_string(int extnum);
++
++const char *
++pfkey_v2_sadb_type_string(int sadb_type);
++
++
++#endif /* __NET_IPSEC_PF_KEY_H */
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.45 2004/04/06 02:49:00 mcr
++ * pullup of algo code from alg-branch.
++ *
++ * Revision 1.44 2003/12/10 01:20:01 mcr
++ * NAT-traversal patches to KLIPS.
++ *
++ * Revision 1.43 2003/10/31 02:26:44 mcr
++ * pulled up port-selector patches.
++ *
++ * Revision 1.42.2.2 2003/10/29 01:09:32 mcr
++ * added debugging for pfkey library.
++ *
++ * Revision 1.42.2.1 2003/09/21 13:59:34 mcr
++ * pre-liminary X.509 patch - does not yet pass tests.
++ *
++ * Revision 1.42 2003/08/25 22:08:19 mcr
++ * removed pfkey_proto_init() from pfkey.h for 2.6 support.
++ *
++ * Revision 1.41 2003/05/07 17:28:57 mcr
++ * new function pfkey_debug_func added for us in debugging from
++
++ * pfkey library.
++ *
++ * Revision 1.40 2003/01/30 02:31:34 rgb
++ *
++ * Convert IPsecSAref_t from signed to unsigned to fix apparent SAref exhaustion bug.
++ *
++ * Revision 1.39 2002/09/20 15:40:21 rgb
++ * Switch from pfkey_alloc_ipsec_sa() to ipsec_sa_alloc().
++ * Added ref parameter to pfkey_sa_build().
++ * Cleaned out unused cruft.
++ *
++ * Revision 1.38 2002/05/14 02:37:24 rgb
++ * Change all references to tdb, TDB or Tunnel Descriptor Block to ips,
++ * ipsec_sa or ipsec_sa.
++ * Added function prototypes for the functions moved to
++ * pfkey_v2_ext_process.c.
++ *
++ * Revision 1.37 2002/04/24 07:36:49 mcr
++ * Moved from ./lib/pfkey.h,v
++ *
++ * Revision 1.36 2002/01/20 20:34:49 mcr
++ * added pfkey_v2_sadb_type_string to decode sadb_type to string.
++ *
++ * Revision 1.35 2001/11/27 05:27:47 mcr
++ * pfkey parses are now maintained by a structure
++ * that includes their name for debug purposes.
++ *
++ * Revision 1.34 2001/11/26 09:23:53 rgb
++ * Merge MCR's ipsec_sa, eroute, proc and struct lifetime changes.
++ *
++ * Revision 1.33 2001/11/06 19:47:47 rgb
++ * Added packet parameter to lifetime and comb structures.
++ *
++ * Revision 1.32 2001/09/08 21:13:34 rgb
++ * Added pfkey ident extension support for ISAKMPd. (NetCelo)
++ *
++ * Revision 1.31 2001/06/14 19:35:16 rgb
++ * Update copyright date.
++ *
++ * Revision 1.30 2001/02/27 07:04:52 rgb
++ * Added satype2name prototype.
++ *
++ * Revision 1.29 2001/02/26 19:59:33 rgb
++ * Ditch unused sadb_satype2proto[], replaced by satype2proto().
++ *
++ * Revision 1.28 2000/10/10 20:10:19 rgb
++ * Added support for debug_ipcomp and debug_verbose to klipsdebug.
++ *
++ * Revision 1.27 2000/09/21 04:20:45 rgb
++ * Fixed array size off-by-one error. (Thanks Svenning!)
++ *
++ * Revision 1.26 2000/09/12 03:26:05 rgb
++ * Added pfkey_acquire prototype.
++ *
++ * Revision 1.25 2000/09/08 19:21:28 rgb
++ * Fix pfkey_prop_build() parameter to be only single indirection.
++ *
++ * Revision 1.24 2000/09/01 18:46:42 rgb
++ * Added a supported algorithms array lists, one per satype and registered
++ * existing algorithms.
++ * Fixed pfkey_list_{insert,remove}_{socket,support}() to allow change to
++ * list.
++ *
++ * Revision 1.23 2000/08/27 01:55:26 rgb
++ * Define OCTETBITS and PFKEYBITS to avoid using 'magic' numbers in code.
++ *
++ * Revision 1.22 2000/08/20 21:39:23 rgb
++ * Added kernel prototypes for kernel funcitions pfkey_upmsg() and
++ * pfkey_expire().
++ *
++ * Revision 1.21 2000/08/15 17:29:23 rgb
++ * Fixes from SZI to untested pfkey_prop_build().
++ *
++ * Revision 1.20 2000/05/10 20:14:19 rgb
++ * Fleshed out sensitivity, proposal and supported extensions.
++ *
++ * Revision 1.19 2000/03/16 14:07:23 rgb
++ * Renamed ALIGN macro to avoid fighting with others in kernel.
++ *
++ * Revision 1.18 2000/01/22 23:24:06 rgb
++ * Added prototypes for proto2satype(), satype2proto() and proto2name().
++ *
++ * Revision 1.17 2000/01/21 06:26:59 rgb
++ * Converted from double tdb arguments to one structure (extr)
++ * containing pointers to all temporary information structures.
++ * Added klipsdebug switching capability.
++ * Dropped unused argument to pfkey_x_satype_build().
++ *
++ * Revision 1.16 1999/12/29 21:17:41 rgb
++ * Changed pfkey_msg_build() I/F to include a struct sadb_msg**
++ * parameter for cleaner manipulation of extensions[] and to guard
++ * against potential memory leaks.
++ * Changed the I/F to pfkey_msg_free() for the same reason.
++ *
++ * Revision 1.15 1999/12/09 23:12:54 rgb
++ * Added macro for BITS_PER_OCTET.
++ * Added argument to pfkey_sa_build() to do eroutes.
++ *
++ * Revision 1.14 1999/12/08 20:33:25 rgb
++ * Changed sa_family_t to uint16_t for 2.0.xx compatibility.
++ *
++ * Revision 1.13 1999/12/07 19:53:40 rgb
++ * Removed unused first argument from extension parsers.
++ * Changed __u* types to uint* to avoid use of asm/types.h and
++ * sys/types.h in userspace code.
++ * Added function prototypes for pfkey message and extensions
++ * initialisation and cleanup.
++ *
++ * Revision 1.12 1999/12/01 22:19:38 rgb
++ * Change pfkey_sa_build to accept an SPI in network byte order.
++ *
++ * Revision 1.11 1999/11/27 11:55:26 rgb
++ * Added extern sadb_satype2proto to enable moving protocol lookup table
++ * to lib/pfkey_v2_parse.c.
++ * Delete unused, moved typedefs.
++ * Add argument to pfkey_msg_parse() for direction.
++ * Consolidated the 4 1-d extension bitmap arrays into one 4-d array.
++ *
++ * Revision 1.10 1999/11/23 22:29:21 rgb
++ * This file has been moved in the distribution from klips/net/ipsec to
++ * lib.
++ * Add macros for dealing with alignment and rounding up more opaquely.
++ * The uint<n>_t type defines have been moved to freeswan.h to avoid
++ * chicken-and-egg problems.
++ * Add macros for dealing with alignment and rounding up more opaque.
++ * Added prototypes for using extention header bitmaps.
++ * Added prototypes of all the build functions.
++ *
++ * Revision 1.9 1999/11/20 21:59:48 rgb
++ * Moved socketlist type declarations and prototypes for shared use.
++ * Slightly modified scope of sockaddr_key declaration.
++ *
++ * Revision 1.8 1999/11/17 14:34:25 rgb
++ * Protect sa_family_t from being used in userspace with GLIBC<2.
++ *
++ * Revision 1.7 1999/10/27 19:40:35 rgb
++ * Add a maximum PFKEY packet size macro.
++ *
++ * Revision 1.6 1999/10/26 16:58:58 rgb
++ * Created a sockaddr_key and key_opt socket extension structures.
++ *
++ * Revision 1.5 1999/06/10 05:24:41 rgb
++ * Renamed variables to reduce confusion.
++ *
++ * Revision 1.4 1999/04/29 15:21:11 rgb
++ * Add pfkey support to debugging.
++ * Add return values to init and cleanup functions.
++ *
++ * Revision 1.3 1999/04/15 17:58:07 rgb
++ * Add RCSID labels.
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/include/pfkeyv2.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,467 @@
++/*
++ * RCSID $Id$
++ */
++
++/*
++RFC 2367 PF_KEY Key Management API July 1998
++
++
++Appendix D: Sample Header File
++
++This file defines structures and symbols for the PF_KEY Version 2
++key management interface. It was written at the U.S. Naval Research
++Laboratory. This file is in the public domain. The authors ask that
++you leave this credit intact on any copies of this file.
++*/
++#ifndef __PFKEY_V2_H
++#define __PFKEY_V2_H 1
++
++#define PF_KEY_V2 2
++#define PFKEYV2_REVISION 199806L
++
++#define SADB_RESERVED 0
++#define SADB_GETSPI 1
++#define SADB_UPDATE 2
++#define SADB_ADD 3
++#define SADB_DELETE 4
++#define SADB_GET 5
++#define SADB_ACQUIRE 6
++#define SADB_REGISTER 7
++#define SADB_EXPIRE 8
++#define SADB_FLUSH 9
++#define SADB_DUMP 10
++#define SADB_X_PROMISC 11
++#define SADB_X_PCHANGE 12
++#define SADB_X_GRPSA 13
++#define SADB_X_ADDFLOW 14
++#define SADB_X_DELFLOW 15
++#define SADB_X_DEBUG 16
++#define SADB_X_NAT_T_NEW_MAPPING 17
++#define SADB_MAX 17
++
++struct sadb_msg {
++ uint8_t sadb_msg_version;
++ uint8_t sadb_msg_type;
++ uint8_t sadb_msg_errno;
++ uint8_t sadb_msg_satype;
++ uint16_t sadb_msg_len;
++ uint16_t sadb_msg_reserved;
++ uint32_t sadb_msg_seq;
++ uint32_t sadb_msg_pid;
++};
++
++struct sadb_ext {
++ uint16_t sadb_ext_len;
++ uint16_t sadb_ext_type;
++};
++
++struct sadb_sa {
++ uint16_t sadb_sa_len;
++ uint16_t sadb_sa_exttype;
++ uint32_t sadb_sa_spi;
++ uint8_t sadb_sa_replay;
++ uint8_t sadb_sa_state;
++ uint8_t sadb_sa_auth;
++ uint8_t sadb_sa_encrypt;
++ uint32_t sadb_sa_flags;
++ uint32_t /*IPsecSAref_t*/ sadb_x_sa_ref; /* 32 bits */
++ uint8_t sadb_x_reserved[4];
++};
++
++struct sadb_sa_v1 {
++ uint16_t sadb_sa_len;
++ uint16_t sadb_sa_exttype;
++ uint32_t sadb_sa_spi;
++ uint8_t sadb_sa_replay;
++ uint8_t sadb_sa_state;
++ uint8_t sadb_sa_auth;
++ uint8_t sadb_sa_encrypt;
++ uint32_t sadb_sa_flags;
++};
++
++struct sadb_lifetime {
++ uint16_t sadb_lifetime_len;
++ uint16_t sadb_lifetime_exttype;
++ uint32_t sadb_lifetime_allocations;
++ uint64_t sadb_lifetime_bytes;
++ uint64_t sadb_lifetime_addtime;
++ uint64_t sadb_lifetime_usetime;
++ uint32_t sadb_x_lifetime_packets;
++ uint32_t sadb_x_lifetime_reserved;
++};
++
++struct sadb_address {
++ uint16_t sadb_address_len;
++ uint16_t sadb_address_exttype;
++ uint8_t sadb_address_proto;
++ uint8_t sadb_address_prefixlen;
++ uint16_t sadb_address_reserved;
++};
++
++struct sadb_key {
++ uint16_t sadb_key_len;
++ uint16_t sadb_key_exttype;
++ uint16_t sadb_key_bits;
++ uint16_t sadb_key_reserved;
++};
++
++struct sadb_ident {
++ uint16_t sadb_ident_len;
++ uint16_t sadb_ident_exttype;
++ uint16_t sadb_ident_type;
++ uint16_t sadb_ident_reserved;
++ uint64_t sadb_ident_id;
++};
++
++struct sadb_sens {
++ uint16_t sadb_sens_len;
++ uint16_t sadb_sens_exttype;
++ uint32_t sadb_sens_dpd;
++ uint8_t sadb_sens_sens_level;
++ uint8_t sadb_sens_sens_len;
++ uint8_t sadb_sens_integ_level;
++ uint8_t sadb_sens_integ_len;
++ uint32_t sadb_sens_reserved;
++};
++
++struct sadb_prop {
++ uint16_t sadb_prop_len;
++ uint16_t sadb_prop_exttype;
++ uint8_t sadb_prop_replay;
++ uint8_t sadb_prop_reserved[3];
++};
++
++struct sadb_comb {
++ uint8_t sadb_comb_auth;
++ uint8_t sadb_comb_encrypt;
++ uint16_t sadb_comb_flags;
++ uint16_t sadb_comb_auth_minbits;
++ uint16_t sadb_comb_auth_maxbits;
++ uint16_t sadb_comb_encrypt_minbits;
++ uint16_t sadb_comb_encrypt_maxbits;
++ uint32_t sadb_comb_reserved;
++ uint32_t sadb_comb_soft_allocations;
++ uint32_t sadb_comb_hard_allocations;
++ uint64_t sadb_comb_soft_bytes;
++ uint64_t sadb_comb_hard_bytes;
++ uint64_t sadb_comb_soft_addtime;
++ uint64_t sadb_comb_hard_addtime;
++ uint64_t sadb_comb_soft_usetime;
++ uint64_t sadb_comb_hard_usetime;
++ uint32_t sadb_x_comb_soft_packets;
++ uint32_t sadb_x_comb_hard_packets;
++};
++
++struct sadb_supported {
++ uint16_t sadb_supported_len;
++ uint16_t sadb_supported_exttype;
++ uint32_t sadb_supported_reserved;
++};
++
++struct sadb_alg {
++ uint8_t sadb_alg_id;
++ uint8_t sadb_alg_ivlen;
++ uint16_t sadb_alg_minbits;
++ uint16_t sadb_alg_maxbits;
++ uint16_t sadb_alg_reserved;
++};
++
++struct sadb_spirange {
++ uint16_t sadb_spirange_len;
++ uint16_t sadb_spirange_exttype;
++ uint32_t sadb_spirange_min;
++ uint32_t sadb_spirange_max;
++ uint32_t sadb_spirange_reserved;
++};
++
++struct sadb_x_kmprivate {
++ uint16_t sadb_x_kmprivate_len;
++ uint16_t sadb_x_kmprivate_exttype;
++ uint32_t sadb_x_kmprivate_reserved;
++};
++
++struct sadb_x_satype {
++ uint16_t sadb_x_satype_len;
++ uint16_t sadb_x_satype_exttype;
++ uint8_t sadb_x_satype_satype;
++ uint8_t sadb_x_satype_reserved[3];
++};
++
++struct sadb_x_policy {
++ uint16_t sadb_x_policy_len;
++ uint16_t sadb_x_policy_exttype;
++ uint16_t sadb_x_policy_type;
++ uint8_t sadb_x_policy_dir;
++ uint8_t sadb_x_policy_reserved;
++ uint32_t sadb_x_policy_id;
++ uint32_t sadb_x_policy_reserved2;
++};
++
++struct sadb_x_debug {
++ uint16_t sadb_x_debug_len;
++ uint16_t sadb_x_debug_exttype;
++ uint32_t sadb_x_debug_tunnel;
++ uint32_t sadb_x_debug_netlink;
++ uint32_t sadb_x_debug_xform;
++ uint32_t sadb_x_debug_eroute;
++ uint32_t sadb_x_debug_spi;
++ uint32_t sadb_x_debug_radij;
++ uint32_t sadb_x_debug_esp;
++ uint32_t sadb_x_debug_ah;
++ uint32_t sadb_x_debug_rcv;
++ uint32_t sadb_x_debug_pfkey;
++ uint32_t sadb_x_debug_ipcomp;
++ uint32_t sadb_x_debug_verbose;
++ uint8_t sadb_x_debug_reserved[4];
++};
++
++struct sadb_x_nat_t_type {
++ uint16_t sadb_x_nat_t_type_len;
++ uint16_t sadb_x_nat_t_type_exttype;
++ uint8_t sadb_x_nat_t_type_type;
++ uint8_t sadb_x_nat_t_type_reserved[3];
++};
++struct sadb_x_nat_t_port {
++ uint16_t sadb_x_nat_t_port_len;
++ uint16_t sadb_x_nat_t_port_exttype;
++ uint16_t sadb_x_nat_t_port_port;
++ uint16_t sadb_x_nat_t_port_reserved;
++};
++
++/*
++ * A protocol structure for passing through the transport level
++ * protocol. It contains more fields than are actually used/needed
++ * but it is this way to be compatible with the structure used in
++ * OpenBSD (http://www.openbsd.org/cgi-bin/cvsweb/src/sys/net/pfkeyv2.h)
++ */
++struct sadb_protocol {
++ uint16_t sadb_protocol_len;
++ uint16_t sadb_protocol_exttype;
++ uint8_t sadb_protocol_proto;
++ uint8_t sadb_protocol_direction;
++ uint8_t sadb_protocol_flags;
++ uint8_t sadb_protocol_reserved2;
++};
++
++#define SADB_EXT_RESERVED 0
++#define SADB_EXT_SA 1
++#define SADB_EXT_LIFETIME_CURRENT 2
++#define SADB_EXT_LIFETIME_HARD 3
++#define SADB_EXT_LIFETIME_SOFT 4
++#define SADB_EXT_ADDRESS_SRC 5
++#define SADB_EXT_ADDRESS_DST 6
++#define SADB_EXT_ADDRESS_PROXY 7
++#define SADB_EXT_KEY_AUTH 8
++#define SADB_EXT_KEY_ENCRYPT 9
++#define SADB_EXT_IDENTITY_SRC 10
++#define SADB_EXT_IDENTITY_DST 11
++#define SADB_EXT_SENSITIVITY 12
++#define SADB_EXT_PROPOSAL 13
++#define SADB_EXT_SUPPORTED_AUTH 14
++#define SADB_EXT_SUPPORTED_ENCRYPT 15
++#define SADB_EXT_SPIRANGE 16
++#define SADB_X_EXT_KMPRIVATE 17
++#define SADB_X_EXT_SATYPE2 18
++#ifdef KERNEL26_HAS_KAME_DUPLICATES
++#define SADB_X_EXT_POLICY 18
++#endif
++#define SADB_X_EXT_SA2 19
++#define SADB_X_EXT_ADDRESS_DST2 20
++#define SADB_X_EXT_ADDRESS_SRC_FLOW 21
++#define SADB_X_EXT_ADDRESS_DST_FLOW 22
++#define SADB_X_EXT_ADDRESS_SRC_MASK 23
++#define SADB_X_EXT_ADDRESS_DST_MASK 24
++#define SADB_X_EXT_DEBUG 25
++#define SADB_X_EXT_PROTOCOL 26
++#define SADB_X_EXT_NAT_T_TYPE 27
++#define SADB_X_EXT_NAT_T_SPORT 28
++#define SADB_X_EXT_NAT_T_DPORT 29
++#define SADB_X_EXT_NAT_T_OA 30
++#define SADB_EXT_MAX 30
++
++/* SADB_X_DELFLOW required over and above SADB_X_SAFLAGS_CLEARFLOW */
++#define SADB_X_EXT_ADDRESS_DELFLOW \
++ ( (1<<SADB_X_EXT_ADDRESS_SRC_FLOW) \
++ | (1<<SADB_X_EXT_ADDRESS_DST_FLOW) \
++ | (1<<SADB_X_EXT_ADDRESS_SRC_MASK) \
++ | (1<<SADB_X_EXT_ADDRESS_DST_MASK))
++
++#define SADB_SATYPE_UNSPEC 0
++#define SADB_SATYPE_AH 2
++#define SADB_SATYPE_ESP 3
++#define SADB_SATYPE_RSVP 5
++#define SADB_SATYPE_OSPFV2 6
++#define SADB_SATYPE_RIPV2 7
++#define SADB_SATYPE_MIP 8
++#define SADB_X_SATYPE_IPIP 9
++#ifdef KERNEL26_HAS_KAME_DUPLICATES
++#define SADB_X_SATYPE_IPCOMP 9 /* ICK! */
++#endif
++#define SADB_X_SATYPE_COMP 10
++#define SADB_X_SATYPE_INT 11
++#define SADB_SATYPE_MAX 11
++
++#define SADB_SASTATE_LARVAL 0
++#define SADB_SASTATE_MATURE 1
++#define SADB_SASTATE_DYING 2
++#define SADB_SASTATE_DEAD 3
++#define SADB_SASTATE_MAX 3
++
++#define SADB_SAFLAGS_PFS 1
++#define SADB_X_SAFLAGS_REPLACEFLOW 2
++#define SADB_X_SAFLAGS_CLEARFLOW 4
++#define SADB_X_SAFLAGS_INFLOW 8
++
++/* not obvious, but these are the same values as used in isakmp,
++ * and in freeswan/ipsec_policy.h. If you need to add any, they
++ * should be added as according to
++ * http://www.iana.org/assignments/isakmp-registry
++ *
++ * and if not, then please try to use a private-use value, and
++ * consider asking IANA to assign a value.
++ */
++#define SADB_AALG_NONE 0
++#define SADB_AALG_MD5HMAC 2
++#define SADB_AALG_SHA1HMAC 3
++#define SADB_X_AALG_SHA2_256HMAC 5
++#define SADB_X_AALG_SHA2_384HMAC 6
++#define SADB_X_AALG_SHA2_512HMAC 7
++#define SADB_X_AALG_RIPEMD160HMAC 8
++#define SADB_X_AALG_NULL 251 /* kame */
++#define SADB_AALG_MAX 251
++
++#define SADB_EALG_NONE 0
++#define SADB_EALG_DESCBC 2
++#define SADB_EALG_3DESCBC 3
++#define SADB_X_EALG_CASTCBC 6
++#define SADB_X_EALG_BLOWFISHCBC 7
++#define SADB_EALG_NULL 11
++#define SADB_X_EALG_AESCBC 12
++#define SADB_EALG_MAX 255
++
++#define SADB_X_CALG_NONE 0
++#define SADB_X_CALG_OUI 1
++#define SADB_X_CALG_DEFLATE 2
++#define SADB_X_CALG_LZS 3
++#define SADB_X_CALG_V42BIS 4
++#ifdef KERNEL26_HAS_KAME_DUPLICATES
++#define SADB_X_CALG_LZJH 4
++#endif
++#define SADB_X_CALG_MAX 4
++
++#define SADB_X_TALG_NONE 0
++#define SADB_X_TALG_IPv4_in_IPv4 1
++#define SADB_X_TALG_IPv6_in_IPv4 2
++#define SADB_X_TALG_IPv4_in_IPv6 3
++#define SADB_X_TALG_IPv6_in_IPv6 4
++#define SADB_X_TALG_MAX 4
++
++
++#define SADB_IDENTTYPE_RESERVED 0
++#define SADB_IDENTTYPE_PREFIX 1
++#define SADB_IDENTTYPE_FQDN 2
++#define SADB_IDENTTYPE_USERFQDN 3
++#define SADB_X_IDENTTYPE_CONNECTION 4
++#define SADB_IDENTTYPE_MAX 4
++
++#define SADB_KEY_FLAGS_MAX 0
++#endif /* __PFKEY_V2_H */
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.30 2004/04/06 02:49:00 mcr
++ * pullup of algo code from alg-branch.
++ *
++ * Revision 1.29 2003/12/22 21:35:58 mcr
++ * new patches from Dr{Who}.
++ *
++ * Revision 1.28 2003/12/22 19:33:15 mcr
++ * added 0.6c NAT-T patch.
++ *
++ * Revision 1.27 2003/12/10 01:20:01 mcr
++ * NAT-traversal patches to KLIPS.
++ *
++ * Revision 1.26 2003/10/31 02:26:44 mcr
++ * pulled up port-selector patches.
++ *
++ * Revision 1.25.4.1 2003/09/21 13:59:34 mcr
++ * pre-liminary X.509 patch - does not yet pass tests.
++ *
++ * Revision 1.25 2003/07/31 23:59:17 mcr
++ * re-introduce kernel 2.6 duplicate values for now.
++ * hope to get them changed!
++ *
++ * Revision 1.24 2003/07/31 22:55:27 mcr
++ * added some definitions to keep pfkeyv2.h files in sync.
++ *
++ * Revision 1.23 2003/05/11 00:43:48 mcr
++ * added comment about origin of values used
++ *
++ * Revision 1.22 2003/01/30 02:31:34 rgb
++ *
++ * Convert IPsecSAref_t from signed to unsigned to fix apparent SAref exhaustion bug.
++ *
++ * Revision 1.21 2002/12/16 19:26:49 mcr
++ * added definition of FS 1.xx sadb structure
++ *
++ * Revision 1.20 2002/09/20 15:40:25 rgb
++ * Added sadb_x_sa_ref to struct sadb_sa.
++ *
++ * Revision 1.19 2002/04/24 07:36:49 mcr
++ * Moved from ./lib/pfkeyv2.h,v
++ *
++ * Revision 1.18 2001/11/06 19:47:47 rgb
++ * Added packet parameter to lifetime and comb structures.
++ *
++ * Revision 1.17 2001/09/08 21:13:35 rgb
++ * Added pfkey ident extension support for ISAKMPd. (NetCelo)
++ *
++ * Revision 1.16 2001/07/06 19:49:46 rgb
++ * Added SADB_X_SAFLAGS_INFLOW for supporting incoming policy checks.
++ *
++ * Revision 1.15 2001/02/26 20:00:43 rgb
++ * Added internal IP protocol 61 for magic SAs.
++ *
++ * Revision 1.14 2001/02/08 18:51:05 rgb
++ * Include RFC document title and appendix subsection title.
++ *
++ * Revision 1.13 2000/10/10 20:10:20 rgb
++ * Added support for debug_ipcomp and debug_verbose to klipsdebug.
++ *
++ * Revision 1.12 2000/09/15 06:41:50 rgb
++ * Added V42BIS constant.
++ *
++ * Revision 1.11 2000/09/12 22:35:37 rgb
++ * Restructured to remove unused extensions from CLEARFLOW messages.
++ *
++ * Revision 1.10 2000/09/12 18:50:09 rgb
++ * Added IPIP tunnel types as algo support.
++ *
++ * Revision 1.9 2000/08/21 16:47:19 rgb
++ * Added SADB_X_CALG_* macros for IPCOMP.
++ *
++ * Revision 1.8 2000/08/09 20:43:34 rgb
++ * Fixed bitmask value for SADB_X_SAFLAGS_CLEAREROUTE.
++ *
++ * Revision 1.7 2000/01/21 06:28:37 rgb
++ * Added flow add/delete message type macros.
++ * Added flow address extension type macros.
++ * Tidied up spacing.
++ * Added klipsdebug switching capability.
++ *
++ * Revision 1.6 1999/11/27 11:56:08 rgb
++ * Add SADB_X_SATYPE_COMP for compression, eventually.
++ *
++ * Revision 1.5 1999/11/23 22:23:16 rgb
++ * This file has been moved in the distribution from klips/net/ipsec to
++ * lib.
++ *
++ * Revision 1.4 1999/04/29 15:23:29 rgb
++ * Add GRPSA support.
++ * Add support for a second SATYPE, SA and DST_ADDRESS.
++ * Add IPPROTO_IPIP support.
++ *
++ * Revision 1.3 1999/04/15 17:58:08 rgb
++ * Add RCSID labels.
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/include/zlib/zlib.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,893 @@
++/* zlib.h -- interface of the 'zlib' general purpose compression library
++ version 1.1.4, March 11th, 2002
++
++ Copyright (C) 1995-2002 Jean-loup Gailly and Mark Adler
++
++ This software is provided 'as-is', without any express or implied
++ warranty. In no event will the authors be held liable for any damages
++ arising from the use of this software.
++
++ Permission is granted to anyone to use this software for any purpose,
++ including commercial applications, and to alter it and redistribute it
++ freely, subject to the following restrictions:
++
++ 1. The origin of this software must not be misrepresented; you must not
++ claim that you wrote the original software. If you use this software
++ in a product, an acknowledgment in the product documentation would be
++ appreciated but is not required.
++ 2. Altered source versions must be plainly marked as such, and must not be
++ misrepresented as being the original software.
++ 3. This notice may not be removed or altered from any source distribution.
++
++ Jean-loup Gailly Mark Adler
++ jloup@gzip.org madler@alumni.caltech.edu
++
++
++ The data format used by the zlib library is described by RFCs (Request for
++ Comments) 1950 to 1952 in the files ftp://ds.internic.net/rfc/rfc1950.txt
++ (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format).
++*/
++
++#ifndef _ZLIB_H
++#define _ZLIB_H
++
++#include "zconf.h"
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#define ZLIB_VERSION "1.1.4"
++
++/*
++ The 'zlib' compression library provides in-memory compression and
++ decompression functions, including integrity checks of the uncompressed
++ data. This version of the library supports only one compression method
++ (deflation) but other algorithms will be added later and will have the same
++ stream interface.
++
++ Compression can be done in a single step if the buffers are large
++ enough (for example if an input file is mmap'ed), or can be done by
++ repeated calls of the compression function. In the latter case, the
++ application must provide more input and/or consume the output
++ (providing more output space) before each call.
++
++ The library also supports reading and writing files in gzip (.gz) format
++ with an interface similar to that of stdio.
++
++ The library does not install any signal handler. The decoder checks
++ the consistency of the compressed data, so the library should never
++ crash even in case of corrupted input.
++*/
++
++typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size));
++typedef void (*free_func) OF((voidpf opaque, voidpf address));
++
++struct internal_state;
++
++typedef struct z_stream_s {
++ Bytef *next_in; /* next input byte */
++ uInt avail_in; /* number of bytes available at next_in */
++ uLong total_in; /* total nb of input bytes read so far */
++
++ Bytef *next_out; /* next output byte should be put there */
++ uInt avail_out; /* remaining free space at next_out */
++ uLong total_out; /* total nb of bytes output so far */
++
++ const char *msg; /* last error message, NULL if no error */
++ struct internal_state FAR *state; /* not visible by applications */
++
++ alloc_func zalloc; /* used to allocate the internal state */
++ free_func zfree; /* used to free the internal state */
++ voidpf opaque; /* private data object passed to zalloc and zfree */
++
++ int data_type; /* best guess about the data type: ascii or binary */
++ uLong adler; /* adler32 value of the uncompressed data */
++ uLong reserved; /* reserved for future use */
++} z_stream;
++
++typedef z_stream FAR *z_streamp;
++
++/*
++ The application must update next_in and avail_in when avail_in has
++ dropped to zero. It must update next_out and avail_out when avail_out
++ has dropped to zero. The application must initialize zalloc, zfree and
++ opaque before calling the init function. All other fields are set by the
++ compression library and must not be updated by the application.
++
++ The opaque value provided by the application will be passed as the first
++ parameter for calls of zalloc and zfree. This can be useful for custom
++ memory management. The compression library attaches no meaning to the
++ opaque value.
++
++ zalloc must return Z_NULL if there is not enough memory for the object.
++ If zlib is used in a multi-threaded application, zalloc and zfree must be
++ thread safe.
++
++ On 16-bit systems, the functions zalloc and zfree must be able to allocate
++ exactly 65536 bytes, but will not be required to allocate more than this
++ if the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS,
++ pointers returned by zalloc for objects of exactly 65536 bytes *must*
++ have their offset normalized to zero. The default allocation function
++ provided by this library ensures this (see zutil.c). To reduce memory
++ requirements and avoid any allocation of 64K objects, at the expense of
++ compression ratio, compile the library with -DMAX_WBITS=14 (see zconf.h).
++
++ The fields total_in and total_out can be used for statistics or
++ progress reports. After compression, total_in holds the total size of
++ the uncompressed data and may be saved for use in the decompressor
++ (particularly if the decompressor wants to decompress everything in
++ a single step).
++*/
++
++ /* constants */
++
++#define Z_NO_FLUSH 0
++#define Z_PARTIAL_FLUSH 1 /* will be removed, use Z_SYNC_FLUSH instead */
++#define Z_SYNC_FLUSH 2
++#define Z_FULL_FLUSH 3
++#define Z_FINISH 4
++/* Allowed flush values; see deflate() below for details */
++
++#define Z_OK 0
++#define Z_STREAM_END 1
++#define Z_NEED_DICT 2
++#define Z_ERRNO (-1)
++#define Z_STREAM_ERROR (-2)
++#define Z_DATA_ERROR (-3)
++#define Z_MEM_ERROR (-4)
++#define Z_BUF_ERROR (-5)
++#define Z_VERSION_ERROR (-6)
++/* Return codes for the compression/decompression functions. Negative
++ * values are errors, positive values are used for special but normal events.
++ */
++
++#define Z_NO_COMPRESSION 0
++#define Z_BEST_SPEED 1
++#define Z_BEST_COMPRESSION 9
++#define Z_DEFAULT_COMPRESSION (-1)
++/* compression levels */
++
++#define Z_FILTERED 1
++#define Z_HUFFMAN_ONLY 2
++#define Z_DEFAULT_STRATEGY 0
++/* compression strategy; see deflateInit2() below for details */
++
++#define Z_BINARY 0
++#define Z_ASCII 1
++#define Z_UNKNOWN 2
++/* Possible values of the data_type field */
++
++#define Z_DEFLATED 8
++/* The deflate compression method (the only one supported in this version) */
++
++#define Z_NULL 0 /* for initializing zalloc, zfree, opaque */
++
++#define zlib_version zlibVersion()
++/* for compatibility with versions < 1.0.2 */
++
++ /* basic functions */
++
++ZEXTERN const char * ZEXPORT zlibVersion OF((void));
++/* The application can compare zlibVersion and ZLIB_VERSION for consistency.
++ If the first character differs, the library code actually used is
++ not compatible with the zlib.h header file used by the application.
++ This check is automatically made by deflateInit and inflateInit.
++ */
++
++/*
++ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level));
++
++ Initializes the internal stream state for compression. The fields
++ zalloc, zfree and opaque must be initialized before by the caller.
++ If zalloc and zfree are set to Z_NULL, deflateInit updates them to
++ use default allocation functions.
++
++ The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9:
++ 1 gives best speed, 9 gives best compression, 0 gives no compression at
++ all (the input data is simply copied a block at a time).
++ Z_DEFAULT_COMPRESSION requests a default compromise between speed and
++ compression (currently equivalent to level 6).
++
++ deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not
++ enough memory, Z_STREAM_ERROR if level is not a valid compression level,
++ Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible
++ with the version assumed by the caller (ZLIB_VERSION).
++ msg is set to null if there is no error message. deflateInit does not
++ perform any compression: this will be done by deflate().
++*/
++
++
++ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush));
++/*
++ deflate compresses as much data as possible, and stops when the input
++ buffer becomes empty or the output buffer becomes full. It may introduce some
++ output latency (reading input without producing any output) except when
++ forced to flush.
++
++ The detailed semantics are as follows. deflate performs one or both of the
++ following actions:
++
++ - Compress more input starting at next_in and update next_in and avail_in
++ accordingly. If not all input can be processed (because there is not
++ enough room in the output buffer), next_in and avail_in are updated and
++ processing will resume at this point for the next call of deflate().
++
++ - Provide more output starting at next_out and update next_out and avail_out
++ accordingly. This action is forced if the parameter flush is non zero.
++ Forcing flush frequently degrades the compression ratio, so this parameter
++ should be set only when necessary (in interactive applications).
++ Some output may be provided even if flush is not set.
++
++ Before the call of deflate(), the application should ensure that at least
++ one of the actions is possible, by providing more input and/or consuming
++ more output, and updating avail_in or avail_out accordingly; avail_out
++ should never be zero before the call. The application can consume the
++ compressed output when it wants, for example when the output buffer is full
++ (avail_out == 0), or after each call of deflate(). If deflate returns Z_OK
++ and with zero avail_out, it must be called again after making room in the
++ output buffer because there might be more output pending.
++
++ If the parameter flush is set to Z_SYNC_FLUSH, all pending output is
++ flushed to the output buffer and the output is aligned on a byte boundary, so
++ that the decompressor can get all input data available so far. (In particular
++ avail_in is zero after the call if enough output space has been provided
++ before the call.) Flushing may degrade compression for some compression
++ algorithms and so it should be used only when necessary.
++
++ If flush is set to Z_FULL_FLUSH, all output is flushed as with
++ Z_SYNC_FLUSH, and the compression state is reset so that decompression can
++ restart from this point if previous compressed data has been damaged or if
++ random access is desired. Using Z_FULL_FLUSH too often can seriously degrade
++ the compression.
++
++ If deflate returns with avail_out == 0, this function must be called again
++ with the same value of the flush parameter and more output space (updated
++ avail_out), until the flush is complete (deflate returns with non-zero
++ avail_out).
++
++ If the parameter flush is set to Z_FINISH, pending input is processed,
++ pending output is flushed and deflate returns with Z_STREAM_END if there
++ was enough output space; if deflate returns with Z_OK, this function must be
++ called again with Z_FINISH and more output space (updated avail_out) but no
++ more input data, until it returns with Z_STREAM_END or an error. After
++ deflate has returned Z_STREAM_END, the only possible operations on the
++ stream are deflateReset or deflateEnd.
++
++ Z_FINISH can be used immediately after deflateInit if all the compression
++ is to be done in a single step. In this case, avail_out must be at least
++ 0.1% larger than avail_in plus 12 bytes. If deflate does not return
++ Z_STREAM_END, then it must be called again as described above.
++
++ deflate() sets strm->adler to the adler32 checksum of all input read
++ so far (that is, total_in bytes).
++
++ deflate() may update data_type if it can make a good guess about
++ the input data type (Z_ASCII or Z_BINARY). In doubt, the data is considered
++ binary. This field is only for information purposes and does not affect
++ the compression algorithm in any manner.
++
++ deflate() returns Z_OK if some progress has been made (more input
++ processed or more output produced), Z_STREAM_END if all input has been
++ consumed and all output has been produced (only when flush is set to
++ Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example
++ if next_in or next_out was NULL), Z_BUF_ERROR if no progress is possible
++ (for example avail_in or avail_out was zero).
++*/
++
++
++ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm));
++/*
++ All dynamically allocated data structures for this stream are freed.
++ This function discards any unprocessed input and does not flush any
++ pending output.
++
++ deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the
++ stream state was inconsistent, Z_DATA_ERROR if the stream was freed
++ prematurely (some input or output was discarded). In the error case,
++ msg may be set but then points to a static string (which must not be
++ deallocated).
++*/
++
++
++/*
++ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm));
++
++ Initializes the internal stream state for decompression. The fields
++ next_in, avail_in, zalloc, zfree and opaque must be initialized before by
++ the caller. If next_in is not Z_NULL and avail_in is large enough (the exact
++ value depends on the compression method), inflateInit determines the
++ compression method from the zlib header and allocates all data structures
++ accordingly; otherwise the allocation will be deferred to the first call of
++ inflate. If zalloc and zfree are set to Z_NULL, inflateInit updates them to
++ use default allocation functions.
++
++ inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough
++ memory, Z_VERSION_ERROR if the zlib library version is incompatible with the
++ version assumed by the caller. msg is set to null if there is no error
++ message. inflateInit does not perform any decompression apart from reading
++ the zlib header if present: this will be done by inflate(). (So next_in and
++ avail_in may be modified, but next_out and avail_out are unchanged.)
++*/
++
++
++ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush));
++/*
++ inflate decompresses as much data as possible, and stops when the input
++ buffer becomes empty or the output buffer becomes full. It may some
++ introduce some output latency (reading input without producing any output)
++ except when forced to flush.
++
++ The detailed semantics are as follows. inflate performs one or both of the
++ following actions:
++
++ - Decompress more input starting at next_in and update next_in and avail_in
++ accordingly. If not all input can be processed (because there is not
++ enough room in the output buffer), next_in is updated and processing
++ will resume at this point for the next call of inflate().
++
++ - Provide more output starting at next_out and update next_out and avail_out
++ accordingly. inflate() provides as much output as possible, until there
++ is no more input data or no more space in the output buffer (see below
++ about the flush parameter).
++
++ Before the call of inflate(), the application should ensure that at least
++ one of the actions is possible, by providing more input and/or consuming
++ more output, and updating the next_* and avail_* values accordingly.
++ The application can consume the uncompressed output when it wants, for
++ example when the output buffer is full (avail_out == 0), or after each
++ call of inflate(). If inflate returns Z_OK and with zero avail_out, it
++ must be called again after making room in the output buffer because there
++ might be more output pending.
++
++ If the parameter flush is set to Z_SYNC_FLUSH, inflate flushes as much
++ output as possible to the output buffer. The flushing behavior of inflate is
++ not specified for values of the flush parameter other than Z_SYNC_FLUSH
++ and Z_FINISH, but the current implementation actually flushes as much output
++ as possible anyway.
++
++ inflate() should normally be called until it returns Z_STREAM_END or an
++ error. However if all decompression is to be performed in a single step
++ (a single call of inflate), the parameter flush should be set to
++ Z_FINISH. In this case all pending input is processed and all pending
++ output is flushed; avail_out must be large enough to hold all the
++ uncompressed data. (The size of the uncompressed data may have been saved
++ by the compressor for this purpose.) The next operation on this stream must
++ be inflateEnd to deallocate the decompression state. The use of Z_FINISH
++ is never required, but can be used to inform inflate that a faster routine
++ may be used for the single inflate() call.
++
++ If a preset dictionary is needed at this point (see inflateSetDictionary
++ below), inflate sets strm-adler to the adler32 checksum of the
++ dictionary chosen by the compressor and returns Z_NEED_DICT; otherwise
++ it sets strm->adler to the adler32 checksum of all output produced
++ so far (that is, total_out bytes) and returns Z_OK, Z_STREAM_END or
++ an error code as described below. At the end of the stream, inflate()
++ checks that its computed adler32 checksum is equal to that saved by the
++ compressor and returns Z_STREAM_END only if the checksum is correct.
++
++ inflate() returns Z_OK if some progress has been made (more input processed
++ or more output produced), Z_STREAM_END if the end of the compressed data has
++ been reached and all uncompressed output has been produced, Z_NEED_DICT if a
++ preset dictionary is needed at this point, Z_DATA_ERROR if the input data was
++ corrupted (input stream not conforming to the zlib format or incorrect
++ adler32 checksum), Z_STREAM_ERROR if the stream structure was inconsistent
++ (for example if next_in or next_out was NULL), Z_MEM_ERROR if there was not
++ enough memory, Z_BUF_ERROR if no progress is possible or if there was not
++ enough room in the output buffer when Z_FINISH is used. In the Z_DATA_ERROR
++ case, the application may then call inflateSync to look for a good
++ compression block.
++*/
++
++
++ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm));
++/*
++ All dynamically allocated data structures for this stream are freed.
++ This function discards any unprocessed input and does not flush any
++ pending output.
++
++ inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state
++ was inconsistent. In the error case, msg may be set but then points to a
++ static string (which must not be deallocated).
++*/
++
++ /* Advanced functions */
++
++/*
++ The following functions are needed only in some special applications.
++*/
++
++/*
++ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm,
++ int level,
++ int method,
++ int windowBits,
++ int memLevel,
++ int strategy));
++
++ This is another version of deflateInit with more compression options. The
++ fields next_in, zalloc, zfree and opaque must be initialized before by
++ the caller.
++
++ The method parameter is the compression method. It must be Z_DEFLATED in
++ this version of the library.
++
++ The windowBits parameter is the base two logarithm of the window size
++ (the size of the history buffer). It should be in the range 8..15 for this
++ version of the library. Larger values of this parameter result in better
++ compression at the expense of memory usage. The default value is 15 if
++ deflateInit is used instead.
++
++ The memLevel parameter specifies how much memory should be allocated
++ for the internal compression state. memLevel=1 uses minimum memory but
++ is slow and reduces compression ratio; memLevel=9 uses maximum memory
++ for optimal speed. The default value is 8. See zconf.h for total memory
++ usage as a function of windowBits and memLevel.
++
++ The strategy parameter is used to tune the compression algorithm. Use the
++ value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a
++ filter (or predictor), or Z_HUFFMAN_ONLY to force Huffman encoding only (no
++ string match). Filtered data consists mostly of small values with a
++ somewhat random distribution. In this case, the compression algorithm is
++ tuned to compress them better. The effect of Z_FILTERED is to force more
++ Huffman coding and less string matching; it is somewhat intermediate
++ between Z_DEFAULT and Z_HUFFMAN_ONLY. The strategy parameter only affects
++ the compression ratio but not the correctness of the compressed output even
++ if it is not set appropriately.
++
++ deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
++ memory, Z_STREAM_ERROR if a parameter is invalid (such as an invalid
++ method). msg is set to null if there is no error message. deflateInit2 does
++ not perform any compression: this will be done by deflate().
++*/
++
++ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm,
++ const Bytef *dictionary,
++ uInt dictLength));
++/*
++ Initializes the compression dictionary from the given byte sequence
++ without producing any compressed output. This function must be called
++ immediately after deflateInit, deflateInit2 or deflateReset, before any
++ call of deflate. The compressor and decompressor must use exactly the same
++ dictionary (see inflateSetDictionary).
++
++ The dictionary should consist of strings (byte sequences) that are likely
++ to be encountered later in the data to be compressed, with the most commonly
++ used strings preferably put towards the end of the dictionary. Using a
++ dictionary is most useful when the data to be compressed is short and can be
++ predicted with good accuracy; the data can then be compressed better than
++ with the default empty dictionary.
++
++ Depending on the size of the compression data structures selected by
++ deflateInit or deflateInit2, a part of the dictionary may in effect be
++ discarded, for example if the dictionary is larger than the window size in
++ deflate or deflate2. Thus the strings most likely to be useful should be
++ put at the end of the dictionary, not at the front.
++
++ Upon return of this function, strm->adler is set to the Adler32 value
++ of the dictionary; the decompressor may later use this value to determine
++ which dictionary has been used by the compressor. (The Adler32 value
++ applies to the whole dictionary even if only a subset of the dictionary is
++ actually used by the compressor.)
++
++ deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a
++ parameter is invalid (such as NULL dictionary) or the stream state is
++ inconsistent (for example if deflate has already been called for this stream
++ or if the compression method is bsort). deflateSetDictionary does not
++ perform any compression: this will be done by deflate().
++*/
++
++ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest,
++ z_streamp source));
++/*
++ Sets the destination stream as a complete copy of the source stream.
++
++ This function can be useful when several compression strategies will be
++ tried, for example when there are several ways of pre-processing the input
++ data with a filter. The streams that will be discarded should then be freed
++ by calling deflateEnd. Note that deflateCopy duplicates the internal
++ compression state which can be quite large, so this strategy is slow and
++ can consume lots of memory.
++
++ deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not
++ enough memory, Z_STREAM_ERROR if the source stream state was inconsistent
++ (such as zalloc being NULL). msg is left unchanged in both source and
++ destination.
++*/
++
++ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm));
++/*
++ This function is equivalent to deflateEnd followed by deflateInit,
++ but does not free and reallocate all the internal compression state.
++ The stream will keep the same compression level and any other attributes
++ that may have been set by deflateInit2.
++
++ deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source
++ stream state was inconsistent (such as zalloc or state being NULL).
++*/
++
++ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm,
++ int level,
++ int strategy));
++/*
++ Dynamically update the compression level and compression strategy. The
++ interpretation of level and strategy is as in deflateInit2. This can be
++ used to switch between compression and straight copy of the input data, or
++ to switch to a different kind of input data requiring a different
++ strategy. If the compression level is changed, the input available so far
++ is compressed with the old level (and may be flushed); the new level will
++ take effect only at the next call of deflate().
++
++ Before the call of deflateParams, the stream state must be set as for
++ a call of deflate(), since the currently available input may have to
++ be compressed and flushed. In particular, strm->avail_out must be non-zero.
++
++ deflateParams returns Z_OK if success, Z_STREAM_ERROR if the source
++ stream state was inconsistent or if a parameter was invalid, Z_BUF_ERROR
++ if strm->avail_out was zero.
++*/
++
++/*
++ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm,
++ int windowBits));
++
++ This is another version of inflateInit with an extra parameter. The
++ fields next_in, avail_in, zalloc, zfree and opaque must be initialized
++ before by the caller.
++
++ The windowBits parameter is the base two logarithm of the maximum window
++ size (the size of the history buffer). It should be in the range 8..15 for
++ this version of the library. The default value is 15 if inflateInit is used
++ instead. If a compressed stream with a larger window size is given as
++ input, inflate() will return with the error code Z_DATA_ERROR instead of
++ trying to allocate a larger window.
++
++ inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
++ memory, Z_STREAM_ERROR if a parameter is invalid (such as a negative
++ memLevel). msg is set to null if there is no error message. inflateInit2
++ does not perform any decompression apart from reading the zlib header if
++ present: this will be done by inflate(). (So next_in and avail_in may be
++ modified, but next_out and avail_out are unchanged.)
++*/
++
++ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm,
++ const Bytef *dictionary,
++ uInt dictLength));
++/*
++ Initializes the decompression dictionary from the given uncompressed byte
++ sequence. This function must be called immediately after a call of inflate
++ if this call returned Z_NEED_DICT. The dictionary chosen by the compressor
++ can be determined from the Adler32 value returned by this call of
++ inflate. The compressor and decompressor must use exactly the same
++ dictionary (see deflateSetDictionary).
++
++ inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a
++ parameter is invalid (such as NULL dictionary) or the stream state is
++ inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the
++ expected one (incorrect Adler32 value). inflateSetDictionary does not
++ perform any decompression: this will be done by subsequent calls of
++ inflate().
++*/
++
++ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm));
++/*
++ Skips invalid compressed data until a full flush point (see above the
++ description of deflate with Z_FULL_FLUSH) can be found, or until all
++ available input is skipped. No output is provided.
++
++ inflateSync returns Z_OK if a full flush point has been found, Z_BUF_ERROR
++ if no more input was provided, Z_DATA_ERROR if no flush point has been found,
++ or Z_STREAM_ERROR if the stream structure was inconsistent. In the success
++ case, the application may save the current current value of total_in which
++ indicates where valid compressed data was found. In the error case, the
++ application may repeatedly call inflateSync, providing more input each time,
++ until success or end of the input data.
++*/
++
++ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm));
++/*
++ This function is equivalent to inflateEnd followed by inflateInit,
++ but does not free and reallocate all the internal decompression state.
++ The stream will keep attributes that may have been set by inflateInit2.
++
++ inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source
++ stream state was inconsistent (such as zalloc or state being NULL).
++*/
++
++
++ /* utility functions */
++
++/*
++ The following utility functions are implemented on top of the
++ basic stream-oriented functions. To simplify the interface, some
++ default options are assumed (compression level and memory usage,
++ standard memory allocation functions). The source code of these
++ utility functions can easily be modified if you need special options.
++*/
++
++ZEXTERN int ZEXPORT compress OF((Bytef *dest, uLongf *destLen,
++ const Bytef *source, uLong sourceLen));
++/*
++ Compresses the source buffer into the destination buffer. sourceLen is
++ the byte length of the source buffer. Upon entry, destLen is the total
++ size of the destination buffer, which must be at least 0.1% larger than
++ sourceLen plus 12 bytes. Upon exit, destLen is the actual size of the
++ compressed buffer.
++ This function can be used to compress a whole file at once if the
++ input file is mmap'ed.
++ compress returns Z_OK if success, Z_MEM_ERROR if there was not
++ enough memory, Z_BUF_ERROR if there was not enough room in the output
++ buffer.
++*/
++
++ZEXTERN int ZEXPORT compress2 OF((Bytef *dest, uLongf *destLen,
++ const Bytef *source, uLong sourceLen,
++ int level));
++/*
++ Compresses the source buffer into the destination buffer. The level
++ parameter has the same meaning as in deflateInit. sourceLen is the byte
++ length of the source buffer. Upon entry, destLen is the total size of the
++ destination buffer, which must be at least 0.1% larger than sourceLen plus
++ 12 bytes. Upon exit, destLen is the actual size of the compressed buffer.
++
++ compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
++ memory, Z_BUF_ERROR if there was not enough room in the output buffer,
++ Z_STREAM_ERROR if the level parameter is invalid.
++*/
++
++ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen,
++ const Bytef *source, uLong sourceLen));
++/*
++ Decompresses the source buffer into the destination buffer. sourceLen is
++ the byte length of the source buffer. Upon entry, destLen is the total
++ size of the destination buffer, which must be large enough to hold the
++ entire uncompressed data. (The size of the uncompressed data must have
++ been saved previously by the compressor and transmitted to the decompressor
++ by some mechanism outside the scope of this compression library.)
++ Upon exit, destLen is the actual size of the compressed buffer.
++ This function can be used to decompress a whole file at once if the
++ input file is mmap'ed.
++
++ uncompress returns Z_OK if success, Z_MEM_ERROR if there was not
++ enough memory, Z_BUF_ERROR if there was not enough room in the output
++ buffer, or Z_DATA_ERROR if the input data was corrupted.
++*/
++
++
++typedef voidp gzFile;
++
++ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode));
++/*
++ Opens a gzip (.gz) file for reading or writing. The mode parameter
++ is as in fopen ("rb" or "wb") but can also include a compression level
++ ("wb9") or a strategy: 'f' for filtered data as in "wb6f", 'h' for
++ Huffman only compression as in "wb1h". (See the description
++ of deflateInit2 for more information about the strategy parameter.)
++
++ gzopen can be used to read a file which is not in gzip format; in this
++ case gzread will directly read from the file without decompression.
++
++ gzopen returns NULL if the file could not be opened or if there was
++ insufficient memory to allocate the (de)compression state; errno
++ can be checked to distinguish the two cases (if errno is zero, the
++ zlib error is Z_MEM_ERROR). */
++
++ZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode));
++/*
++ gzdopen() associates a gzFile with the file descriptor fd. File
++ descriptors are obtained from calls like open, dup, creat, pipe or
++ fileno (in the file has been previously opened with fopen).
++ The mode parameter is as in gzopen.
++ The next call of gzclose on the returned gzFile will also close the
++ file descriptor fd, just like fclose(fdopen(fd), mode) closes the file
++ descriptor fd. If you want to keep fd open, use gzdopen(dup(fd), mode).
++ gzdopen returns NULL if there was insufficient memory to allocate
++ the (de)compression state.
++*/
++
++ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy));
++/*
++ Dynamically update the compression level or strategy. See the description
++ of deflateInit2 for the meaning of these parameters.
++ gzsetparams returns Z_OK if success, or Z_STREAM_ERROR if the file was not
++ opened for writing.
++*/
++
++ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len));
++/*
++ Reads the given number of uncompressed bytes from the compressed file.
++ If the input file was not in gzip format, gzread copies the given number
++ of bytes into the buffer.
++ gzread returns the number of uncompressed bytes actually read (0 for
++ end of file, -1 for error). */
++
++ZEXTERN int ZEXPORT gzwrite OF((gzFile file,
++ const voidp buf, unsigned len));
++/*
++ Writes the given number of uncompressed bytes into the compressed file.
++ gzwrite returns the number of uncompressed bytes actually written
++ (0 in case of error).
++*/
++
++ZEXTERN int ZEXPORTVA gzprintf OF((gzFile file, const char *format, ...));
++/*
++ Converts, formats, and writes the args to the compressed file under
++ control of the format string, as in fprintf. gzprintf returns the number of
++ uncompressed bytes actually written (0 in case of error).
++*/
++
++ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s));
++/*
++ Writes the given null-terminated string to the compressed file, excluding
++ the terminating null character.
++ gzputs returns the number of characters written, or -1 in case of error.
++*/
++
++ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len));
++/*
++ Reads bytes from the compressed file until len-1 characters are read, or
++ a newline character is read and transferred to buf, or an end-of-file
++ condition is encountered. The string is then terminated with a null
++ character.
++ gzgets returns buf, or Z_NULL in case of error.
++*/
++
++ZEXTERN int ZEXPORT gzputc OF((gzFile file, int c));
++/*
++ Writes c, converted to an unsigned char, into the compressed file.
++ gzputc returns the value that was written, or -1 in case of error.
++*/
++
++ZEXTERN int ZEXPORT gzgetc OF((gzFile file));
++/*
++ Reads one byte from the compressed file. gzgetc returns this byte
++ or -1 in case of end of file or error.
++*/
++
++ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush));
++/*
++ Flushes all pending output into the compressed file. The parameter
++ flush is as in the deflate() function. The return value is the zlib
++ error number (see function gzerror below). gzflush returns Z_OK if
++ the flush parameter is Z_FINISH and all output could be flushed.
++ gzflush should be called only when strictly necessary because it can
++ degrade compression.
++*/
++
++ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file,
++ z_off_t offset, int whence));
++/*
++ Sets the starting position for the next gzread or gzwrite on the
++ given compressed file. The offset represents a number of bytes in the
++ uncompressed data stream. The whence parameter is defined as in lseek(2);
++ the value SEEK_END is not supported.
++ If the file is opened for reading, this function is emulated but can be
++ extremely slow. If the file is opened for writing, only forward seeks are
++ supported; gzseek then compresses a sequence of zeroes up to the new
++ starting position.
++
++ gzseek returns the resulting offset location as measured in bytes from
++ the beginning of the uncompressed stream, or -1 in case of error, in
++ particular if the file is opened for writing and the new starting position
++ would be before the current position.
++*/
++
++ZEXTERN int ZEXPORT gzrewind OF((gzFile file));
++/*
++ Rewinds the given file. This function is supported only for reading.
++
++ gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET)
++*/
++
++ZEXTERN z_off_t ZEXPORT gztell OF((gzFile file));
++/*
++ Returns the starting position for the next gzread or gzwrite on the
++ given compressed file. This position represents a number of bytes in the
++ uncompressed data stream.
++
++ gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR)
++*/
++
++ZEXTERN int ZEXPORT gzeof OF((gzFile file));
++/*
++ Returns 1 when EOF has previously been detected reading the given
++ input stream, otherwise zero.
++*/
++
++ZEXTERN int ZEXPORT gzclose OF((gzFile file));
++/*
++ Flushes all pending output if necessary, closes the compressed file
++ and deallocates all the (de)compression state. The return value is the zlib
++ error number (see function gzerror below).
++*/
++
++ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum));
++/*
++ Returns the error message for the last error which occurred on the
++ given compressed file. errnum is set to zlib error number. If an
++ error occurred in the file system and not in the compression library,
++ errnum is set to Z_ERRNO and the application may consult errno
++ to get the exact error code.
++*/
++
++ /* checksum functions */
++
++/*
++ These functions are not related to compression but are exported
++ anyway because they might be useful in applications using the
++ compression library.
++*/
++
++ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len));
++
++/*
++ Update a running Adler-32 checksum with the bytes buf[0..len-1] and
++ return the updated checksum. If buf is NULL, this function returns
++ the required initial value for the checksum.
++ An Adler-32 checksum is almost as reliable as a CRC32 but can be computed
++ much faster. Usage example:
++
++ uLong adler = adler32(0L, Z_NULL, 0);
++
++ while (read_buffer(buffer, length) != EOF) {
++ adler = adler32(adler, buffer, length);
++ }
++ if (adler != original_adler) error();
++*/
++
++ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len));
++/*
++ Update a running crc with the bytes buf[0..len-1] and return the updated
++ crc. If buf is NULL, this function returns the required initial value
++ for the crc. Pre- and post-conditioning (one's complement) is performed
++ within this function so it shouldn't be done by the application.
++ Usage example:
++
++ uLong crc = crc32(0L, Z_NULL, 0);
++
++ while (read_buffer(buffer, length) != EOF) {
++ crc = crc32(crc, buffer, length);
++ }
++ if (crc != original_crc) error();
++*/
++
++
++ /* various hacks, don't look :) */
++
++/* deflateInit and inflateInit are macros to allow checking the zlib version
++ * and the compiler's view of z_stream:
++ */
++ZEXTERN int ZEXPORT deflateInit_ OF((z_streamp strm, int level,
++ const char *version, int stream_size));
++ZEXTERN int ZEXPORT inflateInit_ OF((z_streamp strm,
++ const char *version, int stream_size));
++ZEXTERN int ZEXPORT deflateInit2_ OF((z_streamp strm, int level, int method,
++ int windowBits, int memLevel,
++ int strategy, const char *version,
++ int stream_size));
++ZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int windowBits,
++ const char *version, int stream_size));
++#define deflateInit(strm, level) \
++ deflateInit_((strm), (level), ZLIB_VERSION, sizeof(z_stream))
++#define inflateInit(strm) \
++ inflateInit_((strm), ZLIB_VERSION, sizeof(z_stream))
++#define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \
++ deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\
++ (strategy), ZLIB_VERSION, sizeof(z_stream))
++#define inflateInit2(strm, windowBits) \
++ inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream))
++
++
++#if !defined(_Z_UTIL_H) && !defined(NO_DUMMY_DECL)
++ struct internal_state {int dummy;}; /* hack for buggy compilers */
++#endif
++
++ZEXTERN const char * ZEXPORT zError OF((int err));
++ZEXTERN int ZEXPORT inflateSyncPoint OF((z_streamp z));
++ZEXTERN const uLongf * ZEXPORT get_crc_table OF((void));
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif /* _ZLIB_H */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/include/zlib/zutil.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,225 @@
++/* zutil.h -- internal interface and configuration of the compression library
++ * Copyright (C) 1995-2002 Jean-loup Gailly.
++ * For conditions of distribution and use, see copyright notice in zlib.h
++ */
++
++/* WARNING: this file should *not* be used by applications. It is
++ part of the implementation of the compression library and is
++ subject to change. Applications should only use zlib.h.
++ */
++
++/* @(#) $Id$ */
++
++#ifndef _Z_UTIL_H
++#define _Z_UTIL_H
++
++#include "zlib.h"
++
++#include <linux/string.h>
++#define HAVE_MEMCPY
++
++#if 0 // #ifdef STDC
++# include <stddef.h>
++# include <string.h>
++# include <stdlib.h>
++#endif
++#ifndef __KERNEL__
++#ifdef NO_ERRNO_H
++ extern int errno;
++#else
++# include <errno.h>
++#endif
++#endif
++
++#ifndef local
++# define local static
++#endif
++/* compile with -Dlocal if your debugger can't find static symbols */
++
++typedef unsigned char uch;
++typedef uch FAR uchf;
++typedef unsigned short ush;
++typedef ush FAR ushf;
++typedef unsigned long ulg;
++
++extern const char *z_errmsg[10]; /* indexed by 2-zlib_error */
++/* (size given to avoid silly warnings with Visual C++) */
++
++#define ERR_MSG(err) z_errmsg[Z_NEED_DICT-(err)]
++
++#define ERR_RETURN(strm,err) \
++ return (strm->msg = ERR_MSG(err), (err))
++/* To be used only when the state is known to be valid */
++
++ /* common constants */
++
++#ifndef DEF_WBITS
++# define DEF_WBITS MAX_WBITS
++#endif
++/* default windowBits for decompression. MAX_WBITS is for compression only */
++
++#if MAX_MEM_LEVEL >= 8
++# define DEF_MEM_LEVEL 8
++#else
++# define DEF_MEM_LEVEL MAX_MEM_LEVEL
++#endif
++/* default memLevel */
++
++#define STORED_BLOCK 0
++#define STATIC_TREES 1
++#define DYN_TREES 2
++/* The three kinds of block type */
++
++#define MIN_MATCH 3
++#define MAX_MATCH 258
++/* The minimum and maximum match lengths */
++
++#define PRESET_DICT 0x20 /* preset dictionary flag in zlib header */
++
++ /* target dependencies */
++
++#ifdef MSDOS
++# define OS_CODE 0x00
++# if defined(__TURBOC__) || defined(__BORLANDC__)
++# if(__STDC__ == 1) && (defined(__LARGE__) || defined(__COMPACT__))
++ /* Allow compilation with ANSI keywords only enabled */
++ void _Cdecl farfree( void *block );
++ void *_Cdecl farmalloc( unsigned long nbytes );
++# else
++# include <alloc.h>
++# endif
++# else /* MSC or DJGPP */
++# include <malloc.h>
++# endif
++#endif
++
++#ifdef OS2
++# define OS_CODE 0x06
++#endif
++
++#ifdef WIN32 /* Window 95 & Windows NT */
++# define OS_CODE 0x0b
++#endif
++
++#if defined(VAXC) || defined(VMS)
++# define OS_CODE 0x02
++# define F_OPEN(name, mode) \
++ fopen((name), (mode), "mbc=60", "ctx=stm", "rfm=fix", "mrs=512")
++#endif
++
++#ifdef AMIGA
++# define OS_CODE 0x01
++#endif
++
++#if defined(ATARI) || defined(atarist)
++# define OS_CODE 0x05
++#endif
++
++#if defined(MACOS) || defined(TARGET_OS_MAC)
++# define OS_CODE 0x07
++# if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os
++# include <unix.h> /* for fdopen */
++# else
++# ifndef fdopen
++# define fdopen(fd,mode) NULL /* No fdopen() */
++# endif
++# endif
++#endif
++
++#ifdef __50SERIES /* Prime/PRIMOS */
++# define OS_CODE 0x0F
++#endif
++
++#ifdef TOPS20
++# define OS_CODE 0x0a
++#endif
++
++#if defined(_BEOS_) || defined(RISCOS)
++# define fdopen(fd,mode) NULL /* No fdopen() */
++#endif
++
++#if (defined(_MSC_VER) && (_MSC_VER > 600))
++# define fdopen(fd,type) _fdopen(fd,type)
++#endif
++
++
++ /* Common defaults */
++
++#ifndef OS_CODE
++# define OS_CODE 0x03 /* assume Unix */
++#endif
++
++#ifndef F_OPEN
++# define F_OPEN(name, mode) fopen((name), (mode))
++#endif
++
++ /* functions */
++
++#ifdef HAVE_STRERROR
++ extern char *strerror OF((int));
++# define zstrerror(errnum) strerror(errnum)
++#else
++# define zstrerror(errnum) ""
++#endif
++
++#if defined(pyr)
++# define NO_MEMCPY
++#endif
++#if defined(SMALL_MEDIUM) && !defined(_MSC_VER) && !defined(__SC__)
++ /* Use our own functions for small and medium model with MSC <= 5.0.
++ * You may have to use the same strategy for Borland C (untested).
++ * The __SC__ check is for Symantec.
++ */
++# define NO_MEMCPY
++#endif
++#if defined(STDC) && !defined(HAVE_MEMCPY) && !defined(NO_MEMCPY)
++# define HAVE_MEMCPY
++#endif
++#ifdef HAVE_MEMCPY
++# ifdef SMALL_MEDIUM /* MSDOS small or medium model */
++# define zmemcpy _fmemcpy
++# define zmemcmp _fmemcmp
++# define zmemzero(dest, len) _fmemset(dest, 0, len)
++# else
++# define zmemcpy memcpy
++# define zmemcmp memcmp
++# define zmemzero(dest, len) memset(dest, 0, len)
++# endif
++#else
++ extern void zmemcpy OF((Bytef* dest, const Bytef* source, uInt len));
++ extern int zmemcmp OF((const Bytef* s1, const Bytef* s2, uInt len));
++ extern void zmemzero OF((Bytef* dest, uInt len));
++#endif
++
++/* Diagnostic functions */
++#ifdef DEBUG
++# include <stdio.h>
++ extern int z_verbose;
++ extern void z_error OF((char *m));
++# define Assert(cond,msg) {if(!(cond)) z_error(msg);}
++# define Trace(x) {if (z_verbose>=0) fprintf x ;}
++# define Tracev(x) {if (z_verbose>0) fprintf x ;}
++# define Tracevv(x) {if (z_verbose>1) fprintf x ;}
++# define Tracec(c,x) {if (z_verbose>0 && (c)) fprintf x ;}
++# define Tracecv(c,x) {if (z_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
++
++
++typedef uLong (ZEXPORT *check_func) OF((uLong check, const Bytef *buf,
++ uInt len));
++voidpf zcalloc OF((voidpf opaque, unsigned items, unsigned size));
++void zcfree OF((voidpf opaque, voidpf ptr));
++
++#define ZALLOC(strm, items, size) \
++ (*((strm)->zalloc))((strm)->opaque, (items), (size))
++#define ZFREE(strm, addr) (*((strm)->zfree))((strm)->opaque, (voidpf)(addr))
++#define TRY_FREE(s, p) {if (p) ZFREE(s, p);}
++
++#endif /* _Z_UTIL_H */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/libfreeswan/Makefile.objs Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,21 @@
++obj-y += satot.o
++obj-y += addrtot.o
++obj-y += ultot.o
++obj-y += addrtypeof.o
++obj-y += anyaddr.o
++obj-y += initaddr.o
++obj-y += ultoa.o
++obj-y += addrtoa.o
++obj-y += subnettoa.o
++obj-y += subnetof.o
++obj-y += goodmask.o
++obj-y += datatot.o
++obj-y += rangetoa.o
++obj-y += prng.o
++obj-y += pfkey_v2_parse.o
++obj-y += pfkey_v2_build.o
++obj-y += pfkey_v2_debug.o
++obj-y += pfkey_v2_ext_bits.o
++
++#version.c: ${LIBFREESWANDIR}/version.in.c ${OPENSWANSRCDIR}/Makefile.ver
++# sed '/"/s/xxx/$(IPSECVERSION)/' ${LIBFREESWANDIR}/version.in.c >$@
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/libfreeswan/addrtoa.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,67 @@
++/*
++ * addresses to ASCII
++ * Copyright (C) 1998, 1999 Henry Spencer.
++ *
++ * This library is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU Library General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/lgpl.txt>.
++ *
++ * This library 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 Library General Public
++ * License for more details.
++ *
++ * RCSID $Id$
++ */
++#include "openswan.h"
++
++#define NBYTES 4 /* bytes in an address */
++#define PERBYTE 4 /* three digits plus a dot or NUL */
++#define BUFLEN (NBYTES*PERBYTE)
++
++#if BUFLEN != ADDRTOA_BUF
++#error "ADDRTOA_BUF in openswan.h inconsistent with addrtoa() code"
++#endif
++
++/*
++ - addrtoa - convert binary address to ASCII dotted decimal
++ */
++size_t /* space needed for full conversion */
++addrtoa(addr, format, dst, dstlen)
++struct in_addr addr;
++int format; /* character */
++char *dst; /* need not be valid if dstlen is 0 */
++size_t dstlen;
++{
++ unsigned long a = ntohl(addr.s_addr);
++ int i;
++ size_t n;
++ unsigned long byte;
++ char buf[BUFLEN];
++ char *p;
++
++ switch (format) {
++ case 0:
++ break;
++ default:
++ return 0;
++ break;
++ }
++
++ p = buf;
++ for (i = NBYTES-1; i >= 0; i--) {
++ byte = (a >> (i*8)) & 0xff;
++ p += ultoa(byte, 10, p, PERBYTE);
++ if (i != 0)
++ *(p-1) = '.';
++ }
++ n = p - buf;
++
++ if (dstlen > 0) {
++ if (n > dstlen)
++ buf[dstlen - 1] = '\0';
++ strcpy(dst, buf);
++ }
++ return n;
++}
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/libfreeswan/addrtot.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,334 @@
++/*
++ * addresses to text
++ * Copyright (C) 2000 Henry Spencer.
++ *
++ * This library is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU Library General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/lgpl.txt>.
++ *
++ * This library 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 Library General Public
++ * License for more details.
++ *
++ * RCSID $Id$
++ */
++#include "openswan.h"
++
++#define IP4BYTES 4 /* bytes in an IPv4 address */
++#define PERBYTE 4 /* three digits plus a dot or NUL */
++#define IP6BYTES 16 /* bytes in an IPv6 address */
++
++/* forwards */
++static size_t normal4(const unsigned char *s, size_t len, char *b, char **dp);
++static size_t normal6(const unsigned char *s, size_t len, char *b, char **dp, int squish);
++static size_t reverse4(const unsigned char *s, size_t len, char *b, char **dp);
++static size_t reverse6(const unsigned char *s, size_t len, char *b, char **dp);
++
++/*
++ - addrtot - convert binary address to text (dotted decimal or IPv6 string)
++ */
++size_t /* space needed for full conversion */
++addrtot(src, format, dst, dstlen)
++const ip_address *src;
++int format; /* character */
++char *dst; /* need not be valid if dstlen is 0 */
++size_t dstlen;
++{
++ const unsigned char *b;
++ size_t n;
++ char buf[1+ADDRTOT_BUF+1]; /* :address: */
++ char *p;
++ int t = addrtypeof(src);
++# define TF(t, f) (((t)<<8) | (f))
++
++ n = addrbytesptr(src, &b);
++ if (n == 0) {
++ bad:
++ dst[0]='\0';
++ strncat(dst, "<invalid>", dstlen);
++ return sizeof("<invalid>");
++ }
++
++ switch (TF(t, format)) {
++ case TF(AF_INET, 0):
++ n = normal4(b, n, buf, &p);
++ break;
++ case TF(AF_INET6, 0):
++ n = normal6(b, n, buf, &p, 1);
++ break;
++ case TF(AF_INET, 'Q'):
++ n = normal4(b, n, buf, &p);
++ break;
++ case TF(AF_INET6, 'Q'):
++ n = normal6(b, n, buf, &p, 0);
++ break;
++ case TF(AF_INET, 'r'):
++ n = reverse4(b, n, buf, &p);
++ break;
++ case TF(AF_INET6, 'r'):
++ n = reverse6(b, n, buf, &p);
++ break;
++ default: /* including (AF_INET, 'R') */
++ goto bad;
++ break;
++ }
++
++ if (dstlen > 0) {
++ if (dstlen < n)
++ p[dstlen - 1] = '\0';
++ strcpy(dst, p);
++ }
++ return n;
++}
++
++/*
++ - normal4 - normal IPv4 address-text conversion
++ */
++static size_t /* size of text, including NUL */
++normal4(srcp, srclen, buf, dstp)
++const unsigned char *srcp;
++size_t srclen;
++char *buf; /* guaranteed large enough */
++char **dstp; /* where to put result pointer */
++{
++ int i;
++ char *p;
++
++ if (srclen != IP4BYTES) /* "can't happen" */
++ return 0;
++ p = buf;
++ for (i = 0; i < IP4BYTES; i++) {
++ p += ultot(srcp[i], 10, p, PERBYTE);
++ if (i != IP4BYTES - 1)
++ *(p-1) = '.'; /* overwrites the NUL */
++ }
++ *dstp = buf;
++ return p - buf;
++}
++
++/*
++ - normal6 - normal IPv6 address-text conversion
++ */
++static size_t /* size of text, including NUL */
++normal6(srcp, srclen, buf, dstp, squish)
++const unsigned char *srcp;
++size_t srclen;
++char *buf; /* guaranteed large enough, plus 2 */
++char **dstp; /* where to put result pointer */
++int squish; /* whether to squish out 0:0 */
++{
++ int i;
++ unsigned long piece;
++ char *p;
++ char *q;
++
++ if (srclen != IP6BYTES) /* "can't happen" */
++ return 0;
++ p = buf;
++ *p++ = ':';
++ for (i = 0; i < IP6BYTES/2; i++) {
++ piece = (srcp[2*i] << 8) + srcp[2*i + 1];
++ p += ultot(piece, 16, p, 5); /* 5 = abcd + NUL */
++ *(p-1) = ':'; /* overwrites the NUL */
++ }
++ *p = '\0';
++ q = strstr(buf, ":0:0:");
++ if (squish && q != NULL) { /* zero squishing is possible */
++ p = q + 1;
++ while (*p == '0' && *(p+1) == ':')
++ p += 2;
++ q++;
++ *q++ = ':'; /* overwrite first 0 */
++ while (*p != '\0')
++ *q++ = *p++;
++ *q = '\0';
++ if (!(*(q-1) == ':' && *(q-2) == ':'))
++ *--q = '\0'; /* strip final : unless :: */
++ p = buf;
++ if (!(*p == ':' && *(p+1) == ':'))
++ p++; /* skip initial : unless :: */
++ } else {
++ q = p;
++ *--q = '\0'; /* strip final : */
++ p = buf + 1; /* skip initial : */
++ }
++ *dstp = p;
++ return q - p + 1;
++}
++
++/*
++ - reverse4 - IPv4 reverse-lookup conversion
++ */
++static size_t /* size of text, including NUL */
++reverse4(srcp, srclen, buf, dstp)
++const unsigned char *srcp;
++size_t srclen;
++char *buf; /* guaranteed large enough */
++char **dstp; /* where to put result pointer */
++{
++ int i;
++ char *p;
++
++ if (srclen != IP4BYTES) /* "can't happen" */
++ return 0;
++ p = buf;
++ for (i = IP4BYTES-1; i >= 0; i--) {
++ p += ultot(srcp[i], 10, p, PERBYTE);
++ *(p-1) = '.'; /* overwrites the NUL */
++ }
++ strcpy(p, "IN-ADDR.ARPA.");
++ *dstp = buf;
++ return strlen(buf) + 1;
++}
++
++/*
++ - reverse6 - IPv6 reverse-lookup conversion (RFC 1886)
++ * A trifle inefficient, really shouldn't use ultot...
++ */
++static size_t /* size of text, including NUL */
++reverse6(srcp, srclen, buf, dstp)
++const unsigned char *srcp;
++size_t srclen;
++char *buf; /* guaranteed large enough */
++char **dstp; /* where to put result pointer */
++{
++ int i;
++ unsigned long piece;
++ char *p;
++
++ if (srclen != IP6BYTES) /* "can't happen" */
++ return 0;
++ p = buf;
++ for (i = IP6BYTES-1; i >= 0; i--) {
++ piece = srcp[i];
++ p += ultot(piece&0xf, 16, p, 2);
++ *(p-1) = '.';
++ p += ultot(piece>>4, 16, p, 2);
++ *(p-1) = '.';
++ }
++ strcpy(p, "IP6.ARPA.");
++ *dstp = buf;
++ return strlen(buf) + 1;
++}
++
++/*
++ - reverse6 - modern IPv6 reverse-lookup conversion (RFC 2874)
++ * this version removed as it was obsoleted in the end.
++ */
++
++#ifdef ADDRTOT_MAIN
++
++#include <stdio.h>
++#include <sys/socket.h>
++#include <netinet/in.h>
++#include <arpa/inet.h>
++
++void regress(void);
++
++int
++main(int argc, char *argv[])
++{
++ if (argc < 2) {
++ fprintf(stderr, "Usage: %s {addr|net/mask|begin...end|-r}\n",
++ argv[0]);
++ exit(2);
++ }
++
++ if (strcmp(argv[1], "-r") == 0) {
++ regress();
++ fprintf(stderr, "regress() returned?!?\n");
++ exit(1);
++ }
++ exit(0);
++}
++
++struct rtab {
++ char *input;
++ char format;
++ char *output; /* NULL means error expected */
++} rtab[] = {
++ {"1.2.3.0", 0, "1.2.3.0"},
++ {"1:2::3:4", 0, "1:2::3:4"},
++ {"1:2::3:4", 'Q', "1:2:0:0:0:0:3:4"},
++ {"1:2:0:0:3:4:0:0", 0, "1:2::3:4:0:0"},
++ {"1.2.3.4", 'r' , "4.3.2.1.IN-ADDR.ARPA."},
++ /* 0 1 2 3 4 5 6 7 8 9 a b c d e f 0 1 2 3 4 5 6 7 8 9 a b c d e f */
++ {"1:2::3:4", 'r', "4.0.0.0.3.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.2.0.0.0.1.0.0.0.IP6.ARPA."},
++ {NULL, 0, NULL}
++};
++
++void
++regress()
++{
++ struct rtab *r;
++ int status = 0;
++ ip_address a;
++ char in[100];
++ char buf[100];
++ const char *oops;
++ size_t n;
++
++ for (r = rtab; r->input != NULL; r++) {
++ strcpy(in, r->input);
++
++ /* convert it *to* internal format */
++ oops = ttoaddr(in, strlen(in), 0, &a);
++
++ /* now convert it back */
++
++ n = addrtot(&a, r->format, buf, sizeof(buf));
++
++ if (n == 0 && r->output == NULL)
++ {} /* okay, error expected */
++
++ else if (n == 0) {
++ printf("`%s' atoasr failed\n", r->input);
++ status = 1;
++
++ } else if (r->output == NULL) {
++ printf("`%s' atoasr succeeded unexpectedly '%c'\n",
++ r->input, r->format);
++ status = 1;
++ } else {
++ if (strcasecmp(r->output, buf) != 0) {
++ printf("`%s' '%c' gave `%s', expected `%s'\n",
++ r->input, r->format, buf, r->output);
++ status = 1;
++ }
++ }
++ }
++ exit(status);
++}
++
++#endif /* ADDRTOT_MAIN */
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.15 2004/04/11 17:39:25 mcr
++ * removed internal.h requirements.
++ *
++ * Revision 1.14 2004/03/08 01:59:08 ken
++ * freeswan.h -> openswan.h
++ *
++ * Revision 1.13 2004/01/05 23:21:05 mcr
++ * if the address type is invalid, then return length of <invalid>
++ * string!
++ *
++ * Revision 1.12 2003/12/30 06:42:48 mcr
++ * added $Log$
++ * added Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * added Turn off EOLN_NATIVE flag
++ * added
++ * added (Logical change 1.5010)
++ * added
++ * added Revision 1.15 2004/04/11 17:39:25 mcr
++ * added removed internal.h requirements.
++ * added
++ * added Revision 1.14 2004/03/08 01:59:08 ken
++ * added freeswan.h -> openswan.h
++ * added
++ * added Revision 1.13 2004/01/05 23:21:05 mcr
++ * added if the address type is invalid, then return length of <invalid>
++ * added string!
++ * added
++ *
++ *
++ */
++
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/libfreeswan/addrtypeof.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,93 @@
++/*
++ * extract parts of an ip_address
++ * Copyright (C) 2000 Henry Spencer.
++ *
++ * This library is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU Library General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/lgpl.txt>.
++ *
++ * This library 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 Library General Public
++ * License for more details.
++ *
++ * RCSID $Id$
++ */
++#include "openswan.h"
++
++/*
++ - addrtypeof - get the type of an ip_address
++ */
++int
++addrtypeof(src)
++const ip_address *src;
++{
++ return src->u.v4.sin_family;
++}
++
++/*
++ - addrbytesptr - get pointer to the address bytes of an ip_address
++ */
++size_t /* 0 for error */
++addrbytesptr(src, dstp)
++const ip_address *src;
++const unsigned char **dstp; /* NULL means just a size query */
++{
++ const unsigned char *p;
++ size_t n;
++
++ switch (src->u.v4.sin_family) {
++ case AF_INET:
++ p = (const unsigned char *)&src->u.v4.sin_addr.s_addr;
++ n = 4;
++ break;
++ case AF_INET6:
++ p = (const unsigned char *)&src->u.v6.sin6_addr;
++ n = 16;
++ break;
++ default:
++ return 0;
++ break;
++ }
++
++ if (dstp != NULL)
++ *dstp = p;
++ return n;
++}
++
++/*
++ - addrlenof - get length of the address bytes of an ip_address
++ */
++size_t /* 0 for error */
++addrlenof(src)
++const ip_address *src;
++{
++ return addrbytesptr(src, NULL);
++}
++
++/*
++ - addrbytesof - get the address bytes of an ip_address
++ */
++size_t /* 0 for error */
++addrbytesof(src, dst, dstlen)
++const ip_address *src;
++unsigned char *dst;
++size_t dstlen;
++{
++ const unsigned char *p;
++ size_t n;
++ size_t ncopy;
++
++ n = addrbytesptr(src, &p);
++ if (n == 0)
++ return 0;
++
++ if (dstlen > 0) {
++ ncopy = n;
++ if (ncopy > dstlen)
++ ncopy = dstlen;
++ memcpy(dst, p, ncopy);
++ }
++ return n;
++}
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/libfreeswan/anyaddr.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,145 @@
++/*
++ * special addresses
++ * Copyright (C) 2000 Henry Spencer.
++ *
++ * This library is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU Library General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/lgpl.txt>.
++ *
++ * This library 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 Library General Public
++ * License for more details.
++ *
++ * RCSID $Id$
++ */
++#include "openswan.h"
++
++/* these are mostly fallbacks for the no-IPv6-support-in-library case */
++#ifndef IN6ADDR_ANY_INIT
++#define IN6ADDR_ANY_INIT {{{ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }}}
++#endif
++#ifndef IN6ADDR_LOOPBACK_INIT
++#define IN6ADDR_LOOPBACK_INIT {{{ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 }}}
++#endif
++
++static struct in6_addr v6any = IN6ADDR_ANY_INIT;
++static struct in6_addr v6loop = IN6ADDR_LOOPBACK_INIT;
++
++/*
++ - anyaddr - initialize to the any-address value
++ */
++err_t /* NULL for success, else string literal */
++anyaddr(af, dst)
++int af; /* address family */
++ip_address *dst;
++{
++ uint32_t v4any = htonl(INADDR_ANY);
++
++ switch (af) {
++ case AF_INET:
++ return initaddr((unsigned char *)&v4any, sizeof(v4any), af, dst);
++ break;
++ case AF_INET6:
++ return initaddr((unsigned char *)&v6any, sizeof(v6any), af, dst);
++ break;
++ default:
++ return "unknown address family in anyaddr/unspecaddr";
++ break;
++ }
++}
++
++/*
++ - unspecaddr - initialize to the unspecified-address value
++ */
++err_t /* NULL for success, else string literal */
++unspecaddr(af, dst)
++int af; /* address family */
++ip_address *dst;
++{
++ return anyaddr(af, dst);
++}
++
++/*
++ - loopbackaddr - initialize to the loopback-address value
++ */
++err_t /* NULL for success, else string literal */
++loopbackaddr(af, dst)
++int af; /* address family */
++ip_address *dst;
++{
++ uint32_t v4loop = htonl(INADDR_LOOPBACK);
++
++ switch (af) {
++ case AF_INET:
++ return initaddr((unsigned char *)&v4loop, sizeof(v4loop), af, dst);
++ break;
++ case AF_INET6:
++ return initaddr((unsigned char *)&v6loop, sizeof(v6loop), af, dst);
++ break;
++ default:
++ return "unknown address family in loopbackaddr";
++ break;
++ }
++}
++
++/*
++ - isanyaddr - test for the any-address value
++ */
++int
++isanyaddr(src)
++const ip_address *src;
++{
++ uint32_t v4any = htonl(INADDR_ANY);
++ int cmp;
++
++ switch (src->u.v4.sin_family) {
++ case AF_INET:
++ cmp = memcmp(&src->u.v4.sin_addr.s_addr, &v4any, sizeof(v4any));
++ break;
++ case AF_INET6:
++ cmp = memcmp(&src->u.v6.sin6_addr, &v6any, sizeof(v6any));
++ break;
++ default:
++ return 0;
++ break;
++ }
++
++ return (cmp == 0) ? 1 : 0;
++}
++
++/*
++ - isunspecaddr - test for the unspecified-address value
++ */
++int
++isunspecaddr(src)
++const ip_address *src;
++{
++ return isanyaddr(src);
++}
++
++/*
++ - isloopbackaddr - test for the loopback-address value
++ */
++int
++isloopbackaddr(src)
++const ip_address *src;
++{
++ uint32_t v4loop = htonl(INADDR_LOOPBACK);
++ int cmp;
++
++ switch (src->u.v4.sin_family) {
++ case AF_INET:
++ cmp = memcmp(&src->u.v4.sin_addr.s_addr, &v4loop, sizeof(v4loop));
++ break;
++ case AF_INET6:
++ cmp = memcmp(&src->u.v6.sin6_addr, &v6loop, sizeof(v6loop));
++ break;
++ default:
++ return 0;
++ break;
++ }
++
++ return (cmp == 0) ? 1 : 0;
++}
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/libfreeswan/datatot.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,232 @@
++/*
++ * convert from binary data (e.g. key) to text form
++ * Copyright (C) 2000 Henry Spencer.
++ *
++ * This library is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU Library General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/lgpl.txt>.
++ *
++ * This library 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 Library General Public
++ * License for more details.
++ *
++ * RCSID $Id$
++ */
++#include "openswan.h"
++
++static void convert(const char *src, size_t nreal, int format, char *out);
++
++/*
++ - datatot - convert data bytes to text
++ */
++size_t /* true length (with NUL) for success */
++datatot(src, srclen, format, dst, dstlen)
++const char *src;
++size_t srclen;
++int format; /* character indicating what format */
++char *dst; /* need not be valid if dstlen is 0 */
++size_t dstlen;
++{
++ size_t inblocksize; /* process this many bytes at a time */
++ size_t outblocksize; /* producing this many */
++ size_t breakevery; /* add a _ every this many (0 means don't) */
++ size_t sincebreak; /* output bytes since last _ */
++ char breakchar; /* character used to break between groups */
++ char inblock[10]; /* enough for any format */
++ char outblock[10]; /* enough for any format */
++ char fake[1]; /* fake output area for dstlen == 0 */
++ size_t needed; /* return value */
++ char *stop; /* where the terminating NUL will go */
++ size_t ntodo; /* remaining input */
++ size_t nreal;
++ char *out;
++ char *prefix;
++
++ breakevery = 0;
++ breakchar = '_';
++
++ switch (format) {
++ case 0:
++ case 'h':
++ format = 'x';
++ breakevery = 8;
++ /* FALLTHROUGH */
++ case 'x':
++ inblocksize = 1;
++ outblocksize = 2;
++ prefix = "0x";
++ break;
++ case ':':
++ format = 'x';
++ breakevery = 2;
++ breakchar = ':';
++ /* FALLTHROUGH */
++ case 16:
++ inblocksize = 1;
++ outblocksize = 2;
++ prefix = "";
++ format = 'x';
++ break;
++ case 's':
++ inblocksize = 3;
++ outblocksize = 4;
++ prefix = "0s";
++ break;
++ case 64: /* beware, equals ' ' */
++ inblocksize = 3;
++ outblocksize = 4;
++ prefix = "";
++ format = 's';
++ break;
++ default:
++ return 0;
++ break;
++ }
++ assert(inblocksize < sizeof(inblock));
++ assert(outblocksize < sizeof(outblock));
++ assert(breakevery % outblocksize == 0);
++
++ if (srclen == 0)
++ return 0;
++ ntodo = srclen;
++
++ if (dstlen == 0) { /* dispose of awkward special case */
++ dst = fake;
++ dstlen = 1;
++ }
++ stop = dst + dstlen - 1;
++
++ nreal = strlen(prefix);
++ needed = nreal; /* for starters */
++ if (dstlen <= nreal) { /* prefix won't fit */
++ strncpy(dst, prefix, dstlen - 1);
++ dst += dstlen - 1;
++ } else {
++ strcpy(dst, prefix);
++ dst += nreal;
++ }
++ assert(dst <= stop);
++ sincebreak = 0;
++
++ while (ntodo > 0) {
++ if (ntodo < inblocksize) { /* incomplete input */
++ memset(inblock, 0, sizeof(inblock));
++ memcpy(inblock, src, ntodo);
++ src = inblock;
++ nreal = ntodo;
++ ntodo = inblocksize;
++ } else
++ nreal = inblocksize;
++ out = (outblocksize > stop - dst) ? outblock : dst;
++
++ convert(src, nreal, format, out);
++ needed += outblocksize;
++ sincebreak += outblocksize;
++ if (dst < stop) {
++ if (out != dst) {
++ assert(outblocksize > stop - dst);
++ memcpy(dst, out, stop - dst);
++ dst = stop;
++ } else
++ dst += outblocksize;
++ }
++
++ src += inblocksize;
++ ntodo -= inblocksize;
++ if (breakevery != 0 && sincebreak >= breakevery && ntodo > 0) {
++ if (dst < stop)
++ *dst++ = breakchar;
++ needed++;
++ sincebreak = 0;
++ }
++ }
++
++ assert(dst <= stop);
++ *dst++ = '\0';
++ needed++;
++
++ return needed;
++}
++
++/*
++ - convert - convert one input block to one output block
++ */
++static void
++convert(src, nreal, format, out)
++const char *src;
++size_t nreal; /* how much of the input block is real */
++int format;
++char *out;
++{
++ static char hex[] = "0123456789abcdef";
++ static char base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
++ "abcdefghijklmnopqrstuvwxyz"
++ "0123456789+/";
++ unsigned char c;
++ unsigned char c1, c2, c3;
++
++ assert(nreal > 0);
++ switch (format) {
++ case 'x':
++ assert(nreal == 1);
++ c = (unsigned char)*src;
++ *out++ = hex[c >> 4];
++ *out++ = hex[c & 0xf];
++ break;
++ case 's':
++ c1 = (unsigned char)*src++;
++ c2 = (unsigned char)*src++;
++ c3 = (unsigned char)*src++;
++ *out++ = base64[c1 >> 2]; /* top 6 bits of c1 */
++ c = (c1 & 0x3) << 4; /* bottom 2 of c1... */
++ c |= c2 >> 4; /* ...top 4 of c2 */
++ *out++ = base64[c];
++ if (nreal == 1)
++ *out++ = '=';
++ else {
++ c = (c2 & 0xf) << 2; /* bottom 4 of c2... */
++ c |= c3 >> 6; /* ...top 2 of c3 */
++ *out++ = base64[c];
++ }
++ if (nreal <= 2)
++ *out++ = '=';
++ else
++ *out++ = base64[c3 & 0x3f]; /* bottom 6 of c3 */
++ break;
++ default:
++ assert(nreal == 0); /* unknown format */
++ break;
++ }
++}
++
++/*
++ - datatoa - convert data to ASCII
++ * backward-compatibility synonym for datatot
++ */
++size_t /* true length (with NUL) for success */
++datatoa(src, srclen, format, dst, dstlen)
++const char *src;
++size_t srclen;
++int format; /* character indicating what format */
++char *dst; /* need not be valid if dstlen is 0 */
++size_t dstlen;
++{
++ return datatot(src, srclen, format, dst, dstlen);
++}
++
++/*
++ - bytestoa - convert data bytes to ASCII
++ * backward-compatibility synonym for datatot
++ */
++size_t /* true length (with NUL) for success */
++bytestoa(src, srclen, format, dst, dstlen)
++const char *src;
++size_t srclen;
++int format; /* character indicating what format */
++char *dst; /* need not be valid if dstlen is 0 */
++size_t dstlen;
++{
++ return datatot(src, srclen, format, dst, dstlen);
++}
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/libfreeswan/goodmask.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,100 @@
++/*
++ * minor utilities for subnet-mask manipulation
++ * Copyright (C) 1998, 1999 Henry Spencer.
++ *
++ * This library is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU Library General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/lgpl.txt>.
++ *
++ * This library 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 Library General Public
++ * License for more details.
++ *
++ * RCSID $Id$
++ */
++#include "openswan.h"
++
++#ifndef ABITS
++#define ABITS 32 /* bits in an IPv4 address */
++#endif
++
++/*
++ - goodmask - is this a good (^1*0*$) subnet mask?
++ * You are not expected to understand this. See Henry S. Warren Jr,
++ * "Functions realizable with word-parallel logical and two's-complement
++ * addition instructions", CACM 20.6 (June 1977), p.439.
++ */
++int /* predicate */
++goodmask(mask)
++struct in_addr mask;
++{
++ unsigned long x = ntohl(mask.s_addr);
++ /* clear rightmost contiguous string of 1-bits */
++# define CRCS1B(x) (((x|(x-1))+1)&x)
++# define TOPBIT (1UL << 31)
++
++ /* either zero, or has one string of 1-bits which is left-justified */
++ if (x == 0 || (CRCS1B(x) == 0 && (x&TOPBIT)))
++ return 1;
++ return 0;
++}
++
++/*
++ - masktobits - how many bits in this mask?
++ * The algorithm is essentially a binary search, but highly optimized
++ * for this particular task.
++ */
++int /* -1 means !goodmask() */
++masktobits(mask)
++struct in_addr mask;
++{
++ unsigned long m = ntohl(mask.s_addr);
++ int masklen;
++
++ if (!goodmask(mask))
++ return -1;
++
++ if (m&0x00000001UL)
++ return 32;
++ masklen = 0;
++ if (m&(0x0000ffffUL<<1)) { /* <<1 for 1-origin numbering */
++ masklen |= 0x10;
++ m <<= 16;
++ }
++ if (m&(0x00ff0000UL<<1)) {
++ masklen |= 0x08;
++ m <<= 8;
++ }
++ if (m&(0x0f000000UL<<1)) {
++ masklen |= 0x04;
++ m <<= 4;
++ }
++ if (m&(0x30000000UL<<1)) {
++ masklen |= 0x02;
++ m <<= 2;
++ }
++ if (m&(0x40000000UL<<1))
++ masklen |= 0x01;
++
++ return masklen;
++}
++
++/*
++ - bitstomask - return a mask with this many high bits on
++ */
++struct in_addr
++bitstomask(n)
++int n;
++{
++ struct in_addr result;
++
++ if (n > 0 && n <= ABITS)
++ result.s_addr = htonl(~((1UL << (ABITS - n)) - 1));
++ else if (n == 0)
++ result.s_addr = 0;
++ else
++ result.s_addr = 0; /* best error report we can do */
++ return result;
++}
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/libfreeswan/initaddr.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,50 @@
++/*
++ * initialize address structure
++ * Copyright (C) 2000 Henry Spencer.
++ *
++ * This library is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU Library General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/lgpl.txt>.
++ *
++ * This library 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 Library General Public
++ * License for more details.
++ *
++ * RCSID $Id$
++ */
++#include "openswan.h"
++
++/*
++ - initaddr - initialize ip_address from bytes
++ */
++err_t /* NULL for success, else string literal */
++initaddr(src, srclen, af, dst)
++const unsigned char *src;
++size_t srclen;
++int af; /* address family */
++ip_address *dst;
++{
++ switch (af) {
++ case AF_INET:
++ if (srclen != 4)
++ return "IPv4 address must be exactly 4 bytes";
++ dst->u.v4.sin_family = af;
++ dst->u.v4.sin_port = 0; /* unused */
++ memcpy((char *)&dst->u.v4.sin_addr.s_addr, src, srclen);
++ break;
++ case AF_INET6:
++ if (srclen != 16)
++ return "IPv6 address must be exactly 16 bytes";
++ dst->u.v6.sin6_family = af;
++ dst->u.v6.sin6_flowinfo = 0; /* unused */
++ dst->u.v6.sin6_port = 0; /* unused */
++ memcpy((char *)&dst->u.v6.sin6_addr, src, srclen);
++ break;
++ default:
++ return "unknown address family in initaddr";
++ break;
++ }
++ return NULL;
++}
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/libfreeswan/pfkey_v2_build.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,1559 @@
++/*
++ * RFC2367 PF_KEYv2 Key management API message parser
++ * Copyright (C) 1999, 2000, 2001 Richard Guy Briggs.
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ * RCSID $Id$
++ */
++
++/*
++ * Template from klips/net/ipsec/ipsec/ipsec_parser.c.
++ */
++
++char pfkey_v2_build_c_version[] = "$Id$";
++
++/*
++ * Some ugly stuff to allow consistent debugging code for use in the
++ * kernel and in user space
++*/
++
++#ifdef __KERNEL__
++
++# include <linux/kernel.h> /* for printk */
++
++# include "openswan/ipsec_kversion.h" /* for malloc switch */
++# ifdef MALLOC_SLAB
++# include <linux/slab.h> /* kmalloc() */
++# else /* MALLOC_SLAB */
++# include <linux/malloc.h> /* kmalloc() */
++# endif /* MALLOC_SLAB */
++# include <linux/errno.h> /* error codes */
++# include <linux/types.h> /* size_t */
++# include <linux/interrupt.h> /* mark_bh */
++
++# include <linux/netdevice.h> /* struct device, and other headers */
++# include <linux/etherdevice.h> /* eth_type_trans */
++# include <linux/ip.h> /* struct iphdr */
++# if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
++# include <linux/ipv6.h> /* struct ipv6hdr */
++# endif /* if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) */
++
++# define MALLOC(size) kmalloc(size, GFP_ATOMIC)
++# define FREE(obj) kfree(obj)
++# include <openswan.h>
++#else /* __KERNEL__ */
++
++# include <sys/types.h>
++# include <linux/types.h>
++# include <linux/errno.h>
++# include <malloc.h>
++# include <string.h> /* memset */
++
++# include <openswan.h>
++
++#endif /* __KERNEL__ */
++
++#include <pfkeyv2.h>
++#include <pfkey.h>
++
++#ifdef __KERNEL__
++#include "openswan/radij.h" /* rd_nodes */
++#include "openswan/ipsec_encap.h" /* sockaddr_encap */
++#endif /* __KERNEL__ */
++
++
++#include "openswan/ipsec_sa.h" /* IPSEC_SAREF_NULL, IPSEC_SA_REF_TABLE_IDX_WIDTH */
++#include "openswan/pfkey_debug.h"
++
++
++#define SENDERR(_x) do { error = -(_x); goto errlab; } while (0)
++
++void
++pfkey_extensions_init(struct sadb_ext *extensions[SADB_EXT_MAX + 1])
++{
++ int i;
++
++ for (i = 0; i != SADB_EXT_MAX + 1; i++) {
++ extensions[i] = NULL;
++ }
++}
++
++void
++pfkey_extensions_free(struct sadb_ext *extensions[SADB_EXT_MAX + 1])
++{
++ int i;
++
++ if(!extensions) {
++ return;
++ }
++
++ if(extensions[0]) {
++ memset(extensions[0], 0, sizeof(struct sadb_msg));
++ FREE(extensions[0]);
++ extensions[0] = NULL;
++ }
++
++ for (i = 1; i != SADB_EXT_MAX + 1; i++) {
++ if(extensions[i]) {
++ memset(extensions[i], 0, extensions[i]->sadb_ext_len * IPSEC_PFKEYv2_ALIGN);
++ FREE(extensions[i]);
++ extensions[i] = NULL;
++ }
++ }
++}
++
++void
++pfkey_msg_free(struct sadb_msg **pfkey_msg)
++{
++ if(*pfkey_msg) {
++ memset(*pfkey_msg, 0, (*pfkey_msg)->sadb_msg_len * IPSEC_PFKEYv2_ALIGN);
++ FREE(*pfkey_msg);
++ *pfkey_msg = NULL;
++ }
++}
++
++/* Default extension builders taken from the KLIPS code */
++
++int
++pfkey_msg_hdr_build(struct sadb_ext** pfkey_ext,
++ uint8_t msg_type,
++ uint8_t satype,
++ uint8_t msg_errno,
++ uint32_t seq,
++ uint32_t pid)
++{
++ int error = 0;
++ struct sadb_msg *pfkey_msg = (struct sadb_msg *)*pfkey_ext;
++
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_msg_hdr_build:\n");
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_msg_hdr_build: "
++ "on_entry &pfkey_ext=0p%p pfkey_ext=0p%p *pfkey_ext=0p%p.\n",
++ &pfkey_ext,
++ pfkey_ext,
++ *pfkey_ext);
++ /* sanity checks... */
++ if(pfkey_msg) {
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_msg_hdr_build: "
++ "why is pfkey_msg already pointing to something?\n");
++ SENDERR(EINVAL);
++ }
++
++ if(!msg_type) {
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_msg_hdr_build: "
++ "msg type not set, must be non-zero..\n");
++ SENDERR(EINVAL);
++ }
++
++ if(msg_type > SADB_MAX) {
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_msg_hdr_build: "
++ "msg type too large:%d.\n",
++ msg_type);
++ SENDERR(EINVAL);
++ }
++
++ if(satype > SADB_SATYPE_MAX) {
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_msg_hdr_build: "
++ "satype %d > max %d\n",
++ satype, SADB_SATYPE_MAX);
++ SENDERR(EINVAL);
++ }
++
++ if(!(*pfkey_ext = (struct sadb_ext*)
++ pfkey_msg = (struct sadb_msg*)
++ MALLOC(sizeof(struct sadb_msg)))) {
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_msg_hdr_build: "
++ "memory allocation failed\n");
++ SENDERR(ENOMEM);
++ }
++ memset(pfkey_msg, 0, sizeof(struct sadb_msg));
++
++ pfkey_msg->sadb_msg_len = sizeof(struct sadb_msg) / IPSEC_PFKEYv2_ALIGN;
++
++ pfkey_msg->sadb_msg_type = msg_type;
++ pfkey_msg->sadb_msg_satype = satype;
++
++ pfkey_msg->sadb_msg_version = PF_KEY_V2;
++ pfkey_msg->sadb_msg_errno = msg_errno;
++ pfkey_msg->sadb_msg_reserved = 0;
++ pfkey_msg->sadb_msg_seq = seq;
++ pfkey_msg->sadb_msg_pid = pid;
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_msg_hdr_build: "
++ "on_exit &pfkey_ext=0p%p pfkey_ext=0p%p *pfkey_ext=0p%p.\n",
++ &pfkey_ext,
++ pfkey_ext,
++ *pfkey_ext);
++errlab:
++ return error;
++}
++
++int
++pfkey_sa_ref_build(struct sadb_ext ** pfkey_ext,
++ uint16_t exttype,
++ uint32_t spi,
++ uint8_t replay_window,
++ uint8_t sa_state,
++ uint8_t auth,
++ uint8_t encrypt,
++ uint32_t flags,
++ uint32_t/*IPsecSAref_t*/ ref)
++{
++ int error = 0;
++ struct sadb_sa *pfkey_sa = (struct sadb_sa *)*pfkey_ext;
++
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_sa_build: "
++ "spi=%08x replay=%d sa_state=%d auth=%d encrypt=%d flags=%d\n",
++ ntohl(spi), /* in network order */
++ replay_window,
++ sa_state,
++ auth,
++ encrypt,
++ flags);
++ /* sanity checks... */
++ if(pfkey_sa) {
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_sa_build: "
++ "why is pfkey_sa already pointing to something?\n");
++ SENDERR(EINVAL);
++ }
++
++ if(exttype != SADB_EXT_SA &&
++ exttype != SADB_X_EXT_SA2) {
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_sa_build: "
++ "invalid exttype=%d.\n",
++ exttype);
++ SENDERR(EINVAL);
++ }
++
++ if(replay_window > 64) {
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_sa_build: "
++ "replay window size: %d -- must be 0 <= size <= 64\n",
++ replay_window);
++ SENDERR(EINVAL);
++ }
++
++ if(auth > SADB_AALG_MAX) {
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_sa_build: "
++ "auth=%d > SADB_AALG_MAX=%d.\n",
++ auth,
++ SADB_AALG_MAX);
++ SENDERR(EINVAL);
++ }
++
++ if(encrypt > SADB_EALG_MAX) {
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_sa_build: "
++ "encrypt=%d > SADB_EALG_MAX=%d.\n",
++ encrypt,
++ SADB_EALG_MAX);
++ SENDERR(EINVAL);
++ }
++
++ if(sa_state > SADB_SASTATE_MAX) {
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_sa_build: "
++ "sa_state=%d exceeds MAX=%d.\n",
++ sa_state,
++ SADB_SASTATE_MAX);
++ SENDERR(EINVAL);
++ }
++
++ if(sa_state == SADB_SASTATE_DEAD) {
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_sa_build: "
++ "sa_state=%d is DEAD=%d is not allowed.\n",
++ sa_state,
++ SADB_SASTATE_DEAD);
++ SENDERR(EINVAL);
++ }
++
++ if((IPSEC_SAREF_NULL != ref) && (ref >= (1 << IPSEC_SA_REF_TABLE_IDX_WIDTH))) {
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_sa_build: "
++ "SAref=%d must be (SAref == IPSEC_SAREF_NULL(%d) || SAref < IPSEC_SA_REF_TABLE_NUM_ENTRIES(%d)).\n",
++ ref,
++ IPSEC_SAREF_NULL,
++ IPSEC_SA_REF_TABLE_NUM_ENTRIES);
++ SENDERR(EINVAL);
++ }
++
++ if(!(*pfkey_ext = (struct sadb_ext*)
++ pfkey_sa = (struct sadb_sa*)
++ MALLOC(sizeof(struct sadb_sa)))) {
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_sa_build: "
++ "memory allocation failed\n");
++ SENDERR(ENOMEM);
++ }
++ memset(pfkey_sa, 0, sizeof(struct sadb_sa));
++
++ pfkey_sa->sadb_sa_len = sizeof(*pfkey_sa) / IPSEC_PFKEYv2_ALIGN;
++ pfkey_sa->sadb_sa_exttype = exttype;
++ pfkey_sa->sadb_sa_spi = spi;
++ pfkey_sa->sadb_sa_replay = replay_window;
++ pfkey_sa->sadb_sa_state = sa_state;
++ pfkey_sa->sadb_sa_auth = auth;
++ pfkey_sa->sadb_sa_encrypt = encrypt;
++ pfkey_sa->sadb_sa_flags = flags;
++ pfkey_sa->sadb_x_sa_ref = ref;
++
++errlab:
++ return error;
++}
++
++int
++pfkey_sa_build(struct sadb_ext ** pfkey_ext,
++ uint16_t exttype,
++ uint32_t spi,
++ uint8_t replay_window,
++ uint8_t sa_state,
++ uint8_t auth,
++ uint8_t encrypt,
++ uint32_t flags)
++{
++ return pfkey_sa_ref_build(pfkey_ext,
++ exttype,
++ spi,
++ replay_window,
++ sa_state,
++ auth,
++ encrypt,
++ flags,
++ IPSEC_SAREF_NULL);
++}
++
++int
++pfkey_lifetime_build(struct sadb_ext ** pfkey_ext,
++ uint16_t exttype,
++ uint32_t allocations,
++ uint64_t bytes,
++ uint64_t addtime,
++ uint64_t usetime,
++ uint32_t packets)
++{
++ int error = 0;
++ struct sadb_lifetime *pfkey_lifetime = (struct sadb_lifetime *)*pfkey_ext;
++
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_lifetime_build:\n");
++ /* sanity checks... */
++ if(pfkey_lifetime) {
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_lifetime_build: "
++ "why is pfkey_lifetime already pointing to something?\n");
++ SENDERR(EINVAL);
++ }
++
++ if(exttype != SADB_EXT_LIFETIME_CURRENT &&
++ exttype != SADB_EXT_LIFETIME_HARD &&
++ exttype != SADB_EXT_LIFETIME_SOFT) {
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_lifetime_build: "
++ "invalid exttype=%d.\n",
++ exttype);
++ SENDERR(EINVAL);
++ }
++
++ if(!(*pfkey_ext = (struct sadb_ext*)
++ pfkey_lifetime = (struct sadb_lifetime*)
++ MALLOC(sizeof(struct sadb_lifetime)))) {
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_lifetime_build: "
++ "memory allocation failed\n");
++ SENDERR(ENOMEM);
++ }
++ memset(pfkey_lifetime, 0, sizeof(struct sadb_lifetime));
++
++ pfkey_lifetime->sadb_lifetime_len = sizeof(struct sadb_lifetime) / IPSEC_PFKEYv2_ALIGN;
++ pfkey_lifetime->sadb_lifetime_exttype = exttype;
++ pfkey_lifetime->sadb_lifetime_allocations = allocations;
++ pfkey_lifetime->sadb_lifetime_bytes = bytes;
++ pfkey_lifetime->sadb_lifetime_addtime = addtime;
++ pfkey_lifetime->sadb_lifetime_usetime = usetime;
++ pfkey_lifetime->sadb_x_lifetime_packets = packets;
++
++errlab:
++ return error;
++}
++
++int
++pfkey_address_build(struct sadb_ext** pfkey_ext,
++ uint16_t exttype,
++ uint8_t proto,
++ uint8_t prefixlen,
++ struct sockaddr* address)
++{
++ int error = 0;
++ int saddr_len = 0;
++ char ipaddr_txt[ADDRTOT_BUF + 6/*extra for port number*/];
++ struct sadb_address *pfkey_address = (struct sadb_address *)*pfkey_ext;
++
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_address_build: "
++ "exttype=%d proto=%d prefixlen=%d\n",
++ exttype,
++ proto,
++ prefixlen);
++ /* sanity checks... */
++ if(pfkey_address) {
++ ERROR("pfkey_address_build: "
++ "why is pfkey_address already pointing to something?\n");
++ SENDERR(EINVAL);
++ }
++
++ if (!address) {
++ ERROR("pfkey_address_build: " "address is NULL\n");
++ SENDERR(EINVAL);
++ }
++
++ switch(exttype) {
++ case SADB_EXT_ADDRESS_SRC:
++ case SADB_EXT_ADDRESS_DST:
++ case SADB_EXT_ADDRESS_PROXY:
++ case SADB_X_EXT_ADDRESS_DST2:
++ case SADB_X_EXT_ADDRESS_SRC_FLOW:
++ case SADB_X_EXT_ADDRESS_DST_FLOW:
++ case SADB_X_EXT_ADDRESS_SRC_MASK:
++ case SADB_X_EXT_ADDRESS_DST_MASK:
++#ifdef NAT_TRAVERSAL
++ case SADB_X_EXT_NAT_T_OA:
++#endif
++ break;
++ default:
++ ERROR("pfkey_address_build: "
++ "unrecognised ext_type=%d.\n",
++ exttype);
++ SENDERR(EINVAL);
++ }
++
++ switch(address->sa_family) {
++ case AF_INET:
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_address_build: "
++ "found address family AF_INET.\n");
++ saddr_len = sizeof(struct sockaddr_in);
++ sprintf(ipaddr_txt, "%d.%d.%d.%d:%d"
++ , (((struct sockaddr_in*)address)->sin_addr.s_addr >> 0) & 0xFF
++ , (((struct sockaddr_in*)address)->sin_addr.s_addr >> 8) & 0xFF
++ , (((struct sockaddr_in*)address)->sin_addr.s_addr >> 16) & 0xFF
++ , (((struct sockaddr_in*)address)->sin_addr.s_addr >> 24) & 0xFF
++ , ntohs(((struct sockaddr_in*)address)->sin_port));
++ break;
++ case AF_INET6:
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_address_build: "
++ "found address family AF_INET6.\n");
++ saddr_len = sizeof(struct sockaddr_in6);
++ sprintf(ipaddr_txt, "%x:%x:%x:%x:%x:%x:%x:%x-%x"
++ , ntohs(((struct sockaddr_in6*)address)->sin6_addr.s6_addr16[0])
++ , ntohs(((struct sockaddr_in6*)address)->sin6_addr.s6_addr16[1])
++ , ntohs(((struct sockaddr_in6*)address)->sin6_addr.s6_addr16[2])
++ , ntohs(((struct sockaddr_in6*)address)->sin6_addr.s6_addr16[3])
++ , ntohs(((struct sockaddr_in6*)address)->sin6_addr.s6_addr16[4])
++ , ntohs(((struct sockaddr_in6*)address)->sin6_addr.s6_addr16[5])
++ , ntohs(((struct sockaddr_in6*)address)->sin6_addr.s6_addr16[6])
++ , ntohs(((struct sockaddr_in6*)address)->sin6_addr.s6_addr16[7])
++ , ntohs(((struct sockaddr_in6*)address)->sin6_port));
++ break;
++ default:
++ ERROR("pfkey_address_build: "
++ "address->sa_family=%d not supported.\n",
++ address->sa_family);
++ SENDERR(EPFNOSUPPORT);
++ }
++
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_address_build: "
++ "found address=%s.\n",
++ ipaddr_txt);
++ if(prefixlen != 0) {
++ ERROR("pfkey_address_build: "
++ "address prefixes not supported yet.\n");
++ SENDERR(EAFNOSUPPORT); /* not supported yet */
++ }
++
++ /* allocate some memory for the extension */
++ pfkey_address = (struct sadb_address*)
++ MALLOC(ALIGN_N(sizeof(struct sadb_address) + saddr_len, IPSEC_PFKEYv2_ALIGN));
++ *pfkey_ext = (struct sadb_ext*)pfkey_address;
++
++ if(pfkey_address == NULL ) {
++ ERROR("pfkey_lifetime_build: "
++ "memory allocation failed\n");
++ SENDERR(ENOMEM);
++ }
++ memset(pfkey_address,
++ 0,
++ ALIGN_N(sizeof(struct sadb_address) + saddr_len,
++ IPSEC_PFKEYv2_ALIGN));
++
++ pfkey_address->sadb_address_len = DIVUP(sizeof(struct sadb_address) + saddr_len,
++ IPSEC_PFKEYv2_ALIGN);
++
++ pfkey_address->sadb_address_exttype = exttype;
++ pfkey_address->sadb_address_proto = proto;
++ pfkey_address->sadb_address_prefixlen = prefixlen;
++ pfkey_address->sadb_address_reserved = 0;
++
++ memcpy((char*)pfkey_address + sizeof(struct sadb_address),
++ address,
++ saddr_len);
++
++#if 0
++ for(i = 0; i < sizeof(struct sockaddr_in) - offsetof(struct sockaddr_in, sin_zero); i++) {
++ pfkey_address_s_ska.sin_zero[i] = 0;
++ }
++#endif
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_address_build: "
++ "successful created len: %d.\n", pfkey_address->sadb_address_len);
++
++ errlab:
++ return error;
++}
++
++int
++pfkey_key_build(struct sadb_ext** pfkey_ext,
++ uint16_t exttype,
++ uint16_t key_bits,
++ char* key)
++{
++ int error = 0;
++ struct sadb_key *pfkey_key = (struct sadb_key *)*pfkey_ext;
++
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_key_build:\n");
++ /* sanity checks... */
++ if(pfkey_key) {
++ ERROR("pfkey_key_build: "
++ "why is pfkey_key already pointing to something?\n");
++ SENDERR(EINVAL);
++ }
++
++ if(!key_bits) {
++ ERROR("pfkey_key_build: "
++ "key_bits is zero, it must be non-zero.\n");
++ SENDERR(EINVAL);
++ }
++
++ if( !((exttype == SADB_EXT_KEY_AUTH) || (exttype == SADB_EXT_KEY_ENCRYPT))) {
++ ERROR("pfkey_key_build: "
++ "unsupported extension type=%d.\n",
++ exttype);
++ SENDERR(EINVAL);
++ }
++
++ if(!(*pfkey_ext = (struct sadb_ext*)
++ pfkey_key = (struct sadb_key*)
++ MALLOC(sizeof(struct sadb_key) +
++ DIVUP(key_bits, 64) * IPSEC_PFKEYv2_ALIGN))) {
++ ERROR("pfkey_key_build: "
++ "memory allocation failed\n");
++ SENDERR(ENOMEM);
++ }
++ memset(pfkey_key,
++ 0,
++ sizeof(struct sadb_key) +
++ DIVUP(key_bits, 64) * IPSEC_PFKEYv2_ALIGN);
++
++ pfkey_key->sadb_key_len = DIVUP(sizeof(struct sadb_key) * IPSEC_PFKEYv2_ALIGN + key_bits,
++ 64);
++ pfkey_key->sadb_key_exttype = exttype;
++ pfkey_key->sadb_key_bits = key_bits;
++ pfkey_key->sadb_key_reserved = 0;
++ memcpy((char*)pfkey_key + sizeof(struct sadb_key),
++ key,
++ DIVUP(key_bits, 8));
++
++errlab:
++ return error;
++}
++
++int
++pfkey_ident_build(struct sadb_ext** pfkey_ext,
++ uint16_t exttype,
++ uint16_t ident_type,
++ uint64_t ident_id,
++ uint8_t ident_len,
++ char* ident_string)
++{
++ int error = 0;
++ struct sadb_ident *pfkey_ident = (struct sadb_ident *)*pfkey_ext;
++ int data_len = ident_len * IPSEC_PFKEYv2_ALIGN - sizeof(struct sadb_ident);
++
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_ident_build:\n");
++ /* sanity checks... */
++ if(pfkey_ident) {
++ ERROR("pfkey_ident_build: "
++ "why is pfkey_ident already pointing to something?\n");
++ SENDERR(EINVAL);
++ }
++
++ if( ! ((exttype == SADB_EXT_IDENTITY_SRC) ||
++ (exttype == SADB_EXT_IDENTITY_DST))) {
++ ERROR("pfkey_ident_build: "
++ "unsupported extension type=%d.\n",
++ exttype);
++ SENDERR(EINVAL);
++ }
++
++ if((ident_type == SADB_IDENTTYPE_RESERVED)) {
++ ERROR("pfkey_ident_build: "
++ "ident_type must be non-zero.\n");
++ SENDERR(EINVAL);
++ }
++
++ if(ident_type > SADB_IDENTTYPE_MAX) {
++ ERROR("pfkey_ident_build: "
++ "identtype=%d out of range.\n",
++ ident_type);
++ SENDERR(EINVAL);
++ }
++
++ if(((ident_type == SADB_IDENTTYPE_PREFIX) ||
++ (ident_type == SADB_IDENTTYPE_FQDN)) &&
++ !ident_string) {
++ ERROR("pfkey_ident_build: "
++ "string required to allocate size of extension.\n");
++ SENDERR(EINVAL);
++ }
++
++#if 0
++ if((ident_type == SADB_IDENTTYPE_USERFQDN) ) {
++ }
++#endif
++
++ if(!(*pfkey_ext = (struct sadb_ext*)
++ pfkey_ident = (struct sadb_ident*)
++ MALLOC(ident_len * IPSEC_PFKEYv2_ALIGN))) {
++ ERROR("pfkey_ident_build: "
++ "memory allocation failed\n");
++ SENDERR(ENOMEM);
++ }
++ memset(pfkey_ident, 0, ident_len * IPSEC_PFKEYv2_ALIGN);
++
++ pfkey_ident->sadb_ident_len = ident_len;
++ pfkey_ident->sadb_ident_exttype = exttype;
++ pfkey_ident->sadb_ident_type = ident_type;
++ pfkey_ident->sadb_ident_reserved = 0;
++ pfkey_ident->sadb_ident_id = ident_id;
++ memcpy((char*)pfkey_ident + sizeof(struct sadb_ident),
++ ident_string,
++ data_len);
++
++errlab:
++ return error;
++}
++
++int
++pfkey_sens_build(struct sadb_ext** pfkey_ext,
++ uint32_t dpd,
++ uint8_t sens_level,
++ uint8_t sens_len,
++ uint64_t* sens_bitmap,
++ uint8_t integ_level,
++ uint8_t integ_len,
++ uint64_t* integ_bitmap)
++{
++ int error = 0;
++ struct sadb_sens *pfkey_sens = (struct sadb_sens *)*pfkey_ext;
++ int i;
++ uint64_t* bitmap;
++
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_sens_build:\n");
++ /* sanity checks... */
++ if(pfkey_sens) {
++ ERROR("pfkey_sens_build: "
++ "why is pfkey_sens already pointing to something?\n");
++ SENDERR(EINVAL);
++ }
++
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_sens_build: "
++ "Sorry, I can't build exttype=%d yet.\n",
++ (*pfkey_ext)->sadb_ext_type);
++ SENDERR(EINVAL); /* don't process these yet */
++
++ if(!(*pfkey_ext = (struct sadb_ext*)
++ pfkey_sens = (struct sadb_sens*)
++ MALLOC(sizeof(struct sadb_sens) +
++ (sens_len + integ_len) * sizeof(uint64_t)))) {
++ ERROR("pfkey_sens_build: "
++ "memory allocation failed\n");
++ SENDERR(ENOMEM);
++ }
++ memset(pfkey_sens,
++ 0,
++ sizeof(struct sadb_sens) +
++ (sens_len + integ_len) * sizeof(uint64_t));
++
++ pfkey_sens->sadb_sens_len = (sizeof(struct sadb_sens) +
++ (sens_len + integ_len) * sizeof(uint64_t)) / IPSEC_PFKEYv2_ALIGN;
++ pfkey_sens->sadb_sens_exttype = SADB_EXT_SENSITIVITY;
++ pfkey_sens->sadb_sens_dpd = dpd;
++ pfkey_sens->sadb_sens_sens_level = sens_level;
++ pfkey_sens->sadb_sens_sens_len = sens_len;
++ pfkey_sens->sadb_sens_integ_level = integ_level;
++ pfkey_sens->sadb_sens_integ_len = integ_len;
++ pfkey_sens->sadb_sens_reserved = 0;
++
++ bitmap = (uint64_t*)((char*)pfkey_ext + sizeof(struct sadb_sens));
++ for(i = 0; i < sens_len; i++) {
++ *bitmap = sens_bitmap[i];
++ bitmap++;
++ }
++ for(i = 0; i < integ_len; i++) {
++ *bitmap = integ_bitmap[i];
++ bitmap++;
++ }
++
++errlab:
++ return error;
++}
++
++int
++pfkey_prop_build(struct sadb_ext** pfkey_ext,
++ uint8_t replay,
++ unsigned int comb_num,
++ struct sadb_comb* comb)
++{
++ int error = 0;
++ int i;
++ struct sadb_prop *pfkey_prop = (struct sadb_prop *)*pfkey_ext;
++ struct sadb_comb *combp;
++
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_prop_build:\n");
++ /* sanity checks... */
++ if(pfkey_prop) {
++ ERROR("pfkey_prop_build: "
++ "why is pfkey_prop already pointing to something?\n");
++ SENDERR(EINVAL);
++ }
++
++ if(!(*pfkey_ext = (struct sadb_ext*)
++ pfkey_prop = (struct sadb_prop*)
++ MALLOC(sizeof(struct sadb_prop) +
++ comb_num * sizeof(struct sadb_comb)))) {
++ ERROR("pfkey_prop_build: "
++ "memory allocation failed\n");
++ SENDERR(ENOMEM);
++ }
++ memset(pfkey_prop,
++ 0,
++ sizeof(struct sadb_prop) +
++ comb_num * sizeof(struct sadb_comb));
++
++ pfkey_prop->sadb_prop_len = (sizeof(struct sadb_prop) +
++ comb_num * sizeof(struct sadb_comb)) / IPSEC_PFKEYv2_ALIGN;
++
++ pfkey_prop->sadb_prop_exttype = SADB_EXT_PROPOSAL;
++ pfkey_prop->sadb_prop_replay = replay;
++
++ for(i=0; i<3; i++) {
++ pfkey_prop->sadb_prop_reserved[i] = 0;
++ }
++
++ combp = (struct sadb_comb*)((char*)*pfkey_ext + sizeof(struct sadb_prop));
++ for(i = 0; i < comb_num; i++) {
++ memcpy (combp, &(comb[i]), sizeof(struct sadb_comb));
++ combp++;
++ }
++
++#if 0
++ uint8_t sadb_comb_auth;
++ uint8_t sadb_comb_encrypt;
++ uint16_t sadb_comb_flags;
++ uint16_t sadb_comb_auth_minbits;
++ uint16_t sadb_comb_auth_maxbits;
++ uint16_t sadb_comb_encrypt_minbits;
++ uint16_t sadb_comb_encrypt_maxbits;
++ uint32_t sadb_comb_reserved;
++ uint32_t sadb_comb_soft_allocations;
++ uint32_t sadb_comb_hard_allocations;
++ uint64_t sadb_comb_soft_bytes;
++ uint64_t sadb_comb_hard_bytes;
++ uint64_t sadb_comb_soft_addtime;
++ uint64_t sadb_comb_hard_addtime;
++ uint64_t sadb_comb_soft_usetime;
++ uint64_t sadb_comb_hard_usetime;
++ uint32_t sadb_comb_soft_packets;
++ uint32_t sadb_comb_hard_packets;
++#endif
++errlab:
++ return error;
++}
++
++int
++pfkey_supported_build(struct sadb_ext** pfkey_ext,
++ uint16_t exttype,
++ unsigned int alg_num,
++ struct sadb_alg* alg)
++{
++ int error = 0;
++ unsigned int i;
++ struct sadb_supported *pfkey_supported = (struct sadb_supported *)*pfkey_ext;
++ struct sadb_alg *pfkey_alg;
++
++ /* sanity checks... */
++ if(pfkey_supported) {
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_supported_build: "
++ "why is pfkey_supported already pointing to something?\n");
++ SENDERR(EINVAL);
++ }
++
++ if( !((exttype == SADB_EXT_SUPPORTED_AUTH) || (exttype == SADB_EXT_SUPPORTED_ENCRYPT))) {
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_supported_build: "
++ "unsupported extension type=%d.\n",
++ exttype);
++ SENDERR(EINVAL);
++ }
++
++ if(!(*pfkey_ext = (struct sadb_ext*)
++ pfkey_supported = (struct sadb_supported*)
++ MALLOC(sizeof(struct sadb_supported) +
++ alg_num *
++ sizeof(struct sadb_alg)))) {
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_supported_build: "
++ "memory allocation failed\n");
++ SENDERR(ENOMEM);
++ }
++ memset(pfkey_supported,
++ 0,
++ sizeof(struct sadb_supported) +
++ alg_num *
++ sizeof(struct sadb_alg));
++
++ pfkey_supported->sadb_supported_len = (sizeof(struct sadb_supported) +
++ alg_num *
++ sizeof(struct sadb_alg)) /
++ IPSEC_PFKEYv2_ALIGN;
++ pfkey_supported->sadb_supported_exttype = exttype;
++ pfkey_supported->sadb_supported_reserved = 0;
++
++ pfkey_alg = (struct sadb_alg*)((char*)pfkey_supported + sizeof(struct sadb_supported));
++ for(i = 0; i < alg_num; i++) {
++ memcpy (pfkey_alg, &(alg[i]), sizeof(struct sadb_alg));
++ pfkey_alg->sadb_alg_reserved = 0;
++ pfkey_alg++;
++ }
++
++#if 0
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_supported_build: "
++ "Sorry, I can't build exttype=%d yet.\n",
++ (*pfkey_ext)->sadb_ext_type);
++ SENDERR(EINVAL); /* don't process these yet */
++
++ uint8_t sadb_alg_id;
++ uint8_t sadb_alg_ivlen;
++ uint16_t sadb_alg_minbits;
++ uint16_t sadb_alg_maxbits;
++ uint16_t sadb_alg_reserved;
++#endif
++errlab:
++ return error;
++}
++
++int
++pfkey_spirange_build(struct sadb_ext** pfkey_ext,
++ uint16_t exttype,
++ uint32_t min, /* in network order */
++ uint32_t max) /* in network order */
++{
++ int error = 0;
++ struct sadb_spirange *pfkey_spirange = (struct sadb_spirange *)*pfkey_ext;
++
++ /* sanity checks... */
++ if(pfkey_spirange) {
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_spirange_build: "
++ "why is pfkey_spirange already pointing to something?\n");
++ SENDERR(EINVAL);
++ }
++
++ if(ntohl(max) < ntohl(min)) {
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_spirange_build: "
++ "minspi=%08x must be < maxspi=%08x.\n",
++ ntohl(min),
++ ntohl(max));
++ SENDERR(EINVAL);
++ }
++
++ if(ntohl(min) <= 255) {
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_spirange_build: "
++ "minspi=%08x must be > 255.\n",
++ ntohl(min));
++ SENDERR(EEXIST);
++ }
++
++ if(!(*pfkey_ext = (struct sadb_ext*)
++ pfkey_spirange = (struct sadb_spirange*)
++ MALLOC(sizeof(struct sadb_spirange)))) {
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_spirange_build: "
++ "memory allocation failed\n");
++ SENDERR(ENOMEM);
++ }
++ memset(pfkey_spirange,
++ 0,
++ sizeof(struct sadb_spirange));
++
++ pfkey_spirange->sadb_spirange_len = sizeof(struct sadb_spirange) / IPSEC_PFKEYv2_ALIGN;
++
++ pfkey_spirange->sadb_spirange_exttype = SADB_EXT_SPIRANGE;
++ pfkey_spirange->sadb_spirange_min = min;
++ pfkey_spirange->sadb_spirange_max = max;
++ pfkey_spirange->sadb_spirange_reserved = 0;
++ errlab:
++ return error;
++}
++
++int
++pfkey_x_kmprivate_build(struct sadb_ext** pfkey_ext)
++{
++ int error = 0;
++ struct sadb_x_kmprivate *pfkey_x_kmprivate = (struct sadb_x_kmprivate *)*pfkey_ext;
++
++ /* sanity checks... */
++ if(pfkey_x_kmprivate) {
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_x_kmprivate_build: "
++ "why is pfkey_x_kmprivate already pointing to something?\n");
++ SENDERR(EINVAL);
++ }
++
++ pfkey_x_kmprivate->sadb_x_kmprivate_reserved = 0;
++
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_x_kmprivate_build: "
++ "Sorry, I can't build exttype=%d yet.\n",
++ (*pfkey_ext)->sadb_ext_type);
++ SENDERR(EINVAL); /* don't process these yet */
++
++ if(!(*pfkey_ext = (struct sadb_ext*)
++ pfkey_x_kmprivate = (struct sadb_x_kmprivate*)
++ MALLOC(sizeof(struct sadb_x_kmprivate)))) {
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_x_kmprivate_build: "
++ "memory allocation failed\n");
++ SENDERR(ENOMEM);
++ }
++ memset(pfkey_x_kmprivate,
++ 0,
++ sizeof(struct sadb_x_kmprivate));
++
++ pfkey_x_kmprivate->sadb_x_kmprivate_len =
++ sizeof(struct sadb_x_kmprivate) / IPSEC_PFKEYv2_ALIGN;
++
++ pfkey_x_kmprivate->sadb_x_kmprivate_exttype = SADB_X_EXT_KMPRIVATE;
++ pfkey_x_kmprivate->sadb_x_kmprivate_reserved = 0;
++errlab:
++ return error;
++}
++
++int
++pfkey_x_satype_build(struct sadb_ext** pfkey_ext,
++ uint8_t satype)
++{
++ int error = 0;
++ int i;
++ struct sadb_x_satype *pfkey_x_satype = (struct sadb_x_satype *)*pfkey_ext;
++
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_x_satype_build:\n");
++ /* sanity checks... */
++ if(pfkey_x_satype) {
++ ERROR("pfkey_x_satype_build: "
++ "why is pfkey_x_satype already pointing to something?\n");
++ SENDERR(EINVAL);
++ }
++
++ if(!satype) {
++ ERROR("pfkey_x_satype_build: "
++ "SA type not set, must be non-zero.\n");
++ SENDERR(EINVAL);
++ }
++
++ if(satype > SADB_SATYPE_MAX) {
++ ERROR("pfkey_x_satype_build: "
++ "satype %d > max %d\n",
++ satype, SADB_SATYPE_MAX);
++ SENDERR(EINVAL);
++ }
++
++ if(!(*pfkey_ext = (struct sadb_ext*)pfkey_x_satype = (struct sadb_x_satype*)
++ MALLOC(sizeof(struct sadb_x_satype)))) {
++ ERROR("pfkey_x_satype_build: "
++ "memory allocation failed\n");
++ SENDERR(ENOMEM);
++ }
++ memset(pfkey_x_satype,
++ 0,
++ sizeof(struct sadb_x_satype));
++
++ pfkey_x_satype->sadb_x_satype_len = sizeof(struct sadb_x_satype) / IPSEC_PFKEYv2_ALIGN;
++
++ pfkey_x_satype->sadb_x_satype_exttype = SADB_X_EXT_SATYPE2;
++ pfkey_x_satype->sadb_x_satype_satype = satype;
++ for(i=0; i<3; i++) {
++ pfkey_x_satype->sadb_x_satype_reserved[i] = 0;
++ }
++
++errlab:
++ return error;
++}
++
++int
++pfkey_x_debug_build(struct sadb_ext** pfkey_ext,
++ uint32_t tunnel,
++ uint32_t netlink,
++ uint32_t xform,
++ uint32_t eroute,
++ uint32_t spi,
++ uint32_t radij,
++ uint32_t esp,
++ uint32_t ah,
++ uint32_t rcv,
++ uint32_t pfkey,
++ uint32_t ipcomp,
++ uint32_t verbose)
++{
++ int error = 0;
++ int i;
++ struct sadb_x_debug *pfkey_x_debug = (struct sadb_x_debug *)*pfkey_ext;
++
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_x_debug_build:\n");
++ /* sanity checks... */
++ if(pfkey_x_debug) {
++ ERROR("pfkey_x_debug_build: "
++ "why is pfkey_x_debug already pointing to something?\n");
++ SENDERR(EINVAL);
++ }
++
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_x_debug_build: "
++ "tunnel=%x netlink=%x xform=%x eroute=%x spi=%x radij=%x esp=%x ah=%x rcv=%x pfkey=%x ipcomp=%x verbose=%x?\n",
++ tunnel, netlink, xform, eroute, spi, radij, esp, ah, rcv, pfkey, ipcomp, verbose);
++
++ if(!(*pfkey_ext = (struct sadb_ext*)pfkey_x_debug = (struct sadb_x_debug*)
++ MALLOC(sizeof(struct sadb_x_debug)))) {
++ ERROR("pfkey_x_debug_build: "
++ "memory allocation failed\n");
++ SENDERR(ENOMEM);
++ }
++#if 0
++ memset(pfkey_x_debug,
++ 0,
++ sizeof(struct sadb_x_debug));
++#endif
++
++ pfkey_x_debug->sadb_x_debug_len = sizeof(struct sadb_x_debug) / IPSEC_PFKEYv2_ALIGN;
++ pfkey_x_debug->sadb_x_debug_exttype = SADB_X_EXT_DEBUG;
++
++ pfkey_x_debug->sadb_x_debug_tunnel = tunnel;
++ pfkey_x_debug->sadb_x_debug_netlink = netlink;
++ pfkey_x_debug->sadb_x_debug_xform = xform;
++ pfkey_x_debug->sadb_x_debug_eroute = eroute;
++ pfkey_x_debug->sadb_x_debug_spi = spi;
++ pfkey_x_debug->sadb_x_debug_radij = radij;
++ pfkey_x_debug->sadb_x_debug_esp = esp;
++ pfkey_x_debug->sadb_x_debug_ah = ah;
++ pfkey_x_debug->sadb_x_debug_rcv = rcv;
++ pfkey_x_debug->sadb_x_debug_pfkey = pfkey;
++ pfkey_x_debug->sadb_x_debug_ipcomp = ipcomp;
++ pfkey_x_debug->sadb_x_debug_verbose = verbose;
++
++ for(i=0; i<4; i++) {
++ pfkey_x_debug->sadb_x_debug_reserved[i] = 0;
++ }
++
++errlab:
++ return error;
++}
++
++int
++pfkey_x_nat_t_type_build(struct sadb_ext** pfkey_ext,
++ uint8_t type)
++{
++ int error = 0;
++ int i;
++ struct sadb_x_nat_t_type *pfkey_x_nat_t_type = (struct sadb_x_nat_t_type *)*pfkey_ext;
++
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_x_nat_t_type_build:\n");
++ /* sanity checks... */
++ if(pfkey_x_nat_t_type) {
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_x_nat_t_type_build: "
++ "why is pfkey_x_nat_t_type already pointing to something?\n");
++ SENDERR(EINVAL);
++ }
++
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_x_nat_t_type_build: "
++ "type=%d\n", type);
++
++ if(!(*pfkey_ext = (struct sadb_ext*)pfkey_x_nat_t_type = (struct sadb_x_nat_t_type*)
++ MALLOC(sizeof(struct sadb_x_nat_t_type)))) {
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_x_nat_t_type_build: "
++ "memory allocation failed\n");
++ SENDERR(ENOMEM);
++ }
++
++ pfkey_x_nat_t_type->sadb_x_nat_t_type_len = sizeof(struct sadb_x_nat_t_type) / IPSEC_PFKEYv2_ALIGN;
++ pfkey_x_nat_t_type->sadb_x_nat_t_type_exttype = SADB_X_EXT_NAT_T_TYPE;
++ pfkey_x_nat_t_type->sadb_x_nat_t_type_type = type;
++ for(i=0; i<3; i++) {
++ pfkey_x_nat_t_type->sadb_x_nat_t_type_reserved[i] = 0;
++ }
++
++errlab:
++ return error;
++}
++int
++pfkey_x_nat_t_port_build(struct sadb_ext** pfkey_ext,
++ uint16_t exttype,
++ uint16_t port)
++{
++ int error = 0;
++ struct sadb_x_nat_t_port *pfkey_x_nat_t_port = (struct sadb_x_nat_t_port *)*pfkey_ext;
++
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_x_nat_t_port_build:\n");
++ /* sanity checks... */
++ if(pfkey_x_nat_t_port) {
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_x_nat_t_port_build: "
++ "why is pfkey_x_nat_t_port already pointing to something?\n");
++ SENDERR(EINVAL);
++ }
++
++ switch(exttype) {
++ case SADB_X_EXT_NAT_T_SPORT:
++ case SADB_X_EXT_NAT_T_DPORT:
++ break;
++ default:
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_nat_t_port_build: "
++ "unrecognised ext_type=%d.\n",
++ exttype);
++ SENDERR(EINVAL);
++ }
++
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_x_nat_t_port_build: "
++ "ext=%d, port=%d\n", exttype, port);
++
++ if(!(*pfkey_ext = (struct sadb_ext*)pfkey_x_nat_t_port = (struct sadb_x_nat_t_port*)
++ MALLOC(sizeof(struct sadb_x_nat_t_port)))) {
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_x_nat_t_port_build: "
++ "memory allocation failed\n");
++ SENDERR(ENOMEM);
++ }
++
++ pfkey_x_nat_t_port->sadb_x_nat_t_port_len = sizeof(struct sadb_x_nat_t_port) / IPSEC_PFKEYv2_ALIGN;
++ pfkey_x_nat_t_port->sadb_x_nat_t_port_exttype = exttype;
++ pfkey_x_nat_t_port->sadb_x_nat_t_port_port = port;
++ pfkey_x_nat_t_port->sadb_x_nat_t_port_reserved = 0;
++
++errlab:
++ return error;
++}
++
++int pfkey_x_protocol_build(struct sadb_ext **pfkey_ext,
++ uint8_t protocol)
++{
++ int error = 0;
++ struct sadb_protocol * p = (struct sadb_protocol *)*pfkey_ext;
++ DEBUGGING(PF_KEY_DEBUG_BUILD,"pfkey_x_protocol_build: protocol=%u\n", protocol);
++ /* sanity checks... */
++ if (p != 0) {
++ ERROR("pfkey_x_protocol_build: bogus protocol pointer\n");
++ SENDERR(EINVAL);
++ }
++ if ((p = (struct sadb_protocol*)MALLOC(sizeof(*p))) == 0) {
++ ERROR("pfkey_build: memory allocation failed\n");
++ SENDERR(ENOMEM);
++ }
++ *pfkey_ext = (struct sadb_ext *)p;
++ p->sadb_protocol_len = sizeof(*p) / sizeof(uint64_t);
++ p->sadb_protocol_exttype = SADB_X_EXT_PROTOCOL;
++ p->sadb_protocol_proto = protocol;
++ p->sadb_protocol_flags = 0;
++ p->sadb_protocol_reserved2 = 0;
++ errlab:
++ return error;
++}
++
++
++#if I_DONT_THINK_THIS_WILL_BE_USEFUL
++int (*ext_default_builders[SADB_EXT_MAX +1])(struct sadb_msg*, struct sadb_ext*)
++ =
++{
++ NULL, /* pfkey_msg_build, */
++ pfkey_sa_build,
++ pfkey_lifetime_build,
++ pfkey_lifetime_build,
++ pfkey_lifetime_build,
++ pfkey_address_build,
++ pfkey_address_build,
++ pfkey_address_build,
++ pfkey_key_build,
++ pfkey_key_build,
++ pfkey_ident_build,
++ pfkey_ident_build,
++ pfkey_sens_build,
++ pfkey_prop_build,
++ pfkey_supported_build,
++ pfkey_supported_build,
++ pfkey_spirange_build,
++ pfkey_x_kmprivate_build,
++ pfkey_x_satype_build,
++ pfkey_sa_build,
++ pfkey_address_build,
++ pfkey_address_build,
++ pfkey_address_build,
++ pfkey_address_build,
++ pfkey_address_build,
++ pfkey_x_ext_debug_build
++};
++#endif
++
++int
++pfkey_msg_build(struct sadb_msg **pfkey_msg, struct sadb_ext *extensions[], int dir)
++{
++ int error = 0;
++ unsigned ext;
++ unsigned total_size;
++ struct sadb_ext *pfkey_ext;
++ int extensions_seen = 0;
++#ifndef __KERNEL__
++ struct sadb_ext *extensions_check[SADB_EXT_MAX + 1];
++#endif
++
++ if(!extensions[0]) {
++ ERROR("pfkey_msg_build: "
++ "extensions[0] must be specified (struct sadb_msg).\n");
++ SENDERR(EINVAL);
++ }
++
++ /* figure out the total size for all the requested extensions */
++ total_size = IPSEC_PFKEYv2_WORDS(sizeof(struct sadb_msg));
++ for(ext = 1; ext <= SADB_EXT_MAX; ext++) {
++ if(extensions[ext]) {
++ total_size += (extensions[ext])->sadb_ext_len;
++ }
++ }
++
++ /* allocate that much space */
++ *pfkey_msg = (struct sadb_msg*)MALLOC(total_size * IPSEC_PFKEYv2_ALIGN);
++ if(*pfkey_msg == NULL) {
++ ERROR("pfkey_msg_build: "
++ "memory allocation failed\n");
++ SENDERR(ENOMEM);
++ }
++
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_msg_build: "
++ "pfkey_msg=0p%p allocated %lu bytes, &(extensions[0])=0p%p\n",
++ *pfkey_msg,
++ (unsigned long)(total_size * IPSEC_PFKEYv2_ALIGN),
++ &(extensions[0]));
++
++ memcpy(*pfkey_msg,
++ extensions[0],
++ sizeof(struct sadb_msg));
++ (*pfkey_msg)->sadb_msg_len = total_size;
++ (*pfkey_msg)->sadb_msg_reserved = 0;
++ extensions_seen = 1 ;
++
++ /*
++ * point pfkey_ext to immediately after the space for the header,
++ * i.e. at the first extension location.
++ */
++ pfkey_ext = (struct sadb_ext*)(((char*)(*pfkey_msg)) + sizeof(struct sadb_msg));
++
++ for(ext = 1; ext <= SADB_EXT_MAX; ext++) {
++ /* copy from extension[ext] to buffer */
++ if(extensions[ext]) {
++ /* Is this type of extension permitted for this type of message? */
++ if(!(extensions_bitmaps[dir][EXT_BITS_PERM][(*pfkey_msg)->sadb_msg_type] &
++ 1<<ext)) {
++ ERROR("pfkey_msg_build: "
++ "ext type %d not permitted, exts_perm=%08x, 1<<type=%08x\n",
++ ext,
++ extensions_bitmaps[dir][EXT_BITS_PERM][(*pfkey_msg)->sadb_msg_type],
++ 1<<ext);
++ SENDERR(EINVAL);
++ }
++
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_msg_build: "
++ "copying %lu bytes from extensions[%u] (type=%d)\n",
++ (unsigned long)(extensions[ext]->sadb_ext_len * IPSEC_PFKEYv2_ALIGN),
++ ext,
++ extensions[ext]->sadb_ext_type);
++
++ memcpy(pfkey_ext,
++ extensions[ext],
++ (extensions[ext])->sadb_ext_len * IPSEC_PFKEYv2_ALIGN);
++ ((char*)pfkey_ext) += (extensions[ext])->sadb_ext_len * IPSEC_PFKEYv2_ALIGN;
++ /* Mark that we have seen this extension and remember the header location */
++ extensions_seen |= ( 1 << ext );
++ }
++ }
++
++ /* check required extensions */
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_msg_build: "
++ "extensions permitted=%08x, seen=%08x, required=%08x.\n",
++ extensions_bitmaps[dir][EXT_BITS_PERM][(*pfkey_msg)->sadb_msg_type],
++ extensions_seen,
++ extensions_bitmaps[dir][EXT_BITS_REQ][(*pfkey_msg)->sadb_msg_type]);
++
++ if((extensions_seen &
++ extensions_bitmaps[dir][EXT_BITS_REQ][(*pfkey_msg)->sadb_msg_type]) !=
++ extensions_bitmaps[dir][EXT_BITS_REQ][(*pfkey_msg)->sadb_msg_type]) {
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_msg_build: "
++ "required extensions missing:%08x.\n",
++ extensions_bitmaps[dir][EXT_BITS_REQ][(*pfkey_msg)->sadb_msg_type] -
++ (extensions_seen &
++ extensions_bitmaps[dir][EXT_BITS_REQ][(*pfkey_msg)->sadb_msg_type]) );
++ SENDERR(EINVAL);
++ }
++
++#ifndef __KERNEL__
++/*
++ * this is silly, there is no need to reparse the message that we just built.
++ *
++ */
++ if((error = pfkey_msg_parse(*pfkey_msg, NULL, extensions_check, dir))) {
++ ERROR(
++ "pfkey_msg_build: "
++ "Trouble parsing newly built pfkey message, error=%d.\n",
++ error);
++ SENDERR(-error);
++ }
++#endif
++
++errlab:
++
++ return error;
++}
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.49 2004/04/12 02:59:06 mcr
++ * erroneously moved pfkey_v2_build.c
++ *
++ * Revision 1.48 2004/04/09 18:00:40 mcr
++ * Moved from linux/lib/libfreeswan/pfkey_v2_build.c,v
++ *
++ * Revision 1.47 2004/03/08 01:59:08 ken
++ * freeswan.h -> openswan.h
++ *
++ * Revision 1.46 2003/12/10 01:20:19 mcr
++ * NAT-traversal patches to KLIPS.
++ *
++ * Revision 1.45 2003/12/04 23:01:12 mcr
++ * removed ipsec_netlink.h
++ *
++ * Revision 1.44 2003/10/31 02:27:12 mcr
++ * pulled up port-selector patches and sa_id elimination.
++ *
++ * Revision 1.43.4.2 2003/10/29 01:11:32 mcr
++ * added debugging for pfkey library.
++ *
++ * Revision 1.43.4.1 2003/09/21 13:59:44 mcr
++ * pre-liminary X.509 patch - does not yet pass tests.
++ *
++ * Revision 1.43 2003/05/07 17:29:17 mcr
++ * new function pfkey_debug_func added for us in debugging from
++ * pfkey library.
++ *
++ * Revision 1.42 2003/01/30 02:32:09 rgb
++ *
++ * Rename SAref table macro names for clarity.
++ * Convert IPsecSAref_t from signed to unsigned to fix apparent SAref exhaustion bug.
++ *
++ * Revision 1.41 2002/12/13 18:16:02 mcr
++ * restored sa_ref code
++ *
++ * Revision 1.40 2002/12/13 18:06:52 mcr
++ * temporarily removed sadb_x_sa_ref reference for 2.xx
++ *
++ * Revision 1.39 2002/12/13 17:43:28 mcr
++ * commented out access to sadb_x_sa_ref for 2.xx branch
++ *
++ * Revision 1.38 2002/10/09 03:12:05 dhr
++ *
++ * [kenb+dhr] 64-bit fixes
++ *
++ * Revision 1.37 2002/09/20 15:40:39 rgb
++ * Added new function pfkey_sa_ref_build() to accomodate saref parameter.
++ *
++ * Revision 1.36 2002/09/20 05:01:22 rgb
++ * Generalise for platform independance: fix (ia64) using unsigned for sizes.
++ *
++ * Revision 1.35 2002/07/24 18:44:54 rgb
++ * Type fiddling to tame ia64 compiler.
++ *
++ * Revision 1.34 2002/05/23 07:14:11 rgb
++ * Cleaned up %p variants to 0p%p for test suite cleanup.
++ *
++ * Revision 1.33 2002/04/24 07:55:32 mcr
++ * #include patches and Makefiles for post-reorg compilation.
++ *
++ * Revision 1.32 2002/04/24 07:36:40 mcr
++ * Moved from ./lib/pfkey_v2_build.c,v
++ *
++ * Revision 1.31 2002/01/29 22:25:35 rgb
++ * Re-add ipsec_kversion.h to keep MALLOC happy.
++ *
++ * Revision 1.30 2002/01/29 01:59:09 mcr
++ * removal of kversions.h - sources that needed it now use ipsec_param.h.
++ * updating of IPv6 structures to match latest in6.h version.
++ * removed dead code from openswan.h that also duplicated kversions.h
++ * code.
++ *
++ * Revision 1.29 2001/12/19 21:06:09 rgb
++ * Added port numbers to pfkey_address_build() debugging.
++ *
++ * Revision 1.28 2001/11/06 19:47:47 rgb
++ * Added packet parameter to lifetime and comb structures.
++ *
++ * Revision 1.27 2001/10/18 04:45:24 rgb
++ * 2.4.9 kernel deprecates linux/malloc.h in favour of linux/slab.h,
++ * lib/openswan.h version macros moved to lib/kversions.h.
++ * Other compiler directive cleanups.
++ *
++ * Revision 1.26 2001/09/08 21:13:34 rgb
++ * Added pfkey ident extension support for ISAKMPd. (NetCelo)
++ *
++ * Revision 1.25 2001/06/14 19:35:16 rgb
++ * Update copyright date.
++ *
++ * Revision 1.24 2001/03/20 03:49:45 rgb
++ * Ditch superfluous debug_pfkey declaration.
++ * Move misplaced openswan.h inclusion for kernel case.
++ *
++ * Revision 1.23 2001/03/16 07:41:50 rgb
++ * Put openswan.h include before pluto includes.
++ *
++ * Revision 1.22 2001/02/27 22:24:56 rgb
++ * Re-formatting debug output (line-splitting, joining, 1arg/line).
++ * Check for satoa() return codes.
++ *
++ * Revision 1.21 2000/11/17 18:10:30 rgb
++ * Fixed bugs mostly relating to spirange, to treat all spi variables as
++ * network byte order since this is the way PF_KEYv2 stored spis.
++ *
++ * Revision 1.20 2000/10/12 00:02:39 rgb
++ * Removed 'format, ##' nonsense from debug macros for RH7.0.
++ *
++ * Revision 1.19 2000/10/10 20:10:20 rgb
++ * Added support for debug_ipcomp and debug_verbose to klipsdebug.
++ *
++ * Revision 1.18 2000/09/12 18:59:54 rgb
++ * Added Gerhard's IPv6 support to pfkey parts of libopenswan.
++ *
++ * Revision 1.17 2000/09/12 03:27:00 rgb
++ * Moved DEBUGGING definition to compile kernel with debug off.
++ *
++ * Revision 1.16 2000/09/08 19:22:12 rgb
++ * Fixed pfkey_prop_build() parameter to be only single indirection.
++ * Fixed struct alg copy.
++ *
++ * Revision 1.15 2000/08/20 21:40:01 rgb
++ * Added an address parameter sanity check to pfkey_address_build().
++ *
++ * Revision 1.14 2000/08/15 17:29:23 rgb
++ * Fixes from SZI to untested pfkey_prop_build().
++ *
++ * Revision 1.13 2000/06/02 22:54:14 rgb
++ * Added Gerhard Gessler's struct sockaddr_storage mods for IPv6 support.
++ *
++ * Revision 1.12 2000/05/10 19:24:01 rgb
++ * Fleshed out sensitivity, proposal and supported extensions.
++ *
++ * Revision 1.11 2000/03/16 14:07:23 rgb
++ * Renamed ALIGN macro to avoid fighting with others in kernel.
++ *
++ * Revision 1.10 2000/01/24 21:14:35 rgb
++ * Added disabled pluto pfkey lib debug flag.
++ *
++ * Revision 1.9 2000/01/21 06:27:32 rgb
++ * Added address cases for eroute flows.
++ * Removed unused code.
++ * Dropped unused argument to pfkey_x_satype_build().
++ * Indented compiler directives for readability.
++ * Added klipsdebug switching capability.
++ * Fixed SADB_EXT_MAX bug not permitting last extension access.
++ *
++ * Revision 1.8 1999/12/29 21:17:41 rgb
++ * Changed pfkey_msg_build() I/F to include a struct sadb_msg**
++ * parameter for cleaner manipulation of extensions[] and to guard
++ * against potential memory leaks.
++ * Changed the I/F to pfkey_msg_free() for the same reason.
++ *
++ * Revision 1.7 1999/12/09 23:12:20 rgb
++ * Removed unused cruft.
++ * Added argument to pfkey_sa_build() to do eroutes.
++ * Fixed exttype check in as yet unused pfkey_lifetime_build().
++ *
++ * Revision 1.6 1999/12/07 19:54:29 rgb
++ * Removed static pluto debug flag.
++ * Added functions for pfkey message and extensions initialisation
++ * and cleanup.
++ *
++ * Revision 1.5 1999/12/01 22:20:06 rgb
++ * Changed pfkey_sa_build to accept an SPI in network byte order.
++ * Added <string.h> to quiet userspace compiler.
++ * Moved pfkey_lib_debug variable into the library.
++ * Removed SATYPE check from pfkey_msg_hdr_build so FLUSH will work.
++ * Added extension assembly debugging.
++ * Isolated assignment with brackets to be sure of scope.
++ *
++ * Revision 1.4 1999/11/27 11:57:35 rgb
++ * Added ipv6 headers.
++ * Remove over-zealous algorithm sanity checkers from pfkey_sa_build.
++ * Debugging error messages added.
++ * Fixed missing auth and encrypt assignment bug.
++ * Add argument to pfkey_msg_parse() for direction.
++ * Move parse-after-build check inside pfkey_msg_build().
++ * Consolidated the 4 1-d extension bitmap arrays into one 4-d array.
++ * Add CVS log entry to bottom of file.
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/libfreeswan/pfkey_v2_debug.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,176 @@
++/*
++ * @(#) pfkey version 2 debugging messages
++ *
++ * Copyright (C) 2001 Richard Guy Briggs <rgb@openswan.org>
++ * and Michael Richardson <mcr@openswan.org>
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ * RCSID $Id$
++ *
++ */
++
++#ifdef __KERNEL__
++
++# include <linux/kernel.h> /* for printk */
++
++# include "openswan/ipsec_kversion.h" /* for malloc switch */
++# ifdef MALLOC_SLAB
++# include <linux/slab.h> /* kmalloc() */
++# else /* MALLOC_SLAB */
++# include <linux/malloc.h> /* kmalloc() */
++# endif /* MALLOC_SLAB */
++# include <linux/errno.h> /* error codes */
++# include <linux/types.h> /* size_t */
++# include <linux/interrupt.h> /* mark_bh */
++
++# include <linux/netdevice.h> /* struct device, and other headers */
++# include <linux/etherdevice.h> /* eth_type_trans */
++extern int debug_pfkey;
++
++#else /* __KERNEL__ */
++
++# include <sys/types.h>
++# include <linux/types.h>
++# include <linux/errno.h>
++
++#endif /* __KERNEL__ */
++
++#include "openswan.h"
++#include "pfkeyv2.h"
++#include "pfkey.h"
++
++/*
++ * This file provides ASCII translations of PF_KEY magic numbers.
++ *
++ */
++
++static char *pfkey_sadb_ext_strings[]={
++ "reserved", /* SADB_EXT_RESERVED 0 */
++ "security-association", /* SADB_EXT_SA 1 */
++ "lifetime-current", /* SADB_EXT_LIFETIME_CURRENT 2 */
++ "lifetime-hard", /* SADB_EXT_LIFETIME_HARD 3 */
++ "lifetime-soft", /* SADB_EXT_LIFETIME_SOFT 4 */
++ "source-address", /* SADB_EXT_ADDRESS_SRC 5 */
++ "destination-address", /* SADB_EXT_ADDRESS_DST 6 */
++ "proxy-address", /* SADB_EXT_ADDRESS_PROXY 7 */
++ "authentication-key", /* SADB_EXT_KEY_AUTH 8 */
++ "cipher-key", /* SADB_EXT_KEY_ENCRYPT 9 */
++ "source-identity", /* SADB_EXT_IDENTITY_SRC 10 */
++ "destination-identity", /* SADB_EXT_IDENTITY_DST 11 */
++ "sensitivity-label", /* SADB_EXT_SENSITIVITY 12 */
++ "proposal", /* SADB_EXT_PROPOSAL 13 */
++ "supported-auth", /* SADB_EXT_SUPPORTED_AUTH 14 */
++ "supported-cipher", /* SADB_EXT_SUPPORTED_ENCRYPT 15 */
++ "spi-range", /* SADB_EXT_SPIRANGE 16 */
++ "X-kmpprivate", /* SADB_X_EXT_KMPRIVATE 17 */
++ "X-satype2", /* SADB_X_EXT_SATYPE2 18 */
++ "X-security-association", /* SADB_X_EXT_SA2 19 */
++ "X-destination-address2", /* SADB_X_EXT_ADDRESS_DST2 20 */
++ "X-source-flow-address", /* SADB_X_EXT_ADDRESS_SRC_FLOW 21 */
++ "X-dest-flow-address", /* SADB_X_EXT_ADDRESS_DST_FLOW 22 */
++ "X-source-mask", /* SADB_X_EXT_ADDRESS_SRC_MASK 23 */
++ "X-dest-mask", /* SADB_X_EXT_ADDRESS_DST_MASK 24 */
++ "X-set-debug", /* SADB_X_EXT_DEBUG 25 */
++#ifdef NAT_TRAVERSAL
++ "X-NAT-T-type", /* SADB_X_EXT_NAT_T_TYPE 26 */
++ "X-NAT-T-sport", /* SADB_X_EXT_NAT_T_SPORT 27 */
++ "X-NAT-T-dport", /* SADB_X_EXT_NAT_T_DPORT 28 */
++ "X-NAT-T-OA", /* SADB_X_EXT_NAT_T_OA 29 */
++#endif
++};
++
++const char *
++pfkey_v2_sadb_ext_string(int ext)
++{
++ if(ext <= SADB_EXT_MAX) {
++ return pfkey_sadb_ext_strings[ext];
++ } else {
++ return "unknown-ext";
++ }
++}
++
++
++static char *pfkey_sadb_type_strings[]={
++ "reserved", /* SADB_RESERVED */
++ "getspi", /* SADB_GETSPI */
++ "update", /* SADB_UPDATE */
++ "add", /* SADB_ADD */
++ "delete", /* SADB_DELETE */
++ "get", /* SADB_GET */
++ "acquire", /* SADB_ACQUIRE */
++ "register", /* SADB_REGISTER */
++ "expire", /* SADB_EXPIRE */
++ "flush", /* SADB_FLUSH */
++ "dump", /* SADB_DUMP */
++ "x-promisc", /* SADB_X_PROMISC */
++ "x-pchange", /* SADB_X_PCHANGE */
++ "x-groupsa", /* SADB_X_GRPSA */
++ "x-addflow(eroute)", /* SADB_X_ADDFLOW */
++ "x-delflow(eroute)", /* SADB_X_DELFLOW */
++ "x-debug", /* SADB_X_DEBUG */
++};
++
++const char *
++pfkey_v2_sadb_type_string(int sadb_type)
++{
++ if(sadb_type <= SADB_MAX) {
++ return pfkey_sadb_type_strings[sadb_type];
++ } else {
++ return "unknown-sadb-type";
++ }
++}
++
++
++
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.9 2004/03/08 01:59:08 ken
++ * freeswan.h -> openswan.h
++ *
++ * Revision 1.8 2003/12/10 01:20:19 mcr
++ * NAT-traversal patches to KLIPS.
++ *
++ * Revision 1.7 2002/09/20 05:01:26 rgb
++ * Fixed limit inclusion error in both type and ext string conversion.
++ *
++ * Revision 1.6 2002/04/24 07:55:32 mcr
++ * #include patches and Makefiles for post-reorg compilation.
++ *
++ * Revision 1.5 2002/04/24 07:36:40 mcr
++ * Moved from ./lib/pfkey_v2_debug.c,v
++ *
++ * Revision 1.4 2002/01/29 22:25:36 rgb
++ * Re-add ipsec_kversion.h to keep MALLOC happy.
++ *
++ * Revision 1.3 2002/01/29 01:59:09 mcr
++ * removal of kversions.h - sources that needed it now use ipsec_param.h.
++ * updating of IPv6 structures to match latest in6.h version.
++ * removed dead code from openswan.h that also duplicated kversions.h
++ * code.
++ *
++ * Revision 1.2 2002/01/20 20:34:50 mcr
++ * added pfkey_v2_sadb_type_string to decode sadb_type to string.
++ *
++ * Revision 1.1 2001/11/27 05:30:06 mcr
++ * initial set of debug strings for pfkey debugging.
++ * this will eventually only be included for debug builds.
++ *
++ * Revision 1.1 2001/09/21 04:12:03 mcr
++ * first compilable version.
++ *
++ *
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/libfreeswan/pfkey_v2_ext_bits.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,808 @@
++/*
++ * RFC2367 PF_KEYv2 Key management API message parser
++ * Copyright (C) 1999, 2000, 2001 Richard Guy Briggs.
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ * RCSID $Id$
++ */
++
++/*
++ * Template from klips/net/ipsec/ipsec/ipsec_parse.c.
++ */
++
++char pfkey_v2_ext_bits_c_version[] = "$Id$";
++
++/*
++ * Some ugly stuff to allow consistent debugging code for use in the
++ * kernel and in user space
++*/
++
++#ifdef __KERNEL__
++
++# include <linux/kernel.h> /* for printk */
++
++# include "openswan/ipsec_kversion.h" /* for malloc switch */
++# ifdef MALLOC_SLAB
++# include <linux/slab.h> /* kmalloc() */
++# else /* MALLOC_SLAB */
++# include <linux/malloc.h> /* kmalloc() */
++# endif /* MALLOC_SLAB */
++# include <linux/errno.h> /* error codes */
++# include <linux/types.h> /* size_t */
++# include <linux/interrupt.h> /* mark_bh */
++
++# include <linux/netdevice.h> /* struct device, and other headers */
++# include <linux/etherdevice.h> /* eth_type_trans */
++# include <linux/ip.h> /* struct iphdr */
++# if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
++# include <linux/ipv6.h>
++# endif /* defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) */
++
++#else /* __KERNEL__ */
++
++# include <sys/types.h>
++# include <linux/types.h>
++# include <linux/errno.h>
++#endif
++
++#include <openswan.h>
++#include <pfkeyv2.h>
++#include <pfkey.h>
++
++unsigned int extensions_bitmaps[2/*in/out*/][2/*perm/req*/][SADB_MAX + 1/*ext*/] = {
++
++/* INBOUND EXTENSIONS */
++{
++
++/* PERMITTED IN */
++{
++/* SADB_RESERVED */
++0
++,
++/* SADB_GETSPI */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_ADDRESS_SRC
++| 1<<SADB_EXT_ADDRESS_DST
++| 1<<SADB_EXT_ADDRESS_PROXY
++| 1<<SADB_EXT_SPIRANGE
++,
++/* SADB_UPDATE */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_EXT_LIFETIME_CURRENT
++| 1<<SADB_EXT_LIFETIME_HARD
++| 1<<SADB_EXT_LIFETIME_SOFT
++| 1<<SADB_EXT_ADDRESS_SRC
++| 1<<SADB_EXT_ADDRESS_DST
++| 1<<SADB_EXT_ADDRESS_PROXY
++| 1<<SADB_EXT_KEY_AUTH
++| 1<<SADB_EXT_KEY_ENCRYPT
++| 1<<SADB_EXT_IDENTITY_SRC
++| 1<<SADB_EXT_IDENTITY_DST
++| 1<<SADB_EXT_SENSITIVITY
++| 1<<SADB_X_EXT_NAT_T_SPORT
++| 1<<SADB_X_EXT_NAT_T_DPORT
++,
++/* SADB_ADD */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_EXT_LIFETIME_HARD
++| 1<<SADB_EXT_LIFETIME_SOFT
++| 1<<SADB_EXT_ADDRESS_SRC
++| 1<<SADB_EXT_ADDRESS_DST
++| 1<<SADB_EXT_ADDRESS_PROXY
++| 1<<SADB_EXT_KEY_AUTH
++| 1<<SADB_EXT_KEY_ENCRYPT
++| 1<<SADB_EXT_IDENTITY_SRC
++| 1<<SADB_EXT_IDENTITY_DST
++| 1<<SADB_EXT_SENSITIVITY
++| 1<<SADB_X_EXT_NAT_T_TYPE
++| 1<<SADB_X_EXT_NAT_T_SPORT
++| 1<<SADB_X_EXT_NAT_T_DPORT
++| 1<<SADB_X_EXT_NAT_T_OA
++,
++/* SADB_DELETE */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_EXT_ADDRESS_SRC
++| 1<<SADB_EXT_ADDRESS_DST
++,
++/* SADB_GET */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_EXT_ADDRESS_SRC
++| 1<<SADB_EXT_ADDRESS_DST
++,
++/* SADB_ACQUIRE */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_ADDRESS_SRC
++| 1<<SADB_EXT_ADDRESS_DST
++| 1<<SADB_EXT_ADDRESS_PROXY
++| 1<<SADB_EXT_IDENTITY_SRC
++| 1<<SADB_EXT_IDENTITY_DST
++| 1<<SADB_EXT_SENSITIVITY
++| 1<<SADB_EXT_PROPOSAL
++,
++/* SADB_REGISTER */
++1<<SADB_EXT_RESERVED
++,
++/* SADB_EXPIRE */
++0
++,
++/* SADB_FLUSH */
++1<<SADB_EXT_RESERVED
++,
++/* SADB_DUMP */
++1<<SADB_EXT_RESERVED
++,
++/* SADB_X_PROMISC */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_EXT_LIFETIME_CURRENT
++| 1<<SADB_EXT_LIFETIME_HARD
++| 1<<SADB_EXT_LIFETIME_SOFT
++| 1<<SADB_EXT_ADDRESS_SRC
++| 1<<SADB_EXT_ADDRESS_DST
++| 1<<SADB_EXT_ADDRESS_PROXY
++| 1<<SADB_EXT_KEY_AUTH
++| 1<<SADB_EXT_KEY_ENCRYPT
++| 1<<SADB_EXT_IDENTITY_SRC
++| 1<<SADB_EXT_IDENTITY_DST
++| 1<<SADB_EXT_SENSITIVITY
++| 1<<SADB_EXT_PROPOSAL
++| 1<<SADB_EXT_SUPPORTED_AUTH
++| 1<<SADB_EXT_SUPPORTED_ENCRYPT
++| 1<<SADB_EXT_SPIRANGE
++| 1<<SADB_X_EXT_KMPRIVATE
++| 1<<SADB_X_EXT_SATYPE2
++| 1<<SADB_X_EXT_SA2
++| 1<<SADB_X_EXT_ADDRESS_DST2
++,
++/* SADB_X_PCHANGE */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_EXT_LIFETIME_CURRENT
++| 1<<SADB_EXT_LIFETIME_HARD
++| 1<<SADB_EXT_LIFETIME_SOFT
++| 1<<SADB_EXT_ADDRESS_SRC
++| 1<<SADB_EXT_ADDRESS_DST
++| 1<<SADB_EXT_ADDRESS_PROXY
++| 1<<SADB_EXT_KEY_AUTH
++| 1<<SADB_EXT_KEY_ENCRYPT
++| 1<<SADB_EXT_IDENTITY_SRC
++| 1<<SADB_EXT_IDENTITY_DST
++| 1<<SADB_EXT_SENSITIVITY
++| 1<<SADB_EXT_PROPOSAL
++| 1<<SADB_EXT_SUPPORTED_AUTH
++| 1<<SADB_EXT_SUPPORTED_ENCRYPT
++| 1<<SADB_EXT_SPIRANGE
++| 1<<SADB_X_EXT_KMPRIVATE
++| 1<<SADB_X_EXT_SATYPE2
++| 1<<SADB_X_EXT_SA2
++| 1<<SADB_X_EXT_ADDRESS_DST2
++,
++/* SADB_X_GRPSA */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_EXT_ADDRESS_DST
++| 1<<SADB_X_EXT_SATYPE2
++| 1<<SADB_X_EXT_SA2
++| 1<<SADB_X_EXT_ADDRESS_DST2
++,
++/* SADB_X_ADDFLOW */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_EXT_ADDRESS_SRC
++| 1<<SADB_EXT_ADDRESS_DST
++| 1<<SADB_X_EXT_ADDRESS_SRC_FLOW
++| 1<<SADB_X_EXT_ADDRESS_DST_FLOW
++| 1<<SADB_X_EXT_ADDRESS_SRC_MASK
++| 1<<SADB_X_EXT_ADDRESS_DST_MASK
++| 1<<SADB_EXT_IDENTITY_SRC
++| 1<<SADB_EXT_IDENTITY_DST
++| 1<<SADB_X_EXT_PROTOCOL
++,
++/* SADB_X_DELFLOW */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_X_EXT_ADDRESS_SRC_FLOW
++| 1<<SADB_X_EXT_ADDRESS_DST_FLOW
++| 1<<SADB_X_EXT_ADDRESS_SRC_MASK
++| 1<<SADB_X_EXT_ADDRESS_DST_MASK
++| 1<<SADB_EXT_IDENTITY_SRC
++| 1<<SADB_EXT_IDENTITY_DST
++| 1<<SADB_X_EXT_PROTOCOL
++,
++/* SADB_X_DEBUG */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_X_EXT_DEBUG
++,
++/* SADB_X_NAT_T_NEW_MAPPING */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_EXT_ADDRESS_SRC
++| 1<<SADB_EXT_ADDRESS_DST
++| 1<<SADB_X_EXT_NAT_T_SPORT
++| 1<<SADB_X_EXT_NAT_T_DPORT
++},
++
++/* REQUIRED IN */
++{
++/* SADB_RESERVED */
++0
++,
++/* SADB_GETSPI */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_ADDRESS_SRC
++| 1<<SADB_EXT_ADDRESS_DST
++| 1<<SADB_EXT_SPIRANGE
++,
++/* SADB_UPDATE */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_EXT_ADDRESS_SRC
++| 1<<SADB_EXT_ADDRESS_DST
++/*| 1<<SADB_EXT_KEY_AUTH*/
++/*| 1<<SADB_EXT_KEY_ENCRYPT*/
++,
++/* SADB_ADD */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_EXT_ADDRESS_SRC
++| 1<<SADB_EXT_ADDRESS_DST
++/*| 1<<SADB_EXT_KEY_AUTH*/
++/*| 1<<SADB_EXT_KEY_ENCRYPT*/
++,
++/* SADB_DELETE */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_EXT_ADDRESS_SRC
++| 1<<SADB_EXT_ADDRESS_DST
++,
++/* SADB_GET */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_EXT_ADDRESS_SRC
++| 1<<SADB_EXT_ADDRESS_DST
++,
++/* SADB_ACQUIRE */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_ADDRESS_SRC
++| 1<<SADB_EXT_ADDRESS_DST
++| 1<<SADB_EXT_PROPOSAL
++,
++/* SADB_REGISTER */
++1<<SADB_EXT_RESERVED
++,
++/* SADB_EXPIRE */
++0
++,
++/* SADB_FLUSH */
++1<<SADB_EXT_RESERVED
++,
++/* SADB_DUMP */
++1<<SADB_EXT_RESERVED
++,
++/* SADB_X_PROMISC */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_EXT_LIFETIME_CURRENT
++| 1<<SADB_EXT_LIFETIME_HARD
++| 1<<SADB_EXT_LIFETIME_SOFT
++| 1<<SADB_EXT_ADDRESS_SRC
++| 1<<SADB_EXT_ADDRESS_DST
++| 1<<SADB_EXT_ADDRESS_PROXY
++| 1<<SADB_EXT_KEY_AUTH
++| 1<<SADB_EXT_KEY_ENCRYPT
++| 1<<SADB_EXT_IDENTITY_SRC
++| 1<<SADB_EXT_IDENTITY_DST
++| 1<<SADB_EXT_SENSITIVITY
++| 1<<SADB_EXT_PROPOSAL
++| 1<<SADB_EXT_SUPPORTED_AUTH
++| 1<<SADB_EXT_SUPPORTED_ENCRYPT
++| 1<<SADB_EXT_SPIRANGE
++| 1<<SADB_X_EXT_KMPRIVATE
++| 1<<SADB_X_EXT_SATYPE2
++| 1<<SADB_X_EXT_SA2
++| 1<<SADB_X_EXT_ADDRESS_DST2
++,
++/* SADB_X_PCHANGE */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_EXT_LIFETIME_CURRENT
++| 1<<SADB_EXT_LIFETIME_HARD
++| 1<<SADB_EXT_LIFETIME_SOFT
++| 1<<SADB_EXT_ADDRESS_SRC
++| 1<<SADB_EXT_ADDRESS_DST
++| 1<<SADB_EXT_ADDRESS_PROXY
++| 1<<SADB_EXT_KEY_AUTH
++| 1<<SADB_EXT_KEY_ENCRYPT
++| 1<<SADB_EXT_IDENTITY_SRC
++| 1<<SADB_EXT_IDENTITY_DST
++| 1<<SADB_EXT_SENSITIVITY
++| 1<<SADB_EXT_PROPOSAL
++| 1<<SADB_EXT_SUPPORTED_AUTH
++| 1<<SADB_EXT_SUPPORTED_ENCRYPT
++| 1<<SADB_EXT_SPIRANGE
++| 1<<SADB_X_EXT_KMPRIVATE
++| 1<<SADB_X_EXT_SATYPE2
++| 1<<SADB_X_EXT_SA2
++| 1<<SADB_X_EXT_ADDRESS_DST2
++,
++/* SADB_X_GRPSA */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_EXT_ADDRESS_DST
++/*| 1<<SADB_X_EXT_SATYPE2*/
++/*| 1<<SADB_X_EXT_SA2*/
++/*| 1<<SADB_X_EXT_ADDRESS_DST2*/
++,
++/* SADB_X_ADDFLOW */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_EXT_ADDRESS_DST
++| 1<<SADB_X_EXT_ADDRESS_SRC_FLOW
++| 1<<SADB_X_EXT_ADDRESS_DST_FLOW
++| 1<<SADB_X_EXT_ADDRESS_SRC_MASK
++| 1<<SADB_X_EXT_ADDRESS_DST_MASK
++,
++/* SADB_X_DELFLOW */
++1<<SADB_EXT_RESERVED
++/*| 1<<SADB_EXT_SA*/
++#if 0 /* SADB_X_CLREROUTE doesn't need all these... */
++| 1<<SADB_X_EXT_ADDRESS_SRC_FLOW
++| 1<<SADB_X_EXT_ADDRESS_DST_FLOW
++| 1<<SADB_X_EXT_ADDRESS_SRC_MASK
++| 1<<SADB_X_EXT_ADDRESS_DST_MASK
++#endif
++,
++/* SADB_X_DEBUG */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_X_EXT_DEBUG
++,
++/* SADB_X_NAT_T_NEW_MAPPING */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_EXT_ADDRESS_SRC
++| 1<<SADB_EXT_ADDRESS_DST
++| 1<<SADB_X_EXT_NAT_T_SPORT
++| 1<<SADB_X_EXT_NAT_T_DPORT
++}
++
++},
++
++/* OUTBOUND EXTENSIONS */
++{
++
++/* PERMITTED OUT */
++{
++/* SADB_RESERVED */
++0
++,
++/* SADB_GETSPI */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_EXT_ADDRESS_SRC
++| 1<<SADB_EXT_ADDRESS_DST
++,
++/* SADB_UPDATE */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_EXT_LIFETIME_CURRENT
++| 1<<SADB_EXT_LIFETIME_HARD
++| 1<<SADB_EXT_LIFETIME_SOFT
++| 1<<SADB_EXT_ADDRESS_SRC
++| 1<<SADB_EXT_ADDRESS_DST
++| 1<<SADB_EXT_ADDRESS_PROXY
++| 1<<SADB_EXT_IDENTITY_SRC
++| 1<<SADB_EXT_IDENTITY_DST
++| 1<<SADB_EXT_SENSITIVITY
++| 1<<SADB_X_EXT_NAT_T_SPORT
++| 1<<SADB_X_EXT_NAT_T_DPORT
++,
++/* SADB_ADD */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_EXT_LIFETIME_HARD
++| 1<<SADB_EXT_LIFETIME_SOFT
++| 1<<SADB_EXT_ADDRESS_SRC
++| 1<<SADB_EXT_ADDRESS_DST
++| 1<<SADB_EXT_IDENTITY_SRC
++| 1<<SADB_EXT_IDENTITY_DST
++| 1<<SADB_EXT_SENSITIVITY
++| 1<<SADB_X_EXT_NAT_T_TYPE
++| 1<<SADB_X_EXT_NAT_T_SPORT
++| 1<<SADB_X_EXT_NAT_T_DPORT
++| 1<<SADB_X_EXT_NAT_T_OA
++,
++/* SADB_DELETE */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_EXT_ADDRESS_SRC
++| 1<<SADB_EXT_ADDRESS_DST
++,
++/* SADB_GET */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_EXT_LIFETIME_CURRENT
++| 1<<SADB_EXT_LIFETIME_HARD
++| 1<<SADB_EXT_LIFETIME_SOFT
++| 1<<SADB_EXT_ADDRESS_SRC
++| 1<<SADB_EXT_ADDRESS_DST
++| 1<<SADB_EXT_ADDRESS_PROXY
++| 1<<SADB_EXT_KEY_AUTH
++| 1<<SADB_EXT_KEY_ENCRYPT
++| 1<<SADB_EXT_IDENTITY_SRC
++| 1<<SADB_EXT_IDENTITY_DST
++| 1<<SADB_EXT_SENSITIVITY
++| 1<<SADB_X_EXT_NAT_T_TYPE
++| 1<<SADB_X_EXT_NAT_T_SPORT
++| 1<<SADB_X_EXT_NAT_T_DPORT
++| 1<<SADB_X_EXT_NAT_T_OA
++,
++/* SADB_ACQUIRE */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_ADDRESS_SRC
++| 1<<SADB_EXT_ADDRESS_DST
++| 1<<SADB_EXT_ADDRESS_PROXY
++| 1<<SADB_EXT_IDENTITY_SRC
++| 1<<SADB_EXT_IDENTITY_DST
++| 1<<SADB_EXT_SENSITIVITY
++| 1<<SADB_EXT_PROPOSAL
++,
++/* SADB_REGISTER */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SUPPORTED_AUTH
++| 1<<SADB_EXT_SUPPORTED_ENCRYPT
++,
++/* SADB_EXPIRE */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_EXT_LIFETIME_CURRENT
++| 1<<SADB_EXT_LIFETIME_HARD
++| 1<<SADB_EXT_LIFETIME_SOFT
++| 1<<SADB_EXT_ADDRESS_SRC
++| 1<<SADB_EXT_ADDRESS_DST
++,
++/* SADB_FLUSH */
++1<<SADB_EXT_RESERVED
++,
++/* SADB_DUMP */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_EXT_LIFETIME_CURRENT
++| 1<<SADB_EXT_LIFETIME_HARD
++| 1<<SADB_EXT_LIFETIME_SOFT
++| 1<<SADB_EXT_ADDRESS_SRC
++| 1<<SADB_EXT_ADDRESS_DST
++| 1<<SADB_EXT_ADDRESS_PROXY
++| 1<<SADB_EXT_KEY_AUTH
++| 1<<SADB_EXT_KEY_ENCRYPT
++| 1<<SADB_EXT_IDENTITY_SRC
++| 1<<SADB_EXT_IDENTITY_DST
++| 1<<SADB_EXT_SENSITIVITY
++| 1<<SADB_X_EXT_NAT_T_TYPE
++| 1<<SADB_X_EXT_NAT_T_SPORT
++| 1<<SADB_X_EXT_NAT_T_DPORT
++| 1<<SADB_X_EXT_NAT_T_OA
++,
++/* SADB_X_PROMISC */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_EXT_LIFETIME_CURRENT
++| 1<<SADB_EXT_LIFETIME_HARD
++| 1<<SADB_EXT_LIFETIME_SOFT
++| 1<<SADB_EXT_ADDRESS_SRC
++| 1<<SADB_EXT_ADDRESS_DST
++| 1<<SADB_EXT_ADDRESS_PROXY
++| 1<<SADB_EXT_KEY_AUTH
++| 1<<SADB_EXT_KEY_ENCRYPT
++| 1<<SADB_EXT_IDENTITY_SRC
++| 1<<SADB_EXT_IDENTITY_DST
++| 1<<SADB_EXT_SENSITIVITY
++| 1<<SADB_EXT_PROPOSAL
++| 1<<SADB_EXT_SUPPORTED_AUTH
++| 1<<SADB_EXT_SUPPORTED_ENCRYPT
++| 1<<SADB_EXT_SPIRANGE
++| 1<<SADB_X_EXT_KMPRIVATE
++| 1<<SADB_X_EXT_SATYPE2
++| 1<<SADB_X_EXT_SA2
++| 1<<SADB_X_EXT_ADDRESS_DST2
++,
++/* SADB_X_PCHANGE */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_EXT_LIFETIME_CURRENT
++| 1<<SADB_EXT_LIFETIME_HARD
++| 1<<SADB_EXT_LIFETIME_SOFT
++| 1<<SADB_EXT_ADDRESS_SRC
++| 1<<SADB_EXT_ADDRESS_DST
++| 1<<SADB_EXT_ADDRESS_PROXY
++| 1<<SADB_EXT_KEY_AUTH
++| 1<<SADB_EXT_KEY_ENCRYPT
++| 1<<SADB_EXT_IDENTITY_SRC
++| 1<<SADB_EXT_IDENTITY_DST
++| 1<<SADB_EXT_SENSITIVITY
++| 1<<SADB_EXT_PROPOSAL
++| 1<<SADB_EXT_SUPPORTED_AUTH
++| 1<<SADB_EXT_SUPPORTED_ENCRYPT
++| 1<<SADB_EXT_SPIRANGE
++| 1<<SADB_X_EXT_KMPRIVATE
++| 1<<SADB_X_EXT_SATYPE2
++| 1<<SADB_X_EXT_SA2
++| 1<<SADB_X_EXT_ADDRESS_DST2
++,
++/* SADB_X_GRPSA */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_EXT_ADDRESS_DST
++| 1<<SADB_X_EXT_SATYPE2
++| 1<<SADB_X_EXT_SA2
++| 1<<SADB_X_EXT_ADDRESS_DST2
++,
++/* SADB_X_ADDFLOW */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_EXT_ADDRESS_SRC
++| 1<<SADB_EXT_ADDRESS_DST
++| 1<<SADB_X_EXT_ADDRESS_SRC_FLOW
++| 1<<SADB_X_EXT_ADDRESS_DST_FLOW
++| 1<<SADB_X_EXT_ADDRESS_SRC_MASK
++| 1<<SADB_X_EXT_ADDRESS_DST_MASK
++| 1<<SADB_X_EXT_PROTOCOL
++,
++/* SADB_X_DELFLOW */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_X_EXT_ADDRESS_SRC_FLOW
++| 1<<SADB_X_EXT_ADDRESS_DST_FLOW
++| 1<<SADB_X_EXT_ADDRESS_SRC_MASK
++| 1<<SADB_X_EXT_ADDRESS_DST_MASK
++| 1<<SADB_X_EXT_PROTOCOL
++,
++/* SADB_X_DEBUG */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_X_EXT_DEBUG
++,
++/* SADB_X_NAT_T_NEW_MAPPING */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_EXT_ADDRESS_SRC
++| 1<<SADB_EXT_ADDRESS_DST
++| 1<<SADB_X_EXT_NAT_T_SPORT
++| 1<<SADB_X_EXT_NAT_T_DPORT
++},
++
++/* REQUIRED OUT */
++{
++/* SADB_RESERVED */
++0
++,
++/* SADB_GETSPI */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_EXT_ADDRESS_SRC
++| 1<<SADB_EXT_ADDRESS_DST
++,
++/* SADB_UPDATE */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_EXT_ADDRESS_SRC
++| 1<<SADB_EXT_ADDRESS_DST
++,
++/* SADB_ADD */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_EXT_ADDRESS_SRC
++| 1<<SADB_EXT_ADDRESS_DST
++,
++/* SADB_DELETE */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_EXT_ADDRESS_SRC
++| 1<<SADB_EXT_ADDRESS_DST
++,
++/* SADB_GET */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_EXT_ADDRESS_SRC
++| 1<<SADB_EXT_ADDRESS_DST
++/* | 1<<SADB_EXT_KEY_AUTH */
++/* | 1<<SADB_EXT_KEY_ENCRYPT */
++,
++/* SADB_ACQUIRE */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_ADDRESS_SRC
++| 1<<SADB_EXT_ADDRESS_DST
++| 1<<SADB_EXT_PROPOSAL
++,
++/* SADB_REGISTER */
++1<<SADB_EXT_RESERVED
++/* | 1<<SADB_EXT_SUPPORTED_AUTH
++ | 1<<SADB_EXT_SUPPORTED_ENCRYPT */
++,
++/* SADB_EXPIRE */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_EXT_LIFETIME_CURRENT
++/* | 1<<SADB_EXT_LIFETIME_HARD
++ | 1<<SADB_EXT_LIFETIME_SOFT */
++| 1<<SADB_EXT_ADDRESS_SRC
++| 1<<SADB_EXT_ADDRESS_DST
++,
++/* SADB_FLUSH */
++1<<SADB_EXT_RESERVED
++,
++/* SADB_DUMP */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_EXT_ADDRESS_SRC
++| 1<<SADB_EXT_ADDRESS_DST
++| 1<<SADB_EXT_KEY_AUTH
++| 1<<SADB_EXT_KEY_ENCRYPT
++,
++/* SADB_X_PROMISC */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_EXT_LIFETIME_CURRENT
++| 1<<SADB_EXT_LIFETIME_HARD
++| 1<<SADB_EXT_LIFETIME_SOFT
++| 1<<SADB_EXT_ADDRESS_SRC
++| 1<<SADB_EXT_ADDRESS_DST
++| 1<<SADB_EXT_ADDRESS_PROXY
++| 1<<SADB_EXT_KEY_AUTH
++| 1<<SADB_EXT_KEY_ENCRYPT
++| 1<<SADB_EXT_IDENTITY_SRC
++| 1<<SADB_EXT_IDENTITY_DST
++| 1<<SADB_EXT_SENSITIVITY
++| 1<<SADB_EXT_PROPOSAL
++| 1<<SADB_EXT_SUPPORTED_AUTH
++| 1<<SADB_EXT_SUPPORTED_ENCRYPT
++| 1<<SADB_EXT_SPIRANGE
++| 1<<SADB_X_EXT_KMPRIVATE
++| 1<<SADB_X_EXT_SATYPE2
++| 1<<SADB_X_EXT_SA2
++| 1<<SADB_X_EXT_ADDRESS_DST2
++,
++/* SADB_X_PCHANGE */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_EXT_LIFETIME_CURRENT
++| 1<<SADB_EXT_LIFETIME_HARD
++| 1<<SADB_EXT_LIFETIME_SOFT
++| 1<<SADB_EXT_ADDRESS_SRC
++| 1<<SADB_EXT_ADDRESS_DST
++| 1<<SADB_EXT_ADDRESS_PROXY
++| 1<<SADB_EXT_KEY_AUTH
++| 1<<SADB_EXT_KEY_ENCRYPT
++| 1<<SADB_EXT_IDENTITY_SRC
++| 1<<SADB_EXT_IDENTITY_DST
++| 1<<SADB_EXT_SENSITIVITY
++| 1<<SADB_EXT_PROPOSAL
++| 1<<SADB_EXT_SUPPORTED_AUTH
++| 1<<SADB_EXT_SUPPORTED_ENCRYPT
++| 1<<SADB_EXT_SPIRANGE
++| 1<<SADB_X_EXT_KMPRIVATE
++| 1<<SADB_X_EXT_SATYPE2
++| 1<<SADB_X_EXT_SA2
++| 1<<SADB_X_EXT_ADDRESS_DST2
++,
++/* SADB_X_GRPSA */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_EXT_ADDRESS_DST
++,
++/* SADB_X_ADDFLOW */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_EXT_ADDRESS_DST
++| 1<<SADB_X_EXT_ADDRESS_SRC_FLOW
++| 1<<SADB_X_EXT_ADDRESS_DST_FLOW
++| 1<<SADB_X_EXT_ADDRESS_SRC_MASK
++| 1<<SADB_X_EXT_ADDRESS_DST_MASK
++,
++/* SADB_X_DELFLOW */
++1<<SADB_EXT_RESERVED
++/*| 1<<SADB_EXT_SA*/
++| 1<<SADB_X_EXT_ADDRESS_SRC_FLOW
++| 1<<SADB_X_EXT_ADDRESS_DST_FLOW
++| 1<<SADB_X_EXT_ADDRESS_SRC_MASK
++| 1<<SADB_X_EXT_ADDRESS_DST_MASK
++,
++/* SADB_X_DEBUG */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_X_EXT_DEBUG
++,
++/* SADB_X_NAT_T_NEW_MAPPING */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_EXT_ADDRESS_SRC
++| 1<<SADB_EXT_ADDRESS_DST
++| 1<<SADB_X_EXT_NAT_T_SPORT
++| 1<<SADB_X_EXT_NAT_T_DPORT
++}
++}
++};
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.20 2004/03/08 01:59:08 ken
++ * freeswan.h -> openswan.h
++ *
++ * Revision 1.19 2003/12/22 21:38:13 mcr
++ * removed extraenous #endif.
++ *
++ * Revision 1.18 2003/12/22 19:34:41 mcr
++ * added 0.6c NAT-T patch.
++ *
++ * Revision 1.17 2003/12/10 01:20:19 mcr
++ * NAT-traversal patches to KLIPS.
++ *
++ * Revision 1.16 2003/10/31 02:27:12 mcr
++ * pulled up port-selector patches and sa_id elimination.
++ *
++ * Revision 1.15.30.1 2003/09/21 13:59:44 mcr
++ * pre-liminary X.509 patch - does not yet pass tests.
++ *
++ * Revision 1.15 2002/04/24 07:55:32 mcr
++ * #include patches and Makefiles for post-reorg compilation.
++ *
++ * Revision 1.14 2002/04/24 07:36:40 mcr
++ * Moved from ./lib/pfkey_v2_ext_bits.c,v
++ *
++ * Revision 1.13 2002/01/29 22:25:36 rgb
++ * Re-add ipsec_kversion.h to keep MALLOC happy.
++ *
++ * Revision 1.12 2002/01/29 01:59:10 mcr
++ * removal of kversions.h - sources that needed it now use ipsec_param.h.
++ * updating of IPv6 structures to match latest in6.h version.
++ * removed dead code from openswan.h that also duplicated kversions.h
++ * code.
++ *
++ * Revision 1.11 2001/10/18 04:45:24 rgb
++ * 2.4.9 kernel deprecates linux/malloc.h in favour of linux/slab.h,
++ * lib/openswan.h version macros moved to lib/kversions.h.
++ * Other compiler directive cleanups.
++ *
++ * Revision 1.10 2001/09/08 21:13:35 rgb
++ * Added pfkey ident extension support for ISAKMPd. (NetCelo)
++ *
++ * Revision 1.9 2001/06/14 19:35:16 rgb
++ * Update copyright date.
++ *
++ * Revision 1.8 2001/03/26 23:07:36 rgb
++ * Remove requirement for auth and enc key from UPDATE.
++ *
++ * Revision 1.7 2000/09/12 22:35:37 rgb
++ * Restructured to remove unused extensions from CLEARFLOW messages.
++ *
++ * Revision 1.6 2000/09/09 06:39:01 rgb
++ * Added comments for clarity.
++ *
++ * Revision 1.5 2000/06/02 22:54:14 rgb
++ * Added Gerhard Gessler's struct sockaddr_storage mods for IPv6 support.
++ *
++ * Revision 1.4 2000/01/21 06:27:56 rgb
++ * Added address cases for eroute flows.
++ * Added comments for each message type.
++ * Added klipsdebug switching capability.
++ * Fixed GRPSA bitfields.
++ *
++ * Revision 1.3 1999/12/01 22:20:27 rgb
++ * Remove requirement for a proxy address in an incoming getspi message.
++ *
++ * Revision 1.2 1999/11/27 11:57:06 rgb
++ * Consolidated the 4 1-d extension bitmap arrays into one 4-d array.
++ * Add CVS log entry to bottom of file.
++ * Cleaned out unused bits.
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/libfreeswan/pfkey_v2_parse.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,1820 @@
++/*
++ * RFC2367 PF_KEYv2 Key management API message parser
++ * Copyright (C) 1999, 2000, 2001 Richard Guy Briggs.
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ * RCSID $Id$
++ */
++
++/*
++ * Template from klips/net/ipsec/ipsec/ipsec_parser.c.
++ */
++
++char pfkey_v2_parse_c_version[] = "$Id$";
++
++/*
++ * Some ugly stuff to allow consistent debugging code for use in the
++ * kernel and in user space
++*/
++
++#ifdef __KERNEL__
++
++# include <linux/kernel.h> /* for printk */
++
++#include "openswan/ipsec_kversion.h" /* for malloc switch */
++
++# ifdef MALLOC_SLAB
++# include <linux/slab.h> /* kmalloc() */
++# else /* MALLOC_SLAB */
++# include <linux/malloc.h> /* kmalloc() */
++# endif /* MALLOC_SLAB */
++# include <linux/errno.h> /* error codes */
++# include <linux/types.h> /* size_t */
++# include <linux/interrupt.h> /* mark_bh */
++
++# include <linux/netdevice.h> /* struct device, and other headers */
++# include <linux/etherdevice.h> /* eth_type_trans */
++# include <linux/ip.h> /* struct iphdr */
++# if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
++# include <linux/ipv6.h> /* struct ipv6hdr */
++# endif /* if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) */
++extern int debug_pfkey;
++
++# include <openswan.h>
++
++#include "openswan/ipsec_encap.h"
++
++#else /* __KERNEL__ */
++
++# include <sys/types.h>
++# include <linux/types.h>
++# include <linux/errno.h>
++
++# include <openswan.h>
++# include "constants.h"
++# include "programs/pluto/defs.h" /* for PRINTF_LIKE */
++
++#endif /* __KERNEL__ */
++
++
++#include <pfkeyv2.h>
++#include <pfkey.h>
++
++#include "openswan/ipsec_sa.h" /* IPSEC_SAREF_NULL, IPSEC_SA_REF_TABLE_IDX_WIDTH */
++
++/*
++ * how to handle debugging for pfkey.
++ */
++#include <openswan/pfkey_debug.h>
++
++unsigned int pfkey_lib_debug = PF_KEY_DEBUG_PARSE_NONE;
++void (*pfkey_debug_func)(const char *message, ...) PRINTF_LIKE(1);
++void (*pfkey_error_func)(const char *message, ...) PRINTF_LIKE(1);
++
++
++#define SENDERR(_x) do { error = -(_x); goto errlab; } while (0)
++
++struct satype_tbl {
++ uint8_t proto;
++ uint8_t satype;
++ char* name;
++} static satype_tbl[] = {
++#ifdef __KERNEL__
++ { IPPROTO_ESP, SADB_SATYPE_ESP, "ESP" },
++ { IPPROTO_AH, SADB_SATYPE_AH, "AH" },
++ { IPPROTO_IPIP, SADB_X_SATYPE_IPIP, "IPIP" },
++#ifdef CONFIG_IPSEC_IPCOMP
++ { IPPROTO_COMP, SADB_X_SATYPE_COMP, "COMP" },
++#endif /* CONFIG_IPSEC_IPCOMP */
++ { IPPROTO_INT, SADB_X_SATYPE_INT, "INT" },
++#else /* __KERNEL__ */
++ { SA_ESP, SADB_SATYPE_ESP, "ESP" },
++ { SA_AH, SADB_SATYPE_AH, "AH" },
++ { SA_IPIP, SADB_X_SATYPE_IPIP, "IPIP" },
++ { SA_COMP, SADB_X_SATYPE_COMP, "COMP" },
++ { SA_INT, SADB_X_SATYPE_INT, "INT" },
++#endif /* __KERNEL__ */
++ { 0, 0, "UNKNOWN" }
++};
++
++uint8_t
++satype2proto(uint8_t satype)
++{
++ int i =0;
++
++ while(satype_tbl[i].satype != satype && satype_tbl[i].satype != 0) {
++ i++;
++ }
++ return satype_tbl[i].proto;
++}
++
++uint8_t
++proto2satype(uint8_t proto)
++{
++ int i = 0;
++
++ while(satype_tbl[i].proto != proto && satype_tbl[i].proto != 0) {
++ i++;
++ }
++ return satype_tbl[i].satype;
++}
++
++char*
++satype2name(uint8_t satype)
++{
++ int i = 0;
++
++ while(satype_tbl[i].satype != satype && satype_tbl[i].satype != 0) {
++ i++;
++ }
++ return satype_tbl[i].name;
++}
++
++char*
++proto2name(uint8_t proto)
++{
++ int i = 0;
++
++ while(satype_tbl[i].proto != proto && satype_tbl[i].proto != 0) {
++ i++;
++ }
++ return satype_tbl[i].name;
++}
++
++/* Default extension parsers taken from the KLIPS code */
++
++DEBUG_NO_STATIC int
++pfkey_sa_parse(struct sadb_ext *pfkey_ext)
++{
++ int error = 0;
++ struct sadb_sa *pfkey_sa = (struct sadb_sa *)pfkey_ext;
++#if 0
++ struct sadb_sa sav2;
++#endif
++
++ /* sanity checks... */
++ if(!pfkey_sa) {
++ ERROR("pfkey_sa_parse: "
++ "NULL pointer passed in.\n");
++ SENDERR(EINVAL);
++ }
++
++#if 0
++ /* check if this structure is short, and if so, fix it up.
++ * XXX this is NOT the way to do things.
++ */
++ if(pfkey_sa->sadb_sa_len == sizeof(struct sadb_sa_v1)/IPSEC_PFKEYv2_ALIGN) {
++
++ /* yes, so clear out a temporary structure, and copy first */
++ memset(&sav2, 0, sizeof(sav2));
++ memcpy(&sav2, pfkey_sa, sizeof(struct sadb_sa_v1));
++ sav2.sadb_x_sa_ref=-1;
++ sav2.sadb_sa_len = sizeof(struct sadb_sa) / IPSEC_PFKEYv2_ALIGN;
++
++ pfkey_sa = &sav2;
++ }
++#endif
++
++
++ if(pfkey_sa->sadb_sa_len != sizeof(struct sadb_sa) / IPSEC_PFKEYv2_ALIGN) {
++ ERROR(
++ "pfkey_sa_parse: "
++ "length wrong pfkey_sa->sadb_sa_len=%d sizeof(struct sadb_sa)=%d.\n",
++ pfkey_sa->sadb_sa_len,
++ (int)sizeof(struct sadb_sa));
++ SENDERR(EINVAL);
++ }
++
++ if(pfkey_sa->sadb_sa_encrypt > SADB_EALG_MAX) {
++ ERROR(
++ "pfkey_sa_parse: "
++ "pfkey_sa->sadb_sa_encrypt=%d > SADB_EALG_MAX=%d.\n",
++ pfkey_sa->sadb_sa_encrypt,
++ SADB_EALG_MAX);
++ SENDERR(EINVAL);
++ }
++
++ if(pfkey_sa->sadb_sa_auth > SADB_AALG_MAX) {
++ ERROR(
++ "pfkey_sa_parse: "
++ "pfkey_sa->sadb_sa_auth=%d > SADB_AALG_MAX=%d.\n",
++ pfkey_sa->sadb_sa_auth,
++ SADB_AALG_MAX);
++ SENDERR(EINVAL);
++ }
++
++ if(pfkey_sa->sadb_sa_state > SADB_SASTATE_MAX) {
++ ERROR(
++ "pfkey_sa_parse: "
++ "state=%d exceeds MAX=%d.\n",
++ pfkey_sa->sadb_sa_state,
++ SADB_SASTATE_MAX);
++ SENDERR(EINVAL);
++ }
++
++ if(pfkey_sa->sadb_sa_state == SADB_SASTATE_DEAD) {
++ ERROR(
++ "pfkey_sa_parse: "
++ "state=%d is DEAD=%d.\n",
++ pfkey_sa->sadb_sa_state,
++ SADB_SASTATE_DEAD);
++ SENDERR(EINVAL);
++ }
++
++ if(pfkey_sa->sadb_sa_replay > 64) {
++ ERROR(
++ "pfkey_sa_parse: "
++ "replay window size: %d -- must be 0 <= size <= 64\n",
++ pfkey_sa->sadb_sa_replay);
++ SENDERR(EINVAL);
++ }
++
++ if(! ((pfkey_sa->sadb_sa_exttype == SADB_EXT_SA) ||
++ (pfkey_sa->sadb_sa_exttype == SADB_X_EXT_SA2)))
++ {
++ ERROR(
++ "pfkey_sa_parse: "
++ "unknown exttype=%d, expecting SADB_EXT_SA=%d or SADB_X_EXT_SA2=%d.\n",
++ pfkey_sa->sadb_sa_exttype,
++ SADB_EXT_SA,
++ SADB_X_EXT_SA2);
++ SENDERR(EINVAL);
++ }
++
++ if((IPSEC_SAREF_NULL != pfkey_sa->sadb_x_sa_ref) && (pfkey_sa->sadb_x_sa_ref >= (1 << IPSEC_SA_REF_TABLE_IDX_WIDTH))) {
++ ERROR(
++ "pfkey_sa_parse: "
++ "SAref=%d must be (SAref == IPSEC_SAREF_NULL(%d) || SAref < IPSEC_SA_REF_TABLE_NUM_ENTRIES(%d)).\n",
++ pfkey_sa->sadb_x_sa_ref,
++ IPSEC_SAREF_NULL,
++ IPSEC_SA_REF_TABLE_NUM_ENTRIES);
++ SENDERR(EINVAL);
++ }
++
++ DEBUGGING(PF_KEY_DEBUG_PARSE_STRUCT,
++ "pfkey_sa_parse: "
++ "successfully found len=%d exttype=%d(%s) spi=%08lx replay=%d state=%d auth=%d encrypt=%d flags=%d ref=%d.\n",
++ pfkey_sa->sadb_sa_len,
++ pfkey_sa->sadb_sa_exttype,
++ pfkey_v2_sadb_ext_string(pfkey_sa->sadb_sa_exttype),
++ (long unsigned int)ntohl(pfkey_sa->sadb_sa_spi),
++ pfkey_sa->sadb_sa_replay,
++ pfkey_sa->sadb_sa_state,
++ pfkey_sa->sadb_sa_auth,
++ pfkey_sa->sadb_sa_encrypt,
++ pfkey_sa->sadb_sa_flags,
++ pfkey_sa->sadb_x_sa_ref);
++
++ errlab:
++ return error;
++}
++
++DEBUG_NO_STATIC int
++pfkey_lifetime_parse(struct sadb_ext *pfkey_ext)
++{
++ int error = 0;
++ struct sadb_lifetime *pfkey_lifetime = (struct sadb_lifetime *)pfkey_ext;
++
++ DEBUGGING(PF_KEY_DEBUG_PARSE_FLOW,
++ "pfkey_lifetime_parse:enter\n");
++ /* sanity checks... */
++ if(!pfkey_lifetime) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_lifetime_parse: "
++ "NULL pointer passed in.\n");
++ SENDERR(EINVAL);
++ }
++
++ if(pfkey_lifetime->sadb_lifetime_len !=
++ sizeof(struct sadb_lifetime) / IPSEC_PFKEYv2_ALIGN) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_lifetime_parse: "
++ "length wrong pfkey_lifetime->sadb_lifetime_len=%d sizeof(struct sadb_lifetime)=%d.\n",
++ pfkey_lifetime->sadb_lifetime_len,
++ (int)sizeof(struct sadb_lifetime));
++ SENDERR(EINVAL);
++ }
++
++ if((pfkey_lifetime->sadb_lifetime_exttype != SADB_EXT_LIFETIME_HARD) &&
++ (pfkey_lifetime->sadb_lifetime_exttype != SADB_EXT_LIFETIME_SOFT) &&
++ (pfkey_lifetime->sadb_lifetime_exttype != SADB_EXT_LIFETIME_CURRENT)) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_lifetime_parse: "
++ "unexpected ext_type=%d.\n",
++ pfkey_lifetime->sadb_lifetime_exttype);
++ SENDERR(EINVAL);
++ }
++
++ DEBUGGING(PF_KEY_DEBUG_PARSE_STRUCT,
++ "pfkey_lifetime_parse: "
++ "life_type=%d(%s) alloc=%u bytes=%u add=%u use=%u pkts=%u.\n",
++ pfkey_lifetime->sadb_lifetime_exttype,
++ pfkey_v2_sadb_ext_string(pfkey_lifetime->sadb_lifetime_exttype),
++ pfkey_lifetime->sadb_lifetime_allocations,
++ (unsigned)pfkey_lifetime->sadb_lifetime_bytes,
++ (unsigned)pfkey_lifetime->sadb_lifetime_addtime,
++ (unsigned)pfkey_lifetime->sadb_lifetime_usetime,
++ pfkey_lifetime->sadb_x_lifetime_packets);
++errlab:
++ return error;
++}
++
++DEBUG_NO_STATIC int
++pfkey_address_parse(struct sadb_ext *pfkey_ext)
++{
++ int error = 0;
++ int saddr_len = 0;
++ struct sadb_address *pfkey_address = (struct sadb_address *)pfkey_ext;
++ struct sockaddr* s = (struct sockaddr*)((char*)pfkey_address + sizeof(*pfkey_address));
++ char ipaddr_txt[ADDRTOT_BUF];
++
++ /* sanity checks... */
++ if(!pfkey_address) {
++ ERROR(
++ "pfkey_address_parse: "
++ "NULL pointer passed in.\n");
++ SENDERR(EINVAL);
++ }
++
++ if(pfkey_address->sadb_address_len <
++ (sizeof(struct sadb_address) + sizeof(struct sockaddr))/
++ IPSEC_PFKEYv2_ALIGN) {
++ ERROR("pfkey_address_parse: "
++ "size wrong 1 ext_len=%d, adr_ext_len=%d, saddr_len=%d.\n",
++ pfkey_address->sadb_address_len,
++ (int)sizeof(struct sadb_address),
++ (int)sizeof(struct sockaddr));
++ SENDERR(EINVAL);
++ }
++
++ if(pfkey_address->sadb_address_reserved) {
++ ERROR("pfkey_address_parse: "
++ "res=%d, must be zero.\n",
++ pfkey_address->sadb_address_reserved);
++ SENDERR(EINVAL);
++ }
++
++ switch(pfkey_address->sadb_address_exttype) {
++ case SADB_EXT_ADDRESS_SRC:
++ case SADB_EXT_ADDRESS_DST:
++ case SADB_EXT_ADDRESS_PROXY:
++ case SADB_X_EXT_ADDRESS_DST2:
++ case SADB_X_EXT_ADDRESS_SRC_FLOW:
++ case SADB_X_EXT_ADDRESS_DST_FLOW:
++ case SADB_X_EXT_ADDRESS_SRC_MASK:
++ case SADB_X_EXT_ADDRESS_DST_MASK:
++#ifdef NAT_TRAVERSAL
++ case SADB_X_EXT_NAT_T_OA:
++#endif
++ break;
++ default:
++ ERROR(
++ "pfkey_address_parse: "
++ "unexpected ext_type=%d.\n",
++ pfkey_address->sadb_address_exttype);
++ SENDERR(EINVAL);
++ }
++
++ switch(s->sa_family) {
++ case AF_INET:
++ saddr_len = sizeof(struct sockaddr_in);
++ sprintf(ipaddr_txt, "%d.%d.%d.%d"
++ , (((struct sockaddr_in*)s)->sin_addr.s_addr >> 0) & 0xFF
++ , (((struct sockaddr_in*)s)->sin_addr.s_addr >> 8) & 0xFF
++ , (((struct sockaddr_in*)s)->sin_addr.s_addr >> 16) & 0xFF
++ , (((struct sockaddr_in*)s)->sin_addr.s_addr >> 24) & 0xFF);
++ DEBUGGING(PF_KEY_DEBUG_PARSE_STRUCT,
++ "pfkey_address_parse: "
++ "found exttype=%u(%s) family=%d(AF_INET) address=%s proto=%u port=%u.\n",
++ pfkey_address->sadb_address_exttype,
++ pfkey_v2_sadb_ext_string(pfkey_address->sadb_address_exttype),
++ s->sa_family,
++ ipaddr_txt,
++ pfkey_address->sadb_address_proto,
++ ntohs(((struct sockaddr_in*)s)->sin_port));
++ break;
++ case AF_INET6:
++ saddr_len = sizeof(struct sockaddr_in6);
++ sprintf(ipaddr_txt, "%x:%x:%x:%x:%x:%x:%x:%x"
++ , ntohs(((struct sockaddr_in6*)s)->sin6_addr.s6_addr16[0])
++ , ntohs(((struct sockaddr_in6*)s)->sin6_addr.s6_addr16[1])
++ , ntohs(((struct sockaddr_in6*)s)->sin6_addr.s6_addr16[2])
++ , ntohs(((struct sockaddr_in6*)s)->sin6_addr.s6_addr16[3])
++ , ntohs(((struct sockaddr_in6*)s)->sin6_addr.s6_addr16[4])
++ , ntohs(((struct sockaddr_in6*)s)->sin6_addr.s6_addr16[5])
++ , ntohs(((struct sockaddr_in6*)s)->sin6_addr.s6_addr16[6])
++ , ntohs(((struct sockaddr_in6*)s)->sin6_addr.s6_addr16[7]));
++ DEBUGGING(PF_KEY_DEBUG_PARSE_STRUCT,
++ "pfkey_address_parse: "
++ "found exttype=%u(%s) family=%d(AF_INET6) address=%s proto=%u port=%u.\n",
++ pfkey_address->sadb_address_exttype,
++ pfkey_v2_sadb_ext_string(pfkey_address->sadb_address_exttype),
++ s->sa_family,
++ ipaddr_txt,
++ pfkey_address->sadb_address_proto,
++ ((struct sockaddr_in6*)s)->sin6_port);
++ break;
++ default:
++ ERROR(
++ "pfkey_address_parse: "
++ "s->sa_family=%d not supported.\n",
++ s->sa_family);
++ SENDERR(EPFNOSUPPORT);
++ }
++
++ if(pfkey_address->sadb_address_len !=
++ DIVUP(sizeof(struct sadb_address) + saddr_len, IPSEC_PFKEYv2_ALIGN)) {
++ ERROR(
++ "pfkey_address_parse: "
++ "size wrong 2 ext_len=%d, adr_ext_len=%d, saddr_len=%d.\n",
++ pfkey_address->sadb_address_len,
++ (int)sizeof(struct sadb_address),
++ saddr_len);
++ SENDERR(EINVAL);
++ }
++
++ if(pfkey_address->sadb_address_prefixlen != 0) {
++ ERROR(
++ "pfkey_address_parse: "
++ "address prefixes not supported yet.\n");
++ SENDERR(EAFNOSUPPORT); /* not supported yet */
++ }
++
++ /* XXX check if port!=0 */
++
++ DEBUGGING(PF_KEY_DEBUG_PARSE_FLOW,
++ "pfkey_address_parse: successful.\n");
++ errlab:
++ return error;
++}
++
++DEBUG_NO_STATIC int
++pfkey_key_parse(struct sadb_ext *pfkey_ext)
++{
++ int error = 0;
++ struct sadb_key *pfkey_key = (struct sadb_key *)pfkey_ext;
++
++ /* sanity checks... */
++
++ if(!pfkey_key) {
++ ERROR(
++ "pfkey_key_parse: "
++ "NULL pointer passed in.\n");
++ SENDERR(EINVAL);
++ }
++
++ if(pfkey_key->sadb_key_len < sizeof(struct sadb_key) / IPSEC_PFKEYv2_ALIGN) {
++ ERROR(
++ "pfkey_key_parse: "
++ "size wrong ext_len=%d, key_ext_len=%d.\n",
++ pfkey_key->sadb_key_len,
++ (int)sizeof(struct sadb_key));
++ SENDERR(EINVAL);
++ }
++
++ if(!pfkey_key->sadb_key_bits) {
++ ERROR(
++ "pfkey_key_parse: "
++ "key length set to zero, must be non-zero.\n");
++ SENDERR(EINVAL);
++ }
++
++ if(pfkey_key->sadb_key_len !=
++ DIVUP(sizeof(struct sadb_key) * OCTETBITS + pfkey_key->sadb_key_bits,
++ PFKEYBITS)) {
++ ERROR(
++ "pfkey_key_parse: "
++ "key length=%d does not agree with extension length=%d.\n",
++ pfkey_key->sadb_key_bits,
++ pfkey_key->sadb_key_len);
++ SENDERR(EINVAL);
++ }
++
++ if(pfkey_key->sadb_key_reserved) {
++ ERROR(
++ "pfkey_key_parse: "
++ "res=%d, must be zero.\n",
++ pfkey_key->sadb_key_reserved);
++ SENDERR(EINVAL);
++ }
++
++ if(! ( (pfkey_key->sadb_key_exttype == SADB_EXT_KEY_AUTH) ||
++ (pfkey_key->sadb_key_exttype == SADB_EXT_KEY_ENCRYPT))) {
++ ERROR(
++ "pfkey_key_parse: "
++ "expecting extension type AUTH or ENCRYPT, got %d.\n",
++ pfkey_key->sadb_key_exttype);
++ SENDERR(EINVAL);
++ }
++
++ DEBUGGING(PF_KEY_DEBUG_PARSE_STRUCT,
++ "pfkey_key_parse: "
++ "success, found len=%d exttype=%d(%s) bits=%d reserved=%d.\n",
++ pfkey_key->sadb_key_len,
++ pfkey_key->sadb_key_exttype,
++ pfkey_v2_sadb_ext_string(pfkey_key->sadb_key_exttype),
++ pfkey_key->sadb_key_bits,
++ pfkey_key->sadb_key_reserved);
++
++errlab:
++ return error;
++}
++
++DEBUG_NO_STATIC int
++pfkey_ident_parse(struct sadb_ext *pfkey_ext)
++{
++ int error = 0;
++ struct sadb_ident *pfkey_ident = (struct sadb_ident *)pfkey_ext;
++
++ /* sanity checks... */
++ if(pfkey_ident->sadb_ident_len < sizeof(struct sadb_ident) / IPSEC_PFKEYv2_ALIGN) {
++ ERROR(
++ "pfkey_ident_parse: "
++ "size wrong ext_len=%d, key_ext_len=%d.\n",
++ pfkey_ident->sadb_ident_len,
++ (int)sizeof(struct sadb_ident));
++ SENDERR(EINVAL);
++ }
++
++ if(pfkey_ident->sadb_ident_type > SADB_IDENTTYPE_MAX) {
++ ERROR(
++ "pfkey_ident_parse: "
++ "ident_type=%d out of range, must be less than %d.\n",
++ pfkey_ident->sadb_ident_type,
++ SADB_IDENTTYPE_MAX);
++ SENDERR(EINVAL);
++ }
++
++ if(pfkey_ident->sadb_ident_reserved) {
++ ERROR(
++ "pfkey_ident_parse: "
++ "res=%d, must be zero.\n",
++ pfkey_ident->sadb_ident_reserved);
++ SENDERR(EINVAL);
++ }
++
++ /* string terminator/padding must be zero */
++ if(pfkey_ident->sadb_ident_len > sizeof(struct sadb_ident) / IPSEC_PFKEYv2_ALIGN) {
++ if(*((char*)pfkey_ident + pfkey_ident->sadb_ident_len * IPSEC_PFKEYv2_ALIGN - 1)) {
++ ERROR(
++ "pfkey_ident_parse: "
++ "string padding must be zero, last is 0x%02x.\n",
++ *((char*)pfkey_ident +
++ pfkey_ident->sadb_ident_len * IPSEC_PFKEYv2_ALIGN - 1));
++ SENDERR(EINVAL);
++ }
++ }
++
++ if( ! ((pfkey_ident->sadb_ident_exttype == SADB_EXT_IDENTITY_SRC) ||
++ (pfkey_ident->sadb_ident_exttype == SADB_EXT_IDENTITY_DST))) {
++ ERROR(
++ "pfkey_key_parse: "
++ "expecting extension type IDENTITY_SRC or IDENTITY_DST, got %d.\n",
++ pfkey_ident->sadb_ident_exttype);
++ SENDERR(EINVAL);
++ }
++
++errlab:
++ return error;
++}
++
++DEBUG_NO_STATIC int
++pfkey_sens_parse(struct sadb_ext *pfkey_ext)
++{
++ int error = 0;
++ struct sadb_sens *pfkey_sens = (struct sadb_sens *)pfkey_ext;
++
++ /* sanity checks... */
++ if(pfkey_sens->sadb_sens_len < sizeof(struct sadb_sens) / IPSEC_PFKEYv2_ALIGN) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_sens_parse: "
++ "size wrong ext_len=%d, key_ext_len=%d.\n",
++ pfkey_sens->sadb_sens_len,
++ (int)sizeof(struct sadb_sens));
++ SENDERR(EINVAL);
++ }
++
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_sens_parse: "
++ "Sorry, I can't parse exttype=%d yet.\n",
++ pfkey_ext->sadb_ext_type);
++#if 0
++ SENDERR(EINVAL); /* don't process these yet */
++#endif
++
++errlab:
++ return error;
++}
++
++DEBUG_NO_STATIC int
++pfkey_prop_parse(struct sadb_ext *pfkey_ext)
++{
++ int error = 0;
++ int i, num_comb;
++ struct sadb_prop *pfkey_prop = (struct sadb_prop *)pfkey_ext;
++ struct sadb_comb *pfkey_comb = (struct sadb_comb *)((char*)pfkey_ext + sizeof(struct sadb_prop));
++
++ /* sanity checks... */
++ if((pfkey_prop->sadb_prop_len < sizeof(struct sadb_prop) / IPSEC_PFKEYv2_ALIGN) ||
++ (((pfkey_prop->sadb_prop_len * IPSEC_PFKEYv2_ALIGN) - sizeof(struct sadb_prop)) % sizeof(struct sadb_comb))) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_prop_parse: "
++ "size wrong ext_len=%d, prop_ext_len=%d comb_ext_len=%d.\n",
++ pfkey_prop->sadb_prop_len,
++ (int)sizeof(struct sadb_prop),
++ (int)sizeof(struct sadb_comb));
++ SENDERR(EINVAL);
++ }
++
++ if(pfkey_prop->sadb_prop_replay > 64) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_prop_parse: "
++ "replay window size: %d -- must be 0 <= size <= 64\n",
++ pfkey_prop->sadb_prop_replay);
++ SENDERR(EINVAL);
++ }
++
++ for(i=0; i<3; i++) {
++ if(pfkey_prop->sadb_prop_reserved[i]) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_prop_parse: "
++ "res[%d]=%d, must be zero.\n",
++ i, pfkey_prop->sadb_prop_reserved[i]);
++ SENDERR(EINVAL);
++ }
++ }
++
++ num_comb = ((pfkey_prop->sadb_prop_len * IPSEC_PFKEYv2_ALIGN) - sizeof(struct sadb_prop)) / sizeof(struct sadb_comb);
++
++ for(i = 0; i < num_comb; i++) {
++ if(pfkey_comb->sadb_comb_auth > SADB_AALG_MAX) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_prop_parse: "
++ "pfkey_comb[%d]->sadb_comb_auth=%d > SADB_AALG_MAX=%d.\n",
++ i,
++ pfkey_comb->sadb_comb_auth,
++ SADB_AALG_MAX);
++ SENDERR(EINVAL);
++ }
++
++ if(pfkey_comb->sadb_comb_auth) {
++ if(!pfkey_comb->sadb_comb_auth_minbits) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_prop_parse: "
++ "pfkey_comb[%d]->sadb_comb_auth_minbits=0, fatal.\n",
++ i);
++ SENDERR(EINVAL);
++ }
++ if(!pfkey_comb->sadb_comb_auth_maxbits) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_prop_parse: "
++ "pfkey_comb[%d]->sadb_comb_auth_maxbits=0, fatal.\n",
++ i);
++ SENDERR(EINVAL);
++ }
++ if(pfkey_comb->sadb_comb_auth_minbits > pfkey_comb->sadb_comb_auth_maxbits) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_prop_parse: "
++ "pfkey_comb[%d]->sadb_comb_auth_minbits=%d > maxbits=%d, fatal.\n",
++ i,
++ pfkey_comb->sadb_comb_auth_minbits,
++ pfkey_comb->sadb_comb_auth_maxbits);
++ SENDERR(EINVAL);
++ }
++ } else {
++ if(pfkey_comb->sadb_comb_auth_minbits) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_prop_parse: "
++ "pfkey_comb[%d]->sadb_comb_auth_minbits=%d != 0, fatal.\n",
++ i,
++ pfkey_comb->sadb_comb_auth_minbits);
++ SENDERR(EINVAL);
++ }
++ if(pfkey_comb->sadb_comb_auth_maxbits) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_prop_parse: "
++ "pfkey_comb[%d]->sadb_comb_auth_maxbits=%d != 0, fatal.\n",
++ i,
++ pfkey_comb->sadb_comb_auth_maxbits);
++ SENDERR(EINVAL);
++ }
++ }
++
++ if(pfkey_comb->sadb_comb_encrypt > SADB_EALG_MAX) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_comb_parse: "
++ "pfkey_comb[%d]->sadb_comb_encrypt=%d > SADB_EALG_MAX=%d.\n",
++ i,
++ pfkey_comb->sadb_comb_encrypt,
++ SADB_EALG_MAX);
++ SENDERR(EINVAL);
++ }
++
++ if(pfkey_comb->sadb_comb_encrypt) {
++ if(!pfkey_comb->sadb_comb_encrypt_minbits) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_prop_parse: "
++ "pfkey_comb[%d]->sadb_comb_encrypt_minbits=0, fatal.\n",
++ i);
++ SENDERR(EINVAL);
++ }
++ if(!pfkey_comb->sadb_comb_encrypt_maxbits) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_prop_parse: "
++ "pfkey_comb[%d]->sadb_comb_encrypt_maxbits=0, fatal.\n",
++ i);
++ SENDERR(EINVAL);
++ }
++ if(pfkey_comb->sadb_comb_encrypt_minbits > pfkey_comb->sadb_comb_encrypt_maxbits) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_prop_parse: "
++ "pfkey_comb[%d]->sadb_comb_encrypt_minbits=%d > maxbits=%d, fatal.\n",
++ i,
++ pfkey_comb->sadb_comb_encrypt_minbits,
++ pfkey_comb->sadb_comb_encrypt_maxbits);
++ SENDERR(EINVAL);
++ }
++ } else {
++ if(pfkey_comb->sadb_comb_encrypt_minbits) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_prop_parse: "
++ "pfkey_comb[%d]->sadb_comb_encrypt_minbits=%d != 0, fatal.\n",
++ i,
++ pfkey_comb->sadb_comb_encrypt_minbits);
++ SENDERR(EINVAL);
++ }
++ if(pfkey_comb->sadb_comb_encrypt_maxbits) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_prop_parse: "
++ "pfkey_comb[%d]->sadb_comb_encrypt_maxbits=%d != 0, fatal.\n",
++ i,
++ pfkey_comb->sadb_comb_encrypt_maxbits);
++ SENDERR(EINVAL);
++ }
++ }
++
++ /* XXX do sanity check on flags */
++
++ if(pfkey_comb->sadb_comb_hard_allocations && pfkey_comb->sadb_comb_soft_allocations > pfkey_comb->sadb_comb_hard_allocations) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_prop_parse: "
++ "pfkey_comb[%d]->sadb_comb_soft_allocations=%d > hard_allocations=%d, fatal.\n",
++ i,
++ pfkey_comb->sadb_comb_soft_allocations,
++ pfkey_comb->sadb_comb_hard_allocations);
++ SENDERR(EINVAL);
++ }
++
++ if(pfkey_comb->sadb_comb_hard_bytes && pfkey_comb->sadb_comb_soft_bytes > pfkey_comb->sadb_comb_hard_bytes) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_prop_parse: "
++ "pfkey_comb[%d]->sadb_comb_soft_bytes=%Ld > hard_bytes=%Ld, fatal.\n",
++ i,
++ (unsigned long long int)pfkey_comb->sadb_comb_soft_bytes,
++ (unsigned long long int)pfkey_comb->sadb_comb_hard_bytes);
++ SENDERR(EINVAL);
++ }
++
++ if(pfkey_comb->sadb_comb_hard_addtime && pfkey_comb->sadb_comb_soft_addtime > pfkey_comb->sadb_comb_hard_addtime) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_prop_parse: "
++ "pfkey_comb[%d]->sadb_comb_soft_addtime=%Ld > hard_addtime=%Ld, fatal.\n",
++ i,
++ (unsigned long long int)pfkey_comb->sadb_comb_soft_addtime,
++ (unsigned long long int)pfkey_comb->sadb_comb_hard_addtime);
++ SENDERR(EINVAL);
++ }
++
++ if(pfkey_comb->sadb_comb_hard_usetime && pfkey_comb->sadb_comb_soft_usetime > pfkey_comb->sadb_comb_hard_usetime) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_prop_parse: "
++ "pfkey_comb[%d]->sadb_comb_soft_usetime=%Ld > hard_usetime=%Ld, fatal.\n",
++ i,
++ (unsigned long long int)pfkey_comb->sadb_comb_soft_usetime,
++ (unsigned long long int)pfkey_comb->sadb_comb_hard_usetime);
++ SENDERR(EINVAL);
++ }
++
++ if(pfkey_comb->sadb_x_comb_hard_packets && pfkey_comb->sadb_x_comb_soft_packets > pfkey_comb->sadb_x_comb_hard_packets) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_prop_parse: "
++ "pfkey_comb[%d]->sadb_x_comb_soft_packets=%d > hard_packets=%d, fatal.\n",
++ i,
++ pfkey_comb->sadb_x_comb_soft_packets,
++ pfkey_comb->sadb_x_comb_hard_packets);
++ SENDERR(EINVAL);
++ }
++
++ if(pfkey_comb->sadb_comb_reserved) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_prop_parse: "
++ "comb[%d].res=%d, must be zero.\n",
++ i,
++ pfkey_comb->sadb_comb_reserved);
++ SENDERR(EINVAL);
++ }
++ pfkey_comb++;
++ }
++
++errlab:
++ return error;
++}
++
++DEBUG_NO_STATIC int
++pfkey_supported_parse(struct sadb_ext *pfkey_ext)
++{
++ int error = 0;
++ unsigned int i, num_alg;
++ struct sadb_supported *pfkey_supported = (struct sadb_supported *)pfkey_ext;
++ struct sadb_alg *pfkey_alg = (struct sadb_alg*)((char*)pfkey_ext + sizeof(struct sadb_supported));
++
++ /* sanity checks... */
++ if((pfkey_supported->sadb_supported_len <
++ sizeof(struct sadb_supported) / IPSEC_PFKEYv2_ALIGN) ||
++ (((pfkey_supported->sadb_supported_len * IPSEC_PFKEYv2_ALIGN) -
++ sizeof(struct sadb_supported)) % sizeof(struct sadb_alg))) {
++
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_supported_parse: "
++ "size wrong ext_len=%d, supported_ext_len=%d alg_ext_len=%d.\n",
++ pfkey_supported->sadb_supported_len,
++ (int)sizeof(struct sadb_supported),
++ (int)sizeof(struct sadb_alg));
++ SENDERR(EINVAL);
++ }
++
++ if(pfkey_supported->sadb_supported_reserved) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_supported_parse: "
++ "res=%d, must be zero.\n",
++ pfkey_supported->sadb_supported_reserved);
++ SENDERR(EINVAL);
++ }
++
++ num_alg = ((pfkey_supported->sadb_supported_len * IPSEC_PFKEYv2_ALIGN) - sizeof(struct sadb_supported)) / sizeof(struct sadb_alg);
++
++ for(i = 0; i < num_alg; i++) {
++ /* process algo description */
++ if(pfkey_alg->sadb_alg_reserved) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_supported_parse: "
++ "alg[%d], id=%d, ivlen=%d, minbits=%d, maxbits=%d, res=%d, must be zero.\n",
++ i,
++ pfkey_alg->sadb_alg_id,
++ pfkey_alg->sadb_alg_ivlen,
++ pfkey_alg->sadb_alg_minbits,
++ pfkey_alg->sadb_alg_maxbits,
++ pfkey_alg->sadb_alg_reserved);
++ SENDERR(EINVAL);
++ }
++
++ /* XXX can alg_id auth/enc be determined from info given?
++ Yes, but OpenBSD's method does not iteroperate with rfc2367.
++ rgb, 2000-04-06 */
++
++ switch(pfkey_supported->sadb_supported_exttype) {
++ case SADB_EXT_SUPPORTED_AUTH:
++ if(pfkey_alg->sadb_alg_id > SADB_AALG_MAX) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_supported_parse: "
++ "alg[%d], alg_id=%d > SADB_AALG_MAX=%d, fatal.\n",
++ i,
++ pfkey_alg->sadb_alg_id,
++ SADB_AALG_MAX);
++ SENDERR(EINVAL);
++ }
++ break;
++ case SADB_EXT_SUPPORTED_ENCRYPT:
++ if(pfkey_alg->sadb_alg_id > SADB_EALG_MAX) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_supported_parse: "
++ "alg[%d], alg_id=%d > SADB_EALG_MAX=%d, fatal.\n",
++ i,
++ pfkey_alg->sadb_alg_id,
++ SADB_EALG_MAX);
++ SENDERR(EINVAL);
++ }
++ break;
++ default:
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_supported_parse: "
++ "alg[%d], alg_id=%d > SADB_EALG_MAX=%d, fatal.\n",
++ i,
++ pfkey_alg->sadb_alg_id,
++ SADB_EALG_MAX);
++ SENDERR(EINVAL);
++ }
++ pfkey_alg++;
++ }
++
++ errlab:
++ return error;
++}
++
++DEBUG_NO_STATIC int
++pfkey_spirange_parse(struct sadb_ext *pfkey_ext)
++{
++ int error = 0;
++ struct sadb_spirange *pfkey_spirange = (struct sadb_spirange *)pfkey_ext;
++
++ /* sanity checks... */
++ if(pfkey_spirange->sadb_spirange_len !=
++ sizeof(struct sadb_spirange) / IPSEC_PFKEYv2_ALIGN) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_spirange_parse: "
++ "size wrong ext_len=%d, key_ext_len=%d.\n",
++ pfkey_spirange->sadb_spirange_len,
++ (int)sizeof(struct sadb_spirange));
++ SENDERR(EINVAL);
++ }
++
++ if(pfkey_spirange->sadb_spirange_reserved) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_spirange_parse: "
++ "reserved=%d must be set to zero.\n",
++ pfkey_spirange->sadb_spirange_reserved);
++ SENDERR(EINVAL);
++ }
++
++ if(ntohl(pfkey_spirange->sadb_spirange_max) < ntohl(pfkey_spirange->sadb_spirange_min)) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_spirange_parse: "
++ "minspi=%08x must be < maxspi=%08x.\n",
++ ntohl(pfkey_spirange->sadb_spirange_min),
++ ntohl(pfkey_spirange->sadb_spirange_max));
++ SENDERR(EINVAL);
++ }
++
++ if(ntohl(pfkey_spirange->sadb_spirange_min) <= 255) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_spirange_parse: "
++ "minspi=%08x must be > 255.\n",
++ ntohl(pfkey_spirange->sadb_spirange_min));
++ SENDERR(EEXIST);
++ }
++
++ DEBUGGING(PF_KEY_DEBUG_PARSE_STRUCT,
++ "pfkey_spirange_parse: "
++ "ext_len=%u ext_type=%u(%s) min=%u max=%u res=%u.\n",
++ pfkey_spirange->sadb_spirange_len,
++ pfkey_spirange->sadb_spirange_exttype,
++ pfkey_v2_sadb_ext_string(pfkey_spirange->sadb_spirange_exttype),
++ pfkey_spirange->sadb_spirange_min,
++ pfkey_spirange->sadb_spirange_max,
++ pfkey_spirange->sadb_spirange_reserved);
++ errlab:
++ return error;
++}
++
++DEBUG_NO_STATIC int
++pfkey_x_kmprivate_parse(struct sadb_ext *pfkey_ext)
++{
++ int error = 0;
++ struct sadb_x_kmprivate *pfkey_x_kmprivate = (struct sadb_x_kmprivate *)pfkey_ext;
++
++ /* sanity checks... */
++ if(pfkey_x_kmprivate->sadb_x_kmprivate_len <
++ sizeof(struct sadb_x_kmprivate) / IPSEC_PFKEYv2_ALIGN) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_x_kmprivate_parse: "
++ "size wrong ext_len=%d, key_ext_len=%d.\n",
++ pfkey_x_kmprivate->sadb_x_kmprivate_len,
++ (int)sizeof(struct sadb_x_kmprivate));
++ SENDERR(EINVAL);
++ }
++
++ if(pfkey_x_kmprivate->sadb_x_kmprivate_reserved) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_x_kmprivate_parse: "
++ "reserved=%d must be set to zero.\n",
++ pfkey_x_kmprivate->sadb_x_kmprivate_reserved);
++ SENDERR(EINVAL);
++ }
++
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_x_kmprivate_parse: "
++ "Sorry, I can't parse exttype=%d yet.\n",
++ pfkey_ext->sadb_ext_type);
++ SENDERR(EINVAL); /* don't process these yet */
++
++errlab:
++ return error;
++}
++
++DEBUG_NO_STATIC int
++pfkey_x_satype_parse(struct sadb_ext *pfkey_ext)
++{
++ int error = 0;
++ int i;
++ struct sadb_x_satype *pfkey_x_satype = (struct sadb_x_satype *)pfkey_ext;
++
++ DEBUGGING(PF_KEY_DEBUG_PARSE_FLOW,
++ "pfkey_x_satype_parse: enter\n");
++ /* sanity checks... */
++ if(pfkey_x_satype->sadb_x_satype_len !=
++ sizeof(struct sadb_x_satype) / IPSEC_PFKEYv2_ALIGN) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_x_satype_parse: "
++ "size wrong ext_len=%d, key_ext_len=%d.\n",
++ pfkey_x_satype->sadb_x_satype_len,
++ (int)sizeof(struct sadb_x_satype));
++ SENDERR(EINVAL);
++ }
++
++ if(!pfkey_x_satype->sadb_x_satype_satype) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_x_satype_parse: "
++ "satype is zero, must be non-zero.\n");
++ SENDERR(EINVAL);
++ }
++
++ if(pfkey_x_satype->sadb_x_satype_satype > SADB_SATYPE_MAX) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_x_satype_parse: "
++ "satype %d > max %d, invalid.\n",
++ pfkey_x_satype->sadb_x_satype_satype, SADB_SATYPE_MAX);
++ SENDERR(EINVAL);
++ }
++
++ if(!(satype2proto(pfkey_x_satype->sadb_x_satype_satype))) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_x_satype_parse: "
++ "proto lookup from satype=%d failed.\n",
++ pfkey_x_satype->sadb_x_satype_satype);
++ SENDERR(EINVAL);
++ }
++
++ for(i = 0; i < 3; i++) {
++ if(pfkey_x_satype->sadb_x_satype_reserved[i]) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_x_satype_parse: "
++ "reserved[%d]=%d must be set to zero.\n",
++ i, pfkey_x_satype->sadb_x_satype_reserved[i]);
++ SENDERR(EINVAL);
++ }
++ }
++
++ DEBUGGING(PF_KEY_DEBUG_PARSE_STRUCT,
++ "pfkey_x_satype_parse: "
++ "len=%u ext=%u(%s) satype=%u(%s) res=%u,%u,%u.\n",
++ pfkey_x_satype->sadb_x_satype_len,
++ pfkey_x_satype->sadb_x_satype_exttype,
++ pfkey_v2_sadb_ext_string(pfkey_x_satype->sadb_x_satype_exttype),
++ pfkey_x_satype->sadb_x_satype_satype,
++ satype2name(pfkey_x_satype->sadb_x_satype_satype),
++ pfkey_x_satype->sadb_x_satype_reserved[0],
++ pfkey_x_satype->sadb_x_satype_reserved[1],
++ pfkey_x_satype->sadb_x_satype_reserved[2]);
++errlab:
++ return error;
++}
++
++DEBUG_NO_STATIC int
++pfkey_x_ext_debug_parse(struct sadb_ext *pfkey_ext)
++{
++ int error = 0;
++ int i;
++ struct sadb_x_debug *pfkey_x_debug = (struct sadb_x_debug *)pfkey_ext;
++
++ DEBUGGING(PF_KEY_DEBUG_PARSE_FLOW,
++ "pfkey_x_debug_parse: enter\n");
++ /* sanity checks... */
++ if(pfkey_x_debug->sadb_x_debug_len !=
++ sizeof(struct sadb_x_debug) / IPSEC_PFKEYv2_ALIGN) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_x_debug_parse: "
++ "size wrong ext_len=%d, key_ext_len=%d.\n",
++ pfkey_x_debug->sadb_x_debug_len,
++ (int)sizeof(struct sadb_x_debug));
++ SENDERR(EINVAL);
++ }
++
++ for(i = 0; i < 4; i++) {
++ if(pfkey_x_debug->sadb_x_debug_reserved[i]) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_x_debug_parse: "
++ "reserved[%d]=%d must be set to zero.\n",
++ i, pfkey_x_debug->sadb_x_debug_reserved[i]);
++ SENDERR(EINVAL);
++ }
++ }
++
++errlab:
++ return error;
++}
++
++DEBUG_NO_STATIC int
++pfkey_x_ext_protocol_parse(struct sadb_ext *pfkey_ext)
++{
++ int error = 0;
++ struct sadb_protocol *p = (struct sadb_protocol *)pfkey_ext;
++
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, "pfkey_x_protocol_parse:\n");
++ /* sanity checks... */
++
++ if (p->sadb_protocol_len != sizeof(*p)/IPSEC_PFKEYv2_ALIGN) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_x_protocol_parse: size wrong ext_len=%d, key_ext_len=%d.\n",
++ p->sadb_protocol_len, (int)sizeof(*p));
++ SENDERR(EINVAL);
++ }
++
++ if (p->sadb_protocol_reserved2 != 0) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_protocol_parse: res=%d, must be zero.\n",
++ p->sadb_protocol_reserved2);
++ SENDERR(EINVAL);
++ }
++
++ errlab:
++ return error;
++}
++
++#ifdef NAT_TRAVERSAL
++DEBUG_NO_STATIC int
++pfkey_x_ext_nat_t_type_parse(struct sadb_ext *pfkey_ext)
++{
++ return 0;
++}
++DEBUG_NO_STATIC int
++pfkey_x_ext_nat_t_port_parse(struct sadb_ext *pfkey_ext)
++{
++ return 0;
++}
++#endif
++
++#define DEFINEPARSER(NAME) static struct pf_key_ext_parsers_def NAME##_def={NAME, #NAME};
++
++DEFINEPARSER(pfkey_sa_parse);
++DEFINEPARSER(pfkey_lifetime_parse);
++DEFINEPARSER(pfkey_address_parse);
++DEFINEPARSER(pfkey_key_parse);
++DEFINEPARSER(pfkey_ident_parse);
++DEFINEPARSER(pfkey_sens_parse);
++DEFINEPARSER(pfkey_prop_parse);
++DEFINEPARSER(pfkey_supported_parse);
++DEFINEPARSER(pfkey_spirange_parse);
++DEFINEPARSER(pfkey_x_kmprivate_parse);
++DEFINEPARSER(pfkey_x_satype_parse);
++DEFINEPARSER(pfkey_x_ext_debug_parse);
++DEFINEPARSER(pfkey_x_ext_protocol_parse);
++#ifdef NAT_TRAVERSAL
++DEFINEPARSER(pfkey_x_ext_nat_t_type_parse);
++DEFINEPARSER(pfkey_x_ext_nat_t_port_parse);
++#endif
++
++struct pf_key_ext_parsers_def *ext_default_parsers[]=
++{
++ NULL, /* pfkey_msg_parse, */
++ &pfkey_sa_parse_def,
++ &pfkey_lifetime_parse_def,
++ &pfkey_lifetime_parse_def,
++ &pfkey_lifetime_parse_def,
++ &pfkey_address_parse_def,
++ &pfkey_address_parse_def,
++ &pfkey_address_parse_def,
++ &pfkey_key_parse_def,
++ &pfkey_key_parse_def,
++ &pfkey_ident_parse_def,
++ &pfkey_ident_parse_def,
++ &pfkey_sens_parse_def,
++ &pfkey_prop_parse_def,
++ &pfkey_supported_parse_def,
++ &pfkey_supported_parse_def,
++ &pfkey_spirange_parse_def,
++ &pfkey_x_kmprivate_parse_def,
++ &pfkey_x_satype_parse_def,
++ &pfkey_sa_parse_def,
++ &pfkey_address_parse_def,
++ &pfkey_address_parse_def,
++ &pfkey_address_parse_def,
++ &pfkey_address_parse_def,
++ &pfkey_address_parse_def,
++ &pfkey_x_ext_debug_parse_def,
++ &pfkey_x_ext_protocol_parse_def
++#ifdef NAT_TRAVERSAL
++ ,
++ &pfkey_x_ext_nat_t_type_parse_def,
++ &pfkey_x_ext_nat_t_port_parse_def,
++ &pfkey_x_ext_nat_t_port_parse_def,
++ &pfkey_address_parse_def
++#endif
++};
++
++int
++pfkey_msg_parse(struct sadb_msg *pfkey_msg,
++ struct pf_key_ext_parsers_def *ext_parsers[],
++ struct sadb_ext *extensions[],
++ int dir)
++{
++ int error = 0;
++ int remain;
++ struct sadb_ext *pfkey_ext;
++ int extensions_seen = 0;
++
++ DEBUGGING(PF_KEY_DEBUG_PARSE_STRUCT,
++ "pfkey_msg_parse: "
++ "parsing message ver=%d, type=%d(%s), errno=%d, satype=%d(%s), len=%d, res=%d, seq=%d, pid=%d.\n",
++ pfkey_msg->sadb_msg_version,
++ pfkey_msg->sadb_msg_type,
++ pfkey_v2_sadb_type_string(pfkey_msg->sadb_msg_type),
++ pfkey_msg->sadb_msg_errno,
++ pfkey_msg->sadb_msg_satype,
++ satype2name(pfkey_msg->sadb_msg_satype),
++ pfkey_msg->sadb_msg_len,
++ pfkey_msg->sadb_msg_reserved,
++ pfkey_msg->sadb_msg_seq,
++ pfkey_msg->sadb_msg_pid);
++
++ if(ext_parsers == NULL) ext_parsers = ext_default_parsers;
++
++ pfkey_extensions_init(extensions);
++
++ remain = pfkey_msg->sadb_msg_len;
++ remain -= sizeof(struct sadb_msg) / IPSEC_PFKEYv2_ALIGN;
++
++ pfkey_ext = (struct sadb_ext*)((char*)pfkey_msg +
++ sizeof(struct sadb_msg));
++
++ extensions[0] = (struct sadb_ext *) pfkey_msg;
++
++
++ if(pfkey_msg->sadb_msg_version != PF_KEY_V2) {
++ ERROR("pfkey_msg_parse: "
++ "not PF_KEY_V2 msg, found %d, should be %d.\n",
++ pfkey_msg->sadb_msg_version,
++ PF_KEY_V2);
++ SENDERR(EINVAL);
++ }
++
++ if(!pfkey_msg->sadb_msg_type) {
++ ERROR("pfkey_msg_parse: "
++ "msg type not set, must be non-zero..\n");
++ SENDERR(EINVAL);
++ }
++
++ if(pfkey_msg->sadb_msg_type > SADB_MAX) {
++ ERROR("pfkey_msg_parse: "
++ "msg type=%d > max=%d.\n",
++ pfkey_msg->sadb_msg_type,
++ SADB_MAX);
++ SENDERR(EINVAL);
++ }
++
++ switch(pfkey_msg->sadb_msg_type) {
++ case SADB_GETSPI:
++ case SADB_UPDATE:
++ case SADB_ADD:
++ case SADB_DELETE:
++ case SADB_GET:
++ case SADB_X_GRPSA:
++ case SADB_X_ADDFLOW:
++ if(!satype2proto(pfkey_msg->sadb_msg_satype)) {
++ ERROR("pfkey_msg_parse: "
++ "satype %d conversion to proto failed for msg_type %d (%s).\n",
++ pfkey_msg->sadb_msg_satype,
++ pfkey_msg->sadb_msg_type,
++ pfkey_v2_sadb_type_string(pfkey_msg->sadb_msg_type));
++ SENDERR(EINVAL);
++ } else {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_msg_parse: "
++ "satype %d(%s) conversion to proto gives %d for msg_type %d(%s).\n",
++ pfkey_msg->sadb_msg_satype,
++ satype2name(pfkey_msg->sadb_msg_satype),
++ satype2proto(pfkey_msg->sadb_msg_satype),
++ pfkey_msg->sadb_msg_type,
++ pfkey_v2_sadb_type_string(pfkey_msg->sadb_msg_type));
++ }
++ case SADB_ACQUIRE:
++ case SADB_REGISTER:
++ case SADB_EXPIRE:
++ if(!pfkey_msg->sadb_msg_satype) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_msg_parse: "
++ "satype is zero, must be non-zero for msg_type %d(%s).\n",
++ pfkey_msg->sadb_msg_type,
++ pfkey_v2_sadb_type_string(pfkey_msg->sadb_msg_type));
++ SENDERR(EINVAL);
++ }
++ default:
++ break;
++ }
++
++ /* errno must not be set in downward messages */
++ /* this is not entirely true... a response to an ACQUIRE could return an error */
++ if((dir == EXT_BITS_IN) && (pfkey_msg->sadb_msg_type != SADB_ACQUIRE) && pfkey_msg->sadb_msg_errno) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_msg_parse: "
++ "errno set to %d.\n",
++ pfkey_msg->sadb_msg_errno);
++ SENDERR(EINVAL);
++ }
++
++ DEBUGGING(PF_KEY_DEBUG_PARSE_FLOW,
++ "pfkey_msg_parse: "
++ "remain=%d, ext_type=%d(%s), ext_len=%d.\n",
++ remain,
++ pfkey_ext->sadb_ext_type,
++ pfkey_v2_sadb_ext_string(pfkey_ext->sadb_ext_type),
++ pfkey_ext->sadb_ext_len);
++
++ DEBUGGING(PF_KEY_DEBUG_PARSE_FLOW,
++ "pfkey_msg_parse: "
++ "extensions permitted=%08x, required=%08x.\n",
++ extensions_bitmaps[dir][EXT_BITS_PERM][pfkey_msg->sadb_msg_type],
++ extensions_bitmaps[dir][EXT_BITS_REQ][pfkey_msg->sadb_msg_type]);
++
++ extensions_seen = 1;
++
++ while( (remain * IPSEC_PFKEYv2_ALIGN) >= sizeof(struct sadb_ext) ) {
++ /* Is there enough message left to support another extension header? */
++ if(remain < pfkey_ext->sadb_ext_len) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_msg_parse: "
++ "remain %d less than ext len %d.\n",
++ remain, pfkey_ext->sadb_ext_len);
++ SENDERR(EINVAL);
++ }
++
++ DEBUGGING(PF_KEY_DEBUG_PARSE_FLOW,
++ "pfkey_msg_parse: "
++ "parsing ext type=%d(%s) remain=%d.\n",
++ pfkey_ext->sadb_ext_type,
++ pfkey_v2_sadb_ext_string(pfkey_ext->sadb_ext_type),
++ remain);
++
++ /* Is the extension header type valid? */
++ if((pfkey_ext->sadb_ext_type > SADB_EXT_MAX) || (!pfkey_ext->sadb_ext_type)) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_msg_parse: "
++ "ext type %d(%s) invalid, SADB_EXT_MAX=%d.\n",
++ pfkey_ext->sadb_ext_type,
++ pfkey_v2_sadb_ext_string(pfkey_ext->sadb_ext_type),
++ SADB_EXT_MAX);
++ SENDERR(EINVAL);
++ }
++
++ /* Have we already seen this type of extension? */
++ if((extensions_seen & ( 1 << pfkey_ext->sadb_ext_type )) != 0)
++ {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_msg_parse: "
++ "ext type %d(%s) already seen.\n",
++ pfkey_ext->sadb_ext_type,
++ pfkey_v2_sadb_ext_string(pfkey_ext->sadb_ext_type));
++ SENDERR(EINVAL);
++ }
++
++ /* Do I even know about this type of extension? */
++ if(ext_parsers[pfkey_ext->sadb_ext_type]==NULL) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_msg_parse: "
++ "ext type %d(%s) unknown, ignoring.\n",
++ pfkey_ext->sadb_ext_type,
++ pfkey_v2_sadb_ext_string(pfkey_ext->sadb_ext_type));
++ goto next_ext;
++ }
++
++ /* Is this type of extension permitted for this type of message? */
++ if(!(extensions_bitmaps[dir][EXT_BITS_PERM][pfkey_msg->sadb_msg_type] &
++ 1<<pfkey_ext->sadb_ext_type)) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_msg_parse: "
++ "ext type %d(%s) not permitted, exts_perm_in=%08x, 1<<type=%08x\n",
++ pfkey_ext->sadb_ext_type,
++ pfkey_v2_sadb_ext_string(pfkey_ext->sadb_ext_type),
++ extensions_bitmaps[dir][EXT_BITS_PERM][pfkey_msg->sadb_msg_type],
++ 1<<pfkey_ext->sadb_ext_type);
++ SENDERR(EINVAL);
++ }
++
++ DEBUGGING(PF_KEY_DEBUG_PARSE_STRUCT,
++ "pfkey_msg_parse: "
++ "remain=%d ext_type=%d(%s) ext_len=%d parsing ext 0p%p with parser %s.\n",
++ remain,
++ pfkey_ext->sadb_ext_type,
++ pfkey_v2_sadb_ext_string(pfkey_ext->sadb_ext_type),
++ pfkey_ext->sadb_ext_len,
++ pfkey_ext,
++ ext_parsers[pfkey_ext->sadb_ext_type]->parser_name);
++
++ /* Parse the extension */
++ if((error =
++ (*ext_parsers[pfkey_ext->sadb_ext_type]->parser)(pfkey_ext))) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_msg_parse: "
++ "extension parsing for type %d(%s) failed with error %d.\n",
++ pfkey_ext->sadb_ext_type,
++ pfkey_v2_sadb_ext_string(pfkey_ext->sadb_ext_type),
++ error);
++ SENDERR(-error);
++ }
++ DEBUGGING(PF_KEY_DEBUG_PARSE_FLOW,
++ "pfkey_msg_parse: "
++ "Extension %d(%s) parsed.\n",
++ pfkey_ext->sadb_ext_type,
++ pfkey_v2_sadb_ext_string(pfkey_ext->sadb_ext_type));
++
++ /* Mark that we have seen this extension and remember the header location */
++ extensions_seen |= ( 1 << pfkey_ext->sadb_ext_type );
++ extensions[pfkey_ext->sadb_ext_type] = pfkey_ext;
++
++ next_ext:
++ /* Calculate how much message remains */
++ remain -= pfkey_ext->sadb_ext_len;
++
++ if(!remain) {
++ break;
++ }
++ /* Find the next extension header */
++ pfkey_ext = (struct sadb_ext*)((char*)pfkey_ext +
++ pfkey_ext->sadb_ext_len * IPSEC_PFKEYv2_ALIGN);
++ }
++
++ if(remain) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_msg_parse: "
++ "unexpected remainder of %d.\n",
++ remain);
++ /* why is there still something remaining? */
++ SENDERR(EINVAL);
++ }
++
++ /* check required extensions */
++ DEBUGGING(PF_KEY_DEBUG_PARSE_STRUCT,
++ "pfkey_msg_parse: "
++ "extensions permitted=%08x, seen=%08x, required=%08x.\n",
++ extensions_bitmaps[dir][EXT_BITS_PERM][pfkey_msg->sadb_msg_type],
++ extensions_seen,
++ extensions_bitmaps[dir][EXT_BITS_REQ][pfkey_msg->sadb_msg_type]);
++
++ /* don't check further if it is an error return message since it
++ may not have a body */
++ if(pfkey_msg->sadb_msg_errno) {
++ SENDERR(-error);
++ }
++
++ if((extensions_seen &
++ extensions_bitmaps[dir][EXT_BITS_REQ][pfkey_msg->sadb_msg_type]) !=
++ extensions_bitmaps[dir][EXT_BITS_REQ][pfkey_msg->sadb_msg_type]) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_msg_parse: "
++ "required extensions missing:%08x.\n",
++ extensions_bitmaps[dir][EXT_BITS_REQ][pfkey_msg->sadb_msg_type] -
++ (extensions_seen &
++ extensions_bitmaps[dir][EXT_BITS_REQ][pfkey_msg->sadb_msg_type]));
++ SENDERR(EINVAL);
++ }
++
++ if((dir == EXT_BITS_IN) && (pfkey_msg->sadb_msg_type == SADB_X_DELFLOW)
++ && ((extensions_seen & SADB_X_EXT_ADDRESS_DELFLOW)
++ != SADB_X_EXT_ADDRESS_DELFLOW)
++ && (((extensions_seen & (1<<SADB_EXT_SA)) != (1<<SADB_EXT_SA))
++ || ((((struct sadb_sa*)extensions[SADB_EXT_SA])->sadb_sa_flags
++ & SADB_X_SAFLAGS_CLEARFLOW)
++ != SADB_X_SAFLAGS_CLEARFLOW))) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_msg_parse: "
++ "required SADB_X_DELFLOW extensions missing: either %08x must be present or %08x must be present with SADB_X_SAFLAGS_CLEARFLOW set.\n",
++ SADB_X_EXT_ADDRESS_DELFLOW
++ - (extensions_seen & SADB_X_EXT_ADDRESS_DELFLOW),
++ (1<<SADB_EXT_SA) - (extensions_seen & (1<<SADB_EXT_SA)));
++ SENDERR(EINVAL);
++ }
++
++ switch(pfkey_msg->sadb_msg_type) {
++ case SADB_ADD:
++ case SADB_UPDATE:
++ /* check maturity */
++ if(((struct sadb_sa*)extensions[SADB_EXT_SA])->sadb_sa_state !=
++ SADB_SASTATE_MATURE) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_msg_parse: "
++ "state=%d for add or update should be MATURE=%d.\n",
++ ((struct sadb_sa*)extensions[SADB_EXT_SA])->sadb_sa_state,
++ SADB_SASTATE_MATURE);
++ SENDERR(EINVAL);
++ }
++
++ /* check AH and ESP */
++ switch(((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_satype) {
++ case SADB_SATYPE_AH:
++ if(!(((struct sadb_sa*)extensions[SADB_EXT_SA]) &&
++ ((struct sadb_sa*)extensions[SADB_EXT_SA])->sadb_sa_auth !=
++ SADB_AALG_NONE)) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_msg_parse: "
++ "auth alg is zero, must be non-zero for AH SAs.\n");
++ SENDERR(EINVAL);
++ }
++ if(((struct sadb_sa*)(extensions[SADB_EXT_SA]))->sadb_sa_encrypt !=
++ SADB_EALG_NONE) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_msg_parse: "
++ "AH handed encalg=%d, must be zero.\n",
++ ((struct sadb_sa*)(extensions[SADB_EXT_SA]))->sadb_sa_encrypt);
++ SENDERR(EINVAL);
++ }
++ break;
++ case SADB_SATYPE_ESP:
++ if(!(((struct sadb_sa*)extensions[SADB_EXT_SA]) &&
++ ((struct sadb_sa*)extensions[SADB_EXT_SA])->sadb_sa_encrypt !=
++ SADB_EALG_NONE)) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_msg_parse: "
++ "encrypt alg=%d is zero, must be non-zero for ESP=%d SAs.\n",
++ ((struct sadb_sa*)extensions[SADB_EXT_SA])->sadb_sa_encrypt,
++ ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_satype);
++ SENDERR(EINVAL);
++ }
++ if((((struct sadb_sa*)(extensions[SADB_EXT_SA]))->sadb_sa_encrypt ==
++ SADB_EALG_NULL) &&
++ (((struct sadb_sa*)(extensions[SADB_EXT_SA]))->sadb_sa_auth ==
++ SADB_AALG_NONE) ) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_msg_parse: "
++ "ESP handed encNULL+authNONE, illegal combination.\n");
++ SENDERR(EINVAL);
++ }
++ break;
++ case SADB_X_SATYPE_COMP:
++ if(!(((struct sadb_sa*)extensions[SADB_EXT_SA]) &&
++ ((struct sadb_sa*)extensions[SADB_EXT_SA])->sadb_sa_encrypt !=
++ SADB_EALG_NONE)) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_msg_parse: "
++ "encrypt alg=%d is zero, must be non-zero for COMP=%d SAs.\n",
++ ((struct sadb_sa*)extensions[SADB_EXT_SA])->sadb_sa_encrypt,
++ ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_satype);
++ SENDERR(EINVAL);
++ }
++ if(((struct sadb_sa*)(extensions[SADB_EXT_SA]))->sadb_sa_auth !=
++ SADB_AALG_NONE) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_msg_parse: "
++ "COMP handed auth=%d, must be zero.\n",
++ ((struct sadb_sa*)(extensions[SADB_EXT_SA]))->sadb_sa_auth);
++ SENDERR(EINVAL);
++ }
++ break;
++ default:
++ break;
++ }
++ if(ntohl(((struct sadb_sa*)(extensions[SADB_EXT_SA]))->sadb_sa_spi) <= 255) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_msg_parse: "
++ "spi=%08x must be > 255.\n",
++ ntohl(((struct sadb_sa*)(extensions[SADB_EXT_SA]))->sadb_sa_spi));
++ SENDERR(EINVAL);
++ }
++ default:
++ break;
++ }
++errlab:
++
++ return error;
++}
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.59 2004/04/18 03:03:49 mcr
++ * renamed common include files from pluto directory.
++ *
++ * Revision 1.58 2004/03/08 01:59:08 ken
++ * freeswan.h -> openswan.h
++ *
++ * Revision 1.57 2003/12/10 01:20:19 mcr
++ * NAT-traversal patches to KLIPS.
++ *
++ * Revision 1.56 2003/12/04 23:01:12 mcr
++ * removed ipsec_netlink.h
++ *
++ * Revision 1.55 2003/11/07 01:30:37 ken
++ * Cast sizeof() to int to keep things 64bit clean
++ *
++ * Revision 1.54 2003/10/31 02:27:12 mcr
++ * pulled up port-selector patches and sa_id elimination.
++ *
++ * Revision 1.53.20.2 2003/10/29 01:11:32 mcr
++ * added debugging for pfkey library.
++ *
++ * Revision 1.53.20.1 2003/09/21 13:59:44 mcr
++ * pre-liminary X.509 patch - does not yet pass tests.
++ *
++ * Revision 1.53 2003/01/30 02:32:09 rgb
++ *
++ * Rename SAref table macro names for clarity.
++ * Convert IPsecSAref_t from signed to unsigned to fix apparent SAref exhaustion bug.
++ *
++ * Revision 1.52 2002/12/30 06:53:07 mcr
++ * deal with short SA structures... #if 0 out for now. Probably
++ * not quite the right way.
++ *
++ * Revision 1.51 2002/12/13 18:16:02 mcr
++ * restored sa_ref code
++ *
++ * Revision 1.50 2002/12/13 18:06:52 mcr
++ * temporarily removed sadb_x_sa_ref reference for 2.xx
++ *
++ * Revision 1.49 2002/10/05 05:02:58 dhr
++ *
++ * C labels go on statements
++ *
++ * Revision 1.48 2002/09/20 15:40:45 rgb
++ * Added sadb_x_sa_ref to struct sadb_sa.
++ *
++ * Revision 1.47 2002/09/20 05:01:31 rgb
++ * Fixed usage of pfkey_lib_debug.
++ * Format for function declaration style consistency.
++ * Added text labels to elucidate numeric values presented.
++ * Re-organised debug output to reduce noise in output.
++ *
++ * Revision 1.46 2002/07/24 18:44:54 rgb
++ * Type fiddling to tame ia64 compiler.
++ *
++ * Revision 1.45 2002/05/23 07:14:11 rgb
++ * Cleaned up %p variants to 0p%p for test suite cleanup.
++ *
++ * Revision 1.44 2002/04/24 07:55:32 mcr
++ * #include patches and Makefiles for post-reorg compilation.
++ *
++ * Revision 1.43 2002/04/24 07:36:40 mcr
++ * Moved from ./lib/pfkey_v2_parse.c,v
++ *
++ * Revision 1.42 2002/01/29 22:25:36 rgb
++ * Re-add ipsec_kversion.h to keep MALLOC happy.
++ *
++ * Revision 1.41 2002/01/29 01:59:10 mcr
++ * removal of kversions.h - sources that needed it now use ipsec_param.h.
++ * updating of IPv6 structures to match latest in6.h version.
++ * removed dead code from openswan.h that also duplicated kversions.h
++ * code.
++ *
++ * Revision 1.40 2002/01/20 20:34:50 mcr
++ * added pfkey_v2_sadb_type_string to decode sadb_type to string.
++ *
++ * Revision 1.39 2001/11/27 05:29:22 mcr
++ * pfkey parses are now maintained by a structure
++ * that includes their name for debug purposes.
++ * DEBUGGING() macro changed so that it takes a debug
++ * level so that pf_key() can use this to decode the
++ * structures without innundanting humans.
++ * Also uses pfkey_v2_sadb_ext_string() in messages.
++ *
++ * Revision 1.38 2001/11/06 19:47:47 rgb
++ * Added packet parameter to lifetime and comb structures.
++ *
++ * Revision 1.37 2001/10/18 04:45:24 rgb
++ * 2.4.9 kernel deprecates linux/malloc.h in favour of linux/slab.h,
++ * lib/openswan.h version macros moved to lib/kversions.h.
++ * Other compiler directive cleanups.
++ *
++ * Revision 1.36 2001/06/14 19:35:16 rgb
++ * Update copyright date.
++ *
++ * Revision 1.35 2001/05/03 19:44:51 rgb
++ * Standardise on SENDERR() macro.
++ *
++ * Revision 1.34 2001/03/16 07:41:51 rgb
++ * Put openswan.h include before pluto includes.
++ *
++ * Revision 1.33 2001/02/27 07:13:51 rgb
++ * Added satype2name() function.
++ * Added text to default satype_tbl entry.
++ * Added satype2name() conversions for most satype debug output.
++ *
++ * Revision 1.32 2001/02/26 20:01:09 rgb
++ * Added internal IP protocol 61 for magic SAs.
++ * Ditch unused sadb_satype2proto[], replaced by satype2proto().
++ * Re-formatted debug output (split lines, consistent spacing).
++ * Removed acquire, register and expire requirements for a known satype.
++ * Changed message type checking to a switch structure.
++ * Verify expected NULL auth for IPCOMP.
++ * Enforced spi > 0x100 requirement, now that pass uses a magic SA for
++ * appropriate message types.
++ *
++ * Revision 1.31 2000/12/01 07:09:00 rgb
++ * Added ipcomp sanity check to require encalgo is set.
++ *
++ * Revision 1.30 2000/11/17 18:10:30 rgb
++ * Fixed bugs mostly relating to spirange, to treat all spi variables as
++ * network byte order since this is the way PF_KEYv2 stored spis.
++ *
++ * Revision 1.29 2000/10/12 00:02:39 rgb
++ * Removed 'format, ##' nonsense from debug macros for RH7.0.
++ *
++ * Revision 1.28 2000/09/20 16:23:04 rgb
++ * Remove over-paranoid extension check in the presence of sadb_msg_errno.
++ *
++ * Revision 1.27 2000/09/20 04:04:21 rgb
++ * Changed static functions to DEBUG_NO_STATIC to reveal function names in
++ * oopsen.
++ *
++ * Revision 1.26 2000/09/15 11:37:02 rgb
++ * Merge in heavily modified Svenning Soerensen's <svenning@post5.tele.dk>
++ * IPCOMP zlib deflate code.
++ *
++ * Revision 1.25 2000/09/12 22:35:37 rgb
++ * Restructured to remove unused extensions from CLEARFLOW messages.
++ *
++ * Revision 1.24 2000/09/12 18:59:54 rgb
++ * Added Gerhard's IPv6 support to pfkey parts of libopenswan.
++ *
++ * Revision 1.23 2000/09/12 03:27:00 rgb
++ * Moved DEBUGGING definition to compile kernel with debug off.
++ *
++ * Revision 1.22 2000/09/09 06:39:27 rgb
++ * Restrict pfkey errno check to downward messages only.
++ *
++ * Revision 1.21 2000/09/08 19:22:34 rgb
++ * Enabled pfkey_sens_parse().
++ * Added check for errno on downward acquire messages only.
++ *
++ * Revision 1.20 2000/09/01 18:48:23 rgb
++ * Fixed reserved check bug and added debug output in
++ * pfkey_supported_parse().
++ * Fixed debug output label bug in pfkey_ident_parse().
++ *
++ * Revision 1.19 2000/08/27 01:55:26 rgb
++ * Define OCTETBITS and PFKEYBITS to avoid using 'magic' numbers in code.
++ *
++ * Revision 1.18 2000/08/24 17:00:36 rgb
++ * Ignore unknown extensions instead of failing.
++ *
++ * Revision 1.17 2000/06/02 22:54:14 rgb
++ * Added Gerhard Gessler's struct sockaddr_storage mods for IPv6 support.
++ *
++ * Revision 1.16 2000/05/10 19:25:11 rgb
++ * Fleshed out proposal and supported extensions.
++ *
++ * Revision 1.15 2000/01/24 21:15:31 rgb
++ * Added disabled pluto pfkey lib debug flag.
++ * Added algo debugging reporting.
++ *
++ * Revision 1.14 2000/01/22 23:24:29 rgb
++ * Added new functions proto2satype() and satype2proto() and lookup
++ * table satype_tbl. Also added proto2name() since it was easy.
++ *
++ * Revision 1.13 2000/01/21 09:43:59 rgb
++ * Cast ntohl(spi) as (unsigned long int) to shut up compiler.
++ *
++ * Revision 1.12 2000/01/21 06:28:19 rgb
++ * Added address cases for eroute flows.
++ * Indented compiler directives for readability.
++ * Added klipsdebug switching capability.
++ *
++ * Revision 1.11 1999/12/29 21:14:59 rgb
++ * Fixed debug text cut and paste typo.
++ *
++ * Revision 1.10 1999/12/10 17:45:24 rgb
++ * Added address debugging.
++ *
++ * Revision 1.9 1999/12/09 23:11:42 rgb
++ * Ditched <string.h> include since we no longer use memset().
++ * Use new pfkey_extensions_init() instead of memset().
++ * Added check for SATYPE in pfkey_msg_build().
++ * Tidy up comments and debugging comments.
++ *
++ * Revision 1.8 1999/12/07 19:55:26 rgb
++ * Removed unused first argument from extension parsers.
++ * Removed static pluto debug flag.
++ * Moved message type and state checking to pfkey_msg_parse().
++ * Changed print[fk] type from lx to x to quiet compiler.
++ * Removed redundant remain check.
++ * Changed __u* types to uint* to avoid use of asm/types.h and
++ * sys/types.h in userspace code.
++ *
++ * Revision 1.7 1999/12/01 22:20:51 rgb
++ * Moved pfkey_lib_debug variable into the library.
++ * Added pfkey version check into header parsing.
++ * Added check for SATYPE only for those extensions that require a
++ * non-zero value.
++ *
++ * Revision 1.6 1999/11/27 11:58:05 rgb
++ * Added ipv6 headers.
++ * Moved sadb_satype2proto protocol lookup table from
++ * klips/net/ipsec/pfkey_v2_parser.c.
++ * Enable lifetime_current checking.
++ * Debugging error messages added.
++ * Add argument to pfkey_msg_parse() for direction.
++ * Consolidated the 4 1-d extension bitmap arrays into one 4-d array.
++ * Add CVS log entry to bottom of file.
++ * Moved auth and enc alg check to pfkey_msg_parse().
++ * Enable accidentally disabled spirange parsing.
++ * Moved protocol/algorithm checks from klips/net/ipsec/pfkey_v2_parser.c
++ *
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/libfreeswan/prng.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,201 @@
++/*
++ * crypto-class pseudorandom number generator
++ * currently uses same algorithm as RC4(TM), from Schneier 2nd ed p397
++ * Copyright (C) 2002 Henry Spencer.
++ *
++ * This library is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU Library General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/lgpl.txt>.
++ *
++ * This library 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 Library General Public
++ * License for more details.
++ *
++ * RCSID $Id$
++ */
++#include "openswan.h"
++
++/*
++ - prng_init - initialize PRNG from a key
++ */
++void
++prng_init(prng, key, keylen)
++struct prng *prng;
++const unsigned char *key;
++size_t keylen;
++{
++ unsigned char k[256];
++ int i, j;
++ unsigned const char *p;
++ unsigned const char *keyend = key + keylen;
++ unsigned char t;
++
++ for (i = 0; i <= 255; i++)
++ prng->sbox[i] = i;
++ p = key;
++ for (i = 0; i <= 255; i++) {
++ k[i] = *p++;
++ if (p >= keyend)
++ p = key;
++ }
++ j = 0;
++ for (i = 0; i <= 255; i++) {
++ j = (j + prng->sbox[i] + k[i]) & 0xff;
++ t = prng->sbox[i];
++ prng->sbox[i] = prng->sbox[j];
++ prng->sbox[j] = t;
++ k[i] = 0; /* clear out key memory */
++ }
++ prng->i = 0;
++ prng->j = 0;
++ prng->count = 0;
++}
++
++/*
++ - prng_bytes - get some pseudorandom bytes from PRNG
++ */
++void
++prng_bytes(prng, dst, dstlen)
++struct prng *prng;
++unsigned char *dst;
++size_t dstlen;
++{
++ int i, j, t;
++ unsigned char *p = dst;
++ size_t remain = dstlen;
++# define MAX 4000000000ul
++
++ while (remain > 0) {
++ i = (prng->i + 1) & 0xff;
++ prng->i = i;
++ j = (prng->j + prng->sbox[i]) & 0xff;
++ prng->j = j;
++ t = prng->sbox[i];
++ prng->sbox[i] = prng->sbox[j];
++ prng->sbox[j] = t;
++ t = (t + prng->sbox[i]) & 0xff;
++ *p++ = prng->sbox[t];
++ remain--;
++ }
++ if (prng->count < MAX - dstlen)
++ prng->count += dstlen;
++ else
++ prng->count = MAX;
++}
++
++/*
++ - prnt_count - how many bytes have been extracted from PRNG so far?
++ */
++unsigned long
++prng_count(prng)
++struct prng *prng;
++{
++ return prng->count;
++}
++
++/*
++ - prng_final - clear out PRNG to ensure nothing left in memory
++ */
++void
++prng_final(prng)
++struct prng *prng;
++{
++ int i;
++
++ for (i = 0; i <= 255; i++)
++ prng->sbox[i] = 0;
++ prng->i = 0;
++ prng->j = 0;
++ prng->count = 0; /* just for good measure */
++}
++
++
++
++#ifdef PRNG_MAIN
++
++#include <stdio.h>
++
++void regress();
++
++int
++main(argc, argv)
++int argc;
++char *argv[];
++{
++ struct prng pr;
++ unsigned char buf[100];
++ unsigned char *p;
++ size_t n;
++
++ if (argc < 2) {
++ fprintf(stderr, "Usage: %s {key|-r}\n", argv[0]);
++ exit(2);
++ }
++
++ if (strcmp(argv[1], "-r") == 0) {
++ regress();
++ fprintf(stderr, "regress() returned?!?\n");
++ exit(1);
++ }
++
++ prng_init(&pr, argv[1], strlen(argv[1]));
++ prng_bytes(&pr, buf, 32);
++ printf("0x");
++ for (p = buf, n = 32; n > 0; p++, n--)
++ printf("%02x", *p);
++ printf("\n%lu bytes\n", prng_count(&pr));
++ prng_final(&pr);
++ exit(0);
++}
++
++void
++regress()
++{
++ struct prng pr;
++ unsigned char buf[100];
++ unsigned char *p;
++ size_t n;
++ /* somewhat non-random sample key */
++ unsigned char key[] = "here we go gathering nuts in May";
++ /* first thirty bytes of output from that key */
++ unsigned char good[] = "\x3f\x02\x8e\x4a\x2a\xea\x23\x18\x92\x7c"
++ "\x09\x52\x83\x61\xaa\x26\xce\xbb\x9d\x71"
++ "\x71\xe5\x10\x22\xaf\x60\x54\x8d\x5b\x28";
++ int nzero, none;
++ int show = 0;
++
++ prng_init(&pr, key, strlen(key));
++ prng_bytes(&pr, buf, sizeof(buf));
++ for (p = buf, n = sizeof(buf); n > 0; p++, n--) {
++ if (*p == 0)
++ nzero++;
++ if (*p == 255)
++ none++;
++ }
++ if (nzero > 3 || none > 3) {
++ fprintf(stderr, "suspiciously non-random output!\n");
++ show = 1;
++ }
++ if (memcmp(buf, good, strlen(good)) != 0) {
++ fprintf(stderr, "incorrect output!\n");
++ show = 1;
++ }
++ if (show) {
++ fprintf(stderr, "0x");
++ for (p = buf, n = sizeof(buf); n > 0; p++, n--)
++ fprintf(stderr, "%02x", *p);
++ fprintf(stderr, "\n");
++ exit(1);
++ }
++ if (prng_count(&pr) != sizeof(buf)) {
++ fprintf(stderr, "got %u bytes, but count is %lu\n",
++ sizeof(buf), prng_count(&pr));
++ exit(1);
++ }
++ prng_final(&pr);
++ exit(0);
++}
++
++#endif /* PRNG_MAIN */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/libfreeswan/rangetoa.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,60 @@
++/*
++ * convert binary form of address range to ASCII
++ * Copyright (C) 1998, 1999 Henry Spencer.
++ *
++ * This library is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU Library General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/lgpl.txt>.
++ *
++ * This library 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 Library General Public
++ * License for more details.
++ *
++ * RCSID $Id$
++ */
++#include "openswan.h"
++
++/*
++ - rangetoa - convert address range to ASCII
++ */
++size_t /* space needed for full conversion */
++rangetoa(addrs, format, dst, dstlen)
++struct in_addr addrs[2];
++int format; /* character */
++char *dst; /* need not be valid if dstlen is 0 */
++size_t dstlen;
++{
++ size_t len;
++ size_t rest;
++ int n;
++ char *p;
++
++ switch (format) {
++ case 0:
++ break;
++ default:
++ return 0;
++ break;
++ }
++
++ len = addrtoa(addrs[0], 0, dst, dstlen);
++ if (len < dstlen)
++ for (p = dst + len - 1, n = 3; len < dstlen && n > 0;
++ p++, len++, n--)
++ *p = '.';
++ else
++ p = NULL;
++ if (len < dstlen)
++ rest = dstlen - len;
++ else {
++ if (dstlen > 0)
++ *(dst + dstlen - 1) = '\0';
++ rest = 0;
++ }
++
++ len += addrtoa(addrs[1], 0, p, rest);
++
++ return len;
++}
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/libfreeswan/satot.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,133 @@
++/*
++ * convert from binary form of SA ID to text
++ * Copyright (C) 2000, 2001 Henry Spencer.
++ *
++ * This library is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU Library General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/lgpl.txt>.
++ *
++ * This library 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 Library General Public
++ * License for more details.
++ *
++ * RCSID $Id$
++ */
++#include "openswan.h"
++
++static struct typename {
++ char type;
++ char *name;
++} typenames[] = {
++ { SA_AH, "ah" },
++ { SA_ESP, "esp" },
++ { SA_IPIP, "tun" },
++ { SA_COMP, "comp" },
++ { SA_INT, "int" },
++ { 0, NULL }
++};
++
++/*
++ - satot - convert SA to text "ah507@1.2.3.4"
++ */
++size_t /* space needed for full conversion */
++satot(sa, format, dst, dstlen)
++const ip_said *sa;
++int format; /* character */
++char *dst; /* need not be valid if dstlen is 0 */
++size_t dstlen;
++{
++ size_t len = 0; /* 0 means "not recognized yet" */
++ int base;
++ int showversion; /* use delimiter to show IP version? */
++ struct typename *tn;
++ char *p;
++ char *pre;
++ char buf[10+1+ULTOT_BUF+ADDRTOT_BUF];
++ char unk[10];
++
++ switch (format) {
++ case 0:
++ base = 16;
++ showversion = 1;
++ break;
++ case 'f':
++ base = 17;
++ showversion = 1;
++ break;
++ case 'x':
++ base = 'x';
++ showversion = 0;
++ break;
++ case 'd':
++ base = 10;
++ showversion = 0;
++ break;
++ default:
++ return 0;
++ break;
++ }
++
++ memset(buf, 0, sizeof(buf));
++
++ pre = NULL;
++ for (tn = typenames; tn->name != NULL; tn++)
++ if (sa->proto == tn->type) {
++ pre = tn->name;
++ break; /* NOTE BREAK OUT */
++ }
++ if (pre == NULL) { /* unknown protocol */
++ strcpy(unk, "unk");
++ (void) ultot((unsigned char)sa->proto, 10, unk+strlen(unk),
++ sizeof(unk)-strlen(unk));
++ pre = unk;
++ }
++
++ if (strcmp(pre, PASSTHROUGHTYPE) == 0 &&
++ sa->spi == PASSTHROUGHSPI &&
++ isunspecaddr(&sa->dst)) {
++ strcpy(buf, (addrtypeof(&sa->dst) == AF_INET) ?
++ PASSTHROUGH4NAME :
++ PASSTHROUGH6NAME);
++ len = strlen(buf);
++ }
++
++ if (sa->proto == SA_INT) {
++ switch (ntohl(sa->spi)) {
++ case SPI_PASS: p = "%pass"; break;
++ case SPI_DROP: p = "%drop"; break;
++ case SPI_REJECT: p = "%reject"; break;
++ case SPI_HOLD: p = "%hold"; break;
++ case SPI_TRAP: p = "%trap"; break;
++ case SPI_TRAPSUBNET: p = "%trapsubnet"; break;
++ default: p = NULL; break;
++ }
++ if (p != NULL) {
++ strcpy(buf, p);
++ len = strlen(buf);
++ }
++ }
++
++ if (len == 0) { /* general case needed */
++ strcpy(buf, pre);
++ len = strlen(buf);
++ if (showversion) {
++ *(buf+len) = (addrtypeof(&sa->dst) == AF_INET) ? '.' :
++ ':';
++ len++;
++ *(buf+len) = '\0';
++ }
++ len += ultot(ntohl(sa->spi), base, buf+len, sizeof(buf)-len);
++ *(buf+len-1) = '@';
++ len += addrtot(&sa->dst, 0, buf+len, sizeof(buf)-len);
++ *(buf+len) = '\0';
++ }
++
++ if (dst != NULL) {
++ if (len > dstlen)
++ *(buf+dstlen-1) = '\0';
++ strcpy(dst, buf);
++ }
++ return len;
++}
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/libfreeswan/subnetof.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,59 @@
++/*
++ * minor network-address manipulation utilities
++ * Copyright (C) 1998, 1999 Henry Spencer.
++ *
++ * This library is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU Library General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/lgpl.txt>.
++ *
++ * This library 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 Library General Public
++ * License for more details.
++ *
++ * RCSID $Id$
++ */
++#include "openswan.h"
++
++/*
++ - subnetof - given address and mask, return subnet part
++ */
++struct in_addr
++subnetof(addr, mask)
++struct in_addr addr;
++struct in_addr mask;
++{
++ struct in_addr result;
++
++ result.s_addr = addr.s_addr & mask.s_addr;
++ return result;
++}
++
++/*
++ - hostof - given address and mask, return host part
++ */
++struct in_addr
++hostof(addr, mask)
++struct in_addr addr;
++struct in_addr mask;
++{
++ struct in_addr result;
++
++ result.s_addr = addr.s_addr & ~mask.s_addr;
++ return result;
++}
++
++/*
++ - broadcastof - given (network) address and mask, return broadcast address
++ */
++struct in_addr
++broadcastof(addr, mask)
++struct in_addr addr;
++struct in_addr mask;
++{
++ struct in_addr result;
++
++ result.s_addr = addr.s_addr | ~mask.s_addr;
++ return result;
++}
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/libfreeswan/subnettoa.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,61 @@
++/*
++ * convert binary form of subnet description to ASCII
++ * Copyright (C) 1998, 1999 Henry Spencer.
++ *
++ * This library is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU Library General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/lgpl.txt>.
++ *
++ * This library 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 Library General Public
++ * License for more details.
++ *
++ * RCSID $Id$
++ */
++#include "openswan.h"
++
++/*
++ - subnettoa - convert address and mask to ASCII "addr/mask"
++ * Output expresses the mask as a bit count if possible, else dotted decimal.
++ */
++size_t /* space needed for full conversion */
++subnettoa(addr, mask, format, dst, dstlen)
++struct in_addr addr;
++struct in_addr mask;
++int format; /* character */
++char *dst; /* need not be valid if dstlen is 0 */
++size_t dstlen;
++{
++ size_t len;
++ size_t rest;
++ int n;
++ char *p;
++
++ switch (format) {
++ case 0:
++ break;
++ default:
++ return 0;
++ break;
++ }
++
++ len = addrtoa(addr, 0, dst, dstlen);
++ if (len < dstlen) {
++ dst[len - 1] = '/';
++ p = dst + len;
++ rest = dstlen - len;
++ } else {
++ p = NULL;
++ rest = 0;
++ }
++
++ n = masktobits(mask);
++ if (n >= 0)
++ len += ultoa((unsigned long)n, 10, p, rest);
++ else
++ len += addrtoa(mask, 0, p, rest);
++
++ return len;
++}
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/libfreeswan/ultoa.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,66 @@
++/*
++ * convert unsigned long to ASCII
++ * Copyright (C) 1998, 1999 Henry Spencer.
++ *
++ * This library is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU Library General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/lgpl.txt>.
++ *
++ * This library 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 Library General Public
++ * License for more details.
++ *
++ * RCSID $Id$
++ */
++#include "openswan.h"
++
++/*
++ - ultoa - convert unsigned long to decimal ASCII
++ */
++size_t /* length required for full conversion */
++ultoa(n, base, dst, dstlen)
++unsigned long n;
++int base;
++char *dst; /* need not be valid if dstlen is 0 */
++size_t dstlen;
++{
++ char buf[3*sizeof(unsigned long) + 1];
++ char *bufend = buf + sizeof(buf);
++ size_t len;
++ char *p;
++ static char hex[] = "0123456789abcdef";
++
++ p = bufend;
++ *--p = '\0';
++ if (base == 10) {
++ do {
++ *--p = n%10 + '0';
++ n /= 10;
++ } while (n != 0);
++ } else if (base == 16) {
++ do {
++ *--p = hex[n&0xf];
++ n >>= 4;
++ } while (n != 0);
++ *--p = 'x';
++ *--p = '0';
++ } else if (base == 8) {
++ do {
++ *--p = (n&07) + '0';
++ n >>= 3;
++ } while (n != 0);
++ *--p = '0';
++ } else
++ *--p = '?';
++
++ len = bufend - p;
++
++ if (dstlen > 0) {
++ if (len > dstlen)
++ *(p + dstlen - 1) = '\0';
++ strcpy(dst, p);
++ }
++ return len;
++}
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/libfreeswan/ultot.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,82 @@
++/*
++ * convert unsigned long to text
++ * Copyright (C) 2000 Henry Spencer.
++ *
++ * This library is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU Library General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/lgpl.txt>.
++ *
++ * This library 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 Library General Public
++ * License for more details.
++ *
++ * RCSID $Id$
++ */
++#include "openswan.h"
++
++/*
++ - ultot - convert unsigned long to text
++ */
++size_t /* length required for full conversion */
++ultot(n, base, dst, dstlen)
++unsigned long n;
++int base;
++char *dst; /* need not be valid if dstlen is 0 */
++size_t dstlen;
++{
++ char buf[3*sizeof(unsigned long) + 1];
++ char *bufend = buf + sizeof(buf);
++ size_t len;
++ char *p;
++ static char hex[] = "0123456789abcdef";
++# define HEX32 (32/4)
++
++ p = bufend;
++ *--p = '\0';
++ switch (base) {
++ case 10:
++ case 'd':
++ do {
++ *--p = n%10 + '0';
++ n /= 10;
++ } while (n != 0);
++ break;
++ case 16:
++ case 17:
++ case 'x':
++ do {
++ *--p = hex[n&0xf];
++ n >>= 4;
++ } while (n != 0);
++ if (base == 17)
++ while (bufend - p < HEX32 + 1)
++ *--p = '0';
++ if (base == 'x') {
++ *--p = 'x';
++ *--p = '0';
++ }
++ break;
++ case 8:
++ case 'o':
++ do {
++ *--p = (n&07) + '0';
++ n >>= 3;
++ } while (n != 0);
++ if (base == 'o')
++ *--p = '0';
++ break;
++ default:
++ return 0;
++ break;
++ }
++
++ len = bufend - p;
++ if (dstlen > 0) {
++ if (len > dstlen)
++ *(p + dstlen - 1) = '\0';
++ strcpy(dst, p);
++ }
++ return len;
++}
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/zlib/Makefile Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,118 @@
++# (kernel) Makefile for IPCOMP zlib deflate code
++# Copyright (C) 1998, 1999, 2000, 2001 Richard Guy Briggs.
++# Copyright (C) 2000 Svenning Soerensen
++#
++# This program is free software; you can redistribute it and/or modify it
++# under the terms of the GNU General Public License as published by the
++# Free Software Foundation; either version 2 of the License, or (at your
++# option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++#
++# This program is distributed in the hope that it will be useful, but
++# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++# for more details.
++#
++# RCSID $Id$
++#
++
++
++
++include ../Makefile.inc
++
++
++
++ifndef TOPDIR
++TOPDIR := /usr/src/linux
++endif
++
++
++L_TARGET := zlib.a
++
++obj-y :=
++
++include Makefile.objs
++
++EXTRA_CFLAGS += $(KLIPSCOMPILE)
++
++EXTRA_CFLAGS += -Wall
++#EXTRA_CFLAGS += -Wconversion
++#EXTRA_CFLAGS += -Wmissing-prototypes
++EXTRA_CFLAGS += -Wpointer-arith
++#EXTRA_CFLAGS += -Wcast-qual
++#EXTRA_CFLAGS += -Wmissing-declarations
++EXTRA_CFLAGS += -Wstrict-prototypes
++#EXTRA_CFLAGS += -pedantic
++#EXTRA_CFLAGS += -W
++#EXTRA_CFLAGS += -Wwrite-strings
++EXTRA_CFLAGS += -Wbad-function-cast
++EXTRA_CFLAGS += -DIPCOMP_PREFIX
++
++.S.o:
++ $(CC) -D__ASSEMBLY__ -DNO_UNDERLINE -traditional -c $< -o $*.o
++
++asm-obj-$(CONFIG_M586) += match586.o
++asm-obj-$(CONFIG_M586TSC) += match586.o
++asm-obj-$(CONFIG_M586MMX) += match586.o
++asm-obj-$(CONFIG_M686) += match686.o
++asm-obj-$(CONFIG_MPENTIUMIII) += match686.o
++asm-obj-$(CONFIG_MPENTIUM4) += match686.o
++asm-obj-$(CONFIG_MK6) += match586.o
++asm-obj-$(CONFIG_MK7) += match686.o
++asm-obj-$(CONFIG_MCRUSOE) += match586.o
++asm-obj-$(CONFIG_MWINCHIPC6) += match586.o
++asm-obj-$(CONFIG_MWINCHIP2) += match686.o
++asm-obj-$(CONFIG_MWINCHIP3D) += match686.o
++
++obj-y += $(asm-obj-y)
++ifneq ($(strip $(asm-obj-y)),)
++ EXTRA_CFLAGS += -DASMV
++endif
++
++active-objs := $(sort $(obj-y) $(obj-m))
++L_OBJS := $(obj-y)
++M_OBJS := $(obj-m)
++MIX_OBJS := $(filter $(export-objs), $(active-objs))
++
++include $(TOPDIR)/Rules.make
++
++$(obj-y) : $(TOPDIR)/include/linux/config.h $(TOPDIR)/include/linux/autoconf.h
++
++
++clean:
++ -rm -f *.o *.a
++
++checkprograms:
++programs: $(L_TARGET)
++
++#
++# $Log$
++# Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++# Turn off EOLN_NATIVE flag
++#
++# (Logical change 1.5010)
++#
++# Revision 1.9 2002/04/24 07:55:32 mcr
++# #include patches and Makefiles for post-reorg compilation.
++#
++# Revision 1.8 2002/04/24 07:36:44 mcr
++# Moved from ./zlib/Makefile,v
++#
++# Revision 1.7 2002/03/27 23:34:35 mcr
++# added programs: target
++#
++# Revision 1.6 2001/12/05 20:19:08 henry
++# use new compile-control variable
++#
++# Revision 1.5 2001/11/27 16:38:08 mcr
++# added new "checkprograms" target to deal with programs that
++# are required for "make check", but that may not be ready to
++# build for every user due to external dependancies.
++#
++# Revision 1.4 2001/10/24 14:46:24 henry
++# Makefile.inc
++#
++# Revision 1.3 2001/04/21 23:05:24 rgb
++# Update asm directives for 2.4 style makefiles.
++#
++# Revision 1.2 2001/01/29 22:22:00 rgb
++# Convert to 2.4 new style with back compat.
++#
++# Revision 1.1.1.1 2000/09/29 18:51:33 rgb
++# zlib_beginnings
++#
++#
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/zlib/Makefile.objs Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,27 @@
++obj-$(CONFIG_IPSEC_IPCOMP) += adler32.o
++obj-$(CONFIG_IPSEC_IPCOMP) += deflate.o
++obj-$(CONFIG_IPSEC_IPCOMP) += infblock.o
++obj-$(CONFIG_IPSEC_IPCOMP) += infcodes.o
++obj-$(CONFIG_IPSEC_IPCOMP) += inffast.o
++obj-$(CONFIG_IPSEC_IPCOMP) += inflate.o
++obj-$(CONFIG_IPSEC_IPCOMP) += inftrees.o
++obj-$(CONFIG_IPSEC_IPCOMP) += infutil.o
++obj-$(CONFIG_IPSEC_IPCOMP) += trees.o
++obj-$(CONFIG_IPSEC_IPCOMP) += zutil.o
++
++asm-obj-$(CONFIG_M586) += ${LIBZLIBSRCDIR}/match586.o
++asm-obj-$(CONFIG_M586TSC) += ${LIBZLIBSRCDIR}/match586.o
++asm-obj-$(CONFIG_M586MMX) += ${LIBZLIBSRCDIR}/match586.o
++asm-obj-$(CONFIG_M686) += ${LIBZLIBSRCDIR}/match686.o
++asm-obj-$(CONFIG_MPENTIUMIII) += ${LIBZLIBSRCDIR}/match686.o
++asm-obj-$(CONFIG_MPENTIUM4) += ${LIBZLIBSRCDIR}/match686.o
++asm-obj-$(CONFIG_MK6) += ${LIBZLIBSRCDIR}/match586.o
++asm-obj-$(CONFIG_MK7) += ${LIBZLIBSRCDIR}/match686.o
++asm-obj-$(CONFIG_MCRUSOE) += ${LIBZLIBSRCDIR}/match586.o
++asm-obj-$(CONFIG_MWINCHIPC6) += ${LIBZLIBSRCDIR}/match586.o
++asm-obj-$(CONFIG_MWINCHIP2) += ${LIBZLIBSRCDIR}/match686.o
++asm-obj-$(CONFIG_MWINCHIP3D) += ${LIBZLIBSRCDIR}/match686.o
++
++EXTRA_CFLAGS += -DIPCOMP_PREFIX
++
++
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/zlib/README Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,147 @@
++zlib 1.1.4 is a general purpose data compression library. All the code
++is thread safe. The data format used by the zlib library
++is described by RFCs (Request for Comments) 1950 to 1952 in the files
++http://www.ietf.org/rfc/rfc1950.txt (zlib format), rfc1951.txt (deflate
++format) and rfc1952.txt (gzip format). These documents are also available in
++other formats from ftp://ftp.uu.net/graphics/png/documents/zlib/zdoc-index.html
++
++All functions of the compression library are documented in the file zlib.h
++(volunteer to write man pages welcome, contact jloup@gzip.org). A usage
++example of the library is given in the file example.c which also tests that
++the library is working correctly. Another example is given in the file
++minigzip.c. The compression library itself is composed of all source files
++except example.c and minigzip.c.
++
++To compile all files and run the test program, follow the instructions
++given at the top of Makefile. In short "make test; make install"
++should work for most machines. For Unix: "./configure; make test; make install"
++For MSDOS, use one of the special makefiles such as Makefile.msc.
++For VMS, use Make_vms.com or descrip.mms.
++
++Questions about zlib should be sent to <zlib@gzip.org>, or to
++Gilles Vollant <info@winimage.com> for the Windows DLL version.
++The zlib home page is http://www.zlib.org or http://www.gzip.org/zlib/
++Before reporting a problem, please check this site to verify that
++you have the latest version of zlib; otherwise get the latest version and
++check whether the problem still exists or not.
++
++PLEASE read the zlib FAQ http://www.gzip.org/zlib/zlib_faq.html
++before asking for help.
++
++Mark Nelson <markn@ieee.org> wrote an article about zlib for the Jan. 1997
++issue of Dr. Dobb's Journal; a copy of the article is available in
++http://dogma.net/markn/articles/zlibtool/zlibtool.htm
++
++The changes made in version 1.1.4 are documented in the file ChangeLog.
++The only changes made since 1.1.3 are bug corrections:
++
++- ZFREE was repeated on same allocation on some error conditions.
++ This creates a security problem described in
++ http://www.zlib.org/advisory-2002-03-11.txt
++- Returned incorrect error (Z_MEM_ERROR) on some invalid data
++- Avoid accesses before window for invalid distances with inflate window
++ less than 32K.
++- force windowBits > 8 to avoid a bug in the encoder for a window size
++ of 256 bytes. (A complete fix will be available in 1.1.5).
++
++The beta version 1.1.5beta includes many more changes. A new official
++version 1.1.5 will be released as soon as extensive testing has been
++completed on it.
++
++
++Unsupported third party contributions are provided in directory "contrib".
++
++A Java implementation of zlib is available in the Java Development Kit
++http://www.javasoft.com/products/JDK/1.1/docs/api/Package-java.util.zip.html
++See the zlib home page http://www.zlib.org for details.
++
++A Perl interface to zlib written by Paul Marquess <pmarquess@bfsec.bt.co.uk>
++is in the CPAN (Comprehensive Perl Archive Network) sites
++http://www.cpan.org/modules/by-module/Compress/
++
++A Python interface to zlib written by A.M. Kuchling <amk@magnet.com>
++is available in Python 1.5 and later versions, see
++http://www.python.org/doc/lib/module-zlib.html
++
++A zlib binding for TCL written by Andreas Kupries <a.kupries@westend.com>
++is availlable at http://www.westend.com/~kupries/doc/trf/man/man.html
++
++An experimental package to read and write files in .zip format,
++written on top of zlib by Gilles Vollant <info@winimage.com>, is
++available at http://www.winimage.com/zLibDll/unzip.html
++and also in the contrib/minizip directory of zlib.
++
++
++Notes for some targets:
++
++- To build a Windows DLL version, include in a DLL project zlib.def, zlib.rc
++ and all .c files except example.c and minigzip.c; compile with -DZLIB_DLL
++ The zlib DLL support was initially done by Alessandro Iacopetti and is
++ now maintained by Gilles Vollant <info@winimage.com>. Check the zlib DLL
++ home page at http://www.winimage.com/zLibDll
++
++ From Visual Basic, you can call the DLL functions which do not take
++ a structure as argument: compress, uncompress and all gz* functions.
++ See contrib/visual-basic.txt for more information, or get
++ http://www.tcfb.com/dowseware/cmp-z-it.zip
++
++- For 64-bit Irix, deflate.c must be compiled without any optimization.
++ With -O, one libpng test fails. The test works in 32 bit mode (with
++ the -n32 compiler flag). The compiler bug has been reported to SGI.
++
++- zlib doesn't work with gcc 2.6.3 on a DEC 3000/300LX under OSF/1 2.1
++ it works when compiled with cc.
++
++- on Digital Unix 4.0D (formely OSF/1) on AlphaServer, the cc option -std1
++ is necessary to get gzprintf working correctly. This is done by configure.
++
++- zlib doesn't work on HP-UX 9.05 with some versions of /bin/cc. It works
++ with other compilers. Use "make test" to check your compiler.
++
++- gzdopen is not supported on RISCOS, BEOS and by some Mac compilers.
++
++- For Turbo C the small model is supported only with reduced performance to
++ avoid any far allocation; it was tested with -DMAX_WBITS=11 -DMAX_MEM_LEVEL=3
++
++- For PalmOs, see http://www.cs.uit.no/~perm/PASTA/pilot/software.html
++ Per Harald Myrvang <perm@stud.cs.uit.no>
++
++
++Acknowledgments:
++
++ The deflate format used by zlib was defined by Phil Katz. The deflate
++ and zlib specifications were written by L. Peter Deutsch. Thanks to all the
++ people who reported problems and suggested various improvements in zlib;
++ they are too numerous to cite here.
++
++Copyright notice:
++
++ (C) 1995-2002 Jean-loup Gailly and Mark Adler
++
++ This software is provided 'as-is', without any express or implied
++ warranty. In no event will the authors be held liable for any damages
++ arising from the use of this software.
++
++ Permission is granted to anyone to use this software for any purpose,
++ including commercial applications, and to alter it and redistribute it
++ freely, subject to the following restrictions:
++
++ 1. The origin of this software must not be misrepresented; you must not
++ claim that you wrote the original software. If you use this software
++ in a product, an acknowledgment in the product documentation would be
++ appreciated but is not required.
++ 2. Altered source versions must be plainly marked as such, and must not be
++ misrepresented as being the original software.
++ 3. This notice may not be removed or altered from any source distribution.
++
++ Jean-loup Gailly Mark Adler
++ jloup@gzip.org madler@alumni.caltech.edu
++
++If you use the zlib library in a product, we would appreciate *not*
++receiving lengthy legal documents to sign. The sources are provided
++for free but without warranty of any kind. The library has been
++entirely written by Jean-loup Gailly and Mark Adler; it does not
++include third-party code.
++
++If you redistribute modified sources, we would appreciate that you include
++in the file ChangeLog history information documenting your changes.
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/zlib/README.freeswan Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,13 @@
++The only changes made to these files for use in FreeS/WAN are:
++
++ - In zconf.h, macros are defined to prefix global symbols with "ipcomp_"
++ (or "_ipcomp"), when compiled with -DIPCOMP_PREFIX.
++ - The copyright strings are defined local (static)
++
++ The above changes are made to avoid name collisions with ppp_deflate
++ and ext2compr.
++
++ - Files not needed for FreeS/WAN have been removed
++
++ See the "README" file for information about where to obtain the complete
++ zlib package.
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/zlib/adler32.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,49 @@
++/* adler32.c -- compute the Adler-32 checksum of a data stream
++ * Copyright (C) 1995-2002 Mark Adler
++ * For conditions of distribution and use, see copyright notice in zlib.h
++ */
++
++/* @(#) $Id$ */
++
++#include <zlib/zlib.h>
++#include "zconf.h"
++
++#define BASE 65521L /* largest prime smaller than 65536 */
++#define NMAX 5552
++/* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */
++
++#define DO1(buf,i) {s1 += buf[i]; s2 += s1;}
++#define DO2(buf,i) DO1(buf,i); DO1(buf,i+1);
++#define DO4(buf,i) DO2(buf,i); DO2(buf,i+2);
++#define DO8(buf,i) DO4(buf,i); DO4(buf,i+4);
++#define DO16(buf) DO8(buf,0); DO8(buf,8);
++
++/* ========================================================================= */
++uLong ZEXPORT adler32(adler, buf, len)
++ uLong adler;
++ const Bytef *buf;
++ uInt len;
++{
++ unsigned long s1 = adler & 0xffff;
++ unsigned long s2 = (adler >> 16) & 0xffff;
++ int k;
++
++ if (buf == Z_NULL) return 1L;
++
++ while (len > 0) {
++ k = len < NMAX ? len : NMAX;
++ len -= k;
++ while (k >= 16) {
++ DO16(buf);
++ buf += 16;
++ k -= 16;
++ }
++ if (k != 0) do {
++ s1 += *buf++;
++ s2 += s1;
++ } while (--k);
++ s1 %= BASE;
++ s2 %= BASE;
++ }
++ return (s2 << 16) | s1;
++}
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/zlib/deflate.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,1351 @@
++/* deflate.c -- compress data using the deflation algorithm
++ * Copyright (C) 1995-2002 Jean-loup Gailly.
++ * For conditions of distribution and use, see copyright notice in zlib.h
++ */
++
++/*
++ * ALGORITHM
++ *
++ * The "deflation" process depends on being able to identify portions
++ * of the input text which are identical to earlier input (within a
++ * sliding window trailing behind the input currently being processed).
++ *
++ * The most straightforward technique turns out to be the fastest for
++ * most input files: try all possible matches and select the longest.
++ * The key feature of this algorithm is that insertions into the string
++ * dictionary are very simple and thus fast, and deletions are avoided
++ * completely. Insertions are performed at each input character, whereas
++ * string matches are performed only when the previous match ends. So it
++ * is preferable to spend more time in matches to allow very fast string
++ * insertions and avoid deletions. The matching algorithm for small
++ * strings is inspired from that of Rabin & Karp. A brute force approach
++ * is used to find longer strings when a small match has been found.
++ * A similar algorithm is used in comic (by Jan-Mark Wams) and freeze
++ * (by Leonid Broukhis).
++ * A previous version of this file used a more sophisticated algorithm
++ * (by Fiala and Greene) which is guaranteed to run in linear amortized
++ * time, but has a larger average cost, uses more memory and is patented.
++ * However the F&G algorithm may be faster for some highly redundant
++ * files if the parameter max_chain_length (described below) is too large.
++ *
++ * ACKNOWLEDGEMENTS
++ *
++ * The idea of lazy evaluation of matches is due to Jan-Mark Wams, and
++ * I found it in 'freeze' written by Leonid Broukhis.
++ * Thanks to many people for bug reports and testing.
++ *
++ * REFERENCES
++ *
++ * Deutsch, L.P.,"DEFLATE Compressed Data Format Specification".
++ * Available in ftp://ds.internic.net/rfc/rfc1951.txt
++ *
++ * A description of the Rabin and Karp algorithm is given in the book
++ * "Algorithms" by R. Sedgewick, Addison-Wesley, p252.
++ *
++ * Fiala,E.R., and Greene,D.H.
++ * Data Compression with Finite Windows, Comm.ACM, 32,4 (1989) 490-595
++ *
++ */
++
++/* @(#) $Id$ */
++
++#include "deflate.h"
++
++local const char deflate_copyright[] =
++ " deflate 1.1.4 Copyright 1995-2002 Jean-loup Gailly ";
++/*
++ If you use the zlib library in a product, an acknowledgment is welcome
++ in the documentation of your product. If for some reason you cannot
++ include such an acknowledgment, I would appreciate that you keep this
++ copyright string in the executable of your product.
++ */
++
++/* ===========================================================================
++ * Function prototypes.
++ */
++typedef enum {
++ need_more, /* block not completed, need more input or more output */
++ block_done, /* block flush performed */
++ finish_started, /* finish started, need only more output at next deflate */
++ finish_done /* finish done, accept no more input or output */
++} block_state;
++
++typedef block_state (*compress_func) OF((deflate_state *s, int flush));
++/* Compression function. Returns the block state after the call. */
++
++local void fill_window OF((deflate_state *s));
++local block_state deflate_stored OF((deflate_state *s, int flush));
++local block_state deflate_fast OF((deflate_state *s, int flush));
++local block_state deflate_slow OF((deflate_state *s, int flush));
++local void lm_init OF((deflate_state *s));
++local void putShortMSB OF((deflate_state *s, uInt b));
++local void flush_pending OF((z_streamp strm));
++local int read_buf OF((z_streamp strm, Bytef *buf, unsigned size));
++#ifdef ASMV
++ void match_init OF((void)); /* asm code initialization */
++ uInt longest_match OF((deflate_state *s, IPos cur_match));
++#else
++local uInt longest_match OF((deflate_state *s, IPos cur_match));
++#endif
++
++#ifdef DEBUG
++local void check_match OF((deflate_state *s, IPos start, IPos match,
++ int length));
++#endif
++
++/* ===========================================================================
++ * Local data
++ */
++
++#define NIL 0
++/* Tail of hash chains */
++
++#ifndef TOO_FAR
++# define TOO_FAR 4096
++#endif
++/* Matches of length 3 are discarded if their distance exceeds TOO_FAR */
++
++#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1)
++/* Minimum amount of lookahead, except at the end of the input file.
++ * See deflate.c for comments about the MIN_MATCH+1.
++ */
++
++/* Values for max_lazy_match, good_match and max_chain_length, depending on
++ * the desired pack level (0..9). The values given below have been tuned to
++ * exclude worst case performance for pathological files. Better values may be
++ * found for specific files.
++ */
++typedef struct config_s {
++ ush good_length; /* reduce lazy search above this match length */
++ ush max_lazy; /* do not perform lazy search above this match length */
++ ush nice_length; /* quit search above this match length */
++ ush max_chain;
++ compress_func func;
++} config;
++
++local const config configuration_table[10] = {
++/* good lazy nice chain */
++/* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */
++/* 1 */ {4, 4, 8, 4, deflate_fast}, /* maximum speed, no lazy matches */
++/* 2 */ {4, 5, 16, 8, deflate_fast},
++/* 3 */ {4, 6, 32, 32, deflate_fast},
++
++/* 4 */ {4, 4, 16, 16, deflate_slow}, /* lazy matches */
++/* 5 */ {8, 16, 32, 32, deflate_slow},
++/* 6 */ {8, 16, 128, 128, deflate_slow},
++/* 7 */ {8, 32, 128, 256, deflate_slow},
++/* 8 */ {32, 128, 258, 1024, deflate_slow},
++/* 9 */ {32, 258, 258, 4096, deflate_slow}}; /* maximum compression */
++
++/* Note: the deflate() code requires max_lazy >= MIN_MATCH and max_chain >= 4
++ * For deflate_fast() (levels <= 3) good is ignored and lazy has a different
++ * meaning.
++ */
++
++#define EQUAL 0
++/* result of memcmp for equal strings */
++
++struct static_tree_desc_s {int dummy;}; /* for buggy compilers */
++
++/* ===========================================================================
++ * Update a hash value with the given input byte
++ * IN assertion: all calls to to UPDATE_HASH are made with consecutive
++ * input characters, so that a running hash key can be computed from the
++ * previous key instead of complete recalculation each time.
++ */
++#define UPDATE_HASH(s,h,c) (h = (((h)<<s->hash_shift) ^ (c)) & s->hash_mask)
++
++
++/* ===========================================================================
++ * Insert string str in the dictionary and set match_head to the previous head
++ * of the hash chain (the most recent string with same hash key). Return
++ * the previous length of the hash chain.
++ * If this file is compiled with -DFASTEST, the compression level is forced
++ * to 1, and no hash chains are maintained.
++ * IN assertion: all calls to to INSERT_STRING are made with consecutive
++ * input characters and the first MIN_MATCH bytes of str are valid
++ * (except for the last MIN_MATCH-1 bytes of the input file).
++ */
++#ifdef FASTEST
++#define INSERT_STRING(s, str, match_head) \
++ (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \
++ match_head = s->head[s->ins_h], \
++ s->head[s->ins_h] = (Pos)(str))
++#else
++#define INSERT_STRING(s, str, match_head) \
++ (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \
++ s->prev[(str) & s->w_mask] = match_head = s->head[s->ins_h], \
++ s->head[s->ins_h] = (Pos)(str))
++#endif
++
++/* ===========================================================================
++ * Initialize the hash table (avoiding 64K overflow for 16 bit systems).
++ * prev[] will be initialized on the fly.
++ */
++#define CLEAR_HASH(s) \
++ s->head[s->hash_size-1] = NIL; \
++ zmemzero((Bytef *)s->head, (unsigned)(s->hash_size-1)*sizeof(*s->head));
++
++/* ========================================================================= */
++int ZEXPORT deflateInit_(strm, level, version, stream_size)
++ z_streamp strm;
++ int level;
++ const char *version;
++ int stream_size;
++{
++ return deflateInit2_(strm, level, Z_DEFLATED, MAX_WBITS, DEF_MEM_LEVEL,
++ Z_DEFAULT_STRATEGY, version, stream_size);
++ /* To do: ignore strm->next_in if we use it as window */
++}
++
++/* ========================================================================= */
++int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy,
++ version, stream_size)
++ z_streamp strm;
++ int level;
++ int method;
++ int windowBits;
++ int memLevel;
++ int strategy;
++ const char *version;
++ int stream_size;
++{
++ deflate_state *s;
++ int noheader = 0;
++ static const char* my_version = ZLIB_VERSION;
++
++ ushf *overlay;
++ /* We overlay pending_buf and d_buf+l_buf. This works since the average
++ * output size for (length,distance) codes is <= 24 bits.
++ */
++
++ if (version == Z_NULL || version[0] != my_version[0] ||
++ stream_size != sizeof(z_stream)) {
++ return Z_VERSION_ERROR;
++ }
++ if (strm == Z_NULL) return Z_STREAM_ERROR;
++
++ strm->msg = Z_NULL;
++ if (strm->zalloc == Z_NULL) {
++ return Z_STREAM_ERROR;
++/* strm->zalloc = zcalloc;
++ strm->opaque = (voidpf)0;*/
++ }
++ if (strm->zfree == Z_NULL) return Z_STREAM_ERROR; /* strm->zfree = zcfree; */
++
++ if (level == Z_DEFAULT_COMPRESSION) level = 6;
++#ifdef FASTEST
++ level = 1;
++#endif
++
++ if (windowBits < 0) { /* undocumented feature: suppress zlib header */
++ noheader = 1;
++ windowBits = -windowBits;
++ }
++ if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method != Z_DEFLATED ||
++ windowBits < 9 || windowBits > 15 || level < 0 || level > 9 ||
++ strategy < 0 || strategy > Z_HUFFMAN_ONLY) {
++ return Z_STREAM_ERROR;
++ }
++ s = (deflate_state *) ZALLOC(strm, 1, sizeof(deflate_state));
++ if (s == Z_NULL) return Z_MEM_ERROR;
++ strm->state = (struct internal_state FAR *)s;
++ s->strm = strm;
++
++ s->noheader = noheader;
++ s->w_bits = windowBits;
++ s->w_size = 1 << s->w_bits;
++ s->w_mask = s->w_size - 1;
++
++ s->hash_bits = memLevel + 7;
++ s->hash_size = 1 << s->hash_bits;
++ s->hash_mask = s->hash_size - 1;
++ s->hash_shift = ((s->hash_bits+MIN_MATCH-1)/MIN_MATCH);
++
++ s->window = (Bytef *) ZALLOC(strm, s->w_size, 2*sizeof(Byte));
++ s->prev = (Posf *) ZALLOC(strm, s->w_size, sizeof(Pos));
++ s->head = (Posf *) ZALLOC(strm, s->hash_size, sizeof(Pos));
++
++ s->lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */
++
++ overlay = (ushf *) ZALLOC(strm, s->lit_bufsize, sizeof(ush)+2);
++ s->pending_buf = (uchf *) overlay;
++ s->pending_buf_size = (ulg)s->lit_bufsize * (sizeof(ush)+2L);
++
++ if (s->window == Z_NULL || s->prev == Z_NULL || s->head == Z_NULL ||
++ s->pending_buf == Z_NULL) {
++ strm->msg = ERR_MSG(Z_MEM_ERROR);
++ deflateEnd (strm);
++ return Z_MEM_ERROR;
++ }
++ s->d_buf = overlay + s->lit_bufsize/sizeof(ush);
++ s->l_buf = s->pending_buf + (1+sizeof(ush))*s->lit_bufsize;
++
++ s->level = level;
++ s->strategy = strategy;
++ s->method = (Byte)method;
++
++ return deflateReset(strm);
++}
++
++/* ========================================================================= */
++int ZEXPORT deflateSetDictionary (strm, dictionary, dictLength)
++ z_streamp strm;
++ const Bytef *dictionary;
++ uInt dictLength;
++{
++ deflate_state *s;
++ uInt length = dictLength;
++ uInt n;
++ IPos hash_head = 0;
++
++ if (strm == Z_NULL || strm->state == Z_NULL || dictionary == Z_NULL ||
++ strm->state->status != INIT_STATE) return Z_STREAM_ERROR;
++
++ s = strm->state;
++ strm->adler = adler32(strm->adler, dictionary, dictLength);
++
++ if (length < MIN_MATCH) return Z_OK;
++ if (length > MAX_DIST(s)) {
++ length = MAX_DIST(s);
++#ifndef USE_DICT_HEAD
++ dictionary += dictLength - length; /* use the tail of the dictionary */
++#endif
++ }
++ zmemcpy(s->window, dictionary, length);
++ s->strstart = length;
++ s->block_start = (long)length;
++
++ /* Insert all strings in the hash table (except for the last two bytes).
++ * s->lookahead stays null, so s->ins_h will be recomputed at the next
++ * call of fill_window.
++ */
++ s->ins_h = s->window[0];
++ UPDATE_HASH(s, s->ins_h, s->window[1]);
++ for (n = 0; n <= length - MIN_MATCH; n++) {
++ INSERT_STRING(s, n, hash_head);
++ }
++ if (hash_head) hash_head = 0; /* to make compiler happy */
++ return Z_OK;
++}
++
++/* ========================================================================= */
++int ZEXPORT deflateReset (strm)
++ z_streamp strm;
++{
++ deflate_state *s;
++
++ if (strm == Z_NULL || strm->state == Z_NULL ||
++ strm->zalloc == Z_NULL || strm->zfree == Z_NULL) return Z_STREAM_ERROR;
++
++ strm->total_in = strm->total_out = 0;
++ strm->msg = Z_NULL; /* use zfree if we ever allocate msg dynamically */
++ strm->data_type = Z_UNKNOWN;
++
++ s = (deflate_state *)strm->state;
++ s->pending = 0;
++ s->pending_out = s->pending_buf;
++
++ if (s->noheader < 0) {
++ s->noheader = 0; /* was set to -1 by deflate(..., Z_FINISH); */
++ }
++ s->status = s->noheader ? BUSY_STATE : INIT_STATE;
++ strm->adler = 1;
++ s->last_flush = Z_NO_FLUSH;
++
++ _tr_init(s);
++ lm_init(s);
++
++ return Z_OK;
++}
++
++/* ========================================================================= */
++int ZEXPORT deflateParams(strm, level, strategy)
++ z_streamp strm;
++ int level;
++ int strategy;
++{
++ deflate_state *s;
++ compress_func func;
++ int err = Z_OK;
++
++ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
++ s = strm->state;
++
++ if (level == Z_DEFAULT_COMPRESSION) {
++ level = 6;
++ }
++ if (level < 0 || level > 9 || strategy < 0 || strategy > Z_HUFFMAN_ONLY) {
++ return Z_STREAM_ERROR;
++ }
++ func = configuration_table[s->level].func;
++
++ if (func != configuration_table[level].func && strm->total_in != 0) {
++ /* Flush the last buffer: */
++ err = deflate(strm, Z_PARTIAL_FLUSH);
++ }
++ if (s->level != level) {
++ s->level = level;
++ s->max_lazy_match = configuration_table[level].max_lazy;
++ s->good_match = configuration_table[level].good_length;
++ s->nice_match = configuration_table[level].nice_length;
++ s->max_chain_length = configuration_table[level].max_chain;
++ }
++ s->strategy = strategy;
++ return err;
++}
++
++/* =========================================================================
++ * Put a short in the pending buffer. The 16-bit value is put in MSB order.
++ * IN assertion: the stream state is correct and there is enough room in
++ * pending_buf.
++ */
++local void putShortMSB (s, b)
++ deflate_state *s;
++ uInt b;
++{
++ put_byte(s, (Byte)(b >> 8));
++ put_byte(s, (Byte)(b & 0xff));
++}
++
++/* =========================================================================
++ * Flush as much pending output as possible. All deflate() output goes
++ * through this function so some applications may wish to modify it
++ * to avoid allocating a large strm->next_out buffer and copying into it.
++ * (See also read_buf()).
++ */
++local void flush_pending(strm)
++ z_streamp strm;
++{
++ unsigned len = strm->state->pending;
++
++ if (len > strm->avail_out) len = strm->avail_out;
++ if (len == 0) return;
++
++ zmemcpy(strm->next_out, strm->state->pending_out, len);
++ strm->next_out += len;
++ strm->state->pending_out += len;
++ strm->total_out += len;
++ strm->avail_out -= len;
++ strm->state->pending -= len;
++ if (strm->state->pending == 0) {
++ strm->state->pending_out = strm->state->pending_buf;
++ }
++}
++
++/* ========================================================================= */
++int ZEXPORT deflate (strm, flush)
++ z_streamp strm;
++ int flush;
++{
++ int old_flush; /* value of flush param for previous deflate call */
++ deflate_state *s;
++
++ if (strm == Z_NULL || strm->state == Z_NULL ||
++ flush > Z_FINISH || flush < 0) {
++ return Z_STREAM_ERROR;
++ }
++ s = strm->state;
++
++ if (strm->next_out == Z_NULL ||
++ (strm->next_in == Z_NULL && strm->avail_in != 0) ||
++ (s->status == FINISH_STATE && flush != Z_FINISH)) {
++ ERR_RETURN(strm, Z_STREAM_ERROR);
++ }
++ if (strm->avail_out == 0) ERR_RETURN(strm, Z_BUF_ERROR);
++
++ s->strm = strm; /* just in case */
++ old_flush = s->last_flush;
++ s->last_flush = flush;
++
++ /* Write the zlib header */
++ if (s->status == INIT_STATE) {
++
++ uInt header = (Z_DEFLATED + ((s->w_bits-8)<<4)) << 8;
++ uInt level_flags = (s->level-1) >> 1;
++
++ if (level_flags > 3) level_flags = 3;
++ header |= (level_flags << 6);
++ if (s->strstart != 0) header |= PRESET_DICT;
++ header += 31 - (header % 31);
++
++ s->status = BUSY_STATE;
++ putShortMSB(s, header);
++
++ /* Save the adler32 of the preset dictionary: */
++ if (s->strstart != 0) {
++ putShortMSB(s, (uInt)(strm->adler >> 16));
++ putShortMSB(s, (uInt)(strm->adler & 0xffff));
++ }
++ strm->adler = 1L;
++ }
++
++ /* Flush as much pending output as possible */
++ if (s->pending != 0) {
++ flush_pending(strm);
++ if (strm->avail_out == 0) {
++ /* Since avail_out is 0, deflate will be called again with
++ * more output space, but possibly with both pending and
++ * avail_in equal to zero. There won't be anything to do,
++ * but this is not an error situation so make sure we
++ * return OK instead of BUF_ERROR at next call of deflate:
++ */
++ s->last_flush = -1;
++ return Z_OK;
++ }
++
++ /* Make sure there is something to do and avoid duplicate consecutive
++ * flushes. For repeated and useless calls with Z_FINISH, we keep
++ * returning Z_STREAM_END instead of Z_BUFF_ERROR.
++ */
++ } else if (strm->avail_in == 0 && flush <= old_flush &&
++ flush != Z_FINISH) {
++ ERR_RETURN(strm, Z_BUF_ERROR);
++ }
++
++ /* User must not provide more input after the first FINISH: */
++ if (s->status == FINISH_STATE && strm->avail_in != 0) {
++ ERR_RETURN(strm, Z_BUF_ERROR);
++ }
++
++ /* Start a new block or continue the current one.
++ */
++ if (strm->avail_in != 0 || s->lookahead != 0 ||
++ (flush != Z_NO_FLUSH && s->status != FINISH_STATE)) {
++ block_state bstate;
++
++ bstate = (*(configuration_table[s->level].func))(s, flush);
++
++ if (bstate == finish_started || bstate == finish_done) {
++ s->status = FINISH_STATE;
++ }
++ if (bstate == need_more || bstate == finish_started) {
++ if (strm->avail_out == 0) {
++ s->last_flush = -1; /* avoid BUF_ERROR next call, see above */
++ }
++ return Z_OK;
++ /* If flush != Z_NO_FLUSH && avail_out == 0, the next call
++ * of deflate should use the same flush parameter to make sure
++ * that the flush is complete. So we don't have to output an
++ * empty block here, this will be done at next call. This also
++ * ensures that for a very small output buffer, we emit at most
++ * one empty block.
++ */
++ }
++ if (bstate == block_done) {
++ if (flush == Z_PARTIAL_FLUSH) {
++ _tr_align(s);
++ } else { /* FULL_FLUSH or SYNC_FLUSH */
++ _tr_stored_block(s, (char*)0, 0L, 0);
++ /* For a full flush, this empty block will be recognized
++ * as a special marker by inflate_sync().
++ */
++ if (flush == Z_FULL_FLUSH) {
++ CLEAR_HASH(s); /* forget history */
++ }
++ }
++ flush_pending(strm);
++ if (strm->avail_out == 0) {
++ s->last_flush = -1; /* avoid BUF_ERROR at next call, see above */
++ return Z_OK;
++ }
++ }
++ }
++ Assert(strm->avail_out > 0, "bug2");
++
++ if (flush != Z_FINISH) return Z_OK;
++ if (s->noheader) return Z_STREAM_END;
++
++ /* Write the zlib trailer (adler32) */
++ putShortMSB(s, (uInt)(strm->adler >> 16));
++ putShortMSB(s, (uInt)(strm->adler & 0xffff));
++ flush_pending(strm);
++ /* If avail_out is zero, the application will call deflate again
++ * to flush the rest.
++ */
++ s->noheader = -1; /* write the trailer only once! */
++ return s->pending != 0 ? Z_OK : Z_STREAM_END;
++}
++
++/* ========================================================================= */
++int ZEXPORT deflateEnd (strm)
++ z_streamp strm;
++{
++ int status;
++
++ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
++
++ status = strm->state->status;
++ if (status != INIT_STATE && status != BUSY_STATE &&
++ status != FINISH_STATE) {
++ return Z_STREAM_ERROR;
++ }
++
++ /* Deallocate in reverse order of allocations: */
++ TRY_FREE(strm, strm->state->pending_buf);
++ TRY_FREE(strm, strm->state->head);
++ TRY_FREE(strm, strm->state->prev);
++ TRY_FREE(strm, strm->state->window);
++
++ ZFREE(strm, strm->state);
++ strm->state = Z_NULL;
++
++ return status == BUSY_STATE ? Z_DATA_ERROR : Z_OK;
++}
++
++/* =========================================================================
++ * Copy the source state to the destination state.
++ * To simplify the source, this is not supported for 16-bit MSDOS (which
++ * doesn't have enough memory anyway to duplicate compression states).
++ */
++int ZEXPORT deflateCopy (dest, source)
++ z_streamp dest;
++ z_streamp source;
++{
++#ifdef MAXSEG_64K
++ return Z_STREAM_ERROR;
++#else
++ deflate_state *ds;
++ deflate_state *ss;
++ ushf *overlay;
++
++
++ if (source == Z_NULL || dest == Z_NULL || source->state == Z_NULL) {
++ return Z_STREAM_ERROR;
++ }
++
++ ss = source->state;
++
++ *dest = *source;
++
++ ds = (deflate_state *) ZALLOC(dest, 1, sizeof(deflate_state));
++ if (ds == Z_NULL) return Z_MEM_ERROR;
++ dest->state = (struct internal_state FAR *) ds;
++ *ds = *ss;
++ ds->strm = dest;
++
++ ds->window = (Bytef *) ZALLOC(dest, ds->w_size, 2*sizeof(Byte));
++ ds->prev = (Posf *) ZALLOC(dest, ds->w_size, sizeof(Pos));
++ ds->head = (Posf *) ZALLOC(dest, ds->hash_size, sizeof(Pos));
++ overlay = (ushf *) ZALLOC(dest, ds->lit_bufsize, sizeof(ush)+2);
++ ds->pending_buf = (uchf *) overlay;
++
++ if (ds->window == Z_NULL || ds->prev == Z_NULL || ds->head == Z_NULL ||
++ ds->pending_buf == Z_NULL) {
++ deflateEnd (dest);
++ return Z_MEM_ERROR;
++ }
++ /* following zmemcpy do not work for 16-bit MSDOS */
++ zmemcpy(ds->window, ss->window, ds->w_size * 2 * sizeof(Byte));
++ zmemcpy(ds->prev, ss->prev, ds->w_size * sizeof(Pos));
++ zmemcpy(ds->head, ss->head, ds->hash_size * sizeof(Pos));
++ zmemcpy(ds->pending_buf, ss->pending_buf, (uInt)ds->pending_buf_size);
++
++ ds->pending_out = ds->pending_buf + (ss->pending_out - ss->pending_buf);
++ ds->d_buf = overlay + ds->lit_bufsize/sizeof(ush);
++ ds->l_buf = ds->pending_buf + (1+sizeof(ush))*ds->lit_bufsize;
++
++ ds->l_desc.dyn_tree = ds->dyn_ltree;
++ ds->d_desc.dyn_tree = ds->dyn_dtree;
++ ds->bl_desc.dyn_tree = ds->bl_tree;
++
++ return Z_OK;
++#endif
++}
++
++/* ===========================================================================
++ * Read a new buffer from the current input stream, update the adler32
++ * and total number of bytes read. All deflate() input goes through
++ * this function so some applications may wish to modify it to avoid
++ * allocating a large strm->next_in buffer and copying from it.
++ * (See also flush_pending()).
++ */
++local int read_buf(strm, buf, size)
++ z_streamp strm;
++ Bytef *buf;
++ unsigned size;
++{
++ unsigned len = strm->avail_in;
++
++ if (len > size) len = size;
++ if (len == 0) return 0;
++
++ strm->avail_in -= len;
++
++ if (!strm->state->noheader) {
++ strm->adler = adler32(strm->adler, strm->next_in, len);
++ }
++ zmemcpy(buf, strm->next_in, len);
++ strm->next_in += len;
++ strm->total_in += len;
++
++ return (int)len;
++}
++
++/* ===========================================================================
++ * Initialize the "longest match" routines for a new zlib stream
++ */
++local void lm_init (s)
++ deflate_state *s;
++{
++ s->window_size = (ulg)2L*s->w_size;
++
++ CLEAR_HASH(s);
++
++ /* Set the default configuration parameters:
++ */
++ s->max_lazy_match = configuration_table[s->level].max_lazy;
++ s->good_match = configuration_table[s->level].good_length;
++ s->nice_match = configuration_table[s->level].nice_length;
++ s->max_chain_length = configuration_table[s->level].max_chain;
++
++ s->strstart = 0;
++ s->block_start = 0L;
++ s->lookahead = 0;
++ s->match_length = s->prev_length = MIN_MATCH-1;
++ s->match_available = 0;
++ s->ins_h = 0;
++#ifdef ASMV
++ match_init(); /* initialize the asm code */
++#endif
++}
++
++/* ===========================================================================
++ * Set match_start to the longest match starting at the given string and
++ * return its length. Matches shorter or equal to prev_length are discarded,
++ * in which case the result is equal to prev_length and match_start is
++ * garbage.
++ * IN assertions: cur_match is the head of the hash chain for the current
++ * string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1
++ * OUT assertion: the match length is not greater than s->lookahead.
++ */
++#ifndef ASMV
++/* For 80x86 and 680x0, an optimized version will be provided in match.asm or
++ * match.S. The code will be functionally equivalent.
++ */
++#ifndef FASTEST
++local uInt longest_match(s, cur_match)
++ deflate_state *s;
++ IPos cur_match; /* current match */
++{
++ unsigned chain_length = s->max_chain_length;/* max hash chain length */
++ register Bytef *scan = s->window + s->strstart; /* current string */
++ register Bytef *match; /* matched string */
++ register int len; /* length of current match */
++ int best_len = s->prev_length; /* best match length so far */
++ int nice_match = s->nice_match; /* stop if match long enough */
++ IPos limit = s->strstart > (IPos)MAX_DIST(s) ?
++ s->strstart - (IPos)MAX_DIST(s) : NIL;
++ /* Stop when cur_match becomes <= limit. To simplify the code,
++ * we prevent matches with the string of window index 0.
++ */
++ Posf *prev = s->prev;
++ uInt wmask = s->w_mask;
++
++#ifdef UNALIGNED_OK
++ /* Compare two bytes at a time. Note: this is not always beneficial.
++ * Try with and without -DUNALIGNED_OK to check.
++ */
++ register Bytef *strend = s->window + s->strstart + MAX_MATCH - 1;
++ register ush scan_start = *(ushf*)scan;
++ register ush scan_end = *(ushf*)(scan+best_len-1);
++#else
++ register Bytef *strend = s->window + s->strstart + MAX_MATCH;
++ register Byte scan_end1 = scan[best_len-1];
++ register Byte scan_end = scan[best_len];
++#endif
++
++ /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16.
++ * It is easy to get rid of this optimization if necessary.
++ */
++ Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever");
++
++ /* Do not waste too much time if we already have a good match: */
++ if (s->prev_length >= s->good_match) {
++ chain_length >>= 2;
++ }
++ /* Do not look for matches beyond the end of the input. This is necessary
++ * to make deflate deterministic.
++ */
++ if ((uInt)nice_match > s->lookahead) nice_match = s->lookahead;
++
++ Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead");
++
++ do {
++ Assert(cur_match < s->strstart, "no future");
++ match = s->window + cur_match;
++
++ /* Skip to next match if the match length cannot increase
++ * or if the match length is less than 2:
++ */
++#if (defined(UNALIGNED_OK) && MAX_MATCH == 258)
++ /* This code assumes sizeof(unsigned short) == 2. Do not use
++ * UNALIGNED_OK if your compiler uses a different size.
++ */
++ if (*(ushf*)(match+best_len-1) != scan_end ||
++ *(ushf*)match != scan_start) continue;
++
++ /* It is not necessary to compare scan[2] and match[2] since they are
++ * always equal when the other bytes match, given that the hash keys
++ * are equal and that HASH_BITS >= 8. Compare 2 bytes at a time at
++ * strstart+3, +5, ... up to strstart+257. We check for insufficient
++ * lookahead only every 4th comparison; the 128th check will be made
++ * at strstart+257. If MAX_MATCH-2 is not a multiple of 8, it is
++ * necessary to put more guard bytes at the end of the window, or
++ * to check more often for insufficient lookahead.
++ */
++ Assert(scan[2] == match[2], "scan[2]?");
++ scan++, match++;
++ do {
++ } while (*(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
++ *(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
++ *(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
++ *(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
++ scan < strend);
++ /* The funny "do {}" generates better code on most compilers */
++
++ /* Here, scan <= window+strstart+257 */
++ Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan");
++ if (*scan == *match) scan++;
++
++ len = (MAX_MATCH - 1) - (int)(strend-scan);
++ scan = strend - (MAX_MATCH-1);
++
++#else /* UNALIGNED_OK */
++
++ if (match[best_len] != scan_end ||
++ match[best_len-1] != scan_end1 ||
++ *match != *scan ||
++ *++match != scan[1]) continue;
++
++ /* The check at best_len-1 can be removed because it will be made
++ * again later. (This heuristic is not always a win.)
++ * It is not necessary to compare scan[2] and match[2] since they
++ * are always equal when the other bytes match, given that
++ * the hash keys are equal and that HASH_BITS >= 8.
++ */
++ scan += 2, match++;
++ Assert(*scan == *match, "match[2]?");
++
++ /* We check for insufficient lookahead only every 8th comparison;
++ * the 256th check will be made at strstart+258.
++ */
++ do {
++ } while (*++scan == *++match && *++scan == *++match &&
++ *++scan == *++match && *++scan == *++match &&
++ *++scan == *++match && *++scan == *++match &&
++ *++scan == *++match && *++scan == *++match &&
++ scan < strend);
++
++ Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan");
++
++ len = MAX_MATCH - (int)(strend - scan);
++ scan = strend - MAX_MATCH;
++
++#endif /* UNALIGNED_OK */
++
++ if (len > best_len) {
++ s->match_start = cur_match;
++ best_len = len;
++ if (len >= nice_match) break;
++#ifdef UNALIGNED_OK
++ scan_end = *(ushf*)(scan+best_len-1);
++#else
++ scan_end1 = scan[best_len-1];
++ scan_end = scan[best_len];
++#endif
++ }
++ } while ((cur_match = prev[cur_match & wmask]) > limit
++ && --chain_length != 0);
++
++ if ((uInt)best_len <= s->lookahead) return (uInt)best_len;
++ return s->lookahead;
++}
++
++#else /* FASTEST */
++/* ---------------------------------------------------------------------------
++ * Optimized version for level == 1 only
++ */
++local uInt longest_match(s, cur_match)
++ deflate_state *s;
++ IPos cur_match; /* current match */
++{
++ register Bytef *scan = s->window + s->strstart; /* current string */
++ register Bytef *match; /* matched string */
++ register int len; /* length of current match */
++ register Bytef *strend = s->window + s->strstart + MAX_MATCH;
++
++ /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16.
++ * It is easy to get rid of this optimization if necessary.
++ */
++ Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever");
++
++ Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead");
++
++ Assert(cur_match < s->strstart, "no future");
++
++ match = s->window + cur_match;
++
++ /* Return failure if the match length is less than 2:
++ */
++ if (match[0] != scan[0] || match[1] != scan[1]) return MIN_MATCH-1;
++
++ /* The check at best_len-1 can be removed because it will be made
++ * again later. (This heuristic is not always a win.)
++ * It is not necessary to compare scan[2] and match[2] since they
++ * are always equal when the other bytes match, given that
++ * the hash keys are equal and that HASH_BITS >= 8.
++ */
++ scan += 2, match += 2;
++ Assert(*scan == *match, "match[2]?");
++
++ /* We check for insufficient lookahead only every 8th comparison;
++ * the 256th check will be made at strstart+258.
++ */
++ do {
++ } while (*++scan == *++match && *++scan == *++match &&
++ *++scan == *++match && *++scan == *++match &&
++ *++scan == *++match && *++scan == *++match &&
++ *++scan == *++match && *++scan == *++match &&
++ scan < strend);
++
++ Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan");
++
++ len = MAX_MATCH - (int)(strend - scan);
++
++ if (len < MIN_MATCH) return MIN_MATCH - 1;
++
++ s->match_start = cur_match;
++ return len <= s->lookahead ? len : s->lookahead;
++}
++#endif /* FASTEST */
++#endif /* ASMV */
++
++#ifdef DEBUG
++/* ===========================================================================
++ * Check that the match at match_start is indeed a match.
++ */
++local void check_match(s, start, match, length)
++ deflate_state *s;
++ IPos start, match;
++ int length;
++{
++ /* check that the match is indeed a match */
++ if (zmemcmp(s->window + match,
++ s->window + start, length) != EQUAL) {
++ fprintf(stderr, " start %u, match %u, length %d\n",
++ start, match, length);
++ do {
++ fprintf(stderr, "%c%c", s->window[match++], s->window[start++]);
++ } while (--length != 0);
++ z_error("invalid match");
++ }
++ if (z_verbose > 1) {
++ fprintf(stderr,"\\[%d,%d]", start-match, length);
++ do { putc(s->window[start++], stderr); } while (--length != 0);
++ }
++}
++#else
++# define check_match(s, start, match, length)
++#endif
++
++/* ===========================================================================
++ * Fill the window when the lookahead becomes insufficient.
++ * Updates strstart and lookahead.
++ *
++ * IN assertion: lookahead < MIN_LOOKAHEAD
++ * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD
++ * At least one byte has been read, or avail_in == 0; reads are
++ * performed for at least two bytes (required for the zip translate_eol
++ * option -- not supported here).
++ */
++local void fill_window(s)
++ deflate_state *s;
++{
++ register unsigned n, m;
++ register Posf *p;
++ unsigned more; /* Amount of free space at the end of the window. */
++ uInt wsize = s->w_size;
++
++ do {
++ more = (unsigned)(s->window_size -(ulg)s->lookahead -(ulg)s->strstart);
++
++ /* Deal with !@#$% 64K limit: */
++ if (more == 0 && s->strstart == 0 && s->lookahead == 0) {
++ more = wsize;
++
++ } else if (more == (unsigned)(-1)) {
++ /* Very unlikely, but possible on 16 bit machine if strstart == 0
++ * and lookahead == 1 (input done one byte at time)
++ */
++ more--;
++
++ /* If the window is almost full and there is insufficient lookahead,
++ * move the upper half to the lower one to make room in the upper half.
++ */
++ } else if (s->strstart >= wsize+MAX_DIST(s)) {
++
++ zmemcpy(s->window, s->window+wsize, (unsigned)wsize);
++ s->match_start -= wsize;
++ s->strstart -= wsize; /* we now have strstart >= MAX_DIST */
++ s->block_start -= (long) wsize;
++
++ /* Slide the hash table (could be avoided with 32 bit values
++ at the expense of memory usage). We slide even when level == 0
++ to keep the hash table consistent if we switch back to level > 0
++ later. (Using level 0 permanently is not an optimal usage of
++ zlib, so we don't care about this pathological case.)
++ */
++ n = s->hash_size;
++ p = &s->head[n];
++ do {
++ m = *--p;
++ *p = (Pos)(m >= wsize ? m-wsize : NIL);
++ } while (--n);
++
++ n = wsize;
++#ifndef FASTEST
++ p = &s->prev[n];
++ do {
++ m = *--p;
++ *p = (Pos)(m >= wsize ? m-wsize : NIL);
++ /* If n is not on any hash chain, prev[n] is garbage but
++ * its value will never be used.
++ */
++ } while (--n);
++#endif
++ more += wsize;
++ }
++ if (s->strm->avail_in == 0) return;
++
++ /* If there was no sliding:
++ * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 &&
++ * more == window_size - lookahead - strstart
++ * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1)
++ * => more >= window_size - 2*WSIZE + 2
++ * In the BIG_MEM or MMAP case (not yet supported),
++ * window_size == input_size + MIN_LOOKAHEAD &&
++ * strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD.
++ * Otherwise, window_size == 2*WSIZE so more >= 2.
++ * If there was sliding, more >= WSIZE. So in all cases, more >= 2.
++ */
++ Assert(more >= 2, "more < 2");
++
++ n = read_buf(s->strm, s->window + s->strstart + s->lookahead, more);
++ s->lookahead += n;
++
++ /* Initialize the hash value now that we have some input: */
++ if (s->lookahead >= MIN_MATCH) {
++ s->ins_h = s->window[s->strstart];
++ UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]);
++#if MIN_MATCH != 3
++ Call UPDATE_HASH() MIN_MATCH-3 more times
++#endif
++ }
++ /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage,
++ * but this is not important since only literal bytes will be emitted.
++ */
++
++ } while (s->lookahead < MIN_LOOKAHEAD && s->strm->avail_in != 0);
++}
++
++/* ===========================================================================
++ * Flush the current block, with given end-of-file flag.
++ * IN assertion: strstart is set to the end of the current match.
++ */
++#define FLUSH_BLOCK_ONLY(s, eof) { \
++ _tr_flush_block(s, (s->block_start >= 0L ? \
++ (charf *)&s->window[(unsigned)s->block_start] : \
++ (charf *)Z_NULL), \
++ (ulg)((long)s->strstart - s->block_start), \
++ (eof)); \
++ s->block_start = s->strstart; \
++ flush_pending(s->strm); \
++ Tracev((stderr,"[FLUSH]")); \
++}
++
++/* Same but force premature exit if necessary. */
++#define FLUSH_BLOCK(s, eof) { \
++ FLUSH_BLOCK_ONLY(s, eof); \
++ if (s->strm->avail_out == 0) return (eof) ? finish_started : need_more; \
++}
++
++/* ===========================================================================
++ * Copy without compression as much as possible from the input stream, return
++ * the current block state.
++ * This function does not insert new strings in the dictionary since
++ * uncompressible data is probably not useful. This function is used
++ * only for the level=0 compression option.
++ * NOTE: this function should be optimized to avoid extra copying from
++ * window to pending_buf.
++ */
++local block_state deflate_stored(s, flush)
++ deflate_state *s;
++ int flush;
++{
++ /* Stored blocks are limited to 0xffff bytes, pending_buf is limited
++ * to pending_buf_size, and each stored block has a 5 byte header:
++ */
++ ulg max_block_size = 0xffff;
++ ulg max_start;
++
++ if (max_block_size > s->pending_buf_size - 5) {
++ max_block_size = s->pending_buf_size - 5;
++ }
++
++ /* Copy as much as possible from input to output: */
++ for (;;) {
++ /* Fill the window as much as possible: */
++ if (s->lookahead <= 1) {
++
++ Assert(s->strstart < s->w_size+MAX_DIST(s) ||
++ s->block_start >= (long)s->w_size, "slide too late");
++
++ fill_window(s);
++ if (s->lookahead == 0 && flush == Z_NO_FLUSH) return need_more;
++
++ if (s->lookahead == 0) break; /* flush the current block */
++ }
++ Assert(s->block_start >= 0L, "block gone");
++
++ s->strstart += s->lookahead;
++ s->lookahead = 0;
++
++ /* Emit a stored block if pending_buf will be full: */
++ max_start = s->block_start + max_block_size;
++ if (s->strstart == 0 || (ulg)s->strstart >= max_start) {
++ /* strstart == 0 is possible when wraparound on 16-bit machine */
++ s->lookahead = (uInt)(s->strstart - max_start);
++ s->strstart = (uInt)max_start;
++ FLUSH_BLOCK(s, 0);
++ }
++ /* Flush if we may have to slide, otherwise block_start may become
++ * negative and the data will be gone:
++ */
++ if (s->strstart - (uInt)s->block_start >= MAX_DIST(s)) {
++ FLUSH_BLOCK(s, 0);
++ }
++ }
++ FLUSH_BLOCK(s, flush == Z_FINISH);
++ return flush == Z_FINISH ? finish_done : block_done;
++}
++
++/* ===========================================================================
++ * Compress as much as possible from the input stream, return the current
++ * block state.
++ * This function does not perform lazy evaluation of matches and inserts
++ * new strings in the dictionary only for unmatched strings or for short
++ * matches. It is used only for the fast compression options.
++ */
++local block_state deflate_fast(s, flush)
++ deflate_state *s;
++ int flush;
++{
++ IPos hash_head = NIL; /* head of the hash chain */
++ int bflush; /* set if current block must be flushed */
++
++ for (;;) {
++ /* Make sure that we always have enough lookahead, except
++ * at the end of the input file. We need MAX_MATCH bytes
++ * for the next match, plus MIN_MATCH bytes to insert the
++ * string following the next match.
++ */
++ if (s->lookahead < MIN_LOOKAHEAD) {
++ fill_window(s);
++ if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) {
++ return need_more;
++ }
++ if (s->lookahead == 0) break; /* flush the current block */
++ }
++
++ /* Insert the string window[strstart .. strstart+2] in the
++ * dictionary, and set hash_head to the head of the hash chain:
++ */
++ if (s->lookahead >= MIN_MATCH) {
++ INSERT_STRING(s, s->strstart, hash_head);
++ }
++
++ /* Find the longest match, discarding those <= prev_length.
++ * At this point we have always match_length < MIN_MATCH
++ */
++ if (hash_head != NIL && s->strstart - hash_head <= MAX_DIST(s)) {
++ /* To simplify the code, we prevent matches with the string
++ * of window index 0 (in particular we have to avoid a match
++ * of the string with itself at the start of the input file).
++ */
++ if (s->strategy != Z_HUFFMAN_ONLY) {
++ s->match_length = longest_match (s, hash_head);
++ }
++ /* longest_match() sets match_start */
++ }
++ if (s->match_length >= MIN_MATCH) {
++ check_match(s, s->strstart, s->match_start, s->match_length);
++
++ _tr_tally_dist(s, s->strstart - s->match_start,
++ s->match_length - MIN_MATCH, bflush);
++
++ s->lookahead -= s->match_length;
++
++ /* Insert new strings in the hash table only if the match length
++ * is not too large. This saves time but degrades compression.
++ */
++#ifndef FASTEST
++ if (s->match_length <= s->max_insert_length &&
++ s->lookahead >= MIN_MATCH) {
++ s->match_length--; /* string at strstart already in hash table */
++ do {
++ s->strstart++;
++ INSERT_STRING(s, s->strstart, hash_head);
++ /* strstart never exceeds WSIZE-MAX_MATCH, so there are
++ * always MIN_MATCH bytes ahead.
++ */
++ } while (--s->match_length != 0);
++ s->strstart++;
++ } else
++#endif
++ {
++ s->strstart += s->match_length;
++ s->match_length = 0;
++ s->ins_h = s->window[s->strstart];
++ UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]);
++#if MIN_MATCH != 3
++ Call UPDATE_HASH() MIN_MATCH-3 more times
++#endif
++ /* If lookahead < MIN_MATCH, ins_h is garbage, but it does not
++ * matter since it will be recomputed at next deflate call.
++ */
++ }
++ } else {
++ /* No match, output a literal byte */
++ Tracevv((stderr,"%c", s->window[s->strstart]));
++ _tr_tally_lit (s, s->window[s->strstart], bflush);
++ s->lookahead--;
++ s->strstart++;
++ }
++ if (bflush) FLUSH_BLOCK(s, 0);
++ }
++ FLUSH_BLOCK(s, flush == Z_FINISH);
++ return flush == Z_FINISH ? finish_done : block_done;
++}
++
++/* ===========================================================================
++ * Same as above, but achieves better compression. We use a lazy
++ * evaluation for matches: a match is finally adopted only if there is
++ * no better match at the next window position.
++ */
++local block_state deflate_slow(s, flush)
++ deflate_state *s;
++ int flush;
++{
++ IPos hash_head = NIL; /* head of hash chain */
++ int bflush; /* set if current block must be flushed */
++
++ /* Process the input block. */
++ for (;;) {
++ /* Make sure that we always have enough lookahead, except
++ * at the end of the input file. We need MAX_MATCH bytes
++ * for the next match, plus MIN_MATCH bytes to insert the
++ * string following the next match.
++ */
++ if (s->lookahead < MIN_LOOKAHEAD) {
++ fill_window(s);
++ if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) {
++ return need_more;
++ }
++ if (s->lookahead == 0) break; /* flush the current block */
++ }
++
++ /* Insert the string window[strstart .. strstart+2] in the
++ * dictionary, and set hash_head to the head of the hash chain:
++ */
++ if (s->lookahead >= MIN_MATCH) {
++ INSERT_STRING(s, s->strstart, hash_head);
++ }
++
++ /* Find the longest match, discarding those <= prev_length.
++ */
++ s->prev_length = s->match_length, s->prev_match = s->match_start;
++ s->match_length = MIN_MATCH-1;
++
++ if (hash_head != NIL && s->prev_length < s->max_lazy_match &&
++ s->strstart - hash_head <= MAX_DIST(s)) {
++ /* To simplify the code, we prevent matches with the string
++ * of window index 0 (in particular we have to avoid a match
++ * of the string with itself at the start of the input file).
++ */
++ if (s->strategy != Z_HUFFMAN_ONLY) {
++ s->match_length = longest_match (s, hash_head);
++ }
++ /* longest_match() sets match_start */
++
++ if (s->match_length <= 5 && (s->strategy == Z_FILTERED ||
++ (s->match_length == MIN_MATCH &&
++ s->strstart - s->match_start > TOO_FAR))) {
++
++ /* If prev_match is also MIN_MATCH, match_start is garbage
++ * but we will ignore the current match anyway.
++ */
++ s->match_length = MIN_MATCH-1;
++ }
++ }
++ /* If there was a match at the previous step and the current
++ * match is not better, output the previous match:
++ */
++ if (s->prev_length >= MIN_MATCH && s->match_length <= s->prev_length) {
++ uInt max_insert = s->strstart + s->lookahead - MIN_MATCH;
++ /* Do not insert strings in hash table beyond this. */
++
++ check_match(s, s->strstart-1, s->prev_match, s->prev_length);
++
++ _tr_tally_dist(s, s->strstart -1 - s->prev_match,
++ s->prev_length - MIN_MATCH, bflush);
++
++ /* Insert in hash table all strings up to the end of the match.
++ * strstart-1 and strstart are already inserted. If there is not
++ * enough lookahead, the last two strings are not inserted in
++ * the hash table.
++ */
++ s->lookahead -= s->prev_length-1;
++ s->prev_length -= 2;
++ do {
++ if (++s->strstart <= max_insert) {
++ INSERT_STRING(s, s->strstart, hash_head);
++ }
++ } while (--s->prev_length != 0);
++ s->match_available = 0;
++ s->match_length = MIN_MATCH-1;
++ s->strstart++;
++
++ if (bflush) FLUSH_BLOCK(s, 0);
++
++ } else if (s->match_available) {
++ /* If there was no match at the previous position, output a
++ * single literal. If there was a match but the current match
++ * is longer, truncate the previous match to a single literal.
++ */
++ Tracevv((stderr,"%c", s->window[s->strstart-1]));
++ _tr_tally_lit(s, s->window[s->strstart-1], bflush);
++ if (bflush) {
++ FLUSH_BLOCK_ONLY(s, 0);
++ }
++ s->strstart++;
++ s->lookahead--;
++ if (s->strm->avail_out == 0) return need_more;
++ } else {
++ /* There is no previous match to compare with, wait for
++ * the next step to decide.
++ */
++ s->match_available = 1;
++ s->strstart++;
++ s->lookahead--;
++ }
++ }
++ Assert (flush != Z_NO_FLUSH, "no flush?");
++ if (s->match_available) {
++ Tracevv((stderr,"%c", s->window[s->strstart-1]));
++ _tr_tally_lit(s, s->window[s->strstart-1], bflush);
++ s->match_available = 0;
++ }
++ FLUSH_BLOCK(s, flush == Z_FINISH);
++ return flush == Z_FINISH ? finish_done : block_done;
++}
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/zlib/deflate.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,318 @@
++/* deflate.h -- internal compression state
++ * Copyright (C) 1995-2002 Jean-loup Gailly
++ * For conditions of distribution and use, see copyright notice in zlib.h
++ */
++
++/* WARNING: this file should *not* be used by applications. It is
++ part of the implementation of the compression library and is
++ subject to change. Applications should only use zlib.h.
++ */
++
++/* @(#) $Id$ */
++
++#ifndef _DEFLATE_H
++#define _DEFLATE_H
++
++#include "zlib/zutil.h"
++
++/* ===========================================================================
++ * Internal compression state.
++ */
++
++#define LENGTH_CODES 29
++/* number of length codes, not counting the special END_BLOCK code */
++
++#define LITERALS 256
++/* number of literal bytes 0..255 */
++
++#define L_CODES (LITERALS+1+LENGTH_CODES)
++/* number of Literal or Length codes, including the END_BLOCK code */
++
++#define D_CODES 30
++/* number of distance codes */
++
++#define BL_CODES 19
++/* number of codes used to transfer the bit lengths */
++
++#define HEAP_SIZE (2*L_CODES+1)
++/* maximum heap size */
++
++#define MAX_BITS 15
++/* All codes must not exceed MAX_BITS bits */
++
++#define INIT_STATE 42
++#define BUSY_STATE 113
++#define FINISH_STATE 666
++/* Stream status */
++
++
++/* Data structure describing a single value and its code string. */
++typedef struct ct_data_s {
++ union {
++ ush freq; /* frequency count */
++ ush code; /* bit string */
++ } fc;
++ union {
++ ush dad; /* father node in Huffman tree */
++ ush len; /* length of bit string */
++ } dl;
++} FAR ct_data;
++
++#define Freq fc.freq
++#define Code fc.code
++#define Dad dl.dad
++#define Len dl.len
++
++typedef struct static_tree_desc_s static_tree_desc;
++
++typedef struct tree_desc_s {
++ ct_data *dyn_tree; /* the dynamic tree */
++ int max_code; /* largest code with non zero frequency */
++ static_tree_desc *stat_desc; /* the corresponding static tree */
++} FAR tree_desc;
++
++typedef ush Pos;
++typedef Pos FAR Posf;
++typedef unsigned IPos;
++
++/* A Pos is an index in the character window. We use short instead of int to
++ * save space in the various tables. IPos is used only for parameter passing.
++ */
++
++typedef struct internal_state {
++ z_streamp strm; /* pointer back to this zlib stream */
++ int status; /* as the name implies */
++ Bytef *pending_buf; /* output still pending */
++ ulg pending_buf_size; /* size of pending_buf */
++ Bytef *pending_out; /* next pending byte to output to the stream */
++ int pending; /* nb of bytes in the pending buffer */
++ int noheader; /* suppress zlib header and adler32 */
++ Byte data_type; /* UNKNOWN, BINARY or ASCII */
++ Byte method; /* STORED (for zip only) or DEFLATED */
++ int last_flush; /* value of flush param for previous deflate call */
++
++ /* used by deflate.c: */
++
++ uInt w_size; /* LZ77 window size (32K by default) */
++ uInt w_bits; /* log2(w_size) (8..16) */
++ uInt w_mask; /* w_size - 1 */
++
++ Bytef *window;
++ /* Sliding window. Input bytes are read into the second half of the window,
++ * and move to the first half later to keep a dictionary of at least wSize
++ * bytes. With this organization, matches are limited to a distance of
++ * wSize-MAX_MATCH bytes, but this ensures that IO is always
++ * performed with a length multiple of the block size. Also, it limits
++ * the window size to 64K, which is quite useful on MSDOS.
++ * To do: use the user input buffer as sliding window.
++ */
++
++ ulg window_size;
++ /* Actual size of window: 2*wSize, except when the user input buffer
++ * is directly used as sliding window.
++ */
++
++ Posf *prev;
++ /* Link to older string with same hash index. To limit the size of this
++ * array to 64K, this link is maintained only for the last 32K strings.
++ * An index in this array is thus a window index modulo 32K.
++ */
++
++ Posf *head; /* Heads of the hash chains or NIL. */
++
++ uInt ins_h; /* hash index of string to be inserted */
++ uInt hash_size; /* number of elements in hash table */
++ uInt hash_bits; /* log2(hash_size) */
++ uInt hash_mask; /* hash_size-1 */
++
++ uInt hash_shift;
++ /* Number of bits by which ins_h must be shifted at each input
++ * step. It must be such that after MIN_MATCH steps, the oldest
++ * byte no longer takes part in the hash key, that is:
++ * hash_shift * MIN_MATCH >= hash_bits
++ */
++
++ long block_start;
++ /* Window position at the beginning of the current output block. Gets
++ * negative when the window is moved backwards.
++ */
++
++ uInt match_length; /* length of best match */
++ IPos prev_match; /* previous match */
++ int match_available; /* set if previous match exists */
++ uInt strstart; /* start of string to insert */
++ uInt match_start; /* start of matching string */
++ uInt lookahead; /* number of valid bytes ahead in window */
++
++ uInt prev_length;
++ /* Length of the best match at previous step. Matches not greater than this
++ * are discarded. This is used in the lazy match evaluation.
++ */
++
++ uInt max_chain_length;
++ /* To speed up deflation, hash chains are never searched beyond this
++ * length. A higher limit improves compression ratio but degrades the
++ * speed.
++ */
++
++ uInt max_lazy_match;
++ /* Attempt to find a better match only when the current match is strictly
++ * smaller than this value. This mechanism is used only for compression
++ * levels >= 4.
++ */
++# define max_insert_length max_lazy_match
++ /* Insert new strings in the hash table only if the match length is not
++ * greater than this length. This saves time but degrades compression.
++ * max_insert_length is used only for compression levels <= 3.
++ */
++
++ int level; /* compression level (1..9) */
++ int strategy; /* favor or force Huffman coding*/
++
++ uInt good_match;
++ /* Use a faster search when the previous match is longer than this */
++
++ int nice_match; /* Stop searching when current match exceeds this */
++
++ /* used by trees.c: */
++ /* Didn't use ct_data typedef below to supress compiler warning */
++ struct ct_data_s dyn_ltree[HEAP_SIZE]; /* literal and length tree */
++ struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */
++ struct ct_data_s bl_tree[2*BL_CODES+1]; /* Huffman tree for bit lengths */
++
++ struct tree_desc_s l_desc; /* desc. for literal tree */
++ struct tree_desc_s d_desc; /* desc. for distance tree */
++ struct tree_desc_s bl_desc; /* desc. for bit length tree */
++
++ ush bl_count[MAX_BITS+1];
++ /* number of codes at each bit length for an optimal tree */
++
++ int heap[2*L_CODES+1]; /* heap used to build the Huffman trees */
++ int heap_len; /* number of elements in the heap */
++ int heap_max; /* element of largest frequency */
++ /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used.
++ * The same heap array is used to build all trees.
++ */
++
++ uch depth[2*L_CODES+1];
++ /* Depth of each subtree used as tie breaker for trees of equal frequency
++ */
++
++ uchf *l_buf; /* buffer for literals or lengths */
++
++ uInt lit_bufsize;
++ /* Size of match buffer for literals/lengths. There are 4 reasons for
++ * limiting lit_bufsize to 64K:
++ * - frequencies can be kept in 16 bit counters
++ * - if compression is not successful for the first block, all input
++ * data is still in the window so we can still emit a stored block even
++ * when input comes from standard input. (This can also be done for
++ * all blocks if lit_bufsize is not greater than 32K.)
++ * - if compression is not successful for a file smaller than 64K, we can
++ * even emit a stored file instead of a stored block (saving 5 bytes).
++ * This is applicable only for zip (not gzip or zlib).
++ * - creating new Huffman trees less frequently may not provide fast
++ * adaptation to changes in the input data statistics. (Take for
++ * example a binary file with poorly compressible code followed by
++ * a highly compressible string table.) Smaller buffer sizes give
++ * fast adaptation but have of course the overhead of transmitting
++ * trees more frequently.
++ * - I can't count above 4
++ */
++
++ uInt last_lit; /* running index in l_buf */
++
++ ushf *d_buf;
++ /* Buffer for distances. To simplify the code, d_buf and l_buf have
++ * the same number of elements. To use different lengths, an extra flag
++ * array would be necessary.
++ */
++
++ ulg opt_len; /* bit length of current block with optimal trees */
++ ulg static_len; /* bit length of current block with static trees */
++ uInt matches; /* number of string matches in current block */
++ int last_eob_len; /* bit length of EOB code for last block */
++
++#ifdef DEBUG
++ ulg compressed_len; /* total bit length of compressed file mod 2^32 */
++ ulg bits_sent; /* bit length of compressed data sent mod 2^32 */
++#endif
++
++ ush bi_buf;
++ /* Output buffer. bits are inserted starting at the bottom (least
++ * significant bits).
++ */
++ int bi_valid;
++ /* Number of valid bits in bi_buf. All bits above the last valid bit
++ * are always zero.
++ */
++
++} FAR deflate_state;
++
++/* Output a byte on the stream.
++ * IN assertion: there is enough room in pending_buf.
++ */
++#define put_byte(s, c) {s->pending_buf[s->pending++] = (c);}
++
++
++#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1)
++/* Minimum amount of lookahead, except at the end of the input file.
++ * See deflate.c for comments about the MIN_MATCH+1.
++ */
++
++#define MAX_DIST(s) ((s)->w_size-MIN_LOOKAHEAD)
++/* In order to simplify the code, particularly on 16 bit machines, match
++ * distances are limited to MAX_DIST instead of WSIZE.
++ */
++
++ /* in trees.c */
++void _tr_init OF((deflate_state *s));
++int _tr_tally OF((deflate_state *s, unsigned dist, unsigned lc));
++void _tr_flush_block OF((deflate_state *s, charf *buf, ulg stored_len,
++ int eof));
++void _tr_align OF((deflate_state *s));
++void _tr_stored_block OF((deflate_state *s, charf *buf, ulg stored_len,
++ int eof));
++
++#define d_code(dist) \
++ ((dist) < 256 ? _dist_code[dist] : _dist_code[256+((dist)>>7)])
++/* Mapping from a distance to a distance code. dist is the distance - 1 and
++ * must not have side effects. _dist_code[256] and _dist_code[257] are never
++ * used.
++ */
++
++#ifndef DEBUG
++/* Inline versions of _tr_tally for speed: */
++
++#if defined(GEN_TREES_H) || !defined(STDC)
++ extern uch _length_code[];
++ extern uch _dist_code[];
++#else
++ extern const uch _length_code[];
++ extern const uch _dist_code[];
++#endif
++
++# define _tr_tally_lit(s, c, flush) \
++ { uch cc = (c); \
++ s->d_buf[s->last_lit] = 0; \
++ s->l_buf[s->last_lit++] = cc; \
++ s->dyn_ltree[cc].Freq++; \
++ flush = (s->last_lit == s->lit_bufsize-1); \
++ }
++# define _tr_tally_dist(s, distance, length, flush) \
++ { uch len = (length); \
++ ush dist = (distance); \
++ s->d_buf[s->last_lit] = dist; \
++ s->l_buf[s->last_lit++] = len; \
++ dist--; \
++ s->dyn_ltree[_length_code[len]+LITERALS+1].Freq++; \
++ s->dyn_dtree[d_code(dist)].Freq++; \
++ flush = (s->last_lit == s->lit_bufsize-1); \
++ }
++#else
++# define _tr_tally_lit(s, c, flush) flush = _tr_tally(s, 0, c)
++# define _tr_tally_dist(s, distance, length, flush) \
++ flush = _tr_tally(s, distance, length)
++#endif
++
++#endif /* _DEFLATE_H */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/zlib/infblock.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,403 @@
++/* infblock.c -- interpret and process block types to last block
++ * Copyright (C) 1995-2002 Mark Adler
++ * For conditions of distribution and use, see copyright notice in zlib.h
++ */
++
++#include <zlib/zutil.h>
++#include "infblock.h"
++#include "inftrees.h"
++#include "infcodes.h"
++#include "infutil.h"
++
++struct inflate_codes_state {int dummy;}; /* for buggy compilers */
++
++/* simplify the use of the inflate_huft type with some defines */
++#define exop word.what.Exop
++#define bits word.what.Bits
++
++/* Table for deflate from PKZIP's appnote.txt. */
++local const uInt border[] = { /* Order of the bit length code lengths */
++ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15};
++
++/*
++ Notes beyond the 1.93a appnote.txt:
++
++ 1. Distance pointers never point before the beginning of the output
++ stream.
++ 2. Distance pointers can point back across blocks, up to 32k away.
++ 3. There is an implied maximum of 7 bits for the bit length table and
++ 15 bits for the actual data.
++ 4. If only one code exists, then it is encoded using one bit. (Zero
++ would be more efficient, but perhaps a little confusing.) If two
++ codes exist, they are coded using one bit each (0 and 1).
++ 5. There is no way of sending zero distance codes--a dummy must be
++ sent if there are none. (History: a pre 2.0 version of PKZIP would
++ store blocks with no distance codes, but this was discovered to be
++ too harsh a criterion.) Valid only for 1.93a. 2.04c does allow
++ zero distance codes, which is sent as one code of zero bits in
++ length.
++ 6. There are up to 286 literal/length codes. Code 256 represents the
++ end-of-block. Note however that the static length tree defines
++ 288 codes just to fill out the Huffman codes. Codes 286 and 287
++ cannot be used though, since there is no length base or extra bits
++ defined for them. Similarily, there are up to 30 distance codes.
++ However, static trees define 32 codes (all 5 bits) to fill out the
++ Huffman codes, but the last two had better not show up in the data.
++ 7. Unzip can check dynamic Huffman blocks for complete code sets.
++ The exception is that a single code would not be complete (see #4).
++ 8. The five bits following the block type is really the number of
++ literal codes sent minus 257.
++ 9. Length codes 8,16,16 are interpreted as 13 length codes of 8 bits
++ (1+6+6). Therefore, to output three times the length, you output
++ three codes (1+1+1), whereas to output four times the same length,
++ you only need two codes (1+3). Hmm.
++ 10. In the tree reconstruction algorithm, Code = Code + Increment
++ only if BitLength(i) is not zero. (Pretty obvious.)
++ 11. Correction: 4 Bits: # of Bit Length codes - 4 (4 - 19)
++ 12. Note: length code 284 can represent 227-258, but length code 285
++ really is 258. The last length deserves its own, short code
++ since it gets used a lot in very redundant files. The length
++ 258 is special since 258 - 3 (the min match length) is 255.
++ 13. The literal/length and distance code bit lengths are read as a
++ single stream of lengths. It is possible (and advantageous) for
++ a repeat code (16, 17, or 18) to go across the boundary between
++ the two sets of lengths.
++ */
++
++
++void inflate_blocks_reset(s, z, c)
++inflate_blocks_statef *s;
++z_streamp z;
++uLongf *c;
++{
++ if (c != Z_NULL)
++ *c = s->check;
++ if (s->mode == BTREE || s->mode == DTREE)
++ ZFREE(z, s->sub.trees.blens);
++ if (s->mode == CODES)
++ inflate_codes_free(s->sub.decode.codes, z);
++ s->mode = TYPE;
++ s->bitk = 0;
++ s->bitb = 0;
++ s->read = s->write = s->window;
++ if (s->checkfn != Z_NULL)
++ z->adler = s->check = (*s->checkfn)(0L, (const Bytef *)Z_NULL, 0);
++ Tracev((stderr, "inflate: blocks reset\n"));
++}
++
++
++inflate_blocks_statef *inflate_blocks_new(z, c, w)
++z_streamp z;
++check_func c;
++uInt w;
++{
++ inflate_blocks_statef *s;
++
++ if ((s = (inflate_blocks_statef *)ZALLOC
++ (z,1,sizeof(struct inflate_blocks_state))) == Z_NULL)
++ return s;
++ if ((s->hufts =
++ (inflate_huft *)ZALLOC(z, sizeof(inflate_huft), MANY)) == Z_NULL)
++ {
++ ZFREE(z, s);
++ return Z_NULL;
++ }
++ if ((s->window = (Bytef *)ZALLOC(z, 1, w)) == Z_NULL)
++ {
++ ZFREE(z, s->hufts);
++ ZFREE(z, s);
++ return Z_NULL;
++ }
++ s->end = s->window + w;
++ s->checkfn = c;
++ s->mode = TYPE;
++ Tracev((stderr, "inflate: blocks allocated\n"));
++ inflate_blocks_reset(s, z, Z_NULL);
++ return s;
++}
++
++
++int inflate_blocks(s, z, r)
++inflate_blocks_statef *s;
++z_streamp z;
++int r;
++{
++ uInt t; /* temporary storage */
++ uLong b; /* bit buffer */
++ uInt k; /* bits in bit buffer */
++ Bytef *p; /* input data pointer */
++ uInt n; /* bytes available there */
++ Bytef *q; /* output window write pointer */
++ uInt m; /* bytes to end of window or read pointer */
++
++ /* copy input/output information to locals (UPDATE macro restores) */
++ LOAD
++
++ /* process input based on current state */
++ while (1) switch (s->mode)
++ {
++ case TYPE:
++ NEEDBITS(3)
++ t = (uInt)b & 7;
++ s->last = t & 1;
++ switch (t >> 1)
++ {
++ case 0: /* stored */
++ Tracev((stderr, "inflate: stored block%s\n",
++ s->last ? " (last)" : ""));
++ DUMPBITS(3)
++ t = k & 7; /* go to byte boundary */
++ DUMPBITS(t)
++ s->mode = LENS; /* get length of stored block */
++ break;
++ case 1: /* fixed */
++ Tracev((stderr, "inflate: fixed codes block%s\n",
++ s->last ? " (last)" : ""));
++ {
++ uInt bl, bd;
++ inflate_huft *tl, *td;
++
++ inflate_trees_fixed(&bl, &bd, &tl, &td, z);
++ s->sub.decode.codes = inflate_codes_new(bl, bd, tl, td, z);
++ if (s->sub.decode.codes == Z_NULL)
++ {
++ r = Z_MEM_ERROR;
++ LEAVE
++ }
++ }
++ DUMPBITS(3)
++ s->mode = CODES;
++ break;
++ case 2: /* dynamic */
++ Tracev((stderr, "inflate: dynamic codes block%s\n",
++ s->last ? " (last)" : ""));
++ DUMPBITS(3)
++ s->mode = TABLE;
++ break;
++ case 3: /* illegal */
++ DUMPBITS(3)
++ s->mode = BAD;
++ z->msg = (char*)"invalid block type";
++ r = Z_DATA_ERROR;
++ LEAVE
++ }
++ break;
++ case LENS:
++ NEEDBITS(32)
++ if ((((~b) >> 16) & 0xffff) != (b & 0xffff))
++ {
++ s->mode = BAD;
++ z->msg = (char*)"invalid stored block lengths";
++ r = Z_DATA_ERROR;
++ LEAVE
++ }
++ s->sub.left = (uInt)b & 0xffff;
++ b = k = 0; /* dump bits */
++ Tracev((stderr, "inflate: stored length %u\n", s->sub.left));
++ s->mode = s->sub.left ? STORED : (s->last ? DRY : TYPE);
++ break;
++ case STORED:
++ if (n == 0)
++ LEAVE
++ NEEDOUT
++ t = s->sub.left;
++ if (t > n) t = n;
++ if (t > m) t = m;
++ zmemcpy(q, p, t);
++ p += t; n -= t;
++ q += t; m -= t;
++ if ((s->sub.left -= t) != 0)
++ break;
++ Tracev((stderr, "inflate: stored end, %lu total out\n",
++ z->total_out + (q >= s->read ? q - s->read :
++ (s->end - s->read) + (q - s->window))));
++ s->mode = s->last ? DRY : TYPE;
++ break;
++ case TABLE:
++ NEEDBITS(14)
++ s->sub.trees.table = t = (uInt)b & 0x3fff;
++#ifndef PKZIP_BUG_WORKAROUND
++ if ((t & 0x1f) > 29 || ((t >> 5) & 0x1f) > 29)
++ {
++ s->mode = BAD;
++ z->msg = (char*)"too many length or distance symbols";
++ r = Z_DATA_ERROR;
++ LEAVE
++ }
++#endif
++ t = 258 + (t & 0x1f) + ((t >> 5) & 0x1f);
++ if ((s->sub.trees.blens = (uIntf*)ZALLOC(z, t, sizeof(uInt))) == Z_NULL)
++ {
++ r = Z_MEM_ERROR;
++ LEAVE
++ }
++ DUMPBITS(14)
++ s->sub.trees.index = 0;
++ Tracev((stderr, "inflate: table sizes ok\n"));
++ s->mode = BTREE;
++ case BTREE:
++ while (s->sub.trees.index < 4 + (s->sub.trees.table >> 10))
++ {
++ NEEDBITS(3)
++ s->sub.trees.blens[border[s->sub.trees.index++]] = (uInt)b & 7;
++ DUMPBITS(3)
++ }
++ while (s->sub.trees.index < 19)
++ s->sub.trees.blens[border[s->sub.trees.index++]] = 0;
++ s->sub.trees.bb = 7;
++ t = inflate_trees_bits(s->sub.trees.blens, &s->sub.trees.bb,
++ &s->sub.trees.tb, s->hufts, z);
++ if (t != Z_OK)
++ {
++ r = t;
++ if (r == Z_DATA_ERROR)
++ {
++ ZFREE(z, s->sub.trees.blens);
++ s->mode = BAD;
++ }
++ LEAVE
++ }
++ s->sub.trees.index = 0;
++ Tracev((stderr, "inflate: bits tree ok\n"));
++ s->mode = DTREE;
++ case DTREE:
++ while (t = s->sub.trees.table,
++ s->sub.trees.index < 258 + (t & 0x1f) + ((t >> 5) & 0x1f))
++ {
++ inflate_huft *h;
++ uInt i, j, c;
++
++ t = s->sub.trees.bb;
++ NEEDBITS(t)
++ h = s->sub.trees.tb + ((uInt)b & inflate_mask[t]);
++ t = h->bits;
++ c = h->base;
++ if (c < 16)
++ {
++ DUMPBITS(t)
++ s->sub.trees.blens[s->sub.trees.index++] = c;
++ }
++ else /* c == 16..18 */
++ {
++ i = c == 18 ? 7 : c - 14;
++ j = c == 18 ? 11 : 3;
++ NEEDBITS(t + i)
++ DUMPBITS(t)
++ j += (uInt)b & inflate_mask[i];
++ DUMPBITS(i)
++ i = s->sub.trees.index;
++ t = s->sub.trees.table;
++ if (i + j > 258 + (t & 0x1f) + ((t >> 5) & 0x1f) ||
++ (c == 16 && i < 1))
++ {
++ ZFREE(z, s->sub.trees.blens);
++ s->mode = BAD;
++ z->msg = (char*)"invalid bit length repeat";
++ r = Z_DATA_ERROR;
++ LEAVE
++ }
++ c = c == 16 ? s->sub.trees.blens[i - 1] : 0;
++ do {
++ s->sub.trees.blens[i++] = c;
++ } while (--j);
++ s->sub.trees.index = i;
++ }
++ }
++ s->sub.trees.tb = Z_NULL;
++ {
++ uInt bl, bd;
++ inflate_huft *tl, *td;
++ inflate_codes_statef *c;
++
++ bl = 9; /* must be <= 9 for lookahead assumptions */
++ bd = 6; /* must be <= 9 for lookahead assumptions */
++ t = s->sub.trees.table;
++ t = inflate_trees_dynamic(257 + (t & 0x1f), 1 + ((t >> 5) & 0x1f),
++ s->sub.trees.blens, &bl, &bd, &tl, &td,
++ s->hufts, z);
++ if (t != Z_OK)
++ {
++ if (t == (uInt)Z_DATA_ERROR)
++ {
++ ZFREE(z, s->sub.trees.blens);
++ s->mode = BAD;
++ }
++ r = t;
++ LEAVE
++ }
++ Tracev((stderr, "inflate: trees ok\n"));
++ if ((c = inflate_codes_new(bl, bd, tl, td, z)) == Z_NULL)
++ {
++ r = Z_MEM_ERROR;
++ LEAVE
++ }
++ s->sub.decode.codes = c;
++ }
++ ZFREE(z, s->sub.trees.blens);
++ s->mode = CODES;
++ case CODES:
++ UPDATE
++ if ((r = inflate_codes(s, z, r)) != Z_STREAM_END)
++ return inflate_flush(s, z, r);
++ r = Z_OK;
++ inflate_codes_free(s->sub.decode.codes, z);
++ LOAD
++ Tracev((stderr, "inflate: codes end, %lu total out\n",
++ z->total_out + (q >= s->read ? q - s->read :
++ (s->end - s->read) + (q - s->window))));
++ if (!s->last)
++ {
++ s->mode = TYPE;
++ break;
++ }
++ s->mode = DRY;
++ case DRY:
++ FLUSH
++ if (s->read != s->write)
++ LEAVE
++ s->mode = DONE;
++ case DONE:
++ r = Z_STREAM_END;
++ LEAVE
++ case BAD:
++ r = Z_DATA_ERROR;
++ LEAVE
++ default:
++ r = Z_STREAM_ERROR;
++ LEAVE
++ }
++}
++
++
++int inflate_blocks_free(s, z)
++inflate_blocks_statef *s;
++z_streamp z;
++{
++ inflate_blocks_reset(s, z, Z_NULL);
++ ZFREE(z, s->window);
++ ZFREE(z, s->hufts);
++ ZFREE(z, s);
++ Tracev((stderr, "inflate: blocks freed\n"));
++ return Z_OK;
++}
++
++
++void inflate_set_dictionary(s, d, n)
++inflate_blocks_statef *s;
++const Bytef *d;
++uInt n;
++{
++ zmemcpy(s->window, d, n);
++ s->read = s->write = s->window + n;
++}
++
++
++/* Returns true if inflate is currently at the end of a block generated
++ * by Z_SYNC_FLUSH or Z_FULL_FLUSH.
++ * IN assertion: s != Z_NULL
++ */
++int inflate_blocks_sync_point(s)
++inflate_blocks_statef *s;
++{
++ return s->mode == LENS;
++}
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/zlib/infblock.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,39 @@
++/* infblock.h -- header to use infblock.c
++ * Copyright (C) 1995-2002 Mark Adler
++ * For conditions of distribution and use, see copyright notice in zlib.h
++ */
++
++/* WARNING: this file should *not* be used by applications. It is
++ part of the implementation of the compression library and is
++ subject to change. Applications should only use zlib.h.
++ */
++
++struct inflate_blocks_state;
++typedef struct inflate_blocks_state FAR inflate_blocks_statef;
++
++extern inflate_blocks_statef * inflate_blocks_new OF((
++ z_streamp z,
++ check_func c, /* check function */
++ uInt w)); /* window size */
++
++extern int inflate_blocks OF((
++ inflate_blocks_statef *,
++ z_streamp ,
++ int)); /* initial return code */
++
++extern void inflate_blocks_reset OF((
++ inflate_blocks_statef *,
++ z_streamp ,
++ uLongf *)); /* check value on output */
++
++extern int inflate_blocks_free OF((
++ inflate_blocks_statef *,
++ z_streamp));
++
++extern void inflate_set_dictionary OF((
++ inflate_blocks_statef *s,
++ const Bytef *d, /* dictionary */
++ uInt n)); /* dictionary length */
++
++extern int inflate_blocks_sync_point OF((
++ inflate_blocks_statef *s));
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/zlib/infcodes.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,251 @@
++/* infcodes.c -- process literals and length/distance pairs
++ * Copyright (C) 1995-2002 Mark Adler
++ * For conditions of distribution and use, see copyright notice in zlib.h
++ */
++
++#include <zlib/zutil.h>
++#include "inftrees.h"
++#include "infblock.h"
++#include "infcodes.h"
++#include "infutil.h"
++#include "inffast.h"
++
++/* simplify the use of the inflate_huft type with some defines */
++#define exop word.what.Exop
++#define bits word.what.Bits
++
++typedef enum { /* waiting for "i:"=input, "o:"=output, "x:"=nothing */
++ START, /* x: set up for LEN */
++ LEN, /* i: get length/literal/eob next */
++ LENEXT, /* i: getting length extra (have base) */
++ DIST, /* i: get distance next */
++ DISTEXT, /* i: getting distance extra */
++ COPY, /* o: copying bytes in window, waiting for space */
++ LIT, /* o: got literal, waiting for output space */
++ WASH, /* o: got eob, possibly still output waiting */
++ END, /* x: got eob and all data flushed */
++ BADCODE} /* x: got error */
++inflate_codes_mode;
++
++/* inflate codes private state */
++struct inflate_codes_state {
++
++ /* mode */
++ inflate_codes_mode mode; /* current inflate_codes mode */
++
++ /* mode dependent information */
++ uInt len;
++ union {
++ struct {
++ inflate_huft *tree; /* pointer into tree */
++ uInt need; /* bits needed */
++ } code; /* if LEN or DIST, where in tree */
++ uInt lit; /* if LIT, literal */
++ struct {
++ uInt get; /* bits to get for extra */
++ uInt dist; /* distance back to copy from */
++ } copy; /* if EXT or COPY, where and how much */
++ } sub; /* submode */
++
++ /* mode independent information */
++ Byte lbits; /* ltree bits decoded per branch */
++ Byte dbits; /* dtree bits decoder per branch */
++ inflate_huft *ltree; /* literal/length/eob tree */
++ inflate_huft *dtree; /* distance tree */
++
++};
++
++
++inflate_codes_statef *inflate_codes_new(bl, bd, tl, td, z)
++uInt bl, bd;
++inflate_huft *tl;
++inflate_huft *td; /* need separate declaration for Borland C++ */
++z_streamp z;
++{
++ inflate_codes_statef *c;
++
++ if ((c = (inflate_codes_statef *)
++ ZALLOC(z,1,sizeof(struct inflate_codes_state))) != Z_NULL)
++ {
++ c->mode = START;
++ c->lbits = (Byte)bl;
++ c->dbits = (Byte)bd;
++ c->ltree = tl;
++ c->dtree = td;
++ Tracev((stderr, "inflate: codes new\n"));
++ }
++ return c;
++}
++
++
++int inflate_codes(s, z, r)
++inflate_blocks_statef *s;
++z_streamp z;
++int r;
++{
++ uInt j; /* temporary storage */
++ inflate_huft *t; /* temporary pointer */
++ uInt e; /* extra bits or operation */
++ uLong b; /* bit buffer */
++ uInt k; /* bits in bit buffer */
++ Bytef *p; /* input data pointer */
++ uInt n; /* bytes available there */
++ Bytef *q; /* output window write pointer */
++ uInt m; /* bytes to end of window or read pointer */
++ Bytef *f; /* pointer to copy strings from */
++ inflate_codes_statef *c = s->sub.decode.codes; /* codes state */
++
++ /* copy input/output information to locals (UPDATE macro restores) */
++ LOAD
++
++ /* process input and output based on current state */
++ while (1) switch (c->mode)
++ { /* waiting for "i:"=input, "o:"=output, "x:"=nothing */
++ case START: /* x: set up for LEN */
++#ifndef SLOW
++ if (m >= 258 && n >= 10)
++ {
++ UPDATE
++ r = inflate_fast(c->lbits, c->dbits, c->ltree, c->dtree, s, z);
++ LOAD
++ if (r != Z_OK)
++ {
++ c->mode = r == Z_STREAM_END ? WASH : BADCODE;
++ break;
++ }
++ }
++#endif /* !SLOW */
++ c->sub.code.need = c->lbits;
++ c->sub.code.tree = c->ltree;
++ c->mode = LEN;
++ case LEN: /* i: get length/literal/eob next */
++ j = c->sub.code.need;
++ NEEDBITS(j)
++ t = c->sub.code.tree + ((uInt)b & inflate_mask[j]);
++ DUMPBITS(t->bits)
++ e = (uInt)(t->exop);
++ if (e == 0) /* literal */
++ {
++ c->sub.lit = t->base;
++ Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ?
++ "inflate: literal '%c'\n" :
++ "inflate: literal 0x%02x\n", t->base));
++ c->mode = LIT;
++ break;
++ }
++ if (e & 16) /* length */
++ {
++ c->sub.copy.get = e & 15;
++ c->len = t->base;
++ c->mode = LENEXT;
++ break;
++ }
++ if ((e & 64) == 0) /* next table */
++ {
++ c->sub.code.need = e;
++ c->sub.code.tree = t + t->base;
++ break;
++ }
++ if (e & 32) /* end of block */
++ {
++ Tracevv((stderr, "inflate: end of block\n"));
++ c->mode = WASH;
++ break;
++ }
++ c->mode = BADCODE; /* invalid code */
++ z->msg = (char*)"invalid literal/length code";
++ r = Z_DATA_ERROR;
++ LEAVE
++ case LENEXT: /* i: getting length extra (have base) */
++ j = c->sub.copy.get;
++ NEEDBITS(j)
++ c->len += (uInt)b & inflate_mask[j];
++ DUMPBITS(j)
++ c->sub.code.need = c->dbits;
++ c->sub.code.tree = c->dtree;
++ Tracevv((stderr, "inflate: length %u\n", c->len));
++ c->mode = DIST;
++ case DIST: /* i: get distance next */
++ j = c->sub.code.need;
++ NEEDBITS(j)
++ t = c->sub.code.tree + ((uInt)b & inflate_mask[j]);
++ DUMPBITS(t->bits)
++ e = (uInt)(t->exop);
++ if (e & 16) /* distance */
++ {
++ c->sub.copy.get = e & 15;
++ c->sub.copy.dist = t->base;
++ c->mode = DISTEXT;
++ break;
++ }
++ if ((e & 64) == 0) /* next table */
++ {
++ c->sub.code.need = e;
++ c->sub.code.tree = t + t->base;
++ break;
++ }
++ c->mode = BADCODE; /* invalid code */
++ z->msg = (char*)"invalid distance code";
++ r = Z_DATA_ERROR;
++ LEAVE
++ case DISTEXT: /* i: getting distance extra */
++ j = c->sub.copy.get;
++ NEEDBITS(j)
++ c->sub.copy.dist += (uInt)b & inflate_mask[j];
++ DUMPBITS(j)
++ Tracevv((stderr, "inflate: distance %u\n", c->sub.copy.dist));
++ c->mode = COPY;
++ case COPY: /* o: copying bytes in window, waiting for space */
++ f = q - c->sub.copy.dist;
++ while (f < s->window) /* modulo window size-"while" instead */
++ f += s->end - s->window; /* of "if" handles invalid distances */
++ while (c->len)
++ {
++ NEEDOUT
++ OUTBYTE(*f++)
++ if (f == s->end)
++ f = s->window;
++ c->len--;
++ }
++ c->mode = START;
++ break;
++ case LIT: /* o: got literal, waiting for output space */
++ NEEDOUT
++ OUTBYTE(c->sub.lit)
++ c->mode = START;
++ break;
++ case WASH: /* o: got eob, possibly more output */
++ if (k > 7) /* return unused byte, if any */
++ {
++ Assert(k < 16, "inflate_codes grabbed too many bytes")
++ k -= 8;
++ n++;
++ p--; /* can always return one */
++ }
++ FLUSH
++ if (s->read != s->write)
++ LEAVE
++ c->mode = END;
++ case END:
++ r = Z_STREAM_END;
++ LEAVE
++ case BADCODE: /* x: got error */
++ r = Z_DATA_ERROR;
++ LEAVE
++ default:
++ r = Z_STREAM_ERROR;
++ LEAVE
++ }
++#ifdef NEED_DUMMY_RETURN
++ return Z_STREAM_ERROR; /* Some dumb compilers complain without this */
++#endif
++}
++
++
++void inflate_codes_free(c, z)
++inflate_codes_statef *c;
++z_streamp z;
++{
++ ZFREE(z, c);
++ Tracev((stderr, "inflate: codes free\n"));
++}
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/zlib/infcodes.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,31 @@
++/* infcodes.h -- header to use infcodes.c
++ * Copyright (C) 1995-2002 Mark Adler
++ * For conditions of distribution and use, see copyright notice in zlib.h
++ */
++
++/* WARNING: this file should *not* be used by applications. It is
++ part of the implementation of the compression library and is
++ subject to change. Applications should only use zlib.h.
++ */
++
++#ifndef _INFCODES_H
++#define _INFCODES_H
++
++struct inflate_codes_state;
++typedef struct inflate_codes_state FAR inflate_codes_statef;
++
++extern inflate_codes_statef *inflate_codes_new OF((
++ uInt, uInt,
++ inflate_huft *, inflate_huft *,
++ z_streamp ));
++
++extern int inflate_codes OF((
++ inflate_blocks_statef *,
++ z_streamp ,
++ int));
++
++extern void inflate_codes_free OF((
++ inflate_codes_statef *,
++ z_streamp ));
++
++#endif /* _INFCODES_H */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/zlib/inffast.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,183 @@
++/* inffast.c -- process literals and length/distance pairs fast
++ * Copyright (C) 1995-2002 Mark Adler
++ * For conditions of distribution and use, see copyright notice in zlib.h
++ */
++
++#include <zlib/zutil.h>
++#include "inftrees.h"
++#include "infblock.h"
++#include "infcodes.h"
++#include "infutil.h"
++#include "inffast.h"
++
++struct inflate_codes_state {int dummy;}; /* for buggy compilers */
++
++/* simplify the use of the inflate_huft type with some defines */
++#define exop word.what.Exop
++#define bits word.what.Bits
++
++/* macros for bit input with no checking and for returning unused bytes */
++#define GRABBITS(j) {while(k<(j)){b|=((uLong)NEXTBYTE)<<k;k+=8;}}
++#define UNGRAB {c=z->avail_in-n;c=(k>>3)<c?k>>3:c;n+=c;p-=c;k-=c<<3;}
++
++/* Called with number of bytes left to write in window at least 258
++ (the maximum string length) and number of input bytes available
++ at least ten. The ten bytes are six bytes for the longest length/
++ distance pair plus four bytes for overloading the bit buffer. */
++
++int inflate_fast(bl, bd, tl, td, s, z)
++uInt bl, bd;
++inflate_huft *tl;
++inflate_huft *td; /* need separate declaration for Borland C++ */
++inflate_blocks_statef *s;
++z_streamp z;
++{
++ inflate_huft *t; /* temporary pointer */
++ uInt e; /* extra bits or operation */
++ uLong b; /* bit buffer */
++ uInt k; /* bits in bit buffer */
++ Bytef *p; /* input data pointer */
++ uInt n; /* bytes available there */
++ Bytef *q; /* output window write pointer */
++ uInt m; /* bytes to end of window or read pointer */
++ uInt ml; /* mask for literal/length tree */
++ uInt md; /* mask for distance tree */
++ uInt c; /* bytes to copy */
++ uInt d; /* distance back to copy from */
++ Bytef *r; /* copy source pointer */
++
++ /* load input, output, bit values */
++ LOAD
++
++ /* initialize masks */
++ ml = inflate_mask[bl];
++ md = inflate_mask[bd];
++
++ /* do until not enough input or output space for fast loop */
++ do { /* assume called with m >= 258 && n >= 10 */
++ /* get literal/length code */
++ GRABBITS(20) /* max bits for literal/length code */
++ if ((e = (t = tl + ((uInt)b & ml))->exop) == 0)
++ {
++ DUMPBITS(t->bits)
++ Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ?
++ "inflate: * literal '%c'\n" :
++ "inflate: * literal 0x%02x\n", t->base));
++ *q++ = (Byte)t->base;
++ m--;
++ continue;
++ }
++ do {
++ DUMPBITS(t->bits)
++ if (e & 16)
++ {
++ /* get extra bits for length */
++ e &= 15;
++ c = t->base + ((uInt)b & inflate_mask[e]);
++ DUMPBITS(e)
++ Tracevv((stderr, "inflate: * length %u\n", c));
++
++ /* decode distance base of block to copy */
++ GRABBITS(15); /* max bits for distance code */
++ e = (t = td + ((uInt)b & md))->exop;
++ do {
++ DUMPBITS(t->bits)
++ if (e & 16)
++ {
++ /* get extra bits to add to distance base */
++ e &= 15;
++ GRABBITS(e) /* get extra bits (up to 13) */
++ d = t->base + ((uInt)b & inflate_mask[e]);
++ DUMPBITS(e)
++ Tracevv((stderr, "inflate: * distance %u\n", d));
++
++ /* do the copy */
++ m -= c;
++ r = q - d;
++ if (r < s->window) /* wrap if needed */
++ {
++ do {
++ r += s->end - s->window; /* force pointer in window */
++ } while (r < s->window); /* covers invalid distances */
++ e = s->end - r;
++ if (c > e)
++ {
++ c -= e; /* wrapped copy */
++ do {
++ *q++ = *r++;
++ } while (--e);
++ r = s->window;
++ do {
++ *q++ = *r++;
++ } while (--c);
++ }
++ else /* normal copy */
++ {
++ *q++ = *r++; c--;
++ *q++ = *r++; c--;
++ do {
++ *q++ = *r++;
++ } while (--c);
++ }
++ }
++ else /* normal copy */
++ {
++ *q++ = *r++; c--;
++ *q++ = *r++; c--;
++ do {
++ *q++ = *r++;
++ } while (--c);
++ }
++ break;
++ }
++ else if ((e & 64) == 0)
++ {
++ t += t->base;
++ e = (t += ((uInt)b & inflate_mask[e]))->exop;
++ }
++ else
++ {
++ z->msg = (char*)"invalid distance code";
++ UNGRAB
++ UPDATE
++ return Z_DATA_ERROR;
++ }
++ } while (1);
++ break;
++ }
++ if ((e & 64) == 0)
++ {
++ t += t->base;
++ if ((e = (t += ((uInt)b & inflate_mask[e]))->exop) == 0)
++ {
++ DUMPBITS(t->bits)
++ Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ?
++ "inflate: * literal '%c'\n" :
++ "inflate: * literal 0x%02x\n", t->base));
++ *q++ = (Byte)t->base;
++ m--;
++ break;
++ }
++ }
++ else if (e & 32)
++ {
++ Tracevv((stderr, "inflate: * end of block\n"));
++ UNGRAB
++ UPDATE
++ return Z_STREAM_END;
++ }
++ else
++ {
++ z->msg = (char*)"invalid literal/length code";
++ UNGRAB
++ UPDATE
++ return Z_DATA_ERROR;
++ }
++ } while (1);
++ } while (m >= 258 && n >= 10);
++
++ /* not enough input or output--restore pointers and return */
++ UNGRAB
++ UPDATE
++ return Z_OK;
++}
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/zlib/inffast.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,22 @@
++/* inffast.h -- header to use inffast.c
++ * Copyright (C) 1995-2002 Mark Adler
++ * For conditions of distribution and use, see copyright notice in zlib.h
++ */
++
++/* WARNING: this file should *not* be used by applications. It is
++ part of the implementation of the compression library and is
++ subject to change. Applications should only use zlib.h.
++ */
++
++#ifndef _INFFAST_H
++#define _INFFAST_H
++
++extern int inflate_fast OF((
++ uInt,
++ uInt,
++ inflate_huft *,
++ inflate_huft *,
++ inflate_blocks_statef *,
++ z_streamp ));
++
++#endif /* _INFFAST_H */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/zlib/inffixed.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,151 @@
++/* inffixed.h -- table for decoding fixed codes
++ * Generated automatically by the maketree.c program
++ */
++
++/* WARNING: this file should *not* be used by applications. It is
++ part of the implementation of the compression library and is
++ subject to change. Applications should only use zlib.h.
++ */
++
++local uInt fixed_bl = 9;
++local uInt fixed_bd = 5;
++local inflate_huft fixed_tl[] = {
++ {{{96,7}},256}, {{{0,8}},80}, {{{0,8}},16}, {{{84,8}},115},
++ {{{82,7}},31}, {{{0,8}},112}, {{{0,8}},48}, {{{0,9}},192},
++ {{{80,7}},10}, {{{0,8}},96}, {{{0,8}},32}, {{{0,9}},160},
++ {{{0,8}},0}, {{{0,8}},128}, {{{0,8}},64}, {{{0,9}},224},
++ {{{80,7}},6}, {{{0,8}},88}, {{{0,8}},24}, {{{0,9}},144},
++ {{{83,7}},59}, {{{0,8}},120}, {{{0,8}},56}, {{{0,9}},208},
++ {{{81,7}},17}, {{{0,8}},104}, {{{0,8}},40}, {{{0,9}},176},
++ {{{0,8}},8}, {{{0,8}},136}, {{{0,8}},72}, {{{0,9}},240},
++ {{{80,7}},4}, {{{0,8}},84}, {{{0,8}},20}, {{{85,8}},227},
++ {{{83,7}},43}, {{{0,8}},116}, {{{0,8}},52}, {{{0,9}},200},
++ {{{81,7}},13}, {{{0,8}},100}, {{{0,8}},36}, {{{0,9}},168},
++ {{{0,8}},4}, {{{0,8}},132}, {{{0,8}},68}, {{{0,9}},232},
++ {{{80,7}},8}, {{{0,8}},92}, {{{0,8}},28}, {{{0,9}},152},
++ {{{84,7}},83}, {{{0,8}},124}, {{{0,8}},60}, {{{0,9}},216},
++ {{{82,7}},23}, {{{0,8}},108}, {{{0,8}},44}, {{{0,9}},184},
++ {{{0,8}},12}, {{{0,8}},140}, {{{0,8}},76}, {{{0,9}},248},
++ {{{80,7}},3}, {{{0,8}},82}, {{{0,8}},18}, {{{85,8}},163},
++ {{{83,7}},35}, {{{0,8}},114}, {{{0,8}},50}, {{{0,9}},196},
++ {{{81,7}},11}, {{{0,8}},98}, {{{0,8}},34}, {{{0,9}},164},
++ {{{0,8}},2}, {{{0,8}},130}, {{{0,8}},66}, {{{0,9}},228},
++ {{{80,7}},7}, {{{0,8}},90}, {{{0,8}},26}, {{{0,9}},148},
++ {{{84,7}},67}, {{{0,8}},122}, {{{0,8}},58}, {{{0,9}},212},
++ {{{82,7}},19}, {{{0,8}},106}, {{{0,8}},42}, {{{0,9}},180},
++ {{{0,8}},10}, {{{0,8}},138}, {{{0,8}},74}, {{{0,9}},244},
++ {{{80,7}},5}, {{{0,8}},86}, {{{0,8}},22}, {{{192,8}},0},
++ {{{83,7}},51}, {{{0,8}},118}, {{{0,8}},54}, {{{0,9}},204},
++ {{{81,7}},15}, {{{0,8}},102}, {{{0,8}},38}, {{{0,9}},172},
++ {{{0,8}},6}, {{{0,8}},134}, {{{0,8}},70}, {{{0,9}},236},
++ {{{80,7}},9}, {{{0,8}},94}, {{{0,8}},30}, {{{0,9}},156},
++ {{{84,7}},99}, {{{0,8}},126}, {{{0,8}},62}, {{{0,9}},220},
++ {{{82,7}},27}, {{{0,8}},110}, {{{0,8}},46}, {{{0,9}},188},
++ {{{0,8}},14}, {{{0,8}},142}, {{{0,8}},78}, {{{0,9}},252},
++ {{{96,7}},256}, {{{0,8}},81}, {{{0,8}},17}, {{{85,8}},131},
++ {{{82,7}},31}, {{{0,8}},113}, {{{0,8}},49}, {{{0,9}},194},
++ {{{80,7}},10}, {{{0,8}},97}, {{{0,8}},33}, {{{0,9}},162},
++ {{{0,8}},1}, {{{0,8}},129}, {{{0,8}},65}, {{{0,9}},226},
++ {{{80,7}},6}, {{{0,8}},89}, {{{0,8}},25}, {{{0,9}},146},
++ {{{83,7}},59}, {{{0,8}},121}, {{{0,8}},57}, {{{0,9}},210},
++ {{{81,7}},17}, {{{0,8}},105}, {{{0,8}},41}, {{{0,9}},178},
++ {{{0,8}},9}, {{{0,8}},137}, {{{0,8}},73}, {{{0,9}},242},
++ {{{80,7}},4}, {{{0,8}},85}, {{{0,8}},21}, {{{80,8}},258},
++ {{{83,7}},43}, {{{0,8}},117}, {{{0,8}},53}, {{{0,9}},202},
++ {{{81,7}},13}, {{{0,8}},101}, {{{0,8}},37}, {{{0,9}},170},
++ {{{0,8}},5}, {{{0,8}},133}, {{{0,8}},69}, {{{0,9}},234},
++ {{{80,7}},8}, {{{0,8}},93}, {{{0,8}},29}, {{{0,9}},154},
++ {{{84,7}},83}, {{{0,8}},125}, {{{0,8}},61}, {{{0,9}},218},
++ {{{82,7}},23}, {{{0,8}},109}, {{{0,8}},45}, {{{0,9}},186},
++ {{{0,8}},13}, {{{0,8}},141}, {{{0,8}},77}, {{{0,9}},250},
++ {{{80,7}},3}, {{{0,8}},83}, {{{0,8}},19}, {{{85,8}},195},
++ {{{83,7}},35}, {{{0,8}},115}, {{{0,8}},51}, {{{0,9}},198},
++ {{{81,7}},11}, {{{0,8}},99}, {{{0,8}},35}, {{{0,9}},166},
++ {{{0,8}},3}, {{{0,8}},131}, {{{0,8}},67}, {{{0,9}},230},
++ {{{80,7}},7}, {{{0,8}},91}, {{{0,8}},27}, {{{0,9}},150},
++ {{{84,7}},67}, {{{0,8}},123}, {{{0,8}},59}, {{{0,9}},214},
++ {{{82,7}},19}, {{{0,8}},107}, {{{0,8}},43}, {{{0,9}},182},
++ {{{0,8}},11}, {{{0,8}},139}, {{{0,8}},75}, {{{0,9}},246},
++ {{{80,7}},5}, {{{0,8}},87}, {{{0,8}},23}, {{{192,8}},0},
++ {{{83,7}},51}, {{{0,8}},119}, {{{0,8}},55}, {{{0,9}},206},
++ {{{81,7}},15}, {{{0,8}},103}, {{{0,8}},39}, {{{0,9}},174},
++ {{{0,8}},7}, {{{0,8}},135}, {{{0,8}},71}, {{{0,9}},238},
++ {{{80,7}},9}, {{{0,8}},95}, {{{0,8}},31}, {{{0,9}},158},
++ {{{84,7}},99}, {{{0,8}},127}, {{{0,8}},63}, {{{0,9}},222},
++ {{{82,7}},27}, {{{0,8}},111}, {{{0,8}},47}, {{{0,9}},190},
++ {{{0,8}},15}, {{{0,8}},143}, {{{0,8}},79}, {{{0,9}},254},
++ {{{96,7}},256}, {{{0,8}},80}, {{{0,8}},16}, {{{84,8}},115},
++ {{{82,7}},31}, {{{0,8}},112}, {{{0,8}},48}, {{{0,9}},193},
++ {{{80,7}},10}, {{{0,8}},96}, {{{0,8}},32}, {{{0,9}},161},
++ {{{0,8}},0}, {{{0,8}},128}, {{{0,8}},64}, {{{0,9}},225},
++ {{{80,7}},6}, {{{0,8}},88}, {{{0,8}},24}, {{{0,9}},145},
++ {{{83,7}},59}, {{{0,8}},120}, {{{0,8}},56}, {{{0,9}},209},
++ {{{81,7}},17}, {{{0,8}},104}, {{{0,8}},40}, {{{0,9}},177},
++ {{{0,8}},8}, {{{0,8}},136}, {{{0,8}},72}, {{{0,9}},241},
++ {{{80,7}},4}, {{{0,8}},84}, {{{0,8}},20}, {{{85,8}},227},
++ {{{83,7}},43}, {{{0,8}},116}, {{{0,8}},52}, {{{0,9}},201},
++ {{{81,7}},13}, {{{0,8}},100}, {{{0,8}},36}, {{{0,9}},169},
++ {{{0,8}},4}, {{{0,8}},132}, {{{0,8}},68}, {{{0,9}},233},
++ {{{80,7}},8}, {{{0,8}},92}, {{{0,8}},28}, {{{0,9}},153},
++ {{{84,7}},83}, {{{0,8}},124}, {{{0,8}},60}, {{{0,9}},217},
++ {{{82,7}},23}, {{{0,8}},108}, {{{0,8}},44}, {{{0,9}},185},
++ {{{0,8}},12}, {{{0,8}},140}, {{{0,8}},76}, {{{0,9}},249},
++ {{{80,7}},3}, {{{0,8}},82}, {{{0,8}},18}, {{{85,8}},163},
++ {{{83,7}},35}, {{{0,8}},114}, {{{0,8}},50}, {{{0,9}},197},
++ {{{81,7}},11}, {{{0,8}},98}, {{{0,8}},34}, {{{0,9}},165},
++ {{{0,8}},2}, {{{0,8}},130}, {{{0,8}},66}, {{{0,9}},229},
++ {{{80,7}},7}, {{{0,8}},90}, {{{0,8}},26}, {{{0,9}},149},
++ {{{84,7}},67}, {{{0,8}},122}, {{{0,8}},58}, {{{0,9}},213},
++ {{{82,7}},19}, {{{0,8}},106}, {{{0,8}},42}, {{{0,9}},181},
++ {{{0,8}},10}, {{{0,8}},138}, {{{0,8}},74}, {{{0,9}},245},
++ {{{80,7}},5}, {{{0,8}},86}, {{{0,8}},22}, {{{192,8}},0},
++ {{{83,7}},51}, {{{0,8}},118}, {{{0,8}},54}, {{{0,9}},205},
++ {{{81,7}},15}, {{{0,8}},102}, {{{0,8}},38}, {{{0,9}},173},
++ {{{0,8}},6}, {{{0,8}},134}, {{{0,8}},70}, {{{0,9}},237},
++ {{{80,7}},9}, {{{0,8}},94}, {{{0,8}},30}, {{{0,9}},157},
++ {{{84,7}},99}, {{{0,8}},126}, {{{0,8}},62}, {{{0,9}},221},
++ {{{82,7}},27}, {{{0,8}},110}, {{{0,8}},46}, {{{0,9}},189},
++ {{{0,8}},14}, {{{0,8}},142}, {{{0,8}},78}, {{{0,9}},253},
++ {{{96,7}},256}, {{{0,8}},81}, {{{0,8}},17}, {{{85,8}},131},
++ {{{82,7}},31}, {{{0,8}},113}, {{{0,8}},49}, {{{0,9}},195},
++ {{{80,7}},10}, {{{0,8}},97}, {{{0,8}},33}, {{{0,9}},163},
++ {{{0,8}},1}, {{{0,8}},129}, {{{0,8}},65}, {{{0,9}},227},
++ {{{80,7}},6}, {{{0,8}},89}, {{{0,8}},25}, {{{0,9}},147},
++ {{{83,7}},59}, {{{0,8}},121}, {{{0,8}},57}, {{{0,9}},211},
++ {{{81,7}},17}, {{{0,8}},105}, {{{0,8}},41}, {{{0,9}},179},
++ {{{0,8}},9}, {{{0,8}},137}, {{{0,8}},73}, {{{0,9}},243},
++ {{{80,7}},4}, {{{0,8}},85}, {{{0,8}},21}, {{{80,8}},258},
++ {{{83,7}},43}, {{{0,8}},117}, {{{0,8}},53}, {{{0,9}},203},
++ {{{81,7}},13}, {{{0,8}},101}, {{{0,8}},37}, {{{0,9}},171},
++ {{{0,8}},5}, {{{0,8}},133}, {{{0,8}},69}, {{{0,9}},235},
++ {{{80,7}},8}, {{{0,8}},93}, {{{0,8}},29}, {{{0,9}},155},
++ {{{84,7}},83}, {{{0,8}},125}, {{{0,8}},61}, {{{0,9}},219},
++ {{{82,7}},23}, {{{0,8}},109}, {{{0,8}},45}, {{{0,9}},187},
++ {{{0,8}},13}, {{{0,8}},141}, {{{0,8}},77}, {{{0,9}},251},
++ {{{80,7}},3}, {{{0,8}},83}, {{{0,8}},19}, {{{85,8}},195},
++ {{{83,7}},35}, {{{0,8}},115}, {{{0,8}},51}, {{{0,9}},199},
++ {{{81,7}},11}, {{{0,8}},99}, {{{0,8}},35}, {{{0,9}},167},
++ {{{0,8}},3}, {{{0,8}},131}, {{{0,8}},67}, {{{0,9}},231},
++ {{{80,7}},7}, {{{0,8}},91}, {{{0,8}},27}, {{{0,9}},151},
++ {{{84,7}},67}, {{{0,8}},123}, {{{0,8}},59}, {{{0,9}},215},
++ {{{82,7}},19}, {{{0,8}},107}, {{{0,8}},43}, {{{0,9}},183},
++ {{{0,8}},11}, {{{0,8}},139}, {{{0,8}},75}, {{{0,9}},247},
++ {{{80,7}},5}, {{{0,8}},87}, {{{0,8}},23}, {{{192,8}},0},
++ {{{83,7}},51}, {{{0,8}},119}, {{{0,8}},55}, {{{0,9}},207},
++ {{{81,7}},15}, {{{0,8}},103}, {{{0,8}},39}, {{{0,9}},175},
++ {{{0,8}},7}, {{{0,8}},135}, {{{0,8}},71}, {{{0,9}},239},
++ {{{80,7}},9}, {{{0,8}},95}, {{{0,8}},31}, {{{0,9}},159},
++ {{{84,7}},99}, {{{0,8}},127}, {{{0,8}},63}, {{{0,9}},223},
++ {{{82,7}},27}, {{{0,8}},111}, {{{0,8}},47}, {{{0,9}},191},
++ {{{0,8}},15}, {{{0,8}},143}, {{{0,8}},79}, {{{0,9}},255}
++ };
++local inflate_huft fixed_td[] = {
++ {{{80,5}},1}, {{{87,5}},257}, {{{83,5}},17}, {{{91,5}},4097},
++ {{{81,5}},5}, {{{89,5}},1025}, {{{85,5}},65}, {{{93,5}},16385},
++ {{{80,5}},3}, {{{88,5}},513}, {{{84,5}},33}, {{{92,5}},8193},
++ {{{82,5}},9}, {{{90,5}},2049}, {{{86,5}},129}, {{{192,5}},24577},
++ {{{80,5}},2}, {{{87,5}},385}, {{{83,5}},25}, {{{91,5}},6145},
++ {{{81,5}},7}, {{{89,5}},1537}, {{{85,5}},97}, {{{93,5}},24577},
++ {{{80,5}},4}, {{{88,5}},769}, {{{84,5}},49}, {{{92,5}},12289},
++ {{{82,5}},13}, {{{90,5}},3073}, {{{86,5}},193}, {{{192,5}},24577}
++ };
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/zlib/inflate.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,368 @@
++/* inflate.c -- zlib interface to inflate modules
++ * Copyright (C) 1995-2002 Mark Adler
++ * For conditions of distribution and use, see copyright notice in zlib.h
++ */
++
++#include <zlib/zutil.h>
++#include "infblock.h"
++
++struct inflate_blocks_state {int dummy;}; /* for buggy compilers */
++
++typedef enum {
++ METHOD, /* waiting for method byte */
++ FLAG, /* waiting for flag byte */
++ DICT4, /* four dictionary check bytes to go */
++ DICT3, /* three dictionary check bytes to go */
++ DICT2, /* two dictionary check bytes to go */
++ DICT1, /* one dictionary check byte to go */
++ DICT0, /* waiting for inflateSetDictionary */
++ BLOCKS, /* decompressing blocks */
++ CHECK4, /* four check bytes to go */
++ CHECK3, /* three check bytes to go */
++ CHECK2, /* two check bytes to go */
++ CHECK1, /* one check byte to go */
++ DONE, /* finished check, done */
++ BAD} /* got an error--stay here */
++inflate_mode;
++
++/* inflate private state */
++struct internal_state {
++
++ /* mode */
++ inflate_mode mode; /* current inflate mode */
++
++ /* mode dependent information */
++ union {
++ uInt method; /* if FLAGS, method byte */
++ struct {
++ uLong was; /* computed check value */
++ uLong need; /* stream check value */
++ } check; /* if CHECK, check values to compare */
++ uInt marker; /* if BAD, inflateSync's marker bytes count */
++ } sub; /* submode */
++
++ /* mode independent information */
++ int nowrap; /* flag for no wrapper */
++ uInt wbits; /* log2(window size) (8..15, defaults to 15) */
++ inflate_blocks_statef
++ *blocks; /* current inflate_blocks state */
++
++};
++
++
++int ZEXPORT inflateReset(z)
++z_streamp z;
++{
++ if (z == Z_NULL || z->state == Z_NULL)
++ return Z_STREAM_ERROR;
++ z->total_in = z->total_out = 0;
++ z->msg = Z_NULL;
++ z->state->mode = z->state->nowrap ? BLOCKS : METHOD;
++ inflate_blocks_reset(z->state->blocks, z, Z_NULL);
++ Tracev((stderr, "inflate: reset\n"));
++ return Z_OK;
++}
++
++
++int ZEXPORT inflateEnd(z)
++z_streamp z;
++{
++ if (z == Z_NULL || z->state == Z_NULL || z->zfree == Z_NULL)
++ return Z_STREAM_ERROR;
++ if (z->state->blocks != Z_NULL)
++ inflate_blocks_free(z->state->blocks, z);
++ ZFREE(z, z->state);
++ z->state = Z_NULL;
++ Tracev((stderr, "inflate: end\n"));
++ return Z_OK;
++}
++
++
++int ZEXPORT inflateInit2_(z, w, version, stream_size)
++z_streamp z;
++int w;
++const char *version;
++int stream_size;
++{
++ if (version == Z_NULL || version[0] != ZLIB_VERSION[0] ||
++ stream_size != sizeof(z_stream))
++ return Z_VERSION_ERROR;
++
++ /* initialize state */
++ if (z == Z_NULL)
++ return Z_STREAM_ERROR;
++ z->msg = Z_NULL;
++ if (z->zalloc == Z_NULL)
++ {
++ return Z_STREAM_ERROR;
++/* z->zalloc = zcalloc;
++ z->opaque = (voidpf)0;
++*/
++ }
++ if (z->zfree == Z_NULL) return Z_STREAM_ERROR; /* z->zfree = zcfree; */
++ if ((z->state = (struct internal_state FAR *)
++ ZALLOC(z,1,sizeof(struct internal_state))) == Z_NULL)
++ return Z_MEM_ERROR;
++ z->state->blocks = Z_NULL;
++
++ /* handle undocumented nowrap option (no zlib header or check) */
++ z->state->nowrap = 0;
++ if (w < 0)
++ {
++ w = - w;
++ z->state->nowrap = 1;
++ }
++
++ /* set window size */
++ if (w < 8 || w > 15)
++ {
++ inflateEnd(z);
++ return Z_STREAM_ERROR;
++ }
++ z->state->wbits = (uInt)w;
++
++ /* create inflate_blocks state */
++ if ((z->state->blocks =
++ inflate_blocks_new(z, z->state->nowrap ? Z_NULL : adler32, (uInt)1 << w))
++ == Z_NULL)
++ {
++ inflateEnd(z);
++ return Z_MEM_ERROR;
++ }
++ Tracev((stderr, "inflate: allocated\n"));
++
++ /* reset state */
++ inflateReset(z);
++ return Z_OK;
++}
++
++
++int ZEXPORT inflateInit_(z, version, stream_size)
++z_streamp z;
++const char *version;
++int stream_size;
++{
++ return inflateInit2_(z, DEF_WBITS, version, stream_size);
++}
++
++
++#define NEEDBYTE {if(z->avail_in==0)return r;r=f;}
++#define NEXTBYTE (z->avail_in--,z->total_in++,*z->next_in++)
++
++int ZEXPORT inflate(z, f)
++z_streamp z;
++int f;
++{
++ int r;
++ uInt b;
++
++ if (z == Z_NULL || z->state == Z_NULL || z->next_in == Z_NULL)
++ return Z_STREAM_ERROR;
++ f = f == Z_FINISH ? Z_BUF_ERROR : Z_OK;
++ r = Z_BUF_ERROR;
++ while (1) switch (z->state->mode)
++ {
++ case METHOD:
++ NEEDBYTE
++ if (((z->state->sub.method = NEXTBYTE) & 0xf) != Z_DEFLATED)
++ {
++ z->state->mode = BAD;
++ z->msg = (char*)"unknown compression method";
++ z->state->sub.marker = 5; /* can't try inflateSync */
++ break;
++ }
++ if ((z->state->sub.method >> 4) + 8 > z->state->wbits)
++ {
++ z->state->mode = BAD;
++ z->msg = (char*)"invalid window size";
++ z->state->sub.marker = 5; /* can't try inflateSync */
++ break;
++ }
++ z->state->mode = FLAG;
++ case FLAG:
++ NEEDBYTE
++ b = NEXTBYTE;
++ if (((z->state->sub.method << 8) + b) % 31)
++ {
++ z->state->mode = BAD;
++ z->msg = (char*)"incorrect header check";
++ z->state->sub.marker = 5; /* can't try inflateSync */
++ break;
++ }
++ Tracev((stderr, "inflate: zlib header ok\n"));
++ if (!(b & PRESET_DICT))
++ {
++ z->state->mode = BLOCKS;
++ break;
++ }
++ z->state->mode = DICT4;
++ case DICT4:
++ NEEDBYTE
++ z->state->sub.check.need = (uLong)NEXTBYTE << 24;
++ z->state->mode = DICT3;
++ case DICT3:
++ NEEDBYTE
++ z->state->sub.check.need += (uLong)NEXTBYTE << 16;
++ z->state->mode = DICT2;
++ case DICT2:
++ NEEDBYTE
++ z->state->sub.check.need += (uLong)NEXTBYTE << 8;
++ z->state->mode = DICT1;
++ case DICT1:
++ NEEDBYTE
++ z->state->sub.check.need += (uLong)NEXTBYTE;
++ z->adler = z->state->sub.check.need;
++ z->state->mode = DICT0;
++ return Z_NEED_DICT;
++ case DICT0:
++ z->state->mode = BAD;
++ z->msg = (char*)"need dictionary";
++ z->state->sub.marker = 0; /* can try inflateSync */
++ return Z_STREAM_ERROR;
++ case BLOCKS:
++ r = inflate_blocks(z->state->blocks, z, r);
++ if (r == Z_DATA_ERROR)
++ {
++ z->state->mode = BAD;
++ z->state->sub.marker = 0; /* can try inflateSync */
++ break;
++ }
++ if (r == Z_OK)
++ r = f;
++ if (r != Z_STREAM_END)
++ return r;
++ r = f;
++ inflate_blocks_reset(z->state->blocks, z, &z->state->sub.check.was);
++ if (z->state->nowrap)
++ {
++ z->state->mode = DONE;
++ break;
++ }
++ z->state->mode = CHECK4;
++ case CHECK4:
++ NEEDBYTE
++ z->state->sub.check.need = (uLong)NEXTBYTE << 24;
++ z->state->mode = CHECK3;
++ case CHECK3:
++ NEEDBYTE
++ z->state->sub.check.need += (uLong)NEXTBYTE << 16;
++ z->state->mode = CHECK2;
++ case CHECK2:
++ NEEDBYTE
++ z->state->sub.check.need += (uLong)NEXTBYTE << 8;
++ z->state->mode = CHECK1;
++ case CHECK1:
++ NEEDBYTE
++ z->state->sub.check.need += (uLong)NEXTBYTE;
++
++ if (z->state->sub.check.was != z->state->sub.check.need)
++ {
++ z->state->mode = BAD;
++ z->msg = (char*)"incorrect data check";
++ z->state->sub.marker = 5; /* can't try inflateSync */
++ break;
++ }
++ Tracev((stderr, "inflate: zlib check ok\n"));
++ z->state->mode = DONE;
++ case DONE:
++ return Z_STREAM_END;
++ case BAD:
++ return Z_DATA_ERROR;
++ default:
++ return Z_STREAM_ERROR;
++ }
++#ifdef NEED_DUMMY_RETURN
++ return Z_STREAM_ERROR; /* Some dumb compilers complain without this */
++#endif
++}
++
++
++int ZEXPORT inflateSetDictionary(z, dictionary, dictLength)
++z_streamp z;
++const Bytef *dictionary;
++uInt dictLength;
++{
++ uInt length = dictLength;
++
++ if (z == Z_NULL || z->state == Z_NULL || z->state->mode != DICT0)
++ return Z_STREAM_ERROR;
++
++ if (adler32(1L, dictionary, dictLength) != z->adler) return Z_DATA_ERROR;
++ z->adler = 1L;
++
++ if (length >= ((uInt)1<<z->state->wbits))
++ {
++ length = (1<<z->state->wbits)-1;
++ dictionary += dictLength - length;
++ }
++ inflate_set_dictionary(z->state->blocks, dictionary, length);
++ z->state->mode = BLOCKS;
++ return Z_OK;
++}
++
++
++int ZEXPORT inflateSync(z)
++z_streamp z;
++{
++ uInt n; /* number of bytes to look at */
++ Bytef *p; /* pointer to bytes */
++ uInt m; /* number of marker bytes found in a row */
++ uLong r, w; /* temporaries to save total_in and total_out */
++
++ /* set up */
++ if (z == Z_NULL || z->state == Z_NULL)
++ return Z_STREAM_ERROR;
++ if (z->state->mode != BAD)
++ {
++ z->state->mode = BAD;
++ z->state->sub.marker = 0;
++ }
++ if ((n = z->avail_in) == 0)
++ return Z_BUF_ERROR;
++ p = z->next_in;
++ m = z->state->sub.marker;
++
++ /* search */
++ while (n && m < 4)
++ {
++ static const Byte mark[4] = {0, 0, 0xff, 0xff};
++ if (*p == mark[m])
++ m++;
++ else if (*p)
++ m = 0;
++ else
++ m = 4 - m;
++ p++, n--;
++ }
++
++ /* restore */
++ z->total_in += p - z->next_in;
++ z->next_in = p;
++ z->avail_in = n;
++ z->state->sub.marker = m;
++
++ /* return no joy or set up to restart on a new block */
++ if (m != 4)
++ return Z_DATA_ERROR;
++ r = z->total_in; w = z->total_out;
++ inflateReset(z);
++ z->total_in = r; z->total_out = w;
++ z->state->mode = BLOCKS;
++ return Z_OK;
++}
++
++
++/* Returns true if inflate is currently at the end of a block generated
++ * by Z_SYNC_FLUSH or Z_FULL_FLUSH. This function is used by one PPP
++ * implementation to provide an additional safety check. PPP uses Z_SYNC_FLUSH
++ * but removes the length bytes of the resulting empty stored block. When
++ * decompressing, PPP checks that at the end of input packet, inflate is
++ * waiting for these length bytes.
++ */
++int ZEXPORT inflateSyncPoint(z)
++z_streamp z;
++{
++ if (z == Z_NULL || z->state == Z_NULL || z->state->blocks == Z_NULL)
++ return Z_STREAM_ERROR;
++ return inflate_blocks_sync_point(z->state->blocks);
++}
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/zlib/inftrees.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,454 @@
++/* inftrees.c -- generate Huffman trees for efficient decoding
++ * Copyright (C) 1995-2002 Mark Adler
++ * For conditions of distribution and use, see copyright notice in zlib.h
++ */
++
++#include <zlib/zutil.h>
++#include "inftrees.h"
++
++#if !defined(BUILDFIXED) && !defined(STDC)
++# define BUILDFIXED /* non ANSI compilers may not accept inffixed.h */
++#endif
++
++local const char inflate_copyright[] =
++ " inflate 1.1.4 Copyright 1995-2002 Mark Adler ";
++/*
++ If you use the zlib library in a product, an acknowledgment is welcome
++ in the documentation of your product. If for some reason you cannot
++ include such an acknowledgment, I would appreciate that you keep this
++ copyright string in the executable of your product.
++ */
++struct internal_state {int dummy;}; /* for buggy compilers */
++
++/* simplify the use of the inflate_huft type with some defines */
++#define exop word.what.Exop
++#define bits word.what.Bits
++
++
++local int huft_build OF((
++ uIntf *, /* code lengths in bits */
++ uInt, /* number of codes */
++ uInt, /* number of "simple" codes */
++ const uIntf *, /* list of base values for non-simple codes */
++ const uIntf *, /* list of extra bits for non-simple codes */
++ inflate_huft * FAR*,/* result: starting table */
++ uIntf *, /* maximum lookup bits (returns actual) */
++ inflate_huft *, /* space for trees */
++ uInt *, /* hufts used in space */
++ uIntf * )); /* space for values */
++
++/* Tables for deflate from PKZIP's appnote.txt. */
++local const uInt cplens[31] = { /* Copy lengths for literal codes 257..285 */
++ 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
++ 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0};
++ /* see note #13 above about 258 */
++local const uInt cplext[31] = { /* Extra bits for literal codes 257..285 */
++ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2,
++ 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 112, 112}; /* 112==invalid */
++local const uInt cpdist[30] = { /* Copy offsets for distance codes 0..29 */
++ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
++ 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
++ 8193, 12289, 16385, 24577};
++local const uInt cpdext[30] = { /* Extra bits for distance codes */
++ 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6,
++ 7, 7, 8, 8, 9, 9, 10, 10, 11, 11,
++ 12, 12, 13, 13};
++
++/*
++ Huffman code decoding is performed using a multi-level table lookup.
++ The fastest way to decode is to simply build a lookup table whose
++ size is determined by the longest code. However, the time it takes
++ to build this table can also be a factor if the data being decoded
++ is not very long. The most common codes are necessarily the
++ shortest codes, so those codes dominate the decoding time, and hence
++ the speed. The idea is you can have a shorter table that decodes the
++ shorter, more probable codes, and then point to subsidiary tables for
++ the longer codes. The time it costs to decode the longer codes is
++ then traded against the time it takes to make longer tables.
++
++ This results of this trade are in the variables lbits and dbits
++ below. lbits is the number of bits the first level table for literal/
++ length codes can decode in one step, and dbits is the same thing for
++ the distance codes. Subsequent tables are also less than or equal to
++ those sizes. These values may be adjusted either when all of the
++ codes are shorter than that, in which case the longest code length in
++ bits is used, or when the shortest code is *longer* than the requested
++ table size, in which case the length of the shortest code in bits is
++ used.
++
++ There are two different values for the two tables, since they code a
++ different number of possibilities each. The literal/length table
++ codes 286 possible values, or in a flat code, a little over eight
++ bits. The distance table codes 30 possible values, or a little less
++ than five bits, flat. The optimum values for speed end up being
++ about one bit more than those, so lbits is 8+1 and dbits is 5+1.
++ The optimum values may differ though from machine to machine, and
++ possibly even between compilers. Your mileage may vary.
++ */
++
++
++/* If BMAX needs to be larger than 16, then h and x[] should be uLong. */
++#define BMAX 15 /* maximum bit length of any code */
++
++local int huft_build(b, n, s, d, e, t, m, hp, hn, v)
++uIntf *b; /* code lengths in bits (all assumed <= BMAX) */
++uInt n; /* number of codes (assumed <= 288) */
++uInt s; /* number of simple-valued codes (0..s-1) */
++const uIntf *d; /* list of base values for non-simple codes */
++const uIntf *e; /* list of extra bits for non-simple codes */
++inflate_huft * FAR *t; /* result: starting table */
++uIntf *m; /* maximum lookup bits, returns actual */
++inflate_huft *hp; /* space for trees */
++uInt *hn; /* hufts used in space */
++uIntf *v; /* working area: values in order of bit length */
++/* Given a list of code lengths and a maximum table size, make a set of
++ tables to decode that set of codes. Return Z_OK on success, Z_BUF_ERROR
++ if the given code set is incomplete (the tables are still built in this
++ case), or Z_DATA_ERROR if the input is invalid. */
++{
++
++ uInt a; /* counter for codes of length k */
++ uInt c[BMAX+1]; /* bit length count table */
++ uInt f; /* i repeats in table every f entries */
++ int g; /* maximum code length */
++ int h; /* table level */
++ register uInt i; /* counter, current code */
++ register uInt j; /* counter */
++ register int k; /* number of bits in current code */
++ int l; /* bits per table (returned in m) */
++ uInt mask; /* (1 << w) - 1, to avoid cc -O bug on HP */
++ register uIntf *p; /* pointer into c[], b[], or v[] */
++ inflate_huft *q; /* points to current table */
++ struct inflate_huft_s r; /* table entry for structure assignment */
++ inflate_huft *u[BMAX]; /* table stack */
++ register int w; /* bits before this table == (l * h) */
++ uInt x[BMAX+1]; /* bit offsets, then code stack */
++ uIntf *xp; /* pointer into x */
++ int y; /* number of dummy codes added */
++ uInt z; /* number of entries in current table */
++
++
++ /* Generate counts for each bit length */
++ p = c;
++#define C0 *p++ = 0;
++#define C2 C0 C0 C0 C0
++#define C4 C2 C2 C2 C2
++ C4 /* clear c[]--assume BMAX+1 is 16 */
++ p = b; i = n;
++ do {
++ c[*p++]++; /* assume all entries <= BMAX */
++ } while (--i);
++ if (c[0] == n) /* null input--all zero length codes */
++ {
++ *t = (inflate_huft *)Z_NULL;
++ *m = 0;
++ return Z_OK;
++ }
++
++
++ /* Find minimum and maximum length, bound *m by those */
++ l = *m;
++ for (j = 1; j <= BMAX; j++)
++ if (c[j])
++ break;
++ k = j; /* minimum code length */
++ if ((uInt)l < j)
++ l = j;
++ for (i = BMAX; i; i--)
++ if (c[i])
++ break;
++ g = i; /* maximum code length */
++ if ((uInt)l > i)
++ l = i;
++ *m = l;
++
++
++ /* Adjust last length count to fill out codes, if needed */
++ for (y = 1 << j; j < i; j++, y <<= 1)
++ if ((y -= c[j]) < 0)
++ return Z_DATA_ERROR;
++ if ((y -= c[i]) < 0)
++ return Z_DATA_ERROR;
++ c[i] += y;
++
++
++ /* Generate starting offsets into the value table for each length */
++ x[1] = j = 0;
++ p = c + 1; xp = x + 2;
++ while (--i) { /* note that i == g from above */
++ *xp++ = (j += *p++);
++ }
++
++
++ /* Make a table of values in order of bit lengths */
++ p = b; i = 0;
++ do {
++ if ((j = *p++) != 0)
++ v[x[j]++] = i;
++ } while (++i < n);
++ n = x[g]; /* set n to length of v */
++
++
++ /* Generate the Huffman codes and for each, make the table entries */
++ x[0] = i = 0; /* first Huffman code is zero */
++ p = v; /* grab values in bit order */
++ h = -1; /* no tables yet--level -1 */
++ w = -l; /* bits decoded == (l * h) */
++ u[0] = (inflate_huft *)Z_NULL; /* just to keep compilers happy */
++ q = (inflate_huft *)Z_NULL; /* ditto */
++ z = 0; /* ditto */
++
++ /* go through the bit lengths (k already is bits in shortest code) */
++ for (; k <= g; k++)
++ {
++ a = c[k];
++ while (a--)
++ {
++ /* here i is the Huffman code of length k bits for value *p */
++ /* make tables up to required level */
++ while (k > w + l)
++ {
++ h++;
++ w += l; /* previous table always l bits */
++
++ /* compute minimum size table less than or equal to l bits */
++ z = g - w;
++ z = z > (uInt)l ? l : z; /* table size upper limit */
++ if ((f = 1 << (j = k - w)) > a + 1) /* try a k-w bit table */
++ { /* too few codes for k-w bit table */
++ f -= a + 1; /* deduct codes from patterns left */
++ xp = c + k;
++ if (j < z)
++ while (++j < z) /* try smaller tables up to z bits */
++ {
++ if ((f <<= 1) <= *++xp)
++ break; /* enough codes to use up j bits */
++ f -= *xp; /* else deduct codes from patterns */
++ }
++ }
++ z = 1 << j; /* table entries for j-bit table */
++
++ /* allocate new table */
++ if (*hn + z > MANY) /* (note: doesn't matter for fixed) */
++ return Z_DATA_ERROR; /* overflow of MANY */
++ u[h] = q = hp + *hn;
++ *hn += z;
++
++ /* connect to last table, if there is one */
++ if (h)
++ {
++ x[h] = i; /* save pattern for backing up */
++ r.bits = (Byte)l; /* bits to dump before this table */
++ r.exop = (Byte)j; /* bits in this table */
++ j = i >> (w - l);
++ r.base = (uInt)(q - u[h-1] - j); /* offset to this table */
++ u[h-1][j] = r; /* connect to last table */
++ }
++ else
++ *t = q; /* first table is returned result */
++ }
++
++ /* set up table entry in r */
++ r.bits = (Byte)(k - w);
++ if (p >= v + n)
++ r.exop = 128 + 64; /* out of values--invalid code */
++ else if (*p < s)
++ {
++ r.exop = (Byte)(*p < 256 ? 0 : 32 + 64); /* 256 is end-of-block */
++ r.base = *p++; /* simple code is just the value */
++ }
++ else
++ {
++ r.exop = (Byte)(e[*p - s] + 16 + 64);/* non-simple--look up in lists */
++ r.base = d[*p++ - s];
++ }
++
++ /* fill code-like entries with r */
++ f = 1 << (k - w);
++ for (j = i >> w; j < z; j += f)
++ q[j] = r;
++
++ /* backwards increment the k-bit code i */
++ for (j = 1 << (k - 1); i & j; j >>= 1)
++ i ^= j;
++ i ^= j;
++
++ /* backup over finished tables */
++ mask = (1 << w) - 1; /* needed on HP, cc -O bug */
++ while ((i & mask) != x[h])
++ {
++ h--; /* don't need to update q */
++ w -= l;
++ mask = (1 << w) - 1;
++ }
++ }
++ }
++
++
++ /* Return Z_BUF_ERROR if we were given an incomplete table */
++ return y != 0 && g != 1 ? Z_BUF_ERROR : Z_OK;
++}
++
++
++int inflate_trees_bits(c, bb, tb, hp, z)
++uIntf *c; /* 19 code lengths */
++uIntf *bb; /* bits tree desired/actual depth */
++inflate_huft * FAR *tb; /* bits tree result */
++inflate_huft *hp; /* space for trees */
++z_streamp z; /* for messages */
++{
++ int r;
++ uInt hn = 0; /* hufts used in space */
++ uIntf *v; /* work area for huft_build */
++
++ if ((v = (uIntf*)ZALLOC(z, 19, sizeof(uInt))) == Z_NULL)
++ return Z_MEM_ERROR;
++ r = huft_build(c, 19, 19, (uIntf*)Z_NULL, (uIntf*)Z_NULL,
++ tb, bb, hp, &hn, v);
++ if (r == Z_DATA_ERROR)
++ z->msg = (char*)"oversubscribed dynamic bit lengths tree";
++ else if (r == Z_BUF_ERROR || *bb == 0)
++ {
++ z->msg = (char*)"incomplete dynamic bit lengths tree";
++ r = Z_DATA_ERROR;
++ }
++ ZFREE(z, v);
++ return r;
++}
++
++
++int inflate_trees_dynamic(nl, nd, c, bl, bd, tl, td, hp, z)
++uInt nl; /* number of literal/length codes */
++uInt nd; /* number of distance codes */
++uIntf *c; /* that many (total) code lengths */
++uIntf *bl; /* literal desired/actual bit depth */
++uIntf *bd; /* distance desired/actual bit depth */
++inflate_huft * FAR *tl; /* literal/length tree result */
++inflate_huft * FAR *td; /* distance tree result */
++inflate_huft *hp; /* space for trees */
++z_streamp z; /* for messages */
++{
++ int r;
++ uInt hn = 0; /* hufts used in space */
++ uIntf *v; /* work area for huft_build */
++
++ /* allocate work area */
++ if ((v = (uIntf*)ZALLOC(z, 288, sizeof(uInt))) == Z_NULL)
++ return Z_MEM_ERROR;
++
++ /* build literal/length tree */
++ r = huft_build(c, nl, 257, cplens, cplext, tl, bl, hp, &hn, v);
++ if (r != Z_OK || *bl == 0)
++ {
++ if (r == Z_DATA_ERROR)
++ z->msg = (char*)"oversubscribed literal/length tree";
++ else if (r != Z_MEM_ERROR)
++ {
++ z->msg = (char*)"incomplete literal/length tree";
++ r = Z_DATA_ERROR;
++ }
++ ZFREE(z, v);
++ return r;
++ }
++
++ /* build distance tree */
++ r = huft_build(c + nl, nd, 0, cpdist, cpdext, td, bd, hp, &hn, v);
++ if (r != Z_OK || (*bd == 0 && nl > 257))
++ {
++ if (r == Z_DATA_ERROR)
++ z->msg = (char*)"oversubscribed distance tree";
++ else if (r == Z_BUF_ERROR) {
++#ifdef PKZIP_BUG_WORKAROUND
++ r = Z_OK;
++ }
++#else
++ z->msg = (char*)"incomplete distance tree";
++ r = Z_DATA_ERROR;
++ }
++ else if (r != Z_MEM_ERROR)
++ {
++ z->msg = (char*)"empty distance tree with lengths";
++ r = Z_DATA_ERROR;
++ }
++ ZFREE(z, v);
++ return r;
++#endif
++ }
++
++ /* done */
++ ZFREE(z, v);
++ return Z_OK;
++}
++
++
++/* build fixed tables only once--keep them here */
++#ifdef BUILDFIXED
++local int fixed_built = 0;
++#define FIXEDH 544 /* number of hufts used by fixed tables */
++local inflate_huft fixed_mem[FIXEDH];
++local uInt fixed_bl;
++local uInt fixed_bd;
++local inflate_huft *fixed_tl;
++local inflate_huft *fixed_td;
++#else
++#include "inffixed.h"
++#endif
++
++
++int inflate_trees_fixed(bl, bd, tl, td, z)
++uIntf *bl; /* literal desired/actual bit depth */
++uIntf *bd; /* distance desired/actual bit depth */
++inflate_huft * FAR *tl; /* literal/length tree result */
++inflate_huft * FAR *td; /* distance tree result */
++z_streamp z; /* for memory allocation */
++{
++#ifdef BUILDFIXED
++ /* build fixed tables if not already */
++ if (!fixed_built)
++ {
++ int k; /* temporary variable */
++ uInt f = 0; /* number of hufts used in fixed_mem */
++ uIntf *c; /* length list for huft_build */
++ uIntf *v; /* work area for huft_build */
++
++ /* allocate memory */
++ if ((c = (uIntf*)ZALLOC(z, 288, sizeof(uInt))) == Z_NULL)
++ return Z_MEM_ERROR;
++ if ((v = (uIntf*)ZALLOC(z, 288, sizeof(uInt))) == Z_NULL)
++ {
++ ZFREE(z, c);
++ return Z_MEM_ERROR;
++ }
++
++ /* literal table */
++ for (k = 0; k < 144; k++)
++ c[k] = 8;
++ for (; k < 256; k++)
++ c[k] = 9;
++ for (; k < 280; k++)
++ c[k] = 7;
++ for (; k < 288; k++)
++ c[k] = 8;
++ fixed_bl = 9;
++ huft_build(c, 288, 257, cplens, cplext, &fixed_tl, &fixed_bl,
++ fixed_mem, &f, v);
++
++ /* distance table */
++ for (k = 0; k < 30; k++)
++ c[k] = 5;
++ fixed_bd = 5;
++ huft_build(c, 30, 0, cpdist, cpdext, &fixed_td, &fixed_bd,
++ fixed_mem, &f, v);
++
++ /* done */
++ ZFREE(z, v);
++ ZFREE(z, c);
++ fixed_built = 1;
++ }
++#endif
++ *bl = fixed_bl;
++ *bd = fixed_bd;
++ *tl = fixed_tl;
++ *td = fixed_td;
++ return Z_OK;
++}
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/zlib/inftrees.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,63 @@
++/* inftrees.h -- header to use inftrees.c
++ * Copyright (C) 1995-2002 Mark Adler
++ * For conditions of distribution and use, see copyright notice in zlib.h
++ */
++
++/* WARNING: this file should *not* be used by applications. It is
++ part of the implementation of the compression library and is
++ subject to change. Applications should only use zlib.h.
++ */
++
++/* Huffman code lookup table entry--this entry is four bytes for machines
++ that have 16-bit pointers (e.g. PC's in the small or medium model). */
++
++#ifndef _INFTREES_H
++#define _INFTREES_H
++
++typedef struct inflate_huft_s FAR inflate_huft;
++
++struct inflate_huft_s {
++ union {
++ struct {
++ Byte Exop; /* number of extra bits or operation */
++ Byte Bits; /* number of bits in this code or subcode */
++ } what;
++ uInt pad; /* pad structure to a power of 2 (4 bytes for */
++ } word; /* 16-bit, 8 bytes for 32-bit int's) */
++ uInt base; /* literal, length base, distance base,
++ or table offset */
++};
++
++/* Maximum size of dynamic tree. The maximum found in a long but non-
++ exhaustive search was 1004 huft structures (850 for length/literals
++ and 154 for distances, the latter actually the result of an
++ exhaustive search). The actual maximum is not known, but the
++ value below is more than safe. */
++#define MANY 1440
++
++extern int inflate_trees_bits OF((
++ uIntf *, /* 19 code lengths */
++ uIntf *, /* bits tree desired/actual depth */
++ inflate_huft * FAR *, /* bits tree result */
++ inflate_huft *, /* space for trees */
++ z_streamp)); /* for messages */
++
++extern int inflate_trees_dynamic OF((
++ uInt, /* number of literal/length codes */
++ uInt, /* number of distance codes */
++ uIntf *, /* that many (total) code lengths */
++ uIntf *, /* literal desired/actual bit depth */
++ uIntf *, /* distance desired/actual bit depth */
++ inflate_huft * FAR *, /* literal/length tree result */
++ inflate_huft * FAR *, /* distance tree result */
++ inflate_huft *, /* space for trees */
++ z_streamp)); /* for messages */
++
++extern int inflate_trees_fixed OF((
++ uIntf *, /* literal desired/actual bit depth */
++ uIntf *, /* distance desired/actual bit depth */
++ inflate_huft * FAR *, /* literal/length tree result */
++ inflate_huft * FAR *, /* distance tree result */
++ z_streamp)); /* for memory allocation */
++
++#endif /* _INFTREES_H */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/zlib/infutil.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,87 @@
++/* inflate_util.c -- data and routines common to blocks and codes
++ * Copyright (C) 1995-2002 Mark Adler
++ * For conditions of distribution and use, see copyright notice in zlib.h
++ */
++
++#include <zlib/zutil.h>
++#include "infblock.h"
++#include "inftrees.h"
++#include "infcodes.h"
++#include "infutil.h"
++
++struct inflate_codes_state {int dummy;}; /* for buggy compilers */
++
++/* And'ing with mask[n] masks the lower n bits */
++uInt inflate_mask[17] = {
++ 0x0000,
++ 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff,
++ 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff
++};
++
++
++/* copy as much as possible from the sliding window to the output area */
++int inflate_flush(s, z, r)
++inflate_blocks_statef *s;
++z_streamp z;
++int r;
++{
++ uInt n;
++ Bytef *p;
++ Bytef *q;
++
++ /* local copies of source and destination pointers */
++ p = z->next_out;
++ q = s->read;
++
++ /* compute number of bytes to copy as far as end of window */
++ n = (uInt)((q <= s->write ? s->write : s->end) - q);
++ if (n > z->avail_out) n = z->avail_out;
++ if (n && r == Z_BUF_ERROR) r = Z_OK;
++
++ /* update counters */
++ z->avail_out -= n;
++ z->total_out += n;
++
++ /* update check information */
++ if (s->checkfn != Z_NULL)
++ z->adler = s->check = (*s->checkfn)(s->check, q, n);
++
++ /* copy as far as end of window */
++ zmemcpy(p, q, n);
++ p += n;
++ q += n;
++
++ /* see if more to copy at beginning of window */
++ if (q == s->end)
++ {
++ /* wrap pointers */
++ q = s->window;
++ if (s->write == s->end)
++ s->write = s->window;
++
++ /* compute bytes to copy */
++ n = (uInt)(s->write - q);
++ if (n > z->avail_out) n = z->avail_out;
++ if (n && r == Z_BUF_ERROR) r = Z_OK;
++
++ /* update counters */
++ z->avail_out -= n;
++ z->total_out += n;
++
++ /* update check information */
++ if (s->checkfn != Z_NULL)
++ z->adler = s->check = (*s->checkfn)(s->check, q, n);
++
++ /* copy */
++ zmemcpy(p, q, n);
++ p += n;
++ q += n;
++ }
++
++ /* update pointers */
++ z->next_out = p;
++ s->read = q;
++
++ /* done */
++ return r;
++}
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/zlib/infutil.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,98 @@
++/* infutil.h -- types and macros common to blocks and codes
++ * Copyright (C) 1995-2002 Mark Adler
++ * For conditions of distribution and use, see copyright notice in zlib.h
++ */
++
++/* WARNING: this file should *not* be used by applications. It is
++ part of the implementation of the compression library and is
++ subject to change. Applications should only use zlib.h.
++ */
++
++#ifndef _INFUTIL_H
++#define _INFUTIL_H
++
++typedef enum {
++ TYPE, /* get type bits (3, including end bit) */
++ LENS, /* get lengths for stored */
++ STORED, /* processing stored block */
++ TABLE, /* get table lengths */
++ BTREE, /* get bit lengths tree for a dynamic block */
++ DTREE, /* get length, distance trees for a dynamic block */
++ CODES, /* processing fixed or dynamic block */
++ DRY, /* output remaining window bytes */
++ DONE, /* finished last block, done */
++ BAD} /* got a data error--stuck here */
++inflate_block_mode;
++
++/* inflate blocks semi-private state */
++struct inflate_blocks_state {
++
++ /* mode */
++ inflate_block_mode mode; /* current inflate_block mode */
++
++ /* mode dependent information */
++ union {
++ uInt left; /* if STORED, bytes left to copy */
++ struct {
++ uInt table; /* table lengths (14 bits) */
++ uInt index; /* index into blens (or border) */
++ uIntf *blens; /* bit lengths of codes */
++ uInt bb; /* bit length tree depth */
++ inflate_huft *tb; /* bit length decoding tree */
++ } trees; /* if DTREE, decoding info for trees */
++ struct {
++ inflate_codes_statef
++ *codes;
++ } decode; /* if CODES, current state */
++ } sub; /* submode */
++ uInt last; /* true if this block is the last block */
++
++ /* mode independent information */
++ uInt bitk; /* bits in bit buffer */
++ uLong bitb; /* bit buffer */
++ inflate_huft *hufts; /* single malloc for tree space */
++ Bytef *window; /* sliding window */
++ Bytef *end; /* one byte after sliding window */
++ Bytef *read; /* window read pointer */
++ Bytef *write; /* window write pointer */
++ check_func checkfn; /* check function */
++ uLong check; /* check on output */
++
++};
++
++
++/* defines for inflate input/output */
++/* update pointers and return */
++#define UPDBITS {s->bitb=b;s->bitk=k;}
++#define UPDIN {z->avail_in=n;z->total_in+=p-z->next_in;z->next_in=p;}
++#define UPDOUT {s->write=q;}
++#define UPDATE {UPDBITS UPDIN UPDOUT}
++#define LEAVE {UPDATE return inflate_flush(s,z,r);}
++/* get bytes and bits */
++#define LOADIN {p=z->next_in;n=z->avail_in;b=s->bitb;k=s->bitk;}
++#define NEEDBYTE {if(n)r=Z_OK;else LEAVE}
++#define NEXTBYTE (n--,*p++)
++#define NEEDBITS(j) {while(k<(j)){NEEDBYTE;b|=((uLong)NEXTBYTE)<<k;k+=8;}}
++#define DUMPBITS(j) {b>>=(j);k-=(j);}
++/* output bytes */
++#define WAVAIL (uInt)(q<s->read?s->read-q-1:s->end-q)
++#define LOADOUT {q=s->write;m=(uInt)WAVAIL;}
++#define WRAP {if(q==s->end&&s->read!=s->window){q=s->window;m=(uInt)WAVAIL;}}
++#define FLUSH {UPDOUT r=inflate_flush(s,z,r); LOADOUT}
++#define NEEDOUT {if(m==0){WRAP if(m==0){FLUSH WRAP if(m==0) LEAVE}}r=Z_OK;}
++#define OUTBYTE(a) {*q++=(Byte)(a);m--;}
++/* load local pointers */
++#define LOAD {LOADIN LOADOUT}
++
++/* masks for lower bits (size given to avoid silly warnings with Visual C++) */
++extern uInt inflate_mask[17];
++
++/* copy as much as possible from the sliding window to the output area */
++extern int inflate_flush OF((
++ inflate_blocks_statef *,
++ z_streamp ,
++ int));
++
++struct internal_state {int dummy;}; /* for buggy compilers */
++
++#endif /* _INFUTIL_H */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/zlib/match586.S Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,357 @@
++/* match.s -- Pentium-optimized version of longest_match()
++ * Written for zlib 1.1.2
++ * Copyright (C) 1998 Brian Raiter <breadbox@muppetlabs.com>
++ *
++ * This is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License.
++ */
++
++#ifndef NO_UNDERLINE
++#define match_init _ipcomp_match_init
++#define longest_match _ipcomp_longest_match
++#else
++#define match_init ipcomp_match_init
++#define longest_match ipcomp_longest_match
++#endif
++
++#define MAX_MATCH (258)
++#define MIN_MATCH (3)
++#define MIN_LOOKAHEAD (MAX_MATCH + MIN_MATCH + 1)
++#define MAX_MATCH_8 ((MAX_MATCH + 7) & ~7)
++
++/* stack frame offsets */
++
++#define wmask 0 /* local copy of s->wmask */
++#define window 4 /* local copy of s->window */
++#define windowbestlen 8 /* s->window + bestlen */
++#define chainlenscanend 12 /* high word: current chain len */
++ /* low word: last bytes sought */
++#define scanstart 16 /* first two bytes of string */
++#define scanalign 20 /* dword-misalignment of string */
++#define nicematch 24 /* a good enough match size */
++#define bestlen 28 /* size of best match so far */
++#define scan 32 /* ptr to string wanting match */
++
++#define LocalVarsSize (36)
++/* saved ebx 36 */
++/* saved edi 40 */
++/* saved esi 44 */
++/* saved ebp 48 */
++/* return address 52 */
++#define deflatestate 56 /* the function arguments */
++#define curmatch 60
++
++/* Offsets for fields in the deflate_state structure. These numbers
++ * are calculated from the definition of deflate_state, with the
++ * assumption that the compiler will dword-align the fields. (Thus,
++ * changing the definition of deflate_state could easily cause this
++ * program to crash horribly, without so much as a warning at
++ * compile time. Sigh.)
++ */
++#define dsWSize 36
++#define dsWMask 44
++#define dsWindow 48
++#define dsPrev 56
++#define dsMatchLen 88
++#define dsPrevMatch 92
++#define dsStrStart 100
++#define dsMatchStart 104
++#define dsLookahead 108
++#define dsPrevLen 112
++#define dsMaxChainLen 116
++#define dsGoodMatch 132
++#define dsNiceMatch 136
++
++
++.file "match.S"
++
++.globl match_init, longest_match
++
++.text
++
++/* uInt longest_match(deflate_state *deflatestate, IPos curmatch) */
++
++longest_match:
++
++/* Save registers that the compiler may be using, and adjust %esp to */
++/* make room for our stack frame. */
++
++ pushl %ebp
++ pushl %edi
++ pushl %esi
++ pushl %ebx
++ subl $LocalVarsSize, %esp
++
++/* Retrieve the function arguments. %ecx will hold cur_match */
++/* throughout the entire function. %edx will hold the pointer to the */
++/* deflate_state structure during the function's setup (before */
++/* entering the main loop). */
++
++ movl deflatestate(%esp), %edx
++ movl curmatch(%esp), %ecx
++
++/* if ((uInt)nice_match > s->lookahead) nice_match = s->lookahead; */
++
++ movl dsNiceMatch(%edx), %eax
++ movl dsLookahead(%edx), %ebx
++ cmpl %eax, %ebx
++ jl LookaheadLess
++ movl %eax, %ebx
++LookaheadLess: movl %ebx, nicematch(%esp)
++
++/* register Bytef *scan = s->window + s->strstart; */
++
++ movl dsWindow(%edx), %esi
++ movl %esi, window(%esp)
++ movl dsStrStart(%edx), %ebp
++ lea (%esi,%ebp), %edi
++ movl %edi, scan(%esp)
++
++/* Determine how many bytes the scan ptr is off from being */
++/* dword-aligned. */
++
++ movl %edi, %eax
++ negl %eax
++ andl $3, %eax
++ movl %eax, scanalign(%esp)
++
++/* IPos limit = s->strstart > (IPos)MAX_DIST(s) ? */
++/* s->strstart - (IPos)MAX_DIST(s) : NIL; */
++
++ movl dsWSize(%edx), %eax
++ subl $MIN_LOOKAHEAD, %eax
++ subl %eax, %ebp
++ jg LimitPositive
++ xorl %ebp, %ebp
++LimitPositive:
++
++/* unsigned chain_length = s->max_chain_length; */
++/* if (s->prev_length >= s->good_match) { */
++/* chain_length >>= 2; */
++/* } */
++
++ movl dsPrevLen(%edx), %eax
++ movl dsGoodMatch(%edx), %ebx
++ cmpl %ebx, %eax
++ movl dsMaxChainLen(%edx), %ebx
++ jl LastMatchGood
++ shrl $2, %ebx
++LastMatchGood:
++
++/* chainlen is decremented once beforehand so that the function can */
++/* use the sign flag instead of the zero flag for the exit test. */
++/* It is then shifted into the high word, to make room for the scanend */
++/* scanend value, which it will always accompany. */
++
++ decl %ebx
++ shll $16, %ebx
++
++/* int best_len = s->prev_length; */
++
++ movl dsPrevLen(%edx), %eax
++ movl %eax, bestlen(%esp)
++
++/* Store the sum of s->window + best_len in %esi locally, and in %esi. */
++
++ addl %eax, %esi
++ movl %esi, windowbestlen(%esp)
++
++/* register ush scan_start = *(ushf*)scan; */
++/* register ush scan_end = *(ushf*)(scan+best_len-1); */
++
++ movw (%edi), %bx
++ movw %bx, scanstart(%esp)
++ movw -1(%edi,%eax), %bx
++ movl %ebx, chainlenscanend(%esp)
++
++/* Posf *prev = s->prev; */
++/* uInt wmask = s->w_mask; */
++
++ movl dsPrev(%edx), %edi
++ movl dsWMask(%edx), %edx
++ mov %edx, wmask(%esp)
++
++/* Jump into the main loop. */
++
++ jmp LoopEntry
++
++.balign 16
++
++/* do {
++ * match = s->window + cur_match;
++ * if (*(ushf*)(match+best_len-1) != scan_end ||
++ * *(ushf*)match != scan_start) continue;
++ * [...]
++ * } while ((cur_match = prev[cur_match & wmask]) > limit
++ * && --chain_length != 0);
++ *
++ * Here is the inner loop of the function. The function will spend the
++ * majority of its time in this loop, and majority of that time will
++ * be spent in the first ten instructions.
++ *
++ * Within this loop:
++ * %ebx = chainlenscanend - i.e., ((chainlen << 16) | scanend)
++ * %ecx = curmatch
++ * %edx = curmatch & wmask
++ * %esi = windowbestlen - i.e., (window + bestlen)
++ * %edi = prev
++ * %ebp = limit
++ *
++ * Two optimization notes on the choice of instructions:
++ *
++ * The first instruction uses a 16-bit address, which costs an extra,
++ * unpairable cycle. This is cheaper than doing a 32-bit access and
++ * zeroing the high word, due to the 3-cycle misalignment penalty which
++ * would occur half the time. This also turns out to be cheaper than
++ * doing two separate 8-bit accesses, as the memory is so rarely in the
++ * L1 cache.
++ *
++ * The window buffer, however, apparently spends a lot of time in the
++ * cache, and so it is faster to retrieve the word at the end of the
++ * match string with two 8-bit loads. The instructions that test the
++ * word at the beginning of the match string, however, are executed
++ * much less frequently, and there it was cheaper to use 16-bit
++ * instructions, which avoided the necessity of saving off and
++ * subsequently reloading one of the other registers.
++ */
++LookupLoop:
++ /* 1 U & V */
++ movw (%edi,%edx,2), %cx /* 2 U pipe */
++ movl wmask(%esp), %edx /* 2 V pipe */
++ cmpl %ebp, %ecx /* 3 U pipe */
++ jbe LeaveNow /* 3 V pipe */
++ subl $0x00010000, %ebx /* 4 U pipe */
++ js LeaveNow /* 4 V pipe */
++LoopEntry: movb -1(%esi,%ecx), %al /* 5 U pipe */
++ andl %ecx, %edx /* 5 V pipe */
++ cmpb %bl, %al /* 6 U pipe */
++ jnz LookupLoop /* 6 V pipe */
++ movb (%esi,%ecx), %ah
++ cmpb %bh, %ah
++ jnz LookupLoop
++ movl window(%esp), %eax
++ movw (%eax,%ecx), %ax
++ cmpw scanstart(%esp), %ax
++ jnz LookupLoop
++
++/* Store the current value of chainlen. */
++
++ movl %ebx, chainlenscanend(%esp)
++
++/* Point %edi to the string under scrutiny, and %esi to the string we */
++/* are hoping to match it up with. In actuality, %esi and %edi are */
++/* both pointed (MAX_MATCH_8 - scanalign) bytes ahead, and %edx is */
++/* initialized to -(MAX_MATCH_8 - scanalign). */
++
++ movl window(%esp), %esi
++ movl scan(%esp), %edi
++ addl %ecx, %esi
++ movl scanalign(%esp), %eax
++ movl $(-MAX_MATCH_8), %edx
++ lea MAX_MATCH_8(%edi,%eax), %edi
++ lea MAX_MATCH_8(%esi,%eax), %esi
++
++/* Test the strings for equality, 8 bytes at a time. At the end,
++ * adjust %edx so that it is offset to the exact byte that mismatched.
++ *
++ * We already know at this point that the first three bytes of the
++ * strings match each other, and they can be safely passed over before
++ * starting the compare loop. So what this code does is skip over 0-3
++ * bytes, as much as necessary in order to dword-align the %edi
++ * pointer. (%esi will still be misaligned three times out of four.)
++ *
++ * It should be confessed that this loop usually does not represent
++ * much of the total running time. Replacing it with a more
++ * straightforward "rep cmpsb" would not drastically degrade
++ * performance.
++ */
++LoopCmps:
++ movl (%esi,%edx), %eax
++ movl (%edi,%edx), %ebx
++ xorl %ebx, %eax
++ jnz LeaveLoopCmps
++ movl 4(%esi,%edx), %eax
++ movl 4(%edi,%edx), %ebx
++ xorl %ebx, %eax
++ jnz LeaveLoopCmps4
++ addl $8, %edx
++ jnz LoopCmps
++ jmp LenMaximum
++LeaveLoopCmps4: addl $4, %edx
++LeaveLoopCmps: testl $0x0000FFFF, %eax
++ jnz LenLower
++ addl $2, %edx
++ shrl $16, %eax
++LenLower: subb $1, %al
++ adcl $0, %edx
++
++/* Calculate the length of the match. If it is longer than MAX_MATCH, */
++/* then automatically accept it as the best possible match and leave. */
++
++ lea (%edi,%edx), %eax
++ movl scan(%esp), %edi
++ subl %edi, %eax
++ cmpl $MAX_MATCH, %eax
++ jge LenMaximum
++
++/* If the length of the match is not longer than the best match we */
++/* have so far, then forget it and return to the lookup loop. */
++
++ movl deflatestate(%esp), %edx
++ movl bestlen(%esp), %ebx
++ cmpl %ebx, %eax
++ jg LongerMatch
++ movl chainlenscanend(%esp), %ebx
++ movl windowbestlen(%esp), %esi
++ movl dsPrev(%edx), %edi
++ movl wmask(%esp), %edx
++ andl %ecx, %edx
++ jmp LookupLoop
++
++/* s->match_start = cur_match; */
++/* best_len = len; */
++/* if (len >= nice_match) break; */
++/* scan_end = *(ushf*)(scan+best_len-1); */
++
++LongerMatch: movl nicematch(%esp), %ebx
++ movl %eax, bestlen(%esp)
++ movl %ecx, dsMatchStart(%edx)
++ cmpl %ebx, %eax
++ jge LeaveNow
++ movl window(%esp), %esi
++ addl %eax, %esi
++ movl %esi, windowbestlen(%esp)
++ movl chainlenscanend(%esp), %ebx
++ movw -1(%edi,%eax), %bx
++ movl dsPrev(%edx), %edi
++ movl %ebx, chainlenscanend(%esp)
++ movl wmask(%esp), %edx
++ andl %ecx, %edx
++ jmp LookupLoop
++
++/* Accept the current string, with the maximum possible length. */
++
++LenMaximum: movl deflatestate(%esp), %edx
++ movl $MAX_MATCH, bestlen(%esp)
++ movl %ecx, dsMatchStart(%edx)
++
++/* if ((uInt)best_len <= s->lookahead) return (uInt)best_len; */
++/* return s->lookahead; */
++
++LeaveNow:
++ movl deflatestate(%esp), %edx
++ movl bestlen(%esp), %ebx
++ movl dsLookahead(%edx), %eax
++ cmpl %eax, %ebx
++ jg LookaheadRet
++ movl %ebx, %eax
++LookaheadRet:
++
++/* Restore the stack and return from whence we came. */
++
++ addl $LocalVarsSize, %esp
++ popl %ebx
++ popl %esi
++ popl %edi
++ popl %ebp
++match_init: ret
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/zlib/match686.S Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,330 @@
++/* match.s -- Pentium-Pro-optimized version of longest_match()
++ * Written for zlib 1.1.2
++ * Copyright (C) 1998 Brian Raiter <breadbox@muppetlabs.com>
++ *
++ * This is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License.
++ */
++
++#ifndef NO_UNDERLINE
++#define match_init _ipcomp_match_init
++#define longest_match _ipcomp_longest_match
++#else
++#define match_init ipcomp_match_init
++#define longest_match ipcomp_longest_match
++#endif
++
++#define MAX_MATCH (258)
++#define MIN_MATCH (3)
++#define MIN_LOOKAHEAD (MAX_MATCH + MIN_MATCH + 1)
++#define MAX_MATCH_8 ((MAX_MATCH + 7) & ~7)
++
++/* stack frame offsets */
++
++#define chainlenwmask 0 /* high word: current chain len */
++ /* low word: s->wmask */
++#define window 4 /* local copy of s->window */
++#define windowbestlen 8 /* s->window + bestlen */
++#define scanstart 16 /* first two bytes of string */
++#define scanend 12 /* last two bytes of string */
++#define scanalign 20 /* dword-misalignment of string */
++#define nicematch 24 /* a good enough match size */
++#define bestlen 28 /* size of best match so far */
++#define scan 32 /* ptr to string wanting match */
++
++#define LocalVarsSize (36)
++/* saved ebx 36 */
++/* saved edi 40 */
++/* saved esi 44 */
++/* saved ebp 48 */
++/* return address 52 */
++#define deflatestate 56 /* the function arguments */
++#define curmatch 60
++
++/* Offsets for fields in the deflate_state structure. These numbers
++ * are calculated from the definition of deflate_state, with the
++ * assumption that the compiler will dword-align the fields. (Thus,
++ * changing the definition of deflate_state could easily cause this
++ * program to crash horribly, without so much as a warning at
++ * compile time. Sigh.)
++ */
++#define dsWSize 36
++#define dsWMask 44
++#define dsWindow 48
++#define dsPrev 56
++#define dsMatchLen 88
++#define dsPrevMatch 92
++#define dsStrStart 100
++#define dsMatchStart 104
++#define dsLookahead 108
++#define dsPrevLen 112
++#define dsMaxChainLen 116
++#define dsGoodMatch 132
++#define dsNiceMatch 136
++
++
++.file "match.S"
++
++.globl match_init, longest_match
++
++.text
++
++/* uInt longest_match(deflate_state *deflatestate, IPos curmatch) */
++
++longest_match:
++
++/* Save registers that the compiler may be using, and adjust %esp to */
++/* make room for our stack frame. */
++
++ pushl %ebp
++ pushl %edi
++ pushl %esi
++ pushl %ebx
++ subl $LocalVarsSize, %esp
++
++/* Retrieve the function arguments. %ecx will hold cur_match */
++/* throughout the entire function. %edx will hold the pointer to the */
++/* deflate_state structure during the function's setup (before */
++/* entering the main loop). */
++
++ movl deflatestate(%esp), %edx
++ movl curmatch(%esp), %ecx
++
++/* uInt wmask = s->w_mask; */
++/* unsigned chain_length = s->max_chain_length; */
++/* if (s->prev_length >= s->good_match) { */
++/* chain_length >>= 2; */
++/* } */
++
++ movl dsPrevLen(%edx), %eax
++ movl dsGoodMatch(%edx), %ebx
++ cmpl %ebx, %eax
++ movl dsWMask(%edx), %eax
++ movl dsMaxChainLen(%edx), %ebx
++ jl LastMatchGood
++ shrl $2, %ebx
++LastMatchGood:
++
++/* chainlen is decremented once beforehand so that the function can */
++/* use the sign flag instead of the zero flag for the exit test. */
++/* It is then shifted into the high word, to make room for the wmask */
++/* value, which it will always accompany. */
++
++ decl %ebx
++ shll $16, %ebx
++ orl %eax, %ebx
++ movl %ebx, chainlenwmask(%esp)
++
++/* if ((uInt)nice_match > s->lookahead) nice_match = s->lookahead; */
++
++ movl dsNiceMatch(%edx), %eax
++ movl dsLookahead(%edx), %ebx
++ cmpl %eax, %ebx
++ jl LookaheadLess
++ movl %eax, %ebx
++LookaheadLess: movl %ebx, nicematch(%esp)
++
++/* register Bytef *scan = s->window + s->strstart; */
++
++ movl dsWindow(%edx), %esi
++ movl %esi, window(%esp)
++ movl dsStrStart(%edx), %ebp
++ lea (%esi,%ebp), %edi
++ movl %edi, scan(%esp)
++
++/* Determine how many bytes the scan ptr is off from being */
++/* dword-aligned. */
++
++ movl %edi, %eax
++ negl %eax
++ andl $3, %eax
++ movl %eax, scanalign(%esp)
++
++/* IPos limit = s->strstart > (IPos)MAX_DIST(s) ? */
++/* s->strstart - (IPos)MAX_DIST(s) : NIL; */
++
++ movl dsWSize(%edx), %eax
++ subl $MIN_LOOKAHEAD, %eax
++ subl %eax, %ebp
++ jg LimitPositive
++ xorl %ebp, %ebp
++LimitPositive:
++
++/* int best_len = s->prev_length; */
++
++ movl dsPrevLen(%edx), %eax
++ movl %eax, bestlen(%esp)
++
++/* Store the sum of s->window + best_len in %esi locally, and in %esi. */
++
++ addl %eax, %esi
++ movl %esi, windowbestlen(%esp)
++
++/* register ush scan_start = *(ushf*)scan; */
++/* register ush scan_end = *(ushf*)(scan+best_len-1); */
++/* Posf *prev = s->prev; */
++
++ movzwl (%edi), %ebx
++ movl %ebx, scanstart(%esp)
++ movzwl -1(%edi,%eax), %ebx
++ movl %ebx, scanend(%esp)
++ movl dsPrev(%edx), %edi
++
++/* Jump into the main loop. */
++
++ movl chainlenwmask(%esp), %edx
++ jmp LoopEntry
++
++.balign 16
++
++/* do {
++ * match = s->window + cur_match;
++ * if (*(ushf*)(match+best_len-1) != scan_end ||
++ * *(ushf*)match != scan_start) continue;
++ * [...]
++ * } while ((cur_match = prev[cur_match & wmask]) > limit
++ * && --chain_length != 0);
++ *
++ * Here is the inner loop of the function. The function will spend the
++ * majority of its time in this loop, and majority of that time will
++ * be spent in the first ten instructions.
++ *
++ * Within this loop:
++ * %ebx = scanend
++ * %ecx = curmatch
++ * %edx = chainlenwmask - i.e., ((chainlen << 16) | wmask)
++ * %esi = windowbestlen - i.e., (window + bestlen)
++ * %edi = prev
++ * %ebp = limit
++ */
++LookupLoop:
++ andl %edx, %ecx
++ movzwl (%edi,%ecx,2), %ecx
++ cmpl %ebp, %ecx
++ jbe LeaveNow
++ subl $0x00010000, %edx
++ js LeaveNow
++LoopEntry: movzwl -1(%esi,%ecx), %eax
++ cmpl %ebx, %eax
++ jnz LookupLoop
++ movl window(%esp), %eax
++ movzwl (%eax,%ecx), %eax
++ cmpl scanstart(%esp), %eax
++ jnz LookupLoop
++
++/* Store the current value of chainlen. */
++
++ movl %edx, chainlenwmask(%esp)
++
++/* Point %edi to the string under scrutiny, and %esi to the string we */
++/* are hoping to match it up with. In actuality, %esi and %edi are */
++/* both pointed (MAX_MATCH_8 - scanalign) bytes ahead, and %edx is */
++/* initialized to -(MAX_MATCH_8 - scanalign). */
++
++ movl window(%esp), %esi
++ movl scan(%esp), %edi
++ addl %ecx, %esi
++ movl scanalign(%esp), %eax
++ movl $(-MAX_MATCH_8), %edx
++ lea MAX_MATCH_8(%edi,%eax), %edi
++ lea MAX_MATCH_8(%esi,%eax), %esi
++
++/* Test the strings for equality, 8 bytes at a time. At the end,
++ * adjust %edx so that it is offset to the exact byte that mismatched.
++ *
++ * We already know at this point that the first three bytes of the
++ * strings match each other, and they can be safely passed over before
++ * starting the compare loop. So what this code does is skip over 0-3
++ * bytes, as much as necessary in order to dword-align the %edi
++ * pointer. (%esi will still be misaligned three times out of four.)
++ *
++ * It should be confessed that this loop usually does not represent
++ * much of the total running time. Replacing it with a more
++ * straightforward "rep cmpsb" would not drastically degrade
++ * performance.
++ */
++LoopCmps:
++ movl (%esi,%edx), %eax
++ xorl (%edi,%edx), %eax
++ jnz LeaveLoopCmps
++ movl 4(%esi,%edx), %eax
++ xorl 4(%edi,%edx), %eax
++ jnz LeaveLoopCmps4
++ addl $8, %edx
++ jnz LoopCmps
++ jmp LenMaximum
++LeaveLoopCmps4: addl $4, %edx
++LeaveLoopCmps: testl $0x0000FFFF, %eax
++ jnz LenLower
++ addl $2, %edx
++ shrl $16, %eax
++LenLower: subb $1, %al
++ adcl $0, %edx
++
++/* Calculate the length of the match. If it is longer than MAX_MATCH, */
++/* then automatically accept it as the best possible match and leave. */
++
++ lea (%edi,%edx), %eax
++ movl scan(%esp), %edi
++ subl %edi, %eax
++ cmpl $MAX_MATCH, %eax
++ jge LenMaximum
++
++/* If the length of the match is not longer than the best match we */
++/* have so far, then forget it and return to the lookup loop. */
++
++ movl deflatestate(%esp), %edx
++ movl bestlen(%esp), %ebx
++ cmpl %ebx, %eax
++ jg LongerMatch
++ movl windowbestlen(%esp), %esi
++ movl dsPrev(%edx), %edi
++ movl scanend(%esp), %ebx
++ movl chainlenwmask(%esp), %edx
++ jmp LookupLoop
++
++/* s->match_start = cur_match; */
++/* best_len = len; */
++/* if (len >= nice_match) break; */
++/* scan_end = *(ushf*)(scan+best_len-1); */
++
++LongerMatch: movl nicematch(%esp), %ebx
++ movl %eax, bestlen(%esp)
++ movl %ecx, dsMatchStart(%edx)
++ cmpl %ebx, %eax
++ jge LeaveNow
++ movl window(%esp), %esi
++ addl %eax, %esi
++ movl %esi, windowbestlen(%esp)
++ movzwl -1(%edi,%eax), %ebx
++ movl dsPrev(%edx), %edi
++ movl %ebx, scanend(%esp)
++ movl chainlenwmask(%esp), %edx
++ jmp LookupLoop
++
++/* Accept the current string, with the maximum possible length. */
++
++LenMaximum: movl deflatestate(%esp), %edx
++ movl $MAX_MATCH, bestlen(%esp)
++ movl %ecx, dsMatchStart(%edx)
++
++/* if ((uInt)best_len <= s->lookahead) return (uInt)best_len; */
++/* return s->lookahead; */
++
++LeaveNow:
++ movl deflatestate(%esp), %edx
++ movl bestlen(%esp), %ebx
++ movl dsLookahead(%edx), %eax
++ cmpl %eax, %ebx
++ jg LookaheadRet
++ movl %ebx, %eax
++LookaheadRet:
++
++/* Restore the stack and return from whence we came. */
++
++ addl $LocalVarsSize, %esp
++ popl %ebx
++ popl %esi
++ popl %edi
++ popl %ebp
++match_init: ret
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/zlib/trees.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,1214 @@
++/* trees.c -- output deflated data using Huffman coding
++ * Copyright (C) 1995-2002 Jean-loup Gailly
++ * For conditions of distribution and use, see copyright notice in zlib.h
++ */
++
++/*
++ * ALGORITHM
++ *
++ * The "deflation" process uses several Huffman trees. The more
++ * common source values are represented by shorter bit sequences.
++ *
++ * Each code tree is stored in a compressed form which is itself
++ * a Huffman encoding of the lengths of all the code strings (in
++ * ascending order by source values). The actual code strings are
++ * reconstructed from the lengths in the inflate process, as described
++ * in the deflate specification.
++ *
++ * REFERENCES
++ *
++ * Deutsch, L.P.,"'Deflate' Compressed Data Format Specification".
++ * Available in ftp.uu.net:/pub/archiving/zip/doc/deflate-1.1.doc
++ *
++ * Storer, James A.
++ * Data Compression: Methods and Theory, pp. 49-50.
++ * Computer Science Press, 1988. ISBN 0-7167-8156-5.
++ *
++ * Sedgewick, R.
++ * Algorithms, p290.
++ * Addison-Wesley, 1983. ISBN 0-201-06672-6.
++ */
++
++/* @(#) $Id$ */
++
++/* #define GEN_TREES_H */
++
++#include "deflate.h"
++
++#ifdef DEBUG
++# include <ctype.h>
++#endif
++
++/* ===========================================================================
++ * Constants
++ */
++
++#define MAX_BL_BITS 7
++/* Bit length codes must not exceed MAX_BL_BITS bits */
++
++#define END_BLOCK 256
++/* end of block literal code */
++
++#define REP_3_6 16
++/* repeat previous bit length 3-6 times (2 bits of repeat count) */
++
++#define REPZ_3_10 17
++/* repeat a zero length 3-10 times (3 bits of repeat count) */
++
++#define REPZ_11_138 18
++/* repeat a zero length 11-138 times (7 bits of repeat count) */
++
++local const int extra_lbits[LENGTH_CODES] /* extra bits for each length code */
++ = {0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0};
++
++local const int extra_dbits[D_CODES] /* extra bits for each distance code */
++ = {0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13};
++
++local const int extra_blbits[BL_CODES]/* extra bits for each bit length code */
++ = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7};
++
++local const uch bl_order[BL_CODES]
++ = {16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15};
++/* The lengths of the bit length codes are sent in order of decreasing
++ * probability, to avoid transmitting the lengths for unused bit length codes.
++ */
++
++#define Buf_size (8 * 2*sizeof(char))
++/* Number of bits used within bi_buf. (bi_buf might be implemented on
++ * more than 16 bits on some systems.)
++ */
++
++/* ===========================================================================
++ * Local data. These are initialized only once.
++ */
++
++#define DIST_CODE_LEN 512 /* see definition of array dist_code below */
++
++#if defined(GEN_TREES_H) || !defined(STDC)
++/* non ANSI compilers may not accept trees.h */
++
++local ct_data static_ltree[L_CODES+2];
++/* The static literal tree. Since the bit lengths are imposed, there is no
++ * need for the L_CODES extra codes used during heap construction. However
++ * The codes 286 and 287 are needed to build a canonical tree (see _tr_init
++ * below).
++ */
++
++local ct_data static_dtree[D_CODES];
++/* The static distance tree. (Actually a trivial tree since all codes use
++ * 5 bits.)
++ */
++
++uch _dist_code[DIST_CODE_LEN];
++/* Distance codes. The first 256 values correspond to the distances
++ * 3 .. 258, the last 256 values correspond to the top 8 bits of
++ * the 15 bit distances.
++ */
++
++uch _length_code[MAX_MATCH-MIN_MATCH+1];
++/* length code for each normalized match length (0 == MIN_MATCH) */
++
++local int base_length[LENGTH_CODES];
++/* First normalized length for each code (0 = MIN_MATCH) */
++
++local int base_dist[D_CODES];
++/* First normalized distance for each code (0 = distance of 1) */
++
++#else
++# include "trees.h"
++#endif /* GEN_TREES_H */
++
++struct static_tree_desc_s {
++ const ct_data *static_tree; /* static tree or NULL */
++ const intf *extra_bits; /* extra bits for each code or NULL */
++ int extra_base; /* base index for extra_bits */
++ int elems; /* max number of elements in the tree */
++ int max_length; /* max bit length for the codes */
++};
++
++local static_tree_desc static_l_desc =
++{static_ltree, extra_lbits, LITERALS+1, L_CODES, MAX_BITS};
++
++local static_tree_desc static_d_desc =
++{static_dtree, extra_dbits, 0, D_CODES, MAX_BITS};
++
++local static_tree_desc static_bl_desc =
++{(const ct_data *)0, extra_blbits, 0, BL_CODES, MAX_BL_BITS};
++
++/* ===========================================================================
++ * Local (static) routines in this file.
++ */
++
++local void tr_static_init OF((void));
++local void init_block OF((deflate_state *s));
++local void pqdownheap OF((deflate_state *s, ct_data *tree, int k));
++local void gen_bitlen OF((deflate_state *s, tree_desc *desc));
++local void gen_codes OF((ct_data *tree, int max_code, ushf *bl_count));
++local void build_tree OF((deflate_state *s, tree_desc *desc));
++local void scan_tree OF((deflate_state *s, ct_data *tree, int max_code));
++local void send_tree OF((deflate_state *s, ct_data *tree, int max_code));
++local int build_bl_tree OF((deflate_state *s));
++local void send_all_trees OF((deflate_state *s, int lcodes, int dcodes,
++ int blcodes));
++local void compress_block OF((deflate_state *s, const ct_data *ltree,
++ const ct_data *dtree));
++local void set_data_type OF((deflate_state *s));
++local unsigned bi_reverse OF((unsigned value, int length));
++local void bi_windup OF((deflate_state *s));
++local void bi_flush OF((deflate_state *s));
++local void copy_block OF((deflate_state *s, charf *buf, unsigned len,
++ int header));
++
++#ifdef GEN_TREES_H
++local void gen_trees_header OF((void));
++#endif
++
++#ifndef DEBUG
++# define send_code(s, c, tree) send_bits(s, tree[c].Code, tree[c].Len)
++ /* Send a code of the given tree. c and tree must not have side effects */
++
++#else /* DEBUG */
++# define send_code(s, c, tree) \
++ { if (z_verbose>2) fprintf(stderr,"\ncd %3d ",(c)); \
++ send_bits(s, tree[c].Code, tree[c].Len); }
++#endif
++
++/* ===========================================================================
++ * Output a short LSB first on the stream.
++ * IN assertion: there is enough room in pendingBuf.
++ */
++#define put_short(s, w) { \
++ put_byte(s, (uch)((w) & 0xff)); \
++ put_byte(s, (uch)((ush)(w) >> 8)); \
++}
++
++/* ===========================================================================
++ * Send a value on a given number of bits.
++ * IN assertion: length <= 16 and value fits in length bits.
++ */
++#ifdef DEBUG
++local void send_bits OF((deflate_state *s, int value, int length));
++
++local void send_bits(s, value, length)
++ deflate_state *s;
++ int value; /* value to send */
++ int length; /* number of bits */
++{
++ Tracevv((stderr," l %2d v %4x ", length, value));
++ Assert(length > 0 && length <= 15, "invalid length");
++ s->bits_sent += (ulg)length;
++
++ /* If not enough room in bi_buf, use (valid) bits from bi_buf and
++ * (16 - bi_valid) bits from value, leaving (width - (16-bi_valid))
++ * unused bits in value.
++ */
++ if (s->bi_valid > (int)Buf_size - length) {
++ s->bi_buf |= (value << s->bi_valid);
++ put_short(s, s->bi_buf);
++ s->bi_buf = (ush)value >> (Buf_size - s->bi_valid);
++ s->bi_valid += length - Buf_size;
++ } else {
++ s->bi_buf |= value << s->bi_valid;
++ s->bi_valid += length;
++ }
++}
++#else /* !DEBUG */
++
++#define send_bits(s, value, length) \
++{ int len = length;\
++ if (s->bi_valid > (int)Buf_size - len) {\
++ int val = value;\
++ s->bi_buf |= (val << s->bi_valid);\
++ put_short(s, s->bi_buf);\
++ s->bi_buf = (ush)val >> (Buf_size - s->bi_valid);\
++ s->bi_valid += len - Buf_size;\
++ } else {\
++ s->bi_buf |= (value) << s->bi_valid;\
++ s->bi_valid += len;\
++ }\
++}
++#endif /* DEBUG */
++
++
++#define MAX(a,b) (a >= b ? a : b)
++/* the arguments must not have side effects */
++
++/* ===========================================================================
++ * Initialize the various 'constant' tables.
++ */
++local void tr_static_init()
++{
++#if defined(GEN_TREES_H) || !defined(STDC)
++ static int static_init_done = 0;
++ int n; /* iterates over tree elements */
++ int bits; /* bit counter */
++ int length; /* length value */
++ int code; /* code value */
++ int dist; /* distance index */
++ ush bl_count[MAX_BITS+1];
++ /* number of codes at each bit length for an optimal tree */
++
++ if (static_init_done) return;
++
++ /* For some embedded targets, global variables are not initialized: */
++ static_l_desc.static_tree = static_ltree;
++ static_l_desc.extra_bits = extra_lbits;
++ static_d_desc.static_tree = static_dtree;
++ static_d_desc.extra_bits = extra_dbits;
++ static_bl_desc.extra_bits = extra_blbits;
++
++ /* Initialize the mapping length (0..255) -> length code (0..28) */
++ length = 0;
++ for (code = 0; code < LENGTH_CODES-1; code++) {
++ base_length[code] = length;
++ for (n = 0; n < (1<<extra_lbits[code]); n++) {
++ _length_code[length++] = (uch)code;
++ }
++ }
++ Assert (length == 256, "tr_static_init: length != 256");
++ /* Note that the length 255 (match length 258) can be represented
++ * in two different ways: code 284 + 5 bits or code 285, so we
++ * overwrite length_code[255] to use the best encoding:
++ */
++ _length_code[length-1] = (uch)code;
++
++ /* Initialize the mapping dist (0..32K) -> dist code (0..29) */
++ dist = 0;
++ for (code = 0 ; code < 16; code++) {
++ base_dist[code] = dist;
++ for (n = 0; n < (1<<extra_dbits[code]); n++) {
++ _dist_code[dist++] = (uch)code;
++ }
++ }
++ Assert (dist == 256, "tr_static_init: dist != 256");
++ dist >>= 7; /* from now on, all distances are divided by 128 */
++ for ( ; code < D_CODES; code++) {
++ base_dist[code] = dist << 7;
++ for (n = 0; n < (1<<(extra_dbits[code]-7)); n++) {
++ _dist_code[256 + dist++] = (uch)code;
++ }
++ }
++ Assert (dist == 256, "tr_static_init: 256+dist != 512");
++
++ /* Construct the codes of the static literal tree */
++ for (bits = 0; bits <= MAX_BITS; bits++) bl_count[bits] = 0;
++ n = 0;
++ while (n <= 143) static_ltree[n++].Len = 8, bl_count[8]++;
++ while (n <= 255) static_ltree[n++].Len = 9, bl_count[9]++;
++ while (n <= 279) static_ltree[n++].Len = 7, bl_count[7]++;
++ while (n <= 287) static_ltree[n++].Len = 8, bl_count[8]++;
++ /* Codes 286 and 287 do not exist, but we must include them in the
++ * tree construction to get a canonical Huffman tree (longest code
++ * all ones)
++ */
++ gen_codes((ct_data *)static_ltree, L_CODES+1, bl_count);
++
++ /* The static distance tree is trivial: */
++ for (n = 0; n < D_CODES; n++) {
++ static_dtree[n].Len = 5;
++ static_dtree[n].Code = bi_reverse((unsigned)n, 5);
++ }
++ static_init_done = 1;
++
++# ifdef GEN_TREES_H
++ gen_trees_header();
++# endif
++#endif /* defined(GEN_TREES_H) || !defined(STDC) */
++}
++
++/* ===========================================================================
++ * Genererate the file trees.h describing the static trees.
++ */
++#ifdef GEN_TREES_H
++# ifndef DEBUG
++# include <stdio.h>
++# endif
++
++# define SEPARATOR(i, last, width) \
++ ((i) == (last)? "\n};\n\n" : \
++ ((i) % (width) == (width)-1 ? ",\n" : ", "))
++
++void gen_trees_header()
++{
++ FILE *header = fopen("trees.h", "w");
++ int i;
++
++ Assert (header != NULL, "Can't open trees.h");
++ fprintf(header,
++ "/* header created automatically with -DGEN_TREES_H */\n\n");
++
++ fprintf(header, "local const ct_data static_ltree[L_CODES+2] = {\n");
++ for (i = 0; i < L_CODES+2; i++) {
++ fprintf(header, "{{%3u},{%3u}}%s", static_ltree[i].Code,
++ static_ltree[i].Len, SEPARATOR(i, L_CODES+1, 5));
++ }
++
++ fprintf(header, "local const ct_data static_dtree[D_CODES] = {\n");
++ for (i = 0; i < D_CODES; i++) {
++ fprintf(header, "{{%2u},{%2u}}%s", static_dtree[i].Code,
++ static_dtree[i].Len, SEPARATOR(i, D_CODES-1, 5));
++ }
++
++ fprintf(header, "const uch _dist_code[DIST_CODE_LEN] = {\n");
++ for (i = 0; i < DIST_CODE_LEN; i++) {
++ fprintf(header, "%2u%s", _dist_code[i],
++ SEPARATOR(i, DIST_CODE_LEN-1, 20));
++ }
++
++ fprintf(header, "const uch _length_code[MAX_MATCH-MIN_MATCH+1]= {\n");
++ for (i = 0; i < MAX_MATCH-MIN_MATCH+1; i++) {
++ fprintf(header, "%2u%s", _length_code[i],
++ SEPARATOR(i, MAX_MATCH-MIN_MATCH, 20));
++ }
++
++ fprintf(header, "local const int base_length[LENGTH_CODES] = {\n");
++ for (i = 0; i < LENGTH_CODES; i++) {
++ fprintf(header, "%1u%s", base_length[i],
++ SEPARATOR(i, LENGTH_CODES-1, 20));
++ }
++
++ fprintf(header, "local const int base_dist[D_CODES] = {\n");
++ for (i = 0; i < D_CODES; i++) {
++ fprintf(header, "%5u%s", base_dist[i],
++ SEPARATOR(i, D_CODES-1, 10));
++ }
++
++ fclose(header);
++}
++#endif /* GEN_TREES_H */
++
++/* ===========================================================================
++ * Initialize the tree data structures for a new zlib stream.
++ */
++void _tr_init(s)
++ deflate_state *s;
++{
++ tr_static_init();
++
++ s->l_desc.dyn_tree = s->dyn_ltree;
++ s->l_desc.stat_desc = &static_l_desc;
++
++ s->d_desc.dyn_tree = s->dyn_dtree;
++ s->d_desc.stat_desc = &static_d_desc;
++
++ s->bl_desc.dyn_tree = s->bl_tree;
++ s->bl_desc.stat_desc = &static_bl_desc;
++
++ s->bi_buf = 0;
++ s->bi_valid = 0;
++ s->last_eob_len = 8; /* enough lookahead for inflate */
++#ifdef DEBUG
++ s->compressed_len = 0L;
++ s->bits_sent = 0L;
++#endif
++
++ /* Initialize the first block of the first file: */
++ init_block(s);
++}
++
++/* ===========================================================================
++ * Initialize a new block.
++ */
++local void init_block(s)
++ deflate_state *s;
++{
++ int n; /* iterates over tree elements */
++
++ /* Initialize the trees. */
++ for (n = 0; n < L_CODES; n++) s->dyn_ltree[n].Freq = 0;
++ for (n = 0; n < D_CODES; n++) s->dyn_dtree[n].Freq = 0;
++ for (n = 0; n < BL_CODES; n++) s->bl_tree[n].Freq = 0;
++
++ s->dyn_ltree[END_BLOCK].Freq = 1;
++ s->opt_len = s->static_len = 0L;
++ s->last_lit = s->matches = 0;
++}
++
++#define SMALLEST 1
++/* Index within the heap array of least frequent node in the Huffman tree */
++
++
++/* ===========================================================================
++ * Remove the smallest element from the heap and recreate the heap with
++ * one less element. Updates heap and heap_len.
++ */
++#define pqremove(s, tree, top) \
++{\
++ top = s->heap[SMALLEST]; \
++ s->heap[SMALLEST] = s->heap[s->heap_len--]; \
++ pqdownheap(s, tree, SMALLEST); \
++}
++
++/* ===========================================================================
++ * Compares to subtrees, using the tree depth as tie breaker when
++ * the subtrees have equal frequency. This minimizes the worst case length.
++ */
++#define smaller(tree, n, m, depth) \
++ (tree[n].Freq < tree[m].Freq || \
++ (tree[n].Freq == tree[m].Freq && depth[n] <= depth[m]))
++
++/* ===========================================================================
++ * Restore the heap property by moving down the tree starting at node k,
++ * exchanging a node with the smallest of its two sons if necessary, stopping
++ * when the heap property is re-established (each father smaller than its
++ * two sons).
++ */
++local void pqdownheap(s, tree, k)
++ deflate_state *s;
++ ct_data *tree; /* the tree to restore */
++ int k; /* node to move down */
++{
++ int v = s->heap[k];
++ int j = k << 1; /* left son of k */
++ while (j <= s->heap_len) {
++ /* Set j to the smallest of the two sons: */
++ if (j < s->heap_len &&
++ smaller(tree, s->heap[j+1], s->heap[j], s->depth)) {
++ j++;
++ }
++ /* Exit if v is smaller than both sons */
++ if (smaller(tree, v, s->heap[j], s->depth)) break;
++
++ /* Exchange v with the smallest son */
++ s->heap[k] = s->heap[j]; k = j;
++
++ /* And continue down the tree, setting j to the left son of k */
++ j <<= 1;
++ }
++ s->heap[k] = v;
++}
++
++/* ===========================================================================
++ * Compute the optimal bit lengths for a tree and update the total bit length
++ * for the current block.
++ * IN assertion: the fields freq and dad are set, heap[heap_max] and
++ * above are the tree nodes sorted by increasing frequency.
++ * OUT assertions: the field len is set to the optimal bit length, the
++ * array bl_count contains the frequencies for each bit length.
++ * The length opt_len is updated; static_len is also updated if stree is
++ * not null.
++ */
++local void gen_bitlen(s, desc)
++ deflate_state *s;
++ tree_desc *desc; /* the tree descriptor */
++{
++ ct_data *tree = desc->dyn_tree;
++ int max_code = desc->max_code;
++ const ct_data *stree = desc->stat_desc->static_tree;
++ const intf *extra = desc->stat_desc->extra_bits;
++ int base = desc->stat_desc->extra_base;
++ int max_length = desc->stat_desc->max_length;
++ int h; /* heap index */
++ int n, m; /* iterate over the tree elements */
++ int bits; /* bit length */
++ int xbits; /* extra bits */
++ ush f; /* frequency */
++ int overflow = 0; /* number of elements with bit length too large */
++
++ for (bits = 0; bits <= MAX_BITS; bits++) s->bl_count[bits] = 0;
++
++ /* In a first pass, compute the optimal bit lengths (which may
++ * overflow in the case of the bit length tree).
++ */
++ tree[s->heap[s->heap_max]].Len = 0; /* root of the heap */
++
++ for (h = s->heap_max+1; h < HEAP_SIZE; h++) {
++ n = s->heap[h];
++ bits = tree[tree[n].Dad].Len + 1;
++ if (bits > max_length) bits = max_length, overflow++;
++ tree[n].Len = (ush)bits;
++ /* We overwrite tree[n].Dad which is no longer needed */
++
++ if (n > max_code) continue; /* not a leaf node */
++
++ s->bl_count[bits]++;
++ xbits = 0;
++ if (n >= base) xbits = extra[n-base];
++ f = tree[n].Freq;
++ s->opt_len += (ulg)f * (bits + xbits);
++ if (stree) s->static_len += (ulg)f * (stree[n].Len + xbits);
++ }
++ if (overflow == 0) return;
++
++ Trace((stderr,"\nbit length overflow\n"));
++ /* This happens for example on obj2 and pic of the Calgary corpus */
++
++ /* Find the first bit length which could increase: */
++ do {
++ bits = max_length-1;
++ while (s->bl_count[bits] == 0) bits--;
++ s->bl_count[bits]--; /* move one leaf down the tree */
++ s->bl_count[bits+1] += 2; /* move one overflow item as its brother */
++ s->bl_count[max_length]--;
++ /* The brother of the overflow item also moves one step up,
++ * but this does not affect bl_count[max_length]
++ */
++ overflow -= 2;
++ } while (overflow > 0);
++
++ /* Now recompute all bit lengths, scanning in increasing frequency.
++ * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all
++ * lengths instead of fixing only the wrong ones. This idea is taken
++ * from 'ar' written by Haruhiko Okumura.)
++ */
++ for (bits = max_length; bits != 0; bits--) {
++ n = s->bl_count[bits];
++ while (n != 0) {
++ m = s->heap[--h];
++ if (m > max_code) continue;
++ if (tree[m].Len != (unsigned) bits) {
++ Trace((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits));
++ s->opt_len += ((long)bits - (long)tree[m].Len)
++ *(long)tree[m].Freq;
++ tree[m].Len = (ush)bits;
++ }
++ n--;
++ }
++ }
++}
++
++/* ===========================================================================
++ * Generate the codes for a given tree and bit counts (which need not be
++ * optimal).
++ * IN assertion: the array bl_count contains the bit length statistics for
++ * the given tree and the field len is set for all tree elements.
++ * OUT assertion: the field code is set for all tree elements of non
++ * zero code length.
++ */
++local void gen_codes (tree, max_code, bl_count)
++ ct_data *tree; /* the tree to decorate */
++ int max_code; /* largest code with non zero frequency */
++ ushf *bl_count; /* number of codes at each bit length */
++{
++ ush next_code[MAX_BITS+1]; /* next code value for each bit length */
++ ush code = 0; /* running code value */
++ int bits; /* bit index */
++ int n; /* code index */
++
++ /* The distribution counts are first used to generate the code values
++ * without bit reversal.
++ */
++ for (bits = 1; bits <= MAX_BITS; bits++) {
++ next_code[bits] = code = (code + bl_count[bits-1]) << 1;
++ }
++ /* Check that the bit counts in bl_count are consistent. The last code
++ * must be all ones.
++ */
++ Assert (code + bl_count[MAX_BITS]-1 == (1<<MAX_BITS)-1,
++ "inconsistent bit counts");
++ Tracev((stderr,"\ngen_codes: max_code %d ", max_code));
++
++ for (n = 0; n <= max_code; n++) {
++ int len = tree[n].Len;
++ if (len == 0) continue;
++ /* Now reverse the bits */
++ tree[n].Code = bi_reverse(next_code[len]++, len);
++
++ Tracecv(tree != static_ltree, (stderr,"\nn %3d %c l %2d c %4x (%x) ",
++ n, (isgraph(n) ? n : ' '), len, tree[n].Code, next_code[len]-1));
++ }
++}
++
++/* ===========================================================================
++ * Construct one Huffman tree and assigns the code bit strings and lengths.
++ * Update the total bit length for the current block.
++ * IN assertion: the field freq is set for all tree elements.
++ * OUT assertions: the fields len and code are set to the optimal bit length
++ * and corresponding code. The length opt_len is updated; static_len is
++ * also updated if stree is not null. The field max_code is set.
++ */
++local void build_tree(s, desc)
++ deflate_state *s;
++ tree_desc *desc; /* the tree descriptor */
++{
++ ct_data *tree = desc->dyn_tree;
++ const ct_data *stree = desc->stat_desc->static_tree;
++ int elems = desc->stat_desc->elems;
++ int n, m; /* iterate over heap elements */
++ int max_code = -1; /* largest code with non zero frequency */
++ int node; /* new node being created */
++
++ /* Construct the initial heap, with least frequent element in
++ * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1].
++ * heap[0] is not used.
++ */
++ s->heap_len = 0, s->heap_max = HEAP_SIZE;
++
++ for (n = 0; n < elems; n++) {
++ if (tree[n].Freq != 0) {
++ s->heap[++(s->heap_len)] = max_code = n;
++ s->depth[n] = 0;
++ } else {
++ tree[n].Len = 0;
++ }
++ }
++
++ /* The pkzip format requires that at least one distance code exists,
++ * and that at least one bit should be sent even if there is only one
++ * possible code. So to avoid special checks later on we force at least
++ * two codes of non zero frequency.
++ */
++ while (s->heap_len < 2) {
++ node = s->heap[++(s->heap_len)] = (max_code < 2 ? ++max_code : 0);
++ tree[node].Freq = 1;
++ s->depth[node] = 0;
++ s->opt_len--; if (stree) s->static_len -= stree[node].Len;
++ /* node is 0 or 1 so it does not have extra bits */
++ }
++ desc->max_code = max_code;
++
++ /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree,
++ * establish sub-heaps of increasing lengths:
++ */
++ for (n = s->heap_len/2; n >= 1; n--) pqdownheap(s, tree, n);
++
++ /* Construct the Huffman tree by repeatedly combining the least two
++ * frequent nodes.
++ */
++ node = elems; /* next internal node of the tree */
++ do {
++ pqremove(s, tree, n); /* n = node of least frequency */
++ m = s->heap[SMALLEST]; /* m = node of next least frequency */
++
++ s->heap[--(s->heap_max)] = n; /* keep the nodes sorted by frequency */
++ s->heap[--(s->heap_max)] = m;
++
++ /* Create a new node father of n and m */
++ tree[node].Freq = tree[n].Freq + tree[m].Freq;
++ s->depth[node] = (uch) (MAX(s->depth[n], s->depth[m]) + 1);
++ tree[n].Dad = tree[m].Dad = (ush)node;
++#ifdef DUMP_BL_TREE
++ if (tree == s->bl_tree) {
++ fprintf(stderr,"\nnode %d(%d), sons %d(%d) %d(%d)",
++ node, tree[node].Freq, n, tree[n].Freq, m, tree[m].Freq);
++ }
++#endif
++ /* and insert the new node in the heap */
++ s->heap[SMALLEST] = node++;
++ pqdownheap(s, tree, SMALLEST);
++
++ } while (s->heap_len >= 2);
++
++ s->heap[--(s->heap_max)] = s->heap[SMALLEST];
++
++ /* At this point, the fields freq and dad are set. We can now
++ * generate the bit lengths.
++ */
++ gen_bitlen(s, (tree_desc *)desc);
++
++ /* The field len is now set, we can generate the bit codes */
++ gen_codes ((ct_data *)tree, max_code, s->bl_count);
++}
++
++/* ===========================================================================
++ * Scan a literal or distance tree to determine the frequencies of the codes
++ * in the bit length tree.
++ */
++local void scan_tree (s, tree, max_code)
++ deflate_state *s;
++ ct_data *tree; /* the tree to be scanned */
++ int max_code; /* and its largest code of non zero frequency */
++{
++ int n; /* iterates over all tree elements */
++ int prevlen = -1; /* last emitted length */
++ int curlen; /* length of current code */
++ int nextlen = tree[0].Len; /* length of next code */
++ int count = 0; /* repeat count of the current code */
++ int max_count = 7; /* max repeat count */
++ int min_count = 4; /* min repeat count */
++
++ if (nextlen == 0) max_count = 138, min_count = 3;
++ tree[max_code+1].Len = (ush)0xffff; /* guard */
++
++ for (n = 0; n <= max_code; n++) {
++ curlen = nextlen; nextlen = tree[n+1].Len;
++ if (++count < max_count && curlen == nextlen) {
++ continue;
++ } else if (count < min_count) {
++ s->bl_tree[curlen].Freq += count;
++ } else if (curlen != 0) {
++ if (curlen != prevlen) s->bl_tree[curlen].Freq++;
++ s->bl_tree[REP_3_6].Freq++;
++ } else if (count <= 10) {
++ s->bl_tree[REPZ_3_10].Freq++;
++ } else {
++ s->bl_tree[REPZ_11_138].Freq++;
++ }
++ count = 0; prevlen = curlen;
++ if (nextlen == 0) {
++ max_count = 138, min_count = 3;
++ } else if (curlen == nextlen) {
++ max_count = 6, min_count = 3;
++ } else {
++ max_count = 7, min_count = 4;
++ }
++ }
++}
++
++/* ===========================================================================
++ * Send a literal or distance tree in compressed form, using the codes in
++ * bl_tree.
++ */
++local void send_tree (s, tree, max_code)
++ deflate_state *s;
++ ct_data *tree; /* the tree to be scanned */
++ int max_code; /* and its largest code of non zero frequency */
++{
++ int n; /* iterates over all tree elements */
++ int prevlen = -1; /* last emitted length */
++ int curlen; /* length of current code */
++ int nextlen = tree[0].Len; /* length of next code */
++ int count = 0; /* repeat count of the current code */
++ int max_count = 7; /* max repeat count */
++ int min_count = 4; /* min repeat count */
++
++ /* tree[max_code+1].Len = -1; */ /* guard already set */
++ if (nextlen == 0) max_count = 138, min_count = 3;
++
++ for (n = 0; n <= max_code; n++) {
++ curlen = nextlen; nextlen = tree[n+1].Len;
++ if (++count < max_count && curlen == nextlen) {
++ continue;
++ } else if (count < min_count) {
++ do { send_code(s, curlen, s->bl_tree); } while (--count != 0);
++
++ } else if (curlen != 0) {
++ if (curlen != prevlen) {
++ send_code(s, curlen, s->bl_tree); count--;
++ }
++ Assert(count >= 3 && count <= 6, " 3_6?");
++ send_code(s, REP_3_6, s->bl_tree); send_bits(s, count-3, 2);
++
++ } else if (count <= 10) {
++ send_code(s, REPZ_3_10, s->bl_tree); send_bits(s, count-3, 3);
++
++ } else {
++ send_code(s, REPZ_11_138, s->bl_tree); send_bits(s, count-11, 7);
++ }
++ count = 0; prevlen = curlen;
++ if (nextlen == 0) {
++ max_count = 138, min_count = 3;
++ } else if (curlen == nextlen) {
++ max_count = 6, min_count = 3;
++ } else {
++ max_count = 7, min_count = 4;
++ }
++ }
++}
++
++/* ===========================================================================
++ * Construct the Huffman tree for the bit lengths and return the index in
++ * bl_order of the last bit length code to send.
++ */
++local int build_bl_tree(s)
++ deflate_state *s;
++{
++ int max_blindex; /* index of last bit length code of non zero freq */
++
++ /* Determine the bit length frequencies for literal and distance trees */
++ scan_tree(s, (ct_data *)s->dyn_ltree, s->l_desc.max_code);
++ scan_tree(s, (ct_data *)s->dyn_dtree, s->d_desc.max_code);
++
++ /* Build the bit length tree: */
++ build_tree(s, (tree_desc *)(&(s->bl_desc)));
++ /* opt_len now includes the length of the tree representations, except
++ * the lengths of the bit lengths codes and the 5+5+4 bits for the counts.
++ */
++
++ /* Determine the number of bit length codes to send. The pkzip format
++ * requires that at least 4 bit length codes be sent. (appnote.txt says
++ * 3 but the actual value used is 4.)
++ */
++ for (max_blindex = BL_CODES-1; max_blindex >= 3; max_blindex--) {
++ if (s->bl_tree[bl_order[max_blindex]].Len != 0) break;
++ }
++ /* Update opt_len to include the bit length tree and counts */
++ s->opt_len += 3*(max_blindex+1) + 5+5+4;
++ Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld",
++ s->opt_len, s->static_len));
++
++ return max_blindex;
++}
++
++/* ===========================================================================
++ * Send the header for a block using dynamic Huffman trees: the counts, the
++ * lengths of the bit length codes, the literal tree and the distance tree.
++ * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4.
++ */
++local void send_all_trees(s, lcodes, dcodes, blcodes)
++ deflate_state *s;
++ int lcodes, dcodes, blcodes; /* number of codes for each tree */
++{
++ int rank; /* index in bl_order */
++
++ Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes");
++ Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES,
++ "too many codes");
++ Tracev((stderr, "\nbl counts: "));
++ send_bits(s, lcodes-257, 5); /* not +255 as stated in appnote.txt */
++ send_bits(s, dcodes-1, 5);
++ send_bits(s, blcodes-4, 4); /* not -3 as stated in appnote.txt */
++ for (rank = 0; rank < blcodes; rank++) {
++ Tracev((stderr, "\nbl code %2d ", bl_order[rank]));
++ send_bits(s, s->bl_tree[bl_order[rank]].Len, 3);
++ }
++ Tracev((stderr, "\nbl tree: sent %ld", s->bits_sent));
++
++ send_tree(s, (ct_data *)s->dyn_ltree, lcodes-1); /* literal tree */
++ Tracev((stderr, "\nlit tree: sent %ld", s->bits_sent));
++
++ send_tree(s, (ct_data *)s->dyn_dtree, dcodes-1); /* distance tree */
++ Tracev((stderr, "\ndist tree: sent %ld", s->bits_sent));
++}
++
++/* ===========================================================================
++ * Send a stored block
++ */
++void _tr_stored_block(s, buf, stored_len, eof)
++ deflate_state *s;
++ charf *buf; /* input block */
++ ulg stored_len; /* length of input block */
++ int eof; /* true if this is the last block for a file */
++{
++ send_bits(s, (STORED_BLOCK<<1)+eof, 3); /* send block type */
++#ifdef DEBUG
++ s->compressed_len = (s->compressed_len + 3 + 7) & (ulg)~7L;
++ s->compressed_len += (stored_len + 4) << 3;
++#endif
++ copy_block(s, buf, (unsigned)stored_len, 1); /* with header */
++}
++
++/* ===========================================================================
++ * Send one empty static block to give enough lookahead for inflate.
++ * This takes 10 bits, of which 7 may remain in the bit buffer.
++ * The current inflate code requires 9 bits of lookahead. If the
++ * last two codes for the previous block (real code plus EOB) were coded
++ * on 5 bits or less, inflate may have only 5+3 bits of lookahead to decode
++ * the last real code. In this case we send two empty static blocks instead
++ * of one. (There are no problems if the previous block is stored or fixed.)
++ * To simplify the code, we assume the worst case of last real code encoded
++ * on one bit only.
++ */
++void _tr_align(s)
++ deflate_state *s;
++{
++ send_bits(s, STATIC_TREES<<1, 3);
++ send_code(s, END_BLOCK, static_ltree);
++#ifdef DEBUG
++ s->compressed_len += 10L; /* 3 for block type, 7 for EOB */
++#endif
++ bi_flush(s);
++ /* Of the 10 bits for the empty block, we have already sent
++ * (10 - bi_valid) bits. The lookahead for the last real code (before
++ * the EOB of the previous block) was thus at least one plus the length
++ * of the EOB plus what we have just sent of the empty static block.
++ */
++ if (1 + s->last_eob_len + 10 - s->bi_valid < 9) {
++ send_bits(s, STATIC_TREES<<1, 3);
++ send_code(s, END_BLOCK, static_ltree);
++#ifdef DEBUG
++ s->compressed_len += 10L;
++#endif
++ bi_flush(s);
++ }
++ s->last_eob_len = 7;
++}
++
++/* ===========================================================================
++ * Determine the best encoding for the current block: dynamic trees, static
++ * trees or store, and output the encoded block to the zip file.
++ */
++void _tr_flush_block(s, buf, stored_len, eof)
++ deflate_state *s;
++ charf *buf; /* input block, or NULL if too old */
++ ulg stored_len; /* length of input block */
++ int eof; /* true if this is the last block for a file */
++{
++ ulg opt_lenb, static_lenb; /* opt_len and static_len in bytes */
++ int max_blindex = 0; /* index of last bit length code of non zero freq */
++
++ /* Build the Huffman trees unless a stored block is forced */
++ if (s->level > 0) {
++
++ /* Check if the file is ascii or binary */
++ if (s->data_type == Z_UNKNOWN) set_data_type(s);
++
++ /* Construct the literal and distance trees */
++ build_tree(s, (tree_desc *)(&(s->l_desc)));
++ Tracev((stderr, "\nlit data: dyn %ld, stat %ld", s->opt_len,
++ s->static_len));
++
++ build_tree(s, (tree_desc *)(&(s->d_desc)));
++ Tracev((stderr, "\ndist data: dyn %ld, stat %ld", s->opt_len,
++ s->static_len));
++ /* At this point, opt_len and static_len are the total bit lengths of
++ * the compressed block data, excluding the tree representations.
++ */
++
++ /* Build the bit length tree for the above two trees, and get the index
++ * in bl_order of the last bit length code to send.
++ */
++ max_blindex = build_bl_tree(s);
++
++ /* Determine the best encoding. Compute first the block length in bytes*/
++ opt_lenb = (s->opt_len+3+7)>>3;
++ static_lenb = (s->static_len+3+7)>>3;
++
++ Tracev((stderr, "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u ",
++ opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len,
++ s->last_lit));
++
++ if (static_lenb <= opt_lenb) opt_lenb = static_lenb;
++
++ } else {
++ Assert(buf != (char*)0, "lost buf");
++ opt_lenb = static_lenb = stored_len + 5; /* force a stored block */
++ }
++
++#ifdef FORCE_STORED
++ if (buf != (char*)0) { /* force stored block */
++#else
++ if (stored_len+4 <= opt_lenb && buf != (char*)0) {
++ /* 4: two words for the lengths */
++#endif
++ /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE.
++ * Otherwise we can't have processed more than WSIZE input bytes since
++ * the last block flush, because compression would have been
++ * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to
++ * transform a block into a stored block.
++ */
++ _tr_stored_block(s, buf, stored_len, eof);
++
++#ifdef FORCE_STATIC
++ } else if (static_lenb >= 0) { /* force static trees */
++#else
++ } else if (static_lenb == opt_lenb) {
++#endif
++ send_bits(s, (STATIC_TREES<<1)+eof, 3);
++ compress_block(s, static_ltree, static_dtree);
++#ifdef DEBUG
++ s->compressed_len += 3 + s->static_len;
++#endif
++ } else {
++ send_bits(s, (DYN_TREES<<1)+eof, 3);
++ send_all_trees(s, s->l_desc.max_code+1, s->d_desc.max_code+1,
++ max_blindex+1);
++ compress_block(s, s->dyn_ltree, s->dyn_dtree);
++#ifdef DEBUG
++ s->compressed_len += 3 + s->opt_len;
++#endif
++ }
++ Assert (s->compressed_len == s->bits_sent, "bad compressed size");
++ /* The above check is made mod 2^32, for files larger than 512 MB
++ * and uLong implemented on 32 bits.
++ */
++ init_block(s);
++
++ if (eof) {
++ bi_windup(s);
++#ifdef DEBUG
++ s->compressed_len += 7; /* align on byte boundary */
++#endif
++ }
++ Tracev((stderr,"\ncomprlen %lu(%lu) ", s->compressed_len>>3,
++ s->compressed_len-7*eof));
++}
++
++/* ===========================================================================
++ * Save the match info and tally the frequency counts. Return true if
++ * the current block must be flushed.
++ */
++int _tr_tally (s, dist, lc)
++ deflate_state *s;
++ unsigned dist; /* distance of matched string */
++ unsigned lc; /* match length-MIN_MATCH or unmatched char (if dist==0) */
++{
++ s->d_buf[s->last_lit] = (ush)dist;
++ s->l_buf[s->last_lit++] = (uch)lc;
++ if (dist == 0) {
++ /* lc is the unmatched char */
++ s->dyn_ltree[lc].Freq++;
++ } else {
++ s->matches++;
++ /* Here, lc is the match length - MIN_MATCH */
++ dist--; /* dist = match distance - 1 */
++ Assert((ush)dist < (ush)MAX_DIST(s) &&
++ (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) &&
++ (ush)d_code(dist) < (ush)D_CODES, "_tr_tally: bad match");
++
++ s->dyn_ltree[_length_code[lc]+LITERALS+1].Freq++;
++ s->dyn_dtree[d_code(dist)].Freq++;
++ }
++
++#ifdef TRUNCATE_BLOCK
++ /* Try to guess if it is profitable to stop the current block here */
++ if ((s->last_lit & 0x1fff) == 0 && s->level > 2) {
++ /* Compute an upper bound for the compressed length */
++ ulg out_length = (ulg)s->last_lit*8L;
++ ulg in_length = (ulg)((long)s->strstart - s->block_start);
++ int dcode;
++ for (dcode = 0; dcode < D_CODES; dcode++) {
++ out_length += (ulg)s->dyn_dtree[dcode].Freq *
++ (5L+extra_dbits[dcode]);
++ }
++ out_length >>= 3;
++ Tracev((stderr,"\nlast_lit %u, in %ld, out ~%ld(%ld%%) ",
++ s->last_lit, in_length, out_length,
++ 100L - out_length*100L/in_length));
++ if (s->matches < s->last_lit/2 && out_length < in_length/2) return 1;
++ }
++#endif
++ return (s->last_lit == s->lit_bufsize-1);
++ /* We avoid equality with lit_bufsize because of wraparound at 64K
++ * on 16 bit machines and because stored blocks are restricted to
++ * 64K-1 bytes.
++ */
++}
++
++/* ===========================================================================
++ * Send the block data compressed using the given Huffman trees
++ */
++local void compress_block(s, ltree, dtree)
++ deflate_state *s;
++ const ct_data *ltree; /* literal tree */
++ const ct_data *dtree; /* distance tree */
++{
++ unsigned dist; /* distance of matched string */
++ int lc; /* match length or unmatched char (if dist == 0) */
++ unsigned lx = 0; /* running index in l_buf */
++ unsigned code; /* the code to send */
++ int extra; /* number of extra bits to send */
++
++ if (s->last_lit != 0) do {
++ dist = s->d_buf[lx];
++ lc = s->l_buf[lx++];
++ if (dist == 0) {
++ send_code(s, lc, ltree); /* send a literal byte */
++ Tracecv(isgraph(lc), (stderr," '%c' ", lc));
++ } else {
++ /* Here, lc is the match length - MIN_MATCH */
++ code = _length_code[lc];
++ send_code(s, code+LITERALS+1, ltree); /* send the length code */
++ extra = extra_lbits[code];
++ if (extra != 0) {
++ lc -= base_length[code];
++ send_bits(s, lc, extra); /* send the extra length bits */
++ }
++ dist--; /* dist is now the match distance - 1 */
++ code = d_code(dist);
++ Assert (code < D_CODES, "bad d_code");
++
++ send_code(s, code, dtree); /* send the distance code */
++ extra = extra_dbits[code];
++ if (extra != 0) {
++ dist -= base_dist[code];
++ send_bits(s, dist, extra); /* send the extra distance bits */
++ }
++ } /* literal or match pair ? */
++
++ /* Check that the overlay between pending_buf and d_buf+l_buf is ok: */
++ Assert(s->pending < s->lit_bufsize + 2*lx, "pendingBuf overflow");
++
++ } while (lx < s->last_lit);
++
++ send_code(s, END_BLOCK, ltree);
++ s->last_eob_len = ltree[END_BLOCK].Len;
++}
++
++/* ===========================================================================
++ * Set the data type to ASCII or BINARY, using a crude approximation:
++ * binary if more than 20% of the bytes are <= 6 or >= 128, ascii otherwise.
++ * IN assertion: the fields freq of dyn_ltree are set and the total of all
++ * frequencies does not exceed 64K (to fit in an int on 16 bit machines).
++ */
++local void set_data_type(s)
++ deflate_state *s;
++{
++ int n = 0;
++ unsigned ascii_freq = 0;
++ unsigned bin_freq = 0;
++ while (n < 7) bin_freq += s->dyn_ltree[n++].Freq;
++ while (n < 128) ascii_freq += s->dyn_ltree[n++].Freq;
++ while (n < LITERALS) bin_freq += s->dyn_ltree[n++].Freq;
++ s->data_type = (Byte)(bin_freq > (ascii_freq >> 2) ? Z_BINARY : Z_ASCII);
++}
++
++/* ===========================================================================
++ * Reverse the first len bits of a code, using straightforward code (a faster
++ * method would use a table)
++ * IN assertion: 1 <= len <= 15
++ */
++local unsigned bi_reverse(code, len)
++ unsigned code; /* the value to invert */
++ int len; /* its bit length */
++{
++ register unsigned res = 0;
++ do {
++ res |= code & 1;
++ code >>= 1, res <<= 1;
++ } while (--len > 0);
++ return res >> 1;
++}
++
++/* ===========================================================================
++ * Flush the bit buffer, keeping at most 7 bits in it.
++ */
++local void bi_flush(s)
++ deflate_state *s;
++{
++ if (s->bi_valid == 16) {
++ put_short(s, s->bi_buf);
++ s->bi_buf = 0;
++ s->bi_valid = 0;
++ } else if (s->bi_valid >= 8) {
++ put_byte(s, (Byte)s->bi_buf);
++ s->bi_buf >>= 8;
++ s->bi_valid -= 8;
++ }
++}
++
++/* ===========================================================================
++ * Flush the bit buffer and align the output on a byte boundary
++ */
++local void bi_windup(s)
++ deflate_state *s;
++{
++ if (s->bi_valid > 8) {
++ put_short(s, s->bi_buf);
++ } else if (s->bi_valid > 0) {
++ put_byte(s, (Byte)s->bi_buf);
++ }
++ s->bi_buf = 0;
++ s->bi_valid = 0;
++#ifdef DEBUG
++ s->bits_sent = (s->bits_sent+7) & ~7;
++#endif
++}
++
++/* ===========================================================================
++ * Copy a stored block, storing first the length and its
++ * one's complement if requested.
++ */
++local void copy_block(s, buf, len, header)
++ deflate_state *s;
++ charf *buf; /* the input data */
++ unsigned len; /* its length */
++ int header; /* true if block header must be written */
++{
++ bi_windup(s); /* align on byte boundary */
++ s->last_eob_len = 8; /* enough lookahead for inflate */
++
++ if (header) {
++ put_short(s, (ush)len);
++ put_short(s, (ush)~len);
++#ifdef DEBUG
++ s->bits_sent += 2*16;
++#endif
++ }
++#ifdef DEBUG
++ s->bits_sent += (ulg)len<<3;
++#endif
++ while (len--) {
++ put_byte(s, *buf++);
++ }
++}
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/zlib/trees.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,128 @@
++/* header created automatically with -DGEN_TREES_H */
++
++local const ct_data static_ltree[L_CODES+2] = {
++{{ 12},{ 8}}, {{140},{ 8}}, {{ 76},{ 8}}, {{204},{ 8}}, {{ 44},{ 8}},
++{{172},{ 8}}, {{108},{ 8}}, {{236},{ 8}}, {{ 28},{ 8}}, {{156},{ 8}},
++{{ 92},{ 8}}, {{220},{ 8}}, {{ 60},{ 8}}, {{188},{ 8}}, {{124},{ 8}},
++{{252},{ 8}}, {{ 2},{ 8}}, {{130},{ 8}}, {{ 66},{ 8}}, {{194},{ 8}},
++{{ 34},{ 8}}, {{162},{ 8}}, {{ 98},{ 8}}, {{226},{ 8}}, {{ 18},{ 8}},
++{{146},{ 8}}, {{ 82},{ 8}}, {{210},{ 8}}, {{ 50},{ 8}}, {{178},{ 8}},
++{{114},{ 8}}, {{242},{ 8}}, {{ 10},{ 8}}, {{138},{ 8}}, {{ 74},{ 8}},
++{{202},{ 8}}, {{ 42},{ 8}}, {{170},{ 8}}, {{106},{ 8}}, {{234},{ 8}},
++{{ 26},{ 8}}, {{154},{ 8}}, {{ 90},{ 8}}, {{218},{ 8}}, {{ 58},{ 8}},
++{{186},{ 8}}, {{122},{ 8}}, {{250},{ 8}}, {{ 6},{ 8}}, {{134},{ 8}},
++{{ 70},{ 8}}, {{198},{ 8}}, {{ 38},{ 8}}, {{166},{ 8}}, {{102},{ 8}},
++{{230},{ 8}}, {{ 22},{ 8}}, {{150},{ 8}}, {{ 86},{ 8}}, {{214},{ 8}},
++{{ 54},{ 8}}, {{182},{ 8}}, {{118},{ 8}}, {{246},{ 8}}, {{ 14},{ 8}},
++{{142},{ 8}}, {{ 78},{ 8}}, {{206},{ 8}}, {{ 46},{ 8}}, {{174},{ 8}},
++{{110},{ 8}}, {{238},{ 8}}, {{ 30},{ 8}}, {{158},{ 8}}, {{ 94},{ 8}},
++{{222},{ 8}}, {{ 62},{ 8}}, {{190},{ 8}}, {{126},{ 8}}, {{254},{ 8}},
++{{ 1},{ 8}}, {{129},{ 8}}, {{ 65},{ 8}}, {{193},{ 8}}, {{ 33},{ 8}},
++{{161},{ 8}}, {{ 97},{ 8}}, {{225},{ 8}}, {{ 17},{ 8}}, {{145},{ 8}},
++{{ 81},{ 8}}, {{209},{ 8}}, {{ 49},{ 8}}, {{177},{ 8}}, {{113},{ 8}},
++{{241},{ 8}}, {{ 9},{ 8}}, {{137},{ 8}}, {{ 73},{ 8}}, {{201},{ 8}},
++{{ 41},{ 8}}, {{169},{ 8}}, {{105},{ 8}}, {{233},{ 8}}, {{ 25},{ 8}},
++{{153},{ 8}}, {{ 89},{ 8}}, {{217},{ 8}}, {{ 57},{ 8}}, {{185},{ 8}},
++{{121},{ 8}}, {{249},{ 8}}, {{ 5},{ 8}}, {{133},{ 8}}, {{ 69},{ 8}},
++{{197},{ 8}}, {{ 37},{ 8}}, {{165},{ 8}}, {{101},{ 8}}, {{229},{ 8}},
++{{ 21},{ 8}}, {{149},{ 8}}, {{ 85},{ 8}}, {{213},{ 8}}, {{ 53},{ 8}},
++{{181},{ 8}}, {{117},{ 8}}, {{245},{ 8}}, {{ 13},{ 8}}, {{141},{ 8}},
++{{ 77},{ 8}}, {{205},{ 8}}, {{ 45},{ 8}}, {{173},{ 8}}, {{109},{ 8}},
++{{237},{ 8}}, {{ 29},{ 8}}, {{157},{ 8}}, {{ 93},{ 8}}, {{221},{ 8}},
++{{ 61},{ 8}}, {{189},{ 8}}, {{125},{ 8}}, {{253},{ 8}}, {{ 19},{ 9}},
++{{275},{ 9}}, {{147},{ 9}}, {{403},{ 9}}, {{ 83},{ 9}}, {{339},{ 9}},
++{{211},{ 9}}, {{467},{ 9}}, {{ 51},{ 9}}, {{307},{ 9}}, {{179},{ 9}},
++{{435},{ 9}}, {{115},{ 9}}, {{371},{ 9}}, {{243},{ 9}}, {{499},{ 9}},
++{{ 11},{ 9}}, {{267},{ 9}}, {{139},{ 9}}, {{395},{ 9}}, {{ 75},{ 9}},
++{{331},{ 9}}, {{203},{ 9}}, {{459},{ 9}}, {{ 43},{ 9}}, {{299},{ 9}},
++{{171},{ 9}}, {{427},{ 9}}, {{107},{ 9}}, {{363},{ 9}}, {{235},{ 9}},
++{{491},{ 9}}, {{ 27},{ 9}}, {{283},{ 9}}, {{155},{ 9}}, {{411},{ 9}},
++{{ 91},{ 9}}, {{347},{ 9}}, {{219},{ 9}}, {{475},{ 9}}, {{ 59},{ 9}},
++{{315},{ 9}}, {{187},{ 9}}, {{443},{ 9}}, {{123},{ 9}}, {{379},{ 9}},
++{{251},{ 9}}, {{507},{ 9}}, {{ 7},{ 9}}, {{263},{ 9}}, {{135},{ 9}},
++{{391},{ 9}}, {{ 71},{ 9}}, {{327},{ 9}}, {{199},{ 9}}, {{455},{ 9}},
++{{ 39},{ 9}}, {{295},{ 9}}, {{167},{ 9}}, {{423},{ 9}}, {{103},{ 9}},
++{{359},{ 9}}, {{231},{ 9}}, {{487},{ 9}}, {{ 23},{ 9}}, {{279},{ 9}},
++{{151},{ 9}}, {{407},{ 9}}, {{ 87},{ 9}}, {{343},{ 9}}, {{215},{ 9}},
++{{471},{ 9}}, {{ 55},{ 9}}, {{311},{ 9}}, {{183},{ 9}}, {{439},{ 9}},
++{{119},{ 9}}, {{375},{ 9}}, {{247},{ 9}}, {{503},{ 9}}, {{ 15},{ 9}},
++{{271},{ 9}}, {{143},{ 9}}, {{399},{ 9}}, {{ 79},{ 9}}, {{335},{ 9}},
++{{207},{ 9}}, {{463},{ 9}}, {{ 47},{ 9}}, {{303},{ 9}}, {{175},{ 9}},
++{{431},{ 9}}, {{111},{ 9}}, {{367},{ 9}}, {{239},{ 9}}, {{495},{ 9}},
++{{ 31},{ 9}}, {{287},{ 9}}, {{159},{ 9}}, {{415},{ 9}}, {{ 95},{ 9}},
++{{351},{ 9}}, {{223},{ 9}}, {{479},{ 9}}, {{ 63},{ 9}}, {{319},{ 9}},
++{{191},{ 9}}, {{447},{ 9}}, {{127},{ 9}}, {{383},{ 9}}, {{255},{ 9}},
++{{511},{ 9}}, {{ 0},{ 7}}, {{ 64},{ 7}}, {{ 32},{ 7}}, {{ 96},{ 7}},
++{{ 16},{ 7}}, {{ 80},{ 7}}, {{ 48},{ 7}}, {{112},{ 7}}, {{ 8},{ 7}},
++{{ 72},{ 7}}, {{ 40},{ 7}}, {{104},{ 7}}, {{ 24},{ 7}}, {{ 88},{ 7}},
++{{ 56},{ 7}}, {{120},{ 7}}, {{ 4},{ 7}}, {{ 68},{ 7}}, {{ 36},{ 7}},
++{{100},{ 7}}, {{ 20},{ 7}}, {{ 84},{ 7}}, {{ 52},{ 7}}, {{116},{ 7}},
++{{ 3},{ 8}}, {{131},{ 8}}, {{ 67},{ 8}}, {{195},{ 8}}, {{ 35},{ 8}},
++{{163},{ 8}}, {{ 99},{ 8}}, {{227},{ 8}}
++};
++
++local const ct_data static_dtree[D_CODES] = {
++{{ 0},{ 5}}, {{16},{ 5}}, {{ 8},{ 5}}, {{24},{ 5}}, {{ 4},{ 5}},
++{{20},{ 5}}, {{12},{ 5}}, {{28},{ 5}}, {{ 2},{ 5}}, {{18},{ 5}},
++{{10},{ 5}}, {{26},{ 5}}, {{ 6},{ 5}}, {{22},{ 5}}, {{14},{ 5}},
++{{30},{ 5}}, {{ 1},{ 5}}, {{17},{ 5}}, {{ 9},{ 5}}, {{25},{ 5}},
++{{ 5},{ 5}}, {{21},{ 5}}, {{13},{ 5}}, {{29},{ 5}}, {{ 3},{ 5}},
++{{19},{ 5}}, {{11},{ 5}}, {{27},{ 5}}, {{ 7},{ 5}}, {{23},{ 5}}
++};
++
++const uch _dist_code[DIST_CODE_LEN] = {
++ 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8,
++ 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10,
++10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
++11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
++12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13,
++13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
++13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
++14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
++14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
++14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15,
++15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
++15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
++15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 16, 17,
++18, 18, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22,
++23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
++24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
++26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
++26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27,
++27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
++27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
++28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
++28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
++28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
++29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
++29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
++29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29
++};
++
++const uch _length_code[MAX_MATCH-MIN_MATCH+1]= {
++ 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, 12,
++13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16,
++17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19,
++19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
++21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22,
++22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23,
++23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
++24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
++25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
++25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26,
++26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
++26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
++27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28
++};
++
++local const int base_length[LENGTH_CODES] = {
++0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56,
++64, 80, 96, 112, 128, 160, 192, 224, 0
++};
++
++local const int base_dist[D_CODES] = {
++ 0, 1, 2, 3, 4, 6, 8, 12, 16, 24,
++ 32, 48, 64, 96, 128, 192, 256, 384, 512, 768,
++ 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576
++};
++
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/zlib/zconf.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,309 @@
++/* zconf.h -- configuration of the zlib compression library
++ * Copyright (C) 1995-2002 Jean-loup Gailly.
++ * For conditions of distribution and use, see copyright notice in zlib.h
++ */
++
++/* @(#) $Id$ */
++
++#ifndef _ZCONF_H
++#define _ZCONF_H
++
++/*
++ * If you *really* need a unique prefix for all types and library functions,
++ * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it.
++ */
++#ifdef IPCOMP_PREFIX
++# define deflateInit_ ipcomp_deflateInit_
++# define deflate ipcomp_deflate
++# define deflateEnd ipcomp_deflateEnd
++# define inflateInit_ ipcomp_inflateInit_
++# define inflate ipcomp_inflate
++# define inflateEnd ipcomp_inflateEnd
++# define deflateInit2_ ipcomp_deflateInit2_
++# define deflateSetDictionary ipcomp_deflateSetDictionary
++# define deflateCopy ipcomp_deflateCopy
++# define deflateReset ipcomp_deflateReset
++# define deflateParams ipcomp_deflateParams
++# define inflateInit2_ ipcomp_inflateInit2_
++# define inflateSetDictionary ipcomp_inflateSetDictionary
++# define inflateSync ipcomp_inflateSync
++# define inflateSyncPoint ipcomp_inflateSyncPoint
++# define inflateReset ipcomp_inflateReset
++# define compress ipcomp_compress
++# define compress2 ipcomp_compress2
++# define uncompress ipcomp_uncompress
++# define adler32 ipcomp_adler32
++# define crc32 ipcomp_crc32
++# define get_crc_table ipcomp_get_crc_table
++/* SSS: these also need to be prefixed to avoid clash with ppp_deflate and ext2compression */
++# define inflate_blocks ipcomp_deflate_blocks
++# define inflate_blocks_free ipcomp_deflate_blocks_free
++# define inflate_blocks_new ipcomp_inflate_blocks_new
++# define inflate_blocks_reset ipcomp_inflate_blocks_reset
++# define inflate_blocks_sync_point ipcomp_inflate_blocks_sync_point
++# define inflate_set_dictionary ipcomp_inflate_set_dictionary
++# define inflate_codes ipcomp_inflate_codes
++# define inflate_codes_free ipcomp_inflate_codes_free
++# define inflate_codes_new ipcomp_inflate_codes_new
++# define inflate_fast ipcomp_inflate_fast
++# define inflate_trees_bits ipcomp_inflate_trees_bits
++# define inflate_trees_dynamic ipcomp_inflate_trees_dynamic
++# define inflate_trees_fixed ipcomp_inflate_trees_fixed
++# define inflate_flush ipcomp_inflate_flush
++# define inflate_mask ipcomp_inflate_mask
++# define _dist_code _ipcomp_dist_code
++# define _length_code _ipcomp_length_code
++# define _tr_align _ipcomp_tr_align
++# define _tr_flush_block _ipcomp_tr_flush_block
++# define _tr_init _ipcomp_tr_init
++# define _tr_stored_block _ipcomp_tr_stored_block
++# define _tr_tally _ipcomp_tr_tally
++# define zError ipcomp_zError
++# define z_errmsg ipcomp_z_errmsg
++# define zlibVersion ipcomp_zlibVersion
++# define match_init ipcomp_match_init
++# define longest_match ipcomp_longest_match
++#endif
++
++#ifdef Z_PREFIX
++# define Byte z_Byte
++# define uInt z_uInt
++# define uLong z_uLong
++# define Bytef z_Bytef
++# define charf z_charf
++# define intf z_intf
++# define uIntf z_uIntf
++# define uLongf z_uLongf
++# define voidpf z_voidpf
++# define voidp z_voidp
++#endif
++
++#if (defined(_WIN32) || defined(__WIN32__)) && !defined(WIN32)
++# define WIN32
++#endif
++#if defined(__GNUC__) || defined(WIN32) || defined(__386__) || defined(i386)
++# ifndef __32BIT__
++# define __32BIT__
++# endif
++#endif
++#if defined(__MSDOS__) && !defined(MSDOS)
++# define MSDOS
++#endif
++
++/*
++ * Compile with -DMAXSEG_64K if the alloc function cannot allocate more
++ * than 64k bytes at a time (needed on systems with 16-bit int).
++ */
++#if defined(MSDOS) && !defined(__32BIT__)
++# define MAXSEG_64K
++#endif
++#ifdef MSDOS
++# define UNALIGNED_OK
++#endif
++
++#if (defined(MSDOS) || defined(_WINDOWS) || defined(WIN32)) && !defined(STDC)
++# define STDC
++#endif
++#if defined(__STDC__) || defined(__cplusplus) || defined(__OS2__)
++# ifndef STDC
++# define STDC
++# endif
++#endif
++
++#ifndef STDC
++# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */
++# define const
++# endif
++#endif
++
++/* Some Mac compilers merge all .h files incorrectly: */
++#if defined(__MWERKS__) || defined(applec) ||defined(THINK_C) ||defined(__SC__)
++# define NO_DUMMY_DECL
++#endif
++
++/* Old Borland C incorrectly complains about missing returns: */
++#if defined(__BORLANDC__) && (__BORLANDC__ < 0x500)
++# define NEED_DUMMY_RETURN
++#endif
++
++
++/* Maximum value for memLevel in deflateInit2 */
++#ifndef MAX_MEM_LEVEL
++# ifdef MAXSEG_64K
++# define MAX_MEM_LEVEL 8
++# else
++# define MAX_MEM_LEVEL 9
++# endif
++#endif
++
++/* Maximum value for windowBits in deflateInit2 and inflateInit2.
++ * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files
++ * created by gzip. (Files created by minigzip can still be extracted by
++ * gzip.)
++ */
++#ifndef MAX_WBITS
++# define MAX_WBITS 15 /* 32K LZ77 window */
++#endif
++
++/* The memory requirements for deflate are (in bytes):
++ (1 << (windowBits+2)) + (1 << (memLevel+9))
++ that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values)
++ plus a few kilobytes for small objects. For example, if you want to reduce
++ the default memory requirements from 256K to 128K, compile with
++ make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7"
++ Of course this will generally degrade compression (there's no free lunch).
++
++ The memory requirements for inflate are (in bytes) 1 << windowBits
++ that is, 32K for windowBits=15 (default value) plus a few kilobytes
++ for small objects.
++*/
++
++ /* Type declarations */
++
++#ifndef OF /* function prototypes */
++# ifdef STDC
++# define OF(args) args
++# else
++# define OF(args) ()
++# endif
++#endif
++
++/* The following definitions for FAR are needed only for MSDOS mixed
++ * model programming (small or medium model with some far allocations).
++ * This was tested only with MSC; for other MSDOS compilers you may have
++ * to define NO_MEMCPY in zutil.h. If you don't need the mixed model,
++ * just define FAR to be empty.
++ */
++#if (defined(M_I86SM) || defined(M_I86MM)) && !defined(__32BIT__)
++ /* MSC small or medium model */
++# define SMALL_MEDIUM
++# ifdef _MSC_VER
++# define FAR _far
++# else
++# define FAR far
++# endif
++#endif
++#if defined(__BORLANDC__) && (defined(__SMALL__) || defined(__MEDIUM__))
++# ifndef __32BIT__
++# define SMALL_MEDIUM
++# define FAR _far
++# endif
++#endif
++
++/* Compile with -DZLIB_DLL for Windows DLL support */
++#if defined(ZLIB_DLL)
++# if defined(_WINDOWS) || defined(WINDOWS)
++# ifdef FAR
++# undef FAR
++# endif
++# include <windows.h>
++# define ZEXPORT WINAPI
++# ifdef WIN32
++# define ZEXPORTVA WINAPIV
++# else
++# define ZEXPORTVA FAR _cdecl _export
++# endif
++# endif
++# if defined (__BORLANDC__)
++# if (__BORLANDC__ >= 0x0500) && defined (WIN32)
++# include <windows.h>
++# define ZEXPORT __declspec(dllexport) WINAPI
++# define ZEXPORTRVA __declspec(dllexport) WINAPIV
++# else
++# if defined (_Windows) && defined (__DLL__)
++# define ZEXPORT _export
++# define ZEXPORTVA _export
++# endif
++# endif
++# endif
++#endif
++
++#if defined (__BEOS__)
++# if defined (ZLIB_DLL)
++# define ZEXTERN extern __declspec(dllexport)
++# else
++# define ZEXTERN extern __declspec(dllimport)
++# endif
++#endif
++
++#ifndef ZEXPORT
++# define ZEXPORT
++#endif
++#ifndef ZEXPORTVA
++# define ZEXPORTVA
++#endif
++#ifndef ZEXTERN
++# define ZEXTERN extern
++#endif
++
++#ifndef FAR
++# define FAR
++#endif
++
++#if !defined(MACOS) && !defined(TARGET_OS_MAC)
++typedef unsigned char Byte; /* 8 bits */
++#endif
++typedef unsigned int uInt; /* 16 bits or more */
++typedef unsigned long uLong; /* 32 bits or more */
++
++#ifdef SMALL_MEDIUM
++ /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */
++# define Bytef Byte FAR
++#else
++ typedef Byte FAR Bytef;
++#endif
++typedef char FAR charf;
++typedef int FAR intf;
++typedef uInt FAR uIntf;
++typedef uLong FAR uLongf;
++
++#ifdef STDC
++ typedef void FAR *voidpf;
++ typedef void *voidp;
++#else
++ typedef Byte FAR *voidpf;
++ typedef Byte *voidp;
++#endif
++
++#ifdef HAVE_UNISTD_H
++# include <sys/types.h> /* for off_t */
++# include <unistd.h> /* for SEEK_* and off_t */
++# define z_off_t off_t
++#endif
++#ifndef SEEK_SET
++# define SEEK_SET 0 /* Seek from beginning of file. */
++# define SEEK_CUR 1 /* Seek from current position. */
++# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */
++#endif
++#ifndef z_off_t
++# define z_off_t long
++#endif
++
++/* MVS linker does not support external names larger than 8 bytes */
++#if defined(__MVS__)
++# pragma map(deflateInit_,"DEIN")
++# pragma map(deflateInit2_,"DEIN2")
++# pragma map(deflateEnd,"DEEND")
++# pragma map(inflateInit_,"ININ")
++# pragma map(inflateInit2_,"ININ2")
++# pragma map(inflateEnd,"INEND")
++# pragma map(inflateSync,"INSY")
++# pragma map(inflateSetDictionary,"INSEDI")
++# pragma map(inflate_blocks,"INBL")
++# pragma map(inflate_blocks_new,"INBLNE")
++# pragma map(inflate_blocks_free,"INBLFR")
++# pragma map(inflate_blocks_reset,"INBLRE")
++# pragma map(inflate_codes_free,"INCOFR")
++# pragma map(inflate_codes,"INCO")
++# pragma map(inflate_fast,"INFA")
++# pragma map(inflate_flush,"INFLU")
++# pragma map(inflate_mask,"INMA")
++# pragma map(inflate_set_dictionary,"INSEDI2")
++# pragma map(ipcomp_inflate_copyright,"INCOPY")
++# pragma map(inflate_trees_bits,"INTRBI")
++# pragma map(inflate_trees_dynamic,"INTRDY")
++# pragma map(inflate_trees_fixed,"INTRFI")
++# pragma map(inflate_trees_free,"INTRFR")
++#endif
++
++#endif /* _ZCONF_H */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/zlib/zutil.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,227 @@
++/* zutil.c -- target dependent utility functions for the compression library
++ * Copyright (C) 1995-2002 Jean-loup Gailly.
++ * For conditions of distribution and use, see copyright notice in zlib.h
++ */
++
++/* @(#) $Id$ */
++
++#include <zlib/zutil.h>
++
++#define MY_ZCALLOC
++
++struct internal_state {int dummy;}; /* for buggy compilers */
++
++#ifndef STDC
++extern void exit OF((int));
++#endif
++
++const char *z_errmsg[10] = {
++"need dictionary", /* Z_NEED_DICT 2 */
++"stream end", /* Z_STREAM_END 1 */
++"", /* Z_OK 0 */
++"file error", /* Z_ERRNO (-1) */
++"stream error", /* Z_STREAM_ERROR (-2) */
++"data error", /* Z_DATA_ERROR (-3) */
++"insufficient memory", /* Z_MEM_ERROR (-4) */
++"buffer error", /* Z_BUF_ERROR (-5) */
++"incompatible version",/* Z_VERSION_ERROR (-6) */
++""};
++
++
++const char * ZEXPORT zlibVersion()
++{
++ return ZLIB_VERSION;
++}
++
++#ifdef DEBUG
++
++# ifndef verbose
++# define verbose 0
++# endif
++int z_verbose = verbose;
++
++void z_error (m)
++ char *m;
++{
++ fprintf(stderr, "%s\n", m);
++ exit(1);
++}
++#endif
++
++/* exported to allow conversion of error code to string for compress() and
++ * uncompress()
++ */
++const char * ZEXPORT zError(err)
++ int err;
++{
++ return ERR_MSG(err);
++}
++
++
++#ifndef HAVE_MEMCPY
++
++void zmemcpy(dest, source, len)
++ Bytef* dest;
++ const Bytef* source;
++ uInt len;
++{
++ if (len == 0) return;
++ do {
++ *dest++ = *source++; /* ??? to be unrolled */
++ } while (--len != 0);
++}
++
++int zmemcmp(s1, s2, len)
++ const Bytef* s1;
++ const Bytef* s2;
++ uInt len;
++{
++ uInt j;
++
++ for (j = 0; j < len; j++) {
++ if (s1[j] != s2[j]) return 2*(s1[j] > s2[j])-1;
++ }
++ return 0;
++}
++
++void zmemzero(dest, len)
++ Bytef* dest;
++ uInt len;
++{
++ if (len == 0) return;
++ do {
++ *dest++ = 0; /* ??? to be unrolled */
++ } while (--len != 0);
++}
++#endif
++
++#ifdef __TURBOC__
++#if (defined( __BORLANDC__) || !defined(SMALL_MEDIUM)) && !defined(__32BIT__)
++/* Small and medium model in Turbo C are for now limited to near allocation
++ * with reduced MAX_WBITS and MAX_MEM_LEVEL
++ */
++# define MY_ZCALLOC
++
++/* Turbo C malloc() does not allow dynamic allocation of 64K bytes
++ * and farmalloc(64K) returns a pointer with an offset of 8, so we
++ * must fix the pointer. Warning: the pointer must be put back to its
++ * original form in order to free it, use zcfree().
++ */
++
++#define MAX_PTR 10
++/* 10*64K = 640K */
++
++local int next_ptr = 0;
++
++typedef struct ptr_table_s {
++ voidpf org_ptr;
++ voidpf new_ptr;
++} ptr_table;
++
++local ptr_table table[MAX_PTR];
++/* This table is used to remember the original form of pointers
++ * to large buffers (64K). Such pointers are normalized with a zero offset.
++ * Since MSDOS is not a preemptive multitasking OS, this table is not
++ * protected from concurrent access. This hack doesn't work anyway on
++ * a protected system like OS/2. Use Microsoft C instead.
++ */
++
++voidpf zcalloc (voidpf opaque, unsigned items, unsigned size)
++{
++ voidpf buf = opaque; /* just to make some compilers happy */
++ ulg bsize = (ulg)items*size;
++
++ /* If we allocate less than 65520 bytes, we assume that farmalloc
++ * will return a usable pointer which doesn't have to be normalized.
++ */
++ if (bsize < 65520L) {
++ buf = farmalloc(bsize);
++ if (*(ush*)&buf != 0) return buf;
++ } else {
++ buf = farmalloc(bsize + 16L);
++ }
++ if (buf == NULL || next_ptr >= MAX_PTR) return NULL;
++ table[next_ptr].org_ptr = buf;
++
++ /* Normalize the pointer to seg:0 */
++ *((ush*)&buf+1) += ((ush)((uch*)buf-0) + 15) >> 4;
++ *(ush*)&buf = 0;
++ table[next_ptr++].new_ptr = buf;
++ return buf;
++}
++
++void zcfree (voidpf opaque, voidpf ptr)
++{
++ int n;
++ if (*(ush*)&ptr != 0) { /* object < 64K */
++ farfree(ptr);
++ return;
++ }
++ /* Find the original pointer */
++ for (n = 0; n < next_ptr; n++) {
++ if (ptr != table[n].new_ptr) continue;
++
++ farfree(table[n].org_ptr);
++ while (++n < next_ptr) {
++ table[n-1] = table[n];
++ }
++ next_ptr--;
++ return;
++ }
++ ptr = opaque; /* just to make some compilers happy */
++ Assert(0, "zcfree: ptr not found");
++}
++#endif
++#endif /* __TURBOC__ */
++
++
++#if defined(M_I86) && !defined(__32BIT__)
++/* Microsoft C in 16-bit mode */
++
++# define MY_ZCALLOC
++
++#if (!defined(_MSC_VER) || (_MSC_VER <= 600))
++# define _halloc halloc
++# define _hfree hfree
++#endif
++
++voidpf zcalloc (voidpf opaque, unsigned items, unsigned size)
++{
++ if (opaque) opaque = 0; /* to make compiler happy */
++ return _halloc((long)items, size);
++}
++
++void zcfree (voidpf opaque, voidpf ptr)
++{
++ if (opaque) opaque = 0; /* to make compiler happy */
++ _hfree(ptr);
++}
++
++#endif /* MSC */
++
++
++#ifndef MY_ZCALLOC /* Any system without a special alloc function */
++
++#ifndef STDC
++extern voidp calloc OF((uInt items, uInt size));
++extern void free OF((voidpf ptr));
++#endif
++
++voidpf zcalloc (opaque, items, size)
++ voidpf opaque;
++ unsigned items;
++ unsigned size;
++{
++ if (opaque) items += size - size; /* make compiler happy */
++ return (voidpf)calloc(items, size);
++}
++
++void zcfree (opaque, ptr)
++ voidpf opaque;
++ voidpf ptr;
++{
++ free(ptr);
++ if (opaque) return; /* make compiler happy */
++}
++
++#endif /* MY_ZCALLOC */
+--- linux/net/Config.in.orig Fri Feb 9 14:34:13 2001
++++ linux/net/Config.in Thu Feb 22 19:40:08 2001
+@@ -88,4 +88,9 @@
+ #bool 'Network code profiler' CONFIG_NET_PROFILE
+ endmenu
+
++tristate 'IP Security Protocol (FreeS/WAN IPSEC)' CONFIG_IPSEC
++if [ "$CONFIG_IPSEC" != "n" ]; then
++ source net/ipsec/Config.in
++fi
++
+ endmenu
+RCSID $Id$
+--- linux/net/Makefile.preipsec Mon Jun 11 22:15:27 2001
++++ linux/net/Makefile Tue Nov 6 21:07:43 2001
+@@ -17,6 +17,7 @@
+ subdir-$(CONFIG_NET) += 802 sched
+ subdir-$(CONFIG_INET) += ipv4
+ subdir-$(CONFIG_NETFILTER) += ipv4/netfilter
++subdir-$(CONFIG_IPSEC) += ipsec
+ subdir-$(CONFIG_UNIX) += unix
+ subdir-$(CONFIG_IPV6) += ipv6
+
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/net/ipsec/Config.in Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,76 @@
++#
++# IPSEC configuration
++# Copyright (C) 1998, 1999, 2000,2001 Richard Guy Briggs.
++#
++# This program is free software; you can redistribute it and/or modify it
++# under the terms of the GNU General Public License as published by the
++# Free Software Foundation; either version 2 of the License, or (at your
++# option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++#
++# This program is distributed in the hope that it will be useful, but
++# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++# for more details.
++#
++# RCSID $Id$
++
++comment 'IPSec options (Openswan)'
++
++bool ' IPSEC: IP-in-IP encapsulation (tunnel mode)' CONFIG_IPSEC_IPIP
++
++bool ' IPSEC: Authentication Header' CONFIG_IPSEC_AH
++if [ "$CONFIG_IPSEC_AH" = "y" -o "$CONFIG_IPSEC_ESP" = "y" ]; then
++ bool ' HMAC-MD5 authentication algorithm' CONFIG_IPSEC_AUTH_HMAC_MD5
++ bool ' HMAC-SHA1 authentication algorithm' CONFIG_IPSEC_AUTH_HMAC_SHA1
++fi
++
++bool ' IPSEC: Encapsulating Security Payload' CONFIG_IPSEC_ESP
++if [ "$CONFIG_IPSEC_ESP" = "y" ]; then
++ bool ' 3DES encryption algorithm' CONFIG_IPSEC_ENC_3DES
++ bool ' AES encryption algorithm' CONFIG_IPSEC_ENC_AES
++fi
++
++bool ' IPSEC Modular Extensions' CONFIG_IPSEC_ALG
++if [ "$CONFIG_IPSEC_ALG" != "n" ]; then
++ source net/ipsec/alg/Config.in
++fi
++
++bool ' IPSEC: IP Compression' CONFIG_IPSEC_IPCOMP
++
++bool ' IPSEC Debugging Option' CONFIG_IPSEC_DEBUG
++
++#
++#
++# $Log$
++# Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++# Turn off EOLN_NATIVE flag
++#
++# (Logical change 1.5010)
++#
++# Revision 1.30 2004/06/23 09:49:37 ken
++# Free -> Open
++#
++# Revision 1.29 2004/04/06 02:49:25 mcr
++# pullup of algo code from alg-branch.
++#
++# Revision 1.28 2004/02/03 03:12:26 mcr
++# remove NAT-traversal option from IPsec config,
++# as it should be in the kernel configuration if
++# the NAT-T patch is installed.
++#
++# Revision 1.27.2.2 2004/04/05 04:30:46 mcr
++# patches for alg-branch to compile/work with 2.x openswan
++#
++# Revision 1.27.2.1 2003/12/23 12:48:25 jjo
++# Added missing alg part to linux/net/ipsec/Config.in
++#
++# Revision 1.27 2003/12/10 01:14:27 mcr
++# NAT-traversal patches to KLIPS.
++#
++# Revision 1.26 2002/04/24 07:36:26 mcr
++# Moved from ./klips/net/ipsec/Config.in,v
++#
++# Revision 1.25 2002/02/21 19:55:12 mcr
++# removed all traces of IPSEC_CONFIG_REGRESS because it
++# screwed up 2.2's "make menuconfig" scripts.
++#
++# Revision 1.24 2002/01/28 20:24:31 mcr
++# commented out IPSEC_REGRESS option from user visible config.
++#
++#
++
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/net/ipsec/Makefile Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,337 @@
++# Makefile for KLIPS kernel code as a module
++# Copyright (C) 1998, 1999, 2000,2001 Richard Guy Briggs.
++# Copyright (C) 2002 Michael Richardson <mcr@freeswan.org>
++#
++# This program is free software; you can redistribute it and/or modify it
++# under the terms of the GNU General Public License as published by the
++# Free Software Foundation; either version 2 of the License, or (at your
++# option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++#
++# This program is distributed in the hope that it will be useful, but
++# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++# for more details.
++#
++# RCSID $Id$
++#
++# Note! Dependencies are done automagically by 'make dep', which also
++# removes any old dependencies. DON'T put your own dependencies here
++# unless it's something special (ie not a .c file).
++#
++
++ifeq ($(strip $(KLIPSMODULE)),)
++OPENSWANSRCDIR=.
++else
++OPENSWANSRCDIR=../../..
++endif
++-include ${OPENSWANSRCDIR}/Makefile.ver
++
++ifeq ($(strip $(KLIPS_TOP)),)
++KLIPS_TOP=../..
++endif
++
++ifneq ($(strip $(KLIPSMODULE)),)
++
++ifndef TOPDIR
++TOPDIR:=/usr/src/linux
++endif
++export TOPDIR
++
++endif
++
++#
++# This magic from User-Mode-Linux list. It gets list of -I options, as
++# UML needs some extra, that varry by revision.
++#
++KERNEL_CFLAGS= $(shell $(MAKE) -C $(TOPDIR) --no-print-directory -s -f Makefile ARCH=$(ARCH) MAKEFLAGS= script SCRIPT='@echo $$(CFLAGS)' )
++
++MODULE_CFLAGS= $(shell $(MAKE) -C $(TOPDIR) --no-print-directory -s -f Makefile ARCH=$(ARCH) MAKEFLAGS= script SCRIPT='@echo $$(MODFLAGS)' )
++
++subdir- :=
++subdir-n :=
++subdir-y :=
++subdir-m :=
++
++
++MOD_DESTDIR:=net/ipsec
++
++export TOPDIR
++
++all: ipsec.o
++
++foo:
++ echo KERNEL: ${KERNEL_CFLAGS}
++ echo MODULE: ${MODULE_CFLAGS}
++
++ipsec.o: foo
++
++O_TARGET := ipsec.o
++obj-y := ipsec_init.o ipsec_sa.o ipsec_radij.o radij.o
++obj-y += ipsec_life.o ipsec_proc.o
++obj-y += ipsec_tunnel.o ipsec_xmit.o ipsec_rcv.o ipsec_ipip.o
++obj-y += sysctl_net_ipsec.o
++obj-y += pfkey_v2.o pfkey_v2_parser.o pfkey_v2_ext_process.o
++obj-y += version.o
++obj-$(CONFIG_IPSEC_AH) += ipsec_ah.o
++obj-$(CONFIG_IPSEC_ESP) += ipsec_esp.o
++obj-$(CONFIG_IPSEC_IPCOMP)+= ipsec_ipcomp.o
++
++CFLAGS_ipsec_alg.o += -DEXPORT_SYMTAB
++obj-$(CONFIG_IPSEC_ALG) += ipsec_alg.o
++obj-$(CONFIG_IPSEC_ENC_AES) += ipsec_alg_aes.o
++obj-$(CONFIG_IPSEC_ENC_CRYPTOAPI) += ipsec_alg_cryptoapi.o
++
++export-objs += ipsec_alg.o
++
++
++LIBDESDIR=${KLIPS_TOP}/crypto/ciphers/des
++VPATH+= ${LIBDESDIR}
++
++include ${LIBDESDIR}/Makefile.objs
++
++LIBFREESWANDIR=${KLIPS_TOP}/lib/libfreeswan
++VPATH+=${LIBFREESWANDIR}
++
++include ${LIBFREESWANDIR}/Makefile.objs
++
++# IPcomp stuff
++obj-$(CONFIG_IPSEC_IPCOMP) += ipcomp.o
++
++LIBZLIBSRCDIR=${KLIPS_TOP}/lib/zlib
++VPATH+=${LIBZLIBSRCDIR}
++
++LIBAESDIR=$(KLIPS_TOP)/crypto/ciphers/aes
++VPATH+=${LIBAESDIR}
++include ${LIBAESDIR}/Makefile.objs
++
++# CFLAGS='$(CFLAGS)' \
++# MODULE_CFLAGS='$(MODULE_CFLAGS)' KERNEL_CFLAGS='$(KERNEL_CFLAGS)' \
++#
++include ${LIBZLIBSRCDIR}/Makefile.objs
++
++export-objs := radij.o
++
++EXTRA_CFLAGS += $(ALGO_FLAGS)
++
++
++# include file with .h-style macros that would otherwise be created by
++# config. Must occur before other includes.
++ifneq ($(strip $(MODULE_DEF_INCLUDE)),)
++EXTRA_CFLAGS += -include ${MODULE_DEF_INCLUDE}
++endif
++
++# 'override CFLAGS' should really be 'EXTRA_CFLAGS'
++#EXTRA_CFLAGS += -nostdinc
++EXTRA_CFLAGS += -I${KLIPS_TOP}/include
++
++EXTRA_CFLAGS += -I${TOPDIR}/include
++EXTRA_CFLAGS += -I${LIBZLIBSRCDIR}
++
++ifeq ($(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION),2.4.2-2)
++EXTRA_CFLAGS += -DREDHAT_BOGOSITY
++endif
++
++ifeq ($(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION),2.4.3-12)
++EXTRA_CFLAGS += -DREDHAT_BOGOSITY
++endif
++
++
++#ifeq ($(CONFIG_IPSEC_DEBUG),y)
++#EXTRA_CFLAGS += -g
++#endif
++
++#ifeq ($(CONFIG_IPSEC_ALG), y)
++EXTRA_CFLAGS += -DCONFIG_IPSEC_ALG
++#endif
++# MOST of these flags are in KERNEL_CFLAGS already!
++
++EXTRA_CFLAGS += $(KLIPSCOMPILE)
++EXTRA_CFLAGS += -Wall
++#EXTRA_CFLAGS += -Werror
++#EXTRA_CFLAGS += -Wconversion
++#EXTRA_CFLAGS += -Wmissing-prototypes
++# cannot use both -Wpointer-arith and -Werror with CONFIG_HIGHMEM
++# include/linux/highmem.h has an inline function definition that uses void* arithmentic.
++ifeq ($(CONFIG_NOHIGHMEM),y)
++EXTRA_CFLAGS += -Wpointer-arith
++endif
++#EXTRA_CFLAGS += -Wcast-qual
++#EXTRA_CFLAGS += -Wmissing-declarations
++#EXTRA_CFLAGS += -Wstrict-prototypes
++#EXTRA_CFLAGS += -pedantic
++#EXTRA_CFLAGS += -O3
++#EXTRA_CFLAGS += -W
++#EXTRA_CFLAGS += -Wwrite-strings
++#EXTRA_CFLAGS += -Wbad-function-cast
++
++ifneq ($(strip $(KLIPSMODULE)),)
++# for when we aren't building in the kernel tree
++EXTRA_CFLAGS += -DARCH=${ARCH}
++EXTRA_CFLAGS += -DMODVERSIONS
++EXTRA_CFLAGS += -include ${TOPDIR}/include/linux/modversions.h
++EXTRA_CFLAGS += ${MODULE_CFLAGS}
++endif
++
++EXTRA_CFLAGS += ${KERNEL_CFLAGS}
++
++#EXTRA_CFLAGS += -DRJ_DEBUG -DRJ_DEBUG2
++
++
++# GCC 3.2 (and we presume any other 3.x) wants -falign-functions
++# in place of the traditional -malign-functions. Getting this
++# wrong leads to a warning, which is fatal due to our use of -Werror.
++ifeq ($(patsubst 3.%,3,$(shell $(CC) -dumpversion)),3)
++override CFLAGS:=$(subst -malign-functions=,-falign-functions=,$(CFLAGS))
++endif
++
++
++obj-$(CONFIG_IPSEC_AUTH_HMAC_MD5) += ipsec_md5c.o
++obj-$(CONFIG_IPSEC_AUTH_HMAC_SHA1) += ipsec_sha1.o
++
++###
++### Pre Rules.make
++###
++# undo O_TARGET, obj-y if no static
++ifneq ($(CONFIG_IPSEC),y)
++O_TARGET :=
++ipsec_obj-y := $(obj-y)
++obj-y :=
++subdir-y :=
++endif
++
++# Define obj-m if modular ipsec
++ifeq ($(CONFIG_IPSEC),m)
++obj-m += ipsec.o
++endif
++
++
++# These rules translate from new to old makefile rules
++# Translate to Rules.make lists.
++multi-used := $(filter $(list-multi), $(obj-y) $(obj-m))
++multi-objs := $(foreach m, $(multi-used), $($(basename $(m))-objs))
++active-objs := $(sort $(multi-objs) $(obj-y) $(obj-m))
++O_OBJS := $(obj-y)
++M_OBJS := $(obj-m)
++MIX_OBJS := $(filter $(export-objs), $(active-objs))
++OX_OBJS := $(export-objs)
++SUB_DIRS := $(subdir-y)
++ALL_SUB_DIRS := $(subdir-y) $(subdir-m)
++MOD_SUB_DIRS := $(subdir-m)
++
++# dunno why, but some 2.2 setups may need explicit -DEXPORT_SYMTAB
++# uncomment next line if ipsec_alg.c compilation fails with
++# "parse error before `EXPORT_SYMTAB_not_defined'" --Juanjo
++
++include $(TOPDIR)/Rules.make
++
++###
++### Post Rules.make
++###
++# for modular ipsec, no O_TARGET defined => define ipsec.o creation rules
++ifeq ($(CONFIG_IPSEC),m)
++ipsec.o : $(ipsec_obj-y)
++ rm -f $@
++ $(LD) $(LD_EXTRAFLAGS) -r $(ipsec_obj-y) -o $@
++endif
++
++$(ipsec_obj-y) $(obj-y) $(obj-m): $(TOPDIR)/include/linux/config.h $(TOPDIR)/include/linux/autoconf.h
++
++#$(obj-y) $(obj-m): $(TOPDIR)/include/linux/config.h $(TOPDIR)/include/linux/autoconf.h
++
++USE_STANDARD_AS_RULE=true
++
++clean:
++ $(MAKE) -C alg clean
++ -rm -f *.o
++ -rm -f .*.o.flags
++ -rm -f version.c
++
++tags TAGS: *.c *.h libfreeswan/*.c libfreeswan/*.h
++ etags *.c ../../include/*.h ../../include/freeswan/*.h
++ ctags *.c ../../include/*.h ../../include/freeswan/*.h
++
++tar:
++ tar -cvf /dev/f1 .
++
++#
++# $Log$
++# Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++# Turn off EOLN_NATIVE flag
++#
++# (Logical change 1.5010)
++#
++# Revision 1.72 2004/06/22 14:44:07 ken
++# Merge nice version of Nate's CryptoAPI patch
++#
++# Revision 1.71 2004/04/18 03:04:21 mcr
++# removed duplicate version.o reference.
++#
++# Revision 1.70 2004/04/14 05:09:39 ken
++# We need to link version.o
++#
++# Revision 1.69 2004/04/12 04:02:39 ken
++# version.o no longer exists
++#
++# Revision 1.68 2004/04/11 17:08:41 mcr
++# moved PASSTHROUGH definitions to openswan.h
++# requirement for internal.h removed.
++# version.c is now generated by patch at patch-time.
++#
++# Revision 1.67 2004/04/06 02:49:25 mcr
++# pullup of algo code from alg-branch.
++#
++# Revision 1.66 2004/04/03 19:44:41 ken
++# FREESWANSRCDIR -> OPENSWANSRCDIR (patch by folken)
++#
++# Revision 1.65 2004/02/09 16:22:07 paul
++# Added -f to rm version.c in clean target to prevent bogus error
++#
++# Revision 1.64 2003/12/22 19:40:57 mcr
++# NAT-T patches 0.6c.
++#
++# Revision 1.63 2003/12/13 19:10:21 mcr
++# refactored rcv and xmit code - same as FS 2.05.
++#
++# Revision 1.62.4.2 2004/04/05 04:30:46 mcr
++# patches for alg-branch to compile/work with 2.x openswan
++#
++# Revision 1.62.4.1 2003/12/22 15:25:52 jjo
++# Merged algo-0.8.1-rc11-test1 into alg-branch
++#
++# Revision 1.62 2003/10/31 02:27:55 mcr
++# pulled up port-selector patches and sa_id elimination.
++#
++# Revision 1.61.4.1 2003/10/29 01:30:41 mcr
++# elimited "struct sa_id".
++#
++# Revision 1.61 2003/06/22 21:07:46 mcr
++# adjusted TAGS target in makefile to be useful in 2.00 source layout.
++#
++# Revision 1.60 2003/05/03 23:45:23 mcr
++# rm .o.flags and generated version.c file.
++#
++# Revision 1.59 2003/02/12 19:32:47 rgb
++# Added ipsec_xmit to the list of object files.
++#
++# Revision 1.58 2003/01/03 00:36:44 rgb
++#
++# Added emacs compile-command.
++#
++# Revision 1.57 2002/11/08 23:49:53 mcr
++# use KERNEL_CFLAGS and MODULE_CFLAGS to get proper list
++# of include directories.
++# This also eliminates some of the guesswork in the kernel
++# configuration file.
++#
++# Revision 1.56 2002/11/08 23:23:18 mcr
++# attempt to guess kernel compilation flags (i.e. list of -I)
++# by using some magic targets in the kernel makefile.
++#
++# Revision 1.55 2002/11/08 10:13:33 mcr
++# added additional include directories for module builds for 2.4.19.
++#
++# Revision 1.54 2002/10/20 06:10:30 build
++# CONFIG_NOHIGHMEM for -Wpointer-arith RPM building issues.
++#
++# (elided rest of log)
++#
++# Local Variables:
++# compile-command: "(cd ../../.. && source umlsetup.sh && make -C ${POOLSPACE} module/ipsec.o)"
++# End Variables:
++#
++
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/net/ipsec/alg/Config.alg_aes.in Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,3 @@
++if [ "$CONFIG_IPSEC_ALG" = "y" ]; then
++ tristate ' AES encryption algorithm' CONFIG_IPSEC_ALG_AES
++fi
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/net/ipsec/alg/Config.alg_cryptoapi.in Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,6 @@
++if [ "$CONFIG_IPSEC_ALG" = "y" ]; then
++ dep_tristate ' CRYPTOAPI ciphers support (needs cryptoapi patch)' CONFIG_IPSEC_ALG_CRYPTOAPI $CONFIG_CRYPTO
++ if [ "$CONFIG_IPSEC_ALG_CRYPTOAPI" != "n" ]; then
++ bool ' CRYPTOAPI proprietary ciphers ' CONFIG_IPSEC_ALG_NON_LIBRE
++ fi
++fi
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/net/ipsec/alg/Config.in Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,3 @@
++#Placeholder
++source net/ipsec/alg/Config.alg_aes.in
++source net/ipsec/alg/Config.alg_cryptoapi.in
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/net/ipsec/alg/Makefile Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,112 @@
++# Makefile,v 1.1.2.1 2003/11/21 18:12:23 jjo Exp
++ifeq ($(strip $(KLIPSMODULE)),)
++FREESWANSRCDIR=.
++else
++FREESWANSRCDIR=../../../..
++endif
++ifeq ($(strip $(KLIPS_TOP)),)
++KLIPS_TOP=../../..
++override EXTRA_CFLAGS += -I$(KLIPS_TOP)/include
++endif
++
++ifeq ($(CONFIG_IPSEC_DEBUG),y)
++override EXTRA_CFLAGS += -g
++endif
++
++# LIBCRYPTO normally comes as an argument from "parent" Makefile
++# (this applies both to FS' "make module" and eg. Linux' "make modules"
++# But make dep doest follow same evaluations, so we need this default:
++LIBCRYPTO=$(TOPDIR)/lib/libcrypto
++
++override EXTRA_CFLAGS += -I$(LIBCRYPTO)/include
++override EXTRA_CFLAGS += -Wall -Wpointer-arith -Wstrict-prototypes
++
++MOD_LIST_NAME := NET_MISC_MODULES
++
++#O_TARGET := static_init.o
++
++subdir- :=
++subdir-n :=
++subdir-y :=
++subdir-m :=
++
++obj-y := static_init.o
++
++ARCH_ASM-y :=
++ARCH_ASM-$(CONFIG_M586) := i586
++ARCH_ASM-$(CONFIG_M586TSC) := i586
++ARCH_ASM-$(CONFIG_M586MMX) := i586
++ARCH_ASM-$(CONFIG_MK6) := i586
++ARCH_ASM-$(CONFIG_M686) := i686
++ARCH_ASM-$(CONFIG_MPENTIUMIII) := i686
++ARCH_ASM-$(CONFIG_MPENTIUM4) := i686
++ARCH_ASM-$(CONFIG_MK7) := i686
++ARCH_ASM-$(CONFIG_MCRUSOE) := i586
++ARCH_ASM-$(CONFIG_MWINCHIPC6) := i586
++ARCH_ASM-$(CONFIG_MWINCHIP2) := i586
++ARCH_ASM-$(CONFIG_MWINCHIP3D) := i586
++ARCH_ASM-$(CONFIG_USERMODE) := i586
++
++ARCH_ASM :=$(ARCH_ASM-y)
++ifdef NO_ASM
++ARCH_ASM :=
++endif
++
++# The algorithm makefiles may put dependences, short-circuit them
++null:
++
++makefiles=$(filter-out %.preipsec, $(wildcard Makefile.alg_*))
++ifneq ($(makefiles),)
++#include Makefile.alg_aes
++#include Makefile.alg_aes-opt
++include $(makefiles)
++endif
++
++# These rules translate from new to old makefile rules
++# Translate to Rules.make lists.
++multi-used := $(filter $(list-multi), $(obj-y) $(obj-m))
++multi-objs := $(foreach m, $(multi-used), $($(basename $(m))-objs))
++active-objs := $(sort $(multi-objs) $(obj-y) $(obj-m))
++O_OBJS := $(obj-y)
++M_OBJS := $(obj-m)
++MIX_OBJS := $(filter $(export-objs), $(active-objs))
++#OX_OBJS := $(export-objs)
++SUB_DIRS := $(subdir-y)
++ALL_SUB_DIRS := $(subdir-y) $(subdir-m)
++MOD_SUB_DIRS := $(subdir-m)
++
++
++static_init_mod.o: $(obj-y)
++ rm -f $@
++ $(LD) $(LD_EXTRAFLAGS) $(obj-y) -r -o $@
++
++perlasm: $(LIBCRYPTO)/perlasm
++ ln -sf $? $@
++
++$(obj-y) $(obj-m): $(TOPDIR)/include/linux/config.h $(TOPDIR)/include/linux/autoconf.h $(KLIPS_TOP)/include/freeswan/ipsec_alg.h
++$(alg_obj-y) $(alg_obj-m): perlasm $(TOPDIR)/include/linux/config.h $(TOPDIR)/include/linux/autoconf.h $(KLIPS_TOP)/include/freeswan/ipsec_alg.h
++
++
++all_alg_modules: perlasm $(ALG_MODULES)
++ @echo "ALG_MODULES=$(ALG_MODULES)"
++
++
++#
++# Construct alg. init. function: call ipsec_ALGO_init() for every static algo
++# Needed when there are static algos (with static or modular ipsec.o)
++#
++static_init.c: $(TOPDIR)/include/linux/autoconf.h Makefile $(makefiles) scripts/mk-static_init.c.sh
++ @echo "Re-creating $@"
++ $(SHELL) scripts/mk-static_init.c.sh $(static_init-func-y) > $@
++
++clean:
++ @for i in $(ALG_SUBDIRS);do test -d $$i && make -C $$i clean;done;exit 0
++ @find . -type l -exec rm -f {} \;
++ -rm -f perlasm
++ -rm -rf $(ALG_SUBDIRS)
++ -rm -f *.o static_init.c
++
++ifdef TOPDIR
++include $(TOPDIR)/Rules.make
++endif
++
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/net/ipsec/alg/Makefile.alg_aes Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,23 @@
++MOD_AES := ipsec_aes.o
++
++ALG_MODULES += $(MOD_AES)
++ALG_SUBDIRS += libaes
++
++obj-$(CONFIG_IPSEC_ALG_AES) += $(MOD_AES)
++static_init-func-$(CONFIG_IPSEC_ALG_AES)+= ipsec_aes_init
++alg_obj-$(CONFIG_IPSEC_ALG_AES) += ipsec_alg_aes.o
++
++AES_OBJS := ipsec_alg_aes.o libaes/libaes.a
++
++$(MOD_AES): libaes $(AES_OBJS)
++ $(LD) $(EXTRA_LDFLAGS) -r $(AES_OBJS) -o $@
++
++libaes: $(LIBCRYPTO)/libaes
++ test -d $@ || mkdir $@ ;exit 0
++ test -d $@/asm || mkdir $@/asm;exit 0
++ cd $@ && ln -sf $?/Makefile $?/*.[chS] .
++ cd $@/asm && ln -sf $?/asm/*.S .
++
++libaes/libaes.a: libaes
++ ( cd libaes && \
++ $(MAKE) CC='$(CC)' 'ARCH_ASM=$(ARCH_ASM)' CFLAGS='$(CFLAGS) $(EXTRA_CFLAGS)' libaes.a ;)
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/net/ipsec/alg/Makefile.alg_cryptoapi Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,14 @@
++MOD_CRYPTOAPI := ipsec_cryptoapi.o
++
++ifneq ($(wildcard $(TOPDIR)/include/linux/crypto.h),)
++ALG_MODULES += $(MOD_CRYPTOAPI)
++obj-$(CONFIG_IPSEC_ALG_CRYPTOAPI) += $(MOD_CRYPTOAPI)
++static_init-func-$(CONFIG_IPSEC_ALG_CRYPTOAPI)+= ipsec_cryptoapi_init
++alg_obj-$(CONFIG_IPSEC_ALG_CRYPTOAPI) += ipsec_alg_cryptoapi.o
++else
++$(warning "Linux CryptoAPI (2.4.22+ or 2.6.x) not found, not building ipsec_cryptoapi.o")
++endif
++
++CRYPTOAPI_OBJS := ipsec_alg_cryptoapi.o
++$(MOD_CRYPTOAPI): $(CRYPTOAPI_OBJS)
++ $(LD) -r $(CRYPTOAPI_OBJS) -o $@
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/net/ipsec/alg/ipsec_alg_aes.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,253 @@
++/*
++ * ipsec_alg AES cipher stubs
++ *
++ * Author: JuanJo Ciarlante <jjo-ipsec@mendoza.gov.ar>
++ *
++ * ipsec_alg_aes.c,v 1.1.2.1 2003/11/21 18:12:23 jjo Exp
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ * Fixes by:
++ * PK: Pawel Krawczyk <kravietz@aba.krakow.pl>
++ * Fixes list:
++ * PK: make XCBC comply with latest draft (keylength)
++ *
++ */
++#include <linux/config.h>
++#include <linux/version.h>
++
++/*
++ * special case: ipsec core modular with this static algo inside:
++ * must avoid MODULE magic for this file
++ */
++#if CONFIG_IPSEC_MODULE && CONFIG_IPSEC_ALG_AES
++#undef MODULE
++#endif
++
++#include <linux/module.h>
++#include <linux/init.h>
++
++#include <linux/kernel.h> /* printk() */
++#include <linux/errno.h> /* error codes */
++#include <linux/types.h> /* size_t */
++#include <linux/string.h>
++
++/* Check if __exit is defined, if not null it */
++#ifndef __exit
++#define __exit
++#endif
++
++/* Low freeswan header coupling */
++#include "freeswan/ipsec_alg.h"
++#include "libaes/aes_cbc.h"
++
++#define CONFIG_IPSEC_ALG_AES_MAC 1
++
++#define AES_CONTEXT_T aes_context
++MODULE_AUTHOR("JuanJo Ciarlante <jjo-ipsec@mendoza.gov.ar>");
++static int debug=0;
++MODULE_PARM(debug, "i");
++static int test=0;
++MODULE_PARM(test, "i");
++static int excl=0;
++MODULE_PARM(excl, "i");
++static int keyminbits=0;
++MODULE_PARM(keyminbits, "i");
++static int keymaxbits=0;
++MODULE_PARM(keymaxbits, "i");
++
++#if CONFIG_IPSEC_ALG_AES_MAC
++#include "libaes/aes_xcbc_mac.h"
++
++/*
++ * Not IANA number yet (draft-ietf-ipsec-ciph-aes-xcbc-mac-00.txt).
++ * We use 9 for non-modular algorithm and none for modular, thus
++ * forcing user to specify one on module load. -kravietz
++ */
++#ifdef MODULE
++static int auth_id=0;
++#else
++static int auth_id=9;
++#endif
++MODULE_PARM(auth_id, "i");
++#endif
++
++#define ESP_AES 12 /* truely _constant_ :) */
++
++/* 128, 192 or 256 */
++#define ESP_AES_KEY_SZ_MIN 16 /* 128 bit secret key */
++#define ESP_AES_KEY_SZ_MAX 32 /* 256 bit secret key */
++#define ESP_AES_CBC_BLK_LEN 16 /* AES-CBC block size */
++
++/* Values according to draft-ietf-ipsec-ciph-aes-xcbc-mac-02.txt
++ * -kravietz
++ */
++#define ESP_AES_MAC_KEY_SZ 16 /* 128 bit MAC key */
++#define ESP_AES_MAC_BLK_LEN 16 /* 128 bit block */
++
++static int _aes_set_key(struct ipsec_alg_enc *alg, __u8 * key_e, const __u8 * key, size_t keysize) {
++ int ret;
++ AES_CONTEXT_T *ctx=(AES_CONTEXT_T*)key_e;
++ ret=AES_set_key(ctx, key, keysize)!=0? 0: -EINVAL;
++ if (debug > 0)
++ printk(KERN_DEBUG "klips_debug:_aes_set_key:"
++ "ret=%d key_e=%p key=%p keysize=%d\n",
++ ret, key_e, key, keysize);
++ return ret;
++}
++static int _aes_cbc_encrypt(struct ipsec_alg_enc *alg, __u8 * key_e, __u8 * in, int ilen, const __u8 * iv, int encrypt) {
++ AES_CONTEXT_T *ctx=(AES_CONTEXT_T*)key_e;
++ if (debug > 0)
++ printk(KERN_DEBUG "klips_debug:_aes_cbc_encrypt:"
++ "key_e=%p in=%p ilen=%d iv=%p encrypt=%d\n",
++ key_e, in, ilen, iv, encrypt);
++ return AES_cbc_encrypt(ctx, in, in, ilen, iv, encrypt);
++}
++#if CONFIG_IPSEC_ALG_AES_MAC
++static int _aes_mac_set_key(struct ipsec_alg_auth *alg, __u8 * key_a, const __u8 * key, int keylen) {
++ aes_context_mac *ctxm=(aes_context_mac *)key_a;
++ return AES_xcbc_mac_set_key(ctxm, key, keylen)? 0 : -EINVAL;
++}
++static int _aes_mac_hash(struct ipsec_alg_auth *alg, __u8 * key_a, const __u8 * dat, int len, __u8 * hash, int hashlen) {
++ int ret;
++ char hash_buf[16];
++ aes_context_mac *ctxm=(aes_context_mac *)key_a;
++ ret=AES_xcbc_mac_hash(ctxm, dat, len, hash_buf);
++ memcpy(hash, hash_buf, hashlen);
++ return ret;
++}
++static struct ipsec_alg_auth ipsec_alg_AES_MAC = {
++ ixt_version: IPSEC_ALG_VERSION,
++ ixt_module: THIS_MODULE,
++ ixt_refcnt: ATOMIC_INIT(0),
++ ixt_alg_type: IPSEC_ALG_TYPE_AUTH,
++ ixt_alg_id: 0,
++ ixt_name: "aes_mac",
++ ixt_blocksize: ESP_AES_MAC_BLK_LEN,
++ ixt_keyminbits: ESP_AES_MAC_KEY_SZ*8,
++ ixt_keymaxbits: ESP_AES_MAC_KEY_SZ*8,
++ ixt_a_keylen: ESP_AES_MAC_KEY_SZ,
++ ixt_a_ctx_size: sizeof(aes_context_mac),
++ ixt_a_hmac_set_key: _aes_mac_set_key,
++ ixt_a_hmac_hash:_aes_mac_hash,
++};
++#endif /* CONFIG_IPSEC_ALG_AES_MAC */
++static struct ipsec_alg_enc ipsec_alg_AES = {
++ ixt_version: IPSEC_ALG_VERSION,
++ ixt_module: THIS_MODULE,
++ ixt_refcnt: ATOMIC_INIT(0),
++ ixt_alg_type: IPSEC_ALG_TYPE_ENCRYPT,
++ ixt_alg_id: ESP_AES,
++ ixt_name: "aes",
++ ixt_blocksize: ESP_AES_CBC_BLK_LEN,
++ ixt_keyminbits: ESP_AES_KEY_SZ_MIN*8,
++ ixt_keymaxbits: ESP_AES_KEY_SZ_MAX*8,
++ ixt_e_keylen: ESP_AES_KEY_SZ_MAX,
++ ixt_e_ctx_size: sizeof(AES_CONTEXT_T),
++ ixt_e_set_key: _aes_set_key,
++ ixt_e_cbc_encrypt:_aes_cbc_encrypt,
++};
++
++IPSEC_ALG_MODULE_INIT( ipsec_aes_init )
++{
++ int ret, test_ret;
++ if (keyminbits)
++ ipsec_alg_AES.ixt_keyminbits=keyminbits;
++ if (keymaxbits) {
++ ipsec_alg_AES.ixt_keymaxbits=keymaxbits;
++ if (keymaxbits*8>ipsec_alg_AES.ixt_keymaxbits)
++ ipsec_alg_AES.ixt_e_keylen=keymaxbits*8;
++ }
++ if (excl) ipsec_alg_AES.ixt_state |= IPSEC_ALG_ST_EXCL;
++ ret=register_ipsec_alg_enc(&ipsec_alg_AES);
++ printk("ipsec_aes_init(alg_type=%d alg_id=%d name=%s): ret=%d\n",
++ ipsec_alg_AES.ixt_alg_type,
++ ipsec_alg_AES.ixt_alg_id,
++ ipsec_alg_AES.ixt_name,
++ ret);
++ if (ret==0 && test) {
++ test_ret=ipsec_alg_test(
++ ipsec_alg_AES.ixt_alg_type,
++ ipsec_alg_AES.ixt_alg_id,
++ test);
++ printk("ipsec_aes_init(alg_type=%d alg_id=%d): test_ret=%d\n",
++ ipsec_alg_AES.ixt_alg_type,
++ ipsec_alg_AES.ixt_alg_id,
++ test_ret);
++ }
++#if CONFIG_IPSEC_ALG_AES_MAC
++ if (auth_id!=0){
++ int ret;
++ ipsec_alg_AES_MAC.ixt_alg_id=auth_id;
++ ret=register_ipsec_alg_auth(&ipsec_alg_AES_MAC);
++ printk("ipsec_aes_init(alg_type=%d alg_id=%d name=%s): ret=%d\n",
++ ipsec_alg_AES_MAC.ixt_alg_type,
++ ipsec_alg_AES_MAC.ixt_alg_id,
++ ipsec_alg_AES_MAC.ixt_name,
++ ret);
++ if (ret==0 && test) {
++ test_ret=ipsec_alg_test(
++ ipsec_alg_AES_MAC.ixt_alg_type,
++ ipsec_alg_AES_MAC.ixt_alg_id,
++ test);
++ printk("ipsec_aes_init(alg_type=%d alg_id=%d): test_ret=%d\n",
++ ipsec_alg_AES_MAC.ixt_alg_type,
++ ipsec_alg_AES_MAC.ixt_alg_id,
++ test_ret);
++ }
++ } else {
++ printk(KERN_DEBUG "klips_debug: experimental ipsec_alg_AES_MAC not registered [Ok] (auth_id=%d)\n", auth_id);
++ }
++#endif /* CONFIG_IPSEC_ALG_AES_MAC */
++ return ret;
++}
++IPSEC_ALG_MODULE_EXIT( ipsec_aes_fini )
++{
++#if CONFIG_IPSEC_ALG_AES_MAC
++ if (auth_id) unregister_ipsec_alg_auth(&ipsec_alg_AES_MAC);
++#endif /* CONFIG_IPSEC_ALG_AES_MAC */
++ unregister_ipsec_alg_enc(&ipsec_alg_AES);
++ return;
++}
++#ifdef MODULE_LICENSE
++MODULE_LICENSE("GPL");
++#endif
++
++#if 0+NOT_YET
++#ifndef MODULE
++/*
++ * This is intended for static module setups, currently
++ * doesn't work for modular ipsec.o with static algos inside
++ */
++static int setup_keybits(const char *str)
++{
++ unsigned aux;
++ char *end;
++
++ aux = simple_strtoul(str,&end,0);
++ if (aux != 128 && aux != 192 && aux != 256)
++ return 0;
++ keyminbits = aux;
++
++ if (*end == 0 || *end != ',')
++ return 1;
++ str=end+1;
++ aux = simple_strtoul(str, NULL, 0);
++ if (aux != 128 && aux != 192 && aux != 256)
++ return 0;
++ if (aux >= keyminbits)
++ keymaxbits = aux;
++ return 1;
++}
++__setup("ipsec_aes_keybits=", setup_keybits);
++#endif
++#endif
++EXPORT_NO_SYMBOLS;
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/net/ipsec/alg/ipsec_alg_cryptoapi.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,421 @@
++/*
++ * ipsec_alg to linux cryptoapi GLUE
++ *
++ * Authors: CODE.ar TEAM
++ * Harpo MAxx <harpo@linuxmendoza.org.ar>
++ * JuanJo Ciarlante <jjo-ipsec@mendoza.gov.ar>
++ * Luciano Ruete <docemeses@softhome.net>
++ *
++ * ipsec_alg_cryptoapi.c,v 1.1.2.1 2003/11/21 18:12:23 jjo Exp
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ * Example usage:
++ * modinfo -p ipsec_cryptoapi (quite useful info, including supported algos)
++ * modprobe ipsec_cryptoapi
++ * modprobe ipsec_cryptoapi test=1
++ * modprobe ipsec_cryptoapi excl=1 (exclusive cipher/algo)
++ * modprobe ipsec_cryptoapi noauto=1 aes=1 twofish=1 (only these ciphers)
++ * modprobe ipsec_cryptoapi aes=128,128 (force these keylens)
++ * modprobe ipsec_cryptoapi des_ede3=0 (everything but 3DES)
++ */
++#include <linux/config.h>
++#include <linux/version.h>
++
++/*
++ * special case: ipsec core modular with this static algo inside:
++ * must avoid MODULE magic for this file
++ */
++#if CONFIG_IPSEC_MODULE && CONFIG_IPSEC_ALG_CRYPTOAPI
++#undef MODULE
++#endif
++
++#include <linux/module.h>
++#include <linux/init.h>
++
++#include <linux/kernel.h> /* printk() */
++#include <linux/errno.h> /* error codes */
++#include <linux/types.h> /* size_t */
++#include <linux/string.h>
++
++/* Check if __exit is defined, if not null it */
++#ifndef __exit
++#define __exit
++#endif
++
++/* warn the innocent */
++#if !defined (CONFIG_CRYPTO) && !defined (CONFIG_CRYPTO_MODULE)
++#warning "No linux CryptoAPI found, install 2.4.22+ or 2.6.x"
++#define NO_CRYPTOAPI_SUPPORT
++#endif
++/* Low freeswan header coupling */
++#include "freeswan/ipsec_alg.h"
++
++#include <linux/crypto.h>
++#ifdef CRYPTO_API_VERSION_CODE
++#warning "Old CryptoAPI is not supported. Only linux-2.4.22+ or linux-2.6.x are supported"
++#define NO_CRYPTOAPI_SUPPORT
++#endif
++
++#ifdef NO_CRYPTOAPI_SUPPORT
++#warning "Building an unusable module :P"
++/* Catch old CryptoAPI by not allowing module to load */
++IPSEC_ALG_MODULE_INIT( ipsec_cryptoapi_init )
++{
++ printk(KERN_WARNING "ipsec_cryptoapi.o was not built on stock Linux CryptoAPI (2.4.22+ or 2.6.x), not loading.\n");
++ return -EINVAL;
++}
++#else
++#include <asm/scatterlist.h>
++#include <asm/pgtable.h>
++#include <linux/mm.h>
++
++#define CIPHERNAME_AES "aes"
++#define CIPHERNAME_3DES "des3_ede"
++#define CIPHERNAME_BLOWFISH "blowfish"
++#define CIPHERNAME_CAST "cast5"
++#define CIPHERNAME_SERPENT "serpent"
++#define CIPHERNAME_TWOFISH "twofish"
++
++#define ESP_3DES 3
++#define ESP_AES 12
++#define ESP_BLOWFISH 7 /* truely _constant_ :) */
++#define ESP_CAST 6 /* quite constant :) */
++#define ESP_SERPENT 252 /* from ipsec drafts */
++#define ESP_TWOFISH 253 /* from ipsec drafts */
++
++#define AH_MD5 2
++#define AH_SHA 3
++#define DIGESTNAME_MD5 "md5"
++#define DIGESTNAME_SHA1 "sha1"
++
++MODULE_AUTHOR("Juanjo Ciarlante, Harpo MAxx, Luciano Ruete");
++static int debug=0;
++MODULE_PARM(debug, "i");
++static int test=0;
++MODULE_PARM(test, "i");
++static int excl=0;
++MODULE_PARM(excl, "i");
++
++static int noauto = 0;
++MODULE_PARM(noauto,"i");
++MODULE_PARM_DESC(noauto, "Dont try all known algos, just setup enabled ones");
++
++static int des_ede3[] = {-1, -1};
++static int aes[] = {-1, -1};
++static int blowfish[] = {-1, -1};
++static int cast[] = {-1, -1};
++static int serpent[] = {-1, -1};
++static int twofish[] = {-1, -1};
++
++MODULE_PARM(des_ede3,"1-2i");
++MODULE_PARM(aes,"1-2i");
++MODULE_PARM(blowfish,"1-2i");
++MODULE_PARM(cast,"1-2i");
++MODULE_PARM(serpent,"1-2i");
++MODULE_PARM(twofish,"1-2i");
++MODULE_PARM_DESC(des_ede3, "0: disable | 1: force_enable | min,max: dontuse");
++MODULE_PARM_DESC(aes, "0: disable | 1: force_enable | min,max: keybitlens");
++MODULE_PARM_DESC(blowfish, "0: disable | 1: force_enable | min,max: keybitlens");
++MODULE_PARM_DESC(cast, "0: disable | 1: force_enable | min,max: keybitlens");
++MODULE_PARM_DESC(serpent, "0: disable | 1: force_enable | min,max: keybitlens");
++MODULE_PARM_DESC(twofish, "0: disable | 1: force_enable | min,max: keybitlens");
++
++struct ipsec_alg_capi_cipher {
++ const char *ciphername; /* cryptoapi's ciphername */
++ unsigned blocksize;
++ unsigned short minbits;
++ unsigned short maxbits;
++ int *parm; /* lkm param for this cipher */
++ struct ipsec_alg_enc alg; /* note it's not a pointer */
++};
++static struct ipsec_alg_capi_cipher alg_capi_carray[] = {
++ { CIPHERNAME_AES , 16, 128, 256, aes , { ixt_alg_id: ESP_AES, }},
++ { CIPHERNAME_TWOFISH , 16, 128, 256, twofish, { ixt_alg_id: ESP_TWOFISH, }},
++ { CIPHERNAME_SERPENT , 16, 128, 256, serpent, { ixt_alg_id: ESP_SERPENT, }},
++ { CIPHERNAME_CAST , 8, 128, 128, cast , { ixt_alg_id: ESP_CAST, }},
++ { CIPHERNAME_BLOWFISH , 8, 96, 448, blowfish,{ ixt_alg_id: ESP_BLOWFISH, }},
++ { CIPHERNAME_3DES , 8, 192, 192, des_ede3,{ ixt_alg_id: ESP_3DES, }},
++ { NULL, 0, 0, 0, NULL, {} }
++};
++#ifdef NOT_YET
++struct ipsec_alg_capi_digest {
++ const char *digestname; /* cryptoapi's digestname */
++ struct digest_implementation *di;
++ struct ipsec_alg_auth alg; /* note it's not a pointer */
++};
++static struct ipsec_alg_capi_cipher alg_capi_darray[] = {
++ { DIGESTNAME_MD5, NULL, { ixt_alg_id: AH_MD5, }},
++ { DIGESTNAME_SHA1, NULL, { ixt_alg_id: AH_SHA, }},
++ { NULL, NULL, {} }
++};
++#endif
++/*
++ * "generic" linux cryptoapi setup_cipher() function
++ */
++int setup_cipher(const char *ciphername)
++{
++ return crypto_alg_available(ciphername, 0);
++}
++
++/*
++ * setups ipsec_alg_capi_cipher "hyper" struct components, calling
++ * register_ipsec_alg for cointaned ipsec_alg object
++ */
++static void _capi_destroy_key (struct ipsec_alg_enc *alg, __u8 *key_e);
++static __u8 * _capi_new_key (struct ipsec_alg_enc *alg, const __u8 *key, size_t keylen);
++static int _capi_cbc_encrypt(struct ipsec_alg_enc *alg, __u8 * key_e, __u8 * in, int ilen, const __u8 * iv, int encrypt);
++
++static int
++setup_ipsec_alg_capi_cipher(struct ipsec_alg_capi_cipher *cptr)
++{
++ int ret;
++ cptr->alg.ixt_version = IPSEC_ALG_VERSION;
++ cptr->alg.ixt_module = THIS_MODULE;
++ atomic_set (& cptr->alg.ixt_refcnt, 0);
++ strncpy (cptr->alg.ixt_name , cptr->ciphername, sizeof (cptr->alg.ixt_name));
++
++ cptr->alg.ixt_blocksize=cptr->blocksize;
++ cptr->alg.ixt_keyminbits=cptr->minbits;
++ cptr->alg.ixt_keymaxbits=cptr->maxbits;
++ cptr->alg.ixt_state = 0;
++ if (excl) cptr->alg.ixt_state |= IPSEC_ALG_ST_EXCL;
++ cptr->alg.ixt_e_keylen=cptr->alg.ixt_keymaxbits/8;
++ cptr->alg.ixt_e_ctx_size = 0;
++ cptr->alg.ixt_alg_type = IPSEC_ALG_TYPE_ENCRYPT;
++ cptr->alg.ixt_e_new_key = _capi_new_key;
++ cptr->alg.ixt_e_destroy_key = _capi_destroy_key;
++ cptr->alg.ixt_e_cbc_encrypt = _capi_cbc_encrypt;
++ cptr->alg.ixt_data = cptr;
++
++ ret=register_ipsec_alg_enc(&cptr->alg);
++ printk("setup_ipsec_alg_capi_cipher(): "
++ "alg_type=%d alg_id=%d name=%s "
++ "keyminbits=%d keymaxbits=%d, ret=%d\n",
++ cptr->alg.ixt_alg_type,
++ cptr->alg.ixt_alg_id,
++ cptr->alg.ixt_name,
++ cptr->alg.ixt_keyminbits,
++ cptr->alg.ixt_keymaxbits,
++ ret);
++ return ret;
++}
++/*
++ * called in ipsec_sa_wipe() time, will destroy key contexts
++ * and do 1 unbind()
++ */
++static void
++_capi_destroy_key (struct ipsec_alg_enc *alg, __u8 *key_e)
++{
++ struct crypto_tfm *tfm=(struct crypto_tfm*)key_e;
++
++ if (debug > 0)
++ printk(KERN_DEBUG "klips_debug: _capi_destroy_key:"
++ "name=%s key_e=%p \n",
++ alg->ixt_name, key_e);
++ if (!key_e) {
++ printk(KERN_ERR "klips_debug: _capi_destroy_key:"
++ "name=%s NULL key_e!\n",
++ alg->ixt_name);
++ return;
++ }
++ crypto_free_tfm(tfm);
++}
++
++/*
++ * create new key context, need alg->ixt_data to know which
++ * (of many) cipher inside this module is the target
++ */
++static __u8 *
++_capi_new_key (struct ipsec_alg_enc *alg, const __u8 *key, size_t keylen)
++{
++ struct ipsec_alg_capi_cipher *cptr;
++ struct crypto_tfm *tfm=NULL;
++
++ cptr = alg->ixt_data;
++ if (!cptr) {
++ printk(KERN_ERR "_capi_new_key(): "
++ "NULL ixt_data (?!) for \"%s\" algo\n"
++ , alg->ixt_name);
++ goto err;
++ }
++ if (debug > 0)
++ printk(KERN_DEBUG "klips_debug:_capi_new_key:"
++ "name=%s cptr=%p key=%p keysize=%d\n",
++ alg->ixt_name, cptr, key, keylen);
++
++ /*
++ * alloc tfm
++ */
++ tfm = crypto_alloc_tfm(cptr->ciphername, CRYPTO_TFM_MODE_CBC);
++ if (!tfm) {
++ printk(KERN_ERR "_capi_new_key(): "
++ "NULL tfm for \"%s\" cryptoapi (\"%s\") algo\n"
++ , alg->ixt_name, cptr->ciphername);
++ goto err;
++ }
++ if (crypto_cipher_setkey(tfm, key, keylen) < 0) {
++ printk(KERN_ERR "_capi_new_key(): "
++ "failed new_key() for \"%s\" cryptoapi algo (keylen=%d)\n"
++ , alg->ixt_name, keylen);
++ crypto_free_tfm(tfm);
++ tfm=NULL;
++ }
++err:
++ if (debug > 0)
++ printk(KERN_DEBUG "klips_debug:_capi_new_key:"
++ "name=%s key=%p keylen=%d tfm=%p\n",
++ alg->ixt_name, key, keylen, tfm);
++ return (__u8 *) tfm;
++}
++/*
++ * core encryption function: will use cx->ci to call actual cipher's
++ * cbc function
++ */
++static int
++_capi_cbc_encrypt(struct ipsec_alg_enc *alg, __u8 * key_e, __u8 * in, int ilen, const __u8 * iv, int encrypt) {
++ int error =0;
++ struct crypto_tfm *tfm=(struct crypto_tfm *)key_e;
++ struct scatterlist sg = {
++ .page = virt_to_page(in),
++ .offset = (unsigned long)(in) % PAGE_SIZE,
++ .length=ilen,
++ };
++ if (debug > 1)
++ printk(KERN_DEBUG "klips_debug:_capi_cbc_encrypt:"
++ "key_e=%p "
++ "in=%p out=%p ilen=%d iv=%p encrypt=%d\n"
++ , key_e
++ , in, in, ilen, iv, encrypt);
++ crypto_cipher_set_iv(tfm, iv, crypto_tfm_alg_ivsize(tfm));
++ if (encrypt)
++ error = crypto_cipher_encrypt (tfm, &sg, &sg, ilen);
++ else
++ error = crypto_cipher_decrypt (tfm, &sg, &sg, ilen);
++ if (debug > 1)
++ printk(KERN_DEBUG "klips_debug:_capi_cbc_encrypt:"
++ "error=%d\n"
++ , error);
++ return (error<0)? error : ilen;
++}
++/*
++ * main initialization loop: for each cipher in list, do
++ * 1) setup cryptoapi cipher else continue
++ * 2) register ipsec_alg object
++ */
++static int
++setup_cipher_list (struct ipsec_alg_capi_cipher* clist)
++{
++ struct ipsec_alg_capi_cipher *cptr;
++ /* foreach cipher in list ... */
++ for (cptr=clist;cptr->ciphername;cptr++) {
++ /*
++ * see if cipher has been disabled (0) or
++ * if noauto set and not enabled (1)
++ */
++ if (cptr->parm[0] == 0 || (noauto && cptr->parm[0] < 0)) {
++ if (debug>0)
++ printk(KERN_INFO "setup_cipher_list(): "
++ "ciphername=%s skipped at user request: "
++ "noauto=%d parm[0]=%d parm[1]=%d\n"
++ , cptr->ciphername
++ , noauto
++ , cptr->parm[0]
++ , cptr->parm[1]);
++ continue;
++ }
++ /*
++ * use a local ci to avoid touching cptr->ci,
++ * if register ipsec_alg success then bind cipher
++ */
++ if( setup_cipher(cptr->ciphername) ) {
++ if (debug > 0)
++ printk(KERN_DEBUG "klips_debug:"
++ "setup_cipher_list():"
++ "ciphername=%s found\n"
++ , cptr->ciphername);
++ if (setup_ipsec_alg_capi_cipher(cptr) == 0) {
++
++
++ } else {
++ printk(KERN_ERR "klips_debug:"
++ "setup_cipher_list():"
++ "ciphername=%s failed ipsec_alg_register\n"
++ , cptr->ciphername);
++ }
++ } else {
++ if (debug>0)
++ printk(KERN_INFO "setup_cipher_list(): lookup for ciphername=%s: not found \n",
++ cptr->ciphername);
++ }
++ }
++ return 0;
++}
++/*
++ * deregister ipsec_alg objects and unbind ciphers
++ */
++static int
++unsetup_cipher_list (struct ipsec_alg_capi_cipher* clist)
++{
++ struct ipsec_alg_capi_cipher *cptr;
++ /* foreach cipher in list ... */
++ for (cptr=clist;cptr->ciphername;cptr++) {
++ if (cptr->alg.ixt_state & IPSEC_ALG_ST_REGISTERED) {
++ unregister_ipsec_alg_enc(&cptr->alg);
++ }
++ }
++ return 0;
++}
++/*
++ * test loop for registered algos
++ */
++static int
++test_cipher_list (struct ipsec_alg_capi_cipher* clist)
++{
++ int test_ret;
++ struct ipsec_alg_capi_cipher *cptr;
++ /* foreach cipher in list ... */
++ for (cptr=clist;cptr->ciphername;cptr++) {
++ if (cptr->alg.ixt_state & IPSEC_ALG_ST_REGISTERED) {
++ test_ret=ipsec_alg_test(
++ cptr->alg.ixt_alg_type,
++ cptr->alg.ixt_alg_id,
++ test);
++ printk("test_cipher_list(alg_type=%d alg_id=%d): test_ret=%d\n",
++ cptr->alg.ixt_alg_type,
++ cptr->alg.ixt_alg_id,
++ test_ret);
++ }
++ }
++ return 0;
++}
++
++IPSEC_ALG_MODULE_INIT( ipsec_cryptoapi_init )
++{
++ int ret, test_ret;
++ if ((ret=setup_cipher_list(alg_capi_carray)) < 0)
++ return -EPROTONOSUPPORT;
++ if (ret==0 && test) {
++ test_ret=test_cipher_list(alg_capi_carray);
++ }
++ return ret;
++}
++IPSEC_ALG_MODULE_EXIT( ipsec_cryptoapi_fini )
++{
++ unsetup_cipher_list(alg_capi_carray);
++ return;
++}
++#ifdef MODULE_LICENSE
++MODULE_LICENSE("GPL");
++#endif
++
++EXPORT_NO_SYMBOLS;
++#endif /* NO_CRYPTOAPI_SUPPORT */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/net/ipsec/alg/scripts/mk-static_init.c.sh Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,18 @@
++#!/bin/sh
++cat << EOF
++#include <linux/kernel.h>
++#include <linux/list.h>
++#include "freeswan/ipsec_alg.h"
++$(for i in $*; do
++ test -z "$i" && continue
++ echo "extern int $i(void);"
++done)
++void ipsec_alg_static_init(void){
++ int __attribute__ ((unused)) err=0;
++$(for i in $*; do
++ test -z "$i" && continue
++ echo " if ((err=$i()) < 0)"
++ echo " printk(KERN_WARNING \"$i() returned %d\", err);"
++done)
++}
++EOF
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/net/ipsec/defconfig Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,146 @@
++
++#
++# RCSID $Id$
++#
++
++#
++# FreeS/WAN IPSec implementation, KLIPS kernel config defaults
++#
++
++#
++# First, lets override stuff already set or not in the kernel config.
++#
++# We can't even think about leaving this off...
++CONFIG_INET=y
++
++#
++# This must be on for subnet protection.
++CONFIG_IP_FORWARD=y
++
++# Shut off IPSEC masquerading if it has been enabled, since it will
++# break the compile. IPPROTO_ESP and IPPROTO_AH were included in
++# net/ipv4/ip_masq.c when they should have gone into include/linux/in.h.
++CONFIG_IP_MASQUERADE_IPSEC=n
++
++#
++# Next, lets set the recommended FreeS/WAN configuration.
++#
++
++# To config as static (preferred), 'y'. To config as module, 'm'.
++CONFIG_IPSEC=y
++
++# To do tunnel mode IPSec, this must be enabled.
++CONFIG_IPSEC_IPIP=y
++
++# To enable authentication, say 'y'. (Highly recommended)
++CONFIG_IPSEC_AH=y
++
++# Authentication algorithm(s):
++CONFIG_IPSEC_AUTH_HMAC_MD5=y
++CONFIG_IPSEC_AUTH_HMAC_SHA1=y
++
++# To enable encryption, say 'y'. (Highly recommended)
++CONFIG_IPSEC_ESP=y
++
++# Encryption algorithm(s):
++CONFIG_IPSEC_ENC_3DES=y
++CONFIG_IPSEC_ENC_AES=y
++
++# modular algo extensions (and new ALGOs)
++CONFIG_IPSEC_ALG=y
++CONFIG_IPSEC_ENC_3DES=y
++CONFIG_IPSEC_ENC_AES=y
++
++CONFIG_IPSEC_ALG_TWOFISH=m
++CONFIG_IPSEC_ALG_BLOWFISH=m
++CONFIG_IPSEC_ALG_SERPENT=m
++CONFIG_IPSEC_ALG_MD5=m
++CONFIG_IPSEC_ALG_SHA1=m
++CONFIG_IPSEC_ALG_SHA2=m
++#CONFIG_IPSEC_ALG_CAST=n
++#CONFIG_IPSEC_ALG_NULL=n
++
++# Use CryptoAPI for ALG? - by default, no.
++CONFIG_IPSEC_ENC_CRYPTOAPI=n
++
++
++# IP Compression: new, probably still has minor bugs.
++CONFIG_IPSEC_IPCOMP=y
++
++# To enable userspace-switchable KLIPS debugging, say 'y'.
++CONFIG_IPSEC_DEBUG=y
++
++# NAT Traversal
++CONFIG_IPSEC_NAT_TRAVERSAL=y
++
++#
++#
++# $Log$
++# Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++# Turn off EOLN_NATIVE flag
++#
++# (Logical change 1.5010)
++#
++# Revision 1.25 2004/07/05 01:03:53 mcr
++# fix for adding cryptoapi code.
++# keep it off for now, since UMLs do not have it yet.
++#
++# Revision 1.24 2004/04/06 02:49:25 mcr
++# pullup of algo code from alg-branch.
++#
++# Revision 1.23.2.2 2004/04/05 04:30:46 mcr
++# patches for alg-branch to compile/work with 2.x openswan
++#
++# Revision 1.23.2.1 2003/12/22 15:25:52 jjo
++# . Merged algo-0.8.1-rc11-test1 into alg-branch
++#
++# Revision 1.23 2003/12/10 01:14:27 mcr
++# NAT-traversal patches to KLIPS.
++#
++# Revision 1.22 2003/02/24 19:37:27 mcr
++# changed default compilation mode to static.
++#
++# Revision 1.21 2002/04/24 07:36:27 mcr
++# Moved from ./klips/net/ipsec/defconfig,v
++#
++# Revision 1.20 2002/04/02 04:07:40 mcr
++# default build is now 'm'odule for KLIPS
++#
++# Revision 1.19 2002/03/08 18:57:17 rgb
++# Added a blank line at the beginning of the file to make it easier for
++# other projects to patch ./arch/i386/defconfig, for example
++# LIDS+grSecurity requested by Jason Pattie.
++#
++# Revision 1.18 2000/11/30 17:26:56 rgb
++# Cleaned out unused options and enabled ipcomp by default.
++#
++# Revision 1.17 2000/09/15 11:37:01 rgb
++# Merge in heavily modified Svenning Soerensen's <svenning@post5.tele.dk>
++# IPCOMP zlib deflate code.
++#
++# Revision 1.16 2000/09/08 19:12:55 rgb
++# Change references from DEBUG_IPSEC to CONFIG_IPSEC_DEBUG.
++#
++# Revision 1.15 2000/05/24 19:37:13 rgb
++# *** empty log message ***
++#
++# Revision 1.14 2000/05/11 21:14:57 henry
++# just commenting the FOOBAR=y lines out is not enough
++#
++# Revision 1.13 2000/05/10 20:17:58 rgb
++# Comment out netlink defaults, which are no longer needed.
++#
++# Revision 1.12 2000/05/10 19:13:38 rgb
++# Added configure option to shut off no eroute passthrough.
++#
++# Revision 1.11 2000/03/16 07:09:46 rgb
++# Hardcode PF_KEYv2 support.
++# Disable IPSEC_ICMP by default.
++# Remove DES config option from defaults file.
++#
++# Revision 1.10 2000/01/11 03:09:42 rgb
++# Added a default of 'y' to PF_KEYv2 keying I/F.
++#
++# Revision 1.9 1999/05/08 21:23:12 rgb
++# Added support for 2.2.x kernels.
++#
++# Revision 1.8 1999/04/06 04:54:25 rgb
++# Fix/Add RCSID Id: and Log: bits to make PHMDs happy. This includes
++# patch shell fixes.
++#
++#
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/net/ipsec/ipcomp.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,725 @@
++/*
++ * IPCOMP zlib interface code.
++ * Copyright (C) 2000 Svenning Soerensen <svenning@post5.tele.dk>
++ * Copyright (C) 2000, 2001 Richard Guy Briggs <rgb@conscoop.ottawa.on.ca>
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ */
++
++char ipcomp_c_version[] = "RCSID $Id$";
++
++/* SSS */
++
++#include <linux/config.h>
++#include <linux/version.h>
++
++#define __NO_VERSION__
++#include <linux/module.h>
++#include <linux/kernel.h> /* printk() */
++
++#include "openswan/ipsec_param.h"
++
++#ifdef MALLOC_SLAB
++# include <linux/slab.h> /* kmalloc() */
++#else /* MALLOC_SLAB */
++# include <linux/malloc.h> /* kmalloc() */
++#endif /* MALLOC_SLAB */
++#include <linux/errno.h> /* error codes */
++#include <linux/types.h>
++#include <linux/netdevice.h>
++#include <linux/ip.h>
++#include <linux/skbuff.h>
++
++#include <linux/netdevice.h> /* struct device, and other headers */
++#include <linux/etherdevice.h> /* eth_type_trans */
++#include <linux/ip.h> /* struct iphdr */
++#include <linux/skbuff.h>
++
++#include <openswan.h>
++
++#ifdef NET_21
++# include <net/dst.h>
++# include <asm/uaccess.h>
++# include <linux/in6.h>
++# define proto_priv cb
++#endif /* NET21 */
++#include <asm/checksum.h>
++#include <net/ip.h>
++
++#include "openswan/radij.h"
++#include "openswan/ipsec_encap.h"
++#include "openswan/ipsec_sa.h"
++
++#include "openswan/ipsec_xform.h"
++#include "openswan/ipsec_tunnel.h"
++#include "openswan/ipsec_rcv.h" /* sysctl_ipsec_inbound_policy_check */
++#include "openswan/ipcomp.h"
++#include "zlib/zlib.h"
++#include "zlib/zutil.h"
++
++#include <pfkeyv2.h> /* SADB_X_CALG_DEFLATE */
++
++#ifdef CONFIG_IPSEC_DEBUG
++int sysctl_ipsec_debug_ipcomp = 0;
++#endif /* CONFIG_IPSEC_DEBUG */
++
++static
++struct sk_buff *skb_copy_ipcomp(struct sk_buff *skb, int data_growth, int gfp_mask);
++
++static
++voidpf my_zcalloc(voidpf opaque, uInt items, uInt size)
++{
++ return (voidpf) kmalloc(items*size, GFP_ATOMIC);
++}
++
++static
++void my_zfree(voidpf opaque, voidpf address)
++{
++ kfree(address);
++}
++
++struct sk_buff *skb_compress(struct sk_buff *skb, struct ipsec_sa *ips, unsigned int *flags)
++{
++ struct iphdr *iph;
++ unsigned int iphlen, pyldsz, cpyldsz;
++ unsigned char *buffer;
++ z_stream zs;
++ int zresult;
++
++ KLIPS_PRINT(sysctl_ipsec_debug_ipcomp,
++ "klips_debug:skb_compress: .\n");
++
++ if(skb == NULL) {
++ KLIPS_PRINT(sysctl_ipsec_debug_ipcomp,
++ "klips_debug:skb_compress: "
++ "passed in NULL skb, returning ERROR.\n");
++ if(flags != NULL) {
++ *flags |= IPCOMP_PARMERROR;
++ }
++ return skb;
++ }
++
++ if(ips == NULL) {
++ KLIPS_PRINT(sysctl_ipsec_debug_ipcomp,
++ "klips_debug:skb_compress: "
++ "passed in NULL ipsec_sa needed for cpi, returning ERROR.\n");
++ if(flags) {
++ *flags |= IPCOMP_PARMERROR;
++ }
++ return skb;
++ }
++
++ if (flags == NULL) {
++ KLIPS_PRINT(sysctl_ipsec_debug_ipcomp,
++ "klips_debug:skb_compress: "
++ "passed in NULL flags, returning ERROR.\n");
++ ipsec_kfree_skb(skb);
++ return NULL;
++ }
++
++#ifdef NET_21
++ iph = skb->nh.iph;
++#else /* NET_21 */
++ iph = skb->ip_hdr;
++#endif /* NET_21 */
++
++ switch (iph->protocol) {
++ case IPPROTO_COMP:
++ case IPPROTO_AH:
++ case IPPROTO_ESP:
++ KLIPS_PRINT(sysctl_ipsec_debug_ipcomp,
++ "klips_debug:skb_compress: "
++ "skipping compression of packet with ip protocol %d.\n",
++ iph->protocol);
++ *flags |= IPCOMP_UNCOMPRESSABLE;
++ return skb;
++ }
++
++ /* Don't compress packets already fragmented */
++ if (iph->frag_off & __constant_htons(IP_MF | IP_OFFSET)) {
++ KLIPS_PRINT(sysctl_ipsec_debug_ipcomp,
++ "klips_debug:skb_compress: "
++ "skipping compression of fragmented packet.\n");
++ *flags |= IPCOMP_UNCOMPRESSABLE;
++ return skb;
++ }
++
++ iphlen = iph->ihl << 2;
++ pyldsz = ntohs(iph->tot_len) - iphlen;
++
++ /* Don't compress less than 90 bytes (rfc 2394) */
++ if (pyldsz < 90) {
++ KLIPS_PRINT(sysctl_ipsec_debug_ipcomp,
++ "klips_debug:skb_compress: "
++ "skipping compression of tiny packet, len=%d.\n",
++ pyldsz);
++ *flags |= IPCOMP_UNCOMPRESSABLE;
++ return skb;
++ }
++
++ /* Adaptive decision */
++ if (ips->ips_comp_adapt_skip) {
++ KLIPS_PRINT(sysctl_ipsec_debug_ipcomp,
++ "klips_debug:skb_compress: "
++ "skipping compression: ips_comp_adapt_skip=%d.\n",
++ ips->ips_comp_adapt_skip);
++ ips->ips_comp_adapt_skip--;
++ *flags |= IPCOMP_UNCOMPRESSABLE;
++ return skb;
++ }
++
++ zs.zalloc = my_zcalloc;
++ zs.zfree = my_zfree;
++ zs.opaque = 0;
++
++ /* We want to use deflateInit2 because we don't want the adler
++ header. */
++ zresult = deflateInit2(&zs, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -11,
++ DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
++ if (zresult != Z_OK) {
++ KLIPS_PRINT(sysctl_ipsec_debug_ipcomp,
++ "klips_error:skb_compress: "
++ "deflateInit2() returned error %d (%s), "
++ "skipping compression.\n",
++ zresult,
++ zs.msg ? zs.msg : zError(zresult));
++ *flags |= IPCOMP_COMPRESSIONERROR;
++ return skb;
++ }
++
++
++ /* Max output size. Result should be max this size.
++ * Implementation specific tweak:
++ * If it's not at least 32 bytes and 6.25% smaller than
++ * the original packet, it's probably not worth wasting
++ * the receiver's CPU cycles decompressing it.
++ * Your mileage may vary.
++ */
++ cpyldsz = pyldsz - sizeof(struct ipcomphdr) - (pyldsz <= 512 ? 32 : pyldsz >> 4);
++
++ buffer = kmalloc(cpyldsz, GFP_ATOMIC);
++ if (!buffer) {
++ KLIPS_PRINT(sysctl_ipsec_debug_ipcomp,
++ "klips_error:skb_compress: "
++ "unable to kmalloc(%d, GFP_ATOMIC), "
++ "skipping compression.\n",
++ cpyldsz);
++ *flags |= IPCOMP_COMPRESSIONERROR;
++ deflateEnd(&zs);
++ return skb;
++ }
++
++#ifdef CONFIG_IPSEC_DEBUG
++ if(sysctl_ipsec_debug_ipcomp && sysctl_ipsec_debug_verbose) {
++ __u8 *c;
++ int i;
++
++ c = (__u8*)iph + iphlen;
++ for(i = 0; i < pyldsz; i++, c++) {
++ if(!(i % 16)) {
++ printk(KERN_INFO "skb_compress: before:");
++ }
++ printk("%02x ", *c);
++ if(!((i + 1) % 16)) {
++ printk("\n");
++ }
++ }
++ if(i % 16) {
++ printk("\n");
++ }
++ }
++#endif /* CONFIG_IPSEC_DEBUG */
++
++ zs.next_in = (char *) iph + iphlen; /* start of payload */
++ zs.avail_in = pyldsz;
++ zs.next_out = buffer; /* start of compressed payload */
++ zs.avail_out = cpyldsz;
++
++ /* Finish compression in one step */
++ zresult = deflate(&zs, Z_FINISH);
++
++ /* Free all dynamically allocated buffers */
++ deflateEnd(&zs);
++ if (zresult != Z_STREAM_END) {
++ *flags |= IPCOMP_UNCOMPRESSABLE;
++ kfree(buffer);
++
++ /* Adjust adaptive counters */
++ if (++(ips->ips_comp_adapt_tries) == IPCOMP_ADAPT_INITIAL_TRIES) {
++ KLIPS_PRINT(sysctl_ipsec_debug_ipcomp,
++ "klips_debug:skb_compress: "
++ "first %d packets didn't compress, "
++ "skipping next %d\n",
++ IPCOMP_ADAPT_INITIAL_TRIES,
++ IPCOMP_ADAPT_INITIAL_SKIP);
++ ips->ips_comp_adapt_skip = IPCOMP_ADAPT_INITIAL_SKIP;
++ }
++ else if (ips->ips_comp_adapt_tries == IPCOMP_ADAPT_INITIAL_TRIES + IPCOMP_ADAPT_SUBSEQ_TRIES) {
++ KLIPS_PRINT(sysctl_ipsec_debug_ipcomp,
++ "klips_debug:skb_compress: "
++ "next %d packets didn't compress, "
++ "skipping next %d\n",
++ IPCOMP_ADAPT_SUBSEQ_TRIES,
++ IPCOMP_ADAPT_SUBSEQ_SKIP);
++ ips->ips_comp_adapt_skip = IPCOMP_ADAPT_SUBSEQ_SKIP;
++ ips->ips_comp_adapt_tries = IPCOMP_ADAPT_INITIAL_TRIES;
++ }
++
++ return skb;
++ }
++
++ /* resulting compressed size */
++ cpyldsz -= zs.avail_out;
++
++ /* Insert IPCOMP header */
++ ((struct ipcomphdr*) ((char*) iph + iphlen))->ipcomp_nh = iph->protocol;
++ ((struct ipcomphdr*) ((char*) iph + iphlen))->ipcomp_flags = 0;
++ /* use the bottom 16 bits of the spi for the cpi. The top 16 bits are
++ for internal reference only. */
++ ((struct ipcomphdr*) (((char*)iph) + iphlen))->ipcomp_cpi = htons((__u16)(ntohl(ips->ips_said.spi) & 0x0000ffff));
++ KLIPS_PRINT(sysctl_ipsec_debug_ipcomp,
++ "klips_debug:skb_compress: "
++ "spi=%08x, spi&0xffff=%04x, cpi=%04x, payload size: raw=%d, comp=%d.\n",
++ ntohl(ips->ips_said.spi),
++ ntohl(ips->ips_said.spi) & 0x0000ffff,
++ ntohs(((struct ipcomphdr*)(((char*)iph)+iphlen))->ipcomp_cpi),
++ pyldsz,
++ cpyldsz);
++
++ /* Update IP header */
++ iph->protocol = IPPROTO_COMP;
++ iph->tot_len = htons(iphlen + sizeof(struct ipcomphdr) + cpyldsz);
++#if 1 /* XXX checksum is done by ipsec_tunnel ? */
++ iph->check = 0;
++ iph->check = ip_fast_csum((char *) iph, iph->ihl);
++#endif
++
++ /* Copy compressed payload */
++ memcpy((char *) iph + iphlen + sizeof(struct ipcomphdr),
++ buffer,
++ cpyldsz);
++ kfree(buffer);
++
++ /* Update skb length/tail by "unputting" the shrinkage */
++ skb_put(skb,
++ cpyldsz + sizeof(struct ipcomphdr) - pyldsz);
++
++#ifdef CONFIG_IPSEC_DEBUG
++ if(sysctl_ipsec_debug_ipcomp && sysctl_ipsec_debug_verbose) {
++ __u8 *c;
++ int i;
++
++ c = (__u8*)iph + iphlen + sizeof(struct ipcomphdr);
++ for(i = 0; i < cpyldsz; i++, c++) {
++ if(!(i % 16)) {
++ printk(KERN_INFO "skb_compress: result:");
++ }
++ printk("%02x ", *c);
++ if(!((i + 1) % 16)) {
++ printk("\n");
++ }
++ }
++ if(i % 16) {
++ printk("\n");
++ }
++ }
++#endif /* CONFIG_IPSEC_DEBUG */
++
++ ips->ips_comp_adapt_skip = 0;
++ ips->ips_comp_adapt_tries = 0;
++
++ return skb;
++}
++
++struct sk_buff *skb_decompress(struct sk_buff *skb, struct ipsec_sa *ips, unsigned int *flags)
++{
++ struct sk_buff *nskb = NULL;
++
++ /* original ip header */
++ struct iphdr *oiph, *iph;
++ unsigned int iphlen, pyldsz, cpyldsz;
++ z_stream zs;
++ int zresult;
++
++ KLIPS_PRINT(sysctl_ipsec_debug_ipcomp,
++ "klips_debug:skb_decompress: .\n");
++
++ if(!skb) {
++ KLIPS_PRINT(sysctl_ipsec_debug_ipcomp,
++ "klips_error:skb_decompress: "
++ "passed in NULL skb, returning ERROR.\n");
++ if (flags) *flags |= IPCOMP_PARMERROR;
++ return skb;
++ }
++
++ if(!ips && sysctl_ipsec_inbound_policy_check) {
++ KLIPS_PRINT(sysctl_ipsec_debug_ipcomp,
++ "klips_error:skb_decompress: "
++ "passed in NULL ipsec_sa needed for comp alg, returning ERROR.\n");
++ if (flags) *flags |= IPCOMP_PARMERROR;
++ return skb;
++ }
++
++ if (!flags) {
++ KLIPS_PRINT(sysctl_ipsec_debug_ipcomp,
++ "klips_error:skb_decompress: "
++ "passed in NULL flags, returning ERROR.\n");
++ ipsec_kfree_skb(skb);
++ return NULL;
++ }
++
++#ifdef NET_21
++ oiph = skb->nh.iph;
++#else /* NET_21 */
++ oiph = skb->ip_hdr;
++#endif /* NET_21 */
++
++ iphlen = oiph->ihl << 2;
++
++ if (oiph->protocol != IPPROTO_COMP) {
++ KLIPS_PRINT(sysctl_ipsec_debug_ipcomp,
++ "klips_error:skb_decompress: "
++ "called with non-IPCOMP packet (protocol=%d),"
++ "skipping decompression.\n",
++ oiph->protocol);
++ *flags |= IPCOMP_PARMERROR;
++ return skb;
++ }
++
++ if ( (((struct ipcomphdr*)((char*) oiph + iphlen))->ipcomp_flags != 0)
++ || ((((struct ipcomphdr*) ((char*) oiph + iphlen))->ipcomp_cpi
++ != htons(SADB_X_CALG_DEFLATE))
++ && sysctl_ipsec_inbound_policy_check
++ && (!ips || (ips && (ips->ips_encalg != SADB_X_CALG_DEFLATE)))) ) {
++ KLIPS_PRINT(sysctl_ipsec_debug_ipcomp,
++ "klips_error:skb_decompress: "
++ "called with incompatible IPCOMP packet (flags=%d, "
++ "cpi=%d), ips-compalg=%d, skipping decompression.\n",
++ ntohs(((struct ipcomphdr*) ((char*) oiph + iphlen))->ipcomp_flags),
++ ntohs(((struct ipcomphdr*) ((char*) oiph + iphlen))->ipcomp_cpi),
++ ips ? ips->ips_encalg : 0);
++ *flags |= IPCOMP_PARMERROR;
++
++ return skb;
++ }
++
++ if (ntohs(oiph->frag_off) & ~0x4000) {
++ KLIPS_PRINT(sysctl_ipsec_debug_ipcomp,
++ "klips_error:skb_decompress: "
++ "called with fragmented IPCOMP packet, "
++ "skipping decompression.\n");
++ *flags |= IPCOMP_PARMERROR;
++ return skb;
++ }
++
++ /* original compressed payload size */
++ cpyldsz = ntohs(oiph->tot_len) - iphlen - sizeof(struct ipcomphdr);
++
++ zs.zalloc = my_zcalloc;
++ zs.zfree = my_zfree;
++ zs.opaque = 0;
++
++ zs.next_in = (char *) oiph + iphlen + sizeof(struct ipcomphdr);
++ zs.avail_in = cpyldsz;
++
++ /* Maybe we should be a bit conservative about memory
++ requirements and use inflateInit2 */
++ /* Beware, that this might make us unable to decompress packets
++ from other implementations - HINT: check PGPnet source code */
++ /* We want to use inflateInit2 because we don't want the adler
++ header. */
++ zresult = inflateInit2(&zs, -15);
++ if (zresult != Z_OK) {
++ KLIPS_PRINT(sysctl_ipsec_debug_ipcomp,
++ "klips_error:skb_decompress: "
++ "inflateInit2() returned error %d (%s), "
++ "skipping decompression.\n",
++ zresult,
++ zs.msg ? zs.msg : zError(zresult));
++ *flags |= IPCOMP_DECOMPRESSIONERROR;
++
++ return skb;
++ }
++
++ /* We have no way of knowing the exact length of the resulting
++ decompressed output before we have actually done the decompression.
++ For now, we guess that the packet will not be bigger than the
++ attached ipsec device's mtu or 16260, whichever is biggest.
++ This may be wrong, since the sender's mtu may be bigger yet.
++ XXX This must be dealt with later XXX
++ */
++
++ /* max payload size */
++ pyldsz = skb->dev ? (skb->dev->mtu < 16260 ? 16260 : skb->dev->mtu)
++ : (65520 - iphlen);
++ KLIPS_PRINT(sysctl_ipsec_debug_ipcomp,
++ "klips_debug:skb_decompress: "
++ "max payload size: %d\n", pyldsz);
++
++ while (pyldsz > (cpyldsz + sizeof(struct ipcomphdr)) &&
++ (nskb = skb_copy_ipcomp(skb,
++ pyldsz - cpyldsz - sizeof(struct ipcomphdr),
++ GFP_ATOMIC)) == NULL) {
++ KLIPS_PRINT(sysctl_ipsec_debug_ipcomp,
++ "klips_error:skb_decompress: "
++ "unable to skb_copy_ipcomp(skb, %d, GFP_ATOMIC), "
++ "trying with less payload size.\n",
++ (int)(pyldsz - cpyldsz - sizeof(struct ipcomphdr)));
++ pyldsz >>=1;
++ }
++
++ if (!nskb) {
++ KLIPS_PRINT(sysctl_ipsec_debug_ipcomp,
++ "klips_error:skb_decompress: "
++ "unable to allocate memory, dropping packet.\n");
++ *flags |= IPCOMP_DECOMPRESSIONERROR;
++ inflateEnd(&zs);
++
++ return skb;
++ }
++
++#ifdef CONFIG_IPSEC_DEBUG
++ if(sysctl_ipsec_debug_ipcomp && sysctl_ipsec_debug_verbose) {
++ __u8 *c;
++ int i;
++
++ c = (__u8*)oiph + iphlen + sizeof(struct ipcomphdr);
++ for(i = 0; i < cpyldsz; i++, c++) {
++ if(!(i % 16)) {
++ printk(KERN_INFO "skb_decompress: before:");
++ }
++ printk("%02x ", *c);
++ if(!((i + 1) % 16)) {
++ printk("\n");
++ }
++ }
++ if(i % 16) {
++ printk("\n");
++ }
++ }
++#endif /* CONFIG_IPSEC_DEBUG */
++
++#ifdef NET_21
++ iph = nskb->nh.iph;
++#else /* NET_21 */
++ iph = nskb->ip_hdr;
++#endif /* NET_21 */
++ zs.next_out = (char *)iph + iphlen;
++ zs.avail_out = pyldsz;
++
++ zresult = inflate(&zs, Z_SYNC_FLUSH);
++
++ /* work around a bug in zlib, which sometimes wants to taste an extra
++ * byte when being used in the (undocumented) raw deflate mode.
++ */
++ if (zresult == Z_OK && !zs.avail_in && zs.avail_out) {
++ __u8 zerostuff = 0;
++
++ zs.next_in = &zerostuff;
++ zs.avail_in = 1;
++ zresult = inflate(&zs, Z_FINISH);
++ }
++
++ inflateEnd(&zs);
++ if (zresult != Z_STREAM_END) {
++ KLIPS_PRINT(sysctl_ipsec_debug_ipcomp,
++ "klips_error:skb_decompress: "
++ "inflate() returned error %d (%s), "
++ "skipping decompression.\n",
++ zresult,
++ zs.msg ? zs.msg : zError(zresult));
++ *flags |= IPCOMP_DECOMPRESSIONERROR;
++ ipsec_kfree_skb(nskb);
++
++ return skb;
++ }
++
++ /* Update IP header */
++ /* resulting decompressed size */
++ pyldsz -= zs.avail_out;
++ iph->tot_len = htons(iphlen + pyldsz);
++ iph->protocol = ((struct ipcomphdr*) ((char*) oiph + iphlen))->ipcomp_nh;
++ KLIPS_PRINT(sysctl_ipsec_debug_ipcomp,
++ "klips_debug:skb_decompress: "
++ "spi=%08x, spi&0xffff=%04x, cpi=%04x, payload size: comp=%d, raw=%d, nh=%d.\n",
++ ips ? ntohl(ips->ips_said.spi) : 0,
++ ips ? ntohl(ips->ips_said.spi) & 0x0000ffff : 0,
++ ntohs(((struct ipcomphdr*)(((char*)oiph)+iphlen))->ipcomp_cpi),
++ cpyldsz,
++ pyldsz,
++ iph->protocol);
++
++#if 1 /* XXX checksum is done by ipsec_rcv ? */
++ iph->check = 0;
++ iph->check = ip_fast_csum((char*) iph, iph->ihl);
++#endif
++
++ /* Update skb length/tail by "unputting" the unused data area */
++ skb_put(nskb, -zs.avail_out);
++
++ ipsec_kfree_skb(skb);
++
++ if (iph->protocol == IPPROTO_COMP)
++ {
++#ifdef CONFIG_IPSEC_DEBUG
++ if(sysctl_ipsec_debug_ipcomp)
++ KLIPS_PRINT(sysctl_ipsec_debug_ipcomp,
++ "klips_debug:skb_decompress: "
++ "Eh? inner packet is also compressed, dropping.\n");
++#endif /* CONFIG_IPSEC_DEBUG */
++
++ ipsec_kfree_skb(nskb);
++ return NULL;
++ }
++
++#ifdef CONFIG_IPSEC_DEBUG
++ if(sysctl_ipsec_debug_ipcomp && sysctl_ipsec_debug_verbose) {
++ __u8 *c;
++ int i;
++
++ c = (__u8*)iph + iphlen;
++ for(i = 0; i < pyldsz; i++, c++) {
++ if(!(i % 16)) {
++ printk(KERN_INFO "skb_decompress: result:");
++ }
++ printk("%02x ", *c);
++ if(!((i + 1) % 16)) {
++ printk("\n");
++ }
++ }
++ if(i % 16) {
++ printk("\n");
++ }
++ }
++#endif /* CONFIG_IPSEC_DEBUG */
++
++ return nskb;
++}
++
++
++/* this is derived from skb_copy() in linux 2.2.14 */
++/* May be incompatible with other kernel versions!! */
++static
++struct sk_buff *skb_copy_ipcomp(struct sk_buff *skb, int data_growth, int gfp_mask)
++{
++ struct sk_buff *n;
++ struct iphdr *iph;
++ unsigned long offset;
++ unsigned int iphlen;
++
++ if(!skb) {
++ KLIPS_PRINT(sysctl_ipsec_debug_ipcomp,
++ "klips_debug:skb_copy_ipcomp: "
++ "passed in NULL skb, returning NULL.\n");
++ return NULL;
++ }
++
++ /*
++ * Allocate the copy buffer
++ */
++
++#ifdef NET_21
++ iph = skb->nh.iph;
++#else /* NET_21 */
++ iph = skb->ip_hdr;
++#endif /* NET_21 */
++ if (!iph) return NULL;
++ iphlen = iph->ihl << 2;
++
++ n=alloc_skb(skb->end - skb->head + data_growth, gfp_mask);
++ if(n==NULL)
++ return NULL;
++
++ /*
++ * Shift between the two data areas in bytes
++ */
++
++ offset=n->head-skb->head;
++
++ /* Set the data pointer */
++ skb_reserve(n,skb->data-skb->head);
++ /* Set the tail pointer and length */
++ skb_put(n,skb->len+data_growth);
++ /* Copy the bytes up to and including the ip header */
++ memcpy(n->head,
++ skb->head,
++ ((char *)iph - (char *)skb->head) + iphlen);
++ n->list=NULL;
++ n->next=NULL;
++ n->prev=NULL;
++ n->sk=NULL;
++ n->dev=skb->dev;
++ if (skb->h.raw)
++ n->h.raw=skb->h.raw+offset;
++ else
++ n->h.raw=NULL;
++ n->protocol=skb->protocol;
++#ifdef NET_21
++ n->csum = 0;
++ n->priority=skb->priority;
++ n->dst=dst_clone(skb->dst);
++ n->nh.raw=skb->nh.raw+offset;
++#ifndef NETDEV_23
++ n->is_clone=0;
++#endif /* NETDEV_23 */
++ atomic_set(&n->users, 1);
++ n->destructor = NULL;
++ n->security=skb->security;
++ memcpy(n->cb, skb->cb, sizeof(skb->cb));
++#ifdef CONFIG_IP_FIREWALL
++ n->fwmark = skb->fwmark;
++#endif
++#else /* NET_21 */
++ n->link3=NULL;
++ n->when=skb->when;
++ n->ip_hdr=(struct iphdr *)(((char *)skb->ip_hdr)+offset);
++ n->saddr=skb->saddr;
++ n->daddr=skb->daddr;
++ n->raddr=skb->raddr;
++ n->seq=skb->seq;
++ n->end_seq=skb->end_seq;
++ n->ack_seq=skb->ack_seq;
++ n->acked=skb->acked;
++ n->free=1;
++ n->arp=skb->arp;
++ n->tries=0;
++ n->lock=0;
++ n->users=0;
++ memcpy(n->proto_priv, skb->proto_priv, sizeof(skb->proto_priv));
++#endif /* NET_21 */
++ if (skb->mac.raw)
++ n->mac.raw=skb->mac.raw+offset;
++ else
++ n->mac.raw=NULL;
++#ifndef NETDEV_23
++ n->used=skb->used;
++#endif /* !NETDEV_23 */
++ n->pkt_type=skb->pkt_type;
++#ifndef NETDEV_23
++ n->pkt_bridged=skb->pkt_bridged;
++#endif /* NETDEV_23 */
++ n->ip_summed=0;
++ n->stamp=skb->stamp;
++#ifndef NETDEV_23 /* this seems to have been removed in 2.4 */
++#if defined(CONFIG_SHAPER) || defined(CONFIG_SHAPER_MODULE)
++ n->shapelatency=skb->shapelatency; /* Latency on frame */
++ n->shapeclock=skb->shapeclock; /* Time it should go out */
++ n->shapelen=skb->shapelen; /* Frame length in clocks */
++ n->shapestamp=skb->shapestamp; /* Stamp for shaper */
++ n->shapepend=skb->shapepend; /* Pending */
++#endif /* defined(CONFIG_SHAPER) || defined(CONFIG_SHAPER_MODULE) */
++#endif /* NETDEV_23 */
++#ifdef CONFIG_HIPPI
++ n->private.ifield=skb->private.ifield;
++#endif /* CONFIG_HIPPI */
++
++ return n;
++}
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/net/ipsec/ipsec_ah.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,363 @@
++/*
++ * processing code for AH
++ * Copyright (C) 2003-2004 Michael Richardson <mcr@xelerance.com>
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ */
++
++char ipsec_ah_c_version[] = "RCSID $Id$";
++#include <linux/config.h>
++#include <linux/version.h>
++
++#define __NO_VERSION__
++#include <linux/module.h>
++#include <linux/kernel.h> /* printk() */
++
++#include "openswan/ipsec_param.h"
++
++#ifdef MALLOC_SLAB
++# include <linux/slab.h> /* kmalloc() */
++#else /* MALLOC_SLAB */
++# include <linux/malloc.h> /* kmalloc() */
++#endif /* MALLOC_SLAB */
++#include <linux/errno.h> /* error codes */
++#include <linux/types.h> /* size_t */
++#include <linux/interrupt.h> /* mark_bh */
++
++#include <linux/netdevice.h> /* struct device, and other headers */
++#include <linux/etherdevice.h> /* eth_type_trans */
++#include <linux/ip.h> /* struct iphdr */
++#include <linux/skbuff.h>
++#include <openswan.h>
++#ifdef SPINLOCK
++# ifdef SPINLOCK_23
++# include <linux/spinlock.h> /* *lock* */
++# else /* SPINLOCK_23 */
++# include <asm/spinlock.h> /* *lock* */
++# endif /* SPINLOCK_23 */
++#endif /* SPINLOCK */
++#ifdef NET_21
++# include <asm/uaccess.h>
++# include <linux/in6.h>
++# define proto_priv cb
++#endif /* NET21 */
++#include <asm/checksum.h>
++#include <net/ip.h>
++
++#include "openswan/radij.h"
++#include "openswan/ipsec_encap.h"
++#include "openswan/ipsec_sa.h"
++
++#include "openswan/ipsec_radij.h"
++#include "openswan/ipsec_xform.h"
++#include "openswan/ipsec_tunnel.h"
++#include "openswan/ipsec_rcv.h"
++#include "openswan/ipsec_xmit.h"
++
++#include "openswan/ipsec_auth.h"
++
++#ifdef CONFIG_IPSEC_AH
++#include "openswan/ipsec_ah.h"
++#endif /* CONFIG_IPSEC_AH */
++
++#include "openswan/ipsec_proto.h"
++
++#ifdef CONFIG_IPSEC_DEBUG
++int debug_ah = 0;
++#endif /* CONFIG_IPSEC_DEBUG */
++
++__u32 zeroes[AH_AMAX];
++
++#ifdef CONFIG_IPSEC_AH
++enum ipsec_rcv_value
++ipsec_rcv_ah_checks(struct ipsec_rcv_state *irs,
++ struct sk_buff *skb)
++{
++ int ahminlen;
++
++ ahminlen = irs->hard_header_len + sizeof(struct iphdr);
++
++ /* take care not to deref this pointer until we check the minlen though */
++ irs->protostuff.ahstuff.ahp = (struct ahhdr *) (skb->data + irs->iphlen);
++
++ if((skb->len < ahminlen+sizeof(struct ahhdr)) ||
++ (skb->len < ahminlen+(irs->protostuff.ahstuff.ahp->ah_hl << 2))) {
++ KLIPS_PRINT(debug_rcv & DB_RX_INAU,
++ "klips_debug:ipsec_rcv: "
++ "runt ah packet of skb->len=%d received from %s, dropped.\n",
++ skb->len,
++ irs->ipsaddr_txt);
++ if(irs->stats) {
++ irs->stats->rx_errors++;
++ }
++ return IPSEC_RCV_BADLEN;
++ }
++
++ irs->said.spi = irs->protostuff.ahstuff.ahp->ah_spi;
++
++ /* XXX we only support the one 12-byte authenticator for now */
++ if(irs->protostuff.ahstuff.ahp->ah_hl != ((AHHMAC_HASHLEN+AHHMAC_RPLLEN) >> 2)) {
++ KLIPS_PRINT(debug_rcv & DB_RX_INAU,
++ "klips_debug:ipsec_rcv: "
++ "bad authenticator length %ld, expected %lu from %s.\n",
++ (long)(irs->protostuff.ahstuff.ahp->ah_hl << 2),
++ (unsigned long) sizeof(struct ahhdr),
++ irs->ipsaddr_txt);
++ if(irs->stats) {
++ irs->stats->rx_errors++;
++ }
++ return IPSEC_RCV_BADLEN;
++ }
++
++ return IPSEC_RCV_OK;
++}
++
++
++enum ipsec_rcv_value
++ipsec_rcv_ah_setup_auth(struct ipsec_rcv_state *irs,
++ struct sk_buff *skb,
++ __u32 *replay,
++ unsigned char **authenticator)
++{
++ struct ahhdr *ahp = irs->protostuff.ahstuff.ahp;
++
++ *replay = ntohl(ahp->ah_rpl);
++ *authenticator = ahp->ah_data;
++
++ return IPSEC_RCV_OK;
++}
++
++enum ipsec_rcv_value
++ipsec_rcv_ah_authcalc(struct ipsec_rcv_state *irs,
++ struct sk_buff *skb)
++{
++ struct auth_alg *aa;
++ struct ahhdr *ahp = irs->protostuff.ahstuff.ahp;
++ union {
++ MD5_CTX md5;
++ SHA1_CTX sha1;
++ } tctx;
++ struct iphdr ipo;
++ int ahhlen;
++
++ aa = irs->authfuncs;
++
++ /* copy the initialized keying material */
++ memcpy(&tctx, irs->ictx, irs->ictx_len);
++
++ ipo = *irs->ipp;
++ ipo.tos = 0; /* mutable RFC 2402 3.3.3.1.1.1 */
++ ipo.frag_off = 0;
++ ipo.ttl = 0;
++ ipo.check = 0;
++
++
++ /* do the sanitized header */
++ (*aa->update)((void*)&tctx, (caddr_t)&ipo, sizeof(struct iphdr));
++
++ /* XXX we didn't do the options here! */
++
++ /* now do the AH header itself */
++ ahhlen = AH_BASIC_LEN + (ahp->ah_hl << 2);
++ (*aa->update)((void*)&tctx, (caddr_t)ahp, ahhlen - AHHMAC_HASHLEN);
++
++ /* now, do some zeroes */
++ (*aa->update)((void*)&tctx, (caddr_t)zeroes, AHHMAC_HASHLEN);
++
++ /* finally, do the packet contents themselves */
++ (*aa->update)((void*)&tctx,
++ (caddr_t)skb->data + irs->iphlen + ahhlen,
++ skb->len - irs->iphlen - ahhlen);
++
++ (*aa->final)(irs->hash, (void *)&tctx);
++
++ memcpy(&tctx, irs->octx, irs->octx_len);
++
++ (*aa->update)((void *)&tctx, irs->hash, aa->hashlen);
++ (*aa->final)(irs->hash, (void *)&tctx);
++
++ return IPSEC_RCV_OK;
++}
++
++enum ipsec_rcv_value
++ipsec_rcv_ah_decap(struct ipsec_rcv_state *irs)
++{
++ struct ahhdr *ahp = irs->protostuff.ahstuff.ahp;
++ struct sk_buff *skb;
++ int ahhlen;
++
++ skb=irs->skb;
++
++ ahhlen = AH_BASIC_LEN + (ahp->ah_hl << 2);
++
++ irs->ipp->tot_len = htons(ntohs(irs->ipp->tot_len) - ahhlen);
++ irs->next_header = ahp->ah_nh;
++
++ /*
++ * move the IP header forward by the size of the AH header, which
++ * will remove the the AH header from the packet.
++ */
++ memmove((void *)(skb->data + ahhlen),
++ (void *)(skb->data), irs->iphlen);
++
++ ipsec_rcv_dmp("ah postmove", skb->data, skb->len);
++
++ /* skb_pull below, will move up by ahhlen */
++
++ /* XXX not clear how this can happen, as the message indicates */
++ if(skb->len < ahhlen) {
++ printk(KERN_WARNING
++ "klips_error:ipsec_rcv: "
++ "tried to skb_pull ahhlen=%d, %d available. This should never happen, please report.\n",
++ ahhlen,
++ (int)(skb->len));
++ return IPSEC_RCV_DECAPFAIL;
++ }
++ skb_pull(skb, ahhlen);
++
++ irs->ipp = (struct iphdr *)skb->data;
++
++ ipsec_rcv_dmp("ah postpull", skb->data, skb->len);
++
++ return IPSEC_RCV_OK;
++}
++
++enum ipsec_xmit_value
++ipsec_xmit_ah_setup(struct ipsec_xmit_state *ixs)
++{
++ struct iphdr ipo;
++ struct ahhdr *ahp;
++ __u8 hash[AH_AMAX];
++ union {
++#ifdef CONFIG_IPSEC_AUTH_HMAC_MD5
++ MD5_CTX md5;
++#endif /* CONFIG_IPSEC_AUTH_HMAC_MD5 */
++#ifdef CONFIG_IPSEC_AUTH_HMAC_SHA1
++ SHA1_CTX sha1;
++#endif /* CONFIG_IPSEC_AUTH_HMAC_SHA1 */
++ } tctx;
++ unsigned char *dat = (unsigned char *)ixs->iph;
++
++ ahp = (struct ahhdr *)(dat + ixs->iphlen);
++ ahp->ah_spi = ixs->ipsp->ips_said.spi;
++ ahp->ah_rpl = htonl(++(ixs->ipsp->ips_replaywin_lastseq));
++ ahp->ah_rv = 0;
++ ahp->ah_nh = ixs->iph->protocol;
++ ahp->ah_hl = (sizeof(struct ahhdr) >> 2) - sizeof(__u64)/sizeof(__u32);
++ ixs->iph->protocol = IPPROTO_AH;
++ ipsec_xmit_dmp("ahp", (char*)ahp, sizeof(*ahp));
++
++ ipo = *ixs->iph;
++ ipo.tos = 0;
++ ipo.frag_off = 0;
++ ipo.ttl = 0;
++ ipo.check = 0;
++ ipsec_xmit_dmp("ipo", (char*)&ipo, sizeof(ipo));
++
++ switch(ixs->ipsp->ips_authalg) {
++#ifdef CONFIG_IPSEC_AUTH_HMAC_MD5
++ case AH_MD5:
++ tctx.md5 = ((struct md5_ctx*)(ixs->ipsp->ips_key_a))->ictx;
++ ipsec_xmit_dmp("ictx", (char*)&tctx.md5, sizeof(tctx.md5));
++ MD5Update(&tctx.md5, (unsigned char *)&ipo, sizeof (struct iphdr));
++ ipsec_xmit_dmp("ictx+ipo", (char*)&tctx.md5, sizeof(tctx.md5));
++ MD5Update(&tctx.md5, (unsigned char *)ahp,
++ sizeof(struct ahhdr) - sizeof(ahp->ah_data));
++ ipsec_xmit_dmp("ictx+ahp", (char*)&tctx.md5, sizeof(tctx.md5));
++ MD5Update(&tctx.md5, (unsigned char *)zeroes, AHHMAC_HASHLEN);
++ ipsec_xmit_dmp("ictx+zeroes", (char*)&tctx.md5, sizeof(tctx.md5));
++ MD5Update(&tctx.md5, dat + ixs->iphlen + sizeof(struct ahhdr),
++ ixs->skb->len - ixs->iphlen - sizeof(struct ahhdr));
++ ipsec_xmit_dmp("ictx+dat", (char*)&tctx.md5, sizeof(tctx.md5));
++ MD5Final(hash, &tctx.md5);
++ ipsec_xmit_dmp("ictx hash", (char*)&hash, sizeof(hash));
++ tctx.md5 = ((struct md5_ctx*)(ixs->ipsp->ips_key_a))->octx;
++ ipsec_xmit_dmp("octx", (char*)&tctx.md5, sizeof(tctx.md5));
++ MD5Update(&tctx.md5, hash, AHMD596_ALEN);
++ ipsec_xmit_dmp("octx+hash", (char*)&tctx.md5, sizeof(tctx.md5));
++ MD5Final(hash, &tctx.md5);
++ ipsec_xmit_dmp("octx hash", (char*)&hash, sizeof(hash));
++
++ memcpy(ahp->ah_data, hash, AHHMAC_HASHLEN);
++
++ /* paranoid */
++ memset((caddr_t)&tctx.md5, 0, sizeof(tctx.md5));
++ memset((caddr_t)hash, 0, sizeof(*hash));
++ break;
++#endif /* CONFIG_IPSEC_AUTH_HMAC_MD5 */
++#ifdef CONFIG_IPSEC_AUTH_HMAC_SHA1
++ case AH_SHA:
++ tctx.sha1 = ((struct sha1_ctx*)(ixs->ipsp->ips_key_a))->ictx;
++ SHA1Update(&tctx.sha1, (unsigned char *)&ipo, sizeof (struct iphdr));
++ SHA1Update(&tctx.sha1, (unsigned char *)ahp, sizeof(struct ahhdr) - sizeof(ahp->ah_data));
++ SHA1Update(&tctx.sha1, (unsigned char *)zeroes, AHHMAC_HASHLEN);
++ SHA1Update(&tctx.sha1, dat + ixs->iphlen + sizeof(struct ahhdr),
++ ixs->skb->len - ixs->iphlen - sizeof(struct ahhdr));
++ SHA1Final(hash, &tctx.sha1);
++ tctx.sha1 = ((struct sha1_ctx*)(ixs->ipsp->ips_key_a))->octx;
++ SHA1Update(&tctx.sha1, hash, AHSHA196_ALEN);
++ SHA1Final(hash, &tctx.sha1);
++
++ memcpy(ahp->ah_data, hash, AHHMAC_HASHLEN);
++
++ /* paranoid */
++ memset((caddr_t)&tctx.sha1, 0, sizeof(tctx.sha1));
++ memset((caddr_t)hash, 0, sizeof(*hash));
++ break;
++#endif /* CONFIG_IPSEC_AUTH_HMAC_SHA1 */
++ default:
++ ixs->stats->tx_errors++;
++ return IPSEC_XMIT_AH_BADALG;
++ }
++#ifdef NET_21
++ ixs->skb->h.raw = (unsigned char*)ahp;
++#endif /* NET_21 */
++
++ return IPSEC_XMIT_OK;
++}
++
++struct xform_functions ah_xform_funcs[]={
++ { rcv_checks: ipsec_rcv_ah_checks,
++ rcv_setup_auth: ipsec_rcv_ah_setup_auth,
++ rcv_calc_auth: ipsec_rcv_ah_authcalc,
++ rcv_decrypt: ipsec_rcv_ah_decap,
++
++ xmit_setup: ipsec_xmit_ah_setup,
++ xmit_headroom: sizeof(struct ahhdr),
++ xmit_needtailroom: 0,
++ },
++};
++
++struct inet_protocol ah_protocol =
++{
++ ipsec_rcv, /* AH handler */
++ NULL, /* TUNNEL error control */
++#ifdef NETDEV_25
++ 1, /* no policy */
++#else
++ 0, /* next */
++ IPPROTO_AH, /* protocol ID */
++ 0, /* copy */
++ NULL, /* data */
++ "AH" /* name */
++#endif
++};
++
++#endif /* CONFIG_IPSEC_AH */
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.2 2004/04/06 02:49:25 mcr
++ * pullup of algo code from alg-branch.
++ *
++ *
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/net/ipsec/ipsec_alg.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,953 @@
++/*
++ * Modular extensions service and registration functions
++ *
++ * Author: JuanJo Ciarlante <jjo-ipsec@mendoza.gov.ar>
++ *
++ * Version: 0.8.1
++ *
++ * ipsec_alg.c,v 1.1.2.1 2003/11/21 18:12:23 jjo Exp
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ */
++#ifdef CONFIG_IPSEC_ALG
++#define __NO_VERSION__
++#include <linux/module.h>
++#include <linux/kernel.h> /* printk() */
++
++#include <linux/netdevice.h> /* struct device, and other headers */
++#include <linux/etherdevice.h> /* eth_type_trans */
++#include <linux/ip.h> /* struct iphdr */
++#include <linux/skbuff.h>
++#include <linux/socket.h>
++#include <linux/in.h>
++#include <linux/types.h>
++#include <linux/string.h> /* memcmp() */
++#include <linux/random.h> /* get_random_bytes() */
++#include <linux/errno.h> /* error codes */
++#ifdef SPINLOCK
++# ifdef SPINLOCK_23
++# include <linux/spinlock.h> /* *lock* */
++# else /* SPINLOCK_23 */
++# include <asm/spinlock.h> /* *lock* */
++# endif /* SPINLOCK_23 */
++#endif /* SPINLOCK */
++#ifdef NET_21
++# include <asm/uaccess.h>
++# include <linux/in6.h>
++# define proto_priv cb
++#endif /* NET21 */
++#include "openswan/ipsec_param.h"
++#include <openswan.h>
++#include "openswan/ipsec_sa.h"
++#include "openswan/radij.h"
++#include "openswan/ipsec_encap.h"
++#include "openswan/ipsec_radij.h"
++#include "openswan/ipsec_xform.h"
++#include "openswan/ipsec_tunnel.h"
++#include "openswan/ipsec_rcv.h"
++#if defined(CONFIG_IPSEC_ESP) || defined(CONFIG_IPSEC_AH)
++# include "openswan/ipsec_ah.h"
++#endif /* defined(CONFIG_IPSEC_ESP) || defined(CONFIG_IPSEC_AH) */
++#ifdef CONFIG_IPSEC_ESP
++# include "openswan/ipsec_esp.h"
++#endif /* !CONFIG_IPSEC_ESP */
++#ifdef CONFIG_IPSEC_IPCOMP
++# include "openswan/ipcomp.h"
++#endif /* CONFIG_IPSEC_COMP */
++
++#include <pfkeyv2.h>
++#include <pfkey.h>
++
++#include "openswan/ipsec_alg.h"
++
++#ifndef CONFIG_IPSEC_ALG
++#error This file _MUST_ be compiled with CONFIG_IPSEC_ALG enabled !
++#endif
++#if SADB_EALG_MAX < 255
++#warning Compiling with limited ESP support ( SADB_EALG_MAX < 256 )
++#endif
++
++static rwlock_t ipsec_alg_lock = RW_LOCK_UNLOCKED;
++#define IPSEC_ALG_HASHSZ 16 /* must be power of 2, even 2^0=1 */
++static struct list_head ipsec_alg_hash_table[IPSEC_ALG_HASHSZ];
++
++/* Old gcc's will fail here */
++#define barf_out(fmt, args...) do { printk(KERN_ERR "%s: (%s) " fmt, __FUNCTION__, ixt->ixt_name , ## args)\
++ ; goto out; } while(0)
++
++/*
++ * Must be already protected by lock
++ */
++static void __ipsec_alg_usage_inc(struct ipsec_alg *ixt) {
++ if (ixt->ixt_module)
++ __MOD_INC_USE_COUNT(ixt->ixt_module);
++ atomic_inc(&ixt->ixt_refcnt);
++}
++static void __ipsec_alg_usage_dec(struct ipsec_alg *ixt) {
++ atomic_dec(&ixt->ixt_refcnt);
++ if (ixt->ixt_module)
++ __MOD_DEC_USE_COUNT(ixt->ixt_module);
++}
++/*
++ * simple hash function, optimized for 0-hash (1 list) special
++ * case
++ */
++#if IPSEC_ALG_HASHSZ > 1
++static inline unsigned ipsec_alg_hashfn(int alg_type, int alg_id) {
++ return ((alg_type^alg_id)&(IPSEC_ALG_HASHSZ-1));
++}
++#else
++#define ipsec_alg_hashfn(x,y) (0)
++#endif
++
++/*****************************************************************
++ *
++ * INTERNAL table handling: insert, delete, find
++ *
++ *****************************************************************/
++
++/*
++ * hash table initialization, called from ipsec_alg_init()
++ */
++static void ipsec_alg_hash_init(void) {
++ struct list_head *head = ipsec_alg_hash_table;
++ int i = IPSEC_ALG_HASHSZ;
++ do {
++ INIT_LIST_HEAD(head);
++ head++;
++ i--;
++ } while (i);
++}
++/*
++ * hash list lookup by {alg_type, alg_id} and table head,
++ * must be already protected by lock
++ */
++static struct ipsec_alg *__ipsec_alg_find(unsigned alg_type, unsigned alg_id, struct list_head * head) {
++ struct list_head *p;
++ struct ipsec_alg *ixt=NULL;
++ for (p=head->next; p!=head; p=p->next) {
++ ixt = list_entry(p, struct ipsec_alg, ixt_list);
++ if (ixt->ixt_alg_type == alg_type && ixt->ixt_alg_id==alg_id) {
++ goto out;
++ }
++ }
++ ixt=NULL;
++out:
++ return ixt;
++}
++/*
++ * inserts (in front) a new entry in hash table,
++ * called from ipsec_alg_register() when new algorithm is registered.
++ */
++static int ipsec_alg_insert(struct ipsec_alg *ixt) {
++ int ret=-EINVAL;
++ unsigned hashval=ipsec_alg_hashfn(ixt->ixt_alg_type, ixt->ixt_alg_id);
++ struct list_head *head= ipsec_alg_hash_table + hashval;
++ struct ipsec_alg *ixt_cur;
++ /* new element must be virgin ... */
++ if (ixt->ixt_list.next != &ixt->ixt_list ||
++ ixt->ixt_list.prev != &ixt->ixt_list) {
++ printk(KERN_ERR "ipsec_alg_insert: ixt object \"%s\" "
++ "list head not initialized\n",
++ ixt->ixt_name);
++ return ret;
++ }
++ write_lock_bh(&ipsec_alg_lock);
++ ixt_cur = __ipsec_alg_find(ixt->ixt_alg_type, ixt->ixt_alg_id, head);
++ /* if previous (current) ipsec_alg found check excl flag of _anyone_ */
++ if (ixt_cur && ((ixt->ixt_state|ixt_cur->ixt_state) & IPSEC_ALG_ST_EXCL))
++ barf_out("ipsec_alg for alg_type=%d, alg_id=%d already exist. "
++ "Not loaded (ret=%d).\n",
++ ixt->ixt_alg_type,
++ ixt->ixt_alg_id, ret=-EEXIST);
++ list_add(&ixt->ixt_list, head);
++ ixt->ixt_state |= IPSEC_ALG_ST_REGISTERED;
++ ret=0;
++out:
++ write_unlock_bh(&ipsec_alg_lock);
++ return ret;
++}
++/*
++ * deletes an existing entry in hash table,
++ * called from ipsec_alg_unregister() when algorithm is unregistered.
++ */
++static int ipsec_alg_delete(struct ipsec_alg *ixt) {
++ write_lock_bh(&ipsec_alg_lock);
++ list_del(&ixt->ixt_list);
++ write_unlock_bh(&ipsec_alg_lock);
++ return 0;
++}
++/*
++ * here @user context (read-only when @kernel bh context)
++ * -> no bh disabling
++ *
++ * called from ipsec_sa_init() -> ipsec_alg_sa_init()
++ */
++static struct ipsec_alg *ipsec_alg_get(int alg_type, int alg_id) {
++ unsigned hashval=ipsec_alg_hashfn(alg_type, alg_id);
++ struct list_head *head= ipsec_alg_hash_table + hashval;
++ struct ipsec_alg *ixt;
++ read_lock(&ipsec_alg_lock);
++ ixt=__ipsec_alg_find(alg_type, alg_id, head);
++ if (ixt) __ipsec_alg_usage_inc(ixt);
++ read_unlock(&ipsec_alg_lock);
++ return ixt;
++}
++
++static void ipsec_alg_put(struct ipsec_alg *ixt) {
++ __ipsec_alg_usage_dec((struct ipsec_alg *)ixt);
++}
++
++/*****************************************************************
++ *
++ * INTERFACE for ENC services: key creation, encrypt function
++ *
++ *****************************************************************/
++
++/*
++ * main encrypt service entry point
++ * called from ipsec_rcv() with encrypt=IPSEC_ALG_DECRYPT and
++ * ipsec_tunnel_start_xmit with encrypt=IPSEC_ALG_ENCRYPT
++ */
++int ipsec_alg_esp_encrypt(struct ipsec_sa *sa_p, __u8 * idat, int ilen, const __u8 * iv, int encrypt) {
++ int ret;
++ struct ipsec_alg_enc *ixt_e=sa_p->ips_alg_enc;
++ KLIPS_PRINT(debug_rcv||debug_tunnel,
++ "klips_debug:ipsec_alg_esp_encrypt: "
++ "entering with encalg=%d, ixt_e=%p\n",
++ sa_p->ips_encalg, ixt_e);
++ if (!ixt_e) {
++ KLIPS_PRINT(debug_rcv||debug_tunnel,
++ "klips_debug:ipsec_alg_esp_encrypt: "
++ "NULL ipsec_alg_enc object\n");
++ return -1;
++ }
++ KLIPS_PRINT(debug_rcv||debug_tunnel,
++ "klips_debug:ipsec_alg_esp_encrypt: "
++ "calling cbc_encrypt encalg=%d "
++ "ips_key_e=%p idat=%p ilen=%d iv=%p, encrypt=%d\n",
++ sa_p->ips_encalg,
++ sa_p->ips_key_e, idat, ilen, iv, encrypt);
++ ret=ixt_e->ixt_e_cbc_encrypt(ixt_e, sa_p->ips_key_e, idat, ilen, iv, encrypt);
++ KLIPS_PRINT(debug_rcv||debug_tunnel,
++ "klips_debug:ipsec_alg_esp_encrypt: "
++ "returned ret=%d\n",
++ ret);
++ return ret;
++}
++/*
++ * encryption key context creation function
++ * called from pfkey_v2_parser.c:pfkey_ips_init()
++ */
++int ipsec_alg_enc_key_create(struct ipsec_sa *sa_p) {
++ int ret=-EINVAL;
++ int keyminbits, keymaxbits;
++ caddr_t ekp;
++ struct ipsec_alg_enc *ixt_e=sa_p->ips_alg_enc;
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:ipsec_alg_enc_key_create: "
++ "entering with encalg=%d ixt_e=%p\n",
++ sa_p->ips_encalg, ixt_e);
++ if (!ixt_e) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:ipsec_alg_enc_key_create: "
++ "NULL ipsec_alg_enc object\n");
++ return -EPROTO;
++ }
++
++ /*
++ * grRRR... DES 7bits jurassic stuff ... f*ckk --jjo
++ */
++ switch(ixt_e->ixt_alg_id) {
++ case ESP_3DES:
++ keyminbits=keymaxbits=192;break;
++ case ESP_DES:
++ keyminbits=keymaxbits=64;break;
++ default:
++ keyminbits=ixt_e->ixt_keyminbits;
++ keymaxbits=ixt_e->ixt_keymaxbits;
++ }
++ if(sa_p->ips_key_bits_e<keyminbits ||
++ sa_p->ips_key_bits_e>keymaxbits) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:ipsec_alg_enc_key_create: "
++ "incorrect encryption key size for id=%d: %d bits -- "
++ "must be between %d,%d bits\n" /*octets (bytes)\n"*/,
++ ixt_e->ixt_alg_id,
++ sa_p->ips_key_bits_e, keyminbits, keymaxbits);
++ ret=-EINVAL;
++ goto ixt_out;
++ }
++ /* save encryption key pointer */
++ ekp = sa_p->ips_key_e;
++
++
++ if (ixt_e->ixt_e_new_key) {
++ sa_p->ips_key_e = ixt_e->ixt_e_new_key(ixt_e,
++ ekp, sa_p->ips_key_bits_e/8);
++ ret = (sa_p->ips_key_e)? 0 : -EINVAL;
++ } else {
++ if((sa_p->ips_key_e = (caddr_t)
++ kmalloc((sa_p->ips_key_e_size = ixt_e->ixt_e_ctx_size),
++ GFP_ATOMIC)) == NULL) {
++ ret=-ENOMEM;
++ goto ixt_out;
++ }
++ /* zero-out key_e */
++ memset(sa_p->ips_key_e, 0, sa_p->ips_key_e_size);
++
++ /* I cast here to allow more decoupling in alg module */
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:ipsec_alg_enc_key_create: about to call:"
++ "set_key(key_e=%p, ekp=%p, key_size=%d)\n",
++ (caddr_t)sa_p->ips_key_e, ekp, sa_p->ips_key_bits_e/8);
++ ret = ixt_e->ixt_e_set_key(ixt_e, (caddr_t)sa_p->ips_key_e, ekp, sa_p->ips_key_bits_e/8);
++ }
++ /* paranoid */
++ memset(ekp, 0, sa_p->ips_key_bits_e/8);
++ kfree(ekp);
++ixt_out:
++ return ret;
++}
++
++/***************************************************************
++ *
++ * INTERFACE for AUTH services: key creation, hash functions
++ *
++ ***************************************************************/
++
++/*
++ * auth key context creation function
++ * called from pfkey_v2_parser.c:pfkey_ips_init()
++ */
++int ipsec_alg_auth_key_create(struct ipsec_sa *sa_p) {
++ int ret=-EINVAL;
++ struct ipsec_alg_auth *ixt_a=sa_p->ips_alg_auth;
++ int keyminbits, keymaxbits;
++ unsigned char *akp;
++ unsigned int aks;
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:ipsec_alg_auth_key_create: "
++ "entering with authalg=%d ixt_a=%p\n",
++ sa_p->ips_authalg, ixt_a);
++ if (!ixt_a) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:ipsec_alg_auth_key_create: "
++ "NULL ipsec_alg_auth object\n");
++ return -EPROTO;
++ }
++ keyminbits=ixt_a->ixt_keyminbits;
++ keymaxbits=ixt_a->ixt_keymaxbits;
++ if(sa_p->ips_key_bits_a<keyminbits || sa_p->ips_key_bits_a>keymaxbits) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:ipsec_alg_auth_key_create: incorrect auth"
++ "key size: %d bits -- must be between %d,%d bits\n"/*octets (bytes)\n"*/,
++ sa_p->ips_key_bits_a, keyminbits, keymaxbits);
++ ret=-EINVAL;
++ goto ixt_out;
++ }
++ /* save auth key pointer */
++ sa_p->ips_auth_bits = ixt_a->ixt_a_keylen * 8; /* XXX XXX */
++ akp = sa_p->ips_key_a;
++ aks = sa_p->ips_key_a_size;
++
++ /* will hold: 2 ctx and a blocksize buffer: kb */
++ sa_p->ips_key_a_size = ixt_a->ixt_a_ctx_size;
++ if((sa_p->ips_key_a =
++ (caddr_t) kmalloc(sa_p->ips_key_a_size, GFP_ATOMIC)) == NULL) {
++ ret=-ENOMEM;
++ goto ixt_out;
++ }
++ ixt_a->ixt_a_hmac_set_key(ixt_a, sa_p->ips_key_a, akp, sa_p->ips_key_bits_a/8); /* XXX XXX */
++ ret=0;
++ memset(akp, 0, aks);
++ kfree(akp);
++
++ixt_out:
++ return ret;
++}
++
++
++int ipsec_alg_sa_esp_hash(const struct ipsec_sa *sa_p, const __u8 *espp, int len, __u8 *hash, int hashlen) {
++ struct ipsec_alg_auth *ixt_a=sa_p->ips_alg_auth;
++ if (!ixt_a) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:ipsec_sa_esp_hash: "
++ "NULL ipsec_alg_auth object\n");
++ return -EPROTO;
++ }
++ KLIPS_PRINT(debug_tunnel|debug_rcv,
++ "klips_debug:ipsec_sa_esp_hash: "
++ "hashing %p (%d bytes) to %p (%d bytes)\n",
++ espp, len,
++ hash, hashlen);
++ ixt_a->ixt_a_hmac_hash(ixt_a,
++ sa_p->ips_key_a,
++ espp, len,
++ hash, hashlen);
++ return 0;
++}
++
++/***************************************************************
++ *
++ * INTERFACE for module loading,testing, and unloading
++ *
++ ***************************************************************/
++
++/* validation for registering (enc) module */
++static int check_enc(struct ipsec_alg_enc *ixt) {
++ int ret=-EINVAL;
++ if (ixt->ixt_alg_id==0 || ixt->ixt_alg_id > SADB_EALG_MAX)
++ barf_out("invalid alg_id=%d >= %d\n", ixt->ixt_alg_id, SADB_EALG_MAX);
++ if (ixt->ixt_blocksize==0) /* || ixt->ixt_blocksize%2) need for ESP_NULL */
++ barf_out(KERN_ERR "invalid blocksize=%d\n", ixt->ixt_blocksize);
++ if (ixt->ixt_keyminbits==0 && ixt->ixt_keymaxbits==0 && ixt->ixt_e_keylen==0)
++ goto zero_key_ok;
++ if (ixt->ixt_keyminbits==0)
++ barf_out(KERN_ERR "invalid keyminbits=%d\n", ixt->ixt_keyminbits);
++ if (ixt->ixt_keymaxbits==0)
++ barf_out(KERN_ERR "invalid keymaxbits=%d\n", ixt->ixt_keymaxbits);
++ if (ixt->ixt_e_keylen==0)
++ barf_out(KERN_ERR "invalid keysize=%d\n", ixt->ixt_e_keylen);
++zero_key_ok:
++ if (ixt->ixt_e_ctx_size==0 && ixt->ixt_e_new_key == NULL)
++ barf_out(KERN_ERR "invalid key_e_size=%d and ixt_e_new_key=NULL\n", ixt->ixt_e_ctx_size);
++ if (ixt->ixt_e_cbc_encrypt==NULL)
++ barf_out(KERN_ERR "e_cbc_encrypt() must be not NULL\n");
++ ret=0;
++out:
++ return ret;
++}
++
++/* validation for registering (auth) module */
++static int check_auth(struct ipsec_alg_auth *ixt)
++{
++ int ret=-EINVAL;
++ if (ixt->ixt_alg_id==0 || ixt->ixt_alg_id > SADB_AALG_MAX)
++ barf_out("invalid alg_id=%d > %d (SADB_AALG_MAX)\n", ixt->ixt_alg_id, SADB_AALG_MAX);
++ if (ixt->ixt_blocksize==0 || ixt->ixt_blocksize%2)
++ barf_out(KERN_ERR "invalid blocksize=%d\n", ixt->ixt_blocksize);
++ if (ixt->ixt_blocksize>AH_BLKLEN_MAX)
++ barf_out(KERN_ERR "sorry blocksize=%d > %d. "
++ "Please increase AH_BLKLEN_MAX and recompile\n",
++ ixt->ixt_blocksize,
++ AH_BLKLEN_MAX);
++ if (ixt->ixt_keyminbits==0 && ixt->ixt_keymaxbits==0 && ixt->ixt_a_keylen==0)
++ goto zero_key_ok;
++ if (ixt->ixt_keyminbits==0)
++ barf_out(KERN_ERR "invalid keyminbits=%d\n", ixt->ixt_keyminbits);
++ if (ixt->ixt_keymaxbits==0)
++ barf_out(KERN_ERR "invalid keymaxbits=%d\n", ixt->ixt_keymaxbits);
++ if (ixt->ixt_keymaxbits!=ixt->ixt_keyminbits)
++ barf_out(KERN_ERR "keymaxbits must equal keyminbits (not sure).\n");
++ if (ixt->ixt_a_keylen==0)
++ barf_out(KERN_ERR "invalid keysize=%d\n", ixt->ixt_a_keylen);
++zero_key_ok:
++ if (ixt->ixt_a_ctx_size==0)
++ barf_out(KERN_ERR "invalid a_ctx_size=%d\n", ixt->ixt_a_ctx_size);
++ if (ixt->ixt_a_hmac_set_key==NULL)
++ barf_out(KERN_ERR "a_hmac_set_key() must be not NULL\n");
++ if (ixt->ixt_a_hmac_hash==NULL)
++ barf_out(KERN_ERR "a_hmac_hash() must be not NULL\n");
++ ret=0;
++out:
++ return ret;
++}
++
++/*
++ * Generic (enc, auth) registration entry point
++ */
++int register_ipsec_alg(struct ipsec_alg *ixt) {
++ int ret=-EINVAL;
++ /* Validation */
++ if (ixt==NULL)
++ barf_out("NULL ipsec_alg object passed\n");
++ if ((ixt->ixt_version&0xffffff00) != (IPSEC_ALG_VERSION&0xffffff00))
++ barf_out("incorrect version: %d.%d.%d-%d, "
++ "must be %d.%d.%d[-%d]\n",
++ IPSEC_ALG_VERSION_QUAD(ixt->ixt_version),
++ IPSEC_ALG_VERSION_QUAD(IPSEC_ALG_VERSION));
++ switch(ixt->ixt_alg_type) {
++ case IPSEC_ALG_TYPE_AUTH:
++ if ((ret=check_auth((struct ipsec_alg_auth *)ixt)<0))
++ goto out;
++ break;
++ case IPSEC_ALG_TYPE_ENCRYPT:
++ if ((ret=check_enc((struct ipsec_alg_enc *)ixt)<0))
++ goto out;
++ /*
++ * Adapted two lines below:
++ * ivlen == 0 is possible (NULL enc has blocksize==1)
++ *
++ * fixed NULL support by David De Reu <DeReu@tComLabs.com>
++ */
++ if (ixt->ixt_ivlen == 0 && ixt->ixt_blocksize > 1)
++ ixt->ixt_ivlen = ixt->ixt_blocksize*8;
++ break;
++ default:
++ barf_out("alg_type=%d not supported\n", ixt->ixt_alg_type);
++ }
++ INIT_LIST_HEAD(&ixt->ixt_list);
++ ret = ipsec_alg_insert(ixt);
++ if (ret<0)
++ barf_out(KERN_WARNING "ipsec_alg for alg_id=%d failed."
++ "Not loaded (ret=%d).\n",
++ ixt->ixt_alg_id, ret);
++
++ ret = pfkey_list_insert_supported((struct supported *)&ixt->ixt_support, &(pfkey_supported_list[SADB_SATYPE_ESP]));
++ if (ret==0) {
++ ixt->ixt_state |= IPSEC_ALG_ST_SUPP;
++ /* send register event to userspace */
++ pfkey_register_reply(SADB_SATYPE_ESP, NULL);
++ } else
++ printk(KERN_ERR "pfkey_list_insert_supported returned %d. "
++ "Loading anyway.\n", ret);
++ ret=0;
++out:
++ return ret;
++}
++
++/*
++ * unregister ipsec_alg object from own tables, if
++ * success => calls pfkey_list_remove_supported()
++ */
++int unregister_ipsec_alg(struct ipsec_alg *ixt) {
++ int ret= -EINVAL;
++ switch(ixt->ixt_alg_type) {
++ case IPSEC_ALG_TYPE_AUTH:
++ case IPSEC_ALG_TYPE_ENCRYPT:
++ break;
++ default:
++ /* this is not a typo :) */
++ barf_out("frog found in list (\"%s\"): ixt_p=NULL\n",
++ ixt->ixt_name);
++ }
++
++ ret=ipsec_alg_delete(ixt);
++ if (ixt->ixt_state&IPSEC_ALG_ST_SUPP) {
++ ixt->ixt_state &= ~IPSEC_ALG_ST_SUPP;
++ pfkey_list_remove_supported((struct supported *)&ixt->ixt_support, &(pfkey_supported_list[SADB_SATYPE_ESP]));
++ /* send register event to userspace */
++ pfkey_register_reply(SADB_SATYPE_ESP, NULL);
++ }
++
++out:
++ return ret;
++}
++
++/*
++ * Must be called from user context
++ * used at module load type for testing algo implementation
++ */
++static int ipsec_alg_test_encrypt(int enc_alg, int test) {
++ int ret;
++ caddr_t buf = NULL;
++ int iv_size, keysize, key_e_size;
++ struct ipsec_alg_enc *ixt_e;
++ void *tmp_key_e = NULL;
++ #define BUFSZ 1024
++ #define MARGIN 0
++ #define test_enc (buf+MARGIN)
++ #define test_dec (test_enc+BUFSZ+MARGIN)
++ #define test_tmp (test_dec+BUFSZ+MARGIN)
++ #define test_key_e (test_tmp+BUFSZ+MARGIN)
++ #define test_iv (test_key_e+key_e_size+MARGIN)
++ #define test_key (test_iv+iv_size+MARGIN)
++ #define test_size (BUFSZ*3+key_e_size+iv_size+keysize+MARGIN*7)
++ ixt_e=(struct ipsec_alg_enc *)ipsec_alg_get(IPSEC_ALG_TYPE_ENCRYPT, enc_alg);
++ if (ixt_e==NULL) {
++ KLIPS_PRINT(1,
++ "klips_debug: ipsec_alg_test_encrypt: "
++ "encalg=%d object not found\n",
++ enc_alg);
++ ret=-EINVAL;
++ goto out;
++ }
++ iv_size=ixt_e->ixt_ivlen / 8;
++ key_e_size=ixt_e->ixt_e_ctx_size;
++ keysize=ixt_e->ixt_e_keylen;
++ KLIPS_PRINT(1,
++ "klips_debug: ipsec_alg_test_encrypt: "
++ "enc_alg=%d blocksize=%d key_e_size=%d keysize=%d\n",
++ enc_alg, iv_size, key_e_size, keysize);
++ if ((buf=kmalloc (test_size, GFP_KERNEL)) == NULL) {
++ ret= -ENOMEM;
++ goto out;
++ }
++ get_random_bytes(test_key, keysize);
++ get_random_bytes(test_iv, iv_size);
++ if (ixt_e->ixt_e_new_key) {
++ tmp_key_e = ixt_e->ixt_e_new_key(ixt_e, test_key, keysize);
++ ret = tmp_key_e ? 0 : -EINVAL;
++ } else {
++ tmp_key_e = test_key_e;
++ ret = ixt_e->ixt_e_set_key(ixt_e, test_key_e, test_key, keysize);
++ }
++ if (ret < 0)
++ goto out;
++ get_random_bytes(test_enc, BUFSZ);
++ memcpy(test_tmp, test_enc, BUFSZ);
++ ret=ixt_e->ixt_e_cbc_encrypt(ixt_e, tmp_key_e, test_enc, BUFSZ, test_iv, 1);
++ printk(KERN_INFO
++ "klips_info: ipsec_alg_test_encrypt: "
++ "cbc_encrypt=1 ret=%d\n",
++ ret);
++ ret=memcmp(test_enc, test_tmp, BUFSZ);
++ printk(KERN_INFO
++ "klips_info: ipsec_alg_test_encrypt: "
++ "memcmp(enc, tmp) ret=%d: %s\n", ret,
++ ret!=0? "OK. (encr->DIFFers)" : "FAIL! (encr->SAME)" );
++ memcpy(test_dec, test_enc, BUFSZ);
++ ret=ixt_e->ixt_e_cbc_encrypt(ixt_e, tmp_key_e, test_dec, BUFSZ, test_iv, 0);
++ printk(KERN_INFO
++ "klips_info: ipsec_alg_test_encrypt: "
++ "cbc_encrypt=0 ret=%d\n", ret);
++ ret=memcmp(test_dec, test_tmp, BUFSZ);
++ printk(KERN_INFO
++ "klips_info: ipsec_alg_test_encrypt: "
++ "memcmp(dec,tmp) ret=%d: %s\n", ret,
++ ret==0? "OK. (encr->decr->SAME)" : "FAIL! (encr->decr->DIFFers)" );
++ {
++ /* Shamelessly taken from drivers/md sources O:) */
++ unsigned long now;
++ int i, count, max=0;
++ int encrypt, speed;
++ for (encrypt=0; encrypt <2;encrypt ++) {
++ for (i = 0; i < 5; i++) {
++ now = jiffies;
++ count = 0;
++ while (jiffies == now) {
++ mb();
++ ixt_e->ixt_e_cbc_encrypt(ixt_e,
++ tmp_key_e, test_tmp,
++ BUFSZ, test_iv, encrypt);
++ mb();
++ count++;
++ mb();
++ }
++ if (count > max)
++ max = count;
++ }
++ speed = max * (HZ * BUFSZ / 1024);
++ printk(KERN_INFO
++ "klips_info: ipsec_alg_test_encrypt: "
++ "%s %s speed=%d KB/s\n",
++ ixt_e->ixt_name,
++ encrypt? "encrypt": "decrypt", speed);
++ }
++ }
++out:
++ if (tmp_key_e && ixt_e->ixt_e_destroy_key) ixt_e->ixt_e_destroy_key(ixt_e, tmp_key_e);
++ if (buf) kfree(buf);
++ if (ixt_e) ipsec_alg_put((struct ipsec_alg *)ixt_e);
++ return ret;
++ #undef test_enc
++ #undef test_dec
++ #undef test_tmp
++ #undef test_key_e
++ #undef test_iv
++ #undef test_key
++ #undef test_size
++}
++
++/*
++ * Must be called from user context
++ * used at module load type for testing algo implementation
++ */
++static int ipsec_alg_test_auth(int auth_alg, int test) {
++ int ret;
++ caddr_t buf = NULL;
++ int blocksize, keysize, key_a_size;
++ struct ipsec_alg_auth *ixt_a;
++ #define BUFSZ 1024
++ #define MARGIN 0
++ #define test_auth (buf+MARGIN)
++ #define test_key_a (test_auth+BUFSZ+MARGIN)
++ #define test_key (test_key_a+key_a_size+MARGIN)
++ #define test_hash (test_key+keysize+MARGIN)
++ #define test_size (BUFSZ+key_a_size+keysize+AHHMAC_HASHLEN+MARGIN*4)
++ ixt_a=(struct ipsec_alg_auth *)ipsec_alg_get(IPSEC_ALG_TYPE_AUTH, auth_alg);
++ if (ixt_a==NULL) {
++ KLIPS_PRINT(1,
++ "klips_debug: ipsec_alg_test_auth: "
++ "encalg=%d object not found\n",
++ auth_alg);
++ ret=-EINVAL;
++ goto out;
++ }
++ blocksize=ixt_a->ixt_blocksize;
++ key_a_size=ixt_a->ixt_a_ctx_size;
++ keysize=ixt_a->ixt_a_keylen;
++ KLIPS_PRINT(1,
++ "klips_debug: ipsec_alg_test_auth: "
++ "auth_alg=%d blocksize=%d key_a_size=%d keysize=%d\n",
++ auth_alg, blocksize, key_a_size, keysize);
++ if ((buf=kmalloc (test_size, GFP_KERNEL)) == NULL) {
++ ret= -ENOMEM;
++ goto out;
++ }
++ get_random_bytes(test_key, keysize);
++ ret = ixt_a->ixt_a_hmac_set_key(ixt_a, test_key_a, test_key, keysize);
++ if (ret < 0 )
++ goto out;
++ get_random_bytes(test_auth, BUFSZ);
++ ret=ixt_a->ixt_a_hmac_hash(ixt_a, test_key_a, test_auth, BUFSZ, test_hash, AHHMAC_HASHLEN);
++ printk(KERN_INFO
++ "klips_info: ipsec_alg_test_auth: "
++ "ret=%d\n", ret);
++ {
++ /* Shamelessly taken from drivers/md sources O:) */
++ unsigned long now;
++ int i, count, max=0;
++ int speed;
++ for (i = 0; i < 5; i++) {
++ now = jiffies;
++ count = 0;
++ while (jiffies == now) {
++ mb();
++ ixt_a->ixt_a_hmac_hash(ixt_a, test_key_a, test_auth, BUFSZ, test_hash, AHHMAC_HASHLEN);
++ mb();
++ count++;
++ mb();
++ }
++ if (count > max)
++ max = count;
++ }
++ speed = max * (HZ * BUFSZ / 1024);
++ printk(KERN_INFO
++ "klips_info: ipsec_alg_test_auth: "
++ "%s hash speed=%d KB/s\n",
++ ixt_a->ixt_name,
++ speed);
++ }
++out:
++ if (buf) kfree(buf);
++ if (ixt_a) ipsec_alg_put((struct ipsec_alg *)ixt_a);
++ return ret;
++ #undef test_auth
++ #undef test_key_a
++ #undef test_key
++ #undef test_hash
++ #undef test_size
++}
++
++int ipsec_alg_test(unsigned alg_type, unsigned alg_id, int test) {
++ switch(alg_type) {
++ case IPSEC_ALG_TYPE_ENCRYPT:
++ return ipsec_alg_test_encrypt(alg_id, test);
++ break;
++ case IPSEC_ALG_TYPE_AUTH:
++ return ipsec_alg_test_auth(alg_id, test);
++ break;
++ }
++ printk(KERN_ERR "klips_info: ipsec_alg_test() called incorrectly: "
++ "alg_type=%d alg_id=%d\n",
++ alg_type, alg_id);
++ return -EINVAL;
++}
++
++int ipsec_alg_init(void) {
++ KLIPS_PRINT(1, "klips_info:ipsec_alg_init: "
++ "KLIPS alg v=%d.%d.%d-%d (EALG_MAX=%d, AALG_MAX=%d)\n",
++ IPSEC_ALG_VERSION_QUAD(IPSEC_ALG_VERSION),
++ SADB_EALG_MAX, SADB_AALG_MAX);
++ /* Initialize tables */
++ write_lock_bh(&ipsec_alg_lock);
++ ipsec_alg_hash_init();
++ write_unlock_bh(&ipsec_alg_lock);
++
++ /* Initialize static algos */
++ KLIPS_PRINT(1, "klips_info:ipsec_alg_init: "
++ "calling ipsec_alg_static_init()\n");
++
++ /* If we are suppose to use our AES, and don't have CryptoAPI enabled... */
++#if defined(CONFIG_IPSEC_ENC_AES) && CONFIG_IPSEC_ENC_AES && !defined(CONFIG_IPSEC_ENC_AES_MODULE) && !CONFIG_IPSEC_ENC_CRYPTOAPI && !defined(CONFIG_IPSEC_ENC_CRYPTOAPI_MODULE)
++ {
++ extern int ipsec_aes_init(void);
++ ipsec_aes_init();
++ }
++#endif
++
++ /* If we are doing CryptoAPI, then init */
++#if defined(CONFIG_IPSEC_ENC_CRYPTOAPI) && CONFIG_IPSEC_ENC_CRYPTOAPI && !defined(CONFIG_IPSEC_ENC_CRYPTOAPI_MODULE)
++ {
++ extern int ipsec_cryptoapi_init(void);
++ ipsec_cryptoapi_init();
++ }
++#endif
++
++
++ return 0;
++}
++
++/**********************************************
++ *
++ * INTERFACE for ipsec_sa init and wipe
++ *
++ **********************************************/
++
++/*
++ * Called from pluto -> pfkey_v2_parser.c:pfkey_ipsec_sa_init()
++ */
++int ipsec_alg_sa_init(struct ipsec_sa *sa_p) {
++ struct ipsec_alg_enc *ixt_e;
++ struct ipsec_alg_auth *ixt_a;
++
++ /* Only ESP for now ... */
++ if (sa_p->ips_said.proto != IPPROTO_ESP)
++ return -EPROTONOSUPPORT;
++ KLIPS_PRINT(debug_pfkey, "klips_debug: ipsec_alg_sa_init() :"
++ "entering for encalg=%d, authalg=%d\n",
++ sa_p->ips_encalg, sa_p->ips_authalg);
++ if ((ixt_e=(struct ipsec_alg_enc *)
++ ipsec_alg_get(IPSEC_ALG_TYPE_ENCRYPT, sa_p->ips_encalg))) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug: ipsec_alg_sa_init() :"
++ "found ipsec_alg (ixt_e=%p) for encalg=%d\n",
++ ixt_e, sa_p->ips_encalg);
++ sa_p->ips_alg_enc=ixt_e;
++ }
++ if ((ixt_a=(struct ipsec_alg_auth *)
++ ipsec_alg_get(IPSEC_ALG_TYPE_AUTH, sa_p->ips_authalg))) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug: ipsec_alg_sa_init() :"
++ "found ipsec_alg (ixt_a=%p) for auth=%d\n",
++ ixt_a, sa_p->ips_authalg);
++ sa_p->ips_alg_auth=ixt_a;
++ }
++ return 0;
++}
++
++/*
++ * Called from pluto -> ipsec_sa.c:ipsec_sa_delchain()
++ */
++int ipsec_alg_sa_wipe(struct ipsec_sa *sa_p) {
++ struct ipsec_alg *ixt;
++ if ((ixt=(struct ipsec_alg *)sa_p->ips_alg_enc)) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug: ipsec_alg_sa_wipe() :"
++ "unlinking for encalg=%d\n",
++ ixt->ixt_alg_id);
++ ipsec_alg_put(ixt);
++ }
++ if ((ixt=(struct ipsec_alg *)sa_p->ips_alg_auth)) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug: ipsec_alg_sa_wipe() :"
++ "unlinking for authalg=%d\n",
++ ixt->ixt_alg_id);
++ ipsec_alg_put(ixt);
++ }
++ return 0;
++}
++
++IPSEC_PROCFS_DEBUG_NO_STATIC
++int
++ipsec_xform_get_info(char *buffer,
++ char **start,
++ off_t offset,
++ int length IPSEC_PROC_LAST_ARG)
++{
++ int len = 0;
++ off_t begin = 0;
++ int i;
++ struct list_head *head;
++ struct ipsec_alg *ixt;
++
++ KLIPS_PRINT(debug_tunnel & DB_TN_PROCFS,
++ "klips_debug:ipsec_tncfg_get_info: "
++ "buffer=0p%p, *start=0p%p, offset=%d, length=%d\n",
++ buffer,
++ *start,
++ (int)offset,
++ length);
++
++ for(i = 0, head = ipsec_alg_hash_table; i< IPSEC_ALG_HASHSZ; i++, head++)
++ {
++ struct list_head *p;
++ for (p=head->next; p!=head; p=p->next)
++ {
++ ixt = list_entry(p, struct ipsec_alg, ixt_list);
++ len += ipsec_snprintf(buffer+len, length-len,
++ "VERSION=%d TYPE=%d ID=%d NAME=%s REFCNT=%d ",
++ ixt->ixt_version, ixt->ixt_alg_type, ixt->ixt_alg_id,
++ ixt->ixt_name, ixt->ixt_refcnt);
++
++ len += ipsec_snprintf(buffer+len, length-len,
++ "STATE=%08x BLOCKSIZE=%d IVLEN=%d KEYMINBITS=%d KEYMAXBITS=%d ",
++ ixt->ixt_state, ixt->ixt_blocksize,
++ ixt->ixt_ivlen, ixt->ixt_keyminbits, ixt->ixt_keymaxbits);
++
++ len += ipsec_snprintf(buffer+len, length-len,
++ "IVLEN=%d KEYMINBITS=%d KEYMAXBITS=%d ",
++ ixt->ixt_ivlen, ixt->ixt_keyminbits, ixt->ixt_keymaxbits);
++
++ switch(ixt->ixt_alg_type)
++ {
++ case IPSEC_ALG_TYPE_AUTH:
++ {
++ struct ipsec_alg_auth *auth = (struct ipsec_alg_auth *)ixt;
++
++ len += ipsec_snprintf(buffer+len, length-len,
++ "KEYLEN=%d CTXSIZE=%d AUTHLEN=%d ",
++ auth->ixt_a_keylen, auth->ixt_a_ctx_size,
++ auth->ixt_a_authlen);
++ break;
++ }
++ case IPSEC_ALG_TYPE_ENCRYPT:
++ {
++ struct ipsec_alg_enc *enc = (struct ipsec_alg_enc *)ixt;
++ len += ipsec_snprintf(buffer+len, length-len,
++ "KEYLEN=%d CTXSIZE=%d ",
++ enc->ixt_e_keylen, enc->ixt_e_ctx_size);
++
++ break;
++ }
++ }
++
++ len += ipsec_snprintf(buffer+len, length-len, "\n");
++ }
++ }
++
++ *start = buffer + (offset - begin); /* Start of wanted data */
++ len -= (offset - begin); /* Start slop */
++ if (len > length)
++ len = length;
++ return len;
++}
++
++
++/*
++ * As the author of this module, I ONLY ALLOW using it from
++ * GPL (or same LICENSE TERMS as kernel source) modules.
++ *
++ * In respect to hardware crypto engines this means:
++ * * Closed-source device drivers ARE NOT ALLOWED to use
++ * this interface.
++ * * Closed-source VHDL/Verilog firmware running on
++ * the crypto hardware device IS ALLOWED to use this interface
++ * via a GPL (or same LICENSE TERMS as kernel source) device driver.
++ * --Juan Jose Ciarlante 20/03/2002 (thanks RGB for the correct wording)
++ */
++
++/*
++ * These symbols can only be used from GPL modules
++ * for now, I'm disabling this because it creates false
++ * symbol problems for old modutils.
++ */
++
++/* #ifndef EXPORT_SYMBOL_GPL */
++#undef EXPORT_SYMBOL_GPL
++#define EXPORT_SYMBOL_GPL EXPORT_SYMBOL
++/* #endif */
++EXPORT_SYMBOL_GPL(register_ipsec_alg);
++EXPORT_SYMBOL_GPL(unregister_ipsec_alg);
++EXPORT_SYMBOL_GPL(ipsec_alg_test);
++#endif /* CONFIG_IPSEC_ALG */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/net/ipsec/ipsec_alg_aes.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,263 @@
++/*
++ * ipsec_alg AES cipher stubs
++ *
++ * Author: JuanJo Ciarlante <jjo-ipsec@mendoza.gov.ar>
++ *
++ * ipsec_alg_aes.c,v 1.1.2.1 2003/11/21 18:12:23 jjo Exp
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ * Fixes by:
++ * PK: Pawel Krawczyk <kravietz@aba.krakow.pl>
++ * Fixes list:
++ * PK: make XCBC comply with latest draft (keylength)
++ *
++ */
++#include <linux/config.h>
++#include <linux/version.h>
++
++/*
++ * special case: ipsec core modular with this static algo inside:
++ * must avoid MODULE magic for this file
++ */
++#if CONFIG_IPSEC_MODULE && CONFIG_IPSEC_ALG_AES
++#undef MODULE
++#endif
++
++#include <linux/module.h>
++#include <linux/init.h>
++
++#include <linux/kernel.h> /* printk() */
++#include <linux/errno.h> /* error codes */
++#include <linux/types.h> /* size_t */
++#include <linux/string.h>
++
++/* Check if __exit is defined, if not null it */
++#ifndef __exit
++#define __exit
++#endif
++
++/* Low freeswan header coupling */
++#include "openswan/ipsec_alg.h"
++#include "crypto/aes_cbc.h"
++
++#define CONFIG_IPSEC_ALG_AES_MAC 1
++
++#define AES_CONTEXT_T aes_context
++MODULE_AUTHOR("JuanJo Ciarlante <jjo-ipsec@mendoza.gov.ar>");
++static int debug=0;
++MODULE_PARM(debug, "i");
++static int test=0;
++MODULE_PARM(test, "i");
++static int excl=0;
++MODULE_PARM(excl, "i");
++static int keyminbits=0;
++MODULE_PARM(keyminbits, "i");
++static int keymaxbits=0;
++MODULE_PARM(keymaxbits, "i");
++
++#if CONFIG_IPSEC_ALG_AES_MAC
++#include "crypto/aes_xcbc_mac.h"
++
++/*
++ * Not IANA number yet (draft-ietf-ipsec-ciph-aes-xcbc-mac-00.txt).
++ * We use 9 for non-modular algorithm and none for modular, thus
++ * forcing user to specify one on module load. -kravietz
++ */
++#ifdef MODULE
++static int auth_id=0;
++#else
++static int auth_id=9;
++#endif
++MODULE_PARM(auth_id, "i");
++#endif
++
++#define ESP_AES 12 /* truely _constant_ :) */
++
++/* 128, 192 or 256 */
++#define ESP_AES_KEY_SZ_MIN 16 /* 128 bit secret key */
++#define ESP_AES_KEY_SZ_MAX 32 /* 256 bit secret key */
++#define ESP_AES_CBC_BLK_LEN 16 /* AES-CBC block size */
++
++/* Values according to draft-ietf-ipsec-ciph-aes-xcbc-mac-02.txt
++ * -kravietz
++ */
++#define ESP_AES_MAC_KEY_SZ 16 /* 128 bit MAC key */
++#define ESP_AES_MAC_BLK_LEN 16 /* 128 bit block */
++
++static int _aes_set_key(struct ipsec_alg_enc *alg, __u8 * key_e, const __u8 * key, size_t keysize) {
++ int ret;
++ AES_CONTEXT_T *ctx=(AES_CONTEXT_T*)key_e;
++ ret=AES_set_key(ctx, key, keysize)!=0? 0: -EINVAL;
++ if (debug > 0)
++ printk(KERN_DEBUG "klips_debug:_aes_set_key:"
++ "ret=%d key_e=%p key=%p keysize=%d\n",
++ ret, key_e, key, keysize);
++ return ret;
++}
++static int _aes_cbc_encrypt(struct ipsec_alg_enc *alg, __u8 * key_e, __u8 * in, int ilen, const __u8 * iv, int encrypt) {
++ AES_CONTEXT_T *ctx=(AES_CONTEXT_T*)key_e;
++ if (debug > 0)
++ printk(KERN_DEBUG "klips_debug:_aes_cbc_encrypt:"
++ "key_e=%p in=%p ilen=%d iv=%p encrypt=%d\n",
++ key_e, in, ilen, iv, encrypt);
++ return AES_cbc_encrypt(ctx, in, in, ilen, iv, encrypt);
++}
++#if CONFIG_IPSEC_ALG_AES_MAC
++static int _aes_mac_set_key(struct ipsec_alg_auth *alg, __u8 * key_a, const __u8 * key, int keylen) {
++ aes_context_mac *ctxm=(aes_context_mac *)key_a;
++ return AES_xcbc_mac_set_key(ctxm, key, keylen)? 0 : -EINVAL;
++}
++static int _aes_mac_hash(struct ipsec_alg_auth *alg, __u8 * key_a, const __u8 * dat, int len, __u8 * hash, int hashlen) {
++ int ret;
++ char hash_buf[16];
++ aes_context_mac *ctxm=(aes_context_mac *)key_a;
++ ret=AES_xcbc_mac_hash(ctxm, dat, len, hash_buf);
++ memcpy(hash, hash_buf, hashlen);
++ return ret;
++}
++static struct ipsec_alg_auth ipsec_alg_AES_MAC = {
++ ixt_version: IPSEC_ALG_VERSION,
++ ixt_module: THIS_MODULE,
++ ixt_refcnt: ATOMIC_INIT(0),
++ ixt_alg_type: IPSEC_ALG_TYPE_AUTH,
++ ixt_alg_id: 0,
++ ixt_name: "aes_mac",
++ ixt_blocksize: ESP_AES_MAC_BLK_LEN,
++ ixt_keyminbits: ESP_AES_MAC_KEY_SZ*8,
++ ixt_keymaxbits: ESP_AES_MAC_KEY_SZ*8,
++ ixt_a_keylen: ESP_AES_MAC_KEY_SZ,
++ ixt_a_ctx_size: sizeof(aes_context_mac),
++ ixt_a_hmac_set_key: _aes_mac_set_key,
++ ixt_a_hmac_hash:_aes_mac_hash,
++};
++#endif /* CONFIG_IPSEC_ALG_AES_MAC */
++static struct ipsec_alg_enc ipsec_alg_AES = {
++ ixt_version: IPSEC_ALG_VERSION,
++ ixt_module: THIS_MODULE,
++ ixt_refcnt: ATOMIC_INIT(0),
++ ixt_alg_type: IPSEC_ALG_TYPE_ENCRYPT,
++ ixt_alg_id: ESP_AES,
++ ixt_name: "aes",
++ ixt_blocksize: ESP_AES_CBC_BLK_LEN,
++ ixt_keyminbits: ESP_AES_KEY_SZ_MIN*8,
++ ixt_keymaxbits: ESP_AES_KEY_SZ_MAX*8,
++ ixt_e_keylen: ESP_AES_KEY_SZ_MAX,
++ ixt_e_ctx_size: sizeof(AES_CONTEXT_T),
++ ixt_e_set_key: _aes_set_key,
++ ixt_e_cbc_encrypt:_aes_cbc_encrypt,
++};
++
++#if defined(CONFIG_IPSEC_ENC_AES_MODULE)
++IPSEC_ALG_MODULE_INIT_MOD( ipsec_aes_init )
++#else
++IPSEC_ALG_MODULE_INIT_STATIC( ipsec_aes_init )
++#endif
++{
++ int ret, test_ret;
++
++ if (keyminbits)
++ ipsec_alg_AES.ixt_keyminbits=keyminbits;
++ if (keymaxbits) {
++ ipsec_alg_AES.ixt_keymaxbits=keymaxbits;
++ if (keymaxbits*8>ipsec_alg_AES.ixt_keymaxbits)
++ ipsec_alg_AES.ixt_e_keylen=keymaxbits*8;
++ }
++ if (excl) ipsec_alg_AES.ixt_state |= IPSEC_ALG_ST_EXCL;
++ ret=register_ipsec_alg_enc(&ipsec_alg_AES);
++ printk("ipsec_aes_init(alg_type=%d alg_id=%d name=%s): ret=%d\n",
++ ipsec_alg_AES.ixt_alg_type,
++ ipsec_alg_AES.ixt_alg_id,
++ ipsec_alg_AES.ixt_name,
++ ret);
++ if (ret==0 && test) {
++ test_ret=ipsec_alg_test(
++ ipsec_alg_AES.ixt_alg_type,
++ ipsec_alg_AES.ixt_alg_id,
++ test);
++ printk("ipsec_aes_init(alg_type=%d alg_id=%d): test_ret=%d\n",
++ ipsec_alg_AES.ixt_alg_type,
++ ipsec_alg_AES.ixt_alg_id,
++ test_ret);
++ }
++#if CONFIG_IPSEC_ALG_AES_MAC
++ if (auth_id!=0){
++ int ret;
++ ipsec_alg_AES_MAC.ixt_alg_id=auth_id;
++ ret=register_ipsec_alg_auth(&ipsec_alg_AES_MAC);
++ printk("ipsec_aes_init(alg_type=%d alg_id=%d name=%s): ret=%d\n",
++ ipsec_alg_AES_MAC.ixt_alg_type,
++ ipsec_alg_AES_MAC.ixt_alg_id,
++ ipsec_alg_AES_MAC.ixt_name,
++ ret);
++ if (ret==0 && test) {
++ test_ret=ipsec_alg_test(
++ ipsec_alg_AES_MAC.ixt_alg_type,
++ ipsec_alg_AES_MAC.ixt_alg_id,
++ test);
++ printk("ipsec_aes_init(alg_type=%d alg_id=%d): test_ret=%d\n",
++ ipsec_alg_AES_MAC.ixt_alg_type,
++ ipsec_alg_AES_MAC.ixt_alg_id,
++ test_ret);
++ }
++ } else {
++ printk(KERN_DEBUG "klips_debug: experimental ipsec_alg_AES_MAC not registered [Ok] (auth_id=%d)\n", auth_id);
++ }
++#endif /* CONFIG_IPSEC_ALG_AES_MAC */
++ return ret;
++}
++
++#if defined(CONFIG_IPSEC_ENC_AES_MODULE)
++IPSEC_ALG_MODULE_EXIT_MOD( ipsec_aes_fini )
++#else
++IPSEC_ALG_MODULE_EXIT_STATIC( ipsec_aes_fini )
++#endif
++{
++#if CONFIG_IPSEC_ALG_AES_MAC
++ if (auth_id) unregister_ipsec_alg_auth(&ipsec_alg_AES_MAC);
++#endif /* CONFIG_IPSEC_ALG_AES_MAC */
++ unregister_ipsec_alg_enc(&ipsec_alg_AES);
++ return;
++}
++#ifdef MODULE_LICENSE
++MODULE_LICENSE("GPL");
++#endif
++
++#if 0+NOT_YET
++#ifndef MODULE
++/*
++ * This is intended for static module setups, currently
++ * doesn't work for modular ipsec.o with static algos inside
++ */
++static int setup_keybits(const char *str)
++{
++ unsigned aux;
++ char *end;
++
++ aux = simple_strtoul(str,&end,0);
++ if (aux != 128 && aux != 192 && aux != 256)
++ return 0;
++ keyminbits = aux;
++
++ if (*end == 0 || *end != ',')
++ return 1;
++ str=end+1;
++ aux = simple_strtoul(str, NULL, 0);
++ if (aux != 128 && aux != 192 && aux != 256)
++ return 0;
++ if (aux >= keyminbits)
++ keymaxbits = aux;
++ return 1;
++}
++__setup("ipsec_aes_keybits=", setup_keybits);
++#endif
++#endif
++EXPORT_NO_SYMBOLS;
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/net/ipsec/ipsec_alg_cryptoapi.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,421 @@
++/*
++ * ipsec_alg to linux cryptoapi GLUE
++ *
++ * Authors: CODE.ar TEAM
++ * Harpo MAxx <harpo@linuxmendoza.org.ar>
++ * JuanJo Ciarlante <jjo-ipsec@mendoza.gov.ar>
++ * Luciano Ruete <docemeses@softhome.net>
++ *
++ * ipsec_alg_cryptoapi.c,v 1.1.2.1 2003/11/21 18:12:23 jjo Exp
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ * Example usage:
++ * modinfo -p ipsec_cryptoapi (quite useful info, including supported algos)
++ * modprobe ipsec_cryptoapi
++ * modprobe ipsec_cryptoapi test=1
++ * modprobe ipsec_cryptoapi excl=1 (exclusive cipher/algo)
++ * modprobe ipsec_cryptoapi noauto=1 aes=1 twofish=1 (only these ciphers)
++ * modprobe ipsec_cryptoapi aes=128,128 (force these keylens)
++ * modprobe ipsec_cryptoapi des_ede3=0 (everything but 3DES)
++ */
++#include <linux/config.h>
++#include <linux/version.h>
++
++/*
++ * special case: ipsec core modular with this static algo inside:
++ * must avoid MODULE magic for this file
++ */
++#if CONFIG_IPSEC_MODULE && CONFIG_IPSEC_ALG_CRYPTOAPI
++#undef MODULE
++#endif
++
++#include <linux/module.h>
++#include <linux/init.h>
++
++#include <linux/kernel.h> /* printk() */
++#include <linux/errno.h> /* error codes */
++#include <linux/types.h> /* size_t */
++#include <linux/string.h>
++
++/* Check if __exit is defined, if not null it */
++#ifndef __exit
++#define __exit
++#endif
++
++/* warn the innocent */
++#if !defined (CONFIG_CRYPTO) && !defined (CONFIG_CRYPTO_MODULE)
++#warning "No linux CryptoAPI found, install 2.4.22+ or 2.6.x"
++#define NO_CRYPTOAPI_SUPPORT
++#endif
++/* Low Openswan header coupling */
++#include "openswan/ipsec_alg.h"
++
++#include <linux/crypto.h>
++#ifdef CRYPTO_API_VERSION_CODE
++#warning "Old CryptoAPI is not supported. Only linux-2.4.22+ or linux-2.6.x are supported"
++#define NO_CRYPTOAPI_SUPPORT
++#endif
++
++#ifdef NO_CRYPTOAPI_SUPPORT
++#warning "Building an unusable module :P"
++/* Catch old CryptoAPI by not allowing module to load */
++IPSEC_ALG_MODULE_INIT_STATIC( ipsec_cryptoapi_init )
++{
++ printk(KERN_WARNING "ipsec_cryptoapi.o was not built on stock Linux CryptoAPI (2.4.22+ or 2.6.x), not loading.\n");
++ return -EINVAL;
++}
++#else
++#include <asm/scatterlist.h>
++#include <asm/pgtable.h>
++#include <linux/mm.h>
++
++#define CIPHERNAME_AES "aes"
++#define CIPHERNAME_3DES "des3_ede"
++#define CIPHERNAME_BLOWFISH "blowfish"
++#define CIPHERNAME_CAST "cast5"
++#define CIPHERNAME_SERPENT "serpent"
++#define CIPHERNAME_TWOFISH "twofish"
++
++#define ESP_3DES 3
++#define ESP_AES 12
++#define ESP_BLOWFISH 7 /* truely _constant_ :) */
++#define ESP_CAST 6 /* quite constant :) */
++#define ESP_SERPENT 252 /* from ipsec drafts */
++#define ESP_TWOFISH 253 /* from ipsec drafts */
++
++#define AH_MD5 2
++#define AH_SHA 3
++#define DIGESTNAME_MD5 "md5"
++#define DIGESTNAME_SHA1 "sha1"
++
++MODULE_AUTHOR("Juanjo Ciarlante, Harpo MAxx, Luciano Ruete");
++static int debug=0;
++MODULE_PARM(debug, "i");
++static int test=0;
++MODULE_PARM(test, "i");
++static int excl=0;
++MODULE_PARM(excl, "i");
++
++static int noauto = 0;
++MODULE_PARM(noauto,"i");
++MODULE_PARM_DESC(noauto, "Dont try all known algos, just setup enabled ones");
++
++static int des_ede3[] = {-1, -1};
++static int aes[] = {-1, -1};
++static int blowfish[] = {-1, -1};
++static int cast[] = {-1, -1};
++static int serpent[] = {-1, -1};
++static int twofish[] = {-1, -1};
++
++MODULE_PARM(des_ede3,"1-2i");
++MODULE_PARM(aes,"1-2i");
++MODULE_PARM(blowfish,"1-2i");
++MODULE_PARM(cast,"1-2i");
++MODULE_PARM(serpent,"1-2i");
++MODULE_PARM(twofish,"1-2i");
++MODULE_PARM_DESC(des_ede3, "0: disable | 1: force_enable | min,max: dontuse");
++MODULE_PARM_DESC(aes, "0: disable | 1: force_enable | min,max: keybitlens");
++MODULE_PARM_DESC(blowfish, "0: disable | 1: force_enable | min,max: keybitlens");
++MODULE_PARM_DESC(cast, "0: disable | 1: force_enable | min,max: keybitlens");
++MODULE_PARM_DESC(serpent, "0: disable | 1: force_enable | min,max: keybitlens");
++MODULE_PARM_DESC(twofish, "0: disable | 1: force_enable | min,max: keybitlens");
++
++struct ipsec_alg_capi_cipher {
++ const char *ciphername; /* cryptoapi's ciphername */
++ unsigned blocksize;
++ unsigned short minbits;
++ unsigned short maxbits;
++ int *parm; /* lkm param for this cipher */
++ struct ipsec_alg_enc alg; /* note it's not a pointer */
++};
++static struct ipsec_alg_capi_cipher alg_capi_carray[] = {
++ { CIPHERNAME_AES , 16, 128, 256, aes , { ixt_alg_id: ESP_AES, }},
++ { CIPHERNAME_TWOFISH , 16, 128, 256, twofish, { ixt_alg_id: ESP_TWOFISH, }},
++ { CIPHERNAME_SERPENT , 16, 128, 256, serpent, { ixt_alg_id: ESP_SERPENT, }},
++ { CIPHERNAME_CAST , 8, 128, 128, cast , { ixt_alg_id: ESP_CAST, }},
++ { CIPHERNAME_BLOWFISH , 8, 96, 448, blowfish,{ ixt_alg_id: ESP_BLOWFISH, }},
++ { CIPHERNAME_3DES , 8, 192, 192, des_ede3,{ ixt_alg_id: ESP_3DES, }},
++ { NULL, 0, 0, 0, NULL, {} }
++};
++#ifdef NOT_YET
++struct ipsec_alg_capi_digest {
++ const char *digestname; /* cryptoapi's digestname */
++ struct digest_implementation *di;
++ struct ipsec_alg_auth alg; /* note it's not a pointer */
++};
++static struct ipsec_alg_capi_cipher alg_capi_darray[] = {
++ { DIGESTNAME_MD5, NULL, { ixt_alg_id: AH_MD5, }},
++ { DIGESTNAME_SHA1, NULL, { ixt_alg_id: AH_SHA, }},
++ { NULL, NULL, {} }
++};
++#endif
++/*
++ * "generic" linux cryptoapi setup_cipher() function
++ */
++int setup_cipher(const char *ciphername)
++{
++ return crypto_alg_available(ciphername, 0);
++}
++
++/*
++ * setups ipsec_alg_capi_cipher "hyper" struct components, calling
++ * register_ipsec_alg for cointaned ipsec_alg object
++ */
++static void _capi_destroy_key (struct ipsec_alg_enc *alg, __u8 *key_e);
++static __u8 * _capi_new_key (struct ipsec_alg_enc *alg, const __u8 *key, size_t keylen);
++static int _capi_cbc_encrypt(struct ipsec_alg_enc *alg, __u8 * key_e, __u8 * in, int ilen, const __u8 * iv, int encrypt);
++
++static int
++setup_ipsec_alg_capi_cipher(struct ipsec_alg_capi_cipher *cptr)
++{
++ int ret;
++ cptr->alg.ixt_version = IPSEC_ALG_VERSION;
++ cptr->alg.ixt_module = THIS_MODULE;
++ atomic_set (& cptr->alg.ixt_refcnt, 0);
++ strncpy (cptr->alg.ixt_name , cptr->ciphername, sizeof (cptr->alg.ixt_name));
++
++ cptr->alg.ixt_blocksize=cptr->blocksize;
++ cptr->alg.ixt_keyminbits=cptr->minbits;
++ cptr->alg.ixt_keymaxbits=cptr->maxbits;
++ cptr->alg.ixt_state = 0;
++ if (excl) cptr->alg.ixt_state |= IPSEC_ALG_ST_EXCL;
++ cptr->alg.ixt_e_keylen=cptr->alg.ixt_keymaxbits/8;
++ cptr->alg.ixt_e_ctx_size = 0;
++ cptr->alg.ixt_alg_type = IPSEC_ALG_TYPE_ENCRYPT;
++ cptr->alg.ixt_e_new_key = _capi_new_key;
++ cptr->alg.ixt_e_destroy_key = _capi_destroy_key;
++ cptr->alg.ixt_e_cbc_encrypt = _capi_cbc_encrypt;
++ cptr->alg.ixt_data = cptr;
++
++ ret=register_ipsec_alg_enc(&cptr->alg);
++ printk("setup_ipsec_alg_capi_cipher(): "
++ "alg_type=%d alg_id=%d name=%s "
++ "keyminbits=%d keymaxbits=%d, ret=%d\n",
++ cptr->alg.ixt_alg_type,
++ cptr->alg.ixt_alg_id,
++ cptr->alg.ixt_name,
++ cptr->alg.ixt_keyminbits,
++ cptr->alg.ixt_keymaxbits,
++ ret);
++ return ret;
++}
++/*
++ * called in ipsec_sa_wipe() time, will destroy key contexts
++ * and do 1 unbind()
++ */
++static void
++_capi_destroy_key (struct ipsec_alg_enc *alg, __u8 *key_e)
++{
++ struct crypto_tfm *tfm=(struct crypto_tfm*)key_e;
++
++ if (debug > 0)
++ printk(KERN_DEBUG "klips_debug: _capi_destroy_key:"
++ "name=%s key_e=%p \n",
++ alg->ixt_name, key_e);
++ if (!key_e) {
++ printk(KERN_ERR "klips_debug: _capi_destroy_key:"
++ "name=%s NULL key_e!\n",
++ alg->ixt_name);
++ return;
++ }
++ crypto_free_tfm(tfm);
++}
++
++/*
++ * create new key context, need alg->ixt_data to know which
++ * (of many) cipher inside this module is the target
++ */
++static __u8 *
++_capi_new_key (struct ipsec_alg_enc *alg, const __u8 *key, size_t keylen)
++{
++ struct ipsec_alg_capi_cipher *cptr;
++ struct crypto_tfm *tfm=NULL;
++
++ cptr = alg->ixt_data;
++ if (!cptr) {
++ printk(KERN_ERR "_capi_new_key(): "
++ "NULL ixt_data (?!) for \"%s\" algo\n"
++ , alg->ixt_name);
++ goto err;
++ }
++ if (debug > 0)
++ printk(KERN_DEBUG "klips_debug:_capi_new_key:"
++ "name=%s cptr=%p key=%p keysize=%d\n",
++ alg->ixt_name, cptr, key, keylen);
++
++ /*
++ * alloc tfm
++ */
++ tfm = crypto_alloc_tfm(cptr->ciphername, CRYPTO_TFM_MODE_CBC);
++ if (!tfm) {
++ printk(KERN_ERR "_capi_new_key(): "
++ "NULL tfm for \"%s\" cryptoapi (\"%s\") algo\n"
++ , alg->ixt_name, cptr->ciphername);
++ goto err;
++ }
++ if (crypto_cipher_setkey(tfm, key, keylen) < 0) {
++ printk(KERN_ERR "_capi_new_key(): "
++ "failed new_key() for \"%s\" cryptoapi algo (keylen=%d)\n"
++ , alg->ixt_name, keylen);
++ crypto_free_tfm(tfm);
++ tfm=NULL;
++ }
++err:
++ if (debug > 0)
++ printk(KERN_DEBUG "klips_debug:_capi_new_key:"
++ "name=%s key=%p keylen=%d tfm=%p\n",
++ alg->ixt_name, key, keylen, tfm);
++ return (__u8 *) tfm;
++}
++/*
++ * core encryption function: will use cx->ci to call actual cipher's
++ * cbc function
++ */
++static int
++_capi_cbc_encrypt(struct ipsec_alg_enc *alg, __u8 * key_e, __u8 * in, int ilen, const __u8 * iv, int encrypt) {
++ int error =0;
++ struct crypto_tfm *tfm=(struct crypto_tfm *)key_e;
++ struct scatterlist sg = {
++ .page = virt_to_page(in),
++ .offset = (unsigned long)(in) % PAGE_SIZE,
++ .length=ilen,
++ };
++ if (debug > 1)
++ printk(KERN_DEBUG "klips_debug:_capi_cbc_encrypt:"
++ "key_e=%p "
++ "in=%p out=%p ilen=%d iv=%p encrypt=%d\n"
++ , key_e
++ , in, in, ilen, iv, encrypt);
++ crypto_cipher_set_iv(tfm, iv, crypto_tfm_alg_ivsize(tfm));
++ if (encrypt)
++ error = crypto_cipher_encrypt (tfm, &sg, &sg, ilen);
++ else
++ error = crypto_cipher_decrypt (tfm, &sg, &sg, ilen);
++ if (debug > 1)
++ printk(KERN_DEBUG "klips_debug:_capi_cbc_encrypt:"
++ "error=%d\n"
++ , error);
++ return (error<0)? error : ilen;
++}
++/*
++ * main initialization loop: for each cipher in list, do
++ * 1) setup cryptoapi cipher else continue
++ * 2) register ipsec_alg object
++ */
++static int
++setup_cipher_list (struct ipsec_alg_capi_cipher* clist)
++{
++ struct ipsec_alg_capi_cipher *cptr;
++ /* foreach cipher in list ... */
++ for (cptr=clist;cptr->ciphername;cptr++) {
++ /*
++ * see if cipher has been disabled (0) or
++ * if noauto set and not enabled (1)
++ */
++ if (cptr->parm[0] == 0 || (noauto && cptr->parm[0] < 0)) {
++ if (debug>0)
++ printk(KERN_INFO "setup_cipher_list(): "
++ "ciphername=%s skipped at user request: "
++ "noauto=%d parm[0]=%d parm[1]=%d\n"
++ , cptr->ciphername
++ , noauto
++ , cptr->parm[0]
++ , cptr->parm[1]);
++ continue;
++ }
++ /*
++ * use a local ci to avoid touching cptr->ci,
++ * if register ipsec_alg success then bind cipher
++ */
++ if( setup_cipher(cptr->ciphername) ) {
++ if (debug > 0)
++ printk(KERN_DEBUG "klips_debug:"
++ "setup_cipher_list():"
++ "ciphername=%s found\n"
++ , cptr->ciphername);
++ if (setup_ipsec_alg_capi_cipher(cptr) == 0) {
++
++
++ } else {
++ printk(KERN_ERR "klips_debug:"
++ "setup_cipher_list():"
++ "ciphername=%s failed ipsec_alg_register\n"
++ , cptr->ciphername);
++ }
++ } else {
++ if (debug>0)
++ printk(KERN_INFO "setup_cipher_list(): lookup for ciphername=%s: not found \n",
++ cptr->ciphername);
++ }
++ }
++ return 0;
++}
++/*
++ * deregister ipsec_alg objects and unbind ciphers
++ */
++static int
++unsetup_cipher_list (struct ipsec_alg_capi_cipher* clist)
++{
++ struct ipsec_alg_capi_cipher *cptr;
++ /* foreach cipher in list ... */
++ for (cptr=clist;cptr->ciphername;cptr++) {
++ if (cptr->alg.ixt_state & IPSEC_ALG_ST_REGISTERED) {
++ unregister_ipsec_alg_enc(&cptr->alg);
++ }
++ }
++ return 0;
++}
++/*
++ * test loop for registered algos
++ */
++static int
++test_cipher_list (struct ipsec_alg_capi_cipher* clist)
++{
++ int test_ret;
++ struct ipsec_alg_capi_cipher *cptr;
++ /* foreach cipher in list ... */
++ for (cptr=clist;cptr->ciphername;cptr++) {
++ if (cptr->alg.ixt_state & IPSEC_ALG_ST_REGISTERED) {
++ test_ret=ipsec_alg_test(
++ cptr->alg.ixt_alg_type,
++ cptr->alg.ixt_alg_id,
++ test);
++ printk("test_cipher_list(alg_type=%d alg_id=%d): test_ret=%d\n",
++ cptr->alg.ixt_alg_type,
++ cptr->alg.ixt_alg_id,
++ test_ret);
++ }
++ }
++ return 0;
++}
++
++IPSEC_ALG_MODULE_INIT_STATIC( ipsec_cryptoapi_init )
++{
++ int ret, test_ret;
++ if ((ret=setup_cipher_list(alg_capi_carray)) < 0)
++ return -EPROTONOSUPPORT;
++ if (ret==0 && test) {
++ test_ret=test_cipher_list(alg_capi_carray);
++ }
++ return ret;
++}
++IPSEC_ALG_MODULE_EXIT_STATIC( ipsec_cryptoapi_fini )
++{
++ unsetup_cipher_list(alg_capi_carray);
++ return;
++}
++#ifdef MODULE_LICENSE
++MODULE_LICENSE("GPL");
++#endif
++
++EXPORT_NO_SYMBOLS;
++#endif /* NO_CRYPTOAPI_SUPPORT */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/net/ipsec/ipsec_esp.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,558 @@
++/*
++ * processing code for ESP
++ * Copyright (C) 2003 Michael Richardson <mcr@sandelman.ottawa.on.ca>
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ */
++
++char ipsec_esp_c_version[] = "RCSID $Id$";
++#include <linux/config.h>
++#include <linux/version.h>
++
++#define __NO_VERSION__
++#include <linux/module.h>
++#include <linux/kernel.h> /* printk() */
++
++#include "openswan/ipsec_param.h"
++
++#ifdef MALLOC_SLAB
++# include <linux/slab.h> /* kmalloc() */
++#else /* MALLOC_SLAB */
++# include <linux/malloc.h> /* kmalloc() */
++#endif /* MALLOC_SLAB */
++#include <linux/errno.h> /* error codes */
++#include <linux/types.h> /* size_t */
++#include <linux/interrupt.h> /* mark_bh */
++
++#include <linux/netdevice.h> /* struct device, and other headers */
++#include <linux/etherdevice.h> /* eth_type_trans */
++#include <linux/ip.h> /* struct iphdr */
++#include <linux/skbuff.h>
++#include <openswan.h>
++#ifdef SPINLOCK
++# ifdef SPINLOCK_23
++# include <linux/spinlock.h> /* *lock* */
++# else /* SPINLOCK_23 */
++# include <asm/spinlock.h> /* *lock* */
++# endif /* SPINLOCK_23 */
++#endif /* SPINLOCK */
++#ifdef NET_21
++# include <asm/uaccess.h>
++# include <linux/in6.h>
++# define proto_priv cb
++#endif /* NET21 */
++#include <asm/checksum.h>
++#include <net/ip.h>
++
++#include "openswan/radij.h"
++#include "openswan/ipsec_encap.h"
++#include "openswan/ipsec_sa.h"
++
++#include "openswan/ipsec_radij.h"
++#include "openswan/ipsec_xform.h"
++#include "openswan/ipsec_tunnel.h"
++#include "openswan/ipsec_rcv.h"
++#include "openswan/ipsec_xmit.h"
++
++#include "openswan/ipsec_auth.h"
++
++#ifdef CONFIG_IPSEC_ESP
++#include "openswan/ipsec_esp.h"
++#endif /* CONFIG_IPSEC_ESP */
++
++#include "openswan/ipsec_proto.h"
++#include "openswan/ipsec_alg.h"
++
++#ifdef CONFIG_IPSEC_DEBUG
++int debug_esp = 0;
++#endif /* CONFIG_IPSEC_DEBUG */
++
++
++#ifdef CONFIG_IPSEC_ESP
++enum ipsec_rcv_value
++ipsec_rcv_esp_checks(struct ipsec_rcv_state *irs,
++ struct sk_buff *skb)
++{
++ __u8 proto;
++ int len; /* packet length */
++
++ len = skb->len;
++ proto = irs->ipp->protocol;
++
++ /* XXX this will need to be 8 for IPv6 */
++ if ((proto == IPPROTO_ESP) && ((len - irs->iphlen) % 4)) {
++ printk("klips_error:ipsec_rcv: "
++ "got packet with content length = %d from %s -- should be on 4 octet boundary, packet dropped\n",
++ len - irs->iphlen,
++ irs->ipsaddr_txt);
++ if(irs->stats) {
++ irs->stats->rx_errors++;
++ }
++ return IPSEC_RCV_BADLEN;
++ }
++
++ if(skb->len < (irs->hard_header_len + sizeof(struct iphdr) + sizeof(struct esphdr))) {
++ KLIPS_PRINT(debug_rcv & DB_RX_INAU,
++ "klips_debug:ipsec_rcv: "
++ "runt esp packet of skb->len=%d received from %s, dropped.\n",
++ skb->len,
++ irs->ipsaddr_txt);
++ if(irs->stats) {
++ irs->stats->rx_errors++;
++ }
++ return IPSEC_RCV_BADLEN;
++ }
++
++ irs->protostuff.espstuff.espp = (struct esphdr *)(skb->data + irs->iphlen);
++ irs->said.spi = irs->protostuff.espstuff.espp->esp_spi;
++
++ return IPSEC_RCV_OK;
++}
++
++enum ipsec_rcv_value
++ipsec_rcv_esp_decrypt_setup(struct ipsec_rcv_state *irs,
++ struct sk_buff *skb,
++ __u32 *replay,
++ unsigned char **authenticator)
++{
++ struct esphdr *espp = irs->protostuff.espstuff.espp;
++
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "packet from %s received with seq=%d (iv)=0x%08x%08x iplen=%d esplen=%d sa=%s\n",
++ irs->ipsaddr_txt,
++ (__u32)ntohl(espp->esp_rpl),
++ (__u32)ntohl(*((__u32 *)(espp->esp_iv) )),
++ (__u32)ntohl(*((__u32 *)(espp->esp_iv) + 1)),
++ irs->len,
++ irs->ilen,
++ irs->sa_len ? irs->sa : " (error)");
++
++ *replay = ntohl(espp->esp_rpl);
++ *authenticator = &(skb->data[irs->len - irs->authlen]);
++
++ return IPSEC_RCV_OK;
++}
++
++enum ipsec_rcv_value
++ipsec_rcv_esp_authcalc(struct ipsec_rcv_state *irs,
++ struct sk_buff *skb)
++{
++ struct auth_alg *aa;
++ struct esphdr *espp = irs->protostuff.espstuff.espp;
++ union {
++ MD5_CTX md5;
++ SHA1_CTX sha1;
++ } tctx;
++
++#ifdef CONFIG_IPSEC_ALG
++ if (irs->ipsp->ips_alg_auth) {
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "ipsec_alg hashing proto=%d... ",
++ irs->said.proto);
++ if(irs->said.proto == IPPROTO_ESP) {
++ ipsec_alg_sa_esp_hash(irs->ipsp,
++ (caddr_t)espp, irs->ilen,
++ irs->hash, AHHMAC_HASHLEN);
++ return IPSEC_RCV_OK;
++ }
++ return IPSEC_RCV_BADPROTO;
++ }
++#endif
++ aa = irs->authfuncs;
++
++ /* copy the initialized keying material */
++ memcpy(&tctx, irs->ictx, irs->ictx_len);
++
++ (*aa->update)((void *)&tctx, (caddr_t)espp, irs->ilen);
++
++ (*aa->final)(irs->hash, (void *)&tctx);
++
++ memcpy(&tctx, irs->octx, irs->octx_len);
++
++ (*aa->update)((void *)&tctx, irs->hash, aa->hashlen);
++ (*aa->final)(irs->hash, (void *)&tctx);
++
++ return IPSEC_RCV_OK;
++}
++
++
++enum ipsec_rcv_value
++ipsec_rcv_esp_decrypt(struct ipsec_rcv_state *irs)
++{
++ struct ipsec_sa *ipsp = irs->ipsp;
++ struct esphdr *espp = irs->protostuff.espstuff.espp;
++ int esphlen = 0;
++ __u8 *idat; /* pointer to content to be decrypted/authenticated */
++ __u32 iv[2];
++ int pad = 0, padlen;
++ int badpad = 0;
++ int i;
++ struct sk_buff *skb;
++#ifdef CONFIG_IPSEC_ALG
++ struct ipsec_alg_enc *ixt_e=NULL;
++#endif /* CONFIG_IPSEC_ALG */
++
++ skb=irs->skb;
++
++ idat = skb->data + irs->iphlen;
++
++#ifdef CONFIG_IPSEC_ALG
++ if ((ixt_e=ipsp->ips_alg_enc)) {
++ esphlen = ESP_HEADER_LEN + ixt_e->ixt_ivlen/8;
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "encalg=%d esphlen=%d\n",
++ ipsp->ips_encalg, esphlen);
++ } else
++#endif /* CONFIG_IPSEC_ALG */
++ switch(ipsp->ips_encalg) {
++ case ESP_3DES:
++ iv[0] = *((__u32 *)(espp->esp_iv) );
++ iv[1] = *((__u32 *)(espp->esp_iv) + 1);
++ esphlen = sizeof(struct esphdr);
++ break;
++ default:
++ ipsp->ips_errs.ips_alg_errs += 1;
++ if(irs->stats) {
++ irs->stats->rx_errors++;
++ }
++ return IPSEC_RCV_ESP_BADALG;
++ }
++
++ idat += esphlen;
++ irs->ilen -= esphlen;
++
++#ifdef CONFIG_IPSEC_ALG
++ if (ixt_e)
++ {
++ if (ipsec_alg_esp_encrypt(ipsp,
++ idat, irs->ilen, espp->esp_iv,
++ IPSEC_ALG_DECRYPT) <= 0)
++ {
++ printk("klips_error:ipsec_rcv: "
++ "got packet with esplen = %d "
++ "from %s -- should be on "
++ "ENC(%d) octet boundary, "
++ "packet dropped\n",
++ irs->ilen,
++ irs->ipsaddr_txt,
++ ipsp->ips_encalg);
++ if(irs->stats) {
++ irs->stats->rx_errors++;
++ }
++ return IPSEC_RCV_BAD_DECRYPT;
++ }
++ } else
++#endif /* CONFIG_IPSEC_ALG */
++ switch(ipsp->ips_encalg) {
++ case ESP_3DES:
++ if ((irs->ilen) % 8) {
++ ipsp->ips_errs.ips_encsize_errs += 1;
++ printk("klips_error:ipsec_rcv: "
++ "got packet with esplen = %d from %s -- should be on 8 octet boundary, packet dropped\n",
++ irs->ilen,
++ irs->ipsaddr_txt);
++ if(irs->stats) {
++ irs->stats->rx_errors++;
++ }
++ return IPSEC_RCV_3DES_BADBLOCKING;
++ }
++ des_ede3_cbc_encrypt((des_cblock *)idat,
++ (des_cblock *)idat,
++ irs->ilen,
++ ((struct des_eks *)(ipsp->ips_key_e))[0].ks,
++ ((struct des_eks *)(ipsp->ips_key_e))[1].ks,
++ ((struct des_eks *)(ipsp->ips_key_e))[2].ks,
++ (des_cblock *)iv, 0);
++ break;
++ }
++
++ ipsec_rcv_dmp("postdecrypt", skb->data, skb->len);
++
++ irs->next_header = idat[irs->ilen - 1];
++ padlen = idat[irs->ilen - 2];
++ pad = padlen + 2 + irs->authlen;
++
++ KLIPS_PRINT(debug_rcv & DB_RX_IPAD,
++ "klips_debug:ipsec_rcv: "
++ "padlen=%d, contents: 0x<offset>: 0x<value> 0x<value> ...\n",
++ padlen);
++
++ for (i = 1; i <= padlen; i++) {
++ if((i % 16) == 1) {
++ KLIPS_PRINT(debug_rcv & DB_RX_IPAD,
++ "klips_debug: %02x:",
++ i - 1);
++ }
++ KLIPS_PRINTMORE(debug_rcv & DB_RX_IPAD,
++ " %02x",
++ idat[irs->ilen - 2 - padlen + i - 1]);
++ if(i != idat[irs->ilen - 2 - padlen + i - 1]) {
++ badpad = 1;
++ }
++ if((i % 16) == 0) {
++ KLIPS_PRINTMORE(debug_rcv & DB_RX_IPAD,
++ "\n");
++ }
++ }
++ if((i % 16) != 1) {
++ KLIPS_PRINTMORE(debug_rcv & DB_RX_IPAD,
++ "\n");
++ }
++ if(badpad) {
++ KLIPS_PRINT(debug_rcv & DB_RX_IPAD,
++ "klips_debug:ipsec_rcv: "
++ "warning, decrypted packet from %s has bad padding\n",
++ irs->ipsaddr_txt);
++ KLIPS_PRINT(debug_rcv & DB_RX_IPAD,
++ "klips_debug:ipsec_rcv: "
++ "...may be bad decryption -- not dropped\n");
++ ipsp->ips_errs.ips_encpad_errs += 1;
++ }
++
++ KLIPS_PRINT(debug_rcv & DB_RX_IPAD,
++ "klips_debug:ipsec_rcv: "
++ "packet decrypted from %s: next_header = %d, padding = %d\n",
++ irs->ipsaddr_txt,
++ irs->next_header,
++ pad - 2 - irs->authlen);
++
++ irs->ipp->tot_len = htons(ntohs(irs->ipp->tot_len) - (esphlen + pad));
++
++ /*
++ * move the IP header forward by the size of the ESP header, which
++ * will remove the the ESP header from the packet.
++ */
++ memmove((void *)(skb->data + esphlen),
++ (void *)(skb->data), irs->iphlen);
++
++ ipsec_rcv_dmp("esp postmove", skb->data, skb->len);
++
++ /* skb_pull below, will move up by esphlen */
++
++ /* XXX not clear how this can happen, as the message indicates */
++ if(skb->len < esphlen) {
++ printk(KERN_WARNING
++ "klips_error:ipsec_rcv: "
++ "tried to skb_pull esphlen=%d, %d available. This should never happen, please report.\n",
++ esphlen, (int)(skb->len));
++ return IPSEC_RCV_ESP_DECAPFAIL;
++ }
++ skb_pull(skb, esphlen);
++
++ irs->ipp = (struct iphdr *)skb->data;
++
++ ipsec_rcv_dmp("esp postpull", skb->data, skb->len);
++
++ /* now, trip off the padding from the end */
++ KLIPS_PRINT(debug_rcv & DB_RX_PKTRX,
++ "klips_debug:ipsec_rcv: "
++ "trimming to %d.\n",
++ irs->len - esphlen - pad);
++ if(pad + esphlen <= irs->len) {
++ skb_trim(skb, irs->len - esphlen - pad);
++ } else {
++ KLIPS_PRINT(debug_rcv & DB_RX_PKTRX,
++ "klips_debug:ipsec_rcv: "
++ "bogus packet, size is zero or negative, dropping.\n");
++ return IPSEC_RCV_DECAPFAIL;
++ }
++
++ return IPSEC_RCV_OK;
++}
++
++enum ipsec_xmit_value
++ipsec_xmit_esp_setup(struct ipsec_xmit_state *ixs)
++{
++ __u32 iv[2];
++ struct esphdr *espp;
++ int ilen = 0;
++ int padlen = 0, i;
++ unsigned char *dat;
++ unsigned char *idat, *pad;
++ __u8 hash[AH_AMAX];
++ union {
++#ifdef CONFIG_IPSEC_AUTH_HMAC_MD5
++ MD5_CTX md5;
++#endif /* CONFIG_IPSEC_AUTH_HMAC_MD5 */
++#ifdef CONFIG_IPSEC_AUTH_HMAC_SHA1
++ SHA1_CTX sha1;
++#endif /* CONFIG_IPSEC_AUTH_HMAC_SHA1 */
++ } tctx;
++
++ dat = (unsigned char *)ixs->iph;
++
++ espp = (struct esphdr *)(dat + ixs->iphlen);
++ espp->esp_spi = ixs->ipsp->ips_said.spi;
++ espp->esp_rpl = htonl(++(ixs->ipsp->ips_replaywin_lastseq));
++
++ switch(ixs->ipsp->ips_encalg) {
++#if defined(CONFIG_IPSEC_ENC_3DES)
++#ifdef CONFIG_IPSEC_ENC_3DES
++ case ESP_3DES:
++#endif /* CONFIG_IPSEC_ENC_3DES */
++ iv[0] = *((__u32*)&(espp->esp_iv) ) =
++ ((__u32*)(ixs->ipsp->ips_iv))[0];
++ iv[1] = *((__u32*)&(espp->esp_iv) + 1) =
++ ((__u32*)(ixs->ipsp->ips_iv))[1];
++ break;
++#endif /* defined(CONFIG_IPSEC_ENC_3DES) */
++ default:
++ ixs->stats->tx_errors++;
++ return IPSEC_XMIT_ESP_BADALG;
++ }
++
++ idat = dat + ixs->iphlen + sizeof(struct esphdr);
++ ilen = ixs->skb->len - (ixs->iphlen + sizeof(struct esphdr) + ixs->authlen);
++
++ /* Self-describing padding */
++ pad = &dat[ixs->skb->len - ixs->tailroom];
++ padlen = ixs->tailroom - 2 - ixs->authlen;
++ for (i = 0; i < padlen; i++) {
++ pad[i] = i + 1;
++ }
++ dat[ixs->skb->len - ixs->authlen - 2] = padlen;
++
++ dat[ixs->skb->len - ixs->authlen - 1] = ixs->iph->protocol;
++ ixs->iph->protocol = IPPROTO_ESP;
++
++ switch(ixs->ipsp->ips_encalg) {
++#ifdef CONFIG_IPSEC_ENC_3DES
++ case ESP_3DES:
++ des_ede3_cbc_encrypt((des_cblock *)idat,
++ (des_cblock *)idat,
++ ilen,
++ ((struct des_eks *)(ixs->ipsp->ips_key_e))[0].ks,
++ ((struct des_eks *)(ixs->ipsp->ips_key_e))[1].ks,
++ ((struct des_eks *)(ixs->ipsp->ips_key_e))[2].ks,
++ (des_cblock *)iv, 1);
++ break;
++#endif /* CONFIG_IPSEC_ENC_3DES */
++ default:
++ ixs->stats->tx_errors++;
++ return IPSEC_XMIT_ESP_BADALG;
++ }
++
++ switch(ixs->ipsp->ips_encalg) {
++#if defined(CONFIG_IPSEC_ENC_3DES)
++#ifdef CONFIG_IPSEC_ENC_3DES
++ case ESP_3DES:
++#endif /* CONFIG_IPSEC_ENC_3DES */
++ /* XXX update IV with the last 8 octets of the encryption */
++#if KLIPS_IMPAIRMENT_ESPIV_CBC_ATTACK
++ ((__u32*)(ixs->ipsp->ips_iv))[0] =
++ ((__u32 *)(idat))[(ilen >> 2) - 2];
++ ((__u32*)(ixs->ipsp->ips_iv))[1] =
++ ((__u32 *)(idat))[(ilen >> 2) - 1];
++#else /* KLIPS_IMPAIRMENT_ESPIV_CBC_ATTACK */
++ prng_bytes(&ipsec_prng, (char *)ixs->ipsp->ips_iv, EMT_ESPDES_IV_SZ);
++#endif /* KLIPS_IMPAIRMENT_ESPIV_CBC_ATTACK */
++ break;
++#endif /* defined(CONFIG_IPSEC_ENC_3DES) */
++ default:
++ ixs->stats->tx_errors++;
++ return IPSEC_XMIT_ESP_BADALG;
++ }
++
++ switch(ixs->ipsp->ips_authalg) {
++#ifdef CONFIG_IPSEC_AUTH_HMAC_MD5
++ case AH_MD5:
++ ipsec_xmit_dmp("espp", (char*)espp, ixs->skb->len - ixs->iphlen - ixs->authlen);
++ tctx.md5 = ((struct md5_ctx*)(ixs->ipsp->ips_key_a))->ictx;
++ ipsec_xmit_dmp("ictx", (char*)&tctx.md5, sizeof(tctx.md5));
++ MD5Update(&tctx.md5, (caddr_t)espp, ixs->skb->len - ixs->iphlen - ixs->authlen);
++ ipsec_xmit_dmp("ictx+dat", (char*)&tctx.md5, sizeof(tctx.md5));
++ MD5Final(hash, &tctx.md5);
++ ipsec_xmit_dmp("ictx hash", (char*)&hash, sizeof(hash));
++ tctx.md5 = ((struct md5_ctx*)(ixs->ipsp->ips_key_a))->octx;
++ ipsec_xmit_dmp("octx", (char*)&tctx.md5, sizeof(tctx.md5));
++ MD5Update(&tctx.md5, hash, AHMD596_ALEN);
++ ipsec_xmit_dmp("octx+hash", (char*)&tctx.md5, sizeof(tctx.md5));
++ MD5Final(hash, &tctx.md5);
++ ipsec_xmit_dmp("octx hash", (char*)&hash, sizeof(hash));
++ memcpy(&(dat[ixs->skb->len - ixs->authlen]), hash, ixs->authlen);
++
++ /* paranoid */
++ memset((caddr_t)&tctx.md5, 0, sizeof(tctx.md5));
++ memset((caddr_t)hash, 0, sizeof(*hash));
++ break;
++#endif /* CONFIG_IPSEC_AUTH_HMAC_MD5 */
++#ifdef CONFIG_IPSEC_AUTH_HMAC_SHA1
++ case AH_SHA:
++ tctx.sha1 = ((struct sha1_ctx*)(ixs->ipsp->ips_key_a))->ictx;
++ SHA1Update(&tctx.sha1, (caddr_t)espp, ixs->skb->len - ixs->iphlen - ixs->authlen);
++ SHA1Final(hash, &tctx.sha1);
++ tctx.sha1 = ((struct sha1_ctx*)(ixs->ipsp->ips_key_a))->octx;
++ SHA1Update(&tctx.sha1, hash, AHSHA196_ALEN);
++ SHA1Final(hash, &tctx.sha1);
++ memcpy(&(dat[ixs->skb->len - ixs->authlen]), hash, ixs->authlen);
++
++ /* paranoid */
++ memset((caddr_t)&tctx.sha1, 0, sizeof(tctx.sha1));
++ memset((caddr_t)hash, 0, sizeof(*hash));
++ break;
++#endif /* CONFIG_IPSEC_AUTH_HMAC_SHA1 */
++ case AH_NONE:
++ break;
++ default:
++ ixs->stats->tx_errors++;
++ return IPSEC_XMIT_AH_BADALG;
++ }
++#ifdef NET_21
++ ixs->skb->h.raw = (unsigned char*)espp;
++#endif /* NET_21 */
++
++ return IPSEC_XMIT_OK;
++}
++
++
++struct xform_functions esp_xform_funcs[]={
++ { rcv_checks: ipsec_rcv_esp_checks,
++ rcv_setup_auth: ipsec_rcv_esp_decrypt_setup,
++ rcv_calc_auth: ipsec_rcv_esp_authcalc,
++ rcv_decrypt: ipsec_rcv_esp_decrypt,
++
++ xmit_setup: ipsec_xmit_esp_setup,
++ xmit_headroom: sizeof(struct esphdr),
++ xmit_needtailroom: 1,
++ },
++};
++
++struct inet_protocol esp_protocol =
++{
++ ipsec_rcv, /* ESP handler */
++ NULL, /* TUNNEL error control */
++#ifdef NETDEV_25
++ 1, /* no policy */
++#else
++ 0, /* next */
++ IPPROTO_ESP, /* protocol ID */
++ 0, /* copy */
++ NULL, /* data */
++ "ESP" /* name */
++#endif
++};
++
++
++
++#endif /* !CONFIG_IPSEC_ESP */
++
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.2 2004/04/06 02:49:25 mcr
++ * pullup of algo code from alg-branch.
++ *
++ *
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/net/ipsec/ipsec_init.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,531 @@
++/*
++ * @(#) Initialization code.
++ * Copyright (C) 1996, 1997 John Ioannidis.
++ * Copyright (C) 1998 - 2002 Richard Guy Briggs <rgb@freeswan.org>
++ * 2001 - 2004 Michael Richardson <mcr@xelerance.com>
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ * /proc system code was split out into ipsec_proc.c after rev. 1.70.
++ *
++ */
++
++char ipsec_init_c_version[] = "RCSID $Id$";
++
++#include <linux/config.h>
++#include <linux/version.h>
++#include <linux/module.h>
++#include <linux/kernel.h> /* printk() */
++
++#include "openswan/ipsec_param.h"
++
++#ifdef MALLOC_SLAB
++# include <linux/slab.h> /* kmalloc() */
++#else /* MALLOC_SLAB */
++# include <linux/malloc.h> /* kmalloc() */
++#endif /* MALLOC_SLAB */
++#include <linux/errno.h> /* error codes */
++#include <linux/types.h> /* size_t */
++#include <linux/interrupt.h> /* mark_bh */
++
++#include <linux/netdevice.h> /* struct device, and other headers */
++#include <linux/etherdevice.h> /* eth_type_trans */
++#include <linux/ip.h> /* struct iphdr */
++#include <linux/in.h> /* struct sockaddr_in */
++#include <linux/skbuff.h>
++#include <linux/random.h> /* get_random_bytes() */
++#include <openswan.h>
++
++#ifdef SPINLOCK
++# ifdef SPINLOCK_23
++# include <linux/spinlock.h> /* *lock* */
++# else /* 23_SPINLOCK */
++# include <asm/spinlock.h> /* *lock* */
++# endif /* 23_SPINLOCK */
++#endif /* SPINLOCK */
++
++#ifdef NET_21
++# include <asm/uaccess.h>
++# include <linux/in6.h>
++#endif /* NET_21 */
++
++#include <asm/checksum.h>
++#include <net/ip.h>
++
++#ifdef CONFIG_PROC_FS
++# include <linux/proc_fs.h>
++#endif /* CONFIG_PROC_FS */
++
++#ifdef NETLINK_SOCK
++# include <linux/netlink.h>
++#else
++# include <net/netlink.h>
++#endif
++
++#include "openswan/radij.h"
++
++#include "openswan/ipsec_life.h"
++#include "openswan/ipsec_stats.h"
++#include "openswan/ipsec_sa.h"
++
++#include "openswan/ipsec_encap.h"
++#include "openswan/ipsec_radij.h"
++#include "openswan/ipsec_xform.h"
++#include "openswan/ipsec_tunnel.h"
++
++#include "openswan/ipsec_rcv.h"
++#include "openswan/ipsec_ah.h"
++#include "openswan/ipsec_esp.h"
++
++#ifdef CONFIG_IPSEC_IPCOMP
++# include "openswan/ipcomp.h"
++#endif /* CONFIG_IPSEC_IPCOMP */
++
++#include "openswan/ipsec_proto.h"
++#include "openswan/ipsec_alg.h"
++
++#include <pfkeyv2.h>
++#include <pfkey.h>
++
++#if !defined(CONFIG_IPSEC_ESP) && !defined(CONFIG_IPSEC_AH)
++#error "kernel configuration must include ESP or AH"
++#endif
++
++/*
++ * seems to be present in 2.4.10 (Linus), but also in some RH and other
++ * distro kernels of a lower number.
++ */
++#ifdef MODULE_LICENSE
++MODULE_LICENSE("GPL");
++#endif
++
++#ifdef CONFIG_IPSEC_DEBUG
++int debug_eroute = 0;
++int debug_spi = 0;
++int debug_netlink = 0;
++#endif /* CONFIG_IPSEC_DEBUG */
++
++struct prng ipsec_prng;
++
++extern int ipsec_device_event(struct notifier_block *dnot, unsigned long event, void *ptr);
++/*
++ * the following structure is required so that we receive
++ * event notifications when network devices are enabled and
++ * disabled (ifconfig up and down).
++ */
++static struct notifier_block ipsec_dev_notifier={
++ ipsec_device_event,
++ NULL,
++ 0
++};
++
++#ifdef CONFIG_SYSCTL
++extern int ipsec_sysctl_register(void);
++extern void ipsec_sysctl_unregister(void);
++#endif
++
++static inline int
++openswan_inet_add_protocol(struct inet_protocol *prot, unsigned protocol)
++{
++#ifdef NETDEV_25
++ return inet_add_protocol(prot, protocol);
++#else
++ inet_add_protocol(prot);
++ return 0;
++#endif
++}
++
++static inline int
++openswan_inet_del_protocol(struct inet_protocol *prot, unsigned protocol)
++{
++#ifdef NETDEV_25
++ return inet_del_protocol(prot, protocol);
++#else
++ inet_del_protocol(prot);
++ return 0;
++#endif
++}
++
++/* void */
++int
++ipsec_init(void)
++{
++ int error = 0;
++ unsigned char seed[256];
++#ifdef CONFIG_IPSEC_ENC_3DES
++ extern int des_check_key;
++
++ /* turn off checking of keys */
++ des_check_key=0;
++#endif /* CONFIG_IPSEC_ENC_3DES */
++
++ KLIPS_PRINT(1, "klips_info:ipsec_init: "
++ "KLIPS startup, Openswan KLIPS IPsec stack version: %s\n",
++ ipsec_version_code());
++
++ error |= ipsec_proc_init();
++
++#ifdef SPINLOCK
++ ipsec_sadb.sadb_lock = SPIN_LOCK_UNLOCKED;
++#else /* SPINLOCK */
++ ipsec_sadb.sadb_lock = 0;
++#endif /* SPINLOCK */
++
++#ifndef SPINLOCK
++ tdb_lock.lock = 0;
++ eroute_lock.lock = 0;
++#endif /* !SPINLOCK */
++
++ error |= ipsec_sadb_init();
++ error |= ipsec_radijinit();
++
++ error |= pfkey_init();
++
++ error |= register_netdevice_notifier(&ipsec_dev_notifier);
++
++#ifdef CONFIG_IPSEC_ESP
++ openswan_inet_add_protocol(&esp_protocol, IPPROTO_ESP);
++#endif /* CONFIG_IPSEC_ESP */
++
++#ifdef CONFIG_IPSEC_AH
++ openswan_inet_add_protocol(&ah_protocol, IPPROTO_AH);
++#endif /* CONFIG_IPSEC_AH */
++
++/* we never actually link IPCOMP to the stack */
++#ifdef IPCOMP_USED_ALONE
++#ifdef CONFIG_IPSEC_IPCOMP
++ openswan_inet_add_protocol(&comp_protocol, IPPROTO_COMP);
++#endif /* CONFIG_IPSEC_IPCOMP */
++#endif
++
++ error |= ipsec_tunnel_init_devices();
++
++
++#ifdef CONFIG_SYSCTL
++ error |= ipsec_sysctl_register();
++#endif
++
++#ifdef CONFIG_IPSEC_ALG
++ ipsec_alg_init();
++#endif
++
++ get_random_bytes((void *)seed, sizeof(seed));
++ prng_init(&ipsec_prng, seed, sizeof(seed));
++
++ return error;
++}
++
++
++/* void */
++int
++ipsec_cleanup(void)
++{
++ int error = 0;
++
++#ifdef CONFIG_SYSCTL
++ ipsec_sysctl_unregister();
++#endif
++ KLIPS_PRINT(debug_netlink, /* debug_tunnel & DB_TN_INIT, */
++ "klips_debug:ipsec_cleanup: "
++ "calling ipsec_tunnel_cleanup_devices.\n");
++ error |= ipsec_tunnel_cleanup_devices();
++
++ KLIPS_PRINT(debug_netlink, "called ipsec_tunnel_cleanup_devices");
++
++/* we never actually link IPCOMP to the stack */
++#ifdef IPCOMP_USED_ALONE
++#ifdef CONFIG_IPSEC_IPCOMP
++ if (openswan_inet_del_protocol(&comp_protocol, IPPROTO_COMP) < 0)
++ printk(KERN_INFO "klips_debug:ipsec_cleanup: "
++ "comp close: can't remove protocol\n");
++#endif /* CONFIG_IPSEC_IPCOMP */
++#endif /* IPCOMP_USED_ALONE */
++
++#ifdef CONFIG_IPSEC_AH
++ if (openswan_inet_del_protocol(&ah_protocol, IPPROTO_AH) < 0)
++ printk(KERN_INFO "klips_debug:ipsec_cleanup: "
++ "ah close: can't remove protocol\n");
++#endif /* CONFIG_IPSEC_AH */
++
++#ifdef CONFIG_IPSEC_ESP
++ if (openswan_inet_del_protocol(&esp_protocol, IPPROTO_ESP) < 0)
++ printk(KERN_INFO "klips_debug:ipsec_cleanup: "
++ "esp close: can't remove protocol\n");
++#endif /* CONFIG_IPSEC_ESP */
++
++ error |= unregister_netdevice_notifier(&ipsec_dev_notifier);
++
++ KLIPS_PRINT(debug_netlink, /* debug_tunnel & DB_TN_INIT, */
++ "klips_debug:ipsec_cleanup: "
++ "calling ipsec_sadb_cleanup.\n");
++ error |= ipsec_sadb_cleanup(0);
++ error |= ipsec_sadb_free();
++
++ KLIPS_PRINT(debug_netlink, /* debug_tunnel & DB_TN_INIT, */
++ "klips_debug:ipsec_cleanup: "
++ "calling ipsec_radijcleanup.\n");
++ error |= ipsec_radijcleanup();
++
++ KLIPS_PRINT(debug_pfkey, /* debug_tunnel & DB_TN_INIT, */
++ "klips_debug:ipsec_cleanup: "
++ "calling pfkey_cleanup.\n");
++ error |= pfkey_cleanup();
++
++ ipsec_proc_cleanup();
++
++ prng_final(&ipsec_prng);
++
++ return error;
++}
++
++#ifdef MODULE
++int
++init_module(void)
++{
++ int error = 0;
++
++ error |= ipsec_init();
++
++ return error;
++}
++
++int
++cleanup_module(void)
++{
++ int error = 0;
++
++ KLIPS_PRINT(debug_netlink, /* debug_tunnel & DB_TN_INIT, */
++ "klips_debug:cleanup_module: "
++ "calling ipsec_cleanup.\n");
++
++ error |= ipsec_cleanup();
++
++ KLIPS_PRINT(1, "klips_info:cleanup_module: "
++ "ipsec module unloaded.\n");
++
++ return error;
++}
++#endif /* MODULE */
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.93 2004/04/06 02:49:26 mcr
++ * pullup of algo code from alg-branch.
++ *
++ * Revision 1.92 2004/03/30 15:30:39 ken
++ * Proper Capitalization
++ *
++ * Revision 1.91 2004/03/22 01:51:51 ken
++ * We are open
++ *
++ * Revision 1.90.4.2 2004/04/05 04:30:46 mcr
++ * patches for alg-branch to compile/work with 2.x openswan
++ *
++ * Revision 1.90.4.1 2003/12/22 15:25:52 jjo
++ * Merged algo-0.8.1-rc11-test1 into alg-branch
++ *
++ * Revision 1.90 2003/10/31 02:27:55 mcr
++ * pulled up port-selector patches and sa_id elimination.
++ *
++ * Revision 1.89.4.1 2003/10/29 01:30:41 mcr
++ * elimited "struct sa_id".
++ *
++ * Revision 1.89 2003/07/31 22:47:16 mcr
++ * preliminary (untested by FS-team) 2.5 patches.
++ *
++ * Revision 1.88 2003/06/22 20:05:36 mcr
++ * clarified why IPCOMP was not being registered, and put a new
++ * #ifdef in rather than #if 0.
++ *
++ * Revision 1.87 2002/09/20 15:40:51 rgb
++ * Added a lock to the global ipsec_sadb struct for future use.
++ * Split ipsec_sadb_cleanup from new funciton ipsec_sadb_free to avoid problem
++ * of freeing newly created structures when clearing the reftable upon startup
++ * to start from a known state.
++ *
++ * Revision 1.86 2002/08/15 18:39:15 rgb
++ * Move ipsec_prng outside debug code.
++ *
++ * Revision 1.85 2002/05/14 02:35:29 rgb
++ * Change reference to tdb to ipsa.
++ *
++ * Revision 1.84 2002/04/24 07:55:32 mcr
++ * #include patches and Makefiles for post-reorg compilation.
++ *
++ * Revision 1.83 2002/04/24 07:36:28 mcr
++ * Moved from ./klips/net/ipsec/ipsec_init.c,v
++ *
++ * Revision 1.82 2002/04/20 00:12:25 rgb
++ * Added esp IV CBC attack fix, disabled.
++ *
++ * Revision 1.81 2002/04/09 16:13:32 mcr
++ * switch license to straight GPL.
++ *
++ * Revision 1.80 2002/03/24 07:34:08 rgb
++ * Sanity check for at least one of AH or ESP configured.
++ *
++ * Revision 1.79 2002/02/05 22:55:15 mcr
++ * added MODULE_LICENSE declaration.
++ * This macro does not appear in all kernel versions (see comment).
++ *
++ * Revision 1.78 2002/01/29 17:17:55 mcr
++ * moved include of ipsec_param.h to after include of linux/kernel.h
++ * otherwise, it seems that some option that is set in ipsec_param.h
++ * screws up something subtle in the include path to kernel.h, and
++ * it complains on the snprintf() prototype.
++ *
++ * Revision 1.77 2002/01/29 04:00:51 mcr
++ * more excise of kversions.h header.
++ *
++ * Revision 1.76 2002/01/29 02:13:17 mcr
++ * introduction of ipsec_kversion.h means that include of
++ * ipsec_param.h must preceed any decisions about what files to
++ * include to deal with differences in kernel source.
++ *
++ * Revision 1.75 2001/11/26 09:23:48 rgb
++ * Merge MCR's ipsec_sa, eroute, proc and struct lifetime changes.
++ *
++ * Revision 1.74 2001/11/22 05:44:11 henry
++ * new version stuff
++ *
++ * Revision 1.71.2.2 2001/10/22 20:51:00 mcr
++ * explicitely set des_check_key.
++ *
++ * Revision 1.71.2.1 2001/09/25 02:19:39 mcr
++ * /proc manipulation code moved to new ipsec_proc.c
++ *
++ * Revision 1.73 2001/11/06 19:47:17 rgb
++ * Changed lifetime_packets to uint32 from uint64.
++ *
++ * Revision 1.72 2001/10/18 04:45:19 rgb
++ * 2.4.9 kernel deprecates linux/malloc.h in favour of linux/slab.h,
++ * lib/freeswan.h version macros moved to lib/kversions.h.
++ * Other compiler directive cleanups.
++ *
++ * Revision 1.71 2001/09/20 15:32:45 rgb
++ * Minor pfkey lifetime fixes.
++ *
++ * Revision 1.70 2001/07/06 19:51:21 rgb
++ * Added inbound policy checking code for IPIP SAs.
++ *
++ * Revision 1.69 2001/06/14 19:33:26 rgb
++ * Silence startup message for console, but allow it to be logged.
++ * Update copyright date.
++ *
++ * Revision 1.68 2001/05/29 05:14:36 rgb
++ * Added PMTU to /proc/net/ipsec_tncfg output. See 'man 5 ipsec_tncfg'.
++ *
++ * Revision 1.67 2001/05/04 16:34:52 rgb
++ * Rremove erroneous checking of return codes for proc_net_* in 2.4.
++ *
++ * Revision 1.66 2001/05/03 19:40:34 rgb
++ * Check error return codes in startup and shutdown.
++ *
++ * Revision 1.65 2001/02/28 05:03:27 rgb
++ * Clean up and rationalise startup messages.
++ *
++ * Revision 1.64 2001/02/27 22:24:53 rgb
++ * Re-formatting debug output (line-splitting, joining, 1arg/line).
++ * Check for satoa() return codes.
++ *
++ * Revision 1.63 2000/11/29 20:14:06 rgb
++ * Add src= to the output of /proc/net/ipsec_spi and delete dst from IPIP.
++ *
++ * Revision 1.62 2000/11/06 04:31:24 rgb
++ * Ditched spin_lock_irqsave in favour of spin_lock_bh.
++ * Fixed longlong for pre-2.4 kernels (Svenning).
++ * Add Svenning's adaptive content compression.
++ * Disabled registration of ipcomp handler.
++ *
++ * Revision 1.61 2000/10/11 13:37:54 rgb
++ * #ifdef out debug print that causes proc/net/ipsec_version to oops.
++ *
++ * Revision 1.60 2000/09/20 03:59:01 rgb
++ * Change static info functions to DEBUG_NO_STATIC to reveal function names
++ * in oopsen.
++ *
++ * Revision 1.59 2000/09/16 01:06:26 rgb
++ * Added cast of var to silence compiler warning about long fed to int
++ * format.
++ *
++ * Revision 1.58 2000/09/15 11:37:01 rgb
++ * Merge in heavily modified Svenning Soerensen's <svenning@post5.tele.dk>
++ * IPCOMP zlib deflate code.
++ *
++ * Revision 1.57 2000/09/12 03:21:50 rgb
++ * Moved radij_c_version printing to ipsec_version_get_info().
++ * Reformatted ipsec_version_get_info().
++ * Added sysctl_{,un}register() calls.
++ *
++ * Revision 1.56 2000/09/08 19:16:50 rgb
++ * Change references from DEBUG_IPSEC to CONFIG_IPSEC_DEBUG.
++ * Removed all references to CONFIG_IPSEC_PFKEYv2.
++ *
++ * Revision 1.55 2000/08/30 05:19:03 rgb
++ * Cleaned up no longer used spi_next, netlink register/unregister, other
++ * minor cleanup.
++ * Removed cruft replaced by TDB_XFORM_NAME.
++ * Removed all the rest of the references to tdb_spi, tdb_proto, tdb_dst.
++ * Moved debug version strings to printk when /proc/net/ipsec_version is
++ * called.
++ *
++ * Revision 1.54 2000/08/20 18:31:05 rgb
++ * Changed cosmetic alignment in spi_info.
++ * Changed addtime and usetime to use actual value which is relative
++ * anyways, as intended. (Momchil)
++ *
++ * Revision 1.53 2000/08/18 17:37:03 rgb
++ * Added an (int) cast to shut up the compiler...
++ *
++ * Revision 1.52 2000/08/01 14:51:50 rgb
++ * Removed _all_ remaining traces of DES.
++ *
++ * Revision 1.51 2000/07/25 20:41:22 rgb
++ * Removed duplicate parameter in spi_getinfo.
++ *
++ * Revision 1.50 2000/07/17 03:21:45 rgb
++ * Removed /proc/net/ipsec_spinew.
++ *
++ * Revision 1.49 2000/06/28 05:46:51 rgb
++ * Renamed ivlen to iv_bits for consistency.
++ * Changed output of add and use times to be relative to now.
++ *
++ * Revision 1.48 2000/05/11 18:26:10 rgb
++ * Commented out calls to netlink_attach/detach to avoid activating netlink
++ * in the kenrel config.
++ *
++ * Revision 1.47 2000/05/10 22:35:26 rgb
++ * Comment out most of the startup version information.
++ *
++ * Revision 1.46 2000/03/22 16:15:36 rgb
++ * Fixed renaming of dev_get (MB).
++ *
++ * Revision 1.45 2000/03/16 06:40:48 rgb
++ * Hardcode PF_KEYv2 support.
++ *
++ * Revision 1.44 2000/01/22 23:19:20 rgb
++ * Simplified code to use existing macro TDB_XFORM_NAME().
++ *
++ * Revision 1.43 2000/01/21 06:14:04 rgb
++ * Print individual stats only if non-zero.
++ * Removed 'bits' from each keylength for brevity.
++ * Shortened lifetimes legend for brevity.
++ * Changed wording from 'last_used' to the clearer 'idle'.
++ *
++ * Revision 1.42 1999/12/31 14:57:19 rgb
++ * MB fix for new dummy-less proc_get_info in 2.3.35.
++ *
++ *
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/net/ipsec/ipsec_ipcomp.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,276 @@
++/*
++ * processing code for IPCOMP
++ * Copyright (C) 2003 Michael Richardson <mcr@sandelman.ottawa.on.ca>
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ */
++
++char ipsec_ipcomp_c_version[] = "RCSID $Id$";
++#include <linux/config.h>
++#include <linux/version.h>
++
++#define __NO_VERSION__
++#include <linux/module.h>
++#include <linux/kernel.h> /* printk() */
++
++#include "openswan/ipsec_param.h"
++
++#ifdef MALLOC_SLAB
++# include <linux/slab.h> /* kmalloc() */
++#else /* MALLOC_SLAB */
++# include <linux/malloc.h> /* kmalloc() */
++#endif /* MALLOC_SLAB */
++#include <linux/errno.h> /* error codes */
++#include <linux/types.h> /* size_t */
++#include <linux/interrupt.h> /* mark_bh */
++
++#include <linux/netdevice.h> /* struct device, and other headers */
++#include <linux/etherdevice.h> /* eth_type_trans */
++#include <linux/ip.h> /* struct iphdr */
++#include <linux/skbuff.h>
++#include <openswan.h>
++#ifdef SPINLOCK
++# ifdef SPINLOCK_23
++# include <linux/spinlock.h> /* *lock* */
++# else /* SPINLOCK_23 */
++# include <asm/spinlock.h> /* *lock* */
++# endif /* SPINLOCK_23 */
++#endif /* SPINLOCK */
++#ifdef NET_21
++# include <asm/uaccess.h>
++# include <linux/in6.h>
++# define proto_priv cb
++#endif /* NET21 */
++#include <asm/checksum.h>
++#include <net/ip.h>
++
++#include "openswan/radij.h"
++#include "openswan/ipsec_encap.h"
++#include "openswan/ipsec_sa.h"
++
++#include "openswan/ipsec_radij.h"
++#include "openswan/ipsec_xform.h"
++#include "openswan/ipsec_tunnel.h"
++#include "openswan/ipsec_rcv.h"
++#include "openswan/ipsec_xmit.h"
++
++#include "openswan/ipsec_auth.h"
++
++#ifdef CONFIG_IPSEC_IPCOMP
++#include "openswan/ipsec_ipcomp.h"
++#endif /* CONFIG_IPSEC_IPCOMP */
++
++#include "openswan/ipsec_proto.h"
++
++#ifdef CONFIG_IPSEC_DEBUG
++int debug_ipcomp = 0;
++#endif /* CONFIG_IPSEC_DEBUG */
++
++
++#ifdef CONFIG_IPSEC_IPCOMP
++enum ipsec_rcv_value
++ipsec_rcv_ipcomp_checks(struct ipsec_rcv_state *irs,
++ struct sk_buff *skb)
++{
++ int ipcompminlen;
++
++ ipcompminlen = irs->hard_header_len + sizeof(struct iphdr);
++
++ if(skb->len < (ipcompminlen + sizeof(struct ipcomphdr))) {
++ KLIPS_PRINT(debug_rcv & DB_RX_INAU,
++ "klips_debug:ipsec_rcv: "
++ "runt comp packet of skb->len=%d received from %s, dropped.\n",
++ skb->len,
++ irs->ipsaddr_txt);
++ if(irs->stats) {
++ irs->stats->rx_errors++;
++ }
++ return IPSEC_RCV_BADLEN;
++ }
++
++ irs->protostuff.ipcompstuff.compp = (struct ipcomphdr *)(skb->data + irs->iphlen);
++ irs->said.spi = htonl((__u32)ntohs(irs->protostuff.ipcompstuff.compp->ipcomp_cpi));
++ return IPSEC_RCV_OK;
++}
++
++enum ipsec_rcv_value
++ipsec_rcv_ipcomp_decomp(struct ipsec_rcv_state *irs)
++{
++ unsigned int flags = 0;
++ struct ipsec_sa *ipsp = irs->ipsp;
++ struct sk_buff *skb;
++
++ skb=irs->skb;
++
++ ipsec_xmit_dmp("ipcomp", skb->data, skb->len);
++
++ if(ipsp == NULL) {
++ return IPSEC_RCV_SAIDNOTFOUND;
++ }
++
++#if 0
++ /* we want to check that this wasn't the first SA on the list, because
++ * we don't support bare IPCOMP, for unexplained reasons. MCR
++ */
++ if (ipsp->ips_onext != NULL) {
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "Incoming packet with outer IPCOMP header SA:%s: not yet supported by KLIPS, dropped\n",
++ irs->sa_len ? irs->sa : " (error)");
++ if(irs->stats) {
++ irs->stats->rx_dropped++;
++ }
++
++ return IPSEC_RCV_IPCOMPALONE;
++ }
++#endif
++
++ if(sysctl_ipsec_inbound_policy_check &&
++ ((((ntohl(ipsp->ips_said.spi) & 0x0000ffff) != ntohl(irs->said.spi)) &&
++ (ipsp->ips_encalg != ntohl(irs->said.spi)) /* this is a workaround for peer non-compliance with rfc2393 */
++ ))) {
++ char sa2[SATOT_BUF];
++ size_t sa_len2 = 0;
++
++ sa_len2 = satot(&ipsp->ips_said, 0, sa2, sizeof(sa2));
++
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "Incoming packet with SA(IPCA):%s does not match policy SA(IPCA):%s cpi=%04x cpi->spi=%08x spi=%08x, spi->cpi=%04x for SA grouping, dropped.\n",
++ irs->sa_len ? irs->sa : " (error)",
++ ipsp != NULL ? (sa_len2 ? sa2 : " (error)") : "NULL",
++ ntohs(irs->protostuff.ipcompstuff.compp->ipcomp_cpi),
++ (__u32)ntohl(irs->said.spi),
++ ipsp != NULL ? (__u32)ntohl((ipsp->ips_said.spi)) : 0,
++ ipsp != NULL ? (__u16)(ntohl(ipsp->ips_said.spi) & 0x0000ffff) : 0);
++ if(irs->stats) {
++ irs->stats->rx_dropped++;
++ }
++ return IPSEC_RCV_SAIDNOTFOUND;
++ }
++
++ ipsp->ips_comp_ratio_cbytes += ntohs(irs->ipp->tot_len);
++ irs->next_header = irs->protostuff.ipcompstuff.compp->ipcomp_nh;
++
++ skb = skb_decompress(skb, ipsp, &flags);
++ if (!skb || flags) {
++ spin_unlock(&tdb_lock);
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "skb_decompress() returned error flags=%x, dropped.\n",
++ flags);
++ if (irs->stats) {
++ if (flags)
++ irs->stats->rx_errors++;
++ else
++ irs->stats->rx_dropped++;
++ }
++ return IPSEC_RCV_IPCOMPFAILED;
++ }
++
++ /* make sure we update the pointer */
++ irs->skb = skb;
++
++#ifdef NET_21
++ irs->ipp = skb->nh.iph;
++#else /* NET_21 */
++ irs->ipp = skb->ip_hdr;
++#endif /* NET_21 */
++
++ ipsp->ips_comp_ratio_dbytes += ntohs(irs->ipp->tot_len);
++
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "packet decompressed SA(IPCA):%s cpi->spi=%08x spi=%08x, spi->cpi=%04x, nh=%d.\n",
++ irs->sa_len ? irs->sa : " (error)",
++ (__u32)ntohl(irs->said.spi),
++ ipsp != NULL ? (__u32)ntohl((ipsp->ips_said.spi)) : 0,
++ ipsp != NULL ? (__u16)(ntohl(ipsp->ips_said.spi) & 0x0000ffff) : 0,
++ irs->next_header);
++ KLIPS_IP_PRINT(debug_rcv & DB_RX_PKTRX, irs->ipp);
++
++ return IPSEC_RCV_OK;
++}
++
++enum ipsec_xmit_value
++ipsec_xmit_ipcomp_setup(struct ipsec_xmit_state *ixs)
++{
++ unsigned int flags = 0;
++#ifdef CONFIG_IPSEC_DEBUG
++ unsigned int old_tot_len = ntohs(ixs->iph->tot_len);
++#endif /* CONFIG_IPSEC_DEBUG */
++
++ ixs->ipsp->ips_comp_ratio_dbytes += ntohs(ixs->iph->tot_len);
++
++ ixs->skb = skb_compress(ixs->skb, ixs->ipsp, &flags);
++
++#ifdef NET_21
++ ixs->iph = ixs->skb->nh.iph;
++#else /* NET_21 */
++ ixs->iph = ixs->skb->ip_hdr;
++#endif /* NET_21 */
++
++ ixs->ipsp->ips_comp_ratio_cbytes += ntohs(ixs->iph->tot_len);
++
++#ifdef CONFIG_IPSEC_DEBUG
++ if (debug_tunnel & DB_TN_CROUT)
++ {
++ if (old_tot_len > ntohs(ixs->iph->tot_len))
++ KLIPS_PRINT(debug_tunnel & DB_TN_CROUT,
++ "klips_debug:ipsec_xmit_encap_once: "
++ "packet shrunk from %d to %d bytes after compression, cpi=%04x (should be from spi=%08x, spi&0xffff=%04x.\n",
++ old_tot_len, ntohs(ixs->iph->tot_len),
++ ntohs(((struct ipcomphdr*)(((char*)ixs->iph) + ((ixs->iph->ihl) << 2)))->ipcomp_cpi),
++ ntohl(ixs->ipsp->ips_said.spi),
++ (__u16)(ntohl(ixs->ipsp->ips_said.spi) & 0x0000ffff));
++ else
++ KLIPS_PRINT(debug_tunnel & DB_TN_CROUT,
++ "klips_debug:ipsec_xmit_encap_once: "
++ "packet did not compress (flags = %d).\n",
++ flags);
++ }
++#endif /* CONFIG_IPSEC_DEBUG */
++
++ return IPSEC_XMIT_OK;
++}
++
++struct xform_functions ipcomp_xform_funcs[]={
++ {rcv_checks: ipsec_rcv_ipcomp_checks,
++ rcv_decrypt: ipsec_rcv_ipcomp_decomp,
++ xmit_setup: ipsec_xmit_ipcomp_setup,
++ xmit_headroom: 0,
++ xmit_needtailroom: 0,
++ },
++};
++
++#if 0
++/* We probably don't want to install a pure IPCOMP protocol handler, but
++ only want to handle IPCOMP if it is encapsulated inside an ESP payload
++ (which is already handled) */
++#ifdef CONFIG_IPSEC_IPCOMP
++struct inet_protocol comp_protocol =
++{
++ ipsec_rcv, /* COMP handler */
++ NULL, /* COMP error control */
++#ifdef NETDEV_25
++ 1, /* no policy */
++#else
++ 0, /* next */
++ IPPROTO_COMP, /* protocol ID */
++ 0, /* copy */
++ NULL, /* data */
++ "COMP" /* name */
++#endif
++};
++#endif /* CONFIG_IPSEC_IPCOMP */
++#endif
++
++#endif /* CONFIG_IPSEC_IPCOMP */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/net/ipsec/ipsec_ipip.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,133 @@
++/*
++ * processing code for IPIP
++ * Copyright (C) 2003 Michael Richardson <mcr@sandelman.ottawa.on.ca>
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ */
++
++char ipsec_ipip_c_version[] = "RCSID $Id$";
++#include <linux/config.h>
++#include <linux/version.h>
++
++#define __NO_VERSION__
++#include <linux/module.h>
++#include <linux/kernel.h> /* printk() */
++
++#include "openswan/ipsec_param.h"
++
++#ifdef MALLOC_SLAB
++# include <linux/slab.h> /* kmalloc() */
++#else /* MALLOC_SLAB */
++# include <linux/malloc.h> /* kmalloc() */
++#endif /* MALLOC_SLAB */
++#include <linux/errno.h> /* error codes */
++#include <linux/types.h> /* size_t */
++#include <linux/interrupt.h> /* mark_bh */
++
++#include <linux/netdevice.h> /* struct device, and other headers */
++#include <linux/etherdevice.h> /* eth_type_trans */
++#include <linux/ip.h> /* struct iphdr */
++#include <linux/skbuff.h>
++#include <openswan.h>
++#ifdef SPINLOCK
++# ifdef SPINLOCK_23
++# include <linux/spinlock.h> /* *lock* */
++# else /* SPINLOCK_23 */
++# include <asm/spinlock.h> /* *lock* */
++# endif /* SPINLOCK_23 */
++#endif /* SPINLOCK */
++#ifdef NET_21
++# include <asm/uaccess.h>
++# include <linux/in6.h>
++# define proto_priv cb
++#endif /* NET21 */
++#include <asm/checksum.h>
++#include <net/ip.h>
++
++#include "openswan/radij.h"
++#include "openswan/ipsec_encap.h"
++#include "openswan/ipsec_sa.h"
++
++#include "openswan/ipsec_radij.h"
++#include "openswan/ipsec_xform.h"
++#include "openswan/ipsec_tunnel.h"
++#include "openswan/ipsec_rcv.h"
++#include "openswan/ipsec_xmit.h"
++
++#include "openswan/ipsec_auth.h"
++#include "openswan/ipsec_ipip.h"
++#include "openswan/ipsec_param.h"
++
++#include "openswan/ipsec_proto.h"
++
++enum ipsec_xmit_value
++ipsec_xmit_ipip_setup(struct ipsec_xmit_state *ixs)
++{
++ ixs->iph->version = 4;
++
++ switch(sysctl_ipsec_tos) {
++ case 0:
++#ifdef NET_21
++ ixs->iph->tos = ixs->skb->nh.iph->tos;
++#else /* NET_21 */
++ ixs->iph->tos = ixs->skb->ip_hdr->tos;
++#endif /* NET_21 */
++ break;
++ case 1:
++ ixs->iph->tos = 0;
++ break;
++ default:
++ break;
++ }
++#ifdef NET_21
++#ifdef NETDEV_23
++ ixs->iph->ttl = sysctl_ip_default_ttl;
++#else /* NETDEV_23 */
++ ixs->iph->ttl = ip_statistics.IpDefaultTTL;
++#endif /* NETDEV_23 */
++#else /* NET_21 */
++ ixs->iph->ttl = 64; /* ip_statistics.IpDefaultTTL; */
++#endif /* NET_21 */
++ ixs->iph->frag_off = 0;
++ ixs->iph->saddr = ((struct sockaddr_in*)(ixs->ipsp->ips_addr_s))->sin_addr.s_addr;
++ ixs->iph->daddr = ((struct sockaddr_in*)(ixs->ipsp->ips_addr_d))->sin_addr.s_addr;
++ ixs->iph->protocol = IPPROTO_IPIP;
++ ixs->iph->ihl = sizeof(struct iphdr) >> 2;
++
++ KLIPS_IP_SELECT_IDENT(ixs->iph, ixs->skb);
++
++ ixs->newdst = (__u32)ixs->iph->daddr;
++ ixs->newsrc = (__u32)ixs->iph->saddr;
++
++#ifdef NET_21
++ ixs->skb->h.ipiph = ixs->skb->nh.iph;
++#endif /* NET_21 */
++ return IPSEC_XMIT_OK;
++}
++
++struct xform_functions ipip_xform_funcs[]={
++ { rcv_checks: NULL,
++ rcv_setup_auth: NULL,
++ rcv_calc_auth: NULL,
++ rcv_decrypt: NULL,
++
++ xmit_setup: ipsec_xmit_ipip_setup,
++ xmit_headroom: sizeof(struct iphdr),
++ xmit_needtailroom: 0,
++ },
++};
++
++
++
++
++
++
++
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/net/ipsec/ipsec_life.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,263 @@
++/*
++ * @(#) lifetime structure utilities
++ *
++ * Copyright (C) 2001 Richard Guy Briggs <rgb@freeswan.org>
++ * and Michael Richardson <mcr@freeswan.org>
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ * RCSID $Id$
++ *
++ */
++
++/*
++ * This provides series of utility functions for dealing with lifetime
++ * structures.
++ *
++ * ipsec_check_lifetime - returns -1 hard lifetime exceeded
++ * 0 soft lifetime exceeded
++ * 1 everything is okay
++ * based upon whether or not the count exceeds hard/soft
++ *
++ */
++
++#define __NO_VERSION__
++#include <linux/module.h>
++#include <linux/config.h> /* for CONFIG_IP_FORWARD */
++#include <linux/version.h>
++#include <linux/kernel.h> /* printk() */
++
++#include "openswan/ipsec_param.h"
++
++#include <linux/netdevice.h> /* struct device, struct net_device_stats and other headers */
++#include <linux/etherdevice.h> /* eth_type_trans */
++#include <linux/skbuff.h>
++#include <openswan.h>
++
++#include "openswan/radij.h"
++#include "openswan/ipsec_life.h"
++#include "openswan/ipsec_xform.h"
++#include "openswan/ipsec_eroute.h"
++#include "openswan/ipsec_encap.h"
++#include "openswan/ipsec_radij.h"
++
++#include "openswan/ipsec_sa.h"
++#include "openswan/ipsec_tunnel.h"
++#include "openswan/ipsec_ipe4.h"
++#include "openswan/ipsec_ah.h"
++#include "openswan/ipsec_esp.h"
++
++#ifdef CONFIG_IPSEC_IPCOMP
++#include "openswan/ipcomp.h"
++#endif /* CONFIG_IPSEC_IPCOMP */
++
++#include <pfkeyv2.h>
++#include <pfkey.h>
++
++#include "openswan/ipsec_proto.h"
++
++
++enum ipsec_life_alive
++ipsec_lifetime_check(struct ipsec_lifetime64 *il64,
++ const char *lifename,
++ const char *saname,
++ enum ipsec_life_type ilt,
++ enum ipsec_direction idir,
++ struct ipsec_sa *ips)
++{
++ __u64 count;
++ const char *dir;
++
++ if(saname == NULL) {
++ saname = "unknown-SA";
++ }
++
++ if(idir == ipsec_incoming) {
++ dir = "incoming";
++ } else {
++ dir = "outgoing";
++ }
++
++
++ if(ilt == ipsec_life_timebased) {
++ count = jiffies/HZ - il64->ipl_count;
++ } else {
++ count = il64->ipl_count;
++ }
++
++ if(il64->ipl_hard &&
++ (count > il64->ipl_hard)) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
++ "klips_debug:ipsec_lifetime_check: "
++ "hard %s lifetime of SA:<%s%s%s> %s has been reached, SA expired, "
++ "%s packet dropped.\n",
++ lifename,
++ IPS_XFORM_NAME(ips),
++ saname,
++ dir);
++
++ pfkey_expire(ips, 1);
++ return ipsec_life_harddied;
++ }
++
++ if(il64->ipl_soft &&
++ (count > il64->ipl_soft)) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
++ "klips_debug:ipsec_lifetime_check: "
++ "soft %s lifetime of SA:<%s%s%s> %s has been reached, SA expiring, "
++ "soft expire message sent up, %s packet still processed.\n",
++ lifename,
++ IPS_XFORM_NAME(ips),
++ saname,
++ dir);
++
++ if(ips->ips_state != SADB_SASTATE_DYING) {
++ pfkey_expire(ips, 0);
++ }
++ ips->ips_state = SADB_SASTATE_DYING;
++
++ return ipsec_life_softdied;
++ }
++ return ipsec_life_okay;
++}
++
++
++/*
++ * This function takes a buffer (with length), a lifetime name and type,
++ * and formats a string to represent the current values of the lifetime.
++ *
++ * It returns the number of bytes that the format took (or would take,
++ * if the buffer were large enough: snprintf semantics).
++ * This is used in /proc routines and in debug output.
++ */
++int
++ipsec_lifetime_format(char *buffer,
++ int buflen,
++ char *lifename,
++ enum ipsec_life_type timebaselife,
++ struct ipsec_lifetime64 *lifetime)
++{
++ int len = 0;
++ __u64 count;
++
++ if(timebaselife == ipsec_life_timebased) {
++ count = jiffies/HZ - lifetime->ipl_count;
++ } else {
++ count = lifetime->ipl_count;
++ }
++
++ if(lifetime->ipl_count > 1 ||
++ lifetime->ipl_soft ||
++ lifetime->ipl_hard) {
++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0))
++ len = ipsec_snprintf(buffer, buflen,
++ "%s(%Lu,%Lu,%Lu)",
++ lifename,
++ count,
++ lifetime->ipl_soft,
++ lifetime->ipl_hard);
++#else /* XXX high 32 bits are not displayed */
++ len = ipsec_snprintf(buffer, buflen,
++ "%s(%lu,%lu,%lu)",
++ lifename,
++ (unsigned long)count,
++ (unsigned long)lifetime->ipl_soft,
++ (unsigned long)lifetime->ipl_hard);
++#endif
++ }
++
++ return len;
++}
++
++void
++ipsec_lifetime_update_hard(struct ipsec_lifetime64 *lifetime,
++ __u64 newvalue)
++{
++ if(newvalue &&
++ (!lifetime->ipl_hard ||
++ (newvalue < lifetime->ipl_hard))) {
++ lifetime->ipl_hard = newvalue;
++
++ if(!lifetime->ipl_soft &&
++ (lifetime->ipl_hard < lifetime->ipl_soft)) {
++ lifetime->ipl_soft = lifetime->ipl_hard;
++ }
++ }
++}
++
++void
++ipsec_lifetime_update_soft(struct ipsec_lifetime64 *lifetime,
++ __u64 newvalue)
++{
++ if(newvalue &&
++ (!lifetime->ipl_soft ||
++ (newvalue < lifetime->ipl_soft))) {
++ lifetime->ipl_soft = newvalue;
++
++ if(lifetime->ipl_hard &&
++ (lifetime->ipl_hard < lifetime->ipl_soft)) {
++ lifetime->ipl_soft = lifetime->ipl_hard;
++ }
++ }
++}
++
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.12 2004/04/23 20:44:35 ken
++ * Update comments
++ *
++ * Revision 1.11 2004/04/06 02:49:26 mcr
++ * pullup of algo code from alg-branch.
++ *
++ * Revision 1.10 2004/03/30 11:03:10 paul
++ * two more occurances of snprintf, found by Sam from a users oops msg.
++ *
++ * Revision 1.9 2003/10/31 02:27:55 mcr
++ * pulled up port-selector patches and sa_id elimination.
++ *
++ * Revision 1.8.4.1 2003/10/29 01:30:41 mcr
++ * elimited "struct sa_id".
++ *
++ * Revision 1.8 2003/02/06 02:00:10 rgb
++ * Fixed incorrect debugging text label
++ *
++ * Revision 1.7 2002/05/23 07:16:26 rgb
++ * Fixed absolute/relative reference to lifetime count printout.
++ *
++ * Revision 1.6 2002/04/24 07:55:32 mcr
++ * #include patches and Makefiles for post-reorg compilation.
++ *
++ * Revision 1.5 2002/04/24 07:36:28 mcr
++ * Moved from ./klips/net/ipsec/ipsec_life.c,v
++ *
++ * Revision 1.4 2002/01/29 17:17:55 mcr
++ * moved include of ipsec_param.h to after include of linux/kernel.h
++ * otherwise, it seems that some option that is set in ipsec_param.h
++ * screws up something subtle in the include path to kernel.h, and
++ * it complains on the snprintf() prototype.
++ *
++ * Revision 1.3 2002/01/29 02:13:17 mcr
++ * introduction of ipsec_kversion.h means that include of
++ * ipsec_param.h must preceed any decisions about what files to
++ * include to deal with differences in kernel source.
++ *
++ * Revision 1.2 2001/11/26 09:16:14 rgb
++ * Merge MCR's ipsec_sa, eroute, proc and struct lifetime changes.
++ *
++ * Revision 1.1.2.1 2001/09/25 02:25:57 mcr
++ * lifetime structure created and common functions created.
++ *
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/net/ipsec/ipsec_mast.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,1080 @@
++/*
++ * IPSEC MAST code.
++ * Copyright (C) 1996, 1997 John Ioannidis.
++ * Copyright (C) 1998, 1999, 2000, 2001, 2002 Richard Guy Briggs.
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ */
++
++char ipsec_mast_c_version[] = "RCSID $Id$";
++
++#define __NO_VERSION__
++#include <linux/module.h>
++#include <linux/config.h> /* for CONFIG_IP_FORWARD */
++#include <linux/version.h>
++#include <linux/kernel.h> /* printk() */
++
++#include "freeswan/ipsec_param.h"
++
++#ifdef MALLOC_SLAB
++# include <linux/slab.h> /* kmalloc() */
++#else /* MALLOC_SLAB */
++# include <linux/malloc.h> /* kmalloc() */
++#endif /* MALLOC_SLAB */
++#include <linux/errno.h> /* error codes */
++#include <linux/types.h> /* size_t */
++#include <linux/interrupt.h> /* mark_bh */
++
++#include <linux/netdevice.h> /* struct device, struct net_device_stats, dev_queue_xmit() and other headers */
++#include <linux/etherdevice.h> /* eth_type_trans */
++#include <linux/ip.h> /* struct iphdr */
++#include <linux/tcp.h> /* struct tcphdr */
++#include <linux/udp.h> /* struct udphdr */
++#include <linux/skbuff.h>
++#include <freeswan.h>
++#include <asm/uaccess.h>
++#include <linux/in6.h>
++#include <net/dst.h>
++#undef dev_kfree_skb
++#define dev_kfree_skb(a,b) kfree_skb(a)
++#define PHYSDEV_TYPE
++#include <asm/checksum.h>
++#include <net/icmp.h> /* icmp_send() */
++#include <net/ip.h>
++#include <linux/netfilter_ipv4.h>
++
++#include <linux/if_arp.h>
++
++#include "freeswan/radij.h"
++#include "freeswan/ipsec_life.h"
++#include "freeswan/ipsec_xform.h"
++#include "freeswan/ipsec_eroute.h"
++#include "freeswan/ipsec_encap.h"
++#include "freeswan/ipsec_radij.h"
++#include "freeswan/ipsec_sa.h"
++#include "freeswan/ipsec_tunnel.h"
++#include "freeswan/ipsec_mast.h"
++#include "freeswan/ipsec_ipe4.h"
++#include "freeswan/ipsec_ah.h"
++#include "freeswan/ipsec_esp.h"
++
++#include <pfkeyv2.h>
++#include <pfkey.h>
++
++#include "freeswan/ipsec_proto.h"
++
++int ipsec_maxdevice_count = -1;
++
++DEBUG_NO_STATIC int
++ipsec_mast_open(struct device *dev)
++{
++ struct ipsecpriv *prv = dev->priv;
++
++ /*
++ * Can't open until attached.
++ */
++
++ KLIPS_PRINT(debug_mast & DB_MAST_INIT,
++ "klips_debug:ipsec_mast_open: "
++ "dev = %s, prv->dev = %s\n",
++ dev->name, prv->dev?prv->dev->name:"NONE");
++
++ if (prv->dev == NULL)
++ return -ENODEV;
++
++ MOD_INC_USE_COUNT;
++ return 0;
++}
++
++DEBUG_NO_STATIC int
++ipsec_mast_close(struct device *dev)
++{
++ MOD_DEC_USE_COUNT;
++ return 0;
++}
++
++static inline int ipsec_mast_xmit2(struct sk_buff *skb)
++{
++ return ip_send(skb);
++}
++
++enum ipsec_xmit_value
++ipsec_mast_send(struct ipsec_xmit_state*ixs)
++{
++ /* new route/dst cache code from James Morris */
++ ixs->skb->dev = ixs->physdev;
++ /*skb_orphan(ixs->skb);*/
++ if((ixs->error = ip_route_output(&ixs->route,
++ ixs->skb->nh.iph->daddr,
++ ixs->pass ? 0 : ixs->skb->nh.iph->saddr,
++ RT_TOS(ixs->skb->nh.iph->tos),
++ ixs->physdev->iflink /* rgb: should this be 0? */))) {
++ ixs->stats->tx_errors++;
++ KLIPS_PRINT(debug_mast & DB_MAST_XMIT,
++ "klips_debug:ipsec_xmit_send: "
++ "ip_route_output failed with error code %d, rt->u.dst.dev=%s, dropped\n",
++ ixs->error,
++ ixs->route->u.dst.dev->name);
++ return IPSEC_XMIT_ROUTEERR;
++ }
++ if(ixs->dev == ixs->route->u.dst.dev) {
++ ip_rt_put(ixs->route);
++ /* This is recursion, drop it. */
++ ixs->stats->tx_errors++;
++ KLIPS_PRINT(debug_mast & DB_MAST_XMIT,
++ "klips_debug:ipsec_xmit_send: "
++ "suspect recursion, dev=rt->u.dst.dev=%s, dropped\n",
++ ixs->dev->name);
++ return IPSEC_XMIT_RECURSDETECT;
++ }
++ dst_release(ixs->skb->dst);
++ ixs->skb->dst = &ixs->route->u.dst;
++ ixs->stats->tx_bytes += ixs->skb->len;
++ if(ixs->skb->len < ixs->skb->nh.raw - ixs->skb->data) {
++ ixs->stats->tx_errors++;
++ printk(KERN_WARNING
++ "klips_error:ipsec_xmit_send: "
++ "tried to __skb_pull nh-data=%ld, %d available. This should never happen, please report.\n",
++ (unsigned long)(ixs->skb->nh.raw - ixs->skb->data),
++ ixs->skb->len);
++ return IPSEC_XMIT_PUSHPULLERR;
++ }
++ __skb_pull(ixs->skb, ixs->skb->nh.raw - ixs->skb->data);
++#ifdef SKB_RESET_NFCT
++ nf_conntrack_put(ixs->skb->nfct);
++ ixs->skb->nfct = NULL;
++#ifdef CONFIG_NETFILTER_DEBUG
++ ixs->skb->nf_debug = 0;
++#endif /* CONFIG_NETFILTER_DEBUG */
++#endif /* SKB_RESET_NFCT */
++ KLIPS_PRINT(debug_mast & DB_MAST_XMIT,
++ "klips_debug:ipsec_xmit_send: "
++ "...done, calling ip_send() on device:%s\n",
++ ixs->skb->dev ? ixs->skb->dev->name : "NULL");
++ KLIPS_IP_PRINT(debug_mast & DB_MAST_XMIT, ixs->skb->nh.iph);
++ {
++ int err;
++
++ err = NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, ixs->skb, NULL, ixs->route->u.dst.dev,
++ ipsec_mast_xmit2);
++ if(err != NET_XMIT_SUCCESS && err != NET_XMIT_CN) {
++ if(net_ratelimit())
++ printk(KERN_ERR
++ "klips_error:ipsec_xmit_send: "
++ "ip_send() failed, err=%d\n",
++ -err);
++ ixs->stats->tx_errors++;
++ ixs->stats->tx_aborted_errors++;
++ ixs->skb = NULL;
++ return IPSEC_XMIT_IPSENDFAILURE;
++ }
++ }
++ ixs->stats->tx_packets++;
++
++ ixs->skb = NULL;
++
++ return IPSEC_XMIT_OK;
++}
++
++void
++ipsec_mast_cleanup(struct ipsec_xmit_state*ixs)
++{
++#if defined(HAS_NETIF_QUEUE) || defined (HAVE_NETIF_QUEUE)
++ netif_wake_queue(ixs->dev);
++#else /* defined(HAS_NETIF_QUEUE) || defined (HAVE_NETIF_QUEUE) */
++ ixs->dev->tbusy = 0;
++#endif /* defined(HAS_NETIF_QUEUE) || defined (HAVE_NETIF_QUEUE) */
++ if(ixs->saved_header) {
++ kfree(ixs->saved_header);
++ }
++ if(ixs->skb) {
++ dev_kfree_skb(ixs->skb, FREE_WRITE);
++ }
++ if(ixs->oskb) {
++ dev_kfree_skb(ixs->oskb, FREE_WRITE);
++ }
++ if (ixs->ips.ips_ident_s.data) {
++ kfree(ixs->ips.ips_ident_s.data);
++ }
++ if (ixs->ips.ips_ident_d.data) {
++ kfree(ixs->ips.ips_ident_d.data);
++ }
++}
++
++#if 0
++/*
++ * This function assumes it is being called from dev_queue_xmit()
++ * and that skb is filled properly by that function.
++ */
++int
++ipsec_mast_start_xmit(struct sk_buff *skb, struct device *dev, IPsecSAref_t SAref)
++{
++ struct ipsec_xmit_state ixs_mem;
++ struct ipsec_xmit_state *ixs = &ixs_mem;
++ enum ipsec_xmit_value stat = IPSEC_XMIT_OK;
++
++ /* dev could be a mast device, but should be optional, I think... */
++ /* SAref is also optional, but one of the two must be present. */
++ /* I wonder if it could accept no device or saref and guess? */
++
++/* ipsec_xmit_sanity_check_dev(ixs); */
++
++ ipsec_xmit_sanity_check_skb(ixs);
++
++ ipsec_xmit_adjust_hard_header(ixs);
++
++ stat = ipsec_xmit_encap_bundle(ixs);
++ if(stat != IPSEC_XMIT_OK) {
++ /* SA processing failed */
++ }
++
++ ipsec_xmit_hard_header_restore();
++}
++#endif
++
++DEBUG_NO_STATIC struct net_device_stats *
++ipsec_mast_get_stats(struct device *dev)
++{
++ return &(((struct ipsecpriv *)(dev->priv))->mystats);
++}
++
++/*
++ * Revectored calls.
++ * For each of these calls, a field exists in our private structure.
++ */
++
++DEBUG_NO_STATIC int
++ipsec_mast_hard_header(struct sk_buff *skb, struct device *dev,
++ unsigned short type, void *daddr, void *saddr, unsigned len)
++{
++ struct ipsecpriv *prv = dev->priv;
++ struct device *tmp;
++ int ret;
++ struct net_device_stats *stats; /* This device's statistics */
++
++ if(skb == NULL) {
++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC,
++ "klips_debug:ipsec_mast_hard_header: "
++ "no skb...\n");
++ return -ENODATA;
++ }
++
++ if(dev == NULL) {
++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC,
++ "klips_debug:ipsec_mast_hard_header: "
++ "no device...\n");
++ return -ENODEV;
++ }
++
++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC,
++ "klips_debug:ipsec_mast_hard_header: "
++ "skb->dev=%s dev=%s.\n",
++ skb->dev ? skb->dev->name : "NULL",
++ dev->name);
++
++ if(prv == NULL) {
++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC,
++ "klips_debug:ipsec_mast_hard_header: "
++ "no private space associated with dev=%s\n",
++ dev->name ? dev->name : "NULL");
++ return -ENODEV;
++ }
++
++ stats = (struct net_device_stats *) &(prv->mystats);
++
++ if(prv->dev == NULL) {
++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC,
++ "klips_debug:ipsec_mast_hard_header: "
++ "no physical device associated with dev=%s\n",
++ dev->name ? dev->name : "NULL");
++ stats->tx_dropped++;
++ return -ENODEV;
++ }
++
++ /* check if we have to send a IPv6 packet. It might be a Router
++ Solicitation, where the building of the packet happens in
++ reverse order:
++ 1. ll hdr,
++ 2. IPv6 hdr,
++ 3. ICMPv6 hdr
++ -> skb->nh.raw is still uninitialized when this function is
++ called!! If this is no IPv6 packet, we can print debugging
++ messages, otherwise we skip all debugging messages and just
++ build the ll header */
++ if(type != ETH_P_IPV6) {
++ /* execute this only, if we don't have to build the
++ header for a IPv6 packet */
++ if(!prv->hard_header) {
++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC,
++ "klips_debug:ipsec_mast_hard_header: "
++ "physical device has been detached, packet dropped 0p%p->0p%p len=%d type=%d dev=%s->NULL ",
++ saddr,
++ daddr,
++ len,
++ type,
++ dev->name);
++ KLIPS_PRINTMORE(debug_mast & DB_MAST_REVEC,
++ "ip=%08x->%08x\n",
++ (__u32)ntohl(skb->nh.iph->saddr),
++ (__u32)ntohl(skb->nh.iph->daddr) );
++ stats->tx_dropped++;
++ return -ENODEV;
++ }
++
++#define da ((struct device *)(prv->dev))->dev_addr
++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC,
++ "klips_debug:ipsec_mast_hard_header: "
++ "Revectored 0p%p->0p%p len=%d type=%d dev=%s->%s dev_addr=%02x:%02x:%02x:%02x:%02x:%02x ",
++ saddr,
++ daddr,
++ len,
++ type,
++ dev->name,
++ prv->dev->name,
++ da[0], da[1], da[2], da[3], da[4], da[5]);
++ KLIPS_PRINTMORE(debug_mast & DB_MAST_REVEC,
++ "ip=%08x->%08x\n",
++ (__u32)ntohl(skb->nh.iph->saddr),
++ (__u32)ntohl(skb->nh.iph->daddr) );
++ } else {
++ KLIPS_PRINT(debug_mast,
++ "klips_debug:ipsec_mast_hard_header: "
++ "is IPv6 packet, skip debugging messages, only revector and build linklocal header.\n");
++ }
++ tmp = skb->dev;
++ skb->dev = prv->dev;
++ ret = prv->hard_header(skb, prv->dev, type, (void *)daddr, (void *)saddr, len);
++ skb->dev = tmp;
++ return ret;
++}
++
++DEBUG_NO_STATIC int
++ipsec_mast_rebuild_header(struct sk_buff *skb)
++{
++ struct ipsecpriv *prv = skb->dev->priv;
++ struct device *tmp;
++ int ret;
++ struct net_device_stats *stats; /* This device's statistics */
++
++ if(skb->dev == NULL) {
++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC,
++ "klips_debug:ipsec_mast_rebuild_header: "
++ "no device...");
++ return -ENODEV;
++ }
++
++ if(prv == NULL) {
++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC,
++ "klips_debug:ipsec_mast_rebuild_header: "
++ "no private space associated with dev=%s",
++ skb->dev->name ? skb->dev->name : "NULL");
++ return -ENODEV;
++ }
++
++ stats = (struct net_device_stats *) &(prv->mystats);
++
++ if(prv->dev == NULL) {
++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC,
++ "klips_debug:ipsec_mast_rebuild_header: "
++ "no physical device associated with dev=%s",
++ skb->dev->name ? skb->dev->name : "NULL");
++ stats->tx_dropped++;
++ return -ENODEV;
++ }
++
++ if(!prv->rebuild_header) {
++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC,
++ "klips_debug:ipsec_mast_rebuild_header: "
++ "physical device has been detached, packet dropped skb->dev=%s->NULL ",
++ skb->dev->name);
++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC,
++ "ip=%08x->%08x\n",
++ (__u32)ntohl(skb->nh.iph->saddr),
++ (__u32)ntohl(skb->nh.iph->daddr) );
++ stats->tx_dropped++;
++ return -ENODEV;
++ }
++
++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC,
++ "klips_debug:ipsec_mast: "
++ "Revectored rebuild_header dev=%s->%s ",
++ skb->dev->name, prv->dev->name);
++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC,
++ "ip=%08x->%08x\n",
++ (__u32)ntohl(skb->nh.iph->saddr),
++ (__u32)ntohl(skb->nh.iph->daddr) );
++ tmp = skb->dev;
++ skb->dev = prv->dev;
++
++ ret = prv->rebuild_header(skb);
++ skb->dev = tmp;
++ return ret;
++}
++
++DEBUG_NO_STATIC int
++ipsec_mast_set_mac_address(struct device *dev, void *addr)
++{
++ struct ipsecpriv *prv = dev->priv;
++
++ struct net_device_stats *stats; /* This device's statistics */
++
++ if(dev == NULL) {
++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC,
++ "klips_debug:ipsec_mast_set_mac_address: "
++ "no device...");
++ return -ENODEV;
++ }
++
++ if(prv == NULL) {
++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC,
++ "klips_debug:ipsec_mast_set_mac_address: "
++ "no private space associated with dev=%s",
++ dev->name ? dev->name : "NULL");
++ return -ENODEV;
++ }
++
++ stats = (struct net_device_stats *) &(prv->mystats);
++
++ if(prv->dev == NULL) {
++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC,
++ "klips_debug:ipsec_mast_set_mac_address: "
++ "no physical device associated with dev=%s",
++ dev->name ? dev->name : "NULL");
++ stats->tx_dropped++;
++ return -ENODEV;
++ }
++
++ if(!prv->set_mac_address) {
++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC,
++ "klips_debug:ipsec_mast_set_mac_address: "
++ "physical device has been detached, cannot set - skb->dev=%s->NULL\n",
++ dev->name);
++ return -ENODEV;
++ }
++
++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC,
++ "klips_debug:ipsec_mast_set_mac_address: "
++ "Revectored dev=%s->%s addr=0p%p\n",
++ dev->name, prv->dev->name, addr);
++ return prv->set_mac_address(prv->dev, addr);
++
++}
++
++DEBUG_NO_STATIC void
++ipsec_mast_cache_update(struct hh_cache *hh, struct device *dev, unsigned char * haddr)
++{
++ struct ipsecpriv *prv = dev->priv;
++
++ struct net_device_stats *stats; /* This device's statistics */
++
++ if(dev == NULL) {
++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC,
++ "klips_debug:ipsec_mast_cache_update: "
++ "no device...");
++ return;
++ }
++
++ if(prv == NULL) {
++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC,
++ "klips_debug:ipsec_mast_cache_update: "
++ "no private space associated with dev=%s",
++ dev->name ? dev->name : "NULL");
++ return;
++ }
++
++ stats = (struct net_device_stats *) &(prv->mystats);
++
++ if(prv->dev == NULL) {
++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC,
++ "klips_debug:ipsec_mast_cache_update: "
++ "no physical device associated with dev=%s",
++ dev->name ? dev->name : "NULL");
++ stats->tx_dropped++;
++ return;
++ }
++
++ if(!prv->header_cache_update) {
++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC,
++ "klips_debug:ipsec_mast_cache_update: "
++ "physical device has been detached, cannot set - skb->dev=%s->NULL\n",
++ dev->name);
++ return;
++ }
++
++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC,
++ "klips_debug:ipsec_mast: "
++ "Revectored cache_update\n");
++ prv->header_cache_update(hh, prv->dev, haddr);
++ return;
++}
++
++DEBUG_NO_STATIC int
++ipsec_mast_neigh_setup(struct neighbour *n)
++{
++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC,
++ "klips_debug:ipsec_mast_neigh_setup:\n");
++
++ if (n->nud_state == NUD_NONE) {
++ n->ops = &arp_broken_ops;
++ n->output = n->ops->output;
++ }
++ return 0;
++}
++
++DEBUG_NO_STATIC int
++ipsec_mast_neigh_setup_dev(struct device *dev, struct neigh_parms *p)
++{
++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC,
++ "klips_debug:ipsec_mast_neigh_setup_dev: "
++ "setting up %s\n",
++ dev ? dev->name : "NULL");
++
++ if (p->tbl->family == AF_INET) {
++ p->neigh_setup = ipsec_mast_neigh_setup;
++ p->ucast_probes = 0;
++ p->mcast_probes = 0;
++ }
++ return 0;
++}
++
++/*
++ * We call the attach routine to attach another device.
++ */
++
++DEBUG_NO_STATIC int
++ipsec_mast_attach(struct device *dev, struct device *physdev)
++{
++ int i;
++ struct ipsecpriv *prv = dev->priv;
++
++ if(dev == NULL) {
++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC,
++ "klips_debug:ipsec_mast_attach: "
++ "no device...");
++ return -ENODEV;
++ }
++
++ if(prv == NULL) {
++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC,
++ "klips_debug:ipsec_mast_attach: "
++ "no private space associated with dev=%s",
++ dev->name ? dev->name : "NULL");
++ return -ENODATA;
++ }
++
++ prv->dev = physdev;
++ prv->hard_start_xmit = physdev->hard_start_xmit;
++ prv->get_stats = physdev->get_stats;
++
++ if (physdev->hard_header) {
++ prv->hard_header = physdev->hard_header;
++ dev->hard_header = ipsec_mast_hard_header;
++ } else
++ dev->hard_header = NULL;
++
++ if (physdev->rebuild_header) {
++ prv->rebuild_header = physdev->rebuild_header;
++ dev->rebuild_header = ipsec_mast_rebuild_header;
++ } else
++ dev->rebuild_header = NULL;
++
++ if (physdev->set_mac_address) {
++ prv->set_mac_address = physdev->set_mac_address;
++ dev->set_mac_address = ipsec_mast_set_mac_address;
++ } else
++ dev->set_mac_address = NULL;
++
++ if (physdev->header_cache_update) {
++ prv->header_cache_update = physdev->header_cache_update;
++ dev->header_cache_update = ipsec_mast_cache_update;
++ } else
++ dev->header_cache_update = NULL;
++
++ dev->hard_header_len = physdev->hard_header_len;
++
++/* prv->neigh_setup = physdev->neigh_setup; */
++ dev->neigh_setup = ipsec_mast_neigh_setup_dev;
++ dev->mtu = 16260; /* 0xfff0; */ /* dev->mtu; */
++ prv->mtu = physdev->mtu;
++
++#ifdef PHYSDEV_TYPE
++ dev->type = physdev->type; /* ARPHRD_MAST; */
++#endif /* PHYSDEV_TYPE */
++
++ dev->addr_len = physdev->addr_len;
++ for (i=0; i<dev->addr_len; i++) {
++ dev->dev_addr[i] = physdev->dev_addr[i];
++ }
++#ifdef CONFIG_IPSEC_DEBUG
++ if(debug_mast & DB_MAST_INIT) {
++ printk(KERN_INFO "klips_debug:ipsec_mast_attach: "
++ "physical device %s being attached has HW address: %2x",
++ physdev->name, physdev->dev_addr[0]);
++ for (i=1; i < physdev->addr_len; i++) {
++ printk(":%02x", physdev->dev_addr[i]);
++ }
++ printk("\n");
++ }
++#endif /* CONFIG_IPSEC_DEBUG */
++
++ return 0;
++}
++
++/*
++ * We call the detach routine to detach the ipsec mast from another device.
++ */
++
++DEBUG_NO_STATIC int
++ipsec_mast_detach(struct device *dev)
++{
++ int i;
++ struct ipsecpriv *prv = dev->priv;
++
++ if(dev == NULL) {
++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC,
++ "klips_debug:ipsec_mast_detach: "
++ "no device...");
++ return -ENODEV;
++ }
++
++ if(prv == NULL) {
++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC,
++ "klips_debug:ipsec_mast_detach: "
++ "no private space associated with dev=%s",
++ dev->name ? dev->name : "NULL");
++ return -ENODATA;
++ }
++
++ KLIPS_PRINT(debug_mast & DB_MAST_INIT,
++ "klips_debug:ipsec_mast_detach: "
++ "physical device %s being detached from virtual device %s\n",
++ prv->dev ? prv->dev->name : "NULL",
++ dev->name);
++
++ prv->dev = NULL;
++ prv->hard_start_xmit = NULL;
++ prv->get_stats = NULL;
++
++ prv->hard_header = NULL;
++#ifdef DETACH_AND_DOWN
++ dev->hard_header = NULL;
++#endif /* DETACH_AND_DOWN */
++
++ prv->rebuild_header = NULL;
++#ifdef DETACH_AND_DOWN
++ dev->rebuild_header = NULL;
++#endif /* DETACH_AND_DOWN */
++
++ prv->set_mac_address = NULL;
++#ifdef DETACH_AND_DOWN
++ dev->set_mac_address = NULL;
++#endif /* DETACH_AND_DOWN */
++
++ prv->header_cache_update = NULL;
++#ifdef DETACH_AND_DOWN
++ dev->header_cache_update = NULL;
++#endif /* DETACH_AND_DOWN */
++
++#ifdef DETACH_AND_DOWN
++ dev->neigh_setup = NULL;
++#endif /* DETACH_AND_DOWN */
++
++ dev->hard_header_len = 0;
++#ifdef DETACH_AND_DOWN
++ dev->mtu = 0;
++#endif /* DETACH_AND_DOWN */
++ prv->mtu = 0;
++ for (i=0; i<MAX_ADDR_LEN; i++) {
++ dev->dev_addr[i] = 0;
++ }
++ dev->addr_len = 0;
++#ifdef PHYSDEV_TYPE
++ dev->type = ARPHRD_VOID; /* ARPHRD_MAST; */
++#endif /* PHYSDEV_TYPE */
++
++ return 0;
++}
++
++/*
++ * We call the clear routine to detach all ipsec masts from other devices.
++ */
++DEBUG_NO_STATIC int
++ipsec_mast_clear(void)
++{
++ int i;
++ struct device *ipsecdev = NULL, *prvdev;
++ struct ipsecpriv *prv;
++ char name[9];
++ int ret;
++
++ KLIPS_PRINT(debug_mast & DB_MAST_INIT,
++ "klips_debug:ipsec_mast_clear: .\n");
++
++ for(i = 0; i < IPSEC_NUM_IF; i++) {
++ sprintf(name, IPSEC_DEV_FORMAT, i);
++ if((ipsecdev = ipsec_dev_get(name)) != NULL) {
++ if((prv = (struct ipsecpriv *)(ipsecdev->priv))) {
++ prvdev = (struct device *)(prv->dev);
++ if(prvdev) {
++ KLIPS_PRINT(debug_mast & DB_MAST_INIT,
++ "klips_debug:ipsec_mast_clear: "
++ "physical device for device %s is %s\n",
++ name, prvdev->name);
++ if((ret = ipsec_mast_detach(ipsecdev))) {
++ KLIPS_PRINT(debug_mast & DB_MAST_INIT,
++ "klips_debug:ipsec_mast_clear: "
++ "error %d detatching device %s from device %s.\n",
++ ret, name, prvdev->name);
++ return ret;
++ }
++ }
++ }
++ }
++ }
++ return 0;
++}
++
++DEBUG_NO_STATIC int
++ipsec_mast_ioctl(struct device *dev, struct ifreq *ifr, int cmd)
++{
++ struct ipsecmastconf *cf = (struct ipsecmastconf *)&ifr->ifr_data;
++ struct ipsecpriv *prv = dev->priv;
++ struct device *them; /* physical device */
++#ifdef CONFIG_IP_ALIAS
++ char *colon;
++ char realphysname[IFNAMSIZ];
++#endif /* CONFIG_IP_ALIAS */
++
++ if(dev == NULL) {
++ KLIPS_PRINT(debug_mast & DB_MAST_INIT,
++ "klips_debug:ipsec_mast_ioctl: "
++ "device not supplied.\n");
++ return -ENODEV;
++ }
++
++ KLIPS_PRINT(debug_mast & DB_MAST_INIT,
++ "klips_debug:ipsec_mast_ioctl: "
++ "tncfg service call #%d for dev=%s\n",
++ cmd,
++ dev->name ? dev->name : "NULL");
++ switch (cmd) {
++ /* attach a virtual ipsec? device to a physical device */
++ case IPSEC_SET_DEV:
++ KLIPS_PRINT(debug_mast & DB_MAST_INIT,
++ "klips_debug:ipsec_mast_ioctl: "
++ "calling ipsec_mast_attatch...\n");
++#ifdef CONFIG_IP_ALIAS
++ /* If this is an IP alias interface, get its real physical name */
++ strncpy(realphysname, cf->cf_name, IFNAMSIZ);
++ realphysname[IFNAMSIZ-1] = 0;
++ colon = strchr(realphysname, ':');
++ if (colon) *colon = 0;
++ them = ipsec_dev_get(realphysname);
++#else /* CONFIG_IP_ALIAS */
++ them = ipsec_dev_get(cf->cf_name);
++#endif /* CONFIG_IP_ALIAS */
++
++ if (them == NULL) {
++ KLIPS_PRINT(debug_mast & DB_MAST_INIT,
++ "klips_debug:ipsec_mast_ioctl: "
++ "physical device %s requested is null\n",
++ cf->cf_name);
++ return -ENXIO;
++ }
++
++#if 0
++ if (them->flags & IFF_UP) {
++ KLIPS_PRINT(debug_mast & DB_MAST_INIT,
++ "klips_debug:ipsec_mast_ioctl: "
++ "physical device %s requested is not up.\n",
++ cf->cf_name);
++ return -ENXIO;
++ }
++#endif
++
++ if (prv && prv->dev) {
++ KLIPS_PRINT(debug_mast & DB_MAST_INIT,
++ "klips_debug:ipsec_mast_ioctl: "
++ "virtual device is already connected to %s.\n",
++ prv->dev->name ? prv->dev->name : "NULL");
++ return -EBUSY;
++ }
++ return ipsec_mast_attach(dev, them);
++
++ case IPSEC_DEL_DEV:
++ KLIPS_PRINT(debug_mast & DB_MAST_INIT,
++ "klips_debug:ipsec_mast_ioctl: "
++ "calling ipsec_mast_detatch.\n");
++ if (! prv->dev) {
++ KLIPS_PRINT(debug_mast & DB_MAST_INIT,
++ "klips_debug:ipsec_mast_ioctl: "
++ "physical device not connected.\n");
++ return -ENODEV;
++ }
++ return ipsec_mast_detach(dev);
++
++ case IPSEC_CLR_DEV:
++ KLIPS_PRINT(debug_mast & DB_MAST_INIT,
++ "klips_debug:ipsec_mast_ioctl: "
++ "calling ipsec_mast_clear.\n");
++ return ipsec_mast_clear();
++
++ default:
++ KLIPS_PRINT(debug_mast & DB_MAST_INIT,
++ "klips_debug:ipsec_mast_ioctl: "
++ "unknown command %d.\n",
++ cmd);
++ return -EOPNOTSUPP;
++ }
++}
++
++int
++ipsec_mast_device_event(struct notifier_block *unused, unsigned long event, void *ptr)
++{
++ struct device *dev = ptr;
++ struct device *ipsec_dev;
++ struct ipsecpriv *priv;
++ char name[9];
++ int i;
++
++ if (dev == NULL) {
++ KLIPS_PRINT(debug_mast & DB_MAST_INIT,
++ "klips_debug:ipsec_mast_device_event: "
++ "dev=NULL for event type %ld.\n",
++ event);
++ return(NOTIFY_DONE);
++ }
++
++ /* check for loopback devices */
++ if (dev && (dev->flags & IFF_LOOPBACK)) {
++ return(NOTIFY_DONE);
++ }
++
++ switch (event) {
++ case NETDEV_DOWN:
++ /* look very carefully at the scope of these compiler
++ directives before changing anything... -- RGB */
++
++ case NETDEV_UNREGISTER:
++ switch (event) {
++ case NETDEV_DOWN:
++ KLIPS_PRINT(debug_mast & DB_MAST_INIT,
++ "klips_debug:ipsec_mast_device_event: "
++ "NETDEV_DOWN dev=%s flags=%x\n",
++ dev->name,
++ dev->flags);
++ if(strncmp(dev->name, "ipsec", strlen("ipsec")) == 0) {
++ printk(KERN_CRIT "IPSEC EVENT: KLIPS device %s shut down.\n",
++ dev->name);
++ }
++ break;
++ case NETDEV_UNREGISTER:
++ KLIPS_PRINT(debug_mast & DB_MAST_INIT,
++ "klips_debug:ipsec_mast_device_event: "
++ "NETDEV_UNREGISTER dev=%s flags=%x\n",
++ dev->name,
++ dev->flags);
++ break;
++ }
++
++ /* find the attached physical device and detach it. */
++ for(i = 0; i < IPSEC_NUM_IF; i++) {
++ sprintf(name, IPSEC_DEV_FORMAT, i);
++ ipsec_dev = ipsec_dev_get(name);
++ if(ipsec_dev) {
++ priv = (struct ipsecpriv *)(ipsec_dev->priv);
++ if(priv) {
++ ;
++ if(((struct device *)(priv->dev)) == dev) {
++ /* dev_close(ipsec_dev); */
++ /* return */ ipsec_mast_detach(ipsec_dev);
++ KLIPS_PRINT(debug_mast & DB_MAST_INIT,
++ "klips_debug:ipsec_mast_device_event: "
++ "device '%s' has been detached.\n",
++ ipsec_dev->name);
++ break;
++ }
++ } else {
++ KLIPS_PRINT(debug_mast & DB_MAST_INIT,
++ "klips_debug:ipsec_mast_device_event: "
++ "device '%s' has no private data space!\n",
++ ipsec_dev->name);
++ }
++ }
++ }
++ break;
++ case NETDEV_UP:
++ KLIPS_PRINT(debug_mast & DB_MAST_INIT,
++ "klips_debug:ipsec_mast_device_event: "
++ "NETDEV_UP dev=%s\n",
++ dev->name);
++ break;
++ case NETDEV_REBOOT:
++ KLIPS_PRINT(debug_mast & DB_MAST_INIT,
++ "klips_debug:ipsec_mast_device_event: "
++ "NETDEV_REBOOT dev=%s\n",
++ dev->name);
++ break;
++ case NETDEV_CHANGE:
++ KLIPS_PRINT(debug_mast & DB_MAST_INIT,
++ "klips_debug:ipsec_mast_device_event: "
++ "NETDEV_CHANGE dev=%s flags=%x\n",
++ dev->name,
++ dev->flags);
++ break;
++ case NETDEV_REGISTER:
++ KLIPS_PRINT(debug_mast & DB_MAST_INIT,
++ "klips_debug:ipsec_mast_device_event: "
++ "NETDEV_REGISTER dev=%s\n",
++ dev->name);
++ break;
++ case NETDEV_CHANGEMTU:
++ KLIPS_PRINT(debug_mast & DB_MAST_INIT,
++ "klips_debug:ipsec_mast_device_event: "
++ "NETDEV_CHANGEMTU dev=%s to mtu=%d\n",
++ dev->name,
++ dev->mtu);
++ break;
++ case NETDEV_CHANGEADDR:
++ KLIPS_PRINT(debug_mast & DB_MAST_INIT,
++ "klips_debug:ipsec_mast_device_event: "
++ "NETDEV_CHANGEADDR dev=%s\n",
++ dev->name);
++ break;
++ case NETDEV_GOING_DOWN:
++ KLIPS_PRINT(debug_mast & DB_MAST_INIT,
++ "klips_debug:ipsec_mast_device_event: "
++ "NETDEV_GOING_DOWN dev=%s\n",
++ dev->name);
++ break;
++ case NETDEV_CHANGENAME:
++ KLIPS_PRINT(debug_mast & DB_MAST_INIT,
++ "klips_debug:ipsec_mast_device_event: "
++ "NETDEV_CHANGENAME dev=%s\n",
++ dev->name);
++ break;
++ default:
++ KLIPS_PRINT(debug_mast & DB_MAST_INIT,
++ "klips_debug:ipsec_mast_device_event: "
++ "event type %ld unrecognised for dev=%s\n",
++ event,
++ dev->name);
++ break;
++ }
++ return NOTIFY_DONE;
++}
++
++/*
++ * Called when an ipsec mast device is initialized.
++ * The ipsec mast device structure is passed to us.
++ */
++
++int
++ipsec_mast_init(struct device *dev)
++{
++ int i;
++
++ KLIPS_PRINT(debug_mast,
++ "klips_debug:ipsec_mast_init: "
++ "allocating %lu bytes initialising device: %s\n",
++ (unsigned long) sizeof(struct ipsecpriv),
++ dev->name ? dev->name : "NULL");
++
++ /* Add our mast functions to the device */
++ dev->open = ipsec_mast_open;
++ dev->stop = ipsec_mast_close;
++ dev->hard_start_xmit = ipsec_mast_start_xmit;
++ dev->get_stats = ipsec_mast_get_stats;
++
++ dev->priv = kmalloc(sizeof(struct ipsecpriv), GFP_KERNEL);
++ if (dev->priv == NULL)
++ return -ENOMEM;
++ memset((caddr_t)(dev->priv), 0, sizeof(struct ipsecpriv));
++
++ for(i = 0; i < sizeof(zeroes); i++) {
++ ((__u8*)(zeroes))[i] = 0;
++ }
++
++ dev->set_multicast_list = NULL;
++ dev->do_ioctl = ipsec_mast_ioctl;
++ dev->hard_header = NULL;
++ dev->rebuild_header = NULL;
++ dev->set_mac_address = NULL;
++ dev->header_cache_update= NULL;
++ dev->neigh_setup = ipsec_mast_neigh_setup_dev;
++ dev->hard_header_len = 0;
++ dev->mtu = 0;
++ dev->addr_len = 0;
++ dev->type = ARPHRD_VOID; /* ARPHRD_MAST; */ /* ARPHRD_ETHER; */
++ dev->tx_queue_len = 10; /* Small queue */
++ memset((caddr_t)(dev->broadcast),0xFF, ETH_ALEN); /* what if this is not attached to ethernet? */
++
++ /* New-style flags. */
++ dev->flags = IFF_NOARP /* 0 */ /* Petr Novak */;
++ dev_init_buffers(dev);
++
++ /* We're done. Have I forgotten anything? */
++ return 0;
++}
++
++/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
++/* Module specific interface (but it links with the rest of IPSEC) */
++/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
++
++int
++ipsec_mast_probe(struct device *dev)
++{
++ ipsec_mast_init(dev);
++ return 0;
++}
++
++int
++ipsec_mast_init_devices(void)
++{
++ return 0;
++}
++
++/* void */
++int
++ipsec_mast_cleanup_devices(void)
++{
++ int error = 0;
++ int i;
++ char name[10];
++ struct device *dev_mast;
++
++ for(i = 0; i < ipsec_mastdevice_count; i++) {
++ sprintf(name, MAST_DEV_FORMAT, i);
++ if((dev_mast = ipsec_dev_get(name)) == NULL) {
++ break;
++ }
++ unregister_netdev(dev_mast);
++ kfree(dev_mast->priv);
++ dev_mast->priv=NULL;
++ }
++ return error;
++}
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.3 2003/10/31 02:27:55 mcr
++ * pulled up port-selector patches and sa_id elimination.
++ *
++ * Revision 1.2.4.1 2003/10/29 01:30:41 mcr
++ * elimited "struct sa_id".
++ *
++ * Revision 1.2 2003/06/22 20:06:17 mcr
++ * refactored mast code still had lots of ipsecX junk in it.
++ *
++ * Revision 1.1 2003/02/12 19:31:12 rgb
++ * Refactored from ipsec_tunnel.c
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/net/ipsec/ipsec_md5c.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,448 @@
++/*
++ * RCSID $Id$
++ */
++
++/*
++ * The rest of the code is derived from MD5C.C by RSADSI. Minor cosmetic
++ * changes to accomodate it in the kernel by ji.
++ */
++
++#include <asm/byteorder.h>
++#include <linux/string.h>
++
++#include "openswan/ipsec_md5h.h"
++
++/* MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm
++ */
++
++/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
++rights reserved.
++
++License to copy and use this software is granted provided that it
++is identified as the "RSA Data Security, Inc. MD5 Message-Digest
++Algorithm" in all material mentioning or referencing this software
++or this function.
++
++License is also granted to make and use derivative works provided
++that such works are identified as "derived from the RSA Data
++Security, Inc. MD5 Message-Digest Algorithm" in all material
++mentioning or referencing the derived work.
++
++RSA Data Security, Inc. makes no representations concerning either
++the merchantability of this software or the suitability of this
++software for any particular purpose. It is provided "as is"
++without express or implied warranty of any kind.
++
++These notices must be retained in any copies of any part of this
++documentation and/or software.
++ */
++
++/*
++ * Additions by JI
++ *
++ * HAVEMEMCOPY is defined if mem* routines are available
++ *
++ * HAVEHTON is defined if htons() and htonl() can be used
++ * for big/little endian conversions
++ *
++ */
++
++#define HAVEMEMCOPY
++#ifdef __LITTLE_ENDIAN
++#define LITTLENDIAN
++#endif
++#ifdef __BIG_ENDIAN
++#define BIGENDIAN
++#endif
++
++/* Constants for MD5Transform routine.
++ */
++
++#define S11 7
++#define S12 12
++#define S13 17
++#define S14 22
++#define S21 5
++#define S22 9
++#define S23 14
++#define S24 20
++#define S31 4
++#define S32 11
++#define S33 16
++#define S34 23
++#define S41 6
++#define S42 10
++#define S43 15
++#define S44 21
++
++static void MD5Transform PROTO_LIST ((UINT4 [4], unsigned char [64]));
++
++#ifdef LITTLEENDIAN
++#define Encode MD5_memcpy
++#define Decode MD5_memcpy
++#else
++static void Encode PROTO_LIST
++ ((unsigned char *, UINT4 *, unsigned int));
++static void Decode PROTO_LIST
++ ((UINT4 *, unsigned char *, unsigned int));
++#endif
++
++#ifdef HAVEMEMCOPY
++/* no need to include <memory.h> here; <linux/string.h> defines these */
++#define MD5_memcpy memcpy
++#define MD5_memset memset
++#else
++#ifdef HAVEBCOPY
++#define MD5_memcpy(_a,_b,_c) bcopy((_b),(_a),(_c))
++#define MD5_memset(_a,_b,_c) bzero((_a),(_c))
++#else
++static void MD5_memcpy PROTO_LIST ((POINTER, POINTER, unsigned int));
++static void MD5_memset PROTO_LIST ((POINTER, int, unsigned int));
++#endif
++#endif
++static unsigned char PADDING[64] = {
++ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
++};
++
++/* F, G, H and I are basic MD5 functions.
++ */
++#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
++#define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
++#define H(x, y, z) ((x) ^ (y) ^ (z))
++#define I(x, y, z) ((y) ^ ((x) | (~z)))
++
++/* ROTATE_LEFT rotates x left n bits.
++ */
++#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
++
++/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
++Rotation is separate from addition to prevent recomputation.
++ */
++#define FF(a, b, c, d, x, s, ac) { \
++ (a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \
++ (a) = ROTATE_LEFT ((a), (s)); \
++ (a) += (b); \
++ }
++#define GG(a, b, c, d, x, s, ac) { \
++ (a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \
++ (a) = ROTATE_LEFT ((a), (s)); \
++ (a) += (b); \
++ }
++#define HH(a, b, c, d, x, s, ac) { \
++ (a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \
++ (a) = ROTATE_LEFT ((a), (s)); \
++ (a) += (b); \
++ }
++#define II(a, b, c, d, x, s, ac) { \
++ (a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \
++ (a) = ROTATE_LEFT ((a), (s)); \
++ (a) += (b); \
++ }
++
++/*
++ * MD5 initialization. Begins an MD5 operation, writing a new context.
++ */
++void MD5Init(void *vcontext)
++{
++ MD5_CTX *context = vcontext;
++
++ context->count[0] = context->count[1] = 0;
++ /* Load magic initialization constants.
++*/
++ context->state[0] = 0x67452301;
++ context->state[1] = 0xefcdab89;
++ context->state[2] = 0x98badcfe;
++ context->state[3] = 0x10325476;
++}
++
++/* MD5 block update operation. Continues an MD5 message-digest
++ operation, processing another message block, and updating the
++ context.
++ */
++void MD5Update (vcontext, input, inputLen)
++ void *vcontext;
++ unsigned char *input; /* input block */
++ __u32 inputLen; /* length of input block */
++{
++ MD5_CTX *context = vcontext;
++ __u32 i;
++ unsigned int index, partLen;
++
++ /* Compute number of bytes mod 64 */
++ index = (unsigned int)((context->count[0] >> 3) & 0x3F);
++
++ /* Update number of bits */
++ if ((context->count[0] += ((UINT4)inputLen << 3))
++ < ((UINT4)inputLen << 3))
++ context->count[1]++;
++ context->count[1] += ((UINT4)inputLen >> 29);
++
++ partLen = 64 - index;
++
++ /* Transform as many times as possible.
++*/
++ if (inputLen >= partLen) {
++ MD5_memcpy
++ ((POINTER)&context->buffer[index], (POINTER)input, partLen);
++ MD5Transform (context->state, context->buffer);
++
++ for (i = partLen; i + 63 < inputLen; i += 64)
++ MD5Transform (context->state, &input[i]);
++
++ index = 0;
++ }
++ else
++ i = 0;
++
++ /* Buffer remaining input */
++ MD5_memcpy
++ ((POINTER)&context->buffer[index], (POINTER)&input[i],
++ inputLen-i);
++}
++
++/* MD5 finalization. Ends an MD5 message-digest operation, writing the
++ the message digest and zeroizing the context.
++ */
++void MD5Final (digest, vcontext)
++unsigned char digest[16]; /* message digest */
++void *vcontext; /* context */
++{
++ MD5_CTX *context = vcontext;
++ unsigned char bits[8];
++ unsigned int index, padLen;
++
++ /* Save number of bits */
++ Encode (bits, context->count, 8);
++
++ /* Pad out to 56 mod 64.
++*/
++ index = (unsigned int)((context->count[0] >> 3) & 0x3f);
++ padLen = (index < 56) ? (56 - index) : (120 - index);
++ MD5Update (context, PADDING, padLen);
++
++ /* Append length (before padding) */
++ MD5Update (context, bits, 8);
++
++ if (digest != NULL) /* Bill Simpson's padding */
++ {
++ /* store state in digest */
++ Encode (digest, context->state, 16);
++
++ /* Zeroize sensitive information.
++ */
++ MD5_memset ((POINTER)context, 0, sizeof (*context));
++ }
++}
++
++/* MD5 basic transformation. Transforms state based on block.
++ */
++static void MD5Transform (state, block)
++UINT4 state[4];
++unsigned char block[64];
++{
++ UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16];
++
++ Decode (x, block, 64);
++
++ /* Round 1 */
++ FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */
++ FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */
++ FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */
++ FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */
++ FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */
++ FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */
++ FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */
++ FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */
++ FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */
++ FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */
++ FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */
++ FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */
++ FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */
++ FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */
++ FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */
++ FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */
++
++ /* Round 2 */
++ GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */
++ GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */
++ GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */
++ GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */
++ GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */
++ GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */
++ GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */
++ GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */
++ GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */
++ GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */
++ GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */
++ GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */
++ GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */
++ GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */
++ GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */
++ GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */
++
++ /* Round 3 */
++ HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */
++ HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */
++ HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */
++ HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */
++ HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */
++ HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */
++ HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */
++ HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */
++ HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */
++ HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */
++ HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */
++ HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */
++ HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */
++ HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */
++ HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */
++ HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */
++
++ /* Round 4 */
++ II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */
++ II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */
++ II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */
++ II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */
++ II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */
++ II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */
++ II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */
++ II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */
++ II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */
++ II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */
++ II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */
++ II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */
++ II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */
++ II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */
++ II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */
++ II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */
++
++ state[0] += a;
++ state[1] += b;
++ state[2] += c;
++ state[3] += d;
++
++ /* Zeroize sensitive information.
++*/
++ MD5_memset ((POINTER)x, 0, sizeof (x));
++}
++
++#ifndef LITTLEENDIAN
++
++/* Encodes input (UINT4) into output (unsigned char). Assumes len is
++ a multiple of 4.
++ */
++static void Encode (output, input, len)
++unsigned char *output;
++UINT4 *input;
++unsigned int len;
++{
++ unsigned int i, j;
++
++ for (i = 0, j = 0; j < len; i++, j += 4) {
++ output[j] = (unsigned char)(input[i] & 0xff);
++ output[j+1] = (unsigned char)((input[i] >> 8) & 0xff);
++ output[j+2] = (unsigned char)((input[i] >> 16) & 0xff);
++ output[j+3] = (unsigned char)((input[i] >> 24) & 0xff);
++ }
++}
++
++/* Decodes input (unsigned char) into output (UINT4). Assumes len is
++ a multiple of 4.
++ */
++static void Decode (output, input, len)
++UINT4 *output;
++unsigned char *input;
++unsigned int len;
++{
++ unsigned int i, j;
++
++ for (i = 0, j = 0; j < len; i++, j += 4)
++ output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) |
++ (((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24);
++}
++
++#endif
++
++#ifndef HAVEMEMCOPY
++#ifndef HAVEBCOPY
++/* Note: Replace "for loop" with standard memcpy if possible.
++ */
++
++static void MD5_memcpy (output, input, len)
++POINTER output;
++POINTER input;
++unsigned int len;
++{
++ unsigned int i;
++
++ for (i = 0; i < len; i++)
++
++ output[i] = input[i];
++}
++
++/* Note: Replace "for loop" with standard memset if possible.
++ */
++
++static void MD5_memset (output, value, len)
++POINTER output;
++int value;
++unsigned int len;
++{
++ unsigned int i;
++
++ for (i = 0; i < len; i++)
++ ((char *)output)[i] = (char)value;
++}
++#endif
++#endif
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.8 2004/04/06 02:49:26 mcr
++ * pullup of algo code from alg-branch.
++ *
++ * Revision 1.7 2002/09/10 01:45:14 mcr
++ * changed type of MD5_CTX and SHA1_CTX to void * so that
++ * the function prototypes would match, and could be placed
++ * into a pointer to a function.
++ *
++ * Revision 1.6 2002/04/24 07:55:32 mcr
++ * #include patches and Makefiles for post-reorg compilation.
++ *
++ * Revision 1.5 2002/04/24 07:36:28 mcr
++ * Moved from ./klips/net/ipsec/ipsec_md5c.c,v
++ *
++ * Revision 1.4 1999/12/13 13:59:12 rgb
++ * Quick fix to argument size to Update bugs.
++ *
++ * Revision 1.3 1999/05/21 18:09:28 henry
++ * unnecessary <memory.h> include causes trouble in 2.2
++ *
++ * Revision 1.2 1999/04/06 04:54:26 rgb
++ * Fix/Add RCSID Id: and Log: bits to make PHMDs happy. This includes
++ * patch shell fixes.
++ *
++ * Revision 1.1 1998/06/18 21:27:48 henry
++ * move sources from klips/src to klips/net/ipsec, to keep stupid
++ * kernel-build scripts happier in the presence of symlinks
++ *
++ * Revision 1.2 1998/04/23 20:54:02 rgb
++ * Fixed md5 and sha1 include file nesting issues, to be cleaned up when
++ * verified.
++ *
++ * Revision 1.1 1998/04/09 03:06:08 henry
++ * sources moved up from linux/net/ipsec
++ *
++ * Revision 1.1.1.1 1998/04/08 05:35:04 henry
++ * RGB's ipsec-0.8pre2.tar.gz ipsec-0.8
++ *
++ * Revision 0.3 1996/11/20 14:48:53 ji
++ * Release update only.
++ *
++ * Revision 0.2 1996/11/02 00:18:33 ji
++ * First limited release.
++ *
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/net/ipsec/ipsec_proc.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,1127 @@
++/*
++ * @(#) /proc file system interface code.
++ *
++ * Copyright (C) 1996, 1997 John Ioannidis.
++ * Copyright (C) 1998, 1999, 2000, 2001 Richard Guy Briggs <rgb@freeswan.org>
++ * 2001 Michael Richardson <mcr@freeswan.org>
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ * Split out from ipsec_init.c version 1.70.
++ */
++
++char ipsec_proc_c_version[] = "RCSID $Id$";
++
++
++#include <linux/config.h>
++#include <linux/version.h>
++#define __NO_VERSION__
++#include <linux/module.h>
++#include <linux/kernel.h> /* printk() */
++
++#include "openswan/ipsec_param.h"
++
++#ifdef MALLOC_SLAB
++# include <linux/slab.h> /* kmalloc() */
++#else /* MALLOC_SLAB */
++# include <linux/malloc.h> /* kmalloc() */
++#endif /* MALLOC_SLAB */
++#include <linux/errno.h> /* error codes */
++#include <linux/types.h> /* size_t */
++#include <linux/interrupt.h> /* mark_bh */
++
++#include <linux/netdevice.h> /* struct device, and other headers */
++#include <linux/etherdevice.h> /* eth_type_trans */
++#include <linux/ip.h> /* struct iphdr */
++#include <linux/in.h> /* struct sockaddr_in */
++#include <linux/skbuff.h>
++#include <openswan.h>
++#ifdef SPINLOCK
++#ifdef SPINLOCK_23
++#include <linux/spinlock.h> /* *lock* */
++#else /* SPINLOCK_23 */
++#include <asm/spinlock.h> /* *lock* */
++#endif /* SPINLOCK_23 */
++#endif /* SPINLOCK */
++#ifdef NET_21
++#include <asm/uaccess.h>
++#include <linux/in6.h>
++#endif /* NET_21 */
++#include <asm/checksum.h>
++#include <net/ip.h>
++#ifdef CONFIG_PROC_FS
++#include <linux/proc_fs.h>
++#endif /* CONFIG_PROC_FS */
++#ifdef NETLINK_SOCK
++#include <linux/netlink.h>
++#else
++#include <net/netlink.h>
++#endif
++
++#include "openswan/radij.h"
++
++#include "openswan/ipsec_life.h"
++#include "openswan/ipsec_stats.h"
++#include "openswan/ipsec_sa.h"
++
++#include "openswan/ipsec_encap.h"
++#include "openswan/ipsec_radij.h"
++#include "openswan/ipsec_xform.h"
++#include "openswan/ipsec_tunnel.h"
++#include "openswan/ipsec_xmit.h"
++
++#include "openswan/ipsec_rcv.h"
++#include "openswan/ipsec_ah.h"
++#include "openswan/ipsec_esp.h"
++
++#ifdef CONFIG_IPSEC_IPCOMP
++#include "openswan/ipcomp.h"
++#endif /* CONFIG_IPSEC_IPCOMP */
++
++#include "openswan/ipsec_proto.h"
++
++#include <pfkeyv2.h>
++#include <pfkey.h>
++
++#ifdef CONFIG_PROC_FS
++
++#ifdef IPSEC_PROC_SUBDIRS
++static struct proc_dir_entry *proc_net_ipsec_dir = NULL;
++static struct proc_dir_entry *proc_eroute_dir = NULL;
++static struct proc_dir_entry *proc_spi_dir = NULL;
++static struct proc_dir_entry *proc_spigrp_dir = NULL;
++static struct proc_dir_entry *proc_birth_dir = NULL;
++static struct proc_dir_entry *proc_stats_dir = NULL;
++#endif
++
++struct ipsec_birth_reply ipsec_ipv4_birth_packet;
++struct ipsec_birth_reply ipsec_ipv6_birth_packet;
++
++#define DECREMENT_UNSIGNED(X, amount) ((amount < (X)) ? (X)-amount : 0)
++
++extern int ipsec_xform_get_info(char *buffer, char **start,
++ off_t offset, int length IPSEC_PROC_LAST_ARG);
++
++
++/* ipsec_snprintf: like snprintf except
++ * - size is signed and a negative value is treated as if it were 0
++ * - the returned result is never negative --
++ * an error generates a "?" or null output (depending on space).
++ * (Our callers are too lazy to check for an error return.)
++ *
++ * @param buf String buffer
++ * @param size Size of the string
++ * @param fmt printf string
++ * @param ... Variables to be displayed in fmt
++ * @return int Return code
++ */
++int ipsec_snprintf(char *buf, ssize_t size, const char *fmt, ...)
++{
++ va_list args;
++ int i;
++ size_t possize = size < 0? 0 : size;
++ va_start(args, fmt);
++ i = vsnprintf(buf,possize,fmt,args);
++ va_end(args);
++ if (i < 0) {
++ /* create empty output in place of error */
++ i = 0;
++ if (size > 0) {
++ *buf = '\0';
++ }
++ }
++ return i;
++}
++
++
++IPSEC_PROCFS_DEBUG_NO_STATIC
++int
++ipsec_eroute_get_info(char *buffer,
++ char **start,
++ off_t offset,
++ int length IPSEC_PROC_LAST_ARG)
++{
++ struct wsbuf w = {buffer, length, offset, 0, 0};
++
++#ifdef CONFIG_IPSEC_DEBUG
++ if (debug_radij & DB_RJ_DUMPTREES)
++ rj_dumptrees(); /* XXXXXXXXX */
++#endif /* CONFIG_IPSEC_DEBUG */
++
++ KLIPS_PRINT(debug_tunnel & DB_TN_PROCFS,
++ "klips_debug:ipsec_eroute_get_info: "
++ "buffer=0p%p, *start=0p%p, offset=%d, length=%d\n",
++ buffer,
++ *start,
++ (int)offset,
++ length);
++
++ spin_lock_bh(&eroute_lock);
++
++ rj_walktree(rnh, ipsec_rj_walker_procprint, &w);
++/* rj_walktree(mask_rjhead, ipsec_rj_walker_procprint, &w); */
++
++ spin_unlock_bh(&eroute_lock);
++
++ *start = buffer + (offset - w.begin); /* Start of wanted data */
++ return w.len - (offset - w.begin);
++}
++
++IPSEC_PROCFS_DEBUG_NO_STATIC
++int
++ipsec_spi_get_info(char *buffer,
++ char **start,
++ off_t offset,
++ int length IPSEC_PROC_LAST_ARG)
++{
++ const int max_content = length > 0? length-1 : 0;
++ int len = 0;
++ off_t begin = 0;
++ int i;
++ struct ipsec_sa *sa_p;
++ char sa[SATOT_BUF];
++ char buf_s[SUBNETTOA_BUF];
++ char buf_d[SUBNETTOA_BUF];
++ size_t sa_len;
++
++ KLIPS_PRINT(debug_tunnel & DB_TN_PROCFS,
++ "klips_debug:ipsec_spi_get_info: "
++ "buffer=0p%p, *start=0p%p, offset=%d, length=%d\n",
++ buffer,
++ *start,
++ (int)offset,
++ length);
++
++ spin_lock_bh(&tdb_lock);
++
++ for (i = 0; i < SADB_HASHMOD; i++) {
++ for (sa_p = ipsec_sadb_hash[i];
++ sa_p;
++ sa_p = sa_p->ips_hnext) {
++ atomic_inc(&sa_p->ips_refcount);
++ sa_len = satot(&sa_p->ips_said, 'x', sa, sizeof(sa));
++ len += ipsec_snprintf(buffer+len, length-len, "%s ",
++ sa_len ? sa : " (error)");
++
++ len += ipsec_snprintf(buffer+len, length-len, "%s%s%s",
++ IPS_XFORM_NAME(sa_p));
++
++ len += ipsec_snprintf(buffer+len, length-len, ": dir=%s",
++ (sa_p->ips_flags & EMT_INBOUND) ?
++ "in " : "out");
++
++ if(sa_p->ips_addr_s) {
++ addrtoa(((struct sockaddr_in*)(sa_p->ips_addr_s))->sin_addr,
++ 0, buf_s, sizeof(buf_s));
++ len += ipsec_snprintf(buffer+len, length-len, " src=%s",
++ buf_s);
++ }
++
++ if((sa_p->ips_said.proto == IPPROTO_IPIP)
++ && (sa_p->ips_flags & SADB_X_SAFLAGS_INFLOW)) {
++ subnettoa(sa_p->ips_flow_s.u.v4.sin_addr,
++ sa_p->ips_mask_s.u.v4.sin_addr,
++ 0,
++ buf_s,
++ sizeof(buf_s));
++
++ subnettoa(sa_p->ips_flow_d.u.v4.sin_addr,
++ sa_p->ips_mask_d.u.v4.sin_addr,
++ 0,
++ buf_d,
++ sizeof(buf_d));
++
++ len += ipsec_snprintf(buffer+len, length-len, " policy=%s->%s",
++ buf_s, buf_d);
++ }
++
++ if(sa_p->ips_iv_bits) {
++ int j;
++ len += ipsec_snprintf(buffer+len, length-len, " iv_bits=%dbits iv=0x",
++ sa_p->ips_iv_bits);
++
++ for(j = 0; j < sa_p->ips_iv_bits / 8; j++) {
++ len += ipsec_snprintf(buffer+len, length-len, "%02x",
++ (__u32)((__u8*)(sa_p->ips_iv))[j]);
++ }
++ }
++
++ if(sa_p->ips_encalg || sa_p->ips_authalg) {
++ if(sa_p->ips_replaywin) {
++ len += ipsec_snprintf(buffer+len, length-len, " ooowin=%d",
++ sa_p->ips_replaywin);
++ }
++ if(sa_p->ips_errs.ips_replaywin_errs) {
++ len += ipsec_snprintf(buffer+len, length-len, " ooo_errs=%d",
++ sa_p->ips_errs.ips_replaywin_errs);
++ }
++ if(sa_p->ips_replaywin_lastseq) {
++ len += ipsec_snprintf(buffer+len, length-len, " seq=%d",
++ sa_p->ips_replaywin_lastseq);
++ }
++ if(sa_p->ips_replaywin_bitmap) {
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
++ len += ipsec_snprintf(buffer+len, length-len, " bit=0x%Lx",
++ sa_p->ips_replaywin_bitmap);
++#else
++ len += ipsec_snprintf(buffer+len, length-len, " bit=0x%x%08x",
++ (__u32)(sa_p->ips_replaywin_bitmap >> 32),
++ (__u32)sa_p->ips_replaywin_bitmap);
++#endif
++ }
++ if(sa_p->ips_replaywin_maxdiff) {
++ len += ipsec_snprintf(buffer+len, length-len, " max_seq_diff=%d",
++ sa_p->ips_replaywin_maxdiff);
++ }
++ }
++ if(sa_p->ips_flags & ~EMT_INBOUND) {
++ len += ipsec_snprintf(buffer+len, length-len, " flags=0x%x",
++ sa_p->ips_flags & ~EMT_INBOUND);
++ len += ipsec_snprintf(buffer+len, length-len, "<");
++ /* flag printing goes here */
++ len += ipsec_snprintf(buffer+len, length-len, ">");
++ }
++ if(sa_p->ips_auth_bits) {
++ len += ipsec_snprintf(buffer+len, length-len, " alen=%d",
++ sa_p->ips_auth_bits);
++ }
++ if(sa_p->ips_key_bits_a) {
++ len += ipsec_snprintf(buffer+len, length-len, " aklen=%d",
++ sa_p->ips_key_bits_a);
++ }
++ if(sa_p->ips_errs.ips_auth_errs) {
++ len += ipsec_snprintf(buffer+len, length-len, " auth_errs=%d",
++ sa_p->ips_errs.ips_auth_errs);
++ }
++ if(sa_p->ips_key_bits_e) {
++ len += ipsec_snprintf(buffer+len, length-len, " eklen=%d",
++ sa_p->ips_key_bits_e);
++ }
++ if(sa_p->ips_errs.ips_encsize_errs) {
++ len += ipsec_snprintf(buffer+len, length-len, " encr_size_errs=%d",
++ sa_p->ips_errs.ips_encsize_errs);
++ }
++ if(sa_p->ips_errs.ips_encpad_errs) {
++ len += ipsec_snprintf(buffer+len, length-len, " encr_pad_errs=%d",
++ sa_p->ips_errs.ips_encpad_errs);
++ }
++
++ len += ipsec_snprintf(buffer+len, length-len, " life(c,s,h)=");
++
++ len += ipsec_lifetime_format(buffer + len,
++ length - len,
++ "alloc",
++ ipsec_life_countbased,
++ &sa_p->ips_life.ipl_allocations);
++
++ len += ipsec_lifetime_format(buffer + len,
++ length - len,
++ "bytes",
++ ipsec_life_countbased,
++ &sa_p->ips_life.ipl_bytes);
++
++ len += ipsec_lifetime_format(buffer + len,
++ length - len,
++ "addtime",
++ ipsec_life_timebased,
++ &sa_p->ips_life.ipl_addtime);
++
++ len += ipsec_lifetime_format(buffer + len,
++ length - len,
++ "usetime",
++ ipsec_life_timebased,
++ &sa_p->ips_life.ipl_usetime);
++
++ len += ipsec_lifetime_format(buffer + len,
++ length - len,
++ "packets",
++ ipsec_life_countbased,
++ &sa_p->ips_life.ipl_packets);
++
++ if(sa_p->ips_life.ipl_usetime.ipl_last) { /* XXX-MCR should be last? */
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
++ len += ipsec_snprintf(buffer+len, length-len, " idle=%Ld",
++ jiffies / HZ - sa_p->ips_life.ipl_usetime.ipl_last);
++#else
++ len += ipsec_snprintf(buffer+len, length-len, " idle=%lu",
++ jiffies / HZ - (unsigned long)sa_p->ips_life.ipl_usetime.ipl_last);
++#endif
++ }
++
++#ifdef CONFIG_IPSEC_IPCOMP
++ if(sa_p->ips_said.proto == IPPROTO_COMP &&
++ (sa_p->ips_comp_ratio_dbytes ||
++ sa_p->ips_comp_ratio_cbytes)) {
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
++ len += ipsec_snprintf(buffer+len, length-len, " ratio=%Ld:%Ld",
++ sa_p->ips_comp_ratio_dbytes,
++ sa_p->ips_comp_ratio_cbytes);
++#else
++ len += ipsec_snprintf(buffer+len, length-len, " ratio=%lu:%lu",
++ (unsigned long)sa_p->ips_comp_ratio_dbytes,
++ (unsigned long)sa_p->ips_comp_ratio_cbytes);
++#endif
++ }
++#endif /* CONFIG_IPSEC_IPCOMP */
++
++#ifdef CONFIG_IPSEC_NAT_TRAVERSAL
++ if(sa_p->ips_natt_type != 0) {
++ char *natttype_name;
++
++ switch(sa_p->ips_natt_type)
++ {
++ case ESPINUDP_WITH_NON_IKE:
++ natttype_name="nonike";
++ break;
++ case ESPINUDP_WITH_NON_ESP:
++ natttype_name="nonesp";
++ break;
++ default:
++ natttype_name = "unknown";
++ break;
++ }
++
++ len += ipsec_snprintf(buffer + len, length-len, " natencap=%s",
++ natttype_name);
++
++ len += ipsec_snprintf(buffer + len, length-len, " natsport=%d",
++ sa_p->ips_natt_sport);
++
++ len += ipsec_snprintf(buffer + len,length-len, " natdport=%d",
++ sa_p->ips_natt_dport);
++ }
++#endif /* CONFIG_IPSEC_NAT_TRAVERSAL */
++
++ len += ipsec_snprintf(buffer + len,length-len, " refcount=%d",
++ atomic_read(&sa_p->ips_refcount));
++
++ len += ipsec_snprintf(buffer+len, length-len, " ref=%d",
++ sa_p->ips_ref);
++#ifdef CONFIG_IPSEC_DEBUG
++ if(debug_xform) {
++ len += ipsec_snprintf(buffer+len, length-len, " reftable=%lu refentry=%lu",
++ (unsigned long)IPsecSAref2table(sa_p->ips_ref),
++ (unsigned long)IPsecSAref2entry(sa_p->ips_ref));
++ }
++#endif /* CONFIG_IPSEC_DEBUG */
++
++ len += ipsec_snprintf(buffer+len, length-len, "\n");
++
++ atomic_dec(&sa_p->ips_refcount);
++
++ if (len >= max_content) {
++ /* we've done all that can fit -- stop loops */
++ len = max_content; /* truncate crap */
++ goto done_spi_i;
++ } else {
++ const off_t pos = begin + len; /* file position of end of what we've generated */
++
++ if (pos <= offset) {
++ /* all is before first interesting character:
++ * discard, but note where we are.
++ */
++ len = 0;
++ begin = pos;
++ }
++ }
++ }
++ }
++
++done_spi_i:
++ spin_unlock_bh(&tdb_lock);
++
++ *start = buffer + (offset - begin); /* Start of wanted data */
++ return len - (offset - begin);
++}
++
++IPSEC_PROCFS_DEBUG_NO_STATIC
++int
++ipsec_spigrp_get_info(char *buffer,
++ char **start,
++ off_t offset,
++ int length IPSEC_PROC_LAST_ARG)
++{
++ /* Limit of useful snprintf output */
++ const int max_content = length > 0? length-1 : 0;
++
++ int len = 0;
++ off_t begin = 0;
++ int i;
++ struct ipsec_sa *sa_p, *sa_p2;
++ char sa[SATOT_BUF];
++ size_t sa_len;
++
++ KLIPS_PRINT(debug_tunnel & DB_TN_PROCFS,
++ "klips_debug:ipsec_spigrp_get_info: "
++ "buffer=0p%p, *start=0p%p, offset=%d, length=%d\n",
++ buffer,
++ *start,
++ (int)offset,
++ length);
++
++ spin_lock_bh(&tdb_lock);
++
++ for (i = 0; i < SADB_HASHMOD; i++) {
++ for (sa_p = ipsec_sadb_hash[i];
++ sa_p != NULL;
++ sa_p = sa_p->ips_hnext)
++ {
++ atomic_inc(&sa_p->ips_refcount);
++ if(sa_p->ips_inext == NULL) {
++ sa_p2 = sa_p;
++ while(sa_p2 != NULL) {
++ atomic_inc(&sa_p2->ips_refcount);
++ sa_len = satot(&sa_p2->ips_said,
++ 'x', sa, sizeof(sa));
++
++ len += ipsec_snprintf(buffer+len, length-len, "%s ",
++ sa_len ? sa : " (error)");
++ atomic_dec(&sa_p2->ips_refcount);
++ sa_p2 = sa_p2->ips_onext;
++ }
++ len += ipsec_snprintf(buffer+len, length-len, "\n");
++ }
++
++ atomic_dec(&sa_p->ips_refcount);
++
++ if (len >= max_content) {
++ /* we've done all that can fit -- stop loops */
++ len = max_content; /* truncate crap */
++ goto done_spigrp_i;
++ } else {
++ const off_t pos = begin + len;
++
++ if (pos <= offset) {
++ /* all is before first interesting character:
++ * discard, but note where we are.
++ */
++ len = 0;
++ begin = pos;
++ }
++ }
++ }
++ }
++
++done_spigrp_i:
++ spin_unlock_bh(&tdb_lock);
++
++ *start = buffer + (offset - begin); /* Start of wanted data */
++ return len - (offset - begin);
++}
++
++
++IPSEC_PROCFS_DEBUG_NO_STATIC
++int
++ipsec_tncfg_get_info(char *buffer,
++ char **start,
++ off_t offset,
++ int length IPSEC_PROC_LAST_ARG)
++{
++ /* limit of useful snprintf output */
++ const int max_content = length > 0? length-1 : 0;
++ int len = 0;
++ off_t begin = 0;
++ int i;
++ char name[9];
++ struct device *dev, *privdev;
++ struct ipsecpriv *priv;
++
++ KLIPS_PRINT(debug_tunnel & DB_TN_PROCFS,
++ "klips_debug:ipsec_tncfg_get_info: "
++ "buffer=0p%p, *start=0p%p, offset=%d, length=%d\n",
++ buffer,
++ *start,
++ (int)offset,
++ length);
++
++ for(i = 0; i < IPSEC_NUM_IF; i++) {
++ ipsec_snprintf(name, (ssize_t) sizeof(name), IPSEC_DEV_FORMAT, i);
++ dev = __ipsec_dev_get(name);
++ if(dev) {
++ priv = (struct ipsecpriv *)(dev->priv);
++ len += ipsec_snprintf(buffer+len, length-len, "%s",
++ dev->name);
++ if(priv) {
++ privdev = (struct device *)(priv->dev);
++ len += ipsec_snprintf(buffer+len, length-len, " -> %s",
++ privdev ? privdev->name : "NULL");
++ len += ipsec_snprintf(buffer+len, length-len, " mtu=%d(%d) -> %d",
++ dev->mtu,
++ priv->mtu,
++ privdev ? privdev->mtu : 0);
++ } else {
++ KLIPS_PRINT(debug_tunnel & DB_TN_PROCFS,
++ "klips_debug:ipsec_tncfg_get_info: device '%s' has no private data space!\n",
++ dev->name);
++ }
++ len += ipsec_snprintf(buffer+len, length-len, "\n");
++
++ if (len >= max_content) {
++ /* we've done all that can fit -- stop loop */
++ len = max_content; /* truncate crap */
++ break;
++ } else {
++ const off_t pos = begin + len;
++ if (pos <= offset) {
++ len = 0;
++ begin = pos;
++ }
++ }
++ }
++ }
++ *start = buffer + (offset - begin); /* Start of wanted data */
++ len -= (offset - begin); /* Start slop */
++ if (len > length)
++ len = length;
++ return len;
++}
++
++IPSEC_PROCFS_DEBUG_NO_STATIC
++int
++ipsec_version_get_info(char *buffer,
++ char **start,
++ off_t offset,
++ int length IPSEC_PROC_LAST_ARG)
++{
++ int len = 0;
++ off_t begin = 0;
++
++ KLIPS_PRINT(debug_tunnel & DB_TN_PROCFS,
++ "klips_debug:ipsec_version_get_info: "
++ "buffer=0p%p, *start=0p%p, offset=%d, length=%d\n",
++ buffer,
++ *start,
++ (int)offset,
++ length);
++
++ len += ipsec_snprintf(buffer + len,length-len, "Openswan version: %s\n",
++ ipsec_version_code());
++#if 0
++ KLIPS_PRINT(debug_tunnel & DB_TN_PROCFS,
++ "klips_debug:ipsec_version_get_info: "
++ "ipsec_init version: %s\n",
++ ipsec_init_c_version);
++ KLIPS_PRINT(debug_tunnel & DB_TN_PROCFS,
++ "klips_debug:ipsec_version_get_info: "
++ "ipsec_tunnel version: %s\n",
++ ipsec_tunnel_c_version);
++ KLIPS_PRINT(debug_tunnel & DB_TN_PROCFS,
++ "klips_debug:ipsec_version_get_info: "
++ "ipsec_netlink version: %s\n",
++ ipsec_netlink_c_version);
++ KLIPS_PRINT(debug_tunnel & DB_TN_PROCFS,
++ "klips_debug:ipsec_version_get_info: "
++ "radij_c_version: %s\n",
++ radij_c_version);
++#endif
++
++
++ *start = buffer + (offset - begin); /* Start of wanted data */
++ len -= (offset - begin); /* Start slop */
++ if (len > length)
++ len = length;
++ return len;
++}
++
++IPSEC_PROCFS_DEBUG_NO_STATIC
++int
++ipsec_birth_info(char *page,
++ char **start,
++ off_t offset,
++ int count,
++ int *eof,
++ void *data)
++{
++ struct ipsec_birth_reply *ibr = (struct ipsec_birth_reply *)data;
++ int len;
++
++ if(offset >= ibr->packet_template_len) {
++ if(eof) {
++ *eof=1;
++ }
++ return 0;
++ }
++
++ len = ibr->packet_template_len;
++ len -= offset;
++ if (len > count)
++ len = count;
++
++ memcpy(page + offset, ibr->packet_template+offset, len);
++
++ return len;
++}
++
++IPSEC_PROCFS_DEBUG_NO_STATIC
++int
++ipsec_birth_set(struct file *file, const char *buffer,
++ unsigned long count, void *data)
++{
++ struct ipsec_birth_reply *ibr = (struct ipsec_birth_reply *)data;
++ int len;
++
++ MOD_INC_USE_COUNT;
++ if(count > IPSEC_BIRTH_TEMPLATE_MAXLEN) {
++ len = IPSEC_BIRTH_TEMPLATE_MAXLEN;
++ } else {
++ len = count;
++ }
++
++ if(copy_from_user(ibr->packet_template, buffer, len)) {
++ MOD_DEC_USE_COUNT;
++ return -EFAULT;
++ }
++ ibr->packet_template_len = len;
++
++ MOD_DEC_USE_COUNT;
++
++ return len;
++}
++
++
++#ifdef CONFIG_IPSEC_DEBUG
++IPSEC_PROCFS_DEBUG_NO_STATIC
++int
++ipsec_klipsdebug_get_info(char *buffer,
++ char **start,
++ off_t offset,
++ int length IPSEC_PROC_LAST_ARG)
++{
++ int len = 0;
++ off_t begin = 0;
++
++ KLIPS_PRINT(debug_tunnel & DB_TN_PROCFS,
++ "klips_debug:ipsec_klipsdebug_get_info: "
++ "buffer=0p%p, *start=0p%p, offset=%d, length=%d\n",
++ buffer,
++ *start,
++ (int)offset,
++ length);
++
++ len += ipsec_snprintf(buffer+len, length-len, "debug_tunnel=%08x.\n", debug_tunnel);
++ len += ipsec_snprintf(buffer+len, length-len, "debug_xform=%08x.\n", debug_xform);
++ len += ipsec_snprintf(buffer+len, length-len, "debug_eroute=%08x.\n", debug_eroute);
++ len += ipsec_snprintf(buffer+len, length-len, "debug_spi=%08x.\n", debug_spi);
++ len += ipsec_snprintf(buffer+len, length-len, "debug_radij=%08x.\n", debug_radij);
++ len += ipsec_snprintf(buffer+len, length-len, "debug_esp=%08x.\n", debug_esp);
++ len += ipsec_snprintf(buffer+len, length-len, "debug_ah=%08x.\n", debug_ah);
++ len += ipsec_snprintf(buffer+len, length-len, "debug_rcv=%08x.\n", debug_rcv);
++ len += ipsec_snprintf(buffer+len, length-len, "debug_pfkey=%08x.\n", debug_pfkey);
++
++ *start = buffer + (offset - begin); /* Start of wanted data */
++ len -= (offset - begin); /* Start slop */
++ if (len > length)
++ len = length;
++ return len;
++}
++#endif /* CONFIG_IPSEC_DEBUG */
++
++IPSEC_PROCFS_DEBUG_NO_STATIC
++int
++ipsec_stats_get_int_info(char *buffer,
++ char **start,
++ off_t offset,
++ int length,
++ int *eof,
++ void *data)
++{
++
++ const int max_content = length > 0? length-1 : 0;
++ int len = 0;
++ int *thing;
++
++ thing = (int *)data;
++
++ len = ipsec_snprintf(buffer+len, length-len, "%08x\n", *thing);
++
++ if (len >= max_content)
++ len = max_content; /* truncate crap */
++
++ *start = buffer + offset; /* Start of wanted data */
++ return len > offset? len - offset : 0;
++
++}
++
++#ifndef PROC_FS_2325
++struct proc_dir_entry ipsec_eroute =
++{
++ 0,
++ 12, "ipsec_eroute",
++ S_IFREG | S_IRUGO, 1, 0, 0, 0,
++ &proc_net_inode_operations,
++ ipsec_eroute_get_info,
++ NULL, NULL, NULL, NULL, NULL
++};
++
++struct proc_dir_entry ipsec_spi =
++{
++ 0,
++ 9, "ipsec_spi",
++ S_IFREG | S_IRUGO, 1, 0, 0, 0,
++ &proc_net_inode_operations,
++ ipsec_spi_get_info,
++ NULL, NULL, NULL, NULL, NULL
++};
++
++struct proc_dir_entry ipsec_spigrp =
++{
++ 0,
++ 12, "ipsec_spigrp",
++ S_IFREG | S_IRUGO, 1, 0, 0, 0,
++ &proc_net_inode_operations,
++ ipsec_spigrp_get_info,
++ NULL, NULL, NULL, NULL, NULL
++};
++
++struct proc_dir_entry ipsec_tncfg =
++{
++ 0,
++ 11, "ipsec_tncfg",
++ S_IFREG | S_IRUGO, 1, 0, 0, 0,
++ &proc_net_inode_operations,
++ ipsec_tncfg_get_info,
++ NULL, NULL, NULL, NULL, NULL
++};
++
++struct proc_dir_entry ipsec_version =
++{
++ 0,
++ 13, "ipsec_version",
++ S_IFREG | S_IRUGO, 1, 0, 0, 0,
++ &proc_net_inode_operations,
++ ipsec_version_get_info,
++ NULL, NULL, NULL, NULL, NULL
++};
++
++#ifdef CONFIG_IPSEC_DEBUG
++struct proc_dir_entry ipsec_klipsdebug =
++{
++ 0,
++ 16, "ipsec_klipsdebug",
++ S_IFREG | S_IRUGO, 1, 0, 0, 0,
++ &proc_net_inode_operations,
++ ipsec_klipsdebug_get_info,
++ NULL, NULL, NULL, NULL, NULL
++};
++#endif /* CONFIG_IPSEC_DEBUG */
++#endif /* !PROC_FS_2325 */
++#endif /* CONFIG_PROC_FS */
++
++#if defined(PROC_FS_2325)
++struct ipsec_proc_list {
++ char *name;
++ struct proc_dir_entry **parent;
++ struct proc_dir_entry **dir;
++ read_proc_t *readthing;
++ write_proc_t *writething;
++ void *data;
++};
++static struct ipsec_proc_list proc_items[]={
++#ifdef CONFIG_IPSEC_DEBUG
++ {"klipsdebug", &proc_net_ipsec_dir, NULL, ipsec_klipsdebug_get_info, NULL, NULL},
++#endif
++ {"eroute", &proc_net_ipsec_dir, &proc_eroute_dir, NULL, NULL, NULL},
++ {"all", &proc_eroute_dir, NULL, ipsec_eroute_get_info, NULL, NULL},
++ {"spi", &proc_net_ipsec_dir, &proc_spi_dir, NULL, NULL, NULL},
++ {"all", &proc_spi_dir, NULL, ipsec_spi_get_info, NULL, NULL},
++ {"spigrp", &proc_net_ipsec_dir, &proc_spigrp_dir, NULL, NULL, NULL},
++ {"all", &proc_spigrp_dir, NULL, ipsec_spigrp_get_info, NULL, NULL},
++ {"birth", &proc_net_ipsec_dir, &proc_birth_dir, NULL, NULL, NULL},
++ {"ipv4", &proc_birth_dir, NULL, ipsec_birth_info, ipsec_birth_set, (void *)&ipsec_ipv4_birth_packet},
++ {"ipv6", &proc_birth_dir, NULL, ipsec_birth_info, ipsec_birth_set, (void *)&ipsec_ipv6_birth_packet},
++ {"tncfg", &proc_net_ipsec_dir, NULL, ipsec_tncfg_get_info, NULL, NULL},
++ {"xforms", &proc_net_ipsec_dir, NULL, ipsec_xform_get_info, NULL, NULL},
++ {"stats", &proc_net_ipsec_dir, &proc_stats_dir, NULL, NULL, NULL},
++ {"trap_count", &proc_stats_dir, NULL, ipsec_stats_get_int_info, NULL, &ipsec_xmit_trap_count},
++ {"trap_sendcount", &proc_stats_dir, NULL, ipsec_stats_get_int_info, NULL, &ipsec_xmit_trap_sendcount},
++ {"version", &proc_net_ipsec_dir, NULL, ipsec_version_get_info, NULL, NULL},
++ {NULL, NULL, NULL, NULL, NULL, NULL}
++};
++#endif
++
++int
++ipsec_proc_init()
++{
++ int error = 0;
++#ifdef IPSEC_PROC_SUBDIRS
++ struct proc_dir_entry *item;
++#endif
++
++ /*
++ * just complain because pluto won't run without /proc!
++ */
++#ifndef CONFIG_PROC_FS
++#error You must have PROC_FS built in to use KLIPS
++#endif
++
++ /* for 2.0 kernels */
++#if !defined(PROC_FS_2325) && !defined(PROC_FS_21)
++ error |= proc_register_dynamic(&proc_net, &ipsec_eroute);
++ error |= proc_register_dynamic(&proc_net, &ipsec_spi);
++ error |= proc_register_dynamic(&proc_net, &ipsec_spigrp);
++ error |= proc_register_dynamic(&proc_net, &ipsec_tncfg);
++ error |= proc_register_dynamic(&proc_net, &ipsec_version);
++#ifdef CONFIG_IPSEC_DEBUG
++ error |= proc_register_dynamic(&proc_net, &ipsec_klipsdebug);
++#endif /* CONFIG_IPSEC_DEBUG */
++#endif
++
++ /* for 2.2 kernels */
++#if !defined(PROC_FS_2325) && defined(PROC_FS_21)
++ error |= proc_register(proc_net, &ipsec_eroute);
++ error |= proc_register(proc_net, &ipsec_spi);
++ error |= proc_register(proc_net, &ipsec_spigrp);
++ error |= proc_register(proc_net, &ipsec_tncfg);
++ error |= proc_register(proc_net, &ipsec_version);
++#ifdef CONFIG_IPSEC_DEBUG
++ error |= proc_register(proc_net, &ipsec_klipsdebug);
++#endif /* CONFIG_IPSEC_DEBUG */
++#endif
++
++ /* for 2.4 kernels */
++#if defined(PROC_FS_2325)
++ /* create /proc/net/ipsec */
++
++ /* zero these out before we initialize /proc/net/ipsec/birth/stuff */
++ memset(&ipsec_ipv4_birth_packet, 0, sizeof(struct ipsec_birth_reply));
++ memset(&ipsec_ipv6_birth_packet, 0, sizeof(struct ipsec_birth_reply));
++
++ proc_net_ipsec_dir = proc_mkdir("ipsec", proc_net);
++ if(proc_net_ipsec_dir == NULL) {
++ /* no point in continuing */
++ return 1;
++ }
++
++ {
++ struct ipsec_proc_list *it;
++
++ it=proc_items;
++ while(it->name!=NULL) {
++ if(it->dir) {
++ /* make a dir instead */
++ item = proc_mkdir(it->name, *it->parent);
++ *it->dir = item;
++ } else {
++ item = create_proc_entry(it->name, 0400, *it->parent);
++ }
++ if(item) {
++ item->read_proc = it->readthing;
++ item->write_proc = it->writething;
++ item->data = it->data;
++#ifdef MODULE
++ item->owner = THIS_MODULE;
++#endif
++ } else {
++ error |= 1;
++ }
++ it++;
++ }
++ }
++
++ /* now create some symlinks to provide compatibility */
++ proc_symlink("ipsec_eroute", proc_net, "ipsec/eroute/all");
++ proc_symlink("ipsec_spi", proc_net, "ipsec/spi/all");
++ proc_symlink("ipsec_spigrp", proc_net, "ipsec/spigrp/all");
++ proc_symlink("ipsec_tncfg", proc_net, "ipsec/tncfg");
++ proc_symlink("ipsec_version",proc_net, "ipsec/version");
++ proc_symlink("ipsec_klipsdebug",proc_net,"ipsec/klipsdebug");
++
++#endif /* !PROC_FS_2325 */
++
++ return error;
++}
++
++void
++ipsec_proc_cleanup()
++{
++
++ /* for 2.0 and 2.2 kernels */
++#if !defined(PROC_FS_2325)
++
++#ifdef CONFIG_IPSEC_DEBUG
++ if (proc_net_unregister(ipsec_klipsdebug.low_ino) != 0)
++ printk("klips_debug:ipsec_cleanup: "
++ "cannot unregister /proc/net/ipsec_klipsdebug\n");
++#endif /* CONFIG_IPSEC_DEBUG */
++
++ if (proc_net_unregister(ipsec_version.low_ino) != 0)
++ printk("klips_debug:ipsec_cleanup: "
++ "cannot unregister /proc/net/ipsec_version\n");
++ if (proc_net_unregister(ipsec_eroute.low_ino) != 0)
++ printk("klips_debug:ipsec_cleanup: "
++ "cannot unregister /proc/net/ipsec_eroute\n");
++ if (proc_net_unregister(ipsec_spi.low_ino) != 0)
++ printk("klips_debug:ipsec_cleanup: "
++ "cannot unregister /proc/net/ipsec_spi\n");
++ if (proc_net_unregister(ipsec_spigrp.low_ino) != 0)
++ printk("klips_debug:ipsec_cleanup: "
++ "cannot unregister /proc/net/ipsec_spigrp\n");
++ if (proc_net_unregister(ipsec_tncfg.low_ino) != 0)
++ printk("klips_debug:ipsec_cleanup: "
++ "cannot unregister /proc/net/ipsec_tncfg\n");
++#endif
++
++ /* for 2.4 kernels */
++#if defined(PROC_FS_2325)
++ {
++ struct ipsec_proc_list *it;
++
++ /* find end of list */
++ it=proc_items;
++ while(it->name!=NULL) {
++ it++;
++ }
++ it--;
++
++ do {
++ remove_proc_entry(it->name, *it->parent);
++ it--;
++ } while(it > proc_items);
++ }
++
++
++#ifdef CONFIG_IPSEC_DEBUG
++ remove_proc_entry("ipsec_klipsdebug", proc_net);
++#endif /* CONFIG_IPSEC_DEBUG */
++ remove_proc_entry("ipsec_eroute", proc_net);
++ remove_proc_entry("ipsec_spi", proc_net);
++ remove_proc_entry("ipsec_spigrp", proc_net);
++ remove_proc_entry("ipsec_tncfg", proc_net);
++ remove_proc_entry("ipsec_version", proc_net);
++ remove_proc_entry("ipsec", proc_net);
++#endif /* 2.4 kernel */
++}
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.30 2004/04/25 21:23:11 ken
++ * Pull in dhr's changes from FreeS/WAN 2.06
++ *
++ * Revision 1.29 2004/04/06 02:49:26 mcr
++ * pullup of algo code from alg-branch.
++ *
++ * Revision 1.28 2004/03/28 20:29:58 paul
++ * <hugh_> ssize_t, not ssized_t
++ *
++ * Revision 1.27 2004/03/28 20:27:20 paul
++ * Included tested and confirmed fixes mcr made and dhr verified for
++ * snprint statements. Changed one other snprintf to use ipsec_snprintf
++ * so it wouldnt break compatibility with 2.0/2.2 kernels. Verified with
++ * dhr. (thanks dhr!)
++ *
++ * Revision 1.26 2004/02/09 22:07:06 mcr
++ * added information about nat-traversal setting to spi-output.
++ *
++ * Revision 1.25.4.1 2004/04/05 04:30:46 mcr
++ * patches for alg-branch to compile/work with 2.x openswan
++ *
++ * Revision 1.25 2003/10/31 02:27:55 mcr
++ * pulled up port-selector patches and sa_id elimination.
++ *
++ * Revision 1.24.4.1 2003/10/29 01:30:41 mcr
++ * elimited "struct sa_id".
++ *
++ * Revision 1.24 2003/06/20 01:42:21 mcr
++ * added counters to measure how many ACQUIREs we send to pluto,
++ * and how many are successfully sent.
++ *
++ * Revision 1.23 2003/04/03 17:38:09 rgb
++ * Centralised ipsec_kfree_skb and ipsec_dev_{get,put}.
++ *
++ * Revision 1.22 2002/09/20 15:40:57 rgb
++ * Renamed saref macros for consistency and brevity.
++ *
++ * Revision 1.21 2002/09/20 05:01:35 rgb
++ * Print ref and reftable, refentry seperately.
++ *
++ * Revision 1.20 2002/09/19 02:35:39 mcr
++ * do not define structures needed by /proc/net/ipsec/ if we
++ * aren't going create that directory.
++ *
++ * Revision 1.19 2002/09/10 01:43:25 mcr
++ * fixed problem in /-* comment.
++ *
++ * Revision 1.18 2002/09/03 16:22:11 mcr
++ * fixed initialization of birth/stuff values - some simple
++ * screw ups in the code.
++ * removed debugging that was left in by mistake.
++ *
++ * Revision 1.17 2002/09/02 17:54:53 mcr
++ * changed how the table driven /proc entries are created so that
++ * making subdirs is now explicit rather than implicit.
++ *
++ * Revision 1.16 2002/08/30 01:23:37 mcr
++ * reorganized /proc creating code to clear up ifdefs,
++ * make the 2.4 code table driven, and put things into
++ * /proc/net/ipsec subdir. Symlinks are left for compatibility.
++ *
++ * Revision 1.15 2002/08/13 19:01:25 mcr
++ * patches from kenb to permit compilation of FreeSWAN on ia64.
++ * des library patched to use proper DES_LONG type for ia64.
++ *
++ * Revision 1.14 2002/07/26 08:48:31 rgb
++ * Added SA ref table code.
++ *
++ * Revision 1.13 2002/07/24 18:44:54 rgb
++ * Type fiddling to tame ia64 compiler.
++ *
++ * Revision 1.12 2002/05/27 18:56:07 rgb
++ * Convert to dynamic ipsec device allocation.
++ *
++ * Revision 1.11 2002/05/23 07:14:50 rgb
++ * Added refcount code.
++ * Cleaned up %p variants to 0p%p for test suite cleanup.
++ * Convert "usecount" to "refcount" to remove ambiguity.
++ *
++ * Revision 1.10 2002/04/24 07:55:32 mcr
++ * #include patches and Makefiles for post-reorg compilation.
++ *
++ * Revision 1.9 2002/04/24 07:36:28 mcr
++ * Moved from ./klips/net/ipsec/ipsec_proc.c,v
++ *
++ * Revision 1.8 2002/01/29 17:17:55 mcr
++ * moved include of ipsec_param.h to after include of linux/kernel.h
++ * otherwise, it seems that some option that is set in ipsec_param.h
++ * screws up something subtle in the include path to kernel.h, and
++ * it complains on the snprintf() prototype.
++ *
++ * Revision 1.7 2002/01/29 04:00:52 mcr
++ * more excise of kversions.h header.
++ *
++ * Revision 1.6 2002/01/29 02:13:17 mcr
++ * introduction of ipsec_kversion.h means that include of
++ * ipsec_param.h must preceed any decisions about what files to
++ * include to deal with differences in kernel source.
++ *
++ * Revision 1.5 2002/01/12 02:54:30 mcr
++ * beginnings of /proc/net/ipsec dir.
++ *
++ * Revision 1.4 2001/12/11 02:21:05 rgb
++ * Don't include module version here, fixing 2.2 compile bug.
++ *
++ * Revision 1.3 2001/12/05 07:19:44 rgb
++ * Fixed extraneous #include "version.c" bug causing modular KLIPS failure.
++ *
++ * Revision 1.2 2001/11/26 09:16:14 rgb
++ * Merge MCR's ipsec_sa, eroute, proc and struct lifetime changes.
++ *
++ * Revision 1.74 2001/11/22 05:44:11 henry
++ * new version stuff
++ *
++ * Revision 1.1.2.1 2001/09/25 02:19:40 mcr
++ * /proc manipulation code moved to new ipsec_proc.c
++ *
++ *
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/net/ipsec/ipsec_radij.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,869 @@
++/*
++ * Interface between the IPSEC code and the radix (radij) tree code
++ * Copyright (C) 1996, 1997 John Ioannidis.
++ * Copyright (C) 1998, 1999, 2000, 2001 Richard Guy Briggs.
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ * RCSID $Id$
++ */
++
++#include <linux/config.h>
++#include <linux/version.h>
++#include <linux/kernel.h> /* printk() */
++
++#include "openswan/ipsec_param.h"
++
++#ifdef MALLOC_SLAB
++# include <linux/slab.h> /* kmalloc() */
++#else /* MALLOC_SLAB */
++# include <linux/malloc.h> /* kmalloc() */
++#endif /* MALLOC_SLAB */
++#include <linux/errno.h> /* error codes */
++#include <linux/types.h> /* size_t */
++#include <linux/interrupt.h> /* mark_bh */
++
++#include <linux/netdevice.h> /* struct device, struct net_device_stats and other headers */
++#include <linux/etherdevice.h> /* eth_type_trans */
++#include <linux/ip.h> /* struct iphdr */
++#include <linux/skbuff.h>
++#include <openswan.h>
++#ifdef SPINLOCK
++# ifdef SPINLOCK_23
++# include <linux/spinlock.h> /* *lock* */
++# else /* 23_SPINLOCK */
++# include <asm/spinlock.h> /* *lock* */
++# endif /* 23_SPINLOCK */
++#endif /* SPINLOCK */
++#ifdef NET_21
++# include <asm/uaccess.h>
++# include <linux/in6.h>
++#endif
++#include <asm/checksum.h>
++#include <net/ip.h>
++
++#include "openswan/ipsec_eroute.h"
++#include "openswan/ipsec_sa.h"
++
++#include "openswan/radij.h"
++#include "openswan/ipsec_encap.h"
++#include "openswan/radij.h"
++#include "openswan/ipsec_encap.h"
++#include "openswan/ipsec_radij.h"
++#include "openswan/ipsec_tunnel.h" /* struct ipsecpriv */
++#include "openswan/ipsec_xform.h"
++
++#include <pfkeyv2.h>
++#include <pfkey.h>
++
++#include "openswan/ipsec_proto.h"
++
++#ifdef CONFIG_IPSEC_DEBUG
++int debug_radij = 0;
++#endif /* CONFIG_IPSEC_DEBUG */
++
++struct radij_node_head *rnh = NULL;
++#ifdef SPINLOCK
++spinlock_t eroute_lock = SPIN_LOCK_UNLOCKED;
++#else /* SPINLOCK */
++spinlock_t eroute_lock;
++#endif /* SPINLOCK */
++
++int
++ipsec_radijinit(void)
++{
++ maj_keylen = sizeof (struct sockaddr_encap);
++
++ rj_init();
++
++ if (rj_inithead((void **)&rnh, /*16*/offsetof(struct sockaddr_encap, sen_type) * sizeof(__u8)) == 0) /* 16 is bit offset of sen_type */
++ return -1;
++ return 0;
++}
++
++int
++ipsec_radijcleanup(void)
++{
++ int error;
++
++ spin_lock_bh(&eroute_lock);
++
++ error = radijcleanup();
++
++ spin_unlock_bh(&eroute_lock);
++
++ return error;
++}
++
++int
++ipsec_cleareroutes(void)
++{
++ int error;
++
++ spin_lock_bh(&eroute_lock);
++
++ error = radijcleartree();
++
++ spin_unlock_bh(&eroute_lock);
++
++ return error;
++}
++
++int
++ipsec_breakroute(struct sockaddr_encap *eaddr,
++ struct sockaddr_encap *emask,
++ struct sk_buff **first,
++ struct sk_buff **last)
++{
++ struct eroute *ro;
++ struct radij_node *rn;
++ int error;
++#ifdef CONFIG_IPSEC_DEBUG
++
++ if (debug_eroute) {
++ char buf1[SUBNETTOA_BUF], buf2[SUBNETTOA_BUF];
++ subnettoa(eaddr->sen_ip_src, emask->sen_ip_src, 0, buf1, sizeof(buf1));
++ subnettoa(eaddr->sen_ip_dst, emask->sen_ip_dst, 0, buf2, sizeof(buf2));
++ KLIPS_PRINT(debug_eroute,
++ "klips_debug:ipsec_breakroute: "
++ "attempting to delete eroute for %s:%d->%s:%d %d\n",
++ buf1, ntohs(eaddr->sen_sport),
++ buf2, ntohs(eaddr->sen_dport), eaddr->sen_proto);
++ }
++#endif /* CONFIG_IPSEC_DEBUG */
++
++ spin_lock_bh(&eroute_lock);
++
++ if ((error = rj_delete(eaddr, emask, rnh, &rn)) != 0) {
++ spin_unlock_bh(&eroute_lock);
++ KLIPS_PRINT(debug_eroute,
++ "klips_debug:ipsec_breakroute: "
++ "node not found, eroute delete failed.\n");
++ return error;
++ }
++
++ spin_unlock_bh(&eroute_lock);
++
++ ro = (struct eroute *)rn;
++
++ KLIPS_PRINT(debug_eroute,
++ "klips_debug:ipsec_breakroute: "
++ "deleted eroute=0p%p, ident=0p%p->0p%p, first=0p%p, last=0p%p\n",
++ ro,
++ ro->er_ident_s.data,
++ ro->er_ident_d.data,
++ ro->er_first,
++ ro->er_last);
++
++ if (ro->er_ident_s.data != NULL) {
++ kfree(ro->er_ident_s.data);
++ }
++ if (ro->er_ident_d.data != NULL) {
++ kfree(ro->er_ident_d.data);
++ }
++ if (ro->er_first != NULL) {
++#if 0
++ struct net_device_stats *stats = (struct net_device_stats *) &(((struct ipsecpriv *)(ro->er_first->dev->priv))->mystats);
++ stats->tx_dropped--;
++#endif
++ *first = ro->er_first;
++ }
++ if (ro->er_last != NULL) {
++#if 0
++ struct net_device_stats *stats = (struct net_device_stats *) &(((struct ipsecpriv *)(ro->er_last->dev->priv))->mystats);
++ stats->tx_dropped--;
++#endif
++ *last = ro->er_last;
++ }
++
++ if (rn->rj_flags & (RJF_ACTIVE | RJF_ROOT))
++ panic ("ipsec_breakroute RMT_DELEROUTE root or active node\n");
++ memset((caddr_t)rn, 0, sizeof (struct eroute));
++ kfree(rn);
++
++ return 0;
++}
++
++int
++ipsec_makeroute(struct sockaddr_encap *eaddr,
++ struct sockaddr_encap *emask,
++ ip_said said,
++ uint32_t pid,
++ struct sk_buff *skb,
++ struct ident *ident_s,
++ struct ident *ident_d)
++{
++ struct eroute *retrt;
++ int error;
++ char sa[SATOT_BUF];
++ size_t sa_len;
++
++#ifdef CONFIG_IPSEC_DEBUG
++
++ if (debug_eroute) {
++
++ {
++ char buf1[SUBNETTOA_BUF], buf2[SUBNETTOA_BUF];
++
++ subnettoa(eaddr->sen_ip_src, emask->sen_ip_src, 0, buf1, sizeof(buf1));
++ subnettoa(eaddr->sen_ip_dst, emask->sen_ip_dst, 0, buf2, sizeof(buf2));
++ sa_len = satot(&said, 0, sa, sizeof(sa));
++ KLIPS_PRINT(debug_eroute,
++ "klips_debug:ipsec_makeroute: "
++ "attempting to allocate %lu bytes to insert eroute for %s->%s, SA: %s, PID:%d, skb=0p%p, ident:%s->%s\n",
++ (unsigned long) sizeof(struct eroute),
++ buf1,
++ buf2,
++ sa_len ? sa : " (error)",
++ pid,
++ skb,
++ (ident_s ? (ident_s->data ? ident_s->data : "NULL") : "NULL"),
++ (ident_d ? (ident_d->data ? ident_d->data : "NULL") : "NULL"));
++ }
++ {
++ char buf1[sizeof(struct sockaddr_encap)*2 + 1],
++ buf2[sizeof(struct sockaddr_encap)*2 + 1];
++ int i;
++ unsigned char *b1 = buf1,
++ *b2 = buf2,
++ *ea = (unsigned char *)eaddr,
++ *em = (unsigned char *)emask;
++
++
++ for (i=0; i<sizeof(struct sockaddr_encap); i++) {
++ sprintf(b1, "%02x", ea[i]);
++ sprintf(b2, "%02x", em[i]);
++ b1+=2;
++ b2+=2;
++ }
++ KLIPS_PRINT(debug_eroute, "klips_debug:ipsec_makeroute: %s / %s \n", buf1, buf2);
++ }
++
++ }
++#endif /* CONFIG_IPSEC_DEBUG */
++
++ retrt = (struct eroute *)kmalloc(sizeof (struct eroute), GFP_ATOMIC);
++ if (retrt == NULL) {
++ printk("klips_error:ipsec_makeroute: "
++ "not able to allocate kernel memory");
++ return -ENOMEM;
++ }
++ memset((caddr_t)retrt, 0, sizeof (struct eroute));
++
++ retrt->er_eaddr = *eaddr;
++ retrt->er_emask = *emask;
++ retrt->er_said = said;
++ retrt->er_pid = pid;
++ retrt->er_count = 0;
++ retrt->er_lasttime = jiffies/HZ;
++ rd_key((&(retrt->er_rjt))) = &(retrt->er_eaddr);
++
++ if (ident_s && ident_s->type != SADB_IDENTTYPE_RESERVED) {
++ int data_len = ident_s->len * IPSEC_PFKEYv2_ALIGN - sizeof(struct sadb_ident);
++
++ retrt->er_ident_s.type = ident_s->type;
++ retrt->er_ident_s.id = ident_s->id;
++ retrt->er_ident_s.len = ident_s->len;
++ if(data_len) {
++ KLIPS_PRINT(debug_eroute,
++ "klips_debug:ipsec_makeroute: "
++ "attempting to allocate %u bytes for ident_s.\n",
++ data_len);
++ if(!(retrt->er_ident_s.data = kmalloc(data_len, GFP_KERNEL))) {
++ kfree(retrt);
++ printk("klips_error:ipsec_makeroute: not able to allocate kernel memory (%d)\n", data_len);
++ return ENOMEM;
++ }
++ memcpy(retrt->er_ident_s.data, ident_s->data, data_len);
++ } else {
++ retrt->er_ident_s.data = NULL;
++ }
++ }
++
++ if (ident_d && ident_d->type != SADB_IDENTTYPE_RESERVED) {
++ int data_len = ident_d->len * IPSEC_PFKEYv2_ALIGN - sizeof(struct sadb_ident);
++
++ retrt->er_ident_d.type = ident_d->type;
++ retrt->er_ident_d.id = ident_d->id;
++ retrt->er_ident_d.len = ident_d->len;
++ if(data_len) {
++ KLIPS_PRINT(debug_eroute,
++ "klips_debug:ipsec_makeroute: "
++ "attempting to allocate %u bytes for ident_d.\n",
++ data_len);
++ if(!(retrt->er_ident_d.data = kmalloc(data_len, GFP_KERNEL))) {
++ if (retrt->er_ident_s.data)
++ kfree(retrt->er_ident_s.data);
++ kfree(retrt);
++ printk("klips_error:ipsec_makeroute: not able to allocate kernel memory (%d)\n", data_len);
++ return ENOMEM;
++ }
++ memcpy(retrt->er_ident_d.data, ident_d->data, data_len);
++ } else {
++ retrt->er_ident_d.data = NULL;
++ }
++ }
++ retrt->er_first = skb;
++ retrt->er_last = NULL;
++
++ KLIPS_PRINT(debug_eroute,
++ "klips_debug:ipsec_makeroute: "
++ "calling rj_addroute now\n");
++
++ spin_lock_bh(&eroute_lock);
++
++ error = rj_addroute(&(retrt->er_eaddr), &(retrt->er_emask),
++ rnh, retrt->er_rjt.rd_nodes);
++
++ spin_unlock_bh(&eroute_lock);
++
++ if(error) {
++ sa_len = satot(&said, 0, sa, sizeof(sa));
++ KLIPS_PRINT(debug_eroute,
++ "klips_debug:ipsec_makeroute: "
++ "rj_addroute not able to insert eroute for SA:%s (error:%d)\n",
++ sa_len ? sa : " (error)", error);
++ if (retrt->er_ident_s.data)
++ kfree(retrt->er_ident_s.data);
++ if (retrt->er_ident_d.data)
++ kfree(retrt->er_ident_d.data);
++
++ kfree(retrt);
++
++ return error;
++ }
++
++#ifdef CONFIG_IPSEC_DEBUG
++ if (debug_eroute) {
++ char buf1[SUBNETTOA_BUF], buf2[SUBNETTOA_BUF];
++/*
++ subnettoa(eaddr->sen_ip_src, emask->sen_ip_src, 0, buf1, sizeof(buf1));
++ subnettoa(eaddr->sen_ip_dst, emask->sen_ip_dst, 0, buf2, sizeof(buf2));
++*/
++ subnettoa(rd_key((&(retrt->er_rjt)))->sen_ip_src, rd_mask((&(retrt->er_rjt)))->sen_ip_src, 0, buf1, sizeof(buf1));
++ subnettoa(rd_key((&(retrt->er_rjt)))->sen_ip_dst, rd_mask((&(retrt->er_rjt)))->sen_ip_dst, 0, buf2, sizeof(buf2));
++ sa_len = satot(&retrt->er_said, 0, sa, sizeof(sa));
++
++ KLIPS_PRINT(debug_eroute,
++ "klips_debug:ipsec_makeroute: "
++ "pid=%05d "
++ "count=%10d "
++ "lasttime=%6d "
++ "%-18s -> %-18s => %s\n",
++ retrt->er_pid,
++ retrt->er_count,
++ (int)(jiffies/HZ - retrt->er_lasttime),
++ buf1,
++ buf2,
++ sa_len ? sa : " (error)");
++ }
++#endif /* CONFIG_IPSEC_DEBUG */
++ KLIPS_PRINT(debug_eroute,
++ "klips_debug:ipsec_makeroute: "
++ "succeeded.\n");
++ return 0;
++}
++
++struct eroute *
++ipsec_findroute(struct sockaddr_encap *eaddr)
++{
++ struct radij_node *rn;
++#ifdef CONFIG_IPSEC_DEBUG
++ char buf1[ADDRTOA_BUF], buf2[ADDRTOA_BUF];
++
++ if (debug_radij & DB_RJ_FINDROUTE) {
++ addrtoa(eaddr->sen_ip_src, 0, buf1, sizeof(buf1));
++ addrtoa(eaddr->sen_ip_dst, 0, buf2, sizeof(buf2));
++ KLIPS_PRINT(debug_eroute,
++ "klips_debug:ipsec_findroute: "
++ "%s:%d->%s:%d %d\n",
++ buf1, ntohs(eaddr->sen_sport),
++ buf2, ntohs(eaddr->sen_dport),
++ eaddr->sen_proto);
++ }
++#endif /* CONFIG_IPSEC_DEBUG */
++ rn = rj_match((caddr_t)eaddr, rnh);
++ if(rn) {
++ KLIPS_PRINT(debug_eroute && sysctl_ipsec_debug_verbose,
++ "klips_debug:ipsec_findroute: "
++ "found, points to proto=%d, spi=%x, dst=%x.\n",
++ ((struct eroute*)rn)->er_said.proto,
++ ntohl(((struct eroute*)rn)->er_said.spi),
++ ntohl(((struct eroute*)rn)->er_said.dst.u.v4.sin_addr.s_addr));
++ }
++ return (struct eroute *)rn;
++}
++
++#ifdef CONFIG_PROC_FS
++/** ipsec_rj_walker_procprint: print one line of eroute table output.
++ *
++ * Theoretical BUG: if w->length is less than the length
++ * of some line we should produce, that line will never
++ * be finished. In effect, the "file" will stop part way
++ * through that line.
++ */
++int
++ipsec_rj_walker_procprint(struct radij_node *rn, void *w0)
++{
++ struct eroute *ro = (struct eroute *)rn;
++ struct rjtentry *rd = (struct rjtentry *)rn;
++ struct wsbuf *w = (struct wsbuf *)w0;
++ char buf1[SUBNETTOA_BUF], buf2[SUBNETTOA_BUF];
++ char buf3[16];
++ char sa[SATOT_BUF];
++ size_t sa_len, buf_len;
++ struct sockaddr_encap *key, *mask;
++
++ KLIPS_PRINT(debug_radij,
++ "klips_debug:ipsec_rj_walker_procprint: "
++ "rn=0p%p, w0=0p%p\n",
++ rn,
++ w0);
++ if (rn->rj_b >= 0) {
++ return 0;
++ }
++
++ key = rd_key(rd);
++ mask = rd_mask(rd);
++
++ if (key == NULL || mask == NULL) {
++ return 0;
++ }
++
++ buf_len = subnettoa(key->sen_ip_src, mask->sen_ip_src, 0, buf1, sizeof(buf1));
++ if(key->sen_sport != 0) {
++ sprintf(buf1+buf_len-1, ":%d", ntohs(key->sen_sport));
++ }
++
++ buf_len = subnettoa(key->sen_ip_dst, mask->sen_ip_dst, 0, buf2, sizeof(buf2));
++ if(key->sen_dport != 0) {
++ sprintf(buf2+buf_len-1, ":%d", ntohs(key->sen_dport));
++ }
++
++ buf3[0]='\0';
++ if(key->sen_proto != 0) {
++ sprintf(buf3, ":%d", key->sen_proto);
++ }
++
++ sa_len = satot(&ro->er_said, 'x', sa, sizeof(sa));
++ w->len += ipsec_snprintf(w->buffer + w->len,
++ w->length - w->len,
++ "%-10d "
++ "%-18s -> %-18s => %s%s\n",
++ ro->er_count,
++ buf1,
++ buf2,
++ sa_len ? sa : " (error)",
++ buf3);
++
++ {
++ /* snprintf can only fill the last character with NUL
++ * so the maximum useful character is w->length-1.
++ * However, if w->length == 0, we cannot go back.
++ * (w->length surely cannot be negative.)
++ */
++ int max_content = w->length > 0? w->length-1 : 0;
++
++ if (w->len >= max_content) {
++ /* we've done all that can fit -- stop treewalking */
++ w->len = max_content; /* truncate crap */
++ return -ENOBUFS;
++ } else {
++ const off_t pos = w->begin + w->len; /* file position of end of what we've generated */
++
++ if (pos <= w->offset) {
++ /* all is before first interesting character:
++ * discard, but note where we are.
++ */
++ w->len = 0;
++ w->begin = pos;
++ }
++ return 0;
++ }
++ }
++}
++#endif /* CONFIG_PROC_FS */
++
++int
++ipsec_rj_walker_delete(struct radij_node *rn, void *w0)
++{
++ struct eroute *ro;
++ struct rjtentry *rd = (struct rjtentry *)rn;
++ struct radij_node *rn2;
++ int error;
++ struct sockaddr_encap *key, *mask;
++
++ key = rd_key(rd);
++ mask = rd_mask(rd);
++
++ if(!key || !mask) {
++ return -ENODATA;
++ }
++#ifdef CONFIG_IPSEC_DEBUG
++ if(debug_radij) {
++ char buf1[SUBNETTOA_BUF], buf2[SUBNETTOA_BUF];
++ subnettoa(key->sen_ip_src, mask->sen_ip_src, 0, buf1, sizeof(buf1));
++ subnettoa(key->sen_ip_dst, mask->sen_ip_dst, 0, buf2, sizeof(buf2));
++ KLIPS_PRINT(debug_radij,
++ "klips_debug:ipsec_rj_walker_delete: "
++ "deleting: %s -> %s\n",
++ buf1,
++ buf2);
++ }
++#endif /* CONFIG_IPSEC_DEBUG */
++
++ if((error = rj_delete(key, mask, rnh, &rn2))) {
++ KLIPS_PRINT(debug_radij,
++ "klips_debug:ipsec_rj_walker_delete: "
++ "rj_delete failed with error=%d.\n", error);
++ return error;
++ }
++
++ if(rn2 != rn) {
++ printk("klips_debug:ipsec_rj_walker_delete: "
++ "tried to delete a different node?!? This should never happen!\n");
++ }
++
++ ro = (struct eroute *)rn;
++
++ if (ro->er_ident_s.data)
++ kfree(ro->er_ident_s.data);
++ if (ro->er_ident_d.data)
++ kfree(ro->er_ident_d.data);
++
++ memset((caddr_t)rn, 0, sizeof (struct eroute));
++ kfree(rn);
++
++ return 0;
++}
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.70 2004/04/25 21:10:52 ken
++ * Pull in dhr's changes from FreeS/WAN 2.06
++ *
++ * Revision 1.69 2004/04/06 02:49:26 mcr
++ * pullup of algo code from alg-branch.
++ *
++ * Revision 1.68 2004/03/28 20:27:20 paul
++ * Included tested and confirmed fixes mcr made and dhr verified for
++ * snprint statements. Changed one other snprintf to use ipsec_snprintf
++ * so it wouldnt break compatibility with 2.0/2.2 kernels. Verified with
++ * dhr. (thanks dhr!)
++ *
++ * Revision 1.67.4.1 2004/04/05 04:30:46 mcr
++ * patches for alg-branch to compile/work with 2.x openswan
++ *
++ * Revision 1.67 2003/10/31 02:27:55 mcr
++ * pulled up port-selector patches and sa_id elimination.
++ *
++ * Revision 1.66.24.2 2003/10/29 01:30:41 mcr
++ * elimited "struct sa_id".
++ *
++ * Revision 1.66.24.1 2003/09/21 13:59:56 mcr
++ * pre-liminary X.509 patch - does not yet pass tests.
++ *
++ * Revision 1.66 2002/10/12 23:11:53 dhr
++ *
++ * [KenB + DHR] more 64-bit cleanup
++ *
++ * Revision 1.65 2002/09/20 05:01:40 rgb
++ * Added memory allocation debugging.
++ *
++ * Revision 1.64 2002/05/31 01:46:05 mcr
++ * added && sysctl_ipsec_debug_verbose verbose to ipsec_findroute
++ * as requested in PR#14.
++ *
++ * Revision 1.63 2002/05/23 07:14:11 rgb
++ * Cleaned up %p variants to 0p%p for test suite cleanup.
++ *
++ * Revision 1.62 2002/04/24 07:55:32 mcr
++ * #include patches and Makefiles for post-reorg compilation.
++ *
++ * Revision 1.61 2002/04/24 07:36:29 mcr
++ * Moved from ./klips/net/ipsec/ipsec_radij.c,v
++ *
++ * Revision 1.60 2002/02/19 23:59:45 rgb
++ * Removed redundant compiler directives.
++ *
++ * Revision 1.59 2002/02/06 04:13:47 mcr
++ * missing #ifdef CONFIG_IPSEC_DEBUG.
++ *
++ * Revision 1.58 2002/01/29 17:17:56 mcr
++ * moved include of ipsec_param.h to after include of linux/kernel.h
++ * otherwise, it seems that some option that is set in ipsec_param.h
++ * screws up something subtle in the include path to kernel.h, and
++ * it complains on the snprintf() prototype.
++ *
++ * Revision 1.57 2002/01/29 04:00:52 mcr
++ * more excise of kversions.h header.
++ *
++ * Revision 1.56 2002/01/29 02:13:17 mcr
++ * introduction of ipsec_kversion.h means that include of
++ * ipsec_param.h must preceed any decisions about what files to
++ * include to deal with differences in kernel source.
++ *
++ * Revision 1.55 2001/11/26 09:23:48 rgb
++ * Merge MCR's ipsec_sa, eroute, proc and struct lifetime changes.
++ *
++ * Revision 1.53.2.1 2001/09/25 02:26:32 mcr
++ * headers adjusted for new usage.
++ *
++ * Revision 1.54 2001/10/18 04:45:20 rgb
++ * 2.4.9 kernel deprecates linux/malloc.h in favour of linux/slab.h,
++ * lib/freeswan.h version macros moved to lib/kversions.h.
++ * Other compiler directive cleanups.
++ *
++ * Revision 1.53 2001/09/19 17:19:40 rgb
++ * Debug output bugfix for NetCelo's PF_KEY ident patch.
++ *
++ * Revision 1.52 2001/09/19 16:33:37 rgb
++ * Temporarily disable ident fields to /proc/net/ipsec_eroute.
++ *
++ * Revision 1.51 2001/09/15 16:24:04 rgb
++ * Re-inject first and last HOLD packet when an eroute REPLACE is done.
++ *
++ * Revision 1.50 2001/09/14 16:58:36 rgb
++ * Added support for storing the first and last packets through a HOLD.
++ *
++ * Revision 1.49 2001/09/08 21:13:32 rgb
++ * Added pfkey ident extension support for ISAKMPd. (NetCelo)
++ *
++ * Revision 1.48 2001/06/15 04:12:56 rgb
++ * Fixed kernel memory allocation error return code polarity bug.
++ *
++ * Revision 1.47 2001/06/14 19:35:09 rgb
++ * Update copyright date.
++ *
++ * Revision 1.46 2001/06/08 08:47:18 rgb
++ * Fixed for debug disabled.
++ *
++ * Revision 1.45 2001/05/27 06:12:11 rgb
++ * Added structures for pid, packet count and last access time to eroute.
++ * Added packet count to beginning of /proc/net/ipsec_eroute.
++ *
++ * Revision 1.44 2001/05/03 19:41:01 rgb
++ * Initialise error return variable.
++ * Use more appropriate return value for ipsec_rj_walker_delete().
++ *
++ * Revision 1.43 2001/02/27 22:24:54 rgb
++ * Re-formatting debug output (line-splitting, joining, 1arg/line).
++ * Check for satoa() return codes.
++ *
++ * Revision 1.42 2001/02/27 06:21:57 rgb
++ * Added findroute success instrumentation.
++ *
++ * Revision 1.41 2000/11/06 04:32:08 rgb
++ * Ditched spin_lock_irqsave in favour of spin_lock_bh.
++ *
++ * Revision 1.40 2000/09/08 19:12:56 rgb
++ * Change references from DEBUG_IPSEC to CONFIG_IPSEC_DEBUG.
++ *
++ * Revision 1.39 2000/08/30 05:25:20 rgb
++ * Correct debug text in ipsec_breakroute() from incorrect
++ * "ipsec_callback".
++ *
++ * Revision 1.38 2000/07/28 14:58:31 rgb
++ * Changed kfree_s to kfree, eliminating extra arg to fix 2.4.0-test5.
++ *
++ * Revision 1.37 2000/03/16 14:02:50 rgb
++ * Fixed debug scope to enable compilation with debug off.
++ *
++ * Revision 1.36 2000/01/21 06:14:46 rgb
++ * Added debugging text to ipsec_rj_walker_delete().
++ * Set return code to negative for consistency.
++ *
++ * Revision 1.35 1999/11/23 23:05:24 rgb
++ * Use provided macro ADDRTOA_BUF instead of hardcoded value.
++ *
++ * Revision 1.34 1999/11/18 04:13:56 rgb
++ * Replaced all kernel version macros to shorter, readable form.
++ * Added CONFIG_PROC_FS compiler directives in case it is shut off.
++ *
++ * Revision 1.33 1999/11/17 15:53:39 rgb
++ * Changed all occurrences of #include "../../../lib/freeswan.h"
++ * to #include <freeswan.h> which works due to -Ilibfreeswan in the
++ * klips/net/ipsec/Makefile.
++ *
++ * Revision 1.32 1999/10/26 13:58:33 rgb
++ * Put spinlock flags variable declaration outside the debug compiler
++ * directive to enable compilation with debug shut off.
++ *
++ * Revision 1.31 1999/10/15 22:13:29 rgb
++ * Clean out cruft.
++ * Align /proc/net/ipsec_eroute output for easier readability.
++ * Fix double linefeed in radij debug output.
++ * Fix double locking bug that locks up 2.0.36 but not 2.0.38.
++ *
++ * Revision 1.30 1999/10/08 18:37:33 rgb
++ * Fix end-of-line spacing to sate whining PHMs.
++ *
++ * Revision 1.29 1999/10/03 18:52:45 rgb
++ * Spinlock support for 2.0.xx.
++ * Dumb return code spin_unlock fix.
++ *
++ * Revision 1.28 1999/10/01 16:22:24 rgb
++ * Switch from assignment init. to functional init. of spinlocks.
++ *
++ * Revision 1.27 1999/10/01 15:44:53 rgb
++ * Move spinlock header include to 2.1> scope.
++ *
++ * Revision 1.26 1999/10/01 00:01:23 rgb
++ * Added eroute structure locking.
++ *
++ * Revision 1.25 1999/06/10 16:07:30 rgb
++ * Silence delete eroute on no debug.
++ *
++ * Revision 1.24 1999/05/09 03:25:36 rgb
++ * Fix bug introduced by 2.2 quick-and-dirty patch.
++ *
++ * Revision 1.23 1999/05/05 22:02:31 rgb
++ * Add a quick and dirty port to 2.2 kernels by Marc Boucher <marc@mbsi.ca>.
++ *
++ * Revision 1.22 1999/04/29 15:17:23 rgb
++ * Add return values to init and cleanup functions.
++ * Add sanity checking for null pointer arguments.
++ *
++ * Revision 1.21 1999/04/11 00:28:58 henry
++ * GPL boilerplate
++ *
++ * Revision 1.20 1999/04/06 04:54:26 rgb
++ * Fix/Add RCSID Id: and Log: bits to make PHMDs happy. This includes
++ * patch shell fixes.
++ *
++ * Revision 1.19 1999/02/17 16:50:35 rgb
++ * Clean out unused cruft.
++ * Consolidate for space and speed efficiency.
++ * Convert DEBUG_IPSEC to KLIPS_PRINT
++ *
++ * Revision 1.18 1999/01/22 06:22:06 rgb
++ * Cruft clean-out.
++ * 64-bit clean-up.
++ *
++ * Revision 1.17 1998/12/02 03:09:39 rgb
++ * Clean up debug printing conditionals to compile with debugging off.
++ *
++ * Revision 1.16 1998/12/01 13:49:39 rgb
++ * Wrap version info printing in debug switches.
++ *
++ * Revision 1.15 1998/11/30 13:22:54 rgb
++ * Rationalised all the klips kernel file headers. They are much shorter
++ * now and won't conflict under RH5.2.
++ *
++ * Revision 1.14 1998/10/31 06:48:17 rgb
++ * Fixed up comments in #endif directives.
++ *
++ * Revision 1.13 1998/10/27 13:48:09 rgb
++ * Cleaned up /proc/net/ipsec_* filesystem for easy parsing by scripts.
++ * Fixed less(1) truncated output bug.
++ * Code clean-up.
++ *
++ * Revision 1.12 1998/10/25 02:41:36 rgb
++ * Change return type on ipsec_breakroute and ipsec_makeroute and add an
++ * argument to be able to transmit more infomation about errors.
++ * Fix cut-and-paste debug statement identifier.
++ *
++ * Revision 1.11 1998/10/22 06:45:39 rgb
++ * Cleaned up cruft.
++ * Convert to use satoa for printk.
++ *
++ * Revision 1.10 1998/10/19 14:44:28 rgb
++ * Added inclusion of freeswan.h.
++ * sa_id structure implemented and used: now includes protocol.
++ *
++ * Revision 1.9 1998/10/09 04:30:52 rgb
++ * Added 'klips_debug' prefix to all klips printk debug statements.
++ * Deleted old commented out cruft.
++ *
++ * Revision 1.8 1998/08/06 17:24:23 rgb
++ * Fix addrtoa return code bug from stale manpage advice preventing packets
++ * from being erouted.
++ *
++ * Revision 1.7 1998/08/06 07:44:59 rgb
++ * Fixed /proc/net/ipsec_eroute subnettoa and addrtoa return value bug that
++ * ended up in nothing being printed.
++ *
++ * Revision 1.6 1998/08/05 22:16:41 rgb
++ * Cleanup to prevent cosmetic errors (ie. debug output) from being fatal.
++ *
++ * Revision 1.5 1998/07/29 20:38:44 rgb
++ * Debug and fix subnettoa and addrtoa output.
++ *
++ * Revision 1.4 1998/07/28 00:02:39 rgb
++ * Converting to exclusive use of addrtoa.
++ * Fix eroute delete.
++ *
++ * Revision 1.3 1998/07/14 18:21:26 rgb
++ * Add function to clear the eroute table.
++ *
++ * Revision 1.2 1998/06/23 02:59:14 rgb
++ * Added debugging output to eroute add/delete routines.
++ *
++ * Revision 1.9 1998/06/18 21:29:06 henry
++ * move sources from klips/src to klips/net/ipsec, to keep stupid kernel
++ * build scripts happier in presence of symbolic links
++ *
++ * Revision 1.8 1998/06/05 02:32:26 rgb
++ * Fix spi ntoh kernel debug output.
++ *
++ * Revision 1.7 1998/05/25 20:30:37 rgb
++ * Remove temporary ipsec_walk, rj_deltree and rj_delnodes functions.
++ *
++ * Rename ipsec_rj_walker (ipsec_walk) to ipsec_rj_walker_procprint and
++ * add ipsec_rj_walker_delete.
++ *
++ * Revision 1.6 1998/05/21 13:08:57 rgb
++ * Rewrote procinfo subroutines to avoid *bad things* when more that 3k of
++ * information is available for printout.
++ *
++ * Revision 1.5 1998/05/18 21:35:55 rgb
++ * Clean up output for numerical consistency and readability. Zero freed
++ * eroute memory.
++ *
++ * Revision 1.4 1998/04/21 21:28:58 rgb
++ * Rearrange debug switches to change on the fly debug output from user
++ * space. Only kernel changes checked in at this time. radij.c was also
++ * changed to temporarily remove buggy debugging code in rj_delete causing
++ * an OOPS and hence, netlink device open errors.
++ *
++ * Revision 1.3 1998/04/14 17:30:39 rgb
++ * Fix up compiling errors for radij tree memory reclamation.
++ *
++ * Revision 1.2 1998/04/12 22:03:23 rgb
++ * Updated ESP-3DES-HMAC-MD5-96,
++ * ESP-DES-HMAC-MD5-96,
++ * AH-HMAC-MD5-96,
++ * AH-HMAC-SHA1-96 since Henry started freeswan cvs repository
++ * from old standards (RFC182[5-9] to new (as of March 1998) drafts.
++ *
++ * Fixed eroute references in /proc/net/ipsec*.
++ *
++ * Started to patch module unloading memory leaks in ipsec_netlink and
++ * radij tree unloading.
++ *
++ * Revision 1.1 1998/04/09 03:06:10 henry
++ * sources moved up from linux/net/ipsec
++ *
++ * Revision 1.1.1.1 1998/04/08 05:35:03 henry
++ * RGB's ipsec-0.8pre2.tar.gz ipsec-0.8
++ *
++ * Revision 0.4 1997/01/15 01:28:15 ji
++ * No changes.
++ *
++ * Revision 0.3 1996/11/20 14:39:04 ji
++ * Minor cleanups.
++ * Rationalized debugging code.
++ *
++ * Revision 0.2 1996/11/02 00:18:33 ji
++ * First limited release.
++ *
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/net/ipsec/ipsec_rcv.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,1922 @@
++/*
++ * receive code
++ * Copyright (C) 1996, 1997 John Ioannidis.
++ * Copyright (C) 1998-2003 Richard Guy Briggs.
++ * Copyright (C) 2004 Michael Richardson <mcr@xelerance.com>
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ */
++
++char ipsec_rcv_c_version[] = "RCSID $Id$";
++
++#include <linux/config.h>
++#include <linux/version.h>
++
++#define __NO_VERSION__
++#include <linux/module.h>
++#include <linux/kernel.h> /* printk() */
++
++#include "openswan/ipsec_param.h"
++
++#ifdef MALLOC_SLAB
++# include <linux/slab.h> /* kmalloc() */
++#else /* MALLOC_SLAB */
++# include <linux/malloc.h> /* kmalloc() */
++#endif /* MALLOC_SLAB */
++#include <linux/errno.h> /* error codes */
++#include <linux/types.h> /* size_t */
++#include <linux/interrupt.h> /* mark_bh */
++
++#include <net/udp.h>
++
++#include <linux/netdevice.h> /* struct device, and other headers */
++#include <linux/etherdevice.h> /* eth_type_trans */
++#include <linux/ip.h> /* struct iphdr */
++#include <linux/skbuff.h>
++#include <openswan.h>
++#ifdef SPINLOCK
++# ifdef SPINLOCK_23
++# include <linux/spinlock.h> /* *lock* */
++# else /* SPINLOCK_23 */
++# include <asm/spinlock.h> /* *lock* */
++# endif /* SPINLOCK_23 */
++#endif /* SPINLOCK */
++#ifdef NET_21
++# include <asm/uaccess.h>
++# include <linux/in6.h>
++# define proto_priv cb
++#endif /* NET21 */
++#include <asm/checksum.h>
++#include <net/ip.h>
++
++#include "openswan/radij.h"
++#include "openswan/ipsec_encap.h"
++#include "openswan/ipsec_sa.h"
++
++#include "openswan/ipsec_radij.h"
++#include "openswan/ipsec_xform.h"
++#include "openswan/ipsec_tunnel.h"
++#include "openswan/ipsec_rcv.h"
++
++#include "openswan/ipsec_auth.h"
++
++#include "openswan/ipsec_esp.h"
++
++#ifdef CONFIG_IPSEC_AH
++#include "openswan/ipsec_ah.h"
++#endif /* CONFIG_IPSEC_AH */
++
++#ifdef CONFIG_IPSEC_IPCOMP
++#include "openswan/ipsec_ipcomp.h"
++#endif /* CONFIG_IPSEC_COMP */
++
++#include <pfkeyv2.h>
++#include <pfkey.h>
++
++#include "openswan/ipsec_proto.h"
++#include "openswan/ipsec_alg.h"
++
++#ifdef CONFIG_IPSEC_DEBUG
++int debug_rcv = 0;
++#endif /* CONFIG_IPSEC_DEBUG */
++
++int sysctl_ipsec_inbound_policy_check = 1;
++
++/* This is a private use protocol, and AT&T should be ashamed. They should have
++ * used protocol # 59, which is "no next header" instead of 0xFE.
++ */
++#ifndef IPPROTO_ATT_HEARTBEAT
++#define IPPROTO_ATT_HEARTBEAT 0xFE
++#endif
++
++#ifdef CONFIG_IPSEC_DEBUG
++void
++ipsec_dmp(char *s, caddr_t bb, int len)
++{
++ int i;
++ unsigned char *b = bb;
++
++
++ printk(KERN_INFO "klips_debug:ipsec_tunnel_:dmp: "
++ "at %s, len=%d:",
++ s,
++ len);
++ for (i=0; i < len; i++) {
++ if(!(i%16)){
++ printk("\nklips_debug: ");
++ }
++ printk(" %02x", *b++);
++ }
++ printk("\n");
++}
++#endif /* CONFIG_IPSEC_DEBUG */
++
++/*
++ * Check-replay-window routine, adapted from the original
++ * by J. Hughes, from draft-ietf-ipsec-esp-des-md5-03.txt
++ *
++ * This is a routine that implements a 64 packet window. This is intend-
++ * ed on being an implementation sample.
++ */
++
++DEBUG_NO_STATIC int
++ipsec_checkreplaywindow(struct ipsec_sa*ipsp, __u32 seq)
++{
++ __u32 diff;
++
++ if (ipsp->ips_replaywin == 0) /* replay shut off */
++ return 1;
++ if (seq == 0)
++ return 0; /* first == 0 or wrapped */
++
++ /* new larger sequence number */
++ if (seq > ipsp->ips_replaywin_lastseq) {
++ return 1; /* larger is good */
++ }
++ diff = ipsp->ips_replaywin_lastseq - seq;
++
++ /* too old or wrapped */ /* if wrapped, kill off SA? */
++ if (diff >= ipsp->ips_replaywin) {
++ return 0;
++ }
++ /* this packet already seen */
++ if (ipsp->ips_replaywin_bitmap & (1 << diff))
++ return 0;
++ return 1; /* out of order but good */
++}
++
++DEBUG_NO_STATIC int
++ipsec_updatereplaywindow(struct ipsec_sa*ipsp, __u32 seq)
++{
++ __u32 diff;
++
++ if (ipsp->ips_replaywin == 0) /* replay shut off */
++ return 1;
++ if (seq == 0)
++ return 0; /* first == 0 or wrapped */
++
++ /* new larger sequence number */
++ if (seq > ipsp->ips_replaywin_lastseq) {
++ diff = seq - ipsp->ips_replaywin_lastseq;
++
++ /* In win, set bit for this pkt */
++ if (diff < ipsp->ips_replaywin)
++ ipsp->ips_replaywin_bitmap =
++ (ipsp->ips_replaywin_bitmap << diff) | 1;
++ else
++ /* This packet has way larger seq num */
++ ipsp->ips_replaywin_bitmap = 1;
++
++ if(seq - ipsp->ips_replaywin_lastseq - 1 > ipsp->ips_replaywin_maxdiff) {
++ ipsp->ips_replaywin_maxdiff = seq - ipsp->ips_replaywin_lastseq - 1;
++ }
++ ipsp->ips_replaywin_lastseq = seq;
++ return 1; /* larger is good */
++ }
++ diff = ipsp->ips_replaywin_lastseq - seq;
++
++ /* too old or wrapped */ /* if wrapped, kill off SA? */
++ if (diff >= ipsp->ips_replaywin) {
++/*
++ if(seq < 0.25*max && ipsp->ips_replaywin_lastseq > 0.75*max) {
++ ipsec_sa_delchain(ipsp);
++ }
++*/
++ return 0;
++ }
++ /* this packet already seen */
++ if (ipsp->ips_replaywin_bitmap & (1 << diff))
++ return 0;
++ ipsp->ips_replaywin_bitmap |= (1 << diff); /* mark as seen */
++ return 1; /* out of order but good */
++}
++
++#ifdef CONFIG_IPSEC_AUTH_HMAC_MD5
++struct auth_alg ipsec_rcv_md5[]={
++ {MD5Init, MD5Update, MD5Final, AHMD596_ALEN}
++};
++
++#endif /* CONFIG_IPSEC_AUTH_HMAC_MD5 */
++
++#ifdef CONFIG_IPSEC_AUTH_HMAC_SHA1
++struct auth_alg ipsec_rcv_sha1[]={
++ {SHA1Init, SHA1Update, SHA1Final, AHSHA196_ALEN}
++};
++#endif /* CONFIG_IPSEC_AUTH_HMAC_MD5 */
++
++enum ipsec_rcv_value
++ipsec_rcv_decap_once(struct ipsec_rcv_state *irs, struct xform_functions *proto_funcs)
++{
++ int iphlen;
++ unsigned char *dat;
++ __u8 proto;
++ struct in_addr ipsaddr;
++ struct in_addr ipdaddr;
++ int replay = 0; /* replay value in AH or ESP packet */
++ struct ipsec_sa* ipsnext = NULL; /* next SA towards inside of packet */
++ struct ipsec_sa *newipsp;
++ struct iphdr *ipp;
++ struct sk_buff *skb;
++#ifdef CONFIG_IPSEC_ALG
++ struct ipsec_alg_auth *ixt_a=NULL;
++#endif /* CONFIG_IPSEC_ALG */
++
++ skb = irs->skb;
++ irs->len = skb->len;
++ dat = skb->data;
++ ipp = irs->ipp;
++ proto = ipp->protocol;
++ ipsaddr.s_addr = ipp->saddr;
++ addrtoa(ipsaddr, 0, irs->ipsaddr_txt, sizeof(irs->ipsaddr_txt));
++ ipdaddr.s_addr = ipp->daddr;
++ addrtoa(ipdaddr, 0, irs->ipdaddr_txt, sizeof(irs->ipdaddr_txt));
++
++ iphlen = ipp->ihl << 2;
++ irs->iphlen=iphlen;
++ ipp->check = 0; /* we know the sum is good */
++
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv_decap_once: "
++ "decap (%d) from %s -> %s\n",
++ proto, irs->ipsaddr_txt, irs->ipdaddr_txt);
++
++ /*
++ * Find tunnel control block and (indirectly) call the
++ * appropriate tranform routine. The resulting sk_buf
++ * is a valid IP packet ready to go through input processing.
++ */
++
++ irs->said.dst.u.v4.sin_addr.s_addr = ipp->daddr;
++ irs->said.dst.u.v4.sin_family = AF_INET;
++
++ if(proto_funcs->rcv_checks) {
++ enum ipsec_rcv_value retval =
++ (*proto_funcs->rcv_checks)(irs, skb);
++
++ if(retval < 0) {
++ return retval;
++ }
++ }
++
++ irs->said.proto = proto;
++ irs->sa_len = satot(&irs->said, 0, irs->sa, sizeof(irs->sa));
++ if(irs->sa_len == 0) {
++ strcpy(irs->sa, "(error)");
++ }
++
++ newipsp = ipsec_sa_getbyid(&irs->said);
++ if (newipsp == NULL) {
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "no ipsec_sa for SA:%s: incoming packet with no SA dropped\n",
++ irs->sa_len ? irs->sa : " (error)");
++ if(irs->stats) {
++ irs->stats->rx_dropped++;
++ }
++ return IPSEC_RCV_SAIDNOTFOUND;
++ }
++
++ /* MCR - XXX this is bizarre. ipsec_sa_getbyid returned it, having incremented the refcount,
++ * why in the world would we decrement it here?
++
++ ipsec_sa_put(irs->ipsp);*/ /* incomplete */
++
++ /* If it is in larval state, drop the packet, we cannot process yet. */
++ if(newipsp->ips_state == SADB_SASTATE_LARVAL) {
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "ipsec_sa in larval state, cannot be used yet, dropping packet.\n");
++ if(irs->stats) {
++ irs->stats->rx_dropped++;
++ }
++ ipsec_sa_put(newipsp);
++ return IPSEC_RCV_SAIDNOTLIVE;
++ }
++
++ if(newipsp->ips_state == SADB_SASTATE_DEAD) {
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "ipsec_sa in dead state, cannot be used any more, dropping packet.\n");
++ if(irs->stats) {
++ irs->stats->rx_dropped++;
++ }
++ ipsec_sa_put(newipsp);
++ return IPSEC_RCV_SAIDNOTLIVE;
++ }
++
++ if(sysctl_ipsec_inbound_policy_check) {
++ if(irs->ipp->saddr != ((struct sockaddr_in*)(newipsp->ips_addr_s))->sin_addr.s_addr) {
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "SA:%s, src=%s of pkt does not agree with expected SA source address policy.\n",
++ irs->sa_len ? irs->sa : " (error)",
++ irs->ipsaddr_txt);
++ if(irs->stats) {
++ irs->stats->rx_dropped++;
++ }
++ ipsec_sa_put(newipsp);
++ return IPSEC_RCV_FAILEDINBOUND;
++ }
++
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "SA:%s, src=%s of pkt agrees with expected SA source address policy.\n",
++ irs->sa_len ? irs->sa : " (error)",
++ irs->ipsaddr_txt);
++
++ /*
++ * at this point, we have looked up a new SA, and we want to make sure that if this
++ * isn't the first SA in the list, that the previous SA actually points at this one.
++ */
++ if(irs->ipsp) {
++ if(irs->ipsp->ips_inext != newipsp) {
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "unexpected SA:%s: does not agree with ips->inext policy, dropped\n",
++ irs->sa_len ? irs->sa : " (error)");
++ if(irs->stats) {
++ irs->stats->rx_dropped++;
++ }
++ ipsec_sa_put(newipsp);
++ return IPSEC_RCV_FAILEDINBOUND;
++ }
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "SA:%s grouping from previous SA is OK.\n",
++ irs->sa_len ? irs->sa : " (error)");
++ } else {
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "SA:%s First SA in group.\n",
++ irs->sa_len ? irs->sa : " (error)");
++ }
++
++ /*
++ * previously, at this point, we checked if the back pointer from the new SA that
++ * we just found matched the back pointer. But, we won't do this check anymore,
++ * because we want to be able to nest SAs
++ */
++#ifdef CONFIG_IPSEC_NAT_TRAVERSAL
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "natt_type=%u tdbp->ips_natt_type=%u : %s\n",
++ irs->natt_type, newipsp->ips_natt_type,
++ (irs->natt_type==newipsp->ips_natt_type)?"ok":"bad");
++ if (irs->natt_type != newipsp->ips_natt_type) {
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "SA:%s does not agree with expected NAT-T policy.\n",
++ irs->sa_len ? irs->sa : " (error)");
++ if(irs->stats) {
++ irs->stats->rx_dropped++;
++ }
++ ipsec_sa_put(newipsp);
++ return IPSEC_RCV_FAILEDINBOUND;
++ }
++#endif
++ }
++
++ /* okay, SA checks out, so free any previous SA, and record a new one */
++
++ if(irs->ipsp) {
++ ipsec_sa_put(irs->ipsp);
++ }
++ irs->ipsp=newipsp;
++
++ /* note that the outer code will free the irs->ipsp if there is an error */
++
++
++ /* now check the lifetimes */
++ if(ipsec_lifetime_check(&irs->ipsp->ips_life.ipl_bytes, "bytes",
++ irs->sa, ipsec_life_countbased, ipsec_incoming,
++ irs->ipsp) == ipsec_life_harddied ||
++ ipsec_lifetime_check(&irs->ipsp->ips_life.ipl_addtime, "addtime",
++ irs->sa, ipsec_life_timebased, ipsec_incoming,
++ irs->ipsp) == ipsec_life_harddied ||
++ ipsec_lifetime_check(&irs->ipsp->ips_life.ipl_addtime, "usetime",
++ irs->sa, ipsec_life_timebased, ipsec_incoming,
++ irs->ipsp) == ipsec_life_harddied ||
++ ipsec_lifetime_check(&irs->ipsp->ips_life.ipl_packets, "packets",
++ irs->sa, ipsec_life_countbased, ipsec_incoming,
++ irs->ipsp) == ipsec_life_harddied) {
++ ipsec_sa_delchain(irs->ipsp);
++ if(irs->stats) {
++ irs->stats->rx_dropped++;
++ }
++
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv_decap_once: "
++ "decap (%d) failed lifetime check\n",
++ proto);
++
++ return IPSEC_RCV_LIFETIMEFAILED;
++ }
++
++#ifdef CONFIG_IPSEC_NAT_TRAVERSAL
++ if ((irs->natt_type) &&
++ ( (irs->ipp->saddr != (((struct sockaddr_in*)(newipsp->ips_addr_s))->sin_addr.s_addr)) ||
++ (irs->natt_sport != newipsp->ips_natt_sport)
++ )) {
++ struct sockaddr sipaddr;
++ /** Advertise NAT-T addr change to pluto **/
++ sipaddr.sa_family = AF_INET;
++ ((struct sockaddr_in*)&sipaddr)->sin_addr.s_addr = irs->ipp->saddr;
++ ((struct sockaddr_in*)&sipaddr)->sin_port = htons(irs->natt_sport);
++ pfkey_nat_t_new_mapping(newipsp, &sipaddr, irs->natt_sport);
++ /**
++ * Then allow or block packet depending on
++ * sysctl_ipsec_inbound_policy_check.
++ *
++ * In all cases, pluto will update SA if new mapping is
++ * accepted.
++ */
++ if (sysctl_ipsec_inbound_policy_check) {
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "SA:%s, src=%s:%u of pkt does not agree with expected "
++ "SA source address policy (pluto has been informed).\n",
++ irs->sa_len ? irs->sa : " (error)",
++ irs->ipsaddr_txt, irs->natt_sport);
++ if(irs->stats) {
++ irs->stats->rx_dropped++;
++ }
++ ipsec_sa_put(newipsp);
++ return IPSEC_RCV_FAILEDINBOUND;
++ }
++ }
++#endif
++
++ irs->authfuncs=NULL;
++ /* authenticate, if required */
++#ifdef CONFIG_IPSEC_ALG
++ if ((ixt_a=irs->ipsp->ips_alg_auth)) {
++ irs->authlen = AHHMAC_HASHLEN;
++ irs->authfuncs = NULL;
++ irs->ictx = NULL;
++ irs->octx = NULL;
++ irs->ictx_len = 0;
++ irs->octx_len = 0;
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "authalg=%d authlen=%d\n",
++ irs->ipsp->ips_authalg,
++ irs->authlen);
++ } else
++#endif /* CONFIG_IPSEC_ALG */
++ switch(irs->ipsp->ips_authalg) {
++#ifdef CONFIG_IPSEC_AUTH_HMAC_MD5
++ case AH_MD5:
++ irs->authlen = AHHMAC_HASHLEN;
++ irs->authfuncs = ipsec_rcv_md5;
++ irs->ictx = (void *)&((struct md5_ctx*)(irs->ipsp->ips_key_a))->ictx;
++ irs->octx = (void *)&((struct md5_ctx*)(irs->ipsp->ips_key_a))->octx;
++ irs->ictx_len = sizeof(((struct md5_ctx*)(irs->ipsp->ips_key_a))->ictx);
++ irs->octx_len = sizeof(((struct md5_ctx*)(irs->ipsp->ips_key_a))->octx);
++ break;
++#endif /* CONFIG_IPSEC_AUTH_HMAC_MD5 */
++#ifdef CONFIG_IPSEC_AUTH_HMAC_SHA1
++ case AH_SHA:
++ irs->authlen = AHHMAC_HASHLEN;
++ irs->authfuncs = ipsec_rcv_sha1;
++ irs->ictx = (void *)&((struct sha1_ctx*)(irs->ipsp->ips_key_a))->ictx;
++ irs->octx = (void *)&((struct sha1_ctx*)(irs->ipsp->ips_key_a))->octx;
++ irs->ictx_len = sizeof(((struct sha1_ctx*)(irs->ipsp->ips_key_a))->ictx);
++ irs->octx_len = sizeof(((struct sha1_ctx*)(irs->ipsp->ips_key_a))->octx);
++ break;
++#endif /* CONFIG_IPSEC_AUTH_HMAC_SHA1 */
++ case AH_NONE:
++ irs->authlen = 0;
++ irs->authfuncs = NULL;
++ irs->ictx = NULL;
++ irs->octx = NULL;
++ irs->ictx_len = 0;
++ irs->octx_len = 0;
++ break;
++ default:
++ irs->ipsp->ips_errs.ips_alg_errs += 1;
++ if(irs->stats) {
++ irs->stats->rx_errors++;
++ }
++ return IPSEC_RCV_BADAUTH;
++ }
++
++ irs->ilen = irs->len - iphlen - irs->authlen;
++ if(irs->ilen <= 0) {
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "runt %s packet with no data, dropping.\n",
++ (proto == IPPROTO_ESP ? "esp" : "ah"));
++ if(irs->stats) {
++ irs->stats->rx_dropped++;
++ }
++ return IPSEC_RCV_BADLEN;
++ }
++
++#ifdef CONFIG_IPSEC_ALG
++ if(irs->authfuncs || ixt_a) {
++#else
++ if(irs->authfuncs) {
++#endif
++ unsigned char *authenticator = NULL;
++
++ if(proto_funcs->rcv_setup_auth) {
++ enum ipsec_rcv_value retval
++ = (*proto_funcs->rcv_setup_auth)(irs, skb,
++ &replay,
++ &authenticator);
++ if(retval < 0) {
++ return retval;
++ }
++ }
++
++ if(!authenticator) {
++ irs->ipsp->ips_errs.ips_auth_errs += 1;
++ if(irs->stats) {
++ irs->stats->rx_dropped++;
++ }
++ return IPSEC_RCV_BADAUTH;
++ }
++
++ if(!ipsec_checkreplaywindow(irs->ipsp, replay)) {
++ irs->ipsp->ips_errs.ips_replaywin_errs += 1;
++ KLIPS_PRINT(debug_rcv & DB_RX_REPLAY,
++ "klips_debug:ipsec_rcv: "
++ "duplicate frame from %s, packet dropped\n",
++ irs->ipsaddr_txt);
++ if(irs->stats) {
++ irs->stats->rx_dropped++;
++ }
++ return IPSEC_RCV_REPLAYFAILED;
++ }
++
++ /*
++ * verify authenticator
++ */
++
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "encalg = %d, authalg = %d.\n",
++ irs->ipsp->ips_encalg,
++ irs->ipsp->ips_authalg);
++
++ /* calculate authenticator */
++ if(proto_funcs->rcv_calc_auth == NULL) {
++ return IPSEC_RCV_BADAUTH;
++ }
++ (*proto_funcs->rcv_calc_auth)(irs, skb);
++
++ if (memcmp(irs->hash, authenticator, irs->authlen)) {
++ irs->ipsp->ips_errs.ips_auth_errs += 1;
++ KLIPS_PRINT(debug_rcv & DB_RX_INAU,
++ "klips_debug:ipsec_rcv: "
++ "auth failed on incoming packet from %s: hash=%08x%08x%08x auth=%08x%08x%08x, dropped\n",
++ irs->ipsaddr_txt,
++ ntohl(*(__u32*)&irs->hash[0]),
++ ntohl(*(__u32*)&irs->hash[4]),
++ ntohl(*(__u32*)&irs->hash[8]),
++ ntohl(*(__u32*)authenticator),
++ ntohl(*((__u32*)authenticator + 1)),
++ ntohl(*((__u32*)authenticator + 2)));
++ if(irs->stats) {
++ irs->stats->rx_dropped++;
++ }
++ return IPSEC_RCV_AUTHFAILED;
++ } else {
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "authentication successful.\n");
++ }
++
++ /* Crypto hygiene: clear memory used to calculate autheticator.
++ * The length varies with the algorithm.
++ */
++ memset(irs->hash, 0, irs->authlen);
++
++ /* If the sequence number == 0, expire SA, it had rolled */
++ if(irs->ipsp->ips_replaywin && !replay /* !irs->ipsp->ips_replaywin_lastseq */) {
++ ipsec_sa_delchain(irs->ipsp);
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "replay window counter rolled, expiring SA.\n");
++ if(irs->stats) {
++ irs->stats->rx_dropped++;
++ }
++ return IPSEC_RCV_REPLAYROLLED;
++ }
++
++ /* now update the replay counter */
++ if (!ipsec_updatereplaywindow(irs->ipsp, replay)) {
++ irs->ipsp->ips_errs.ips_replaywin_errs += 1;
++ KLIPS_PRINT(debug_rcv & DB_RX_REPLAY,
++ "klips_debug:ipsec_rcv: "
++ "duplicate frame from %s, packet dropped\n",
++ irs->ipsaddr_txt);
++ if(irs->stats) {
++ irs->stats->rx_dropped++;
++ }
++ return IPSEC_RCV_REPLAYROLLED;
++ }
++ }
++
++ if(proto_funcs->rcv_decrypt) {
++ enum ipsec_rcv_value retval =
++ (*proto_funcs->rcv_decrypt)(irs);
++
++ if(retval != IPSEC_RCV_OK) {
++ return retval;
++ }
++ }
++
++ /*
++ * Adjust pointers
++ */
++ skb = irs->skb;
++ irs->len = skb->len;
++ dat = skb->data;
++
++#ifdef NET_21
++/* skb->h.ipiph=(struct iphdr *)skb->data; */
++ skb->nh.raw = skb->data;
++ skb->h.raw = skb->nh.raw + (skb->nh.iph->ihl << 2);
++
++ memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options));
++#else /* NET_21 */
++ skb->h.iph=(struct iphdr *)skb->data;
++ skb->ip_hdr=(struct iphdr *)skb->data;
++ memset(skb->proto_priv, 0, sizeof(struct options));
++#endif /* NET_21 */
++
++ ipp = (struct iphdr *)dat;
++ ipsaddr.s_addr = ipp->saddr;
++ addrtoa(ipsaddr, 0, irs->ipsaddr_txt, sizeof(irs->ipsaddr_txt));
++ ipdaddr.s_addr = ipp->daddr;
++ addrtoa(ipdaddr, 0, irs->ipdaddr_txt, sizeof(irs->ipdaddr_txt));
++ /*
++ * Discard the original ESP/AH header
++ */
++ ipp->protocol = irs->next_header;
++
++ ipp->check = 0; /* NOTE: this will be included in checksum */
++ ipp->check = ip_fast_csum((unsigned char *)dat, iphlen >> 2);
++
++ KLIPS_PRINT(debug_rcv & DB_RX_PKTRX,
++ "klips_debug:ipsec_rcv: "
++ "after <%s%s%s>, SA:%s:\n",
++ IPS_XFORM_NAME(irs->ipsp),
++ irs->sa_len ? irs->sa : " (error)");
++ KLIPS_IP_PRINT(debug_rcv & DB_RX_PKTRX, ipp);
++
++ skb->protocol = htons(ETH_P_IP);
++ skb->ip_summed = 0;
++
++ ipsnext = irs->ipsp->ips_inext;
++ if(sysctl_ipsec_inbound_policy_check) {
++ if(ipsnext) {
++ if(
++ ipp->protocol != IPPROTO_AH
++ && ipp->protocol != IPPROTO_ESP
++#ifdef CONFIG_IPSEC_IPCOMP
++ && ipp->protocol != IPPROTO_COMP
++ && (ipsnext->ips_said.proto != IPPROTO_COMP
++ || ipsnext->ips_inext)
++#endif /* CONFIG_IPSEC_IPCOMP */
++ && ipp->protocol != IPPROTO_IPIP
++ && ipp->protocol != 0xFE /* added to support heartbeats to AT&T SIG/GIG */
++ ) {
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "packet with incomplete policy dropped, last successful SA:%s.\n",
++ irs->sa_len ? irs->sa : " (error)");
++ if(irs->stats) {
++ irs->stats->rx_dropped++;
++ }
++ return IPSEC_RCV_FAILEDINBOUND;
++ }
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "SA:%s, Another IPSEC header to process.\n",
++ irs->sa_len ? irs->sa : " (error)");
++ } else {
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "No ips_inext from this SA:%s.\n",
++ irs->sa_len ? irs->sa : " (error)");
++ }
++ }
++
++#ifdef CONFIG_IPSEC_IPCOMP
++ /* update ipcomp ratio counters, even if no ipcomp packet is present */
++ if (ipsnext
++ && ipsnext->ips_said.proto == IPPROTO_COMP
++ && ipp->protocol != IPPROTO_COMP) {
++ ipsnext->ips_comp_ratio_cbytes += ntohs(ipp->tot_len);
++ ipsnext->ips_comp_ratio_dbytes += ntohs(ipp->tot_len);
++ }
++#endif /* CONFIG_IPSEC_IPCOMP */
++
++ irs->ipsp->ips_life.ipl_bytes.ipl_count += irs->len;
++ irs->ipsp->ips_life.ipl_bytes.ipl_last = irs->len;
++
++ if(!irs->ipsp->ips_life.ipl_usetime.ipl_count) {
++ irs->ipsp->ips_life.ipl_usetime.ipl_count = jiffies / HZ;
++ }
++ irs->ipsp->ips_life.ipl_usetime.ipl_last = jiffies / HZ;
++ irs->ipsp->ips_life.ipl_packets.ipl_count += 1;
++
++#ifdef CONFIG_NETFILTER
++ if(proto == IPPROTO_ESP || proto == IPPROTO_AH) {
++ skb->nfmark = (skb->nfmark & (~(IPsecSAref2NFmark(IPSEC_SA_REF_MASK))))
++ | IPsecSAref2NFmark(IPsecSA2SAref(irs->ipsp));
++ KLIPS_PRINT(debug_rcv & DB_RX_PKTRX,
++ "klips_debug:ipsec_rcv: "
++ "%s SA sets skb->nfmark=0x%x.\n",
++ proto == IPPROTO_ESP ? "ESP" : "AH",
++ (unsigned)skb->nfmark);
++ }
++#endif /* CONFIG_NETFILTER */
++
++ return IPSEC_RCV_OK;
++}
++
++
++int
++#ifdef PROTO_HANDLER_SINGLE_PARM
++ipsec_rcv(struct sk_buff *skb)
++#else /* PROTO_HANDLER_SINGLE_PARM */
++#ifdef NET_21
++ipsec_rcv(struct sk_buff *skb, unsigned short xlen)
++#else /* NET_21 */
++ipsec_rcv(struct sk_buff *skb, struct device *dev, struct options *opt,
++ __u32 daddr_unused, unsigned short xlen, __u32 saddr,
++ int redo, struct inet_protocol *protocol)
++#endif /* NET_21 */
++#endif /* PROTO_HANDLER_SINGLE_PARM */
++{
++#ifdef NET_21
++#ifdef CONFIG_IPSEC_DEBUG
++ struct device *dev = skb->dev;
++#endif /* CONFIG_IPSEC_DEBUG */
++#endif /* NET_21 */
++ unsigned char protoc;
++ struct iphdr *ipp;
++ struct ipsec_sa *ipsp = NULL;
++ struct net_device_stats *stats = NULL; /* This device's statistics */
++ struct device *ipsecdev = NULL, *prvdev;
++ struct ipsecpriv *prv;
++ char name[9];
++ int i;
++ struct in_addr ipsaddr;
++ struct in_addr ipdaddr;
++
++ struct ipsec_sa* ipsnext = NULL; /* next SA towards inside of packet */
++ struct ipsec_rcv_state irs;
++
++ /* Don't unlink in the middle of a turnaround */
++ MOD_INC_USE_COUNT;
++
++ memset(&irs, 0, sizeof(struct ipsec_rcv_state));
++
++ if (skb == NULL) {
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "NULL skb passed in.\n");
++ goto rcvleave;
++ }
++
++ if (skb->data == NULL) {
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "NULL skb->data passed in, packet is bogus, dropping.\n");
++ goto rcvleave;
++ }
++
++#ifdef CONFIG_IPSEC_NAT_TRAVERSAL
++ if (skb->sk && skb->nh.iph && skb->nh.iph->protocol==IPPROTO_UDP) {
++ /**
++ * Packet comes from udp_queue_rcv_skb so it is already defrag,
++ * checksum verified, ... (ie safe to use)
++ *
++ * If the packet is not for us, return -1 and udp_queue_rcv_skb
++ * will continue to handle it (do not kfree skb !!).
++ */
++#ifndef UDP_OPT_IN_SOCK
++ struct udp_opt {
++ __u32 esp_in_udp;
++ };
++ struct udp_opt *tp = (struct udp_opt *)&(skb->sk->tp_pinfo.af_tcp);
++#else
++ struct udp_opt *tp = &(skb->sk->tp_pinfo.af_udp);
++#endif
++ struct iphdr *ip = (struct iphdr *)skb->nh.iph;
++ struct udphdr *udp = (struct udphdr *)((__u32 *)ip+ip->ihl);
++ __u8 *udpdata = (__u8 *)udp + sizeof(struct udphdr);
++ __u32 *udpdata32 = (__u32 *)udpdata;
++
++ irs.natt_sport = ntohs(udp->source);
++ irs.natt_dport = ntohs(udp->dest);
++
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "suspected ESPinUDP packet (NAT-Traversal) [%d].\n",
++ tp->esp_in_udp);
++ KLIPS_IP_PRINT(debug_rcv, ip);
++
++ if (udpdata < skb->tail) {
++ unsigned int len = skb->tail - udpdata;
++ if ((len==1) && (udpdata[0]==0xff)) {
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ /* not IPv6 compliant message */
++ "NAT-keepalive from %d.%d.%d.%d.\n", NIPQUAD(ip->saddr));
++ goto rcvleave;
++ }
++ else if ( (tp->esp_in_udp == ESPINUDP_WITH_NON_IKE) &&
++ (len > (2*sizeof(__u32) + sizeof(struct esphdr))) &&
++ (udpdata32[0]==0) && (udpdata32[1]==0) ) {
++ /* ESP Packet with Non-IKE header */
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "ESPinUDP pkt with Non-IKE - spi=0x%x\n",
++ ntohl(udpdata32[2]));
++ irs.natt_type = ESPINUDP_WITH_NON_IKE;
++ irs.natt_len = sizeof(struct udphdr)+(2*sizeof(__u32));
++ }
++ else if ( (tp->esp_in_udp == ESPINUDP_WITH_NON_ESP) &&
++ (len > sizeof(struct esphdr)) &&
++ (udpdata32[0]!=0) ) {
++ /* ESP Packet without Non-ESP header */
++ irs.natt_type = ESPINUDP_WITH_NON_ESP;
++ irs.natt_len = sizeof(struct udphdr);
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "ESPinUDP pkt without Non-ESP - spi=0x%x\n",
++ ntohl(udpdata32[0]));
++ }
++ else {
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "IKE packet - not handled here\n");
++ MOD_DEC_USE_COUNT;
++ return -1;
++ }
++ }
++ else {
++ MOD_DEC_USE_COUNT;
++ return -1;
++ }
++ }
++#endif
++
++#ifdef IPH_is_SKB_PULLED
++ /* In Linux 2.4.4, the IP header has been skb_pull()ed before the
++ packet is passed to us. So we'll skb_push() to get back to it. */
++ if (skb->data == skb->h.raw) {
++ skb_push(skb, skb->h.raw - skb->nh.raw);
++ }
++#endif /* IPH_is_SKB_PULLED */
++
++ /* dev->hard_header_len is unreliable and should not be used */
++ irs.hard_header_len = skb->mac.raw ? (skb->data - skb->mac.raw) : 0;
++ if((irs.hard_header_len < 0) || (irs.hard_header_len > skb_headroom(skb)))
++ irs.hard_header_len = 0;
++
++#ifdef NET_21
++ /* if skb was cloned (most likely due to a packet sniffer such as
++ tcpdump being momentarily attached to the interface), make
++ a copy of our own to modify */
++ if(skb_cloned(skb)) {
++ /* include any mac header while copying.. */
++ if(skb_headroom(skb) < irs.hard_header_len) {
++ printk(KERN_WARNING "klips_error:ipsec_rcv: "
++ "tried to skb_push hhlen=%d, %d available. This should never happen, please report.\n",
++ irs.hard_header_len,
++ skb_headroom(skb));
++ goto rcvleave;
++ }
++ skb_push(skb, irs.hard_header_len);
++ if
++#ifdef SKB_COW_NEW
++ (skb_cow(skb, skb_headroom(skb)) != 0)
++#else /* SKB_COW_NEW */
++ ((skb = skb_cow(skb, skb_headroom(skb))) == NULL)
++#endif /* SKB_COW_NEW */
++ {
++ goto rcvleave;
++ }
++ if(skb->len < irs.hard_header_len) {
++ printk(KERN_WARNING "klips_error:ipsec_rcv: "
++ "tried to skb_pull hhlen=%d, %d available. This should never happen, please report.\n",
++ irs.hard_header_len,
++ skb->len);
++ goto rcvleave;
++ }
++ skb_pull(skb, irs.hard_header_len);
++ }
++
++#endif /* NET_21 */
++
++#if IP_FRAGMENT_LINEARIZE
++ /* In Linux 2.4.4, we may have to reassemble fragments. They are
++ not assembled automatically to save TCP from having to copy
++ twice.
++ */
++ if (skb_is_nonlinear(skb)) {
++ if (skb_linearize(skb, GFP_ATOMIC) != 0) {
++ goto rcvleave;
++ }
++ }
++#endif /* IP_FRAGMENT_LINEARIZE */
++
++#ifdef CONFIG_IPSEC_NAT_TRAVERSAL
++ if (irs.natt_len) {
++ /**
++ * Now, we are sure packet is ESPinUDP. Remove natt_len bytes from
++ * packet and modify protocol to ESP.
++ */
++ if (((unsigned char *)skb->data > (unsigned char *)skb->nh.iph) &&
++ ((unsigned char *)skb->nh.iph > (unsigned char *)skb->head)) {
++ unsigned int _len = (unsigned char *)skb->data -
++ (unsigned char *)skb->nh.iph;
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: adjusting skb: skb_push(%u)\n",
++ _len);
++ skb_push(skb, _len);
++ }
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "removing %d bytes from ESPinUDP packet\n", irs.natt_len);
++ ipp = (struct iphdr *)skb->data;
++ irs.iphlen = ipp->ihl << 2;
++ ipp->tot_len = htons(ntohs(ipp->tot_len) - irs.natt_len);
++ if (skb->len < irs.iphlen + irs.natt_len) {
++ printk(KERN_WARNING
++ "klips_error:ipsec_rcv: "
++ "ESPinUDP packet is too small (%d < %d+%d). "
++ "This should never happen, please report.\n",
++ (int)(skb->len), irs.iphlen, irs.natt_len);
++ goto rcvleave;
++ }
++ memmove(skb->data + irs.natt_len, skb->data, irs.iphlen);
++ skb_pull(skb, irs.natt_len);
++
++ /* update nh.iph */
++ ipp = skb->nh.iph = (struct iphdr *)skb->data;
++
++ /* modify protocol */
++ ipp->protocol = IPPROTO_ESP;
++
++ skb->sk = NULL;
++
++ KLIPS_IP_PRINT(debug_rcv, skb->nh.iph);
++ }
++#endif
++
++ ipp = skb->nh.iph;
++ ipsaddr.s_addr = ipp->saddr;
++ addrtoa(ipsaddr, 0, irs.ipsaddr_txt, sizeof(irs.ipsaddr_txt));
++ ipdaddr.s_addr = ipp->daddr;
++ addrtoa(ipdaddr, 0, irs.ipdaddr_txt, sizeof(irs.ipdaddr_txt));
++ irs.iphlen = ipp->ihl << 2;
++
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "<<< Info -- ");
++ KLIPS_PRINTMORE(debug_rcv && skb->dev, "skb->dev=%s ",
++ skb->dev->name ? skb->dev->name : "NULL");
++ KLIPS_PRINTMORE(debug_rcv && dev, "dev=%s ",
++ dev->name ? dev->name : "NULL");
++ KLIPS_PRINTMORE(debug_rcv, "\n");
++
++ KLIPS_PRINT(debug_rcv && !(skb->dev && dev && (skb->dev == dev)),
++ "klips_debug:ipsec_rcv: "
++ "Informational -- **if this happens, find out why** skb->dev:%s is not equal to dev:%s\n",
++ skb->dev ? (skb->dev->name ? skb->dev->name : "NULL") : "NULL",
++ dev ? (dev->name ? dev->name : "NULL") : "NULL");
++
++ protoc = ipp->protocol;
++#ifndef NET_21
++ if((!protocol) || (protocol->protocol != protoc)) {
++ KLIPS_PRINT(debug_rcv & DB_RX_IPSA,
++ "klips_debug:ipsec_rcv: "
++ "protocol arg is NULL or unequal to the packet contents, this is odd, using value in packet.\n");
++ }
++#endif /* !NET_21 */
++
++ if( (protoc != IPPROTO_AH) &&
++#ifdef CONFIG_IPSEC_IPCOMP_disabled_until_we_register_IPCOMP_HANDLER
++ (protoc != IPPROTO_COMP) &&
++#endif /* CONFIG_IPSEC_IPCOMP */
++ (protoc != IPPROTO_ESP) ) {
++ KLIPS_PRINT(debug_rcv & DB_RX_IPSA,
++ "klips_debug:ipsec_rcv: Why the hell is someone "
++ "passing me a non-ipsec protocol = %d packet? -- dropped.\n",
++ protoc);
++ goto rcvleave;
++ }
++
++ if(skb->dev) {
++ for(i = 0; i < IPSEC_NUM_IF; i++) {
++ sprintf(name, IPSEC_DEV_FORMAT, i);
++ if(!strcmp(name, skb->dev->name)) {
++ prv = (struct ipsecpriv *)(skb->dev->priv);
++ if(prv) {
++ stats = (struct net_device_stats *) &(prv->mystats);
++ }
++ ipsecdev = skb->dev;
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "Info -- pkt already proc'ed a group of ipsec headers, processing next group of ipsec headers.\n");
++ break;
++ }
++ if((ipsecdev = __ipsec_dev_get(name)) == NULL) {
++ KLIPS_PRINT(debug_rcv,
++ "klips_error:ipsec_rcv: "
++ "device %s does not exist\n",
++ name);
++ }
++ prv = ipsecdev ? (struct ipsecpriv *)(ipsecdev->priv) : NULL;
++ prvdev = prv ? (struct device *)(prv->dev) : NULL;
++
++#if 0
++ KLIPS_PRINT(debug_rcv && prvdev,
++ "klips_debug:ipsec_rcv: "
++ "physical device for device %s is %s\n",
++ name,
++ prvdev->name);
++#endif
++ if(prvdev && skb->dev &&
++ !strcmp(prvdev->name, skb->dev->name)) {
++ stats = prv ? ((struct net_device_stats *) &(prv->mystats)) : NULL;
++ skb->dev = ipsecdev;
++ KLIPS_PRINT(debug_rcv && prvdev,
++ "klips_debug:ipsec_rcv: "
++ "assigning packet ownership to virtual device %s from physical device %s.\n",
++ name, prvdev->name);
++ if(stats) {
++ stats->rx_packets++;
++ }
++ break;
++ }
++ }
++ } else {
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "device supplied with skb is NULL\n");
++ }
++
++ if(stats == NULL) {
++ KLIPS_PRINT((debug_rcv),
++ "klips_error:ipsec_rcv: "
++ "packet received from physical I/F (%s) not connected to ipsec I/F. Cannot record stats. May not have SA for decoding. Is IPSEC traffic expected on this I/F? Check routing.\n",
++ skb->dev ? (skb->dev->name ? skb->dev->name : "NULL") : "NULL");
++ }
++
++ KLIPS_IP_PRINT(debug_rcv, ipp);
++
++ /* begin decapsulating loop here */
++
++ /*
++ The spinlock is to prevent any other process from
++ accessing or deleting the ipsec_sa hash table or any of the
++ ipsec_sa s while we are using and updating them.
++
++ This is not optimal, but was relatively straightforward
++ at the time. A better way to do it has been planned for
++ more than a year, to lock the hash table and put reference
++ counts on each ipsec_sa instead. This is not likely to happen
++ in KLIPS1 unless a volunteer contributes it, but will be
++ designed into KLIPS2.
++ */
++ spin_lock(&tdb_lock);
++
++ /* set up for decap loop */
++ irs.stats= stats;
++ irs.ipp = ipp;
++ irs.ipsp = NULL;
++ irs.ilen = 0;
++ irs.authlen=0;
++ irs.authfuncs=NULL;
++ irs.skb = skb;
++
++ do {
++ int decap_stat;
++ struct xform_functions *proto_funcs;
++
++ switch(irs.ipp->protocol) {
++ case IPPROTO_ESP:
++ proto_funcs = esp_xform_funcs;
++ break;
++
++#ifdef CONFIG_IPSEC_AH
++ case IPPROTO_AH:
++ proto_funcs = ah_xform_funcs;
++ break;
++#endif /* !CONFIG_IPSEC_AH */
++
++#ifdef CONFIG_IPSEC_IPCOMP
++ case IPPROTO_COMP:
++ proto_funcs = ipcomp_xform_funcs;
++ break;
++#endif /* !CONFIG_IPSEC_IPCOMP */
++ default:
++ if(irs.stats) {
++ irs.stats->rx_errors++;
++ }
++ decap_stat = IPSEC_RCV_BADPROTO;
++ goto rcvleave;
++ }
++
++ decap_stat = ipsec_rcv_decap_once(&irs, proto_funcs);
++
++ if(decap_stat != IPSEC_RCV_OK) {
++ spin_unlock(&tdb_lock);
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: decap_once failed: %d\n",
++ decap_stat);
++
++ goto rcvleave;
++ }
++ /* end decapsulation loop here */
++ } while( (irs.ipp->protocol == IPPROTO_ESP )
++ || (irs.ipp->protocol == IPPROTO_AH )
++#ifdef CONFIG_IPSEC_IPCOMP
++ || (irs.ipp->protocol == IPPROTO_COMP)
++#endif /* CONFIG_IPSEC_IPCOMP */
++ );
++
++ /* set up for decap loop */
++ ipp =irs.ipp;
++ ipsp =irs.ipsp;
++ ipsnext = ipsp->ips_inext;
++ skb = irs.skb;
++
++ /* if there is an IPCOMP, but we don't have an IPPROTO_COMP,
++ * then we can just skip it
++ */
++#ifdef CONFIG_IPSEC_IPCOMP
++ if(ipsnext && ipsnext->ips_said.proto == IPPROTO_COMP) {
++ ipsp = ipsnext;
++ ipsnext = ipsp->ips_inext;
++ }
++#endif /* CONFIG_IPSEC_IPCOMP */
++
++#ifdef CONFIG_IPSEC_NAT_TRAVERSAL
++ if ((irs.natt_type) && (ipp->protocol != IPPROTO_IPIP)) {
++ /**
++ * NAT-Traversal and Transport Mode:
++ * we need to correct TCP/UDP checksum
++ *
++ * If we've got NAT-OA, we can fix checksum without recalculation.
++ */
++ __u32 natt_oa = ipsp->ips_natt_oa ?
++ ((struct sockaddr_in*)(ipsp->ips_natt_oa))->sin_addr.s_addr : 0;
++ __u16 pkt_len = skb->tail - (unsigned char *)ipp;
++ __u16 data_len = pkt_len - (ipp->ihl << 2);
++
++ switch (ipp->protocol) {
++ case IPPROTO_TCP:
++ if (data_len >= sizeof(struct tcphdr)) {
++ struct tcphdr *tcp = (struct tcphdr *)((__u32 *)ipp+ipp->ihl);
++ if (natt_oa) {
++ __u32 buff[2] = { ~natt_oa, ipp->saddr };
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "NAT-T & TRANSPORT: "
++ "fix TCP checksum using NAT-OA\n");
++ tcp->check = csum_fold(
++ csum_partial((unsigned char *)buff, sizeof(buff),
++ tcp->check^0xffff));
++ }
++ else {
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "NAT-T & TRANSPORT: recalc TCP checksum\n");
++ if (pkt_len > (ntohs(ipp->tot_len)))
++ data_len -= (pkt_len - ntohs(ipp->tot_len));
++ tcp->check = 0;
++ tcp->check = csum_tcpudp_magic(ipp->saddr, ipp->daddr,
++ data_len, IPPROTO_TCP,
++ csum_partial((unsigned char *)tcp, data_len, 0));
++ }
++ }
++ else {
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "NAT-T & TRANSPORT: can't fix TCP checksum\n");
++ }
++ break;
++ case IPPROTO_UDP:
++ if (data_len >= sizeof(struct udphdr)) {
++ struct udphdr *udp = (struct udphdr *)((__u32 *)ipp+ipp->ihl);
++ if (udp->check == 0) {
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "NAT-T & TRANSPORT: UDP checksum already 0\n");
++ }
++ else if (natt_oa) {
++ __u32 buff[2] = { ~natt_oa, ipp->saddr };
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "NAT-T & TRANSPORT: "
++ "fix UDP checksum using NAT-OA\n");
++ udp->check = csum_fold(
++ csum_partial((unsigned char *)buff, sizeof(buff),
++ udp->check^0xffff));
++ }
++ else {
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "NAT-T & TRANSPORT: zero UDP checksum\n");
++ udp->check = 0;
++ }
++ }
++ else {
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "NAT-T & TRANSPORT: can't fix UDP checksum\n");
++ }
++ break;
++ default:
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "NAT-T & TRANSPORT: non TCP/UDP packet -- do nothing\n");
++ break;
++ }
++ }
++#endif
++
++ /*
++ * XXX this needs to be locked from when it was first looked
++ * up in the decapsulation loop. Perhaps it is better to put
++ * the IPIP decap inside the loop.
++ */
++ if(ipsnext) {
++ ipsp = ipsnext;
++ irs.sa_len = satot(&irs.said, 0, irs.sa, sizeof(irs.sa));
++ if((ipp->protocol != IPPROTO_IPIP) &&
++ ( 0xFE != ipp->protocol)) { /* added to support AT&T heartbeats to SIG/GIG */
++ spin_unlock(&tdb_lock);
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "SA:%s, Hey! How did this get through? Dropped.\n",
++ irs.sa_len ? irs.sa : " (error)");
++ if(stats) {
++ stats->rx_dropped++;
++ }
++ goto rcvleave;
++ }
++ if(sysctl_ipsec_inbound_policy_check) {
++ if((ipsnext = ipsp->ips_inext)) {
++ char sa2[SATOT_BUF];
++ size_t sa_len2;
++ sa_len2 = satot(&ipsnext->ips_said, 0, sa2, sizeof(sa2));
++ spin_unlock(&tdb_lock);
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "unexpected SA:%s after IPIP SA:%s\n",
++ sa_len2 ? sa2 : " (error)",
++ irs.sa_len ? irs.sa : " (error)");
++ if(stats) {
++ stats->rx_dropped++;
++ }
++ goto rcvleave;
++ }
++ if(ipp->saddr != ((struct sockaddr_in*)(ipsp->ips_addr_s))->sin_addr.s_addr) {
++ spin_unlock(&tdb_lock);
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "SA:%s, src=%s of pkt does not agree with expected SA source address policy.\n",
++ irs.sa_len ? irs.sa : " (error)",
++ irs.ipsaddr_txt);
++ if(stats) {
++ stats->rx_dropped++;
++ }
++ goto rcvleave;
++ }
++ }
++
++ if(ipp->protocol == IPPROTO_IPIP) /* added to support AT&T heartbeats to SIG/GIG */
++ {
++ /*
++ * XXX this needs to be locked from when it was first looked
++ * up in the decapsulation loop. Perhaps it is better to put
++ * the IPIP decap inside the loop.
++ */
++ ipsp->ips_life.ipl_bytes.ipl_count += skb->len;
++ ipsp->ips_life.ipl_bytes.ipl_last = skb->len;
++
++ if(!ipsp->ips_life.ipl_usetime.ipl_count) {
++ ipsp->ips_life.ipl_usetime.ipl_count = jiffies / HZ;
++ }
++ ipsp->ips_life.ipl_usetime.ipl_last = jiffies / HZ;
++ ipsp->ips_life.ipl_packets.ipl_count += 1;
++
++ if(skb->len < irs.iphlen) {
++ spin_unlock(&tdb_lock);
++ printk(KERN_WARNING "klips_debug:ipsec_rcv: "
++ "tried to skb_pull iphlen=%d, %d available. This should never happen, please report.\n",
++ irs.iphlen,
++ (int)(skb->len));
++
++ goto rcvleave;
++ }
++ skb_pull(skb, irs.iphlen);
++
++#ifdef NET_21
++ ipp = (struct iphdr *)skb->nh.raw = skb->data;
++ skb->h.raw = skb->nh.raw + (skb->nh.iph->ihl << 2);
++
++ memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options));
++#else /* NET_21 */
++ ipp = skb->ip_hdr = skb->h.iph = (struct iphdr *)skb->data;
++
++ memset(skb->proto_priv, 0, sizeof(struct options));
++#endif /* NET_21 */
++ ipsaddr.s_addr = ipp->saddr;
++ addrtoa(ipsaddr, 0, irs.ipsaddr_txt, sizeof(irs.ipsaddr_txt));
++ ipdaddr.s_addr = ipp->daddr;
++ addrtoa(ipdaddr, 0, irs.ipdaddr_txt, sizeof(irs.ipdaddr_txt));
++
++ skb->protocol = htons(ETH_P_IP);
++ skb->ip_summed = 0;
++ KLIPS_PRINT(debug_rcv & DB_RX_PKTRX,
++ "klips_debug:ipsec_rcv: "
++ "IPIP tunnel stripped.\n");
++ KLIPS_IP_PRINT(debug_rcv & DB_RX_PKTRX, ipp);
++ }
++
++ if(sysctl_ipsec_inbound_policy_check
++ /*
++ Note: "xor" (^) logically replaces "not equal"
++ (!=) and "bitwise or" (|) logically replaces
++ "boolean or" (||). This is done to speed up
++ execution by doing only bitwise operations and
++ no branch operations
++ */
++ && (((ipp->saddr & ipsp->ips_mask_s.u.v4.sin_addr.s_addr)
++ ^ ipsp->ips_flow_s.u.v4.sin_addr.s_addr)
++ | ((ipp->daddr & ipsp->ips_mask_d.u.v4.sin_addr.s_addr)
++ ^ ipsp->ips_flow_d.u.v4.sin_addr.s_addr)) )
++ {
++ char sflow_txt[SUBNETTOA_BUF], dflow_txt[SUBNETTOA_BUF];
++
++ subnettoa(ipsp->ips_flow_s.u.v4.sin_addr,
++ ipsp->ips_mask_s.u.v4.sin_addr,
++ 0, sflow_txt, sizeof(sflow_txt));
++ subnettoa(ipsp->ips_flow_d.u.v4.sin_addr,
++ ipsp->ips_mask_d.u.v4.sin_addr,
++ 0, dflow_txt, sizeof(dflow_txt));
++ spin_unlock(&tdb_lock);
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "SA:%s, inner tunnel policy [%s -> %s] does not agree with pkt contents [%s -> %s].\n",
++ irs.sa_len ? irs.sa : " (error)",
++ sflow_txt,
++ dflow_txt,
++ irs.ipsaddr_txt,
++ irs.ipdaddr_txt);
++ if(stats) {
++ stats->rx_dropped++;
++ }
++ goto rcvleave;
++ }
++#ifdef CONFIG_NETFILTER
++ skb->nfmark = (skb->nfmark & (~(IPsecSAref2NFmark(IPSEC_SA_REF_TABLE_MASK))))
++ | IPsecSAref2NFmark(IPsecSA2SAref(ipsp));
++ KLIPS_PRINT(debug_rcv & DB_RX_PKTRX,
++ "klips_debug:ipsec_rcv: "
++ "IPIP SA sets skb->nfmark=0x%x.\n",
++ (unsigned)skb->nfmark);
++#endif /* CONFIG_NETFILTER */
++ }
++
++ spin_unlock(&tdb_lock);
++
++#ifdef NET_21
++ if(stats) {
++ stats->rx_bytes += skb->len;
++ }
++ if(skb->dst) {
++ dst_release(skb->dst);
++ skb->dst = NULL;
++ }
++ skb->pkt_type = PACKET_HOST;
++ if(irs.hard_header_len &&
++ (skb->mac.raw != (skb->data - irs.hard_header_len)) &&
++ (irs.hard_header_len <= skb_headroom(skb))) {
++ /* copy back original MAC header */
++ memmove(skb->data - irs.hard_header_len, skb->mac.raw, irs.hard_header_len);
++ skb->mac.raw = skb->data - irs.hard_header_len;
++ }
++#endif /* NET_21 */
++
++#ifdef CONFIG_IPSEC_IPCOMP
++ if(ipp->protocol == IPPROTO_COMP) {
++ unsigned int flags = 0;
++
++ if(sysctl_ipsec_inbound_policy_check) {
++ KLIPS_PRINT(debug_rcv & DB_RX_PKTRX,
++ "klips_debug:ipsec_rcv: "
++ "inbound policy checking enabled, IPCOMP follows IPIP, dropped.\n");
++ if (stats) {
++ stats->rx_errors++;
++ }
++ goto rcvleave;
++ }
++ /*
++ XXX need a ipsec_sa for updating ratio counters but it is not
++ following policy anyways so it is not a priority
++ */
++ skb = skb_decompress(skb, NULL, &flags);
++ if (!skb || flags) {
++ KLIPS_PRINT(debug_rcv & DB_RX_PKTRX,
++ "klips_debug:ipsec_rcv: "
++ "skb_decompress() returned error flags: %d, dropped.\n",
++ flags);
++ if (stats) {
++ stats->rx_errors++;
++ }
++ goto rcvleave;
++ }
++ }
++#endif /* CONFIG_IPSEC_IPCOMP */
++
++#ifdef SKB_RESET_NFCT
++ nf_conntrack_put(skb->nfct);
++ skb->nfct = NULL;
++#ifdef CONFIG_NETFILTER_DEBUG
++ skb->nf_debug = 0;
++#endif /* CONFIG_NETFILTER_DEBUG */
++#endif /* SKB_RESET_NFCT */
++ KLIPS_PRINT(debug_rcv & DB_RX_PKTRX,
++ "klips_debug:ipsec_rcv: "
++ "netif_rx() called.\n");
++ netif_rx(skb);
++
++ MOD_DEC_USE_COUNT;
++ return(0);
++
++ rcvleave:
++ if(skb) {
++ ipsec_kfree_skb(skb);
++ }
++
++ MOD_DEC_USE_COUNT;
++ return(0);
++}
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.143.4.2 2004/08/22 03:29:06 mcr
++ * include udp.h regardless of nat-t support.
++ *
++ * Revision 1.143.4.1 2004/08/21 02:14:58 ken
++ * Patch from Jochen Eisinger for AT&T MTS Heartbeat packet support
++ *
++ * Revision 1.143 2004/05/10 22:27:00 mcr
++ * fix for ESP-3DES-noauth test case.
++ *
++ * Revision 1.142 2004/05/10 22:25:57 mcr
++ * reformat of calls to ipsec_lifetime_check().
++ *
++ * Revision 1.141 2004/04/06 02:49:26 mcr
++ * pullup of algo code from alg-branch.
++ *
++ * Revision 1.140 2004/02/03 03:12:53 mcr
++ * removed erroneously, double patched code.
++ *
++ * Revision 1.139 2004/01/05 23:21:29 mcr
++ * initialize sin_family in ipsec_rcv.c
++ *
++ * Revision 1.138 2003/12/24 19:46:52 mcr
++ * if sock.h patch has not been applied, then define appropriate
++ * structure so we can use it. This is serious inferior, and
++ * depends upon the concept that the structure in question is
++ * smaller than the other members of that union.
++ * getting rid of differing methods is a better solution.
++ *
++ * Revision 1.137 2003/12/22 19:40:57 mcr
++ * NAT-T patches 0.6c.
++ *
++ * Revision 1.136 2003/12/15 18:13:12 mcr
++ * when compiling with NAT traversal, don't assume that the
++ * kernel has been patched, unless CONFIG_IPSEC_NAT_NON_ESP
++ * is set.
++ *
++ * Revision 1.135 2003/12/13 19:10:21 mcr
++ * refactored rcv and xmit code - same as FS 2.05.
++ *
++ * Revision 1.134.2.1 2003/12/22 15:25:52 jjo
++ * Merged algo-0.8.1-rc11-test1 into alg-branch
++ *
++ * Revision 1.134 2003/12/10 01:14:27 mcr
++ * NAT-traversal patches to KLIPS.
++ *
++ * Revision 1.133 2003/10/31 02:27:55 mcr
++ * pulled up port-selector patches and sa_id elimination.
++ *
++ * Revision 1.132.2.1 2003/10/29 01:30:41 mcr
++ * elimited "struct sa_id".
++ *
++ * Revision 1.132 2003/09/02 19:51:48 mcr
++ * fixes for PR#252.
++ *
++ * Revision 1.131 2003/07/31 22:47:16 mcr
++ * preliminary (untested by FS-team) 2.5 patches.
++ *
++ * Revision 1.130 2003/04/03 17:38:25 rgb
++ * Centralised ipsec_kfree_skb and ipsec_dev_{get,put}.
++ * Clarified logic for non-connected devices.
++ *
++ * Revision 1.129 2003/02/06 02:21:34 rgb
++ *
++ * Moved "struct auth_alg" from ipsec_rcv.c to ipsec_ah.h .
++ * Changed "struct ah" to "struct ahhdr" and "struct esp" to "struct esphdr".
++ * Removed "#ifdef INBOUND_POLICY_CHECK_eroute" dead code.
++ *
++ * Revision 1.128 2002/12/13 20:58:03 rgb
++ * Relegated MCR's recent "_dmp" routine to debug_verbose.
++ * Cleaned up printing of source and destination addresses in debug output.
++ *
++ * Revision 1.127 2002/12/04 16:00:16 rgb
++ *
++ * Fixed AH decapsulation pointer update bug and added some comments and
++ * debugging.
++ * This bug was caught by west-ah-0[12].
++ *
++ * Revision 1.126 2002/11/04 05:03:43 mcr
++ * fixes for IPCOMP. There were two problems:
++ * 1) the irs->ipp pointer was not being updated properly after
++ * the ESP descryption. The meant nothing for IPIP, as the
++ * later IP header overwrote the earlier one.
++ * 2) the more serious problem was that skb_decompress will
++ * usually allocate a new SKB, so we have to make sure that
++ * it doesn't get lost.
++ * #2 meant removing the skb argument from the ->decrypt routine
++ * and moving it to the irs->skb, so it could be value/result.
++ *
++ * Revision 1.125 2002/11/01 01:53:35 dhr
++ *
++ * fix typo
++ *
++ * Revision 1.124 2002/10/31 22:49:01 dhr
++ *
++ * - eliminate unused variable "hash"
++ * - reduce scope of variable "authenticator"
++ * - add comment on a couple of tricky bits
++ *
++ * Revision 1.123 2002/10/31 22:39:56 dhr
++ *
++ * use correct type for result of function calls
++ *
++ * Revision 1.122 2002/10/31 22:36:25 dhr
++ *
++ * simplify complex test
++ *
++ * Revision 1.121 2002/10/31 22:34:04 dhr
++ *
++ * ipsprev is never used: ditch it
++ *
++ * Revision 1.120 2002/10/31 22:30:21 dhr
++ *
++ * eliminate redundant assignments
++ *
++ * Revision 1.119 2002/10/31 22:27:43 dhr
++ *
++ * make whitespace canonical
++ *
++ * Revision 1.118 2002/10/30 05:47:17 rgb
++ * Fixed cut-and-paste error mis-identifying comp runt as ah.
++ *
++ * Revision 1.117 2002/10/17 16:37:45 rgb
++ * Remove compp intermediate variable and in-line its contents
++ * where used
++ *
++ * Revision 1.116 2002/10/12 23:11:53 dhr
++ *
++ * [KenB + DHR] more 64-bit cleanup
++ *
++ * Revision 1.115 2002/10/07 19:06:58 rgb
++ * Minor fixups and activation to west-rcv-nfmark-set-01 test to check for SA reference properly set on incoming.
++ *
++ * Revision 1.114 2002/10/07 18:31:31 rgb
++ * Set saref on incoming packets.
++ *
++ * Revision 1.113 2002/09/16 21:28:12 mcr
++ * adjust hash length for HMAC calculation - must look at whether
++ * it is MD5 or SHA1.
++ *
++ * Revision 1.112 2002/09/16 21:19:15 mcr
++ * fixes for west-ah-icmp-01 - length of AH header must be
++ * calculated properly, and next_header field properly copied.
++ *
++ * Revision 1.111 2002/09/10 02:45:56 mcr
++ * re-factored the ipsec_rcv function into several functions,
++ * ipsec_rcv_decap_once, and a set of functions for AH, ESP and IPCOMP.
++ * In addition, the MD5 and SHA1 functions are replaced with pointers.
++ *
++ * Revision 1.110 2002/08/30 06:34:33 rgb
++ * Fix scope of shift in AH header length check.
++ *
++ * Revision 1.109 2002/08/27 16:49:20 rgb
++ * Fixed ESP short packet DOS (and AH and IPCOMP).
++ *
++ * Revision 1.108 2002/07/24 18:44:54 rgb
++ * Type fiddling to tame ia64 compiler.
++ *
++ * Revision 1.107 2002/05/27 18:58:18 rgb
++ * Convert to dynamic ipsec device allocation.
++ * Remove final vistiges of tdb references via IPSEC_KLIPS1_COMPAT.
++ *
++ * Revision 1.106 2002/05/23 07:15:21 rgb
++ * Pointer clean-up.
++ * Added refcount code.
++ *
++ * Revision 1.105 2002/05/14 02:35:06 rgb
++ * Change all references to tdb, TDB or Tunnel Descriptor Block to ips,
++ * ipsec_sa or ipsec_sa.
++ * Change references to _TDB to _IPSA.
++ *
++ * Revision 1.104 2002/04/24 07:55:32 mcr
++ * #include patches and Makefiles for post-reorg compilation.
++ *
++ * Revision 1.103 2002/04/24 07:36:30 mcr
++ * Moved from ./klips/net/ipsec/ipsec_rcv.c,v
++ *
++ * Revision 1.102 2002/01/29 17:17:56 mcr
++ * moved include of ipsec_param.h to after include of linux/kernel.h
++ * otherwise, it seems that some option that is set in ipsec_param.h
++ * screws up something subtle in the include path to kernel.h, and
++ * it complains on the snprintf() prototype.
++ *
++ * Revision 1.101 2002/01/29 04:00:52 mcr
++ * more excise of kversions.h header.
++ *
++ * Revision 1.100 2002/01/29 02:13:17 mcr
++ * introduction of ipsec_kversion.h means that include of
++ * ipsec_param.h must preceed any decisions about what files to
++ * include to deal with differences in kernel source.
++ *
++ * Revision 1.99 2002/01/28 21:40:59 mcr
++ * should use #if to test boolean option rather than #ifdef.
++ *
++ * Revision 1.98 2002/01/20 20:19:36 mcr
++ * renamed option to IP_FRAGMENT_LINEARIZE.
++ *
++ * Revision 1.97 2002/01/12 02:55:36 mcr
++ * fix for post-2.4.4 to linearize skb's when ESP packet
++ * was assembled from fragments.
++ *
++ * Revision 1.96 2001/11/26 09:23:49 rgb
++ * Merge MCR's ipsec_sa, eroute, proc and struct lifetime changes.
++ *
++ * Revision 1.93.2.2 2001/10/22 20:54:07 mcr
++ * include des.h, removed phony prototypes and fixed calling
++ * conventions to match real prototypes.
++ *
++ * Revision 1.93.2.1 2001/09/25 02:22:22 mcr
++ * struct tdb -> struct ipsec_sa.
++ * lifetime checks moved to ipsec_life.c
++ * some sa(tdb) manipulation functions renamed.
++ *
++ * Revision 1.95 2001/11/06 19:49:07 rgb
++ * Added variable descriptions.
++ * Removed unauthenticated sequence==0 check to prevent DoS.
++ *
++ * Revision 1.94 2001/10/18 04:45:20 rgb
++ * 2.4.9 kernel deprecates linux/malloc.h in favour of linux/slab.h,
++ * lib/freeswan.h version macros moved to lib/kversions.h.
++ * Other compiler directive cleanups.
++ *
++ * Revision 1.93 2001/09/07 22:17:24 rgb
++ * Fix for removal of transport layer protocol handler arg in 2.4.4.
++ * Fix to accomodate peer non-conformance to IPCOMP rfc2393.
++ *
++ * Revision 1.92 2001/08/27 19:44:41 rgb
++ * Fix error in comment.
++ *
++ * Revision 1.91 2001/07/20 19:31:48 dhr
++ * [DHR] fix source and destination subnets of policy in diagnostic
++ *
++ * Revision 1.90 2001/07/06 19:51:09 rgb
++ * Added inbound policy checking code for IPIP SAs.
++ * Renamed unused function argument for ease and intuitive naming.
++ *
++ * Revision 1.89 2001/06/22 19:35:23 rgb
++ * Disable ipcomp processing if we are handed a ipcomp packet with no esp
++ * or ah header.
++ * Print protocol if we are handed a non-ipsec packet.
++ *
++ * Revision 1.88 2001/06/20 06:30:47 rgb
++ * Fixed transport mode IPCOMP policy check bug.
++ *
++ * Revision 1.87 2001/06/13 20:58:40 rgb
++ * Added parentheses around assignment used as truth value to silence
++ * compiler.
++ *
++ * Revision 1.86 2001/06/07 22:25:23 rgb
++ * Added a source address policy check for tunnel mode. It still does
++ * not check client addresses and masks.
++ * Only decapsulate IPIP if it is expected.
++ *
++ * Revision 1.85 2001/05/30 08:14:02 rgb
++ * Removed vestiges of esp-null transforms.
++ *
++ * Revision 1.84 2001/05/27 06:12:11 rgb
++ * Added structures for pid, packet count and last access time to eroute.
++ * Added packet count to beginning of /proc/net/ipsec_eroute.
++ *
++ * Revision 1.83 2001/05/04 16:45:47 rgb
++ * Remove unneeded code. ipp is not used after this point.
++ *
++ * Revision 1.82 2001/05/04 16:36:00 rgb
++ * Fix skb_cow() call for 2.4.4. (SS)
++ *
++ * Revision 1.81 2001/05/02 14:46:53 rgb
++ * Fix typo for compiler directive to pull IPH back.
++ *
++ * Revision 1.80 2001/04/30 19:46:34 rgb
++ * Update for 2.4.4. We now receive the skb with skb->data pointing to
++ * h.raw.
++ *
++ * Revision 1.79 2001/04/23 15:01:15 rgb
++ * Added spin_lock() check to prevent double-locking for multiple
++ * transforms and hence kernel lock-ups with SMP kernels.
++ * Minor spin_unlock() adjustments to unlock before non-dependant prints
++ * and IPSEC device stats updates.
++ *
++ * Revision 1.78 2001/04/21 23:04:24 rgb
++ * Check if soft expire has already been sent before sending another to
++ * prevent ACQUIRE flooding.
++ *
++ * Revision 1.77 2001/03/16 07:35:20 rgb
++ * Ditch extra #if 1 around now permanent policy checking code.
++ *
++ * Revision 1.76 2001/02/27 22:24:54 rgb
++ * Re-formatting debug output (line-splitting, joining, 1arg/line).
++ * Check for satoa() return codes.
++ *
++ * Revision 1.75 2001/02/19 22:28:30 rgb
++ * Minor change to virtual device discovery code to assert which I/F has
++ * been found.
++ *
++ * Revision 1.74 2000/11/25 03:50:36 rgb
++ * Oops fix by minor re-arrangement of code to avoid accessing a freed tdb.
++ *
++ * Revision 1.73 2000/11/09 20:52:15 rgb
++ * More spinlock shuffling, locking earlier and unlocking later in rcv to
++ * include ipcomp and prevent races, renaming some tdb variables that got
++ * forgotten, moving some unlocks to include tdbs and adding a missing
++ * unlock. Thanks to Svenning for some of these.
++ *
++ * Revision 1.72 2000/11/09 20:11:22 rgb
++ * Minor shuffles to fix non-standard kernel config option selection.
++ *
++ * Revision 1.71 2000/11/06 04:36:18 rgb
++ * Ditched spin_lock_irqsave in favour of spin_lock.
++ * Minor initial protocol check rewrite.
++ * Clean up debug printing.
++ * Clean up tdb handling on ipcomp.
++ * Fixed transport mode null pointer de-reference without ipcomp.
++ * Add Svenning's adaptive content compression.
++ * Disabled registration of ipcomp handler.
++ *
++ * Revision 1.70 2000/10/30 23:41:43 henry
++ * Hans-Joerg Hoexer's null-pointer fix
++ *
++ * Revision 1.69 2000/10/10 18:54:16 rgb
++ * Added a fix for incoming policy check with ipcomp enabled but
++ * uncompressible.
++ *
++ * Revision 1.68 2000/09/22 17:53:12 rgb
++ * Fixed ipcomp tdb pointers update for policy checking.
++ *
++ * Revision 1.67 2000/09/21 03:40:58 rgb
++ * Added more debugging to try and track down the cpi outward copy problem.
++ *
++ * Revision 1.66 2000/09/20 04:00:10 rgb
++ * Changed static functions to DEBUG_NO_STATIC to reveal function names for
++ * debugging oopsen.
++ *
++ * Revision 1.65 2000/09/19 07:07:16 rgb
++ * Added debugging to inbound policy check for ipcomp.
++ * Added missing spin_unlocks (thanks Svenning!).
++ * Fixed misplaced tdbnext pointers causing mismatched ipip policy check.
++ * Protect ipcomp policy check following ipip decap with sysctl switch.
++ *
++ * Revision 1.64 2000/09/18 21:27:29 rgb
++ * 2.0 fixes.
++ *
++ * Revision 1.63 2000/09/18 02:35:50 rgb
++ * Added policy checking to ipcomp and re-enabled policy checking by
++ * default.
++ * Optimised satoa calls.
++ *
++ * Revision 1.62 2000/09/17 21:02:32 rgb
++ * Clean up debugging, removing slow timestamp debug code.
++ *
++ * Revision 1.61 2000/09/16 01:07:55 rgb
++ * Fixed erroneous ref from struct ipcomp to struct ipcomphdr.
++ *
++ * Revision 1.60 2000/09/15 11:37:01 rgb
++ * Merge in heavily modified Svenning Soerensen's <svenning@post5.tele.dk>
++ * IPCOMP zlib deflate code.
++ *
++ * Revision 1.59 2000/09/15 04:56:20 rgb
++ * Remove redundant satoa() call, reformat comment.
++ *
++ * Revision 1.58 2000/09/13 08:00:52 rgb
++ * Flick on inbound policy checking.
++ *
++ * Revision 1.57 2000/09/12 03:22:19 rgb
++ * Converted inbound_policy_check to sysctl.
++ * Re-enabled policy backcheck.
++ * Moved policy checks to top and within tdb lock.
++ *
++ * Revision 1.56 2000/09/08 19:12:56 rgb
++ * Change references from DEBUG_IPSEC to CONFIG_IPSEC_DEBUG.
++ *
++ * Revision 1.55 2000/08/28 18:15:46 rgb
++ * Added MB's nf-debug reset patch.
++ *
++ * Revision 1.54 2000/08/27 01:41:26 rgb
++ * More minor tweaks to the bad padding debug code.
++ *
++ * Revision 1.53 2000/08/24 16:54:16 rgb
++ * Added KLIPS_PRINTMORE macro to continue lines without KERN_INFO level
++ * info.
++ * Tidied up device reporting at the start of ipsec_rcv.
++ * Tidied up bad padding debugging and processing.
++ *
++ * Revision 1.52 2000/08/20 21:36:03 rgb
++ * Activated pfkey_expire() calls.
++ * Added a hard/soft expiry parameter to pfkey_expire().
++ * Added sanity checking to avoid propagating zero or smaller-length skbs
++ * from a bogus decryption.
++ * Re-arranged the order of soft and hard expiry to conform to RFC2367.
++ * Clean up references to CONFIG_IPSEC_PFKEYv2.
++ *
++ * Revision 1.51 2000/08/18 21:23:30 rgb
++ * Improve bad padding warning so that the printk buffer doesn't get
++ * trampled.
++ *
++ * Revision 1.50 2000/08/01 14:51:51 rgb
++ * Removed _all_ remaining traces of DES.
++ *
++ * Revision 1.49 2000/07/28 13:50:53 rgb
++ * Changed enet_statistics to net_device_stats and added back compatibility
++ * for pre-2.1.19.
++ *
++ * Revision 1.48 2000/05/10 19:14:40 rgb
++ * Only check usetime against soft and hard limits if the tdb has been
++ * used.
++ * Cast output of ntohl so that the broken prototype doesn't make our
++ * compile noisy.
++ *
++ * Revision 1.47 2000/05/09 17:45:43 rgb
++ * Fix replay bitmap corruption bug upon receipt of bogus packet
++ * with correct SPI. This was a DoS.
++ *
++ * Revision 1.46 2000/03/27 02:31:58 rgb
++ * Fixed authentication failure printout bug.
++ *
++ * Revision 1.45 2000/03/22 16:15:37 rgb
++ * Fixed renaming of dev_get (MB).
++ *
++ * Revision 1.44 2000/03/16 08:17:24 rgb
++ * Hardcode PF_KEYv2 support.
++ * Fixed minor bug checking AH header length.
++ *
++ * Revision 1.43 2000/03/14 12:26:59 rgb
++ * Added skb->nfct support for clearing netfilter conntrack bits (MB).
++ *
++ * Revision 1.42 2000/01/26 10:04:04 rgb
++ * Fixed inbound policy checking on transport mode bug.
++ * Fixed noisy 2.0 printk arguments.
++ *
++ * Revision 1.41 2000/01/24 20:58:02 rgb
++ * Improve debugging/reporting support for (disabled) inbound
++ * policy checking.
++ *
++ * Revision 1.40 2000/01/22 23:20:10 rgb
++ * Fixed up inboud policy checking code.
++ * Cleaned out unused crud.
++ *
++ * Revision 1.39 2000/01/21 06:15:29 rgb
++ * Added sanity checks on skb_push(), skb_pull() to prevent panics.
++ * Fixed cut-and-paste debug_tunnel to debug_rcv.
++ * Added inbound policy checking code, disabled.
++ * Simplified output code by updating ipp to post-IPIP decapsulation.
++ *
++ * elided pre-2000 comments. Use "cvs log"
++ *
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/net/ipsec/ipsec_sa.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,1383 @@
++/*
++ * Common routines for IPsec SA maintenance routines.
++ *
++ * Copyright (C) 1996, 1997 John Ioannidis.
++ * Copyright (C) 1998, 1999, 2000, 2001, 2002 Richard Guy Briggs.
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ * RCSID $Id$
++ *
++ * This is the file formerly known as "ipsec_xform.h"
++ *
++ */
++
++#include <linux/config.h>
++#include <linux/version.h>
++#include <linux/kernel.h> /* printk() */
++
++#include "openswan/ipsec_param.h"
++
++#ifdef MALLOC_SLAB
++# include <linux/slab.h> /* kmalloc() */
++#else /* MALLOC_SLAB */
++# include <linux/malloc.h> /* kmalloc() */
++#endif /* MALLOC_SLAB */
++#include <linux/vmalloc.h> /* vmalloc() */
++#include <linux/errno.h> /* error codes */
++#include <linux/types.h> /* size_t */
++#include <linux/interrupt.h> /* mark_bh */
++
++#include <linux/netdevice.h> /* struct device, and other headers */
++#include <linux/etherdevice.h> /* eth_type_trans */
++#include <linux/ip.h> /* struct iphdr */
++#include <linux/skbuff.h>
++#include <openswan.h>
++#ifdef SPINLOCK
++#ifdef SPINLOCK_23
++#include <linux/spinlock.h> /* *lock* */
++#else /* SPINLOCK_23 */
++#include <asm/spinlock.h> /* *lock* */
++#endif /* SPINLOCK_23 */
++#endif /* SPINLOCK */
++#ifdef NET_21
++#include <asm/uaccess.h>
++#include <linux/in6.h>
++#endif
++#include <asm/checksum.h>
++#include <net/ip.h>
++
++#include "openswan/radij.h"
++
++#include "openswan/ipsec_stats.h"
++#include "openswan/ipsec_life.h"
++#include "openswan/ipsec_sa.h"
++#include "openswan/ipsec_xform.h"
++
++#include "openswan/ipsec_encap.h"
++#include "openswan/ipsec_radij.h"
++#include "openswan/ipsec_xform.h"
++#include "openswan/ipsec_ipe4.h"
++#include "openswan/ipsec_ah.h"
++#include "openswan/ipsec_esp.h"
++
++#include <pfkeyv2.h>
++#include <pfkey.h>
++
++#include "openswan/ipsec_proto.h"
++#include "openswan/ipsec_alg.h"
++
++
++#ifdef CONFIG_IPSEC_DEBUG
++int debug_xform = 0;
++#endif /* CONFIG_IPSEC_DEBUG */
++
++#define SENDERR(_x) do { error = -(_x); goto errlab; } while (0)
++
++struct ipsec_sa *ipsec_sadb_hash[SADB_HASHMOD];
++#ifdef SPINLOCK
++spinlock_t tdb_lock = SPIN_LOCK_UNLOCKED;
++#else /* SPINLOCK */
++spinlock_t tdb_lock;
++#endif /* SPINLOCK */
++
++struct ipsec_sadb ipsec_sadb;
++
++#if IPSEC_SA_REF_CODE
++
++/* the sub table must be narrower (or equal) in bits than the variable type
++ in the main table to count the number of unused entries in it. */
++typedef struct {
++ int testSizeOf_refSubTable :
++ ((sizeof(IPsecRefTableUnusedCount) * 8) < IPSEC_SA_REF_SUBTABLE_IDX_WIDTH ? -1 : 1);
++} dummy;
++
++
++/* The field where the saref will be hosted in the skb must be wide enough to
++ accomodate the information it needs to store. */
++typedef struct {
++ int testSizeOf_refField :
++ (IPSEC_SA_REF_HOST_FIELD_WIDTH < IPSEC_SA_REF_TABLE_IDX_WIDTH ? -1 : 1 );
++} dummy2;
++
++
++#define IPS_HASH(said) (((said)->spi + (said)->dst.u.v4.sin_addr.s_addr + (said)->proto) % SADB_HASHMOD)
++
++
++void
++ipsec_SAtest(void)
++{
++ IPsecSAref_t SAref = 258;
++ struct ipsec_sa ips;
++ ips.ips_ref = 772;
++
++ printk("klips_debug:ipsec_SAtest: "
++ "IPSEC_SA_REF_SUBTABLE_IDX_WIDTH=%u\n"
++ "IPSEC_SA_REF_MAINTABLE_NUM_ENTRIES=%u\n"
++ "IPSEC_SA_REF_SUBTABLE_NUM_ENTRIES=%u\n"
++ "IPSEC_SA_REF_HOST_FIELD_WIDTH=%lu\n"
++ "IPSEC_SA_REF_TABLE_MASK=%x\n"
++ "IPSEC_SA_REF_ENTRY_MASK=%x\n"
++ "IPsecSAref2table(%d)=%u\n"
++ "IPsecSAref2entry(%d)=%u\n"
++ "IPsecSAref2NFmark(%d)=%u\n"
++ "IPsecSAref2SA(%d)=%p\n"
++ "IPsecSA2SAref(%p)=%d\n"
++ ,
++ IPSEC_SA_REF_SUBTABLE_IDX_WIDTH,
++ IPSEC_SA_REF_MAINTABLE_NUM_ENTRIES,
++ IPSEC_SA_REF_SUBTABLE_NUM_ENTRIES,
++ (unsigned long) IPSEC_SA_REF_HOST_FIELD_WIDTH,
++ IPSEC_SA_REF_TABLE_MASK,
++ IPSEC_SA_REF_ENTRY_MASK,
++ SAref, IPsecSAref2table(SAref),
++ SAref, IPsecSAref2entry(SAref),
++ SAref, IPsecSAref2NFmark(SAref),
++ SAref, IPsecSAref2SA(SAref),
++ (&ips), IPsecSA2SAref((&ips))
++ );
++ return;
++}
++
++int
++ipsec_SAref_recycle(void)
++{
++ int table;
++ int entry;
++ int error = 0;
++
++ ipsec_sadb.refFreeListHead = -1;
++ ipsec_sadb.refFreeListTail = -1;
++
++ if(ipsec_sadb.refFreeListCont == IPSEC_SA_REF_MAINTABLE_NUM_ENTRIES * IPSEC_SA_REF_SUBTABLE_NUM_ENTRIES) {
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_SAref_recycle: "
++ "end of table reached, continuing at start..\n");
++ ipsec_sadb.refFreeListCont = 0;
++ }
++
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_SAref_recycle: "
++ "recycling, continuing from SAref=%d (0p%p), table=%d, entry=%d.\n",
++ ipsec_sadb.refFreeListCont,
++ (ipsec_sadb.refTable[IPsecSAref2table(ipsec_sadb.refFreeListCont)] != NULL) ? IPsecSAref2SA(ipsec_sadb.refFreeListCont) : NULL,
++ IPsecSAref2table(ipsec_sadb.refFreeListCont),
++ IPsecSAref2entry(ipsec_sadb.refFreeListCont));
++
++ for(table = IPsecSAref2table(ipsec_sadb.refFreeListCont);
++ table < IPSEC_SA_REF_MAINTABLE_NUM_ENTRIES;
++ table++) {
++ if(ipsec_sadb.refTable[table] == NULL) {
++ error = ipsec_SArefSubTable_alloc(table);
++ if(error) {
++ return error;
++ }
++ }
++ for(entry = IPsecSAref2entry(ipsec_sadb.refFreeListCont);
++ entry < IPSEC_SA_REF_SUBTABLE_NUM_ENTRIES;
++ entry++) {
++ if(ipsec_sadb.refTable[table]->entry[entry] == NULL) {
++ ipsec_sadb.refFreeList[++ipsec_sadb.refFreeListTail] = IPsecSArefBuild(table, entry);
++ if(ipsec_sadb.refFreeListTail == (IPSEC_SA_REF_FREELIST_NUM_ENTRIES - 1)) {
++ ipsec_sadb.refFreeListHead = 0;
++ ipsec_sadb.refFreeListCont = ipsec_sadb.refFreeList[ipsec_sadb.refFreeListTail] + 1;
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_SAref_recycle: "
++ "SArefFreeList refilled.\n");
++ return 0;
++ }
++ }
++ }
++ }
++
++ if(ipsec_sadb.refFreeListTail == -1) {
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_SAref_recycle: "
++ "out of room in the SArefTable.\n");
++
++ return(-ENOSPC);
++ }
++
++ ipsec_sadb.refFreeListHead = 0;
++ ipsec_sadb.refFreeListCont = ipsec_sadb.refFreeList[ipsec_sadb.refFreeListTail] + 1;
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_SAref_recycle: "
++ "SArefFreeList partly refilled to %d of %d.\n",
++ ipsec_sadb.refFreeListTail,
++ IPSEC_SA_REF_FREELIST_NUM_ENTRIES);
++ return 0;
++}
++
++int
++ipsec_SArefSubTable_alloc(unsigned table)
++{
++ unsigned entry;
++ struct IPsecSArefSubTable* SArefsub;
++
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_SArefSubTable_alloc: "
++ "allocating %lu bytes for table %u of %u.\n",
++ (unsigned long) (IPSEC_SA_REF_SUBTABLE_NUM_ENTRIES * sizeof(struct ipsec_sa *)),
++ table,
++ IPSEC_SA_REF_MAINTABLE_NUM_ENTRIES);
++
++ /* allocate another sub-table */
++ SArefsub = vmalloc(IPSEC_SA_REF_SUBTABLE_NUM_ENTRIES * sizeof(struct ipsec_sa *));
++ if(SArefsub == NULL) {
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_SArefSubTable_alloc: "
++ "error allocating memory for table %u of %u!\n",
++ table,
++ IPSEC_SA_REF_MAINTABLE_NUM_ENTRIES);
++ return -ENOMEM;
++ }
++
++ /* add this sub-table to the main table */
++ ipsec_sadb.refTable[table] = SArefsub;
++
++ /* initialise each element to NULL */
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_SArefSubTable_alloc: "
++ "initialising %u elements (2 ^ %u) of table %u.\n",
++ IPSEC_SA_REF_SUBTABLE_NUM_ENTRIES,
++ IPSEC_SA_REF_SUBTABLE_IDX_WIDTH,
++ table);
++ for(entry = 0; entry < IPSEC_SA_REF_SUBTABLE_NUM_ENTRIES; entry++) {
++ SArefsub->entry[entry] = NULL;
++ }
++
++ return 0;
++}
++#endif /* IPSEC_SA_REF_CODE */
++
++int
++ipsec_saref_freelist_init(void)
++{
++ int i;
++
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_saref_freelist_init: "
++ "initialising %u elements of FreeList.\n",
++ IPSEC_SA_REF_FREELIST_NUM_ENTRIES);
++
++ for(i = 0; i < IPSEC_SA_REF_FREELIST_NUM_ENTRIES; i++) {
++ ipsec_sadb.refFreeList[i] = IPSEC_SAREF_NULL;
++ }
++ ipsec_sadb.refFreeListHead = -1;
++ ipsec_sadb.refFreeListCont = 0;
++ ipsec_sadb.refFreeListTail = -1;
++
++ return 0;
++}
++
++int
++ipsec_sadb_init(void)
++{
++ int error = 0;
++ unsigned i;
++
++ for(i = 0; i < SADB_HASHMOD; i++) {
++ ipsec_sadb_hash[i] = NULL;
++ }
++ /* parts above are for the old style SADB hash table */
++
++
++#if IPSEC_SA_REF_CODE
++ /* initialise SA reference table */
++
++ /* initialise the main table */
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_sadb_init: "
++ "initialising main table of size %u (2 ^ %u).\n",
++ IPSEC_SA_REF_MAINTABLE_NUM_ENTRIES,
++ IPSEC_SA_REF_MAINTABLE_IDX_WIDTH);
++ {
++ unsigned table;
++ for(table = 0; table < IPSEC_SA_REF_MAINTABLE_NUM_ENTRIES; table++) {
++ ipsec_sadb.refTable[table] = NULL;
++ }
++ }
++
++ /* allocate the first sub-table */
++ error = ipsec_SArefSubTable_alloc(0);
++ if(error) {
++ return error;
++ }
++
++ error = ipsec_saref_freelist_init();
++#endif /* IPSEC_SA_REF_CODE */
++ return error;
++}
++
++#if IPSEC_SA_REF_CODE
++IPsecSAref_t
++ipsec_SAref_alloc(int*error) /* pass in error var by pointer */
++{
++ IPsecSAref_t SAref;
++
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_SAref_alloc: "
++ "SAref requested... head=%d, cont=%d, tail=%d, listsize=%d.\n",
++ ipsec_sadb.refFreeListHead,
++ ipsec_sadb.refFreeListCont,
++ ipsec_sadb.refFreeListTail,
++ IPSEC_SA_REF_FREELIST_NUM_ENTRIES);
++
++ if(ipsec_sadb.refFreeListHead == -1) {
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_SAref_alloc: "
++ "FreeList empty, recycling...\n");
++ *error = ipsec_SAref_recycle();
++ if(*error) {
++ return IPSEC_SAREF_NULL;
++ }
++ }
++
++ SAref = ipsec_sadb.refFreeList[ipsec_sadb.refFreeListHead];
++ if(SAref == IPSEC_SAREF_NULL) {
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_SAref_alloc: "
++ "unexpected error, refFreeListHead = %d points to invalid entry.\n",
++ ipsec_sadb.refFreeListHead);
++ *error = -ESPIPE;
++ return IPSEC_SAREF_NULL;
++ }
++
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_SAref_alloc: "
++ "allocating SAref=%d, table=%u, entry=%u of %u.\n",
++ SAref,
++ IPsecSAref2table(SAref),
++ IPsecSAref2entry(SAref),
++ IPSEC_SA_REF_MAINTABLE_NUM_ENTRIES * IPSEC_SA_REF_SUBTABLE_NUM_ENTRIES);
++
++ ipsec_sadb.refFreeList[ipsec_sadb.refFreeListHead] = IPSEC_SAREF_NULL;
++ ipsec_sadb.refFreeListHead++;
++ if(ipsec_sadb.refFreeListHead > ipsec_sadb.refFreeListTail) {
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_SAref_alloc: "
++ "last FreeList entry allocated, resetting list head to empty.\n");
++ ipsec_sadb.refFreeListHead = -1;
++ }
++
++ return SAref;
++}
++#endif /* IPSEC_SA_REF_CODE */
++
++int
++ipsec_sa_print(struct ipsec_sa *ips)
++{
++ char sa[SATOT_BUF];
++ size_t sa_len;
++
++ printk(KERN_INFO "klips_debug: SA:");
++ if(ips == NULL) {
++ printk("NULL\n");
++ return -ENOENT;
++ }
++ printk(" ref=%d", ips->ips_ref);
++ printk(" refcount=%d", atomic_read(&ips->ips_refcount));
++ if(ips->ips_hnext != NULL) {
++ printk(" hnext=0p%p", ips->ips_hnext);
++ }
++ if(ips->ips_inext != NULL) {
++ printk(" inext=0p%p", ips->ips_inext);
++ }
++ if(ips->ips_onext != NULL) {
++ printk(" onext=0p%p", ips->ips_onext);
++ }
++ sa_len = satot(&ips->ips_said, 0, sa, sizeof(sa));
++ printk(" said=%s", sa_len ? sa : " (error)");
++ if(ips->ips_seq) {
++ printk(" seq=%u", ips->ips_seq);
++ }
++ if(ips->ips_pid) {
++ printk(" pid=%u", ips->ips_pid);
++ }
++ if(ips->ips_authalg) {
++ printk(" authalg=%u", ips->ips_authalg);
++ }
++ if(ips->ips_encalg) {
++ printk(" encalg=%u", ips->ips_encalg);
++ }
++ printk(" XFORM=%s%s%s", IPS_XFORM_NAME(ips));
++ if(ips->ips_replaywin) {
++ printk(" ooowin=%u", ips->ips_replaywin);
++ }
++ if(ips->ips_flags) {
++ printk(" flags=%u", ips->ips_flags);
++ }
++ if(ips->ips_addr_s) {
++ char buf[SUBNETTOA_BUF];
++ addrtoa(((struct sockaddr_in*)(ips->ips_addr_s))->sin_addr,
++ 0, buf, sizeof(buf));
++ printk(" src=%s", buf);
++ }
++ if(ips->ips_addr_d) {
++ char buf[SUBNETTOA_BUF];
++ addrtoa(((struct sockaddr_in*)(ips->ips_addr_s))->sin_addr,
++ 0, buf, sizeof(buf));
++ printk(" dst=%s", buf);
++ }
++ if(ips->ips_addr_p) {
++ char buf[SUBNETTOA_BUF];
++ addrtoa(((struct sockaddr_in*)(ips->ips_addr_p))->sin_addr,
++ 0, buf, sizeof(buf));
++ printk(" proxy=%s", buf);
++ }
++ if(ips->ips_key_bits_a) {
++ printk(" key_bits_a=%u", ips->ips_key_bits_a);
++ }
++ if(ips->ips_key_bits_e) {
++ printk(" key_bits_e=%u", ips->ips_key_bits_e);
++ }
++
++ printk("\n");
++ return 0;
++}
++
++struct ipsec_sa*
++ipsec_sa_alloc(int*error) /* pass in error var by pointer */
++{
++ struct ipsec_sa* ips;
++
++ if((ips = kmalloc(sizeof(*ips), GFP_ATOMIC) ) == NULL) {
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_sa_alloc: "
++ "memory allocation error\n");
++ *error = -ENOMEM;
++ return NULL;
++ }
++ memset((caddr_t)ips, 0, sizeof(*ips));
++#if IPSEC_SA_REF_CODE
++ ips->ips_ref = ipsec_SAref_alloc(error); /* pass in error return by pointer */
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_sa_alloc: "
++ "allocated %lu bytes for ipsec_sa struct=0p%p ref=%d.\n",
++ (unsigned long) sizeof(*ips),
++ ips,
++ ips->ips_ref);
++ if(ips->ips_ref == IPSEC_SAREF_NULL) {
++ kfree(ips);
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_sa_alloc: "
++ "SAref allocation error\n");
++ return NULL;
++ }
++
++ atomic_inc(&ips->ips_refcount);
++ IPsecSAref2SA(ips->ips_ref) = ips;
++#endif /* IPSEC_SA_REF_CODE */
++
++ *error = 0;
++ return(ips);
++}
++
++int
++ipsec_sa_free(struct ipsec_sa* ips)
++{
++ return ipsec_sa_wipe(ips);
++}
++
++struct ipsec_sa *
++ipsec_sa_getbyid(ip_said *said)
++{
++ int hashval;
++ struct ipsec_sa *ips;
++ char sa[SATOT_BUF];
++ size_t sa_len;
++
++ if(said == NULL) {
++ KLIPS_PRINT(debug_xform,
++ "klips_error:ipsec_sa_getbyid: "
++ "null pointer passed in!\n");
++ return NULL;
++ }
++
++ sa_len = satot(said, 0, sa, sizeof(sa));
++
++ hashval = IPS_HASH(said);
++
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_sa_getbyid: "
++ "linked entry in ipsec_sa table for hash=%d of SA:%s requested.\n",
++ hashval,
++ sa_len ? sa : " (error)");
++
++ if((ips = ipsec_sadb_hash[hashval]) == NULL) {
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_sa_getbyid: "
++ "no entries in ipsec_sa table for hash=%d of SA:%s.\n",
++ hashval,
++ sa_len ? sa : " (error)");
++ return NULL;
++ }
++
++ for (; ips; ips = ips->ips_hnext) {
++ if ((ips->ips_said.spi == said->spi) &&
++ (ips->ips_said.dst.u.v4.sin_addr.s_addr == said->dst.u.v4.sin_addr.s_addr) &&
++ (ips->ips_said.proto == said->proto)) {
++ atomic_inc(&ips->ips_refcount);
++ return ips;
++ }
++ }
++
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_sa_getbyid: "
++ "no entry in linked list for hash=%d of SA:%s.\n",
++ hashval,
++ sa_len ? sa : " (error)");
++ return NULL;
++}
++
++int
++ipsec_sa_put(struct ipsec_sa *ips)
++{
++ char sa[SATOT_BUF];
++ size_t sa_len;
++
++ if(ips == NULL) {
++ KLIPS_PRINT(debug_xform,
++ "klips_error:ipsec_sa_put: "
++ "null pointer passed in!\n");
++ return -1;
++ }
++
++ sa_len = satot(&ips->ips_said, 0, sa, sizeof(sa));
++
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_sa_put: "
++ "ipsec_sa SA:%s, ref:%d reference count decremented.\n",
++ sa_len ? sa : " (error)",
++ ips->ips_ref);
++
++ atomic_dec(&ips->ips_refcount);
++
++ return 0;
++}
++
++/*
++ The ipsec_sa table better *NOT* be locked before it is handed in, or SMP locks will happen
++*/
++int
++ipsec_sa_add(struct ipsec_sa *ips)
++{
++ int error = 0;
++ unsigned int hashval;
++
++ if(ips == NULL) {
++ KLIPS_PRINT(debug_xform,
++ "klips_error:ipsec_sa_add: "
++ "null pointer passed in!\n");
++ return -ENODATA;
++ }
++ hashval = IPS_HASH(&ips->ips_said);
++
++ atomic_inc(&ips->ips_refcount);
++ spin_lock_bh(&tdb_lock);
++
++ ips->ips_hnext = ipsec_sadb_hash[hashval];
++ ipsec_sadb_hash[hashval] = ips;
++
++ spin_unlock_bh(&tdb_lock);
++
++ return error;
++}
++
++/*
++ The ipsec_sa table better be locked before it is handed in, or races might happen
++*/
++int
++ipsec_sa_del(struct ipsec_sa *ips)
++{
++ unsigned int hashval;
++ struct ipsec_sa *ipstp;
++ char sa[SATOT_BUF];
++ size_t sa_len;
++
++ if(ips == NULL) {
++ KLIPS_PRINT(debug_xform,
++ "klips_error:ipsec_sa_del: "
++ "null pointer passed in!\n");
++ return -ENODATA;
++ }
++
++ sa_len = satot(&ips->ips_said, 0, sa, sizeof(sa));
++ if(ips->ips_inext || ips->ips_onext) {
++ KLIPS_PRINT(debug_xform,
++ "klips_error:ipsec_sa_del: "
++ "SA:%s still linked!\n",
++ sa_len ? sa : " (error)");
++ return -EMLINK;
++ }
++
++ hashval = IPS_HASH(&ips->ips_said);
++
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_sa_del: "
++ "deleting SA:%s, hashval=%d.\n",
++ sa_len ? sa : " (error)",
++ hashval);
++ if(ipsec_sadb_hash[hashval] == NULL) {
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_sa_del: "
++ "no entries in ipsec_sa table for hash=%d of SA:%s.\n",
++ hashval,
++ sa_len ? sa : " (error)");
++ return -ENOENT;
++ }
++
++ if (ips == ipsec_sadb_hash[hashval]) {
++ ipsec_sadb_hash[hashval] = ipsec_sadb_hash[hashval]->ips_hnext;
++ ips->ips_hnext = NULL;
++ atomic_dec(&ips->ips_refcount);
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_sa_del: "
++ "successfully deleted first ipsec_sa in chain.\n");
++ return 0;
++ } else {
++ for (ipstp = ipsec_sadb_hash[hashval];
++ ipstp;
++ ipstp = ipstp->ips_hnext) {
++ if (ipstp->ips_hnext == ips) {
++ ipstp->ips_hnext = ips->ips_hnext;
++ ips->ips_hnext = NULL;
++ atomic_dec(&ips->ips_refcount);
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_sa_del: "
++ "successfully deleted link in ipsec_sa chain.\n");
++ return 0;
++ }
++ }
++ }
++
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_sa_del: "
++ "no entries in linked list for hash=%d of SA:%s.\n",
++ hashval,
++ sa_len ? sa : " (error)");
++ return -ENOENT;
++}
++
++/*
++ The ipsec_sa table better be locked before it is handed in, or races
++ might happen
++*/
++int
++ipsec_sa_delchain(struct ipsec_sa *ips)
++{
++ struct ipsec_sa *ipsdel;
++ int error = 0;
++ char sa[SATOT_BUF];
++ size_t sa_len;
++
++ if(ips == NULL) {
++ KLIPS_PRINT(debug_xform,
++ "klips_error:ipsec_sa_delchain: "
++ "null pointer passed in!\n");
++ return -ENODATA;
++ }
++
++ sa_len = satot(&ips->ips_said, 0, sa, sizeof(sa));
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_sa_delchain: "
++ "passed SA:%s\n",
++ sa_len ? sa : " (error)");
++ while(ips->ips_onext != NULL) {
++ ips = ips->ips_onext;
++ }
++
++ while(ips) {
++ /* XXX send a pfkey message up to advise of deleted ipsec_sa */
++ sa_len = satot(&ips->ips_said, 0, sa, sizeof(sa));
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_sa_delchain: "
++ "unlinking and delting SA:%s",
++ sa_len ? sa : " (error)");
++ ipsdel = ips;
++ ips = ips->ips_inext;
++ if(ips != NULL) {
++ sa_len = satot(&ips->ips_said, 0, sa, sizeof(sa));
++ KLIPS_PRINT(debug_xform,
++ ", inext=%s",
++ sa_len ? sa : " (error)");
++ atomic_dec(&ipsdel->ips_refcount);
++ ipsdel->ips_inext = NULL;
++ atomic_dec(&ips->ips_refcount);
++ ips->ips_onext = NULL;
++ }
++ KLIPS_PRINT(debug_xform,
++ ".\n");
++ if((error = ipsec_sa_del(ipsdel))) {
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_sa_delchain: "
++ "ipsec_sa_del returned error %d.\n", -error);
++ return error;
++ }
++ if((error = ipsec_sa_wipe(ipsdel))) {
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_sa_delchain: "
++ "ipsec_sa_wipe returned error %d.\n", -error);
++ return error;
++ }
++ }
++ return error;
++}
++
++int
++ipsec_sadb_cleanup(__u8 proto)
++{
++ unsigned i;
++ int error = 0;
++ struct ipsec_sa *ips, **ipsprev, *ipsdel;
++ char sa[SATOT_BUF];
++ size_t sa_len;
++
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_sadb_cleanup: "
++ "cleaning up proto=%d.\n",
++ proto);
++
++ spin_lock_bh(&tdb_lock);
++
++ for (i = 0; i < SADB_HASHMOD; i++) {
++ ipsprev = &(ipsec_sadb_hash[i]);
++ ips = ipsec_sadb_hash[i];
++ if(ips != NULL) {
++ atomic_inc(&ips->ips_refcount);
++ }
++ for(; ips != NULL;) {
++ sa_len = satot(&ips->ips_said, 0, sa, sizeof(sa));
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_sadb_cleanup: "
++ "checking SA:%s, hash=%d, ref=%d",
++ sa_len ? sa : " (error)",
++ i,
++ ips->ips_ref);
++ ipsdel = ips;
++ ips = ipsdel->ips_hnext;
++ if(ips != NULL) {
++ atomic_inc(&ips->ips_refcount);
++ sa_len = satot(&ips->ips_said, 0, sa, sizeof(sa));
++ KLIPS_PRINT(debug_xform,
++ ", hnext=%s",
++ sa_len ? sa : " (error)");
++ }
++ if(*ipsprev != NULL) {
++ sa_len = satot(&(*ipsprev)->ips_said, 0, sa, sizeof(sa));
++ KLIPS_PRINT(debug_xform,
++ ", *ipsprev=%s",
++ sa_len ? sa : " (error)");
++ if((*ipsprev)->ips_hnext) {
++ sa_len = satot(&(*ipsprev)->ips_hnext->ips_said, 0, sa, sizeof(sa));
++ KLIPS_PRINT(debug_xform,
++ ", *ipsprev->ips_hnext=%s",
++ sa_len ? sa : " (error)");
++ }
++ }
++ KLIPS_PRINT(debug_xform,
++ ".\n");
++ if(proto == 0 || (proto == ipsdel->ips_said.proto)) {
++ sa_len = satot(&ipsdel->ips_said, 0, sa, sizeof(sa));
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_sadb_cleanup: "
++ "deleting SA chain:%s.\n",
++ sa_len ? sa : " (error)");
++ if((error = ipsec_sa_delchain(ipsdel))) {
++ SENDERR(-error);
++ }
++ ipsprev = &(ipsec_sadb_hash[i]);
++ ips = ipsec_sadb_hash[i];
++
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_sadb_cleanup: "
++ "deleted SA chain:%s",
++ sa_len ? sa : " (error)");
++ if(ips != NULL) {
++ sa_len = satot(&ips->ips_said, 0, sa, sizeof(sa));
++ KLIPS_PRINT(debug_xform,
++ ", ipsec_sadb_hash[%d]=%s",
++ i,
++ sa_len ? sa : " (error)");
++ }
++ if(*ipsprev != NULL) {
++ sa_len = satot(&(*ipsprev)->ips_said, 0, sa, sizeof(sa));
++ KLIPS_PRINT(debug_xform,
++ ", *ipsprev=%s",
++ sa_len ? sa : " (error)");
++ if((*ipsprev)->ips_hnext != NULL) {
++ sa_len = satot(&(*ipsprev)->ips_hnext->ips_said, 0, sa, sizeof(sa));
++ KLIPS_PRINT(debug_xform,
++ ", *ipsprev->ips_hnext=%s",
++ sa_len ? sa : " (error)");
++ }
++ }
++ KLIPS_PRINT(debug_xform,
++ ".\n");
++ } else {
++ ipsprev = &ipsdel;
++ }
++ if(ipsdel != NULL) {
++ ipsec_sa_put(ipsdel);
++ }
++ }
++ }
++ errlab:
++
++ spin_unlock_bh(&tdb_lock);
++
++
++#if IPSEC_SA_REF_CODE
++ /* clean up SA reference table */
++
++ /* go through the ref table and clean out all the SAs */
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_sadb_cleanup: "
++ "removing SAref entries and tables.");
++ {
++ unsigned table, entry;
++ for(table = 0; table < IPSEC_SA_REF_MAINTABLE_NUM_ENTRIES; table++) {
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_sadb_cleanup: "
++ "cleaning SAref table=%u.\n",
++ table);
++ if(ipsec_sadb.refTable[table] == NULL) {
++ printk("\n");
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_sadb_cleanup: "
++ "cleaned %u used refTables.\n",
++ table);
++ break;
++ }
++ for(entry = 0; entry < IPSEC_SA_REF_SUBTABLE_NUM_ENTRIES; entry++) {
++ if(ipsec_sadb.refTable[table]->entry[entry] != NULL) {
++ ipsec_sa_delchain(ipsec_sadb.refTable[table]->entry[entry]);
++ ipsec_sadb.refTable[table]->entry[entry] = NULL;
++ }
++ }
++ }
++ }
++#endif /* IPSEC_SA_REF_CODE */
++
++ return(error);
++}
++
++int
++ipsec_sadb_free(void)
++{
++ int error = 0;
++
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_sadb_free: "
++ "freeing SArefTable memory.\n");
++
++ /* clean up SA reference table */
++
++ /* go through the ref table and clean out all the SAs if any are
++ left and free table memory */
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_sadb_free: "
++ "removing SAref entries and tables.\n");
++ {
++ unsigned table, entry;
++ for(table = 0; table < IPSEC_SA_REF_MAINTABLE_NUM_ENTRIES; table++) {
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_sadb_free: "
++ "removing SAref table=%u.\n",
++ table);
++ if(ipsec_sadb.refTable[table] == NULL) {
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_sadb_free: "
++ "removed %u used refTables.\n",
++ table);
++ break;
++ }
++ for(entry = 0; entry < IPSEC_SA_REF_SUBTABLE_NUM_ENTRIES; entry++) {
++ if(ipsec_sadb.refTable[table]->entry[entry] != NULL) {
++ ipsec_sa_delchain(ipsec_sadb.refTable[table]->entry[entry]);
++ ipsec_sadb.refTable[table]->entry[entry] = NULL;
++ }
++ }
++ vfree(ipsec_sadb.refTable[table]);
++ ipsec_sadb.refTable[table] = NULL;
++ }
++ }
++
++ return(error);
++}
++
++int
++ipsec_sa_wipe(struct ipsec_sa *ips)
++{
++ if(ips == NULL) {
++ return -ENODATA;
++ }
++
++ /* if(atomic_dec_and_test(ips)) {
++ }; */
++
++#if IPSEC_SA_REF_CODE
++ /* remove me from the SArefTable */
++ {
++ char sa[SATOT_BUF];
++ size_t sa_len;
++ sa_len = satot(&ips->ips_said, 0, sa, sizeof(sa));
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_sa_wipe: "
++ "removing SA=%s(0p%p), SAref=%d, table=%d(0p%p), entry=%d from the refTable.\n",
++ sa_len ? sa : " (error)",
++ ips,
++ ips->ips_ref,
++ IPsecSAref2table(IPsecSA2SAref(ips)),
++ ipsec_sadb.refTable[IPsecSAref2table(IPsecSA2SAref(ips))],
++ IPsecSAref2entry(IPsecSA2SAref(ips)));
++ }
++ if(ips->ips_ref == IPSEC_SAREF_NULL) {
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_sa_wipe: "
++ "why does this SA not have a valid SAref?.\n");
++ }
++ ipsec_sadb.refTable[IPsecSAref2table(IPsecSA2SAref(ips))]->entry[IPsecSAref2entry(IPsecSA2SAref(ips))] = NULL;
++ ips->ips_ref = IPSEC_SAREF_NULL;
++ ipsec_sa_put(ips);
++#endif /* IPSEC_SA_REF_CODE */
++
++ /* paranoid clean up */
++ if(ips->ips_addr_s != NULL) {
++ memset((caddr_t)(ips->ips_addr_s), 0, ips->ips_addr_s_size);
++ kfree(ips->ips_addr_s);
++ }
++ ips->ips_addr_s = NULL;
++
++ if(ips->ips_addr_d != NULL) {
++ memset((caddr_t)(ips->ips_addr_d), 0, ips->ips_addr_d_size);
++ kfree(ips->ips_addr_d);
++ }
++ ips->ips_addr_d = NULL;
++
++ if(ips->ips_addr_p != NULL) {
++ memset((caddr_t)(ips->ips_addr_p), 0, ips->ips_addr_p_size);
++ kfree(ips->ips_addr_p);
++ }
++ ips->ips_addr_p = NULL;
++
++#ifdef CONFIG_IPSEC_NAT_TRAVERSAL
++ if(ips->ips_natt_oa) {
++ memset((caddr_t)(ips->ips_natt_oa), 0, ips->ips_natt_oa_size);
++ kfree(ips->ips_natt_oa);
++ }
++ ips->ips_natt_oa = NULL;
++#endif
++
++ if(ips->ips_key_a != NULL) {
++ memset((caddr_t)(ips->ips_key_a), 0, ips->ips_key_a_size);
++ kfree(ips->ips_key_a);
++ }
++ ips->ips_key_a = NULL;
++
++ if(ips->ips_key_e != NULL) {
++#ifdef CONFIG_IPSEC_ALG
++ if (ips->ips_alg_enc&&ips->ips_alg_enc->ixt_e_destroy_key) {
++ ips->ips_alg_enc->ixt_e_destroy_key(ips->ips_alg_enc,
++ ips->ips_key_e);
++ } else {
++#endif /* CONFIG_IPSEC_ALG */
++ memset((caddr_t)(ips->ips_key_e), 0, ips->ips_key_e_size);
++ kfree(ips->ips_key_e);
++#ifdef CONFIG_IPSEC_ALG
++ }
++#endif /* CONFIG_IPSEC_ALG */
++ }
++ ips->ips_key_e = NULL;
++
++ if(ips->ips_iv != NULL) {
++ memset((caddr_t)(ips->ips_iv), 0, ips->ips_iv_size);
++ kfree(ips->ips_iv);
++ }
++ ips->ips_iv = NULL;
++
++ if(ips->ips_ident_s.data != NULL) {
++ memset((caddr_t)(ips->ips_ident_s.data),
++ 0,
++ ips->ips_ident_s.len * IPSEC_PFKEYv2_ALIGN - sizeof(struct sadb_ident));
++ kfree(ips->ips_ident_s.data);
++ }
++ ips->ips_ident_s.data = NULL;
++
++ if(ips->ips_ident_d.data != NULL) {
++ memset((caddr_t)(ips->ips_ident_d.data),
++ 0,
++ ips->ips_ident_d.len * IPSEC_PFKEYv2_ALIGN - sizeof(struct sadb_ident));
++ kfree(ips->ips_ident_d.data);
++ }
++ ips->ips_ident_d.data = NULL;
++
++#ifdef CONFIG_IPSEC_ALG
++ if (ips->ips_alg_enc||ips->ips_alg_auth) {
++ ipsec_alg_sa_wipe(ips);
++ }
++#endif /* CONFIG_IPSEC_ALG */
++
++ memset((caddr_t)ips, 0, sizeof(*ips));
++ kfree(ips);
++ ips = NULL;
++
++ return 0;
++}
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.23 2004/04/06 02:49:26 mcr
++ * pullup of algo code from alg-branch.
++ *
++ * Revision 1.22.2.1 2003/12/22 15:25:52 jjo
++ * . Merged algo-0.8.1-rc11-test1 into alg-branch
++ *
++ * Revision 1.22 2003/12/10 01:14:27 mcr
++ * NAT-traversal patches to KLIPS.
++ *
++ * Revision 1.21 2003/10/31 02:27:55 mcr
++ * pulled up port-selector patches and sa_id elimination.
++ *
++ * Revision 1.20.4.1 2003/10/29 01:30:41 mcr
++ * elimited "struct sa_id".
++ *
++ * Revision 1.20 2003/02/06 01:50:34 rgb
++ * Fixed initialisation bug for first sadb hash bucket that would only manifest itself on platforms where NULL != 0.
++ *
++ * Revision 1.19 2003/01/30 02:32:22 rgb
++ *
++ * Rename SAref table macro names for clarity.
++ * Transmit error code through to caller from callee for better diagnosis of problems.
++ * Convert IPsecSAref_t from signed to unsigned to fix apparent SAref exhaustion bug.
++ *
++ * Revision 1.18 2002/10/12 23:11:53 dhr
++ *
++ * [KenB + DHR] more 64-bit cleanup
++ *
++ * Revision 1.17 2002/10/07 18:31:43 rgb
++ * Move field width sanity checks to ipsec_sa.c
++ *
++ * Revision 1.16 2002/09/20 15:41:02 rgb
++ * Re-wrote most of the SAref code to eliminate Entry pointers.
++ * Added SAref code compiler directive switch.
++ * Added a saref test function for testing macros.
++ * Switch from pfkey_alloc_ipsec_sa() to ipsec_sa_alloc().
++ * Split ipsec_sadb_cleanup from new funciton ipsec_sadb_free to avoid problem
++ * of freeing newly created structures when clearing the reftable upon startup
++ * to start from a known state.
++ * Place all ipsec sadb globals into one struct.
++ * Rework saref freelist.
++ * Added memory allocation debugging.
++ *
++ * Revision 1.15 2002/09/20 05:01:44 rgb
++ * Update copyright date.
++ *
++ * Revision 1.14 2002/08/13 19:01:25 mcr
++ * patches from kenb to permit compilation of FreeSWAN on ia64.
++ * des library patched to use proper DES_LONG type for ia64.
++ *
++ * Revision 1.13 2002/07/29 03:06:20 mcr
++ * get rid of variable not used warnings.
++ *
++ * Revision 1.12 2002/07/26 08:48:31 rgb
++ * Added SA ref table code.
++ *
++ * Revision 1.11 2002/06/04 16:48:49 rgb
++ * Tidied up pointer code for processor independance.
++ *
++ * Revision 1.10 2002/05/23 07:16:17 rgb
++ * Added ipsec_sa_put() for releasing an ipsec_sa refcount.
++ * Pointer clean-up.
++ * Added refcount code.
++ * Convert "usecount" to "refcount" to remove ambiguity.
++ *
++ * Revision 1.9 2002/05/14 02:34:49 rgb
++ * Converted reference from ipsec_sa_put to ipsec_sa_add to avoid confusion
++ * with "put" usage in the kernel.
++ * Change all references to tdb, TDB or Tunnel Descriptor Block to ips,
++ * ipsec_sa or ipsec_sa.
++ * Added some preliminary refcount code.
++ *
++ * Revision 1.8 2002/04/24 07:55:32 mcr
++ * #include patches and Makefiles for post-reorg compilation.
++ *
++ * Revision 1.7 2002/04/24 07:36:30 mcr
++ * Moved from ./klips/net/ipsec/ipsec_sa.c,v
++ *
++ * Revision 1.6 2002/04/20 00:12:25 rgb
++ * Added esp IV CBC attack fix, disabled.
++ *
++ * Revision 1.5 2002/01/29 17:17:56 mcr
++ * moved include of ipsec_param.h to after include of linux/kernel.h
++ * otherwise, it seems that some option that is set in ipsec_param.h
++ * screws up something subtle in the include path to kernel.h, and
++ * it complains on the snprintf() prototype.
++ *
++ * Revision 1.4 2002/01/29 04:00:52 mcr
++ * more excise of kversions.h header.
++ *
++ * Revision 1.3 2002/01/29 02:13:18 mcr
++ * introduction of ipsec_kversion.h means that include of
++ * ipsec_param.h must preceed any decisions about what files to
++ * include to deal with differences in kernel source.
++ *
++ * Revision 1.2 2001/11/26 09:16:15 rgb
++ * Merge MCR's ipsec_sa, eroute, proc and struct lifetime changes.
++ *
++ * Revision 1.1.2.2 2001/10/22 21:05:41 mcr
++ * removed phony prototype for des_set_key.
++ *
++ * Revision 1.1.2.1 2001/09/25 02:24:57 mcr
++ * struct tdb -> struct ipsec_sa.
++ * sa(tdb) manipulation functions renamed and moved to ipsec_sa.c
++ * ipsec_xform.c removed. header file still contains useful things.
++ *
++ *
++ *
++ * CLONED from ipsec_xform.c:
++ * Revision 1.53 2001/09/08 21:13:34 rgb
++ * Added pfkey ident extension support for ISAKMPd. (NetCelo)
++ *
++ * Revision 1.52 2001/06/14 19:35:11 rgb
++ * Update copyright date.
++ *
++ * Revision 1.51 2001/05/30 08:14:03 rgb
++ * Removed vestiges of esp-null transforms.
++ *
++ * Revision 1.50 2001/05/03 19:43:18 rgb
++ * Initialise error return variable.
++ * Update SENDERR macro.
++ * Fix sign of error return code for ipsec_tdbcleanup().
++ * Use more appropriate return code for ipsec_tdbwipe().
++ *
++ * Revision 1.49 2001/04/19 18:56:17 rgb
++ * Fixed tdb table locking comments.
++ *
++ * Revision 1.48 2001/02/27 22:24:55 rgb
++ * Re-formatting debug output (line-splitting, joining, 1arg/line).
++ * Check for satoa() return codes.
++ *
++ * Revision 1.47 2000/11/06 04:32:08 rgb
++ * Ditched spin_lock_irqsave in favour of spin_lock_bh.
++ *
++ * Revision 1.46 2000/09/20 16:21:57 rgb
++ * Cleaned up ident string alloc/free.
++ *
++ * Revision 1.45 2000/09/08 19:16:51 rgb
++ * Change references from DEBUG_IPSEC to CONFIG_IPSEC_DEBUG.
++ * Removed all references to CONFIG_IPSEC_PFKEYv2.
++ *
++ * Revision 1.44 2000/08/30 05:29:04 rgb
++ * Compiler-define out no longer used tdb_init() in ipsec_xform.c.
++ *
++ * Revision 1.43 2000/08/18 21:30:41 rgb
++ * Purged all tdb_spi, tdb_proto and tdb_dst macros. They are unclear.
++ *
++ * Revision 1.42 2000/08/01 14:51:51 rgb
++ * Removed _all_ remaining traces of DES.
++ *
++ * Revision 1.41 2000/07/28 14:58:31 rgb
++ * Changed kfree_s to kfree, eliminating extra arg to fix 2.4.0-test5.
++ *
++ * Revision 1.40 2000/06/28 05:50:11 rgb
++ * Actually set iv_bits.
++ *
++ * Revision 1.39 2000/05/10 23:11:09 rgb
++ * Added netlink debugging output.
++ * Added a cast to quiet down the ntohl bug.
++ *
++ * Revision 1.38 2000/05/10 19:18:42 rgb
++ * Cast output of ntohl so that the broken prototype doesn't make our
++ * compile noisy.
++ *
++ * Revision 1.37 2000/03/16 14:04:59 rgb
++ * Hardwired CONFIG_IPSEC_PFKEYv2 on.
++ *
++ * Revision 1.36 2000/01/26 10:11:28 rgb
++ * Fixed spacing in error text causing run-in words.
++ *
++ * Revision 1.35 2000/01/21 06:17:16 rgb
++ * Tidied up compiler directive indentation for readability.
++ * Added ictx,octx vars for simplification.(kravietz)
++ * Added macros for HMAC padding magic numbers.(kravietz)
++ * Fixed missing key length reporting bug.
++ * Fixed bug in tdbwipe to return immediately on NULL tdbp passed in.
++ *
++ * Revision 1.34 1999/12/08 00:04:19 rgb
++ * Fixed SA direction overwriting bug for netlink users.
++ *
++ * Revision 1.33 1999/12/01 22:16:44 rgb
++ * Minor formatting changes in ESP MD5 initialisation.
++ *
++ * Revision 1.32 1999/11/25 09:06:36 rgb
++ * Fixed error return messages, should be returning negative numbers.
++ * Implemented SENDERR macro for propagating error codes.
++ * Added debug message and separate error code for algorithms not compiled
++ * in.
++ *
++ * Revision 1.31 1999/11/23 23:06:26 rgb
++ * Sort out pfkey and freeswan headers, putting them in a library path.
++ *
++ * Revision 1.30 1999/11/18 04:09:20 rgb
++ * Replaced all kernel version macros to shorter, readable form.
++ *
++ * Revision 1.29 1999/11/17 15:53:40 rgb
++ * Changed all occurrences of #include "../../../lib/freeswan.h"
++ * to #include <freeswan.h> which works due to -Ilibfreeswan in the
++ * klips/net/ipsec/Makefile.
++ *
++ * Revision 1.28 1999/10/18 20:04:01 rgb
++ * Clean-out unused cruft.
++ *
++ * Revision 1.27 1999/10/03 19:01:03 rgb
++ * Spinlock support for 2.3.xx and 2.0.xx kernels.
++ *
++ * Revision 1.26 1999/10/01 16:22:24 rgb
++ * Switch from assignment init. to functional init. of spinlocks.
++ *
++ * Revision 1.25 1999/10/01 15:44:54 rgb
++ * Move spinlock header include to 2.1> scope.
++ *
++ * Revision 1.24 1999/10/01 00:03:46 rgb
++ * Added tdb structure locking.
++ * Minor formatting changes.
++ * Add function to initialize tdb hash table.
++ *
++ * Revision 1.23 1999/05/25 22:42:12 rgb
++ * Add deltdbchain() debugging.
++ *
++ * Revision 1.22 1999/05/25 21:24:31 rgb
++ * Add debugging statements to deltdbchain().
++ *
++ * Revision 1.21 1999/05/25 03:51:48 rgb
++ * Refix error return code.
++ *
++ * Revision 1.20 1999/05/25 03:34:07 rgb
++ * Fix error return for flush.
++ *
++ * Revision 1.19 1999/05/09 03:25:37 rgb
++ * Fix bug introduced by 2.2 quick-and-dirty patch.
++ *
++ * Revision 1.18 1999/05/05 22:02:32 rgb
++ * Add a quick and dirty port to 2.2 kernels by Marc Boucher <marc@mbsi.ca>.
++ *
++ * Revision 1.17 1999/04/29 15:20:16 rgb
++ * Change gettdb parameter to a pointer to reduce stack loading and
++ * facilitate parameter sanity checking.
++ * Add sanity checking for null pointer arguments.
++ * Add debugging instrumentation.
++ * Add function deltdbchain() which will take care of unlinking,
++ * zeroing and deleting a chain of tdbs.
++ * Add a parameter to tdbcleanup to be able to delete a class of SAs.
++ * tdbwipe now actually zeroes the tdb as well as any of its pointed
++ * structures.
++ *
++ * Revision 1.16 1999/04/16 15:36:29 rgb
++ * Fix cut-and-paste error causing a memory leak in IPIP TDB freeing.
++ *
++ * Revision 1.15 1999/04/11 00:29:01 henry
++ * GPL boilerplate
++ *
++ * Revision 1.14 1999/04/06 04:54:28 rgb
++ * Fix/Add RCSID Id: and Log: bits to make PHMDs happy. This includes
++ * patch shell fixes.
++ *
++ * Revision 1.13 1999/02/19 18:23:01 rgb
++ * Nix debug off compile warning.
++ *
++ * Revision 1.12 1999/02/17 16:52:16 rgb
++ * Consolidate satoa()s for space and speed efficiency.
++ * Convert DEBUG_IPSEC to KLIPS_PRINT
++ * Clean out unused cruft.
++ * Ditch NET_IPIP dependancy.
++ * Loop for 3des key setting.
++ *
++ * Revision 1.11 1999/01/26 02:09:05 rgb
++ * Remove ah/esp/IPIP switching on include files.
++ * Removed CONFIG_IPSEC_ALGO_SWITCH macro.
++ * Removed dead code.
++ * Clean up debug code when switched off.
++ * Remove references to INET_GET_PROTOCOL.
++ * Added code exclusion macros to reduce code from unused algorithms.
++ *
++ * Revision 1.10 1999/01/22 06:28:55 rgb
++ * Cruft clean-out.
++ * Put random IV generation in kernel.
++ * Added algorithm switch code.
++ * Enhanced debugging.
++ * 64-bit clean-up.
++ *
++ * Revision 1.9 1998/11/30 13:22:55 rgb
++ * Rationalised all the klips kernel file headers. They are much shorter
++ * now and won't conflict under RH5.2.
++ *
++ * Revision 1.8 1998/11/25 04:59:06 rgb
++ * Add conditionals for no IPIP tunnel code.
++ * Delete commented out code.
++ *
++ * Revision 1.7 1998/10/31 06:50:41 rgb
++ * Convert xform ASCII names to no spaces.
++ * Fixed up comments in #endif directives.
++ *
++ * Revision 1.6 1998/10/19 14:44:28 rgb
++ * Added inclusion of freeswan.h.
++ * sa_id structure implemented and used: now includes protocol.
++ *
++ * Revision 1.5 1998/10/09 04:32:19 rgb
++ * Added 'klips_debug' prefix to all klips printk debug statements.
++ *
++ * Revision 1.4 1998/08/12 00:11:31 rgb
++ * Added new xform functions to the xform table.
++ * Fixed minor debug output spelling error.
++ *
++ * Revision 1.3 1998/07/09 17:45:31 rgb
++ * Clarify algorithm not available message.
++ *
++ * Revision 1.2 1998/06/23 03:00:51 rgb
++ * Check for presence of IPIP protocol if it is setup one way (we don't
++ * know what has been set up the other way and can only assume it will be
++ * symmetrical with the exception of keys).
++ *
++ * Revision 1.1 1998/06/18 21:27:51 henry
++ * move sources from klips/src to klips/net/ipsec, to keep stupid
++ * kernel-build scripts happier in the presence of symlinks
++ *
++ * Revision 1.3 1998/06/11 05:54:59 rgb
++ * Added transform version string pointer to xformsw initialisations.
++ *
++ * Revision 1.2 1998/04/21 21:28:57 rgb
++ * Rearrange debug switches to change on the fly debug output from user
++ * space. Only kernel changes checked in at this time. radij.c was also
++ * changed to temporarily remove buggy debugging code in rj_delete causing
++ * an OOPS and hence, netlink device open errors.
++ *
++ * Revision 1.1 1998/04/09 03:06:13 henry
++ * sources moved up from linux/net/ipsec
++ *
++ * Revision 1.1.1.1 1998/04/08 05:35:02 henry
++ * RGB's ipsec-0.8pre2.tar.gz ipsec-0.8
++ *
++ * Revision 0.5 1997/06/03 04:24:48 ji
++ * Added ESP-3DES-MD5-96
++ *
++ * Revision 0.4 1997/01/15 01:28:15 ji
++ * Added new transforms.
++ *
++ * Revision 0.3 1996/11/20 14:39:04 ji
++ * Minor cleanups.
++ * Rationalized debugging code.
++ *
++ * Revision 0.2 1996/11/02 00:18:33 ji
++ * First limited release.
++ *
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/net/ipsec/ipsec_sha1.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,219 @@
++/*
++ * RCSID $Id$
++ */
++
++/*
++ * The rest of the code is derived from sha1.c by Steve Reid, which is
++ * public domain.
++ * Minor cosmetic changes to accomodate it in the Linux kernel by ji.
++ */
++
++#include <asm/byteorder.h>
++#include <linux/string.h>
++
++#include "openswan/ipsec_sha1.h"
++
++#if defined(rol)
++#undef rol
++#endif
++
++#define SHA1HANDSOFF
++
++#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
++
++/* blk0() and blk() perform the initial expand. */
++/* I got the idea of expanding during the round function from SSLeay */
++#ifdef __LITTLE_ENDIAN
++#define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \
++ |(rol(block->l[i],8)&0x00FF00FF))
++#else
++#define blk0(i) block->l[i]
++#endif
++#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
++ ^block->l[(i+2)&15]^block->l[i&15],1))
++
++/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
++#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
++#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
++#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
++#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
++#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
++
++
++/* Hash a single 512-bit block. This is the core of the algorithm. */
++
++void SHA1Transform(__u32 state[5], __u8 buffer[64])
++{
++__u32 a, b, c, d, e;
++typedef union {
++ unsigned char c[64];
++ __u32 l[16];
++} CHAR64LONG16;
++CHAR64LONG16* block;
++#ifdef SHA1HANDSOFF
++static unsigned char workspace[64];
++ block = (CHAR64LONG16*)workspace;
++ memcpy(block, buffer, 64);
++#else
++ block = (CHAR64LONG16*)buffer;
++#endif
++ /* Copy context->state[] to working vars */
++ a = state[0];
++ b = state[1];
++ c = state[2];
++ d = state[3];
++ e = state[4];
++ /* 4 rounds of 20 operations each. Loop unrolled. */
++ R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
++ R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
++ R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
++ R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
++ R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
++ R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
++ R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
++ R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
++ R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
++ R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
++ R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
++ R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
++ R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
++ R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
++ R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
++ R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
++ R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
++ R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
++ R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
++ R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
++ /* Add the working vars back into context.state[] */
++ state[0] += a;
++ state[1] += b;
++ state[2] += c;
++ state[3] += d;
++ state[4] += e;
++ /* Wipe variables */
++ a = b = c = d = e = 0;
++}
++
++
++/* SHA1Init - Initialize new context */
++
++void SHA1Init(void *vcontext)
++{
++ SHA1_CTX* context = vcontext;
++
++ /* SHA1 initialization constants */
++ context->state[0] = 0x67452301;
++ context->state[1] = 0xEFCDAB89;
++ context->state[2] = 0x98BADCFE;
++ context->state[3] = 0x10325476;
++ context->state[4] = 0xC3D2E1F0;
++ context->count[0] = context->count[1] = 0;
++}
++
++
++/* Run your data through this. */
++
++void SHA1Update(void *vcontext, unsigned char* data, __u32 len)
++{
++ SHA1_CTX* context = vcontext;
++ __u32 i, j;
++
++ j = context->count[0];
++ if ((context->count[0] += len << 3) < j)
++ context->count[1]++;
++ context->count[1] += (len>>29);
++ j = (j >> 3) & 63;
++ if ((j + len) > 63) {
++ memcpy(&context->buffer[j], data, (i = 64-j));
++ SHA1Transform(context->state, context->buffer);
++ for ( ; i + 63 < len; i += 64) {
++ SHA1Transform(context->state, &data[i]);
++ }
++ j = 0;
++ }
++ else i = 0;
++ memcpy(&context->buffer[j], &data[i], len - i);
++}
++
++
++/* Add padding and return the message digest. */
++
++void SHA1Final(unsigned char digest[20], void *vcontext)
++{
++ __u32 i, j;
++ unsigned char finalcount[8];
++ SHA1_CTX* context = vcontext;
++
++ for (i = 0; i < 8; i++) {
++ finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
++ >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */
++ }
++ SHA1Update(context, (unsigned char *)"\200", 1);
++ while ((context->count[0] & 504) != 448) {
++ SHA1Update(context, (unsigned char *)"\0", 1);
++ }
++ SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */
++ for (i = 0; i < 20; i++) {
++ digest[i] = (unsigned char)
++ ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
++ }
++ /* Wipe variables */
++ i = j = 0;
++ memset(context->buffer, 0, 64);
++ memset(context->state, 0, 20);
++ memset(context->count, 0, 8);
++ memset(&finalcount, 0, 8);
++#ifdef SHA1HANDSOFF /* make SHA1Transform overwrite its own static vars */
++ SHA1Transform(context->state, context->buffer);
++#endif
++}
++
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.9 2004/04/06 02:49:26 mcr
++ * pullup of algo code from alg-branch.
++ *
++ * Revision 1.8 2002/09/10 01:45:14 mcr
++ * changed type of MD5_CTX and SHA1_CTX to void * so that
++ * the function prototypes would match, and could be placed
++ * into a pointer to a function.
++ *
++ * Revision 1.7 2002/04/24 07:55:32 mcr
++ * #include patches and Makefiles for post-reorg compilation.
++ *
++ * Revision 1.6 2002/04/24 07:36:30 mcr
++ * Moved from ./klips/net/ipsec/ipsec_sha1.c,v
++ *
++ * Revision 1.5 1999/12/13 13:59:13 rgb
++ * Quick fix to argument size to Update bugs.
++ *
++ * Revision 1.4 1999/04/11 00:29:00 henry
++ * GPL boilerplate
++ *
++ * Revision 1.3 1999/04/06 04:54:27 rgb
++ * Fix/Add RCSID Id: and Log: bits to make PHMDs happy. This includes
++ * patch shell fixes.
++ *
++ * Revision 1.2 1999/01/22 06:55:50 rgb
++ * 64-bit clean-up.
++ *
++ * Revision 1.1 1998/06/18 21:27:50 henry
++ * move sources from klips/src to klips/net/ipsec, to keep stupid
++ * kernel-build scripts happier in the presence of symlinks
++ *
++ * Revision 1.2 1998/04/23 20:54:04 rgb
++ * Fixed md5 and sha1 include file nesting issues, to be cleaned up when
++ * verified.
++ *
++ * Revision 1.1 1998/04/09 03:06:11 henry
++ * sources moved up from linux/net/ipsec
++ *
++ * Revision 1.1.1.1 1998/04/08 05:35:05 henry
++ * RGB's ipsec-0.8pre2.tar.gz ipsec-0.8
++ *
++ * Revision 0.4 1997/01/15 01:28:15 ji
++ * New transform
++ *
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/net/ipsec/ipsec_tunnel.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,2645 @@
++/*
++ * IPSEC Tunneling code. Heavily based on drivers/net/new_tunnel.c
++ * Copyright (C) 1996, 1997 John Ioannidis.
++ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 Richard Guy Briggs.
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ */
++
++char ipsec_tunnel_c_version[] = "RCSID $Id$";
++
++#define __NO_VERSION__
++#include <linux/module.h>
++#include <linux/config.h> /* for CONFIG_IP_FORWARD */
++#include <linux/version.h>
++#include <linux/kernel.h> /* printk() */
++
++#include "openswan/ipsec_param.h"
++
++#ifdef MALLOC_SLAB
++# include <linux/slab.h> /* kmalloc() */
++#else /* MALLOC_SLAB */
++# include <linux/malloc.h> /* kmalloc() */
++#endif /* MALLOC_SLAB */
++#include <linux/errno.h> /* error codes */
++#include <linux/types.h> /* size_t */
++#include <linux/interrupt.h> /* mark_bh */
++
++#include <linux/netdevice.h> /* struct device, struct net_device_stats, dev_queue_xmit() and other headers */
++#include <linux/etherdevice.h> /* eth_type_trans */
++#include <linux/ip.h> /* struct iphdr */
++#include <linux/tcp.h> /* struct tcphdr */
++#include <linux/udp.h> /* struct udphdr */
++#include <linux/skbuff.h>
++#include <openswan.h>
++#ifdef NET_21
++# include <asm/uaccess.h>
++# include <linux/in6.h>
++# define ip_chk_addr inet_addr_type
++# define IS_MYADDR RTN_LOCAL
++# include <net/dst.h>
++# undef dev_kfree_skb
++# define dev_kfree_skb(a,b) kfree_skb(a)
++# define PHYSDEV_TYPE
++#endif /* NET_21 */
++#include <asm/checksum.h>
++#include <net/icmp.h> /* icmp_send() */
++#include <net/ip.h>
++#ifdef NETDEV_23
++# include <linux/netfilter_ipv4.h>
++#endif /* NETDEV_23 */
++
++#include <linux/if_arp.h>
++
++#include "openswan/radij.h"
++#include "openswan/ipsec_life.h"
++#include "openswan/ipsec_xform.h"
++#include "openswan/ipsec_eroute.h"
++#include "openswan/ipsec_encap.h"
++#include "openswan/ipsec_radij.h"
++#include "openswan/ipsec_sa.h"
++#include "openswan/ipsec_tunnel.h"
++#include "openswan/ipsec_xmit.h"
++#include "openswan/ipsec_ipe4.h"
++#include "openswan/ipsec_ah.h"
++#include "openswan/ipsec_esp.h"
++
++#include <pfkeyv2.h>
++#include <pfkey.h>
++
++#include "openswan/ipsec_proto.h"
++#ifdef CONFIG_IPSEC_NAT_TRAVERSAL
++#include <linux/udp.h>
++#endif
++
++static __u32 zeroes[64];
++
++#ifdef CONFIG_IPSEC_DEBUG
++int debug_tunnel = 0;
++#endif /* CONFIG_IPSEC_DEBUG */
++
++DEBUG_NO_STATIC int
++ipsec_tunnel_open(struct device *dev)
++{
++ struct ipsecpriv *prv = dev->priv;
++
++ /*
++ * Can't open until attached.
++ */
++
++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
++ "klips_debug:ipsec_tunnel_open: "
++ "dev = %s, prv->dev = %s\n",
++ dev->name, prv->dev?prv->dev->name:"NONE");
++
++ if (prv->dev == NULL)
++ return -ENODEV;
++
++ MOD_INC_USE_COUNT;
++ return 0;
++}
++
++DEBUG_NO_STATIC int
++ipsec_tunnel_close(struct device *dev)
++{
++ MOD_DEC_USE_COUNT;
++ return 0;
++}
++
++#ifdef NETDEV_23
++static inline int ipsec_tunnel_xmit2(struct sk_buff *skb)
++{
++#ifdef NETDEV_25 /* 2.6 kernels */
++ return dst_output(skb);
++#else
++ return ip_send(skb);
++#endif
++}
++#endif /* NETDEV_23 */
++
++enum ipsec_xmit_value
++ipsec_tunnel_strip_hard_header(struct ipsec_xmit_state *ixs)
++{
++ /* ixs->physdev->hard_header_len is unreliable and should not be used */
++ ixs->hard_header_len = (unsigned char *)(ixs->iph) - ixs->skb->data;
++
++ if(ixs->hard_header_len < 0) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
++ "klips_error:ipsec_xmit_strip_hard_header: "
++ "Negative hard_header_len (%d)?!\n", ixs->hard_header_len);
++ ixs->stats->tx_dropped++;
++ return IPSEC_XMIT_BADHHLEN;
++ }
++
++ /* while ixs->physdev->hard_header_len is unreliable and
++ * should not be trusted, it accurate and required for ATM, GRE and
++ * some other interfaces to work. Thanks to Willy Tarreau
++ * <willy@w.ods.org>.
++ */
++ if(ixs->hard_header_len == 0) { /* no hard header present */
++ ixs->hard_header_stripped = 1;
++ ixs->hard_header_len = ixs->physdev->hard_header_len;
++ }
++
++#ifdef CONFIG_IPSEC_DEBUG
++ if (debug_tunnel & DB_TN_XMIT) {
++ int i;
++ char c;
++
++ printk(KERN_INFO "klips_debug:ipsec_xmit_strip_hard_header: "
++ ">>> skb->len=%ld hard_header_len:%d",
++ (unsigned long int)ixs->skb->len, ixs->hard_header_len);
++ c = ' ';
++ for (i=0; i < ixs->hard_header_len; i++) {
++ printk("%c%02x", c, ixs->skb->data[i]);
++ c = ':';
++ }
++ printk(" \n");
++ }
++#endif /* CONFIG_IPSEC_DEBUG */
++
++ KLIPS_IP_PRINT(debug_tunnel & DB_TN_XMIT, ixs->iph);
++
++ KLIPS_PRINT(debug_tunnel & DB_TN_CROUT,
++ "klips_debug:ipsec_xmit_strip_hard_header: "
++ "Original head,tailroom: %d,%d\n",
++ skb_headroom(ixs->skb), skb_tailroom(ixs->skb));
++
++ return IPSEC_XMIT_OK;
++}
++
++enum ipsec_xmit_value
++ipsec_tunnel_SAlookup(struct ipsec_xmit_state *ixs)
++{
++ /*
++ * First things first -- look us up in the erouting tables.
++ */
++ ixs->matcher.sen_len = sizeof (struct sockaddr_encap);
++ ixs->matcher.sen_family = AF_ENCAP;
++ ixs->matcher.sen_type = SENT_IP4;
++ ixs->matcher.sen_ip_src.s_addr = ixs->iph->saddr;
++ ixs->matcher.sen_ip_dst.s_addr = ixs->iph->daddr;
++ ixs->matcher.sen_proto = ixs->iph->protocol;
++ ipsec_extract_ports(ixs->iph, &ixs->matcher);
++
++ /*
++ * The spinlock is to prevent any other process from accessing or deleting
++ * the eroute while we are using and updating it.
++ */
++ spin_lock(&eroute_lock);
++
++ ixs->eroute = ipsec_findroute(&ixs->matcher);
++
++ if(ixs->iph->protocol == IPPROTO_UDP) {
++ if(ixs->skb->sk) {
++ ixs->sport=ntohs(ixs->skb->sk->sport);
++ ixs->dport=ntohs(ixs->skb->sk->dport);
++ } else if((ntohs(ixs->iph->frag_off) & IP_OFFSET) == 0 &&
++ ((ixs->skb->len - ixs->hard_header_len) >=
++ ((ixs->iph->ihl << 2) + sizeof(struct udphdr)))) {
++ ixs->sport=ntohs(((struct udphdr*)((caddr_t)ixs->iph+(ixs->iph->ihl<<2)))->source);
++ ixs->dport=ntohs(((struct udphdr*)((caddr_t)ixs->iph + (ixs->iph->ihl<<2)))->dest);
++ } else {
++ ixs->sport=0; ixs->dport=0;
++ }
++ }
++
++ /* default to a %drop eroute */
++ ixs->outgoing_said.proto = IPPROTO_INT;
++ ixs->outgoing_said.spi = htonl(SPI_DROP);
++ ixs->outgoing_said.dst.u.v4.sin_addr.s_addr = INADDR_ANY;
++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
++ "klips_debug:ipsec_xmit_SAlookup: "
++ "checking for local udp/500 IKE packet "
++ "saddr=%x, er=0p%p, daddr=%x, er_dst=%x, proto=%d sport=%d dport=%d\n",
++ ntohl((unsigned int)ixs->iph->saddr),
++ ixs->eroute,
++ ntohl((unsigned int)ixs->iph->daddr),
++ ixs->eroute ? ntohl((unsigned int)ixs->eroute->er_said.dst.u.v4.sin_addr.s_addr) : 0,
++ ixs->iph->protocol,
++ ixs->sport,
++ ixs->dport);
++
++ /*
++ * Quick cheat for now...are we udp/500? If so, let it through
++ * without interference since it is most likely an IKE packet.
++ */
++
++ if (ip_chk_addr((unsigned long)ixs->iph->saddr) == IS_MYADDR
++ && (!ixs->eroute
++ || ixs->iph->daddr == ixs->eroute->er_said.dst.u.v4.sin_addr.s_addr
++ || INADDR_ANY == ixs->eroute->er_said.dst.u.v4.sin_addr.s_addr)
++
++ && ((ixs->sport == 500) || (ixs->sport == 4500))) {
++ /* Whatever the eroute, this is an IKE message
++ * from us (i.e. not being forwarded).
++ * Furthermore, if there is a tunnel eroute,
++ * the destination is the peer for this eroute.
++ * So %pass the packet: modify the default %drop.
++ */
++ ixs->outgoing_said.spi = htonl(SPI_PASS);
++ if(!(ixs->skb->sk) && ((ntohs(ixs->iph->frag_off) & IP_MF) != 0)) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
++ "klips_debug:ipsec_xmit_SAlookup: "
++ "local UDP/500 (probably IKE) passthrough: base fragment, rest of fragments will probably get filtered.\n");
++ }
++ } else if (ixs->eroute) {
++ ixs->eroute->er_count++;
++ ixs->eroute->er_lasttime = jiffies/HZ;
++ if(ixs->eroute->er_said.proto==IPPROTO_INT
++ && ixs->eroute->er_said.spi==htonl(SPI_HOLD)) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
++ "klips_debug:ipsec_xmit_SAlookup: "
++ "shunt SA of HOLD: skb stored in HOLD.\n");
++ if(ixs->eroute->er_last != NULL) {
++ kfree_skb(ixs->eroute->er_last);
++ }
++ ixs->eroute->er_last = ixs->skb;
++ ixs->skb = NULL;
++ ixs->stats->tx_dropped++;
++ spin_unlock(&eroute_lock);
++ return IPSEC_XMIT_STOLEN;
++ }
++ ixs->outgoing_said = ixs->eroute->er_said;
++ ixs->eroute_pid = ixs->eroute->er_pid;
++ /* Copy of the ident for the TRAP/TRAPSUBNET eroutes */
++ if(ixs->outgoing_said.proto==IPPROTO_INT
++ && (ixs->outgoing_said.spi==htonl(SPI_TRAP)
++ || (ixs->outgoing_said.spi==htonl(SPI_TRAPSUBNET)))) {
++ int len;
++
++ ixs->ips.ips_ident_s.type = ixs->eroute->er_ident_s.type;
++ ixs->ips.ips_ident_s.id = ixs->eroute->er_ident_s.id;
++ ixs->ips.ips_ident_s.len = ixs->eroute->er_ident_s.len;
++ if (ixs->ips.ips_ident_s.len) {
++ len = ixs->ips.ips_ident_s.len * IPSEC_PFKEYv2_ALIGN - sizeof(struct sadb_ident);
++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
++ "klips_debug:ipsec_xmit_SAlookup: "
++ "allocating %d bytes for ident_s shunt SA of HOLD: skb stored in HOLD.\n",
++ len);
++ if ((ixs->ips.ips_ident_s.data = kmalloc(len, GFP_ATOMIC)) == NULL) {
++ printk(KERN_WARNING "klips_debug:ipsec_xmit_SAlookup: "
++ "Failed, tried to allocate %d bytes for source ident.\n",
++ len);
++ ixs->stats->tx_dropped++;
++ spin_unlock(&eroute_lock);
++ return IPSEC_XMIT_ERRMEMALLOC;
++ }
++ memcpy(ixs->ips.ips_ident_s.data, ixs->eroute->er_ident_s.data, len);
++ }
++ ixs->ips.ips_ident_d.type = ixs->eroute->er_ident_d.type;
++ ixs->ips.ips_ident_d.id = ixs->eroute->er_ident_d.id;
++ ixs->ips.ips_ident_d.len = ixs->eroute->er_ident_d.len;
++ if (ixs->ips.ips_ident_d.len) {
++ len = ixs->ips.ips_ident_d.len * IPSEC_PFKEYv2_ALIGN - sizeof(struct sadb_ident);
++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
++ "klips_debug:ipsec_xmit_SAlookup: "
++ "allocating %d bytes for ident_d shunt SA of HOLD: skb stored in HOLD.\n",
++ len);
++ if ((ixs->ips.ips_ident_d.data = kmalloc(len, GFP_ATOMIC)) == NULL) {
++ printk(KERN_WARNING "klips_debug:ipsec_xmit_SAlookup: "
++ "Failed, tried to allocate %d bytes for dest ident.\n",
++ len);
++ ixs->stats->tx_dropped++;
++ spin_unlock(&eroute_lock);
++ return IPSEC_XMIT_ERRMEMALLOC;
++ }
++ memcpy(ixs->ips.ips_ident_d.data, ixs->eroute->er_ident_d.data, len);
++ }
++ }
++ }
++
++ spin_unlock(&eroute_lock);
++ return IPSEC_XMIT_OK;
++}
++
++enum ipsec_xmit_value
++ipsec_tunnel_restore_hard_header(struct ipsec_xmit_state*ixs)
++{
++ KLIPS_PRINT(debug_tunnel & DB_TN_CROUT,
++ "klips_debug:ipsec_xmit_restore_hard_header: "
++ "After recursive xforms -- head,tailroom: %d,%d\n",
++ skb_headroom(ixs->skb),
++ skb_tailroom(ixs->skb));
++
++ if(ixs->saved_header) {
++ if(skb_headroom(ixs->skb) < ixs->hard_header_len) {
++ printk(KERN_WARNING
++ "klips_error:ipsec_xmit_restore_hard_header: "
++ "tried to skb_push hhlen=%d, %d available. This should never happen, please report.\n",
++ ixs->hard_header_len,
++ skb_headroom(ixs->skb));
++ ixs->stats->tx_errors++;
++ return IPSEC_XMIT_PUSHPULLERR;
++
++ }
++ skb_push(ixs->skb, ixs->hard_header_len);
++ {
++ int i;
++ for (i = 0; i < ixs->hard_header_len; i++) {
++ ixs->skb->data[i] = ixs->saved_header[i];
++ }
++ }
++ }
++#ifdef CONFIG_IPSEC_NAT_TRAVERSAL
++ if (ixs->natt_type && ixs->natt_head) {
++ struct iphdr *ipp = ixs->skb->nh.iph;
++ struct udphdr *udp;
++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
++ "klips_debug:ipsec_tunnel_start_xmit: "
++ "encapsuling packet into UDP (NAT-Traversal) (%d %d)\n",
++ ixs->natt_type, ixs->natt_head);
++
++ ixs->iphlen = ipp->ihl << 2;
++ ipp->tot_len =
++ htons(ntohs(ipp->tot_len) + ixs->natt_head);
++ if(skb_tailroom(ixs->skb) < ixs->natt_head) {
++ printk(KERN_WARNING "klips_error:ipsec_tunnel_start_xmit: "
++ "tried to skb_put %d, %d available. "
++ "This should never happen, please report.\n",
++ ixs->natt_head,
++ skb_tailroom(ixs->skb));
++ ixs->stats->tx_errors++;
++ return IPSEC_XMIT_ESPUDP;
++ }
++ skb_put(ixs->skb, ixs->natt_head);
++
++ udp = (struct udphdr *)((char *)ipp + ixs->iphlen);
++
++ /* move ESP hdr after UDP hdr */
++ memmove((void *)((char *)udp + ixs->natt_head),
++ (void *)(udp),
++ ntohs(ipp->tot_len) - ixs->iphlen - ixs->natt_head);
++
++ /* clear UDP & Non-IKE Markers (if any) */
++ memset(udp, 0, ixs->natt_head);
++
++ /* fill UDP with usefull informations ;-) */
++ udp->source = htons(ixs->natt_sport);
++ udp->dest = htons(ixs->natt_dport);
++ udp->len = htons(ntohs(ipp->tot_len) - ixs->iphlen);
++
++ /* set protocol */
++ ipp->protocol = IPPROTO_UDP;
++
++ /* fix IP checksum */
++ ipp->check = 0;
++ ipp->check = ip_fast_csum((unsigned char *)ipp, ipp->ihl);
++ }
++#endif
++ KLIPS_PRINT(debug_tunnel & DB_TN_CROUT,
++ "klips_debug:ipsec_xmit_restore_hard_header: "
++ "With hard_header, final head,tailroom: %d,%d\n",
++ skb_headroom(ixs->skb),
++ skb_tailroom(ixs->skb));
++
++ return IPSEC_XMIT_OK;
++}
++
++enum ipsec_xmit_value
++ipsec_tunnel_send(struct ipsec_xmit_state*ixs)
++{
++#ifdef NETDEV_25
++ struct flowi fl;
++#endif
++
++#ifdef NET_21 /* 2.2 and 2.4 kernels */
++ /* new route/dst cache code from James Morris */
++ ixs->skb->dev = ixs->physdev;
++#ifdef NETDEV_25
++ fl.oif = ixs->physdev->iflink;
++ fl.nl_u.ip4_u.daddr = ixs->skb->nh.iph->daddr;
++ fl.nl_u.ip4_u.saddr = ixs->pass ? 0 : ixs->skb->nh.iph->saddr;
++ fl.nl_u.ip4_u.tos = RT_TOS(ixs->skb->nh.iph->tos);
++ fl.proto = ixs->skb->nh.iph->protocol;
++ if ((ixs->error = ip_route_output_key(&ixs->route, &fl))) {
++#else
++ /*skb_orphan(ixs->skb);*/
++ if((ixs->error = ip_route_output(&ixs->route,
++ ixs->skb->nh.iph->daddr,
++ ixs->pass ? 0 : ixs->skb->nh.iph->saddr,
++ RT_TOS(ixs->skb->nh.iph->tos),
++ /* mcr->rgb: should this be 0 instead? */
++ ixs->physdev->iflink))) {
++#endif
++ ixs->stats->tx_errors++;
++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
++ "klips_debug:ipsec_xmit_send: "
++ "ip_route_output failed with error code %d, rt->u.dst.dev=%s, dropped\n",
++ ixs->error,
++ ixs->route->u.dst.dev->name);
++ return IPSEC_XMIT_ROUTEERR;
++ }
++ if(ixs->dev == ixs->route->u.dst.dev) {
++ ip_rt_put(ixs->route);
++ /* This is recursion, drop it. */
++ ixs->stats->tx_errors++;
++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
++ "klips_debug:ipsec_xmit_send: "
++ "suspect recursion, dev=rt->u.dst.dev=%s, dropped\n",
++ ixs->dev->name);
++ return IPSEC_XMIT_RECURSDETECT;
++ }
++ dst_release(ixs->skb->dst);
++ ixs->skb->dst = &ixs->route->u.dst;
++ ixs->stats->tx_bytes += ixs->skb->len;
++ if(ixs->skb->len < ixs->skb->nh.raw - ixs->skb->data) {
++ ixs->stats->tx_errors++;
++ printk(KERN_WARNING
++ "klips_error:ipsec_xmit_send: "
++ "tried to __skb_pull nh-data=%ld, %d available. This should never happen, please report.\n",
++ (unsigned long)(ixs->skb->nh.raw - ixs->skb->data),
++ ixs->skb->len);
++ return IPSEC_XMIT_PUSHPULLERR;
++ }
++ __skb_pull(ixs->skb, ixs->skb->nh.raw - ixs->skb->data);
++#ifdef SKB_RESET_NFCT
++ if(!ixs->pass) {
++ nf_conntrack_put(ixs->skb->nfct);
++ ixs->skb->nfct = NULL;
++ }
++#ifdef CONFIG_NETFILTER_DEBUG
++ ixs->skb->nf_debug = 0;
++#endif /* CONFIG_NETFILTER_DEBUG */
++#endif /* SKB_RESET_NFCT */
++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
++ "klips_debug:ipsec_xmit_send: "
++ "...done, calling ip_send() on device:%s\n",
++ ixs->skb->dev ? ixs->skb->dev->name : "NULL");
++ KLIPS_IP_PRINT(debug_tunnel & DB_TN_XMIT, ixs->skb->nh.iph);
++#ifdef NETDEV_23 /* 2.4 kernels */
++ {
++ int err;
++
++ err = NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, ixs->skb, NULL, ixs->route->u.dst.dev,
++ ipsec_tunnel_xmit2);
++ if(err != NET_XMIT_SUCCESS && err != NET_XMIT_CN) {
++ if(net_ratelimit())
++ printk(KERN_ERR
++ "klips_error:ipsec_xmit_send: "
++ "ip_send() failed, err=%d\n",
++ -err);
++ ixs->stats->tx_errors++;
++ ixs->stats->tx_aborted_errors++;
++ ixs->skb = NULL;
++ return IPSEC_XMIT_IPSENDFAILURE;
++ }
++ }
++#else /* NETDEV_23 */ /* 2.2 kernels */
++ ip_send(ixs->skb);
++#endif /* NETDEV_23 */
++#else /* NET_21 */ /* 2.0 kernels */
++ ixs->skb->arp = 1;
++ /* ISDN/ASYNC PPP from Matjaz Godec. */
++ /* skb->protocol = htons(ETH_P_IP); */
++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
++ "klips_debug:ipsec_xmit_send: "
++ "...done, calling dev_queue_xmit() or ip_fragment().\n");
++ IP_SEND(ixs->skb, ixs->physdev);
++#endif /* NET_21 */
++ ixs->stats->tx_packets++;
++
++ ixs->skb = NULL;
++
++ return IPSEC_XMIT_OK;
++}
++
++void
++ipsec_tunnel_cleanup(struct ipsec_xmit_state*ixs)
++{
++#if defined(HAS_NETIF_QUEUE) || defined (HAVE_NETIF_QUEUE)
++ netif_wake_queue(ixs->dev);
++#else /* defined(HAS_NETIF_QUEUE) || defined (HAVE_NETIF_QUEUE) */
++ ixs->dev->tbusy = 0;
++#endif /* defined(HAS_NETIF_QUEUE) || defined (HAVE_NETIF_QUEUE) */
++ if(ixs->saved_header) {
++ kfree(ixs->saved_header);
++ }
++ if(ixs->skb) {
++ dev_kfree_skb(ixs->skb, FREE_WRITE);
++ }
++ if(ixs->oskb) {
++ dev_kfree_skb(ixs->oskb, FREE_WRITE);
++ }
++ if (ixs->ips.ips_ident_s.data) {
++ kfree(ixs->ips.ips_ident_s.data);
++ }
++ if (ixs->ips.ips_ident_d.data) {
++ kfree(ixs->ips.ips_ident_d.data);
++ }
++}
++
++/*
++ * This function assumes it is being called from dev_queue_xmit()
++ * and that skb is filled properly by that function.
++ */
++int
++ipsec_tunnel_start_xmit(struct sk_buff *skb, struct device *dev)
++{
++ struct ipsec_xmit_state ixs_mem;
++ struct ipsec_xmit_state *ixs = &ixs_mem;
++ enum ipsec_xmit_value stat;
++
++#ifdef CONFIG_IPSEC_NAT_TRAVERSAL
++ ixs->natt_type = 0, ixs->natt_head = 0;
++ ixs->natt_sport = 0, ixs->natt_dport = 0;
++#endif
++
++ memset((caddr_t)ixs, 0, sizeof(*ixs));
++ ixs->oskb = NULL;
++ ixs->saved_header = NULL; /* saved copy of the hard header */
++ ixs->route = NULL;
++ memset((caddr_t)&(ixs->ips), 0, sizeof(ixs->ips));
++ ixs->dev = dev;
++ ixs->skb = skb;
++
++ stat = ipsec_xmit_sanity_check_dev(ixs);
++ if(stat != IPSEC_XMIT_OK) {
++ goto cleanup;
++ }
++
++ stat = ipsec_xmit_sanity_check_skb(ixs);
++ if(stat != IPSEC_XMIT_OK) {
++ goto cleanup;
++ }
++
++ stat = ipsec_tunnel_strip_hard_header(ixs);
++ if(stat != IPSEC_XMIT_OK) {
++ goto cleanup;
++ }
++
++ stat = ipsec_tunnel_SAlookup(ixs);
++ if(stat != IPSEC_XMIT_OK) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
++ "klips_debug:ipsec_tunnel_start_xmit: SAlookup failed: %d\n",
++ stat);
++ goto cleanup;
++ }
++
++ ixs->innersrc = ixs->iph->saddr;
++ /* start encapsulation loop here XXX */
++ do {
++ stat = ipsec_xmit_encap_bundle(ixs);
++ if(stat != IPSEC_XMIT_OK) {
++ if(stat == IPSEC_XMIT_PASS) {
++ goto bypass;
++ }
++
++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
++ "klips_debug:ipsec_tunnel_start_xmit: encap_bundle failed: %d\n",
++ stat);
++ goto cleanup;
++ }
++
++ ixs->matcher.sen_ip_src.s_addr = ixs->iph->saddr;
++ ixs->matcher.sen_ip_dst.s_addr = ixs->iph->daddr;
++ ixs->matcher.sen_proto = ixs->iph->protocol;
++ ipsec_extract_ports(ixs->iph, &ixs->matcher);
++
++ spin_lock(&eroute_lock);
++ ixs->eroute = ipsec_findroute(&ixs->matcher);
++ if(ixs->eroute) {
++ ixs->outgoing_said = ixs->eroute->er_said;
++ ixs->eroute_pid = ixs->eroute->er_pid;
++ ixs->eroute->er_count++;
++ ixs->eroute->er_lasttime = jiffies/HZ;
++ }
++ spin_unlock(&eroute_lock);
++
++ KLIPS_PRINT((debug_tunnel & DB_TN_XMIT) &&
++ /* ((ixs->orgdst != ixs->newdst) || (ixs->orgsrc != ixs->newsrc)) */
++ (ixs->orgedst != ixs->outgoing_said.dst.u.v4.sin_addr.s_addr) &&
++ ixs->outgoing_said.dst.u.v4.sin_addr.s_addr &&
++ ixs->eroute,
++ "klips_debug:ipsec_tunnel_start_xmit: "
++ "We are recursing here.\n");
++
++ } while(/*((ixs->orgdst != ixs->newdst) || (ixs->orgsrc != ixs->newsrc))*/
++ (ixs->orgedst != ixs->outgoing_said.dst.u.v4.sin_addr.s_addr) &&
++ ixs->outgoing_said.dst.u.v4.sin_addr.s_addr &&
++ ixs->eroute);
++
++ stat = ipsec_tunnel_restore_hard_header(ixs);
++ if(stat != IPSEC_XMIT_OK) {
++ goto cleanup;
++ }
++
++ bypass:
++ stat = ipsec_tunnel_send(ixs);
++
++ cleanup:
++ ipsec_tunnel_cleanup(ixs);
++
++ return 0;
++}
++
++DEBUG_NO_STATIC struct net_device_stats *
++ipsec_tunnel_get_stats(struct device *dev)
++{
++ return &(((struct ipsecpriv *)(dev->priv))->mystats);
++}
++
++/*
++ * Revectored calls.
++ * For each of these calls, a field exists in our private structure.
++ */
++
++DEBUG_NO_STATIC int
++ipsec_tunnel_hard_header(struct sk_buff *skb, struct device *dev,
++ unsigned short type, void *daddr, void *saddr, unsigned len)
++{
++ struct ipsecpriv *prv = dev->priv;
++ struct device *tmp;
++ int ret;
++ struct net_device_stats *stats; /* This device's statistics */
++
++ if(skb == NULL) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
++ "klips_debug:ipsec_tunnel_hard_header: "
++ "no skb...\n");
++ return -ENODATA;
++ }
++
++ if(dev == NULL) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
++ "klips_debug:ipsec_tunnel_hard_header: "
++ "no device...\n");
++ return -ENODEV;
++ }
++
++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
++ "klips_debug:ipsec_tunnel_hard_header: "
++ "skb->dev=%s dev=%s.\n",
++ skb->dev ? skb->dev->name : "NULL",
++ dev->name);
++
++ if(prv == NULL) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
++ "klips_debug:ipsec_tunnel_hard_header: "
++ "no private space associated with dev=%s\n",
++ dev->name ? dev->name : "NULL");
++ return -ENODEV;
++ }
++
++ stats = (struct net_device_stats *) &(prv->mystats);
++
++ if(prv->dev == NULL) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
++ "klips_debug:ipsec_tunnel_hard_header: "
++ "no physical device associated with dev=%s\n",
++ dev->name ? dev->name : "NULL");
++ stats->tx_dropped++;
++ return -ENODEV;
++ }
++
++ /* check if we have to send a IPv6 packet. It might be a Router
++ Solicitation, where the building of the packet happens in
++ reverse order:
++ 1. ll hdr,
++ 2. IPv6 hdr,
++ 3. ICMPv6 hdr
++ -> skb->nh.raw is still uninitialized when this function is
++ called!! If this is no IPv6 packet, we can print debugging
++ messages, otherwise we skip all debugging messages and just
++ build the ll header */
++ if(type != ETH_P_IPV6) {
++ /* execute this only, if we don't have to build the
++ header for a IPv6 packet */
++ if(!prv->hard_header) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
++ "klips_debug:ipsec_tunnel_hard_header: "
++ "physical device has been detached, packet dropped 0p%p->0p%p len=%d type=%d dev=%s->NULL ",
++ saddr,
++ daddr,
++ len,
++ type,
++ dev->name);
++#ifdef NET_21
++ KLIPS_PRINTMORE(debug_tunnel & DB_TN_REVEC,
++ "ip=%08x->%08x\n",
++ (__u32)ntohl(skb->nh.iph->saddr),
++ (__u32)ntohl(skb->nh.iph->daddr) );
++#else /* NET_21 */
++ KLIPS_PRINTMORE(debug_tunnel & DB_TN_REVEC,
++ "ip=%08x->%08x\n",
++ (__u32)ntohl(skb->ip_hdr->saddr),
++ (__u32)ntohl(skb->ip_hdr->daddr) );
++#endif /* NET_21 */
++ stats->tx_dropped++;
++ return -ENODEV;
++ }
++
++#define da ((struct device *)(prv->dev))->dev_addr
++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
++ "klips_debug:ipsec_tunnel_hard_header: "
++ "Revectored 0p%p->0p%p len=%d type=%d dev=%s->%s dev_addr=%02x:%02x:%02x:%02x:%02x:%02x ",
++ saddr,
++ daddr,
++ len,
++ type,
++ dev->name,
++ prv->dev->name,
++ da[0], da[1], da[2], da[3], da[4], da[5]);
++#ifdef NET_21
++ KLIPS_PRINTMORE(debug_tunnel & DB_TN_REVEC,
++ "ip=%08x->%08x\n",
++ (__u32)ntohl(skb->nh.iph->saddr),
++ (__u32)ntohl(skb->nh.iph->daddr) );
++#else /* NET_21 */
++ KLIPS_PRINTMORE(debug_tunnel & DB_TN_REVEC,
++ "ip=%08x->%08x\n",
++ (__u32)ntohl(skb->ip_hdr->saddr),
++ (__u32)ntohl(skb->ip_hdr->daddr) );
++#endif /* NET_21 */
++ } else {
++ KLIPS_PRINT(debug_tunnel,
++ "klips_debug:ipsec_tunnel_hard_header: "
++ "is IPv6 packet, skip debugging messages, only revector and build linklocal header.\n");
++ }
++ tmp = skb->dev;
++ skb->dev = prv->dev;
++ ret = prv->hard_header(skb, prv->dev, type, (void *)daddr, (void *)saddr, len);
++ skb->dev = tmp;
++ return ret;
++}
++
++DEBUG_NO_STATIC int
++#ifdef NET_21
++ipsec_tunnel_rebuild_header(struct sk_buff *skb)
++#else /* NET_21 */
++ipsec_tunnel_rebuild_header(void *buff, struct device *dev,
++ unsigned long raddr, struct sk_buff *skb)
++#endif /* NET_21 */
++{
++ struct ipsecpriv *prv = skb->dev->priv;
++ struct device *tmp;
++ int ret;
++ struct net_device_stats *stats; /* This device's statistics */
++
++ if(skb->dev == NULL) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
++ "klips_debug:ipsec_tunnel_rebuild_header: "
++ "no device...");
++ return -ENODEV;
++ }
++
++ if(prv == NULL) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
++ "klips_debug:ipsec_tunnel_rebuild_header: "
++ "no private space associated with dev=%s",
++ skb->dev->name ? skb->dev->name : "NULL");
++ return -ENODEV;
++ }
++
++ stats = (struct net_device_stats *) &(prv->mystats);
++
++ if(prv->dev == NULL) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
++ "klips_debug:ipsec_tunnel_rebuild_header: "
++ "no physical device associated with dev=%s",
++ skb->dev->name ? skb->dev->name : "NULL");
++ stats->tx_dropped++;
++ return -ENODEV;
++ }
++
++ if(!prv->rebuild_header) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
++ "klips_debug:ipsec_tunnel_rebuild_header: "
++ "physical device has been detached, packet dropped skb->dev=%s->NULL ",
++ skb->dev->name);
++#ifdef NET_21
++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
++ "ip=%08x->%08x\n",
++ (__u32)ntohl(skb->nh.iph->saddr),
++ (__u32)ntohl(skb->nh.iph->daddr) );
++#else /* NET_21 */
++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
++ "ip=%08x->%08x\n",
++ (__u32)ntohl(skb->ip_hdr->saddr),
++ (__u32)ntohl(skb->ip_hdr->daddr) );
++#endif /* NET_21 */
++ stats->tx_dropped++;
++ return -ENODEV;
++ }
++
++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
++ "klips_debug:ipsec_tunnel: "
++ "Revectored rebuild_header dev=%s->%s ",
++ skb->dev->name, prv->dev->name);
++#ifdef NET_21
++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
++ "ip=%08x->%08x\n",
++ (__u32)ntohl(skb->nh.iph->saddr),
++ (__u32)ntohl(skb->nh.iph->daddr) );
++#else /* NET_21 */
++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
++ "ip=%08x->%08x\n",
++ (__u32)ntohl(skb->ip_hdr->saddr),
++ (__u32)ntohl(skb->ip_hdr->daddr) );
++#endif /* NET_21 */
++ tmp = skb->dev;
++ skb->dev = prv->dev;
++
++#ifdef NET_21
++ ret = prv->rebuild_header(skb);
++#else /* NET_21 */
++ ret = prv->rebuild_header(buff, prv->dev, raddr, skb);
++#endif /* NET_21 */
++ skb->dev = tmp;
++ return ret;
++}
++
++DEBUG_NO_STATIC int
++ipsec_tunnel_set_mac_address(struct device *dev, void *addr)
++{
++ struct ipsecpriv *prv = dev->priv;
++
++ struct net_device_stats *stats; /* This device's statistics */
++
++ if(dev == NULL) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
++ "klips_debug:ipsec_tunnel_set_mac_address: "
++ "no device...");
++ return -ENODEV;
++ }
++
++ if(prv == NULL) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
++ "klips_debug:ipsec_tunnel_set_mac_address: "
++ "no private space associated with dev=%s",
++ dev->name ? dev->name : "NULL");
++ return -ENODEV;
++ }
++
++ stats = (struct net_device_stats *) &(prv->mystats);
++
++ if(prv->dev == NULL) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
++ "klips_debug:ipsec_tunnel_set_mac_address: "
++ "no physical device associated with dev=%s",
++ dev->name ? dev->name : "NULL");
++ stats->tx_dropped++;
++ return -ENODEV;
++ }
++
++ if(!prv->set_mac_address) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
++ "klips_debug:ipsec_tunnel_set_mac_address: "
++ "physical device has been detached, cannot set - skb->dev=%s->NULL\n",
++ dev->name);
++ return -ENODEV;
++ }
++
++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
++ "klips_debug:ipsec_tunnel_set_mac_address: "
++ "Revectored dev=%s->%s addr=0p%p\n",
++ dev->name, prv->dev->name, addr);
++ return prv->set_mac_address(prv->dev, addr);
++
++}
++
++#ifndef NET_21
++DEBUG_NO_STATIC void
++ipsec_tunnel_cache_bind(struct hh_cache **hhp, struct device *dev,
++ unsigned short htype, __u32 daddr)
++{
++ struct ipsecpriv *prv = dev->priv;
++
++ struct net_device_stats *stats; /* This device's statistics */
++
++ if(dev == NULL) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
++ "klips_debug:ipsec_tunnel_cache_bind: "
++ "no device...");
++ return;
++ }
++
++ if(prv == NULL) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
++ "klips_debug:ipsec_tunnel_cache_bind: "
++ "no private space associated with dev=%s",
++ dev->name ? dev->name : "NULL");
++ return;
++ }
++
++ stats = (struct net_device_stats *) &(prv->mystats);
++
++ if(prv->dev == NULL) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
++ "klips_debug:ipsec_tunnel_cache_bind: "
++ "no physical device associated with dev=%s",
++ dev->name ? dev->name : "NULL");
++ stats->tx_dropped++;
++ return;
++ }
++
++ if(!prv->header_cache_bind) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
++ "klips_debug:ipsec_tunnel_cache_bind: "
++ "physical device has been detached, cannot set - skb->dev=%s->NULL\n",
++ dev->name);
++ stats->tx_dropped++;
++ return;
++ }
++
++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
++ "klips_debug:ipsec_tunnel_cache_bind: "
++ "Revectored \n");
++ prv->header_cache_bind(hhp, prv->dev, htype, daddr);
++ return;
++}
++#endif /* !NET_21 */
++
++
++DEBUG_NO_STATIC void
++ipsec_tunnel_cache_update(struct hh_cache *hh, struct device *dev, unsigned char * haddr)
++{
++ struct ipsecpriv *prv = dev->priv;
++
++ struct net_device_stats *stats; /* This device's statistics */
++
++ if(dev == NULL) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
++ "klips_debug:ipsec_tunnel_cache_update: "
++ "no device...");
++ return;
++ }
++
++ if(prv == NULL) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
++ "klips_debug:ipsec_tunnel_cache_update: "
++ "no private space associated with dev=%s",
++ dev->name ? dev->name : "NULL");
++ return;
++ }
++
++ stats = (struct net_device_stats *) &(prv->mystats);
++
++ if(prv->dev == NULL) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
++ "klips_debug:ipsec_tunnel_cache_update: "
++ "no physical device associated with dev=%s",
++ dev->name ? dev->name : "NULL");
++ stats->tx_dropped++;
++ return;
++ }
++
++ if(!prv->header_cache_update) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
++ "klips_debug:ipsec_tunnel_cache_update: "
++ "physical device has been detached, cannot set - skb->dev=%s->NULL\n",
++ dev->name);
++ return;
++ }
++
++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
++ "klips_debug:ipsec_tunnel: "
++ "Revectored cache_update\n");
++ prv->header_cache_update(hh, prv->dev, haddr);
++ return;
++}
++
++#ifdef NET_21
++DEBUG_NO_STATIC int
++ipsec_tunnel_neigh_setup(struct neighbour *n)
++{
++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
++ "klips_debug:ipsec_tunnel_neigh_setup:\n");
++
++ if (n->nud_state == NUD_NONE) {
++ n->ops = &arp_broken_ops;
++ n->output = n->ops->output;
++ }
++ return 0;
++}
++
++DEBUG_NO_STATIC int
++ipsec_tunnel_neigh_setup_dev(struct device *dev, struct neigh_parms *p)
++{
++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
++ "klips_debug:ipsec_tunnel_neigh_setup_dev: "
++ "setting up %s\n",
++ dev ? dev->name : "NULL");
++
++ if (p->tbl->family == AF_INET) {
++ p->neigh_setup = ipsec_tunnel_neigh_setup;
++ p->ucast_probes = 0;
++ p->mcast_probes = 0;
++ }
++ return 0;
++}
++#endif /* NET_21 */
++
++/*
++ * We call the attach routine to attach another device.
++ */
++
++DEBUG_NO_STATIC int
++ipsec_tunnel_attach(struct device *dev, struct device *physdev)
++{
++ int i;
++ struct ipsecpriv *prv = dev->priv;
++
++ if(dev == NULL) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
++ "klips_debug:ipsec_tunnel_attach: "
++ "no device...");
++ return -ENODEV;
++ }
++
++ if(prv == NULL) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
++ "klips_debug:ipsec_tunnel_attach: "
++ "no private space associated with dev=%s",
++ dev->name ? dev->name : "NULL");
++ return -ENODATA;
++ }
++
++ prv->dev = physdev;
++ prv->hard_start_xmit = physdev->hard_start_xmit;
++ prv->get_stats = physdev->get_stats;
++
++ if (physdev->hard_header) {
++ prv->hard_header = physdev->hard_header;
++ dev->hard_header = ipsec_tunnel_hard_header;
++ } else
++ dev->hard_header = NULL;
++
++ if (physdev->rebuild_header) {
++ prv->rebuild_header = physdev->rebuild_header;
++ dev->rebuild_header = ipsec_tunnel_rebuild_header;
++ } else
++ dev->rebuild_header = NULL;
++
++ if (physdev->set_mac_address) {
++ prv->set_mac_address = physdev->set_mac_address;
++ dev->set_mac_address = ipsec_tunnel_set_mac_address;
++ } else
++ dev->set_mac_address = NULL;
++
++#ifndef NET_21
++ if (physdev->header_cache_bind) {
++ prv->header_cache_bind = physdev->header_cache_bind;
++ dev->header_cache_bind = ipsec_tunnel_cache_bind;
++ } else
++ dev->header_cache_bind = NULL;
++#endif /* !NET_21 */
++
++ if (physdev->header_cache_update) {
++ prv->header_cache_update = physdev->header_cache_update;
++ dev->header_cache_update = ipsec_tunnel_cache_update;
++ } else
++ dev->header_cache_update = NULL;
++
++ dev->hard_header_len = physdev->hard_header_len;
++
++#ifdef NET_21
++/* prv->neigh_setup = physdev->neigh_setup; */
++ dev->neigh_setup = ipsec_tunnel_neigh_setup_dev;
++#endif /* NET_21 */
++ dev->mtu = 16260; /* 0xfff0; */ /* dev->mtu; */
++ prv->mtu = physdev->mtu;
++
++#ifdef PHYSDEV_TYPE
++ dev->type = physdev->type; /* ARPHRD_TUNNEL; */
++#endif /* PHYSDEV_TYPE */
++
++ dev->addr_len = physdev->addr_len;
++ for (i=0; i<dev->addr_len; i++) {
++ dev->dev_addr[i] = physdev->dev_addr[i];
++ }
++#ifdef CONFIG_IPSEC_DEBUG
++ if(debug_tunnel & DB_TN_INIT) {
++ printk(KERN_INFO "klips_debug:ipsec_tunnel_attach: "
++ "physical device %s being attached has HW address: %2x",
++ physdev->name, physdev->dev_addr[0]);
++ for (i=1; i < physdev->addr_len; i++) {
++ printk(":%02x", physdev->dev_addr[i]);
++ }
++ printk("\n");
++ }
++#endif /* CONFIG_IPSEC_DEBUG */
++
++ return 0;
++}
++
++/*
++ * We call the detach routine to detach the ipsec tunnel from another device.
++ */
++
++DEBUG_NO_STATIC int
++ipsec_tunnel_detach(struct device *dev)
++{
++ int i;
++ struct ipsecpriv *prv = dev->priv;
++
++ if(dev == NULL) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
++ "klips_debug:ipsec_tunnel_detach: "
++ "no device...");
++ return -ENODEV;
++ }
++
++ if(prv == NULL) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
++ "klips_debug:ipsec_tunnel_detach: "
++ "no private space associated with dev=%s",
++ dev->name ? dev->name : "NULL");
++ return -ENODATA;
++ }
++
++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
++ "klips_debug:ipsec_tunnel_detach: "
++ "physical device %s being detached from virtual device %s\n",
++ prv->dev ? prv->dev->name : "NULL",
++ dev->name);
++
++ ipsec_dev_put(prv->dev);
++ prv->dev = NULL;
++ prv->hard_start_xmit = NULL;
++ prv->get_stats = NULL;
++
++ prv->hard_header = NULL;
++#ifdef DETACH_AND_DOWN
++ dev->hard_header = NULL;
++#endif /* DETACH_AND_DOWN */
++
++ prv->rebuild_header = NULL;
++#ifdef DETACH_AND_DOWN
++ dev->rebuild_header = NULL;
++#endif /* DETACH_AND_DOWN */
++
++ prv->set_mac_address = NULL;
++#ifdef DETACH_AND_DOWN
++ dev->set_mac_address = NULL;
++#endif /* DETACH_AND_DOWN */
++
++#ifndef NET_21
++ prv->header_cache_bind = NULL;
++#ifdef DETACH_AND_DOWN
++ dev->header_cache_bind = NULL;
++#endif /* DETACH_AND_DOWN */
++#endif /* !NET_21 */
++
++ prv->header_cache_update = NULL;
++#ifdef DETACH_AND_DOWN
++ dev->header_cache_update = NULL;
++#endif /* DETACH_AND_DOWN */
++
++#ifdef NET_21
++/* prv->neigh_setup = NULL; */
++#ifdef DETACH_AND_DOWN
++ dev->neigh_setup = NULL;
++#endif /* DETACH_AND_DOWN */
++#endif /* NET_21 */
++ dev->hard_header_len = 0;
++#ifdef DETACH_AND_DOWN
++ dev->mtu = 0;
++#endif /* DETACH_AND_DOWN */
++ prv->mtu = 0;
++ for (i=0; i<MAX_ADDR_LEN; i++) {
++ dev->dev_addr[i] = 0;
++ }
++ dev->addr_len = 0;
++#ifdef PHYSDEV_TYPE
++ dev->type = ARPHRD_VOID; /* ARPHRD_TUNNEL; */
++#endif /* PHYSDEV_TYPE */
++
++ return 0;
++}
++
++/*
++ * We call the clear routine to detach all ipsec tunnels from other devices.
++ */
++DEBUG_NO_STATIC int
++ipsec_tunnel_clear(void)
++{
++ int i;
++ struct device *ipsecdev = NULL, *prvdev;
++ struct ipsecpriv *prv;
++ char name[9];
++ int ret;
++
++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
++ "klips_debug:ipsec_tunnel_clear: .\n");
++
++ for(i = 0; i < IPSEC_NUM_IF; i++) {
++ ipsecdev = ipsecdevices[i];
++ if(ipsecdev != NULL) {
++ if((prv = (struct ipsecpriv *)(ipsecdev->priv))) {
++ prvdev = (struct device *)(prv->dev);
++ if(prvdev) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
++ "klips_debug:ipsec_tunnel_clear: "
++ "physical device for device %s is %s\n",
++ name, prvdev->name);
++ if((ret = ipsec_tunnel_detach(ipsecdev))) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
++ "klips_debug:ipsec_tunnel_clear: "
++ "error %d detatching device %s from device %s.\n",
++ ret, name, prvdev->name);
++ return ret;
++ }
++ }
++ }
++ }
++ }
++ return 0;
++}
++
++DEBUG_NO_STATIC int
++ipsec_tunnel_ioctl(struct device *dev, struct ifreq *ifr, int cmd)
++{
++ struct ipsectunnelconf *cf = (struct ipsectunnelconf *)&ifr->ifr_data;
++ struct ipsecpriv *prv = dev->priv;
++ struct device *them; /* physical device */
++#ifdef CONFIG_IP_ALIAS
++ char *colon;
++ char realphysname[IFNAMSIZ];
++#endif /* CONFIG_IP_ALIAS */
++
++ if(dev == NULL) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
++ "klips_debug:ipsec_tunnel_ioctl: "
++ "device not supplied.\n");
++ return -ENODEV;
++ }
++
++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
++ "klips_debug:ipsec_tunnel_ioctl: "
++ "tncfg service call #%d for dev=%s\n",
++ cmd,
++ dev->name ? dev->name : "NULL");
++ switch (cmd) {
++ /* attach a virtual ipsec? device to a physical device */
++ case IPSEC_SET_DEV:
++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
++ "klips_debug:ipsec_tunnel_ioctl: "
++ "calling ipsec_tunnel_attatch...\n");
++#ifdef CONFIG_IP_ALIAS
++ /* If this is an IP alias interface, get its real physical name */
++ strncpy(realphysname, cf->cf_name, IFNAMSIZ);
++ realphysname[IFNAMSIZ-1] = 0;
++ colon = strchr(realphysname, ':');
++ if (colon) *colon = 0;
++ them = ipsec_dev_get(realphysname);
++#else /* CONFIG_IP_ALIAS */
++ them = ipsec_dev_get(cf->cf_name);
++#endif /* CONFIG_IP_ALIAS */
++
++ if (them == NULL) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
++ "klips_debug:ipsec_tunnel_ioctl: "
++ "physical device %s requested is null\n",
++ cf->cf_name);
++ ipsec_dev_put(them);
++ return -ENXIO;
++ }
++
++#if 0
++ if (them->flags & IFF_UP) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
++ "klips_debug:ipsec_tunnel_ioctl: "
++ "physical device %s requested is not up.\n",
++ cf->cf_name);
++ ipsec_dev_put(them);
++ return -ENXIO;
++ }
++#endif
++
++ if (prv && prv->dev) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
++ "klips_debug:ipsec_tunnel_ioctl: "
++ "virtual device is already connected to %s.\n",
++ prv->dev->name ? prv->dev->name : "NULL");
++ ipsec_dev_put(them);
++ return -EBUSY;
++ }
++ return ipsec_tunnel_attach(dev, them);
++
++ case IPSEC_DEL_DEV:
++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
++ "klips_debug:ipsec_tunnel_ioctl: "
++ "calling ipsec_tunnel_detatch.\n");
++ if (! prv->dev) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
++ "klips_debug:ipsec_tunnel_ioctl: "
++ "physical device not connected.\n");
++ return -ENODEV;
++ }
++ return ipsec_tunnel_detach(dev);
++
++ case IPSEC_CLR_DEV:
++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
++ "klips_debug:ipsec_tunnel_ioctl: "
++ "calling ipsec_tunnel_clear.\n");
++ return ipsec_tunnel_clear();
++
++ default:
++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
++ "klips_debug:ipsec_tunnel_ioctl: "
++ "unknown command %d.\n",
++ cmd);
++ return -EOPNOTSUPP;
++ }
++}
++
++int
++ipsec_device_event(struct notifier_block *unused, unsigned long event, void *ptr)
++{
++ struct device *dev = ptr;
++ struct device *ipsec_dev;
++ struct ipsecpriv *priv;
++ int i;
++
++ if (dev == NULL) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
++ "klips_debug:ipsec_device_event: "
++ "dev=NULL for event type %ld.\n",
++ event);
++ return(NOTIFY_DONE);
++ }
++
++ /* check for loopback devices */
++ if (dev && (dev->flags & IFF_LOOPBACK)) {
++ return(NOTIFY_DONE);
++ }
++
++ switch (event) {
++ case NETDEV_DOWN:
++ /* look very carefully at the scope of these compiler
++ directives before changing anything... -- RGB */
++#ifdef NET_21
++ case NETDEV_UNREGISTER:
++ switch (event) {
++ case NETDEV_DOWN:
++#endif /* NET_21 */
++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
++ "klips_debug:ipsec_device_event: "
++ "NETDEV_DOWN dev=%s flags=%x\n",
++ dev->name,
++ dev->flags);
++ if(strncmp(dev->name, "ipsec", strlen("ipsec")) == 0) {
++ printk(KERN_CRIT "IPSEC EVENT: KLIPS device %s shut down.\n",
++ dev->name);
++ }
++#ifdef NET_21
++ break;
++ case NETDEV_UNREGISTER:
++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
++ "klips_debug:ipsec_device_event: "
++ "NETDEV_UNREGISTER dev=%s flags=%x\n",
++ dev->name,
++ dev->flags);
++ break;
++ }
++#endif /* NET_21 */
++
++ /* find the attached physical device and detach it. */
++ for(i = 0; i < IPSEC_NUM_IF; i++) {
++ ipsec_dev = ipsecdevices[i];
++
++ if(ipsec_dev) {
++ priv = (struct ipsecpriv *)(ipsec_dev->priv);
++ if(priv) {
++ ;
++ if(((struct device *)(priv->dev)) == dev) {
++ /* dev_close(ipsec_dev); */
++ /* return */ ipsec_tunnel_detach(ipsec_dev);
++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
++ "klips_debug:ipsec_device_event: "
++ "device '%s' has been detached.\n",
++ ipsec_dev->name);
++ break;
++ }
++ } else {
++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
++ "klips_debug:ipsec_device_event: "
++ "device '%s' has no private data space!\n",
++ ipsec_dev->name);
++ }
++ }
++ }
++ break;
++ case NETDEV_UP:
++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
++ "klips_debug:ipsec_device_event: "
++ "NETDEV_UP dev=%s\n",
++ dev->name);
++ break;
++#ifdef NET_21
++ case NETDEV_REBOOT:
++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
++ "klips_debug:ipsec_device_event: "
++ "NETDEV_REBOOT dev=%s\n",
++ dev->name);
++ break;
++ case NETDEV_CHANGE:
++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
++ "klips_debug:ipsec_device_event: "
++ "NETDEV_CHANGE dev=%s flags=%x\n",
++ dev->name,
++ dev->flags);
++ break;
++ case NETDEV_REGISTER:
++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
++ "klips_debug:ipsec_device_event: "
++ "NETDEV_REGISTER dev=%s\n",
++ dev->name);
++ break;
++ case NETDEV_CHANGEMTU:
++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
++ "klips_debug:ipsec_device_event: "
++ "NETDEV_CHANGEMTU dev=%s to mtu=%d\n",
++ dev->name,
++ dev->mtu);
++ break;
++ case NETDEV_CHANGEADDR:
++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
++ "klips_debug:ipsec_device_event: "
++ "NETDEV_CHANGEADDR dev=%s\n",
++ dev->name);
++ break;
++ case NETDEV_GOING_DOWN:
++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
++ "klips_debug:ipsec_device_event: "
++ "NETDEV_GOING_DOWN dev=%s\n",
++ dev->name);
++ break;
++ case NETDEV_CHANGENAME:
++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
++ "klips_debug:ipsec_device_event: "
++ "NETDEV_CHANGENAME dev=%s\n",
++ dev->name);
++ break;
++#endif /* NET_21 */
++ default:
++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
++ "klips_debug:ipsec_device_event: "
++ "event type %ld unrecognised for dev=%s\n",
++ event,
++ dev->name);
++ break;
++ }
++ return NOTIFY_DONE;
++}
++
++/*
++ * Called when an ipsec tunnel device is initialized.
++ * The ipsec tunnel device structure is passed to us.
++ */
++
++int
++ipsec_tunnel_init(struct device *dev)
++{
++ int i;
++
++ KLIPS_PRINT(debug_tunnel,
++ "klips_debug:ipsec_tunnel_init: "
++ "allocating %lu bytes initialising device: %s\n",
++ (unsigned long) sizeof(struct ipsecpriv),
++ dev->name ? dev->name : "NULL");
++
++ /* Add our tunnel functions to the device */
++ dev->open = ipsec_tunnel_open;
++ dev->stop = ipsec_tunnel_close;
++ dev->hard_start_xmit = ipsec_tunnel_start_xmit;
++ dev->get_stats = ipsec_tunnel_get_stats;
++
++ dev->priv = kmalloc(sizeof(struct ipsecpriv), GFP_KERNEL);
++ if (dev->priv == NULL)
++ return -ENOMEM;
++ memset((caddr_t)(dev->priv), 0, sizeof(struct ipsecpriv));
++
++ for(i = 0; i < sizeof(zeroes); i++) {
++ ((__u8*)(zeroes))[i] = 0;
++ }
++
++#ifndef NET_21
++ /* Initialize the tunnel device structure */
++ for (i = 0; i < DEV_NUMBUFFS; i++)
++ skb_queue_head_init(&dev->buffs[i]);
++#endif /* !NET_21 */
++
++ dev->set_multicast_list = NULL;
++ dev->do_ioctl = ipsec_tunnel_ioctl;
++ dev->hard_header = NULL;
++ dev->rebuild_header = NULL;
++ dev->set_mac_address = NULL;
++#ifndef NET_21
++ dev->header_cache_bind = NULL;
++#endif /* !NET_21 */
++ dev->header_cache_update= NULL;
++
++#ifdef NET_21
++/* prv->neigh_setup = NULL; */
++ dev->neigh_setup = ipsec_tunnel_neigh_setup_dev;
++#endif /* NET_21 */
++ dev->hard_header_len = 0;
++ dev->mtu = 0;
++ dev->addr_len = 0;
++ dev->type = ARPHRD_VOID; /* ARPHRD_TUNNEL; */ /* ARPHRD_ETHER; */
++ dev->tx_queue_len = 10; /* Small queue */
++ memset((caddr_t)(dev->broadcast),0xFF, ETH_ALEN); /* what if this is not attached to ethernet? */
++
++ /* New-style flags. */
++ dev->flags = IFF_NOARP /* 0 */ /* Petr Novak */;
++#ifdef NET_21
++ dev_init_buffers(dev);
++#else /* NET_21 */
++ dev->family = AF_INET;
++ dev->pa_addr = 0;
++ dev->pa_brdaddr = 0;
++ dev->pa_mask = 0;
++ dev->pa_alen = 4;
++#endif /* NET_21 */
++
++ /* We're done. Have I forgotten anything? */
++ return 0;
++}
++
++/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
++/* Module specific interface (but it links with the rest of IPSEC) */
++/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
++
++int
++ipsec_tunnel_probe(struct device *dev)
++{
++ ipsec_tunnel_init(dev);
++ return 0;
++}
++
++struct device *ipsecdevices[IPSEC_NUM_IF];
++
++int
++ipsec_tunnel_init_devices(void)
++{
++ int i;
++ char name[IFNAMSIZ];
++ struct device *dev_ipsec;
++
++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
++ "klips_debug:ipsec_tunnel_init_devices: "
++ "creating and registering IPSEC_NUM_IF=%u devices, allocating %lu per device, IFNAMSIZ=%u.\n",
++ IPSEC_NUM_IF,
++ (unsigned long) (sizeof(struct device) + IFNAMSIZ),
++ IFNAMSIZ);
++
++ for(i = 0; i < IPSEC_NUM_IF; i++) {
++ sprintf(name, IPSEC_DEV_FORMAT, i);
++ dev_ipsec = (struct device*)kmalloc(sizeof(struct device), GFP_KERNEL);
++ if (dev_ipsec == NULL) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
++ "klips_debug:ipsec_tunnel_init_devices: "
++ "failed to allocate memory for device %s, quitting device init.\n",
++ name);
++ return -ENOMEM;
++ }
++ memset((caddr_t)dev_ipsec, 0, sizeof(struct device));
++#ifdef NETDEV_23
++ strncpy(dev_ipsec->name, name, sizeof(dev_ipsec->name));
++#else /* NETDEV_23 */
++ dev_ipsec->name = (char*)kmalloc(IFNAMSIZ, GFP_KERNEL);
++ if (dev_ipsec->name == NULL) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
++ "klips_debug:ipsec_tunnel_init_devices: "
++ "failed to allocate memory for device %s name, quitting device init.\n",
++ name);
++ return -ENOMEM;
++ }
++ memset((caddr_t)dev_ipsec->name, 0, IFNAMSIZ);
++ strncpy(dev_ipsec->name, name, IFNAMSIZ);
++#endif /* NETDEV_23 */
++ dev_ipsec->next = NULL;
++ dev_ipsec->init = &ipsec_tunnel_probe;
++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
++ "klips_debug:ipsec_tunnel_init_devices: "
++ "registering device %s\n",
++ dev_ipsec->name);
++
++ /* reference and hold the device reference */
++ dev_hold(dev_ipsec);
++ ipsecdevices[i]=dev_ipsec;
++
++ if (register_netdev(dev_ipsec) != 0) {
++ KLIPS_PRINT(1 || debug_tunnel & DB_TN_INIT,
++ "klips_debug:ipsec_tunnel_init_devices: "
++ "registering device %s failed, quitting device init.\n",
++ dev_ipsec->name);
++ return -EIO;
++ } else {
++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
++ "klips_debug:ipsec_tunnel_init_devices: "
++ "registering device %s succeeded, continuing...\n",
++ dev_ipsec->name);
++ }
++ }
++ return 0;
++}
++
++/* void */
++int
++ipsec_tunnel_cleanup_devices(void)
++{
++ int error = 0;
++ int i;
++ char name[32];
++ struct device *dev_ipsec;
++
++ for(i = 0; i < IPSEC_NUM_IF; i++) {
++ dev_ipsec = ipsecdevices[i];
++ if(dev_ipsec == NULL) {
++ continue;
++ }
++
++ /* release reference */
++ ipsecdevices[i]=NULL;
++ ipsec_dev_put(dev_ipsec);
++
++ KLIPS_PRINT(debug_tunnel, "Unregistering %s (refcnt=%d)\n",
++ name,
++ atomic_read(&dev_ipsec->refcnt));
++ unregister_netdev(dev_ipsec);
++ KLIPS_PRINT(debug_tunnel, "Unregisted %s\n", name);
++#ifndef NETDEV_23
++ kfree(dev_ipsec->name);
++ dev_ipsec->name=NULL;
++#endif /* !NETDEV_23 */
++ kfree(dev_ipsec->priv);
++ dev_ipsec->priv=NULL;
++ }
++ return error;
++}
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.220 2004/04/06 02:49:26 mcr
++ * pullup of algo code from alg-branch.
++ *
++ * Revision 1.219 2004/02/03 03:13:17 mcr
++ * minor edits for readability, and error reporting.
++ *
++ * Revision 1.218 2004/01/27 20:29:20 mcr
++ * fix for unregister_netdev() problem for underlying eth0.
++ *
++ * Revision 1.217 2003/12/10 01:14:27 mcr
++ * NAT-traversal patches to KLIPS.
++ *
++ * Revision 1.216 2003/12/04 23:01:17 mcr
++ * removed ipsec_netlink.h
++ *
++ * Revision 1.215 2003/12/04 16:35:16 ken
++ * Fix for ATM devices where physdev->hard_header_len *is* correct
++ *
++ * Revision 1.214 2003/11/25 23:52:37 mcr
++ * fix typo in patch - ixs-> needed.
++ *
++ * Revision 1.213 2003/11/24 18:25:49 mcr
++ * patch from willy@w.ods.org to fix problems with ATM interfaces.
++ *
++ * Revision 1.212 2003/10/31 02:27:55 mcr
++ * pulled up port-selector patches and sa_id elimination.
++ *
++ * Revision 1.211.2.2 2003/10/29 01:30:41 mcr
++ * elimited "struct sa_id".
++ *
++ * Revision 1.211.2.1 2003/09/21 13:59:56 mcr
++ * pre-liminary X.509 patch - does not yet pass tests.
++ *
++ * Revision 1.211 2003/09/10 16:46:30 mcr
++ * patches for 2.4 backport/2.6 existence.
++ *
++ * Revision 1.210 2003/07/31 22:47:16 mcr
++ * preliminary (untested by FS-team) 2.5 patches.
++ *
++ * Revision 1.209 2003/06/22 21:28:43 mcr
++ * inability to unload module was caused by calls to dev_get
++ * (ipsec_dev_get), to gather a device from a name. There is
++ * simply no reason to look the devices up - they should be kept
++ * in a nice array, ready for use.
++ *
++ * Revision 1.208 2003/06/22 21:25:07 mcr
++ * all staticly counted ipsecXXX device support removed.
++ *
++ * Revision 1.207 2003/04/02 20:15:37 mcr
++ * fix for PR#204 - do not clear connection tracking info if we
++ * the packet is being sent in the clear.
++ *
++ * Revision 1.206 2003/02/12 19:32:51 rgb
++ * Refactored file to:
++ * ipsec_xmit.c
++ * ipsec_xmit.h
++ * ipsec_mast.c
++ *
++ * Revision 1.205 2003/02/06 17:47:00 rgb
++ *
++ * Remove unused ipsec_tunnel_lock() and ipsec_tunnel_unlock() code.
++ * Refactor ipsec_tunnel_start_xmit() further into:
++ * ipsec_xmit_sanity_check_dev()
++ * ipsec_xmit_sanity_check_skb()
++ * ipsec_xmit_strip_hard_header()
++ * ipsec_xmit_restore_hard_header()
++ * ipsec_xmit_send()
++ * ipsec_xmit_cleanup()
++ * and start a skeletal ipsec_mast_start_xmit() .
++ *
++ * Revision 1.204 2003/02/06 06:43:46 rgb
++ *
++ * Refactor ipsec_tunnel_start_xmit, bringing out:
++ * ipsec_xmit_SAlookup
++ * ipsec_xmit_encap_once
++ * ipsec_xmit_encap_bundle
++ *
++ * Revision 1.203 2003/02/06 02:21:34 rgb
++ *
++ * Moved "struct auth_alg" from ipsec_rcv.c to ipsec_ah.h .
++ * Changed "struct ah" to "struct ahhdr" and "struct esp" to "struct esphdr".
++ * Removed "#ifdef INBOUND_POLICY_CHECK_eroute" dead code.
++ *
++ * Revision 1.202 2003/01/03 07:38:01 rgb
++ *
++ * Start to refactor ipsec_tunnel_start_xmit() by putting local variables
++ * into struct ipsec_xmit_state and renaming a few variables to give more
++ * unique or searchable names.
++ *
++ * Revision 1.201 2003/01/03 00:31:28 rgb
++ *
++ * Clean up memset usage, including fixing 2 places where keys were not
++ * properly wiped.
++ *
++ * Revision 1.200 2002/12/06 02:24:02 mcr
++ * patches for compiling against SUSE 8.1 kernels. Requires
++ * an additional -DSUSE_LINUX_2_4_19_IS_STUPID.
++ *
++ * Revision 1.199 2002/10/12 23:11:53 dhr
++ *
++ * [KenB + DHR] more 64-bit cleanup
++ *
++ * Revision 1.198 2002/10/05 05:02:58 dhr
++ *
++ * C labels go on statements
++ *
++ * Revision 1.197 2002/09/20 05:01:50 rgb
++ * Added compiler directive to switch on IP options and fix IP options bug.
++ * Make ip->ihl treatment consistent using shifts rather than multiplications.
++ * Check for large enough packet before accessing udp header for IKE bypass.
++ * Added memory allocation debugging.
++ * Fixed potential memory allocation failure-induced oops.
++ *
++ * Revision 1.196 2002/07/24 18:44:54 rgb
++ * Type fiddling to tame ia64 compiler.
++ *
++ * Revision 1.195 2002/07/23 03:36:07 rgb
++ * Fixed 2.2 device initialisation hang.
++ *
++ * Revision 1.194 2002/05/27 21:40:34 rgb
++ * Set unused ipsec devices to ARPHRD_VOID to avoid confusing iproute2.
++ * Cleaned up intermediate step to dynamic device allocation.
++ *
++ * Revision 1.193 2002/05/27 19:31:36 rgb
++ * Convert to dynamic ipsec device allocation.
++ * Remove final vistiges of tdb references via IPSEC_KLIPS1_COMPAT.
++ *
++ * Revision 1.192 2002/05/23 07:14:28 rgb
++ * Added refcount code.
++ * Cleaned up %p variants to 0p%p for test suite cleanup.
++ *
++ * Revision 1.191 2002/05/14 02:34:37 rgb
++ * Change all references to tdb, TDB or Tunnel Descriptor Block to ips,
++ * ipsec_sa or ipsec_sa.
++ *
++ * Revision 1.190 2002/04/24 07:55:32 mcr
++ * #include patches and Makefiles for post-reorg compilation.
++ *
++ * Revision 1.189 2002/04/24 07:36:32 mcr
++ * Moved from ./klips/net/ipsec/ipsec_tunnel.c,v
++ *
++ * Revision 1.188 2002/04/20 00:12:25 rgb
++ * Added esp IV CBC attack fix, disabled.
++ *
++ * Revision 1.187 2002/03/23 19:55:17 rgb
++ * Fix for 2.2 local IKE fragmentation blackhole. Still won't work if
++ * iptraf or another pcap app is running.
++ *
++ * Revision 1.186 2002/03/19 03:26:22 rgb
++ * Applied DHR's tunnel patch to streamline IKE/specialSA processing.
++ *
++ * Revision 1.185 2002/02/20 04:13:05 rgb
++ * Send back ICMP_PKT_FILTERED upon %reject.
++ *
++ * Revision 1.184 2002/01/29 17:17:56 mcr
++ * moved include of ipsec_param.h to after include of linux/kernel.h
++ * otherwise, it seems that some option that is set in ipsec_param.h
++ * screws up something subtle in the include path to kernel.h, and
++ * it complains on the snprintf() prototype.
++ *
++ * Revision 1.183 2002/01/29 04:00:53 mcr
++ * more excise of kversions.h header.
++ *
++ * Revision 1.182 2002/01/29 02:13:18 mcr
++ * introduction of ipsec_kversion.h means that include of
++ * ipsec_param.h must preceed any decisions about what files to
++ * include to deal with differences in kernel source.
++ *
++ * Revision 1.181 2002/01/07 20:00:33 rgb
++ * Added IKE destination port debugging.
++ *
++ * Revision 1.180 2001/12/21 21:49:54 rgb
++ * Fixed bug as a result of moving IKE bypass above %trap/%hold code.
++ *
++ * Revision 1.179 2001/12/19 21:08:14 rgb
++ * Added transport protocol ports to ipsec_print_ip().
++ * Update eroute info for non-SA targets.
++ * Added obey DF code disabled.
++ * Fixed formatting bugs in ipsec_tunnel_hard_header().
++ *
++ * Revision 1.178 2001/12/05 09:36:10 rgb
++ * Moved the UDP/500 IKE check just above the %hold/%trap checks to avoid
++ * IKE packets being stolen by the %hold (and returned to the sending KMd
++ * in an ACQUIRE, ironically ;-).
++ *
++ * Revision 1.177 2001/11/26 09:23:50 rgb
++ * Merge MCR's ipsec_sa, eroute, proc and struct lifetime changes.
++ *
++ * Revision 1.170.2.1 2001/09/25 02:28:27 mcr
++ * struct tdb -> struct ipsec_sa.
++ * lifetime checks moved to common routines.
++ * cleaned up includes.
++ *
++ * Revision 1.170.2.2 2001/10/22 21:08:01 mcr
++ * include des.h, removed phony prototypes and fixed calling
++ * conventions to match real prototypes.
++ *
++ * Revision 1.176 2001/11/09 18:32:31 rgb
++ * Added Hans Schultz' fragmented UDP/500 IKE socket port selector.
++ *
++ * Revision 1.175 2001/11/06 20:47:00 rgb
++ * Added Eric Espie's TRAPSUBNET fix, minus spin-lock-bh dabbling.
++ *
++ * Revision 1.174 2001/11/06 19:50:43 rgb
++ * Moved IP_SEND, ICMP_SEND, DEV_QUEUE_XMIT macros to ipsec_tunnel.h for
++ * use also by pfkey_v2_parser.c
++ *
++ * Revision 1.173 2001/10/29 21:53:44 henry
++ * tone down the device-down message slightly, until we can make it smarter
++ *
++ * Revision 1.172 2001/10/26 04:59:37 rgb
++ * Added a critical level syslog message if an ipsec device goes down.
++ *
++ * Revision 1.171 2001/10/18 04:45:21 rgb
++ * 2.4.9 kernel deprecates linux/malloc.h in favour of linux/slab.h,
++ * lib/freeswan.h version macros moved to lib/kversions.h.
++ * Other compiler directive cleanups.
++ *
++ * Revision 1.170 2001/09/25 00:09:50 rgb
++ * Added NetCelo's TRAPSUBNET code to convert a new type TRAPSUBNET into a
++ * HOLD.
++ *
++ * Revision 1.169 2001/09/15 16:24:05 rgb
++ * Re-inject first and last HOLD packet when an eroute REPLACE is done.
++ *
++ * Revision 1.168 2001/09/14 16:58:37 rgb
++ * Added support for storing the first and last packets through a HOLD.
++ *
++ * Revision 1.167 2001/09/08 21:13:33 rgb
++ * Added pfkey ident extension support for ISAKMPd. (NetCelo)
++ *
++ * Revision 1.166 2001/08/27 19:47:59 rgb
++ * Clear tdb before usage.
++ * Added comment: clear IF before calling routing?
++ *
++ * Revision 1.165 2001/07/03 01:23:53 rgb
++ * Send back ICMP iff DF set, !ICMP, offset==0, sysctl_icmp, iph->tot_len >
++ * emtu, and don't drop.
++ *
++ * Revision 1.164 2001/06/14 19:35:10 rgb
++ * Update copyright date.
++ *
++ * Revision 1.163 2001/06/06 20:28:51 rgb
++ * Added sanity checks for NULL skbs and devices.
++ * Added more debugging output to various functions.
++ * Removed redundant dev->priv argument to ipsec_tunnel_{at,de}tach().
++ * Renamed ipsec_tunnel_attach() virtual and physical device arguments.
++ * Corrected neigh_setup() device function assignment.
++ * Keep valid pointers to ipsec_tunnel_*() on detach.
++ * Set dev->type to the originally-initiallised value.
++ *
++ * Revision 1.162 2001/06/01 07:28:04 rgb
++ * Added sanity checks for detached devices. Don't down virtual devices
++ * to prevent packets going out in the clear if the detached device comes
++ * back up.
++ *
++ * Revision 1.161 2001/05/30 08:14:52 rgb
++ * Removed vestiges of esp-null transforms.
++ * NetDev Notifier instrumentation to track down disappearing devices.
++ *
++ * Revision 1.160 2001/05/29 05:15:12 rgb
++ * Added SS' PMTU patch which notifies sender if packet doesn't fit
++ * physical MTU (if it wasn't ICMP) and then drops it.
++ *
++ * Revision 1.159 2001/05/27 06:12:12 rgb
++ * Added structures for pid, packet count and last access time to eroute.
++ * Added packet count to beginning of /proc/net/ipsec_eroute.
++ *
++ * Revision 1.158 2001/05/24 05:39:33 rgb
++ * Applied source zeroing to 2.2 ip_route_output() call as well to enable
++ * PASS eroutes for opportunism.
++ *
++ * Revision 1.157 2001/05/23 22:35:28 rgb
++ * 2.4 source override simplification.
++ *
++ * Revision 1.156 2001/05/23 21:41:31 rgb
++ * Added error return code printing on ip_route_output().
++ *
++ * Revision 1.155 2001/05/23 05:09:13 rgb
++ * Fixed incorrect ip_route_output() failure message.
++ *
++ * Revision 1.154 2001/05/21 14:53:31 rgb
++ * Added debug statement for case when ip_route_output() fails, causing
++ * packet to be dropped, but log looked ok.
++ *
++ * Revision 1.153 2001/05/19 02:37:54 rgb
++ * Fixed missing comment termination.
++ *
++ * Revision 1.152 2001/05/19 02:35:50 rgb
++ * Debug code optimisation for non-debug speed.
++ * Kernel version compiler define comments.
++ * 2.2 and 2.4 kernel ip_send device and ip debug output added.
++ *
++ * Revision 1.151 2001/05/18 16:17:35 rgb
++ * Changed reference from "magic" to "shunt" SAs.
++ *
++ * Revision 1.150 2001/05/18 16:12:19 rgb
++ * Changed UDP/500 bypass test from 3 nested ifs to one anded if.
++ *
++ * Revision 1.149 2001/05/16 04:39:33 rgb
++ * Add default == eroute.dest to IKE bypass conditions for magic eroutes.
++ *
++ * Revision 1.148 2001/05/05 03:31:41 rgb
++ * IP frag debugging updates and enhancements.
++ *
++ * Revision 1.147 2001/05/03 19:41:40 rgb
++ * Added SS' skb_cow fix for 2.4.4.
++ *
++ * Revision 1.146 2001/04/30 19:28:16 rgb
++ * Update for 2.4.4. ip_select_ident() now has 3 args.
++ *
++ * Revision 1.145 2001/04/23 14:56:10 rgb
++ * Added spin_lock() check to prevent double-locking for multiple
++ * transforms and hence kernel lock-ups with SMP kernels.
++ *
++ * Revision 1.144 2001/04/21 23:04:45 rgb
++ * Define out skb->used for 2.4 kernels.
++ * Check if soft expire has already been sent before sending another to
++ * prevent ACQUIRE flooding.
++ *
++ * Revision 1.143 2001/03/16 07:37:21 rgb
++ * Added comments to all #endifs.
++ *
++ * Revision 1.142 2001/02/28 05:03:27 rgb
++ * Clean up and rationalise startup messages.
++ *
++ * Revision 1.141 2001/02/27 22:24:54 rgb
++ * Re-formatting debug output (line-splitting, joining, 1arg/line).
++ * Check for satoa() return codes.
++ *
++ * Revision 1.140 2001/02/27 06:40:12 rgb
++ * Fixed TRAP->HOLD eroute byte order.
++ *
++ * Revision 1.139 2001/02/26 20:38:59 rgb
++ * Added compiler defines for 2.4.x-specific code.
++ *
++ * Revision 1.138 2001/02/26 19:57:27 rgb
++ * Implement magic SAs %drop, %reject, %trap, %hold, %pass as part
++ * of the new SPD and to support opportunistic.
++ * Drop sysctl_ipsec_{no_eroute_pass,opportunistic}, replaced by magic SAs.
++ *
++ * Revision 1.137 2001/02/19 22:29:49 rgb
++ * Fixes for presence of active ipv6 segments which share ipsec physical
++ * device (gg).
++ *
++ * Revision 1.136 2001/01/29 22:30:38 rgb
++ * Fixed minor acquire debug printing bug.
++ *
++ * Revision 1.135 2001/01/29 22:19:45 rgb
++ * Zero source address for 2.4 bypass route lookup.
++ *
++ * Revision 1.134 2001/01/23 20:19:49 rgb
++ * 2.4 fix to remove removed is_clone member.
++ *
++ * Revision 1.133 2000/12/09 22:08:35 rgb
++ * Fix NET_23 bug, should be NETDEV_23.
++ *
++ * Revision 1.132 2000/12/01 06:54:50 rgb
++ * Fix for new 2.4 IP TTL default variable name.
++ *
++ * Revision 1.131 2000/11/09 20:52:15 rgb
++ * More spinlock shuffling, locking earlier and unlocking later in rcv to
++ * include ipcomp and prevent races, renaming some tdb variables that got
++ * forgotten, moving some unlocks to include tdbs and adding a missing
++ * unlock. Thanks to Svenning for some of these.
++ *
++ * Revision 1.130 2000/11/09 20:11:22 rgb
++ * Minor shuffles to fix non-standard kernel config option selection.
++ *
++ * Revision 1.129 2000/11/06 04:32:49 rgb
++ * Clean up debug printing.
++ * Copy skb->protocol for all kernel versions.
++ * Ditched spin_lock_irqsave in favour of spin_lock.
++ * Disabled TTL decrement, done in ip_forward.
++ * Added debug printing before pfkey_acquire().
++ * Fixed printk-deltdbchain-spin_lock races (Svenning).
++ * Use defaultTTL for 2.1+ kernels.
++ * Add Svenning's adaptive content compression.
++ * Fix up debug display arguments.
++ *
++ * Revision 1.128 2000/09/28 00:58:57 rgb
++ * Moved the IKE passthrough check after the eroute lookup so we can pass
++ * IKE through intermediate tunnels.
++ *
++ * Revision 1.127 2000/09/22 17:52:11 rgb
++ * Fixed misleading ipcomp debug output.
++ *
++ * Revision 1.126 2000/09/22 04:22:56 rgb
++ * Fixed dumb spi->cpi conversion error.
++ *
++ * Revision 1.125 2000/09/21 04:34:48 rgb
++ * A few debug-specific things should be hidden under
++ * CONFIG_IPSEC_DEBUG.(MB)
++ * Improved ip_send() error handling.(MB)
++ *
++ * Revision 1.124 2000/09/21 03:40:58 rgb
++ * Added more debugging to try and track down the cpi outward copy problem.
++ *
++ * Revision 1.123 2000/09/19 07:08:49 rgb
++ * Added debugging to outgoing compression report.
++ *
++ * Revision 1.122 2000/09/18 19:21:26 henry
++ * RGB-supplied fix for RH5.2 problem
++ *
++ * Revision 1.121 2000/09/17 21:05:09 rgb
++ * Added tdb to skb_compress call to write in cpi.
++ *
++ * Revision 1.120 2000/09/17 16:57:16 rgb
++ * Added Svenning's patch to remove restriction of ipcomp to innermost
++ * transform.
++ *
++ * Revision 1.119 2000/09/15 11:37:01 rgb
++ * Merge in heavily modified Svenning Soerensen's <svenning@post5.tele.dk>
++ * IPCOMP zlib deflate code.
++ *
++ * Revision 1.118 2000/09/15 04:57:16 rgb
++ * Moved debug output after sanity check.
++ * Added tos copy sysctl.
++ *
++ * Revision 1.117 2000/09/12 03:22:51 rgb
++ * Converted ipsec_icmp, no_eroute_pass, opportunistic and #if0 debugs to
++ * sysctl.
++ *
++ * Revision 1.116 2000/09/08 19:18:19 rgb
++ * Change references from DEBUG_IPSEC to CONFIG_IPSEC_DEBUG.
++ * Added outgoing opportunistic hook, ifdef'ed out.
++ *
++ * Revision 1.115 2000/08/30 05:27:29 rgb
++ * Removed all the rest of the references to tdb_spi, tdb_proto, tdb_dst.
++ * Kill remainder of tdb_xform, tdb_xdata, xformsw.
++ *
++ * Revision 1.114 2000/08/28 18:15:46 rgb
++ * Added MB's nf-debug reset patch.
++ *
++ * Revision 1.113 2000/08/27 02:26:40 rgb
++ * Send all no-eroute-bypass, pluto-bypass and passthrough packets through
++ * fragmentation machinery for 2.0, 2.2 and 2.4 kernels.
++ *
++ * Revision 1.112 2000/08/20 21:37:33 rgb
++ * Activated pfkey_expire() calls.
++ * Added a hard/soft expiry parameter to pfkey_expire(). (Momchil)
++ * Re-arranged the order of soft and hard expiry to conform to RFC2367.
++ * Clean up references to CONFIG_IPSEC_PFKEYv2.
++ *
++ * Revision 1.111 2000/08/01 14:51:51 rgb
++ * Removed _all_ remaining traces of DES.
++ *
++ * Revision 1.110 2000/07/28 14:58:31 rgb
++ * Changed kfree_s to kfree, eliminating extra arg to fix 2.4.0-test5.
++ *
++ * Revision 1.109 2000/07/28 13:50:54 rgb
++ * Changed enet_statistics to net_device_stats and added back compatibility
++ * for pre-2.1.19.
++ *
++ * Revision 1.108 2000/05/16 03:03:11 rgb
++ * Updates for 2.3.99pre8 from MB.
++ *
++ * Revision 1.107 2000/05/10 23:08:21 rgb
++ * Print a debug warning about bogus packets received by the outgoing
++ * processing machinery only when klipsdebug is not set to none.
++ * Comment out the device initialisation informational messages.
++ *
++ * Revision 1.106 2000/05/10 19:17:14 rgb
++ * Define an IP_SEND macro, intending to have all packet passthroughs
++ * use fragmentation. This didn't quite work, but is a step in the
++ * right direction.
++ * Added buffer allocation debugging statements.
++ * Added configure option to shut off no eroute passthrough.
++ * Only check usetime against soft and hard limits if the tdb has been
++ * used.
++ * Cast output of ntohl so that the broken prototype doesn't make our
++ * compile noisy.
++ *
++ * Revision 1.105 2000/03/22 16:15:37 rgb
++ * Fixed renaming of dev_get (MB).
++ *
++ * Revision 1.104 2000/03/16 14:04:15 rgb
++ * Indented headers for readability.
++ * Fixed debug scope to enable compilation with debug off.
++ * Added macros for ip_chk_addr and IS_MYADDR for identifying self.
++ *
++ * Revision 1.103 2000/03/16 07:11:07 rgb
++ * Hardcode PF_KEYv2 support.
++ * Fixed bug which allowed UDP/500 packet from another machine
++ * through in the clear.
++ * Added disabled skb->protocol fix for ISDN/ASYNC PPP from Matjaz Godec.
++ *
++ * Revision 1.102 2000/03/14 12:26:59 rgb
++ * Added skb->nfct support for clearing netfilter conntrack bits (MB).
++ *
++ * Revision 1.101 2000/02/14 21:05:22 rgb
++ * Added MB's netif_queue fix for kernels 2.3.43+.
++ *
++ * Revision 1.100 2000/01/26 10:04:57 rgb
++ * Fixed noisy 2.0 printk arguments.
++ *
++ * Revision 1.99 2000/01/21 06:16:25 rgb
++ * Added sanity checks on skb_push(), skb_pull() to prevent panics.
++ * Switched to AF_ENCAP macro.
++ * Shortened debug output per packet and re-arranging debug_tunnel
++ * bitmap flags, while retaining necessary information to avoid
++ * trampling the kernel print ring buffer.
++ * Reformatted recursion switch code.
++ * Changed all references to tdb_proto to tdb_said.proto for clarity.
++ *
++ * Revision 1.98 2000/01/13 08:09:31 rgb
++ * Shuffled debug_tunnel switches to focus output.
++ * Fixed outgoing recursion bug, limiting to recursing only if the remote
++ * SG changes and if it is valid, ie. not passthrough.
++ * Clarified a number of debug messages.
++ *
++ * Revision 1.97 2000/01/10 16:37:16 rgb
++ * MB support for new ip_select_ident() upon disappearance of
++ * ip_id_count in 2.3.36+.
++ *
++ * Revision 1.96 1999/12/31 14:59:08 rgb
++ * MB fix to use new skb_copy_expand in kernel 2.3.35.
++ *
++ * Revision 1.95 1999/12/29 21:15:44 rgb
++ * Fix tncfg to aliased device bug.
++ *
++ * Revision 1.94 1999/12/22 04:26:06 rgb
++ * Converted all 'static' functions to 'DEBUG_NO_STATIC' to enable
++ * debugging by providing external labels to all functions with debugging
++ * turned on.
++ *
++ * Revision 1.93 1999/12/13 13:30:14 rgb
++ * Changed MTU reports and HW address reporting back to debug only.
++ *
++ * Revision 1.92 1999/12/07 18:57:56 rgb
++ * Fix PFKEY symbol compile error (SADB_*) without pfkey enabled.
++ *
++ * Revision 1.91 1999/12/01 22:15:36 rgb
++ * Add checks for LARVAL and DEAD SAs.
++ * Change state of SA from MATURE to DYING when a soft lifetime is
++ * reached and print debug warning.
++ *
++ * Revision 1.90 1999/11/23 23:04:04 rgb
++ * Use provided macro ADDRTOA_BUF instead of hardcoded value.
++ * Sort out pfkey and freeswan headers, putting them in a library path.
++ *
++ * Revision 1.89 1999/11/18 18:50:59 rgb
++ * Changed all device registrations for static linking to
++ * dynamic to reduce the number and size of patches.
++ *
++ * Revision 1.88 1999/11/18 04:09:19 rgb
++ * Replaced all kernel version macros to shorter, readable form.
++ *
++ * Revision 1.87 1999/11/17 15:53:40 rgb
++ * Changed all occurrences of #include "../../../lib/freeswan.h"
++ * to #include <freeswan.h> which works due to -Ilibfreeswan in the
++ * klips/net/ipsec/Makefile.
++ *
++ * Revision 1.86 1999/10/16 18:25:37 rgb
++ * Moved SA lifetime expiry checks before packet processing.
++ * Expire SA on replay counter rollover.
++ *
++ * Revision 1.85 1999/10/16 04:24:31 rgb
++ * Add stats for time since last packet.
++ *
++ * Revision 1.84 1999/10/16 00:30:47 rgb
++ * Added SA lifetime counting.
++ *
++ * Revision 1.83 1999/10/15 22:15:57 rgb
++ * Clean out cruft.
++ * Add debugging.
++ *
++ * Revision 1.82 1999/10/08 18:26:19 rgb
++ * Fix 2.0.3x outgoing fragmented packet memory leak.
++ *
++ * Revision 1.81 1999/10/05 02:38:54 rgb
++ * Lower the default mtu of virtual devices to 16260.
++ *
++ * Revision 1.80 1999/10/03 18:56:41 rgb
++ * Spinlock support for 2.3.xx.
++ * Don't forget to undo spinlocks on error!
++ * Check for valid eroute before copying the structure.
++ *
++ * Revision 1.79 1999/10/01 15:44:53 rgb
++ * Move spinlock header include to 2.1> scope.
++ *
++ * Revision 1.78 1999/10/01 00:02:43 rgb
++ * Added tdb structure locking.
++ * Added eroute structure locking.
++ *
++ * Revision 1.77 1999/09/30 02:52:29 rgb
++ * Add Marc Boucher's Copy-On-Write code (same as ipsec_rcv.c).
++ *
++ * Revision 1.76 1999/09/25 19:31:27 rgb
++ * Refine MSS hack to affect SYN, but not SYN+ACK packets.
++ *
++ * Revision 1.75 1999/09/24 22:52:38 rgb
++ * Fix two things broken in 2.0.38 by trying to fix network notifiers.
++ *
++ * Revision 1.74 1999/09/24 00:30:37 rgb
++ * Add test for changed source as well as destination to check for
++ * recursion.
++ *
++ * Revision 1.73 1999/09/23 20:52:24 rgb
++ * Add James Morris' MSS hack patch, disabled.
++ *
++ * Revision 1.72 1999/09/23 20:22:40 rgb
++ * Enable, tidy and fix network notifier code.
++ *
++ * Revision 1.71 1999/09/23 18:09:05 rgb
++ * Clean up 2.2.x fragmenting traces.
++ * Disable dev->type switching, forcing ARPHRD_TUNNEL.
++ *
++ * Revision 1.70 1999/09/22 14:14:24 rgb
++ * Add sanity checks for revectored calls to prevent calling a downed I/F.
++ *
++ * Revision 1.69 1999/09/21 15:00:57 rgb
++ * Add Marc Boucher's packet size check.
++ * Flesh out network device notifier code.
++ *
++ * Revision 1.68 1999/09/18 11:39:57 rgb
++ * Start to add (disabled) netdevice notifier code.
++ *
++ * Revision 1.67 1999/09/17 23:44:40 rgb
++ * Add a comment warning potential code hackers to stay away from mac.raw.
++ *
++ * Revision 1.66 1999/09/17 18:04:02 rgb
++ * Add fix for unpredictable hard_header_len for ISDN folks (thanks MB).
++ * Ditch TTL decrement in 2.2 (MB).
++ *
++ * Revision 1.65 1999/09/15 23:15:35 henry
++ * Marc Boucher's PPP fixes
++ *
++ * Revision 1.64 1999/09/07 13:40:53 rgb
++ * Ditch unreliable references to skb->mac.raw.
++ *
++ * Revision 1.63 1999/08/28 11:33:09 rgb
++ * Check for null skb->mac pointer.
++ *
++ * Revision 1.62 1999/08/28 02:02:30 rgb
++ * Add Marc Boucher's fix for properly dealing with skb->sk.
++ *
++ * Revision 1.61 1999/08/27 05:23:05 rgb
++ * Clean up skb->data/raw/nh/h manipulation.
++ * Add Marc Boucher's mods to aid tcpdump.
++ * Add sanity checks to skb->raw/nh/h pointer copies in skb_copy_expand.
++ * Re-order hard_header stripping -- might be able to remove it...
++ *
++ * Revision 1.60 1999/08/26 20:01:02 rgb
++ * Tidy up compiler directives and macros.
++ * Re-enable ICMP for tunnels where inner_dst != outer_dst.
++ * Remove unnecessary skb->dev = physdev assignment affecting 2.2.x.
++ *
++ * Revision 1.59 1999/08/25 15:44:41 rgb
++ * Clean up from 2.2.x instrumenting for compilation under 2.0.36.
++ *
++ * Revision 1.58 1999/08/25 15:00:54 rgb
++ * Add dst cache code for 2.2.xx.
++ * Add sanity check for skb packet header pointers.
++ * Add/modify debugging instrumentation to *_start_xmit, *_hard_header and
++ * *_rebuild_header.
++ * Add neigh_* cache code.
++ * Change dev->type back to ARPHRD_TUNNEL.
++ *
++ * Revision 1.57 1999/08/17 21:50:23 rgb
++ * Fixed minor debug output bugs.
++ * Regrouped error recovery exit code.
++ * Added compiler directives to remove unwanted code and symbols.
++ * Shut off ICMP messages: to be refined to only send ICMP to remote systems.
++ * Add debugging code for output function addresses.
++ * Fix minor bug in (possibly unused) header_cache_bind function.
++ * Add device neighbour caching code.
++ * Change dev->type from ARPHRD_TUNNEL to physdev->type.
++ *
++ * Revision 1.56 1999/08/03 17:22:56 rgb
++ * Debug output clarification using KERN_* macros. Other inactive changes
++ * added.
++ *
++ * Revision 1.55 1999/08/03 16:58:46 rgb
++ * Fix skb_copy_expand size bug. Was getting incorrect size.
++ *
++ * Revision 1.54 1999/07/14 19:32:38 rgb
++ * Fix oversize packet crash and ssh stalling in 2.2.x kernels.
++ *
++ * Revision 1.53 1999/06/10 15:44:02 rgb
++ * Minor reformatting and clean-up.
++ *
++ * Revision 1.52 1999/05/09 03:25:36 rgb
++ * Fix bug introduced by 2.2 quick-and-dirty patch.
++ *
++ * Revision 1.51 1999/05/08 21:24:59 rgb
++ * Add casting to silence the 2.2.x compile.
++ *
++ * Revision 1.50 1999/05/05 22:02:32 rgb
++ * Add a quick and dirty port to 2.2 kernels by Marc Boucher <marc@mbsi.ca>.
++ *
++ * Revision 1.49 1999/04/29 15:18:52 rgb
++ * Change gettdb parameter to a pointer to reduce stack loading and
++ * facilitate parameter sanity checking.
++ * Fix undetected bug that might have tried to access a null pointer.
++ * Eliminate unnessessary usage of tdb_xform member to further switch
++ * away from the transform switch to the algorithm switch.
++ * Add return values to init and cleanup functions.
++ *
++ * Revision 1.48 1999/04/16 15:38:00 rgb
++ * Minor rearrangement of freeing code to avoid memory leaks with impossible or
++ * rare situations.
++ *
++ * Revision 1.47 1999/04/15 15:37:25 rgb
++ * Forward check changes from POST1_00 branch.
++ *
++ * Revision 1.32.2.4 1999/04/13 21:00:18 rgb
++ * Ditch 'things I wish I had known before...'.
++ *
++ * Revision 1.32.2.3 1999/04/13 20:34:38 rgb
++ * Free skb after fragmentation.
++ * Use stats more effectively.
++ * Add I/F to mtu notch-down reporting.
++ *
++ * Revision 1.32.2.2 1999/04/02 04:26:14 rgb
++ * Backcheck from HEAD, pre1.0.
++ *
++ * Revision 1.46 1999/04/11 00:29:00 henry
++ * GPL boilerplate
++ *
++ * Revision 1.45 1999/04/07 15:42:01 rgb
++ * Fix mtu/ping bug AGAIN!
++ *
++ * Revision 1.44 1999/04/06 04:54:27 rgb
++ * Fix/Add RCSID Id: and Log: bits to make PHMDs happy. This includes
++ * patch shell fixes.
++ *
++ * Revision 1.43 1999/04/04 03:57:07 rgb
++ * ip_fragment() doesn't free the supplied skb. Freed.
++ *
++ * Revision 1.42 1999/04/01 23:27:15 rgb
++ * Preload size of virtual mtu.
++ *
++ * Revision 1.41 1999/04/01 09:31:23 rgb
++ * Invert meaning of ICMP PMTUD config option and clarify.
++ * Code clean-up.
++ *
++ * Revision 1.40 1999/04/01 04:37:17 rgb
++ * SSH stalling bug fix.
++ *
++ * Revision 1.39 1999/03/31 23:44:28 rgb
++ * Don't send ICMP on DF and frag_off.
++ *
++ * Revision 1.38 1999/03/31 15:20:10 rgb
++ * Quiet down debugging.
++ *
++ * Revision 1.37 1999/03/31 08:30:31 rgb
++ * Add switch to shut off ICMP PMTUD packets.
++ *
++ * Revision 1.36 1999/03/31 05:44:47 rgb
++ * Keep PMTU reduction private.
++ *
++ * Revision 1.35 1999/03/27 15:13:02 rgb
++ * PMTU/fragmentation bug fix.
++ *
++ * Revision 1.34 1999/03/17 21:19:26 rgb
++ * Fix kmalloc nonatomic bug.
++ *
++ * Revision 1.33 1999/03/17 15:38:42 rgb
++ * Code clean-up.
++ * ESP_NULL IV bug fix.
++ *
++ * Revision 1.32 1999/03/01 20:44:25 rgb
++ * Code clean-up.
++ * Memory leak bug fix.
++ *
++ * Revision 1.31 1999/02/27 00:02:09 rgb
++ * Tune to report the MTU reduction once, rather than after every recursion
++ * through the encapsulating code, preventing tcp stream stalling.
++ *
++ * Revision 1.30 1999/02/24 20:21:01 rgb
++ * Reformat debug printk's.
++ * Fix recursive encapsulation, dynamic MTU bugs and add debugging code.
++ * Clean-up.
++ *
++ * Revision 1.29 1999/02/22 17:08:14 rgb
++ * Fix recursive encapsulation code.
++ *
++ * Revision 1.28 1999/02/19 18:27:02 rgb
++ * Improve DF, fragmentation and PMTU behaviour and add dynamic MTU discovery.
++ *
++ * Revision 1.27 1999/02/17 16:51:37 rgb
++ * Clean out unused cruft.
++ * Temporarily tone down volume of debug output.
++ * Temporarily shut off fragment rejection.
++ * Disabled temporary failed recursive encapsulation loop.
++ *
++ * Revision 1.26 1999/02/12 21:21:26 rgb
++ * Move KLIPS_PRINT to ipsec_netlink.h for accessibility.
++ *
++ * Revision 1.25 1999/02/11 19:38:27 rgb
++ * More clean-up.
++ * Add sanity checking for skb_copy_expand() to prevent kernel panics on
++ * skb_put() values out of range.
++ * Fix head/tailroom calculation causing skb_put() out-of-range values.
++ * Fix return values to prevent 'nonatomic alloc_skb' warnings.
++ * Allocate new skb iff needed.
++ * Added more debug statements.
++ * Make headroom depend on structure, not hard-coded values.
++ *
++ * Revision 1.24 1999/02/10 23:20:33 rgb
++ * Shut up annoying 'statement has no effect' compiler warnings with
++ * debugging compiled out.
++ *
++ * Revision 1.23 1999/02/10 22:36:30 rgb
++ * Clean-up obsolete, unused and messy code.
++ * Converted most IPSEC_DEBUG statements to KLIPS_PRINT macros.
++ * Rename ipsec_tunnel_do_xmit to ipsec_tunnel_start_xmit and eliminated
++ * original ipsec_tunnel_start_xmit.
++ * Send all packet with different inner and outer destinations directly to
++ * the attached physical device, rather than back through ip_forward,
++ * preventing disappearing routes problems.
++ * Do sanity checking before investing too much CPU in allocating new
++ * structures.
++ * Fail on IP header options: We cannot process them yet.
++ * Add some helpful comments.
++ * Use virtual device for parameters instead of physical device.
++ *
++ * Revision 1.22 1999/02/10 03:03:02 rgb
++ * Duh. Fixed the TTL bug: forgot to update the checksum.
++ *
++ * Revision 1.21 1999/02/09 23:17:53 rgb
++ * Add structure members to ipsec_print_ip debug function.
++ * Temporarily fix TTL bug preventing tunnel mode from functioning.
++ *
++ * Revision 1.20 1999/02/09 00:14:25 rgb
++ * Add KLIPSPRINT macro. (Not used yet, though.)
++ * Delete old ip_tunnel code (BADCODE).
++ * Decrement TTL in outgoing packet.
++ * Set TTL on new IPIP_TUNNEL to default, not existing packet TTL.
++ * Delete ethernet only feature and fix hard-coded hard_header_len.
++ *
++ * Revision 1.19 1999/01/29 17:56:22 rgb
++ * 64-bit re-fix submitted by Peter Onion.
++ *
++ * Revision 1.18 1999/01/28 22:43:24 rgb
++ * Fixed bug in ipsec_print_ip that caused an OOPS, found by P.Onion.
++ *
++ * Revision 1.17 1999/01/26 02:08:16 rgb
++ * Removed CONFIG_IPSEC_ALGO_SWITCH macro.
++ * Removed dead code.
++ *
++ * Revision 1.16 1999/01/22 06:25:26 rgb
++ * Cruft clean-out.
++ * Added algorithm switch code.
++ * 64-bit clean-up.
++ * Passthrough on IPIP protocol, spi 0x0 fix.
++ * Enhanced debugging.
++ *
++ * Revision 1.15 1998/12/01 13:22:04 rgb
++ * Added support for debug printing of version info.
++ *
++ * Revision 1.14 1998/11/30 13:22:55 rgb
++ * Rationalised all the klips kernel file headers. They are much shorter
++ * now and won't conflict under RH5.2.
++ *
++ * Revision 1.13 1998/11/17 21:13:52 rgb
++ * Put IKE port bypass debug output in user-switched debug statements.
++ *
++ * Revision 1.12 1998/11/13 13:20:25 rgb
++ * Fixed ntohs bug in udp/500 hole for IKE.
++ *
++ * Revision 1.11 1998/11/10 08:01:19 rgb
++ * Kill tcp/500 hole, keep udp/500 hole.
++ *
++ * Revision 1.10 1998/11/09 21:29:26 rgb
++ * If no eroute is found, discard packet and incr. tx_error.
++ *
++ * Revision 1.9 1998/10/31 06:50:00 rgb
++ * Add tcp/udp/500 bypass.
++ * Fixed up comments in #endif directives.
++ *
++ * Revision 1.8 1998/10/27 00:34:31 rgb
++ * Reformat debug output of IP headers.
++ * Newlines added before calls to ipsec_print_ip.
++ *
++ * Revision 1.7 1998/10/19 14:44:28 rgb
++ * Added inclusion of freeswan.h.
++ * sa_id structure implemented and used: now includes protocol.
++ *
++ * Revision 1.6 1998/10/09 04:31:35 rgb
++ * Added 'klips_debug' prefix to all klips printk debug statements.
++ *
++ * Revision 1.5 1998/08/28 03:09:51 rgb
++ * Prevent kernel log spam with default route through ipsec.
++ *
++ * Revision 1.4 1998/08/05 22:23:09 rgb
++ * Change setdev return code to ENXIO for a non-existant physical device.
++ *
++ * Revision 1.3 1998/07/29 20:41:11 rgb
++ * Add ipsec_tunnel_clear to clear all tunnel attachments.
++ *
++ * Revision 1.2 1998/06/25 20:00:33 rgb
++ * Clean up #endif comments.
++ * Rename dev_ipsec to dev_ipsec0 for consistency.
++ * Document ipsec device fields.
++ * Make ipsec_tunnel_probe visible from rest of kernel for static linking.
++ * Get debugging report for *every* ipsec device initialisation.
++ * Comment out redundant code.
++ *
++ * Revision 1.1 1998/06/18 21:27:50 henry
++ * move sources from klips/src to klips/net/ipsec, to keep stupid
++ * kernel-build scripts happier in the presence of symlinks
++ *
++ * Revision 1.8 1998/06/14 23:49:40 rgb
++ * Clarify version reporting on module loading.
++ *
++ * Revision 1.7 1998/05/27 23:19:20 rgb
++ * Added version reporting.
++ *
++ * Revision 1.6 1998/05/18 21:56:23 rgb
++ * Clean up for numerical consistency of output and cleaning up debug code.
++ *
++ * Revision 1.5 1998/05/12 02:44:23 rgb
++ * Clarifying 'no e-route to host' message.
++ *
++ * Revision 1.4 1998/04/30 15:34:35 rgb
++ * Enclosed most remaining debugging statements in #ifdef's to make it quieter.
++ *
++ * Revision 1.3 1998/04/21 21:28:54 rgb
++ * Rearrange debug switches to change on the fly debug output from user
++ * space. Only kernel changes checked in at this time. radij.c was also
++ * changed to temporarily remove buggy debugging code in rj_delete causing
++ * an OOPS and hence, netlink device open errors.
++ *
++ * Revision 1.2 1998/04/12 22:03:24 rgb
++ * Updated ESP-3DES-HMAC-MD5-96,
++ * ESP-DES-HMAC-MD5-96,
++ * AH-HMAC-MD5-96,
++ * AH-HMAC-SHA1-96 since Henry started freeswan cvs repository
++ * from old standards (RFC182[5-9] to new (as of March 1998) drafts.
++ *
++ * Fixed eroute references in /proc/net/ipsec*.
++ *
++ * Started to patch module unloading memory leaks in ipsec_netlink and
++ * radij tree unloading.
++ *
++ * Revision 1.1 1998/04/09 03:06:12 henry
++ * sources moved up from linux/net/ipsec
++ *
++ * Revision 1.1.1.1 1998/04/08 05:35:04 henry
++ * RGB's ipsec-0.8pre2.tar.gz ipsec-0.8
++ *
++ * Revision 0.5 1997/06/03 04:24:48 ji
++ * Added transport mode.
++ * Changed the way routing is done.
++ * Lots of bug fixes.
++ *
++ * Revision 0.4 1997/01/15 01:28:15 ji
++ * No changes.
++ *
++ * Revision 0.3 1996/11/20 14:39:04 ji
++ * Minor cleanups.
++ * Rationalized debugging code.
++ *
++ * Revision 0.2 1996/11/02 00:18:33 ji
++ * First limited release.
++ *
++ * Local Variables:
++ * c-style: linux
++ * End:
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/net/ipsec/ipsec_xform.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,351 @@
++/*
++ * Common routines for IPSEC transformations.
++ * Copyright (C) 1996, 1997 John Ioannidis.
++ * Copyright (C) 1998, 1999, 2000, 2001 Richard Guy Briggs.
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ * RCSID $Id$
++ */
++
++#include <linux/config.h>
++#include <linux/version.h>
++#include <linux/kernel.h> /* printk() */
++
++#include "freeswan/ipsec_param.h"
++
++#ifdef MALLOC_SLAB
++# include <linux/slab.h> /* kmalloc() */
++#else /* MALLOC_SLAB */
++# include <linux/malloc.h> /* kmalloc() */
++#endif /* MALLOC_SLAB */
++#include <linux/errno.h> /* error codes */
++#include <linux/types.h> /* size_t */
++#include <linux/interrupt.h> /* mark_bh */
++
++#include <linux/netdevice.h> /* struct device, and other headers */
++#include <linux/etherdevice.h> /* eth_type_trans */
++#include <linux/ip.h> /* struct iphdr */
++#include <linux/skbuff.h>
++#include <linux/random.h> /* get_random_bytes() */
++#include <freeswan.h>
++#ifdef SPINLOCK
++# ifdef SPINLOCK_23
++# include <linux/spinlock.h> /* *lock* */
++# else /* SPINLOCK_23 */
++# include <asm/spinlock.h> /* *lock* */
++# endif /* SPINLOCK_23 */
++#endif /* SPINLOCK */
++#ifdef NET_21
++# include <asm/uaccess.h>
++# include <linux/in6.h>
++#endif
++#include <asm/checksum.h>
++#include <net/ip.h>
++
++#include "freeswan/radij.h"
++#include "freeswan/ipsec_encap.h"
++#include "freeswan/ipsec_radij.h"
++#include "freeswan/ipsec_xform.h"
++#include "freeswan/ipsec_ipe4.h"
++#include "freeswan/ipsec_ah.h"
++#include "freeswan/ipsec_esp.h"
++
++#include <pfkeyv2.h>
++#include <pfkey.h>
++
++#ifdef CONFIG_IPSEC_DEBUG
++int debug_xform = 0;
++#endif /* CONFIG_IPSEC_DEBUG */
++
++#ifdef SPINLOCK
++spinlock_t tdb_lock = SPIN_LOCK_UNLOCKED;
++#else /* SPINLOCK */
++spinlock_t tdb_lock;
++#endif /* SPINLOCK */
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.63 2003/10/31 02:27:55 mcr
++ * pulled up port-selector patches and sa_id elimination.
++ *
++ * Revision 1.62.30.1 2003/10/29 01:30:41 mcr
++ * elimited "struct sa_id".
++ *
++ * Revision 1.62 2002/05/14 02:34:21 rgb
++ * Delete stale code.
++ *
++ * Revision 1.61 2002/04/24 07:55:32 mcr
++ * #include patches and Makefiles for post-reorg compilation.
++ *
++ * Revision 1.60 2002/04/24 07:36:33 mcr
++ * Moved from ./klips/net/ipsec/ipsec_xform.c,v
++ *
++ * Revision 1.59 2002/03/29 15:01:36 rgb
++ * Delete decommissioned code.
++ *
++ * Revision 1.58 2002/01/29 17:17:57 mcr
++ * moved include of ipsec_param.h to after include of linux/kernel.h
++ * otherwise, it seems that some option that is set in ipsec_param.h
++ * screws up something subtle in the include path to kernel.h, and
++ * it complains on the snprintf() prototype.
++ *
++ * Revision 1.57 2002/01/29 04:00:53 mcr
++ * more excise of kversions.h header.
++ *
++ * Revision 1.56 2001/11/27 05:17:22 mcr
++ * turn off the worst of the per-packet debugging.
++ *
++ * Revision 1.55 2001/11/26 09:23:50 rgb
++ * Merge MCR's ipsec_sa, eroute, proc and struct lifetime changes.
++ *
++ * Revision 1.54 2001/10/18 04:45:21 rgb
++ * 2.4.9 kernel deprecates linux/malloc.h in favour of linux/slab.h,
++ * lib/freeswan.h version macros moved to lib/kversions.h.
++ * Other compiler directive cleanups.
++ *
++ * Revision 1.53 2001/09/08 21:13:34 rgb
++ * Added pfkey ident extension support for ISAKMPd. (NetCelo)
++ *
++ * Revision 1.52 2001/06/14 19:35:11 rgb
++ * Update copyright date.
++ *
++ * Revision 1.51 2001/05/30 08:14:03 rgb
++ * Removed vestiges of esp-null transforms.
++ *
++ * Revision 1.50 2001/05/03 19:43:18 rgb
++ * Initialise error return variable.
++ * Update SENDERR macro.
++ * Fix sign of error return code for ipsec_tdbcleanup().
++ * Use more appropriate return code for ipsec_tdbwipe().
++ *
++ * Revision 1.49 2001/04/19 18:56:17 rgb
++ * Fixed tdb table locking comments.
++ *
++ * Revision 1.48 2001/02/27 22:24:55 rgb
++ * Re-formatting debug output (line-splitting, joining, 1arg/line).
++ * Check for satoa() return codes.
++ *
++ * Revision 1.47 2000/11/06 04:32:08 rgb
++ * Ditched spin_lock_irqsave in favour of spin_lock_bh.
++ *
++ * Revision 1.46 2000/09/20 16:21:57 rgb
++ * Cleaned up ident string alloc/free.
++ *
++ * Revision 1.45 2000/09/08 19:16:51 rgb
++ * Change references from DEBUG_IPSEC to CONFIG_IPSEC_DEBUG.
++ * Removed all references to CONFIG_IPSEC_PFKEYv2.
++ *
++ * Revision 1.44 2000/08/30 05:29:04 rgb
++ * Compiler-define out no longer used tdb_init() in ipsec_xform.c.
++ *
++ * Revision 1.43 2000/08/18 21:30:41 rgb
++ * Purged all tdb_spi, tdb_proto and tdb_dst macros. They are unclear.
++ *
++ * Revision 1.42 2000/08/01 14:51:51 rgb
++ * Removed _all_ remaining traces of DES.
++ *
++ * Revision 1.41 2000/07/28 14:58:31 rgb
++ * Changed kfree_s to kfree, eliminating extra arg to fix 2.4.0-test5.
++ *
++ * Revision 1.40 2000/06/28 05:50:11 rgb
++ * Actually set iv_bits.
++ *
++ * Revision 1.39 2000/05/10 23:11:09 rgb
++ * Added netlink debugging output.
++ * Added a cast to quiet down the ntohl bug.
++ *
++ * Revision 1.38 2000/05/10 19:18:42 rgb
++ * Cast output of ntohl so that the broken prototype doesn't make our
++ * compile noisy.
++ *
++ * Revision 1.37 2000/03/16 14:04:59 rgb
++ * Hardwired CONFIG_IPSEC_PFKEYv2 on.
++ *
++ * Revision 1.36 2000/01/26 10:11:28 rgb
++ * Fixed spacing in error text causing run-in words.
++ *
++ * Revision 1.35 2000/01/21 06:17:16 rgb
++ * Tidied up compiler directive indentation for readability.
++ * Added ictx,octx vars for simplification.(kravietz)
++ * Added macros for HMAC padding magic numbers.(kravietz)
++ * Fixed missing key length reporting bug.
++ * Fixed bug in tdbwipe to return immediately on NULL tdbp passed in.
++ *
++ * Revision 1.34 1999/12/08 00:04:19 rgb
++ * Fixed SA direction overwriting bug for netlink users.
++ *
++ * Revision 1.33 1999/12/01 22:16:44 rgb
++ * Minor formatting changes in ESP MD5 initialisation.
++ *
++ * Revision 1.32 1999/11/25 09:06:36 rgb
++ * Fixed error return messages, should be returning negative numbers.
++ * Implemented SENDERR macro for propagating error codes.
++ * Added debug message and separate error code for algorithms not compiled
++ * in.
++ *
++ * Revision 1.31 1999/11/23 23:06:26 rgb
++ * Sort out pfkey and freeswan headers, putting them in a library path.
++ *
++ * Revision 1.30 1999/11/18 04:09:20 rgb
++ * Replaced all kernel version macros to shorter, readable form.
++ *
++ * Revision 1.29 1999/11/17 15:53:40 rgb
++ * Changed all occurrences of #include "../../../lib/freeswan.h"
++ * to #include <freeswan.h> which works due to -Ilibfreeswan in the
++ * klips/net/ipsec/Makefile.
++ *
++ * Revision 1.28 1999/10/18 20:04:01 rgb
++ * Clean-out unused cruft.
++ *
++ * Revision 1.27 1999/10/03 19:01:03 rgb
++ * Spinlock support for 2.3.xx and 2.0.xx kernels.
++ *
++ * Revision 1.26 1999/10/01 16:22:24 rgb
++ * Switch from assignment init. to functional init. of spinlocks.
++ *
++ * Revision 1.25 1999/10/01 15:44:54 rgb
++ * Move spinlock header include to 2.1> scope.
++ *
++ * Revision 1.24 1999/10/01 00:03:46 rgb
++ * Added tdb structure locking.
++ * Minor formatting changes.
++ * Add function to initialize tdb hash table.
++ *
++ * Revision 1.23 1999/05/25 22:42:12 rgb
++ * Add deltdbchain() debugging.
++ *
++ * Revision 1.22 1999/05/25 21:24:31 rgb
++ * Add debugging statements to deltdbchain().
++ *
++ * Revision 1.21 1999/05/25 03:51:48 rgb
++ * Refix error return code.
++ *
++ * Revision 1.20 1999/05/25 03:34:07 rgb
++ * Fix error return for flush.
++ *
++ * Revision 1.19 1999/05/09 03:25:37 rgb
++ * Fix bug introduced by 2.2 quick-and-dirty patch.
++ *
++ * Revision 1.18 1999/05/05 22:02:32 rgb
++ * Add a quick and dirty port to 2.2 kernels by Marc Boucher <marc@mbsi.ca>.
++ *
++ * Revision 1.17 1999/04/29 15:20:16 rgb
++ * Change gettdb parameter to a pointer to reduce stack loading and
++ * facilitate parameter sanity checking.
++ * Add sanity checking for null pointer arguments.
++ * Add debugging instrumentation.
++ * Add function deltdbchain() which will take care of unlinking,
++ * zeroing and deleting a chain of tdbs.
++ * Add a parameter to tdbcleanup to be able to delete a class of SAs.
++ * tdbwipe now actually zeroes the tdb as well as any of its pointed
++ * structures.
++ *
++ * Revision 1.16 1999/04/16 15:36:29 rgb
++ * Fix cut-and-paste error causing a memory leak in IPIP TDB freeing.
++ *
++ * Revision 1.15 1999/04/11 00:29:01 henry
++ * GPL boilerplate
++ *
++ * Revision 1.14 1999/04/06 04:54:28 rgb
++ * Fix/Add RCSID Id: and Log: bits to make PHMDs happy. This includes
++ * patch shell fixes.
++ *
++ * Revision 1.13 1999/02/19 18:23:01 rgb
++ * Nix debug off compile warning.
++ *
++ * Revision 1.12 1999/02/17 16:52:16 rgb
++ * Consolidate satoa()s for space and speed efficiency.
++ * Convert DEBUG_IPSEC to KLIPS_PRINT
++ * Clean out unused cruft.
++ * Ditch NET_IPIP dependancy.
++ * Loop for 3des key setting.
++ *
++ * Revision 1.11 1999/01/26 02:09:05 rgb
++ * Remove ah/esp/IPIP switching on include files.
++ * Removed CONFIG_IPSEC_ALGO_SWITCH macro.
++ * Removed dead code.
++ * Clean up debug code when switched off.
++ * Remove references to INET_GET_PROTOCOL.
++ * Added code exclusion macros to reduce code from unused algorithms.
++ *
++ * Revision 1.10 1999/01/22 06:28:55 rgb
++ * Cruft clean-out.
++ * Put random IV generation in kernel.
++ * Added algorithm switch code.
++ * Enhanced debugging.
++ * 64-bit clean-up.
++ *
++ * Revision 1.9 1998/11/30 13:22:55 rgb
++ * Rationalised all the klips kernel file headers. They are much shorter
++ * now and won't conflict under RH5.2.
++ *
++ * Revision 1.8 1998/11/25 04:59:06 rgb
++ * Add conditionals for no IPIP tunnel code.
++ * Delete commented out code.
++ *
++ * Revision 1.7 1998/10/31 06:50:41 rgb
++ * Convert xform ASCII names to no spaces.
++ * Fixed up comments in #endif directives.
++ *
++ * Revision 1.6 1998/10/19 14:44:28 rgb
++ * Added inclusion of freeswan.h.
++ * sa_id structure implemented and used: now includes protocol.
++ *
++ * Revision 1.5 1998/10/09 04:32:19 rgb
++ * Added 'klips_debug' prefix to all klips printk debug statements.
++ *
++ * Revision 1.4 1998/08/12 00:11:31 rgb
++ * Added new xform functions to the xform table.
++ * Fixed minor debug output spelling error.
++ *
++ * Revision 1.3 1998/07/09 17:45:31 rgb
++ * Clarify algorithm not available message.
++ *
++ * Revision 1.2 1998/06/23 03:00:51 rgb
++ * Check for presence of IPIP protocol if it is setup one way (we don't
++ * know what has been set up the other way and can only assume it will be
++ * symmetrical with the exception of keys).
++ *
++ * Revision 1.1 1998/06/18 21:27:51 henry
++ * move sources from klips/src to klips/net/ipsec, to keep stupid
++ * kernel-build scripts happier in the presence of symlinks
++ *
++ * Revision 1.3 1998/06/11 05:54:59 rgb
++ * Added transform version string pointer to xformsw initialisations.
++ *
++ * Revision 1.2 1998/04/21 21:28:57 rgb
++ * Rearrange debug switches to change on the fly debug output from user
++ * space. Only kernel changes checked in at this time. radij.c was also
++ * changed to temporarily remove buggy debugging code in rj_delete causing
++ * an OOPS and hence, netlink device open errors.
++ *
++ * Revision 1.1 1998/04/09 03:06:13 henry
++ * sources moved up from linux/net/ipsec
++ *
++ * Revision 1.1.1.1 1998/04/08 05:35:02 henry
++ * RGB's ipsec-0.8pre2.tar.gz ipsec-0.8
++ *
++ * Revision 0.5 1997/06/03 04:24:48 ji
++ * Added ESP-3DES-MD5-96
++ *
++ * Revision 0.4 1997/01/15 01:28:15 ji
++ * Added new transforms.
++ *
++ * Revision 0.3 1996/11/20 14:39:04 ji
++ * Minor cleanups.
++ * Rationalized debugging code.
++ *
++ * Revision 0.2 1996/11/02 00:18:33 ji
++ * First limited release.
++ *
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/net/ipsec/ipsec_xmit.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,1869 @@
++/*
++ * IPSEC Transmit code.
++ * Copyright (C) 1996, 1997 John Ioannidis.
++ * Copyright (C) 1998-2003 Richard Guy Briggs.
++ * Copyright (C) 2004 Michael Richardson <mcr@xelerance.com>
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ */
++
++char ipsec_xmit_c_version[] = "RCSID $Id$";
++
++#define __NO_VERSION__
++#include <linux/module.h>
++#include <linux/config.h> /* for CONFIG_IP_FORWARD */
++#include <linux/version.h>
++#include <linux/kernel.h> /* printk() */
++
++#include "openswan/ipsec_param.h"
++
++#ifdef MALLOC_SLAB
++# include <linux/slab.h> /* kmalloc() */
++#else /* MALLOC_SLAB */
++# include <linux/malloc.h> /* kmalloc() */
++#endif /* MALLOC_SLAB */
++#include <linux/errno.h> /* error codes */
++#include <linux/types.h> /* size_t */
++#include <linux/interrupt.h> /* mark_bh */
++
++#include <linux/netdevice.h> /* struct device, struct net_device_stats, dev_queue_xmit() and other headers */
++#include <linux/etherdevice.h> /* eth_type_trans */
++#include <linux/ip.h> /* struct iphdr */
++#include <linux/tcp.h> /* struct tcphdr */
++#include <linux/udp.h> /* struct udphdr */
++#include <linux/skbuff.h>
++#include <openswan.h>
++#ifdef NET_21
++# define MSS_HACK_ /* experimental */
++# include <asm/uaccess.h>
++# include <linux/in6.h>
++# include <net/dst.h>
++# define proto_priv cb
++#endif /* NET_21 */
++#include <asm/checksum.h>
++#include <net/icmp.h> /* icmp_send() */
++#include <net/ip.h>
++#ifdef NETDEV_23
++# include <linux/netfilter_ipv4.h>
++#endif /* NETDEV_23 */
++
++#include <linux/if_arp.h>
++#ifdef MSS_HACK
++# include <net/tcp.h> /* TCP options */
++#endif /* MSS_HACK */
++
++#include "openswan/radij.h"
++#include "openswan/ipsec_life.h"
++#include "openswan/ipsec_xform.h"
++#include "openswan/ipsec_eroute.h"
++#include "openswan/ipsec_encap.h"
++#include "openswan/ipsec_radij.h"
++#include "openswan/ipsec_xmit.h"
++#include "openswan/ipsec_sa.h"
++#include "openswan/ipsec_tunnel.h"
++#include "openswan/ipsec_ipe4.h"
++#include "openswan/ipsec_ah.h"
++#include "openswan/ipsec_esp.h"
++
++#ifdef CONFIG_IPSEC_IPCOMP
++#include "openswan/ipcomp.h"
++#endif /* CONFIG_IPSEC_IPCOMP */
++
++#include <pfkeyv2.h>
++#include <pfkey.h>
++
++#include "openswan/ipsec_proto.h"
++#include "openswan/ipsec_alg.h"
++
++
++/*
++ * Stupid kernel API differences in APIs. Not only do some
++ * kernels not have ip_select_ident, but some have differing APIs,
++ * and SuSE has one with one parameter, but no way of checking to
++ * see what is really what.
++ */
++
++#ifdef SUSE_LINUX_2_4_19_IS_STUPID
++#define KLIPS_IP_SELECT_IDENT(iph, skb) ip_select_ident(iph)
++#else
++
++/* simplest case, nothing */
++#if !defined(IP_SELECT_IDENT)
++#define KLIPS_IP_SELECT_IDENT(iph, skb) do { iph->id = htons(ip_id_count++); } while(0)
++#endif
++
++/* kernels > 2.3.37-ish */
++#if defined(IP_SELECT_IDENT) && !defined(IP_SELECT_IDENT_NEW)
++#define KLIPS_IP_SELECT_IDENT(iph, skb) ip_select_ident(iph, skb->dst)
++#endif
++
++/* kernels > 2.4.2 */
++#if defined(IP_SELECT_IDENT) && defined(IP_SELECT_IDENT_NEW)
++#define KLIPS_IP_SELECT_IDENT(iph, skb) ip_select_ident(iph, skb->dst, NULL)
++#endif
++
++#endif /* SUSE_LINUX_2_4_19_IS_STUPID */
++
++
++static __u32 zeroes[64];
++
++#ifdef CONFIG_IPSEC_DEBUG
++int sysctl_ipsec_debug_verbose = 0;
++#endif /* CONFIG_IPSEC_DEBUG */
++
++int ipsec_xmit_trap_count = 0;
++int ipsec_xmit_trap_sendcount = 0;
++
++int sysctl_ipsec_icmp = 0;
++int sysctl_ipsec_tos = 0;
++
++#ifdef CONFIG_IPSEC_DEBUG_
++DEBUG_NO_STATIC void
++dmp(char *s, caddr_t bb, int len)
++{
++ int i;
++ unsigned char *b = bb;
++
++ if (debug_tunnel) {
++ printk(KERN_INFO "klips_debug:ipsec_tunnel_:dmp: "
++ "at %s, len=%d:",
++ s,
++ len);
++ for (i=0; i < len; i++) {
++ if(!(i%16)){
++ printk("\nklips_debug: ");
++ }
++ printk(" %02x", *b++);
++ }
++ printk("\n");
++ }
++}
++#else /* CONFIG_IPSEC_DEBUG */
++#define dmp(_x, _y, _z)
++#endif /* CONFIG_IPSEC_DEBUG */
++
++#ifndef SKB_COPY_EXPAND
++/*
++ * This is mostly skbuff.c:skb_copy().
++ */
++struct sk_buff *
++skb_copy_expand(struct sk_buff *skb, int headroom, int tailroom, int priority)
++{
++ struct sk_buff *n;
++ unsigned long offset;
++
++ /*
++ * Do sanity checking
++ */
++ if((headroom < 0) || (tailroom < 0) || ((headroom+tailroom) < 0)) {
++ printk(KERN_WARNING
++ "klips_error:skb_copy_expand: "
++ "Illegal negative head,tailroom %d,%d\n",
++ headroom,
++ tailroom);
++ return NULL;
++ }
++ /*
++ * Allocate the copy buffer
++ */
++
++#ifndef NET_21
++ IS_SKB(skb);
++#endif /* !NET_21 */
++
++
++ n=alloc_skb(skb->end - skb->head + headroom + tailroom, priority);
++
++ KLIPS_PRINT(debug_tunnel & DB_TN_CROUT,
++ "klips_debug:skb_copy_expand: "
++ "allocating %d bytes, head=0p%p data=0p%p tail=0p%p end=0p%p end-head=%d tail-data=%d\n",
++ skb->end - skb->head + headroom + tailroom,
++ skb->head,
++ skb->data,
++ skb->tail,
++ skb->end,
++ skb->end - skb->head,
++ skb->tail - skb->data);
++
++ if(n==NULL)
++ return NULL;
++
++ /*
++ * Shift between the two data areas in bytes
++ */
++
++ /* Set the data pointer */
++ skb_reserve(n,skb->data-skb->head+headroom);
++ /* Set the tail pointer and length */
++ if(skb_tailroom(n) < skb->len) {
++ printk(KERN_WARNING "klips_error:skb_copy_expand: "
++ "tried to skb_put %ld, %d available. This should never happen, please report.\n",
++ (unsigned long int)skb->len,
++ skb_tailroom(n));
++ ipsec_kfree_skb(n);
++ return NULL;
++ }
++ skb_put(n,skb->len);
++
++ offset=n->head + headroom - skb->head;
++
++ /* Copy the bytes */
++ memcpy(n->head + headroom, skb->head,skb->end-skb->head);
++#ifdef NET_21
++ n->csum=skb->csum;
++ n->priority=skb->priority;
++ n->dst=dst_clone(skb->dst);
++ if(skb->nh.raw)
++ n->nh.raw=skb->nh.raw+offset;
++#ifndef NETDEV_23
++ n->is_clone=0;
++#endif /* NETDEV_23 */
++ atomic_set(&n->users, 1);
++ n->destructor = NULL;
++ n->security=skb->security;
++#else /* NET_21 */
++ n->link3=NULL;
++ n->when=skb->when;
++ if(skb->ip_hdr)
++ n->ip_hdr=(struct iphdr *)(((char *)skb->ip_hdr)+offset);
++ n->saddr=skb->saddr;
++ n->daddr=skb->daddr;
++ n->raddr=skb->raddr;
++ n->seq=skb->seq;
++ n->end_seq=skb->end_seq;
++ n->ack_seq=skb->ack_seq;
++ n->acked=skb->acked;
++ n->free=1;
++ n->arp=skb->arp;
++ n->tries=0;
++ n->lock=0;
++ n->users=0;
++#endif /* NET_21 */
++ n->protocol=skb->protocol;
++ n->list=NULL;
++ n->sk=NULL;
++ n->dev=skb->dev;
++ if(skb->h.raw)
++ n->h.raw=skb->h.raw+offset;
++ if(skb->mac.raw)
++ n->mac.raw=skb->mac.raw+offset;
++ memcpy(n->proto_priv, skb->proto_priv, sizeof(skb->proto_priv));
++#ifndef NETDEV_23
++ n->used=skb->used;
++#endif /* !NETDEV_23 */
++ n->pkt_type=skb->pkt_type;
++ n->stamp=skb->stamp;
++
++#ifndef NET_21
++ IS_SKB(n);
++#endif /* !NET_21 */
++ return n;
++}
++#endif /* !SKB_COPY_EXPAND */
++
++#ifdef CONFIG_IPSEC_DEBUG
++void
++ipsec_print_ip(struct iphdr *ip)
++{
++ char buf[ADDRTOA_BUF];
++
++ printk(KERN_INFO "klips_debug: IP:");
++ printk(" ihl:%d", ip->ihl << 2);
++ printk(" ver:%d", ip->version);
++ printk(" tos:%d", ip->tos);
++ printk(" tlen:%d", ntohs(ip->tot_len));
++ printk(" id:%d", ntohs(ip->id));
++ printk(" %s%s%sfrag_off:%d",
++ ip->frag_off & __constant_htons(IP_CE) ? "CE " : "",
++ ip->frag_off & __constant_htons(IP_DF) ? "DF " : "",
++ ip->frag_off & __constant_htons(IP_MF) ? "MF " : "",
++ (ntohs(ip->frag_off) & IP_OFFSET) << 3);
++ printk(" ttl:%d", ip->ttl);
++ printk(" proto:%d", ip->protocol);
++ if(ip->protocol == IPPROTO_UDP)
++ printk(" (UDP)");
++ if(ip->protocol == IPPROTO_TCP)
++ printk(" (TCP)");
++ if(ip->protocol == IPPROTO_ICMP)
++ printk(" (ICMP)");
++ printk(" chk:%d", ntohs(ip->check));
++ addrtoa(*((struct in_addr*)(&ip->saddr)), 0, buf, sizeof(buf));
++ printk(" saddr:%s", buf);
++ if(ip->protocol == IPPROTO_UDP)
++ printk(":%d",
++ ntohs(((struct udphdr*)((caddr_t)ip + (ip->ihl << 2)))->source));
++ if(ip->protocol == IPPROTO_TCP)
++ printk(":%d",
++ ntohs(((struct tcphdr*)((caddr_t)ip + (ip->ihl << 2)))->source));
++ addrtoa(*((struct in_addr*)(&ip->daddr)), 0, buf, sizeof(buf));
++ printk(" daddr:%s", buf);
++ if(ip->protocol == IPPROTO_UDP)
++ printk(":%d",
++ ntohs(((struct udphdr*)((caddr_t)ip + (ip->ihl << 2)))->dest));
++ if(ip->protocol == IPPROTO_TCP)
++ printk(":%d",
++ ntohs(((struct tcphdr*)((caddr_t)ip + (ip->ihl << 2)))->dest));
++ if(ip->protocol == IPPROTO_ICMP)
++ printk(" type:code=%d:%d",
++ ((struct icmphdr*)((caddr_t)ip + (ip->ihl << 2)))->type,
++ ((struct icmphdr*)((caddr_t)ip + (ip->ihl << 2)))->code);
++ printk("\n");
++
++ if(sysctl_ipsec_debug_verbose) {
++ __u8 *c;
++ int i;
++
++ c = ((__u8*)ip) + ip->ihl*4;
++ for(i = 0; i < ntohs(ip->tot_len) - ip->ihl*4; i++ /*, c++*/) {
++ if(!(i % 16)) {
++ printk(KERN_INFO
++ "klips_debug: @%03x:",
++ i);
++ }
++ printk(" %02x", /***/c[i]);
++ if(!((i + 1) % 16)) {
++ printk("\n");
++ }
++ }
++ if(i % 16) {
++ printk("\n");
++ }
++ }
++}
++#endif /* CONFIG_IPSEC_DEBUG */
++
++#ifdef MSS_HACK
++/*
++ * Issues:
++ * 1) Fragments arriving in the tunnel should probably be rejected.
++ * 2) How does this affect syncookies, mss_cache, dst cache ?
++ * 3) Path MTU discovery handling needs to be reviewed. For example,
++ * if we receive an ICMP 'packet too big' message from an intermediate
++ * router specifying it's next hop MTU, our stack may process this and
++ * adjust the MSS without taking our AH/ESP overheads into account.
++ */
++
++
++/*
++ * Recaclulate checksum using differences between changed datum,
++ * borrowed from netfilter.
++ */
++DEBUG_NO_STATIC u_int16_t
++ipsec_fast_csum(u_int32_t oldvalinv, u_int32_t newval, u_int16_t oldcheck)
++{
++ u_int32_t diffs[] = { oldvalinv, newval };
++ return csum_fold(csum_partial((char *)diffs, sizeof(diffs),
++ oldcheck^0xFFFF));
++}
++
++/*
++ * Determine effective MSS.
++ *
++ * Note that we assume that there is always an MSS option for our own
++ * SYN segments, which is mentioned in tcp_syn_build_options(), kernel 2.2.x.
++ * This could change, and we should probably parse TCP options instead.
++ *
++ */
++DEBUG_NO_STATIC u_int8_t
++ipsec_adjust_mss(struct sk_buff *skb, struct tcphdr *tcph, u_int16_t mtu)
++{
++ u_int16_t oldmss, newmss;
++ u_int32_t *mssp;
++ struct sock *sk = skb->sk;
++
++ newmss = tcp_sync_mss(sk, mtu);
++ printk(KERN_INFO "klips: setting mss to %u\n", newmss);
++ mssp = (u_int32_t *)tcph + sizeof(struct tcphdr) / sizeof(u_int32_t);
++ oldmss = ntohl(*mssp) & 0x0000FFFF;
++ *mssp = htonl((TCPOPT_MSS << 24) | (TCPOLEN_MSS << 16) | newmss);
++ tcph->check = ipsec_fast_csum(htons(~oldmss),
++ htons(newmss), tcph->check);
++ return 1;
++}
++#endif /* MSS_HACK */
++
++/*
++ * Sanity checks
++ */
++enum ipsec_xmit_value
++ipsec_xmit_sanity_check_dev(struct ipsec_xmit_state *ixs)
++{
++
++ if (ixs->dev == NULL) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
++ "klips_error:ipsec_xmit_sanity_check_dev: "
++ "No device associated with skb!\n" );
++ return IPSEC_XMIT_NODEV;
++ }
++
++ ixs->prv = ixs->dev->priv;
++ if (ixs->prv == NULL) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
++ "klips_error:ipsec_xmit_sanity_check_dev: "
++ "Device has no private structure!\n" );
++ return IPSEC_XMIT_NOPRIVDEV;
++ }
++
++ ixs->physdev = ixs->prv->dev;
++ if (ixs->physdev == NULL) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
++ "klips_error:ipsec_xmit_sanity_check_dev: "
++ "Device is not attached to physical device!\n" );
++ return IPSEC_XMIT_NOPHYSDEV;
++ }
++
++ ixs->physmtu = ixs->physdev->mtu;
++
++ ixs->stats = (struct net_device_stats *) &(ixs->prv->mystats);
++
++ return IPSEC_XMIT_OK;
++}
++
++enum ipsec_xmit_value
++ipsec_xmit_sanity_check_skb(struct ipsec_xmit_state *ixs)
++{
++ /*
++ * Return if there is nothing to do. (Does this ever happen?) XXX
++ */
++ if (ixs->skb == NULL) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
++ "klips_error:ipsec_xmit_sanity_check_skb: "
++ "Nothing to do!\n" );
++ return IPSEC_XMIT_NOSKB;
++ }
++#ifdef NET_21
++ /* if skb was cloned (most likely due to a packet sniffer such as
++ tcpdump being momentarily attached to the interface), make
++ a copy of our own to modify */
++ if(skb_cloned(ixs->skb)) {
++ if
++#ifdef SKB_COW_NEW
++ (skb_cow(ixs->skb, skb_headroom(ixs->skb)) != 0)
++#else /* SKB_COW_NEW */
++ ((ixs->skb = skb_cow(ixs->skb, skb_headroom(ixs->skb))) == NULL)
++#endif /* SKB_COW_NEW */
++ {
++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
++ "klips_error:ipsec_xmit_sanity_check_skb: "
++ "skb_cow failed to allocate buffer, dropping.\n" );
++ ixs->stats->tx_dropped++;
++ return IPSEC_XMIT_ERRSKBALLOC;
++ }
++ }
++#endif /* NET_21 */
++
++#ifdef NET_21
++ ixs->iph = ixs->skb->nh.iph;
++#else /* NET_21 */
++ ixs->iph = ixs->skb->ip_hdr;
++#endif /* NET_21 */
++
++ /* sanity check for IP version as we can't handle IPv6 right now */
++ if (ixs->iph->version != 4) {
++ KLIPS_PRINT(debug_tunnel,
++ "klips_debug:ipsec_xmit_sanity_check_skb: "
++ "found IP Version %d but cannot process other IP versions than v4.\n",
++ ixs->iph->version); /* XXX */
++ ixs->stats->tx_dropped++;
++ return IPSEC_XMIT_NOIPV6;
++ }
++
++#if IPSEC_DISALLOW_IPOPTIONS
++ if ((ixs->iph->ihl << 2) != sizeof (struct iphdr)) {
++ KLIPS_PRINT(debug_tunnel,
++ "klips_debug:ipsec_xmit_sanity_check_skb: "
++ "cannot process IP header options yet. May be mal-formed packet.\n"); /* XXX */
++ ixs->stats->tx_dropped++;
++ return IPSEC_XMIT_NOIPOPTIONS;
++ }
++#endif /* IPSEC_DISALLOW_IPOPTIONS */
++
++#ifndef NET_21
++ if (ixs->iph->ttl <= 0) {
++ /* Tell the sender its packet died... */
++ ICMP_SEND(ixs->skb, ICMP_TIME_EXCEEDED, ICMP_EXC_TTL, 0, ixs->physdev);
++
++ KLIPS_PRINT(debug_tunnel, "klips_debug:ipsec_xmit_sanity_check_skb: "
++ "TTL=0, too many hops!\n");
++ ixs->stats->tx_dropped++;
++ return IPSEC_XMIT_TTLEXPIRED;
++ }
++#endif /* !NET_21 */
++
++ return IPSEC_XMIT_OK;
++}
++
++enum ipsec_xmit_value
++ipsec_xmit_encap_once(struct ipsec_xmit_state *ixs)
++{
++#ifdef CONFIG_IPSEC_ESP
++ struct esphdr *espp;
++#ifdef CONFIG_IPSEC_ENC_3DES
++ __u32 iv[ESP_IV_MAXSZ_INT];
++#endif /* !CONFIG_IPSEC_ENC_3DES */
++ unsigned char *idat, *pad;
++ int authlen = 0, padlen = 0, i;
++#endif /* !CONFIG_IPSEC_ESP */
++#ifdef CONFIG_IPSEC_AH
++ struct iphdr ipo;
++ struct ahhdr *ahp;
++#endif /* CONFIG_IPSEC_AH */
++#if defined(CONFIG_IPSEC_AUTH_HMAC_MD5) || defined(CONFIG_IPSEC_AUTH_HMAC_SHA1)
++ union {
++#ifdef CONFIG_IPSEC_AUTH_HMAC_MD5
++ MD5_CTX md5;
++#endif /* CONFIG_IPSEC_AUTH_HMAC_MD5 */
++#ifdef CONFIG_IPSEC_AUTH_HMAC_SHA1
++ SHA1_CTX sha1;
++#endif /* CONFIG_IPSEC_AUTH_HMAC_SHA1 */
++ } tctx;
++ __u8 hash[AH_AMAX];
++#endif /* defined(CONFIG_IPSEC_AUTH_HMAC_MD5) || defined(CONFIG_IPSEC_AUTH_HMAC_SHA1) */
++ int headroom = 0, tailroom = 0, ilen = 0, len = 0;
++ unsigned char *dat;
++ int blocksize = 8; /* XXX: should be inside ixs --jjo */
++#ifdef CONFIG_IPSEC_ALG
++ struct ipsec_alg_enc *ixt_e = NULL;
++ struct ipsec_alg_auth *ixt_a = NULL;
++#endif /* CONFIG_IPSEC_ALG */
++
++ ixs->iphlen = ixs->iph->ihl << 2;
++ ixs->pyldsz = ntohs(ixs->iph->tot_len) - ixs->iphlen;
++ ixs->sa_len = satot(&ixs->ipsp->ips_said, 0, ixs->sa_txt, SATOT_BUF);
++ KLIPS_PRINT(debug_tunnel & DB_TN_OXFS,
++ "klips_debug:ipsec_xmit_encap_once: "
++ "calling output for <%s%s%s>, SA:%s\n",
++ IPS_XFORM_NAME(ixs->ipsp),
++ ixs->sa_len ? ixs->sa_txt : " (error)");
++
++ switch(ixs->ipsp->ips_said.proto) {
++#ifdef CONFIG_IPSEC_AH
++ case IPPROTO_AH:
++ headroom += sizeof(struct ahhdr);
++ break;
++#endif /* CONFIG_IPSEC_AH */
++#ifdef CONFIG_IPSEC_ESP
++ case IPPROTO_ESP:
++#ifdef CONFIG_IPSEC_ALG
++ if ((ixt_e=ixs->ipsp->ips_alg_enc)) {
++ blocksize = ixt_e->ixt_blocksize;
++ headroom += ESP_HEADER_LEN + ixt_e->ixt_ivlen/8;
++ } else
++#endif /* CONFIG_IPSEC_ALG */
++ switch(ixs->ipsp->ips_encalg) {
++#ifdef CONFIG_IPSEC_ENC_3DES
++ case ESP_3DES:
++ headroom += sizeof(struct esphdr);
++ break;
++#endif /* CONFIG_IPSEC_ENC_3DES */
++ default:
++ ixs->stats->tx_errors++;
++ return IPSEC_XMIT_ESP_BADALG;
++ }
++#ifdef CONFIG_IPSEC_ALG
++ if ((ixt_a=ixs->ipsp->ips_alg_auth)) {
++ tailroom += AHHMAC_HASHLEN;
++ } else
++#endif /* CONFIG_IPSEC_ALG */
++ switch(ixs->ipsp->ips_authalg) {
++#ifdef CONFIG_IPSEC_AUTH_HMAC_MD5
++ case AH_MD5:
++ authlen = AHHMAC_HASHLEN;
++ break;
++#endif /* CONFIG_IPSEC_AUTH_HMAC_MD5 */
++#ifdef CONFIG_IPSEC_AUTH_HMAC_SHA1
++ case AH_SHA:
++ authlen = AHHMAC_HASHLEN;
++ break;
++#endif /* CONFIG_IPSEC_AUTH_HMAC_SHA1 */
++ case AH_NONE:
++ break;
++ default:
++ ixs->stats->tx_errors++;
++ return IPSEC_XMIT_ESP_BADALG;
++ }
++#ifdef CONFIG_IPSEC_ALG
++ tailroom += blocksize != 1 ?
++ ((blocksize - ((ixs->pyldsz + 2) % blocksize)) % blocksize) + 2 :
++ ((4 - ((ixs->pyldsz + 2) % 4)) % 4) + 2;
++#else
++ tailroom += ((8 - ((ixs->pyldsz + 2 * sizeof(unsigned char)) % 8)) % 8) + 2;
++#endif /* CONFIG_IPSEC_ALG */
++ tailroom += authlen;
++ break;
++#endif /* !CONFIG_IPSEC_ESP */
++#ifdef CONFIG_IPSEC_IPIP
++ case IPPROTO_IPIP:
++ headroom += sizeof(struct iphdr);
++ ixs->iphlen = sizeof(struct iphdr);
++ break;
++#endif /* !CONFIG_IPSEC_IPIP */
++#ifdef CONFIG_IPSEC_IPCOMP
++ case IPPROTO_COMP:
++ break;
++#endif /* CONFIG_IPSEC_IPCOMP */
++ default:
++ ixs->stats->tx_errors++;
++ return IPSEC_XMIT_BADPROTO;
++ }
++
++ KLIPS_PRINT(debug_tunnel & DB_TN_CROUT,
++ "klips_debug:ipsec_xmit_encap_once: "
++ "pushing %d bytes, putting %d, proto %d.\n",
++ headroom, tailroom, ixs->ipsp->ips_said.proto);
++ if(skb_headroom(ixs->skb) < headroom) {
++ printk(KERN_WARNING
++ "klips_error:ipsec_xmit_encap_once: "
++ "tried to skb_push headroom=%d, %d available. This should never happen, please report.\n",
++ headroom, skb_headroom(ixs->skb));
++ ixs->stats->tx_errors++;
++ return IPSEC_XMIT_ESP_PUSHPULLERR;
++ }
++ dat = skb_push(ixs->skb, headroom);
++ ilen = ixs->skb->len - tailroom;
++ if(skb_tailroom(ixs->skb) < tailroom) {
++ printk(KERN_WARNING
++ "klips_error:ipsec_xmit_encap_once: "
++ "tried to skb_put %d, %d available. This should never happen, please report.\n",
++ tailroom, skb_tailroom(ixs->skb));
++ ixs->stats->tx_errors++;
++ return IPSEC_XMIT_ESP_PUSHPULLERR;
++ }
++ skb_put(ixs->skb, tailroom);
++ KLIPS_PRINT(debug_tunnel & DB_TN_CROUT,
++ "klips_debug:ipsec_xmit_encap_once: "
++ "head,tailroom: %d,%d before xform.\n",
++ skb_headroom(ixs->skb), skb_tailroom(ixs->skb));
++ len = ixs->skb->len;
++ if(len > 0xfff0) {
++ printk(KERN_WARNING "klips_error:ipsec_xmit_encap_once: "
++ "tot_len (%d) > 65520. This should never happen, please report.\n",
++ len);
++ ixs->stats->tx_errors++;
++ return IPSEC_XMIT_BADLEN;
++ }
++ memmove((void *)dat, (void *)(dat + headroom), ixs->iphlen);
++ ixs->iph = (struct iphdr *)dat;
++ ixs->iph->tot_len = htons(ixs->skb->len);
++
++ switch(ixs->ipsp->ips_said.proto) {
++#ifdef CONFIG_IPSEC_ESP
++ case IPPROTO_ESP:
++ espp = (struct esphdr *)(dat + ixs->iphlen);
++ espp->esp_spi = ixs->ipsp->ips_said.spi;
++ espp->esp_rpl = htonl(++(ixs->ipsp->ips_replaywin_lastseq));
++
++#ifdef CONFIG_IPSEC_ALG
++ if (!ixt_e)
++#endif /* CONFIG_IPSEC_ALG */
++ switch(ixs->ipsp->ips_encalg) {
++#if defined(CONFIG_IPSEC_ENC_3DES)
++#ifdef CONFIG_IPSEC_ENC_3DES
++ case ESP_3DES:
++#endif /* CONFIG_IPSEC_ENC_3DES */
++ iv[0] = *((__u32*)&(espp->esp_iv) ) =
++ ((__u32*)(ixs->ipsp->ips_iv))[0];
++ iv[1] = *((__u32*)&(espp->esp_iv) + 1) =
++ ((__u32*)(ixs->ipsp->ips_iv))[1];
++ break;
++#endif /* defined(CONFIG_IPSEC_ENC_3DES) */
++ default:
++ ixs->stats->tx_errors++;
++ return IPSEC_XMIT_ESP_BADALG;
++ }
++
++ idat = dat + ixs->iphlen + headroom;
++ ilen = len - (ixs->iphlen + headroom + authlen);
++
++ /* Self-describing padding */
++ pad = &dat[len - tailroom];
++ padlen = tailroom - 2 - authlen;
++ for (i = 0; i < padlen; i++) {
++ pad[i] = i + 1;
++ }
++ dat[len - authlen - 2] = padlen;
++
++ dat[len - authlen - 1] = ixs->iph->protocol;
++ ixs->iph->protocol = IPPROTO_ESP;
++
++#ifdef CONFIG_IPSEC_ALG
++ /* Do all operations here:
++ * copy IV->ESP, encrypt, update ips IV
++ */
++ if (ixt_e) {
++ int ret;
++ memcpy(espp->esp_iv,
++ ixs->ipsp->ips_iv,
++ ixt_e->ixt_ivlen/8);
++ ret=ipsec_alg_esp_encrypt(ixs->ipsp,
++ idat, ilen, espp->esp_iv,
++ IPSEC_ALG_ENCRYPT);
++ memcpy(ixs->ipsp->ips_iv,
++ idat + ilen - ixt_e->ixt_ivlen/8,
++ ixt_e->ixt_ivlen/8);
++ } else
++#endif /* CONFIG_IPSEC_ALG */
++ switch(ixs->ipsp->ips_encalg) {
++#ifdef CONFIG_IPSEC_ENC_3DES
++ case ESP_3DES:
++ des_ede3_cbc_encrypt((des_cblock *)idat,
++ (des_cblock *)idat,
++ ilen,
++ ((struct des_eks *)(ixs->ipsp->ips_key_e))[0].ks,
++ ((struct des_eks *)(ixs->ipsp->ips_key_e))[1].ks,
++ ((struct des_eks *)(ixs->ipsp->ips_key_e))[2].ks,
++ (des_cblock *)iv, 1);
++ break;
++#endif /* CONFIG_IPSEC_ENC_3DES */
++ default:
++ ixs->stats->tx_errors++;
++ return IPSEC_XMIT_ESP_BADALG;
++ }
++
++#ifdef CONFIG_IPSEC_ALG
++ if (!ixt_e)
++#endif /* CONFIG_IPSEC_ALG */
++ switch(ixs->ipsp->ips_encalg) {
++#if defined(CONFIG_IPSEC_ENC_3DES)
++#ifdef CONFIG_IPSEC_ENC_3DES
++ case ESP_3DES:
++#endif /* CONFIG_IPSEC_ENC_3DES */
++ /* XXX update IV with the last 8 octets of the encryption */
++#if KLIPS_IMPAIRMENT_ESPIV_CBC_ATTACK
++ ((__u32*)(ixs->ipsp->ips_iv))[0] =
++ ((__u32 *)(idat))[(ilen >> 2) - 2];
++ ((__u32*)(ixs->ipsp->ips_iv))[1] =
++ ((__u32 *)(idat))[(ilen >> 2) - 1];
++#else /* KLIPS_IMPAIRMENT_ESPIV_CBC_ATTACK */
++ prng_bytes(&ipsec_prng, (char *)ixs->ipsp->ips_iv, EMT_ESPDES_IV_SZ);
++#endif /* KLIPS_IMPAIRMENT_ESPIV_CBC_ATTACK */
++ break;
++#endif /* defined(CONFIG_IPSEC_ENC_3DES) */
++ default:
++ ixs->stats->tx_errors++;
++ return IPSEC_XMIT_ESP_BADALG;
++ }
++
++#ifdef CONFIG_IPSEC_ALG
++ if (ixt_a) {
++ ipsec_alg_sa_esp_hash(ixs->ipsp,
++ (caddr_t)espp, len - ixs->iphlen - authlen,
++ &(dat[len - authlen]), authlen);
++
++ } else
++#endif /* CONFIG_IPSEC_ALG */
++ switch(ixs->ipsp->ips_authalg) {
++#ifdef CONFIG_IPSEC_AUTH_HMAC_MD5
++ case AH_MD5:
++ dmp("espp", (char*)espp, len - ixs->iphlen - authlen);
++ tctx.md5 = ((struct md5_ctx*)(ixs->ipsp->ips_key_a))->ictx;
++ dmp("ictx", (char*)&tctx.md5, sizeof(tctx.md5));
++ MD5Update(&tctx.md5, (caddr_t)espp, len - ixs->iphlen - authlen);
++ dmp("ictx+dat", (char*)&tctx.md5, sizeof(tctx.md5));
++ MD5Final(hash, &tctx.md5);
++ dmp("ictx hash", (char*)&hash, sizeof(hash));
++ tctx.md5 = ((struct md5_ctx*)(ixs->ipsp->ips_key_a))->octx;
++ dmp("octx", (char*)&tctx.md5, sizeof(tctx.md5));
++ MD5Update(&tctx.md5, hash, AHMD596_ALEN);
++ dmp("octx+hash", (char*)&tctx.md5, sizeof(tctx.md5));
++ MD5Final(hash, &tctx.md5);
++ dmp("octx hash", (char*)&hash, sizeof(hash));
++ memcpy(&(dat[len - authlen]), hash, authlen);
++
++ /* paranoid */
++ memset((caddr_t)&tctx.md5, 0, sizeof(tctx.md5));
++ memset((caddr_t)hash, 0, sizeof(*hash));
++ break;
++#endif /* CONFIG_IPSEC_AUTH_HMAC_MD5 */
++#ifdef CONFIG_IPSEC_AUTH_HMAC_SHA1
++ case AH_SHA:
++ tctx.sha1 = ((struct sha1_ctx*)(ixs->ipsp->ips_key_a))->ictx;
++ SHA1Update(&tctx.sha1, (caddr_t)espp, len - ixs->iphlen - authlen);
++ SHA1Final(hash, &tctx.sha1);
++ tctx.sha1 = ((struct sha1_ctx*)(ixs->ipsp->ips_key_a))->octx;
++ SHA1Update(&tctx.sha1, hash, AHSHA196_ALEN);
++ SHA1Final(hash, &tctx.sha1);
++ memcpy(&(dat[len - authlen]), hash, authlen);
++
++ /* paranoid */
++ memset((caddr_t)&tctx.sha1, 0, sizeof(tctx.sha1));
++ memset((caddr_t)hash, 0, sizeof(*hash));
++ break;
++#endif /* CONFIG_IPSEC_AUTH_HMAC_SHA1 */
++ case AH_NONE:
++ break;
++ default:
++ ixs->stats->tx_errors++;
++ return IPSEC_XMIT_AH_BADALG;
++ }
++#ifdef NET_21
++ ixs->skb->h.raw = (unsigned char*)espp;
++#endif /* NET_21 */
++ break;
++#endif /* !CONFIG_IPSEC_ESP */
++#ifdef CONFIG_IPSEC_AH
++ case IPPROTO_AH:
++ ahp = (struct ahhdr *)(dat + ixs->iphlen);
++ ahp->ah_spi = ixs->ipsp->ips_said.spi;
++ ahp->ah_rpl = htonl(++(ixs->ipsp->ips_replaywin_lastseq));
++ ahp->ah_rv = 0;
++ ahp->ah_nh = ixs->iph->protocol;
++ ahp->ah_hl = (headroom >> 2) - sizeof(__u64)/sizeof(__u32);
++ ixs->iph->protocol = IPPROTO_AH;
++ dmp("ahp", (char*)ahp, sizeof(*ahp));
++
++ ipo = *ixs->iph;
++ ipo.tos = 0;
++ ipo.frag_off = 0;
++ ipo.ttl = 0;
++ ipo.check = 0;
++ dmp("ipo", (char*)&ipo, sizeof(ipo));
++
++ switch(ixs->ipsp->ips_authalg) {
++#ifdef CONFIG_IPSEC_AUTH_HMAC_MD5
++ case AH_MD5:
++ tctx.md5 = ((struct md5_ctx*)(ixs->ipsp->ips_key_a))->ictx;
++ dmp("ictx", (char*)&tctx.md5, sizeof(tctx.md5));
++ MD5Update(&tctx.md5, (unsigned char *)&ipo, sizeof (struct iphdr));
++ dmp("ictx+ipo", (char*)&tctx.md5, sizeof(tctx.md5));
++ MD5Update(&tctx.md5, (unsigned char *)ahp, headroom - sizeof(ahp->ah_data));
++ dmp("ictx+ahp", (char*)&tctx.md5, sizeof(tctx.md5));
++ MD5Update(&tctx.md5, (unsigned char *)zeroes, AHHMAC_HASHLEN);
++ dmp("ictx+zeroes", (char*)&tctx.md5, sizeof(tctx.md5));
++ MD5Update(&tctx.md5, dat + ixs->iphlen + headroom, len - ixs->iphlen - headroom);
++ dmp("ictx+dat", (char*)&tctx.md5, sizeof(tctx.md5));
++ MD5Final(hash, &tctx.md5);
++ dmp("ictx hash", (char*)&hash, sizeof(hash));
++ tctx.md5 = ((struct md5_ctx*)(ixs->ipsp->ips_key_a))->octx;
++ dmp("octx", (char*)&tctx.md5, sizeof(tctx.md5));
++ MD5Update(&tctx.md5, hash, AHMD596_ALEN);
++ dmp("octx+hash", (char*)&tctx.md5, sizeof(tctx.md5));
++ MD5Final(hash, &tctx.md5);
++ dmp("octx hash", (char*)&hash, sizeof(hash));
++
++ memcpy(ahp->ah_data, hash, AHHMAC_HASHLEN);
++
++ /* paranoid */
++ memset((caddr_t)&tctx.md5, 0, sizeof(tctx.md5));
++ memset((caddr_t)hash, 0, sizeof(*hash));
++ break;
++#endif /* CONFIG_IPSEC_AUTH_HMAC_MD5 */
++#ifdef CONFIG_IPSEC_AUTH_HMAC_SHA1
++ case AH_SHA:
++ tctx.sha1 = ((struct sha1_ctx*)(ixs->ipsp->ips_key_a))->ictx;
++ SHA1Update(&tctx.sha1, (unsigned char *)&ipo, sizeof (struct iphdr));
++ SHA1Update(&tctx.sha1, (unsigned char *)ahp, headroom - sizeof(ahp->ah_data));
++ SHA1Update(&tctx.sha1, (unsigned char *)zeroes, AHHMAC_HASHLEN);
++ SHA1Update(&tctx.sha1, dat + ixs->iphlen + headroom, len - ixs->iphlen - headroom);
++ SHA1Final(hash, &tctx.sha1);
++ tctx.sha1 = ((struct sha1_ctx*)(ixs->ipsp->ips_key_a))->octx;
++ SHA1Update(&tctx.sha1, hash, AHSHA196_ALEN);
++ SHA1Final(hash, &tctx.sha1);
++
++ memcpy(ahp->ah_data, hash, AHHMAC_HASHLEN);
++
++ /* paranoid */
++ memset((caddr_t)&tctx.sha1, 0, sizeof(tctx.sha1));
++ memset((caddr_t)hash, 0, sizeof(*hash));
++ break;
++#endif /* CONFIG_IPSEC_AUTH_HMAC_SHA1 */
++ default:
++ ixs->stats->tx_errors++;
++ return IPSEC_XMIT_AH_BADALG;
++ }
++#ifdef NET_21
++ ixs->skb->h.raw = (unsigned char*)ahp;
++#endif /* NET_21 */
++ break;
++#endif /* CONFIG_IPSEC_AH */
++#ifdef CONFIG_IPSEC_IPIP
++ case IPPROTO_IPIP:
++ ixs->iph->version = 4;
++ switch(sysctl_ipsec_tos) {
++ case 0:
++#ifdef NET_21
++ ixs->iph->tos = ixs->skb->nh.iph->tos;
++#else /* NET_21 */
++ ixs->iph->tos = ixs->skb->ip_hdr->tos;
++#endif /* NET_21 */
++ break;
++ case 1:
++ ixs->iph->tos = 0;
++ break;
++ default:
++ break;
++ }
++#ifdef NET_21
++#ifdef NETDEV_23
++ ixs->iph->ttl = sysctl_ip_default_ttl;
++#else /* NETDEV_23 */
++ ixs->iph->ttl = ip_statistics.IpDefaultTTL;
++#endif /* NETDEV_23 */
++#else /* NET_21 */
++ ixs->iph->ttl = 64; /* ip_statistics.IpDefaultTTL; */
++#endif /* NET_21 */
++ ixs->iph->frag_off = 0;
++ ixs->iph->saddr = ((struct sockaddr_in*)(ixs->ipsp->ips_addr_s))->sin_addr.s_addr;
++ ixs->iph->daddr = ((struct sockaddr_in*)(ixs->ipsp->ips_addr_d))->sin_addr.s_addr;
++ ixs->iph->protocol = IPPROTO_IPIP;
++ ixs->iph->ihl = sizeof(struct iphdr) >> 2;
++
++ KLIPS_IP_SELECT_IDENT(ixs->iph, ixs->skb);
++
++ ixs->newdst = (__u32)ixs->iph->daddr;
++ ixs->newsrc = (__u32)ixs->iph->saddr;
++
++#ifdef NET_21
++ ixs->skb->h.ipiph = ixs->skb->nh.iph;
++#endif /* NET_21 */
++ break;
++#endif /* !CONFIG_IPSEC_IPIP */
++#ifdef CONFIG_IPSEC_IPCOMP
++ case IPPROTO_COMP:
++ {
++ unsigned int flags = 0;
++#ifdef CONFIG_IPSEC_DEBUG
++ unsigned int old_tot_len = ntohs(ixs->iph->tot_len);
++#endif /* CONFIG_IPSEC_DEBUG */
++ ixs->ipsp->ips_comp_ratio_dbytes += ntohs(ixs->iph->tot_len);
++
++ ixs->skb = skb_compress(ixs->skb, ixs->ipsp, &flags);
++
++#ifdef NET_21
++ ixs->iph = ixs->skb->nh.iph;
++#else /* NET_21 */
++ ixs->iph = ixs->skb->ip_hdr;
++#endif /* NET_21 */
++
++ ixs->ipsp->ips_comp_ratio_cbytes += ntohs(ixs->iph->tot_len);
++
++#ifdef CONFIG_IPSEC_DEBUG
++ if (debug_tunnel & DB_TN_CROUT)
++ {
++ if (old_tot_len > ntohs(ixs->iph->tot_len))
++ KLIPS_PRINT(debug_tunnel & DB_TN_CROUT,
++ "klips_debug:ipsec_xmit_encap_once: "
++ "packet shrunk from %d to %d bytes after compression, cpi=%04x (should be from spi=%08x, spi&0xffff=%04x.\n",
++ old_tot_len, ntohs(ixs->iph->tot_len),
++ ntohs(((struct ipcomphdr*)(((char*)ixs->iph) + ((ixs->iph->ihl) << 2)))->ipcomp_cpi),
++ ntohl(ixs->ipsp->ips_said.spi),
++ (__u16)(ntohl(ixs->ipsp->ips_said.spi) & 0x0000ffff));
++ else
++ KLIPS_PRINT(debug_tunnel & DB_TN_CROUT,
++ "klips_debug:ipsec_xmit_encap_once: "
++ "packet did not compress (flags = %d).\n",
++ flags);
++ }
++#endif /* CONFIG_IPSEC_DEBUG */
++ }
++ break;
++#endif /* CONFIG_IPSEC_IPCOMP */
++ default:
++ ixs->stats->tx_errors++;
++ return IPSEC_XMIT_BADPROTO;
++ }
++
++#ifdef NET_21
++ ixs->skb->nh.raw = ixs->skb->data;
++#else /* NET_21 */
++ ixs->skb->ip_hdr = ixs->skb->h.iph = (struct iphdr *) ixs->skb->data;
++#endif /* NET_21 */
++ ixs->iph->check = 0;
++ ixs->iph->check = ip_fast_csum((unsigned char *)ixs->iph, ixs->iph->ihl);
++
++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
++ "klips_debug:ipsec_xmit_encap_once: "
++ "after <%s%s%s>, SA:%s:\n",
++ IPS_XFORM_NAME(ixs->ipsp),
++ ixs->sa_len ? ixs->sa_txt : " (error)");
++ KLIPS_IP_PRINT(debug_tunnel & DB_TN_XMIT, ixs->iph);
++
++ ixs->ipsp->ips_life.ipl_bytes.ipl_count += len;
++ ixs->ipsp->ips_life.ipl_bytes.ipl_last = len;
++
++ if(!ixs->ipsp->ips_life.ipl_usetime.ipl_count) {
++ ixs->ipsp->ips_life.ipl_usetime.ipl_count = jiffies / HZ;
++ }
++ ixs->ipsp->ips_life.ipl_usetime.ipl_last = jiffies / HZ;
++ ixs->ipsp->ips_life.ipl_packets.ipl_count++;
++
++ ixs->ipsp = ixs->ipsp->ips_onext;
++
++ return IPSEC_XMIT_OK;
++}
++
++/*
++ * If the IP packet (iph) is a carrying TCP/UDP, then set the encaps
++ * source and destination ports to those from the TCP/UDP header.
++ */
++void ipsec_extract_ports(struct iphdr * iph, struct sockaddr_encap * er)
++{
++ struct udphdr *udp;
++
++ switch (iph->protocol) {
++ case IPPROTO_UDP:
++ case IPPROTO_TCP:
++ /*
++ * The ports are at the same offsets in a TCP and UDP
++ * header so hack it ...
++ */
++ udp = (struct udphdr*)(((char*)iph)+(iph->ihl<<2));
++ er->sen_sport = udp->source;
++ er->sen_dport = udp->dest;
++ break;
++ default:
++ er->sen_sport = 0;
++ er->sen_dport = 0;
++ break;
++ }
++}
++
++/*
++ * A TRAP eroute is installed and we want to replace it with a HOLD
++ * eroute.
++ */
++static int create_hold_eroute(struct eroute *origtrap,
++ struct sk_buff * skb, struct iphdr * iph,
++ uint32_t eroute_pid)
++{
++ struct eroute hold_eroute;
++ ip_said hold_said;
++ struct sk_buff *first, *last;
++ int error;
++
++ first = last = NULL;
++ memset((caddr_t)&hold_eroute, 0, sizeof(hold_eroute));
++ memset((caddr_t)&hold_said, 0, sizeof(hold_said));
++
++ hold_said.proto = IPPROTO_INT;
++ hold_said.spi = htonl(SPI_HOLD);
++ hold_said.dst.u.v4.sin_addr.s_addr = INADDR_ANY;
++
++ hold_eroute.er_eaddr.sen_len = sizeof(struct sockaddr_encap);
++ hold_eroute.er_emask.sen_len = sizeof(struct sockaddr_encap);
++ hold_eroute.er_eaddr.sen_family = AF_ENCAP;
++ hold_eroute.er_emask.sen_family = AF_ENCAP;
++ hold_eroute.er_eaddr.sen_type = SENT_IP4;
++ hold_eroute.er_emask.sen_type = 255;
++
++ hold_eroute.er_eaddr.sen_ip_src.s_addr = iph->saddr;
++ hold_eroute.er_eaddr.sen_ip_dst.s_addr = iph->daddr;
++ hold_eroute.er_emask.sen_ip_src.s_addr = INADDR_BROADCAST;
++ hold_eroute.er_emask.sen_ip_dst.s_addr = INADDR_BROADCAST;
++ hold_eroute.er_emask.sen_sport = 0;
++ hold_eroute.er_emask.sen_dport = 0;
++ hold_eroute.er_pid = eroute_pid;
++ hold_eroute.er_count = 0;
++ hold_eroute.er_lasttime = jiffies/HZ;
++
++ /*
++ * if it wasn't captured by a wildcard, then don't record it as
++ * a wildcard.
++ */
++ if(origtrap->er_eaddr.sen_proto != 0) {
++ hold_eroute.er_eaddr.sen_proto = iph->protocol;
++
++ if((iph->protocol == IPPROTO_TCP ||
++ iph->protocol == IPPROTO_UDP) &&
++ (origtrap->er_eaddr.sen_sport != 0 ||
++ origtrap->er_eaddr.sen_dport != 0)) {
++
++ if(origtrap->er_eaddr.sen_sport != 0)
++ hold_eroute.er_emask.sen_sport = ~0;
++
++ if(origtrap->er_eaddr.sen_dport != 0)
++ hold_eroute.er_emask.sen_dport = ~0;
++
++ ipsec_extract_ports(iph, &hold_eroute.er_eaddr);
++ }
++ }
++
++#ifdef CONFIG_IPSEC_DEBUG
++ if (debug_pfkey) {
++ char buf1[64], buf2[64];
++ subnettoa(hold_eroute.er_eaddr.sen_ip_src,
++ hold_eroute.er_emask.sen_ip_src, 0, buf1, sizeof(buf1));
++ subnettoa(hold_eroute.er_eaddr.sen_ip_dst,
++ hold_eroute.er_emask.sen_ip_dst, 0, buf2, sizeof(buf2));
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:ipsec_tunnel_start_xmit: "
++ "calling breakeroute and makeroute for %s:%d->%s:%d %d HOLD eroute.\n",
++ buf1, ntohs(hold_eroute.er_eaddr.sen_sport),
++ buf2, ntohs(hold_eroute.er_eaddr.sen_dport),
++ hold_eroute.er_eaddr.sen_proto);
++ }
++#endif /* CONFIG_IPSEC_DEBUG */
++
++ if (ipsec_breakroute(&(hold_eroute.er_eaddr), &(hold_eroute.er_emask),
++ &first, &last)) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:ipsec_tunnel_start_xmit: "
++ "HOLD breakeroute found nothing.\n");
++ } else {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:ipsec_tunnel_start_xmit: "
++ "HOLD breakroute deleted %u.%u.%u.%u:%u -> %u.%u.%u.%u:%u %u\n",
++ NIPQUAD(hold_eroute.er_eaddr.sen_ip_src),
++ ntohs(hold_eroute.er_eaddr.sen_sport),
++ NIPQUAD(hold_eroute.er_eaddr.sen_ip_dst),
++ ntohs(hold_eroute.er_eaddr.sen_dport),
++ hold_eroute.er_eaddr.sen_proto);
++ }
++ if (first != NULL)
++ kfree_skb(first);
++ if (last != NULL)
++ kfree_skb(last);
++
++ error = ipsec_makeroute(&(hold_eroute.er_eaddr),
++ &(hold_eroute.er_emask),
++ hold_said, eroute_pid, skb, NULL, NULL);
++ if (error) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:ipsec_tunnel_start_xmit: "
++ "HOLD makeroute returned %d, failed.\n", error);
++ } else {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:ipsec_tunnel_start_xmit: "
++ "HOLD makeroute call successful.\n");
++ }
++ return (error == 0);
++}
++
++enum ipsec_xmit_value
++ipsec_xmit_encap_bundle(struct ipsec_xmit_state *ixs)
++{
++#ifdef CONFIG_IPSEC_ALG
++ struct ipsec_alg_enc *ixt_e = NULL;
++ struct ipsec_alg_auth *ixt_a = NULL;
++ int blocksize = 8;
++#endif /* CONFIG_IPSEC_ALG */
++ enum ipsec_xmit_value bundle_stat = IPSEC_XMIT_OK;
++
++ ixs->newdst = ixs->orgdst = ixs->iph->daddr;
++ ixs->newsrc = ixs->orgsrc = ixs->iph->saddr;
++ ixs->orgedst = ixs->outgoing_said.dst.u.v4.sin_addr.s_addr;
++ ixs->iphlen = ixs->iph->ihl << 2;
++ ixs->pyldsz = ntohs(ixs->iph->tot_len) - ixs->iphlen;
++ ixs->max_headroom = ixs->max_tailroom = 0;
++
++ if (ixs->outgoing_said.proto == IPPROTO_INT) {
++ switch (ntohl(ixs->outgoing_said.spi)) {
++ case SPI_DROP:
++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
++ "klips_debug:ipsec_xmit_encap_bundle: "
++ "shunt SA of DROP or no eroute: dropping.\n");
++ ixs->stats->tx_dropped++;
++ break;
++
++ case SPI_REJECT:
++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
++ "klips_debug:ipsec_xmit_encap_bundle: "
++ "shunt SA of REJECT: notifying and dropping.\n");
++ ICMP_SEND(ixs->skb,
++ ICMP_DEST_UNREACH,
++ ICMP_PKT_FILTERED,
++ 0,
++ ixs->physdev);
++ ixs->stats->tx_dropped++;
++ break;
++
++ case SPI_PASS:
++#ifdef NET_21
++ ixs->pass = 1;
++#endif /* NET_21 */
++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
++ "klips_debug:ipsec_xmit_encap_bundle: "
++ "PASS: calling dev_queue_xmit\n");
++ return IPSEC_XMIT_PASS;
++ goto cleanup;
++
++ case SPI_HOLD:
++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
++ "klips_debug:ipsec_xmit_encap_bundle: "
++ "shunt SA of HOLD: this does not make sense here, dropping.\n");
++ ixs->stats->tx_dropped++;
++ break;
++
++ case SPI_TRAP:
++ case SPI_TRAPSUBNET:
++ {
++ struct sockaddr_in src, dst;
++#ifdef CONFIG_IPSEC_DEBUG
++ char bufsrc[ADDRTOA_BUF], bufdst[ADDRTOA_BUF];
++#endif /* CONFIG_IPSEC_DEBUG */
++
++ /* Signal all listening KMds with a PF_KEY ACQUIRE */
++
++ src.sin_family = AF_INET;
++ dst.sin_family = AF_INET;
++ src.sin_addr.s_addr = ixs->iph->saddr;
++ dst.sin_addr.s_addr = ixs->iph->daddr;
++
++ ixs->ips.ips_transport_protocol = 0;
++ src.sin_port = 0;
++ dst.sin_port = 0;
++ {
++ int i;
++ for(i = 0;
++ i < sizeof(struct sockaddr_in)
++ - offsetof(struct sockaddr_in, sin_zero);
++ i++) {
++ src.sin_zero[i] = 0;
++ dst.sin_zero[i] = 0;
++ }
++ }
++
++ if(ixs->eroute->er_eaddr.sen_proto != 0) {
++ ixs->ips.ips_transport_protocol = ixs->iph->protocol;
++
++ if(ixs->eroute->er_eaddr.sen_sport != 0) {
++ src.sin_port =
++ (ixs->iph->protocol == IPPROTO_UDP
++ ? ((struct udphdr*) (((caddr_t)ixs->iph) + (ixs->iph->ihl << 2)))->source
++ : (ixs->iph->protocol == IPPROTO_TCP
++ ? ((struct tcphdr*)((caddr_t)ixs->iph + (ixs->iph->ihl << 2)))->source
++ : 0));
++ }
++ if(ixs->eroute->er_eaddr.sen_dport != 0) {
++ dst.sin_port =
++ (ixs->iph->protocol == IPPROTO_UDP
++ ? ((struct udphdr*) (((caddr_t)ixs->iph) + (ixs->iph->ihl << 2)))->dest
++ : (ixs->iph->protocol == IPPROTO_TCP
++ ? ((struct tcphdr*)((caddr_t)ixs->iph + (ixs->iph->ihl << 2)))->dest
++ : 0));
++ }
++ }
++
++ ixs->ips.ips_addr_s = (struct sockaddr*)(&src);
++ ixs->ips.ips_addr_d = (struct sockaddr*)(&dst);
++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
++ "klips_debug:ipsec_xmit_encap_bundle: "
++ "SADB_ACQUIRE sent with src=%s:%d, dst=%s:%d, proto=%d.\n",
++ addrtoa(((struct sockaddr_in*)(ixs->ips.ips_addr_s))->sin_addr, 0, bufsrc, sizeof(bufsrc)) <= ADDRTOA_BUF ? bufsrc : "BAD_ADDR",
++ ntohs(((struct sockaddr_in*)(ixs->ips.ips_addr_s))->sin_port),
++ addrtoa(((struct sockaddr_in*)(ixs->ips.ips_addr_d))->sin_addr, 0, bufdst, sizeof(bufdst)) <= ADDRTOA_BUF ? bufdst : "BAD_ADDR",
++ ntohs(((struct sockaddr_in*)(ixs->ips.ips_addr_d))->sin_port),
++ ixs->ips.ips_said.proto);
++
++ /* increment count of total traps needed */
++ ipsec_xmit_trap_count++;
++
++ if (pfkey_acquire(&ixs->ips) == 0) {
++
++ /* note that we succeeded */
++ ipsec_xmit_trap_sendcount++;
++
++ if (ixs->outgoing_said.spi==htonl(SPI_TRAPSUBNET)) {
++ /*
++ * The spinlock is to prevent any other
++ * process from accessing or deleting
++ * the eroute while we are using and
++ * updating it.
++ */
++ spin_lock(&eroute_lock);
++ ixs->eroute = ipsec_findroute(&ixs->matcher);
++ if(ixs->eroute) {
++ ixs->eroute->er_said.spi = htonl(SPI_HOLD);
++ ixs->eroute->er_first = ixs->skb;
++ ixs->skb = NULL;
++ }
++ spin_unlock(&eroute_lock);
++ } else if (create_hold_eroute(ixs->eroute,
++ ixs->skb,
++ ixs->iph,
++ ixs->eroute_pid)) {
++ ixs->skb = NULL;
++ }
++ /* whether or not the above succeeded, we continue */
++
++ }
++ ixs->stats->tx_dropped++;
++ }
++ default:
++ /* XXX what do we do with an unknown shunt spi? */
++ break;
++ } /* switch (ntohl(ixs->outgoing_said.spi)) */
++ return IPSEC_XMIT_STOLEN;
++ } /* if (ixs->outgoing_said.proto == IPPROTO_INT) */
++
++ /*
++ The spinlock is to prevent any other process from
++ accessing or deleting the ipsec_sa hash table or any of the
++ ipsec_sa s while we are using and updating them.
++
++ This is not optimal, but was relatively straightforward
++ at the time. A better way to do it has been planned for
++ more than a year, to lock the hash table and put reference
++ counts on each ipsec_sa instead. This is not likely to happen
++ in KLIPS1 unless a volunteer contributes it, but will be
++ designed into KLIPS2.
++ */
++ spin_lock(&tdb_lock);
++
++ ixs->ipsp = ipsec_sa_getbyid(&ixs->outgoing_said);
++ ixs->sa_len = satot(&ixs->outgoing_said, 0, ixs->sa_txt, sizeof(ixs->sa_txt));
++
++ if (ixs->ipsp == NULL) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
++ "klips_debug:ipsec_xmit_encap_bundle: "
++ "no ipsec_sa for SA%s: outgoing packet with no SA, dropped.\n",
++ ixs->sa_len ? ixs->sa_txt : " (error)");
++ ixs->stats->tx_dropped++;
++ bundle_stat = IPSEC_XMIT_SAIDNOTFOUND;
++ goto cleanup;
++ }
++
++ ipsec_sa_put(ixs->ipsp); /* incomplete */
++
++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
++ "klips_debug:ipsec_xmit_encap_bundle: "
++ "found ipsec_sa -- SA:<%s%s%s> %s\n",
++ IPS_XFORM_NAME(ixs->ipsp),
++ ixs->sa_len ? ixs->sa_txt : " (error)");
++
++ /*
++ * How much headroom do we need to be able to apply
++ * all the grouped transforms?
++ */
++ ixs->ipsq = ixs->ipsp; /* save the head of the ipsec_sa chain */
++ while (ixs->ipsp) {
++ ixs->sa_len = satot(&ixs->ipsp->ips_said, 0, ixs->sa_txt, sizeof(ixs->sa_txt));
++ if(ixs->sa_len == 0) {
++ strcpy(ixs->sa_txt, "(error)");
++ }
++
++ /* If it is in larval state, drop the packet, we cannot process yet. */
++ if(ixs->ipsp->ips_state == SADB_SASTATE_LARVAL) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
++ "klips_debug:ipsec_xmit_encap_bundle: "
++ "ipsec_sa in larval state for SA:<%s%s%s> %s, cannot be used yet, dropping packet.\n",
++ IPS_XFORM_NAME(ixs->ipsp),
++ ixs->sa_len ? ixs->sa_txt : " (error)");
++ ixs->stats->tx_errors++;
++ bundle_stat = IPSEC_XMIT_SAIDNOTLIVE;
++ goto cleanup;
++ }
++
++ if(ixs->ipsp->ips_state == SADB_SASTATE_DEAD) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
++ "klips_debug:ipsec_xmit_encap_bundle: "
++ "ipsec_sa in dead state for SA:<%s%s%s> %s, can no longer be used, dropping packet.\n",
++ IPS_XFORM_NAME(ixs->ipsp),
++ ixs->sa_len ? ixs->sa_txt : " (error)");
++ ixs->stats->tx_errors++;
++ bundle_stat = IPSEC_XMIT_SAIDNOTLIVE;
++ goto cleanup;
++ }
++
++ /* If the replay window counter == -1, expire SA, it will roll */
++ if(ixs->ipsp->ips_replaywin && ixs->ipsp->ips_replaywin_lastseq == -1) {
++ pfkey_expire(ixs->ipsp, 1);
++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
++ "klips_debug:ipsec_xmit_encap_bundle: "
++ "replay window counter rolled for SA:<%s%s%s> %s, packet dropped, expiring SA.\n",
++ IPS_XFORM_NAME(ixs->ipsp),
++ ixs->sa_len ? ixs->sa_txt : " (error)");
++ ipsec_sa_delchain(ixs->ipsp);
++ ixs->stats->tx_errors++;
++ bundle_stat = IPSEC_XMIT_REPLAYROLLED;
++ goto cleanup;
++ }
++
++ /*
++ * if this is the first time we are using this SA, mark start time,
++ * and offset hard/soft counters by "now" for later checking.
++ */
++#if 0
++ if(ixs->ipsp->ips_life.ipl_usetime.count == 0) {
++ ixs->ipsp->ips_life.ipl_usetime.count = jiffies;
++ ixs->ipsp->ips_life.ipl_usetime.hard += jiffies;
++ ixs->ipsp->ips_life.ipl_usetime.soft += jiffies;
++ }
++#endif
++
++
++ if(ipsec_lifetime_check(&ixs->ipsp->ips_life.ipl_bytes, "bytes", ixs->sa_txt,
++ ipsec_life_countbased, ipsec_outgoing, ixs->ipsp) == ipsec_life_harddied ||
++ ipsec_lifetime_check(&ixs->ipsp->ips_life.ipl_addtime, "addtime",ixs->sa_txt,
++ ipsec_life_timebased, ipsec_outgoing, ixs->ipsp) == ipsec_life_harddied ||
++ ipsec_lifetime_check(&ixs->ipsp->ips_life.ipl_usetime, "usetime",ixs->sa_txt,
++ ipsec_life_timebased, ipsec_outgoing, ixs->ipsp) == ipsec_life_harddied ||
++ ipsec_lifetime_check(&ixs->ipsp->ips_life.ipl_packets, "packets",ixs->sa_txt,
++ ipsec_life_countbased, ipsec_outgoing, ixs->ipsp) == ipsec_life_harddied) {
++
++ ipsec_sa_delchain(ixs->ipsp);
++ ixs->stats->tx_errors++;
++ bundle_stat = IPSEC_XMIT_LIFETIMEFAILED;
++ goto cleanup;
++ }
++
++
++ ixs->headroom = ixs->tailroom = 0;
++ KLIPS_PRINT(debug_tunnel & DB_TN_CROUT,
++ "klips_debug:ipsec_xmit_encap_bundle: "
++ "calling room for <%s%s%s>, SA:%s\n",
++ IPS_XFORM_NAME(ixs->ipsp),
++ ixs->sa_len ? ixs->sa_txt : " (error)");
++ switch(ixs->ipsp->ips_said.proto) {
++#ifdef CONFIG_IPSEC_AH
++ case IPPROTO_AH:
++ ixs->headroom += sizeof(struct ahhdr);
++ break;
++#endif /* CONFIG_IPSEC_AH */
++#ifdef CONFIG_IPSEC_ESP
++ case IPPROTO_ESP:
++#ifdef CONFIG_IPSEC_ALG
++ if ((ixt_e=ixs->ipsp->ips_alg_enc)) {
++ blocksize = ixt_e->ixt_blocksize;
++ ixs->headroom += ESP_HEADER_LEN + ixt_e->ixt_ivlen/8;
++ } else
++#endif /* CONFIG_IPSEC_ALG */
++ switch(ixs->ipsp->ips_encalg) {
++#ifdef CONFIG_IPSEC_ENC_3DES
++ case ESP_3DES:
++ ixs->headroom += sizeof(struct esphdr);
++ break;
++#endif /* CONFIG_IPSEC_ENC_3DES */
++ default:
++ ixs->stats->tx_errors++;
++ bundle_stat = IPSEC_XMIT_ESP_BADALG;
++ goto cleanup;
++ }
++#ifdef CONFIG_IPSEC_ALG
++ if ((ixt_a=ixs->ipsp->ips_alg_auth)) {
++ ixs->tailroom += AHHMAC_HASHLEN;
++ } else
++#endif /* CONFIG_IPSEC_ALG */
++ switch(ixs->ipsp->ips_authalg) {
++#ifdef CONFIG_IPSEC_AUTH_HMAC_MD5
++ case AH_MD5:
++ ixs->tailroom += AHHMAC_HASHLEN;
++ break;
++#endif /* CONFIG_IPSEC_AUTH_HMAC_MD5 */
++#ifdef CONFIG_IPSEC_AUTH_HMAC_SHA1
++ case AH_SHA:
++ ixs->tailroom += AHHMAC_HASHLEN;
++ break;
++#endif /* CONFIG_IPSEC_AUTH_HMAC_SHA1 */
++ case AH_NONE:
++ break;
++ default:
++ ixs->stats->tx_errors++;
++ bundle_stat = IPSEC_XMIT_AH_BADALG;
++ goto cleanup;
++ }
++#ifdef CONFIG_IPSEC_ALG
++ ixs->tailroom += blocksize != 1 ?
++ ((blocksize - ((ixs->pyldsz + 2) % blocksize)) % blocksize) + 2 :
++ ((4 - ((ixs->pyldsz + 2) % 4)) % 4) + 2;
++#else
++ ixs->tailroom += ((8 - ((ixs->pyldsz + 2 * sizeof(unsigned char)) % 8)) % 8) + 2;
++#endif /* CONFIG_IPSEC_ALG */
++#ifdef CONFIG_IPSEC_NAT_TRAVERSAL
++ if ((ixs->ipsp->ips_natt_type) && (!ixs->natt_type)) {
++ ixs->natt_type = ixs->ipsp->ips_natt_type;
++ ixs->natt_sport = ixs->ipsp->ips_natt_sport;
++ ixs->natt_dport = ixs->ipsp->ips_natt_dport;
++ switch (ixs->natt_type) {
++ case ESPINUDP_WITH_NON_IKE:
++ ixs->natt_head = sizeof(struct udphdr)+(2*sizeof(__u32));
++ break;
++
++ case ESPINUDP_WITH_NON_ESP:
++ ixs->natt_head = sizeof(struct udphdr);
++ break;
++
++ default:
++ KLIPS_PRINT(debug_tunnel & DB_TN_CROUT
++ , "klips_xmit: invalid nat-t type %d"
++ , ixs->natt_type);
++ bundle_stat = IPSEC_XMIT_ESPUDP_BADTYPE;
++ goto cleanup;
++
++ break;
++ }
++ ixs->tailroom += ixs->natt_head;
++ }
++#endif
++ break;
++#endif /* !CONFIG_IPSEC_ESP */
++#ifdef CONFIG_IPSEC_IPIP
++ case IPPROTO_IPIP:
++ ixs->headroom += sizeof(struct iphdr);
++ break;
++#endif /* !CONFIG_IPSEC_IPIP */
++ case IPPROTO_COMP:
++#ifdef CONFIG_IPSEC_IPCOMP
++ /*
++ We can't predict how much the packet will
++ shrink without doing the actual compression.
++ We could do it here, if we were the first
++ encapsulation in the chain. That might save
++ us a skb_copy_expand, since we might fit
++ into the existing skb then. However, this
++ would be a bit unclean (and this hack has
++ bit us once), so we better not do it. After
++ all, the skb_copy_expand is cheap in
++ comparison to the actual compression.
++ At least we know the packet will not grow.
++ */
++ break;
++#endif /* CONFIG_IPSEC_IPCOMP */
++ default:
++ ixs->stats->tx_errors++;
++ bundle_stat = IPSEC_XMIT_BADPROTO;
++ goto cleanup;
++ }
++ ixs->ipsp = ixs->ipsp->ips_onext;
++ KLIPS_PRINT(debug_tunnel & DB_TN_CROUT,
++ "klips_debug:ipsec_xmit_encap_bundle: "
++ "Required head,tailroom: %d,%d\n",
++ ixs->headroom, ixs->tailroom);
++ ixs->max_headroom += ixs->headroom;
++ ixs->max_tailroom += ixs->tailroom;
++ ixs->pyldsz += (ixs->headroom + ixs->tailroom);
++ }
++ ixs->ipsp = ixs->ipsq; /* restore the head of the ipsec_sa chain */
++
++ KLIPS_PRINT(debug_tunnel & DB_TN_CROUT,
++ "klips_debug:ipsec_xmit_encap_bundle: "
++ "existing head,tailroom: %d,%d before applying xforms with head,tailroom: %d,%d .\n",
++ skb_headroom(ixs->skb), skb_tailroom(ixs->skb),
++ ixs->max_headroom, ixs->max_tailroom);
++
++ ixs->tot_headroom += ixs->max_headroom;
++ ixs->tot_tailroom += ixs->max_tailroom;
++
++ ixs->mtudiff = ixs->prv->mtu + ixs->tot_headroom + ixs->tot_tailroom - ixs->physmtu;
++
++ KLIPS_PRINT(debug_tunnel & DB_TN_CROUT,
++ "klips_debug:ipsec_xmit_encap_bundle: "
++ "mtu:%d physmtu:%d tothr:%d tottr:%d mtudiff:%d ippkttotlen:%d\n",
++ ixs->prv->mtu, ixs->physmtu,
++ ixs->tot_headroom, ixs->tot_tailroom, ixs->mtudiff, ntohs(ixs->iph->tot_len));
++ if(ixs->mtudiff > 0) {
++ int newmtu = ixs->physmtu - (ixs->tot_headroom + ((ixs->tot_tailroom + 2) & ~7) + 5);
++
++ KLIPS_PRINT(debug_tunnel & DB_TN_CROUT,
++ "klips_info:ipsec_xmit_encap_bundle: "
++ "dev %s mtu of %d decreased by %d to %d\n",
++ ixs->dev->name,
++ ixs->prv->mtu,
++ ixs->prv->mtu - newmtu,
++ newmtu);
++ ixs->prv->mtu = newmtu;
++#ifdef NET_21
++#if 0
++ ixs->skb->dst->pmtu = ixs->prv->mtu; /* RGB */
++#endif /* 0 */
++#else /* NET_21 */
++#if 0
++ ixs->dev->mtu = ixs->prv->mtu; /* RGB */
++#endif /* 0 */
++#endif /* NET_21 */
++ }
++
++ /*
++ If the sender is doing PMTU discovery, and the
++ packet doesn't fit within ixs->prv->mtu, notify him
++ (unless it was an ICMP packet, or it was not the
++ zero-offset packet) and send it anyways.
++
++ Note: buggy firewall configuration may prevent the
++ ICMP packet from getting back.
++ */
++ if(sysctl_ipsec_icmp
++ && ixs->prv->mtu < ntohs(ixs->iph->tot_len)
++ && (ixs->iph->frag_off & __constant_htons(IP_DF)) ) {
++ int notify = ixs->iph->protocol != IPPROTO_ICMP
++ && (ixs->iph->frag_off & __constant_htons(IP_OFFSET)) == 0;
++
++#ifdef IPSEC_obey_DF
++ KLIPS_PRINT(debug_tunnel & DB_TN_CROUT,
++ "klips_debug:ipsec_xmit_encap_bundle: "
++ "fragmentation needed and DF set; %sdropping packet\n",
++ notify ? "sending ICMP and " : "");
++ if (notify)
++ ICMP_SEND(ixs->skb,
++ ICMP_DEST_UNREACH,
++ ICMP_FRAG_NEEDED,
++ ixs->prv->mtu,
++ ixs->physdev);
++ ixs->stats->tx_errors++;
++ bundle_stat = IPSEC_XMIT_CANNOTFRAG;
++ goto cleanup;
++#else /* IPSEC_obey_DF */
++ KLIPS_PRINT(debug_tunnel & DB_TN_CROUT,
++ "klips_debug:ipsec_xmit_encap_bundle: "
++ "fragmentation needed and DF set; %spassing packet\n",
++ notify ? "sending ICMP and " : "");
++ if (notify)
++ ICMP_SEND(ixs->skb,
++ ICMP_DEST_UNREACH,
++ ICMP_FRAG_NEEDED,
++ ixs->prv->mtu,
++ ixs->physdev);
++#endif /* IPSEC_obey_DF */
++ }
++
++#ifdef MSS_HACK
++ /*
++ * If this is a transport mode TCP packet with
++ * SYN set, determine an effective MSS based on
++ * AH/ESP overheads determined above.
++ */
++ if (ixs->iph->protocol == IPPROTO_TCP
++ && ixs->outgoing_said.proto != IPPROTO_IPIP) {
++ struct tcphdr *tcph = ixs->skb->h.th;
++ if (tcph->syn && !tcph->ack) {
++ if(!ipsec_adjust_mss(ixs->skb, tcph, ixs->prv->mtu)) {
++ printk(KERN_WARNING
++ "klips_warning:ipsec_xmit_encap_bundle: "
++ "ipsec_adjust_mss() failed\n");
++ ixs->stats->tx_errors++;
++ bundle_stat = IPSEC_XMIT_MSSERR;
++ goto cleanup;
++ }
++ }
++ }
++#endif /* MSS_HACK */
++
++#ifdef CONFIG_IPSEC_NAT_TRAVERSAL
++ if ((ixs->natt_type) && (ixs->outgoing_said.proto != IPPROTO_IPIP)) {
++ /**
++ * NAT-Traversal and Transport Mode:
++ * we need to correct TCP/UDP checksum
++ *
++ * If we've got NAT-OA, we can fix checksum without recalculation.
++ * If we don't we can zero udp checksum.
++ */
++ __u32 natt_oa = ixs->ipsp->ips_natt_oa ?
++ ((struct sockaddr_in*)(ixs->ipsp->ips_natt_oa))->sin_addr.s_addr : 0;
++ __u16 pkt_len = ixs->skb->tail - (unsigned char *)ixs->iph;
++ __u16 data_len = pkt_len - (ixs->iph->ihl << 2);
++ switch (ixs->iph->protocol) {
++ case IPPROTO_TCP:
++ if (data_len >= sizeof(struct tcphdr)) {
++ struct tcphdr *tcp = (struct tcphdr *)((__u32 *)ixs->iph+ixs->iph->ihl);
++ if (natt_oa) {
++ __u32 buff[2] = { ~ixs->iph->daddr, natt_oa };
++ KLIPS_PRINT(debug_tunnel,
++ "klips_debug:ipsec_tunnel_start_xmit: "
++ "NAT-T & TRANSPORT: "
++ "fix TCP checksum using NAT-OA\n");
++ tcp->check = csum_fold(
++ csum_partial((unsigned char *)buff, sizeof(buff),
++ tcp->check^0xffff));
++ }
++ else {
++ KLIPS_PRINT(debug_tunnel,
++ "klips_debug:ipsec_tunnel_start_xmit: "
++ "NAT-T & TRANSPORT: do not recalc TCP checksum\n");
++ }
++ }
++ else {
++ KLIPS_PRINT(debug_tunnel,
++ "klips_debug:ipsec_tunnel_start_xmit: "
++ "NAT-T & TRANSPORT: can't fix TCP checksum\n");
++ }
++ break;
++ case IPPROTO_UDP:
++ if (data_len >= sizeof(struct udphdr)) {
++ struct udphdr *udp = (struct udphdr *)((__u32 *)ixs->iph+ixs->iph->ihl);
++ if (udp->check == 0) {
++ KLIPS_PRINT(debug_tunnel,
++ "klips_debug:ipsec_tunnel_start_xmit: "
++ "NAT-T & TRANSPORT: UDP checksum already 0\n");
++ }
++ else if (natt_oa) {
++ __u32 buff[2] = { ~ixs->iph->daddr, natt_oa };
++ KLIPS_PRINT(debug_tunnel,
++ "klips_debug:ipsec_tunnel_start_xmit: "
++ "NAT-T & TRANSPORT: "
++ "fix UDP checksum using NAT-OA\n");
++ udp->check = csum_fold(
++ csum_partial((unsigned char *)buff, sizeof(buff),
++ udp->check^0xffff));
++ }
++ else {
++ KLIPS_PRINT(debug_tunnel,
++ "klips_debug:ipsec_tunnel_start_xmit: "
++ "NAT-T & TRANSPORT: zero UDP checksum\n");
++ udp->check = 0;
++ }
++ }
++ else {
++ KLIPS_PRINT(debug_tunnel,
++ "klips_debug:ipsec_tunnel_start_xmit: "
++ "NAT-T & TRANSPORT: can't fix UDP checksum\n");
++ }
++ break;
++ default:
++ KLIPS_PRINT(debug_tunnel,
++ "klips_debug:ipsec_tunnel_start_xmit: "
++ "NAT-T & TRANSPORT: non TCP/UDP packet -- do nothing\n");
++ break;
++ }
++ }
++#endif /* CONFIG_IPSEC_NAT_TRAVERSAL */
++
++ if(!ixs->hard_header_stripped) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
++ "klips_debug:ipsec_xmit_encap_bundle: "
++ "allocating %d bytes for hardheader.\n",
++ ixs->hard_header_len);
++ if((ixs->saved_header = kmalloc(ixs->hard_header_len, GFP_ATOMIC)) == NULL) {
++ printk(KERN_WARNING "klips_debug:ipsec_xmit_encap_bundle: "
++ "Failed, tried to allocate %d bytes for temp hard_header.\n",
++ ixs->hard_header_len);
++ ixs->stats->tx_errors++;
++ bundle_stat = IPSEC_XMIT_ERRMEMALLOC;
++ goto cleanup;
++ }
++ {
++ int i;
++ for (i = 0; i < ixs->hard_header_len; i++) {
++ ixs->saved_header[i] = ixs->skb->data[i];
++ }
++ }
++ if(ixs->skb->len < ixs->hard_header_len) {
++ printk(KERN_WARNING "klips_error:ipsec_xmit_encap_bundle: "
++ "tried to skb_pull hhlen=%d, %d available. This should never happen, please report.\n",
++ ixs->hard_header_len, (int)(ixs->skb->len));
++ ixs->stats->tx_errors++;
++ bundle_stat = IPSEC_XMIT_ESP_PUSHPULLERR;
++ goto cleanup;
++ }
++ skb_pull(ixs->skb, ixs->hard_header_len);
++ ixs->hard_header_stripped = 1;
++
++/* ixs->iph = (struct iphdr *) (ixs->skb->data); */
++ KLIPS_PRINT(debug_tunnel & DB_TN_CROUT,
++ "klips_debug:ipsec_xmit_encap_bundle: "
++ "head,tailroom: %d,%d after hard_header stripped.\n",
++ skb_headroom(ixs->skb), skb_tailroom(ixs->skb));
++ KLIPS_IP_PRINT(debug_tunnel & DB_TN_CROUT, ixs->iph);
++ } else {
++ KLIPS_PRINT(debug_tunnel & DB_TN_CROUT,
++ "klips_debug:ipsec_xmit_encap_bundle: "
++ "hard header already stripped.\n");
++ }
++
++ ixs->ll_headroom = (ixs->hard_header_len + 15) & ~15;
++
++ if ((skb_headroom(ixs->skb) >= ixs->max_headroom + 2 * ixs->ll_headroom) &&
++ (skb_tailroom(ixs->skb) >= ixs->max_tailroom)
++#ifndef NET_21
++ && ixs->skb->free
++#endif /* !NET_21 */
++ ) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_CROUT,
++ "klips_debug:ipsec_xmit_encap_bundle: "
++ "data fits in existing skb\n");
++ } else {
++ struct sk_buff* tskb;
++
++ if(!ixs->oskb) {
++ ixs->oskb = ixs->skb;
++ }
++
++ tskb = skb_copy_expand(ixs->skb,
++ /* The need for 2 * link layer length here remains unexplained...RGB */
++ ixs->max_headroom + 2 * ixs->ll_headroom,
++ ixs->max_tailroom,
++ GFP_ATOMIC);
++#ifdef NET_21
++ if(tskb && ixs->skb->sk) {
++ skb_set_owner_w(tskb, ixs->skb->sk);
++ }
++#endif /* NET_21 */
++ if(ixs->skb != ixs->oskb) {
++ ipsec_kfree_skb(ixs->skb);
++ }
++ ixs->skb = tskb;
++ if (!ixs->skb) {
++ printk(KERN_WARNING
++ "klips_debug:ipsec_xmit_encap_bundle: "
++ "Failed, tried to allocate %d head and %d tailroom\n",
++ ixs->max_headroom, ixs->max_tailroom);
++ ixs->stats->tx_errors++;
++ bundle_stat = IPSEC_XMIT_ERRSKBALLOC;
++ goto cleanup;
++ }
++ KLIPS_PRINT(debug_tunnel & DB_TN_CROUT,
++ "klips_debug:ipsec_xmit_encap_bundle: "
++ "head,tailroom: %d,%d after allocation\n",
++ skb_headroom(ixs->skb), skb_tailroom(ixs->skb));
++ }
++
++ /*
++ * Apply grouped transforms to packet
++ */
++ while (ixs->ipsp) {
++ enum ipsec_xmit_value encap_stat = IPSEC_XMIT_OK;
++
++ encap_stat = ipsec_xmit_encap_once(ixs);
++ if(encap_stat != IPSEC_XMIT_OK) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
++ "klips_debug:ipsec_xmit_encap_bundle: encap_once failed: %d\n",
++ encap_stat);
++
++ bundle_stat = IPSEC_XMIT_ENCAPFAIL;
++ goto cleanup;
++ }
++ }
++ /* end encapsulation loop here XXX */
++ cleanup:
++ spin_unlock(&tdb_lock);
++ return bundle_stat;
++}
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.8 2004/04/06 02:49:26 mcr
++ * pullup of algo code from alg-branch.
++ *
++ * Revision 1.7 2004/02/03 03:13:41 mcr
++ * mark invalid encapsulation states.
++ *
++ * Revision 1.6.2.1 2003/12/22 15:25:52 jjo
++ * Merged algo-0.8.1-rc11-test1 into alg-branch
++ *
++ * Revision 1.6 2003/12/10 01:14:27 mcr
++ * NAT-traversal patches to KLIPS.
++ *
++ * Revision 1.5 2003/10/31 02:27:55 mcr
++ * pulled up port-selector patches and sa_id elimination.
++ *
++ * Revision 1.4.4.2 2003/10/29 01:37:39 mcr
++ * when creating %hold from %trap, only make the %hold as
++ * specific as the %trap was - so if the protocol and ports
++ * were wildcards, then the %hold will be too.
++ *
++ * Revision 1.4.4.1 2003/09/21 13:59:56 mcr
++ * pre-liminary X.509 patch - does not yet pass tests.
++ *
++ * Revision 1.4 2003/06/20 02:28:10 mcr
++ * misstype of variable name, not detected by module build.
++ *
++ * Revision 1.3 2003/06/20 01:42:21 mcr
++ * added counters to measure how many ACQUIREs we send to pluto,
++ * and how many are successfully sent.
++ *
++ * Revision 1.2 2003/04/03 17:38:35 rgb
++ * Centralised ipsec_kfree_skb and ipsec_dev_{get,put}.
++ * Normalised coding style.
++ * Simplified logic and reduced duplication of code.
++ *
++ * Revision 1.1 2003/02/12 19:31:23 rgb
++ * Refactored from ipsec_tunnel.c
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/net/ipsec/pfkey_v2.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,2126 @@
++/*
++ * @(#) RFC2367 PF_KEYv2 Key management API domain socket I/F
++ * Copyright (C) 1999, 2000, 2001 Richard Guy Briggs.
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ * RCSID $Id$
++ */
++
++/*
++ * Template from /usr/src/linux-2.0.36/net/unix/af_unix.c.
++ * Hints from /usr/src/linux-2.0.36/net/ipv4/udp.c.
++ */
++
++#define __NO_VERSION__
++#include <linux/module.h>
++#include <linux/version.h>
++#include <linux/config.h>
++#include <linux/kernel.h>
++
++#include "openswan/ipsec_param.h"
++
++#include <linux/major.h>
++#include <linux/signal.h>
++#include <linux/sched.h>
++#include <linux/errno.h>
++#include <linux/string.h>
++#include <linux/stat.h>
++#include <linux/socket.h>
++#include <linux/un.h>
++#include <linux/fcntl.h>
++#include <linux/termios.h>
++#include <linux/socket.h>
++#include <linux/sockios.h>
++#include <linux/net.h> /* struct socket */
++#include <linux/in.h>
++#include <linux/fs.h>
++#ifdef MALLOC_SLAB
++# include <linux/slab.h> /* kmalloc() */
++#else /* MALLOC_SLAB */
++# include <linux/malloc.h> /* kmalloc() */
++#endif /* MALLOC_SLAB */
++#include <asm/segment.h>
++#include <linux/skbuff.h>
++#include <linux/netdevice.h>
++#include <net/sock.h> /* struct sock */
++/* #include <net/tcp.h> */
++#include <net/af_unix.h>
++#ifdef CONFIG_PROC_FS
++# include <linux/proc_fs.h>
++#endif /* CONFIG_PROC_FS */
++
++#include <linux/types.h>
++
++#include <openswan.h>
++#ifdef NET_21
++# include <asm/uaccess.h>
++# include <linux/in6.h>
++#endif /* NET_21 */
++
++#include "openswan/radij.h"
++#include "openswan/ipsec_encap.h"
++#include "openswan/ipsec_sa.h"
++
++#include <pfkeyv2.h>
++#include <pfkey.h>
++
++#include "openswan/ipsec_proto.h"
++
++#ifdef CONFIG_IPSEC_DEBUG
++int debug_pfkey = 0;
++extern int sysctl_ipsec_debug_verbose;
++#endif /* CONFIG_IPSEC_DEBUG */
++
++#define SENDERR(_x) do { error = -(_x); goto errlab; } while (0)
++
++#ifndef SOCKOPS_WRAPPED
++#define SOCKOPS_WRAPPED(name) name
++#endif /* SOCKOPS_WRAPPED */
++
++extern struct proto_ops pfkey_ops;
++struct sock *pfkey_sock_list = NULL;
++struct supported_list *pfkey_supported_list[SADB_SATYPE_MAX+1];
++
++struct socket_list *pfkey_open_sockets = NULL;
++struct socket_list *pfkey_registered_sockets[SADB_SATYPE_MAX+1];
++
++int pfkey_msg_interp(struct sock *, struct sadb_msg *, struct sadb_msg **);
++
++int
++pfkey_list_remove_socket(struct socket *socketp, struct socket_list **sockets)
++{
++ struct socket_list *socket_listp,*prev;
++
++ if(!socketp) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_list_remove_socket: "
++ "NULL socketp handed in, failed.\n");
++ return -EINVAL;
++ }
++
++ if(!sockets) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_list_remove_socket: "
++ "NULL sockets list handed in, failed.\n");
++ return -EINVAL;
++ }
++
++ socket_listp = *sockets;
++ prev = NULL;
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_list_remove_socket: "
++ "removing sock=0p%p\n",
++ socketp);
++
++ while(socket_listp != NULL) {
++ if(socket_listp->socketp == socketp) {
++ if(prev != NULL) {
++ prev->next = socket_listp->next;
++ } else {
++ *sockets = socket_listp->next;
++ }
++
++ kfree((void*)socket_listp);
++
++ break;
++ }
++ prev = socket_listp;
++ socket_listp = socket_listp->next;
++ }
++
++ return 0;
++}
++
++int
++pfkey_list_insert_socket(struct socket *socketp, struct socket_list **sockets)
++{
++ struct socket_list *socket_listp;
++
++ if(!socketp) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_list_insert_socket: "
++ "NULL socketp handed in, failed.\n");
++ return -EINVAL;
++ }
++
++ if(!sockets) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_list_insert_socket: "
++ "NULL sockets list handed in, failed.\n");
++ return -EINVAL;
++ }
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_list_insert_socket: "
++ "allocating %lu bytes for socketp=0p%p\n",
++ (unsigned long) sizeof(struct socket_list),
++ socketp);
++
++ if((socket_listp = (struct socket_list *)kmalloc(sizeof(struct socket_list), GFP_KERNEL)) == NULL) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_list_insert_socket: "
++ "memory allocation error.\n");
++ return -ENOMEM;
++ }
++
++ socket_listp->socketp = socketp;
++ socket_listp->next = *sockets;
++ *sockets = socket_listp;
++
++ return 0;
++}
++
++int
++pfkey_list_remove_supported(struct supported *supported, struct supported_list **supported_list)
++{
++ struct supported_list *supported_listp = *supported_list, *prev = NULL;
++
++ if(!supported) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_list_remove_supported: "
++ "NULL supported handed in, failed.\n");
++ return -EINVAL;
++ }
++
++ if(!supported_list) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_list_remove_supported: "
++ "NULL supported_list handed in, failed.\n");
++ return -EINVAL;
++ }
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_list_remove_supported: "
++ "removing supported=0p%p\n",
++ supported);
++
++ while(supported_listp != NULL) {
++ if(supported_listp->supportedp == supported) {
++ if(prev != NULL) {
++ prev->next = supported_listp->next;
++ } else {
++ *supported_list = supported_listp->next;
++ }
++
++ kfree((void*)supported_listp);
++
++ break;
++ }
++ prev = supported_listp;
++ supported_listp = supported_listp->next;
++ }
++
++ return 0;
++}
++
++int
++pfkey_list_insert_supported(struct supported *supported, struct supported_list **supported_list)
++{
++ struct supported_list *supported_listp;
++
++ if(!supported) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_list_insert_supported: "
++ "NULL supported handed in, failed.\n");
++ return -EINVAL;
++ }
++
++ if(!supported_list) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_list_insert_supported: "
++ "NULL supported_list handed in, failed.\n");
++ return -EINVAL;
++ }
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_list_insert_supported: "
++ "allocating %lu bytes for incoming, supported=0p%p, supported_list=0p%p\n",
++ (unsigned long) sizeof(struct supported_list),
++ supported,
++ supported_list);
++
++ supported_listp = (struct supported_list *)kmalloc(sizeof(struct supported_list), GFP_KERNEL);
++ if(supported_listp == NULL) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_list_insert_supported: "
++ "memory allocation error.\n");
++ return -ENOMEM;
++ }
++
++ supported_listp->supportedp = supported;
++ supported_listp->next = *supported_list;
++ *supported_list = supported_listp;
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_list_insert_supported: "
++ "outgoing, supported=0p%p, supported_list=0p%p\n",
++ supported,
++ supported_list);
++
++ return 0;
++}
++
++#ifndef NET_21
++DEBUG_NO_STATIC void
++pfkey_state_change(struct sock *sk)
++{
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_state_change: .\n");
++ if(!sk->dead) {
++ wake_up_interruptible(sk->sleep);
++ }
++}
++#endif /* !NET_21 */
++
++#ifndef NET_21
++DEBUG_NO_STATIC void
++pfkey_data_ready(struct sock *sk, int len)
++{
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_data_ready: "
++ "sk=0p%p len=%d\n",
++ sk,
++ len);
++ if(!sk->dead) {
++ wake_up_interruptible(sk->sleep);
++ sock_wake_async(sk->socket, 1);
++ }
++}
++
++DEBUG_NO_STATIC void
++pfkey_write_space(struct sock *sk)
++{
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_write_space: .\n");
++ if(!sk->dead) {
++ wake_up_interruptible(sk->sleep);
++ sock_wake_async(sk->socket, 2);
++ }
++}
++#endif /* !NET_21 */
++
++DEBUG_NO_STATIC void
++pfkey_insert_socket(struct sock *sk)
++{
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_insert_socket: "
++ "sk=0p%p\n",
++ sk);
++ cli();
++ sk->next=pfkey_sock_list;
++ pfkey_sock_list=sk;
++ sti();
++}
++
++DEBUG_NO_STATIC void
++pfkey_remove_socket(struct sock *sk)
++{
++ struct sock **s;
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_remove_socket: .\n");
++ cli();
++ s=&pfkey_sock_list;
++
++ while(*s!=NULL) {
++ if(*s==sk) {
++ *s=sk->next;
++ sk->next=NULL;
++ sti();
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_remove_socket: "
++ "succeeded.\n");
++ return;
++ }
++ s=&((*s)->next);
++ }
++ sti();
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_remove_socket: "
++ "not found.\n");
++ return;
++}
++
++DEBUG_NO_STATIC void
++pfkey_destroy_socket(struct sock *sk)
++{
++ struct sk_buff *skb;
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_destroy_socket: .\n");
++ pfkey_remove_socket(sk);
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_destroy_socket: "
++ "pfkey_remove_socket called.\n");
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_destroy_socket: "
++ "sk(0p%p)->(&0p%p)receive_queue.{next=0p%p,prev=0p%p}.\n",
++ sk,
++ &(sk->receive_queue),
++ sk->receive_queue.next,
++ sk->receive_queue.prev);
++ while(sk && ((skb=skb_dequeue(&(sk->receive_queue)))!=NULL)) {
++#ifdef NET_21
++#ifdef CONFIG_IPSEC_DEBUG
++ if(debug_pfkey && sysctl_ipsec_debug_verbose) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_destroy_socket: "
++ "skb=0p%p dequeued.\n", skb);
++ printk(KERN_INFO "klips_debug:pfkey_destroy_socket: "
++ "pfkey_skb contents:");
++ printk(" next:0p%p", skb->next);
++ printk(" prev:0p%p", skb->prev);
++ printk(" list:0p%p", skb->list);
++ printk(" sk:0p%p", skb->sk);
++ printk(" stamp:%ld.%ld", skb->stamp.tv_sec, skb->stamp.tv_usec);
++ printk(" dev:0p%p", skb->dev);
++ if(skb->dev) {
++ if(skb->dev->name) {
++ printk(" dev->name:%s", skb->dev->name);
++ } else {
++ printk(" dev->name:NULL?");
++ }
++ } else {
++ printk(" dev:NULL");
++ }
++ printk(" h:0p%p", skb->h.raw);
++ printk(" nh:0p%p", skb->nh.raw);
++ printk(" mac:0p%p", skb->mac.raw);
++ printk(" dst:0p%p", skb->dst);
++ if(sysctl_ipsec_debug_verbose) {
++ int i;
++
++ printk(" cb");
++ for(i=0; i<48; i++) {
++ printk(":%2x", skb->cb[i]);
++ }
++ }
++ printk(" len:%d", skb->len);
++ printk(" csum:%d", skb->csum);
++#ifndef NETDEV_23
++ printk(" used:%d", skb->used);
++ printk(" is_clone:%d", skb->is_clone);
++#endif /* NETDEV_23 */
++ printk(" cloned:%d", skb->cloned);
++ printk(" pkt_type:%d", skb->pkt_type);
++ printk(" ip_summed:%d", skb->ip_summed);
++ printk(" priority:%d", skb->priority);
++ printk(" protocol:%d", skb->protocol);
++ printk(" security:%d", skb->security);
++ printk(" truesize:%d", skb->truesize);
++ printk(" head:0p%p", skb->head);
++ printk(" data:0p%p", skb->data);
++ printk(" tail:0p%p", skb->tail);
++ printk(" end:0p%p", skb->end);
++ if(sysctl_ipsec_debug_verbose) {
++ unsigned char* i;
++ printk(" data");
++ for(i = skb->head; i < skb->end; i++) {
++ printk(":%2x", (unsigned char)(*(i)));
++ }
++ }
++ printk(" destructor:0p%p", skb->destructor);
++ printk("\n");
++ }
++#endif /* CONFIG_IPSEC_DEBUG */
++#endif /* NET_21 */
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_destroy_socket: "
++ "skb=0p%p freed.\n",
++ skb);
++ ipsec_kfree_skb(skb);
++ }
++
++ sk->dead = 1;
++ sk_free(sk);
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_destroy_socket: destroyed.\n");
++}
++
++int
++pfkey_upmsg(struct socket *sock, struct sadb_msg *pfkey_msg)
++{
++ int error = 0;
++ struct sk_buff * skb = NULL;
++ struct sock *sk;
++
++ if(sock == NULL) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_upmsg: "
++ "NULL socket passed in.\n");
++ return -EINVAL;
++ }
++
++ if(pfkey_msg == NULL) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_upmsg: "
++ "NULL pfkey_msg passed in.\n");
++ return -EINVAL;
++ }
++
++#ifdef NET_21
++ sk = sock->sk;
++#else /* NET_21 */
++ sk = sock->data;
++#endif /* NET_21 */
++
++ if(sk == NULL) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_upmsg: "
++ "NULL sock passed in.\n");
++ return -EINVAL;
++ }
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_upmsg: "
++ "allocating %d bytes...\n",
++ (int)(pfkey_msg->sadb_msg_len * IPSEC_PFKEYv2_ALIGN));
++ if(!(skb = alloc_skb(pfkey_msg->sadb_msg_len * IPSEC_PFKEYv2_ALIGN, GFP_ATOMIC) )) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_upmsg: "
++ "no buffers left to send up a message.\n");
++ return -ENOBUFS;
++ }
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_upmsg: "
++ "...allocated at 0p%p.\n",
++ skb);
++
++ skb->dev = NULL;
++
++ if(skb_tailroom(skb) < pfkey_msg->sadb_msg_len * IPSEC_PFKEYv2_ALIGN) {
++ printk(KERN_WARNING "klips_error:pfkey_upmsg: "
++ "tried to skb_put %ld, %d available. This should never happen, please report.\n",
++ (unsigned long int)pfkey_msg->sadb_msg_len * IPSEC_PFKEYv2_ALIGN,
++ skb_tailroom(skb));
++ ipsec_kfree_skb(skb);
++ return -ENOBUFS;
++ }
++ skb->h.raw = skb_put(skb, pfkey_msg->sadb_msg_len * IPSEC_PFKEYv2_ALIGN);
++ memcpy(skb->h.raw, pfkey_msg, pfkey_msg->sadb_msg_len * IPSEC_PFKEYv2_ALIGN);
++
++#ifndef NET_21
++ skb->free = 1;
++#endif /* !NET_21 */
++
++ if((error = sock_queue_rcv_skb(sk, skb)) < 0) {
++ skb->sk=NULL;
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_upmsg: "
++ "error=%d calling sock_queue_rcv_skb with skb=0p%p.\n",
++ error,
++ skb);
++ ipsec_kfree_skb(skb);
++ return error;
++ }
++ return error;
++}
++
++DEBUG_NO_STATIC int
++pfkey_create(struct socket *sock, int protocol)
++{
++ struct sock *sk;
++
++ if(sock == NULL) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_create: "
++ "socket NULL.\n");
++ return -EINVAL;
++ }
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_create: "
++ "sock=0p%p type:%d state:%d flags:%ld protocol:%d\n",
++ sock,
++ sock->type,
++ (unsigned int)(sock->state),
++ sock->flags, protocol);
++
++ if(sock->type != SOCK_RAW) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_create: "
++ "only SOCK_RAW supported.\n");
++ return -ESOCKTNOSUPPORT;
++ }
++
++ if(protocol != PF_KEY_V2) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_create: "
++ "protocol not PF_KEY_V2.\n");
++ return -EPROTONOSUPPORT;
++ }
++
++ if((current->uid != 0)) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_create: "
++ "must be root to open pfkey sockets.\n");
++ return -EACCES;
++ }
++
++#ifdef NET_21
++ sock->state = SS_UNCONNECTED;
++#endif /* NET_21 */
++ MOD_INC_USE_COUNT;
++#ifdef NET_21
++ if((sk=(struct sock *)sk_alloc(PF_KEY, GFP_KERNEL, 1)) == NULL)
++#else /* NET_21 */
++ if((sk=(struct sock *)sk_alloc(GFP_KERNEL)) == NULL)
++#endif /* NET_21 */
++ {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_create: "
++ "Out of memory trying to allocate.\n");
++ MOD_DEC_USE_COUNT;
++ return -ENOMEM;
++ }
++
++#ifndef NET_21
++ memset(sk, 0, sizeof(*sk));
++#endif /* !NET_21 */
++
++#ifdef NET_21
++ sock_init_data(sock, sk);
++
++ sk->destruct = NULL;
++ sk->reuse = 1;
++ sock->ops = &pfkey_ops;
++
++ sk->zapped=0;
++ sk->family = PF_KEY;
++/* sk->num = protocol; */
++ sk->protocol = protocol;
++ key_pid(sk) = current->pid;
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_create: "
++ "sock->fasync_list=0p%p sk->sleep=0p%p.\n",
++ sock->fasync_list,
++ sk->sleep);
++#else /* NET_21 */
++ sk->type=sock->type;
++ init_timer(&sk->timer);
++ skb_queue_head_init(&sk->write_queue);
++ skb_queue_head_init(&sk->receive_queue);
++ skb_queue_head_init(&sk->back_log);
++ sk->rcvbuf=SK_RMEM_MAX;
++ sk->sndbuf=SK_WMEM_MAX;
++ sk->allocation=GFP_KERNEL;
++ sk->state=TCP_CLOSE;
++ sk->priority=SOPRI_NORMAL;
++ sk->state_change=pfkey_state_change;
++ sk->data_ready=pfkey_data_ready;
++ sk->write_space=pfkey_write_space;
++ sk->error_report=pfkey_state_change;
++ sk->mtu=4096;
++ sk->socket=sock;
++ sock->data=(void *)sk;
++ sk->sleep=sock->wait;
++#endif /* NET_21 */
++
++ pfkey_insert_socket(sk);
++ pfkey_list_insert_socket(sock, &pfkey_open_sockets);
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_create: "
++ "Socket sock=0p%p sk=0p%p initialised.\n", sock, sk);
++ return 0;
++}
++
++#ifndef NET_21
++DEBUG_NO_STATIC int
++pfkey_dup(struct socket *newsock, struct socket *oldsock)
++{
++ struct sock *sk;
++
++ if(newsock==NULL) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_dup: "
++ "No new socket attached.\n");
++ return -EINVAL;
++ }
++
++ if(oldsock==NULL) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_dup: "
++ "No old socket attached.\n");
++ return -EINVAL;
++ }
++
++#ifdef NET_21
++ sk=oldsock->sk;
++#else /* NET_21 */
++ sk=oldsock->data;
++#endif /* NET_21 */
++
++ /* May not have data attached */
++ if(sk==NULL) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_dup: "
++ "No sock attached to old socket.\n");
++ return -EINVAL;
++ }
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_dup: .\n");
++
++ return pfkey_create(newsock, sk->protocol);
++}
++#endif /* !NET_21 */
++
++DEBUG_NO_STATIC int
++#ifdef NETDEV_23
++pfkey_release(struct socket *sock)
++#else /* NETDEV_23 */
++pfkey_release(struct socket *sock, struct socket *peersock)
++#endif /* NETDEV_23 */
++{
++ struct sock *sk;
++ int i;
++
++ if(sock==NULL) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_release: "
++ "No socket attached.\n");
++ return 0; /* -EINVAL; */
++ }
++
++#ifdef NET_21
++ sk=sock->sk;
++#else /* NET_21 */
++ sk=sock->data;
++#endif /* NET_21 */
++
++ /* May not have data attached */
++ if(sk==NULL) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_release: "
++ "No sk attached to sock=0p%p.\n", sock);
++ return 0; /* -EINVAL; */
++ }
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_release: "
++ "sock=0p%p sk=0p%p\n", sock, sk);
++
++#ifdef NET_21
++ if(!sk->dead)
++#endif /* NET_21 */
++ if(sk->state_change) {
++ sk->state_change(sk);
++ }
++
++#ifdef NET_21
++ sock->sk = NULL;
++#else /* NET_21 */
++ sock->data = NULL;
++#endif /* NET_21 */
++
++ /* Try to flush out this socket. Throw out buffers at least */
++ pfkey_destroy_socket(sk);
++ pfkey_list_remove_socket(sock, &pfkey_open_sockets);
++ for(i = SADB_SATYPE_UNSPEC; i <= SADB_SATYPE_MAX; i++) {
++ pfkey_list_remove_socket(sock, &(pfkey_registered_sockets[i]));
++ }
++
++ MOD_DEC_USE_COUNT;
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_release: "
++ "succeeded.\n");
++
++ return 0;
++}
++
++#ifndef NET_21
++DEBUG_NO_STATIC int
++pfkey_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
++{
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_bind: "
++ "operation not supported.\n");
++ return -EINVAL;
++}
++
++DEBUG_NO_STATIC int
++pfkey_connect(struct socket *sock, struct sockaddr *uaddr, int addr_len, int flags)
++{
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_connect: "
++ "operation not supported.\n");
++ return -EINVAL;
++}
++
++DEBUG_NO_STATIC int
++pfkey_socketpair(struct socket *a, struct socket *b)
++{
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_socketpair: "
++ "operation not supported.\n");
++ return -EINVAL;
++}
++
++DEBUG_NO_STATIC int
++pfkey_accept(struct socket *sock, struct socket *newsock, int flags)
++{
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_aaccept: "
++ "operation not supported.\n");
++ return -EINVAL;
++}
++
++DEBUG_NO_STATIC int
++pfkey_getname(struct socket *sock, struct sockaddr *uaddr, int *uaddr_len,
++ int peer)
++{
++ struct sockaddr *ska = (struct sockaddr*)uaddr;
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_getname: .\n");
++ ska->sa_family = PF_KEY;
++ *uaddr_len = sizeof(*ska);
++ return 0;
++}
++
++DEBUG_NO_STATIC int
++pfkey_select(struct socket *sock, int sel_type, select_table *wait)
++{
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_select: "
++ ".sock=0p%p sk=0p%p sel_type=%d\n",
++ sock,
++ sock->data,
++ sel_type);
++ if(sock == NULL) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_select: "
++ "Null socket passed in.\n");
++ return -EINVAL;
++ }
++ return datagram_select(sock->data, sel_type, wait);
++}
++
++DEBUG_NO_STATIC int
++pfkey_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
++{
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_ioctl: "
++ "not supported.\n");
++ return -EINVAL;
++}
++
++DEBUG_NO_STATIC int
++pfkey_listen(struct socket *sock, int backlog)
++{
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_listen: "
++ "not supported.\n");
++ return -EINVAL;
++}
++#endif /* !NET_21 */
++
++DEBUG_NO_STATIC int
++pfkey_shutdown(struct socket *sock, int mode)
++{
++ struct sock *sk;
++
++ if(sock == NULL) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_shutdown: "
++ "NULL socket passed in.\n");
++ return -EINVAL;
++ }
++
++#ifdef NET_21
++ sk=sock->sk;
++#else /* NET_21 */
++ sk=sock->data;
++#endif /* NET_21 */
++
++ if(sk == NULL) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_shutdown: "
++ "No sock attached to socket.\n");
++ return -EINVAL;
++ }
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_shutdown: "
++ "mode=%x.\n", mode);
++ mode++;
++
++ if(mode&SEND_SHUTDOWN) {
++ sk->shutdown|=SEND_SHUTDOWN;
++ sk->state_change(sk);
++ }
++
++ if(mode&RCV_SHUTDOWN) {
++ sk->shutdown|=RCV_SHUTDOWN;
++ sk->state_change(sk);
++ }
++ return 0;
++}
++
++#ifndef NET_21
++DEBUG_NO_STATIC int
++pfkey_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen)
++{
++#ifndef NET_21
++ struct sock *sk;
++
++ if(sock == NULL) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_setsockopt: "
++ "Null socket passed in.\n");
++ return -EINVAL;
++ }
++
++ sk=sock->data;
++
++ if(sk == NULL) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_setsockopt: "
++ "Null sock passed in.\n");
++ return -EINVAL;
++ }
++#endif /* !NET_21 */
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_setsockopt: .\n");
++ if(level!=SOL_SOCKET) {
++ return -EOPNOTSUPP;
++ }
++#ifdef NET_21
++ return sock_setsockopt(sock, level, optname, optval, optlen);
++#else /* NET_21 */
++ return sock_setsockopt(sk, level, optname, optval, optlen);
++#endif /* NET_21 */
++}
++
++DEBUG_NO_STATIC int
++pfkey_getsockopt(struct socket *sock, int level, int optname, char *optval, int *optlen)
++{
++#ifndef NET_21
++ struct sock *sk;
++
++ if(sock == NULL) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_setsockopt: "
++ "Null socket passed in.\n");
++ return -EINVAL;
++ }
++
++ sk=sock->data;
++
++ if(sk == NULL) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_setsockopt: "
++ "Null sock passed in.\n");
++ return -EINVAL;
++ }
++#endif /* !NET_21 */
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_getsockopt: .\n");
++ if(level!=SOL_SOCKET) {
++ return -EOPNOTSUPP;
++ }
++#ifdef NET_21
++ return sock_getsockopt(sock, level, optname, optval, optlen);
++#else /* NET_21 */
++ return sock_getsockopt(sk, level, optname, optval, optlen);
++#endif /* NET_21 */
++}
++
++DEBUG_NO_STATIC int
++pfkey_fcntl(struct socket *sock, unsigned int cmd, unsigned long arg)
++{
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_fcntl: "
++ "not supported.\n");
++ return -EINVAL;
++}
++#endif /* !NET_21 */
++
++/*
++ * Send PF_KEY data down.
++ */
++
++DEBUG_NO_STATIC int
++#ifdef NET_21
++pfkey_sendmsg(struct socket *sock, struct msghdr *msg, int len, struct scm_cookie *scm)
++#else /* NET_21 */
++pfkey_sendmsg(struct socket *sock, struct msghdr *msg, int len, int nonblock, int flags)
++#endif /* NET_21 */
++{
++ struct sock *sk;
++ int error = 0;
++ struct sadb_msg *pfkey_msg = NULL, *pfkey_reply = NULL;
++
++ if(sock == NULL) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_sendmsg: "
++ "Null socket passed in.\n");
++ SENDERR(EINVAL);
++ }
++
++#ifdef NET_21
++ sk = sock->sk;
++#else /* NET_21 */
++ sk = sock->data;
++#endif /* NET_21 */
++
++ if(sk == NULL) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_sendmsg: "
++ "Null sock passed in.\n");
++ SENDERR(EINVAL);
++ }
++
++ if(msg == NULL) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_sendmsg: "
++ "Null msghdr passed in.\n");
++ SENDERR(EINVAL);
++ }
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_sendmsg: .\n");
++ if(sk->err) {
++ error = sock_error(sk);
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_sendmsg: "
++ "sk->err is non-zero, returns %d.\n",
++ error);
++ SENDERR(-error);
++ }
++
++ if((current->uid != 0)) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_sendmsg: "
++ "must be root to send messages to pfkey sockets.\n");
++ SENDERR(EACCES);
++ }
++
++#ifdef NET_21
++ if(msg->msg_control)
++#else /* NET_21 */
++ if(flags || msg->msg_control)
++#endif /* NET_21 */
++ {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_sendmsg: "
++ "can't set flags or set msg_control.\n");
++ SENDERR(EINVAL);
++ }
++
++ if(sk->shutdown & SEND_SHUTDOWN) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_sendmsg: "
++ "shutdown.\n");
++ send_sig(SIGPIPE, current, 0);
++ SENDERR(EPIPE);
++ }
++
++ if(len < sizeof(struct sadb_msg)) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_sendmsg: "
++ "bogus msg len of %d, too small.\n", len);
++ SENDERR(EMSGSIZE);
++ }
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_sendmsg: "
++ "allocating %d bytes for downward message.\n",
++ len);
++ if((pfkey_msg = (struct sadb_msg*)kmalloc(len, GFP_KERNEL)) == NULL) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_sendmsg: "
++ "memory allocation error.\n");
++ SENDERR(ENOBUFS);
++ }
++
++ memcpy_fromiovec((void *)pfkey_msg, msg->msg_iov, len);
++
++ if(pfkey_msg->sadb_msg_version != PF_KEY_V2) {
++ KLIPS_PRINT(1 || debug_pfkey,
++ "klips_debug:pfkey_sendmsg: "
++ "not PF_KEY_V2 msg, found %d, should be %d.\n",
++ pfkey_msg->sadb_msg_version,
++ PF_KEY_V2);
++ kfree((void*)pfkey_msg);
++ return -EINVAL;
++ }
++
++ if(len != pfkey_msg->sadb_msg_len * IPSEC_PFKEYv2_ALIGN) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_sendmsg: "
++ "bogus msg len of %d, not %d byte aligned.\n",
++ len, (int)IPSEC_PFKEYv2_ALIGN);
++ SENDERR(EMSGSIZE);
++ }
++
++#if 0
++ /* This check is questionable, since a downward message could be
++ the result of an ACQUIRE either from kernel (PID==0) or
++ userspace (some other PID). */
++ /* check PID */
++ if(pfkey_msg->sadb_msg_pid != current->pid) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_sendmsg: "
++ "pid (%d) does not equal sending process pid (%d).\n",
++ pfkey_msg->sadb_msg_pid, current->pid);
++ SENDERR(EINVAL);
++ }
++#endif
++
++ if(pfkey_msg->sadb_msg_reserved) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_sendmsg: "
++ "reserved field must be zero, set to %d.\n",
++ pfkey_msg->sadb_msg_reserved);
++ SENDERR(EINVAL);
++ }
++
++ if((pfkey_msg->sadb_msg_type > SADB_MAX) || (!pfkey_msg->sadb_msg_type)){
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_sendmsg: "
++ "msg type too large or small:%d.\n",
++ pfkey_msg->sadb_msg_type);
++ SENDERR(EINVAL);
++ }
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_sendmsg: "
++ "msg sent for parsing.\n");
++
++ if((error = pfkey_msg_interp(sk, pfkey_msg, &pfkey_reply))) {
++ struct socket_list *pfkey_socketsp;
++
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_sendmsg: "
++ "pfkey_msg_parse returns %d.\n",
++ error);
++
++ if((pfkey_reply = (struct sadb_msg*)kmalloc(sizeof(struct sadb_msg), GFP_KERNEL)) == NULL) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_sendmsg: "
++ "memory allocation error.\n");
++ SENDERR(ENOBUFS);
++ }
++ memcpy((void*)pfkey_reply, (void*)pfkey_msg, sizeof(struct sadb_msg));
++ pfkey_reply->sadb_msg_errno = -error;
++ pfkey_reply->sadb_msg_len = sizeof(struct sadb_msg) / IPSEC_PFKEYv2_ALIGN;
++
++ for(pfkey_socketsp = pfkey_open_sockets;
++ pfkey_socketsp;
++ pfkey_socketsp = pfkey_socketsp->next) {
++ int error_upmsg = 0;
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_sendmsg: "
++ "sending up error=%d message=0p%p to socket=0p%p.\n",
++ error,
++ pfkey_reply,
++ pfkey_socketsp->socketp);
++ if((error_upmsg = pfkey_upmsg(pfkey_socketsp->socketp, pfkey_reply))) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_sendmsg: "
++ "sending up error message to socket=0p%p failed with error=%d.\n",
++ pfkey_socketsp->socketp,
++ error_upmsg);
++ /* pfkey_msg_free(&pfkey_reply); */
++ /* SENDERR(-error); */
++ }
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_sendmsg: "
++ "sending up error message to socket=0p%p succeeded.\n",
++ pfkey_socketsp->socketp);
++ }
++
++ pfkey_msg_free(&pfkey_reply);
++
++ SENDERR(-error);
++ }
++
++ errlab:
++ if (pfkey_msg) {
++ kfree((void*)pfkey_msg);
++ }
++
++ if(error) {
++ return error;
++ } else {
++ return len;
++ }
++}
++
++/*
++ * Receive PF_KEY data up.
++ */
++
++DEBUG_NO_STATIC int
++#ifdef NET_21
++pfkey_recvmsg(struct socket *sock, struct msghdr *msg, int size, int flags, struct scm_cookie *scm)
++#else /* NET_21 */
++pfkey_recvmsg(struct socket *sock, struct msghdr *msg, int size, int noblock, int flags, int *addr_len)
++#endif /* NET_21 */
++{
++ struct sock *sk;
++#ifdef NET_21
++ int noblock = flags & MSG_DONTWAIT;
++#endif /* NET_21 */
++ struct sk_buff *skb;
++ int error;
++
++ if(sock == NULL) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_recvmsg: "
++ "Null socket passed in.\n");
++ return -EINVAL;
++ }
++
++#ifdef NET_21
++ sk = sock->sk;
++#else /* NET_21 */
++ sk = sock->data;
++#endif /* NET_21 */
++
++ if(sk == NULL) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_recvmsg: "
++ "Null sock passed in for sock=0p%p.\n", sock);
++ return -EINVAL;
++ }
++
++ if(msg == NULL) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_recvmsg: "
++ "Null msghdr passed in for sock=0p%p, sk=0p%p.\n",
++ sock, sk);
++ return -EINVAL;
++ }
++
++ KLIPS_PRINT(debug_pfkey && sysctl_ipsec_debug_verbose,
++ "klips_debug:pfkey_recvmsg: sock=0p%p sk=0p%p msg=0p%p size=%d.\n",
++ sock, sk, msg, size);
++ if(flags & ~MSG_PEEK) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_sendmsg: "
++ "flags (%d) other than MSG_PEEK not supported.\n",
++ flags);
++ return -EOPNOTSUPP;
++ }
++
++#ifdef NET_21
++ msg->msg_namelen = 0; /* sizeof(*ska); */
++#else /* NET_21 */
++ if(addr_len) {
++ *addr_len = 0; /* sizeof(*ska); */
++ }
++#endif /* NET_21 */
++
++ if(sk->err) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_sendmsg: "
++ "sk->err=%d.\n", sk->err);
++ return sock_error(sk);
++ }
++
++ if((skb = skb_recv_datagram(sk, flags, noblock, &error) ) == NULL) {
++ return error;
++ }
++
++ if(size > skb->len) {
++ size = skb->len;
++ }
++#ifdef NET_21
++ else if(size <skb->len) {
++ msg->msg_flags |= MSG_TRUNC;
++ }
++#endif /* NET_21 */
++
++ skb_copy_datagram_iovec(skb, 0, msg->msg_iov, size);
++ sk->stamp=skb->stamp;
++
++ skb_free_datagram(sk, skb);
++ return size;
++}
++
++#ifdef NET_21
++struct net_proto_family pfkey_family_ops = {
++ PF_KEY,
++ pfkey_create
++};
++
++struct proto_ops SOCKOPS_WRAPPED(pfkey_ops) = {
++#ifdef NETDEV_23
++ family: PF_KEY,
++ release: pfkey_release,
++ bind: sock_no_bind,
++ connect: sock_no_connect,
++ socketpair: sock_no_socketpair,
++ accept: sock_no_accept,
++ getname: sock_no_getname,
++ poll: datagram_poll,
++ ioctl: sock_no_ioctl,
++ listen: sock_no_listen,
++ shutdown: pfkey_shutdown,
++ setsockopt: sock_no_setsockopt,
++ getsockopt: sock_no_getsockopt,
++ sendmsg: pfkey_sendmsg,
++ recvmsg: pfkey_recvmsg,
++ mmap: sock_no_mmap,
++#else /* NETDEV_23 */
++ PF_KEY,
++ sock_no_dup,
++ pfkey_release,
++ sock_no_bind,
++ sock_no_connect,
++ sock_no_socketpair,
++ sock_no_accept,
++ sock_no_getname,
++ datagram_poll,
++ sock_no_ioctl,
++ sock_no_listen,
++ pfkey_shutdown,
++ sock_no_setsockopt,
++ sock_no_getsockopt,
++ sock_no_fcntl,
++ pfkey_sendmsg,
++ pfkey_recvmsg
++#endif /* NETDEV_23 */
++};
++
++#ifdef NETDEV_23
++#include <linux/smp_lock.h>
++SOCKOPS_WRAP(pfkey, PF_KEY);
++#endif /* NETDEV_23 */
++
++#else /* NET_21 */
++struct proto_ops pfkey_proto_ops = {
++ PF_KEY,
++ pfkey_create,
++ pfkey_dup,
++ pfkey_release,
++ pfkey_bind,
++ pfkey_connect,
++ pfkey_socketpair,
++ pfkey_accept,
++ pfkey_getname,
++ pfkey_select,
++ pfkey_ioctl,
++ pfkey_listen,
++ pfkey_shutdown,
++ pfkey_setsockopt,
++ pfkey_getsockopt,
++ pfkey_fcntl,
++ pfkey_sendmsg,
++ pfkey_recvmsg
++};
++#endif /* NET_21 */
++
++#ifdef CONFIG_PROC_FS
++#ifndef PROC_FS_2325
++DEBUG_NO_STATIC
++#endif /* PROC_FS_2325 */
++int
++pfkey_get_info(char *buffer, char **start, off_t offset, int length
++#ifndef PROC_NO_DUMMY
++, int dummy
++#endif /* !PROC_NO_DUMMY */
++)
++{
++ const int max_content = length > 0? length-1 : 0;
++ off_t begin=0;
++ int len=0;
++ struct sock *sk=pfkey_sock_list;
++
++#ifdef CONFIG_IPSEC_DEBUG
++ if(!sysctl_ipsec_debug_verbose) {
++#endif /* CONFIG_IPSEC_DEBUG */
++ len+= snprintf(buffer,length,
++ " sock pid socket next prev e n p sndbf Flags Type St\n");
++#ifdef CONFIG_IPSEC_DEBUG
++ } else {
++ len+= snprintf(buffer,length,
++ " sock pid d sleep socket next prev e r z n p sndbf stamp Flags Type St\n");
++ }
++#endif /* CONFIG_IPSEC_DEBUG */
++
++ while(sk!=NULL) {
++#ifdef CONFIG_IPSEC_DEBUG
++ if(!sysctl_ipsec_debug_verbose) {
++#endif /* CONFIG_IPSEC_DEBUG */
++ len += ipsec_snprintf(buffer+len, length-len,
++ "%8p %5d %8p %8p %8p %d %d %d %5d %08lX %8X %2X\n",
++ sk,
++ key_pid(sk),
++ sk->socket,
++ sk->next,
++ sk->prev,
++ sk->err,
++ sk->num,
++ sk->protocol,
++ sk->sndbuf,
++ sk->socket->flags,
++ sk->socket->type,
++ sk->socket->state);
++#ifdef CONFIG_IPSEC_DEBUG
++ } else {
++ len += ipsec_snprintf(buffer+len, length-len,
++ "%8p %5d %d %8p %8p %8p %8p %d %d %d %d %d %5d %d.%06d %08lX %8X %2X\n",
++ sk,
++ key_pid(sk),
++ sk->dead,
++ sk->sleep,
++ sk->socket,
++ sk->next,
++ sk->prev,
++ sk->err,
++ sk->reuse,
++ sk->zapped,
++ sk->num,
++ sk->protocol,
++ sk->sndbuf,
++ (unsigned int)sk->stamp.tv_sec,
++ (unsigned int)sk->stamp.tv_usec,
++ sk->socket->flags,
++ sk->socket->type,
++ sk->socket->state);
++ }
++#endif /* CONFIG_IPSEC_DEBUG */
++
++ if (len >= max_content) {
++ /* we've done all that can fit -- stop loop */
++ len = max_content; /* truncate crap */
++ break;
++ } else {
++
++ const off_t pos = begin + len; /* file position of end of what we've generated */
++
++ if (pos <= offset) {
++ /* all is before first interesting character:
++ * discard, but note where we are.
++ */
++ len = 0;
++ begin = pos;
++ }
++ }
++ sk=sk->next;
++
++ }
++
++ *start = buffer + (offset - begin); /* Start of wanted data */
++ return len - (offset - begin);
++
++}
++
++#ifndef PROC_FS_2325
++DEBUG_NO_STATIC
++#endif /* PROC_FS_2325 */
++int
++pfkey_supported_get_info(char *buffer, char **start, off_t offset, int length
++#ifndef PROC_NO_DUMMY
++, int dummy
++#endif /* !PROC_NO_DUMMY */
++)
++{
++ const int max_content = length > 0? length-1 : 0;
++ off_t begin=0;
++ int len=0;
++ int satype;
++ struct supported_list *pfkey_supported_p;
++
++ len += ipsec_snprintf(buffer, length,
++ "satype exttype alg_id ivlen minbits maxbits\n");
++
++ for(satype = SADB_SATYPE_UNSPEC; satype <= SADB_SATYPE_MAX; satype++) {
++ pfkey_supported_p = pfkey_supported_list[satype];
++ while(pfkey_supported_p) {
++ len += ipsec_snprintf(buffer+len, length-len,
++ " %2d %2d %2d %3d %3d %3d\n",
++ satype,
++ pfkey_supported_p->supportedp->supported_alg_exttype,
++ pfkey_supported_p->supportedp->supported_alg_id,
++ pfkey_supported_p->supportedp->supported_alg_ivlen,
++ pfkey_supported_p->supportedp->supported_alg_minbits,
++ pfkey_supported_p->supportedp->supported_alg_maxbits);
++
++ if (len >= max_content) {
++ /* we've done all that can fit -- stop loop */
++ len = max_content; /* truncate crap */
++ break;
++ } else {
++ const off_t pos = begin + len; /* file position of end of what we've generated */
++
++ if (pos <= offset) {
++ /* all is before first interesting character:
++ * discard, but note where we are.
++ */
++ len = 0;
++ begin = pos;
++ }
++ }
++
++ pfkey_supported_p = pfkey_supported_p->next;
++ }
++ }
++
++ *start = buffer + (offset - begin); /* Start of wanted data */
++ return len - (offset - begin);
++
++}
++
++#ifndef PROC_FS_2325
++DEBUG_NO_STATIC
++#endif /* PROC_FS_2325 */
++int
++pfkey_registered_get_info(char *buffer, char **start, off_t offset, int length
++#ifndef PROC_NO_DUMMY
++, int dummy
++#endif /* !PROC_NO_DUMMY */
++)
++{
++ const int max_content = length > 0? length-1 : 0;
++ off_t begin=0;
++ int len=0;
++ int satype;
++ struct socket_list *pfkey_sockets;
++
++ len += ipsec_snprintf(buffer, length,
++ "satype socket pid sk\n");
++
++ for(satype = SADB_SATYPE_UNSPEC; satype <= SADB_SATYPE_MAX; satype++) {
++ pfkey_sockets = pfkey_registered_sockets[satype];
++ while(pfkey_sockets) {
++#ifdef NET_21
++ len += ipsec_snprintf(buffer+len, length-len,
++ " %2d %8p %5d %8p\n",
++ satype,
++ pfkey_sockets->socketp,
++ key_pid(pfkey_sockets->socketp->sk),
++ pfkey_sockets->socketp->sk);
++#else /* NET_21 */
++ len += ipsec_snprintf(buffer+len, length-len,
++ " %2d %8p N/A %8p\n",
++ satype,
++ pfkey_sockets->socketp,
++#if 0
++ key_pid((pfkey_sockets->socketp)->data),
++#endif
++ (pfkey_sockets->socketp)->data);
++#endif /* NET_21 */
++
++ if (len >= max_content) {
++ /* we've done all that can fit -- stop loop (could stop two) */
++ len = max_content; /* truncate crap */
++ break;
++ } else {
++ const off_t pos = begin + len; /* file position of end of what we've generated */
++
++ if (pos <= offset) {
++ /* all is before first interesting character:
++ * discard, but note where we are.
++ */
++ len = 0;
++ begin = pos;
++ }
++ }
++
++
++ pfkey_sockets = pfkey_sockets->next;
++ }
++ }
++ *start = buffer + (offset - begin); /* Start of wanted data */
++ return len - (offset - begin);
++}
++
++#ifndef PROC_FS_2325
++struct proc_dir_entry proc_net_pfkey =
++{
++ 0,
++ 6, "pf_key",
++ S_IFREG | S_IRUGO, 1, 0, 0,
++ 0, &proc_net_inode_operations,
++ pfkey_get_info
++};
++struct proc_dir_entry proc_net_pfkey_supported =
++{
++ 0,
++ 16, "pf_key_supported",
++ S_IFREG | S_IRUGO, 1, 0, 0,
++ 0, &proc_net_inode_operations,
++ pfkey_supported_get_info
++};
++struct proc_dir_entry proc_net_pfkey_registered =
++{
++ 0,
++ 17, "pf_key_registered",
++ S_IFREG | S_IRUGO, 1, 0, 0,
++ 0, &proc_net_inode_operations,
++ pfkey_registered_get_info
++};
++#endif /* !PROC_FS_2325 */
++#endif /* CONFIG_PROC_FS */
++
++DEBUG_NO_STATIC int
++supported_add_all(int satype, struct supported supported[], int size)
++{
++ int i;
++ int error = 0;
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:init_pfkey: "
++ "sizeof(supported_init_<satype=%d>)[%d]/sizeof(struct supported)[%d]=%d.\n",
++ satype,
++ size,
++ (int)sizeof(struct supported),
++ (int)(size/sizeof(struct supported)));
++
++ for(i = 0; i < size / sizeof(struct supported); i++) {
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:init_pfkey: "
++ "i=%d inserting satype=%d exttype=%d id=%d ivlen=%d minbits=%d maxbits=%d.\n",
++ i,
++ satype,
++ supported[i].supported_alg_exttype,
++ supported[i].supported_alg_id,
++ supported[i].supported_alg_ivlen,
++ supported[i].supported_alg_minbits,
++ supported[i].supported_alg_maxbits);
++
++ error |= pfkey_list_insert_supported(&(supported[i]),
++ &(pfkey_supported_list[satype]));
++ }
++ return error;
++}
++
++DEBUG_NO_STATIC int
++supported_remove_all(int satype)
++{
++ int error = 0;
++ struct supported*supportedp;
++
++ while(pfkey_supported_list[satype]) {
++ supportedp = pfkey_supported_list[satype]->supportedp;
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:init_pfkey: "
++ "removing satype=%d exttype=%d id=%d ivlen=%d minbits=%d maxbits=%d.\n",
++ satype,
++ supportedp->supported_alg_exttype,
++ supportedp->supported_alg_id,
++ supportedp->supported_alg_ivlen,
++ supportedp->supported_alg_minbits,
++ supportedp->supported_alg_maxbits);
++
++ error |= pfkey_list_remove_supported(supportedp,
++ &(pfkey_supported_list[satype]));
++ }
++ return error;
++}
++
++int
++pfkey_init(void)
++{
++ int error = 0;
++ int i;
++
++ static struct supported supported_init_ah[] = {
++#ifdef CONFIG_IPSEC_AUTH_HMAC_MD5
++ {SADB_EXT_SUPPORTED_AUTH, SADB_AALG_MD5HMAC, 0, 128, 128},
++#endif /* CONFIG_IPSEC_AUTH_HMAC_MD5 */
++#ifdef CONFIG_IPSEC_AUTH_HMAC_SHA1
++ {SADB_EXT_SUPPORTED_AUTH, SADB_AALG_SHA1HMAC, 0, 160, 160}
++#endif /* CONFIG_IPSEC_AUTH_HMAC_SHA1 */
++ };
++ static struct supported supported_init_esp[] = {
++#ifdef CONFIG_IPSEC_AUTH_HMAC_MD5
++ {SADB_EXT_SUPPORTED_AUTH, SADB_AALG_MD5HMAC, 0, 128, 128},
++#endif /* CONFIG_IPSEC_AUTH_HMAC_MD5 */
++#ifdef CONFIG_IPSEC_AUTH_HMAC_SHA1
++ {SADB_EXT_SUPPORTED_AUTH, SADB_AALG_SHA1HMAC, 0, 160, 160},
++#endif /* CONFIG_IPSEC_AUTH_HMAC_SHA1 */
++#ifdef CONFIG_IPSEC_ENC_3DES
++ {SADB_EXT_SUPPORTED_ENCRYPT, SADB_EALG_3DESCBC, 64, 168, 168},
++#endif /* CONFIG_IPSEC_ENC_3DES */
++ };
++ static struct supported supported_init_ipip[] = {
++ {SADB_EXT_SUPPORTED_ENCRYPT, SADB_X_TALG_IPv4_in_IPv4, 0, 32, 32}
++#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
++ , {SADB_EXT_SUPPORTED_ENCRYPT, SADB_X_TALG_IPv6_in_IPv4, 0, 128, 32}
++ , {SADB_EXT_SUPPORTED_ENCRYPT, SADB_X_TALG_IPv4_in_IPv6, 0, 32, 128}
++ , {SADB_EXT_SUPPORTED_ENCRYPT, SADB_X_TALG_IPv6_in_IPv6, 0, 128, 128}
++#endif /* defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) */
++ };
++#ifdef CONFIG_IPSEC_IPCOMP
++ static struct supported supported_init_ipcomp[] = {
++ {SADB_EXT_SUPPORTED_ENCRYPT, SADB_X_CALG_DEFLATE, 0, 1, 1}
++ };
++#endif /* CONFIG_IPSEC_IPCOMP */
++
++#if 0
++ printk(KERN_INFO
++ "klips_info:pfkey_init: "
++ "FreeS/WAN: initialising PF_KEYv2 domain sockets.\n");
++#endif
++
++ for(i = SADB_SATYPE_UNSPEC; i <= SADB_SATYPE_MAX; i++) {
++ pfkey_registered_sockets[i] = NULL;
++ pfkey_supported_list[i] = NULL;
++ }
++
++ error |= supported_add_all(SADB_SATYPE_AH, supported_init_ah, sizeof(supported_init_ah));
++ error |= supported_add_all(SADB_SATYPE_ESP, supported_init_esp, sizeof(supported_init_esp));
++#ifdef CONFIG_IPSEC_IPCOMP
++ error |= supported_add_all(SADB_X_SATYPE_COMP, supported_init_ipcomp, sizeof(supported_init_ipcomp));
++#endif /* CONFIG_IPSEC_IPCOMP */
++ error |= supported_add_all(SADB_X_SATYPE_IPIP, supported_init_ipip, sizeof(supported_init_ipip));
++
++#ifdef NET_21
++ error |= sock_register(&pfkey_family_ops);
++#else /* NET_21 */
++ error |= sock_register(pfkey_proto_ops.family, &pfkey_proto_ops);
++#endif /* NET_21 */
++
++#ifdef CONFIG_PROC_FS
++# ifndef PROC_FS_2325
++# ifdef PROC_FS_21
++ error |= proc_register(proc_net, &proc_net_pfkey);
++ error |= proc_register(proc_net, &proc_net_pfkey_supported);
++ error |= proc_register(proc_net, &proc_net_pfkey_registered);
++# else /* PROC_FS_21 */
++ error |= proc_register_dynamic(&proc_net, &proc_net_pfkey);
++ error |= proc_register_dynamic(&proc_net, &proc_net_pfkey_supported);
++ error |= proc_register_dynamic(&proc_net, &proc_net_pfkey_registered);
++# endif /* PROC_FS_21 */
++# else /* !PROC_FS_2325 */
++ proc_net_create ("pf_key", 0, pfkey_get_info);
++ proc_net_create ("pf_key_supported", 0, pfkey_supported_get_info);
++ proc_net_create ("pf_key_registered", 0, pfkey_registered_get_info);
++# endif /* !PROC_FS_2325 */
++#endif /* CONFIG_PROC_FS */
++
++ return error;
++}
++
++int
++pfkey_cleanup(void)
++{
++ int error = 0;
++
++ printk(KERN_INFO "klips_info:pfkey_cleanup: "
++ "shutting down PF_KEY domain sockets.\n");
++#ifdef NET_21
++ error |= sock_unregister(PF_KEY);
++#else /* NET_21 */
++ error |= sock_unregister(pfkey_proto_ops.family);
++#endif /* NET_21 */
++
++ error |= supported_remove_all(SADB_SATYPE_AH);
++ error |= supported_remove_all(SADB_SATYPE_ESP);
++#ifdef CONFIG_IPSEC_IPCOMP
++ error |= supported_remove_all(SADB_X_SATYPE_COMP);
++#endif /* CONFIG_IPSEC_IPCOMP */
++ error |= supported_remove_all(SADB_X_SATYPE_IPIP);
++
++#ifdef CONFIG_PROC_FS
++# ifndef PROC_FS_2325
++ if (proc_net_unregister(proc_net_pfkey.low_ino) != 0)
++ printk("klips_debug:pfkey_cleanup: "
++ "cannot unregister /proc/net/pf_key\n");
++ if (proc_net_unregister(proc_net_pfkey_supported.low_ino) != 0)
++ printk("klips_debug:pfkey_cleanup: "
++ "cannot unregister /proc/net/pf_key_supported\n");
++ if (proc_net_unregister(proc_net_pfkey_registered.low_ino) != 0)
++ printk("klips_debug:pfkey_cleanup: "
++ "cannot unregister /proc/net/pf_key_registered\n");
++# else /* !PROC_FS_2325 */
++ proc_net_remove ("pf_key");
++ proc_net_remove ("pf_key_supported");
++ proc_net_remove ("pf_key_registered");
++# endif /* !PROC_FS_2325 */
++#endif /* CONFIG_PROC_FS */
++
++ /* other module unloading cleanup happens here */
++ return error;
++}
++
++#ifdef MODULE
++#if 0
++int
++init_module(void)
++{
++ pfkey_init();
++ return 0;
++}
++
++void
++cleanup_module(void)
++{
++ pfkey_cleanup();
++}
++#endif /* 0 */
++#else /* MODULE */
++void
++pfkey_proto_init(struct net_proto *pro)
++{
++ pfkey_init();
++}
++#endif /* MODULE */
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.81 2004/04/25 21:23:11 ken
++ * Pull in dhr's changes from FreeS/WAN 2.06
++ *
++ * Revision 1.80 2004/04/06 02:49:26 mcr
++ * pullup of algo code from alg-branch.
++ *
++ * Revision 1.79.4.1 2003/12/22 15:25:52 jjo
++ * . Merged algo-0.8.1-rc11-test1 into alg-branch
++ *
++ * Revision 1.79 2003/10/31 02:27:55 mcr
++ * pulled up port-selector patches and sa_id elimination.
++ *
++ * Revision 1.78.4.1 2003/10/29 01:30:41 mcr
++ * elimited "struct sa_id".
++ *
++ * Revision 1.78 2003/04/03 17:38:09 rgb
++ * Centralised ipsec_kfree_skb and ipsec_dev_{get,put}.
++ *
++ * Revision 1.77 2002/10/17 16:49:36 mcr
++ * sock->ops should reference the unwrapped options so that
++ * we get hacked in locking on SMP systems.
++ *
++ * Revision 1.76 2002/10/12 23:11:53 dhr
++ *
++ * [KenB + DHR] more 64-bit cleanup
++ *
++ * Revision 1.75 2002/09/20 05:01:57 rgb
++ * Added memory allocation debugging.
++ *
++ * Revision 1.74 2002/09/19 02:42:50 mcr
++ * do not define the pfkey_ops function for now.
++ *
++ * Revision 1.73 2002/09/17 17:29:23 mcr
++ * #if 0 out some dead code - pfkey_ops is never used as written.
++ *
++ * Revision 1.72 2002/07/24 18:44:54 rgb
++ * Type fiddling to tame ia64 compiler.
++ *
++ * Revision 1.71 2002/05/23 07:14:11 rgb
++ * Cleaned up %p variants to 0p%p for test suite cleanup.
++ *
++ * Revision 1.70 2002/04/24 07:55:32 mcr
++ * #include patches and Makefiles for post-reorg compilation.
++ *
++ * Revision 1.69 2002/04/24 07:36:33 mcr
++ * Moved from ./klips/net/ipsec/pfkey_v2.c,v
++ *
++ * Revision 1.68 2002/03/08 01:15:17 mcr
++ * put some internal structure only debug messages behind
++ * && sysctl_ipsec_debug_verbose.
++ *
++ * Revision 1.67 2002/01/29 17:17:57 mcr
++ * moved include of ipsec_param.h to after include of linux/kernel.h
++ * otherwise, it seems that some option that is set in ipsec_param.h
++ * screws up something subtle in the include path to kernel.h, and
++ * it complains on the snprintf() prototype.
++ *
++ * Revision 1.66 2002/01/29 04:00:54 mcr
++ * more excise of kversions.h header.
++ *
++ * Revision 1.65 2002/01/29 02:13:18 mcr
++ * introduction of ipsec_kversion.h means that include of
++ * ipsec_param.h must preceed any decisions about what files to
++ * include to deal with differences in kernel source.
++ *
++ * Revision 1.64 2001/11/26 09:23:51 rgb
++ * Merge MCR's ipsec_sa, eroute, proc and struct lifetime changes.
++ *
++ * Revision 1.61.2.1 2001/09/25 02:28:44 mcr
++ * cleaned up includes.
++ *
++ * Revision 1.63 2001/11/12 19:38:00 rgb
++ * Continue trying other sockets even if one fails and return only original
++ * error.
++ *
++ * Revision 1.62 2001/10/18 04:45:22 rgb
++ * 2.4.9 kernel deprecates linux/malloc.h in favour of linux/slab.h,
++ * lib/freeswan.h version macros moved to lib/kversions.h.
++ * Other compiler directive cleanups.
++ *
++ * Revision 1.61 2001/09/20 15:32:59 rgb
++ * Min/max cleanup.
++ *
++ * Revision 1.60 2001/06/14 19:35:12 rgb
++ * Update copyright date.
++ *
++ * Revision 1.59 2001/06/13 15:35:48 rgb
++ * Fixed #endif comments.
++ *
++ * Revision 1.58 2001/05/04 16:37:24 rgb
++ * Remove erroneous checking of return codes for proc_net_* in 2.4.
++ *
++ * Revision 1.57 2001/05/03 19:43:36 rgb
++ * Initialise error return variable.
++ * Check error return codes in startup and shutdown.
++ * Standardise on SENDERR() macro.
++ *
++ * Revision 1.56 2001/04/21 23:05:07 rgb
++ * Define out skb->used for 2.4 kernels.
++ *
++ * Revision 1.55 2001/02/28 05:03:28 rgb
++ * Clean up and rationalise startup messages.
++ *
++ * Revision 1.54 2001/02/27 22:24:55 rgb
++ * Re-formatting debug output (line-splitting, joining, 1arg/line).
++ * Check for satoa() return codes.
++ *
++ * Revision 1.53 2001/02/27 06:48:18 rgb
++ * Fixed pfkey socket unregister log message to reflect type and function.
++ *
++ * Revision 1.52 2001/02/26 22:34:38 rgb
++ * Fix error return code that was getting overwritten by the error return
++ * code of an upmsg.
++ *
++ * Revision 1.51 2001/01/30 23:42:47 rgb
++ * Allow pfkey msgs from pid other than user context required for ACQUIRE
++ * and subsequent ADD or UDATE.
++ *
++ * Revision 1.50 2001/01/23 20:22:59 rgb
++ * 2.4 fix to remove removed is_clone member.
++ *
++ * Revision 1.49 2000/11/06 04:33:47 rgb
++ * Changed non-exported functions to DEBUG_NO_STATIC.
++ *
++ * Revision 1.48 2000/09/29 19:47:41 rgb
++ * Update copyright.
++ *
++ * Revision 1.47 2000/09/22 04:23:04 rgb
++ * Added more debugging to pfkey_upmsg() call from pfkey_sendmsg() error.
++ *
++ * Revision 1.46 2000/09/21 04:20:44 rgb
++ * Fixed array size off-by-one error. (Thanks Svenning!)
++ *
++ * Revision 1.45 2000/09/20 04:01:26 rgb
++ * Changed static functions to DEBUG_NO_STATIC for revealing function names
++ * in oopsen.
++ *
++ * Revision 1.44 2000/09/19 00:33:17 rgb
++ * 2.0 fixes.
++ *
++ * Revision 1.43 2000/09/16 01:28:13 rgb
++ * Fixed use of 0 in p format warning.
++ *
++ * Revision 1.42 2000/09/16 01:09:41 rgb
++ * Fixed debug format warning for pointers that was expecting ints.
++ *
++ * Revision 1.41 2000/09/13 15:54:00 rgb
++ * Rewrote pfkey_get_info(), added pfkey_{supported,registered}_get_info().
++ * Moved supported algos add and remove to functions.
++ *
++ * Revision 1.40 2000/09/12 18:49:28 rgb
++ * Added IPIP tunnel and IPCOMP register support.
++ *
++ * Revision 1.39 2000/09/12 03:23:49 rgb
++ * Converted #if0 debugs to sysctl.
++ * Removed debug_pfkey initialisations that prevented no_debug loading or
++ * linking.
++ *
++ * Revision 1.38 2000/09/09 06:38:02 rgb
++ * Return positive errno in pfkey_reply error message.
++ *
++ * Revision 1.37 2000/09/08 19:19:09 rgb
++ * Change references from DEBUG_IPSEC to CONFIG_IPSEC_DEBUG.
++ * Clean-up of long-unused crud...
++ * Create pfkey error message on on failure.
++ * Give pfkey_list_{insert,remove}_{socket,supported}() some error
++ * checking.
++ *
++ * Revision 1.36 2000/09/01 18:49:38 rgb
++ * Reap experimental NET_21_ bits.
++ * Turned registered sockets list into an array of one list per satype.
++ * Remove references to deprecated sklist_{insert,remove}_socket.
++ * Removed leaking socket debugging code.
++ * Removed duplicate pfkey_insert_socket in pfkey_create.
++ * Removed all references to pfkey msg->msg_name, since it is not used for
++ * pfkey.
++ * Added a supported algorithms array lists, one per satype and registered
++ * existing algorithms.
++ * Fixed pfkey_list_{insert,remove}_{socket,support}() to allow change to
++ * list.
++ * Only send pfkey_expire() messages to sockets registered for that satype.
++ *
++ * Revision 1.35 2000/08/24 17:03:00 rgb
++ * Corrected message size error return code for PF_KEYv2.
++ * Removed downward error prohibition.
++ *
++ * Revision 1.34 2000/08/21 16:32:26 rgb
++ * Re-formatted for cosmetic consistency and readability.
++ *
++ * Revision 1.33 2000/08/20 21:38:24 rgb
++ * Added a pfkey_reply parameter to pfkey_msg_interp(). (Momchil)
++ * Extended the upward message initiation of pfkey_sendmsg(). (Momchil)
++ *
++ * Revision 1.32 2000/07/28 14:58:31 rgb
++ * Changed kfree_s to kfree, eliminating extra arg to fix 2.4.0-test5.
++ *
++ * Revision 1.31 2000/05/16 03:04:00 rgb
++ * Updates for 2.3.99pre8 from MB.
++ *
++ * Revision 1.30 2000/05/10 19:22:21 rgb
++ * Use sklist private functions for 2.3.xx compatibility.
++ *
++ * Revision 1.29 2000/03/22 16:17:03 rgb
++ * Fixed SOCKOPS_WRAPPED macro for SMP (MB).
++ *
++ * Revision 1.28 2000/02/21 19:30:45 rgb
++ * Removed references to pkt_bridged for 2.3.47 compatibility.
++ *
++ * Revision 1.27 2000/02/14 21:07:00 rgb
++ * Fixed /proc/net/pf-key legend spacing.
++ *
++ * Revision 1.26 2000/01/22 03:46:59 rgb
++ * Fixed pfkey error return mechanism so that we are able to free the
++ * local copy of the pfkey_msg, plugging a memory leak and silencing
++ * the bad object free complaints.
++ *
++ * Revision 1.25 2000/01/21 06:19:44 rgb
++ * Moved pfkey_list_remove_socket() calls to before MOD_USE_DEC_COUNT.
++ * Added debugging to pfkey_upmsg.
++ *
++ * Revision 1.24 2000/01/10 16:38:23 rgb
++ * MB fixups for 2.3.x.
++ *
++ * Revision 1.23 1999/12/09 23:22:16 rgb
++ * Added more instrumentation for debugging 2.0 socket
++ * selection/reading.
++ * Removed erroneous 2.0 wait==NULL check bug in select.
++ *
++ * Revision 1.22 1999/12/08 20:32:16 rgb
++ * Tidied up 2.0.xx support, after major pfkey work, eliminating
++ * msg->msg_name twiddling in the process, since it is not defined
++ * for PF_KEYv2.
++ *
++ * Revision 1.21 1999/12/01 22:17:19 rgb
++ * Set skb->dev to zero on new skb in case it is a reused skb.
++ * Added check for skb_put overflow and freeing to avoid upmsg on error.
++ * Added check for wrong pfkey version and freeing to avoid upmsg on
++ * error.
++ * Shut off content dumping in pfkey_destroy.
++ * Added debugging message for size of buffer allocated for upmsg.
++ *
++ * Revision 1.20 1999/11/27 12:11:00 rgb
++ * Minor clean-up, enabling quiet operation of pfkey if desired.
++ *
++ * Revision 1.19 1999/11/25 19:04:21 rgb
++ * Update proc_fs code for pfkey to use dynamic registration.
++ *
++ * Revision 1.18 1999/11/25 09:07:17 rgb
++ * Implemented SENDERR macro for propagating error codes.
++ * Fixed error return code bug.
++ *
++ * Revision 1.17 1999/11/23 23:07:20 rgb
++ * Change name of pfkey_msg_parser to pfkey_msg_interp since it no longer
++ * parses. (PJO)
++ * Sort out pfkey and freeswan headers, putting them in a library path.
++ *
++ * Revision 1.16 1999/11/20 22:00:22 rgb
++ * Moved socketlist type declarations and prototypes for shared use.
++ * Renamed reformatted and generically extended for use by other socket
++ * lists pfkey_{del,add}_open_socket to pfkey_list_{remove,insert}_socket.
++ *
++ * Revision 1.15 1999/11/18 04:15:09 rgb
++ * Make pfkey_data_ready temporarily available for 2.2.x testing.
++ * Clean up pfkey_destroy_socket() debugging statements.
++ * Add Peter Onion's code to send messages up to all listening sockets.
++ * Changed all occurrences of #include "../../../lib/freeswan.h"
++ * to #include <freeswan.h> which works due to -Ilibfreeswan in the
++ * klips/net/ipsec/Makefile.
++ * Replaced all kernel version macros to shorter, readable form.
++ * Added CONFIG_PROC_FS compiler directives in case it is shut off.
++ *
++ * Revision 1.14 1999/11/17 16:01:00 rgb
++ * Make pfkey_data_ready temporarily available for 2.2.x testing.
++ * Clean up pfkey_destroy_socket() debugging statements.
++ * Add Peter Onion's code to send messages up to all listening sockets.
++ * Changed #include "../../../lib/freeswan.h" to #include <freeswan.h>
++ * which works due to -Ilibfreeswan in the klips/net/ipsec/Makefile.
++ *
++ * Revision 1.13 1999/10/27 19:59:51 rgb
++ * Removed af_unix comments that are no longer relevant.
++ * Added debug prink statements.
++ * Added to the /proc output in pfkey_get_info.
++ * Made most functions non-static to enable oops tracing.
++ * Re-enable skb dequeueing and freeing.
++ * Fix skb_alloc() and skb_put() size bug in pfkey_upmsg().
++ *
++ * Revision 1.12 1999/10/26 17:05:42 rgb
++ * Complete re-ordering based on proto_ops structure order.
++ * Separated out proto_ops structures for 2.0.x and 2.2.x for clarity.
++ * Simplification to use built-in socket ops where possible for 2.2.x.
++ * Add shorter macros for compiler directives to visually clean-up.
++ * Add lots of sk skb dequeueing debugging statements.
++ * Added to the /proc output in pfkey_get_info.
++ *
++ * Revision 1.11 1999/09/30 02:55:10 rgb
++ * Bogus skb detection.
++ * Fix incorrect /proc/net/ipsec-eroute printk message.
++ *
++ * Revision 1.10 1999/09/21 15:22:13 rgb
++ * Temporary fix while I figure out the right way to destroy sockets.
++ *
++ * Revision 1.9 1999/07/08 19:19:44 rgb
++ * Fix pointer format warning.
++ * Fix missing member error under 2.0.xx kernels.
++ *
++ * Revision 1.8 1999/06/13 07:24:04 rgb
++ * Add more debugging.
++ *
++ * Revision 1.7 1999/06/10 05:24:17 rgb
++ * Clarified compiler directives.
++ * Renamed variables to reduce confusion.
++ * Used sklist_*_socket() kernel functions to simplify 2.2.x socket support.
++ * Added lots of sanity checking.
++ *
++ * Revision 1.6 1999/06/03 18:59:50 rgb
++ * More updates to 2.2.x socket support. Almost works, oops at end of call.
++ *
++ * Revision 1.5 1999/05/25 22:44:05 rgb
++ * Start fixing 2.2 sockets.
++ *
++ * Revision 1.4 1999/04/29 15:21:34 rgb
++ * Move log to the end of the file.
++ * Eliminate min/max redefinition in #include <net/tcp.h>.
++ * Correct path for pfkey #includes
++ * Standardise an error return method.
++ * Add debugging instrumentation.
++ * Move message type checking to pfkey_msg_parse().
++ * Add check for errno incorrectly set.
++ * Add check for valid PID.
++ * Add check for reserved illegally set.
++ * Add check for message out of bounds.
++ *
++ * Revision 1.3 1999/04/15 17:58:07 rgb
++ * Add RCSID labels.
++ *
++ * Revision 1.2 1999/04/15 15:37:26 rgb
++ * Forward check changes from POST1_00 branch.
++ *
++ * Revision 1.1.2.2 1999/04/13 20:37:12 rgb
++ * Header Title correction.
++ *
++ * Revision 1.1.2.1 1999/03/26 20:58:55 rgb
++ * Add pfkeyv2 support to KLIPS.
++ *
++ *
++ * RFC 2367
++ * PF_KEY_v2 Key Management API
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/net/ipsec/pfkey_v2_ext_process.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,922 @@
++/*
++ * @(#) RFC2367 PF_KEYv2 Key management API message parser
++ * Copyright (C) 1998-2003 Richard Guy Briggs.
++ * Copyright (C) 2004 Michael Richardson <mcr@xelerance.com>
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ * RCSID $Id$
++ */
++
++/*
++ * Template from klips/net/ipsec/ipsec/ipsec_netlink.c.
++ */
++
++char pfkey_v2_ext_process_c_version[] = "$Id$";
++
++#include <linux/config.h>
++#include <linux/version.h>
++#include <linux/kernel.h> /* printk() */
++
++#include "openswan/ipsec_param.h"
++
++#ifdef MALLOC_SLAB
++# include <linux/slab.h> /* kmalloc() */
++#else /* MALLOC_SLAB */
++# include <linux/malloc.h> /* kmalloc() */
++#endif /* MALLOC_SLAB */
++#include <linux/errno.h> /* error codes */
++#include <linux/types.h> /* size_t */
++#include <linux/interrupt.h> /* mark_bh */
++
++#include <linux/netdevice.h> /* struct device, and other headers */
++#include <linux/etherdevice.h> /* eth_type_trans */
++#include <linux/ip.h> /* struct iphdr */
++#include <linux/skbuff.h>
++
++#include <openswan.h>
++
++#include <crypto/des.h>
++
++#ifdef SPINLOCK
++# ifdef SPINLOCK_23
++# include <linux/spinlock.h> /* *lock* */
++# else /* SPINLOCK_23 */
++# include <asm/spinlock.h> /* *lock* */
++# endif /* SPINLOCK_23 */
++#endif /* SPINLOCK */
++#ifdef NET_21
++# include <asm/uaccess.h>
++# include <linux/in6.h>
++# define ip_chk_addr inet_addr_type
++# define IS_MYADDR RTN_LOCAL
++#endif
++#include <asm/checksum.h>
++#include <net/ip.h>
++#ifdef NETLINK_SOCK
++# include <linux/netlink.h>
++#else
++# include <net/netlink.h>
++#endif
++
++#include <linux/random.h> /* get_random_bytes() */
++
++#include "openswan/radij.h"
++#include "openswan/ipsec_encap.h"
++#include "openswan/ipsec_sa.h"
++
++#include "openswan/ipsec_radij.h"
++#include "openswan/ipsec_xform.h"
++#include "openswan/ipsec_ah.h"
++#include "openswan/ipsec_esp.h"
++#include "openswan/ipsec_tunnel.h"
++#include "openswan/ipsec_rcv.h"
++#include "openswan/ipcomp.h"
++
++#include <pfkeyv2.h>
++#include <pfkey.h>
++
++#include "openswan/ipsec_proto.h"
++#include "openswan/ipsec_alg.h"
++
++#define SENDERR(_x) do { error = -(_x); goto errlab; } while (0)
++
++int
++pfkey_sa_process(struct sadb_ext *pfkey_ext, struct pfkey_extracted_data* extr)
++{
++ struct sadb_sa *pfkey_sa = (struct sadb_sa *)pfkey_ext;
++ int error = 0;
++ struct ipsec_sa* ipsp;
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_sa_process: .\n");
++
++ if(!extr || !extr->ips) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_sa_process: "
++ "extr or extr->ips is NULL, fatal\n");
++ SENDERR(EINVAL);
++ }
++
++ switch(pfkey_ext->sadb_ext_type) {
++ case SADB_EXT_SA:
++ ipsp = extr->ips;
++ break;
++ case SADB_X_EXT_SA2:
++ if(extr->ips2 == NULL) {
++ extr->ips2 = ipsec_sa_alloc(&error); /* pass error var by pointer */
++ }
++ if(extr->ips2 == NULL) {
++ SENDERR(-error);
++ }
++ ipsp = extr->ips2;
++ break;
++ default:
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_sa_process: "
++ "invalid exttype=%d.\n",
++ pfkey_ext->sadb_ext_type);
++ SENDERR(EINVAL);
++ }
++
++ ipsp->ips_said.spi = pfkey_sa->sadb_sa_spi;
++ ipsp->ips_replaywin = pfkey_sa->sadb_sa_replay;
++ ipsp->ips_state = pfkey_sa->sadb_sa_state;
++ ipsp->ips_flags = pfkey_sa->sadb_sa_flags;
++ ipsp->ips_replaywin_lastseq = ipsp->ips_replaywin_bitmap = 0;
++ ipsp->ips_ref_rel = pfkey_sa->sadb_x_sa_ref;
++
++ switch(ipsp->ips_said.proto) {
++ case IPPROTO_AH:
++ ipsp->ips_authalg = pfkey_sa->sadb_sa_auth;
++ ipsp->ips_encalg = SADB_EALG_NONE;
++ break;
++ case IPPROTO_ESP:
++ ipsp->ips_authalg = pfkey_sa->sadb_sa_auth;
++ ipsp->ips_encalg = pfkey_sa->sadb_sa_encrypt;
++#ifdef CONFIG_IPSEC_ALG
++ ipsec_alg_sa_init(ipsp);
++#endif /* CONFIG_IPSEC_ALG */
++ break;
++ case IPPROTO_IPIP:
++ ipsp->ips_authalg = AH_NONE;
++ ipsp->ips_encalg = ESP_NONE;
++ break;
++#ifdef CONFIG_IPSEC_IPCOMP
++ case IPPROTO_COMP:
++ ipsp->ips_authalg = AH_NONE;
++ ipsp->ips_encalg = pfkey_sa->sadb_sa_encrypt;
++ break;
++#endif /* CONFIG_IPSEC_IPCOMP */
++ case IPPROTO_INT:
++ ipsp->ips_authalg = AH_NONE;
++ ipsp->ips_encalg = ESP_NONE;
++ break;
++ case 0:
++ break;
++ default:
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_sa_process: "
++ "unknown proto=%d.\n",
++ ipsp->ips_said.proto);
++ SENDERR(EINVAL);
++ }
++
++errlab:
++ return error;
++}
++
++int
++pfkey_lifetime_process(struct sadb_ext *pfkey_ext, struct pfkey_extracted_data* extr)
++{
++ int error = 0;
++ struct sadb_lifetime *pfkey_lifetime = (struct sadb_lifetime *)pfkey_ext;
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_lifetime_process: .\n");
++
++ if(!extr || !extr->ips) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_lifetime_process: "
++ "extr or extr->ips is NULL, fatal\n");
++ SENDERR(EINVAL);
++ }
++
++ switch(pfkey_lifetime->sadb_lifetime_exttype) {
++ case SADB_EXT_LIFETIME_CURRENT:
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_lifetime_process: "
++ "lifetime_current not supported yet.\n");
++ SENDERR(EINVAL);
++ break;
++ case SADB_EXT_LIFETIME_HARD:
++ ipsec_lifetime_update_hard(&extr->ips->ips_life.ipl_allocations,
++ pfkey_lifetime->sadb_lifetime_allocations);
++
++ ipsec_lifetime_update_hard(&extr->ips->ips_life.ipl_bytes,
++ pfkey_lifetime->sadb_lifetime_bytes);
++
++ ipsec_lifetime_update_hard(&extr->ips->ips_life.ipl_addtime,
++ pfkey_lifetime->sadb_lifetime_addtime);
++
++ ipsec_lifetime_update_hard(&extr->ips->ips_life.ipl_usetime,
++ pfkey_lifetime->sadb_lifetime_usetime);
++
++ break;
++
++ case SADB_EXT_LIFETIME_SOFT:
++ ipsec_lifetime_update_soft(&extr->ips->ips_life.ipl_allocations,
++ pfkey_lifetime->sadb_lifetime_allocations);
++
++ ipsec_lifetime_update_soft(&extr->ips->ips_life.ipl_bytes,
++ pfkey_lifetime->sadb_lifetime_bytes);
++
++ ipsec_lifetime_update_soft(&extr->ips->ips_life.ipl_addtime,
++ pfkey_lifetime->sadb_lifetime_addtime);
++
++ ipsec_lifetime_update_soft(&extr->ips->ips_life.ipl_usetime,
++ pfkey_lifetime->sadb_lifetime_usetime);
++
++ break;
++ default:
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_lifetime_process: "
++ "invalid exttype=%d.\n",
++ pfkey_ext->sadb_ext_type);
++ SENDERR(EINVAL);
++ }
++
++errlab:
++ return error;
++}
++
++int
++pfkey_address_process(struct sadb_ext *pfkey_ext, struct pfkey_extracted_data* extr)
++{
++ int error = 0;
++ int saddr_len = 0;
++ char ipaddr_txt[ADDRTOA_BUF];
++ unsigned char **sap;
++ unsigned short * portp = 0;
++ struct sadb_address *pfkey_address = (struct sadb_address *)pfkey_ext;
++ struct sockaddr* s = (struct sockaddr*)((char*)pfkey_address + sizeof(*pfkey_address));
++ struct ipsec_sa* ipsp;
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_address_process:\n");
++
++ if(!extr || !extr->ips) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_address_process: "
++ "extr or extr->ips is NULL, fatal\n");
++ SENDERR(EINVAL);
++ }
++
++ switch(s->sa_family) {
++ case AF_INET:
++ saddr_len = sizeof(struct sockaddr_in);
++ addrtoa(((struct sockaddr_in*)s)->sin_addr, 0, ipaddr_txt, sizeof(ipaddr_txt));
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_address_process: "
++ "found address family=%d, AF_INET, %s.\n",
++ s->sa_family,
++ ipaddr_txt);
++ break;
++#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
++ case AF_INET6:
++ saddr_len = sizeof(struct sockaddr_in6);
++ break;
++#endif /* defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) */
++ default:
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_address_process: "
++ "s->sa_family=%d not supported.\n",
++ s->sa_family);
++ SENDERR(EPFNOSUPPORT);
++ }
++
++ switch(pfkey_address->sadb_address_exttype) {
++ case SADB_EXT_ADDRESS_SRC:
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_address_process: "
++ "found src address.\n");
++ sap = (unsigned char **)&(extr->ips->ips_addr_s);
++ extr->ips->ips_addr_s_size = saddr_len;
++ break;
++ case SADB_EXT_ADDRESS_DST:
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_address_process: "
++ "found dst address.\n");
++ sap = (unsigned char **)&(extr->ips->ips_addr_d);
++ extr->ips->ips_addr_d_size = saddr_len;
++ break;
++ case SADB_EXT_ADDRESS_PROXY:
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_address_process: "
++ "found proxy address.\n");
++ sap = (unsigned char **)&(extr->ips->ips_addr_p);
++ extr->ips->ips_addr_p_size = saddr_len;
++ break;
++ case SADB_X_EXT_ADDRESS_DST2:
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_address_process: "
++ "found 2nd dst address.\n");
++ if(extr->ips2 == NULL) {
++ extr->ips2 = ipsec_sa_alloc(&error); /* pass error var by pointer */
++ }
++ if(extr->ips2 == NULL) {
++ SENDERR(-error);
++ }
++ sap = (unsigned char **)&(extr->ips2->ips_addr_d);
++ extr->ips2->ips_addr_d_size = saddr_len;
++ break;
++ case SADB_X_EXT_ADDRESS_SRC_FLOW:
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_address_process: "
++ "found src flow address.\n");
++ if(pfkey_alloc_eroute(&(extr->eroute)) == ENOMEM) {
++ SENDERR(ENOMEM);
++ }
++ sap = (unsigned char **)&(extr->eroute->er_eaddr.sen_ip_src);
++ portp = &(extr->eroute->er_eaddr.sen_sport);
++ break;
++ case SADB_X_EXT_ADDRESS_DST_FLOW:
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_address_process: "
++ "found dst flow address.\n");
++ if(pfkey_alloc_eroute(&(extr->eroute)) == ENOMEM) {
++ SENDERR(ENOMEM);
++ }
++ sap = (unsigned char **)&(extr->eroute->er_eaddr.sen_ip_dst);
++ portp = &(extr->eroute->er_eaddr.sen_dport);
++ break;
++ case SADB_X_EXT_ADDRESS_SRC_MASK:
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_address_process: "
++ "found src mask address.\n");
++ if(pfkey_alloc_eroute(&(extr->eroute)) == ENOMEM) {
++ SENDERR(ENOMEM);
++ }
++ sap = (unsigned char **)&(extr->eroute->er_emask.sen_ip_src);
++ portp = &(extr->eroute->er_emask.sen_sport);
++ break;
++ case SADB_X_EXT_ADDRESS_DST_MASK:
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_address_process: "
++ "found dst mask address.\n");
++ if(pfkey_alloc_eroute(&(extr->eroute)) == ENOMEM) {
++ SENDERR(ENOMEM);
++ }
++ sap = (unsigned char **)&(extr->eroute->er_emask.sen_ip_dst);
++ portp = &(extr->eroute->er_emask.sen_dport);
++ break;
++#ifdef NAT_TRAVERSAL
++ case SADB_X_EXT_NAT_T_OA:
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_address_process: "
++ "found NAT-OA address.\n");
++ sap = (unsigned char **)&(extr->ips->ips_natt_oa);
++ extr->ips->ips_natt_oa_size = saddr_len;
++ break;
++#endif
++ default:
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_address_process: "
++ "unrecognised ext_type=%d.\n",
++ pfkey_address->sadb_address_exttype);
++ SENDERR(EINVAL);
++ }
++
++ switch(pfkey_address->sadb_address_exttype) {
++ case SADB_EXT_ADDRESS_SRC:
++ case SADB_EXT_ADDRESS_DST:
++ case SADB_EXT_ADDRESS_PROXY:
++ case SADB_X_EXT_ADDRESS_DST2:
++#ifdef NAT_TRAVERSAL
++ case SADB_X_EXT_NAT_T_OA:
++#endif
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_address_process: "
++ "allocating %d bytes for saddr.\n",
++ saddr_len);
++ if(!(*sap = kmalloc(saddr_len, GFP_KERNEL))) {
++ SENDERR(ENOMEM);
++ }
++ memcpy(*sap, s, saddr_len);
++ break;
++ default:
++ if(s->sa_family != AF_INET) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_address_process: "
++ "s->sa_family=%d not supported.\n",
++ s->sa_family);
++ SENDERR(EPFNOSUPPORT);
++ }
++ (unsigned long)(*sap) = ((struct sockaddr_in*)s)->sin_addr.s_addr;
++ if (portp != 0)
++ *portp = ((struct sockaddr_in*)s)->sin_port;
++#ifdef CONFIG_IPSEC_DEBUG
++ if(extr->eroute) {
++ char buf1[64], buf2[64];
++ if (debug_pfkey) {
++ subnettoa(extr->eroute->er_eaddr.sen_ip_src,
++ extr->eroute->er_emask.sen_ip_src, 0, buf1, sizeof(buf1));
++ subnettoa(extr->eroute->er_eaddr.sen_ip_dst,
++ extr->eroute->er_emask.sen_ip_dst, 0, buf2, sizeof(buf2));
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_address_parse: "
++ "extr->eroute set to %s:%d->%s:%d\n",
++ buf1,
++ ntohs(extr->eroute->er_eaddr.sen_sport),
++ buf2,
++ ntohs(extr->eroute->er_eaddr.sen_dport));
++ }
++ }
++#endif /* CONFIG_IPSEC_DEBUG */
++ }
++
++ ipsp = extr->ips;
++ switch(pfkey_address->sadb_address_exttype) {
++ case SADB_X_EXT_ADDRESS_DST2:
++ ipsp = extr->ips2;
++ case SADB_EXT_ADDRESS_DST:
++ if(s->sa_family == AF_INET) {
++ ipsp->ips_said.dst.u.v4.sin_addr.s_addr = ((struct sockaddr_in*)(ipsp->ips_addr_d))->sin_addr.s_addr;
++ ipsp->ips_said.dst.u.v4.sin_family = AF_INET;
++ addrtoa(((struct sockaddr_in*)(ipsp->ips_addr_d))->sin_addr,
++ 0,
++ ipaddr_txt,
++ sizeof(ipaddr_txt));
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_address_process: "
++ "ips_said.dst set to %s.\n",
++ ipaddr_txt);
++ } else {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_address_process: "
++ "uh, ips_said.dst doesn't do address family=%d yet, said will be invalid.\n",
++ s->sa_family);
++ }
++ default:
++ break;
++ }
++
++ /* XXX check if port!=0 */
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_address_process: successful.\n");
++ errlab:
++ return error;
++}
++
++int
++pfkey_key_process(struct sadb_ext *pfkey_ext, struct pfkey_extracted_data* extr)
++{
++ int error = 0;
++ struct sadb_key *pfkey_key = (struct sadb_key *)pfkey_ext;
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_key_process: .\n");
++
++ if(!extr || !extr->ips) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_key_process: "
++ "extr or extr->ips is NULL, fatal\n");
++ SENDERR(EINVAL);
++ }
++
++ switch(pfkey_key->sadb_key_exttype) {
++ case SADB_EXT_KEY_AUTH:
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_key_process: "
++ "allocating %d bytes for authkey.\n",
++ DIVUP(pfkey_key->sadb_key_bits, 8));
++ if(!(extr->ips->ips_key_a = kmalloc(DIVUP(pfkey_key->sadb_key_bits, 8), GFP_KERNEL))) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_key_process: "
++ "memory allocation error.\n");
++ SENDERR(ENOMEM);
++ }
++ extr->ips->ips_key_bits_a = pfkey_key->sadb_key_bits;
++ extr->ips->ips_key_a_size = DIVUP(pfkey_key->sadb_key_bits, 8);
++ memcpy(extr->ips->ips_key_a,
++ (char*)pfkey_key + sizeof(struct sadb_key),
++ extr->ips->ips_key_a_size);
++ break;
++ case SADB_EXT_KEY_ENCRYPT: /* Key(s) */
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_key_process: "
++ "allocating %d bytes for enckey.\n",
++ DIVUP(pfkey_key->sadb_key_bits, 8));
++ if(!(extr->ips->ips_key_e = kmalloc(DIVUP(pfkey_key->sadb_key_bits, 8), GFP_KERNEL))) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_key_process: "
++ "memory allocation error.\n");
++ SENDERR(ENOMEM);
++ }
++ extr->ips->ips_key_bits_e = pfkey_key->sadb_key_bits;
++ extr->ips->ips_key_e_size = DIVUP(pfkey_key->sadb_key_bits, 8);
++ memcpy(extr->ips->ips_key_e,
++ (char*)pfkey_key + sizeof(struct sadb_key),
++ extr->ips->ips_key_e_size);
++ break;
++ default:
++ SENDERR(EINVAL);
++ }
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_key_process: "
++ "success.\n");
++errlab:
++ return error;
++}
++
++int
++pfkey_ident_process(struct sadb_ext *pfkey_ext, struct pfkey_extracted_data* extr)
++{
++ int error = 0;
++ struct sadb_ident *pfkey_ident = (struct sadb_ident *)pfkey_ext;
++ int data_len;
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_ident_process: .\n");
++
++ if(!extr || !extr->ips) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_ident_process: "
++ "extr or extr->ips is NULL, fatal\n");
++ SENDERR(EINVAL);
++ }
++
++ switch(pfkey_ident->sadb_ident_exttype) {
++ case SADB_EXT_IDENTITY_SRC:
++ data_len = pfkey_ident->sadb_ident_len * IPSEC_PFKEYv2_ALIGN - sizeof(struct sadb_ident);
++
++ extr->ips->ips_ident_s.type = pfkey_ident->sadb_ident_type;
++ extr->ips->ips_ident_s.id = pfkey_ident->sadb_ident_id;
++ extr->ips->ips_ident_s.len = pfkey_ident->sadb_ident_len;
++ if(data_len) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_ident_process: "
++ "allocating %d bytes for ident_s.\n",
++ data_len);
++ if(!(extr->ips->ips_ident_s.data
++ = kmalloc(data_len, GFP_KERNEL))) {
++ SENDERR(ENOMEM);
++ }
++ memcpy(extr->ips->ips_ident_s.data,
++ (char*)pfkey_ident + sizeof(struct sadb_ident),
++ data_len);
++ } else {
++ extr->ips->ips_ident_s.data = NULL;
++ }
++ break;
++ case SADB_EXT_IDENTITY_DST: /* Identity(ies) */
++ data_len = pfkey_ident->sadb_ident_len * IPSEC_PFKEYv2_ALIGN - sizeof(struct sadb_ident);
++
++ extr->ips->ips_ident_d.type = pfkey_ident->sadb_ident_type;
++ extr->ips->ips_ident_d.id = pfkey_ident->sadb_ident_id;
++ extr->ips->ips_ident_d.len = pfkey_ident->sadb_ident_len;
++ if(data_len) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_ident_process: "
++ "allocating %d bytes for ident_d.\n",
++ data_len);
++ if(!(extr->ips->ips_ident_d.data
++ = kmalloc(data_len, GFP_KERNEL))) {
++ SENDERR(ENOMEM);
++ }
++ memcpy(extr->ips->ips_ident_d.data,
++ (char*)pfkey_ident + sizeof(struct sadb_ident),
++ data_len);
++ } else {
++ extr->ips->ips_ident_d.data = NULL;
++ }
++ break;
++ default:
++ SENDERR(EINVAL);
++ }
++errlab:
++ return error;
++}
++
++int
++pfkey_sens_process(struct sadb_ext *pfkey_ext, struct pfkey_extracted_data* extr)
++{
++ int error = 0;
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_sens_process: "
++ "Sorry, I can't process exttype=%d yet.\n",
++ pfkey_ext->sadb_ext_type);
++ SENDERR(EINVAL); /* don't process these yet */
++ errlab:
++ return error;
++}
++
++int
++pfkey_prop_process(struct sadb_ext *pfkey_ext, struct pfkey_extracted_data* extr)
++{
++ int error = 0;
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_prop_process: "
++ "Sorry, I can't process exttype=%d yet.\n",
++ pfkey_ext->sadb_ext_type);
++ SENDERR(EINVAL); /* don't process these yet */
++
++ errlab:
++ return error;
++}
++
++int
++pfkey_supported_process(struct sadb_ext *pfkey_ext, struct pfkey_extracted_data* extr)
++{
++ int error = 0;
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_supported_process: "
++ "Sorry, I can't process exttype=%d yet.\n",
++ pfkey_ext->sadb_ext_type);
++ SENDERR(EINVAL); /* don't process these yet */
++
++errlab:
++ return error;
++}
++
++int
++pfkey_spirange_process(struct sadb_ext *pfkey_ext, struct pfkey_extracted_data* extr)
++{
++ int error = 0;
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_spirange_process: .\n");
++/* errlab: */
++ return error;
++}
++
++int
++pfkey_x_kmprivate_process(struct sadb_ext *pfkey_ext, struct pfkey_extracted_data* extr)
++{
++ int error = 0;
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_kmprivate_process: "
++ "Sorry, I can't process exttype=%d yet.\n",
++ pfkey_ext->sadb_ext_type);
++ SENDERR(EINVAL); /* don't process these yet */
++
++errlab:
++ return error;
++}
++
++int
++pfkey_x_satype_process(struct sadb_ext *pfkey_ext, struct pfkey_extracted_data* extr)
++{
++ int error = 0;
++ struct sadb_x_satype *pfkey_x_satype = (struct sadb_x_satype *)pfkey_ext;
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_satype_process: .\n");
++
++ if(!extr || !extr->ips) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_satype_process: "
++ "extr or extr->ips is NULL, fatal\n");
++ SENDERR(EINVAL);
++ }
++
++ if(extr->ips2 == NULL) {
++ extr->ips2 = ipsec_sa_alloc(&error); /* pass error var by pointer */
++ }
++ if(extr->ips2 == NULL) {
++ SENDERR(-error);
++ }
++ if(!(extr->ips2->ips_said.proto = satype2proto(pfkey_x_satype->sadb_x_satype_satype))) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_satype_process: "
++ "proto lookup from satype=%d failed.\n",
++ pfkey_x_satype->sadb_x_satype_satype);
++ SENDERR(EINVAL);
++ }
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_satype_process: "
++ "protocol==%d decoded from satype==%d(%s).\n",
++ extr->ips2->ips_said.proto,
++ pfkey_x_satype->sadb_x_satype_satype,
++ satype2name(pfkey_x_satype->sadb_x_satype_satype));
++
++errlab:
++ return error;
++}
++
++
++#ifdef CONFIG_IPSEC_NAT_TRAVERSAL
++int
++pfkey_x_nat_t_type_process(struct sadb_ext *pfkey_ext, struct pfkey_extracted_data* extr)
++{
++ int error = 0;
++ struct sadb_x_nat_t_type *pfkey_x_nat_t_type = (struct sadb_x_nat_t_type *)pfkey_ext;
++
++ if(!pfkey_x_nat_t_type) {
++ printk("klips_debug:pfkey_x_nat_t_type_process: "
++ "null pointer passed in\n");
++ SENDERR(EINVAL);
++ }
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_nat_t_type_process: %d.\n",
++ pfkey_x_nat_t_type->sadb_x_nat_t_type_type);
++
++ if(!extr || !extr->ips) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_nat_t_type_process: "
++ "extr or extr->ips is NULL, fatal\n");
++ SENDERR(EINVAL);
++ }
++
++ switch(pfkey_x_nat_t_type->sadb_x_nat_t_type_type) {
++ case ESPINUDP_WITH_NON_IKE: /* with Non-IKE (older version) */
++ case ESPINUDP_WITH_NON_ESP: /* with Non-ESP */
++
++ extr->ips->ips_natt_type = pfkey_x_nat_t_type->sadb_x_nat_t_type_type;
++ break;
++ default:
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_nat_t_type_process: "
++ "unknown type %d.\n",
++ pfkey_x_nat_t_type->sadb_x_nat_t_type_type);
++ SENDERR(EINVAL);
++ break;
++ }
++
++errlab:
++ return error;
++}
++
++int
++pfkey_x_nat_t_port_process(struct sadb_ext *pfkey_ext, struct pfkey_extracted_data* extr)
++{
++ int error = 0;
++ struct sadb_x_nat_t_port *pfkey_x_nat_t_port = (struct sadb_x_nat_t_port *)pfkey_ext;
++
++ if(!pfkey_x_nat_t_port) {
++ printk("klips_debug:pfkey_x_nat_t_port_process: "
++ "null pointer passed in\n");
++ SENDERR(EINVAL);
++ }
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_nat_t_port_process: %d/%d.\n",
++ pfkey_x_nat_t_port->sadb_x_nat_t_port_exttype,
++ pfkey_x_nat_t_port->sadb_x_nat_t_port_port);
++
++ if(!extr || !extr->ips) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_nat_t_type_process: "
++ "extr or extr->ips is NULL, fatal\n");
++ SENDERR(EINVAL);
++ }
++
++ switch(pfkey_x_nat_t_port->sadb_x_nat_t_port_exttype) {
++ case SADB_X_EXT_NAT_T_SPORT:
++ extr->ips->ips_natt_sport = pfkey_x_nat_t_port->sadb_x_nat_t_port_port;
++ break;
++ case SADB_X_EXT_NAT_T_DPORT:
++ extr->ips->ips_natt_dport = pfkey_x_nat_t_port->sadb_x_nat_t_port_port;
++ break;
++ default:
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_nat_t_port_process: "
++ "unknown exttype %d.\n",
++ pfkey_x_nat_t_port->sadb_x_nat_t_port_exttype);
++ SENDERR(EINVAL);
++ break;
++ }
++
++errlab:
++ return error;
++}
++#endif
++
++int
++pfkey_x_debug_process(struct sadb_ext *pfkey_ext, struct pfkey_extracted_data* extr)
++{
++ int error = 0;
++ struct sadb_x_debug *pfkey_x_debug = (struct sadb_x_debug *)pfkey_ext;
++
++ if(!pfkey_x_debug) {
++ printk("klips_debug:pfkey_x_debug_process: "
++ "null pointer passed in\n");
++ SENDERR(EINVAL);
++ }
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_debug_process: .\n");
++
++#ifdef CONFIG_IPSEC_DEBUG
++ if(pfkey_x_debug->sadb_x_debug_netlink >>
++ (sizeof(pfkey_x_debug->sadb_x_debug_netlink) * 8 - 1)) {
++ pfkey_x_debug->sadb_x_debug_netlink &=
++ ~(1 << (sizeof(pfkey_x_debug->sadb_x_debug_netlink) * 8 -1));
++ debug_tunnel |= pfkey_x_debug->sadb_x_debug_tunnel;
++ debug_netlink |= pfkey_x_debug->sadb_x_debug_netlink;
++ debug_xform |= pfkey_x_debug->sadb_x_debug_xform;
++ debug_eroute |= pfkey_x_debug->sadb_x_debug_eroute;
++ debug_spi |= pfkey_x_debug->sadb_x_debug_spi;
++ debug_radij |= pfkey_x_debug->sadb_x_debug_radij;
++ debug_esp |= pfkey_x_debug->sadb_x_debug_esp;
++ debug_ah |= pfkey_x_debug->sadb_x_debug_ah;
++ debug_rcv |= pfkey_x_debug->sadb_x_debug_rcv;
++ debug_pfkey |= pfkey_x_debug->sadb_x_debug_pfkey;
++#ifdef CONFIG_IPSEC_IPCOMP
++ sysctl_ipsec_debug_ipcomp |= pfkey_x_debug->sadb_x_debug_ipcomp;
++#endif /* CONFIG_IPSEC_IPCOMP */
++ sysctl_ipsec_debug_verbose |= pfkey_x_debug->sadb_x_debug_verbose;
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_debug_process: "
++ "set\n");
++ } else {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_debug_process: "
++ "unset\n");
++ debug_tunnel &= pfkey_x_debug->sadb_x_debug_tunnel;
++ debug_netlink &= pfkey_x_debug->sadb_x_debug_netlink;
++ debug_xform &= pfkey_x_debug->sadb_x_debug_xform;
++ debug_eroute &= pfkey_x_debug->sadb_x_debug_eroute;
++ debug_spi &= pfkey_x_debug->sadb_x_debug_spi;
++ debug_radij &= pfkey_x_debug->sadb_x_debug_radij;
++ debug_esp &= pfkey_x_debug->sadb_x_debug_esp;
++ debug_ah &= pfkey_x_debug->sadb_x_debug_ah;
++ debug_rcv &= pfkey_x_debug->sadb_x_debug_rcv;
++ debug_pfkey &= pfkey_x_debug->sadb_x_debug_pfkey;
++#ifdef CONFIG_IPSEC_IPCOMP
++ sysctl_ipsec_debug_ipcomp &= pfkey_x_debug->sadb_x_debug_ipcomp;
++#endif /* CONFIG_IPSEC_IPCOMP */
++ sysctl_ipsec_debug_verbose &= pfkey_x_debug->sadb_x_debug_verbose;
++ }
++#else /* CONFIG_IPSEC_DEBUG */
++ printk("klips_debug:pfkey_x_debug_process: "
++ "debugging not enabled\n");
++ SENDERR(EINVAL);
++#endif /* CONFIG_IPSEC_DEBUG */
++
++errlab:
++ return error;
++}
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.15 2004/04/06 02:49:26 mcr
++ * pullup of algo code from alg-branch.
++ *
++ * Revision 1.14 2004/02/03 03:13:59 mcr
++ * no longer #ifdef out NON_ESP mode. That was a mistake.
++ *
++ * Revision 1.13 2003/12/15 18:13:12 mcr
++ * when compiling with NAT traversal, don't assume that the
++ * kernel has been patched, unless CONFIG_IPSEC_NAT_NON_ESP
++ * is set.
++ *
++ * Revision 1.12.2.1 2003/12/22 15:25:52 jjo
++ * Merged algo-0.8.1-rc11-test1 into alg-branch
++ *
++ * Revision 1.12 2003/12/10 01:14:27 mcr
++ * NAT-traversal patches to KLIPS.
++ *
++ * Revision 1.11 2003/10/31 02:27:55 mcr
++ * pulled up port-selector patches and sa_id elimination.
++ *
++ * Revision 1.10.4.2 2003/10/29 01:30:41 mcr
++ * elimited "struct sa_id".
++ *
++ * Revision 1.10.4.1 2003/09/21 13:59:56 mcr
++ * pre-liminary X.509 patch - does not yet pass tests.
++ *
++ * Revision 1.10 2003/02/06 01:51:41 rgb
++ * Removed no longer relevant comment
++ *
++ * Revision 1.9 2003/01/30 02:32:44 rgb
++ *
++ * Transmit error code through to caller from callee for better diagnosis of problems.
++ *
++ * Revision 1.8 2002/12/13 22:42:22 mcr
++ * restored sa_ref code
++ *
++ * Revision 1.7 2002/12/13 22:40:48 mcr
++ * temporarily removed sadb_x_sa_ref reference for 2.xx
++ *
++ * Revision 1.6 2002/10/05 05:02:58 dhr
++ *
++ * C labels go on statements
++ *
++ * Revision 1.5 2002/09/20 15:41:08 rgb
++ * Switch from pfkey_alloc_ipsec_sa() to ipsec_sa_alloc().
++ * Added sadb_x_sa_ref to struct sadb_sa.
++ *
++ * Revision 1.4 2002/09/20 05:02:02 rgb
++ * Added memory allocation debugging.
++ *
++ * Revision 1.3 2002/07/24 18:44:54 rgb
++ * Type fiddling to tame ia64 compiler.
++ *
++ * Revision 1.2 2002/05/27 18:55:03 rgb
++ * Remove final vistiges of tdb references via IPSEC_KLIPS1_COMPAT.
++ *
++ * Revision 1.1 2002/05/14 02:33:51 rgb
++ * Moved all the extension processing functions to pfkey_v2_ext_process.c.
++ *
++ *
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/net/ipsec/pfkey_v2_parser.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,4018 @@
++/*
++ * @(#) RFC2367 PF_KEYv2 Key management API message parser
++ * Copyright (C) 1999, 2000, 2001 Richard Guy Briggs <rgb@freeswan.org>
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ * RCSID $Id$
++ */
++
++/*
++ * Template from klips/net/ipsec/ipsec/ipsec_netlink.c.
++ */
++
++char pfkey_v2_parser_c_version[] = "$Id$";
++
++#include <linux/config.h>
++#include <linux/version.h>
++#include <linux/kernel.h> /* printk() */
++
++#include "openswan/ipsec_param.h"
++
++#ifdef MALLOC_SLAB
++# include <linux/slab.h> /* kmalloc() */
++#else /* MALLOC_SLAB */
++# include <linux/malloc.h> /* kmalloc() */
++#endif /* MALLOC_SLAB */
++#include <linux/errno.h> /* error codes */
++#include <linux/types.h> /* size_t */
++#include <linux/interrupt.h> /* mark_bh */
++
++#include <linux/netdevice.h> /* struct device, and other headers */
++#include <linux/etherdevice.h> /* eth_type_trans */
++#include <linux/ip.h> /* struct iphdr */
++#include <linux/skbuff.h>
++
++#include <openswan.h>
++
++#include <crypto/des.h>
++
++#ifdef SPINLOCK
++# ifdef SPINLOCK_23
++# include <linux/spinlock.h> /* *lock* */
++# else /* SPINLOCK_23 */
++# include <asm/spinlock.h> /* *lock* */
++# endif /* SPINLOCK_23 */
++#endif /* SPINLOCK */
++#ifdef NET_21
++# include <asm/uaccess.h>
++# include <linux/in6.h>
++# define ip_chk_addr inet_addr_type
++# define IS_MYADDR RTN_LOCAL
++#endif
++#include <asm/checksum.h>
++#include <net/ip.h>
++#ifdef NETLINK_SOCK
++# include <linux/netlink.h>
++#else
++# include <net/netlink.h>
++#endif
++
++#include <linux/random.h> /* get_random_bytes() */
++
++#include "openswan/radij.h"
++#include "openswan/ipsec_encap.h"
++#include "openswan/ipsec_sa.h"
++
++#include "openswan/ipsec_radij.h"
++#include "openswan/ipsec_xform.h"
++#include "openswan/ipsec_ah.h"
++#include "openswan/ipsec_esp.h"
++#include "openswan/ipsec_tunnel.h"
++#include "openswan/ipsec_rcv.h"
++#include "openswan/ipcomp.h"
++
++#include <pfkeyv2.h>
++#include <pfkey.h>
++
++#include "openswan/ipsec_proto.h"
++#include "openswan/ipsec_alg.h"
++
++
++#define SENDERR(_x) do { error = -(_x); goto errlab; } while (0)
++
++struct sklist_t {
++ struct socket *sk;
++ struct sklist_t* next;
++} pfkey_sklist_head, *pfkey_sklist, *pfkey_sklist_prev;
++
++__u32 pfkey_msg_seq = 0;
++
++
++#if 0
++#define DUMP_SAID dump_said(&extr->ips->ips_said, __LINE__)
++#define DUMP_SAID2 dump_said(&extr.ips->ips_said, __LINE__)
++static void dump_said(ip_said *s, int line)
++{
++ char msa[SATOT_BUF];
++ size_t msa_len;
++
++ msa_len = satot(s, 0, msa, sizeof(msa));
++
++ printk("line: %d msa: %s\n", line, msa);
++}
++#endif
++
++
++int
++pfkey_alloc_eroute(struct eroute** eroute)
++{
++ int error = 0;
++ if(*eroute) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_alloc_eroute: "
++ "eroute struct already allocated\n");
++ SENDERR(EEXIST);
++ }
++
++ if((*eroute = kmalloc(sizeof(**eroute), GFP_ATOMIC) ) == NULL) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_alloc_eroute: "
++ "memory allocation error\n");
++ SENDERR(ENOMEM);
++ }
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_alloc_eroute: "
++ "allocating %lu bytes for an eroute at 0p%p\n",
++ (unsigned long) sizeof(**eroute), *eroute);
++
++ memset((caddr_t)*eroute, 0, sizeof(**eroute));
++ (*eroute)->er_eaddr.sen_len =
++ (*eroute)->er_emask.sen_len = sizeof(struct sockaddr_encap);
++ (*eroute)->er_eaddr.sen_family =
++ (*eroute)->er_emask.sen_family = AF_ENCAP;
++ (*eroute)->er_eaddr.sen_type = SENT_IP4;
++ (*eroute)->er_emask.sen_type = 255;
++ (*eroute)->er_pid = 0;
++ (*eroute)->er_count = 0;
++ (*eroute)->er_lasttime = jiffies/HZ;
++
++ errlab:
++ return(error);
++}
++
++DEBUG_NO_STATIC int
++pfkey_x_protocol_process(struct sadb_ext *pfkey_ext,
++ struct pfkey_extracted_data *extr)
++{
++ int error = 0;
++ struct sadb_protocol * p = (struct sadb_protocol *)pfkey_ext;
++
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_x_protocol_process: %p\n", extr);
++
++ if (extr == 0) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_protocol_process:"
++ "extr is NULL, fatal\n");
++ SENDERR(EINVAL);
++ }
++ if (extr->eroute == 0) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_protocol_process:"
++ "extr->eroute is NULL, fatal\n");
++ SENDERR(EINVAL);
++ }
++
++ extr->eroute->er_eaddr.sen_proto = p->sadb_protocol_proto;
++ extr->eroute->er_emask.sen_proto = p->sadb_protocol_proto ? ~0:0;
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_protocol_process: protocol = %d.\n",
++ p->sadb_protocol_proto);
++ errlab:
++ return error;
++}
++
++DEBUG_NO_STATIC int
++pfkey_ipsec_sa_init(struct ipsec_sa *ipsp, struct sadb_ext **extensions)
++{
++ int i;
++ int error = 0;
++ char sa[SATOT_BUF];
++ size_t sa_len;
++ char ipaddr_txt[ADDRTOA_BUF];
++ char ipaddr2_txt[ADDRTOA_BUF];
++#if defined (CONFIG_IPSEC_AUTH_HMAC_MD5) || defined (CONFIG_IPSEC_AUTH_HMAC_SHA1)
++ unsigned char kb[AHMD596_BLKLEN];
++#endif
++#ifdef CONFIG_IPSEC_ALG
++ struct ipsec_alg_enc *ixt_e = NULL;
++ struct ipsec_alg_auth *ixt_a = NULL;
++#endif /* CONFIG_IPSEC_ALG */
++
++ if(ipsp == NULL) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_ipsec_sa_init: "
++ "ipsp is NULL, fatal\n");
++ SENDERR(EINVAL);
++ }
++
++ sa_len = satot(&ipsp->ips_said, 0, sa, sizeof(sa));
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_ipsec_sa_init: "
++ "(pfkey defined) called for SA:%s\n",
++ sa_len ? sa : " (error)");
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_ipsec_sa_init: "
++ "calling init routine of %s%s%s\n",
++ IPS_XFORM_NAME(ipsp));
++
++ switch(ipsp->ips_said.proto) {
++
++#ifdef CONFIG_IPSEC_IPIP
++ case IPPROTO_IPIP: {
++ addrtoa(((struct sockaddr_in*)(ipsp->ips_addr_s))->sin_addr,
++ 0,
++ ipaddr_txt, sizeof(ipaddr_txt));
++ addrtoa(((struct sockaddr_in*)(ipsp->ips_addr_d))->sin_addr,
++ 0,
++ ipaddr2_txt, sizeof(ipaddr_txt));
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_ipsec_sa_init: "
++ "(pfkey defined) IPIP ipsec_sa set for %s->%s.\n",
++ ipaddr_txt,
++ ipaddr2_txt);
++ }
++ break;
++#endif /* !CONFIG_IPSEC_IPIP */
++#ifdef CONFIG_IPSEC_AH
++ case IPPROTO_AH:
++ switch(ipsp->ips_authalg) {
++# ifdef CONFIG_IPSEC_AUTH_HMAC_MD5
++ case AH_MD5: {
++ unsigned char *akp;
++ unsigned int aks;
++ MD5_CTX *ictx;
++ MD5_CTX *octx;
++
++ if(ipsp->ips_key_bits_a != (AHMD596_KLEN * 8)) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_ipsec_sa_init: "
++ "incorrect key size: %d bits -- must be %d bits\n"/*octets (bytes)\n"*/,
++ ipsp->ips_key_bits_a, AHMD596_KLEN * 8);
++ SENDERR(EINVAL);
++ }
++
++# if KLIPS_DIVULGE_HMAC_KEY
++ KLIPS_PRINT(debug_pfkey && sysctl_ipsec_debug_verbose,
++ "klips_debug:pfkey_ipsec_sa_init: "
++ "hmac md5-96 key is 0x%08x %08x %08x %08x\n",
++ ntohl(*(((__u32 *)ipsp->ips_key_a)+0)),
++ ntohl(*(((__u32 *)ipsp->ips_key_a)+1)),
++ ntohl(*(((__u32 *)ipsp->ips_key_a)+2)),
++ ntohl(*(((__u32 *)ipsp->ips_key_a)+3)));
++# endif /* KLIPS_DIVULGE_HMAC_KEY */
++
++ ipsp->ips_auth_bits = AHMD596_ALEN * 8;
++
++ /* save the pointer to the key material */
++ akp = ipsp->ips_key_a;
++ aks = ipsp->ips_key_a_size;
++
++ KLIPS_PRINT(debug_pfkey && sysctl_ipsec_debug_verbose,
++ "klips_debug:pfkey_ipsec_sa_init: "
++ "allocating %lu bytes for md5_ctx.\n",
++ (unsigned long) sizeof(struct md5_ctx));
++ if((ipsp->ips_key_a = (caddr_t)
++ kmalloc(sizeof(struct md5_ctx), GFP_ATOMIC)) == NULL) {
++ ipsp->ips_key_a = akp;
++ SENDERR(ENOMEM);
++ }
++ ipsp->ips_key_a_size = sizeof(struct md5_ctx);
++
++ for (i = 0; i < DIVUP(ipsp->ips_key_bits_a, 8); i++) {
++ kb[i] = akp[i] ^ HMAC_IPAD;
++ }
++ for (; i < AHMD596_BLKLEN; i++) {
++ kb[i] = HMAC_IPAD;
++ }
++
++ ictx = &(((struct md5_ctx*)(ipsp->ips_key_a))->ictx);
++ MD5Init(ictx);
++ MD5Update(ictx, kb, AHMD596_BLKLEN);
++
++ for (i = 0; i < AHMD596_BLKLEN; i++) {
++ kb[i] ^= (HMAC_IPAD ^ HMAC_OPAD);
++ }
++
++ octx = &(((struct md5_ctx*)(ipsp->ips_key_a))->octx);
++ MD5Init(octx);
++ MD5Update(octx, kb, AHMD596_BLKLEN);
++
++# if KLIPS_DIVULGE_HMAC_KEY
++ KLIPS_PRINT(debug_pfkey && sysctl_ipsec_debug_verbose,
++ "klips_debug:pfkey_ipsec_sa_init: "
++ "MD5 ictx=0x%08x %08x %08x %08x octx=0x%08x %08x %08x %08x\n",
++ ((__u32*)ictx)[0],
++ ((__u32*)ictx)[1],
++ ((__u32*)ictx)[2],
++ ((__u32*)ictx)[3],
++ ((__u32*)octx)[0],
++ ((__u32*)octx)[1],
++ ((__u32*)octx)[2],
++ ((__u32*)octx)[3] );
++# endif /* KLIPS_DIVULGE_HMAC_KEY */
++
++ /* zero key buffer -- paranoid */
++ memset(akp, 0, aks);
++ kfree(akp);
++ }
++ break;
++# endif /* CONFIG_IPSEC_AUTH_HMAC_MD5 */
++# ifdef CONFIG_IPSEC_AUTH_HMAC_SHA1
++ case AH_SHA: {
++ unsigned char *akp;
++ unsigned int aks;
++ SHA1_CTX *ictx;
++ SHA1_CTX *octx;
++
++ if(ipsp->ips_key_bits_a != (AHSHA196_KLEN * 8)) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_ipsec_sa_init: "
++ "incorrect key size: %d bits -- must be %d bits\n"/*octets (bytes)\n"*/,
++ ipsp->ips_key_bits_a, AHSHA196_KLEN * 8);
++ SENDERR(EINVAL);
++ }
++
++# if KLIPS_DIVULGE_HMAC_KEY
++ KLIPS_PRINT(debug_pfkey && sysctl_ipsec_debug_verbose,
++ "klips_debug:pfkey_ipsec_sa_init: "
++ "hmac sha1-96 key is 0x%08x %08x %08x %08x\n",
++ ntohl(*(((__u32 *)ipsp->ips_key_a)+0)),
++ ntohl(*(((__u32 *)ipsp->ips_key_a)+1)),
++ ntohl(*(((__u32 *)ipsp->ips_key_a)+2)),
++ ntohl(*(((__u32 *)ipsp->ips_key_a)+3)));
++# endif /* KLIPS_DIVULGE_HMAC_KEY */
++
++ ipsp->ips_auth_bits = AHSHA196_ALEN * 8;
++
++ /* save the pointer to the key material */
++ akp = ipsp->ips_key_a;
++ aks = ipsp->ips_key_a_size;
++
++ KLIPS_PRINT(debug_pfkey && sysctl_ipsec_debug_verbose,
++ "klips_debug:pfkey_ipsec_sa_init: "
++ "allocating %lu bytes for sha1_ctx.\n",
++ (unsigned long) sizeof(struct sha1_ctx));
++ if((ipsp->ips_key_a = (caddr_t)
++ kmalloc(sizeof(struct sha1_ctx), GFP_ATOMIC)) == NULL) {
++ ipsp->ips_key_a = akp;
++ SENDERR(ENOMEM);
++ }
++ ipsp->ips_key_a_size = sizeof(struct sha1_ctx);
++
++ for (i = 0; i < DIVUP(ipsp->ips_key_bits_a, 8); i++) {
++ kb[i] = akp[i] ^ HMAC_IPAD;
++ }
++ for (; i < AHMD596_BLKLEN; i++) {
++ kb[i] = HMAC_IPAD;
++ }
++
++ ictx = &(((struct sha1_ctx*)(ipsp->ips_key_a))->ictx);
++ SHA1Init(ictx);
++ SHA1Update(ictx, kb, AHSHA196_BLKLEN);
++
++ for (i = 0; i < AHSHA196_BLKLEN; i++) {
++ kb[i] ^= (HMAC_IPAD ^ HMAC_OPAD);
++ }
++
++ octx = &(((struct sha1_ctx*)(ipsp->ips_key_a))->octx);
++ SHA1Init(octx);
++ SHA1Update(octx, kb, AHSHA196_BLKLEN);
++
++# if KLIPS_DIVULGE_HMAC_KEY
++ KLIPS_PRINT(debug_pfkey && sysctl_ipsec_debug_verbose,
++ "klips_debug:pfkey_ipsec_sa_init: "
++ "SHA1 ictx=0x%08x %08x %08x %08x octx=0x%08x %08x %08x %08x\n",
++ ((__u32*)ictx)[0],
++ ((__u32*)ictx)[1],
++ ((__u32*)ictx)[2],
++ ((__u32*)ictx)[3],
++ ((__u32*)octx)[0],
++ ((__u32*)octx)[1],
++ ((__u32*)octx)[2],
++ ((__u32*)octx)[3] );
++# endif /* KLIPS_DIVULGE_HMAC_KEY */
++ /* zero key buffer -- paranoid */
++ memset(akp, 0, aks);
++ kfree(akp);
++ }
++ break;
++# endif /* CONFIG_IPSEC_AUTH_HMAC_SHA1 */
++ default:
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_ipsec_sa_init: "
++ "authalg=%d support not available in the kernel",
++ ipsp->ips_authalg);
++ SENDERR(EINVAL);
++ }
++ break;
++#endif /* CONFIG_IPSEC_AH */
++#ifdef CONFIG_IPSEC_ESP
++ case IPPROTO_ESP: {
++#if defined (CONFIG_IPSEC_AUTH_HMAC_MD5) || defined (CONFIG_IPSEC_AUTH_HMAC_SHA1)
++ unsigned char *akp;
++ unsigned int aks;
++#endif
++#if defined (CONFIG_IPSEC_ENC_3DES)
++ unsigned char *ekp;
++ unsigned int eks;
++#endif
++
++ ipsp->ips_iv_size = 0;
++#ifdef CONFIG_IPSEC_ALG
++ if ((ixt_e=ipsp->ips_alg_enc)) {
++ ipsp->ips_iv_size = ixt_e->ixt_ivlen/8;
++ } else
++#endif /* CONFIG_IPSEC_ALG */
++ switch(ipsp->ips_encalg) {
++# ifdef CONFIG_IPSEC_ENC_3DES
++ case ESP_3DES:
++# endif /* CONFIG_IPSEC_ENC_3DES */
++# if defined(CONFIG_IPSEC_ENC_3DES)
++ KLIPS_PRINT(debug_pfkey && sysctl_ipsec_debug_verbose,
++ "klips_debug:pfkey_ipsec_sa_init: "
++ "allocating %u bytes for iv.\n",
++ EMT_ESPDES_IV_SZ);
++ if((ipsp->ips_iv = (caddr_t)
++ kmalloc((ipsp->ips_iv_size = EMT_ESPDES_IV_SZ), GFP_ATOMIC)) == NULL) {
++ SENDERR(ENOMEM);
++ }
++ prng_bytes(&ipsec_prng, (char *)ipsp->ips_iv, EMT_ESPDES_IV_SZ);
++ ipsp->ips_iv_bits = ipsp->ips_iv_size * 8;
++ ipsp->ips_iv_size = EMT_ESPDES_IV_SZ;
++ break;
++# endif /* defined(CONFIG_IPSEC_ENC_3DES) */
++ case ESP_NONE:
++ break;
++ default:
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_ipsec_sa_init: "
++ "encalg=%d support not available in the kernel",
++ ipsp->ips_encalg);
++ SENDERR(EINVAL);
++ }
++
++ /* Create IV */
++ if (ipsp->ips_iv_size) {
++ if((ipsp->ips_iv = (caddr_t)
++ kmalloc(ipsp->ips_iv_size, GFP_ATOMIC)) == NULL) {
++ SENDERR(ENOMEM);
++ }
++ prng_bytes(&ipsec_prng, (char *)ipsp->ips_iv, ipsp->ips_iv_size);
++ ipsp->ips_iv_bits = ipsp->ips_iv_size * 8;
++ }
++
++#ifdef CONFIG_IPSEC_ALG
++ if (ixt_e) {
++ if ((error=ipsec_alg_enc_key_create(ipsp)) < 0)
++ SENDERR(-error);
++ } else
++#endif /* CONFIG_IPSEC_ALG */
++ switch(ipsp->ips_encalg) {
++# ifdef CONFIG_IPSEC_ENC_3DES
++ case ESP_3DES:
++ if(ipsp->ips_key_bits_e != (EMT_ESP3DES_KEY_SZ * 8)) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_ipsec_sa_init: "
++ "incorrect encryption key size: %d bits -- must be %d bits\n"/*octets (bytes)\n"*/,
++ ipsp->ips_key_bits_e, EMT_ESP3DES_KEY_SZ * 8);
++ SENDERR(EINVAL);
++ }
++
++ /* save encryption key pointer */
++ ekp = ipsp->ips_key_e;
++ eks = ipsp->ips_key_e_size;
++
++ KLIPS_PRINT(debug_pfkey && sysctl_ipsec_debug_verbose,
++ "klips_debug:pfkey_ipsec_sa_init: "
++ "allocating %lu bytes for 3des.\n",
++ (unsigned long) (3 * sizeof(struct des_eks)));
++ if((ipsp->ips_key_e = (caddr_t)
++ kmalloc(3 * sizeof(struct des_eks), GFP_ATOMIC)) == NULL) {
++ ipsp->ips_key_e = ekp;
++ SENDERR(ENOMEM);
++ }
++ ipsp->ips_key_e_size = 3 * sizeof(struct des_eks);
++
++ for(i = 0; i < 3; i++) {
++#if KLIPS_DIVULGE_CYPHER_KEY
++ KLIPS_PRINT(debug_pfkey && sysctl_ipsec_debug_verbose,
++ "klips_debug:pfkey_ipsec_sa_init: "
++ "3des key %d/3 is 0x%08x%08x\n",
++ i + 1,
++ ntohl(*((__u32 *)ekp + i * 2)),
++ ntohl(*((__u32 *)ekp + i * 2 + 1)));
++# endif
++#if KLIPS_FIXES_DES_PARITY
++ /* force parity */
++ des_set_odd_parity((des_cblock *)(ekp + EMT_ESPDES_KEY_SZ * i));
++#endif
++ error = des_set_key((des_cblock *)(ekp + EMT_ESPDES_KEY_SZ * i),
++ ((struct des_eks *)(ipsp->ips_key_e))[i].ks);
++ if (error == -1)
++ printk("klips_debug:pfkey_ipsec_sa_init: "
++ "parity error in des key %d/3\n",
++ i + 1);
++ else if (error == -2)
++ printk("klips_debug:pfkey_ipsec_sa_init: "
++ "illegal weak des key %d/3\n", i + 1);
++ if (error) {
++ memset(ekp, 0, eks);
++ kfree(ekp);
++ SENDERR(EINVAL);
++ }
++ }
++
++ /* paranoid */
++ memset(ekp, 0, eks);
++ kfree(ekp);
++ break;
++# endif /* CONFIG_IPSEC_ENC_3DES */
++ case ESP_NONE:
++ break;
++ default:
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_ipsec_sa_init: "
++ "encalg=%d support not available in the kernel",
++ ipsp->ips_encalg);
++ SENDERR(EINVAL);
++ }
++
++#ifdef CONFIG_IPSEC_ALG
++ if ((ixt_a=ipsp->ips_alg_auth)) {
++ if ((error=ipsec_alg_auth_key_create(ipsp)) < 0)
++ SENDERR(-error);
++ } else
++#endif /* CONFIG_IPSEC_ALG */
++
++ switch(ipsp->ips_authalg) {
++# ifdef CONFIG_IPSEC_AUTH_HMAC_MD5
++ case AH_MD5: {
++ MD5_CTX *ictx;
++ MD5_CTX *octx;
++
++ if(ipsp->ips_key_bits_a != (AHMD596_KLEN * 8)) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_ipsec_sa_init: "
++ "incorrect authorisation key size: %d bits -- must be %d bits\n"/*octets (bytes)\n"*/,
++ ipsp->ips_key_bits_a,
++ AHMD596_KLEN * 8);
++ SENDERR(EINVAL);
++ }
++
++# if KLIPS_DIVULGE_HMAC_KEY
++ KLIPS_PRINT(debug_pfkey && sysctl_ipsec_debug_verbose,
++ "klips_debug:pfkey_ipsec_sa_init: "
++ "hmac md5-96 key is 0x%08x %08x %08x %08x\n",
++ ntohl(*(((__u32 *)(ipsp->ips_key_a))+0)),
++ ntohl(*(((__u32 *)(ipsp->ips_key_a))+1)),
++ ntohl(*(((__u32 *)(ipsp->ips_key_a))+2)),
++ ntohl(*(((__u32 *)(ipsp->ips_key_a))+3)));
++# endif /* KLIPS_DIVULGE_HMAC_KEY */
++ ipsp->ips_auth_bits = AHMD596_ALEN * 8;
++
++ /* save the pointer to the key material */
++ akp = ipsp->ips_key_a;
++ aks = ipsp->ips_key_a_size;
++
++ KLIPS_PRINT(debug_pfkey && sysctl_ipsec_debug_verbose,
++ "klips_debug:pfkey_ipsec_sa_init: "
++ "allocating %lu bytes for md5_ctx.\n",
++ (unsigned long) sizeof(struct md5_ctx));
++ if((ipsp->ips_key_a = (caddr_t)
++ kmalloc(sizeof(struct md5_ctx), GFP_ATOMIC)) == NULL) {
++ ipsp->ips_key_a = akp;
++ SENDERR(ENOMEM);
++ }
++ ipsp->ips_key_a_size = sizeof(struct md5_ctx);
++
++ for (i = 0; i < DIVUP(ipsp->ips_key_bits_a, 8); i++) {
++ kb[i] = akp[i] ^ HMAC_IPAD;
++ }
++ for (; i < AHMD596_BLKLEN; i++) {
++ kb[i] = HMAC_IPAD;
++ }
++
++ ictx = &(((struct md5_ctx*)(ipsp->ips_key_a))->ictx);
++ MD5Init(ictx);
++ MD5Update(ictx, kb, AHMD596_BLKLEN);
++
++ for (i = 0; i < AHMD596_BLKLEN; i++) {
++ kb[i] ^= (HMAC_IPAD ^ HMAC_OPAD);
++ }
++
++ octx = &(((struct md5_ctx*)(ipsp->ips_key_a))->octx);
++ MD5Init(octx);
++ MD5Update(octx, kb, AHMD596_BLKLEN);
++
++# if KLIPS_DIVULGE_HMAC_KEY
++ KLIPS_PRINT(debug_pfkey && sysctl_ipsec_debug_verbose,
++ "klips_debug:pfkey_ipsec_sa_init: "
++ "MD5 ictx=0x%08x %08x %08x %08x octx=0x%08x %08x %08x %08x\n",
++ ((__u32*)ictx)[0],
++ ((__u32*)ictx)[1],
++ ((__u32*)ictx)[2],
++ ((__u32*)ictx)[3],
++ ((__u32*)octx)[0],
++ ((__u32*)octx)[1],
++ ((__u32*)octx)[2],
++ ((__u32*)octx)[3] );
++# endif /* KLIPS_DIVULGE_HMAC_KEY */
++ /* paranoid */
++ memset(akp, 0, aks);
++ kfree(akp);
++ break;
++ }
++# endif /* CONFIG_IPSEC_AUTH_HMAC_MD5 */
++# ifdef CONFIG_IPSEC_AUTH_HMAC_SHA1
++ case AH_SHA: {
++ SHA1_CTX *ictx;
++ SHA1_CTX *octx;
++
++ if(ipsp->ips_key_bits_a != (AHSHA196_KLEN * 8)) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_ipsec_sa_init: "
++ "incorrect authorisation key size: %d bits -- must be %d bits\n"/*octets (bytes)\n"*/,
++ ipsp->ips_key_bits_a,
++ AHSHA196_KLEN * 8);
++ SENDERR(EINVAL);
++ }
++
++# if KLIPS_DIVULGE_HMAC_KEY
++ KLIPS_PRINT(debug_pfkey && sysctl_ipsec_debug_verbose,
++ "klips_debug:pfkey_ipsec_sa_init: "
++ "hmac sha1-96 key is 0x%08x %08x %08x %08x\n",
++ ntohl(*(((__u32 *)ipsp->ips_key_a)+0)),
++ ntohl(*(((__u32 *)ipsp->ips_key_a)+1)),
++ ntohl(*(((__u32 *)ipsp->ips_key_a)+2)),
++ ntohl(*(((__u32 *)ipsp->ips_key_a)+3)));
++# endif /* KLIPS_DIVULGE_HMAC_KEY */
++ ipsp->ips_auth_bits = AHSHA196_ALEN * 8;
++
++ /* save the pointer to the key material */
++ akp = ipsp->ips_key_a;
++ aks = ipsp->ips_key_a_size;
++
++ KLIPS_PRINT(debug_pfkey && sysctl_ipsec_debug_verbose,
++ "klips_debug:pfkey_ipsec_sa_init: "
++ "allocating %lu bytes for sha1_ctx.\n",
++ (unsigned long) sizeof(struct sha1_ctx));
++ if((ipsp->ips_key_a = (caddr_t)
++ kmalloc(sizeof(struct sha1_ctx), GFP_ATOMIC)) == NULL) {
++ ipsp->ips_key_a = akp;
++ SENDERR(ENOMEM);
++ }
++ ipsp->ips_key_a_size = sizeof(struct sha1_ctx);
++
++ for (i = 0; i < DIVUP(ipsp->ips_key_bits_a, 8); i++) {
++ kb[i] = akp[i] ^ HMAC_IPAD;
++ }
++ for (; i < AHMD596_BLKLEN; i++) {
++ kb[i] = HMAC_IPAD;
++ }
++
++ ictx = &(((struct sha1_ctx*)(ipsp->ips_key_a))->ictx);
++ SHA1Init(ictx);
++ SHA1Update(ictx, kb, AHSHA196_BLKLEN);
++
++ for (i = 0; i < AHSHA196_BLKLEN; i++) {
++ kb[i] ^= (HMAC_IPAD ^ HMAC_OPAD);
++ }
++
++ octx = &((struct sha1_ctx*)(ipsp->ips_key_a))->octx;
++ SHA1Init(octx);
++ SHA1Update(octx, kb, AHSHA196_BLKLEN);
++
++# if KLIPS_DIVULGE_HMAC_KEY
++ KLIPS_PRINT(debug_pfkey && sysctl_ipsec_debug_verbose,
++ "klips_debug:pfkey_ipsec_sa_init: "
++ "SHA1 ictx=0x%08x %08x %08x %08x octx=0x%08x %08x %08x %08x\n",
++ ((__u32*)ictx)[0],
++ ((__u32*)ictx)[1],
++ ((__u32*)ictx)[2],
++ ((__u32*)ictx)[3],
++ ((__u32*)octx)[0],
++ ((__u32*)octx)[1],
++ ((__u32*)octx)[2],
++ ((__u32*)octx)[3] );
++# endif /* KLIPS_DIVULGE_HMAC_KEY */
++ memset(akp, 0, aks);
++ kfree(akp);
++ break;
++ }
++# endif /* CONFIG_IPSEC_AUTH_HMAC_SHA1 */
++ case AH_NONE:
++ break;
++ default:
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_ipsec_sa_init: "
++ "authalg=%d support not available in the kernel.\n",
++ ipsp->ips_authalg);
++ SENDERR(EINVAL);
++ }
++ }
++ break;
++#endif /* !CONFIG_IPSEC_ESP */
++#ifdef CONFIG_IPSEC_IPCOMP
++ case IPPROTO_COMP:
++ ipsp->ips_comp_adapt_tries = 0;
++ ipsp->ips_comp_adapt_skip = 0;
++ ipsp->ips_comp_ratio_cbytes = 0;
++ ipsp->ips_comp_ratio_dbytes = 0;
++ break;
++#endif /* CONFIG_IPSEC_IPCOMP */
++ default:
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_ipsec_sa_init: "
++ "proto=%d unknown.\n",
++ ipsp->ips_said.proto);
++ SENDERR(EINVAL);
++ }
++
++ errlab:
++ return(error);
++}
++
++
++int
++pfkey_safe_build(int error, struct sadb_ext *extensions[SADB_MAX+1])
++{
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_safe_build: "
++ "error=%d\n",
++ error);
++ if (!error) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_safe_build:"
++ "success.\n");
++ return 1;
++ } else {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_safe_build:"
++ "caught error %d\n",
++ error);
++ pfkey_extensions_free(extensions);
++ return 0;
++ }
++}
++
++
++DEBUG_NO_STATIC int
++pfkey_getspi_parse(struct sock *sk, struct sadb_ext **extensions, struct pfkey_extracted_data* extr)
++{
++ int error = 0;
++ ipsec_spi_t minspi = htonl(256), maxspi = htonl(-1L);
++ int found_avail = 0;
++ struct ipsec_sa *ipsq;
++ char sa[SATOT_BUF];
++ size_t sa_len;
++ struct sadb_ext *extensions_reply[SADB_EXT_MAX+1];
++ struct sadb_msg *pfkey_reply = NULL;
++ struct socket_list *pfkey_socketsp;
++ uint8_t satype = ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_satype;
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_getspi_parse: .\n");
++
++ pfkey_extensions_init(extensions_reply);
++
++ if(extr == NULL || extr->ips == NULL) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_getspi_parse: "
++ "error, extr or extr->ipsec_sa pointer NULL\n");
++ SENDERR(EINVAL);
++ }
++
++ if(extensions[SADB_EXT_SPIRANGE]) {
++ minspi = ((struct sadb_spirange *)extensions[SADB_EXT_SPIRANGE])->sadb_spirange_min;
++ maxspi = ((struct sadb_spirange *)extensions[SADB_EXT_SPIRANGE])->sadb_spirange_max;
++ }
++
++ if(maxspi == minspi) {
++ extr->ips->ips_said.spi = maxspi;
++ ipsq = ipsec_sa_getbyid(&(extr->ips->ips_said));
++ if(ipsq != NULL) {
++ sa_len = satot(&extr->ips->ips_said, 0, sa, sizeof(sa));
++ ipsec_sa_put(ipsq);
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_getspi_parse: "
++ "EMT_GETSPI found an old ipsec_sa for SA: %s, delete it first.\n",
++ sa_len ? sa : " (error)");
++ SENDERR(EEXIST);
++ } else {
++ found_avail = 1;
++ }
++ } else {
++ int i = 0;
++ __u32 rand_val;
++ __u32 spi_diff;
++ while( ( i < (spi_diff = (ntohl(maxspi) - ntohl(minspi)))) && !found_avail ) {
++ prng_bytes(&ipsec_prng, (char *) &(rand_val),
++ ( (spi_diff < (2^8)) ? 1 :
++ ( (spi_diff < (2^16)) ? 2 :
++ ( (spi_diff < (2^24)) ? 3 :
++ 4 ) ) ) );
++ extr->ips->ips_said.spi = htonl(ntohl(minspi) +
++ (rand_val %
++ (spi_diff + 1)));
++ i++;
++ ipsq = ipsec_sa_getbyid(&(extr->ips->ips_said));
++ if(ipsq == NULL) {
++ found_avail = 1;
++ } else {
++ ipsec_sa_put(ipsq);
++ }
++ }
++ }
++
++ sa_len = satot(&extr->ips->ips_said, 0, sa, sizeof(sa));
++
++ if (!found_avail) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_getspi_parse: "
++ "found an old ipsec_sa for SA: %s, delete it first.\n",
++ sa_len ? sa : " (error)");
++ SENDERR(EEXIST);
++ }
++
++ if(ip_chk_addr((unsigned long)extr->ips->ips_said.dst.u.v4.sin_addr.s_addr) == IS_MYADDR) {
++ extr->ips->ips_flags |= EMT_INBOUND;
++ }
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_getspi_parse: "
++ "existing ipsec_sa not found (this is good) for SA: %s, %s-bound, allocating.\n",
++ sa_len ? sa : " (error)",
++ extr->ips->ips_flags & EMT_INBOUND ? "in" : "out");
++
++ /* XXX extr->ips->ips_rcvif = &(enc_softc[em->em_if].enc_if);*/
++ extr->ips->ips_rcvif = NULL;
++ extr->ips->ips_life.ipl_addtime.ipl_count = jiffies/HZ;
++
++ extr->ips->ips_state = SADB_SASTATE_LARVAL;
++
++ if(!extr->ips->ips_life.ipl_allocations.ipl_count) {
++ extr->ips->ips_life.ipl_allocations.ipl_count += 1;
++ }
++
++ if(!(pfkey_safe_build(error = pfkey_msg_hdr_build(&extensions_reply[0],
++ SADB_GETSPI,
++ satype,
++ 0,
++ ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_seq,
++ ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_pid),
++ extensions_reply)
++ && pfkey_safe_build(error = pfkey_sa_ref_build(&extensions_reply[SADB_EXT_SA],
++ SADB_EXT_SA,
++ extr->ips->ips_said.spi,
++ 0,
++ SADB_SASTATE_LARVAL,
++ 0,
++ 0,
++ 0,
++ extr->ips->ips_ref),
++ extensions_reply)
++ && pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_EXT_ADDRESS_SRC],
++ SADB_EXT_ADDRESS_SRC,
++ 0, /*extr->ips->ips_said.proto,*/
++ 0,
++ extr->ips->ips_addr_s),
++ extensions_reply)
++ && pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_EXT_ADDRESS_DST],
++ SADB_EXT_ADDRESS_DST,
++ 0, /*extr->ips->ips_said.proto,*/
++ 0,
++ extr->ips->ips_addr_d),
++ extensions_reply) )) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_getspi_parse: "
++ "failed to build the getspi reply message extensions\n");
++ goto errlab;
++ }
++
++ if((error = pfkey_msg_build(&pfkey_reply, extensions_reply, EXT_BITS_OUT))) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_getspi_parse: "
++ "failed to build the getspi reply message\n");
++ SENDERR(-error);
++ }
++ for(pfkey_socketsp = pfkey_open_sockets;
++ pfkey_socketsp;
++ pfkey_socketsp = pfkey_socketsp->next) {
++ if((error = pfkey_upmsg(pfkey_socketsp->socketp, pfkey_reply))) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_getspi_parse: "
++ "sending up getspi reply message for satype=%d(%s) to socket=0p%p failed with error=%d.\n",
++ satype,
++ satype2name(satype),
++ pfkey_socketsp->socketp,
++ error);
++ SENDERR(-error);
++ }
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_getspi_parse: "
++ "sending up getspi reply message for satype=%d(%s) to socket=0p%p succeeded.\n",
++ satype,
++ satype2name(satype),
++ pfkey_socketsp->socketp);
++ }
++
++ if((error = ipsec_sa_add(extr->ips))) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_getspi_parse: "
++ "failed to add the larval SA=%s with error=%d.\n",
++ sa_len ? sa : " (error)",
++ error);
++ SENDERR(-error);
++ }
++ extr->ips = NULL;
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_getspi_parse: "
++ "successful for SA: %s\n",
++ sa_len ? sa : " (error)");
++
++ errlab:
++ if (pfkey_reply) {
++ pfkey_msg_free(&pfkey_reply);
++ }
++ pfkey_extensions_free(extensions_reply);
++ return error;
++}
++
++DEBUG_NO_STATIC int
++pfkey_update_parse(struct sock *sk, struct sadb_ext **extensions, struct pfkey_extracted_data* extr)
++{
++ int error = 0;
++ struct ipsec_sa* ipsq;
++ char sa[SATOT_BUF];
++ size_t sa_len;
++ struct sadb_ext *extensions_reply[SADB_EXT_MAX+1];
++ struct sadb_msg *pfkey_reply = NULL;
++ struct socket_list *pfkey_socketsp;
++ uint8_t satype = ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_satype;
++#ifdef CONFIG_IPSEC_NAT_TRAVERSAL
++ struct ipsec_sa *nat_t_ips_saved = NULL;
++#endif
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_update_parse: .\n");
++
++ pfkey_extensions_init(extensions_reply);
++
++ if(((struct sadb_sa*)extensions[SADB_EXT_SA])->sadb_sa_state != SADB_SASTATE_MATURE) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_update_parse: "
++ "error, sa_state=%d must be MATURE=%d\n",
++ ((struct sadb_sa*)extensions[SADB_EXT_SA])->sadb_sa_state,
++ SADB_SASTATE_MATURE);
++ SENDERR(EINVAL);
++ }
++
++ if(extr == NULL || extr->ips == NULL) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_update_parse: "
++ "error, extr or extr->ips pointer NULL\n");
++ SENDERR(EINVAL);
++ }
++
++ sa_len = satot(&extr->ips->ips_said, 0, sa, sizeof(sa));
++
++ spin_lock_bh(&tdb_lock);
++
++ ipsq = ipsec_sa_getbyid(&(extr->ips->ips_said));
++ if (ipsq == NULL) {
++ spin_unlock_bh(&tdb_lock);
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_update_parse: "
++ "reserved ipsec_sa for SA: %s not found. Call SADB_GETSPI first or call SADB_ADD instead.\n",
++ sa_len ? sa : " (error)");
++ SENDERR(ENOENT);
++ }
++
++ if(ip_chk_addr((unsigned long)extr->ips->ips_said.dst.u.v4.sin_addr.s_addr) == IS_MYADDR) {
++ extr->ips->ips_flags |= EMT_INBOUND;
++ }
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_update_parse: "
++ "existing ipsec_sa found (this is good) for SA: %s, %s-bound, updating.\n",
++ sa_len ? sa : " (error)",
++ extr->ips->ips_flags & EMT_INBOUND ? "in" : "out");
++
++#ifdef CONFIG_IPSEC_NAT_TRAVERSAL
++ if (extr->ips->ips_natt_sport || extr->ips->ips_natt_dport) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_update_parse: only updating NAT-T ports "
++ "(%u:%u -> %u:%u)\n",
++ ipsq->ips_natt_sport, ipsq->ips_natt_dport,
++ extr->ips->ips_natt_sport, extr->ips->ips_natt_dport);
++
++ if (extr->ips->ips_natt_sport) {
++ ipsq->ips_natt_sport = extr->ips->ips_natt_sport;
++ if (ipsq->ips_addr_s->sa_family == AF_INET) {
++ ((struct sockaddr_in *)(ipsq->ips_addr_s))->sin_port = htons(extr->ips->ips_natt_sport);
++ }
++ }
++
++ if (extr->ips->ips_natt_dport) {
++ ipsq->ips_natt_dport = extr->ips->ips_natt_dport;
++ if (ipsq->ips_addr_d->sa_family == AF_INET) {
++ ((struct sockaddr_in *)(ipsq->ips_addr_d))->sin_port = htons(extr->ips->ips_natt_dport);
++ }
++ }
++
++ nat_t_ips_saved = extr->ips;
++ extr->ips = ipsq;
++ }
++ else {
++#endif
++
++ /* XXX extr->ips->ips_rcvif = &(enc_softc[em->em_if].enc_if);*/
++ extr->ips->ips_rcvif = NULL;
++ if ((error = pfkey_ipsec_sa_init(extr->ips, extensions))) {
++ ipsec_sa_put(ipsq);
++ spin_unlock_bh(&tdb_lock);
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_update_parse: "
++ "not successful for SA: %s, deleting.\n",
++ sa_len ? sa : " (error)");
++ SENDERR(-error);
++ }
++
++ extr->ips->ips_life.ipl_addtime.ipl_count = ipsq->ips_life.ipl_addtime.ipl_count;
++ ipsec_sa_put(ipsq);
++ if((error = ipsec_sa_delchain(ipsq))) {
++ spin_unlock_bh(&tdb_lock);
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_update_parse: "
++ "error=%d, trouble deleting intermediate ipsec_sa for SA=%s.\n",
++ error,
++ sa_len ? sa : " (error)");
++ SENDERR(-error);
++ }
++#ifdef CONFIG_IPSEC_NAT_TRAVERSAL
++ }
++#endif
++
++ spin_unlock_bh(&tdb_lock);
++
++ if(!(pfkey_safe_build(error = pfkey_msg_hdr_build(&extensions_reply[0],
++ SADB_UPDATE,
++ satype,
++ 0,
++ ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_seq,
++ ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_pid),
++ extensions_reply)
++ && pfkey_safe_build(error = pfkey_sa_ref_build(&extensions_reply[SADB_EXT_SA],
++ SADB_EXT_SA,
++ extr->ips->ips_said.spi,
++ extr->ips->ips_replaywin,
++ extr->ips->ips_state,
++ extr->ips->ips_authalg,
++ extr->ips->ips_encalg,
++ extr->ips->ips_flags,
++ extr->ips->ips_ref),
++ extensions_reply)
++ /* The 3 lifetime extentions should only be sent if non-zero. */
++ && (extensions[SADB_EXT_LIFETIME_HARD]
++ ? pfkey_safe_build(error = pfkey_lifetime_build(&extensions_reply[SADB_EXT_LIFETIME_HARD],
++ SADB_EXT_LIFETIME_HARD,
++ extr->ips->ips_life.ipl_allocations.ipl_hard,
++ extr->ips->ips_life.ipl_bytes.ipl_hard,
++ extr->ips->ips_life.ipl_addtime.ipl_hard,
++ extr->ips->ips_life.ipl_usetime.ipl_hard,
++ extr->ips->ips_life.ipl_packets.ipl_hard),
++ extensions_reply) : 1)
++ && (extensions[SADB_EXT_LIFETIME_SOFT]
++ ? pfkey_safe_build(error = pfkey_lifetime_build(&extensions_reply[SADB_EXT_LIFETIME_SOFT],
++ SADB_EXT_LIFETIME_SOFT,
++ extr->ips->ips_life.ipl_allocations.ipl_count,
++ extr->ips->ips_life.ipl_bytes.ipl_count,
++ extr->ips->ips_life.ipl_addtime.ipl_count,
++ extr->ips->ips_life.ipl_usetime.ipl_count,
++ extr->ips->ips_life.ipl_packets.ipl_count),
++ extensions_reply) : 1)
++ && (extr->ips->ips_life.ipl_allocations.ipl_count
++ || extr->ips->ips_life.ipl_bytes.ipl_count
++ || extr->ips->ips_life.ipl_addtime.ipl_count
++ || extr->ips->ips_life.ipl_usetime.ipl_count
++ || extr->ips->ips_life.ipl_packets.ipl_count
++
++ ? pfkey_safe_build(error = pfkey_lifetime_build(&extensions_reply[SADB_EXT_LIFETIME_CURRENT],
++ SADB_EXT_LIFETIME_CURRENT,
++ extr->ips->ips_life.ipl_allocations.ipl_count,
++ extr->ips->ips_life.ipl_bytes.ipl_count,
++ extr->ips->ips_life.ipl_addtime.ipl_count,
++ extr->ips->ips_life.ipl_usetime.ipl_count,
++ extr->ips->ips_life.ipl_packets.ipl_count),
++ extensions_reply) : 1)
++ && pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_EXT_ADDRESS_SRC],
++ SADB_EXT_ADDRESS_SRC,
++ 0, /*extr->ips->ips_said.proto,*/
++ 0,
++ extr->ips->ips_addr_s),
++ extensions_reply)
++ && pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_EXT_ADDRESS_DST],
++ SADB_EXT_ADDRESS_DST,
++ 0, /*extr->ips->ips_said.proto,*/
++ 0,
++ extr->ips->ips_addr_d),
++ extensions_reply)
++ && (extr->ips->ips_ident_s.data
++ ? pfkey_safe_build(error = pfkey_ident_build(&extensions_reply[SADB_EXT_IDENTITY_SRC],
++ SADB_EXT_IDENTITY_SRC,
++ extr->ips->ips_ident_s.type,
++ extr->ips->ips_ident_s.id,
++ extr->ips->ips_ident_s.len,
++ extr->ips->ips_ident_s.data),
++ extensions_reply) : 1)
++ && (extr->ips->ips_ident_d.data
++ ? pfkey_safe_build(error = pfkey_ident_build(&extensions_reply[SADB_EXT_IDENTITY_DST],
++ SADB_EXT_IDENTITY_DST,
++ extr->ips->ips_ident_d.type,
++ extr->ips->ips_ident_d.id,
++ extr->ips->ips_ident_d.len,
++ extr->ips->ips_ident_d.data),
++ extensions_reply) : 1)
++#if 0
++ /* FIXME: This won't work yet because I have not finished
++ it. */
++ && (extr->ips->ips_sens_
++ ? pfkey_safe_build(error = pfkey_sens_build(&extensions_reply[SADB_EXT_SENSITIVITY],
++ extr->ips->ips_sens_dpd,
++ extr->ips->ips_sens_sens_level,
++ extr->ips->ips_sens_sens_len,
++ extr->ips->ips_sens_sens_bitmap,
++ extr->ips->ips_sens_integ_level,
++ extr->ips->ips_sens_integ_len,
++ extr->ips->ips_sens_integ_bitmap),
++ extensions_reply) : 1)
++#endif
++ )) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_update_parse: "
++ "failed to build the update reply message extensions\n");
++ SENDERR(-error);
++ }
++
++ if((error = pfkey_msg_build(&pfkey_reply, extensions_reply, EXT_BITS_OUT))) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_update_parse: "
++ "failed to build the update reply message\n");
++ SENDERR(-error);
++ }
++ for(pfkey_socketsp = pfkey_open_sockets;
++ pfkey_socketsp;
++ pfkey_socketsp = pfkey_socketsp->next) {
++ if((error = pfkey_upmsg(pfkey_socketsp->socketp, pfkey_reply))) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_update_parse: "
++ "sending up update reply message for satype=%d(%s) to socket=0p%p failed with error=%d.\n",
++ satype,
++ satype2name(satype),
++ pfkey_socketsp->socketp,
++ error);
++ SENDERR(-error);
++ }
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_update_parse: "
++ "sending up update reply message for satype=%d(%s) to socket=0p%p succeeded.\n",
++ satype,
++ satype2name(satype),
++ pfkey_socketsp->socketp);
++ }
++
++#ifdef CONFIG_IPSEC_NAT_TRAVERSAL
++ if (nat_t_ips_saved) {
++ /**
++ * As we _really_ update existing SA, we keep tdbq and need to delete
++ * parsed ips (nat_t_ips_saved, was extr->ips).
++ *
++ * goto errlab with extr->ips = nat_t_ips_saved will free it.
++ */
++
++ extr->ips = nat_t_ips_saved;
++
++ error = 0;
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_update_parse (NAT-T ports): "
++ "successful for SA: %s\n",
++ sa_len ? sa : " (error)");
++
++ goto errlab;
++ }
++#endif
++
++ if((error = ipsec_sa_add(extr->ips))) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_update_parse: "
++ "failed to update the mature SA=%s with error=%d.\n",
++ sa_len ? sa : " (error)",
++ error);
++ SENDERR(-error);
++ }
++ extr->ips = NULL;
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_update_parse: "
++ "successful for SA: %s\n",
++ sa_len ? sa : " (error)");
++
++ errlab:
++ if (pfkey_reply) {
++ pfkey_msg_free(&pfkey_reply);
++ }
++ pfkey_extensions_free(extensions_reply);
++ return error;
++}
++
++DEBUG_NO_STATIC int
++pfkey_add_parse(struct sock *sk, struct sadb_ext **extensions, struct pfkey_extracted_data* extr)
++{
++ int error = 0;
++ struct ipsec_sa* ipsq;
++ char sa[SATOT_BUF];
++ size_t sa_len;
++ struct sadb_ext *extensions_reply[SADB_EXT_MAX+1];
++ struct sadb_msg *pfkey_reply = NULL;
++ struct socket_list *pfkey_socketsp;
++ uint8_t satype = ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_satype;
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_add_parse: .\n");
++
++ pfkey_extensions_init(extensions_reply);
++
++ if(((struct sadb_sa*)extensions[SADB_EXT_SA])->sadb_sa_state != SADB_SASTATE_MATURE) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_add_parse: "
++ "error, sa_state=%d must be MATURE=%d\n",
++ ((struct sadb_sa*)extensions[SADB_EXT_SA])->sadb_sa_state,
++ SADB_SASTATE_MATURE);
++ SENDERR(EINVAL);
++ }
++
++ if(!extr || !extr->ips) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_add_parse: "
++ "extr or extr->ips pointer NULL\n");
++ SENDERR(EINVAL);
++ }
++
++ sa_len = satot(&extr->ips->ips_said, 0, sa, sizeof(sa));
++
++ ipsq = ipsec_sa_getbyid(&(extr->ips->ips_said));
++ if(ipsq != NULL) {
++ ipsec_sa_put(ipsq);
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_add_parse: "
++ "found an old ipsec_sa for SA%s, delete it first.\n",
++ sa_len ? sa : " (error)");
++ SENDERR(EEXIST);
++ }
++
++ if(ip_chk_addr((unsigned long)extr->ips->ips_said.dst.u.v4.sin_addr.s_addr) == IS_MYADDR) {
++ extr->ips->ips_flags |= EMT_INBOUND;
++ }
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_add_parse: "
++ "existing ipsec_sa not found (this is good) for SA%s, %s-bound, allocating.\n",
++ sa_len ? sa : " (error)",
++ extr->ips->ips_flags & EMT_INBOUND ? "in" : "out");
++
++ /* XXX extr->ips->ips_rcvif = &(enc_softc[em->em_if].enc_if);*/
++ extr->ips->ips_rcvif = NULL;
++
++ if ((error = pfkey_ipsec_sa_init(extr->ips, extensions))) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_add_parse: "
++ "not successful for SA: %s, deleting.\n",
++ sa_len ? sa : " (error)");
++ SENDERR(-error);
++ }
++
++ extr->ips->ips_life.ipl_addtime.ipl_count = jiffies / HZ;
++ if(!extr->ips->ips_life.ipl_allocations.ipl_count) {
++ extr->ips->ips_life.ipl_allocations.ipl_count += 1;
++ }
++
++ if(!(pfkey_safe_build(error = pfkey_msg_hdr_build(&extensions_reply[0],
++ SADB_ADD,
++ satype,
++ 0,
++ ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_seq,
++ ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_pid),
++ extensions_reply)
++ && pfkey_safe_build(error = pfkey_sa_ref_build(&extensions_reply[SADB_EXT_SA],
++ SADB_EXT_SA,
++ extr->ips->ips_said.spi,
++ extr->ips->ips_replaywin,
++ extr->ips->ips_state,
++ extr->ips->ips_authalg,
++ extr->ips->ips_encalg,
++ extr->ips->ips_flags,
++ extr->ips->ips_ref),
++ extensions_reply)
++ /* The 3 lifetime extentions should only be sent if non-zero. */
++ && (extensions[SADB_EXT_LIFETIME_HARD]
++ ? pfkey_safe_build(error = pfkey_lifetime_build(&extensions_reply[SADB_EXT_LIFETIME_HARD],
++ SADB_EXT_LIFETIME_HARD,
++ extr->ips->ips_life.ipl_allocations.ipl_hard,
++ extr->ips->ips_life.ipl_bytes.ipl_hard,
++ extr->ips->ips_life.ipl_addtime.ipl_hard,
++ extr->ips->ips_life.ipl_usetime.ipl_hard,
++ extr->ips->ips_life.ipl_packets.ipl_hard),
++ extensions_reply) : 1)
++ && (extensions[SADB_EXT_LIFETIME_SOFT]
++ ? pfkey_safe_build(error = pfkey_lifetime_build(&extensions_reply[SADB_EXT_LIFETIME_SOFT],
++ SADB_EXT_LIFETIME_SOFT,
++ extr->ips->ips_life.ipl_allocations.ipl_soft,
++ extr->ips->ips_life.ipl_bytes.ipl_soft,
++ extr->ips->ips_life.ipl_addtime.ipl_soft,
++ extr->ips->ips_life.ipl_usetime.ipl_soft,
++ extr->ips->ips_life.ipl_packets.ipl_soft),
++ extensions_reply) : 1)
++ && pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_EXT_ADDRESS_SRC],
++ SADB_EXT_ADDRESS_SRC,
++ 0, /*extr->ips->ips_said.proto,*/
++ 0,
++ extr->ips->ips_addr_s),
++ extensions_reply)
++ && pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_EXT_ADDRESS_DST],
++ SADB_EXT_ADDRESS_DST,
++ 0, /*extr->ips->ips_said.proto,*/
++ 0,
++ extr->ips->ips_addr_d),
++ extensions_reply)
++ && (extr->ips->ips_ident_s.data
++ ? pfkey_safe_build(error = pfkey_ident_build(&extensions_reply[SADB_EXT_IDENTITY_SRC],
++ SADB_EXT_IDENTITY_SRC,
++ extr->ips->ips_ident_s.type,
++ extr->ips->ips_ident_s.id,
++ extr->ips->ips_ident_s.len,
++ extr->ips->ips_ident_s.data),
++ extensions_reply) : 1)
++ && (extr->ips->ips_ident_d.data
++ ? pfkey_safe_build(error = pfkey_ident_build(&extensions_reply[SADB_EXT_IDENTITY_DST],
++ SADB_EXT_IDENTITY_DST,
++ extr->ips->ips_ident_d.type,
++ extr->ips->ips_ident_d.id,
++ extr->ips->ips_ident_d.len,
++ extr->ips->ips_ident_d.data),
++ extensions_reply) : 1)
++#if 0
++ /* FIXME: This won't work yet because I have not finished
++ it. */
++ && (extr->ips->ips_sens_
++ ? pfkey_safe_build(error = pfkey_sens_build(&extensions_reply[SADB_EXT_SENSITIVITY],
++ extr->ips->ips_sens_dpd,
++ extr->ips->ips_sens_sens_level,
++ extr->ips->ips_sens_sens_len,
++ extr->ips->ips_sens_sens_bitmap,
++ extr->ips->ips_sens_integ_level,
++ extr->ips->ips_sens_integ_len,
++ extr->ips->ips_sens_integ_bitmap),
++ extensions_reply) : 1)
++#endif
++ )) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_add_parse: "
++ "failed to build the add reply message extensions\n");
++ SENDERR(-error);
++ }
++
++ if((error = pfkey_msg_build(&pfkey_reply, extensions_reply, EXT_BITS_OUT))) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_add_parse: "
++ "failed to build the add reply message\n");
++ SENDERR(-error);
++ }
++ for(pfkey_socketsp = pfkey_open_sockets;
++ pfkey_socketsp;
++ pfkey_socketsp = pfkey_socketsp->next) {
++ if((error = pfkey_upmsg(pfkey_socketsp->socketp, pfkey_reply))) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_add_parse: "
++ "sending up add reply message for satype=%d(%s) to socket=0p%p failed with error=%d.\n",
++ satype,
++ satype2name(satype),
++ pfkey_socketsp->socketp,
++ error);
++ SENDERR(-error);
++ }
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_add_parse: "
++ "sending up add reply message for satype=%d(%s) to socket=0p%p succeeded.\n",
++ satype,
++ satype2name(satype),
++ pfkey_socketsp->socketp);
++ }
++
++ if((error = ipsec_sa_add(extr->ips))) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_add_parse: "
++ "failed to add the mature SA=%s with error=%d.\n",
++ sa_len ? sa : " (error)",
++ error);
++ SENDERR(-error);
++ }
++ extr->ips = NULL;
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_add_parse: "
++ "successful for SA: %s\n",
++ sa_len ? sa : " (error)");
++
++ errlab:
++ if (pfkey_reply) {
++ pfkey_msg_free(&pfkey_reply);
++ }
++ pfkey_extensions_free(extensions_reply);
++ return error;
++}
++
++DEBUG_NO_STATIC int
++pfkey_delete_parse(struct sock *sk, struct sadb_ext **extensions, struct pfkey_extracted_data* extr)
++{
++ struct ipsec_sa *ipsp;
++ char sa[SATOT_BUF];
++ size_t sa_len;
++ int error = 0;
++ struct sadb_ext *extensions_reply[SADB_EXT_MAX+1];
++ struct sadb_msg *pfkey_reply = NULL;
++ struct socket_list *pfkey_socketsp;
++ uint8_t satype = ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_satype;
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_delete_parse: .\n");
++
++ pfkey_extensions_init(extensions_reply);
++
++ if(!extr || !extr->ips) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_delete_parse: "
++ "extr or extr->ips pointer NULL, fatal\n");
++ SENDERR(EINVAL);
++ }
++
++ sa_len = satot(&extr->ips->ips_said, 0, sa, sizeof(sa));
++
++ spin_lock_bh(&tdb_lock);
++
++ ipsp = ipsec_sa_getbyid(&(extr->ips->ips_said));
++ if (ipsp == NULL) {
++ spin_unlock_bh(&tdb_lock);
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_delete_parse: "
++ "ipsec_sa not found for SA:%s, could not delete.\n",
++ sa_len ? sa : " (error)");
++ SENDERR(ESRCH);
++ }
++
++ ipsec_sa_put(ipsp);
++ if((error = ipsec_sa_delchain(ipsp))) {
++ spin_unlock_bh(&tdb_lock);
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_delete_parse: "
++ "error=%d returned trying to delete ipsec_sa for SA:%s.\n",
++ error,
++ sa_len ? sa : " (error)");
++ SENDERR(-error);
++ }
++ spin_unlock_bh(&tdb_lock);
++
++ if(!(pfkey_safe_build(error = pfkey_msg_hdr_build(&extensions_reply[0],
++ SADB_DELETE,
++ satype,
++ 0,
++ ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_seq,
++ ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_pid),
++ extensions_reply)
++ && pfkey_safe_build(error = pfkey_sa_ref_build(&extensions_reply[SADB_EXT_SA],
++ SADB_EXT_SA,
++ extr->ips->ips_said.spi,
++ 0,
++ 0,
++ 0,
++ 0,
++ 0,
++ extr->ips->ips_ref),
++ extensions_reply)
++ && pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_EXT_ADDRESS_SRC],
++ SADB_EXT_ADDRESS_SRC,
++ 0, /*extr->ips->ips_said.proto,*/
++ 0,
++ extr->ips->ips_addr_s),
++ extensions_reply)
++ && pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_EXT_ADDRESS_DST],
++ SADB_EXT_ADDRESS_DST,
++ 0, /*extr->ips->ips_said.proto,*/
++ 0,
++ extr->ips->ips_addr_d),
++ extensions_reply)
++ )) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_delete_parse: "
++ "failed to build the delete reply message extensions\n");
++ SENDERR(-error);
++ }
++
++ if((error = pfkey_msg_build(&pfkey_reply, extensions_reply, EXT_BITS_OUT))) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_delete_parse: "
++ "failed to build the delete reply message\n");
++ SENDERR(-error);
++ }
++ for(pfkey_socketsp = pfkey_open_sockets;
++ pfkey_socketsp;
++ pfkey_socketsp = pfkey_socketsp->next) {
++ if((error = pfkey_upmsg(pfkey_socketsp->socketp, pfkey_reply))) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_delete_parse: "
++ "sending up delete reply message for satype=%d(%s) to socket=0p%p failed with error=%d.\n",
++ satype,
++ satype2name(satype),
++ pfkey_socketsp->socketp,
++ error);
++ SENDERR(-error);
++ }
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_delete_parse: "
++ "sending up delete reply message for satype=%d(%s) to socket=0p%p succeeded.\n",
++ satype,
++ satype2name(satype),
++ pfkey_socketsp->socketp);
++ }
++
++ errlab:
++ if (pfkey_reply) {
++ pfkey_msg_free(&pfkey_reply);
++ }
++ pfkey_extensions_free(extensions_reply);
++ return error;
++}
++
++DEBUG_NO_STATIC int
++pfkey_get_parse(struct sock *sk, struct sadb_ext **extensions, struct pfkey_extracted_data* extr)
++{
++ int error = 0;
++ struct ipsec_sa *ipsp;
++ char sa[SATOT_BUF];
++ size_t sa_len;
++ struct sadb_ext *extensions_reply[SADB_EXT_MAX+1];
++ struct sadb_msg *pfkey_reply = NULL;
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_get_parse: .\n");
++
++ pfkey_extensions_init(extensions_reply);
++
++ if(!extr || !extr->ips) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_get_parse: "
++ "extr or extr->ips pointer NULL, fatal\n");
++ SENDERR(EINVAL);
++ }
++
++ sa_len = satot(&extr->ips->ips_said, 0, sa, sizeof(sa));
++
++ spin_lock_bh(&tdb_lock);
++
++ ipsp = ipsec_sa_getbyid(&(extr->ips->ips_said));
++ if (ipsp == NULL) {
++ spin_unlock_bh(&tdb_lock);
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_get_parse: "
++ "ipsec_sa not found for SA=%s, could not get.\n",
++ sa_len ? sa : " (error)");
++ SENDERR(ESRCH);
++ }
++
++ if(!(pfkey_safe_build(error = pfkey_msg_hdr_build(&extensions_reply[0],
++ SADB_GET,
++ ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_satype,
++ 0,
++ ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_seq,
++ ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_pid),
++ extensions_reply)
++ && pfkey_safe_build(error = pfkey_sa_ref_build(&extensions_reply[SADB_EXT_SA],
++ SADB_EXT_SA,
++ extr->ips->ips_said.spi,
++ extr->ips->ips_replaywin,
++ extr->ips->ips_state,
++ extr->ips->ips_authalg,
++ extr->ips->ips_encalg,
++ extr->ips->ips_flags,
++ extr->ips->ips_ref),
++ extensions_reply)
++ /* The 3 lifetime extentions should only be sent if non-zero. */
++ && (ipsp->ips_life.ipl_allocations.ipl_count
++ || ipsp->ips_life.ipl_bytes.ipl_count
++ || ipsp->ips_life.ipl_addtime.ipl_count
++ || ipsp->ips_life.ipl_usetime.ipl_count
++ || ipsp->ips_life.ipl_packets.ipl_count
++ ? pfkey_safe_build(error = pfkey_lifetime_build(&extensions_reply[SADB_EXT_LIFETIME_CURRENT],
++ SADB_EXT_LIFETIME_CURRENT,
++ ipsp->ips_life.ipl_allocations.ipl_count,
++ ipsp->ips_life.ipl_bytes.ipl_count,
++ ipsp->ips_life.ipl_addtime.ipl_count,
++ ipsp->ips_life.ipl_usetime.ipl_count,
++ ipsp->ips_life.ipl_packets.ipl_count),
++ extensions_reply) : 1)
++ && (ipsp->ips_life.ipl_allocations.ipl_hard
++ || ipsp->ips_life.ipl_bytes.ipl_hard
++ || ipsp->ips_life.ipl_addtime.ipl_hard
++ || ipsp->ips_life.ipl_usetime.ipl_hard
++ || ipsp->ips_life.ipl_packets.ipl_hard
++ ? pfkey_safe_build(error = pfkey_lifetime_build(&extensions_reply[SADB_EXT_LIFETIME_HARD],
++ SADB_EXT_LIFETIME_HARD,
++ ipsp->ips_life.ipl_allocations.ipl_hard,
++ ipsp->ips_life.ipl_bytes.ipl_hard,
++ ipsp->ips_life.ipl_addtime.ipl_hard,
++ ipsp->ips_life.ipl_usetime.ipl_hard,
++ ipsp->ips_life.ipl_packets.ipl_hard),
++ extensions_reply) : 1)
++ && (ipsp->ips_life.ipl_allocations.ipl_soft
++ || ipsp->ips_life.ipl_bytes.ipl_soft
++ || ipsp->ips_life.ipl_addtime.ipl_soft
++ || ipsp->ips_life.ipl_usetime.ipl_soft
++ || ipsp->ips_life.ipl_packets.ipl_soft
++ ? pfkey_safe_build(error = pfkey_lifetime_build(&extensions_reply[SADB_EXT_LIFETIME_SOFT],
++ SADB_EXT_LIFETIME_SOFT,
++ ipsp->ips_life.ipl_allocations.ipl_soft,
++ ipsp->ips_life.ipl_bytes.ipl_soft,
++ ipsp->ips_life.ipl_addtime.ipl_soft,
++ ipsp->ips_life.ipl_usetime.ipl_soft,
++ ipsp->ips_life.ipl_packets.ipl_soft),
++ extensions_reply) : 1)
++ && pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_EXT_ADDRESS_SRC],
++ SADB_EXT_ADDRESS_SRC,
++ 0, /*extr->ips->ips_said.proto,*/
++ 0,
++ extr->ips->ips_addr_s),
++ extensions_reply)
++ && pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_EXT_ADDRESS_DST],
++ SADB_EXT_ADDRESS_DST,
++ 0, /*extr->ips->ips_said.proto,*/
++ 0,
++ extr->ips->ips_addr_d),
++ extensions_reply)
++ && (extr->ips->ips_addr_p
++ ? pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_EXT_ADDRESS_PROXY],
++ SADB_EXT_ADDRESS_PROXY,
++ 0, /*extr->ips->ips_said.proto,*/
++ 0,
++ extr->ips->ips_addr_p),
++ extensions_reply) : 1)
++#if 0
++ /* FIXME: This won't work yet because the keys are not
++ stored directly in the ipsec_sa. They are stored as
++ contexts. */
++ && (extr->ips->ips_key_a_size
++ ? pfkey_safe_build(error = pfkey_key_build(&extensions_reply[SADB_EXT_KEY_AUTH],
++ SADB_EXT_KEY_AUTH,
++ extr->ips->ips_key_a_size * 8,
++ extr->ips->ips_key_a),
++ extensions_reply) : 1)
++ /* FIXME: This won't work yet because the keys are not
++ stored directly in the ipsec_sa. They are stored as
++ key schedules. */
++ && (extr->ips->ips_key_e_size
++ ? pfkey_safe_build(error = pfkey_key_build(&extensions_reply[SADB_EXT_KEY_ENCRYPT],
++ SADB_EXT_KEY_ENCRYPT,
++ extr->ips->ips_key_e_size * 8,
++ extr->ips->ips_key_e),
++ extensions_reply) : 1)
++#endif
++ && (extr->ips->ips_ident_s.data
++ ? pfkey_safe_build(error = pfkey_ident_build(&extensions_reply[SADB_EXT_IDENTITY_SRC],
++ SADB_EXT_IDENTITY_SRC,
++ extr->ips->ips_ident_s.type,
++ extr->ips->ips_ident_s.id,
++ extr->ips->ips_ident_s.len,
++ extr->ips->ips_ident_s.data),
++ extensions_reply) : 1)
++ && (extr->ips->ips_ident_d.data
++ ? pfkey_safe_build(error = pfkey_ident_build(&extensions_reply[SADB_EXT_IDENTITY_DST],
++ SADB_EXT_IDENTITY_DST,
++ extr->ips->ips_ident_d.type,
++ extr->ips->ips_ident_d.id,
++ extr->ips->ips_ident_d.len,
++ extr->ips->ips_ident_d.data),
++ extensions_reply) : 1)
++#if 0
++ /* FIXME: This won't work yet because I have not finished
++ it. */
++ && (extr->ips->ips_sens_
++ ? pfkey_safe_build(error = pfkey_sens_build(&extensions_reply[SADB_EXT_SENSITIVITY],
++ extr->ips->ips_sens_dpd,
++ extr->ips->ips_sens_sens_level,
++ extr->ips->ips_sens_sens_len,
++ extr->ips->ips_sens_sens_bitmap,
++ extr->ips->ips_sens_integ_level,
++ extr->ips->ips_sens_integ_len,
++ extr->ips->ips_sens_integ_bitmap),
++ extensions_reply) : 1)
++#endif
++ )) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_get_parse: "
++ "failed to build the get reply message extensions\n");
++ ipsec_sa_put(ipsp);
++ spin_unlock_bh(&tdb_lock);
++ SENDERR(-error);
++ }
++
++ ipsec_sa_put(ipsp);
++ spin_unlock_bh(&tdb_lock);
++
++ if((error = pfkey_msg_build(&pfkey_reply, extensions_reply, EXT_BITS_OUT))) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_get_parse: "
++ "failed to build the get reply message\n");
++ SENDERR(-error);
++ }
++
++ if((error = pfkey_upmsg(sk->socket, pfkey_reply))) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_get_parse: "
++ "failed to send the get reply message\n");
++ SENDERR(-error);
++ }
++
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_get_parse: "
++ "succeeded in sending get reply message.\n");
++
++ errlab:
++ if (pfkey_reply) {
++ pfkey_msg_free(&pfkey_reply);
++ }
++ pfkey_extensions_free(extensions_reply);
++ return error;
++}
++
++DEBUG_NO_STATIC int
++pfkey_acquire_parse(struct sock *sk, struct sadb_ext **extensions, struct pfkey_extracted_data* extr)
++{
++ int error = 0;
++ struct socket_list *pfkey_socketsp;
++ uint8_t satype = ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_satype;
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_acquire_parse: .\n");
++
++ /* XXX I don't know if we want an upper bound, since userspace may
++ want to register itself for an satype > SADB_SATYPE_MAX. */
++ if((satype == 0) || (satype > SADB_SATYPE_MAX)) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_acquire_parse: "
++ "SATYPE=%d invalid.\n",
++ satype);
++ SENDERR(EINVAL);
++ }
++
++ if(!(pfkey_registered_sockets[satype])) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_acquire_parse: "
++ "no sockets registered for SAtype=%d(%s).\n",
++ satype,
++ satype2name(satype));
++ SENDERR(EPROTONOSUPPORT);
++ }
++
++ for(pfkey_socketsp = pfkey_registered_sockets[satype];
++ pfkey_socketsp;
++ pfkey_socketsp = pfkey_socketsp->next) {
++ if((error = pfkey_upmsg(pfkey_socketsp->socketp,
++ ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])))) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_acquire_parse: "
++ "sending up acquire reply message for satype=%d(%s) to socket=0p%p failed with error=%d.\n",
++ satype,
++ satype2name(satype),
++ pfkey_socketsp->socketp,
++ error);
++ SENDERR(-error);
++ }
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_acquire_parse: "
++ "sending up acquire reply message for satype=%d(%s) to socket=0p%p succeeded.\n",
++ satype,
++ satype2name(satype),
++ pfkey_socketsp->socketp);
++ }
++
++ errlab:
++ return error;
++}
++
++DEBUG_NO_STATIC int
++pfkey_register_parse(struct sock *sk, struct sadb_ext **extensions, struct pfkey_extracted_data* extr)
++{
++ int error = 0;
++ uint8_t satype = ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_satype;
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_register_parse: .\n");
++
++ /* XXX I don't know if we want an upper bound, since userspace may
++ want to register itself for an satype > SADB_SATYPE_MAX. */
++ if((satype == 0) || (satype > SADB_SATYPE_MAX)) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_register_parse: "
++ "SATYPE=%d invalid.\n",
++ satype);
++ SENDERR(EINVAL);
++ }
++
++ if(!pfkey_list_insert_socket(sk->socket,
++ &(pfkey_registered_sockets[satype]))) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_register_parse: "
++ "SATYPE=%02d(%s) successfully registered by KMd (pid=%d).\n",
++ satype,
++ satype2name(satype),
++ key_pid(sk));
++ };
++
++ /* send up register msg with supported SATYPE algos */
++
++ error=pfkey_register_reply(satype, (struct sadb_msg*)extensions[SADB_EXT_RESERVED]);
++ errlab:
++ return error;
++}
++
++int
++pfkey_register_reply(int satype, struct sadb_msg *sadb_msg)
++{
++ struct sadb_ext *extensions_reply[SADB_EXT_MAX+1];
++ struct sadb_msg *pfkey_reply = NULL;
++ struct socket_list *pfkey_socketsp;
++ struct supported_list *pfkey_supported_listp;
++ unsigned int alg_num_a = 0, alg_num_e = 0;
++ struct sadb_alg *alg_a = NULL, *alg_e = NULL, *alg_ap = NULL, *alg_ep = NULL;
++ int error = 0;
++
++ pfkey_extensions_init(extensions_reply);
++
++ if((satype == 0) || (satype > SADB_SATYPE_MAX)) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_register_reply: "
++ "SAtype=%d unspecified or unknown.\n",
++ satype);
++ SENDERR(EINVAL);
++ }
++ if(!(pfkey_registered_sockets[satype])) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_register_reply: "
++ "no sockets registered for SAtype=%d(%s).\n",
++ satype,
++ satype2name(satype));
++ SENDERR(EPROTONOSUPPORT);
++ }
++ /* send up register msg with supported SATYPE algos */
++ pfkey_supported_listp = pfkey_supported_list[satype];
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_register_reply: "
++ "pfkey_supported_list[%d]=0p%p\n",
++ satype,
++ pfkey_supported_list[satype]);
++ while(pfkey_supported_listp) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_register_reply: "
++ "checking supported=0p%p\n",
++ pfkey_supported_listp);
++ if(pfkey_supported_listp->supportedp->supported_alg_exttype == SADB_EXT_SUPPORTED_AUTH) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_register_reply: "
++ "adding auth alg.\n");
++ alg_num_a++;
++ }
++ if(pfkey_supported_listp->supportedp->supported_alg_exttype == SADB_EXT_SUPPORTED_ENCRYPT) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_register_reply: "
++ "adding encrypt alg.\n");
++ alg_num_e++;
++ }
++ pfkey_supported_listp = pfkey_supported_listp->next;
++ }
++
++ if(alg_num_a) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_register_reply: "
++ "allocating %lu bytes for auth algs.\n",
++ (unsigned long) (alg_num_a * sizeof(struct sadb_alg)));
++ if((alg_a = kmalloc(alg_num_a * sizeof(struct sadb_alg), GFP_ATOMIC) ) == NULL) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_register_reply: "
++ "auth alg memory allocation error\n");
++ SENDERR(ENOMEM);
++ }
++ alg_ap = alg_a;
++ }
++
++ if(alg_num_e) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_register_reply: "
++ "allocating %lu bytes for enc algs.\n",
++ (unsigned long) (alg_num_e * sizeof(struct sadb_alg)));
++ if((alg_e = kmalloc(alg_num_e * sizeof(struct sadb_alg), GFP_ATOMIC) ) == NULL) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_register_reply: "
++ "enc alg memory allocation error\n");
++ SENDERR(ENOMEM);
++ }
++ alg_ep = alg_e;
++ }
++
++ pfkey_supported_listp = pfkey_supported_list[satype];
++ while(pfkey_supported_listp) {
++ if(alg_num_a) {
++ if(pfkey_supported_listp->supportedp->supported_alg_exttype == SADB_EXT_SUPPORTED_AUTH) {
++ alg_ap->sadb_alg_id = pfkey_supported_listp->supportedp->supported_alg_id;
++ alg_ap->sadb_alg_ivlen = pfkey_supported_listp->supportedp->supported_alg_ivlen;
++ alg_ap->sadb_alg_minbits = pfkey_supported_listp->supportedp->supported_alg_minbits;
++ alg_ap->sadb_alg_maxbits = pfkey_supported_listp->supportedp->supported_alg_maxbits;
++ alg_ap->sadb_alg_reserved = 0;
++ KLIPS_PRINT(debug_pfkey && sysctl_ipsec_debug_verbose,
++ "klips_debug:pfkey_register_reply: "
++ "adding auth=0p%p\n",
++ alg_ap);
++ alg_ap++;
++ }
++ }
++ if(alg_num_e) {
++ if(pfkey_supported_listp->supportedp->supported_alg_exttype == SADB_EXT_SUPPORTED_ENCRYPT) {
++ alg_ep->sadb_alg_id = pfkey_supported_listp->supportedp->supported_alg_id;
++ alg_ep->sadb_alg_ivlen = pfkey_supported_listp->supportedp->supported_alg_ivlen;
++ alg_ep->sadb_alg_minbits = pfkey_supported_listp->supportedp->supported_alg_minbits;
++ alg_ep->sadb_alg_maxbits = pfkey_supported_listp->supportedp->supported_alg_maxbits;
++ alg_ep->sadb_alg_reserved = 0;
++ KLIPS_PRINT(debug_pfkey && sysctl_ipsec_debug_verbose,
++ "klips_debug:pfkey_register_reply: "
++ "adding encrypt=0p%p\n",
++ alg_ep);
++ alg_ep++;
++ }
++ }
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_register_reply: "
++ "found satype=%d(%s) exttype=%d id=%d ivlen=%d minbits=%d maxbits=%d.\n",
++ satype,
++ satype2name(satype),
++ pfkey_supported_listp->supportedp->supported_alg_exttype,
++ pfkey_supported_listp->supportedp->supported_alg_id,
++ pfkey_supported_listp->supportedp->supported_alg_ivlen,
++ pfkey_supported_listp->supportedp->supported_alg_minbits,
++ pfkey_supported_listp->supportedp->supported_alg_maxbits);
++ pfkey_supported_listp = pfkey_supported_listp->next;
++ }
++ if(!(pfkey_safe_build(error = pfkey_msg_hdr_build(&extensions_reply[0],
++ SADB_REGISTER,
++ satype,
++ 0,
++ sadb_msg? sadb_msg->sadb_msg_seq : ++pfkey_msg_seq,
++ sadb_msg? sadb_msg->sadb_msg_pid: current->pid),
++ extensions_reply) &&
++ (alg_num_a ? pfkey_safe_build(error = pfkey_supported_build(&extensions_reply[SADB_EXT_SUPPORTED_AUTH],
++ SADB_EXT_SUPPORTED_AUTH,
++ alg_num_a,
++ alg_a),
++ extensions_reply) : 1) &&
++ (alg_num_e ? pfkey_safe_build(error = pfkey_supported_build(&extensions_reply[SADB_EXT_SUPPORTED_ENCRYPT],
++ SADB_EXT_SUPPORTED_ENCRYPT,
++ alg_num_e,
++ alg_e),
++ extensions_reply) : 1))) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_register_reply: "
++ "failed to build the register message extensions_reply\n");
++ SENDERR(-error);
++ }
++
++ if((error = pfkey_msg_build(&pfkey_reply, extensions_reply, EXT_BITS_OUT))) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_register_reply: "
++ "failed to build the register message\n");
++ SENDERR(-error);
++ }
++ /* this should go to all registered sockets for that satype only */
++ for(pfkey_socketsp = pfkey_registered_sockets[satype];
++ pfkey_socketsp;
++ pfkey_socketsp = pfkey_socketsp->next) {
++ if((error = pfkey_upmsg(pfkey_socketsp->socketp, pfkey_reply))) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_register_reply: "
++ "sending up acquire message for satype=%d(%s) to socket=0p%p failed with error=%d.\n",
++ satype,
++ satype2name(satype),
++ pfkey_socketsp->socketp,
++ error);
++ SENDERR(-error);
++ }
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_register_reply: "
++ "sending up register message for satype=%d(%s) to socket=0p%p succeeded.\n",
++ satype,
++ satype2name(satype),
++ pfkey_socketsp->socketp);
++ }
++
++ errlab:
++ if(alg_a) {
++ kfree(alg_a);
++ }
++ if(alg_e) {
++ kfree(alg_e);
++ }
++ if (pfkey_reply) {
++ pfkey_msg_free(&pfkey_reply);
++ }
++ pfkey_extensions_free(extensions_reply);
++ return error;
++}
++
++DEBUG_NO_STATIC int
++pfkey_expire_parse(struct sock *sk, struct sadb_ext **extensions, struct pfkey_extracted_data* extr)
++{
++ int error = 0;
++ struct socket_list *pfkey_socketsp;
++#ifdef CONFIG_IPSEC_DEBUG
++ uint8_t satype = ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_satype;
++#endif /* CONFIG_IPSEC_DEBUG */
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_expire_parse: .\n");
++
++ if(pfkey_open_sockets) {
++ for(pfkey_socketsp = pfkey_open_sockets;
++ pfkey_socketsp;
++ pfkey_socketsp = pfkey_socketsp->next) {
++ if((error = pfkey_upmsg(pfkey_socketsp->socketp,
++ ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])))) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_expire_parse: "
++ "sending up expire reply message for satype=%d(%s) to socket=0p%p failed with error=%d.\n",
++ satype,
++ satype2name(satype),
++ pfkey_socketsp->socketp,
++ error);
++ SENDERR(-error);
++ }
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_expire_parse: "
++ "sending up expire reply message for satype=%d(%s) to socket=0p%p succeeded.\n",
++ satype,
++ satype2name(satype),
++ pfkey_socketsp->socketp);
++ }
++ }
++
++ errlab:
++ return error;
++}
++
++DEBUG_NO_STATIC int
++pfkey_flush_parse(struct sock *sk, struct sadb_ext **extensions, struct pfkey_extracted_data* extr)
++{
++ int error = 0;
++ struct socket_list *pfkey_socketsp;
++ uint8_t satype = ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_satype;
++ uint8_t proto = 0;
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_flush_parse: "
++ "flushing type %d SAs\n",
++ satype);
++
++ if(satype && !(proto = satype2proto(satype))) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_flush_parse: "
++ "satype %d lookup failed.\n",
++ ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_satype);
++ SENDERR(EINVAL);
++ }
++
++ if ((error = ipsec_sadb_cleanup(proto))) {
++ SENDERR(-error);
++ }
++
++ if(pfkey_open_sockets) {
++ for(pfkey_socketsp = pfkey_open_sockets;
++ pfkey_socketsp;
++ pfkey_socketsp = pfkey_socketsp->next) {
++ if((error = pfkey_upmsg(pfkey_socketsp->socketp,
++ ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])))) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_flush_parse: "
++ "sending up flush reply message for satype=%d(%s) (proto=%d) to socket=0p%p failed with error=%d.\n",
++ satype,
++ satype2name(satype),
++ proto,
++ pfkey_socketsp->socketp,
++ error);
++ SENDERR(-error);
++ }
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_flush_parse: "
++ "sending up flush reply message for satype=%d(%s) to socket=0p%p succeeded.\n",
++ satype,
++ satype2name(satype),
++ pfkey_socketsp->socketp);
++ }
++ }
++
++ errlab:
++ return error;
++}
++
++DEBUG_NO_STATIC int
++pfkey_dump_parse(struct sock *sk, struct sadb_ext **extensions, struct pfkey_extracted_data* extr)
++{
++ int error = 0;
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_dump_parse: .\n");
++
++ SENDERR(ENOSYS);
++ errlab:
++ return error;
++}
++
++DEBUG_NO_STATIC int
++pfkey_x_promisc_parse(struct sock *sk, struct sadb_ext **extensions, struct pfkey_extracted_data* extr)
++{
++ int error = 0;
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_promisc_parse: .\n");
++
++ SENDERR(ENOSYS);
++ errlab:
++ return error;
++}
++
++DEBUG_NO_STATIC int
++pfkey_x_pchange_parse(struct sock *sk, struct sadb_ext **extensions, struct pfkey_extracted_data* extr)
++{
++ int error = 0;
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_pchange_parse: .\n");
++
++ SENDERR(ENOSYS);
++ errlab:
++ return error;
++}
++
++DEBUG_NO_STATIC int
++pfkey_x_grpsa_parse(struct sock *sk, struct sadb_ext **extensions, struct pfkey_extracted_data* extr)
++{
++ struct ipsec_sa *ips1p, *ips2p, *ipsp;
++ struct sadb_ext *extensions_reply[SADB_EXT_MAX+1];
++ struct sadb_msg *pfkey_reply = NULL;
++ struct socket_list *pfkey_socketsp;
++ uint8_t satype = ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_satype;
++ char sa1[SATOT_BUF], sa2[SATOT_BUF];
++ size_t sa_len1, sa_len2 = 0;
++ int error = 0;
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_grpsa_parse: .\n");
++
++ pfkey_extensions_init(extensions_reply);
++
++ if(extr == NULL || extr->ips == NULL) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_grpsa_parse: "
++ "extr or extr->ips is NULL, fatal.\n");
++ SENDERR(EINVAL);
++ }
++
++ sa_len1 = satot(&extr->ips->ips_said, 0, sa1, sizeof(sa1));
++ if(extr->ips2 != NULL) {
++ sa_len2 = satot(&extr->ips2->ips_said, 0, sa2, sizeof(sa2));
++ }
++
++ spin_lock_bh(&tdb_lock);
++
++ ips1p = ipsec_sa_getbyid(&(extr->ips->ips_said));
++ if(ips1p == NULL) {
++ spin_unlock_bh(&tdb_lock);
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_grpsa_parse: "
++ "reserved ipsec_sa for SA1: %s not found. Call SADB_ADD/UPDATE first.\n",
++ sa_len1 ? sa1 : " (error)");
++ SENDERR(ENOENT);
++ }
++ if(extr->ips2) { /* GRPSA */
++ ips2p = ipsec_sa_getbyid(&(extr->ips2->ips_said));
++ if(ips2p == NULL) {
++ ipsec_sa_put(ips1p);
++ spin_unlock_bh(&tdb_lock);
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_grpsa_parse: "
++ "reserved ipsec_sa for SA2: %s not found. Call SADB_ADD/UPDATE first.\n",
++ sa_len2 ? sa2 : " (error)");
++ SENDERR(ENOENT);
++ }
++
++ /* Is either one already linked? */
++ if(ips1p->ips_onext) {
++ ipsec_sa_put(ips1p);
++ ipsec_sa_put(ips2p);
++ spin_unlock_bh(&tdb_lock);
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_grpsa_parse: "
++ "ipsec_sa for SA: %s is already linked.\n",
++ sa_len1 ? sa1 : " (error)");
++ SENDERR(EEXIST);
++ }
++ if(ips2p->ips_inext) {
++ ipsec_sa_put(ips1p);
++ ipsec_sa_put(ips2p);
++ spin_unlock_bh(&tdb_lock);
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_grpsa_parse: "
++ "ipsec_sa for SA: %s is already linked.\n",
++ sa_len2 ? sa2 : " (error)");
++ SENDERR(EEXIST);
++ }
++
++ /* Is extr->ips already linked to extr->ips2? */
++ ipsp = ips2p;
++ while(ipsp) {
++ if(ipsp == ips1p) {
++ ipsec_sa_put(ips1p);
++ ipsec_sa_put(ips2p);
++ spin_unlock_bh(&tdb_lock);
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_grpsa_parse: "
++ "ipsec_sa for SA: %s is already linked to %s.\n",
++ sa_len1 ? sa1 : " (error)",
++ sa_len2 ? sa2 : " (error)");
++ SENDERR(EEXIST);
++ }
++ ipsp = ipsp->ips_onext;
++ }
++
++ /* link 'em */
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_grpsa_parse: "
++ "linking ipsec_sa SA: %s with %s.\n",
++ sa_len1 ? sa1 : " (error)",
++ sa_len2 ? sa2 : " (error)");
++ ips1p->ips_onext = ips2p;
++ ips2p->ips_inext = ips1p;
++ } else { /* UNGRPSA */
++ ipsec_sa_put(ips1p);
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_grpsa_parse: "
++ "unlinking ipsec_sa SA: %s.\n",
++ sa_len1 ? sa1 : " (error)");
++ while(ips1p->ips_onext) {
++ ips1p = ips1p->ips_onext;
++ }
++ while(ips1p->ips_inext) {
++ ipsp = ips1p;
++ ips1p = ips1p->ips_inext;
++ ipsec_sa_put(ips1p);
++ ipsp->ips_inext = NULL;
++ ipsec_sa_put(ipsp);
++ ips1p->ips_onext = NULL;
++ }
++ }
++
++ spin_unlock_bh(&tdb_lock);
++
++ if(!(pfkey_safe_build(error = pfkey_msg_hdr_build(&extensions_reply[0],
++ SADB_X_GRPSA,
++ satype,
++ 0,
++ ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_seq,
++ ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_pid),
++ extensions_reply)
++ && pfkey_safe_build(error = pfkey_sa_ref_build(&extensions_reply[SADB_EXT_SA],
++ SADB_EXT_SA,
++ extr->ips->ips_said.spi,
++ extr->ips->ips_replaywin,
++ extr->ips->ips_state,
++ extr->ips->ips_authalg,
++ extr->ips->ips_encalg,
++ extr->ips->ips_flags,
++ extr->ips->ips_ref),
++ extensions_reply)
++ && pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_EXT_ADDRESS_DST],
++ SADB_EXT_ADDRESS_DST,
++ 0, /*extr->ips->ips_said.proto,*/
++ 0,
++ extr->ips->ips_addr_d),
++ extensions_reply)
++ && (extr->ips2
++ ? (pfkey_safe_build(error = pfkey_x_satype_build(&extensions_reply[SADB_X_EXT_SATYPE2],
++ ((struct sadb_x_satype*)extensions[SADB_X_EXT_SATYPE2])->sadb_x_satype_satype
++ /* proto2satype(extr->ips2->ips_said.proto) */),
++ extensions_reply)
++ && pfkey_safe_build(error = pfkey_sa_ref_build(&extensions_reply[SADB_X_EXT_SA2],
++ SADB_X_EXT_SA2,
++ extr->ips2->ips_said.spi,
++ extr->ips2->ips_replaywin,
++ extr->ips2->ips_state,
++ extr->ips2->ips_authalg,
++ extr->ips2->ips_encalg,
++ extr->ips2->ips_flags,
++ extr->ips2->ips_ref),
++ extensions_reply)
++ && pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_X_EXT_ADDRESS_DST2],
++ SADB_X_EXT_ADDRESS_DST2,
++ 0, /*extr->ips->ips_said.proto,*/
++ 0,
++ extr->ips2->ips_addr_d),
++ extensions_reply) ) : 1 )
++ )) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_x_grpsa_parse: "
++ "failed to build the x_grpsa reply message extensions\n");
++ SENDERR(-error);
++ }
++
++ if((error = pfkey_msg_build(&pfkey_reply, extensions_reply, EXT_BITS_OUT))) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_x_grpsa_parse: "
++ "failed to build the x_grpsa reply message\n");
++ SENDERR(-error);
++ }
++
++ for(pfkey_socketsp = pfkey_open_sockets;
++ pfkey_socketsp;
++ pfkey_socketsp = pfkey_socketsp->next) {
++ if((error = pfkey_upmsg(pfkey_socketsp->socketp, pfkey_reply))) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_x_grpsa_parse: "
++ "sending up x_grpsa reply message for satype=%d(%s) to socket=0p%p failed with error=%d.\n",
++ satype,
++ satype2name(satype),
++ pfkey_socketsp->socketp,
++ error);
++ SENDERR(-error);
++ }
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_x_grpsa_parse: "
++ "sending up x_grpsa reply message for satype=%d(%s) to socket=0p%p succeeded.\n",
++ satype,
++ satype2name(satype),
++ pfkey_socketsp->socketp);
++ }
++
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_x_grpsa_parse: "
++ "succeeded in sending x_grpsa reply message.\n");
++
++ errlab:
++ if (pfkey_reply) {
++ pfkey_msg_free(&pfkey_reply);
++ }
++ pfkey_extensions_free(extensions_reply);
++ return error;
++}
++
++DEBUG_NO_STATIC int
++pfkey_x_addflow_parse(struct sock *sk, struct sadb_ext **extensions, struct pfkey_extracted_data* extr)
++{
++ int error = 0;
++#ifdef CONFIG_IPSEC_DEBUG
++ char buf1[64], buf2[64];
++#endif /* CONFIG_IPSEC_DEBUG */
++ struct sadb_ext *extensions_reply[SADB_EXT_MAX+1];
++ struct sadb_msg *pfkey_reply = NULL;
++ struct socket_list *pfkey_socketsp;
++ uint8_t satype = ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_satype;
++ ip_address srcflow, dstflow, srcmask, dstmask;
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_addflow_parse: .\n");
++
++ pfkey_extensions_init(extensions_reply);
++
++ memset((caddr_t)&srcflow, 0, sizeof(srcflow));
++ memset((caddr_t)&dstflow, 0, sizeof(dstflow));
++ memset((caddr_t)&srcmask, 0, sizeof(srcmask));
++ memset((caddr_t)&dstmask, 0, sizeof(dstmask));
++
++ if(!extr || !(extr->ips) || !(extr->eroute)) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_addflow_parse: "
++ "missing extr, ipsec_sa or eroute data.\n");
++ SENDERR(EINVAL);
++ }
++
++ srcflow.u.v4.sin_family = AF_INET;
++ dstflow.u.v4.sin_family = AF_INET;
++ srcmask.u.v4.sin_family = AF_INET;
++ dstmask.u.v4.sin_family = AF_INET;
++ srcflow.u.v4.sin_addr = extr->eroute->er_eaddr.sen_ip_src;
++ dstflow.u.v4.sin_addr = extr->eroute->er_eaddr.sen_ip_dst;
++ srcmask.u.v4.sin_addr = extr->eroute->er_emask.sen_ip_src;
++ dstmask.u.v4.sin_addr = extr->eroute->er_emask.sen_ip_dst;
++
++#ifdef CONFIG_IPSEC_DEBUG
++ if (debug_pfkey) {
++ subnettoa(extr->eroute->er_eaddr.sen_ip_src,
++ extr->eroute->er_emask.sen_ip_src, 0, buf1, sizeof(buf1));
++ subnettoa(extr->eroute->er_eaddr.sen_ip_dst,
++ extr->eroute->er_emask.sen_ip_dst, 0, buf2, sizeof(buf2));
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_addflow_parse: "
++ "calling breakeroute and/or makeroute for %s->%s\n",
++ buf1, buf2);
++ }
++#endif /* CONFIG_IPSEC_DEBUG */
++ if(extr->ips->ips_flags & SADB_X_SAFLAGS_INFLOW) {
++/* if(ip_chk_addr((unsigned long)extr->ips->ips_said.dst.u.v4.sin_addr.s_addr) == IS_MYADDR) */
++ struct ipsec_sa *ipsp, *ipsq;
++ char sa[SATOT_BUF];
++ size_t sa_len;
++
++ ipsq = ipsec_sa_getbyid(&(extr->ips->ips_said));
++ if(ipsq == NULL) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_addflow_parse: "
++ "ipsec_sa not found, cannot set incoming policy.\n");
++ SENDERR(ENOENT);
++ }
++
++ ipsp = ipsq;
++ while(ipsp && ipsp->ips_said.proto != IPPROTO_IPIP) {
++ ipsp = ipsp->ips_inext;
++ }
++
++ if(ipsp == NULL) {
++ ipsec_sa_put(ipsq);
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_addflow_parse: "
++ "SA chain does not have an IPIP SA, cannot set incoming policy.\n");
++ SENDERR(ENOENT);
++ }
++
++ sa_len = satot(&extr->ips->ips_said, 0, sa, sizeof(sa));
++
++ ipsp->ips_flags |= SADB_X_SAFLAGS_INFLOW;
++ ipsp->ips_flow_s = srcflow;
++ ipsp->ips_flow_d = dstflow;
++ ipsp->ips_mask_s = srcmask;
++ ipsp->ips_mask_d = dstmask;
++
++ ipsec_sa_put(ipsq);
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_addflow_parse: "
++ "inbound eroute, setting incoming policy information in IPIP ipsec_sa for SA: %s.\n",
++ sa_len ? sa : " (error)");
++ } else {
++ struct sk_buff *first = NULL, *last = NULL;
++
++ if(extr->ips->ips_flags & SADB_X_SAFLAGS_REPLACEFLOW) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_addflow_parse: "
++ "REPLACEFLOW flag set, calling breakeroute.\n");
++ if ((error = ipsec_breakroute(&(extr->eroute->er_eaddr),
++ &(extr->eroute->er_emask),
++ &first, &last))) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_addflow_parse: "
++ "breakeroute returned %d. first=0p%p, last=0p%p\n",
++ error,
++ first,
++ last);
++ if(first != NULL) {
++ ipsec_kfree_skb(first);
++ }
++ if(last != NULL) {
++ ipsec_kfree_skb(last);
++ }
++ SENDERR(-error);
++ }
++ }
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_addflow_parse: "
++ "calling makeroute.\n");
++
++ if ((error = ipsec_makeroute(&(extr->eroute->er_eaddr),
++ &(extr->eroute->er_emask),
++ extr->ips->ips_said,
++ ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_pid,
++ NULL,
++ &(extr->ips->ips_ident_s),
++ &(extr->ips->ips_ident_d)))) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_addflow_parse: "
++ "makeroute returned %d.\n", error);
++ SENDERR(-error);
++ }
++ if(first != NULL) {
++ KLIPS_PRINT(debug_eroute,
++ "klips_debug:pfkey_x_addflow_parse: "
++ "first=0p%p HOLD packet re-injected.\n",
++ first);
++ DEV_QUEUE_XMIT(first, first->dev, SOPRI_NORMAL);
++ }
++ if(last != NULL) {
++ KLIPS_PRINT(debug_eroute,
++ "klips_debug:pfkey_x_addflow_parse: "
++ "last=0p%p HOLD packet re-injected.\n",
++ last);
++ DEV_QUEUE_XMIT(last, last->dev, SOPRI_NORMAL);
++ }
++ }
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_addflow_parse: "
++ "makeroute call successful.\n");
++
++ if(!(pfkey_safe_build(error = pfkey_msg_hdr_build(&extensions_reply[0],
++ SADB_X_ADDFLOW,
++ satype,
++ 0,
++ ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_seq,
++ ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_pid),
++ extensions_reply)
++ && pfkey_safe_build(error = pfkey_sa_ref_build(&extensions_reply[SADB_EXT_SA],
++ SADB_EXT_SA,
++ extr->ips->ips_said.spi,
++ extr->ips->ips_replaywin,
++ extr->ips->ips_state,
++ extr->ips->ips_authalg,
++ extr->ips->ips_encalg,
++ extr->ips->ips_flags,
++ extr->ips->ips_ref),
++ extensions_reply)
++ && (extensions[SADB_EXT_ADDRESS_SRC]
++ ? pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_EXT_ADDRESS_SRC],
++ SADB_EXT_ADDRESS_SRC,
++ 0, /*extr->ips->ips_said.proto,*/
++ 0,
++ extr->ips->ips_addr_s),
++ extensions_reply) : 1)
++ && (extensions[SADB_EXT_ADDRESS_DST]
++ ? pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_EXT_ADDRESS_DST],
++ SADB_EXT_ADDRESS_DST,
++ 0, /*extr->ips->ips_said.proto,*/
++ 0,
++ extr->ips->ips_addr_d),
++ extensions_reply) : 1)
++ && pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_X_EXT_ADDRESS_SRC_FLOW],
++ SADB_X_EXT_ADDRESS_SRC_FLOW,
++ 0, /*extr->ips->ips_said.proto,*/
++ 0,
++ (struct sockaddr*)&srcflow),
++ extensions_reply)
++ && pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_X_EXT_ADDRESS_DST_FLOW],
++ SADB_X_EXT_ADDRESS_DST_FLOW,
++ 0, /*extr->ips->ips_said.proto,*/
++ 0,
++ (struct sockaddr*)&dstflow),
++ extensions_reply)
++ && pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_X_EXT_ADDRESS_SRC_MASK],
++ SADB_X_EXT_ADDRESS_SRC_MASK,
++ 0, /*extr->ips->ips_said.proto,*/
++ 0,
++ (struct sockaddr*)&srcmask),
++ extensions_reply)
++ && pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_X_EXT_ADDRESS_DST_MASK],
++ SADB_X_EXT_ADDRESS_DST_MASK,
++ 0, /*extr->ips->ips_said.proto,*/
++ 0,
++ (struct sockaddr*)&dstmask),
++ extensions_reply)
++ )) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_x_addflow_parse: "
++ "failed to build the x_addflow reply message extensions\n");
++ SENDERR(-error);
++ }
++
++ if((error = pfkey_msg_build(&pfkey_reply, extensions_reply, EXT_BITS_OUT))) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_x_addflow_parse: "
++ "failed to build the x_addflow reply message\n");
++ SENDERR(-error);
++ }
++
++ for(pfkey_socketsp = pfkey_open_sockets;
++ pfkey_socketsp;
++ pfkey_socketsp = pfkey_socketsp->next) {
++ if((error = pfkey_upmsg(pfkey_socketsp->socketp, pfkey_reply))) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_x_addflow_parse: "
++ "sending up x_addflow reply message for satype=%d(%s) to socket=0p%p failed with error=%d.\n",
++ satype,
++ satype2name(satype),
++ pfkey_socketsp->socketp,
++ error);
++ SENDERR(-error);
++ }
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_x_addflow_parse: "
++ "sending up x_addflow reply message for satype=%d(%s) (proto=%d) to socket=0p%p succeeded.\n",
++ satype,
++ satype2name(satype),
++ extr->ips->ips_said.proto,
++ pfkey_socketsp->socketp);
++ }
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_addflow_parse: "
++ "extr->ips cleaned up and freed.\n");
++
++ errlab:
++ if (pfkey_reply) {
++ pfkey_msg_free(&pfkey_reply);
++ }
++ pfkey_extensions_free(extensions_reply);
++ return error;
++}
++
++DEBUG_NO_STATIC int
++pfkey_x_delflow_parse(struct sock *sk, struct sadb_ext **extensions, struct pfkey_extracted_data* extr)
++{
++ int error = 0;
++#ifdef CONFIG_IPSEC_DEBUG
++ char buf1[64], buf2[64];
++#endif /* CONFIG_IPSEC_DEBUG */
++ struct sadb_ext *extensions_reply[SADB_EXT_MAX+1];
++ struct sadb_msg *pfkey_reply = NULL;
++ struct socket_list *pfkey_socketsp;
++ uint8_t satype = ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_satype;
++ ip_address srcflow, dstflow, srcmask, dstmask;
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_delflow_parse: .\n");
++
++ pfkey_extensions_init(extensions_reply);
++
++ memset((caddr_t)&srcflow, 0, sizeof(srcflow));
++ memset((caddr_t)&dstflow, 0, sizeof(dstflow));
++ memset((caddr_t)&srcmask, 0, sizeof(srcmask));
++ memset((caddr_t)&dstmask, 0, sizeof(dstmask));
++
++ if(!extr || !(extr->ips)) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_delflow_parse: "
++ "extr, or extr->ips is NULL, fatal\n");
++ SENDERR(EINVAL);
++ }
++
++ if(extr->ips->ips_flags & SADB_X_SAFLAGS_CLEARFLOW) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_delflow_parse: "
++ "CLEARFLOW flag set, calling cleareroutes.\n");
++ if ((error = ipsec_cleareroutes()))
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_delflow_parse: "
++ "cleareroutes returned %d.\n", error);
++ SENDERR(-error);
++ } else {
++ struct sk_buff *first = NULL, *last = NULL;
++
++ if(!(extr->eroute)) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_delflow_parse: "
++ "extr->eroute is NULL, fatal.\n");
++ SENDERR(EINVAL);
++ }
++
++ srcflow.u.v4.sin_family = AF_INET;
++ dstflow.u.v4.sin_family = AF_INET;
++ srcmask.u.v4.sin_family = AF_INET;
++ dstmask.u.v4.sin_family = AF_INET;
++ srcflow.u.v4.sin_addr = extr->eroute->er_eaddr.sen_ip_src;
++ dstflow.u.v4.sin_addr = extr->eroute->er_eaddr.sen_ip_dst;
++ srcmask.u.v4.sin_addr = extr->eroute->er_emask.sen_ip_src;
++ dstmask.u.v4.sin_addr = extr->eroute->er_emask.sen_ip_dst;
++
++#ifdef CONFIG_IPSEC_DEBUG
++ if (debug_pfkey) {
++ subnettoa(extr->eroute->er_eaddr.sen_ip_src,
++ extr->eroute->er_emask.sen_ip_src, 0, buf1, sizeof(buf1));
++ subnettoa(extr->eroute->er_eaddr.sen_ip_dst,
++ extr->eroute->er_emask.sen_ip_dst, 0, buf2, sizeof(buf2));
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_delflow_parse: "
++ "calling breakeroute for %s->%s\n",
++ buf1, buf2);
++ }
++#endif /* CONFIG_IPSEC_DEBUG */
++ error = ipsec_breakroute(&(extr->eroute->er_eaddr),
++ &(extr->eroute->er_emask),
++ &first, &last);
++ if(error) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_delflow_parse: "
++ "breakeroute returned %d. first=0p%p, last=0p%p\n",
++ error,
++ first,
++ last);
++ }
++ if(first != NULL) {
++ ipsec_kfree_skb(first);
++ }
++ if(last != NULL) {
++ ipsec_kfree_skb(last);
++ }
++ if(error) {
++ SENDERR(-error);
++ }
++ }
++
++ if(!(pfkey_safe_build(error = pfkey_msg_hdr_build(&extensions_reply[0],
++ SADB_X_DELFLOW,
++ satype,
++ 0,
++ ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_seq,
++ ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_pid),
++ extensions_reply)
++ && pfkey_safe_build(error = pfkey_sa_ref_build(&extensions_reply[SADB_EXT_SA],
++ SADB_EXT_SA,
++ extr->ips->ips_said.spi,
++ extr->ips->ips_replaywin,
++ extr->ips->ips_state,
++ extr->ips->ips_authalg,
++ extr->ips->ips_encalg,
++ extr->ips->ips_flags,
++ extr->ips->ips_ref),
++ extensions_reply)
++ && pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_X_EXT_ADDRESS_SRC_FLOW],
++ SADB_X_EXT_ADDRESS_SRC_FLOW,
++ 0, /*extr->ips->ips_said.proto,*/
++ 0,
++ (struct sockaddr*)&srcflow),
++ extensions_reply)
++ && pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_X_EXT_ADDRESS_DST_FLOW],
++ SADB_X_EXT_ADDRESS_DST_FLOW,
++ 0, /*extr->ips->ips_said.proto,*/
++ 0,
++ (struct sockaddr*)&dstflow),
++ extensions_reply)
++ && pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_X_EXT_ADDRESS_SRC_MASK],
++ SADB_X_EXT_ADDRESS_SRC_MASK,
++ 0, /*extr->ips->ips_said.proto,*/
++ 0,
++ (struct sockaddr*)&srcmask),
++ extensions_reply)
++ && pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_X_EXT_ADDRESS_DST_MASK],
++ SADB_X_EXT_ADDRESS_DST_MASK,
++ 0, /*extr->ips->ips_said.proto,*/
++ 0,
++ (struct sockaddr*)&dstmask),
++ extensions_reply)
++ )) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_x_delflow_parse: "
++ "failed to build the x_delflow reply message extensions\n");
++ SENDERR(-error);
++ }
++
++ if((error = pfkey_msg_build(&pfkey_reply, extensions_reply, EXT_BITS_OUT))) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_x_delflow_parse: "
++ "failed to build the x_delflow reply message\n");
++ SENDERR(-error);
++ }
++
++ for(pfkey_socketsp = pfkey_open_sockets;
++ pfkey_socketsp;
++ pfkey_socketsp = pfkey_socketsp->next) {
++ if((error = pfkey_upmsg(pfkey_socketsp->socketp, pfkey_reply))) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_x_delflow_parse: "
++ "sending up x_delflow reply message for satype=%d(%s) to socket=0p%p failed with error=%d.\n",
++ satype,
++ satype2name(satype),
++ pfkey_socketsp->socketp,
++ error);
++ SENDERR(-error);
++ }
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_x_delflow_parse: "
++ "sending up x_delflow reply message for satype=%d(%s) to socket=0p%p succeeded.\n",
++ satype,
++ satype2name(satype),
++ pfkey_socketsp->socketp);
++ }
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_delflow_parse: "
++ "extr->ips cleaned up and freed.\n");
++
++ errlab:
++ if (pfkey_reply) {
++ pfkey_msg_free(&pfkey_reply);
++ }
++ pfkey_extensions_free(extensions_reply);
++ return error;
++}
++
++DEBUG_NO_STATIC int
++pfkey_x_msg_debug_parse(struct sock *sk, struct sadb_ext **extensions, struct pfkey_extracted_data* extr)
++{
++ int error = 0;
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_msg_debug_parse: .\n");
++
++/* errlab:*/
++ return error;
++}
++
++/* pfkey_expire expects the ipsec_sa table to be locked before being called. */
++int
++pfkey_expire(struct ipsec_sa *ipsp, int hard)
++{
++ struct sadb_ext *extensions[SADB_EXT_MAX+1];
++ struct sadb_msg *pfkey_msg = NULL;
++ struct socket_list *pfkey_socketsp;
++ int error = 0;
++ uint8_t satype;
++
++ pfkey_extensions_init(extensions);
++
++ if(!(satype = proto2satype(ipsp->ips_said.proto))) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_expire: "
++ "satype lookup for protocol %d lookup failed.\n",
++ ipsp->ips_said.proto);
++ SENDERR(EINVAL);
++ }
++
++ if(!pfkey_open_sockets) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_expire: "
++ "no sockets listening.\n");
++ SENDERR(EPROTONOSUPPORT);
++ }
++
++ if (!(pfkey_safe_build(error = pfkey_msg_hdr_build(&extensions[0],
++ SADB_EXPIRE,
++ satype,
++ 0,
++ ++pfkey_msg_seq,
++ 0),
++ extensions)
++ && pfkey_safe_build(error = pfkey_sa_ref_build(&extensions[SADB_EXT_SA],
++ SADB_EXT_SA,
++ ipsp->ips_said.spi,
++ ipsp->ips_replaywin,
++ ipsp->ips_state,
++ ipsp->ips_authalg,
++ ipsp->ips_encalg,
++ ipsp->ips_flags,
++ ipsp->ips_ref),
++ extensions)
++ && pfkey_safe_build(error = pfkey_lifetime_build(&extensions[SADB_EXT_LIFETIME_CURRENT],
++ SADB_EXT_LIFETIME_CURRENT,
++ ipsp->ips_life.ipl_allocations.ipl_count,
++ ipsp->ips_life.ipl_bytes.ipl_count,
++ ipsp->ips_life.ipl_addtime.ipl_count,
++ ipsp->ips_life.ipl_usetime.ipl_count,
++ ipsp->ips_life.ipl_packets.ipl_count),
++ extensions)
++ && (hard ?
++ pfkey_safe_build(error = pfkey_lifetime_build(&extensions[SADB_EXT_LIFETIME_HARD],
++ SADB_EXT_LIFETIME_HARD,
++ ipsp->ips_life.ipl_allocations.ipl_hard,
++ ipsp->ips_life.ipl_bytes.ipl_hard,
++ ipsp->ips_life.ipl_addtime.ipl_hard,
++ ipsp->ips_life.ipl_usetime.ipl_hard,
++ ipsp->ips_life.ipl_packets.ipl_hard),
++ extensions)
++ : pfkey_safe_build(error = pfkey_lifetime_build(&extensions[SADB_EXT_LIFETIME_SOFT],
++ SADB_EXT_LIFETIME_SOFT,
++ ipsp->ips_life.ipl_allocations.ipl_soft,
++ ipsp->ips_life.ipl_bytes.ipl_soft,
++ ipsp->ips_life.ipl_addtime.ipl_soft,
++ ipsp->ips_life.ipl_usetime.ipl_soft,
++ ipsp->ips_life.ipl_packets.ipl_soft),
++ extensions))
++ && pfkey_safe_build(error = pfkey_address_build(&extensions[SADB_EXT_ADDRESS_SRC],
++ SADB_EXT_ADDRESS_SRC,
++ 0, /* ipsp->ips_said.proto, */
++ 0,
++ ipsp->ips_addr_s),
++ extensions)
++ && pfkey_safe_build(error = pfkey_address_build(&extensions[SADB_EXT_ADDRESS_DST],
++ SADB_EXT_ADDRESS_DST,
++ 0, /* ipsp->ips_said.proto, */
++ 0,
++ ipsp->ips_addr_d),
++ extensions))) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_expire: "
++ "failed to build the expire message extensions\n");
++ spin_unlock(&tdb_lock);
++ goto errlab;
++ }
++
++ if ((error = pfkey_msg_build(&pfkey_msg, extensions, EXT_BITS_OUT))) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_expire: "
++ "failed to build the expire message\n");
++ SENDERR(-error);
++ }
++
++ for(pfkey_socketsp = pfkey_open_sockets;
++ pfkey_socketsp;
++ pfkey_socketsp = pfkey_socketsp->next) {
++ if((error = pfkey_upmsg(pfkey_socketsp->socketp, pfkey_msg))) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_expire: "
++ "sending up expire message for satype=%d(%s) to socket=0p%p failed with error=%d.\n",
++ satype,
++ satype2name(satype),
++ pfkey_socketsp->socketp,
++ error);
++ SENDERR(-error);
++ }
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_expire: "
++ "sending up expire message for satype=%d(%s) (proto=%d) to socket=0p%p succeeded.\n",
++ satype,
++ satype2name(satype),
++ ipsp->ips_said.proto,
++ pfkey_socketsp->socketp);
++ }
++
++ errlab:
++ if (pfkey_msg) {
++ pfkey_msg_free(&pfkey_msg);
++ }
++ pfkey_extensions_free(extensions);
++ return error;
++}
++
++int
++pfkey_acquire(struct ipsec_sa *ipsp)
++{
++ struct sadb_ext *extensions[SADB_EXT_MAX+1];
++ struct sadb_msg *pfkey_msg = NULL;
++ struct socket_list *pfkey_socketsp;
++ int error = 0;
++ struct sadb_comb comb[] = {
++ /* auth; encrypt; flags; */
++ /* auth_minbits; auth_maxbits; encrypt_minbits; encrypt_maxbits; */
++ /* reserved; soft_allocations; hard_allocations; soft_bytes; hard_bytes; */
++ /* soft_addtime; hard_addtime; soft_usetime; hard_usetime; */
++ /* soft_packets; hard_packets; */
++ { SADB_AALG_MD5HMAC, SADB_EALG_3DESCBC, SADB_SAFLAGS_PFS,
++ 128, 128, 168, 168,
++ 0, 0, 0, 0, 0,
++ 57600, 86400, 57600, 86400,
++ 0, 0 },
++ { SADB_AALG_SHA1HMAC, SADB_EALG_3DESCBC, SADB_SAFLAGS_PFS,
++ 160, 160, 168, 168,
++ 0, 0, 0, 0, 0,
++ 57600, 86400, 57600, 86400,
++ 0, 0 }
++ };
++
++ /* XXX This should not be hard-coded. It should be taken from the spdb */
++ uint8_t satype = SADB_SATYPE_ESP;
++
++ pfkey_extensions_init(extensions);
++
++ if((satype == 0) || (satype > SADB_SATYPE_MAX)) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_acquire: "
++ "SAtype=%d unspecified or unknown.\n",
++ satype);
++ SENDERR(EINVAL);
++ }
++
++ if(!(pfkey_registered_sockets[satype])) {
++ KLIPS_PRINT(1|debug_pfkey, "klips_debug:pfkey_acquire: "
++ "no sockets registered for SAtype=%d(%s).\n",
++ satype,
++ satype2name(satype));
++ SENDERR(EPROTONOSUPPORT);
++ }
++
++ if (!(pfkey_safe_build(error = pfkey_msg_hdr_build(&extensions[0],
++ SADB_ACQUIRE,
++ satype,
++ 0,
++ ++pfkey_msg_seq,
++ 0),
++ extensions)
++ && pfkey_safe_build(error = pfkey_address_build(&extensions[SADB_EXT_ADDRESS_SRC],
++ SADB_EXT_ADDRESS_SRC,
++ ipsp->ips_transport_protocol,
++ 0,
++ ipsp->ips_addr_s),
++ extensions)
++ && pfkey_safe_build(error = pfkey_address_build(&extensions[SADB_EXT_ADDRESS_DST],
++ SADB_EXT_ADDRESS_DST,
++ ipsp->ips_transport_protocol,
++ 0,
++ ipsp->ips_addr_d),
++ extensions)
++#if 0
++ && (ipsp->ips_addr_p
++ ? pfkey_safe_build(error = pfkey_address_build(&extensions[SADB_EXT_ADDRESS_PROXY],
++ SADB_EXT_ADDRESS_PROXY,
++ ipsp->ips_transport_protocol,
++ 0,
++ ipsp->ips_addr_p),
++ extensions) : 1)
++#endif
++ && (ipsp->ips_ident_s.type != SADB_IDENTTYPE_RESERVED
++ ? pfkey_safe_build(error = pfkey_ident_build(&extensions[SADB_EXT_IDENTITY_SRC],
++ SADB_EXT_IDENTITY_SRC,
++ ipsp->ips_ident_s.type,
++ ipsp->ips_ident_s.id,
++ ipsp->ips_ident_s.len,
++ ipsp->ips_ident_s.data),
++ extensions) : 1)
++
++ && (ipsp->ips_ident_d.type != SADB_IDENTTYPE_RESERVED
++ ? pfkey_safe_build(error = pfkey_ident_build(&extensions[SADB_EXT_IDENTITY_DST],
++ SADB_EXT_IDENTITY_DST,
++ ipsp->ips_ident_d.type,
++ ipsp->ips_ident_d.id,
++ ipsp->ips_ident_d.len,
++ ipsp->ips_ident_d.data),
++ extensions) : 1)
++#if 0
++ /* FIXME: This won't work yet because I have not finished
++ it. */
++ && (ipsp->ips_sens_
++ ? pfkey_safe_build(error = pfkey_sens_build(&extensions[SADB_EXT_SENSITIVITY],
++ ipsp->ips_sens_dpd,
++ ipsp->ips_sens_sens_level,
++ ipsp->ips_sens_sens_len,
++ ipsp->ips_sens_sens_bitmap,
++ ipsp->ips_sens_integ_level,
++ ipsp->ips_sens_integ_len,
++ ipsp->ips_sens_integ_bitmap),
++ extensions) : 1)
++#endif
++ && pfkey_safe_build(error = pfkey_prop_build(&extensions[SADB_EXT_PROPOSAL],
++ 64, /* replay */
++ sizeof(comb)/sizeof(struct sadb_comb),
++ &(comb[0])),
++ extensions)
++ )) {
++ KLIPS_PRINT(1|debug_pfkey, "klips_debug:pfkey_acquire: "
++ "failed to build the acquire message extensions\n");
++ SENDERR(-error);
++ }
++
++ if ((error = pfkey_msg_build(&pfkey_msg, extensions, EXT_BITS_OUT))) {
++ KLIPS_PRINT(1|debug_pfkey, "klips_debug:pfkey_acquire: "
++ "failed to build the acquire message\n");
++ SENDERR(-error);
++ }
++
++#if KLIPS_PFKEY_ACQUIRE_LOSSAGE > 0
++ if(sysctl_ipsec_regress_pfkey_lossage) {
++ return(0);
++ }
++#endif
++
++ /* this should go to all registered sockets for that satype only */
++ for(pfkey_socketsp = pfkey_registered_sockets[satype];
++ pfkey_socketsp;
++ pfkey_socketsp = pfkey_socketsp->next) {
++ if((error = pfkey_upmsg(pfkey_socketsp->socketp, pfkey_msg))) {
++ KLIPS_PRINT(1|debug_pfkey, "klips_debug:pfkey_acquire: "
++ "sending up acquire message for satype=%d(%s) to socket=0p%p failed with error=%d.\n",
++ satype,
++ satype2name(satype),
++ pfkey_socketsp->socketp,
++ error);
++ SENDERR(-error);
++ }
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_acquire: "
++ "sending up acquire message for satype=%d(%s) to socket=0p%p succeeded.\n",
++ satype,
++ satype2name(satype),
++ pfkey_socketsp->socketp);
++ }
++
++ errlab:
++ if (pfkey_msg) {
++ pfkey_msg_free(&pfkey_msg);
++ }
++ pfkey_extensions_free(extensions);
++ return error;
++}
++
++#ifdef CONFIG_IPSEC_NAT_TRAVERSAL
++int
++pfkey_nat_t_new_mapping(struct ipsec_sa *ipsp, struct sockaddr *ipaddr,
++ __u16 sport)
++{
++ struct sadb_ext *extensions[SADB_EXT_MAX+1];
++ struct sadb_msg *pfkey_msg = NULL;
++ struct socket_list *pfkey_socketsp;
++ int error = 0;
++ uint8_t satype = (ipsp->ips_said.proto==IPPROTO_ESP) ? SADB_SATYPE_ESP : 0;
++
++ /* Construct SADB_X_NAT_T_NEW_MAPPING message */
++
++ pfkey_extensions_init(extensions);
++
++ if((satype == 0) || (satype > SADB_SATYPE_MAX)) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_nat_t_new_mapping: "
++ "SAtype=%d unspecified or unknown.\n",
++ satype);
++ SENDERR(EINVAL);
++ }
++
++ if(!(pfkey_registered_sockets[satype])) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_nat_t_new_mapping: "
++ "no sockets registered for SAtype=%d(%s).\n",
++ satype,
++ satype2name(satype));
++ SENDERR(EPROTONOSUPPORT);
++ }
++
++ if (!(pfkey_safe_build
++ (error = pfkey_msg_hdr_build(&extensions[0], SADB_X_NAT_T_NEW_MAPPING,
++ satype, 0, ++pfkey_msg_seq, 0), extensions)
++ /* SA */
++ && pfkey_safe_build
++ (error = pfkey_sa_build(&extensions[SADB_EXT_SA],
++ SADB_EXT_SA, ipsp->ips_said.spi, 0, 0, 0, 0, 0), extensions)
++ /* ADDRESS_SRC = old addr */
++ && pfkey_safe_build
++ (error = pfkey_address_build(&extensions[SADB_EXT_ADDRESS_SRC],
++ SADB_EXT_ADDRESS_SRC, ipsp->ips_said.proto, 0, ipsp->ips_addr_s),
++ extensions)
++ /* NAT_T_SPORT = old port */
++ && pfkey_safe_build
++ (error = pfkey_x_nat_t_port_build(&extensions[SADB_X_EXT_NAT_T_SPORT],
++ SADB_X_EXT_NAT_T_SPORT, ipsp->ips_natt_sport), extensions)
++ /* ADDRESS_DST = new addr */
++ && pfkey_safe_build
++ (error = pfkey_address_build(&extensions[SADB_EXT_ADDRESS_DST],
++ SADB_EXT_ADDRESS_DST, ipsp->ips_said.proto, 0, ipaddr), extensions)
++ /* NAT_T_DPORT = new port */
++ && pfkey_safe_build
++ (error = pfkey_x_nat_t_port_build(&extensions[SADB_X_EXT_NAT_T_DPORT],
++ SADB_X_EXT_NAT_T_DPORT, sport), extensions)
++ )) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_nat_t_new_mapping: "
++ "failed to build the nat_t_new_mapping message extensions\n");
++ SENDERR(-error);
++ }
++
++ if ((error = pfkey_msg_build(&pfkey_msg, extensions, EXT_BITS_OUT))) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_nat_t_new_mapping: "
++ "failed to build the nat_t_new_mapping message\n");
++ SENDERR(-error);
++ }
++
++ /* this should go to all registered sockets for that satype only */
++ for(pfkey_socketsp = pfkey_registered_sockets[satype];
++ pfkey_socketsp;
++ pfkey_socketsp = pfkey_socketsp->next) {
++ if((error = pfkey_upmsg(pfkey_socketsp->socketp, pfkey_msg))) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_nat_t_new_mapping: "
++ "sending up nat_t_new_mapping message for satype=%d(%s) to socket=%p failed with error=%d.\n",
++ satype,
++ satype2name(satype),
++ pfkey_socketsp->socketp,
++ error);
++ SENDERR(-error);
++ }
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_nat_t_new_mapping: "
++ "sending up nat_t_new_mapping message for satype=%d(%s) to socket=%p succeeded.\n",
++ satype,
++ satype2name(satype),
++ pfkey_socketsp->socketp);
++ }
++
++ errlab:
++ if (pfkey_msg) {
++ pfkey_msg_free(&pfkey_msg);
++ }
++ pfkey_extensions_free(extensions);
++ return error;
++}
++
++DEBUG_NO_STATIC int
++pfkey_x_nat_t_new_mapping_parse(struct sock *sk, struct sadb_ext **extensions, struct pfkey_extracted_data* extr)
++{
++ /* SADB_X_NAT_T_NEW_MAPPING not used in kernel */
++ return -EINVAL;
++}
++#endif
++
++DEBUG_NO_STATIC int (*ext_processors[SADB_EXT_MAX+1])(struct sadb_ext *pfkey_ext, struct pfkey_extracted_data* extr) =
++{
++ NULL, /* pfkey_msg_process, */
++ pfkey_sa_process,
++ pfkey_lifetime_process,
++ pfkey_lifetime_process,
++ pfkey_lifetime_process,
++ pfkey_address_process,
++ pfkey_address_process,
++ pfkey_address_process,
++ pfkey_key_process,
++ pfkey_key_process,
++ pfkey_ident_process,
++ pfkey_ident_process,
++ pfkey_sens_process,
++ pfkey_prop_process,
++ pfkey_supported_process,
++ pfkey_supported_process,
++ pfkey_spirange_process,
++ pfkey_x_kmprivate_process,
++ pfkey_x_satype_process,
++ pfkey_sa_process,
++ pfkey_address_process,
++ pfkey_address_process,
++ pfkey_address_process,
++ pfkey_address_process,
++ pfkey_address_process,
++ pfkey_x_debug_process,
++ pfkey_x_protocol_process
++#ifdef CONFIG_IPSEC_NAT_TRAVERSAL
++ ,
++ pfkey_x_nat_t_type_process,
++ pfkey_x_nat_t_port_process,
++ pfkey_x_nat_t_port_process,
++ pfkey_address_process
++#endif
++};
++
++
++DEBUG_NO_STATIC int (*msg_parsers[SADB_MAX +1])(struct sock *sk, struct sadb_ext *extensions[], struct pfkey_extracted_data* extr)
++ =
++{
++ NULL, /* RESERVED */
++ pfkey_getspi_parse,
++ pfkey_update_parse,
++ pfkey_add_parse,
++ pfkey_delete_parse,
++ pfkey_get_parse,
++ pfkey_acquire_parse,
++ pfkey_register_parse,
++ pfkey_expire_parse,
++ pfkey_flush_parse,
++ pfkey_dump_parse,
++ pfkey_x_promisc_parse,
++ pfkey_x_pchange_parse,
++ pfkey_x_grpsa_parse,
++ pfkey_x_addflow_parse,
++ pfkey_x_delflow_parse,
++ pfkey_x_msg_debug_parse
++#ifdef CONFIG_IPSEC_NAT_TRAVERSAL
++ , pfkey_x_nat_t_new_mapping_parse
++#endif
++};
++
++int
++pfkey_build_reply(struct sadb_msg *pfkey_msg, struct pfkey_extracted_data *extr,
++ struct sadb_msg **pfkey_reply)
++{
++ struct sadb_ext *extensions[SADB_EXT_MAX+1];
++ int error = 0;
++ int msg_type = pfkey_msg->sadb_msg_type;
++ int seq = pfkey_msg->sadb_msg_seq;
++
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_build_reply: "
++ "building reply with type: %d\n",
++ msg_type);
++ pfkey_extensions_init(extensions);
++ if (!extr || !extr->ips) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_build_reply: "
++ "bad ipsec_sa passed\n");
++ return EINVAL;
++ }
++ error = pfkey_safe_build(pfkey_msg_hdr_build(&extensions[0],
++ msg_type,
++ proto2satype(extr->ips->ips_said.proto),
++ 0,
++ seq,
++ pfkey_msg->sadb_msg_pid),
++ extensions) &&
++ (!(extensions_bitmaps[EXT_BITS_OUT][EXT_BITS_REQ][msg_type] &
++ 1 << SADB_EXT_SA)
++ || pfkey_safe_build(pfkey_sa_ref_build(&extensions[SADB_EXT_SA],
++ SADB_EXT_SA,
++ extr->ips->ips_said.spi,
++ extr->ips->ips_replaywin,
++ extr->ips->ips_state,
++ extr->ips->ips_authalg,
++ extr->ips->ips_encalg,
++ extr->ips->ips_flags,
++ extr->ips->ips_ref),
++ extensions)) &&
++ (!(extensions_bitmaps[EXT_BITS_OUT][EXT_BITS_REQ][msg_type] &
++ 1 << SADB_EXT_LIFETIME_CURRENT)
++ || pfkey_safe_build(pfkey_lifetime_build(&extensions
++ [SADB_EXT_LIFETIME_CURRENT],
++ SADB_EXT_LIFETIME_CURRENT,
++ extr->ips->ips_life.ipl_allocations.ipl_count,
++ extr->ips->ips_life.ipl_bytes.ipl_count,
++ extr->ips->ips_life.ipl_addtime.ipl_count,
++ extr->ips->ips_life.ipl_usetime.ipl_count,
++ extr->ips->ips_life.ipl_packets.ipl_count),
++ extensions)) &&
++ (!(extensions_bitmaps[EXT_BITS_OUT][EXT_BITS_REQ][msg_type] &
++ 1 << SADB_EXT_ADDRESS_SRC)
++ || pfkey_safe_build(pfkey_address_build(&extensions[SADB_EXT_ADDRESS_SRC],
++ SADB_EXT_ADDRESS_SRC,
++ extr->ips->ips_said.proto,
++ 0,
++ extr->ips->ips_addr_s),
++ extensions)) &&
++ (!(extensions_bitmaps[EXT_BITS_OUT][EXT_BITS_REQ][msg_type] &
++ 1 << SADB_EXT_ADDRESS_DST)
++ || pfkey_safe_build(pfkey_address_build(&extensions[SADB_EXT_ADDRESS_DST],
++ SADB_EXT_ADDRESS_DST,
++ extr->ips->ips_said.proto,
++ 0,
++ extr->ips->ips_addr_d),
++ extensions));
++
++ if (error == 0) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_build_reply: "
++ "building extensions failed\n");
++ return EINVAL;
++ }
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_build_reply: "
++ "built extensions, proceed to build the message\n");
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_build_reply: "
++ "extensions[1]=0p%p\n",
++ extensions[1]);
++ error = pfkey_msg_build(pfkey_reply, extensions, EXT_BITS_OUT);
++ pfkey_extensions_free(extensions);
++
++ return error;
++}
++
++int
++pfkey_msg_interp(struct sock *sk, struct sadb_msg *pfkey_msg,
++ struct sadb_msg **pfkey_reply)
++{
++ int error = 0;
++ int i;
++ struct sadb_ext *extensions[SADB_EXT_MAX+1];
++ struct pfkey_extracted_data extr = {NULL, NULL, NULL};
++
++ pfkey_extensions_init(extensions);
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_msg_interp: "
++ "parsing message ver=%d, type=%d, errno=%d, satype=%d(%s), len=%d, res=%d, seq=%d, pid=%d.\n",
++ pfkey_msg->sadb_msg_version,
++ pfkey_msg->sadb_msg_type,
++ pfkey_msg->sadb_msg_errno,
++ pfkey_msg->sadb_msg_satype,
++ satype2name(pfkey_msg->sadb_msg_satype),
++ pfkey_msg->sadb_msg_len,
++ pfkey_msg->sadb_msg_reserved,
++ pfkey_msg->sadb_msg_seq,
++ pfkey_msg->sadb_msg_pid);
++
++ extr.ips = ipsec_sa_alloc(&error); /* pass in error var by pointer */
++ if(extr.ips == NULL) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_msg_interp: "
++ "memory allocation error.\n");
++ SENDERR(-error);
++ }
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_msg_interp: "
++ "allocated extr->ips=0p%p.\n",
++ extr.ips);
++
++ if(pfkey_msg->sadb_msg_satype > SADB_SATYPE_MAX) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_msg_interp: "
++ "satype %d > max %d\n",
++ pfkey_msg->sadb_msg_satype,
++ SADB_SATYPE_MAX);
++ SENDERR(EINVAL);
++ }
++
++ switch(pfkey_msg->sadb_msg_type) {
++ case SADB_GETSPI:
++ case SADB_UPDATE:
++ case SADB_ADD:
++ case SADB_DELETE:
++ case SADB_X_GRPSA:
++ case SADB_X_ADDFLOW:
++
++ if(!(extr.ips->ips_said.proto = satype2proto(pfkey_msg->sadb_msg_satype))) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_msg_interp: "
++ "satype %d lookup failed.\n",
++ pfkey_msg->sadb_msg_satype);
++ SENDERR(EINVAL);
++ } else {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_msg_interp: "
++ "satype %d lookups to proto=%d.\n",
++ pfkey_msg->sadb_msg_satype,
++ extr.ips->ips_said.proto);
++ }
++ break;
++ default:
++ break;
++ }
++
++ /* The NULL below causes the default extension parsers to be used */
++ /* Parse the extensions */
++ if((error = pfkey_msg_parse(pfkey_msg, NULL, extensions, EXT_BITS_IN)))
++ {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_msg_interp: "
++ "message parsing failed with error %d.\n",
++ error);
++ SENDERR(-error);
++ }
++
++ /* Process the extensions */
++ for(i=1; i <= SADB_EXT_MAX;i++) {
++ if(extensions[i] != NULL) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_msg_interp: "
++ "processing ext %d 0p%p with processor 0p%p.\n",
++ i, extensions[i], ext_processors[i]);
++ if((error = ext_processors[i](extensions[i], &extr))) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_msg_interp: "
++ "extension processing for type %d failed with error %d.\n",
++ i,
++ error);
++ SENDERR(-error);
++ }
++
++ }
++
++ }
++
++ /* Parse the message types */
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_msg_interp: "
++ "parsing message type %d(%s) with msg_parser 0p%p.\n",
++ pfkey_msg->sadb_msg_type,
++ pfkey_v2_sadb_type_string(pfkey_msg->sadb_msg_type),
++ msg_parsers[pfkey_msg->sadb_msg_type]);
++ if((error = msg_parsers[pfkey_msg->sadb_msg_type](sk, extensions, &extr))) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_msg_interp: "
++ "message parsing failed with error %d.\n",
++ error);
++ SENDERR(-error);
++ }
++
++#if 0
++ error = pfkey_build_reply(pfkey_msg, &extr, pfkey_reply);
++ if (error) {
++ *pfkey_reply = NULL;
++ }
++#endif
++ errlab:
++ if(extr.ips != NULL) {
++ ipsec_sa_wipe(extr.ips);
++ }
++ if(extr.ips2 != NULL) {
++ ipsec_sa_wipe(extr.ips2);
++ }
++ if (extr.eroute != NULL) {
++ kfree(extr.eroute);
++ }
++ return(error);
++}
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.123 2004/04/06 02:49:26 mcr
++ * pullup of algo code from alg-branch.
++ *
++ * Revision 1.122.2.2 2004/04/05 04:30:46 mcr
++ * patches for alg-branch to compile/work with 2.x openswan
++ *
++ * Revision 1.122.2.1 2003/12/22 15:25:52 jjo
++ * . Merged algo-0.8.1-rc11-test1 into alg-branch
++ *
++ * Revision 1.122 2003/12/10 01:14:27 mcr
++ * NAT-traversal patches to KLIPS.
++ *
++ * Revision 1.121 2003/10/31 02:27:55 mcr
++ * pulled up port-selector patches and sa_id elimination.
++ *
++ * Revision 1.120.4.2 2003/10/29 01:30:41 mcr
++ * elimited "struct sa_id".
++ *
++ * Revision 1.120.4.1 2003/09/21 13:59:56 mcr
++ * pre-liminary X.509 patch - does not yet pass tests.
++ *
++ * Revision 1.120 2003/04/03 17:38:09 rgb
++ * Centralised ipsec_kfree_skb and ipsec_dev_{get,put}.
++ *
++ * Revision 1.119 2003/02/06 01:52:37 rgb
++ * Removed no longer relevant comment
++ *
++ * Revision 1.118 2003/01/30 02:32:44 rgb
++ *
++ * Transmit error code through to caller from callee for better diagnosis of problems.
++ *
++ * Revision 1.117 2003/01/16 18:48:13 rgb
++ *
++ * Fixed sign bug in error return from an sa allocation call in
++ * pfkey_msg_interp.
++ *
++ * Revision 1.116 2002/10/17 16:38:01 rgb
++ * Change pfkey_alloc_eroute() to never static since its consumers
++ * have been moved outside the file.
++ *
++ * Revision 1.115 2002/10/12 23:11:53 dhr
++ *
++ * [KenB + DHR] more 64-bit cleanup
++ *
++ * Revision 1.114 2002/10/05 05:02:58 dhr
++ *
++ * C labels go on statements
++ *
++ * Revision 1.113 2002/09/30 19:11:22 rgb
++ * Turn on debugging for upgoing acquire messages to test for reliability.
++ *
++ * Revision 1.112 2002/09/20 15:41:16 rgb
++ * Switch from pfkey_alloc_ipsec_sa() to ipsec_sa_alloc().
++ * Added sadb_x_sa_ref to struct sadb_sa.
++ * Added ref parameter to pfkey_sa_build().
++ *
++ * Revision 1.111 2002/09/20 05:02:08 rgb
++ * Added memory allocation debugging.
++ * Convert to switch to divulge hmac keys for debugging.
++ * Added text labels to elucidate numeric values presented.
++ *
++ * Revision 1.110 2002/08/03 18:03:05 mcr
++ * loop that checks for SPI's to have been already linked
++ * fails to actually step to next pointer, but continuously
++ * resets to head of list. Wrong pointer used.
++ * test east-icmp-02 revealed this.
++ *
++ * Revision 1.109 2002/07/26 08:48:31 rgb
++ * Added SA ref table code.
++ *
++ * Revision 1.108 2002/05/27 18:55:03 rgb
++ * Remove final vistiges of tdb references via IPSEC_KLIPS1_COMPAT.
++ *
++ * Revision 1.107 2002/05/23 07:16:08 rgb
++ * Added ipsec_sa_put() for releasing an ipsec_sa refcount.
++ * Pointer clean-up.
++ * Added refcount code.
++ *
++ * Revision 1.106 2002/05/14 02:34:13 rgb
++ * Converted reference from ipsec_sa_put to ipsec_sa_add to avoid confusion
++ * with "put" usage in the kernel.
++ * Change all references to tdb, TDB or Tunnel Descriptor Block to ips,
++ * ipsec_sa or ipsec_sa.
++ * Moved all the extension parsing functions to pfkey_v2_ext_process.c.
++ *
++ * Revision 1.105 2002/04/24 07:55:32 mcr
++ * #include patches and Makefiles for post-reorg compilation.
++ *
++ * Revision 1.104 2002/04/24 07:36:34 mcr
++ * Moved from ./klips/net/ipsec/pfkey_v2_parser.c,v
++ *
++ * Revision 1.103 2002/04/20 00:12:25 rgb
++ * Added esp IV CBC attack fix, disabled.
++ *
++ * Revision 1.102 2002/03/08 01:15:17 mcr
++ * put some internal structure only debug messages behind
++ * && sysctl_ipsec_debug_verbose.
++ *
++ * Revision 1.101 2002/01/29 17:17:57 mcr
++ * moved include of ipsec_param.h to after include of linux/kernel.h
++ * otherwise, it seems that some option that is set in ipsec_param.h
++ * screws up something subtle in the include path to kernel.h, and
++ * it complains on the snprintf() prototype.
++ *
++ * Revision 1.100 2002/01/29 04:00:54 mcr
++ * more excise of kversions.h header.
++ *
++ * Revision 1.99 2002/01/29 02:13:19 mcr
++ * introduction of ipsec_kversion.h means that include of
++ * ipsec_param.h must preceed any decisions about what files to
++ * include to deal with differences in kernel source.
++ *
++ * Revision 1.98 2002/01/12 02:57:57 mcr
++ * first regression test causes acquire messages to be lost
++ * 100% of the time. This is to help testing of pluto.
++ *
++ * Revision 1.97 2001/11/26 09:23:52 rgb
++ * Merge MCR's ipsec_sa, eroute, proc and struct lifetime changes.
++ *
++ * Revision 1.93.2.4 2001/10/23 04:20:27 mcr
++ * parity was forced on wrong structure! prototypes help here.
++ *
++ * Revision 1.93.2.3 2001/10/22 21:14:59 mcr
++ * include des.h, removed phony prototypes and fixed calling
++ * conventions to match real prototypes.
++ *
++ * Revision 1.93.2.2 2001/10/15 05:39:03 mcr
++ * %08lx is not the right format for u32. Use %08x. 64-bit safe? ha.
++ *
++ * Revision 1.93.2.1 2001/09/25 02:30:14 mcr
++ * struct tdb -> struct ipsec_sa.
++ * use new lifetime structure. common format routines for debug.
++ *
++ * Revision 1.96 2001/11/06 20:47:54 rgb
++ * Fixed user context call to ipsec_dev_start_xmit() bug. Call
++ * dev_queue_xmit() instead.
++ *
++ * Revision 1.95 2001/11/06 19:47:46 rgb
++ * Added packet parameter to lifetime and comb structures.
++ *
++ * Revision 1.94 2001/10/18 04:45:23 rgb
++ * 2.4.9 kernel deprecates linux/malloc.h in favour of linux/slab.h,
++ * lib/freeswan.h version macros moved to lib/kversions.h.
++ * Other compiler directive cleanups.
++ *
++ * Revision 1.93 2001/09/20 15:32:59 rgb
++ * Min/max cleanup.
++ *
++ * Revision 1.92 2001/09/19 16:35:48 rgb
++ * PF_KEY ident fix for getspi from NetCelo (puttdb duplication).
++ *
++ * Revision 1.91 2001/09/15 16:24:06 rgb
++ * Re-inject first and last HOLD packet when an eroute REPLACE is done.
++ *
++ * Revision 1.90 2001/09/14 16:58:38 rgb
++ * Added support for storing the first and last packets through a HOLD.
++ *
++ * Revision 1.89 2001/09/08 21:14:07 rgb
++ * Added pfkey ident extension support for ISAKMPd. (NetCelo)
++ * Better state coherency (error management) between pf_key and IKE daemon.
++ * (NetCelo)
++ *
++ * Revision 1.88 2001/08/27 19:42:44 rgb
++ * Fix memory leak of encrypt and auth structs in pfkey register.
++ *
++ * Revision 1.87 2001/07/06 19:50:46 rgb
++ * Removed unused debugging code.
++ * Added inbound policy checking code for IPIP SAs.
++ *
++ * Revision 1.86 2001/06/20 06:26:04 rgb
++ * Changed missing SA errors from EEXIST to ENOENT and added debug output
++ * for already linked SAs.
++ *
++ * Revision 1.85 2001/06/15 04:57:02 rgb
++ * Remove single error return condition check and check for all errors in
++ * the case of a replace eroute delete operation. This means that
++ * applications must expect to be deleting something before replacing it
++ * and if nothing is found, complain.
++ *
++ * Revision 1.84 2001/06/14 19:35:12 rgb
++ * Update copyright date.
++ *
++ * Revision 1.83 2001/06/12 00:03:19 rgb
++ * Silence debug set/unset under normal conditions.
++ *
++ * Revision 1.82 2001/05/30 08:14:04 rgb
++ * Removed vestiges of esp-null transforms.
++ *
++ * Revision 1.81 2001/05/27 06:12:12 rgb
++ * Added structures for pid, packet count and last access time to eroute.
++ * Added packet count to beginning of /proc/net/ipsec_eroute.
++ *
++ * Revision 1.80 2001/05/03 19:43:59 rgb
++ * Check error return codes for all build function calls.
++ * Standardise on SENDERR() macro.
++ *
++ * Revision 1.79 2001/04/20 21:09:16 rgb
++ * Cleaned up fixed tdbwipes.
++ * Free pfkey_reply and clean up extensions_reply for grpsa, addflow and
++ * delflow (Per Cederqvist) plugging memleaks.
++ *
++ * Revision 1.78 2001/04/19 19:02:39 rgb
++ * Fixed extr.tdb freeing, stealing it for getspi, update and add.
++ * Refined a couple of spinlocks, fixed the one in update.
++ *
++ * Revision 1.77 2001/04/18 20:26:16 rgb
++ * Wipe/free eroute and both tdbs from extr at end of pfkey_msg_interp()
++ * instead of inside each message type parser. This fixes two memleaks.
++ *
++ * Revision 1.76 2001/04/17 23:51:18 rgb
++ * Quiet down pfkey_x_debug_process().
++ *
++ * Revision 1.75 2001/03/29 01:55:05 rgb
++ * Fixed pfkey key init memleak.
++ * Fixed pfkey encryption key debug output.
++ *
++ * Revision 1.74 2001/03/27 05:29:14 rgb
++ * Debug output cleanup/silencing.
++ *
++ * Revision 1.73 2001/02/28 05:03:28 rgb
++ * Clean up and rationalise startup messages.
++ *
++ * Revision 1.72 2001/02/27 22:24:56 rgb
++ * Re-formatting debug output (line-splitting, joining, 1arg/line).
++ * Check for satoa() return codes.
++ *
++ * Revision 1.71 2001/02/27 06:59:30 rgb
++ * Added satype2name() conversions most places satype is debug printed.
++ *
++ * Revision 1.70 2001/02/26 22:37:08 rgb
++ * Fixed 'unknown proto' INT bug in new code.
++ * Added satype to protocol debugging instrumentation.
++ *
++ * Revision 1.69 2001/02/26 19:57:51 rgb
++ * Re-formatted debug output (split lines, consistent spacing).
++ * Fixed as yet undetected FLUSH bug which called ipsec_tdbcleanup()
++ * with an satype instead of proto.
++ * Checked for satype consistency and fixed minor bugs.
++ * Fixed undetected ungrpspi bug that tried to upmsg a second tdb.
++ * Check for satype sanity in pfkey_expire().
++ * Added satype sanity check to addflow.
++ *
++ * Revision 1.68 2001/02/12 23:14:40 rgb
++ * Remove double spin lock in pfkey_expire().
++ *
++ * Revision 1.67 2001/01/31 19:23:40 rgb
++ * Fixed double-unlock bug introduced by grpsa upmsg (found by Lars Heete).
++ *
++ * Revision 1.66 2001/01/29 22:20:04 rgb
++ * Fix minor add upmsg lifetime bug.
++ *
++ * Revision 1.65 2001/01/24 06:12:33 rgb
++ * Fixed address extension compile bugs just introduced.
++ *
++ * Revision 1.64 2001/01/24 00:31:15 rgb
++ * Added upmsg for addflow/delflow.
++ *
++ * Revision 1.63 2001/01/23 22:02:55 rgb
++ * Added upmsg to x_grpsa.
++ * Fixed lifetimes extentions to add/update/get upmsg.
++ *
++ * Revision 1.62 2000/11/30 21:47:51 rgb
++ * Fix error return bug after returning from pfkey_tdb_init().
++ *
++ * Revision 1.61 2000/11/17 18:10:29 rgb
++ * Fixed bugs mostly relating to spirange, to treat all spi variables as
++ * network byte order since this is the way PF_KEYv2 stored spis.
++ *
++ * Revision 1.60 2000/11/06 04:34:53 rgb
++ * Changed non-exported functions to DEBUG_NO_STATIC.
++ * Add Svenning's adaptive content compression.
++ * Ditched spin_lock_irqsave in favour of spin_lock/_bh.
++ * Fixed double unlock bug (Svenning).
++ * Fixed pfkey_msg uninitialized bug in pfkey_{expire,acquire}().
++ * Fixed incorrect extension type (prop) in pfkey)acquire().
++ *
++ * Revision 1.59 2000/10/11 15:25:12 rgb
++ * Fixed IPCOMP disabled compile bug.
++ *
++ * Revision 1.58 2000/10/11 14:54:03 rgb
++ * Fixed pfkey_acquire() satype to SADB_SATYPE_ESP and removed pfkey
++ * protocol violations of setting pfkey_address_build() protocol parameter
++ * to non-zero except in the case of pfkey_acquire().
++ *
++ * Revision 1.57 2000/10/10 20:10:18 rgb
++ * Added support for debug_ipcomp and debug_verbose to klipsdebug.
++ *
++ * Revision 1.56 2000/10/06 20:24:36 rgb
++ * Fixes to pfkey_acquire to initialize extensions[] and use correct
++ * ipproto.
++ *
++ * Revision 1.55 2000/10/03 03:20:57 rgb
++ * Added brackets to get a?b:c scope right for pfkey_register reply.
++ *
++ * Revision 1.54 2000/09/29 19:49:30 rgb
++ * As-yet-unused-bits cleanup.
++ *
++ * Revision 1.53 2000/09/28 00:35:45 rgb
++ * Padded SATYPE printout in pfkey_register for vertical alignment.
++ *
++ * Revision 1.52 2000/09/20 16:21:58 rgb
++ * Cleaned up ident string alloc/free.
++ *
++ * Revision 1.51 2000/09/20 04:04:20 rgb
++ * Changed static functions to DEBUG_NO_STATIC to reveal function names in
++ * oopsen.
++ *
++ * Revision 1.50 2000/09/16 01:10:53 rgb
++ * Fixed unused var warning with debug off.
++ *
++ * Revision 1.49 2000/09/15 11:37:02 rgb
++ * Merge in heavily modified Svenning Soerensen's <svenning@post5.tele.dk>
++ * IPCOMP zlib deflate code.
++ *
++ * Revision 1.48 2000/09/15 04:57:57 rgb
++ * Cleaned up existing IPCOMP code before svenning addition.
++ * Initialize pfkey_reply and extensions_reply in case of early error in
++ * message parsing functions (thanks Kai!).
++ *
++ * Revision 1.47 2000/09/13 08:02:56 rgb
++ * Added KMd registration notification.
++ *
++ * Revision 1.46 2000/09/12 22:35:36 rgb
++ * Restructured to remove unused extensions from CLEARFLOW messages.
++ *
++ * Revision 1.45 2000/09/12 03:24:23 rgb
++ * Converted #if0 debugs to sysctl.
++ *
++ * Revision 1.44 2000/09/09 06:38:39 rgb
++ * Correct SADB message type for update, add and delete.
++ *
++ * Revision 1.43 2000/09/08 19:19:56 rgb
++ * Change references from DEBUG_IPSEC to CONFIG_IPSEC_DEBUG.
++ * Removed all references to CONFIG_IPSEC_PFKEYv2.
++ * Put in sanity checks in most msg type parsers to catch invalid satypes
++ * and empty socket lists.
++ * Moved spin-locks in pfkey_get_parse() to simplify.
++ * Added pfkey_acquire().
++ * Added upwards messages to update, add, delete, acquire_parse,
++ * expire_parse and flush.
++ * Fix pfkey_prop_build() parameter to be only single indirection.
++ * Changed all replies to use pfkey_reply.
++ * Check return code on puttdb() and deltdbchain() in getspi, update,
++ * add, delete.
++ * Fixed up all pfkey replies to open and registered sockets.
++ *
++ * Revision 1.42 2000/09/01 18:50:26 rgb
++ * Added a supported algorithms array lists, one per satype and registered
++ * existing algorithms.
++ * Fixed pfkey_list_{insert,remove}_{socket,support}() to allow change to
++ * list.
++ * Only send pfkey_expire() messages to sockets registered for that satype.
++ * Added reply to pfkey_getspi_parse().
++ * Added reply to pfkey_get_parse().
++ * Fixed debug output label bug in pfkey_lifetime_process().
++ * Cleaned up pfkey_sa_process a little.
++ * Moved pfkey_safe_build() above message type parsers to make it available
++ * for creating replies.
++ * Added comments for future work in pfkey_acquire_parse().
++ * Fleshed out guts of pfkey_register_parse().
++ *
++ * Revision 1.41 2000/08/24 16:58:11 rgb
++ * Fixed key debugging variables.
++ * Fixed error return code for a failed search.
++ * Changed order of pfkey_get operations.
++ *
++ * Revision 1.40 2000/08/21 16:32:27 rgb
++ * Re-formatted for cosmetic consistency and readability.
++ *
++ * Revision 1.39 2000/08/20 21:38:57 rgb
++ * Bugfixes to as-yet-unused pfkey_update_parse() and
++ * pfkey_register_parse(). (Momchil)
++ * Added functions pfkey_safe_build(), pfkey_expire() and
++ * pfkey_build_reply(). (Momchil)
++ * Added a pfkey_reply parameter to pfkey_msg_interp(). (Momchil)
++ *
++ * Revision 1.38 2000/08/18 21:30:41 rgb
++ * Purged all tdb_spi, tdb_proto and tdb_dst macros. They are unclear.
++ *
++ * Revision 1.37 2000/08/18 18:18:02 rgb
++ * Cosmetic and descriptive changes made to debug test.
++ * getspi and update fixes from Momchil.
++ *
++ * Revision 1.36 2000/08/15 15:41:55 rgb
++ * Fixed the (as yet unused and untested) pfkey_getspi() routine.
++ *
++ * Revision 1.35 2000/08/01 14:51:52 rgb
++ * Removed _all_ remaining traces of DES.
++ *
++ * Revision 1.34 2000/07/28 14:58:32 rgb
++ * Changed kfree_s to kfree, eliminating extra arg to fix 2.4.0-test5.
++ *
++ * Revision 1.33 2000/06/28 05:50:11 rgb
++ * Actually set iv_bits.
++ *
++ * Revision 1.32 2000/05/30 18:36:56 rgb
++ * Fix AH auth hash setup bug. This breaks interop with previous PF_KEY
++ * FreeS/WAN, but fixes interop with other implementations.
++ *
++ * Revision 1.31 2000/03/16 14:05:48 rgb
++ * Fixed brace scope preventing non-debug compile.
++ * Added null parameter check for pfkey_x_debug().
++ *
++ * Revision 1.30 2000/01/22 23:21:13 rgb
++ * Use new function satype2proto().
++ *
++ * Revision 1.29 2000/01/22 08:40:21 rgb
++ * Invert condition to known value to avoid AF_INET6 in 2.0.36.
++ *
++ * Revision 1.28 2000/01/22 07:58:57 rgb
++ * Fixed REPLACEFLOW bug, missing braces around KLIPS_PRINT *and* SENDERR.
++ *
++ * Revision 1.27 2000/01/22 03:48:01 rgb
++ * Added extr pointer component debugging.
++ *
++ * Revision 1.26 2000/01/21 09:41:25 rgb
++ * Changed a (void*) to (char*) cast to do proper pointer math.
++ * Don't call tdbwipe if tdb2 is NULL.
++ *
++ * Revision 1.25 2000/01/21 06:21:01 rgb
++ * Added address cases for eroute flows.
++ * Tidied up compiler directive indentation for readability.
++ * Added ictx,octx vars for simplification.
++ * Added macros for HMAC padding magic numbers.
++ * Converted from double tdb arguments to one structure (extr)
++ * containing pointers to all temporary information structures
++ * and checking for valid arguments to all ext processors and
++ * msg type parsers.
++ * Added spiungrp'ing.
++ * Added klipsdebug switching capability.
++ * Removed sa_process() check for zero protocol.
++ * Added address case for DST2 for grouping.
++ * Added/changed minor debugging instrumentation.
++ * Fixed spigrp for single said, ungrouping case.
++ * Added code to parse addflow and delflow messages.
++ * Removed redundant statements duplicating tdbwipe() functionality
++ * and causing double kfrees.
++ * Permit addflow to have a protocol of 0.
++ *
++ * Revision 1.24 1999/12/09 23:23:00 rgb
++ * Added check to pfkey_sa_process() to do eroutes.
++ * Converted to DIVUP() macro.
++ * Converted if() to switch() in pfkey_register_parse().
++ * Use new pfkey_extensions_init() instead of memset().
++ *
++ * Revision 1.23 1999/12/01 22:18:13 rgb
++ * Preset minspi and maxspi values in case and spirange extension is not
++ * included and check for the presence of an spirange extension before
++ * using it. Initialise tdb_sastate to LARVAL.
++ * Fixed debugging output typo.
++ * Fixed authentication context initialisation bugs (4 places).
++ *
++ * Revision 1.22 1999/11/27 11:53:08 rgb
++ * Moved pfkey_msg_parse prototype to pfkey.h
++ * Moved exts_permitted/required prototype to pfkey.h.
++ * Moved sadb_satype2proto protocol lookup table to lib/pfkey_v2_parse.c.
++ * Deleted SADB_X_EXT_SA2 code from pfkey_sa_process() since it will never
++ * be called.
++ * Moved protocol/algorithm checks to lib/pfkey_v2_parse.c
++ * Debugging error messages added.
++ * Enable lifetime_current checking.
++ * Remove illegal requirement for SA extension to be present in an
++ * originating GETSPI call.
++ * Re-instate requirement for UPDATE or ADD message to be MATURE.
++ * Add argument to pfkey_msg_parse() for direction.
++ * Fixed IPIP dst address bug and purged redundant, leaky code.
++ *
++ * Revision 1.21 1999/11/24 05:24:20 rgb
++ * hanged 'void*extensions' to 'struct sadb_ext*extensions'.
++ * Fixed indention.
++ * Ditched redundant replay check.
++ * Fixed debug message text from 'parse' to 'process'.
++ * Added more debug output.
++ * Forgot to zero extensions array causing bug, fixed.
++ *
++ * Revision 1.20 1999/11/23 23:08:13 rgb
++ * Move all common parsing code to lib/pfkey_v2_parse.c and rename
++ * remaining bits to *_process. (PJO)
++ * Add macros for dealing with alignment and rounding up more opaquely.
++ * Use provided macro ADDRTOA_BUF instead of hardcoded value.
++ * Sort out pfkey and freeswan headers, putting them in a library path.
++ * Corrected a couple of bugs in as-yet-inactive code.
++ *
++ * Revision 1.19 1999/11/20 22:01:10 rgb
++ * Add more descriptive error messages for non-zero reserved fields.
++ * Add more descriptive error message for spirange parsing.
++ * Start on supported extension parsing.
++ * Start on register and get message parsing.
++ *
++ * Revision 1.18 1999/11/18 04:09:20 rgb
++ * Replaced all kernel version macros to shorter, readable form.
++ *
++ * Revision 1.17 1999/11/17 15:53:41 rgb
++ * Changed all occurrences of #include "../../../lib/freeswan.h"
++ * to #include <freeswan.h> which works due to -Ilibfreeswan in the
++ * klips/net/ipsec/Makefile.
++ *
++ * Revision 1.16 1999/10/26 16:57:43 rgb
++ * Add shorter macros for compiler directives to visually clean-up.
++ * Give ipv6 code meaningful compiler directive.
++ * Add comments to other #if 0 debug code.
++ * Remove unused *_bh_atomic() calls.
++ * Fix mis-placed spinlock.
++ *
++ * Revision 1.15 1999/10/16 18:27:10 rgb
++ * Clean-up unused cruft.
++ * Fix-up lifetime_allocations_c and lifetime_addtime_c initialisations.
++ *
++ * Revision 1.14 1999/10/08 18:37:34 rgb
++ * Fix end-of-line spacing to sate whining PHMs.
++ *
++ * Revision 1.13 1999/10/03 18:49:12 rgb
++ * Spinlock fixes for 2.0.xx and 2.3.xx.
++ *
++ * Revision 1.12 1999/10/01 15:44:54 rgb
++ * Move spinlock header include to 2.1> scope.
++ *
++ * Revision 1.11 1999/10/01 00:05:45 rgb
++ * Added tdb structure locking.
++ * Use 'jiffies' instead of do_get_timeofday().
++ * Fix lifetime assignments.
++ *
++ * Revision 1.10 1999/09/21 15:24:45 rgb
++ * Rework spirange code to save entropy and prevent endless loops.
++ *
++ * Revision 1.9 1999/09/16 12:10:21 rgb
++ * Minor fixes to random spi selection for correctness and entropy conservation.
++ *
++ * Revision 1.8 1999/05/25 22:54:46 rgb
++ * Fix comparison that should be an assignment in an if.
++ *
++ * Revision 1.7 1999/05/09 03:25:37 rgb
++ * Fix bug introduced by 2.2 quick-and-dirty patch.
++ *
++ * Revision 1.6 1999/05/08 21:32:30 rgb
++ * Fix error return reporting.
++ *
++ * Revision 1.5 1999/05/05 22:02:33 rgb
++ * Add a quick and dirty port to 2.2 kernels by Marc Boucher <marc@mbsi.ca>.
++ *
++ * Revision 1.4 1999/04/29 15:22:40 rgb
++ * Standardise an error return method.
++ * Add debugging instrumentation.
++ * Add check for existence of macros min/max.
++ * Add extensions permitted/required in/out filters.
++ * Add satype-to-protocol table.
++ * Add a second tdb pointer to each parser to accomodate GRPSA.
++ * Move AH & no_algo_set to GETSPI, UPDATE and ADD.
++ * Add OOO window check.
++ * Add support for IPPROTO_IPIP and hooks for IPPROTO_COMP.
++ * Add timestamp to lifetime parse.
++ * Fix address structure length checking bug.
++ * Fix address structure allocation bug (forgot to kmalloc!).
++ * Add checks for extension lengths.
++ * Add checks for extension reserved illegal values.
++ * Add check for spirange legal values.
++ * Add an extension type for parsing a second satype, SA and
++ * DST_ADDRESS.
++ * Make changes to tdb_init() template to get pfkey_tdb_init(),
++ * eliminating any mention of xformsw.
++ * Implement getspi, update and grpsa (not tested).
++ * Add stubs for as yet unimplemented message types.
++ * Add table of message parsers to substitute for msg_parse switch.
++ *
++ * Revision 1.3 1999/04/15 17:58:07 rgb
++ * Add RCSID labels.
++ *
++ * Revision 1.2 1999/04/15 15:37:26 rgb
++ * Forward check changes from POST1_00 branch.
++ *
++ * Revision 1.1.2.1 1999/03/26 20:58:56 rgb
++ * Add pfkeyv2 support to KLIPS.
++ *
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/net/ipsec/radij.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,1225 @@
++char radij_c_version[] = "RCSID $Id$";
++
++/*
++ * This file is defived from ${SRC}/sys/net/radix.c of BSD 4.4lite
++ *
++ * Variable and procedure names have been modified so that they don't
++ * conflict with the original BSD code, as a small number of modifications
++ * have been introduced and we may want to reuse this code in BSD.
++ *
++ * The `j' in `radij' is pronounced as a voiceless guttural (like a Greek
++ * chi or a German ch sound (as `doch', not as in `milch'), or even a
++ * spanish j as in Juan. It is not as far back in the throat like
++ * the corresponding Hebrew sound, nor is it a soft breath like the English h.
++ * It has nothing to do with the Dutch ij sound.
++ *
++ * Here is the appropriate copyright notice:
++ */
++
++/*
++ * Copyright (c) 1988, 1989, 1993
++ * The Regents of the University of California. All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ * 1. Redistributions of source code must retain the above copyright
++ * notice, this list of conditions and the following disclaimer.
++ * 2. Redistributions in binary form must reproduce the above copyright
++ * notice, this list of conditions and the following disclaimer in the
++ * documentation and/or other materials provided with the distribution.
++ * 3. All advertising materials mentioning features or use of this software
++ * must display the following acknowledgement:
++ * This product includes software developed by the University of
++ * California, Berkeley and its contributors.
++ * 4. Neither the name of the University nor the names of its contributors
++ * may be used to endorse or promote products derived from this software
++ * without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ *
++ * @(#)radix.c 8.2 (Berkeley) 1/4/94
++ */
++
++/*
++ * Routines to build and maintain radix trees for routing lookups.
++ */
++
++#include <linux/config.h>
++#include <linux/version.h>
++#include <linux/kernel.h> /* printk() */
++
++#include "openswan/ipsec_param.h"
++
++#ifdef MALLOC_SLAB
++# include <linux/slab.h> /* kmalloc() */
++#else /* MALLOC_SLAB */
++# include <linux/malloc.h> /* kmalloc() */
++#endif /* MALLOC_SLAB */
++#include <linux/errno.h> /* error codes */
++#include <linux/types.h> /* size_t */
++#include <linux/interrupt.h> /* mark_bh */
++
++#include <linux/netdevice.h> /* struct device, and other headers */
++#include <linux/etherdevice.h> /* eth_type_trans */
++#include <linux/ip.h> /* struct iphdr */
++#include <linux/skbuff.h>
++#ifdef NET_21
++# include <asm/uaccess.h>
++# include <linux/in6.h>
++#endif /* NET_21 */
++#include <asm/checksum.h>
++#include <net/ip.h>
++
++#include <openswan.h>
++
++#include "openswan/radij.h"
++#include "openswan/ipsec_encap.h"
++#include "openswan/ipsec_radij.h"
++
++int maj_keylen;
++struct radij_mask *rj_mkfreelist;
++struct radij_node_head *mask_rjhead;
++static int gotOddMasks;
++static char *maskedKey;
++static char *rj_zeroes, *rj_ones;
++
++#define rj_masktop (mask_rjhead->rnh_treetop)
++#ifdef Bcmp
++# undef Bcmp
++#endif /* Bcmp */
++#define Bcmp(a, b, l) (l == 0 ? 0 : memcmp((caddr_t)(b), (caddr_t)(a), (size_t)l))
++/*
++ * The data structure for the keys is a radix tree with one way
++ * branching removed. The index rj_b at an internal node n represents a bit
++ * position to be tested. The tree is arranged so that all descendants
++ * of a node n have keys whose bits all agree up to position rj_b - 1.
++ * (We say the index of n is rj_b.)
++ *
++ * There is at least one descendant which has a one bit at position rj_b,
++ * and at least one with a zero there.
++ *
++ * A route is determined by a pair of key and mask. We require that the
++ * bit-wise logical and of the key and mask to be the key.
++ * We define the index of a route to associated with the mask to be
++ * the first bit number in the mask where 0 occurs (with bit number 0
++ * representing the highest order bit).
++ *
++ * We say a mask is normal if every bit is 0, past the index of the mask.
++ * If a node n has a descendant (k, m) with index(m) == index(n) == rj_b,
++ * and m is a normal mask, then the route applies to every descendant of n.
++ * If the index(m) < rj_b, this implies the trailing last few bits of k
++ * before bit b are all 0, (and hence consequently true of every descendant
++ * of n), so the route applies to all descendants of the node as well.
++ *
++ * The present version of the code makes no use of normal routes,
++ * but similar logic shows that a non-normal mask m such that
++ * index(m) <= index(n) could potentially apply to many children of n.
++ * Thus, for each non-host route, we attach its mask to a list at an internal
++ * node as high in the tree as we can go.
++ */
++
++struct radij_node *
++rj_search(v_arg, head)
++ void *v_arg;
++ struct radij_node *head;
++{
++ register struct radij_node *x;
++ register caddr_t v;
++
++ for (x = head, v = v_arg; x->rj_b >= 0;) {
++ if (x->rj_bmask & v[x->rj_off])
++ x = x->rj_r;
++ else
++ x = x->rj_l;
++ }
++ return (x);
++};
++
++struct radij_node *
++rj_search_m(v_arg, head, m_arg)
++ struct radij_node *head;
++ void *v_arg, *m_arg;
++{
++ register struct radij_node *x;
++ register caddr_t v = v_arg, m = m_arg;
++
++ for (x = head; x->rj_b >= 0;) {
++ if ((x->rj_bmask & m[x->rj_off]) &&
++ (x->rj_bmask & v[x->rj_off]))
++ x = x->rj_r;
++ else
++ x = x->rj_l;
++ }
++ return x;
++};
++
++int
++rj_refines(m_arg, n_arg)
++ void *m_arg, *n_arg;
++{
++ register caddr_t m = m_arg, n = n_arg;
++ register caddr_t lim, lim2 = lim = n + *(u_char *)n;
++ int longer = (*(u_char *)n++) - (int)(*(u_char *)m++);
++ int masks_are_equal = 1;
++
++ if (longer > 0)
++ lim -= longer;
++ while (n < lim) {
++ if (*n & ~(*m))
++ return 0;
++ if (*n++ != *m++)
++ masks_are_equal = 0;
++
++ }
++ while (n < lim2)
++ if (*n++)
++ return 0;
++ if (masks_are_equal && (longer < 0))
++ for (lim2 = m - longer; m < lim2; )
++ if (*m++)
++ return 1;
++ return (!masks_are_equal);
++}
++
++
++struct radij_node *
++rj_match(v_arg, head)
++ void *v_arg;
++ struct radij_node_head *head;
++{
++ caddr_t v = v_arg;
++ register struct radij_node *t = head->rnh_treetop, *x;
++ register caddr_t cp = v, cp2, cp3;
++ caddr_t cplim, mstart;
++ struct radij_node *saved_t, *top = t;
++ int off = t->rj_off, vlen = *(u_char *)cp, matched_off;
++
++ /*
++ * Open code rj_search(v, top) to avoid overhead of extra
++ * subroutine call.
++ */
++ for (; t->rj_b >= 0; ) {
++ if (t->rj_bmask & cp[t->rj_off])
++ t = t->rj_r;
++ else
++ t = t->rj_l;
++ }
++ /*
++ * See if we match exactly as a host destination
++ */
++ KLIPS_PRINT(debug_radij,
++ "klips_debug:rj_match: "
++ "* See if we match exactly as a host destination\n");
++
++ cp += off; cp2 = t->rj_key + off; cplim = v + vlen;
++ for (; cp < cplim; cp++, cp2++)
++ if (*cp != *cp2)
++ goto on1;
++ /*
++ * This extra grot is in case we are explicitly asked
++ * to look up the default. Ugh!
++ */
++ if ((t->rj_flags & RJF_ROOT) && t->rj_dupedkey)
++ t = t->rj_dupedkey;
++ return t;
++on1:
++ matched_off = cp - v;
++ saved_t = t;
++ KLIPS_PRINT(debug_radij,
++ "klips_debug:rj_match: "
++ "** try to match a leaf, t=0p%p\n", t);
++ do {
++ if (t->rj_mask) {
++ /*
++ * Even if we don't match exactly as a hosts;
++ * we may match if the leaf we wound up at is
++ * a route to a net.
++ */
++ cp3 = matched_off + t->rj_mask;
++ cp2 = matched_off + t->rj_key;
++ for (; cp < cplim; cp++)
++ if ((*cp2++ ^ *cp) & *cp3++)
++ break;
++ if (cp == cplim)
++ return t;
++ cp = matched_off + v;
++ }
++ } while ((t = t->rj_dupedkey));
++ t = saved_t;
++ /* start searching up the tree */
++ KLIPS_PRINT(debug_radij,
++ "klips_debug:rj_match: "
++ "*** start searching up the tree, t=0p%p\n",
++ t);
++ do {
++ register struct radij_mask *m;
++
++ t = t->rj_p;
++ KLIPS_PRINT(debug_radij,
++ "klips_debug:rj_match: "
++ "**** t=0p%p\n",
++ t);
++ if ((m = t->rj_mklist)) {
++ /*
++ * After doing measurements here, it may
++ * turn out to be faster to open code
++ * rj_search_m here instead of always
++ * copying and masking.
++ */
++ /* off = min(t->rj_off, matched_off); */
++ off = t->rj_off;
++ if (matched_off < off)
++ off = matched_off;
++ mstart = maskedKey + off;
++ do {
++ cp2 = mstart;
++ cp3 = m->rm_mask + off;
++ KLIPS_PRINT(debug_radij,
++ "klips_debug:rj_match: "
++ "***** cp2=0p%p cp3=0p%p\n",
++ cp2, cp3);
++ for (cp = v + off; cp < cplim;)
++ *cp2++ = *cp++ & *cp3++;
++ x = rj_search(maskedKey, t);
++ while (x && x->rj_mask != m->rm_mask)
++ x = x->rj_dupedkey;
++ if (x &&
++ (Bcmp(mstart, x->rj_key + off,
++ vlen - off) == 0))
++ return x;
++ } while ((m = m->rm_mklist));
++ }
++ } while (t != top);
++ KLIPS_PRINT(debug_radij,
++ "klips_debug:rj_match: "
++ "***** not found.\n");
++ return 0;
++};
++
++#ifdef RJ_DEBUG
++int rj_nodenum;
++struct radij_node *rj_clist;
++int rj_saveinfo;
++DEBUG_NO_STATIC void traverse(struct radij_node *);
++#ifdef RJ_DEBUG2
++int rj_debug = 1;
++#else
++int rj_debug = 0;
++#endif /* RJ_DEBUG2 */
++#endif /* RJ_DEBUG */
++
++struct radij_node *
++rj_newpair(v, b, nodes)
++ void *v;
++ int b;
++ struct radij_node nodes[2];
++{
++ register struct radij_node *tt = nodes, *t = tt + 1;
++ t->rj_b = b; t->rj_bmask = 0x80 >> (b & 7);
++ t->rj_l = tt; t->rj_off = b >> 3;
++ tt->rj_b = -1; tt->rj_key = (caddr_t)v; tt->rj_p = t;
++ tt->rj_flags = t->rj_flags = RJF_ACTIVE;
++#ifdef RJ_DEBUG
++ tt->rj_info = rj_nodenum++; t->rj_info = rj_nodenum++;
++ tt->rj_twin = t; tt->rj_ybro = rj_clist; rj_clist = tt;
++#endif /* RJ_DEBUG */
++ return t;
++}
++
++struct radij_node *
++rj_insert(v_arg, head, dupentry, nodes)
++ void *v_arg;
++ struct radij_node_head *head;
++ int *dupentry;
++ struct radij_node nodes[2];
++{
++ caddr_t v = v_arg;
++ struct radij_node *top = head->rnh_treetop;
++ int head_off = top->rj_off, vlen = (int)*((u_char *)v);
++ register struct radij_node *t = rj_search(v_arg, top);
++ register caddr_t cp = v + head_off;
++ register int b;
++ struct radij_node *tt;
++ /*
++ *find first bit at which v and t->rj_key differ
++ */
++ {
++ register caddr_t cp2 = t->rj_key + head_off;
++ register int cmp_res;
++ caddr_t cplim = v + vlen;
++
++ while (cp < cplim)
++ if (*cp2++ != *cp++)
++ goto on1;
++ *dupentry = 1;
++ return t;
++on1:
++ *dupentry = 0;
++ cmp_res = (cp[-1] ^ cp2[-1]) & 0xff;
++ for (b = (cp - v) << 3; cmp_res; b--)
++ cmp_res >>= 1;
++ }
++ {
++ register struct radij_node *p, *x = top;
++ cp = v;
++ do {
++ p = x;
++ if (cp[x->rj_off] & x->rj_bmask)
++ x = x->rj_r;
++ else x = x->rj_l;
++ } while (b > (unsigned) x->rj_b); /* x->rj_b < b && x->rj_b >= 0 */
++#ifdef RJ_DEBUG
++ if (rj_debug)
++ printk("klips_debug:rj_insert: Going In:\n"), traverse(p);
++#endif /* RJ_DEBUG */
++ t = rj_newpair(v_arg, b, nodes); tt = t->rj_l;
++ if ((cp[p->rj_off] & p->rj_bmask) == 0)
++ p->rj_l = t;
++ else
++ p->rj_r = t;
++ x->rj_p = t; t->rj_p = p; /* frees x, p as temp vars below */
++ if ((cp[t->rj_off] & t->rj_bmask) == 0) {
++ t->rj_r = x;
++ } else {
++ t->rj_r = tt; t->rj_l = x;
++ }
++#ifdef RJ_DEBUG
++ if (rj_debug)
++ printk("klips_debug:rj_insert: Coming out:\n"), traverse(p);
++#endif /* RJ_DEBUG */
++ }
++ return (tt);
++}
++
++struct radij_node *
++rj_addmask(n_arg, search, skip)
++ int search, skip;
++ void *n_arg;
++{
++ caddr_t netmask = (caddr_t)n_arg;
++ register struct radij_node *x;
++ register caddr_t cp, cplim;
++ register int b, mlen, j;
++ int maskduplicated;
++
++ mlen = *(u_char *)netmask;
++ if (search) {
++ x = rj_search(netmask, rj_masktop);
++ mlen = *(u_char *)netmask;
++ if (Bcmp(netmask, x->rj_key, mlen) == 0)
++ return (x);
++ }
++ R_Malloc(x, struct radij_node *, maj_keylen + 2 * sizeof (*x));
++ if (x == 0)
++ return (0);
++ Bzero(x, maj_keylen + 2 * sizeof (*x));
++ cp = (caddr_t)(x + 2);
++ Bcopy(netmask, cp, mlen);
++ netmask = cp;
++ x = rj_insert(netmask, mask_rjhead, &maskduplicated, x);
++ /*
++ * Calculate index of mask.
++ */
++ cplim = netmask + mlen;
++ for (cp = netmask + skip; cp < cplim; cp++)
++ if (*(u_char *)cp != 0xff)
++ break;
++ b = (cp - netmask) << 3;
++ if (cp != cplim) {
++ if (*cp != 0) {
++ gotOddMasks = 1;
++ for (j = 0x80; j; b++, j >>= 1)
++ if ((j & *cp) == 0)
++ break;
++ }
++ }
++ x->rj_b = -1 - b;
++ return (x);
++}
++
++#if 0
++struct radij_node *
++#endif
++int
++rj_addroute(v_arg, n_arg, head, treenodes)
++ void *v_arg, *n_arg;
++ struct radij_node_head *head;
++ struct radij_node treenodes[2];
++{
++ caddr_t v = (caddr_t)v_arg, netmask = (caddr_t)n_arg;
++ register struct radij_node *t, *x=NULL, *tt;
++ struct radij_node *saved_tt, *top = head->rnh_treetop;
++ short b = 0, b_leaf;
++ int mlen, keyduplicated;
++ caddr_t cplim;
++ struct radij_mask *m, **mp;
++
++ /*
++ * In dealing with non-contiguous masks, there may be
++ * many different routes which have the same mask.
++ * We will find it useful to have a unique pointer to
++ * the mask to speed avoiding duplicate references at
++ * nodes and possibly save time in calculating indices.
++ */
++ if (netmask) {
++ x = rj_search(netmask, rj_masktop);
++ mlen = *(u_char *)netmask;
++ if (Bcmp(netmask, x->rj_key, mlen) != 0) {
++ x = rj_addmask(netmask, 0, top->rj_off);
++ if (x == 0)
++ return -ENOMEM; /* (0) rgb */
++ }
++ netmask = x->rj_key;
++ b = -1 - x->rj_b;
++ }
++ /*
++ * Deal with duplicated keys: attach node to previous instance
++ */
++ saved_tt = tt = rj_insert(v, head, &keyduplicated, treenodes);
++#ifdef RJ_DEBUG
++ printk("addkey: duplicated: %d\n", keyduplicated);
++#endif
++ if (keyduplicated) {
++ do {
++ if (tt->rj_mask == netmask)
++ return -EEXIST; /* -ENXIO; (0) rgb */
++ t = tt;
++ if (netmask == 0 ||
++ (tt->rj_mask && rj_refines(netmask, tt->rj_mask)))
++ break;
++ } while ((tt = tt->rj_dupedkey));
++ /*
++ * If the mask is not duplicated, we wouldn't
++ * find it among possible duplicate key entries
++ * anyway, so the above test doesn't hurt.
++ *
++ * We sort the masks for a duplicated key the same way as
++ * in a masklist -- most specific to least specific.
++ * This may require the unfortunate nuisance of relocating
++ * the head of the list.
++ */
++ if (tt && t == saved_tt) {
++ struct radij_node *xx = x;
++ /* link in at head of list */
++ (tt = treenodes)->rj_dupedkey = t;
++ tt->rj_flags = t->rj_flags;
++ tt->rj_p = x = t->rj_p;
++ if (x->rj_l == t) x->rj_l = tt; else x->rj_r = tt;
++ saved_tt = tt; x = xx;
++ } else {
++ (tt = treenodes)->rj_dupedkey = t->rj_dupedkey;
++ t->rj_dupedkey = tt;
++ }
++#ifdef RJ_DEBUG
++ t=tt+1; tt->rj_info = rj_nodenum++; t->rj_info = rj_nodenum++;
++ tt->rj_twin = t; tt->rj_ybro = rj_clist; rj_clist = tt;
++#endif /* RJ_DEBUG */
++ t = saved_tt;
++ tt->rj_key = (caddr_t) v;
++ tt->rj_b = -1;
++ tt->rj_flags = t->rj_flags & ~RJF_ROOT;
++ }
++ /*
++ * Put mask in tree.
++ */
++ if (netmask) {
++ tt->rj_mask = netmask;
++ tt->rj_b = x->rj_b;
++ }
++ t = saved_tt->rj_p;
++ b_leaf = -1 - t->rj_b;
++ if (t->rj_r == saved_tt) x = t->rj_l; else x = t->rj_r;
++ /* Promote general routes from below */
++ if (x->rj_b < 0) {
++ if (x->rj_mask && (x->rj_b >= b_leaf) && x->rj_mklist == 0) {
++ MKGet(m);
++ if (m) {
++ Bzero(m, sizeof *m);
++ m->rm_b = x->rj_b;
++ m->rm_mask = x->rj_mask;
++ x->rj_mklist = t->rj_mklist = m;
++ }
++ }
++ } else if (x->rj_mklist) {
++ /*
++ * Skip over masks whose index is > that of new node
++ */
++ for (mp = &x->rj_mklist; (m = *mp); mp = &m->rm_mklist)
++ if (m->rm_b >= b_leaf)
++ break;
++ t->rj_mklist = m; *mp = 0;
++ }
++ /* Add new route to highest possible ancestor's list */
++ if ((netmask == 0) || (b > t->rj_b )) {
++#ifdef RJ_DEBUG
++ printk("klips:radij.c: netmask = %p or b(%d)>t->rjb(%d)\n", netmask, b, t->rj_b);
++#endif
++ return 0; /* tt rgb */ /* can't lift at all */
++ }
++ b_leaf = tt->rj_b;
++ do {
++ x = t;
++ t = t->rj_p;
++ } while (b <= t->rj_b && x != top);
++ /*
++ * Search through routes associated with node to
++ * insert new route according to index.
++ * For nodes of equal index, place more specific
++ * masks first.
++ */
++ cplim = netmask + mlen;
++ for (mp = &x->rj_mklist; (m = *mp); mp = &m->rm_mklist) {
++ if (m->rm_b < b_leaf)
++ continue;
++ if (m->rm_b > b_leaf)
++ break;
++ if (m->rm_mask == netmask) {
++ m->rm_refs++;
++ tt->rj_mklist = m;
++#ifdef RJ_DEBUG
++ printk("klips:radij.c: m->rm_mask %p == netmask\n", netmask);
++#endif
++ return 0; /* tt rgb */
++ }
++ if (rj_refines(netmask, m->rm_mask))
++ break;
++ }
++ MKGet(m);
++ if (m == 0) {
++ printk("klips_debug:rj_addroute: "
++ "Mask for route not entered\n");
++ return 0; /* (tt) rgb */
++ }
++ Bzero(m, sizeof *m);
++ m->rm_b = b_leaf;
++ m->rm_mask = netmask;
++ m->rm_mklist = *mp;
++ *mp = m;
++ tt->rj_mklist = m;
++#ifdef RJ_DEBUG
++ printk("klips:radij.c: addroute done\n");
++#endif
++ return 0; /* tt rgb */
++}
++
++int
++rj_delete(v_arg, netmask_arg, head, node)
++ void *v_arg, *netmask_arg;
++ struct radij_node_head *head;
++ struct radij_node **node;
++{
++ register struct radij_node *t, *p, *x, *tt;
++ struct radij_mask *m, *saved_m, **mp;
++ struct radij_node *dupedkey, *saved_tt, *top;
++ caddr_t v, netmask;
++ int b, head_off, vlen;
++
++ v = v_arg;
++ netmask = netmask_arg;
++ x = head->rnh_treetop;
++ tt = rj_search(v, x);
++ head_off = x->rj_off;
++ vlen = *(u_char *)v;
++ saved_tt = tt;
++ top = x;
++ if (tt == 0 ||
++ Bcmp(v + head_off, tt->rj_key + head_off, vlen - head_off))
++ return -EFAULT; /* (0) rgb */
++ /*
++ * Delete our route from mask lists.
++ */
++ if ((dupedkey = tt->rj_dupedkey)) {
++ if (netmask)
++ netmask = rj_search(netmask, rj_masktop)->rj_key;
++ while (tt->rj_mask != netmask)
++ if ((tt = tt->rj_dupedkey) == 0)
++ return -ENOENT; /* -ENXIO; (0) rgb */
++ }
++ if (tt->rj_mask == 0 || (saved_m = m = tt->rj_mklist) == 0)
++ goto on1;
++ if (m->rm_mask != tt->rj_mask) {
++ printk("klips_debug:rj_delete: "
++ "inconsistent annotation\n");
++ goto on1;
++ }
++ if (--m->rm_refs >= 0)
++ goto on1;
++ b = -1 - tt->rj_b;
++ t = saved_tt->rj_p;
++ if (b > t->rj_b)
++ goto on1; /* Wasn't lifted at all */
++ do {
++ x = t;
++ t = t->rj_p;
++ } while (b <= t->rj_b && x != top);
++ for (mp = &x->rj_mklist; (m = *mp); mp = &m->rm_mklist)
++ if (m == saved_m) {
++ *mp = m->rm_mklist;
++ MKFree(m);
++ break;
++ }
++ if (m == 0)
++ printk("klips_debug:rj_delete: "
++ "couldn't find our annotation\n");
++on1:
++ /*
++ * Eliminate us from tree
++ */
++ if (tt->rj_flags & RJF_ROOT)
++ return -EFAULT; /* (0) rgb */
++#ifdef RJ_DEBUG
++ /* Get us out of the creation list */
++ for (t = rj_clist; t && t->rj_ybro != tt; t = t->rj_ybro) {}
++ if (t) t->rj_ybro = tt->rj_ybro;
++#endif /* RJ_DEBUG */
++ t = tt->rj_p;
++ if (dupedkey) {
++ if (tt == saved_tt) {
++ x = dupedkey; x->rj_p = t;
++ if (t->rj_l == tt) t->rj_l = x; else t->rj_r = x;
++ } else {
++ for (x = p = saved_tt; p && p->rj_dupedkey != tt;)
++ p = p->rj_dupedkey;
++ if (p) p->rj_dupedkey = tt->rj_dupedkey;
++ else printk("klips_debug:rj_delete: "
++ "couldn't find node that we started with\n");
++ }
++ t = tt + 1;
++ if (t->rj_flags & RJF_ACTIVE) {
++#ifndef RJ_DEBUG
++ *++x = *t; p = t->rj_p;
++#else
++ b = t->rj_info; *++x = *t; t->rj_info = b; p = t->rj_p;
++#endif /* RJ_DEBUG */
++ if (p->rj_l == t) p->rj_l = x; else p->rj_r = x;
++ x->rj_l->rj_p = x; x->rj_r->rj_p = x;
++ }
++ goto out;
++ }
++ if (t->rj_l == tt) x = t->rj_r; else x = t->rj_l;
++ p = t->rj_p;
++ if (p->rj_r == t) p->rj_r = x; else p->rj_l = x;
++ x->rj_p = p;
++ /*
++ * Demote routes attached to us.
++ */
++ if (t->rj_mklist) {
++ if (x->rj_b >= 0) {
++ for (mp = &x->rj_mklist; (m = *mp);)
++ mp = &m->rm_mklist;
++ *mp = t->rj_mklist;
++ } else {
++ for (m = t->rj_mklist; m;) {
++ struct radij_mask *mm = m->rm_mklist;
++ if (m == x->rj_mklist && (--(m->rm_refs) < 0)) {
++ x->rj_mklist = 0;
++ MKFree(m);
++ } else
++ printk("klips_debug:rj_delete: "
++ "Orphaned Mask 0p%p at 0p%p\n", m, x);
++ m = mm;
++ }
++ }
++ }
++ /*
++ * We may be holding an active internal node in the tree.
++ */
++ x = tt + 1;
++ if (t != x) {
++#ifndef RJ_DEBUG
++ *t = *x;
++#else
++ b = t->rj_info; *t = *x; t->rj_info = b;
++#endif /* RJ_DEBUG */
++ t->rj_l->rj_p = t; t->rj_r->rj_p = t;
++ p = x->rj_p;
++ if (p->rj_l == x) p->rj_l = t; else p->rj_r = t;
++ }
++out:
++ tt->rj_flags &= ~RJF_ACTIVE;
++ tt[1].rj_flags &= ~RJF_ACTIVE;
++ *node = tt;
++ return 0; /* (tt) rgb */
++}
++
++int
++rj_walktree(h, f, w)
++ struct radij_node_head *h;
++ register int (*f)(struct radij_node *,void *);
++ void *w;
++{
++ int error;
++ struct radij_node *base, *next;
++ register struct radij_node *rn;
++
++ if(!h || !f /* || !w */) {
++ return -ENODATA;
++ }
++
++ rn = h->rnh_treetop;
++ /*
++ * This gets complicated because we may delete the node
++ * while applying the function f to it, so we need to calculate
++ * the successor node in advance.
++ */
++ /* First time through node, go left */
++ while (rn->rj_b >= 0)
++ rn = rn->rj_l;
++ for (;;) {
++#ifdef CONFIG_IPSEC_DEBUG
++ if(debug_radij) {
++ printk("klips_debug:rj_walktree: "
++ "for: rn=0p%p rj_b=%d rj_flags=%x",
++ rn,
++ rn->rj_b,
++ rn->rj_flags);
++ rn->rj_b >= 0 ?
++ printk(" node off=%x\n",
++ rn->rj_off) :
++ printk(" leaf key = %08x->%08x\n",
++ (u_int)ntohl(((struct sockaddr_encap *)rn->rj_key)->sen_ip_src.s_addr),
++ (u_int)ntohl(((struct sockaddr_encap *)rn->rj_key)->sen_ip_dst.s_addr))
++ ;
++ }
++#endif /* CONFIG_IPSEC_DEBUG */
++ base = rn;
++ /* If at right child go back up, otherwise, go right */
++ while (rn->rj_p->rj_r == rn && (rn->rj_flags & RJF_ROOT) == 0)
++ rn = rn->rj_p;
++ /* Find the next *leaf* since next node might vanish, too */
++ for (rn = rn->rj_p->rj_r; rn->rj_b >= 0;)
++ rn = rn->rj_l;
++ next = rn;
++#ifdef CONFIG_IPSEC_DEBUG
++ if(debug_radij) {
++ printk("klips_debug:rj_walktree: "
++ "processing leaves, rn=0p%p rj_b=%d rj_flags=%x",
++ rn,
++ rn->rj_b,
++ rn->rj_flags);
++ rn->rj_b >= 0 ?
++ printk(" node off=%x\n",
++ rn->rj_off) :
++ printk(" leaf key = %08x->%08x\n",
++ (u_int)ntohl(((struct sockaddr_encap *)rn->rj_key)->sen_ip_src.s_addr),
++ (u_int)ntohl(((struct sockaddr_encap *)rn->rj_key)->sen_ip_dst.s_addr))
++ ;
++ }
++#endif /* CONFIG_IPSEC_DEBUG */
++ /* Process leaves */
++ while ((rn = base)) {
++ base = rn->rj_dupedkey;
++#ifdef CONFIG_IPSEC_DEBUG
++ if(debug_radij) {
++ printk("klips_debug:rj_walktree: "
++ "while: base=0p%p rn=0p%p rj_b=%d rj_flags=%x",
++ base,
++ rn,
++ rn->rj_b,
++ rn->rj_flags);
++ rn->rj_b >= 0 ?
++ printk(" node off=%x\n",
++ rn->rj_off) :
++ printk(" leaf key = %08x->%08x\n",
++ (u_int)ntohl(((struct sockaddr_encap *)rn->rj_key)->sen_ip_src.s_addr),
++ (u_int)ntohl(((struct sockaddr_encap *)rn->rj_key)->sen_ip_dst.s_addr))
++ ;
++ }
++#endif /* CONFIG_IPSEC_DEBUG */
++ if (!(rn->rj_flags & RJF_ROOT) && (error = (*f)(rn, w)))
++ return (-error);
++ }
++ rn = next;
++ if (rn->rj_flags & RJF_ROOT)
++ return (0);
++ }
++ /* NOTREACHED */
++}
++
++int
++rj_inithead(head, off)
++ void **head;
++ int off;
++{
++ register struct radij_node_head *rnh;
++ register struct radij_node *t, *tt, *ttt;
++ if (*head)
++ return (1);
++ R_Malloc(rnh, struct radij_node_head *, sizeof (*rnh));
++ if (rnh == NULL)
++ return (0);
++ Bzero(rnh, sizeof (*rnh));
++ *head = rnh;
++ t = rj_newpair(rj_zeroes, off, rnh->rnh_nodes);
++ ttt = rnh->rnh_nodes + 2;
++ t->rj_r = ttt;
++ t->rj_p = t;
++ tt = t->rj_l;
++ tt->rj_flags = t->rj_flags = RJF_ROOT | RJF_ACTIVE;
++ tt->rj_b = -1 - off;
++ *ttt = *tt;
++ ttt->rj_key = rj_ones;
++ rnh->rnh_addaddr = rj_addroute;
++ rnh->rnh_deladdr = rj_delete;
++ rnh->rnh_matchaddr = rj_match;
++ rnh->rnh_walktree = rj_walktree;
++ rnh->rnh_treetop = t;
++ return (1);
++}
++
++void
++rj_init()
++{
++ char *cp, *cplim;
++
++ if (maj_keylen == 0) {
++ printk("klips_debug:rj_init: "
++ "radij functions require maj_keylen be set\n");
++ return;
++ }
++ R_Malloc(rj_zeroes, char *, 3 * maj_keylen);
++ if (rj_zeroes == NULL)
++ panic("rj_init");
++ Bzero(rj_zeroes, 3 * maj_keylen);
++ rj_ones = cp = rj_zeroes + maj_keylen;
++ maskedKey = cplim = rj_ones + maj_keylen;
++ while (cp < cplim)
++ *cp++ = -1;
++ if (rj_inithead((void **)&mask_rjhead, 0) == 0)
++ panic("rj_init 2");
++}
++
++void
++rj_preorder(struct radij_node *rn, int l)
++{
++ int i;
++
++ if (rn == NULL){
++ printk("klips_debug:rj_preorder: "
++ "NULL pointer\n");
++ return;
++ }
++
++ if (rn->rj_b >= 0){
++ rj_preorder(rn->rj_l, l+1);
++ rj_preorder(rn->rj_r, l+1);
++ printk("klips_debug:");
++ for (i=0; i<l; i++)
++ printk("*");
++ printk(" off = %d\n",
++ rn->rj_off);
++ } else {
++ printk("klips_debug:");
++ for (i=0; i<l; i++)
++ printk("@");
++ printk(" flags = %x",
++ (u_int)rn->rj_flags);
++ if (rn->rj_flags & RJF_ACTIVE) {
++ printk(" @key=0p%p",
++ rn->rj_key);
++ printk(" key = %08x->%08x",
++ (u_int)ntohl(((struct sockaddr_encap *)rn->rj_key)->sen_ip_src.s_addr),
++ (u_int)ntohl(((struct sockaddr_encap *)rn->rj_key)->sen_ip_dst.s_addr));
++ printk(" @mask=0p%p",
++ rn->rj_mask);
++ if (rn->rj_mask)
++ printk(" mask = %08x->%08x",
++ (u_int)ntohl(((struct sockaddr_encap *)rn->rj_mask)->sen_ip_src.s_addr),
++ (u_int)ntohl(((struct sockaddr_encap *)rn->rj_mask)->sen_ip_dst.s_addr));
++ if (rn->rj_dupedkey)
++ printk(" dupedkey = 0p%p",
++ rn->rj_dupedkey);
++ }
++ printk("\n");
++ }
++}
++
++#ifdef RJ_DEBUG
++DEBUG_NO_STATIC void traverse(struct radij_node *p)
++{
++ rj_preorder(p, 0);
++}
++#endif /* RJ_DEBUG */
++
++void
++rj_dumptrees(void)
++{
++ rj_preorder(rnh->rnh_treetop, 0);
++}
++
++void
++rj_free_mkfreelist(void)
++{
++ struct radij_mask *mknp, *mknp2;
++
++ mknp = rj_mkfreelist;
++ while(mknp)
++ {
++ mknp2 = mknp;
++ mknp = mknp->rm_mklist;
++ kfree(mknp2);
++ }
++}
++
++int
++radijcleartree(void)
++{
++ return rj_walktree(rnh, ipsec_rj_walker_delete, NULL);
++}
++
++int
++radijcleanup(void)
++{
++ int error = 0;
++
++ error = radijcleartree();
++
++ rj_free_mkfreelist();
++
++/* rj_walktree(mask_rjhead, ipsec_rj_walker_delete, NULL); */
++ if(mask_rjhead) {
++ kfree(mask_rjhead);
++ }
++
++ if(rj_zeroes) {
++ kfree(rj_zeroes);
++ }
++
++ if(rnh) {
++ kfree(rnh);
++ }
++
++ return error;
++}
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.46 2004/04/06 02:49:26 mcr
++ * pullup of algo code from alg-branch.
++ *
++ * Revision 1.45 2003/10/31 02:27:55 mcr
++ * pulled up port-selector patches and sa_id elimination.
++ *
++ * Revision 1.44.30.1 2003/10/29 01:30:41 mcr
++ * elimited "struct sa_id".
++ *
++ * Revision 1.44 2002/07/24 18:44:54 rgb
++ * Type fiddling to tame ia64 compiler.
++ *
++ * Revision 1.43 2002/05/23 07:14:11 rgb
++ * Cleaned up %p variants to 0p%p for test suite cleanup.
++ *
++ * Revision 1.42 2002/04/24 07:55:32 mcr
++ * #include patches and Makefiles for post-reorg compilation.
++ *
++ * Revision 1.41 2002/04/24 07:36:35 mcr
++ * Moved from ./klips/net/ipsec/radij.c,v
++ *
++ * Revision 1.40 2002/01/29 17:17:58 mcr
++ * moved include of ipsec_param.h to after include of linux/kernel.h
++ * otherwise, it seems that some option that is set in ipsec_param.h
++ * screws up something subtle in the include path to kernel.h, and
++ * it complains on the snprintf() prototype.
++ *
++ * Revision 1.39 2002/01/29 04:00:55 mcr
++ * more excise of kversions.h header.
++ *
++ * Revision 1.38 2002/01/29 02:13:19 mcr
++ * introduction of ipsec_kversion.h means that include of
++ * ipsec_param.h must preceed any decisions about what files to
++ * include to deal with differences in kernel source.
++ *
++ * Revision 1.37 2001/10/18 04:45:23 rgb
++ * 2.4.9 kernel deprecates linux/malloc.h in favour of linux/slab.h,
++ * lib/freeswan.h version macros moved to lib/kversions.h.
++ * Other compiler directive cleanups.
++ *
++ * Revision 1.36 2001/08/22 13:43:51 henry
++ * eliminate the single use of min() to avoid problems with Linus changing it
++ *
++ * Revision 1.35 2001/06/15 04:57:29 rgb
++ * Clarified error return codes.
++ * Changed mask add already exists to EEXIST.
++ * Changed mask delete did not exist to ENOENT.
++ *
++ * Revision 1.34 2001/05/03 19:44:26 rgb
++ * Fix sign of error return codes for rj_addroute().
++ *
++ * Revision 1.33 2001/02/27 22:24:56 rgb
++ * Re-formatting debug output (line-splitting, joining, 1arg/line).
++ * Check for satoa() return codes.
++ *
++ * Revision 1.32 2001/02/27 06:23:15 rgb
++ * Debug line splitting.
++ *
++ * Revision 1.31 2000/11/06 04:35:21 rgb
++ * Clear table *before* releasing other items in radijcleanup.
++ *
++ * Revision 1.30 2000/09/20 04:07:40 rgb
++ * Changed static functions to DEBUG_NO_STATIC to reveal function names in
++ * oopsen.
++ *
++ * Revision 1.29 2000/09/12 03:25:02 rgb
++ * Moved radij_c_version printing to ipsec_version_get_info().
++ *
++ * Revision 1.28 2000/09/08 19:12:56 rgb
++ * Change references from DEBUG_IPSEC to CONFIG_IPSEC_DEBUG.
++ *
++ * Revision 1.27 2000/07/28 14:58:32 rgb
++ * Changed kfree_s to kfree, eliminating extra arg to fix 2.4.0-test5.
++ *
++ * Revision 1.26 2000/05/10 23:11:37 rgb
++ * Comment out most of the startup version information.
++ *
++ * Revision 1.25 2000/01/21 06:21:47 rgb
++ * Change return codes to negative on error.
++ *
++ * Revision 1.24 1999/11/18 04:09:20 rgb
++ * Replaced all kernel version macros to shorter, readable form.
++ *
++ * Revision 1.23 1999/11/17 15:53:41 rgb
++ * Changed all occurrences of #include "../../../lib/freeswan.h"
++ * to #include <freeswan.h> which works due to -Ilibfreeswan in the
++ * klips/net/ipsec/Makefile.
++ *
++ * Revision 1.22 1999/10/15 22:17:28 rgb
++ * Modify radijcleanup() to call radijcleartree().
++ *
++ * Revision 1.21 1999/10/08 18:37:34 rgb
++ * Fix end-of-line spacing to sate whining PHMs.
++ *
++ * Revision 1.20 1999/10/01 15:44:54 rgb
++ * Move spinlock header include to 2.1> scope.
++ *
++ * Revision 1.19 1999/10/01 08:35:52 rgb
++ * Add spinlock include to shut up compiler for 2.0.38.
++ *
++ * Revision 1.18 1999/09/23 18:02:52 rgb
++ * De-alarm the search failure message so it doesn't sound so grave.
++ *
++ * Revision 1.17 1999/05/25 21:26:01 rgb
++ * Fix rj_walktree() sanity checking bug.
++ *
++ * Revision 1.16 1999/05/09 03:25:38 rgb
++ * Fix bug introduced by 2.2 quick-and-dirty patch.
++ *
++ * Revision 1.15 1999/05/05 22:02:33 rgb
++ * Add a quick and dirty port to 2.2 kernels by Marc Boucher <marc@mbsi.ca>.
++ *
++ * Revision 1.14 1999/04/29 15:24:15 rgb
++ * Add sanity checking for null pointer arguments.
++ * Standardise an error return method.
++ *
++ * Revision 1.13 1999/04/11 00:29:02 henry
++ * GPL boilerplate
++ *
++ * Revision 1.12 1999/04/06 04:54:28 rgb
++ * Fix/Add RCSID Id: and Log: bits to make PHMDs happy. This includes
++ * patch shell fixes.
++ *
++ * Revision 1.11 1999/02/17 16:52:53 rgb
++ * Convert DEBUG_IPSEC to KLIPS_PRINT
++ * Clean out unused cruft.
++ *
++ * Revision 1.10 1999/01/22 06:30:05 rgb
++ * Cruft clean-out.
++ * 64-bit clean-up.
++ *
++ * Revision 1.9 1998/12/01 13:22:04 rgb
++ * Added support for debug printing of version info.
++ *
++ * Revision 1.8 1998/11/30 13:22:55 rgb
++ * Rationalised all the klips kernel file headers. They are much shorter
++ * now and won't conflict under RH5.2.
++ *
++ * Revision 1.7 1998/10/25 02:43:26 rgb
++ * Change return type on rj_addroute and rj_delete and add and argument
++ * to the latter to be able to transmit more infomation about errors.
++ *
++ * Revision 1.6 1998/10/19 14:30:06 rgb
++ * Added inclusion of freeswan.h.
++ *
++ * Revision 1.5 1998/10/09 04:33:27 rgb
++ * Added 'klips_debug' prefix to all klips printk debug statements.
++ * Fixed output formatting slightly.
++ *
++ * Revision 1.4 1998/07/28 00:06:59 rgb
++ * Add debug detail to tree traversing.
++ *
++ * Revision 1.3 1998/07/14 18:07:58 rgb
++ * Add a routine to clear the eroute tree.
++ *
++ * Revision 1.2 1998/06/25 20:03:22 rgb
++ * Cleanup #endif comments. Debug output for rj_init.
++ *
++ * Revision 1.1 1998/06/18 21:30:22 henry
++ * move sources from klips/src to klips/net/ipsec to keep stupid kernel
++ * build scripts happier about symlinks
++ *
++ * Revision 1.8 1998/05/25 20:34:15 rgb
++ * Remove temporary ipsec_walk, rj_deltree and rj_delnodes functions.
++ *
++ * Rename ipsec_rj_walker (ipsec_walk) to ipsec_rj_walker_procprint and
++ * add ipsec_rj_walker_delete.
++ *
++ * Recover memory for eroute table on unload of module.
++ *
++ * Revision 1.7 1998/05/21 12:58:58 rgb
++ * Moved 'extern' definitions to ipsec_radij.h to support /proc 3k limit fix.
++ *
++ * Revision 1.6 1998/04/23 20:57:29 rgb
++ * Cleaned up compiler warnings for unused debugging functions.
++ *
++ * Revision 1.5 1998/04/22 16:51:38 rgb
++ * Tidy up radij debug code from recent rash of modifications to debug code.
++ *
++ * Revision 1.4 1998/04/21 21:28:56 rgb
++ * Rearrange debug switches to change on the fly debug output from user
++ * space. Only kernel changes checked in at this time. radij.c was also
++ * changed to temporarily remove buggy debugging code in rj_delete causing
++ * an OOPS and hence, netlink device open errors.
++ *
++ * Revision 1.3 1998/04/14 17:30:37 rgb
++ * Fix up compiling errors for radij tree memory reclamation.
++ *
++ * Revision 1.2 1998/04/12 22:03:25 rgb
++ * Updated ESP-3DES-HMAC-MD5-96,
++ * ESP-DES-HMAC-MD5-96,
++ * AH-HMAC-MD5-96,
++ * AH-HMAC-SHA1-96 since Henry started freeswan cvs repository
++ * from old standards (RFC182[5-9] to new (as of March 1998) drafts.
++ *
++ * Fixed eroute references in /proc/net/ipsec*.
++ *
++ * Started to patch module unloading memory leaks in ipsec_netlink and
++ * radij tree unloading.
++ *
++ * Revision 1.1 1998/04/09 03:06:15 henry
++ * sources moved up from linux/net/ipsec
++ *
++ * Revision 1.1.1.1 1998/04/08 05:35:03 henry
++ * RGB's ipsec-0.8pre2.tar.gz ipsec-0.8
++ *
++ * Revision 0.4 1997/01/15 01:28:15 ji
++ * No changes.
++ *
++ * Revision 0.3 1996/11/20 14:39:04 ji
++ * Minor cleanups.
++ * Rationalized debugging code.
++ *
++ * Revision 0.2 1996/11/02 00:18:33 ji
++ * First limited release.
++ *
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/net/ipsec/sysctl_net_ipsec.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,196 @@
++/*
++ * sysctl interface to net IPSEC subsystem.
++ * Copyright (C) 1998, 1999, 2000, 2001 Richard Guy Briggs.
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ * RCSID $Id$
++ */
++
++/* -*- linux-c -*-
++ *
++ * Initiated April 3, 1998, Richard Guy Briggs <rgb@conscoop.ottawa.on.ca>
++ */
++
++#include <linux/mm.h>
++#include <linux/sysctl.h>
++
++#include "openswan/ipsec_param.h"
++
++#ifdef CONFIG_SYSCTL
++
++#define NET_IPSEC 2112 /* Random number */
++#ifdef CONFIG_IPSEC_DEBUG
++extern int debug_ah;
++extern int debug_esp;
++extern int debug_tunnel;
++extern int debug_eroute;
++extern int debug_spi;
++extern int debug_radij;
++extern int debug_netlink;
++extern int debug_xform;
++extern int debug_rcv;
++extern int debug_pfkey;
++extern int sysctl_ipsec_debug_verbose;
++#ifdef CONFIG_IPSEC_IPCOMP
++extern int sysctl_ipsec_debug_ipcomp;
++#endif /* CONFIG_IPSEC_IPCOMP */
++#endif /* CONFIG_IPSEC_DEBUG */
++
++extern int sysctl_ipsec_icmp;
++extern int sysctl_ipsec_inbound_policy_check;
++extern int sysctl_ipsec_tos;
++int sysctl_ipsec_regress_pfkey_lossage;
++
++enum {
++#ifdef CONFIG_IPSEC_DEBUG
++ NET_IPSEC_DEBUG_AH=1,
++ NET_IPSEC_DEBUG_ESP=2,
++ NET_IPSEC_DEBUG_TUNNEL=3,
++ NET_IPSEC_DEBUG_EROUTE=4,
++ NET_IPSEC_DEBUG_SPI=5,
++ NET_IPSEC_DEBUG_RADIJ=6,
++ NET_IPSEC_DEBUG_NETLINK=7,
++ NET_IPSEC_DEBUG_XFORM=8,
++ NET_IPSEC_DEBUG_RCV=9,
++ NET_IPSEC_DEBUG_PFKEY=10,
++ NET_IPSEC_DEBUG_VERBOSE=11,
++ NET_IPSEC_DEBUG_IPCOMP=12,
++#endif /* CONFIG_IPSEC_DEBUG */
++ NET_IPSEC_ICMP=13,
++ NET_IPSEC_INBOUND_POLICY_CHECK=14,
++ NET_IPSEC_TOS=15,
++ NET_IPSEC_REGRESS_PFKEY_LOSSAGE=16,
++};
++
++static ctl_table ipsec_table[] = {
++#ifdef CONFIG_IPSEC_DEBUG
++ { NET_IPSEC_DEBUG_AH, "debug_ah", &debug_ah,
++ sizeof(int), 0644, NULL, &proc_dointvec},
++ { NET_IPSEC_DEBUG_ESP, "debug_esp", &debug_esp,
++ sizeof(int), 0644, NULL, &proc_dointvec},
++ { NET_IPSEC_DEBUG_TUNNEL, "debug_tunnel", &debug_tunnel,
++ sizeof(int), 0644, NULL, &proc_dointvec},
++ { NET_IPSEC_DEBUG_EROUTE, "debug_eroute", &debug_eroute,
++ sizeof(int), 0644, NULL, &proc_dointvec},
++ { NET_IPSEC_DEBUG_SPI, "debug_spi", &debug_spi,
++ sizeof(int), 0644, NULL, &proc_dointvec},
++ { NET_IPSEC_DEBUG_RADIJ, "debug_radij", &debug_radij,
++ sizeof(int), 0644, NULL, &proc_dointvec},
++ { NET_IPSEC_DEBUG_NETLINK, "debug_netlink", &debug_netlink,
++ sizeof(int), 0644, NULL, &proc_dointvec},
++ { NET_IPSEC_DEBUG_XFORM, "debug_xform", &debug_xform,
++ sizeof(int), 0644, NULL, &proc_dointvec},
++ { NET_IPSEC_DEBUG_RCV, "debug_rcv", &debug_rcv,
++ sizeof(int), 0644, NULL, &proc_dointvec},
++ { NET_IPSEC_DEBUG_PFKEY, "debug_pfkey", &debug_pfkey,
++ sizeof(int), 0644, NULL, &proc_dointvec},
++ { NET_IPSEC_DEBUG_VERBOSE, "debug_verbose",&sysctl_ipsec_debug_verbose,
++ sizeof(int), 0644, NULL, &proc_dointvec},
++#ifdef CONFIG_IPSEC_IPCOMP
++ { NET_IPSEC_DEBUG_IPCOMP, "debug_ipcomp", &sysctl_ipsec_debug_ipcomp,
++ sizeof(int), 0644, NULL, &proc_dointvec},
++#endif /* CONFIG_IPSEC_IPCOMP */
++
++#ifdef CONFIG_IPSEC_REGRESS
++ { NET_IPSEC_REGRESS_PFKEY_LOSSAGE, "pfkey_lossage",
++ &sysctl_ipsec_regress_pfkey_lossage,
++ sizeof(int), 0644, NULL, &proc_dointvec},
++#endif /* CONFIG_IPSEC_REGRESS */
++
++#endif /* CONFIG_IPSEC_DEBUG */
++ { NET_IPSEC_ICMP, "icmp", &sysctl_ipsec_icmp,
++ sizeof(int), 0644, NULL, &proc_dointvec},
++ { NET_IPSEC_INBOUND_POLICY_CHECK, "inbound_policy_check", &sysctl_ipsec_inbound_policy_check,
++ sizeof(int), 0644, NULL, &proc_dointvec},
++ { NET_IPSEC_TOS, "tos", &sysctl_ipsec_tos,
++ sizeof(int), 0644, NULL, &proc_dointvec},
++ {0}
++};
++
++static ctl_table ipsec_net_table[] = {
++ { NET_IPSEC, "ipsec", NULL, 0, 0555, ipsec_table },
++ { 0 }
++};
++
++static ctl_table ipsec_root_table[] = {
++ { CTL_NET, "net", NULL, 0, 0555, ipsec_net_table },
++ { 0 }
++};
++
++static struct ctl_table_header *ipsec_table_header;
++
++int ipsec_sysctl_register(void)
++{
++ ipsec_table_header = register_sysctl_table(ipsec_root_table, 0);
++ if (!ipsec_table_header) {
++ return -ENOMEM;
++ }
++ return 0;
++}
++
++void ipsec_sysctl_unregister(void)
++{
++ unregister_sysctl_table(ipsec_table_header);
++}
++
++#endif /* CONFIG_SYSCTL */
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.16 2004/04/06 02:49:26 mcr
++ * pullup of algo code from alg-branch.
++ *
++ * Revision 1.15 2002/04/24 07:55:32 mcr
++ * #include patches and Makefiles for post-reorg compilation.
++ *
++ * Revision 1.14 2002/04/24 07:36:35 mcr
++ * Moved from ./klips/net/ipsec/sysctl_net_ipsec.c,v
++ *
++ * Revision 1.13 2002/01/12 02:58:32 mcr
++ * first regression test causes acquire messages to be lost
++ * 100% of the time. This is to help testing of pluto.
++ *
++ * Revision 1.12 2001/06/14 19:35:13 rgb
++ * Update copyright date.
++ *
++ * Revision 1.11 2001/02/26 19:58:13 rgb
++ * Drop sysctl_ipsec_{no_eroute_pass,opportunistic}, replaced by magic SAs.
++ *
++ * Revision 1.10 2000/09/16 01:50:15 rgb
++ * Protect sysctl_ipsec_debug_ipcomp with compiler defines too so that the
++ * linker won't blame rj_delete() for missing symbols. ;-> Damn statics...
++ *
++ * Revision 1.9 2000/09/15 23:17:51 rgb
++ * Moved stuff around to compile with debug off.
++ *
++ * Revision 1.8 2000/09/15 11:37:02 rgb
++ * Merge in heavily modified Svenning Soerensen's <svenning@post5.tele.dk>
++ * IPCOMP zlib deflate code.
++ *
++ * Revision 1.7 2000/09/15 07:37:15 rgb
++ * Munged silly log comment that was causing a warning.
++ *
++ * Revision 1.6 2000/09/15 04:58:23 rgb
++ * Added tos runtime switch.
++ * Removed 'sysctl_ipsec_' prefix from /proc/sys/net/ipsec/ filenames.
++ *
++ * Revision 1.5 2000/09/12 03:25:28 rgb
++ * Filled in and implemented sysctl.
++ *
++ * Revision 1.4 1999/04/11 00:29:03 henry
++ * GPL boilerplate
++ *
++ * Revision 1.3 1999/04/06 04:54:29 rgb
++ * Fix/Add RCSID Id: and Log: bits to make PHMDs happy. This includes
++ * patch shell fixes.
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/net/ipsec/version.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,44 @@
++/*
++ * return IPsec version information
++ * Copyright (C) 2001 Henry Spencer.
++ *
++ * This library is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU Library General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/lgpl.txt>.
++ *
++ * This library 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 Library General Public
++ * License for more details.
++ *
++ * RCSID $Id$
++ */
++
++#ifdef __KERNEL__
++#include <linux/netdevice.h>
++#endif
++
++#include "openswan.h"
++
++#define V "cvs2002Mar12_01:19:03" /* substituted in by Makefile */
++static const char openswan_number[] = V;
++static const char openswan_string[] = "Openswan " V;
++
++/*
++ - ipsec_version_code - return IPsec version number/code, as string
++ */
++const char *
++ipsec_version_code()
++{
++ return openswan_number;
++}
++
++/*
++ - ipsec_version_string - return full version string
++ */
++const char *
++ipsec_version_string()
++{
++ return openswan_string;
++}
+RCSID $Id$
+--- ./net/ipv4/af_inet.c.preipsec Wed Apr 26 15:13:17 2000
++++ ./net/ipv4/af_inet.c Fri Jun 30 15:01:27 2000
+@@ -1019,6 +1019,17 @@
+ ip_mr_init();
+ #endif
+
++#if defined(CONFIG_IPSEC)
++ {
++ extern /* void */ int ipsec_init(void);
++ /*
++ * Initialise AF_INET ESP and AH protocol support including
++ * e-routing and SA tables
++ */
++ ipsec_init();
++ }
++#endif /* CONFIG_IPSEC */
++
+ /*
+ * Create all the /proc entries.
+ */
+--- /dev/null Fri May 10 13:59:54 2002
++++ linux/net/ipsec/Makefile.ver Sun Jul 28 22:10:40 2002
+@@ -0,0 +1 @@
++IPSECVERSION=cvs2002Mar12_01:19:03
+make[1]: Leaving directory `/data/mtx/oe/tmp/work/openswan-2.2.0-r0/openswan-2.2.0'
diff --git a/linux/linux-mtx-1-2.4.24/14-au1000-eth-vlan.diff b/linux/linux-mtx-1-2.4.24/14-au1000-eth-vlan.diff
index e69de29bb2..a4fa3169cc 100644
--- a/linux/linux-mtx-1-2.4.24/14-au1000-eth-vlan.diff
+++ b/linux/linux-mtx-1-2.4.24/14-au1000-eth-vlan.diff
@@ -0,0 +1,10 @@
+--- linux/drivers/net/au1000_eth.c.orig 2004-11-18 13:44:52.163605416 +0100
++++ linux/drivers/net/au1000_eth.c 2004-11-18 13:51:42.096286176 +0100
+@@ -1384,6 +1384,7 @@
+ control |= MAC_FULL_DUPLEX;
+ }
+ aup->mac->control = control;
++ aup->mac->vlan1_tag = 0x8100; /* activate vlan support */
+ au_sync();
+
+ spin_unlock_irqrestore(&aup->lock, flags);
diff --git a/linux/linux-mtx-1-2.4.24/15-mtd-proc-partition-rw.diff b/linux/linux-mtx-1-2.4.24/15-mtd-proc-partition-rw.diff
index e69de29bb2..54ba5fff98 100644
--- a/linux/linux-mtx-1-2.4.24/15-mtd-proc-partition-rw.diff
+++ b/linux/linux-mtx-1-2.4.24/15-mtd-proc-partition-rw.diff
@@ -0,0 +1,173 @@
+diff -Nurb linux/drivers/mtd/mtdcore.c linux-mtd-rw/drivers/mtd/mtdcore.c
+--- linux/drivers/mtd/mtdcore.c 2004-11-18 13:16:00.000000000 +0100
++++ linux-mtd-rw/drivers/mtd/mtdcore.c 2004-11-18 15:27:13.130036616 +0100
+@@ -25,6 +25,10 @@
+
+ #include <linux/mtd/mtd.h>
+
++/* this symbol is exported by the procfs. */
++extern struct proc_dir_entry *proc_sys_root;
++
++
+ /* These are exported solely for the purpose of mtd_blkdevs.c. You
+ should not use them for _anything_ else */
+ DECLARE_MUTEX(mtd_table_mutex);
+@@ -336,8 +340,83 @@
+
+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
+ static struct proc_dir_entry *proc_mtd;
++
++static struct proc_dir_entry *proc_sys_mtd;
++static struct proc_dir_entry *proc_sys_mtd_partition[MAX_MTD_DEVICES];
++static struct proc_dir_entry *proc_sys_mtd_partition_rw[MAX_MTD_DEVICES];
+ #endif
+
++/*===================================0
++ * mtdproc_read_partition_access
++ */
++static int mtdproc_read_partition_access ( char *page, char **start, off_t off,int count,
++ int *eof, void *data
++ )
++{
++ int partid = (unsigned int)data;
++ int len = 0;
++
++ // NO RETURN FROM HERE UNTIL "up(&mtd_table_mutex)".
++ down(&mtd_table_mutex);
++
++ if (partid < MAX_MTD_DEVICES)
++ {
++ struct mtd_info *this = mtd_table[partid];
++ if (this)
++ {
++ page[len] = (this->flags & MTD_WRITEABLE) ? '1' : '0';
++ len++;
++ }
++ }
++
++ up(&mtd_table_mutex);
++
++ if (off >= len)
++ return 0;
++ *start = page + off;
++ return ((count < len-off) ? count : len-off);
++}
++
++
++static int mtdproc_write_partition_access (struct file *file, const char *buffer,
++ unsigned long count, void *data)
++{
++ int partid = (unsigned int)data;
++ int len = 0;
++
++ // NO RETURN FROM HERE UNTIL "up(&mtd_table_mutex)".
++ down(&mtd_table_mutex);
++
++ if (partid < MAX_MTD_DEVICES)
++ {
++ struct mtd_info *this = mtd_table[partid];
++ if (this && count > 0)
++ {
++ switch (*buffer)
++ {
++ case '0':
++ this->flags &= ~(this->master_flags & MTD_WRITEABLE);
++ break;
++
++ case '1':
++ this->flags |= ~(this->master_flags & MTD_WRITEABLE);
++ break;
++
++ default:
++ break;
++ }
++ }
++ }
++
++ up(&mtd_table_mutex);
++
++ return count;
++}
++
++
++
++
++
+ static inline int mtd_proc_info (char *buf, int i)
+ {
+ struct mtd_info *this = mtd_table[i];
+@@ -349,6 +428,7 @@
+ this->erasesize, this->name);
+ }
+
++
+ static int mtd_read_proc ( char *page, char **start, off_t off,int count
+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
+ ,int *eof, void *data_unused
+@@ -404,12 +484,31 @@
+ /*====================================================================*/
+ /* Init code */
+
++
+ int __init init_mtd(void)
+ {
+ #ifdef CONFIG_PROC_FS
+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
++ int i;
++
+ if ((proc_mtd = create_proc_entry( "mtd", 0, 0 )))
+ proc_mtd->read_proc = mtd_read_proc;
++
++ proc_sys_mtd = proc_mkdir("mtd", proc_sys_root);
++ for (i=0; i<MAX_MTD_DEVICES; i++)
++ {
++ char partname[10];
++ sprintf (partname, "%d", i);
++
++ proc_sys_mtd_partition[i] = proc_mkdir(partname, proc_sys_mtd);
++ proc_sys_mtd_partition_rw[i] = create_proc_entry ("rw", S_IFREG | S_IRUGO, proc_sys_mtd_partition[i]);
++ if (proc_sys_mtd_partition_rw[i])
++ {
++ proc_sys_mtd_partition_rw[i]->read_proc = mtdproc_read_partition_access;
++ proc_sys_mtd_partition_rw[i]->write_proc = mtdproc_write_partition_access;
++ proc_sys_mtd_partition_rw[i]->data = (void *)i;
++ }
++ }
+ #else
+ proc_register_dynamic(&proc_root,&mtd_proc_entry);
+ #endif
+@@ -425,6 +524,8 @@
+ return 0;
+ }
+
++
++
+ static void __exit cleanup_mtd(void)
+ {
+ #ifdef CONFIG_PM
+diff -Nurb linux/drivers/mtd/mtdpart.c linux-mtd-rw/drivers/mtd/mtdpart.c
+--- linux/drivers/mtd/mtdpart.c 2004-11-18 13:16:00.000000000 +0100
++++ linux-mtd-rw/drivers/mtd/mtdpart.c 2004-11-18 15:27:13.131036464 +0100
+@@ -341,6 +341,9 @@
+ /* set up the MTD object for this partition */
+ slave->mtd.type = master->type;
+ slave->mtd.flags = master->flags & ~parts[i].mask_flags;
++ slave->mtd.master_flags = master->flags;
++ slave->mtd.mask_flags = parts[i].mask_flags;
++
+ slave->mtd.size = parts[i].size;
+ slave->mtd.oobblock = master->oobblock;
+ slave->mtd.oobsize = master->oobsize;
+diff -Nurb linux/include/linux/mtd/mtd.h linux-mtd-rw/include/linux/mtd/mtd.h
+--- linux/include/linux/mtd/mtd.h 2004-11-18 13:16:31.000000000 +0100
++++ linux-mtd-rw/include/linux/mtd/mtd.h 2004-11-18 15:27:13.000000000 +0100
+@@ -232,6 +232,9 @@
+
+ struct module *owner;
+ int usecount;
++
++ u_int32_t master_flags;
++ u_int32_t mask_flags;
+ };
+
+
diff --git a/linux/linux-mtx-1-2.4.27/01-mtd-2004-01-27.diff b/linux/linux-mtx-1-2.4.27/01-mtd-2004-01-27.diff
index e69de29bb2..74e6375385 100644
--- a/linux/linux-mtx-1-2.4.27/01-mtd-2004-01-27.diff
+++ b/linux/linux-mtx-1-2.4.27/01-mtd-2004-01-27.diff
@@ -0,0 +1,51503 @@
+diff -Nurb linux-mips-2.4.27/drivers/mtd/Config.in linux/drivers/mtd/Config.in
+--- linux-mips-2.4.27/drivers/mtd/Config.in 2003-02-26 01:53:49.000000000 +0100
++++ linux/drivers/mtd/Config.in 2004-11-19 10:25:11.607244176 +0100
+@@ -1,5 +1,5 @@
+
+-# $Id$
++# $Id$
+
+ mainmenu_option next_comment
+ comment 'Memory Technology Devices (MTD)'
+@@ -30,6 +30,7 @@
+ if [ "$CONFIG_NFTL" = "y" -o "$CONFIG_NFTL" = "m" ]; then
+ bool ' Write support for NFTL (BETA)' CONFIG_NFTL_RW
+ fi
++ dep_tristate ' INFTL (Inverse NAND Flash Translation Layer) support' CONFIG_INFTL $CONFIG_MTD
+
+ source drivers/mtd/chips/Config.in
+
+diff -Nurb linux-mips-2.4.27/drivers/mtd/Makefile linux/drivers/mtd/Makefile
+--- linux-mips-2.4.27/drivers/mtd/Makefile 2003-02-26 01:53:49.000000000 +0100
++++ linux/drivers/mtd/Makefile 2004-11-19 10:25:11.608244024 +0100
+@@ -1,30 +1,7 @@
+ #
+ # Makefile for the memory technology device drivers.
+ #
+-# Note! Dependencies are done automagically by 'make dep', which also
+-# removes any old dependencies. DON'T put your own dependencies here
+-# unless it's something special (ie not a .c file).
+-#
+-# Note 2! The CFLAGS definitions are now inherited from the
+-# parent makes..
+-#
+-# $Id$
+-
+-
+-obj-y += chips/chipslink.o maps/mapslink.o \
+- devices/devlink.o nand/nandlink.o
+-obj-m :=
+-obj-n :=
+-obj- :=
+-
+-O_TARGET := mtdlink.o
+-
+-export-objs := mtdcore.o mtdpart.o redboot.o cmdlinepart.o afs.o mtdconcat.o
+-list-multi := nftl.o
+-
+-mod-subdirs :=
+-subdir-y := chips maps devices nand
+-subdir-m := $(subdir-y)
++# $Id$
+
+ # *** BIG UGLY NOTE ***
+ #
+@@ -52,15 +29,44 @@
+
+ # 'Users' - code which presents functionality to userspace.
+ obj-$(CONFIG_MTD_CHAR) += mtdchar.o
+-obj-$(CONFIG_MTD_BLOCK) += mtdblock.o
+-obj-$(CONFIG_MTD_BLOCK_RO) += mtdblock_ro.o
+-obj-$(CONFIG_FTL) += ftl.o
+-obj-$(CONFIG_NFTL) += nftl.o
++obj-$(CONFIG_MTD_BLOCK) += mtdblock.o mtd_blkdevs.o
++obj-$(CONFIG_MTD_BLOCK_RO) += mtdblock_ro.o mtd_blkdevs.o
++obj-$(CONFIG_FTL) += ftl.o mtd_blkdevs.o
++obj-$(CONFIG_NFTL) += nftl.o mtd_blkdevs.o
++obj-$(CONFIG_INFTL) += inftl.o mtd_blkdevs.o
+
+ nftl-objs := nftlcore.o nftlmount.o
++inftl-objs := inftlcore.o inftlmount.o
++
++ifeq ($(PATCHLEVEL),4)
++
++export-objs := mtdcore.o mtdpart.o redboot.o cmdlinepart.o afs.o \
++ mtdconcat.o mtd_blkdevs-24.o
++
++mtd_blkdevs-objs := mtd_blkdevs-24.o
++
++obj-y += chips/chipslink.o maps/mapslink.o \
++ devices/devlink.o nand/nandlink.o
++
++O_TARGET := mtdlink.o
++
++list-multi := nftl.o inftl.o mtd_blkdevs.o
++
++mod-subdirs :=
++subdir-y := chips maps devices nand
++subdir-m := $(subdir-y)
+
+ include $(TOPDIR)/Rules.make
+
+ nftl.o: $(nftl-objs)
+ $(LD) -r -o $@ $(nftl-objs)
+
++inftl.o: $(inftl-objs)
++ $(LD) -r -o $@ $(inftl-objs)
++
++mtd_blkdevs.o: $(mtd_blkdevs-objs)
++ $(LD) -r -o $@ $(mtd_blkdevs-objs)
++
++else
++obj-y += chips/ maps/ devices/ nand/
++endif
+diff -Nurb linux-mips-2.4.27/drivers/mtd/afs.c linux/drivers/mtd/afs.c
+--- linux-mips-2.4.27/drivers/mtd/afs.c 2003-02-26 01:53:49.000000000 +0100
++++ linux/drivers/mtd/afs.c 2004-11-19 10:25:11.610243720 +0100
+@@ -21,7 +21,7 @@
+ This is access code for flashes using ARM's flash partitioning
+ standards.
+
+- $Id$
++ $Id$
+
+ ======================================================================*/
+
+@@ -76,17 +76,19 @@
+ return ret;
+ }
+
++ ret = 1;
++
+ /*
+ * Does it contain the magic number?
+ */
+ if (fs.signature != 0xa0ffff9f)
+- ret = 1;
++ ret = 0;
+
+ /*
+ * Don't touch the SIB.
+ */
+ if (fs.type == 2)
+- ret = 1;
++ ret = 0;
+
+ *iis_start = fs.image_info_base & mask;
+ *img_start = fs.image_start & mask;
+@@ -96,14 +98,14 @@
+ * be located after the footer structure.
+ */
+ if (*iis_start >= ptr)
+- ret = 1;
++ ret = 0;
+
+ /*
+ * Check the start of this image. The image
+ * data can not be located after this block.
+ */
+ if (*img_start > off)
+- ret = 1;
++ ret = 0;
+
+ return ret;
+ }
+@@ -125,7 +127,9 @@
+ return ret;
+ }
+
+-int parse_afs_partitions(struct mtd_info *mtd, struct mtd_partition **pparts)
++static int parse_afs_partitions(struct mtd_info *mtd,
++ struct mtd_partition **pparts,
++ unsigned long origin)
+ {
+ struct mtd_partition *parts;
+ u_int mask, off, idx, sz;
+@@ -150,7 +154,7 @@
+ ret = afs_read_footer(mtd, &img_ptr, &iis_ptr, off, mask);
+ if (ret < 0)
+ break;
+- if (ret == 1)
++ if (ret == 0)
+ continue;
+
+ ret = afs_read_iis(mtd, &iis, iis_ptr);
+@@ -183,7 +187,7 @@
+ ret = afs_read_footer(mtd, &img_ptr, &iis_ptr, off, mask);
+ if (ret < 0)
+ break;
+- if (ret == 1)
++ if (ret == 0)
+ continue;
+
+ /* Read the image info block */
+@@ -227,7 +231,25 @@
+ return idx ? idx : ret;
+ }
+
+-EXPORT_SYMBOL(parse_afs_partitions);
++static struct mtd_part_parser afs_parser = {
++ .owner = THIS_MODULE,
++ .parse_fn = parse_afs_partitions,
++ .name = "afs",
++};
++
++static int __init afs_parser_init(void)
++{
++ return register_mtd_parser(&afs_parser);
++}
++
++static void __exit afs_parser_exit(void)
++{
++ deregister_mtd_parser(&afs_parser);
++}
++
++module_init(afs_parser_init);
++module_exit(afs_parser_exit);
++
+
+ MODULE_AUTHOR("ARM Ltd");
+ MODULE_DESCRIPTION("ARM Firmware Suite partition parser");
+diff -Nurb linux-mips-2.4.27/drivers/mtd/chips/Config.in linux/drivers/mtd/chips/Config.in
+--- linux-mips-2.4.27/drivers/mtd/chips/Config.in 2003-02-26 01:53:49.000000000 +0100
++++ linux/drivers/mtd/chips/Config.in 2004-11-19 10:25:11.712228216 +0100
+@@ -1,6 +1,6 @@
+ # drivers/mtd/chips/Config.in
+
+-# $Id$
++# $Id$
+
+ mainmenu_option next_comment
+
+@@ -11,13 +11,12 @@
+
+ if [ "$CONFIG_MTD_CFI" = "y" -o "$CONFIG_MTD_JEDECPROBE" = "y" ]; then
+ define_bool CONFIG_MTD_GEN_PROBE y
+-else
+- if [ "$CONFIG_MTD_CFI" = "m" -o "$CONFIG_MTD_JEDECPROBE" = "m" ]; then
++elif [ "$CONFIG_MTD_CFI" = "m" -o "$CONFIG_MTD_JEDECPROBE" = "m" ]; then
+ define_bool CONFIG_MTD_GEN_PROBE m
+- else
++else
+ define_bool CONFIG_MTD_GEN_PROBE n
+- fi
+ fi
++
+ if [ "$CONFIG_MTD_GEN_PROBE" = "y" -o "$CONFIG_MTD_GEN_PROBE" = "m" ]; then
+ bool ' Flash chip driver advanced configuration options' CONFIG_MTD_CFI_ADV_OPTIONS
+ if [ "$CONFIG_MTD_CFI_ADV_OPTIONS" = "y" ]; then
+@@ -44,8 +43,27 @@
+ fi
+ dep_tristate ' Support for Intel/Sharp flash chips' CONFIG_MTD_CFI_INTELEXT $CONFIG_MTD_GEN_PROBE
+ dep_tristate ' Support for AMD/Fujitsu flash chips' CONFIG_MTD_CFI_AMDSTD $CONFIG_MTD_GEN_PROBE
++if [ "$CONFIG_MTD_CFI_AMDSTD" = "y" -o "$CONFIG_MTD_CFI_AMDSTD" = "m" ]; then
++ bool ' Retry failed commands (erase/program)' CONFIG_MTD_CFI_AMDSTD_RETRY n
++ if [ "$CONFIG_MTD_CFI_AMDSTD_RETRY" = "y" ]; then
++ int ' Max retries of failed commands (erase/program)' CONFIG_MTD_CFI_AMDSTD_RETRY_MAX 0
++ fi
++fi
++
+ dep_tristate ' Support for ST (Advanced Architecture) flash chips' CONFIG_MTD_CFI_STAA $CONFIG_MTD_GEN_PROBE
+
++if [ "$CONFIG_MTD_CFI_INTELEXT" = "y" \
++ -o "$CONFIG_MTD_CFI_AMDSTD" = "y" \
++ -o "$CONFIG_MTD_CFI_STAA" = "y" ]; then
++ define_bool CONFIG_MTD_CFI_UTIL y
++elif [ "$CONFIG_MTD_CFI_INTELEXT" = "m" \
++ -o "$CONFIG_MTD_CFI_AMDSTD" = "m" \
++ -o "$CONFIG_MTD_CFI_STAA" = "m" ]; then
++ define_bool CONFIG_MTD_CFI_UTIL m
++else
++ define_bool CONFIG_MTD_CFI_UTIL n
++fi
++
+ dep_tristate ' Support for RAM chips in bus mapping' CONFIG_MTD_RAM $CONFIG_MTD
+ dep_tristate ' Support for ROM chips in bus mapping' CONFIG_MTD_ROM $CONFIG_MTD
+ dep_tristate ' Support for absent chips in bus mapping' CONFIG_MTD_ABSENT $CONFIG_MTD
+diff -Nurb linux-mips-2.4.27/drivers/mtd/chips/Makefile linux/drivers/mtd/chips/Makefile
+--- linux-mips-2.4.27/drivers/mtd/chips/Makefile 2003-07-05 05:23:38.000000000 +0200
++++ linux/drivers/mtd/chips/Makefile 2004-11-19 10:25:11.714227912 +0100
+@@ -1,11 +1,12 @@
+ #
+ # linux/drivers/chips/Makefile
+ #
+-# $Id$
++# $Id$
+
++ifeq ($(PATCHLEVEL),4)
+ O_TARGET := chipslink.o
+-
+-export-objs := chipreg.o gen_probe.o
++export-objs := chipreg.o gen_probe.o cfi_util.o
++endif
+
+ # *** BIG UGLY NOTE ***
+ #
+@@ -17,6 +18,7 @@
+ obj-$(CONFIG_MTD) += chipreg.o
+ obj-$(CONFIG_MTD_AMDSTD) += amd_flash.o
+ obj-$(CONFIG_MTD_CFI) += cfi_probe.o
++obj-$(CONFIG_MTD_CFI_UTIL) += cfi_util.o
+ obj-$(CONFIG_MTD_CFI_STAA) += cfi_cmdset_0020.o
+ obj-$(CONFIG_MTD_CFI_AMDSTD) += cfi_cmdset_0002.o
+ obj-$(CONFIG_MTD_CFI_INTELEXT) += cfi_cmdset_0001.o
+@@ -28,4 +30,4 @@
+ obj-$(CONFIG_MTD_SHARP) += sharp.o
+ obj-$(CONFIG_MTD_ABSENT) += map_absent.o
+
+-include $(TOPDIR)/Rules.make
++-include $(TOPDIR)/Rules.make
+diff -Nurb linux-mips-2.4.27/drivers/mtd/chips/amd_flash.c linux/drivers/mtd/chips/amd_flash.c
+--- linux-mips-2.4.27/drivers/mtd/chips/amd_flash.c 2003-02-26 01:53:49.000000000 +0100
++++ linux/drivers/mtd/chips/amd_flash.c 2004-11-19 10:25:11.722226696 +0100
+@@ -3,7 +3,7 @@
+ *
+ * Author: Jonas Holmberg <jonas.holmberg@axis.com>
+ *
+- * $Id$
++ * $Id$
+ *
+ * Copyright (c) 2001 Axis Communications AB
+ *
+@@ -19,6 +19,7 @@
+ #include <linux/slab.h>
+ #include <linux/delay.h>
+ #include <linux/interrupt.h>
++#include <linux/init.h>
+ #include <linux/mtd/map.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/flashchip.h>
+@@ -125,10 +126,10 @@
+
+
+ static struct mtd_chip_driver amd_flash_chipdrv = {
+- probe: amd_flash_probe,
+- destroy: amd_flash_destroy,
+- name: "amd_flash",
+- module: THIS_MODULE
++ .probe = amd_flash_probe,
++ .destroy = amd_flash_destroy,
++ .name = "amd_flash",
++ .module = THIS_MODULE
+ };
+
+
+@@ -140,11 +141,11 @@
+ static inline __u32 wide_read(struct map_info *map, __u32 addr)
+ {
+ if (map->buswidth == 1) {
+- return map->read8(map, addr);
++ return map_read8(map, addr);
+ } else if (map->buswidth == 2) {
+- return map->read16(map, addr);
++ return map_read16(map, addr);
+ } else if (map->buswidth == 4) {
+- return map->read32(map, addr);
++ return map_read32(map, addr);
+ }
+
+ return 0;
+@@ -153,11 +154,11 @@
+ static inline void wide_write(struct map_info *map, __u32 val, __u32 addr)
+ {
+ if (map->buswidth == 1) {
+- map->write8(map, val, addr);
++ map_write8(map, val, addr);
+ } else if (map->buswidth == 2) {
+- map->write16(map, val, addr);
++ map_write16(map, val, addr);
+ } else if (map->buswidth == 4) {
+- map->write32(map, val, addr);
++ map_write32(map, val, addr);
+ }
+ }
+
+@@ -424,231 +425,228 @@
+
+ static struct mtd_info *amd_flash_probe(struct map_info *map)
+ {
+- /* Keep this table on the stack so that it gets deallocated after the
+- * probe is done.
+- */
+- const struct amd_flash_info table[] = {
++ static const struct amd_flash_info table[] = {
+ {
+- mfr_id: MANUFACTURER_AMD,
+- dev_id: AM29LV160DT,
+- name: "AMD AM29LV160DT",
+- size: 0x00200000,
+- numeraseregions: 4,
+- regions: {
+- { offset: 0x000000, erasesize: 0x10000, numblocks: 31 },
+- { offset: 0x1F0000, erasesize: 0x08000, numblocks: 1 },
+- { offset: 0x1F8000, erasesize: 0x02000, numblocks: 2 },
+- { offset: 0x1FC000, erasesize: 0x04000, numblocks: 1 }
++ .mfr_id = MANUFACTURER_AMD,
++ .dev_id = AM29LV160DT,
++ .name = "AMD AM29LV160DT",
++ .size = 0x00200000,
++ .numeraseregions = 4,
++ .regions = {
++ { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 31 },
++ { .offset = 0x1F0000, .erasesize = 0x08000, .numblocks = 1 },
++ { .offset = 0x1F8000, .erasesize = 0x02000, .numblocks = 2 },
++ { .offset = 0x1FC000, .erasesize = 0x04000, .numblocks = 1 }
+ }
+ }, {
+- mfr_id: MANUFACTURER_AMD,
+- dev_id: AM29LV160DB,
+- name: "AMD AM29LV160DB",
+- size: 0x00200000,
+- numeraseregions: 4,
+- regions: {
+- { offset: 0x000000, erasesize: 0x04000, numblocks: 1 },
+- { offset: 0x004000, erasesize: 0x02000, numblocks: 2 },
+- { offset: 0x008000, erasesize: 0x08000, numblocks: 1 },
+- { offset: 0x010000, erasesize: 0x10000, numblocks: 31 }
++ .mfr_id = MANUFACTURER_AMD,
++ .dev_id = AM29LV160DB,
++ .name = "AMD AM29LV160DB",
++ .size = 0x00200000,
++ .numeraseregions = 4,
++ .regions = {
++ { .offset = 0x000000, .erasesize = 0x04000, .numblocks = 1 },
++ { .offset = 0x004000, .erasesize = 0x02000, .numblocks = 2 },
++ { .offset = 0x008000, .erasesize = 0x08000, .numblocks = 1 },
++ { .offset = 0x010000, .erasesize = 0x10000, .numblocks = 31 }
+ }
+ }, {
+- mfr_id: MANUFACTURER_TOSHIBA,
+- dev_id: TC58FVT160,
+- name: "Toshiba TC58FVT160",
+- size: 0x00200000,
+- numeraseregions: 4,
+- regions: {
+- { offset: 0x000000, erasesize: 0x10000, numblocks: 31 },
+- { offset: 0x1F0000, erasesize: 0x08000, numblocks: 1 },
+- { offset: 0x1F8000, erasesize: 0x02000, numblocks: 2 },
+- { offset: 0x1FC000, erasesize: 0x04000, numblocks: 1 }
++ .mfr_id = MANUFACTURER_TOSHIBA,
++ .dev_id = TC58FVT160,
++ .name = "Toshiba TC58FVT160",
++ .size = 0x00200000,
++ .numeraseregions = 4,
++ .regions = {
++ { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 31 },
++ { .offset = 0x1F0000, .erasesize = 0x08000, .numblocks = 1 },
++ { .offset = 0x1F8000, .erasesize = 0x02000, .numblocks = 2 },
++ { .offset = 0x1FC000, .erasesize = 0x04000, .numblocks = 1 }
+ }
+ }, {
+- mfr_id: MANUFACTURER_FUJITSU,
+- dev_id: MBM29LV160TE,
+- name: "Fujitsu MBM29LV160TE",
+- size: 0x00200000,
+- numeraseregions: 4,
+- regions: {
+- { offset: 0x000000, erasesize: 0x10000, numblocks: 31 },
+- { offset: 0x1F0000, erasesize: 0x08000, numblocks: 1 },
+- { offset: 0x1F8000, erasesize: 0x02000, numblocks: 2 },
+- { offset: 0x1FC000, erasesize: 0x04000, numblocks: 1 }
++ .mfr_id = MANUFACTURER_FUJITSU,
++ .dev_id = MBM29LV160TE,
++ .name = "Fujitsu MBM29LV160TE",
++ .size = 0x00200000,
++ .numeraseregions = 4,
++ .regions = {
++ { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 31 },
++ { .offset = 0x1F0000, .erasesize = 0x08000, .numblocks = 1 },
++ { .offset = 0x1F8000, .erasesize = 0x02000, .numblocks = 2 },
++ { .offset = 0x1FC000, .erasesize = 0x04000, .numblocks = 1 }
+ }
+ }, {
+- mfr_id: MANUFACTURER_TOSHIBA,
+- dev_id: TC58FVB160,
+- name: "Toshiba TC58FVB160",
+- size: 0x00200000,
+- numeraseregions: 4,
+- regions: {
+- { offset: 0x000000, erasesize: 0x04000, numblocks: 1 },
+- { offset: 0x004000, erasesize: 0x02000, numblocks: 2 },
+- { offset: 0x008000, erasesize: 0x08000, numblocks: 1 },
+- { offset: 0x010000, erasesize: 0x10000, numblocks: 31 }
++ .mfr_id = MANUFACTURER_TOSHIBA,
++ .dev_id = TC58FVB160,
++ .name = "Toshiba TC58FVB160",
++ .size = 0x00200000,
++ .numeraseregions = 4,
++ .regions = {
++ { .offset = 0x000000, .erasesize = 0x04000, .numblocks = 1 },
++ { .offset = 0x004000, .erasesize = 0x02000, .numblocks = 2 },
++ { .offset = 0x008000, .erasesize = 0x08000, .numblocks = 1 },
++ { .offset = 0x010000, .erasesize = 0x10000, .numblocks = 31 }
+ }
+ }, {
+- mfr_id: MANUFACTURER_FUJITSU,
+- dev_id: MBM29LV160BE,
+- name: "Fujitsu MBM29LV160BE",
+- size: 0x00200000,
+- numeraseregions: 4,
+- regions: {
+- { offset: 0x000000, erasesize: 0x04000, numblocks: 1 },
+- { offset: 0x004000, erasesize: 0x02000, numblocks: 2 },
+- { offset: 0x008000, erasesize: 0x08000, numblocks: 1 },
+- { offset: 0x010000, erasesize: 0x10000, numblocks: 31 }
++ .mfr_id = MANUFACTURER_FUJITSU,
++ .dev_id = MBM29LV160BE,
++ .name = "Fujitsu MBM29LV160BE",
++ .size = 0x00200000,
++ .numeraseregions = 4,
++ .regions = {
++ { .offset = 0x000000, .erasesize = 0x04000, .numblocks = 1 },
++ { .offset = 0x004000, .erasesize = 0x02000, .numblocks = 2 },
++ { .offset = 0x008000, .erasesize = 0x08000, .numblocks = 1 },
++ { .offset = 0x010000, .erasesize = 0x10000, .numblocks = 31 }
+ }
+ }, {
+- mfr_id: MANUFACTURER_AMD,
+- dev_id: AM29LV800BB,
+- name: "AMD AM29LV800BB",
+- size: 0x00100000,
+- numeraseregions: 4,
+- regions: {
+- { offset: 0x000000, erasesize: 0x04000, numblocks: 1 },
+- { offset: 0x004000, erasesize: 0x02000, numblocks: 2 },
+- { offset: 0x008000, erasesize: 0x08000, numblocks: 1 },
+- { offset: 0x010000, erasesize: 0x10000, numblocks: 15 }
++ .mfr_id = MANUFACTURER_AMD,
++ .dev_id = AM29LV800BB,
++ .name = "AMD AM29LV800BB",
++ .size = 0x00100000,
++ .numeraseregions = 4,
++ .regions = {
++ { .offset = 0x000000, .erasesize = 0x04000, .numblocks = 1 },
++ { .offset = 0x004000, .erasesize = 0x02000, .numblocks = 2 },
++ { .offset = 0x008000, .erasesize = 0x08000, .numblocks = 1 },
++ { .offset = 0x010000, .erasesize = 0x10000, .numblocks = 15 }
+ }
+ }, {
+- mfr_id: MANUFACTURER_AMD,
+- dev_id: AM29F800BB,
+- name: "AMD AM29F800BB",
+- size: 0x00100000,
+- numeraseregions: 4,
+- regions: {
+- { offset: 0x000000, erasesize: 0x04000, numblocks: 1 },
+- { offset: 0x004000, erasesize: 0x02000, numblocks: 2 },
+- { offset: 0x008000, erasesize: 0x08000, numblocks: 1 },
+- { offset: 0x010000, erasesize: 0x10000, numblocks: 15 }
++ .mfr_id = MANUFACTURER_AMD,
++ .dev_id = AM29F800BB,
++ .name = "AMD AM29F800BB",
++ .size = 0x00100000,
++ .numeraseregions = 4,
++ .regions = {
++ { .offset = 0x000000, .erasesize = 0x04000, .numblocks = 1 },
++ { .offset = 0x004000, .erasesize = 0x02000, .numblocks = 2 },
++ { .offset = 0x008000, .erasesize = 0x08000, .numblocks = 1 },
++ { .offset = 0x010000, .erasesize = 0x10000, .numblocks = 15 }
+ }
+ }, {
+- mfr_id: MANUFACTURER_AMD,
+- dev_id: AM29LV800BT,
+- name: "AMD AM29LV800BT",
+- size: 0x00100000,
+- numeraseregions: 4,
+- regions: {
+- { offset: 0x000000, erasesize: 0x10000, numblocks: 15 },
+- { offset: 0x0F0000, erasesize: 0x08000, numblocks: 1 },
+- { offset: 0x0F8000, erasesize: 0x02000, numblocks: 2 },
+- { offset: 0x0FC000, erasesize: 0x04000, numblocks: 1 }
++ .mfr_id = MANUFACTURER_AMD,
++ .dev_id = AM29LV800BT,
++ .name = "AMD AM29LV800BT",
++ .size = 0x00100000,
++ .numeraseregions = 4,
++ .regions = {
++ { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 15 },
++ { .offset = 0x0F0000, .erasesize = 0x08000, .numblocks = 1 },
++ { .offset = 0x0F8000, .erasesize = 0x02000, .numblocks = 2 },
++ { .offset = 0x0FC000, .erasesize = 0x04000, .numblocks = 1 }
+ }
+ }, {
+- mfr_id: MANUFACTURER_AMD,
+- dev_id: AM29F800BT,
+- name: "AMD AM29F800BT",
+- size: 0x00100000,
+- numeraseregions: 4,
+- regions: {
+- { offset: 0x000000, erasesize: 0x10000, numblocks: 15 },
+- { offset: 0x0F0000, erasesize: 0x08000, numblocks: 1 },
+- { offset: 0x0F8000, erasesize: 0x02000, numblocks: 2 },
+- { offset: 0x0FC000, erasesize: 0x04000, numblocks: 1 }
++ .mfr_id = MANUFACTURER_AMD,
++ .dev_id = AM29F800BT,
++ .name = "AMD AM29F800BT",
++ .size = 0x00100000,
++ .numeraseregions = 4,
++ .regions = {
++ { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 15 },
++ { .offset = 0x0F0000, .erasesize = 0x08000, .numblocks = 1 },
++ { .offset = 0x0F8000, .erasesize = 0x02000, .numblocks = 2 },
++ { .offset = 0x0FC000, .erasesize = 0x04000, .numblocks = 1 }
+ }
+ }, {
+- mfr_id: MANUFACTURER_AMD,
+- dev_id: AM29LV800BB,
+- name: "AMD AM29LV800BB",
+- size: 0x00100000,
+- numeraseregions: 4,
+- regions: {
+- { offset: 0x000000, erasesize: 0x10000, numblocks: 15 },
+- { offset: 0x0F0000, erasesize: 0x08000, numblocks: 1 },
+- { offset: 0x0F8000, erasesize: 0x02000, numblocks: 2 },
+- { offset: 0x0FC000, erasesize: 0x04000, numblocks: 1 }
++ .mfr_id = MANUFACTURER_AMD,
++ .dev_id = AM29LV800BB,
++ .name = "AMD AM29LV800BB",
++ .size = 0x00100000,
++ .numeraseregions = 4,
++ .regions = {
++ { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 15 },
++ { .offset = 0x0F0000, .erasesize = 0x08000, .numblocks = 1 },
++ { .offset = 0x0F8000, .erasesize = 0x02000, .numblocks = 2 },
++ { .offset = 0x0FC000, .erasesize = 0x04000, .numblocks = 1 }
+ }
+ }, {
+- mfr_id: MANUFACTURER_FUJITSU,
+- dev_id: MBM29LV800BB,
+- name: "Fujitsu MBM29LV800BB",
+- size: 0x00100000,
+- numeraseregions: 4,
+- regions: {
+- { offset: 0x000000, erasesize: 0x04000, numblocks: 1 },
+- { offset: 0x004000, erasesize: 0x02000, numblocks: 2 },
+- { offset: 0x008000, erasesize: 0x08000, numblocks: 1 },
+- { offset: 0x010000, erasesize: 0x10000, numblocks: 15 }
++ .mfr_id = MANUFACTURER_FUJITSU,
++ .dev_id = MBM29LV800BB,
++ .name = "Fujitsu MBM29LV800BB",
++ .size = 0x00100000,
++ .numeraseregions = 4,
++ .regions = {
++ { .offset = 0x000000, .erasesize = 0x04000, .numblocks = 1 },
++ { .offset = 0x004000, .erasesize = 0x02000, .numblocks = 2 },
++ { .offset = 0x008000, .erasesize = 0x08000, .numblocks = 1 },
++ { .offset = 0x010000, .erasesize = 0x10000, .numblocks = 15 }
+ }
+ }, {
+- mfr_id: MANUFACTURER_ST,
+- dev_id: M29W800T,
+- name: "ST M29W800T",
+- size: 0x00100000,
+- numeraseregions: 4,
+- regions: {
+- { offset: 0x000000, erasesize: 0x10000, numblocks: 15 },
+- { offset: 0x0F0000, erasesize: 0x08000, numblocks: 1 },
+- { offset: 0x0F8000, erasesize: 0x02000, numblocks: 2 },
+- { offset: 0x0FC000, erasesize: 0x04000, numblocks: 1 }
++ .mfr_id = MANUFACTURER_ST,
++ .dev_id = M29W800T,
++ .name = "ST M29W800T",
++ .size = 0x00100000,
++ .numeraseregions = 4,
++ .regions = {
++ { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 15 },
++ { .offset = 0x0F0000, .erasesize = 0x08000, .numblocks = 1 },
++ { .offset = 0x0F8000, .erasesize = 0x02000, .numblocks = 2 },
++ { .offset = 0x0FC000, .erasesize = 0x04000, .numblocks = 1 }
+ }
+ }, {
+- mfr_id: MANUFACTURER_ST,
+- dev_id: M29W160DT,
+- name: "ST M29W160DT",
+- size: 0x00200000,
+- numeraseregions: 4,
+- regions: {
+- { offset: 0x000000, erasesize: 0x10000, numblocks: 31 },
+- { offset: 0x1F0000, erasesize: 0x08000, numblocks: 1 },
+- { offset: 0x1F8000, erasesize: 0x02000, numblocks: 2 },
+- { offset: 0x1FC000, erasesize: 0x04000, numblocks: 1 }
++ .mfr_id = MANUFACTURER_ST,
++ .dev_id = M29W160DT,
++ .name = "ST M29W160DT",
++ .size = 0x00200000,
++ .numeraseregions = 4,
++ .regions = {
++ { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 31 },
++ { .offset = 0x1F0000, .erasesize = 0x08000, .numblocks = 1 },
++ { .offset = 0x1F8000, .erasesize = 0x02000, .numblocks = 2 },
++ { .offset = 0x1FC000, .erasesize = 0x04000, .numblocks = 1 }
+ }
+ }, {
+- mfr_id: MANUFACTURER_ST,
+- dev_id: M29W160DB,
+- name: "ST M29W160DB",
+- size: 0x00200000,
+- numeraseregions: 4,
+- regions: {
+- { offset: 0x000000, erasesize: 0x04000, numblocks: 1 },
+- { offset: 0x004000, erasesize: 0x02000, numblocks: 2 },
+- { offset: 0x008000, erasesize: 0x08000, numblocks: 1 },
+- { offset: 0x010000, erasesize: 0x10000, numblocks: 31 }
++ .mfr_id = MANUFACTURER_ST,
++ .dev_id = M29W160DB,
++ .name = "ST M29W160DB",
++ .size = 0x00200000,
++ .numeraseregions = 4,
++ .regions = {
++ { .offset = 0x000000, .erasesize = 0x04000, .numblocks = 1 },
++ { .offset = 0x004000, .erasesize = 0x02000, .numblocks = 2 },
++ { .offset = 0x008000, .erasesize = 0x08000, .numblocks = 1 },
++ { .offset = 0x010000, .erasesize = 0x10000, .numblocks = 31 }
+ }
+ }, {
+- mfr_id: MANUFACTURER_AMD,
+- dev_id: AM29BDS323D,
+- name: "AMD AM29BDS323D",
+- size: 0x00400000,
+- numeraseregions: 3,
+- regions: {
+- { offset: 0x000000, erasesize: 0x10000, numblocks: 48 },
+- { offset: 0x300000, erasesize: 0x10000, numblocks: 15 },
+- { offset: 0x3f0000, erasesize: 0x02000, numblocks: 8 },
++ .mfr_id = MANUFACTURER_AMD,
++ .dev_id = AM29BDS323D,
++ .name = "AMD AM29BDS323D",
++ .size = 0x00400000,
++ .numeraseregions = 3,
++ .regions = {
++ { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 48 },
++ { .offset = 0x300000, .erasesize = 0x10000, .numblocks = 15 },
++ { .offset = 0x3f0000, .erasesize = 0x02000, .numblocks = 8 },
+ }
+ }, {
+- mfr_id: MANUFACTURER_AMD,
+- dev_id: AM29BDS643D,
+- name: "AMD AM29BDS643D",
+- size: 0x00800000,
+- numeraseregions: 3,
+- regions: {
+- { offset: 0x000000, erasesize: 0x10000, numblocks: 96 },
+- { offset: 0x600000, erasesize: 0x10000, numblocks: 31 },
+- { offset: 0x7f0000, erasesize: 0x02000, numblocks: 8 },
++ .mfr_id = MANUFACTURER_AMD,
++ .dev_id = AM29BDS643D,
++ .name = "AMD AM29BDS643D",
++ .size = 0x00800000,
++ .numeraseregions = 3,
++ .regions = {
++ { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 96 },
++ { .offset = 0x600000, .erasesize = 0x10000, .numblocks = 31 },
++ { .offset = 0x7f0000, .erasesize = 0x02000, .numblocks = 8 },
+ }
+ }, {
+- mfr_id: MANUFACTURER_ATMEL,
+- dev_id: AT49xV16x,
+- name: "Atmel AT49xV16x",
+- size: 0x00200000,
+- numeraseregions: 2,
+- regions: {
+- { offset: 0x000000, erasesize: 0x02000, numblocks: 8 },
+- { offset: 0x010000, erasesize: 0x10000, numblocks: 31 }
++ .mfr_id = MANUFACTURER_ATMEL,
++ .dev_id = AT49xV16x,
++ .name = "Atmel AT49xV16x",
++ .size = 0x00200000,
++ .numeraseregions = 2,
++ .regions = {
++ { .offset = 0x000000, .erasesize = 0x02000, .numblocks = 8 },
++ { .offset = 0x010000, .erasesize = 0x10000, .numblocks = 31 }
+ }
+ }, {
+- mfr_id: MANUFACTURER_ATMEL,
+- dev_id: AT49xV16xT,
+- name: "Atmel AT49xV16xT",
+- size: 0x00200000,
+- numeraseregions: 2,
+- regions: {
+- { offset: 0x000000, erasesize: 0x10000, numblocks: 31 },
+- { offset: 0x1F0000, erasesize: 0x02000, numblocks: 8 }
++ .mfr_id = MANUFACTURER_ATMEL,
++ .dev_id = AT49xV16xT,
++ .name = "Atmel AT49xV16xT",
++ .size = 0x00200000,
++ .numeraseregions = 2,
++ .regions = {
++ { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 31 },
++ { .offset = 0x1F0000, .erasesize = 0x02000, .numblocks = 8 }
+ }
+ }
+ };
+@@ -822,7 +820,7 @@
+
+ chip->state = FL_READY;
+
+- map->copy_from(map, buf, adr, len);
++ map_copy_from(map, buf, adr, len);
+
+ wake_up(&chip->wq);
+ spin_unlock_bh(chip->mutex);
+@@ -984,7 +982,7 @@
+ u_char tmp_buf[4];
+ __u32 datum;
+
+- map->copy_from(map, tmp_buf,
++ map_copy_from(map, tmp_buf,
+ bus_ofs + private->chips[chipnum].start,
+ map->buswidth);
+ while (len && i < map->buswidth)
+@@ -1057,7 +1055,7 @@
+ u_char tmp_buf[2];
+ __u32 datum;
+
+- map->copy_from(map, tmp_buf,
++ map_copy_from(map, tmp_buf,
+ ofs + private->chips[chipnum].start,
+ map->buswidth);
+ while (len--) {
+@@ -1178,7 +1176,7 @@
+ __u8 verify;
+
+ for (address = adr; address < (adr + size); address++) {
+- if ((verify = map->read8(map, address)) != 0xFF) {
++ if ((verify = map_read8(map, address)) != 0xFF) {
+ error = 1;
+ break;
+ }
+diff -Nurb linux-mips-2.4.27/drivers/mtd/chips/cfi_cmdset_0001.c linux/drivers/mtd/chips/cfi_cmdset_0001.c
+--- linux-mips-2.4.27/drivers/mtd/chips/cfi_cmdset_0001.c 2003-07-05 05:23:38.000000000 +0200
++++ linux/drivers/mtd/chips/cfi_cmdset_0001.c 2004-11-19 10:25:11.733225024 +0100
+@@ -4,7 +4,7 @@
+ *
+ * (C) 2000 Red Hat. GPL'd
+ *
+- * $Id$
++ * $Id$
+ *
+ *
+ * 10/10/2000 Nicolas Pitre <nico@cam.org>
+@@ -21,6 +21,7 @@
+ #include <linux/types.h>
+ #include <linux/kernel.h>
+ #include <linux/sched.h>
++#include <linux/init.h>
+ #include <asm/io.h>
+ #include <asm/byteorder.h>
+
+@@ -29,10 +30,14 @@
+ #include <linux/delay.h>
+ #include <linux/interrupt.h>
+ #include <linux/mtd/map.h>
+-#include <linux/mtd/cfi.h>
++#include <linux/mtd/mtd.h>
+ #include <linux/mtd/compatmac.h>
++#include <linux/mtd/cfi.h>
++
++/* #define CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE */
+
+-// debugging, turns off buffer write mode #define FORCE_WORD_WRITE
++// debugging, turns off buffer write mode if set to 1
++#define FORCE_WORD_WRITE 0
+
+ static int cfi_intelext_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
+ static int cfi_intelext_read_user_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
+@@ -52,16 +57,21 @@
+
+ static struct mtd_info *cfi_intelext_setup (struct map_info *);
+
+-static int do_point (struct mtd_info *mtd, loff_t from, size_t len,
++static int cfi_intelext_point (struct mtd_info *mtd, loff_t from, size_t len,
+ size_t *retlen, u_char **mtdbuf);
+-static void do_unpoint (struct mtd_info *mtd, u_char *addr, loff_t from,
++static void cfi_intelext_unpoint (struct mtd_info *mtd, u_char *addr, loff_t from,
+ size_t len);
+
++
++/*
++ * *********** SETUP AND PROBE BITS ***********
++ */
++
+ static struct mtd_chip_driver cfi_intelext_chipdrv = {
+- probe: NULL, /* Not usable directly */
+- destroy: cfi_intelext_destroy,
+- name: "cfi_cmdset_0001",
+- module: THIS_MODULE
++ .probe = NULL, /* Not usable directly */
++ .destroy = cfi_intelext_destroy,
++ .name = "cfi_cmdset_0001",
++ .module = THIS_MODULE
+ };
+
+ /* #define DEBUG_LOCK_BITS */
+@@ -102,13 +112,63 @@
+ }
+
+ printk(" Vcc Logic Supply Optimum Program/Erase Voltage: %d.%d V\n",
+- extp->VccOptimal >> 8, extp->VccOptimal & 0xf);
++ extp->VccOptimal >> 4, extp->VccOptimal & 0xf);
+ if (extp->VppOptimal)
+ printk(" Vpp Programming Supply Optimum Program/Erase Voltage: %d.%d V\n",
+- extp->VppOptimal >> 8, extp->VppOptimal & 0xf);
++ extp->VppOptimal >> 4, extp->VppOptimal & 0xf);
+ }
+ #endif
+
++#ifdef CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE
++/* Some Intel Strata Flash prior to FPO revision C has bugs in this area */
++static void fixup_intel_strataflash(struct map_info *map, void* param)
++{
++ struct cfi_private *cfi = map->fldrv_priv;
++ struct cfi_pri_amdstd *extp = cfi->cmdset_priv;
++
++ printk(KERN_WARNING "cfi_cmdset_0001: Suspend "
++ "erase on write disabled.\n");
++ extp->SuspendCmdSupport &= ~1;
++}
++#endif
++
++static void fixup_st_m28w320ct(struct map_info *map, void* param)
++{
++ struct cfi_private *cfi = map->fldrv_priv;
++
++ cfi->cfiq->BufWriteTimeoutTyp = 0; /* Not supported */
++ cfi->cfiq->BufWriteTimeoutMax = 0; /* Not supported */
++}
++
++static void fixup_st_m28w320cb(struct map_info *map, void* param)
++{
++ struct cfi_private *cfi = map->fldrv_priv;
++
++ /* Note this is done after the region info is endian swapped */
++ cfi->cfiq->EraseRegionInfo[1] =
++ (cfi->cfiq->EraseRegionInfo[1] & 0xffff0000) | 0x3e;
++};
++
++static struct cfi_fixup fixup_table[] = {
++#ifdef CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE
++ {
++ CFI_MFR_ANY, CFI_ID_ANY,
++ fixup_intel_strataflash, NULL
++ },
++#endif
++ {
++ 0x0020, /* STMicroelectronics */
++ 0x00ba, /* M28W320CT */
++ fixup_st_m28w320ct, NULL
++ }, {
++ 0x0020, /* STMicroelectronics */
++ 0x00bb, /* M28W320CB */
++ fixup_st_m28w320cb, NULL
++ }, {
++ 0, 0, NULL, NULL
++ }
++};
++
+ /* This routine is made available to other mtd code via
+ * inter_module_register. It must only be accessed through
+ * inter_module_get which will bump the use count of this module. The
+@@ -120,7 +180,6 @@
+ {
+ struct cfi_private *cfi = map->fldrv_priv;
+ int i;
+- __u32 base = cfi->chips[0].start;
+
+ if (cfi->cfi_mode == CFI_MODE_CFI) {
+ /*
+@@ -130,59 +189,29 @@
+ */
+ __u16 adr = primary?cfi->cfiq->P_ADR:cfi->cfiq->A_ADR;
+ struct cfi_pri_intelext *extp;
+- int ofs_factor = cfi->interleave * cfi->device_type;
+-
+- //printk(" Intel/Sharp Extended Query Table at 0x%4.4X\n", adr);
+- if (!adr)
+- return NULL;
+
+- /* Switch it into Query Mode */
+- cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL);
+-
+- extp = kmalloc(sizeof(*extp), GFP_KERNEL);
+- if (!extp) {
+- printk(KERN_ERR "Failed to allocate memory\n");
++ extp = (struct cfi_pri_intelext*)cfi_read_pri(map, adr, sizeof(*extp), "Intel/Sharp");
++ if (!extp)
+ return NULL;
+- }
+-
+- /* Read in the Extended Query Table */
+- for (i=0; i<sizeof(*extp); i++) {
+- ((unsigned char *)extp)[i] =
+- cfi_read_query(map, (base+((adr+i)*ofs_factor)));
+- }
+-
+- if (extp->MajorVersion != '1' ||
+- (extp->MinorVersion < '0' || extp->MinorVersion > '3')) {
+- printk(KERN_WARNING " Unknown IntelExt Extended Query "
+- "version %c.%c.\n", extp->MajorVersion,
+- extp->MinorVersion);
+- kfree(extp);
+- return NULL;
+- }
+
+ /* Do some byteswapping if necessary */
+ extp->FeatureSupport = le32_to_cpu(extp->FeatureSupport);
+ extp->BlkStatusRegMask = le16_to_cpu(extp->BlkStatusRegMask);
+ extp->ProtRegAddr = le16_to_cpu(extp->ProtRegAddr);
+
++ /* Install our own private info structure */
++ cfi->cmdset_priv = extp;
++
++ cfi_fixup(map, fixup_table);
++
+ #ifdef DEBUG_CFI_FEATURES
+ /* Tell the user about it in lots of lovely detail */
+ cfi_tell_features(extp);
+ #endif
+
+ if(extp->SuspendCmdSupport & 1) {
+-//#define CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE
+-#ifdef CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE
+-/* Some Intel Strata Flash prior to FPO revision C has bugs in this area */
+- printk(KERN_WARNING "cfi_cmdset_0001: Suspend "
+- "erase on write disabled.\n");
+- extp->SuspendCmdSupport &= ~1;
+-#else
+ printk(KERN_NOTICE "cfi_cmdset_0001: Erase suspend on write enabled\n");
+-#endif
+ }
+- /* Install our own private info structure */
+- cfi->cmdset_priv = extp;
+ }
+
+ for (i=0; i< cfi->numchips; i++) {
+@@ -194,8 +223,6 @@
+
+ map->fldrv = &cfi_intelext_chipdrv;
+
+- /* Make sure it's in read mode */
+- cfi_send_gen_cmd(0xff, 0x55, base, map, cfi, cfi->device_type, NULL);
+ return cfi_intelext_setup(map);
+ }
+
+@@ -261,20 +288,16 @@
+ mtd->erase = cfi_intelext_erase_varsize;
+ mtd->read = cfi_intelext_read;
+
+- if(map->point && map->unpoint){
+- mtd->point = do_point;
+- mtd->unpoint = do_unpoint;
++ if (map_is_linear(map)) {
++ mtd->point = cfi_intelext_point;
++ mtd->unpoint = cfi_intelext_unpoint;
+ }
+
+-#ifndef FORCE_WORD_WRITE
+- if ( cfi->cfiq->BufWriteTimeoutTyp ) {
+- printk("Using buffer write method\n" );
++ if ( cfi->cfiq->BufWriteTimeoutTyp && !FORCE_WORD_WRITE) {
++ printk(KERN_INFO "Using buffer write method\n" );
+ mtd->write = cfi_intelext_write_buffers;
+ } else {
+-#else
+- {
+-#endif
+- printk("Using word write method\n" );
++ printk(KERN_INFO "Using word write method\n" );
+ mtd->write = cfi_intelext_write_words;
+ }
+ mtd->read_user_prot_reg = cfi_intelext_read_user_prot_reg;
+@@ -286,8 +309,8 @@
+ mtd->resume = cfi_intelext_resume;
+ mtd->flags = MTD_CAP_NORFLASH;
+ map->fldrv = &cfi_intelext_chipdrv;
+- MOD_INC_USE_COUNT;
+ mtd->name = map->name;
++ __module_get(THIS_MODULE);
+ return mtd;
+
+ setup_err:
+@@ -301,78 +324,170 @@
+ return NULL;
+ }
+
+-static int do_point_onechip (struct map_info *map, struct flchip *chip, loff_t adr, size_t len)
++/*
++ * *********** CHIP ACCESS FUNCTIONS ***********
++ */
++
++static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr, int mode)
+ {
+- cfi_word status, status_OK;
+- unsigned long timeo;
+ DECLARE_WAITQUEUE(wait, current);
+- unsigned long cmd_addr;
+ struct cfi_private *cfi = map->fldrv_priv;
++ cfi_word status, status_OK = CMD(0x80);
++ unsigned long timeo;
++ struct cfi_pri_intelext *cfip = (struct cfi_pri_intelext *)cfi->cmdset_priv;
+
+- adr += chip->start;
+-
+- /* Ensure cmd read/writes are aligned. */
+- cmd_addr = adr & ~(CFIDEV_BUSWIDTH-1);
+-
+- /* Let's determine this according to the interleave only once */
+- status_OK = CMD(0x80);
+-
++ resettime:
+ timeo = jiffies + HZ;
+ retry:
+- spin_lock(chip->mutex);
+-
+- /* Check that the chip's ready to talk to us.
+- * If it's in FL_ERASING state, suspend it and make it talk now.
+- */
+ switch (chip->state) {
+
+- case FL_READY:
+- case FL_POINT:
++ case FL_STATUS:
++ for (;;) {
++ status = cfi_read(map, adr);
++ if ((status & status_OK) == status_OK)
+ break;
+
++ if (time_after(jiffies, timeo)) {
++ printk(KERN_ERR "Waiting for chip to be ready timed out. Status %llx\n",
++ (long long)status);
++ spin_unlock(chip->mutex);
++ return -EIO;
++ }
++ spin_unlock(chip->mutex);
++ cfi_udelay(1);
++ spin_lock(chip->mutex);
++ /* Someone else might have been playing with it. */
++ goto retry;
++ }
++
++ case FL_READY:
+ case FL_CFI_QUERY:
+ case FL_JEDEC_QUERY:
+- cfi_write(map, CMD(0x70), cmd_addr);
+- chip->state = FL_STATUS;
++ return 0;
+
+- case FL_STATUS:
+- status = cfi_read(map, cmd_addr);
+- if ((status & status_OK) == status_OK) {
+- cfi_write(map, CMD(0xff), cmd_addr);
+- chip->state = FL_READY;
++ case FL_ERASING:
++ if (!(cfip->FeatureSupport & 2) ||
++ !(mode == FL_READY || mode == FL_POINT ||
++ (mode == FL_WRITING && (cfip->SuspendCmdSupport & 1))))
++ goto sleep;
++
++
++ /* Erase suspend */
++ cfi_write(map, CMD(0xB0), adr);
++
++ /* If the flash has finished erasing, then 'erase suspend'
++ * appears to make some (28F320) flash devices switch to
++ * 'read' mode. Make sure that we switch to 'read status'
++ * mode so we get the right data. --rmk
++ */
++ cfi_write(map, CMD(0x70), adr);
++ chip->oldstate = FL_ERASING;
++ chip->state = FL_ERASE_SUSPENDING;
++ chip->erase_suspended = 1;
++ for (;;) {
++ status = cfi_read(map, adr);
++ if ((status & status_OK) == status_OK)
+ break;
+- }
+
+- /* Urgh. Chip not yet ready to talk to us. */
+ if (time_after(jiffies, timeo)) {
+- spin_unlock(chip->mutex);
+- printk(KERN_ERR "waiting for chip to be ready timed out in read. WSM status = %llx\n", (__u64)status);
++ /* Urgh. Resume and pretend we weren't here. */
++ cfi_write(map, CMD(0xd0), adr);
++ /* Make sure we're in 'read status' mode if it had finished */
++ cfi_write(map, CMD(0x70), adr);
++ chip->state = FL_ERASING;
++ chip->oldstate = FL_READY;
++ printk(KERN_ERR "Chip not ready after erase "
++ "suspended: status = 0x%x\n", status);
+ return -EIO;
+ }
+
+- /* Latency issues. Drop the lock, wait a while and retry */
+ spin_unlock(chip->mutex);
+ cfi_udelay(1);
+- goto retry;
++ spin_lock(chip->mutex);
++ /* Nobody will touch it while it's in state FL_ERASE_SUSPENDING.
++ So we can just loop here. */
++ }
++ chip->state = FL_STATUS;
++ return 0;
++
++ case FL_POINT:
++ /* Only if there's no operation suspended... */
++ if (mode == FL_READY && chip->oldstate == FL_READY)
++ return 0;
+
+ default:
+- /* Stick ourselves on a wait queue to be woken when
+- someone changes the status */
++ sleep:
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ add_wait_queue(&chip->wq, &wait);
+ spin_unlock(chip->mutex);
+ schedule();
+ remove_wait_queue(&chip->wq, &wait);
+- timeo = jiffies + HZ;
+- goto retry;
++ spin_lock(chip->mutex);
++ goto resettime;
+ }
++}
++
++static void put_chip(struct map_info *map, struct flchip *chip, unsigned long adr)
++{
++ struct cfi_private *cfi = map->fldrv_priv;
++
++ switch(chip->oldstate) {
++ case FL_ERASING:
++ chip->state = chip->oldstate;
++ /* What if one interleaved chip has finished and the
++ other hasn't? The old code would leave the finished
++ one in READY mode. That's bad, and caused -EROFS
++ errors to be returned from do_erase_oneblock because
++ that's the only bit it checked for at the time.
++ As the state machine appears to explicitly allow
++ sending the 0x70 (Read Status) command to an erasing
++ chip and expecting it to be ignored, that's what we
++ do. */
++ cfi_write(map, CMD(0xd0), adr);
++ cfi_write(map, CMD(0x70), adr);
++ chip->oldstate = FL_READY;
++ chip->state = FL_ERASING;
++ break;
++
++ case FL_READY:
++ case FL_STATUS:
++ /* We should really make set_vpp() count, rather than doing this */
++ DISABLE_VPP(map);
++ break;
++ default:
++ printk(KERN_ERR "put_chip() called with oldstate %d!!\n", chip->oldstate);
++ }
++ wake_up(&chip->wq);
++}
++
++static int do_point_onechip (struct map_info *map, struct flchip *chip, loff_t adr, size_t len)
++{
++ unsigned long cmd_addr;
++ struct cfi_private *cfi = map->fldrv_priv;
++ int ret = 0;
++
++ adr += chip->start;
++
++ /* Ensure cmd read/writes are aligned. */
++ cmd_addr = adr & ~(CFIDEV_BUSWIDTH-1);
++
++ spin_lock(chip->mutex);
++
++ ret = get_chip(map, chip, cmd_addr, FL_POINT);
++
++ if (!ret) {
++ if (chip->state != FL_POINT && chip->state != FL_READY)
++ cfi_write(map, CMD(0xff), cmd_addr);
+
+ chip->state = FL_POINT;
+ chip->ref_point_counter++;
++ }
+ spin_unlock(chip->mutex);
+- return 0;
++
++ return ret;
+ }
+-static int do_point (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char **mtdbuf)
++
++static int cfi_intelext_point (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char **mtdbuf)
+ {
+ struct map_info *map = mtd->priv;
+ struct cfi_private *cfi = map->fldrv_priv;
+@@ -380,12 +495,10 @@
+ int chipnum;
+ int ret = 0;
+
+- if (from + len > mtd->size)
++ if (!map->virt || (from + len > mtd->size))
+ return -EINVAL;
+
+- *mtdbuf = map->point(map, from, len);
+- if(*mtdbuf == NULL)
+- return -EINVAL; /* can not point this region */
++ *mtdbuf = (void *)map->virt + from;
+ *retlen = 0;
+
+ /* Now lock the chip(s) to POINT state */
+@@ -418,14 +531,13 @@
+ return 0;
+ }
+
+-static void do_unpoint (struct mtd_info *mtd, u_char *addr, loff_t from, size_t len)
++static void cfi_intelext_unpoint (struct mtd_info *mtd, u_char *addr, loff_t from, size_t len)
+ {
+ struct map_info *map = mtd->priv;
+ struct cfi_private *cfi = map->fldrv_priv;
+ unsigned long ofs;
+ int chipnum;
+
+- map->unpoint(map, addr, from, len);
+ /* Now unlock the chip(s) POINT state */
+
+ /* ofs: offset within the first chip that the first read should start */
+@@ -446,13 +558,14 @@
+ thislen = len;
+
+ spin_lock(chip->mutex);
+- if(chip->state == FL_POINT){
++ if (chip->state == FL_POINT) {
+ chip->ref_point_counter--;
+ if(chip->ref_point_counter == 0)
+ chip->state = FL_READY;
+ } else
+- printk("Warning: unpoint called on non pointed region\n"); /* Should this give an error? */
+- wake_up(&chip->wq);
++ printk(KERN_ERR "Warning: unpoint called on non pointed region\n"); /* Should this give an error? */
++
++ put_chip(map, chip, chip->start);
+ spin_unlock(chip->mutex);
+
+ len -= thislen;
+@@ -463,136 +576,32 @@
+
+ static inline int do_read_onechip(struct map_info *map, struct flchip *chip, loff_t adr, size_t len, u_char *buf)
+ {
+- cfi_word status, status_OK;
+- unsigned long timeo;
+- DECLARE_WAITQUEUE(wait, current);
+- int suspended = 0;
+ unsigned long cmd_addr;
+ struct cfi_private *cfi = map->fldrv_priv;
++ int ret;
+
+ adr += chip->start;
+
+ /* Ensure cmd read/writes are aligned. */
+ cmd_addr = adr & ~(CFIDEV_BUSWIDTH-1);
+
+- /* Let's determine this according to the interleave only once */
+- status_OK = CMD(0x80);
+-
+- timeo = jiffies + HZ;
+- retry:
+ spin_lock(chip->mutex);
+-
+- /* Check that the chip's ready to talk to us.
+- * If it's in FL_ERASING state, suspend it and make it talk now.
+- */
+- switch (chip->state) {
+- case FL_ERASING:
+- if (!cfi->cmdset_priv ||
+- !(((struct cfi_pri_intelext *)cfi->cmdset_priv)->FeatureSupport & 2))
+- goto sleep; /* We don't support erase suspend */
+-
+- cfi_write (map, CMD(0xb0), cmd_addr);
+- /* If the flash has finished erasing, then 'erase suspend'
+- * appears to make some (28F320) flash devices switch to
+- * 'read' mode. Make sure that we switch to 'read status'
+- * mode so we get the right data. --rmk
+- */
+- cfi_write(map, CMD(0x70), cmd_addr);
+- chip->oldstate = FL_ERASING;
+- chip->state = FL_ERASE_SUSPENDING;
+- // printk("Erase suspending at 0x%lx\n", cmd_addr);
+- for (;;) {
+- status = cfi_read(map, cmd_addr);
+- if ((status & status_OK) == status_OK)
+- break;
+-
+- if (time_after(jiffies, timeo)) {
+- /* Urgh */
+- cfi_write(map, CMD(0xd0), cmd_addr);
+- /* make sure we're in 'read status' mode */
+- cfi_write(map, CMD(0x70), cmd_addr);
+- chip->state = FL_ERASING;
+- spin_unlock(chip->mutex);
+- printk(KERN_ERR "Chip not ready after erase "
+- "suspended: status = 0x%llx\n", (__u64)status);
+- return -EIO;
+- }
+-
++ ret = get_chip(map, chip, cmd_addr, FL_READY);
++ if (ret) {
+ spin_unlock(chip->mutex);
+- cfi_udelay(1);
+- spin_lock(chip->mutex);
++ return ret;
+ }
+
+- suspended = 1;
++ if (chip->state != FL_POINT && chip->state != FL_READY) {
+ cfi_write(map, CMD(0xff), cmd_addr);
+- chip->state = FL_READY;
+- break;
+-
+-#if 0
+- case FL_WRITING:
+- /* Not quite yet */
+-#endif
+-
+- case FL_READY:
+- case FL_POINT:
+- break;
+-
+- case FL_CFI_QUERY:
+- case FL_JEDEC_QUERY:
+- cfi_write(map, CMD(0x70), cmd_addr);
+- chip->state = FL_STATUS;
+
+- case FL_STATUS:
+- status = cfi_read(map, cmd_addr);
+- if ((status & status_OK) == status_OK) {
+- cfi_write(map, CMD(0xff), cmd_addr);
+ chip->state = FL_READY;
+- break;
+ }
+
+- /* Urgh. Chip not yet ready to talk to us. */
+- if (time_after(jiffies, timeo)) {
+- spin_unlock(chip->mutex);
+- printk(KERN_ERR "waiting for chip to be ready timed out in read. WSM status = %llx\n", (__u64)status);
+- return -EIO;
+- }
++ map_copy_from(map, buf, adr, len);
+
+- /* Latency issues. Drop the lock, wait a while and retry */
+- spin_unlock(chip->mutex);
+- cfi_udelay(1);
+- goto retry;
++ put_chip(map, chip, cmd_addr);
+
+- default:
+- sleep:
+- /* Stick ourselves on a wait queue to be woken when
+- someone changes the status */
+- set_current_state(TASK_UNINTERRUPTIBLE);
+- add_wait_queue(&chip->wq, &wait);
+- spin_unlock(chip->mutex);
+- schedule();
+- remove_wait_queue(&chip->wq, &wait);
+- timeo = jiffies + HZ;
+- goto retry;
+- }
+-
+- map->copy_from(map, buf, adr, len);
+-
+- if (suspended) {
+- chip->state = chip->oldstate;
+- /* What if one interleaved chip has finished and the
+- other hasn't? The old code would leave the finished
+- one in READY mode. That's bad, and caused -EROFS
+- errors to be returned from do_erase_oneblock because
+- that's the only bit it checked for at the time.
+- As the state machine appears to explicitly allow
+- sending the 0x70 (Read Status) command to an erasing
+- chip and expecting it to be ignored, that's what we
+- do. */
+- cfi_write(map, CMD(0xd0), cmd_addr);
+- cfi_write(map, CMD(0x70), cmd_addr);
+- }
+-
+- wake_up(&chip->wq);
+ spin_unlock(chip->mutex);
+ return 0;
+ }
+@@ -640,70 +649,52 @@
+ {
+ struct map_info *map = mtd->priv;
+ struct cfi_private *cfi = map->fldrv_priv;
+- struct cfi_pri_intelext *extp=cfi->cmdset_priv;
+- int ofs_factor = cfi->interleave * cfi->device_type;
+- int count=len;
++ struct cfi_pri_intelext *extp = cfi->cmdset_priv;
+ struct flchip *chip;
+- int chip_num,offst;
+- unsigned long timeo;
+- DECLARE_WAITQUEUE(wait, current);
++ int ofs_factor = cfi->interleave * cfi->device_type;
++ int count = len;
++ int chip_num, offst;
++ int ret;
+
+- chip=0;
+- /* Calculate which chip & protection register offset we need */
+- chip_num=((unsigned int)from/reg_sz);
+- offst=from-(reg_sz*chip_num)+base_offst;
++ chip_num = ((unsigned int)from/reg_sz);
++ offst = from - (reg_sz*chip_num)+base_offst;
+
+- while(count){
++ while (count) {
++ /* Calculate which chip & protection register offset we need */
+
+- if(chip_num>=cfi->numchips)
++ if (chip_num >= cfi->numchips)
+ goto out;
+
+- /* Make sure that the chip is in the right state */
++ chip = &cfi->chips[chip_num];
+
+- timeo = jiffies + HZ;
+- chip=&cfi->chips[chip_num];
+- retry:
+ spin_lock(chip->mutex);
+-
+- switch (chip->state) {
+- case FL_READY:
+- case FL_STATUS:
+- case FL_CFI_QUERY:
+- case FL_JEDEC_QUERY:
+- break;
+-
+- default:
+- /* Stick ourselves on a wait queue to be woken when
+- someone changes the status */
+- set_current_state(TASK_UNINTERRUPTIBLE);
+- add_wait_queue(&chip->wq, &wait);
++ ret = get_chip(map, chip, chip->start, FL_JEDEC_QUERY);
++ if (ret) {
+ spin_unlock(chip->mutex);
+- schedule();
+- remove_wait_queue(&chip->wq, &wait);
+- timeo = jiffies + HZ;
+- goto retry;
++ return (len-count)?:ret;
+ }
+
+- /* Now read the data required from this flash */
++ if (chip->state != FL_JEDEC_QUERY) {
++ cfi_write(map, CMD(0x90), chip->start);
++ chip->state = FL_JEDEC_QUERY;
++ }
+
+- cfi_send_gen_cmd(0x90, 0x55,chip->start, map, cfi, cfi->device_type, NULL);
+- while(count && ((offst-base_offst)<reg_sz)){
+- *buf=map->read8(map,(chip->start+((extp->ProtRegAddr+1)*ofs_factor)+offst));
++ while (count && ((offst-base_offst) < reg_sz)) {
++ *buf = map_read8(map,(chip->start+((extp->ProtRegAddr+1)*ofs_factor)+offst));
+ buf++;
+ offst++;
+ count--;
+ }
+
+- chip->state=FL_CFI_QUERY;
++ put_chip(map, chip, chip->start);
+ spin_unlock(chip->mutex);
++
+ /* Move on to the next chip */
+ chip_num++;
+- offst=base_offst;
+-
++ offst = base_offst;
+ }
+
+ out:
+- wake_up(&chip->wq);
+ return len-count;
+ }
+
+@@ -749,103 +740,20 @@
+ static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned long adr, cfi_word datum)
+ {
+ struct cfi_private *cfi = map->fldrv_priv;
+- struct cfi_pri_intelext *extp = cfi->cmdset_priv;
+- cfi_word status, status_OK;
+- unsigned long timeo;
+- DECLARE_WAITQUEUE(wait, current);
+- int z, suspended=0, ret=0;
+-
+- adr += chip->start;
+-
+- /* Let's determine this according to the interleave only once */
+- status_OK = CMD(0x80);
+-
+- timeo = jiffies + HZ;
+- retry:
+- spin_lock(chip->mutex);
+-
+- /* Check that the chip's ready to talk to us.
+- * Later, we can actually think about interrupting it
+- * if it's in FL_ERASING state.
+- * Not just yet, though.
+- */
+- switch (chip->state) {
+- case FL_READY:
+- break;
+-
+- case FL_CFI_QUERY:
+- case FL_JEDEC_QUERY:
+- cfi_write(map, CMD(0x70), adr);
+- chip->state = FL_STATUS;
+-
+- case FL_STATUS:
+- status = cfi_read(map, adr);
+- if ((status & status_OK) == status_OK)
+- break;
+-
+- /* Urgh. Chip not yet ready to talk to us. */
+- if (time_after(jiffies, timeo)) {
+- spin_unlock(chip->mutex);
+- printk(KERN_ERR "waiting for chip to be ready timed out in read\n");
+- return -EIO;
+- }
+-
+- /* Latency issues. Drop the lock, wait a while and retry */
+- spin_unlock(chip->mutex);
+- cfi_udelay(1);
+- goto retry;
+-
+- case FL_ERASING:
+- if (!extp ||
+- !((extp->FeatureSupport & 2) && (extp->SuspendCmdSupport & 1)))
+- goto sleep; /* We don't support erase suspend */
+-
+- cfi_write (map, CMD(0xb0), adr);
+-
+- /* If the flash has finished erasing, then 'erase suspend'
+- * appears to make some (28F320) flash devices switch to
+- * 'read' mode. Make sure that we switch to 'read status'
+- * mode so we get the right data. --rmk
+- */
+- cfi_write(map, CMD(0x70), adr);
+- chip->oldstate = FL_ERASING;
+- chip->state = FL_ERASE_SUSPENDING;
+- for (;;) {
+- status = cfi_read(map, adr);
+- if ((status & status_OK) == status_OK)
+- break;
++ cfi_word status, status_OK;
++ unsigned long timeo;
++ int z, ret=0;
+
+- if (time_after(jiffies, timeo)) {
+- /* Urgh */
+- cfi_write(map, CMD(0xd0), adr);
+- /* make sure we're in 'read status' mode */
+- cfi_write(map, CMD(0x70), adr);
+- chip->state = FL_ERASING;
+- spin_unlock(chip->mutex);
+- printk(KERN_ERR "Chip not ready after erase "
+- "suspended: status = 0x%x\n", status);
+- return -EIO;
+- }
++ adr += chip->start;
+
+- spin_unlock(chip->mutex);
+- cfi_udelay(1);
+- spin_lock(chip->mutex);
+- }
+- suspended = 1;
+- chip->state = FL_STATUS;
+- break;
++ /* Let's determine this according to the interleave only once */
++ status_OK = CMD(0x80);
+
+- default:
+- sleep:
+- /* Stick ourselves on a wait queue to be woken when
+- someone changes the status */
+- set_current_state(TASK_UNINTERRUPTIBLE);
+- add_wait_queue(&chip->wq, &wait);
++ spin_lock(chip->mutex);
++ ret = get_chip(map, chip, adr, FL_WRITING);
++ if (ret) {
+ spin_unlock(chip->mutex);
+- schedule();
+- remove_wait_queue(&chip->wq, &wait);
+- timeo = jiffies + HZ;
+- goto retry;
++ return ret;
+ }
+
+ ENABLE_VPP(map);
+@@ -862,6 +770,8 @@
+ for (;;) {
+ if (chip->state != FL_WRITING) {
+ /* Someone's suspended the write. Sleep */
++ DECLARE_WAITQUEUE(wait, current);
++
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ add_wait_queue(&chip->wq, &wait);
+ spin_unlock(chip->mutex);
+@@ -879,7 +789,6 @@
+ /* OK Still waiting */
+ if (time_after(jiffies, timeo)) {
+ chip->state = FL_STATUS;
+- DISABLE_VPP(map);
+ printk(KERN_ERR "waiting for chip to be ready timed out in word write\n");
+ ret = -EIO;
+ goto out;
+@@ -908,27 +817,11 @@
+ /* put back into read status register mode */
+ cfi_write(map, CMD(0x70), adr);
+ ret = -EROFS;
+- goto out;
+ }
+ out:
+- if (suspended) {
+- chip->state = chip->oldstate;
+- /* What if one interleaved chip has finished and the
+- other hasn't? The old code would leave the finished
+- one in READY mode. That's bad, and caused -EROFS
+- errors to be returned from do_erase_oneblock because
+- that's the only bit it checked for at the time.
+- As the state machine appears to explicitly allow
+- sending the 0x70 (Read Status) command to an erasing
+- chip and expecting it to be ignored, that's what we
+- do. */
+- cfi_write(map, CMD(0xd0), adr);
+- cfi_write(map, CMD(0x70), adr);
+- } else
+- DISABLE_VPP(map); /* must not clear the VPP if there is a suspended erase to be resumed */
+-
+- wake_up(&chip->wq);
++ put_chip(map, chip, adr);
+ spin_unlock(chip->mutex);
++
+ return ret;
+ }
+
+@@ -1059,11 +952,9 @@
+ unsigned long adr, const u_char *buf, int len)
+ {
+ struct cfi_private *cfi = map->fldrv_priv;
+- struct cfi_pri_intelext *extp = cfi->cmdset_priv;
+ cfi_word status, status_OK;
+ unsigned long cmd_adr, timeo;
+- DECLARE_WAITQUEUE(wait, current);
+- int wbufsize, z, suspended=0, ret=0;
++ int wbufsize, z, ret=0, bytes, words;
+
+ wbufsize = CFIDEV_INTERLEAVE << cfi->cfiq->MaxBufWriteSize;
+ adr += chip->start;
+@@ -1072,91 +963,18 @@
+ /* Let's determine this according to the interleave only once */
+ status_OK = CMD(0x80);
+
+- timeo = jiffies + HZ;
+- retry:
+ spin_lock(chip->mutex);
+-
+- /* Check that the chip's ready to talk to us.
+- * Later, we can actually think about interrupting it
+- * if it's in FL_ERASING state.
+- * Not just yet, though.
+- */
+- switch (chip->state) {
+- case FL_READY:
+- case FL_CFI_QUERY:
+- case FL_JEDEC_QUERY:
+- cfi_write(map, CMD(0x70), cmd_adr);
+- chip->state = FL_STATUS;
+-
+- case FL_STATUS:
+- status = cfi_read(map, cmd_adr);
+- if ((status & status_OK) == status_OK)
+- break;
+- /* Urgh. Chip not yet ready to talk to us. */
+- if (time_after(jiffies, timeo)) {
++ ret = get_chip(map, chip, cmd_adr, FL_WRITING);
++ if (ret) {
+ spin_unlock(chip->mutex);
+- printk(KERN_ERR "waiting for chip to be ready timed out in buffer write\n");
+- return -EIO;
++ return ret;
+ }
+
+- /* Latency issues. Drop the lock, wait a while and retry */
+- spin_unlock(chip->mutex);
+- cfi_udelay(1);
+- goto retry;
+-
+- case FL_ERASING:
+- if (!extp ||
+- !((extp->FeatureSupport & 2) && (extp->SuspendCmdSupport & 1)))
+- goto sleep; /* We don't support erase suspend */
+-
+- cfi_write (map, CMD(0xb0), adr);
+-
+- /* If the flash has finished erasing, then 'erase suspend'
+- * appears to make some (28F320) flash devices switch to
+- * 'read' mode. Make sure that we switch to 'read status'
+- * mode so we get the right data. --rmk
+- */
+- cfi_write(map, CMD(0x70), adr);
+- chip->oldstate = FL_ERASING;
+- chip->state = FL_ERASE_SUSPENDING;
+- for (;;) {
+- status = cfi_read(map, adr);
+- if ((status & status_OK) == status_OK)
+- break;
+-
+- if (time_after(jiffies, timeo)) {
+- /* Urgh */
+- cfi_write(map, CMD(0xd0), adr);
+- /* make sure we're in 'read status' mode */
+- cfi_write(map, CMD(0x70), adr);
+- chip->state = FL_ERASING;
+- spin_unlock(chip->mutex);
+- printk(KERN_ERR "Chip not ready after erase "
+- "suspended: status = 0x%x\n", status);
+- return -EIO;
+- }
++ if (chip->state != FL_STATUS)
++ cfi_write(map, CMD(0x70), cmd_adr);
+
+- spin_unlock(chip->mutex);
+- cfi_udelay(1);
+- spin_lock(chip->mutex);
+- }
+- suspended = 1;
+- chip->state = FL_STATUS;
+- break;
++ status = cfi_read(map, cmd_adr);
+
+- default:
+- sleep:
+- /* Stick ourselves on a wait queue to be woken when
+- someone changes the status */
+- set_current_state(TASK_UNINTERRUPTIBLE);
+- add_wait_queue(&chip->wq, &wait);
+- spin_unlock(chip->mutex);
+- schedule();
+- remove_wait_queue(&chip->wq, &wait);
+- timeo = jiffies + HZ;
+- goto retry;
+- }
+- /* We know we're now in FL_STATUS mode, and 'status' is current */
+ /* §4.8 of the 28FxxxJ3A datasheet says "Any time SR.4 and/or SR.5 is set
+ [...], the device will not accept any more Write to Buffer commands".
+ So we must check here and reset those bits if they're set. Otherwise
+@@ -1185,7 +1003,6 @@
+ /* Argh. Not ready for write to buffer */
+ cfi_write(map, CMD(0x70), cmd_adr);
+ chip->state = FL_STATUS;
+- DISABLE_VPP(map);
+ printk(KERN_ERR "Chip not ready for buffer write. Xstatus = %llx, status = %llx\n", (__u64)status, (__u64)cfi_read(map, cmd_adr));
+ /* Odd. Clear status bits */
+ cfi_write(map, CMD(0x50), cmd_adr);
+@@ -1196,20 +1013,42 @@
+ }
+
+ /* Write length of data to come */
+- cfi_write(map, CMD(len/CFIDEV_BUSWIDTH-1), cmd_adr );
++ bytes = len & (CFIDEV_BUSWIDTH-1);
++ words = len / CFIDEV_BUSWIDTH;
++ cfi_write(map, CMD(words - !bytes), cmd_adr );
+
+ /* Write data */
+- for (z = 0; z < len; z += CFIDEV_BUSWIDTH) {
++ z = 0;
++ while(z < words * CFIDEV_BUSWIDTH) {
+ if (cfi_buswidth_is_1()) {
+- map->write8 (map, *((__u8*)buf)++, adr+z);
++ map_write8 (map, *((__u8*)buf)++, adr+z);
+ } else if (cfi_buswidth_is_2()) {
+- map->write16 (map, *((__u16*)buf)++, adr+z);
++ map_write16 (map, *((__u16*)buf)++, adr+z);
+ } else if (cfi_buswidth_is_4()) {
+- map->write32 (map, *((__u32*)buf)++, adr+z);
++ map_write32 (map, *((__u32*)buf)++, adr+z);
+ } else if (cfi_buswidth_is_8()) {
+- map->write64 (map, *((__u64*)buf)++, adr+z);
++ map_write64 (map, *((__u64*)buf)++, adr+z);
++ } else {
++ ret = -EINVAL;
++ goto out;
++ }
++ z += CFIDEV_BUSWIDTH;
++ }
++ if (bytes) {
++ int i = 0, n = 0;
++ u_char tmp_buf[8], *tmp_p = tmp_buf;
++
++ while (bytes--)
++ tmp_buf[i++] = buf[n++];
++ while (i < CFIDEV_BUSWIDTH)
++ tmp_buf[i++] = 0xff;
++ if (cfi_buswidth_is_2()) {
++ map_write16 (map, *((__u16*)tmp_p)++, adr+z);
++ } else if (cfi_buswidth_is_4()) {
++ map_write32 (map, *((__u32*)tmp_p)++, adr+z);
++ } else if (cfi_buswidth_is_8()) {
++ map_write64 (map, *((__u64*)tmp_p)++, adr+z);
+ } else {
+- DISABLE_VPP(map);
+ ret = -EINVAL;
+ goto out;
+ }
+@@ -1227,6 +1066,7 @@
+ for (;;) {
+ if (chip->state != FL_WRITING) {
+ /* Someone's suspended the write. Sleep */
++ DECLARE_WAITQUEUE(wait, current);
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ add_wait_queue(&chip->wq, &wait);
+ spin_unlock(chip->mutex);
+@@ -1244,7 +1084,6 @@
+ /* OK Still waiting */
+ if (time_after(jiffies, timeo)) {
+ chip->state = FL_STATUS;
+- DISABLE_VPP(map);
+ printk(KERN_ERR "waiting for chip to be ready timed out in bufwrite\n");
+ ret = -EIO;
+ goto out;
+@@ -1266,6 +1105,7 @@
+
+ /* Done and happy. */
+ chip->state = FL_STATUS;
++
+ /* check for lock bit */
+ if (status & CMD(0x02)) {
+ /* clear status */
+@@ -1273,26 +1113,10 @@
+ /* put back into read status register mode */
+ cfi_write(map, CMD(0x70), adr);
+ ret = -EROFS;
+- goto out;
+ }
+- out:
+- if (suspended) {
+- chip->state = chip->oldstate;
+- /* What if one interleaved chip has finished and the
+- other hasn't? The old code would leave the finished
+- one in READY mode. That's bad, and caused -EROFS
+- errors to be returned from do_erase_oneblock because
+- that's the only bit it checked for at the time.
+- As the state machine appears to explicitly allow
+- sending the 0x70 (Read Status) command to an erasing
+- chip and expecting it to be ignored, that's what we
+- do. */
+- cfi_write(map, CMD(0xd0), adr);
+- cfi_write(map, CMD(0x70), adr);
+- } else
+- DISABLE_VPP(map); /* must not clear the VPP if there is a suspended erase to be resumed */
+
+- wake_up(&chip->wq);
++ out:
++ put_chip(map, chip, cmd_adr);
+ spin_unlock(chip->mutex);
+ return ret;
+ }
+@@ -1336,12 +1160,12 @@
+ }
+
+ /* Write buffer is worth it only if more than one word to write... */
+- while(len > CFIDEV_BUSWIDTH) {
++ while(len) {
+ /* We must not cross write block boundaries */
+ int size = wbufsize - (ofs & (wbufsize-1));
+
+ if (size > len)
+- size = len & ~(CFIDEV_BUSWIDTH-1);
++ size = len;
+ ret = do_write_buffer(map, &cfi->chips[chipnum],
+ ofs, buf, size);
+ if (ret)
+@@ -1359,17 +1183,6 @@
+ return 0;
+ }
+ }
+-
+- /* ... and write the remaining bytes */
+- if (len > 0) {
+- size_t local_retlen;
+- ret = cfi_intelext_write_words(mtd, ofs + (chipnum << cfi->chipshift),
+- len, &local_retlen, buf);
+- if (ret)
+- return ret;
+- (*retlen) += local_retlen;
+- }
+-
+ return 0;
+ }
+
+@@ -1479,45 +1292,12 @@
+ /* Let's determine this according to the interleave only once */
+ status_OK = CMD(0x80);
+
+- timeo = jiffies + HZ;
+-retry:
++ retry:
+ spin_lock(chip->mutex);
+-
+- /* Check that the chip's ready to talk to us. */
+- switch (chip->state) {
+- case FL_CFI_QUERY:
+- case FL_JEDEC_QUERY:
+- case FL_READY:
+- cfi_write(map, CMD(0x70), adr);
+- chip->state = FL_STATUS;
+-
+- case FL_STATUS:
+- status = cfi_read(map, adr);
+- if ((status & status_OK) == status_OK)
+- break;
+-
+- /* Urgh. Chip not yet ready to talk to us. */
+- if (time_after(jiffies, timeo)) {
+- spin_unlock(chip->mutex);
+- printk(KERN_ERR "waiting for chip to be ready timed out in erase\n");
+- return -EIO;
+- }
+-
+- /* Latency issues. Drop the lock, wait a while and retry */
+- spin_unlock(chip->mutex);
+- cfi_udelay(1);
+- goto retry;
+-
+- default:
+- /* Stick ourselves on a wait queue to be woken when
+- someone changes the status */
+- set_current_state(TASK_UNINTERRUPTIBLE);
+- add_wait_queue(&chip->wq, &wait);
++ ret = get_chip(map, chip, adr, FL_ERASING);
++ if (ret) {
+ spin_unlock(chip->mutex);
+- schedule();
+- remove_wait_queue(&chip->wq, &wait);
+- timeo = jiffies + HZ;
+- goto retry;
++ return ret;
+ }
+
+ ENABLE_VPP(map);
+@@ -1528,7 +1308,7 @@
+ cfi_write(map, CMD(0x20), adr);
+ cfi_write(map, CMD(0xD0), adr);
+ chip->state = FL_ERASING;
+- chip->oldstate = 0;
++ chip->erase_suspended = 0;
+
+ spin_unlock(chip->mutex);
+ set_current_state(TASK_UNINTERRUPTIBLE);
+@@ -1550,11 +1330,11 @@
+ spin_lock(chip->mutex);
+ continue;
+ }
+- if (chip->oldstate) {
++ if (chip->erase_suspended) {
+ /* This erase was suspended and resumed.
+ Adjust the timeout */
+ timeo = jiffies + (HZ*20); /* FIXME */
+- chip->oldstate = 0;
++ chip->erase_suspended = 0;
+ }
+
+ status = cfi_read(map, adr);
+@@ -1658,39 +1438,22 @@
+ int i;
+ struct flchip *chip;
+ int ret = 0;
+- DECLARE_WAITQUEUE(wait, current);
+
+ for (i=0; !ret && i<cfi->numchips; i++) {
+ chip = &cfi->chips[i];
+
+- retry:
+ spin_lock(chip->mutex);
++ ret = get_chip(map, chip, chip->start, FL_SYNCING);
+
+- switch(chip->state) {
+- case FL_READY:
+- case FL_STATUS:
+- case FL_CFI_QUERY:
+- case FL_JEDEC_QUERY:
++ if (!ret) {
+ chip->oldstate = chip->state;
+ chip->state = FL_SYNCING;
+ /* No need to wake_up() on this state change -
+ * as the whole point is that nobody can do anything
+ * with the chip now anyway.
+ */
+- case FL_SYNCING:
+- spin_unlock(chip->mutex);
+- break;
+-
+- default:
+- /* Not an idle state */
+- add_wait_queue(&chip->wq, &wait);
+-
+- spin_unlock(chip->mutex);
+- schedule();
+- remove_wait_queue(&chip->wq, &wait);
+-
+- goto retry;
+ }
++ spin_unlock(chip->mutex);
+ }
+
+ /* Unlock the chips again */
+@@ -1731,52 +1494,18 @@
+ struct cfi_private *cfi = map->fldrv_priv;
+ cfi_word status, status_OK;
+ unsigned long timeo = jiffies + HZ;
+- DECLARE_WAITQUEUE(wait, current);
++ int ret;
+
+ adr += chip->start;
+
+ /* Let's determine this according to the interleave only once */
+ status_OK = CMD(0x80);
+
+- timeo = jiffies + HZ;
+-retry:
+ spin_lock(chip->mutex);
+-
+- /* Check that the chip's ready to talk to us. */
+- switch (chip->state) {
+- case FL_CFI_QUERY:
+- case FL_JEDEC_QUERY:
+- case FL_READY:
+- cfi_write(map, CMD(0x70), adr);
+- chip->state = FL_STATUS;
+-
+- case FL_STATUS:
+- status = cfi_read(map, adr);
+- if ((status & status_OK) == status_OK)
+- break;
+-
+- /* Urgh. Chip not yet ready to talk to us. */
+- if (time_after(jiffies, timeo)) {
+- spin_unlock(chip->mutex);
+- printk(KERN_ERR "%s: waiting for chip to be ready timed out\n", __FUNCTION__);
+- return -EIO;
+- }
+-
+- /* Latency issues. Drop the lock, wait a while and retry */
+- spin_unlock(chip->mutex);
+- cfi_udelay(1);
+- goto retry;
+-
+- default:
+- /* Stick ourselves on a wait queue to be woken when
+- someone changes the status */
+- set_current_state(TASK_UNINTERRUPTIBLE);
+- add_wait_queue(&chip->wq, &wait);
++ ret = get_chip(map, chip, adr, FL_LOCKING);
++ if (ret) {
+ spin_unlock(chip->mutex);
+- schedule();
+- remove_wait_queue(&chip->wq, &wait);
+- timeo = jiffies + HZ;
+- goto retry;
++ return ret;
+ }
+
+ ENABLE_VPP(map);
+@@ -1823,8 +1552,7 @@
+
+ /* Done and happy. */
+ chip->state = FL_STATUS;
+- DISABLE_VPP(map);
+- wake_up(&chip->wq);
++ put_chip(map, chip, adr);
+ spin_unlock(chip->mutex);
+ return 0;
+ }
+@@ -1889,22 +1617,23 @@
+
+ spin_lock(chip->mutex);
+
+- switch(chip->state) {
++ switch (chip->state) {
+ case FL_READY:
+ case FL_STATUS:
+ case FL_CFI_QUERY:
+ case FL_JEDEC_QUERY:
++ if (chip->oldstate == FL_READY) {
+ chip->oldstate = chip->state;
+ chip->state = FL_PM_SUSPENDED;
+ /* No need to wake_up() on this state change -
+ * as the whole point is that nobody can do anything
+ * with the chip now anyway.
+ */
+- case FL_PM_SUSPENDED:
++ }
+ break;
+-
+ default:
+ ret = -EAGAIN;
++ case FL_PM_SUSPENDED:
+ break;
+ }
+ spin_unlock(chip->mutex);
+diff -Nurb linux-mips-2.4.27/drivers/mtd/chips/cfi_cmdset_0002.c linux/drivers/mtd/chips/cfi_cmdset_0002.c
+--- linux-mips-2.4.27/drivers/mtd/chips/cfi_cmdset_0002.c 2003-02-26 01:53:49.000000000 +0100
++++ linux/drivers/mtd/chips/cfi_cmdset_0002.c 2004-11-19 10:25:11.745223200 +0100
+@@ -6,16 +6,22 @@
+ *
+ * 2_by_8 routines added by Simon Munton
+ *
++ * 4_by_16 work by Carolyn J. Smith
++ *
++ * Occasionally maintained by Thayne Harbaugh tharbaugh at lnxi dot com
++ *
+ * This code is GPL
+ *
+- * $Id$
++ * $Id$
+ *
+ */
+
++#include <linux/config.h>
+ #include <linux/module.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
+ #include <linux/sched.h>
++#include <linux/init.h>
+ #include <asm/io.h>
+ #include <asm/byteorder.h>
+
+@@ -23,16 +29,50 @@
+ #include <linux/slab.h>
+ #include <linux/delay.h>
+ #include <linux/interrupt.h>
++#include <linux/mtd/compatmac.h>
+ #include <linux/mtd/map.h>
++#include <linux/mtd/mtd.h>
+ #include <linux/mtd/cfi.h>
+
+ #define AMD_BOOTLOC_BUG
++#define FORCE_WORD_WRITE 0
++
++
++/*
++ * This is an attempt to coalesce the retry logic in one place - that way
++ * there aren't #ifdefs scattered throughout.
++ */
++#ifdef CONFIG_MTD_CFI_AMDSTD_RETRY
++
++#ifndef CONFIG_MTD_CFI_AMDSTD_RETRY_MAX
++#define CONFIG_MTD_CFI_AMDSTD_RETRY_MAX 0
++#endif
++
++#define RETRY_CMD_LABEL retry_cmd: do {} while (0)
++#define HANDLE_WACKY_STATE() handle_wacky_state(__func__, retry_cmd_cnt, adr, datum, prev_oldstatus, prev_status, oldstatus, status)
++static int retry_cmd_max = CONFIG_MTD_CFI_AMDSTD_RETRY_MAX;
++#define DECLARE_RETRY_CMD_CNT() int retry_cmd_cnt = 0
++#ifdef CONFIG_MTD_CFI_AMDSTD_RETRY
++#define CHECK_RETRIES() do { if (++retry_cmd_cnt <= retry_cmd_max) goto retry_cmd; } while (0)
++#endif
++
++#else
++
++#define RETRY_CMD_LABEL do {} while (0)
++#define HANDLE_WACKY_STATE() handle_wacky_state(__func__, adr, datum, prev_oldstatus, prev_status, oldstatus, status)
++#define DECLARE_RETRY_CMD_CNT()
++#define CHECK_RETRIES()
++
++#endif /* !defined(CONFIG_MTD_CFI_AMDSTD_RETRY) */
++
+
+ static int cfi_amdstd_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
+-static int cfi_amdstd_write(struct mtd_info *, loff_t, size_t, size_t *, const u_char *);
++static int cfi_amdstd_write_words(struct mtd_info *, loff_t, size_t, size_t *, const u_char *);
++static int cfi_amdstd_write_buffers(struct mtd_info *, loff_t, size_t, size_t *, const u_char *);
+ static int cfi_amdstd_erase_chip(struct mtd_info *, struct erase_info *);
+-static int cfi_amdstd_erase_onesize(struct mtd_info *, struct erase_info *);
+ static int cfi_amdstd_erase_varsize(struct mtd_info *, struct erase_info *);
++static int cfi_amdstd_lock_varsize(struct mtd_info *, loff_t, size_t);
++static int cfi_amdstd_unlock_varsize(struct mtd_info *, loff_t, size_t);
+ static void cfi_amdstd_sync (struct mtd_info *);
+ static int cfi_amdstd_suspend (struct mtd_info *);
+ static void cfi_amdstd_resume (struct mtd_info *);
+@@ -45,55 +85,136 @@
+
+
+ static struct mtd_chip_driver cfi_amdstd_chipdrv = {
+- probe: NULL, /* Not usable directly */
+- destroy: cfi_amdstd_destroy,
+- name: "cfi_cmdset_0002",
+- module: THIS_MODULE
++ .probe = NULL, /* Not usable directly */
++ .destroy = cfi_amdstd_destroy,
++ .name = "cfi_cmdset_0002",
++ .module = THIS_MODULE
+ };
+
+-struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary)
++
++/* #define DEBUG_LOCK_BITS */
++/* #define DEBUG_CFI_FEATURES */
++
++
++#ifdef DEBUG_CFI_FEATURES
++static void cfi_tell_features(struct cfi_pri_amdstd *extp)
+ {
+- struct cfi_private *cfi = map->fldrv_priv;
+- unsigned char bootloc;
+- int ofs_factor = cfi->interleave * cfi->device_type;
+- int i;
+- __u8 major, minor;
+- __u32 base = cfi->chips[0].start;
++ const char* erase_suspend[3] = {
++ "Not supported", "Read only", "Read/write"
++ };
++ const char* top_bottom[6] = {
++ "No WP", "8x8KiB sectors at top & bottom, no WP",
++ "Bottom boot", "Top boot",
++ "Uniform, Bottom WP", "Uniform, Top WP"
++ };
++
++ printk(" Silicon revision: %d\n", extp->SiliconRevision >> 1);
++ printk(" Address sensitive unlock: %s\n",
++ (extp->SiliconRevision & 1) ? "Not required" : "Required");
+
+- if (cfi->cfi_mode==CFI_MODE_CFI){
+- __u16 adr = primary?cfi->cfiq->P_ADR:cfi->cfiq->A_ADR;
++ if (extp->EraseSuspend < ARRAY_SIZE(erase_suspend))
++ printk(" Erase Suspend: %s\n", erase_suspend[extp->EraseSuspend]);
++ else
++ printk(" Erase Suspend: Unknown value %d\n", extp->EraseSuspend);
+
+- cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL);
++ if (extp->BlkProt == 0)
++ printk(" Block protection: Not supported\n");
++ else
++ printk(" Block protection: %d sectors per group\n", extp->BlkProt);
++
++
++ printk(" Temporary block unprotect: %s\n",
++ extp->TmpBlkUnprotect ? "Supported" : "Not supported");
++ printk(" Block protect/unprotect scheme: %d\n", extp->BlkProtUnprot);
++ printk(" Number of simultaneous operations: %d\n", extp->SimultaneousOps);
++ printk(" Burst mode: %s\n",
++ extp->BurstMode ? "Supported" : "Not supported");
++ if (extp->PageMode == 0)
++ printk(" Page mode: Not supported\n");
++ else
++ printk(" Page mode: %d word page\n", extp->PageMode << 2);
+
+- major = cfi_read_query(map, base + (adr+3)*ofs_factor);
+- minor = cfi_read_query(map, base + (adr+4)*ofs_factor);
++ printk(" Vpp Supply Minimum Program/Erase Voltage: %d.%d V\n",
++ extp->VppMin >> 4, extp->VppMin & 0xf);
++ printk(" Vpp Supply Maximum Program/Erase Voltage: %d.%d V\n",
++ extp->VppMax >> 4, extp->VppMax & 0xf);
+
+- printk(KERN_NOTICE " Amd/Fujitsu Extended Query Table v%c.%c at 0x%4.4X\n",
+- major, minor, adr);
+- cfi_send_gen_cmd(0xf0, 0x55, base, map, cfi, cfi->device_type, NULL);
+-
+- cfi_send_gen_cmd(0xaa, 0x555, base, map, cfi, cfi->device_type, NULL);
+- cfi_send_gen_cmd(0x55, 0x2aa, base, map, cfi, cfi->device_type, NULL);
+- cfi_send_gen_cmd(0x90, 0x555, base, map, cfi, cfi->device_type, NULL);
+- cfi->mfr = cfi_read_query(map, base);
+- cfi->id = cfi_read_query(map, base + ofs_factor);
++ if (extp->TopBottom < ARRAY_SIZE(top_bottom))
++ printk(" Top/Bottom Boot Block: %s\n", top_bottom[extp->TopBottom]);
++ else
++ printk(" Top/Bottom Boot Block: Unknown value %d\n", extp->TopBottom);
++}
++#endif
+
+- /* Wheee. Bring me the head of someone at AMD. */
+ #ifdef AMD_BOOTLOC_BUG
++/* Wheee. Bring me the head of someone at AMD. */
++static void fixup_amd_bootblock(struct map_info *map, void* param)
++{
++ struct cfi_private *cfi = map->fldrv_priv;
++ struct cfi_pri_amdstd *extp = cfi->cmdset_priv;
++ __u8 major = extp->MajorVersion;
++ __u8 minor = extp->MinorVersion;
++
+ if (((major << 8) | minor) < 0x3131) {
+ /* CFI version 1.0 => don't trust bootloc */
+ if (cfi->id & 0x80) {
+ printk(KERN_WARNING "%s: JEDEC Device ID is 0x%02X. Assuming broken CFI table.\n", map->name, cfi->id);
+- bootloc = 3; /* top boot */
++ extp->TopBottom = 3; /* top boot */
+ } else {
+- bootloc = 2; /* bottom boot */
++ extp->TopBottom = 2; /* bottom boot */
+ }
+- } else
++ }
++}
+ #endif
++
++static struct cfi_fixup fixup_table[] = {
++#ifdef AMD_BOOTLOC_BUG
+ {
+- cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL);
+- bootloc = cfi_read_query(map, base + (adr+15)*ofs_factor);
++ 0x0001, /* AMD */
++ CFI_ID_ANY,
++ fixup_amd_bootblock, NULL
++ },
++#endif
++ { 0, 0, NULL, NULL }
++};
++
++
++struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary)
++{
++ struct cfi_private *cfi = map->fldrv_priv;
++ unsigned char bootloc;
++ int i;
++
++ if (cfi->cfi_mode==CFI_MODE_CFI){
++ /*
++ * It's a real CFI chip, not one for which the probe
++ * routine faked a CFI structure. So we read the feature
++ * table from it.
++ */
++ __u16 adr = primary?cfi->cfiq->P_ADR:cfi->cfiq->A_ADR;
++ struct cfi_pri_amdstd *extp;
++
++ extp = (struct cfi_pri_amdstd*)cfi_read_pri(map, adr, sizeof(*extp), "Amd/Fujitsu");
++ if (!extp)
++ return NULL;
++
++ /* Install our own private info structure */
++ cfi->cmdset_priv = extp;
++
++ cfi_fixup(map, fixup_table);
++
++#ifdef DEBUG_CFI_FEATURES
++ /* Tell the user about it in lots of lovely detail */
++ cfi_tell_features(extp);
++#endif
++
++ bootloc = extp->TopBottom;
++ if ((bootloc != 2) && (bootloc != 3)) {
++ printk(KERN_WARNING "%s: CFI does not contain boot "
++ "bank location. Assuming top.\n", map->name);
++ bootloc = 2;
+ }
++
+ if (bootloc == 3 && cfi->cfiq->NumEraseRegions > 1) {
+ printk(KERN_WARNING "%s: Swapping erase regions for broken CFI table.\n", map->name);
+
+@@ -106,6 +227,11 @@
+ cfi->cfiq->EraseRegionInfo[j] = swap;
+ }
+ }
++ /*
++ * These might already be setup (more correctly) by
++ * jedec_probe.c - still need it for cfi_probe.c path.
++ */
++ if ( ! (cfi->addr_unlock1 && cfi->addr_unlock2) ) {
+ switch (cfi->device_type) {
+ case CFI_DEVICETYPE_X8:
+ cfi->addr_unlock1 = 0x555;
+@@ -125,9 +251,13 @@
+ cfi->addr_unlock2 = 0xaaa;
+ break;
+ default:
+- printk(KERN_NOTICE "Eep. Unknown cfi_cmdset_0002 device type %d\n", cfi->device_type);
++ printk(KERN_WARNING
++ "MTD %s(): Unsupported device type %d\n",
++ __func__, cfi->device_type);
+ return NULL;
+ }
++ }
++
+ } /* CFI mode */
+
+ for (i=0; i< cfi->numchips; i++) {
+@@ -138,15 +268,17 @@
+
+ map->fldrv = &cfi_amdstd_chipdrv;
+
+- cfi_send_gen_cmd(0xf0, 0x55, base, map, cfi, cfi->device_type, NULL);
+ return cfi_amdstd_setup(map);
+ }
+
++
+ static struct mtd_info *cfi_amdstd_setup(struct map_info *map)
+ {
+ struct cfi_private *cfi = map->fldrv_priv;
+ struct mtd_info *mtd;
+ unsigned long devsize = (1<<cfi->cfiq->DevSize) * cfi->interleave;
++ unsigned long offset = 0;
++ int i,j;
+
+ mtd = kmalloc(sizeof(*mtd), GFP_KERNEL);
+ printk(KERN_NOTICE "number of %s chips: %d\n",
+@@ -163,15 +295,9 @@
+ /* Also select the correct geometry setup too */
+ mtd->size = devsize * cfi->numchips;
+
+- if (cfi->cfiq->NumEraseRegions == 1) {
+- /* No need to muck about with multiple erase sizes */
+- mtd->erasesize = ((cfi->cfiq->EraseRegionInfo[0] >> 8) & ~0xff) * cfi->interleave;
+- } else {
+- unsigned long offset = 0;
+- int i,j;
+-
+ mtd->numeraseregions = cfi->cfiq->NumEraseRegions * cfi->numchips;
+- mtd->eraseregions = kmalloc(sizeof(struct mtd_erase_region_info) * mtd->numeraseregions, GFP_KERNEL);
++ mtd->eraseregions = kmalloc(sizeof(struct mtd_erase_region_info)
++ * mtd->numeraseregions, GFP_KERNEL);
+ if (!mtd->eraseregions) {
+ printk(KERN_WARNING "Failed to allocate memory for MTD erase region info\n");
+ goto setup_err;
+@@ -206,39 +332,52 @@
+ mtd->eraseregions[i].numblocks);
+ }
+ #endif
+- }
+
+ switch (CFIDEV_BUSWIDTH)
+ {
+ case 1:
+ case 2:
+ case 4:
+-#if 1
+- if (mtd->numeraseregions > 1)
+- mtd->erase = cfi_amdstd_erase_varsize;
+- else
++#ifdef CFI_WORD_64
++ case 8:
+ #endif
+- if (((cfi->cfiq->EraseRegionInfo[0] & 0xffff) + 1) == 1)
++ if (mtd->numeraseregions == 1
++ && ((cfi->cfiq->EraseRegionInfo[0] & 0xffff) + 1) == 1) {
+ mtd->erase = cfi_amdstd_erase_chip;
+- else
+- mtd->erase = cfi_amdstd_erase_onesize;
++ } else {
++ mtd->erase = cfi_amdstd_erase_varsize;
++ mtd->lock = cfi_amdstd_lock_varsize;
++ mtd->unlock = cfi_amdstd_unlock_varsize;
++ }
++
++ if ( cfi->cfiq->BufWriteTimeoutTyp && !FORCE_WORD_WRITE) {
++ DEBUG(MTD_DEBUG_LEVEL1, "Using buffer write method\n" );
++ mtd->write = cfi_amdstd_write_buffers;
++ } else {
++ DEBUG(MTD_DEBUG_LEVEL1, "Using word write method\n" );
++ mtd->write = cfi_amdstd_write_words;
++ }
++
+ mtd->read = cfi_amdstd_read;
+- mtd->write = cfi_amdstd_write;
+ break;
+
+ default:
+- printk(KERN_WARNING "Unsupported buswidth\n");
++ printk(KERN_WARNING "MTD %s(): Unsupported buswidth %d\n",
++ __func__, CFIDEV_BUSWIDTH);
+ goto setup_err;
+ break;
+ }
+ if (cfi->fast_prog) {
+- /* In cfi_amdstd_write() we frob the protection stuff
++ /* In cfi_amdstd_write_words() we frob the protection stuff
+ without paying any attention to the state machine.
+ This upsets in-progress erases. So we turn this flag
+ off for now till the code gets fixed. */
+ printk(KERN_NOTICE "cfi_cmdset_0002: Disabling fast programming due to code brokenness.\n");
+ cfi->fast_prog = 0;
+ }
++ /* FIXME: erase-suspend-program is broken. See
++ http://lists.infradead.org/pipermail/linux-mtd/2003-December/009001.html */
++ printk(KERN_NOTICE "cfi_cmdset_0002: Disabling erase-suspend-program due to code brokenness.\n");
+
+
+ /* does this chip have a secsi area? */
+@@ -266,7 +405,7 @@
+ mtd->flags = MTD_CAP_NORFLASH;
+ map->fldrv = &cfi_amdstd_chipdrv;
+ mtd->name = map->name;
+- MOD_INC_USE_COUNT;
++ __module_get(THIS_MODULE);
+ return mtd;
+
+ setup_err:
+@@ -280,46 +419,210 @@
+ return NULL;
+ }
+
+-static inline int do_read_onechip(struct map_info *map, struct flchip *chip, loff_t adr, size_t len, u_char *buf)
++
++/* This is more work to coalesce the retry #ifdefs in one location */
++static inline void handle_wacky_state(const char *func,
++#ifdef CONFIG_MTD_CFI_AMDSTD_RETRY
++ int retry_cmd_cnt,
++#endif
++ unsigned long adr,
++ cfi_word datum,
++ cfi_word prev_oldstatus,
++ cfi_word prev_status,
++ cfi_word oldstatus,
++ cfi_word status)
++{
++#ifdef CONFIG_MTD_CFI_AMDSTD_RETRY
++ if ( retry_cmd_cnt == retry_cmd_max ) {
++#endif
++ printk(KERN_WARNING
++ "MTD %s(): Wacky! Unable to decode failure status\n"
++ "Possible buggy device - try "
++#ifdef CONFIG_MTD_CFI_AMDSTD_RETRY
++ "increasing retry_cmd_max from %d\n"
++#else
++ "enabling CONFIG_MTD_CFI_AMDSTD_RETRY\n"
++ "in your kernel config and setting driver retry_cmd_max\n"
++#endif
++ , func
++#ifdef CONFIG_MTD_CFI_AMDSTD_RETRY
++ , retry_cmd_max
++#endif
++ );
++
++ printk(KERN_WARNING
++ "MTD %s(): 0x%.8lx(0x%.8x): 0x%.8x 0x%.8x 0x%.8x 0x%.8x\n",
++ func, adr, datum,
++ prev_oldstatus, prev_status,
++ oldstatus, status);
++#ifdef CONFIG_MTD_CFI_AMDSTD_RETRY
++ }
++#endif
++}
++
++
++static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr, int mode)
+ {
+ DECLARE_WAITQUEUE(wait, current);
+- unsigned long timeo = jiffies + HZ;
++ struct cfi_private *cfi = map->fldrv_priv;
++ cfi_word status, oldstatus;
++ cfi_word dq6 = CMD(1<<6);
++ cfi_word dq2 = CMD(1<<2);
++ unsigned long timeo;
++ struct cfi_pri_amdstd *cfip = (struct cfi_pri_amdstd *)cfi->cmdset_priv;
+
++ resettime:
++ timeo = jiffies + HZ;
+ retry:
++ switch (chip->state) {
++
++ case FL_STATUS:
++ for (;;) {
++ oldstatus = cfi_read(map, adr);
++ status = cfi_read(map, adr);
++ if (((oldstatus ^ status) & (dq6 | dq2)) == 0)
++ break;
++
++ if (time_after(jiffies, timeo)) {
++ printk(KERN_ERR "Waiting for chip to be ready timed out. Status %llx\n",
++ (long long)status);
++ cfi_spin_unlock(chip->mutex);
++ return -EIO;
++ }
++ cfi_spin_unlock(chip->mutex);
++ cfi_udelay(1);
+ cfi_spin_lock(chip->mutex);
++ /* Someone else might have been playing with it. */
++ goto retry;
++ }
+
+- if (chip->state != FL_READY){
+-#if 0
+- printk(KERN_DEBUG "Waiting for chip to read, status = %d\n", chip->state);
+-#endif
+- set_current_state(TASK_UNINTERRUPTIBLE);
+- add_wait_queue(&chip->wq, &wait);
++ case FL_READY:
++ case FL_CFI_QUERY:
++ case FL_JEDEC_QUERY:
++ return 0;
++
++ case FL_ERASING:
++ if (mode == FL_WRITING) /* FIXME: Erase-suspend-program appears broken. */
++ goto sleep;
++
++ if (!(mode == FL_READY || mode == FL_POINT
++ || (mode == FL_WRITING && (cfip->EraseSuspend & 0x2))
++ || (mode == FL_WRITING && (cfip->EraseSuspend & 0x1))))
++ goto sleep;
++
++ oldstatus = cfi_read(map, adr);
++ status = cfi_read(map, adr);
++ if ((oldstatus ^ status) & dq2) {
++ printk(KERN_ERR "Can't suspend erase -- block in progress\n");
++ goto sleep;
++ }
++
++ /* Erase suspend */
++ /* FIXME - is there a way to verify suspend? */
++ cfi_write(map, CMD(0xB0), chip->in_progress_block_addr);
++ chip->oldstate = FL_ERASING;
++ chip->state = FL_ERASE_SUSPENDING;
++ chip->erase_suspended = 1;
++ for (;;) {
++ oldstatus = cfi_read(map, chip->in_progress_block_addr);
++ status = cfi_read(map, chip->in_progress_block_addr);
++ if (((oldstatus ^ status) & dq6) == 0)
++ break;
++
++ if (time_after(jiffies, timeo)) {
++ /* Urgh. Resume and pretend we weren't here. */
++ /* FIXME - is there a way to verify resume? */
++ cfi_write(map, CMD(0x30), chip->in_progress_block_addr);
++ chip->state = FL_ERASING;
++ chip->oldstate = FL_READY;
++ printk(KERN_ERR "Chip not ready after erase "
++ "suspended: status = 0x%x\n", status);
++ return -EIO;
++ }
+
+ cfi_spin_unlock(chip->mutex);
++ cfi_udelay(1);
++ cfi_spin_lock(chip->mutex);
++ /* Nobody will touch it while it's in state FL_ERASE_SUSPENDING.
++ So we can just loop here. */
++ }
++ chip->state = FL_READY;
++ return 0;
++
++ case FL_POINT:
++ /* Only if there's no operation suspended... */
++ if (mode == FL_READY && chip->oldstate == FL_READY)
++ return 0;
+
++ default:
++ sleep:
++ set_current_state(TASK_UNINTERRUPTIBLE);
++ add_wait_queue(&chip->wq, &wait);
++ cfi_spin_unlock(chip->mutex);
+ schedule();
+ remove_wait_queue(&chip->wq, &wait);
+-#if 0
+- if(signal_pending(current))
+- return -EINTR;
+-#endif
+- timeo = jiffies + HZ;
++ cfi_spin_lock(chip->mutex);
++ goto resettime;
++ }
++}
+
+- goto retry;
++
++static void put_chip(struct map_info *map, struct flchip *chip, unsigned long adr)
++{
++ struct cfi_private *cfi = map->fldrv_priv;
++
++ switch(chip->oldstate) {
++ case FL_ERASING:
++ chip->state = chip->oldstate;
++ cfi_write(map, CMD(0x30), chip->in_progress_block_addr);
++ chip->oldstate = FL_READY;
++ chip->state = FL_ERASING;
++ break;
++
++ case FL_READY:
++ case FL_STATUS:
++ /* We should really make set_vpp() count, rather than doing this */
++ DISABLE_VPP(map);
++ break;
++ default:
++ printk(KERN_ERR "MTD: put_chip() called with oldstate %d!!\n", chip->oldstate);
+ }
++ wake_up(&chip->wq);
++}
++
++
++static inline int do_read_onechip(struct map_info *map, struct flchip *chip, loff_t adr, size_t len, u_char *buf)
++{
++ unsigned long cmd_addr;
++ struct cfi_private *cfi = map->fldrv_priv;
++ int ret;
+
+ adr += chip->start;
+
++ /* Ensure cmd read/writes are aligned. */
++ cmd_addr = adr & ~(CFIDEV_BUSWIDTH-1);
++
++ cfi_spin_lock(chip->mutex);
++ ret = get_chip(map, chip, cmd_addr, FL_READY);
++ if (ret) {
++ cfi_spin_unlock(chip->mutex);
++ return ret;
++ }
++
++ if (chip->state != FL_POINT && chip->state != FL_READY) {
++ cfi_write(map, CMD(0xf0), cmd_addr);
+ chip->state = FL_READY;
++ }
+
+- map->copy_from(map, buf, adr, len);
++ map_copy_from(map, buf, adr, len);
+
+- wake_up(&chip->wq);
+- cfi_spin_unlock(chip->mutex);
++ put_chip(map, chip, cmd_addr);
+
++ cfi_spin_unlock(chip->mutex);
+ return 0;
+ }
+
++
+ static int cfi_amdstd_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf)
+ {
+ struct map_info *map = mtd->priv;
+@@ -361,6 +664,7 @@
+ return ret;
+ }
+
++
+ static inline int do_read_secsi_onechip(struct map_info *map, struct flchip *chip, loff_t adr, size_t len, u_char *buf)
+ {
+ DECLARE_WAITQUEUE(wait, current);
+@@ -394,12 +698,14 @@
+
+ chip->state = FL_READY;
+
++ /* should these be CFI_DEVICETYPE_X8 instead of cfi->device_type? */
+ cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
+ cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL);
+ cfi_send_gen_cmd(0x88, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
+
+- map->copy_from(map, buf, adr, len);
++ map_copy_from(map, buf, adr, len);
+
++ /* should these be CFI_DEVICETYPE_X8 instead of cfi->device_type? */
+ cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
+ cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL);
+ cfi_send_gen_cmd(0x90, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
+@@ -454,125 +760,241 @@
+ return ret;
+ }
+
+-static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned long adr, __u32 datum, int fast)
++
++static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned long adr, cfi_word datum, int fast)
+ {
+- unsigned long timeo = jiffies + HZ;
+- unsigned int oldstatus, status;
+- unsigned int dq6, dq5;
+ struct cfi_private *cfi = map->fldrv_priv;
+- DECLARE_WAITQUEUE(wait, current);
++ unsigned long timeo = jiffies + HZ;
++ cfi_word oldstatus, status, prev_oldstatus, prev_status;
++ cfi_word dq6 = CMD(1<<6);
++ /*
++ * We use a 1ms + 1 jiffies generic timeout for writes (most devices
++ * have a max write time of a few hundreds usec). However, we should
++ * use the maximum timeout value given by the chip at probe time
++ * instead. Unfortunately, struct flchip does have a field for
++ * maximum timeout, only for typical which can be far too short
++ * depending of the conditions. The ' + 1' is to avoid having a
++ * timeout of 0 jiffies if HZ is smaller than 1000.
++ */
++ unsigned long uWriteTimeout = ( HZ / 1000 ) + 1;
+ int ret = 0;
++ int ta = 0;
++ DECLARE_RETRY_CMD_CNT();
+
+- retry:
+- cfi_spin_lock(chip->mutex);
+-
+- if (chip->state != FL_READY) {
+-#if 0
+- printk(KERN_DEBUG "Waiting for chip to write, status = %d\n", chip->state);
+-#endif
+- set_current_state(TASK_UNINTERRUPTIBLE);
+- add_wait_queue(&chip->wq, &wait);
++ adr += chip->start;
+
++ cfi_spin_lock(chip->mutex);
++ ret = get_chip(map, chip, adr, FL_WRITING);
++ if (ret) {
+ cfi_spin_unlock(chip->mutex);
+-
+- schedule();
+- remove_wait_queue(&chip->wq, &wait);
+-#if 0
+- printk(KERN_DEBUG "Wake up to write:\n");
+- if(signal_pending(current))
+- return -EINTR;
+-#endif
+- timeo = jiffies + HZ;
+-
+- goto retry;
++ return ret;
+ }
+
+- chip->state = FL_WRITING;
++ RETRY_CMD_LABEL;
++ DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): WRITE 0x%.8lx(0x%.8x)\n",
++ __func__, adr, datum );
++
++ /*
++ * Check for a NOP for the case when the datum to write is already
++ * present - it saves time and works around buggy chips that corrupt
++ * data at other locations when 0xff is written to a location that
++ * already contains 0xff.
++ */
++ status = cfi_read(map, adr);
++ if (status == datum) {
++ DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): NOP 0x%.8x == 0x%.8x\n",
++ __func__, status, datum );
++ goto op_done;
++ }
+
+- adr += chip->start;
+ ENABLE_VPP(map);
+ if (fast) { /* Unlock bypass */
+ cfi_send_gen_cmd(0xA0, 0, chip->start, map, cfi, cfi->device_type, NULL);
+- }
+- else {
++ } else {
++ /*
++ * The CFI_DEVICETYPE_X8 argument is needed even when
++ * cfi->device_type != CFI_DEVICETYPE_X8. The addresses for
++ * command sequences don't scale even when the device is
++ * wider. This is the case for many of the cfi_send_gen_cmd()
++ * below. I'm not sure, however, why some use
++ * cfi->device_type.
++ */
+ cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
+ cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
+ cfi_send_gen_cmd(0xA0, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
+ }
+-
+ cfi_write(map, datum, adr);
++ chip->state = FL_WRITING;
+
+ cfi_spin_unlock(chip->mutex);
+ cfi_udelay(chip->word_write_time);
+ cfi_spin_lock(chip->mutex);
+
+- /* Polling toggle bits instead of reading back many times
+- This ensures that write operation is really completed,
+- or tells us why it failed. */
+- dq6 = CMD(1<<6);
+- dq5 = CMD(1<<5);
+- timeo = jiffies + (HZ/1000); /* setting timeout to 1ms for now */
++ /*
++ * Polling toggle bits instead of reading back many times This ensures
++ * that write operation is really completed, or tells us why it
++ * failed.
++ *
++ * It may appear that the polling and decoding of error state might be
++ * simplified. Don't do it unless you really know what you are doing.
++ *
++ * You must remember that JESD21-C 3.5.3 states that the status must
++ * be read back an _additional_ two times before a failure is
++ * determined. This is because these devices have internal state
++ * machines that are asynchronous to the external data bus. During an
++ * erase or write the read-back status of the polling bits might be
++ * transitioning internaly when the external read-back occurs. This
++ * means that the bits aren't in the final state and they might appear
++ * to report an error as they are in a transient state: dq7 is
++ * asynchronous with dq6 and other status bits.
++ *
++ * This asynchronous behaviour can cause infrequent errors that will
++ * usually disappear the next time an erase or write happens (Try
++ * tracking those errors down!). To ensure that the bits are not in
++ * transition, the location must be read-back two more times and
++ * compared against what was written - BOTH reads MUST match what was
++ * written. Don't think this can be simplified to only the last read
++ * matching the datum written: status bits *can* match the datum
++ * written.
++ *
++ * If the final comparison fails, error state can *then* be decoded.
++ *
++ * - Thayne Harbaugh
++ */
++ /* See comment above for timeout value. */
++ timeo = jiffies + uWriteTimeout;
++ for (;;) {
++ if (chip->state != FL_WRITING) {
++ /* Someone's suspended the write. Sleep */
++ DECLARE_WAITQUEUE(wait, current);
++
++ set_current_state(TASK_UNINTERRUPTIBLE);
++ add_wait_queue(&chip->wq, &wait);
++ cfi_spin_unlock(chip->mutex);
++ schedule();
++ remove_wait_queue(&chip->wq, &wait);
++ timeo = jiffies + (HZ / 2); /* FIXME */
++ cfi_spin_lock(chip->mutex);
++ continue;
++ }
+
+ oldstatus = cfi_read(map, adr);
+ status = cfi_read(map, adr);
++ DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): Check 0x%.8x 0x%.8x\n",
++ __func__, oldstatus, status );
+
+- while( (status & dq6) != (oldstatus & dq6) &&
+- (status & dq5) != dq5 &&
+- !time_after(jiffies, timeo) ) {
++ /*
++ * This only checks if dq6 is still toggling and that our
++ * timer hasn't expired. We purposefully ignore the chip's
++ * internal timer that will assert dq5 and leave dq6 toggling.
++ * This is done for a variety of reasons:
++ *
++ * 1) Not all chips support dq5.
++ *
++ * 2) Dealing with asynchronous status bit and data updates
++ * and reading a device two more times creates _messy_ logic
++ * when trying to deal with interleaved devices - some may be
++ * changing while others are still busy.
++ *
++ * 3) Checking dq5 only helps to optimize an error case that
++ * should at worst be infrequent and at best non-existent.
++ *
++ * If our timeout occurs _then_ we will check dq5 to see if
++ * the device also had an internal timeout.
++ */
++ if ( (((status ^ oldstatus) & dq6) == 0)
++ || ( ta = time_after(jiffies, timeo)) )
++ break;
+
+- if (need_resched()) {
++ /* Latency issues. Drop the lock, wait a while and retry */
+ cfi_spin_unlock(chip->mutex);
+- yield();
++ cfi_udelay(1);
+ cfi_spin_lock(chip->mutex);
+- } else
+- udelay(1);
+-
+- oldstatus = cfi_read( map, adr );
+- status = cfi_read( map, adr );
+ }
+
+- if( (status & dq6) != (oldstatus & dq6) ) {
+- /* The erasing didn't stop?? */
+- if( (status & dq5) == dq5 ) {
+- /* When DQ5 raises, we must check once again
+- if DQ6 is toggling. If not, the erase has been
+- completed OK. If not, reset chip. */
++ /*
++ * Something kicked us out of the read-back loop. We'll check success
++ * befor checking failure. Even though dq6 might be true data, it is
++ * unkown if all of the other bits have changed to true data due to
++ * the asynchronous nature of the internal state machine. We will
++ * read two more times and use this to either verify that the write
++ * completed successfully or that something really went wrong. BOTH
++ * reads must match what was written - this certifies that bits aren't
++ * still changing and that the status bits erroneously match the datum
++ * that was written.
++ */
++ prev_oldstatus = oldstatus;
++ prev_status = status;
+ oldstatus = cfi_read(map, adr);
+ status = cfi_read(map, adr);
++ DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): Check 0x%.8x 0x%.8x\n",
++ __func__, oldstatus, status );
+
+- if ( (oldstatus & 0x00FF) == (status & 0x00FF) ) {
+- printk(KERN_WARNING "Warning: DQ5 raised while program operation was in progress, however operation completed OK\n" );
++ if ( oldstatus == datum && status == datum ) {
++ /* success - do nothing */
++ goto op_done;
++ }
++
++ if ( ta ) {
++ /* Only check dq5 on the chips that are still toggling. */
++ cfi_word dq5mask = ( ( status ^ oldstatus ) & dq6 ) >> 1;
++ if ( status & dq5mask ) {
++ /* dq5 asserted - decode interleave chips */
++ printk( KERN_WARNING
++ "MTD %s(): FLASH internal timeout: 0x%.8x 0x%.8x 0x%8x\n",
++ __func__,
++ status & dq5mask, status, datum );
+ } else {
+- /* DQ5 is active so we can do a reset and stop the erase */
+- cfi_write(map, CMD(0xF0), chip->start);
+- printk(KERN_WARNING "Internal flash device timeout occurred or write operation was performed while flash was programming.\n" );
++ printk( KERN_WARNING
++ "MTD %s(): Software timed out during write.\n",
++ __func__ );
++ }
++ goto op_failed;
+ }
+- } else {
+- printk(KERN_WARNING "Waiting for write to complete timed out in do_write_oneword.");
+
++ /*
++ * If we get to here then it means that something
++ * is wrong and it's not a timeout. Something
++ * is seriously wacky! Dump some debug info.
++ */
++ /*
++ * Found a clue about the chips that reach this state.
++ * Some flash chips (SST >cough<)
++ * are horribly broken. They do not ignore traffic that is
++ * destined to other devices. This happens because some solutions
++ * are on shared busses, the erase and program sequences have
++ * have multiple commands, and the sequence is interspersed with
++ * commands destined to other devices. A good flash chip will
++ * examine the command and destination address and will ignore
++ * commands that are for other devices.
++ */
++ HANDLE_WACKY_STATE();
++
++ op_failed:
++ /* reset on all failures. */
++ cfi_write( map, CMD(0xF0), chip->start );
++ /* FIXME - should have reset delay before continuing */
++ CHECK_RETRIES();
++ ret = -EIO;
++
++ op_done:
+ chip->state = FL_READY;
+- wake_up(&chip->wq);
+- cfi_spin_unlock(chip->mutex);
+- DISABLE_VPP(map);
+- ret = -EIO;
+- }
+- }
+-
+- DISABLE_VPP(map);
+- chip->state = FL_READY;
+- wake_up(&chip->wq);
++ put_chip(map, chip, adr);
+ cfi_spin_unlock(chip->mutex);
+
+ return ret;
+ }
+
+-static int cfi_amdstd_write (struct mtd_info *mtd, loff_t to , size_t len, size_t *retlen, const u_char *buf)
++
++static int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len,
++ size_t *retlen, const u_char *buf)
+ {
+ struct map_info *map = mtd->priv;
+ struct cfi_private *cfi = map->fldrv_priv;
+ int ret = 0;
+ int chipnum;
+ unsigned long ofs, chipstart;
++ DECLARE_WAITQUEUE(wait, current);
+
+ *retlen = 0;
+ if (!len)
+@@ -587,19 +1009,52 @@
+ unsigned long bus_ofs = ofs & ~(CFIDEV_BUSWIDTH-1);
+ int i = ofs - bus_ofs;
+ int n = 0;
+- u_char tmp_buf[4];
+- __u32 datum;
++ u_char tmp_buf[8];
++ cfi_word datum;
++
++ retry:
++ cfi_spin_lock(cfi->chips[chipnum].mutex);
++
++ if (cfi->chips[chipnum].state != FL_READY) {
++#if 0
++ printk(KERN_DEBUG "Waiting for chip to write, status = %d\n", cfi->chips[chipnum].state);
++#endif
++ set_current_state(TASK_UNINTERRUPTIBLE);
++ add_wait_queue(&cfi->chips[chipnum].wq, &wait);
++
++ cfi_spin_unlock(cfi->chips[chipnum].mutex);
++
++ schedule();
++ remove_wait_queue(&cfi->chips[chipnum].wq, &wait);
++#if 0
++ if(signal_pending(current))
++ return -EINTR;
++#endif
++ goto retry;
++ }
++
++ map_copy_from(map, tmp_buf, bus_ofs + cfi->chips[chipnum].start, CFIDEV_BUSWIDTH);
+
+- map->copy_from(map, tmp_buf, bus_ofs + cfi->chips[chipnum].start, CFIDEV_BUSWIDTH);
+- while (len && i < CFIDEV_BUSWIDTH)
+- tmp_buf[i++] = buf[n++], len--;
++ cfi_spin_unlock(cfi->chips[chipnum].mutex);
++
++ while (len && i < CFIDEV_BUSWIDTH) {
++ tmp_buf[i++] = buf[n++];
++ len--;
++ }
+
++ /* already know that buswidth > 1 */
+ if (cfi_buswidth_is_2()) {
+ datum = *(__u16*)tmp_buf;
+ } else if (cfi_buswidth_is_4()) {
+ datum = *(__u32*)tmp_buf;
++#ifdef CFI_WORD_64
++ } else if (cfi_buswidth_is_8()) {
++ datum = *(__u64*)tmp_buf;
++#endif
+ } else {
+- return -EINVAL; /* should never happen, but be safe */
++ printk(KERN_WARNING "MTD %s(): Unsupported buswidth %d\n",
++ __func__, CFIDEV_BUSWIDTH);
++ return -EINVAL;
+ }
+
+ ret = do_write_oneword(map, &cfi->chips[chipnum],
+@@ -628,7 +1083,7 @@
+
+ /* We are now aligned, write as much as possible */
+ while(len >= CFIDEV_BUSWIDTH) {
+- __u32 datum;
++ cfi_word datum;
+
+ if (cfi_buswidth_is_1()) {
+ datum = *(__u8*)buf;
+@@ -636,7 +1091,13 @@
+ datum = *(__u16*)buf;
+ } else if (cfi_buswidth_is_4()) {
+ datum = *(__u32*)buf;
++#ifdef CFI_WORD_64
++ } else if (cfi_buswidth_is_8()) {
++ datum = *(__u64*)buf;
++#endif
+ } else {
++ printk(KERN_WARNING "MTD %s(): Unsupported buswidth %d\n",
++ __func__, CFIDEV_BUSWIDTH);
+ return -EINVAL;
+ }
+ ret = do_write_oneword(map, &cfi->chips[chipnum],
+@@ -685,10 +1146,34 @@
+ /* Write the trailing bytes if any */
+ if (len & (CFIDEV_BUSWIDTH-1)) {
+ int i = 0, n = 0;
+- u_char tmp_buf[4];
+- __u32 datum;
++ u_char tmp_buf[8];
++ cfi_word datum;
++
++ retry1:
++ cfi_spin_lock(cfi->chips[chipnum].mutex);
++
++ if (cfi->chips[chipnum].state != FL_READY) {
++#if 0
++ printk(KERN_DEBUG "Waiting for chip to write, status = %d\n", cfi->chips[chipnum].state);
++#endif
++ set_current_state(TASK_UNINTERRUPTIBLE);
++ add_wait_queue(&cfi->chips[chipnum].wq, &wait);
++
++ cfi_spin_unlock(cfi->chips[chipnum].mutex);
++
++ schedule();
++ remove_wait_queue(&cfi->chips[chipnum].wq, &wait);
++#if 0
++ if(signal_pending(current))
++ return -EINTR;
++#endif
++ goto retry1;
++ }
++
++ map_copy_from(map, tmp_buf, ofs + cfi->chips[chipnum].start, CFIDEV_BUSWIDTH);
++
++ cfi_spin_unlock(cfi->chips[chipnum].mutex);
+
+- map->copy_from(map, tmp_buf, ofs + cfi->chips[chipnum].start, CFIDEV_BUSWIDTH);
+ while (len--)
+ tmp_buf[i++] = buf[n++];
+
+@@ -696,8 +1181,14 @@
+ datum = *(__u16*)tmp_buf;
+ } else if (cfi_buswidth_is_4()) {
+ datum = *(__u32*)tmp_buf;
++#ifdef CFI_WORD_64
++ } else if (cfi_buswidth_is_8()) {
++ datum = *(__u64*)tmp_buf;
++#endif
+ } else {
+- return -EINVAL; /* should never happen, but be safe */
++ printk(KERN_WARNING "MTD %s(): Unsupported buswidth %d\n",
++ __func__, CFIDEV_BUSWIDTH);
++ return -EINVAL;
+ }
+
+ ret = do_write_oneword(map, &cfi->chips[chipnum],
+@@ -711,289 +1202,446 @@
+ return 0;
+ }
+
+-static inline int do_erase_chip(struct map_info *map, struct flchip *chip)
++
++/*
++ * FIXME: interleaved mode not tested, and probably not supported!
++ */
++static inline int do_write_buffer(struct map_info *map, struct flchip *chip,
++ unsigned long adr, const u_char *buf, int len)
+ {
+- unsigned int oldstatus, status;
+- unsigned int dq6, dq5;
+- unsigned long timeo = jiffies + HZ;
+- unsigned int adr;
+ struct cfi_private *cfi = map->fldrv_priv;
+- DECLARE_WAITQUEUE(wait, current);
+-
+- retry:
+- cfi_spin_lock(chip->mutex);
++ unsigned long timeo = jiffies + HZ;
++ cfi_word oldstatus, status, prev_oldstatus, prev_status;
++ cfi_word dq6 = CMD(1<<6);
++ /* see comments in do_write_oneword() regarding uWriteTimeo. */
++ static unsigned long uWriteTimeout = ( HZ / 1000 ) + 1;
++ int ret = -EIO;
++ int ta = 0;
++ unsigned long cmd_adr;
++ int z, bytes, words;
++ cfi_word datum;
++ DECLARE_RETRY_CMD_CNT();
+
+- if (chip->state != FL_READY){
+- set_current_state(TASK_UNINTERRUPTIBLE);
+- add_wait_queue(&chip->wq, &wait);
++ adr += chip->start;
++ cmd_adr = adr;
+
++ cfi_spin_lock(chip->mutex);
++ ret = get_chip(map, chip, adr, FL_WRITING);
++ if (ret) {
+ cfi_spin_unlock(chip->mutex);
++ return ret;
++ }
+
+- schedule();
+- remove_wait_queue(&chip->wq, &wait);
+-#if 0
+- if(signal_pending(current))
+- return -EINTR;
++ if (cfi_buswidth_is_1()) {
++ datum = *(__u8*)buf;
++ } else if (cfi_buswidth_is_2()) {
++ datum = *(__u16*)buf;
++ } else if (cfi_buswidth_is_4()) {
++ datum = *(__u32*)buf;
++#ifdef CFI_WORD_64
++ } else if (cfi_buswidth_is_8()) {
++ datum = *(__u64*)buf;
+ #endif
+- timeo = jiffies + HZ;
+-
+- goto retry;
++ } else {
++ printk(KERN_WARNING "MTD %s(): Unsupported buswidth %d\n",
++ __func__, CFIDEV_BUSWIDTH);
++ return -EINVAL;
+ }
+
+- chip->state = FL_ERASING;
++ RETRY_CMD_LABEL;
++ DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): WRITE 0x%.8lx(0x%.8x)\n",
++ __func__, adr, datum );
+
+- /* Handle devices with one erase region, that only implement
+- * the chip erase command.
+- */
+ ENABLE_VPP(map);
+ cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
+ cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
+- cfi_send_gen_cmd(0x80, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
+- cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
+- cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
+- cfi_send_gen_cmd(0x10, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
+- timeo = jiffies + (HZ*20);
+- adr = cfi->addr_unlock1;
++ //cfi_send_gen_cmd(0xA0, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
+
+- /* Wait for the end of programing/erasure by using the toggle method.
+- * As long as there is a programming procedure going on, bit 6 of the last
+- * written byte is toggling it's state with each consectuve read.
+- * The toggling stops as soon as the procedure is completed.
+- *
+- * If the process has gone on for too long on the chip bit 5 gets.
+- * After bit5 is set you can kill the operation by sending a reset
+- * command to the chip.
+- */
+- dq6 = CMD(1<<6);
+- dq5 = CMD(1<<5);
++ /* Write Buffer Load */
++ cfi_write(map, CMD(0x25), cmd_adr);
+
+- oldstatus = cfi_read(map, adr);
+- status = cfi_read(map, adr);
+- while( ((status & dq6) != (oldstatus & dq6)) &&
+- ((status & dq5) != dq5) &&
+- !time_after(jiffies, timeo)) {
+- int wait_reps;
++ chip->state = FL_WRITING_TO_BUFFER;
++
++ /* Write length of data to come */
++ bytes = len & (CFIDEV_BUSWIDTH-1);
++ words = len / CFIDEV_BUSWIDTH;
++ cfi_write(map, CMD(words - !bytes), cmd_adr );
++ /* Write data */
++ z = 0;
++ while(z < words * CFIDEV_BUSWIDTH) {
++ if (cfi_buswidth_is_1()) {
++ datum = *((__u8*)buf);
++ map_write8 (map, *((__u8*)buf)++, adr+z);
++ } else if (cfi_buswidth_is_2()) {
++ datum = *((__u16*)buf);
++ map_write16 (map, *((__u16*)buf)++, adr+z);
++ } else if (cfi_buswidth_is_4()) {
++ datum = *((__u32*)buf);
++ map_write32 (map, *((__u32*)buf)++, adr+z);
++#ifdef CFI_WORD_64
++ } else if (cfi_buswidth_is_8()) {
++ datum = *((__u64*)buf);
++ map_write64 (map, *((__u64*)buf)++, adr+z);
++#endif
++ } else {
++ printk(KERN_WARNING "MTD %s(): Unsupported buswidth %d\n",
++ __func__, CFIDEV_BUSWIDTH);
++ ret = -EINVAL;
++ goto op_failed;
++ }
++ z += CFIDEV_BUSWIDTH;
++ }
++ if (bytes) {
++ int i = 0, n = 0;
++ u_char tmp_buf[8], *tmp_p = tmp_buf;
++
++ while (bytes--)
++ tmp_buf[i++] = buf[n++];
++ while (i < CFIDEV_BUSWIDTH)
++ tmp_buf[i++] = 0xff;
++ if (cfi_buswidth_is_2()) {
++ datum = *((__u16*)tmp_p);
++ map_write16 (map, *((__u16*)tmp_p)++, adr+z);
++ } else if (cfi_buswidth_is_4()) {
++ datum = *((__u32*)tmp_p);
++ map_write32 (map, *((__u32*)tmp_p)++, adr+z);
++#ifdef CFI_WORD_64
++ } else if (cfi_buswidth_is_8()) {
++ datum = *((__u64*)tmp_p);
++ map_write64 (map, *((__u64*)tmp_p)++, adr+z);
++#endif
++ } else {
++ printk(KERN_WARNING "MTD %s(): Unsupported buswidth %d\n",
++ __func__, CFIDEV_BUSWIDTH);
++ ret = -EINVAL;
++ goto op_failed;
++ }
++ } else if (words > 0) {
++ z -= CFIDEV_BUSWIDTH;
++ }
++
++ adr += z;
++
++ /* Write Buffer Program Confirm: GO GO GO */
++ cfi_write(map, CMD(0x29), cmd_adr);
++ chip->state = FL_WRITING;
+
+- /* an initial short sleep */
+ cfi_spin_unlock(chip->mutex);
+- schedule_timeout(HZ/100);
++ cfi_udelay(chip->buffer_write_time);
+ cfi_spin_lock(chip->mutex);
+
+- if (chip->state != FL_ERASING) {
+- /* Someone's suspended the erase. Sleep */
++ timeo = jiffies + uWriteTimeout;
++
++ for (;;) {
++ if (chip->state != FL_WRITING) {
++ /* Someone's suspended the write. Sleep */
++ DECLARE_WAITQUEUE(wait, current);
++
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ add_wait_queue(&chip->wq, &wait);
+-
+ cfi_spin_unlock(chip->mutex);
+- printk("erase suspended. Sleeping\n");
+-
+ schedule();
+ remove_wait_queue(&chip->wq, &wait);
+-#if 0
+- if (signal_pending(current))
+- return -EINTR;
+-#endif
+- timeo = jiffies + (HZ*2); /* FIXME */
++ timeo = jiffies + (HZ / 2); /* FIXME */
+ cfi_spin_lock(chip->mutex);
+ continue;
+ }
+
+- /* Busy wait for 1/10 of a milisecond */
+- for(wait_reps = 0;
+- (wait_reps < 100) &&
+- ((status & dq6) != (oldstatus & dq6)) &&
+- ((status & dq5) != dq5);
+- wait_reps++) {
++ oldstatus = cfi_read(map, adr);
++ status = cfi_read(map, adr);
++ DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): Check 0x%.8x 0x%.8x\n",
++ __func__, oldstatus, status );
++
++ /* See comments in do_write_oneword() about checking status */
++ if ( (((status ^ oldstatus) & dq6) == 0)
++ || ( ta = time_after(jiffies, timeo)) ) {
++ break;
++ }
+
+ /* Latency issues. Drop the lock, wait a while and retry */
+ cfi_spin_unlock(chip->mutex);
+-
+ cfi_udelay(1);
+-
+ cfi_spin_lock(chip->mutex);
+- oldstatus = cfi_read(map, adr);
+- status = cfi_read(map, adr);
+ }
++
++ /* See comments in do_write_oneword() about "two more checks" */
++ prev_oldstatus = oldstatus;
++ prev_status = status;
+ oldstatus = cfi_read(map, adr);
+ status = cfi_read(map, adr);
++ DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): Check 0x%.8x 0x%.8x\n",
++ __func__, oldstatus, status );
++
++ if ( oldstatus == datum && status == datum ) {
++ /* success - do nothing */
++ goto op_done;
++ }
++
++ if ( ta ) {
++ /* Only check dq5 on the chips that are still toggling. */
++ cfi_word dq5mask = ( ( status ^ oldstatus ) & dq6 ) >> 1;
++ if ( status & dq5mask ) {
++ /* dq5 asserted - decode interleave chips */
++ printk( KERN_WARNING
++ "MTD %s(): FLASH internal timeout: 0x%.8x 0x%.8x 0x%8x\n",
++ __func__,
++ status & dq5mask, status, datum );
++ } else {
++ printk( KERN_WARNING
++ "MTD %s(): Software timed out during write.\n",
++ __func__ );
+ }
+- if ((status & dq6) != (oldstatus & dq6)) {
+- /* The erasing didn't stop?? */
+- if ((status & dq5) == dq5) {
+- /* dq5 is active so we can do a reset and stop the erase */
+- cfi_write(map, CMD(0xF0), chip->start);
++ goto op_failed;
+ }
++
++ HANDLE_WACKY_STATE();
++
++ op_failed:
++ /* reset on all failures. */
++ cfi_write( map, CMD(0xF0), chip->start );
++ /* FIXME - should have reset delay before continuing */
++ CHECK_RETRIES();
++ ret = -EIO;
++
++ op_done:
+ chip->state = FL_READY;
+- wake_up(&chip->wq);
++ put_chip(map, chip, adr);
+ cfi_spin_unlock(chip->mutex);
+- printk("waiting for erase to complete timed out.");
+- DISABLE_VPP(map);
+- return -EIO;
++
++ return ret;
++}
++
++
++static int cfi_amdstd_write_buffers(struct mtd_info *mtd, loff_t to, size_t len,
++ size_t *retlen, const u_char *buf)
++{
++ struct map_info *map = mtd->priv;
++ struct cfi_private *cfi = map->fldrv_priv;
++ int wbufsize = CFIDEV_INTERLEAVE << cfi->cfiq->MaxBufWriteSize;
++ int ret = 0;
++ int chipnum;
++ unsigned long ofs;
++
++ *retlen = 0;
++ if (!len)
++ return 0;
++
++ chipnum = to >> cfi->chipshift;
++ ofs = to - (chipnum << cfi->chipshift);
++
++ /* If it's not bus-aligned, do the first word write */
++ if (ofs & (CFIDEV_BUSWIDTH-1)) {
++ size_t local_len = (-ofs)&(CFIDEV_BUSWIDTH-1);
++ if (local_len > len)
++ local_len = len;
++ ret = cfi_amdstd_write_words(mtd, to, local_len,
++ retlen, buf);
++ if (ret)
++ return ret;
++ ofs += local_len;
++ buf += local_len;
++ len -= local_len;
++
++ if (ofs >> cfi->chipshift) {
++ chipnum ++;
++ ofs = 0;
++ if (chipnum == cfi->numchips)
++ return 0;
++ }
++ }
++
++ /* Write buffer is worth it only if more than one word to write... */
++ while (len) {
++ /* We must not cross write block boundaries */
++ int size = wbufsize - (ofs & (wbufsize-1));
++
++ if (size > len)
++ size = len;
++ ret = do_write_buffer(map, &cfi->chips[chipnum],
++ ofs, buf, size);
++ if (ret)
++ return ret;
++
++ ofs += size;
++ buf += size;
++ (*retlen) += size;
++ len -= size;
++
++ if (ofs >> cfi->chipshift) {
++ chipnum ++;
++ ofs = 0;
++ if (chipnum == cfi->numchips)
++ return 0;
++ }
+ }
+- DISABLE_VPP(map);
+- chip->state = FL_READY;
+- wake_up(&chip->wq);
+- cfi_spin_unlock(chip->mutex);
+
+ return 0;
+ }
+
+-static inline int do_erase_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr)
++
++/*
++ * Handle devices with one erase region, that only implement
++ * the chip erase command.
++ */
++static inline int do_erase_chip(struct map_info *map, struct flchip *chip)
+ {
+- unsigned int oldstatus, status;
+- unsigned int dq6, dq5;
+- unsigned long timeo = jiffies + HZ;
+ struct cfi_private *cfi = map->fldrv_priv;
++ cfi_word oldstatus, status, prev_oldstatus, prev_status;
++ cfi_word dq6 = CMD(1<<6);
++ unsigned long timeo = jiffies + HZ;
++ unsigned long int adr;
+ DECLARE_WAITQUEUE(wait, current);
++ int ret = 0;
++ int ta = 0;
++ cfi_word datum = 0;
++ DECLARE_RETRY_CMD_CNT();
+
+- retry:
+- cfi_spin_lock(chip->mutex);
+-
+- if (chip->state != FL_READY){
+- set_current_state(TASK_UNINTERRUPTIBLE);
+- add_wait_queue(&chip->wq, &wait);
++ adr = cfi->addr_unlock1;
+
++ cfi_spin_lock(chip->mutex);
++ ret = get_chip(map, chip, adr, FL_WRITING);
++ if (ret) {
+ cfi_spin_unlock(chip->mutex);
+-
+- schedule();
+- remove_wait_queue(&chip->wq, &wait);
+-#if 0
+- if(signal_pending(current))
+- return -EINTR;
+-#endif
+- timeo = jiffies + HZ;
+-
+- goto retry;
++ return ret;
+ }
+
+- chip->state = FL_ERASING;
++ RETRY_CMD_LABEL;
++ DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): ERASE 0x%.8lx\n",
++ __func__, chip->start );
+
+- adr += chip->start;
+ ENABLE_VPP(map);
+ cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
+ cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
+ cfi_send_gen_cmd(0x80, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
+ cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
+ cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
+- cfi_write(map, CMD(0x30), adr);
+-
+- timeo = jiffies + (HZ*20);
+-
+- /* Wait for the end of programing/erasure by using the toggle method.
+- * As long as there is a programming procedure going on, bit 6 of the last
+- * written byte is toggling it's state with each consectuve read.
+- * The toggling stops as soon as the procedure is completed.
+- *
+- * If the process has gone on for too long on the chip bit 5 gets.
+- * After bit5 is set you can kill the operation by sending a reset
+- * command to the chip.
+- */
+- dq6 = CMD(1<<6);
+- dq5 = CMD(1<<5);
++ cfi_send_gen_cmd(0x10, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
+
+- oldstatus = cfi_read(map, adr);
+- status = cfi_read(map, adr);
+- while( ((status & dq6) != (oldstatus & dq6)) &&
+- ((status & dq5) != dq5) &&
+- !time_after(jiffies, timeo)) {
+- int wait_reps;
++ chip->state = FL_ERASING;
++ chip->erase_suspended = 0;
++ chip->in_progress_block_addr = adr;
+
+- /* an initial short sleep */
+ cfi_spin_unlock(chip->mutex);
+- schedule_timeout(HZ/100);
++ set_current_state(TASK_UNINTERRUPTIBLE);
++ schedule_timeout((chip->erase_time*HZ)/(2*1000));
+ cfi_spin_lock(chip->mutex);
+
++ timeo = jiffies + (HZ*20);
++
++ for (;;) {
+ if (chip->state != FL_ERASING) {
+ /* Someone's suspended the erase. Sleep */
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ add_wait_queue(&chip->wq, &wait);
+-
+ cfi_spin_unlock(chip->mutex);
+- printk(KERN_DEBUG "erase suspended. Sleeping\n");
+-
+ schedule();
+ remove_wait_queue(&chip->wq, &wait);
+-#if 0
+- if (signal_pending(current))
+- return -EINTR;
+-#endif
+- timeo = jiffies + (HZ*2); /* FIXME */
+ cfi_spin_lock(chip->mutex);
+ continue;
+ }
++ if (chip->erase_suspended) {
++ /* This erase was suspended and resumed.
++ Adjust the timeout */
++ timeo = jiffies + (HZ*20); /* FIXME */
++ chip->erase_suspended = 0;
++ }
+
+- /* Busy wait for 1/10 of a milisecond */
+- for(wait_reps = 0;
+- (wait_reps < 100) &&
+- ((status & dq6) != (oldstatus & dq6)) &&
+- ((status & dq5) != dq5);
+- wait_reps++) {
++ oldstatus = cfi_read(map, adr);
++ status = cfi_read(map, adr);
++ DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): Check 0x%.8x 0x%.8x\n",
++ __func__, oldstatus, status );
++ if ( (((status ^ oldstatus) & dq6) == 0)
++ || ( ta = time_after(jiffies, timeo)) )
++ break;
+
+ /* Latency issues. Drop the lock, wait a while and retry */
+ cfi_spin_unlock(chip->mutex);
+-
+- cfi_udelay(1);
+-
++ set_current_state(TASK_UNINTERRUPTIBLE);
++ schedule_timeout(1);
+ cfi_spin_lock(chip->mutex);
+- oldstatus = cfi_read(map, adr);
+- status = cfi_read(map, adr);
+ }
++
++ prev_oldstatus = oldstatus;
++ prev_status = status;
+ oldstatus = cfi_read(map, adr);
+ status = cfi_read(map, adr);
+- }
+- if( (status & dq6) != (oldstatus & dq6) )
+- {
+- /* The erasing didn't stop?? */
+- if( ( status & dq5 ) == dq5 )
+- {
+- /* When DQ5 raises, we must check once again if DQ6 is toggling.
+- If not, the erase has been completed OK. If not, reset chip. */
+- oldstatus = cfi_read( map, adr );
+- status = cfi_read( map, adr );
++ DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): Check 0x%.8x 0x%.8x\n",
++ __func__, oldstatus, status );
+
+- if( ( oldstatus & 0x00FF ) == ( status & 0x00FF ) )
+- {
+- printk( "Warning: DQ5 raised while erase operation was in progress, but erase completed OK\n" );
+- }
+- else
+- {
+- /* DQ5 is active so we can do a reset and stop the erase */
+- cfi_write(map, CMD(0xF0), chip->start);
+- printk( KERN_WARNING "Internal flash device timeout occured or write operation was performed while flash was erasing\n" );
++ if ( cfi_buswidth_is_1() ) {
++ datum = (__u8)~0;
++ } else if ( cfi_buswidth_is_2() ) {
++ datum = (__u16)~0;
++ } else if ( cfi_buswidth_is_4() ) {
++ datum = (__u32)~0;
++#ifdef CFI_WORD_64
++ } else if ( cfi_buswidth_is_8() ) {
++ datum = (__u64)~0;
++#endif
++ } else {
++ printk(KERN_WARNING "MTD %s(): Unsupported buswidth %d\n",
++ __func__, CFIDEV_BUSWIDTH);
++ goto op_failed;
++ }
++
++ if ( oldstatus == datum && status == datum ) {
++ /* success - do nothing */
++ goto op_done;
++ }
++
++ if ( ta ) {
++ /* Only check dq5 on the chips that are still toggling. */
++ cfi_word dq5mask = ( ( status ^ oldstatus ) & dq6 ) >> 1;
++ if ( status & dq5mask ) {
++ /* dq5 asserted - decode interleave chips */
++ printk( KERN_WARNING
++ "MTD %s(): FLASH internal timeout: 0x%.8x\n",
++ __func__,
++ status & dq5mask );
++ } else {
++ printk( KERN_WARNING
++ "MTD %s(): Software timed out during write.\n",
++ __func__ );
+ }
++ goto op_failed;
+ }
+- else
+- {
+- printk( "Waiting for erase to complete timed out in do_erase_oneblock.");
+
+- chip->state = FL_READY;
+- wake_up(&chip->wq);
+- cfi_spin_unlock(chip->mutex);
+- DISABLE_VPP(map);
+- return -EIO;
+- }
+- }
++ HANDLE_WACKY_STATE();
+
+- DISABLE_VPP(map);
++ op_failed:
++ /* reset on all failures. */
++ cfi_write( map, CMD(0xF0), chip->start );
++ /* FIXME - should have reset delay before continuing */
++ CHECK_RETRIES();
++ ret = -EIO;
++
++ op_done:
+ chip->state = FL_READY;
+- wake_up(&chip->wq);
++ put_chip(map, chip, adr);
+ cfi_spin_unlock(chip->mutex);
+- return 0;
++
++ return ret;
+ }
+
+-static int cfi_amdstd_erase_varsize(struct mtd_info *mtd, struct erase_info *instr)
++
++typedef int (*frob_t)(struct map_info *map, struct flchip *chip,
++ unsigned long adr, void *thunk);
++
++
++static int cfi_amdstd_varsize_frob(struct mtd_info *mtd, frob_t frob,
++ loff_t ofs, size_t len, void *thunk)
+ {
+ struct map_info *map = mtd->priv;
+ struct cfi_private *cfi = map->fldrv_priv;
+- unsigned long adr, len;
++ unsigned long adr;
+ int chipnum, ret = 0;
+ int i, first;
+ struct mtd_erase_region_info *regions = mtd->eraseregions;
+
+- if (instr->addr > mtd->size)
++ if (ofs > mtd->size)
+ return -EINVAL;
+
+- if ((instr->len + instr->addr) > mtd->size)
++ if ((len + ofs) > mtd->size)
+ return -EINVAL;
+
+ /* Check that both start and end of the requested erase are
+@@ -1008,7 +1656,7 @@
+ start of the requested erase, and then go back one.
+ */
+
+- while (i < mtd->numeraseregions && instr->addr >= regions[i].offset)
++ while (i < mtd->numeraseregions && ofs >= regions[i].offset)
+ i++;
+ i--;
+
+@@ -1018,7 +1666,7 @@
+ effect here.
+ */
+
+- if (instr->addr & (regions[i].erasesize-1))
++ if (ofs & (regions[i].erasesize-1))
+ return -EINVAL;
+
+ /* Remember the erase region we start on */
+@@ -1028,7 +1676,7 @@
+ * with the erase region at that address.
+ */
+
+- while (i<mtd->numeraseregions && (instr->addr + instr->len) >= regions[i].offset)
++ while (i<mtd->numeraseregions && (ofs + len) >= regions[i].offset)
+ i++;
+
+ /* As before, drop back one to point at the region in which
+@@ -1036,17 +1684,16 @@
+ */
+ i--;
+
+- if ((instr->addr + instr->len) & (regions[i].erasesize-1))
++ if ((ofs + len) & (regions[i].erasesize-1))
+ return -EINVAL;
+
+- chipnum = instr->addr >> cfi->chipshift;
+- adr = instr->addr - (chipnum << cfi->chipshift);
+- len = instr->len;
++ chipnum = ofs >> cfi->chipshift;
++ adr = ofs - (chipnum << cfi->chipshift);
+
+ i=first;
+
+- while(len) {
+- ret = do_erase_oneblock(map, &cfi->chips[chipnum], adr);
++ while (len) {
++ ret = (*frob)(map, &cfi->chips[chipnum], adr, thunk);
+
+ if (ret)
+ return ret;
+@@ -1066,50 +1713,171 @@
+ }
+ }
+
+- instr->state = MTD_ERASE_DONE;
+- if (instr->callback)
+- instr->callback(instr);
+-
+ return 0;
+ }
+
+-static int cfi_amdstd_erase_onesize(struct mtd_info *mtd, struct erase_info *instr)
++
++static inline int do_erase_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr, void *thunk)
+ {
+- struct map_info *map = mtd->priv;
+ struct cfi_private *cfi = map->fldrv_priv;
+- unsigned long adr, len;
+- int chipnum, ret = 0;
++ cfi_word oldstatus, status, prev_oldstatus, prev_status;
++ cfi_word dq6 = CMD(1<<6);
++ unsigned long timeo = jiffies + HZ;
++ DECLARE_WAITQUEUE(wait, current);
++ int ret = 0;
++ int ta = 0;
++ cfi_word datum = 0;
++ DECLARE_RETRY_CMD_CNT();
+
+- if (instr->addr & (mtd->erasesize - 1))
+- return -EINVAL;
++ adr += chip->start;
+
+- if (instr->len & (mtd->erasesize -1))
+- return -EINVAL;
++ cfi_spin_lock(chip->mutex);
++ ret = get_chip(map, chip, adr, FL_ERASING);
++ if (ret) {
++ cfi_spin_unlock(chip->mutex);
++ return ret;
++ }
+
+- if ((instr->len + instr->addr) > mtd->size)
+- return -EINVAL;
++ RETRY_CMD_LABEL;
++ DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): ERASE 0x%.8lx\n",
++ __func__, adr );
+
+- chipnum = instr->addr >> cfi->chipshift;
+- adr = instr->addr - (chipnum << cfi->chipshift);
+- len = instr->len;
++ ENABLE_VPP(map);
++ cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
++ cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
++ cfi_send_gen_cmd(0x80, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
++ cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
++ cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
++ cfi_write(map, CMD(0x30), adr);
+
+- while(len) {
+- ret = do_erase_oneblock(map, &cfi->chips[chipnum], adr);
++ chip->state = FL_ERASING;
++ chip->erase_suspended = 0;
++ chip->in_progress_block_addr = adr;
+
+- if (ret)
+- return ret;
++ cfi_spin_unlock(chip->mutex);
++ set_current_state(TASK_UNINTERRUPTIBLE);
++ schedule_timeout((chip->erase_time*HZ)/(2*1000));
++ cfi_spin_lock(chip->mutex);
+
+- adr += mtd->erasesize;
+- len -= mtd->erasesize;
++ timeo = jiffies + (HZ*20);
+
+- if (adr >> cfi->chipshift) {
+- adr = 0;
+- chipnum++;
++ /* Wait for the end of programing/erasure by using the toggle method.
++ * As long as there is a programming procedure going on, bit 6 is
++ * toggling its state with each consecutive read. The toggling stops
++ * as soon as the procedure is completed.
++ *
++ * If the process has gone on for too long on the chip, bit 5 gets
++ * set. After bit5 is set you can kill the operation by sending a
++ * reset command to the chip.
++ */
++ /* See comments in do_write_oneword(). */
+
+- if (chipnum >= cfi->numchips)
++ for (;;) {
++ if (chip->state != FL_ERASING) {
++ /* Someone's suspended the erase. Sleep */
++ set_current_state(TASK_UNINTERRUPTIBLE);
++ add_wait_queue(&chip->wq, &wait);
++ cfi_spin_unlock(chip->mutex);
++ schedule();
++ remove_wait_queue(&chip->wq, &wait);
++ cfi_spin_lock(chip->mutex);
++ continue;
++ }
++ if (chip->erase_suspended) {
++ /* This erase was suspended and resumed.
++ Adjust the timeout */
++ timeo = jiffies + (HZ*20); /* FIXME */
++ chip->erase_suspended = 0;
++ }
++
++ oldstatus = cfi_read(map, adr);
++ status = cfi_read(map, adr);
++ DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): Check 0x%.8x 0x%.8x\n",
++ __func__, oldstatus, status );
++ if ( (((status ^ oldstatus) & dq6) == 0)
++ || ( ta = time_after(jiffies, timeo)) )
+ break;
++
++ /* Latency issues. Drop the lock, wait a while and retry */
++ cfi_spin_unlock(chip->mutex);
++ set_current_state(TASK_UNINTERRUPTIBLE);
++ schedule_timeout(1);
++ cfi_spin_lock(chip->mutex);
+ }
++
++ prev_oldstatus = oldstatus;
++ prev_status = status;
++ oldstatus = cfi_read(map, adr);
++ status = cfi_read(map, adr);
++ DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): Check 0x%.8x 0x%.8x\n",
++ __func__, oldstatus, status );
++
++ if ( cfi_buswidth_is_1() ) {
++ datum = (__u8)~0;
++ } else if ( cfi_buswidth_is_2() ) {
++ datum = (__u16)~0;
++ } else if ( cfi_buswidth_is_4() ) {
++ datum = (__u32)~0;
++#ifdef CFI_WORD_64
++ } else if ( cfi_buswidth_is_8() ) {
++ datum = (__u64)~0;
++#endif
++ } else {
++ printk(KERN_WARNING "MTD %s(): Unsupported buswidth %d\n",
++ __func__, CFIDEV_BUSWIDTH);
++ goto op_failed;
++ }
++
++ if ( oldstatus == datum && status == datum ) {
++ /* success - do nothing */
++ goto op_done;
++ }
++
++ if ( ta ) {
++ /* Only check dq5 on the chips that are still toggling. */
++ cfi_word dq5mask = ( ( status ^ oldstatus ) & dq6 ) >> 1;
++ if ( status & dq5mask ) {
++ /* dq5 asserted - decode interleave chips */
++ printk( KERN_WARNING
++ "MTD %s(): FLASH internal timeout: 0x%.8x\n",
++ __func__,
++ status & dq5mask );
++ } else {
++ printk( KERN_WARNING
++ "MTD %s(): Software timed out during write.\n",
++ __func__ );
+ }
++ goto op_failed;
++ }
++
++ HANDLE_WACKY_STATE();
++
++ op_failed:
++ /* reset on all failures. */
++ cfi_write( map, CMD(0xF0), chip->start );
++ /* FIXME - should have reset delay before continuing */
++ CHECK_RETRIES();
++ ret = -EIO;
++
++ op_done:
++ chip->state = FL_READY;
++ put_chip(map, chip, adr);
++ cfi_spin_unlock(chip->mutex);
++ return ret;
++}
++
++
++int cfi_amdstd_erase_varsize(struct mtd_info *mtd, struct erase_info *instr)
++{
++ unsigned long ofs, len;
++ int ret;
++
++ ofs = instr->addr;
++ len = instr->len;
++
++ ret = cfi_amdstd_varsize_frob(mtd, do_erase_oneblock, ofs, len, 0);
++ if (ret)
++ return ret;
+
+ instr->state = MTD_ERASE_DONE;
+ if (instr->callback)
+@@ -1118,6 +1886,7 @@
+ return 0;
+ }
+
++
+ static int cfi_amdstd_erase_chip(struct mtd_info *mtd, struct erase_info *instr)
+ {
+ struct map_info *map = mtd->priv;
+@@ -1141,6 +1910,7 @@
+ return 0;
+ }
+
++
+ static void cfi_amdstd_sync (struct mtd_info *mtd)
+ {
+ struct map_info *map = mtd->priv;
+@@ -1254,6 +2024,7 @@
+ return ret;
+ }
+
++
+ static void cfi_amdstd_resume(struct mtd_info *mtd)
+ {
+ struct map_info *map = mtd->priv;
+@@ -1279,6 +2050,137 @@
+ }
+ }
+
++
++#ifdef DEBUG_LOCK_BITS
++
++static int do_printlockstatus_oneblock(struct map_info *map,
++ struct flchip *chip,
++ unsigned long adr,
++ void *thunk)
++{
++ struct cfi_private *cfi = map->fldrv_priv;
++ int ofs_factor = cfi->interleave * cfi->device_type;
++
++ cfi_send_gen_cmd(0x90, 0x55, 0, map, cfi, cfi->device_type, NULL);
++ printk(KERN_DEBUG "block status register for 0x%08lx is %x\n",
++ adr, cfi_read_query(map, adr+(2*ofs_factor)));
++ cfi_send_gen_cmd(0xff, 0x55, 0, map, cfi, cfi->device_type, NULL);
++
++ return 0;
++}
++
++
++#define debug_dump_locks(mtd, frob, ofs, len, thunk) \
++ cfi_amdstd_varsize_frob((mtd), (frob), (ofs), (len), (thunk))
++
++#else
++
++#define debug_dump_locks(...)
++
++#endif /* DEBUG_LOCK_BITS */
++
++
++struct xxlock_thunk {
++ cfi_word val;
++ flstate_t state;
++};
++
++
++#define DO_XXLOCK_ONEBLOCK_LOCK ((struct xxlock_thunk){0x01, FL_LOCKING})
++#define DO_XXLOCK_ONEBLOCK_UNLOCK ((struct xxlock_thunk){0x00, FL_UNLOCKING})
++
++
++/*
++ * FIXME - this is *very* specific to a particular chip. It likely won't
++ * work for all chips that require unlock. It also hasn't been tested
++ * with interleaved chips.
++ */
++static int do_xxlock_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr, void *thunk)
++{
++ struct cfi_private *cfi = map->fldrv_priv;
++ struct xxlock_thunk *xxlt = (struct xxlock_thunk *)thunk;
++ int ret;
++
++ /*
++ * This is easy because these are writes to registers and not writes
++ * to flash memory - that means that we don't have to check status
++ * and timeout.
++ */
++
++ adr += chip->start;
++ /*
++ * lock block registers:
++ * - on 64k boundariesand
++ * - bit 1 set high
++ * - block lock registers are 4MiB lower - overflow subtract (danger)
++ */
++ adr = ((adr & ~0xffff) | 0x2) + ~0x3fffff;
++
++ cfi_spin_lock(chip->mutex);
++ ret = get_chip(map, chip, adr, FL_LOCKING);
++ if (ret) {
++ cfi_spin_unlock(chip->mutex);
++ return ret;
++ }
++
++ chip->state = xxlt->state;
++ cfi_write(map, CMD(xxlt->val), adr);
++
++ /* Done and happy. */
++ chip->state = FL_READY;
++ put_chip(map, chip, adr);
++ cfi_spin_unlock(chip->mutex);
++ return 0;
++}
++
++
++static int cfi_amdstd_lock_varsize(struct mtd_info *mtd,
++ loff_t ofs,
++ size_t len)
++{
++ int ret;
++
++ DEBUG(MTD_DEBUG_LEVEL3,
++ "%s: lock status before, ofs=0x%08llx, len=0x%08X\n",
++ __func__, ofs, len);
++ debug_dump_locks(mtd, do_printlockstatus_oneblock, ofs, len, 0);
++
++ ret = cfi_amdstd_varsize_frob(mtd, do_xxlock_oneblock, ofs, len,
++ (void *)&DO_XXLOCK_ONEBLOCK_LOCK);
++
++ DEBUG(MTD_DEBUG_LEVEL3,
++ "%s: lock status after, ret=%d\n",
++ __func__, ret);
++
++ debug_dump_locks(mtd, do_printlockstatus_oneblock, ofs, len, 0);
++
++ return ret;
++}
++
++
++static int cfi_amdstd_unlock_varsize(struct mtd_info *mtd,
++ loff_t ofs,
++ size_t len)
++{
++ int ret;
++
++ DEBUG(MTD_DEBUG_LEVEL3,
++ "%s: lock status before, ofs=0x%08llx, len=0x%08X\n",
++ __func__, ofs, len);
++ debug_dump_locks(mtd, do_printlockstatus_oneblock, ofs, len, 0);
++
++ ret = cfi_amdstd_varsize_frob(mtd, do_xxlock_oneblock, ofs, len,
++ (void *)&DO_XXLOCK_ONEBLOCK_UNLOCK);
++
++ DEBUG(MTD_DEBUG_LEVEL3,
++ "%s: lock status after, ret=%d\n",
++ __func__, ret);
++ debug_dump_locks(mtd, do_printlockstatus_oneblock, ofs, len, 0);
++
++ return ret;
++}
++
++
+ static void cfi_amdstd_destroy(struct mtd_info *mtd)
+ {
+ struct map_info *map = mtd->priv;
+@@ -1291,17 +2193,20 @@
+
+ static char im_name[]="cfi_cmdset_0002";
+
++
+ int __init cfi_amdstd_init(void)
+ {
+ inter_module_register(im_name, THIS_MODULE, &cfi_cmdset_0002);
+ return 0;
+ }
+
++
+ static void __exit cfi_amdstd_exit(void)
+ {
+ inter_module_unregister(im_name);
+ }
+
++
+ module_init(cfi_amdstd_init);
+ module_exit(cfi_amdstd_exit);
+
+@@ -1309,3 +2214,7 @@
+ MODULE_AUTHOR("Crossnet Co. <info@crossnet.co.jp> et al.");
+ MODULE_DESCRIPTION("MTD chip driver for AMD/Fujitsu flash chips");
+
++#ifdef CONFIG_MTD_CFI_AMDSTD_RETRY
++MODULE_PARM(retry_cmd_max, "i");
++MODULE_PARM_DESC(retry_cmd_max, "Number of times to retry an erase or program command if it fails - should only be needed by buggy hardware: default 0");
++#endif
+diff -Nurb linux-mips-2.4.27/drivers/mtd/chips/cfi_cmdset_0020.c linux/drivers/mtd/chips/cfi_cmdset_0020.c
+--- linux-mips-2.4.27/drivers/mtd/chips/cfi_cmdset_0020.c 2003-02-26 01:53:49.000000000 +0100
++++ linux/drivers/mtd/chips/cfi_cmdset_0020.c 2004-11-19 10:25:11.747222896 +0100
+@@ -21,16 +21,19 @@
+ #include <linux/types.h>
+ #include <linux/kernel.h>
+ #include <linux/sched.h>
++#include <linux/init.h>
+ #include <asm/io.h>
+ #include <asm/byteorder.h>
+
+ #include <linux/errno.h>
++#include <linux/init.h>
+ #include <linux/slab.h>
+ #include <linux/delay.h>
+ #include <linux/interrupt.h>
++#include <linux/mtd/compatmac.h>
+ #include <linux/mtd/map.h>
+ #include <linux/mtd/cfi.h>
+-#include <linux/mtd/compatmac.h>
++#include <linux/mtd/mtd.h>
+
+
+ static int cfi_staa_read(struct mtd_info *, loff_t, size_t, size_t *, u_char *);
+@@ -51,10 +54,10 @@
+ static struct mtd_info *cfi_staa_setup (struct map_info *);
+
+ static struct mtd_chip_driver cfi_staa_chipdrv = {
+- probe: NULL, /* Not usable directly */
+- destroy: cfi_staa_destroy,
+- name: "cfi_cmdset_0020",
+- module: THIS_MODULE
++ .probe = NULL, /* Not usable directly */
++ .destroy = cfi_staa_destroy,
++ .name = "cfi_cmdset_0020",
++ .module = THIS_MODULE
+ };
+
+ /* #define DEBUG_LOCK_BITS */
+@@ -113,7 +116,6 @@
+ {
+ struct cfi_private *cfi = map->fldrv_priv;
+ int i;
+- __u32 base = cfi->chips[0].start;
+
+ if (cfi->cfi_mode) {
+ /*
+@@ -123,35 +125,10 @@
+ */
+ __u16 adr = primary?cfi->cfiq->P_ADR:cfi->cfiq->A_ADR;
+ struct cfi_pri_intelext *extp;
+- int ofs_factor = cfi->interleave * cfi->device_type;
+-
+- printk(" ST Microelectronics Extended Query Table at 0x%4.4X\n", adr);
+- if (!adr)
+- return NULL;
+-
+- /* Switch it into Query Mode */
+- cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL);
+-
+- extp = kmalloc(sizeof(*extp), GFP_KERNEL);
+- if (!extp) {
+- printk(KERN_ERR "Failed to allocate memory\n");
+- return NULL;
+- }
+-
+- /* Read in the Extended Query Table */
+- for (i=0; i<sizeof(*extp); i++) {
+- ((unsigned char *)extp)[i] =
+- cfi_read_query(map, (base+((adr+i)*ofs_factor)));
+- }
+
+- if (extp->MajorVersion != '1' ||
+- (extp->MinorVersion < '0' || extp->MinorVersion > '2')) {
+- printk(KERN_WARNING " Unknown staa Extended Query "
+- "version %c.%c.\n", extp->MajorVersion,
+- extp->MinorVersion);
+- kfree(extp);
++ extp = (struct cfi_pri_intelext*)cfi_read_pri(map, adr, sizeof(*extp), "ST Microelectronics");
++ if (!extp)
+ return NULL;
+- }
+
+ /* Do some byteswapping if necessary */
+ extp->FeatureSupport = cfi32_to_cpu(extp->FeatureSupport);
+@@ -172,11 +149,6 @@
+ cfi->chips[i].erase_time = 1024;
+ }
+
+- map->fldrv = &cfi_staa_chipdrv;
+- MOD_INC_USE_COUNT;
+-
+- /* Make sure it's in read mode */
+- cfi_send_gen_cmd(0xff, 0x55, base, map, cfi, cfi->device_type, NULL);
+ return cfi_staa_setup(map);
+ }
+
+@@ -208,6 +180,7 @@
+ if (!mtd->eraseregions) {
+ printk(KERN_ERR "Failed to allocate memory for MTD erase region info\n");
+ kfree(cfi->cmdset_priv);
++ kfree(mtd);
+ return NULL;
+ }
+
+@@ -232,6 +205,7 @@
+ printk(KERN_WARNING "Sum of regions (%lx) != total size of set of interleaved chips (%lx)\n", offset, devsize);
+ kfree(mtd->eraseregions);
+ kfree(cfi->cmdset_priv);
++ kfree(mtd);
+ return NULL;
+ }
+
+@@ -256,7 +230,7 @@
+ mtd->flags |= MTD_ECC; /* FIXME: Not all STMicro flashes have this */
+ mtd->eccsize = 8; /* FIXME: Should be 0 for STMicro flashes w/out ECC */
+ map->fldrv = &cfi_staa_chipdrv;
+- MOD_INC_USE_COUNT;
++ __module_get(THIS_MODULE);
+ mtd->name = map->name;
+ return mtd;
+ }
+@@ -288,7 +262,7 @@
+ */
+ switch (chip->state) {
+ case FL_ERASING:
+- if (!((struct cfi_pri_intelext *)cfi->cmdset_priv)->FeatureSupport & 2)
++ if (!(((struct cfi_pri_intelext *)cfi->cmdset_priv)->FeatureSupport & 2))
+ goto sleep; /* We don't support erase suspend */
+
+ cfi_write (map, CMD(0xb0), cmd_addr);
+@@ -374,7 +348,7 @@
+ goto retry;
+ }
+
+- map->copy_from(map, buf, adr, len);
++ map_copy_from(map, buf, adr, len);
+
+ if (suspended) {
+ chip->state = chip->oldstate;
+@@ -540,11 +514,11 @@
+ /* Write data */
+ for (z = 0; z < len; z += CFIDEV_BUSWIDTH) {
+ if (cfi_buswidth_is_1()) {
+- map->write8 (map, *((__u8*)buf)++, adr+z);
++ map_write8 (map, *((__u8*)buf)++, adr+z);
+ } else if (cfi_buswidth_is_2()) {
+- map->write16 (map, *((__u16*)buf)++, adr+z);
++ map_write16 (map, *((__u16*)buf)++, adr+z);
+ } else if (cfi_buswidth_is_4()) {
+- map->write32 (map, *((__u32*)buf)++, adr+z);
++ map_write32 (map, *((__u32*)buf)++, adr+z);
+ } else {
+ DISABLE_VPP(map);
+ return -EINVAL;
+@@ -1436,13 +1410,13 @@
+
+ static char im_name[]="cfi_cmdset_0020";
+
+-mod_init_t cfi_staa_init(void)
++int __init cfi_staa_init(void)
+ {
+ inter_module_register(im_name, THIS_MODULE, &cfi_cmdset_0020);
+ return 0;
+ }
+
+-mod_exit_t cfi_staa_exit(void)
++static void __exit cfi_staa_exit(void)
+ {
+ inter_module_unregister(im_name);
+ }
+diff -Nurb linux-mips-2.4.27/drivers/mtd/chips/cfi_probe.c linux/drivers/mtd/chips/cfi_probe.c
+--- linux-mips-2.4.27/drivers/mtd/chips/cfi_probe.c 2003-02-26 01:53:49.000000000 +0100
++++ linux/drivers/mtd/chips/cfi_probe.c 2004-11-19 10:25:11.766220008 +0100
+@@ -1,13 +1,14 @@
+ /*
+ Common Flash Interface probe code.
+ (C) 2000 Red Hat. GPL'd.
+- $Id$
++ $Id$
+ */
+
+ #include <linux/config.h>
+ #include <linux/module.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
+ #include <asm/io.h>
+ #include <asm/byteorder.h>
+ #include <linux/errno.h>
+@@ -25,7 +26,7 @@
+ #endif
+
+ static int cfi_probe_chip(struct map_info *map, __u32 base,
+- struct flchip *chips, struct cfi_private *cfi);
++ unsigned long *chip_map, struct cfi_private *cfi);
+ static int cfi_chip_setup(struct map_info *map, struct cfi_private *cfi);
+
+ struct mtd_info *cfi_probe(struct map_info *map);
+@@ -48,7 +49,7 @@
+ }
+
+ static int cfi_probe_chip(struct map_info *map, __u32 base,
+- struct flchip *chips, struct cfi_private *cfi)
++ unsigned long *chip_map, struct cfi_private *cfi)
+ {
+ int i;
+
+@@ -77,18 +78,24 @@
+ }
+
+ /* Check each previous chip to see if it's an alias */
+- for (i=0; i<cfi->numchips; i++) {
++ for (i=0; i < (base >> cfi->chipshift); i++) {
++ unsigned long start;
++ if(!test_bit(i, chip_map)) {
++ /* Skip location; no valid chip at this address */
++ continue;
++ }
++ start = i << cfi->chipshift;
+ /* This chip should be in read mode if it's one
+ we've already touched. */
+- if (qry_present(map,chips[i].start,cfi)) {
++ if (qry_present(map, start, cfi)) {
+ /* Eep. This chip also had the QRY marker.
+ * Is it an alias for the new one? */
+- cfi_send_gen_cmd(0xF0, 0, chips[i].start, map, cfi, cfi->device_type, NULL);
++ cfi_send_gen_cmd(0xF0, 0, start, map, cfi, cfi->device_type, NULL);
+
+ /* If the QRY marker goes away, it's an alias */
+- if (!qry_present(map, chips[i].start, cfi)) {
++ if (!qry_present(map, start, cfi)) {
+ printk(KERN_DEBUG "%s: Found an alias at 0x%x for the chip at 0x%lx\n",
+- map->name, base, chips[i].start);
++ map->name, base, start);
+ return 0;
+ }
+ /* Yes, it's actually got QRY for data. Most
+@@ -99,7 +106,7 @@
+
+ if (qry_present(map, base, cfi)) {
+ printk(KERN_DEBUG "%s: Found an alias at 0x%x for the chip at 0x%lx\n",
+- map->name, base, chips[i].start);
++ map->name, base, start);
+ return 0;
+ }
+ }
+@@ -107,13 +114,7 @@
+
+ /* OK, if we got to here, then none of the previous chips appear to
+ be aliases for the current one. */
+- if (cfi->numchips == MAX_CFI_CHIPS) {
+- printk(KERN_WARNING"%s: Too many flash chips detected. Increase MAX_CFI_CHIPS from %d.\n", map->name, MAX_CFI_CHIPS);
+- /* Doesn't matter about resetting it to Read Mode - we're not going to talk to it anyway */
+- return -1;
+- }
+- chips[cfi->numchips].start = base;
+- chips[cfi->numchips].state = FL_READY;
++ set_bit((base >> cfi->chipshift), chip_map); /* Update chip map */
+ cfi->numchips++;
+
+ /* Put it back into Read Mode */
+@@ -179,9 +180,28 @@
+ (cfi->cfiq->EraseRegionInfo[i] & 0xffff) + 1);
+ #endif
+ }
++
++ /* Note we put the device back into Read Mode BEFORE going into Auto
++ * Select Mode, as some devices support nesting of modes, others
++ * don't. This way should always work.
++ * On cmdset 0001 the writes of 0xaa and 0x55 are not needed, and
++ * so should be treated as nops or illegal (and so put the device
++ * back into Read Mode, which is a nop in this case).
++ */
++ cfi_send_gen_cmd(0xf0, 0, base, map, cfi, cfi->device_type, NULL);
++ cfi_send_gen_cmd(0xaa, 0x555, base, map, cfi, cfi->device_type, NULL);
++ cfi_send_gen_cmd(0x55, 0x2aa, base, map, cfi, cfi->device_type, NULL);
++ cfi_send_gen_cmd(0x90, 0x555, base, map, cfi, cfi->device_type, NULL);
++ cfi->mfr = cfi_read_query(map, base);
++ cfi->id = cfi_read_query(map, base + ofs_factor);
++
+ /* Put it back into Read Mode */
+ cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL);
+
++ printk(KERN_INFO "%s: Found %d x%d devices at 0x%x in %d-bit mode\n",
++ map->name, cfi->interleave, cfi->device_type*8, base,
++ map->buswidth*8);
++
+ return 1;
+ }
+
+@@ -240,11 +260,11 @@
+ printk("No Alternate Algorithm Table\n");
+
+
+- printk("Vcc Minimum: %x.%x V\n", cfip->VccMin >> 4, cfip->VccMin & 0xf);
+- printk("Vcc Maximum: %x.%x V\n", cfip->VccMax >> 4, cfip->VccMax & 0xf);
++ printk("Vcc Minimum: %2d.%d V\n", cfip->VccMin >> 4, cfip->VccMin & 0xf);
++ printk("Vcc Maximum: %2d.%d V\n", cfip->VccMax >> 4, cfip->VccMax & 0xf);
+ if (cfip->VppMin) {
+- printk("Vpp Minimum: %x.%x V\n", cfip->VppMin >> 4, cfip->VppMin & 0xf);
+- printk("Vpp Maximum: %x.%x V\n", cfip->VppMax >> 4, cfip->VppMax & 0xf);
++ printk("Vpp Minimum: %2d.%d V\n", cfip->VppMin >> 4, cfip->VppMin & 0xf);
++ printk("Vpp Maximum: %2d.%d V\n", cfip->VppMax >> 4, cfip->VppMax & 0xf);
+ }
+ else
+ printk("No Vpp line\n");
+@@ -303,8 +323,8 @@
+ #endif /* DEBUG_CFI */
+
+ static struct chip_probe cfi_chip_probe = {
+- name: "CFI",
+- probe_chip: cfi_probe_chip
++ .name = "CFI",
++ .probe_chip = cfi_probe_chip
+ };
+
+ struct mtd_info *cfi_probe(struct map_info *map)
+@@ -317,9 +337,9 @@
+ }
+
+ static struct mtd_chip_driver cfi_chipdrv = {
+- probe: cfi_probe,
+- name: "cfi_probe",
+- module: THIS_MODULE
++ .probe = cfi_probe,
++ .name = "cfi_probe",
++ .module = THIS_MODULE
+ };
+
+ int __init cfi_probe_init(void)
+diff -Nurb linux-mips-2.4.27/drivers/mtd/chips/cfi_util.c linux/drivers/mtd/chips/cfi_util.c
+--- linux-mips-2.4.27/drivers/mtd/chips/cfi_util.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/mtd/chips/cfi_util.c 2004-11-19 10:25:11.767219856 +0100
+@@ -0,0 +1,91 @@
++/*
++ * Common Flash Interface support:
++ * Generic utility functions not dependant on command set
++ *
++ * Copyright (C) 2002 Red Hat
++ * Copyright (C) 2003 STMicroelectronics Limited
++ *
++ * This code is covered by the GPL.
++ *
++ * $Id$
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/kernel.h>
++#include <linux/sched.h>
++#include <asm/io.h>
++#include <asm/byteorder.h>
++
++#include <linux/errno.h>
++#include <linux/slab.h>
++#include <linux/delay.h>
++#include <linux/interrupt.h>
++#include <linux/mtd/map.h>
++#include <linux/mtd/cfi.h>
++#include <linux/mtd/compatmac.h>
++
++struct cfi_extquery *
++cfi_read_pri(struct map_info *map, __u16 adr, __u16 size, const char* name)
++{
++ struct cfi_private *cfi = map->fldrv_priv;
++ __u32 base = 0; // cfi->chips[0].start;
++ int ofs_factor = cfi->interleave * cfi->device_type;
++ int i;
++ struct cfi_extquery *extp = NULL;
++
++ printk(" %s Extended Query Table at 0x%4.4X\n", name, adr);
++ if (!adr)
++ goto out;
++
++ /* Switch it into Query Mode */
++ cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL);
++
++ extp = kmalloc(size, GFP_KERNEL);
++ if (!extp) {
++ printk(KERN_ERR "Failed to allocate memory\n");
++ goto out;
++ }
++
++ /* Read in the Extended Query Table */
++ for (i=0; i<size; i++) {
++ ((unsigned char *)extp)[i] =
++ cfi_read_query(map, base+((adr+i)*ofs_factor));
++ }
++
++ if (extp->MajorVersion != '1' ||
++ (extp->MinorVersion < '0' || extp->MinorVersion > '3')) {
++ printk(KERN_WARNING " Unknown %s Extended Query "
++ "version %c.%c.\n", name, extp->MajorVersion,
++ extp->MinorVersion);
++ kfree(extp);
++ extp = NULL;
++ goto out;
++ }
++
++out:
++ /* Make sure it's in read mode */
++ cfi_send_gen_cmd(0xf0, 0, base, map, cfi, cfi->device_type, NULL);
++
++ return extp;
++}
++
++EXPORT_SYMBOL(cfi_read_pri);
++
++void cfi_fixup(struct map_info *map, struct cfi_fixup* fixups)
++{
++ struct cfi_private *cfi = map->fldrv_priv;
++ struct cfi_fixup *f;
++
++ for (f=fixups; f->fixup; f++) {
++ if (((f->mfr == CFI_MFR_ANY) || (f->mfr == cfi->mfr)) &&
++ ((f->id == CFI_ID_ANY) || (f->id == cfi->id))) {
++ f->fixup(map, f->param);
++ }
++ }
++}
++
++EXPORT_SYMBOL(cfi_fixup);
++
++MODULE_LICENSE("GPL");
+diff -Nurb linux-mips-2.4.27/drivers/mtd/chips/chipreg.c linux/drivers/mtd/chips/chipreg.c
+--- linux-mips-2.4.27/drivers/mtd/chips/chipreg.c 2003-02-26 01:53:49.000000000 +0100
++++ linux/drivers/mtd/chips/chipreg.c 2004-11-19 10:25:11.769219552 +0100
+@@ -1,5 +1,5 @@
+ /*
+- * $Id$
++ * $Id$
+ *
+ * Registration for chip drivers
+ *
+@@ -7,10 +7,13 @@
+
+ #include <linux/kernel.h>
+ #include <linux/config.h>
++#include <linux/module.h>
+ #include <linux/kmod.h>
+ #include <linux/spinlock.h>
+-#include <linux/mtd/compatmac.h>
++#include <linux/slab.h>
+ #include <linux/mtd/map.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/compatmac.h>
+
+ spinlock_t chip_drvs_lock = SPIN_LOCK_UNLOCKED;
+ static LIST_HEAD(chip_drvs_list);
+@@ -44,10 +47,8 @@
+ break;
+ }
+ }
+- if (ret && !try_inc_mod_count(ret->module)) {
+- /* Eep. Failed. */
++ if (ret && !try_module_get(ret->module))
+ ret = NULL;
+- }
+
+ spin_unlock(&chip_drvs_lock);
+
+@@ -64,32 +65,46 @@
+
+ drv = get_mtd_chip_driver(name);
+
+- if (!drv && !request_module(name))
++ if (!drv && !request_module("%s", name))
+ drv = get_mtd_chip_driver(name);
+
+ if (!drv)
+ return NULL;
+
+ ret = drv->probe(map);
+-#ifdef CONFIG_MODULES
++
+ /* We decrease the use count here. It may have been a
+ probe-only module, which is no longer required from this
+ point, having given us a handle on (and increased the use
+ count of) the actual driver code.
+ */
+- if(drv->module)
+- __MOD_DEC_USE_COUNT(drv->module);
+-#endif
++ module_put(drv->module);
+
+ if (ret)
+ return ret;
+
+ return NULL;
+ }
++/*
++ * Destroy an MTD device which was created for a map device.
++ * Make sure the MTD device is already unregistered before calling this
++ */
++void map_destroy(struct mtd_info *mtd)
++{
++ struct map_info *map = mtd->priv;
++
++ if (map->fldrv->destroy)
++ map->fldrv->destroy(mtd);
++
++ module_put(map->fldrv->module);
++
++ kfree(mtd);
++}
+
+ EXPORT_SYMBOL(register_mtd_chip_driver);
+ EXPORT_SYMBOL(unregister_mtd_chip_driver);
+ EXPORT_SYMBOL(do_map_probe);
++EXPORT_SYMBOL(map_destroy);
+
+ MODULE_LICENSE("GPL");
+ MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
+diff -Nurb linux-mips-2.4.27/drivers/mtd/chips/gen_probe.c linux/drivers/mtd/chips/gen_probe.c
+--- linux-mips-2.4.27/drivers/mtd/chips/gen_probe.c 2003-08-13 19:19:18.000000000 +0200
++++ linux/drivers/mtd/chips/gen_probe.c 2004-11-19 10:25:11.770219400 +0100
+@@ -1,14 +1,17 @@
+ /*
+ * Routines common to all CFI-type probes.
+- * (C) 2001, 2001 Red Hat, Inc.
++ * (C) 2001-2003 Red Hat, Inc.
+ * GPL'd
+- * $Id$
++ * $Id$
+ */
+
+ #include <linux/kernel.h>
++#include <linux/slab.h>
++#include <linux/module.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+ #include <linux/mtd/cfi.h>
++#include <linux/mtd/mtd.h>
+ #include <linux/mtd/gen_probe.h>
+
+ static struct mtd_info *check_cmd_set(struct map_info *, int);
+@@ -50,11 +53,11 @@
+
+ struct cfi_private *genprobe_ident_chips(struct map_info *map, struct chip_probe *cp)
+ {
+- unsigned long base=0;
+ struct cfi_private cfi;
+ struct cfi_private *retcfi;
+- struct flchip chip[MAX_CFI_CHIPS];
+- int i;
++ unsigned long *chip_map;
++ int i, j;
++ int max_chips;
+
+ memset(&cfi, 0, sizeof(cfi));
+
+@@ -77,8 +80,6 @@
+ return NULL;
+ }
+ #endif
+- chip[0].start = 0;
+- chip[0].state = FL_READY;
+ cfi.chipshift = cfi.cfiq->DevSize;
+
+ switch(cfi.interleave) {
+@@ -103,20 +104,28 @@
+ cfi.numchips = 1;
+
+ /*
++ * Allocate memory for bitmap of valid chips.
++ * Align bitmap storage size to full byte.
++ */
++ max_chips = map->size >> cfi.chipshift;
++ chip_map = kmalloc((max_chips / 8) + ((max_chips % 8) ? 1 : 0), GFP_KERNEL);
++ if (!chip_map) {
++ printk(KERN_WARNING "%s: kmalloc failed for CFI chip map\n", map->name);
++ kfree(cfi.cfiq);
++ return NULL;
++ }
++
++ set_bit(0, chip_map); /* Mark first chip valid */
++
++ /*
+ * Now probe for other chips, checking sensibly for aliases while
+ * we're at it. The new_chip probe above should have let the first
+ * chip in read mode.
+- *
+- * NOTE: Here, we're checking if there is room for another chip
+- * the same size within the mapping. Therefore,
+- * base + chipsize <= map->size is the correct thing to do,
+- * because, base + chipsize would be the _first_ byte of the
+- * next chip, not the one we're currently pondering.
+ */
+
+- for (base = (1<<cfi.chipshift); base + (1<<cfi.chipshift) <= map->size;
+- base += (1<<cfi.chipshift))
+- cp->probe_chip(map, base, &chip[0], &cfi);
++ for (i = 1; i < max_chips; i++) {
++ cp->probe_chip(map, i << cfi.chipshift, chip_map, &cfi);
++ }
+
+ /*
+ * Now allocate the space for the structures we need to return to
+@@ -128,19 +137,26 @@
+ if (!retcfi) {
+ printk(KERN_WARNING "%s: kmalloc failed for CFI private structure\n", map->name);
+ kfree(cfi.cfiq);
++ kfree(chip_map);
+ return NULL;
+ }
+
+ memcpy(retcfi, &cfi, sizeof(cfi));
+- memcpy(&retcfi->chips[0], chip, sizeof(struct flchip) * cfi.numchips);
++ memset(&retcfi->chips[0], 0, sizeof(struct flchip) * cfi.numchips);
+
+- /* Fix up the stuff that breaks when you move it */
+- for (i=0; i< retcfi->numchips; i++) {
+- init_waitqueue_head(&retcfi->chips[i].wq);
+- spin_lock_init(&retcfi->chips[i]._spinlock);
+- retcfi->chips[i].mutex = &retcfi->chips[i]._spinlock;
++ for (i = 0, j = 0; (j < cfi.numchips) && (i < max_chips); i++) {
++ if(test_bit(i, chip_map)) {
++ struct flchip *pchip = &retcfi->chips[j++];
++
++ pchip->start = (i << cfi.chipshift);
++ pchip->state = FL_READY;
++ init_waitqueue_head(&pchip->wq);
++ spin_lock_init(&pchip->_spinlock);
++ pchip->mutex = &pchip->_spinlock;
++ }
+ }
+
++ kfree(chip_map);
+ return retcfi;
+ }
+
+diff -Nurb linux-mips-2.4.27/drivers/mtd/chips/jedec.c linux/drivers/mtd/chips/jedec.c
+--- linux-mips-2.4.27/drivers/mtd/chips/jedec.c 2003-02-26 01:53:49.000000000 +0100
++++ linux/drivers/mtd/chips/jedec.c 2004-11-19 10:25:11.772219096 +0100
+@@ -11,10 +11,16 @@
+ * not going to guess how to send commands to them, plus I expect they will
+ * all speak CFI..
+ *
+- * $Id$
++ * $Id$
+ */
+
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/kernel.h>
+ #include <linux/mtd/jedec.h>
++#include <linux/mtd/map.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/compatmac.h>
+
+ static struct mtd_info *jedec_probe(struct map_info *);
+ static int jedec_probe8(struct map_info *map,unsigned long base,
+@@ -33,14 +39,51 @@
+
+ /* Listing of parts and sizes. We need this table to learn the sector
+ size of the chip and the total length */
+-static const struct JEDECTable JEDEC_table[] =
+- {{0x013D,"AMD Am29F017D",2*1024*1024,64*1024,MTD_CAP_NORFLASH},
+- {0x01AD,"AMD Am29F016",2*1024*1024,64*1024,MTD_CAP_NORFLASH},
+- {0x01D5,"AMD Am29F080",1*1024*1024,64*1024,MTD_CAP_NORFLASH},
+- {0x01A4,"AMD Am29F040",512*1024,64*1024,MTD_CAP_NORFLASH},
+- {0x20E3,"AMD Am29W040B",512*1024,64*1024,MTD_CAP_NORFLASH},
+- {0xC2AD,"Macronix MX29F016",2*1024*1024,64*1024,MTD_CAP_NORFLASH},
+- {}};
++static const struct JEDECTable JEDEC_table[] = {
++ {
++ .jedec = 0x013D,
++ .name = "AMD Am29F017D",
++ .size = 2*1024*1024,
++ .sectorsize = 64*1024,
++ .capabilities = MTD_CAP_NORFLASH
++ },
++ {
++ .jedec = 0x01AD,
++ .name = "AMD Am29F016",
++ .size = 2*1024*1024,
++ .sectorsize = 64*1024,
++ .capabilities = MTD_CAP_NORFLASH
++ },
++ {
++ .jedec = 0x01D5,
++ .name = "AMD Am29F080",
++ .size = 1*1024*1024,
++ .sectorsize = 64*1024,
++ .capabilities = MTD_CAP_NORFLASH
++ },
++ {
++ .jedec = 0x01A4,
++ .name = "AMD Am29F040",
++ .size = 512*1024,
++ .sectorsize = 64*1024,
++ .capabilities = MTD_CAP_NORFLASH
++ },
++ {
++ .jedec = 0x20E3,
++ .name = "AMD Am29W040B",
++ .size = 512*1024,
++ .sectorsize = 64*1024,
++ .capabilities = MTD_CAP_NORFLASH
++ },
++ {
++ .jedec = 0xC2AD,
++ .name = "Macronix MX29F016",
++ .size = 2*1024*1024,
++ .sectorsize = 64*1024,
++ .capabilities = MTD_CAP_NORFLASH
++ },
++ { .jedec = 0x0 }
++};
+
+ static const struct JEDECTable *jedec_idtoinf(__u8 mfr,__u8 id);
+ static void jedec_sync(struct mtd_info *mtd) {};
+@@ -54,9 +97,9 @@
+
+
+ static struct mtd_chip_driver jedec_chipdrv = {
+- probe: jedec_probe,
+- name: "jedec",
+- module: THIS_MODULE
++ .probe = jedec_probe,
++ .name = "jedec",
++ .module = THIS_MODULE
+ };
+
+ /* Probe entry point */
+@@ -131,8 +174,7 @@
+ /* Generate a part name that includes the number of different chips and
+ other configuration information */
+ count = 1;
+- strncpy(Part,map->name,sizeof(Part)-10);
+- Part[sizeof(Part)-11] = 0;
++ strlcpy(Part,map->name,sizeof(Part)-10);
+ strcat(Part," ");
+ Uniq = 0;
+ for (I = 0; priv->chips[I].jedec != 0 && I < MAX_JEDEC_CHIPS; I++)
+@@ -209,8 +251,7 @@
+ // printk("Part: '%s'\n",Part);
+
+ memset(MTD,0,sizeof(*MTD));
+- // strncpy(MTD->name,Part,sizeof(MTD->name));
+- // MTD->name[sizeof(MTD->name)-1] = 0;
++ // strlcpy(MTD->name,Part,sizeof(MTD->name));
+ MTD->name = map->name;
+ MTD->type = MTD_NORFLASH;
+ MTD->flags = MTD_CAP_NORFLASH;
+@@ -229,7 +270,7 @@
+ MTD->priv = map;
+ map->fldrv_priv = priv;
+ map->fldrv = &jedec_chipdrv;
+- MOD_INC_USE_COUNT;
++ __module_get(THIS_MODULE);
+ return MTD;
+ }
+
+@@ -351,8 +392,8 @@
+ static int jedec_probe8(struct map_info *map,unsigned long base,
+ struct jedec_private *priv)
+ {
+- #define flread(x) map->read8(map,base+x)
+- #define flwrite(v,x) map->write8(map,v,base+x)
++ #define flread(x) map_read8(map,base+x)
++ #define flwrite(v,x) map_write8(map,v,base+x)
+
+ const unsigned long AutoSel1 = 0xAA;
+ const unsigned long AutoSel2 = 0x55;
+@@ -411,8 +452,8 @@
+ static int jedec_probe32(struct map_info *map,unsigned long base,
+ struct jedec_private *priv)
+ {
+- #define flread(x) map->read32(map,base+((x)<<2))
+- #define flwrite(v,x) map->write32(map,v,base+((x)<<2))
++ #define flread(x) map_read32(map,base+((x)<<2))
++ #define flwrite(v,x) map_write32(map,v,base+((x)<<2))
+
+ const unsigned long AutoSel1 = 0xAAAAAAAA;
+ const unsigned long AutoSel2 = 0x55555555;
+@@ -490,7 +531,7 @@
+ {
+ struct map_info *map = (struct map_info *)mtd->priv;
+
+- map->copy_from(map, buf, from, len);
++ map_copy_from(map, buf, from, len);
+ *retlen = len;
+ return 0;
+ }
+@@ -514,7 +555,7 @@
+ get = priv->bank_fill[0] - offset;
+
+ bank /= priv->bank_fill[0];
+- map->copy_from(map,buf + *retlen,bank*my_bank_size + offset,get);
++ map_copy_from(map,buf + *retlen,bank*my_bank_size + offset,get);
+
+ len -= get;
+ *retlen += get;
+@@ -545,8 +586,8 @@
+ static int flash_erase(struct mtd_info *mtd, struct erase_info *instr)
+ {
+ // Does IO to the currently selected chip
+- #define flread(x) map->read8(map,chip->base+((x)<<chip->addrshift))
+- #define flwrite(v,x) map->write8(map,v,chip->base+((x)<<chip->addrshift))
++ #define flread(x) map_read8(map,chip->base+((x)<<chip->addrshift))
++ #define flwrite(v,x) map_write8(map,v,chip->base+((x)<<chip->addrshift))
+
+ unsigned long Time = 0;
+ unsigned long NoTime = 0;
+@@ -608,7 +649,7 @@
+
+ /* Poll the flash for erasure completion, specs say this can take as long
+ as 480 seconds to do all the sectors (for a 2 meg flash).
+- Erasure time is dependant on chip age, temp and wear.. */
++ Erasure time is dependent on chip age, temp and wear.. */
+
+ /* This being a generic routine assumes a 32 bit bus. It does read32s
+ and bundles interleved chips into the same grouping. This will work
+@@ -651,19 +692,19 @@
+ or this is not really flash ;> */
+ switch (map->buswidth) {
+ case 1:
+- Last[0] = map->read8(map,(chip->base >> chip->addrshift) + chip->start + off);
+- Last[1] = map->read8(map,(chip->base >> chip->addrshift) + chip->start + off);
+- Last[2] = map->read8(map,(chip->base >> chip->addrshift) + chip->start + off);
++ Last[0] = map_read8(map,(chip->base >> chip->addrshift) + chip->start + off);
++ Last[1] = map_read8(map,(chip->base >> chip->addrshift) + chip->start + off);
++ Last[2] = map_read8(map,(chip->base >> chip->addrshift) + chip->start + off);
+ break;
+ case 2:
+- Last[0] = map->read16(map,(chip->base >> chip->addrshift) + chip->start + off);
+- Last[1] = map->read16(map,(chip->base >> chip->addrshift) + chip->start + off);
+- Last[2] = map->read16(map,(chip->base >> chip->addrshift) + chip->start + off);
++ Last[0] = map_read16(map,(chip->base >> chip->addrshift) + chip->start + off);
++ Last[1] = map_read16(map,(chip->base >> chip->addrshift) + chip->start + off);
++ Last[2] = map_read16(map,(chip->base >> chip->addrshift) + chip->start + off);
+ break;
+ case 3:
+- Last[0] = map->read32(map,(chip->base >> chip->addrshift) + chip->start + off);
+- Last[1] = map->read32(map,(chip->base >> chip->addrshift) + chip->start + off);
+- Last[2] = map->read32(map,(chip->base >> chip->addrshift) + chip->start + off);
++ Last[0] = map_read32(map,(chip->base >> chip->addrshift) + chip->start + off);
++ Last[1] = map_read32(map,(chip->base >> chip->addrshift) + chip->start + off);
++ Last[2] = map_read32(map,(chip->base >> chip->addrshift) + chip->start + off);
+ break;
+ }
+ Count = 3;
+@@ -699,13 +740,13 @@
+
+ switch (map->buswidth) {
+ case 1:
+- Last[Count % 4] = map->read8(map,(chip->base >> chip->addrshift) + chip->start + off);
++ Last[Count % 4] = map_read8(map,(chip->base >> chip->addrshift) + chip->start + off);
+ break;
+ case 2:
+- Last[Count % 4] = map->read16(map,(chip->base >> chip->addrshift) + chip->start + off);
++ Last[Count % 4] = map_read16(map,(chip->base >> chip->addrshift) + chip->start + off);
+ break;
+ case 4:
+- Last[Count % 4] = map->read32(map,(chip->base >> chip->addrshift) + chip->start + off);
++ Last[Count % 4] = map_read32(map,(chip->base >> chip->addrshift) + chip->start + off);
+ break;
+ }
+ Count++;
+@@ -755,10 +796,10 @@
+ size_t *retlen, const u_char *buf)
+ {
+ /* Does IO to the currently selected chip. It takes the bank addressing
+- base (which is divisable by the chip size) adds the necesary lower bits
+- of addrshift (interleve index) and then adds the control register index. */
+- #define flread(x) map->read8(map,base+(off&((1<<chip->addrshift)-1))+((x)<<chip->addrshift))
+- #define flwrite(v,x) map->write8(map,v,base+(off&((1<<chip->addrshift)-1))+((x)<<chip->addrshift))
++ base (which is divisible by the chip size) adds the necessary lower bits
++ of addrshift (interleave index) and then adds the control register index. */
++ #define flread(x) map_read8(map,base+(off&((1<<chip->addrshift)-1))+((x)<<chip->addrshift))
++ #define flwrite(v,x) map_write8(map,v,base+(off&((1<<chip->addrshift)-1))+((x)<<chip->addrshift))
+
+ struct map_info *map = (struct map_info *)mtd->priv;
+ struct jedec_private *priv = (struct jedec_private *)map->fldrv_priv;
+@@ -794,7 +835,7 @@
+ // Loop over this page
+ for (; off != (chip->size << chip->addrshift) && len != 0; start++, len--, off++,buf++)
+ {
+- unsigned char oldbyte = map->read8(map,base+off);
++ unsigned char oldbyte = map_read8(map,base+off);
+ unsigned char Last[4];
+ unsigned long Count = 0;
+
+@@ -809,10 +850,10 @@
+ flwrite(0xAA,0x555);
+ flwrite(0x55,0x2AA);
+ flwrite(0xA0,0x555);
+- map->write8(map,*buf,base + off);
+- Last[0] = map->read8(map,base + off);
+- Last[1] = map->read8(map,base + off);
+- Last[2] = map->read8(map,base + off);
++ map_write8(map,*buf,base + off);
++ Last[0] = map_read8(map,base + off);
++ Last[1] = map_read8(map,base + off);
++ Last[2] = map_read8(map,base + off);
+
+ /* Wait for the flash to finish the operation. We store the last 4
+ status bytes that have been retrieved so we can determine why
+@@ -820,7 +861,7 @@
+ failure */
+ for (Count = 3; Last[(Count - 1) % 4] != Last[(Count - 2) % 4] &&
+ Count < 10000; Count++)
+- Last[Count % 4] = map->read8(map,base + off);
++ Last[Count % 4] = map_read8(map,base + off);
+ if (Last[(Count - 1) % 4] != *buf)
+ {
+ jedec_flash_failed(Last[(Count - 3) % 4]);
+diff -Nurb linux-mips-2.4.27/drivers/mtd/chips/jedec_probe.c linux/drivers/mtd/chips/jedec_probe.c
+--- linux-mips-2.4.27/drivers/mtd/chips/jedec_probe.c 2003-02-26 01:53:49.000000000 +0100
++++ linux/drivers/mtd/chips/jedec_probe.c 2004-11-19 10:25:11.774218792 +0100
+@@ -1,9 +1,11 @@
+ /*
+ Common Flash Interface probe code.
+ (C) 2000 Red Hat. GPL'd.
+- $Id$
++ $Id$
+ See JEDEC (http://www.jedec.org/) standard JESD21C (section 3.5)
+ for the standard this probe goes back to.
++
++ Occasionally maintained by Thayne Harbaugh tharbaugh at lnxi dot com
+ */
+
+ #include <linux/config.h>
+@@ -15,7 +17,9 @@
+ #include <linux/errno.h>
+ #include <linux/slab.h>
+ #include <linux/interrupt.h>
++#include <linux/init.h>
+
++#include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+ #include <linux/mtd/cfi.h>
+ #include <linux/mtd/gen_probe.h>
+@@ -26,20 +30,24 @@
+ #define MANUFACTURER_FUJITSU 0x0004
+ #define MANUFACTURER_INTEL 0x0089
+ #define MANUFACTURER_MACRONIX 0x00C2
+-#define MANUFACTURER_ST 0x0020
++#define MANUFACTURER_PMC 0x009D
+ #define MANUFACTURER_SST 0x00BF
++#define MANUFACTURER_ST 0x0020
+ #define MANUFACTURER_TOSHIBA 0x0098
++#define MANUFACTURER_WINBOND 0x00da
+
+
+ /* AMD */
+ #define AM29F800BB 0x2258
+ #define AM29F800BT 0x22D6
++#define AM29LV400BB 0x22BA
++#define AM29LV400BT 0x22B9
+ #define AM29LV800BB 0x225B
+ #define AM29LV800BT 0x22DA
+ #define AM29LV160DT 0x22C4
+ #define AM29LV160DB 0x2249
+ #define AM29F017D 0x003D
+-#define AM29F016 0x00AD
++#define AM29F016D 0x00AD
+ #define AM29F080 0x00D5
+ #define AM29F040 0x00A4
+ #define AM29LV040B 0x004F
+@@ -54,6 +62,7 @@
+ #define AT49BV32XT 0x00C9
+
+ /* Fujitsu */
++#define MBM29F040C 0x00A4
+ #define MBM29LV650UE 0x22D7
+ #define MBM29LV320TE 0x22F6
+ #define MBM29LV320BE 0x22F9
+@@ -61,6 +70,9 @@
+ #define MBM29LV160BE 0x2249
+ #define MBM29LV800BA 0x225B
+ #define MBM29LV800TA 0x22DA
++#define MBM29LV400TC 0x22B9
++#define MBM29LV400BC 0x22BA
++
+
+ /* Intel */
+ #define I28F004B3T 0x00d4
+@@ -93,8 +105,14 @@
+ #define MX29F004T 0x0045
+ #define MX29F004B 0x0046
+
++/* PMC */
++#define PM49FL002 0x006D
++#define PM49FL004 0x006E
++#define PM49FL008 0x006A
++
+ /* ST - www.st.com */
+-#define M29W800T 0x00D7
++#define M29W800DT 0x00D7
++#define M29W800DB 0x005B
+ #define M29W160DT 0x22C4
+ #define M29W160DB 0x2249
+ #define M29W040B 0x00E3
+@@ -110,6 +128,7 @@
+ #define SST39LF040 0x00D7
+ #define SST39SF010A 0x00B5
+ #define SST39SF020A 0x00B6
++#define SST49LF004B 0x0060
+ #define SST49LF030A 0x001C
+ #define SST49LF040A 0x0051
+ #define SST49LF080A 0x005B
+@@ -122,15 +141,87 @@
+ #define TC58FVT641 0x0093
+ #define TC58FVB641 0x0095
+
++/* Winbond */
++#define W49V002A 0x00b0
++
++
++/*
++ * Unlock address sets for AMD command sets.
++ * Intel command sets use the MTD_UADDR_UNNECESSARY.
++ * Each identifier, except MTD_UADDR_UNNECESSARY, and
++ * MTD_UADDR_NO_SUPPORT must be defined below in unlock_addrs[].
++ * MTD_UADDR_NOT_SUPPORTED must be 0 so that structure
++ * initialization need not require initializing all of the
++ * unlock addresses for all bit widths.
++ */
++enum uaddr {
++ MTD_UADDR_NOT_SUPPORTED = 0, /* data width not supported */
++ MTD_UADDR_0x0555_0x02AA,
++ MTD_UADDR_0x0555_0x0AAA,
++ MTD_UADDR_0x5555_0x2AAA,
++ MTD_UADDR_0x0AAA_0x0555,
++ MTD_UADDR_DONT_CARE, /* Requires an arbitrary address */
++ MTD_UADDR_UNNECESSARY, /* Does not require any address */
++};
++
++
++struct unlock_addr {
++ int addr1;
++ int addr2;
++};
++
++
++/*
++ * I don't like the fact that the first entry in unlock_addrs[]
++ * exists, but is for MTD_UADDR_NOT_SUPPORTED - and, therefore,
++ * should not be used. The problem is that structures with
++ * initializers have extra fields initialized to 0. It is _very_
++ * desireable to have the unlock address entries for unsupported
++ * data widths automatically initialized - that means that
++ * MTD_UADDR_NOT_SUPPORTED must be 0 and the first entry here
++ * must go unused.
++ */
++static const struct unlock_addr unlock_addrs[] = {
++ [MTD_UADDR_NOT_SUPPORTED] = {
++ .addr1 = 0xffff,
++ .addr2 = 0xffff
++ },
++
++ [MTD_UADDR_0x0555_0x02AA] = {
++ .addr1 = 0x0555,
++ .addr2 = 0x02aa
++ },
++
++ [MTD_UADDR_0x0555_0x0AAA] = {
++ .addr1 = 0x0555,
++ .addr2 = 0x0aaa
++ },
++
++ [MTD_UADDR_0x5555_0x2AAA] = {
++ .addr1 = 0x5555,
++ .addr2 = 0x2aaa
++ },
++
++ [MTD_UADDR_0x0AAA_0x0555] = {
++ .addr1 = 0x0AAA,
++ .addr2 = 0x0555
++ },
++
++ [MTD_UADDR_DONT_CARE] = {
++ .addr1 = 0x0000, /* Doesn't matter which address */
++ .addr2 = 0x0000 /* is used - must be last entry */
++ }
++};
++
+
+ struct amd_flash_info {
+ const __u16 mfr_id;
+ const __u16 dev_id;
+ const char *name;
+ const int DevSize;
+- const int InterfaceDesc;
+ const int NumEraseRegions;
+ const int CmdSet;
++ const __u8 uaddr[4]; /* unlock addrs for 8, 16, 32, 64 */
+ const ulong regions[4];
+ };
+
+@@ -145,760 +236,1214 @@
+ #define SIZE_4MiB 22
+ #define SIZE_8MiB 23
+
++
++/*
++ * Please keep this list ordered by manufacturer!
++ * Fortunately, the list isn't searched often and so a
++ * slow, linear search isn't so bad.
++ */
+ static const struct amd_flash_info jedec_table[] = {
+ {
+- mfr_id: MANUFACTURER_AMD,
+- dev_id: AM29F032B,
+- name: "AMD AM29F032B",
+- DevSize: SIZE_4MiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 1,
+- regions: {ERASEINFO(0x10000,64)
+- }
+- }, {
+- mfr_id: MANUFACTURER_AMD,
+- dev_id: AM29LV160DT,
+- name: "AMD AM29LV160DT",
+- DevSize: SIZE_2MiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 4,
+- regions: {ERASEINFO(0x10000,31),
++ .mfr_id = MANUFACTURER_AMD,
++ .dev_id = AM29F032B,
++ .name = "AMD AM29F032B",
++ .uaddr = {
++ [0] = MTD_UADDR_0x0555_0x02AA /* x8 */
++ },
++ .DevSize = SIZE_4MiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 1,
++ .regions = {
++ ERASEINFO(0x10000,64)
++ }
++ }, {
++ .mfr_id = MANUFACTURER_AMD,
++ .dev_id = AM29LV160DT,
++ .name = "AMD AM29LV160DT",
++ .uaddr = {
++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */
++ [1] = MTD_UADDR_0x0555_0x02AA /* x16 */
++ },
++ .DevSize = SIZE_2MiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 4,
++ .regions = {
++ ERASEINFO(0x10000,31),
+ ERASEINFO(0x08000,1),
+ ERASEINFO(0x02000,2),
+ ERASEINFO(0x04000,1)
+ }
+ }, {
+- mfr_id: MANUFACTURER_AMD,
+- dev_id: AM29LV160DB,
+- name: "AMD AM29LV160DB",
+- DevSize: SIZE_2MiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 4,
+- regions: {ERASEINFO(0x04000,1),
++ .mfr_id = MANUFACTURER_AMD,
++ .dev_id = AM29LV160DB,
++ .name = "AMD AM29LV160DB",
++ .uaddr = {
++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */
++ [1] = MTD_UADDR_0x0555_0x02AA /* x16 */
++ },
++ .DevSize = SIZE_2MiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 4,
++ .regions = {
++ ERASEINFO(0x04000,1),
+ ERASEINFO(0x02000,2),
+ ERASEINFO(0x08000,1),
+ ERASEINFO(0x10000,31)
+ }
+ }, {
+- mfr_id: MANUFACTURER_TOSHIBA,
+- dev_id: TC58FVT160,
+- name: "Toshiba TC58FVT160",
+- DevSize: SIZE_2MiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 4,
+- regions: {ERASEINFO(0x10000,31),
++ .mfr_id = MANUFACTURER_AMD,
++ .dev_id = AM29LV400BB,
++ .name = "AMD AM29LV400BB",
++ .uaddr = {
++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */
++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */
++ },
++ .DevSize = SIZE_512KiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 4,
++ .regions = {
++ ERASEINFO(0x04000,1),
++ ERASEINFO(0x02000,2),
++ ERASEINFO(0x08000,1),
++ ERASEINFO(0x10000,7)
++ }
++ }, {
++ .mfr_id = MANUFACTURER_AMD,
++ .dev_id = AM29LV400BT,
++ .name = "AMD AM29LV400BT",
++ .uaddr = {
++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */
++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */
++ },
++ .DevSize = SIZE_512KiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 4,
++ .regions = {
++ ERASEINFO(0x10000,7),
+ ERASEINFO(0x08000,1),
+ ERASEINFO(0x02000,2),
+ ERASEINFO(0x04000,1)
+ }
+ }, {
+- mfr_id: MANUFACTURER_TOSHIBA,
+- dev_id: TC58FVB160,
+- name: "Toshiba TC58FVB160",
+- DevSize: SIZE_2MiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 4,
+- regions: {ERASEINFO(0x04000,1),
++ .mfr_id = MANUFACTURER_AMD,
++ .dev_id = AM29LV800BB,
++ .name = "AMD AM29LV800BB",
++ .uaddr = {
++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */
++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */
++ },
++ .DevSize = SIZE_1MiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 4,
++ .regions = {
++ ERASEINFO(0x04000,1),
+ ERASEINFO(0x02000,2),
+ ERASEINFO(0x08000,1),
+- ERASEINFO(0x10000,31)
++ ERASEINFO(0x10000,15),
+ }
+ }, {
+- mfr_id: MANUFACTURER_TOSHIBA,
+- dev_id: TC58FVB321,
+- name: "Toshiba TC58FVB321",
+- DevSize: SIZE_4MiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 2,
+- regions: {ERASEINFO(0x02000,8),
+- ERASEINFO(0x10000,63)
++ .mfr_id = MANUFACTURER_AMD,
++ .dev_id = AM29F800BB,
++ .name = "AMD AM29F800BB",
++ .uaddr = {
++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */
++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */
++ },
++ .DevSize = SIZE_1MiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 4,
++ .regions = {
++ ERASEINFO(0x04000,1),
++ ERASEINFO(0x02000,2),
++ ERASEINFO(0x08000,1),
++ ERASEINFO(0x10000,15),
+ }
+ }, {
+- mfr_id: MANUFACTURER_TOSHIBA,
+- dev_id: TC58FVT321,
+- name: "Toshiba TC58FVT321",
+- DevSize: SIZE_4MiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 2,
+- regions: {ERASEINFO(0x10000,63),
+- ERASEINFO(0x02000,8)
++ .mfr_id = MANUFACTURER_AMD,
++ .dev_id = AM29LV800BT,
++ .name = "AMD AM29LV800BT",
++ .uaddr = {
++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */
++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */
++ },
++ .DevSize = SIZE_1MiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 4,
++ .regions = {
++ ERASEINFO(0x10000,15),
++ ERASEINFO(0x08000,1),
++ ERASEINFO(0x02000,2),
++ ERASEINFO(0x04000,1)
+ }
+ }, {
+- mfr_id: MANUFACTURER_TOSHIBA,
+- dev_id: TC58FVB641,
+- name: "Toshiba TC58FVB641",
+- DevSize: SIZE_8MiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 2,
+- regions: {ERASEINFO(0x02000,8),
+- ERASEINFO(0x10000,127)
++ .mfr_id = MANUFACTURER_AMD,
++ .dev_id = AM29F800BT,
++ .name = "AMD AM29F800BT",
++ .uaddr = {
++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */
++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */
++ },
++ .DevSize = SIZE_1MiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 4,
++ .regions = {
++ ERASEINFO(0x10000,15),
++ ERASEINFO(0x08000,1),
++ ERASEINFO(0x02000,2),
++ ERASEINFO(0x04000,1)
+ }
+ }, {
+- mfr_id: MANUFACTURER_TOSHIBA,
+- dev_id: TC58FVT641,
+- name: "Toshiba TC58FVT641",
+- DevSize: SIZE_8MiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 2,
+- regions: {ERASEINFO(0x10000,127),
+- ERASEINFO(0x02000,8)
++ .mfr_id = MANUFACTURER_AMD,
++ .dev_id = AM29F017D,
++ .name = "AMD AM29F017D",
++ .uaddr = {
++ [0] = MTD_UADDR_DONT_CARE /* x8 */
++ },
++ .DevSize = SIZE_2MiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 1,
++ .regions = {
++ ERASEINFO(0x10000,32),
++ }
++ }, {
++ .mfr_id = MANUFACTURER_AMD,
++ .dev_id = AM29F016D,
++ .name = "AMD AM29F016D",
++ .uaddr = {
++ [0] = MTD_UADDR_0x0555_0x02AA /* x8 */
++ },
++ .DevSize = SIZE_2MiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 1,
++ .regions = {
++ ERASEINFO(0x10000,32),
++ }
++ }, {
++ .mfr_id = MANUFACTURER_AMD,
++ .dev_id = AM29F080,
++ .name = "AMD AM29F080",
++ .uaddr = {
++ [0] = MTD_UADDR_0x0555_0x02AA /* x8 */
++ },
++ .DevSize = SIZE_1MiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 1,
++ .regions = {
++ ERASEINFO(0x10000,16),
++ }
++ }, {
++ .mfr_id = MANUFACTURER_AMD,
++ .dev_id = AM29F040,
++ .name = "AMD AM29F040",
++ .uaddr = {
++ [0] = MTD_UADDR_0x0555_0x02AA /* x8 */
++ },
++ .DevSize = SIZE_512KiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 1,
++ .regions = {
++ ERASEINFO(0x10000,8),
++ }
++ }, {
++ .mfr_id = MANUFACTURER_AMD,
++ .dev_id = AM29LV040B,
++ .name = "AMD AM29LV040B",
++ .uaddr = {
++ [0] = MTD_UADDR_0x0555_0x02AA /* x8 */
++ },
++ .DevSize = SIZE_512KiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 1,
++ .regions = {
++ ERASEINFO(0x10000,8),
++ }
++ }, {
++ .mfr_id = MANUFACTURER_ATMEL,
++ .dev_id = AT49BV512,
++ .name = "Atmel AT49BV512",
++ .uaddr = {
++ [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */
++ },
++ .DevSize = SIZE_64KiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 1,
++ .regions = {
++ ERASEINFO(0x10000,1)
++ }
++ }, {
++ .mfr_id = MANUFACTURER_ATMEL,
++ .dev_id = AT29LV512,
++ .name = "Atmel AT29LV512",
++ .uaddr = {
++ [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */
++ },
++ .DevSize = SIZE_64KiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 1,
++ .regions = {
++ ERASEINFO(0x80,256),
++ ERASEINFO(0x80,256)
++ }
++ }, {
++ .mfr_id = MANUFACTURER_ATMEL,
++ .dev_id = AT49BV16X,
++ .name = "Atmel AT49BV16X",
++ .uaddr = {
++ [0] = MTD_UADDR_0x0555_0x0AAA, /* x8 */
++ [1] = MTD_UADDR_0x0555_0x0AAA /* x16 */
++ },
++ .DevSize = SIZE_2MiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 2,
++ .regions = {
++ ERASEINFO(0x02000,8),
++ ERASEINFO(0x10000,31)
+ }
+ }, {
+- mfr_id: MANUFACTURER_FUJITSU,
+- dev_id: MBM29LV650UE,
+- name: "Fujitsu MBM29LV650UE",
+- DevSize: SIZE_8MiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 1,
+- regions: {ERASEINFO(0x10000,128)
+- }
+- }, {
+- mfr_id: MANUFACTURER_FUJITSU,
+- dev_id: MBM29LV320TE,
+- name: "Fujitsu MBM29LV320TE",
+- DevSize: SIZE_4MiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 2,
+- regions: {ERASEINFO(0x10000,63),
++ .mfr_id = MANUFACTURER_ATMEL,
++ .dev_id = AT49BV16XT,
++ .name = "Atmel AT49BV16XT",
++ .uaddr = {
++ [0] = MTD_UADDR_0x0555_0x0AAA, /* x8 */
++ [1] = MTD_UADDR_0x0555_0x0AAA /* x16 */
++ },
++ .DevSize = SIZE_2MiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 2,
++ .regions = {
++ ERASEINFO(0x10000,31),
+ ERASEINFO(0x02000,8)
+ }
+ }, {
+- mfr_id: MANUFACTURER_FUJITSU,
+- dev_id: MBM29LV320BE,
+- name: "Fujitsu MBM29LV320BE",
+- DevSize: SIZE_4MiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 2,
+- regions: {ERASEINFO(0x02000,8),
++ .mfr_id = MANUFACTURER_ATMEL,
++ .dev_id = AT49BV32X,
++ .name = "Atmel AT49BV32X",
++ .uaddr = {
++ [0] = MTD_UADDR_0x0555_0x0AAA, /* x8 */
++ [1] = MTD_UADDR_0x0555_0x0AAA /* x16 */
++ },
++ .DevSize = SIZE_4MiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 2,
++ .regions = {
++ ERASEINFO(0x02000,8),
+ ERASEINFO(0x10000,63)
+ }
+ }, {
+- mfr_id: MANUFACTURER_FUJITSU,
+- dev_id: MBM29LV160TE,
+- name: "Fujitsu MBM29LV160TE",
+- DevSize: SIZE_2MiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 4,
+- regions: {ERASEINFO(0x10000,31),
+- ERASEINFO(0x08000,1),
+- ERASEINFO(0x02000,2),
+- ERASEINFO(0x04000,1)
++ .mfr_id = MANUFACTURER_ATMEL,
++ .dev_id = AT49BV32XT,
++ .name = "Atmel AT49BV32XT",
++ .uaddr = {
++ [0] = MTD_UADDR_0x0555_0x0AAA, /* x8 */
++ [1] = MTD_UADDR_0x0555_0x0AAA /* x16 */
++ },
++ .DevSize = SIZE_4MiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 2,
++ .regions = {
++ ERASEINFO(0x10000,63),
++ ERASEINFO(0x02000,8)
+ }
+ }, {
+- mfr_id: MANUFACTURER_FUJITSU,
+- dev_id: MBM29LV160BE,
+- name: "Fujitsu MBM29LV160BE",
+- DevSize: SIZE_2MiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 4,
+- regions: {ERASEINFO(0x04000,1),
+- ERASEINFO(0x02000,2),
+- ERASEINFO(0x08000,1),
+- ERASEINFO(0x10000,31)
++ .mfr_id = MANUFACTURER_FUJITSU,
++ .dev_id = MBM29F040C,
++ .name = "Fujitsu MBM29F040C",
++ .uaddr = {
++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */
++ },
++ .DevSize = SIZE_512KiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 1,
++ .regions = {
++ ERASEINFO(0x10000,8)
++ }
++ }, {
++ .mfr_id = MANUFACTURER_FUJITSU,
++ .dev_id = MBM29LV650UE,
++ .name = "Fujitsu MBM29LV650UE",
++ .uaddr = {
++ [0] = MTD_UADDR_DONT_CARE /* x16 */
++ },
++ .DevSize = SIZE_8MiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 1,
++ .regions = {
++ ERASEINFO(0x10000,128)
++ }
++ }, {
++ .mfr_id = MANUFACTURER_FUJITSU,
++ .dev_id = MBM29LV320TE,
++ .name = "Fujitsu MBM29LV320TE",
++ .uaddr = {
++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */
++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */
++ },
++ .DevSize = SIZE_4MiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 2,
++ .regions = {
++ ERASEINFO(0x10000,63),
++ ERASEINFO(0x02000,8)
+ }
+ }, {
+- mfr_id: MANUFACTURER_FUJITSU,
+- dev_id: MBM29LV800BA,
+- name: "Fujitsu MBM29LV800BA",
+- DevSize: SIZE_1MiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 4,
+- regions: {ERASEINFO(0x04000,1),
+- ERASEINFO(0x02000,2),
+- ERASEINFO(0x08000,1),
+- ERASEINFO(0x10000,15)
++ .mfr_id = MANUFACTURER_FUJITSU,
++ .dev_id = MBM29LV320BE,
++ .name = "Fujitsu MBM29LV320BE",
++ .uaddr = {
++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */
++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */
++ },
++ .DevSize = SIZE_4MiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 2,
++ .regions = {
++ ERASEINFO(0x02000,8),
++ ERASEINFO(0x10000,63)
+ }
+ }, {
+- mfr_id: MANUFACTURER_FUJITSU,
+- dev_id: MBM29LV800TA,
+- name: "Fujitsu MBM29LV800TA",
+- DevSize: SIZE_1MiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 4,
+- regions: {ERASEINFO(0x10000,15),
++ .mfr_id = MANUFACTURER_FUJITSU,
++ .dev_id = MBM29LV160TE,
++ .name = "Fujitsu MBM29LV160TE",
++ .uaddr = {
++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */
++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */
++ },
++ .DevSize = SIZE_2MiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 4,
++ .regions = {
++ ERASEINFO(0x10000,31),
+ ERASEINFO(0x08000,1),
+ ERASEINFO(0x02000,2),
+ ERASEINFO(0x04000,1)
+ }
+ }, {
+- mfr_id: MANUFACTURER_AMD,
+- dev_id: AM29LV800BB,
+- name: "AMD AM29LV800BB",
+- DevSize: SIZE_1MiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 4,
+- regions: {ERASEINFO(0x04000,1),
++ .mfr_id = MANUFACTURER_FUJITSU,
++ .dev_id = MBM29LV160BE,
++ .name = "Fujitsu MBM29LV160BE",
++ .uaddr = {
++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */
++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */
++ },
++ .DevSize = SIZE_2MiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 4,
++ .regions = {
++ ERASEINFO(0x04000,1),
+ ERASEINFO(0x02000,2),
+ ERASEINFO(0x08000,1),
+- ERASEINFO(0x10000,15),
++ ERASEINFO(0x10000,31)
+ }
+ }, {
+- mfr_id: MANUFACTURER_AMD,
+- dev_id: AM29F800BB,
+- name: "AMD AM29F800BB",
+- DevSize: SIZE_1MiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 4,
+- regions: {ERASEINFO(0x04000,1),
++ .mfr_id = MANUFACTURER_FUJITSU,
++ .dev_id = MBM29LV800BA,
++ .name = "Fujitsu MBM29LV800BA",
++ .uaddr = {
++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */
++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */
++ },
++ .DevSize = SIZE_1MiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 4,
++ .regions = {
++ ERASEINFO(0x04000,1),
+ ERASEINFO(0x02000,2),
+ ERASEINFO(0x08000,1),
+- ERASEINFO(0x10000,15),
++ ERASEINFO(0x10000,15)
+ }
+ }, {
+- mfr_id: MANUFACTURER_AMD,
+- dev_id: AM29LV800BT,
+- name: "AMD AM29LV800BT",
+- DevSize: SIZE_1MiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 4,
+- regions: {ERASEINFO(0x10000,15),
++ .mfr_id = MANUFACTURER_FUJITSU,
++ .dev_id = MBM29LV800TA,
++ .name = "Fujitsu MBM29LV800TA",
++ .uaddr = {
++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */
++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */
++ },
++ .DevSize = SIZE_1MiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 4,
++ .regions = {
++ ERASEINFO(0x10000,15),
+ ERASEINFO(0x08000,1),
+ ERASEINFO(0x02000,2),
+ ERASEINFO(0x04000,1)
+ }
+ }, {
+- mfr_id: MANUFACTURER_AMD,
+- dev_id: AM29F800BT,
+- name: "AMD AM29F800BT",
+- DevSize: SIZE_1MiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 4,
+- regions: {ERASEINFO(0x10000,15),
+- ERASEINFO(0x08000,1),
++ .mfr_id = MANUFACTURER_FUJITSU,
++ .dev_id = MBM29LV400BC,
++ .name = "Fujitsu MBM29LV400BC",
++ .uaddr = {
++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */
++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */
++ },
++ .DevSize = SIZE_512KiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 4,
++ .regions = {
++ ERASEINFO(0x04000,1),
+ ERASEINFO(0x02000,2),
+- ERASEINFO(0x04000,1)
++ ERASEINFO(0x08000,1),
++ ERASEINFO(0x10000,7)
+ }
+ }, {
+- mfr_id: MANUFACTURER_AMD,
+- dev_id: AM29LV800BB,
+- name: "AMD AM29LV800BB",
+- DevSize: SIZE_1MiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 4,
+- regions: {ERASEINFO(0x10000,15),
++ .mfr_id = MANUFACTURER_FUJITSU,
++ .dev_id = MBM29LV400TC,
++ .name = "Fujitsu MBM29LV400TC",
++ .uaddr = {
++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */
++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */
++ },
++ .DevSize = SIZE_512KiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 4,
++ .regions = {
++ ERASEINFO(0x10000,7),
+ ERASEINFO(0x08000,1),
+ ERASEINFO(0x02000,2),
+ ERASEINFO(0x04000,1)
+ }
+ }, {
+- mfr_id: MANUFACTURER_INTEL,
+- dev_id: I28F004B3B,
+- name: "Intel 28F004B3B",
+- DevSize: SIZE_512KiB,
+- CmdSet: P_ID_INTEL_STD,
+- NumEraseRegions: 2,
+- regions: {
++ .mfr_id = MANUFACTURER_INTEL,
++ .dev_id = I28F004B3B,
++ .name = "Intel 28F004B3B",
++ .uaddr = {
++ [0] = MTD_UADDR_UNNECESSARY, /* x8 */
++ },
++ .DevSize = SIZE_512KiB,
++ .CmdSet = P_ID_INTEL_STD,
++ .NumEraseRegions= 2,
++ .regions = {
+ ERASEINFO(0x02000, 8),
+ ERASEINFO(0x10000, 7),
+ }
+ }, {
+- mfr_id: MANUFACTURER_INTEL,
+- dev_id: I28F004B3T,
+- name: "Intel 28F004B3T",
+- DevSize: SIZE_512KiB,
+- CmdSet: P_ID_INTEL_STD,
+- NumEraseRegions: 2,
+- regions: {
++ .mfr_id = MANUFACTURER_INTEL,
++ .dev_id = I28F004B3T,
++ .name = "Intel 28F004B3T",
++ .uaddr = {
++ [0] = MTD_UADDR_UNNECESSARY, /* x8 */
++ },
++ .DevSize = SIZE_512KiB,
++ .CmdSet = P_ID_INTEL_STD,
++ .NumEraseRegions= 2,
++ .regions = {
+ ERASEINFO(0x10000, 7),
+ ERASEINFO(0x02000, 8),
+ }
+ }, {
+- mfr_id: MANUFACTURER_INTEL,
+- dev_id: I28F400B3B,
+- name: "Intel 28F400B3B",
+- DevSize: SIZE_512KiB,
+- CmdSet: P_ID_INTEL_STD,
+- NumEraseRegions: 2,
+- regions: {
++ .mfr_id = MANUFACTURER_INTEL,
++ .dev_id = I28F400B3B,
++ .name = "Intel 28F400B3B",
++ .uaddr = {
++ [0] = MTD_UADDR_UNNECESSARY, /* x8 */
++ [1] = MTD_UADDR_UNNECESSARY, /* x16 */
++ },
++ .DevSize = SIZE_512KiB,
++ .CmdSet = P_ID_INTEL_STD,
++ .NumEraseRegions= 2,
++ .regions = {
+ ERASEINFO(0x02000, 8),
+ ERASEINFO(0x10000, 7),
+ }
+ }, {
+- mfr_id: MANUFACTURER_INTEL,
+- dev_id: I28F400B3T,
+- name: "Intel 28F400B3T",
+- DevSize: SIZE_512KiB,
+- CmdSet: P_ID_INTEL_STD,
+- NumEraseRegions: 2,
+- regions: {
++ .mfr_id = MANUFACTURER_INTEL,
++ .dev_id = I28F400B3T,
++ .name = "Intel 28F400B3T",
++ .uaddr = {
++ [0] = MTD_UADDR_UNNECESSARY, /* x8 */
++ [1] = MTD_UADDR_UNNECESSARY, /* x16 */
++ },
++ .DevSize = SIZE_512KiB,
++ .CmdSet = P_ID_INTEL_STD,
++ .NumEraseRegions= 2,
++ .regions = {
+ ERASEINFO(0x10000, 7),
+ ERASEINFO(0x02000, 8),
+ }
+ }, {
+- mfr_id: MANUFACTURER_INTEL,
+- dev_id: I28F008B3B,
+- name: "Intel 28F008B3B",
+- DevSize: SIZE_1MiB,
+- CmdSet: P_ID_INTEL_STD,
+- NumEraseRegions: 2,
+- regions: {
++ .mfr_id = MANUFACTURER_INTEL,
++ .dev_id = I28F008B3B,
++ .name = "Intel 28F008B3B",
++ .uaddr = {
++ [0] = MTD_UADDR_UNNECESSARY, /* x8 */
++ },
++ .DevSize = SIZE_1MiB,
++ .CmdSet = P_ID_INTEL_STD,
++ .NumEraseRegions= 2,
++ .regions = {
+ ERASEINFO(0x02000, 8),
+ ERASEINFO(0x10000, 15),
+ }
+ }, {
+- mfr_id: MANUFACTURER_INTEL,
+- dev_id: I28F008B3T,
+- name: "Intel 28F008B3T",
+- DevSize: SIZE_1MiB,
+- CmdSet: P_ID_INTEL_STD,
+- NumEraseRegions: 2,
+- regions: {
++ .mfr_id = MANUFACTURER_INTEL,
++ .dev_id = I28F008B3T,
++ .name = "Intel 28F008B3T",
++ .uaddr = {
++ [0] = MTD_UADDR_UNNECESSARY, /* x8 */
++ },
++ .DevSize = SIZE_1MiB,
++ .CmdSet = P_ID_INTEL_STD,
++ .NumEraseRegions= 2,
++ .regions = {
+ ERASEINFO(0x10000, 15),
+ ERASEINFO(0x02000, 8),
+ }
+ }, {
+- mfr_id: MANUFACTURER_INTEL,
+- dev_id: I28F008S5,
+- name: "Intel 28F008S5",
+- DevSize: SIZE_1MiB,
+- CmdSet: P_ID_INTEL_EXT,
+- NumEraseRegions: 1,
+- regions: {ERASEINFO(0x10000,16),
+- }
+- }, {
+- mfr_id: MANUFACTURER_INTEL,
+- dev_id: I28F016S5,
+- name: "Intel 28F016S5",
+- DevSize: SIZE_2MiB,
+- CmdSet: P_ID_INTEL_EXT,
+- NumEraseRegions: 1,
+- regions: {ERASEINFO(0x10000,32),
+- }
+- }, {
+- mfr_id: MANUFACTURER_INTEL,
+- dev_id: I28F008SA,
+- name: "Intel 28F008SA",
+- DevSize: SIZE_1MiB,
+- CmdSet: P_ID_INTEL_STD,
+- NumEraseRegions: 1,
+- regions: {
++ .mfr_id = MANUFACTURER_INTEL,
++ .dev_id = I28F008S5,
++ .name = "Intel 28F008S5",
++ .uaddr = {
++ [0] = MTD_UADDR_UNNECESSARY, /* x8 */
++ },
++ .DevSize = SIZE_1MiB,
++ .CmdSet = P_ID_INTEL_EXT,
++ .NumEraseRegions= 1,
++ .regions = {
++ ERASEINFO(0x10000,16),
++ }
++ }, {
++ .mfr_id = MANUFACTURER_INTEL,
++ .dev_id = I28F016S5,
++ .name = "Intel 28F016S5",
++ .uaddr = {
++ [0] = MTD_UADDR_UNNECESSARY, /* x8 */
++ },
++ .DevSize = SIZE_2MiB,
++ .CmdSet = P_ID_INTEL_EXT,
++ .NumEraseRegions= 1,
++ .regions = {
++ ERASEINFO(0x10000,32),
++ }
++ }, {
++ .mfr_id = MANUFACTURER_INTEL,
++ .dev_id = I28F008SA,
++ .name = "Intel 28F008SA",
++ .uaddr = {
++ [0] = MTD_UADDR_UNNECESSARY, /* x8 */
++ },
++ .DevSize = SIZE_1MiB,
++ .CmdSet = P_ID_INTEL_STD,
++ .NumEraseRegions= 1,
++ .regions = {
+ ERASEINFO(0x10000, 16),
+ }
+ }, {
+- mfr_id: MANUFACTURER_INTEL,
+- dev_id: I28F800B3B,
+- name: "Intel 28F800B3B",
+- DevSize: SIZE_1MiB,
+- CmdSet: P_ID_INTEL_STD,
+- NumEraseRegions: 2,
+- regions: {
++ .mfr_id = MANUFACTURER_INTEL,
++ .dev_id = I28F800B3B,
++ .name = "Intel 28F800B3B",
++ .uaddr = {
++ [1] = MTD_UADDR_UNNECESSARY, /* x16 */
++ },
++ .DevSize = SIZE_1MiB,
++ .CmdSet = P_ID_INTEL_STD,
++ .NumEraseRegions= 2,
++ .regions = {
+ ERASEINFO(0x02000, 8),
+ ERASEINFO(0x10000, 15),
+ }
+ }, {
+- mfr_id: MANUFACTURER_INTEL,
+- dev_id: I28F800B3T,
+- name: "Intel 28F800B3T",
+- DevSize: SIZE_1MiB,
+- CmdSet: P_ID_INTEL_STD,
+- NumEraseRegions: 2,
+- regions: {
++ .mfr_id = MANUFACTURER_INTEL,
++ .dev_id = I28F800B3T,
++ .name = "Intel 28F800B3T",
++ .uaddr = {
++ [1] = MTD_UADDR_UNNECESSARY, /* x16 */
++ },
++ .DevSize = SIZE_1MiB,
++ .CmdSet = P_ID_INTEL_STD,
++ .NumEraseRegions= 2,
++ .regions = {
+ ERASEINFO(0x10000, 15),
+ ERASEINFO(0x02000, 8),
+ }
+ }, {
+- mfr_id: MANUFACTURER_INTEL,
+- dev_id: I28F016B3B,
+- name: "Intel 28F016B3B",
+- DevSize: SIZE_2MiB,
+- CmdSet: P_ID_INTEL_STD,
+- NumEraseRegions: 2,
+- regions: {
++ .mfr_id = MANUFACTURER_INTEL,
++ .dev_id = I28F016B3B,
++ .name = "Intel 28F016B3B",
++ .uaddr = {
++ [0] = MTD_UADDR_UNNECESSARY, /* x8 */
++ },
++ .DevSize = SIZE_2MiB,
++ .CmdSet = P_ID_INTEL_STD,
++ .NumEraseRegions= 2,
++ .regions = {
+ ERASEINFO(0x02000, 8),
+ ERASEINFO(0x10000, 31),
+ }
+ }, {
+- mfr_id: MANUFACTURER_INTEL,
+- dev_id: I28F016S3,
+- name: "Intel I28F016S3",
+- DevSize: SIZE_2MiB,
+- CmdSet: P_ID_INTEL_STD,
+- NumEraseRegions: 1,
+- regions: {
++ .mfr_id = MANUFACTURER_INTEL,
++ .dev_id = I28F016S3,
++ .name = "Intel I28F016S3",
++ .uaddr = {
++ [0] = MTD_UADDR_UNNECESSARY, /* x8 */
++ },
++ .DevSize = SIZE_2MiB,
++ .CmdSet = P_ID_INTEL_STD,
++ .NumEraseRegions= 1,
++ .regions = {
+ ERASEINFO(0x10000, 32),
+ }
+ }, {
+- mfr_id: MANUFACTURER_INTEL,
+- dev_id: I28F016B3T,
+- name: "Intel 28F016B3T",
+- DevSize: SIZE_2MiB,
+- CmdSet: P_ID_INTEL_STD,
+- NumEraseRegions: 2,
+- regions: {
++ .mfr_id = MANUFACTURER_INTEL,
++ .dev_id = I28F016B3T,
++ .name = "Intel 28F016B3T",
++ .uaddr = {
++ [0] = MTD_UADDR_UNNECESSARY, /* x8 */
++ },
++ .DevSize = SIZE_2MiB,
++ .CmdSet = P_ID_INTEL_STD,
++ .NumEraseRegions= 2,
++ .regions = {
+ ERASEINFO(0x10000, 31),
+ ERASEINFO(0x02000, 8),
+ }
+ }, {
+- mfr_id: MANUFACTURER_INTEL,
+- dev_id: I28F160B3B,
+- name: "Intel 28F160B3B",
+- DevSize: SIZE_2MiB,
+- CmdSet: P_ID_INTEL_STD,
+- NumEraseRegions: 2,
+- regions: {
++ .mfr_id = MANUFACTURER_INTEL,
++ .dev_id = I28F160B3B,
++ .name = "Intel 28F160B3B",
++ .uaddr = {
++ [1] = MTD_UADDR_UNNECESSARY, /* x16 */
++ },
++ .DevSize = SIZE_2MiB,
++ .CmdSet = P_ID_INTEL_STD,
++ .NumEraseRegions= 2,
++ .regions = {
+ ERASEINFO(0x02000, 8),
+ ERASEINFO(0x10000, 31),
+ }
+ }, {
+- mfr_id: MANUFACTURER_INTEL,
+- dev_id: I28F160B3T,
+- name: "Intel 28F160B3T",
+- DevSize: SIZE_2MiB,
+- CmdSet: P_ID_INTEL_STD,
+- NumEraseRegions: 2,
+- regions: {
++ .mfr_id = MANUFACTURER_INTEL,
++ .dev_id = I28F160B3T,
++ .name = "Intel 28F160B3T",
++ .uaddr = {
++ [1] = MTD_UADDR_UNNECESSARY, /* x16 */
++ },
++ .DevSize = SIZE_2MiB,
++ .CmdSet = P_ID_INTEL_STD,
++ .NumEraseRegions= 2,
++ .regions = {
+ ERASEINFO(0x10000, 31),
+ ERASEINFO(0x02000, 8),
+ }
+ }, {
+- mfr_id: MANUFACTURER_INTEL,
+- dev_id: I28F320B3B,
+- name: "Intel 28F320B3B",
+- DevSize: SIZE_4MiB,
+- CmdSet: P_ID_INTEL_STD,
+- NumEraseRegions: 2,
+- regions: {
++ .mfr_id = MANUFACTURER_INTEL,
++ .dev_id = I28F320B3B,
++ .name = "Intel 28F320B3B",
++ .uaddr = {
++ [1] = MTD_UADDR_UNNECESSARY, /* x16 */
++ },
++ .DevSize = SIZE_4MiB,
++ .CmdSet = P_ID_INTEL_STD,
++ .NumEraseRegions= 2,
++ .regions = {
+ ERASEINFO(0x02000, 8),
+ ERASEINFO(0x10000, 63),
+ }
+ }, {
+- mfr_id: MANUFACTURER_INTEL,
+- dev_id: I28F320B3T,
+- name: "Intel 28F320B3T",
+- DevSize: SIZE_4MiB,
+- CmdSet: P_ID_INTEL_STD,
+- NumEraseRegions: 2,
+- regions: {
++ .mfr_id = MANUFACTURER_INTEL,
++ .dev_id = I28F320B3T,
++ .name = "Intel 28F320B3T",
++ .uaddr = {
++ [1] = MTD_UADDR_UNNECESSARY, /* x16 */
++ },
++ .DevSize = SIZE_4MiB,
++ .CmdSet = P_ID_INTEL_STD,
++ .NumEraseRegions= 2,
++ .regions = {
+ ERASEINFO(0x10000, 63),
+ ERASEINFO(0x02000, 8),
+ }
+ }, {
+- mfr_id: MANUFACTURER_INTEL,
+- dev_id: I28F640B3B,
+- name: "Intel 28F640B3B",
+- DevSize: SIZE_8MiB,
+- CmdSet: P_ID_INTEL_STD,
+- NumEraseRegions: 2,
+- regions: {
++ .mfr_id = MANUFACTURER_INTEL,
++ .dev_id = I28F640B3B,
++ .name = "Intel 28F640B3B",
++ .uaddr = {
++ [1] = MTD_UADDR_UNNECESSARY, /* x16 */
++ },
++ .DevSize = SIZE_8MiB,
++ .CmdSet = P_ID_INTEL_STD,
++ .NumEraseRegions= 2,
++ .regions = {
+ ERASEINFO(0x02000, 8),
+ ERASEINFO(0x10000, 127),
+ }
+ }, {
+- mfr_id: MANUFACTURER_INTEL,
+- dev_id: I28F640B3T,
+- name: "Intel 28F640B3T",
+- DevSize: SIZE_8MiB,
+- CmdSet: P_ID_INTEL_STD,
+- NumEraseRegions: 2,
+- regions: {
++ .mfr_id = MANUFACTURER_INTEL,
++ .dev_id = I28F640B3T,
++ .name = "Intel 28F640B3T",
++ .uaddr = {
++ [1] = MTD_UADDR_UNNECESSARY, /* x16 */
++ },
++ .DevSize = SIZE_8MiB,
++ .CmdSet = P_ID_INTEL_STD,
++ .NumEraseRegions= 2,
++ .regions = {
+ ERASEINFO(0x10000, 127),
+ ERASEINFO(0x02000, 8),
+ }
+ }, {
+- mfr_id: MANUFACTURER_INTEL,
+- dev_id: I82802AB,
+- name: "Intel 82802AB",
+- DevSize: SIZE_512KiB,
+- CmdSet: P_ID_INTEL_EXT,
+- NumEraseRegions: 1,
+- regions: {ERASEINFO(0x10000,8),
+- }
+- }, {
+- mfr_id: MANUFACTURER_INTEL,
+- dev_id: I82802AC,
+- name: "Intel 82802AC",
+- DevSize: SIZE_1MiB,
+- CmdSet: P_ID_INTEL_EXT,
+- NumEraseRegions: 1,
+- regions: {ERASEINFO(0x10000,16),
+- }
+- }, {
+- mfr_id: MANUFACTURER_ST,
+- dev_id: M29W800T,
+- name: "ST M29W800T",
+- DevSize: SIZE_1MiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 4,
+- regions: {ERASEINFO(0x10000,15),
++ .mfr_id = MANUFACTURER_INTEL,
++ .dev_id = I82802AB,
++ .name = "Intel 82802AB",
++ .uaddr = {
++ [0] = MTD_UADDR_UNNECESSARY, /* x8 */
++ },
++ .DevSize = SIZE_512KiB,
++ .CmdSet = P_ID_INTEL_EXT,
++ .NumEraseRegions= 1,
++ .regions = {
++ ERASEINFO(0x10000,8),
++ }
++ }, {
++ .mfr_id = MANUFACTURER_INTEL,
++ .dev_id = I82802AC,
++ .name = "Intel 82802AC",
++ .uaddr = {
++ [0] = MTD_UADDR_UNNECESSARY, /* x8 */
++ },
++ .DevSize = SIZE_1MiB,
++ .CmdSet = P_ID_INTEL_EXT,
++ .NumEraseRegions= 1,
++ .regions = {
++ ERASEINFO(0x10000,16),
++ }
++ }, {
++ .mfr_id = MANUFACTURER_MACRONIX,
++ .dev_id = MX29LV160T,
++ .name = "MXIC MX29LV160T",
++ .uaddr = {
++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */
++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */
++ },
++ .DevSize = SIZE_2MiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 4,
++ .regions = {
++ ERASEINFO(0x10000,31),
+ ERASEINFO(0x08000,1),
+ ERASEINFO(0x02000,2),
+ ERASEINFO(0x04000,1)
+ }
+ }, {
+- mfr_id: MANUFACTURER_ST,
+- dev_id: M29W160DT,
+- name: "ST M29W160DT",
+- DevSize: SIZE_2MiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 4,
+- regions: {ERASEINFO(0x10000,31),
+- ERASEINFO(0x08000,1),
+- ERASEINFO(0x02000,2),
+- ERASEINFO(0x04000,1)
+- }
+- }, {
+- mfr_id: MANUFACTURER_ST,
+- dev_id: M29W160DB,
+- name: "ST M29W160DB",
+- DevSize: SIZE_2MiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 4,
+- regions: {ERASEINFO(0x04000,1),
++ .mfr_id = MANUFACTURER_MACRONIX,
++ .dev_id = MX29LV160B,
++ .name = "MXIC MX29LV160B",
++ .uaddr = {
++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */
++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */
++ },
++ .DevSize = SIZE_2MiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 4,
++ .regions = {
++ ERASEINFO(0x04000,1),
+ ERASEINFO(0x02000,2),
+ ERASEINFO(0x08000,1),
+ ERASEINFO(0x10000,31)
+ }
+ }, {
+- mfr_id: MANUFACTURER_ATMEL,
+- dev_id: AT49BV512,
+- name: "Atmel AT49BV512",
+- DevSize: SIZE_64KiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 1,
+- regions: {ERASEINFO(0x10000,1)
+- }
+- }, {
+- mfr_id: MANUFACTURER_ATMEL,
+- dev_id: AT29LV512,
+- name: "Atmel AT29LV512",
+- DevSize: SIZE_64KiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 1,
+- regions: {
+- ERASEINFO(0x80,256),
+- ERASEINFO(0x80,256)
+- }
+- }, {
+- mfr_id: MANUFACTURER_ATMEL,
+- dev_id: AT49BV16X,
+- name: "Atmel AT49BV16X",
+- DevSize: SIZE_2MiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 2,
+- regions: {ERASEINFO(0x02000,8),
+- ERASEINFO(0x10000,31)
++ .mfr_id = MANUFACTURER_MACRONIX,
++ .dev_id = MX29F016,
++ .name = "Macronix MX29F016",
++ .uaddr = {
++ [0] = MTD_UADDR_0x0555_0x02AA /* x8 */
++ },
++ .DevSize = SIZE_2MiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 1,
++ .regions = {
++ ERASEINFO(0x10000,32),
++ }
++ }, {
++ .mfr_id = MANUFACTURER_MACRONIX,
++ .dev_id = MX29F004T,
++ .name = "Macronix MX29F004T",
++ .uaddr = {
++ [0] = MTD_UADDR_0x0555_0x02AA /* x8 */
++ },
++ .DevSize = SIZE_512KiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 4,
++ .regions = {
++ ERASEINFO(0x10000,7),
++ ERASEINFO(0x08000,1),
++ ERASEINFO(0x02000,2),
++ ERASEINFO(0x04000,1),
+ }
+ }, {
+- mfr_id: MANUFACTURER_ATMEL,
+- dev_id: AT49BV16XT,
+- name: "Atmel AT49BV16XT",
+- DevSize: SIZE_2MiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 2,
+- regions: {ERASEINFO(0x10000,31),
+- ERASEINFO(0x02000,8)
++ .mfr_id = MANUFACTURER_MACRONIX,
++ .dev_id = MX29F004B,
++ .name = "Macronix MX29F004B",
++ .uaddr = {
++ [0] = MTD_UADDR_0x0555_0x02AA /* x8 */
++ },
++ .DevSize = SIZE_512KiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 4,
++ .regions = {
++ ERASEINFO(0x04000,1),
++ ERASEINFO(0x02000,2),
++ ERASEINFO(0x08000,1),
++ ERASEINFO(0x10000,7),
+ }
+ }, {
+- mfr_id: MANUFACTURER_ATMEL,
+- dev_id: AT49BV32X,
+- name: "Atmel AT49BV32X",
+- DevSize: SIZE_4MiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 2,
+- regions: {ERASEINFO(0x02000,8),
+- ERASEINFO(0x10000,63)
++ .mfr_id = MANUFACTURER_PMC,
++ .dev_id = PM49FL002,
++ .name = "PMC Pm49FL002",
++ .uaddr = {
++ [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */
++ },
++ .DevSize = SIZE_256KiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 1,
++ .regions = {
++ ERASEINFO( 0x01000, 64 )
++ }
++ }, {
++ .mfr_id = MANUFACTURER_PMC,
++ .dev_id = PM49FL004,
++ .name = "PMC Pm49FL004",
++ .uaddr = {
++ [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */
++ },
++ .DevSize = SIZE_512KiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 1,
++ .regions = {
++ ERASEINFO( 0x01000, 128 )
++ }
++ }, {
++ .mfr_id = MANUFACTURER_PMC,
++ .dev_id = PM49FL008,
++ .name = "PMC Pm49FL008",
++ .uaddr = {
++ [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */
++ },
++ .DevSize = SIZE_1MiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 1,
++ .regions = {
++ ERASEINFO( 0x01000, 256 )
++ }
++ }, {
++ .mfr_id = MANUFACTURER_SST,
++ .dev_id = SST39LF512,
++ .name = "SST 39LF512",
++ .uaddr = {
++ [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */
++ },
++ .DevSize = SIZE_64KiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 1,
++ .regions = {
++ ERASEINFO(0x01000,16),
++ }
++ }, {
++ .mfr_id = MANUFACTURER_SST,
++ .dev_id = SST39LF010,
++ .name = "SST 39LF010",
++ .uaddr = {
++ [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */
++ },
++ .DevSize = SIZE_128KiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 1,
++ .regions = {
++ ERASEINFO(0x01000,32),
++ }
++ }, {
++ .mfr_id = MANUFACTURER_SST,
++ .dev_id = SST39LF020,
++ .name = "SST 39LF020",
++ .uaddr = {
++ [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */
++ },
++ .DevSize = SIZE_256KiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 1,
++ .regions = {
++ ERASEINFO(0x01000,64),
++ }
++ }, {
++ .mfr_id = MANUFACTURER_SST,
++ .dev_id = SST39LF040,
++ .name = "SST 39LF040",
++ .uaddr = {
++ [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */
++ },
++ .DevSize = SIZE_512KiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 1,
++ .regions = {
++ ERASEINFO(0x01000,128),
++ }
++ }, {
++ .mfr_id = MANUFACTURER_SST,
++ .dev_id = SST39SF010A,
++ .name = "SST 39SF010A",
++ .uaddr = {
++ [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */
++ },
++ .DevSize = SIZE_128KiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 1,
++ .regions = {
++ ERASEINFO(0x01000,32),
++ }
++ }, {
++ .mfr_id = MANUFACTURER_SST,
++ .dev_id = SST39SF020A,
++ .name = "SST 39SF020A",
++ .uaddr = {
++ [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */
++ },
++ .DevSize = SIZE_256KiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 1,
++ .regions = {
++ ERASEINFO(0x01000,64),
++ }
++ }, {
++ .mfr_id = MANUFACTURER_SST,
++ .dev_id = SST49LF004B,
++ .name = "SST 49LF004B",
++ .uaddr = {
++ [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */
++ },
++ .DevSize = SIZE_512KiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 1,
++ .regions = {
++ ERASEINFO(0x01000,128),
++ }
++ }, {
++ .mfr_id = MANUFACTURER_SST,
++ .dev_id = SST49LF030A,
++ .name = "SST 49LF030A",
++ .uaddr = {
++ [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */
++ },
++ .DevSize = SIZE_512KiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 1,
++ .regions = {
++ ERASEINFO(0x01000,96),
++ }
++ }, {
++ .mfr_id = MANUFACTURER_SST,
++ .dev_id = SST49LF040A,
++ .name = "SST 49LF040A",
++ .uaddr = {
++ [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */
++ },
++ .DevSize = SIZE_512KiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 1,
++ .regions = {
++ ERASEINFO(0x01000,128),
++ }
++ }, {
++ .mfr_id = MANUFACTURER_SST,
++ .dev_id = SST49LF080A,
++ .name = "SST 49LF080A",
++ .uaddr = {
++ [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */
++ },
++ .DevSize = SIZE_1MiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 1,
++ .regions = {
++ ERASEINFO(0x01000,256),
++ }
++ }, {
++ .mfr_id = MANUFACTURER_ST, /* FIXME - CFI device? */
++ .dev_id = M29W800DT,
++ .name = "ST M29W800DT",
++ .uaddr = {
++ [0] = MTD_UADDR_0x5555_0x2AAA, /* x8 */
++ [1] = MTD_UADDR_0x5555_0x2AAA /* x16 */
++ },
++ .DevSize = SIZE_1MiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 4,
++ .regions = {
++ ERASEINFO(0x10000,15),
++ ERASEINFO(0x08000,1),
++ ERASEINFO(0x02000,2),
++ ERASEINFO(0x04000,1)
+ }
+ }, {
+- mfr_id: MANUFACTURER_ATMEL,
+- dev_id: AT49BV32XT,
+- name: "Atmel AT49BV32XT",
+- DevSize: SIZE_4MiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 2,
+- regions: {ERASEINFO(0x10000,63),
+- ERASEINFO(0x02000,8)
++ .mfr_id = MANUFACTURER_ST, /* FIXME - CFI device? */
++ .dev_id = M29W800DB,
++ .name = "ST M29W800DB",
++ .uaddr = {
++ [0] = MTD_UADDR_0x5555_0x2AAA, /* x8 */
++ [1] = MTD_UADDR_0x5555_0x2AAA /* x16 */
++ },
++ .DevSize = SIZE_1MiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 4,
++ .regions = {
++ ERASEINFO(0x04000,1),
++ ERASEINFO(0x02000,2),
++ ERASEINFO(0x08000,1),
++ ERASEINFO(0x10000,15)
+ }
+ }, {
+- mfr_id: MANUFACTURER_AMD,
+- dev_id: AM29F017D,
+- name: "AMD AM29F017D",
+- DevSize: SIZE_2MiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 1,
+- regions: {ERASEINFO(0x10000,32),
+- }
+- }, {
+- mfr_id: MANUFACTURER_AMD,
+- dev_id: AM29F016,
+- name: "AMD AM29F016",
+- DevSize: SIZE_2MiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 1,
+- regions: {ERASEINFO(0x10000,32),
+- }
+- }, {
+- mfr_id: MANUFACTURER_AMD,
+- dev_id: AM29F080,
+- name: "AMD AM29F080",
+- DevSize: SIZE_1MiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 1,
+- regions: {ERASEINFO(0x10000,16),
+- }
+- }, {
+- mfr_id: MANUFACTURER_AMD,
+- dev_id: AM29F040,
+- name: "AMD AM29F040",
+- DevSize: SIZE_512KiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 1,
+- regions: {ERASEINFO(0x10000,8),
+- }
+- }, {
+- mfr_id: MANUFACTURER_AMD,
+- dev_id: AM29LV040B,
+- name: "AMD AM29LV040B",
+- DevSize: SIZE_512KiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 1,
+- regions: {ERASEINFO(0x10000,8),
+- }
+- }, {
+- mfr_id: MANUFACTURER_ST,
+- dev_id: M29W040B,
+- name: "ST M29W040B",
+- DevSize: SIZE_512KiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 1,
+- regions: {ERASEINFO(0x10000,8),
+- }
+- }, {
+- mfr_id: MANUFACTURER_MACRONIX,
+- dev_id: MX29LV160T,
+- name: "MXIC MX29LV160T",
+- DevSize: SIZE_2MiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 4,
+- regions: {ERASEINFO(0x10000,31),
++ .mfr_id = MANUFACTURER_ST, /* FIXME - CFI device? */
++ .dev_id = M29W160DT,
++ .name = "ST M29W160DT",
++ .uaddr = {
++ [0] = MTD_UADDR_0x0555_0x02AA, /* x8 */
++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */
++ },
++ .DevSize = SIZE_2MiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 4,
++ .regions = {
++ ERASEINFO(0x10000,31),
+ ERASEINFO(0x08000,1),
+ ERASEINFO(0x02000,2),
+ ERASEINFO(0x04000,1)
+ }
+ }, {
+- mfr_id: MANUFACTURER_MACRONIX,
+- dev_id: MX29LV160B,
+- name: "MXIC MX29LV160B",
+- DevSize: SIZE_2MiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 4,
+- regions: {ERASEINFO(0x04000,1),
++ .mfr_id = MANUFACTURER_ST, /* FIXME - CFI device? */
++ .dev_id = M29W160DB,
++ .name = "ST M29W160DB",
++ .uaddr = {
++ [0] = MTD_UADDR_0x0555_0x02AA, /* x8 */
++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */
++ },
++ .DevSize = SIZE_2MiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 4,
++ .regions = {
++ ERASEINFO(0x04000,1),
+ ERASEINFO(0x02000,2),
+ ERASEINFO(0x08000,1),
+ ERASEINFO(0x10000,31)
+ }
+ }, {
+- mfr_id: MANUFACTURER_MACRONIX,
+- dev_id: MX29F016,
+- name: "Macronix MX29F016",
+- DevSize: SIZE_2MiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 1,
+- regions: {ERASEINFO(0x10000,32),
+- }
+- }, {
+- mfr_id: MANUFACTURER_MACRONIX,
+- dev_id: MX29F004T,
+- name: "Macronix MX29F004T",
+- DevSize: SIZE_512KiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 4,
+- regions: {ERASEINFO(0x10000,7),
++ .mfr_id = MANUFACTURER_ST,
++ .dev_id = M29W040B,
++ .name = "ST M29W040B",
++ .uaddr = {
++ [0] = MTD_UADDR_0x0555_0x02AA /* x8 */
++ },
++ .DevSize = SIZE_512KiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 1,
++ .regions = {
++ ERASEINFO(0x10000,8),
++ }
++ }, {
++ .mfr_id = MANUFACTURER_TOSHIBA,
++ .dev_id = TC58FVT160,
++ .name = "Toshiba TC58FVT160",
++ .uaddr = {
++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */
++ [1] = MTD_UADDR_0x0555_0x02AA /* x16 */
++ },
++ .DevSize = SIZE_2MiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 4,
++ .regions = {
++ ERASEINFO(0x10000,31),
+ ERASEINFO(0x08000,1),
+ ERASEINFO(0x02000,2),
+- ERASEINFO(0x04000,1),
++ ERASEINFO(0x04000,1)
+ }
+ }, {
+- mfr_id: MANUFACTURER_MACRONIX,
+- dev_id: MX29F004B,
+- name: "Macronix MX29F004B",
+- DevSize: SIZE_512KiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 4,
+- regions: {ERASEINFO(0x04000,1),
++ .mfr_id = MANUFACTURER_TOSHIBA,
++ .dev_id = TC58FVB160,
++ .name = "Toshiba TC58FVB160",
++ .uaddr = {
++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */
++ [1] = MTD_UADDR_0x0555_0x02AA /* x16 */
++ },
++ .DevSize = SIZE_2MiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 4,
++ .regions = {
++ ERASEINFO(0x04000,1),
+ ERASEINFO(0x02000,2),
+ ERASEINFO(0x08000,1),
+- ERASEINFO(0x10000,7),
++ ERASEINFO(0x10000,31)
++ }
++ }, {
++ .mfr_id = MANUFACTURER_TOSHIBA,
++ .dev_id = TC58FVB321,
++ .name = "Toshiba TC58FVB321",
++ .uaddr = {
++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */
++ [1] = MTD_UADDR_0x0555_0x02AA /* x16 */
++ },
++ .DevSize = SIZE_4MiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 2,
++ .regions = {
++ ERASEINFO(0x02000,8),
++ ERASEINFO(0x10000,63)
++ }
++ }, {
++ .mfr_id = MANUFACTURER_TOSHIBA,
++ .dev_id = TC58FVT321,
++ .name = "Toshiba TC58FVT321",
++ .uaddr = {
++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */
++ [1] = MTD_UADDR_0x0555_0x02AA /* x16 */
++ },
++ .DevSize = SIZE_4MiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 2,
++ .regions = {
++ ERASEINFO(0x10000,63),
++ ERASEINFO(0x02000,8)
++ }
++ }, {
++ .mfr_id = MANUFACTURER_TOSHIBA,
++ .dev_id = TC58FVB641,
++ .name = "Toshiba TC58FVB641",
++ .uaddr = {
++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */
++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */
++ },
++ .DevSize = SIZE_8MiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 2,
++ .regions = {
++ ERASEINFO(0x02000,8),
++ ERASEINFO(0x10000,127)
++ }
++ }, {
++ .mfr_id = MANUFACTURER_TOSHIBA,
++ .dev_id = TC58FVT641,
++ .name = "Toshiba TC58FVT641",
++ .uaddr = {
++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */
++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */
++ },
++ .DevSize = SIZE_8MiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 2,
++ .regions = {
++ ERASEINFO(0x10000,127),
++ ERASEINFO(0x02000,8)
+ }
+ }, {
+- mfr_id: MANUFACTURER_SST,
+- dev_id: SST39LF512,
+- name: "SST 39LF512",
+- DevSize: SIZE_64KiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 1,
+- regions: {ERASEINFO(0x01000,16),
+- }
+- }, {
+- mfr_id: MANUFACTURER_SST,
+- dev_id: SST39LF010,
+- name: "SST 39LF010",
+- DevSize: SIZE_128KiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 1,
+- regions: {ERASEINFO(0x01000,32),
+- }
+- }, {
+- mfr_id: MANUFACTURER_SST,
+- dev_id: SST39LF020,
+- name: "SST 39LF020",
+- DevSize: SIZE_256KiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 1,
+- regions: {ERASEINFO(0x01000,64),
+- }
+- }, {
+- mfr_id: MANUFACTURER_SST,
+- dev_id: SST39LF040,
+- name: "SST 39LF040",
+- DevSize: SIZE_512KiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 1,
+- regions: {ERASEINFO(0x01000,128),
+- }
+- }, {
+- mfr_id: MANUFACTURER_SST,
+- dev_id: SST39SF010A,
+- name: "SST 39SF010A",
+- DevSize: SIZE_128KiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 1,
+- regions: {ERASEINFO(0x01000,32),
+- }
+- }, {
+- mfr_id: MANUFACTURER_SST,
+- dev_id: SST39SF020A,
+- name: "SST 39SF020A",
+- DevSize: SIZE_256KiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 1,
+- regions: {ERASEINFO(0x01000,64),
+- }
+- }, {
+- mfr_id: MANUFACTURER_SST,
+- dev_id: SST49LF030A,
+- name: "SST 49LF030A",
+- DevSize: SIZE_512KiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 1,
+- regions: {ERASEINFO(0x01000,96),
+- }
+- }, {
+- mfr_id: MANUFACTURER_SST,
+- dev_id: SST49LF040A,
+- name: "SST 49LF040A",
+- DevSize: SIZE_512KiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 1,
+- regions: {ERASEINFO(0x01000,128),
+- }
+- }, {
+- mfr_id: MANUFACTURER_SST,
+- dev_id: SST49LF080A,
+- name: "SST 49LF080A",
+- DevSize: SIZE_1MiB,
+- CmdSet: P_ID_AMD_STD,
+- NumEraseRegions: 1,
+- regions: {ERASEINFO(0x01000,256),
++ .mfr_id = MANUFACTURER_WINBOND,
++ .dev_id = W49V002A,
++ .name = "Winbond W49V002A",
++ .uaddr = {
++ [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */
++ },
++ .DevSize = SIZE_256KiB,
++ .CmdSet = P_ID_AMD_STD,
++ .NumEraseRegions= 4,
++ .regions = {
++ ERASEINFO(0x10000, 3),
++ ERASEINFO(0x08000, 1),
++ ERASEINFO(0x02000, 2),
++ ERASEINFO(0x04000, 1),
+ }
+ }
+ };
+@@ -907,7 +1452,7 @@
+ static int cfi_jedec_setup(struct cfi_private *p_cfi, int index);
+
+ static int jedec_probe_chip(struct map_info *map, __u32 base,
+- struct flchip *chips, struct cfi_private *cfi);
++ unsigned long *chip_map, struct cfi_private *cfi);
+
+ struct mtd_info *jedec_probe(struct map_info *map);
+
+@@ -944,11 +1489,43 @@
+ * this should be safe.
+ */
+ cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL);
++ /* FIXME - should have reset delay before continuing */
++}
+
++
++static inline __u8 finfo_uaddr(const struct amd_flash_info *finfo, int device_type)
++{
++ int uaddr_idx;
++ __u8 uaddr = MTD_UADDR_NOT_SUPPORTED;
++
++ switch ( device_type ) {
++ case CFI_DEVICETYPE_X8: uaddr_idx = 0; break;
++ case CFI_DEVICETYPE_X16: uaddr_idx = 1; break;
++ case CFI_DEVICETYPE_X32: uaddr_idx = 2; break;
++ default:
++ printk(KERN_NOTICE "MTD: %s(): unknown device_type %d\n",
++ __func__, device_type);
++ goto uaddr_done;
++ }
++
++ uaddr = finfo->uaddr[uaddr_idx];
++
++ if (uaddr != MTD_UADDR_NOT_SUPPORTED ) {
++ /* ASSERT("The unlock addresses for non-8-bit mode
++ are bollocks. We don't really need an array."); */
++ uaddr = finfo->uaddr[0];
++ }
++
++ uaddr_done:
++ return uaddr;
+ }
++
++
+ static int cfi_jedec_setup(struct cfi_private *p_cfi, int index)
+ {
+ int i,num_erase_regions;
++ unsigned long mask;
++ __u8 uaddr;
+
+ printk("Found: %s\n",jedec_table[index].name);
+
+@@ -971,41 +1548,170 @@
+ p_cfi->cfiq->EraseRegionInfo[i] = jedec_table[index].regions[i];
+ }
+ p_cfi->cmdset_priv = 0;
++
++ /* This may be redundant for some cases, but it doesn't hurt */
++ p_cfi->mfr = jedec_table[index].mfr_id;
++ p_cfi->id = jedec_table[index].dev_id;
++
++ uaddr = finfo_uaddr(&jedec_table[index], p_cfi->device_type);
++ if ( uaddr == MTD_UADDR_NOT_SUPPORTED ) {
++ kfree( p_cfi->cfiq );
++ return 0;
++ }
++
++ /* Mask out address bits which are smaller than the device type */
++ mask = ~(p_cfi->device_type-1);
++ p_cfi->addr_unlock1 = unlock_addrs[uaddr].addr1 & mask;
++ p_cfi->addr_unlock2 = unlock_addrs[uaddr].addr2 & mask;
++
+ return 1; /* ok */
+ }
+
+-static int jedec_probe_chip(struct map_info *map, __u32 base,
+- struct flchip *chips, struct cfi_private *cfi)
++
++/*
++ * There is a BIG problem properly ID'ing the JEDEC devic and guaranteeing
++ * the mapped address, unlock addresses, and proper chip ID. This function
++ * attempts to minimize errors. It is doubtfull that this probe will ever
++ * be perfect - consequently there should be some module parameters that
++ * could be manually specified to force the chip info.
++ */
++static inline int jedec_match( __u32 base,
++ struct map_info *map,
++ struct cfi_private *cfi,
++ const struct amd_flash_info *finfo )
+ {
+- int i;
+- int unlockpass = 0;
++ int rc = 0; /* failure until all tests pass */
++ u32 mfr, id;
++ __u8 uaddr;
++ unsigned long mask;
+
+- if (!cfi->numchips) {
++ /*
++ * The IDs must match. For X16 and X32 devices operating in
++ * a lower width ( X8 or X16 ), the device ID's are usually just
++ * the lower byte(s) of the larger device ID for wider mode. If
++ * a part is found that doesn't fit this assumption (device id for
++ * smaller width mode is completely unrealated to full-width mode)
++ * then the jedec_table[] will have to be augmented with the IDs
++ * for different widths.
++ */
+ switch (cfi->device_type) {
+ case CFI_DEVICETYPE_X8:
+- cfi->addr_unlock1 = 0x555;
+- cfi->addr_unlock2 = 0x2aa;
++ mfr = (__u8)finfo->mfr_id;
++ id = (__u8)finfo->dev_id;
+ break;
+ case CFI_DEVICETYPE_X16:
+- cfi->addr_unlock1 = 0xaaa;
+- if (map->buswidth == cfi->interleave) {
+- /* X16 chip(s) in X8 mode */
+- cfi->addr_unlock2 = 0x555;
+- } else {
+- cfi->addr_unlock2 = 0x554;
+- }
++ mfr = (__u16)finfo->mfr_id;
++ id = (__u16)finfo->dev_id;
+ break;
+ case CFI_DEVICETYPE_X32:
+- cfi->addr_unlock1 = 0x1555;
+- cfi->addr_unlock2 = 0xaaa;
++ mfr = (__u16)finfo->mfr_id;
++ id = (__u32)finfo->dev_id;
+ break;
+ default:
+- printk(KERN_NOTICE "Eep. Unknown jedec_probe device type %d\n", cfi->device_type);
+- return 0;
++ printk(KERN_WARNING
++ "MTD %s(): Unsupported device type %d\n",
++ __func__, cfi->device_type);
++ goto match_done;
++ }
++ if ( cfi->mfr != mfr || cfi->id != id ) {
++ goto match_done;
++ }
++
++ /* the part size must fit in the memory window */
++ DEBUG( MTD_DEBUG_LEVEL3,
++ "MTD %s(): Check fit 0x%.8x + 0x%.8x = 0x%.8x\n",
++ __func__, base, 1 << finfo->DevSize, base + (1 << finfo->DevSize) );
++ if ( base + cfi->interleave * ( 1 << finfo->DevSize ) > map->size ) {
++ DEBUG( MTD_DEBUG_LEVEL3,
++ "MTD %s(): 0x%.4x 0x%.4x %dKiB doesn't fit\n",
++ __func__, finfo->mfr_id, finfo->dev_id,
++ 1 << finfo->DevSize );
++ goto match_done;
++ }
++
++ uaddr = finfo_uaddr(finfo, cfi->device_type);
++ if ( uaddr == MTD_UADDR_NOT_SUPPORTED ) {
++ goto match_done;
++ }
++
++ mask = ~(cfi->device_type-1);
++
++ DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): check unlock addrs 0x%.4x 0x%.4x\n",
++ __func__, cfi->addr_unlock1, cfi->addr_unlock2 );
++ if ( MTD_UADDR_UNNECESSARY != uaddr && MTD_UADDR_DONT_CARE != uaddr
++ && ( (unlock_addrs[uaddr].addr1 & mask) != cfi->addr_unlock1 ||
++ (unlock_addrs[uaddr].addr2 & mask) != cfi->addr_unlock2 ) ) {
++ DEBUG( MTD_DEBUG_LEVEL3,
++ "MTD %s(): 0x%.4x 0x%.4x did not match\n",
++ __func__,
++ unlock_addrs[uaddr].addr1 & mask,
++ unlock_addrs[uaddr].addr2 & mask);
++ goto match_done;
+ }
++
++ /*
++ * Make sure the ID's dissappear when the device is taken out of
++ * ID mode. The only time this should fail when it should succeed
++ * is when the ID's are written as data to the same
++ * addresses. For this rare and unfortunate case the chip
++ * cannot be probed correctly.
++ * FIXME - write a driver that takes all of the chip info as
++ * module parameters, doesn't probe but forces a load.
++ */
++ DEBUG( MTD_DEBUG_LEVEL3,
++ "MTD %s(): check ID's disappear when not in ID mode\n",
++ __func__ );
++ jedec_reset( base, map, cfi );
++ mfr = jedec_read_mfr( map, base, cfi );
++ id = jedec_read_id( map, base, cfi );
++ if ( mfr == cfi->mfr && id == cfi->id ) {
++ DEBUG( MTD_DEBUG_LEVEL3,
++ "MTD %s(): ID 0x%.2x:0x%.2x did not change after reset:\n"
++ "You might need to manually specify JEDEC parameters.\n",
++ __func__, cfi->mfr, cfi->id );
++ goto match_done;
++ }
++
++ /* all tests passed - mark as success */
++ rc = 1;
++
++ /*
++ * Put the device back in ID mode - only need to do this if we
++ * were truly frobbing a real device.
++ */
++ DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): return to ID mode\n", __func__ );
++ if(cfi->addr_unlock1) {
++ cfi_send_gen_cmd(0xaa, cfi->addr_unlock1, base, map, cfi, CFI_DEVICETYPE_X8, NULL);
++ cfi_send_gen_cmd(0x55, cfi->addr_unlock2, base, map, cfi, CFI_DEVICETYPE_X8, NULL);
+ }
++ cfi_send_gen_cmd(0x90, cfi->addr_unlock1, base, map, cfi, CFI_DEVICETYPE_X8, NULL);
++ /* FIXME - should have a delay before continuing */
++
++ match_done:
++ return rc;
++}
++
++
++static int jedec_probe_chip(struct map_info *map, __u32 base,
++ unsigned long *chip_map, struct cfi_private *cfi)
++{
++ int i;
++ enum uaddr uaddr_idx = MTD_UADDR_NOT_SUPPORTED;
+
+ retry:
++ if (!cfi->numchips) {
++ unsigned long mask = ~(cfi->device_type-1);
++
++ uaddr_idx++;
++
++ if (MTD_UADDR_UNNECESSARY == uaddr_idx)
++ return 0;
++
++ /* Mask out address bits which are smaller than the device type */
++ cfi->addr_unlock1 = unlock_addrs[uaddr_idx].addr1 & mask;
++ cfi->addr_unlock2 = unlock_addrs[uaddr_idx].addr2 & mask;
++ }
++
+ /* Make certain we aren't probing past the end of map */
+ if (base >= map->size) {
+ printk(KERN_NOTICE
+@@ -1038,6 +1744,7 @@
+ cfi_send_gen_cmd(0x55, cfi->addr_unlock2, base, map, cfi, CFI_DEVICETYPE_X8, NULL);
+ }
+ cfi_send_gen_cmd(0x90, cfi->addr_unlock1, base, map, cfi, CFI_DEVICETYPE_X8, NULL);
++ /* FIXME - should have a delay before continuing */
+
+ if (!cfi->numchips) {
+ /* This is the first time we're called. Set up the CFI
+@@ -1045,26 +1752,21 @@
+
+ cfi->mfr = jedec_read_mfr(map, base, cfi);
+ cfi->id = jedec_read_id(map, base, cfi);
+- printk(KERN_INFO "Search for id:(%02x %02x) interleave(%d) type(%d)\n",
++ DEBUG(MTD_DEBUG_LEVEL3,
++ "Search for id:(%02x %02x) interleave(%d) type(%d)\n",
+ cfi->mfr, cfi->id, cfi->interleave, cfi->device_type);
+ for (i=0; i<sizeof(jedec_table)/sizeof(jedec_table[0]); i++) {
+- if (cfi->mfr == jedec_table[i].mfr_id &&
+- cfi->id == jedec_table[i].dev_id) {
++ if ( jedec_match( base, map, cfi, &jedec_table[i] ) ) {
++ DEBUG( MTD_DEBUG_LEVEL3,
++ "MTD %s(): matched device 0x%x,0x%x unlock_addrs: 0x%.4x 0x%.4x\n",
++ __func__, cfi->mfr, cfi->id,
++ cfi->addr_unlock1, cfi->addr_unlock2 );
+ if (!cfi_jedec_setup(cfi, i))
+ return 0;
+ goto ok_out;
+ }
+ }
+- switch(unlockpass++) {
+- case 0:
+- cfi->addr_unlock1 |= cfi->addr_unlock1 << 4;
+- cfi->addr_unlock2 |= cfi->addr_unlock2 << 4;
+- goto retry;
+- case 1:
+- cfi->addr_unlock1 = cfi->addr_unlock2 = 0;
+ goto retry;
+- }
+- return 0;
+ } else {
+ __u16 mfr;
+ __u16 id;
+@@ -1081,21 +1783,24 @@
+ }
+ }
+
+- /* Check each previous chip to see if it's an alias */
+- for (i=0; i<cfi->numchips; i++) {
+- /* This chip should be in read mode if it's one
+- we've already touched. */
+- if (jedec_read_mfr(map, chips[i].start, cfi) == cfi->mfr &&
+- jedec_read_id(map, chips[i].start, cfi) == cfi->id) {
++ /* Check each previous chip locations to see if it's an alias */
++ for (i=0; i < (base >> cfi->chipshift); i++) {
++ unsigned long start;
++ if(!test_bit(i, chip_map)) {
++ continue; /* Skip location; no valid chip at this address */
++ }
++ start = i << cfi->chipshift;
++ if (jedec_read_mfr(map, start, cfi) == cfi->mfr &&
++ jedec_read_id(map, start, cfi) == cfi->id) {
+ /* Eep. This chip also looks like it's in autoselect mode.
+ Is it an alias for the new one? */
+- jedec_reset(chips[i].start, map, cfi);
++ jedec_reset(start, map, cfi);
+
+ /* If the device IDs go away, it's an alias */
+ if (jedec_read_mfr(map, base, cfi) != cfi->mfr ||
+ jedec_read_id(map, base, cfi) != cfi->id) {
+ printk(KERN_DEBUG "%s: Found an alias at 0x%x for the chip at 0x%lx\n",
+- map->name, base, chips[i].start);
++ map->name, base, start);
+ return 0;
+ }
+
+@@ -1107,7 +1812,7 @@
+ if (jedec_read_mfr(map, base, cfi) == cfi->mfr &&
+ jedec_read_id(map, base, cfi) == cfi->id) {
+ printk(KERN_DEBUG "%s: Found an alias at 0x%x for the chip at 0x%lx\n",
+- map->name, base, chips[i].start);
++ map->name, base, start);
+ return 0;
+ }
+ }
+@@ -1115,13 +1820,7 @@
+
+ /* OK, if we got to here, then none of the previous chips appear to
+ be aliases for the current one. */
+- if (cfi->numchips == MAX_CFI_CHIPS) {
+- printk(KERN_WARNING"%s: Too many flash chips detected. Increase MAX_CFI_CHIPS from %d.\n", map->name, MAX_CFI_CHIPS);
+- /* Doesn't matter about resetting it to Read Mode - we're not going to talk to it anyway */
+- return -1;
+- }
+- chips[cfi->numchips].start = base;
+- chips[cfi->numchips].state = FL_READY;
++ set_bit((base >> cfi->chipshift), chip_map); /* Update chip map */
+ cfi->numchips++;
+
+ ok_out:
+@@ -1136,8 +1835,8 @@
+ }
+
+ static struct chip_probe jedec_chip_probe = {
+- name: "JEDEC",
+- probe_chip: jedec_probe_chip
++ .name = "JEDEC",
++ .probe_chip = jedec_probe_chip
+ };
+
+ struct mtd_info *jedec_probe(struct map_info *map)
+@@ -1150,9 +1849,9 @@
+ }
+
+ static struct mtd_chip_driver jedec_chipdrv = {
+- probe: jedec_probe,
+- name: "jedec_probe",
+- module: THIS_MODULE
++ .probe = jedec_probe,
++ .name = "jedec_probe",
++ .module = THIS_MODULE
+ };
+
+ int __init jedec_probe_init(void)
+diff -Nurb linux-mips-2.4.27/drivers/mtd/chips/map_absent.c linux/drivers/mtd/chips/map_absent.c
+--- linux-mips-2.4.27/drivers/mtd/chips/map_absent.c 2001-11-05 21:15:51.000000000 +0100
++++ linux/drivers/mtd/chips/map_absent.c 2004-11-19 10:25:11.776218488 +0100
+@@ -1,7 +1,7 @@
+ /*
+ * Common code to handle absent "placeholder" devices
+ * Copyright 2001 Resilience Corporation <ebrower@resilience.com>
+- * $Id$
++ * $Id$
+ *
+ * This map driver is used to allocate "placeholder" MTD
+ * devices on systems that have socketed/removable media.
+@@ -23,9 +23,10 @@
+ #include <linux/kernel.h>
+ #include <linux/errno.h>
+ #include <linux/slab.h>
+-
++#include <linux/init.h>
++#include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+-
++#include <linux/mtd/compatmac.h>
+
+ static int map_absent_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
+ static int map_absent_write (struct mtd_info *, loff_t, size_t, size_t *, const u_char *);
+@@ -36,10 +37,10 @@
+
+
+ static struct mtd_chip_driver map_absent_chipdrv = {
+- probe: map_absent_probe,
+- destroy: map_absent_destroy,
+- name: "map_absent",
+- module: THIS_MODULE
++ .probe = map_absent_probe,
++ .destroy = map_absent_destroy,
++ .name = "map_absent",
++ .module = THIS_MODULE
+ };
+
+ static struct mtd_info *map_absent_probe(struct map_info *map)
+@@ -65,7 +66,7 @@
+ mtd->flags = 0;
+ mtd->erasesize = PAGE_SIZE;
+
+- MOD_INC_USE_COUNT;
++ __module_get(THIS_MODULE);
+ return mtd;
+ }
+
+diff -Nurb linux-mips-2.4.27/drivers/mtd/chips/map_ram.c linux/drivers/mtd/chips/map_ram.c
+--- linux-mips-2.4.27/drivers/mtd/chips/map_ram.c 2001-11-05 21:15:51.000000000 +0100
++++ linux/drivers/mtd/chips/map_ram.c 2004-11-19 10:25:11.777218336 +0100
+@@ -1,7 +1,7 @@
+ /*
+ * Common code to handle map devices which are simple RAM
+ * (C) 2000 Red Hat. GPL'd.
+- * $Id$
++ * $Id$
+ */
+
+ #include <linux/module.h>
+@@ -11,8 +11,10 @@
+ #include <asm/byteorder.h>
+ #include <linux/errno.h>
+ #include <linux/slab.h>
+-
++#include <linux/init.h>
++#include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
++#include <linux/mtd/compatmac.h>
+
+
+ static int mapram_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
+@@ -23,9 +25,9 @@
+
+
+ static struct mtd_chip_driver mapram_chipdrv = {
+- probe: map_ram_probe,
+- name: "map_ram",
+- module: THIS_MODULE
++ .probe = map_ram_probe,
++ .name = "map_ram",
++ .module = THIS_MODULE
+ };
+
+ static struct mtd_info *map_ram_probe(struct map_info *map)
+@@ -34,21 +36,21 @@
+
+ /* Check the first byte is RAM */
+ #if 0
+- map->write8(map, 0x55, 0);
+- if (map->read8(map, 0) != 0x55)
++ map_write8(map, 0x55, 0);
++ if (map_read8(map, 0) != 0x55)
+ return NULL;
+
+- map->write8(map, 0xAA, 0);
+- if (map->read8(map, 0) != 0xAA)
++ map_write8(map, 0xAA, 0);
++ if (map_read8(map, 0) != 0xAA)
+ return NULL;
+
+ /* Check the last byte is RAM */
+- map->write8(map, 0x55, map->size-1);
+- if (map->read8(map, map->size-1) != 0x55)
++ map_write8(map, 0x55, map->size-1);
++ if (map_read8(map, map->size-1) != 0x55)
+ return NULL;
+
+- map->write8(map, 0xAA, map->size-1);
+- if (map->read8(map, map->size-1) != 0xAA)
++ map_write8(map, 0xAA, map->size-1);
++ if (map_read8(map, map->size-1) != 0xAA)
+ return NULL;
+ #endif
+ /* OK. It seems to be RAM. */
+@@ -74,7 +76,7 @@
+ while(mtd->size & (mtd->erasesize - 1))
+ mtd->erasesize >>= 1;
+
+- MOD_INC_USE_COUNT;
++ __module_get(THIS_MODULE);
+ return mtd;
+ }
+
+@@ -83,7 +85,7 @@
+ {
+ struct map_info *map = (struct map_info *)mtd->priv;
+
+- map->copy_from(map, buf, from, len);
++ map_copy_from(map, buf, from, len);
+ *retlen = len;
+ return 0;
+ }
+@@ -92,7 +94,7 @@
+ {
+ struct map_info *map = (struct map_info *)mtd->priv;
+
+- map->copy_to(map, to, buf, len);
++ map_copy_to(map, to, buf, len);
+ *retlen = len;
+ return 0;
+ }
+@@ -105,7 +107,7 @@
+ unsigned long i;
+
+ for (i=0; i<instr->len; i++)
+- map->write8(map, 0xFF, instr->addr + i);
++ map_write8(map, 0xFF, instr->addr + i);
+
+ if (instr->callback)
+ instr->callback(instr);
+diff -Nurb linux-mips-2.4.27/drivers/mtd/chips/map_rom.c linux/drivers/mtd/chips/map_rom.c
+--- linux-mips-2.4.27/drivers/mtd/chips/map_rom.c 2001-11-05 21:15:52.000000000 +0100
++++ linux/drivers/mtd/chips/map_rom.c 2004-11-19 10:25:11.778218184 +0100
+@@ -1,7 +1,7 @@
+ /*
+ * Common code to handle map devices which are simple ROM
+ * (C) 2000 Red Hat. GPL'd.
+- * $Id$
++ * $Id$
+ */
+
+ #include <linux/version.h>
+@@ -12,8 +12,10 @@
+ #include <asm/byteorder.h>
+ #include <linux/errno.h>
+ #include <linux/slab.h>
+-
++#include <linux/init.h>
++#include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
++#include <linux/mtd/compatmac.h>
+
+ static int maprom_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
+ static int maprom_write (struct mtd_info *, loff_t, size_t, size_t *, const u_char *);
+@@ -21,9 +23,9 @@
+ struct mtd_info *map_rom_probe(struct map_info *map);
+
+ static struct mtd_chip_driver maprom_chipdrv = {
+- probe: map_rom_probe,
+- name: "map_rom",
+- module: THIS_MODULE
++ .probe = map_rom_probe,
++ .name = "map_rom",
++ .module = THIS_MODULE
+ };
+
+ struct mtd_info *map_rom_probe(struct map_info *map)
+@@ -49,7 +51,7 @@
+ while(mtd->size & (mtd->erasesize - 1))
+ mtd->erasesize >>= 1;
+
+- MOD_INC_USE_COUNT;
++ __module_get(THIS_MODULE);
+ return mtd;
+ }
+
+@@ -58,7 +60,7 @@
+ {
+ struct map_info *map = (struct map_info *)mtd->priv;
+
+- map->copy_from(map, buf, from, len);
++ map_copy_from(map, buf, from, len);
+ *retlen = len;
+ return 0;
+ }
+diff -Nurb linux-mips-2.4.27/drivers/mtd/chips/sharp.c linux/drivers/mtd/chips/sharp.c
+--- linux-mips-2.4.27/drivers/mtd/chips/sharp.c 2003-02-26 01:53:49.000000000 +0100
++++ linux/drivers/mtd/chips/sharp.c 2004-11-19 10:25:11.792216056 +0100
+@@ -4,7 +4,7 @@
+ * Copyright 2000,2001 David A. Schleef <ds@schleef.org>
+ * 2000,2001 Lineo, Inc.
+ *
+- * $Id$
++ * $Id$
+ *
+ * Devices supported:
+ * LH28F016SCT Symmetrical block flash memory, 2Mx8
+@@ -28,6 +28,7 @@
+ #include <linux/errno.h>
+ #include <linux/interrupt.h>
+ #include <linux/mtd/map.h>
++#include <linux/mtd/mtd.h>
+ #include <linux/mtd/cfi.h>
+ #include <linux/delay.h>
+
+@@ -98,10 +99,10 @@
+ static void sharp_destroy(struct mtd_info *mtd);
+
+ static struct mtd_chip_driver sharp_chipdrv = {
+- probe: sharp_probe,
+- destroy: sharp_destroy,
+- name: "sharp",
+- module: THIS_MODULE
++ .probe = sharp_probe,
++ .destroy = sharp_destroy,
++ .name = "sharp",
++ .module = THIS_MODULE
+ };
+
+
+@@ -116,8 +117,10 @@
+ return NULL;
+
+ sharp = kmalloc(sizeof(*sharp), GFP_KERNEL);
+- if(!sharp)
++ if(!sharp) {
++ kfree(mtd);
+ return NULL;
++ }
+
+ memset(mtd, 0, sizeof(*mtd));
+
+@@ -163,12 +166,12 @@
+ u32 read0, read4;
+ int width = 4;
+
+- tmp = map->read32(map, base+0);
++ tmp = map_read32(map, base+0);
+
+- map->write32(map, CMD_READ_ID, base+0);
++ map_write32(map, CMD_READ_ID, base+0);
+
+- read0=map->read32(map, base+0);
+- read4=map->read32(map, base+4);
++ read0=map_read32(map, base+0);
++ read4=map_read32(map, base+4);
+ if(read0 == 0x89898989){
+ printk("Looks like sharp flash\n");
+ switch(read4){
+@@ -196,10 +199,10 @@
+ printk("Sort-of looks like sharp flash, 0x%08x 0x%08x\n",
+ read0,read4);
+ }
+- }else if((map->read32(map, base+0) == CMD_READ_ID)){
++ }else if((map_read32(map, base+0) == CMD_READ_ID)){
+ /* RAM, probably */
+ printk("Looks like RAM\n");
+- map->write32(map, tmp, base+0);
++ map_write32(map, tmp, base+0);
+ }else{
+ printk("Doesn't look like sharp flash, 0x%08x 0x%08x\n",
+ read0,read4);
+@@ -221,10 +224,10 @@
+
+ switch(chip->state){
+ case FL_READY:
+- map->write32(map,CMD_READ_STATUS,adr);
++ map_write32(map,CMD_READ_STATUS,adr);
+ chip->state = FL_STATUS;
+ case FL_STATUS:
+- status = map->read32(map,adr);
++ status = map_read32(map,adr);
+ //printk("status=%08x\n",status);
+
+ udelay(100);
+@@ -252,7 +255,7 @@
+ goto retry;
+ }
+
+- map->write32(map,CMD_RESET, adr);
++ map_write32(map,CMD_RESET, adr);
+
+ chip->state = FL_READY;
+
+@@ -293,7 +296,7 @@
+ if(ret<0)
+ break;
+
+- map->copy_from(map,buf,ofs,thislen);
++ map_copy_from(map,buf,ofs,thislen);
+
+ sharp_release(&sharp->chips[chipnum]);
+
+@@ -354,17 +357,17 @@
+ ret = sharp_wait(map,chip);
+
+ for(try=0;try<10;try++){
+- map->write32(map,CMD_BYTE_WRITE,adr);
++ map_write32(map,CMD_BYTE_WRITE,adr);
+ /* cpu_to_le32 -> hack to fix the writel be->le conversion */
+- map->write32(map,cpu_to_le32(datum),adr);
++ map_write32(map,cpu_to_le32(datum),adr);
+
+ chip->state = FL_WRITING;
+
+ timeo = jiffies + (HZ/2);
+
+- map->write32(map,CMD_READ_STATUS,adr);
++ map_write32(map,CMD_READ_STATUS,adr);
+ for(i=0;i<100;i++){
+- status = map->read32(map,adr);
++ status = map_read32(map,adr);
+ if((status & SR_READY)==SR_READY)
+ break;
+ }
+@@ -377,9 +380,9 @@
+
+ printk("sharp: error writing byte at addr=%08lx status=%08x\n",adr,status);
+
+- map->write32(map,CMD_CLEAR_STATUS,adr);
++ map_write32(map,CMD_CLEAR_STATUS,adr);
+ }
+- map->write32(map,CMD_RESET,adr);
++ map_write32(map,CMD_RESET,adr);
+ chip->state = FL_READY;
+
+ wake_up(&chip->wq);
+@@ -436,14 +439,14 @@
+ int status;
+ DECLARE_WAITQUEUE(wait, current);
+
+- map->write32(map,CMD_READ_STATUS,adr);
+- status = map->read32(map,adr);
++ map_write32(map,CMD_READ_STATUS,adr);
++ status = map_read32(map,adr);
+
+ timeo = jiffies + HZ;
+
+ while(time_before(jiffies, timeo)){
+- map->write32(map,CMD_READ_STATUS,adr);
+- status = map->read32(map,adr);
++ map_write32(map,CMD_READ_STATUS,adr);
++ status = map_read32(map,adr);
+ if((status & SR_READY)==SR_READY){
+ ret = 0;
+ goto out;
+@@ -485,26 +488,26 @@
+ sharp_unlock_oneblock(map,chip,adr);
+ #endif
+
+- map->write32(map,CMD_BLOCK_ERASE_1,adr);
+- map->write32(map,CMD_BLOCK_ERASE_2,adr);
++ map_write32(map,CMD_BLOCK_ERASE_1,adr);
++ map_write32(map,CMD_BLOCK_ERASE_2,adr);
+
+ chip->state = FL_ERASING;
+
+ ret = sharp_do_wait_for_ready(map,chip,adr);
+ if(ret<0)return ret;
+
+- map->write32(map,CMD_READ_STATUS,adr);
+- status = map->read32(map,adr);
++ map_write32(map,CMD_READ_STATUS,adr);
++ status = map_read32(map,adr);
+
+ if(!(status&SR_ERRORS)){
+- map->write32(map,CMD_RESET,adr);
++ map_write32(map,CMD_RESET,adr);
+ chip->state = FL_READY;
+ //spin_unlock_bh(chip->mutex);
+ return 0;
+ }
+
+ printk("sharp: error erasing block at addr=%08lx status=%08x\n",adr,status);
+- map->write32(map,CMD_CLEAR_STATUS,adr);
++ map_write32(map,CMD_CLEAR_STATUS,adr);
+
+ //spin_unlock_bh(chip->mutex);
+
+@@ -518,17 +521,17 @@
+ int i;
+ int status;
+
+- map->write32(map,CMD_CLEAR_BLOCK_LOCKS_1,adr);
+- map->write32(map,CMD_CLEAR_BLOCK_LOCKS_2,adr);
++ map_write32(map,CMD_CLEAR_BLOCK_LOCKS_1,adr);
++ map_write32(map,CMD_CLEAR_BLOCK_LOCKS_2,adr);
+
+ udelay(100);
+
+- status = map->read32(map,adr);
++ status = map_read32(map,adr);
+ printk("status=%08x\n",status);
+
+ for(i=0;i<1000;i++){
+- //map->write32(map,CMD_READ_STATUS,adr);
+- status = map->read32(map,adr);
++ //map_write32(map,CMD_READ_STATUS,adr);
++ status = map_read32(map,adr);
+ if((status & SR_READY)==SR_READY)
+ break;
+ udelay(100);
+@@ -538,13 +541,13 @@
+ }
+
+ if(!(status&SR_ERRORS)){
+- map->write32(map,CMD_RESET,adr);
++ map_write32(map,CMD_RESET,adr);
+ chip->state = FL_READY;
+ return;
+ }
+
+ printk("sharp: error unlocking block at addr=%08lx status=%08x\n",adr,status);
+- map->write32(map,CMD_CLEAR_STATUS,adr);
++ map_write32(map,CMD_CLEAR_STATUS,adr);
+ }
+ #endif
+
+diff -Nurb linux-mips-2.4.27/drivers/mtd/cmdlinepart.c linux/drivers/mtd/cmdlinepart.c
+--- linux-mips-2.4.27/drivers/mtd/cmdlinepart.c 2003-02-26 01:53:49.000000000 +0100
++++ linux/drivers/mtd/cmdlinepart.c 2004-11-19 10:25:11.628240984 +0100
+@@ -1,5 +1,5 @@
+ /*
+- * $Id$
++ * $Id$
+ *
+ * Read flash partition table from command line
+ *
+@@ -28,7 +28,7 @@
+
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/partitions.h>
+-#include <asm/setup.h>
++#include <linux/mtd/compatmac.h>
+ #include <linux/bootmem.h>
+
+ /* error message prefix */
+@@ -178,8 +178,7 @@
+ parts[this_part].mask_flags = mask_flags;
+ if (name)
+ {
+- strncpy(extra_mem, name, name_len);
+- extra_mem[name_len] = 0;
++ strlcpy(extra_mem, name, name_len + 1);
+ }
+ else
+ {
+@@ -258,8 +257,7 @@
+ this_mtd->parts = parts;
+ this_mtd->num_parts = num_parts;
+ this_mtd->mtd_id = (char*)(this_mtd + 1);
+- strncpy(this_mtd->mtd_id, mtd_id, mtd_id_len);
+- this_mtd->mtd_id[mtd_id_len] = 0;
++ strlcpy(this_mtd->mtd_id, mtd_id, mtd_id_len + 1);
+
+ /* link into chain */
+ this_mtd->next = partitions;
+@@ -291,13 +289,14 @@
+ * information. It returns partitions for the requested mtd device, or
+ * the first one in the chain if a NULL mtd_id is passed in.
+ */
+-int parse_cmdline_partitions(struct mtd_info *master,
++static int parse_cmdline_partitions(struct mtd_info *master,
+ struct mtd_partition **pparts,
+- const char *mtd_id)
++ unsigned long origin)
+ {
+ unsigned long offset;
+ int i;
+ struct cmdline_mtd_partition *part;
++ char *mtd_id = master->name;
+
+ if(!cmdline)
+ return -EINVAL;
+@@ -349,7 +348,25 @@
+
+ __setup("mtdparts=", mtdpart_setup);
+
+-EXPORT_SYMBOL(parse_cmdline_partitions);
++static struct mtd_part_parser cmdline_parser = {
++ .owner = THIS_MODULE,
++ .parse_fn = parse_cmdline_partitions,
++ .name = "cmdlinepart",
++};
++
++static int __init cmdline_parser_init(void)
++{
++ return register_mtd_parser(&cmdline_parser);
++}
++
++static void __exit cmdline_parser_exit(void)
++{
++ deregister_mtd_parser(&cmdline_parser);
++}
++
++module_init(cmdline_parser_init);
++module_exit(cmdline_parser_exit);
++
+
+ MODULE_LICENSE("GPL");
+ MODULE_AUTHOR("Marius Groeger <mag@sysgo.de>");
+diff -Nurb linux-mips-2.4.27/drivers/mtd/devices/Config.in linux/drivers/mtd/devices/Config.in
+--- linux-mips-2.4.27/drivers/mtd/devices/Config.in 2003-02-26 01:53:49.000000000 +0100
++++ linux/drivers/mtd/devices/Config.in 2004-11-19 10:25:11.811213168 +0100
+@@ -1,6 +1,6 @@
+-# drivers/mtd/maps/Config.in
++# drivers/mtd/devices/Config.in
+
+-# $Id$
++# $Id$
+
+ mainmenu_option next_comment
+
+@@ -28,13 +28,13 @@
+ dep_tristate ' MTD emulation using block device' CONFIG_MTD_BLKMTD $CONFIG_MTD
+
+ comment 'Disk-On-Chip Device Drivers'
+- dep_tristate ' M-Systems Disk-On-Chip 1000' CONFIG_MTD_DOC1000 $CONFIG_MTD
+ dep_tristate ' M-Systems Disk-On-Chip 2000 and Millennium' CONFIG_MTD_DOC2000 $CONFIG_MTD
+ dep_tristate ' M-Systems Disk-On-Chip Millennium-only alternative driver (see help)' CONFIG_MTD_DOC2001 $CONFIG_MTD
+- if [ "$CONFIG_MTD_DOC2001" = "y" -o "$CONFIG_MTD_DOC2000" = "y" ]; then
++ dep_tristate ' M-Systems Disk-On-Chip Millennium Plus driver (see help)' CONFIG_MTD_DOC2001PLUS $CONFIG_MTD
++ if [ "$CONFIG_MTD_DOC2001PLUS" = "y" -o "$CONFIG_MTD_DOC2001" = "y" -o "$CONFIG_MTD_DOC2000" = "y" ]; then
+ define_bool CONFIG_MTD_DOCPROBE y
+ else
+- if [ "$CONFIG_MTD_DOC2001" = "m" -o "$CONFIG_MTD_DOC2000" = "m" ]; then
++ if [ "$CONFIG_MTD_DOC2001PLUS" = "m" -o "$CONFIG_MTD_DOC2001" = "m" -o "$CONFIG_MTD_DOC2000" = "m" ]; then
+ define_bool CONFIG_MTD_DOCPROBE m
+ else
+ define_bool CONFIG_MTD_DOCPROBE n
+diff -Nurb linux-mips-2.4.27/drivers/mtd/devices/Makefile linux/drivers/mtd/devices/Makefile
+--- linux-mips-2.4.27/drivers/mtd/devices/Makefile 2002-03-30 09:15:50.000000000 +0100
++++ linux/drivers/mtd/devices/Makefile 2004-11-19 10:25:11.813212864 +0100
+@@ -1,9 +1,12 @@
+ #
+ # linux/drivers/devices/Makefile
+ #
+-# $Id$
++# $Id$
+
++ifeq ($(PATCHLEVEL),4)
+ O_TARGET := devlink.o
++export-objs := docecc.o
++endif
+
+ # *** BIG UGLY NOTE ***
+ #
+@@ -12,15 +15,16 @@
+ # here where previously there was none. We now have to ensure that
+ # doc200[01].o are linked before docprobe.o
+
+-obj-$(CONFIG_MTD_DOC1000) += doc1000.o
+ obj-$(CONFIG_MTD_DOC2000) += doc2000.o
+ obj-$(CONFIG_MTD_DOC2001) += doc2001.o
++obj-$(CONFIG_MTD_DOC2001PLUS) += doc2001plus.o
+ obj-$(CONFIG_MTD_DOCPROBE) += docprobe.o docecc.o
+ obj-$(CONFIG_MTD_SLRAM) += slram.o
++obj-$(CONFIG_MTD_PHRAM) += phram.o
+ obj-$(CONFIG_MTD_PMC551) += pmc551.o
+ obj-$(CONFIG_MTD_MS02NV) += ms02-nv.o
+ obj-$(CONFIG_MTD_MTDRAM) += mtdram.o
+ obj-$(CONFIG_MTD_LART) += lart.o
+ obj-$(CONFIG_MTD_BLKMTD) += blkmtd.o
+
+-include $(TOPDIR)/Rules.make
++-include $(TOPDIR)/Rules.make
+diff -Nurb linux-mips-2.4.27/drivers/mtd/devices/blkmtd-25.c linux/drivers/mtd/devices/blkmtd-25.c
+--- linux-mips-2.4.27/drivers/mtd/devices/blkmtd-25.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/mtd/devices/blkmtd-25.c 2004-11-19 10:25:11.814212712 +0100
+@@ -0,0 +1,827 @@
++/*
++ * $Id$
++ *
++ * blkmtd.c - use a block device as a fake MTD
++ *
++ * Author: Simon Evans <spse@secret.org.uk>
++ *
++ * Copyright (C) 2001,2002 Simon Evans
++ *
++ * Licence: GPL
++ *
++ * How it works:
++ * The driver uses raw/io to read/write the device and the page
++ * cache to cache access. Writes update the page cache with the
++ * new data and mark it dirty and add the page into a BIO which
++ * is then written out.
++ *
++ * It can be loaded Read-Only to prevent erases and writes to the
++ * medium.
++ *
++ */
++
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/fs.h>
++#include <linux/blkdev.h>
++#include <linux/bio.h>
++#include <linux/pagemap.h>
++#include <linux/list.h>
++#include <linux/init.h>
++#include <linux/mtd/mtd.h>
++
++
++#define err(format, arg...) printk(KERN_ERR "blkmtd: " format "\n" , ## arg)
++#define info(format, arg...) printk(KERN_INFO "blkmtd: " format "\n" , ## arg)
++#define warn(format, arg...) printk(KERN_WARNING "blkmtd: " format "\n" , ## arg)
++#define crit(format, arg...) printk(KERN_CRIT "blkmtd: " format "\n" , ## arg)
++
++
++/* Default erase size in K, always make it a multiple of PAGE_SIZE */
++#define CONFIG_MTD_BLKDEV_ERASESIZE (128 << 10) /* 128KiB */
++#define VERSION "$Revision$"
++
++/* Info for the block device */
++struct blkmtd_dev {
++ struct list_head list;
++ struct block_device *blkdev;
++ struct mtd_info mtd_info;
++ struct semaphore wrbuf_mutex;
++};
++
++
++/* Static info about the MTD, used in cleanup_module */
++static LIST_HEAD(blkmtd_device_list);
++
++
++static void blkmtd_sync(struct mtd_info *mtd);
++
++#define MAX_DEVICES 4
++
++/* Module parameters passed by insmod/modprobe */
++char *device[MAX_DEVICES]; /* the block device to use */
++int erasesz[MAX_DEVICES]; /* optional default erase size */
++int ro[MAX_DEVICES]; /* optional read only flag */
++int sync;
++
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Simon Evans <spse@secret.org.uk>");
++MODULE_DESCRIPTION("Emulate an MTD using a block device");
++MODULE_PARM(device, "1-4s");
++MODULE_PARM_DESC(device, "block device to use");
++MODULE_PARM(erasesz, "1-4i");
++MODULE_PARM_DESC(erasesz, "optional erase size to use in KiB. eg 4=4KiB.");
++MODULE_PARM(ro, "1-4i");
++MODULE_PARM_DESC(ro, "1=Read only, writes and erases cause errors");
++MODULE_PARM(sync, "i");
++MODULE_PARM_DESC(sync, "1=Synchronous writes");
++
++
++/* completion handler for BIO reads */
++static int bi_read_complete(struct bio *bio, unsigned int bytes_done, int error)
++{
++ if (bio->bi_size)
++ return 1;
++
++ complete((struct completion*)bio->bi_private);
++ return 0;
++}
++
++
++/* completion handler for BIO writes */
++static int bi_write_complete(struct bio *bio, unsigned int bytes_done, int error)
++{
++ const int uptodate = test_bit(BIO_UPTODATE, &bio->bi_flags);
++ struct bio_vec *bvec = bio->bi_io_vec + bio->bi_vcnt - 1;
++
++ if (bio->bi_size)
++ return 1;
++
++ if(!uptodate)
++ err("bi_write_complete: not uptodate\n");
++
++ do {
++ struct page *page = bvec->bv_page;
++ DEBUG(3, "Cleaning up page %ld\n", page->index);
++ if (--bvec >= bio->bi_io_vec)
++ prefetchw(&bvec->bv_page->flags);
++
++ if (uptodate) {
++ SetPageUptodate(page);
++ } else {
++ ClearPageUptodate(page);
++ SetPageError(page);
++ }
++ ClearPageDirty(page);
++ unlock_page(page);
++ page_cache_release(page);
++ } while (bvec >= bio->bi_io_vec);
++
++ complete((struct completion*)bio->bi_private);
++ return 0;
++}
++
++
++/* read one page from the block device */
++static int blkmtd_readpage(struct blkmtd_dev *dev, struct page *page)
++{
++ struct bio *bio;
++ struct completion event;
++ int err = -ENOMEM;
++
++ if(PageUptodate(page)) {
++ DEBUG(2, "blkmtd: readpage page %ld is already upto date\n", page->index);
++ unlock_page(page);
++ return 0;
++ }
++
++ ClearPageUptodate(page);
++ ClearPageError(page);
++
++ bio = bio_alloc(GFP_KERNEL, 1);
++ if(bio) {
++ init_completion(&event);
++ bio->bi_bdev = dev->blkdev;
++ bio->bi_sector = page->index << (PAGE_SHIFT-9);
++ bio->bi_private = &event;
++ bio->bi_end_io = bi_read_complete;
++ if(bio_add_page(bio, page, PAGE_SIZE, 0) == PAGE_SIZE) {
++ submit_bio(READ, bio);
++ blk_run_queues();
++ wait_for_completion(&event);
++ err = test_bit(BIO_UPTODATE, &bio->bi_flags) ? 0 : -EIO;
++ bio_put(bio);
++ }
++ }
++
++ if(err)
++ SetPageError(page);
++ else
++ SetPageUptodate(page);
++ flush_dcache_page(page);
++ unlock_page(page);
++ return err;
++}
++
++
++/* write out the current BIO and wait for it to finish */
++static int blkmtd_write_out(struct bio *bio)
++{
++ struct completion event;
++ int err;
++
++ if(!bio->bi_vcnt) {
++ bio_put(bio);
++ return 0;
++ }
++
++ init_completion(&event);
++ bio->bi_private = &event;
++ bio->bi_end_io = bi_write_complete;
++ submit_bio(WRITE, bio);
++ blk_run_queues();
++ wait_for_completion(&event);
++ DEBUG(3, "submit_bio completed, bi_vcnt = %d\n", bio->bi_vcnt);
++ err = test_bit(BIO_UPTODATE, &bio->bi_flags) ? 0 : -EIO;
++ bio_put(bio);
++ return err;
++}
++
++
++/**
++ * blkmtd_add_page - add a page to the current BIO
++ * @bio: bio to add to (NULL to alloc initial bio)
++ * @blkdev: block device
++ * @page: page to add
++ * @pagecnt: pages left to add
++ *
++ * Adds a page to the current bio, allocating it if necessary. If it cannot be
++ * added, the current bio is written out and a new one is allocated. Returns
++ * the new bio to add or NULL on error
++ */
++static struct bio *blkmtd_add_page(struct bio *bio, struct block_device *blkdev,
++ struct page *page, int pagecnt)
++{
++
++ retry:
++ if(!bio) {
++ bio = bio_alloc(GFP_KERNEL, pagecnt);
++ if(!bio)
++ return NULL;
++ bio->bi_sector = page->index << (PAGE_SHIFT-9);
++ bio->bi_bdev = blkdev;
++ }
++
++ if(bio_add_page(bio, page, PAGE_SIZE, 0) != PAGE_SIZE) {
++ blkmtd_write_out(bio);
++ bio = NULL;
++ goto retry;
++ }
++ return bio;
++}
++
++
++/**
++ * write_pages - write block of data to device via the page cache
++ * @dev: device to write to
++ * @buf: data source or NULL if erase (output is set to 0xff)
++ * @to: offset into output device
++ * @len: amount to data to write
++ * @retlen: amount of data written
++ *
++ * Grab pages from the page cache and fill them with the source data.
++ * Non page aligned start and end result in a readin of the page and
++ * part of the page being modified. Pages are added to the bio and then written
++ * out.
++ */
++static int write_pages(struct blkmtd_dev *dev, const u_char *buf, loff_t to,
++ size_t len, size_t *retlen)
++{
++ int pagenr, offset;
++ size_t start_len = 0, end_len;
++ int pagecnt = 0;
++ int err = 0;
++ struct bio *bio = NULL;
++ size_t thislen = 0;
++
++ pagenr = to >> PAGE_SHIFT;
++ offset = to & ~PAGE_MASK;
++
++ DEBUG(2, "blkmtd: write_pages: buf = %p to = %ld len = %d pagenr = %d offset = %d\n",
++ buf, (long)to, len, pagenr, offset);
++
++ /* see if we have to do a partial write at the start */
++ if(offset) {
++ start_len = ((offset + len) > PAGE_SIZE) ? PAGE_SIZE - offset : len;
++ len -= start_len;
++ }
++
++ /* calculate the length of the other two regions */
++ end_len = len & ~PAGE_MASK;
++ len -= end_len;
++
++ if(start_len)
++ pagecnt++;
++
++ if(len)
++ pagecnt += len >> PAGE_SHIFT;
++
++ if(end_len)
++ pagecnt++;
++
++ down(&dev->wrbuf_mutex);
++
++ DEBUG(3, "blkmtd: write: start_len = %d len = %d end_len = %d pagecnt = %d\n",
++ start_len, len, end_len, pagecnt);
++
++ if(start_len) {
++ /* do partial start region */
++ struct page *page;
++
++ DEBUG(3, "blkmtd: write: doing partial start, page = %d len = %d offset = %d\n",
++ pagenr, start_len, offset);
++
++ BUG_ON(!buf);
++ page = read_cache_page(dev->blkdev->bd_inode->i_mapping, pagenr, (filler_t *)blkmtd_readpage, dev);
++ lock_page(page);
++ if(PageDirty(page)) {
++ err("to = %lld start_len = %d len = %d end_len = %d pagenr = %d\n",
++ to, start_len, len, end_len, pagenr);
++ BUG();
++ }
++ memcpy(page_address(page)+offset, buf, start_len);
++ SetPageDirty(page);
++ SetPageUptodate(page);
++ buf += start_len;
++ thislen = start_len;
++ bio = blkmtd_add_page(bio, dev->blkdev, page, pagecnt);
++ if(!bio) {
++ err = -ENOMEM;
++ err("bio_add_page failed\n");
++ goto write_err;
++ }
++ pagecnt--;
++ pagenr++;
++ }
++
++ /* Now do the main loop to a page aligned, n page sized output */
++ if(len) {
++ int pagesc = len >> PAGE_SHIFT;
++ DEBUG(3, "blkmtd: write: whole pages start = %d, count = %d\n",
++ pagenr, pagesc);
++ while(pagesc) {
++ struct page *page;
++
++ /* see if page is in the page cache */
++ DEBUG(3, "blkmtd: write: grabbing page %d from page cache\n", pagenr);
++ page = grab_cache_page(dev->blkdev->bd_inode->i_mapping, pagenr);
++ if(PageDirty(page)) {
++ BUG();
++ }
++ if(!page) {
++ warn("write: cannot grab cache page %d", pagenr);
++ err = -ENOMEM;
++ goto write_err;
++ }
++ if(!buf) {
++ memset(page_address(page), 0xff, PAGE_SIZE);
++ } else {
++ memcpy(page_address(page), buf, PAGE_SIZE);
++ buf += PAGE_SIZE;
++ }
++ bio = blkmtd_add_page(bio, dev->blkdev, page, pagecnt);
++ if(!bio) {
++ err = -ENOMEM;
++ err("bio_add_page failed\n");
++ goto write_err;
++ }
++ pagenr++;
++ pagecnt--;
++ SetPageDirty(page);
++ SetPageUptodate(page);
++ pagesc--;
++ thislen += PAGE_SIZE;
++ }
++ }
++
++ if(end_len) {
++ /* do the third region */
++ struct page *page;
++ DEBUG(3, "blkmtd: write: doing partial end, page = %d len = %d\n",
++ pagenr, end_len);
++ BUG_ON(!buf);
++ page = read_cache_page(dev->blkdev->bd_inode->i_mapping, pagenr, (filler_t *)blkmtd_readpage, dev);
++ lock_page(page);
++ if(PageDirty(page)) {
++ err("to = %lld start_len = %d len = %d end_len = %d pagenr = %d\n",
++ to, start_len, len, end_len, pagenr);
++ BUG();
++ }
++ memcpy(page_address(page), buf, end_len);
++ SetPageDirty(page);
++ SetPageUptodate(page);
++ DEBUG(3, "blkmtd: write: writing out partial end\n");
++ thislen += end_len;
++ bio = blkmtd_add_page(bio, dev->blkdev, page, pagecnt);
++ if(!bio) {
++ err = -ENOMEM;
++ err("bio_add_page failed\n");
++ goto write_err;
++ }
++ pagenr++;
++ }
++
++ DEBUG(3, "blkmtd: write: got %d vectors to write\n", bio->bi_vcnt);
++ write_err:
++ if(bio)
++ blkmtd_write_out(bio);
++
++ DEBUG(2, "blkmtd: write: end, retlen = %d, err = %d\n", *retlen, err);
++ up(&dev->wrbuf_mutex);
++
++ if(retlen)
++ *retlen = thislen;
++ return err;
++}
++
++
++/* erase a specified part of the device */
++static int blkmtd_erase(struct mtd_info *mtd, struct erase_info *instr)
++{
++ struct blkmtd_dev *dev = mtd->priv;
++ struct mtd_erase_region_info *einfo = mtd->eraseregions;
++ int numregions = mtd->numeraseregions;
++ size_t from;
++ u_long len;
++ int err = -EIO;
++ int retlen;
++
++ instr->state = MTD_ERASING;
++ from = instr->addr;
++ len = instr->len;
++
++ /* check erase region has valid start and length */
++ DEBUG(2, "blkmtd: erase: dev = `%s' from = 0x%x len = 0x%lx\n",
++ mtd->name+9, from, len);
++ while(numregions) {
++ DEBUG(3, "blkmtd: checking erase region = 0x%08X size = 0x%X num = 0x%x\n",
++ einfo->offset, einfo->erasesize, einfo->numblocks);
++ if(from >= einfo->offset
++ && from < einfo->offset + (einfo->erasesize * einfo->numblocks)) {
++ if(len == einfo->erasesize
++ && ( (from - einfo->offset) % einfo->erasesize == 0))
++ break;
++ }
++ numregions--;
++ einfo++;
++ }
++
++ if(!numregions) {
++ /* Not a valid erase block */
++ err("erase: invalid erase request 0x%lX @ 0x%08X", len, from);
++ instr->state = MTD_ERASE_FAILED;
++ err = -EIO;
++ }
++
++ if(instr->state != MTD_ERASE_FAILED) {
++ /* do the erase */
++ DEBUG(3, "Doing erase from = %d len = %ld\n", from, len);
++ err = write_pages(dev, NULL, from, len, &retlen);
++ if(err || retlen != len) {
++ err("erase failed err = %d", err);
++ instr->state = MTD_ERASE_FAILED;
++ } else {
++ instr->state = MTD_ERASE_DONE;
++ }
++ }
++
++ DEBUG(3, "blkmtd: erase: checking callback\n");
++ if (instr->callback) {
++ (*(instr->callback))(instr);
++ }
++ DEBUG(2, "blkmtd: erase: finished (err = %d)\n", err);
++ return err;
++}
++
++
++/* read a range of the data via the page cache */
++static int blkmtd_read(struct mtd_info *mtd, loff_t from, size_t len,
++ size_t *retlen, u_char *buf)
++{
++ struct blkmtd_dev *dev = mtd->priv;
++ int err = 0;
++ int offset;
++ int pagenr, pages;
++ size_t thislen = 0;
++
++ DEBUG(2, "blkmtd: read: dev = `%s' from = %ld len = %d buf = %p\n",
++ mtd->name+9, (long int)from, len, buf);
++
++ if(from > mtd->size)
++ return -EINVAL;
++ if(from + len > mtd->size)
++ len = mtd->size - from;
++
++ pagenr = from >> PAGE_SHIFT;
++ offset = from - (pagenr << PAGE_SHIFT);
++
++ pages = (offset+len+PAGE_SIZE-1) >> PAGE_SHIFT;
++ DEBUG(3, "blkmtd: read: pagenr = %d offset = %d, pages = %d\n",
++ pagenr, offset, pages);
++
++ while(pages) {
++ struct page *page;
++ int cpylen;
++
++ DEBUG(3, "blkmtd: read: looking for page: %d\n", pagenr);
++ page = read_cache_page(dev->blkdev->bd_inode->i_mapping, pagenr, (filler_t *)blkmtd_readpage, dev);
++ if(IS_ERR(page)) {
++ err = -EIO;
++ goto readerr;
++ }
++
++ cpylen = (PAGE_SIZE > len) ? len : PAGE_SIZE;
++ if(offset+cpylen > PAGE_SIZE)
++ cpylen = PAGE_SIZE-offset;
++
++ memcpy(buf + thislen, page_address(page) + offset, cpylen);
++ offset = 0;
++ len -= cpylen;
++ thislen += cpylen;
++ pagenr++;
++ pages--;
++ if(!PageDirty(page))
++ page_cache_release(page);
++ }
++
++ readerr:
++ if(retlen)
++ *retlen = thislen;
++ DEBUG(2, "blkmtd: end read: retlen = %d, err = %d\n", thislen, err);
++ return err;
++}
++
++
++/* write data to the underlying device */
++static int blkmtd_write(struct mtd_info *mtd, loff_t to, size_t len,
++ size_t *retlen, const u_char *buf)
++{
++ struct blkmtd_dev *dev = mtd->priv;
++ int err;
++
++ if(!len)
++ return 0;
++
++ DEBUG(2, "blkmtd: write: dev = `%s' to = %ld len = %d buf = %p\n",
++ mtd->name+9, (long int)to, len, buf);
++
++ if(to >= mtd->size) {
++ return -ENOSPC;
++ }
++
++ if(to + len > mtd->size) {
++ len = mtd->size - to;
++ }
++
++ err = write_pages(dev, buf, to, len, retlen);
++ if(err > 0)
++ err = 0;
++ DEBUG(2, "blkmtd: write: end, err = %d\n", err);
++ return err;
++}
++
++
++/* sync the device - wait until the write queue is empty */
++static void blkmtd_sync(struct mtd_info *mtd)
++{
++ /* Currently all writes are synchronous */
++}
++
++
++static void free_device(struct blkmtd_dev *dev)
++{
++ DEBUG(2, "blkmtd: free_device() dev = %p\n", dev);
++ if(dev) {
++ if(dev->mtd_info.eraseregions)
++ kfree(dev->mtd_info.eraseregions);
++ if(dev->mtd_info.name)
++ kfree(dev->mtd_info.name);
++
++ if(dev->blkdev) {
++ invalidate_inode_pages(dev->blkdev->bd_inode->i_mapping);
++ close_bdev_excl(dev->blkdev, BDEV_RAW);
++ }
++ kfree(dev);
++ }
++}
++
++
++/* For a given size and initial erase size, calculate the number
++ * and size of each erase region. Goes round the loop twice,
++ * once to find out how many regions, then allocates space,
++ * then round the loop again to fill it in.
++ */
++static struct mtd_erase_region_info *calc_erase_regions(
++ size_t erase_size, size_t total_size, int *regions)
++{
++ struct mtd_erase_region_info *info = NULL;
++
++ DEBUG(2, "calc_erase_regions, es = %d size = %d regions = %d\n",
++ erase_size, total_size, *regions);
++ /* Make any user specified erasesize be a power of 2
++ and at least PAGE_SIZE */
++ if(erase_size) {
++ int es = erase_size;
++ erase_size = 1;
++ while(es != 1) {
++ es >>= 1;
++ erase_size <<= 1;
++ }
++ if(erase_size < PAGE_SIZE)
++ erase_size = PAGE_SIZE;
++ } else {
++ erase_size = CONFIG_MTD_BLKDEV_ERASESIZE;
++ }
++
++ *regions = 0;
++
++ do {
++ int tot_size = total_size;
++ int er_size = erase_size;
++ int count = 0, offset = 0, regcnt = 0;
++
++ while(tot_size) {
++ count = tot_size / er_size;
++ if(count) {
++ tot_size = tot_size % er_size;
++ if(info) {
++ DEBUG(2, "adding to erase info off=%d er=%d cnt=%d\n",
++ offset, er_size, count);
++ (info+regcnt)->offset = offset;
++ (info+regcnt)->erasesize = er_size;
++ (info+regcnt)->numblocks = count;
++ (*regions)++;
++ }
++ regcnt++;
++ offset += (count * er_size);
++ }
++ while(er_size > tot_size)
++ er_size >>= 1;
++ }
++ if(info == NULL) {
++ info = kmalloc(regcnt * sizeof(struct mtd_erase_region_info), GFP_KERNEL);
++ if(!info)
++ break;
++ }
++ } while(!(*regions));
++ DEBUG(2, "calc_erase_regions done, es = %d size = %d regions = %d\n",
++ erase_size, total_size, *regions);
++ return info;
++}
++
++
++extern dev_t __init name_to_dev_t(const char *line);
++
++static struct blkmtd_dev *add_device(char *devname, int readonly, int erase_size)
++{
++ struct block_device *bdev;
++ int mode;
++ struct blkmtd_dev *dev;
++
++ if(!devname)
++ return NULL;
++
++ /* Get a handle on the device */
++
++
++#ifdef MODULE
++ mode = (readonly) ? O_RDONLY : O_RDWR;
++ bdev = open_bdev_excl(devname, mode, BDEV_RAW, NULL);
++#else
++ mode = (readonly) ? FMODE_READ : FMODE_WRITE;
++ bdev = open_by_devnum(name_to_dev_t(devname), mode, BDEV_RAW);
++#endif
++ if(IS_ERR(bdev)) {
++ err("error: cannot open device %s", devname);
++ DEBUG(2, "blkmtd: opening bdev returned %ld\n", PTR_ERR(bdev));
++ return NULL;
++ }
++
++ DEBUG(1, "blkmtd: found a block device major = %d, minor = %d\n",
++ MAJOR(bdev->bd_dev), MINOR(bdev->bd_dev));
++
++ if(MAJOR(bdev->bd_dev) == MTD_BLOCK_MAJOR) {
++ err("attempting to use an MTD device as a block device");
++ blkdev_put(bdev, BDEV_RAW);
++ return NULL;
++ }
++
++ dev = kmalloc(sizeof(struct blkmtd_dev), GFP_KERNEL);
++ if(dev == NULL) {
++ blkdev_put(bdev, BDEV_RAW);
++ return NULL;
++ }
++
++ memset(dev, 0, sizeof(struct blkmtd_dev));
++ if(!readonly) {
++ init_MUTEX(&dev->wrbuf_mutex);
++ }
++
++ dev->blkdev = bdev;
++ dev->mtd_info.size = dev->blkdev->bd_inode->i_size & PAGE_MASK;
++
++ /* Setup the MTD structure */
++ /* make the name contain the block device in */
++ dev->mtd_info.name = kmalloc(sizeof("blkmtd: ") + strlen(devname), GFP_KERNEL);
++ if(dev->mtd_info.name == NULL)
++ goto devinit_err;
++
++ sprintf(dev->mtd_info.name, "blkmtd: %s", devname);
++ dev->mtd_info.eraseregions = calc_erase_regions(erase_size, dev->mtd_info.size,
++ &dev->mtd_info.numeraseregions);
++ if(dev->mtd_info.eraseregions == NULL)
++ goto devinit_err;
++
++ dev->mtd_info.erasesize = dev->mtd_info.eraseregions->erasesize;
++ DEBUG(1, "blkmtd: init: found %d erase regions\n",
++ dev->mtd_info.numeraseregions);
++
++ if(readonly) {
++ dev->mtd_info.type = MTD_ROM;
++ dev->mtd_info.flags = MTD_CAP_ROM;
++ } else {
++ dev->mtd_info.type = MTD_RAM;
++ dev->mtd_info.flags = MTD_CAP_RAM;
++ dev->mtd_info.erase = blkmtd_erase;
++ dev->mtd_info.write = blkmtd_write;
++ dev->mtd_info.writev = default_mtd_writev;
++ dev->mtd_info.sync = blkmtd_sync;
++ }
++ dev->mtd_info.read = blkmtd_read;
++ dev->mtd_info.readv = default_mtd_readv;
++ dev->mtd_info.priv = dev;
++ dev->mtd_info.owner = THIS_MODULE;
++
++ list_add(&dev->list, &blkmtd_device_list);
++ if (add_mtd_device(&dev->mtd_info)) {
++ /* Device didnt get added, so free the entry */
++ list_del(&dev->list);
++ goto devinit_err;
++ } else {
++ info("mtd%d: [%s] erase_size = %dKiB %s",
++ dev->mtd_info.index, dev->mtd_info.name + strlen("blkmtd: "),
++ dev->mtd_info.erasesize >> 10,
++ readonly ? "(read-only)" : "");
++ }
++
++ return dev;
++
++ devinit_err:
++ free_device(dev);
++ return NULL;
++}
++
++
++/* Cleanup and exit - sync the device and kill of the kernel thread */
++static void __devexit cleanup_blkmtd(void)
++{
++ struct list_head *temp1, *temp2;
++
++ /* Remove the MTD devices */
++ list_for_each_safe(temp1, temp2, &blkmtd_device_list) {
++ struct blkmtd_dev *dev = list_entry(temp1, struct blkmtd_dev,
++ list);
++ blkmtd_sync(&dev->mtd_info);
++ del_mtd_device(&dev->mtd_info);
++ info("mtd%d: [%s] removed", dev->mtd_info.index,
++ dev->mtd_info.name + strlen("blkmtd: "));
++ list_del(&dev->list);
++ free_device(dev);
++ }
++}
++
++#ifndef MODULE
++
++/* Handle kernel boot params */
++
++
++static int __init param_blkmtd_device(char *str)
++{
++ int i;
++
++ for(i = 0; i < MAX_DEVICES; i++) {
++ device[i] = str;
++ DEBUG(2, "blkmtd: device setup: %d = %s\n", i, device[i]);
++ strsep(&str, ",");
++ }
++ return 1;
++}
++
++
++static int __init param_blkmtd_erasesz(char *str)
++{
++ int i;
++ for(i = 0; i < MAX_DEVICES; i++) {
++ char *val = strsep(&str, ",");
++ if(val)
++ erasesz[i] = simple_strtoul(val, NULL, 0);
++ DEBUG(2, "blkmtd: erasesz setup: %d = %d\n", i, erasesz[i]);
++ }
++
++ return 1;
++}
++
++
++static int __init param_blkmtd_ro(char *str)
++{
++ int i;
++ for(i = 0; i < MAX_DEVICES; i++) {
++ char *val = strsep(&str, ",");
++ if(val)
++ ro[i] = simple_strtoul(val, NULL, 0);
++ DEBUG(2, "blkmtd: ro setup: %d = %d\n", i, ro[i]);
++ }
++
++ return 1;
++}
++
++
++static int __init param_blkmtd_sync(char *str)
++{
++ if(str[0] == '1')
++ sync = 1;
++ return 1;
++}
++
++__setup("blkmtd_device=", param_blkmtd_device);
++__setup("blkmtd_erasesz=", param_blkmtd_erasesz);
++__setup("blkmtd_ro=", param_blkmtd_ro);
++__setup("blkmtd_sync=", param_blkmtd_sync);
++
++#endif
++
++
++/* Startup */
++static int __init init_blkmtd(void)
++{
++ int i;
++
++ info("version " VERSION);
++ /* Check args - device[0] is the bare minimum*/
++ if(!device[0]) {
++ err("error: missing `device' name\n");
++ return -EINVAL;
++ }
++
++ for(i = 0; i < MAX_DEVICES; i++)
++ add_device(device[i], ro[i], erasesz[i] << 10);
++
++ if(list_empty(&blkmtd_device_list))
++ return -EINVAL;
++
++ return 0;
++}
++
++module_init(init_blkmtd);
++module_exit(cleanup_blkmtd);
+diff -Nurb linux-mips-2.4.27/drivers/mtd/devices/blkmtd.c linux/drivers/mtd/devices/blkmtd.c
+--- linux-mips-2.4.27/drivers/mtd/devices/blkmtd.c 2003-02-26 01:53:49.000000000 +0100
++++ linux/drivers/mtd/devices/blkmtd.c 2004-11-19 10:25:11.816212408 +0100
+@@ -1,5 +1,5 @@
+ /*
+- * $Id$
++ * $Id$
+ *
+ * blkmtd.c - use a block device as a fake MTD
+ *
+@@ -143,7 +143,7 @@
+ for(cnt = 0; cnt < pages; cnt++) {
+ page = grab_cache_page(dev->binding->bd_inode->i_mapping, pagenrs[cnt]);
+ pagelst[cnt] = page;
+- if(!PageUptodate(page)) {
++ if(!Page_Uptodate(page)) {
+ iobuf->blocks[iobuf->nr_pages] = pagenrs[cnt];
+ iobuf->maplist[iobuf->nr_pages++] = page;
+ }
+@@ -912,7 +912,7 @@
+ dev->mtd_info.point = 0;
+ dev->mtd_info.unpoint = 0;
+ dev->mtd_info.priv = dev;
+- dev->mtd_info.module = THIS_MODULE;
++ dev->mtd_info.owner = THIS_MODULE;
+
+ list_add(&dev->list, &blkmtd_device_list);
+ if (add_mtd_device(&dev->mtd_info)) {
+diff -Nurb linux-mips-2.4.27/drivers/mtd/devices/doc2000.c linux/drivers/mtd/devices/doc2000.c
+--- linux-mips-2.4.27/drivers/mtd/devices/doc2000.c 2003-02-26 01:53:49.000000000 +0100
++++ linux/drivers/mtd/devices/doc2000.c 2004-11-19 10:25:11.818212104 +0100
+@@ -4,7 +4,7 @@
+ * (c) 1999 Machine Vision Holdings, Inc.
+ * (c) 1999, 2000 David Woodhouse <dwmw2@infradead.org>
+ *
+- * $Id$
++ * $Id$
+ */
+
+ #include <linux/kernel.h>
+@@ -25,6 +25,7 @@
+ #include <linux/mtd/doc2000.h>
+
+ #define DOC_SUPPORT_2000
++#define DOC_SUPPORT_2000TSOP
+ #define DOC_SUPPORT_MILLENNIUM
+
+ #ifdef DOC_SUPPORT_2000
+@@ -33,7 +34,7 @@
+ #define DoC_is_2000(doc) (0)
+ #endif
+
+-#ifdef DOC_SUPPORT_MILLENNIUM
++#if defined(DOC_SUPPORT_2000TSOP) || defined(DOC_SUPPORT_MILLENNIUM)
+ #define DoC_is_Millennium(doc) (doc->ChipID == DOC_ChipID_DocMil)
+ #else
+ #define DoC_is_Millennium(doc) (0)
+@@ -56,6 +57,9 @@
+ size_t *retlen, u_char *buf, u_char *eccbuf, int oobsel);
+ static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len,
+ size_t *retlen, const u_char *buf, u_char *eccbuf, int oobsel);
++static int doc_writev_ecc(struct mtd_info *mtd, const struct iovec *vecs,
++ unsigned long count, loff_t to, size_t *retlen,
++ u_char *eccbuf, struct nand_oobinfo *oobsel);
+ static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
+ size_t *retlen, u_char *buf);
+ static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
+@@ -92,6 +96,10 @@
+
+ /* Out-of-line routine to wait for chip response */
+ while (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B)) {
++ /* issue 2 read from NOP register after reading from CDSNControl register
++ see Software Requirement 11.4 item 2. */
++ DoC_Delay(doc, 2);
++
+ if (time_after(jiffies, timeo)) {
+ DEBUG(MTD_DEBUG_LEVEL2, "_DoC_WaitReady timed out.\n");
+ return -EIO;
+@@ -145,6 +153,8 @@
+
+ /* Send the command */
+ WriteDOC_(command, docptr, doc->ioreg);
++ if (DoC_is_Millennium(doc))
++ WriteDOC(command, docptr, WritePipeTerm);
+
+ /* Lower the CLE line */
+ WriteDOC(xtraflags | CDSN_CTRL_CE, docptr, CDSNControl);
+@@ -206,6 +216,9 @@
+ }
+ }
+
++ if (DoC_is_Millennium(doc))
++ WriteDOC(ofs & 0xff, docptr, WritePipeTerm);
++
+ DoC_Delay(doc, 2); /* Needed for some slow flash chips. mf. */
+
+ /* FIXME: The SlowIO's for millennium could be replaced by
+@@ -344,15 +357,25 @@
+
+ /* Read the manufacturer and device id codes from the device */
+
+- /* CDSN Slow IO register see Software Requirement 11.4 item 5. */
++ if (DoC_is_Millennium(doc)) {
++ DoC_Delay(doc, 2);
++ dummy = ReadDOC(doc->virtadr, ReadPipeInit);
++ mfr = ReadDOC(doc->virtadr, LastDataRead);
++
++ DoC_Delay(doc, 2);
++ dummy = ReadDOC(doc->virtadr, ReadPipeInit);
++ id = ReadDOC(doc->virtadr, LastDataRead);
++ } else {
++ /* CDSN Slow IO register see Software Req 11.4 item 5. */
+ dummy = ReadDOC(doc->virtadr, CDSNSlowIO);
+ DoC_Delay(doc, 2);
+ mfr = ReadDOC_(doc->virtadr, doc->ioreg);
+
+- /* CDSN Slow IO register see Software Requirement 11.4 item 5. */
++ /* CDSN Slow IO register see Software Req 11.4 item 5. */
+ dummy = ReadDOC(doc->virtadr, CDSNSlowIO);
+ DoC_Delay(doc, 2);
+ id = ReadDOC_(doc->virtadr, doc->ioreg);
++ }
+
+ /* No response - return failure */
+ if (mfr == 0xff || mfr == 0)
+@@ -410,20 +433,16 @@
+
+ /* DoC_ScanChips: Find all NAND chips present in a DiskOnChip, and identify them */
+
+-static void DoC_ScanChips(struct DiskOnChip *this)
++static void DoC_ScanChips(struct DiskOnChip *this, int maxchips)
+ {
+ int floor, chip;
+ int numchips[MAX_FLOORS];
+- int maxchips = MAX_CHIPS;
+ int ret = 1;
+
+ this->numchips = 0;
+ this->mfr = 0;
+ this->id = 0;
+
+- if (DoC_is_Millennium(this))
+- maxchips = MAX_CHIPS_MIL;
+-
+ /* For each floor, find the number of valid chips it contains */
+ for (floor = 0; floor < MAX_FLOORS; floor++) {
+ ret = 1;
+@@ -515,6 +534,7 @@
+ {
+ struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv;
+ struct DiskOnChip *old = NULL;
++ int maxchips;
+
+ /* We must avoid being called twice for the same device. */
+
+@@ -538,14 +558,28 @@
+
+
+ switch (this->ChipID) {
++ case DOC_ChipID_Doc2kTSOP:
++ mtd->name = "DiskOnChip 2000 TSOP";
++ this->ioreg = DoC_Mil_CDSN_IO;
++ /* Pretend it's a Millennium */
++ this->ChipID = DOC_ChipID_DocMil;
++ maxchips = MAX_CHIPS;
++ break;
+ case DOC_ChipID_Doc2k:
+ mtd->name = "DiskOnChip 2000";
+ this->ioreg = DoC_2k_CDSN_IO;
++ maxchips = MAX_CHIPS;
+ break;
+ case DOC_ChipID_DocMil:
+ mtd->name = "DiskOnChip Millennium";
+ this->ioreg = DoC_Mil_CDSN_IO;
++ maxchips = MAX_CHIPS_MIL;
+ break;
++ default:
++ printk("Unknown ChipID 0x%02x\n", this->ChipID);
++ kfree(mtd);
++ iounmap((void *) this->virtadr);
++ return;
+ }
+
+ printk(KERN_NOTICE "%s found at address 0x%lX\n", mtd->name,
+@@ -553,11 +587,12 @@
+
+ mtd->type = MTD_NANDFLASH;
+ mtd->flags = MTD_CAP_NANDFLASH;
++ mtd->ecctype = MTD_ECC_RS_DiskOnChip;
+ mtd->size = 0;
+ mtd->erasesize = 0;
+ mtd->oobblock = 512;
+ mtd->oobsize = 16;
+- mtd->module = THIS_MODULE;
++ mtd->owner = THIS_MODULE;
+ mtd->erase = doc_erase;
+ mtd->point = NULL;
+ mtd->unpoint = NULL;
+@@ -565,6 +600,7 @@
+ mtd->write = doc_write;
+ mtd->read_ecc = doc_read_ecc;
+ mtd->write_ecc = doc_write_ecc;
++ mtd->writev_ecc = doc_writev_ecc;
+ mtd->read_oob = doc_read_oob;
+ mtd->write_oob = doc_write_oob;
+ mtd->sync = NULL;
+@@ -577,7 +613,7 @@
+ init_MUTEX(&this->lock);
+
+ /* Ident all the chips present. */
+- DoC_ScanChips(this);
++ DoC_ScanChips(this, maxchips);
+
+ if (!this->totlen) {
+ kfree(mtd);
+@@ -608,6 +644,7 @@
+ unsigned char syndrome[6];
+ volatile char dummy;
+ int i, len256 = 0, ret=0;
++ size_t left = len;
+
+ docptr = this->virtadr;
+
+@@ -617,6 +654,10 @@
+
+ down(&this->lock);
+
++ *retlen = 0;
++ while (left) {
++ len = left;
++
+ /* Don't allow a single read to cross a 512-byte block boundary */
+ if (from + len > ((from | 0x1ff) + 1))
+ len = ((from | 0x1ff) + 1) - from;
+@@ -673,7 +714,7 @@
+ DoC_ReadBuf(this, &buf[len256], len - len256);
+
+ /* Let the caller know we completed it */
+- *retlen = len;
++ *retlen += len;
+
+ if (eccbuf) {
+ /* Read the ECC data through the DiskOnChip ECC logic */
+@@ -730,11 +771,16 @@
+
+ /* according to 11.4.1, we need to wait for the busy line
+ * drop if we read to the end of the page. */
+- if(0 == ((from + *retlen) & 0x1ff))
++ if(0 == ((from + len) & 0x1ff))
+ {
+ DoC_WaitReady(this);
+ }
+
++ from += len;
++ left -= len;
++ buf += len;
++ }
++
+ up(&this->lock);
+
+ return ret;
+@@ -757,6 +803,8 @@
+ volatile char dummy;
+ int len256 = 0;
+ struct Nand *mychip;
++ size_t left = len;
++ int status;
+
+ docptr = this->virtadr;
+
+@@ -766,15 +814,21 @@
+
+ down(&this->lock);
+
++ *retlen = 0;
++ while (left) {
++ len = left;
++
+ /* Don't allow a single write to cross a 512-byte block boundary */
+ if (to + len > ((to | 0x1ff) + 1))
+ len = ((to | 0x1ff) + 1) - to;
+
+ /* The ECC will not be calculated correctly if less than 512 is written */
++/* DBB-
+ if (len != 0x200 && eccbuf)
+ printk(KERN_WARNING
+ "ECC needs a full sector write (adr: %lx size %lx)\n",
+ (long) to, (long) len);
++ -DBB */
+
+ /* printk("DoC_Write (adr: %lx size %lx)\n", (long) to, (long) len); */
+
+@@ -853,6 +907,9 @@
+ WriteDOC_(0, docptr, this->ioreg);
+ }
+
++ WriteDOC(CDSN_CTRL_ECC_IO | CDSN_CTRL_FLASH_IO | CDSN_CTRL_CE, docptr,
++ CDSNControl);
++
+ /* Read the ECC data through the DiskOnChip ECC logic */
+ for (di = 0; di < 6; di++) {
+ eccbuf[di] = ReadDOC(docptr, ECCSyndrome0 + di);
+@@ -874,10 +931,16 @@
+ DoC_Command(this, NAND_CMD_STATUS, CDSN_CTRL_WP);
+ /* There's an implicit DoC_WaitReady() in DoC_Command */
+
++ if (DoC_is_Millennium(this)) {
++ ReadDOC(docptr, ReadPipeInit);
++ status = ReadDOC(docptr, LastDataRead);
++ } else {
+ dummy = ReadDOC(docptr, CDSNSlowIO);
+ DoC_Delay(this, 2);
++ status = ReadDOC_(docptr, this->ioreg);
++ }
+
+- if (ReadDOC_(docptr, this->ioreg) & 1) {
++ if (status & 1) {
+ printk(KERN_ERR "Error programming flash\n");
+ /* Error in programming */
+ *retlen = 0;
+@@ -886,7 +949,7 @@
+ }
+
+ /* Let the caller know we completed it */
+- *retlen = len;
++ *retlen += len;
+
+ if (eccbuf) {
+ unsigned char x[8];
+@@ -901,13 +964,81 @@
+ x[7]=0x55;
+
+ ret = doc_write_oob_nolock(mtd, to, 8, &dummy, x);
++ if (ret) {
+ up(&this->lock);
+ return ret;
+ }
++ }
++
++ to += len;
++ left -= len;
++ buf += len;
++ }
++
+ up(&this->lock);
+ return 0;
+ }
+
++static int doc_writev_ecc(struct mtd_info *mtd, const struct iovec *vecs,
++ unsigned long count, loff_t to, size_t *retlen,
++ u_char *eccbuf, struct nand_oobinfo *oobsel)
++{
++ static char static_buf[512];
++ static DECLARE_MUTEX(writev_buf_sem);
++
++ size_t totretlen = 0;
++ size_t thisvecofs = 0;
++ int ret= 0;
++
++ down(&writev_buf_sem);
++
++ while(count) {
++ size_t thislen, thisretlen;
++ unsigned char *buf;
++
++ buf = vecs->iov_base + thisvecofs;
++ thislen = vecs->iov_len - thisvecofs;
++
++
++ if (thislen >= 512) {
++ thislen = thislen & ~(512-1);
++ thisvecofs += thislen;
++ } else {
++ /* Not enough to fill a page. Copy into buf */
++ memcpy(static_buf, buf, thislen);
++ buf = &static_buf[thislen];
++
++ while(count && thislen < 512) {
++ vecs++;
++ count--;
++ thisvecofs = min((512-thislen), vecs->iov_len);
++ memcpy(buf, vecs->iov_base, thisvecofs);
++ thislen += thisvecofs;
++ buf += thisvecofs;
++ }
++ buf = static_buf;
++ }
++ if (count && thisvecofs == vecs->iov_len) {
++ thisvecofs = 0;
++ vecs++;
++ count--;
++ }
++ ret = doc_write_ecc(mtd, to, thislen, &thisretlen, buf, eccbuf, oobsel);
++
++ totretlen += thisretlen;
++
++ if (ret || thisretlen != thislen)
++ break;
++
++ to += thislen;
++ }
++
++ up(&writev_buf_sem);
++ *retlen = totretlen;
++ return ret;
++}
++
++
+ static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
+ size_t * retlen, u_char * buf)
+ {
+@@ -977,6 +1108,7 @@
+ unsigned long docptr = this->virtadr;
+ struct Nand *mychip = &this->chips[ofs >> this->chipshift];
+ volatile int dummy;
++ int status;
+
+ // printk("doc_write_oob(%lx, %d): %2.2X %2.2X %2.2X %2.2X ... %2.2X %2.2X .. %2.2X %2.2X\n",(long)ofs, len,
+ // buf[0], buf[1], buf[2], buf[3], buf[8], buf[9], buf[14],buf[15]);
+@@ -1025,10 +1157,16 @@
+ DoC_Command(this, NAND_CMD_STATUS, 0);
+ /* DoC_WaitReady() is implicit in DoC_Command */
+
++ if (DoC_is_Millennium(this)) {
++ ReadDOC(docptr, ReadPipeInit);
++ status = ReadDOC(docptr, LastDataRead);
++ } else {
+ dummy = ReadDOC(docptr, CDSNSlowIO);
+ DoC_Delay(this, 2);
++ status = ReadDOC_(docptr, this->ioreg);
++ }
+
+- if (ReadDOC_(docptr, this->ioreg) & 1) {
++ if (status & 1) {
+ printk(KERN_ERR "Error programming oob data\n");
+ /* There was an error */
+ *retlen = 0;
+@@ -1044,10 +1182,16 @@
+ DoC_Command(this, NAND_CMD_STATUS, 0);
+ /* DoC_WaitReady() is implicit in DoC_Command */
+
++ if (DoC_is_Millennium(this)) {
++ ReadDOC(docptr, ReadPipeInit);
++ status = ReadDOC(docptr, LastDataRead);
++ } else {
+ dummy = ReadDOC(docptr, CDSNSlowIO);
+ DoC_Delay(this, 2);
++ status = ReadDOC_(docptr, this->ioreg);
++ }
+
+- if (ReadDOC_(docptr, this->ioreg) & 1) {
++ if (status & 1) {
+ printk(KERN_ERR "Error programming oob data\n");
+ /* There was an error */
+ *retlen = 0;
+@@ -1080,6 +1224,7 @@
+ volatile int dummy;
+ unsigned long docptr;
+ struct Nand *mychip;
++ int status;
+
+ down(&this->lock);
+
+@@ -1111,10 +1256,16 @@
+
+ DoC_Command(this, NAND_CMD_STATUS, CDSN_CTRL_WP);
+
++ if (DoC_is_Millennium(this)) {
++ ReadDOC(docptr, ReadPipeInit);
++ status = ReadDOC(docptr, LastDataRead);
++ } else {
+ dummy = ReadDOC(docptr, CDSNSlowIO);
+ DoC_Delay(this, 2);
++ status = ReadDOC_(docptr, this->ioreg);
++ }
+
+- if (ReadDOC_(docptr, this->ioreg) & 1) {
++ if (status & 1) {
+ printk(KERN_ERR "Error erasing at 0x%x\n", ofs);
+ /* There was an error */
+ instr->state = MTD_ERASE_FAILED;
+diff -Nurb linux-mips-2.4.27/drivers/mtd/devices/doc2001.c linux/drivers/mtd/devices/doc2001.c
+--- linux-mips-2.4.27/drivers/mtd/devices/doc2001.c 2003-02-26 01:53:49.000000000 +0100
++++ linux/drivers/mtd/devices/doc2001.c 2004-11-19 10:25:11.835209520 +0100
+@@ -4,7 +4,7 @@
+ * (c) 1999 Machine Vision Holdings, Inc.
+ * (c) 1999, 2000 David Woodhouse <dwmw2@infradead.org>
+ *
+- * $Id$
++ * $Id$
+ */
+
+ #include <linux/kernel.h>
+@@ -359,14 +359,15 @@
+
+ mtd->type = MTD_NANDFLASH;
+ mtd->flags = MTD_CAP_NANDFLASH;
++ mtd->ecctype = MTD_ECC_RS_DiskOnChip;
+ mtd->size = 0;
+
+- /* FIXME: erase size is not always 8kB */
++ /* FIXME: erase size is not always 8KiB */
+ mtd->erasesize = 0x2000;
+
+ mtd->oobblock = 512;
+ mtd->oobsize = 16;
+- mtd->module = THIS_MODULE;
++ mtd->owner = THIS_MODULE;
+ mtd->erase = doc_erase;
+ mtd->point = NULL;
+ mtd->unpoint = NULL;
+diff -Nurb linux-mips-2.4.27/drivers/mtd/devices/doc2001plus.c linux/drivers/mtd/devices/doc2001plus.c
+--- linux-mips-2.4.27/drivers/mtd/devices/doc2001plus.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/mtd/devices/doc2001plus.c 2004-11-19 10:25:11.837209216 +0100
+@@ -0,0 +1,1154 @@
++/*
++ * Linux driver for Disk-On-Chip Millennium Plus
++ *
++ * (c) 2002-2003 Greg Ungerer <gerg@snapgear.com>
++ * (c) 2002-2003 SnapGear Inc
++ * (c) 1999 Machine Vision Holdings, Inc.
++ * (c) 1999, 2000 David Woodhouse <dwmw2@infradead.org>
++ *
++ * $Id$
++ *
++ * Released under GPL
++ */
++
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <asm/errno.h>
++#include <asm/io.h>
++#include <asm/uaccess.h>
++#include <linux/miscdevice.h>
++#include <linux/pci.h>
++#include <linux/delay.h>
++#include <linux/slab.h>
++#include <linux/sched.h>
++#include <linux/init.h>
++#include <linux/types.h>
++
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/nand.h>
++#include <linux/mtd/doc2000.h>
++
++/* #define ECC_DEBUG */
++
++/* I have no idea why some DoC chips can not use memcop_form|to_io().
++ * This may be due to the different revisions of the ASIC controller built-in or
++ * simplily a QA/Bug issue. Who knows ?? If you have trouble, please uncomment
++ * this:*/
++#undef USE_MEMCPY
++
++static int doc_read(struct mtd_info *mtd, loff_t from, size_t len,
++ size_t *retlen, u_char *buf);
++static int doc_write(struct mtd_info *mtd, loff_t to, size_t len,
++ size_t *retlen, const u_char *buf);
++static int doc_read_ecc(struct mtd_info *mtd, loff_t from, size_t len,
++ size_t *retlen, u_char *buf, u_char *eccbuf,
++ struct nand_oobinfo *oobsel);
++static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len,
++ size_t *retlen, const u_char *buf, u_char *eccbuf,
++ struct nand_oobinfo *oobsel);
++static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
++ size_t *retlen, u_char *buf);
++static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
++ size_t *retlen, const u_char *buf);
++static int doc_erase (struct mtd_info *mtd, struct erase_info *instr);
++
++static struct mtd_info *docmilpluslist = NULL;
++
++
++/* Perform the required delay cycles by writing to the NOP register */
++static void DoC_Delay(unsigned long docptr, int cycles)
++{
++ int i;
++
++ for (i = 0; (i < cycles); i++)
++ WriteDOC(0, docptr, Mplus_NOP);
++}
++
++#define CDSN_CTRL_FR_B_MASK (CDSN_CTRL_FR_B0 | CDSN_CTRL_FR_B1)
++
++/* DOC_WaitReady: Wait for RDY line to be asserted by the flash chip */
++static int _DoC_WaitReady(unsigned long docptr)
++{
++ unsigned int c = 0xffff;
++
++ DEBUG(MTD_DEBUG_LEVEL3,
++ "_DoC_WaitReady called for out-of-line wait\n");
++
++ /* Out-of-line routine to wait for chip response */
++ while (((ReadDOC(docptr, Mplus_FlashControl) & CDSN_CTRL_FR_B_MASK) != CDSN_CTRL_FR_B_MASK) && --c)
++ ;
++
++ if (c == 0)
++ DEBUG(MTD_DEBUG_LEVEL2, "_DoC_WaitReady timed out.\n");
++
++ return (c == 0);
++}
++
++static inline int DoC_WaitReady(unsigned long docptr)
++{
++ /* This is inline, to optimise the common case, where it's ready instantly */
++ int ret = 0;
++
++ /* read form NOP register should be issued prior to the read from CDSNControl
++ see Software Requirement 11.4 item 2. */
++ DoC_Delay(docptr, 4);
++
++ if ((ReadDOC(docptr, Mplus_FlashControl) & CDSN_CTRL_FR_B_MASK) != CDSN_CTRL_FR_B_MASK)
++ /* Call the out-of-line routine to wait */
++ ret = _DoC_WaitReady(docptr);
++
++ return ret;
++}
++
++/* For some reason the Millennium Plus seems to occassionally put itself
++ * into reset mode. For me this happens randomly, with no pattern that I
++ * can detect. M-systems suggest always check this on any block level
++ * operation and setting to normal mode if in reset mode.
++ */
++static inline void DoC_CheckASIC(unsigned long docptr)
++{
++ /* Make sure the DoC is in normal mode */
++ if ((ReadDOC(docptr, Mplus_DOCControl) & DOC_MODE_NORMAL) == 0) {
++ WriteDOC((DOC_MODE_NORMAL | DOC_MODE_MDWREN), docptr, Mplus_DOCControl);
++ WriteDOC(~(DOC_MODE_NORMAL | DOC_MODE_MDWREN), docptr, Mplus_CtrlConfirm);
++ }
++}
++
++/* DoC_Command: Send a flash command to the flash chip through the Flash
++ * command register. Need 2 Write Pipeline Terminates to complete send.
++ */
++static inline void DoC_Command(unsigned long docptr, unsigned char command,
++ unsigned char xtraflags)
++{
++ WriteDOC(command, docptr, Mplus_FlashCmd);
++ WriteDOC(command, docptr, Mplus_WritePipeTerm);
++ WriteDOC(command, docptr, Mplus_WritePipeTerm);
++}
++
++/* DoC_Address: Set the current address for the flash chip through the Flash
++ * Address register. Need 2 Write Pipeline Terminates to complete send.
++ */
++static inline void DoC_Address(struct DiskOnChip *doc, int numbytes,
++ unsigned long ofs, unsigned char xtraflags1,
++ unsigned char xtraflags2)
++{
++ unsigned long docptr = doc->virtadr;
++
++ /* Allow for possible Mill Plus internal flash interleaving */
++ ofs >>= doc->interleave;
++
++ switch (numbytes) {
++ case 1:
++ /* Send single byte, bits 0-7. */
++ WriteDOC(ofs & 0xff, docptr, Mplus_FlashAddress);
++ break;
++ case 2:
++ /* Send bits 9-16 followed by 17-23 */
++ WriteDOC((ofs >> 9) & 0xff, docptr, Mplus_FlashAddress);
++ WriteDOC((ofs >> 17) & 0xff, docptr, Mplus_FlashAddress);
++ break;
++ case 3:
++ /* Send 0-7, 9-16, then 17-23 */
++ WriteDOC(ofs & 0xff, docptr, Mplus_FlashAddress);
++ WriteDOC((ofs >> 9) & 0xff, docptr, Mplus_FlashAddress);
++ WriteDOC((ofs >> 17) & 0xff, docptr, Mplus_FlashAddress);
++ break;
++ default:
++ return;
++ }
++
++ WriteDOC(0x00, docptr, Mplus_WritePipeTerm);
++ WriteDOC(0x00, docptr, Mplus_WritePipeTerm);
++}
++
++/* DoC_SelectChip: Select a given flash chip within the current floor */
++static int DoC_SelectChip(unsigned long docptr, int chip)
++{
++ /* No choice for flash chip on Millennium Plus */
++ return 0;
++}
++
++/* DoC_SelectFloor: Select a given floor (bank of flash chips) */
++static int DoC_SelectFloor(unsigned long docptr, int floor)
++{
++ WriteDOC((floor & 0x3), docptr, Mplus_DeviceSelect);
++ return 0;
++}
++
++/*
++ * Translate the given offset into the appropriate command and offset.
++ * This does the mapping using the 16bit interleave layout defined by
++ * M-Systems, and looks like this for a sector pair:
++ * +-----------+-------+-------+-------+--------------+---------+-----------+
++ * | 0 --- 511 |512-517|518-519|520-521| 522 --- 1033 |1034-1039|1040 - 1055|
++ * +-----------+-------+-------+-------+--------------+---------+-----------+
++ * | Data 0 | ECC 0 |Flags0 |Flags1 | Data 1 |ECC 1 | OOB 1 + 2 |
++ * +-----------+-------+-------+-------+--------------+---------+-----------+
++ */
++/* FIXME: This lives in INFTL not here. Other users of flash devices
++ may not want it */
++static unsigned int DoC_GetDataOffset(struct mtd_info *mtd, loff_t *from)
++{
++ struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv;
++
++ if (this->interleave) {
++ unsigned int ofs = *from & 0x3ff;
++ unsigned int cmd;
++
++ if (ofs < 512) {
++ cmd = NAND_CMD_READ0;
++ ofs &= 0x1ff;
++ } else if (ofs < 1014) {
++ cmd = NAND_CMD_READ1;
++ ofs = (ofs & 0x1ff) + 10;
++ } else {
++ cmd = NAND_CMD_READOOB;
++ ofs = ofs - 1014;
++ }
++
++ *from = (*from & ~0x3ff) | ofs;
++ return cmd;
++ } else {
++ /* No interleave */
++ if ((*from) & 0x100)
++ return NAND_CMD_READ1;
++ return NAND_CMD_READ0;
++ }
++}
++
++static unsigned int DoC_GetECCOffset(struct mtd_info *mtd, loff_t *from)
++{
++ unsigned int ofs, cmd;
++
++ if (*from & 0x200) {
++ cmd = NAND_CMD_READOOB;
++ ofs = 10 + (*from & 0xf);
++ } else {
++ cmd = NAND_CMD_READ1;
++ ofs = (*from & 0xf);
++ }
++
++ *from = (*from & ~0x3ff) | ofs;
++ return cmd;
++}
++
++static unsigned int DoC_GetFlagsOffset(struct mtd_info *mtd, loff_t *from)
++{
++ unsigned int ofs, cmd;
++
++ cmd = NAND_CMD_READ1;
++ ofs = (*from & 0x200) ? 8 : 6;
++ *from = (*from & ~0x3ff) | ofs;
++ return cmd;
++}
++
++static unsigned int DoC_GetHdrOffset(struct mtd_info *mtd, loff_t *from)
++{
++ unsigned int ofs, cmd;
++
++ cmd = NAND_CMD_READOOB;
++ ofs = (*from & 0x200) ? 24 : 16;
++ *from = (*from & ~0x3ff) | ofs;
++ return cmd;
++}
++
++static inline void MemReadDOC(unsigned long docptr, unsigned char *buf, int len)
++{
++#ifndef USE_MEMCPY
++ int i;
++ for (i = 0; i < len; i++)
++ buf[i] = ReadDOC(docptr, Mil_CDSN_IO + i);
++#else
++ memcpy_fromio(buf, docptr + DoC_Mil_CDSN_IO, len);
++#endif
++}
++
++static inline void MemWriteDOC(unsigned long docptr, unsigned char *buf, int len)
++{
++#ifndef USE_MEMCPY
++ int i;
++ for (i = 0; i < len; i++)
++ WriteDOC(buf[i], docptr, Mil_CDSN_IO + i);
++#else
++ memcpy_toio(docptr + DoC_Mil_CDSN_IO, buf, len);
++#endif
++}
++
++/* DoC_IdentChip: Identify a given NAND chip given {floor,chip} */
++static int DoC_IdentChip(struct DiskOnChip *doc, int floor, int chip)
++{
++ int mfr, id, i, j;
++ volatile char dummy;
++ unsigned long docptr = doc->virtadr;
++
++ /* Page in the required floor/chip */
++ DoC_SelectFloor(docptr, floor);
++ DoC_SelectChip(docptr, chip);
++
++ /* Millennium Plus bus cycle sequence as per figure 2, section 2.4 */
++ WriteDOC((DOC_FLASH_CE | DOC_FLASH_WP), docptr, Mplus_FlashSelect);
++
++ /* Reset the chip, see Software Requirement 11.4 item 1. */
++ DoC_Command(docptr, NAND_CMD_RESET, 0);
++ DoC_WaitReady(docptr);
++
++ /* Read the NAND chip ID: 1. Send ReadID command */
++ DoC_Command(docptr, NAND_CMD_READID, 0);
++
++ /* Read the NAND chip ID: 2. Send address byte zero */
++ DoC_Address(doc, 1, 0x00, 0, 0x00);
++
++ WriteDOC(0, docptr, Mplus_FlashControl);
++ DoC_WaitReady(docptr);
++
++ /* Read the manufacturer and device id codes of the flash device through
++ CDSN IO register see Software Requirement 11.4 item 5.*/
++ dummy = ReadDOC(docptr, Mplus_ReadPipeInit);
++ dummy = ReadDOC(docptr, Mplus_ReadPipeInit);
++
++ mfr = ReadDOC(docptr, Mil_CDSN_IO);
++ if (doc->interleave)
++ dummy = ReadDOC(docptr, Mil_CDSN_IO); /* 2 way interleave */
++
++ id = ReadDOC(docptr, Mil_CDSN_IO);
++ if (doc->interleave)
++ dummy = ReadDOC(docptr, Mil_CDSN_IO); /* 2 way interleave */
++
++ dummy = ReadDOC(docptr, Mplus_LastDataRead);
++ dummy = ReadDOC(docptr, Mplus_LastDataRead);
++
++ /* Disable flash internally */
++ WriteDOC(0, docptr, Mplus_FlashSelect);
++
++ /* No response - return failure */
++ if (mfr == 0xff || mfr == 0)
++ return 0;
++
++ for (i = 0; nand_flash_ids[i].name != NULL; i++) {
++ if (id == nand_flash_ids[i].id) {
++ /* Try to identify manufacturer */
++ for (j = 0; nand_manuf_ids[j].id != 0x0; j++) {
++ if (nand_manuf_ids[j].id == mfr)
++ break;
++ }
++ printk(KERN_INFO "Flash chip found: Manufacturer ID: %2.2X, "
++ "Chip ID: %2.2X (%s:%s)\n", mfr, id,
++ nand_manuf_ids[j].name, nand_flash_ids[i].name);
++ doc->mfr = mfr;
++ doc->id = id;
++ doc->chipshift = nand_flash_ids[i].chipshift;
++ doc->erasesize = nand_flash_ids[i].erasesize << doc->interleave;
++ break;
++ }
++ }
++
++ if (nand_flash_ids[i].name == NULL)
++ return 0;
++ return 1;
++}
++
++/* DoC_ScanChips: Find all NAND chips present in a DiskOnChip, and identify them */
++static void DoC_ScanChips(struct DiskOnChip *this)
++{
++ int floor, chip;
++ int numchips[MAX_FLOORS_MPLUS];
++ int ret;
++
++ this->numchips = 0;
++ this->mfr = 0;
++ this->id = 0;
++
++ /* Work out the intended interleave setting */
++ this->interleave = 0;
++ if (this->ChipID == DOC_ChipID_DocMilPlus32)
++ this->interleave = 1;
++
++ /* Check the ASIC agrees */
++ if ( (this->interleave << 2) !=
++ (ReadDOC(this->virtadr, Mplus_Configuration) & 4)) {
++ u_char conf = ReadDOC(this->virtadr, Mplus_Configuration);
++ printk(KERN_NOTICE "Setting DiskOnChip Millennium Plus interleave to %s\n",
++ this->interleave?"on (16-bit)":"off (8-bit)");
++ conf ^= 4;
++ WriteDOC(this->virtadr, conf, Mplus_Configuration);
++ }
++
++ /* For each floor, find the number of valid chips it contains */
++ for (floor = 0,ret = 1; floor < MAX_FLOORS_MPLUS; floor++) {
++ numchips[floor] = 0;
++ for (chip = 0; chip < MAX_CHIPS_MPLUS && ret != 0; chip++) {
++ ret = DoC_IdentChip(this, floor, chip);
++ if (ret) {
++ numchips[floor]++;
++ this->numchips++;
++ }
++ }
++ }
++ /* If there are none at all that we recognise, bail */
++ if (!this->numchips) {
++ printk("No flash chips recognised.\n");
++ return;
++ }
++
++ /* Allocate an array to hold the information for each chip */
++ this->chips = kmalloc(sizeof(struct Nand) * this->numchips, GFP_KERNEL);
++ if (!this->chips){
++ printk("MTD: No memory for allocating chip info structures\n");
++ return;
++ }
++
++ /* Fill out the chip array with {floor, chipno} for each
++ * detected chip in the device. */
++ for (floor = 0, ret = 0; floor < MAX_FLOORS_MPLUS; floor++) {
++ for (chip = 0 ; chip < numchips[floor] ; chip++) {
++ this->chips[ret].floor = floor;
++ this->chips[ret].chip = chip;
++ this->chips[ret].curadr = 0;
++ this->chips[ret].curmode = 0x50;
++ ret++;
++ }
++ }
++
++ /* Calculate and print the total size of the device */
++ this->totlen = this->numchips * (1 << this->chipshift);
++ printk(KERN_INFO "%d flash chips found. Total DiskOnChip size: %ld MiB\n",
++ this->numchips ,this->totlen >> 20);
++}
++
++static int DoCMilPlus_is_alias(struct DiskOnChip *doc1, struct DiskOnChip *doc2)
++{
++ int tmp1, tmp2, retval;
++
++ if (doc1->physadr == doc2->physadr)
++ return 1;
++
++ /* Use the alias resolution register which was set aside for this
++ * purpose. If it's value is the same on both chips, they might
++ * be the same chip, and we write to one and check for a change in
++ * the other. It's unclear if this register is usuable in the
++ * DoC 2000 (it's in the Millennium docs), but it seems to work. */
++ tmp1 = ReadDOC(doc1->virtadr, Mplus_AliasResolution);
++ tmp2 = ReadDOC(doc2->virtadr, Mplus_AliasResolution);
++ if (tmp1 != tmp2)
++ return 0;
++
++ WriteDOC((tmp1+1) % 0xff, doc1->virtadr, Mplus_AliasResolution);
++ tmp2 = ReadDOC(doc2->virtadr, Mplus_AliasResolution);
++ if (tmp2 == (tmp1+1) % 0xff)
++ retval = 1;
++ else
++ retval = 0;
++
++ /* Restore register contents. May not be necessary, but do it just to
++ * be safe. */
++ WriteDOC(tmp1, doc1->virtadr, Mplus_AliasResolution);
++
++ return retval;
++}
++
++static const char im_name[] = "DoCMilPlus_init";
++
++/* This routine is made available to other mtd code via
++ * inter_module_register. It must only be accessed through
++ * inter_module_get which will bump the use count of this module. The
++ * addresses passed back in mtd are valid as long as the use count of
++ * this module is non-zero, i.e. between inter_module_get and
++ * inter_module_put. Keith Owens <kaos@ocs.com.au> 29 Oct 2000.
++ */
++static void DoCMilPlus_init(struct mtd_info *mtd)
++{
++ struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv;
++ struct DiskOnChip *old = NULL;
++
++ /* We must avoid being called twice for the same device. */
++ if (docmilpluslist)
++ old = (struct DiskOnChip *)docmilpluslist->priv;
++
++ while (old) {
++ if (DoCMilPlus_is_alias(this, old)) {
++ printk(KERN_NOTICE "Ignoring DiskOnChip Millennium "
++ "Plus at 0x%lX - already configured\n",
++ this->physadr);
++ iounmap((void *)this->virtadr);
++ kfree(mtd);
++ return;
++ }
++ if (old->nextdoc)
++ old = (struct DiskOnChip *)old->nextdoc->priv;
++ else
++ old = NULL;
++ }
++
++ mtd->name = "DiskOnChip Millennium Plus";
++ printk(KERN_NOTICE "DiskOnChip Millennium Plus found at "
++ "address 0x%lX\n", this->physadr);
++
++ mtd->type = MTD_NANDFLASH;
++ mtd->flags = MTD_CAP_NANDFLASH;
++ mtd->ecctype = MTD_ECC_RS_DiskOnChip;
++ mtd->size = 0;
++
++ mtd->erasesize = 0;
++ mtd->oobblock = 512;
++ mtd->oobsize = 16;
++ mtd->owner = THIS_MODULE;
++ mtd->erase = doc_erase;
++ mtd->point = NULL;
++ mtd->unpoint = NULL;
++ mtd->read = doc_read;
++ mtd->write = doc_write;
++ mtd->read_ecc = doc_read_ecc;
++ mtd->write_ecc = doc_write_ecc;
++ mtd->read_oob = doc_read_oob;
++ mtd->write_oob = doc_write_oob;
++ mtd->sync = NULL;
++
++ this->totlen = 0;
++ this->numchips = 0;
++ this->curfloor = -1;
++ this->curchip = -1;
++
++ /* Ident all the chips present. */
++ DoC_ScanChips(this);
++
++ if (!this->totlen) {
++ kfree(mtd);
++ iounmap((void *)this->virtadr);
++ } else {
++ this->nextdoc = docmilpluslist;
++ docmilpluslist = mtd;
++ mtd->size = this->totlen;
++ mtd->erasesize = this->erasesize;
++ add_mtd_device(mtd);
++ return;
++ }
++}
++
++#if 0
++static int doc_dumpblk(struct mtd_info *mtd, loff_t from)
++{
++ int i;
++ loff_t fofs;
++ struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv;
++ unsigned long docptr = this->virtadr;
++ struct Nand *mychip = &this->chips[from >> (this->chipshift)];
++ unsigned char *bp, buf[1056];
++ char c[32];
++
++ from &= ~0x3ff;
++
++ /* Don't allow read past end of device */
++ if (from >= this->totlen)
++ return -EINVAL;
++
++ DoC_CheckASIC(docptr);
++
++ /* Find the chip which is to be used and select it */
++ if (this->curfloor != mychip->floor) {
++ DoC_SelectFloor(docptr, mychip->floor);
++ DoC_SelectChip(docptr, mychip->chip);
++ } else if (this->curchip != mychip->chip) {
++ DoC_SelectChip(docptr, mychip->chip);
++ }
++ this->curfloor = mychip->floor;
++ this->curchip = mychip->chip;
++
++ /* Millennium Plus bus cycle sequence as per figure 2, section 2.4 */
++ WriteDOC((DOC_FLASH_CE | DOC_FLASH_WP), docptr, Mplus_FlashSelect);
++
++ /* Reset the chip, see Software Requirement 11.4 item 1. */
++ DoC_Command(docptr, NAND_CMD_RESET, 0);
++ DoC_WaitReady(docptr);
++
++ fofs = from;
++ DoC_Command(docptr, DoC_GetDataOffset(mtd, &fofs), 0);
++ DoC_Address(this, 3, fofs, 0, 0x00);
++ WriteDOC(0, docptr, Mplus_FlashControl);
++ DoC_WaitReady(docptr);
++
++ /* disable the ECC engine */
++ WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf);
++
++ ReadDOC(docptr, Mplus_ReadPipeInit);
++ ReadDOC(docptr, Mplus_ReadPipeInit);
++
++ /* Read the data via the internal pipeline through CDSN IO
++ register, see Pipelined Read Operations 11.3 */
++ MemReadDOC(docptr, buf, 1054);
++ buf[1054] = ReadDOC(docptr, Mplus_LastDataRead);
++ buf[1055] = ReadDOC(docptr, Mplus_LastDataRead);
++
++ memset(&c[0], 0, sizeof(c));
++ printk("DUMP OFFSET=%x:\n", (int)from);
++
++ for (i = 0, bp = &buf[0]; (i < 1056); i++) {
++ if ((i % 16) == 0)
++ printk("%08x: ", i);
++ printk(" %02x", *bp);
++ c[(i & 0xf)] = ((*bp >= 0x20) && (*bp <= 0x7f)) ? *bp : '.';
++ bp++;
++ if (((i + 1) % 16) == 0)
++ printk(" %s\n", c);
++ }
++ printk("\n");
++
++ /* Disable flash internally */
++ WriteDOC(0, docptr, Mplus_FlashSelect);
++
++ return 0;
++}
++#endif
++
++static int doc_read(struct mtd_info *mtd, loff_t from, size_t len,
++ size_t *retlen, u_char *buf)
++{
++ /* Just a special case of doc_read_ecc */
++ return doc_read_ecc(mtd, from, len, retlen, buf, NULL, NULL);
++}
++
++static int doc_read_ecc(struct mtd_info *mtd, loff_t from, size_t len,
++ size_t *retlen, u_char *buf, u_char *eccbuf,
++ struct nand_oobinfo *oobsel)
++{
++ int ret, i;
++ volatile char dummy;
++ loff_t fofs;
++ unsigned char syndrome[6];
++ struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv;
++ unsigned long docptr = this->virtadr;
++ struct Nand *mychip = &this->chips[from >> (this->chipshift)];
++
++ /* Don't allow read past end of device */
++ if (from >= this->totlen)
++ return -EINVAL;
++
++ /* Don't allow a single read to cross a 512-byte block boundary */
++ if (from + len > ((from | 0x1ff) + 1))
++ len = ((from | 0x1ff) + 1) - from;
++
++ DoC_CheckASIC(docptr);
++
++ /* Find the chip which is to be used and select it */
++ if (this->curfloor != mychip->floor) {
++ DoC_SelectFloor(docptr, mychip->floor);
++ DoC_SelectChip(docptr, mychip->chip);
++ } else if (this->curchip != mychip->chip) {
++ DoC_SelectChip(docptr, mychip->chip);
++ }
++ this->curfloor = mychip->floor;
++ this->curchip = mychip->chip;
++
++ /* Millennium Plus bus cycle sequence as per figure 2, section 2.4 */
++ WriteDOC((DOC_FLASH_CE | DOC_FLASH_WP), docptr, Mplus_FlashSelect);
++
++ /* Reset the chip, see Software Requirement 11.4 item 1. */
++ DoC_Command(docptr, NAND_CMD_RESET, 0);
++ DoC_WaitReady(docptr);
++
++ fofs = from;
++ DoC_Command(docptr, DoC_GetDataOffset(mtd, &fofs), 0);
++ DoC_Address(this, 3, fofs, 0, 0x00);
++ WriteDOC(0, docptr, Mplus_FlashControl);
++ DoC_WaitReady(docptr);
++
++ if (eccbuf) {
++ /* init the ECC engine, see Reed-Solomon EDC/ECC 11.1 .*/
++ WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf);
++ WriteDOC(DOC_ECC_EN, docptr, Mplus_ECCConf);
++ } else {
++ /* disable the ECC engine */
++ WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf);
++ }
++
++ /* Let the caller know we completed it */
++ *retlen = len;
++ ret = 0;
++
++ ReadDOC(docptr, Mplus_ReadPipeInit);
++ ReadDOC(docptr, Mplus_ReadPipeInit);
++
++ if (eccbuf) {
++ /* Read the data via the internal pipeline through CDSN IO
++ register, see Pipelined Read Operations 11.3 */
++ MemReadDOC(docptr, buf, len);
++
++ /* Read the ECC data following raw data */
++ MemReadDOC(docptr, eccbuf, 4);
++ eccbuf[4] = ReadDOC(docptr, Mplus_LastDataRead);
++ eccbuf[5] = ReadDOC(docptr, Mplus_LastDataRead);
++
++ /* Flush the pipeline */
++ dummy = ReadDOC(docptr, Mplus_ECCConf);
++ dummy = ReadDOC(docptr, Mplus_ECCConf);
++
++ /* Check the ECC Status */
++ if (ReadDOC(docptr, Mplus_ECCConf) & 0x80) {
++ int nb_errors;
++ /* There was an ECC error */
++#ifdef ECC_DEBUG
++ printk("DiskOnChip ECC Error: Read at %lx\n", (long)from);
++#endif
++ /* Read the ECC syndrom through the DiskOnChip ECC logic.
++ These syndrome will be all ZERO when there is no error */
++ for (i = 0; i < 6; i++)
++ syndrome[i] = ReadDOC(docptr, Mplus_ECCSyndrome0 + i);
++
++ nb_errors = doc_decode_ecc(buf, syndrome);
++#ifdef ECC_DEBUG
++ printk("ECC Errors corrected: %x\n", nb_errors);
++#endif
++ if (nb_errors < 0) {
++ /* We return error, but have actually done the read. Not that
++ this can be told to user-space, via sys_read(), but at least
++ MTD-aware stuff can know about it by checking *retlen */
++#ifdef ECC_DEBUG
++ printk("%s(%d): Millennium Plus ECC error (from=0x%x:\n",
++ __FILE__, __LINE__, (int)from);
++ printk(" syndrome= %02x:%02x:%02x:%02x:%02x:"
++ "%02x\n",
++ syndrome[0], syndrome[1], syndrome[2],
++ syndrome[3], syndrome[4], syndrome[5]);
++ printk(" eccbuf= %02x:%02x:%02x:%02x:%02x:"
++ "%02x\n",
++ eccbuf[0], eccbuf[1], eccbuf[2],
++ eccbuf[3], eccbuf[4], eccbuf[5]);
++#endif
++ ret = -EIO;
++ }
++ }
++
++#ifdef PSYCHO_DEBUG
++ printk("ECC DATA at %lx: %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n",
++ (long)from, eccbuf[0], eccbuf[1], eccbuf[2], eccbuf[3],
++ eccbuf[4], eccbuf[5]);
++#endif
++
++ /* disable the ECC engine */
++ WriteDOC(DOC_ECC_DIS, docptr , Mplus_ECCConf);
++ } else {
++ /* Read the data via the internal pipeline through CDSN IO
++ register, see Pipelined Read Operations 11.3 */
++ MemReadDOC(docptr, buf, len-2);
++ buf[len-2] = ReadDOC(docptr, Mplus_LastDataRead);
++ buf[len-1] = ReadDOC(docptr, Mplus_LastDataRead);
++ }
++
++ /* Disable flash internally */
++ WriteDOC(0, docptr, Mplus_FlashSelect);
++
++ return ret;
++}
++
++static int doc_write(struct mtd_info *mtd, loff_t to, size_t len,
++ size_t *retlen, const u_char *buf)
++{
++ char eccbuf[6];
++ return doc_write_ecc(mtd, to, len, retlen, buf, eccbuf, NULL);
++}
++
++static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len,
++ size_t *retlen, const u_char *buf, u_char *eccbuf,
++ struct nand_oobinfo *oobsel)
++{
++ int i, before, ret = 0;
++ loff_t fto;
++ volatile char dummy;
++ struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv;
++ unsigned long docptr = this->virtadr;
++ struct Nand *mychip = &this->chips[to >> (this->chipshift)];
++
++ /* Don't allow write past end of device */
++ if (to >= this->totlen)
++ return -EINVAL;
++
++ /* Don't allow writes which aren't exactly one block (512 bytes) */
++ if ((to & 0x1ff) || (len != 0x200))
++ return -EINVAL;
++
++ /* Determine position of OOB flags, before or after data */
++ before = (this->interleave && (to & 0x200));
++
++ DoC_CheckASIC(docptr);
++
++ /* Find the chip which is to be used and select it */
++ if (this->curfloor != mychip->floor) {
++ DoC_SelectFloor(docptr, mychip->floor);
++ DoC_SelectChip(docptr, mychip->chip);
++ } else if (this->curchip != mychip->chip) {
++ DoC_SelectChip(docptr, mychip->chip);
++ }
++ this->curfloor = mychip->floor;
++ this->curchip = mychip->chip;
++
++ /* Millennium Plus bus cycle sequence as per figure 2, section 2.4 */
++ WriteDOC(DOC_FLASH_CE, docptr, Mplus_FlashSelect);
++
++ /* Reset the chip, see Software Requirement 11.4 item 1. */
++ DoC_Command(docptr, NAND_CMD_RESET, 0);
++ DoC_WaitReady(docptr);
++
++ /* Set device to appropriate plane of flash */
++ fto = to;
++ WriteDOC(DoC_GetDataOffset(mtd, &fto), docptr, Mplus_FlashCmd);
++
++ /* On interleaved devices the flags for 2nd half 512 are before data */
++ if (eccbuf && before)
++ fto -= 2;
++
++ /* issue the Serial Data In command to initial the Page Program process */
++ DoC_Command(docptr, NAND_CMD_SEQIN, 0x00);
++ DoC_Address(this, 3, fto, 0x00, 0x00);
++
++ /* Disable the ECC engine */
++ WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf);
++
++ if (eccbuf) {
++ if (before) {
++ /* Write the block status BLOCK_USED (0x5555) */
++ WriteDOC(0x55, docptr, Mil_CDSN_IO);
++ WriteDOC(0x55, docptr, Mil_CDSN_IO);
++ }
++
++ /* init the ECC engine, see Reed-Solomon EDC/ECC 11.1 .*/
++ WriteDOC(DOC_ECC_EN | DOC_ECC_RW, docptr, Mplus_ECCConf);
++ }
++
++ MemWriteDOC(docptr, (unsigned char *) buf, len);
++
++ if (eccbuf) {
++ /* Write ECC data to flash, the ECC info is generated by
++ the DiskOnChip ECC logic see Reed-Solomon EDC/ECC 11.1 */
++ DoC_Delay(docptr, 3);
++
++ /* Read the ECC data through the DiskOnChip ECC logic */
++ for (i = 0; i < 6; i++)
++ eccbuf[i] = ReadDOC(docptr, Mplus_ECCSyndrome0 + i);
++
++ /* disable the ECC engine */
++ WriteDOC(DOC_ECC_DIS, docptr, Mplus_ECCConf);
++
++ /* Write the ECC data to flash */
++ MemWriteDOC(docptr, eccbuf, 6);
++
++ if (!before) {
++ /* Write the block status BLOCK_USED (0x5555) */
++ WriteDOC(0x55, docptr, Mil_CDSN_IO+6);
++ WriteDOC(0x55, docptr, Mil_CDSN_IO+7);
++ }
++
++#ifdef PSYCHO_DEBUG
++ printk("OOB data at %lx is %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n",
++ (long) to, eccbuf[0], eccbuf[1], eccbuf[2], eccbuf[3],
++ eccbuf[4], eccbuf[5]);
++#endif
++ }
++
++ WriteDOC(0x00, docptr, Mplus_WritePipeTerm);
++ WriteDOC(0x00, docptr, Mplus_WritePipeTerm);
++
++ /* Commit the Page Program command and wait for ready
++ see Software Requirement 11.4 item 1.*/
++ DoC_Command(docptr, NAND_CMD_PAGEPROG, 0x00);
++ DoC_WaitReady(docptr);
++
++ /* Read the status of the flash device through CDSN IO register
++ see Software Requirement 11.4 item 5.*/
++ DoC_Command(docptr, NAND_CMD_STATUS, 0);
++ dummy = ReadDOC(docptr, Mplus_ReadPipeInit);
++ dummy = ReadDOC(docptr, Mplus_ReadPipeInit);
++ DoC_Delay(docptr, 2);
++ if ((dummy = ReadDOC(docptr, Mplus_LastDataRead)) & 1) {
++ printk("MTD: Error 0x%x programming at 0x%x\n", dummy, (int)to);
++ /* Error in programming
++ FIXME: implement Bad Block Replacement (in nftl.c ??) */
++ *retlen = 0;
++ ret = -EIO;
++ }
++ dummy = ReadDOC(docptr, Mplus_LastDataRead);
++
++ /* Disable flash internally */
++ WriteDOC(0, docptr, Mplus_FlashSelect);
++
++ /* Let the caller know we completed it */
++ *retlen = len;
++
++ return ret;
++}
++
++static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
++ size_t *retlen, u_char *buf)
++{
++ loff_t fofs, base;
++ struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv;
++ unsigned long docptr = this->virtadr;
++ struct Nand *mychip = &this->chips[ofs >> this->chipshift];
++ size_t i, size, got, want;
++
++ DoC_CheckASIC(docptr);
++
++ /* Find the chip which is to be used and select it */
++ if (this->curfloor != mychip->floor) {
++ DoC_SelectFloor(docptr, mychip->floor);
++ DoC_SelectChip(docptr, mychip->chip);
++ } else if (this->curchip != mychip->chip) {
++ DoC_SelectChip(docptr, mychip->chip);
++ }
++ this->curfloor = mychip->floor;
++ this->curchip = mychip->chip;
++
++ /* Millennium Plus bus cycle sequence as per figure 2, section 2.4 */
++ WriteDOC((DOC_FLASH_CE | DOC_FLASH_WP), docptr, Mplus_FlashSelect);
++
++ /* disable the ECC engine */
++ WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf);
++ DoC_WaitReady(docptr);
++
++ /* Maximum of 16 bytes in the OOB region, so limit read to that */
++ if (len > 16)
++ len = 16;
++ got = 0;
++ want = len;
++
++ for (i = 0; ((i < 3) && (want > 0)); i++) {
++ /* Figure out which region we are accessing... */
++ fofs = ofs;
++ base = ofs & 0xf;
++ if (!this->interleave) {
++ DoC_Command(docptr, NAND_CMD_READOOB, 0);
++ size = 16 - base;
++ } else if (base < 6) {
++ DoC_Command(docptr, DoC_GetECCOffset(mtd, &fofs), 0);
++ size = 6 - base;
++ } else if (base < 8) {
++ DoC_Command(docptr, DoC_GetFlagsOffset(mtd, &fofs), 0);
++ size = 8 - base;
++ } else {
++ DoC_Command(docptr, DoC_GetHdrOffset(mtd, &fofs), 0);
++ size = 16 - base;
++ }
++ if (size > want)
++ size = want;
++
++ /* Issue read command */
++ DoC_Address(this, 3, fofs, 0, 0x00);
++ WriteDOC(0, docptr, Mplus_FlashControl);
++ DoC_WaitReady(docptr);
++
++ ReadDOC(docptr, Mplus_ReadPipeInit);
++ ReadDOC(docptr, Mplus_ReadPipeInit);
++ MemReadDOC(docptr, &buf[got], size - 2);
++ buf[got + size - 2] = ReadDOC(docptr, Mplus_LastDataRead);
++ buf[got + size - 1] = ReadDOC(docptr, Mplus_LastDataRead);
++
++ ofs += size;
++ got += size;
++ want -= size;
++ }
++
++ /* Disable flash internally */
++ WriteDOC(0, docptr, Mplus_FlashSelect);
++
++ *retlen = len;
++ return 0;
++}
++
++static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
++ size_t *retlen, const u_char *buf)
++{
++ volatile char dummy;
++ loff_t fofs, base;
++ struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv;
++ unsigned long docptr = this->virtadr;
++ struct Nand *mychip = &this->chips[ofs >> this->chipshift];
++ size_t i, size, got, want;
++ int ret = 0;
++
++ DoC_CheckASIC(docptr);
++
++ /* Find the chip which is to be used and select it */
++ if (this->curfloor != mychip->floor) {
++ DoC_SelectFloor(docptr, mychip->floor);
++ DoC_SelectChip(docptr, mychip->chip);
++ } else if (this->curchip != mychip->chip) {
++ DoC_SelectChip(docptr, mychip->chip);
++ }
++ this->curfloor = mychip->floor;
++ this->curchip = mychip->chip;
++
++ /* Millennium Plus bus cycle sequence as per figure 2, section 2.4 */
++ WriteDOC(DOC_FLASH_CE, docptr, Mplus_FlashSelect);
++
++
++ /* Maximum of 16 bytes in the OOB region, so limit write to that */
++ if (len > 16)
++ len = 16;
++ got = 0;
++ want = len;
++
++ for (i = 0; ((i < 3) && (want > 0)); i++) {
++ /* Reset the chip, see Software Requirement 11.4 item 1. */
++ DoC_Command(docptr, NAND_CMD_RESET, 0);
++ DoC_WaitReady(docptr);
++
++ /* Figure out which region we are accessing... */
++ fofs = ofs;
++ base = ofs & 0x0f;
++ if (!this->interleave) {
++ WriteDOC(NAND_CMD_READOOB, docptr, Mplus_FlashCmd);
++ size = 16 - base;
++ } else if (base < 6) {
++ WriteDOC(DoC_GetECCOffset(mtd, &fofs), docptr, Mplus_FlashCmd);
++ size = 6 - base;
++ } else if (base < 8) {
++ WriteDOC(DoC_GetFlagsOffset(mtd, &fofs), docptr, Mplus_FlashCmd);
++ size = 8 - base;
++ } else {
++ WriteDOC(DoC_GetHdrOffset(mtd, &fofs), docptr, Mplus_FlashCmd);
++ size = 16 - base;
++ }
++ if (size > want)
++ size = want;
++
++ /* Issue the Serial Data In command to initial the Page Program process */
++ DoC_Command(docptr, NAND_CMD_SEQIN, 0x00);
++ DoC_Address(this, 3, fofs, 0, 0x00);
++
++ /* Disable the ECC engine */
++ WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf);
++
++ /* Write the data via the internal pipeline through CDSN IO
++ register, see Pipelined Write Operations 11.2 */
++ MemWriteDOC(docptr, (unsigned char *) &buf[got], size);
++ WriteDOC(0x00, docptr, Mplus_WritePipeTerm);
++ WriteDOC(0x00, docptr, Mplus_WritePipeTerm);
++
++ /* Commit the Page Program command and wait for ready
++ see Software Requirement 11.4 item 1.*/
++ DoC_Command(docptr, NAND_CMD_PAGEPROG, 0x00);
++ DoC_WaitReady(docptr);
++
++ /* Read the status of the flash device through CDSN IO register
++ see Software Requirement 11.4 item 5.*/
++ DoC_Command(docptr, NAND_CMD_STATUS, 0x00);
++ dummy = ReadDOC(docptr, Mplus_ReadPipeInit);
++ dummy = ReadDOC(docptr, Mplus_ReadPipeInit);
++ DoC_Delay(docptr, 2);
++ if ((dummy = ReadDOC(docptr, Mplus_LastDataRead)) & 1) {
++ printk("MTD: Error 0x%x programming oob at 0x%x\n",
++ dummy, (int)ofs);
++ /* FIXME: implement Bad Block Replacement */
++ *retlen = 0;
++ ret = -EIO;
++ }
++ dummy = ReadDOC(docptr, Mplus_LastDataRead);
++
++ ofs += size;
++ got += size;
++ want -= size;
++ }
++
++ /* Disable flash internally */
++ WriteDOC(0, docptr, Mplus_FlashSelect);
++
++ *retlen = len;
++ return ret;
++}
++
++int doc_erase(struct mtd_info *mtd, struct erase_info *instr)
++{
++ volatile char dummy;
++ struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv;
++ __u32 ofs = instr->addr;
++ __u32 len = instr->len;
++ unsigned long docptr = this->virtadr;
++ struct Nand *mychip = &this->chips[ofs >> this->chipshift];
++
++ DoC_CheckASIC(docptr);
++
++ if (len != mtd->erasesize)
++ printk(KERN_WARNING "MTD: Erase not right size (%x != %x)n",
++ len, mtd->erasesize);
++
++ /* Find the chip which is to be used and select it */
++ if (this->curfloor != mychip->floor) {
++ DoC_SelectFloor(docptr, mychip->floor);
++ DoC_SelectChip(docptr, mychip->chip);
++ } else if (this->curchip != mychip->chip) {
++ DoC_SelectChip(docptr, mychip->chip);
++ }
++ this->curfloor = mychip->floor;
++ this->curchip = mychip->chip;
++
++ instr->state = MTD_ERASE_PENDING;
++
++ /* Millennium Plus bus cycle sequence as per figure 2, section 2.4 */
++ WriteDOC(DOC_FLASH_CE, docptr, Mplus_FlashSelect);
++
++ DoC_Command(docptr, NAND_CMD_RESET, 0x00);
++ DoC_WaitReady(docptr);
++
++ DoC_Command(docptr, NAND_CMD_ERASE1, 0);
++ DoC_Address(this, 2, ofs, 0, 0x00);
++ DoC_Command(docptr, NAND_CMD_ERASE2, 0);
++ DoC_WaitReady(docptr);
++ instr->state = MTD_ERASING;
++
++ /* Read the status of the flash device through CDSN IO register
++ see Software Requirement 11.4 item 5. */
++ DoC_Command(docptr, NAND_CMD_STATUS, 0);
++ dummy = ReadDOC(docptr, Mplus_ReadPipeInit);
++ dummy = ReadDOC(docptr, Mplus_ReadPipeInit);
++ if ((dummy = ReadDOC(docptr, Mplus_LastDataRead)) & 1) {
++ printk("MTD: Error 0x%x erasing at 0x%x\n", dummy, ofs);
++ /* FIXME: implement Bad Block Replacement (in nftl.c ??) */
++ instr->state = MTD_ERASE_FAILED;
++ } else {
++ instr->state = MTD_ERASE_DONE;
++ }
++ dummy = ReadDOC(docptr, Mplus_LastDataRead);
++
++ /* Disable flash internally */
++ WriteDOC(0, docptr, Mplus_FlashSelect);
++
++ if (instr->callback)
++ instr->callback(instr);
++
++ return 0;
++}
++
++/****************************************************************************
++ *
++ * Module stuff
++ *
++ ****************************************************************************/
++
++int __init init_doc2001plus(void)
++{
++ inter_module_register(im_name, THIS_MODULE, &DoCMilPlus_init);
++ return 0;
++}
++
++static void __exit cleanup_doc2001plus(void)
++{
++ struct mtd_info *mtd;
++ struct DiskOnChip *this;
++
++ while ((mtd=docmilpluslist)) {
++ this = (struct DiskOnChip *)mtd->priv;
++ docmilpluslist = this->nextdoc;
++
++ del_mtd_device(mtd);
++
++ iounmap((void *)this->virtadr);
++ kfree(this->chips);
++ kfree(mtd);
++ }
++ inter_module_unregister(im_name);
++}
++
++module_exit(cleanup_doc2001plus);
++module_init(init_doc2001plus);
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Greg Ungerer <gerg@snapgear.com> et al.");
++MODULE_DESCRIPTION("Driver for DiskOnChip Millennium Plus");
+diff -Nurb linux-mips-2.4.27/drivers/mtd/devices/docecc.c linux/drivers/mtd/devices/docecc.c
+--- linux-mips-2.4.27/drivers/mtd/devices/docecc.c 2001-11-05 21:15:52.000000000 +0100
++++ linux/drivers/mtd/devices/docecc.c 2004-11-19 10:25:11.851207088 +0100
+@@ -7,7 +7,7 @@
+ * Author: Fabrice Bellard (fabrice.bellard@netgem.com)
+ * Copyright (C) 2000 Netgem S.A.
+ *
+- * $Id$
++ * $Id$
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -519,6 +519,8 @@
+ return nb_errors;
+ }
+
++EXPORT_SYMBOL_GPL(doc_decode_ecc);
++
+ MODULE_LICENSE("GPL");
+ MODULE_AUTHOR("Fabrice Bellard <fabrice.bellard@netgem.com>");
+ MODULE_DESCRIPTION("ECC code for correcting errors detected by DiskOnChip 2000 and Millennium ECC hardware");
+diff -Nurb linux-mips-2.4.27/drivers/mtd/devices/docprobe.c linux/drivers/mtd/devices/docprobe.c
+--- linux-mips-2.4.27/drivers/mtd/devices/docprobe.c 2003-06-16 01:42:21.000000000 +0200
++++ linux/drivers/mtd/devices/docprobe.c 2004-11-19 10:25:11.853206784 +0100
+@@ -4,7 +4,7 @@
+ /* (C) 1999 Machine Vision Holdings, Inc. */
+ /* (C) 1999-2003 David Woodhouse <dwmw2@infradead.org> */
+
+-/* $Id$ */
++/* $Id$ */
+
+
+
+@@ -31,14 +31,12 @@
+ /* DOC_SINGLE_DRIVER:
+ Millennium driver has been merged into DOC2000 driver.
+
+- The newly-merged driver doesn't appear to work for writing. It's the
+- same with the DiskOnChip 2000 and the Millennium. If you have a
+- Millennium and you want write support to work, remove the definition
+- of DOC_SINGLE_DRIVER below to use the old doc2001-specific driver.
+-
+- Otherwise, it's left on in the hope that it'll annoy someone with
+- a Millennium enough that they go through and work out what the
+- difference is :)
++ The old Millennium-only driver has been retained just in case there
++ are problems with the new code. If the combined driver doesn't work
++ for you, you can try the old one by undefining DOC_SINGLE_DRIVER
++ below and also enabling it in your configuration. If this fixes the
++ problems, please send a report to the MTD mailing list at
++ <linux-mtd@lists.infradead.org>.
+ */
+ #define DOC_SINGLE_DRIVER
+
+@@ -47,18 +45,15 @@
+ #include <linux/module.h>
+ #include <asm/errno.h>
+ #include <asm/io.h>
+-#include <asm/uaccess.h>
+-#include <linux/miscdevice.h>
+-#include <linux/pci.h>
+ #include <linux/delay.h>
+ #include <linux/slab.h>
+-#include <linux/sched.h>
+ #include <linux/init.h>
+ #include <linux/types.h>
+
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/nand.h>
+ #include <linux/mtd/doc2000.h>
++#include <linux/mtd/compatmac.h>
+
+ /* Where to look for the devices? */
+ #ifndef CONFIG_MTD_DOCPROBE_ADDRESS
+@@ -92,17 +87,17 @@
+ 0xff000000,
+ #elif defined(CONFIG_MOMENCO_OCELOT_G) || defined (CONFIG_MOMENCO_OCELOT_C)
+ 0xff000000,
+-#else
++##else
+ #warning Unknown architecture for DiskOnChip. No default probe locations defined
+ #endif
+- 0 };
++ 0xffffffff };
+
+ /* doccheck: Probe a given memory window to see if there's a DiskOnChip present */
+
+ static inline int __init doccheck(unsigned long potential, unsigned long physadr)
+ {
+ unsigned long window=potential;
+- unsigned char tmp, ChipID;
++ unsigned char tmp, tmpb, tmpc, ChipID;
+ #ifndef DOC_PASSIVE_PROBE
+ unsigned char tmp2;
+ #endif
+@@ -140,26 +135,80 @@
+ window, DOCControl);
+ #endif /* !DOC_PASSIVE_PROBE */
+
++ /* We need to read the ChipID register four times. For some
++ newer DiskOnChip 2000 units, the first three reads will
++ return the DiskOnChip Millennium ident. Don't ask. */
+ ChipID = ReadDOC(window, ChipID);
+
+ switch (ChipID) {
+ case DOC_ChipID_Doc2k:
+ /* Check the TOGGLE bit in the ECC register */
+ tmp = ReadDOC(window, 2k_ECCStatus) & DOC_TOGGLE_BIT;
+- if ((ReadDOC(window, 2k_ECCStatus) & DOC_TOGGLE_BIT) != tmp)
++ tmpb = ReadDOC(window, 2k_ECCStatus) & DOC_TOGGLE_BIT;
++ tmpc = ReadDOC(window, 2k_ECCStatus) & DOC_TOGGLE_BIT;
++ if (tmp != tmpb && tmp == tmpc)
+ return ChipID;
+ break;
+
+ case DOC_ChipID_DocMil:
++ /* Check for the new 2000 with Millennium ASIC */
++ ReadDOC(window, ChipID);
++ ReadDOC(window, ChipID);
++ if (ReadDOC(window, ChipID) != DOC_ChipID_DocMil)
++ ChipID = DOC_ChipID_Doc2kTSOP;
++
+ /* Check the TOGGLE bit in the ECC register */
+ tmp = ReadDOC(window, ECCConf) & DOC_TOGGLE_BIT;
+- if ((ReadDOC(window, ECCConf) & DOC_TOGGLE_BIT) != tmp)
++ tmpb = ReadDOC(window, ECCConf) & DOC_TOGGLE_BIT;
++ tmpc = ReadDOC(window, ECCConf) & DOC_TOGGLE_BIT;
++ if (tmp != tmpb && tmp == tmpc)
++ return ChipID;
++ break;
++
++ case DOC_ChipID_DocMilPlus16:
++ case DOC_ChipID_DocMilPlus32:
++ case 0:
++ /* Possible Millennium+, need to do more checks */
++#ifndef DOC_PASSIVE_PROBE
++ /* Possibly release from power down mode */
++ for (tmp = 0; (tmp < 4); tmp++)
++ ReadDOC(window, Mplus_Power);
++
++ /* Reset the DiskOnChip ASIC */
++ tmp = DOC_MODE_RESET | DOC_MODE_MDWREN | DOC_MODE_RST_LAT |
++ DOC_MODE_BDECT;
++ WriteDOC(tmp, window, Mplus_DOCControl);
++ WriteDOC(~tmp, window, Mplus_CtrlConfirm);
++
++ mdelay(1);
++ /* Enable the DiskOnChip ASIC */
++ tmp = DOC_MODE_NORMAL | DOC_MODE_MDWREN | DOC_MODE_RST_LAT |
++ DOC_MODE_BDECT;
++ WriteDOC(tmp, window, Mplus_DOCControl);
++ WriteDOC(~tmp, window, Mplus_CtrlConfirm);
++ mdelay(1);
++#endif /* !DOC_PASSIVE_PROBE */
++
++ ChipID = ReadDOC(window, ChipID);
++
++ switch (ChipID) {
++ case DOC_ChipID_DocMilPlus16:
++ case DOC_ChipID_DocMilPlus32:
++ /* Check the TOGGLE bit in the toggle register */
++ tmp = ReadDOC(window, Mplus_Toggle) & DOC_TOGGLE_BIT;
++ tmpb = ReadDOC(window, Mplus_Toggle) & DOC_TOGGLE_BIT;
++ tmpc = ReadDOC(window, Mplus_Toggle) & DOC_TOGGLE_BIT;
++ if (tmp != tmpb && tmp == tmpc)
+ return ChipID;
++ default:
+ break;
++ }
++ /* FALL TRHU */
+
+ default:
+-#ifndef CONFIG_MTD_DOCPROBE_55AA
+- printk(KERN_WARNING "Possible DiskOnChip with unknown ChipID %2.2X found at 0x%lx\n",
++
++#ifdef CONFIG_MTD_DOCPROBE_55AA
++ printk(KERN_DEBUG "Possible DiskOnChip with unknown ChipID %2.2X found at 0x%lx\n",
+ ChipID, physadr);
+ #endif
+ #ifndef DOC_PASSIVE_PROBE
+@@ -200,6 +249,12 @@
+ return;
+
+ if ((ChipID = doccheck(docptr, physadr))) {
++ if (ChipID == DOC_ChipID_Doc2kTSOP) {
++ /* Remove this at your own peril. The hardware driver works but nothing prevents you from erasing bad blocks */
++ printk(KERN_NOTICE "Refusing to drive DiskOnChip 2000 TSOP until Bad Block Table is correctly supported by INFTL\n");
++ iounmap((void *)docptr);
++ return;
++ }
+ docfound = 1;
+ mtd = kmalloc(sizeof(struct DiskOnChip) + sizeof(struct mtd_info), GFP_KERNEL);
+
+@@ -221,6 +276,12 @@
+ sprintf(namebuf, "with ChipID %2.2X", ChipID);
+
+ switch(ChipID) {
++ case DOC_ChipID_Doc2kTSOP:
++ name="2000 TSOP";
++ im_funcname = "DoC2k_init";
++ im_modname = "doc2000";
++ break;
++
+ case DOC_ChipID_Doc2k:
+ name="2000";
+ im_funcname = "DoC2k_init";
+@@ -237,6 +298,13 @@
+ im_modname = "doc2001";
+ #endif /* DOC_SINGLE_DRIVER */
+ break;
++
++ case DOC_ChipID_DocMilPlus16:
++ case DOC_ChipID_DocMilPlus32:
++ name="MillenniumPlus";
++ im_funcname = "DoCMilPlus_init";
++ im_modname = "doc2001plus";
++ break;
+ }
+
+ if (im_funcname)
+@@ -248,6 +316,7 @@
+ return;
+ }
+ printk(KERN_NOTICE "Cannot find driver for DiskOnChip %s at 0x%lX\n", name, physadr);
++ kfree(mtd);
+ }
+ iounmap((void *)docptr);
+ }
+@@ -267,7 +336,7 @@
+ printk(KERN_INFO "Using configured DiskOnChip probe address 0x%lx\n", doc_config_location);
+ DoC_Probe(doc_config_location);
+ } else {
+- for (i=0; doc_locations[i]; i++) {
++ for (i=0; (doc_locations[i] != 0xffffffff); i++) {
+ DoC_Probe(doc_locations[i]);
+ }
+ }
+@@ -275,11 +344,7 @@
+ found, so the user knows we at least tried. */
+ if (!docfound)
+ printk(KERN_INFO "No recognised DiskOnChip devices found\n");
+- /* So it looks like we've been used and we get unloaded */
+- MOD_INC_USE_COUNT;
+- MOD_DEC_USE_COUNT;
+- return 0;
+-
++ return -EAGAIN;
+ }
+
+ module_init(init_doc);
+diff -Nurb linux-mips-2.4.27/drivers/mtd/devices/lart.c linux/drivers/mtd/devices/lart.c
+--- linux-mips-2.4.27/drivers/mtd/devices/lart.c 2001-11-05 21:15:52.000000000 +0100
++++ linux/drivers/mtd/devices/lart.c 2004-11-19 10:25:11.854206632 +0100
+@@ -2,7 +2,7 @@
+ /*
+ * MTD driver for the 28F160F3 Flash Memory (non-CFI) on LART.
+ *
+- * $Id$
++ * $Id$
+ *
+ * Author: Abraham vd Merwe <abraham@2d3d.co.za>
+ *
+@@ -584,45 +584,40 @@
+
+ static struct mtd_info mtd;
+
+-static struct mtd_erase_region_info erase_regions[] =
+-{
++static struct mtd_erase_region_info erase_regions[] = {
+ /* parameter blocks */
+ {
+- offset: 0x00000000,
+- erasesize: FLASH_BLOCKSIZE_PARAM,
+- numblocks: FLASH_NUMBLOCKS_16m_PARAM
++ .offset = 0x00000000,
++ .erasesize = FLASH_BLOCKSIZE_PARAM,
++ .numblocks = FLASH_NUMBLOCKS_16m_PARAM,
+ },
+ /* main blocks */
+ {
+- offset: FLASH_BLOCKSIZE_PARAM * FLASH_NUMBLOCKS_16m_PARAM,
+- erasesize: FLASH_BLOCKSIZE_MAIN,
+- numblocks: FLASH_NUMBLOCKS_16m_MAIN
++ .offset = FLASH_BLOCKSIZE_PARAM * FLASH_NUMBLOCKS_16m_PARAM,
++ .erasesize = FLASH_BLOCKSIZE_MAIN,
++ .numblocks = FLASH_NUMBLOCKS_16m_MAIN,
+ }
+ };
+
+ #ifdef HAVE_PARTITIONS
+-static struct mtd_partition lart_partitions[] =
+-{
++static struct mtd_partition lart_partitions[] = {
+ /* blob */
+ {
+- name: "blob",
+- offset: BLOB_START,
+- size: BLOB_LEN,
+- mask_flags: 0
++ .name = "blob",
++ .offset = BLOB_START,
++ .size = BLOB_LEN,
+ },
+ /* kernel */
+ {
+- name: "kernel",
+- offset: KERNEL_START, /* MTDPART_OFS_APPEND */
+- size: KERNEL_LEN,
+- mask_flags: 0
++ .name = "kernel",
++ .offset = KERNEL_START, /* MTDPART_OFS_APPEND */
++ .size = KERNEL_LEN,
+ },
+ /* initial ramdisk / file system */
+ {
+- name: "file system",
+- offset: INITRD_START, /* MTDPART_OFS_APPEND */
+- size: INITRD_LEN, /* MTDPART_SIZ_FULL */
+- mask_flags: 0
++ .name = "file system",
++ .offset = INITRD_START, /* MTDPART_OFS_APPEND */
++ .size = INITRD_LEN, /* MTDPART_SIZ_FULL */
+ }
+ };
+ #endif
+@@ -646,10 +641,10 @@
+ mtd.erasesize = FLASH_BLOCKSIZE_MAIN;
+ mtd.numeraseregions = NB_OF (erase_regions);
+ mtd.eraseregions = erase_regions;
+- mtd.module = THIS_MODULE;
+ mtd.erase = flash_erase;
+ mtd.read = flash_read;
+ mtd.write = flash_write;
++ mtd.owner = THIS_MODULE;
+
+ #ifdef LART_DEBUG
+ printk (KERN_DEBUG
+diff -Nurb linux-mips-2.4.27/drivers/mtd/devices/ms02-nv.c linux/drivers/mtd/devices/ms02-nv.c
+--- linux-mips-2.4.27/drivers/mtd/devices/ms02-nv.c 2004-07-30 12:22:40.000000000 +0200
++++ linux/drivers/mtd/devices/ms02-nv.c 2004-11-19 10:25:11.856206328 +0100
+@@ -6,7 +6,7 @@
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+- * $Id$
++ * $Id$
+ */
+
+ #include <linux/init.h>
+@@ -31,7 +31,7 @@
+ static char version[] __initdata =
+ "ms02-nv.c: v.1.0.0 13 Aug 2001 Maciej W. Rozycki.\n";
+
+-MODULE_AUTHOR("Maciej W. Rozycki <macro@linux-mips.org>");
++MODULE_AUTHOR("Maciej W. Rozycki <macro@ds2.pg.gda.pl>");
+ MODULE_DESCRIPTION("DEC MS02-NV NVRAM module driver");
+ MODULE_LICENSE("GPL");
+
+@@ -222,7 +222,7 @@
+ mtd->flags = MTD_CAP_RAM | MTD_XIP;
+ mtd->size = fixsize;
+ mtd->name = (char *)ms02nv_name;
+- mtd->module = THIS_MODULE;
++ mtd->owner = THIS_MODULE;
+ mtd->read = ms02nv_read;
+ mtd->write = ms02nv_write;
+
+diff -Nurb linux-mips-2.4.27/drivers/mtd/devices/mtdram.c linux/drivers/mtd/devices/mtdram.c
+--- linux-mips-2.4.27/drivers/mtd/devices/mtdram.c 2003-02-26 01:53:49.000000000 +0100
++++ linux/drivers/mtd/devices/mtdram.c 2004-11-19 10:25:11.859205872 +0100
+@@ -1,6 +1,6 @@
+ /*
+ * mtdram - a test mtd device
+- * $Id$
++ * $Id$
+ * Author: Alexander Larsson <alex@cendio.se>
+ *
+ * Copyright (c) 1999 Alexander Larsson <alex@cendio.se>
+@@ -13,6 +13,8 @@
+ #include <linux/module.h>
+ #include <linux/slab.h>
+ #include <linux/ioport.h>
++#include <linux/vmalloc.h>
++#include <linux/init.h>
+ #include <linux/mtd/compatmac.h>
+ #include <linux/mtd/mtd.h>
+
+@@ -136,7 +138,7 @@
+ mtd->erasesize = MTDRAM_ERASE_SIZE;
+ mtd->priv = mapped_address;
+
+- mtd->module = THIS_MODULE;
++ mtd->owner = THIS_MODULE;
+ mtd->erase = ram_erase;
+ mtd->point = ram_point;
+ mtd->unpoint = ram_unpoint;
+diff -Nurb linux-mips-2.4.27/drivers/mtd/devices/phram.c linux/drivers/mtd/devices/phram.c
+--- linux-mips-2.4.27/drivers/mtd/devices/phram.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/mtd/devices/phram.c 2004-11-19 10:25:11.860205720 +0100
+@@ -0,0 +1,362 @@
++/**
++ *
++ * $Id$
++ *
++ * Copyright (c) Jochen Schaeuble <psionic@psionic.de>
++ * 07/2003 rewritten by Joern Engel <joern@wh.fh-wedel.de>
++ *
++ * DISCLAIMER: This driver makes use of Rusty's excellent module code,
++ * so it will not work for 2.4 without changes and it wont work for 2.4
++ * as a module without major changes. Oh well!
++ *
++ * Usage:
++ *
++ * one commend line parameter per device, each in the form:
++ * phram=<name>,<start>,<len>
++ * <name> may be up to 63 characters.
++ * <start> and <len> can be octal, decimal or hexadecimal. If followed
++ * by "k", "M" or "G", the numbers will be interpreted as kilo, mega or
++ * gigabytes.
++ *
++ */
++
++#include <asm/io.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/list.h>
++#include <linux/module.h>
++#include <linux/moduleparam.h>
++#include <linux/mtd/mtd.h>
++
++#define ERROR(fmt, args...) printk(KERN_ERR "phram: " fmt , ## args)
++
++struct phram_mtd_list {
++ struct list_head list;
++ struct mtd_info *mtdinfo;
++};
++
++static LIST_HEAD(phram_list);
++
++
++
++int phram_erase(struct mtd_info *mtd, struct erase_info *instr)
++{
++ u_char *start = (u_char *)mtd->priv;
++
++ if (instr->addr + instr->len > mtd->size)
++ return -EINVAL;
++
++ memset(start + instr->addr, 0xff, instr->len);
++
++ /* This'll catch a few races. Free the thing before returning :)
++ * I don't feel at all ashamed. This kind of thing is possible anyway
++ * with flash, but unlikely.
++ */
++
++ instr->state = MTD_ERASE_DONE;
++
++ if (instr->callback)
++ (*(instr->callback))(instr);
++ else
++ kfree(instr);
++
++ return 0;
++}
++
++int phram_point(struct mtd_info *mtd, loff_t from, size_t len,
++ size_t *retlen, u_char **mtdbuf)
++{
++ u_char *start = (u_char *)mtd->priv;
++
++ if (from + len > mtd->size)
++ return -EINVAL;
++
++ *mtdbuf = start + from;
++ *retlen = len;
++ return 0;
++}
++
++void phram_unpoint(struct mtd_info *mtd, u_char *addr, loff_t from, size_t len)
++{
++}
++
++int phram_read(struct mtd_info *mtd, loff_t from, size_t len,
++ size_t *retlen, u_char *buf)
++{
++ u_char *start = (u_char *)mtd->priv;
++
++ if (from + len > mtd->size)
++ return -EINVAL;
++
++ memcpy(buf, start + from, len);
++
++ *retlen = len;
++ return 0;
++}
++
++int phram_write(struct mtd_info *mtd, loff_t to, size_t len,
++ size_t *retlen, const u_char *buf)
++{
++ u_char *start = (u_char *)mtd->priv;
++
++ if (to + len > mtd->size)
++ return -EINVAL;
++
++ memcpy(start + to, buf, len);
++
++ *retlen = len;
++ return 0;
++}
++
++
++
++static void unregister_devices(void)
++{
++ struct phram_mtd_list *this;
++
++ list_for_each_entry(this, &phram_list, list) {
++ del_mtd_device(this->mtdinfo);
++ iounmap(this->mtdinfo->priv);
++ kfree(this->mtdinfo);
++ kfree(this);
++ }
++}
++
++static int register_device(char *name, unsigned long start, unsigned long len)
++{
++ struct phram_mtd_list *new;
++ int ret = -ENOMEM;
++
++ new = kmalloc(sizeof(*new), GFP_KERNEL);
++ if (!new)
++ goto out0;
++
++ new->mtdinfo = kmalloc(sizeof(struct mtd_info), GFP_KERNEL);
++ if (!new->mtdinfo)
++ goto out1;
++
++ memset(new->mtdinfo, 0, sizeof(struct mtd_info));
++
++ ret = -EIO;
++ new->mtdinfo->priv = ioremap(start, len);
++ if (!new->mtdinfo->priv) {
++ ERROR("ioremap failed\n");
++ goto out2;
++ }
++
++
++ new->mtdinfo->name = name;
++ new->mtdinfo->size = len;
++ new->mtdinfo->flags = MTD_CAP_RAM | MTD_ERASEABLE | MTD_VOLATILE;
++ new->mtdinfo->erase = phram_erase;
++ new->mtdinfo->point = phram_point;
++ new->mtdinfo->unpoint = phram_unpoint;
++ new->mtdinfo->read = phram_read;
++ new->mtdinfo->write = phram_write;
++ new->mtdinfo->owner = THIS_MODULE;
++ new->mtdinfo->type = MTD_RAM;
++ new->mtdinfo->erasesize = 0x0;
++
++ ret = -EAGAIN;
++ if (add_mtd_device(new->mtdinfo)) {
++ ERROR("Failed to register new device\n");
++ goto out3;
++ }
++
++ list_add_tail(&new->list, &phram_list);
++ return 0;
++
++out3:
++ iounmap(new->mtdinfo->priv);
++out2:
++ kfree(new->mtdinfo);
++out1:
++ kfree(new);
++out0:
++ return ret;
++}
++
++static int ustrtoul(const char *cp, char **endp, unsigned int base)
++{
++ unsigned long result = simple_strtoul(cp, endp, base);
++
++ switch (**endp) {
++ case 'G':
++ result *= 1024;
++ case 'M':
++ result *= 1024;
++ case 'k':
++ result *= 1024;
++ endp++;
++ }
++ return result;
++}
++
++static int parse_num32(uint32_t *num32, const char *token)
++{
++ char *endp;
++ unsigned long n;
++
++ n = ustrtoul(token, &endp, 0);
++ if (*endp)
++ return -EINVAL;
++
++ *num32 = n;
++ return 0;
++}
++
++static int parse_name(char **pname, const char *token)
++{
++ size_t len;
++ char *name;
++
++ len = strlen(token) + 1;
++ if (len > 64)
++ return -ENOSPC;
++
++ name = kmalloc(len, GFP_KERNEL);
++ if (!name)
++ return -ENOMEM;
++
++ strcpy(name, token);
++
++ *pname = name;
++ return 0;
++}
++
++#define parse_err(fmt, args...) do { \
++ ERROR(fmt , ## args); \
++ return 0; \
++} while (0)
++
++static int phram_setup(const char *val, struct kernel_param *kp)
++{
++ char buf[64+12+12], *str = buf;
++ char *token[3];
++ char *name;
++ uint32_t start;
++ uint32_t len;
++ int i, ret;
++
++ if (strnlen(val, sizeof(str)) >= sizeof(str))
++ parse_err("parameter too long\n");
++
++ strcpy(str, val);
++
++ for (i=0; i<3; i++)
++ token[i] = strsep(&str, ",");
++
++ if (str)
++ parse_err("too many arguments\n");
++
++ if (!token[2])
++ parse_err("not enough arguments\n");
++
++ ret = parse_name(&name, token[0]);
++ if (ret == -ENOMEM)
++ parse_err("out of memory\n");
++ if (ret == -ENOSPC)
++ parse_err("name too long\n");
++ if (ret)
++ return 0;
++
++ ret = parse_num32(&start, token[1]);
++ if (ret)
++ parse_err("illegal start address\n");
++
++ ret = parse_num32(&len, token[2]);
++ if (ret)
++ parse_err("illegal device length\n");
++
++ register_device(name, start, len);
++
++ return 0;
++}
++
++module_param_call(phram, phram_setup, NULL, NULL, 000);
++MODULE_PARM_DESC(phram, "Memory region to map. \"map=<name>,<start><length>\"");
++
++/*
++ * Just for compatibility with slram, this is horrible and should go someday.
++ */
++static int __init slram_setup(const char *val, struct kernel_param *kp)
++{
++ char buf[256], *str = buf;
++
++ if (!val || !val[0])
++ parse_err("no arguments to \"slram=\"\n");
++
++ if (strnlen(val, sizeof(str)) >= sizeof(str))
++ parse_err("parameter too long\n");
++
++ strcpy(str, val);
++
++ while (str) {
++ char *token[3];
++ char *name;
++ uint32_t start;
++ uint32_t len;
++ int i, ret;
++
++ for (i=0; i<3; i++) {
++ token[i] = strsep(&str, ",");
++ if (token[i])
++ continue;
++ parse_err("wrong number of arguments to \"slram=\"\n");
++ }
++
++ /* name */
++ ret = parse_name(&name, token[0]);
++ if (ret == -ENOMEM)
++ parse_err("of memory\n");
++ if (ret == -ENOSPC)
++ parse_err("too long\n");
++ if (ret)
++ return 1;
++
++ /* start */
++ ret = parse_num32(&start, token[1]);
++ if (ret)
++ parse_err("illegal start address\n");
++
++ /* len */
++ if (token[2][0] == '+')
++ ret = parse_num32(&len, token[2] + 1);
++ else
++ ret = parse_num32(&len, token[2]);
++
++ if (ret)
++ parse_err("illegal device length\n");
++
++ if (token[2][0] != '+') {
++ if (len < start)
++ parse_err("end < start\n");
++ len -= start;
++ }
++
++ register_device(name, start, len);
++ }
++ return 1;
++}
++
++module_param_call(slram, slram_setup, NULL, NULL, 000);
++MODULE_PARM_DESC(slram, "List of memory regions to map. \"map=<name>,<start><length/end>\"");
++
++
++int __init init_phram(void)
++{
++ printk(KERN_ERR "phram loaded\n");
++ return 0;
++}
++
++static void __exit cleanup_phram(void)
++{
++ unregister_devices();
++}
++
++module_init(init_phram);
++module_exit(cleanup_phram);
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Jörn Engel <joern@wh.fh-wedel.de>");
++MODULE_DESCRIPTION("MTD driver for physical RAM");
+diff -Nurb linux-mips-2.4.27/drivers/mtd/devices/pmc551.c linux/drivers/mtd/devices/pmc551.c
+--- linux-mips-2.4.27/drivers/mtd/devices/pmc551.c 2003-02-26 01:53:49.000000000 +0100
++++ linux/drivers/mtd/devices/pmc551.c 2004-11-19 10:25:11.862205416 +0100
+@@ -1,5 +1,5 @@
+ /*
+- * $Id$
++ * $Id$
+ *
+ * PMC551 PCI Mezzanine Ram Device
+ *
+@@ -681,11 +681,6 @@
+
+ printk(KERN_INFO PMC551_VERSION);
+
+- if(!pci_present()) {
+- printk(KERN_NOTICE "pmc551: PCI not enabled.\n");
+- return -ENODEV;
+- }
+-
+ /*
+ * PCU-bus chipset probe.
+ */
+@@ -787,10 +782,10 @@
+ mtd->write = pmc551_write;
+ mtd->point = pmc551_point;
+ mtd->unpoint = pmc551_unpoint;
+- mtd->module = THIS_MODULE;
+ mtd->type = MTD_RAM;
+ mtd->name = "PMC551 RAM board";
+ mtd->erasesize = 0x10000;
++ mtd->owner = THIS_MODULE;
+
+ if (add_mtd_device(mtd)) {
+ printk(KERN_NOTICE "pmc551: Failed to register new device\n");
+diff -Nurb linux-mips-2.4.27/drivers/mtd/devices/slram.c linux/drivers/mtd/devices/slram.c
+--- linux-mips-2.4.27/drivers/mtd/devices/slram.c 2003-02-26 01:53:49.000000000 +0100
++++ linux/drivers/mtd/devices/slram.c 2004-11-19 10:25:11.863205264 +0100
+@@ -1,6 +1,6 @@
+ /*======================================================================
+
+- $Id$
++ $Id$
+
+ This driver provides a method to access memory not used by the kernel
+ itself (i.e. if the kernel commandline mem=xxx is used). To actually
+@@ -199,7 +199,7 @@
+ (*curmtd)->mtdinfo->unpoint = slram_unpoint;
+ (*curmtd)->mtdinfo->read = slram_read;
+ (*curmtd)->mtdinfo->write = slram_write;
+- (*curmtd)->mtdinfo->module = THIS_MODULE;
++ (*curmtd)->mtdinfo->owner = THIS_MODULE;
+ (*curmtd)->mtdinfo->type = MTD_RAM;
+ (*curmtd)->mtdinfo->erasesize = 0x0;
+
+diff -Nurb linux-mips-2.4.27/drivers/mtd/ftl.c linux/drivers/mtd/ftl.c
+--- linux-mips-2.4.27/drivers/mtd/ftl.c 2003-02-26 01:53:49.000000000 +0100
++++ linux/drivers/mtd/ftl.c 2004-11-19 10:25:11.630240680 +0100
+@@ -1,5 +1,5 @@
+ /* This version ported to the Linux-MTD system by dwmw2@infradead.org
+- * $Id$
++ * $Id$
+ *
+ * Fixes: Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+ * - fixes some leaks on failure in build_maps and ftl_notify_add, cleanups
+@@ -55,8 +55,8 @@
+ contact M-Systems (http://www.m-sys.com) directly.
+
+ ======================================================================*/
++#include <linux/mtd/blktrans.h>
+ #include <linux/module.h>
+-#include <linux/mtd/compatmac.h>
+ #include <linux/mtd/mtd.h>
+ /*#define PSYCHO_DEBUG */
+
+@@ -68,43 +68,13 @@
+ #include <linux/timer.h>
+ #include <linux/major.h>
+ #include <linux/fs.h>
+-#include <linux/ioctl.h>
++#include <linux/init.h>
+ #include <linux/hdreg.h>
+-
+-#if (LINUX_VERSION_CODE >= 0x20100)
+ #include <linux/vmalloc.h>
+-#endif
+-#if (LINUX_VERSION_CODE >= 0x20303)
+ #include <linux/blkpg.h>
+-#endif
++#include <asm/uaccess.h>
+
+ #include <linux/mtd/ftl.h>
+-/*====================================================================*/
+-/* Stuff which really ought to be in compatmac.h */
+-
+-#if (LINUX_VERSION_CODE < 0x20328)
+-#define register_disk(dev, drive, minors, ops, size) \
+- do { (dev)->part[(drive)*(minors)].nr_sects = size; \
+- if (size == 0) (dev)->part[(drive)*(minors)].start_sect = -1; \
+- resetup_one_dev(dev, drive); } while (0)
+-#endif
+-
+-#if (LINUX_VERSION_CODE < 0x20320)
+-#define BLK_DEFAULT_QUEUE(n) blk_dev[n].request_fn
+-#define blk_init_queue(q, req) q = (req)
+-#define blk_cleanup_queue(q) q = NULL
+-#define request_arg_t void
+-#else
+-#define request_arg_t request_queue_t *q
+-#endif
+-
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,14)
+-#define BLK_INC_USE_COUNT MOD_INC_USE_COUNT
+-#define BLK_DEC_USE_COUNT MOD_DEC_USE_COUNT
+-#else
+-#define BLK_INC_USE_COUNT do {} while(0)
+-#define BLK_DEC_USE_COUNT do {} while(0)
+-#endif
+
+ /*====================================================================*/
+
+@@ -119,19 +89,6 @@
+ #define FTL_MAJOR 44
+ #endif
+
+-/* Funky stuff for setting up a block device */
+-#define MAJOR_NR FTL_MAJOR
+-#define DEVICE_NAME "ftl"
+-#define DEVICE_REQUEST do_ftl_request
+-#define DEVICE_ON(device)
+-#define DEVICE_OFF(device)
+-
+-#define DEVICE_NR(minor) ((minor)>>5)
+-#define REGION_NR(minor) (((minor)>>3)&3)
+-#define PART_NR(minor) ((minor)&7)
+-#define MINOR_NR(dev,reg,part) (((dev)<<5)+((reg)<<3)+(part))
+-
+-#include <linux/blk.h>
+
+ /*====================================================================*/
+
+@@ -142,8 +99,7 @@
+ #define MAX_REGION 4
+
+ /* Maximum number of partitions in an FTL region */
+-#define PART_BITS 3
+-#define MAX_PART 8
++#define PART_BITS 4
+
+ /* Maximum number of outstanding erase requests per socket */
+ #define MAX_ERASE 8
+@@ -154,7 +110,7 @@
+
+ /* Each memory region corresponds to a minor device */
+ typedef struct partition_t {
+- struct mtd_info *mtd;
++ struct mtd_blktrans_dev mbd;
+ u_int32_t state;
+ u_int32_t *VirtualBlockMap;
+ u_int32_t *VirtualPageMap;
+@@ -179,21 +135,10 @@
+ region_info_t region;
+ memory_handle_t handle;
+ #endif
+- atomic_t open;
+ } partition_t;
+
+-partition_t *myparts[MAX_MTD_DEVICES];
+-
+-static void ftl_notify_add(struct mtd_info *mtd);
+-static void ftl_notify_remove(struct mtd_info *mtd);
+-
+ void ftl_freepart(partition_t *part);
+
+-static struct mtd_notifier ftl_notifier = {
+- add: ftl_notify_add,
+- remove: ftl_notify_remove,
+-};
+-
+ /* Partition state flags */
+ #define FTL_FORMATTED 0x01
+
+@@ -204,51 +149,11 @@
+ #define XFER_PREPARED 0x03
+ #define XFER_FAILED 0x04
+
+-static struct hd_struct ftl_hd[MINOR_NR(MAX_DEV, 0, 0)];
+-static int ftl_sizes[MINOR_NR(MAX_DEV, 0, 0)];
+-static int ftl_blocksizes[MINOR_NR(MAX_DEV, 0, 0)];
+-
+-static struct gendisk ftl_gendisk = {
+- major: FTL_MAJOR,
+- major_name: "ftl",
+- minor_shift: PART_BITS,
+- max_p: MAX_PART,
+-#if (LINUX_VERSION_CODE < 0x20328)
+- max_nr: MAX_DEV*MAX_PART,
+-#endif
+- part: ftl_hd,
+- sizes: ftl_sizes,
+-};
+-
+ /*====================================================================*/
+
+-static int ftl_ioctl(struct inode *inode, struct file *file,
+- u_int cmd, u_long arg);
+-static int ftl_open(struct inode *inode, struct file *file);
+-static release_t ftl_close(struct inode *inode, struct file *file);
+-static int ftl_reread_partitions(int minor);
+
+ static void ftl_erase_callback(struct erase_info *done);
+
+-#if LINUX_VERSION_CODE < 0x20326
+-static struct file_operations ftl_blk_fops = {
+- open: ftl_open,
+- release: ftl_close,
+- ioctl: ftl_ioctl,
+- read: block_read,
+- write: block_write,
+- fsync: block_fsync
+-};
+-#else
+-static struct block_device_operations ftl_blk_fops = {
+-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,14)
+- owner: THIS_MODULE,
+-#endif
+- open: ftl_open,
+- release: ftl_close,
+- ioctl: ftl_ioctl,
+-};
+-#endif
+
+ /*======================================================================
+
+@@ -264,13 +169,13 @@
+ loff_t offset, max_offset;
+ int ret;
+ part->header.FormattedSize = 0;
+- max_offset = (0x100000<part->mtd->size)?0x100000:part->mtd->size;
++ max_offset = (0x100000<part->mbd.mtd->size)?0x100000:part->mbd.mtd->size;
+ /* Search first megabyte for a valid FTL header */
+ for (offset = 0;
+ (offset + sizeof(header)) < max_offset;
+- offset += part->mtd->erasesize ? : 0x2000) {
++ offset += part->mbd.mtd->erasesize ? : 0x2000) {
+
+- ret = part->mtd->read(part->mtd, offset, sizeof(header), &ret,
++ ret = part->mbd.mtd->read(part->mbd.mtd, offset, sizeof(header), &ret,
+ (unsigned char *)&header);
+
+ if (ret)
+@@ -283,15 +188,15 @@
+ printk(KERN_NOTICE "ftl_cs: FTL header not found.\n");
+ return -ENOENT;
+ }
+- if ((le16_to_cpu(header.NumEraseUnits) > 65536) || header.BlockSize != 9 ||
++ if (header.BlockSize != 9 ||
+ (header.EraseUnitSize < 10) || (header.EraseUnitSize > 31) ||
+ (header.NumTransferUnits >= le16_to_cpu(header.NumEraseUnits))) {
+ printk(KERN_NOTICE "ftl_cs: FTL header corrupt!\n");
+ return -1;
+ }
+- if ((1 << header.EraseUnitSize) != part->mtd->erasesize) {
++ if ((1 << header.EraseUnitSize) != part->mbd.mtd->erasesize) {
+ printk(KERN_NOTICE "ftl: FTL EraseUnitSize %x != MTD erasesize %x\n",
+- 1 << header.EraseUnitSize,part->mtd->erasesize);
++ 1 << header.EraseUnitSize,part->mbd.mtd->erasesize);
+ return -1;
+ }
+ part->header = header;
+@@ -326,7 +231,7 @@
+ for (i = 0; i < le16_to_cpu(part->header.NumEraseUnits); i++) {
+ offset = ((i + le16_to_cpu(part->header.FirstPhysicalEUN))
+ << part->header.EraseUnitSize);
+- ret = part->mtd->read(part->mtd, offset, sizeof(header), &retval,
++ ret = part->mbd.mtd->read(part->mbd.mtd, offset, sizeof(header), &retval,
+ (unsigned char *)&header);
+
+ if (ret)
+@@ -391,7 +296,7 @@
+ part->EUNInfo[i].Deleted = 0;
+ offset = part->EUNInfo[i].Offset + le32_to_cpu(header.BAMOffset);
+
+- ret = part->mtd->read(part->mtd, offset,
++ ret = part->mbd.mtd->read(part->mbd.mtd, offset,
+ part->BlocksPerUnit * sizeof(u_int32_t), &retval,
+ (unsigned char *)part->bam_cache);
+
+@@ -456,7 +361,7 @@
+ erase->len = 1 << part->header.EraseUnitSize;
+ erase->priv = (u_long)part;
+
+- ret = part->mtd->erase(part->mtd, erase);
++ ret = part->mbd.mtd->erase(part->mbd.mtd, erase);
+
+ if (!ret)
+ xfer->EraseCount++;
+@@ -523,7 +428,7 @@
+ header.LogicalEUN = cpu_to_le16(0xffff);
+ header.EraseCount = cpu_to_le32(xfer->EraseCount);
+
+- ret = part->mtd->write(part->mtd, xfer->Offset, sizeof(header),
++ ret = part->mbd.mtd->write(part->mbd.mtd, xfer->Offset, sizeof(header),
+ &retlen, (u_char *)&header);
+
+ if (ret) {
+@@ -539,7 +444,7 @@
+
+ for (i = 0; i < nbam; i++, offset += sizeof(u_int32_t)) {
+
+- ret = part->mtd->write(part->mtd, offset, sizeof(u_int32_t),
++ ret = part->mbd.mtd->write(part->mbd.mtd, offset, sizeof(u_int32_t),
+ &retlen, (u_char *)&ctl);
+
+ if (ret)
+@@ -586,7 +491,7 @@
+
+ offset = eun->Offset + le32_to_cpu(part->header.BAMOffset);
+
+- ret = part->mtd->read(part->mtd, offset,
++ ret = part->mbd.mtd->read(part->mbd.mtd, offset,
+ part->BlocksPerUnit * sizeof(u_int32_t),
+ &retlen, (u_char *) (part->bam_cache));
+
+@@ -604,7 +509,7 @@
+ offset = xfer->Offset + 20; /* Bad! */
+ unit = cpu_to_le16(0x7fff);
+
+- ret = part->mtd->write(part->mtd, offset, sizeof(u_int16_t),
++ ret = part->mbd.mtd->write(part->mbd.mtd, offset, sizeof(u_int16_t),
+ &retlen, (u_char *) &unit);
+
+ if (ret) {
+@@ -624,7 +529,7 @@
+ break;
+ case BLOCK_DATA:
+ case BLOCK_REPLACEMENT:
+- ret = part->mtd->read(part->mtd, src, SECTOR_SIZE,
++ ret = part->mbd.mtd->read(part->mbd.mtd, src, SECTOR_SIZE,
+ &retlen, (u_char *) buf);
+ if (ret) {
+ printk(KERN_WARNING "ftl: Error reading old xfer unit in copy_erase_unit\n");
+@@ -632,7 +537,7 @@
+ }
+
+
+- ret = part->mtd->write(part->mtd, dest, SECTOR_SIZE,
++ ret = part->mbd.mtd->write(part->mbd.mtd, dest, SECTOR_SIZE,
+ &retlen, (u_char *) buf);
+ if (ret) {
+ printk(KERN_WARNING "ftl: Error writing new xfer unit in copy_erase_unit\n");
+@@ -651,7 +556,7 @@
+ }
+
+ /* Write the BAM to the transfer unit */
+- ret = part->mtd->write(part->mtd, xfer->Offset + le32_to_cpu(part->header.BAMOffset),
++ ret = part->mbd.mtd->write(part->mbd.mtd, xfer->Offset + le32_to_cpu(part->header.BAMOffset),
+ part->BlocksPerUnit * sizeof(int32_t), &retlen,
+ (u_char *)part->bam_cache);
+ if (ret) {
+@@ -661,7 +566,7 @@
+
+
+ /* All clear? Then update the LogicalEUN again */
+- ret = part->mtd->write(part->mtd, xfer->Offset + 20, sizeof(u_int16_t),
++ ret = part->mbd.mtd->write(part->mbd.mtd, xfer->Offset + 20, sizeof(u_int16_t),
+ &retlen, (u_char *)&srcunitswap);
+
+ if (ret) {
+@@ -749,8 +654,8 @@
+ if (queued) {
+ DEBUG(1, "ftl_cs: waiting for transfer "
+ "unit to be prepared...\n");
+- if (part->mtd->sync)
+- part->mtd->sync(part->mtd);
++ if (part->mbd.mtd->sync)
++ part->mbd.mtd->sync(part->mbd.mtd);
+ } else {
+ static int ne = 0;
+ if (++ne < 5)
+@@ -848,7 +753,7 @@
+ /* Invalidate cache */
+ part->bam_index = 0xffff;
+
+- ret = part->mtd->read(part->mtd,
++ ret = part->mbd.mtd->read(part->mbd.mtd,
+ part->EUNInfo[eun].Offset + le32_to_cpu(part->header.BAMOffset),
+ part->BlocksPerUnit * sizeof(u_int32_t),
+ &retlen, (u_char *) (part->bam_cache));
+@@ -877,78 +782,6 @@
+
+ } /* find_free */
+
+-/*======================================================================
+-
+- This gets a memory handle for the region corresponding to the
+- minor device number.
+-
+-======================================================================*/
+-
+-static int ftl_open(struct inode *inode, struct file *file)
+-{
+- int minor = MINOR(inode->i_rdev);
+- partition_t *partition;
+-
+- if (minor>>4 >= MAX_MTD_DEVICES)
+- return -ENODEV;
+-
+- partition = myparts[minor>>4];
+-
+- if (!partition)
+- return -ENODEV;
+-
+- if (partition->state != FTL_FORMATTED)
+- return -ENXIO;
+-
+- if (ftl_gendisk.part[minor].nr_sects == 0)
+- return -ENXIO;
+-
+- BLK_INC_USE_COUNT;
+-
+- if (!get_mtd_device(partition->mtd, -1)) {
+- BLK_DEC_USE_COUNT;
+- return -ENXIO;
+- }
+-
+- if ((file->f_mode & 2) && !(partition->mtd->flags & MTD_CLEAR_BITS) ) {
+- put_mtd_device(partition->mtd);
+- BLK_DEC_USE_COUNT;
+- return -EROFS;
+- }
+-
+- DEBUG(0, "ftl_cs: ftl_open(%d)\n", minor);
+-
+- atomic_inc(&partition->open);
+-
+- return 0;
+-}
+-
+-/*====================================================================*/
+-
+-static release_t ftl_close(struct inode *inode, struct file *file)
+-{
+- int minor = MINOR(inode->i_rdev);
+- partition_t *part = myparts[minor >> 4];
+- int i;
+-
+- DEBUG(0, "ftl_cs: ftl_close(%d)\n", minor);
+-
+- /* Wait for any pending erase operations to complete */
+- if (part->mtd->sync)
+- part->mtd->sync(part->mtd);
+-
+- for (i = 0; i < part->header.NumTransferUnits; i++) {
+- if (part->XferInfo[i].state == XFER_ERASED)
+- prepare_xfer(part, i);
+- }
+-
+- atomic_dec(&part->open);
+-
+- put_mtd_device(part->mtd);
+- BLK_DEC_USE_COUNT;
+- release_return(0);
+-} /* ftl_close */
+-
+
+ /*======================================================================
+
+@@ -983,7 +816,7 @@
+ else {
+ offset = (part->EUNInfo[log_addr / bsize].Offset
+ + (log_addr % bsize));
+- ret = part->mtd->read(part->mtd, offset, SECTOR_SIZE,
++ ret = part->mbd.mtd->read(part->mbd.mtd, offset, SECTOR_SIZE,
+ &retlen, (u_char *) buffer);
+
+ if (ret) {
+@@ -1022,7 +855,7 @@
+ le32_to_cpu(part->header.BAMOffset));
+
+ #ifdef PSYCHO_DEBUG
+- ret = part->mtd->read(part->mtd, offset, sizeof(u_int32_t),
++ ret = part->mbd.mtd->read(part->mbd.mtd, offset, sizeof(u_int32_t),
+ &retlen, (u_char *)&old_addr);
+ if (ret) {
+ printk(KERN_WARNING"ftl: Error reading old_addr in set_bam_entry: %d\n",ret);
+@@ -1059,7 +892,7 @@
+ #endif
+ part->bam_cache[blk] = le_virt_addr;
+ }
+- ret = part->mtd->write(part->mtd, offset, sizeof(u_int32_t),
++ ret = part->mbd.mtd->write(part->mbd.mtd, offset, sizeof(u_int32_t),
+ &retlen, (u_char *)&le_virt_addr);
+
+ if (ret) {
+@@ -1119,7 +952,7 @@
+ part->EUNInfo[part->bam_index].Deleted++;
+ offset = (part->EUNInfo[part->bam_index].Offset +
+ blk * SECTOR_SIZE);
+- ret = part->mtd->write(part->mtd, offset, SECTOR_SIZE, &retlen,
++ ret = part->mbd.mtd->write(part->mbd.mtd, offset, SECTOR_SIZE, &retlen,
+ buffer);
+
+ if (ret) {
+@@ -1151,164 +984,32 @@
+ return 0;
+ } /* ftl_write */
+
+-/*======================================================================
+-
+- IOCTL calls for getting device parameters.
+-
+-======================================================================*/
+-
+-static int ftl_ioctl(struct inode *inode, struct file *file,
+- u_int cmd, u_long arg)
++static int ftl_getgeo(struct mtd_blktrans_dev *dev, struct hd_geometry *geo)
+ {
+- struct hd_geometry *geo = (struct hd_geometry *)arg;
+- int ret = 0, minor = MINOR(inode->i_rdev);
+- partition_t *part= myparts[minor >> 4];
++ partition_t *part = (void *)dev;
+ u_long sect;
+
+- if (!part)
+- return -ENODEV; /* How? */
+-
+- switch (cmd) {
+- case HDIO_GETGEO:
+- ret = verify_area(VERIFY_WRITE, (long *)arg, sizeof(*geo));
+- if (ret) return ret;
+- /* Sort of arbitrary: round size down to 4K boundary */
++ /* Sort of arbitrary: round size down to 4KiB boundary */
+ sect = le32_to_cpu(part->header.FormattedSize)/SECTOR_SIZE;
+- put_user(1, (char *)&geo->heads);
+- put_user(8, (char *)&geo->sectors);
+- put_user((sect>>3), (short *)&geo->cylinders);
+- put_user(ftl_hd[minor].start_sect, (u_long *)&geo->start);
+- break;
+- case BLKGETSIZE:
+- ret = put_user(ftl_hd[minor].nr_sects, (unsigned long *)arg);
+- break;
+-#ifdef BLKGETSIZE64
+- case BLKGETSIZE64:
+- ret = put_user((u64)ftl_hd[minor].nr_sects << 9, (u64 *)arg);
+- break;
+-#endif
+- case BLKRRPART:
+- ret = ftl_reread_partitions(minor);
+- break;
+-#if (LINUX_VERSION_CODE < 0x20303)
+- case BLKFLSBUF:
+-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
+- if (!capable(CAP_SYS_ADMIN)) return -EACCES;
+-#endif
+- fsync_dev(inode->i_rdev);
+- invalidate_buffers(inode->i_rdev);
+- break;
+- RO_IOCTLS(inode->i_rdev, arg);
+-#else
+- case BLKROSET:
+- case BLKROGET:
+- case BLKFLSBUF:
+- ret = blk_ioctl(inode->i_rdev, cmd, arg);
+- break;
+-#endif
+- default:
+- ret = -EINVAL;
+- }
+-
+- return ret;
+-} /* ftl_ioctl */
+-
+-/*======================================================================
+
+- Handler for block device requests
++ geo->heads = 1;
++ geo->sectors = 8;
++ geo->cylinders = sect >> 3;
+
+-======================================================================*/
+-
+-static int ftl_reread_partitions(int minor)
+-{
+- partition_t *part = myparts[minor >> 4];
+- int i, whole;
+-
+- DEBUG(0, "ftl_cs: ftl_reread_partition(%d)\n", minor);
+- if ((atomic_read(&part->open) > 1)) {
+- return -EBUSY;
+- }
+- whole = minor & ~(MAX_PART-1);
+-
+- i = MAX_PART - 1;
+- while (i-- > 0) {
+- if (ftl_hd[whole+i].nr_sects > 0) {
+- kdev_t rdev = MKDEV(FTL_MAJOR, whole+i);
+-
+- invalidate_device(rdev, 1);
+- }
+- ftl_hd[whole+i].start_sect = 0;
+- ftl_hd[whole+i].nr_sects = 0;
+- }
+-
+- scan_header(part);
+-
+- register_disk(&ftl_gendisk, whole >> PART_BITS, MAX_PART,
+- &ftl_blk_fops, le32_to_cpu(part->header.FormattedSize)/SECTOR_SIZE);
+-
+-#ifdef PCMCIA_DEBUG
+- for (i = 0; i < MAX_PART; i++) {
+- if (ftl_hd[whole+i].nr_sects > 0)
+- printk(KERN_INFO " %d: start %ld size %ld\n", i,
+- ftl_hd[whole+i].start_sect,
+- ftl_hd[whole+i].nr_sects);
+- }
+-#endif
+ return 0;
+ }
+
+-/*======================================================================
+-
+- Handler for block device requests
+-
+-======================================================================*/
+-
+-static void do_ftl_request(request_arg_t)
++static int ftl_readsect(struct mtd_blktrans_dev *dev,
++ unsigned long block, char *buf)
+ {
+- int ret, minor;
+- partition_t *part;
+-
+- do {
+- // sti();
+- INIT_REQUEST;
+-
+- minor = MINOR(CURRENT->rq_dev);
+-
+- part = myparts[minor >> 4];
+- if (part) {
+- ret = 0;
+-
+- switch (CURRENT->cmd) {
+- case READ:
+- ret = ftl_read(part, CURRENT->buffer,
+- CURRENT->sector+ftl_hd[minor].start_sect,
+- CURRENT->current_nr_sectors);
+- if (ret) printk("ftl_read returned %d\n", ret);
+- break;
+-
+- case WRITE:
+- ret = ftl_write(part, CURRENT->buffer,
+- CURRENT->sector+ftl_hd[minor].start_sect,
+- CURRENT->current_nr_sectors);
+- if (ret) printk("ftl_write returned %d\n", ret);
+- break;
+-
+- default:
+- panic("ftl_cs: unknown block command!\n");
+-
+- }
+- } else {
+- ret = 1;
+- printk("NULL part in ftl_request\n");
+- }
+-
+- if (!ret) {
+- CURRENT->sector += CURRENT->current_nr_sectors;
+- }
++ return ftl_read((void *)dev, buf, block, 1);
++}
+
+- end_request((ret == 0) ? 1 : 0);
+- } while (1);
+-} /* do_ftl_request */
++static int ftl_writesect(struct mtd_blktrans_dev *dev,
++ unsigned long block, char *buf)
++{
++ return ftl_write((void *)dev, buf, block, 1);
++}
+
+ /*====================================================================*/
+
+@@ -1337,19 +1038,9 @@
+
+ } /* ftl_freepart */
+
+-static void ftl_notify_add(struct mtd_info *mtd)
++static void ftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
+ {
+ partition_t *partition;
+- int device;
+-
+- for (device=0; device < MAX_MTD_DEVICES && myparts[device]; device++)
+- ;
+-
+- if (device == MAX_MTD_DEVICES) {
+- printk(KERN_NOTICE "Maximum number of FTL partitions reached\n"
+- "Not scanning <%s>\n", mtd->name);
+- return;
+- }
+
+ partition = kmalloc(sizeof(partition_t), GFP_KERNEL);
+
+@@ -1361,92 +1052,55 @@
+
+ memset(partition, 0, sizeof(partition_t));
+
+- partition->mtd = mtd;
++ partition->mbd.mtd = mtd;
+
+ if ((scan_header(partition) == 0) &&
+ (build_maps(partition) == 0)) {
+
+ partition->state = FTL_FORMATTED;
+- atomic_set(&partition->open, 0);
+- myparts[device] = partition;
+- ftl_reread_partitions(device << 4);
+ #ifdef PCMCIA_DEBUG
+- printk(KERN_INFO "ftl_cs: opening %d kb FTL partition\n",
++ printk(KERN_INFO "ftl_cs: opening %d KiB FTL partition\n",
+ le32_to_cpu(partition->header.FormattedSize) >> 10);
+ #endif
++ partition->mbd.size = le32_to_cpu(partition->header.FormattedSize) >> 9;
++ partition->mbd.blksize = SECTOR_SIZE;
++ partition->mbd.tr = tr;
++ partition->mbd.devnum = -1;
++ if (add_mtd_blktrans_dev((void *)partition))
++ kfree(partition);
++
+ } else
+ kfree(partition);
+ }
+
+-static void ftl_notify_remove(struct mtd_info *mtd)
++static void ftl_remove_dev(struct mtd_blktrans_dev *dev)
+ {
+- int i,j;
+-
+- /* Q: What happens if you try to remove a device which has
+- * a currently-open FTL partition on it?
+- *
+- * A: You don't. The ftl_open routine is responsible for
+- * increasing the use count of the driver module which
+- * it uses.
+- */
+-
+- /* That's the theory, anyway :) */
+-
+- for (i=0; i< MAX_MTD_DEVICES; i++)
+- if (myparts[i] && myparts[i]->mtd == mtd) {
+-
+- if (myparts[i]->state == FTL_FORMATTED)
+- ftl_freepart(myparts[i]);
+-
+- myparts[i]->state = 0;
+- for (j=0; j<16; j++) {
+- ftl_gendisk.part[j].nr_sects=0;
+- ftl_gendisk.part[j].start_sect=0;
+- }
+- kfree(myparts[i]);
+- myparts[i] = NULL;
+- }
++ del_mtd_blktrans_dev(dev);
++ kfree(dev);
+ }
+
++struct mtd_blktrans_ops ftl_tr = {
++ .name = "ftl",
++ .major = FTL_MAJOR,
++ .part_bits = PART_BITS,
++ .readsect = ftl_readsect,
++ .writesect = ftl_writesect,
++ .getgeo = ftl_getgeo,
++ .add_mtd = ftl_add_mtd,
++ .remove_dev = ftl_remove_dev,
++ .owner = THIS_MODULE,
++};
++
+ int init_ftl(void)
+ {
+- int i;
+-
+- memset(myparts, 0, sizeof(myparts));
++ DEBUG(0, "$Id$\n");
+
+- DEBUG(0, "$Id$\n");
+-
+- if (register_blkdev(FTL_MAJOR, "ftl", &ftl_blk_fops)) {
+- printk(KERN_NOTICE "ftl_cs: unable to grab major "
+- "device number!\n");
+- return -EAGAIN;
+- }
+-
+- for (i = 0; i < MINOR_NR(MAX_DEV, 0, 0); i++)
+- ftl_blocksizes[i] = 1024;
+- for (i = 0; i < MAX_DEV*MAX_PART; i++) {
+- ftl_hd[i].nr_sects = 0;
+- ftl_hd[i].start_sect = 0;
+- }
+- blksize_size[FTL_MAJOR] = ftl_blocksizes;
+- ftl_gendisk.major = FTL_MAJOR;
+- blk_init_queue(BLK_DEFAULT_QUEUE(FTL_MAJOR), &do_ftl_request);
+- add_gendisk(&ftl_gendisk);
+-
+- register_mtd_user(&ftl_notifier);
+-
+- return 0;
++ return register_mtd_blktrans(&ftl_tr);
+ }
+
+ static void __exit cleanup_ftl(void)
+ {
+- unregister_mtd_user(&ftl_notifier);
+-
+- unregister_blkdev(FTL_MAJOR, "ftl");
+- blk_cleanup_queue(BLK_DEFAULT_QUEUE(FTL_MAJOR));
+- blksize_size[FTL_MAJOR] = NULL;
+-
+- del_gendisk(&ftl_gendisk);
++ deregister_mtd_blktrans(&ftl_tr);
+ }
+
+ module_init(init_ftl);
+diff -Nurb linux-mips-2.4.27/drivers/mtd/inftlcore.c linux/drivers/mtd/inftlcore.c
+--- linux-mips-2.4.27/drivers/mtd/inftlcore.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/mtd/inftlcore.c 2004-11-19 10:25:11.632240376 +0100
+@@ -0,0 +1,900 @@
++/*
++ * inftlcore.c -- Linux driver for Inverse Flash Translation Layer (INFTL)
++ *
++ * (C) Copyright 2002, Greg Ungerer (gerg@snapgear.com)
++ *
++ * Based heavily on the nftlcore.c code which is:
++ * (c) 1999 Machine Vision Holdings, Inc.
++ * Author: David Woodhouse <dwmw2@infradead.org>
++ *
++ * $Id$
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ */
++
++#include <linux/config.h>
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/delay.h>
++#include <linux/slab.h>
++#include <linux/sched.h>
++#include <linux/init.h>
++#include <linux/kmod.h>
++#include <linux/hdreg.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/nftl.h>
++#include <linux/mtd/inftl.h>
++#include <asm/uaccess.h>
++#include <asm/errno.h>
++#include <asm/io.h>
++
++/*
++ * Maximum number of loops while examining next block, to have a
++ * chance to detect consistency problems (they should never happen
++ * because of the checks done in the mounting.
++ */
++#define MAX_LOOPS 10000
++
++extern void INFTL_dumptables(struct INFTLrecord *inftl);
++extern void INFTL_dumpVUchains(struct INFTLrecord *inftl);
++
++static void inftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
++{
++ struct INFTLrecord *inftl;
++ unsigned long temp;
++
++ if (mtd->ecctype != MTD_ECC_RS_DiskOnChip)
++ return;
++
++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: add_mtd for %s\n", mtd->name);
++
++ inftl = kmalloc(sizeof(*inftl), GFP_KERNEL);
++
++ if (!inftl) {
++ printk(KERN_WARNING "INFTL: Out of memory for data structures\n");
++ return;
++ }
++ memset(inftl, 0, sizeof(*inftl));
++
++ inftl->mbd.mtd = mtd;
++ inftl->mbd.devnum = -1;
++ inftl->mbd.blksize = 512;
++ inftl->mbd.tr = tr;
++
++ if (INFTL_mount(inftl) < 0) {
++ printk(KERN_WARNING "INFTL: could not mount device\n");
++ kfree(inftl);
++ return;
++ }
++
++ /* OK, it's a new one. Set up all the data structures. */
++
++ /* Calculate geometry */
++ inftl->cylinders = 1024;
++ inftl->heads = 16;
++
++ temp = inftl->cylinders * inftl->heads;
++ inftl->sectors = inftl->mbd.size / temp;
++ if (inftl->mbd.size % temp) {
++ inftl->sectors++;
++ temp = inftl->cylinders * inftl->sectors;
++ inftl->heads = inftl->mbd.size / temp;
++
++ if (inftl->mbd.size % temp) {
++ inftl->heads++;
++ temp = inftl->heads * inftl->sectors;
++ inftl->cylinders = inftl->mbd.size / temp;
++ }
++ }
++
++ if (inftl->mbd.size != inftl->heads * inftl->cylinders * inftl->sectors) {
++ /*
++ Oh no we don't have
++ mbd.size == heads * cylinders * sectors
++ */
++ printk(KERN_WARNING "INFTL: cannot calculate a geometry to "
++ "match size of 0x%lx.\n", inftl->mbd.size);
++ printk(KERN_WARNING "INFTL: using C:%d H:%d S:%d "
++ "(== 0x%lx sects)\n",
++ inftl->cylinders, inftl->heads , inftl->sectors,
++ (long)inftl->cylinders * (long)inftl->heads *
++ (long)inftl->sectors );
++ }
++
++ if (add_mtd_blktrans_dev(&inftl->mbd)) {
++ if (inftl->PUtable)
++ kfree(inftl->PUtable);
++ if (inftl->VUtable)
++ kfree(inftl->VUtable);
++ kfree(inftl);
++ return;
++ }
++#ifdef PSYCHO_DEBUG
++ printk(KERN_INFO "INFTL: Found new nftl%c\n", nftl->mbd.devnum + 'a');
++#endif
++ return;
++}
++
++static void inftl_remove_dev(struct mtd_blktrans_dev *dev)
++{
++ struct INFTLrecord *inftl = (void *)dev;
++
++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: remove_dev (i=%d)\n", dev->devnum);
++
++ del_mtd_blktrans_dev(dev);
++
++ if (inftl->PUtable)
++ kfree(inftl->PUtable);
++ if (inftl->VUtable)
++ kfree(inftl->VUtable);
++ kfree(inftl);
++}
++
++/*
++ * Actual INFTL access routines.
++ */
++
++/*
++ * INFTL_findfreeblock: Find a free Erase Unit on the INFTL partition.
++ * This function is used when the give Virtual Unit Chain.
++ */
++static u16 INFTL_findfreeblock(struct INFTLrecord *inftl, int desperate)
++{
++ u16 pot = inftl->LastFreeEUN;
++ int silly = inftl->nb_blocks;
++
++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_findfreeblock(inftl=0x%x,"
++ "desperate=%d)\n", (int)inftl, desperate);
++
++ /*
++ * Normally, we force a fold to happen before we run out of free
++ * blocks completely.
++ */
++ if (!desperate && inftl->numfreeEUNs < 2) {
++ DEBUG(MTD_DEBUG_LEVEL1, "INFTL: there are too few free "
++ "EUNs (%d)\n", inftl->numfreeEUNs);
++ return 0xffff;
++ }
++
++ /* Scan for a free block */
++ do {
++ if (inftl->PUtable[pot] == BLOCK_FREE) {
++ inftl->LastFreeEUN = pot;
++ return pot;
++ }
++
++ if (++pot > inftl->lastEUN)
++ pot = 0;
++
++ if (!silly--) {
++ printk(KERN_WARNING "INFTL: no free blocks found! "
++ "EUN range = %d - %d\n", 0, inftl->LastFreeEUN);
++ return BLOCK_NIL;
++ }
++ } while (pot != inftl->LastFreeEUN);
++
++ return BLOCK_NIL;
++}
++
++static u16 INFTL_foldchain(struct INFTLrecord *inftl, unsigned thisVUC, unsigned pendingblock)
++{
++ u16 BlockMap[MAX_SECTORS_PER_UNIT];
++ unsigned char BlockDeleted[MAX_SECTORS_PER_UNIT];
++ unsigned int thisEUN, prevEUN, status;
++ int block, silly;
++ unsigned int targetEUN;
++ struct inftl_oob oob;
++ size_t retlen;
++
++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_foldchain(inftl=0x%x,thisVUC=%d,"
++ "pending=%d)\n", (int)inftl, thisVUC, pendingblock);
++
++ memset(BlockMap, 0xff, sizeof(BlockMap));
++ memset(BlockDeleted, 0, sizeof(BlockDeleted));
++
++ thisEUN = targetEUN = inftl->VUtable[thisVUC];
++
++ if (thisEUN == BLOCK_NIL) {
++ printk(KERN_WARNING "INFTL: trying to fold non-existent "
++ "Virtual Unit Chain %d!\n", thisVUC);
++ return BLOCK_NIL;
++ }
++
++ /*
++ * Scan to find the Erase Unit which holds the actual data for each
++ * 512-byte block within the Chain.
++ */
++ silly = MAX_LOOPS;
++ while (thisEUN < inftl->nb_blocks) {
++ for (block = 0; block < inftl->EraseSize/SECTORSIZE; block ++) {
++ if ((BlockMap[block] != 0xffff) || BlockDeleted[block])
++ continue;
++
++ if (MTD_READOOB(inftl->mbd.mtd, (thisEUN * inftl->EraseSize)
++ + (block * SECTORSIZE), 16 , &retlen,
++ (char *)&oob) < 0)
++ status = SECTOR_IGNORE;
++ else
++ status = oob.b.Status | oob.b.Status1;
++
++ switch(status) {
++ case SECTOR_FREE:
++ case SECTOR_IGNORE:
++ break;
++ case SECTOR_USED:
++ BlockMap[block] = thisEUN;
++ continue;
++ case SECTOR_DELETED:
++ BlockDeleted[block] = 1;
++ continue;
++ default:
++ printk(KERN_WARNING "INFTL: unknown status "
++ "for block %d in EUN %d: %x\n",
++ block, thisEUN, status);
++ break;
++ }
++ }
++
++ if (!silly--) {
++ printk(KERN_WARNING "INFTL: infinite loop in Virtual "
++ "Unit Chain 0x%x\n", thisVUC);
++ return BLOCK_NIL;
++ }
++
++ thisEUN = inftl->PUtable[thisEUN];
++ }
++
++ /*
++ * OK. We now know the location of every block in the Virtual Unit
++ * Chain, and the Erase Unit into which we are supposed to be copying.
++ * Go for it.
++ */
++ DEBUG(MTD_DEBUG_LEVEL1, "INFTL: folding chain %d into unit %d\n",
++ thisVUC, targetEUN);
++
++ for (block = 0; block < inftl->EraseSize/SECTORSIZE ; block++) {
++ unsigned char movebuf[SECTORSIZE];
++ int ret;
++
++ /*
++ * If it's in the target EUN already, or if it's pending write,
++ * do nothing.
++ */
++ if (BlockMap[block] == targetEUN || (pendingblock ==
++ (thisVUC * (inftl->EraseSize / SECTORSIZE) + block))) {
++ continue;
++ }
++
++ /*
++ * Copy only in non free block (free blocks can only
++ * happen in case of media errors or deleted blocks).
++ */
++ if (BlockMap[block] == BLOCK_NIL)
++ continue;
++
++ ret = MTD_READECC(inftl->mbd.mtd, (inftl->EraseSize *
++ BlockMap[block]) + (block * SECTORSIZE), SECTORSIZE,
++ &retlen, movebuf, (char *)&oob, NULL);
++ if (ret < 0) {
++ ret = MTD_READECC(inftl->mbd.mtd, (inftl->EraseSize *
++ BlockMap[block]) + (block * SECTORSIZE),
++ SECTORSIZE, &retlen, movebuf, (char *)&oob,
++ NULL);
++ if (ret != -EIO)
++ DEBUG(MTD_DEBUG_LEVEL1, "INFTL: error went "
++ "away on retry?\n");
++ }
++ MTD_WRITEECC(inftl->mbd.mtd, (inftl->EraseSize * targetEUN) +
++ (block * SECTORSIZE), SECTORSIZE, &retlen,
++ movebuf, (char *)&oob, NULL);
++ }
++
++ /*
++ * Newest unit in chain now contains data from _all_ older units.
++ * So go through and erase each unit in chain, oldest first. (This
++ * is important, by doing oldest first if we crash/reboot then it
++ * it is relatively simple to clean up the mess).
++ */
++ DEBUG(MTD_DEBUG_LEVEL1, "INFTL: want to erase virtual chain %d\n",
++ thisVUC);
++
++ for (;;) {
++ /* Find oldest unit in chain. */
++ thisEUN = inftl->VUtable[thisVUC];
++ prevEUN = BLOCK_NIL;
++ while (inftl->PUtable[thisEUN] != BLOCK_NIL) {
++ prevEUN = thisEUN;
++ thisEUN = inftl->PUtable[thisEUN];
++ }
++
++ /* Check if we are all done */
++ if (thisEUN == targetEUN)
++ break;
++
++ if (INFTL_formatblock(inftl, thisEUN) < 0) {
++ /*
++ * Could not erase : mark block as reserved.
++ * FixMe: Update Bad Unit Table on disk.
++ */
++ inftl->PUtable[thisEUN] = BLOCK_RESERVED;
++ } else {
++ /* Correctly erased : mark it as free */
++ inftl->PUtable[thisEUN] = BLOCK_FREE;
++ inftl->PUtable[prevEUN] = BLOCK_NIL;
++ inftl->numfreeEUNs++;
++ }
++ }
++
++ return targetEUN;
++}
++
++u16 INFTL_makefreeblock(struct INFTLrecord *inftl, unsigned pendingblock)
++{
++ /*
++ * This is the part that needs some cleverness applied.
++ * For now, I'm doing the minimum applicable to actually
++ * get the thing to work.
++ * Wear-levelling and other clever stuff needs to be implemented
++ * and we also need to do some assessment of the results when
++ * the system loses power half-way through the routine.
++ */
++ u16 LongestChain = 0;
++ u16 ChainLength = 0, thislen;
++ u16 chain, EUN;
++
++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_makefreeblock(inftl=0x%x,"
++ "pending=%d)\n", (int)inftl, pendingblock);
++
++ for (chain = 0; chain < inftl->nb_blocks; chain++) {
++ EUN = inftl->VUtable[chain];
++ thislen = 0;
++
++ while (EUN <= inftl->lastEUN) {
++ thislen++;
++ EUN = inftl->PUtable[EUN];
++ if (thislen > 0xff00) {
++ printk(KERN_WARNING "INFTL: endless loop in "
++ "Virtual Chain %d: Unit %x\n",
++ chain, EUN);
++ /*
++ * Actually, don't return failure.
++ * Just ignore this chain and get on with it.
++ */
++ thislen = 0;
++ break;
++ }
++ }
++
++ if (thislen > ChainLength) {
++ ChainLength = thislen;
++ LongestChain = chain;
++ }
++ }
++
++ if (ChainLength < 2) {
++ printk(KERN_WARNING "INFTL: no Virtual Unit Chains available "
++ "for folding. Failing request\n");
++ return BLOCK_NIL;
++ }
++
++ return INFTL_foldchain(inftl, LongestChain, pendingblock);
++}
++
++static int nrbits(unsigned int val, int bitcount)
++{
++ int i, total = 0;
++
++ for (i = 0; (i < bitcount); i++)
++ total += (((0x1 << i) & val) ? 1 : 0);
++ return total;
++}
++
++/*
++ * INFTL_findwriteunit: Return the unit number into which we can write
++ * for this block. Make it available if it isn't already.
++ */
++static inline u16 INFTL_findwriteunit(struct INFTLrecord *inftl, unsigned block)
++{
++ unsigned int thisVUC = block / (inftl->EraseSize / SECTORSIZE);
++ unsigned int thisEUN, writeEUN, prev_block, status;
++ unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize -1);
++ struct inftl_oob oob;
++ struct inftl_bci bci;
++ unsigned char anac, nacs, parity;
++ size_t retlen;
++ int silly, silly2 = 3;
++
++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_findwriteunit(inftl=0x%x,"
++ "block=%d)\n", (int)inftl, block);
++
++ do {
++ /*
++ * Scan the media to find a unit in the VUC which has
++ * a free space for the block in question.
++ */
++ writeEUN = BLOCK_NIL;
++ thisEUN = inftl->VUtable[thisVUC];
++ silly = MAX_LOOPS;
++
++ while (thisEUN <= inftl->lastEUN) {
++ MTD_READOOB(inftl->mbd.mtd, (thisEUN * inftl->EraseSize) +
++ blockofs, 8, &retlen, (char *)&bci);
++
++ status = bci.Status | bci.Status1;
++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: status of block %d in "
++ "EUN %d is %x\n", block , writeEUN, status);
++
++ switch(status) {
++ case SECTOR_FREE:
++ writeEUN = thisEUN;
++ break;
++ case SECTOR_DELETED:
++ case SECTOR_USED:
++ /* Can't go any further */
++ goto hitused;
++ case SECTOR_IGNORE:
++ break;
++ default:
++ /*
++ * Invalid block. Don't use it any more.
++ * Must implement.
++ */
++ break;
++ }
++
++ if (!silly--) {
++ printk(KERN_WARNING "INFTL: infinite loop in "
++ "Virtual Unit Chain 0x%x\n", thisVUC);
++ return 0xffff;
++ }
++
++ /* Skip to next block in chain */
++ thisEUN = inftl->PUtable[thisEUN];
++ }
++
++hitused:
++ if (writeEUN != BLOCK_NIL)
++ return writeEUN;
++
++
++ /*
++ * OK. We didn't find one in the existing chain, or there
++ * is no existing chain. Allocate a new one.
++ */
++ writeEUN = INFTL_findfreeblock(inftl, 0);
++
++ if (writeEUN == BLOCK_NIL) {
++ /*
++ * That didn't work - there were no free blocks just
++ * waiting to be picked up. We're going to have to fold
++ * a chain to make room.
++ */
++ thisEUN = INFTL_makefreeblock(inftl, 0xffff);
++
++ /*
++ * Hopefully we free something, lets try again.
++ * This time we are desperate...
++ */
++ DEBUG(MTD_DEBUG_LEVEL1, "INFTL: using desperate==1 "
++ "to find free EUN to accommodate write to "
++ "VUC %d\n", thisVUC);
++ writeEUN = INFTL_findfreeblock(inftl, 1);
++ if (writeEUN == BLOCK_NIL) {
++ /*
++ * Ouch. This should never happen - we should
++ * always be able to make some room somehow.
++ * If we get here, we've allocated more storage
++ * space than actual media, or our makefreeblock
++ * routine is missing something.
++ */
++ printk(KERN_WARNING "INFTL: cannot make free "
++ "space.\n");
++#ifdef DEBUG
++ INFTL_dumptables(inftl);
++ INFTL_dumpVUchains(inftl);
++#endif
++ return BLOCK_NIL;
++ }
++ }
++
++ /*
++ * Insert new block into virtual chain. Firstly update the
++ * block headers in flash...
++ */
++ anac = 0;
++ nacs = 0;
++ thisEUN = inftl->VUtable[thisVUC];
++ if (thisEUN != BLOCK_NIL) {
++ MTD_READOOB(inftl->mbd.mtd, thisEUN * inftl->EraseSize
++ + 8, 8, &retlen, (char *)&oob.u);
++ anac = oob.u.a.ANAC + 1;
++ nacs = oob.u.a.NACs + 1;
++ }
++
++ prev_block = inftl->VUtable[thisVUC];
++ if (prev_block < inftl->nb_blocks)
++ prev_block -= inftl->firstEUN;
++
++ parity = (nrbits(thisVUC, 16) & 0x1) ? 0x1 : 0;
++ parity |= (nrbits(prev_block, 16) & 0x1) ? 0x2 : 0;
++ parity |= (nrbits(anac, 8) & 0x1) ? 0x4 : 0;
++ parity |= (nrbits(nacs, 8) & 0x1) ? 0x8 : 0;
++
++ oob.u.a.virtualUnitNo = cpu_to_le16(thisVUC);
++ oob.u.a.prevUnitNo = cpu_to_le16(prev_block);
++ oob.u.a.ANAC = anac;
++ oob.u.a.NACs = nacs;
++ oob.u.a.parityPerField = parity;
++ oob.u.a.discarded = 0xaa;
++
++ MTD_WRITEOOB(inftl->mbd.mtd, writeEUN * inftl->EraseSize + 8, 8,
++ &retlen, (char *)&oob.u);
++
++ /* Also back up header... */
++ oob.u.b.virtualUnitNo = cpu_to_le16(thisVUC);
++ oob.u.b.prevUnitNo = cpu_to_le16(prev_block);
++ oob.u.b.ANAC = anac;
++ oob.u.b.NACs = nacs;
++ oob.u.b.parityPerField = parity;
++ oob.u.b.discarded = 0xaa;
++
++ MTD_WRITEOOB(inftl->mbd.mtd, writeEUN * inftl->EraseSize +
++ SECTORSIZE * 4 + 8, 8, &retlen, (char *)&oob.u);
++
++ inftl->PUtable[writeEUN] = inftl->VUtable[thisVUC];
++ inftl->VUtable[thisVUC] = writeEUN;
++
++ inftl->numfreeEUNs--;
++ return writeEUN;
++
++ } while (silly2--);
++
++ printk(KERN_WARNING "INFTL: error folding to make room for Virtual "
++ "Unit Chain 0x%x\n", thisVUC);
++ return 0xffff;
++}
++
++/*
++ * Given a Virtual Unit Chain, see if it can be deleted, and if so do it.
++ */
++static void INFTL_trydeletechain(struct INFTLrecord *inftl, unsigned thisVUC)
++{
++ unsigned char BlockUsed[MAX_SECTORS_PER_UNIT];
++ unsigned char BlockDeleted[MAX_SECTORS_PER_UNIT];
++ unsigned int thisEUN, status;
++ int block, silly;
++ struct inftl_bci bci;
++ size_t retlen;
++
++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_trydeletechain(inftl=0x%x,"
++ "thisVUC=%d)\n", (int)inftl, thisVUC);
++
++ memset(BlockUsed, 0, sizeof(BlockUsed));
++ memset(BlockDeleted, 0, sizeof(BlockDeleted));
++
++ thisEUN = inftl->VUtable[thisVUC];
++ if (thisEUN == BLOCK_NIL) {
++ printk(KERN_WARNING "INFTL: trying to delete non-existent "
++ "Virtual Unit Chain %d!\n", thisVUC);
++ return;
++ }
++
++ /*
++ * Scan through the Erase Units to determine whether any data is in
++ * each of the 512-byte blocks within the Chain.
++ */
++ silly = MAX_LOOPS;
++ while (thisEUN < inftl->nb_blocks) {
++ for (block = 0; block < inftl->EraseSize/SECTORSIZE; block++) {
++ if (BlockUsed[block] || BlockDeleted[block])
++ continue;
++
++ if (MTD_READOOB(inftl->mbd.mtd, (thisEUN * inftl->EraseSize)
++ + (block * SECTORSIZE), 8 , &retlen,
++ (char *)&bci) < 0)
++ status = SECTOR_IGNORE;
++ else
++ status = bci.Status | bci.Status1;
++
++ switch(status) {
++ case SECTOR_FREE:
++ case SECTOR_IGNORE:
++ break;
++ case SECTOR_USED:
++ BlockUsed[block] = 1;
++ continue;
++ case SECTOR_DELETED:
++ BlockDeleted[block] = 1;
++ continue;
++ default:
++ printk(KERN_WARNING "INFTL: unknown status "
++ "for block %d in EUN %d: 0x%x\n",
++ block, thisEUN, status);
++ }
++ }
++
++ if (!silly--) {
++ printk(KERN_WARNING "INFTL: infinite loop in Virtual "
++ "Unit Chain 0x%x\n", thisVUC);
++ return;
++ }
++
++ thisEUN = inftl->PUtable[thisEUN];
++ }
++
++ for (block = 0; block < inftl->EraseSize/SECTORSIZE; block++)
++ if (BlockUsed[block])
++ return;
++
++ /*
++ * For each block in the chain free it and make it available
++ * for future use. Erase from the oldest unit first.
++ */
++ DEBUG(MTD_DEBUG_LEVEL1, "INFTL: deleting empty VUC %d\n", thisVUC);
++
++ for (;;) {
++ u16 *prevEUN = &inftl->VUtable[thisVUC];
++ thisEUN = *prevEUN;
++
++ /* If the chain is all gone already, we're done */
++ if (thisEUN == BLOCK_NIL) {
++ DEBUG(MTD_DEBUG_LEVEL2, "INFTL: Empty VUC %d for deletion was already absent\n", thisEUN);
++ return;
++ }
++
++ /* Find oldest unit in chain. */
++ while (inftl->PUtable[thisEUN] != BLOCK_NIL) {
++ BUG_ON(thisEUN >= inftl->nb_blocks);
++
++ prevEUN = &inftl->PUtable[thisEUN];
++ thisEUN = *prevEUN;
++ }
++
++ DEBUG(MTD_DEBUG_LEVEL3, "Deleting EUN %d from VUC %d\n",
++ thisEUN, thisVUC);
++
++ if (INFTL_formatblock(inftl, thisEUN) < 0) {
++ /*
++ * Could not erase : mark block as reserved.
++ * FixMe: Update Bad Unit Table on medium.
++ */
++ inftl->PUtable[thisEUN] = BLOCK_RESERVED;
++ } else {
++ /* Correctly erased : mark it as free */
++ inftl->PUtable[thisEUN] = BLOCK_FREE;
++ inftl->numfreeEUNs++;
++ }
++
++ /* Now sort out whatever was pointing to it... */
++ *prevEUN = BLOCK_NIL;
++
++ /* Ideally we'd actually be responsive to new
++ requests while we're doing this -- if there's
++ free space why should others be made to wait? */
++ cond_resched();
++ }
++
++ inftl->VUtable[thisVUC] = BLOCK_NIL;
++}
++
++static int INFTL_deleteblock(struct INFTLrecord *inftl, unsigned block)
++{
++ unsigned int thisEUN = inftl->VUtable[block / (inftl->EraseSize / SECTORSIZE)];
++ unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize - 1);
++ unsigned int status;
++ int silly = MAX_LOOPS;
++ size_t retlen;
++ struct inftl_bci bci;
++
++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_deleteblock(inftl=0x%x,"
++ "block=%d)\n", (int)inftl, block);
++
++ while (thisEUN < inftl->nb_blocks) {
++ if (MTD_READOOB(inftl->mbd.mtd, (thisEUN * inftl->EraseSize) +
++ blockofs, 8, &retlen, (char *)&bci) < 0)
++ status = SECTOR_IGNORE;
++ else
++ status = bci.Status | bci.Status1;
++
++ switch (status) {
++ case SECTOR_FREE:
++ case SECTOR_IGNORE:
++ break;
++ case SECTOR_DELETED:
++ thisEUN = BLOCK_NIL;
++ goto foundit;
++ case SECTOR_USED:
++ goto foundit;
++ default:
++ printk(KERN_WARNING "INFTL: unknown status for "
++ "block %d in EUN %d: 0x%x\n",
++ block, thisEUN, status);
++ break;
++ }
++
++ if (!silly--) {
++ printk(KERN_WARNING "INFTL: infinite loop in Virtual "
++ "Unit Chain 0x%x\n",
++ block / (inftl->EraseSize / SECTORSIZE));
++ return 1;
++ }
++ thisEUN = inftl->PUtable[thisEUN];
++ }
++
++foundit:
++ if (thisEUN != BLOCK_NIL) {
++ loff_t ptr = (thisEUN * inftl->EraseSize) + blockofs;
++
++ if (MTD_READOOB(inftl->mbd.mtd, ptr, 8, &retlen, (char *)&bci) < 0)
++ return -EIO;
++ bci.Status = bci.Status1 = SECTOR_DELETED;
++ if (MTD_WRITEOOB(inftl->mbd.mtd, ptr, 8, &retlen, (char *)&bci) < 0)
++ return -EIO;
++ INFTL_trydeletechain(inftl, block / (inftl->EraseSize / SECTORSIZE));
++ }
++ return 0;
++}
++
++static int inftl_writeblock(struct mtd_blktrans_dev *mbd, unsigned long block,
++ char *buffer)
++{
++ struct INFTLrecord *inftl = (void *)mbd;
++ unsigned int writeEUN;
++ unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize - 1);
++ size_t retlen;
++ u8 eccbuf[6];
++ char *p, *pend;
++
++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: inftl_writeblock(inftl=0x%x,block=%d,"
++ "buffer=0x%x)\n", (int)inftl, block, (int)buffer);
++
++ /* Is block all zero? */
++ pend = buffer + SECTORSIZE;
++ for (p = buffer; p < pend && !*p; p++)
++ ;
++
++ if (p < pend) {
++ writeEUN = INFTL_findwriteunit(inftl, block);
++
++ if (writeEUN == BLOCK_NIL) {
++ printk(KERN_WARNING "inftl_writeblock(): cannot find "
++ "block to write to\n");
++ /*
++ * If we _still_ haven't got a block to use,
++ * we're screwed.
++ */
++ return 1;
++ }
++
++ MTD_WRITEECC(inftl->mbd.mtd, (writeEUN * inftl->EraseSize) +
++ blockofs, SECTORSIZE, &retlen, (char *)buffer,
++ (char *)eccbuf, NULL);
++ /*
++ * No need to write SECTOR_USED flags since they are written
++ * in mtd_writeecc
++ */
++ } else {
++ INFTL_deleteblock(inftl, block);
++ }
++
++ return 0;
++}
++
++static int inftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block,
++ char *buffer)
++{
++ struct INFTLrecord *inftl = (void *)mbd;
++ unsigned int thisEUN = inftl->VUtable[block / (inftl->EraseSize / SECTORSIZE)];
++ unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize - 1);
++ unsigned int status;
++ int silly = MAX_LOOPS;
++ struct inftl_bci bci;
++ size_t retlen;
++
++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: inftl_readblock(inftl=0x%x,block=%d,"
++ "buffer=0x%x)\n", (int)inftl, block, (int)buffer);
++
++ while (thisEUN < inftl->nb_blocks) {
++ if (MTD_READOOB(inftl->mbd.mtd, (thisEUN * inftl->EraseSize) +
++ blockofs, 8, &retlen, (char *)&bci) < 0)
++ status = SECTOR_IGNORE;
++ else
++ status = bci.Status | bci.Status1;
++
++ switch (status) {
++ case SECTOR_DELETED:
++ thisEUN = BLOCK_NIL;
++ goto foundit;
++ case SECTOR_USED:
++ goto foundit;
++ case SECTOR_FREE:
++ case SECTOR_IGNORE:
++ break;
++ default:
++ printk(KERN_WARNING "INFTL: unknown status for "
++ "block %ld in EUN %d: 0x%04x\n",
++ block, thisEUN, status);
++ break;
++ }
++
++ if (!silly--) {
++ printk(KERN_WARNING "INFTL: infinite loop in "
++ "Virtual Unit Chain 0x%lx\n",
++ block / (inftl->EraseSize / SECTORSIZE));
++ return 1;
++ }
++
++ thisEUN = inftl->PUtable[thisEUN];
++ }
++
++foundit:
++ if (thisEUN == BLOCK_NIL) {
++ /* The requested block is not on the media, return all 0x00 */
++ memset(buffer, 0, SECTORSIZE);
++ } else {
++ size_t retlen;
++ loff_t ptr = (thisEUN * inftl->EraseSize) + blockofs;
++ u_char eccbuf[6];
++ if (MTD_READECC(inftl->mbd.mtd, ptr, SECTORSIZE, &retlen,
++ buffer, eccbuf, NULL))
++ return -EIO;
++ }
++ return 0;
++}
++
++static int inftl_getgeo(struct mtd_blktrans_dev *dev, struct hd_geometry *geo)
++{
++ struct INFTLrecord *inftl = (void *)dev;
++
++ geo->heads = inftl->heads;
++ geo->sectors = inftl->sectors;
++ geo->cylinders = inftl->cylinders;
++
++ return 0;
++}
++
++struct mtd_blktrans_ops inftl_tr = {
++ .name = "inftl",
++ .major = INFTL_MAJOR,
++ .part_bits = INFTL_PARTN_BITS,
++ .getgeo = inftl_getgeo,
++ .readsect = inftl_readblock,
++ .writesect = inftl_writeblock,
++ .add_mtd = inftl_add_mtd,
++ .remove_dev = inftl_remove_dev,
++ .owner = THIS_MODULE,
++};
++
++extern char inftlmountrev[];
++
++int __init init_inftl(void)
++{
++ printk(KERN_INFO "INFTL: inftlcore.c $Revision$, "
++ "inftlmount.c %s\n", inftlmountrev);
++
++ return register_mtd_blktrans(&inftl_tr);
++}
++
++static void __exit cleanup_inftl(void)
++{
++ deregister_mtd_blktrans(&inftl_tr);
++}
++
++module_init(init_inftl);
++module_exit(cleanup_inftl);
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Greg Ungerer <gerg@snapgear.com>, David Woodhouse <dwmw2@infradead.org>, Fabrice Bellard <fabrice.bellard@netgem.com> et al.");
++MODULE_DESCRIPTION("Support code for Inverse Flash Translation Layer, used on M-Systems DiskOnChip 2000, Millennium and Millennium Plus");
+diff -Nurb linux-mips-2.4.27/drivers/mtd/inftlmount.c linux/drivers/mtd/inftlmount.c
+--- linux-mips-2.4.27/drivers/mtd/inftlmount.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/mtd/inftlmount.c 2004-11-19 10:25:11.633240224 +0100
+@@ -0,0 +1,817 @@
++/*
++ * inftlmount.c -- INFTL mount code with extensive checks.
++ *
++ * Author: Greg Ungerer (gerg@snapgear.com)
++ * (C) Copyright 2002-2003, Greg Ungerer (gerg@snapgear.com)
++ *
++ * Based heavily on the nftlmount.c code which is:
++ * Author: Fabrice Bellard (fabrice.bellard@netgem.com)
++ * Copyright (C) 2000 Netgem S.A.
++ *
++ * $Id$
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ */
++
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <asm/errno.h>
++#include <asm/io.h>
++#include <asm/uaccess.h>
++#include <linux/miscdevice.h>
++#include <linux/pci.h>
++#include <linux/delay.h>
++#include <linux/slab.h>
++#include <linux/sched.h>
++#include <linux/init.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/nftl.h>
++#include <linux/mtd/inftl.h>
++#include <linux/mtd/compatmac.h>
++
++char inftlmountrev[]="$Revision$";
++
++/*
++ * find_boot_record: Find the INFTL Media Header and its Spare copy which
++ * contains the various device information of the INFTL partition and
++ * Bad Unit Table. Update the PUtable[] table according to the Bad
++ * Unit Table. PUtable[] is used for management of Erase Unit in
++ * other routines in inftlcore.c and inftlmount.c.
++ */
++static int find_boot_record(struct INFTLrecord *inftl)
++{
++ struct inftl_unittail h1;
++ //struct inftl_oob oob;
++ unsigned int i, block, boot_record_count = 0;
++ u8 buf[SECTORSIZE];
++ struct INFTLMediaHeader *mh = &inftl->MediaHdr;
++ struct INFTLPartition *ip;
++ int retlen;
++
++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: find_boot_record(inftl=0x%x)\n",
++ (int)inftl);
++
++ /*
++ * Assume logical EraseSize == physical erasesize for starting the
++ * scan. We'll sort it out later if we find a MediaHeader which says
++ * otherwise.
++ */
++ inftl->EraseSize = inftl->mbd.mtd->erasesize;
++ inftl->nb_blocks = inftl->mbd.mtd->size / inftl->EraseSize;
++
++ inftl->MediaUnit = BLOCK_NIL;
++ inftl->SpareMediaUnit = BLOCK_NIL;
++
++ /* Search for a valid boot record */
++ for (block = 0; block < inftl->nb_blocks; block++) {
++ int ret;
++
++ /*
++ * Check for BNAND header first. Then whinge if it's found
++ * but later checks fail.
++ */
++ if ((ret = MTD_READ(inftl->mbd.mtd, block * inftl->EraseSize,
++ SECTORSIZE, &retlen, buf))) {
++ static int warncount = 5;
++
++ if (warncount) {
++ printk(KERN_WARNING "INFTL: block read at 0x%x "
++ "of mtd%d failed: %d\n",
++ block * inftl->EraseSize,
++ inftl->mbd.mtd->index, ret);
++ if (!--warncount)
++ printk(KERN_WARNING "INFTL: further "
++ "failures for this block will "
++ "not be printed\n");
++ }
++ continue;
++ }
++
++ if (retlen < 6 || memcmp(buf, "BNAND", 6)) {
++ /* BNAND\0 not found. Continue */
++ continue;
++ }
++
++ /* To be safer with BIOS, also use erase mark as discriminant */
++ if ((ret = MTD_READOOB(inftl->mbd.mtd, block * inftl->EraseSize +
++ SECTORSIZE + 8, 8, &retlen, (char *)&h1) < 0)) {
++ printk(KERN_WARNING "INFTL: ANAND header found at "
++ "0x%x in mtd%d, but OOB data read failed "
++ "(err %d)\n", block * inftl->EraseSize,
++ inftl->mbd.mtd->index, ret);
++ continue;
++ }
++
++ if (boot_record_count) {
++ /*
++ * We've already processed one. So we just check if
++ * this one is the same as the first one we found.
++ */
++ if (memcmp(mh, buf, sizeof(struct INFTLMediaHeader))) {
++ printk(KERN_WARNING "INFTL: Media Headers at "
++ "0x%x and 0x%x disagree.\n",
++ inftl->MediaUnit * inftl->EraseSize,
++ block * inftl->EraseSize);
++ return -1;
++ }
++ if (boot_record_count == 1)
++ inftl->SpareMediaUnit = block;
++
++ /*
++ * Mark this boot record (INFTL MediaHeader) block as
++ * reserved.
++ */
++ inftl->PUtable[block] = BLOCK_RESERVED;
++
++ boot_record_count++;
++ continue;
++ }
++
++ /*
++ * This is the first we've seen.
++ * Copy the media header structure into place.
++ */
++ memcpy(mh, buf, sizeof(struct INFTLMediaHeader));
++ mh->NoOfBootImageBlocks = le32_to_cpu(mh->NoOfBootImageBlocks);
++ mh->NoOfBinaryPartitions = le32_to_cpu(mh->NoOfBinaryPartitions);
++ mh->NoOfBDTLPartitions = le32_to_cpu(mh->NoOfBDTLPartitions);
++ mh->BlockMultiplierBits = le32_to_cpu(mh->BlockMultiplierBits);
++ mh->FormatFlags = le32_to_cpu(mh->FormatFlags);
++ mh->PercentUsed = le32_to_cpu(mh->PercentUsed);
++
++#ifdef CONFIG_MTD_DEBUG_VERBOSE
++ if (CONFIG_MTD_DEBUG_VERBOSE >= 2) {
++ printk("INFTL: Media Header ->\n"
++ " bootRecordID = %s\n"
++ " NoOfBootImageBlocks = %d\n"
++ " NoOfBinaryPartitions = %d\n"
++ " NoOfBDTLPartitions = %d\n"
++ " BlockMultiplerBits = %d\n"
++ " FormatFlgs = %d\n"
++ " OsakVersion = 0x%x\n"
++ " PercentUsed = %d\n",
++ mh->bootRecordID, mh->NoOfBootImageBlocks,
++ mh->NoOfBinaryPartitions,
++ mh->NoOfBDTLPartitions,
++ mh->BlockMultiplierBits, mh->FormatFlags,
++ mh->OsakVersion, mh->PercentUsed);
++ }
++#endif
++
++ if (mh->NoOfBDTLPartitions == 0) {
++ printk(KERN_WARNING "INFTL: Media Header sanity check "
++ "failed: NoOfBDTLPartitions (%d) == 0, "
++ "must be at least 1\n", mh->NoOfBDTLPartitions);
++ return -1;
++ }
++
++ if ((mh->NoOfBDTLPartitions + mh->NoOfBinaryPartitions) > 4) {
++ printk(KERN_WARNING "INFTL: Media Header sanity check "
++ "failed: Total Partitions (%d) > 4, "
++ "BDTL=%d Binary=%d\n", mh->NoOfBDTLPartitions +
++ mh->NoOfBinaryPartitions,
++ mh->NoOfBDTLPartitions,
++ mh->NoOfBinaryPartitions);
++ return -1;
++ }
++
++ if (mh->BlockMultiplierBits > 1) {
++ printk(KERN_WARNING "INFTL: sorry, we don't support "
++ "UnitSizeFactor 0x%02x\n",
++ mh->BlockMultiplierBits);
++ return -1;
++ } else if (mh->BlockMultiplierBits == 1) {
++ printk(KERN_WARNING "INFTL: support for INFTL with "
++ "UnitSizeFactor 0x%02x is experimental\n",
++ mh->BlockMultiplierBits);
++ inftl->EraseSize = inftl->mbd.mtd->erasesize <<
++ (0xff - mh->BlockMultiplierBits);
++ inftl->nb_blocks = inftl->mbd.mtd->size / inftl->EraseSize;
++ }
++
++ /* Scan the partitions */
++ for (i = 0; (i < 4); i++) {
++ ip = &mh->Partitions[i];
++ ip->virtualUnits = le32_to_cpu(ip->virtualUnits);
++ ip->firstUnit = le32_to_cpu(ip->firstUnit);
++ ip->lastUnit = le32_to_cpu(ip->lastUnit);
++ ip->flags = le32_to_cpu(ip->flags);
++ ip->spareUnits = le32_to_cpu(ip->spareUnits);
++ ip->Reserved0 = le32_to_cpu(ip->Reserved0);
++
++#ifdef CONFIG_MTD_DEBUG_VERBOSE
++ if (CONFIG_MTD_DEBUG_VERBOSE >= 2) {
++ printk(" PARTITION[%d] ->\n"
++ " virtualUnits = %d\n"
++ " firstUnit = %d\n"
++ " lastUnit = %d\n"
++ " flags = 0x%x\n"
++ " spareUnits = %d\n",
++ i, ip->virtualUnits, ip->firstUnit,
++ ip->lastUnit, ip->flags,
++ ip->spareUnits);
++ }
++#endif
++
++ if (ip->Reserved0 != ip->firstUnit) {
++ struct erase_info *instr = &inftl->instr;
++
++ /*
++ * Most likely this is using the
++ * undocumented qiuck mount feature.
++ * We don't support that, we will need
++ * to erase the hidden block for full
++ * compatibility.
++ */
++ instr->addr = ip->Reserved0 * inftl->EraseSize;
++ instr->len = inftl->EraseSize;
++ MTD_ERASE(inftl->mbd.mtd, instr);
++ }
++ if ((ip->lastUnit - ip->firstUnit + 1) < ip->virtualUnits) {
++ printk(KERN_WARNING "INFTL: Media Header "
++ "Partition %d sanity check failed\n"
++ " firstUnit %d : lastUnit %d > "
++ "virtualUnits %d\n", i, ip->lastUnit,
++ ip->firstUnit, ip->Reserved0);
++ return -1;
++ }
++ if (ip->Reserved1 != 0) {
++ printk(KERN_WARNING "INFTL: Media Header "
++ "Partition %d sanity check failed: "
++ "Reserved1 %d != 0\n",
++ i, ip->Reserved1);
++ return -1;
++ }
++
++ if (ip->flags & INFTL_BDTL)
++ break;
++ }
++
++ if (i >= 4) {
++ printk(KERN_WARNING "INFTL: Media Header Partition "
++ "sanity check failed:\n No partition "
++ "marked as Disk Partition\n");
++ return -1;
++ }
++
++ inftl->nb_boot_blocks = ip->firstUnit;
++ inftl->numvunits = ip->virtualUnits;
++ if (inftl->numvunits > (inftl->nb_blocks -
++ inftl->nb_boot_blocks - 2)) {
++ printk(KERN_WARNING "INFTL: Media Header sanity check "
++ "failed:\n numvunits (%d) > nb_blocks "
++ "(%d) - nb_boot_blocks(%d) - 2\n",
++ inftl->numvunits, inftl->nb_blocks,
++ inftl->nb_boot_blocks);
++ return -1;
++ }
++
++ inftl->mbd.size = inftl->numvunits *
++ (inftl->EraseSize / SECTORSIZE);
++
++ /*
++ * Block count is set to last used EUN (we won't need to keep
++ * any meta-data past that point).
++ */
++ inftl->firstEUN = ip->firstUnit;
++ inftl->lastEUN = ip->lastUnit;
++ inftl->nb_blocks = ip->lastUnit + 1;
++
++ /* Memory alloc */
++ inftl->PUtable = kmalloc(inftl->nb_blocks * sizeof(u16), GFP_KERNEL);
++ if (!inftl->PUtable) {
++ printk(KERN_WARNING "INFTL: allocation of PUtable "
++ "failed (%d bytes)\n",
++ inftl->nb_blocks * sizeof(u16));
++ return -ENOMEM;
++ }
++
++ inftl->VUtable = kmalloc(inftl->nb_blocks * sizeof(u16), GFP_KERNEL);
++ if (!inftl->VUtable) {
++ kfree(inftl->PUtable);
++ printk(KERN_WARNING "INFTL: allocation of VUtable "
++ "failed (%d bytes)\n",
++ inftl->nb_blocks * sizeof(u16));
++ return -ENOMEM;
++ }
++
++ /* Mark the blocks before INFTL MediaHeader as reserved */
++ for (i = 0; i < inftl->nb_boot_blocks; i++)
++ inftl->PUtable[i] = BLOCK_RESERVED;
++ /* Mark all remaining blocks as potentially containing data */
++ for (; i < inftl->nb_blocks; i++)
++ inftl->PUtable[i] = BLOCK_NOTEXPLORED;
++
++ /* Mark this boot record (NFTL MediaHeader) block as reserved */
++ inftl->PUtable[block] = BLOCK_RESERVED;
++
++#if 0
++ /* Read Bad Erase Unit Table and modify PUtable[] accordingly */
++ for (i = 0; i < inftl->nb_blocks; i++) {
++ if ((i & (SECTORSIZE - 1)) == 0) {
++ /* read one sector for every SECTORSIZE of blocks */
++ if ((ret = MTD_READECC(inftl->mbd.mtd,
++ block * inftl->EraseSize + i + SECTORSIZE,
++ SECTORSIZE, &retlen, buf,
++ (char *)&oob, NULL)) < 0) {
++ printk(KERN_WARNING "INFTL: read of "
++ "bad sector table failed "
++ "(err %d)\n", ret);
++ kfree(inftl->VUtable);
++ kfree(inftl->PUtable);
++ return -1;
++ }
++ }
++ /* Mark the Bad Erase Unit as RESERVED in PUtable */
++ if (buf[i & (SECTORSIZE - 1)] != 0xff)
++ inftl->PUtable[i] = BLOCK_RESERVED;
++ }
++#endif
++
++ inftl->MediaUnit = block;
++ boot_record_count++;
++ }
++
++ return boot_record_count ? 0 : -1;
++}
++
++static int memcmpb(void *a, int c, int n)
++{
++ int i;
++ for (i = 0; i < n; i++) {
++ if (c != ((unsigned char *)a)[i])
++ return 1;
++ }
++ return 0;
++}
++
++/*
++ * check_free_sector: check if a free sector is actually FREE,
++ * i.e. All 0xff in data and oob area.
++ */
++static int check_free_sectors(struct INFTLrecord *inftl, unsigned int address,
++ int len, int check_oob)
++{
++ int i, retlen;
++ u8 buf[SECTORSIZE];
++
++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: check_free_sectors(inftl=0x%x,"
++ "address=0x%x,len=%d,check_oob=%d)\n", (int)inftl,
++ address, len, check_oob);
++
++ for (i = 0; i < len; i += SECTORSIZE) {
++ /*
++ * We want to read the sector without ECC check here since a
++ * free sector does not have ECC syndrome on it yet.
++ */
++ if (MTD_READ(inftl->mbd.mtd, address, SECTORSIZE, &retlen, buf) < 0)
++ return -1;
++ if (memcmpb(buf, 0xff, SECTORSIZE) != 0)
++ return -1;
++
++ if (check_oob) {
++ if (MTD_READOOB(inftl->mbd.mtd, address,
++ inftl->mbd.mtd->oobsize, &retlen, buf) < 0)
++ return -1;
++ if (memcmpb(buf, 0xff, inftl->mbd.mtd->oobsize) != 0)
++ return -1;
++ }
++ address += SECTORSIZE;
++ }
++
++ return 0;
++}
++
++/*
++ * INFTL_format: format a Erase Unit by erasing ALL Erase Zones in the Erase
++ * Unit and Update INFTL metadata. Each erase operation is
++ * checked with check_free_sectors.
++ *
++ * Return: 0 when succeed, -1 on error.
++ *
++ * ToDo: 1. Is it neceressary to check_free_sector after erasing ??
++ * 2. UnitSizeFactor != 0xFF
++ */
++int INFTL_formatblock(struct INFTLrecord *inftl, int block)
++{
++ int retlen;
++ struct inftl_unittail uci;
++ struct erase_info *instr = &inftl->instr;
++
++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_formatblock(inftl=0x%x,"
++ "block=%d)\n", (int)inftl, block);
++
++ memset(instr, 0, sizeof(struct erase_info));
++
++ /* FIXME: Shouldn't we be setting the 'discarded' flag to zero
++ _first_? */
++
++ /* Use async erase interface, test return code */
++ instr->addr = block * inftl->EraseSize;
++ instr->len = inftl->EraseSize;
++ MTD_ERASE(inftl->mbd.mtd, instr);
++
++ if (instr->state == MTD_ERASE_FAILED) {
++ /*
++ * Could not format, FixMe: We should update the BadUnitTable
++ * both in memory and on disk.
++ */
++ printk(KERN_WARNING "INFTL: error while formatting block %d\n",
++ block);
++ return -1;
++ }
++
++ /*
++ * Check the "freeness" of Erase Unit before updating metadata.
++ * FixMe: is this check really necessary? Since we have check the
++ * return code after the erase operation.
++ */
++ if (check_free_sectors(inftl, instr->addr, inftl->EraseSize, 1) != 0)
++ return -1;
++
++ uci.EraseMark = cpu_to_le16(ERASE_MARK);
++ uci.EraseMark1 = cpu_to_le16(ERASE_MARK);
++ uci.Reserved[0] = 0;
++ uci.Reserved[1] = 0;
++ uci.Reserved[2] = 0;
++ uci.Reserved[3] = 0;
++ if (MTD_WRITEOOB(inftl->mbd.mtd, block * inftl->EraseSize + SECTORSIZE * 2 +
++ 8, 8, &retlen, (char *)&uci) < 0)
++ return -1;
++ return 0;
++}
++
++/*
++ * format_chain: Format an invalid Virtual Unit chain. It frees all the Erase
++ * Units in a Virtual Unit Chain, i.e. all the units are disconnected.
++ *
++ * Since the chain is invalid then we will have to erase it from its
++ * head (normally for INFTL we go from the oldest). But if it has a
++ * loop then there is no oldest...
++ */
++static void format_chain(struct INFTLrecord *inftl, unsigned int first_block)
++{
++ unsigned int block = first_block, block1;
++
++ printk(KERN_WARNING "INFTL: formatting chain at block %d\n",
++ first_block);
++
++ for (;;) {
++ block1 = inftl->PUtable[block];
++
++ printk(KERN_WARNING "INFTL: formatting block %d\n", block);
++ if (INFTL_formatblock(inftl, block) < 0) {
++ /*
++ * Cannot format !!!! Mark it as Bad Unit,
++ * FixMe: update the BadUnitTable on disk.
++ */
++ inftl->PUtable[block] = BLOCK_RESERVED;
++ } else {
++ inftl->PUtable[block] = BLOCK_FREE;
++ }
++
++ /* Goto next block on the chain */
++ block = block1;
++
++ if (block == BLOCK_NIL || block >= inftl->lastEUN)
++ break;
++ }
++}
++
++void INFTL_dumptables(struct INFTLrecord *s)
++{
++ int i;
++
++ printk("-------------------------------------------"
++ "----------------------------------\n");
++
++ printk("VUtable[%d] ->", s->nb_blocks);
++ for (i = 0; i < s->nb_blocks; i++) {
++ if ((i % 8) == 0)
++ printk("\n%04x: ", i);
++ printk("%04x ", s->VUtable[i]);
++ }
++
++ printk("\n-------------------------------------------"
++ "----------------------------------\n");
++
++ printk("PUtable[%d-%d=%d] ->", s->firstEUN, s->lastEUN, s->nb_blocks);
++ for (i = 0; i <= s->lastEUN; i++) {
++ if ((i % 8) == 0)
++ printk("\n%04x: ", i);
++ printk("%04x ", s->PUtable[i]);
++ }
++
++ printk("\n-------------------------------------------"
++ "----------------------------------\n");
++
++ printk("INFTL ->\n"
++ " EraseSize = %d\n"
++ " h/s/c = %d/%d/%d\n"
++ " numvunits = %d\n"
++ " firstEUN = %d\n"
++ " lastEUN = %d\n"
++ " numfreeEUNs = %d\n"
++ " LastFreeEUN = %d\n"
++ " nb_blocks = %d\n"
++ " nb_boot_blocks = %d",
++ s->EraseSize, s->heads, s->sectors, s->cylinders,
++ s->numvunits, s->firstEUN, s->lastEUN, s->numfreeEUNs,
++ s->LastFreeEUN, s->nb_blocks, s->nb_boot_blocks);
++
++ printk("\n-------------------------------------------"
++ "----------------------------------\n");
++}
++
++void INFTL_dumpVUchains(struct INFTLrecord *s)
++{
++ int logical, block, i;
++
++ printk("-------------------------------------------"
++ "----------------------------------\n");
++
++ printk("INFTL Virtual Unit Chains:\n");
++ for (logical = 0; logical < s->nb_blocks; logical++) {
++ block = s->VUtable[logical];
++ if (block > s->nb_blocks)
++ continue;
++ printk(" LOGICAL %d --> %d ", logical, block);
++ for (i = 0; i < s->nb_blocks; i++) {
++ if (s->PUtable[block] == BLOCK_NIL)
++ break;
++ block = s->PUtable[block];
++ printk("%d ", block);
++ }
++ printk("\n");
++ }
++
++ printk("-------------------------------------------"
++ "----------------------------------\n");
++}
++
++int INFTL_mount(struct INFTLrecord *s)
++{
++ unsigned int block, first_block, prev_block, last_block;
++ unsigned int first_logical_block, logical_block, erase_mark;
++ int chain_length, do_format_chain;
++ struct inftl_unithead1 h0;
++ struct inftl_unittail h1;
++ int i, retlen;
++ u8 *ANACtable, ANAC;
++
++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_mount(inftl=0x%x)\n", (int)s);
++
++ /* Search for INFTL MediaHeader and Spare INFTL Media Header */
++ if (find_boot_record(s) < 0) {
++ printk(KERN_WARNING "INFTL: could not find valid boot record?\n");
++ return -1;
++ }
++
++ /* Init the logical to physical table */
++ for (i = 0; i < s->nb_blocks; i++)
++ s->VUtable[i] = BLOCK_NIL;
++
++ logical_block = block = BLOCK_NIL;
++
++ /* Temporary buffer to store ANAC numbers. */
++ ANACtable = kmalloc(s->nb_blocks * sizeof(u8), GFP_KERNEL);
++ memset(ANACtable, 0, s->nb_blocks);
++
++ /*
++ * First pass is to explore each physical unit, and construct the
++ * virtual chains that exist (newest physical unit goes into VUtable).
++ * Any block that is in any way invalid will be left in the
++ * NOTEXPLORED state. Then at the end we will try to format it and
++ * mark it as free.
++ */
++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: pass 1, explore each unit\n");
++ for (first_block = s->firstEUN; first_block <= s->lastEUN; first_block++) {
++ if (s->PUtable[first_block] != BLOCK_NOTEXPLORED)
++ continue;
++
++ do_format_chain = 0;
++ first_logical_block = BLOCK_NIL;
++ last_block = BLOCK_NIL;
++ block = first_block;
++
++ for (chain_length = 0; ; chain_length++) {
++
++ if ((chain_length == 0) &&
++ (s->PUtable[block] != BLOCK_NOTEXPLORED)) {
++ /* Nothing to do here, onto next block */
++ break;
++ }
++
++ if (MTD_READOOB(s->mbd.mtd, block * s->EraseSize + 8,
++ 8, &retlen, (char *)&h0) < 0 ||
++ MTD_READOOB(s->mbd.mtd, block * s->EraseSize +
++ 2 * SECTORSIZE + 8, 8, &retlen, (char *)&h1) < 0) {
++ /* Should never happen? */
++ do_format_chain++;
++ break;
++ }
++
++ logical_block = le16_to_cpu(h0.virtualUnitNo);
++ prev_block = le16_to_cpu(h0.prevUnitNo);
++ erase_mark = le16_to_cpu((h1.EraseMark | h1.EraseMark1));
++ ANACtable[block] = h0.ANAC;
++
++ /* Previous block is relative to start of Partition */
++ if (prev_block < s->nb_blocks)
++ prev_block += s->firstEUN;
++
++ /* Already explored partial chain? */
++ if (s->PUtable[block] != BLOCK_NOTEXPLORED) {
++ /* Check if chain for this logical */
++ if (logical_block == first_logical_block) {
++ if (last_block != BLOCK_NIL)
++ s->PUtable[last_block] = block;
++ }
++ break;
++ }
++
++ /* Check for invalid block */
++ if (erase_mark != ERASE_MARK) {
++ printk(KERN_WARNING "INFTL: corrupt block %d "
++ "in chain %d, chain length %d, erase "
++ "mark 0x%x?\n", block, first_block,
++ chain_length, erase_mark);
++ /*
++ * Assume end of chain, probably incomplete
++ * fold/erase...
++ */
++ if (chain_length == 0)
++ do_format_chain++;
++ break;
++ }
++
++ /* Check for it being free already then... */
++ if ((logical_block == BLOCK_FREE) ||
++ (logical_block == BLOCK_NIL)) {
++ s->PUtable[block] = BLOCK_FREE;
++ break;
++ }
++
++ /* Sanity checks on block numbers */
++ if ((logical_block >= s->nb_blocks) ||
++ ((prev_block >= s->nb_blocks) &&
++ (prev_block != BLOCK_NIL))) {
++ if (chain_length > 0) {
++ printk(KERN_WARNING "INFTL: corrupt "
++ "block %d in chain %d?\n",
++ block, first_block);
++ do_format_chain++;
++ }
++ break;
++ }
++
++ if (first_logical_block == BLOCK_NIL) {
++ first_logical_block = logical_block;
++ } else {
++ if (first_logical_block != logical_block) {
++ /* Normal for folded chain... */
++ break;
++ }
++ }
++
++ /*
++ * Current block is valid, so if we followed a virtual
++ * chain to get here then we can set the previous
++ * block pointer in our PUtable now. Then move onto
++ * the previous block in the chain.
++ */
++ s->PUtable[block] = BLOCK_NIL;
++ if (last_block != BLOCK_NIL)
++ s->PUtable[last_block] = block;
++ last_block = block;
++ block = prev_block;
++
++ /* Check for end of chain */
++ if (block == BLOCK_NIL)
++ break;
++
++ /* Validate next block before following it... */
++ if (block > s->lastEUN) {
++ printk(KERN_WARNING "INFTL: invalid previous "
++ "block %d in chain %d?\n", block,
++ first_block);
++ do_format_chain++;
++ break;
++ }
++ }
++
++ if (do_format_chain) {
++ format_chain(s, first_block);
++ continue;
++ }
++
++ /*
++ * Looks like a valid chain then. It may not really be the
++ * newest block in the chain, but it is the newest we have
++ * found so far. We might update it in later iterations of
++ * this loop if we find something newer.
++ */
++ s->VUtable[first_logical_block] = first_block;
++ logical_block = BLOCK_NIL;
++ }
++
++#ifdef CONFIG_MTD_DEBUG_VERBOSE
++ if (CONFIG_MTD_DEBUG_VERBOSE >= 2)
++ INFTL_dumptables(s);
++#endif
++
++ /*
++ * Second pass, check for infinite loops in chains. These are
++ * possible because we don't update the previous pointers when
++ * we fold chains. No big deal, just fix them up in PUtable.
++ */
++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: pass 2, validate virtual chains\n");
++ for (logical_block = 0; logical_block < s->numvunits; logical_block++) {
++ block = s->VUtable[logical_block];
++ last_block = BLOCK_NIL;
++
++ /* Check for free/reserved/nil */
++ if (block >= BLOCK_RESERVED)
++ continue;
++
++ ANAC = ANACtable[block];
++ for (i = 0; i < s->numvunits; i++) {
++ if (s->PUtable[block] == BLOCK_NIL)
++ break;
++ if (s->PUtable[block] > s->lastEUN) {
++ printk(KERN_WARNING "INFTL: invalid prev %d, "
++ "in virtual chain %d\n",
++ s->PUtable[block], logical_block);
++ s->PUtable[block] = BLOCK_NIL;
++
++ }
++ if (ANACtable[block] != ANAC) {
++ /*
++ * Chain must point back to itself. This is ok,
++ * but we will need adjust the tables with this
++ * newest block and oldest block.
++ */
++ s->VUtable[logical_block] = block;
++ s->PUtable[last_block] = BLOCK_NIL;
++ break;
++ }
++
++ ANAC--;
++ last_block = block;
++ block = s->PUtable[block];
++ }
++
++ if (i >= s->nb_blocks) {
++ /*
++ * Uhoo, infinite chain with valid ANACS!
++ * Format whole chain...
++ */
++ format_chain(s, first_block);
++ }
++ }
++
++#ifdef CONFIG_MTD_DEBUG_VERBOSE
++ if (CONFIG_MTD_DEBUG_VERBOSE >= 2)
++ INFTL_dumptables(s);
++ if (CONFIG_MTD_DEBUG_VERBOSE >= 2)
++ INFTL_dumpVUchains(s);
++#endif
++
++ /*
++ * Third pass, format unreferenced blocks and init free block count.
++ */
++ s->numfreeEUNs = 0;
++ s->LastFreeEUN = BLOCK_NIL;
++
++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: pass 3, format unused blocks\n");
++ for (block = s->firstEUN; block <= s->lastEUN; block++) {
++ if (s->PUtable[block] == BLOCK_NOTEXPLORED) {
++ printk("INFTL: unreferenced block %d, formatting it\n",
++ block);
++ if (INFTL_formatblock(s, block) < 0)
++ s->PUtable[block] = BLOCK_RESERVED;
++ else
++ s->PUtable[block] = BLOCK_FREE;
++ }
++ if (s->PUtable[block] == BLOCK_FREE) {
++ s->numfreeEUNs++;
++ if (s->LastFreeEUN == BLOCK_NIL)
++ s->LastFreeEUN = block;
++ }
++ }
++
++ kfree(ANACtable);
++ return 0;
++}
+diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/Config.in linux/drivers/mtd/maps/Config.in
+--- linux-mips-2.4.27/drivers/mtd/maps/Config.in 2004-02-26 01:46:35.000000000 +0100
++++ linux/drivers/mtd/maps/Config.in 2004-11-19 10:25:11.888201464 +0100
+@@ -1,17 +1,14 @@
+ # drivers/mtd/maps/Config.in
+
+-# $Id$
++# $Id$
+
+ mainmenu_option next_comment
+
+ comment 'Mapping drivers for chip access'
+
+-dep_tristate ' CFI Flash device in physical memory map' CONFIG_MTD_PHYSMAP $CONFIG_MTD_GEN_PROBE
+-if [ "$CONFIG_MTD_PHYSMAP" = "y" -o "$CONFIG_MTD_PHYSMAP" = "m" ]; then
+- hex ' Physical start address of flash mapping' CONFIG_MTD_PHYSMAP_START 0x8000000
+- hex ' Physical length of flash mapping' CONFIG_MTD_PHYSMAP_LEN 0x4000000
+- int ' Bus width in octets' CONFIG_MTD_PHYSMAP_BUSWIDTH 2
+-fi
++bool ' Support for non-linear mappings of flash chips' CONFIG_MTD_COMPLEX_MAPPINGS
++
++bool ' CFI Flash device in physical memory map' CONFIG_MTD_PHYSMAP $CONFIG_MTD_GEN_PROBE
+
+ if [ "$CONFIG_SPARC" = "y" -o "$CONFIG_SPARC64" = "y" ]; then
+ dep_tristate ' Sun Microsystems userflash support' CONFIG_MTD_SUN_UFLASH $CONFIG_MTD_CFI
+@@ -21,56 +18,68 @@
+ dep_tristate ' CFI Flash device mapped on Photron PNC-2000' CONFIG_MTD_PNC2000 $CONFIG_MTD_CFI $CONFIG_MTD_PARTITIONS
+ dep_tristate ' CFI Flash device mapped on AMD SC520 CDP' CONFIG_MTD_SC520CDP $CONFIG_MTD_CFI
+ dep_tristate ' CFI Flash device mapped on AMD NetSc520' CONFIG_MTD_NETSC520 $CONFIG_MTD_CFI $CONFIG_MTD_PARTITIONS
+- dep_tristate ' CFI Flash device mapped on Arcom SBC-GXx boards' CONFIG_MTD_SBC_GXX $CONFIG_MTD_CFI_INTELEXT $CONFIG_MTD_PARTITIONS
+- dep_tristate ' CFI Flash device mapped on Arcom ELAN-104NC' CONFIG_MTD_ELAN_104NC $CONFIG_MTD_CFI_INTELEXT $CONFIG_MTD_PARTITIONS
++ dep_tristate ' CFI Flash device mapped on Arcom SBC-GXx boards' CONFIG_MTD_SBC_GXX $CONFIG_MTD_CFI_INTELEXT $CONFIG_MTD_PARTITIONS $CONFIG_MTD_COMPLEX_MAPPINGS
++ dep_tristate ' CFI Flash device mapped on Arcom ELAN-104NC' CONFIG_MTD_ELAN_104NC $CONFIG_MTD_CFI_INTELEXT $CONFIG_MTD_PARTITIONS $CONFIG_MTD_COMPLEX_MAPPINGS
+ dep_tristate ' CFI Flash device mapped on DIL/Net PC' CONFIG_MTD_DILNETPC $CONFIG_MTD_CFI_INTELEXT $CONFIG_MTD_PARTITIONS $CONFIG_MTD_CONCAT
+ if [ "$CONFIG_MTD_DILNETPC" = "y" -o "$CONFIG_MTD_DILNETPC" = "m" ]; then
+ hex ' Size of boot partition' CONFIG_MTD_DILNETPC_BOOTSIZE 0x80000
+ fi
+- dep_tristate ' JEDEC Flash device mapped on Mixcom piggyback card' CONFIG_MTD_MIXMEM $CONFIG_MTD_JEDEC
+- dep_tristate ' JEDEC Flash device mapped on Octagon 5066 SBC' CONFIG_MTD_OCTAGON $CONFIG_MTD_JEDEC
+- dep_tristate ' JEDEC Flash device mapped on Tempustech VMAX SBC301' CONFIG_MTD_VMAX $CONFIG_MTD_JEDEC
++ dep_tristate ' JEDEC Flash device mapped on Octagon 5066 SBC' CONFIG_MTD_OCTAGON $CONFIG_MTD_JEDEC $CONFIG_MTD_COMPLEX_MAPPINGS
++ dep_tristate ' JEDEC Flash device mapped on Tempustech VMAX SBC301' CONFIG_MTD_VMAX $CONFIG_MTD_JEDEC $CONFIG_MTD_COMPLEX_MAPPINGS
+ dep_tristate ' Flash device mapped with DOCCS on NatSemi SCx200' CONFIG_MTD_SCx200_DOCFLASH $CONFIG_MTD_CFI
+ dep_tristate ' BIOS flash chip on Intel L440GX boards' CONFIG_MTD_L440GX $CONFIG_MTD_JEDECPROBE
+ dep_tristate ' ROM connected to AMD76X southbridge' CONFIG_MTD_AMD76XROM $CONFIG_MTD_GEN_PROBE
+- dep_tristate ' ROM connected to Intel Hub Controller 2' CONFIG_MTD_ICH2ROM $CONFIG_MTD_JEDECPROBE
++ dep_tristate ' ROM connected to Intel Hub Controller 2/3/4/5' CONFIG_MTD_ICHXROM $CONFIG_MTD_JEDECPROBE $CONFIG_MTD_COMPLEX_MAPPINGS
+ dep_tristate ' CFI Flash device mapped on SnapGear/SecureEdge' CONFIG_MTD_NETtel $CONFIG_MTD_PARTITIONS
+ dep_tristate ' BIOS flash chip on Intel SCB2 boards' CONFIG_MTD_SCB2_FLASH $CONFIG_MTD_GEN_PROBE
+ fi
+
+-if [ "$CONFIG_PPC" = "y" ]; then
+- dep_tristate ' CFI Flash device mapped on TQM8XXL' CONFIG_MTD_TQM8XXL $CONFIG_MTD_CFI $CONFIG_TQM8xxL
++if [ "$CONFIG_PPC32" = "y" ]; then
++ if [ "$CONFIG_6xx" = "y" -a "$CONFIG_8260" = "y" ]; then
++ dep_tristate ' Flash device on SBC8240' CONFIG_MTD_SBC8240 $CONFIG_MTD_JEDECPROBE
++ fi
++ if [ "$CONFIG_8xx" = "y" ]; then
++ if [ "$CONFIG_TQM8xxL" = "y" ]; then
++ dep_tristate ' CFI Flash device mapped on TQM8XXL' CONFIG_MTD_TQM8XXL $CONFIG_MTD_CFI
++ fi
++ if [ "$CONFIG_RPXLITE" = "y" -o "$CONFIG_RPXCLASSIC" = "y" ]; then
+ dep_tristate ' CFI Flash device mapped on RPX Lite or CLLF' CONFIG_MTD_RPXLITE $CONFIG_MTD_CFI
++ fi
++ if [ "$CONFIG_MBX" = "y" ]; then
+ dep_tristate ' System flash on MBX860 board' CONFIG_MTD_MBX860 $CONFIG_MTD_CFI
++ fi
++ if [ "$CONFIG_DBOX2" = "y" ]; then
+ dep_tristate ' CFI Flash device mapped on D-Box2' CONFIG_MTD_DBOX2 $CONFIG_MTD_CFI
++ fi
+ dep_tristate ' CFI Flash device mapping on FlagaDM' CONFIG_MTD_CFI_FLAGADM $CONFIG_MTD_CFI
+- dep_tristate ' CFI Flash device mapped on IBM Redwood-4/5' CONFIG_MTD_REDWOOD $CONFIG_MTD_CFI
++ fi
++ if [ "$CONFIG_4xx" = "y" ]; then
++ if [ "$CONFIG_40x" = "y" ]; then
++ if [ "$CONFIG_REDWOOD_4" = "y" -o "$CONFIG_REDWOOD_5" = "y" -o "$CONFIG_REDWOOD_6" = "y" ]; then
++ dep_tristate ' CFI Flash device mapped on IBM Redwood' CONFIG_MTD_REDWOOD $CONFIG_MTD_CFI
++ fi
++ dep_tristate ' CFI Flash device mapped on IBM Beech' CONFIG_MTD_BEECH $CONFIG_MTD_CFI $CONFIG_BEECH
++ dep_tristate ' CFI Flash device mapped on IBM Arctic' CONFIG_MTD_ARCTIC $CONFIG_MTD_CFI $CONFIG_ARCTIC2
++ fi
++ if [ "$CONFIG_440" = "y" ]; then
++ dep_tristate ' Flash devices mapped on IBM Ebony' CONFIG_MTD_EBONY $CONFIG_MTD_CFI $CONFIG_EBONY
++ fi
++ fi
+ fi
+
+-if [ "$CONFIG_MIPS" = "y" ]; then
+- dep_tristate ' Pb1000 MTD support' CONFIG_MTD_PB1000 $CONFIG_MIPS_PB1000
+- dep_tristate ' Pb1500 MTD support' CONFIG_MTD_PB1500 $CONFIG_MIPS_PB1500
+- dep_tristate ' Pb1100 MTD support' CONFIG_MTD_PB1100 $CONFIG_MIPS_PB1100
+- dep_tristate ' Bosporus MTD support' CONFIG_MTD_BOSPORUS $CONFIG_MIPS_BOSPORUS
+- dep_tristate ' XXS1500 boot flash device' CONFIG_MTD_XXS1500 $CONFIG_MIPS_XXS1500
+- dep_tristate ' MTX-1 flash device' CONFIG_MTD_MTX1 $CONFIG_MIPS_MTX1
+- if [ "$CONFIG_MTD_PB1500" = "y" -o "$CONFIG_MTD_PB1500" = "m" \
+- -o "$CONFIG_MTD_PB1100" = "y" -o "$CONFIG_MTD_PB1100" = "m" ]; then
+- bool ' Pb[15]00 boot flash device' CONFIG_MTD_PB1500_BOOT
+- bool ' Pb[15]00 user flash device (2nd 32MiB bank)' CONFIG_MTD_PB1500_USER
++if [ "$CONFIG_MIPS" = "y" -o "$CONFIG_MIPS64" = "y" ]; then
++ if [ "$CONFIG_MIPS_PB1000" = "y" -o "$CONFIG_MIPS_PB1100" = "y" -o "$CONFIG_MIPS_PB1500" = "y" ]; then
++ tristate ' Pb1x00 MTD support' CONFIG_MTD_PB1XXX
++ if [ "$CONFIG_MIPS_PB1500" = "y" -o "$CONFIG_MIPS_PB1100" = "m" ]; then
++ bool ' Pb1x00 boot flash device' CONFIG_MTD_PB1500_BOOT
++ bool ' Pb1x00 user flash device (2nd 32MiB bank)' CONFIG_MTD_PB1500_USER
++ fi
+ fi
+ tristate ' Db1x00 MTD support' CONFIG_MTD_DB1X00
+ if [ "$CONFIG_MTD_DB1X00" = "y" -o "$CONFIG_MTD_DB1X00" = "m" ]; then
+ bool ' Db1x00 boot flash device' CONFIG_MTD_DB1X00_BOOT
+ bool ' Db1x00 user flash device (2nd bank)' CONFIG_MTD_DB1X00_USER
+ fi
+- tristate ' Pb1550 MTD support' CONFIG_MTD_PB1550
+- if [ "$CONFIG_MTD_PB1550" = "y" -o "$CONFIG_MTD_PB1550" = "m" ]; then
+- bool ' Pb1550 Boot Flash' CONFIG_MTD_PB1550_BOOT
+- bool ' Pb1550 User Parameter Flash' CONFIG_MTD_PB1550_USER
+- fi
+- dep_tristate ' Hydrogen 3 MTD support' CONFIG_MTD_HYDROGEN3 $CONFIG_MIPS_HYDROGEN3
+- dep_tristate ' Mirage MTD support' CONFIG_MTD_MIRAGE $CONFIG_MIPS_MIRAGE
+ dep_tristate ' Flash chip mapping on ITE QED-4N-S01B, Globespan IVR or custom board' CONFIG_MTD_CSTM_MIPS_IXX $CONFIG_MTD_CFI $CONFIG_MTD_JEDEC $CONFIG_MTD_PARTITIONS
+ if [ "$CONFIG_MTD_CSTM_MIPS_IXX" = "y" -o "$CONFIG_MTD_CSTM_MIPS_IXX" = "m" ]; then
+ hex ' Physical start address of flash mapping' CONFIG_MTD_CSTM_MIPS_IXX_START 0x8000000
+@@ -78,7 +87,7 @@
+ int ' Bus width in octets' CONFIG_MTD_CSTM_MIPS_IXX_BUSWIDTH 2
+ fi
+ dep_tristate ' Momenco Ocelot boot flash device' CONFIG_MTD_OCELOT $CONFIG_MOMENCO_OCELOT
+- dep_tristate ' LASAT flash device' CONFIG_MTD_LASAT $CONFIG_MTD_CFI $CONFIG_LASAT
++ dep_tristate ' LASAT flash device' CONFIG_MTD_LASAT $CONFIG_LASAT
+ fi
+
+ if [ "$CONFIG_SUPERH" = "y" ]; then
+@@ -90,21 +99,24 @@
+ fi
+
+ if [ "$CONFIG_ARM" = "y" ]; then
+- dep_tristate ' CFI Flash device mapped on Nora' CONFIG_MTD_NORA $CONFIG_MTD_CFI
+ dep_tristate ' CFI Flash device mapped on ARM Integrator/P720T' CONFIG_MTD_ARM_INTEGRATOR $CONFIG_MTD_CFI
+ dep_tristate ' Cirrus CDB89712 evaluation board mappings' CONFIG_MTD_CDB89712 $CONFIG_MTD_CFI $CONFIG_ARCH_CDB89712
+ dep_tristate ' CFI Flash device mapped on StrongARM SA11x0' CONFIG_MTD_SA1100 $CONFIG_MTD_CFI $CONFIG_ARCH_SA1100 $CONFIG_MTD_PARTITIONS
+- dep_tristate ' CFI Flash device mapped on DC21285 Footbridge' CONFIG_MTD_DC21285 $CONFIG_MTD_CFI $CONFIG_ARCH_FOOTBRIDGE
++ dep_tristate ' CFI Flash device mapped on DC21285 Footbridge' CONFIG_MTD_DC21285 $CONFIG_MTD_CFI $CONFIG_ARCH_FOOTBRIDGE $CONFIG_MTD_COMPLEX_MAPPINGS
+ dep_tristate ' CFI Flash device mapped on the XScale IQ80310 board' CONFIG_MTD_IQ80310 $CONFIG_MTD_CFI $CONFIG_ARCH_IQ80310
++ dep_tristate ' CFI Flash device mapped on the XScale Lubbock board' CONFIG_MTD_LUBBOCK $CONFIG_MTD_CFI $CONFIG_ARCH_LUBBOCK
++ dep_tristate ' CFI Flash device mapped on XScale IXP425 systems' CONFIG_MTD_IXP425 $CONFIG_MTD_CFI $CONFIG_MTD_COMPLEX_MAPPINGS
+ dep_tristate ' CFI Flash device mapped on Epxa10db' CONFIG_MTD_EPXA10DB $CONFIG_MTD_CFI $CONFIG_MTD_PARTITIONS $CONFIG_ARCH_CAMELOT
+ dep_tristate ' CFI Flash device mapped on the FortuNet board' CONFIG_MTD_FORTUNET $CONFIG_MTD_CFI $CONFIG_MTD_PARTITIONS $CONFIG_SA1100_FORTUNET
+ dep_tristate ' NV-RAM mapping AUTCPU12 board' CONFIG_MTD_AUTCPU12 $CONFIG_ARCH_AUTCPU12
+ dep_tristate ' CFI Flash device mapped on EDB7312' CONFIG_MTD_EDB7312 $CONFIG_MTD_CFI
++ dep_tristate ' CFI Flash device mapped on Hynix evaluation boards' CONFIG_MTD_H720X $CONFIG_MTD_CFI
+ dep_tristate ' JEDEC Flash device mapped on impA7' CONFIG_MTD_IMPA7 $CONFIG_MTD_JEDECPROBE
+ dep_tristate ' JEDEC Flash device mapped on Ceiva/Polaroid PhotoMax Digital Picture Frame' CONFIG_MTD_CEIVA $CONFIG_MTD_JEDECPROBE $CONFIG_ARCH_CEIVA
++ dep_tristate ' NOR Flash device on TOTO board' CONFIG_MTD_NOR_TOTO $CONFIG_MTD $CONFIG_OMAP_TOTO
+ fi
+ if [ "$CONFIG_ALPHA" = "y" ]; then
+- dep_tristate ' Flash chip mapping on TSUNAMI' CONFIG_MTD_TSUNAMI $CONFIG_MTD_GENPROBE
++ dep_tristate ' Flash chip mapping on TSUNAMI' CONFIG_MTD_TSUNAMI $CONFIG_MTD_GENPROBE $CONFIG_MTD_COMPLEX_MAPPINGS
+ fi
+
+ if [ "$CONFIG_UCLINUX" = "y" ]; then
+@@ -112,7 +124,7 @@
+ fi
+
+ # This needs CFI or JEDEC, depending on the cards found.
+-dep_tristate ' PCI MTD driver' CONFIG_MTD_PCI $CONFIG_MTD $CONFIG_PCI
+-dep_tristate ' PCMCIA MTD driver' CONFIG_MTD_PCMCIA $CONFIG_MTD $CONFIG_PCMCIA
++dep_tristate ' PCI MTD driver' CONFIG_MTD_PCI $CONFIG_MTD $CONFIG_PCI $CONFIG_MTD_COMPLEX_MAPPINGS
++dep_tristate ' PCMCIA MTD driver' CONFIG_MTD_PCMCIA $CONFIG_MTD $CONFIG_PCMCIA $CONFIG_MTD_COMPLEX_MAPPINGS
+
+ endmenu
+diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/Makefile linux/drivers/mtd/maps/Makefile
+--- linux-mips-2.4.27/drivers/mtd/maps/Makefile 2004-02-26 01:46:35.000000000 +0100
++++ linux/drivers/mtd/maps/Makefile 2004-11-19 10:25:11.890201160 +0100
+@@ -1,12 +1,16 @@
+ #
+ # linux/drivers/maps/Makefile
+ #
+-# $Id$
++# $Id$
+
+-BELOW25 := $(shell echo $(PATCHLEVEL) | sed s/[1234]/y/)
+-
+-ifeq ($(BELOW25),y)
++ifeq ($(PATCHLEVEL),4)
+ O_TARGET := mapslink.o
++export-objs := map_funcs.o
++endif
++
++
++ifeq ($(CONFIG_MTD_COMPLEX_MAPPINGS),y)
++obj-$(CONFIG_MTD) += map_funcs.o
+ endif
+
+ # Chip mappings
+@@ -21,19 +25,13 @@
+ obj-$(CONFIG_MTD_IQ80310) += iq80310.o
+ obj-$(CONFIG_MTD_L440GX) += l440gx.o
+ obj-$(CONFIG_MTD_AMD76XROM) += amd76xrom.o
+-obj-$(CONFIG_MTD_ICH2ROM) += ich2rom.o
++obj-$(CONFIG_MTD_ICHXROM) += ichxrom.o
+ obj-$(CONFIG_MTD_TSUNAMI) += tsunami_flash.o
++obj-$(CONFIG_MTD_LUBBOCK) += lubbock-flash.o
+ obj-$(CONFIG_MTD_MBX860) += mbx860.o
+-obj-$(CONFIG_MTD_NORA) += nora.o
+ obj-$(CONFIG_MTD_CEIVA) += ceiva.o
+ obj-$(CONFIG_MTD_OCTAGON) += octagon-5066.o
+-ifneq ($(CONFIG_MTD_PHYSMAP),n)
+- ifeq ($(CONFIG_MTD_PHYSMAP_BUSWIDTH),8)
+- obj-$(CONFIG_MTD_PHYSMAP) += physmap64.o
+- else
+- obj-$(CONFIG_MTD_PHYSMAP) += physmap.o
+- endif
+-endif
++obj-$(CONFIG_MTD_PHYSMAP) += physmap.o
+ obj-$(CONFIG_MTD_PNC2000) += pnc2000.o
+ obj-$(CONFIG_MTD_PCMCIA) += pcmciamtd.o
+ obj-$(CONFIG_MTD_RPXLITE) += rpxlite.o
+@@ -49,16 +47,9 @@
+ obj-$(CONFIG_MTD_OCELOT) += ocelot.o
+ obj-$(CONFIG_MTD_SOLUTIONENGINE)+= solutionengine.o
+ obj-$(CONFIG_MTD_PCI) += pci.o
+-obj-$(CONFIG_MTD_PB1000) += pb1xxx-flash.o
+-obj-$(CONFIG_MTD_PB1100) += pb1xxx-flash.o
+-obj-$(CONFIG_MTD_PB1500) += pb1xxx-flash.o
+-obj-$(CONFIG_MTD_XXS1500) += xxs1500.o
+-obj-$(CONFIG_MTD_MTX1) += mtx-1.o
+-obj-$(CONFIG_MTD_LASAT) += lasat.o
++obj-$(CONFIG_MTD_PB1XXX) += pb1xxx-flash.o
+ obj-$(CONFIG_MTD_DB1X00) += db1x00-flash.o
+-obj-$(CONFIG_MTD_PB1550) += pb1550-flash.o
+-obj-$(CONFIG_MTD_HYDROGEN3) += hydrogen3-flash.o
+-obj-$(CONFIG_MTD_BOSPORUS) += pb1xxx-flash.o
++obj-$(CONFIG_MTD_LASAT) += lasat.o
+ obj-$(CONFIG_MTD_AUTCPU12) += autcpu12-nvram.o
+ obj-$(CONFIG_MTD_EDB7312) += edb7312.o
+ obj-$(CONFIG_MTD_IMPA7) += impa7.o
+@@ -67,6 +58,13 @@
+ obj-$(CONFIG_MTD_UCLINUX) += uclinux.o
+ obj-$(CONFIG_MTD_NETtel) += nettel.o
+ obj-$(CONFIG_MTD_SCB2_FLASH) += scb2_flash.o
+-obj-$(CONFIG_MTD_MIRAGE) += mirage-flash.o
++obj-$(CONFIG_MTD_EBONY) += ebony.o
++obj-$(CONFIG_MTD_BEECH) += beech-mtd.o
++obj-$(CONFIG_MTD_ARCTIC) += arctic-mtd.o
++obj-$(CONFIG_MTD_H720X) += h720x-flash.o
++obj-$(CONFIG_MTD_SBC8240) += sbc8240.o
++obj-$(CONFIG_MTD_NOR_TOTO) += omap-toto-flash.o
++obj-$(CONFIG_MTD_MPC1211) += mpc1211.o
++obj-$(CONFIG_MTD_IXP425) += ixp425.o
+
+-include $(TOPDIR)/Rules.make
++-include $(TOPDIR)/Rules.make
+diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/amd76xrom.c linux/drivers/mtd/maps/amd76xrom.c
+--- linux-mips-2.4.27/drivers/mtd/maps/amd76xrom.c 2003-02-26 01:53:49.000000000 +0100
++++ linux/drivers/mtd/maps/amd76xrom.c 2004-11-19 10:25:11.891201008 +0100
+@@ -2,12 +2,13 @@
+ * amd76xrom.c
+ *
+ * Normal mappings of chips in physical memory
+- * $Id$
++ * $Id$
+ */
+
+ #include <linux/module.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
+ #include <asm/io.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+@@ -16,77 +17,59 @@
+ #include <linux/pci_ids.h>
+
+
++#define xstr(s) str(s)
++#define str(s) #s
++#define MOD_NAME xstr(KBUILD_BASENAME)
++
++#define MTD_DEV_NAME_LENGTH 16
++
+ struct amd76xrom_map_info {
+ struct map_info map;
+ struct mtd_info *mtd;
+ unsigned long window_addr;
+ u32 window_start, window_size;
+ struct pci_dev *pdev;
++ struct resource window_rsrc;
++ struct resource rom_rsrc;
++ char mtd_name[MTD_DEV_NAME_LENGTH];
+ };
+
+-static __u8 amd76xrom_read8(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readb(map->map_priv_1 + ofs);
+-}
+-
+-static __u16 amd76xrom_read16(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readw(map->map_priv_1 + ofs);
+-}
+
+-static __u32 amd76xrom_read32(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readl(map->map_priv_1 + ofs);
+-}
++static struct amd76xrom_map_info amd76xrom_map = {
++ .map = {
++ .name = MOD_NAME,
++ .size = 0,
++ .buswidth = 1,
++ }
++ /* remaining fields of structure are initialized to 0 */
++};
+
+-static void amd76xrom_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+-{
+- memcpy_fromio(to, map->map_priv_1 + from, len);
+-}
+
+-static void amd76xrom_write8(struct map_info *map, __u8 d, unsigned long adr)
++static void amd76xrom_cleanup(struct amd76xrom_map_info *info)
+ {
+- __raw_writeb(d, map->map_priv_1 + adr);
+- mb();
+-}
++ u8 byte;
+
+-static void amd76xrom_write16(struct map_info *map, __u16 d, unsigned long adr)
+-{
+- __raw_writew(d, map->map_priv_1 + adr);
+- mb();
+-}
++ /* Disable writes through the rom window */
++ pci_read_config_byte(info->pdev, 0x40, &byte);
++ pci_write_config_byte(info->pdev, 0x40, byte & ~1);
+
+-static void amd76xrom_write32(struct map_info *map, __u32 d, unsigned long adr)
+-{
+- __raw_writel(d, map->map_priv_1 + adr);
+- mb();
+-}
++ if (info->mtd) {
++ del_mtd_device(info->mtd);
++ map_destroy(info->mtd);
++ info->mtd = NULL;
++ info->map.virt = 0;
++ }
++ if (info->rom_rsrc.parent)
++ release_resource(&info->rom_rsrc);
++ if (info->window_rsrc.parent)
++ release_resource(&info->window_rsrc);
+
+-static void amd76xrom_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+-{
+- memcpy_toio(map->map_priv_1 + to, from, len);
++ if (info->window_addr) {
++ iounmap((void *)(info->window_addr));
++ info->window_addr = 0;
++ }
+ }
+
+-static struct amd76xrom_map_info amd76xrom_map = {
+- map: {
+- name: "AMD76X rom",
+- size: 0,
+- buswidth: 1,
+- read8: amd76xrom_read8,
+- read16: amd76xrom_read16,
+- read32: amd76xrom_read32,
+- copy_from: amd76xrom_copy_from,
+- write8: amd76xrom_write8,
+- write16: amd76xrom_write16,
+- write32: amd76xrom_write32,
+- copy_to: amd76xrom_copy_to,
+- /* The standard rom socket is for single power supply chips
+- * that don't have an extra vpp.
+- */
+- },
+- mtd: 0,
+- window_addr: 0,
+-};
+
+ static int __devinit amd76xrom_init_one (struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+@@ -97,6 +80,10 @@
+ u8 segen_bits;
+ };
+ static struct rom_window rom_window[] = {
++ /*
++ * Need the 5MiB window for chips that have block lock/unlock
++ * registers located below 4MiB window.
++ */
+ { 0xffb00000, 5*1024*1024, (1<<7) | (1<<6), },
+ { 0xffc00000, 4*1024*1024, (1<<7), },
+ { 0xffff0000, 64*1024, 0 },
+@@ -112,19 +99,29 @@
+ int i;
+ u32 rom_size;
+
++ info->pdev = pdev;
+ window = &rom_window[0];
+-#if 0
+- while(window->size) {
+- if (request_mem_region(window->start, window->size, "amd76xrom")) {
+- break;
+- }
+- window++;
+- }
+- if (!window->size) {
+- printk(KERN_ERR "amd76xrom: cannot reserve rom window\n");
+- goto err_out_none;
++
++ while (window->size) {
++ /*
++ * Try to reserve the window mem region. If this fails then
++ * it is likely due to a fragment of the window being
++ * "reseved" by the BIOS. In the case that the
++ * request_mem_region() fails then once the rom size is
++ * discovered we will try to reserve the unreserved fragment.
++ */
++ info->window_rsrc.name = MOD_NAME;
++ info->window_rsrc.start = window->start;
++ info->window_rsrc.end = window->start + window->size - 1;
++ info->window_rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
++ if (request_resource(&iomem_resource, &info->window_rsrc)) {
++ info->window_rsrc.parent = NULL;
++ printk(KERN_ERR MOD_NAME
++ " %s(): Unable to register resource"
++ " 0x%.08lx-0x%.08lx - kernel bug?\n",
++ __func__,
++ info->window_rsrc.start, info->window_rsrc.end);
+ }
+-#endif
+
+ /* Enable the selected rom window */
+ pci_read_config_byte(pdev, 0x43, &byte);
+@@ -136,49 +133,94 @@
+
+ /* FIXME handle registers 0x80 - 0x8C the bios region locks */
+
+- printk(KERN_NOTICE "amd76xrom window : %x at %x\n",
++ printk(KERN_NOTICE MOD_NAME " window : %x at %x\n",
+ window->size, window->start);
+ /* For write accesses caches are useless */
+- info->window_addr = (unsigned long)ioremap_nocache(window->start, window->size);
++ info->window_addr =
++ (unsigned long)ioremap_nocache(window->start,
++ window->size);
+
+ if (!info->window_addr) {
+ printk(KERN_ERR "Failed to ioremap\n");
+- goto err_out_free_mmio_region;
++ continue;
+ }
+- info->mtd = 0;
++
++ info->mtd = NULL;
++
+ for(i = 0; (rom_size = rom_probe_sizes[i]); i++) {
+ char **chip_type;
+ if (rom_size > window->size) {
+ continue;
+ }
+- info->map.map_priv_1 =
++ info->map.phys = window->start + window->size - rom_size;
++ info->map.virt =
+ info->window_addr + window->size - rom_size;
+ info->map.size = rom_size;
++ simple_map_init(&info->map);
+ chip_type = rom_probe_types;
+ for(; !info->mtd && *chip_type; chip_type++) {
+ info->mtd = do_map_probe(*chip_type, &amd76xrom_map.map);
+ }
+- if (info->mtd) {
+- break;
+- }
++ if (info->mtd) goto found_mtd;
+ }
+- if (!info->mtd) {
+- goto err_out_iounmap;
++ iounmap((void *)(info->window_addr));
++ info->window_addr = 0;
++
++ /* Disable writes through the rom window */
++ pci_read_config_byte(pdev, 0x40, &byte);
++ pci_write_config_byte(pdev, 0x40, byte & ~1);
++
++ window++;
+ }
+- printk(KERN_NOTICE "amd76xrom chip at offset: %x\n",
++ goto failed;
++
++ found_mtd:
++ printk(KERN_NOTICE MOD_NAME " chip at offset: 0x%x\n",
+ window->size - rom_size);
+
+- info->mtd->module = THIS_MODULE;
++ info->mtd->owner = THIS_MODULE;
++
++ if (!info->window_rsrc.parent) {
++ /* failed to reserve entire window - try fragments */
++ info->window_rsrc.name = MOD_NAME;
++ info->window_rsrc.start = window->start;
++ info->window_rsrc.end = window->start + window->size - rom_size - 1;
++ info->window_rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
++ if (request_resource(&iomem_resource, &info->window_rsrc)) {
++ printk(KERN_ERR MOD_NAME
++ ": cannot reserve window resource fragment\n");
++ goto failed;
++ }
++ }
++
+ add_mtd_device(info->mtd);
+ info->window_start = window->start;
+ info->window_size = window->size;
++
++ if (info->window_rsrc.parent) {
++ /*
++ * Registering the MTD device in iomem may not be possible
++ * if there is a BIOS "reserved" and BUSY range. If this
++ * fails then continue anyway.
++ */
++ snprintf(info->mtd_name, MTD_DEV_NAME_LENGTH,
++ "mtd%d", info->mtd->index);
++
++ info->rom_rsrc.name = info->mtd_name;
++ info->rom_rsrc.start = window->start + window->size - rom_size;
++ info->rom_rsrc.end = window->start + window->size - 1;
++ info->rom_rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
++ if (request_resource(&info->window_rsrc, &info->rom_rsrc)) {
++ printk(KERN_ERR MOD_NAME
++ ": cannot reserve MTD resource\n");
++ info->rom_rsrc.parent = NULL;
++ }
++ }
++
+ return 0;
+
+-err_out_iounmap:
+- iounmap((void *)(info->window_addr));
+-err_out_free_mmio_region:
+- release_mem_region(window->start, window->size);
+-err_out_none:
++ failed:
++ amd76xrom_cleanup(info);
+ return -ENODEV;
+ }
+
+@@ -186,21 +228,8 @@
+ static void __devexit amd76xrom_remove_one (struct pci_dev *pdev)
+ {
+ struct amd76xrom_map_info *info = &amd76xrom_map;
+- u8 byte;
+-
+- del_mtd_device(info->mtd);
+- map_destroy(info->mtd);
+- info->mtd = 0;
+- info->map.map_priv_1 = 0;
+-
+- iounmap((void *)(info->window_addr));
+- info->window_addr = 0;
+-
+- /* Disable writes through the rom window */
+- pci_read_config_byte(pdev, 0x40, &byte);
+- pci_write_config_byte(pdev, 0x40, byte & ~1);
+
+- release_mem_region(info->window_start, info->window_size);
++ amd76xrom_cleanup(info);
+ }
+
+ static struct pci_device_id amd76xrom_pci_tbl[] __devinitdata = {
+@@ -208,6 +237,7 @@
+ PCI_ANY_ID, PCI_ANY_ID, },
+ { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7440,
+ PCI_ANY_ID, PCI_ANY_ID, },
++ { PCI_VENDOR_ID_AMD, 0x7468 }, /* amd8111 support */
+ { 0, }
+ };
+
+@@ -215,10 +245,10 @@
+
+ #if 0
+ static struct pci_driver amd76xrom_driver = {
+- name: "amd76xrom",
+- id_table: amd76xrom_pci_tbl,
+- probe: amd76xrom_init_one,
+- remove: amd76xrom_remove_one,
++ .name = MOD_NAME,
++ .id_table = amd76xrom_pci_tbl,
++ .probe = amd76xrom_init_one,
++ .remove = amd76xrom_remove_one,
+ };
+ #endif
+
+diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/arctic-mtd.c linux/drivers/mtd/maps/arctic-mtd.c
+--- linux-mips-2.4.27/drivers/mtd/maps/arctic-mtd.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/mtd/maps/arctic-mtd.c 2004-11-19 10:25:11.893200704 +0100
+@@ -0,0 +1,135 @@
++/*
++ * $Id$
++ *
++ * drivers/mtd/maps/arctic-mtd.c MTD mappings and partition tables for
++ * IBM 405LP Arctic boards.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ *
++ * Copyright (C) 2002, International Business Machines Corporation
++ * All Rights Reserved.
++ *
++ * Bishop Brock
++ * IBM Research, Austin Center for Low-Power Computing
++ * bcbrock@us.ibm.com
++ * March 2002
++ *
++ * modified for Arctic by,
++ * David Gibson
++ * IBM OzLabs, Canberra, Australia
++ * <arctic@gibson.dropbear.id.au>
++ */
++
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/init.h>
++
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/map.h>
++#include <linux/mtd/partitions.h>
++
++#include <asm/io.h>
++#include <asm/ibm4xx.h>
++
++/*
++ * 0 : 0xFE00 0000 - 0xFEFF FFFF : Filesystem 1 (16MiB)
++ * 1 : 0xFF00 0000 - 0xFF4F FFFF : kernel (5.12MiB)
++ * 2 : 0xFF50 0000 - 0xFFF5 FFFF : Filesystem 2 (10.624MiB) (if non-XIP)
++ * 3 : 0xFFF6 0000 - 0xFFFF FFFF : PIBS Firmware (640KiB)
++ */
++
++#define FFS1_SIZE 0x01000000 /* 16MiB */
++#define KERNEL_SIZE 0x00500000 /* 5.12MiB */
++#define FFS2_SIZE 0x00a60000 /* 10.624MiB */
++#define FIRMWARE_SIZE 0x000a0000 /* 640KiB */
++
++
++#define NAME "Arctic Linux Flash"
++#define PADDR SUBZERO_BOOTFLASH_PADDR
++#define BUSWIDTH 2
++#define SIZE SUBZERO_BOOTFLASH_SIZE
++#define PARTITIONS 4
++
++/* Flash memories on these boards are memory resources, accessed big-endian. */
++
++{
++ /* do nothing for now */
++}
++
++static struct map_info arctic_mtd_map = {
++ .name = NAME,
++ .size = SIZE,
++ .buswidth = BUSWIDTH,
++ .phys = PADDR,
++};
++
++static struct mtd_info *arctic_mtd;
++
++static struct mtd_partition arctic_partitions[PARTITIONS] = {
++ { .name = "Filesystem",
++ .size = FFS1_SIZE,
++ .offset = 0,},
++ { .name = "Kernel",
++ .size = KERNEL_SIZE,
++ .offset = FFS1_SIZE,},
++ { .name = "Filesystem",
++ .size = FFS2_SIZE,
++ .offset = FFS1_SIZE + KERNEL_SIZE,},
++ { .name = "Firmware",
++ .size = FIRMWARE_SIZE,
++ .offset = SUBZERO_BOOTFLASH_SIZE - FIRMWARE_SIZE,},
++};
++
++static int __init
++init_arctic_mtd(void)
++{
++ printk("%s: 0x%08x at 0x%08x\n", NAME, SIZE, PADDR);
++
++ arctic_mtd_map.virt = (unsigned long) ioremap(PADDR, SIZE);
++
++ if (!arctic_mtd_map.virt) {
++ printk("%s: failed to ioremap 0x%x\n", NAME, PADDR);
++ return -EIO;
++ }
++ simple_map_init(&arctic_mtd_map);
++
++ printk("%s: probing %d-bit flash bus\n", NAME, BUSWIDTH * 8);
++ arctic_mtd = do_map_probe("cfi_probe", &arctic_mtd_map);
++
++ if (!arctic_mtd)
++ return -ENXIO;
++
++ arctic_mtd->owner = THIS_MODULE;
++
++ return add_mtd_partitions(arctic_mtd, arctic_partitions, PARTITIONS);
++}
++
++static void __exit
++cleanup_arctic_mtd(void)
++{
++ if (arctic_mtd) {
++ del_mtd_partitions(arctic_mtd);
++ map_destroy(arctic_mtd);
++ iounmap((void *) arctic_mtd_map.virt);
++ }
++}
++
++module_init(init_arctic_mtd);
++module_exit(cleanup_arctic_mtd);
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("David Gibson <arctic@gibson.dropbear.id.au>");
++MODULE_DESCRIPTION("MTD map and partitions for IBM 405LP Arctic boards");
+diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/autcpu12-nvram.c linux/drivers/mtd/maps/autcpu12-nvram.c
+--- linux-mips-2.4.27/drivers/mtd/maps/autcpu12-nvram.c 2002-06-27 00:35:50.000000000 +0200
++++ linux/drivers/mtd/maps/autcpu12-nvram.c 2004-11-19 10:25:11.894200552 +0100
+@@ -2,7 +2,7 @@
+ * NV-RAM memory access on autcpu12
+ * (C) 2002 Thomas Gleixner (gleixner@autronix.de)
+ *
+- * $Id$
++ * $Id$
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -24,6 +24,7 @@
+ #include <linux/types.h>
+ #include <linux/kernel.h>
+ #include <linux/ioport.h>
++#include <linux/init.h>
+ #include <asm/io.h>
+ #include <asm/sizes.h>
+ #include <asm/hardware.h>
+@@ -32,80 +33,27 @@
+ #include <linux/mtd/map.h>
+ #include <linux/mtd/partitions.h>
+
+-__u8 autcpu12_read8(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readb(map->map_priv_1 + ofs);
+-}
+-
+-__u16 autcpu12_read16(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readw(map->map_priv_1 + ofs);
+-}
+-
+-__u32 autcpu12_read32(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readl(map->map_priv_1 + ofs);
+-}
+-
+-void autcpu12_write8(struct map_info *map, __u8 d, unsigned long adr)
+-{
+- __raw_writeb(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-void autcpu12_write16(struct map_info *map, __u16 d, unsigned long adr)
+-{
+- __raw_writew(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-void autcpu12_write32(struct map_info *map, __u32 d, unsigned long adr)
+-{
+- __raw_writel(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-void autcpu12_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+-{
+- memcpy_fromio(to, map->map_priv_1 + from, len);
+-}
+-
+-void autcpu12_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+-{
+- while(len) {
+- __raw_writeb(*(unsigned char *) from, map->map_priv_1 + to);
+- from++;
+- to++;
+- len--;
+- }
+-}
+
+ static struct mtd_info *sram_mtd;
+
+ struct map_info autcpu12_sram_map = {
+- name: "SRAM",
+- size: 32768,
+- buswidth: 8,
+- read8: autcpu12_read8,
+- read16: autcpu12_read16,
+- read32: autcpu12_read32,
+- copy_from: autcpu12_copy_from,
+- write8: autcpu12_write8,
+- write16: autcpu12_write16,
+- write32: autcpu12_write32,
+- copy_to: autcpu12_copy_to
++ .name = "SRAM",
++ .size = 32768,
++ .buswidth = 4,
++ .phys = 0x12000000,
+ };
+
+ static int __init init_autcpu12_sram (void)
+ {
+ int err, save0, save1;
+
+- autcpu12_sram_map.map_priv_1 = (unsigned long)ioremap(0x12000000, SZ_128K);
+- if (!autcpu12_sram_map.map_priv_1) {
++ autcpu12_sram_map.virt = (unsigned long)ioremap(0x12000000, SZ_128K);
++ if (!autcpu12_sram_map.virt) {
+ printk("Failed to ioremap autcpu12 NV-RAM space\n");
+ err = -EIO;
+ goto out;
+ }
++ simple_map_init(&autcpu_sram_map);
+
+ /*
+ * Check for 32K/128K
+@@ -115,20 +63,20 @@
+ * Read and check result on ofs 0x0
+ * Restore contents
+ */
+- save0 = autcpu12_read32(&autcpu12_sram_map,0);
+- save1 = autcpu12_read32(&autcpu12_sram_map,0x10000);
+- autcpu12_write32(&autcpu12_sram_map,~save0,0x10000);
++ save0 = map_read32(&autcpu12_sram_map,0);
++ save1 = map_read32(&autcpu12_sram_map,0x10000);
++ map_write32(&autcpu12_sram_map,~save0,0x10000);
+ /* if we find this pattern on 0x0, we have 32K size
+ * restore contents and exit
+ */
+- if ( autcpu12_read32(&autcpu12_sram_map,0) != save0) {
+- autcpu12_write32(&autcpu12_sram_map,save0,0x0);
++ if ( map_read32(&autcpu12_sram_map,0) != save0) {
++ map_write32(&autcpu12_sram_map,save0,0x0);
+ goto map;
+ }
+ /* We have a 128K found, restore 0x10000 and set size
+ * to 128K
+ */
+- autcpu12_write32(&autcpu12_sram_map,save1,0x10000);
++ ma[_write32(&autcpu12_sram_map,save1,0x10000);
+ autcpu12_sram_map.size = SZ_128K;
+
+ map:
+@@ -139,7 +87,7 @@
+ goto out_ioremap;
+ }
+
+- sram_mtd->module = THIS_MODULE;
++ sram_mtd->owner = THIS_MODULE;
+ sram_mtd->erasesize = 16;
+
+ if (add_mtd_device(sram_mtd)) {
+@@ -148,7 +96,7 @@
+ goto out_probe;
+ }
+
+- printk("NV-RAM device size %ldK registered on AUTCPU12\n",autcpu12_sram_map.size/SZ_1K);
++ printk("NV-RAM device size %ldKiB registered on AUTCPU12\n",autcpu12_sram_map.size/SZ_1K);
+
+ return 0;
+
+@@ -157,7 +105,7 @@
+ sram_mtd = 0;
+
+ out_ioremap:
+- iounmap((void *)autcpu12_sram_map.map_priv_1);
++ iounmap((void *)autcpu12_sram_map.virt);
+ out:
+ return err;
+ }
+@@ -167,7 +115,7 @@
+ if (sram_mtd) {
+ del_mtd_device(sram_mtd);
+ map_destroy(sram_mtd);
+- iounmap((void *)autcpu12_sram_map.map_priv_1);
++ iounmap((void *)autcpu12_sram_map.virt);
+ }
+ }
+
+diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/beech-mtd.c linux/drivers/mtd/maps/beech-mtd.c
+--- linux-mips-2.4.27/drivers/mtd/maps/beech-mtd.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/mtd/maps/beech-mtd.c 2004-11-19 10:25:11.895200400 +0100
+@@ -0,0 +1,112 @@
++/*
++ * $Id$
++ *
++ * drivers/mtd/maps/beech-mtd.c MTD mappings and partition tables for
++ * IBM 405LP Beech boards.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ *
++ * Copyright (C) 2002, International Business Machines Corporation
++ * All Rights Reserved.
++ *
++ * Bishop Brock
++ * IBM Research, Austin Center for Low-Power Computing
++ * bcbrock@us.ibm.com
++ * March 2002
++ *
++ */
++
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/init.h>
++
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/map.h>
++#include <linux/mtd/partitions.h>
++
++#include <asm/io.h>
++#include <asm/ibm4xx.h>
++
++#define NAME "Beech Linux Flash"
++#define PADDR BEECH_BIGFLASH_PADDR
++#define SIZE BEECH_BIGFLASH_SIZE
++#define BUSWIDTH 1
++
++/* Flash memories on these boards are memory resources, accessed big-endian. */
++
++
++static struct map_info beech_mtd_map = {
++ .name = NAME,
++ .size = SIZE,
++ .buswidth = BUSWIDTH,
++ .phys = PADDR
++};
++
++static struct mtd_info *beech_mtd;
++
++static struct mtd_partition beech_partitions[2] = {
++ {
++ .name = "Linux Kernel",
++ .size = BEECH_KERNEL_SIZE,
++ .offset = BEECH_KERNEL_OFFSET
++ }, {
++ .name = "Free Area",
++ .size = BEECH_FREE_AREA_SIZE,
++ .offset = BEECH_FREE_AREA_OFFSET
++ }
++};
++
++static int __init
++init_beech_mtd(void)
++{
++ printk("%s: 0x%08x at 0x%08x\n", NAME, SIZE, PADDR);
++
++ beech_mtd_map.virt = (unsigned long) ioremap(PADDR, SIZE);
++
++ if (!beech_mtd_map.virt) {
++ printk("%s: failed to ioremap 0x%x\n", NAME, PADDR);
++ return -EIO;
++ }
++
++ simple_map_init(&beech_mtd_map);
++
++ printk("%s: probing %d-bit flash bus\n", NAME, BUSWIDTH * 8);
++ beech_mtd = do_map_probe("cfi_probe", &beech_mtd_map);
++
++ if (!beech_mtd)
++ return -ENXIO;
++
++ beech_mtd->owner = THIS_MODULE;
++
++ return add_mtd_partitions(beech_mtd, beech_partitions, 2);
++}
++
++static void __exit
++cleanup_beech_mtd(void)
++{
++ if (beech_mtd) {
++ del_mtd_partitions(beech_mtd);
++ map_destroy(beech_mtd);
++ iounmap((void *) beech_mtd_map.virt);
++ }
++}
++
++module_init(init_beech_mtd);
++module_exit(cleanup_beech_mtd);
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Bishop Brock <bcbrock@us.ibm.com>");
++MODULE_DESCRIPTION("MTD map and partitions for IBM 405LP Beech boards");
+diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/cdb89712.c linux/drivers/mtd/maps/cdb89712.c
+--- linux-mips-2.4.27/drivers/mtd/maps/cdb89712.c 2001-11-05 21:15:52.000000000 +0100
++++ linux/drivers/mtd/maps/cdb89712.c 2004-11-19 10:25:11.897200096 +0100
+@@ -1,13 +1,14 @@
+ /*
+ * Flash on Cirrus CDB89712
+ *
+- * $Id$
++ * $Id$
+ */
+
+ #include <linux/module.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
+ #include <linux/ioport.h>
++#include <linux/init.h>
+ #include <asm/io.h>
+ #include <asm/arch/hardware.h>
+ #include <linux/mtd/mtd.h>
+@@ -16,77 +17,21 @@
+
+
+
+-__u8 cdb89712_read8(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readb(map->map_priv_1 + ofs);
+-}
+-
+-__u16 cdb89712_read16(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readw(map->map_priv_1 + ofs);
+-}
+-
+-__u32 cdb89712_read32(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readl(map->map_priv_1 + ofs);
+-}
+-
+-void cdb89712_write8(struct map_info *map, __u8 d, unsigned long adr)
+-{
+- __raw_writeb(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-void cdb89712_write16(struct map_info *map, __u16 d, unsigned long adr)
+-{
+- __raw_writew(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-void cdb89712_write32(struct map_info *map, __u32 d, unsigned long adr)
+-{
+- __raw_writel(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-void cdb89712_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+-{
+- // printk ("cdb89712_copy_from: 0x%x@0x%x -> 0x%x\n", len, from, to);
+- memcpy_fromio(to, map->map_priv_1 + from, len);
+-}
+-
+-void cdb89712_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+-{
+- while(len) {
+- __raw_writeb(*(unsigned char *) from, map->map_priv_1 + to);
+- from++;
+- to++;
+- len--;
+- }
+-}
+-
+
+ static struct mtd_info *flash_mtd;
+
+ struct map_info cdb89712_flash_map = {
+- name: "flash",
+- size: FLASH_SIZE,
+- buswidth: FLASH_WIDTH,
+- read8: cdb89712_read8,
+- read16: cdb89712_read16,
+- read32: cdb89712_read32,
+- copy_from: cdb89712_copy_from,
+- write8: cdb89712_write8,
+- write16: cdb89712_write16,
+- write32: cdb89712_write32,
+- copy_to: cdb89712_copy_to
++ .name = "flash",
++ .size = FLASH_SIZE,
++ .buswidth = FLASH_WIDTH,
++ .phys = FLASH_START,
+ };
+
+ struct resource cdb89712_flash_resource = {
+- name: "Flash",
+- start: FLASH_START,
+- end: FLASH_START + FLASH_SIZE - 1,
+- flags: IORESOURCE_IO | IORESOURCE_BUSY,
++ .name = "Flash",
++ .start = FLASH_START,
++ .end = FLASH_START + FLASH_SIZE - 1,
++ .flags = IORESOURCE_IO | IORESOURCE_BUSY,
+ };
+
+ static int __init init_cdb89712_flash (void)
+@@ -99,13 +44,13 @@
+ goto out;
+ }
+
+- cdb89712_flash_map.map_priv_1 = (unsigned long)ioremap(FLASH_START, FLASH_SIZE);
+- if (!cdb89712_flash_map.map_priv_1) {
++ cdb89712_flash_map.virt = (unsigned long)ioremap(FLASH_START, FLASH_SIZE);
++ if (!cdb89712_flash_map.virt) {
+ printk(KERN_NOTICE "Failed to ioremap Cdb89712 FLASH space\n");
+ err = -EIO;
+ goto out_resource;
+ }
+-
++ simple_map_init(&cdb89712_flash_map);
+ flash_mtd = do_map_probe("cfi_probe", &cdb89712_flash_map);
+ if (!flash_mtd) {
+ flash_mtd = do_map_probe("map_rom", &cdb89712_flash_map);
+@@ -118,7 +63,7 @@
+ goto out_ioremap;
+ }
+
+- flash_mtd->module = THIS_MODULE;
++ flash_mtd->owner = THIS_MODULE;
+
+ if (add_mtd_device(flash_mtd)) {
+ printk("FLASH device addition failed\n");
+@@ -132,7 +77,7 @@
+ map_destroy(flash_mtd);
+ flash_mtd = 0;
+ out_ioremap:
+- iounmap((void *)cdb89712_flash_map.map_priv_1);
++ iounmap((void *)cdb89712_flash_map.virt);
+ out_resource:
+ release_resource (&cdb89712_flash_resource);
+ out:
+@@ -146,24 +91,17 @@
+ static struct mtd_info *sram_mtd;
+
+ struct map_info cdb89712_sram_map = {
+- name: "SRAM",
+- size: SRAM_SIZE,
+- buswidth: SRAM_WIDTH,
+- read8: cdb89712_read8,
+- read16: cdb89712_read16,
+- read32: cdb89712_read32,
+- copy_from: cdb89712_copy_from,
+- write8: cdb89712_write8,
+- write16: cdb89712_write16,
+- write32: cdb89712_write32,
+- copy_to: cdb89712_copy_to
++ .name = "SRAM",
++ .size = SRAM_SIZE,
++ .buswidth = SRAM_WIDTH,
++ .phys = SRAM_START,
+ };
+
+ struct resource cdb89712_sram_resource = {
+- name: "SRAM",
+- start: SRAM_START,
+- end: SRAM_START + SRAM_SIZE - 1,
+- flags: IORESOURCE_IO | IORESOURCE_BUSY,
++ .name = "SRAM",
++ .start = SRAM_START,
++ .end = SRAM_START + SRAM_SIZE - 1,
++ .flags = IORESOURCE_IO | IORESOURCE_BUSY,
+ };
+
+ static int __init init_cdb89712_sram (void)
+@@ -176,13 +114,13 @@
+ goto out;
+ }
+
+- cdb89712_sram_map.map_priv_1 = (unsigned long)ioremap(SRAM_START, SRAM_SIZE);
+- if (!cdb89712_sram_map.map_priv_1) {
++ cdb89712_sram_map.virt = (unsigned long)ioremap(SRAM_START, SRAM_SIZE);
++ if (!cdb89712_sram_map.virt) {
+ printk(KERN_NOTICE "Failed to ioremap Cdb89712 SRAM space\n");
+ err = -EIO;
+ goto out_resource;
+ }
+-
++ simple_map_init(&cdb89712_sram_map);
+ sram_mtd = do_map_probe("map_ram", &cdb89712_sram_map);
+ if (!sram_mtd) {
+ printk("SRAM probe failed\n");
+@@ -190,7 +128,7 @@
+ goto out_ioremap;
+ }
+
+- sram_mtd->module = THIS_MODULE;
++ sram_mtd->owner = THIS_MODULE;
+ sram_mtd->erasesize = 16;
+
+ if (add_mtd_device(sram_mtd)) {
+@@ -205,7 +143,7 @@
+ map_destroy(sram_mtd);
+ sram_mtd = 0;
+ out_ioremap:
+- iounmap((void *)cdb89712_sram_map.map_priv_1);
++ iounmap((void *)cdb89712_sram_map.virt);
+ out_resource:
+ release_resource (&cdb89712_sram_resource);
+ out:
+@@ -221,20 +159,17 @@
+ static struct mtd_info *bootrom_mtd;
+
+ struct map_info cdb89712_bootrom_map = {
+- name: "BootROM",
+- size: BOOTROM_SIZE,
+- buswidth: BOOTROM_WIDTH,
+- read8: cdb89712_read8,
+- read16: cdb89712_read16,
+- read32: cdb89712_read32,
+- copy_from: cdb89712_copy_from,
++ .name = "BootROM",
++ .size = BOOTROM_SIZE,
++ .buswidth = BOOTROM_WIDTH,
++ .phys = BOOTROM_START,
+ };
+
+ struct resource cdb89712_bootrom_resource = {
+- name: "BootROM",
+- start: BOOTROM_START,
+- end: BOOTROM_START + BOOTROM_SIZE - 1,
+- flags: IORESOURCE_IO | IORESOURCE_BUSY,
++ .name = "BootROM",
++ .start = BOOTROM_START,
++ .end = BOOTROM_START + BOOTROM_SIZE - 1,
++ .flags = IORESOURCE_IO | IORESOURCE_BUSY,
+ };
+
+ static int __init init_cdb89712_bootrom (void)
+@@ -247,13 +182,13 @@
+ goto out;
+ }
+
+- cdb89712_bootrom_map.map_priv_1 = (unsigned long)ioremap(BOOTROM_START, BOOTROM_SIZE);
+- if (!cdb89712_bootrom_map.map_priv_1) {
++ cdb89712_bootrom_map.virt = (unsigned long)ioremap(BOOTROM_START, BOOTROM_SIZE);
++ if (!cdb89712_bootrom_map.virt) {
+ printk(KERN_NOTICE "Failed to ioremap Cdb89712 BootROM space\n");
+ err = -EIO;
+ goto out_resource;
+ }
+-
++ simple_map_init(&cdb89712_bootrom_map);
+ bootrom_mtd = do_map_probe("map_rom", &cdb89712_bootrom_map);
+ if (!bootrom_mtd) {
+ printk("BootROM probe failed\n");
+@@ -261,7 +196,7 @@
+ goto out_ioremap;
+ }
+
+- bootrom_mtd->module = THIS_MODULE;
++ bootrom_mtd->owner = THIS_MODULE;
+ bootrom_mtd->erasesize = 0x10000;
+
+ if (add_mtd_device(bootrom_mtd)) {
+@@ -276,7 +211,7 @@
+ map_destroy(bootrom_mtd);
+ bootrom_mtd = 0;
+ out_ioremap:
+- iounmap((void *)cdb89712_bootrom_map.map_priv_1);
++ iounmap((void *)cdb89712_bootrom_map.virt);
+ out_resource:
+ release_resource (&cdb89712_bootrom_resource);
+ out:
+@@ -306,21 +241,21 @@
+ if (sram_mtd) {
+ del_mtd_device(sram_mtd);
+ map_destroy(sram_mtd);
+- iounmap((void *)cdb89712_sram_map.map_priv_1);
++ iounmap((void *)cdb89712_sram_map.virt);
+ release_resource (&cdb89712_sram_resource);
+ }
+
+ if (flash_mtd) {
+ del_mtd_device(flash_mtd);
+ map_destroy(flash_mtd);
+- iounmap((void *)cdb89712_flash_map.map_priv_1);
++ iounmap((void *)cdb89712_flash_map.virt);
+ release_resource (&cdb89712_flash_resource);
+ }
+
+ if (bootrom_mtd) {
+ del_mtd_device(bootrom_mtd);
+ map_destroy(bootrom_mtd);
+- iounmap((void *)cdb89712_bootrom_map.map_priv_1);
++ iounmap((void *)cdb89712_bootrom_map.virt);
+ release_resource (&cdb89712_bootrom_resource);
+ }
+ }
+diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/ceiva.c linux/drivers/mtd/maps/ceiva.c
+--- linux-mips-2.4.27/drivers/mtd/maps/ceiva.c 2003-02-26 01:53:49.000000000 +0100
++++ linux/drivers/mtd/maps/ceiva.c 2004-11-19 10:25:11.898199944 +0100
+@@ -11,7 +11,7 @@
+ *
+ * (C) 2000 Nicolas Pitre <nico@cam.org>
+ *
+- * $Id$
++ * $Id$
+ */
+
+ #include <linux/config.h>
+@@ -19,6 +19,7 @@
+ #include <linux/types.h>
+ #include <linux/ioport.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
+
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+@@ -31,62 +32,10 @@
+ #include <asm/sizes.h>
+
+ /*
+- * This isnt complete yet, so...
++ * This isn't complete yet, so...
+ */
+ #define CONFIG_MTD_CEIVA_STATICMAP
+
+-static __u8 clps_read8(struct map_info *map, unsigned long ofs)
+-{
+- return readb(map->map_priv_1 + ofs);
+-}
+-
+-static __u16 clps_read16(struct map_info *map, unsigned long ofs)
+-{
+- return readw(map->map_priv_1 + ofs);
+-}
+-
+-static __u32 clps_read32(struct map_info *map, unsigned long ofs)
+-{
+- return readl(map->map_priv_1 + ofs);
+-}
+-
+-static void clps_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+-{
+- memcpy(to, (void *)(map->map_priv_1 + from), len);
+-}
+-
+-static void clps_write8(struct map_info *map, __u8 d, unsigned long adr)
+-{
+- writeb(d, map->map_priv_1 + adr);
+-}
+-
+-static void clps_write16(struct map_info *map, __u16 d, unsigned long adr)
+-{
+- writew(d, map->map_priv_1 + adr);
+-}
+-
+-static void clps_write32(struct map_info *map, __u32 d, unsigned long adr)
+-{
+- writel(d, map->map_priv_1 + adr);
+-}
+-
+-static void clps_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+-{
+- memcpy((void *)(map->map_priv_1 + to), from, len);
+-}
+-
+-static struct map_info clps_map __initdata = {
+- name: "clps flash",
+- read8: clps_read8,
+- read16: clps_read16,
+- read32: clps_read32,
+- copy_from: clps_copy_from,
+- write8: clps_write8,
+- write16: clps_write16,
+- write32: clps_write32,
+- copy_to: clps_copy_to,
+-};
+-
+ #ifdef CONFIG_MTD_CEIVA_STATICMAP
+ /*
+ * See include/linux/mtd/partitions.h for definition of the mtd_partition
+@@ -176,7 +125,7 @@
+ maps = kmalloc(sizeof(struct map_info) * nr, GFP_KERNEL);
+ if (!maps)
+ return -ENOMEM;
+-
++ memset(maps, 0, sizeof(struct map_info) * nr);
+ /*
+ * Claim and then map the memory regions.
+ */
+@@ -191,7 +140,9 @@
+ }
+
+ clps[i].map = maps + i;
+- memcpy(clps[i].map, &clps_map, sizeof(struct map_info));
++
++ clps[i].map->name = "clps flash";
++ clps[i].map->phys = clps[i].base;
+
+ clps[i].vbase = ioremap(clps[i].base, clps[i].size);
+ if (!clps[i].vbase) {
+@@ -199,16 +150,18 @@
+ break;
+ }
+
+- clps[i].map->map_priv_1 = (unsigned long)clps[i].vbase;
++ clps[i].map->virt = (unsigned long)clps[i].vbase;
+ clps[i].map->buswidth = clps[i].width;
+ clps[i].map->size = clps[i].size;
+
++ simple_map_init(&clps[i].map);
++
+ clps[i].mtd = do_map_probe("jedec_probe", clps[i].map);
+ if (clps[i].mtd == NULL) {
+ ret = -ENXIO;
+ break;
+ }
+- clps[i].mtd->module = THIS_MODULE;
++ clps[i].mtd->owner = THIS_MODULE;
+ subdev[i] = clps[i].mtd;
+
+ printk(KERN_INFO "clps flash: JEDEC device at 0x%08lx, %dMiB, "
+@@ -318,10 +271,8 @@
+ return nr;
+ }
+
+-extern int parse_redboot_partitions(struct mtd_info *master, struct mtd_partition **pparts);
+-extern int parse_cmdline_partitions(struct mtd_info *master, struct mtd_partition **pparts, char *);
+-
+ static struct mtd_partition *parsed_parts;
++static const char *probes[] = { "cmdlinepart", "RedBoot", NULL };
+
+ static void __init clps_locate_partitions(struct mtd_info *mtd)
+ {
+@@ -331,20 +282,11 @@
+ /*
+ * Partition selection stuff.
+ */
+-#ifdef CONFIG_MTD_CMDLINE_PARTS
+- nr_parts = parse_cmdline_partitions(mtd, &parsed_parts, "clps");
++ nr_parts = parse_mtd_partitions(mtd, probes, &parsed_parts, 0);
+ if (nr_parts > 0) {
+ part_type = "command line";
+ break;
+ }
+-#endif
+-#ifdef CONFIG_MTD_REDBOOT_PARTS
+- nr_parts = parse_redboot_partitions(mtd, &parsed_parts);
+- if (nr_parts > 0) {
+- part_type = "RedBoot";
+- break;
+- }
+-#endif
+ #ifdef CONFIG_MTD_CEIVA_STATICMAP
+ nr_parts = clps_static_partitions(&parsed_parts);
+ if (nr_parts > 0) {
+diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/cfi_flagadm.c linux/drivers/mtd/maps/cfi_flagadm.c
+--- linux-mips-2.4.27/drivers/mtd/maps/cfi_flagadm.c 2001-11-05 21:15:52.000000000 +0100
++++ linux/drivers/mtd/maps/cfi_flagadm.c 2004-11-19 10:25:11.900199640 +0100
+@@ -1,7 +1,7 @@
+ /*
+ * Copyright © 2001 Flaga hf. Medical Devices, Kári Davíðsson <kd@flaga.is>
+ *
+- * $Id$
++ * $Id$
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+@@ -27,6 +27,7 @@
+ #include <linux/module.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
+ #include <asm/io.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+@@ -55,83 +56,33 @@
+ #define FLASH_PARTITION3_ADDR 0x00240000
+ #define FLASH_PARTITION3_SIZE 0x001C0000
+
+-__u8 flagadm_read8(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readb(map->map_priv_1 + ofs);
+-}
+-
+-__u16 flagadm_read16(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readw(map->map_priv_1 + ofs);
+-}
+-
+-__u32 flagadm_read32(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readl(map->map_priv_1 + ofs);
+-}
+-
+-void flagadm_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+-{
+- memcpy_fromio(to, map->map_priv_1 + from, len);
+-}
+-
+-void flagadm_write8(struct map_info *map, __u8 d, unsigned long adr)
+-{
+- __raw_writeb(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-void flagadm_write16(struct map_info *map, __u16 d, unsigned long adr)
+-{
+- __raw_writew(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-void flagadm_write32(struct map_info *map, __u32 d, unsigned long adr)
+-{
+- __raw_writel(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-void flagadm_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+-{
+- memcpy_toio(map->map_priv_1 + to, from, len);
+-}
+
+ struct map_info flagadm_map = {
+- name: "FlagaDM flash device",
+- size: FLASH_SIZE,
+- buswidth: 2,
+- read8: flagadm_read8,
+- read16: flagadm_read16,
+- read32: flagadm_read32,
+- copy_from: flagadm_copy_from,
+- write8: flagadm_write8,
+- write16: flagadm_write16,
+- write32: flagadm_write32,
+- copy_to: flagadm_copy_to
++ .name = "FlagaDM flash device",
++ .size = FLASH_SIZE,
++ .buswidth = 2,
+ };
+
+ struct mtd_partition flagadm_parts[] = {
+ {
+- name : "Bootloader",
+- offset : FLASH_PARTITION0_ADDR,
+- size : FLASH_PARTITION0_SIZE
++ .name = "Bootloader",
++ .offset = FLASH_PARTITION0_ADDR,
++ .size = FLASH_PARTITION0_SIZE
+ },
+ {
+- name : "Kernel image",
+- offset : FLASH_PARTITION1_ADDR,
+- size : FLASH_PARTITION1_SIZE
++ .name = "Kernel image",
++ .offset = FLASH_PARTITION1_ADDR,
++ .size = FLASH_PARTITION1_SIZE
+ },
+ {
+- name : "Initial ramdisk image",
+- offset : FLASH_PARTITION2_ADDR,
+- size : FLASH_PARTITION2_SIZE
++ .name = "Initial ramdisk image",
++ .offset = FLASH_PARTITION2_ADDR,
++ .size = FLASH_PARTITION2_SIZE
+ },
+ {
+- name : "Persistant storage",
+- offset : FLASH_PARTITION3_ADDR,
+- size : FLASH_PARTITION3_SIZE
++ .name = "Persistant storage",
++ .offset = FLASH_PARTITION3_ADDR,
++ .size = FLASH_PARTITION3_SIZE
+ }
+ };
+
+@@ -144,22 +95,26 @@
+ printk(KERN_NOTICE "FlagaDM flash device: %x at %x\n",
+ FLASH_SIZE, FLASH_PHYS_ADDR);
+
+- flagadm_map.map_priv_1 = (unsigned long)ioremap(FLASH_PHYS_ADDR,
++ flagadm_map.phys = FLASH_PHYS_ADDR;
++ flagadm_map.virt = (unsigned long)ioremap(FLASH_PHYS_ADDR,
+ FLASH_SIZE);
+
+- if (!flagadm_map.map_priv_1) {
++ if (!flagadm_map.virt) {
+ printk("Failed to ioremap\n");
+ return -EIO;
+ }
++
++ simple_map_init(&flagadm_map);
++
+ mymtd = do_map_probe("cfi_probe", &flagadm_map);
+ if (mymtd) {
+- mymtd->module = THIS_MODULE;
++ mymtd->owner = THIS_MODULE;
+ add_mtd_partitions(mymtd, flagadm_parts, PARTITION_COUNT);
+ printk(KERN_NOTICE "FlagaDM flash device initialized\n");
+ return 0;
+ }
+
+- iounmap((void *)flagadm_map.map_priv_1);
++ iounmap((void *)flagadm_map.virt);
+ return -ENXIO;
+ }
+
+@@ -169,9 +124,9 @@
+ del_mtd_partitions(mymtd);
+ map_destroy(mymtd);
+ }
+- if (flagadm_map.map_priv_1) {
+- iounmap((void *)flagadm_map.map_priv_1);
+- flagadm_map.map_priv_1 = 0;
++ if (flagadm_map.virt) {
++ iounmap((void *)flagadm_map.virt);
++ flagadm_map.virt = 0;
+ }
+ }
+
+diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/cstm_mips_ixx.c linux/drivers/mtd/maps/cstm_mips_ixx.c
+--- linux-mips-2.4.27/drivers/mtd/maps/cstm_mips_ixx.c 2001-11-05 21:15:52.000000000 +0100
++++ linux/drivers/mtd/maps/cstm_mips_ixx.c 2004-11-19 10:25:11.901199488 +0100
+@@ -1,5 +1,5 @@
+ /*
+- * $Id$
++ * $Id$
+ *
+ * Mapping of a custom board with both AMD CFI and JEDEC flash in partitions.
+ * Config with both CFI and JEDEC device support.
+@@ -33,55 +33,13 @@
+ #include <linux/module.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
+ #include <asm/io.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+ #include <linux/mtd/partitions.h>
+ #include <linux/config.h>
+-
+-#if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR)
+ #include <linux/delay.h>
+-#endif
+-
+-__u8 cstm_mips_ixx_read8(struct map_info *map, unsigned long ofs)
+-{
+- return *(__u8 *)(map->map_priv_1 + ofs);
+-}
+-
+-__u16 cstm_mips_ixx_read16(struct map_info *map, unsigned long ofs)
+-{
+- return *(__u16 *)(map->map_priv_1 + ofs);
+-}
+-
+-__u32 cstm_mips_ixx_read32(struct map_info *map, unsigned long ofs)
+-{
+- return *(__u32 *)(map->map_priv_1 + ofs);
+-}
+-
+-void cstm_mips_ixx_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+-{
+- memcpy_fromio(to, map->map_priv_1 + from, len);
+-}
+-
+-void cstm_mips_ixx_write8(struct map_info *map, __u8 d, unsigned long adr)
+-{
+- *(__u8 *)(map->map_priv_1 + adr) = d;
+-}
+-
+-void cstm_mips_ixx_write16(struct map_info *map, __u16 d, unsigned long adr)
+-{
+- *(__u16 *)(map->map_priv_1 + adr) = d;
+-}
+-
+-void cstm_mips_ixx_write32(struct map_info *map, __u32 d, unsigned long adr)
+-{
+- *(__u32 *)(map->map_priv_1 + adr) = d;
+-}
+-
+-void cstm_mips_ixx_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+-{
+- memcpy_toio(map->map_priv_1 + to, from, len);
+-}
+
+ #if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR)
+ #define CC_GCR 0xB4013818
+@@ -97,10 +55,17 @@
+ #define CC_GPAICR 0xB4013804
+ #endif /* defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR) */
+
++#if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR)
+ void cstm_mips_ixx_set_vpp(struct map_info *map,int vpp)
+ {
++ static spinlock_t vpp_lock = SPIN_LOCK_UNLOCKED;
++ static int vpp_count = 0;
++ unsigned long flags;
++
++ spin_lock_irqsave(&vpp_lock, flags);
++
+ if (vpp) {
+-#if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR)
++ if (!vpp_count++) {
+ __u16 data;
+ __u8 data1;
+ static u8 first = 1;
+@@ -116,10 +81,9 @@
+ enabling vpp after powerup */
+ udelay(40);
+ }
+-#endif /* CONFIG_MIPS_ITE8172 */
+ }
+- else {
+-#if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR)
++ } else {
++ if (!--vpp_count) {
+ __u16 data;
+
+ // Set GPIO port B pin3 to high
+@@ -127,26 +91,11 @@
+ data = (data & 0xff3f) | 0x0040;
+ *(__u16 *)CC_GPBCR = data;
+ *(__u8 *)CC_GPBDR = (*(__u8*)CC_GPBDR) & 0xf7;
+-#endif /* CONFIG_MIPS_ITE8172 */
+ }
++ }
++ spin_unlock_irqrestore(&vpp_lock, flags);
+ }
+-
+-const struct map_info basic_cstm_mips_ixx_map = {
+- NULL,
+- 0,
+- 0,
+- cstm_mips_ixx_read8,
+- cstm_mips_ixx_read16,
+- cstm_mips_ixx_read32,
+- cstm_mips_ixx_copy_from,
+- cstm_mips_ixx_write8,
+- cstm_mips_ixx_write16,
+- cstm_mips_ixx_write32,
+- cstm_mips_ixx_copy_to,
+- cstm_mips_ixx_set_vpp,
+- 0,
+- 0
+-};
++#endif
+
+ /* board and partition description */
+
+@@ -175,9 +124,9 @@
+ static struct mtd_partition cstm_mips_ixx_partitions[PHYSMAP_NUMBER][MAX_PHYSMAP_PARTITIONS] = {
+ { // 28F128J3A in 2x16 configuration
+ {
+- name: "main partition ",
+- size: 0x02000000, // 128 x 2 x 128k byte sectors
+- offset: 0,
++ .name = "main partition ",
++ .size = 0x02000000, // 128 x 2 x 128k byte sectors
++ .offset = 0,
+ },
+ },
+ };
+@@ -197,9 +146,9 @@
+ static struct mtd_partition cstm_mips_ixx_partitions[PHYSMAP_NUMBER][MAX_PHYSMAP_PARTITIONS] = {
+ {
+ {
+- name: "main partition",
+- size: CONFIG_MTD_CSTM_MIPS_IXX_LEN,
+- offset: 0,
++ .name = "main partition",
++ .size = CONFIG_MTD_CSTM_MIPS_IXX_LEN,
++ .offset = 0,
+ },
+ },
+ };
+@@ -216,17 +165,24 @@
+
+ /* Initialize mapping */
+ for (i=0;i<PHYSMAP_NUMBER;i++) {
+- printk(KERN_NOTICE "cstm_mips_ixx flash device: %lx at %lx\n", cstm_mips_ixx_board_desc[i].window_size, cstm_mips_ixx_board_desc[i].window_addr);
+- memcpy((char *)&cstm_mips_ixx_map[i],(char *)&basic_cstm_mips_ixx_map,sizeof(struct map_info));
+- cstm_mips_ixx_map[i].map_priv_1 = (unsigned long)ioremap(cstm_mips_ixx_board_desc[i].window_addr, cstm_mips_ixx_board_desc[i].window_size);
+- if (!cstm_mips_ixx_map[i].map_priv_1) {
++ printk(KERN_NOTICE "cstm_mips_ixx flash device: 0x%lx at 0x%lx\n",
++ cstm_mips_ixx_board_desc[i].window_size, cstm_mips_ixx_board_desc[i].window_addr);
++
++
++ cstm_mips_ixx_map[i].phys = cstm_mips_ixx_board_desc[i].window_addr;
++ cstm_mips_ixx_map[i].virt = (unsigned long)ioremap(cstm_mips_ixx_board_desc[i].window_addr, cstm_mips_ixx_board_desc[i].window_size);
++ if (!cstm_mips_ixx_map[i].virt) {
+ printk(KERN_WARNING "Failed to ioremap\n");
+ return -EIO;
+ }
+ cstm_mips_ixx_map[i].name = cstm_mips_ixx_board_desc[i].name;
+ cstm_mips_ixx_map[i].size = cstm_mips_ixx_board_desc[i].window_size;
+ cstm_mips_ixx_map[i].buswidth = cstm_mips_ixx_board_desc[i].buswidth;
+- //printk(KERN_NOTICE "cstm_mips_ixx: ioremap is %x\n",(unsigned int)(cstm_mips_ixx_map[i].map_priv_1));
++#if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR)
++ cstm_mips_ixx_map[i].set_vpp = cstm_mips_ixx_set_vpp;
++#endif
++ simple_map_init(&cstm_mips_ixx_map[i]);
++ //printk(KERN_NOTICE "cstm_mips_ixx: ioremap is %x\n",(unsigned int)(cstm_mips_ixx_map[i].virt));
+ }
+
+ #if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR)
+@@ -244,7 +200,7 @@
+ printk(KERN_NOTICE "cstm_mips_ixx %d jedec: mymtd is %x\n",i,(unsigned int)mymtd);
+ }
+ if (mymtd) {
+- mymtd->module = THIS_MODULE;
++ mymtd->owner = THIS_MODULE;
+
+ cstm_mips_ixx_map[i].map_priv_2 = (unsigned long)mymtd;
+ add_mtd_partitions(mymtd, parts, cstm_mips_ixx_board_desc[i].num_partitions);
+@@ -266,9 +222,9 @@
+ del_mtd_partitions(mymtd);
+ map_destroy(mymtd);
+ }
+- if (cstm_mips_ixx_map[i].map_priv_1) {
+- iounmap((void *)cstm_mips_ixx_map[i].map_priv_1);
+- cstm_mips_ixx_map[i].map_priv_1 = 0;
++ if (cstm_mips_ixx_map[i].virt) {
++ iounmap((void *)cstm_mips_ixx_map[i].virt);
++ cstm_mips_ixx_map[i].virt = 0;
+ }
+ }
+ }
+diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/db1x00-flash.c linux/drivers/mtd/maps/db1x00-flash.c
+--- linux-mips-2.4.27/drivers/mtd/maps/db1x00-flash.c 2003-02-16 07:25:24.000000000 +0100
++++ linux/drivers/mtd/maps/db1x00-flash.c 2004-11-19 10:25:11.903199184 +0100
+@@ -8,6 +8,7 @@
+ #include <linux/config.h>
+ #include <linux/module.h>
+ #include <linux/types.h>
++#include <linux/init.h>
+ #include <linux/kernel.h>
+
+ #include <linux/mtd/mtd.h>
+@@ -29,76 +30,6 @@
+ static unsigned long flash_size;
+
+ static BCSR * const bcsr = (BCSR *)0xAE000000;
+-
+-__u8 physmap_read8(struct map_info *map, unsigned long ofs)
+-{
+- __u8 ret;
+- ret = __raw_readb(map->map_priv_1 + ofs);
+- DBG("read8 from %x, %x\n", (unsigned)(map->map_priv_1 + ofs), ret);
+- return ret;
+-}
+-
+-__u16 physmap_read16(struct map_info *map, unsigned long ofs)
+-{
+- __u16 ret;
+- ret = __raw_readw(map->map_priv_1 + ofs);
+- DBG("read16 from %x, %x\n", (unsigned)(map->map_priv_1 + ofs), ret);
+- return ret;
+-}
+-
+-__u32 physmap_read32(struct map_info *map, unsigned long ofs)
+-{
+- __u32 ret;
+- ret = __raw_readl(map->map_priv_1 + ofs);
+- DBG("read32 from %x, %x\n", (unsigned)(map->map_priv_1 + ofs), ret);
+- return ret;
+-}
+-
+-void physmap_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+-{
+- DBG("physmap_copy from %x to %x\n", (unsigned)from, (unsigned)to);
+- memcpy_fromio(to, map->map_priv_1 + from, len);
+-}
+-
+-void physmap_write8(struct map_info *map, __u8 d, unsigned long adr)
+-{
+- DBG("write8 at %x, %x\n", (unsigned)(map->map_priv_1 + adr), d);
+- __raw_writeb(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-void physmap_write16(struct map_info *map, __u16 d, unsigned long adr)
+-{
+- DBG("write16 at %x, %x\n", (unsigned)(map->map_priv_1 + adr), d);
+- __raw_writew(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-void physmap_write32(struct map_info *map, __u32 d, unsigned long adr)
+-{
+- DBG("write32 at %x, %x\n", (unsigned)(map->map_priv_1 + adr), d);
+- __raw_writel(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-void physmap_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+-{
+- DBG("physmap_copy_to %x from %x\n", (unsigned)to, (unsigned)from);
+- memcpy_toio(map->map_priv_1 + to, from, len);
+-}
+-
+-static struct map_info db1x00_map = {
+- name: "Db1x00 flash",
+- read8: physmap_read8,
+- read16: physmap_read16,
+- read32: physmap_read32,
+- copy_from: physmap_copy_from,
+- write8: physmap_write8,
+- write16: physmap_write16,
+- write32: physmap_write32,
+- copy_to: physmap_copy_to,
+-};
+-
+ static unsigned char flash_buswidth = 4;
+
+ /*
+@@ -115,58 +46,62 @@
+ */
+ static struct mtd_partition db1x00_partitions[] = {
+ {
+- name: "User FS",
+- size: 0x1c00000,
+- offset: 0x0000000
++ .name = "User FS",
++ .size = 0x1c00000,
++ .offset = 0x0000000
+ },{
+- name: "yamon",
+- size: 0x0100000,
+- offset: MTDPART_OFS_APPEND,
+- mask_flags: MTD_WRITEABLE
++ .name = "yamon",
++ .size = 0x0100000,
++ .offset = MTDPART_OFS_APPEND,
++ .mask_flags = MTD_WRITEABLE
+ },{
+- name: "raw kernel",
+- size: (0x300000-0x40000), /* last 256KB is yamon env */
+- offset: MTDPART_OFS_APPEND,
++ .name = "raw kernel",
++ .size = (0x300000-0x40000), /* last 256KB is env */
++ .offset = MTDPART_OFS_APPEND,
+ }
+ };
+ #elif defined(DB1X00_BOOT_ONLY)
+ static struct mtd_partition db1x00_partitions[] = {
+ {
+- name: "User FS",
+- size: 0x00c00000,
+- offset: 0x0000000
++ .name = "User FS",
++ .size = 0x00c00000,
++ .offset = 0x0000000
+ },{
+- name: "yamon",
+- size: 0x0100000,
+- offset: MTDPART_OFS_APPEND,
+- mask_flags: MTD_WRITEABLE
++ .name = "yamon",
++ .size = 0x0100000,
++ .offset = MTDPART_OFS_APPEND,
++ .mask_flags = MTD_WRITEABLE
+ },{
+- name: "raw kernel",
+- size: (0x300000-0x40000), /* last 256KB is yamon env */
+- offset: MTDPART_OFS_APPEND,
++ .name = "raw kernel",
++ .size = (0x300000-0x40000), /* last 256KB is env */
++ .offset = MTDPART_OFS_APPEND,
+ }
+ };
+ #elif defined(DB1X00_USER_ONLY)
+ static struct mtd_partition db1x00_partitions[] = {
+ {
+- name: "User FS",
+- size: 0x0e00000,
+- offset: 0x0000000
++ .name = "User FS",
++ .size = 0x0e00000,
++ .offset = 0x0000000
+ },{
+- name: "raw kernel",
+- size: MTDPART_SIZ_FULL,
+- offset: MTDPART_OFS_APPEND,
++ .name = "raw kernel",
++ .size = MTDPART_SIZ_FULL,
++ .offset = MTDPART_OFS_APPEND,
+ }
+ };
+ #else
+ #error MTD_DB1X00 define combo error /* should never happen */
+ #endif
++#define NB_OF(x) (sizeof(x)/sizeof(x[0]))
+
++#define NAME "Db1x00 Linux Flash"
+
+-#define NB_OF(x) (sizeof(x)/sizeof(x[0]))
++static struct map_info db1xxx_mtd_map = {
++ .name = NAME,
++};
+
+ static struct mtd_partition *parsed_parts;
+-static struct mtd_info *mymtd;
++static struct mtd_info *db1xxx_mtd;
+
+ /*
+ * Probe the flash density and setup window address and size
+@@ -174,7 +109,7 @@
+ * want the MTD driver to be probing the boot or user flash,
+ * so having the option to enable only one bank is important.
+ */
+-int setup_flash_params()
++int setup_flash_params(void)
+ {
+ switch ((bcsr->status >> 14) & 0x3) {
+ case 0: /* 64Mbit devices */
+@@ -228,6 +163,10 @@
+ default:
+ return 1;
+ }
++ db1xxx_mtd_map.size = window_size;
++ db1xxx_mtd_map.buswidth = flash_buswidth;
++ db1xxx_mtd_map.phys = window_addr;
++ db1xxx_mtd_map.buswidth = flash_buswidth;
+ return 0;
+ }
+
+@@ -235,10 +174,6 @@
+ {
+ struct mtd_partition *parts;
+ int nb_parts = 0;
+- char *part_type;
+-
+- /* Default flash buswidth */
+- db1x00_map.buswidth = flash_buswidth;
+
+ if (setup_flash_params())
+ return -ENXIO;
+@@ -246,32 +181,29 @@
+ /*
+ * Static partition definition selection
+ */
+- part_type = "static";
+ parts = db1x00_partitions;
+ nb_parts = NB_OF(db1x00_partitions);
+- db1x00_map.size = window_size;
+
+ /*
+ * Now let's probe for the actual flash. Do it here since
+ * specific machine settings might have been set above.
+ */
+ printk(KERN_NOTICE "Db1xxx flash: probing %d-bit flash bus\n",
+- db1x00_map.buswidth*8);
+- db1x00_map.map_priv_1 =
+- (unsigned long)ioremap(window_addr, window_size);
+- mymtd = do_map_probe("cfi_probe", &db1x00_map);
+- if (!mymtd) return -ENXIO;
+- mymtd->module = THIS_MODULE;
++ db1xxx_mtd_map.buswidth*8);
++ db1xxx_mtd_map.virt = (unsigned long)ioremap(window_addr, window_size);
++ db1xxx_mtd = do_map_probe("cfi_probe", &db1xxx_mtd_map);
++ if (!db1xxx_mtd) return -ENXIO;
++ db1xxx_mtd->owner = THIS_MODULE;
+
+- add_mtd_partitions(mymtd, parts, nb_parts);
++ add_mtd_partitions(db1xxx_mtd, parts, nb_parts);
+ return 0;
+ }
+
+ static void __exit db1x00_mtd_cleanup(void)
+ {
+- if (mymtd) {
+- del_mtd_partitions(mymtd);
+- map_destroy(mymtd);
++ if (db1xxx_mtd) {
++ del_mtd_partitions(db1xxx_mtd);
++ map_destroy(db1xxx_mtd);
+ if (parsed_parts)
+ kfree(parsed_parts);
+ }
+diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/dbox2-flash.c linux/drivers/mtd/maps/dbox2-flash.c
+--- linux-mips-2.4.27/drivers/mtd/maps/dbox2-flash.c 2001-11-05 21:15:52.000000000 +0100
++++ linux/drivers/mtd/maps/dbox2-flash.c 2004-11-19 10:25:11.904199032 +0100
+@@ -1,12 +1,13 @@
+ /*
+- * $Id$
++ * $Id$
+ *
+- * Nokia / Sagem D-Box 2 flash driver
++ * D-Box 2 flash driver
+ */
+
+ #include <linux/module.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
+ #include <asm/io.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+@@ -16,22 +17,44 @@
+ /* partition_info gives details on the logical partitions that the split the
+ * single flash device into. If the size if zero we use up to the end of the
+ * device. */
+-static struct mtd_partition partition_info[]= {{name: "BR bootloader", // raw
+- size: 128 * 1024,
+- offset: 0,
+- mask_flags: MTD_WRITEABLE},
+- {name: "PPC bootloader", // flfs
+- size: 128 * 1024,
+- offset: MTDPART_OFS_APPEND,
+- mask_flags: 0},
+- {name: "Kernel", // idxfs
+- size: 768 * 1024,
+- offset: MTDPART_OFS_APPEND,
+- mask_flags: 0},
+- {name: "System", // jffs
+- size: MTDPART_SIZ_FULL,
+- offset: MTDPART_OFS_APPEND,
+- mask_flags: 0}};
++static struct mtd_partition partition_info[]= {
++ {
++ .name = "BR bootloader",
++ .size = 128 * 1024,
++ .offset = 0,
++ .mask_flags = MTD_WRITEABLE
++ },
++ {
++ .name = "flfs (ppcboot)",
++ .size = 128 * 1024,
++ .offset = MTDPART_OFS_APPEND,
++ .mask_flags = 0
++ },
++ {
++ .name = "root (cramfs)",
++ .size = 7040 * 1024,
++ .offset = MTDPART_OFS_APPEND,
++ .mask_flags = 0
++ },
++ {
++ .name = "var (jffs2)",
++ .size = 896 * 1024,
++ .offset = MTDPART_OFS_APPEND,
++ .mask_flags = 0
++ },
++ {
++ .name = "flash without bootloader",
++ .size = MTDPART_SIZ_FULL,
++ .offset = 128 * 1024,
++ .mask_flags = 0
++ },
++ {
++ .name = "complete flash",
++ .size = MTDPART_SIZ_FULL,
++ .offset = 0,
++ .mask_flags = MTD_WRITEABLE
++ }
++};
+
+ #define NUM_PARTITIONS (sizeof(partition_info) / sizeof(partition_info[0]))
+
+@@ -40,72 +63,24 @@
+
+ static struct mtd_info *mymtd;
+
+-__u8 dbox2_flash_read8(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readb(map->map_priv_1 + ofs);
+-}
+-
+-__u16 dbox2_flash_read16(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readw(map->map_priv_1 + ofs);
+-}
+-
+-__u32 dbox2_flash_read32(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readl(map->map_priv_1 + ofs);
+-}
+-
+-void dbox2_flash_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+-{
+- memcpy_fromio(to, map->map_priv_1 + from, len);
+-}
+-
+-void dbox2_flash_write8(struct map_info *map, __u8 d, unsigned long adr)
+-{
+- __raw_writeb(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-void dbox2_flash_write16(struct map_info *map, __u16 d, unsigned long adr)
+-{
+- __raw_writew(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-void dbox2_flash_write32(struct map_info *map, __u32 d, unsigned long adr)
+-{
+- __raw_writel(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-void dbox2_flash_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+-{
+- memcpy_toio(map->map_priv_1 + to, from, len);
+-}
+
+ struct map_info dbox2_flash_map = {
+- name: "D-Box 2 flash memory",
+- size: WINDOW_SIZE,
+- buswidth: 4,
+- read8: dbox2_flash_read8,
+- read16: dbox2_flash_read16,
+- read32: dbox2_flash_read32,
+- copy_from: dbox2_flash_copy_from,
+- write8: dbox2_flash_write8,
+- write16: dbox2_flash_write16,
+- write32: dbox2_flash_write32,
+- copy_to: dbox2_flash_copy_to
++ .name = "D-Box 2 flash memory",
++ .size = WINDOW_SIZE,
++ .buswidth = 4,
++ .phys = WINDOW_ADDR,
+ };
+
+ int __init init_dbox2_flash(void)
+ {
+ printk(KERN_NOTICE "D-Box 2 flash driver (size->0x%X mem->0x%X)\n", WINDOW_SIZE, WINDOW_ADDR);
+- dbox2_flash_map.map_priv_1 = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE);
++ dbox2_flash_map.virt = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE);
+
+- if (!dbox2_flash_map.map_priv_1) {
++ if (!dbox2_flash_map.virt) {
+ printk("Failed to ioremap\n");
+ return -EIO;
+ }
++ simple_map_init(&dbox2_flash_map);
+
+ // Probe for dual Intel 28F320 or dual AMD
+ mymtd = do_map_probe("cfi_probe", &dbox2_flash_map);
+@@ -117,7 +92,7 @@
+ }
+
+ if (mymtd) {
+- mymtd->module = THIS_MODULE;
++ mymtd->owner = THIS_MODULE;
+
+ /* Create MTD devices for each partition. */
+ add_mtd_partitions(mymtd, partition_info, NUM_PARTITIONS);
+@@ -125,7 +100,7 @@
+ return 0;
+ }
+
+- iounmap((void *)dbox2_flash_map.map_priv_1);
++ iounmap((void *)dbox2_flash_map.virt);
+ return -ENXIO;
+ }
+
+@@ -135,9 +110,9 @@
+ del_mtd_partitions(mymtd);
+ map_destroy(mymtd);
+ }
+- if (dbox2_flash_map.map_priv_1) {
+- iounmap((void *)dbox2_flash_map.map_priv_1);
+- dbox2_flash_map.map_priv_1 = 0;
++ if (dbox2_flash_map.virt) {
++ iounmap((void *)dbox2_flash_map.virt);
++ dbox2_flash_map.virt = 0;
+ }
+ }
+
+@@ -146,5 +121,5 @@
+
+
+ MODULE_LICENSE("GPL");
+-MODULE_AUTHOR("Kári Davíðsson <kd@flaga.is>");
+-MODULE_DESCRIPTION("MTD map driver for Nokia/Sagem D-Box 2 board");
++MODULE_AUTHOR("Kári Davíðsson <kd@flaga.is>, Bastian Blank <waldi@tuxbox.org>, Alexander Wild <wild@te-elektronik.com>");
++MODULE_DESCRIPTION("MTD map driver for D-Box 2 board");
+diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/dc21285.c linux/drivers/mtd/maps/dc21285.c
+--- linux-mips-2.4.27/drivers/mtd/maps/dc21285.c 2003-02-26 01:53:49.000000000 +0100
++++ linux/drivers/mtd/maps/dc21285.c 2004-11-19 10:25:11.906198728 +0100
+@@ -5,12 +5,13 @@
+ *
+ * This code is GPL
+ *
+- * $Id$
++ * $Id$
+ */
+ #include <linux/config.h>
+ #include <linux/module.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
+
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+@@ -92,26 +93,42 @@
+ }
+
+ struct map_info dc21285_map = {
+- name: "DC21285 flash",
+- size: 16*1024*1024,
+- read8: dc21285_read8,
+- read16: dc21285_read16,
+- read32: dc21285_read32,
+- copy_from: dc21285_copy_from,
+- write8: dc21285_write8,
+- write16: dc21285_write16,
+- write32: dc21285_write32,
+- copy_to: dc21285_copy_to
++ .name = "DC21285 flash",
++ .phys = NO_XIP,
++ .size = 16*1024*1024,
++ .read8 = dc21285_read8,
++ .read16 = dc21285_read16,
++ .read32 = dc21285_read32,
++ .copy_from = dc21285_copy_from,
++ .write8 = dc21285_write8,
++ .write16 = dc21285_write16,
++ .write32 = dc21285_write32,
++ .copy_to = dc21285_copy_to
+ };
+
+
+ /* Partition stuff */
+ static struct mtd_partition *dc21285_parts;
+-
+-extern int parse_redboot_partitions(struct mtd_info *, struct mtd_partition **);
++#ifdef CONFIG_MTD_PARTITIONS
++static const char *probes[] = { "RedBoot", "cmdlinepart", NULL };
++#endif
+
+ int __init init_dc21285(void)
+ {
++
++ /*
++ * Flash timing is determined with bits 19-16 of the
++ * CSR_SA110_CNTL. The value is the number of wait cycles, or
++ * 0 for 16 cycles (the default). Cycles are 20 ns.
++ * Here we use 7 for 140 ns flash chips.
++ */
++ /* access time */
++ *CSR_SA110_CNTL = ((*CSR_SA110_CNTL & ~0x000f0000) | (7 << 16));
++ /* burst time */
++ *CSR_SA110_CNTL = ((*CSR_SA110_CNTL & ~0x00f00000) | (7 << 20));
++ /* tristate time */
++ *CSR_SA110_CNTL = ((*CSR_SA110_CNTL & ~0x0f000000) | (7 << 24));
++
+ /* Determine buswidth */
+ switch (*CSR_SA110_CNTL & (3<<14)) {
+ case SA110_CNTL_ROMWIDTH_8:
+@@ -141,33 +158,18 @@
+ if (mymtd) {
+ int nrparts = 0;
+
+- mymtd->module = THIS_MODULE;
++ mymtd->owner = THIS_MODULE;
+
+ /* partition fixup */
+
+-#ifdef CONFIG_MTD_REDBOOT_PARTS
+- nrparts = parse_redboot_partitions(mymtd, &dc21285_parts);
+-#endif
++#ifdef CONFIG_MTD_PARTITIONS
++ nrparts = parse_mtd_partitions(mymtd, probes, &dc21285_parts, (void *)0);
+ if (nrparts > 0) {
+ add_mtd_partitions(mymtd, dc21285_parts, nrparts);
+- } else if (nrparts == 0) {
+- printk(KERN_NOTICE "RedBoot partition table failed\n");
+- add_mtd_device(mymtd);
++ return 0;
+ }
+-
+- /*
+- * Flash timing is determined with bits 19-16 of the
+- * CSR_SA110_CNTL. The value is the number of wait cycles, or
+- * 0 for 16 cycles (the default). Cycles are 20 ns.
+- * Here we use 7 for 140 ns flash chips.
+- */
+- /* access time */
+- *CSR_SA110_CNTL = ((*CSR_SA110_CNTL & ~0x000f0000) | (7 << 16));
+- /* burst time */
+- *CSR_SA110_CNTL = ((*CSR_SA110_CNTL & ~0x00f00000) | (7 << 20));
+- /* tristate time */
+- *CSR_SA110_CNTL = ((*CSR_SA110_CNTL & ~0x0f000000) | (7 << 24));
+-
++#endif
++ add_mtd_device(mymtd);
+ return 0;
+ }
+
+@@ -177,17 +179,16 @@
+
+ static void __exit cleanup_dc21285(void)
+ {
+- if (mymtd) {
++#ifdef CONFIG_MTD_PARTITIONS
++ if (dc21285_parts) {
++ del_mtd_partitions(mymtd);
++ kfree(dc21285_parts);
++ } else
++#endif
+ del_mtd_device(mymtd);
++
+ map_destroy(mymtd);
+- mymtd = NULL;
+- }
+- if (dc21285_map.map_priv_1) {
+ iounmap((void *)dc21285_map.map_priv_1);
+- dc21285_map.map_priv_1 = 0;
+- }
+- if(dc21285_parts)
+- kfree(dc21285_parts);
+ }
+
+ module_init(init_dc21285);
+diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/dilnetpc.c linux/drivers/mtd/maps/dilnetpc.c
+--- linux-mips-2.4.27/drivers/mtd/maps/dilnetpc.c 2002-06-27 00:35:50.000000000 +0200
++++ linux/drivers/mtd/maps/dilnetpc.c 2004-11-19 10:25:11.907198576 +0100
+@@ -14,7 +14,7 @@
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+- * $Id$
++ * $Id$
+ *
+ * The DIL/Net PC is a tiny embedded PC board made by SSV Embedded Systems
+ * featuring the AMD Elan SC410 processor. There are two variants of this
+@@ -29,6 +29,7 @@
+ #include <linux/module.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
+ #include <asm/io.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+@@ -36,7 +37,7 @@
+ #include <linux/mtd/concat.h>
+
+ /*
+-** The DIL/NetPC keeps it's BIOS in two distinct flash blocks.
++** The DIL/NetPC keeps its BIOS in two distinct flash blocks.
+ ** Destroying any of these blocks transforms the DNPC into
+ ** a paperweight (albeit not a very useful one, considering
+ ** it only weighs a few grams).
+@@ -189,45 +190,6 @@
+ }
+
+
+-static __u8 dnpc_read8(struct map_info *map, unsigned long ofs)
+-{
+- return readb(map->map_priv_1 + ofs);
+-}
+-
+-static __u16 dnpc_read16(struct map_info *map, unsigned long ofs)
+-{
+- return readw(map->map_priv_1 + ofs);
+-}
+-
+-static __u32 dnpc_read32(struct map_info *map, unsigned long ofs)
+-{
+- return readl(map->map_priv_1 + ofs);
+-}
+-
+-static void dnpc_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+-{
+- memcpy_fromio(to, (void *)(map->map_priv_1 + from), len);
+-}
+-
+-static void dnpc_write8(struct map_info *map, __u8 d, unsigned long adr)
+-{
+- writeb(d, map->map_priv_1 + adr);
+-}
+-
+-static void dnpc_write16(struct map_info *map, __u16 d, unsigned long adr)
+-{
+- writew(d, map->map_priv_1 + adr);
+-}
+-
+-static void dnpc_write32(struct map_info *map, __u32 d, unsigned long adr)
+-{
+- writel(d, map->map_priv_1 + adr);
+-}
+-
+-static void dnpc_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+-{
+- memcpy_toio((void *)(map->map_priv_1 + to), from, len);
+-}
+
+ /*
+ ************************************************************
+@@ -288,19 +250,11 @@
+ #define WINDOW_ADDR FLASH_BASE
+
+ static struct map_info dnpc_map = {
+- name: "ADNP Flash Bank",
+- size: ADNP_WINDOW_SIZE,
+- buswidth: 1,
+- read8: dnpc_read8,
+- read16: dnpc_read16,
+- read32: dnpc_read32,
+- copy_from: dnpc_copy_from,
+- write8: dnpc_write8,
+- write16: dnpc_write16,
+- write32: dnpc_write32,
+- copy_to: dnpc_copy_to,
+- set_vpp: adnp_set_vpp,
+- map_priv_2: WINDOW_ADDR
++ .name = "ADNP Flash Bank",
++ .size = ADNP_WINDOW_SIZE,
++ .buswidth = 1,
++ .set_vpp = adnp_set_vpp,
++ .phys = WINDOW_ADDR
+ };
+
+ /*
+@@ -316,29 +270,29 @@
+ static struct mtd_partition partition_info[]=
+ {
+ {
+- name: "ADNP boot",
+- offset: 0,
+- size: 0xf0000,
++ .name = "ADNP boot",
++ .offset = 0,
++ .size = 0xf0000,
+ },
+ {
+- name: "ADNP system BIOS",
+- offset: MTDPART_OFS_NXTBLK,
+- size: 0x10000,
++ .name = "ADNP system BIOS",
++ .offset = MTDPART_OFS_NXTBLK,
++ .size = 0x10000,
+ #ifdef DNPC_BIOS_BLOCKS_WRITEPROTECTED
+- mask_flags: MTD_WRITEABLE,
++ .mask_flags = MTD_WRITEABLE,
+ #endif
+ },
+ {
+- name: "ADNP file system",
+- offset: MTDPART_OFS_NXTBLK,
+- size: 0x2f0000,
++ .name = "ADNP file system",
++ .offset = MTDPART_OFS_NXTBLK,
++ .size = 0x2f0000,
+ },
+ {
+- name: "ADNP system BIOS entry",
+- offset: MTDPART_OFS_NXTBLK,
+- size: MTDPART_SIZ_FULL,
++ .name = "ADNP system BIOS entry",
++ .offset = MTDPART_OFS_NXTBLK,
++ .size = MTDPART_SIZ_FULL,
+ #ifdef DNPC_BIOS_BLOCKS_WRITEPROTECTED
+- mask_flags: MTD_WRITEABLE,
++ .mask_flags = MTD_WRITEABLE,
+ #endif
+ },
+ };
+@@ -369,21 +323,21 @@
+ static struct mtd_partition higlvl_partition_info[]=
+ {
+ {
+- name: "ADNP boot block",
+- offset: 0,
+- size: CONFIG_MTD_DILNETPC_BOOTSIZE,
++ .name = "ADNP boot block",
++ .offset = 0,
++ .size = CONFIG_MTD_DILNETPC_BOOTSIZE,
+ },
+ {
+- name: "ADNP file system space",
+- offset: MTDPART_OFS_NXTBLK,
+- size: ADNP_WINDOW_SIZE-CONFIG_MTD_DILNETPC_BOOTSIZE-0x20000,
++ .name = "ADNP file system space",
++ .offset = MTDPART_OFS_NXTBLK,
++ .size = ADNP_WINDOW_SIZE-CONFIG_MTD_DILNETPC_BOOTSIZE-0x20000,
+ },
+ {
+- name: "ADNP system BIOS + BIOS Entry",
+- offset: MTDPART_OFS_NXTBLK,
+- size: MTDPART_SIZ_FULL,
++ .name = "ADNP system BIOS + BIOS Entry",
++ .offset = MTDPART_OFS_NXTBLK,
++ .size = MTDPART_SIZ_FULL,
+ #ifdef DNPC_BIOS_BLOCKS_WRITEPROTECTED
+- mask_flags: MTD_WRITEABLE,
++ .mask_flags = MTD_WRITEABLE,
+ #endif
+ },
+ };
+@@ -447,18 +401,19 @@
+ }
+
+ printk(KERN_NOTICE "DIL/Net %s flash: 0x%lx at 0x%lx\n",
+- is_dnp ? "DNPC" : "ADNP", dnpc_map.size, dnpc_map.map_priv_2);
++ is_dnp ? "DNPC" : "ADNP", dnpc_map.size, dnpc_map.phys);
+
+- dnpc_map.map_priv_1 = (unsigned long)ioremap_nocache(dnpc_map.map_priv_2, dnpc_map.size);
++ dnpc_map.virt = (unsigned long)ioremap_nocache(dnpc_map.phys, dnpc_map.size);
+
+- dnpc_map_flash(dnpc_map.map_priv_2, dnpc_map.size);
++ dnpc_map_flash(dnpc_map.phys, dnpc_map.size);
+
+- if (!dnpc_map.map_priv_1) {
++ if (!dnpc_map.virt) {
+ printk("Failed to ioremap_nocache\n");
+ return -EIO;
+ }
++ simple_map_init(&dnpc_map);
+
+- printk("FLASH virtual address: 0x%lx\n", dnpc_map.map_priv_1);
++ printk("FLASH virtual address: 0x%lx\n", dnpc_map.virt);
+
+ mymtd = do_map_probe("jedec_probe", &dnpc_map);
+
+@@ -475,11 +430,11 @@
+ mymtd->erasesize = 0x10000;
+
+ if (!mymtd) {
+- iounmap((void *)dnpc_map.map_priv_1);
++ iounmap((void *)dnpc_map.virt);
+ return -ENXIO;
+ }
+
+- mymtd->module = THIS_MODULE;
++ mymtd->owner = THIS_MODULE;
+
+ /*
+ ** Supply pointers to lowlvl_parts[] array to add_mtd_partitions()
+@@ -525,10 +480,10 @@
+ del_mtd_partitions(mymtd);
+ map_destroy(mymtd);
+ }
+- if (dnpc_map.map_priv_1) {
+- iounmap((void *)dnpc_map.map_priv_1);
++ if (dnpc_map.virt) {
++ iounmap((void *)dnpc_map.virt);
+ dnpc_unmap_flash();
+- dnpc_map.map_priv_1 = 0;
++ dnpc_map.virt = 0;
+ }
+ }
+
+diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/ebony.c linux/drivers/mtd/maps/ebony.c
+--- linux-mips-2.4.27/drivers/mtd/maps/ebony.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/mtd/maps/ebony.c 2004-11-19 10:25:11.909198272 +0100
+@@ -0,0 +1,164 @@
++/*
++ * $Id$
++ *
++ * Mapping for Ebony user flash
++ *
++ * Matt Porter <mporter@mvista.com>
++ *
++ * Copyright 2002 MontaVista Software Inc.
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version.
++ */
++
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/map.h>
++#include <linux/mtd/partitions.h>
++#include <linux/config.h>
++#include <asm/io.h>
++#include <asm/ibm440.h>
++#include <platforms/ebony.h>
++
++static struct mtd_info *flash;
++
++static struct map_info ebony_small_map = {
++ .name = "Ebony small flash",
++ .size = EBONY_SMALL_FLASH_SIZE,
++ .buswidth = 1,
++};
++
++static struct map_info ebony_large_map = {
++ .name = "Ebony large flash",
++ .size = EBONY_LARGE_FLASH_SIZE,
++ .buswidth = 1,
++};
++
++static struct mtd_partition ebony_small_partitions[] = {
++ {
++ .name = "OpenBIOS",
++ .offset = 0x0,
++ .size = 0x80000,
++ }
++};
++
++static struct mtd_partition ebony_large_partitions[] = {
++ {
++ .name = "fs",
++ .offset = 0,
++ .size = 0x380000,
++ },
++ {
++ .name = "firmware",
++ .offset = 0x380000,
++ .size = 0x80000,
++ }
++};
++
++int __init init_ebony(void)
++{
++ u8 fpga0_reg;
++ unsigned long fpga0_adr;
++ unsigned long long small_flash_base, large_flash_base;
++
++ fpga0_adr = ioremap64(EBONY_FPGA_ADDR, 16);
++ if (!fpga0_adr)
++ return -ENOMEM;
++
++ fpga0_reg = readb(fpga0_adr);
++ iounmap64(fpga0_adr);
++
++ if (EBONY_BOOT_SMALL_FLASH(fpga0_reg) &&
++ !EBONY_FLASH_SEL(fpga0_reg))
++ small_flash_base = EBONY_SMALL_FLASH_HIGH2;
++ else if (EBONY_BOOT_SMALL_FLASH(fpga0_reg) &&
++ EBONY_FLASH_SEL(fpga0_reg))
++ small_flash_base = EBONY_SMALL_FLASH_HIGH1;
++ else if (!EBONY_BOOT_SMALL_FLASH(fpga0_reg) &&
++ !EBONY_FLASH_SEL(fpga0_reg))
++ small_flash_base = EBONY_SMALL_FLASH_LOW2;
++ else
++ small_flash_base = EBONY_SMALL_FLASH_LOW1;
++
++ if (EBONY_BOOT_SMALL_FLASH(fpga0_reg) &&
++ !EBONY_ONBRD_FLASH_EN(fpga0_reg))
++ large_flash_base = EBONY_LARGE_FLASH_LOW;
++ else
++ large_flash_base = EBONY_LARGE_FLASH_HIGH;
++
++ ebony_small_map.phys = small_flash_base;
++ ebony_small_map.virt =
++ (unsigned long)ioremap64(small_flash_base,
++ ebony_small_map.size);
++
++ if (!ebony_small_map.virt) {
++ printk("Failed to ioremap flash\n");
++ return -EIO;
++ }
++
++ simple_map_init(&ebony_small_map);
++
++ flash = do_map_probe("map_rom", &ebony_small_map);
++ if (flash) {
++ flash->owner = THIS_MODULE;
++ add_mtd_partitions(flash, ebony_small_partitions,
++ ARRAY_SIZE(ebony_small_partitions));
++ } else {
++ printk("map probe failed for flash\n");
++ return -ENXIO;
++ }
++
++ ebony_large_map.phys = large_flash_base;
++ ebony_large_map.virt =
++ (unsigned long)ioremap64(large_flash_base,
++ ebony_large_map.size);
++
++ if (!ebony_large_map.virt) {
++ printk("Failed to ioremap flash\n");
++ return -EIO;
++ }
++
++ simple_map_init(&ebony_large_map);
++
++ flash = do_map_probe("cfi_probe", &ebony_large_map);
++ if (flash) {
++ flash->owner = THIS_MODULE;
++ add_mtd_partitions(flash, ebony_large_partitions,
++ ARRAY_SIZE(ebony_large_partitions));
++ } else {
++ printk("map probe failed for flash\n");
++ return -ENXIO;
++ }
++
++ return 0;
++}
++
++static void __exit cleanup_ebony(void)
++{
++ if (flash) {
++ del_mtd_partitions(flash);
++ map_destroy(flash);
++ }
++
++ if (ebony_small_map.virt) {
++ iounmap((void *)ebony_small_map.virt);
++ ebony_small_map.virt = 0;
++ }
++
++ if (ebony_large_map.virt) {
++ iounmap((void *)ebony_large_map.virt);
++ ebony_large_map.virt = 0;
++ }
++}
++
++module_init(init_ebony);
++module_exit(cleanup_ebony);
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Matt Porter <mporter@mvista.com>");
++MODULE_DESCRIPTION("MTD map and partitions for IBM 440GP Ebony boards");
+diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/edb7312.c linux/drivers/mtd/maps/edb7312.c
+--- linux-mips-2.4.27/drivers/mtd/maps/edb7312.c 2003-02-26 01:53:49.000000000 +0100
++++ linux/drivers/mtd/maps/edb7312.c 2004-11-19 10:25:11.910198120 +0100
+@@ -1,5 +1,5 @@
+ /*
+- * $Id$
++ * $Id$
+ *
+ * Handle mapping of the NOR flash on Cogent EDB7312 boards
+ *
+@@ -13,6 +13,7 @@
+ #include <linux/module.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
+ #include <asm/io.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+@@ -35,61 +36,11 @@
+
+ static struct mtd_info *mymtd;
+
+-__u8 edb7312nor_read8(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readb(map->map_priv_1 + ofs);
+-}
+-
+-__u16 edb7312nor_read16(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readw(map->map_priv_1 + ofs);
+-}
+-
+-__u32 edb7312nor_read32(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readl(map->map_priv_1 + ofs);
+-}
+-
+-void edb7312nor_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+-{
+- memcpy_fromio(to, map->map_priv_1 + from, len);
+-}
+-
+-void edb7312nor_write8(struct map_info *map, __u8 d, unsigned long adr)
+-{
+- __raw_writeb(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-void edb7312nor_write16(struct map_info *map, __u16 d, unsigned long adr)
+-{
+- __raw_writew(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-void edb7312nor_write32(struct map_info *map, __u32 d, unsigned long adr)
+-{
+- __raw_writel(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-void edb7312nor_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+-{
+- memcpy_toio(map->map_priv_1 + to, from, len);
+-}
+-
+ struct map_info edb7312nor_map = {
+- name: "NOR flash on EDB7312",
+- size: WINDOW_SIZE,
+- buswidth: BUSWIDTH,
+- read8: edb7312nor_read8,
+- read16: edb7312nor_read16,
+- read32: edb7312nor_read32,
+- copy_from: edb7312nor_copy_from,
+- write8: edb7312nor_write8,
+- write16: edb7312nor_write16,
+- write32: edb7312nor_write32,
+- copy_to: edb7312nor_copy_to
++ .name = "NOR flash on EDB7312",
++ .size = WINDOW_SIZE,
++ .buswidth = BUSWIDTH,
++ .phys = WINDOW_ADDR,
+ };
+
+ #ifdef CONFIG_MTD_PARTITIONS
+@@ -100,29 +51,23 @@
+ static struct mtd_partition static_partitions[3] =
+ {
+ {
+- name: "ARMboot",
+- size: 0x40000,
+- offset: 0
++ .name = "ARMboot",
++ .size = 0x40000,
++ .offset = 0
+ },
+ {
+- name: "Kernel",
+- size: 0x200000,
+- offset: 0x40000
++ .name = "Kernel",
++ .size = 0x200000,
++ .offset = 0x40000
+ },
+ {
+- name: "RootFS",
+- size: 0xDC0000,
+- offset: 0x240000
++ .name = "RootFS",
++ .size = 0xDC0000,
++ .offset = 0x240000
+ },
+ };
+
+-#define NB_OF(x) (sizeof (x) / sizeof (x[0]))
+-
+-#ifdef CONFIG_MTD_CMDLINE_PARTS
+-int parse_cmdline_partitions(struct mtd_info *master,
+- struct mtd_partition **pparts,
+- const char *mtd_id);
+-#endif
++static const char *probes[] = { "RedBoot", "cmdlinepart", NULL };
+
+ #endif
+
+@@ -137,32 +82,33 @@
+
+ printk(KERN_NOTICE MSG_PREFIX "0x%08x at 0x%08x\n",
+ WINDOW_SIZE, WINDOW_ADDR);
+- edb7312nor_map.map_priv_1 = (unsigned long)
++ edb7312nor_map.virt = (unsigned long)
+ ioremap(WINDOW_ADDR, WINDOW_SIZE);
+
+- if (!edb7312nor_map.map_priv_1) {
++ if (!edb7312nor_map.virt) {
+ printk(MSG_PREFIX "failed to ioremap\n");
+ return -EIO;
+ }
+
++ simple_map_init(&edb7312nor_map);
++
+ mymtd = 0;
+ type = rom_probe_types;
+ for(; !mymtd && *type; type++) {
+ mymtd = do_map_probe(*type, &edb7312nor_map);
+ }
+ if (mymtd) {
+- mymtd->module = THIS_MODULE;
++ mymtd->owner = THIS_MODULE;
+
+ #ifdef CONFIG_MTD_PARTITIONS
+-#ifdef CONFIG_MTD_CMDLINE_PARTS
+- mtd_parts_nb = parse_cmdline_partitions(mymtd, &mtd_parts, MTDID);
++ mtd_parts_nb = parse_mtd_partitions(mymtd, probes, &mtd_parts, MTDID);
+ if (mtd_parts_nb > 0)
+- part_type = "command line";
+-#endif
++ part_type = "detected";
++
+ if (mtd_parts_nb == 0)
+ {
+ mtd_parts = static_partitions;
+- mtd_parts_nb = NB_OF(static_partitions);
++ mtd_parts_nb = ARRAY_SIZE(static_partitions);
+ part_type = "static";
+ }
+ #endif
+@@ -178,7 +124,7 @@
+ return 0;
+ }
+
+- iounmap((void *)edb7312nor_map.map_priv_1);
++ iounmap((void *)edb7312nor_map.virt);
+ return -ENXIO;
+ }
+
+@@ -188,9 +134,9 @@
+ del_mtd_device(mymtd);
+ map_destroy(mymtd);
+ }
+- if (edb7312nor_map.map_priv_1) {
+- iounmap((void *)edb7312nor_map.map_priv_1);
+- edb7312nor_map.map_priv_1 = 0;
++ if (edb7312nor_map.virt) {
++ iounmap((void *)edb7312nor_map.virt);
++ edb7312nor_map.virt = 0;
+ }
+ }
+
+diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/elan-104nc.c linux/drivers/mtd/maps/elan-104nc.c
+--- linux-mips-2.4.27/drivers/mtd/maps/elan-104nc.c 2002-06-27 00:35:50.000000000 +0200
++++ linux/drivers/mtd/maps/elan-104nc.c 2004-11-19 10:25:11.912197816 +0100
+@@ -16,7 +16,7 @@
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+
+- $Id$
++ $Id$
+
+ The ELAN-104NC has up to 8 Mibyte of Intel StrataFlash (28F320/28F640) in x16
+ mode. This drivers uses the CFI probe and Intel Extended Command Set drivers.
+@@ -40,6 +40,7 @@
+ #include <asm/io.h>
+
+ #include <linux/mtd/map.h>
++#include <linux/mtd/mtd.h>
+ #include <linux/mtd/partitions.h>
+
+ #define WINDOW_START 0xb0000
+@@ -59,14 +60,14 @@
+ * single flash device into. If the size if zero we use up to the end of the
+ * device. */
+ static struct mtd_partition partition_info[]={
+- { name: "ELAN-104NC flash boot partition",
+- offset: 0,
+- size: 640*1024 },
+- { name: "ELAN-104NC flash partition 1",
+- offset: 640*1024,
+- size: 896*1024 },
+- { name: "ELAN-104NC flash partition 2",
+- offset: (640+896)*1024 }
++ { .name = "ELAN-104NC flash boot partition",
++ .offset = 0,
++ .size = 640*1024 },
++ { .name = "ELAN-104NC flash partition 1",
++ .offset = 640*1024,
++ .size = 896*1024 },
++ { .name = "ELAN-104NC flash partition 2",
++ .offset = (640+896)*1024 }
+ };
+ #define NUM_PARTITIONS (sizeof(partition_info)/sizeof(partition_info[0]))
+
+@@ -195,19 +196,20 @@
+ }
+
+ static struct map_info elan_104nc_map = {
+- name: "ELAN-104NC flash",
+- size: 8*1024*1024, /* this must be set to a maximum possible amount
++ .name = "ELAN-104NC flash",
++ .phys = NO_XIP,
++ .size = 8*1024*1024, /* this must be set to a maximum possible amount
+ of flash so the cfi probe routines find all
+ the chips */
+- buswidth: 2,
+- read8: elan_104nc_read8,
+- read16: elan_104nc_read16,
+- read32: elan_104nc_read32,
+- copy_from: elan_104nc_copy_from,
+- write8: elan_104nc_write8,
+- write16: elan_104nc_write16,
+- write32: elan_104nc_write32,
+- copy_to: elan_104nc_copy_to
++ .buswidth = 2,
++ .read8 = elan_104nc_read8,
++ .read16 = elan_104nc_read16,
++ .read32 = elan_104nc_read32,
++ .copy_from = elan_104nc_copy_from,
++ .write8 = elan_104nc_write8,
++ .write16 = elan_104nc_write16,
++ .write32 = elan_104nc_write32,
++ .copy_to = elan_104nc_copy_to
+ };
+
+ /* MTD device for all of the flash. */
+@@ -221,20 +223,13 @@
+ }
+
+ iounmap((void *)iomapadr);
+- release_region(PAGE_IO,PAGE_IO_SIZE);
+ }
+
+ int __init init_elan_104nc(void)
+ {
+- /* Urg! We use I/O port 0x22 without request_region()ing it */
+- /*
+- if (check_region(PAGE_IO,PAGE_IO_SIZE) != 0) {
+- printk( KERN_ERR"%s: IO ports 0x%x-0x%x in use\n",
+- elan_104nc_map.name,
+- PAGE_IO, PAGE_IO+PAGE_IO_SIZE-1 );
+- return -EAGAIN;
+- }
+- */
++ /* Urg! We use I/O port 0x22 without request_region()ing it,
++ because it's already allocated to the PIC. */
++
+ iomapadr = (unsigned long)ioremap(WINDOW_START, WINDOW_LENGTH);
+ if (!iomapadr) {
+ printk( KERN_ERR"%s: failed to ioremap memory region\n",
+@@ -242,10 +237,6 @@
+ return -EIO;
+ }
+
+- /*
+- request_region( PAGE_IO, PAGE_IO_SIZE, "ELAN-104NC flash" );
+- */
+-
+ printk( KERN_INFO"%s: IO:0x%x-0x%x MEM:0x%x-0x%x\n",
+ elan_104nc_map.name,
+ PAGE_IO, PAGE_IO+PAGE_IO_SIZE-1,
+@@ -260,7 +251,7 @@
+ return -ENXIO;
+ }
+
+- all_mtd->module=THIS_MODULE;
++ all_mtd->owner = THIS_MODULE;
+
+ /* Create MTD devices for each partition. */
+ add_mtd_partitions( all_mtd, partition_info, NUM_PARTITIONS );
+diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/epxa10db-flash.c linux/drivers/mtd/maps/epxa10db-flash.c
+--- linux-mips-2.4.27/drivers/mtd/maps/epxa10db-flash.c 2003-02-26 01:53:49.000000000 +0100
++++ linux/drivers/mtd/maps/epxa10db-flash.c 2004-11-19 10:25:11.913197664 +0100
+@@ -5,7 +5,7 @@
+ * Copyright (C) 2001 Altera Corporation
+ * Copyright (C) 2001 Red Hat, Inc.
+ *
+- * $Id$
++ * $Id$
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -26,6 +26,7 @@
+ #include <linux/module.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
+ #include <asm/io.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+@@ -43,87 +44,38 @@
+
+ static struct mtd_info *mymtd;
+
+-extern int parse_redboot_partitions(struct mtd_info *, struct mtd_partition **);
+ static int epxa_default_partitions(struct mtd_info *master, struct mtd_partition **pparts);
+
+-static __u8 epxa_read8(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readb(map->map_priv_1 + ofs);
+-}
+-
+-static __u16 epxa_read16(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readw(map->map_priv_1 + ofs);
+-}
+-
+-static __u32 epxa_read32(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readl(map->map_priv_1 + ofs);
+-}
+-
+-static void epxa_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+-{
+- memcpy_fromio(to, (void *)(map->map_priv_1 + from), len);
+-}
+-
+-static void epxa_write8(struct map_info *map, __u8 d, unsigned long adr)
+-{
+- __raw_writeb(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-static void epxa_write16(struct map_info *map, __u16 d, unsigned long adr)
+-{
+- __raw_writew(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-static void epxa_write32(struct map_info *map, __u32 d, unsigned long adr)
+-{
+- __raw_writel(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-static void epxa_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+-{
+- memcpy_toio((void *)(map->map_priv_1 + to), from, len);
+-}
+-
+-
+
+ static struct map_info epxa_map = {
+- name: "EPXA flash",
+- size: FLASH_SIZE,
+- buswidth: 2,
+- read8: epxa_read8,
+- read16: epxa_read16,
+- read32: epxa_read32,
+- copy_from: epxa_copy_from,
+- write8: epxa_write8,
+- write16: epxa_write16,
+- write32: epxa_write32,
+- copy_to: epxa_copy_to
++ .name = "EPXA flash",
++ .size = FLASH_SIZE,
++ .buswidth = 2,
++ .phys = FLASH_START,
+ };
+
++static const char *probes[] = { "RedBoot", "afs", NULL };
+
+ static int __init epxa_mtd_init(void)
+ {
+ int i;
+
+- printk(KERN_NOTICE "%s flash device: %x at %x\n", BOARD_NAME, FLASH_SIZE, FLASH_START);
+- epxa_map.map_priv_1 = (unsigned long)ioremap(FLASH_START, FLASH_SIZE);
+- if (!epxa_map.map_priv_1) {
++ printk(KERN_NOTICE "%s flash device: 0x%x at 0x%x\n", BOARD_NAME, FLASH_SIZE, FLASH_START);
++
++ epxa_map.virt = (unsigned long)ioremap(FLASH_START, FLASH_SIZE);
++ if (!epxa_map.virt) {
+ printk("Failed to ioremap %s flash\n",BOARD_NAME);
+ return -EIO;
+ }
++ simple_map_init(&epxa_map);
+
+ mymtd = do_map_probe("cfi_probe", &epxa_map);
+ if (!mymtd) {
+- iounmap((void *)epxa_map.map_priv_1);
++ iounmap((void *)epxa_map.virt);
+ return -ENXIO;
+ }
+
+- mymtd->module = THIS_MODULE;
++ mymtd->owner = THIS_MODULE;
+
+ /* Unlock the flash device. */
+ if(mymtd->unlock){
+@@ -135,23 +87,14 @@
+ }
+ }
+
+-#ifdef CONFIG_MTD_REDBOOT_PARTS
+- nr_parts = parse_redboot_partitions(mymtd, &parts);
+-
+- if (nr_parts > 0) {
+- add_mtd_partitions(mymtd, parts, nr_parts);
+- return 0;
+- }
+-#endif
+-#ifdef CONFIG_MTD_AFS_PARTS
+- nr_parts = parse_afs_partitions(mymtd, &parts);
++#ifdef CONFIG_MTD_PARTITIONS
++ nr_parts = parse_mtd_partitions(mymtd, probes, &parts, 0);
+
+ if (nr_parts > 0) {
+ add_mtd_partitions(mymtd, parts, nr_parts);
+ return 0;
+ }
+ #endif
+-
+ /* No recognised partitioning schemes found - use defaults */
+ nr_parts = epxa_default_partitions(mymtd, &parts);
+ if (nr_parts > 0) {
+@@ -173,9 +116,9 @@
+ del_mtd_device(mymtd);
+ map_destroy(mymtd);
+ }
+- if (epxa_map.map_priv_1) {
+- iounmap((void *)epxa_map.map_priv_1);
+- epxa_map.map_priv_1 = 0;
++ if (epxa_map.virt) {
++ iounmap((void *)epxa_map.virt);
++ epxa_map.virt = 0;
+ }
+ }
+
+diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/fortunet.c linux/drivers/mtd/maps/fortunet.c
+--- linux-mips-2.4.27/drivers/mtd/maps/fortunet.c 2003-02-26 01:53:49.000000000 +0100
++++ linux/drivers/mtd/maps/fortunet.c 2004-11-19 10:25:11.915197360 +0100
+@@ -1,11 +1,12 @@
+ /* fortunet.c memory map
+ *
+- * $Id$
++ * $Id$
+ */
+
+ #include <linux/module.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
+ #include <asm/io.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+@@ -23,7 +24,7 @@
+
+ struct map_region
+ {
+- int window_addr_phyical;
++ int window_addr_physical;
+ int altbuswidth;
+ struct map_info map_info;
+ struct mtd_info *mymtd;
+@@ -37,57 +38,10 @@
+ static int map_regions_parts[MAX_NUM_REGIONS] = {0,0,0,0};
+
+
+-__u8 fortunet_read8(struct map_info *map, unsigned long ofs)
+-{
+- return *(__u8 *)(map->map_priv_1 + ofs);
+-}
+-
+-__u16 fortunet_read16(struct map_info *map, unsigned long ofs)
+-{
+- return *(__u16 *)(map->map_priv_1 + ofs);
+-}
+-
+-__u32 fortunet_read32(struct map_info *map, unsigned long ofs)
+-{
+- return *(__u32 *)(map->map_priv_1 + ofs);
+-}
+-
+-void fortunet_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+-{
+- memcpy(to, (void *)(map->map_priv_1 + from), len);
+-}
+-
+-void fortunet_write8(struct map_info *map, __u8 d, unsigned long adr)
+-{
+- *(__u8 *)(map->map_priv_1 + adr) = d;
+-}
+-
+-void fortunet_write16(struct map_info *map, __u16 d, unsigned long adr)
+-{
+- *(__u16 *)(map->map_priv_1 + adr) = d;
+-}
+-
+-void fortunet_write32(struct map_info *map, __u32 d, unsigned long adr)
+-{
+- *(__u32 *)(map->map_priv_1 + adr) = d;
+-}
+-
+-void fortunet_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+-{
+- memcpy((void *)(map->map_priv_1 + to), from, len);
+-}
+
+ struct map_info default_map = {
+- size: DEF_WINDOW_SIZE,
+- buswidth: 4,
+- read8: fortunet_read8,
+- read16: fortunet_read16,
+- read32: fortunet_read32,
+- copy_from: fortunet_copy_from,
+- write8: fortunet_write8,
+- write16: fortunet_write16,
+- write32: fortunet_write32,
+- copy_to: fortunet_copy_to
++ .size = DEF_WINDOW_SIZE,
++ .buswidth = 4,
+ };
+
+ static char * __init get_string_option(char *dest,int dest_size,char *sor)
+@@ -147,7 +101,7 @@
+ get_options (get_string_option(string,sizeof(string),line),6,params);
+ if(params[0]<1)
+ {
+- printk(MTD_FORTUNET_PK "Bad paramters for MTD Region "
++ printk(MTD_FORTUNET_PK "Bad parameters for MTD Region "
+ " name,region-number[,base,size,buswidth,altbuswidth]\n");
+ return 1;
+ }
+@@ -161,14 +115,14 @@
+ memcpy(&map_regions[params[1]].map_info,
+ &default_map,sizeof(map_regions[params[1]].map_info));
+ map_regions_set[params[1]] = 1;
+- map_regions[params[1]].window_addr_phyical = DEF_WINDOW_ADDR_PHY;
++ map_regions[params[1]].window_addr_physical = DEF_WINDOW_ADDR_PHY;
+ map_regions[params[1]].altbuswidth = 2;
+ map_regions[params[1]].mymtd = NULL;
+ map_regions[params[1]].map_info.name = map_regions[params[1]].map_name;
+ strcpy(map_regions[params[1]].map_info.name,string);
+ if(params[0]>1)
+ {
+- map_regions[params[1]].window_addr_phyical = params[2];
++ map_regions[params[1]].window_addr_physical = params[2];
+ }
+ if(params[0]>2)
+ {
+@@ -185,14 +139,14 @@
+ return 1;
+ }
+
+-static int __init MTD_New_Partion(char *line)
++static int __init MTD_New_Partition(char *line)
+ {
+ char string[MAX_NAME_SIZE];
+ int params[4];
+ get_options (get_string_option(string,sizeof(string),line),4,params);
+ if(params[0]<3)
+ {
+- printk(MTD_FORTUNET_PK "Bad paramters for MTD Partion "
++ printk(MTD_FORTUNET_PK "Bad parameters for MTD Partition "
+ " name,region-number,size,offset\n");
+ return 1;
+ }
+@@ -204,7 +158,7 @@
+ }
+ if(map_regions_parts[params[1]]>=MAX_NUM_PARTITIONS)
+ {
+- printk(MTD_FORTUNET_PK "Out of space for partion in this region\n");
++ printk(MTD_FORTUNET_PK "Out of space for partition in this region\n");
+ return 1;
+ }
+ map_regions[params[1]].parts[map_regions_parts[params[1]]].name =
+@@ -220,7 +174,10 @@
+ }
+
+ __setup("MTD_Region=", MTD_New_Region);
+-__setup("MTD_Partion=", MTD_New_Partion);
++__setup("MTD_Partition=", MTD_New_Partition);
++
++/* Backwards-spelling-compatibility */
++__setup("MTD_Partion=", MTD_New_Partition);
+
+ int __init init_fortunet(void)
+ {
+@@ -229,13 +186,13 @@
+ {
+ if(map_regions_parts[ix]&&(!map_regions_set[ix]))
+ {
+- printk(MTD_FORTUNET_PK "Region %d is not setup (Seting to default)\n",
++ printk(MTD_FORTUNET_PK "Region %d is not setup (Setting to default)\n",
+ ix);
+ memset(&map_regions[ix],0,sizeof(map_regions[ix]));
+ memcpy(&map_regions[ix].map_info,&default_map,
+ sizeof(map_regions[ix].map_info));
+ map_regions_set[ix] = 1;
+- map_regions[ix].window_addr_phyical = DEF_WINDOW_ADDR_PHY;
++ map_regions[ix].window_addr_physical = DEF_WINDOW_ADDR_PHY;
+ map_regions[ix].altbuswidth = 2;
+ map_regions[ix].mymtd = NULL;
+ map_regions[ix].map_info.name = map_regions[ix].map_name;
+@@ -244,30 +201,35 @@
+ if(map_regions_set[ix])
+ {
+ iy++;
+- printk(KERN_NOTICE MTD_FORTUNET_PK "%s flash device at phyicaly "
++ printk(KERN_NOTICE MTD_FORTUNET_PK "%s flash device at physically "
+ " address %x size %x\n",
+ map_regions[ix].map_info.name,
+- map_regions[ix].window_addr_phyical,
++ map_regions[ix].window_addr_physical,
+ map_regions[ix].map_info.size);
+- map_regions[ix].map_info.map_priv_1 =
++
++ map_regions[ix].map_info.phys = map_regions[ix].window_addr_physical,
++
++ map_regions[ix].map_info.virt =
+ (int)ioremap_nocache(
+- map_regions[ix].window_addr_phyical,
++ map_regions[ix].window_addr_physical,
+ map_regions[ix].map_info.size);
+- if(!map_regions[ix].map_info.map_priv_1)
++ if(!map_regions[ix].map_info.virt)
+ {
+ printk(MTD_FORTUNET_PK "%s flash failed to ioremap!\n",
+ map_regions[ix].map_info.name);
+ return -ENXIO;
+ }
+- printk(KERN_NOTICE MTD_FORTUNET_PK "%s flash is veritualy at: %x\n",
++ simple_map_init(&map_regions[ix].map_info);
++
++ printk(KERN_NOTICE MTD_FORTUNET_PK "%s flash is virtually at: %x\n",
+ map_regions[ix].map_info.name,
+- map_regions[ix].map_info.map_priv_1);
++ map_regions[ix].map_info.virt);
+ map_regions[ix].mymtd = do_map_probe("cfi_probe",
+ &map_regions[ix].map_info);
+ if((!map_regions[ix].mymtd)&&(
+ map_regions[ix].altbuswidth!=map_regions[ix].map_info.buswidth))
+ {
+- printk(KERN_NOTICE MTD_FORTUNET_PK "Trying alternet buswidth "
++ printk(KERN_NOTICE MTD_FORTUNET_PK "Trying alternate buswidth "
+ "for %s flash.\n",
+ map_regions[ix].map_info.name);
+ map_regions[ix].map_info.buswidth =
+@@ -275,7 +237,7 @@
+ map_regions[ix].mymtd = do_map_probe("cfi_probe",
+ &map_regions[ix].map_info);
+ }
+- map_regions[ix].mymtd->module = THIS_MODULE;
++ map_regions[ix].mymtd->owner = THIS_MODULE;
+ add_mtd_partitions(map_regions[ix].mymtd,
+ map_regions[ix].parts,map_regions_parts[ix]);
+ }
+@@ -297,7 +259,7 @@
+ del_mtd_partitions( map_regions[ix].mymtd );
+ map_destroy( map_regions[ix].mymtd );
+ }
+- iounmap((void *)map_regions[ix].map_info.map_priv_1);
++ iounmap((void *)map_regions[ix].map_info.virt);
+ }
+ }
+ }
+diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/h720x-flash.c linux/drivers/mtd/maps/h720x-flash.c
+--- linux-mips-2.4.27/drivers/mtd/maps/h720x-flash.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/mtd/maps/h720x-flash.c 2004-11-19 10:25:11.917197056 +0100
+@@ -0,0 +1,142 @@
++/*
++ * Flash memory access on Hynix GMS30C7201/HMS30C7202 based
++ * evaluation boards
++ *
++ * (C) 2002 Jungjun Kim <jungjun.kim@hynix.com>
++ * 2003 Thomas Gleixner <tglx@linutronix.de>
++*/
++
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/errno.h>
++#include <linux/slab.h>
++
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/map.h>
++#include <linux/mtd/partitions.h>
++#include <asm/hardware.h>
++#include <asm/io.h>
++
++static struct mtd_info *mymtd;
++
++static struct map_info h720x_map = {
++ .name = "H720X",
++ .buswidth = 4,
++ .size = FLASH_SIZE,
++ .phys = FLASH_PHYS,
++};
++
++static struct mtd_partition h720x_partitions[] = {
++ {
++ .name = "ArMon",
++ .size = 0x00080000,
++ .offset = 0,
++ .mask_flags = MTD_WRITEABLE
++ },{
++ .name = "Env",
++ .size = 0x00040000,
++ .offset = 0x00080000,
++ .mask_flags = MTD_WRITEABLE
++ },{
++ .name = "Kernel",
++ .size = 0x00180000,
++ .offset = 0x000c0000,
++ .mask_flags = MTD_WRITEABLE
++ },{
++ .name = "Ramdisk",
++ .size = 0x00400000,
++ .offset = 0x00240000,
++ .mask_flags = MTD_WRITEABLE
++ },{
++ .name = "jffs2",
++ .size = MTDPART_SIZ_FULL,
++ .offset = MTDPART_OFS_APPEND
++ }
++};
++
++#define NUM_PARTITIONS (sizeof(h720x_partitions)/sizeof(h720x_partitions[0]))
++
++static int nr_mtd_parts;
++static struct mtd_partition *mtd_parts;
++static const char *probes[] = { "cmdlinepart", NULL };
++
++/*
++ * Initialize FLASH support
++ */
++int __init h720x_mtd_init(void)
++{
++
++ char *part_type = NULL;
++
++ h720x_map.virt = (unsigned long)ioremap(FLASH_PHYS, FLASH_SIZE);
++
++ if (!h720x_map.virt) {
++ printk(KERN_ERR "H720x-MTD: ioremap failed\n");
++ return -EIO;
++ }
++
++ simple_map_init(&h720x_map);
++
++ // Probe for flash buswidth 4
++ printk (KERN_INFO "H720x-MTD probing 32bit FLASH\n");
++ mymtd = do_map_probe("cfi_probe", &h720x_map);
++ if (!mymtd) {
++ printk (KERN_INFO "H720x-MTD probing 16bit FLASH\n");
++ // Probe for buswidth 2
++ h720x_map.buswidth = 2;
++ mymtd = do_map_probe("cfi_probe", &h720x_map);
++ }
++
++ if (mymtd) {
++ mymtd->owner = THIS_MODULE;
++
++#ifdef CONFIG_MTD_PARTITIONS
++ nr_mtd_parts = parse_mtd_partitions(mymtd, probes, &mtd_parts, 0);
++ if (nr_mtd_parts > 0)
++ part_type = "command line";
++#endif
++ if (nr_mtd_parts <= 0) {
++ mtd_parts = h720x_partitions;
++ nr_mtd_parts = NUM_PARTITIONS;
++ part_type = "builtin";
++ }
++ printk(KERN_INFO "Using %s partition table\n", part_type);
++ add_mtd_partitions(mymtd, mtd_parts, nr_mtd_parts);
++ return 0;
++ }
++
++ iounmap((void *)h720x_map.virt);
++ return -ENXIO;
++}
++
++/*
++ * Cleanup
++ */
++static void __exit h720x_mtd_cleanup(void)
++{
++
++ if (mymtd) {
++ del_mtd_partitions(mymtd);
++ map_destroy(mymtd);
++ }
++
++ /* Free partition info, if commandline partition was used */
++ if (mtd_parts && (mtd_parts != h720x_partitions))
++ kfree (mtd_parts);
++
++ if (h720x_map.virt) {
++ iounmap((void *)h720x_map.virt);
++ h720x_map.virt = 0;
++ }
++}
++
++
++module_init(h720x_mtd_init);
++module_exit(h720x_mtd_cleanup);
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Thomas Gleixner <tglx@linutronix.de>");
++MODULE_DESCRIPTION("MTD map driver for Hynix evaluation boards");
+diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/ichxrom.c linux/drivers/mtd/maps/ichxrom.c
+--- linux-mips-2.4.27/drivers/mtd/maps/ichxrom.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/mtd/maps/ichxrom.c 2004-11-19 10:25:11.918196904 +0100
+@@ -0,0 +1,380 @@
++/*
++ * ichxrom.c
++ *
++ * Normal mappings of chips in physical memory
++ * $Id$
++ */
++
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <asm/io.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/map.h>
++#include <linux/config.h>
++#include <linux/pci.h>
++#include <linux/pci_ids.h>
++
++#define xstr(s) str(s)
++#define str(s) #s
++#define MOD_NAME xstr(KBUILD_BASENAME)
++
++#define MTD_DEV_NAME_LENGTH 16
++
++#define RESERVE_MEM_REGION 0
++
++#define ICHX_FWH_REGION_START 0xFF000000UL
++#define ICHX_FWH_REGION_SIZE 0x01000000UL
++#define BIOS_CNTL 0x4e
++#define FWH_DEC_EN1 0xE3
++#define FWH_DEC_EN2 0xF0
++#define FWH_SEL1 0xE8
++#define FWH_SEL2 0xEE
++
++struct ichxrom_map_info {
++ struct map_info map;
++ struct mtd_info *mtd;
++ unsigned long window_addr;
++ struct pci_dev *pdev;
++ struct resource window_rsrc;
++ struct resource rom_rsrc;
++ char mtd_name[MTD_DEV_NAME_LENGTH];
++};
++
++static inline unsigned long addr(struct map_info *map, unsigned long ofs)
++{
++ unsigned long offset;
++ offset = ((8*1024*1024) - map->size) + ofs;
++ if (offset >= (4*1024*1024)) {
++ offset += 0x400000;
++ }
++ return map->map_priv_1 + 0x400000 + offset;
++}
++
++static inline unsigned long dbg_addr(struct map_info *map, unsigned long addr)
++{
++ return addr - map->map_priv_1 + ICHX_FWH_REGION_START;
++}
++
++static __u8 ichxrom_read8(struct map_info *map, unsigned long ofs)
++{
++ return __raw_readb(addr(map, ofs));
++}
++
++static __u16 ichxrom_read16(struct map_info *map, unsigned long ofs)
++{
++ return __raw_readw(addr(map, ofs));
++}
++
++static __u32 ichxrom_read32(struct map_info *map, unsigned long ofs)
++{
++ return __raw_readl(addr(map, ofs));
++}
++
++static void ichxrom_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
++{
++ memcpy_fromio(to, addr(map, from), len);
++}
++
++static void ichxrom_write8(struct map_info *map, __u8 d, unsigned long ofs)
++{
++ __raw_writeb(d, addr(map,ofs));
++ mb();
++}
++
++static void ichxrom_write16(struct map_info *map, __u16 d, unsigned long ofs)
++{
++ __raw_writew(d, addr(map, ofs));
++ mb();
++}
++
++static void ichxrom_write32(struct map_info *map, __u32 d, unsigned long ofs)
++{
++ __raw_writel(d, addr(map, ofs));
++ mb();
++}
++
++static void ichxrom_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
++{
++ memcpy_toio(addr(map, to), from, len);
++}
++
++static struct ichxrom_map_info ichxrom_map = {
++ .map = {
++ .name = MOD_NAME,
++ .phys = NO_XIP,
++ .size = 0,
++ .buswidth = 1,
++ .read8 = ichxrom_read8,
++ .read16 = ichxrom_read16,
++ .read32 = ichxrom_read32,
++ .copy_from = ichxrom_copy_from,
++ .write8 = ichxrom_write8,
++ .write16 = ichxrom_write16,
++ .write32 = ichxrom_write32,
++ .copy_to = ichxrom_copy_to,
++ /* Firmware hubs only use vpp when being programmed
++ * in a factory setting. So in-place programming
++ * needs to use a different method.
++ */
++ },
++ /* remaining fields of structure are initialized to 0 */
++};
++
++enum fwh_lock_state {
++ FWH_DENY_WRITE = 1,
++ FWH_IMMUTABLE = 2,
++ FWH_DENY_READ = 4,
++};
++
++static void ichxrom_cleanup(struct ichxrom_map_info *info)
++{
++ u16 word;
++
++ /* Disable writes through the rom window */
++ pci_read_config_word(info->pdev, BIOS_CNTL, &word);
++ pci_write_config_word(info->pdev, BIOS_CNTL, word & ~1);
++
++ if (info->mtd) {
++ del_mtd_device(info->mtd);
++ map_destroy(info->mtd);
++ info->mtd = NULL;
++ info->map.virt = 0;
++ }
++ if (info->rom_rsrc.parent)
++ release_resource(&info->rom_rsrc);
++ if (info->window_rsrc.parent)
++ release_resource(&info->window_rsrc);
++
++ if (info->window_addr) {
++ iounmap((void *)(info->window_addr));
++ info->window_addr = 0;
++ }
++}
++
++
++static int ichxrom_set_lock_state(struct mtd_info *mtd, loff_t ofs, size_t len,
++ enum fwh_lock_state state)
++{
++ struct map_info *map = mtd->priv;
++ unsigned long start = ofs;
++ unsigned long end = start + len -1;
++
++ /* FIXME do I need to guard against concurrency here? */
++ /* round down to 64K boundaries */
++ start = start & ~0xFFFF;
++ end = end & ~0xFFFF;
++ while (start <= end) {
++ unsigned long ctrl_addr;
++ ctrl_addr = addr(map, start) - 0x400000 + 2;
++ writeb(state, ctrl_addr);
++ start = start + 0x10000;
++ }
++ return 0;
++}
++
++static int ichxrom_lock(struct mtd_info *mtd, loff_t ofs, size_t len)
++{
++ return ichxrom_set_lock_state(mtd, ofs, len, FWH_DENY_WRITE);
++}
++
++static int ichxrom_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
++{
++ return ichxrom_set_lock_state(mtd, ofs, len, 0);
++}
++
++static int __devinit ichxrom_init_one (struct pci_dev *pdev,
++ const struct pci_device_id *ent)
++{
++ u16 word;
++ struct ichxrom_map_info *info = &ichxrom_map;
++ unsigned long map_size;
++
++ /* For now I just handle the ichx and I assume there
++ * are not a lot of resources up at the top of the address
++ * space. It is possible to handle other devices in the
++ * top 16MB but it is very painful. Also since
++ * you can only really attach a FWH to an ICHX there
++ * a number of simplifications you can make.
++ *
++ * Also you can page firmware hubs if an 8MB window isn't enough
++ * but don't currently handle that case either.
++ */
++
++ info->pdev = pdev;
++
++ /*
++ * Try to reserve the window mem region. If this fails then
++ * it is likely due to the window being "reseved" by the BIOS.
++ */
++ info->window_rsrc.name = MOD_NAME;
++ info->window_rsrc.start = ICHX_FWH_REGION_START;
++ info->window_rsrc.end = ICHX_FWH_REGION_START + ICHX_FWH_REGION_SIZE - 1;
++ info->window_rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
++ if (request_resource(&iomem_resource, &info->window_rsrc)) {
++ info->window_rsrc.parent = NULL;
++ printk(KERN_ERR MOD_NAME
++ " %s(): Unable to register resource"
++ " 0x%.08lx-0x%.08lx - kernel bug?\n",
++ __func__,
++ info->window_rsrc.start, info->window_rsrc.end);
++ }
++
++ /* Enable writes through the rom window */
++ pci_read_config_word(pdev, BIOS_CNTL, &word);
++ if (!(word & 1) && (word & (1<<1))) {
++ /* The BIOS will generate an error if I enable
++ * this device, so don't even try.
++ */
++ printk(KERN_ERR MOD_NAME ": firmware access control, I can't enable writes\n");
++ goto failed;
++ }
++ pci_write_config_word(pdev, BIOS_CNTL, word | 1);
++
++
++ /* Map the firmware hub into my address space. */
++ /* Does this use too much virtual address space? */
++ info->window_addr = (unsigned long)ioremap(
++ ICHX_FWH_REGION_START, ICHX_FWH_REGION_SIZE);
++ if (!info->window_addr) {
++ printk(KERN_ERR "Failed to ioremap\n");
++ goto failed;
++ }
++
++ /* For now assume the firmware has setup all relevant firmware
++ * windows. We don't have enough information to handle this case
++ * intelligently.
++ */
++
++ /* FIXME select the firmware hub and enable a window to it. */
++
++ info->mtd = 0;
++ info->map.map_priv_1 = info->window_addr;
++
++ map_size = ICHX_FWH_REGION_SIZE;
++ while(!info->mtd && (map_size > 0)) {
++ info->map.size = map_size;
++ info->mtd = do_map_probe("jedec_probe", &ichxrom_map.map);
++ map_size -= 512*1024;
++ }
++ if (!info->mtd) {
++ goto failed;
++ }
++ /* I know I can only be a firmware hub here so put
++ * in the special lock and unlock routines.
++ */
++ info->mtd->lock = ichxrom_lock;
++ info->mtd->unlock = ichxrom_unlock;
++
++ info->mtd->owner = THIS_MODULE;
++ add_mtd_device(info->mtd);
++
++ if (info->window_rsrc.parent) {
++ /*
++ * Registering the MTD device in iomem may not be possible
++ * if there is a BIOS "reserved" and BUSY range. If this
++ * fails then continue anyway.
++ */
++ snprintf(info->mtd_name, MTD_DEV_NAME_LENGTH,
++ "mtd%d", info->mtd->index);
++
++ info->rom_rsrc.name = info->mtd_name;
++ info->rom_rsrc.start = ICHX_FWH_REGION_START
++ + ICHX_FWH_REGION_SIZE - map_size;
++ info->rom_rsrc.end = ICHX_FWH_REGION_START
++ + ICHX_FWH_REGION_SIZE;
++ info->rom_rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
++ if (request_resource(&info->window_rsrc, &info->rom_rsrc)) {
++ printk(KERN_ERR MOD_NAME
++ ": cannot reserve MTD resource\n");
++ info->rom_rsrc.parent = NULL;
++ }
++ }
++
++ return 0;
++
++ failed:
++ ichxrom_cleanup(info);
++ return -ENODEV;
++}
++
++
++static void __devexit ichxrom_remove_one (struct pci_dev *pdev)
++{
++ struct ichxrom_map_info *info = &ichxrom_map;
++ u16 word;
++
++ del_mtd_device(info->mtd);
++ map_destroy(info->mtd);
++ info->mtd = 0;
++ info->map.map_priv_1 = 0;
++
++ iounmap((void *)(info->window_addr));
++ info->window_addr = 0;
++
++ /* Disable writes through the rom window */
++ pci_read_config_word(pdev, BIOS_CNTL, &word);
++ pci_write_config_word(pdev, BIOS_CNTL, word & ~1);
++
++#if RESERVE_MEM_REGION
++ release_mem_region(ICHX_FWH_REGION_START, ICHX_FWH_REGION_SIZE);
++#endif
++}
++
++static struct pci_device_id ichxrom_pci_tbl[] __devinitdata = {
++ { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0,
++ PCI_ANY_ID, PCI_ANY_ID, },
++ { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_0,
++ PCI_ANY_ID, PCI_ANY_ID, },
++ { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_0,
++ PCI_ANY_ID, PCI_ANY_ID, },
++ { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_0,
++ PCI_ANY_ID, PCI_ANY_ID, },
++ { 0, },
++};
++
++MODULE_DEVICE_TABLE(pci, ichxrom_pci_tbl);
++
++#if 0
++static struct pci_driver ichxrom_driver = {
++ .name = MOD_NAME,
++ .id_table = ichxrom_pci_tbl,
++ .probe = ichxrom_init_one,
++ .remove = ichxrom_remove_one,
++};
++#endif
++
++static struct pci_dev *mydev;
++int __init init_ichxrom(void)
++{
++ struct pci_dev *pdev;
++ struct pci_device_id *id;
++ pdev = 0;
++ for(id = ichxrom_pci_tbl; id->vendor; id++) {
++ pdev = pci_find_device(id->vendor, id->device, 0);
++ if (pdev) {
++ break;
++ }
++ }
++ if (pdev) {
++ mydev = pdev;
++ return ichxrom_init_one(pdev, &ichxrom_pci_tbl[0]);
++ }
++ return -ENXIO;
++#if 0
++ return pci_module_init(&ichxrom_driver);
++#endif
++}
++
++static void __exit cleanup_ichxrom(void)
++{
++ ichxrom_remove_one(mydev);
++}
++
++module_init(init_ichxrom);
++module_exit(cleanup_ichxrom);
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Eric Biederman <ebiederman@lnxi.com>");
++MODULE_DESCRIPTION("MTD map driver for BIOS chips on the ICHX southbridge");
+diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/impa7.c linux/drivers/mtd/maps/impa7.c
+--- linux-mips-2.4.27/drivers/mtd/maps/impa7.c 2003-02-26 01:53:49.000000000 +0100
++++ linux/drivers/mtd/maps/impa7.c 2004-11-19 10:25:11.920196600 +0100
+@@ -1,5 +1,5 @@
+ /*
+- * $Id$
++ * $Id$
+ *
+ * Handle mapping of the NOR flash on implementa A7 boards
+ *
+@@ -13,6 +13,7 @@
+ #include <linux/module.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
+ #include <asm/io.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+@@ -37,75 +38,17 @@
+
+ static struct mtd_info *impa7_mtd[NUM_FLASHBANKS] = { 0 };
+
+-__u8 impa7_read8(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readb(map->map_priv_1 + ofs);
+-}
+-
+-__u16 impa7_read16(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readw(map->map_priv_1 + ofs);
+-}
+-
+-__u32 impa7_read32(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readl(map->map_priv_1 + ofs);
+-}
+-
+-void impa7_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+-{
+- memcpy_fromio(to, map->map_priv_1 + from, len);
+-}
+-
+-void impa7_write8(struct map_info *map, __u8 d, unsigned long adr)
+-{
+- __raw_writeb(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-void impa7_write16(struct map_info *map, __u16 d, unsigned long adr)
+-{
+- __raw_writew(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-void impa7_write32(struct map_info *map, __u32 d, unsigned long adr)
+-{
+- __raw_writel(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-void impa7_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+-{
+- memcpy_toio(map->map_priv_1 + to, from, len);
+-}
+
+ static struct map_info impa7_map[NUM_FLASHBANKS] = {
+ {
+- name: "impA7 NOR Flash Bank #0",
+- size: WINDOW_SIZE0,
+- buswidth: BUSWIDTH,
+- read8: impa7_read8,
+- read16: impa7_read16,
+- read32: impa7_read32,
+- copy_from: impa7_copy_from,
+- write8: impa7_write8,
+- write16: impa7_write16,
+- write32: impa7_write32,
+- copy_to: impa7_copy_to
++ .name = "impA7 NOR Flash Bank #0",
++ .size = WINDOW_SIZE0,
++ .buswidth = BUSWIDTH,
+ },
+ {
+- name: "impA7 NOR Flash Bank #1",
+- size: WINDOW_SIZE1,
+- buswidth: BUSWIDTH,
+- read8: impa7_read8,
+- read16: impa7_read16,
+- read32: impa7_read32,
+- copy_from: impa7_copy_from,
+- write8: impa7_write8,
+- write16: impa7_write16,
+- write32: impa7_write32,
+- copy_to: impa7_copy_to
++ .name = "impA7 NOR Flash Bank #1",
++ .size = WINDOW_SIZE1,
++ .buswidth = BUSWIDTH,
+ },
+ };
+
+@@ -117,24 +60,18 @@
+ static struct mtd_partition static_partitions[] =
+ {
+ {
+- name: "FileSystem",
+- size: 0x800000,
+- offset: 0x00000000
++ .name = "FileSystem",
++ .size = 0x800000,
++ .offset = 0x00000000
+ },
+ };
+
+-#define NB_OF(x) (sizeof (x) / sizeof (x[0]))
++static int mtd_parts_nb[NUM_FLASHBANKS];
++static struct mtd_partition *mtd_parts[NUM_FLASHBANKS];
+
+-#ifdef CONFIG_MTD_CMDLINE_PARTS
+-int parse_cmdline_partitions(struct mtd_info *master,
+- struct mtd_partition **pparts,
+- const char *mtd_id);
+ #endif
+
+-#endif
+-
+-static int mtd_parts_nb = 0;
+-static struct mtd_partition *mtd_parts = 0;
++static const char *probes[] = { "cmdlinepart", NULL };
+
+ int __init init_impa7(void)
+ {
+@@ -146,20 +83,21 @@
+ { WINDOW_ADDR0, WINDOW_SIZE0 },
+ { WINDOW_ADDR1, WINDOW_SIZE1 },
+ };
+- char mtdid[10];
+ int devicesfound = 0;
+
+ for(i=0; i<NUM_FLASHBANKS; i++)
+ {
+ printk(KERN_NOTICE MSG_PREFIX "probing 0x%08lx at 0x%08lx\n",
+ pt[i].size, pt[i].addr);
+- impa7_map[i].map_priv_1 = (unsigned long)
+- ioremap(pt[i].addr, pt[i].size);
+
+- if (!impa7_map[i].map_priv_1) {
++ impa7_map[i].phys = pt[i].addr;
++ impa7_map[i].virt = (unsigned long)
++ ioremap(pt[i].addr, pt[i].size);
++ if (!impa7_map[i].virt) {
+ printk(MSG_PREFIX "failed to ioremap\n");
+ return -EIO;
+ }
++ simple_map_init(&impa7_map[i]);
+
+ impa7_mtd[i] = 0;
+ type = rom_probe_types;
+@@ -167,43 +105,34 @@
+ impa7_mtd[i] = do_map_probe(*type, &impa7_map[i]);
+ }
+
+- if (impa7_mtd[i])
+- {
+- impa7_mtd[i]->module = THIS_MODULE;
+- add_mtd_device(impa7_mtd[i]);
++ if (impa7_mtd[i]) {
++ impa7_mtd[i]->owner = THIS_MODULE;
+ devicesfound++;
+ #ifdef CONFIG_MTD_PARTITIONS
+-#ifdef CONFIG_MTD_CMDLINE_PARTS
+- sprintf(mtdid, MTDID, i);
+- mtd_parts_nb = parse_cmdline_partitions(impa7_mtd[i],
+- &mtd_parts,
+- mtdid);
+- if (mtd_parts_nb > 0)
++ mtd_parts_nb[i] = parse_mtd_partitions(impa7_mtd[i],
++ probes,
++ &mtd_parts[i],
++ 0);
++ if (mtd_parts_nb[i] > 0) {
+ part_type = "command line";
+-#endif
+- if (mtd_parts_nb <= 0)
+- {
+- mtd_parts = static_partitions;
+- mtd_parts_nb = NB_OF(static_partitions);
++ } else {
++ mtd_parts[i] = static_partitions;
++ mtd_parts_nb[i] = ARRAY_SIZE(static_partitions);
+ part_type = "static";
+ }
+- if (mtd_parts_nb <= 0)
+- {
+- printk(KERN_NOTICE MSG_PREFIX
+- "no partition info available\n");
+- }
+- else
+- {
++
+ printk(KERN_NOTICE MSG_PREFIX
+ "using %s partition definition\n",
+ part_type);
+ add_mtd_partitions(impa7_mtd[i],
+- mtd_parts, mtd_parts_nb);
+- }
++ mtd_parts[i], mtd_parts_nb[i]);
++#else
++ add_mtd_device(impa7_mtd[i]);
++
+ #endif
+ }
+ else
+- iounmap((void *)impa7_map[i].map_priv_1);
++ iounmap((void *)impa7_map[i].virt);
+ }
+ return devicesfound == 0 ? -ENXIO : 0;
+ }
+@@ -211,17 +140,16 @@
+ static void __exit cleanup_impa7(void)
+ {
+ int i;
+- for (i=0; i<NUM_FLASHBANKS; i++)
+- {
+- if (impa7_mtd[i])
+- {
++ for (i=0; i<NUM_FLASHBANKS; i++) {
++ if (impa7_mtd[i]) {
++#ifdef CONFIG_MTD_PARTITIONS
++ del_mtd_partitions(impa7_mtd[i]);
++#else
+ del_mtd_device(impa7_mtd[i]);
++#endif
+ map_destroy(impa7_mtd[i]);
+- }
+- if (impa7_map[i].map_priv_1)
+- {
+- iounmap((void *)impa7_map[i].map_priv_1);
+- impa7_map[i].map_priv_1 = 0;
++ iounmap((void *)impa7_map[i].virt);
++ impa7_map[i].virt = 0;
+ }
+ }
+ }
+diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/integrator-flash-v24.c linux/drivers/mtd/maps/integrator-flash-v24.c
+--- linux-mips-2.4.27/drivers/mtd/maps/integrator-flash-v24.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/mtd/maps/integrator-flash-v24.c 2004-11-19 10:25:11.921196448 +0100
+@@ -0,0 +1,258 @@
++/*======================================================================
++
++ drivers/mtd/maps/armflash.c: ARM Flash Layout/Partitioning
++
++ Copyright (C) 2000 ARM Limited
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License as published by
++ the Free Software Foundation; either version 2 of the License, or
++ (at your option) any later version.
++
++ This program is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ GNU General Public License for more details.
++
++ You should have received a copy of the GNU General Public License
++ along with this program; if not, write to the Free Software
++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++
++ This is access code for flashes using ARM's flash partitioning
++ standards.
++
++ $Id$
++
++======================================================================*/
++
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/kernel.h>
++#include <linux/slab.h>
++#include <linux/ioport.h>
++#include <linux/init.h>
++
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/map.h>
++#include <linux/mtd/partitions.h>
++
++#include <asm/hardware.h>
++#include <asm/io.h>
++#include <asm/system.h>
++
++// board specific stuff - sorry, it should be in arch/arm/mach-*.
++#ifdef CONFIG_ARCH_INTEGRATOR
++
++#define FLASH_BASE INTEGRATOR_FLASH_BASE
++#define FLASH_SIZE INTEGRATOR_FLASH_SIZE
++
++#define FLASH_PART_SIZE 0x400000
++
++#define SC_CTRLC (IO_ADDRESS(INTEGRATOR_SC_BASE) + INTEGRATOR_SC_CTRLC_OFFSET)
++#define SC_CTRLS (IO_ADDRESS(INTEGRATOR_SC_BASE) + INTEGRATOR_SC_CTRLS_OFFSET)
++#define EBI_CSR1 (IO_ADDRESS(INTEGRATOR_EBI_BASE) + INTEGRATOR_EBI_CSR1_OFFSET)
++#define EBI_LOCK (IO_ADDRESS(INTEGRATOR_EBI_BASE) + INTEGRATOR_EBI_LOCK_OFFSET)
++
++/*
++ * Initialise the flash access systems:
++ * - Disable VPP
++ * - Assert WP
++ * - Set write enable bit in EBI reg
++ */
++static void armflash_flash_init(void)
++{
++ unsigned int tmp;
++
++ __raw_writel(INTEGRATOR_SC_CTRL_nFLVPPEN | INTEGRATOR_SC_CTRL_nFLWP, SC_CTRLC);
++
++ tmp = __raw_readl(EBI_CSR1) | INTEGRATOR_EBI_WRITE_ENABLE;
++ __raw_writel(tmp, EBI_CSR1);
++
++ if (!(__raw_readl(EBI_CSR1) & INTEGRATOR_EBI_WRITE_ENABLE)) {
++ __raw_writel(0xa05f, EBI_LOCK);
++ __raw_writel(tmp, EBI_CSR1);
++ __raw_writel(0, EBI_LOCK);
++ }
++}
++
++/*
++ * Shutdown the flash access systems:
++ * - Disable VPP
++ * - Assert WP
++ * - Clear write enable bit in EBI reg
++ */
++static void armflash_flash_exit(void)
++{
++ unsigned int tmp;
++
++ __raw_writel(INTEGRATOR_SC_CTRL_nFLVPPEN | INTEGRATOR_SC_CTRL_nFLWP, SC_CTRLC);
++
++ /*
++ * Clear the write enable bit in system controller EBI register.
++ */
++ tmp = __raw_readl(EBI_CSR1) & ~INTEGRATOR_EBI_WRITE_ENABLE;
++ __raw_writel(tmp, EBI_CSR1);
++
++ if (__raw_readl(EBI_CSR1) & INTEGRATOR_EBI_WRITE_ENABLE) {
++ __raw_writel(0xa05f, EBI_LOCK);
++ __raw_writel(tmp, EBI_CSR1);
++ __raw_writel(0, EBI_LOCK);
++ }
++}
++
++static void armflash_flash_wp(int on)
++{
++ unsigned int reg;
++
++ if (on)
++ reg = SC_CTRLC;
++ else
++ reg = SC_CTRLS;
++
++ __raw_writel(INTEGRATOR_SC_CTRL_nFLWP, reg);
++}
++
++static void armflash_set_vpp(struct map_info *map, int on)
++{
++ unsigned int reg;
++
++ if (on)
++ reg = SC_CTRLS;
++ else
++ reg = SC_CTRLC;
++
++ __raw_writel(INTEGRATOR_SC_CTRL_nFLVPPEN, reg);
++}
++#endif
++
++#ifdef CONFIG_ARCH_P720T
++
++#define FLASH_BASE (0x04000000)
++#define FLASH_SIZE (64*1024*1024)
++
++#define FLASH_PART_SIZE (4*1024*1024)
++#define FLASH_BLOCK_SIZE (128*1024)
++
++static void armflash_flash_init(void)
++{
++}
++
++static void armflash_flash_exit(void)
++{
++}
++
++static void armflash_flash_wp(int on)
++{
++}
++
++static void armflash_set_vpp(struct map_info *map, int on)
++{
++}
++#endif
++
++
++static struct map_info armflash_map =
++{
++ .name = "AFS",
++ .set_vpp = armflash_set_vpp,
++ .phys = FLASH_BASE,
++};
++
++static struct mtd_info *mtd;
++static struct mtd_partition *parts;
++static const char *probes[] = { "RedBoot", "afs", NULL };
++
++static int __init armflash_cfi_init(void *base, u_int size)
++{
++ int ret;
++
++ armflash_flash_init();
++ armflash_flash_wp(1);
++
++ /*
++ * look for CFI based flash parts fitted to this board
++ */
++ armflash_map.size = size;
++ armflash_map.buswidth = 4;
++ armflash_map.virt = (unsigned long) base;
++
++ simple_map_init(&armflash_map);
++
++ /*
++ * Also, the CFI layer automatically works out what size
++ * of chips we have, and does the necessary identification
++ * for us automatically.
++ */
++ mtd = do_map_probe("cfi_probe", &armflash_map);
++ if (!mtd)
++ return -ENXIO;
++
++ mtd->owner = THIS_MODULE;
++
++ ret = parse_mtd_partitions(mtd, probes, &parts, (void *)0);
++ if (ret > 0) {
++ ret = add_mtd_partitions(mtd, parts, ret);
++ if (ret)
++ printk(KERN_ERR "mtd partition registration "
++ "failed: %d\n", ret);
++ }
++
++ /*
++ * If we got an error, free all resources.
++ */
++ if (ret < 0) {
++ del_mtd_partitions(mtd);
++ map_destroy(mtd);
++ }
++
++ return ret;
++}
++
++static void armflash_cfi_exit(void)
++{
++ if (mtd) {
++ del_mtd_partitions(mtd);
++ map_destroy(mtd);
++ }
++ if (parts)
++ kfree(parts);
++}
++
++static int __init armflash_init(void)
++{
++ int err = -EBUSY;
++ void *base;
++
++ if (request_mem_region(FLASH_BASE, FLASH_SIZE, "flash") == NULL)
++ goto out;
++
++ base = ioremap(FLASH_BASE, FLASH_SIZE);
++ err = -ENOMEM;
++ if (base == NULL)
++ goto release;
++
++ err = armflash_cfi_init(base, FLASH_SIZE);
++ if (err) {
++ iounmap(base);
++release:
++ release_mem_region(FLASH_BASE, FLASH_SIZE);
++ }
++out:
++ return err;
++}
++
++static void __exit armflash_exit(void)
++{
++ armflash_cfi_exit();
++ iounmap((void *)armflash_map.virt);
++ release_mem_region(FLASH_BASE, FLASH_SIZE);
++ armflash_flash_exit();
++}
++
++module_init(armflash_init);
++module_exit(armflash_exit);
++
++MODULE_AUTHOR("ARM Ltd");
++MODULE_DESCRIPTION("ARM Integrator CFI map driver");
++MODULE_LICENSE("GPL");
+diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/integrator-flash.c linux/drivers/mtd/maps/integrator-flash.c
+--- linux-mips-2.4.27/drivers/mtd/maps/integrator-flash.c 2002-06-27 00:35:50.000000000 +0200
++++ linux/drivers/mtd/maps/integrator-flash.c 2004-11-19 10:25:11.923196144 +0100
+@@ -1,8 +1,9 @@
+ /*======================================================================
+
+- drivers/mtd/maps/armflash.c: ARM Flash Layout/Partitioning
++ drivers/mtd/maps/integrator-flash.c: ARM Integrator flash map driver
+
+ Copyright (C) 2000 ARM Limited
++ Copyright (C) 2003 Deep Blue Solutions Ltd.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+@@ -21,7 +22,7 @@
+ This is access code for flashes using ARM's flash partitioning
+ standards.
+
+- $Id$
++ $Id$
+
+ ======================================================================*/
+
+@@ -31,268 +32,181 @@
+ #include <linux/kernel.h>
+ #include <linux/slab.h>
+ #include <linux/ioport.h>
++#include <linux/device.h>
+ #include <linux/init.h>
+
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+ #include <linux/mtd/partitions.h>
+
++#include <asm/mach/flash.h>
+ #include <asm/hardware.h>
+ #include <asm/io.h>
+ #include <asm/system.h>
+
+-extern int parse_afs_partitions(struct mtd_info *, struct mtd_partition **);
+-
+-// board specific stuff - sorry, it should be in arch/arm/mach-*.
+-#ifdef CONFIG_ARCH_INTEGRATOR
+-
+-#define FLASH_BASE INTEGRATOR_FLASH_BASE
+-#define FLASH_SIZE INTEGRATOR_FLASH_SIZE
+-
+-#define FLASH_PART_SIZE 0x400000
+-
+-#define SC_CTRLC (IO_ADDRESS(INTEGRATOR_SC_BASE) + INTEGRATOR_SC_CTRLC_OFFSET)
+-#define SC_CTRLS (IO_ADDRESS(INTEGRATOR_SC_BASE) + INTEGRATOR_SC_CTRLS_OFFSET)
+-#define EBI_CSR1 (IO_ADDRESS(INTEGRATOR_EBI_BASE) + INTEGRATOR_EBI_CSR1_OFFSET)
+-#define EBI_LOCK (IO_ADDRESS(INTEGRATOR_EBI_BASE) + INTEGRATOR_EBI_LOCK_OFFSET)
+-
+-/*
+- * Initialise the flash access systems:
+- * - Disable VPP
+- * - Assert WP
+- * - Set write enable bit in EBI reg
+- */
+-static void armflash_flash_init(void)
+-{
+- unsigned int tmp;
+-
+- __raw_writel(INTEGRATOR_SC_CTRL_nFLVPPEN | INTEGRATOR_SC_CTRL_nFLWP, SC_CTRLC);
+-
+- tmp = __raw_readl(EBI_CSR1) | INTEGRATOR_EBI_WRITE_ENABLE;
+- __raw_writel(tmp, EBI_CSR1);
+-
+- if (!(__raw_readl(EBI_CSR1) & INTEGRATOR_EBI_WRITE_ENABLE)) {
+- __raw_writel(0xa05f, EBI_LOCK);
+- __raw_writel(tmp, EBI_CSR1);
+- __raw_writel(0, EBI_LOCK);
+- }
+-}
+-
+-/*
+- * Shutdown the flash access systems:
+- * - Disable VPP
+- * - Assert WP
+- * - Clear write enable bit in EBI reg
+- */
+-static void armflash_flash_exit(void)
+-{
+- unsigned int tmp;
+-
+- __raw_writel(INTEGRATOR_SC_CTRL_nFLVPPEN | INTEGRATOR_SC_CTRL_nFLWP, SC_CTRLC);
+-
+- /*
+- * Clear the write enable bit in system controller EBI register.
+- */
+- tmp = __raw_readl(EBI_CSR1) & ~INTEGRATOR_EBI_WRITE_ENABLE;
+- __raw_writel(tmp, EBI_CSR1);
+-
+- if (__raw_readl(EBI_CSR1) & INTEGRATOR_EBI_WRITE_ENABLE) {
+- __raw_writel(0xa05f, EBI_LOCK);
+- __raw_writel(tmp, EBI_CSR1);
+- __raw_writel(0, EBI_LOCK);
+- }
+-}
+-
+-static void armflash_flash_wp(int on)
+-{
+- unsigned int reg;
+-
+- if (on)
+- reg = SC_CTRLC;
+- else
+- reg = SC_CTRLS;
+-
+- __raw_writel(INTEGRATOR_SC_CTRL_nFLWP, reg);
+-}
+-
+-static void armflash_set_vpp(struct map_info *map, int on)
+-{
+- unsigned int reg;
+-
+- if (on)
+- reg = SC_CTRLS;
+- else
+- reg = SC_CTRLC;
+-
+- __raw_writel(INTEGRATOR_SC_CTRL_nFLVPPEN, reg);
+-}
+-#endif
+-
+ #ifdef CONFIG_ARCH_P720T
+-
+ #define FLASH_BASE (0x04000000)
+ #define FLASH_SIZE (64*1024*1024)
+-
+-#define FLASH_PART_SIZE (4*1024*1024)
+-#define FLASH_BLOCK_SIZE (128*1024)
+-
+-static void armflash_flash_init(void)
+-{
+-}
+-
+-static void armflash_flash_exit(void)
+-{
+-}
+-
+-static void armflash_flash_wp(int on)
+-{
+-}
+-
+-static void armflash_set_vpp(struct map_info *map, int on)
+-{
+-}
+ #endif
+
+-static __u8 armflash_read8(struct map_info *map, unsigned long ofs)
+-{
+- return readb(ofs + map->map_priv_2);
+-}
+-
+-static __u16 armflash_read16(struct map_info *map, unsigned long ofs)
+-{
+- return readw(ofs + map->map_priv_2);
+-}
++struct armflash_info {
++ struct flash_platform_data *plat;
++ struct resource *res;
++ struct mtd_partition *parts;
++ struct mtd_info *mtd;
++ struct map_info map;
++};
+
+-static __u32 armflash_read32(struct map_info *map, unsigned long ofs)
++static void armflash_set_vpp(struct map_info *map, int on)
+ {
+- return readl(ofs + map->map_priv_2);
+-}
++ struct armflash_info *info = container_of(map, struct armflash_info, map);
+
+-static void armflash_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+-{
+- memcpy(to, (void *) (from + map->map_priv_2), len);
++ if (info->plat && info->plat->set_vpp)
++ info->plat->set_vpp(on);
+ }
+
+-static void armflash_write8(struct map_info *map, __u8 d, unsigned long adr)
+-{
+- writeb(d, adr + map->map_priv_2);
+-}
+-
+-static void armflash_write16(struct map_info *map, __u16 d, unsigned long adr)
+-{
+- writew(d, adr + map->map_priv_2);
+-}
++static const char *probes[] = { "RedBoot", "afs", NULL };
+
+-static void armflash_write32(struct map_info *map, __u32 d, unsigned long adr)
++static int armflash_probe(struct device *_dev)
+ {
+- writel(d, adr + map->map_priv_2);
+-}
++ struct platform_device *dev = to_platform_device(_dev);
++ struct flash_platform_data *plat = dev->dev.platform_data;
++ struct resource *res = dev->resource;
++ unsigned int size = res->end - res->start + 1;
++ struct armflash_info *info;
++ int err;
++ void *base;
+
+-static void armflash_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+-{
+- memcpy((void *) (to + map->map_priv_2), from, len);
+-}
++ info = kmalloc(sizeof(struct armflash_info), GFP_KERNEL);
++ if (!info) {
++ err = -ENOMEM;
++ goto out;
++ }
+
+-static struct map_info armflash_map =
+-{
+- name: "AFS",
+- read8: armflash_read8,
+- read16: armflash_read16,
+- read32: armflash_read32,
+- copy_from: armflash_copy_from,
+- write8: armflash_write8,
+- write16: armflash_write16,
+- write32: armflash_write32,
+- copy_to: armflash_copy_to,
+- set_vpp: armflash_set_vpp,
+-};
++ memset(info, 0, sizeof(struct armflash_info));
+
+-static struct mtd_info *mtd;
+-static struct mtd_partition *parts;
++ info->plat = plat;
++ if (plat && plat->init) {
++ err = plat->init();
++ if (err)
++ goto no_resource;
++ }
+
+-static int __init armflash_cfi_init(void *base, u_int size)
+-{
+- int ret;
++ info->res = request_mem_region(res->start, size, "armflash");
++ if (!info->res) {
++ err = -EBUSY;
++ goto no_resource;
++ }
+
+- armflash_flash_init();
+- armflash_flash_wp(1);
++ base = ioremap(res->start, size);
++ if (!base) {
++ err = -ENOMEM;
++ goto no_mem;
++ }
+
+ /*
+ * look for CFI based flash parts fitted to this board
+ */
+- armflash_map.size = size;
+- armflash_map.buswidth = 4;
+- armflash_map.map_priv_2 = (unsigned long) base;
++ info->map.size = size;
++ info->map.buswidth = plat->width;
++ info->map.phys = res->start;
++ info->map.virt = (unsigned long) base;
++ info->map.name = dev->dev.bus_id;
++ info->map.set_vpp = armflash_set_vpp;
++
++ simple_map_init(&info->map);
+
+ /*
+ * Also, the CFI layer automatically works out what size
+ * of chips we have, and does the necessary identification
+ * for us automatically.
+ */
+- mtd = do_map_probe("cfi_probe", &armflash_map);
+- if (!mtd)
+- return -ENXIO;
+-
+- mtd->module = THIS_MODULE;
+-
+- ret = parse_afs_partitions(mtd, &parts);
+- if (ret > 0) {
+- ret = add_mtd_partitions(mtd, parts, ret);
+- if (ret)
+- printk(KERN_ERR "mtd partition registration "
+- "failed: %d\n", ret);
++ info->mtd = do_map_probe(plat->map_name, &info->map);
++ if (!info->mtd) {
++ err = -ENXIO;
++ goto no_device;
+ }
+
++ info->mtd->owner = THIS_MODULE;
++
++ err = parse_mtd_partitions(info->mtd, probes, &info->parts, 0);
++ if (err > 0) {
++ err = add_mtd_partitions(info->mtd, info->parts, err);
++ if (err)
++ printk(KERN_ERR
++ "mtd partition registration failed: %d\n", err);
++ }
++
++ if (err == 0)
++ dev_set_drvdata(&dev->dev, info);
++
+ /*
+ * If we got an error, free all resources.
+ */
+- if (ret < 0) {
+- del_mtd_partitions(mtd);
+- map_destroy(mtd);
++ if (err < 0) {
++ if (info->mtd) {
++ del_mtd_partitions(info->mtd);
++ map_destroy(info->mtd);
+ }
++ if (info->parts)
++ kfree(info->parts);
+
+- return ret;
+-}
+-
+-static void armflash_cfi_exit(void)
+-{
+- if (mtd) {
+- del_mtd_partitions(mtd);
+- map_destroy(mtd);
++ no_device:
++ iounmap(base);
++ no_mem:
++ release_mem_region(res->start, size);
++ no_resource:
++ if (plat && plat->exit)
++ plat->exit();
++ kfree(info);
+ }
+- if (parts)
+- kfree(parts);
++ out:
++ return err;
+ }
+
+-static int __init armflash_init(void)
++static int armflash_remove(struct device *_dev)
+ {
+- int err = -EBUSY;
+- void *base;
++ struct platform_device *dev = to_platform_device(_dev);
++ struct armflash_info *info = dev_get_drvdata(&dev->dev);
+
+- if (request_mem_region(FLASH_BASE, FLASH_SIZE, "flash") == NULL)
+- goto out;
++ dev_set_drvdata(&dev->dev, NULL);
+
+- base = ioremap(FLASH_BASE, FLASH_SIZE);
+- err = -ENOMEM;
+- if (base == NULL)
+- goto release;
++ if (info) {
++ if (info->mtd) {
++ del_mtd_partitions(info->mtd);
++ map_destroy(info->mtd);
++ }
++ if (info->parts)
++ kfree(info->parts);
+
+- err = armflash_cfi_init(base, FLASH_SIZE);
+- if (err) {
+- iounmap(base);
+-release:
+- release_mem_region(FLASH_BASE, FLASH_SIZE);
++ iounmap((void *)info->map.virt);
++ release_resource(info->res);
++ kfree(info->res);
++
++ if (info->plat && info->plat->exit)
++ info->plat->exit();
++
++ kfree(info);
+ }
+-out:
+- return err;
++
++ return 0;
++}
++
++static struct device_driver armflash_driver = {
++ .name = "armflash",
++ .bus = &platform_bus_type,
++ .probe = armflash_probe,
++ .remove = armflash_remove,
++};
++
++static int __init armflash_init(void)
++{
++ return driver_register(&armflash_driver);
+ }
+
+ static void __exit armflash_exit(void)
+ {
+- armflash_cfi_exit();
+- iounmap((void *)armflash_map.map_priv_2);
+- release_mem_region(FLASH_BASE, FLASH_SIZE);
+- armflash_flash_exit();
++ driver_unregister(&armflash_driver);
+ }
+
+ module_init(armflash_init);
+diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/iq80310.c linux/drivers/mtd/maps/iq80310.c
+--- linux-mips-2.4.27/drivers/mtd/maps/iq80310.c 2002-06-27 00:35:50.000000000 +0200
++++ linux/drivers/mtd/maps/iq80310.c 2004-11-19 10:25:11.924195992 +0100
+@@ -1,5 +1,5 @@
+ /*
+- * $Id$
++ * $Id$
+ *
+ * Mapping for the Intel XScale IQ80310 evaluation board
+ *
+@@ -14,6 +14,8 @@
+ #include <linux/module.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/slab.h>
+ #include <asm/io.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+@@ -26,127 +28,72 @@
+
+ static struct mtd_info *mymtd;
+
+-static __u8 iq80310_read8(struct map_info *map, unsigned long ofs)
+-{
+- return *(__u8 *)(map->map_priv_1 + ofs);
+-}
+-
+-static __u16 iq80310_read16(struct map_info *map, unsigned long ofs)
+-{
+- return *(__u16 *)(map->map_priv_1 + ofs);
+-}
+-
+-static __u32 iq80310_read32(struct map_info *map, unsigned long ofs)
+-{
+- return *(__u32 *)(map->map_priv_1 + ofs);
+-}
+-
+-static void iq80310_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+-{
+- memcpy(to, (void *)(map->map_priv_1 + from), len);
+-}
+-
+-static void iq80310_write8(struct map_info *map, __u8 d, unsigned long adr)
+-{
+- *(__u8 *)(map->map_priv_1 + adr) = d;
+-}
+-
+-static void iq80310_write16(struct map_info *map, __u16 d, unsigned long adr)
+-{
+- *(__u16 *)(map->map_priv_1 + adr) = d;
+-}
+-
+-static void iq80310_write32(struct map_info *map, __u32 d, unsigned long adr)
+-{
+- *(__u32 *)(map->map_priv_1 + adr) = d;
+-}
+-
+-static void iq80310_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+-{
+- memcpy((void *)(map->map_priv_1 + to), from, len);
+-}
+-
+ static struct map_info iq80310_map = {
+- name: "IQ80310 flash",
+- size: WINDOW_SIZE,
+- buswidth: BUSWIDTH,
+- read8: iq80310_read8,
+- read16: iq80310_read16,
+- read32: iq80310_read32,
+- copy_from: iq80310_copy_from,
+- write8: iq80310_write8,
+- write16: iq80310_write16,
+- write32: iq80310_write32,
+- copy_to: iq80310_copy_to
++ .name = "IQ80310 flash",
++ .size = WINDOW_SIZE,
++ .buswidth = BUSWIDTH,
++ .phys = WINDOW_ADDR
+ };
+
+ static struct mtd_partition iq80310_partitions[4] = {
+ {
+- name: "Firmware",
+- size: 0x00080000,
+- offset: 0,
+- mask_flags: MTD_WRITEABLE /* force read-only */
++ .name = "Firmware",
++ .size = 0x00080000,
++ .offset = 0,
++ .mask_flags = MTD_WRITEABLE /* force read-only */
+ },{
+- name: "Kernel",
+- size: 0x000a0000,
+- offset: 0x00080000,
++ .name = "Kernel",
++ .size = 0x000a0000,
++ .offset = 0x00080000,
+ },{
+- name: "Filesystem",
+- size: 0x00600000,
+- offset: 0x00120000
++ .name = "Filesystem",
++ .size = 0x00600000,
++ .offset = 0x00120000
+ },{
+- name: "RedBoot",
+- size: 0x000e0000,
+- offset: 0x00720000,
+- mask_flags: MTD_WRITEABLE
++ .name = "RedBoot",
++ .size = 0x000e0000,
++ .offset = 0x00720000,
++ .mask_flags = MTD_WRITEABLE
+ }
+ };
+
+-#define NB_OF(x) (sizeof(x)/sizeof(x[0]))
+-
+ static struct mtd_info *mymtd;
+ static struct mtd_partition *parsed_parts;
+-
+-extern int parse_redboot_partitions(struct mtd_info *master, struct mtd_partition **pparts);
++static const char *probes[] = { "RedBoot", "cmdlinepart", NULL };
+
+ static int __init init_iq80310(void)
+ {
+ struct mtd_partition *parts;
+ int nb_parts = 0;
+ int parsed_nr_parts = 0;
+- char *part_type = "static";
++ int ret;
+
+- iq80310_map.map_priv_1 = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE);
+- if (!iq80310_map.map_priv_1) {
++ iq80310_map.virt = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE);
++ if (!iq80310_map.virt) {
+ printk("Failed to ioremap\n");
+ return -EIO;
+ }
++ simple_map_init(&iq80310_map);
++
+ mymtd = do_map_probe("cfi_probe", &iq80310_map);
+ if (!mymtd) {
+- iounmap((void *)iq80310_map.map_priv_1);
++ iounmap((void *)iq80310_map.virt);
+ return -ENXIO;
+ }
+- mymtd->module = THIS_MODULE;
++ mymtd->owner = THIS_MODULE;
+
+-#ifdef CONFIG_MTD_REDBOOT_PARTS
+- if (parsed_nr_parts == 0) {
+- int ret = parse_redboot_partitions(mymtd, &parsed_parts);
++ ret = parse_mtd_partitions(mymtd, probes, &parsed_parts, 0);
+
+- if (ret > 0) {
+- part_type = "RedBoot";
++ if (ret > 0)
+ parsed_nr_parts = ret;
+- }
+- }
+-#endif
+
+ if (parsed_nr_parts > 0) {
+ parts = parsed_parts;
+ nb_parts = parsed_nr_parts;
+ } else {
+ parts = iq80310_partitions;
+- nb_parts = NB_OF(iq80310_partitions);
++ nb_parts = ARRAY_SIZE(iq80310_partitions);
+ }
+- printk(KERN_NOTICE "Using %s partition definition\n", part_type);
+ add_mtd_partitions(mymtd, parts, nb_parts);
+ return 0;
+ }
+@@ -159,8 +106,8 @@
+ if (parsed_parts)
+ kfree(parsed_parts);
+ }
+- if (iq80310_map.map_priv_1)
+- iounmap((void *)iq80310_map.map_priv_1);
++ if (iq80310_map.virt)
++ iounmap((void *)iq80310_map.virt);
+ }
+
+ module_init(init_iq80310);
+diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/ixp425.c linux/drivers/mtd/maps/ixp425.c
+--- linux-mips-2.4.27/drivers/mtd/maps/ixp425.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/mtd/maps/ixp425.c 2004-11-19 10:25:11.925195840 +0100
+@@ -0,0 +1,220 @@
++/*
++ * $Id$
++ *
++ * drivers/mtd/maps/ixp425.c
++ *
++ * MTD Map file for IXP425 based systems. Please do not make per-board
++ * map driver as the code will be 90% identical. For now just add
++ * if(machine_is_XXX()) checks to the code. I'll clean this stuff to
++ * use platform_data in the the future so we can get rid of that too.
++ *
++ * Original Author: Intel Corporation
++ * Maintainer: Deepak Saxena <dsaxena@mvista.com>
++ *
++ * Copyright (C) 2002 Intel Corporation
++ * Copyright (C) 2003 MontaVista Software, Inc.
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/string.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/map.h>
++#include <linux/mtd/partitions.h>
++#include <linux/ioport.h>
++#include <asm/io.h>
++#include <asm/mach-types.h>
++
++#include <linux/reboot.h>
++
++#define WINDOW_ADDR 0x50000000
++#define BUSWIDTH 2
++
++#ifndef __ARMEB__
++#define BYTE0(h) ((h) & 0xFF)
++#define BYTE1(h) (((h) >> 8) & 0xFF)
++#else
++#define BYTE0(h) (((h) >> 8) & 0xFF)
++#define BYTE1(h) ((h) & 0xFF)
++#endif
++
++static __u16
++ixp425_read16(struct map_info *map, unsigned long ofs)
++{
++ return *(__u16 *) (map->map_priv_1 + ofs);
++}
++
++/*
++ * The IXP425 expansion bus only allows 16-bit wide acceses
++ * when attached to a 16-bit wide device (such as the 28F128J3A),
++ * so we can't just memcpy_fromio().
++ */
++static void
++ixp425_copy_from(struct map_info *map, void *to,
++ unsigned long from, ssize_t len)
++{
++ int i;
++ u8 *dest = (u8 *) to;
++ u16 *src = (u16 *) (map->map_priv_1 + from);
++ u16 data;
++
++ for (i = 0; i < (len / 2); i++) {
++ data = src[i];
++ dest[i * 2] = BYTE0(data);
++ dest[i * 2 + 1] = BYTE1(data);
++ }
++
++ if (len & 1)
++ dest[len - 1] = BYTE0(src[i]);
++}
++
++static void
++ixp425_write16(struct map_info *map, __u16 d, unsigned long adr)
++{
++ *(__u16 *) (map->map_priv_1 + adr) = d;
++}
++
++static struct map_info ixp425_map = {
++ .name = "IXP425 Flash",
++ .buswidth = BUSWIDTH,
++ .read16 = ixp425_read16,
++ .copy_from = ixp425_copy_from,
++ .write16 = ixp425_write16,
++};
++
++/*
++ * Put flash back in read mode so RedBoot can boot properly.
++ */
++int ixp425_mtd_reboot(struct notifier_block *n, unsigned long code, void *p)
++{
++ if (code != SYS_RESTART)
++ return NOTIFY_DONE;
++
++ ixp425_write16(&ixp425_map, 0xff, 0x55 * 0x2);
++ return NOTIFY_DONE;
++}
++
++static struct notifier_block ixp425_mtd_notifier = {
++ notifier_call:ixp425_mtd_reboot,
++ next:NULL,
++ priority:0
++};
++
++static struct mtd_partition *parsed_parts;
++static const char *probes[] = { "RedBoot", "cmdlinepart", NULL };
++
++static struct mtd_partition ixp425_partitions[] = {
++ {
++ .name = "image",
++ .offset = 0x00040000,
++ .size = 0x00400000,
++ }, {
++ .name = "user",
++ .offset = 0x00440000,
++ .size = MTDPART_SIZ_FULL
++ }
++};
++
++#define NB_OF(x) (sizeof(x)/sizeof(x[0]))
++
++static struct mtd_info *ixp425_mtd;
++static struct resource *mtd_resource;
++
++static void
++ixp425_exit(void)
++{
++ if (ixp425_mtd) {
++ del_mtd_partitions(ixp425_mtd);
++ map_destroy(ixp425_mtd);
++ }
++ if (ixp425_map.map_priv_1)
++ iounmap((void *) ixp425_map.map_priv_1);
++ if (mtd_resource)
++ release_mem_region(WINDOW_ADDR, ixp425_map.size);
++
++ if (parsed_parts)
++ kfree(parsed_parts);
++
++ unregister_reboot_notifier(&ixp425_mtd_notifier);
++
++ /* Disable flash write */
++ *IXP425_EXP_CS0 &= ~IXP425_FLASH_WRITABLE;
++
++ if(machine_is_adi_coyote())
++ *IXP425_EXP_CS1 &= ~IXP425_FLASH_WRITABLE;
++}
++
++static int __init
++ixp425_init(void)
++{
++ int res = -1, npart;
++
++ /* Enable flash write */
++ *IXP425_EXP_CS0 |= IXP425_FLASH_WRITABLE;
++
++ /*
++ * Coyote requires CS1 write to be enabled and has 32MB flash.
++ * This will move to the platform init code in 2.6
++ */
++ if(machine_is_adi_coyote()) {
++ *IXP425_EXP_CS1 |= IXP425_FLASH_WRITABLE;
++ ixp425_map.size = 0x02000000;
++ } else
++ ixp425_map.size = 0x01000000;
++
++ ixp425_map.map_priv_1 = 0;
++ mtd_resource =
++ request_mem_region(WINDOW_ADDR, ixp425_map.size, "IXP425 Flash");
++ if (!mtd_resource) {
++ printk(KERN_ERR
++ "ixp425 flash: Could not request mem region.\n");
++ res = -ENOMEM;
++ goto Error;
++ }
++
++ ixp425_map.map_priv_1 =
++ (unsigned long) ioremap(WINDOW_ADDR, ixp425_map.size);
++ if (!ixp425_map.map_priv_1) {
++ printk("ixp425 Flash: Failed to map IO region. (ioremap)\n");
++ res = -EIO;
++ goto Error;
++ }
++
++ ixp425_mtd = do_map_probe("cfi_probe", &ixp425_map);
++ if (!ixp425_mtd) {
++ res = -ENXIO;
++ goto Error;
++ }
++ ixp425_mtd->owner = THIS_MODULE;
++
++ /* Try to parse RedBoot partitions */
++ npart = parse_mtd_partitions(ixp425_mtd, probes, &parsed_parts, 0);
++ if (npart > 0)
++ res = add_mtd_partitions(ixp425_mtd, parsed_parts, npart);
++ else {
++ printk("IXP425 Flash: Using static MTD partitions.\n");
++ res = add_mtd_partitions(ixp425_mtd, ixp425_partitions,
++ NB_OF(ixp425_partitions));
++ }
++
++ if (res)
++ goto Error;
++
++ register_reboot_notifier(&ixp425_mtd_notifier);
++
++ return res;
++
++Error:
++ ixp425_exit();
++ return res;
++}
++
++module_init(ixp425_init);
++module_exit(ixp425_exit);
++
++MODULE_LICENSE("GPL");
++MODULE_DESCRIPTION("MTD map driver for ixp425 evaluation board");
++MODULE_AUTHOR("Deepak Saxena");
+diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/l440gx.c linux/drivers/mtd/maps/l440gx.c
+--- linux-mips-2.4.27/drivers/mtd/maps/l440gx.c 2002-06-27 00:35:50.000000000 +0200
++++ linux/drivers/mtd/maps/l440gx.c 2004-11-19 10:25:11.927195536 +0100
+@@ -1,5 +1,5 @@
+ /*
+- * $Id$
++ * $Id$
+ *
+ * BIOS Flash chip on Intel 440GX board.
+ *
+@@ -9,6 +9,7 @@
+ #include <linux/module.h>
+ #include <linux/pci.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
+ #include <asm/io.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+@@ -27,48 +28,6 @@
+
+ static struct mtd_info *mymtd;
+
+-__u8 l440gx_read8(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readb(map->map_priv_1 + ofs);
+-}
+-
+-__u16 l440gx_read16(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readw(map->map_priv_1 + ofs);
+-}
+-
+-__u32 l440gx_read32(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readl(map->map_priv_1 + ofs);
+-}
+-
+-void l440gx_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+-{
+- memcpy_fromio(to, map->map_priv_1 + from, len);
+-}
+-
+-void l440gx_write8(struct map_info *map, __u8 d, unsigned long adr)
+-{
+- __raw_writeb(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-void l440gx_write16(struct map_info *map, __u16 d, unsigned long adr)
+-{
+- __raw_writew(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-void l440gx_write32(struct map_info *map, __u32 d, unsigned long adr)
+-{
+- __raw_writel(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-void l440gx_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+-{
+- memcpy_toio(map->map_priv_1 + to, from, len);
+-}
+
+ /* Is this really the vpp port? */
+ void l440gx_set_vpp(struct map_info *map, int vpp)
+@@ -85,22 +44,15 @@
+ }
+
+ struct map_info l440gx_map = {
+- name: "L440GX BIOS",
+- size: WINDOW_SIZE,
+- buswidth: BUSWIDTH,
+- read8: l440gx_read8,
+- read16: l440gx_read16,
+- read32: l440gx_read32,
+- copy_from: l440gx_copy_from,
+- write8: l440gx_write8,
+- write16: l440gx_write16,
+- write32: l440gx_write32,
+- copy_to: l440gx_copy_to,
++ .name = "L440GX BIOS",
++ .size = WINDOW_SIZE,
++ .buswidth = BUSWIDTH,
++ .phys = WINDOW_ADDR,
+ #if 0
+ /* FIXME verify that this is the
+ * appripriate code for vpp enable/disable
+ */
+- set_vpp: l440gx_set_vpp
++ .set_vpp = l440gx_set_vpp
+ #endif
+ };
+
+@@ -113,7 +65,6 @@
+ dev = pci_find_device(PCI_VENDOR_ID_INTEL,
+ PCI_DEVICE_ID_INTEL_82371AB_0, NULL);
+
+-
+ pm_dev = pci_find_device(PCI_VENDOR_ID_INTEL,
+ PCI_DEVICE_ID_INTEL_82371AB_3, NULL);
+
+@@ -122,15 +73,14 @@
+ return -ENODEV;
+ }
+
++ l440gx_map.virt = (unsigned long)ioremap_nocache(WINDOW_ADDR, WINDOW_SIZE);
+
+- l440gx_map.map_priv_1 = (unsigned long)ioremap_nocache(WINDOW_ADDR, WINDOW_SIZE);
+-
+- if (!l440gx_map.map_priv_1) {
++ if (!l440gx_map.virt) {
+ printk(KERN_WARNING "Failed to ioremap L440GX flash region\n");
+ return -ENOMEM;
+ }
+-
+- printk(KERN_NOTICE "window_addr = 0x%08lx\n", (unsigned long)l440gx_map.map_priv_1);
++ simple_map_init(&l440gx_map);
++ printk(KERN_NOTICE "window_addr = 0x%08lx\n", (unsigned long)l440gx_map.virt);
+
+ /* Setup the pm iobase resource
+ * This code should move into some kind of generic bridge
+@@ -153,7 +103,7 @@
+ /* Allocate the resource region */
+ if (pci_assign_resource(pm_dev, PIIXE_IOBASE_RESOURCE) != 0) {
+ printk(KERN_WARNING "Could not allocate pm iobase resource\n");
+- iounmap((void *)l440gx_map.map_priv_1);
++ iounmap((void *)l440gx_map.virt);
+ return -ENXIO;
+ }
+ }
+@@ -181,13 +131,13 @@
+ mymtd = do_map_probe("map_rom", &l440gx_map);
+ }
+ if (mymtd) {
+- mymtd->module = THIS_MODULE;
++ mymtd->owner = THIS_MODULE;
+
+ add_mtd_device(mymtd);
+ return 0;
+ }
+
+- iounmap((void *)l440gx_map.map_priv_1);
++ iounmap((void *)l440gx_map.virt);
+ return -ENXIO;
+ }
+
+@@ -196,7 +146,7 @@
+ del_mtd_device(mymtd);
+ map_destroy(mymtd);
+
+- iounmap((void *)l440gx_map.map_priv_1);
++ iounmap((void *)l440gx_map.virt);
+ }
+
+ module_init(init_l440gx);
+diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/lasat.c linux/drivers/mtd/maps/lasat.c
+--- linux-mips-2.4.27/drivers/mtd/maps/lasat.c 2003-08-18 04:59:02.000000000 +0200
++++ linux/drivers/mtd/maps/lasat.c 2004-11-19 10:25:11.928195384 +0100
+@@ -1,11 +1,20 @@
+ /*
+- * Flash device on lasat 100 and 200 boards
++ * Flash device on Lasat 100 and 200 boards
++ *
++ * (C) 2002 Brian Murphy <brian@murphy.dk>
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License version
++ * 2 as published by the Free Software Foundation.
++ *
++ * $Id$
+ *
+ */
+
+ #include <linux/module.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
+ #include <asm/io.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+@@ -13,123 +22,80 @@
+ #include <linux/config.h>
+ #include <asm/lasat/lasat.h>
+
+-static struct mtd_info *mymtd;
+-
+-static __u8 sp_read8(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readb(map->map_priv_1 + ofs);
+-}
+-
+-static __u16 sp_read16(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readw(map->map_priv_1 + ofs);
+-}
+-
+-static __u32 sp_read32(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readl(map->map_priv_1 + ofs);
+-}
+-
+-static void sp_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+-{
+- memcpy_fromio(to, map->map_priv_1 + from, len);
+-}
++static struct mtd_info *lasat_mtd;
+
+-static void sp_write8(struct map_info *map, __u8 d, unsigned long adr)
+-{
+- __raw_writeb(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-static void sp_write16(struct map_info *map, __u16 d, unsigned long adr)
+-{
+- __raw_writew(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-static void sp_write32(struct map_info *map, __u32 d, unsigned long adr)
+-{
+- __raw_writel(d, map->map_priv_1 + adr);
+- mb();
+-}
++static struct mtd_partition partition_info[LASAT_MTD_LAST];
++static char *lasat_mtd_partnames[] = {"Bootloader", "Service", "Normal", "Filesystem", "Config"};
+
+-static void sp_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
++static void lasat_set_vpp(struct map_info *map, int vpp)
+ {
+- memcpy_toio(map->map_priv_1 + to, from, len);
++ if (vpp)
++ *lasat_misc->flash_wp_reg |= 1 << lasat_misc->flash_wp_bit;
++ else
++ *lasat_misc->flash_wp_reg &= ~(1 << lasat_misc->flash_wp_bit);
+ }
+
+-static struct map_info sp_map = {
+- name: "SP flash",
+- buswidth: 4,
+- read8: sp_read8,
+- read16: sp_read16,
+- read32: sp_read32,
+- copy_from: sp_copy_from,
+- write8: sp_write8,
+- write16: sp_write16,
+- write32: sp_write32,
+- copy_to: sp_copy_to
++static struct map_info lasat_map = {
++ .name = "LASAT flash",
++ .buswidth = 4,
++ .set_vpp = lasat_set_vpp
+ };
+
+-static struct mtd_partition partition_info[LASAT_MTD_LAST];
+-static char *lasat_mtd_partnames[] = {"Bootloader", "Service", "Normal", "Config", "Filesystem"};
+-
+-static int __init init_sp(void)
++static int __init init_lasat(void)
+ {
+ int i;
+- int nparts = 0;
+- /* this does not play well with the old flash code which
+- * protects and uprotects the flash when necessary */
++ /* since we use AMD chips and set_vpp is not implimented
++ * for these (yet) we still have to permanently enable flash write */
+ printk(KERN_NOTICE "Unprotecting flash\n");
+- *lasat_misc->flash_wp_reg |= 1 << lasat_misc->flash_wp_bit;
++ ENABLE_VPP((&lasat_map));
+
+- sp_map.map_priv_1 = ioremap_nocache(
+- lasat_flash_partition_start(LASAT_MTD_BOOTLOADER),
+- lasat_board_info.li_flash_size);
+- sp_map.size = lasat_board_info.li_flash_size;
++ lasat_map.phys = lasat_flash_partition_start(LASAT_MTD_BOOTLOADER);
++ lasat_map.virt = (unsigned long)ioremap_nocache(
++ lasat_map.phys, lasat_board_info.li_flash_size);
++ lasat_map.size = lasat_board_info.li_flash_size;
+
+- printk(KERN_NOTICE "sp flash device: %lx at %lx\n",
+- sp_map.size, sp_map.map_priv_1);
++ simple_map_init(&lasat_map);
+
+ for (i=0; i < LASAT_MTD_LAST; i++)
+ partition_info[i].name = lasat_mtd_partnames[i];
+
+- mymtd = do_map_probe("cfi_probe", &sp_map);
+- if (mymtd) {
++ lasat_mtd = do_map_probe("cfi_probe", &lasat_map);
++
++ if (!lasat_mtd)
++ lasat_mtd = do_map_probe("jedec_probe", &lasat_map);
++
++ if (lasat_mtd) {
+ u32 size, offset = 0;
+
+- mymtd->module = THIS_MODULE;
++ lasat_mtd->owner = THIS_MODULE;
+
+ for (i=0; i < LASAT_MTD_LAST; i++) {
+ size = lasat_flash_partition_size(i);
+- if (size != 0) {
+- nparts++;
+ partition_info[i].size = size;
+ partition_info[i].offset = offset;
+ offset += size;
+ }
+- }
+
+- add_mtd_partitions( mymtd, partition_info, nparts );
++ add_mtd_partitions( lasat_mtd, partition_info, LASAT_MTD_LAST );
+ return 0;
+ }
+
+ return -ENXIO;
+ }
+
+-static void __exit cleanup_sp(void)
++static void __exit cleanup_lasat(void)
+ {
+- if (mymtd) {
+- del_mtd_partitions(mymtd);
+- map_destroy(mymtd);
++ if (lasat_mtd) {
++ del_mtd_partitions(lasat_mtd);
++ map_destroy(lasat_mtd);
+ }
+- if (sp_map.map_priv_1) {
+- sp_map.map_priv_1 = 0;
++ if (lasat_map.virt) {
++ lasat_map.virt = 0;
+ }
+ }
+
+-module_init(init_sp);
+-module_exit(cleanup_sp);
++module_init(init_lasat);
++module_exit(cleanup_lasat);
+
+ MODULE_LICENSE("GPL");
+ MODULE_AUTHOR("Brian Murphy <brian@murphy.dk>");
+diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/lubbock-flash.c linux/drivers/mtd/maps/lubbock-flash.c
+--- linux-mips-2.4.27/drivers/mtd/maps/lubbock-flash.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/mtd/maps/lubbock-flash.c 2004-11-19 10:25:11.930195080 +0100
+@@ -0,0 +1,151 @@
++/*
++ * $Id$
++ *
++ * Map driver for the Lubbock developer platform.
++ *
++ * Author: Nicolas Pitre
++ * Copyright: (C) 2001 MontaVista Software Inc.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ */
++
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <asm/io.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/map.h>
++#include <linux/mtd/partitions.h>
++#include <asm/hardware.h>
++
++
++#define ROM_ADDR 0x00000000
++#define FLASH_ADDR 0x04000000
++
++#define WINDOW_SIZE 64*1024*1024
++
++static struct map_info lubbock_maps[2] = { {
++ .size = WINDOW_SIZE,
++ .phys = 0x00000000,
++}, {
++ .size = WINDOW_SIZE,
++ .phys = 0x04000000,
++} };
++
++static struct mtd_partition lubbock_partitions[] = {
++ {
++ .name = "Bootloader",
++ .size = 0x00040000,
++ .offset = 0,
++ .mask_flags = MTD_WRITEABLE /* force read-only */
++ },{
++ .name = "Kernel",
++ .size = 0x00100000,
++ .offset = 0x00040000,
++ },{
++ .name = "Filesystem",
++ .size = MTDPART_SIZ_FULL,
++ .offset = 0x00140000
++ }
++};
++
++static struct mtd_info *mymtds[2];
++static struct mtd_partition *parsed_parts[2];
++static int nr_parsed_parts[2];
++
++static const char *probes[] = { "RedBoot", "cmdlinepart", NULL };
++
++static int __init init_lubbock(void)
++{
++ int flashboot = (CONF_SWITCHES & 1);
++ int ret = 0, i;
++
++ lubbock_maps[0].buswidth = lubbock_maps[1].buswidth =
++ (BOOT_DEF & 1) ? 2 : 4;
++
++ /* Compensate for the nROMBT switch which swaps the flash banks */
++ printk(KERN_NOTICE "Lubbock configured to boot from %s (bank %d)\n",
++ flashboot?"Flash":"ROM", flashboot);
++
++ lubbock_maps[flashboot^1].name = "Lubbock Application Flash";
++ lubbock_maps[flashboot].name = "Lubbock Boot ROM";
++
++ for (i = 0; i < 2; i++) {
++ lubbock_maps[i].virt = (unsigned long)__ioremap(lubbock_maps[i].phys, WINDOW_SIZE, 0);
++ if (!lubbock_maps[i].virt) {
++ printk(KERN_WARNING "Failed to ioremap %s\n", lubbock_maps[i].name);
++ if (!ret)
++ ret = -ENOMEM;
++ continue;
++ }
++ simple_map_init(&lubbock_maps[i]);
++
++ printk(KERN_NOTICE "Probing %s at physical address 0x%08lx (%d-bit buswidth)\n",
++ lubbock_maps[i].name, lubbock_maps[i].phys,
++ lubbock_maps[i].buswidth * 8);
++
++ mymtds[i] = do_map_probe("cfi_probe", &lubbock_maps[i]);
++
++ if (!mymtds[i]) {
++ iounmap((void *)lubbock_maps[i].virt);
++ if (!ret)
++ ret = -EIO;
++ continue;
++ }
++ mymtds[i]->owner = THIS_MODULE;
++
++ int ret = parse_mtd_partitions(mymtds[i], probes,
++ &parsed_parts[i], 0);
++
++ if (ret > 0)
++ nr_parsed_parts[i] = ret;
++ }
++
++ if (!mymtds[0] && !mymtds[1])
++ return ret;
++
++ for (i = 0; i < 2; i++) {
++ if (!mymtds[i]) {
++ printk(KERN_WARNING "%s is absent. Skipping\n", lubbock_maps[i].name);
++ } else if (nr_parsed_parts[i]) {
++ add_mtd_partitions(mymtds[i], parsed_parts[i], nr_parsed_parts[i]);
++ } else if (!i) {
++ printk("Using static partitions on %s\n", lubbock_maps[i].name);
++ add_mtd_partitions(mymtds[i], lubbock_partitions, ARRAY_SIZE(lubbock_partitions));
++ } else {
++ printk("Registering %s as whole device\n", lubbock_maps[i].name);
++ add_mtd_device(mymtds[i]);
++ }
++ }
++ return 0;
++}
++
++static void __exit cleanup_lubbock(void)
++{
++ int i;
++ for (i = 0; i < 2; i++) {
++ if (!mymtds[i])
++ continue;
++
++ if (nr_parsed_parts[i] || !i)
++ del_mtd_partitions(mymtds[i]);
++ else
++ del_mtd_device(mymtds[i]);
++
++ map_destroy(mymtds[i]);
++ iounmap((void *)lubbock_maps[i].virt);
++
++ if (parsed_parts[i])
++ kfree(parsed_parts[i]);
++ }
++}
++
++module_init(init_lubbock);
++module_exit(cleanup_lubbock);
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Nicolas Pitre <nico@cam.org>");
++MODULE_DESCRIPTION("MTD map driver for Intel Lubbock");
+diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/map_funcs.c linux/drivers/mtd/maps/map_funcs.c
+--- linux-mips-2.4.27/drivers/mtd/maps/map_funcs.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/mtd/maps/map_funcs.c 2004-11-19 10:25:11.931194928 +0100
+@@ -0,0 +1,96 @@
++/*
++ * $Id$
++ *
++ * Out-of-line map I/O functions for simple maps when CONFIG_COMPLEX_MAPPINGS
++ * is enabled.
++ */
++
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/config.h>
++#include <linux/types.h>
++#include <linux/string.h>
++#include <asm/io.h>
++
++#include <linux/mtd/map.h>
++
++static u8 simple_map_read8(struct map_info *map, unsigned long ofs)
++{
++ return __raw_readb(map->virt + ofs);
++}
++
++static u16 simple_map_read16(struct map_info *map, unsigned long ofs)
++{
++ return __raw_readw(map->virt + ofs);
++}
++
++static u32 simple_map_read32(struct map_info *map, unsigned long ofs)
++{
++ return __raw_readl(map->virt + ofs);
++}
++
++static u64 simple_map_read64(struct map_info *map, unsigned long ofs)
++{
++#ifndef CONFIG_MTD_CFI_B8 /* 64-bit mappings */
++ BUG();
++ return 0;
++#else
++ return __raw_readll(map->virt + ofs);
++#endif
++}
++
++static void simple_map_write8(struct map_info *map, u8 datum, unsigned long ofs)
++{
++ __raw_writeb(datum, map->virt + ofs);
++ mb();
++}
++
++static void simple_map_write16(struct map_info *map, u16 datum, unsigned long ofs)
++{
++ __raw_writew(datum, map->virt + ofs);
++ mb();
++}
++
++static void simple_map_write32(struct map_info *map, u32 datum, unsigned long ofs)
++{
++ __raw_writel(datum, map->virt + ofs);
++ mb();
++}
++
++static void simple_map_write64(struct map_info *map, u64 datum, unsigned long ofs)
++{
++#ifndef CONFIG_MTD_CFI_B8 /* 64-bit mappings */
++ BUG();
++#else
++ __raw_writell(datum, map->virt + ofs);
++ mb();
++#endif /* CFI_B8 */
++}
++
++static void simple_map_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
++{
++ memcpy_fromio(to, map->virt + from, len);
++}
++
++static void simple_map_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
++{
++ memcpy_toio(map->virt + to, from, len);
++}
++
++void simple_map_init(struct map_info *map)
++{
++ map->read8 = simple_map_read8;
++ map->read16 = simple_map_read16;
++ map->read32 = simple_map_read32;
++ map->read64 = simple_map_read64;
++ map->write8 = simple_map_write8;
++ map->write16 = simple_map_write16;
++ map->write32 = simple_map_write32;
++ map->write64 = simple_map_write64;
++ map->copy_from = simple_map_copy_from;
++ map->copy_to = simple_map_copy_to;
++}
++
++EXPORT_SYMBOL(simple_map_init);
++
++MODULE_LICENSE("GPL");
+diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/mbx860.c linux/drivers/mtd/maps/mbx860.c
+--- linux-mips-2.4.27/drivers/mtd/maps/mbx860.c 2002-06-27 00:35:50.000000000 +0200
++++ linux/drivers/mtd/maps/mbx860.c 2004-11-19 10:25:11.933194624 +0100
+@@ -1,5 +1,5 @@
+ /*
+- * $Id$
++ * $Id$
+ *
+ * Handle mapping of the flash on MBX860 boards
+ *
+@@ -15,6 +15,7 @@
+ #include <linux/module.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
+ #include <asm/io.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+@@ -36,91 +37,46 @@
+ * single flash device into. If the size if zero we use up to the end of the
+ * device. */
+ static struct mtd_partition partition_info[]={
+- { name: "MBX flash BOOT partition",
+- offset: 0,
+- size: BOOT_PARTITION_SIZE_KiB*1024 },
+- { name: "MBX flash DATA partition",
+- offset: BOOT_PARTITION_SIZE_KiB*1024,
+- size: (KERNEL_PARTITION_SIZE_KiB)*1024 },
+- { name: "MBX flash APPLICATION partition",
+- offset: (BOOT_PARTITION_SIZE_KiB+KERNEL_PARTITION_SIZE_KiB)*1024 }
++ { .name = "MBX flash BOOT partition",
++ .offset = 0,
++ .size = BOOT_PARTITION_SIZE_KiB*1024 },
++ { .name = "MBX flash DATA partition",
++ .offset = BOOT_PARTITION_SIZE_KiB*1024,
++ .size = (KERNEL_PARTITION_SIZE_KiB)*1024 },
++ { .name = "MBX flash APPLICATION partition",
++ .offset = (BOOT_PARTITION_SIZE_KiB+KERNEL_PARTITION_SIZE_KiB)*1024 }
+ };
+
+
+ static struct mtd_info *mymtd;
+
+-__u8 mbx_read8(struct map_info *map, unsigned long ofs)
+-{
+- return readb(map->map_priv_1 + ofs);
+-}
+-
+-__u16 mbx_read16(struct map_info *map, unsigned long ofs)
+-{
+- return readw(map->map_priv_1 + ofs);
+-}
+-
+-__u32 mbx_read32(struct map_info *map, unsigned long ofs)
+-{
+- return readl(map->map_priv_1 + ofs);
+-}
+-
+-void mbx_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+-{
+- memcpy_fromio(to, (void *)(map->map_priv_1 + from), len);
+-}
+-
+-void mbx_write8(struct map_info *map, __u8 d, unsigned long adr)
+-{
+- writeb(d, map->map_priv_1 + adr);
+-}
+-
+-void mbx_write16(struct map_info *map, __u16 d, unsigned long adr)
+-{
+- writew(d, map->map_priv_1 + adr);
+-}
+-
+-void mbx_write32(struct map_info *map, __u32 d, unsigned long adr)
+-{
+- writel(d, map->map_priv_1 + adr);
+-}
+-
+-void mbx_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+-{
+- memcpy_toio((void *)(map->map_priv_1 + to), from, len);
+-}
+-
+ struct map_info mbx_map = {
+- name: "MBX flash",
+- size: WINDOW_SIZE,
+- buswidth: 4,
+- read8: mbx_read8,
+- read16: mbx_read16,
+- read32: mbx_read32,
+- copy_from: mbx_copy_from,
+- write8: mbx_write8,
+- write16: mbx_write16,
+- write32: mbx_write32,
+- copy_to: mbx_copy_to
++ .name = "MBX flash",
++ .size = WINDOW_SIZE,
++ .phys = WINDOW_ADDR,
++ .buswidth = 4,
+ };
+
+ int __init init_mbx(void)
+ {
+- printk(KERN_NOTICE "Motorola MBX flash device: %x at %x\n", WINDOW_SIZE*4, WINDOW_ADDR);
+- mbx_map.map_priv_1 = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE * 4);
++ printk(KERN_NOTICE "Motorola MBX flash device: 0x%x at 0x%x\n", WINDOW_SIZE*4, WINDOW_ADDR);
++ mbx_map.virt = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE * 4);
+
+- if (!mbx_map.map_priv_1) {
++ if (!mbx_map.virt) {
+ printk("Failed to ioremap\n");
+ return -EIO;
+ }
++ simple_map_init(&mbx_map);
++
+ mymtd = do_map_probe("jedec_probe", &mbx_map);
+ if (mymtd) {
+- mymtd->module = THIS_MODULE;
++ mymtd->owner = THIS_MODULE;
+ add_mtd_device(mymtd);
+ add_mtd_partitions(mymtd, partition_info, NUM_PARTITIONS);
+ return 0;
+ }
+
+- iounmap((void *)mbx_map.map_priv_1);
++ iounmap((void *)mbx_map.virt);
+ return -ENXIO;
+ }
+
+@@ -130,9 +86,9 @@
+ del_mtd_device(mymtd);
+ map_destroy(mymtd);
+ }
+- if (mbx_map.map_priv_1) {
+- iounmap((void *)mbx_map.map_priv_1);
+- mbx_map.map_priv_1 = 0;
++ if (mbx_map.virt) {
++ iounmap((void *)mbx_map.virt);
++ mbx_map.virt = 0;
+ }
+ }
+
+diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/mpc1211.c linux/drivers/mtd/maps/mpc1211.c
+--- linux-mips-2.4.27/drivers/mtd/maps/mpc1211.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/mtd/maps/mpc1211.c 2004-11-19 10:25:11.934194472 +0100
+@@ -0,0 +1,79 @@
++/*
++ * Flash on MPC-1211
++ *
++ * (C) 2002 Interface, Saito.K & Jeanne
++ *
++ * GPL'd
++ */
++
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/kernel.h>
++#include <asm/io.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/map.h>
++#include <linux/mtd/partitions.h>
++#include <linux/config.h>
++
++static struct mtd_info *flash_mtd;
++static struct mtd_partition *parsed_parts;
++
++struct map_info mpc1211_flash_map = {
++ .name = "MPC-1211 FLASH",
++ .size = 0x80000,
++ .buswidth = 1,
++};
++
++static struct mtd_partition mpc1211_partitions[] = {
++ {
++ .name = "IPL & ETH-BOOT",
++ .offset = 0x00000000,
++ .size = 0x10000,
++ },
++ {
++ .name = "Flash FS",
++ .offset = 0x00010000,
++ .size = MTDPART_SIZ_FULL,
++ }
++};
++
++static int __init init_mpc1211_maps(void)
++{
++ int nr_parts;
++
++ mpc1211_flash_map.phys = 0;
++ mpc1211_flash_map.virt = P2SEGADDR(0);
++
++ simple_map_init(&mpc1211_flash_map);
++
++ printk(KERN_NOTICE "Probing for flash chips at 0x00000000:\n");
++ flash_mtd = do_map_probe("jedec_probe", &mpc1211_flash_map);
++ if (!flash_mtd) {
++ printk(KERN_NOTICE "Flash chips not detected at either possible location.\n");
++ return -ENXIO;
++ }
++ printk(KERN_NOTICE "MPC-1211: Flash at 0x%08lx\n", mpc1211_flash_map.virt & 0x1fffffff);
++ flash_mtd->module = THIS_MODULE;
++
++ parsed_parts = mpc1211_partitions;
++ nr_parts = ARRAY_SIZE(mpc1211_partitions);
++
++ add_mtd_partitions(flash_mtd, parsed_parts, nr_parts);
++ return 0;
++}
++
++static void __exit cleanup_mpc1211_maps(void)
++{
++ if (parsed_parts)
++ del_mtd_partitions(flash_mtd);
++ else
++ del_mtd_device(flash_mtd);
++ map_destroy(flash_mtd);
++}
++
++module_init(init_mpc1211_maps);
++module_exit(cleanup_mpc1211_maps);
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Saito.K & Jeanne <ksaito@interface.co.jp>");
++MODULE_DESCRIPTION("MTD map driver for MPC-1211 boards. Interface");
+diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/netsc520.c linux/drivers/mtd/maps/netsc520.c
+--- linux-mips-2.4.27/drivers/mtd/maps/netsc520.c 2001-11-05 21:15:52.000000000 +0100
++++ linux/drivers/mtd/maps/netsc520.c 2004-11-19 10:25:11.935194320 +0100
+@@ -3,7 +3,7 @@
+ * Copyright (C) 2001 Mark Langsdorf (mark.langsdorf@amd.com)
+ * based on sc520cdp.c by Sysgo Real-Time Solutions GmbH
+ *
+- * $Id$
++ * $Id$
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -27,6 +27,7 @@
+ #include <linux/module.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
+ #include <asm/io.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+@@ -50,95 +51,41 @@
+ ** recoverable afterwards.
+ */
+
+-static __u8 netsc520_read8(struct map_info *map, unsigned long ofs)
+-{
+- return readb(map->map_priv_1 + ofs);
+-}
+-
+-static __u16 netsc520_read16(struct map_info *map, unsigned long ofs)
+-{
+- return readw(map->map_priv_1 + ofs);
+-}
+-
+-static __u32 netsc520_read32(struct map_info *map, unsigned long ofs)
+-{
+- return readl(map->map_priv_1 + ofs);
+-}
+-
+-static void netsc520_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+-{
+- memcpy_fromio(to, (void *)(map->map_priv_1 + from), len);
+-}
+-
+-static void netsc520_write8(struct map_info *map, __u8 d, unsigned long adr)
+-{
+- writeb(d, map->map_priv_1 + adr);
+-}
+-
+-static void netsc520_write16(struct map_info *map, __u16 d, unsigned long adr)
+-{
+- writew(d, map->map_priv_1 + adr);
+-}
+-
+-static void netsc520_write32(struct map_info *map, __u32 d, unsigned long adr)
+-{
+- writel(d, map->map_priv_1 + adr);
+-}
+-
+-static void netsc520_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+-{
+- memcpy_toio((void *)(map->map_priv_1 + to), from, len);
+-}
+-
+ /* partition_info gives details on the logical partitions that the split the
+ * single flash device into. If the size if zero we use up to the end of the
+ * device. */
+ static struct mtd_partition partition_info[]={
+ {
+- name: "NetSc520 boot kernel",
+- offset: 0,
+- size: 0xc0000
++ .name = "NetSc520 boot kernel",
++ .offset = 0,
++ .size = 0xc0000
+ },
+ {
+- name: "NetSc520 Low BIOS",
+- offset: 0xc0000,
+- size: 0x40000
++ .name = "NetSc520 Low BIOS",
++ .offset = 0xc0000,
++ .size = 0x40000
+ },
+ {
+- name: "NetSc520 file system",
+- offset: 0x100000,
+- size: 0xe80000
++ .name = "NetSc520 file system",
++ .offset = 0x100000,
++ .size = 0xe80000
+ },
+ {
+- name: "NetSc520 High BIOS",
+- offset: 0xf80000,
+- size: 0x80000
++ .name = "NetSc520 High BIOS",
++ .offset = 0xf80000,
++ .size = 0x80000
+ },
+ };
+ #define NUM_PARTITIONS (sizeof(partition_info)/sizeof(partition_info[0]))
+
+-/*
+- * If no idea what is going on here. This is taken from the FlashFX stuff.
+- */
+-#define ROMCS 1
+-
+-
+ #define WINDOW_SIZE 0x00100000
+ #define WINDOW_ADDR 0x00200000
+
+ static struct map_info netsc520_map = {
+- name: "netsc520 Flash Bank",
+- size: WINDOW_SIZE,
+- buswidth: 4,
+- read8: netsc520_read8,
+- read16: netsc520_read16,
+- read32: netsc520_read32,
+- copy_from: netsc520_copy_from,
+- write8: netsc520_write8,
+- write16: netsc520_write16,
+- write32: netsc520_write32,
+- copy_to: netsc520_copy_to,
+- map_priv_2: WINDOW_ADDR
++ .name = "netsc520 Flash Bank",
++ .size = WINDOW_SIZE,
++ .buswidth = 4,
++ .phys = WINDOW_ADDR,
+ };
+
+ #define NUM_FLASH_BANKS (sizeof(netsc520_map)/sizeof(struct map_info))
+@@ -147,13 +94,16 @@
+
+ static int __init init_netsc520(void)
+ {
+- printk(KERN_NOTICE "NetSc520 flash device: %lx at %lx\n", netsc520_map.size, netsc520_map.map_priv_2);
+- netsc520_map.map_priv_1 = (unsigned long)ioremap_nocache(netsc520_map.map_priv_2, netsc520_map.size);
++ printk(KERN_NOTICE "NetSc520 flash device: 0x%lx at 0x%lx\n", netsc520_map.size, netsc520_map.phys);
++ netsc520_map.virt = (unsigned long)ioremap_nocache(netsc520_map.phys, netsc520_map.size);
+
+- if (!netsc520_map.map_priv_1) {
++ if (!netsc520_map.virt) {
+ printk("Failed to ioremap_nocache\n");
+ return -EIO;
+ }
++
++ simple_map_init(&netsc520_map);
++
+ mymtd = do_map_probe("cfi_probe", &netsc520_map);
+ if(!mymtd)
+ mymtd = do_map_probe("map_ram", &netsc520_map);
+@@ -161,11 +111,11 @@
+ mymtd = do_map_probe("map_rom", &netsc520_map);
+
+ if (!mymtd) {
+- iounmap((void *)netsc520_map.map_priv_1);
++ iounmap((void *)netsc520_map.virt);
+ return -ENXIO;
+ }
+
+- mymtd->module = THIS_MODULE;
++ mymtd->owner = THIS_MODULE;
+ add_mtd_partitions( mymtd, partition_info, NUM_PARTITIONS );
+ return 0;
+ }
+@@ -176,9 +126,9 @@
+ del_mtd_partitions(mymtd);
+ map_destroy(mymtd);
+ }
+- if (netsc520_map.map_priv_1) {
+- iounmap((void *)netsc520_map.map_priv_1);
+- netsc520_map.map_priv_1 = 0;
++ if (netsc520_map.virt) {
++ iounmap((void *)netsc520_map.virt);
++ netsc520_map.virt = 0;
+ }
+ }
+
+diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/nettel.c linux/drivers/mtd/maps/nettel.c
+--- linux-mips-2.4.27/drivers/mtd/maps/nettel.c 2003-02-26 01:53:49.000000000 +0100
++++ linux/drivers/mtd/maps/nettel.c 2004-11-19 10:25:11.937194016 +0100
+@@ -6,7 +6,7 @@
+ * (C) Copyright 2000-2001, Greg Ungerer (gerg@snapgear.com)
+ * (C) Copyright 2001-2002, SnapGear (www.snapgear.com)
+ *
+- * $Id$
++ * $Id$
+ */
+
+ /****************************************************************************/
+@@ -59,128 +59,72 @@
+
+ /****************************************************************************/
+
+-static __u8 nettel_read8(struct map_info *map, unsigned long ofs)
+-{
+- return(readb(map->map_priv_1 + ofs));
+-}
+-
+-static __u16 nettel_read16(struct map_info *map, unsigned long ofs)
+-{
+- return(readw(map->map_priv_1 + ofs));
+-}
+-
+-static __u32 nettel_read32(struct map_info *map, unsigned long ofs)
+-{
+- return(readl(map->map_priv_1 + ofs));
+-}
+-
+-static void nettel_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+-{
+- memcpy_fromio(to, map->map_priv_1 + from, len);
+-}
+-
+-static void nettel_write8(struct map_info *map, __u8 d, unsigned long adr)
+-{
+- writeb(d, map->map_priv_1 + adr);
+-}
+-
+-static void nettel_write16(struct map_info *map, __u16 d, unsigned long adr)
+-{
+- writew(d, map->map_priv_1 + adr);
+-}
+-
+-static void nettel_write32(struct map_info *map, __u32 d, unsigned long adr)
+-{
+- writel(d, map->map_priv_1 + adr);
+-}
+-
+-static void nettel_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+-{
+- memcpy_toio(map->map_priv_1 + to, from, len);
+-}
+-
+ /****************************************************************************/
+
+ #ifdef CONFIG_MTD_CFI_INTELEXT
+ static struct map_info nettel_intel_map = {
+- name: "SnapGear Intel",
+- size: 0,
+- buswidth: INTEL_BUSWIDTH,
+- read8: nettel_read8,
+- read16: nettel_read16,
+- read32: nettel_read32,
+- copy_from: nettel_copy_from,
+- write8: nettel_write8,
+- write16: nettel_write16,
+- write32: nettel_write32,
+- copy_to: nettel_copy_to
++ .name = "SnapGear Intel",
++ .size = 0,
++ .buswidth = INTEL_BUSWIDTH,
+ };
+
+ static struct mtd_partition nettel_intel_partitions[] = {
+ {
+- name: "SnapGear kernel",
+- offset: 0,
+- size: 0x000e0000
++ .name = "SnapGear kernel",
++ .offset = 0,
++ .size = 0x000e0000
+ },
+ {
+- name: "SnapGear filesystem",
+- offset: 0x00100000,
++ .name = "SnapGear filesystem",
++ .offset = 0x00100000,
+ },
+ {
+- name: "SnapGear config",
+- offset: 0x000e0000,
+- size: 0x00020000
++ .name = "SnapGear config",
++ .offset = 0x000e0000,
++ .size = 0x00020000
+ },
+ {
+- name: "SnapGear Intel",
+- offset: 0
++ .name = "SnapGear Intel",
++ .offset = 0
+ },
+ {
+- name: "SnapGear BIOS Config",
+- offset: 0x007e0000,
+- size: 0x00020000
++ .name = "SnapGear BIOS Config",
++ .offset = 0x007e0000,
++ .size = 0x00020000
+ },
+ {
+- name: "SnapGear BIOS",
+- offset: 0x007e0000,
+- size: 0x00020000
++ .name = "SnapGear BIOS",
++ .offset = 0x007e0000,
++ .size = 0x00020000
+ },
+ };
+ #endif
+
+ static struct map_info nettel_amd_map = {
+- name: "SnapGear AMD",
+- size: AMD_WINDOW_MAXSIZE,
+- buswidth: AMD_BUSWIDTH,
+- read8: nettel_read8,
+- read16: nettel_read16,
+- read32: nettel_read32,
+- copy_from: nettel_copy_from,
+- write8: nettel_write8,
+- write16: nettel_write16,
+- write32: nettel_write32,
+- copy_to: nettel_copy_to
++ .name = "SnapGear AMD",
++ .size = AMD_WINDOW_MAXSIZE,
++ .buswidth = AMD_BUSWIDTH,
+ };
+
+ static struct mtd_partition nettel_amd_partitions[] = {
+ {
+- name: "SnapGear BIOS config",
+- offset: 0x000e0000,
+- size: 0x00010000
++ .name = "SnapGear BIOS config",
++ .offset = 0x000e0000,
++ .size = 0x00010000
+ },
+ {
+- name: "SnapGear BIOS",
+- offset: 0x000f0000,
+- size: 0x00010000
++ .name = "SnapGear BIOS",
++ .offset = 0x000f0000,
++ .size = 0x00010000
+ },
+ {
+- name: "SnapGear AMD",
+- offset: 0
++ .name = "SnapGear AMD",
++ .offset = 0
+ },
+ {
+- name: "SnapGear high BIOS",
+- offset: 0x001f0000,
+- size: 0x00010000
++ .name = "SnapGear high BIOS",
++ .offset = 0x001f0000,
++ .size = 0x00010000
+ }
+ };
+
+@@ -328,18 +272,20 @@
+ *amdpar = SC520_PAR(SC520_PAR_BOOTCS, amdaddr, maxsize);
+ __asm__ ("wbinvd");
+
+- nettel_amd_map.map_priv_1 = (unsigned long)
++ nettel_amd_map.phys = amdaddr;
++ nettel_amd_map.virt = (unsigned long)
+ ioremap_nocache(amdaddr, maxsize);
+- if (!nettel_amd_map.map_priv_1) {
++ if (!nettel_amd_map.virt) {
+ printk("SNAPGEAR: failed to ioremap() BOOTCS\n");
+ return(-EIO);
+ }
++ simple_map_init(&nettel_amd_map);
+
+ if ((amd_mtd = do_map_probe("jedec_probe", &nettel_amd_map))) {
+ printk(KERN_NOTICE "SNAPGEAR: AMD flash device size = %dK\n",
+ amd_mtd->size>>10);
+
+- amd_mtd->module = THIS_MODULE;
++ amd_mtd->owner = THIS_MODULE;
+
+ /* The high BIOS partition is only present for 2MB units */
+ num_amd_partitions = NUM_AMD_PARTITIONS;
+@@ -387,8 +333,8 @@
+
+ /* Destroy useless AMD MTD mapping */
+ amd_mtd = NULL;
+- iounmap((void *) nettel_amd_map.map_priv_1);
+- nettel_amd_map.map_priv_1 = (unsigned long) NULL;
++ iounmap((void *) nettel_amd_map.virt);
++ nettel_amd_map.virt = (unsigned long) NULL;
+ #else
+ /* Only AMD flash supported */
+ return(-ENXIO);
+@@ -411,16 +357,18 @@
+
+ /* Probe for the the size of the first Intel flash */
+ nettel_intel_map.size = maxsize;
+- nettel_intel_map.map_priv_1 = (unsigned long)
++ nettel_intel_map.phys = intel0addr;
++ nettel_intel_map.virt = (unsigned long)
+ ioremap_nocache(intel0addr, maxsize);
+- if (!nettel_intel_map.map_priv_1) {
++ if (!nettel_intel_map.virt) {
+ printk("SNAPGEAR: failed to ioremap() ROMCS1\n");
+ return(-EIO);
+ }
++ simple_map_init(&nettel_intel_map);
+
+ intel_mtd = do_map_probe("cfi_probe", &nettel_intel_map);
+ if (! intel_mtd) {
+- iounmap((void *) nettel_intel_map.map_priv_1);
++ iounmap((void *) nettel_intel_map.virt);
+ return(-ENXIO);
+ }
+
+@@ -441,19 +389,19 @@
+ /* Delete the old map and probe again to do both chips */
+ map_destroy(intel_mtd);
+ intel_mtd = NULL;
+- iounmap((void *) nettel_intel_map.map_priv_1);
++ iounmap((void *) nettel_intel_map.virt);
+
+ nettel_intel_map.size = maxsize;
+- nettel_intel_map.map_priv_1 = (unsigned long)
++ nettel_intel_map.virt = (unsigned long)
+ ioremap_nocache(intel0addr, maxsize);
+- if (!nettel_intel_map.map_priv_1) {
++ if (!nettel_intel_map.virt) {
+ printk("SNAPGEAR: failed to ioremap() ROMCS1/2\n");
+ return(-EIO);
+ }
+
+ intel_mtd = do_map_probe("cfi_probe", &nettel_intel_map);
+ if (! intel_mtd) {
+- iounmap((void *) nettel_intel_map.map_priv_1);
++ iounmap((void *) nettel_intel_map.virt);
+ return(-ENXIO);
+ }
+
+@@ -468,7 +416,7 @@
+ printk(KERN_NOTICE "SNAPGEAR: Intel flash device size = %dK\n",
+ (intel_mtd->size >> 10));
+
+- intel_mtd->module = THIS_MODULE;
++ intel_mtd->owner = THIS_MODULE;
+
+ #ifndef CONFIG_BLK_DEV_INITRD
+ ROOT_DEV = MKDEV(MTD_BLOCK_MAJOR, 1);
+@@ -523,18 +471,18 @@
+ del_mtd_partitions(amd_mtd);
+ map_destroy(amd_mtd);
+ }
+- if (nettel_amd_map.map_priv_1) {
+- iounmap((void *)nettel_amd_map.map_priv_1);
+- nettel_amd_map.map_priv_1 = 0;
++ if (nettel_amd_map.virt) {
++ iounmap((void *)nettel_amd_map.virt);
++ nettel_amd_map.virt = 0;
+ }
+ #ifdef CONFIG_MTD_CFI_INTELEXT
+ if (intel_mtd) {
+ del_mtd_partitions(intel_mtd);
+ map_destroy(intel_mtd);
+ }
+- if (nettel_intel_map.map_priv_1) {
+- iounmap((void *)nettel_intel_map.map_priv_1);
+- nettel_intel_map.map_priv_1 = 0;
++ if (nettel_intel_map.virt) {
++ iounmap((void *)nettel_intel_map.virt);
++ nettel_intel_map.virt = 0;
+ }
+ #endif
+ }
+diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/ocelot.c linux/drivers/mtd/maps/ocelot.c
+--- linux-mips-2.4.27/drivers/mtd/maps/ocelot.c 2001-11-05 21:15:52.000000000 +0100
++++ linux/drivers/mtd/maps/ocelot.c 2004-11-19 10:25:11.938193864 +0100
+@@ -1,5 +1,5 @@
+ /*
+- * $Id$
++ * $Id$
+ *
+ * Flash on Momenco Ocelot
+ */
+@@ -7,6 +7,7 @@
+ #include <linux/module.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
+ #include <asm/io.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+@@ -20,47 +21,23 @@
+ #define NVRAM_WINDOW_SIZE 0x00007FF0
+ #define NVRAM_BUSWIDTH 1
+
+-extern int parse_redboot_partitions(struct mtd_info *master, struct mtd_partition **pparts);
+-
+ static unsigned int cacheflush = 0;
+
+ static struct mtd_info *flash_mtd;
+ static struct mtd_info *nvram_mtd;
+
+-__u8 ocelot_read8(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readb(map->map_priv_1 + ofs);
+-}
+-
+-void ocelot_write8(struct map_info *map, __u8 d, unsigned long adr)
+-{
+- cacheflush = 1;
+- __raw_writeb(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-void ocelot_copy_from_cache(struct map_info *map, void *to, unsigned long from, ssize_t len)
+-{
+- if (cacheflush) {
+- dma_cache_inv(map->map_priv_2, map->size);
+- cacheflush = 0;
+- }
+- memcpy_fromio(to, map->map_priv_1 + from, len);
+-}
+-
+-void ocelot_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
++static void ocelot_ram_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf)
+ {
+- memcpy_fromio(to, map->map_priv_1 + from, len);
+-}
++ struct map_info *map = (struct map_info *)mtd->priv;
++ size_t done = 0;
+
+-void ocelot_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+-{
+ /* If we use memcpy, it does word-wide writes. Even though we told the
+ GT64120A that it's an 8-bit wide region, word-wide writes don't work.
+ We end up just writing the first byte of the four to all four bytes.
+ So we have this loop instead */
++ *retlen = len;
+ while(len) {
+- __raw_writeb(*(unsigned char *) from, map->map_priv_1 + to);
++ __raw_writeb(*(unsigned char *) from, map->virt + to);
+ from++;
+ to++;
+ len--;
+@@ -70,24 +47,21 @@
+ static struct mtd_partition *parsed_parts;
+
+ struct map_info ocelot_flash_map = {
+- name: "Ocelot boot flash",
+- size: FLASH_WINDOW_SIZE,
+- buswidth: FLASH_BUSWIDTH,
+- read8: ocelot_read8,
+- copy_from: ocelot_copy_from_cache,
+- write8: ocelot_write8,
++ .name = "Ocelot boot flash",
++ .size = FLASH_WINDOW_SIZE,
++ .buswidth = FLASH_BUSWIDTH,
++ .phys = FLASH_WINDOW_ADDR,
+ };
+
+ struct map_info ocelot_nvram_map = {
+- name: "Ocelot NVRAM",
+- size: NVRAM_WINDOW_SIZE,
+- buswidth: NVRAM_BUSWIDTH,
+- read8: ocelot_read8,
+- copy_from: ocelot_copy_from,
+- write8: ocelot_write8,
+- copy_to: ocelot_copy_to
++ .name = "Ocelot NVRAM",
++ .size = NVRAM_WINDOW_SIZE,
++ .buswidth = NVRAM_BUSWIDTH,
++ .phys = NVRAM_WINDOW_ADDR,
+ };
+
++static const char *probes[] = { "RedBoot", NULL };
++
+ static int __init init_ocelot_maps(void)
+ {
+ void *pld;
+@@ -107,12 +81,13 @@
+ iounmap(pld);
+
+ /* Now ioremap the NVRAM space */
+- ocelot_nvram_map.map_priv_1 = (unsigned long)ioremap_nocache(NVRAM_WINDOW_ADDR, NVRAM_WINDOW_SIZE);
+- if (!ocelot_nvram_map.map_priv_1) {
++ ocelot_nvram_map.virt = (unsigned long)ioremap_nocache(NVRAM_WINDOW_ADDR, NVRAM_WINDOW_SIZE);
++ if (!ocelot_nvram_map.virt) {
+ printk(KERN_NOTICE "Failed to ioremap Ocelot NVRAM space\n");
+ return -EIO;
+ }
+- // ocelot_nvram_map.map_priv_2 = ocelot_nvram_map.map_priv_1;
++
++ simple_map_init(&ocelot_nvram_map);
+
+ /* And do the RAM probe on it to get an MTD device */
+ nvram_mtd = do_map_probe("map_ram", &ocelot_nvram_map);
+@@ -120,22 +95,21 @@
+ printk("NVRAM probe failed\n");
+ goto fail_1;
+ }
+- nvram_mtd->module = THIS_MODULE;
++ nvram_mtd->owner = THIS_MODULE;
+ nvram_mtd->erasesize = 16;
++ /* Override the write() method */
++ nvram_mtd->write = ocelot_ram_write;
+
+ /* Now map the flash space */
+- ocelot_flash_map.map_priv_1 = (unsigned long)ioremap_nocache(FLASH_WINDOW_ADDR, FLASH_WINDOW_SIZE);
+- if (!ocelot_flash_map.map_priv_1) {
++ ocelot_flash_map.virt = (unsigned long)ioremap_nocache(FLASH_WINDOW_ADDR, FLASH_WINDOW_SIZE);
++ if (!ocelot_flash_map.virt) {
+ printk(KERN_NOTICE "Failed to ioremap Ocelot flash space\n");
+ goto fail_2;
+ }
+ /* Now the cached version */
+- ocelot_flash_map.map_priv_2 = (unsigned long)__ioremap(FLASH_WINDOW_ADDR, FLASH_WINDOW_SIZE, 0);
++ ocelot_flash_map.cached = (unsigned long)__ioremap(FLASH_WINDOW_ADDR, FLASH_WINDOW_SIZE, 0);
+
+- if (!ocelot_flash_map.map_priv_2) {
+- /* Doesn't matter if it failed. Just use the uncached version */
+- ocelot_flash_map.map_priv_2 = ocelot_flash_map.map_priv_1;
+- }
++ simple_map_init(&ocelot_flash_map);
+
+ /* Only probe for flash if the write jumper is present */
+ if (brd_status & 0x40) {
+@@ -155,10 +129,10 @@
+
+ add_mtd_device(nvram_mtd);
+
+- flash_mtd->module = THIS_MODULE;
+- nr_parts = parse_redboot_partitions(flash_mtd, &parsed_parts);
++ flash_mtd->owner = THIS_MODULE;
++ nr_parts = parse_mtd_partitions(flash_mtd, probes, &parsed_parts, 0);
+
+- if (nr_parts)
++ if (nr_parts > 0)
+ add_mtd_partitions(flash_mtd, parsed_parts, nr_parts);
+ else
+ add_mtd_device(flash_mtd);
+@@ -166,14 +140,13 @@
+ return 0;
+
+ fail3:
+- iounmap((void *)ocelot_flash_map.map_priv_1);
+- if (ocelot_flash_map.map_priv_2 &&
+- ocelot_flash_map.map_priv_2 != ocelot_flash_map.map_priv_1)
+- iounmap((void *)ocelot_flash_map.map_priv_2);
++ iounmap((void *)ocelot_flash_map.virt);
++ if (ocelot_flash_map.cached)
++ iounmap((void *)ocelot_flash_map.cached);
+ fail_2:
+ map_destroy(nvram_mtd);
+ fail_1:
+- iounmap((void *)ocelot_nvram_map.map_priv_1);
++ iounmap((void *)ocelot_nvram_map.virt);
+
+ return -ENXIO;
+ }
+@@ -182,16 +155,16 @@
+ {
+ del_mtd_device(nvram_mtd);
+ map_destroy(nvram_mtd);
+- iounmap((void *)ocelot_nvram_map.map_priv_1);
++ iounmap((void *)ocelot_nvram_map.virt);
+
+ if (parsed_parts)
+ del_mtd_partitions(flash_mtd);
+ else
+ del_mtd_device(flash_mtd);
+ map_destroy(flash_mtd);
+- iounmap((void *)ocelot_flash_map.map_priv_1);
+- if (ocelot_flash_map.map_priv_2 != ocelot_flash_map.map_priv_1)
+- iounmap((void *)ocelot_flash_map.map_priv_2);
++ iounmap((void *)ocelot_flash_map.virt);
++ if (ocelot_flash_map.cached)
++ iounmap((void *)ocelot_flash_map.cached);
+ }
+
+ module_init(init_ocelot_maps);
+diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/octagon-5066.c linux/drivers/mtd/maps/octagon-5066.c
+--- linux-mips-2.4.27/drivers/mtd/maps/octagon-5066.c 2003-08-13 19:19:18.000000000 +0200
++++ linux/drivers/mtd/maps/octagon-5066.c 2004-11-19 10:25:11.940193560 +0100
+@@ -1,4 +1,4 @@
+-// $Id$
++// $Id$
+ /* ######################################################################
+
+ Octagon 5066 MTD Driver.
+@@ -31,6 +31,7 @@
+ #include <asm/io.h>
+
+ #include <linux/mtd/map.h>
++#include <linux/mtd/mtd.h>
+
+ #define WINDOW_START 0xe8000
+ #define WINDOW_LENGTH 0x8000
+@@ -151,32 +152,34 @@
+
+ static struct map_info oct5066_map[2] = {
+ {
+- name: "Octagon 5066 Socket",
+- size: 512 * 1024,
+- buswidth: 1,
+- read8: oct5066_read8,
+- read16: oct5066_read16,
+- read32: oct5066_read32,
+- copy_from: oct5066_copy_from,
+- write8: oct5066_write8,
+- write16: oct5066_write16,
+- write32: oct5066_write32,
+- copy_to: oct5066_copy_to,
+- map_priv_1: 1<<6
++ .name = "Octagon 5066 Socket",
++ .phys = NO_XIP,
++ .size = 512 * 1024,
++ .buswidth = 1,
++ .read8 = oct5066_read8,
++ .read16 = oct5066_read16,
++ .read32 = oct5066_read32,
++ .copy_from = oct5066_copy_from,
++ .write8 = oct5066_write8,
++ .write16 = oct5066_write16,
++ .write32 = oct5066_write32,
++ .copy_to = oct5066_copy_to,
++ .map_priv_1 = 1<<6
+ },
+ {
+- name: "Octagon 5066 Internal Flash",
+- size: 2 * 1024 * 1024,
+- buswidth: 1,
+- read8: oct5066_read8,
+- read16: oct5066_read16,
+- read32: oct5066_read32,
+- copy_from: oct5066_copy_from,
+- write8: oct5066_write8,
+- write16: oct5066_write16,
+- write32: oct5066_write32,
+- copy_to: oct5066_copy_to,
+- map_priv_1: 2<<6
++ .name = "Octagon 5066 Internal Flash",
++ .phys = NO_XIP,
++ .size = 2 * 1024 * 1024,
++ .buswidth = 1,
++ .read8 = oct5066_read8,
++ .read16 = oct5066_read16,
++ .read32 = oct5066_read32,
++ .copy_from = oct5066_copy_from,
++ .write8 = oct5066_write8,
++ .write16 = oct5066_write16,
++ .write32 = oct5066_write32,
++ .copy_to = oct5066_copy_to,
++ .map_priv_1 = 2<<6
+ }
+ };
+
+@@ -244,6 +247,7 @@
+ }
+ if (OctProbe() != 0) {
+ printk(KERN_NOTICE "5066: Octagon Probe Failed, is this an Octagon 5066 SBC?\n");
++ iounmap((void *)iomapadr);
+ ret = -EAGAIN;
+ goto out_unmap;
+ }
+@@ -261,7 +265,7 @@
+ if (!oct5066_mtd[i])
+ oct5066_mtd[i] = do_map_probe("map_rom", &oct5066_map[i]);
+ if (oct5066_mtd[i]) {
+- oct5066_mtd[i]->module = THIS_MODULE;
++ oct5066_mtd[i]->owner = THIS_MODULE;
+ add_mtd_device(oct5066_mtd[i]);
+ }
+ }
+diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/omap-toto-flash.c linux/drivers/mtd/maps/omap-toto-flash.c
+--- linux-mips-2.4.27/drivers/mtd/maps/omap-toto-flash.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/mtd/maps/omap-toto-flash.c 2004-11-19 10:25:11.941193408 +0100
+@@ -0,0 +1,137 @@
++/*
++ * NOR Flash memory access on TI Toto board
++ *
++ * jzhang@ti.com (C) 2003 Texas Instruments.
++ *
++ * (C) 2002 MontVista Software, Inc.
++ *
++ * $Id$
++ */
++
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/kernel.h>
++
++#include <linux/errno.h>
++#include <linux/init.h>
++
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/map.h>
++#include <linux/mtd/partitions.h>
++
++#include <asm/hardware.h>
++#include <asm/io.h>
++
++
++#ifndef CONFIG_ARCH_OMAP
++#error This is for OMAP architecture only
++#endif
++
++//these lines need be moved to a hardware header file
++#define OMAP_TOTO_FLASH_BASE 0xd8000000
++#define OMAP_TOTO_FLASH_SIZE 0x80000
++
++static struct map_info omap_toto_map_flash = {
++ .name = "OMAP Toto flash",
++ .buswidth = 2,
++ .virt = OMAP_TOTO_FLASH_BASE,
++};
++
++
++static struct mtd_partition toto_flash_partitions[] = {
++ {
++ .name = "BootLoader",
++ .size = 0x00040000, /* hopefully u-boot will stay 128k + 128*/
++ .offset = 0,
++ .mask_flags = MTD_WRITEABLE, /* force read-only */
++ }, {
++ .name = "ReservedSpace",
++ .size = 0x00030000,
++ .offset = MTDPART_OFS_APPEND,
++ //mask_flags: MTD_WRITEABLE, /* force read-only */
++ }, {
++ .name = "EnvArea", /* bottom 64KiB for env vars */
++ .size = MTDPART_SIZ_FULL,
++ .offset = MTDPART_OFS_APPEND,
++ }
++};
++
++static struct mtd_partition *parsed_parts;
++
++static struct mtd_info *flash_mtd;
++
++static int __init init_flash (void)
++{
++
++ struct mtd_partition *parts;
++ int nb_parts = 0;
++ int parsed_nr_parts = 0;
++ const char *part_type;
++
++ /*
++ * Static partition definition selection
++ */
++ part_type = "static";
++
++ parts = toto_flash_partitions;
++ nb_parts = ARRAY_SIZE(toto_flash_partitions);
++ omap_toto_map_flash.size = OMAP_TOTO_FLASH_SIZE;
++ omap_toto_map_flash.phys = virt_to_phys(OMAP_TOTO_FLASH_BASE);
++
++ simple_map_init(&omap_toto_map_flash);
++ /*
++ * Now let's probe for the actual flash. Do it here since
++ * specific machine settings might have been set above.
++ */
++ printk(KERN_NOTICE "OMAP toto flash: probing %d-bit flash bus\n",
++ omap_toto_map_flash.buswidth*8);
++ flash_mtd = do_map_probe("jedec_probe", &omap_toto_map_flash);
++ if (!flash_mtd)
++ return -ENXIO;
++
++ if (parsed_nr_parts > 0) {
++ parts = parsed_parts;
++ nb_parts = parsed_nr_parts;
++ }
++
++ if (nb_parts == 0) {
++ printk(KERN_NOTICE "OMAP toto flash: no partition info available,"
++ "registering whole flash at once\n");
++ if (add_mtd_device(flash_mtd)){
++ return -ENXIO;
++ }
++ } else {
++ printk(KERN_NOTICE "Using %s partition definition\n",
++ part_type);
++ return add_mtd_partitions(flash_mtd, parts, nb_parts);
++ }
++ return 0;
++}
++
++int __init omap_toto_mtd_init(void)
++{
++ int status;
++
++ if (status = init_flash()) {
++ printk(KERN_ERR "OMAP Toto Flash: unable to init map for toto flash\n");
++ }
++ return status;
++}
++
++static void __exit omap_toto_mtd_cleanup(void)
++{
++ if (flash_mtd) {
++ del_mtd_partitions(flash_mtd);
++ map_destroy(flash_mtd);
++ if (parsed_parts)
++ kfree(parsed_parts);
++ }
++}
++
++module_init(omap_toto_mtd_init);
++module_exit(omap_toto_mtd_cleanup);
++
++MODULE_AUTHOR("Jian Zhang");
++MODULE_DESCRIPTION("OMAP Toto board map driver");
++MODULE_LICENSE("GPL");
+diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/pb1xxx-flash.c linux/drivers/mtd/maps/pb1xxx-flash.c
+--- linux-mips-2.4.27/drivers/mtd/maps/pb1xxx-flash.c 2003-05-19 08:27:22.000000000 +0200
++++ linux/drivers/mtd/maps/pb1xxx-flash.c 2004-11-19 10:25:11.943193104 +0100
+@@ -3,12 +3,13 @@
+ *
+ * (C) 2001 Pete Popov <ppopov@mvista.com>
+ *
+- * $Id$
++ * $Id$
+ */
+
+ #include <linux/config.h>
+ #include <linux/module.h>
+ #include <linux/types.h>
++#include <linux/init.h>
+ #include <linux/kernel.h>
+
+ #include <linux/mtd/mtd.h>
+@@ -25,210 +26,110 @@
+ #endif
+
+ #ifdef CONFIG_MIPS_PB1000
++
+ #define WINDOW_ADDR 0x1F800000
+ #define WINDOW_SIZE 0x800000
+-#endif
+-
+-__u8 physmap_read8(struct map_info *map, unsigned long ofs)
+-{
+- __u8 ret;
+- ret = __raw_readb(map->map_priv_1 + ofs);
+- DBG("read8 from %x, %x\n", (unsigned)(map->map_priv_1 + ofs), ret);
+- return ret;
+-}
+-
+-__u16 physmap_read16(struct map_info *map, unsigned long ofs)
+-{
+- __u16 ret;
+- ret = __raw_readw(map->map_priv_1 + ofs);
+- DBG("read16 from %x, %x\n", (unsigned)(map->map_priv_1 + ofs), ret);
+- return ret;
+-}
+-
+-__u32 physmap_read32(struct map_info *map, unsigned long ofs)
+-{
+- __u32 ret;
+- ret = __raw_readl(map->map_priv_1 + ofs);
+- DBG("read32 from %x, %x\n", (unsigned)(map->map_priv_1 + ofs), ret);
+- return ret;
+-}
+-
+-void physmap_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+-{
+- DBG("physmap_copy from %x to %x\n", (unsigned)from, (unsigned)to);
+- memcpy_fromio(to, map->map_priv_1 + from, len);
+-}
+-
+-void physmap_write8(struct map_info *map, __u8 d, unsigned long adr)
+-{
+- DBG("write8 at %x, %x\n", (unsigned)(map->map_priv_1 + adr), d);
+- __raw_writeb(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-void physmap_write16(struct map_info *map, __u16 d, unsigned long adr)
+-{
+- DBG("write16 at %x, %x\n", (unsigned)(map->map_priv_1 + adr), d);
+- __raw_writew(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-void physmap_write32(struct map_info *map, __u32 d, unsigned long adr)
+-{
+- DBG("write32 at %x, %x\n", (unsigned)(map->map_priv_1 + adr), d);
+- __raw_writel(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-void physmap_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+-{
+- DBG("physmap_copy_to %x from %x\n", (unsigned)to, (unsigned)from);
+- memcpy_toio(map->map_priv_1 + to, from, len);
+-}
+-
+-
+-
+-static struct map_info pb1xxx_map = {
+- name: "Pb1xxx flash",
+- read8: physmap_read8,
+- read16: physmap_read16,
+- read32: physmap_read32,
+- copy_from: physmap_copy_from,
+- write8: physmap_write8,
+- write16: physmap_write16,
+- write32: physmap_write32,
+- copy_to: physmap_copy_to,
+-};
+
+-
+-#ifdef CONFIG_MIPS_PB1000
+-
+-static unsigned long flash_size = 0x00800000;
+-static unsigned char flash_buswidth = 4;
+ static struct mtd_partition pb1xxx_partitions[] = {
+ {
+- name: "yamon env",
+- size: 0x00020000,
+- offset: 0,
+- mask_flags: MTD_WRITEABLE
+- },{
+- name: "User FS",
+- size: 0x003e0000,
+- offset: 0x20000,
+- },{
+- name: "boot code",
+- size: 0x100000,
+- offset: 0x400000,
+- mask_flags: MTD_WRITEABLE
+- },{
+- name: "raw/kernel",
+- size: 0x300000,
+- offset: 0x500000
+- }
++ .name = "yamon env",
++ .size = 0x00020000,
++ .offset = 0,
++ .mask_flags = MTD_WRITEABLE},
++ {
++ .name = "User FS",
++ .size = 0x003e0000,
++ .offset = 0x20000,},
++ {
++ .name = "boot code",
++ .size = 0x100000,
++ .offset = 0x400000,
++ .mask_flags = MTD_WRITEABLE},
++ {
++ .name = "raw/kernel",
++ .size = 0x300000,
++ .offset = 0x500000}
+ };
+
+ #elif defined(CONFIG_MIPS_PB1500) || defined(CONFIG_MIPS_PB1100)
+
+-static unsigned char flash_buswidth = 4;
+ #if defined(CONFIG_MTD_PB1500_BOOT) && defined(CONFIG_MTD_PB1500_USER)
+-/* both 32MiB banks will be used. Combine the first 32MiB bank and the
+- * first 28MiB of the second bank together into a single jffs/jffs2
++/* both 32MB banks will be used. Combine the first 32MB bank and the
++ * first 28MB of the second bank together into a single jffs/jffs2
+ * partition.
+ */
+-static unsigned long flash_size = 0x04000000;
+ #define WINDOW_ADDR 0x1C000000
+ #define WINDOW_SIZE 0x4000000
+ static struct mtd_partition pb1xxx_partitions[] = {
+ {
+- name: "User FS",
+- size: 0x3c00000,
+- offset: 0x0000000
+- },{
+- name: "yamon",
+- size: 0x0100000,
+- offset: 0x3c00000,
+- mask_flags: MTD_WRITEABLE
+- },{
+- name: "raw kernel",
+- size: 0x02c0000,
+- offset: 0x3d00000
++ .name = "User FS",
++ .size = 0x3c00000,
++ .offset = 0x0000000
++ },{
++ .name = "yamon",
++ .size = 0x0100000,
++ .offset = 0x3c00000,
++ .mask_flags = MTD_WRITEABLE
++ },{
++ .name = "raw kernel",
++ .size = 0x02c0000,
++ .offset = 0x3d00000
+ }
+ };
+ #elif defined(CONFIG_MTD_PB1500_BOOT) && !defined(CONFIG_MTD_PB1500_USER)
+-static unsigned long flash_size = 0x02000000;
+ #define WINDOW_ADDR 0x1E000000
+ #define WINDOW_SIZE 0x2000000
+ static struct mtd_partition pb1xxx_partitions[] = {
+ {
+- name: "User FS",
+- size: 0x1c00000,
+- offset: 0x0000000
+- },{
+- name: "yamon",
+- size: 0x0100000,
+- offset: 0x1c00000,
+- mask_flags: MTD_WRITEABLE
+- },{
+- name: "raw kernel",
+- size: 0x02c0000,
+- offset: 0x1d00000
++ .name = "User FS",
++ .size = 0x1c00000,
++ .offset = 0x0000000
++ },{
++ .name = "yamon",
++ .size = 0x0100000,
++ .offset = 0x1c00000,
++ .mask_flags = MTD_WRITEABLE
++ },{
++ .name = "raw kernel",
++ .size = 0x02c0000,
++ .offset = 0x1d00000
+ }
+ };
+ #elif !defined(CONFIG_MTD_PB1500_BOOT) && defined(CONFIG_MTD_PB1500_USER)
+-static unsigned long flash_size = 0x02000000;
+ #define WINDOW_ADDR 0x1C000000
+ #define WINDOW_SIZE 0x2000000
+ static struct mtd_partition pb1xxx_partitions[] = {
+ {
+- name: "User FS",
+- size: 0x1e00000,
+- offset: 0x0000000
+- },{
+- name: "raw kernel",
+- size: 0x0200000,
+- offset: 0x1e00000,
++ .name = "User FS",
++ .size = 0x1e00000,
++ .offset = 0x0000000
++ },{
++ .name = "raw kernel",
++ .size = 0x0200000,
++ .offset = 0x1e00000,
+ }
+ };
+ #else
+ #error MTD_PB1500 define combo error /* should never happen */
+ #endif
+-#elif defined(CONFIG_MTD_BOSPORUS)
+-static unsigned char flash_buswidth = 2;
+-static unsigned long flash_size = 0x02000000;
+-#define WINDOW_ADDR 0x1F000000
+-#define WINDOW_SIZE 0x2000000
+-static struct mtd_partition pb1xxx_partitions[] = {
+- {
+- name: "User FS",
+- size: 0x00400000,
+- offset: 0x00000000,
+- },{
+- name: "Yamon-2",
+- size: 0x00100000,
+- offset: 0x00400000,
+- },{
+- name: "Root FS",
+- size: 0x00700000,
+- offset: 0x00500000,
+- },{
+- name: "Yamon-1",
+- size: 0x00100000,
+- offset: 0x00C00000,
+- },{
+- name: "Kernel",
+- size: 0x00300000,
+- offset: 0x00D00000,
+- }
+-};
+ #else
+ #error Unsupported board
+ #endif
+
++#define NAME "Pb1x00 Linux Flash"
++#define PADDR WINDOW_ADDR
++#define BUSWIDTH 4
++#define SIZE WINDOW_SIZE
++#define PARTITIONS 4
++
++static struct map_info pb1xxx_mtd_map = {
++ .name = NAME,
++ .size = SIZE,
++ .buswidth = BUSWIDTH,
++ .phys = PADDR,
++};
+
+-#define NB_OF(x) (sizeof(x)/sizeof(x[0]))
+-
+-static struct mtd_partition *parsed_parts;
+-static struct mtd_info *mymtd;
++static struct mtd_info *pb1xxx_mtd;
+
+ int __init pb1xxx_mtd_init(void)
+ {
+@@ -236,40 +137,37 @@
+ int nb_parts = 0;
+ char *part_type;
+
+- /* Default flash buswidth */
+- pb1xxx_map.buswidth = flash_buswidth;
+-
+ /*
+ * Static partition definition selection
+ */
+ part_type = "static";
+ parts = pb1xxx_partitions;
+- nb_parts = NB_OF(pb1xxx_partitions);
+- pb1xxx_map.size = flash_size;
++ nb_parts = ARRAY_SIZE(pb1xxx_partitions);
+
+ /*
+ * Now let's probe for the actual flash. Do it here since
+ * specific machine settings might have been set above.
+ */
+ printk(KERN_NOTICE "Pb1xxx flash: probing %d-bit flash bus\n",
+- pb1xxx_map.buswidth*8);
+- pb1xxx_map.map_priv_1 =
+- (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE);
+- mymtd = do_map_probe("cfi_probe", &pb1xxx_map);
+- if (!mymtd) return -ENXIO;
+- mymtd->module = THIS_MODULE;
++ BUSWIDTH*8);
++ pb1xxx_mtd_map.virt = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE);
++
++ simple_map_init(&pb1xxx_mtd_map);
++
++ pb1xxx_mtd = do_map_probe("cfi_probe", &pb1xxx_mtd_map);
++ if (!pb1xxx_mtd) return -ENXIO;
++ pb1xxx_mtd->owner = THIS_MODULE;
+
+- add_mtd_partitions(mymtd, parts, nb_parts);
++ add_mtd_partitions(pb1xxx_mtd, parts, nb_parts);
+ return 0;
+ }
+
+ static void __exit pb1xxx_mtd_cleanup(void)
+ {
+- if (mymtd) {
+- del_mtd_partitions(mymtd);
+- map_destroy(mymtd);
+- if (parsed_parts)
+- kfree(parsed_parts);
++ if (pb1xxx_mtd) {
++ del_mtd_partitions(pb1xxx_mtd);
++ map_destroy(pb1xxx_mtd);
++ iounmap((void *) pb1xxx_mtd_map.virt);
+ }
+ }
+
+diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/pci.c linux/drivers/mtd/maps/pci.c
+--- linux-mips-2.4.27/drivers/mtd/maps/pci.c 2003-02-26 01:53:49.000000000 +0100
++++ linux/drivers/mtd/maps/pci.c 2004-11-19 10:25:11.944192952 +0100
+@@ -7,7 +7,7 @@
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+- * $Id$
++ * $Id$
+ *
+ * Generic PCI memory map driver. We support the following boards:
+ * - Intel IQ80310 ATU.
+@@ -98,10 +98,10 @@
+ }
+
+ static struct mtd_pci_info intel_iq80310_info = {
+- init: intel_iq80310_init,
+- exit: intel_iq80310_exit,
+- translate: intel_iq80310_translate,
+- map_name: "cfi_probe",
++ .init = intel_iq80310_init,
++ .exit = intel_iq80310_exit,
++ .translate = intel_iq80310_translate,
++ .map_name = "cfi_probe",
+ };
+
+ /*
+@@ -181,10 +181,10 @@
+ }
+
+ static struct mtd_pci_info intel_dc21285_info = {
+- init: intel_dc21285_init,
+- exit: intel_dc21285_exit,
+- translate: intel_dc21285_translate,
+- map_name: "jedec_probe",
++ .init = intel_dc21285_init,
++ .exit = intel_dc21285_exit,
++ .translate = intel_dc21285_translate,
++ .map_name = "jedec_probe",
+ };
+
+ /*
+@@ -193,22 +193,20 @@
+
+ static struct pci_device_id mtd_pci_ids[] __devinitdata = {
+ {
+- vendor: PCI_VENDOR_ID_INTEL,
+- device: 0x530d,
+- subvendor: PCI_ANY_ID,
+- subdevice: PCI_ANY_ID,
+- class: PCI_CLASS_MEMORY_OTHER << 8,
+- class_mask: 0xffff00,
+- driver_data: (unsigned long)&intel_iq80310_info,
++ .vendor = PCI_VENDOR_ID_INTEL,
++ .device = 0x530d,
++ .subvendor = PCI_ANY_ID,
++ .subdevice = PCI_ANY_ID,
++ .class = PCI_CLASS_MEMORY_OTHER << 8,
++ .class_mask = 0xffff00,
++ .driver_data = (unsigned long)&intel_iq80310_info,
+ },
+ {
+- vendor: PCI_VENDOR_ID_DEC,
+- device: PCI_DEVICE_ID_DEC_21285,
+- subvendor: 0, /* DC21285 defaults to 0 on reset */
+- subdevice: 0, /* DC21285 defaults to 0 on reset */
+- class: 0,
+- class_mask: 0,
+- driver_data: (unsigned long)&intel_dc21285_info,
++ .vendor = PCI_VENDOR_ID_DEC,
++ .device = PCI_DEVICE_ID_DEC_21285,
++ .subvendor = 0, /* DC21285 defaults to 0 on reset */
++ .subdevice = 0, /* DC21285 defaults to 0 on reset */
++ .driver_data = (unsigned long)&intel_dc21285_info,
+ },
+ { 0, }
+ };
+@@ -275,14 +273,15 @@
+ }
+
+ static struct map_info mtd_pci_map = {
+- read8: mtd_pci_read8,
+- read16: mtd_pci_read16,
+- read32: mtd_pci_read32,
+- copy_from: mtd_pci_copyfrom,
+- write8: mtd_pci_write8,
+- write16: mtd_pci_write16,
+- write32: mtd_pci_write32,
+- copy_to: mtd_pci_copyto,
++ .phys = NO_XIP,
++ .read8 = mtd_pci_read8,
++ .read16 = mtd_pci_read16,
++ .read32 = mtd_pci_read32,
++ .copy_from = mtd_pci_copyfrom,
++ .write8 = mtd_pci_write8,
++ .write16 = mtd_pci_write16,
++ .write32 = mtd_pci_write32,
++ .copy_to = mtd_pci_copyto,
+ };
+
+ static int __devinit
+@@ -322,7 +321,7 @@
+ if (!mtd)
+ goto release;
+
+- mtd->module = THIS_MODULE;
++ mtd->owner = THIS_MODULE;
+ add_mtd_device(mtd);
+
+ pci_set_drvdata(dev, mtd);
+@@ -359,10 +358,10 @@
+ }
+
+ static struct pci_driver mtd_pci_driver = {
+- name: "MTD PCI",
+- probe: mtd_pci_probe,
+- remove: __devexit_p(mtd_pci_remove),
+- id_table: mtd_pci_ids,
++ .name = "MTD PCI",
++ .probe = mtd_pci_probe,
++ .remove = __devexit_p(mtd_pci_remove),
++ .id_table = mtd_pci_ids,
+ };
+
+ static int __init mtd_pci_maps_init(void)
+diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/pcmciamtd.c linux/drivers/mtd/maps/pcmciamtd.c
+--- linux-mips-2.4.27/drivers/mtd/maps/pcmciamtd.c 2003-02-26 01:53:49.000000000 +0100
++++ linux/drivers/mtd/maps/pcmciamtd.c 2004-11-19 10:25:11.946192648 +0100
+@@ -1,5 +1,5 @@
+ /*
+- * $Id$
++ * $Id$
+ *
+ * pcmciamtd.c - MTD driver for PCMCIA flash memory cards
+ *
+@@ -14,6 +14,7 @@
+ #include <linux/module.h>
+ #include <linux/slab.h>
+ #include <linux/timer.h>
++#include <linux/init.h>
+ #include <asm/io.h>
+ #include <asm/system.h>
+
+@@ -24,6 +25,7 @@
+ #include <pcmcia/ds.h>
+
+ #include <linux/mtd/map.h>
++#include <linux/mtd/mtd.h>
+
+ #ifdef CONFIG_MTD_DEBUG
+ static int debug = CONFIG_MTD_DEBUG_VERBOSE;
+@@ -47,7 +49,7 @@
+
+
+ #define DRIVER_DESC "PCMCIA Flash memory card driver"
+-#define DRIVER_VERSION "$Revision$"
++#define DRIVER_VERSION "$Revision$"
+
+ /* Size of the PCMCIA address space: 26 bits = 64 MB */
+ #define MAX_PCMCIA_ADDR 0x4000000
+@@ -96,7 +98,7 @@
+ MODULE_PARM(mem_speed, "i");
+ MODULE_PARM_DESC(mem_speed, "Set memory access speed in ns");
+ MODULE_PARM(force_size, "i");
+-MODULE_PARM_DESC(force_size, "Force size of card in MB (1-64)");
++MODULE_PARM_DESC(force_size, "Force size of card in MiB (1-64)");
+ MODULE_PARM(setvpp, "i");
+ MODULE_PARM_DESC(setvpp, "Set Vpp (0=Never, 1=On writes, 2=Always on, default=0)");
+ MODULE_PARM(vpp, "i");
+@@ -106,11 +108,13 @@
+
+
+
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,69)
+ static inline void cs_error(client_handle_t handle, int func, int ret)
+ {
+ error_info_t err = { func, ret };
+ CardServices(ReportError, handle, &err);
+ }
++#endif
+
+
+ /* read/write{8,16} copy_{from,to} routines with window remapping to access whole card */
+@@ -529,6 +533,7 @@
+
+ card_settings(dev, link, &new_name);
+
++ dev->pcmcia_map.phys = NO_XIP;
+ dev->pcmcia_map.read8 = pcmcia_read8_remap;
+ dev->pcmcia_map.read16 = pcmcia_read16_remap;
+ dev->pcmcia_map.copy_from = pcmcia_copy_from_remap;
+@@ -539,7 +544,7 @@
+ dev->pcmcia_map.set_vpp = pcmciamtd_set_vpp;
+
+ /* Request a memory window for PCMCIA. Some architeures can map windows upto the maximum
+- that PCMCIA can support (64Mb) - this is ideal and we aim for a window the size of the
++ that PCMCIA can support (64MiB) - this is ideal and we aim for a window the size of the
+ whole card - otherwise we try smaller windows until we succeed */
+
+ req.Attributes = WIN_MEMORY_TYPE_CM | WIN_ENABLE;
+@@ -552,7 +557,7 @@
+
+ do {
+ int ret;
+- DEBUG(2, "requesting window with size = %dKB memspeed = %d",
++ DEBUG(2, "requesting window with size = %dKiB memspeed = %d",
+ req.Size >> 10, req.AccessSpeed);
+ link->win = (window_handle_t)link->handle;
+ ret = CardServices(RequestWindow, &link->win, &req);
+@@ -560,7 +565,7 @@
+ if(ret) {
+ req.Size >>= 1;
+ } else {
+- DEBUG(2, "Got window of size %dKB", req.Size >> 10);
++ DEBUG(2, "Got window of size %dKiB", req.Size >> 10);
+ dev->win_size = req.Size;
+ break;
+ }
+@@ -573,7 +578,7 @@
+ pcmciamtd_release((u_long)link);
+ return;
+ }
+- DEBUG(1, "Allocated a window of %dKB", dev->win_size >> 10);
++ DEBUG(1, "Allocated a window of %dKiB", dev->win_size >> 10);
+
+ /* Get write protect status */
+ CS_CHECK(GetStatus, link->handle, &status);
+@@ -642,21 +647,21 @@
+ }
+
+ dev->mtd_info = mtd;
+- mtd->module = THIS_MODULE;
++ mtd->owner = THIS_MODULE;
+
+ if(new_name) {
+ int size = 0;
+ char unit = ' ';
+ /* Since we are using a default name, make it better by adding in the
+ size */
+- if(mtd->size < 1048576) { /* <1MB in size, show size in K */
++ if(mtd->size < 1048576) { /* <1MiB in size, show size in KiB */
+ size = mtd->size >> 10;
+ unit = 'K';
+ } else {
+ size = mtd->size >> 20;
+ unit = 'M';
+ }
+- snprintf(dev->mtd_name, sizeof(dev->mtd_name), "%d%cB %s", size, unit, "PCMCIA Memory card");
++ snprintf(dev->mtd_name, sizeof(dev->mtd_name), "%d%ciB %s", size, unit, "PCMCIA Memory card");
+ }
+
+ /* If the memory found is fits completely into the mapped PCMCIA window,
+@@ -828,16 +833,20 @@
+ }
+
+
++#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,68)
++static struct pcmcia_driver pcmciamtd_driver = {
++ .drv = {
++ .name = "pcmciamtd"
++ },
++ .attach = pcmciamtd_attach,
++ .detach = pcmciamtd_detach,
++ .owner = THIS_MODULE
++};
++#endif
++
++
+ static int __init init_pcmciamtd(void)
+ {
+- servinfo_t serv;
+-
+- info(DRIVER_DESC " " DRIVER_VERSION);
+- CardServices(GetCardServicesInfo, &serv);
+- if (serv.Revision != CS_RELEASE_CODE) {
+- err("Card Services release does not match!");
+- return -1;
+- }
+
+ if(buswidth && buswidth != 1 && buswidth != 2) {
+ info("bad buswidth (%d), using default", buswidth);
+@@ -851,15 +860,24 @@
+ info("bad mem_type (%d), using default", mem_type);
+ mem_type = 0;
+ }
++
++#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,68)
++ return pcmcia_register_driver(&pcmciamtd_driver);
++#else
+ register_pccard_driver(&dev_info, &pcmciamtd_attach, &pcmciamtd_detach);
+ return 0;
++#endif
+ }
+
+
+ static void __exit exit_pcmciamtd(void)
+ {
+ DEBUG(1, DRIVER_DESC " unloading");
++#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,68)
++ pcmcia_unregister_driver(&pcmciamtd_driver);
++#else
+ unregister_pccard_driver(&dev_info);
++#endif
+
+ while(dev_list) {
+ dev_link_t *link = dev_list;
+diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/physmap.c linux/drivers/mtd/maps/physmap.c
+--- linux-mips-2.4.27/drivers/mtd/maps/physmap.c 2003-02-26 01:53:50.000000000 +0100
++++ linux/drivers/mtd/maps/physmap.c 2004-11-19 10:25:11.947192496 +0100
+@@ -1,179 +1,114 @@
+ /*
+- * $Id$
++ * $Id$
+ *
+ * Normal mappings of chips in physical memory
++ *
++ * Copyright (C) 2003 MontaVista Software Inc.
++ * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net
++ *
++ * 031022 - [jsun] add run-time configure and partition setup
+ */
+
+ #include <linux/module.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/slab.h>
+ #include <asm/io.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+ #include <linux/config.h>
+-
+-#ifdef CONFIG_MTD_PARTITIONS
+ #include <linux/mtd/partitions.h>
+-#endif
+-
+-#define WINDOW_ADDR CONFIG_MTD_PHYSMAP_START
+-#define WINDOW_SIZE CONFIG_MTD_PHYSMAP_LEN
+-#define BUSWIDTH CONFIG_MTD_PHYSMAP_BUSWIDTH
+
+ static struct mtd_info *mymtd;
+
+-__u8 physmap_read8(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readb(map->map_priv_1 + ofs);
+-}
+-
+-__u16 physmap_read16(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readw(map->map_priv_1 + ofs);
+-}
+-
+-__u32 physmap_read32(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readl(map->map_priv_1 + ofs);
+-}
+-
+-void physmap_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+-{
+- memcpy_fromio(to, map->map_priv_1 + from, len);
+-}
++struct map_info physmap_map = {.name = "phys_mapped_flash"};
+
+-void physmap_write8(struct map_info *map, __u8 d, unsigned long adr)
+-{
+- __raw_writeb(d, map->map_priv_1 + adr);
+- mb();
+-}
++#ifdef CONFIG_MTD_PARTITIONS
++static struct mtd_partition *mtd_parts;
++static int mtd_parts_nb;
+
+-void physmap_write16(struct map_info *map, __u16 d, unsigned long adr)
+-{
+- __raw_writew(d, map->map_priv_1 + adr);
+- mb();
+-}
++static int num_physmap_partitions;
++static struct mtd_partition *physmap_partitions;
+
+-void physmap_write32(struct map_info *map, __u32 d, unsigned long adr)
+-{
+- __raw_writel(d, map->map_priv_1 + adr);
+- mb();
+-}
++char *part_probes[] __initdata = {"cmdlinepart", "RedBoot", NULL};
+
+-void physmap_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
++void physmap_set_partitions(struct mtd_partition *parts, int num_parts)
+ {
+- memcpy_toio(map->map_priv_1 + to, from, len);
++ physmap_partitions=parts;
++ num_physmap_partitions=num_parts;
+ }
+-
+-struct map_info physmap_map = {
+- name: "Physically mapped flash",
+- size: WINDOW_SIZE,
+- buswidth: BUSWIDTH,
+- read8: physmap_read8,
+- read16: physmap_read16,
+- read32: physmap_read32,
+- copy_from: physmap_copy_from,
+- write8: physmap_write8,
+- write16: physmap_write16,
+- write32: physmap_write32,
+- copy_to: physmap_copy_to
+-};
+-
+-#ifdef CONFIG_MTD_PARTITIONS
+-#ifdef CONFIG_MTD_CMDLINE_PARTS
+-static struct mtd_partition *mtd_parts = 0;
+-static int mtd_parts_nb = 0;
+-#else
+-static struct mtd_partition physmap_partitions[] = {
+-/* Put your own partition definitions here */
+-#if 0
+- {
+- name: "bootROM",
+- size: 0x80000,
+- offset: 0,
+- mask_flags: MTD_WRITEABLE, /* force read-only */
+- }, {
+- name: "zImage",
+- size: 0x100000,
+- offset: MTDPART_OFS_APPEND,
+- mask_flags: MTD_WRITEABLE, /* force read-only */
+- }, {
+- name: "ramdisk.gz",
+- size: 0x300000,
+- offset: MTDPART_OFS_APPEND,
+- mask_flags: MTD_WRITEABLE, /* force read-only */
+- }, {
+- name: "User FS",
+- size: MTDPART_SIZ_FULL,
+- offset: MTDPART_OFS_APPEND,
+- }
+-#endif
+-};
+-
+-#define NUM_PARTITIONS (sizeof(physmap_partitions)/sizeof(struct mtd_partition))
+-
+-#endif
+-#endif
++#endif /* CONFIG_MTD_PARTITIONS */
+
+ int __init init_physmap(void)
+ {
+ static const char *rom_probe_types[] = { "cfi_probe", "jedec_probe", "map_rom", 0 };
+ const char **type;
+
+- printk(KERN_NOTICE "physmap flash device: %x at %x\n", WINDOW_SIZE, WINDOW_ADDR);
+- physmap_map.map_priv_1 = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE);
++ printk(KERN_NOTICE "physmap flash device: %lx at %lx\n", physmap_map.size, physmap_map.phys);
++ physmap_map.virt = (unsigned long)ioremap(physmap_map.phys, physmap_map.size);
+
+- if (!physmap_map.map_priv_1) {
++ if (!physmap_map.virt) {
+ printk("Failed to ioremap\n");
+ return -EIO;
+ }
+
++ simple_map_init(&physmap_map);
++
+ mymtd = 0;
+ type = rom_probe_types;
+ for(; !mymtd && *type; type++) {
+ mymtd = do_map_probe(*type, &physmap_map);
+ }
+ if (mymtd) {
+- mymtd->module = THIS_MODULE;
++ mymtd->owner = THIS_MODULE;
+
+- add_mtd_device(mymtd);
+ #ifdef CONFIG_MTD_PARTITIONS
+-#ifdef CONFIG_MTD_CMDLINE_PARTS
+- mtd_parts_nb = parse_cmdline_partitions(mymtd, &mtd_parts,
+- "phys");
++ mtd_parts_nb = parse_mtd_partitions(mymtd, part_probes,
++ &mtd_parts, 0);
++
+ if (mtd_parts_nb > 0)
+ {
+- printk(KERN_NOTICE
+- "Using command line partition definition\n");
+ add_mtd_partitions (mymtd, mtd_parts, mtd_parts_nb);
++ return 0;
+ }
+-#else
+- if (NUM_PARTITIONS != 0)
++
++ if (num_physmap_partitions != 0)
+ {
+ printk(KERN_NOTICE
+ "Using physmap partition definition\n");
+- add_mtd_partitions (mymtd, physmap_partitions, NUM_PARTITIONS);
++ add_mtd_partitions (mymtd, physmap_partitions, num_physmap_partitions);
++ return 0;
+ }
+
+ #endif
+-#endif
++ add_mtd_device(mymtd);
++
+ return 0;
+ }
+
+- iounmap((void *)physmap_map.map_priv_1);
++ iounmap((void *)physmap_map.virt);
+ return -ENXIO;
+ }
+
+ static void __exit cleanup_physmap(void)
+ {
+- if (mymtd) {
++#ifdef CONFIG_MTD_PARTITIONS
++ if (mtd_parts_nb) {
++ del_mtd_partitions(mymtd);
++ kfree(mtd_parts);
++ } else if (num_physmap_partitions) {
++ del_mtd_partitions(mymtd);
++ } else {
+ del_mtd_device(mymtd);
+- map_destroy(mymtd);
+- }
+- if (physmap_map.map_priv_1) {
+- iounmap((void *)physmap_map.map_priv_1);
+- physmap_map.map_priv_1 = 0;
+ }
++#else
++ del_mtd_device(mymtd);
++#endif
++ map_destroy(mymtd);
++
++ iounmap((void *)physmap_map.virt);
++ physmap_map.virt = 0;
+ }
+
+ module_init(init_physmap);
+diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/pnc2000.c linux/drivers/mtd/maps/pnc2000.c
+--- linux-mips-2.4.27/drivers/mtd/maps/pnc2000.c 2001-11-05 21:15:52.000000000 +0100
++++ linux/drivers/mtd/maps/pnc2000.c 2004-11-19 10:25:11.949192192 +0100
+@@ -5,12 +5,13 @@
+ *
+ * This code is GPL
+ *
+- * $Id$
++ * $Id$
+ */
+
+ #include <linux/module.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
+
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+@@ -24,58 +25,13 @@
+ * MAP DRIVER STUFF
+ */
+
+-__u8 pnc_read8(struct map_info *map, unsigned long ofs)
+-{
+- return *(__u8 *)(WINDOW_ADDR + ofs);
+-}
+-
+-__u16 pnc_read16(struct map_info *map, unsigned long ofs)
+-{
+- return *(__u16 *)(WINDOW_ADDR + ofs);
+-}
+-
+-__u32 pnc_read32(struct map_info *map, unsigned long ofs)
+-{
+- return *(volatile unsigned int *)(WINDOW_ADDR + ofs);
+-}
+-
+-void pnc_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+-{
+- memcpy(to, (void *)(WINDOW_ADDR + from), len);
+-}
+-
+-void pnc_write8(struct map_info *map, __u8 d, unsigned long adr)
+-{
+- *(__u8 *)(WINDOW_ADDR + adr) = d;
+-}
+-
+-void pnc_write16(struct map_info *map, __u16 d, unsigned long adr)
+-{
+- *(__u16 *)(WINDOW_ADDR + adr) = d;
+-}
+-
+-void pnc_write32(struct map_info *map, __u32 d, unsigned long adr)
+-{
+- *(__u32 *)(WINDOW_ADDR + adr) = d;
+-}
+-
+-void pnc_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+-{
+- memcpy((void *)(WINDOW_ADDR + to), from, len);
+-}
+
+ struct map_info pnc_map = {
+- name: "PNC-2000",
+- size: WINDOW_SIZE,
+- buswidth: 4,
+- read8: pnc_read8,
+- read16: pnc_read16,
+- read32: pnc_read32,
+- copy_from: pnc_copy_from,
+- write8: pnc_write8,
+- write16: pnc_write16,
+- write32: pnc_write32,
+- copy_to: pnc_copy_to
++ .name = "PNC-2000",
++ .size = WINDOW_SIZE,
++ .buswidth = 4,
++ .phys = 0xFFFFFFFF,
++ .virt = WINDOW_ADDR,
+ };
+
+
+@@ -84,19 +40,19 @@
+ */
+ static struct mtd_partition pnc_partitions[3] = {
+ {
+- name: "PNC-2000 boot firmware",
+- size: 0x20000,
+- offset: 0
++ .name = "PNC-2000 boot firmware",
++ .size = 0x20000,
++ .offset = 0
+ },
+ {
+- name: "PNC-2000 kernel",
+- size: 0x1a0000,
+- offset: 0x20000
++ .name = "PNC-2000 kernel",
++ .size = 0x1a0000,
++ .offset = 0x20000
+ },
+ {
+- name: "PNC-2000 filesystem",
+- size: 0x240000,
+- offset: 0x1c0000
++ .name = "PNC-2000 filesystem",
++ .size = 0x240000,
++ .offset = 0x1c0000
+ }
+ };
+
+@@ -110,9 +66,11 @@
+ {
+ printk(KERN_NOTICE "Photron PNC-2000 flash mapping: %x at %x\n", WINDOW_SIZE, WINDOW_ADDR);
+
++ simple_map_init(&pnc_map);
++
+ mymtd = do_map_probe("cfi_probe", &pnc_map);
+ if (mymtd) {
+- mymtd->module = THIS_MODULE;
++ mymtd->owner = THIS_MODULE;
+ return add_mtd_partitions(mymtd, pnc_partitions, 3);
+ }
+
+diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/redwood.c linux/drivers/mtd/maps/redwood.c
+--- linux-mips-2.4.27/drivers/mtd/maps/redwood.c 2003-02-26 01:53:50.000000000 +0100
++++ linux/drivers/mtd/maps/redwood.c 2004-11-19 10:25:11.962190216 +0100
+@@ -1,38 +1,23 @@
+ /*
+- * $Id:
++ * $Id$
+ *
+- * redwood.c - mapper for IBM Redwood-4/5 board.
++ * drivers/mtd/maps/redwood.c
+ *
+- * Copyright 2001 MontaVista Softare Inc.
++ * FLASH map for the IBM Redwood 4/5/6 boards.
+ *
+- * This program is free software; you can redistribute it and/or modify it
+- * under the terms of the GNU General Public License as published by the
+- * Free Software Foundation; either version 2 of the License, or (at your
+- * option) any later version.
+- *
+- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+- * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+- * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+- *
+- * You should have received a copy of the GNU General Public License along
+- * with this program; if not, write to the Free Software Foundation, Inc.,
+- * 675 Mass Ave, Cambridge, MA 02139, USA.
+- *
+- * History: 12/17/2001 - Armin
+- * migrated to use do_map_probe
++ * Author: MontaVista Software, Inc. <source@mvista.com>
+ *
++ * 2001-2003 (c) MontaVista, Software, Inc. This file is licensed under
++ * the terms of the GNU General Public License version 2. This program
++ * is licensed "as is" without any warranty of any kind, whether express
++ * or implied.
+ */
+
++#include <linux/config.h>
+ #include <linux/module.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
+
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+@@ -40,96 +25,102 @@
+
+ #include <asm/io.h>
+
++#if !defined (CONFIG_REDWOOD_6)
++
+ #define WINDOW_ADDR 0xffc00000
+ #define WINDOW_SIZE 0x00400000
+
+-__u8 redwood_flash_read8(struct map_info *map, unsigned long ofs)
+-{
+- return *(__u8 *)(map->map_priv_1 + ofs);
+-}
+-
+-__u16 redwood_flash_read16(struct map_info *map, unsigned long ofs)
+-{
+- return *(__u16 *)(map->map_priv_1 + ofs);
+-}
+-
+-__u32 redwood_flash_read32(struct map_info *map, unsigned long ofs)
+-{
+- return *(volatile unsigned int *)(map->map_priv_1 + ofs);
+-}
+-
+-void redwood_flash_copy_from(struct map_info *map, void *to,
+- unsigned long from, ssize_t len)
+-{
+- memcpy(to, (void *)(map->map_priv_1 + from), len);
+-}
+-
+-void redwood_flash_write8(struct map_info *map, __u8 d, unsigned long adr)
+-{
+- *(__u8 *)(map->map_priv_1 + adr) = d;
+-}
+-
+-void redwood_flash_write16(struct map_info *map, __u16 d, unsigned long adr)
+-{
+- *(__u16 *)(map->map_priv_1 + adr) = d;
+-}
+-
+-void redwood_flash_write32(struct map_info *map, __u32 d, unsigned long adr)
+-{
+- *(__u32 *)(map->map_priv_1 + adr) = d;
+-}
+-
+-void redwood_flash_copy_to(struct map_info *map, unsigned long to,
+- const void *from, ssize_t len)
+-{
+- memcpy((void *)(map->map_priv_1 + to), from, len);
+-}
++#define RW_PART0_OF 0
++#define RW_PART0_SZ 0x10000
++#define RW_PART1_OF RW_PART0_SZ
++#define RW_PART1_SZ 0x200000 - 0x10000
++#define RW_PART2_OF 0x200000
++#define RW_PART2_SZ 0x10000
++#define RW_PART3_OF 0x210000
++#define RW_PART3_SZ 0x200000 - (0x10000 + 0x20000)
++#define RW_PART4_OF 0x3e0000
++#define RW_PART4_SZ 0x20000
+
+-struct map_info redwood_flash_map = {
+- name: "IBM Redwood",
+- size: WINDOW_SIZE,
+- buswidth: 2,
+- read8: redwood_flash_read8,
+- read16: redwood_flash_read16,
+- read32: redwood_flash_read32,
+- copy_from: redwood_flash_copy_from,
+- write8: redwood_flash_write8,
+- write16: redwood_flash_write16,
+- write32: redwood_flash_write32,
+- copy_to: redwood_flash_copy_to
++static struct mtd_partition redwood_flash_partitions[] = {
++ {
++ .name = "Redwood OpenBIOS Vital Product Data",
++ .offset = RW_PART0_OF,
++ .size = RW_PART0_SZ,
++ .mask_flags = MTD_WRITEABLE /* force read-only */
++ },
++ {
++ .name = "Redwood kernel",
++ .offset = RW_PART1_OF,
++ .size = RW_PART1_SZ
++ },
++ {
++ .name = "Redwood OpenBIOS non-volatile storage",
++ .offset = RW_PART2_OF,
++ .size = RW_PART2_SZ,
++ .mask_flags = MTD_WRITEABLE /* force read-only */
++ },
++ {
++ .name = "Redwood filesystem",
++ .offset = RW_PART3_OF,
++ .size = RW_PART3_SZ
++ },
++ {
++ .name = "Redwood OpenBIOS",
++ .offset = RW_PART4_OF,
++ .size = RW_PART4_SZ,
++ .mask_flags = MTD_WRITEABLE /* force read-only */
++ }
+ };
+
++#else /* CONFIG_REDWOOD_6 */
++/* FIXME: the window is bigger - armin */
++#define WINDOW_ADDR 0xff800000
++#define WINDOW_SIZE 0x00800000
++
++#define RW_PART0_OF 0
++#define RW_PART0_SZ 0x400000 /* 4 MiB data */
++#define RW_PART1_OF RW_PART0_OF + RW_PART0_SZ
++#define RW_PART1_SZ 0x10000 /* 64K VPD */
++#define RW_PART2_OF RW_PART1_OF + RW_PART1_SZ
++#define RW_PART2_SZ 0x400000 - (0x10000 + 0x20000)
++#define RW_PART3_OF RW_PART2_OF + RW_PART2_SZ
++#define RW_PART3_SZ 0x20000
+
+ static struct mtd_partition redwood_flash_partitions[] = {
+ {
+- name: "Redwood OpenBIOS Vital Product Data",
+- offset: 0,
+- size: 0x10000,
+- mask_flags: MTD_WRITEABLE /* force read-only */
++ .name = "Redwood filesystem",
++ .offset = RW_PART0_OF,
++ .size = RW_PART0_SZ
+ },
+ {
+- name: "Redwood kernel",
+- offset: 0x10000,
+- size: 0x200000 - 0x10000
++ .name = "Redwood OpenBIOS Vital Product Data",
++ .offset = RW_PART1_OF,
++ .size = RW_PART1_SZ,
++ .mask_flags = MTD_WRITEABLE /* force read-only */
+ },
+ {
+- name: "Redwood OpenBIOS non-volatile storage",
+- offset: 0x200000,
+- size: 0x10000,
+- mask_flags: MTD_WRITEABLE /* force read-only */
++ .name = "Redwood kernel",
++ .offset = RW_PART2_OF,
++ .size = RW_PART2_SZ
+ },
+ {
+- name: "Redwood filesystem",
+- offset: 0x210000,
+- size: 0x200000 - (0x10000 + 0x20000)
+- },
+- {
+- name: "Redwood OpenBIOS",
+- offset: 0x3e0000,
+- size: 0x20000,
+- mask_flags: MTD_WRITEABLE /* force read-only */
++ .name = "Redwood OpenBIOS",
++ .offset = RW_PART3_OF,
++ .size = RW_PART3_SZ,
++ .mask_flags = MTD_WRITEABLE /* force read-only */
+ }
+ };
++
++#endif /* CONFIG_REDWOOD_6 */
++
++struct map_info redwood_flash_map = {
++ .name = "IBM Redwood",
++ .size = WINDOW_SIZE,
++ .buswidth = 2,
++ .phys = WINDOW_ADDR,
++};
++
++
+ #define NUM_REDWOOD_FLASH_PARTITIONS \
+ (sizeof(redwood_flash_partitions)/sizeof(redwood_flash_partitions[0]))
+
+@@ -140,18 +131,19 @@
+ printk(KERN_NOTICE "redwood: flash mapping: %x at %x\n",
+ WINDOW_SIZE, WINDOW_ADDR);
+
+- redwood_flash_map.map_priv_1 =
++ redwood_flash_map.virt =
+ (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE);
+
+- if (!redwood_flash_map.map_priv_1) {
++ if (!redwood_flash_map.virt) {
+ printk("init_redwood_flash: failed to ioremap\n");
+ return -EIO;
+ }
++ simple_map_init(&redwood_flash_map);
+
+ redwood_mtd = do_map_probe("cfi_probe",&redwood_flash_map);
+
+ if (redwood_mtd) {
+- redwood_mtd->module = THIS_MODULE;
++ redwood_mtd->owner = THIS_MODULE;
+ return add_mtd_partitions(redwood_mtd,
+ redwood_flash_partitions,
+ NUM_REDWOOD_FLASH_PARTITIONS);
+@@ -164,10 +156,15 @@
+ {
+ if (redwood_mtd) {
+ del_mtd_partitions(redwood_mtd);
+- iounmap((void *)redwood_flash_map.map_priv_1);
++ /* moved iounmap after map_destroy - armin */
+ map_destroy(redwood_mtd);
++ iounmap((void *)redwood_flash_map.virt);
+ }
+ }
+
+ module_init(init_redwood_flash);
+ module_exit(cleanup_redwood_flash);
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("MontaVista Software <source@mvista.com>");
++MODULE_DESCRIPTION("MTD map driver for the IBM Redwood reference boards");
+diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/rpxlite.c linux/drivers/mtd/maps/rpxlite.c
+--- linux-mips-2.4.27/drivers/mtd/maps/rpxlite.c 2001-11-05 21:15:52.000000000 +0100
++++ linux/drivers/mtd/maps/rpxlite.c 2004-11-19 10:25:11.963190064 +0100
+@@ -1,5 +1,5 @@
+ /*
+- * $Id$
++ * $Id$
+ *
+ * Handle mapping of the flash on the RPX Lite and CLLF boards
+ */
+@@ -7,6 +7,7 @@
+ #include <linux/module.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
+ #include <asm/io.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+@@ -17,80 +18,31 @@
+
+ static struct mtd_info *mymtd;
+
+-__u8 rpxlite_read8(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readb(map->map_priv_1 + ofs);
+-}
+-
+-__u16 rpxlite_read16(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readw(map->map_priv_1 + ofs);
+-}
+-
+-__u32 rpxlite_read32(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readl(map->map_priv_1 + ofs);
+-}
+-
+-void rpxlite_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+-{
+- memcpy_fromio(to, (void *)(map->map_priv_1 + from), len);
+-}
+-
+-void rpxlite_write8(struct map_info *map, __u8 d, unsigned long adr)
+-{
+- __raw_writeb(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-void rpxlite_write16(struct map_info *map, __u16 d, unsigned long adr)
+-{
+- __raw_writew(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-void rpxlite_write32(struct map_info *map, __u32 d, unsigned long adr)
+-{
+- __raw_writel(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-void rpxlite_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+-{
+- memcpy_toio((void *)(map->map_priv_1 + to), from, len);
+-}
+-
+-struct map_info rpxlite_map = {
+- name: "RPX",
+- size: WINDOW_SIZE,
+- buswidth: 4,
+- read8: rpxlite_read8,
+- read16: rpxlite_read16,
+- read32: rpxlite_read32,
+- copy_from: rpxlite_copy_from,
+- write8: rpxlite_write8,
+- write16: rpxlite_write16,
+- write32: rpxlite_write32,
+- copy_to: rpxlite_copy_to
++static struct map_info rpxlite_map = {
++ .name = "RPX",
++ .size = WINDOW_SIZE,
++ .buswidth = 4,
++ .phys = WINDOW_ADDR,
+ };
+
+ int __init init_rpxlite(void)
+ {
+ printk(KERN_NOTICE "RPX Lite or CLLF flash device: %x at %x\n", WINDOW_SIZE*4, WINDOW_ADDR);
+- rpxlite_map.map_priv_1 = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE * 4);
++ rpxlite_map.virt = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE * 4);
+
+- if (!rpxlite_map.map_priv_1) {
++ if (!rpxlite_map.virt) {
+ printk("Failed to ioremap\n");
+ return -EIO;
+ }
++ simple_map_init(&rpxlite_map);
+ mymtd = do_map_probe("cfi_probe", &rpxlite_map);
+ if (mymtd) {
+- mymtd->module = THIS_MODULE;
++ mymtd->owner = THIS_MODULE;
+ add_mtd_device(mymtd);
+ return 0;
+ }
+
+- iounmap((void *)rpxlite_map.map_priv_1);
++ iounmap((void *)rpxlite_map.virt);
+ return -ENXIO;
+ }
+
+@@ -100,9 +52,9 @@
+ del_mtd_device(mymtd);
+ map_destroy(mymtd);
+ }
+- if (rpxlite_map.map_priv_1) {
+- iounmap((void *)rpxlite_map.map_priv_1);
+- rpxlite_map.map_priv_1 = 0;
++ if (rpxlite_map.virt) {
++ iounmap((void *)rpxlite_map.virt);
++ rpxlite_map.virt = 0;
+ }
+ }
+
+diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/sa1100-flash.c linux/drivers/mtd/maps/sa1100-flash.c
+--- linux-mips-2.4.27/drivers/mtd/maps/sa1100-flash.c 2003-02-26 01:53:50.000000000 +0100
++++ linux/drivers/mtd/maps/sa1100-flash.c 2004-11-19 10:25:11.966189608 +0100
+@@ -3,7 +3,7 @@
+ *
+ * (C) 2000 Nicolas Pitre <nico@cam.org>
+ *
+- * $Id$
++ * $Id$
+ */
+
+ #include <linux/config.h>
+@@ -11,278 +11,212 @@
+ #include <linux/types.h>
+ #include <linux/ioport.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/errno.h>
++#include <linux/slab.h>
+
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+ #include <linux/mtd/partitions.h>
++#include <linux/mtd/concat.h>
+
+ #include <asm/hardware.h>
++#include <asm/mach-types.h>
+ #include <asm/io.h>
++#include <asm/sizes.h>
+
++#include <asm/arch/h3600.h>
+
+ #ifndef CONFIG_ARCH_SA1100
+ #error This is for SA1100 architecture only
+ #endif
+
++/*
++ * This isnt complete yet, so...
++ */
++#define CONFIG_MTD_SA1100_STATICMAP 1
+
+-#define WINDOW_ADDR 0xe8000000
+-
+-static __u8 sa1100_read8(struct map_info *map, unsigned long ofs)
+-{
+- return readb(map->map_priv_1 + ofs);
+-}
+-
+-static __u16 sa1100_read16(struct map_info *map, unsigned long ofs)
+-{
+- return readw(map->map_priv_1 + ofs);
+-}
+-
+-static __u32 sa1100_read32(struct map_info *map, unsigned long ofs)
+-{
+- return readl(map->map_priv_1 + ofs);
+-}
+-
+-static void sa1100_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+-{
+- memcpy(to, (void *)(map->map_priv_1 + from), len);
+-}
+-
+-static void sa1100_write8(struct map_info *map, __u8 d, unsigned long adr)
+-{
+- writeb(d, map->map_priv_1 + adr);
+-}
+-
+-static void sa1100_write16(struct map_info *map, __u16 d, unsigned long adr)
+-{
+- writew(d, map->map_priv_1 + adr);
+-}
+-
+-static void sa1100_write32(struct map_info *map, __u32 d, unsigned long adr)
+-{
+- writel(d, map->map_priv_1 + adr);
+-}
+-
+-static void sa1100_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+-{
+- memcpy((void *)(map->map_priv_1 + to), from, len);
+-}
+-
+-static struct map_info sa1100_map = {
+- name: "SA1100 flash",
+- read8: sa1100_read8,
+- read16: sa1100_read16,
+- read32: sa1100_read32,
+- copy_from: sa1100_copy_from,
+- write8: sa1100_write8,
+- write16: sa1100_write16,
+- write32: sa1100_write32,
+- copy_to: sa1100_copy_to,
+-
+- map_priv_1: WINDOW_ADDR,
+- map_priv_2: -1,
+-};
+-
+-
++#ifdef CONFIG_MTD_SA1100_STATICMAP
+ /*
+ * Here are partition information for all known SA1100-based devices.
+ * See include/linux/mtd/partitions.h for definition of the mtd_partition
+ * structure.
+ *
+- * The *_max_flash_size is the maximum possible mapped flash size which
+- * is not necessarily the actual flash size. It must be no more than
+- * the value specified in the "struct map_desc *_io_desc" mapping
+- * definition for the corresponding machine.
++ * Please note:
++ * 1. We no longer support static flash mappings via the machine io_desc
++ * structure.
++ * 2. The flash size given should be the largest flash size that can
++ * be accommodated.
++ *
++ * The MTD layer will detect flash chip aliasing and reduce the size of
++ * the map accordingly.
+ *
+ * Please keep these in alphabetical order, and formatted as per existing
+ * entries. Thanks.
+ */
+
+ #ifdef CONFIG_SA1100_ADSBITSY
+-#define ADSBITSY_FLASH_SIZE 0x02000000
+ static struct mtd_partition adsbitsy_partitions[] = {
+ {
+- name: "bootROM",
+- size: 0x80000,
+- offset: 0,
+- mask_flags: MTD_WRITEABLE, /* force read-only */
+- }, {
+- name: "zImage",
+- size: 0x100000,
+- offset: MTDPART_OFS_APPEND,
+- mask_flags: MTD_WRITEABLE, /* force read-only */
+- }, {
+- name: "ramdisk.gz",
+- size: 0x300000,
+- offset: MTDPART_OFS_APPEND,
+- mask_flags: MTD_WRITEABLE, /* force read-only */
+- }, {
+- name: "User FS",
+- size: MTDPART_SIZ_FULL,
+- offset: MTDPART_OFS_APPEND,
++ .name = "bootROM",
++ .size = 0x80000,
++ .offset = 0,
++ .mask_flags = MTD_WRITEABLE, /* force read-only */
++ }, {
++ .name = "zImage",
++ .size = 0x100000,
++ .offset = MTDPART_OFS_APPEND,
++ .mask_flags = MTD_WRITEABLE, /* force read-only */
++ }, {
++ .name = "ramdisk.gz",
++ .size = 0x300000,
++ .offset = MTDPART_OFS_APPEND,
++ .mask_flags = MTD_WRITEABLE, /* force read-only */
++ }, {
++ .name = "User FS",
++ .size = MTDPART_SIZ_FULL,
++ .offset = MTDPART_OFS_APPEND,
+ }
+ };
+ #endif
+
+ #ifdef CONFIG_SA1100_ASSABET
+ /* Phase 4 Assabet has two 28F160B3 flash parts in bank 0: */
+-#define ASSABET4_FLASH_SIZE 0x00400000
+ static struct mtd_partition assabet4_partitions[] = {
+ {
+- name: "bootloader",
+- size: 0x00020000,
+- offset: 0,
+- mask_flags: MTD_WRITEABLE,
+- }, {
+- name: "bootloader params",
+- size: 0x00020000,
+- offset: MTDPART_OFS_APPEND,
+- mask_flags: MTD_WRITEABLE,
+- }, {
+- name: "jffs",
+- size: MTDPART_SIZ_FULL,
+- offset: MTDPART_OFS_APPEND,
++ .name = "bootloader",
++ .size = 0x00020000,
++ .offset = 0,
++ .mask_flags = MTD_WRITEABLE,
++ }, {
++ .name = "bootloader params",
++ .size = 0x00020000,
++ .offset = MTDPART_OFS_APPEND,
++ .mask_flags = MTD_WRITEABLE,
++ }, {
++ .name = "jffs",
++ .size = MTDPART_SIZ_FULL,
++ .offset = MTDPART_OFS_APPEND,
+ }
+ };
+
+ /* Phase 5 Assabet has two 28F128J3A flash parts in bank 0: */
+-#define ASSABET5_FLASH_SIZE 0x02000000
+ static struct mtd_partition assabet5_partitions[] = {
+ {
+- name: "bootloader",
+- size: 0x00040000,
+- offset: 0,
+- mask_flags: MTD_WRITEABLE,
+- }, {
+- name: "bootloader params",
+- size: 0x00040000,
+- offset: MTDPART_OFS_APPEND,
+- mask_flags: MTD_WRITEABLE,
+- }, {
+- name: "jffs",
+- size: MTDPART_SIZ_FULL,
+- offset: MTDPART_OFS_APPEND,
++ .name = "bootloader",
++ .size = 0x00040000,
++ .offset = 0,
++ .mask_flags = MTD_WRITEABLE,
++ }, {
++ .name = "bootloader params",
++ .size = 0x00040000,
++ .offset = MTDPART_OFS_APPEND,
++ .mask_flags = MTD_WRITEABLE,
++ }, {
++ .name = "jffs",
++ .size = MTDPART_SIZ_FULL,
++ .offset = MTDPART_OFS_APPEND,
+ }
+ };
+
+-#define ASSABET_FLASH_SIZE ASSABET5_FLASH_SIZE
+ #define assabet_partitions assabet5_partitions
+ #endif
+
+ #ifdef CONFIG_SA1100_BADGE4
+-
+ /*
+- * 1 x Intel 28F320C3BA100 Advanced+ Boot Block Flash (32 Mi bit)
++ * 1 x Intel 28F320C3 Advanced+ Boot Block Flash (32 Mi bit)
+ * Eight 4 KiW Parameter Bottom Blocks (64 KiB)
+ * Sixty-three 32 KiW Main Blocks (4032 Ki b)
++ *
++ * <or>
++ *
++ * 1 x Intel 28F640C3 Advanced+ Boot Block Flash (64 Mi bit)
++ * Eight 4 KiW Parameter Bottom Blocks (64 KiB)
++ * One-hundred-twenty-seven 32 KiW Main Blocks (8128 Ki b)
+ */
+-#define BADGE4_FLASH_SIZE 0x00400000
+ static struct mtd_partition badge4_partitions[] = {
+ {
+- name: "BLOB boot loader",
+- offset: 0,
+- size: 0x0000A000
+- }, {
+- name: "params",
+- offset: MTDPART_OFS_APPEND,
+- size: 0x00006000
+- }, {
+- name: "kernel",
+- offset: MTDPART_OFS_APPEND,
+- size: 0x00100000
+- }, {
+- name: "root",
+- offset: MTDPART_OFS_APPEND,
+- size: MTDPART_SIZ_FULL
++ .name = "BLOB boot loader",
++ .offset = 0,
++ .size = 0x0000A000
++ }, {
++ .name = "params",
++ .offset = MTDPART_OFS_APPEND,
++ .size = 0x00006000
++ }, {
++ .name = "root",
++ .offset = MTDPART_OFS_APPEND,
++ .size = MTDPART_SIZ_FULL
+ }
+ };
+-
+ #endif
+
+
+ #ifdef CONFIG_SA1100_CERF
+ #ifdef CONFIG_SA1100_CERF_FLASH_32MB
+-#define CERF_FLASH_SIZE 0x02000000
+-static struct mtd_partition cerf_partitions[] = {
+- {
+- name: "firmware",
+- size: 0x00040000,
+- offset: 0,
+- }, {
+- name: "params",
+- size: 0x00040000,
+- offset: 0x00040000,
+- }, {
+- name: "kernel",
+- size: 0x00100000,
+- offset: 0x00080000,
+- }, {
+- name: "rootdisk",
+- size: 0x01E80000,
+- offset: 0x00180000,
+- }
+-};
++# define CERF_FLASH_SIZE 0x02000000
+ #elif defined CONFIG_SA1100_CERF_FLASH_16MB
+-#define CERF_FLASH_SIZE 0x01000000
++# define CERF_FLASH_SIZE 0x01000000
++#elif defined CONFIG_SA1100_CERF_FLASH_8MB
++# define CERF_FLASH_SIZE 0x00800000
++#else
++# error "Undefined flash size for CERF in sa1100-flash.c"
++#endif
++
+ static struct mtd_partition cerf_partitions[] = {
+ {
+- name: "firmware",
+- size: 0x00020000,
+- offset: 0,
+- }, {
+- name: "params",
+- size: 0x00020000,
+- offset: 0x00020000,
+- }, {
+- name: "kernel",
+- size: 0x00100000,
+- offset: 0x00040000,
+- }, {
+- name: "rootdisk",
+- size: 0x00EC0000,
+- offset: 0x00140000,
++ .name = "Bootloader",
++ .size = 0x00020000,
++ .offset = 0x00000000,
++ }, {
++ .name = "Params",
++ .size = 0x00040000,
++ .offset = 0x00020000,
++ }, {
++ .name = "Kernel",
++ .size = 0x00100000,
++ .offset = 0x00060000,
++ }, {
++ .name = "Filesystem",
++ .size = CERF_FLASH_SIZE-0x00160000,
++ .offset = 0x00160000,
+ }
+ };
+-#elif defined CONFIG_SA1100_CERF_FLASH_8MB
+-# error "Unwritten type definition"
+-#else
+-# error "Undefined memory orientation for CERF in sa1100-flash.c"
+-#endif
+ #endif
+
+ #ifdef CONFIG_SA1100_CONSUS
+-#define CONSUS_FLASH_SIZE 0x02000000
+ static struct mtd_partition consus_partitions[] = {
+ {
+- name: "Consus boot firmware",
+- offset: 0,
+- size: 0x00040000,
+- mask_flags: MTD_WRITABLE, /* force read-only */
+- }, {
+- name: "Consus kernel",
+- offset: 0x00040000,
+- size: 0x00100000,
+- mask_flags: 0,
++ .name = "Consus boot firmware",
++ .offset = 0,
++ .size = 0x00040000,
++ .mask_flags = MTD_WRITABLE, /* force read-only */
++ }, {
++ .name = "Consus kernel",
++ .offset = 0x00040000,
++ .size = 0x00100000,
++ .mask_flags = 0,
+ }, {
+- name: "Consus disk",
+- offset: 0x00140000,
++ .name = "Consus disk",
++ .offset = 0x00140000,
+ /* The rest (up to 16M) for jffs. We could put 0 and
+ make it find the size automatically, but right now
+ i have 32 megs. jffs will use all 32 megs if given
+ the chance, and this leads to horrible problems
+ when you try to re-flash the image because blob
+ won't erase the whole partition. */
+- size: 0x01000000 - 0x00140000,
+- mask_flags: 0,
++ .size = 0x01000000 - 0x00140000,
++ .mask_flags = 0,
+ }, {
+ /* this disk is a secondary disk, which can be used as
+ needed, for simplicity, make it the size of the other
+ consus partition, although realistically it could be
+ the remainder of the disk (depending on the file
+ system used) */
+- name: "Consus disk2",
+- offset: 0x01000000,
+- size: 0x01000000 - 0x00140000,
+- mask_flags: 0,
++ .name = "Consus disk2",
++ .offset = 0x01000000,
++ .size = 0x01000000 - 0x00140000,
++ .mask_flags = 0,
+ }
+ };
+ #endif
+@@ -292,96 +226,95 @@
+ #define FLEXANET_FLASH_SIZE 0x02000000
+ static struct mtd_partition flexanet_partitions[] = {
+ {
+- name: "bootloader",
+- size: 0x00040000,
+- offset: 0,
+- mask_flags: MTD_WRITEABLE,
+- }, {
+- name: "bootloader params",
+- size: 0x00040000,
+- offset: MTDPART_OFS_APPEND,
+- mask_flags: MTD_WRITEABLE,
+- }, {
+- name: "kernel",
+- size: 0x000C0000,
+- offset: MTDPART_OFS_APPEND,
+- mask_flags: MTD_WRITEABLE,
+- }, {
+- name: "altkernel",
+- size: 0x000C0000,
+- offset: MTDPART_OFS_APPEND,
+- mask_flags: MTD_WRITEABLE,
+- }, {
+- name: "root",
+- size: 0x00400000,
+- offset: MTDPART_OFS_APPEND,
+- mask_flags: MTD_WRITEABLE,
+- }, {
+- name: "free1",
+- size: 0x00300000,
+- offset: MTDPART_OFS_APPEND,
+- mask_flags: MTD_WRITEABLE,
+- }, {
+- name: "free2",
+- size: 0x00300000,
+- offset: MTDPART_OFS_APPEND,
+- mask_flags: MTD_WRITEABLE,
+- }, {
+- name: "free3",
+- size: MTDPART_SIZ_FULL,
+- offset: MTDPART_OFS_APPEND,
+- mask_flags: MTD_WRITEABLE,
++ .name = "bootloader",
++ .size = 0x00040000,
++ .offset = 0,
++ .mask_flags = MTD_WRITEABLE,
++ }, {
++ .name = "bootloader params",
++ .size = 0x00040000,
++ .offset = MTDPART_OFS_APPEND,
++ .mask_flags = MTD_WRITEABLE,
++ }, {
++ .name = "kernel",
++ .size = 0x000C0000,
++ .offset = MTDPART_OFS_APPEND,
++ .mask_flags = MTD_WRITEABLE,
++ }, {
++ .name = "altkernel",
++ .size = 0x000C0000,
++ .offset = MTDPART_OFS_APPEND,
++ .mask_flags = MTD_WRITEABLE,
++ }, {
++ .name = "root",
++ .size = 0x00400000,
++ .offset = MTDPART_OFS_APPEND,
++ .mask_flags = MTD_WRITEABLE,
++ }, {
++ .name = "free1",
++ .size = 0x00300000,
++ .offset = MTDPART_OFS_APPEND,
++ .mask_flags = MTD_WRITEABLE,
++ }, {
++ .name = "free2",
++ .size = 0x00300000,
++ .offset = MTDPART_OFS_APPEND,
++ .mask_flags = MTD_WRITEABLE,
++ }, {
++ .name = "free3",
++ .size = MTDPART_SIZ_FULL,
++ .offset = MTDPART_OFS_APPEND,
++ .mask_flags = MTD_WRITEABLE,
+ }
+ };
+ #endif
+
+ #ifdef CONFIG_SA1100_FREEBIRD
+-#define FREEBIRD_FLASH_SIZE 0x02000000
+ static struct mtd_partition freebird_partitions[] = {
+-#if CONFIG_SA1100_FREEBIRD_NEW
++#ifdef CONFIG_SA1100_FREEBIRD_NEW
+ {
+- name: "firmware",
+- size: 0x00040000,
+- offset: 0,
+- mask_flags: MTD_WRITEABLE, /* force read-only */
+- }, {
+- name: "kernel",
+- size: 0x00080000,
+- offset: 0x00040000,
+- }, {
+- name: "params",
+- size: 0x00040000,
+- offset: 0x000C0000,
+- }, {
+- name: "initrd",
+- size: 0x00100000,
+- offset: 0x00100000,
+- }, {
+- name: "root cramfs",
+- size: 0x00300000,
+- offset: 0x00200000,
+- }, {
+- name: "usr cramfs",
+- size: 0x00C00000,
+- offset: 0x00500000,
+- }, {
+- name: "local",
+- size: MTDPART_SIZ_FULL,
+- offset: 0x01100000,
++ .name = "firmware",
++ .size = 0x00040000,
++ .offset = 0,
++ .mask_flags = MTD_WRITEABLE, /* force read-only */
++ }, {
++ .name = "kernel",
++ .size = 0x00080000,
++ .offset = 0x00040000,
++ }, {
++ .name = "params",
++ .size = 0x00040000,
++ .offset = 0x000C0000,
++ }, {
++ .name = "initrd",
++ .size = 0x00100000,
++ .offset = 0x00100000,
++ }, {
++ .name = "root cramfs",
++ .size = 0x00300000,
++ .offset = 0x00200000,
++ }, {
++ .name = "usr cramfs",
++ .size = 0x00C00000,
++ .offset = 0x00500000,
++ }, {
++ .name = "local",
++ .size = MTDPART_SIZ_FULL,
++ .offset = 0x01100000,
+ }
+ #else
+ {
+- size: 0x00040000,
+- offset: 0,
++ .size = 0x00040000,
++ .offset = 0,
+ }, {
+- size: 0x000c0000,
+- offset: MTDPART_OFS_APPEND,
++ .size = 0x000c0000,
++ .offset = MTDPART_OFS_APPEND,
+ }, {
+- size: 0x00400000,
+- offset: MTDPART_OFS_APPEND,
++ .size = 0x00400000,
++ .offset = MTDPART_OFS_APPEND,
+ }, {
+- size: MTDPART_SIZ_FULL,
+- offset: MTDPART_OFS_APPEND,
++ .size = MTDPART_SIZ_FULL,
++ .offset = MTDPART_OFS_APPEND,
+ }
+ #endif
+ };
+@@ -389,178 +322,215 @@
+
+ #ifdef CONFIG_SA1100_FRODO
+ /* Frodo has 2 x 16M 28F128J3A flash chips in bank 0: */
+-#define FRODO_FLASH_SIZE 0x02000000
+ static struct mtd_partition frodo_partitions[] =
+ {
+ {
+- name: "Boot Loader",
+- size: 0x00040000,
+- offset: 0x00000000
+- }, {
+- name: "Parameter Block",
+- size: 0x00040000,
+- offset: MTDPART_OFS_APPEND
+- }, {
+- name: "Linux Kernel",
+- size: 0x00100000,
+- offset: MTDPART_OFS_APPEND
+- }, {
+- name: "Ramdisk",
+- size: 0x00680000,
+- offset: MTDPART_OFS_APPEND
+- }, {
+- name: "Flash File System",
+- size: MTDPART_SIZ_FULL,
+- offset: MTDPART_OFS_APPEND
++ .name = "bootloader",
++ .size = 0x00040000,
++ .offset = 0x00000000,
++ .mask_flags = MTD_WRITEABLE
++ }, {
++ .name = "bootloader params",
++ .size = 0x00040000,
++ .offset = MTDPART_OFS_APPEND,
++ .mask_flags = MTD_WRITEABLE
++ }, {
++ .name = "kernel",
++ .size = 0x00100000,
++ .offset = MTDPART_OFS_APPEND,
++ .mask_flags = MTD_WRITEABLE
++ }, {
++ .name = "ramdisk",
++ .size = 0x00400000,
++ .offset = MTDPART_OFS_APPEND,
++ .mask_flags = MTD_WRITEABLE
++ }, {
++ .name = "file system",
++ .size = MTDPART_SIZ_FULL,
++ .offset = MTDPART_OFS_APPEND
+ }
+ };
+ #endif
+
+ #ifdef CONFIG_SA1100_GRAPHICSCLIENT
+-#define GRAPHICSCLIENT_FLASH_SIZE 0x02000000
+ static struct mtd_partition graphicsclient_partitions[] = {
+ {
+- name: "zImage",
+- size: 0x100000,
+- offset: 0,
+- mask_flags: MTD_WRITEABLE, /* force read-only */
+- }, {
+- name: "ramdisk.gz",
+- size: 0x300000,
+- offset: MTDPART_OFS_APPEND,
+- mask_flags: MTD_WRITEABLE, /* force read-only */
+- }, {
+- name: "User FS",
+- size: MTDPART_SIZ_FULL,
+- offset: MTDPART_OFS_APPEND,
++ .name = "zImage",
++ .size = 0x100000,
++ .offset = 0,
++ .mask_flags = MTD_WRITEABLE, /* force read-only */
++ }, {
++ .name = "ramdisk.gz",
++ .size = 0x300000,
++ .offset = MTDPART_OFS_APPEND,
++ .mask_flags = MTD_WRITEABLE, /* force read-only */
++ }, {
++ .name = "User FS",
++ .size = MTDPART_SIZ_FULL,
++ .offset = MTDPART_OFS_APPEND,
+ }
+ };
+ #endif
+
+ #ifdef CONFIG_SA1100_GRAPHICSMASTER
+-#define GRAPHICSMASTER_FLASH_SIZE 0x01000000
+ static struct mtd_partition graphicsmaster_partitions[] = {
+ {
+- name: "zImage",
+- size: 0x100000,
+- offset: 0,
+- mask_flags: MTD_WRITEABLE, /* force read-only */
++ .name = "zImage",
++ .size = 0x100000,
++ .offset = 0,
++ .mask_flags = MTD_WRITEABLE, /* force read-only */
+ },
+ {
+- name: "ramdisk.gz",
+- size: 0x300000,
+- offset: MTDPART_OFS_APPEND,
+- mask_flags: MTD_WRITEABLE, /* force read-only */
++ .name = "ramdisk.gz",
++ .size = 0x300000,
++ .offset = MTDPART_OFS_APPEND,
++ .mask_flags = MTD_WRITEABLE, /* force read-only */
+ },
+ {
+- name: "User FS",
+- size: MTDPART_SIZ_FULL,
+- offset: MTDPART_OFS_APPEND,
++ .name = "User FS",
++ .size = MTDPART_SIZ_FULL,
++ .offset = MTDPART_OFS_APPEND,
+ }
+ };
+ #endif
+
+-#ifdef CONFIG_SA1100_H3600
+-#define H3600_FLASH_SIZE 0x02000000
+-static struct mtd_partition h3600_partitions[] = {
++#ifdef CONFIG_SA1100_H3XXX
++static struct mtd_partition h3xxx_partitions[] = {
+ {
+- name: "H3600 boot firmware",
+- size: 0x00040000,
+- offset: 0,
+- mask_flags: MTD_WRITEABLE, /* force read-only */
+- }, {
+- name: "H3600 kernel",
+- size: 0x00080000,
+- offset: 0x00040000,
++ .name = "H3XXX boot firmware",
++ .size = 0x00040000,
++ .offset = 0,
++ .mask_flags = MTD_WRITEABLE, /* force read-only */
+ }, {
+- name: "H3600 params",
+- size: 0x00040000,
+- offset: 0x000C0000,
++#ifdef CONFIG_MTD_2PARTS_IPAQ
++ .name = "H3XXX root jffs2",
++ .size = MTDPART_SIZ_FULL,
++ .offset = 0x00040000,
++#else
++ .name = "H3XXX kernel",
++ .size = 0x00080000,
++ .offset = 0x00040000,
++ }, {
++ .name = "H3XXX params",
++ .size = 0x00040000,
++ .offset = 0x000C0000,
+ }, {
+ #ifdef CONFIG_JFFS2_FS
+- name: "H3600 root jffs2",
+- size: MTDPART_SIZ_FULL,
+- offset: 0x00100000,
++ .name = "H3XXX root jffs2",
++ .size = MTDPART_SIZ_FULL,
++ .offset = 0x00100000,
+ #else
+- name: "H3600 initrd",
+- size: 0x00100000,
+- offset: 0x00100000,
++ .name = "H3XXX initrd",
++ .size = 0x00100000,
++ .offset = 0x00100000,
+ }, {
+- name: "H3600 root cramfs",
+- size: 0x00300000,
+- offset: 0x00200000,
++ .name = "H3XXX root cramfs",
++ .size = 0x00300000,
++ .offset = 0x00200000,
+ }, {
+- name: "H3600 usr cramfs",
+- size: 0x00800000,
+- offset: 0x00500000,
++ .name = "H3XXX usr cramfs",
++ .size = 0x00800000,
++ .offset = 0x00500000,
+ }, {
+- name: "H3600 usr local",
+- size: MTDPART_SIZ_FULL,
+- offset: 0x00d00000,
++ .name = "H3XXX usr local",
++ .size = MTDPART_SIZ_FULL,
++ .offset = 0x00d00000,
++#endif
+ #endif
+ }
+ };
+
+-static void h3600_set_vpp(struct map_info *map, int vpp)
++static void h3xxx_set_vpp(struct map_info *map, int vpp)
+ {
+ assign_h3600_egpio(IPAQ_EGPIO_VPP_ON, vpp);
+ }
++#else
++#define h3xxx_set_vpp NULL
++#endif
++
++#ifdef CONFIG_SA1100_HACKKIT
++static struct mtd_partition hackkit_partitions[] = {
++ {
++ .name = "BLOB",
++ .size = 0x00040000,
++ .offset = 0x00000000,
++ .mask_flags = MTD_WRITEABLE, /* force read-only */
++ }, {
++ .name = "config",
++ .size = 0x00040000,
++ .offset = MTDPART_OFS_APPEND,
++ }, {
++ .name = "kernel",
++ .size = 0x00100000,
++ .offset = MTDPART_OFS_APPEND,
++ }, {
++ .name = "initrd",
++ .size = 0x00180000,
++ .offset = MTDPART_OFS_APPEND,
++ }, {
++ .name = "rootfs",
++ .size = 0x700000,
++ .offset = MTDPART_OFS_APPEND,
++ }, {
++ .name = "data",
++ .size = MTDPART_SIZ_FULL,
++ .offset = MTDPART_OFS_APPEND,
++ }
++};
+ #endif
+
+ #ifdef CONFIG_SA1100_HUW_WEBPANEL
+-#define HUW_WEBPANEL_FLASH_SIZE 0x01000000
+ static struct mtd_partition huw_webpanel_partitions[] = {
+ {
+- name: "Loader",
+- size: 0x00040000,
+- offset: 0,
+- }, {
+- name: "Sector 1",
+- size: 0x00040000,
+- offset: MTDPART_OFS_APPEND,
++ .name = "Loader",
++ .size = 0x00040000,
++ .offset = 0,
++ }, {
++ .name = "Sector 1",
++ .size = 0x00040000,
++ .offset = MTDPART_OFS_APPEND,
+ }, {
+- size: MTDPART_SIZ_FULL,
+- offset: MTDPART_OFS_APPEND,
++ .size = MTDPART_SIZ_FULL,
++ .offset = MTDPART_OFS_APPEND,
+ }
+ };
+ #endif
+
+ #ifdef CONFIG_SA1100_JORNADA720
+-#define JORNADA720_FLASH_SIZE 0x02000000
+ static struct mtd_partition jornada720_partitions[] = {
+ {
+- name: "JORNADA720 boot firmware",
+- size: 0x00040000,
+- offset: 0,
+- mask_flags: MTD_WRITEABLE, /* force read-only */
++ .name = "JORNADA720 boot firmware",
++ .size = 0x00040000,
++ .offset = 0,
++ .mask_flags = MTD_WRITEABLE, /* force read-only */
+ }, {
+- name: "JORNADA720 kernel",
+- size: 0x000c0000,
+- offset: 0x00040000,
++ .name = "JORNADA720 kernel",
++ .size = 0x000c0000,
++ .offset = 0x00040000,
+ }, {
+- name: "JORNADA720 params",
+- size: 0x00040000,
+- offset: 0x00100000,
++ .name = "JORNADA720 params",
++ .size = 0x00040000,
++ .offset = 0x00100000,
+ }, {
+- name: "JORNADA720 initrd",
+- size: 0x00100000,
+- offset: 0x00140000,
++ .name = "JORNADA720 initrd",
++ .size = 0x00100000,
++ .offset = 0x00140000,
+ }, {
+- name: "JORNADA720 root cramfs",
+- size: 0x00300000,
+- offset: 0x00240000,
++ .name = "JORNADA720 root cramfs",
++ .size = 0x00300000,
++ .offset = 0x00240000,
+ }, {
+- name: "JORNADA720 usr cramfs",
+- size: 0x00800000,
+- offset: 0x00540000,
++ .name = "JORNADA720 usr cramfs",
++ .size = 0x00800000,
++ .offset = 0x00540000,
+ }, {
+- name: "JORNADA720 usr local",
+- size: 0 /* will expand to the end of the flash */
+- offset: 0x00d00000,
++ .name = "JORNADA720 usr local",
++ .size = 0, /* will expand to the end of the flash */
++ .offset = 0x00d00000,
+ }
+ };
+
+-static void jornada720_set_vpp(int vpp)
++static void jornada720_set_vpp(struct map_info *map, int vpp)
+ {
+ if (vpp)
+ PPSR |= 0x80;
+@@ -568,454 +538,811 @@
+ PPSR &= ~0x80;
+ PPDR |= 0x80;
+ }
+-
++#else
++#define jornada720_set_vpp NULL
+ #endif
+
+ #ifdef CONFIG_SA1100_PANGOLIN
+-#define PANGOLIN_FLASH_SIZE 0x04000000
+ static struct mtd_partition pangolin_partitions[] = {
+ {
+- name: "boot firmware",
+- size: 0x00080000,
+- offset: 0x00000000,
+- mask_flags: MTD_WRITEABLE, /* force read-only */
+- }, {
+- name: "kernel",
+- size: 0x00100000,
+- offset: 0x00080000,
+- }, {
+- name: "initrd",
+- size: 0x00280000,
+- offset: 0x00180000,
+- }, {
+- name: "initrd-test",
+- size: 0x03C00000,
+- offset: 0x00400000,
++ .name = "boot firmware",
++ .size = 0x00080000,
++ .offset = 0x00000000,
++ .mask_flags = MTD_WRITEABLE, /* force read-only */
++ }, {
++ .name = "kernel",
++ .size = 0x00100000,
++ .offset = 0x00080000,
++ }, {
++ .name = "initrd",
++ .size = 0x00280000,
++ .offset = 0x00180000,
++ }, {
++ .name = "initrd-test",
++ .size = 0x03C00000,
++ .offset = 0x00400000,
+ }
+ };
+ #endif
+
+ #ifdef CONFIG_SA1100_PT_SYSTEM3
+ /* erase size is 0x40000 == 256k partitions have to have this boundary */
+-#define SYSTEM3_FLASH_SIZE 0x01000000
+ static struct mtd_partition system3_partitions[] = {
+ {
+- name: "BLOB",
+- size: 0x00040000,
+- offset: 0x00000000,
+- mask_flags: MTD_WRITEABLE, /* force read-only */
+- }, {
+- name: "config",
+- size: 0x00040000,
+- offset: MTDPART_OFS_APPEND,
+- }, {
+- name: "kernel",
+- size: 0x00100000,
+- offset: MTDPART_OFS_APPEND,
+- }, {
+- name: "root",
+- size: MTDPART_SIZ_FULL,
+- offset: MTDPART_OFS_APPEND,
++ .name = "BLOB",
++ .size = 0x00040000,
++ .offset = 0x00000000,
++ .mask_flags = MTD_WRITEABLE, /* force read-only */
++ }, {
++ .name = "config",
++ .size = 0x00040000,
++ .offset = MTDPART_OFS_APPEND,
++ }, {
++ .name = "kernel",
++ .size = 0x00100000,
++ .offset = MTDPART_OFS_APPEND,
++ }, {
++ .name = "root",
++ .size = MTDPART_SIZ_FULL,
++ .offset = MTDPART_OFS_APPEND,
+ }
+ };
+ #endif
+
+ #ifdef CONFIG_SA1100_SHANNON
+-#define SHANNON_FLASH_SIZE 0x00400000
+ static struct mtd_partition shannon_partitions[] = {
+ {
+- name: "BLOB boot loader",
+- offset: 0,
+- size: 0x20000
++ .name = "BLOB boot loader",
++ .offset = 0,
++ .size = 0x20000
+ },
+ {
+- name: "kernel",
+- offset: MTDPART_OFS_APPEND,
+- size: 0xe0000
++ .name = "kernel",
++ .offset = MTDPART_OFS_APPEND,
++ .size = 0xe0000
+ },
+ {
+- name: "initrd",
+- offset: MTDPART_OFS_APPEND,
+- size: MTDPART_SIZ_FULL
++ .name = "initrd",
++ .offset = MTDPART_OFS_APPEND,
++ .size = MTDPART_SIZ_FULL
+ }
+ };
+
+ #endif
+
+ #ifdef CONFIG_SA1100_SHERMAN
+-#define SHERMAN_FLASH_SIZE 0x02000000
+ static struct mtd_partition sherman_partitions[] = {
+ {
+- size: 0x50000,
+- offset: 0,
++ .size = 0x50000,
++ .offset = 0,
+ }, {
+- size: 0x70000,
+- offset: MTDPART_OFS_APPEND,
++ .size = 0x70000,
++ .offset = MTDPART_OFS_APPEND,
+ }, {
+- size: 0x600000,
+- offset: MTDPART_OFS_APPEND,
++ .size = 0x600000,
++ .offset = MTDPART_OFS_APPEND,
+ }, {
+- size: 0xA0000,
+- offset: MTDPART_OFS_APPEND,
++ .size = 0xA0000,
++ .offset = MTDPART_OFS_APPEND,
+ }
+ };
+ #endif
+
+ #ifdef CONFIG_SA1100_SIMPAD
+-#define SIMPAD_FLASH_SIZE 0x02000000
+ static struct mtd_partition simpad_partitions[] = {
+ {
+- name: "SIMpad boot firmware",
+- size: 0x00080000,
+- offset: 0,
+- mask_flags: MTD_WRITEABLE, /* force read-only */
+- }, {
+- name: "SIMpad kernel",
+- size: 0x00100000,
+- offset: 0x00080000,
+- }, {
+-#ifdef CONFIG_JFFS2_FS
+- name: "SIMpad root jffs2",
+- size: MTDPART_SIZ_FULL,
+- offset: 0x00180000,
++ .name = "SIMpad boot firmware",
++ .size = 0x00080000,
++ .offset = 0,
++ .mask_flags = MTD_WRITEABLE, /* force read-only */
++ }, {
++ .name = "SIMpad kernel",
++ .size = 0x00100000,
++ .offset = MTDPART_OFS_APPEND,
++ }, {
++#ifdef CONFIG_ROOT_CRAMFS
++ .name = "SIMpad root cramfs",
++ .size =0x00D80000,
++ .offset = MTDPART_OFS_APPEND
++
++ }, {
++ .name = "SIMpad local jffs2",
++ .size = MTDPART_SIZ_FULL,
++ .offset = MTDPART_OFS_APPEND
+ #else
+- name: "SIMpad initrd",
+- size: 0x00300000,
+- offset: 0x00180000,
+- }, {
+- name: "SIMpad root cramfs",
+- size: 0x00300000,
+- offset: 0x00480000,
+- }, {
+- name: "SIMpad usr cramfs",
+- size: 0x005c0000,
+- offset: 0x00780000,
+- }, {
+- name: "SIMpad usr local",
+- size: MTDPART_SIZ_FULL,
+- offset: 0x00d40000,
++ .name = "SIMpad root jffs2",
++ .size = MTDPART_SIZ_FULL,
++ .offset = MTDPART_OFS_APPEND
+ #endif
+ }
+ };
+ #endif /* CONFIG_SA1100_SIMPAD */
+
+ #ifdef CONFIG_SA1100_STORK
+-#define STORK_FLASH_SIZE 0x02000000
+ static struct mtd_partition stork_partitions[] = {
+ {
+- name: "STORK boot firmware",
+- size: 0x00040000,
+- offset: 0,
+- mask_flags: MTD_WRITEABLE, /* force read-only */
+- }, {
+- name: "STORK params",
+- size: 0x00040000,
+- offset: 0x00040000,
+- }, {
+- name: "STORK kernel",
+- size: 0x00100000,
+- offset: 0x00080000,
++ .name = "STORK boot firmware",
++ .size = 0x00040000,
++ .offset = 0,
++ .mask_flags = MTD_WRITEABLE, /* force read-only */
++ }, {
++ .name = "STORK params",
++ .size = 0x00040000,
++ .offset = 0x00040000,
++ }, {
++ .name = "STORK kernel",
++ .size = 0x00100000,
++ .offset = 0x00080000,
+ }, {
+ #ifdef CONFIG_JFFS2_FS
+- name: "STORK root jffs2",
+- offset: 0x00180000,
+- size: MTDPART_SIZ_FULL,
++ .name = "STORK root jffs2",
++ .offset = 0x00180000,
++ .size = MTDPART_SIZ_FULL,
+ #else
+- name: "STORK initrd",
+- size: 0x00100000,
+- offset: 0x00180000,
+- }, {
+- name: "STORK root cramfs",
+- size: 0x00300000,
+- offset: 0x00280000,
+- }, {
+- name: "STORK usr cramfs",
+- size: 0x00800000,
+- offset: 0x00580000,
+- }, {
+- name: "STORK usr local",
+- offset: 0x00d80000,
+- size: MTDPART_SIZ_FULL,
++ .name = "STORK initrd",
++ .size = 0x00100000,
++ .offset = 0x00180000,
++ }, {
++ .name = "STORK root cramfs",
++ .size = 0x00300000,
++ .offset = 0x00280000,
++ }, {
++ .name = "STORK usr cramfs",
++ .size = 0x00800000,
++ .offset = 0x00580000,
++ }, {
++ .name = "STORK usr local",
++ .offset = 0x00d80000,
++ .size = MTDPART_SIZ_FULL,
++#endif
++ }
++};
+ #endif
++
++#ifdef CONFIG_SA1100_TRIZEPS
++static struct mtd_partition trizeps_partitions[] = {
++ {
++ .name = "Bootloader",
++ .size = 0x00100000,
++ .offset = 0,
++ }, {
++ .name = "Kernel",
++ .size = 0x00100000,
++ .offset = MTDPART_OFS_APPEND,
++ }, {
++ .name = "root",
++ .size = MTDPART_SIZ_FULL,
++ .offset = MTDPART_OFS_APPEND,
+ }
+ };
+ #endif
+
+ #ifdef CONFIG_SA1100_YOPY
+-#define YOPY_FLASH_SIZE 0x08000000
+ static struct mtd_partition yopy_partitions[] = {
+ {
+- name: "boot firmware",
+- size: 0x00040000,
+- offset: 0x00000000,
+- mask_flags: MTD_WRITEABLE, /* force read-only */
++ .name = "boot firmware",
++ .size = 0x00040000,
++ .offset = 0x00000000,
++ .mask_flags = MTD_WRITEABLE, /* force read-only */
+ }, {
+- name: "kernel",
+- size: 0x00080000,
+- offset: 0x00080000,
++ .name = "kernel",
++ .size = 0x00080000,
++ .offset = 0x00080000,
+ }, {
+- name: "initrd",
+- size: 0x00300000,
+- offset: 0x00100000,
++ .name = "initrd",
++ .size = 0x00300000,
++ .offset = 0x00100000,
+ }, {
+- name: "root",
+- size: 0x01000000,
+- offset: 0x00400000,
++ .name = "root",
++ .size = 0x01000000,
++ .offset = 0x00400000,
+ }
+ };
+ #endif
+
+-extern int parse_redboot_partitions(struct mtd_info *master, struct mtd_partition **pparts);
+-extern int parse_bootldr_partitions(struct mtd_info *master, struct mtd_partition **pparts);
+-
+-static struct mtd_partition *parsed_parts;
+-static struct mtd_info *mymtd;
+-
+-int __init sa1100_mtd_init(void)
++static int __init sa1100_static_partitions(struct mtd_partition **parts)
+ {
+- struct mtd_partition *parts;
+- int nb_parts = 0, ret;
+- int parsed_nr_parts = 0;
+- const char *part_type;
+- unsigned long base = -1UL;
+-
+- /* Default flash buswidth */
+- sa1100_map.buswidth = (MSC0 & MSC_RBW) ? 2 : 4;
+-
+- /*
+- * Static partition definition selection
+- */
+- part_type = "static";
++ int nb_parts = 0;
+
+ #ifdef CONFIG_SA1100_ADSBITSY
+ if (machine_is_adsbitsy()) {
+- parts = adsbitsy_partitions;
++ *parts = adsbitsy_partitions;
+ nb_parts = ARRAY_SIZE(adsbitsy_partitions);
+- sa1100_map.size = ADSBITSY_FLASH_SIZE;
+- sa1100_map.buswidth = (MSC1 & MSC_RBW) ? 2 : 4;
+ }
+ #endif
+ #ifdef CONFIG_SA1100_ASSABET
+ if (machine_is_assabet()) {
+- parts = assabet_partitions;
++ *parts = assabet_partitions;
+ nb_parts = ARRAY_SIZE(assabet_partitions);
+- sa1100_map.size = ASSABET_FLASH_SIZE;
+ }
+ #endif
+ #ifdef CONFIG_SA1100_BADGE4
+ if (machine_is_badge4()) {
+- parts = badge4_partitions;
++ *parts = badge4_partitions;
+ nb_parts = ARRAY_SIZE(badge4_partitions);
+- sa1100_map.size = BADGE4_FLASH_SIZE;
+ }
+ #endif
+ #ifdef CONFIG_SA1100_CERF
+ if (machine_is_cerf()) {
+- parts = cerf_partitions;
++ *parts = cerf_partitions;
+ nb_parts = ARRAY_SIZE(cerf_partitions);
+- sa1100_map.size = CERF_FLASH_SIZE;
+ }
+ #endif
+ #ifdef CONFIG_SA1100_CONSUS
+ if (machine_is_consus()) {
+- parts = consus_partitions;
++ *parts = consus_partitions;
+ nb_parts = ARRAY_SIZE(consus_partitions);
+- sa1100_map.size = CONSUS_FLASH_SIZE;
+ }
+ #endif
+ #ifdef CONFIG_SA1100_FLEXANET
+ if (machine_is_flexanet()) {
+- parts = flexanet_partitions;
++ *parts = flexanet_partitions;
+ nb_parts = ARRAY_SIZE(flexanet_partitions);
+- sa1100_map.size = FLEXANET_FLASH_SIZE;
+ }
+ #endif
+ #ifdef CONFIG_SA1100_FREEBIRD
+ if (machine_is_freebird()) {
+- parts = freebird_partitions;
++ *parts = freebird_partitions;
+ nb_parts = ARRAY_SIZE(freebird_partitions);
+- sa1100_map.size = FREEBIRD_FLASH_SIZE;
+ }
+ #endif
+ #ifdef CONFIG_SA1100_FRODO
+ if (machine_is_frodo()) {
+- parts = frodo_partitions;
++ *parts = frodo_partitions;
+ nb_parts = ARRAY_SIZE(frodo_partitions);
+- sa1100_map.size = FRODO_FLASH_SIZE;
+- base = 0x00000000;
+ }
+ #endif
+ #ifdef CONFIG_SA1100_GRAPHICSCLIENT
+ if (machine_is_graphicsclient()) {
+- parts = graphicsclient_partitions;
++ *parts = graphicsclient_partitions;
+ nb_parts = ARRAY_SIZE(graphicsclient_partitions);
+- sa1100_map.size = GRAPHICSCLIENT_FLASH_SIZE;
+- sa1100_map.buswidth = (MSC1 & MSC_RBW) ? 2:4;
+ }
+ #endif
+ #ifdef CONFIG_SA1100_GRAPHICSMASTER
+ if (machine_is_graphicsmaster()) {
+- parts = graphicsmaster_partitions;
++ *parts = graphicsmaster_partitions;
+ nb_parts = ARRAY_SIZE(graphicsmaster_partitions);
+- sa1100_map.size = GRAPHICSMASTER_FLASH_SIZE;
+- sa1100_map.buswidth = (MSC1 & MSC_RBW) ? 2:4;
+ }
+ #endif
+-#ifdef CONFIG_SA1100_H3600
+- if (machine_is_h3600()) {
+- parts = h3600_partitions;
+- nb_parts = ARRAY_SIZE(h3600_partitions);
+- sa1100_map.size = H3600_FLASH_SIZE;
+- sa1100_map.set_vpp = h3600_set_vpp;
++#ifdef CONFIG_SA1100_H3XXX
++ if (machine_is_h3xxx()) {
++ *parts = h3xxx_partitions;
++ nb_parts = ARRAY_SIZE(h3xxx_partitions);
++ }
++#endif
++#ifdef CONFIG_SA1100_HACKKIT
++ if (machine_is_hackkit()) {
++ *parts = hackkit_partitions;
++ nb_parts = ARRAY_SIZE(hackkit_partitions);
+ }
+ #endif
+ #ifdef CONFIG_SA1100_HUW_WEBPANEL
+ if (machine_is_huw_webpanel()) {
+- parts = huw_webpanel_partitions;
++ *parts = huw_webpanel_partitions;
+ nb_parts = ARRAY_SIZE(huw_webpanel_partitions);
+- sa1100_map.size = HUW_WEBPANEL_FLASH_SIZE;
+ }
+ #endif
+ #ifdef CONFIG_SA1100_JORNADA720
+ if (machine_is_jornada720()) {
+- parts = jornada720_partitions;
++ *parts = jornada720_partitions;
+ nb_parts = ARRAY_SIZE(jornada720_partitions);
+- sa1100_map.size = JORNADA720_FLASH_SIZE;
+- sa1100_map.set_vpp = jornada720_set_vpp;
+ }
+ #endif
+ #ifdef CONFIG_SA1100_PANGOLIN
+ if (machine_is_pangolin()) {
+- parts = pangolin_partitions;
++ *parts = pangolin_partitions;
+ nb_parts = ARRAY_SIZE(pangolin_partitions);
+- sa1100_map.size = PANGOLIN_FLASH_SIZE;
+ }
+ #endif
+ #ifdef CONFIG_SA1100_PT_SYSTEM3
+ if (machine_is_pt_system3()) {
+- parts = system3_partitions;
++ *parts = system3_partitions;
+ nb_parts = ARRAY_SIZE(system3_partitions);
+- sa1100_map.size = SYSTEM3_FLASH_SIZE;
+ }
+ #endif
+ #ifdef CONFIG_SA1100_SHANNON
+ if (machine_is_shannon()) {
+- parts = shannon_partitions;
++ *parts = shannon_partitions;
+ nb_parts = ARRAY_SIZE(shannon_partitions);
+- sa1100_map.size = SHANNON_FLASH_SIZE;
+ }
+ #endif
+ #ifdef CONFIG_SA1100_SHERMAN
+ if (machine_is_sherman()) {
+- parts = sherman_partitions;
++ *parts = sherman_partitions;
+ nb_parts = ARRAY_SIZE(sherman_partitions);
+- sa1100_map.size = SHERMAN_FLASH_SIZE;
+ }
+ #endif
+ #ifdef CONFIG_SA1100_SIMPAD
+ if (machine_is_simpad()) {
+- parts = simpad_partitions;
++ *parts = simpad_partitions;
+ nb_parts = ARRAY_SIZE(simpad_partitions);
+- sa1100_map.size = SIMPAD_FLASH_SIZE;
+ }
+ #endif
+ #ifdef CONFIG_SA1100_STORK
+ if (machine_is_stork()) {
+- parts = stork_partitions;
++ *parts = stork_partitions;
+ nb_parts = ARRAY_SIZE(stork_partitions);
+- sa1100_map.size = STORK_FLASH_SIZE;
++ }
++#endif
++#ifdef CONFIG_SA1100_TRIZEPS
++ if (machine_is_trizeps()) {
++ *parts = trizeps_partitions;
++ nb_parts = ARRAY_SIZE(trizeps_partitions);
+ }
+ #endif
+ #ifdef CONFIG_SA1100_YOPY
+ if (machine_is_yopy()) {
+- parts = yopy_partitions;
++ *parts = yopy_partitions;
+ nb_parts = ARRAY_SIZE(yopy_partitions);
+- sa1100_map.size = YOPY_FLASH_SIZE;
+ }
+ #endif
+
++ return nb_parts;
++}
++#endif
++
++struct sa_info {
++ unsigned long base;
++ unsigned long size;
++ int width;
++ void *vbase;
++ void (*set_vpp)(struct map_info *, int);
++ struct map_info *map;
++ struct mtd_info *mtd;
++ struct resource *res;
++};
++
++#define NR_SUBMTD 4
++
++static struct sa_info info[NR_SUBMTD];
++
++static int __init sa1100_setup_mtd(struct sa_info *sa, int nr, struct mtd_info **rmtd)
++{
++ struct mtd_info *subdev[nr];
++ struct map_info *maps;
++ int i, found = 0, ret = 0;
++
+ /*
+- * For simple flash devices, use ioremap to map the flash.
++ * Allocate the map_info structs in one go.
+ */
+- if (base != (unsigned long)-1) {
+- if (!request_mem_region(base, sa1100_map.size, "flash"))
+- return -EBUSY;
+- sa1100_map.map_priv_2 = base;
+- sa1100_map.map_priv_1 = (unsigned long)
+- ioremap(base, sa1100_map.size);
++ maps = kmalloc(sizeof(struct map_info) * nr, GFP_KERNEL);
++ if (!maps)
++ return -ENOMEM;
++
++ memset(maps, 0, sizeof(struct map_info) * nr);
++
++ /*
++ * Claim and then map the memory regions.
++ */
++ for (i = 0; i < nr; i++) {
++ if (sa[i].base == (unsigned long)-1)
++ break;
++
++ sa[i].res = request_mem_region(sa[i].base, sa[i].size, "sa1100 flash");
++ if (!sa[i].res) {
++ ret = -EBUSY;
++ break;
++ }
++
++ sa[i].map = maps + i;
++
++ sa[i].vbase = ioremap(sa[i].base, sa[i].size);
++ if (!sa[i].vbase) {
+ ret = -ENOMEM;
+- if (!sa1100_map.map_priv_1)
+- goto out_err;
++ break;
+ }
+
++ sa[i].map->virt = (unsigned long)sa[i].vbase;
++ sa[i].map->phys = sa[i].base;
++ sa[i].map->set_vpp = sa[i].set_vpp;
++ sa[i].map->buswidth = sa[i].width;
++ sa[i].map->size = sa[i].size;
++
++ simple_map_init(sa[i].map);
++
+ /*
+ * Now let's probe for the actual flash. Do it here since
+ * specific machine settings might have been set above.
+ */
+- printk(KERN_NOTICE "SA1100 flash: probing %d-bit flash bus\n", sa1100_map.buswidth*8);
+- mymtd = do_map_probe("cfi_probe", &sa1100_map);
++ sa[i].mtd = do_map_probe("cfi_probe", sa[i].map);
++ if (sa[i].mtd == NULL) {
+ ret = -ENXIO;
+- if (!mymtd)
+- goto out_err;
+- mymtd->module = THIS_MODULE;
++ break;
++ }
++ sa[i].mtd->owner = THIS_MODULE;
++ subdev[i] = sa[i].mtd;
++
++ printk(KERN_INFO "SA1100 flash: CFI device at 0x%08lx, %dMiB, "
++ "%d-bit\n", sa[i].base, sa[i].mtd->size >> 20,
++ sa[i].width * 8);
++ found += 1;
++ }
+
+ /*
+- * Dynamic partition selection stuff (might override the static ones)
++ * ENXIO is special. It means we didn't find a chip when
++ * we probed. We need to tear down the mapping, free the
++ * resource and mark it as such.
+ */
+-#ifdef CONFIG_MTD_REDBOOT_PARTS
+- if (parsed_nr_parts == 0) {
+- int ret = parse_redboot_partitions(mymtd, &parsed_parts);
++ if (ret == -ENXIO) {
++ iounmap(sa[i].vbase);
++ sa[i].vbase = NULL;
++ release_resource(sa[i].res);
++ sa[i].res = NULL;
++ }
+
+- if (ret > 0) {
+- part_type = "RedBoot";
+- parsed_nr_parts = ret;
++ /*
++ * If we found one device, don't bother with concat support.
++ * If we found multiple devices, use concat if we have it
++ * available, otherwise fail.
++ */
++ if (ret == 0 || ret == -ENXIO) {
++ if (found == 1) {
++ *rmtd = subdev[0];
++ ret = 0;
++ } else if (found > 1) {
++ /*
++ * We detected multiple devices. Concatenate
++ * them together.
++ */
++#ifdef CONFIG_MTD_CONCAT
++ *rmtd = mtd_concat_create(subdev, found,
++ "sa1100 flash");
++ if (*rmtd == NULL)
++ ret = -ENXIO;
++#else
++ printk(KERN_ERR "SA1100 flash: multiple devices "
++ "found but MTD concat support disabled.\n");
++ ret = -ENXIO;
++#endif
+ }
+ }
++
++ /*
++ * If we failed, clean up.
++ */
++ if (ret) {
++ do {
++ if (sa[i].mtd)
++ map_destroy(sa[i].mtd);
++ if (sa[i].vbase)
++ iounmap(sa[i].vbase);
++ if (sa[i].res)
++ release_resource(sa[i].res);
++ } while (i--);
++
++ kfree(maps);
++ }
++
++ return ret;
++}
++
++static void __exit sa1100_destroy_mtd(struct sa_info *sa, struct mtd_info *mtd)
++{
++ int i;
++
++ del_mtd_partitions(mtd);
++
++#ifdef CONFIG_MTD_CONCAT
++ if (mtd != sa[0].mtd)
++ mtd_concat_destroy(mtd);
+ #endif
+-#ifdef CONFIG_MTD_CMDLINE_PARTS
+- if (parsed_nr_parts == 0) {
+- int ret = parse_cmdline_partitions(mymtd, &parsed_parts, "sa1100");
+- if (ret > 0) {
+- part_type = "Command Line";
+- parsed_nr_parts = ret;
++
++ for (i = NR_SUBMTD; i >= 0; i--) {
++ if (sa[i].mtd)
++ map_destroy(sa[i].mtd);
++ if (sa[i].vbase)
++ iounmap(sa[i].vbase);
++ if (sa[i].res)
++ release_resource(sa[i].res);
+ }
++ kfree(sa[0].map);
++}
++
++/*
++ * A Thought: can we automatically detect the flash?
++ * - Check to see if the region is busy (yes -> failure)
++ * - Is the MSC setup for flash (no -> failure)
++ * - Probe for flash
++ */
++
++static struct map_info sa1100_probe_map __initdata = {
++ .name = "SA1100-flash",
++};
++
++static void __init sa1100_probe_one_cs(unsigned int msc, unsigned long phys)
++{
++ struct mtd_info *mtd;
++
++ printk(KERN_INFO "* Probing 0x%08lx: MSC = 0x%04x %d bit ",
++ phys, msc & 0xffff, msc & MSC_RBW ? 16 : 32);
++
++ if (check_mem_region(phys, 0x08000000)) {
++ printk("busy\n");
++ return;
+ }
+-#endif
+
+- if (parsed_nr_parts > 0) {
+- parts = parsed_parts;
+- nb_parts = parsed_nr_parts;
++ if ((msc & 3) == 1) {
++ printk("wrong type\n");
++ return;
+ }
+
+- if (nb_parts == 0) {
+- printk(KERN_NOTICE "SA1100 flash: no partition info available, registering whole flash at once\n");
+- add_mtd_device(mymtd);
+- } else {
+- printk(KERN_NOTICE "Using %s partition definition\n", part_type);
+- add_mtd_partitions(mymtd, parts, nb_parts);
++ sa1100_probe_map.buswidth = msc & MSC_RBW ? 2 : 4;
++ sa1100_probe_map.size = SZ_1M;
++ sa1100_probe_map.phys = phys;
++ sa1100_probe_map.virt = (unsigned long)ioremap(phys, SZ_1M);
++ if (sa1100_probe_map.virt == 0)
++ goto fail;
++ simple_map_init(&sa1100_probe_map);
++
++ /* Shame cfi_probe blurts out kernel messages... */
++ mtd = do_map_probe("cfi_probe", &sa1100_probe_map);
++ if (mtd)
++ map_destroy(mtd);
++ iounmap((void *)sa1100_probe_map.virt);
++
++ if (!mtd)
++ goto fail;
++
++ printk("pass\n");
++ return;
++
++ fail:
++ printk("failed\n");
++}
++
++static void __init sa1100_probe_flash(void)
++{
++ printk(KERN_INFO "-- SA11xx Flash probe. Please report results.\n");
++ sa1100_probe_one_cs(MSC0, SA1100_CS0_PHYS);
++ sa1100_probe_one_cs(MSC0 >> 16, SA1100_CS1_PHYS);
++ sa1100_probe_one_cs(MSC1, SA1100_CS2_PHYS);
++ sa1100_probe_one_cs(MSC1 >> 16, SA1100_CS3_PHYS);
++ sa1100_probe_one_cs(MSC2, SA1100_CS4_PHYS);
++ sa1100_probe_one_cs(MSC2 >> 16, SA1100_CS5_PHYS);
++ printk(KERN_INFO "-- SA11xx Flash probe complete.\n");
++}
++
++static int __init sa1100_locate_flash(void)
++{
++ int i, nr = -ENODEV;
++
++ sa1100_probe_flash();
++
++ if (machine_is_adsbitsy()) {
++ info[0].base = SA1100_CS1_PHYS;
++ info[0].size = SZ_32M;
++ nr = 1;
++ }
++ if (machine_is_assabet()) {
++ info[0].base = SA1100_CS0_PHYS;
++ info[0].size = SZ_32M;
++ info[1].base = SA1100_CS1_PHYS; /* neponset */
++ info[1].size = SZ_32M;
++ nr = 2;
++ }
++ if (machine_is_badge4()) {
++ info[0].base = SA1100_CS0_PHYS;
++ info[0].size = SZ_64M;
++ nr = 1;
++ }
++ if (machine_is_cerf()) {
++ info[0].base = SA1100_CS0_PHYS;
++ info[0].size = SZ_32M;
++ nr = 1;
++ }
++ if (machine_is_consus()) {
++ info[0].base = SA1100_CS0_PHYS;
++ info[0].size = SZ_32M;
++ nr = 1;
++ }
++ if (machine_is_flexanet()) {
++ info[0].base = SA1100_CS0_PHYS;
++ info[0].size = SZ_32M;
++ nr = 1;
++ }
++ if (machine_is_freebird()) {
++ info[0].base = SA1100_CS0_PHYS;
++ info[0].size = SZ_32M;
++ nr = 1;
++ }
++ if (machine_is_frodo()) {
++ info[0].base = SA1100_CS0_PHYS;
++ info[0].size = SZ_32M;
++ nr = 1;
++ }
++ if (machine_is_graphicsclient()) {
++ info[0].base = SA1100_CS1_PHYS;
++ info[0].size = SZ_32M;
++ nr = 1;
+ }
+- return 0;
++ if (machine_is_graphicsmaster()) {
++ info[0].base = SA1100_CS1_PHYS;
++ info[0].size = SZ_16M;
++ nr = 1;
++ }
++ if (machine_is_h3xxx()) {
++ info[0].set_vpp = h3xxx_set_vpp;
++ info[0].base = SA1100_CS0_PHYS;
++ info[0].size = SZ_32M;
++ nr = 1;
++ }
++ if (machine_is_huw_webpanel()) {
++ info[0].base = SA1100_CS0_PHYS;
++ info[0].size = SZ_16M;
++ nr = 1;
++ }
++ if (machine_is_itsy()) {
++ info[0].base = SA1100_CS0_PHYS;
++ info[0].size = SZ_32M;
++ nr = 1;
++ }
++ if (machine_is_jornada720()) {
++ info[0].set_vpp = jornada720_set_vpp;
++ info[0].base = SA1100_CS0_PHYS;
++ info[0].size = SZ_32M;
++ nr = 1;
++ }
++ if (machine_is_nanoengine()) {
++ info[0].base = SA1100_CS0_PHYS;
++ info[1].size = SZ_32M;
++ nr = 1;
++ }
++ if (machine_is_pangolin()) {
++ info[0].base = SA1100_CS0_PHYS;
++ info[0].size = SZ_64M;
++ nr = 1;
++ }
++ if (machine_is_pfs168()) {
++ info[0].base = SA1100_CS0_PHYS;
++ info[0].size = SZ_32M;
++ nr = 1;
++ }
++ if (machine_is_pleb()) {
++ info[0].base = SA1100_CS0_PHYS;
++ info[0].size = SZ_4M;
++ info[1].base = SA1100_CS1_PHYS;
++ info[1].size = SZ_4M;
++ nr = 2;
++ }
++ if (machine_is_pt_system3()) {
++ info[0].base = SA1100_CS0_PHYS;
++ info[0].size = SZ_16M;
++ nr = 1;
++ }
++ if (machine_is_shannon()) {
++ info[0].base = SA1100_CS0_PHYS;
++ info[0].size = SZ_4M;
++ nr = 1;
++ }
++ if (machine_is_sherman()) {
++ info[0].base = SA1100_CS0_PHYS;
++ info[0].size = SZ_32M;
++ nr = 1;
++ }
++ if (machine_is_simpad()) {
++ info[0].base = SA1100_CS0_PHYS;
++ info[0].size = SZ_16M;
++ info[1].base = SA1100_CS1_PHYS;
++ info[1].size = SZ_16M;
++ nr = 2;
++ }
++ if (machine_is_stork()) {
++ info[0].base = SA1100_CS0_PHYS;
++ info[0].size = SZ_32M;
++ nr = 1;
++ }
++ if (machine_is_trizeps()) {
++ info[0].base = SA1100_CS0_PHYS;
++ info[0].size = SZ_16M;
++ nr = 1;
++ }
++ if (machine_is_victor()) {
++ info[0].base = SA1100_CS0_PHYS;
++ info[0].size = SZ_2M;
++ nr = 1;
++ }
++ if (machine_is_yopy()) {
++ info[0].base = SA1100_CS0_PHYS;
++ info[0].size = SZ_64M;
++ info[1].base = SA1100_CS1_PHYS;
++ info[1].size = SZ_64M;
++ nr = 2;
++ }
++
++ if (nr < 0)
++ return nr;
+
+- out_err:
+- if (sa1100_map.map_priv_2 != -1) {
+- iounmap((void *)sa1100_map.map_priv_1);
+- release_mem_region(sa1100_map.map_priv_2, sa1100_map.size);
++ /*
++ * Retrieve the buswidth from the MSC registers.
++ * We currently only implement CS0 and CS1 here.
++ */
++ for (i = 0; i < nr; i++) {
++ switch (info[i].base) {
++ default:
++ printk(KERN_WARNING "SA1100 flash: unknown base address "
++ "0x%08lx, assuming CS0\n", info[i].base);
++ case SA1100_CS0_PHYS:
++ info[i].width = (MSC0 & MSC_RBW) ? 2 : 4;
++ break;
++
++ case SA1100_CS1_PHYS:
++ info[i].width = ((MSC0 >> 16) & MSC_RBW) ? 2 : 4;
++ break;
+ }
+- return ret;
++ }
++
++ return nr;
+ }
+
+-static void __exit sa1100_mtd_cleanup(void)
++static struct mtd_partition *parsed_parts;
++const char *part_probes[] = { "cmdlinepart", "RedBoot", NULL };
++
++static void __init sa1100_locate_partitions(struct mtd_info *mtd)
+ {
+- if (mymtd) {
+- del_mtd_partitions(mymtd);
+- map_destroy(mymtd);
+- if (parsed_parts)
+- kfree(parsed_parts);
++ const char *part_type = NULL;
++ int nr_parts = 0;
++
++ do {
++ /*
++ * Partition selection stuff.
++ */
++#ifdef CONFIG_MTD_PARTITIONS
++ nr_parts = parse_mtd_partitions(mtd, part_probes, &parsed_parts, 0);
++ if (nr_parts > 0) {
++ part_type = "dynamic";
++ break;
+ }
+- if (sa1100_map.map_priv_2 != -1) {
+- iounmap((void *)sa1100_map.map_priv_1);
+- release_mem_region(sa1100_map.map_priv_2, sa1100_map.size);
++#endif
++#ifdef CONFIG_MTD_SA1100_STATICMAP
++ nr_parts = sa1100_static_partitions(&parsed_parts);
++ if (nr_parts > 0) {
++ part_type = "static";
++ break;
+ }
++#endif
++ } while (0);
++
++ if (nr_parts == 0) {
++ printk(KERN_NOTICE "SA1100 flash: no partition info "
++ "available, registering whole flash\n");
++ add_mtd_device(mtd);
++ } else {
++ printk(KERN_NOTICE "SA1100 flash: using %s partition "
++ "definition\n", part_type);
++ add_mtd_partitions(mtd, parsed_parts, nr_parts);
++ }
++
++ /* Always succeeds. */
++}
++
++static void __exit sa1100_destroy_partitions(void)
++{
++ if (parsed_parts)
++ kfree(parsed_parts);
++}
++
++static struct mtd_info *mymtd;
++
++static int __init sa1100_mtd_init(void)
++{
++ int ret;
++ int nr;
++
++ nr = sa1100_locate_flash();
++ if (nr < 0)
++ return nr;
++
++ ret = sa1100_setup_mtd(info, nr, &mymtd);
++ if (ret == 0)
++ sa1100_locate_partitions(mymtd);
++
++ return ret;
++}
++
++static void __exit sa1100_mtd_cleanup(void)
++{
++ sa1100_destroy_mtd(info, mymtd);
++ sa1100_destroy_partitions();
+ }
+
+ module_init(sa1100_mtd_init);
+diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/sbc8240.c linux/drivers/mtd/maps/sbc8240.c
+--- linux-mips-2.4.27/drivers/mtd/maps/sbc8240.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/mtd/maps/sbc8240.c 2004-11-19 10:25:11.967189456 +0100
+@@ -0,0 +1,417 @@
++/*
++ * Handle mapping of the flash memory access routines on the SBC8240 board.
++ *
++ * Carolyn Smith, Tektronix, Inc.
++ *
++ * This code is GPLed
++ *
++ * $Id$
++ *
++ */
++
++/*
++ * The SBC8240 has 2 flash banks.
++ * Bank 0 is a 512 KiB AMD AM29F040B; 8 x 64 KiB sectors.
++ * It contains the U-Boot code (7 sectors) and the environment (1 sector).
++ * Bank 1 is 4 x 1 MiB AMD AM29LV800BT; 15 x 64 KiB sectors, 1 x 32 KiB sector,
++ * 2 x 8 KiB sectors, 1 x 16 KiB sectors.
++ * Both parts are JEDEC compatible.
++ */
++
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/kernel.h>
++#include <asm/io.h>
++
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/map.h>
++#include <linux/mtd/cfi.h>
++
++#ifdef CONFIG_MTD_PARTITIONS
++#include <linux/mtd/partitions.h>
++#endif
++
++#define DEBUG
++
++#ifdef DEBUG
++# define debugk(fmt,args...) printk(fmt ,##args)
++#else
++# define debugk(fmt,args...)
++#endif
++
++
++#define WINDOW_ADDR0 0xFFF00000 /* 512 KiB */
++#define WINDOW_SIZE0 0x00080000
++#define BUSWIDTH0 1
++
++#define WINDOW_ADDR1 0xFF000000 /* 4 MiB */
++#define WINDOW_SIZE1 0x00400000
++#define BUSWIDTH1 8
++
++#define MSG_PREFIX "sbc8240:" /* prefix for our printk()'s */
++#define MTDID "sbc8240-%d" /* for mtdparts= partitioning */
++
++
++static __u8 sbc8240_read8 (struct map_info *map, unsigned long ofs)
++{
++ return __raw_readb(map->map_priv_1 + ofs);
++}
++
++static __u16 sbc8240_read16 (struct map_info *map, unsigned long ofs)
++{
++ return __raw_readw(map->map_priv_1 + ofs);
++}
++
++static __u32 sbc8240_read32 (struct map_info *map, unsigned long ofs)
++{
++ return __raw_readl(map->map_priv_1 + ofs);
++}
++
++unsigned long long result64;
++
++static __u64 sbc8240_read64 (struct map_info *map, unsigned long ofs)
++{
++ unsigned long flags, msr, saved_msr;
++ volatile long saved_fr[2];
++ volatile unsigned long long result;
++ volatile unsigned long *p;
++
++ save_flags(flags);
++ cli();
++
++ /* turn off floating point unavailable exceptions */
++
++ __asm__ __volatile__ (
++ "mfmsr %0"
++ : "=r" (msr) :);
++
++ saved_msr = msr;
++ msr |= MSR_FP;
++ msr &= ~(MSR_FE0 | MSR_FE1);
++
++ __asm__ __volatile__ (
++ "mtmsr %0\n"
++ "isync\n"
++ : : "r" (msr));
++
++ /* read the data via a floating point register */
++
++ ofs = map->map_priv_1 + ofs;
++ p = (unsigned long *) &result64;
++
++ __asm__ __volatile__ (
++ "lfd 1,0(%1)\n"
++ "stfd 1,0(%0)\n"
++ : : "r" (p), "r" (ofs)
++ );
++
++ /* restore state */
++
++ __asm__ __volatile__ (
++ "mtmsr %0\n"
++ "isync\n"
++ : : "r" (saved_msr));
++
++ restore_flags(flags);
++
++ p = (unsigned long *) &result64;
++ debugk("sbc8240_read64 ofs 0x%x result 0x%08x%08x\n", ofs, *p, *(p+1));
++
++ return result64;
++}
++
++static void sbc8240_copy_from (struct map_info *map,
++ void *to, unsigned long from, ssize_t len)
++{
++ memcpy_fromio (to, (void *) (map->map_priv_1 + from), len);
++}
++
++static void sbc8240_write8 (struct map_info *map, __u8 d, unsigned long adr)
++{
++ __raw_writeb(d, map->map_priv_1 + adr);
++ mb();
++}
++
++static void sbc8240_write16 (struct map_info *map, __u16 d,
++ unsigned long adr)
++{
++ __raw_writew(d, map->map_priv_1 + adr);
++ mb();
++}
++
++static void sbc8240_write32 (struct map_info *map, __u32 d,
++ unsigned long adr)
++{
++ __raw_writel(d, map->map_priv_1 + adr);
++ mb();
++}
++
++static void sbc8240_write64 (struct map_info *map, __u64 data,
++ unsigned long adr)
++{
++ unsigned long long tmp;
++ unsigned long flags, msr, saved_msr, *p;
++ volatile long saved_fr[2];
++
++ save_flags(flags);
++ cli();
++
++ /* turn off floating point unavailable exceptions */
++
++ __asm__ __volatile__ (
++ "mfmsr %0"
++ : "=r" (msr) :);
++
++ saved_msr = msr;
++ msr |= MSR_FP;
++ msr &= ~(MSR_FE0 | MSR_FE1);
++
++ __asm__ __volatile__ (
++ "mtmsr %0\n"
++ "isync\n"
++ : : "r" (msr));
++
++
++ /* write the data via a floating point register */
++
++ tmp = data;
++ p = (unsigned long *) &tmp;
++ adr = map->map_priv_1 + adr;
++ debugk("sbc8240_write64 adr 0x%x data 0x%08x%08x\n", adr, *p, *(p+1));
++
++ __asm__ __volatile__ (
++ "stfd 1,0(%2)\n"
++ "lfd 1,0(%0)\n"
++ "stfd 1,0(%1)\n"
++ "lfd 1,0(%2)\n"
++ : : "r" (p), "r" (adr), "b" (saved_fr)
++ );
++
++ /* restore state */
++
++ __asm__ __volatile__ (
++ "mtmsr %0\n"
++ "isync\n"
++ : : "r" (saved_msr));
++
++ restore_flags(flags);
++}
++
++static void sbc8240_copy_to (struct map_info *map,
++ unsigned long to, const void *from, ssize_t len)
++{
++ memcpy_toio ((void *) (map->map_priv_1 + to), from, len);
++}
++
++static struct map_info sbc8240_map[2] = {
++ {
++ .name = "sbc8240 Flash Bank #0",
++ .size = WINDOW_SIZE0,
++ .buswidth = BUSWIDTH0,
++ .read8 = sbc8240_read8,
++ .read16 = sbc8240_read16,
++ .read32 = sbc8240_read32,
++ .read64 = sbc8240_read64,
++ .copy_from = sbc8240_copy_from,
++ .write8 = sbc8240_write8,
++ .write16 = sbc8240_write16,
++ .write32 = sbc8240_write32,
++ .write64 = sbc8240_write64,
++ .copy_to = sbc8240_copy_to
++ },
++ {
++ .name = "sbc8240 Flash Bank #1",
++ .size = WINDOW_SIZE1,
++ .buswidth = BUSWIDTH1,
++ .read8 = sbc8240_read8,
++ .read16 = sbc8240_read16,
++ .read32 = sbc8240_read32,
++ .read64 = sbc8240_read64,
++ .copy_from = sbc8240_copy_from,
++ .write8 = sbc8240_write8,
++ .write16 = sbc8240_write16,
++ .write32 = sbc8240_write32,
++ .write64 = sbc8240_write64,
++ .copy_to = sbc8240_copy_to
++ }
++};
++
++#define NUM_FLASH_BANKS (sizeof(sbc8240_map) / sizeof(struct map_info))
++
++/*
++ * The following defines the partition layout of SBC8240 boards.
++ *
++ * See include/linux/mtd/partitions.h for definition of the
++ * mtd_partition structure.
++ *
++ * The *_max_flash_size is the maximum possible mapped flash size
++ * which is not necessarily the actual flash size. It must correspond
++ * to the value specified in the mapping definition defined by the
++ * "struct map_desc *_io_desc" for the corresponding machine.
++ */
++
++#ifdef CONFIG_MTD_PARTITIONS
++
++static struct mtd_partition sbc8240_uboot_partitions [] = {
++ /* Bank 0 */
++ {
++ .name = "U-boot", /* U-Boot Firmware */
++ .offset = 0,
++ .size = 0x00070000, /* 7 x 64 KiB sectors */
++ .mask_flags = MTD_WRITEABLE, /* force read-only */
++ },
++ {
++ .name = "environment", /* U-Boot environment */
++ .offset = 0x00070000,
++ .size = 0x00010000, /* 1 x 64 KiB sector */
++ },
++};
++
++static struct mtd_partition sbc8240_fs_partitions [] = {
++ {
++ .name = "jffs", /* JFFS filesystem */
++ .offset = 0,
++ .size = 0x003C0000, /* 4 * 15 * 64KiB */
++ },
++ {
++ .name = "tmp32",
++ .offset = 0x003C0000,
++ .size = 0x00020000, /* 4 * 32KiB */
++ },
++ {
++ .name = "tmp8a",
++ .offset = 0x003E0000,
++ .size = 0x00008000, /* 4 * 8KiB */
++ },
++ {
++ .name = "tmp8b",
++ .offset = 0x003E8000,
++ .size = 0x00008000, /* 4 * 8KiB */
++ },
++ {
++ .name = "tmp16",
++ .offset = 0x003F0000,
++ .size = 0x00010000, /* 4 * 16KiB */
++ }
++};
++
++#define NB_OF(x) (sizeof (x) / sizeof (x[0]))
++
++/* trivial struct to describe partition information */
++struct mtd_part_def
++{
++ int nums;
++ unsigned char *type;
++ struct mtd_partition* mtd_part;
++};
++
++static struct mtd_info *sbc8240_mtd[NUM_FLASH_BANKS];
++static struct mtd_part_def sbc8240_part_banks[NUM_FLASH_BANKS];
++
++
++#endif /* CONFIG_MTD_PARTITIONS */
++
++
++int __init init_sbc8240_mtd (void)
++{
++ static struct _cjs {
++ u_long addr;
++ u_long size;
++ } pt[NUM_FLASH_BANKS] = {
++ {
++ .addr = WINDOW_ADDR0,
++ .size = WINDOW_SIZE0
++ },
++ {
++ .addr = WINDOW_ADDR1,
++ .size = WINDOW_SIZE1
++ },
++ };
++
++ int devicesfound = 0;
++ int i;
++
++ for (i = 0; i < NUM_FLASH_BANKS; i++) {
++ printk (KERN_NOTICE MSG_PREFIX
++ "Probing 0x%08lx at 0x%08lx\n", pt[i].size, pt[i].addr);
++
++ sbc8240_map[i].map_priv_1 =
++ (unsigned long) ioremap (pt[i].addr, pt[i].size);
++ if (!sbc8240_map[i].map_priv_1) {
++ printk (MSG_PREFIX "failed to ioremap\n");
++ return -EIO;
++ }
++
++ sbc8240_mtd[i] = do_map_probe("jedec_probe", &sbc8240_map[i]);
++
++ if (sbc8240_mtd[i]) {
++ sbc8240_mtd[i]->module = THIS_MODULE;
++ devicesfound++;
++ }
++ }
++
++ if (!devicesfound) {
++ printk(KERN_NOTICE MSG_PREFIX
++ "No suppported flash chips found!\n");
++ return -ENXIO;
++ }
++
++#ifdef CONFIG_MTD_PARTITIONS
++ sbc8240_part_banks[0].mtd_part = sbc8240_uboot_partitions;
++ sbc8240_part_banks[0].type = "static image";
++ sbc8240_part_banks[0].nums = NB_OF(sbc8240_uboot_partitions);
++ sbc8240_part_banks[1].mtd_part = sbc8240_fs_partitions;
++ sbc8240_part_banks[1].type = "static file system";
++ sbc8240_part_banks[1].nums = NB_OF(sbc8240_fs_partitions);
++
++ for (i = 0; i < NUM_FLASH_BANKS; i++) {
++
++ if (!sbc8240_mtd[i]) continue;
++ if (sbc8240_part_banks[i].nums == 0) {
++ printk (KERN_NOTICE MSG_PREFIX
++ "No partition info available, registering whole device\n");
++ add_mtd_device(sbc8240_mtd[i]);
++ } else {
++ printk (KERN_NOTICE MSG_PREFIX
++ "Using %s partition definition\n", sbc8240_part_banks[i].mtd_part->name);
++ add_mtd_partitions (sbc8240_mtd[i],
++ sbc8240_part_banks[i].mtd_part,
++ sbc8240_part_banks[i].nums);
++ }
++ }
++#else
++ printk(KERN_NOTICE MSG_PREFIX
++ "Registering %d flash banks at once\n", devicesfound);
++
++ for (i = 0; i < devicesfound; i++) {
++ add_mtd_device(sbc8240_mtd[i]);
++ }
++#endif /* CONFIG_MTD_PARTITIONS */
++
++ return devicesfound == 0 ? -ENXIO : 0;
++}
++
++static void __exit cleanup_sbc8240_mtd (void)
++{
++ int i;
++
++ for (i = 0; i < NUM_FLASH_BANKS; i++) {
++ if (sbc8240_mtd[i]) {
++ del_mtd_device (sbc8240_mtd[i]);
++ map_destroy (sbc8240_mtd[i]);
++ }
++ if (sbc8240_map[i].map_priv_1) {
++ iounmap ((void *) sbc8240_map[i].map_priv_1);
++ sbc8240_map[i].map_priv_1 = 0;
++ }
++ }
++}
++
++module_init (init_sbc8240_mtd);
++module_exit (cleanup_sbc8240_mtd);
++
++MODULE_LICENSE ("GPL");
++MODULE_AUTHOR ("Carolyn Smith <carolyn.smith@tektronix.com>");
++MODULE_DESCRIPTION ("MTD map driver for SBC8240 boards");
++
+diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/sbc_gxx.c linux/drivers/mtd/maps/sbc_gxx.c
+--- linux-mips-2.4.27/drivers/mtd/maps/sbc_gxx.c 2003-02-26 01:53:50.000000000 +0100
++++ linux/drivers/mtd/maps/sbc_gxx.c 2004-11-19 10:25:11.969189152 +0100
+@@ -17,7 +17,7 @@
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+
+- $Id$
++ $Id$
+
+ The SBC-MediaGX / SBC-GXx has up to 16 MiB of
+ Intel StrataFlash (28F320/28F640) in x8 mode.
+@@ -91,14 +91,14 @@
+ * single flash device into. If the size if zero we use up to the end of the
+ * device. */
+ static struct mtd_partition partition_info[]={
+- { name: "SBC-GXx flash boot partition",
+- offset: 0,
+- size: BOOT_PARTITION_SIZE_KiB*1024 },
+- { name: "SBC-GXx flash data partition",
+- offset: BOOT_PARTITION_SIZE_KiB*1024,
+- size: (DATA_PARTITION_SIZE_KiB)*1024 },
+- { name: "SBC-GXx flash application partition",
+- offset: (BOOT_PARTITION_SIZE_KiB+DATA_PARTITION_SIZE_KiB)*1024 }
++ { .name = "SBC-GXx flash boot partition",
++ .offset = 0,
++ .size = BOOT_PARTITION_SIZE_KiB*1024 },
++ { .name = "SBC-GXx flash data partition",
++ .offset = BOOT_PARTITION_SIZE_KiB*1024,
++ .size = (DATA_PARTITION_SIZE_KiB)*1024 },
++ { .name = "SBC-GXx flash application partition",
++ .offset = (BOOT_PARTITION_SIZE_KiB+DATA_PARTITION_SIZE_KiB)*1024 }
+ };
+
+ #define NUM_PARTITIONS 3
+@@ -203,19 +203,20 @@
+ }
+
+ static struct map_info sbc_gxx_map = {
+- name: "SBC-GXx flash",
+- size: MAX_SIZE_KiB*1024, /* this must be set to a maximum possible amount
++ .name = "SBC-GXx flash",
++ .phys = NO_XIP,
++ .size = MAX_SIZE_KiB*1024, /* this must be set to a maximum possible amount
+ of flash so the cfi probe routines find all
+ the chips */
+- buswidth: 1,
+- read8: sbc_gxx_read8,
+- read16: sbc_gxx_read16,
+- read32: sbc_gxx_read32,
+- copy_from: sbc_gxx_copy_from,
+- write8: sbc_gxx_write8,
+- write16: sbc_gxx_write16,
+- write32: sbc_gxx_write32,
+- copy_to: sbc_gxx_copy_to
++ .buswidth = 1,
++ .read8 = sbc_gxx_read8,
++ .read16 = sbc_gxx_read16,
++ .read32 = sbc_gxx_read32,
++ .copy_from = sbc_gxx_copy_from,
++ .write8 = sbc_gxx_write8,
++ .write16 = sbc_gxx_write16,
++ .write32 = sbc_gxx_write32,
++ .copy_to = sbc_gxx_copy_to
+ };
+
+ /* MTD device for all of the flash. */
+@@ -234,12 +235,6 @@
+
+ int __init init_sbc_gxx(void)
+ {
+- if (check_region(PAGE_IO,PAGE_IO_SIZE) != 0) {
+- printk( KERN_ERR"%s: IO ports 0x%x-0x%x in use\n",
+- sbc_gxx_map.name,
+- PAGE_IO, PAGE_IO+PAGE_IO_SIZE-1 );
+- return -EAGAIN;
+- }
+ iomapadr = (unsigned long)ioremap(WINDOW_START, WINDOW_LENGTH);
+ if (!iomapadr) {
+ printk( KERN_ERR"%s: failed to ioremap memory region\n",
+@@ -247,7 +242,14 @@
+ return -EIO;
+ }
+
+- request_region( PAGE_IO, PAGE_IO_SIZE, "SBC-GXx flash" );
++ if (!request_region( PAGE_IO, PAGE_IO_SIZE, "SBC-GXx flash")) {
++ printk( KERN_ERR"%s: IO ports 0x%x-0x%x in use\n",
++ sbc_gxx_map.name,
++ PAGE_IO, PAGE_IO+PAGE_IO_SIZE-1 );
++ iounmap((void *)iomapadr);
++ return -EAGAIN;
++ }
++
+
+ printk( KERN_INFO"%s: IO:0x%x-0x%x MEM:0x%x-0x%x\n",
+ sbc_gxx_map.name,
+@@ -261,7 +263,7 @@
+ return -ENXIO;
+ }
+
+- all_mtd->module=THIS_MODULE;
++ all_mtd->owner = THIS_MODULE;
+
+ /* Create MTD devices for each partition. */
+ add_mtd_partitions(all_mtd, partition_info, NUM_PARTITIONS );
+diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/sc520cdp.c linux/drivers/mtd/maps/sc520cdp.c
+--- linux-mips-2.4.27/drivers/mtd/maps/sc520cdp.c 2002-06-27 00:35:50.000000000 +0200
++++ linux/drivers/mtd/maps/sc520cdp.c 2004-11-19 10:25:11.970189000 +0100
+@@ -16,7 +16,7 @@
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+- * $Id$
++ * $Id$
+ *
+ *
+ * The SC520CDP is an evaluation board for the Elan SC520 processor available
+@@ -29,6 +29,7 @@
+ #include <linux/module.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
+ #include <asm/io.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+@@ -84,88 +85,25 @@
+ #define WINDOW_SIZE_1 0x00800000
+ #define WINDOW_SIZE_2 0x00080000
+
+-static __u8 sc520cdp_read8(struct map_info *map, unsigned long ofs)
+-{
+- return readb(map->map_priv_1 + ofs);
+-}
+-
+-static __u16 sc520cdp_read16(struct map_info *map, unsigned long ofs)
+-{
+- return readw(map->map_priv_1 + ofs);
+-}
+-
+-static __u32 sc520cdp_read32(struct map_info *map, unsigned long ofs)
+-{
+- return readl(map->map_priv_1 + ofs);
+-}
+-
+-static void sc520cdp_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+-{
+- memcpy_fromio(to, (void *)(map->map_priv_1 + from), len);
+-}
+-
+-static void sc520cdp_write8(struct map_info *map, __u8 d, unsigned long adr)
+-{
+- writeb(d, map->map_priv_1 + adr);
+-}
+-
+-static void sc520cdp_write16(struct map_info *map, __u16 d, unsigned long adr)
+-{
+- writew(d, map->map_priv_1 + adr);
+-}
+-
+-static void sc520cdp_write32(struct map_info *map, __u32 d, unsigned long adr)
+-{
+- writel(d, map->map_priv_1 + adr);
+-}
+-
+-static void sc520cdp_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+-{
+- memcpy_toio((void *)(map->map_priv_1 + to), from, len);
+-}
+
+ static struct map_info sc520cdp_map[] = {
+ {
+- name: "SC520CDP Flash Bank #0",
+- size: WINDOW_SIZE_0,
+- buswidth: 4,
+- read8: sc520cdp_read8,
+- read16: sc520cdp_read16,
+- read32: sc520cdp_read32,
+- copy_from: sc520cdp_copy_from,
+- write8: sc520cdp_write8,
+- write16: sc520cdp_write16,
+- write32: sc520cdp_write32,
+- copy_to: sc520cdp_copy_to,
+- map_priv_2: WINDOW_ADDR_0
++ .name = "SC520CDP Flash Bank #0",
++ .size = WINDOW_SIZE_0,
++ .buswidth = 4,
++ .phys = WINDOW_ADDR_0
+ },
+ {
+- name: "SC520CDP Flash Bank #1",
+- size: WINDOW_SIZE_1,
+- buswidth: 4,
+- read8: sc520cdp_read8,
+- read16: sc520cdp_read16,
+- read32: sc520cdp_read32,
+- copy_from: sc520cdp_copy_from,
+- write8: sc520cdp_write8,
+- write16: sc520cdp_write16,
+- write32: sc520cdp_write32,
+- copy_to: sc520cdp_copy_to,
+- map_priv_2: WINDOW_ADDR_1
++ .name = "SC520CDP Flash Bank #1",
++ .size = WINDOW_SIZE_1,
++ .buswidth = 4,
++ .phys = WINDOW_ADDR_1
+ },
+ {
+- name: "SC520CDP DIL Flash",
+- size: WINDOW_SIZE_2,
+- buswidth: 1,
+- read8: sc520cdp_read8,
+- read16: sc520cdp_read16,
+- read32: sc520cdp_read32,
+- copy_from: sc520cdp_copy_from,
+- write8: sc520cdp_write8,
+- write16: sc520cdp_write16,
+- write32: sc520cdp_write32,
+- copy_to: sc520cdp_copy_to,
+- map_priv_2: WINDOW_ADDR_2
++ .name = "SC520CDP DIL Flash",
++ .size = WINDOW_SIZE_2,
++ .buswidth = 1,
++ .phys = WINDOW_ADDR_2
+ },
+ };
+
+@@ -255,9 +193,9 @@
+ /* map in SC520's MMCR area */
+ mmcr = (unsigned long *)ioremap_nocache(SC520_MMCR_BASE, SC520_MMCR_EXTENT);
+ if(!mmcr) { /* ioremap_nocache failed: skip the PAR reprogramming */
+- /* force map_priv_2 fields to BIOS defaults: */
++ /* force physical address fields to BIOS defaults: */
+ for(i = 0; i < NUM_FLASH_BANKS; i++)
+- sc520cdp_map[i].map_priv_2 = par_table[i].default_address;
++ sc520cdp_map[i].phys = par_table[i].default_address;
+ return;
+ }
+
+@@ -282,7 +220,7 @@
+ sc520cdp_map[i].name);
+ printk(KERN_NOTICE "Trying default address 0x%lx\n",
+ par_table[i].default_address);
+- sc520cdp_map[i].map_priv_2 = par_table[i].default_address;
++ sc520cdp_map[i].phys = par_table[i].default_address;
+ }
+ }
+ iounmap((void *)mmcr);
+@@ -300,13 +238,18 @@
+ #endif
+
+ for (i = 0; i < NUM_FLASH_BANKS; i++) {
+- printk(KERN_NOTICE "SC520 CDP flash device: %lx at %lx\n", sc520cdp_map[i].size, sc520cdp_map[i].map_priv_2);
+- sc520cdp_map[i].map_priv_1 = (unsigned long)ioremap_nocache(sc520cdp_map[i].map_priv_2, sc520cdp_map[i].size);
++ printk(KERN_NOTICE "SC520 CDP flash device: 0x%lx at 0x%lx\n",
++ sc520cdp_map[i].size, sc520cdp_map[i].phys);
+
+- if (!sc520cdp_map[i].map_priv_1) {
++ sc520cdp_map[i].virt = (unsigned long)ioremap_nocache(sc520cdp_map[i].phys, sc520cdp_map[i].size);
++
++ if (!sc520cdp_map[i].virt) {
+ printk("Failed to ioremap_nocache\n");
+ return -EIO;
+ }
++
++ simple_map_init(&sc520cdp_map[i]);
++
+ mymtd[i] = do_map_probe("cfi_probe", &sc520cdp_map[i]);
+ if(!mymtd[i])
+ mymtd[i] = do_map_probe("jedec_probe", &sc520cdp_map[i]);
+@@ -314,11 +257,11 @@
+ mymtd[i] = do_map_probe("map_rom", &sc520cdp_map[i]);
+
+ if (mymtd[i]) {
+- mymtd[i]->module = THIS_MODULE;
++ mymtd[i]->owner = THIS_MODULE;
+ ++devices_found;
+ }
+ else {
+- iounmap((void *)sc520cdp_map[i].map_priv_1);
++ iounmap((void *)sc520cdp_map[i].virt);
+ }
+ }
+ if(devices_found >= 2) {
+@@ -346,9 +289,9 @@
+ for (i = 0; i < NUM_FLASH_BANKS; i++) {
+ if (mymtd[i])
+ map_destroy(mymtd[i]);
+- if (sc520cdp_map[i].map_priv_1) {
+- iounmap((void *)sc520cdp_map[i].map_priv_1);
+- sc520cdp_map[i].map_priv_1 = 0;
++ if (sc520cdp_map[i].virt) {
++ iounmap((void *)sc520cdp_map[i].virt);
++ sc520cdp_map[i].virt = 0;
+ }
+ }
+ }
+diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/scb2_flash.c linux/drivers/mtd/maps/scb2_flash.c
+--- linux-mips-2.4.27/drivers/mtd/maps/scb2_flash.c 2003-02-26 01:53:50.000000000 +0100
++++ linux/drivers/mtd/maps/scb2_flash.c 2004-11-19 10:25:11.972188696 +0100
+@@ -1,6 +1,6 @@
+ /*
+ * MTD map driver for BIOS Flash on Intel SCB2 boards
+- * $Id$
++ * $Id$
+ * Copyright (C) 2002 Sun Microsystems, Inc.
+ * Tim Hockin <thockin@sun.com>
+ *
+@@ -14,7 +14,7 @@
+ * try to request it here, but if it fails, we carry on anyway.
+ *
+ * This is how the chip is attached, so said the schematic:
+- * * a 4 MiB (32 Mb) 16 bit chip
++ * * a 4 MiB (32 Mib) 16 bit chip
+ * * a 1 MiB memory region
+ * * A20 and A21 pulled up
+ * * D8-D15 ignored
+@@ -48,6 +48,7 @@
+ #include <linux/module.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
+ #include <asm/io.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+@@ -60,65 +61,13 @@
+ #define SCB2_ADDR 0xfff00000
+ #define SCB2_WINDOW 0x00100000
+
+-static __u8 scb2_read8(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readb(map->map_priv_1 + ofs);
+-}
+-
+-static __u16 scb2_read16(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readw(map->map_priv_1 + ofs);
+-}
+-
+-static __u32 scb2_read32(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readl(map->map_priv_1 + ofs);
+-}
+-
+-static void scb2_copy_from(struct map_info *map, void *to,
+- unsigned long from, ssize_t len)
+-{
+- memcpy_fromio(to, map->map_priv_1 + from, len);
+-}
+-
+-static void scb2_write8(struct map_info *map, __u8 d, unsigned long adr)
+-{
+- __raw_writeb(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-static void scb2_write16(struct map_info *map, __u16 d, unsigned long adr)
+-{
+- __raw_writew(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-static void scb2_write32(struct map_info *map, __u32 d, unsigned long adr)
+-{
+- __raw_writel(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-static void scb2_copy_to(struct map_info *map, unsigned long to,
+- const void *from, ssize_t len)
+-{
+- memcpy_toio(map->map_priv_1 + to, from, len);
+-}
+
+ static void *scb2_ioaddr;
+ static struct mtd_info *scb2_mtd;
+ struct map_info scb2_map = {
+- name: "SCB2 BIOS Flash",
+- size: 0,
+- buswidth: 1,
+- read8: scb2_read8,
+- read16: scb2_read16,
+- read32: scb2_read32,
+- copy_from: scb2_copy_from,
+- write8: scb2_write8,
+- write16: scb2_write16,
+- write32: scb2_write32,
+- copy_to: scb2_copy_to,
++ .name = "SCB2 BIOS Flash",
++ .size = 0,
++ .buswidth = 1,
+ };
+ static int region_fail;
+
+@@ -137,6 +86,8 @@
+ return -1;
+ }
+
++ /* I wasn't here. I didn't see. dwmw2. */
++
+ /* the chip is sometimes bigger than the map - what a waste */
+ mtd->size = map->size;
+
+@@ -211,9 +162,12 @@
+ return -ENOMEM;
+ }
+
+- scb2_map.map_priv_1 = (unsigned long)scb2_ioaddr;
++ scb2_map.phys = SCB2_ADDR;
++ scb2_map.virt = (unsigned long)scb2_ioaddr;
+ scb2_map.size = SCB2_WINDOW;
+
++ simple_map_init(&scb2_map);
++
+ /* try to find a chip */
+ scb2_mtd = do_map_probe("cfi_probe", &scb2_map);
+
+@@ -225,7 +179,7 @@
+ return -ENODEV;
+ }
+
+- scb2_mtd->module = THIS_MODULE;
++ scb2_mtd->owner = THIS_MODULE;
+ if (scb2_fixup_mtd(scb2_mtd) < 0) {
+ del_mtd_device(scb2_mtd);
+ map_destroy(scb2_mtd);
+@@ -235,7 +189,7 @@
+ return -ENODEV;
+ }
+
+- printk(KERN_NOTICE MODNAME ": chip size %x at offset %x\n",
++ printk(KERN_NOTICE MODNAME ": chip size 0x%x at offset 0x%x\n",
+ scb2_mtd->size, SCB2_WINDOW - scb2_mtd->size);
+
+ add_mtd_device(scb2_mtd);
+@@ -266,19 +220,19 @@
+
+ static struct pci_device_id scb2_flash_pci_ids[] __devinitdata = {
+ {
+- vendor: PCI_VENDOR_ID_SERVERWORKS,
+- device: PCI_DEVICE_ID_SERVERWORKS_CSB5,
+- subvendor: PCI_ANY_ID,
+- subdevice: PCI_ANY_ID
++ .vendor = PCI_VENDOR_ID_SERVERWORKS,
++ .device = PCI_DEVICE_ID_SERVERWORKS_CSB5,
++ .subvendor = PCI_ANY_ID,
++ .subdevice = PCI_ANY_ID
+ },
+ { 0, }
+ };
+
+ static struct pci_driver scb2_flash_driver = {
+- name: "Intel SCB2 BIOS Flash",
+- id_table: scb2_flash_pci_ids,
+- probe: scb2_flash_probe,
+- remove: __devexit_p(scb2_flash_remove),
++ .name = "Intel SCB2 BIOS Flash",
++ .id_table = scb2_flash_pci_ids,
++ .probe = scb2_flash_probe,
++ .remove = __devexit_p(scb2_flash_remove),
+ };
+
+ static int __init
+diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/scx200_docflash.c linux/drivers/mtd/maps/scx200_docflash.c
+--- linux-mips-2.4.27/drivers/mtd/maps/scx200_docflash.c 2003-02-26 01:53:50.000000000 +0100
++++ linux/drivers/mtd/maps/scx200_docflash.c 2004-11-19 10:25:11.973188544 +0100
+@@ -2,7 +2,7 @@
+
+ Copyright (c) 2001,2002 Christer Weinigel <wingel@nano-system.com>
+
+- $Id$
++ $Id$
+
+ National Semiconductor SCx200 flash mapped with DOCCS
+ */
+@@ -11,6 +11,7 @@
+ #include <linux/config.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
+ #include <asm/io.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+@@ -75,46 +76,9 @@
+ #define NUM_PARTITIONS (sizeof(partition_info)/sizeof(partition_info[0]))
+ #endif
+
+-static __u8 scx200_docflash_read8(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readb(map->map_priv_1 + ofs);
+-}
+-
+-static __u16 scx200_docflash_read16(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readw(map->map_priv_1 + ofs);
+-}
+-
+-static void scx200_docflash_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+-{
+- memcpy_fromio(to, map->map_priv_1 + from, len);
+-}
+-
+-static void scx200_docflash_write8(struct map_info *map, __u8 d, unsigned long adr)
+-{
+- __raw_writeb(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-static void scx200_docflash_write16(struct map_info *map, __u16 d, unsigned long adr)
+-{
+- __raw_writew(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-static void scx200_docflash_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+-{
+- memcpy_toio(map->map_priv_1 + to, from, len);
+-}
+
+ static struct map_info scx200_docflash_map = {
+ .name = "NatSemi SCx200 DOCCS Flash",
+- .read8 = scx200_docflash_read8,
+- .read16 = scx200_docflash_read16,
+- .copy_from = scx200_docflash_copy_from,
+- .write8 = scx200_docflash_write8,
+- .write16 = scx200_docflash_write16,
+- .copy_to = scx200_docflash_copy_to
+ };
+
+ int __init init_scx200_docflash(void)
+@@ -213,8 +177,11 @@
+ else
+ scx200_docflash_map.buswidth = 2;
+
+- scx200_docflash_map.map_priv_1 = (unsigned long)ioremap(docmem.start, scx200_docflash_map.size);
+- if (!scx200_docflash_map.map_priv_1) {
++ simple_map_init(&scx200_docflash_map);
++
++ scx200_docflash_map.phys = docmem.start;
++ scx200_docflash_map.virt = (unsigned long)ioremap(docmem.start, scx200_docflash_map.size);
++ if (!scx200_docflash_map.virt) {
+ printk(KERN_ERR NAME ": failed to ioremap the flash\n");
+ release_resource(&docmem);
+ return -EIO;
+@@ -223,7 +190,7 @@
+ mymtd = do_map_probe(flashtype, &scx200_docflash_map);
+ if (!mymtd) {
+ printk(KERN_ERR NAME ": unable to detect flash\n");
+- iounmap((void *)scx200_docflash_map.map_priv_1);
++ iounmap((void *)scx200_docflash_map.virt);
+ release_resource(&docmem);
+ return -ENXIO;
+ }
+@@ -231,7 +198,7 @@
+ if (size < mymtd->size)
+ printk(KERN_WARNING NAME ": warning, flash mapping is smaller than flash size\n");
+
+- mymtd->module = THIS_MODULE;
++ mymtd->owner = THIS_MODULE;
+
+ #if PARTITION
+ partition_info[3].offset = mymtd->size-partition_info[3].size;
+@@ -253,8 +220,8 @@
+ #endif
+ map_destroy(mymtd);
+ }
+- if (scx200_docflash_map.map_priv_1) {
+- iounmap((void *)scx200_docflash_map.map_priv_1);
++ if (scx200_docflash_map.virt) {
++ iounmap((void *)scx200_docflash_map.virt);
+ release_resource(&docmem);
+ }
+ }
+diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/solutionengine.c linux/drivers/mtd/maps/solutionengine.c
+--- linux-mips-2.4.27/drivers/mtd/maps/solutionengine.c 2002-06-27 00:35:50.000000000 +0200
++++ linux/drivers/mtd/maps/solutionengine.c 2004-11-19 10:25:11.975188240 +0100
+@@ -1,5 +1,5 @@
+ /*
+- * $Id$
++ * $Id$
+ *
+ * Flash and EPROM on Hitachi Solution Engine and similar boards.
+ *
+@@ -11,6 +11,7 @@
+ #include <linux/module.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
+ #include <asm/io.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+@@ -18,60 +19,39 @@
+ #include <linux/config.h>
+
+
+-extern int parse_redboot_partitions(struct mtd_info *master, struct mtd_partition **pparts);
+-
+-__u32 soleng_read32(struct map_info *map, unsigned long ofs)
+-{
+- return __raw_readl(map->map_priv_1 + ofs);
+-}
+-
+-void soleng_write32(struct map_info *map, __u32 d, unsigned long adr)
+-{
+- __raw_writel(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-void soleng_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+-{
+- memcpy_fromio(to, map->map_priv_1 + from, len);
+-}
+-
+-
+ static struct mtd_info *flash_mtd;
+ static struct mtd_info *eprom_mtd;
+
+ static struct mtd_partition *parsed_parts;
+
+ struct map_info soleng_eprom_map = {
+- name: "Solution Engine EPROM",
+- size: 0x400000,
+- buswidth: 4,
+- copy_from: soleng_copy_from,
++ .name = "Solution Engine EPROM",
++ .size = 0x400000,
++ .buswidth = 4,
+ };
+
+ struct map_info soleng_flash_map = {
+- name: "Solution Engine FLASH",
+- size: 0x400000,
+- buswidth: 4,
+- read32: soleng_read32,
+- copy_from: soleng_copy_from,
+- write32: soleng_write32,
++ .name = "Solution Engine FLASH",
++ .size = 0x400000,
++ .buswidth = 4,
+ };
+
++static const char *probes[] = { "RedBoot", "cmdlinepart", NULL };
++
+ #ifdef CONFIG_MTD_SUPERH_RESERVE
+ static struct mtd_partition superh_se_partitions[] = {
+ /* Reserved for boot code, read-only */
+ {
+- name: "flash_boot",
+- offset: 0x00000000,
+- size: CONFIG_MTD_SUPERH_RESERVE,
+- mask_flags: MTD_WRITEABLE,
++ .name = "flash_boot",
++ .offset = 0x00000000,
++ .size = CONFIG_MTD_SUPERH_RESERVE,
++ .mask_flags = MTD_WRITEABLE,
+ },
+ /* All else is writable (e.g. JFFS) */
+ {
+- name: "Flash FS",
+- offset: MTDPART_OFS_NXTBLK,
+- size: MTDPART_SIZ_FULL,
++ .name = "Flash FS",
++ .offset = MTDPART_OFS_NXTBLK,
++ .size = MTDPART_SIZ_FULL,
+ }
+ };
+ #endif /* CONFIG_MTD_SUPERH_RESERVE */
+@@ -81,16 +61,22 @@
+ int nr_parts = 0;
+
+ /* First probe at offset 0 */
+- soleng_flash_map.map_priv_1 = P2SEGADDR(0);
+- soleng_eprom_map.map_priv_1 = P1SEGADDR(0x01000000);
++ soleng_flash_map.phys = 0;
++ soleng_flash_map.virt = P2SEGADDR(0);
++ soleng_eprom_map.phys = 0x01000000;
++ soleng_eprom_map.virt = P1SEGADDR(0x01000000);
++ simple_map_init(&soleng_eprom_map);
++ simple_map_init(&soleng_flash_map);
+
+ printk(KERN_NOTICE "Probing for flash chips at 0x00000000:\n");
+ flash_mtd = do_map_probe("cfi_probe", &soleng_flash_map);
+ if (!flash_mtd) {
+ /* Not there. Try swapping */
+ printk(KERN_NOTICE "Probing for flash chips at 0x01000000:\n");
+- soleng_flash_map.map_priv_1 = P2SEGADDR(0x01000000);
+- soleng_eprom_map.map_priv_1 = P1SEGADDR(0);
++ soleng_flash_map.phys = 0x01000000;
++ soleng_flash_map.virt = P2SEGADDR(0x01000000);
++ soleng_eprom_map.phys = 0;
++ soleng_eprom_map.virt = P1SEGADDR(0);
+ flash_mtd = do_map_probe("cfi_probe", &soleng_flash_map);
+ if (!flash_mtd) {
+ /* Eep. */
+@@ -99,25 +85,20 @@
+ }
+ }
+ printk(KERN_NOTICE "Solution Engine: Flash at 0x%08lx, EPROM at 0x%08lx\n",
+- soleng_flash_map.map_priv_1 & 0x1fffffff,
+- soleng_eprom_map.map_priv_1 & 0x1fffffff);
+- flash_mtd->module = THIS_MODULE;
++ soleng_flash_map.phys & 0x1fffffff,
++ soleng_eprom_map.phys & 0x1fffffff);
++ flash_mtd->owner = THIS_MODULE;
+
+ eprom_mtd = do_map_probe("map_rom", &soleng_eprom_map);
+ if (eprom_mtd) {
+- eprom_mtd->module = THIS_MODULE;
++ eprom_mtd->owner = THIS_MODULE;
+ add_mtd_device(eprom_mtd);
+ }
+
+-#ifdef CONFIG_MTD_REDBOOT_PARTS
+- nr_parts = parse_redboot_partitions(flash_mtd, &parsed_parts);
+- if (nr_parts > 0)
+- printk(KERN_NOTICE "Found RedBoot partition table.\n");
+- else if (nr_parts < 0)
+- printk(KERN_NOTICE "Error looking for RedBoot partitions.\n");
+-#endif /* CONFIG_MTD_REDBOOT_PARTS */
++ nr_parts = parse_mtd_partitions(flash_mtd, probes, &parsed_parts, 0);
++
+ #if CONFIG_MTD_SUPERH_RESERVE
+- if (nr_parts == 0) {
++ if (nr_parts <= 0) {
+ printk(KERN_NOTICE "Using configured partition at 0x%08x.\n",
+ CONFIG_MTD_SUPERH_RESERVE);
+ parsed_parts = superh_se_partitions;
+diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/sun_uflash.c linux/drivers/mtd/maps/sun_uflash.c
+--- linux-mips-2.4.27/drivers/mtd/maps/sun_uflash.c 2001-11-05 21:15:52.000000000 +0100
++++ linux/drivers/mtd/maps/sun_uflash.c 2004-11-19 10:25:11.976188088 +0100
+@@ -1,4 +1,4 @@
+-/* $Id$
++/* $Id$
+ *
+ * sun_uflash - Driver implementation for user-programmable flash
+ * present on many Sun Microsystems SME boardsets.
+@@ -48,60 +48,11 @@
+ struct list_head list;
+ };
+
+-__u8 uflash_read8(struct map_info *map, unsigned long ofs)
+-{
+- return(__raw_readb(map->map_priv_1 + ofs));
+-}
+-
+-__u16 uflash_read16(struct map_info *map, unsigned long ofs)
+-{
+- return(__raw_readw(map->map_priv_1 + ofs));
+-}
+-
+-__u32 uflash_read32(struct map_info *map, unsigned long ofs)
+-{
+- return(__raw_readl(map->map_priv_1 + ofs));
+-}
+-
+-void uflash_copy_from(struct map_info *map, void *to, unsigned long from,
+- ssize_t len)
+-{
+- memcpy_fromio(to, map->map_priv_1 + from, len);
+-}
+-
+-void uflash_write8(struct map_info *map, __u8 d, unsigned long adr)
+-{
+- __raw_writeb(d, map->map_priv_1 + adr);
+-}
+-
+-void uflash_write16(struct map_info *map, __u16 d, unsigned long adr)
+-{
+- __raw_writew(d, map->map_priv_1 + adr);
+-}
+-
+-void uflash_write32(struct map_info *map, __u32 d, unsigned long adr)
+-{
+- __raw_writel(d, map->map_priv_1 + adr);
+-}
+-
+-void uflash_copy_to(struct map_info *map, unsigned long to, const void *from,
+- ssize_t len)
+-{
+- memcpy_toio(map->map_priv_1 + to, from, len);
+-}
+
+ struct map_info uflash_map_templ = {
+- name: "SUNW,???-????",
+- size: UFLASH_WINDOW_SIZE,
+- buswidth: UFLASH_BUSWIDTH,
+- read8: uflash_read8,
+- read16: uflash_read16,
+- read32: uflash_read32,
+- copy_from: uflash_copy_from,
+- write8: uflash_write8,
+- write16: uflash_write16,
+- write32: uflash_write32,
+- copy_to: uflash_copy_to
++ .name = "SUNW,???-????",
++ .size = UFLASH_WINDOW_SIZE,
++ .buswidth = UFLASH_BUSWIDTH,
+ };
+
+ int uflash_devinit(struct linux_ebus_device* edev)
+@@ -145,20 +96,22 @@
+ if(0 != pdev->name && 0 < strlen(pdev->name)) {
+ pdev->map.name = pdev->name;
+ }
+-
+- pdev->map.map_priv_1 =
++ pdev->phys = edev->resource[0].start;
++ pdev->virt =
+ (unsigned long)ioremap_nocache(edev->resource[0].start, pdev->map.size);
+- if(0 == pdev->map.map_priv_1) {
++ if(0 == pdev->map.virt) {
+ printk("%s: failed to map device\n", __FUNCTION__);
+ kfree(pdev->name);
+ kfree(pdev);
+ return(-1);
+ }
+
++ simple_map_init(&pdev->map);
++
+ /* MTD registration */
+ pdev->mtd = do_map_probe("cfi_probe", &pdev->map);
+ if(0 == pdev->mtd) {
+- iounmap((void *)pdev->map.map_priv_1);
++ iounmap((void *)pdev->map.virt);
+ kfree(pdev->name);
+ kfree(pdev);
+ return(-ENXIO);
+@@ -166,7 +119,7 @@
+
+ list_add(&pdev->list, &device_list);
+
+- pdev->mtd->module = THIS_MODULE;
++ pdev->mtd->owner = THIS_MODULE;
+
+ add_mtd_device(pdev->mtd);
+ return(0);
+@@ -211,9 +164,9 @@
+ del_mtd_device(udev->mtd);
+ map_destroy(udev->mtd);
+ }
+- if(0 != udev->map.map_priv_1) {
+- iounmap((void*)udev->map.map_priv_1);
+- udev->map.map_priv_1 = 0;
++ if(0 != udev->map.virt) {
++ iounmap((void*)udev->map.virt);
++ udev->map.virt = 0;
+ }
+ if(0 != udev->name) {
+ kfree(udev->name);
+diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/tqm8xxl.c linux/drivers/mtd/maps/tqm8xxl.c
+--- linux-mips-2.4.27/drivers/mtd/maps/tqm8xxl.c 2003-02-26 01:53:50.000000000 +0100
++++ linux/drivers/mtd/maps/tqm8xxl.c 2004-11-19 10:25:11.978187784 +0100
+@@ -2,7 +2,7 @@
+ * Handle mapping of the flash memory access routines
+ * on TQM8xxL based devices.
+ *
+- * $Id$
++ * $Id$
+ *
+ * based on rpxlite.c
+ *
+@@ -26,6 +26,7 @@
+ #include <linux/module.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
+ #include <asm/io.h>
+
+ #include <linux/mtd/mtd.h>
+@@ -51,46 +52,6 @@
+ static unsigned long num_banks;
+ static unsigned long start_scan_addr;
+
+-__u8 tqm8xxl_read8(struct map_info *map, unsigned long ofs)
+-{
+- return *((__u8 *)(map->map_priv_1 + ofs));
+-}
+-
+-__u16 tqm8xxl_read16(struct map_info *map, unsigned long ofs)
+-{
+- return *((__u16 *)(map->map_priv_1 + ofs));
+-}
+-
+-__u32 tqm8xxl_read32(struct map_info *map, unsigned long ofs)
+-{
+- return *((__u32 *)(map->map_priv_1 + ofs));
+-}
+-
+-void tqm8xxl_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+-{
+- memcpy_fromio(to, (void *)(map->map_priv_1 + from), len);
+-}
+-
+-void tqm8xxl_write8(struct map_info *map, __u8 d, unsigned long adr)
+-{
+- *((__u8 *)(map->map_priv_1 + adr)) = d;
+-}
+-
+-void tqm8xxl_write16(struct map_info *map, __u16 d, unsigned long adr)
+-{
+- *((__u16 *)( map->map_priv_1 + adr)) = d;
+-}
+-
+-void tqm8xxl_write32(struct map_info *map, __u32 d, unsigned long adr)
+-{
+- *((__u32 *)(map->map_priv_1 + adr)) = d;
+-}
+-
+-void tqm8xxl_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+-{
+- memcpy_toio((void *)(map->map_priv_1 + to), from, len);
+-}
+-
+ /*
+ * Here are partition information for all known TQM8xxL series devices.
+ * See include/linux/mtd/partitions.h for definition of the mtd_partition
+@@ -107,50 +68,48 @@
+ static unsigned long tqm8xxl_max_flash_size = 0x00800000;
+
+ /* partition definition for first flash bank
+- * also ref. to "drivers\char\flash_config.c"
++ * (cf. "drivers/char/flash_config.c")
+ */
+ static struct mtd_partition tqm8xxl_partitions[] = {
+ {
+- name: "ppcboot",
+- offset: 0x00000000,
+- size: 0x00020000, /* 128KB */
+- mask_flags: MTD_WRITEABLE, /* force read-only */
++ .name = "ppcboot",
++ .offset = 0x00000000,
++ .size = 0x00020000, /* 128KB */
++ .mask_flags = MTD_WRITEABLE, /* force read-only */
+ },
+ {
+- name: "kernel", /* default kernel image */
+- offset: 0x00020000,
+- size: 0x000e0000,
+- mask_flags: MTD_WRITEABLE, /* force read-only */
++ .name = "kernel", /* default kernel image */
++ .offset = 0x00020000,
++ .size = 0x000e0000,
++ .mask_flags = MTD_WRITEABLE, /* force read-only */
+ },
+ {
+- name: "user",
+- offset: 0x00100000,
+- size: 0x00100000,
++ .name = "user",
++ .offset = 0x00100000,
++ .size = 0x00100000,
+ },
+ {
+- name: "initrd",
+- offset: 0x00200000,
+- size: 0x00200000,
++ .name = "initrd",
++ .offset = 0x00200000,
++ .size = 0x00200000,
+ }
+ };
+-/* partition definition for second flahs bank */
++/* partition definition for second flash bank */
+ static struct mtd_partition tqm8xxl_fs_partitions[] = {
+ {
+- name: "cramfs",
+- offset: 0x00000000,
+- size: 0x00200000,
++ .name = "cramfs",
++ .offset = 0x00000000,
++ .size = 0x00200000,
+ },
+ {
+- name: "jffs",
+- offset: 0x00200000,
+- size: 0x00200000,
+- //size: MTDPART_SIZ_FULL,
++ .name = "jffs",
++ .offset = 0x00200000,
++ .size = 0x00200000,
++ .//size = MTDPART_SIZ_FULL,
+ }
+ };
+ #endif
+
+-#define NB_OF(x) (sizeof(x)/sizeof(x[0]))
+-
+ int __init init_tqm_mtd(void)
+ {
+ int idx = 0, ret = 0;
+@@ -160,67 +119,73 @@
+
+ flash_addr = bd->bi_flashstart;
+ flash_size = bd->bi_flashsize;
+- //request maximum flash size address spzce
++
++ //request maximum flash size address space
+ start_scan_addr = (unsigned long)ioremap(flash_addr, flash_size);
+ if (!start_scan_addr) {
+- //printk("%s:Failed to ioremap address:0x%x\n", __FUNCTION__, FLASH_ADDR);
+- printk("%s:Failed to ioremap address:0x%x\n", __FUNCTION__, flash_addr);
++ printk(KERN_WARNING "%s:Failed to ioremap address:0x%x\n", __FUNCTION__, flash_addr);
+ return -EIO;
+ }
+- for(idx = 0 ; idx < FLASH_BANK_MAX ; idx++)
+- {
++
++ for (idx = 0 ; idx < FLASH_BANK_MAX ; idx++) {
+ if(mtd_size >= flash_size)
+ break;
+
+- printk("%s: chip probing count %d\n", __FUNCTION__, idx);
++ printk(KERN_INFO "%s: chip probing count %d\n", __FUNCTION__, idx);
+
+ map_banks[idx] = (struct map_info *)kmalloc(sizeof(struct map_info), GFP_KERNEL);
+- if(map_banks[idx] == NULL)
+- {
+- //return -ENOMEM;
++ if(map_banks[idx] == NULL) {
+ ret = -ENOMEM;
++ /* FIXME: What if some MTD devices were probed already? */
+ goto error_mem;
+ }
++
+ memset((void *)map_banks[idx], 0, sizeof(struct map_info));
+ map_banks[idx]->name = (char *)kmalloc(16, GFP_KERNEL);
+- if(map_banks[idx]->name == NULL)
+- {
+- //return -ENOMEM;
++
++ if (!map_banks[idx]->name) {
+ ret = -ENOMEM;
++ /* FIXME: What if some MTD devices were probed already? */
+ goto error_mem;
+ }
+- memset((void *)map_banks[idx]->name, 0, 16);
+-
+ sprintf(map_banks[idx]->name, "TQM8xxL%d", idx);
++
+ map_banks[idx]->size = flash_size;
+ map_banks[idx]->buswidth = 4;
+- map_banks[idx]->read8 = tqm8xxl_read8;
+- map_banks[idx]->read16 = tqm8xxl_read16;
+- map_banks[idx]->read32 = tqm8xxl_read32;
+- map_banks[idx]->copy_from = tqm8xxl_copy_from;
+- map_banks[idx]->write8 = tqm8xxl_write8;
+- map_banks[idx]->write16 = tqm8xxl_write16;
+- map_banks[idx]->write32 = tqm8xxl_write32;
+- map_banks[idx]->copy_to = tqm8xxl_copy_to;
++
++ simple_map_init(map_banks[idx]);
++
++ map_banks[idx]->virt = start_scan_addr;
++ map_banks[idx]->phys = flash_addr;
++ /* FIXME: This looks utterly bogus, but I'm trying to
++ preserve the behaviour of the original (shown here)...
++
+ map_banks[idx]->map_priv_1 =
+ start_scan_addr + ((idx > 0) ?
+ (mtd_banks[idx-1] ? mtd_banks[idx-1]->size : 0) : 0);
++ */
++
++ if (idx && mtd_banks[idx-1]) {
++ map_banks[idx]->virt += mtd_banks[idx-1]->size;
++ map_banks[idx]->phys += mtd_banks[idx-1]->size;
++ }
++
+ //start to probe flash chips
+ mtd_banks[idx] = do_map_probe("cfi_probe", map_banks[idx]);
+- if(mtd_banks[idx])
+- {
+- mtd_banks[idx]->module = THIS_MODULE;
++
++ if (mtd_banks[idx]) {
++ mtd_banks[idx]->owner = THIS_MODULE;
+ mtd_size += mtd_banks[idx]->size;
+ num_banks++;
+- printk("%s: bank%d, name:%s, size:%dbytes \n", __FUNCTION__, num_banks,
++
++ printk(KERN_INFO "%s: bank%d, name:%s, size:%dbytes \n", __FUNCTION__, num_banks,
+ mtd_banks[idx]->name, mtd_banks[idx]->size);
+ }
+ }
+
+ /* no supported flash chips found */
+- if(!num_banks)
+- {
+- printk("TQM8xxL: No support flash chips found!\n");
++ if (!num_banks) {
++ printk(KERN_NOTICE "TQM8xxL: No support flash chips found!\n");
+ ret = -ENXIO;
+ goto error_mem;
+ }
+@@ -231,12 +196,13 @@
+ */
+ part_banks[0].mtd_part = tqm8xxl_partitions;
+ part_banks[0].type = "Static image";
+- part_banks[0].nums = NB_OF(tqm8xxl_partitions);
++ part_banks[0].nums = ARRAY_SIZE(tqm8xxl_partitions);
++
+ part_banks[1].mtd_part = tqm8xxl_fs_partitions;
+ part_banks[1].type = "Static file system";
+- part_banks[1].nums = NB_OF(tqm8xxl_fs_partitions);
+- for(idx = 0; idx < num_banks ; idx++)
+- {
++ part_banks[1].nums = ARRAY_SIZE(tqm8xxl_fs_partitions);
++
++ for(idx = 0; idx < num_banks ; idx++) {
+ if (part_banks[idx].nums == 0) {
+ printk(KERN_NOTICE "TQM flash%d: no partition info available, registering whole flash at once\n", idx);
+ add_mtd_device(mtd_banks[idx]);
+@@ -254,12 +220,9 @@
+ #endif
+ return 0;
+ error_mem:
+- for(idx = 0 ; idx < FLASH_BANK_MAX ; idx++)
+- {
+- if(map_banks[idx] != NULL)
+- {
+- if(map_banks[idx]->name != NULL)
+- {
++ for(idx = 0 ; idx < FLASH_BANK_MAX ; idx++) {
++ if(map_banks[idx] != NULL) {
++ if(map_banks[idx]->name != NULL) {
+ kfree(map_banks[idx]->name);
+ map_banks[idx]->name = NULL;
+ }
+@@ -267,18 +230,15 @@
+ map_banks[idx] = NULL;
+ }
+ }
+- //return -ENOMEM;
+ error:
+ iounmap((void *)start_scan_addr);
+- //return -ENXIO;
+ return ret;
+ }
+
+ static void __exit cleanup_tqm_mtd(void)
+ {
+ unsigned int idx = 0;
+- for(idx = 0 ; idx < num_banks ; idx++)
+- {
++ for(idx = 0 ; idx < num_banks ; idx++) {
+ /* destroy mtd_info previously allocated */
+ if (mtd_banks[idx]) {
+ del_mtd_partitions(mtd_banks[idx]);
+@@ -288,6 +248,7 @@
+ kfree(map_banks[idx]->name);
+ kfree(map_banks[idx]);
+ }
++
+ if (start_scan_addr) {
+ iounmap((void *)start_scan_addr);
+ start_scan_addr = 0;
+diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/tsunami_flash.c linux/drivers/mtd/maps/tsunami_flash.c
+--- linux-mips-2.4.27/drivers/mtd/maps/tsunami_flash.c 2002-06-27 00:35:50.000000000 +0200
++++ linux/drivers/mtd/maps/tsunami_flash.c 2004-11-19 10:25:11.979187632 +0100
+@@ -2,11 +2,13 @@
+ * tsunami_flash.c
+ *
+ * flash chip on alpha ds10...
+- * $Id$
++ * $Id$
+ */
+ #include <asm/io.h>
+ #include <asm/core_tsunami.h>
++#include <linux/init.h>
+ #include <linux/mtd/map.h>
++#include <linux/mtd/mtd.h>
+
+ #define FLASH_ENABLE_PORT 0x00C00001
+ #define FLASH_ENABLE_BYTE 0x01
+@@ -58,18 +60,12 @@
+ static struct map_info tsunami_flash_map = {
+ .name = "flash chip on the Tsunami TIG bus",
+ .size = MAX_TIG_FLASH_SIZE,
++ .phys = NO_XIP;
+ .buswidth = 1,
+ .read8 = tsunami_flash_read8,
+- .read16 = 0,
+- .read32 = 0,
+ .copy_from = tsunami_flash_copy_from,
+ .write8 = tsunami_flash_write8,
+- .write16 = 0,
+- .write32 = 0,
+ .copy_to = tsunami_flash_copy_to,
+- .set_vpp = 0,
+- .map_priv_1 = 0,
+-
+ };
+
+ static struct mtd_info *tsunami_flash_mtd;
+@@ -99,7 +95,7 @@
+ tsunami_flash_mtd = do_map_probe(*type, &tsunami_flash_map);
+ }
+ if (tsunami_flash_mtd) {
+- tsunami_flash_mtd->module = THIS_MODULE;
++ tsunami_flash_mtd->owner = THIS_MODULE;
+ add_mtd_device(tsunami_flash_mtd);
+ return 0;
+ }
+diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/uclinux.c linux/drivers/mtd/maps/uclinux.c
+--- linux-mips-2.4.27/drivers/mtd/maps/uclinux.c 2003-02-26 01:53:50.000000000 +0100
++++ linux/drivers/mtd/maps/uclinux.c 2004-11-19 10:25:11.980187480 +0100
+@@ -5,7 +5,7 @@
+ *
+ * (C) Copyright 2002, Greg Ungerer (gerg@snapgear.com)
+ *
+- * $Id$
++ * $Id$
+ */
+
+ /****************************************************************************/
+@@ -24,58 +24,11 @@
+
+ /****************************************************************************/
+
+-__u8 uclinux_read8(struct map_info *map, unsigned long ofs)
+-{
+- return(*((__u8 *) (map->map_priv_1 + ofs)));
+-}
+-
+-__u16 uclinux_read16(struct map_info *map, unsigned long ofs)
+-{
+- return(*((__u16 *) (map->map_priv_1 + ofs)));
+-}
+-
+-__u32 uclinux_read32(struct map_info *map, unsigned long ofs)
+-{
+- return(*((__u32 *) (map->map_priv_1 + ofs)));
+-}
+-
+-void uclinux_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+-{
+- memcpy(to, (void *)(map->map_priv_1 + from), len);
+-}
+-
+-void uclinux_write8(struct map_info *map, __u8 d, unsigned long adr)
+-{
+- *((__u8 *) (map->map_priv_1 + adr)) = d;
+-}
+-
+-void uclinux_write16(struct map_info *map, __u16 d, unsigned long adr)
+-{
+- *((__u16 *) (map->map_priv_1 + adr)) = d;
+-}
+-
+-void uclinux_write32(struct map_info *map, __u32 d, unsigned long adr)
+-{
+- *((__u32 *) (map->map_priv_1 + adr)) = d;
+-}
+-
+-void uclinux_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+-{
+- memcpy((void *) (map->map_priv_1 + to), from, len);
+-}
+
+ /****************************************************************************/
+
+ struct map_info uclinux_ram_map = {
+- name: "RAM",
+- read8: uclinux_read8,
+- read16: uclinux_read16,
+- read32: uclinux_read32,
+- copy_from: uclinux_copy_from,
+- write8: uclinux_write8,
+- write16: uclinux_write16,
+- write32: uclinux_write32,
+- copy_to: uclinux_copy_to,
++ .name = "RAM",
+ };
+
+ struct mtd_info *uclinux_ram_mtdinfo;
+@@ -83,7 +36,7 @@
+ /****************************************************************************/
+
+ struct mtd_partition uclinux_romfs[] = {
+- { name: "ROMfs", offset: 0 }
++ { .name = "ROMfs" }
+ };
+
+ #define NUM_PARTITIONS (sizeof(uclinux_romfs) / sizeof(uclinux_romfs[0]))
+@@ -94,7 +47,7 @@
+ size_t *retlen, u_char **mtdbuf)
+ {
+ struct map_info *map = (struct map_info *) mtd->priv;
+- *mtdbuf = (u_char *) (map->map_priv_1 + ((int) from));
++ *mtdbuf = (u_char *) (map->virt + ((int) from));
+ *retlen = len;
+ return(0);
+ }
+@@ -108,29 +61,31 @@
+ extern char _ebss;
+
+ mapp = &uclinux_ram_map;
+- mapp->map_priv_2 = (unsigned long) &_ebss;
++ mapp->phys = (unsigned long) &_ebss;
+ mapp->size = PAGE_ALIGN(*((unsigned long *)((&_ebss) + 8)));
+ mapp->buswidth = 4;
+
+ printk("uclinux[mtd]: RAM probe address=0x%x size=0x%x\n",
+ (int) mapp->map_priv_2, (int) mapp->size);
+
+- mapp->map_priv_1 = (unsigned long)
+- ioremap_nocache(mapp->map_priv_2, mapp->size);
++ mapp->virt = (unsigned long)
++ ioremap_nocache(mapp->phys, mapp->size);
+
+- if (mapp->map_priv_1 == 0) {
++ if (mapp->virt == 0) {
+ printk("uclinux[mtd]: ioremap_nocache() failed\n");
+ return(-EIO);
+ }
+
++ simple_map_init(mapp);
++
+ mtd = do_map_probe("map_ram", mapp);
+ if (!mtd) {
+ printk("uclinux[mtd]: failed to find a mapping?\n");
+- iounmap((void *) mapp->map_priv_1);
++ iounmap((void *) mapp->virt);
+ return(-ENXIO);
+ }
+
+- mtd->module = THIS_MODULE;
++ mtd->owner = THIS_MODULE;
+ mtd->point = uclinux_point;
+ mtd->priv = mapp;
+
+@@ -155,8 +110,8 @@
+ uclinux_ram_mtdinfo = NULL;
+ }
+ if (uclinux_ram_map.map_priv_1) {
+- iounmap((void *) uclinux_ram_map.map_priv_1);
+- uclinux_ram_map.map_priv_1 = 0;
++ iounmap((void *) uclinux_ram_map.virt);
++ uclinux_ram_map.virt = 0;
+ }
+ }
+
+diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/vmax301.c linux/drivers/mtd/maps/vmax301.c
+--- linux-mips-2.4.27/drivers/mtd/maps/vmax301.c 2001-11-05 21:15:52.000000000 +0100
++++ linux/drivers/mtd/maps/vmax301.c 2004-11-19 10:25:11.982187176 +0100
+@@ -1,4 +1,4 @@
+-// $Id$
++// $Id$
+ /* ######################################################################
+
+ Tempustech VMAX SBC301 MTD Driver.
+@@ -24,6 +24,7 @@
+ #include <asm/io.h>
+
+ #include <linux/mtd/map.h>
++#include <linux/mtd/mtd.h>
+
+
+ #define WINDOW_START 0xd8000
+@@ -142,34 +143,36 @@
+
+ static struct map_info vmax_map[2] = {
+ {
+- name: "VMAX301 Internal Flash",
+- size: 3*2*1024*1024,
+- buswidth: 1,
+- read8: vmax301_read8,
+- read16: vmax301_read16,
+- read32: vmax301_read32,
+- copy_from: vmax301_copy_from,
+- write8: vmax301_write8,
+- write16: vmax301_write16,
+- write32: vmax301_write32,
+- copy_to: vmax301_copy_to,
+- map_priv_1: WINDOW_START + WINDOW_LENGTH,
+- map_priv_2: 0xFFFFFFFF
++ .name = "VMAX301 Internal Flash",
++ .phys = NO_XIP,
++ .size = 3*2*1024*1024,
++ .buswidth = 1,
++ .read8 = vmax301_read8,
++ .read16 = vmax301_read16,
++ .read32 = vmax301_read32,
++ .copy_from = vmax301_copy_from,
++ .write8 = vmax301_write8,
++ .write16 = vmax301_write16,
++ .write32 = vmax301_write32,
++ .copy_to = vmax301_copy_to,
++ .map_priv_1 = WINDOW_START + WINDOW_LENGTH,
++ .map_priv_2 = 0xFFFFFFFF
+ },
+ {
+- name: "VMAX301 Socket",
+- size: 0,
+- buswidth: 1,
+- read8: vmax301_read8,
+- read16: vmax301_read16,
+- read32: vmax301_read32,
+- copy_from: vmax301_copy_from,
+- write8: vmax301_write8,
+- write16: vmax301_write16,
+- write32: vmax301_write32,
+- copy_to: vmax301_copy_to,
+- map_priv_1: WINDOW_START + (3*WINDOW_LENGTH),
+- map_priv_2: 0xFFFFFFFF
++ .name = "VMAX301 Socket",
++ .phys = NO_XIP,
++ .size = 0,
++ .buswidth = 1,
++ .read8 = vmax301_read8,
++ .read16 = vmax301_read16,
++ .read32 = vmax301_read32,
++ .copy_from = vmax301_copy_from,
++ .write8 = vmax301_write8,
++ .write16 = vmax301_write16,
++ .write32 = vmax301_write32,
++ .copy_to = vmax301_copy_to,
++ .map_priv_1 = WINDOW_START + (3*WINDOW_LENGTH),
++ .map_priv_2 = 0xFFFFFFFF
+ }
+ };
+
+@@ -206,8 +209,8 @@
+ address of the first half, because it's used more
+ often.
+ */
+- vmax_map[0].map_priv_1 = iomapadr + WINDOW_START;
+- vmax_map[1].map_priv_1 = iomapadr + (3*WINDOW_START);
++ vmax_map[0].map_priv_2 = iomapadr + WINDOW_START;
++ vmax_map[1].map_priv_2 = iomapadr + (3*WINDOW_START);
+
+ for (i=0; i<2; i++) {
+ vmax_mtd[i] = do_map_probe("cfi_probe", &vmax_map[i]);
+@@ -218,7 +221,7 @@
+ if (!vmax_mtd[i])
+ vmax_mtd[i] = do_map_probe("map_rom", &vmax_map[i]);
+ if (vmax_mtd[i]) {
+- vmax_mtd[i]->module = THIS_MODULE;
++ vmax_mtd[i]->owner = THIS_MODULE;
+ add_mtd_device(vmax_mtd[i]);
+ }
+ }
+diff -Nurb linux-mips-2.4.27/drivers/mtd/mtd_blkdevs-24.c linux/drivers/mtd/mtd_blkdevs-24.c
+--- linux-mips-2.4.27/drivers/mtd/mtd_blkdevs-24.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/mtd/mtd_blkdevs-24.c 2004-11-19 10:25:11.640239160 +0100
+@@ -0,0 +1,699 @@
++/*
++ * $Id$
++ *
++ * (C) 2003 David Woodhouse <dwmw2@infradead.org>
++ *
++ * Interface to Linux 2.4 block layer for MTD 'translation layers'.
++ *
++ */
++
++#include <linux/kernel.h>
++#include <linux/slab.h>
++#include <linux/module.h>
++#include <linux/list.h>
++#include <linux/fs.h>
++#include <linux/mtd/blktrans.h>
++#include <linux/mtd/mtd.h>
++#include <linux/blkdev.h>
++#include <linux/blk.h>
++#include <linux/blkpg.h>
++#include <linux/spinlock.h>
++#include <linux/hdreg.h>
++#include <linux/init.h>
++#include <asm/semaphore.h>
++#include <asm/uaccess.h>
++
++static LIST_HEAD(blktrans_majors);
++
++extern struct semaphore mtd_table_mutex;
++extern struct mtd_info *mtd_table[];
++
++struct mtd_blkcore_priv {
++ devfs_handle_t devfs_dir_handle;
++ int blksizes[256];
++ int sizes[256];
++ struct hd_struct part_table[256];
++ struct gendisk gd;
++ spinlock_t devs_lock; /* See comment in _request function */
++ struct completion thread_dead;
++ int exiting;
++ wait_queue_head_t thread_wq;
++};
++
++static inline struct mtd_blktrans_dev *tr_get_dev(struct mtd_blktrans_ops *tr,
++ int devnum)
++{
++ struct list_head *this;
++ struct mtd_blktrans_dev *d;
++
++ list_for_each(this, &tr->devs) {
++ d = list_entry(this, struct mtd_blktrans_dev, list);
++
++ if (d->devnum == devnum)
++ return d;
++ }
++ return NULL;
++}
++
++static inline struct mtd_blktrans_ops *get_tr(int major)
++{
++ struct list_head *this;
++ struct mtd_blktrans_ops *t;
++
++ list_for_each(this, &blktrans_majors) {
++ t = list_entry(this, struct mtd_blktrans_ops, list);
++
++ if (t->major == major)
++ return t;
++ }
++ return NULL;
++}
++
++static int do_blktrans_request(struct mtd_blktrans_ops *tr,
++ struct mtd_blktrans_dev *dev,
++ struct request *req)
++{
++ unsigned long block, nsect;
++ char *buf;
++ int minor;
++
++ minor = MINOR(req->rq_dev);
++ block = req->sector;
++ nsect = req->current_nr_sectors;
++ buf = req->buffer;
++
++ if (block + nsect > tr->blkcore_priv->part_table[minor].nr_sects) {
++ printk(KERN_WARNING "Access beyond end of device.\n");
++ return 0;
++ }
++ block += tr->blkcore_priv->part_table[minor].start_sect;
++
++ switch(req->cmd) {
++ case READ:
++ for (; nsect > 0; nsect--, block++, buf += 512)
++ if (tr->readsect(dev, block, buf))
++ return 0;
++ return 1;
++
++ case WRITE:
++ if (!tr->writesect)
++ return 0;
++
++ for (; nsect > 0; nsect--, block++, buf += 512)
++ if (tr->writesect(dev, block, buf))
++ return 0;
++ return 1;
++
++ default:
++ printk(KERN_NOTICE "Unknown request cmd %d\n", req->cmd);
++ return 0;
++ }
++}
++
++static int mtd_blktrans_thread(void *arg)
++{
++ struct mtd_blktrans_ops *tr = arg;
++ struct request_queue *rq = BLK_DEFAULT_QUEUE(tr->major);
++
++ /* we might get involved when memory gets low, so use PF_MEMALLOC */
++ current->flags |= PF_MEMALLOC;
++
++ snprintf(current->comm, sizeof(current->comm), "%sd", tr->name);
++
++ /* daemonize() doesn't do this for us since some kernel threads
++ actually want to deal with signals. We can't just call
++ exit_sighand() since that'll cause an oops when we finally
++ do exit. */
++
++#ifndef __rh_config_h__ /* HAVE_NPTL */
++ spin_lock_irq(&current->sigmask_lock);
++ sigfillset(&current->blocked);
++ recalc_sigpending(current);
++ spin_unlock_irq(&current->sigmask_lock);
++#else
++ spin_lock_irq(&current->sighand->siglock);
++ sigfillset(&current->blocked);
++ recalc_sigpending();
++ spin_unlock_irq(&current->sighand->siglock);
++#endif
++ daemonize();
++
++ while (!tr->blkcore_priv->exiting) {
++ struct request *req;
++ struct mtd_blktrans_dev *dev;
++ int devnum;
++ int res = 0;
++ DECLARE_WAITQUEUE(wait, current);
++
++ spin_lock_irq(&io_request_lock);
++
++ if (list_empty(&rq->queue_head)) {
++
++ add_wait_queue(&tr->blkcore_priv->thread_wq, &wait);
++ set_current_state(TASK_INTERRUPTIBLE);
++
++ spin_unlock_irq(&io_request_lock);
++
++ schedule();
++ remove_wait_queue(&tr->blkcore_priv->thread_wq, &wait);
++
++ continue;
++ }
++
++ req = blkdev_entry_next_request(&rq->queue_head);
++
++ devnum = MINOR(req->rq_dev) >> tr->part_bits;
++
++ /* The ll_rw_blk code knows not to touch the request
++ at the head of the queue */
++ spin_unlock_irq(&io_request_lock);
++
++ /* FIXME: Where can we store the dev, on which
++ we already have a refcount anyway? We need to
++ lock against concurrent addition/removal of devices,
++ but if we use the mtd_table_mutex we deadlock when
++ grok_partitions is called from the registration
++ callbacks. */
++ spin_lock(&tr->blkcore_priv->devs_lock);
++ dev = tr_get_dev(tr, devnum);
++ spin_unlock(&tr->blkcore_priv->devs_lock);
++
++ BUG_ON(!dev);
++
++ /* Ensure serialisation of requests */
++ down(&dev->sem);
++
++ res = do_blktrans_request(tr, dev, req);
++ up(&dev->sem);
++
++ if (!end_that_request_first(req, res, tr->name)) {
++ spin_lock_irq(&io_request_lock);
++ blkdev_dequeue_request(req);
++ end_that_request_last(req);
++ spin_unlock_irq(&io_request_lock);
++ }
++ }
++ complete_and_exit(&tr->blkcore_priv->thread_dead, 0);
++}
++
++static void mtd_blktrans_request(struct request_queue *rq)
++{
++ struct mtd_blktrans_ops *tr = rq->queuedata;
++ wake_up(&tr->blkcore_priv->thread_wq);
++}
++
++int blktrans_open(struct inode *i, struct file *f)
++{
++ struct mtd_blktrans_ops *tr = NULL;
++ struct mtd_blktrans_dev *dev = NULL;
++ int major_nr = MAJOR(i->i_rdev);
++ int minor_nr = MINOR(i->i_rdev);
++ int devnum;
++ int ret = -ENODEV;
++
++ if (is_read_only(i->i_rdev) && (f->f_mode & FMODE_WRITE))
++ return -EROFS;
++
++ down(&mtd_table_mutex);
++
++ tr = get_tr(major_nr);
++
++ if (!tr)
++ goto out;
++
++ devnum = minor_nr >> tr->part_bits;
++
++ dev = tr_get_dev(tr, devnum);
++
++ if (!dev)
++ goto out;
++
++ if (!tr->blkcore_priv->part_table[minor_nr].nr_sects) {
++ ret = -ENODEV;
++ goto out;
++ }
++
++ if (!try_inc_mod_count(dev->mtd->owner))
++ goto out;
++
++ if (!try_inc_mod_count(tr->owner))
++ goto out_tr;
++
++ dev->mtd->usecount++;
++
++ ret = 0;
++ if (tr->open && (ret = tr->open(dev))) {
++ dev->mtd->usecount--;
++ if (dev->mtd->owner)
++ __MOD_DEC_USE_COUNT(dev->mtd->owner);
++ out_tr:
++ if (tr->owner)
++ __MOD_DEC_USE_COUNT(tr->owner);
++ }
++ out:
++ up(&mtd_table_mutex);
++
++ return ret;
++}
++
++int blktrans_release(struct inode *i, struct file *f)
++{
++ struct mtd_blktrans_dev *dev;
++ struct mtd_blktrans_ops *tr;
++ int ret = 0;
++ int devnum;
++
++ down(&mtd_table_mutex);
++
++ tr = get_tr(MAJOR(i->i_rdev));
++ if (!tr) {
++ up(&mtd_table_mutex);
++ return -ENODEV;
++ }
++
++ devnum = MINOR(i->i_rdev) >> tr->part_bits;
++ dev = tr_get_dev(tr, devnum);
++
++ if (!dev) {
++ up(&mtd_table_mutex);
++ return -ENODEV;
++ }
++
++ if (tr->release)
++ ret = tr->release(dev);
++
++ if (!ret) {
++ dev->mtd->usecount--;
++ if (dev->mtd->owner)
++ __MOD_DEC_USE_COUNT(dev->mtd->owner);
++ if (tr->owner)
++ __MOD_DEC_USE_COUNT(tr->owner);
++ }
++
++ up(&mtd_table_mutex);
++
++ return ret;
++}
++
++static int mtd_blktrans_rrpart(kdev_t rdev, struct mtd_blktrans_ops *tr,
++ struct mtd_blktrans_dev *dev)
++{
++ struct gendisk *gd = &(tr->blkcore_priv->gd);
++ int i;
++ int minor = MINOR(rdev);
++
++ if (minor & ((1<<tr->part_bits)-1) || !tr->part_bits) {
++ /* BLKRRPART on a partition. Go away. */
++ return -ENOTTY;
++ }
++
++ if (!capable(CAP_SYS_ADMIN))
++ return -EACCES;
++
++ /* We are required to prevent simultaneous open() ourselves.
++ The core doesn't do that for us. Did I ever mention how
++ much the Linux block layer sucks? Sledgehammer approach... */
++ down(&mtd_table_mutex);
++
++ for (i=0; i < (1<<tr->part_bits); i++) {
++ invalidate_device(MKDEV(tr->major, minor+i), 1);
++ gd->part[minor + i].start_sect = 0;
++ gd->part[minor + i].nr_sects = 0;
++ }
++
++ grok_partitions(gd, minor, 1 << tr->part_bits,
++ tr->blkcore_priv->sizes[minor]);
++ up(&mtd_table_mutex);
++
++ return 0;
++}
++
++static int blktrans_ioctl(struct inode *inode, struct file *file,
++ unsigned int cmd, unsigned long arg)
++{
++ struct mtd_blktrans_dev *dev;
++ struct mtd_blktrans_ops *tr;
++ int devnum;
++
++ switch(cmd) {
++ case BLKGETSIZE:
++ case BLKGETSIZE64:
++ case BLKBSZSET:
++ case BLKBSZGET:
++ case BLKROSET:
++ case BLKROGET:
++ case BLKRASET:
++ case BLKRAGET:
++ case BLKPG:
++ case BLKELVGET:
++ case BLKELVSET:
++ return blk_ioctl(inode->i_rdev, cmd, arg);
++ }
++
++ down(&mtd_table_mutex);
++
++ tr = get_tr(MAJOR(inode->i_rdev));
++ if (!tr) {
++ up(&mtd_table_mutex);
++ return -ENODEV;
++ }
++
++ devnum = MINOR(inode->i_rdev) >> tr->part_bits;
++ dev = tr_get_dev(tr, devnum);
++
++ up(&mtd_table_mutex);
++
++ if (!dev)
++ return -ENODEV;
++
++ switch(cmd) {
++ case BLKRRPART:
++ return mtd_blktrans_rrpart(inode->i_rdev, tr, dev);
++
++ case BLKFLSBUF:
++ blk_ioctl(inode->i_rdev, cmd, arg);
++ if (tr->flush)
++ return tr->flush(dev);
++ /* The core code did the work, we had nothing to do. */
++ return 0;
++
++ case HDIO_GETGEO:
++ if (tr->getgeo) {
++ struct hd_geometry g;
++ struct gendisk *gd = &(tr->blkcore_priv->gd);
++ int ret;
++
++ memset(&g, 0, sizeof(g));
++ ret = tr->getgeo(dev, &g);
++ if (ret)
++ return ret;
++
++ g.start = gd->part[MINOR(inode->i_rdev)].start_sect;
++ if (copy_to_user((void *)arg, &g, sizeof(g)))
++ return -EFAULT;
++ return 0;
++ } /* else */
++ default:
++ return -ENOTTY;
++ }
++}
++
++struct block_device_operations mtd_blktrans_ops = {
++ .owner = THIS_MODULE,
++ .open = blktrans_open,
++ .release = blktrans_release,
++ .ioctl = blktrans_ioctl,
++};
++
++int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new)
++{
++ struct mtd_blktrans_ops *tr = new->tr;
++ struct list_head *this;
++ int last_devnum = -1;
++ int i;
++
++ if (!down_trylock(&mtd_table_mutex)) {
++ up(&mtd_table_mutex);
++ BUG();
++ }
++
++ spin_lock(&tr->blkcore_priv->devs_lock);
++
++ list_for_each(this, &tr->devs) {
++ struct mtd_blktrans_dev *d = list_entry(this, struct mtd_blktrans_dev, list);
++ if (new->devnum == -1) {
++ /* Use first free number */
++ if (d->devnum != last_devnum+1) {
++ /* Found a free devnum. Plug it in here */
++ new->devnum = last_devnum+1;
++ list_add_tail(&new->list, &d->list);
++ goto added;
++ }
++ } else if (d->devnum == new->devnum) {
++ /* Required number taken */
++ spin_unlock(&tr->blkcore_priv->devs_lock);
++ return -EBUSY;
++ } else if (d->devnum > new->devnum) {
++ /* Required number was free */
++ list_add_tail(&new->list, &d->list);
++ goto added;
++ }
++ last_devnum = d->devnum;
++ }
++ if (new->devnum == -1)
++ new->devnum = last_devnum+1;
++
++ if ((new->devnum << tr->part_bits) > 256) {
++ spin_unlock(&tr->blkcore_priv->devs_lock);
++ return -EBUSY;
++ }
++
++ init_MUTEX(&new->sem);
++ list_add_tail(&new->list, &tr->devs);
++ added:
++ spin_unlock(&tr->blkcore_priv->devs_lock);
++
++ if (!tr->writesect)
++ new->readonly = 1;
++
++ for (i = new->devnum << tr->part_bits;
++ i < (new->devnum+1) << tr->part_bits;
++ i++) {
++ set_device_ro(MKDEV(tr->major, i), new->readonly);
++ tr->blkcore_priv->blksizes[i] = new->blksize;
++ tr->blkcore_priv->sizes[i] = 0;
++ tr->blkcore_priv->part_table[i].nr_sects = 0;
++ tr->blkcore_priv->part_table[i].start_sect = 0;
++ }
++
++ /*
++ <viro_zzz> dwmw2: BLOCK_SIZE_BITS has nothing to do with block devices
++ <viro> dwmw2: any code which sets blk_size[][] should be
++ size >> 10 /+ 2.4 and its dumb units */
++
++ tr->blkcore_priv->sizes[new->devnum << tr->part_bits] =
++ (new->size * new->blksize) >> 10; /* 2.4 and its dumb units */
++
++ /* But this is still in device's sectors? $DEITY knows */
++ tr->blkcore_priv->part_table[new->devnum << tr->part_bits].nr_sects = new->size;
++
++ if (tr->part_bits) {
++ grok_partitions(&tr->blkcore_priv->gd, new->devnum,
++ 1 << tr->part_bits, new->size);
++ }
++#ifdef CONFIG_DEVFS_FS
++ if (!tr->part_bits) {
++ char name[2];
++
++ name[0] = '0' + new->devnum;
++ name[1] = 0;
++
++ new->blkcore_priv =
++ devfs_register(tr->blkcore_priv->devfs_dir_handle,
++ name, DEVFS_FL_DEFAULT, tr->major,
++ new->devnum, S_IFBLK|S_IRUGO|S_IWUGO,
++ &mtd_blktrans_ops, NULL);
++ }
++#endif
++ return 0;
++}
++
++int del_mtd_blktrans_dev(struct mtd_blktrans_dev *old)
++{
++ struct mtd_blktrans_ops *tr = old->tr;
++ int i;
++
++ if (!down_trylock(&mtd_table_mutex)) {
++ up(&mtd_table_mutex);
++ BUG();
++ }
++
++#ifdef CONFIG_DEVFS_FS
++ if (!tr->part_bits) {
++ devfs_unregister(old->blkcore_priv);
++ old->blkcore_priv = NULL;
++ } else {
++ devfs_register_partitions(&tr->blkcore_priv->gd,
++ old->devnum << tr->part_bits, 1);
++ }
++#endif
++ spin_lock(&tr->blkcore_priv->devs_lock);
++ list_del(&old->list);
++ spin_unlock(&tr->blkcore_priv->devs_lock);
++
++ for (i = (old->devnum << tr->part_bits);
++ i < ((old->devnum+1) << tr->part_bits); i++) {
++ tr->blkcore_priv->sizes[i] = 0;
++ tr->blkcore_priv->part_table[i].nr_sects = 0;
++ tr->blkcore_priv->part_table[i].start_sect = 0;
++ }
++
++ return 0;
++}
++
++void blktrans_notify_remove(struct mtd_info *mtd)
++{
++ struct list_head *this, *this2, *next;
++
++ list_for_each(this, &blktrans_majors) {
++ struct mtd_blktrans_ops *tr = list_entry(this, struct mtd_blktrans_ops, list);
++
++ list_for_each_safe(this2, next, &tr->devs) {
++ struct mtd_blktrans_dev *dev = list_entry(this2, struct mtd_blktrans_dev, list);
++
++ if (dev->mtd == mtd)
++ tr->remove_dev(dev);
++ }
++ }
++}
++
++void blktrans_notify_add(struct mtd_info *mtd)
++{
++ struct list_head *this;
++
++ if (mtd->type == MTD_ABSENT)
++ return;
++
++ list_for_each(this, &blktrans_majors) {
++ struct mtd_blktrans_ops *tr = list_entry(this, struct mtd_blktrans_ops, list);
++
++ tr->add_mtd(tr, mtd);
++ }
++
++}
++
++static struct mtd_notifier blktrans_notifier = {
++ .add = blktrans_notify_add,
++ .remove = blktrans_notify_remove,
++};
++
++int register_mtd_blktrans(struct mtd_blktrans_ops *tr)
++{
++ int ret, i;
++
++ /* Register the notifier if/when the first device type is
++ registered, to prevent the link/init ordering from fucking
++ us over. */
++ if (!blktrans_notifier.list.next)
++ register_mtd_user(&blktrans_notifier);
++
++ tr->blkcore_priv = kmalloc(sizeof(*tr->blkcore_priv), GFP_KERNEL);
++ if (!tr->blkcore_priv)
++ return -ENOMEM;
++
++ memset(tr->blkcore_priv, 0, sizeof(*tr->blkcore_priv));
++
++ down(&mtd_table_mutex);
++
++ ret = devfs_register_blkdev(tr->major, tr->name, &mtd_blktrans_ops);
++ if (ret) {
++ printk(KERN_WARNING "Unable to register %s block device on major %d: %d\n",
++ tr->name, tr->major, ret);
++ kfree(tr->blkcore_priv);
++ up(&mtd_table_mutex);
++ return ret;
++ }
++
++ blk_init_queue(BLK_DEFAULT_QUEUE(tr->major), &mtd_blktrans_request);
++ (BLK_DEFAULT_QUEUE(tr->major))->queuedata = tr;
++
++ init_completion(&tr->blkcore_priv->thread_dead);
++ init_waitqueue_head(&tr->blkcore_priv->thread_wq);
++
++ ret = kernel_thread(mtd_blktrans_thread, tr,
++ CLONE_FS|CLONE_FILES|CLONE_SIGHAND);
++ if (ret < 0) {
++ blk_cleanup_queue(BLK_DEFAULT_QUEUE(tr->major));
++ devfs_unregister_blkdev(tr->major, tr->name);
++ kfree(tr->blkcore_priv);
++ up(&mtd_table_mutex);
++ return ret;
++ }
++
++ tr->blkcore_priv->devfs_dir_handle =
++ devfs_mk_dir(NULL, tr->name, NULL);
++
++ blksize_size[tr->major] = tr->blkcore_priv->blksizes;
++ blk_size[tr->major] = tr->blkcore_priv->sizes;
++
++ tr->blkcore_priv->gd.major = tr->major;
++ tr->blkcore_priv->gd.major_name = tr->name;
++ tr->blkcore_priv->gd.minor_shift = tr->part_bits;
++ tr->blkcore_priv->gd.max_p = (1<<tr->part_bits) - 1;
++ tr->blkcore_priv->gd.part = tr->blkcore_priv->part_table;
++ tr->blkcore_priv->gd.sizes = tr->blkcore_priv->sizes;
++ tr->blkcore_priv->gd.nr_real = 256 >> tr->part_bits;
++
++ spin_lock_init(&tr->blkcore_priv->devs_lock);
++
++ add_gendisk(&tr->blkcore_priv->gd);
++
++ INIT_LIST_HEAD(&tr->devs);
++ list_add(&tr->list, &blktrans_majors);
++
++ for (i=0; i<MAX_MTD_DEVICES; i++) {
++ if (mtd_table[i] && mtd_table[i]->type != MTD_ABSENT)
++ tr->add_mtd(tr, mtd_table[i]);
++ }
++ up(&mtd_table_mutex);
++
++ return 0;
++}
++
++int deregister_mtd_blktrans(struct mtd_blktrans_ops *tr)
++{
++ struct list_head *this, *next;
++
++ down(&mtd_table_mutex);
++
++ /* Clean up the kernel thread */
++ tr->blkcore_priv->exiting = 1;
++ wake_up(&tr->blkcore_priv->thread_wq);
++ wait_for_completion(&tr->blkcore_priv->thread_dead);
++
++ /* Remove it from the list of active majors */
++ list_del(&tr->list);
++
++ /* Remove each of its devices */
++ list_for_each_safe(this, next, &tr->devs) {
++ struct mtd_blktrans_dev *dev = list_entry(this, struct mtd_blktrans_dev, list);
++ tr->remove_dev(dev);
++ }
++
++ blksize_size[tr->major] = NULL;
++ blk_size[tr->major] = NULL;
++
++ del_gendisk(&tr->blkcore_priv->gd);
++
++ blk_cleanup_queue(BLK_DEFAULT_QUEUE(tr->major));
++ devfs_unregister_blkdev(tr->major, tr->name);
++
++ devfs_unregister(tr->blkcore_priv->devfs_dir_handle);
++
++ up(&mtd_table_mutex);
++
++ kfree(tr->blkcore_priv);
++
++ if (!list_empty(&tr->devs))
++ BUG();
++ return 0;
++}
++
++static void __exit mtd_blktrans_exit(void)
++{
++ /* No race here -- if someone's currently in register_mtd_blktrans
++ we're screwed anyway. */
++ if (blktrans_notifier.list.next)
++ unregister_mtd_user(&blktrans_notifier);
++}
++
++module_exit(mtd_blktrans_exit);
++
++EXPORT_SYMBOL_GPL(register_mtd_blktrans);
++EXPORT_SYMBOL_GPL(deregister_mtd_blktrans);
++EXPORT_SYMBOL_GPL(add_mtd_blktrans_dev);
++EXPORT_SYMBOL_GPL(del_mtd_blktrans_dev);
++
++MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
++MODULE_LICENSE("GPL");
++MODULE_DESCRIPTION("Common interface to block layer for MTD 'translation layers'");
+diff -Nurb linux-mips-2.4.27/drivers/mtd/mtd_blkdevs.c linux/drivers/mtd/mtd_blkdevs.c
+--- linux-mips-2.4.27/drivers/mtd/mtd_blkdevs.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/mtd/mtd_blkdevs.c 2004-11-19 10:25:11.642238856 +0100
+@@ -0,0 +1,479 @@
++/*
++ * $Id$
++ *
++ * (C) 2003 David Woodhouse <dwmw2@infradead.org>
++ *
++ * Interface to Linux 2.5 block layer for MTD 'translation layers'.
++ *
++ */
++
++#include <linux/kernel.h>
++#include <linux/slab.h>
++#include <linux/module.h>
++#include <linux/list.h>
++#include <linux/fs.h>
++#include <linux/mtd/blktrans.h>
++#include <linux/mtd/mtd.h>
++#include <linux/blkdev.h>
++#include <linux/blk.h>
++#include <linux/blkpg.h>
++#include <linux/spinlock.h>
++#include <linux/hdreg.h>
++#include <linux/init.h>
++#include <asm/semaphore.h>
++#include <asm/uaccess.h>
++#include <linux/devfs_fs_kernel.h>
++
++static LIST_HEAD(blktrans_majors);
++
++extern struct semaphore mtd_table_mutex;
++extern struct mtd_info *mtd_table[];
++
++struct mtd_blkcore_priv {
++ struct completion thread_dead;
++ int exiting;
++ wait_queue_head_t thread_wq;
++ struct request_queue *rq;
++ spinlock_t queue_lock;
++};
++
++static int do_blktrans_request(struct mtd_blktrans_ops *tr,
++ struct mtd_blktrans_dev *dev,
++ struct request *req)
++{
++ unsigned long block, nsect;
++ char *buf;
++
++ block = req->sector;
++ nsect = req->current_nr_sectors;
++ buf = req->buffer;
++
++ if (!(req->flags & REQ_CMD))
++ return 0;
++
++ if (block + nsect > get_capacity(req->rq_disk))
++ return 0;
++
++ switch(rq_data_dir(req)) {
++ case READ:
++ for (; nsect > 0; nsect--, block++, buf += 512)
++ if (tr->readsect(dev, block, buf))
++ return 0;
++ return 1;
++
++ case WRITE:
++ if (!tr->writesect)
++ return 0;
++
++ for (; nsect > 0; nsect--, block++, buf += 512)
++ if (tr->writesect(dev, block, buf))
++ return 0;
++ return 1;
++
++ default:
++ printk(KERN_NOTICE "Unknown request %ld\n", rq_data_dir(req));
++ return 0;
++ }
++}
++
++static int mtd_blktrans_thread(void *arg)
++{
++ struct mtd_blktrans_ops *tr = arg;
++ struct request_queue *rq = tr->blkcore_priv->rq;
++
++ /* we might get involved when memory gets low, so use PF_MEMALLOC */
++ current->flags |= PF_MEMALLOC;
++
++ daemonize("%sd", tr->name);
++
++ /* daemonize() doesn't do this for us since some kernel threads
++ actually want to deal with signals. We can't just call
++ exit_sighand() since that'll cause an oops when we finally
++ do exit. */
++ spin_lock_irq(&current->sighand->siglock);
++ sigfillset(&current->blocked);
++ recalc_sigpending();
++ spin_unlock_irq(&current->sighand->siglock);
++
++ spin_lock_irq(rq->queue_lock);
++
++ while (!tr->blkcore_priv->exiting) {
++ struct request *req;
++ struct mtd_blktrans_dev *dev;
++ int res = 0;
++ DECLARE_WAITQUEUE(wait, current);
++
++ req = elv_next_request(rq);
++
++ if (!req) {
++ add_wait_queue(&tr->blkcore_priv->thread_wq, &wait);
++ set_current_state(TASK_INTERRUPTIBLE);
++
++ spin_unlock_irq(rq->queue_lock);
++
++ schedule();
++ remove_wait_queue(&tr->blkcore_priv->thread_wq, &wait);
++
++ spin_lock_irq(rq->queue_lock);
++
++ continue;
++ }
++
++ dev = req->rq_disk->private_data;
++ tr = dev->tr;
++
++ spin_unlock_irq(rq->queue_lock);
++
++ down(&dev->sem);
++ res = do_blktrans_request(tr, dev, req);
++ up(&dev->sem);
++
++ spin_lock_irq(rq->queue_lock);
++
++ end_request(req, res);
++ }
++ complete_and_exit(&tr->blkcore_priv->thread_dead, 0);
++}
++
++static void mtd_blktrans_request(struct request_queue *rq)
++{
++ struct mtd_blktrans_ops *tr = rq->queuedata;
++ wake_up(&tr->blkcore_priv->thread_wq);
++}
++
++
++int blktrans_open(struct inode *i, struct file *f)
++{
++ struct mtd_blktrans_dev *dev;
++ struct mtd_blktrans_ops *tr;
++ int ret = -ENODEV;
++
++ dev = i->i_bdev->bd_disk->private_data;
++ tr = dev->tr;
++
++ if (!try_module_get(dev->mtd->owner))
++ goto out;
++
++ if (!try_module_get(tr->owner))
++ goto out_tr;
++
++ /* FIXME: Locking. A hot pluggable device can go away
++ (del_mtd_device can be called for it) without its module
++ being unloaded. */
++ dev->mtd->usecount++;
++
++ ret = 0;
++ if (tr->open && (ret = tr->open(dev))) {
++ dev->mtd->usecount--;
++ module_put(dev->mtd->owner);
++ out_tr:
++ module_put(tr->owner);
++ }
++ out:
++ return ret;
++}
++
++int blktrans_release(struct inode *i, struct file *f)
++{
++ struct mtd_blktrans_dev *dev;
++ struct mtd_blktrans_ops *tr;
++ int ret = 0;
++
++ dev = i->i_bdev->bd_disk->private_data;
++ tr = dev->tr;
++
++ if (tr->release)
++ ret = tr->release(dev);
++
++ if (!ret) {
++ dev->mtd->usecount--;
++ module_put(dev->mtd->owner);
++ module_put(tr->owner);
++ }
++
++ return ret;
++}
++
++
++static int blktrans_ioctl(struct inode *inode, struct file *file,
++ unsigned int cmd, unsigned long arg)
++{
++ struct mtd_blktrans_dev *dev = inode->i_bdev->bd_disk->private_data;
++ struct mtd_blktrans_ops *tr = dev->tr;
++
++ switch (cmd) {
++ case BLKFLSBUF:
++ if (tr->flush)
++ return tr->flush(dev);
++ /* The core code did the work, we had nothing to do. */
++ return 0;
++
++ case HDIO_GETGEO:
++ if (tr->getgeo) {
++ struct hd_geometry g;
++ int ret;
++
++ memset(&g, 0, sizeof(g));
++ ret = tr->getgeo(dev, &g);
++
++ if (ret)
++ return ret;
++
++ g.start = get_start_sect(inode->i_bdev);
++ if (copy_to_user((void *)arg, &g, sizeof(g)))
++ return -EFAULT;
++ return 0;
++ } /* else */
++ default:
++ return -ENOTTY;
++ }
++}
++
++struct block_device_operations mtd_blktrans_ops = {
++ .owner = THIS_MODULE,
++ .open = blktrans_open,
++ .release = blktrans_release,
++ .ioctl = blktrans_ioctl,
++};
++
++int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new)
++{
++ struct mtd_blktrans_ops *tr = new->tr;
++ struct list_head *this;
++ int last_devnum = -1;
++ struct gendisk *gd;
++
++ if (!down_trylock(&mtd_table_mutex)) {
++ up(&mtd_table_mutex);
++ BUG();
++ }
++
++ list_for_each(this, &tr->devs) {
++ struct mtd_blktrans_dev *d = list_entry(this, struct mtd_blktrans_dev, list);
++ if (new->devnum == -1) {
++ /* Use first free number */
++ if (d->devnum != last_devnum+1) {
++ /* Found a free devnum. Plug it in here */
++ new->devnum = last_devnum+1;
++ list_add_tail(&new->list, &d->list);
++ goto added;
++ }
++ } else if (d->devnum == new->devnum) {
++ /* Required number taken */
++ return -EBUSY;
++ } else if (d->devnum > new->devnum) {
++ /* Required number was free */
++ list_add_tail(&new->list, &d->list);
++ goto added;
++ }
++ last_devnum = d->devnum;
++ }
++ if (new->devnum == -1)
++ new->devnum = last_devnum+1;
++
++ if ((new->devnum << tr->part_bits) > 256) {
++ return -EBUSY;
++ }
++
++ init_MUTEX(&new->sem);
++ list_add_tail(&new->list, &tr->devs);
++ added:
++ if (!tr->writesect)
++ new->readonly = 1;
++
++ gd = alloc_disk(1 << tr->part_bits);
++ if (!gd) {
++ list_del(&new->list);
++ return -ENOMEM;
++ }
++ gd->major = tr->major;
++ gd->first_minor = (new->devnum) << tr->part_bits;
++ gd->fops = &mtd_blktrans_ops;
++
++ snprintf(gd->disk_name, sizeof(gd->disk_name),
++ "%s%c", tr->name, (tr->part_bits?'a':'0') + new->devnum);
++ snprintf(gd->devfs_name, sizeof(gd->devfs_name),
++ "%s/%c", tr->name, (tr->part_bits?'a':'0') + new->devnum);
++
++ /* 2.5 has capacity in units of 512 bytes while still
++ having BLOCK_SIZE_BITS set to 10. Just to keep us amused. */
++ set_capacity(gd, (new->size * new->blksize) >> 9);
++
++ gd->private_data = new;
++ new->blkcore_priv = gd;
++ gd->queue = tr->blkcore_priv->rq;
++
++ if (new->readonly)
++ set_disk_ro(gd, 1);
++
++ add_disk(gd);
++
++ return 0;
++}
++
++int del_mtd_blktrans_dev(struct mtd_blktrans_dev *old)
++{
++ if (!down_trylock(&mtd_table_mutex)) {
++ up(&mtd_table_mutex);
++ BUG();
++ }
++
++ list_del(&old->list);
++
++ del_gendisk(old->blkcore_priv);
++ put_disk(old->blkcore_priv);
++
++ return 0;
++}
++
++void blktrans_notify_remove(struct mtd_info *mtd)
++{
++ struct list_head *this, *this2, *next;
++
++ list_for_each(this, &blktrans_majors) {
++ struct mtd_blktrans_ops *tr = list_entry(this, struct mtd_blktrans_ops, list);
++
++ list_for_each_safe(this2, next, &tr->devs) {
++ struct mtd_blktrans_dev *dev = list_entry(this2, struct mtd_blktrans_dev, list);
++
++ if (dev->mtd == mtd)
++ tr->remove_dev(dev);
++ }
++ }
++}
++
++void blktrans_notify_add(struct mtd_info *mtd)
++{
++ struct list_head *this;
++
++ if (mtd->type == MTD_ABSENT)
++ return;
++
++ list_for_each(this, &blktrans_majors) {
++ struct mtd_blktrans_ops *tr = list_entry(this, struct mtd_blktrans_ops, list);
++
++ tr->add_mtd(tr, mtd);
++ }
++
++}
++
++static struct mtd_notifier blktrans_notifier = {
++ .add = blktrans_notify_add,
++ .remove = blktrans_notify_remove,
++};
++
++int register_mtd_blktrans(struct mtd_blktrans_ops *tr)
++{
++ int ret, i;
++
++ /* Register the notifier if/when the first device type is
++ registered, to prevent the link/init ordering from fucking
++ us over. */
++ if (!blktrans_notifier.list.next)
++ register_mtd_user(&blktrans_notifier);
++
++ tr->blkcore_priv = kmalloc(sizeof(*tr->blkcore_priv), GFP_KERNEL);
++ if (!tr->blkcore_priv)
++ return -ENOMEM;
++
++ memset(tr->blkcore_priv, 0, sizeof(*tr->blkcore_priv));
++
++ down(&mtd_table_mutex);
++
++ ret = register_blkdev(tr->major, tr->name);
++ if (ret) {
++ printk(KERN_WARNING "Unable to register %s block device on major %d: %d\n",
++ tr->name, tr->major, ret);
++ kfree(tr->blkcore_priv);
++ up(&mtd_table_mutex);
++ return ret;
++ }
++ spin_lock_init(&tr->blkcore_priv->queue_lock);
++ init_completion(&tr->blkcore_priv->thread_dead);
++ init_waitqueue_head(&tr->blkcore_priv->thread_wq);
++
++ tr->blkcore_priv->rq = blk_init_queue(mtd_blktrans_request, &tr->blkcore_priv->queue_lock);
++ if (!tr->blkcore_priv->rq) {
++ unregister_blkdev(tr->major, tr->name);
++ kfree(tr->blkcore_priv);
++ up(&mtd_table_mutex);
++ return -ENOMEM;
++ }
++
++ tr->blkcore_priv->rq->queuedata = tr;
++
++ ret = kernel_thread(mtd_blktrans_thread, tr,
++ CLONE_FS|CLONE_FILES|CLONE_SIGHAND);
++ if (ret < 0) {
++ blk_cleanup_queue(tr->blkcore_priv->rq);
++ unregister_blkdev(tr->major, tr->name);
++ kfree(tr->blkcore_priv);
++ up(&mtd_table_mutex);
++ return ret;
++ }
++
++ devfs_mk_dir(tr->name);
++
++ INIT_LIST_HEAD(&tr->devs);
++ list_add(&tr->list, &blktrans_majors);
++
++ for (i=0; i<MAX_MTD_DEVICES; i++) {
++ if (mtd_table[i] && mtd_table[i]->type != MTD_ABSENT)
++ tr->add_mtd(tr, mtd_table[i]);
++ }
++
++ up(&mtd_table_mutex);
++
++ return 0;
++}
++
++int deregister_mtd_blktrans(struct mtd_blktrans_ops *tr)
++{
++ struct list_head *this, *next;
++
++ down(&mtd_table_mutex);
++
++ /* Clean up the kernel thread */
++ tr->blkcore_priv->exiting = 1;
++ wake_up(&tr->blkcore_priv->thread_wq);
++ wait_for_completion(&tr->blkcore_priv->thread_dead);
++
++ /* Remove it from the list of active majors */
++ list_del(&tr->list);
++
++ list_for_each_safe(this, next, &tr->devs) {
++ struct mtd_blktrans_dev *dev = list_entry(this, struct mtd_blktrans_dev, list);
++ tr->remove_dev(dev);
++ }
++
++ devfs_remove(tr->name);
++ blk_cleanup_queue(tr->blkcore_priv->rq);
++ unregister_blkdev(tr->major, tr->name);
++
++ up(&mtd_table_mutex);
++
++ kfree(tr->blkcore_priv);
++
++ if (!list_empty(&tr->devs))
++ BUG();
++ return 0;
++}
++
++static void __exit mtd_blktrans_exit(void)
++{
++ /* No race here -- if someone's currently in register_mtd_blktrans
++ we're screwed anyway. */
++ if (blktrans_notifier.list.next)
++ unregister_mtd_user(&blktrans_notifier);
++}
++
++module_exit(mtd_blktrans_exit);
++
++EXPORT_SYMBOL_GPL(register_mtd_blktrans);
++EXPORT_SYMBOL_GPL(deregister_mtd_blktrans);
++EXPORT_SYMBOL_GPL(add_mtd_blktrans_dev);
++EXPORT_SYMBOL_GPL(del_mtd_blktrans_dev);
++
++MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
++MODULE_LICENSE("GPL");
++MODULE_DESCRIPTION("Common interface to block layer for MTD 'translation layers'");
+diff -Nurb linux-mips-2.4.27/drivers/mtd/mtdblock.c linux/drivers/mtd/mtdblock.c
+--- linux-mips-2.4.27/drivers/mtd/mtdblock.c 2003-02-26 01:53:49.000000000 +0100
++++ linux/drivers/mtd/mtdblock.c 2004-11-19 10:25:11.643238704 +0100
+@@ -1,52 +1,25 @@
+ /*
+ * Direct MTD block device access
+ *
+- * $Id$
++ * $Id$
+ *
+- * 02-nov-2000 Nicolas Pitre Added read-modify-write with cache
++ * (C) 2000-2003 Nicolas Pitre <nico@cam.org>
++ * (C) 1999-2003 David Woodhouse <dwmw2@infradead.org>
+ */
+
+ #include <linux/config.h>
+ #include <linux/types.h>
+ #include <linux/module.h>
+ #include <linux/kernel.h>
++#include <linux/fs.h>
++#include <linux/init.h>
+ #include <linux/slab.h>
++#include <linux/vmalloc.h>
+ #include <linux/mtd/mtd.h>
+-#include <linux/mtd/compatmac.h>
+-
+-#define MAJOR_NR MTD_BLOCK_MAJOR
+-#define DEVICE_NAME "mtdblock"
+-#define DEVICE_REQUEST mtdblock_request
+-#define DEVICE_NR(device) (device)
+-#define DEVICE_ON(device)
+-#define DEVICE_OFF(device)
+-#define DEVICE_NO_RANDOM
+-#include <linux/blk.h>
+-/* for old kernels... */
+-#ifndef QUEUE_EMPTY
+-#define QUEUE_EMPTY (!CURRENT)
+-#endif
+-#if LINUX_VERSION_CODE < 0x20300
+-#define QUEUE_PLUGGED (blk_dev[MAJOR_NR].plug_tq.sync)
+-#else
+-#define QUEUE_PLUGGED (blk_dev[MAJOR_NR].request_queue.plugged)
+-#endif
+-
+-#ifdef CONFIG_DEVFS_FS
+-#include <linux/devfs_fs_kernel.h>
+-static void mtd_notify_add(struct mtd_info* mtd);
+-static void mtd_notify_remove(struct mtd_info* mtd);
+-static struct mtd_notifier notifier = {
+- mtd_notify_add,
+- mtd_notify_remove,
+- NULL
+-};
+-static devfs_handle_t devfs_dir_handle = NULL;
+-static devfs_handle_t devfs_rw_handle[MAX_MTD_DEVICES];
+-#endif
++#include <linux/mtd/blktrans.h>
+
+ static struct mtdblk_dev {
+- struct mtd_info *mtd; /* Locked */
++ struct mtd_info *mtd;
+ int count;
+ struct semaphore cache_sem;
+ unsigned char *cache_data;
+@@ -55,19 +28,6 @@
+ enum { STATE_EMPTY, STATE_CLEAN, STATE_DIRTY } cache_state;
+ } *mtdblks[MAX_MTD_DEVICES];
+
+-static spinlock_t mtdblks_lock;
+-
+-static int mtd_sizes[MAX_MTD_DEVICES];
+-static int mtd_blksizes[MAX_MTD_DEVICES];
+-
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,14)
+-#define BLK_INC_USE_COUNT MOD_INC_USE_COUNT
+-#define BLK_DEC_USE_COUNT MOD_DEC_USE_COUNT
+-#else
+-#define BLK_INC_USE_COUNT do {} while(0)
+-#define BLK_DEC_USE_COUNT do {} while(0)
+-#endif
+-
+ /*
+ * Cache stuff...
+ *
+@@ -151,7 +111,7 @@
+ return ret;
+
+ /*
+- * Here we could argably set the cache state to STATE_CLEAN.
++ * Here we could argubly set the cache state to STATE_CLEAN.
+ * However this could lead to inconsistency since we will not
+ * be notified if this content is altered on the flash by other
+ * means. Let's declare it empty and leave buffering tasks to
+@@ -277,57 +237,47 @@
+ return 0;
+ }
+
++static int mtdblock_readsect(struct mtd_blktrans_dev *dev,
++ unsigned long block, char *buf)
++{
++ struct mtdblk_dev *mtdblk = mtdblks[dev->devnum];
++ return do_cached_read(mtdblk, block<<9, 512, buf);
++}
+
++static int mtdblock_writesect(struct mtd_blktrans_dev *dev,
++ unsigned long block, char *buf)
++{
++ struct mtdblk_dev *mtdblk = mtdblks[dev->devnum];
++ if (unlikely(!mtdblk->cache_data)) {
++ mtdblk->cache_data = vmalloc(mtdblk->mtd->erasesize);
++ if (!mtdblk->cache_data)
++ return -EINTR;
++ /* -EINTR is not really correct, but it is the best match
++ * documented in man 2 write for all cases. We could also
++ * return -EAGAIN sometimes, but why bother?
++ */
++ }
++ return do_cached_write(mtdblk, block<<9, 512, buf);
++}
+
+-static int mtdblock_open(struct inode *inode, struct file *file)
++static int mtdblock_open(struct mtd_blktrans_dev *mbd)
+ {
+ struct mtdblk_dev *mtdblk;
+- struct mtd_info *mtd;
+- int dev;
++ struct mtd_info *mtd = mbd->mtd;
++ int dev = mbd->devnum;
+
+ DEBUG(MTD_DEBUG_LEVEL1,"mtdblock_open\n");
+
+- if (!inode)
+- return -EINVAL;
+-
+- dev = MINOR(inode->i_rdev);
+- if (dev >= MAX_MTD_DEVICES)
+- return -EINVAL;
+-
+- BLK_INC_USE_COUNT;
+-
+- mtd = get_mtd_device(NULL, dev);
+- if (!mtd)
+- return -ENODEV;
+- if (MTD_ABSENT == mtd->type) {
+- put_mtd_device(mtd);
+- BLK_DEC_USE_COUNT;
+- return -ENODEV;
+- }
+-
+- spin_lock(&mtdblks_lock);
+-
+- /* If it's already open, no need to piss about. */
+ if (mtdblks[dev]) {
+ mtdblks[dev]->count++;
+- spin_unlock(&mtdblks_lock);
+- put_mtd_device(mtd);
+ return 0;
+ }
+
+- /* OK, it's not open. Try to find it */
+-
+- /* First we have to drop the lock, because we have to
+- to things which might sleep.
+- */
+- spin_unlock(&mtdblks_lock);
+-
++ /* OK, it's not open. Create cache info for it */
+ mtdblk = kmalloc(sizeof(struct mtdblk_dev), GFP_KERNEL);
+- if (!mtdblk) {
+- put_mtd_device(mtd);
+- BLK_DEC_USE_COUNT;
++ if (!mtdblk)
+ return -ENOMEM;
+- }
++
+ memset(mtdblk, 0, sizeof(*mtdblk));
+ mtdblk->count = 1;
+ mtdblk->mtd = mtd;
+@@ -337,336 +287,102 @@
+ if ((mtdblk->mtd->flags & MTD_CAP_RAM) != MTD_CAP_RAM &&
+ mtdblk->mtd->erasesize) {
+ mtdblk->cache_size = mtdblk->mtd->erasesize;
+- mtdblk->cache_data = vmalloc(mtdblk->mtd->erasesize);
+- if (!mtdblk->cache_data) {
+- put_mtd_device(mtdblk->mtd);
+- kfree(mtdblk);
+- BLK_DEC_USE_COUNT;
+- return -ENOMEM;
+- }
+- }
+-
+- /* OK, we've created a new one. Add it to the list. */
+-
+- spin_lock(&mtdblks_lock);
+-
+- if (mtdblks[dev]) {
+- /* Another CPU made one at the same time as us. */
+- mtdblks[dev]->count++;
+- spin_unlock(&mtdblks_lock);
+- put_mtd_device(mtdblk->mtd);
+- vfree(mtdblk->cache_data);
+- kfree(mtdblk);
+- return 0;
++ mtdblk->cache_data = NULL;
+ }
+
+ mtdblks[dev] = mtdblk;
+- mtd_sizes[dev] = mtdblk->mtd->size/1024;
+- if (mtdblk->mtd->erasesize)
+- mtd_blksizes[dev] = mtdblk->mtd->erasesize;
+- if (mtd_blksizes[dev] > PAGE_SIZE)
+- mtd_blksizes[dev] = PAGE_SIZE;
+- set_device_ro (inode->i_rdev, !(mtdblk->mtd->flags & MTD_WRITEABLE));
+-
+- spin_unlock(&mtdblks_lock);
+
+ DEBUG(MTD_DEBUG_LEVEL1, "ok\n");
+
+ return 0;
+ }
+
+-static release_t mtdblock_release(struct inode *inode, struct file *file)
++static int mtdblock_release(struct mtd_blktrans_dev *mbd)
+ {
+- int dev;
+- struct mtdblk_dev *mtdblk;
+- DEBUG(MTD_DEBUG_LEVEL1, "mtdblock_release\n");
++ int dev = mbd->devnum;
++ struct mtdblk_dev *mtdblk = mtdblks[dev];
+
+- if (inode == NULL)
+- release_return(-ENODEV);
+-
+- dev = MINOR(inode->i_rdev);
+- mtdblk = mtdblks[dev];
++ DEBUG(MTD_DEBUG_LEVEL1, "mtdblock_release\n");
+
+ down(&mtdblk->cache_sem);
+ write_cached_data(mtdblk);
+ up(&mtdblk->cache_sem);
+
+- spin_lock(&mtdblks_lock);
+ if (!--mtdblk->count) {
+ /* It was the last usage. Free the device */
+ mtdblks[dev] = NULL;
+- spin_unlock(&mtdblks_lock);
+ if (mtdblk->mtd->sync)
+ mtdblk->mtd->sync(mtdblk->mtd);
+- put_mtd_device(mtdblk->mtd);
+ vfree(mtdblk->cache_data);
+ kfree(mtdblk);
+- } else {
+- spin_unlock(&mtdblks_lock);
+ }
+-
+ DEBUG(MTD_DEBUG_LEVEL1, "ok\n");
+
+- BLK_DEC_USE_COUNT;
+- release_return(0);
+-}
+-
+-
+-/*
+- * This is a special request_fn because it is executed in a process context
+- * to be able to sleep independently of the caller. The io_request_lock
+- * is held upon entry and exit.
+- * The head of our request queue is considered active so there is no need
+- * to dequeue requests before we are done.
+- */
+-static void handle_mtdblock_request(void)
+-{
+- struct request *req;
+- struct mtdblk_dev *mtdblk;
+- unsigned int res;
+-
+- for (;;) {
+- INIT_REQUEST;
+- req = CURRENT;
+- spin_unlock_irq(&io_request_lock);
+- mtdblk = mtdblks[MINOR(req->rq_dev)];
+- res = 0;
+-
+- if (MINOR(req->rq_dev) >= MAX_MTD_DEVICES)
+- panic("%s: minor out of bounds", __FUNCTION__);
+-
+- if ((req->sector + req->current_nr_sectors) > (mtdblk->mtd->size >> 9))
+- goto end_req;
+-
+- // Handle the request
+- switch (req->cmd)
+- {
+- int err;
+-
+- case READ:
+- down(&mtdblk->cache_sem);
+- err = do_cached_read (mtdblk, req->sector << 9,
+- req->current_nr_sectors << 9,
+- req->buffer);
+- up(&mtdblk->cache_sem);
+- if (!err)
+- res = 1;
+- break;
+-
+- case WRITE:
+- // Read only device
+- if ( !(mtdblk->mtd->flags & MTD_WRITEABLE) )
+- break;
+-
+- // Do the write
+- down(&mtdblk->cache_sem);
+- err = do_cached_write (mtdblk, req->sector << 9,
+- req->current_nr_sectors << 9,
+- req->buffer);
+- up(&mtdblk->cache_sem);
+- if (!err)
+- res = 1;
+- break;
+- }
+-
+-end_req:
+- spin_lock_irq(&io_request_lock);
+- end_request(res);
+- }
+-}
+-
+-static volatile int leaving = 0;
+-static DECLARE_MUTEX_LOCKED(thread_sem);
+-static DECLARE_WAIT_QUEUE_HEAD(thr_wq);
+-
+-int mtdblock_thread(void *dummy)
+-{
+- struct task_struct *tsk = current;
+- DECLARE_WAITQUEUE(wait, tsk);
+-
+- /* we might get involved when memory gets low, so use PF_MEMALLOC */
+- tsk->flags |= PF_MEMALLOC;
+- strcpy(tsk->comm, "mtdblockd");
+- spin_lock_irq(&tsk->sigmask_lock);
+- sigfillset(&tsk->blocked);
+- recalc_sigpending(tsk);
+- spin_unlock_irq(&tsk->sigmask_lock);
+- daemonize();
+-
+- while (!leaving) {
+- add_wait_queue(&thr_wq, &wait);
+- set_current_state(TASK_INTERRUPTIBLE);
+- spin_lock_irq(&io_request_lock);
+- if (QUEUE_EMPTY || QUEUE_PLUGGED) {
+- spin_unlock_irq(&io_request_lock);
+- schedule();
+- remove_wait_queue(&thr_wq, &wait);
+- } else {
+- remove_wait_queue(&thr_wq, &wait);
+- set_current_state(TASK_RUNNING);
+- handle_mtdblock_request();
+- spin_unlock_irq(&io_request_lock);
+- }
+- }
+-
+- up(&thread_sem);
+ return 0;
+ }
+
+-#if LINUX_VERSION_CODE < 0x20300
+-#define RQFUNC_ARG void
+-#else
+-#define RQFUNC_ARG request_queue_t *q
+-#endif
+-
+-static void mtdblock_request(RQFUNC_ARG)
++static int mtdblock_flush(struct mtd_blktrans_dev *dev)
+ {
+- /* Don't do anything, except wake the thread if necessary */
+- wake_up(&thr_wq);
+-}
++ struct mtdblk_dev *mtdblk = mtdblks[dev->devnum];
+
+-
+-static int mtdblock_ioctl(struct inode * inode, struct file * file,
+- unsigned int cmd, unsigned long arg)
+-{
+- struct mtdblk_dev *mtdblk;
+-
+- mtdblk = mtdblks[MINOR(inode->i_rdev)];
+-
+-#ifdef PARANOIA
+- if (!mtdblk)
+- BUG();
+-#endif
+-
+- switch (cmd) {
+- case BLKGETSIZE: /* Return device size */
+- return put_user((mtdblk->mtd->size >> 9), (unsigned long *) arg);
+-
+-#ifdef BLKGETSIZE64
+- case BLKGETSIZE64:
+- return put_user((u64)mtdblk->mtd->size, (u64 *)arg);
+-#endif
+-
+- case BLKFLSBUF:
+-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
+- if(!capable(CAP_SYS_ADMIN))
+- return -EACCES;
+-#endif
+- fsync_dev(inode->i_rdev);
+- invalidate_buffers(inode->i_rdev);
+ down(&mtdblk->cache_sem);
+ write_cached_data(mtdblk);
+ up(&mtdblk->cache_sem);
++
+ if (mtdblk->mtd->sync)
+ mtdblk->mtd->sync(mtdblk->mtd);
+ return 0;
+-
+- default:
+- return -EINVAL;
+- }
+ }
+
+-#if LINUX_VERSION_CODE < 0x20326
+-static struct file_operations mtd_fops =
++static void mtdblock_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
+ {
+- open: mtdblock_open,
+- ioctl: mtdblock_ioctl,
+- release: mtdblock_release,
+- read: block_read,
+- write: block_write
+-};
+-#else
+-static struct block_device_operations mtd_fops =
+-{
+-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,14)
+- owner: THIS_MODULE,
+-#endif
+- open: mtdblock_open,
+- release: mtdblock_release,
+- ioctl: mtdblock_ioctl
+-};
+-#endif
++ struct mtd_blktrans_dev *dev = kmalloc(sizeof(*dev), GFP_KERNEL);
+
+-#ifdef CONFIG_DEVFS_FS
+-/* Notification that a new device has been added. Create the devfs entry for
+- * it. */
+-
+-static void mtd_notify_add(struct mtd_info* mtd)
+-{
+- char name[8];
+-
+- if (!mtd || mtd->type == MTD_ABSENT)
++ if (!dev)
+ return;
+
+- sprintf(name, "%d", mtd->index);
+- devfs_rw_handle[mtd->index] = devfs_register(devfs_dir_handle, name,
+- DEVFS_FL_DEFAULT, MTD_BLOCK_MAJOR, mtd->index,
+- S_IFBLK | S_IRUGO | S_IWUGO,
+- &mtd_fops, NULL);
+-}
+-
+-static void mtd_notify_remove(struct mtd_info* mtd)
+-{
+- if (!mtd || mtd->type == MTD_ABSENT)
+- return;
++ memset(dev, 0, sizeof(*dev));
+
+- devfs_unregister(devfs_rw_handle[mtd->index]);
+-}
+-#endif
++ dev->mtd = mtd;
++ dev->devnum = mtd->index;
++ dev->blksize = 512;
++ dev->size = mtd->size >> 9;
++ dev->tr = tr;
++
++ if (!(mtd->flags & MTD_WRITEABLE))
++ dev->readonly = 1;
++
++ add_mtd_blktrans_dev(dev);
++}
++
++static void mtdblock_remove_dev(struct mtd_blktrans_dev *dev)
++{
++ del_mtd_blktrans_dev(dev);
++ kfree(dev);
++}
++
++struct mtd_blktrans_ops mtdblock_tr = {
++ .name = "mtdblock",
++ .major = 31,
++ .part_bits = 0,
++ .open = mtdblock_open,
++ .flush = mtdblock_flush,
++ .release = mtdblock_release,
++ .readsect = mtdblock_readsect,
++ .writesect = mtdblock_writesect,
++ .add_mtd = mtdblock_add_mtd,
++ .remove_dev = mtdblock_remove_dev,
++ .owner = THIS_MODULE,
++};
+
+ int __init init_mtdblock(void)
+ {
+- int i;
+-
+- spin_lock_init(&mtdblks_lock);
+-#ifdef CONFIG_DEVFS_FS
+- if (devfs_register_blkdev(MTD_BLOCK_MAJOR, DEVICE_NAME, &mtd_fops))
+- {
+- printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n",
+- MTD_BLOCK_MAJOR);
+- return -EAGAIN;
+- }
+-
+- devfs_dir_handle = devfs_mk_dir(NULL, DEVICE_NAME, NULL);
+- register_mtd_user(&notifier);
+-#else
+- if (register_blkdev(MAJOR_NR,DEVICE_NAME,&mtd_fops)) {
+- printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n",
+- MTD_BLOCK_MAJOR);
+- return -EAGAIN;
+- }
+-#endif
+-
+- /* We fill it in at open() time. */
+- for (i=0; i< MAX_MTD_DEVICES; i++) {
+- mtd_sizes[i] = 0;
+- mtd_blksizes[i] = BLOCK_SIZE;
+- }
+- init_waitqueue_head(&thr_wq);
+- /* Allow the block size to default to BLOCK_SIZE. */
+- blksize_size[MAJOR_NR] = mtd_blksizes;
+- blk_size[MAJOR_NR] = mtd_sizes;
+-
+- blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), &mtdblock_request);
+- kernel_thread (mtdblock_thread, NULL, CLONE_FS|CLONE_FILES|CLONE_SIGHAND);
+- return 0;
++ return register_mtd_blktrans(&mtdblock_tr);
+ }
+
+ static void __exit cleanup_mtdblock(void)
+ {
+- leaving = 1;
+- wake_up(&thr_wq);
+- down(&thread_sem);
+-#ifdef CONFIG_DEVFS_FS
+- unregister_mtd_user(&notifier);
+- devfs_unregister(devfs_dir_handle);
+- devfs_unregister_blkdev(MTD_BLOCK_MAJOR, DEVICE_NAME);
+-#else
+- unregister_blkdev(MAJOR_NR,DEVICE_NAME);
+-#endif
+- blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
+- blksize_size[MAJOR_NR] = NULL;
+- blk_size[MAJOR_NR] = NULL;
++ deregister_mtd_blktrans(&mtdblock_tr);
+ }
+
+ module_init(init_mtdblock);
+diff -Nurb linux-mips-2.4.27/drivers/mtd/mtdblock_ro.c linux/drivers/mtd/mtdblock_ro.c
+--- linux-mips-2.4.27/drivers/mtd/mtdblock_ro.c 2003-02-26 01:53:49.000000000 +0100
++++ linux/drivers/mtd/mtdblock_ro.c 2004-11-19 10:25:11.645238400 +0100
+@@ -1,301 +1,87 @@
+ /*
+- * $Id$
++ * $Id$
+ *
+- * Read-only version of the mtdblock device, without the
+- * read/erase/modify/writeback stuff
++ * (C) 2003 David Woodhouse <dwmw2@infradead.org>
++ *
++ * Simple read-only (writable only for RAM) mtdblock driver
+ */
+
+-#ifdef MTDBLOCK_DEBUG
+-#define DEBUGLVL debug
+-#endif
+-
+-
+-#include <linux/module.h>
+-#include <linux/types.h>
+-
++#include <linux/init.h>
++#include <linux/slab.h>
+ #include <linux/mtd/mtd.h>
+-#include <linux/mtd/compatmac.h>
+-
+-#define MAJOR_NR MTD_BLOCK_MAJOR
+-#define DEVICE_NAME "mtdblock"
+-#define DEVICE_REQUEST mtdblock_request
+-#define DEVICE_NR(device) (device)
+-#define DEVICE_ON(device)
+-#define DEVICE_OFF(device)
+-#define DEVICE_NO_RANDOM
+-#include <linux/blk.h>
+-
+-#if LINUX_VERSION_CODE < 0x20300
+-#define RQFUNC_ARG void
+-#define blkdev_dequeue_request(req) do {CURRENT = req->next;} while (0)
+-#else
+-#define RQFUNC_ARG request_queue_t *q
+-#endif
+-
+-#ifdef MTDBLOCK_DEBUG
+-static int debug = MTDBLOCK_DEBUG;
+-MODULE_PARM(debug, "i");
+-#endif
+-
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,14)
+-#define BLK_INC_USE_COUNT MOD_INC_USE_COUNT
+-#define BLK_DEC_USE_COUNT MOD_DEC_USE_COUNT
+-#else
+-#define BLK_INC_USE_COUNT do {} while(0)
+-#define BLK_DEC_USE_COUNT do {} while(0)
+-#endif
+-
+-static int mtd_sizes[MAX_MTD_DEVICES];
++#include <linux/mtd/blktrans.h>
+
+-
+-static int mtdblock_open(struct inode *inode, struct file *file)
++static int mtdblock_readsect(struct mtd_blktrans_dev *dev,
++ unsigned long block, char *buf)
+ {
+- struct mtd_info *mtd = NULL;
+-
+- int dev;
+-
+- DEBUG(1,"mtdblock_open\n");
+-
+- if (inode == 0)
+- return -EINVAL;
+-
+- dev = MINOR(inode->i_rdev);
+-
+- mtd = get_mtd_device(NULL, dev);
+- if (!mtd)
+- return -EINVAL;
+- if (MTD_ABSENT == mtd->type) {
+- put_mtd_device(mtd);
+- return -EINVAL;
+- }
+-
+- BLK_INC_USE_COUNT;
+-
+- mtd_sizes[dev] = mtd->size>>9;
+-
+- DEBUG(1, "ok\n");
++ size_t retlen;
+
++ if (dev->mtd->read(dev->mtd, (block * 512), 512, &retlen, buf))
++ return 1;
+ return 0;
+ }
+
+-static release_t mtdblock_release(struct inode *inode, struct file *file)
++static int mtdblock_writesect(struct mtd_blktrans_dev *dev,
++ unsigned long block, char *buf)
+ {
+- int dev;
+- struct mtd_info *mtd;
+-
+- DEBUG(1, "mtdblock_release\n");
+-
+- if (inode == NULL)
+- release_return(-ENODEV);
+-
+- dev = MINOR(inode->i_rdev);
+- mtd = __get_mtd_device(NULL, dev);
+-
+- if (!mtd) {
+- printk(KERN_WARNING "MTD device is absent on mtd_release!\n");
+- BLK_DEC_USE_COUNT;
+- release_return(-ENODEV);
+- }
+-
+- if (mtd->sync)
+- mtd->sync(mtd);
+-
+- put_mtd_device(mtd);
+-
+- DEBUG(1, "ok\n");
++ size_t retlen;
+
+- BLK_DEC_USE_COUNT;
+- release_return(0);
++ if (dev->mtd->write(dev->mtd, (block * 512), 512, &retlen, buf))
++ return 1;
++ return 0;
+ }
+
+-
+-static void mtdblock_request(RQFUNC_ARG)
++static void mtdblock_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
+ {
+- struct request *current_request;
+- unsigned int res = 0;
+- struct mtd_info *mtd;
+-
+- while (1)
+- {
+- /* Grab the Request and unlink it from the request list, INIT_REQUEST
+- will execute a return if we are done. */
+- INIT_REQUEST;
+- current_request = CURRENT;
+-
+- if (MINOR(current_request->rq_dev) >= MAX_MTD_DEVICES)
+- {
+- printk("mtd: Unsupported device!\n");
+- end_request(0);
+- continue;
+- }
+-
+- // Grab our MTD structure
+-
+- mtd = __get_mtd_device(NULL, MINOR(current_request->rq_dev));
+- if (!mtd) {
+- printk("MTD device %d doesn't appear to exist any more\n", CURRENT_DEV);
+- end_request(0);
+- }
+-
+- if (current_request->sector << 9 > mtd->size ||
+- (current_request->sector + current_request->current_nr_sectors) << 9 > mtd->size)
+- {
+- printk("mtd: Attempt to read past end of device!\n");
+- printk("size: %x, sector: %lx, nr_sectors %lx\n", mtd->size,
+- current_request->sector, current_request->current_nr_sectors);
+- end_request(0);
+- continue;
+- }
+-
+- /* Remove the request we are handling from the request list so nobody messes
+- with it */
+-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
+- /* Now drop the lock that the ll_rw_blk functions grabbed for us
+- and process the request. This is necessary due to the extreme time
+- we spend processing it. */
+- spin_unlock_irq(&io_request_lock);
+-#endif
+-
+- // Handle the request
+- switch (current_request->cmd)
+- {
+- size_t retlen;
++ struct mtd_blktrans_dev *dev = kmalloc(sizeof(*dev), GFP_KERNEL);
+
+- case READ:
+- if (MTD_READ(mtd,current_request->sector<<9,
+- current_request->current_nr_sectors << 9,
+- &retlen, current_request->buffer) == 0)
+- res = 1;
+- else
+- res = 0;
+- break;
++ if (!dev)
++ return;
+
+- case WRITE:
++ memset(dev, 0, sizeof(*dev));
+
+- /* printk("mtdblock_request WRITE sector=%d(%d)\n",current_request->sector,
+- current_request->current_nr_sectors);
+- */
++ dev->mtd = mtd;
++ dev->devnum = mtd->index;
++ dev->blksize = 512;
++ dev->size = mtd->size >> 9;
++ dev->tr = tr;
++ if ((mtd->flags & (MTD_CLEAR_BITS|MTD_SET_BITS|MTD_WRITEABLE)) !=
++ (MTD_CLEAR_BITS|MTD_SET_BITS|MTD_WRITEABLE))
++ dev->readonly = 1;
+
+- // Read only device
+- if ((mtd->flags & MTD_CAP_RAM) == 0)
+- {
+- res = 0;
+- break;
+- }
+-
+- // Do the write
+- if (MTD_WRITE(mtd,current_request->sector<<9,
+- current_request->current_nr_sectors << 9,
+- &retlen, current_request->buffer) == 0)
+- res = 1;
+- else
+- res = 0;
+- break;
+-
+- // Shouldn't happen
+- default:
+- printk("mtd: unknown request\n");
+- break;
+- }
+-
+- // Grab the lock and re-thread the item onto the linked list
+-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
+- spin_lock_irq(&io_request_lock);
+-#endif
+- end_request(res);
+- }
++ add_mtd_blktrans_dev(dev);
+ }
+
+-
+-
+-static int mtdblock_ioctl(struct inode * inode, struct file * file,
+- unsigned int cmd, unsigned long arg)
++static void mtdblock_remove_dev(struct mtd_blktrans_dev *dev)
+ {
+- struct mtd_info *mtd;
+-
+- mtd = __get_mtd_device(NULL, MINOR(inode->i_rdev));
+-
+- if (!mtd) return -EINVAL;
+-
+- switch (cmd) {
+- case BLKGETSIZE: /* Return device size */
+- return put_user((mtd->size >> 9), (unsigned long *) arg);
+-
+-#ifdef BLKGETSIZE64
+- case BLKGETSIZE64:
+- return put_user((u64)mtd->size, (u64 *)arg);
+-#endif
+-
+- case BLKFLSBUF:
+-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
+- if(!capable(CAP_SYS_ADMIN)) return -EACCES;
+-#endif
+- fsync_dev(inode->i_rdev);
+- invalidate_buffers(inode->i_rdev);
+- if (mtd->sync)
+- mtd->sync(mtd);
+- return 0;
+-
+- default:
+- return -ENOTTY;
+- }
++ del_mtd_blktrans_dev(dev);
++ kfree(dev);
+ }
+
+-#if LINUX_VERSION_CODE < 0x20326
+-static struct file_operations mtd_fops =
+-{
+- open: mtdblock_open,
+- ioctl: mtdblock_ioctl,
+- release: mtdblock_release,
+- read: block_read,
+- write: block_write
+-};
+-#else
+-static struct block_device_operations mtd_fops =
+-{
+-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,14)
+- owner: THIS_MODULE,
+-#endif
+- open: mtdblock_open,
+- release: mtdblock_release,
+- ioctl: mtdblock_ioctl
++struct mtd_blktrans_ops mtdblock_tr = {
++ .name = "mtdblock",
++ .major = 31,
++ .part_bits = 0,
++ .readsect = mtdblock_readsect,
++ .writesect = mtdblock_writesect,
++ .add_mtd = mtdblock_add_mtd,
++ .remove_dev = mtdblock_remove_dev,
++ .owner = THIS_MODULE,
+ };
+-#endif
+
+-int __init init_mtdblock(void)
++static int __init mtdblock_init(void)
+ {
+- int i;
+-
+- if (register_blkdev(MAJOR_NR,DEVICE_NAME,&mtd_fops)) {
+- printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n",
+- MTD_BLOCK_MAJOR);
+- return -EAGAIN;
+- }
+-
+- /* We fill it in at open() time. */
+- for (i=0; i< MAX_MTD_DEVICES; i++) {
+- mtd_sizes[i] = 0;
+- }
+-
+- /* Allow the block size to default to BLOCK_SIZE. */
+- blksize_size[MAJOR_NR] = NULL;
+- blk_size[MAJOR_NR] = mtd_sizes;
+-
+- blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), &mtdblock_request);
+- return 0;
++ return register_mtd_blktrans(&mtdblock_tr);
+ }
+
+-static void __exit cleanup_mtdblock(void)
++static void __exit mtdblock_exit(void)
+ {
+- unregister_blkdev(MAJOR_NR,DEVICE_NAME);
+- blk_size[MAJOR_NR] = NULL;
+- blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
++ deregister_mtd_blktrans(&mtdblock_tr);
+ }
+
+-module_init(init_mtdblock);
+-module_exit(cleanup_mtdblock);
+-
++module_init(mtdblock_init);
++module_exit(mtdblock_exit);
+
+ MODULE_LICENSE("GPL");
+-MODULE_AUTHOR("Erwin Authried <eauth@softsys.co.at> et al.");
++MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
+ MODULE_DESCRIPTION("Simple read-only block device emulation access to MTD devices");
+diff -Nurb linux-mips-2.4.27/drivers/mtd/mtdchar.c linux/drivers/mtd/mtdchar.c
+--- linux-mips-2.4.27/drivers/mtd/mtdchar.c 2004-08-14 20:38:51.000000000 +0200
++++ linux/drivers/mtd/mtdchar.c 2004-11-19 10:25:11.647238096 +0100
+@@ -1,8 +1,7 @@
+ /*
+- * $Id$
++ * $Id$
+ *
+ * Character-device access to raw MTD devices.
+- * Pure 2.4 version - compatibility cruft removed to mtdchar-compat.c
+ *
+ */
+
+@@ -10,7 +9,11 @@
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+ #include <linux/mtd/mtd.h>
++#include <linux/mtd/compatmac.h>
+ #include <linux/slab.h>
++#include <linux/init.h>
++#include <linux/fs.h>
++#include <asm/uaccess.h>
+
+ #ifdef CONFIG_DEVFS_FS
+ #include <linux/devfs_fs_kernel.h>
+@@ -18,8 +21,8 @@
+ static void mtd_notify_remove(struct mtd_info* mtd);
+
+ static struct mtd_notifier notifier = {
+- add: mtd_notify_add,
+- remove: mtd_notify_remove,
++ .add = mtd_notify_add,
++ .remove = mtd_notify_remove,
+ };
+
+ static devfs_handle_t devfs_dir_handle;
+@@ -60,7 +63,7 @@
+
+ static int mtd_open(struct inode *inode, struct file *file)
+ {
+- int minor = minor(inode->i_rdev);
++ int minor = iminor(inode);
+ int devnum = minor >> 1;
+ struct mtd_info *mtd;
+
+@@ -125,15 +128,11 @@
+ int ret=0;
+ int len;
+ char *kbuf;
+- loff_t pos = *ppos;
+
+ DEBUG(MTD_DEBUG_LEVEL0,"MTD_read\n");
+
+- if (pos < 0 || pos > mtd->size)
+- return 0;
+-
+- if (count > mtd->size - pos)
+- count = mtd->size - pos;
++ if (*ppos + count > mtd->size)
++ count = mtd->size - *ppos;
+
+ if (!count)
+ return 0;
+@@ -150,9 +149,9 @@
+ if (!kbuf)
+ return -ENOMEM;
+
+- ret = MTD_READ(mtd, pos, len, &retlen, kbuf);
++ ret = MTD_READ(mtd, *ppos, len, &retlen, kbuf);
+ if (!ret) {
+- pos += retlen;
++ *ppos += retlen;
+ if (copy_to_user(buf, kbuf, retlen)) {
+ kfree(kbuf);
+ return -EFAULT;
+@@ -171,8 +170,6 @@
+ kfree(kbuf);
+ }
+
+- *ppos = pos;
+-
+ return total_retlen;
+ } /* mtd_read */
+
+@@ -182,17 +179,16 @@
+ char *kbuf;
+ size_t retlen;
+ size_t total_retlen=0;
+- loff_t pos = *ppos;
+ int ret=0;
+ int len;
+
+ DEBUG(MTD_DEBUG_LEVEL0,"MTD_write\n");
+
+- if (pos < 0 || pos >= mtd->size)
++ if (*ppos == mtd->size)
+ return -ENOSPC;
+
+- if (count > mtd->size - pos)
+- count = mtd->size - pos;
++ if (*ppos + count > mtd->size)
++ count = mtd->size - *ppos;
+
+ if (!count)
+ return 0;
+@@ -214,9 +210,9 @@
+ return -EFAULT;
+ }
+
+- ret = (*(mtd->write))(mtd, pos, len, &retlen, kbuf);
++ ret = (*(mtd->write))(mtd, *ppos, len, &retlen, kbuf);
+ if (!ret) {
+- pos += retlen;
++ *ppos += retlen;
+ total_retlen += retlen;
+ count -= retlen;
+ buf += retlen;
+@@ -228,7 +224,6 @@
+
+ kfree(kbuf);
+ }
+- *ppos = pos;
+
+ return total_retlen;
+ } /* mtd_write */
+@@ -450,81 +445,13 @@
+ break;
+ }
+
+- case MEMWRITEDATA:
+- {
+- struct mtd_oob_buf buf;
+- void *databuf;
+- ssize_t retlen;
+-
+- if (copy_from_user(&buf, (struct mtd_oob_buf *)arg, sizeof(struct mtd_oob_buf)))
+- return -EFAULT;
+-
+- if (buf.length > 0x4096)
+- return -EINVAL;
+-
+- if (!mtd->write_ecc)
+- ret = -EOPNOTSUPP;
+- else
+- ret = verify_area(VERIFY_READ, (char *)buf.ptr, buf.length);
+-
+- if (ret)
+- return ret;
+-
+- databuf = kmalloc(buf.length, GFP_KERNEL);
+- if (!databuf)
+- return -ENOMEM;
+-
+- if (copy_from_user(databuf, buf.ptr, buf.length)) {
+- kfree(databuf);
+- return -EFAULT;
+- }
+-
+- ret = (mtd->write_ecc)(mtd, buf.start, buf.length, &retlen, databuf, NULL, 0);
+-
+- if (copy_to_user((void *)arg + sizeof(u_int32_t), &retlen, sizeof(u_int32_t)))
+- ret = -EFAULT;
+-
+- kfree(databuf);
+- break;
+-
+- }
+-
+- case MEMREADDATA:
++ case MEMSETOOBSEL:
+ {
+- struct mtd_oob_buf buf;
+- void *databuf;
+- ssize_t retlen = 0;
+-
+- if (copy_from_user(&buf, (struct mtd_oob_buf *)arg, sizeof(struct mtd_oob_buf)))
++ if (copy_from_user(&mtd->oobinfo ,(void *)arg, sizeof(struct nand_oobinfo)))
+ return -EFAULT;
+-
+- if (buf.length > 0x4096)
+- return -EINVAL;
+-
+- if (!mtd->read_ecc)
+- ret = -EOPNOTSUPP;
+- else
+- ret = verify_area(VERIFY_WRITE, (char *)buf.ptr, buf.length);
+-
+- if (ret)
+- return ret;
+-
+- databuf = kmalloc(buf.length, GFP_KERNEL);
+- if (!databuf)
+- return -ENOMEM;
+-
+- ret = (mtd->read_ecc)(mtd, buf.start, buf.length, &retlen, databuf, NULL, 0);
+-
+- if (copy_to_user((void *)arg + sizeof(u_int32_t), &retlen, sizeof(u_int32_t)))
+- ret = -EFAULT;
+- else if (retlen && copy_to_user(buf.ptr, databuf, retlen))
+- ret = -EFAULT;
+-
+- kfree(databuf);
+ break;
+ }
+
+-
+ default:
+ DEBUG(MTD_DEBUG_LEVEL0, "Invalid ioctl %x (MEMGETINFO = %x)\n", cmd, MEMGETINFO);
+ ret = -ENOTTY;
+@@ -534,13 +461,13 @@
+ } /* memory_ioctl */
+
+ static struct file_operations mtd_fops = {
+- owner: THIS_MODULE,
+- llseek: mtd_lseek, /* lseek */
+- read: mtd_read, /* read */
+- write: mtd_write, /* write */
+- ioctl: mtd_ioctl, /* ioctl */
+- open: mtd_open, /* open */
+- release: mtd_close, /* release */
++ .owner = THIS_MODULE,
++ .llseek = mtd_lseek,
++ .read = mtd_read,
++ .write = mtd_write,
++ .ioctl = mtd_ioctl,
++ .open = mtd_open,
++ .release = mtd_close,
+ };
+
+
+@@ -580,26 +507,18 @@
+
+ static int __init init_mtdchar(void)
+ {
+-#ifdef CONFIG_DEVFS_FS
+- if (devfs_register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops))
++ if (register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops))
+ {
+ printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n",
+ MTD_CHAR_MAJOR);
+ return -EAGAIN;
+ }
+
++#ifdef CONFIG_DEVFS_FS
+ devfs_dir_handle = devfs_mk_dir(NULL, "mtd", NULL);
+
+ register_mtd_user(&notifier);
+-#else
+- if (register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops))
+- {
+- printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n",
+- MTD_CHAR_MAJOR);
+- return -EAGAIN;
+- }
+ #endif
+-
+ return 0;
+ }
+
+@@ -608,10 +527,8 @@
+ #ifdef CONFIG_DEVFS_FS
+ unregister_mtd_user(&notifier);
+ devfs_unregister(devfs_dir_handle);
+- devfs_unregister_chrdev(MTD_CHAR_MAJOR, "mtd");
+-#else
+- unregister_chrdev(MTD_CHAR_MAJOR, "mtd");
+ #endif
++ unregister_chrdev(MTD_CHAR_MAJOR, "mtd");
+ }
+
+ module_init(init_mtdchar);
+diff -Nurb linux-mips-2.4.27/drivers/mtd/mtdconcat.c linux/drivers/mtd/mtdconcat.c
+--- linux-mips-2.4.27/drivers/mtd/mtdconcat.c 2003-02-26 01:53:49.000000000 +0100
++++ linux/drivers/mtd/mtdconcat.c 2004-11-19 10:25:11.649237792 +0100
+@@ -3,9 +3,11 @@
+ *
+ * (C) 2002 Robert Kaiser <rkaiser@sysgo.de>
+ *
++ * NAND support by Christian Gan <cgan@iders.ca>
++ *
+ * This code is GPL
+ *
+- * $Id$
++ * $Id$
+ */
+
+ #include <linux/module.h>
+@@ -35,21 +37,20 @@
+ #define SIZEOF_STRUCT_MTD_CONCAT(num_subdev) \
+ ((sizeof(struct mtd_concat) + (num_subdev) * sizeof(struct mtd_info *)))
+
+-
+ /*
+ * Given a pointer to the MTD object in the mtd_concat structure,
+ * we can retrieve the pointer to that structure with this macro.
+ */
+ #define CONCAT(x) ((struct mtd_concat *)(x))
+
+-
+ /*
+ * MTD methods which look up the relevant subdevice, translate the
+ * effective address and pass through to the subdevice.
+ */
+
+-static int concat_read (struct mtd_info *mtd, loff_t from, size_t len,
+- size_t *retlen, u_char *buf)
++static int
++concat_read(struct mtd_info *mtd, loff_t from, size_t len,
++ size_t * retlen, u_char * buf)
+ {
+ struct mtd_concat *concat = CONCAT(mtd);
+ int err = -EINVAL;
+@@ -57,43 +58,43 @@
+
+ *retlen = 0;
+
+- for(i = 0; i < concat->num_subdev; i++)
+- {
++ for (i = 0; i < concat->num_subdev; i++) {
+ struct mtd_info *subdev = concat->subdev[i];
+ size_t size, retsize;
+
+- if (from >= subdev->size)
+- {
++ if (from >= subdev->size) {
++ /* Not destined for this subdev */
+ size = 0;
+ from -= subdev->size;
++ continue;
+ }
+- else
+- {
+ if (from + len > subdev->size)
++ /* First part goes into this subdev */
+ size = subdev->size - from;
+ else
++ /* Entire transaction goes into this subdev */
+ size = len;
+
+ err = subdev->read(subdev, from, size, &retsize, buf);
+
+- if(err)
++ if (err)
+ break;
+
+ *retlen += retsize;
+ len -= size;
+- if(len == 0)
++ if (len == 0)
+ break;
+
+ err = -EINVAL;
+ buf += size;
+ from = 0;
+ }
+- }
+ return err;
+ }
+
+-static int concat_write (struct mtd_info *mtd, loff_t to, size_t len,
+- size_t *retlen, const u_char *buf)
++static int
++concat_write(struct mtd_info *mtd, loff_t to, size_t len,
++ size_t * retlen, const u_char * buf)
+ {
+ struct mtd_concat *concat = CONCAT(mtd);
+ int err = -EINVAL;
+@@ -104,18 +105,15 @@
+
+ *retlen = 0;
+
+- for(i = 0; i < concat->num_subdev; i++)
+- {
++ for (i = 0; i < concat->num_subdev; i++) {
+ struct mtd_info *subdev = concat->subdev[i];
+ size_t size, retsize;
+
+- if (to >= subdev->size)
+- {
++ if (to >= subdev->size) {
+ size = 0;
+ to -= subdev->size;
++ continue;
+ }
+- else
+- {
+ if (to + len > subdev->size)
+ size = subdev->size - to;
+ else
+@@ -126,25 +124,232 @@
+ else
+ err = subdev->write(subdev, to, size, &retsize, buf);
+
+- if(err)
++ if (err)
+ break;
+
+ *retlen += retsize;
+ len -= size;
+- if(len == 0)
++ if (len == 0)
+ break;
+
+ err = -EINVAL;
+ buf += size;
+ to = 0;
+ }
++ return err;
++}
++
++static int
++concat_read_ecc(struct mtd_info *mtd, loff_t from, size_t len,
++ size_t * retlen, u_char * buf, u_char * eccbuf,
++ struct nand_oobinfo *oobsel)
++{
++ struct mtd_concat *concat = CONCAT(mtd);
++ int err = -EINVAL;
++ int i;
++
++ *retlen = 0;
++
++ for (i = 0; i < concat->num_subdev; i++) {
++ struct mtd_info *subdev = concat->subdev[i];
++ size_t size, retsize;
++
++ if (from >= subdev->size) {
++ /* Not destined for this subdev */
++ size = 0;
++ from -= subdev->size;
++ continue;
++ }
++
++ if (from + len > subdev->size)
++ /* First part goes into this subdev */
++ size = subdev->size - from;
++ else
++ /* Entire transaction goes into this subdev */
++ size = len;
++
++ if (subdev->read_ecc)
++ err = subdev->read_ecc(subdev, from, size,
++ &retsize, buf, eccbuf, oobsel);
++ else
++ err = -EINVAL;
++
++ if (err)
++ break;
++
++ *retlen += retsize;
++ len -= size;
++ if (len == 0)
++ break;
++
++ err = -EINVAL;
++ buf += size;
++ if (eccbuf) {
++ eccbuf += subdev->oobsize;
++ /* in nand.c at least, eccbufs are
++ tagged with 2 (int)eccstatus'; we
++ must account for these */
++ eccbuf += 2 * (sizeof (int));
++ }
++ from = 0;
+ }
+ return err;
+ }
+
+-static void concat_erase_callback (struct erase_info *instr)
++static int
++concat_write_ecc(struct mtd_info *mtd, loff_t to, size_t len,
++ size_t * retlen, const u_char * buf, u_char * eccbuf,
++ struct nand_oobinfo *oobsel)
+ {
+- wake_up((wait_queue_head_t *)instr->priv);
++ struct mtd_concat *concat = CONCAT(mtd);
++ int err = -EINVAL;
++ int i;
++
++ if (!(mtd->flags & MTD_WRITEABLE))
++ return -EROFS;
++
++ *retlen = 0;
++
++ for (i = 0; i < concat->num_subdev; i++) {
++ struct mtd_info *subdev = concat->subdev[i];
++ size_t size, retsize;
++
++ if (to >= subdev->size) {
++ size = 0;
++ to -= subdev->size;
++ continue;
++ }
++ if (to + len > subdev->size)
++ size = subdev->size - to;
++ else
++ size = len;
++
++ if (!(subdev->flags & MTD_WRITEABLE))
++ err = -EROFS;
++ else if (subdev->write_ecc)
++ err = subdev->write_ecc(subdev, to, size,
++ &retsize, buf, eccbuf, oobsel);
++ else
++ err = -EINVAL;
++
++ if (err)
++ break;
++
++ *retlen += retsize;
++ len -= size;
++ if (len == 0)
++ break;
++
++ err = -EINVAL;
++ buf += size;
++ if (eccbuf)
++ eccbuf += subdev->oobsize;
++ to = 0;
++ }
++ return err;
++}
++
++static int
++concat_read_oob(struct mtd_info *mtd, loff_t from, size_t len,
++ size_t * retlen, u_char * buf)
++{
++ struct mtd_concat *concat = CONCAT(mtd);
++ int err = -EINVAL;
++ int i;
++
++ *retlen = 0;
++
++ for (i = 0; i < concat->num_subdev; i++) {
++ struct mtd_info *subdev = concat->subdev[i];
++ size_t size, retsize;
++
++ if (from >= subdev->size) {
++ /* Not destined for this subdev */
++ size = 0;
++ from -= subdev->size;
++ continue;
++ }
++ if (from + len > subdev->size)
++ /* First part goes into this subdev */
++ size = subdev->size - from;
++ else
++ /* Entire transaction goes into this subdev */
++ size = len;
++
++ if (subdev->read_oob)
++ err = subdev->read_oob(subdev, from, size,
++ &retsize, buf);
++ else
++ err = -EINVAL;
++
++ if (err)
++ break;
++
++ *retlen += retsize;
++ len -= size;
++ if (len == 0)
++ break;
++
++ err = -EINVAL;
++ buf += size;
++ from = 0;
++ }
++ return err;
++}
++
++static int
++concat_write_oob(struct mtd_info *mtd, loff_t to, size_t len,
++ size_t * retlen, const u_char * buf)
++{
++ struct mtd_concat *concat = CONCAT(mtd);
++ int err = -EINVAL;
++ int i;
++
++ if (!(mtd->flags & MTD_WRITEABLE))
++ return -EROFS;
++
++ *retlen = 0;
++
++ for (i = 0; i < concat->num_subdev; i++) {
++ struct mtd_info *subdev = concat->subdev[i];
++ size_t size, retsize;
++
++ if (to >= subdev->size) {
++ size = 0;
++ to -= subdev->size;
++ continue;
++ }
++ if (to + len > subdev->size)
++ size = subdev->size - to;
++ else
++ size = len;
++
++ if (!(subdev->flags & MTD_WRITEABLE))
++ err = -EROFS;
++ else if (subdev->write_oob)
++ err = subdev->write_oob(subdev, to, size, &retsize,
++ buf);
++ else
++ err = -EINVAL;
++
++ if (err)
++ break;
++
++ *retlen += retsize;
++ len -= size;
++ if (len == 0)
++ break;
++
++ err = -EINVAL;
++ buf += size;
++ to = 0;
++ }
++ return err;
++}
++
++static void concat_erase_callback(struct erase_info *instr)
++{
++ wake_up((wait_queue_head_t *) instr->priv);
+ }
+
+ static int concat_dev_erase(struct mtd_info *mtd, struct erase_info *erase)
+@@ -160,18 +365,18 @@
+
+ erase->mtd = mtd;
+ erase->callback = concat_erase_callback;
+- erase->priv = (unsigned long)&waitq;
++ erase->priv = (unsigned long) &waitq;
+
+ /*
+ * FIXME: Allow INTERRUPTIBLE. Which means
+ * not having the wait_queue head on the stack.
+ */
+ err = mtd->erase(mtd, erase);
+- if (!err)
+- {
++ if (!err) {
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ add_wait_queue(&waitq, &wait);
+- if (erase->state != MTD_ERASE_DONE && erase->state != MTD_ERASE_FAILED)
++ if (erase->state != MTD_ERASE_DONE
++ && erase->state != MTD_ERASE_FAILED)
+ schedule();
+ remove_wait_queue(&waitq, &wait);
+ set_current_state(TASK_RUNNING);
+@@ -181,7 +386,7 @@
+ return err;
+ }
+
+-static int concat_erase (struct mtd_info *mtd, struct erase_info *instr)
++static int concat_erase(struct mtd_info *mtd, struct erase_info *instr)
+ {
+ struct mtd_concat *concat = CONCAT(mtd);
+ struct mtd_info *subdev;
+@@ -192,10 +397,10 @@
+ if (!(mtd->flags & MTD_WRITEABLE))
+ return -EROFS;
+
+- if(instr->addr > concat->mtd.size)
++ if (instr->addr > concat->mtd.size)
+ return -EINVAL;
+
+- if(instr->len + instr->addr > concat->mtd.size)
++ if (instr->len + instr->addr > concat->mtd.size)
+ return -EINVAL;
+
+ /*
+@@ -204,23 +409,22 @@
+ * region info rather than looking at each particular sub-device
+ * in turn.
+ */
+- if (!concat->mtd.numeraseregions)
+- { /* the easy case: device has uniform erase block size */
+- if(instr->addr & (concat->mtd.erasesize - 1))
++ if (!concat->mtd.numeraseregions) {
++ /* the easy case: device has uniform erase block size */
++ if (instr->addr & (concat->mtd.erasesize - 1))
+ return -EINVAL;
+- if(instr->len & (concat->mtd.erasesize - 1))
++ if (instr->len & (concat->mtd.erasesize - 1))
+ return -EINVAL;
+- }
+- else
+- { /* device has variable erase size */
+- struct mtd_erase_region_info *erase_regions = concat->mtd.eraseregions;
++ } else {
++ /* device has variable erase size */
++ struct mtd_erase_region_info *erase_regions =
++ concat->mtd.eraseregions;
+
+ /*
+ * Find the erase region where the to-be-erased area begins:
+ */
+- for(i = 0; i < concat->mtd.numeraseregions &&
+- instr->addr >= erase_regions[i].offset; i++)
+- ;
++ for (i = 0; i < concat->mtd.numeraseregions &&
++ instr->addr >= erase_regions[i].offset; i++) ;
+ --i;
+
+ /*
+@@ -228,25 +432,26 @@
+ * to-be-erased area begins. Verify that the starting
+ * offset is aligned to this region's erase size:
+ */
+- if (instr->addr & (erase_regions[i].erasesize-1))
++ if (instr->addr & (erase_regions[i].erasesize - 1))
+ return -EINVAL;
+
+ /*
+ * now find the erase region where the to-be-erased area ends:
+ */
+- for(; i < concat->mtd.numeraseregions &&
+- (instr->addr + instr->len) >= erase_regions[i].offset ; ++i)
+- ;
++ for (; i < concat->mtd.numeraseregions &&
++ (instr->addr + instr->len) >= erase_regions[i].offset;
++ ++i) ;
+ --i;
+ /*
+ * check if the ending offset is aligned to this region's erase size
+ */
+- if ((instr->addr + instr->len) & (erase_regions[i].erasesize-1))
++ if ((instr->addr + instr->len) & (erase_regions[i].erasesize -
++ 1))
+ return -EINVAL;
+ }
+
+ /* make a local copy of instr to avoid modifying the caller's struct */
+- erase = kmalloc(sizeof(struct erase_info),GFP_KERNEL);
++ erase = kmalloc(sizeof (struct erase_info), GFP_KERNEL);
+
+ if (!erase)
+ return -ENOMEM;
+@@ -258,39 +463,40 @@
+ * find the subdevice where the to-be-erased area begins, adjust
+ * starting offset to be relative to the subdevice start
+ */
+- for(i = 0; i < concat->num_subdev; i++)
+- {
++ for (i = 0; i < concat->num_subdev; i++) {
+ subdev = concat->subdev[i];
+- if(subdev->size <= erase->addr)
++ if (subdev->size <= erase->addr)
+ erase->addr -= subdev->size;
+ else
+ break;
+ }
+- if(i >= concat->num_subdev) /* must never happen since size */
+- BUG(); /* limit has been verified above */
++
++ /* must never happen since size limit has been verified above */
++ if (i >= concat->num_subdev)
++ BUG();
+
+ /* now do the erase: */
+ err = 0;
+- for(;length > 0; i++) /* loop for all subevices affected by this request */
+- {
++ for (; length > 0; i++) {
++ /* loop for all subdevices affected by this request */
+ subdev = concat->subdev[i]; /* get current subdevice */
+
+ /* limit length to subdevice's size: */
+- if(erase->addr + length > subdev->size)
++ if (erase->addr + length > subdev->size)
+ erase->len = subdev->size - erase->addr;
+ else
+ erase->len = length;
+
+- if (!(subdev->flags & MTD_WRITEABLE))
+- {
++ if (!(subdev->flags & MTD_WRITEABLE)) {
+ err = -EROFS;
+ break;
+ }
+ length -= erase->len;
+- if ((err = concat_dev_erase(subdev, erase)))
+- {
+- if(err == -EINVAL) /* sanity check: must never happen since */
+- BUG(); /* block alignment has been checked above */
++ if ((err = concat_dev_erase(subdev, erase))) {
++ /* sanity check: should never happen since
++ * block alignment has been checked above */
++ if (err == -EINVAL)
++ BUG();
+ break;
+ }
+ /*
+@@ -313,7 +519,7 @@
+ return 0;
+ }
+
+-static int concat_lock (struct mtd_info *mtd, loff_t ofs, size_t len)
++static int concat_lock(struct mtd_info *mtd, loff_t ofs, size_t len)
+ {
+ struct mtd_concat *concat = CONCAT(mtd);
+ int i, err = -EINVAL;
+@@ -321,18 +527,15 @@
+ if ((len + ofs) > mtd->size)
+ return -EINVAL;
+
+- for(i = 0; i < concat->num_subdev; i++)
+- {
++ for (i = 0; i < concat->num_subdev; i++) {
+ struct mtd_info *subdev = concat->subdev[i];
+ size_t size;
+
+- if (ofs >= subdev->size)
+- {
++ if (ofs >= subdev->size) {
+ size = 0;
+ ofs -= subdev->size;
++ continue;
+ }
+- else
+- {
+ if (ofs + len > subdev->size)
+ size = subdev->size - ofs;
+ else
+@@ -340,21 +543,21 @@
+
+ err = subdev->lock(subdev, ofs, size);
+
+- if(err)
++ if (err)
+ break;
+
+ len -= size;
+- if(len == 0)
++ if (len == 0)
+ break;
+
+ err = -EINVAL;
+ ofs = 0;
+ }
+- }
++
+ return err;
+ }
+
+-static int concat_unlock (struct mtd_info *mtd, loff_t ofs, size_t len)
++static int concat_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
+ {
+ struct mtd_concat *concat = CONCAT(mtd);
+ int i, err = 0;
+@@ -362,18 +565,15 @@
+ if ((len + ofs) > mtd->size)
+ return -EINVAL;
+
+- for(i = 0; i < concat->num_subdev; i++)
+- {
++ for (i = 0; i < concat->num_subdev; i++) {
+ struct mtd_info *subdev = concat->subdev[i];
+ size_t size;
+
+- if (ofs >= subdev->size)
+- {
++ if (ofs >= subdev->size) {
+ size = 0;
+ ofs -= subdev->size;
++ continue;
+ }
+- else
+- {
+ if (ofs + len > subdev->size)
+ size = subdev->size - ofs;
+ else
+@@ -381,17 +581,17 @@
+
+ err = subdev->unlock(subdev, ofs, size);
+
+- if(err)
++ if (err)
+ break;
+
+ len -= size;
+- if(len == 0)
++ if (len == 0)
+ break;
+
+ err = -EINVAL;
+ ofs = 0;
+ }
+- }
++
+ return err;
+ }
+
+@@ -400,8 +600,7 @@
+ struct mtd_concat *concat = CONCAT(mtd);
+ int i;
+
+- for(i = 0; i < concat->num_subdev; i++)
+- {
++ for (i = 0; i < concat->num_subdev; i++) {
+ struct mtd_info *subdev = concat->subdev[i];
+ subdev->sync(subdev);
+ }
+@@ -412,10 +611,9 @@
+ struct mtd_concat *concat = CONCAT(mtd);
+ int i, rc = 0;
+
+- for(i = 0; i < concat->num_subdev; i++)
+- {
++ for (i = 0; i < concat->num_subdev; i++) {
+ struct mtd_info *subdev = concat->subdev[i];
+- if((rc = subdev->suspend(subdev)) < 0)
++ if ((rc = subdev->suspend(subdev)) < 0)
+ return rc;
+ }
+ return rc;
+@@ -426,8 +624,7 @@
+ struct mtd_concat *concat = CONCAT(mtd);
+ int i;
+
+- for(i = 0; i < concat->num_subdev; i++)
+- {
++ for (i = 0; i < concat->num_subdev; i++) {
+ struct mtd_info *subdev = concat->subdev[i];
+ subdev->resume(subdev);
+ }
+@@ -439,11 +636,10 @@
+ * stored to *new_dev upon success. This function does _not_
+ * register any devices: this is the caller's responsibility.
+ */
+-struct mtd_info *mtd_concat_create(
+- struct mtd_info *subdev[], /* subdevices to concatenate */
++struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to concatenate */
+ int num_devs, /* number of subdevices */
+- char *name) /* name for the new device */
+-{
++ char *name)
++{ /* name for the new device */
+ int i;
+ size_t size;
+ struct mtd_concat *concat;
+@@ -451,21 +647,21 @@
+ int num_erase_region;
+
+ printk(KERN_NOTICE "Concatenating MTD devices:\n");
+- for(i = 0; i < num_devs; i++)
++ for (i = 0; i < num_devs; i++)
+ printk(KERN_NOTICE "(%d): \"%s\"\n", i, subdev[i]->name);
+ printk(KERN_NOTICE "into device \"%s\"\n", name);
+
+ /* allocate the device structure */
+ size = SIZEOF_STRUCT_MTD_CONCAT(num_devs);
+- concat = kmalloc (size, GFP_KERNEL);
+- if(!concat)
+- {
+- printk ("memory allocation error while creating concatenated device \"%s\"\n",
++ concat = kmalloc(size, GFP_KERNEL);
++ if (!concat) {
++ printk
++ ("memory allocation error while creating concatenated device \"%s\"\n",
+ name);
+ return NULL;
+ }
+ memset(concat, 0, size);
+- concat->subdev = (struct mtd_info **)(concat + 1);
++ concat->subdev = (struct mtd_info **) (concat + 1);
+
+ /*
+ * Set up the new "super" device's MTD object structure, check for
+@@ -479,39 +675,53 @@
+ concat->mtd.oobsize = subdev[0]->oobsize;
+ concat->mtd.ecctype = subdev[0]->ecctype;
+ concat->mtd.eccsize = subdev[0]->eccsize;
++ if (subdev[0]->read_ecc)
++ concat->mtd.read_ecc = concat_read_ecc;
++ if (subdev[0]->write_ecc)
++ concat->mtd.write_ecc = concat_write_ecc;
++ if (subdev[0]->read_oob)
++ concat->mtd.read_oob = concat_read_oob;
++ if (subdev[0]->write_oob)
++ concat->mtd.write_oob = concat_write_oob;
+
+ concat->subdev[0] = subdev[0];
+
+- for(i = 1; i < num_devs; i++)
+- {
+- if(concat->mtd.type != subdev[i]->type)
+- {
++ for (i = 1; i < num_devs; i++) {
++ if (concat->mtd.type != subdev[i]->type) {
+ kfree(concat);
+- printk ("Incompatible device type on \"%s\"\n", subdev[i]->name);
++ printk("Incompatible device type on \"%s\"\n",
++ subdev[i]->name);
+ return NULL;
+ }
+- if(concat->mtd.flags != subdev[i]->flags)
+- { /*
+- * Expect all flags except MTD_WRITEABLE to be equal on
+- * all subdevices.
++ if (concat->mtd.flags != subdev[i]->flags) {
++ /*
++ * Expect all flags except MTD_WRITEABLE to be
++ * equal on all subdevices.
+ */
+- if((concat->mtd.flags ^ subdev[i]->flags) & ~MTD_WRITEABLE)
+- {
++ if ((concat->mtd.flags ^ subdev[i]->
++ flags) & ~MTD_WRITEABLE) {
+ kfree(concat);
+- printk ("Incompatible device flags on \"%s\"\n", subdev[i]->name);
++ printk("Incompatible device flags on \"%s\"\n",
++ subdev[i]->name);
+ return NULL;
+- }
+- else /* if writeable attribute differs, make super device writeable */
+- concat->mtd.flags |= subdev[i]->flags & MTD_WRITEABLE;
++ } else
++ /* if writeable attribute differs,
++ make super device writeable */
++ concat->mtd.flags |=
++ subdev[i]->flags & MTD_WRITEABLE;
+ }
+ concat->mtd.size += subdev[i]->size;
+- if(concat->mtd.oobblock != subdev[i]->oobblock ||
++ if (concat->mtd.oobblock != subdev[i]->oobblock ||
+ concat->mtd.oobsize != subdev[i]->oobsize ||
+ concat->mtd.ecctype != subdev[i]->ecctype ||
+- concat->mtd.eccsize != subdev[i]->eccsize)
+- {
++ concat->mtd.eccsize != subdev[i]->eccsize ||
++ !concat->mtd.read_ecc != !subdev[i]->read_ecc ||
++ !concat->mtd.write_ecc != !subdev[i]->write_ecc ||
++ !concat->mtd.read_oob != !subdev[i]->read_oob ||
++ !concat->mtd.write_oob != !subdev[i]->write_oob) {
+ kfree(concat);
+- printk ("Incompatible OOB or ECC data on \"%s\"\n", subdev[i]->name);
++ printk("Incompatible OOB or ECC data on \"%s\"\n",
++ subdev[i]->name);
+ return NULL;
+ }
+ concat->subdev[i] = subdev[i];
+@@ -535,7 +745,6 @@
+ concat->mtd.suspend = concat_suspend;
+ concat->mtd.resume = concat_resume;
+
+-
+ /*
+ * Combine the erase block size info of the subdevices:
+ *
+@@ -544,44 +753,44 @@
+ */
+ max_erasesize = curr_erasesize = subdev[0]->erasesize;
+ num_erase_region = 1;
+- for(i = 0; i < num_devs; i++)
+- {
+- if(subdev[i]->numeraseregions == 0)
+- { /* current subdevice has uniform erase size */
+- if(subdev[i]->erasesize != curr_erasesize)
+- { /* if it differs from the last subdevice's erase size, count it */
++ for (i = 0; i < num_devs; i++) {
++ if (subdev[i]->numeraseregions == 0) {
++ /* current subdevice has uniform erase size */
++ if (subdev[i]->erasesize != curr_erasesize) {
++ /* if it differs from the last subdevice's erase size, count it */
+ ++num_erase_region;
+ curr_erasesize = subdev[i]->erasesize;
+- if(curr_erasesize > max_erasesize)
++ if (curr_erasesize > max_erasesize)
+ max_erasesize = curr_erasesize;
+ }
+- }
+- else
+- { /* current subdevice has variable erase size */
++ } else {
++ /* current subdevice has variable erase size */
+ int j;
+- for(j = 0; j < subdev[i]->numeraseregions; j++)
+- { /* walk the list of erase regions, count any changes */
+- if(subdev[i]->eraseregions[j].erasesize != curr_erasesize)
+- {
++ for (j = 0; j < subdev[i]->numeraseregions; j++) {
++
++ /* walk the list of erase regions, count any changes */
++ if (subdev[i]->eraseregions[j].erasesize !=
++ curr_erasesize) {
+ ++num_erase_region;
+- curr_erasesize = subdev[i]->eraseregions[j].erasesize;
+- if(curr_erasesize > max_erasesize)
++ curr_erasesize =
++ subdev[i]->eraseregions[j].
++ erasesize;
++ if (curr_erasesize > max_erasesize)
+ max_erasesize = curr_erasesize;
+ }
+ }
+ }
+ }
+
+- if(num_erase_region == 1)
+- { /*
++ if (num_erase_region == 1) {
++ /*
+ * All subdevices have the same uniform erase size.
+ * This is easy:
+ */
+ concat->mtd.erasesize = curr_erasesize;
+ concat->mtd.numeraseregions = 0;
+- }
+- else
+- { /*
++ } else {
++ /*
+ * erase block size varies across the subdevices: allocate
+ * space to store the data describing the variable erase regions
+ */
+@@ -590,12 +799,13 @@
+
+ concat->mtd.erasesize = max_erasesize;
+ concat->mtd.numeraseregions = num_erase_region;
+- concat->mtd.eraseregions = erase_region_p = kmalloc (
+- num_erase_region * sizeof(struct mtd_erase_region_info), GFP_KERNEL);
+- if(!erase_region_p)
+- {
++ concat->mtd.eraseregions = erase_region_p =
++ kmalloc(num_erase_region *
++ sizeof (struct mtd_erase_region_info), GFP_KERNEL);
++ if (!erase_region_p) {
+ kfree(concat);
+- printk ("memory allocation error while creating erase region list"
++ printk
++ ("memory allocation error while creating erase region list"
+ " for device \"%s\"\n", name);
+ return NULL;
+ }
+@@ -606,41 +816,48 @@
+ */
+ curr_erasesize = subdev[0]->erasesize;
+ begin = position = 0;
+- for(i = 0; i < num_devs; i++)
+- {
+- if(subdev[i]->numeraseregions == 0)
+- { /* current subdevice has uniform erase size */
+- if(subdev[i]->erasesize != curr_erasesize)
+- { /*
++ for (i = 0; i < num_devs; i++) {
++ if (subdev[i]->numeraseregions == 0) {
++ /* current subdevice has uniform erase size */
++ if (subdev[i]->erasesize != curr_erasesize) {
++ /*
+ * fill in an mtd_erase_region_info structure for the area
+ * we have walked so far:
+ */
+ erase_region_p->offset = begin;
+- erase_region_p->erasesize = curr_erasesize;
+- erase_region_p->numblocks = (position - begin) / curr_erasesize;
++ erase_region_p->erasesize =
++ curr_erasesize;
++ erase_region_p->numblocks =
++ (position - begin) / curr_erasesize;
+ begin = position;
+
+ curr_erasesize = subdev[i]->erasesize;
+ ++erase_region_p;
+ }
+ position += subdev[i]->size;
+- }
+- else
+- { /* current subdevice has variable erase size */
++ } else {
++ /* current subdevice has variable erase size */
+ int j;
+- for(j = 0; j < subdev[i]->numeraseregions; j++)
+- { /* walk the list of erase regions, count any changes */
+- if(subdev[i]->eraseregions[j].erasesize != curr_erasesize)
+- {
++ for (j = 0; j < subdev[i]->numeraseregions; j++) {
++ /* walk the list of erase regions, count any changes */
++ if (subdev[i]->eraseregions[j].
++ erasesize != curr_erasesize) {
+ erase_region_p->offset = begin;
+- erase_region_p->erasesize = curr_erasesize;
+- erase_region_p->numblocks = (position - begin) / curr_erasesize;
++ erase_region_p->erasesize =
++ curr_erasesize;
++ erase_region_p->numblocks =
++ (position -
++ begin) / curr_erasesize;
+ begin = position;
+
+- curr_erasesize = subdev[i]->eraseregions[j].erasesize;
++ curr_erasesize =
++ subdev[i]->eraseregions[j].
++ erasesize;
+ ++erase_region_p;
+ }
+- position += subdev[i]->eraseregions[j].numblocks * curr_erasesize;
++ position +=
++ subdev[i]->eraseregions[j].
++ numblocks * curr_erasesize;
+ }
+ }
+ }
+@@ -660,16 +877,14 @@
+ void mtd_concat_destroy(struct mtd_info *mtd)
+ {
+ struct mtd_concat *concat = CONCAT(mtd);
+- if(concat->mtd.numeraseregions)
++ if (concat->mtd.numeraseregions)
+ kfree(concat->mtd.eraseregions);
+ kfree(concat);
+ }
+
+-
+ EXPORT_SYMBOL(mtd_concat_create);
+ EXPORT_SYMBOL(mtd_concat_destroy);
+
+-
+ MODULE_LICENSE("GPL");
+ MODULE_AUTHOR("Robert Kaiser <rkaiser@sysgo.de>");
+ MODULE_DESCRIPTION("Generic support for concatenating of MTD devices");
+diff -Nurb linux-mips-2.4.27/drivers/mtd/mtdcore.c linux/drivers/mtd/mtdcore.c
+--- linux-mips-2.4.27/drivers/mtd/mtdcore.c 2003-02-26 01:53:49.000000000 +0100
++++ linux/drivers/mtd/mtdcore.c 2004-11-19 10:25:11.650237640 +0100
+@@ -1,5 +1,5 @@
+ /*
+- * $Id$
++ * $Id$
+ *
+ * Core registration and callback routines for MTD
+ * drivers and users.
+@@ -17,6 +17,7 @@
+ #include <linux/major.h>
+ #include <linux/fs.h>
+ #include <linux/ioctl.h>
++#include <linux/init.h>
+ #include <linux/mtd/compatmac.h>
+ #ifdef CONFIG_PROC_FS
+ #include <linux/proc_fs.h>
+@@ -24,9 +25,15 @@
+
+ #include <linux/mtd/mtd.h>
+
+-static DECLARE_MUTEX(mtd_table_mutex);
+-static struct mtd_info *mtd_table[MAX_MTD_DEVICES];
+-static struct mtd_notifier *mtd_notifiers = NULL;
++/* These are exported solely for the purpose of mtd_blkdevs.c. You
++ should not use them for _anything_ else */
++DECLARE_MUTEX(mtd_table_mutex);
++struct mtd_info *mtd_table[MAX_MTD_DEVICES];
++
++EXPORT_SYMBOL_GPL(mtd_table_mutex);
++EXPORT_SYMBOL_GPL(mtd_table);
++
++static LIST_HEAD(mtd_notifiers);
+
+ /**
+ * add_mtd_device - register an MTD device
+@@ -44,21 +51,28 @@
+
+ down(&mtd_table_mutex);
+
+- for (i=0; i< MAX_MTD_DEVICES; i++)
+- if (!mtd_table[i])
+- {
+- struct mtd_notifier *not=mtd_notifiers;
++ for (i=0; i < MAX_MTD_DEVICES; i++)
++ if (!mtd_table[i]) {
++ struct list_head *this;
+
+ mtd_table[i] = mtd;
+ mtd->index = i;
++ mtd->usecount = 0;
++
+ DEBUG(0, "mtd: Giving out device %d to %s\n",i, mtd->name);
+- while (not)
+- {
+- (*(not->add))(mtd);
+- not = not->next;
++ /* No need to get a refcount on the module containing
++ the notifier, since we hold the mtd_table_mutex */
++ list_for_each(this, &mtd_notifiers) {
++ struct mtd_notifier *not = list_entry(this, struct mtd_notifier, list);
++ not->add(mtd);
+ }
++
+ up(&mtd_table_mutex);
+- MOD_INC_USE_COUNT;
++ /* We _know_ we aren't being removed, because
++ our caller is still holding us here. So none
++ of this try_ nonsense, and no bitching about it
++ either. :) */
++ __module_get(THIS_MODULE);
+ return 0;
+ }
+
+@@ -78,29 +92,34 @@
+
+ int del_mtd_device (struct mtd_info *mtd)
+ {
+- struct mtd_notifier *not=mtd_notifiers;
+- int i;
++ int ret;
+
+ down(&mtd_table_mutex);
+
+- for (i=0; i < MAX_MTD_DEVICES; i++)
+- {
+- if (mtd_table[i] == mtd)
+- {
+- while (not)
+- {
+- (*(not->remove))(mtd);
+- not = not->next;
+- }
+- mtd_table[i] = NULL;
+- up (&mtd_table_mutex);
+- MOD_DEC_USE_COUNT;
+- return 0;
++ if (mtd_table[mtd->index] != mtd) {
++ ret = -ENODEV;
++ } else if (mtd->usecount) {
++ printk(KERN_NOTICE "Removing MTD device #%d (%s) with use count %d\n",
++ mtd->index, mtd->name, mtd->usecount);
++ ret = -EBUSY;
++ } else {
++ struct list_head *this;
++
++ /* No need to get a refcount on the module containing
++ the notifier, since we hold the mtd_table_mutex */
++ list_for_each(this, &mtd_notifiers) {
++ struct mtd_notifier *not = list_entry(this, struct mtd_notifier, list);
++ not->remove(mtd);
+ }
++
++ mtd_table[mtd->index] = NULL;
++
++ module_put(THIS_MODULE);
++ ret = 0;
+ }
+
+ up(&mtd_table_mutex);
+- return 1;
++ return ret;
+ }
+
+ /**
+@@ -118,10 +137,9 @@
+
+ down(&mtd_table_mutex);
+
+- new->next = mtd_notifiers;
+- mtd_notifiers = new;
++ list_add(&new->list, &mtd_notifiers);
+
+- MOD_INC_USE_COUNT;
++ __module_get(THIS_MODULE);
+
+ for (i=0; i< MAX_MTD_DEVICES; i++)
+ if (mtd_table[i])
+@@ -142,34 +160,24 @@
+
+ int unregister_mtd_user (struct mtd_notifier *old)
+ {
+- struct mtd_notifier **prev = &mtd_notifiers;
+- struct mtd_notifier *cur;
+ int i;
+
+ down(&mtd_table_mutex);
+
+- while ((cur = *prev)) {
+- if (cur == old) {
+- *prev = cur->next;
+-
+- MOD_DEC_USE_COUNT;
++ module_put(THIS_MODULE);
+
+ for (i=0; i< MAX_MTD_DEVICES; i++)
+ if (mtd_table[i])
+ old->remove(mtd_table[i]);
+
++ list_del(&old->list);
+ up(&mtd_table_mutex);
+ return 0;
+- }
+- prev = &cur->next;
+- }
+- up(&mtd_table_mutex);
+- return 1;
+ }
+
+
+ /**
+- * __get_mtd_device - obtain a validated handle for an MTD device
++ * get_mtd_device - obtain a validated handle for an MTD device
+ * @mtd: last known address of the required MTD device
+ * @num: internal device number of the required MTD device
+ *
+@@ -177,11 +185,10 @@
+ * table, if any. Given an address and num == -1, search the device table
+ * for a device with that address and return if it's still present. Given
+ * both, return the num'th driver only if its address matches. Return NULL
+- * if not. get_mtd_device() increases the use count, but
+- * __get_mtd_device() doesn't - you should generally use get_mtd_device().
++ * if not.
+ */
+
+-struct mtd_info *__get_mtd_device(struct mtd_info *mtd, int num)
++struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num)
+ {
+ struct mtd_info *ret = NULL;
+ int i;
+@@ -198,10 +205,27 @@
+ ret = NULL;
+ }
+
++ if (ret && !try_module_get(ret->owner))
++ ret = NULL;
++
++ if (ret)
++ ret->usecount++;
++
+ up(&mtd_table_mutex);
+ return ret;
+ }
+
++void put_mtd_device(struct mtd_info *mtd)
++{
++ int c;
++
++ down(&mtd_table_mutex);
++ c = --mtd->usecount;
++ up(&mtd_table_mutex);
++ BUG_ON(c < 0);
++
++ module_put(mtd->owner);
++}
+
+ /* default_mtd_writev - default mtd writev method for MTD devices that
+ * dont implement their own
+@@ -265,7 +289,8 @@
+
+ EXPORT_SYMBOL(add_mtd_device);
+ EXPORT_SYMBOL(del_mtd_device);
+-EXPORT_SYMBOL(__get_mtd_device);
++EXPORT_SYMBOL(get_mtd_device);
++EXPORT_SYMBOL(put_mtd_device);
+ EXPORT_SYMBOL(register_mtd_user);
+ EXPORT_SYMBOL(unregister_mtd_user);
+ EXPORT_SYMBOL(default_mtd_writev);
+diff -Nurb linux-mips-2.4.27/drivers/mtd/mtdpart.c linux/drivers/mtd/mtdpart.c
+--- linux-mips-2.4.27/drivers/mtd/mtdpart.c 2003-02-26 01:53:49.000000000 +0100
++++ linux/drivers/mtd/mtdpart.c 2004-11-19 10:25:11.652237336 +0100
+@@ -5,7 +5,7 @@
+ *
+ * This code is GPL
+ *
+- * $Id$
++ * $Id$
+ *
+ * 02-21-2002 Thomas Gleixner <gleixner@autronix.de>
+ * added support for read_oob, write_oob
+@@ -16,10 +16,11 @@
+ #include <linux/kernel.h>
+ #include <linux/slab.h>
+ #include <linux/list.h>
+-
++#include <linux/config.h>
++#include <linux/kmod.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/partitions.h>
+-
++#include <linux/mtd/compatmac.h>
+
+ /* Our partition linked list */
+ static LIST_HEAD(mtd_partitions);
+@@ -54,8 +55,12 @@
+ len = 0;
+ else if (from + len > mtd->size)
+ len = mtd->size - from;
++ if (part->master->read_ecc == NULL)
+ return part->master->read (part->master, from + part->offset,
+ len, retlen, buf);
++ else
++ return part->master->read_ecc (part->master, from + part->offset,
++ len, retlen, buf, NULL, &mtd->oobinfo);
+ }
+
+ static int part_point (struct mtd_info *mtd, loff_t from, size_t len,
+@@ -78,9 +83,11 @@
+
+
+ static int part_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
+- size_t *retlen, u_char *buf, u_char *eccbuf, int oobsel)
++ size_t *retlen, u_char *buf, u_char *eccbuf, struct nand_oobinfo *oobsel)
+ {
+ struct mtd_part *part = PART(mtd);
++ if (oobsel == NULL)
++ oobsel = &mtd->oobinfo;
+ if (from >= mtd->size)
+ len = 0;
+ else if (from + len > mtd->size)
+@@ -113,7 +120,7 @@
+ size_t *retlen, u_char *buf)
+ {
+ struct mtd_part *part = PART(mtd);
+- return part->master->read_user_prot_reg (part->master, from,
++ return part->master->read_fact_prot_reg (part->master, from,
+ len, retlen, buf);
+ }
+
+@@ -127,17 +134,24 @@
+ len = 0;
+ else if (to + len > mtd->size)
+ len = mtd->size - to;
++ if (part->master->write_ecc == NULL)
+ return part->master->write (part->master, to + part->offset,
+ len, retlen, buf);
++ else
++ return part->master->write_ecc (part->master, to + part->offset,
++ len, retlen, buf, NULL, &mtd->oobinfo);
++
+ }
+
+ static int part_write_ecc (struct mtd_info *mtd, loff_t to, size_t len,
+ size_t *retlen, const u_char *buf,
+- u_char *eccbuf, int oobsel)
++ u_char *eccbuf, struct nand_oobinfo *oobsel)
+ {
+ struct mtd_part *part = PART(mtd);
+ if (!(mtd->flags & MTD_WRITEABLE))
+ return -EROFS;
++ if (oobsel == NULL)
++ oobsel = &mtd->oobinfo;
+ if (to >= mtd->size)
+ len = 0;
+ else if (to + len > mtd->size)
+@@ -174,25 +188,37 @@
+ struct mtd_part *part = PART(mtd);
+ if (!(mtd->flags & MTD_WRITEABLE))
+ return -EROFS;
++ if (part->master->writev_ecc == NULL)
+ return part->master->writev (part->master, vecs, count,
+ to + part->offset, retlen);
++ else
++ return part->master->writev_ecc (part->master, vecs, count,
++ to + part->offset, retlen,
++ NULL, &mtd->oobinfo);
+ }
+
+ static int part_readv (struct mtd_info *mtd, struct iovec *vecs,
+ unsigned long count, loff_t from, size_t *retlen)
+ {
+ struct mtd_part *part = PART(mtd);
++ if (part->master->readv_ecc == NULL)
+ return part->master->readv (part->master, vecs, count,
+ from + part->offset, retlen);
++ else
++ return part->master->readv_ecc (part->master, vecs, count,
++ from + part->offset, retlen,
++ NULL, &mtd->oobinfo);
+ }
+
+ static int part_writev_ecc (struct mtd_info *mtd, const struct iovec *vecs,
+ unsigned long count, loff_t to, size_t *retlen,
+- u_char *eccbuf, int oobsel)
++ u_char *eccbuf, struct nand_oobinfo *oobsel)
+ {
+ struct mtd_part *part = PART(mtd);
+ if (!(mtd->flags & MTD_WRITEABLE))
+ return -EROFS;
++ if (oobsel == NULL)
++ oobsel = &mtd->oobinfo;
+ return part->master->writev_ecc (part->master, vecs, count,
+ to + part->offset, retlen,
+ eccbuf, oobsel);
+@@ -200,9 +226,11 @@
+
+ static int part_readv_ecc (struct mtd_info *mtd, struct iovec *vecs,
+ unsigned long count, loff_t from, size_t *retlen,
+- u_char *eccbuf, int oobsel)
++ u_char *eccbuf, struct nand_oobinfo *oobsel)
+ {
+ struct mtd_part *part = PART(mtd);
++ if (oobsel == NULL)
++ oobsel = &mtd->oobinfo;
+ return part->master->readv_ecc (part->master, vecs, count,
+ from + part->offset, retlen,
+ eccbuf, oobsel);
+@@ -288,7 +316,7 @@
+ */
+
+ int add_mtd_partitions(struct mtd_info *master,
+- struct mtd_partition *parts,
++ const struct mtd_partition *parts,
+ int nbparts)
+ {
+ struct mtd_part *slave;
+@@ -321,7 +349,7 @@
+
+ slave->mtd.name = parts[i].name;
+ slave->mtd.bank_size = master->bank_size;
+- slave->mtd.module = master->module;
++ slave->mtd.owner = master->owner;
+
+ slave->mtd.read = part_read;
+ slave->mtd.write = part_write;
+@@ -452,6 +480,75 @@
+ EXPORT_SYMBOL(add_mtd_partitions);
+ EXPORT_SYMBOL(del_mtd_partitions);
+
++static spinlock_t part_parser_lock = SPIN_LOCK_UNLOCKED;
++static LIST_HEAD(part_parsers);
++
++struct mtd_part_parser *get_partition_parser(const char *name)
++{
++ struct list_head *this;
++ void *ret = NULL;
++ spin_lock(&part_parser_lock);
++
++ list_for_each(this, &part_parsers) {
++ struct mtd_part_parser *p = list_entry(this, struct mtd_part_parser, list);
++
++ if (!strcmp(p->name, name) && try_module_get(p->owner)) {
++ ret = p;
++ break;
++ }
++ }
++ spin_unlock(&part_parser_lock);
++
++ return ret;
++}
++
++int register_mtd_parser(struct mtd_part_parser *p)
++{
++ spin_lock(&part_parser_lock);
++ list_add(&p->list, &part_parsers);
++ spin_unlock(&part_parser_lock);
++
++ return 0;
++}
++
++int deregister_mtd_parser(struct mtd_part_parser *p)
++{
++ spin_lock(&part_parser_lock);
++ list_del(&p->list);
++ spin_unlock(&part_parser_lock);
++ return 0;
++}
++
++int parse_mtd_partitions(struct mtd_info *master, const char **types,
++ struct mtd_partition **pparts, unsigned long origin)
++{
++ struct mtd_part_parser *parser;
++ int ret = 0;
++
++ for ( ; ret <= 0 && *types; types++) {
++ parser = get_partition_parser(*types);
++#ifdef CONFIG_KMOD
++ if (!parser && !request_module("%s", *types))
++ parser = get_partition_parser(*types);
++#endif
++ if (!parser) {
++ printk(KERN_NOTICE "%s partition parsing not available\n",
++ *types);
++ continue;
++ }
++ ret = (*parser->parse_fn)(master, pparts, origin);
++ if (ret > 0) {
++ printk(KERN_NOTICE "%d %s partitions found on MTD device %s\n",
++ ret, parser->name, master->name);
++ }
++ put_partition_parser(parser);
++ }
++ return ret;
++}
++
++EXPORT_SYMBOL_GPL(parse_mtd_partitions);
++EXPORT_SYMBOL_GPL(register_mtd_parser);
++EXPORT_SYMBOL_GPL(deregister_mtd_parser);
+
+ MODULE_LICENSE("GPL");
+ MODULE_AUTHOR("Nicolas Pitre <nico@cam.org>");
+diff -Nurb linux-mips-2.4.27/drivers/mtd/nand/Config.in linux/drivers/mtd/nand/Config.in
+--- linux-mips-2.4.27/drivers/mtd/nand/Config.in 2003-02-26 01:53:50.000000000 +0100
++++ linux/drivers/mtd/nand/Config.in 2004-11-19 10:25:11.987186416 +0100
+@@ -1,6 +1,6 @@
+ # drivers/mtd/nand/Config.in
+
+-# $Id$
++# $Id$
+
+ mainmenu_option next_comment
+
+@@ -11,26 +11,27 @@
+ bool ' Verify NAND page writes' CONFIG_MTD_NAND_VERIFY_WRITE
+ fi
+
+-if [ "$CONFIG_ARM" = "y" -a "$CONFIG_ARCH_P720T" = "y" ]; then
+- dep_tristate ' NAND Flash device on SPIA board' CONFIG_MTD_NAND_SPIA $CONFIG_MTD_NAND
++if [ "$CONFIG_ARM" = "y" ]; then
++ dep_tristate ' NAND Flash device on SPIA board' CONFIG_MTD_NAND_SPIA $CONFIG_MTD_NAND $CONFIG_ARCH_P720T
++ dep_tristate ' NAND Flash device on TOTO board' CONFIG_MTD_NAND_TOTO $CONFIG_MTD_NAND $CONFIG_ARCH_OMAP
++ dep_tristate ' SmartMedia Card on AUTCPU12 board' CONFIG_MTD_NAND_AUTCPU12 $CONFIG_MTD_NAND $CONFIG_ARCH_AUTCPU12
++ dep_tristate ' NAND Flash device on EDP7312 board' CONFIG_MTD_NAND_EDB7312 $CONFIG_MTD_NAND $CONFIG_ARCH_EDB7312
+ fi
+
+-if [ "$CONFIG_ARCH_AUTCPU12" = "y" ]; then
+- dep_tristate ' SmartMedia Card on AUTCPU12 board' CONFIG_MTD_NAND_AUTCPU12 $CONFIG_MTD_NAND
+-fi
+-
+-if [ "$CONFIG_ARCH_EDB7312" = "y" ]; then
+- dep_tristate ' NAND Flash device on EDP7312 board' CONFIG_MTD_NAND_EDB7312 $CONFIG_MTD_NAND
+-fi
+-
+-if [ "$CONFIG_MTD_DOC2001" = "y" -o "$CONFIG_MTD_DOC2000" = "y" -o "$CONFIG_MTD_NAND" = "y" ]; then
++if [ "$CONFIG_MTD_DOC2001PLUS" = "y" -o "$CONFIG_MTD_DOC2001" = "y" -o "$CONFIG_MTD_DOC2000" = "y" -o "$CONFIG_MTD_NAND" = "y" ]; then
+ define_bool CONFIG_MTD_NAND_IDS y
++else
++ if [ "$CONFIG_MTD_DOC2001PLUS" = "m" -o "$CONFIG_MTD_DOC2001" = "m" -o "$CONFIG_MTD_DOC2000" = "m" -o "$CONFIG_MTD_NAND" = "m" ]; then
++ define_bool CONFIG_MTD_NAND_IDS m
++ fi
+ fi
+
+-if [ "$CONFIG_MTD_NAND_IDS" != "y" ]; then
+-if [ "$CONFIG_MTD_DOC2001" = "m" -o "$CONFIG_MTD_DOC2000" = "m" -o "$CONFIG_MTD_NAND" = "m" ]; then
+- define_bool CONFIG_MTD_NAND_IDS m
++if [ "$CONFIG_TOSHIBA_RBTX4925" = "y" ]; then
++ dep_tristate ' SmartMedia Card on Toshiba RBTX4925 reference board' CONFIG_MTD_NAND_TX4925NDFMC $CONFIG_MTD_NAND $CONFIG_TOSHIBA_RBTX4925_MPLEX_NAND
+ fi
++
++if [ "$CONFIG_TOSHIBA_RBTX4938" = "y" ]; then
++ dep_tristate ' NAND Flash device on Toshiba RBTX4938 reference board' CONFIG_MTD_NAND_TX4938NDFMC $CONFIG_MTD_NAND $CONFIG_TOSHIBA_RBTX4938_MPLEX_NAND
+ fi
+
+ endmenu
+diff -Nurb linux-mips-2.4.27/drivers/mtd/nand/Makefile linux/drivers/mtd/nand/Makefile
+--- linux-mips-2.4.27/drivers/mtd/nand/Makefile 2003-02-26 01:53:50.000000000 +0100
++++ linux/drivers/mtd/nand/Makefile 2004-11-19 10:25:11.989186112 +0100
+@@ -1,16 +1,20 @@
+ #
+ # linux/drivers/nand/Makefile
+ #
+-# $Id$
++# $Id$
+
++ifeq ($(PATCHLEVEL),4)
+ O_TARGET := nandlink.o
+-
+ export-objs := nand.o nand_ecc.o nand_ids.o
++endif
+
+ obj-$(CONFIG_MTD_NAND) += nand.o nand_ecc.o
+ obj-$(CONFIG_MTD_NAND_SPIA) += spia.o
++obj-$(CONFIG_MTD_NAND_TOTO) += toto.o
+ obj-$(CONFIG_MTD_NAND_AUTCPU12) += autcpu12.o
+ obj-$(CONFIG_MTD_NAND_EDB7312) += edb7312.o
++obj-$(CONFIG_MTD_NAND_TX4925NDFMC) += tx4925ndfmc.o
++obj-$(CONFIG_MTD_NAND_TX4938NDFMC) += tx4938ndfmc.o
+ obj-$(CONFIG_MTD_NAND_IDS) += nand_ids.o
+
+-include $(TOPDIR)/Rules.make
++-include $(TOPDIR)/Rules.make
+diff -Nurb linux-mips-2.4.27/drivers/mtd/nand/autcpu12.c linux/drivers/mtd/nand/autcpu12.c
+--- linux-mips-2.4.27/drivers/mtd/nand/autcpu12.c 2003-02-26 01:53:50.000000000 +0100
++++ linux/drivers/mtd/nand/autcpu12.c 2004-11-19 10:25:11.990185960 +0100
+@@ -4,9 +4,9 @@
+ * Copyright (c) 2002 Thomas Gleixner <tgxl@linutronix.de>
+ *
+ * Derived from drivers/mtd/spia.c
+- * Copyright (C) 2000 Steven J. Hill (sjhill@cotw.com)
++ * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
+ *
+- * $Id$
++ * $Id$
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+@@ -25,10 +25,10 @@
+ * added page_cache
+ *
+ * 10-06-2002 TG 128K card support added
+- *
+ */
+
+ #include <linux/slab.h>
++#include <linux/init.h>
+ #include <linux/module.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/nand.h>
+@@ -70,6 +70,7 @@
+ /*
+ * Define partitions for flash devices
+ */
++extern struct nand_oobinfo jffs2_oobinfo;
+
+ static struct mtd_partition partition_info16k[] = {
+ { name: "AUTCPU12 flash partition 1",
+@@ -95,7 +96,7 @@
+ size: 16 * SZ_1M },
+ { name: "AUTCPU12 flash partition 2",
+ offset: 16 * SZ_1M,
+- size: 48 * SZ_1M},
++ size: 48 * SZ_1M },
+ };
+
+ static struct mtd_partition partition_info128k[] = {
+@@ -104,7 +105,7 @@
+ size: 16 * SZ_1M },
+ { name: "AUTCPU12 flash partition 2",
+ offset: 16 * SZ_1M,
+- size: 112 * SZ_1M},
++ size: 112 * SZ_1M },
+ };
+
+ #define NUM_PARTITIONS16K 2
+@@ -114,7 +115,7 @@
+ /*
+ * hardware specific access to control-lines
+ */
+-void autcpu12_hwcontrol(int cmd)
++static void autcpu12_hwcontrol(struct mtd_info *mtd, int cmd)
+ {
+
+ switch(cmd){
+@@ -133,7 +134,7 @@
+ /*
+ * read device ready pin
+ */
+-int autcpu12_device_ready(void)
++int autcpu12_device_ready(struct mtd_info *mtd)
+ {
+
+ return ( (*(volatile unsigned char *) (autcpu12_io_base + autcpu12_pedr)) & AUTCPU12_SMC_RDY) ? 1 : 0;
+@@ -184,7 +185,7 @@
+ this->eccmode = NAND_ECC_SOFT;
+
+ /* Scan to find existance of the device */
+- if (nand_scan (autcpu12_mtd)) {
++ if (nand_scan (autcpu12_mtd, 1)) {
+ err = -ENXIO;
+ goto out_ior;
+ }
+@@ -197,15 +198,6 @@
+ goto out_ior;
+ }
+
+- /* Allocate memory for internal data buffer */
+- this->data_cache = kmalloc (sizeof(u_char) * (autcpu12_mtd->oobblock + autcpu12_mtd->oobsize), GFP_KERNEL);
+- if (!this->data_cache) {
+- printk ("Unable to allocate NAND data cache for AUTCPU12.\n");
+- err = -ENOMEM;
+- goto out_buf;
+- }
+- this->cache_page = -1;
+-
+ /* Register the partitions */
+ switch(autcpu12_mtd->size){
+ case SZ_16M: add_mtd_partitions(autcpu12_mtd, partition_info16k, NUM_PARTITIONS16K); break;
+@@ -215,13 +207,11 @@
+ default: {
+ printk ("Unsupported SmartMedia device\n");
+ err = -ENXIO;
+- goto out_cac;
++ goto out_buf;
+ }
+ }
+ goto out;
+
+-out_cac:
+- kfree (this->data_cache);
+ out_buf:
+ kfree (this->data_buf);
+ out_ior:
+@@ -250,7 +240,6 @@
+
+ /* Free internal data buffers */
+ kfree (this->data_buf);
+- kfree (this->data_cache);
+
+ /* unmap physical adress */
+ iounmap((void *)autcpu12_fio_base);
+diff -Nurb linux-mips-2.4.27/drivers/mtd/nand/diskonchip.c linux/drivers/mtd/nand/diskonchip.c
+--- linux-mips-2.4.27/drivers/mtd/nand/diskonchip.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/mtd/nand/diskonchip.c 2004-11-19 10:25:11.992185656 +0100
+@@ -0,0 +1,534 @@
++/*
++ * drivers/mtd/nand/diskonchip.c
++ *
++ * (C) 2003 Red Hat, Inc.
++ *
++ * Author: David Woodhouse <dwmw2@infradead.org>
++ *
++ * Interface to generic NAND code for M-Systems DiskOnChip devices
++ *
++ * $Id$
++ */
++
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/sched.h>
++#include <linux/delay.h>
++#include <asm/io.h>
++
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/nand.h>
++#include <linux/mtd/doc2000.h>
++#include <linux/mtd/compatmac.h>
++
++struct doc_priv {
++ unsigned long virtadr;
++ unsigned long physadr;
++ u_char ChipID;
++ u_char CDSNControl;
++ int chips_per_floor; /* The number of chips detected on each floor */
++ int curfloor;
++ int curchip;
++};
++
++#define DoC_is_Millennium(doc) ((doc)->ChipID == DOC_ChipID_DocMil)
++#define DoC_is_2000(doc) ((doc)->ChipID == DOC_ChipID_Doc2k)
++
++static void doc200x_hwcontrol(struct mtd_info *mtd, int cmd);
++static void doc200x_select_chip(struct mtd_info *mtd, int chip);
++
++static int debug=0;
++MODULE_PARM(debug, "i");
++
++static int try_dword=1;
++MODULE_PARM(try_dword, "i");
++
++static void DoC_Delay(struct doc_priv *doc, unsigned short cycles)
++{
++ volatile char dummy;
++ int i;
++
++ for (i = 0; i < cycles; i++) {
++ if (DoC_is_Millennium(doc))
++ dummy = ReadDOC(doc->virtadr, NOP);
++ else
++ dummy = ReadDOC(doc->virtadr, DOCStatus);
++ }
++
++}
++/* DOC_WaitReady: Wait for RDY line to be asserted by the flash chip */
++static int _DoC_WaitReady(struct doc_priv *doc)
++{
++ unsigned long docptr = doc->virtadr;
++ unsigned long timeo = jiffies + (HZ * 10);
++
++ if(debug) printk("_DoC_WaitReady...\n");
++ /* Out-of-line routine to wait for chip response */
++ while (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B)) {
++ if (time_after(jiffies, timeo)) {
++ printk("_DoC_WaitReady timed out.\n");
++ return -EIO;
++ }
++ udelay(1);
++ cond_resched();
++ }
++
++ return 0;
++}
++
++static inline int DoC_WaitReady(struct doc_priv *doc)
++{
++ unsigned long docptr = doc->virtadr;
++ int ret = 0;
++
++ DoC_Delay(doc, 4);
++
++ if (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B))
++ /* Call the out-of-line routine to wait */
++ ret = _DoC_WaitReady(doc);
++
++ DoC_Delay(doc, 2);
++ if(debug) printk("DoC_WaitReady OK\n");
++ return ret;
++}
++
++static void doc2000_write_byte(struct mtd_info *mtd, u_char datum)
++{
++ struct nand_chip *this = mtd->priv;
++ struct doc_priv *doc = (void *)this->priv;
++ unsigned long docptr = doc->virtadr;
++
++ if(debug)printk("write_byte %02x\n", datum);
++ WriteDOC(datum, docptr, CDSNSlowIO);
++ WriteDOC(datum, docptr, 2k_CDSN_IO);
++}
++
++static u_char doc2000_read_byte(struct mtd_info *mtd)
++{
++ struct nand_chip *this = mtd->priv;
++ struct doc_priv *doc = (void *)this->priv;
++ unsigned long docptr = doc->virtadr;
++
++ ReadDOC(docptr, CDSNSlowIO);
++ u_char ret = ReadDOC(docptr, 2k_CDSN_IO);
++ if (debug) printk("read_byte returns %02x\n", ret);
++ return ret;
++}
++static void doc2000_writebuf(struct mtd_info *mtd,
++ const u_char *buf, int len)
++{
++ struct nand_chip *this = mtd->priv;
++ struct doc_priv *doc = (void *)this->priv;
++ unsigned long docptr = doc->virtadr;
++ int i;
++ if (debug)printk("writebuf of %d bytes: ", len);
++ for (i=0; i < len; i++) {
++ WriteDOC_(buf[i], docptr, DoC_2k_CDSN_IO + i);
++ if (debug && i < 16)
++ printk("%02x ", buf[i]);
++ }
++ if (debug) printk("\n");
++}
++
++static void doc2000_readbuf(struct mtd_info *mtd,
++ u_char *buf, int len)
++{
++ struct nand_chip *this = mtd->priv;
++ struct doc_priv *doc = (void *)this->priv;
++ unsigned long docptr = doc->virtadr;
++ int i;
++
++ if (debug)printk("readbuf of %d bytes: ", len);
++
++ for (i=0; i < len; i++) {
++ buf[i] = ReadDOC(docptr, 2k_CDSN_IO + i);
++ }
++}
++
++static void doc2000_readbuf_dword(struct mtd_info *mtd,
++ u_char *buf, int len)
++{
++ struct nand_chip *this = mtd->priv;
++ struct doc_priv *doc = (void *)this->priv;
++ unsigned long docptr = doc->virtadr;
++ int i;
++
++ if (debug) printk("readbuf_dword of %d bytes: ", len);
++
++ if (unlikely((((unsigned long)buf)|len) & 3)) {
++ for (i=0; i < len; i++) {
++ *(uint8_t *)(&buf[i]) = ReadDOC(docptr, 2k_CDSN_IO + i);
++ }
++ } else {
++ for (i=0; i < len; i+=4) {
++ *(uint32_t*)(&buf[i]) = readl(docptr + DoC_2k_CDSN_IO + i);
++ }
++ }
++}
++
++static int doc2000_verifybuf(struct mtd_info *mtd,
++ const u_char *buf, int len)
++{
++ struct nand_chip *this = mtd->priv;
++ struct doc_priv *doc = (void *)this->priv;
++ unsigned long docptr = doc->virtadr;
++ int i;
++
++ for (i=0; i < len; i++)
++ if (buf[i] != ReadDOC(docptr, 2k_CDSN_IO))
++ return i;
++ return 0;
++}
++
++static uint16_t doc200x_ident_chip(struct mtd_info *mtd, int nr)
++{
++ struct nand_chip *this = mtd->priv;
++ struct doc_priv *doc = (void *)this->priv;
++ uint16_t ret;
++
++ doc200x_select_chip(mtd, nr);
++ doc200x_hwcontrol(mtd, NAND_CTL_SETCLE);
++ this->write_byte(mtd, NAND_CMD_READID);
++ doc200x_hwcontrol(mtd, NAND_CTL_CLRCLE);
++ doc200x_hwcontrol(mtd, NAND_CTL_SETALE);
++ this->write_byte(mtd, 0);
++ doc200x_hwcontrol(mtd, NAND_CTL_CLRALE);
++
++ ret = this->read_byte(mtd) << 8;
++ ret |= this->read_byte(mtd);
++
++ if (doc->ChipID == DOC_ChipID_Doc2k && try_dword && !nr) {
++ /* First chip probe. See if we get same results by 32-bit access */
++ union {
++ uint32_t dword;
++ uint8_t byte[4];
++ } ident;
++ struct nand_chip *this = mtd->priv;
++ struct doc_priv *doc = (void *)this->priv;
++ unsigned long docptr = doc->virtadr;
++
++ doc200x_hwcontrol(mtd, NAND_CTL_SETCLE);
++ doc2000_write_byte(mtd, NAND_CMD_READID);
++ doc200x_hwcontrol(mtd, NAND_CTL_CLRCLE);
++ doc200x_hwcontrol(mtd, NAND_CTL_SETALE);
++ doc2000_write_byte(mtd, 0);
++ doc200x_hwcontrol(mtd, NAND_CTL_CLRALE);
++
++ ident.dword = readl(docptr + DoC_2k_CDSN_IO);
++ if (((ident.byte[0] << 8) | ident.byte[1]) == ret) {
++ printk(KERN_INFO "DiskOnChip 2000 responds to DWORD access\n");
++ this->read_buf = &doc2000_readbuf_dword;
++ }
++ }
++
++ return ret;
++}
++
++static void doc2000_count_chips(struct mtd_info *mtd)
++{
++ struct nand_chip *this = mtd->priv;
++ struct doc_priv *doc = (void *)this->priv;
++ uint16_t mfrid;
++ int i;
++
++ /* Max 4 chips per floor on DiskOnChip 2000 */
++ doc->chips_per_floor = 4;
++
++ /* Find out what the first chip is */
++ mfrid = doc200x_ident_chip(mtd, 0);
++
++ /* Find how many chips in each floor. */
++ for (i = 1; i < 4; i++) {
++ if (doc200x_ident_chip(mtd, i) != mfrid)
++ break;
++ }
++ doc->chips_per_floor = i;
++}
++
++static int doc200x_wait(struct mtd_info *mtd, struct nand_chip *this, int state)
++{
++ struct doc_priv *doc = (void *)this->priv;
++
++ int status;
++
++ DoC_WaitReady(doc);
++ this->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1);
++ DoC_WaitReady(doc);
++ status = (int)this->read_byte(mtd);
++
++ return status;
++}
++
++static void doc2001_write_byte(struct mtd_info *mtd, u_char datum)
++{
++ struct nand_chip *this = mtd->priv;
++ struct doc_priv *doc = (void *)this->priv;
++ unsigned long docptr = doc->virtadr;
++
++ WriteDOC(datum, docptr, CDSNSlowIO);
++ WriteDOC(datum, docptr, Mil_CDSN_IO);
++ WriteDOC(datum, docptr, WritePipeTerm);
++}
++
++static u_char doc2001_read_byte(struct mtd_info *mtd)
++{
++ struct nand_chip *this = mtd->priv;
++ struct doc_priv *doc = (void *)this->priv;
++ unsigned long docptr = doc->virtadr;
++
++ ReadDOC(docptr, CDSNSlowIO);
++ /* 11.4.5 -- delay twice to allow extended length cycle */
++ DoC_Delay(doc, 2);
++ ReadDOC(docptr, ReadPipeInit);
++ return ReadDOC(docptr, Mil_CDSN_IO);
++}
++
++static void doc2001_writebuf(struct mtd_info *mtd,
++ const u_char *buf, int len)
++{
++ struct nand_chip *this = mtd->priv;
++ struct doc_priv *doc = (void *)this->priv;
++ unsigned long docptr = doc->virtadr;
++ int i;
++
++ for (i=0; i < len; i++)
++ WriteDOC_(buf[i], docptr, DoC_Mil_CDSN_IO + i);
++ /* Terminate write pipeline */
++ WriteDOC(0x00, docptr, WritePipeTerm);
++}
++
++static void doc2001_readbuf(struct mtd_info *mtd,
++ u_char *buf, int len)
++{
++ struct nand_chip *this = mtd->priv;
++ struct doc_priv *doc = (void *)this->priv;
++ unsigned long docptr = doc->virtadr;
++ int i;
++
++ /* Start read pipeline */
++ ReadDOC(docptr, ReadPipeInit);
++
++ for (i=0; i < len-1; i++)
++ buf[i] = ReadDOC(docptr, Mil_CDSN_IO);
++
++ /* Terminate read pipeline */
++ buf[i] = ReadDOC(docptr, LastDataRead);
++}
++static int doc2001_verifybuf(struct mtd_info *mtd,
++ const u_char *buf, int len)
++{
++ struct nand_chip *this = mtd->priv;
++ struct doc_priv *doc = (void *)this->priv;
++ unsigned long docptr = doc->virtadr;
++ int i;
++
++ /* Start read pipeline */
++ ReadDOC(docptr, ReadPipeInit);
++
++ for (i=0; i < len-1; i++)
++ if (buf[i] != ReadDOC(docptr, Mil_CDSN_IO)) {
++ ReadDOC(docptr, LastDataRead);
++ return i;
++ }
++ if (buf[i] != ReadDOC(docptr, LastDataRead))
++ return i;
++ return 0;
++}
++
++static void doc200x_select_chip(struct mtd_info *mtd, int chip)
++{
++ struct nand_chip *this = mtd->priv;
++ struct doc_priv *doc = (void *)this->priv;
++ unsigned long docptr = doc->virtadr;
++ int floor = 0;
++
++ /* 11.4.4 -- deassert CE before changing chip */
++ doc200x_hwcontrol(mtd, NAND_CTL_CLRNCE);
++
++ if(debug)printk("select chip (%d)\n", chip);
++
++ if (chip == -1)
++ return;
++
++ floor = chip / doc->chips_per_floor;
++ chip -= (floor * doc->chips_per_floor);
++
++ WriteDOC(floor, docptr, FloorSelect);
++ WriteDOC(chip, docptr, CDSNDeviceSelect);
++
++ doc200x_hwcontrol(mtd, NAND_CTL_SETNCE);
++
++ doc->curchip = chip;
++ doc->curfloor = floor;
++}
++
++static void doc200x_hwcontrol(struct mtd_info *mtd, int cmd)
++{
++ struct nand_chip *this = mtd->priv;
++ struct doc_priv *doc = (void *)this->priv;
++ unsigned long docptr = doc->virtadr;
++
++ switch(cmd) {
++ case NAND_CTL_SETNCE:
++ doc->CDSNControl |= CDSN_CTRL_CE;
++ break;
++ case NAND_CTL_CLRNCE:
++ doc->CDSNControl &= ~CDSN_CTRL_CE;
++ break;
++ case NAND_CTL_SETCLE:
++ doc->CDSNControl |= CDSN_CTRL_CLE;
++ break;
++ case NAND_CTL_CLRCLE:
++ doc->CDSNControl &= ~CDSN_CTRL_CLE;
++ break;
++ case NAND_CTL_SETALE:
++ doc->CDSNControl |= CDSN_CTRL_ALE;
++ break;
++ case NAND_CTL_CLRALE:
++ doc->CDSNControl &= ~CDSN_CTRL_ALE;
++ break;
++ case NAND_CTL_SETWP:
++ doc->CDSNControl |= CDSN_CTRL_WP;
++ break;
++ case NAND_CTL_CLRWP:
++ doc->CDSNControl &= ~CDSN_CTRL_WP;
++ break;
++ }
++ if (debug)printk("hwcontrol(%d): %02x\n", cmd, doc->CDSNControl);
++ WriteDOC(doc->CDSNControl, docptr, CDSNControl);
++ /* 11.4.3 -- 4 NOPs after CSDNControl write */
++ DoC_Delay(doc, 4);
++}
++
++static int doc200x_dev_ready(struct mtd_info *mtd)
++{
++ struct nand_chip *this = mtd->priv;
++ struct doc_priv *doc = (void *)this->priv;
++ unsigned long docptr = doc->virtadr;
++
++ /* 11.4.2 -- must NOP four times before checking FR/B# */
++ DoC_Delay(doc, 4);
++ if (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B)) {
++ if(debug)
++ printk("not ready\n");
++ return 0;
++ }
++ /* 11.4.2 -- Must NOP twice if it's ready */
++ DoC_Delay(doc, 2);
++ if (debug)printk("was ready\n");
++ return 1;
++}
++
++static int doc200x_block_bad(struct mtd_info *mtd, unsigned long block)
++{
++ /* FIXME: Look it up in the BBT */
++ return 0;
++}
++
++struct doc_priv mydoc = {
++ .physadr = 0xd4000,
++ .curfloor = -1,
++ .curchip = -1,
++};
++
++u_char mydatabuf[528];
++
++struct nand_chip mynand = {
++ .priv = (void *)&mydoc,
++ .select_chip = doc200x_select_chip,
++ .hwcontrol = doc200x_hwcontrol,
++ .dev_ready = doc200x_dev_ready,
++ .waitfunc = doc200x_wait,
++ .block_bad = doc200x_block_bad,
++ .eccmode = NAND_ECC_SOFT,
++ .data_buf = mydatabuf,
++};
++
++struct mtd_info mymtd = {
++ .priv = (void *)&mynand,
++ .owner = THIS_MODULE,
++};
++
++int __init init_nanddoc(void)
++{
++ mydoc.virtadr = (unsigned long)ioremap(mydoc.physadr, DOC_IOREMAP_LEN);
++ int nrchips = 1;
++ char *name;
++
++ WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_RESET,
++ mydoc.virtadr, DOCControl);
++ WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_RESET,
++ mydoc.virtadr, DOCControl);
++
++ WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_NORMAL,
++ mydoc.virtadr, DOCControl);
++ WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_NORMAL,
++ mydoc.virtadr, DOCControl);
++
++ mydoc.ChipID = ReadDOC(mydoc.virtadr, ChipID);
++
++ switch(mydoc.ChipID) {
++ case DOC_ChipID_DocMil:
++ mynand.write_byte = doc2001_write_byte;
++ mynand.read_byte = doc2001_read_byte;
++ mynand.write_buf = doc2001_writebuf;
++ mynand.read_buf = doc2001_readbuf;
++ mynand.verify_buf = doc2001_verifybuf;
++
++ ReadDOC(mydoc.virtadr, ChipID);
++ ReadDOC(mydoc.virtadr, ChipID);
++ if (ReadDOC(mydoc.virtadr, ChipID) != DOC_ChipID_DocMil) {
++ /* It's not a Millennium; it's one of the newer
++ DiskOnChip 2000 units with a similar ASIC.
++ Treat it like a Millennium, except that it
++ can have multiple chips. */
++ doc2000_count_chips(&mymtd);
++ nrchips = 4 * mydoc.chips_per_floor;
++ name = "DiskOnChip 2000 (INFTL Model)";
++ } else {
++ /* Bog-standard Millennium */
++ mydoc.chips_per_floor = 1;
++ nrchips = 1;
++ name = "DiskOnChip Millennium";
++ }
++ break;
++
++ case DOC_ChipID_Doc2k:
++ mynand.write_byte = doc2000_write_byte;
++ mynand.read_byte = doc2000_read_byte;
++ mynand.write_buf = doc2000_writebuf;
++ mynand.read_buf = doc2000_readbuf;
++ mynand.verify_buf = doc2000_verifybuf;
++
++ doc2000_count_chips(&mymtd);
++ nrchips = 4 * mydoc.chips_per_floor;
++ name = "DiskOnChip 2000 (NFTL Model)";
++ mydoc.CDSNControl |= CDSN_CTRL_FLASH_IO;
++
++ break;
++
++ default:
++ return -EIO;
++ }
++ if (nand_scan(&mymtd, nrchips)) {
++ iounmap((void *)mydoc.virtadr);
++ return -EIO;
++ }
++ mymtd.name = name;
++ add_mtd_device(&mymtd);
++
++ return 0;
++}
++
++void __exit cleanup_nanddoc(void)
++{
++ del_mtd_device(&mymtd);
++ iounmap((void *)mydoc.virtadr);
++}
++
++module_init(init_nanddoc);
++module_exit(cleanup_nanddoc);
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
++MODULE_DESCRIPTION("M-Systems DiskOnChip 2000 and Millennium device driver\n");
+diff -Nurb linux-mips-2.4.27/drivers/mtd/nand/edb7312.c linux/drivers/mtd/nand/edb7312.c
+--- linux-mips-2.4.27/drivers/mtd/nand/edb7312.c 2003-02-26 01:53:50.000000000 +0100
++++ linux/drivers/mtd/nand/edb7312.c 2004-11-19 10:25:11.997184896 +0100
+@@ -6,7 +6,7 @@
+ * Derived from drivers/mtd/nand/autcpu12.c
+ * Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de)
+ *
+- * $Id$
++ * $Id$
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+@@ -20,6 +20,7 @@
+
+ #include <linux/slab.h>
+ #include <linux/module.h>
++#include <linux/init.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/nand.h>
+ #include <linux/mtd/partitions.h>
+@@ -77,16 +78,13 @@
+ };
+ #define NUM_PARTITIONS 1
+
+-extern int parse_cmdline_partitions(struct mtd_info *master,
+- struct mtd_partition **pparts,
+- const char *mtd_id);
+ #endif
+
+
+ /*
+ * hardware specific access to control-lines
+ */
+-static void ep7312_hwcontrol(int cmd)
++static void ep7312_hwcontrol(struct mtd_info *mtd, int cmd)
+ {
+ switch(cmd) {
+
+@@ -116,10 +114,13 @@
+ /*
+ * read device ready pin
+ */
+-static int ep7312_device_ready(void)
++static int ep7312_device_ready(struct mtd_info *mtd)
+ {
+ return 1;
+ }
++#ifdef CONFIG_MTD_PARTITIONS
++const char *part_probes[] = { "cmdlinepart", NULL };
++#endif
+
+ /*
+ * Main initialization routine
+@@ -174,7 +175,7 @@
+ this->chip_delay = 15;
+
+ /* Scan to find existence of the device */
+- if (nand_scan (ep7312_mtd)) {
++ if (nand_scan (ep7312_mtd, 1)) {
+ iounmap((void *)ep7312_fio_base);
+ kfree (ep7312_mtd);
+ return -ENXIO;
+@@ -189,27 +190,16 @@
+ return -ENOMEM;
+ }
+
+- /* Allocate memory for internal data buffer */
+- this->data_cache = kmalloc (sizeof(u_char) * (ep7312_mtd->oobblock + ep7312_mtd->oobsize), GFP_KERNEL);
+- if (!this->data_cache) {
+- printk("Unable to allocate NAND data cache for EDB7312.\n");
+- kfree (this->data_buf);
+- iounmap((void *)ep7312_fio_base);
+- kfree (ep7312_mtd);
+- return -ENOMEM;
+- }
+- this->cache_page = -1;
+-
+-#ifdef CONFIG_MTD_CMDLINE_PARTS
+- mtd_parts_nb = parse_cmdline_partitions(ep7312_mtd, &mtd_parts,
+- "edb7312-nand");
++#ifdef CONFIG_PARTITIONS
++ ep7312_mtd->name = "edb7312-nand";
++ mtd_parts_nb = parse_mtd_partitions(ep7312_mtd, part_probes,
++ &mtd_parts, 0);
+ if (mtd_parts_nb > 0)
+ part_type = "command line";
+ else
+ mtd_parts_nb = 0;
+ #endif
+- if (mtd_parts_nb == 0)
+- {
++ if (mtd_parts_nb == 0) {
+ mtd_parts = partition_info;
+ mtd_parts_nb = NUM_PARTITIONS;
+ part_type = "static";
+@@ -236,7 +226,6 @@
+
+ /* Free internal data buffer */
+ kfree (this->data_buf);
+- kfree (this->data_cache);
+
+ /* Free the MTD device structure */
+ kfree (ep7312_mtd);
+diff -Nurb linux-mips-2.4.27/drivers/mtd/nand/nand.c linux/drivers/mtd/nand/nand.c
+--- linux-mips-2.4.27/drivers/mtd/nand/nand.c 2003-02-26 01:53:50.000000000 +0100
++++ linux/drivers/mtd/nand/nand.c 2004-11-19 10:25:12.010182920 +0100
+@@ -8,7 +8,7 @@
+ * Additional technical information is available on
+ * http://www.linux-mtd.infradead.org/tech/nand.html
+ *
+- * Copyright (C) 2000 Steven J. Hill (sjhill@cotw.com)
++ * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
+ * 2002 Thomas Gleixner (tglx@linutronix.de)
+ *
+ * 10-29-2001 Thomas Gleixner (tglx@linutronix.de)
+@@ -112,10 +112,27 @@
+ * for mtd->read_ecc / mtd->write_ecc
+ * some minor cleanups
+ *
+- * 12-05-2000 tglx: Dave Ellis (DGE@sixnetio) provided the fix for
++ * 12-05-2002 tglx: Dave Ellis (DGE@sixnetio) provided the fix for
+ * WRITE_VERIFY long time ago. Thanks for remembering me.
+ *
+- * $Id$
++ * 02-14-2003 tglx: Reject non page aligned writes
++ * Fixed ecc select in nand_write_page to match semantics.
++ *
++ * 02-18-2003 tglx: Changed oobsel to pointer. Added a default oob-selector
++ *
++ * 02-18-2003 tglx: Implemented oobsel again. Now it uses a pointer to
++ + a structure, which will be supplied by a filesystem driver
++ * If NULL is given, then the defaults (none or defaults
++ * supplied by ioctl (MEMSETOOBSEL) are used.
++ * For partitions the partition defaults are used (mtdpart.c)
++ *
++ * 06-04-2003 tglx: fix compile errors and fix write verify problem for
++ * some chips, which need either a delay between the readback
++ * and the next write command or have the CE removed. The
++ * CE disable/enable is much faster than a 20us delay and
++ * it should work on all available chips.
++ *
++ * $Id$
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+@@ -130,102 +147,151 @@
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/nand.h>
+ #include <linux/mtd/nand_ecc.h>
++#include <linux/mtd/compatmac.h>
+ #include <linux/interrupt.h>
+ #include <asm/io.h>
+
+ /*
+- * Macros for low-level register control
+- */
+-#define nand_select() this->hwcontrol(NAND_CTL_SETNCE);
+-
+-#define nand_deselect() this->hwcontrol(NAND_CTL_CLRNCE);
+-
+-/*
+- * out of band configuration for different filesystems
+- */
+-static int oobconfigs[][6] = {
+- { 0,0,0,0,0,0},
+-
+- { NAND_JFFS2_OOB_ECCPOS0, NAND_JFFS2_OOB_ECCPOS1, NAND_JFFS2_OOB_ECCPOS2,
+- NAND_JFFS2_OOB_ECCPOS3, NAND_JFFS2_OOB_ECCPOS4, NAND_JFFS2_OOB_ECCPOS5 },
+-
+- { NAND_YAFFS_OOB_ECCPOS0, NAND_YAFFS_OOB_ECCPOS1, NAND_YAFFS_OOB_ECCPOS2,
+- NAND_YAFFS_OOB_ECCPOS3, NAND_YAFFS_OOB_ECCPOS4, NAND_YAFFS_OOB_ECCPOS5 }
+-};
+-
+-/*
+ * NAND low-level MTD interface functions
+ */
++static void nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len);
++static void nand_read_buf(struct mtd_info *mtd, u_char *buf, int len);
++static int nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len);
++
+ static int nand_read (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf);
+ static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
+- size_t * retlen, u_char * buf, u_char * eccbuf, int oobsel);
++ size_t * retlen, u_char * buf, u_char * eccbuf, struct nand_oobinfo *oobsel);
+ static int nand_read_oob (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf);
+ static int nand_write (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char * buf);
+ static int nand_write_ecc (struct mtd_info *mtd, loff_t to, size_t len,
+- size_t * retlen, const u_char * buf, u_char * eccbuf, int oobsel);
++ size_t * retlen, const u_char * buf, u_char * eccbuf, struct nand_oobinfo *oobsel);
+ static int nand_write_oob (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char *buf);
+ static int nand_writev (struct mtd_info *mtd, const struct iovec *vecs,
+ unsigned long count, loff_t to, size_t * retlen);
+ static int nand_writev_ecc (struct mtd_info *mtd, const struct iovec *vecs,
+- unsigned long count, loff_t to, size_t * retlen, u_char *eccbuf, int oobsel);
++ unsigned long count, loff_t to, size_t * retlen, u_char *eccbuf, struct nand_oobinfo *oobsel);
+ static int nand_erase (struct mtd_info *mtd, struct erase_info *instr);
+ static void nand_sync (struct mtd_info *mtd);
+-static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int page, int col,
+- int last, u_char *oob_buf, int oobsel);
++static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int page, u_char *oob_buf, struct nand_oobinfo *oobsel);
++
++static u_char nand_read_byte(struct mtd_info *mtd)
++{
++ struct nand_chip *this = mtd->priv;
++ return readb(this->IO_ADDR_R);
++}
++
++static void nand_write_byte(struct mtd_info *mtd, u_char byte)
++{
++ struct nand_chip *this = mtd->priv;
++ writeb(byte, this->IO_ADDR_W);
++}
++
++static void nand_select_chip(struct mtd_info *mtd, int chip)
++{
++ struct nand_chip *this = mtd->priv;
++ switch(chip) {
++ case -1:
++ this->hwcontrol(mtd, NAND_CTL_CLRNCE);
++ break;
++ case 0:
++ this->hwcontrol(mtd, NAND_CTL_SETNCE);
++ break;
++
++ default:
++ BUG();
++ }
++}
++
++static void nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
++{
++ int i;
++ struct nand_chip *this = mtd->priv;
++
++ for (i=0; i<len; i++)
++ writeb(buf[i], this->IO_ADDR_W);
++}
++
++static void nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
++{
++ int i;
++ struct nand_chip *this = mtd->priv;
++
++ for (i=0; i<len; i++)
++ buf[i] = readb(this->IO_ADDR_R);
++}
++
++static int nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len)
++{
++ int i;
++ struct nand_chip *this = mtd->priv;
++
++ for (i=0; i<len; i++)
++ if (buf[i] != readb(this->IO_ADDR_R))
++ return i;
++
++ return 0;
++}
++
++/* Appropriate chip should already be selected */
++static int nand_block_bad(struct mtd_info *mtd, unsigned long page)
++{
++ struct nand_chip *this = mtd->priv;
++
++ this->cmdfunc (mtd, NAND_CMD_READOOB, NAND_BADBLOCK_POS, page);
++ if (this->read_byte(mtd) != 0xff)
++ return 1;
++
++ return 0;
++}
++
+ /*
+ * Send command to NAND device
+ */
+ static void nand_command (struct mtd_info *mtd, unsigned command, int column, int page_addr)
+ {
+ register struct nand_chip *this = mtd->priv;
+- register unsigned long NAND_IO_ADDR = this->IO_ADDR_W;
+
+ /* Begin command latch cycle */
+- this->hwcontrol (NAND_CTL_SETCLE);
++ this->hwcontrol(mtd, NAND_CTL_SETCLE);
+ /*
+ * Write out the command to the device.
+ */
+- if (command != NAND_CMD_SEQIN)
+- writeb (command, NAND_IO_ADDR);
+- else {
+- if (mtd->oobblock == 256 && column >= 256) {
+- column -= 256;
+- writeb (NAND_CMD_READOOB, NAND_IO_ADDR);
+- writeb (NAND_CMD_SEQIN, NAND_IO_ADDR);
+- } else if (mtd->oobblock == 512 && column >= 256) {
+- if (column < 512) {
+- column -= 256;
+- writeb (NAND_CMD_READ1, NAND_IO_ADDR);
+- writeb (NAND_CMD_SEQIN, NAND_IO_ADDR);
+- } else {
+- column -= 512;
+- writeb (NAND_CMD_READOOB, NAND_IO_ADDR);
+- writeb (NAND_CMD_SEQIN, NAND_IO_ADDR);
+- }
++ if (command == NAND_CMD_SEQIN) {
++ int readcmd;
++
++ if (column >= mtd->oobblock) {
++ /* OOB area */
++ column -= mtd->oobblock;
++ readcmd = NAND_CMD_READOOB;
++ } else if (column < 256) {
++ /* First 256 bytes --> READ0 */
++ readcmd = NAND_CMD_READ0;
+ } else {
+- writeb (NAND_CMD_READ0, NAND_IO_ADDR);
+- writeb (NAND_CMD_SEQIN, NAND_IO_ADDR);
++ column -= 256;
++ readcmd = NAND_CMD_READ1;
+ }
++ this->write_byte(mtd, readcmd);
+ }
++ this->write_byte(mtd, command);
+
+ /* Set ALE and clear CLE to start address cycle */
+- this->hwcontrol (NAND_CTL_CLRCLE);
++ this->hwcontrol(mtd, NAND_CTL_CLRCLE);
+
+ if (column != -1 || page_addr != -1) {
+- this->hwcontrol (NAND_CTL_SETALE);
++ this->hwcontrol(mtd, NAND_CTL_SETALE);
+
+ /* Serially input address */
+ if (column != -1)
+- writeb (column, NAND_IO_ADDR);
++ this->write_byte(mtd, column);
+ if (page_addr != -1) {
+- writeb ((unsigned char) (page_addr & 0xff), NAND_IO_ADDR);
+- writeb ((unsigned char) ((page_addr >> 8) & 0xff), NAND_IO_ADDR);
++ this->write_byte(mtd, (unsigned char) (page_addr & 0xff));
++ this->write_byte(mtd, (unsigned char) ((page_addr >> 8) & 0xff));
+ /* One more address cycle for higher density devices */
+ if (mtd->size & 0x0c000000)
+- writeb ((unsigned char) ((page_addr >> 16) & 0x0f), NAND_IO_ADDR);
++ this->write_byte(mtd, (unsigned char) ((page_addr >> 16) & 0x0f));
+ }
+ /* Latch in address */
+- this->hwcontrol (NAND_CTL_CLRALE);
++ this->hwcontrol(mtd, NAND_CTL_CLRALE);
+ }
+
+ /*
+@@ -244,10 +310,11 @@
+ case NAND_CMD_RESET:
+ if (this->dev_ready)
+ break;
+- this->hwcontrol (NAND_CTL_SETCLE);
+- writeb (NAND_CMD_STATUS, NAND_IO_ADDR);
+- this->hwcontrol (NAND_CTL_CLRCLE);
+- while ( !(readb (this->IO_ADDR_R) & 0x40));
++ udelay(this->chip_delay);
++ this->hwcontrol(mtd, NAND_CTL_SETCLE);
++ this->write_byte(mtd, NAND_CMD_STATUS);
++ this->hwcontrol(mtd, NAND_CTL_CLRCLE);
++ while ( !(this->read_byte(mtd) & 0x40));
+ return;
+
+ /* This applies to read commands */
+@@ -263,7 +330,7 @@
+ }
+
+ /* wait until command is processed */
+- while (!this->dev_ready());
++ while (!this->dev_ready(mtd));
+ }
+
+ /*
+@@ -288,17 +355,17 @@
+ spin_unlock_bh (&this->chip_lock);
+ return;
+ }
+-
++#if 0 /* This was broken. And of dubious utility */
+ if (this->state == FL_ERASING) {
+ if (new_state != FL_ERASING) {
+ this->state = new_state;
+ spin_unlock_bh (&this->chip_lock);
+- nand_select (); /* select in any case */
++ this->select_chip(mtd, 0); /* select in any case */
+ this->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
+ return;
+ }
+ }
+-
++#endif
+ set_current_state (TASK_UNINTERRUPTIBLE);
+ add_wait_queue (&this->wq, &wait);
+ spin_unlock_bh (&this->chip_lock);
+@@ -334,17 +401,17 @@
+ return 0;
+ }
+ if (this->dev_ready) {
+- if (this->dev_ready ())
++ if (this->dev_ready(mtd))
+ break;
+ }
+- if (readb (this->IO_ADDR_R) & 0x40)
++ if (this->read_byte(mtd) & 0x40)
+ break;
+
+ spin_unlock_bh (&this->chip_lock);
+ yield ();
+ spin_lock_bh (&this->chip_lock);
+ }
+- status = (int) readb (this->IO_ADDR_R);
++ status = (int) this->read_byte(mtd);
+ spin_unlock_bh (&this->chip_lock);
+
+ return status;
+@@ -352,14 +419,15 @@
+
+ /*
+ * Nand_page_program function is used for write and writev !
++ * This function will always program a full page of data
++ * If you call it with a non page aligned buffer, you're lost :)
+ */
+-static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this,
+- int page, int col, int last, u_char *oob_buf, int oobsel)
++static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int page, u_char *oob_buf, struct nand_oobinfo *oobsel)
+ {
+ int i, status;
+ u_char ecc_code[6], *oob_data;
+- int eccmode = oobsel ? this->eccmode : NAND_ECC_NONE;
+- int *oob_config = oobconfigs[oobsel];
++ int eccmode = oobsel->useecc ? this->eccmode : NAND_ECC_NONE;
++ int *oob_config = oobsel->eccpos;
+
+ /* pad oob area, if we have no oob buffer from fs-driver */
+ if (!oob_buf) {
+@@ -369,66 +437,42 @@
+ } else
+ oob_data = oob_buf;
+
+- /* software ecc 3 Bytes ECC / 256 Byte Data ? */
+- if (eccmode == NAND_ECC_SOFT) {
+- /* Read back previous written data, if col > 0 */
+- if (col) {
+- this->cmdfunc (mtd, NAND_CMD_READ0, 0, page);
+- for (i = 0; i < col; i++)
+- this->data_poi[i] = readb (this->IO_ADDR_R);
+- }
+- if ((col < this->eccsize) && (last >= this->eccsize)) {
+- this->calculate_ecc (&this->data_poi[0], &(ecc_code[0]));
+- for (i = 0; i < 3; i++)
+- oob_data[oob_config[i]] = ecc_code[i];
+- }
+- /* Calculate and write the second ECC if we have enough data */
+- if ((mtd->oobblock == 512) && (last == 512)) {
+- this->calculate_ecc (&this->data_poi[256], &(ecc_code[3]));
+- for (i = 3; i < 6; i++)
+- oob_data[oob_config[i]] = ecc_code[i];
+- }
+- } else {
+- /* For hardware ECC skip ECC, if we have no full page write */
+- if (eccmode != NAND_ECC_NONE && (col || last != mtd->oobblock))
+- eccmode = NAND_ECC_NONE;
+- }
+-
+- /* Prepad for partial page programming !!! */
+- for (i = 0; i < col; i++)
+- this->data_poi[i] = 0xff;
+-
+- /* Postpad for partial page programming !!! oob is already padded */
+- for (i = last; i < mtd->oobblock; i++)
+- this->data_poi[i] = 0xff;
+-
+ /* Send command to begin auto page programming */
+ this->cmdfunc (mtd, NAND_CMD_SEQIN, 0x00, page);
+
+ /* Write out complete page of data, take care of eccmode */
+- switch (this->eccmode) {
++ switch (eccmode) {
+ /* No ecc and software ecc 3/256, write all */
+ case NAND_ECC_NONE:
++ printk (KERN_WARNING "Writing data without ECC to NAND-FLASH is not recommended\n");
++ this->write_buf(mtd, this->data_poi, mtd->oobblock);
++ break;
+ case NAND_ECC_SOFT:
+- for (i = 0; i < mtd->oobblock; i++)
+- writeb ( this->data_poi[i] , this->IO_ADDR_W);
++ this->calculate_ecc(mtd, &this->data_poi[0], &(ecc_code[0]));
++ for (i = 0; i < 3; i++)
++ oob_data[oob_config[i]] = ecc_code[i];
++ /* Calculate and write the second ECC for 512 Byte page size */
++ if (mtd->oobblock == 512) {
++ this->calculate_ecc(mtd, &this->data_poi[256], &(ecc_code[3]));
++ for (i = 3; i < 6; i++)
++ oob_data[oob_config[i]] = ecc_code[i];
++ }
++ this->write_buf(mtd, this->data_poi, mtd->oobblock);
+ break;
+
+ /* Hardware ecc 3 byte / 256 data, write first half, get ecc, then second, if 512 byte pagesize */
+ case NAND_ECC_HW3_256:
+- this->enable_hwecc (NAND_ECC_WRITE); /* enable hardware ecc logic for write */
+- for (i = 0; i < mtd->eccsize; i++)
+- writeb ( this->data_poi[i] , this->IO_ADDR_W);
++ this->enable_hwecc(mtd, NAND_ECC_WRITE); /* enable hardware ecc logic for write */
++ this->write_buf(mtd, this->data_poi, mtd->eccsize);
+
+- this->calculate_ecc (NULL, &(ecc_code[0]));
++ this->calculate_ecc(mtd, NULL, &(ecc_code[0]));
+ for (i = 0; i < 3; i++)
+ oob_data[oob_config[i]] = ecc_code[i];
+
+ if (mtd->oobblock == 512) {
+- this->enable_hwecc (NAND_ECC_WRITE); /* enable hardware ecc logic for write*/
+- for (i = mtd->eccsize; i < mtd->oobblock; i++)
+- writeb ( this->data_poi[i] , this->IO_ADDR_W);
+- this->calculate_ecc (NULL, &(ecc_code[3]));
++ this->enable_hwecc(mtd, NAND_ECC_WRITE); /* enable hardware ecc logic for write*/
++ this->write_buf(mtd, &this->data_poi[mtd->eccsize], mtd->oobblock - mtd->eccsize);
++ this->calculate_ecc(mtd, NULL, &(ecc_code[3]));
+ for (i = 3; i < 6; i++)
+ oob_data[oob_config[i]] = ecc_code[i];
+ }
+@@ -436,20 +480,18 @@
+
+ /* Hardware ecc 3 byte / 512 byte data, write full page */
+ case NAND_ECC_HW3_512:
+- this->enable_hwecc (NAND_ECC_WRITE); /* enable hardware ecc logic */
+- for (i = 0; i < mtd->oobblock; i++)
+- writeb ( this->data_poi[i] , this->IO_ADDR_W);
+- this->calculate_ecc (NULL, &(ecc_code[0]));
++ this->enable_hwecc(mtd, NAND_ECC_WRITE); /* enable hardware ecc logic */
++ this->write_buf(mtd, this->data_poi, mtd->oobblock);
++ this->calculate_ecc(mtd, NULL, &(ecc_code[0]));
+ for (i = 0; i < 3; i++)
+ oob_data[oob_config[i]] = ecc_code[i];
+ break;
+
+ /* Hardware ecc 6 byte / 512 byte data, write full page */
+ case NAND_ECC_HW6_512:
+- this->enable_hwecc (NAND_ECC_WRITE); /* enable hardware ecc logic */
+- for (i = 0; i < mtd->oobblock; i++)
+- writeb ( this->data_poi[i] , this->IO_ADDR_W);
+- this->calculate_ecc (NULL, &(ecc_code[0]));
++ this->enable_hwecc(mtd, NAND_ECC_WRITE); /* enable hardware ecc logic */
++ this->write_buf(mtd, this->data_poi, mtd->oobblock);
++ this->calculate_ecc(mtd, NULL, &(ecc_code[0]));
+ for (i = 0; i < 6; i++)
+ oob_data[oob_config[i]] = ecc_code[i];
+ break;
+@@ -460,8 +502,7 @@
+ }
+
+ /* Write out OOB data */
+- for (i = 0; i < mtd->oobsize; i++)
+- writeb ( oob_data[i] , this->IO_ADDR_W);
++ this->write_buf(mtd, oob_data, mtd->oobsize);
+
+ /* Send command to actually program the data */
+ this->cmdfunc (mtd, NAND_CMD_PAGEPROG, -1, -1);
+@@ -490,25 +531,21 @@
+ */
+
+ /* Send command to read back the page */
+- this->cmdfunc (mtd, NAND_CMD_READ0, col, page);
++ this->cmdfunc (mtd, NAND_CMD_READ0, 0, page);
+ /* Loop through and verify the data */
+- for (i = col; i < last; i++) {
+- if (this->data_poi[i] != readb (this->IO_ADDR_R)) {
++ if (this->verify_buf(mtd, this->data_poi, mtd->oobblock)) {
+ DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write verify, page 0x%08x ", __FUNCTION__, page);
+ return -EIO;
+ }
+- }
+
+ /* check, if we have a fs-supplied oob-buffer */
+ if (oob_buf) {
+- for (i = 0; i < mtd->oobsize; i++) {
+- if (oob_data[i] != readb (this->IO_ADDR_R)) {
++ if (this->verify_buf(mtd, oob_data, mtd->oobsize)) {
+ DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write verify, page 0x%08x ", __FUNCTION__, page);
+ return -EIO;
+ }
+- }
+ } else {
+- if (eccmode != NAND_ECC_NONE && !col && last == mtd->oobblock) {
++ if (eccmode != NAND_ECC_NONE) {
+ int ecc_bytes = 0;
+
+ switch (this->eccmode) {
+@@ -518,8 +555,7 @@
+ case NAND_ECC_HW6_512: ecc_bytes = 6; break;
+ }
+
+- for (i = 0; i < mtd->oobsize; i++)
+- oob_data[i] = readb (this->IO_ADDR_R);
++ this->read_buf(mtd, oob_data, mtd->oobsize);
+
+ for (i = 0; i < ecc_bytes; i++) {
+ if (oob_data[oob_config[i]] != ecc_code[i]) {
+@@ -531,6 +567,13 @@
+ }
+ }
+ }
++ /*
++ * Terminate the read command. This is faster than sending a reset command or
++ * applying a 20us delay before issuing the next programm sequence.
++ * This is not a problem for all chips, but I have found a bunch of them.
++ */
++ this->select_chip(mtd, -1);
++ this->select_chip(mtd, 0);
+ #endif
+ return 0;
+ }
+@@ -540,7 +583,7 @@
+ */
+ static int nand_read (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf)
+ {
+- return (nand_read_ecc (mtd, from, len, retlen, buf, NULL, 0));
++ return nand_read_ecc (mtd, from, len, retlen, buf, NULL, NULL);
+ }
+
+
+@@ -548,7 +591,7 @@
+ * NAND read with ECC
+ */
+ static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
+- size_t * retlen, u_char * buf, u_char * oob_buf, int oobsel)
++ size_t * retlen, u_char * buf, u_char * oob_buf, struct nand_oobinfo *oobsel)
+ {
+ int j, col, page, end, ecc;
+ int erase_state = 0;
+@@ -557,9 +600,15 @@
+ u_char *data_poi, *oob_data = oob_buf;
+ u_char ecc_calc[6];
+ u_char ecc_code[6];
+- int eccmode = oobsel ? this->eccmode : NAND_ECC_NONE;
++ int eccmode;
++ int *oob_config;
++
++ // use chip default if zero
++ if (oobsel == NULL)
++ oobsel = &mtd->oobinfo;
+
+- int *oob_config = oobconfigs[oobsel];
++ eccmode = oobsel->useecc ? this->eccmode : NAND_ECC_NONE;
++ oob_config = oobsel->eccpos;
+
+ DEBUG (MTD_DEBUG_LEVEL3, "nand_read_ecc: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len);
+
+@@ -574,7 +623,7 @@
+ nand_get_chip (this, mtd ,FL_READING, &erase_state);
+
+ /* Select the NAND device */
+- nand_select ();
++ this->select_chip(mtd, 0);
+
+ /* First we calculate the starting page */
+ page = from >> this->page_shift;
+@@ -596,7 +645,7 @@
+ if (!this->dev_ready)
+ udelay (this->chip_delay);
+ else
+- while (!this->dev_ready());
++ while (!this->dev_ready(mtd));
+ }
+
+ /*
+@@ -616,39 +665,40 @@
+
+ j = 0;
+ switch (eccmode) {
+- case NAND_ECC_NONE: /* No ECC, Read in a page */
+- while (j < end)
+- data_poi[j++] = readb (this->IO_ADDR_R);
++ case NAND_ECC_NONE: { /* No ECC, Read in a page */
++ static unsigned long lastwhinge = 0;
++ if ((lastwhinge / HZ) != (jiffies / HZ)) {
++ printk (KERN_WARNING "Reading data from NAND FLASH without ECC is not recommended\n");
++ lastwhinge = jiffies;
++ }
++ this->read_buf(mtd, data_poi, end);
+ break;
++ }
+
+ case NAND_ECC_SOFT: /* Software ECC 3/256: Read in a page + oob data */
+- while (j < end)
+- data_poi[j++] = readb (this->IO_ADDR_R);
+- this->calculate_ecc (&data_poi[0], &ecc_calc[0]);
++ this->read_buf(mtd, data_poi, end);
++ this->calculate_ecc(mtd, &data_poi[0], &ecc_calc[0]);
+ if (mtd->oobblock == 512)
+- this->calculate_ecc (&data_poi[256], &ecc_calc[3]);
++ this->calculate_ecc(mtd, &data_poi[256], &ecc_calc[3]);
+ break;
+
+ case NAND_ECC_HW3_256: /* Hardware ECC 3 byte /256 byte data: Read in first 256 byte, get ecc, */
+- this->enable_hwecc (NAND_ECC_READ);
+- while (j < ecc)
+- data_poi[j++] = readb (this->IO_ADDR_R);
+- this->calculate_ecc (&data_poi[0], &ecc_calc[0]); /* read from hardware */
++ this->enable_hwecc(mtd, NAND_ECC_READ);
++ this->read_buf(mtd, data_poi, ecc);
++ this->calculate_ecc(mtd, &data_poi[0], &ecc_calc[0]); /* read from hardware */
+
+ if (mtd->oobblock == 512) { /* read second, if pagesize = 512 */
+- this->enable_hwecc (NAND_ECC_READ);
+- while (j < end)
+- data_poi[j++] = readb (this->IO_ADDR_R);
+- this->calculate_ecc (&data_poi[256], &ecc_calc[3]); /* read from hardware */
++ this->enable_hwecc(mtd, NAND_ECC_READ);
++ this->read_buf(mtd, &data_poi[ecc], end-ecc);
++ this->calculate_ecc(mtd, &data_poi[256], &ecc_calc[3]); /* read from hardware */
+ }
+ break;
+
+ case NAND_ECC_HW3_512:
+ case NAND_ECC_HW6_512: /* Hardware ECC 3/6 byte / 512 byte data : Read in a page */
+- this->enable_hwecc (NAND_ECC_READ);
+- while (j < end)
+- data_poi[j++] = readb (this->IO_ADDR_R);
+- this->calculate_ecc (&data_poi[0], &ecc_calc[0]); /* read from hardware */
++ this->enable_hwecc(mtd, NAND_ECC_READ);
++ this->read_buf(mtd, data_poi, end);
++ this->calculate_ecc(mtd, &data_poi[0], &ecc_calc[0]); /* read from hardware */
+ break;
+
+ default:
+@@ -658,7 +708,7 @@
+
+ /* read oobdata */
+ for (j = 0; j < mtd->oobsize; j++)
+- oob_data[oob + j] = readb (this->IO_ADDR_R);
++ oob_data[oob + j] = this->read_byte(mtd);
+
+ /* Skip ECC, if not active */
+ if (eccmode == NAND_ECC_NONE)
+@@ -669,7 +719,7 @@
+ ecc_code[j] = oob_data[oob + oob_config[j]];
+
+ /* correct data, if neccecary */
+- ecc_status = this->correct_data (&data_poi[0], &ecc_code[0], &ecc_calc[0]);
++ ecc_status = this->correct_data(mtd, &data_poi[0], &ecc_code[0], &ecc_calc[0]);
+ /* check, if we have a fs supplied oob-buffer */
+ if (oob_buf) {
+ oob += mtd->oobsize;
+@@ -682,7 +732,7 @@
+ }
+
+ if (mtd->oobblock == 512 && eccmode != NAND_ECC_HW3_512) {
+- ecc_status = this->correct_data (&data_poi[256], &ecc_code[3], &ecc_calc[3]);
++ ecc_status = this->correct_data(mtd, &data_poi[256], &ecc_code[3], &ecc_calc[3]);
+ if (oob_buf) {
+ *((int *)&oob_data[oob]) = ecc_status;
+ oob += sizeof(int);
+@@ -705,7 +755,7 @@
+ }
+
+ /* De-select the NAND device */
+- nand_deselect ();
++ this->select_chip(mtd, -1);
+
+ /* Wake up anyone waiting on the device */
+ spin_lock_bh (&this->chip_lock);
+@@ -753,7 +803,7 @@
+ nand_get_chip (this, mtd , FL_READING, &erase_state);
+
+ /* Select the NAND device */
+- nand_select ();
++ this->select_chip(mtd, 0);
+
+ /* Send the read command */
+ this->cmdfunc (mtd, NAND_CMD_READOOB, col, page);
+@@ -761,13 +811,20 @@
+ * Read the data, if we read more than one page
+ * oob data, let the device transfer the data !
+ */
+- for (i = 0; i < len; i++) {
+- buf[i] = readb (this->IO_ADDR_R);
+- if ((col++ & (mtd->oobsize - 1)) == (mtd->oobsize - 1))
++ i = 0;
++ while (i < len) {
++ int thislen = (mtd->oobsize - col) & (mtd->oobsize - 1);
++ if (!thislen)
++ thislen = mtd->oobsize;
++ thislen = min_t(int, thislen, len);
++ this->read_buf(mtd, &buf[i], thislen);
++ i += thislen;
++ col += thislen;
++ /* Delay between pages */
+ udelay (this->chip_delay);
+ }
+ /* De-select the NAND device */
+- nand_deselect ();
++ this->select_chip(mtd, -1);
+
+ /* Wake up anyone waiting on the device */
+ spin_lock_bh (&this->chip_lock);
+@@ -780,45 +837,54 @@
+ return 0;
+ }
+
++#define NOTALIGNED(x) (x & (mtd->oobblock-1)) != 0
++
+ /*
+ * Use NAND write ECC
+ */
+ static int nand_write (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char * buf)
+ {
+- return (nand_write_ecc (mtd, to, len, retlen, buf, NULL, 0));
++ return (nand_write_ecc (mtd, to, len, retlen, buf, NULL, NULL));
+ }
+ /*
+ * NAND write with ECC
+ */
+ static int nand_write_ecc (struct mtd_info *mtd, loff_t to, size_t len,
+- size_t * retlen, const u_char * buf, u_char * eccbuf, int oobsel)
++ size_t * retlen, const u_char * buf, u_char * eccbuf, struct nand_oobinfo *oobsel)
+ {
+- int i, page, col, cnt, ret = 0, oob = 0, written = 0;
++ int page, ret = 0, oob = 0, written = 0;
+ struct nand_chip *this = mtd->priv;
+
+ DEBUG (MTD_DEBUG_LEVEL3, "nand_write_ecc: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len);
+
+ /* Do not allow write past end of device */
+ if ((to + len) > mtd->size) {
+- DEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: Attempt to write past end of page\n");
++ DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: Attempt to write past end of page\n");
++ return -EINVAL;
++ }
++
++ /* reject writes, which are not page aligned */
++ if (NOTALIGNED (to) || NOTALIGNED(len)) {
++ printk (KERN_NOTICE "nand_write_ecc: Attempt to write not page aligned data\n");
+ return -EINVAL;
+ }
+
++ // if oobsel is NULL, use chip defaults
++ if (oobsel == NULL)
++ oobsel = &mtd->oobinfo;
++
+ /* Shift to get page */
+ page = ((int) to) >> this->page_shift;
+
+- /* Get the starting column */
+- col = to & (mtd->oobblock - 1);
+-
+ /* Grab the lock and see if the device is available */
+ nand_get_chip (this, mtd, FL_WRITING, NULL);
+
+ /* Select the NAND device */
+- nand_select ();
++ this->select_chip(mtd, 0);
+
+ /* Check the WP bit */
+ this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1);
+- if (!(readb (this->IO_ADDR_R) & 0x80)) {
++ if (!(this->read_byte(mtd) & 0x80)) {
+ DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: Device is write protected!!!\n");
+ ret = -EIO;
+ goto out;
+@@ -826,42 +892,27 @@
+
+ /* Loop until all data is written */
+ while (written < len) {
+- /*
+- * Check, if we have a full page write, then we can
+- * use the given buffer, else we have to copy
+- */
+- if (!col && (len - written) >= mtd->oobblock) {
++ int cnt = mtd->oobblock;
+ this->data_poi = (u_char*) &buf[written];
+- cnt = mtd->oobblock;
+- } else {
+- cnt = 0;
+- for (i = col; i < len && i < mtd->oobblock; i++) {
+- this->data_buf[i] = buf[written + i];
+- cnt++;
+- }
+- this->data_poi = this->data_buf;
+- }
+- /* We use the same function for write and writev !) */
++ /* We use the same function for write and writev */
+ if (eccbuf) {
+- ret = nand_write_page (mtd, this, page, col, cnt ,&eccbuf[oob], oobsel);
++ ret = nand_write_page (mtd, this, page, &eccbuf[oob], oobsel);
+ oob += mtd->oobsize;
+ } else
+- ret = nand_write_page (mtd, this, page, col, cnt, NULL, oobsel);
++ ret = nand_write_page (mtd, this, page, NULL, oobsel);
+
+ if (ret)
+ goto out;
+
+ /* Update written bytes count */
+ written += cnt;
+- /* Next write is aligned */
+- col = 0;
+ /* Increment page address */
+ page++;
+ }
+
+ out:
+ /* De-select the NAND device */
+- nand_deselect ();
++ this->select_chip(mtd, -1);
+
+ /* Wake up anyone waiting on the device */
+ spin_lock_bh (&this->chip_lock);
+@@ -873,13 +924,21 @@
+ return ret;
+ }
+
++static u_char ffchars[] = {
++ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
++ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
++};
++
+ /*
+ * NAND write out-of-band
+ */
+ static int nand_write_oob (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char * buf)
+ {
+- int i, column, page, status, ret = 0;
++ int column, page, status, ret = 0;
+ struct nand_chip *this = mtd->priv;
++#ifdef CONFIG_MTD_NAND_VERIFY_WRITE
++ int i;
++#endif
+
+ DEBUG (MTD_DEBUG_LEVEL3, "nand_write_oob: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len);
+
+@@ -902,27 +961,31 @@
+ nand_get_chip (this, mtd, FL_WRITING, NULL);
+
+ /* Select the NAND device */
+- nand_select ();
++ this->select_chip(mtd, 0);
++
++ /* Reset the chip. Some chips (like the Toshiba TC5832DC found
++ in one of my DiskOnChip 2000 test units) will clear the whole
++ data page too if we don't do this. I have no clue why, but
++ I seem to have 'fixed' it in the doc2000 driver in
++ August 1999. dwmw2. */
++ this->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
+
+ /* Check the WP bit */
+ this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1);
+- if (!(readb (this->IO_ADDR_R) & 0x80)) {
++ if (!(this->read_byte(mtd) & 0x80)) {
+ DEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: Device is write protected!!!\n");
+ ret = -EIO;
+ goto out;
+ }
+-
+ /* Write out desired data */
+ this->cmdfunc (mtd, NAND_CMD_SEQIN, mtd->oobblock, page);
++
+ /* prepad 0xff for partial programming */
+- for (i = 0; i < column; i++)
+- writeb (0xff, this->IO_ADDR_W);
++ this->write_buf(mtd, ffchars, column);
+ /* write data */
+- for (i = 0; i < len; i++)
+- writeb (buf[i], this->IO_ADDR_W);
++ this->write_buf(mtd, buf, len);
+ /* postpad 0xff for partial programming */
+- for (i = len + column; i < mtd->oobsize; i++)
+- writeb (0xff, this->IO_ADDR_W);
++ this->write_buf(mtd, ffchars, mtd->oobsize - (len+column));
+
+ /* Send command to program the OOB data */
+ this->cmdfunc (mtd, NAND_CMD_PAGEPROG, -1, -1);
+@@ -944,7 +1007,7 @@
+
+ /* Loop through and verify the data */
+ for (i = 0; i < len; i++) {
+- if (buf[i] != readb (this->IO_ADDR_R)) {
++ if (buf[i] != this->read_byte(mtd)) {
+ DEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: " "Failed write verify, page 0x%08x\n", page);
+ ret = -EIO;
+ goto out;
+@@ -954,7 +1017,7 @@
+
+ out:
+ /* De-select the NAND device */
+- nand_deselect ();
++ this->select_chip(mtd, -1);
+
+ /* Wake up anyone waiting on the device */
+ spin_lock_bh (&this->chip_lock);
+@@ -976,9 +1039,9 @@
+ }
+
+ static int nand_writev_ecc (struct mtd_info *mtd, const struct iovec *vecs, unsigned long count,
+- loff_t to, size_t * retlen, u_char *eccbuf, int oobsel)
++ loff_t to, size_t * retlen, u_char *eccbuf, struct nand_oobinfo *oobsel)
+ {
+- int i, page, col, cnt, len, total_len, ret = 0, written = 0;
++ int i, page, len, total_len, ret = 0, written = 0;
+ struct nand_chip *this = mtd->priv;
+
+ /* Calculate total length of data */
+@@ -995,39 +1058,42 @@
+ return -EINVAL;
+ }
+
++ /* reject writes, which are not page aligned */
++ if (NOTALIGNED (to) || NOTALIGNED(total_len)) {
++ printk (KERN_NOTICE "nand_write_ecc: Attempt to write not page aligned data\n");
++ return -EINVAL;
++ }
++
++ // if oobsel is NULL, use chip defaults
++ if (oobsel == NULL)
++ oobsel = &mtd->oobinfo;
++
+ /* Shift to get page */
+ page = ((int) to) >> this->page_shift;
+
+- /* Get the starting column */
+- col = to & (mtd->oobblock - 1);
+-
+ /* Grab the lock and see if the device is available */
+ nand_get_chip (this, mtd, FL_WRITING, NULL);
+
+ /* Select the NAND device */
+- nand_select ();
++ this->select_chip(mtd, 0);
+
+ /* Check the WP bit */
+ this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1);
+- if (!(readb (this->IO_ADDR_R) & 0x80)) {
++ if (!(this->read_byte(mtd) & 0x80)) {
+ DEBUG (MTD_DEBUG_LEVEL0, "nand_writev: Device is write protected!!!\n");
+ ret = -EIO;
+ goto out;
+ }
+
+ /* Loop until all iovecs' data has been written */
+- cnt = col;
+ len = 0;
+-
+ while (count) {
+ /*
+- * Check, if we write from offset 0 and if the tuple
+- * gives us not enough data for a full page write. Then we
+- * can use the iov direct, else we have to copy into
+- * data_buf.
++ * Check, if the tuple gives us not enough data for a
++ * full page write. Then we can use the iov direct,
++ * else we have to copy into data_buf.
+ */
+- if (!cnt && (vecs->iov_len - len) >= mtd->oobblock) {
+- cnt = mtd->oobblock;
++ if ((vecs->iov_len - len) >= mtd->oobblock) {
+ this->data_poi = (u_char *) vecs->iov_base;
+ this->data_poi += len;
+ len += mtd->oobblock;
+@@ -1042,6 +1108,7 @@
+ * Read data out of each tuple until we have a full page
+ * to write or we've read all the tuples.
+ */
++ int cnt = 0;
+ while ((cnt < mtd->oobblock) && count) {
+ if (vecs->iov_base != NULL && vecs->iov_len) {
+ this->data_buf[cnt++] = ((u_char *) vecs->iov_base)[len++];
+@@ -1057,15 +1124,12 @@
+ }
+
+ /* We use the same function for write and writev !) */
+- ret = nand_write_page (mtd, this, page, col, cnt, NULL, oobsel);
++ ret = nand_write_page (mtd, this, page, NULL, oobsel);
+ if (ret)
+ goto out;
+
+ /* Update written bytes count */
+- written += (cnt - col);
+-
+- /* Reset written byte counter and column */
+- col = cnt = 0;
++ written += mtd->oobblock;;
+
+ /* Increment page address */
+ page++;
+@@ -1073,7 +1137,7 @@
+
+ out:
+ /* De-select the NAND device */
+- nand_deselect ();
++ this->select_chip(mtd, -1);
+
+ /* Wake up anyone waiting on the device */
+ spin_lock_bh (&this->chip_lock);
+@@ -1125,11 +1189,11 @@
+ pages_per_block = mtd->erasesize / mtd->oobblock;
+
+ /* Select the NAND device */
+- nand_select ();
++ this->select_chip(mtd, 0);
+
+ /* Check the WP bit */
+ this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1);
+- if (!(readb (this->IO_ADDR_R) & 0x80)) {
++ if (!(this->read_byte(mtd) & 0x80)) {
+ DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Device is write protected!!!\n");
+ instr->state = MTD_ERASE_FAILED;
+ goto erase_exit;
+@@ -1142,8 +1206,7 @@
+
+ while (len) {
+ /* Check if we have a bad block, we do not erase bad blocks ! */
+- this->cmdfunc (mtd, NAND_CMD_READOOB, NAND_BADBLOCK_POS, page);
+- if (readb (this->IO_ADDR_R) != 0xff) {
++ if (this->block_bad(mtd, page)) {
+ printk (KERN_WARNING "nand_erase: attempt to erase a bad block at page 0x%08x\n", page);
+ instr->state = MTD_ERASE_FAILED;
+ goto erase_exit;
+@@ -1179,7 +1242,7 @@
+ if (this->state == FL_ERASING || this->state == FL_READY) {
+ /* Select the NAND device again, if we were interrupted */
+ this->state = FL_ERASING;
+- nand_select ();
++ this->select_chip(mtd, 0);
+ continue;
+ } else {
+ set_current_state (TASK_UNINTERRUPTIBLE);
+@@ -1194,7 +1257,7 @@
+
+ erase_exit:
+ /* De-select the NAND device */
+- nand_deselect ();
++ this->select_chip(mtd, -1);
+ spin_unlock_bh (&this->chip_lock);
+
+ ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO;;
+@@ -1205,6 +1268,7 @@
+ /* The device is ready */
+ spin_lock_bh (&this->chip_lock);
+ this->state = FL_READY;
++ wake_up (&this->wq);
+ spin_unlock_bh (&this->chip_lock);
+
+ /* Return more or less happy */
+@@ -1259,7 +1323,7 @@
+ /*
+ * Scan for the NAND device
+ */
+-int nand_scan (struct mtd_info *mtd)
++int nand_scan (struct mtd_info *mtd, int maxchips)
+ {
+ int i, nand_maf_id, nand_dev_id;
+ struct nand_chip *this = mtd->priv;
+@@ -1276,23 +1340,38 @@
+ if (this->waitfunc == NULL)
+ this->waitfunc = nand_wait;
+
++ if (!this->block_bad)
++ this->block_bad = nand_block_bad;
++ if (!this->select_chip)
++ this->select_chip = nand_select_chip;
++ if (!this->write_byte)
++ this->write_byte = nand_write_byte;
++ if (!this->read_byte)
++ this->read_byte = nand_read_byte;
++ if (!this->write_buf)
++ this->write_buf = nand_write_buf;
++ if (!this->read_buf)
++ this->read_buf = nand_read_buf;
++ if (!this->verify_buf)
++ this->verify_buf = nand_verify_buf;
++
+ /* Select the device */
+- nand_select ();
++ this->select_chip(mtd, 0);
+
+ /* Send the command for reading device ID */
+ this->cmdfunc (mtd, NAND_CMD_READID, 0x00, -1);
+
+ /* Read manufacturer and device IDs */
+- nand_maf_id = readb (this->IO_ADDR_R);
+- nand_dev_id = readb (this->IO_ADDR_R);
++ nand_maf_id = this->read_byte(mtd);
++ nand_dev_id = this->read_byte(mtd);
+
+ /* Print and store flash device information */
+ for (i = 0; nand_flash_ids[i].name != NULL; i++) {
+ if (nand_dev_id == nand_flash_ids[i].id && !mtd->size) {
+ mtd->name = nand_flash_ids[i].name;
+ mtd->erasesize = nand_flash_ids[i].erasesize;
+- mtd->size = (1 << nand_flash_ids[i].chipshift);
+ mtd->eccsize = 256;
++ this->chipshift = nand_flash_ids[i].chipshift;
+ if (nand_flash_ids[i].page256) {
+ mtd->oobblock = 256;
+ mtd->oobsize = 8;
+@@ -1307,13 +1386,34 @@
+ if (nand_manuf_ids[i].id == nand_maf_id)
+ break;
+ }
+- printk (KERN_INFO "NAND device: Manufacture ID:"
++ printk (KERN_INFO "NAND device: Manufacturer ID:"
+ " 0x%02x, Chip ID: 0x%02x (%s %s)\n", nand_maf_id, nand_dev_id,
+ nand_manuf_ids[i].name , mtd->name);
+ break;
+ }
+ }
+
++ if (!mtd->name) {
++ printk (KERN_WARNING "No NAND device found!!!\n");
++ return 1;
++ }
++
++ for (i=1; i < maxchips; i++) {
++ this->select_chip(mtd, i);
++
++ /* Send the command for reading device ID */
++ this->cmdfunc (mtd, NAND_CMD_READID, 0x00, -1);
++
++ /* Read manufacturer and device IDs */
++ if (nand_maf_id != this->read_byte(mtd) ||
++ nand_dev_id != this->read_byte(mtd))
++ break;
++ }
++ if (i > 1)
++ printk(KERN_INFO "%d NAND chips detected\n", i);
++
++ mtd->size = (1 << this->chipshift) /* * i when we fix the rest of the code */;
++
+ /*
+ * check ECC mode, default to software
+ * if 3byte/512byte hardware ECC is selected and we have 256 byte pagesize
+@@ -1324,6 +1424,7 @@
+ switch (this->eccmode) {
+
+ case NAND_ECC_HW3_512:
++ case NAND_ECC_HW6_512:
+ if (mtd->oobblock == 256) {
+ printk (KERN_WARNING "512 byte HW ECC not possible on 256 Byte pagesize, fallback to SW ECC \n");
+ this->eccmode = NAND_ECC_SOFT;
+@@ -1340,6 +1441,7 @@
+ BUG();
+
+ case NAND_ECC_NONE:
++ printk (KERN_WARNING "NAND_ECC_NONE selected by board driver. This is not recommended !!\n");
+ this->eccmode = NAND_ECC_NONE;
+ break;
+
+@@ -1359,18 +1461,11 @@
+ spin_lock_init (&this->chip_lock);
+
+ /* De-select the device */
+- nand_deselect ();
+-
+- /* Print warning message for no device */
+- if (!mtd->size) {
+- printk (KERN_WARNING "No NAND device found!!!\n");
+- return 1;
+- }
++ this->select_chip(mtd, -1);
+
+ /* Fill in remaining MTD driver data */
+ mtd->type = MTD_NANDFLASH;
+ mtd->flags = MTD_CAP_NANDFLASH | MTD_ECC;
+- mtd->module = THIS_MODULE;
+ mtd->ecctype = MTD_ECC_SW;
+ mtd->erase = nand_erase;
+ mtd->point = NULL;
+@@ -1389,6 +1484,7 @@
+ mtd->unlock = NULL;
+ mtd->suspend = NULL;
+ mtd->resume = NULL;
++ mtd->owner = THIS_MODULE;
+
+ /* Return happy */
+ return 0;
+@@ -1397,5 +1493,5 @@
+ EXPORT_SYMBOL (nand_scan);
+
+ MODULE_LICENSE ("GPL");
+-MODULE_AUTHOR ("Steven J. Hill <sjhill@cotw.com>, Thomas Gleixner <tglx@linutronix.de>");
++MODULE_AUTHOR ("Steven J. Hill <sjhill@realitydiluted.com>, Thomas Gleixner <tglx@linutronix.de>");
+ MODULE_DESCRIPTION ("Generic NAND flash driver code");
+diff -Nurb linux-mips-2.4.27/drivers/mtd/nand/nand_ecc.c linux/drivers/mtd/nand/nand_ecc.c
+--- linux-mips-2.4.27/drivers/mtd/nand/nand_ecc.c 2003-02-26 01:53:50.000000000 +0100
++++ linux/drivers/mtd/nand/nand_ecc.c 2004-11-19 10:25:12.011182768 +0100
+@@ -1,10 +1,10 @@
+ /*
+ * drivers/mtd/nand_ecc.c
+ *
+- * Copyright (C) 2000 Steven J. Hill (sjhill@cotw.com)
++ * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
+ * Toshiba America Electronics Components, Inc.
+ *
+- * $Id$
++ * $Id$
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+@@ -17,6 +17,7 @@
+ #include <linux/types.h>
+ #include <linux/kernel.h>
+ #include <linux/module.h>
++#include <linux/mtd/nand_ecc.h>
+
+ /*
+ * Pre-calculated 256-way 1 byte column parity
+@@ -84,7 +85,7 @@
+ /*
+ * Calculate 3 byte ECC code for 256 byte block
+ */
+-void nand_calculate_ecc (const u_char *dat, u_char *ecc_code)
++void nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code)
+ {
+ u_char idx, reg1, reg2, reg3;
+ int j;
+@@ -119,7 +120,7 @@
+ /*
+ * Detect and correct a 1 bit error for 256 byte block
+ */
+-int nand_correct_data (u_char *dat, u_char *read_ecc, u_char *calc_ecc)
++int nand_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc)
+ {
+ u_char a, b, c, d1, d2, d3, add, bit, i;
+
+@@ -209,5 +210,5 @@
+ EXPORT_SYMBOL(nand_correct_data);
+
+ MODULE_LICENSE("GPL");
+-MODULE_AUTHOR("Steven J. Hill <sjhill@cotw.com>");
++MODULE_AUTHOR("Steven J. Hill <sjhill@realitydiluted.com>");
+ MODULE_DESCRIPTION("Generic NAND ECC support");
+diff -Nurb linux-mips-2.4.27/drivers/mtd/nand/nand_ids.c linux/drivers/mtd/nand/nand_ids.c
+--- linux-mips-2.4.27/drivers/mtd/nand/nand_ids.c 2003-02-26 01:53:50.000000000 +0100
++++ linux/drivers/mtd/nand/nand_ids.c 2004-11-19 10:25:12.013182464 +0100
+@@ -4,7 +4,7 @@
+ * Copyright (C) 2002 Thomas Gleixner (tglx@linutronix.de)
+ *
+ *
+- * $Id$
++ * $Id$
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+@@ -18,21 +18,21 @@
+ * Chip ID list
+ */
+ struct nand_flash_dev nand_flash_ids[] = {
+- {"NAND 1MB 5V", 0x6e, 20, 0x1000, 1}, // 1Mb 5V
+- {"NAND 2MB 5V", 0x64, 21, 0x1000, 1}, // 2Mb 5V
+- {"NAND 4MB 5V", 0x6b, 22, 0x2000, 0}, // 4Mb 5V
+- {"NAND 1MB 3,3V", 0xe8, 20, 0x1000, 1}, // 1Mb 3.3V
+- {"NAND 1MB 3,3V", 0xec, 20, 0x1000, 1}, // 1Mb 3.3V
+- {"NAND 2MB 3,3V", 0xea, 21, 0x1000, 1}, // 2Mb 3.3V
+- {"NAND 4MB 3,3V", 0xd5, 22, 0x2000, 0}, // 4Mb 3.3V
+- {"NAND 4MB 3,3V", 0xe3, 22, 0x2000, 0}, // 4Mb 3.3V
+- {"NAND 4MB 3,3V", 0xe5, 22, 0x2000, 0}, // 4Mb 3.3V
+- {"NAND 8MB 3,3V", 0xd6, 23, 0x2000, 0}, // 8Mb 3.3V
+- {"NAND 8MB 3,3V", 0xe6, 23, 0x2000, 0}, // 8Mb 3.3V
+- {"NAND 16MB 3,3V", 0x73, 24, 0x4000, 0},// 16Mb 3,3V
+- {"NAND 32MB 3,3V", 0x75, 25, 0x4000, 0}, // 32Mb 3,3V
+- {"NAND 64MB 3,3V", 0x76, 26, 0x4000, 0}, // 64Mb 3,3V
+- {"NAND 128MB 3,3V", 0x79, 27, 0x4000, 0}, // 128Mb 3,3V
++ {"NAND 1MiB 5V", 0x6e, 20, 0x1000, 1},
++ {"NAND 2MiB 5V", 0x64, 21, 0x1000, 1},
++ {"NAND 4MiB 5V", 0x6b, 22, 0x2000, 0},
++ {"NAND 1MiB 3,3V", 0xe8, 20, 0x1000, 1},
++ {"NAND 1MiB 3,3V", 0xec, 20, 0x1000, 1},
++ {"NAND 2MiB 3,3V", 0xea, 21, 0x1000, 1},
++ {"NAND 4MiB 3,3V", 0xd5, 22, 0x2000, 0},
++ {"NAND 4MiB 3,3V", 0xe3, 22, 0x2000, 0},
++ {"NAND 4MiB 3,3V", 0xe5, 22, 0x2000, 0},
++ {"NAND 8MiB 3,3V", 0xd6, 23, 0x2000, 0},
++ {"NAND 8MiB 3,3V", 0xe6, 23, 0x2000, 0},
++ {"NAND 16MiB 3,3V", 0x73, 24, 0x4000, 0},
++ {"NAND 32MiB 3,3V", 0x75, 25, 0x4000, 0},
++ {"NAND 64MiB 3,3V", 0x76, 26, 0x4000, 0},
++ {"NAND 128MiB 3,3V", 0x79, 27, 0x4000, 0},
+ {NULL,}
+ };
+
+diff -Nurb linux-mips-2.4.27/drivers/mtd/nand/spia.c linux/drivers/mtd/nand/spia.c
+--- linux-mips-2.4.27/drivers/mtd/nand/spia.c 2003-02-26 01:53:50.000000000 +0100
++++ linux/drivers/mtd/nand/spia.c 2004-11-19 10:25:12.014182312 +0100
+@@ -1,14 +1,14 @@
+ /*
+ * drivers/mtd/nand/spia.c
+ *
+- * Copyright (C) 2000 Steven J. Hill (sjhill@cotw.com)
++ * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
+ *
+ *
+ * 10-29-2001 TG change to support hardwarespecific access
+ * to controllines (due to change in nand.c)
+ * page_cache added
+ *
+- * $Id$
++ * $Id$
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+@@ -20,6 +20,8 @@
+ * a 64Mibit (8MiB x 8 bits) NAND flash device.
+ */
+
++#include <linux/kernel.h>
++#include <linux/init.h>
+ #include <linux/slab.h>
+ #include <linux/module.h>
+ #include <linux/mtd/mtd.h>
+@@ -35,14 +37,14 @@
+ /*
+ * Values specific to the SPIA board (used with EP7212 processor)
+ */
+-#define SPIA_IO_ADDR = 0xd0000000 /* Start of EP7212 IO address space */
+-#define SPIA_FIO_ADDR = 0xf0000000 /* Address where flash is mapped */
+-#define SPIA_PEDR = 0x0080 /*
++#define SPIA_IO_BASE 0xd0000000 /* Start of EP7212 IO address space */
++#define SPIA_FIO_BASE 0xf0000000 /* Address where flash is mapped */
++#define SPIA_PEDR 0x0080 /*
+ * IO offset to Port E data register
+ * where the CLE, ALE and NCE pins
+ * are wired to.
+ */
+-#define SPIA_PEDDR = 0x00c0 /*
++#define SPIA_PEDDR 0x00c0 /*
+ * IO offset to Port E data direction
+ * register so we can control the IO
+ * lines.
+@@ -62,21 +64,20 @@
+ MODULE_PARM(spia_pedr, "i");
+ MODULE_PARM(spia_peddr, "i");
+
+-__setup("spia_io_base=",spia_io_base);
+-__setup("spia_fio_base=",spia_fio_base);
+-__setup("spia_pedr=",spia_pedr);
+-__setup("spia_peddr=",spia_peddr);
+-
+ /*
+ * Define partitions for flash device
+ */
+ const static struct mtd_partition partition_info[] = {
+- { name: "SPIA flash partition 1",
+- offset: 0,
+- size: 2*1024*1024 },
+- { name: "SPIA flash partition 2",
+- offset: 2*1024*1024,
+- size: 6*1024*1024 }
++ {
++ .name = "SPIA flash partition 1",
++ .offset = 0,
++ .size = 2*1024*1024
++ },
++ {
++ .name = "SPIA flash partition 2",
++ .offset = 2*1024*1024,
++ .size = 6*1024*1024
++ }
+ };
+ #define NUM_PARTITIONS 2
+
+@@ -84,7 +85,7 @@
+ /*
+ * hardware specific access to control-lines
+ */
+-void spia_hwcontrol(int cmd){
++static void spia_hwcontrol(struct mtd_info *mtd, int cmd){
+
+ switch(cmd){
+
+@@ -139,7 +140,7 @@
+ this->chip_delay = 15;
+
+ /* Scan to find existence of the device */
+- if (nand_scan (spia_mtd)) {
++ if (nand_scan (spia_mtd, 1)) {
+ kfree (spia_mtd);
+ return -ENXIO;
+ }
+@@ -152,16 +153,6 @@
+ return -ENOMEM;
+ }
+
+- /* Allocate memory for internal data buffer */
+- this->data_cache = kmalloc (sizeof(u_char) * (spia_mtd->oobblock + spia_mtd->oobsize), GFP_KERNEL);
+- if (!this->data_cache) {
+- printk ("Unable to allocate NAND data cache for SPIA.\n");
+- kfree (this->data_buf);
+- kfree (spia_mtd);
+- return = -ENOMEM;
+- }
+- this->cache_page = -1;
+-
+ /* Register the partitions */
+ add_mtd_partitions(spia_mtd, partition_info, NUM_PARTITIONS);
+
+@@ -183,7 +174,6 @@
+
+ /* Free internal data buffer */
+ kfree (this->data_buf);
+- kfree (this->page_cache);
+
+ /* Free the MTD device structure */
+ kfree (spia_mtd);
+@@ -192,5 +182,5 @@
+ #endif
+
+ MODULE_LICENSE("GPL");
+-MODULE_AUTHOR("Steven J. Hill <sjhill@cotw.com");
++MODULE_AUTHOR("Steven J. Hill <sjhill@realitydiluted.com");
+ MODULE_DESCRIPTION("Board-specific glue layer for NAND flash on SPIA board");
+diff -Nurb linux-mips-2.4.27/drivers/mtd/nand/toto.c linux/drivers/mtd/nand/toto.c
+--- linux-mips-2.4.27/drivers/mtd/nand/toto.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/mtd/nand/toto.c 2004-11-19 10:25:12.016182008 +0100
+@@ -0,0 +1,221 @@
++/*
++ * drivers/mtd/nand/toto.c
++ *
++ * Copyright (c) 2003 Texas Instruments
++ *
++ * Derived from drivers/mtd/autcpu12.c
++ *
++ * Copyright (c) 2002 Thomas Gleixner <tgxl@linutronix.de>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ *
++ * Overview:
++ * This is a device driver for the NAND flash device found on the
++ * TI fido board. It supports 32MiB and 64MiB cards
++ *
++ * $Id$
++ */
++
++#include <linux/slab.h>
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/delay.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/nand.h>
++#include <linux/mtd/partitions.h>
++#include <asm/io.h>
++#include <asm/arch/hardware.h>
++#include <asm/sizes.h>
++#include <asm/arch/toto.h>
++#include <asm/arch-omap1510/hardware.h>
++#include <asm/arch/gpio.h>
++
++/*
++ * MTD structure for TOTO board
++ */
++static struct mtd_info *toto_mtd = NULL;
++
++static int toto_io_base = OMAP_FLASH_1_BASE;
++
++#define CONFIG_NAND_WORKAROUND 1
++
++#define NAND_NCE 0x4000
++#define NAND_CLE 0x1000
++#define NAND_ALE 0x0002
++#define NAND_MASK (NAND_CLE | NAND_ALE | NAND_NCE)
++
++#define T_NAND_CTL_CLRALE(iob) gpiosetout(NAND_ALE, 0)
++#define T_NAND_CTL_SETALE(iob) gpiosetout(NAND_ALE, NAND_ALE)
++#ifdef CONFIG_NAND_WORKAROUND /* "some" dev boards busted, blue wired to rts2 :( */
++#define T_NAND_CTL_CLRCLE(iob) gpiosetout(NAND_CLE, 0); rts2setout(2, 2)
++#define T_NAND_CTL_SETCLE(iob) gpiosetout(NAND_CLE, NAND_CLE); rts2setout(2, 0)
++#else
++#define T_NAND_CTL_CLRCLE(iob) gpiosetout(NAND_CLE, 0)
++#define T_NAND_CTL_SETCLE(iob) gpiosetout(NAND_CLE, NAND_CLE)
++#endif
++#define T_NAND_CTL_SETNCE(iob) gpiosetout(NAND_NCE, 0)
++#define T_NAND_CTL_CLRNCE(iob) gpiosetout(NAND_NCE, NAND_NCE)
++
++/*
++ * Define partitions for flash devices
++ */
++
++static struct mtd_partition partition_info64M[] = {
++ { .name = "toto kernel partition 1",
++ .offset = 0,
++ .size = 2 * SZ_1M },
++ { .name = "toto file sys partition 2",
++ .offset = 2 * SZ_1M,
++ .size = 14 * SZ_1M },
++ { .name = "toto user partition 3",
++ .offset = 16 * SZ_1M,
++ .size = 16 * SZ_1M },
++ { .name = "toto devboard extra partition 4",
++ .offset = 32 * SZ_1M,
++ .size = 32 * SZ_1M },
++};
++
++static struct mtd_partition partition_info32M[] = {
++ { .name = "toto kernel partition 1",
++ .offset = 0,
++ .size = 2 * SZ_1M },
++ { .name = "toto file sys partition 2",
++ .offset = 2 * SZ_1M,
++ .size = 14 * SZ_1M },
++ { .name = "toto user partition 3",
++ .offset = 16 * SZ_1M,
++ .size = 16 * SZ_1M },
++};
++
++#define NUM_PARTITIONS32M 3
++#define NUM_PARTITIONS64M 4
++/*
++ * hardware specific access to control-lines
++*/
++
++static void toto_hwcontrol(struct mtd_info *mtd, int cmd)
++{
++
++ udelay(1); /* hopefully enough time for tc make proceding write to clear */
++ switch(cmd){
++
++ case NAND_CTL_SETCLE: T_NAND_CTL_SETCLE(cmd); break;
++ case NAND_CTL_CLRCLE: T_NAND_CTL_CLRCLE(cmd); break;
++
++ case NAND_CTL_SETALE: T_NAND_CTL_SETALE(cmd); break;
++ case NAND_CTL_CLRALE: T_NAND_CTL_CLRALE(cmd); break;
++
++ case NAND_CTL_SETNCE: T_NAND_CTL_SETNCE(cmd); break;
++ case NAND_CTL_CLRNCE: T_NAND_CTL_CLRNCE(cmd); break;
++ }
++ udelay(1); /* allow time to ensure gpio state to over take memory write */
++}
++
++/*
++ * Main initialization routine
++ */
++int __init toto_init (void)
++{
++ struct nand_chip *this;
++ int err = 0;
++
++ /* Allocate memory for MTD device structure and private data */
++ toto_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip),
++ GFP_KERNEL);
++ if (!toto_mtd) {
++ printk (KERN_WARNING "Unable to allocate toto NAND MTD device structure.\n");
++ err = -ENOMEM;
++ goto out;
++ }
++
++ /* Get pointer to private data */
++ this = (struct nand_chip *) (&toto_mtd[1]);
++
++ /* Initialize structures */
++ memset((char *) toto_mtd, 0, sizeof(struct mtd_info));
++ memset((char *) this, 0, sizeof(struct nand_chip));
++
++ /* Link the private data with the MTD structure */
++ toto_mtd->priv = this;
++
++ /* Set address of NAND IO lines */
++ this->IO_ADDR_R = toto_io_base;
++ this->IO_ADDR_W = toto_io_base;
++ this->hwcontrol = toto_hwcontrol;
++ this->dev_ready = NULL;
++ /* 25 us command delay time */
++ this->chip_delay = 30;
++ this->eccmode = NAND_ECC_SOFT;
++
++ /* Scan to find existance of the device */
++ if (nand_scan (toto_mtd, 1)) {
++ err = -ENXIO;
++ goto out_mtd;
++ }
++
++ /* Allocate memory for internal data buffer */
++ this->data_buf = kmalloc (sizeof(u_char) * (toto_mtd->oobblock + toto_mtd->oobsize), GFP_KERNEL);
++ if (!this->data_buf) {
++ printk (KERN_WARNING "Unable to allocate NAND data buffer for toto.\n");
++ err = -ENOMEM;
++ goto out_mtd;
++ }
++
++ /* Register the partitions */
++ switch(toto_mtd->size){
++ case SZ_64M: add_mtd_partitions(toto_mtd, partition_info64M, NUM_PARTITIONS64M); break;
++ case SZ_32M: add_mtd_partitions(toto_mtd, partition_info32M, NUM_PARTITIONS32M); break;
++ default: {
++ printk (KERN_WARNING "Unsupported Nand device\n");
++ err = -ENXIO;
++ goto out_buf;
++ }
++ }
++
++ gpioreserve(NAND_MASK); /* claim our gpios */
++ archflashwp(0,0); /* open up flash for writing */
++
++ goto out;
++
++out_buf:
++ kfree (this->data_buf);
++out_mtd:
++ kfree (toto_mtd);
++out:
++ return err;
++}
++
++module_init(toto_init);
++
++/*
++ * Clean up routine
++ */
++static void __exit toto_cleanup (void)
++{
++ struct nand_chip *this = (struct nand_chip *) &toto_mtd[1];
++
++ /* Unregister partitions */
++ del_mtd_partitions(toto_mtd);
++
++ /* Unregister the device */
++ del_mtd_device (toto_mtd);
++
++ /* Free internal data buffers */
++ kfree (this->data_buf);
++
++ /* Free the MTD device structure */
++ kfree (toto_mtd);
++
++ /* stop flash writes */
++ archflashwp(0,1);
++
++ /* release gpios to system */
++ gpiorelease(NAND_MASK);
++}
++module_exit(toto_cleanup);
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Richard Woodruff <r-woodruff2@ti.com>");
++MODULE_DESCRIPTION("Glue layer for NAND flash on toto board");
+diff -Nurb linux-mips-2.4.27/drivers/mtd/nand/tx4925ndfmc.c linux/drivers/mtd/nand/tx4925ndfmc.c
+--- linux-mips-2.4.27/drivers/mtd/nand/tx4925ndfmc.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/mtd/nand/tx4925ndfmc.c 2004-11-19 10:25:12.017181856 +0100
+@@ -0,0 +1,442 @@
++/*
++ * drivers/mtd/tx4925ndfmc.c
++ *
++ * Overview:
++ * This is a device driver for the NAND flash device found on the
++ * Toshiba RBTX4925 reference board, which is a SmartMediaCard. It supports
++ * 16MiB, 32MiB and 64MiB cards.
++ *
++ * Author: MontaVista Software, Inc. source@mvista.com
++ *
++ * Derived from drivers/mtd/autcpu12.c
++ * Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de)
++ *
++ * $Id$
++ *
++ * Copyright (C) 2001 Toshiba Corporation
++ *
++ * 2003 (c) MontaVista Software, Inc. This file is licensed under
++ * the terms of the GNU General Public License version 2. This program
++ * is licensed "as is" without any warranty of any kind, whether express
++ * or implied.
++ *
++ */
++
++#include <linux/slab.h>
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/nand.h>
++#include <linux/mtd/partitions.h>
++#include <linux/delay.h>
++#include <asm/io.h>
++#include <asm/tx4925/tx4925_nand.h>
++
++extern struct nand_oobinfo jffs2_oobinfo;
++
++/*
++ * MTD structure for RBTX4925 board
++ */
++static struct mtd_info *tx4925ndfmc_mtd = NULL;
++
++/*
++ * Module stuff
++ */
++#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
++#define tx4925ndfmc_init init_module
++#define tx4925ndfmc_cleanup cleanup_module
++#endif
++
++/*
++ * Define partitions for flash devices
++ */
++
++static struct mtd_partition partition_info16k[] = {
++ { .name = "RBTX4925 flash partition 1",
++ .offset = 0,
++ .size = 8 * 0x00100000 },
++ { .name = "RBTX4925 flash partition 2",
++ .offset = 8 * 0x00100000,
++ .size = 8 * 0x00100000 },
++};
++
++static struct mtd_partition partition_info32k[] = {
++ { .name = "RBTX4925 flash partition 1",
++ .offset = 0,
++ .size = 8 * 0x00100000 },
++ { .name = "RBTX4925 flash partition 2",
++ .offset = 8 * 0x00100000,
++ .size = 24 * 0x00100000 },
++};
++
++static struct mtd_partition partition_info64k[] = {
++ { .name = "User FS",
++ .offset = 0,
++ .size = 16 * 0x00100000 },
++ { .name = "RBTX4925 flash partition 2",
++ .offset = 16 * 0x00100000,
++ .size = 48 * 0x00100000},
++};
++
++static struct mtd_partition partition_info128k[] = {
++ { .name = "Skip bad section",
++ .offset = 0,
++ .size = 16 * 0x00100000 },
++ { .name = "User FS",
++ .offset = 16 * 0x00100000,
++ .size = 112 * 0x00100000 },
++};
++#define NUM_PARTITIONS16K 2
++#define NUM_PARTITIONS32K 2
++#define NUM_PARTITIONS64K 2
++#define NUM_PARTITIONS128K 2
++
++/*
++ * hardware specific access to control-lines
++*/
++static void tx4925ndfmc_hwcontrol(struct mtd_info *mtd, int cmd)
++{
++
++ switch(cmd){
++
++ case NAND_CTL_SETCLE:
++ tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_CLE;
++ break;
++ case NAND_CTL_CLRCLE:
++ tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_CLE;
++ break;
++ case NAND_CTL_SETALE:
++ tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_ALE;
++ break;
++ case NAND_CTL_CLRALE:
++ tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_ALE;
++ break;
++ case NAND_CTL_SETNCE:
++ tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_CE;
++ break;
++ case NAND_CTL_CLRNCE:
++ tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_CE;
++ break;
++ case NAND_CTL_SETWP:
++ tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_WE;
++ break;
++ case NAND_CTL_CLRWP:
++ tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_WE;
++ break;
++ }
++}
++
++/*
++* read device ready pin
++*/
++static int tx4925ndfmc_device_ready(struct mtd_info *mtd)
++{
++ int ready;
++ ready = (tx4925_ndfmcptr->sr & TX4925_NDSFR_BUSY) ? 0 : 1;
++ return ready;
++}
++void tx4925ndfmc_enable_hwecc(struct mtd_info *mtd, int mode)
++{
++ /* reset first */
++ tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_ECC_CNTL_MASK;
++ tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_ECC_CNTL_MASK;
++ tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_ECC_CNTL_ENAB;
++}
++static void tx4925ndfmc_disable_ecc(void)
++{
++ tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_ECC_CNTL_MASK;
++}
++static void tx4925ndfmc_enable_read_ecc(void)
++{
++ tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_ECC_CNTL_MASK;
++ tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_ECC_CNTL_READ;
++}
++void tx4925ndfmc_readecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code){
++ int i;
++ u_char *ecc = ecc_code;
++ tx4925ndfmc_enable_read_ecc();
++ for (i = 0;i < 6;i++,ecc++)
++ *ecc = tx4925_read_nfmc(&(tx4925_ndfmcptr->dtr));
++ tx4925ndfmc_disable_ecc();
++}
++void tx4925ndfmc_device_setup(void)
++{
++
++ *(unsigned char *)0xbb005000 &= ~0x08;
++
++ /* reset NDFMC */
++ tx4925_ndfmcptr->rstr |= TX4925_NDFRSTR_RST;
++ while (tx4925_ndfmcptr->rstr & TX4925_NDFRSTR_RST);
++
++ /* setup BusSeparete, Hold Time, Strobe Pulse Width */
++ tx4925_ndfmcptr->mcr = TX4925_BSPRT ? TX4925_NDFMCR_BSPRT : 0;
++ tx4925_ndfmcptr->spr = TX4925_HOLD << 4 | TX4925_SPW;
++}
++static u_char tx4925ndfmc_nand_read_byte(struct mtd_info *mtd)
++{
++ struct nand_chip *this = mtd->priv;
++ return tx4925_read_nfmc(this->IO_ADDR_R);
++}
++
++static void tx4925ndfmc_nand_write_byte(struct mtd_info *mtd, u_char byte)
++{
++ struct nand_chip *this = mtd->priv;
++ tx4925_write_nfmc(byte, this->IO_ADDR_W);
++}
++
++static void tx4925ndfmc_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
++{
++ int i;
++ struct nand_chip *this = mtd->priv;
++
++ for (i=0; i<len; i++)
++ tx4925_write_nfmc(buf[i], this->IO_ADDR_W);
++}
++
++static void tx4925ndfmc_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
++{
++ int i;
++ struct nand_chip *this = mtd->priv;
++
++ for (i=0; i<len; i++)
++ buf[i] = tx4925_read_nfmc(this->IO_ADDR_R);
++}
++
++static int tx4925ndfmc_nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len)
++{
++ int i;
++ struct nand_chip *this = mtd->priv;
++
++ for (i=0; i<len; i++)
++ if (buf[i] != tx4925_read_nfmc(this->IO_ADDR_R))
++ return i;
++
++ return 0;
++}
++
++/*
++ * Send command to NAND device
++ */
++static void tx4925ndfmc_nand_command (struct mtd_info *mtd, unsigned command, int column, int page_addr)
++{
++ register struct nand_chip *this = mtd->priv;
++
++ /* Begin command latch cycle */
++ this->hwcontrol(mtd, NAND_CTL_SETCLE);
++ /*
++ * Write out the command to the device.
++ */
++ if (command == NAND_CMD_SEQIN) {
++ int readcmd;
++
++ if (column >= mtd->oobblock) {
++ /* OOB area */
++ column -= mtd->oobblock;
++ readcmd = NAND_CMD_READOOB;
++ } else if (column < 256) {
++ /* First 256 bytes --> READ0 */
++ readcmd = NAND_CMD_READ0;
++ } else {
++ column -= 256;
++ readcmd = NAND_CMD_READ1;
++ }
++ this->write_byte(mtd, readcmd);
++ }
++ this->write_byte(mtd, command);
++
++ /* Set ALE and clear CLE to start address cycle */
++ this->hwcontrol(mtd, NAND_CTL_CLRCLE);
++
++ if (column != -1 || page_addr != -1) {
++ this->hwcontrol(mtd, NAND_CTL_SETALE);
++
++ /* Serially input address */
++ if (column != -1)
++ this->write_byte(mtd, column);
++ if (page_addr != -1) {
++ this->write_byte(mtd, (unsigned char) (page_addr & 0xff));
++ this->write_byte(mtd, (unsigned char) ((page_addr >> 8) & 0xff));
++ /* One more address cycle for higher density devices */
++ if (mtd->size & 0x0c000000)
++ this->write_byte(mtd, (unsigned char) ((page_addr >> 16) & 0x0f));
++ }
++ /* Latch in address */
++ this->hwcontrol(mtd, NAND_CTL_CLRALE);
++ }
++
++ /*
++ * program and erase have their own busy handlers
++ * status and sequential in needs no delay
++ */
++ switch (command) {
++
++ case NAND_CMD_PAGEPROG:
++ /* Turn off WE */
++ this->hwcontrol (mtd, NAND_CTL_CLRWP);
++ return;
++
++ case NAND_CMD_SEQIN:
++ /* Turn on WE */
++ this->hwcontrol (mtd, NAND_CTL_SETWP);
++ return;
++
++ case NAND_CMD_ERASE1:
++ case NAND_CMD_ERASE2:
++ case NAND_CMD_STATUS:
++ return;
++
++ case NAND_CMD_RESET:
++ if (this->dev_ready)
++ break;
++ this->hwcontrol(mtd, NAND_CTL_SETCLE);
++ this->write_byte(mtd, NAND_CMD_STATUS);
++ this->hwcontrol(mtd, NAND_CTL_CLRCLE);
++ while ( !(this->read_byte(mtd) & 0x40));
++ return;
++
++ /* This applies to read commands */
++ default:
++ /*
++ * If we don't have access to the busy pin, we apply the given
++ * command delay
++ */
++ if (!this->dev_ready) {
++ udelay (this->chip_delay);
++ return;
++ }
++ }
++
++ /* wait until command is processed */
++ while (!this->dev_ready(mtd));
++}
++
++#ifdef CONFIG_MTD_CMDLINE_PARTS
++extern int parse_cmdline_partitions(struct mtd_info *master, struct mtd_partitio
++n **pparts, char *);
++#endif
++
++/*
++ * Main initialization routine
++ */
++extern int nand_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc);
++int __init tx4925ndfmc_init (void)
++{
++ struct nand_chip *this;
++ int err = 0;
++
++ /* Allocate memory for MTD device structure and private data */
++ tx4925ndfmc_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip),
++ GFP_KERNEL);
++ if (!tx4925ndfmc_mtd) {
++ printk ("Unable to allocate RBTX4925 NAND MTD device structure.\n");
++ err = -ENOMEM;
++ goto out;
++ }
++
++ tx4925ndfmc_device_setup();
++
++ /* io is indirect via a register so don't need to ioremap address */
++
++ /* Get pointer to private data */
++ this = (struct nand_chip *) (&tx4925ndfmc_mtd[1]);
++
++ /* Initialize structures */
++ memset((char *) tx4925ndfmc_mtd, 0, sizeof(struct mtd_info));
++ memset((char *) this, 0, sizeof(struct nand_chip));
++
++ /* Link the private data with the MTD structure */
++ tx4925ndfmc_mtd->priv = this;
++
++ /* Set address of NAND IO lines */
++ this->IO_ADDR_R = (unsigned long)&(tx4925_ndfmcptr->dtr);
++ this->IO_ADDR_W = (unsigned long)&(tx4925_ndfmcptr->dtr);
++ this->hwcontrol = tx4925ndfmc_hwcontrol;
++ this->enable_hwecc = tx4925ndfmc_enable_hwecc;
++ this->calculate_ecc = tx4925ndfmc_readecc;
++ this->correct_data = nand_correct_data;
++ this->eccmode = NAND_ECC_HW6_512;
++ this->dev_ready = tx4925ndfmc_device_ready;
++ /* 20 us command delay time */
++ this->chip_delay = 20;
++ this->read_byte = tx4925ndfmc_nand_read_byte;
++ this->write_byte = tx4925ndfmc_nand_write_byte;
++ this->cmdfunc = tx4925ndfmc_nand_command;
++ this->write_buf = tx4925ndfmc_nand_write_buf;
++ this->read_buf = tx4925ndfmc_nand_read_buf;
++ this->verify_buf = tx4925ndfmc_nand_verify_buf;
++
++ /* Scan to find existance of the device */
++ if (nand_scan (tx4925ndfmc_mtd, 1)) {
++ err = -ENXIO;
++ goto out_ior;
++ }
++
++ /* Allocate memory for internal data buffer */
++ this->data_buf = kmalloc (sizeof(u_char) * (tx4925ndfmc_mtd->oobblock + tx4925ndfmc_mtd->oobsize), GFP_KERNEL);
++ if (!this->data_buf) {
++ printk ("Unable to allocate NAND data buffer for RBTX4925.\n");
++ err = -ENOMEM;
++ goto out_ior;
++ }
++
++ /* Register the partitions */
++#ifdef CONFIG_MTD_CMDLINE_PARTS
++ {
++ int mtd_parts_nb = 0;
++ struct mtd_partition *mtd_parts = 0;
++ mtd_parts_nb = parse_cmdline_partitions(tx4925ndfmc_mtd, &mtd_parts, "tx4925ndfmc");
++ if (mtd_parts_nb > 0)
++ add_mtd_partitions(tx4925ndfmc_mtd, mtd_parts, mtd_parts_nb);
++ else
++ add_mtd_device(tx4925ndfmc_mtd);
++ }
++#else /* ifdef CONFIG_MTD_CMDLINE_PARTS */
++ switch(tx4925ndfmc_mtd->size){
++ case 0x01000000: add_mtd_partitions(tx4925ndfmc_mtd, partition_info16k, NUM_PARTITIONS16K); break;
++ case 0x02000000: add_mtd_partitions(tx4925ndfmc_mtd, partition_info32k, NUM_PARTITIONS32K); break;
++ case 0x04000000: add_mtd_partitions(tx4925ndfmc_mtd, partition_info64k, NUM_PARTITIONS64K); break;
++ case 0x08000000: add_mtd_partitions(tx4925ndfmc_mtd, partition_info128k, NUM_PARTITIONS128K); break;
++ default: {
++ printk ("Unsupported SmartMedia device\n");
++ err = -ENXIO;
++ goto out_buf;
++ }
++ }
++#endif /* ifdef CONFIG_MTD_CMDLINE_PARTS */
++ goto out;
++
++out_buf:
++ kfree (this->data_buf);
++out_ior:
++out:
++ return err;
++}
++
++module_init(tx4925ndfmc_init);
++
++/*
++ * Clean up routine
++ */
++#ifdef MODULE
++static void __exit tx4925ndfmc_cleanup (void)
++{
++ struct nand_chip *this = (struct nand_chip *) &tx4925ndfmc_mtd[1];
++
++ /* Unregister partitions */
++ del_mtd_partitions(tx4925ndfmc_mtd);
++
++ /* Unregister the device */
++ del_mtd_device (tx4925ndfmc_mtd);
++
++ /* Free internal data buffers */
++ kfree (this->data_buf);
++
++ /* Free the MTD device structure */
++ kfree (tx4925ndfmc_mtd);
++}
++module_exit(tx4925ndfmc_cleanup);
++#endif
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Alice Hennessy <ahennessy@mvista.com>");
++MODULE_DESCRIPTION("Glue layer for SmartMediaCard on Toshiba RBTX4925");
+diff -Nurb linux-mips-2.4.27/drivers/mtd/nand/tx4938ndfmc.c linux/drivers/mtd/nand/tx4938ndfmc.c
+--- linux-mips-2.4.27/drivers/mtd/nand/tx4938ndfmc.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/mtd/nand/tx4938ndfmc.c 2004-11-19 10:25:12.019181552 +0100
+@@ -0,0 +1,422 @@
++/*
++ * drivers/mtd/nand/tx4938ndfmc.c
++ *
++ * Overview:
++ * This is a device driver for the NAND flash device connected to
++ * TX4938 internal NAND Memory Controller.
++ * TX4938 NDFMC is almost same as TX4925 NDFMC, but register size are 64 bit.
++ *
++ * Author: source@mvista.com
++ *
++ * Based on spia.c by Steven J. Hill
++ *
++ * $Id$
++ *
++ * Copyright (C) 2000-2001 Toshiba Corporation
++ *
++ * 2003 (c) MontaVista Software, Inc. This file is licensed under the
++ * terms of the GNU General Public License version 2. This program is
++ * licensed "as is" without any warranty of any kind, whether express
++ * or implied.
++ */
++#include <linux/config.h>
++#include <linux/slab.h>
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/nand.h>
++#include <linux/mtd/nand_ecc.h>
++#include <linux/mtd/partitions.h>
++#include <asm/io.h>
++#include <asm/bootinfo.h>
++#include <linux/delay.h>
++#include <asm/tx4938/rbtx4938.h>
++
++extern struct nand_oobinfo jffs2_oobinfo;
++
++/*
++ * MTD structure for TX4938 NDFMC
++ */
++static struct mtd_info *tx4938ndfmc_mtd;
++
++/*
++ * Define partitions for flash device
++ */
++#define flush_wb() (void)tx4938_ndfmcptr->mcr;
++
++#define NUM_PARTITIONS 3
++#define NUMBER_OF_CIS_BLOCKS 24
++#define SIZE_OF_BLOCK 0x00004000
++#define NUMBER_OF_BLOCK_PER_ZONE 1024
++#define SIZE_OF_ZONE (NUMBER_OF_BLOCK_PER_ZONE * SIZE_OF_BLOCK)
++#ifndef CONFIG_MTD_CMDLINE_PARTS
++/*
++ * You can use the following sample of MTD partitions
++ * on the NAND Flash Memory 32MB or more.
++ *
++ * The following figure shows the image of the sample partition on
++ * the 32MB NAND Flash Memory.
++ *
++ * Block No.
++ * 0 +-----------------------------+ ------
++ * | CIS | ^
++ * 24 +-----------------------------+ |
++ * | kernel image | | Zone 0
++ * | | |
++ * +-----------------------------+ |
++ * 1023 | unused area | v
++ * +-----------------------------+ ------
++ * 1024 | JFFS2 | ^
++ * | | |
++ * | | | Zone 1
++ * | | |
++ * | | |
++ * | | v
++ * 2047 +-----------------------------+ ------
++ *
++ */
++static struct mtd_partition partition_info[NUM_PARTITIONS] = {
++ {
++ .name = "RBTX4938 CIS Area",
++ .offset = 0,
++ .size = (NUMBER_OF_CIS_BLOCKS * SIZE_OF_BLOCK),
++ .mask_flags = MTD_WRITEABLE /* This partition is NOT writable */
++ },
++ {
++ .name = "RBTX4938 kernel image",
++ .offset = MTDPART_OFS_APPEND,
++ .size = 8 * 0x00100000, /* 8MB (Depends on size of kernel image) */
++ .mask_flags = MTD_WRITEABLE /* This partition is NOT writable */
++ },
++ {
++ .name = "Root FS (JFFS2)",
++ .offset = (0 + SIZE_OF_ZONE), /* start address of next zone */
++ .size = MTDPART_SIZ_FULL
++ },
++};
++#endif
++
++static void tx4938ndfmc_hwcontrol(struct mtd_info *mtd, int cmd)
++{
++ switch (cmd) {
++ case NAND_CTL_SETCLE:
++ tx4938_ndfmcptr->mcr |= TX4938_NDFMCR_CLE;
++ break;
++ case NAND_CTL_CLRCLE:
++ tx4938_ndfmcptr->mcr &= ~TX4938_NDFMCR_CLE;
++ break;
++ case NAND_CTL_SETALE:
++ tx4938_ndfmcptr->mcr |= TX4938_NDFMCR_ALE;
++ break;
++ case NAND_CTL_CLRALE:
++ tx4938_ndfmcptr->mcr &= ~TX4938_NDFMCR_ALE;
++ break;
++ /* TX4938_NDFMCR_CE bit is 0:high 1:low */
++ case NAND_CTL_SETNCE:
++ tx4938_ndfmcptr->mcr |= TX4938_NDFMCR_CE;
++ break;
++ case NAND_CTL_CLRNCE:
++ tx4938_ndfmcptr->mcr &= ~TX4938_NDFMCR_CE;
++ break;
++ case NAND_CTL_SETWP:
++ tx4938_ndfmcptr->mcr |= TX4938_NDFMCR_WE;
++ break;
++ case NAND_CTL_CLRWP:
++ tx4938_ndfmcptr->mcr &= ~TX4938_NDFMCR_WE;
++ break;
++ }
++}
++static int tx4938ndfmc_dev_ready(struct mtd_info *mtd)
++{
++ flush_wb();
++ return !(tx4938_ndfmcptr->sr & TX4938_NDFSR_BUSY);
++}
++static void tx4938ndfmc_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code)
++{
++ u32 mcr = tx4938_ndfmcptr->mcr;
++ mcr &= ~TX4938_NDFMCR_ECC_ALL;
++ tx4938_ndfmcptr->mcr = mcr | TX4938_NDFMCR_ECC_OFF;
++ tx4938_ndfmcptr->mcr = mcr | TX4938_NDFMCR_ECC_READ;
++ ecc_code[1] = tx4938_ndfmcptr->dtr;
++ ecc_code[0] = tx4938_ndfmcptr->dtr;
++ ecc_code[2] = tx4938_ndfmcptr->dtr;
++ tx4938_ndfmcptr->mcr = mcr | TX4938_NDFMCR_ECC_OFF;
++}
++static void tx4938ndfmc_enable_hwecc(struct mtd_info *mtd, int mode)
++{
++ u32 mcr = tx4938_ndfmcptr->mcr;
++ mcr &= ~TX4938_NDFMCR_ECC_ALL;
++ tx4938_ndfmcptr->mcr = mcr | TX4938_NDFMCR_ECC_RESET;
++ tx4938_ndfmcptr->mcr = mcr | TX4938_NDFMCR_ECC_OFF;
++ tx4938_ndfmcptr->mcr = mcr | TX4938_NDFMCR_ECC_ON;
++}
++
++static u_char tx4938ndfmc_nand_read_byte(struct mtd_info *mtd)
++{
++ struct nand_chip *this = mtd->priv;
++ return tx4938_read_nfmc(this->IO_ADDR_R);
++}
++
++static void tx4938ndfmc_nand_write_byte(struct mtd_info *mtd, u_char byte)
++{
++ struct nand_chip *this = mtd->priv;
++ tx4938_write_nfmc(byte, this->IO_ADDR_W);
++}
++
++static void tx4938ndfmc_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
++{
++ int i;
++ struct nand_chip *this = mtd->priv;
++
++ for (i=0; i<len; i++)
++ tx4938_write_nfmc(buf[i], this->IO_ADDR_W);
++}
++
++static void tx4938ndfmc_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
++{
++ int i;
++ struct nand_chip *this = mtd->priv;
++
++ for (i=0; i<len; i++)
++ buf[i] = tx4938_read_nfmc(this->IO_ADDR_R);
++}
++
++static int tx4938ndfmc_nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len)
++{
++ int i;
++ struct nand_chip *this = mtd->priv;
++
++ for (i=0; i<len; i++)
++ if (buf[i] != tx4938_read_nfmc(this->IO_ADDR_R))
++ return i;
++
++ return 0;
++}
++
++/*
++ * Send command to NAND device
++ */
++static void tx4938ndfmc_nand_command (struct mtd_info *mtd, unsigned command, int column, int page_addr)
++{
++ register struct nand_chip *this = mtd->priv;
++
++ /* Begin command latch cycle */
++ this->hwcontrol(mtd, NAND_CTL_SETCLE);
++ /*
++ * Write out the command to the device.
++ */
++ if (command == NAND_CMD_SEQIN) {
++ int readcmd;
++
++ if (column >= mtd->oobblock) {
++ /* OOB area */
++ column -= mtd->oobblock;
++ readcmd = NAND_CMD_READOOB;
++ } else if (column < 256) {
++ /* First 256 bytes --> READ0 */
++ readcmd = NAND_CMD_READ0;
++ } else {
++ column -= 256;
++ readcmd = NAND_CMD_READ1;
++ }
++ this->write_byte(mtd, readcmd);
++ }
++ this->write_byte(mtd, command);
++
++ /* Set ALE and clear CLE to start address cycle */
++ this->hwcontrol(mtd, NAND_CTL_CLRCLE);
++
++ if (column != -1 || page_addr != -1) {
++ this->hwcontrol(mtd, NAND_CTL_SETALE);
++
++ /* Serially input address */
++ if (column != -1)
++ this->write_byte(mtd, column);
++ if (page_addr != -1) {
++ this->write_byte(mtd, (unsigned char) (page_addr & 0xff));
++ this->write_byte(mtd, (unsigned char) ((page_addr >> 8) & 0xff));
++ /* One more address cycle for higher density devices */
++ if (mtd->size & 0x0c000000)
++ this->write_byte(mtd, (unsigned char) ((page_addr >> 16) & 0x0f));
++ }
++ /* Latch in address */
++ this->hwcontrol(mtd, NAND_CTL_CLRALE);
++ }
++
++ /*
++ * program and erase have their own busy handlers
++ * status and sequential in needs no delay
++ */
++ switch (command) {
++
++ case NAND_CMD_PAGEPROG:
++ /* Turn off WE */
++ this->hwcontrol (mtd, NAND_CTL_CLRWP);
++ return;
++
++ case NAND_CMD_SEQIN:
++ /* Turn on WE */
++ this->hwcontrol (mtd, NAND_CTL_SETWP);
++ return;
++
++ case NAND_CMD_ERASE1:
++ case NAND_CMD_ERASE2:
++ case NAND_CMD_STATUS:
++ return;
++
++ case NAND_CMD_RESET:
++ if (this->dev_ready)
++ break;
++ this->hwcontrol(mtd, NAND_CTL_SETCLE);
++ this->write_byte(mtd, NAND_CMD_STATUS);
++ this->hwcontrol(mtd, NAND_CTL_CLRCLE);
++ while ( !(this->read_byte(mtd) & 0x40));
++ return;
++
++ /* This applies to read commands */
++ default:
++ /*
++ * If we don't have access to the busy pin, we apply the given
++ * command delay
++ */
++ if (!this->dev_ready) {
++ udelay (this->chip_delay);
++ return;
++ }
++ }
++
++ /* wait until command is processed */
++ while (!this->dev_ready(mtd));
++}
++
++#ifdef CONFIG_MTD_CMDLINE_PARTS
++extern int parse_cmdline_partitions(struct mtd_info *master, struct mtd_partition **pparts, char *);
++#endif
++/*
++ * Main initialization routine
++ */
++int __init tx4938ndfmc_init (void)
++{
++ struct nand_chip *this;
++ int bsprt = 0, hold = 0xf, spw = 0xf;
++ int protected = 0;
++
++ if ((*rbtx4938_piosel_ptr & 0x0c) != 0x08) {
++ printk("TX4938 NDFMC: disabled by IOC PIOSEL\n");
++ return -ENODEV;
++ }
++ bsprt = 1;
++ hold = 2;
++ spw = 9 - 1; /* 8 GBUSCLK = 80ns (@ GBUSCLK 100MHz) */
++
++ if ((tx4938_ccfgptr->pcfg &
++ (TX4938_PCFG_ATA_SEL|TX4938_PCFG_ISA_SEL|TX4938_PCFG_NDF_SEL))
++ != TX4938_PCFG_NDF_SEL) {
++ printk("TX4938 NDFMC: disabled by PCFG.\n");
++ return -ENODEV;
++ }
++
++ /* reset NDFMC */
++ tx4938_ndfmcptr->rstr |= TX4938_NDFRSTR_RST;
++ while (tx4938_ndfmcptr->rstr & TX4938_NDFRSTR_RST)
++ ;
++ /* setup BusSeparete, Hold Time, Strobe Pulse Width */
++ tx4938_ndfmcptr->mcr = bsprt ? TX4938_NDFMCR_BSPRT : 0;
++ tx4938_ndfmcptr->spr = hold << 4 | spw;
++
++ /* Allocate memory for MTD device structure and private data */
++ tx4938ndfmc_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip),
++ GFP_KERNEL);
++ if (!tx4938ndfmc_mtd) {
++ printk ("Unable to allocate TX4938 NDFMC MTD device structure.\n");
++ return -ENOMEM;
++ }
++
++ /* Get pointer to private data */
++ this = (struct nand_chip *) (&tx4938ndfmc_mtd[1]);
++
++ /* Initialize structures */
++ memset((char *) tx4938ndfmc_mtd, 0, sizeof(struct mtd_info));
++ memset((char *) this, 0, sizeof(struct nand_chip));
++
++ /* Link the private data with the MTD structure */
++ tx4938ndfmc_mtd->priv = this;
++
++ /* Set address of NAND IO lines */
++ this->IO_ADDR_R = (unsigned long)&tx4938_ndfmcptr->dtr;
++ this->IO_ADDR_W = (unsigned long)&tx4938_ndfmcptr->dtr;
++ this->hwcontrol = tx4938ndfmc_hwcontrol;
++ this->dev_ready = tx4938ndfmc_dev_ready;
++ this->calculate_ecc = tx4938ndfmc_calculate_ecc;
++ this->correct_data = nand_correct_data;
++ this->enable_hwecc = tx4938ndfmc_enable_hwecc;
++ this->eccmode = NAND_ECC_HW3_256;
++ this->chip_delay = 100;
++ this->read_byte = tx4938ndfmc_nand_read_byte;
++ this->write_byte = tx4938ndfmc_nand_write_byte;
++ this->cmdfunc = tx4938ndfmc_nand_command;
++ this->write_buf = tx4938ndfmc_nand_write_buf;
++ this->read_buf = tx4938ndfmc_nand_read_buf;
++ this->verify_buf = tx4938ndfmc_nand_verify_buf;
++
++ /* Scan to find existance of the device */
++ if (nand_scan (tx4938ndfmc_mtd, 1)) {
++ kfree (tx4938ndfmc_mtd);
++ return -ENXIO;
++ }
++
++ /* Allocate memory for internal data buffer */
++ this->data_buf = kmalloc (sizeof(u_char) * (tx4938ndfmc_mtd->oobblock + tx4938ndfmc_mtd->oobsize), GFP_KERNEL);
++ if (!this->data_buf) {
++ printk ("Unable to allocate NAND data buffer for TX4938.\n");
++ kfree (tx4938ndfmc_mtd);
++ return -ENOMEM;
++ }
++
++ if (protected) {
++ printk(KERN_INFO "TX4938 NDFMC: write protected.\n");
++ tx4938ndfmc_mtd->flags &= ~(MTD_WRITEABLE | MTD_ERASEABLE);
++ }
++
++#ifdef CONFIG_MTD_CMDLINE_PARTS
++ {
++ int mtd_parts_nb = 0;
++ struct mtd_partition *mtd_parts = 0;
++ mtd_parts_nb = parse_cmdline_partitions(tx4938ndfmc_mtd, &mtd_parts, "tx4938ndfmc");
++ if (mtd_parts_nb > 0)
++ add_mtd_partitions(tx4938ndfmc_mtd, mtd_parts, mtd_parts_nb);
++ else
++ add_mtd_device(tx4938ndfmc_mtd);
++ }
++#else
++ add_mtd_partitions(tx4938ndfmc_mtd, partition_info, NUM_PARTITIONS );
++#endif
++
++ return 0;
++}
++module_init(tx4938ndfmc_init);
++
++/*
++ * Clean up routine
++ */
++static void __exit tx4938ndfmc_cleanup (void)
++{
++ struct nand_chip *this = (struct nand_chip *) tx4938ndfmc_mtd->priv;
++
++ /* Unregister the device */
++#ifdef CONFIG_MTD_CMDLINE_PARTS
++ del_mtd_partitions(tx4938ndfmc_mtd);
++#endif
++ del_mtd_device (tx4938ndfmc_mtd);
++
++ /* Free the MTD device structure */
++ kfree (tx4938ndfmc_mtd);
++
++ /* Free internal data buffer */
++ kfree (this->data_buf);
++}
++module_exit(tx4938ndfmc_cleanup);
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Alice Hennessy <ahennessy@mvista.com>");
++MODULE_DESCRIPTION("Board-specific glue layer for NAND flash on TX4938 NDFMC");
+diff -Nurb linux-mips-2.4.27/drivers/mtd/nftlcore.c linux/drivers/mtd/nftlcore.c
+--- linux-mips-2.4.27/drivers/mtd/nftlcore.c 2003-02-26 01:53:49.000000000 +0100
++++ linux/drivers/mtd/nftlcore.c 2004-11-19 10:25:11.653237184 +0100
+@@ -1,7 +1,7 @@
+ /* Linux driver for NAND Flash Translation Layer */
+ /* (c) 1999 Machine Vision Holdings, Inc. */
+ /* Author: David Woodhouse <dwmw2@infradead.org> */
+-/* $Id$ */
++/* $Id$ */
+
+ /*
+ The contents of this file are distributed under the GNU General
+@@ -23,15 +23,13 @@
+ #include <linux/slab.h>
+ #include <linux/sched.h>
+ #include <linux/init.h>
+-#include <linux/blkpg.h>
++#include <linux/hdreg.h>
+
+-#ifdef CONFIG_KMOD
+ #include <linux/kmod.h>
+-#endif
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/nand.h>
+ #include <linux/mtd/nftl.h>
+-#include <linux/mtd/compatmac.h>
++#include <linux/mtd/blktrans.h>
+
+ /* maximum number of loops while examining next block, to have a
+ chance to detect consistency problems (they should never happen
+@@ -39,187 +37,95 @@
+
+ #define MAX_LOOPS 10000
+
+-/* NFTL block device stuff */
+-#define MAJOR_NR NFTL_MAJOR
+-#define DEVICE_REQUEST nftl_request
+-#define DEVICE_OFF(device)
+-
+-
+-#include <linux/blk.h>
+-#include <linux/hdreg.h>
+-
+-/* Linux-specific block device functions */
+-
+-/* I _HATE_ the Linux block device setup more than anything else I've ever
+- * encountered, except ...
+- */
+-
+-static int nftl_sizes[256];
+-static int nftl_blocksizes[256];
+-
+-/* .. for the Linux partition table handling. */
+-struct hd_struct part_table[256];
+-
+-#if LINUX_VERSION_CODE < 0x20328
+-static void dummy_init (struct gendisk *crap)
+-{}
+-#endif
+-
+-static struct gendisk nftl_gendisk = {
+- major: MAJOR_NR,
+- major_name: "nftl",
+- minor_shift: NFTL_PARTN_BITS, /* Bits to shift to get real from partition */
+- max_p: (1<<NFTL_PARTN_BITS)-1, /* Number of partitions per real */
+-#if LINUX_VERSION_CODE < 0x20328
+- max_nr: MAX_NFTLS, /* maximum number of real */
+- init: dummy_init, /* init function */
+-#endif
+- part: part_table, /* hd struct */
+- sizes: nftl_sizes, /* block sizes */
+-};
+-
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,14)
+-#define BLK_INC_USE_COUNT MOD_INC_USE_COUNT
+-#define BLK_DEC_USE_COUNT MOD_DEC_USE_COUNT
+-#else
+-#define BLK_INC_USE_COUNT do {} while(0)
+-#define BLK_DEC_USE_COUNT do {} while(0)
+-#endif
+-
+-struct NFTLrecord *NFTLs[MAX_NFTLS];
+
+-static void NFTL_setup(struct mtd_info *mtd)
++static void nftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
+ {
+- int i;
+ struct NFTLrecord *nftl;
+ unsigned long temp;
+- int firstfree = -1;
+-
+- DEBUG(MTD_DEBUG_LEVEL1,"NFTL_setup\n");
+
+- for (i = 0; i < MAX_NFTLS; i++) {
+- if (!NFTLs[i] && firstfree == -1)
+- firstfree = i;
+- else if (NFTLs[i] && NFTLs[i]->mtd == mtd) {
+- /* This is a Spare Media Header for an NFTL we've already found */
+- DEBUG(MTD_DEBUG_LEVEL1, "MTD already mounted as NFTL\n");
++ if (mtd->ecctype != MTD_ECC_RS_DiskOnChip)
+ return;
+- }
+- }
+- if (firstfree == -1) {
+- printk(KERN_WARNING "No more NFTL slot available\n");
+- return;
+- }
++
++ DEBUG(MTD_DEBUG_LEVEL1, "NFTL: add_mtd for %s\n", mtd->name);
+
+ nftl = kmalloc(sizeof(struct NFTLrecord), GFP_KERNEL);
++
+ if (!nftl) {
+- printk(KERN_WARNING "Out of memory for NFTL data structures\n");
++ printk(KERN_WARNING "NFTL: out of memory for data structures\n");
+ return;
+ }
++ memset(nftl, 0, sizeof(*nftl));
+
+- init_MUTEX(&nftl->mutex);
+-
+- nftl->mtd = mtd;
++ nftl->mbd.mtd = mtd;
++ nftl->mbd.devnum = -1;
++ nftl->mbd.blksize = 512;
++ nftl->mbd.tr = tr;
+
+ if (NFTL_mount(nftl) < 0) {
+- printk(KERN_WARNING "Could not mount NFTL device\n");
++ printk(KERN_WARNING "NFTL: could not mount device\n");
+ kfree(nftl);
+ return;
+ }
+
+ /* OK, it's a new one. Set up all the data structures. */
+-#ifdef PSYCHO_DEBUG
+- printk("Found new NFTL nftl%c\n", firstfree + 'a');
+-#endif
+
+- /* linux stuff */
+- nftl->usecount = 0;
++ /* Calculate geometry */
+ nftl->cylinders = 1024;
+ nftl->heads = 16;
+
+ temp = nftl->cylinders * nftl->heads;
+- nftl->sectors = nftl->nr_sects / temp;
+- if (nftl->nr_sects % temp) {
++ nftl->sectors = nftl->mbd.size / temp;
++ if (nftl->mbd.size % temp) {
+ nftl->sectors++;
+ temp = nftl->cylinders * nftl->sectors;
+- nftl->heads = nftl->nr_sects / temp;
++ nftl->heads = nftl->mbd.size / temp;
+
+- if (nftl->nr_sects % temp) {
++ if (nftl->mbd.size % temp) {
+ nftl->heads++;
+ temp = nftl->heads * nftl->sectors;
+- nftl->cylinders = nftl->nr_sects / temp;
++ nftl->cylinders = nftl->mbd.size / temp;
+ }
+ }
+
+- if (nftl->nr_sects != nftl->heads * nftl->cylinders * nftl->sectors) {
+- printk(KERN_WARNING "Cannot calculate an NFTL geometry to "
+- "match size of 0x%x.\n", nftl->nr_sects);
+- printk(KERN_WARNING "Using C:%d H:%d S:%d (== 0x%lx sects)\n",
++ if (nftl->mbd.size != nftl->heads * nftl->cylinders * nftl->sectors) {
++ /*
++ Oh no we don't have
++ mbd.size == heads * cylinders * sectors
++ */
++ printk(KERN_WARNING "NFTL: cannot calculate a geometry to "
++ "match size of 0x%lx.\n", nftl->mbd.size);
++ printk(KERN_WARNING "NFTL: using C:%d H:%d S:%d "
++ "(== 0x%lx sects)\n",
+ nftl->cylinders, nftl->heads , nftl->sectors,
+- (long)nftl->cylinders * (long)nftl->heads * (long)nftl->sectors );
+-
+- /* Oh no we don't have nftl->nr_sects = nftl->heads * nftl->cylinders * nftl->sectors; */
++ (long)nftl->cylinders * (long)nftl->heads *
++ (long)nftl->sectors );
+ }
+- NFTLs[firstfree] = nftl;
+- /* Finally, set up the block device sizes */
+- nftl_sizes[firstfree * 16] = nftl->nr_sects;
+- //nftl_blocksizes[firstfree*16] = 512;
+- part_table[firstfree * 16].nr_sects = nftl->nr_sects;
+-
+- nftl_gendisk.nr_real++;
+-
+- /* partition check ... */
+-#if LINUX_VERSION_CODE < 0x20328
+- resetup_one_dev(&nftl_gendisk, firstfree);
+-#else
+- grok_partitions(&nftl_gendisk, firstfree, 1<<NFTL_PARTN_BITS, nftl->nr_sects);
+-#endif
+-}
+-
+-static void NFTL_unsetup(int i)
+-{
+- struct NFTLrecord *nftl = NFTLs[i];
+-
+- DEBUG(MTD_DEBUG_LEVEL1, "NFTL_unsetup %d\n", i);
+-
+- NFTLs[i] = NULL;
+
++ if (add_mtd_blktrans_dev(&nftl->mbd)) {
+ if (nftl->ReplUnitTable)
+ kfree(nftl->ReplUnitTable);
+ if (nftl->EUNtable)
+ kfree(nftl->EUNtable);
+-
+- nftl_gendisk.nr_real--;
+ kfree(nftl);
+-}
+-
+-/* Search the MTD device for NFTL partitions */
+-static void NFTL_notify_add(struct mtd_info *mtd)
+-{
+- DEBUG(MTD_DEBUG_LEVEL1, "NFTL_notify_add for %s\n", mtd->name);
+-
+- if (mtd) {
+- if (!mtd->read_oob) {
+- /* If this MTD doesn't have out-of-band data,
+- then there's no point continuing */
+- DEBUG(MTD_DEBUG_LEVEL1, "No OOB data, quitting\n");
+ return;
+ }
+- DEBUG(MTD_DEBUG_LEVEL3, "mtd->read = %p, size = %d, erasesize = %d\n",
+- mtd->read, mtd->size, mtd->erasesize);
+-
+- NFTL_setup(mtd);
+- }
++#ifdef PSYCHO_DEBUG
++ printk(KERN_INFO "NFTL: Found new nftl%c\n", nftl->mbd.devnum + 'a');
++#endif
+ }
+
+-static void NFTL_notify_remove(struct mtd_info *mtd)
++static void nftl_remove_dev(struct mtd_blktrans_dev *dev)
+ {
+- int i;
++ struct NFTLrecord *nftl = (void *)dev;
+
+- for (i = 0; i < MAX_NFTLS; i++) {
+- if (NFTLs[i] && NFTLs[i]->mtd == mtd)
+- NFTL_unsetup(i);
+- }
++ DEBUG(MTD_DEBUG_LEVEL1, "NFTL: remove_dev (i=%d)\n", dev->devnum);
++
++ del_mtd_blktrans_dev(dev);
++ if (nftl->ReplUnitTable)
++ kfree(nftl->ReplUnitTable);
++ if (nftl->EUNtable)
++ kfree(nftl->EUNtable);
++ kfree(nftl);
+ }
+
+ #ifdef CONFIG_NFTL_RW
+@@ -303,7 +209,7 @@
+
+ targetEUN = thisEUN;
+ for (block = 0; block < nftl->EraseSize / 512; block ++) {
+- MTD_READOOB(nftl->mtd,
++ MTD_READOOB(nftl->mbd.mtd,
+ (thisEUN * nftl->EraseSize) + (block * 512),
+ 16 , &retlen, (char *)&oob);
+ if (block == 2) {
+@@ -420,7 +326,7 @@
+ chain by selecting the longer one */
+ oob.u.c.FoldMark = oob.u.c.FoldMark1 = cpu_to_le16(FOLD_MARK_IN_PROGRESS);
+ oob.u.c.unused = 0xffffffff;
+- MTD_WRITEOOB(nftl->mtd, (nftl->EraseSize * targetEUN) + 2 * 512 + 8,
++ MTD_WRITEOOB(nftl->mbd.mtd, (nftl->EraseSize * targetEUN) + 2 * 512 + 8,
+ 8, &retlen, (char *)&oob.u);
+ }
+
+@@ -444,16 +350,16 @@
+ if (BlockMap[block] == BLOCK_NIL)
+ continue;
+
+- ret = MTD_READECC(nftl->mtd, (nftl->EraseSize * BlockMap[block]) + (block * 512),
++ ret = MTD_READECC(nftl->mbd.mtd, (nftl->EraseSize * BlockMap[block]) + (block * 512),
+ 512, &retlen, movebuf, (char *)&oob, NAND_ECC_DISKONCHIP);
+ if (ret < 0) {
+- ret = MTD_READECC(nftl->mtd, (nftl->EraseSize * BlockMap[block])
++ ret = MTD_READECC(nftl->mbd.mtd, (nftl->EraseSize * BlockMap[block])
+ + (block * 512), 512, &retlen,
+ movebuf, (char *)&oob, NAND_ECC_DISKONCHIP);
+ if (ret != -EIO)
+ printk("Error went away on retry.\n");
+ }
+- MTD_WRITEECC(nftl->mtd, (nftl->EraseSize * targetEUN) + (block * 512),
++ MTD_WRITEECC(nftl->mbd.mtd, (nftl->EraseSize * targetEUN) + (block * 512),
+ 512, &retlen, movebuf, (char *)&oob, NAND_ECC_DISKONCHIP);
+ }
+
+@@ -462,7 +368,7 @@
+ = cpu_to_le16(thisVUC);
+ oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum = 0xffff;
+
+- MTD_WRITEOOB(nftl->mtd, (nftl->EraseSize * targetEUN) + 8,
++ MTD_WRITEOOB(nftl->mbd.mtd, (nftl->EraseSize * targetEUN) + 8,
+ 8, &retlen, (char *)&oob.u);
+
+ /* OK. We've moved the whole lot into the new block. Now we have to free the original blocks. */
+@@ -582,7 +488,7 @@
+
+ lastEUN = writeEUN;
+
+- MTD_READOOB(nftl->mtd, (writeEUN * nftl->EraseSize) + blockofs,
++ MTD_READOOB(nftl->mbd.mtd, (writeEUN * nftl->EraseSize) + blockofs,
+ 8, &retlen, (char *)&bci);
+
+ DEBUG(MTD_DEBUG_LEVEL2, "Status of block %d in EUN %d is %x\n",
+@@ -670,12 +576,12 @@
+ nftl->ReplUnitTable[writeEUN] = BLOCK_NIL;
+
+ /* ... and on the flash itself */
+- MTD_READOOB(nftl->mtd, writeEUN * nftl->EraseSize + 8, 8,
++ MTD_READOOB(nftl->mbd.mtd, writeEUN * nftl->EraseSize + 8, 8,
+ &retlen, (char *)&oob.u);
+
+ oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC);
+
+- MTD_WRITEOOB(nftl->mtd, writeEUN * nftl->EraseSize + 8, 8,
++ MTD_WRITEOOB(nftl->mbd.mtd, writeEUN * nftl->EraseSize + 8, 8,
+ &retlen, (char *)&oob.u);
+
+ /* we link the new block to the chain only after the
+@@ -685,13 +591,13 @@
+ /* Both in our cache... */
+ nftl->ReplUnitTable[lastEUN] = writeEUN;
+ /* ... and on the flash itself */
+- MTD_READOOB(nftl->mtd, (lastEUN * nftl->EraseSize) + 8,
++ MTD_READOOB(nftl->mbd.mtd, (lastEUN * nftl->EraseSize) + 8,
+ 8, &retlen, (char *)&oob.u);
+
+ oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum
+ = cpu_to_le16(writeEUN);
+
+- MTD_WRITEOOB(nftl->mtd, (lastEUN * nftl->EraseSize) + 8,
++ MTD_WRITEOOB(nftl->mbd.mtd, (lastEUN * nftl->EraseSize) + 8,
+ 8, &retlen, (char *)&oob.u);
+ }
+
+@@ -704,8 +610,10 @@
+ return 0xffff;
+ }
+
+-static int NFTL_writeblock(struct NFTLrecord *nftl, unsigned block, char *buffer)
++static int nftl_writeblock(struct mtd_blktrans_dev *mbd, unsigned long block,
++ char *buffer)
+ {
++ struct NFTLrecord *nftl = (void *)mbd;
+ u16 writeEUN;
+ unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1);
+ size_t retlen;
+@@ -720,7 +628,7 @@
+ return 1;
+ }
+
+- MTD_WRITEECC(nftl->mtd, (writeEUN * nftl->EraseSize) + blockofs,
++ MTD_WRITEECC(nftl->mbd.mtd, (writeEUN * nftl->EraseSize) + blockofs,
+ 512, &retlen, (char *)buffer, (char *)eccbuf, NAND_ECC_DISKONCHIP);
+ /* no need to write SECTOR_USED flags since they are written in mtd_writeecc */
+
+@@ -728,8 +636,10 @@
+ }
+ #endif /* CONFIG_NFTL_RW */
+
+-static int NFTL_readblock(struct NFTLrecord *nftl, unsigned block, char *buffer)
++static int nftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block,
++ char *buffer)
+ {
++ struct NFTLrecord *nftl = (void *)mbd;
+ u16 lastgoodEUN;
+ u16 thisEUN = nftl->EUNtable[block / (nftl->EraseSize / 512)];
+ unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1);
+@@ -742,7 +652,7 @@
+
+ if (thisEUN != BLOCK_NIL) {
+ while (thisEUN < nftl->nb_blocks) {
+- if (MTD_READOOB(nftl->mtd, (thisEUN * nftl->EraseSize) + blockofs,
++ if (MTD_READOOB(nftl->mbd.mtd, (thisEUN * nftl->EraseSize) + blockofs,
+ 8, &retlen, (char *)&bci) < 0)
+ status = SECTOR_IGNORE;
+ else
+@@ -761,13 +671,13 @@
+ case SECTOR_IGNORE:
+ break;
+ default:
+- printk("Unknown status for block %d in EUN %d: %x\n",
++ printk("Unknown status for block %ld in EUN %d: %x\n",
+ block, thisEUN, status);
+ break;
+ }
+
+ if (!silly--) {
+- printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%x\n",
++ printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%lx\n",
+ block / (nftl->EraseSize / 512));
+ return 1;
+ }
+@@ -783,264 +693,22 @@
+ loff_t ptr = (lastgoodEUN * nftl->EraseSize) + blockofs;
+ size_t retlen;
+ u_char eccbuf[6];
+- if (MTD_READECC(nftl->mtd, ptr, 512, &retlen, buffer, eccbuf, NAND_ECC_DISKONCHIP))
++ if (MTD_READECC(nftl->mbd.mtd, ptr, 512, &retlen, buffer, eccbuf, NAND_ECC_DISKONCHIP))
+ return -EIO;
+ }
+ return 0;
+ }
+
+-static int nftl_ioctl(struct inode * inode, struct file * file, unsigned int cmd, unsigned long arg)
+-{
+- struct NFTLrecord *nftl;
+- int p;
+-
+- nftl = NFTLs[MINOR(inode->i_rdev) >> NFTL_PARTN_BITS];
+-
+- if (!nftl) return -EINVAL;
+-
+- switch (cmd) {
+- case HDIO_GETGEO: {
+- struct hd_geometry g;
+-
+- g.heads = nftl->heads;
+- g.sectors = nftl->sectors;
+- g.cylinders = nftl->cylinders;
+- g.start = part_table[MINOR(inode->i_rdev)].start_sect;
+- return copy_to_user((void *)arg, &g, sizeof g) ? -EFAULT : 0;
+- }
+- case BLKGETSIZE: /* Return device size */
+- return put_user(part_table[MINOR(inode->i_rdev)].nr_sects,
+- (unsigned long *) arg);
+-
+-#ifdef BLKGETSIZE64
+- case BLKGETSIZE64:
+- return put_user((u64)part_table[MINOR(inode->i_rdev)].nr_sects << 9,
+- (u64 *)arg);
+-#endif
+-
+- case BLKFLSBUF:
+- if (!capable(CAP_SYS_ADMIN)) return -EACCES;
+- fsync_dev(inode->i_rdev);
+- invalidate_buffers(inode->i_rdev);
+- if (nftl->mtd->sync)
+- nftl->mtd->sync(nftl->mtd);
+- return 0;
+-
+- case BLKRRPART:
+- if (!capable(CAP_SYS_ADMIN)) return -EACCES;
+- if (nftl->usecount > 1) return -EBUSY;
+- /*
+- * We have to flush all buffers and invalidate caches,
+- * or we won't be able to re-use the partitions,
+- * if there was a change and we don't want to reboot
+- */
+- p = (1<<NFTL_PARTN_BITS) - 1;
+- while (p-- > 0) {
+- kdev_t devp = MKDEV(MAJOR(inode->i_dev), MINOR(inode->i_dev)+p);
+- if (part_table[p].nr_sects > 0)
+- invalidate_device (devp, 1);
+-
+- part_table[MINOR(inode->i_dev)+p].start_sect = 0;
+- part_table[MINOR(inode->i_dev)+p].nr_sects = 0;
+- }
+-
+-#if LINUX_VERSION_CODE < 0x20328
+- resetup_one_dev(&nftl_gendisk, MINOR(inode->i_rdev) >> NFTL_PARTN_BITS);
+-#else
+- grok_partitions(&nftl_gendisk, MINOR(inode->i_rdev) >> NFTL_PARTN_BITS,
+- 1<<NFTL_PARTN_BITS, nftl->nr_sects);
+-#endif
+- return 0;
+-
+-#if (LINUX_VERSION_CODE < 0x20303)
+- RO_IOCTLS(inode->i_rdev, arg); /* ref. linux/blk.h */
+-#else
+- case BLKROSET:
+- case BLKROGET:
+- case BLKSSZGET:
+- return blk_ioctl(inode->i_rdev, cmd, arg);
+-#endif
+-
+- default:
+- return -EINVAL;
+- }
+-}
+-
+-void nftl_request(RQFUNC_ARG)
+-{
+- unsigned int dev, block, nsect;
+- struct NFTLrecord *nftl;
+- char *buffer;
+- struct request *req;
+- int res;
+-
+- while (1) {
+- INIT_REQUEST; /* blk.h */
+- req = CURRENT;
+-
+- /* We can do this because the generic code knows not to
+- touch the request at the head of the queue */
+- spin_unlock_irq(&io_request_lock);
+-
+- DEBUG(MTD_DEBUG_LEVEL2, "NFTL_request\n");
+- DEBUG(MTD_DEBUG_LEVEL3, "NFTL %s request, from sector 0x%04lx for 0x%04lx sectors\n",
+- (req->cmd == READ) ? "Read " : "Write",
+- req->sector, req->current_nr_sectors);
+-
+- dev = MINOR(req->rq_dev);
+- block = req->sector;
+- nsect = req->current_nr_sectors;
+- buffer = req->buffer;
+- res = 1; /* succeed */
+-
+- if (dev >= MAX_NFTLS * (1<<NFTL_PARTN_BITS)) {
+- /* there is no such partition */
+- printk("nftl: bad minor number: device = %s\n",
+- kdevname(req->rq_dev));
+- res = 0; /* fail */
+- goto repeat;
+- }
+-
+- nftl = NFTLs[dev / (1<<NFTL_PARTN_BITS)];
+- DEBUG(MTD_DEBUG_LEVEL3, "Waiting for mutex\n");
+- down(&nftl->mutex);
+- DEBUG(MTD_DEBUG_LEVEL3, "Got mutex\n");
+-
+- if (block + nsect > part_table[dev].nr_sects) {
+- /* access past the end of device */
+- printk("nftl%c%d: bad access: block = %d, count = %d\n",
+- (MINOR(req->rq_dev)>>6)+'a', dev & 0xf, block, nsect);
+- up(&nftl->mutex);
+- res = 0; /* fail */
+- goto repeat;
+- }
+-
+- block += part_table[dev].start_sect;
+-
+- if (req->cmd == READ) {
+- DEBUG(MTD_DEBUG_LEVEL2, "NFTL read request of 0x%x sectors @ %x "
+- "(req->nr_sectors == %lx)\n", nsect, block, req->nr_sectors);
+-
+- for ( ; nsect > 0; nsect-- , block++, buffer += 512) {
+- /* Read a single sector to req->buffer + (512 * i) */
+- if (NFTL_readblock(nftl, block, buffer)) {
+- DEBUG(MTD_DEBUG_LEVEL2, "NFTL read request failed\n");
+- up(&nftl->mutex);
+- res = 0;
+- goto repeat;
+- }
+- }
+-
+- DEBUG(MTD_DEBUG_LEVEL2,"NFTL read request completed OK\n");
+- up(&nftl->mutex);
+- goto repeat;
+- } else if (req->cmd == WRITE) {
+- DEBUG(MTD_DEBUG_LEVEL2, "NFTL write request of 0x%x sectors @ %x "
+- "(req->nr_sectors == %lx)\n", nsect, block,
+- req->nr_sectors);
+-#ifdef CONFIG_NFTL_RW
+- for ( ; nsect > 0; nsect-- , block++, buffer += 512) {
+- /* Read a single sector to req->buffer + (512 * i) */
+- if (NFTL_writeblock(nftl, block, buffer)) {
+- DEBUG(MTD_DEBUG_LEVEL1,"NFTL write request failed\n");
+- up(&nftl->mutex);
+- res = 0;
+- goto repeat;
+- }
+- }
+- DEBUG(MTD_DEBUG_LEVEL2,"NFTL write request completed OK\n");
+-#else
+- res = 0; /* Writes always fail */
+-#endif /* CONFIG_NFTL_RW */
+- up(&nftl->mutex);
+- goto repeat;
+- } else {
+- DEBUG(MTD_DEBUG_LEVEL0, "NFTL unknown request\n");
+- up(&nftl->mutex);
+- res = 0;
+- goto repeat;
+- }
+- repeat:
+- DEBUG(MTD_DEBUG_LEVEL3, "end_request(%d)\n", res);
+- spin_lock_irq(&io_request_lock);
+- end_request(res);
+- }
+-}
+-
+-static int nftl_open(struct inode *ip, struct file *fp)
+-{
+- int nftlnum = MINOR(ip->i_rdev) >> NFTL_PARTN_BITS;
+- struct NFTLrecord *thisNFTL;
+- thisNFTL = NFTLs[nftlnum];
+-
+- DEBUG(MTD_DEBUG_LEVEL2,"NFTL_open\n");
+-
+-#ifdef CONFIG_KMOD
+- if (!thisNFTL && nftlnum == 0) {
+- request_module("docprobe");
+- thisNFTL = NFTLs[nftlnum];
+- }
+-#endif
+- if (!thisNFTL) {
+- DEBUG(MTD_DEBUG_LEVEL2,"ENODEV: thisNFTL = %d, minor = %d, ip = %p, fp = %p\n",
+- nftlnum, ip->i_rdev, ip, fp);
+- return -ENODEV;
+- }
+-
+-#ifndef CONFIG_NFTL_RW
+- if (fp->f_mode & FMODE_WRITE)
+- return -EROFS;
+-#endif /* !CONFIG_NFTL_RW */
+-
+- thisNFTL->usecount++;
+- BLK_INC_USE_COUNT;
+- if (!get_mtd_device(thisNFTL->mtd, -1)) {
+- BLK_DEC_USE_COUNT;
+- return -ENXIO;
+- }
+-
+- return 0;
+-}
+-
+-static int nftl_release(struct inode *inode, struct file *fp)
++static int nftl_getgeo(struct mtd_blktrans_dev *dev, struct hd_geometry *geo)
+ {
+- struct NFTLrecord *thisNFTL;
+-
+- thisNFTL = NFTLs[MINOR(inode->i_rdev) / 16];
+-
+- DEBUG(MTD_DEBUG_LEVEL2, "NFTL_release\n");
+-
+- if (thisNFTL->mtd->sync)
+- thisNFTL->mtd->sync(thisNFTL->mtd);
+- thisNFTL->usecount--;
+- BLK_DEC_USE_COUNT;
++ struct NFTLrecord *nftl = (void *)dev;
+
+- put_mtd_device(thisNFTL->mtd);
++ geo->heads = nftl->heads;
++ geo->sectors = nftl->sectors;
++ geo->cylinders = nftl->cylinders;
+
+ return 0;
+ }
+-#if LINUX_VERSION_CODE < 0x20326
+-static struct file_operations nftl_fops = {
+- read: block_read,
+- write: block_write,
+- ioctl: nftl_ioctl,
+- open: nftl_open,
+- release: nftl_release,
+- fsync: block_fsync,
+-};
+-#else
+-static struct block_device_operations nftl_fops =
+-{
+-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,14)
+- owner: THIS_MODULE,
+-#endif
+- open: nftl_open,
+- release: nftl_release,
+- ioctl: nftl_ioctl
+-};
+-#endif
+-
+-
+
+ /****************************************************************************
+ *
+@@ -1048,49 +716,33 @@
+ *
+ ****************************************************************************/
+
+-static struct mtd_notifier nftl_notifier = {
+- add: NFTL_notify_add,
+- remove: NFTL_notify_remove
++
++struct mtd_blktrans_ops nftl_tr = {
++ .name = "nftl",
++ .major = NFTL_MAJOR,
++ .part_bits = NFTL_PARTN_BITS,
++ .getgeo = nftl_getgeo,
++ .readsect = nftl_readblock,
++#ifdef CONFIG_NFTL_RW
++ .writesect = nftl_writeblock,
++#endif
++ .add_mtd = nftl_add_mtd,
++ .remove_dev = nftl_remove_dev,
++ .owner = THIS_MODULE,
+ };
+
+ extern char nftlmountrev[];
+
+ int __init init_nftl(void)
+ {
+- int i;
+-
+-#ifdef PRERELEASE
+- printk(KERN_INFO "NFTL driver: nftlcore.c $Revision$, nftlmount.c %s\n", nftlmountrev);
+-#endif
+-
+- if (register_blkdev(MAJOR_NR, "nftl", &nftl_fops)){
+- printk("unable to register NFTL block device on major %d\n", MAJOR_NR);
+- return -EBUSY;
+- } else {
+- blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), &nftl_request);
++ printk(KERN_INFO "NFTL driver: nftlcore.c $Revision$, nftlmount.c %s\n", nftlmountrev);
+
+- /* set block size to 1kB each */
+- for (i = 0; i < 256; i++) {
+- nftl_blocksizes[i] = 1024;
+- }
+- blksize_size[MAJOR_NR] = nftl_blocksizes;
+-
+- add_gendisk(&nftl_gendisk);
+- }
+-
+- register_mtd_user(&nftl_notifier);
+-
+- return 0;
++ return register_mtd_blktrans(&nftl_tr);
+ }
+
+ static void __exit cleanup_nftl(void)
+ {
+- unregister_mtd_user(&nftl_notifier);
+- unregister_blkdev(MAJOR_NR, "nftl");
+-
+- blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
+-
+- del_gendisk(&nftl_gendisk);
++ deregister_mtd_blktrans(&nftl_tr);
+ }
+
+ module_init(init_nftl);
+diff -Nurb linux-mips-2.4.27/drivers/mtd/nftlmount.c linux/drivers/mtd/nftlmount.c
+--- linux-mips-2.4.27/drivers/mtd/nftlmount.c 2003-07-05 05:23:38.000000000 +0200
++++ linux/drivers/mtd/nftlmount.c 2004-11-19 10:25:11.655236880 +0100
+@@ -4,7 +4,7 @@
+ * Author: Fabrice Bellard (fabrice.bellard@netgem.com)
+ * Copyright (C) 2000 Netgem S.A.
+ *
+- * $Id$
++ * $Id$
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -21,26 +21,17 @@
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+-#define __NO_VERSION__
+ #include <linux/kernel.h>
+-#include <linux/module.h>
+ #include <asm/errno.h>
+-#include <asm/io.h>
+-#include <asm/uaccess.h>
+-#include <linux/miscdevice.h>
+-#include <linux/pci.h>
+ #include <linux/delay.h>
+ #include <linux/slab.h>
+-#include <linux/sched.h>
+-#include <linux/init.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/nand.h>
+ #include <linux/mtd/nftl.h>
+-#include <linux/mtd/compatmac.h>
+
+ #define SECTORSIZE 512
+
+-char nftlmountrev[]="$Revision$";
++char nftlmountrev[]="$Revision$";
+
+ /* find_boot_record: Find the NFTL Media Header and its Spare copy which contains the
+ * various device information of the NFTL partition and Bad Unit Table. Update
+@@ -59,8 +50,8 @@
+
+ /* Assume logical EraseSize == physical erasesize for starting the scan.
+ We'll sort it out later if we find a MediaHeader which says otherwise */
+- nftl->EraseSize = nftl->mtd->erasesize;
+- nftl->nb_blocks = nftl->mtd->size / nftl->EraseSize;
++ nftl->EraseSize = nftl->mbd.mtd->erasesize;
++ nftl->nb_blocks = nftl->mbd.mtd->size / nftl->EraseSize;
+
+ nftl->MediaUnit = BLOCK_NIL;
+ nftl->SpareMediaUnit = BLOCK_NIL;
+@@ -71,12 +62,12 @@
+
+ /* Check for ANAND header first. Then can whinge if it's found but later
+ checks fail */
+- if ((ret = MTD_READ(nftl->mtd, block * nftl->EraseSize, SECTORSIZE, &retlen, buf))) {
++ if ((ret = MTD_READ(nftl->mbd.mtd, block * nftl->EraseSize, SECTORSIZE, &retlen, buf))) {
+ static int warncount = 5;
+
+ if (warncount) {
+ printk(KERN_WARNING "Block read at 0x%x of mtd%d failed: %d\n",
+- block * nftl->EraseSize, nftl->mtd->index, ret);
++ block * nftl->EraseSize, nftl->mbd.mtd->index, ret);
+ if (!--warncount)
+ printk(KERN_WARNING "Further failures for this block will not be printed\n");
+ }
+@@ -87,16 +78,16 @@
+ /* ANAND\0 not found. Continue */
+ #if 0
+ printk(KERN_DEBUG "ANAND header not found at 0x%x in mtd%d\n",
+- block * nftl->EraseSize, nftl->mtd->index);
++ block * nftl->EraseSize, nftl->mbd.mtd->index);
+ #endif
+ continue;
+ }
+
+ /* To be safer with BIOS, also use erase mark as discriminant */
+- if ((ret = MTD_READOOB(nftl->mtd, block * nftl->EraseSize + SECTORSIZE + 8,
+- 8, &retlen, (char *)&h1)) < 0) {
++ if ((ret = MTD_READOOB(nftl->mbd.mtd, block * nftl->EraseSize + SECTORSIZE + 8,
++ 8, &retlen, (char *)&h1) < 0)) {
+ printk(KERN_WARNING "ANAND header found at 0x%x in mtd%d, but OOB data read failed (err %d)\n",
+- block * nftl->EraseSize, nftl->mtd->index, ret);
++ block * nftl->EraseSize, nftl->mbd.mtd->index, ret);
+ continue;
+ }
+
+@@ -106,23 +97,23 @@
+ */
+ if (le16_to_cpu(h1.EraseMark | h1.EraseMark1) != ERASE_MARK) {
+ printk(KERN_NOTICE "ANAND header found at 0x%x in mtd%d, but erase mark not present (0x%04x,0x%04x instead)\n",
+- block * nftl->EraseSize, nftl->mtd->index,
++ block * nftl->EraseSize, nftl->mbd.mtd->index,
+ le16_to_cpu(h1.EraseMark), le16_to_cpu(h1.EraseMark1));
+ continue;
+ }
+
+ /* Finally reread to check ECC */
+- if ((ret = MTD_READECC(nftl->mtd, block * nftl->EraseSize, SECTORSIZE,
+- &retlen, buf, (char *)&oob, NAND_ECC_DISKONCHIP)) < 0) {
++ if ((ret = MTD_READECC(nftl->mbd.mtd, block * nftl->EraseSize, SECTORSIZE,
++ &retlen, buf, (char *)&oob, NAND_ECC_DISKONCHIP) < 0)) {
+ printk(KERN_NOTICE "ANAND header found at 0x%x in mtd%d, but ECC read failed (err %d)\n",
+- block * nftl->EraseSize, nftl->mtd->index, ret);
++ block * nftl->EraseSize, nftl->mbd.mtd->index, ret);
+ continue;
+ }
+
+ /* Paranoia. Check the ANAND header is still there after the ECC read */
+ if (memcmp(buf, "ANAND", 6)) {
+ printk(KERN_NOTICE "ANAND header found at 0x%x in mtd%d, but went away on reread!\n",
+- block * nftl->EraseSize, nftl->mtd->index);
++ block * nftl->EraseSize, nftl->mbd.mtd->index);
+ printk(KERN_NOTICE "New data are: %02x %02x %02x %02x %02x %02x\n",
+ buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]);
+ continue;
+@@ -137,8 +128,12 @@
+ printk(KERN_NOTICE "NFTL Media Headers at 0x%x and 0x%x disagree.\n",
+ nftl->MediaUnit * nftl->EraseSize, block * nftl->EraseSize);
+ /* if (debug) Print both side by side */
++ if (boot_record_count < 2) {
++ /* We haven't yet seen two real ones */
+ return -1;
+ }
++ continue;
++ }
+ if (boot_record_count == 1)
+ nftl->SpareMediaUnit = block;
+
+@@ -163,8 +158,8 @@
+ } else if (mh->UnitSizeFactor != 0xff) {
+ printk(KERN_NOTICE "WARNING: Support for NFTL with UnitSizeFactor 0x%02x is experimental\n",
+ mh->UnitSizeFactor);
+- nftl->EraseSize = nftl->mtd->erasesize << (0xff - mh->UnitSizeFactor);
+- nftl->nb_blocks = nftl->mtd->size / nftl->EraseSize;
++ nftl->EraseSize = nftl->mbd.mtd->erasesize << (0xff - mh->UnitSizeFactor);
++ nftl->nb_blocks = nftl->mbd.mtd->size / nftl->EraseSize;
+ }
+ nftl->nb_boot_blocks = le16_to_cpu(mh->FirstPhysicalEUN);
+ if ((nftl->nb_boot_blocks + 2) >= nftl->nb_blocks) {
+@@ -182,7 +177,7 @@
+ return -1;
+ }
+
+- nftl->nr_sects = nftl->numvunits * (nftl->EraseSize / SECTORSIZE);
++ nftl->mbd.size = nftl->numvunits * (nftl->EraseSize / SECTORSIZE);
+
+ /* If we're not using the last sectors in the device for some reason,
+ reduce nb_blocks accordingly so we forget they're there */
+@@ -220,7 +215,7 @@
+ for (i = 0; i < nftl->nb_blocks; i++) {
+ if ((i & (SECTORSIZE - 1)) == 0) {
+ /* read one sector for every SECTORSIZE of blocks */
+- if ((ret = MTD_READECC(nftl->mtd, block * nftl->EraseSize +
++ if ((ret = MTD_READECC(nftl->mbd.mtd, block * nftl->EraseSize +
+ i + SECTORSIZE, SECTORSIZE, &retlen, buf,
+ (char *)&oob, NAND_ECC_DISKONCHIP)) < 0) {
+ printk(KERN_NOTICE "Read of bad sector table failed (err %d)\n",
+@@ -263,16 +258,16 @@
+ for (i = 0; i < len; i += SECTORSIZE) {
+ /* we want to read the sector without ECC check here since a free
+ sector does not have ECC syndrome on it yet */
+- if (MTD_READ(nftl->mtd, address, SECTORSIZE, &retlen, buf) < 0)
++ if (MTD_READ(nftl->mbd.mtd, address, SECTORSIZE, &retlen, buf) < 0)
+ return -1;
+ if (memcmpb(buf, 0xff, SECTORSIZE) != 0)
+ return -1;
+
+ if (check_oob) {
+- if (MTD_READOOB(nftl->mtd, address, nftl->mtd->oobsize,
++ if (MTD_READOOB(nftl->mbd.mtd, address, nftl->mbd.mtd->oobsize,
+ &retlen, buf) < 0)
+ return -1;
+- if (memcmpb(buf, 0xff, nftl->mtd->oobsize) != 0)
++ if (memcmpb(buf, 0xff, nftl->mbd.mtd->oobsize) != 0)
+ return -1;
+ }
+ address += SECTORSIZE;
+@@ -297,7 +292,7 @@
+ struct erase_info *instr = &nftl->instr;
+
+ /* Read the Unit Control Information #1 for Wear-Leveling */
+- if (MTD_READOOB(nftl->mtd, block * nftl->EraseSize + SECTORSIZE + 8,
++ if (MTD_READOOB(nftl->mbd.mtd, block * nftl->EraseSize + SECTORSIZE + 8,
+ 8, &retlen, (char *)&uci) < 0)
+ goto default_uci1;
+
+@@ -314,7 +309,7 @@
+ /* XXX: use async erase interface, XXX: test return code */
+ instr->addr = block * nftl->EraseSize;
+ instr->len = nftl->EraseSize;
+- MTD_ERASE(nftl->mtd, instr);
++ MTD_ERASE(nftl->mbd.mtd, instr);
+
+ if (instr->state == MTD_ERASE_FAILED) {
+ /* could not format, FixMe: We should update the BadUnitTable
+@@ -337,7 +332,7 @@
+ return -1;
+
+ uci.WearInfo = le32_to_cpu(nb_erases);
+- if (MTD_WRITEOOB(nftl->mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8,
++ if (MTD_WRITEOOB(nftl->mbd.mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8,
+ &retlen, (char *)&uci) < 0)
+ return -1;
+ return 0;
+@@ -363,7 +358,7 @@
+ block = first_block;
+ for (;;) {
+ for (i = 0; i < sectors_per_block; i++) {
+- if (MTD_READOOB(nftl->mtd, block * nftl->EraseSize + i * SECTORSIZE,
++ if (MTD_READOOB(nftl->mbd.mtd, block * nftl->EraseSize + i * SECTORSIZE,
+ 8, &retlen, (char *)&bci) < 0)
+ status = SECTOR_IGNORE;
+ else
+@@ -383,7 +378,7 @@
+ /* sector not free actually : mark it as SECTOR_IGNORE */
+ bci.Status = SECTOR_IGNORE;
+ bci.Status1 = SECTOR_IGNORE;
+- MTD_WRITEOOB(nftl->mtd,
++ MTD_WRITEOOB(nftl->mbd.mtd,
+ block * nftl->EraseSize + i * SECTORSIZE,
+ 8, &retlen, (char *)&bci);
+ }
+@@ -476,7 +471,7 @@
+ size_t retlen;
+
+ /* check erase mark. */
+- if (MTD_READOOB(nftl->mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8,
++ if (MTD_READOOB(nftl->mbd.mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8,
+ &retlen, (char *)&h1) < 0)
+ return -1;
+
+@@ -491,7 +486,7 @@
+ h1.EraseMark = cpu_to_le16(ERASE_MARK);
+ h1.EraseMark1 = cpu_to_le16(ERASE_MARK);
+ h1.WearInfo = cpu_to_le32(0);
+- if (MTD_WRITEOOB(nftl->mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8,
++ if (MTD_WRITEOOB(nftl->mbd.mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8,
+ &retlen, (char *)&h1) < 0)
+ return -1;
+ } else {
+@@ -503,7 +498,7 @@
+ SECTORSIZE, 0) != 0)
+ return -1;
+
+- if (MTD_READOOB(nftl->mtd, block * nftl->EraseSize + i,
++ if (MTD_READOOB(nftl->mbd.mtd, block * nftl->EraseSize + i,
+ 16, &retlen, buf) < 0)
+ return -1;
+ if (i == SECTORSIZE) {
+@@ -533,7 +528,7 @@
+ struct nftl_uci2 uci;
+ size_t retlen;
+
+- if (MTD_READOOB(nftl->mtd, block * nftl->EraseSize + 2 * SECTORSIZE + 8,
++ if (MTD_READOOB(nftl->mbd.mtd, block * nftl->EraseSize + 2 * SECTORSIZE + 8,
+ 8, &retlen, (char *)&uci) < 0)
+ return 0;
+
+@@ -572,9 +567,9 @@
+
+ for (;;) {
+ /* read the block header. If error, we format the chain */
+- if (MTD_READOOB(s->mtd, block * s->EraseSize + 8, 8,
++ if (MTD_READOOB(s->mbd.mtd, block * s->EraseSize + 8, 8,
+ &retlen, (char *)&h0) < 0 ||
+- MTD_READOOB(s->mtd, block * s->EraseSize + SECTORSIZE + 8, 8,
++ MTD_READOOB(s->mbd.mtd, block * s->EraseSize + SECTORSIZE + 8, 8,
+ &retlen, (char *)&h1) < 0) {
+ s->ReplUnitTable[block] = BLOCK_NIL;
+ do_format_chain = 1;
+diff -Nurb linux-mips-2.4.27/drivers/mtd/redboot.c linux/drivers/mtd/redboot.c
+--- linux-mips-2.4.27/drivers/mtd/redboot.c 2001-12-02 12:34:42.000000000 +0100
++++ linux/drivers/mtd/redboot.c 2004-11-19 10:25:11.656236728 +0100
+@@ -1,5 +1,5 @@
+ /*
+- * $Id$
++ * $Id$
+ *
+ * Parse RedBoot-style Flash Image System (FIS) tables and
+ * produce a Linux partition array to match.
+@@ -7,6 +7,7 @@
+
+ #include <linux/kernel.h>
+ #include <linux/slab.h>
++#include <linux/init.h>
+
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/partitions.h>
+@@ -34,7 +35,9 @@
+ return 1;
+ }
+
+-int parse_redboot_partitions(struct mtd_info *master, struct mtd_partition **pparts)
++static int parse_redboot_partitions(struct mtd_info *master,
++ struct mtd_partition **pparts,
++ unsigned long fis_origin)
+ {
+ int nrparts = 0;
+ struct fis_image_desc *buf;
+@@ -43,7 +46,9 @@
+ int ret, i;
+ size_t retlen;
+ char *names;
++ char *nullname;
+ int namelen = 0;
++ static char nullstring[] = "unallocated";
+
+ buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
+
+@@ -90,7 +95,11 @@
+ goto out;
+ }
+ new_fl->img = &buf[i];
++ if (fis_origin) {
++ buf[i].flash_base -= fis_origin;
++ } else {
+ buf[i].flash_base &= master->size-1;
++ }
+
+ /* I'm sure the JFFS2 code has done me permanent damage.
+ * I now think the following is _normal_
+@@ -110,18 +119,24 @@
+ if (tmp_fl->img->flash_base + tmp_fl->img->size + master->erasesize < tmp_fl->next->img->flash_base)
+ nrparts++;
+ }
+- parts = kmalloc(sizeof(*parts)*nrparts + namelen, GFP_KERNEL);
++ parts = kmalloc(sizeof(*parts)*nrparts + sizeof(nullstring) + namelen, GFP_KERNEL);
+
+ if (!parts) {
+ ret = -ENOMEM;
+ goto out;
+ }
+- names = (char *)&parts[nrparts];
++
+ memset(parts, 0, sizeof(*parts)*nrparts + namelen);
++
++ /* FIXME: Include nullname only if it's used */
++ nullname = (char *)&parts[nrparts];
++ sprintf(nullname, nullstring);
++ names = nullname + sizeof(nullstring);
++
+ i=0;
+
+ if (fl->img->flash_base) {
+- parts[0].name = "unallocated space";
++ parts[0].name = nullname;
+ parts[0].size = fl->img->flash_base;
+ parts[0].offset = 0;
+ }
+@@ -133,11 +148,11 @@
+ strcpy(names, fl->img->name);
+ names += strlen(names)+1;
+
+- if(fl->next && fl->img->flash_base + fl->img->size + master->erasesize < fl->next->img->flash_base) {
++ if(fl->next && fl->img->flash_base + fl->img->size + master->erasesize <= fl->next->img->flash_base) {
+ i++;
+ parts[i].offset = parts[i-1].size + parts[i-1].offset;
+ parts[i].size = fl->next->img->flash_base - parts[i].offset;
+- parts[i].name = "unallocated space";
++ parts[i].name = nullname;
+ }
+ tmp_fl = fl;
+ fl = fl->next;
+@@ -155,7 +170,24 @@
+ return ret;
+ }
+
+-EXPORT_SYMBOL(parse_redboot_partitions);
++static struct mtd_part_parser redboot_parser = {
++ .owner = THIS_MODULE,
++ .parse_fn = parse_redboot_partitions,
++ .name = "RedBoot",
++};
++
++static int __init redboot_parser_init(void)
++{
++ return register_mtd_parser(&redboot_parser);
++}
++
++static void __exit redboot_parser_exit(void)
++{
++ deregister_mtd_parser(&redboot_parser);
++}
++
++module_init(redboot_parser_init);
++module_exit(redboot_parser_exit);
+
+ MODULE_LICENSE("GPL");
+ MODULE_AUTHOR("Red Hat, Inc. - David Woodhouse <dwmw2@cambridge.redhat.com>");
+diff -Nurb linux-mips-2.4.27/fs/Config.in linux/fs/Config.in
+--- linux-mips-2.4.27/fs/Config.in 2004-02-20 02:22:19.000000000 +0100
++++ linux/fs/Config.in 2004-11-19 10:25:12.229149632 +0100
+@@ -49,6 +49,7 @@
+ dep_tristate 'Journalling Flash File System v2 (JFFS2) support' CONFIG_JFFS2_FS $CONFIG_MTD
+ if [ "$CONFIG_JFFS2_FS" = "y" -o "$CONFIG_JFFS2_FS" = "m" ] ; then
+ int 'JFFS2 debugging verbosity (0 = quiet, 2 = noisy)' CONFIG_JFFS2_FS_DEBUG 0
++ bool 'JFFS2 support for NAND chips' CONFIG_JFFS2_FS_NAND
+ fi
+ tristate 'Compressed ROM file system support' CONFIG_CRAMFS
+ bool 'Virtual memory file system support (former shm fs)' CONFIG_TMPFS
+diff -Nurb linux-mips-2.4.27/fs/jffs2/Makefile linux/fs/jffs2/Makefile
+--- linux-mips-2.4.27/fs/jffs2/Makefile 2003-08-13 19:19:25.000000000 +0200
++++ linux/fs/jffs2/Makefile 2004-11-19 10:25:12.071173648 +0100
+@@ -1,7 +1,7 @@
+ #
+ # Makefile for the linux Journalling Flash FileSystem (JFFS) routines.
+ #
+-# $Id$
++# $Id$
+ #
+ # Note! Dependencies are done automagically by 'make dep', which also
+ # removes any old dependencies. DON'T put your own dependencies here
+@@ -10,16 +10,31 @@
+ # Note 2! The CFLAGS definitions are now in the main makefile...
+
+
+-COMPR_OBJS := compr.o compr_rubin.o compr_rtime.o pushpull.o \
+- compr_zlib.o
++obj-$(CONFIG_JFFS2_FS) := jffs2.o
++
++COMPR_OBJS := compr.o compr_rubin.o compr_rtime.o compr_zlib.o
+ JFFS2_OBJS := dir.o file.o ioctl.o nodelist.o malloc.o \
+- read.o nodemgmt.o readinode.o super.o write.o scan.o gc.o \
+- symlink.o build.o erase.o background.o
++ read.o nodemgmt.o readinode.o write.o scan.o gc.o \
++ symlink.o build.o erase.o background.o fs.o writev.o
+
+-O_TARGET := jffs2.o
++BELOW25 := $(shell echo $(PATCHLEVEL) | sed s/[1234]/y/)
++
++ifeq ($(BELOW25),y)
++LINUX_OBJS := super-v24.o crc32.o rbtree.o
++else
++LINUX_OBJS := super.o
++endif
+
+-obj-y := $(COMPR_OBJS) $(JFFS2_OBJS)
+-obj-m := $(O_TARGET)
++NAND_OBJS-$(CONFIG_JFFS2_FS_NAND) := wbuf.o
+
++jffs2-objs := $(COMPR_OBJS) $(JFFS2_OBJS) $(VERS_OBJS) $(NAND_OBJS-y) \
++ $(LINUX_OBJS)
++
++
++# 2.4 build compatibility
++ifeq ($(BELOW25),y)
++obj-y := $(jffs2-objs)
++O_TARGET := jffs2.o
+ include $(TOPDIR)/Rules.make
++endif
+
+diff -Nurb linux-mips-2.4.27/fs/jffs2/background.c linux/fs/jffs2/background.c
+--- linux-mips-2.4.27/fs/jffs2/background.c 2001-11-06 08:56:10.000000000 +0100
++++ linux/fs/jffs2/background.c 2004-11-19 10:25:12.072173496 +0100
+@@ -1,61 +1,36 @@
+ /*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+- * Copyright (C) 2001 Red Hat, Inc.
++ * Copyright (C) 2001-2003 Red Hat, Inc.
+ *
+- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
++ * Created by David Woodhouse <dwmw2@redhat.com>
+ *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
++ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+- * The contents of this file are subject to the Red Hat eCos Public
+- * License Version 1.1 (the "Licence"); you may not use this file
+- * except in compliance with the Licence. You may obtain a copy of
+- * the Licence at http://www.redhat.com/
+- *
+- * Software distributed under the Licence is distributed on an "AS IS"
+- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+- * See the Licence for the specific language governing rights and
+- * limitations under the Licence.
+- *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
+- *
+- * Alternatively, the contents of this file may be used under the
+- * terms of the GNU General Public License version 2 (the "GPL"), in
+- * which case the provisions of the GPL are applicable instead of the
+- * above. If you wish to allow the use of your version of this file
+- * only under the terms of the GPL and not to allow others to use your
+- * version of this file under the RHEPL, indicate your decision by
+- * deleting the provisions above and replace them with the notice and
+- * other provisions required by the GPL. If you do not delete the
+- * provisions above, a recipient may use your version of this file
+- * under either the RHEPL or the GPL.
+- *
+- * $Id$
++ * $Id$
+ *
+ */
+
+ #define __KERNEL_SYSCALLS__
+
+ #include <linux/kernel.h>
+-#include <linux/sched.h>
+-#include <linux/unistd.h>
+ #include <linux/jffs2.h>
+ #include <linux/mtd/mtd.h>
+-#include <linux/interrupt.h>
+ #include <linux/completion.h>
++#include <linux/sched.h>
++#include <linux/unistd.h>
++#include <linux/suspend.h>
+ #include "nodelist.h"
+
+
+ static int jffs2_garbage_collect_thread(void *);
+-static int thread_should_wake(struct jffs2_sb_info *c);
+
+ void jffs2_garbage_collect_trigger(struct jffs2_sb_info *c)
+ {
+- spin_lock_bh(&c->erase_completion_lock);
+- if (c->gc_task && thread_should_wake(c))
++ spin_lock(&c->erase_completion_lock);
++ if (c->gc_task && jffs2_thread_should_wake(c))
+ send_sig(SIGHUP, c->gc_task, 1);
+- spin_unlock_bh(&c->erase_completion_lock);
++ spin_unlock(&c->erase_completion_lock);
+ }
+
+ /* This must only ever be called when no GC thread is currently running */
+@@ -86,12 +61,12 @@
+
+ void jffs2_stop_garbage_collect_thread(struct jffs2_sb_info *c)
+ {
+- spin_lock_bh(&c->erase_completion_lock);
++ spin_lock(&c->erase_completion_lock);
+ if (c->gc_task) {
+ D1(printk(KERN_DEBUG "jffs2: Killing GC task %d\n", c->gc_task->pid));
+ send_sig(SIGKILL, c->gc_task, 1);
+ }
+- spin_unlock_bh(&c->erase_completion_lock);
++ spin_unlock(&c->erase_completion_lock);
+ wait_for_completion(&c->gc_thread_exit);
+ }
+
+@@ -99,34 +74,37 @@
+ {
+ struct jffs2_sb_info *c = _c;
+
+- daemonize();
+- current->tty = NULL;
++ daemonize("jffs2_gcd_mtd%d", c->mtd->index);
++ allow_signal(SIGKILL);
++ allow_signal(SIGSTOP);
++ allow_signal(SIGCONT);
++
+ c->gc_task = current;
+ up(&c->gc_thread_start);
+
+- sprintf(current->comm, "jffs2_gcd_mtd%d", c->mtd->index);
+-
+- /* FIXME in the 2.2 backport */
+- current->nice = 10;
++ set_user_nice(current, 10);
+
+ for (;;) {
+- spin_lock_irq(&current->sigmask_lock);
+- siginitsetinv (&current->blocked, sigmask(SIGHUP) | sigmask(SIGKILL) | sigmask(SIGSTOP) | sigmask(SIGCONT));
+- recalc_sigpending(current);
+- spin_unlock_irq(&current->sigmask_lock);
++ allow_signal(SIGHUP);
+
+- if (!thread_should_wake(c)) {
++ if (!jffs2_thread_should_wake(c)) {
+ set_current_state (TASK_INTERRUPTIBLE);
+ D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread sleeping...\n"));
+- /* Yes, there's a race here; we checked thread_should_wake() before
+- setting current->state to TASK_INTERRUPTIBLE. But it doesn't
++ /* Yes, there's a race here; we checked jffs2_thread_should_wake()
++ before setting current->state to TASK_INTERRUPTIBLE. But it doesn't
+ matter - We don't care if we miss a wakeup, because the GC thread
+ is only an optimisation anyway. */
+ schedule();
+ }
+
+- if (current->need_resched)
+- schedule();
++ if (current->flags & PF_FREEZE) {
++ refrigerator(0);
++ /* refrigerator() should recalc sigpending for us
++ but doesn't. No matter - allow_signal() will. */
++ continue;
++ }
++
++ cond_resched();
+
+ /* Put_super will send a SIGKILL and then wait on the sem.
+ */
+@@ -134,9 +112,7 @@
+ siginfo_t info;
+ unsigned long signr;
+
+- spin_lock_irq(&current->sigmask_lock);
+- signr = dequeue_signal(&current->blocked, &info);
+- spin_unlock_irq(&current->sigmask_lock);
++ signr = dequeue_signal_lock(current, &current->blocked, &info);
+
+ switch(signr) {
+ case SIGSTOP:
+@@ -147,9 +123,10 @@
+
+ case SIGKILL:
+ D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): SIGKILL received.\n"));
+- spin_lock_bh(&c->erase_completion_lock);
++ die:
++ spin_lock(&c->erase_completion_lock);
+ c->gc_task = NULL;
+- spin_unlock_bh(&c->erase_completion_lock);
++ spin_unlock(&c->erase_completion_lock);
+ complete_and_exit(&c->gc_thread_exit, 0);
+
+ case SIGHUP:
+@@ -157,27 +134,15 @@
+ break;
+ default:
+ D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): signal %ld received\n", signr));
+-
+ }
+ }
+ /* We don't want SIGHUP to interrupt us. STOP and KILL are OK though. */
+- spin_lock_irq(&current->sigmask_lock);
+- siginitsetinv (&current->blocked, sigmask(SIGKILL) | sigmask(SIGSTOP) | sigmask(SIGCONT));
+- recalc_sigpending(current);
+- spin_unlock_irq(&current->sigmask_lock);
++ disallow_signal(SIGHUP);
+
+ D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): pass\n"));
+- jffs2_garbage_collect_pass(c);
++ if (jffs2_garbage_collect_pass(c) == -ENOSPC) {
++ printk(KERN_NOTICE "No space for garbage collection. Aborting GC thread\n");
++ goto die;
++ }
+ }
+-}
+-
+-static int thread_should_wake(struct jffs2_sb_info *c)
+-{
+- D1(printk(KERN_DEBUG "thread_should_wake(): nr_free_blocks %d, nr_erasing_blocks %d, dirty_size 0x%x\n",
+- c->nr_free_blocks, c->nr_erasing_blocks, c->dirty_size));
+- if (c->nr_free_blocks + c->nr_erasing_blocks < JFFS2_RESERVED_BLOCKS_GCTRIGGER &&
+- c->dirty_size > c->sector_size)
+- return 1;
+- else
+- return 0;
+ }
+diff -Nurb linux-mips-2.4.27/fs/jffs2/build.c linux/fs/jffs2/build.c
+--- linux-mips-2.4.27/fs/jffs2/build.c 2003-07-05 05:23:44.000000000 +0200
++++ linux/fs/jffs2/build.c 2004-11-19 10:25:12.073173344 +0100
+@@ -1,47 +1,22 @@
+ /*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+- * Copyright (C) 2001 Red Hat, Inc.
++ * Copyright (C) 2001-2003 Red Hat, Inc.
+ *
+- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
++ * Created by David Woodhouse <dwmw2@redhat.com>
+ *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
++ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+- * The contents of this file are subject to the Red Hat eCos Public
+- * License Version 1.1 (the "Licence"); you may not use this file
+- * except in compliance with the Licence. You may obtain a copy of
+- * the Licence at http://www.redhat.com/
+- *
+- * Software distributed under the Licence is distributed on an "AS IS"
+- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+- * See the Licence for the specific language governing rights and
+- * limitations under the Licence.
+- *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
+- *
+- * Alternatively, the contents of this file may be used under the
+- * terms of the GNU General Public License version 2 (the "GPL"), in
+- * which case the provisions of the GPL are applicable instead of the
+- * above. If you wish to allow the use of your version of this file
+- * only under the terms of the GPL and not to allow others to use your
+- * version of this file under the RHEPL, indicate your decision by
+- * deleting the provisions above and replace them with the notice and
+- * other provisions required by the GPL. If you do not delete the
+- * provisions above, a recipient may use your version of this file
+- * under either the RHEPL or the GPL.
+- *
+- * $Id$
++ * $Id$
+ *
+ */
+
+ #include <linux/kernel.h>
+-#include <linux/jffs2.h>
++#include <linux/sched.h>
+ #include <linux/slab.h>
+ #include "nodelist.h"
+
+-int jffs2_build_inode_pass1(struct jffs2_sb_info *, struct jffs2_inode_cache *);
+-int jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *, struct jffs2_inode_cache *);
++static void jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *, struct jffs2_inode_cache *, struct jffs2_full_dirent **);
+
+ static inline struct jffs2_inode_cache *
+ first_inode_chain(int *i, struct jffs2_sb_info *c)
+@@ -68,16 +43,52 @@
+ ic; \
+ ic = next_inode(&i, ic, (c)))
+
++
++static inline void jffs2_build_inode_pass1(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic)
++{
++ struct jffs2_full_dirent *fd;
++
++ D1(printk(KERN_DEBUG "jffs2_build_inode building directory inode #%u\n", ic->ino));
++
++ /* For each child, increase nlink */
++ for(fd = ic->scan_dents; fd; fd = fd->next) {
++ struct jffs2_inode_cache *child_ic;
++ if (!fd->ino)
++ continue;
++
++ /* XXX: Can get high latency here with huge directories */
++
++ child_ic = jffs2_get_ino_cache(c, fd->ino);
++ if (!child_ic) {
++ printk(KERN_NOTICE "Eep. Child \"%s\" (ino #%u) of dir ino #%u doesn't exist!\n",
++ fd->name, fd->ino, ic->ino);
++ continue;
++ }
++
++ if (child_ic->nlink++ && fd->type == DT_DIR) {
++ printk(KERN_NOTICE "Child dir \"%s\" (ino #%u) of dir ino #%u appears to be a hard link\n", fd->name, fd->ino, ic->ino);
++ if (fd->ino == 1 && ic->ino == 1) {
++ printk(KERN_NOTICE "This is mostly harmless, and probably caused by creating a JFFS2 image\n");
++ printk(KERN_NOTICE "using a buggy version of mkfs.jffs2. Use at least v1.17.\n");
++ }
++ /* What do we do about it? */
++ }
++ D1(printk(KERN_DEBUG "Increased nlink for child \"%s\" (ino #%u)\n", fd->name, fd->ino));
++ /* Can't free them. We might need them in pass 2 */
++ }
++}
++
+ /* Scan plan:
+ - Scan physical nodes. Build map of inodes/dirents. Allocate inocaches as we go
+ - Scan directory tree from top down, setting nlink in inocaches
+ - Scan inocaches for inodes with nlink==0
+ */
+-int jffs2_build_filesystem(struct jffs2_sb_info *c)
++static int jffs2_build_filesystem(struct jffs2_sb_info *c)
+ {
+ int ret;
+ int i;
+ struct jffs2_inode_cache *ic;
++ struct jffs2_full_dirent *dead_fds = NULL;
+
+ /* First, scan the medium and build all the inode caches with
+ lists of physical nodes */
+@@ -90,14 +101,17 @@
+ return ret;
+
+ D1(printk(KERN_DEBUG "Scanned flash completely\n"));
+- /* Now build the data map for each inode, marking obsoleted nodes
+- as such, and also increase nlink of any children. */
++ D1(jffs2_dump_block_lists(c));
++
++ /* Now scan the directory tree, increasing nlink according to every dirent found. */
+ for_each_inode(i, c, ic) {
+ D1(printk(KERN_DEBUG "Pass 1: ino #%u\n", ic->ino));
+- ret = jffs2_build_inode_pass1(c, ic);
+- if (ret) {
+- D1(printk(KERN_WARNING "Eep. jffs2_build_inode_pass1 for ino %d returned %d\n", ic->ino, ret));
+- return ret;
++
++ D1(BUG_ON(ic->ino > c->highest_ino));
++
++ if (ic->scan_dents) {
++ jffs2_build_inode_pass1(c, ic);
++ cond_resched();
+ }
+ }
+ D1(printk(KERN_DEBUG "Pass 1 complete\n"));
+@@ -107,181 +121,226 @@
+ children too, and repeat the scan. As that's going to be
+ a fairly uncommon occurrence, it's not so evil to do it this
+ way. Recursion bad. */
+- do {
+- D1(printk(KERN_DEBUG "Pass 2 (re)starting\n"));
+- ret = 0;
++ D1(printk(KERN_DEBUG "Pass 2 starting\n"));
++
+ for_each_inode(i, c, ic) {
+ D1(printk(KERN_DEBUG "Pass 2: ino #%u, nlink %d, ic %p, nodes %p\n", ic->ino, ic->nlink, ic, ic->nodes));
+ if (ic->nlink)
+ continue;
+
+- ret = jffs2_build_remove_unlinked_inode(c, ic);
+- if (ret)
+- break;
+- /* -EAGAIN means the inode's nlink was zero, so we deleted it,
+- and furthermore that it had children and their nlink has now
+- gone to zero too. So we have to restart the scan. */
++ jffs2_build_remove_unlinked_inode(c, ic, &dead_fds);
++ cond_resched();
++ }
++
++ D1(printk(KERN_DEBUG "Pass 2a starting\n"));
++
++ while (dead_fds) {
++ struct jffs2_inode_cache *ic;
++ struct jffs2_full_dirent *fd = dead_fds;
++
++ dead_fds = fd->next;
++
++ ic = jffs2_get_ino_cache(c, fd->ino);
++ D1(printk(KERN_DEBUG "Removing dead_fd ino #%u (\"%s\"), ic at %p\n", fd->ino, fd->name, ic));
++
++ if (ic)
++ jffs2_build_remove_unlinked_inode(c, ic, &dead_fds);
++ jffs2_free_full_dirent(fd);
+ }
+- } while(ret == -EAGAIN);
+
+ D1(printk(KERN_DEBUG "Pass 2 complete\n"));
+
+- /* Finally, we can scan again and free the dirent nodes and scan_info structs */
++ /* Finally, we can scan again and free the dirent structs */
+ for_each_inode(i, c, ic) {
+- struct jffs2_scan_info *scan = ic->scan;
+ struct jffs2_full_dirent *fd;
+ D1(printk(KERN_DEBUG "Pass 3: ino #%u, ic %p, nodes %p\n", ic->ino, ic, ic->nodes));
+- if (!scan) {
+- if (ic->nlink) {
+- D1(printk(KERN_WARNING "Why no scan struct for ino #%u which has nlink %d?\n", ic->ino, ic->nlink));
+- }
+- continue;
+- }
+- ic->scan = NULL;
+- while(scan->dents) {
+- fd = scan->dents;
+- scan->dents = fd->next;
++
++ while(ic->scan_dents) {
++ fd = ic->scan_dents;
++ ic->scan_dents = fd->next;
+ jffs2_free_full_dirent(fd);
+ }
+- kfree(scan);
++ ic->scan_dents = NULL;
++ cond_resched();
+ }
+ D1(printk(KERN_DEBUG "Pass 3 complete\n"));
++ D1(jffs2_dump_block_lists(c));
++
++ /* Rotate the lists by some number to ensure wear levelling */
++ jffs2_rotate_lists(c);
+
+ return ret;
+ }
+
+-int jffs2_build_inode_pass1(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic)
++static void jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic, struct jffs2_full_dirent **dead_fds)
+ {
+- struct jffs2_tmp_dnode_info *tn;
++ struct jffs2_raw_node_ref *raw;
+ struct jffs2_full_dirent *fd;
+- struct jffs2_node_frag *fraglist = NULL;
+- struct jffs2_tmp_dnode_info *metadata = NULL;
+-
+- D1(printk(KERN_DEBUG "jffs2_build_inode building inode #%u\n", ic->ino));
+- if (ic->ino > c->highest_ino)
+- c->highest_ino = ic->ino;
+
+- if (!ic->scan->tmpnodes && ic->ino != 1) {
+- D1(printk(KERN_DEBUG "jffs2_build_inode: ino #%u has no data nodes!\n", ic->ino));
+- }
+- /* Build the list to make sure any obsolete nodes are marked as such */
+- while(ic->scan->tmpnodes) {
+- tn = ic->scan->tmpnodes;
+- ic->scan->tmpnodes = tn->next;
+-
+- if (metadata && tn->version > metadata->version) {
+- D1(printk(KERN_DEBUG "jffs2_build_inode_pass1 ignoring old metadata at 0x%08x\n",
+- metadata->fn->raw->flash_offset &~3));
++ D1(printk(KERN_DEBUG "JFFS2: Removing ino #%u with nlink == zero.\n", ic->ino));
+
+- jffs2_free_full_dnode(metadata->fn);
+- jffs2_free_tmp_dnode_info(metadata);
+- metadata = NULL;
++ for (raw = ic->nodes; raw != (void *)ic; raw = raw->next_in_ino) {
++ D1(printk(KERN_DEBUG "obsoleting node at 0x%08x\n", ref_offset(raw)));
++ jffs2_mark_node_obsolete(c, raw);
+ }
+
+- if (tn->fn->size) {
+- jffs2_add_full_dnode_to_fraglist (c, &fraglist, tn->fn);
+- jffs2_free_tmp_dnode_info(tn);
+- } else {
+- if (!metadata) {
+- metadata = tn;
+- } else {
+- D1(printk(KERN_DEBUG "jffs2_build_inode_pass1 ignoring new metadata at 0x%08x\n",
+- tn->fn->raw->flash_offset &~3));
+-
+- jffs2_free_full_dnode(tn->fn);
+- jffs2_free_tmp_dnode_info(tn);
+- }
+- }
+- }
++ if (ic->scan_dents) {
++ int whinged = 0;
++ D1(printk(KERN_DEBUG "Inode #%u was a directory which may have children...\n", ic->ino));
+
+- /* OK. Now clear up */
+- if (metadata) {
+- jffs2_free_full_dnode(metadata->fn);
+- jffs2_free_tmp_dnode_info(metadata);
+- }
+- metadata = NULL;
++ while(ic->scan_dents) {
++ struct jffs2_inode_cache *child_ic;
+
+- while (fraglist) {
+- struct jffs2_node_frag *frag;
+- frag = fraglist;
+- fraglist = fraglist->next;
++ fd = ic->scan_dents;
++ ic->scan_dents = fd->next;
+
+- if (frag->node && !(--frag->node->frags)) {
+- jffs2_free_full_dnode(frag->node);
++ if (!fd->ino) {
++ /* It's a deletion dirent. Ignore it */
++ D1(printk(KERN_DEBUG "Child \"%s\" is a deletion dirent, skipping...\n", fd->name));
++ jffs2_free_full_dirent(fd);
++ continue;
+ }
+- jffs2_free_node_frag(frag);
++ if (!whinged) {
++ whinged = 1;
++ printk(KERN_NOTICE "Inode #%u was a directory with children - removing those too...\n", ic->ino);
+ }
+
+- /* Now for each child, increase nlink */
+- for(fd=ic->scan->dents; fd; fd = fd->next) {
+- struct jffs2_inode_cache *child_ic;
+- if (!fd->ino)
+- continue;
++ D1(printk(KERN_DEBUG "Removing child \"%s\", ino #%u\n",
++ fd->name, fd->ino));
+
+ child_ic = jffs2_get_ino_cache(c, fd->ino);
+ if (!child_ic) {
+- printk(KERN_NOTICE "Eep. Child \"%s\" (ino #%u) of dir ino #%u doesn't exist!\n",
+- fd->name, fd->ino, ic->ino);
++ printk(KERN_NOTICE "Cannot remove child \"%s\", ino #%u, because it doesn't exist\n", fd->name, fd->ino);
++ jffs2_free_full_dirent(fd);
+ continue;
+ }
+
+- if (child_ic->nlink++ && fd->type == DT_DIR) {
+- printk(KERN_NOTICE "Child dir \"%s\" (ino #%u) of dir ino #%u appears to be a hard link\n", fd->name, fd->ino, ic->ino);
+- if (fd->ino == 1 && ic->ino == 1) {
+- printk(KERN_NOTICE "This is mostly harmless, and probably caused by creating a JFFS2 image\n");
+- printk(KERN_NOTICE "using a buggy version of mkfs.jffs2. Use at least v1.17.\n");
++ /* Reduce nlink of the child. If it's now zero, stick it on the
++ dead_fds list to be cleaned up later. Else just free the fd */
++
++ child_ic->nlink--;
++
++ if (!child_ic->nlink) {
++ D1(printk(KERN_DEBUG "Inode #%u (\"%s\") has now got zero nlink. Adding to dead_fds list.\n",
++ fd->ino, fd->name));
++ fd->next = *dead_fds;
++ *dead_fds = fd;
++ } else {
++ D1(printk(KERN_DEBUG "Inode #%u (\"%s\") has now got nlink %d. Ignoring.\n",
++ fd->ino, fd->name, child_ic->nlink));
++ jffs2_free_full_dirent(fd);
+ }
+- /* What do we do about it? */
+ }
+- D1(printk(KERN_DEBUG "Increased nlink for child \"%s\" (ino #%u)\n", fd->name, fd->ino));
+- /* Can't free them. We might need them in pass 2 */
+ }
+- return 0;
++
++ /*
++ We don't delete the inocache from the hash list and free it yet.
++ The erase code will do that, when all the nodes are completely gone.
++ */
+ }
+
+-int jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic)
++static void jffs2_calc_trigger_levels(struct jffs2_sb_info *c)
+ {
+- struct jffs2_raw_node_ref *raw;
+- struct jffs2_full_dirent *fd;
+- int ret = 0;
++ uint32_t size;
+
+- if(!ic->scan) {
+- D1(printk(KERN_DEBUG "ino #%u was already removed\n", ic->ino));
+- return 0;
+- }
++ /* Deletion should almost _always_ be allowed. We're fairly
++ buggered once we stop allowing people to delete stuff
++ because there's not enough free space... */
++ c->resv_blocks_deletion = 2;
++
++ /* Be conservative about how much space we need before we allow writes.
++ On top of that which is required for deletia, require an extra 2%
++ of the medium to be available, for overhead caused by nodes being
++ split across blocks, etc. */
++
++ size = c->flash_size / 50; /* 2% of flash size */
++ size += c->nr_blocks * 100; /* And 100 bytes per eraseblock */
++ size += c->sector_size - 1; /* ... and round up */
++
++ c->resv_blocks_write = c->resv_blocks_deletion + (size / c->sector_size);
++
++ /* When do we let the GC thread run in the background */
++
++ c->resv_blocks_gctrigger = c->resv_blocks_write + 1;
++
++ /* When do we allow garbage collection to merge nodes to make
++ long-term progress at the expense of short-term space exhaustion? */
++ c->resv_blocks_gcmerge = c->resv_blocks_deletion + 1;
++
++ /* When do we allow garbage collection to eat from bad blocks rather
++ than actually making progress? */
++ c->resv_blocks_gcbad = 0;//c->resv_blocks_deletion + 2;
++
++ /* If there's less than this amount of dirty space, don't bother
++ trying to GC to make more space. It'll be a fruitless task */
++ c->nospc_dirty_size = c->sector_size + (c->flash_size / 100);
++
++ D1(printk(KERN_DEBUG "JFFS2 trigger levels (size %d KiB, block size %d KiB, %d blocks)\n",
++ c->flash_size / 1024, c->sector_size / 1024, c->nr_blocks));
++ D1(printk(KERN_DEBUG "Blocks required to allow deletion: %d (%d KiB)\n",
++ c->resv_blocks_deletion, c->resv_blocks_deletion*c->sector_size/1024));
++ D1(printk(KERN_DEBUG "Blocks required to allow writes: %d (%d KiB)\n",
++ c->resv_blocks_write, c->resv_blocks_write*c->sector_size/1024));
++ D1(printk(KERN_DEBUG "Blocks required to quiesce GC thread: %d (%d KiB)\n",
++ c->resv_blocks_gctrigger, c->resv_blocks_gctrigger*c->sector_size/1024));
++ D1(printk(KERN_DEBUG "Blocks required to allow GC merges: %d (%d KiB)\n",
++ c->resv_blocks_gcmerge, c->resv_blocks_gcmerge*c->sector_size/1024));
++ D1(printk(KERN_DEBUG "Blocks required to GC bad blocks: %d (%d KiB)\n",
++ c->resv_blocks_gcbad, c->resv_blocks_gcbad*c->sector_size/1024));
++ D1(printk(KERN_DEBUG "Amount of dirty space required to GC: %d bytes\n",
++ c->nospc_dirty_size));
++}
+
+- D1(printk(KERN_DEBUG "JFFS2: Removing ino #%u with nlink == zero.\n", ic->ino));
++int jffs2_do_mount_fs(struct jffs2_sb_info *c)
++{
++ int i;
+
+- for (raw = ic->nodes; raw != (void *)ic; raw = raw->next_in_ino) {
+- D1(printk(KERN_DEBUG "obsoleting node at 0x%08x\n", raw->flash_offset&~3));
+- jffs2_mark_node_obsolete(c, raw);
++ c->free_size = c->flash_size;
++ c->nr_blocks = c->flash_size / c->sector_size;
++ c->blocks = kmalloc(sizeof(struct jffs2_eraseblock) * c->nr_blocks, GFP_KERNEL);
++ if (!c->blocks)
++ return -ENOMEM;
++ for (i=0; i<c->nr_blocks; i++) {
++ INIT_LIST_HEAD(&c->blocks[i].list);
++ c->blocks[i].offset = i * c->sector_size;
++ c->blocks[i].free_size = c->sector_size;
++ c->blocks[i].dirty_size = 0;
++ c->blocks[i].wasted_size = 0;
++ c->blocks[i].unchecked_size = 0;
++ c->blocks[i].used_size = 0;
++ c->blocks[i].first_node = NULL;
++ c->blocks[i].last_node = NULL;
++ }
++
++ init_MUTEX(&c->alloc_sem);
++ init_MUTEX(&c->erase_free_sem);
++ init_waitqueue_head(&c->erase_wait);
++ init_waitqueue_head(&c->inocache_wq);
++ spin_lock_init(&c->erase_completion_lock);
++ spin_lock_init(&c->inocache_lock);
++
++ INIT_LIST_HEAD(&c->clean_list);
++ INIT_LIST_HEAD(&c->very_dirty_list);
++ INIT_LIST_HEAD(&c->dirty_list);
++ INIT_LIST_HEAD(&c->erasable_list);
++ INIT_LIST_HEAD(&c->erasing_list);
++ INIT_LIST_HEAD(&c->erase_pending_list);
++ INIT_LIST_HEAD(&c->erasable_pending_wbuf_list);
++ INIT_LIST_HEAD(&c->erase_complete_list);
++ INIT_LIST_HEAD(&c->free_list);
++ INIT_LIST_HEAD(&c->bad_list);
++ INIT_LIST_HEAD(&c->bad_used_list);
++ c->highest_ino = 1;
++
++ if (jffs2_build_filesystem(c)) {
++ D1(printk(KERN_DEBUG "build_fs failed\n"));
++ jffs2_free_ino_caches(c);
++ jffs2_free_raw_node_refs(c);
++ kfree(c->blocks);
++ return -EIO;
+ }
+
+- if (ic->scan->dents) {
+- printk(KERN_NOTICE "Inode #%u was a directory with children - removing those too...\n", ic->ino);
+-
+- while(ic->scan->dents) {
+- struct jffs2_inode_cache *child_ic;
++ jffs2_calc_trigger_levels(c);
+
+- fd = ic->scan->dents;
+- ic->scan->dents = fd->next;
+-
+- D1(printk(KERN_DEBUG "Removing child \"%s\", ino #%u\n",
+- fd->name, fd->ino));
+-
+- child_ic = jffs2_get_ino_cache(c, fd->ino);
+- if (!child_ic) {
+- printk(KERN_NOTICE "Cannot remove child \"%s\", ino #%u, because it doesn't exist\n", fd->name, fd->ino);
+- continue;
+- }
+- jffs2_free_full_dirent(fd);
+- child_ic->nlink--;
+- }
+- ret = -EAGAIN;
+- }
+- kfree(ic->scan);
+- ic->scan = NULL;
+- // jffs2_del_ino_cache(c, ic);
+- // jffs2_free_inode_cache(ic);
+- return ret;
++ return 0;
+ }
+diff -Nurb linux-mips-2.4.27/fs/jffs2/compr.c linux/fs/jffs2/compr.c
+--- linux-mips-2.4.27/fs/jffs2/compr.c 2001-11-05 21:16:18.000000000 +0100
++++ linux/fs/jffs2/compr.c 2004-11-19 10:25:12.085171520 +0100
+@@ -1,59 +1,37 @@
+ /*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+- * Copyright (C) 2001 Red Hat, Inc.
++ * Copyright (C) 2001-2003 Red Hat, Inc.
+ *
+ * Created by Arjan van de Ven <arjanv@redhat.com>
+ *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
++ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+- * The contents of this file are subject to the Red Hat eCos Public
+- * License Version 1.1 (the "Licence"); you may not use this file
+- * except in compliance with the Licence. You may obtain a copy of
+- * the Licence at http://www.redhat.com/
+- *
+- * Software distributed under the Licence is distributed on an "AS IS"
+- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+- * See the Licence for the specific language governing rights and
+- * limitations under the Licence.
+- *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
+- *
+- * Alternatively, the contents of this file may be used under the
+- * terms of the GNU General Public License version 2 (the "GPL"), in
+- * which case the provisions of the GPL are applicable instead of the
+- * above. If you wish to allow the use of your version of this file
+- * only under the terms of the GPL and not to allow others to use your
+- * version of this file under the RHEPL, indicate your decision by
+- * deleting the provisions above and replace them with the notice and
+- * other provisions required by the GPL. If you do not delete the
+- * provisions above, a recipient may use your version of this file
+- * under either the RHEPL or the GPL.
+- *
+- * $Id$
++ * $Id$
+ *
+ */
+
+ #include <linux/kernel.h>
+ #include <linux/string.h>
+-#include <linux/types.h>
+ #include <linux/errno.h>
++#include <linux/types.h>
++#include <linux/slab.h>
+ #include <linux/jffs2.h>
++#include "nodelist.h"
+
+-int zlib_compress(unsigned char *data_in, unsigned char *cpage_out, __u32 *sourcelen, __u32 *dstlen);
+-void zlib_decompress(unsigned char *data_in, unsigned char *cpage_out, __u32 srclen, __u32 destlen);
+-int rtime_compress(unsigned char *data_in, unsigned char *cpage_out, __u32 *sourcelen, __u32 *dstlen);
+-void rtime_decompress(unsigned char *data_in, unsigned char *cpage_out, __u32 srclen, __u32 destlen);
+-int rubinmips_compress(unsigned char *data_in, unsigned char *cpage_out, __u32 *sourcelen, __u32 *dstlen);
+-void rubinmips_decompress(unsigned char *data_in, unsigned char *cpage_out, __u32 srclen, __u32 destlen);
+-int dynrubin_compress(unsigned char *data_in, unsigned char *cpage_out, __u32 *sourcelen, __u32 *dstlen);
+-void dynrubin_decompress(unsigned char *data_in, unsigned char *cpage_out, __u32 srclen, __u32 destlen);
++int jffs2_zlib_compress(unsigned char *data_in, unsigned char *cpage_out, uint32_t *sourcelen, uint32_t *dstlen);
++void jffs2_zlib_decompress(unsigned char *data_in, unsigned char *cpage_out, uint32_t srclen, uint32_t destlen);
++int jffs2_rtime_compress(unsigned char *data_in, unsigned char *cpage_out, uint32_t *sourcelen, uint32_t *dstlen);
++void jffs2_rtime_decompress(unsigned char *data_in, unsigned char *cpage_out, uint32_t srclen, uint32_t destlen);
++int jffs2_rubinmips_compress(unsigned char *data_in, unsigned char *cpage_out, uint32_t *sourcelen, uint32_t *dstlen);
++void jffs2_rubinmips_decompress(unsigned char *data_in, unsigned char *cpage_out, uint32_t srclen, uint32_t destlen);
++int jffs2_dynrubin_compress(unsigned char *data_in, unsigned char *cpage_out, uint32_t *sourcelen, uint32_t *dstlen);
++void jffs2_dynrubin_decompress(unsigned char *data_in, unsigned char *cpage_out, uint32_t srclen, uint32_t destlen);
+
+
+ /* jffs2_compress:
+ * @data: Pointer to uncompressed data
+- * @cdata: Pointer to buffer for compressed data
++ * @cdata: Pointer to returned pointer to buffer for compressed data
+ * @datalen: On entry, holds the amount of data available for compression.
+ * On exit, expected to hold the amount of data actually compressed.
+ * @cdatalen: On entry, holds the amount of space available for compressed
+@@ -68,47 +46,59 @@
+ * jffs2_compress should compress as much as will fit, and should set
+ * *datalen accordingly to show the amount of data which were compressed.
+ */
+-unsigned char jffs2_compress(unsigned char *data_in, unsigned char *cpage_out,
+- __u32 *datalen, __u32 *cdatalen)
++unsigned char jffs2_compress(unsigned char *data_in, unsigned char **cpage_out,
++ uint32_t *datalen, uint32_t *cdatalen)
+ {
++#ifdef JFFS2_COMPRESSION
+ int ret;
+
+- ret = zlib_compress(data_in, cpage_out, datalen, cdatalen);
++ *cpage_out = kmalloc(*cdatalen, GFP_KERNEL);
++ if (!*cpage_out) {
++ printk(KERN_WARNING "No memory for compressor allocation. Compression failed\n");
++ goto out;
++ }
++
++#ifdef JFFS2_USE_ZLIB
++ ret = jffs2_zlib_compress(data_in, *cpage_out, datalen, cdatalen);
+ if (!ret) {
+ return JFFS2_COMPR_ZLIB;
+ }
+-#if 0 /* Disabled 23/9/1. With zlib it hardly ever gets a look in */
+- ret = dynrubin_compress(data_in, cpage_out, datalen, cdatalen);
++#endif
++#ifdef JFFS2_USE_DYNRUBIN
++ ret = jffs2_dynrubin_compress(data_in, *cpage_out, datalen, cdatalen);
+ if (!ret) {
+ return JFFS2_COMPR_DYNRUBIN;
+ }
+ #endif
+-#if 0 /* Disabled 26/2/1. Obsoleted by dynrubin */
+- ret = rubinmips_compress(data_in, cpage_out, datalen, cdatalen);
++#ifdef JFFS2_USE_RUBINMIPS
++ ret = jffs2_rubinmips_compress(data_in, *cpage_out, datalen, cdatalen);
+ if (!ret) {
+ return JFFS2_COMPR_RUBINMIPS;
+ }
+ #endif
++#ifdef JFFS2_USE_RTIME
+ /* rtime does manage to recompress already-compressed data */
+- ret = rtime_compress(data_in, cpage_out, datalen, cdatalen);
++ ret = jffs2_rtime_compress(data_in, *cpage_out, datalen, cdatalen);
+ if (!ret) {
+ return JFFS2_COMPR_RTIME;
+ }
+-#if 0
+- /* We don't need to copy. Let the caller special-case the COMPR_NONE case. */
+- /* If we get here, no compression is going to work */
+- /* But we might want to use the fragmentation part -- Arjan */
+- memcpy(cpage_out,data_in,min(*datalen,*cdatalen));
+- if (*datalen > *cdatalen)
+- *datalen = *cdatalen;
+ #endif
++ kfree(*cpage_out);
++#endif /* Compression */
++ out:
++ *cpage_out = data_in;
++ *datalen = *cdatalen;
+ return JFFS2_COMPR_NONE; /* We failed to compress */
+-
+ }
+
++void jffs2_free_comprbuf(unsigned char *comprbuf, unsigned char *orig)
++{
++ if (orig != comprbuf)
++ kfree(comprbuf);
++}
+
+ int jffs2_decompress(unsigned char comprtype, unsigned char *cdata_in,
+- unsigned char *data_out, __u32 cdatalen, __u32 datalen)
++ unsigned char *data_out, uint32_t cdatalen, uint32_t datalen)
+ {
+ switch (comprtype) {
+ case JFFS2_COMPR_NONE:
+@@ -119,30 +109,27 @@
+ case JFFS2_COMPR_ZERO:
+ memset(data_out, 0, datalen);
+ break;
+-
++#ifdef JFFS2_USE_ZLIB
+ case JFFS2_COMPR_ZLIB:
+- zlib_decompress(cdata_in, data_out, cdatalen, datalen);
++ jffs2_zlib_decompress(cdata_in, data_out, cdatalen, datalen);
+ break;
+-
++#endif
++#ifdef JFFS2_USE_RTIME
+ case JFFS2_COMPR_RTIME:
+- rtime_decompress(cdata_in, data_out, cdatalen, datalen);
++ jffs2_rtime_decompress(cdata_in, data_out, cdatalen, datalen);
+ break;
+-
+- case JFFS2_COMPR_RUBINMIPS:
+-#if 0 /* Disabled 23/9/1 */
+- rubinmips_decompress(cdata_in, data_out, cdatalen, datalen);
+-#else
+- printk(KERN_WARNING "JFFS2: Rubinmips compression encountered but support not compiled in!\n");
+ #endif
++#ifdef JFFS2_USE_RUBINMIPS
++ case JFFS2_COMPR_RUBINMIPS:
++ jffs2_rubinmips_decompress(cdata_in, data_out, cdatalen, datalen);
+ break;
+- case JFFS2_COMPR_DYNRUBIN:
+-#if 1 /* Phase this one out */
+- dynrubin_decompress(cdata_in, data_out, cdatalen, datalen);
+-#else
+- printk(KERN_WARNING "JFFS2: Dynrubin compression encountered but support not compiled in!\n");
+ #endif
+- break;
++#ifdef JFFS2_USE_DYNRUBIN
++ case JFFS2_COMPR_DYNRUBIN:
+
++ jffs2_dynrubin_decompress(cdata_in, data_out, cdatalen, datalen);
++ break;
++#endif
+ default:
+ printk(KERN_NOTICE "Unknown JFFS2 compression type 0x%02x\n", comprtype);
+ return -EIO;
+diff -Nurb linux-mips-2.4.27/fs/jffs2/compr_rtime.c linux/fs/jffs2/compr_rtime.c
+--- linux-mips-2.4.27/fs/jffs2/compr_rtime.c 2001-10-19 03:24:56.000000000 +0200
++++ linux/fs/jffs2/compr_rtime.c 2004-11-19 10:25:12.087171216 +0100
+@@ -1,43 +1,19 @@
+ /*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+- * Copyright (C) 2001 Red Hat, Inc.
++ * Copyright (C) 2001-2003 Red Hat, Inc.
+ *
+ * Created by Arjan van de Ven <arjanv@redhat.com>
+ *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
++ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+- * The contents of this file are subject to the Red Hat eCos Public
+- * License Version 1.1 (the "Licence"); you may not use this file
+- * except in compliance with the Licence. You may obtain a copy of
+- * the Licence at http://www.redhat.com/
+- *
+- * Software distributed under the Licence is distributed on an "AS IS"
+- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+- * See the Licence for the specific language governing rights and
+- * limitations under the Licence.
+- *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
+- *
+- * Alternatively, the contents of this file may be used under the
+- * terms of the GNU General Public License version 2 (the "GPL"), in
+- * which case the provisions of the GPL are applicable instead of the
+- * above. If you wish to allow the use of your version of this file
+- * only under the terms of the GPL and not to allow others to use your
+- * version of this file under the RHEPL, indicate your decision by
+- * deleting the provisions above and replace them with the notice and
+- * other provisions required by the GPL. If you do not delete the
+- * provisions above, a recipient may use your version of this file
+- * under either the RHEPL or the GPL.
+- *
+- * $Id$
++ * $Id$
+ *
+ *
+ * Very simple lz77-ish encoder.
+ *
+ * Theory of operation: Both encoder and decoder have a list of "last
+- * occurances" for every possible source-value; after sending the
++ * occurrences" for every possible source-value; after sending the
+ * first source-byte, the second byte indicated the "run" length of
+ * matches
+ *
+@@ -51,10 +27,10 @@
+ #include <linux/string.h>
+
+ /* _compress returns the compressed size, -1 if bigger */
+-int rtime_compress(unsigned char *data_in, unsigned char *cpage_out,
+- __u32 *sourcelen, __u32 *dstlen)
++int jffs2_rtime_compress(unsigned char *data_in, unsigned char *cpage_out,
++ uint32_t *sourcelen, uint32_t *dstlen)
+ {
+- int positions[256];
++ short positions[256];
+ int outpos = 0;
+ int pos=0;
+
+@@ -91,10 +67,10 @@
+ }
+
+
+-void rtime_decompress(unsigned char *data_in, unsigned char *cpage_out,
+- __u32 srclen, __u32 destlen)
++void jffs2_rtime_decompress(unsigned char *data_in, unsigned char *cpage_out,
++ uint32_t srclen, uint32_t destlen)
+ {
+- int positions[256];
++ short positions[256];
+ int outpos = 0;
+ int pos=0;
+
+diff -Nurb linux-mips-2.4.27/fs/jffs2/compr_rubin.c linux/fs/jffs2/compr_rubin.c
+--- linux-mips-2.4.27/fs/jffs2/compr_rubin.c 2001-11-05 21:16:18.000000000 +0100
++++ linux/fs/jffs2/compr_rubin.c 2004-11-19 10:25:12.088171064 +0100
+@@ -1,37 +1,13 @@
+ /*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+- * Copyright (C) 2001 Red Hat, Inc.
++ * Copyright (C) 2001, 2002 Red Hat, Inc.
+ *
+ * Created by Arjan van de Ven <arjanv@redhat.com>
+ *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
++ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+- * The contents of this file are subject to the Red Hat eCos Public
+- * License Version 1.1 (the "Licence"); you may not use this file
+- * except in compliance with the Licence. You may obtain a copy of
+- * the Licence at http://www.redhat.com/
+- *
+- * Software distributed under the Licence is distributed on an "AS IS"
+- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+- * See the Licence for the specific language governing rights and
+- * limitations under the Licence.
+- *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
+- *
+- * Alternatively, the contents of this file may be used under the
+- * terms of the GNU General Public License version 2 (the "GPL"), in
+- * which case the provisions of the GPL are applicable instead of the
+- * above. If you wish to allow the use of your version of this file
+- * only under the terms of the GPL and not to allow others to use your
+- * version of this file under the RHEPL, indicate your decision by
+- * deleting the provisions above and replace them with the notice and
+- * other provisions required by the GPL. If you do not delete the
+- * provisions above, a recipient may use your version of this file
+- * under either the RHEPL or the GPL.
+- *
+- * $Id$
++ * $Id$
+ *
+ */
+
+@@ -43,7 +19,7 @@
+
+
+
+-void init_rubin(struct rubin_state *rs, int div, int *bits)
++static void init_rubin(struct rubin_state *rs, int div, int *bits)
+ {
+ int c;
+
+@@ -56,7 +32,7 @@
+ }
+
+
+-int encode(struct rubin_state *rs, long A, long B, int symbol)
++static int encode(struct rubin_state *rs, long A, long B, int symbol)
+ {
+
+ long i0, i1;
+@@ -91,7 +67,7 @@
+ }
+
+
+-void end_rubin(struct rubin_state *rs)
++static void end_rubin(struct rubin_state *rs)
+ {
+
+ int i;
+@@ -104,7 +80,7 @@
+ }
+
+
+-void init_decode(struct rubin_state *rs, int div, int *bits)
++static void init_decode(struct rubin_state *rs, int div, int *bits)
+ {
+ init_rubin(rs, div, bits);
+
+@@ -151,7 +127,7 @@
+ rs->rec_q = rec_q;
+ }
+
+-int decode(struct rubin_state *rs, long A, long B)
++static int decode(struct rubin_state *rs, long A, long B)
+ {
+ unsigned long p = rs->p, q = rs->q;
+ long i0, threshold;
+@@ -212,8 +188,8 @@
+
+
+
+-int rubin_do_compress(int bit_divider, int *bits, unsigned char *data_in,
+- unsigned char *cpage_out, __u32 *sourcelen, __u32 *dstlen)
++static int rubin_do_compress(int bit_divider, int *bits, unsigned char *data_in,
++ unsigned char *cpage_out, uint32_t *sourcelen, uint32_t *dstlen)
+ {
+ int outpos = 0;
+ int pos=0;
+@@ -246,20 +222,20 @@
+ }
+ #if 0
+ /* _compress returns the compressed size, -1 if bigger */
+-int rubinmips_compress(unsigned char *data_in, unsigned char *cpage_out,
+- __u32 *sourcelen, __u32 *dstlen)
++int jffs2_rubinmips_compress(unsigned char *data_in, unsigned char *cpage_out,
++ uint32_t *sourcelen, uint32_t *dstlen)
+ {
+ return rubin_do_compress(BIT_DIVIDER_MIPS, bits_mips, data_in, cpage_out, sourcelen, dstlen);
+ }
+ #endif
+-int dynrubin_compress(unsigned char *data_in, unsigned char *cpage_out,
+- __u32 *sourcelen, __u32 *dstlen)
++int jffs2_dynrubin_compress(unsigned char *data_in, unsigned char *cpage_out,
++ uint32_t *sourcelen, uint32_t *dstlen)
+ {
+ int bits[8];
+ unsigned char histo[256];
+ int i;
+ int ret;
+- __u32 mysrclen, mydstlen;
++ uint32_t mysrclen, mydstlen;
+
+ mysrclen = *sourcelen;
+ mydstlen = *dstlen - 8;
+@@ -315,8 +291,8 @@
+ return 0;
+ }
+
+-void rubin_do_decompress(int bit_divider, int *bits, unsigned char *cdata_in,
+- unsigned char *page_out, __u32 srclen, __u32 destlen)
++static void rubin_do_decompress(int bit_divider, int *bits, unsigned char *cdata_in,
++ unsigned char *page_out, uint32_t srclen, uint32_t destlen)
+ {
+ int outpos = 0;
+ struct rubin_state rs;
+@@ -330,14 +306,14 @@
+ }
+
+
+-void rubinmips_decompress(unsigned char *data_in, unsigned char *cpage_out,
+- __u32 sourcelen, __u32 dstlen)
++void jffs2_rubinmips_decompress(unsigned char *data_in, unsigned char *cpage_out,
++ uint32_t sourcelen, uint32_t dstlen)
+ {
+ rubin_do_decompress(BIT_DIVIDER_MIPS, bits_mips, data_in, cpage_out, sourcelen, dstlen);
+ }
+
+-void dynrubin_decompress(unsigned char *data_in, unsigned char *cpage_out,
+- __u32 sourcelen, __u32 dstlen)
++void jffs2_dynrubin_decompress(unsigned char *data_in, unsigned char *cpage_out,
++ uint32_t sourcelen, uint32_t dstlen)
+ {
+ int bits[8];
+ int c;
+diff -Nurb linux-mips-2.4.27/fs/jffs2/compr_rubin.h linux/fs/jffs2/compr_rubin.h
+--- linux-mips-2.4.27/fs/jffs2/compr_rubin.h 2001-10-19 03:24:56.000000000 +0200
++++ linux/fs/jffs2/compr_rubin.h 2004-11-19 10:25:12.090170760 +0100
+@@ -1,7 +1,7 @@
+ /* Rubin encoder/decoder header */
+ /* work started at : aug 3, 1994 */
+ /* last modification : aug 15, 1994 */
+-/* $Id$ */
++/* $Id$ */
+
+ #include "pushpull.h"
+
+@@ -19,10 +19,3 @@
+ int bit_divider;
+ int bits[8];
+ };
+-
+-
+-void init_rubin (struct rubin_state *rs, int div, int *bits);
+-int encode (struct rubin_state *, long, long, int);
+-void end_rubin (struct rubin_state *);
+-void init_decode (struct rubin_state *, int div, int *bits);
+-int decode (struct rubin_state *, long, long);
+diff -Nurb linux-mips-2.4.27/fs/jffs2/compr_zlib.c linux/fs/jffs2/compr_zlib.c
+--- linux-mips-2.4.27/fs/jffs2/compr_zlib.c 2003-01-11 18:53:17.000000000 +0100
++++ linux/fs/jffs2/compr_zlib.c 2004-11-19 10:25:12.091170608 +0100
+@@ -1,50 +1,26 @@
+ /*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+- * Copyright (C) 2001, 2002 Red Hat, Inc.
++ * Copyright (C) 2001-2003 Red Hat, Inc.
+ *
+- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
++ * Created by David Woodhouse <dwmw2@redhat.com>
+ *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
++ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+- * The contents of this file are subject to the Red Hat eCos Public
+- * License Version 1.1 (the "Licence"); you may not use this file
+- * except in compliance with the Licence. You may obtain a copy of
+- * the Licence at http://www.redhat.com/
+- *
+- * Software distributed under the Licence is distributed on an "AS IS"
+- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+- * See the Licence for the specific language governing rights and
+- * limitations under the Licence.
+- *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
+- *
+- * Alternatively, the contents of this file may be used under the
+- * terms of the GNU General Public License version 2 (the "GPL"), in
+- * which case the provisions of the GPL are applicable instead of the
+- * above. If you wish to allow the use of your version of this file
+- * only under the terms of the GPL and not to allow others to use your
+- * version of this file under the RHEPL, indicate your decision by
+- * deleting the provisions above and replace them with the notice and
+- * other provisions required by the GPL. If you do not delete the
+- * provisions above, a recipient may use your version of this file
+- * under either the RHEPL or the GPL.
+- *
+- * $Id$
++ * $Id$
+ *
+ */
+
+-#ifndef __KERNEL__
++#if !defined(__KERNEL__) && !defined(__ECOS)
+ #error "The userspace support got too messy and was removed. Update your mkfs.jffs2"
+ #endif
+
+ #include <linux/config.h>
+ #include <linux/kernel.h>
+-#include <linux/mtd/compatmac.h> /* for min() */
+ #include <linux/slab.h>
+-#include <linux/jffs2.h>
+ #include <linux/zlib.h>
++#include <linux/zutil.h>
++#include <asm/semaphore.h>
+ #include "nodelist.h"
+
+ /* Plan: call deflate() with avail_in == *sourcelen,
+@@ -58,21 +34,24 @@
+
+ static DECLARE_MUTEX(deflate_sem);
+ static DECLARE_MUTEX(inflate_sem);
+-static void *deflate_workspace;
+-static void *inflate_workspace;
++static z_stream inf_strm, def_strm;
++
++#ifdef __KERNEL__ /* Linux-only */
++#include <linux/vmalloc.h>
++#include <linux/init.h>
+
+ int __init jffs2_zlib_init(void)
+ {
+- deflate_workspace = vmalloc(zlib_deflate_workspacesize());
+- if (!deflate_workspace) {
++ def_strm.workspace = vmalloc(zlib_deflate_workspacesize());
++ if (!def_strm.workspace) {
+ printk(KERN_WARNING "Failed to allocate %d bytes for deflate workspace\n", zlib_deflate_workspacesize());
+ return -ENOMEM;
+ }
+ D1(printk(KERN_DEBUG "Allocated %d bytes for deflate workspace\n", zlib_deflate_workspacesize()));
+- inflate_workspace = vmalloc(zlib_inflate_workspacesize());
+- if (!inflate_workspace) {
++ inf_strm.workspace = vmalloc(zlib_inflate_workspacesize());
++ if (!inf_strm.workspace) {
+ printk(KERN_WARNING "Failed to allocate %d bytes for inflate workspace\n", zlib_inflate_workspacesize());
+- vfree(deflate_workspace);
++ vfree(def_strm.workspace);
+ return -ENOMEM;
+ }
+ D1(printk(KERN_DEBUG "Allocated %d bytes for inflate workspace\n", zlib_inflate_workspacesize()));
+@@ -81,97 +60,120 @@
+
+ void jffs2_zlib_exit(void)
+ {
+- vfree(deflate_workspace);
+- vfree(inflate_workspace);
++ vfree(def_strm.workspace);
++ vfree(inf_strm.workspace);
+ }
++#endif /* __KERNEL__ */
+
+-int zlib_compress(unsigned char *data_in, unsigned char *cpage_out,
+- __u32 *sourcelen, __u32 *dstlen)
++int jffs2_zlib_compress(unsigned char *data_in, unsigned char *cpage_out,
++ uint32_t *sourcelen, uint32_t *dstlen)
+ {
+- z_stream strm;
+ int ret;
+
+ if (*dstlen <= STREAM_END_SPACE)
+ return -1;
+
+ down(&deflate_sem);
+- strm.workspace = deflate_workspace;
+
+- if (Z_OK != zlib_deflateInit(&strm, 3)) {
++ if (Z_OK != zlib_deflateInit(&def_strm, 3)) {
+ printk(KERN_WARNING "deflateInit failed\n");
+ up(&deflate_sem);
+ return -1;
+ }
+
+- strm.next_in = data_in;
+- strm.total_in = 0;
++ def_strm.next_in = data_in;
++ def_strm.total_in = 0;
+
+- strm.next_out = cpage_out;
+- strm.total_out = 0;
++ def_strm.next_out = cpage_out;
++ def_strm.total_out = 0;
+
+- while (strm.total_out < *dstlen - STREAM_END_SPACE && strm.total_in < *sourcelen) {
+- strm.avail_out = *dstlen - (strm.total_out + STREAM_END_SPACE);
+- strm.avail_in = min((unsigned)(*sourcelen-strm.total_in), strm.avail_out);
++ while (def_strm.total_out < *dstlen - STREAM_END_SPACE && def_strm.total_in < *sourcelen) {
++ def_strm.avail_out = *dstlen - (def_strm.total_out + STREAM_END_SPACE);
++ def_strm.avail_in = min((unsigned)(*sourcelen-def_strm.total_in), def_strm.avail_out);
+ D1(printk(KERN_DEBUG "calling deflate with avail_in %d, avail_out %d\n",
+- strm.avail_in, strm.avail_out));
+- ret = zlib_deflate(&strm, Z_PARTIAL_FLUSH);
++ def_strm.avail_in, def_strm.avail_out));
++ ret = zlib_deflate(&def_strm, Z_PARTIAL_FLUSH);
+ D1(printk(KERN_DEBUG "deflate returned with avail_in %d, avail_out %d, total_in %ld, total_out %ld\n",
+- strm.avail_in, strm.avail_out, strm.total_in, strm.total_out));
++ def_strm.avail_in, def_strm.avail_out, def_strm.total_in, def_strm.total_out));
+ if (ret != Z_OK) {
+ D1(printk(KERN_DEBUG "deflate in loop returned %d\n", ret));
+- zlib_deflateEnd(&strm);
++ zlib_deflateEnd(&def_strm);
+ up(&deflate_sem);
+ return -1;
+ }
+ }
+- strm.avail_out += STREAM_END_SPACE;
+- strm.avail_in = 0;
+- ret = zlib_deflate(&strm, Z_FINISH);
+- zlib_deflateEnd(&strm);
+- up(&deflate_sem);
++ def_strm.avail_out += STREAM_END_SPACE;
++ def_strm.avail_in = 0;
++ ret = zlib_deflate(&def_strm, Z_FINISH);
++ zlib_deflateEnd(&def_strm);
++
+ if (ret != Z_STREAM_END) {
+ D1(printk(KERN_DEBUG "final deflate returned %d\n", ret));
+- return -1;
++ ret = -1;
++ goto out;
+ }
+
+- D1(printk(KERN_DEBUG "zlib compressed %ld bytes into %ld\n",
+- strm.total_in, strm.total_out));
++ if (def_strm.total_out >= def_strm.total_in) {
++ D1(printk(KERN_DEBUG "zlib compressed %ld bytes into %ld; failing\n",
++ def_strm.total_in, def_strm.total_out));
++ ret = -1;
++ goto out;
++ }
+
+- if (strm.total_out >= strm.total_in)
+- return -1;
++ D1(printk(KERN_DEBUG "zlib compressed %ld bytes into %ld\n",
++ def_strm.total_in, def_strm.total_out));
+
+- *dstlen = strm.total_out;
+- *sourcelen = strm.total_in;
+- return 0;
++ *dstlen = def_strm.total_out;
++ *sourcelen = def_strm.total_in;
++ ret = 0;
++ out:
++ up(&deflate_sem);
++ return ret;
+ }
+
+-void zlib_decompress(unsigned char *data_in, unsigned char *cpage_out,
+- __u32 srclen, __u32 destlen)
++void jffs2_zlib_decompress(unsigned char *data_in, unsigned char *cpage_out,
++ uint32_t srclen, uint32_t destlen)
+ {
+- z_stream strm;
+ int ret;
++ int wbits = MAX_WBITS;
+
+ down(&inflate_sem);
+- strm.workspace = inflate_workspace;
+
+- if (Z_OK != zlib_inflateInit(&strm)) {
++ inf_strm.next_in = data_in;
++ inf_strm.avail_in = srclen;
++ inf_strm.total_in = 0;
++
++ inf_strm.next_out = cpage_out;
++ inf_strm.avail_out = destlen;
++ inf_strm.total_out = 0;
++
++ /* If it's deflate, and it's got no preset dictionary, then
++ we can tell zlib to skip the adler32 check. */
++ if (srclen > 2 && !(data_in[1] & PRESET_DICT) &&
++ ((data_in[0] & 0x0f) == Z_DEFLATED) &&
++ !(((data_in[0]<<8) + data_in[1]) % 31)) {
++
++ D2(printk(KERN_DEBUG "inflate skipping adler32\n"));
++ wbits = -((data_in[0] >> 4) + 8);
++ inf_strm.next_in += 2;
++ inf_strm.avail_in -= 2;
++ } else {
++ /* Let this remain D1 for now -- it should never happen */
++ D1(printk(KERN_DEBUG "inflate not skipping adler32\n"));
++ }
++
++
++ if (Z_OK != zlib_inflateInit2(&inf_strm, wbits)) {
+ printk(KERN_WARNING "inflateInit failed\n");
+ up(&inflate_sem);
+ return;
+ }
+- strm.next_in = data_in;
+- strm.avail_in = srclen;
+- strm.total_in = 0;
+-
+- strm.next_out = cpage_out;
+- strm.avail_out = destlen;
+- strm.total_out = 0;
+
+- while((ret = zlib_inflate(&strm, Z_FINISH)) == Z_OK)
++ while((ret = zlib_inflate(&inf_strm, Z_FINISH)) == Z_OK)
+ ;
+ if (ret != Z_STREAM_END) {
+ printk(KERN_NOTICE "inflate returned %d\n", ret);
+ }
+- zlib_inflateEnd(&strm);
++ zlib_inflateEnd(&inf_strm);
+ up(&inflate_sem);
+ }
+diff -Nurb linux-mips-2.4.27/fs/jffs2/comprtest.c linux/fs/jffs2/comprtest.c
+--- linux-mips-2.4.27/fs/jffs2/comprtest.c 2001-10-19 03:24:56.000000000 +0200
++++ linux/fs/jffs2/comprtest.c 2004-11-19 10:25:12.093170304 +0100
+@@ -1,4 +1,4 @@
+-/* $Id$ */
++/* $Id$ */
+
+ #include <linux/kernel.h>
+ #include <linux/string.h>
+@@ -266,13 +266,13 @@
+ static unsigned char decomprbuf[TESTDATA_LEN];
+
+ int jffs2_decompress(unsigned char comprtype, unsigned char *cdata_in,
+- unsigned char *data_out, __u32 cdatalen, __u32 datalen);
++ unsigned char *data_out, uint32_t cdatalen, uint32_t datalen);
+ unsigned char jffs2_compress(unsigned char *data_in, unsigned char *cpage_out,
+- __u32 *datalen, __u32 *cdatalen);
++ uint32_t *datalen, uint32_t *cdatalen);
+
+ int init_module(void ) {
+ unsigned char comprtype;
+- __u32 c, d;
++ uint32_t c, d;
+ int ret;
+
+ printk("Original data: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
+diff -Nurb linux-mips-2.4.27/fs/jffs2/crc32.c linux/fs/jffs2/crc32.c
+--- linux-mips-2.4.27/fs/jffs2/crc32.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/fs/jffs2/crc32.c 2004-11-19 10:25:12.095170000 +0100
+@@ -0,0 +1,97 @@
++/*
++ * COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or
++ * code or tables extracted from it, as desired without restriction.
++ *
++ * First, the polynomial itself and its table of feedback terms. The
++ * polynomial is
++ * X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0
++ *
++ * Note that we take it "backwards" and put the highest-order term in
++ * the lowest-order bit. The X^32 term is "implied"; the LSB is the
++ * X^31 term, etc. The X^0 term (usually shown as "+1") results in
++ * the MSB being 1
++ *
++ * Note that the usual hardware shift register implementation, which
++ * is what we're using (we're merely optimizing it by doing eight-bit
++ * chunks at a time) shifts bits into the lowest-order term. In our
++ * implementation, that means shifting towards the right. Why do we
++ * do it this way? Because the calculated CRC must be transmitted in
++ * order from highest-order term to lowest-order term. UARTs transmit
++ * characters in order from LSB to MSB. By storing the CRC this way
++ * we hand it to the UART in the order low-byte to high-byte; the UART
++ * sends each low-bit to hight-bit; and the result is transmission bit
++ * by bit from highest- to lowest-order term without requiring any bit
++ * shuffling on our part. Reception works similarly
++ *
++ * The feedback terms table consists of 256, 32-bit entries. Notes
++ *
++ * The table can be generated at runtime if desired; code to do so
++ * is shown later. It might not be obvious, but the feedback
++ * terms simply represent the results of eight shift/xor opera
++ * tions for all combinations of data and CRC register values
++ *
++ * The values must be right-shifted by eight bits by the "updcrc
++ * logic; the shift must be unsigned (bring in zeroes). On some
++ * hardware you could probably optimize the shift in assembler by
++ * using byte-swap instructions
++ * polynomial $edb88320
++ */
++
++/* $Id$ */
++
++#include "crc32.h"
++
++const uint32_t crc32_table[256] = {
++ 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L,
++ 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L,
++ 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
++ 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL,
++ 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L,
++ 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L,
++ 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L,
++ 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL,
++ 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L,
++ 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL,
++ 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L,
++ 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L,
++ 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L,
++ 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL,
++ 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL,
++ 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L,
++ 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL,
++ 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L,
++ 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L,
++ 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L,
++ 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL,
++ 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L,
++ 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L,
++ 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,
++ 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L,
++ 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L,
++ 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L,
++ 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L,
++ 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L,
++ 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL,
++ 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL,
++ 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L,
++ 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L,
++ 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL,
++ 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL,
++ 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L,
++ 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL,
++ 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L,
++ 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL,
++ 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L,
++ 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL,
++ 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L,
++ 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L,
++ 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL,
++ 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L,
++ 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L,
++ 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L,
++ 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,
++ 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L,
++ 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L,
++ 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL,
++ 0x2d02ef8dL
++};
+diff -Nurb linux-mips-2.4.27/fs/jffs2/crc32.h linux/fs/jffs2/crc32.h
+--- linux-mips-2.4.27/fs/jffs2/crc32.h 1970-01-01 01:00:00.000000000 +0100
++++ linux/fs/jffs2/crc32.h 2004-11-19 10:25:12.096169848 +0100
+@@ -0,0 +1,21 @@
++#ifndef CRC32_H
++#define CRC32_H
++
++/* $Id$ */
++
++#include <linux/types.h>
++
++extern const uint32_t crc32_table[256];
++
++/* Return a 32-bit CRC of the contents of the buffer. */
++
++static inline uint32_t
++crc32(uint32_t val, const void *ss, int len)
++{
++ const unsigned char *s = ss;
++ while (--len >= 0)
++ val = crc32_table[(val ^ *s++) & 0xff] ^ (val >> 8);
++ return val;
++}
++
++#endif
+diff -Nurb linux-mips-2.4.27/fs/jffs2/dir.c linux/fs/jffs2/dir.c
+--- linux-mips-2.4.27/fs/jffs2/dir.c 2003-11-17 02:07:44.000000000 +0100
++++ linux/fs/jffs2/dir.c 2004-11-19 10:25:12.097169696 +0100
+@@ -1,84 +1,73 @@
+ /*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+- * Copyright (C) 2001 Red Hat, Inc.
++ * Copyright (C) 2001-2003 Red Hat, Inc.
+ *
+- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
++ * Created by David Woodhouse <dwmw2@redhat.com>
+ *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
++ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+- * The contents of this file are subject to the Red Hat eCos Public
+- * License Version 1.1 (the "Licence"); you may not use this file
+- * except in compliance with the Licence. You may obtain a copy of
+- * the Licence at http://www.redhat.com/
+- *
+- * Software distributed under the Licence is distributed on an "AS IS"
+- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+- * See the Licence for the specific language governing rights and
+- * limitations under the Licence.
+- *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
+- *
+- * Alternatively, the contents of this file may be used under the
+- * terms of the GNU General Public License version 2 (the "GPL"), in
+- * which case the provisions of the GPL are applicable instead of the
+- * above. If you wish to allow the use of your version of this file
+- * only under the terms of the GPL and not to allow others to use your
+- * version of this file under the RHEPL, indicate your decision by
+- * deleting the provisions above and replace them with the notice and
+- * other provisions required by the GPL. If you do not delete the
+- * provisions above, a recipient may use your version of this file
+- * under either the RHEPL or the GPL.
+- *
+- * $Id$
++ * $Id$
+ *
+ */
+
+ #include <linux/kernel.h>
+ #include <linux/slab.h>
++#include <linux/sched.h>
+ #include <linux/fs.h>
+-#include <linux/mtd/compatmac.h> /* For completion */
++#include <linux/crc32.h>
+ #include <linux/jffs2.h>
+ #include <linux/jffs2_fs_i.h>
+ #include <linux/jffs2_fs_sb.h>
++#include <linux/time.h>
+ #include "nodelist.h"
+-#include <linux/crc32.h>
++
++/* Urgh. Please tell me there's a nicer way of doing these. */
++#include <linux/version.h>
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,48)
++typedef int mknod_arg_t;
++#define NAMEI_COMPAT(x) ((void *)x)
++#else
++typedef dev_t mknod_arg_t;
++#define NAMEI_COMPAT(x) (x)
++#endif
+
+ static int jffs2_readdir (struct file *, void *, filldir_t);
+
+-static int jffs2_create (struct inode *,struct dentry *,int);
+-static struct dentry *jffs2_lookup (struct inode *,struct dentry *);
++static int jffs2_create (struct inode *,struct dentry *,int,
++ struct nameidata *);
++static struct dentry *jffs2_lookup (struct inode *,struct dentry *,
++ struct nameidata *);
+ static int jffs2_link (struct dentry *,struct inode *,struct dentry *);
+ static int jffs2_unlink (struct inode *,struct dentry *);
+ static int jffs2_symlink (struct inode *,struct dentry *,const char *);
+ static int jffs2_mkdir (struct inode *,struct dentry *,int);
+ static int jffs2_rmdir (struct inode *,struct dentry *);
+-static int jffs2_mknod (struct inode *,struct dentry *,int,int);
++static int jffs2_mknod (struct inode *,struct dentry *,int,mknod_arg_t);
+ static int jffs2_rename (struct inode *, struct dentry *,
+ struct inode *, struct dentry *);
+
+ struct file_operations jffs2_dir_operations =
+ {
+- read: generic_read_dir,
+- readdir: jffs2_readdir,
+- ioctl: jffs2_ioctl,
+- fsync: jffs2_null_fsync
++ .read = generic_read_dir,
++ .readdir = jffs2_readdir,
++ .ioctl = jffs2_ioctl,
++ .fsync = jffs2_fsync
+ };
+
+
+ struct inode_operations jffs2_dir_inode_operations =
+ {
+- create: jffs2_create,
+- lookup: jffs2_lookup,
+- link: jffs2_link,
+- unlink: jffs2_unlink,
+- symlink: jffs2_symlink,
+- mkdir: jffs2_mkdir,
+- rmdir: jffs2_rmdir,
+- mknod: jffs2_mknod,
+- rename: jffs2_rename,
+- setattr: jffs2_setattr,
++ .create = NAMEI_COMPAT(jffs2_create),
++ .lookup = NAMEI_COMPAT(jffs2_lookup),
++ .link = jffs2_link,
++ .unlink = jffs2_unlink,
++ .symlink = jffs2_symlink,
++ .mkdir = jffs2_mkdir,
++ .rmdir = jffs2_rmdir,
++ .mknod = jffs2_mknod,
++ .rename = jffs2_rename,
++ .setattr = jffs2_setattr,
+ };
+
+ /***********************************************************************/
+@@ -88,12 +77,13 @@
+ and we use the same hash function as the dentries. Makes this
+ nice and simple
+ */
+-static struct dentry *jffs2_lookup(struct inode *dir_i, struct dentry *target)
++static struct dentry *jffs2_lookup(struct inode *dir_i, struct dentry *target,
++ struct nameidata *nd)
+ {
+ struct jffs2_inode_info *dir_f;
+ struct jffs2_sb_info *c;
+ struct jffs2_full_dirent *fd = NULL, *fd_list;
+- __u32 ino = 0;
++ uint32_t ino = 0;
+ struct inode *inode = NULL;
+
+ D1(printk(KERN_DEBUG "jffs2_lookup()\n"));
+@@ -153,8 +143,9 @@
+ offset++;
+ }
+ if (offset == 1) {
+- D1(printk(KERN_DEBUG "Dirent 1: \"..\", ino #%lu\n", filp->f_dentry->d_parent->d_inode->i_ino));
+- if (filldir(dirent, "..", 2, 1, filp->f_dentry->d_parent->d_inode->i_ino, DT_DIR) < 0)
++ unsigned long pino = parent_ino(filp->f_dentry);
++ D1(printk(KERN_DEBUG "Dirent 1: \"..\", ino #%lu\n", pino));
++ if (filldir(dirent, "..", 2, 1, pino, DT_DIR) < 0)
+ goto out;
+ offset++;
+ }
+@@ -188,18 +179,14 @@
+
+ /***********************************************************************/
+
+-static int jffs2_create(struct inode *dir_i, struct dentry *dentry, int mode)
++
++static int jffs2_create(struct inode *dir_i, struct dentry *dentry, int mode,
++ struct nameidata *nd)
+ {
++ struct jffs2_raw_inode *ri;
+ struct jffs2_inode_info *f, *dir_f;
+ struct jffs2_sb_info *c;
+ struct inode *inode;
+- struct jffs2_raw_inode *ri;
+- struct jffs2_raw_dirent *rd;
+- struct jffs2_full_dnode *fn;
+- struct jffs2_full_dirent *fd;
+- int namelen;
+- __u32 alloclen, phys_ofs;
+- __u32 writtenlen;
+ int ret;
+
+ ri = jffs2_alloc_raw_inode();
+@@ -210,23 +197,11 @@
+
+ D1(printk(KERN_DEBUG "jffs2_create()\n"));
+
+- /* Try to reserve enough space for both node and dirent.
+- * Just the node will do for now, though
+- */
+- namelen = dentry->d_name.len;
+- ret = jffs2_reserve_space(c, sizeof(*ri), &phys_ofs, &alloclen, ALLOC_NORMAL);
+- D1(printk(KERN_DEBUG "jffs2_create(): reserved 0x%x bytes\n", alloclen));
+- if (ret) {
+- jffs2_free_raw_inode(ri);
+- return ret;
+- }
+-
+ inode = jffs2_new_inode(dir_i, mode, ri);
+
+ if (IS_ERR(inode)) {
+ D1(printk(KERN_DEBUG "jffs2_new_inode() failed\n"));
+ jffs2_free_raw_inode(ri);
+- jffs2_complete_reservation(c);
+ return PTR_ERR(inode);
+ }
+
+@@ -236,93 +211,22 @@
+ inode->i_mapping->nrpages = 0;
+
+ f = JFFS2_INODE_INFO(inode);
++ dir_f = JFFS2_INODE_INFO(dir_i);
+
+- ri->data_crc = 0;
+- ri->node_crc = crc32(0, ri, sizeof(*ri)-8);
+-
+- fn = jffs2_write_dnode(inode, ri, NULL, 0, phys_ofs, &writtenlen);
+- D1(printk(KERN_DEBUG "jffs2_create created file with mode 0x%x\n", ri->mode));
+- jffs2_free_raw_inode(ri);
+-
+- if (IS_ERR(fn)) {
+- D1(printk(KERN_DEBUG "jffs2_write_dnode() failed\n"));
+- /* Eeek. Wave bye bye */
+- up(&f->sem);
+- jffs2_complete_reservation(c);
+- jffs2_clear_inode(inode);
+- return PTR_ERR(fn);
+- }
+- /* No data here. Only a metadata node, which will be
+- obsoleted by the first data write
+- */
+- f->metadata = fn;
+-
+- /* Work out where to put the dirent node now. */
+- writtenlen = PAD(writtenlen);
+- phys_ofs += writtenlen;
+- alloclen -= writtenlen;
+- up(&f->sem);
+-
+- if (alloclen < sizeof(*rd)+namelen) {
+- /* Not enough space left in this chunk. Get some more */
+- jffs2_complete_reservation(c);
+- ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL);
++ ret = jffs2_do_create(c, dir_f, f, ri,
++ dentry->d_name.name, dentry->d_name.len);
+
+ if (ret) {
+- /* Eep. */
+- D1(printk(KERN_DEBUG "jffs2_reserve_space() for dirent failed\n"));
+ jffs2_clear_inode(inode);
++ make_bad_inode(inode);
++ iput(inode);
++ jffs2_free_raw_inode(ri);
+ return ret;
+ }
+- }
+
+- rd = jffs2_alloc_raw_dirent();
+- if (!rd) {
+- /* Argh. Now we treat it like a normal delete */
+- jffs2_complete_reservation(c);
+- jffs2_clear_inode(inode);
+- return -ENOMEM;
+- }
+-
+- dir_f = JFFS2_INODE_INFO(dir_i);
+- down(&dir_f->sem);
+-
+- rd->magic = JFFS2_MAGIC_BITMASK;
+- rd->nodetype = JFFS2_NODETYPE_DIRENT;
+- rd->totlen = sizeof(*rd) + namelen;
+- rd->hdr_crc = crc32(0, rd, sizeof(struct jffs2_unknown_node)-4);
+-
+- rd->pino = dir_i->i_ino;
+- rd->version = ++dir_f->highest_version;
+- rd->ino = inode->i_ino;
+- rd->mctime = CURRENT_TIME;
+- rd->nsize = namelen;
+- rd->type = DT_REG;
+- rd->node_crc = crc32(0, rd, sizeof(*rd)-8);
+- rd->name_crc = crc32(0, dentry->d_name.name, namelen);
+-
+- fd = jffs2_write_dirent(dir_i, rd, dentry->d_name.name, namelen, phys_ofs, &writtenlen);
+-
+- jffs2_complete_reservation(c);
+-
+- if (IS_ERR(fd)) {
+- /* dirent failed to write. Delete the inode normally
+- as if it were the final unlink() */
+- jffs2_free_raw_dirent(rd);
+- up(&dir_f->sem);
+- jffs2_clear_inode(inode);
+- return PTR_ERR(fd);
+- }
+-
+- dir_i->i_mtime = dir_i->i_ctime = rd->mctime;
+-
+- jffs2_free_raw_dirent(rd);
+-
+- /* Link the fd into the inode's list, obsoleting an old
+- one if necessary. */
+- jffs2_add_fd_to_list(c, fd, &dir_f->dents);
+- up(&dir_f->sem);
++ dir_i->i_mtime = dir_i->i_ctime = ITIME(je32_to_cpu(ri->ctime));
+
++ jffs2_free_raw_inode(ri);
+ d_instantiate(dentry, inode);
+
+ D1(printk(KERN_DEBUG "jffs2_create: Created ino #%lu with mode %o, nlink %d(%d). nrpages %ld\n",
+@@ -332,173 +236,48 @@
+
+ /***********************************************************************/
+
+-static int jffs2_do_unlink(struct inode *dir_i, struct dentry *dentry, int rename)
+-{
+- struct jffs2_inode_info *dir_f, *f;
+- struct jffs2_sb_info *c;
+- struct jffs2_raw_dirent *rd;
+- struct jffs2_full_dirent *fd;
+- __u32 alloclen, phys_ofs;
+- int ret;
+-
+- c = JFFS2_SB_INFO(dir_i->i_sb);
+-
+- rd = jffs2_alloc_raw_dirent();
+- if (!rd)
+- return -ENOMEM;
+-
+- ret = jffs2_reserve_space(c, sizeof(*rd)+dentry->d_name.len, &phys_ofs, &alloclen, ALLOC_DELETION);
+- if (ret) {
+- jffs2_free_raw_dirent(rd);
+- return ret;
+- }
+-
+- dir_f = JFFS2_INODE_INFO(dir_i);
+- down(&dir_f->sem);
+-
+- /* Build a deletion node */
+- rd->magic = JFFS2_MAGIC_BITMASK;
+- rd->nodetype = JFFS2_NODETYPE_DIRENT;
+- rd->totlen = sizeof(*rd) + dentry->d_name.len;
+- rd->hdr_crc = crc32(0, rd, sizeof(struct jffs2_unknown_node)-4);
+-
+- rd->pino = dir_i->i_ino;
+- rd->version = ++dir_f->highest_version;
+- rd->ino = 0;
+- rd->mctime = CURRENT_TIME;
+- rd->nsize = dentry->d_name.len;
+- rd->type = DT_UNKNOWN;
+- rd->node_crc = crc32(0, rd, sizeof(*rd)-8);
+- rd->name_crc = crc32(0, dentry->d_name.name, dentry->d_name.len);
+-
+- fd = jffs2_write_dirent(dir_i, rd, dentry->d_name.name, dentry->d_name.len, phys_ofs, NULL);
+-
+- jffs2_complete_reservation(c);
+- jffs2_free_raw_dirent(rd);
+-
+- if (IS_ERR(fd)) {
+- up(&dir_f->sem);
+- return PTR_ERR(fd);
+- }
+-
+- /* File it. This will mark the old one obsolete. */
+- jffs2_add_fd_to_list(c, fd, &dir_f->dents);
+- up(&dir_f->sem);
+-
+- if (!rename) {
+- f = JFFS2_INODE_INFO(dentry->d_inode);
+- down(&f->sem);
+-
+- while (f->dents) {
+- /* There can be only deleted ones */
+- fd = f->dents;
+-
+- f->dents = fd->next;
+-
+- if (fd->ino) {
+- printk(KERN_WARNING "Deleting inode #%u with active dentry \"%s\"->ino #%u\n",
+- f->inocache->ino, fd->name, fd->ino);
+- } else {
+- D1(printk(KERN_DEBUG "Removing deletion dirent for \"%s\" from dir ino #%u\n", fd->name, f->inocache->ino));
+- }
+- jffs2_mark_node_obsolete(c, fd->raw);
+- jffs2_free_full_dirent(fd);
+- }
+- /* Don't oops on unlinking a bad inode */
+- if (f->inocache)
+- f->inocache->nlink--;
+- dentry->d_inode->i_nlink--;
+- up(&f->sem);
+- }
+-
+- return 0;
+-}
+
+ static int jffs2_unlink(struct inode *dir_i, struct dentry *dentry)
+ {
+- return jffs2_do_unlink(dir_i, dentry, 0);
+-}
+-/***********************************************************************/
+-
+-static int jffs2_do_link (struct dentry *old_dentry, struct inode *dir_i, struct dentry *dentry, int rename)
+-{
+- struct jffs2_inode_info *dir_f, *f;
+- struct jffs2_sb_info *c;
+- struct jffs2_raw_dirent *rd;
+- struct jffs2_full_dirent *fd;
+- __u32 alloclen, phys_ofs;
++ struct jffs2_sb_info *c = JFFS2_SB_INFO(dir_i->i_sb);
++ struct jffs2_inode_info *dir_f = JFFS2_INODE_INFO(dir_i);
++ struct jffs2_inode_info *dead_f = JFFS2_INODE_INFO(dentry->d_inode);
+ int ret;
+
+- c = JFFS2_SB_INFO(dir_i->i_sb);
+-
+- rd = jffs2_alloc_raw_dirent();
+- if (!rd)
+- return -ENOMEM;
+-
+- ret = jffs2_reserve_space(c, sizeof(*rd)+dentry->d_name.len, &phys_ofs, &alloclen, ALLOC_NORMAL);
+- if (ret) {
+- jffs2_free_raw_dirent(rd);
++ ret = jffs2_do_unlink(c, dir_f, dentry->d_name.name,
++ dentry->d_name.len, dead_f);
++ if (dead_f->inocache)
++ dentry->d_inode->i_nlink = dead_f->inocache->nlink;
+ return ret;
+- }
+-
+- dir_f = JFFS2_INODE_INFO(dir_i);
+- down(&dir_f->sem);
+-
+- /* Build a deletion node */
+- rd->magic = JFFS2_MAGIC_BITMASK;
+- rd->nodetype = JFFS2_NODETYPE_DIRENT;
+- rd->totlen = sizeof(*rd) + dentry->d_name.len;
+- rd->hdr_crc = crc32(0, rd, sizeof(struct jffs2_unknown_node)-4);
+-
+- rd->pino = dir_i->i_ino;
+- rd->version = ++dir_f->highest_version;
+- rd->ino = old_dentry->d_inode->i_ino;
+- rd->mctime = CURRENT_TIME;
+- rd->nsize = dentry->d_name.len;
+-
+- /* XXX: This is ugly. */
+- rd->type = (old_dentry->d_inode->i_mode & S_IFMT) >> 12;
+- if (!rd->type) rd->type = DT_REG;
+-
+- rd->node_crc = crc32(0, rd, sizeof(*rd)-8);
+- rd->name_crc = crc32(0, dentry->d_name.name, dentry->d_name.len);
+-
+- fd = jffs2_write_dirent(dir_i, rd, dentry->d_name.name, dentry->d_name.len, phys_ofs, NULL);
+-
+- jffs2_complete_reservation(c);
+- jffs2_free_raw_dirent(rd);
+-
+- if (IS_ERR(fd)) {
+- up(&dir_f->sem);
+- return PTR_ERR(fd);
+- }
+-
+- /* File it. This will mark the old one obsolete. */
+- jffs2_add_fd_to_list(c, fd, &dir_f->dents);
+- up(&dir_f->sem);
+-
+- if (!rename) {
+- f = JFFS2_INODE_INFO(old_dentry->d_inode);
+- down(&f->sem);
+- old_dentry->d_inode->i_nlink = ++f->inocache->nlink;
+- up(&f->sem);
+- }
+- return 0;
+ }
++/***********************************************************************/
++
+
+ static int jffs2_link (struct dentry *old_dentry, struct inode *dir_i, struct dentry *dentry)
+ {
++ struct jffs2_sb_info *c = JFFS2_SB_INFO(old_dentry->d_inode->i_sb);
++ struct jffs2_inode_info *f = JFFS2_INODE_INFO(old_dentry->d_inode);
++ struct jffs2_inode_info *dir_f = JFFS2_INODE_INFO(dir_i);
+ int ret;
++ uint8_t type;
+
+- /* Can't link a bad inode. */
+- if (!JFFS2_INODE_INFO(old_dentry->d_inode)->inocache)
++ /* Don't let people make hard links to bad inodes. */
++ if (!f->inocache)
+ return -EIO;
+
+ if (S_ISDIR(old_dentry->d_inode->i_mode))
+ return -EPERM;
+
+- ret = jffs2_do_link(old_dentry, dir_i, dentry, 0);
++ /* XXX: This is ugly */
++ type = (old_dentry->d_inode->i_mode & S_IFMT) >> 12;
++ if (!type) type = DT_REG;
++
++ ret = jffs2_do_link(c, dir_f, f->inocache->ino, type, dentry->d_name.name, dentry->d_name.len);
++
+ if (!ret) {
++ down(&f->sem);
++ old_dentry->d_inode->i_nlink = ++f->inocache->nlink;
++ up(&f->sem);
+ d_instantiate(dentry, old_dentry->d_inode);
+ atomic_inc(&old_dentry->d_inode->i_count);
+ }
+@@ -517,8 +296,7 @@
+ struct jffs2_full_dnode *fn;
+ struct jffs2_full_dirent *fd;
+ int namelen;
+- __u32 alloclen, phys_ofs;
+- __u32 writtenlen;
++ uint32_t alloclen, phys_ofs;
+ int ret;
+
+ /* FIXME: If you care. We'd need to use frags for the target
+@@ -556,15 +334,16 @@
+
+ f = JFFS2_INODE_INFO(inode);
+
+- inode->i_size = ri->isize = ri->dsize = ri->csize = strlen(target);
+- ri->totlen = sizeof(*ri) + ri->dsize;
+- ri->hdr_crc = crc32(0, ri, sizeof(struct jffs2_unknown_node)-4);
++ inode->i_size = strlen(target);
++ ri->isize = ri->dsize = ri->csize = cpu_to_je32(inode->i_size);
++ ri->totlen = cpu_to_je32(sizeof(*ri) + inode->i_size);
++ ri->hdr_crc = cpu_to_je32(crc32(0, ri, sizeof(struct jffs2_unknown_node)-4));
+
+ ri->compr = JFFS2_COMPR_NONE;
+- ri->data_crc = crc32(0, target, strlen(target));
+- ri->node_crc = crc32(0, ri, sizeof(*ri)-8);
++ ri->data_crc = cpu_to_je32(crc32(0, target, strlen(target)));
++ ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
+
+- fn = jffs2_write_dnode(inode, ri, target, strlen(target), phys_ofs, &writtenlen);
++ fn = jffs2_write_dnode(c, f, ri, target, strlen(target), phys_ofs, ALLOC_NORMAL);
+
+ jffs2_free_raw_inode(ri);
+
+@@ -581,13 +360,6 @@
+ f->metadata = fn;
+ up(&f->sem);
+
+- /* Work out where to put the dirent node now. */
+- writtenlen = (writtenlen+3)&~3;
+- phys_ofs += writtenlen;
+- alloclen -= writtenlen;
+-
+- if (alloclen < sizeof(*rd)+namelen) {
+- /* Not enough space left in this chunk. Get some more */
+ jffs2_complete_reservation(c);
+ ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL);
+ if (ret) {
+@@ -595,7 +367,6 @@
+ jffs2_clear_inode(inode);
+ return ret;
+ }
+- }
+
+ rd = jffs2_alloc_raw_dirent();
+ if (!rd) {
+@@ -608,41 +379,42 @@
+ dir_f = JFFS2_INODE_INFO(dir_i);
+ down(&dir_f->sem);
+
+- rd->magic = JFFS2_MAGIC_BITMASK;
+- rd->nodetype = JFFS2_NODETYPE_DIRENT;
+- rd->totlen = sizeof(*rd) + namelen;
+- rd->hdr_crc = crc32(0, rd, sizeof(struct jffs2_unknown_node)-4);
+-
+- rd->pino = dir_i->i_ino;
+- rd->version = ++dir_f->highest_version;
+- rd->ino = inode->i_ino;
+- rd->mctime = CURRENT_TIME;
++ rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
++ rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT);
++ rd->totlen = cpu_to_je32(sizeof(*rd) + namelen);
++ rd->hdr_crc = cpu_to_je32(crc32(0, rd, sizeof(struct jffs2_unknown_node)-4));
++
++ rd->pino = cpu_to_je32(dir_i->i_ino);
++ rd->version = cpu_to_je32(++dir_f->highest_version);
++ rd->ino = cpu_to_je32(inode->i_ino);
++ rd->mctime = cpu_to_je32(get_seconds());
+ rd->nsize = namelen;
+ rd->type = DT_LNK;
+- rd->node_crc = crc32(0, rd, sizeof(*rd)-8);
+- rd->name_crc = crc32(0, dentry->d_name.name, namelen);
++ rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8));
++ rd->name_crc = cpu_to_je32(crc32(0, dentry->d_name.name, namelen));
+
+- fd = jffs2_write_dirent(dir_i, rd, dentry->d_name.name, namelen, phys_ofs, &writtenlen);
+-
+- jffs2_complete_reservation(c);
++ fd = jffs2_write_dirent(c, dir_f, rd, dentry->d_name.name, namelen, phys_ofs, ALLOC_NORMAL);
+
+ if (IS_ERR(fd)) {
+ /* dirent failed to write. Delete the inode normally
+ as if it were the final unlink() */
++ jffs2_complete_reservation(c);
+ jffs2_free_raw_dirent(rd);
+ up(&dir_f->sem);
+ jffs2_clear_inode(inode);
+ return PTR_ERR(fd);
+ }
+
+- dir_i->i_mtime = dir_i->i_ctime = rd->mctime;
++ dir_i->i_mtime = dir_i->i_ctime = ITIME(je32_to_cpu(rd->mctime));
+
+ jffs2_free_raw_dirent(rd);
+
+ /* Link the fd into the inode's list, obsoleting an old
+ one if necessary. */
+ jffs2_add_fd_to_list(c, fd, &dir_f->dents);
++
+ up(&dir_f->sem);
++ jffs2_complete_reservation(c);
+
+ d_instantiate(dentry, inode);
+ return 0;
+@@ -659,8 +431,7 @@
+ struct jffs2_full_dnode *fn;
+ struct jffs2_full_dirent *fd;
+ int namelen;
+- __u32 alloclen, phys_ofs;
+- __u32 writtenlen;
++ uint32_t alloclen, phys_ofs;
+ int ret;
+
+ mode |= S_IFDIR;
+@@ -692,13 +463,15 @@
+
+ inode->i_op = &jffs2_dir_inode_operations;
+ inode->i_fop = &jffs2_dir_operations;
++ /* Directories get nlink 2 at start */
++ inode->i_nlink = 2;
+
+ f = JFFS2_INODE_INFO(inode);
+
+- ri->data_crc = 0;
+- ri->node_crc = crc32(0, ri, sizeof(*ri)-8);
++ ri->data_crc = cpu_to_je32(0);
++ ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
+
+- fn = jffs2_write_dnode(inode, ri, NULL, 0, phys_ofs, &writtenlen);
++ fn = jffs2_write_dnode(c, f, ri, NULL, 0, phys_ofs, ALLOC_NORMAL);
+
+ jffs2_free_raw_inode(ri);
+
+@@ -715,13 +488,6 @@
+ f->metadata = fn;
+ up(&f->sem);
+
+- /* Work out where to put the dirent node now. */
+- writtenlen = PAD(writtenlen);
+- phys_ofs += writtenlen;
+- alloclen -= writtenlen;
+-
+- if (alloclen < sizeof(*rd)+namelen) {
+- /* Not enough space left in this chunk. Get some more */
+ jffs2_complete_reservation(c);
+ ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL);
+ if (ret) {
+@@ -729,7 +495,6 @@
+ jffs2_clear_inode(inode);
+ return ret;
+ }
+- }
+
+ rd = jffs2_alloc_raw_dirent();
+ if (!rd) {
+@@ -742,41 +507,43 @@
+ dir_f = JFFS2_INODE_INFO(dir_i);
+ down(&dir_f->sem);
+
+- rd->magic = JFFS2_MAGIC_BITMASK;
+- rd->nodetype = JFFS2_NODETYPE_DIRENT;
+- rd->totlen = sizeof(*rd) + namelen;
+- rd->hdr_crc = crc32(0, rd, sizeof(struct jffs2_unknown_node)-4);
+-
+- rd->pino = dir_i->i_ino;
+- rd->version = ++dir_f->highest_version;
+- rd->ino = inode->i_ino;
+- rd->mctime = CURRENT_TIME;
++ rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
++ rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT);
++ rd->totlen = cpu_to_je32(sizeof(*rd) + namelen);
++ rd->hdr_crc = cpu_to_je32(crc32(0, rd, sizeof(struct jffs2_unknown_node)-4));
++
++ rd->pino = cpu_to_je32(dir_i->i_ino);
++ rd->version = cpu_to_je32(++dir_f->highest_version);
++ rd->ino = cpu_to_je32(inode->i_ino);
++ rd->mctime = cpu_to_je32(get_seconds());
+ rd->nsize = namelen;
+ rd->type = DT_DIR;
+- rd->node_crc = crc32(0, rd, sizeof(*rd)-8);
+- rd->name_crc = crc32(0, dentry->d_name.name, namelen);
++ rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8));
++ rd->name_crc = cpu_to_je32(crc32(0, dentry->d_name.name, namelen));
+
+- fd = jffs2_write_dirent(dir_i, rd, dentry->d_name.name, namelen, phys_ofs, &writtenlen);
+-
+- jffs2_complete_reservation(c);
++ fd = jffs2_write_dirent(c, dir_f, rd, dentry->d_name.name, namelen, phys_ofs, ALLOC_NORMAL);
+
+ if (IS_ERR(fd)) {
+ /* dirent failed to write. Delete the inode normally
+ as if it were the final unlink() */
++ jffs2_complete_reservation(c);
+ jffs2_free_raw_dirent(rd);
+ up(&dir_f->sem);
+ jffs2_clear_inode(inode);
+ return PTR_ERR(fd);
+ }
+
+- dir_i->i_mtime = dir_i->i_ctime = rd->mctime;
++ dir_i->i_mtime = dir_i->i_ctime = ITIME(je32_to_cpu(rd->mctime));
++ dir_i->i_nlink++;
+
+ jffs2_free_raw_dirent(rd);
+
+ /* Link the fd into the inode's list, obsoleting an old
+ one if necessary. */
+ jffs2_add_fd_to_list(c, fd, &dir_f->dents);
++
+ up(&dir_f->sem);
++ jffs2_complete_reservation(c);
+
+ d_instantiate(dentry, inode);
+ return 0;
+@@ -786,15 +553,19 @@
+ {
+ struct jffs2_inode_info *f = JFFS2_INODE_INFO(dentry->d_inode);
+ struct jffs2_full_dirent *fd;
++ int ret;
+
+ for (fd = f->dents ; fd; fd = fd->next) {
+ if (fd->ino)
+ return -ENOTEMPTY;
+ }
+- return jffs2_unlink(dir_i, dentry);
++ ret = jffs2_unlink(dir_i, dentry);
++ if (!ret)
++ dir_i->i_nlink--;
++ return ret;
+ }
+
+-static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, int rdev)
++static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, mknod_arg_t rdev)
+ {
+ struct jffs2_inode_info *f, *dir_f;
+ struct jffs2_sb_info *c;
+@@ -804,12 +575,14 @@
+ struct jffs2_full_dnode *fn;
+ struct jffs2_full_dirent *fd;
+ int namelen;
+- unsigned short dev;
++ jint16_t dev;
+ int devlen = 0;
+- __u32 alloclen, phys_ofs;
+- __u32 writtenlen;
++ uint32_t alloclen, phys_ofs;
+ int ret;
+
++ if (!old_valid_dev(rdev))
++ return -EINVAL;
++
+ ri = jffs2_alloc_raw_inode();
+ if (!ri)
+ return -ENOMEM;
+@@ -817,7 +590,7 @@
+ c = JFFS2_SB_INFO(dir_i->i_sb);
+
+ if (S_ISBLK(mode) || S_ISCHR(mode)) {
+- dev = (MAJOR(to_kdev_t(rdev)) << 8) | MINOR(to_kdev_t(rdev));
++ dev = cpu_to_je16(old_encode_dev(rdev));
+ devlen = sizeof(dev);
+ }
+
+@@ -844,15 +617,15 @@
+
+ f = JFFS2_INODE_INFO(inode);
+
+- ri->dsize = ri->csize = devlen;
+- ri->totlen = sizeof(*ri) + ri->csize;
+- ri->hdr_crc = crc32(0, ri, sizeof(struct jffs2_unknown_node)-4);
++ ri->dsize = ri->csize = cpu_to_je32(devlen);
++ ri->totlen = cpu_to_je32(sizeof(*ri) + devlen);
++ ri->hdr_crc = cpu_to_je32(crc32(0, ri, sizeof(struct jffs2_unknown_node)-4));
+
+ ri->compr = JFFS2_COMPR_NONE;
+- ri->data_crc = crc32(0, &dev, devlen);
+- ri->node_crc = crc32(0, ri, sizeof(*ri)-8);
++ ri->data_crc = cpu_to_je32(crc32(0, &dev, devlen));
++ ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
+
+- fn = jffs2_write_dnode(inode, ri, (char *)&dev, devlen, phys_ofs, &writtenlen);
++ fn = jffs2_write_dnode(c, f, ri, (char *)&dev, devlen, phys_ofs, ALLOC_NORMAL);
+
+ jffs2_free_raw_inode(ri);
+
+@@ -869,13 +642,6 @@
+ f->metadata = fn;
+ up(&f->sem);
+
+- /* Work out where to put the dirent node now. */
+- writtenlen = (writtenlen+3)&~3;
+- phys_ofs += writtenlen;
+- alloclen -= writtenlen;
+-
+- if (alloclen < sizeof(*rd)+namelen) {
+- /* Not enough space left in this chunk. Get some more */
+ jffs2_complete_reservation(c);
+ ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL);
+ if (ret) {
+@@ -883,7 +649,6 @@
+ jffs2_clear_inode(inode);
+ return ret;
+ }
+- }
+
+ rd = jffs2_alloc_raw_dirent();
+ if (!rd) {
+@@ -896,44 +661,45 @@
+ dir_f = JFFS2_INODE_INFO(dir_i);
+ down(&dir_f->sem);
+
+- rd->magic = JFFS2_MAGIC_BITMASK;
+- rd->nodetype = JFFS2_NODETYPE_DIRENT;
+- rd->totlen = sizeof(*rd) + namelen;
+- rd->hdr_crc = crc32(0, rd, sizeof(struct jffs2_unknown_node)-4);
+-
+- rd->pino = dir_i->i_ino;
+- rd->version = ++dir_f->highest_version;
+- rd->ino = inode->i_ino;
+- rd->mctime = CURRENT_TIME;
++ rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
++ rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT);
++ rd->totlen = cpu_to_je32(sizeof(*rd) + namelen);
++ rd->hdr_crc = cpu_to_je32(crc32(0, rd, sizeof(struct jffs2_unknown_node)-4));
++
++ rd->pino = cpu_to_je32(dir_i->i_ino);
++ rd->version = cpu_to_je32(++dir_f->highest_version);
++ rd->ino = cpu_to_je32(inode->i_ino);
++ rd->mctime = cpu_to_je32(get_seconds());
+ rd->nsize = namelen;
+
+ /* XXX: This is ugly. */
+ rd->type = (mode & S_IFMT) >> 12;
+
+- rd->node_crc = crc32(0, rd, sizeof(*rd)-8);
+- rd->name_crc = crc32(0, dentry->d_name.name, namelen);
+-
+- fd = jffs2_write_dirent(dir_i, rd, dentry->d_name.name, namelen, phys_ofs, &writtenlen);
++ rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8));
++ rd->name_crc = cpu_to_je32(crc32(0, dentry->d_name.name, namelen));
+
+- jffs2_complete_reservation(c);
++ fd = jffs2_write_dirent(c, dir_f, rd, dentry->d_name.name, namelen, phys_ofs, ALLOC_NORMAL);
+
+ if (IS_ERR(fd)) {
+ /* dirent failed to write. Delete the inode normally
+ as if it were the final unlink() */
++ jffs2_complete_reservation(c);
+ jffs2_free_raw_dirent(rd);
+ up(&dir_f->sem);
+ jffs2_clear_inode(inode);
+ return PTR_ERR(fd);
+ }
+
+- dir_i->i_mtime = dir_i->i_ctime = rd->mctime;
++ dir_i->i_mtime = dir_i->i_ctime = ITIME(je32_to_cpu(rd->mctime));
+
+ jffs2_free_raw_dirent(rd);
+
+ /* Link the fd into the inode's list, obsoleting an old
+ one if necessary. */
+ jffs2_add_fd_to_list(c, fd, &dir_f->dents);
++
+ up(&dir_f->sem);
++ jffs2_complete_reservation(c);
+
+ d_instantiate(dentry, inode);
+
+@@ -944,7 +710,9 @@
+ struct inode *new_dir_i, struct dentry *new_dentry)
+ {
+ int ret;
++ struct jffs2_sb_info *c = JFFS2_SB_INFO(old_dir_i->i_sb);
+ struct jffs2_inode_info *victim_f = NULL;
++ uint8_t type;
+
+ /* The VFS will check for us and prevent trying to rename a
+ * file over a directory and vice versa, but if it's a directory,
+@@ -973,7 +741,15 @@
+ */
+
+ /* Make a hard link */
+- ret = jffs2_do_link(old_dentry, new_dir_i, new_dentry, 1);
++
++ /* XXX: This is ugly */
++ type = (old_dentry->d_inode->i_mode & S_IFMT) >> 12;
++ if (!type) type = DT_REG;
++
++ ret = jffs2_do_link(c, JFFS2_INODE_INFO(new_dir_i),
++ old_dentry->d_inode->i_ino, type,
++ new_dentry->d_name.name, new_dentry->d_name.len);
++
+ if (ret)
+ return ret;
+
+@@ -989,22 +765,36 @@
+ }
+ }
+
++ /* If it was a directory we moved, and there was no victim,
++ increase i_nlink on its new parent */
++ if (S_ISDIR(old_dentry->d_inode->i_mode) && !victim_f)
++ new_dir_i->i_nlink++;
++
+ /* Unlink the original */
+- ret = jffs2_do_unlink(old_dir_i, old_dentry, 1);
++ ret = jffs2_do_unlink(c, JFFS2_INODE_INFO(old_dir_i),
++ old_dentry->d_name.name, old_dentry->d_name.len, NULL);
++
++ /* We don't touch inode->i_nlink */
+
+ if (ret) {
+ /* Oh shit. We really ought to make a single node which can do both atomically */
+ struct jffs2_inode_info *f = JFFS2_INODE_INFO(old_dentry->d_inode);
+ down(&f->sem);
++ old_dentry->d_inode->i_nlink++;
+ if (f->inocache)
+- old_dentry->d_inode->i_nlink = f->inocache->nlink++;
++ f->inocache->nlink++;
+ up(&f->sem);
+
+ printk(KERN_NOTICE "jffs2_rename(): Link succeeded, unlink failed (err %d). You now have a hard link\n", ret);
+ /* Might as well let the VFS know */
+ d_instantiate(new_dentry, old_dentry->d_inode);
+ atomic_inc(&old_dentry->d_inode->i_count);
+- }
+ return ret;
++ }
++
++ if (S_ISDIR(old_dentry->d_inode->i_mode))
++ old_dir_i->i_nlink--;
++
++ return 0;
+ }
+
+diff -Nurb linux-mips-2.4.27/fs/jffs2/erase.c linux/fs/jffs2/erase.c
+--- linux-mips-2.4.27/fs/jffs2/erase.c 2003-11-17 02:07:44.000000000 +0100
++++ linux/fs/jffs2/erase.c 2004-11-19 10:25:12.099169392 +0100
+@@ -1,68 +1,60 @@
+ /*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+- * Copyright (C) 2001 Red Hat, Inc.
++ * Copyright (C) 2001-2003 Red Hat, Inc.
+ *
+- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
++ * Created by David Woodhouse <dwmw2@redhat.com>
+ *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
++ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+- * The contents of this file are subject to the Red Hat eCos Public
+- * License Version 1.1 (the "Licence"); you may not use this file
+- * except in compliance with the Licence. You may obtain a copy of
+- * the Licence at http://www.redhat.com/
+- *
+- * Software distributed under the Licence is distributed on an "AS IS"
+- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+- * See the Licence for the specific language governing rights and
+- * limitations under the Licence.
+- *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
+- *
+- * Alternatively, the contents of this file may be used under the
+- * terms of the GNU General Public License version 2 (the "GPL"), in
+- * which case the provisions of the GPL are applicable instead of the
+- * above. If you wish to allow the use of your version of this file
+- * only under the terms of the GPL and not to allow others to use your
+- * version of this file under the RHEPL, indicate your decision by
+- * deleting the provisions above and replace them with the notice and
+- * other provisions required by the GPL. If you do not delete the
+- * provisions above, a recipient may use your version of this file
+- * under either the RHEPL or the GPL.
+- *
+- * $Id$
++ * $Id$
+ *
+ */
++
+ #include <linux/kernel.h>
+ #include <linux/slab.h>
+ #include <linux/mtd/mtd.h>
+-#include <linux/jffs2.h>
+-#include <linux/interrupt.h>
+-#include "nodelist.h"
++#include <linux/compiler.h>
+ #include <linux/crc32.h>
++#include <linux/sched.h>
++#include <linux/pagemap.h>
++#include "nodelist.h"
+
+ struct erase_priv_struct {
+ struct jffs2_eraseblock *jeb;
+ struct jffs2_sb_info *c;
+ };
+
++#ifndef __ECOS
+ static void jffs2_erase_callback(struct erase_info *);
++#endif
++static void jffs2_erase_failed(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
++static void jffs2_erase_succeeded(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
+ static void jffs2_free_all_node_refs(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
++static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
+
+ void jffs2_erase_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
+ {
+- struct erase_info *instr;
+ int ret;
++#ifdef __ECOS
++ ret = jffs2_flash_erase(c, jeb);
++ if (!ret) {
++ jffs2_erase_succeeded(c, jeb);
++ return;
++ }
++#else /* Linux */
++ struct erase_info *instr;
+
+ instr = kmalloc(sizeof(struct erase_info) + sizeof(struct erase_priv_struct), GFP_KERNEL);
+ if (!instr) {
+ printk(KERN_WARNING "kmalloc for struct erase_info in jffs2_erase_block failed. Refiling block for later\n");
+- spin_lock_bh(&c->erase_completion_lock);
++ spin_lock(&c->erase_completion_lock);
+ list_del(&jeb->list);
+ list_add(&jeb->list, &c->erase_pending_list);
+ c->erasing_size -= c->sector_size;
+- spin_unlock_bh(&c->erase_completion_lock);
++ c->dirty_size += c->sector_size;
++ jeb->dirty_size = c->sector_size;
++ spin_unlock(&c->erase_completion_lock);
+ return;
+ }
+
+@@ -77,19 +69,27 @@
+ ((struct erase_priv_struct *)instr->priv)->jeb = jeb;
+ ((struct erase_priv_struct *)instr->priv)->c = c;
+
++ /* NAND , read out the fail counter, if possible */
++ if (!jffs2_can_mark_obsolete(c))
++ jffs2_nand_read_failcnt(c,jeb);
++
+ ret = c->mtd->erase(c->mtd, instr);
+- if (!ret) {
++ if (!ret)
+ return;
+- }
++
++ kfree(instr);
++#endif /* __ECOS */
++
+ if (ret == -ENOMEM || ret == -EAGAIN) {
+ /* Erase failed immediately. Refile it on the list */
+ D1(printk(KERN_DEBUG "Erase at 0x%08x failed: %d. Refiling on erase_pending_list\n", jeb->offset, ret));
+- spin_lock_bh(&c->erase_completion_lock);
++ spin_lock(&c->erase_completion_lock);
+ list_del(&jeb->list);
+ list_add(&jeb->list, &c->erase_pending_list);
+ c->erasing_size -= c->sector_size;
+- spin_unlock_bh(&c->erase_completion_lock);
+- kfree(instr);
++ c->dirty_size += c->sector_size;
++ jeb->dirty_size = c->sector_size;
++ spin_unlock(&c->erase_completion_lock);
+ return;
+ }
+
+@@ -97,74 +97,101 @@
+ printk(KERN_WARNING "Erase at 0x%08x failed immediately: -EROFS. Is the sector locked?\n", jeb->offset);
+ else
+ printk(KERN_WARNING "Erase at 0x%08x failed immediately: errno %d\n", jeb->offset, ret);
+- spin_lock_bh(&c->erase_completion_lock);
+- list_del(&jeb->list);
+- list_add(&jeb->list, &c->bad_list);
+- c->nr_erasing_blocks--;
+- c->bad_size += c->sector_size;
+- c->erasing_size -= c->sector_size;
+- spin_unlock_bh(&c->erase_completion_lock);
+- wake_up(&c->erase_wait);
+- kfree(instr);
++
++ jffs2_erase_failed(c, jeb);
+ }
+
+-void jffs2_erase_pending_blocks(struct jffs2_sb_info *c)
++void jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count)
+ {
+ struct jffs2_eraseblock *jeb;
+
+- spin_lock_bh(&c->erase_completion_lock);
+- while (!list_empty(&c->erase_pending_list)) {
++ down(&c->erase_free_sem);
+
+- jeb = list_entry(c->erase_pending_list.next, struct jffs2_eraseblock, list);
++ spin_lock(&c->erase_completion_lock);
+
+- D1(printk(KERN_DEBUG "Starting erase of pending block 0x%08x\n", jeb->offset));
++ while (!list_empty(&c->erase_complete_list) ||
++ !list_empty(&c->erase_pending_list)) {
+
++ if (!list_empty(&c->erase_complete_list)) {
++ jeb = list_entry(c->erase_complete_list.next, struct jffs2_eraseblock, list);
++ list_del(&jeb->list);
++ spin_unlock(&c->erase_completion_lock);
++ jffs2_mark_erased_block(c, jeb);
++
++ if (!--count) {
++ D1(printk(KERN_DEBUG "Count reached. jffs2_erase_pending_blocks leaving\n"));
++ goto done;
++ }
++
++ } else if (!list_empty(&c->erase_pending_list)) {
++ jeb = list_entry(c->erase_pending_list.next, struct jffs2_eraseblock, list);
++ D1(printk(KERN_DEBUG "Starting erase of pending block 0x%08x\n", jeb->offset));
+ list_del(&jeb->list);
+ c->erasing_size += c->sector_size;
++ c->wasted_size -= jeb->wasted_size;
+ c->free_size -= jeb->free_size;
+ c->used_size -= jeb->used_size;
+ c->dirty_size -= jeb->dirty_size;
+- jeb->used_size = jeb->dirty_size = jeb->free_size = 0;
++ jeb->wasted_size = jeb->used_size = jeb->dirty_size = jeb->free_size = 0;
+ jffs2_free_all_node_refs(c, jeb);
+ list_add(&jeb->list, &c->erasing_list);
+- spin_unlock_bh(&c->erase_completion_lock);
++ spin_unlock(&c->erase_completion_lock);
+
+ jffs2_erase_block(c, jeb);
++
++ } else {
++ BUG();
++ }
++
+ /* Be nice */
+- if (current->need_resched)
+- schedule();
+- spin_lock_bh(&c->erase_completion_lock);
++ cond_resched();
++ spin_lock(&c->erase_completion_lock);
+ }
+- spin_unlock_bh(&c->erase_completion_lock);
++
++ spin_unlock(&c->erase_completion_lock);
++ done:
+ D1(printk(KERN_DEBUG "jffs2_erase_pending_blocks completed\n"));
++
++ up(&c->erase_free_sem);
++}
++
++static void jffs2_erase_succeeded(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
++{
++ D1(printk(KERN_DEBUG "Erase completed successfully at 0x%08x\n", jeb->offset));
++ spin_lock(&c->erase_completion_lock);
++ list_del(&jeb->list);
++ list_add_tail(&jeb->list, &c->erase_complete_list);
++ spin_unlock(&c->erase_completion_lock);
++ /* Ensure that kupdated calls us again to mark them clean */
++ jffs2_erase_pending_trigger(c);
+ }
+
++static void jffs2_erase_failed(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
++{
++ spin_lock(&c->erase_completion_lock);
++ c->erasing_size -= c->sector_size;
++ c->bad_size += c->sector_size;
++ list_del(&jeb->list);
++ list_add(&jeb->list, &c->bad_list);
++ c->nr_erasing_blocks--;
++ spin_unlock(&c->erase_completion_lock);
++ wake_up(&c->erase_wait);
++}
+
++#ifndef __ECOS
+ static void jffs2_erase_callback(struct erase_info *instr)
+ {
+ struct erase_priv_struct *priv = (void *)instr->priv;
+
+ if(instr->state != MTD_ERASE_DONE) {
+ printk(KERN_WARNING "Erase at 0x%08x finished, but state != MTD_ERASE_DONE. State is 0x%x instead.\n", instr->addr, instr->state);
+- spin_lock(&priv->c->erase_completion_lock);
+- priv->c->erasing_size -= priv->c->sector_size;
+- priv->c->bad_size += priv->c->sector_size;
+- list_del(&priv->jeb->list);
+- list_add(&priv->jeb->list, &priv->c->bad_list);
+- priv->c->nr_erasing_blocks--;
+- spin_unlock(&priv->c->erase_completion_lock);
+- wake_up(&priv->c->erase_wait);
++ jffs2_erase_failed(priv->c, priv->jeb);
+ } else {
+- D1(printk(KERN_DEBUG "Erase completed successfully at 0x%08x\n", instr->addr));
+- spin_lock(&priv->c->erase_completion_lock);
+- list_del(&priv->jeb->list);
+- list_add_tail(&priv->jeb->list, &priv->c->erase_complete_list);
+- spin_unlock(&priv->c->erase_completion_lock);
++ jffs2_erase_succeeded(priv->c, priv->jeb);
+ }
+- /* Make sure someone picks up the block off the erase_complete list */
+- OFNI_BS_2SFFJ(priv->c)->s_dirt = 1;
+ kfree(instr);
+ }
++#endif /* !__ECOS */
+
+ /* Hmmm. Maybe we should accept the extra space it takes and make
+ this a standard doubly-linked list? */
+@@ -221,7 +248,7 @@
+ this = ic->nodes;
+
+ while(this) {
+- printk( "0x%08x(%d)->", this->flash_offset & ~3, this->flash_offset &3);
++ printk( "0x%08x(%d)->", ref_offset(this), ref_flags(this));
+ if (++i == 5) {
+ printk("\n" KERN_DEBUG);
+ i=0;
+@@ -256,54 +283,43 @@
+ jeb->last_node = NULL;
+ }
+
+-void jffs2_erase_pending_trigger(struct jffs2_sb_info *c)
++static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
+ {
+- OFNI_BS_2SFFJ(c)->s_dirt = 1;
+-}
+-
+-void jffs2_mark_erased_blocks(struct jffs2_sb_info *c)
+-{
+- static struct jffs2_unknown_node marker = {JFFS2_MAGIC_BITMASK, JFFS2_NODETYPE_CLEANMARKER, sizeof(struct jffs2_unknown_node)};
+- struct jffs2_eraseblock *jeb;
+- struct jffs2_raw_node_ref *marker_ref;
++ struct jffs2_raw_node_ref *marker_ref = NULL;
+ unsigned char *ebuf;
+- ssize_t retlen;
++ size_t retlen;
+ int ret;
+
+- marker.hdr_crc = crc32(0, &marker, sizeof(struct jffs2_unknown_node)-4);
+-
+- spin_lock_bh(&c->erase_completion_lock);
+- while (!list_empty(&c->erase_complete_list)) {
+- jeb = list_entry(c->erase_complete_list.next, struct jffs2_eraseblock, list);
+- list_del(&jeb->list);
+- spin_unlock_bh(&c->erase_completion_lock);
+-
++ if (!jffs2_cleanmarker_oob(c)) {
+ marker_ref = jffs2_alloc_raw_node_ref();
+ if (!marker_ref) {
+ printk(KERN_WARNING "Failed to allocate raw node ref for clean marker\n");
+- /* Come back later */
++ /* Stick it back on the list from whence it came and come back later */
+ jffs2_erase_pending_trigger(c);
++ spin_lock(&c->erase_completion_lock);
++ list_add(&jeb->list, &c->erase_complete_list);
++ spin_unlock(&c->erase_completion_lock);
+ return;
+ }
+-
++ }
+ ebuf = kmalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!ebuf) {
+ printk(KERN_WARNING "Failed to allocate page buffer for verifying erase at 0x%08x. Assuming it worked\n", jeb->offset);
+ } else {
+- __u32 ofs = jeb->offset;
++ uint32_t ofs = jeb->offset;
+
+ D1(printk(KERN_DEBUG "Verifying erase at 0x%08x\n", jeb->offset));
+ while(ofs < jeb->offset + c->sector_size) {
+- __u32 readlen = min((__u32)PAGE_SIZE, jeb->offset + c->sector_size - ofs);
++ uint32_t readlen = min((uint32_t)PAGE_SIZE, jeb->offset + c->sector_size - ofs);
+ int i;
+
+- ret = c->mtd->read(c->mtd, ofs, readlen, &retlen, ebuf);
+- if (ret < 0) {
++ ret = jffs2_flash_read(c, ofs, readlen, &retlen, ebuf);
++ if (ret) {
+ printk(KERN_WARNING "Read of newly-erased block at 0x%08x failed: %d. Putting on bad_list\n", ofs, ret);
+ goto bad;
+ }
+ if (retlen != readlen) {
+- printk(KERN_WARNING "Short read from newly-erased block at 0x%08x. Wanted %d, got %d\n", ofs, readlen, retlen);
++ printk(KERN_WARNING "Short read from newly-erased block at 0x%08x. Wanted %d, got %zd\n", ofs, readlen, retlen);
+ goto bad;
+ }
+ for (i=0; i<readlen; i += sizeof(unsigned long)) {
+@@ -312,62 +328,89 @@
+ if (datum + 1) {
+ printk(KERN_WARNING "Newly-erased block contained word 0x%lx at offset 0x%08x\n", datum, ofs + i);
+ bad:
++ if (!jffs2_cleanmarker_oob(c))
+ jffs2_free_raw_node_ref(marker_ref);
++ else
++ jffs2_write_nand_badblock( c ,jeb );
+ kfree(ebuf);
+ bad2:
+- spin_lock_bh(&c->erase_completion_lock);
++ spin_lock(&c->erase_completion_lock);
+ c->erasing_size -= c->sector_size;
+ c->bad_size += c->sector_size;
+
+ list_add_tail(&jeb->list, &c->bad_list);
+ c->nr_erasing_blocks--;
+- spin_unlock_bh(&c->erase_completion_lock);
++ spin_unlock(&c->erase_completion_lock);
+ wake_up(&c->erase_wait);
+ return;
+ }
+ }
+ ofs += readlen;
++ cond_resched();
+ }
+ kfree(ebuf);
+ }
+
+ /* Write the erase complete marker */
+ D1(printk(KERN_DEBUG "Writing erased marker to block at 0x%08x\n", jeb->offset));
+- ret = c->mtd->write(c->mtd, jeb->offset, sizeof(marker), &retlen, (char *)&marker);
++ if (jffs2_cleanmarker_oob(c)) {
++
++ if (jffs2_write_nand_cleanmarker(c, jeb))
++ goto bad2;
++
++ jeb->first_node = jeb->last_node = NULL;
++
++ jeb->free_size = c->sector_size;
++ jeb->used_size = 0;
++ jeb->dirty_size = 0;
++ jeb->wasted_size = 0;
++ } else {
++ struct jffs2_unknown_node marker = {
++ .magic = cpu_to_je16(JFFS2_MAGIC_BITMASK),
++ .nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER),
++ .totlen = cpu_to_je32(c->cleanmarker_size)
++ };
++
++ marker.hdr_crc = cpu_to_je32(crc32(0, &marker, sizeof(struct jffs2_unknown_node)-4));
++
++ /* We only write the header; the rest was noise or padding anyway */
++ ret = jffs2_flash_write(c, jeb->offset, sizeof(marker), &retlen, (char *)&marker);
+ if (ret) {
+ printk(KERN_WARNING "Write clean marker to block at 0x%08x failed: %d\n",
+ jeb->offset, ret);
+ goto bad2;
+ }
+ if (retlen != sizeof(marker)) {
+- printk(KERN_WARNING "Short write to newly-erased block at 0x%08x: Wanted %d, got %d\n",
++ printk(KERN_WARNING "Short write to newly-erased block at 0x%08x: Wanted %d, got %zd\n",
+ jeb->offset, sizeof(marker), retlen);
+ goto bad2;
+ }
+
+ marker_ref->next_in_ino = NULL;
+ marker_ref->next_phys = NULL;
+- marker_ref->flash_offset = jeb->offset;
+- marker_ref->totlen = PAD(sizeof(marker));
++ marker_ref->flash_offset = jeb->offset | REF_NORMAL;
++ marker_ref->__totlen = c->cleanmarker_size;
+
+ jeb->first_node = jeb->last_node = marker_ref;
+
+- jeb->free_size = c->sector_size - marker_ref->totlen;
+- jeb->used_size = marker_ref->totlen;
++ jeb->free_size = c->sector_size - c->cleanmarker_size;
++ jeb->used_size = c->cleanmarker_size;
+ jeb->dirty_size = 0;
++ jeb->wasted_size = 0;
++ }
+
+- spin_lock_bh(&c->erase_completion_lock);
++ spin_lock(&c->erase_completion_lock);
+ c->erasing_size -= c->sector_size;
+ c->free_size += jeb->free_size;
+ c->used_size += jeb->used_size;
+
+ ACCT_SANITY_CHECK(c,jeb);
+- ACCT_PARANOIA_CHECK(jeb);
++ D1(ACCT_PARANOIA_CHECK(jeb));
+
+ list_add_tail(&jeb->list, &c->free_list);
+ c->nr_erasing_blocks--;
+ c->nr_free_blocks++;
++ spin_unlock(&c->erase_completion_lock);
+ wake_up(&c->erase_wait);
+- }
+- spin_unlock_bh(&c->erase_completion_lock);
+ }
++
+diff -Nurb linux-mips-2.4.27/fs/jffs2/file.c linux/fs/jffs2/file.c
+--- linux-mips-2.4.27/fs/jffs2/file.c 2003-11-17 02:07:44.000000000 +0100
++++ linux/fs/jffs2/file.c 2004-11-19 10:25:12.101169088 +0100
+@@ -1,319 +1,106 @@
+ /*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+- * Copyright (C) 2001 Red Hat, Inc.
++ * Copyright (C) 2001-2003 Red Hat, Inc.
+ *
+- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
++ * Created by David Woodhouse <dwmw2@redhat.com>
+ *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
++ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+- * The contents of this file are subject to the Red Hat eCos Public
+- * License Version 1.1 (the "Licence"); you may not use this file
+- * except in compliance with the Licence. You may obtain a copy of
+- * the Licence at http://www.redhat.com/
+- *
+- * Software distributed under the Licence is distributed on an "AS IS"
+- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+- * See the Licence for the specific language governing rights and
+- * limitations under the Licence.
+- *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
+- *
+- * Alternatively, the contents of this file may be used under the
+- * terms of the GNU General Public License version 2 (the "GPL"), in
+- * which case the provisions of the GPL are applicable instead of the
+- * above. If you wish to allow the use of your version of this file
+- * only under the terms of the GPL and not to allow others to use your
+- * version of this file under the RHEPL, indicate your decision by
+- * deleting the provisions above and replace them with the notice and
+- * other provisions required by the GPL. If you do not delete the
+- * provisions above, a recipient may use your version of this file
+- * under either the RHEPL or the GPL.
+- *
+- * $Id$
++ * $Id$
+ *
+ */
+
++#include <linux/version.h>
+ #include <linux/kernel.h>
+-#include <linux/mtd/compatmac.h> /* for min() */
+ #include <linux/slab.h>
+ #include <linux/fs.h>
++#include <linux/time.h>
+ #include <linux/pagemap.h>
++#include <linux/highmem.h>
++#include <linux/crc32.h>
+ #include <linux/jffs2.h>
+ #include "nodelist.h"
+-#include <linux/crc32.h>
+
+ extern int generic_file_open(struct inode *, struct file *) __attribute__((weak));
+ extern loff_t generic_file_llseek(struct file *file, loff_t offset, int origin) __attribute__((weak));
+
+
+-int jffs2_null_fsync(struct file *filp, struct dentry *dentry, int datasync)
++int jffs2_fsync(struct file *filp, struct dentry *dentry, int datasync)
+ {
+- /* Move along. Nothing to see here */
++ struct inode *inode = dentry->d_inode;
++ struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
++
++ /* Trigger GC to flush any pending writes for this inode */
++ jffs2_flush_wbuf_gc(c, inode->i_ino);
++
+ return 0;
+ }
+
+ struct file_operations jffs2_file_operations =
+ {
+- llseek: generic_file_llseek,
+- open: generic_file_open,
+- read: generic_file_read,
+- write: generic_file_write,
+- ioctl: jffs2_ioctl,
+- mmap: generic_file_mmap,
+- fsync: jffs2_null_fsync
++ .llseek = generic_file_llseek,
++ .open = generic_file_open,
++ .read = generic_file_read,
++ .write = generic_file_write,
++ .ioctl = jffs2_ioctl,
++ .mmap = generic_file_readonly_mmap,
++ .fsync = jffs2_fsync,
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,29)
++ .sendfile = generic_file_sendfile
++#endif
+ };
+
+ /* jffs2_file_inode_operations */
+
+ struct inode_operations jffs2_file_inode_operations =
+ {
+- setattr: jffs2_setattr
++ .setattr = jffs2_setattr
+ };
+
+ struct address_space_operations jffs2_file_address_operations =
+ {
+- readpage: jffs2_readpage,
+- prepare_write: jffs2_prepare_write,
+- commit_write: jffs2_commit_write
++ .readpage = jffs2_readpage,
++ .prepare_write =jffs2_prepare_write,
++ .commit_write = jffs2_commit_write
+ };
+
+-int jffs2_setattr (struct dentry *dentry, struct iattr *iattr)
+-{
+- struct jffs2_full_dnode *old_metadata, *new_metadata;
+- struct inode *inode = dentry->d_inode;
+- struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
+- struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
+- struct jffs2_raw_inode *ri;
+- unsigned short dev;
+- unsigned char *mdata = NULL;
+- int mdatalen = 0;
+- unsigned int ivalid;
+- __u32 phys_ofs, alloclen;
+- int ret;
+- D1(printk(KERN_DEBUG "jffs2_setattr(): ino #%lu\n", inode->i_ino));
+- ret = inode_change_ok(inode, iattr);
+- if (ret)
+- return ret;
+-
+- /* Special cases - we don't want more than one data node
+- for these types on the medium at any time. So setattr
+- must read the original data associated with the node
+- (i.e. the device numbers or the target name) and write
+- it out again with the appropriate data attached */
+- if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode)) {
+- /* For these, we don't actually need to read the old node */
+- dev = (MAJOR(to_kdev_t(dentry->d_inode->i_rdev)) << 8) |
+- MINOR(to_kdev_t(dentry->d_inode->i_rdev));
+- mdata = (char *)&dev;
+- mdatalen = sizeof(dev);
+- D1(printk(KERN_DEBUG "jffs2_setattr(): Writing %d bytes of kdev_t\n", mdatalen));
+- } else if (S_ISLNK(inode->i_mode)) {
+- mdatalen = f->metadata->size;
+- mdata = kmalloc(f->metadata->size, GFP_USER);
+- if (!mdata)
+- return -ENOMEM;
+- ret = jffs2_read_dnode(c, f->metadata, mdata, 0, mdatalen);
+- if (ret) {
+- kfree(mdata);
+- return ret;
+- }
+- D1(printk(KERN_DEBUG "jffs2_setattr(): Writing %d bytes of symlink target\n", mdatalen));
+- }
+-
+- ri = jffs2_alloc_raw_inode();
+- if (!ri) {
+- if (S_ISLNK(inode->i_mode))
+- kfree(mdata);
+- return -ENOMEM;
+- }
+-
+- ret = jffs2_reserve_space(c, sizeof(*ri) + mdatalen, &phys_ofs, &alloclen, ALLOC_NORMAL);
+- if (ret) {
+- jffs2_free_raw_inode(ri);
+- if (S_ISLNK(inode->i_mode))
+- kfree(mdata);
+- return ret;
+- }
+- down(&f->sem);
+- ivalid = iattr->ia_valid;
+-
+- ri->magic = JFFS2_MAGIC_BITMASK;
+- ri->nodetype = JFFS2_NODETYPE_INODE;
+- ri->totlen = sizeof(*ri) + mdatalen;
+- ri->hdr_crc = crc32(0, ri, sizeof(struct jffs2_unknown_node)-4);
+-
+- ri->ino = inode->i_ino;
+- ri->version = ++f->highest_version;
+-
+- ri->mode = (ivalid & ATTR_MODE)?iattr->ia_mode:inode->i_mode;
+- ri->uid = (ivalid & ATTR_UID)?iattr->ia_uid:inode->i_uid;
+- ri->gid = (ivalid & ATTR_GID)?iattr->ia_gid:inode->i_gid;
+-
+- if (ivalid & ATTR_MODE && ri->mode & S_ISGID &&
+- !in_group_p(ri->gid) && !capable(CAP_FSETID))
+- ri->mode &= ~S_ISGID;
+-
+- ri->isize = (ivalid & ATTR_SIZE)?iattr->ia_size:inode->i_size;
+- ri->atime = (ivalid & ATTR_ATIME)?iattr->ia_atime:inode->i_atime;
+- ri->mtime = (ivalid & ATTR_MTIME)?iattr->ia_mtime:inode->i_mtime;
+- ri->ctime = (ivalid & ATTR_CTIME)?iattr->ia_ctime:inode->i_ctime;
+-
+- ri->offset = 0;
+- ri->csize = ri->dsize = mdatalen;
+- ri->compr = JFFS2_COMPR_NONE;
+- if (inode->i_size < ri->isize) {
+- /* It's an extension. Make it a hole node */
+- ri->compr = JFFS2_COMPR_ZERO;
+- ri->dsize = ri->isize - inode->i_size;
+- ri->offset = inode->i_size;
+- }
+- ri->node_crc = crc32(0, ri, sizeof(*ri)-8);
+- if (mdatalen)
+- ri->data_crc = crc32(0, mdata, mdatalen);
+- else
+- ri->data_crc = 0;
+-
+- new_metadata = jffs2_write_dnode(inode, ri, mdata, mdatalen, phys_ofs, NULL);
+- if (S_ISLNK(inode->i_mode))
+- kfree(mdata);
+-
+- jffs2_complete_reservation(c);
+-
+- if (IS_ERR(new_metadata)) {
+- jffs2_free_raw_inode(ri);
+- up(&f->sem);
+- return PTR_ERR(new_metadata);
+- }
+- /* It worked. Update the inode */
+- inode->i_atime = ri->atime;
+- inode->i_ctime = ri->ctime;
+- inode->i_mtime = ri->mtime;
+- inode->i_mode = ri->mode;
+- inode->i_uid = ri->uid;
+- inode->i_gid = ri->gid;
+-
+-
+- old_metadata = f->metadata;
+-
+- if (inode->i_size > ri->isize) {
+- vmtruncate(inode, ri->isize);
+- jffs2_truncate_fraglist (c, &f->fraglist, ri->isize);
+- }
+-
+- if (inode->i_size < ri->isize) {
+- jffs2_add_full_dnode_to_inode(c, f, new_metadata);
+- inode->i_size = ri->isize;
+- f->metadata = NULL;
+- } else {
+- f->metadata = new_metadata;
+- }
+- if (old_metadata) {
+- jffs2_mark_node_obsolete(c, old_metadata->raw);
+- jffs2_free_full_dnode(old_metadata);
+- }
+- jffs2_free_raw_inode(ri);
+- up(&f->sem);
+- return 0;
+-}
+-
+ int jffs2_do_readpage_nolock (struct inode *inode, struct page *pg)
+ {
+ struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
+ struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
+- struct jffs2_node_frag *frag = f->fraglist;
+- __u32 offset = pg->index << PAGE_CACHE_SHIFT;
+- __u32 end = offset + PAGE_CACHE_SIZE;
+ unsigned char *pg_buf;
+ int ret;
+
+- D1(printk(KERN_DEBUG "jffs2_do_readpage_nolock(): ino #%lu, page at offset 0x%x\n", inode->i_ino, offset));
++ D2(printk(KERN_DEBUG "jffs2_do_readpage_nolock(): ino #%lu, page at offset 0x%lx\n", inode->i_ino, pg->index << PAGE_CACHE_SHIFT));
+
+ if (!PageLocked(pg))
+ PAGE_BUG(pg);
+
+- while(frag && frag->ofs + frag->size <= offset) {
+- // D1(printk(KERN_DEBUG "skipping frag %d-%d; before the region we care about\n", frag->ofs, frag->ofs + frag->size));
+- frag = frag->next;
+- }
+-
+ pg_buf = kmap(pg);
++ /* FIXME: Can kmap fail? */
+
+- /* XXX FIXME: Where a single physical node actually shows up in two
+- frags, we read it twice. Don't do that. */
+- /* Now we're pointing at the first frag which overlaps our page */
+- while(offset < end) {
+- D2(printk(KERN_DEBUG "jffs2_readpage: offset %d, end %d\n", offset, end));
+- if (!frag || frag->ofs > offset) {
+- __u32 holesize = end - offset;
+- if (frag) {
+- D1(printk(KERN_NOTICE "Eep. Hole in ino %ld fraglist. frag->ofs = 0x%08x, offset = 0x%08x\n", inode->i_ino, frag->ofs, offset));
+- holesize = min(holesize, frag->ofs - offset);
+- D1(jffs2_print_frag_list(f));
+- }
+- D1(printk(KERN_DEBUG "Filling non-frag hole from %d-%d\n", offset, offset+holesize));
+- memset(pg_buf, 0, holesize);
+- pg_buf += holesize;
+- offset += holesize;
+- continue;
+- } else if (frag->ofs < offset && (offset & (PAGE_CACHE_SIZE-1)) != 0) {
+- D1(printk(KERN_NOTICE "Eep. Overlap in ino #%ld fraglist. frag->ofs = 0x%08x, offset = 0x%08x\n",
+- inode->i_ino, frag->ofs, offset));
+- D1(jffs2_print_frag_list(f));
+- memset(pg_buf, 0, end - offset);
+- ClearPageUptodate(pg);
+- SetPageError(pg);
+- kunmap(pg);
+- return -EIO;
+- } else if (!frag->node) {
+- __u32 holeend = min(end, frag->ofs + frag->size);
+- D1(printk(KERN_DEBUG "Filling frag hole from %d-%d (frag 0x%x 0x%x)\n", offset, holeend, frag->ofs, frag->ofs + frag->size));
+- memset(pg_buf, 0, holeend - offset);
+- pg_buf += holeend - offset;
+- offset = holeend;
+- frag = frag->next;
+- continue;
+- } else {
+- __u32 readlen;
+- __u32 fragofs; /* offset within the frag to start reading */
++ ret = jffs2_read_inode_range(c, f, pg_buf, pg->index << PAGE_CACHE_SHIFT, PAGE_CACHE_SIZE);
+
+- fragofs = offset - frag->ofs;
+- readlen = min(frag->size - fragofs, end - offset);
+- D1(printk(KERN_DEBUG "Reading %d-%d from node at 0x%x\n", frag->ofs+fragofs,
+- fragofs+frag->ofs+readlen, frag->node->raw->flash_offset & ~3));
+- ret = jffs2_read_dnode(c, frag->node, pg_buf, fragofs + frag->ofs - frag->node->ofs, readlen);
+- D2(printk(KERN_DEBUG "node read done\n"));
+ if (ret) {
+- D1(printk(KERN_DEBUG"jffs2_readpage error %d\n",ret));
+- memset(pg_buf, 0, readlen);
+ ClearPageUptodate(pg);
+ SetPageError(pg);
+- kunmap(pg);
+- return ret;
+- }
+-
+- pg_buf += readlen;
+- offset += readlen;
+- frag = frag->next;
+- D2(printk(KERN_DEBUG "node read was OK. Looping\n"));
+- }
+- }
+- D2(printk(KERN_DEBUG "readpage finishing\n"));
++ } else {
+ SetPageUptodate(pg);
+ ClearPageError(pg);
++ }
+
+ flush_dcache_page(pg);
+-
+ kunmap(pg);
+- D1(printk(KERN_DEBUG "readpage finished\n"));
++
++ D2(printk(KERN_DEBUG "readpage finished\n"));
+ return 0;
+ }
+
+ int jffs2_do_readpage_unlock(struct inode *inode, struct page *pg)
+ {
+ int ret = jffs2_do_readpage_nolock(inode, pg);
+- UnlockPage(pg);
++ unlock_page(pg);
+ return ret;
+ }
+
+@@ -333,17 +120,17 @@
+ {
+ struct inode *inode = pg->mapping->host;
+ struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
+- __u32 pageofs = pg->index << PAGE_CACHE_SHIFT;
++ uint32_t pageofs = pg->index << PAGE_CACHE_SHIFT;
+ int ret = 0;
+
+- D1(printk(KERN_DEBUG "jffs2_prepare_write() nrpages %ld\n", inode->i_mapping->nrpages));
++ D1(printk(KERN_DEBUG "jffs2_prepare_write()\n"));
+
+ if (pageofs > inode->i_size) {
+ /* Make new hole frag from old EOF to new page */
+ struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
+ struct jffs2_raw_inode ri;
+ struct jffs2_full_dnode *fn;
+- __u32 phys_ofs, alloc_len;
++ uint32_t phys_ofs, alloc_len;
+
+ D1(printk(KERN_DEBUG "Writing new hole frag 0x%x-0x%x between current EOF and new page\n",
+ (unsigned int)inode->i_size, pageofs));
+@@ -355,29 +142,30 @@
+ down(&f->sem);
+ memset(&ri, 0, sizeof(ri));
+
+- ri.magic = JFFS2_MAGIC_BITMASK;
+- ri.nodetype = JFFS2_NODETYPE_INODE;
+- ri.totlen = sizeof(ri);
+- ri.hdr_crc = crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4);
+-
+- ri.ino = f->inocache->ino;
+- ri.version = ++f->highest_version;
+- ri.mode = inode->i_mode;
+- ri.uid = inode->i_uid;
+- ri.gid = inode->i_gid;
+- ri.isize = max((__u32)inode->i_size, pageofs);
+- ri.atime = ri.ctime = ri.mtime = CURRENT_TIME;
+- ri.offset = inode->i_size;
+- ri.dsize = pageofs - inode->i_size;
+- ri.csize = 0;
++ ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
++ ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE);
++ ri.totlen = cpu_to_je32(sizeof(ri));
++ ri.hdr_crc = cpu_to_je32(crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4));
++
++ ri.ino = cpu_to_je32(f->inocache->ino);
++ ri.version = cpu_to_je32(++f->highest_version);
++ ri.mode = cpu_to_jemode(inode->i_mode);
++ ri.uid = cpu_to_je16(inode->i_uid);
++ ri.gid = cpu_to_je16(inode->i_gid);
++ ri.isize = cpu_to_je32(max((uint32_t)inode->i_size, pageofs));
++ ri.atime = ri.ctime = ri.mtime = cpu_to_je32(get_seconds());
++ ri.offset = cpu_to_je32(inode->i_size);
++ ri.dsize = cpu_to_je32(pageofs - inode->i_size);
++ ri.csize = cpu_to_je32(0);
+ ri.compr = JFFS2_COMPR_ZERO;
+- ri.node_crc = crc32(0, &ri, sizeof(ri)-8);
+- ri.data_crc = 0;
++ ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8));
++ ri.data_crc = cpu_to_je32(0);
++
++ fn = jffs2_write_dnode(c, f, &ri, NULL, 0, phys_ofs, ALLOC_NORMAL);
+
+- fn = jffs2_write_dnode(inode, &ri, NULL, 0, phys_ofs, NULL);
+- jffs2_complete_reservation(c);
+ if (IS_ERR(fn)) {
+ ret = PTR_ERR(fn);
++ jffs2_complete_reservation(c);
+ up(&f->sem);
+ return ret;
+ }
+@@ -391,16 +179,17 @@
+ D1(printk(KERN_DEBUG "Eep. add_full_dnode_to_inode() failed in prepare_write, returned %d\n", ret));
+ jffs2_mark_node_obsolete(c, fn->raw);
+ jffs2_free_full_dnode(fn);
++ jffs2_complete_reservation(c);
+ up(&f->sem);
+ return ret;
+ }
++ jffs2_complete_reservation(c);
+ inode->i_size = pageofs;
+ up(&f->sem);
+ }
+
+-
+ /* Read in the page if it wasn't already present, unless it's a whole page */
+- if (!Page_Uptodate(pg) && (start || end < PAGE_CACHE_SIZE)) {
++ if (!PageUptodate(pg) && (start || end < PAGE_CACHE_SIZE)) {
+ down(&f->sem);
+ ret = jffs2_do_readpage_nolock(inode, pg);
+ up(&f->sem);
+@@ -417,14 +206,12 @@
+ struct inode *inode = pg->mapping->host;
+ struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
+ struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
+- __u32 newsize = max_t(__u32, filp->f_dentry->d_inode->i_size, (pg->index << PAGE_CACHE_SHIFT) + end);
+- __u32 file_ofs = (pg->index << PAGE_CACHE_SHIFT);
+- __u32 writelen = min((__u32)PAGE_CACHE_SIZE, newsize - file_ofs);
+ struct jffs2_raw_inode *ri;
+ int ret = 0;
+- ssize_t writtenlen = 0;
++ uint32_t writtenlen = 0;
+
+- D1(printk(KERN_DEBUG "jffs2_commit_write(): ino #%lu, page at 0x%lx, range %d-%d, flags %lx\n", inode->i_ino, pg->index << PAGE_CACHE_SHIFT, start, end, pg->flags));
++ D1(printk(KERN_DEBUG "jffs2_commit_write(): ino #%lu, page at 0x%lx, range %d-%d, flags %lx\n",
++ inode->i_ino, pg->index << PAGE_CACHE_SHIFT, start, end, pg->flags));
+
+ if (!start && end == PAGE_CACHE_SIZE) {
+ /* We need to avoid deadlock with page_cache_read() in
+@@ -435,109 +222,47 @@
+ }
+
+ ri = jffs2_alloc_raw_inode();
+- if (!ri)
+- return -ENOMEM;
+-
+- while(writelen) {
+- struct jffs2_full_dnode *fn;
+- unsigned char *comprbuf = NULL;
+- unsigned char comprtype = JFFS2_COMPR_NONE;
+- __u32 phys_ofs, alloclen;
+- __u32 datalen, cdatalen;
+
+- D2(printk(KERN_DEBUG "jffs2_commit_write() loop: 0x%x to write to 0x%x\n", writelen, file_ofs));
+-
+- ret = jffs2_reserve_space(c, sizeof(*ri) + JFFS2_MIN_DATA_LEN, &phys_ofs, &alloclen, ALLOC_NORMAL);
+- if (ret) {
+- SetPageError(pg);
+- D1(printk(KERN_DEBUG "jffs2_reserve_space returned %d\n", ret));
+- break;
++ if (!ri) {
++ D1(printk(KERN_DEBUG "jffs2_commit_write(): Allocation of raw inode failed\n"));
++ return -ENOMEM;
+ }
+- down(&f->sem);
+- datalen = writelen;
+- cdatalen = min(alloclen - sizeof(*ri), writelen);
+-
+- comprbuf = kmalloc(cdatalen, GFP_KERNEL);
+- if (comprbuf) {
+- comprtype = jffs2_compress(page_address(pg)+ (file_ofs & (PAGE_CACHE_SIZE-1)), comprbuf, &datalen, &cdatalen);
+- }
+- if (comprtype == JFFS2_COMPR_NONE) {
+- /* Either compression failed, or the allocation of comprbuf failed */
+- if (comprbuf)
+- kfree(comprbuf);
+- comprbuf = page_address(pg) + (file_ofs & (PAGE_CACHE_SIZE -1));
+- datalen = cdatalen;
+- }
+- /* Now comprbuf points to the data to be written, be it compressed or not.
+- comprtype holds the compression type, and comprtype == JFFS2_COMPR_NONE means
+- that the comprbuf doesn't need to be kfree()d.
+- */
+-
+- ri->magic = JFFS2_MAGIC_BITMASK;
+- ri->nodetype = JFFS2_NODETYPE_INODE;
+- ri->totlen = sizeof(*ri) + cdatalen;
+- ri->hdr_crc = crc32(0, ri, sizeof(struct jffs2_unknown_node)-4);
+-
+- ri->ino = inode->i_ino;
+- ri->version = ++f->highest_version;
+- ri->mode = inode->i_mode;
+- ri->uid = inode->i_uid;
+- ri->gid = inode->i_gid;
+- ri->isize = max((__u32)inode->i_size, file_ofs + datalen);
+- ri->atime = ri->ctime = ri->mtime = CURRENT_TIME;
+- ri->offset = file_ofs;
+- ri->csize = cdatalen;
+- ri->dsize = datalen;
+- ri->compr = comprtype;
+- ri->node_crc = crc32(0, ri, sizeof(*ri)-8);
+- ri->data_crc = crc32(0, comprbuf, cdatalen);
+-
+- fn = jffs2_write_dnode(inode, ri, comprbuf, cdatalen, phys_ofs, NULL);
+
+- jffs2_complete_reservation(c);
++ /* Set the fields that the generic jffs2_write_inode_range() code can't find */
++ ri->ino = cpu_to_je32(inode->i_ino);
++ ri->mode = cpu_to_jemode(inode->i_mode);
++ ri->uid = cpu_to_je16(inode->i_uid);
++ ri->gid = cpu_to_je16(inode->i_gid);
++ ri->isize = cpu_to_je32((uint32_t)inode->i_size);
++ ri->atime = ri->ctime = ri->mtime = cpu_to_je32(get_seconds());
++
++ /* In 2.4, it was already kmapped by generic_file_write(). Doesn't
++ hurt to do it again. The alternative is ifdefs, which are ugly. */
++ kmap(pg);
++
++ ret = jffs2_write_inode_range(c, f, ri, page_address(pg) + start,
++ (pg->index << PAGE_CACHE_SHIFT) + start,
++ end - start, &writtenlen);
+
+- if (comprtype != JFFS2_COMPR_NONE)
+- kfree(comprbuf);
++ kunmap(pg);
+
+- if (IS_ERR(fn)) {
+- ret = PTR_ERR(fn);
+- up(&f->sem);
+- SetPageError(pg);
+- break;
+- }
+- ret = jffs2_add_full_dnode_to_inode(c, f, fn);
+- if (f->metadata) {
+- jffs2_mark_node_obsolete(c, f->metadata->raw);
+- jffs2_free_full_dnode(f->metadata);
+- f->metadata = NULL;
+- }
+- up(&f->sem);
+ if (ret) {
+- /* Eep */
+- D1(printk(KERN_DEBUG "Eep. add_full_dnode_to_inode() failed in commit_write, returned %d\n", ret));
+- jffs2_mark_node_obsolete(c, fn->raw);
+- jffs2_free_full_dnode(fn);
++ /* There was an error writing. */
+ SetPageError(pg);
+- break;
+ }
+- inode->i_size = ri->isize;
++
++ if (writtenlen) {
++ if (inode->i_size < (pg->index << PAGE_CACHE_SHIFT) + start + writtenlen) {
++ inode->i_size = (pg->index << PAGE_CACHE_SHIFT) + start + writtenlen;
+ inode->i_blocks = (inode->i_size + 511) >> 9;
+- inode->i_ctime = inode->i_mtime = ri->ctime;
+- if (!datalen) {
+- printk(KERN_WARNING "Eep. We didn't actually write any bloody data\n");
+- ret = -EIO;
+- SetPageError(pg);
+- break;
++
++ inode->i_ctime = inode->i_mtime = ITIME(je32_to_cpu(ri->ctime));
+ }
+- D1(printk(KERN_DEBUG "increasing writtenlen by %d\n", datalen));
+- writtenlen += datalen;
+- file_ofs += datalen;
+- writelen -= datalen;
+ }
+
+ jffs2_free_raw_inode(ri);
+
+- if (writtenlen < end) {
++ if (start+writtenlen < end) {
+ /* generic_file_write has written more to the page cache than we've
+ actually written to the medium. Mark the page !Uptodate so that
+ it gets reread */
+@@ -545,13 +270,7 @@
+ SetPageError(pg);
+ ClearPageUptodate(pg);
+ }
+- if (writtenlen <= start) {
+- /* We didn't even get to the start of the affected part */
+- ret = ret?ret:-ENOSPC;
+- D1(printk(KERN_DEBUG "jffs2_commit_write(): Only %x bytes written to page. start (%x) not reached, returning %d\n", writtenlen, start, ret));
+- }
+- writtenlen = min(end-start, writtenlen-start);
+
+- D1(printk(KERN_DEBUG "jffs2_commit_write() returning %d. nrpages is %ld\n",writtenlen?writtenlen:ret, inode->i_mapping->nrpages));
++ D1(printk(KERN_DEBUG "jffs2_commit_write() returning %d\n",writtenlen?writtenlen:ret));
+ return writtenlen?writtenlen:ret;
+ }
+diff -Nurb linux-mips-2.4.27/fs/jffs2/fs.c linux/fs/jffs2/fs.c
+--- linux-mips-2.4.27/fs/jffs2/fs.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/fs/jffs2/fs.c 2004-11-19 10:25:12.102168936 +0100
+@@ -0,0 +1,618 @@
++/*
++ * JFFS2 -- Journalling Flash File System, Version 2.
++ *
++ * Copyright (C) 2001-2003 Red Hat, Inc.
++ *
++ * Created by David Woodhouse <dwmw2@redhat.com>
++ *
++ * For licensing information, see the file 'LICENCE' in this directory.
++ *
++ * $Id$
++ *
++ */
++
++#include <linux/version.h>
++#include <linux/config.h>
++#include <linux/kernel.h>
++#include <linux/sched.h>
++#include <linux/fs.h>
++#include <linux/list.h>
++#include <linux/mtd/mtd.h>
++#include <linux/pagemap.h>
++#include <linux/slab.h>
++#include <linux/vfs.h>
++#include <linux/crc32.h>
++#include "nodelist.h"
++
++
++static int jffs2_do_setattr (struct inode *inode, struct iattr *iattr)
++{
++ struct jffs2_full_dnode *old_metadata, *new_metadata;
++ struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
++ struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
++ struct jffs2_raw_inode *ri;
++ unsigned short dev;
++ unsigned char *mdata = NULL;
++ int mdatalen = 0;
++ unsigned int ivalid;
++ uint32_t phys_ofs, alloclen;
++ int ret;
++ D1(printk(KERN_DEBUG "jffs2_setattr(): ino #%lu\n", inode->i_ino));
++ ret = inode_change_ok(inode, iattr);
++ if (ret)
++ return ret;
++
++ /* Special cases - we don't want more than one data node
++ for these types on the medium at any time. So setattr
++ must read the original data associated with the node
++ (i.e. the device numbers or the target name) and write
++ it out again with the appropriate data attached */
++ if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode)) {
++ /* For these, we don't actually need to read the old node */
++ dev = old_encode_dev(inode->i_rdev);
++ mdata = (char *)&dev;
++ mdatalen = sizeof(dev);
++ D1(printk(KERN_DEBUG "jffs2_setattr(): Writing %d bytes of kdev_t\n", mdatalen));
++ } else if (S_ISLNK(inode->i_mode)) {
++ mdatalen = f->metadata->size;
++ mdata = kmalloc(f->metadata->size, GFP_USER);
++ if (!mdata)
++ return -ENOMEM;
++ ret = jffs2_read_dnode(c, f->metadata, mdata, 0, mdatalen);
++ if (ret) {
++ kfree(mdata);
++ return ret;
++ }
++ D1(printk(KERN_DEBUG "jffs2_setattr(): Writing %d bytes of symlink target\n", mdatalen));
++ }
++
++ ri = jffs2_alloc_raw_inode();
++ if (!ri) {
++ if (S_ISLNK(inode->i_mode))
++ kfree(mdata);
++ return -ENOMEM;
++ }
++
++ ret = jffs2_reserve_space(c, sizeof(*ri) + mdatalen, &phys_ofs, &alloclen, ALLOC_NORMAL);
++ if (ret) {
++ jffs2_free_raw_inode(ri);
++ if (S_ISLNK(inode->i_mode & S_IFMT))
++ kfree(mdata);
++ return ret;
++ }
++ down(&f->sem);
++ ivalid = iattr->ia_valid;
++
++ ri->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
++ ri->nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE);
++ ri->totlen = cpu_to_je32(sizeof(*ri) + mdatalen);
++ ri->hdr_crc = cpu_to_je32(crc32(0, ri, sizeof(struct jffs2_unknown_node)-4));
++
++ ri->ino = cpu_to_je32(inode->i_ino);
++ ri->version = cpu_to_je32(++f->highest_version);
++
++ ri->uid = cpu_to_je16((ivalid & ATTR_UID)?iattr->ia_uid:inode->i_uid);
++ ri->gid = cpu_to_je16((ivalid & ATTR_GID)?iattr->ia_gid:inode->i_gid);
++
++ if (ivalid & ATTR_MODE)
++ if (iattr->ia_mode & S_ISGID &&
++ !in_group_p(je16_to_cpu(ri->gid)) && !capable(CAP_FSETID))
++ ri->mode = cpu_to_jemode(iattr->ia_mode & ~S_ISGID);
++ else
++ ri->mode = cpu_to_jemode(iattr->ia_mode);
++ else
++ ri->mode = cpu_to_jemode(inode->i_mode);
++
++
++ ri->isize = cpu_to_je32((ivalid & ATTR_SIZE)?iattr->ia_size:inode->i_size);
++ ri->atime = cpu_to_je32(I_SEC((ivalid & ATTR_ATIME)?iattr->ia_atime:inode->i_atime));
++ ri->mtime = cpu_to_je32(I_SEC((ivalid & ATTR_MTIME)?iattr->ia_mtime:inode->i_mtime));
++ ri->ctime = cpu_to_je32(I_SEC((ivalid & ATTR_CTIME)?iattr->ia_ctime:inode->i_ctime));
++
++ ri->offset = cpu_to_je32(0);
++ ri->csize = ri->dsize = cpu_to_je32(mdatalen);
++ ri->compr = JFFS2_COMPR_NONE;
++ if (ivalid & ATTR_SIZE && inode->i_size < iattr->ia_size) {
++ /* It's an extension. Make it a hole node */
++ ri->compr = JFFS2_COMPR_ZERO;
++ ri->dsize = cpu_to_je32(iattr->ia_size - inode->i_size);
++ ri->offset = cpu_to_je32(inode->i_size);
++ }
++ ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
++ if (mdatalen)
++ ri->data_crc = cpu_to_je32(crc32(0, mdata, mdatalen));
++ else
++ ri->data_crc = cpu_to_je32(0);
++
++ new_metadata = jffs2_write_dnode(c, f, ri, mdata, mdatalen, phys_ofs, ALLOC_NORMAL);
++ if (S_ISLNK(inode->i_mode))
++ kfree(mdata);
++
++ if (IS_ERR(new_metadata)) {
++ jffs2_complete_reservation(c);
++ jffs2_free_raw_inode(ri);
++ up(&f->sem);
++ return PTR_ERR(new_metadata);
++ }
++ /* It worked. Update the inode */
++ inode->i_atime = ITIME(je32_to_cpu(ri->atime));
++ inode->i_ctime = ITIME(je32_to_cpu(ri->ctime));
++ inode->i_mtime = ITIME(je32_to_cpu(ri->mtime));
++ inode->i_mode = jemode_to_cpu(ri->mode);
++ inode->i_uid = je16_to_cpu(ri->uid);
++ inode->i_gid = je16_to_cpu(ri->gid);
++
++
++ old_metadata = f->metadata;
++
++ if (ivalid & ATTR_SIZE && inode->i_size > iattr->ia_size)
++ jffs2_truncate_fraglist (c, &f->fragtree, iattr->ia_size);
++
++ if (ivalid & ATTR_SIZE && inode->i_size < iattr->ia_size) {
++ jffs2_add_full_dnode_to_inode(c, f, new_metadata);
++ inode->i_size = iattr->ia_size;
++ f->metadata = NULL;
++ } else {
++ f->metadata = new_metadata;
++ }
++ if (old_metadata) {
++ jffs2_mark_node_obsolete(c, old_metadata->raw);
++ jffs2_free_full_dnode(old_metadata);
++ }
++ jffs2_free_raw_inode(ri);
++
++ up(&f->sem);
++ jffs2_complete_reservation(c);
++
++ /* We have to do the vmtruncate() without f->sem held, since
++ some pages may be locked and waiting for it in readpage().
++ We are protected from a simultaneous write() extending i_size
++ back past iattr->ia_size, because do_truncate() holds the
++ generic inode semaphore. */
++ if (ivalid & ATTR_SIZE && inode->i_size > iattr->ia_size)
++ vmtruncate(inode, iattr->ia_size);
++
++ return 0;
++}
++
++int jffs2_setattr(struct dentry *dentry, struct iattr *iattr)
++{
++ return jffs2_do_setattr(dentry->d_inode, iattr);
++}
++
++int jffs2_statfs(struct super_block *sb, struct kstatfs *buf)
++{
++ struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
++ unsigned long avail;
++
++ buf->f_type = JFFS2_SUPER_MAGIC;
++ buf->f_bsize = 1 << PAGE_SHIFT;
++ buf->f_blocks = c->flash_size >> PAGE_SHIFT;
++ buf->f_files = 0;
++ buf->f_ffree = 0;
++ buf->f_namelen = JFFS2_MAX_NAME_LEN;
++
++ spin_lock(&c->erase_completion_lock);
++
++ avail = c->dirty_size + c->free_size;
++ if (avail > c->sector_size * c->resv_blocks_write)
++ avail -= c->sector_size * c->resv_blocks_write;
++ else
++ avail = 0;
++
++ buf->f_bavail = buf->f_bfree = avail >> PAGE_SHIFT;
++
++ D1(jffs2_dump_block_lists(c));
++
++ spin_unlock(&c->erase_completion_lock);
++
++ return 0;
++}
++
++
++void jffs2_clear_inode (struct inode *inode)
++{
++ /* We can forget about this inode for now - drop all
++ * the nodelists associated with it, etc.
++ */
++ struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
++ struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
++
++ D1(printk(KERN_DEBUG "jffs2_clear_inode(): ino #%lu mode %o\n", inode->i_ino, inode->i_mode));
++
++ jffs2_do_clear_inode(c, f);
++}
++
++void jffs2_read_inode (struct inode *inode)
++{
++ struct jffs2_inode_info *f;
++ struct jffs2_sb_info *c;
++ struct jffs2_raw_inode latest_node;
++ int ret;
++
++ D1(printk(KERN_DEBUG "jffs2_read_inode(): inode->i_ino == %lu\n", inode->i_ino));
++
++ f = JFFS2_INODE_INFO(inode);
++ c = JFFS2_SB_INFO(inode->i_sb);
++
++ jffs2_init_inode_info(f);
++
++ ret = jffs2_do_read_inode(c, f, inode->i_ino, &latest_node);
++
++ if (ret) {
++ make_bad_inode(inode);
++ up(&f->sem);
++ return;
++ }
++ inode->i_mode = jemode_to_cpu(latest_node.mode);
++ inode->i_uid = je16_to_cpu(latest_node.uid);
++ inode->i_gid = je16_to_cpu(latest_node.gid);
++ inode->i_size = je32_to_cpu(latest_node.isize);
++ inode->i_atime = ITIME(je32_to_cpu(latest_node.atime));
++ inode->i_mtime = ITIME(je32_to_cpu(latest_node.mtime));
++ inode->i_ctime = ITIME(je32_to_cpu(latest_node.ctime));
++
++ inode->i_nlink = f->inocache->nlink;
++
++ inode->i_blksize = PAGE_SIZE;
++ inode->i_blocks = (inode->i_size + 511) >> 9;
++
++ switch (inode->i_mode & S_IFMT) {
++ jint16_t rdev;
++
++ case S_IFLNK:
++ inode->i_op = &jffs2_symlink_inode_operations;
++ break;
++
++ case S_IFDIR:
++ {
++ struct jffs2_full_dirent *fd;
++
++ for (fd=f->dents; fd; fd = fd->next) {
++ if (fd->type == DT_DIR && fd->ino)
++ inode->i_nlink++;
++ }
++ /* and '..' */
++ inode->i_nlink++;
++ /* Root dir gets i_nlink 3 for some reason */
++ if (inode->i_ino == 1)
++ inode->i_nlink++;
++
++ inode->i_op = &jffs2_dir_inode_operations;
++ inode->i_fop = &jffs2_dir_operations;
++ break;
++ }
++ case S_IFREG:
++ inode->i_op = &jffs2_file_inode_operations;
++ inode->i_fop = &jffs2_file_operations;
++ inode->i_mapping->a_ops = &jffs2_file_address_operations;
++ inode->i_mapping->nrpages = 0;
++ break;
++
++ case S_IFBLK:
++ case S_IFCHR:
++ /* Read the device numbers from the media */
++ D1(printk(KERN_DEBUG "Reading device numbers from flash\n"));
++ if (jffs2_read_dnode(c, f->metadata, (char *)&rdev, 0, sizeof(rdev)) < 0) {
++ /* Eep */
++ printk(KERN_NOTICE "Read device numbers for inode %lu failed\n", (unsigned long)inode->i_ino);
++ up(&f->sem);
++ jffs2_do_clear_inode(c, f);
++ make_bad_inode(inode);
++ return;
++ }
++
++ case S_IFSOCK:
++ case S_IFIFO:
++ inode->i_op = &jffs2_file_inode_operations;
++ init_special_inode(inode, inode->i_mode,
++ old_decode_dev((je16_to_cpu(rdev))));
++ break;
++
++ default:
++ printk(KERN_WARNING "jffs2_read_inode(): Bogus imode %o for ino %lu\n", inode->i_mode, (unsigned long)inode->i_ino);
++ }
++
++ up(&f->sem);
++
++ D1(printk(KERN_DEBUG "jffs2_read_inode() returning\n"));
++}
++
++void jffs2_dirty_inode(struct inode *inode)
++{
++ struct iattr iattr;
++
++ if (!(inode->i_state & I_DIRTY_DATASYNC)) {
++ D2(printk(KERN_DEBUG "jffs2_dirty_inode() not calling setattr() for ino #%lu\n", inode->i_ino));
++ return;
++ }
++
++ D1(printk(KERN_DEBUG "jffs2_dirty_inode() calling setattr() for ino #%lu\n", inode->i_ino));
++
++ iattr.ia_valid = ATTR_MODE|ATTR_UID|ATTR_GID|ATTR_ATIME|ATTR_MTIME|ATTR_CTIME;
++ iattr.ia_mode = inode->i_mode;
++ iattr.ia_uid = inode->i_uid;
++ iattr.ia_gid = inode->i_gid;
++ iattr.ia_atime = inode->i_atime;
++ iattr.ia_mtime = inode->i_mtime;
++ iattr.ia_ctime = inode->i_ctime;
++
++ jffs2_do_setattr(inode, &iattr);
++}
++
++int jffs2_remount_fs (struct super_block *sb, int *flags, char *data)
++{
++ struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
++
++ if (c->flags & JFFS2_SB_FLAG_RO && !(sb->s_flags & MS_RDONLY))
++ return -EROFS;
++
++ /* We stop if it was running, then restart if it needs to.
++ This also catches the case where it was stopped and this
++ is just a remount to restart it */
++ if (!(sb->s_flags & MS_RDONLY))
++ jffs2_stop_garbage_collect_thread(c);
++
++ if (!(*flags & MS_RDONLY))
++ jffs2_start_garbage_collect_thread(c);
++
++ sb->s_flags = (sb->s_flags & ~MS_RDONLY)|(*flags & MS_RDONLY);
++
++ return 0;
++}
++
++void jffs2_write_super (struct super_block *sb)
++{
++ struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
++ sb->s_dirt = 0;
++
++ if (sb->s_flags & MS_RDONLY)
++ return;
++
++ D1(printk(KERN_DEBUG "jffs2_write_super()\n"));
++ jffs2_garbage_collect_trigger(c);
++ jffs2_erase_pending_blocks(c, 0);
++ jffs2_flush_wbuf_gc(c, 0);
++}
++
++
++/* jffs2_new_inode: allocate a new inode and inocache, add it to the hash,
++ fill in the raw_inode while you're at it. */
++struct inode *jffs2_new_inode (struct inode *dir_i, int mode, struct jffs2_raw_inode *ri)
++{
++ struct inode *inode;
++ struct super_block *sb = dir_i->i_sb;
++ struct jffs2_sb_info *c;
++ struct jffs2_inode_info *f;
++ int ret;
++
++ D1(printk(KERN_DEBUG "jffs2_new_inode(): dir_i %ld, mode 0x%x\n", dir_i->i_ino, mode));
++
++ c = JFFS2_SB_INFO(sb);
++
++ inode = new_inode(sb);
++
++ if (!inode)
++ return ERR_PTR(-ENOMEM);
++
++ f = JFFS2_INODE_INFO(inode);
++ jffs2_init_inode_info(f);
++
++ memset(ri, 0, sizeof(*ri));
++ /* Set OS-specific defaults for new inodes */
++ ri->uid = cpu_to_je16(current->fsuid);
++
++ if (dir_i->i_mode & S_ISGID) {
++ ri->gid = cpu_to_je16(dir_i->i_gid);
++ if (S_ISDIR(mode))
++ mode |= S_ISGID;
++ } else {
++ ri->gid = cpu_to_je16(current->fsgid);
++ }
++ ri->mode = cpu_to_jemode(mode);
++ ret = jffs2_do_new_inode (c, f, mode, ri);
++ if (ret) {
++ make_bad_inode(inode);
++ iput(inode);
++ return ERR_PTR(ret);
++ }
++ inode->i_nlink = 1;
++ inode->i_ino = je32_to_cpu(ri->ino);
++ inode->i_mode = jemode_to_cpu(ri->mode);
++ inode->i_gid = je16_to_cpu(ri->gid);
++ inode->i_uid = je16_to_cpu(ri->uid);
++ inode->i_atime = inode->i_ctime = inode->i_mtime = CURRENT_TIME;
++ ri->atime = ri->mtime = ri->ctime = cpu_to_je32(I_SEC(inode->i_mtime));
++
++ inode->i_blksize = PAGE_SIZE;
++ inode->i_blocks = 0;
++ inode->i_size = 0;
++
++ insert_inode_hash(inode);
++
++ return inode;
++}
++
++
++int jffs2_do_fill_super(struct super_block *sb, void *data, int silent)
++{
++ struct jffs2_sb_info *c;
++ struct inode *root_i;
++ int ret;
++ size_t blocks;
++
++ c = JFFS2_SB_INFO(sb);
++
++ c->flash_size = c->mtd->size;
++
++ /*
++ * Check, if we have to concatenate physical blocks to larger virtual blocks
++ * to reduce the memorysize for c->blocks. (kmalloc allows max. 128K allocation)
++ */
++ blocks = c->flash_size / c->mtd->erasesize;
++ while ((blocks * sizeof (struct jffs2_eraseblock)) > (128 * 1024))
++ blocks >>= 1;
++
++ c->sector_size = c->flash_size / blocks;
++ if (c->sector_size != c->mtd->erasesize)
++ printk(KERN_INFO "jffs2: Erase block size too small (%dKiB). Using virtual blocks size (%dKiB) instead\n",
++ c->mtd->erasesize / 1024, c->sector_size / 1024);
++
++ if (c->flash_size < 5*c->sector_size) {
++ printk(KERN_ERR "jffs2: Too few erase blocks (%d)\n", c->flash_size / c->sector_size);
++ return -EINVAL;
++ }
++
++ c->cleanmarker_size = sizeof(struct jffs2_unknown_node);
++ /* Joern -- stick alignment for weird 8-byte-page flash here */
++
++ if (jffs2_cleanmarker_oob(c)) {
++ /* NAND (or other bizarre) flash... do setup accordingly */
++ ret = jffs2_nand_flash_setup(c);
++ if (ret)
++ return ret;
++ }
++
++ c->inocache_list = kmalloc(INOCACHE_HASHSIZE * sizeof(struct jffs2_inode_cache *), GFP_KERNEL);
++ if (!c->inocache_list) {
++ ret = -ENOMEM;
++ goto out_wbuf;
++ }
++ memset(c->inocache_list, 0, INOCACHE_HASHSIZE * sizeof(struct jffs2_inode_cache *));
++
++ if ((ret = jffs2_do_mount_fs(c)))
++ goto out_inohash;
++
++ ret = -EINVAL;
++
++ D1(printk(KERN_DEBUG "jffs2_do_fill_super(): Getting root inode\n"));
++ root_i = iget(sb, 1);
++ if (is_bad_inode(root_i)) {
++ D1(printk(KERN_WARNING "get root inode failed\n"));
++ goto out_nodes;
++ }
++
++ D1(printk(KERN_DEBUG "jffs2_do_fill_super(): d_alloc_root()\n"));
++ sb->s_root = d_alloc_root(root_i);
++ if (!sb->s_root)
++ goto out_root_i;
++
++#if LINUX_VERSION_CODE >= 0x20403
++ sb->s_maxbytes = 0xFFFFFFFF;
++#endif
++ sb->s_blocksize = PAGE_CACHE_SIZE;
++ sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
++ sb->s_magic = JFFS2_SUPER_MAGIC;
++ if (!(sb->s_flags & MS_RDONLY))
++ jffs2_start_garbage_collect_thread(c);
++ return 0;
++
++ out_root_i:
++ iput(root_i);
++ out_nodes:
++ jffs2_free_ino_caches(c);
++ jffs2_free_raw_node_refs(c);
++ kfree(c->blocks);
++ out_inohash:
++ kfree(c->inocache_list);
++ out_wbuf:
++ jffs2_nand_flash_cleanup(c);
++
++ return ret;
++}
++
++void jffs2_gc_release_inode(struct jffs2_sb_info *c,
++ struct jffs2_inode_info *f)
++{
++ iput(OFNI_EDONI_2SFFJ(f));
++}
++
++struct jffs2_inode_info *jffs2_gc_fetch_inode(struct jffs2_sb_info *c,
++ int inum, int nlink)
++{
++ struct inode *inode;
++ struct jffs2_inode_cache *ic;
++ if (!nlink) {
++ /* The inode has zero nlink but its nodes weren't yet marked
++ obsolete. This has to be because we're still waiting for
++ the final (close() and) iput() to happen.
++
++ There's a possibility that the final iput() could have
++ happened while we were contemplating. In order to ensure
++ that we don't cause a new read_inode() (which would fail)
++ for the inode in question, we use ilookup() in this case
++ instead of iget().
++
++ The nlink can't _become_ zero at this point because we're
++ holding the alloc_sem, and jffs2_do_unlink() would also
++ need that while decrementing nlink on any inode.
++ */
++ inode = ilookup(OFNI_BS_2SFFJ(c), inum);
++ if (!inode) {
++ D1(printk(KERN_DEBUG "ilookup() failed for ino #%u; inode is probably deleted.\n",
++ inum));
++
++ spin_lock(&c->inocache_lock);
++ ic = jffs2_get_ino_cache(c, inum);
++ if (!ic) {
++ D1(printk(KERN_DEBUG "Inode cache for ino #%u is gone.\n", inum));
++ spin_unlock(&c->inocache_lock);
++ return NULL;
++ }
++ if (ic->state != INO_STATE_CHECKEDABSENT) {
++ /* Wait for progress. Don't just loop */
++ D1(printk(KERN_DEBUG "Waiting for ino #%u in state %d\n",
++ ic->ino, ic->state));
++ sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock);
++ } else {
++ spin_unlock(&c->inocache_lock);
++ }
++
++ return NULL;
++ }
++ } else {
++ /* Inode has links to it still; they're not going away because
++ jffs2_do_unlink() would need the alloc_sem and we have it.
++ Just iget() it, and if read_inode() is necessary that's OK.
++ */
++ inode = iget(OFNI_BS_2SFFJ(c), inum);
++ if (!inode)
++ return ERR_PTR(-ENOMEM);
++ }
++ if (is_bad_inode(inode)) {
++ printk(KERN_NOTICE "Eep. read_inode() failed for ino #%u. nlink %d\n",
++ inum, nlink);
++ /* NB. This will happen again. We need to do something appropriate here. */
++ iput(inode);
++ return ERR_PTR(-EIO);
++ }
++
++ return JFFS2_INODE_INFO(inode);
++}
++
++unsigned char *jffs2_gc_fetch_page(struct jffs2_sb_info *c,
++ struct jffs2_inode_info *f,
++ unsigned long offset,
++ unsigned long *priv)
++{
++ struct inode *inode = OFNI_EDONI_2SFFJ(f);
++ struct page *pg;
++
++ pg = read_cache_page(inode->i_mapping, offset >> PAGE_CACHE_SHIFT,
++ (void *)jffs2_do_readpage_unlock, inode);
++ if (IS_ERR(pg))
++ return (void *)pg;
++
++ *priv = (unsigned long)pg;
++ return kmap(pg);
++}
++
++void jffs2_gc_release_page(struct jffs2_sb_info *c,
++ unsigned char *ptr,
++ unsigned long *priv)
++{
++ struct page *pg = (void *)*priv;
++
++ kunmap(pg);
++ page_cache_release(pg);
++}
+diff -Nurb linux-mips-2.4.27/fs/jffs2/gc.c linux/fs/jffs2/gc.c
+--- linux-mips-2.4.27/fs/jffs2/gc.c 2003-11-17 02:07:44.000000000 +0100
++++ linux/fs/jffs2/gc.c 2004-11-19 10:25:12.104168632 +0100
+@@ -1,76 +1,67 @@
+ /*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+- * Copyright (C) 2001 Red Hat, Inc.
++ * Copyright (C) 2001-2003 Red Hat, Inc.
+ *
+- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
++ * Created by David Woodhouse <dwmw2@redhat.com>
+ *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
++ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+- * The contents of this file are subject to the Red Hat eCos Public
+- * License Version 1.1 (the "Licence"); you may not use this file
+- * except in compliance with the Licence. You may obtain a copy of
+- * the Licence at http://www.redhat.com/
+- *
+- * Software distributed under the Licence is distributed on an "AS IS"
+- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+- * See the Licence for the specific language governing rights and
+- * limitations under the Licence.
+- *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
+- *
+- * Alternatively, the contents of this file may be used under the
+- * terms of the GNU General Public License version 2 (the "GPL"), in
+- * which case the provisions of the GPL are applicable instead of the
+- * above. If you wish to allow the use of your version of this file
+- * only under the terms of the GPL and not to allow others to use your
+- * version of this file under the RHEPL, indicate your decision by
+- * deleting the provisions above and replace them with the notice and
+- * other provisions required by the GPL. If you do not delete the
+- * provisions above, a recipient may use your version of this file
+- * under either the RHEPL or the GPL.
+- *
+- * $Id$
++ * $Id$
+ *
+ */
+
+ #include <linux/kernel.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/slab.h>
+-#include <linux/jffs2.h>
+-#include <linux/sched.h>
+-#include <linux/interrupt.h>
+ #include <linux/pagemap.h>
+-#include "nodelist.h"
+ #include <linux/crc32.h>
++#include <linux/compiler.h>
++#include <linux/stat.h>
++#include "nodelist.h"
+
++static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c,
++ struct jffs2_inode_cache *ic,
++ struct jffs2_raw_node_ref *raw);
+ static int jffs2_garbage_collect_metadata(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
+- struct inode *inode, struct jffs2_full_dnode *fd);
++ struct jffs2_inode_info *f, struct jffs2_full_dnode *fd);
+ static int jffs2_garbage_collect_dirent(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
+- struct inode *inode, struct jffs2_full_dirent *fd);
++ struct jffs2_inode_info *f, struct jffs2_full_dirent *fd);
+ static int jffs2_garbage_collect_deletion_dirent(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
+- struct inode *inode, struct jffs2_full_dirent *fd);
++ struct jffs2_inode_info *f, struct jffs2_full_dirent *fd);
+ static int jffs2_garbage_collect_hole(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
+- struct inode *indeo, struct jffs2_full_dnode *fn,
+- __u32 start, __u32 end);
++ struct jffs2_inode_info *f, struct jffs2_full_dnode *fn,
++ uint32_t start, uint32_t end);
+ static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
+- struct inode *inode, struct jffs2_full_dnode *fn,
+- __u32 start, __u32 end);
++ struct jffs2_inode_info *f, struct jffs2_full_dnode *fn,
++ uint32_t start, uint32_t end);
++static int jffs2_garbage_collect_live(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
++ struct jffs2_raw_node_ref *raw, struct jffs2_inode_info *f);
+
+ /* Called with erase_completion_lock held */
+ static struct jffs2_eraseblock *jffs2_find_gc_block(struct jffs2_sb_info *c)
+ {
+ struct jffs2_eraseblock *ret;
+ struct list_head *nextlist = NULL;
++ int n = jiffies % 128;
+
+ /* Pick an eraseblock to garbage collect next. This is where we'll
+ put the clever wear-levelling algorithms. Eventually. */
+- if (!list_empty(&c->bad_used_list) && c->nr_free_blocks > JFFS2_RESERVED_BLOCKS_GCBAD) {
++ /* We possibly want to favour the dirtier blocks more when the
++ number of free blocks is low. */
++ if (!list_empty(&c->bad_used_list) && c->nr_free_blocks > c->resv_blocks_gcbad) {
+ D1(printk(KERN_DEBUG "Picking block from bad_used_list to GC next\n"));
+ nextlist = &c->bad_used_list;
+- } else if (jiffies % 100 && !list_empty(&c->dirty_list)) {
+- /* Most of the time, pick one off the dirty list */
++ } else if (n < 50 && !list_empty(&c->erasable_list)) {
++ /* Note that most of them will have gone directly to be erased.
++ So don't favour the erasable_list _too_ much. */
++ D1(printk(KERN_DEBUG "Picking block from erasable_list to GC next\n"));
++ nextlist = &c->erasable_list;
++ } else if (n < 110 && !list_empty(&c->very_dirty_list)) {
++ /* Most of the time, pick one off the very_dirty list */
++ D1(printk(KERN_DEBUG "Picking block from very_dirty_list to GC next\n"));
++ nextlist = &c->very_dirty_list;
++ } else if (n < 126 && !list_empty(&c->dirty_list)) {
+ D1(printk(KERN_DEBUG "Picking block from dirty_list to GC next\n"));
+ nextlist = &c->dirty_list;
+ } else if (!list_empty(&c->clean_list)) {
+@@ -80,9 +71,16 @@
+ D1(printk(KERN_DEBUG "Picking block from dirty_list to GC next (clean_list was empty)\n"));
+
+ nextlist = &c->dirty_list;
++ } else if (!list_empty(&c->very_dirty_list)) {
++ D1(printk(KERN_DEBUG "Picking block from very_dirty_list to GC next (clean_list and dirty_list were empty)\n"));
++ nextlist = &c->very_dirty_list;
++ } else if (!list_empty(&c->erasable_list)) {
++ D1(printk(KERN_DEBUG "Picking block from erasable_list to GC next (clean_list and {very_,}dirty_list were empty)\n"));
++
++ nextlist = &c->erasable_list;
+ } else {
+- /* Eep. Both were empty */
+- printk(KERN_NOTICE "jffs2: No clean _or_ dirty blocks to GC from! Where are they all?\n");
++ /* Eep. All were empty */
++ printk(KERN_NOTICE "jffs2: No clean, dirty _or_ erasable blocks to GC from! Where are they all?\n");
+ return NULL;
+ }
+
+@@ -94,6 +92,17 @@
+ printk(KERN_WARNING "Eep. ret->gc_node for block at 0x%08x is NULL\n", ret->offset);
+ BUG();
+ }
++
++ /* Have we accidentally picked a clean block with wasted space ? */
++ if (ret->wasted_size) {
++ D1(printk(KERN_DEBUG "Converting wasted_size %08x to dirty_size\n", ret->wasted_size));
++ ret->dirty_size += ret->wasted_size;
++ c->wasted_size -= ret->wasted_size;
++ c->dirty_size += ret->wasted_size;
++ ret->wasted_size = 0;
++ }
++
++ D1(jffs2_dump_block_lists(c));
+ return ret;
+ }
+
+@@ -103,21 +112,90 @@
+ */
+ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
+ {
+- struct jffs2_eraseblock *jeb;
+ struct jffs2_inode_info *f;
+- struct jffs2_raw_node_ref *raw;
+- struct jffs2_node_frag *frag;
+- struct jffs2_full_dnode *fn = NULL;
+- struct jffs2_full_dirent *fd;
+ struct jffs2_inode_cache *ic;
+- __u32 start = 0, end = 0, nrfrags = 0;
+- struct inode *inode;
+- int ret = 0;
++ struct jffs2_eraseblock *jeb;
++ struct jffs2_raw_node_ref *raw;
++ int ret = 0, inum, nlink;
+
+ if (down_interruptible(&c->alloc_sem))
+ return -EINTR;
+
+- spin_lock_bh(&c->erase_completion_lock);
++ for (;;) {
++ spin_lock(&c->erase_completion_lock);
++ if (!c->unchecked_size)
++ break;
++
++ /* We can't start doing GC yet. We haven't finished checking
++ the node CRCs etc. Do it now. */
++
++ /* checked_ino is protected by the alloc_sem */
++ if (c->checked_ino > c->highest_ino) {
++ printk(KERN_CRIT "Checked all inodes but still 0x%x bytes of unchecked space?\n",
++ c->unchecked_size);
++ D1(jffs2_dump_block_lists(c));
++ spin_unlock(&c->erase_completion_lock);
++ BUG();
++ }
++
++ spin_unlock(&c->erase_completion_lock);
++
++ spin_lock(&c->inocache_lock);
++
++ ic = jffs2_get_ino_cache(c, c->checked_ino++);
++
++ if (!ic) {
++ spin_unlock(&c->inocache_lock);
++ continue;
++ }
++
++ if (!ic->nlink) {
++ D1(printk(KERN_DEBUG "Skipping check of ino #%d with nlink zero\n",
++ ic->ino));
++ spin_unlock(&c->inocache_lock);
++ continue;
++ }
++ switch(ic->state) {
++ case INO_STATE_CHECKEDABSENT:
++ case INO_STATE_PRESENT:
++ D1(printk(KERN_DEBUG "Skipping ino #%u already checked\n", ic->ino));
++ spin_unlock(&c->inocache_lock);
++ continue;
++
++ case INO_STATE_GC:
++ case INO_STATE_CHECKING:
++ printk(KERN_WARNING "Inode #%u is in state %d during CRC check phase!\n", ic->ino, ic->state);
++ spin_unlock(&c->inocache_lock);
++ BUG();
++
++ case INO_STATE_READING:
++ /* We need to wait for it to finish, lest we move on
++ and trigger the BUG() above while we haven't yet
++ finished checking all its nodes */
++ D1(printk(KERN_DEBUG "Waiting for ino #%u to finish reading\n", ic->ino));
++ up(&c->alloc_sem);
++ sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock);
++ return 0;
++
++ default:
++ BUG();
++
++ case INO_STATE_UNCHECKED:
++ ;
++ }
++ ic->state = INO_STATE_CHECKING;
++ spin_unlock(&c->inocache_lock);
++
++ D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass() triggering inode scan of ino#%u\n", ic->ino));
++
++ ret = jffs2_do_crccheck_inode(c, ic);
++ if (ret)
++ printk(KERN_WARNING "Returned error for crccheck of ino #%u. Expect badness...\n", ic->ino);
++
++ jffs2_set_inocache_state(c, ic, INO_STATE_CHECKEDABSENT);
++ up(&c->alloc_sem);
++ return ret;
++ }
+
+ /* First, work out which block we're garbage-collecting */
+ jeb = c->gcblock;
+@@ -127,12 +205,14 @@
+
+ if (!jeb) {
+ printk(KERN_NOTICE "jffs2: Couldn't find erase block to garbage collect!\n");
+- spin_unlock_bh(&c->erase_completion_lock);
++ spin_unlock(&c->erase_completion_lock);
+ up(&c->alloc_sem);
+ return -EIO;
+ }
+
+- D1(printk(KERN_DEBUG "garbage collect from block at phys 0x%08x\n", jeb->offset));
++ D1(printk(KERN_DEBUG "GC from block %08x, used_size %08x, dirty_size %08x, free_size %08x\n", jeb->offset, jeb->used_size, jeb->dirty_size, jeb->free_size));
++ D1(if (c->nextblock)
++ printk(KERN_DEBUG "Nextblock at %08x, used_size %08x, dirty_size %08x, wasted_size %08x, free_size %08x\n", c->nextblock->offset, c->nextblock->used_size, c->nextblock->dirty_size, c->nextblock->wasted_size, c->nextblock->free_size));
+
+ if (!jeb->used_size) {
+ up(&c->alloc_sem);
+@@ -141,92 +221,211 @@
+
+ raw = jeb->gc_node;
+
+- while(raw->flash_offset & 1) {
+- D1(printk(KERN_DEBUG "Node at 0x%08x is obsolete... skipping\n", raw->flash_offset &~3));
+- jeb->gc_node = raw = raw->next_phys;
+- if (!raw) {
++ while(ref_obsolete(raw)) {
++ D1(printk(KERN_DEBUG "Node at 0x%08x is obsolete... skipping\n", ref_offset(raw)));
++ raw = raw->next_phys;
++ if (unlikely(!raw)) {
+ printk(KERN_WARNING "eep. End of raw list while still supposedly nodes to GC\n");
+ printk(KERN_WARNING "erase block at 0x%08x. free_size 0x%08x, dirty_size 0x%08x, used_size 0x%08x\n",
+ jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size);
+- spin_unlock_bh(&c->erase_completion_lock);
++ jeb->gc_node = raw;
++ spin_unlock(&c->erase_completion_lock);
+ up(&c->alloc_sem);
+ BUG();
+ }
+ }
+- D1(printk(KERN_DEBUG "Going to garbage collect node at 0x%08x\n", raw->flash_offset &~3));
++ jeb->gc_node = raw;
++
++ D1(printk(KERN_DEBUG "Going to garbage collect node at 0x%08x\n", ref_offset(raw)));
++
+ if (!raw->next_in_ino) {
+ /* Inode-less node. Clean marker, snapshot or something like that */
+- spin_unlock_bh(&c->erase_completion_lock);
++ /* FIXME: If it's something that needs to be copied, including something
++ we don't grok that has JFFS2_NODETYPE_RWCOMPAT_COPY, we should do so */
++ spin_unlock(&c->erase_completion_lock);
+ jffs2_mark_node_obsolete(c, raw);
+ up(&c->alloc_sem);
+ goto eraseit_lock;
+ }
+
+ ic = jffs2_raw_ref_to_ic(raw);
+- D1(printk(KERN_DEBUG "Inode number is #%u\n", ic->ino));
+-
+- spin_unlock_bh(&c->erase_completion_lock);
+
+- D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass collecting from block @0x%08x. Node @0x%08x, ino #%u\n", jeb->offset, raw->flash_offset&~3, ic->ino));
+- if (!ic->nlink) {
+- /* The inode has zero nlink but its nodes weren't yet marked
+- obsolete. This has to be because we're still waiting for
+- the final (close() and) iput() to happen.
+-
+- There's a possibility that the final iput() could have
+- happened while we were contemplating. In order to ensure
+- that we don't cause a new read_inode() (which would fail)
+- for the inode in question, we use ilookup() in this case
+- instead of iget().
+-
+- The nlink can't _become_ zero at this point because we're
+- holding the alloc_sem, and jffs2_do_unlink() would also
+- need that while decrementing nlink on any inode.
++ /* We need to hold the inocache. Either the erase_completion_lock or
++ the inocache_lock are sufficient; we trade down since the inocache_lock
++ causes less contention. */
++ spin_lock(&c->inocache_lock);
++
++ spin_unlock(&c->erase_completion_lock);
++
++ D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass collecting from block @0x%08x. Node @0x%08x(%d), ino #%u\n", jeb->offset, ref_offset(raw), ref_flags(raw), ic->ino));
++
++ /* Three possibilities:
++ 1. Inode is already in-core. We must iget it and do proper
++ updating to its fragtree, etc.
++ 2. Inode is not in-core, node is REF_PRISTINE. We lock the
++ inocache to prevent a read_inode(), copy the node intact.
++ 3. Inode is not in-core, node is not pristine. We must iget()
++ and take the slow path.
+ */
+- inode = ilookup(OFNI_BS_2SFFJ(c), ic->ino);
+- if (!inode) {
+- D1(printk(KERN_DEBUG "ilookup() failed for ino #%u; inode is probably deleted.\n",
++
++ switch(ic->state) {
++ case INO_STATE_CHECKEDABSENT:
++ /* It's been checked, but it's not currently in-core.
++ We can just copy any pristine nodes, but have
++ to prevent anyone else from doing read_inode() while
++ we're at it, so we set the state accordingly */
++ if (ref_flags(raw) == REF_PRISTINE)
++ ic->state = INO_STATE_GC;
++ else {
++ D1(printk(KERN_DEBUG "Ino #%u is absent but node not REF_PRISTINE. Reading.\n",
+ ic->ino));
+- up(&c->alloc_sem);
+- return 0;
+ }
+- } else {
+- /* Inode has links to it still; they're not going away because
+- jffs2_do_unlink() would need the alloc_sem and we have it.
+- Just iget() it, and if read_inode() is necessary that's OK.
++ break;
++
++ case INO_STATE_PRESENT:
++ /* It's in-core. GC must iget() it. */
++ break;
++
++ case INO_STATE_UNCHECKED:
++ case INO_STATE_CHECKING:
++ case INO_STATE_GC:
++ /* Should never happen. We should have finished checking
++ by the time we actually start doing any GC, and since
++ we're holding the alloc_sem, no other garbage collection
++ can happen.
+ */
+- inode = iget(OFNI_BS_2SFFJ(c), ic->ino);
+- if (!inode) {
++ printk(KERN_CRIT "Inode #%u already in state %d in jffs2_garbage_collect_pass()!\n",
++ ic->ino, ic->state);
+ up(&c->alloc_sem);
+- return -ENOMEM;
++ spin_unlock(&c->inocache_lock);
++ BUG();
++
++ case INO_STATE_READING:
++ /* Someone's currently trying to read it. We must wait for
++ them to finish and then go through the full iget() route
++ to do the GC. However, sometimes read_inode() needs to get
++ the alloc_sem() (for marking nodes invalid) so we must
++ drop the alloc_sem before sleeping. */
++
++ up(&c->alloc_sem);
++ D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass() waiting for ino #%u in state %d\n",
++ ic->ino, ic->state));
++ sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock);
++ /* And because we dropped the alloc_sem we must start again from the
++ beginning. Ponder chance of livelock here -- we're returning success
++ without actually making any progress.
++
++ Q: What are the chances that the inode is back in INO_STATE_READING
++ again by the time we next enter this function? And that this happens
++ enough times to cause a real delay?
++
++ A: Small enough that I don't care :)
++ */
++ return 0;
+ }
++
++ /* OK. Now if the inode is in state INO_STATE_GC, we are going to copy the
++ node intact, and we don't have to muck about with the fragtree etc.
++ because we know it's not in-core. If it _was_ in-core, we go through
++ all the iget() crap anyway */
++
++ if (ic->state == INO_STATE_GC) {
++ spin_unlock(&c->inocache_lock);
++
++ ret = jffs2_garbage_collect_pristine(c, ic, raw);
++
++ spin_lock(&c->inocache_lock);
++ ic->state = INO_STATE_CHECKEDABSENT;
++ wake_up(&c->inocache_wq);
++
++ if (ret != -EBADFD) {
++ spin_unlock(&c->inocache_lock);
++ goto release_sem;
+ }
+- if (is_bad_inode(inode)) {
+- printk(KERN_NOTICE "Eep. read_inode() failed for ino #%u\n", ic->ino);
+- /* NB. This will happen again. We need to do something appropriate here. */
++
++ /* Fall through if it wanted us to, with inocache_lock held */
++ }
++
++ /* Prevent the fairly unlikely race where the gcblock is
++ entirely obsoleted by the final close of a file which had
++ the only valid nodes in the block, followed by erasure,
++ followed by freeing of the ic because the erased block(s)
++ held _all_ the nodes of that inode.... never been seen but
++ it's vaguely possible. */
++
++ inum = ic->ino;
++ nlink = ic->nlink;
++ spin_unlock(&c->inocache_lock);
++
++ f = jffs2_gc_fetch_inode(c, inum, nlink);
++ if (IS_ERR(f))
++ return PTR_ERR(f);
++ if (!f)
++ return 0;
++
++ ret = jffs2_garbage_collect_live(c, jeb, raw, f);
++
++ jffs2_gc_release_inode(c, f);
++
++ release_sem:
+ up(&c->alloc_sem);
+- iput(inode);
+- return -EIO;
++
++ eraseit_lock:
++ /* If we've finished this block, start it erasing */
++ spin_lock(&c->erase_completion_lock);
++
++ eraseit:
++ if (c->gcblock && !c->gcblock->used_size) {
++ D1(printk(KERN_DEBUG "Block at 0x%08x completely obsoleted by GC. Moving to erase_pending_list\n", c->gcblock->offset));
++ /* We're GC'ing an empty block? */
++ list_add_tail(&c->gcblock->list, &c->erase_pending_list);
++ c->gcblock = NULL;
++ c->nr_erasing_blocks++;
++ jffs2_erase_pending_trigger(c);
+ }
++ spin_unlock(&c->erase_completion_lock);
++
++ return ret;
++}
++
++static int jffs2_garbage_collect_live(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
++ struct jffs2_raw_node_ref *raw, struct jffs2_inode_info *f)
++{
++ struct jffs2_node_frag *frag;
++ struct jffs2_full_dnode *fn = NULL;
++ struct jffs2_full_dirent *fd;
++ uint32_t start = 0, end = 0, nrfrags = 0;
++ int ret = 0;
+
+- f = JFFS2_INODE_INFO(inode);
+ down(&f->sem);
++
+ /* Now we have the lock for this inode. Check that it's still the one at the head
+ of the list. */
+
+- if (raw->flash_offset & 1) {
++ spin_lock(&c->erase_completion_lock);
++
++ if (c->gcblock != jeb) {
++ spin_unlock(&c->erase_completion_lock);
++ D1(printk(KERN_DEBUG "GC block is no longer gcblock. Restart\n"));
++ goto upnout;
++ }
++ if (ref_obsolete(raw)) {
++ spin_unlock(&c->erase_completion_lock);
+ D1(printk(KERN_DEBUG "node to be GC'd was obsoleted in the meantime.\n"));
+ /* They'll call again */
+ goto upnout;
+ }
++ spin_unlock(&c->erase_completion_lock);
++
+ /* OK. Looks safe. And nobody can get us now because we have the semaphore. Move the block */
+ if (f->metadata && f->metadata->raw == raw) {
+ fn = f->metadata;
+- ret = jffs2_garbage_collect_metadata(c, jeb, inode, fn);
++ ret = jffs2_garbage_collect_metadata(c, jeb, f, fn);
+ goto upnout;
+ }
+
+- for (frag = f->fraglist; frag; frag = frag->next) {
++ /* FIXME. Read node and do lookup? */
++ for (frag = frag_first(&f->fragtree); frag; frag = frag_next(frag)) {
+ if (frag->node && frag->node->raw == raw) {
+ fn = frag->node;
+ end = frag->ofs + frag->size;
+@@ -237,13 +436,22 @@
+ }
+ }
+ if (fn) {
++ if (ref_flags(raw) == REF_PRISTINE) {
++ ret = jffs2_garbage_collect_pristine(c, f->inocache, raw);
++ if (!ret) {
++ /* Urgh. Return it sensibly. */
++ frag->node->raw = f->inocache->nodes;
++ }
++ if (ret != -EBADFD)
++ goto upnout;
++ }
+ /* We found a datanode. Do the GC */
+ if((start >> PAGE_CACHE_SHIFT) < ((end-1) >> PAGE_CACHE_SHIFT)) {
+ /* It crosses a page boundary. Therefore, it must be a hole. */
+- ret = jffs2_garbage_collect_hole(c, jeb, inode, fn, start, end);
++ ret = jffs2_garbage_collect_hole(c, jeb, f, fn, start, end);
+ } else {
+ /* It could still be a hole. But we GC the page this way anyway */
+- ret = jffs2_garbage_collect_dnode(c, jeb, inode, fn, start, end);
++ ret = jffs2_garbage_collect_dnode(c, jeb, f, fn, start, end);
+ }
+ goto upnout;
+ }
+@@ -255,12 +463,13 @@
+ }
+
+ if (fd && fd->ino) {
+- ret = jffs2_garbage_collect_dirent(c, jeb, inode, fd);
++ ret = jffs2_garbage_collect_dirent(c, jeb, f, fd);
+ } else if (fd) {
+- ret = jffs2_garbage_collect_deletion_dirent(c, jeb, inode, fd);
++ ret = jffs2_garbage_collect_deletion_dirent(c, jeb, f, fd);
+ } else {
+- printk(KERN_WARNING "Raw node at 0x%08x wasn't in node lists for ino #%lu\n", raw->flash_offset&~3, inode->i_ino);
+- if (raw->flash_offset & 1) {
++ printk(KERN_WARNING "Raw node at 0x%08x wasn't in node lists for ino #%u\n",
++ ref_offset(raw), f->inocache->ino);
++ if (ref_obsolete(raw)) {
+ printk(KERN_WARNING "But it's obsolete so we don't mind too much\n");
+ } else {
+ ret = -EIO;
+@@ -268,46 +477,197 @@
+ }
+ upnout:
+ up(&f->sem);
+- up(&c->alloc_sem);
+- iput(inode);
+
+- eraseit_lock:
+- /* If we've finished this block, start it erasing */
+- spin_lock_bh(&c->erase_completion_lock);
++ return ret;
++}
+
+- eraseit:
+- if (c->gcblock && !c->gcblock->used_size) {
+- D1(printk(KERN_DEBUG "Block at 0x%08x completely obsoleted by GC. Moving to erase_pending_list\n", c->gcblock->offset));
+- /* We're GC'ing an empty block? */
+- list_add_tail(&c->gcblock->list, &c->erase_pending_list);
+- c->gcblock = NULL;
+- c->nr_erasing_blocks++;
+- jffs2_erase_pending_trigger(c);
++static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c,
++ struct jffs2_inode_cache *ic,
++ struct jffs2_raw_node_ref *raw)
++{
++ union jffs2_node_union *node;
++ struct jffs2_raw_node_ref *nraw;
++ size_t retlen;
++ int ret;
++ uint32_t phys_ofs, alloclen;
++ uint32_t crc, rawlen;
++ int retried = 0;
++
++ D1(printk(KERN_DEBUG "Going to GC REF_PRISTINE node at 0x%08x\n", ref_offset(raw)));
++
++ rawlen = ref_totlen(c, c->gcblock, raw);
++
++ /* Ask for a small amount of space (or the totlen if smaller) because we
++ don't want to force wastage of the end of a block if splitting would
++ work. */
++ ret = jffs2_reserve_space_gc(c, min_t(uint32_t, sizeof(struct jffs2_raw_inode) + JFFS2_MIN_DATA_LEN,
++ rawlen), &phys_ofs, &alloclen);
++ if (ret)
++ return ret;
++
++ if (alloclen < rawlen) {
++ /* Doesn't fit untouched. We'll go the old route and split it */
++ return -EBADFD;
++ }
++
++ node = kmalloc(rawlen, GFP_KERNEL);
++ if (!node)
++ return -ENOMEM;
++
++ ret = jffs2_flash_read(c, ref_offset(raw), rawlen, &retlen, (char *)node);
++ if (!ret && retlen != rawlen)
++ ret = -EIO;
++ if (ret)
++ goto out_node;
++
++ crc = crc32(0, node, sizeof(struct jffs2_unknown_node)-4);
++ if (je32_to_cpu(node->u.hdr_crc) != crc) {
++ printk(KERN_WARNING "Header CRC failed on REF_PRISTINE node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
++ ref_offset(raw), je32_to_cpu(node->u.hdr_crc), crc);
++ goto bail;
++ }
++
++ switch(je16_to_cpu(node->u.nodetype)) {
++ case JFFS2_NODETYPE_INODE:
++ crc = crc32(0, node, sizeof(node->i)-8);
++ if (je32_to_cpu(node->i.node_crc) != crc) {
++ printk(KERN_WARNING "Node CRC failed on REF_PRISTINE data node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
++ ref_offset(raw), je32_to_cpu(node->i.node_crc), crc);
++ goto bail;
++ }
++
++ if (je32_to_cpu(node->i.dsize)) {
++ crc = crc32(0, node->i.data, je32_to_cpu(node->i.csize));
++ if (je32_to_cpu(node->i.data_crc) != crc) {
++ printk(KERN_WARNING "Data CRC failed on REF_PRISTINE data node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
++ ref_offset(raw), je32_to_cpu(node->i.data_crc), crc);
++ goto bail;
+ }
+- spin_unlock_bh(&c->erase_completion_lock);
++ }
++ break;
++
++ case JFFS2_NODETYPE_DIRENT:
++ crc = crc32(0, node, sizeof(node->d)-8);
++ if (je32_to_cpu(node->d.node_crc) != crc) {
++ printk(KERN_WARNING "Node CRC failed on REF_PRISTINE dirent node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
++ ref_offset(raw), je32_to_cpu(node->d.node_crc), crc);
++ goto bail;
++ }
++
++ if (node->d.nsize) {
++ crc = crc32(0, node->d.name, node->d.nsize);
++ if (je32_to_cpu(node->d.name_crc) != crc) {
++ printk(KERN_WARNING "Name CRC failed on REF_PRISTINE dirent ode at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
++ ref_offset(raw), je32_to_cpu(node->d.name_crc), crc);
++ goto bail;
++ }
++ }
++ break;
++ default:
++ printk(KERN_WARNING "Unknown node type for REF_PRISTINE node at 0x%08x: 0x%04x\n",
++ ref_offset(raw), je16_to_cpu(node->u.nodetype));
++ goto bail;
++ }
++
++ nraw = jffs2_alloc_raw_node_ref();
++ if (!nraw) {
++ ret = -ENOMEM;
++ goto out_node;
++ }
++
++ /* OK, all the CRCs are good; this node can just be copied as-is. */
++ retry:
++ nraw->flash_offset = phys_ofs;
++ nraw->__totlen = rawlen;
++ nraw->next_phys = NULL;
++
++ ret = jffs2_flash_write(c, phys_ofs, rawlen, &retlen, (char *)node);
++
++ if (ret || (retlen != rawlen)) {
++ printk(KERN_NOTICE "Write of %d bytes at 0x%08x failed. returned %d, retlen %zd\n",
++ rawlen, phys_ofs, ret, retlen);
++ if (retlen) {
++ /* Doesn't belong to any inode */
++ nraw->next_in_ino = NULL;
++
++ nraw->flash_offset |= REF_OBSOLETE;
++ jffs2_add_physical_node_ref(c, nraw);
++ jffs2_mark_node_obsolete(c, nraw);
++ } else {
++ printk(KERN_NOTICE "Not marking the space at 0x%08x as dirty because the flash driver returned retlen zero\n", nraw->flash_offset);
++ jffs2_free_raw_node_ref(nraw);
++ }
++ if (!retried && (nraw == jffs2_alloc_raw_node_ref())) {
++ /* Try to reallocate space and retry */
++ uint32_t dummy;
++ struct jffs2_eraseblock *jeb = &c->blocks[phys_ofs / c->sector_size];
++
++ retried = 1;
++
++ D1(printk(KERN_DEBUG "Retrying failed write of REF_PRISTINE node.\n"));
++
++ ACCT_SANITY_CHECK(c,jeb);
++ D1(ACCT_PARANOIA_CHECK(jeb));
++
++ ret = jffs2_reserve_space_gc(c, rawlen, &phys_ofs, &dummy);
++
++ if (!ret) {
++ D1(printk(KERN_DEBUG "Allocated space at 0x%08x to retry failed write.\n", phys_ofs));
+
++ ACCT_SANITY_CHECK(c,jeb);
++ D1(ACCT_PARANOIA_CHECK(jeb));
++
++ goto retry;
++ }
++ D1(printk(KERN_DEBUG "Failed to allocate space to retry failed write: %d!\n", ret));
++ jffs2_free_raw_node_ref(nraw);
++ }
++
++ if (!ret)
++ ret = -EIO;
++ goto out_node;
++ }
++ nraw->flash_offset |= REF_PRISTINE;
++ jffs2_add_physical_node_ref(c, nraw);
++
++ /* Link into per-inode list. This is safe because of the ic
++ state being INO_STATE_GC. Note that if we're doing this
++ for an inode which is in-code, the 'nraw' pointer is then
++ going to be fetched from ic->nodes by our caller. */
++ nraw->next_in_ino = ic->nodes;
++ ic->nodes = nraw;
++
++ jffs2_mark_node_obsolete(c, raw);
++ D1(printk(KERN_DEBUG "WHEEE! GC REF_PRISTINE node at 0x%08x succeeded\n", ref_offset(raw)));
++
++ out_node:
++ kfree(node);
+ return ret;
++ bail:
++ ret = -EBADFD;
++ goto out_node;
+ }
+
+ static int jffs2_garbage_collect_metadata(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
+- struct inode *inode, struct jffs2_full_dnode *fn)
++ struct jffs2_inode_info *f, struct jffs2_full_dnode *fn)
+ {
+- struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
+ struct jffs2_full_dnode *new_fn;
+ struct jffs2_raw_inode ri;
+- unsigned short dev;
++ jint16_t dev;
+ char *mdata = NULL, mdatalen = 0;
+- __u32 alloclen, phys_ofs;
++ uint32_t alloclen, phys_ofs;
+ int ret;
+
+- if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode)) {
++ if (S_ISBLK(JFFS2_F_I_MODE(f)) ||
++ S_ISCHR(JFFS2_F_I_MODE(f)) ) {
+ /* For these, we don't actually need to read the old node */
+- dev = (MAJOR(to_kdev_t(inode->i_rdev)) << 8) |
+- MINOR(to_kdev_t(inode->i_rdev));
++ /* FIXME: for minor or major > 255. */
++ dev = cpu_to_je16(((JFFS2_F_I_RDEV_MAJ(f) << 8) |
++ JFFS2_F_I_RDEV_MIN(f)));
+ mdata = (char *)&dev;
+ mdatalen = sizeof(dev);
+ D1(printk(KERN_DEBUG "jffs2_garbage_collect_metadata(): Writing %d bytes of kdev_t\n", mdatalen));
+- } else if (S_ISLNK(inode->i_mode)) {
++ } else if (S_ISLNK(JFFS2_F_I_MODE(f))) {
+ mdatalen = fn->size;
+ mdata = kmalloc(fn->size, GFP_KERNEL);
+ if (!mdata) {
+@@ -326,34 +686,34 @@
+
+ ret = jffs2_reserve_space_gc(c, sizeof(ri) + mdatalen, &phys_ofs, &alloclen);
+ if (ret) {
+- printk(KERN_WARNING "jffs2_reserve_space_gc of %d bytes for garbage_collect_metadata failed: %d\n",
++ printk(KERN_WARNING "jffs2_reserve_space_gc of %zd bytes for garbage_collect_metadata failed: %d\n",
+ sizeof(ri)+ mdatalen, ret);
+ goto out;
+ }
+
+ memset(&ri, 0, sizeof(ri));
+- ri.magic = JFFS2_MAGIC_BITMASK;
+- ri.nodetype = JFFS2_NODETYPE_INODE;
+- ri.totlen = sizeof(ri) + mdatalen;
+- ri.hdr_crc = crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4);
+-
+- ri.ino = inode->i_ino;
+- ri.version = ++f->highest_version;
+- ri.mode = inode->i_mode;
+- ri.uid = inode->i_uid;
+- ri.gid = inode->i_gid;
+- ri.isize = inode->i_size;
+- ri.atime = inode->i_atime;
+- ri.ctime = inode->i_ctime;
+- ri.mtime = inode->i_mtime;
+- ri.offset = 0;
+- ri.csize = mdatalen;
+- ri.dsize = mdatalen;
++ ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
++ ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE);
++ ri.totlen = cpu_to_je32(sizeof(ri) + mdatalen);
++ ri.hdr_crc = cpu_to_je32(crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4));
++
++ ri.ino = cpu_to_je32(f->inocache->ino);
++ ri.version = cpu_to_je32(++f->highest_version);
++ ri.mode = cpu_to_jemode(JFFS2_F_I_MODE(f));
++ ri.uid = cpu_to_je16(JFFS2_F_I_UID(f));
++ ri.gid = cpu_to_je16(JFFS2_F_I_GID(f));
++ ri.isize = cpu_to_je32(JFFS2_F_I_SIZE(f));
++ ri.atime = cpu_to_je32(JFFS2_F_I_ATIME(f));
++ ri.ctime = cpu_to_je32(JFFS2_F_I_CTIME(f));
++ ri.mtime = cpu_to_je32(JFFS2_F_I_MTIME(f));
++ ri.offset = cpu_to_je32(0);
++ ri.csize = cpu_to_je32(mdatalen);
++ ri.dsize = cpu_to_je32(mdatalen);
+ ri.compr = JFFS2_COMPR_NONE;
+- ri.node_crc = crc32(0, &ri, sizeof(ri)-8);
+- ri.data_crc = crc32(0, mdata, mdatalen);
++ ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8));
++ ri.data_crc = cpu_to_je32(crc32(0, mdata, mdatalen));
+
+- new_fn = jffs2_write_dnode(inode, &ri, mdata, mdatalen, phys_ofs, NULL);
++ new_fn = jffs2_write_dnode(c, f, &ri, mdata, mdatalen, phys_ofs, ALLOC_GC);
+
+ if (IS_ERR(new_fn)) {
+ printk(KERN_WARNING "Error writing new dnode: %ld\n", PTR_ERR(new_fn));
+@@ -364,41 +724,40 @@
+ jffs2_free_full_dnode(fn);
+ f->metadata = new_fn;
+ out:
+- if (S_ISLNK(inode->i_mode))
++ if (S_ISLNK(JFFS2_F_I_MODE(f)))
+ kfree(mdata);
+ return ret;
+ }
+
+ static int jffs2_garbage_collect_dirent(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
+- struct inode *inode, struct jffs2_full_dirent *fd)
++ struct jffs2_inode_info *f, struct jffs2_full_dirent *fd)
+ {
+- struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
+ struct jffs2_full_dirent *new_fd;
+ struct jffs2_raw_dirent rd;
+- __u32 alloclen, phys_ofs;
++ uint32_t alloclen, phys_ofs;
+ int ret;
+
+- rd.magic = JFFS2_MAGIC_BITMASK;
+- rd.nodetype = JFFS2_NODETYPE_DIRENT;
++ rd.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
++ rd.nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT);
+ rd.nsize = strlen(fd->name);
+- rd.totlen = sizeof(rd) + rd.nsize;
+- rd.hdr_crc = crc32(0, &rd, sizeof(struct jffs2_unknown_node)-4);
++ rd.totlen = cpu_to_je32(sizeof(rd) + rd.nsize);
++ rd.hdr_crc = cpu_to_je32(crc32(0, &rd, sizeof(struct jffs2_unknown_node)-4));
+
+- rd.pino = inode->i_ino;
+- rd.version = ++f->highest_version;
+- rd.ino = fd->ino;
+- rd.mctime = max(inode->i_mtime, inode->i_ctime);
++ rd.pino = cpu_to_je32(f->inocache->ino);
++ rd.version = cpu_to_je32(++f->highest_version);
++ rd.ino = cpu_to_je32(fd->ino);
++ rd.mctime = cpu_to_je32(max(JFFS2_F_I_MTIME(f), JFFS2_F_I_CTIME(f)));
+ rd.type = fd->type;
+- rd.node_crc = crc32(0, &rd, sizeof(rd)-8);
+- rd.name_crc = crc32(0, fd->name, rd.nsize);
++ rd.node_crc = cpu_to_je32(crc32(0, &rd, sizeof(rd)-8));
++ rd.name_crc = cpu_to_je32(crc32(0, fd->name, rd.nsize));
+
+ ret = jffs2_reserve_space_gc(c, sizeof(rd)+rd.nsize, &phys_ofs, &alloclen);
+ if (ret) {
+- printk(KERN_WARNING "jffs2_reserve_space_gc of %d bytes for garbage_collect_dirent failed: %d\n",
++ printk(KERN_WARNING "jffs2_reserve_space_gc of %zd bytes for garbage_collect_dirent failed: %d\n",
+ sizeof(rd)+rd.nsize, ret);
+ return ret;
+ }
+- new_fd = jffs2_write_dirent(inode, &rd, fd->name, rd.nsize, phys_ofs, NULL);
++ new_fd = jffs2_write_dirent(c, f, &rd, fd->name, rd.nsize, phys_ofs, ALLOC_GC);
+
+ if (IS_ERR(new_fd)) {
+ printk(KERN_WARNING "jffs2_write_dirent in garbage_collect_dirent failed: %ld\n", PTR_ERR(new_fd));
+@@ -409,19 +768,98 @@
+ }
+
+ static int jffs2_garbage_collect_deletion_dirent(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
+- struct inode *inode, struct jffs2_full_dirent *fd)
++ struct jffs2_inode_info *f, struct jffs2_full_dirent *fd)
+ {
+- struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
+ struct jffs2_full_dirent **fdp = &f->dents;
+ int found = 0;
+
+- /* FIXME: When we run on NAND flash, we need to work out whether
+- this deletion dirent is still needed to actively delete a
+- 'real' dirent with the same name that's still somewhere else
+- on the flash. For now, we know that we've actually obliterated
+- all the older dirents when they became obsolete, so we didn't
+- really need to write the deletion to flash in the first place.
+- */
++ /* On a medium where we can't actually mark nodes obsolete
++ pernamently, such as NAND flash, we need to work out
++ whether this deletion dirent is still needed to actively
++ delete a 'real' dirent with the same name that's still
++ somewhere else on the flash. */
++ if (!jffs2_can_mark_obsolete(c)) {
++ struct jffs2_raw_dirent *rd;
++ struct jffs2_raw_node_ref *raw;
++ int ret;
++ size_t retlen;
++ int name_len = strlen(fd->name);
++ uint32_t name_crc = crc32(0, fd->name, name_len);
++ uint32_t rawlen = ref_totlen(c, jeb, fd->raw);
++
++ rd = kmalloc(rawlen, GFP_KERNEL);
++ if (!rd)
++ return -ENOMEM;
++
++ /* Prevent the erase code from nicking the obsolete node refs while
++ we're looking at them. I really don't like this extra lock but
++ can't see any alternative. Suggestions on a postcard to... */
++ down(&c->erase_free_sem);
++
++ for (raw = f->inocache->nodes; raw != (void *)f->inocache; raw = raw->next_in_ino) {
++
++ /* We only care about obsolete ones */
++ if (!(ref_obsolete(raw)))
++ continue;
++
++ /* Any dirent with the same name is going to have the same length... */
++ if (ref_totlen(c, NULL, raw) != rawlen)
++ continue;
++
++ /* Doesn't matter if there's one in the same erase block. We're going to
++ delete it too at the same time. */
++ if ((raw->flash_offset & ~(c->sector_size-1)) ==
++ (fd->raw->flash_offset & ~(c->sector_size-1)))
++ continue;
++
++ D1(printk(KERN_DEBUG "Check potential deletion dirent at %08x\n", ref_offset(raw)));
++
++ /* This is an obsolete node belonging to the same directory, and it's of the right
++ length. We need to take a closer look...*/
++ ret = jffs2_flash_read(c, ref_offset(raw), rawlen, &retlen, (char *)rd);
++ if (ret) {
++ printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Read error (%d) reading obsolete node at %08x\n", ret, ref_offset(raw));
++ /* If we can't read it, we don't need to continue to obsolete it. Continue */
++ continue;
++ }
++ if (retlen != rawlen) {
++ printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Short read (%zd not %zd) reading header from obsolete node at %08x\n",
++ retlen, rawlen, ref_offset(raw));
++ continue;
++ }
++
++ if (je16_to_cpu(rd->nodetype) != JFFS2_NODETYPE_DIRENT)
++ continue;
++
++ /* If the name CRC doesn't match, skip */
++ if (je32_to_cpu(rd->name_crc) != name_crc)
++ continue;
++
++ /* If the name length doesn't match, or it's another deletion dirent, skip */
++ if (rd->nsize != name_len || !je32_to_cpu(rd->ino))
++ continue;
++
++ /* OK, check the actual name now */
++ if (memcmp(rd->name, fd->name, name_len))
++ continue;
++
++ /* OK. The name really does match. There really is still an older node on
++ the flash which our deletion dirent obsoletes. So we have to write out
++ a new deletion dirent to replace it */
++ up(&c->erase_free_sem);
++
++ D1(printk(KERN_DEBUG "Deletion dirent at %08x still obsoletes real dirent \"%s\" at %08x for ino #%u\n",
++ ref_offset(fd->raw), fd->name, ref_offset(raw), je32_to_cpu(rd->ino)));
++ kfree(rd);
++
++ return jffs2_garbage_collect_dirent(c, jeb, f, fd);
++ }
++
++ up(&c->erase_free_sem);
++ kfree(rd);
++ }
++
++ /* No need for it any more. Just mark it obsolete and remove it from the list */
+ while (*fdp) {
+ if ((*fdp) == fd) {
+ found = 1;
+@@ -431,7 +869,7 @@
+ fdp = &(*fdp)->next;
+ }
+ if (!found) {
+- printk(KERN_WARNING "Deletion dirent \"%s\" not found in list for ino #%lu\n", fd->name, inode->i_ino);
++ printk(KERN_WARNING "Deletion dirent \"%s\" not found in list for ino #%u\n", fd->name, f->inocache->ino);
+ }
+ jffs2_mark_node_obsolete(c, fd->raw);
+ jffs2_free_full_dirent(fd);
+@@ -439,93 +877,95 @@
+ }
+
+ static int jffs2_garbage_collect_hole(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
+- struct inode *inode, struct jffs2_full_dnode *fn,
+- __u32 start, __u32 end)
++ struct jffs2_inode_info *f, struct jffs2_full_dnode *fn,
++ uint32_t start, uint32_t end)
+ {
+- struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
+ struct jffs2_raw_inode ri;
+ struct jffs2_node_frag *frag;
+ struct jffs2_full_dnode *new_fn;
+- __u32 alloclen, phys_ofs;
++ uint32_t alloclen, phys_ofs;
+ int ret;
+
+- D1(printk(KERN_DEBUG "Writing replacement hole node for ino #%lu from offset 0x%x to 0x%x\n",
+- inode->i_ino, start, end));
++ D1(printk(KERN_DEBUG "Writing replacement hole node for ino #%u from offset 0x%x to 0x%x\n",
++ f->inocache->ino, start, end));
+
+ memset(&ri, 0, sizeof(ri));
+
+ if(fn->frags > 1) {
+ size_t readlen;
+- __u32 crc;
++ uint32_t crc;
+ /* It's partially obsoleted by a later write. So we have to
+ write it out again with the _same_ version as before */
+- ret = c->mtd->read(c->mtd, fn->raw->flash_offset & ~3, sizeof(ri), &readlen, (char *)&ri);
++ ret = jffs2_flash_read(c, ref_offset(fn->raw), sizeof(ri), &readlen, (char *)&ri);
+ if (readlen != sizeof(ri) || ret) {
+- printk(KERN_WARNING "Node read failed in jffs2_garbage_collect_hole. Ret %d, retlen %d. Data will be lost by writing new hold node\n", ret, readlen);
++ printk(KERN_WARNING "Node read failed in jffs2_garbage_collect_hole. Ret %d, retlen %zd. Data will be lost by writing new hole node\n", ret, readlen);
+ goto fill;
+ }
+- if (ri.nodetype != JFFS2_NODETYPE_INODE) {
++ if (je16_to_cpu(ri.nodetype) != JFFS2_NODETYPE_INODE) {
+ printk(KERN_WARNING "jffs2_garbage_collect_hole: Node at 0x%08x had node type 0x%04x instead of JFFS2_NODETYPE_INODE(0x%04x)\n",
+- fn->raw->flash_offset & ~3, ri.nodetype, JFFS2_NODETYPE_INODE);
++ ref_offset(fn->raw),
++ je16_to_cpu(ri.nodetype), JFFS2_NODETYPE_INODE);
+ return -EIO;
+ }
+- if (ri.totlen != sizeof(ri)) {
+- printk(KERN_WARNING "jffs2_garbage_collect_hole: Node at 0x%08x had totlen 0x%x instead of expected 0x%x\n",
+- fn->raw->flash_offset & ~3, ri.totlen, sizeof(ri));
++ if (je32_to_cpu(ri.totlen) != sizeof(ri)) {
++ printk(KERN_WARNING "jffs2_garbage_collect_hole: Node at 0x%08x had totlen 0x%x instead of expected 0x%zx\n",
++ ref_offset(fn->raw),
++ je32_to_cpu(ri.totlen), sizeof(ri));
+ return -EIO;
+ }
+ crc = crc32(0, &ri, sizeof(ri)-8);
+- if (crc != ri.node_crc) {
++ if (crc != je32_to_cpu(ri.node_crc)) {
+ printk(KERN_WARNING "jffs2_garbage_collect_hole: Node at 0x%08x had CRC 0x%08x which doesn't match calculated CRC 0x%08x\n",
+- fn->raw->flash_offset & ~3, ri.node_crc, crc);
++ ref_offset(fn->raw),
++ je32_to_cpu(ri.node_crc), crc);
+ /* FIXME: We could possibly deal with this by writing new holes for each frag */
+- printk(KERN_WARNING "Data in the range 0x%08x to 0x%08x of inode #%lu will be lost\n",
+- start, end, inode->i_ino);
++ printk(KERN_WARNING "Data in the range 0x%08x to 0x%08x of inode #%u will be lost\n",
++ start, end, f->inocache->ino);
+ goto fill;
+ }
+ if (ri.compr != JFFS2_COMPR_ZERO) {
+- printk(KERN_WARNING "jffs2_garbage_collect_hole: Node 0x%08x wasn't a hole node!\n", fn->raw->flash_offset & ~3);
+- printk(KERN_WARNING "Data in the range 0x%08x to 0x%08x of inode #%lu will be lost\n",
+- start, end, inode->i_ino);
++ printk(KERN_WARNING "jffs2_garbage_collect_hole: Node 0x%08x wasn't a hole node!\n", ref_offset(fn->raw));
++ printk(KERN_WARNING "Data in the range 0x%08x to 0x%08x of inode #%u will be lost\n",
++ start, end, f->inocache->ino);
+ goto fill;
+ }
+ } else {
+ fill:
+- ri.magic = JFFS2_MAGIC_BITMASK;
+- ri.nodetype = JFFS2_NODETYPE_INODE;
+- ri.totlen = sizeof(ri);
+- ri.hdr_crc = crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4);
+-
+- ri.ino = inode->i_ino;
+- ri.version = ++f->highest_version;
+- ri.offset = start;
+- ri.dsize = end - start;
+- ri.csize = 0;
++ ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
++ ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE);
++ ri.totlen = cpu_to_je32(sizeof(ri));
++ ri.hdr_crc = cpu_to_je32(crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4));
++
++ ri.ino = cpu_to_je32(f->inocache->ino);
++ ri.version = cpu_to_je32(++f->highest_version);
++ ri.offset = cpu_to_je32(start);
++ ri.dsize = cpu_to_je32(end - start);
++ ri.csize = cpu_to_je32(0);
+ ri.compr = JFFS2_COMPR_ZERO;
+ }
+- ri.mode = inode->i_mode;
+- ri.uid = inode->i_uid;
+- ri.gid = inode->i_gid;
+- ri.isize = inode->i_size;
+- ri.atime = inode->i_atime;
+- ri.ctime = inode->i_ctime;
+- ri.mtime = inode->i_mtime;
+- ri.data_crc = 0;
+- ri.node_crc = crc32(0, &ri, sizeof(ri)-8);
++ ri.mode = cpu_to_jemode(JFFS2_F_I_MODE(f));
++ ri.uid = cpu_to_je16(JFFS2_F_I_UID(f));
++ ri.gid = cpu_to_je16(JFFS2_F_I_GID(f));
++ ri.isize = cpu_to_je32(JFFS2_F_I_SIZE(f));
++ ri.atime = cpu_to_je32(JFFS2_F_I_ATIME(f));
++ ri.ctime = cpu_to_je32(JFFS2_F_I_CTIME(f));
++ ri.mtime = cpu_to_je32(JFFS2_F_I_MTIME(f));
++ ri.data_crc = cpu_to_je32(0);
++ ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8));
+
+ ret = jffs2_reserve_space_gc(c, sizeof(ri), &phys_ofs, &alloclen);
+ if (ret) {
+- printk(KERN_WARNING "jffs2_reserve_space_gc of %d bytes for garbage_collect_hole failed: %d\n",
++ printk(KERN_WARNING "jffs2_reserve_space_gc of %zd bytes for garbage_collect_hole failed: %d\n",
+ sizeof(ri), ret);
+ return ret;
+ }
+- new_fn = jffs2_write_dnode(inode, &ri, NULL, 0, phys_ofs, NULL);
++ new_fn = jffs2_write_dnode(c, f, &ri, NULL, 0, phys_ofs, ALLOC_GC);
+
+ if (IS_ERR(new_fn)) {
+ printk(KERN_WARNING "Error writing new hole node: %ld\n", PTR_ERR(new_fn));
+ return PTR_ERR(new_fn);
+ }
+- if (ri.version == f->highest_version) {
++ if (je32_to_cpu(ri.version) == f->highest_version) {
+ jffs2_add_full_dnode_to_inode(c, f, new_fn);
+ if (f->metadata) {
+ jffs2_mark_node_obsolete(c, f->metadata->raw);
+@@ -541,12 +981,17 @@
+ * number as before. (Except in case of error -- see 'goto fill;'
+ * above.)
+ */
+- D1(if(fn->frags <= 1) {
++ D1(if(unlikely(fn->frags <= 1)) {
+ printk(KERN_WARNING "jffs2_garbage_collect_hole: Replacing fn with %d frag(s) but new ver %d != highest_version %d of ino #%d\n",
+- fn->frags, ri.version, f->highest_version, ri.ino);
++ fn->frags, je32_to_cpu(ri.version), f->highest_version,
++ je32_to_cpu(ri.ino));
+ });
+
+- for (frag = f->fraglist; frag; frag = frag->next) {
++ /* This is a partially-overlapped hole node. Mark it REF_NORMAL not REF_PRISTINE */
++ mark_ref_normal(new_fn->raw);
++
++ for (frag = jffs2_lookup_node_frag(&f->fragtree, fn->ofs);
++ frag; frag = frag_next(frag)) {
+ if (frag->ofs > fn->size + fn->ofs)
+ break;
+ if (frag->node == fn) {
+@@ -571,49 +1016,146 @@
+ }
+
+ static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
+- struct inode *inode, struct jffs2_full_dnode *fn,
+- __u32 start, __u32 end)
++ struct jffs2_inode_info *f, struct jffs2_full_dnode *fn,
++ uint32_t start, uint32_t end)
+ {
+- struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
+ struct jffs2_full_dnode *new_fn;
+ struct jffs2_raw_inode ri;
+- __u32 alloclen, phys_ofs, offset, orig_end;
++ uint32_t alloclen, phys_ofs, offset, orig_end, orig_start;
+ int ret = 0;
+ unsigned char *comprbuf = NULL, *writebuf;
+- struct page *pg;
++ unsigned long pg;
+ unsigned char *pg_ptr;
+
+-
+ memset(&ri, 0, sizeof(ri));
+
+- D1(printk(KERN_DEBUG "Writing replacement dnode for ino #%lu from offset 0x%x to 0x%x\n",
+- inode->i_ino, start, end));
++ D1(printk(KERN_DEBUG "Writing replacement dnode for ino #%u from offset 0x%x to 0x%x\n",
++ f->inocache->ino, start, end));
+
+ orig_end = end;
++ orig_start = start;
+
++ if (c->nr_free_blocks + c->nr_erasing_blocks > c->resv_blocks_gcmerge) {
++ /* Attempt to do some merging. But only expand to cover logically
++ adjacent frags if the block containing them is already considered
++ to be dirty. Otherwise we end up with GC just going round in
++ circles dirtying the nodes it already wrote out, especially
++ on NAND where we have small eraseblocks and hence a much higher
++ chance of nodes having to be split to cross boundaries. */
+
+- /* If we're looking at the last node in the block we're
+- garbage-collecting, we allow ourselves to merge as if the
+- block was already erasing. We're likely to be GC'ing a
+- partial page, and the next block we GC is likely to have
+- the other half of this page right at the beginning, which
+- means we'd expand it _then_, as nr_erasing_blocks would have
+- increased since we checked, and in doing so would obsolete
+- the partial node which we'd have written here. Meaning that
+- the GC would churn and churn, and just leave dirty blocks in
+- it's wake.
+- */
+- if(c->nr_free_blocks + c->nr_erasing_blocks > JFFS2_RESERVED_BLOCKS_GCMERGE - (fn->raw->next_phys?0:1)) {
+- /* Shitloads of space */
+- /* FIXME: Integrate this properly with GC calculations */
+- start &= ~(PAGE_CACHE_SIZE-1);
+- end = min_t(__u32, start + PAGE_CACHE_SIZE, inode->i_size);
+- D1(printk(KERN_DEBUG "Plenty of free space, so expanding to write from offset 0x%x to 0x%x\n",
+- start, end));
+- if (end < orig_end) {
+- printk(KERN_WARNING "Eep. jffs2_garbage_collect_dnode extended node to write, but it got smaller: start 0x%x, orig_end 0x%x, end 0x%x\n", start, orig_end, end);
+- end = orig_end;
++ struct jffs2_node_frag *frag;
++ uint32_t min, max;
++
++ min = start & ~(PAGE_CACHE_SIZE-1);
++ max = min + PAGE_CACHE_SIZE;
++
++ frag = jffs2_lookup_node_frag(&f->fragtree, start);
++
++ /* BUG_ON(!frag) but that'll happen anyway... */
++
++ BUG_ON(frag->ofs != start);
++
++ /* First grow down... */
++ while((frag = frag_prev(frag)) && frag->ofs >= min) {
++
++ /* If the previous frag doesn't even reach the beginning, there's
++ excessive fragmentation. Just merge. */
++ if (frag->ofs > min) {
++ D1(printk(KERN_DEBUG "Expanding down to cover partial frag (0x%x-0x%x)\n",
++ frag->ofs, frag->ofs+frag->size));
++ start = frag->ofs;
++ continue;
++ }
++ /* OK. This frag holds the first byte of the page. */
++ if (!frag->node || !frag->node->raw) {
++ D1(printk(KERN_DEBUG "First frag in page is hole (0x%x-0x%x). Not expanding down.\n",
++ frag->ofs, frag->ofs+frag->size));
++ break;
++ } else {
++
++ /* OK, it's a frag which extends to the beginning of the page. Does it live
++ in a block which is still considered clean? If so, don't obsolete it.
++ If not, cover it anyway. */
++
++ struct jffs2_raw_node_ref *raw = frag->node->raw;
++ struct jffs2_eraseblock *jeb;
++
++ jeb = &c->blocks[raw->flash_offset / c->sector_size];
++
++ if (jeb == c->gcblock) {
++ D1(printk(KERN_DEBUG "Expanding down to cover frag (0x%x-0x%x) in gcblock at %08x\n",
++ frag->ofs, frag->ofs+frag->size, ref_offset(raw)));
++ start = frag->ofs;
++ break;
+ }
++ if (!ISDIRTY(jeb->dirty_size + jeb->wasted_size)) {
++ D1(printk(KERN_DEBUG "Not expanding down to cover frag (0x%x-0x%x) in clean block %08x\n",
++ frag->ofs, frag->ofs+frag->size, jeb->offset));
++ break;
++ }
++
++ D1(printk(KERN_DEBUG "Expanding down to cover frag (0x%x-0x%x) in dirty block %08x\n",
++ frag->ofs, frag->ofs+frag->size, jeb->offset));
++ start = frag->ofs;
++ break;
++ }
++ }
++
++ /* ... then up */
++
++ /* Find last frag which is actually part of the node we're to GC. */
++ frag = jffs2_lookup_node_frag(&f->fragtree, end-1);
++
++ while((frag = frag_next(frag)) && frag->ofs+frag->size <= max) {
++
++ /* If the previous frag doesn't even reach the beginning, there's lots
++ of fragmentation. Just merge. */
++ if (frag->ofs+frag->size < max) {
++ D1(printk(KERN_DEBUG "Expanding up to cover partial frag (0x%x-0x%x)\n",
++ frag->ofs, frag->ofs+frag->size));
++ end = frag->ofs + frag->size;
++ continue;
++ }
++
++ if (!frag->node || !frag->node->raw) {
++ D1(printk(KERN_DEBUG "Last frag in page is hole (0x%x-0x%x). Not expanding up.\n",
++ frag->ofs, frag->ofs+frag->size));
++ break;
++ } else {
++
++ /* OK, it's a frag which extends to the beginning of the page. Does it live
++ in a block which is still considered clean? If so, don't obsolete it.
++ If not, cover it anyway. */
++
++ struct jffs2_raw_node_ref *raw = frag->node->raw;
++ struct jffs2_eraseblock *jeb;
++
++ jeb = &c->blocks[raw->flash_offset / c->sector_size];
++
++ if (jeb == c->gcblock) {
++ D1(printk(KERN_DEBUG "Expanding up to cover frag (0x%x-0x%x) in gcblock at %08x\n",
++ frag->ofs, frag->ofs+frag->size, ref_offset(raw)));
++ end = frag->ofs + frag->size;
++ break;
++ }
++ if (!ISDIRTY(jeb->dirty_size + jeb->wasted_size)) {
++ D1(printk(KERN_DEBUG "Not expanding up to cover frag (0x%x-0x%x) in clean block %08x\n",
++ frag->ofs, frag->ofs+frag->size, jeb->offset));
++ break;
++ }
++
++ D1(printk(KERN_DEBUG "Expanding up to cover frag (0x%x-0x%x) in dirty block %08x\n",
++ frag->ofs, frag->ofs+frag->size, jeb->offset));
++ end = frag->ofs + frag->size;
++ break;
++ }
++ }
++ D1(printk(KERN_DEBUG "Expanded dnode to write from (0x%x-0x%x) to (0x%x-0x%x)\n",
++ orig_start, orig_end, start, end));
++
++ BUG_ON(end > JFFS2_F_I_SIZE(f));
++ BUG_ON(end < orig_end);
++ BUG_ON(start > orig_start);
+ }
+
+ /* First, use readpage() to read the appropriate page into the page cache */
+@@ -623,63 +1165,57 @@
+ * page OK. We'll actually write it out again in commit_write, which is a little
+ * suboptimal, but at least we're correct.
+ */
+- pg = read_cache_page(inode->i_mapping, start >> PAGE_CACHE_SHIFT, (void *)jffs2_do_readpage_unlock, inode);
++ pg_ptr = jffs2_gc_fetch_page(c, f, start, &pg);
+
+- if (IS_ERR(pg)) {
+- printk(KERN_WARNING "read_cache_page() returned error: %ld\n", PTR_ERR(pg));
+- return PTR_ERR(pg);
++ if (IS_ERR(pg_ptr)) {
++ printk(KERN_WARNING "read_cache_page() returned error: %ld\n", PTR_ERR(pg_ptr));
++ return PTR_ERR(pg_ptr);
+ }
+- pg_ptr = (char *)kmap(pg);
+- comprbuf = kmalloc(end - start, GFP_KERNEL);
+
+ offset = start;
+ while(offset < orig_end) {
+- __u32 datalen;
+- __u32 cdatalen;
++ uint32_t datalen;
++ uint32_t cdatalen;
+ char comprtype = JFFS2_COMPR_NONE;
+
+ ret = jffs2_reserve_space_gc(c, sizeof(ri) + JFFS2_MIN_DATA_LEN, &phys_ofs, &alloclen);
+
+ if (ret) {
+- printk(KERN_WARNING "jffs2_reserve_space_gc of %d bytes for garbage_collect_dnode failed: %d\n",
++ printk(KERN_WARNING "jffs2_reserve_space_gc of %zd bytes for garbage_collect_dnode failed: %d\n",
+ sizeof(ri)+ JFFS2_MIN_DATA_LEN, ret);
+ break;
+ }
+- cdatalen = min(alloclen - sizeof(ri), end - offset);
++ cdatalen = min_t(uint32_t, alloclen - sizeof(ri), end - offset);
+ datalen = end - offset;
+
+ writebuf = pg_ptr + (offset & (PAGE_CACHE_SIZE -1));
+
+- if (comprbuf) {
+- comprtype = jffs2_compress(writebuf, comprbuf, &datalen, &cdatalen);
+- }
+- if (comprtype) {
+- writebuf = comprbuf;
+- } else {
+- datalen = cdatalen;
+- }
+- ri.magic = JFFS2_MAGIC_BITMASK;
+- ri.nodetype = JFFS2_NODETYPE_INODE;
+- ri.totlen = sizeof(ri) + cdatalen;
+- ri.hdr_crc = crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4);
+-
+- ri.ino = inode->i_ino;
+- ri.version = ++f->highest_version;
+- ri.mode = inode->i_mode;
+- ri.uid = inode->i_uid;
+- ri.gid = inode->i_gid;
+- ri.isize = inode->i_size;
+- ri.atime = inode->i_atime;
+- ri.ctime = inode->i_ctime;
+- ri.mtime = inode->i_mtime;
+- ri.offset = offset;
+- ri.csize = cdatalen;
+- ri.dsize = datalen;
++ comprtype = jffs2_compress(writebuf, &comprbuf, &datalen, &cdatalen);
++
++ ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
++ ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE);
++ ri.totlen = cpu_to_je32(sizeof(ri) + cdatalen);
++ ri.hdr_crc = cpu_to_je32(crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4));
++
++ ri.ino = cpu_to_je32(f->inocache->ino);
++ ri.version = cpu_to_je32(++f->highest_version);
++ ri.mode = cpu_to_jemode(JFFS2_F_I_MODE(f));
++ ri.uid = cpu_to_je16(JFFS2_F_I_UID(f));
++ ri.gid = cpu_to_je16(JFFS2_F_I_GID(f));
++ ri.isize = cpu_to_je32(JFFS2_F_I_SIZE(f));
++ ri.atime = cpu_to_je32(JFFS2_F_I_ATIME(f));
++ ri.ctime = cpu_to_je32(JFFS2_F_I_CTIME(f));
++ ri.mtime = cpu_to_je32(JFFS2_F_I_MTIME(f));
++ ri.offset = cpu_to_je32(offset);
++ ri.csize = cpu_to_je32(cdatalen);
++ ri.dsize = cpu_to_je32(datalen);
+ ri.compr = comprtype;
+- ri.node_crc = crc32(0, &ri, sizeof(ri)-8);
+- ri.data_crc = crc32(0, writebuf, cdatalen);
++ ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8));
++ ri.data_crc = cpu_to_je32(crc32(0, comprbuf, cdatalen));
++
++ new_fn = jffs2_write_dnode(c, f, &ri, comprbuf, cdatalen, phys_ofs, ALLOC_GC);
+
+- new_fn = jffs2_write_dnode(inode, &ri, writebuf, cdatalen, phys_ofs, NULL);
++ jffs2_free_comprbuf(comprbuf, writebuf);
+
+ if (IS_ERR(new_fn)) {
+ printk(KERN_WARNING "Error writing new dnode: %ld\n", PTR_ERR(new_fn));
+@@ -694,12 +1230,8 @@
+ f->metadata = NULL;
+ }
+ }
+- if (comprbuf) kfree(comprbuf);
+
+- kunmap(pg);
+- /* XXX: Does the page get freed automatically? */
+- /* AAA: Judging by the unmount getting stuck in __wait_on_page, nope. */
+- page_cache_release(pg);
++ jffs2_gc_release_page(c, pg_ptr, &pg);
+ return ret;
+ }
+
+diff -Nurb linux-mips-2.4.27/fs/jffs2/ioctl.c linux/fs/jffs2/ioctl.c
+--- linux-mips-2.4.27/fs/jffs2/ioctl.c 2001-10-19 03:24:56.000000000 +0200
++++ linux/fs/jffs2/ioctl.c 2004-11-19 10:25:12.108168024 +0100
+@@ -1,37 +1,13 @@
+ /*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+- * Copyright (C) 2001 Red Hat, Inc.
++ * Copyright (C) 2001-2003 Red Hat, Inc.
+ *
+- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
++ * Created by David Woodhouse <dwmw2@redhat.com>
+ *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
++ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+- * The contents of this file are subject to the Red Hat eCos Public
+- * License Version 1.1 (the "Licence"); you may not use this file
+- * except in compliance with the Licence. You may obtain a copy of
+- * the Licence at http://www.redhat.com/
+- *
+- * Software distributed under the Licence is distributed on an "AS IS"
+- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+- * See the Licence for the specific language governing rights and
+- * limitations under the Licence.
+- *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
+- *
+- * Alternatively, the contents of this file may be used under the
+- * terms of the GNU General Public License version 2 (the "GPL"), in
+- * which case the provisions of the GPL are applicable instead of the
+- * above. If you wish to allow the use of your version of this file
+- * only under the terms of the GPL and not to allow others to use your
+- * version of this file under the RHEPL, indicate your decision by
+- * deleting the provisions above and replace them with the notice and
+- * other provisions required by the GPL. If you do not delete the
+- * provisions above, a recipient may use your version of this file
+- * under either the RHEPL or the GPL.
+- *
+- * $Id$
++ * $Id$
+ *
+ */
+
+@@ -42,6 +18,6 @@
+ {
+ /* Later, this will provide for lsattr.jffs2 and chattr.jffs2, which
+ will include compression support etc. */
+- return -EINVAL;
++ return -ENOTTY;
+ }
+
+diff -Nurb linux-mips-2.4.27/fs/jffs2/malloc.c linux/fs/jffs2/malloc.c
+--- linux-mips-2.4.27/fs/jffs2/malloc.c 2001-10-19 03:24:56.000000000 +0200
++++ linux/fs/jffs2/malloc.c 2004-11-19 10:25:12.110167720 +0100
+@@ -1,37 +1,13 @@
+ /*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+- * Copyright (C) 2001 Red Hat, Inc.
++ * Copyright (C) 2001-2003 Red Hat, Inc.
+ *
+- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
++ * Created by David Woodhouse <dwmw2@redhat.com>
+ *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
++ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+- * The contents of this file are subject to the Red Hat eCos Public
+- * License Version 1.1 (the "Licence"); you may not use this file
+- * except in compliance with the Licence. You may obtain a copy of
+- * the Licence at http://www.redhat.com/
+- *
+- * Software distributed under the Licence is distributed on an "AS IS"
+- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+- * See the Licence for the specific language governing rights and
+- * limitations under the Licence.
+- *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
+- *
+- * Alternatively, the contents of this file may be used under the
+- * terms of the GNU General Public License version 2 (the "GPL"), in
+- * which case the provisions of the GPL are applicable instead of the
+- * above. If you wish to allow the use of your version of this file
+- * only under the terms of the GPL and not to allow others to use your
+- * version of this file under the RHEPL, indicate your decision by
+- * deleting the provisions above and replace them with the notice and
+- * other provisions required by the GPL. If you do not delete the
+- * provisions above, a recipient may use your version of this file
+- * under either the RHEPL or the GPL.
+- *
+- * $Id$
++ * $Id$
+ *
+ */
+
+@@ -47,6 +23,9 @@
+ #define JFFS2_SLAB_POISON 0
+ #endif
+
++// replace this by #define D3 (x) x for cache debugging
++#define D3(x)
++
+ /* These are initialised to NULL in the kernel startup code.
+ If you're porting to other operating systems, beware */
+ static kmem_cache_t *full_dnode_slab;
+@@ -57,57 +36,47 @@
+ static kmem_cache_t *node_frag_slab;
+ static kmem_cache_t *inode_cache_slab;
+
+-void jffs2_free_tmp_dnode_info_list(struct jffs2_tmp_dnode_info *tn)
+-{
+- struct jffs2_tmp_dnode_info *next;
+-
+- while (tn) {
+- next = tn;
+- tn = tn->next;
+- jffs2_free_full_dnode(next->fn);
+- jffs2_free_tmp_dnode_info(next);
+- }
+-}
+-
+-void jffs2_free_full_dirent_list(struct jffs2_full_dirent *fd)
+-{
+- struct jffs2_full_dirent *next;
+-
+- while (fd) {
+- next = fd->next;
+- jffs2_free_full_dirent(fd);
+- fd = next;
+- }
+-}
+-
+ int __init jffs2_create_slab_caches(void)
+ {
+- full_dnode_slab = kmem_cache_create("jffs2_full_dnode", sizeof(struct jffs2_full_dnode), 0, JFFS2_SLAB_POISON, NULL, NULL);
++ full_dnode_slab = kmem_cache_create("jffs2_full_dnode",
++ sizeof(struct jffs2_full_dnode),
++ 0, JFFS2_SLAB_POISON, NULL, NULL);
+ if (!full_dnode_slab)
+ goto err;
+
+- raw_dirent_slab = kmem_cache_create("jffs2_raw_dirent", sizeof(struct jffs2_raw_dirent), 0, JFFS2_SLAB_POISON, NULL, NULL);
++ raw_dirent_slab = kmem_cache_create("jffs2_raw_dirent",
++ sizeof(struct jffs2_raw_dirent),
++ 0, JFFS2_SLAB_POISON, NULL, NULL);
+ if (!raw_dirent_slab)
+ goto err;
+
+- raw_inode_slab = kmem_cache_create("jffs2_raw_inode", sizeof(struct jffs2_raw_inode), 0, JFFS2_SLAB_POISON, NULL, NULL);
++ raw_inode_slab = kmem_cache_create("jffs2_raw_inode",
++ sizeof(struct jffs2_raw_inode),
++ 0, JFFS2_SLAB_POISON, NULL, NULL);
+ if (!raw_inode_slab)
+ goto err;
+
+- tmp_dnode_info_slab = kmem_cache_create("jffs2_tmp_dnode", sizeof(struct jffs2_tmp_dnode_info), 0, JFFS2_SLAB_POISON, NULL, NULL);
++ tmp_dnode_info_slab = kmem_cache_create("jffs2_tmp_dnode",
++ sizeof(struct jffs2_tmp_dnode_info),
++ 0, JFFS2_SLAB_POISON, NULL, NULL);
+ if (!tmp_dnode_info_slab)
+ goto err;
+
+- raw_node_ref_slab = kmem_cache_create("jffs2_raw_node_ref", sizeof(struct jffs2_raw_node_ref), 0, JFFS2_SLAB_POISON, NULL, NULL);
++ raw_node_ref_slab = kmem_cache_create("jffs2_raw_node_ref",
++ sizeof(struct jffs2_raw_node_ref),
++ 0, JFFS2_SLAB_POISON, NULL, NULL);
+ if (!raw_node_ref_slab)
+ goto err;
+
+- node_frag_slab = kmem_cache_create("jffs2_node_frag", sizeof(struct jffs2_node_frag), 0, JFFS2_SLAB_POISON, NULL, NULL);
++ node_frag_slab = kmem_cache_create("jffs2_node_frag",
++ sizeof(struct jffs2_node_frag),
++ 0, JFFS2_SLAB_POISON, NULL, NULL);
+ if (!node_frag_slab)
+ goto err;
+
+- inode_cache_slab = kmem_cache_create("jffs2_inode_cache", sizeof(struct jffs2_inode_cache), 0, JFFS2_SLAB_POISON, NULL, NULL);
+-
++ inode_cache_slab = kmem_cache_create("jffs2_inode_cache",
++ sizeof(struct jffs2_inode_cache),
++ 0, JFFS2_SLAB_POISON, NULL, NULL);
+ if (inode_cache_slab)
+ return 0;
+ err:
+@@ -131,7 +100,6 @@
+ kmem_cache_destroy(node_frag_slab);
+ if(inode_cache_slab)
+ kmem_cache_destroy(inode_cache_slab);
+-
+ }
+
+ struct jffs2_full_dirent *jffs2_alloc_full_dirent(int namesize)
+@@ -146,75 +114,92 @@
+
+ struct jffs2_full_dnode *jffs2_alloc_full_dnode(void)
+ {
+- void *ret = kmem_cache_alloc(full_dnode_slab, GFP_KERNEL);
++ struct jffs2_full_dnode *ret = kmem_cache_alloc(full_dnode_slab, GFP_KERNEL);
++ D3 (printk (KERN_DEBUG "alloc_full_dnode at %p\n", ret));
+ return ret;
+ }
+
+ void jffs2_free_full_dnode(struct jffs2_full_dnode *x)
+ {
++ D3 (printk (KERN_DEBUG "free full_dnode at %p\n", x));
+ kmem_cache_free(full_dnode_slab, x);
+ }
+
+ struct jffs2_raw_dirent *jffs2_alloc_raw_dirent(void)
+ {
+- return kmem_cache_alloc(raw_dirent_slab, GFP_KERNEL);
++ struct jffs2_raw_dirent *ret = kmem_cache_alloc(raw_dirent_slab, GFP_KERNEL);
++ D3 (printk (KERN_DEBUG "alloc_raw_dirent\n", ret));
++ return ret;
+ }
+
+ void jffs2_free_raw_dirent(struct jffs2_raw_dirent *x)
+ {
++ D3 (printk (KERN_DEBUG "free_raw_dirent at %p\n", x));
+ kmem_cache_free(raw_dirent_slab, x);
+ }
+
+ struct jffs2_raw_inode *jffs2_alloc_raw_inode(void)
+ {
+- return kmem_cache_alloc(raw_inode_slab, GFP_KERNEL);
++ struct jffs2_raw_inode *ret = kmem_cache_alloc(raw_inode_slab, GFP_KERNEL);
++ D3 (printk (KERN_DEBUG "alloc_raw_inode at %p\n", ret));
++ return ret;
+ }
+
+ void jffs2_free_raw_inode(struct jffs2_raw_inode *x)
+ {
++ D3 (printk (KERN_DEBUG "free_raw_inode at %p\n", x));
+ kmem_cache_free(raw_inode_slab, x);
+ }
+
+ struct jffs2_tmp_dnode_info *jffs2_alloc_tmp_dnode_info(void)
+ {
+- return kmem_cache_alloc(tmp_dnode_info_slab, GFP_KERNEL);
++ struct jffs2_tmp_dnode_info *ret = kmem_cache_alloc(tmp_dnode_info_slab, GFP_KERNEL);
++ D3 (printk (KERN_DEBUG "alloc_tmp_dnode_info at %p\n", ret));
++ return ret;
+ }
+
+ void jffs2_free_tmp_dnode_info(struct jffs2_tmp_dnode_info *x)
+ {
++ D3 (printk (KERN_DEBUG "free_tmp_dnode_info at %p\n", x));
+ kmem_cache_free(tmp_dnode_info_slab, x);
+ }
+
+ struct jffs2_raw_node_ref *jffs2_alloc_raw_node_ref(void)
+ {
+- return kmem_cache_alloc(raw_node_ref_slab, GFP_KERNEL);
++ struct jffs2_raw_node_ref *ret = kmem_cache_alloc(raw_node_ref_slab, GFP_KERNEL);
++ D3 (printk (KERN_DEBUG "alloc_raw_node_ref at %p\n", ret));
++ return ret;
+ }
+
+ void jffs2_free_raw_node_ref(struct jffs2_raw_node_ref *x)
+ {
++ D3 (printk (KERN_DEBUG "free_raw_node_ref at %p\n", x));
+ kmem_cache_free(raw_node_ref_slab, x);
+ }
+
+ struct jffs2_node_frag *jffs2_alloc_node_frag(void)
+ {
+- return kmem_cache_alloc(node_frag_slab, GFP_KERNEL);
++ struct jffs2_node_frag *ret = kmem_cache_alloc(node_frag_slab, GFP_KERNEL);
++ D3 (printk (KERN_DEBUG "alloc_node_frag at %p\n", ret));
++ return ret;
+ }
+
+ void jffs2_free_node_frag(struct jffs2_node_frag *x)
+ {
++ D3 (printk (KERN_DEBUG "free_node_frag at %p\n", x));
+ kmem_cache_free(node_frag_slab, x);
+ }
+
+ struct jffs2_inode_cache *jffs2_alloc_inode_cache(void)
+ {
+ struct jffs2_inode_cache *ret = kmem_cache_alloc(inode_cache_slab, GFP_KERNEL);
+- D1(printk(KERN_DEBUG "Allocated inocache at %p\n", ret));
++ D3 (printk(KERN_DEBUG "Allocated inocache at %p\n", ret));
+ return ret;
+ }
+
+ void jffs2_free_inode_cache(struct jffs2_inode_cache *x)
+ {
+- D1(printk(KERN_DEBUG "Freeing inocache at %p\n", x));
++ D3 (printk(KERN_DEBUG "Freeing inocache at %p\n", x));
+ kmem_cache_free(inode_cache_slab, x);
+ }
+
+diff -Nurb linux-mips-2.4.27/fs/jffs2/nodelist.c linux/fs/jffs2/nodelist.c
+--- linux-mips-2.4.27/fs/jffs2/nodelist.c 2003-07-05 05:23:44.000000000 +0200
++++ linux/fs/jffs2/nodelist.c 2004-11-19 10:25:12.112167416 +0100
+@@ -1,44 +1,24 @@
+ /*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+- * Copyright (C) 2001, 2002 Red Hat, Inc.
++ * Copyright (C) 2001-2003 Red Hat, Inc.
+ *
+- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
++ * Created by David Woodhouse <dwmw2@redhat.com>
+ *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
++ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+- * The contents of this file are subject to the Red Hat eCos Public
+- * License Version 1.1 (the "Licence"); you may not use this file
+- * except in compliance with the Licence. You may obtain a copy of
+- * the Licence at http://www.redhat.com/
+- *
+- * Software distributed under the Licence is distributed on an "AS IS"
+- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+- * See the Licence for the specific language governing rights and
+- * limitations under the Licence.
+- *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
+- *
+- * Alternatively, the contents of this file may be used under the
+- * terms of the GNU General Public License version 2 (the "GPL"), in
+- * which case the provisions of the GPL are applicable instead of the
+- * above. If you wish to allow the use of your version of this file
+- * only under the terms of the GPL and not to allow others to use your
+- * version of this file under the RHEPL, indicate your decision by
+- * deleting the provisions above and replace them with the notice and
+- * other provisions required by the GPL. If you do not delete the
+- * provisions above, a recipient may use your version of this file
+- * under either the RHEPL or the GPL.
+- *
+- * $Id$
++ * $Id$
+ *
+ */
+
+ #include <linux/kernel.h>
+-#include <linux/jffs2.h>
++#include <linux/sched.h>
+ #include <linux/fs.h>
+ #include <linux/mtd/mtd.h>
++#include <linux/rbtree.h>
++#include <linux/crc32.h>
++#include <linux/slab.h>
++#include <linux/pagemap.h>
+ #include "nodelist.h"
+
+ void jffs2_add_fd_to_list(struct jffs2_sb_info *c, struct jffs2_full_dirent *new, struct jffs2_full_dirent **list)
+@@ -78,7 +58,7 @@
+ /* Put a new tmp_dnode_info into the list, keeping the list in
+ order of increasing version
+ */
+-void jffs2_add_tn_to_list(struct jffs2_tmp_dnode_info *tn, struct jffs2_tmp_dnode_info **list)
++static void jffs2_add_tn_to_list(struct jffs2_tmp_dnode_info *tn, struct jffs2_tmp_dnode_info **list)
+ {
+ struct jffs2_tmp_dnode_info **prev = list;
+
+@@ -89,13 +69,37 @@
+ *prev = tn;
+ }
+
++static void jffs2_free_tmp_dnode_info_list(struct jffs2_tmp_dnode_info *tn)
++{
++ struct jffs2_tmp_dnode_info *next;
++
++ while (tn) {
++ next = tn;
++ tn = tn->next;
++ jffs2_free_full_dnode(next->fn);
++ jffs2_free_tmp_dnode_info(next);
++ }
++}
++
++static void jffs2_free_full_dirent_list(struct jffs2_full_dirent *fd)
++{
++ struct jffs2_full_dirent *next;
++
++ while (fd) {
++ next = fd->next;
++ jffs2_free_full_dirent(fd);
++ fd = next;
++ }
++}
++
++
+ /* Get tmp_dnode_info and full_dirent for all non-obsolete nodes associated
+ with this ino, returning the former in order of version */
+
+ int jffs2_get_inode_nodes(struct jffs2_sb_info *c, ino_t ino, struct jffs2_inode_info *f,
+ struct jffs2_tmp_dnode_info **tnp, struct jffs2_full_dirent **fdp,
+- __u32 *highest_version, __u32 *latest_mctime,
+- __u32 *mctime_ver)
++ uint32_t *highest_version, uint32_t *latest_mctime,
++ uint32_t *mctime_ver)
+ {
+ struct jffs2_raw_node_ref *ref = f->inocache->nodes;
+ struct jffs2_tmp_dnode_info *tn, *ret_tn = NULL;
+@@ -109,43 +113,71 @@
+
+ D1(printk(KERN_DEBUG "jffs2_get_inode_nodes(): ino #%lu\n", ino));
+ if (!f->inocache->nodes) {
+- printk(KERN_WARNING "Eep. no nodes for ino #%lu\n", ino);
++ printk(KERN_WARNING "Eep. no nodes for ino #%lu\n", (unsigned long)ino);
+ }
++
++ spin_lock(&c->erase_completion_lock);
++
+ for (ref = f->inocache->nodes; ref && ref->next_in_ino; ref = ref->next_in_ino) {
+ /* Work out whether it's a data node or a dirent node */
+- if (ref->flash_offset & 1) {
++ if (ref_obsolete(ref)) {
+ /* FIXME: On NAND flash we may need to read these */
+- D1(printk(KERN_DEBUG "node at 0x%08x is obsoleted. Ignoring.\n", ref->flash_offset &~3));
++ D1(printk(KERN_DEBUG "node at 0x%08x is obsoleted. Ignoring.\n", ref_offset(ref)));
+ continue;
+ }
+- err = c->mtd->read(c->mtd, (ref->flash_offset & ~3), min(ref->totlen, sizeof(node)), &retlen, (void *)&node);
++ /* We can hold a pointer to a non-obsolete node without the spinlock,
++ but _obsolete_ nodes may disappear at any time, if the block
++ they're in gets erased */
++ spin_unlock(&c->erase_completion_lock);
++
++ cond_resched();
++
++ /* FIXME: point() */
++ err = jffs2_flash_read(c, (ref_offset(ref)),
++ min_t(uint32_t, ref_totlen(c, NULL, ref), sizeof(node)),
++ &retlen, (void *)&node);
+ if (err) {
+- printk(KERN_WARNING "error %d reading node at 0x%08x in get_inode_nodes()\n", err, (ref->flash_offset) & ~3);
++ printk(KERN_WARNING "error %d reading node at 0x%08x in get_inode_nodes()\n", err, ref_offset(ref));
+ goto free_out;
+ }
+
+
+ /* Check we've managed to read at least the common node header */
+- if (retlen < min(ref->totlen, sizeof(node.u))) {
++ if (retlen < min_t(uint32_t, ref_totlen(c, NULL, ref), sizeof(node.u))) {
+ printk(KERN_WARNING "short read in get_inode_nodes()\n");
+ err = -EIO;
+ goto free_out;
+ }
+
+- switch (node.u.nodetype) {
++ switch (je16_to_cpu(node.u.nodetype)) {
+ case JFFS2_NODETYPE_DIRENT:
+- D1(printk(KERN_DEBUG "Node at %08x is a dirent node\n", ref->flash_offset &~3));
++ D1(printk(KERN_DEBUG "Node at %08x (%d) is a dirent node\n", ref_offset(ref), ref_flags(ref)));
++ if (ref_flags(ref) == REF_UNCHECKED) {
++ printk(KERN_WARNING "BUG: Dirent node at 0x%08x never got checked? How?\n", ref_offset(ref));
++ BUG();
++ }
+ if (retlen < sizeof(node.d)) {
+ printk(KERN_WARNING "short read in get_inode_nodes()\n");
+ err = -EIO;
+ goto free_out;
+ }
+- if (node.d.version > *highest_version)
+- *highest_version = node.d.version;
+- if (ref->flash_offset & 1) {
+- /* Obsoleted */
++ /* sanity check */
++ if (PAD((node.d.nsize + sizeof (node.d))) != PAD(je32_to_cpu (node.d.totlen))) {
++ printk(KERN_NOTICE "jffs2_get_inode_nodes(): Illegal nsize in node at 0x%08x: nsize 0x%02x, totlen %04x\n",
++ ref_offset(ref), node.d.nsize, je32_to_cpu(node.d.totlen));
++ jffs2_mark_node_obsolete(c, ref);
++ spin_lock(&c->erase_completion_lock);
+ continue;
+ }
++ if (je32_to_cpu(node.d.version) > *highest_version)
++ *highest_version = je32_to_cpu(node.d.version);
++ if (ref_obsolete(ref)) {
++ /* Obsoleted. This cannot happen, surely? dwmw2 20020308 */
++ printk(KERN_ERR "Dirent node at 0x%08x became obsolete while we weren't looking\n",
++ ref_offset(ref));
++ BUG();
++ }
++
+ fd = jffs2_alloc_full_dirent(node.d.nsize+1);
+ if (!fd) {
+ err = -ENOMEM;
+@@ -153,29 +185,30 @@
+ }
+ memset(fd,0,sizeof(struct jffs2_full_dirent) + node.d.nsize+1);
+ fd->raw = ref;
+- fd->version = node.d.version;
+- fd->ino = node.d.ino;
++ fd->version = je32_to_cpu(node.d.version);
++ fd->ino = je32_to_cpu(node.d.ino);
+ fd->type = node.d.type;
+
+ /* Pick out the mctime of the latest dirent */
+ if(fd->version > *mctime_ver) {
+ *mctime_ver = fd->version;
+- *latest_mctime = node.d.mctime;
++ *latest_mctime = je32_to_cpu(node.d.mctime);
+ }
+
+ /* memcpy as much of the name as possible from the raw
+ dirent we've already read from the flash
+ */
+ if (retlen > sizeof(struct jffs2_raw_dirent))
+- memcpy(&fd->name[0], &node.d.name[0], min((__u32)node.d.nsize, (retlen-sizeof(struct jffs2_raw_dirent))));
++ memcpy(&fd->name[0], &node.d.name[0], min_t(uint32_t, node.d.nsize, (retlen-sizeof(struct jffs2_raw_dirent))));
+
+ /* Do we need to copy any more of the name directly
+ from the flash?
+ */
+ if (node.d.nsize + sizeof(struct jffs2_raw_dirent) > retlen) {
++ /* FIXME: point() */
+ int already = retlen - sizeof(struct jffs2_raw_dirent);
+
+- err = c->mtd->read(c->mtd, (ref->flash_offset & ~3) + retlen,
++ err = jffs2_flash_read(c, (ref_offset(ref)) + retlen,
+ node.d.nsize - already, &retlen, &fd->name[already]);
+ if (!err && retlen != node.d.nsize - already)
+ err = -EIO;
+@@ -196,21 +229,126 @@
+ break;
+
+ case JFFS2_NODETYPE_INODE:
+- D1(printk(KERN_DEBUG "Node at %08x is a data node\n", ref->flash_offset &~3));
++ D1(printk(KERN_DEBUG "Node at %08x (%d) is a data node\n", ref_offset(ref), ref_flags(ref)));
+ if (retlen < sizeof(node.i)) {
+ printk(KERN_WARNING "read too short for dnode\n");
+ err = -EIO;
+ goto free_out;
+ }
+- if (node.i.version > *highest_version)
+- *highest_version = node.i.version;
+- D1(printk(KERN_DEBUG "version %d, highest_version now %d\n", node.i.version, *highest_version));
+-
+- if (ref->flash_offset & 1) {
+- D1(printk(KERN_DEBUG "obsoleted\n"));
+- /* Obsoleted */
++ if (je32_to_cpu(node.i.version) > *highest_version)
++ *highest_version = je32_to_cpu(node.i.version);
++ D1(printk(KERN_DEBUG "version %d, highest_version now %d\n", je32_to_cpu(node.i.version), *highest_version));
++
++ if (ref_obsolete(ref)) {
++ /* Obsoleted. This cannot happen, surely? dwmw2 20020308 */
++ printk(KERN_ERR "Inode node at 0x%08x became obsolete while we weren't looking\n",
++ ref_offset(ref));
++ BUG();
++ }
++
++ /* If we've never checked the CRCs on this node, check them now. */
++ if (ref_flags(ref) == REF_UNCHECKED) {
++ uint32_t crc, len;
++ struct jffs2_eraseblock *jeb;
++
++ crc = crc32(0, &node, sizeof(node.i)-8);
++ if (crc != je32_to_cpu(node.i.node_crc)) {
++ printk(KERN_NOTICE "jffs2_get_inode_nodes(): CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
++ ref_offset(ref), je32_to_cpu(node.i.node_crc), crc);
++ jffs2_mark_node_obsolete(c, ref);
++ spin_lock(&c->erase_completion_lock);
+ continue;
+ }
++
++ /* sanity checks */
++ if ( je32_to_cpu(node.i.offset) > je32_to_cpu(node.i.isize) ||
++ PAD(je32_to_cpu(node.i.csize) + sizeof (node.i)) != PAD(je32_to_cpu(node.i.totlen))) {
++ printk(KERN_NOTICE "jffs2_get_inode_nodes(): Inode corrupted at 0x%08x, totlen %d, #ino %d, version %d, isize %d, csize %d, dsize %d \n",
++ ref_offset(ref), je32_to_cpu(node.i.totlen), je32_to_cpu(node.i.ino),
++ je32_to_cpu(node.i.version), je32_to_cpu(node.i.isize),
++ je32_to_cpu(node.i.csize), je32_to_cpu(node.i.dsize));
++ jffs2_mark_node_obsolete(c, ref);
++ spin_lock(&c->erase_completion_lock);
++ continue;
++ }
++
++ if (node.i.compr != JFFS2_COMPR_ZERO && je32_to_cpu(node.i.csize)) {
++ unsigned char *buf=NULL;
++ uint32_t pointed = 0;
++#ifndef __ECOS
++ if (c->mtd->point) {
++ err = c->mtd->point (c->mtd, ref_offset(ref) + sizeof(node.i), je32_to_cpu(node.i.csize),
++ &retlen, &buf);
++ if (!err && retlen < je32_to_cpu(node.i.csize)) {
++ D1(printk(KERN_DEBUG "MTD point returned len too short: 0x%zx\n", retlen));
++ c->mtd->unpoint(c->mtd, buf, ref_offset(ref) + sizeof(node.i), je32_to_cpu(node.i.csize));
++ } else if (err){
++ D1(printk(KERN_DEBUG "MTD point failed %d\n", err));
++ } else
++ pointed = 1; /* succefully pointed to device */
++ }
++#endif
++ if(!pointed){
++ buf = kmalloc(je32_to_cpu(node.i.csize), GFP_KERNEL);
++ if (!buf)
++ return -ENOMEM;
++
++ err = jffs2_flash_read(c, ref_offset(ref) + sizeof(node.i), je32_to_cpu(node.i.csize),
++ &retlen, buf);
++ if (!err && retlen != je32_to_cpu(node.i.csize))
++ err = -EIO;
++ if (err) {
++ kfree(buf);
++ return err;
++ }
++ }
++ crc = crc32(0, buf, je32_to_cpu(node.i.csize));
++ if(!pointed)
++ kfree(buf);
++#ifndef __ECOS
++ else
++ c->mtd->unpoint(c->mtd, buf, ref_offset(ref) + sizeof(node.i), je32_to_cpu(node.i.csize));
++#endif
++
++ if (crc != je32_to_cpu(node.i.data_crc)) {
++ printk(KERN_NOTICE "jffs2_get_inode_nodes(): Data CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
++ ref_offset(ref), je32_to_cpu(node.i.data_crc), crc);
++ jffs2_mark_node_obsolete(c, ref);
++ spin_lock(&c->erase_completion_lock);
++ continue;
++ }
++
++ }
++
++ /* Mark the node as having been checked and fix the accounting accordingly */
++ spin_lock(&c->erase_completion_lock);
++ jeb = &c->blocks[ref->flash_offset / c->sector_size];
++ len = ref_totlen(c, jeb, ref);
++
++ jeb->used_size += len;
++ jeb->unchecked_size -= len;
++ c->used_size += len;
++ c->unchecked_size -= len;
++
++ /* If node covers at least a whole page, or if it starts at the
++ beginning of a page and runs to the end of the file, or if
++ it's a hole node, mark it REF_PRISTINE, else REF_NORMAL.
++
++ If it's actually overlapped, it'll get made NORMAL (or OBSOLETE)
++ when the overlapping node(s) get added to the tree anyway.
++ */
++ if ((je32_to_cpu(node.i.dsize) >= PAGE_CACHE_SIZE) ||
++ ( ((je32_to_cpu(node.i.offset)&(PAGE_CACHE_SIZE-1))==0) &&
++ (je32_to_cpu(node.i.dsize)+je32_to_cpu(node.i.offset) == je32_to_cpu(node.i.isize)))) {
++ D1(printk(KERN_DEBUG "Marking node at 0x%08x REF_PRISTINE\n", ref_offset(ref)));
++ ref->flash_offset = ref_offset(ref) | REF_PRISTINE;
++ } else {
++ D1(printk(KERN_DEBUG "Marking node at 0x%08x REF_NORMAL\n", ref_offset(ref)));
++ ref->flash_offset = ref_offset(ref) | REF_NORMAL;
++ }
++ spin_unlock(&c->erase_completion_lock);
++ }
++
+ tn = jffs2_alloc_tmp_dnode_info();
+ if (!tn) {
+ D1(printk(KERN_DEBUG "alloc tn failed\n"));
+@@ -225,36 +363,76 @@
+ jffs2_free_tmp_dnode_info(tn);
+ goto free_out;
+ }
+- tn->version = node.i.version;
+- tn->fn->ofs = node.i.offset;
++ tn->version = je32_to_cpu(node.i.version);
++ tn->fn->ofs = je32_to_cpu(node.i.offset);
+ /* There was a bug where we wrote hole nodes out with
+ csize/dsize swapped. Deal with it */
+- if (node.i.compr == JFFS2_COMPR_ZERO && !node.i.dsize && node.i.csize)
+- tn->fn->size = node.i.csize;
++ if (node.i.compr == JFFS2_COMPR_ZERO && !je32_to_cpu(node.i.dsize) && je32_to_cpu(node.i.csize))
++ tn->fn->size = je32_to_cpu(node.i.csize);
+ else // normal case...
+- tn->fn->size = node.i.dsize;
++ tn->fn->size = je32_to_cpu(node.i.dsize);
+ tn->fn->raw = ref;
+- D1(printk(KERN_DEBUG "dnode @%08x: ver %u, offset %04x, dsize %04x\n", ref->flash_offset &~3, node.i.version, node.i.offset, node.i.dsize));
++ D1(printk(KERN_DEBUG "dnode @%08x: ver %u, offset %04x, dsize %04x\n",
++ ref_offset(ref), je32_to_cpu(node.i.version),
++ je32_to_cpu(node.i.offset), je32_to_cpu(node.i.dsize)));
+ jffs2_add_tn_to_list(tn, &ret_tn);
+ break;
+
+ default:
+- switch(node.u.nodetype & JFFS2_COMPAT_MASK) {
++ if (ref_flags(ref) == REF_UNCHECKED) {
++ struct jffs2_eraseblock *jeb;
++ uint32_t len;
++
++ printk(KERN_ERR "Eep. Unknown node type %04x at %08x was marked REF_UNCHECKED\n",
++ je16_to_cpu(node.u.nodetype), ref_offset(ref));
++
++ /* Mark the node as having been checked and fix the accounting accordingly */
++ spin_lock(&c->erase_completion_lock);
++ jeb = &c->blocks[ref->flash_offset / c->sector_size];
++ len = ref_totlen(c, jeb, ref);
++
++ jeb->used_size += len;
++ jeb->unchecked_size -= len;
++ c->used_size += len;
++ c->unchecked_size -= len;
++
++ mark_ref_normal(ref);
++ spin_unlock(&c->erase_completion_lock);
++ }
++ node.u.nodetype = cpu_to_je16(JFFS2_NODE_ACCURATE | je16_to_cpu(node.u.nodetype));
++ if (crc32(0, &node, sizeof(struct jffs2_unknown_node)-4) != je32_to_cpu(node.u.hdr_crc)) {
++ /* Hmmm. This should have been caught at scan time. */
++ printk(KERN_ERR "Node header CRC failed at %08x. But it must have been OK earlier.\n",
++ ref_offset(ref));
++ printk(KERN_ERR "Node was: { %04x, %04x, %08x, %08x }\n",
++ je16_to_cpu(node.u.magic), je16_to_cpu(node.u.nodetype), je32_to_cpu(node.u.totlen),
++ je32_to_cpu(node.u.hdr_crc));
++ jffs2_mark_node_obsolete(c, ref);
++ } else switch(je16_to_cpu(node.u.nodetype) & JFFS2_COMPAT_MASK) {
+ case JFFS2_FEATURE_INCOMPAT:
+- printk(KERN_NOTICE "Unknown INCOMPAT nodetype %04X at %08X\n", node.u.nodetype, ref->flash_offset & ~3);
++ printk(KERN_NOTICE "Unknown INCOMPAT nodetype %04X at %08x\n", je16_to_cpu(node.u.nodetype), ref_offset(ref));
++ /* EEP */
++ BUG();
+ break;
+ case JFFS2_FEATURE_ROCOMPAT:
+- printk(KERN_NOTICE "Unknown ROCOMPAT nodetype %04X at %08X\n", node.u.nodetype, ref->flash_offset & ~3);
++ printk(KERN_NOTICE "Unknown ROCOMPAT nodetype %04X at %08x\n", je16_to_cpu(node.u.nodetype), ref_offset(ref));
++ if (!(c->flags & JFFS2_SB_FLAG_RO))
++ BUG();
+ break;
+ case JFFS2_FEATURE_RWCOMPAT_COPY:
+- printk(KERN_NOTICE "Unknown RWCOMPAT_COPY nodetype %04X at %08X\n", node.u.nodetype, ref->flash_offset & ~3);
++ printk(KERN_NOTICE "Unknown RWCOMPAT_COPY nodetype %04X at %08x\n", je16_to_cpu(node.u.nodetype), ref_offset(ref));
+ break;
+ case JFFS2_FEATURE_RWCOMPAT_DELETE:
+- printk(KERN_NOTICE "Unknown RWCOMPAT_DELETE nodetype %04X at %08X\n", node.u.nodetype, ref->flash_offset & ~3);
++ printk(KERN_NOTICE "Unknown RWCOMPAT_DELETE nodetype %04X at %08x\n", je16_to_cpu(node.u.nodetype), ref_offset(ref));
++ jffs2_mark_node_obsolete(c, ref);
+ break;
+ }
++
+ }
++ spin_lock(&c->erase_completion_lock);
++
+ }
++ spin_unlock(&c->erase_completion_lock);
+ *tnp = ret_tn;
+ *fdp = ret_fd;
+
+@@ -266,19 +444,30 @@
+ return err;
+ }
+
++void jffs2_set_inocache_state(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic, int state)
++{
++ spin_lock(&c->inocache_lock);
++ ic->state = state;
++ wake_up(&c->inocache_wq);
++ spin_unlock(&c->inocache_lock);
++}
++
++/* During mount, this needs no locking. During normal operation, its
++ callers want to do other stuff while still holding the inocache_lock.
++ Rather than introducing special case get_ino_cache functions or
++ callbacks, we just let the caller do the locking itself. */
++
+ struct jffs2_inode_cache *jffs2_get_ino_cache(struct jffs2_sb_info *c, uint32_t ino)
+ {
+ struct jffs2_inode_cache *ret;
+
+ D2(printk(KERN_DEBUG "jffs2_get_ino_cache(): ino %u\n", ino));
+- spin_lock (&c->inocache_lock);
++
+ ret = c->inocache_list[ino % INOCACHE_HASHSIZE];
+ while (ret && ret->ino < ino) {
+ ret = ret->next;
+ }
+
+- spin_unlock(&c->inocache_lock);
+-
+ if (ret && ret->ino != ino)
+ ret = NULL;
+
+@@ -299,6 +488,7 @@
+ }
+ new->next = *prev;
+ *prev = new;
++
+ spin_unlock(&c->inocache_lock);
+ }
+
+@@ -316,6 +506,7 @@
+ if ((*prev) == old) {
+ *prev = old->next;
+ }
++
+ spin_unlock(&c->inocache_lock);
+ }
+
+@@ -352,3 +543,128 @@
+ }
+ }
+
++struct jffs2_node_frag *jffs2_lookup_node_frag(struct rb_root *fragtree, uint32_t offset)
++{
++ /* The common case in lookup is that there will be a node
++ which precisely matches. So we go looking for that first */
++ struct rb_node *next;
++ struct jffs2_node_frag *prev = NULL;
++ struct jffs2_node_frag *frag = NULL;
++
++ D2(printk(KERN_DEBUG "jffs2_lookup_node_frag(%p, %d)\n", fragtree, offset));
++
++ next = fragtree->rb_node;
++
++ while(next) {
++ frag = rb_entry(next, struct jffs2_node_frag, rb);
++
++ D2(printk(KERN_DEBUG "Considering frag %d-%d (%p). left %p, right %p\n",
++ frag->ofs, frag->ofs+frag->size, frag, frag->rb.rb_left, frag->rb.rb_right));
++ if (frag->ofs + frag->size <= offset) {
++ D2(printk(KERN_DEBUG "Going right from frag %d-%d, before the region we care about\n",
++ frag->ofs, frag->ofs+frag->size));
++ /* Remember the closest smaller match on the way down */
++ if (!prev || frag->ofs > prev->ofs)
++ prev = frag;
++ next = frag->rb.rb_right;
++ } else if (frag->ofs > offset) {
++ D2(printk(KERN_DEBUG "Going left from frag %d-%d, after the region we care about\n",
++ frag->ofs, frag->ofs+frag->size));
++ next = frag->rb.rb_left;
++ } else {
++ D2(printk(KERN_DEBUG "Returning frag %d,%d, matched\n",
++ frag->ofs, frag->ofs+frag->size));
++ return frag;
++ }
++ }
++
++ /* Exact match not found. Go back up looking at each parent,
++ and return the closest smaller one */
++
++ if (prev)
++ D2(printk(KERN_DEBUG "No match. Returning frag %d,%d, closest previous\n",
++ prev->ofs, prev->ofs+prev->size));
++ else
++ D2(printk(KERN_DEBUG "Returning NULL, empty fragtree\n"));
++
++ return prev;
++}
++
++/* Pass 'c' argument to indicate that nodes should be marked obsolete as
++ they're killed. */
++void jffs2_kill_fragtree(struct rb_root *root, struct jffs2_sb_info *c)
++{
++ struct jffs2_node_frag *frag;
++ struct jffs2_node_frag *parent;
++
++ if (!root->rb_node)
++ return;
++
++ frag = (rb_entry(root->rb_node, struct jffs2_node_frag, rb));
++
++ while(frag) {
++ if (frag->rb.rb_left) {
++ D2(printk(KERN_DEBUG "Going left from frag (%p) %d-%d\n",
++ frag, frag->ofs, frag->ofs+frag->size));
++ frag = frag_left(frag);
++ continue;
++ }
++ if (frag->rb.rb_right) {
++ D2(printk(KERN_DEBUG "Going right from frag (%p) %d-%d\n",
++ frag, frag->ofs, frag->ofs+frag->size));
++ frag = frag_right(frag);
++ continue;
++ }
++
++ D2(printk(KERN_DEBUG "jffs2_kill_fragtree: frag at 0x%x-0x%x: node %p, frags %d--\n",
++ frag->ofs, frag->ofs+frag->size, frag->node,
++ frag->node?frag->node->frags:0));
++
++ if (frag->node && !(--frag->node->frags)) {
++ /* Not a hole, and it's the final remaining frag
++ of this node. Free the node */
++ if (c)
++ jffs2_mark_node_obsolete(c, frag->node->raw);
++
++ jffs2_free_full_dnode(frag->node);
++ }
++ parent = frag_parent(frag);
++ if (parent) {
++ if (frag_left(parent) == frag)
++ parent->rb.rb_left = NULL;
++ else
++ parent->rb.rb_right = NULL;
++ }
++
++ jffs2_free_node_frag(frag);
++ frag = parent;
++
++ cond_resched();
++ }
++}
++
++void jffs2_fragtree_insert(struct jffs2_node_frag *newfrag, struct jffs2_node_frag *base)
++{
++ struct rb_node *parent = &base->rb;
++ struct rb_node **link = &parent;
++
++ D2(printk(KERN_DEBUG "jffs2_fragtree_insert(%p; %d-%d, %p)\n", newfrag,
++ newfrag->ofs, newfrag->ofs+newfrag->size, base));
++
++ while (*link) {
++ parent = *link;
++ base = rb_entry(parent, struct jffs2_node_frag, rb);
++
++ D2(printk(KERN_DEBUG "fragtree_insert considering frag at 0x%x\n", base->ofs));
++ if (newfrag->ofs > base->ofs)
++ link = &base->rb.rb_right;
++ else if (newfrag->ofs < base->ofs)
++ link = &base->rb.rb_left;
++ else {
++ printk(KERN_CRIT "Duplicate frag at %08x (%p,%p)\n", newfrag->ofs, newfrag, base);
++ BUG();
++ }
++ }
++
++ rb_link_node(&newfrag->rb, &base->rb, link);
++}
+diff -Nurb linux-mips-2.4.27/fs/jffs2/nodelist.h linux/fs/jffs2/nodelist.h
+--- linux-mips-2.4.27/fs/jffs2/nodelist.h 2003-11-17 02:07:44.000000000 +0100
++++ linux/fs/jffs2/nodelist.h 2004-11-19 10:25:12.113167264 +0100
+@@ -1,48 +1,35 @@
+ /*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+- * Copyright (C) 2001 Red Hat, Inc.
++ * Copyright (C) 2001-2003 Red Hat, Inc.
+ *
+- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
++ * Created by David Woodhouse <dwmw2@redhat.com>
+ *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
++ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+- * The contents of this file are subject to the Red Hat eCos Public
+- * License Version 1.1 (the "Licence"); you may not use this file
+- * except in compliance with the Licence. You may obtain a copy of
+- * the Licence at http://www.redhat.com/
+- *
+- * Software distributed under the Licence is distributed on an "AS IS"
+- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+- * See the Licence for the specific language governing rights and
+- * limitations under the Licence.
+- *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
+- *
+- * Alternatively, the contents of this file may be used under the
+- * terms of the GNU General Public License version 2 (the "GPL"), in
+- * which case the provisions of the GPL are applicable instead of the
+- * above. If you wish to allow the use of your version of this file
+- * only under the terms of the GPL and not to allow others to use your
+- * version of this file under the RHEPL, indicate your decision by
+- * deleting the provisions above and replace them with the notice and
+- * other provisions required by the GPL. If you do not delete the
+- * provisions above, a recipient may use your version of this file
+- * under either the RHEPL or the GPL.
+- *
+- * $Id$
++ * $Id$
+ *
+ */
+
++#ifndef __JFFS2_NODELIST_H__
++#define __JFFS2_NODELIST_H__
++
+ #include <linux/config.h>
+ #include <linux/fs.h>
+-
++#include <linux/types.h>
++#include <linux/jffs2.h>
+ #include <linux/jffs2_fs_sb.h>
+ #include <linux/jffs2_fs_i.h>
+
++#ifdef __ECOS
++#include "os-ecos.h"
++#else
++#include <linux/mtd/compatmac.h> /* For min/max in older kernels */
++#include "os-linux.h"
++#endif
++
+ #ifndef CONFIG_JFFS2_FS_DEBUG
+-#define CONFIG_JFFS2_FS_DEBUG 2
++#define CONFIG_JFFS2_FS_DEBUG 1
+ #endif
+
+ #if CONFIG_JFFS2_FS_DEBUG > 0
+@@ -71,17 +58,21 @@
+ for this inode instead. The inode_cache will have NULL in the first
+ word so you know when you've got there :) */
+ struct jffs2_raw_node_ref *next_phys;
+- // __u32 ino;
+- __u32 flash_offset;
+- __u32 totlen;
+-// __u16 nodetype;
++ uint32_t flash_offset;
++ uint32_t __totlen; /* This may die; use ref_totlen(c, jeb, ) below */
++};
+
+ /* flash_offset & 3 always has to be zero, because nodes are
+ always aligned at 4 bytes. So we have a couple of extra bits
+- to play with. So we set the least significant bit to 1 to
+- signify that the node is obsoleted by later nodes.
+- */
+-};
++ to play with, which indicate the node's status; see below: */
++#define REF_UNCHECKED 0 /* We haven't yet checked the CRC or built its inode */
++#define REF_OBSOLETE 1 /* Obsolete, can be completely ignored */
++#define REF_PRISTINE 2 /* Completely clean. GC without looking */
++#define REF_NORMAL 3 /* Possibly overlapped. Read the page and write again on GC */
++#define ref_flags(ref) ((ref)->flash_offset & 3)
++#define ref_offset(ref) ((ref)->flash_offset & ~3)
++#define ref_obsolete(ref) (((ref)->flash_offset & 3) == REF_OBSOLETE)
++#define mark_ref_normal(ref) do { (ref)->flash_offset = ref_offset(ref) | REF_NORMAL; } while(0)
+
+ /*
+ Used for keeping track of deletion nodes &c, which can only be marked
+@@ -101,19 +92,35 @@
+ a pointer to the first physical node which is part of this inode, too.
+ */
+ struct jffs2_inode_cache {
+- struct jffs2_scan_info *scan; /* Used during scan to hold
+- temporary lists of nodes, and later must be set to
++ struct jffs2_full_dirent *scan_dents; /* Used during scan to hold
++ temporary lists of dirents, and later must be set to
+ NULL to mark the end of the raw_node_ref->next_in_ino
+ chain. */
+ struct jffs2_inode_cache *next;
+ struct jffs2_raw_node_ref *nodes;
+- __u32 ino;
++ uint32_t ino;
+ int nlink;
++ int state;
+ };
+
++/* Inode states for 'state' above. We need the 'GC' state to prevent
++ someone from doing a read_inode() while we're moving a 'REF_PRISTINE'
++ node without going through all the iget() nonsense */
++#define INO_STATE_UNCHECKED 0 /* CRC checks not yet done */
++#define INO_STATE_CHECKING 1 /* CRC checks in progress */
++#define INO_STATE_PRESENT 2 /* In core */
++#define INO_STATE_CHECKEDABSENT 3 /* Checked, cleared again */
++#define INO_STATE_GC 4 /* GCing a 'pristine' node */
++#define INO_STATE_READING 5 /* In read_inode() */
++
++#define INOCACHE_HASHSIZE 128
++
+ struct jffs2_scan_info {
+ struct jffs2_full_dirent *dents;
+ struct jffs2_tmp_dnode_info *tmpnodes;
++ /* Latest i_size info */
++ uint32_t version;
++ uint32_t isize;
+ };
+ /*
+ Larger representation of a raw node, kept in-core only when the
+@@ -123,9 +130,9 @@
+ struct jffs2_full_dnode
+ {
+ struct jffs2_raw_node_ref *raw;
+- __u32 ofs; /* Don't really need this, but optimisation */
+- __u32 size;
+- __u32 frags; /* Number of fragments which currently refer
++ uint32_t ofs; /* Don't really need this, but optimisation */
++ uint32_t size;
++ uint32_t frags; /* Number of fragments which currently refer
+ to this node. When this reaches zero,
+ the node is obsolete.
+ */
+@@ -140,15 +147,15 @@
+ {
+ struct jffs2_tmp_dnode_info *next;
+ struct jffs2_full_dnode *fn;
+- __u32 version;
++ uint32_t version;
+ };
+
+ struct jffs2_full_dirent
+ {
+ struct jffs2_raw_node_ref *raw;
+ struct jffs2_full_dirent *next;
+- __u32 version;
+- __u32 ino; /* == zero for unlink */
++ uint32_t version;
++ uint32_t ino; /* == zero for unlink */
+ unsigned int nhash;
+ unsigned char type;
+ unsigned char name[0];
+@@ -159,21 +166,23 @@
+ */
+ struct jffs2_node_frag
+ {
+- struct jffs2_node_frag *next;
++ struct rb_node rb;
+ struct jffs2_full_dnode *node; /* NULL for holes */
+- __u32 size;
+- __u32 ofs; /* Don't really need this, but optimisation */
++ uint32_t size;
++ uint32_t ofs; /* Don't really need this, but optimisation */
+ };
+
+ struct jffs2_eraseblock
+ {
+ struct list_head list;
+ int bad_count;
+- __u32 offset; /* of this block in the MTD */
++ uint32_t offset; /* of this block in the MTD */
+
+- __u32 used_size;
+- __u32 dirty_size;
+- __u32 free_size; /* Note that sector_size - free_size
++ uint32_t unchecked_size;
++ uint32_t used_size;
++ uint32_t dirty_size;
++ uint32_t wasted_size;
++ uint32_t free_size; /* Note that sector_size - free_size
+ is the address of the first free space */
+ struct jffs2_raw_node_ref *first_node;
+ struct jffs2_raw_node_ref *last_node;
+@@ -190,45 +199,134 @@
+ };
+
+ #define ACCT_SANITY_CHECK(c, jeb) do { \
+- if (jeb->used_size + jeb->dirty_size + jeb->free_size != c->sector_size) { \
+- printk(KERN_NOTICE "Eeep. Space accounting for block at 0x%08x is screwed\n", jeb->offset); \
+- printk(KERN_NOTICE "free 0x%08x + dirty 0x%08x + used %08x != total %08x\n", \
+- jeb->free_size, jeb->dirty_size, jeb->used_size, c->sector_size); \
++ struct jffs2_eraseblock *___j = jeb; \
++ if ((___j) && ___j->used_size + ___j->dirty_size + ___j->free_size + ___j->wasted_size + ___j->unchecked_size != c->sector_size) { \
++ printk(KERN_NOTICE "Eeep. Space accounting for block at 0x%08x is screwed\n", ___j->offset); \
++ printk(KERN_NOTICE "free 0x%08x + dirty 0x%08x + used %08x + wasted %08x + unchecked %08x != total %08x\n", \
++ ___j->free_size, ___j->dirty_size, ___j->used_size, ___j->wasted_size, ___j->unchecked_size, c->sector_size); \
+ BUG(); \
+ } \
+- if (c->used_size + c->dirty_size + c->free_size + c->erasing_size + c->bad_size != c->flash_size) { \
++ if (c->used_size + c->dirty_size + c->free_size + c->erasing_size + c->bad_size + c->wasted_size + c->unchecked_size != c->flash_size) { \
+ printk(KERN_NOTICE "Eeep. Space accounting superblock info is screwed\n"); \
+- printk(KERN_NOTICE "free 0x%08x + dirty 0x%08x + used %08x + erasing %08x + bad %08x != total %08x\n", \
+- c->free_size, c->dirty_size, c->used_size, c->erasing_size, c->bad_size, c->flash_size); \
++ printk(KERN_NOTICE "free 0x%08x + dirty 0x%08x + used %08x + erasing %08x + bad %08x + wasted %08x + unchecked %08x != total %08x\n", \
++ c->free_size, c->dirty_size, c->used_size, c->erasing_size, c->bad_size, c->wasted_size, c->unchecked_size, c->flash_size); \
+ BUG(); \
+ } \
+ } while(0)
+
++static inline void paranoia_failed_dump(struct jffs2_eraseblock *jeb)
++{
++ struct jffs2_raw_node_ref *ref;
++ int i=0;
++
++ printk(KERN_NOTICE);
++ for (ref = jeb->first_node; ref; ref = ref->next_phys) {
++ printk("%08x->", ref_offset(ref));
++ if (++i == 8) {
++ i = 0;
++ printk("\n" KERN_NOTICE);
++ }
++ }
++ printk("\n");
++}
++
++
+ #define ACCT_PARANOIA_CHECK(jeb) do { \
+- __u32 my_used_size = 0; \
++ uint32_t my_used_size = 0; \
++ uint32_t my_unchecked_size = 0; \
+ struct jffs2_raw_node_ref *ref2 = jeb->first_node; \
+ while (ref2) { \
+- if (!(ref2->flash_offset & 1)) \
+- my_used_size += ref2->totlen; \
++ if (unlikely(ref2->flash_offset < jeb->offset || \
++ ref2->flash_offset > jeb->offset + c->sector_size)) { \
++ printk(KERN_NOTICE "Node %08x shouldn't be in block at %08x!\n", \
++ ref_offset(ref2), jeb->offset); \
++ paranoia_failed_dump(jeb); \
++ BUG(); \
++ } \
++ if (ref_flags(ref2) == REF_UNCHECKED) \
++ my_unchecked_size += ref_totlen(c, jeb, ref2); \
++ else if (!ref_obsolete(ref2)) \
++ my_used_size += ref_totlen(c, jeb, ref2); \
++ if (unlikely((!ref2->next_phys) != (ref2 == jeb->last_node))) { \
++ printk("ref for node at %p (phys %08x) has next_phys->%p (%08x), last_node->%p (phys %08x)\n", \
++ ref2, ref_offset(ref2), ref2->next_phys, ref_offset(ref2->next_phys), \
++ jeb->last_node, ref_offset(jeb->last_node)); \
++ paranoia_failed_dump(jeb); \
++ BUG(); \
++ } \
+ ref2 = ref2->next_phys; \
+ } \
+ if (my_used_size != jeb->used_size) { \
+ printk(KERN_NOTICE "Calculated used size %08x != stored used size %08x\n", my_used_size, jeb->used_size); \
+ BUG(); \
+ } \
++ if (my_unchecked_size != jeb->unchecked_size) { \
++ printk(KERN_NOTICE "Calculated unchecked size %08x != stored unchecked size %08x\n", my_unchecked_size, jeb->unchecked_size); \
++ BUG(); \
++ } \
+ } while(0)
+
++/* Calculate totlen from surrounding nodes or eraseblock */
++static inline uint32_t __ref_totlen(struct jffs2_sb_info *c,
++ struct jffs2_eraseblock *jeb,
++ struct jffs2_raw_node_ref *ref)
++{
++ uint32_t ref_end;
++
++ if (ref->next_phys)
++ ref_end = ref_offset(ref->next_phys);
++ else {
++ if (!jeb)
++ jeb = &c->blocks[ref->flash_offset / c->sector_size];
++
++ /* Last node in block. Use free_space */
++ BUG_ON(ref != jeb->last_node);
++ ref_end = jeb->offset + c->sector_size - jeb->free_size;
++ }
++ return ref_end - ref_offset(ref);
++}
++
++static inline uint32_t ref_totlen(struct jffs2_sb_info *c,
++ struct jffs2_eraseblock *jeb,
++ struct jffs2_raw_node_ref *ref)
++{
++ uint32_t ret;
++
++ D1(if (jeb && jeb != &c->blocks[ref->flash_offset / c->sector_size]) {
++ printk(KERN_CRIT "ref_totlen called with wrong block -- at 0x%08x instead of 0x%08x; ref 0x%08x\n",
++ jeb->offset, c->blocks[ref->flash_offset / c->sector_size].offset, ref_offset(ref));
++ BUG();
++ })
++
++#if 1
++ ret = ref->__totlen;
++#else
++ /* This doesn't actually work yet */
++ ret = __ref_totlen(c, jeb, ref);
++ if (ret != ref->__totlen) {
++ printk(KERN_CRIT "Totlen for ref at %p (0x%08x-0x%08x) miscalculated as 0x%x instead of %x\n",
++ ref, ref_offset(ref), ref_offset(ref)+ref->__totlen,
++ ret, ref->__totlen);
++ if (!jeb)
++ jeb = &c->blocks[ref->flash_offset / c->sector_size];
++ paranoia_failed_dump(jeb);
++ BUG();
++ }
++#endif
++ return ret;
++}
++
++
+ #define ALLOC_NORMAL 0 /* Normal allocation */
+ #define ALLOC_DELETION 1 /* Deletion node. Best to allow it */
+ #define ALLOC_GC 2 /* Space requested for GC. Give it or die */
++#define ALLOC_NORETRY 3 /* For jffs2_write_dnode: On failure, return -EAGAIN instead of retrying */
+
+-#define JFFS2_RESERVED_BLOCKS_BASE 3 /* Number of free blocks there must be before we... */
+-#define JFFS2_RESERVED_BLOCKS_WRITE (JFFS2_RESERVED_BLOCKS_BASE + 2) /* ... allow a normal filesystem write */
+-#define JFFS2_RESERVED_BLOCKS_DELETION (JFFS2_RESERVED_BLOCKS_BASE + 1) /* ... allow a normal filesystem deletion */
+-#define JFFS2_RESERVED_BLOCKS_GCTRIGGER (JFFS2_RESERVED_BLOCKS_BASE + 3) /* ... wake up the GC thread */
+-#define JFFS2_RESERVED_BLOCKS_GCBAD (JFFS2_RESERVED_BLOCKS_BASE + 1) /* ... pick a block from the bad_list to GC */
+-#define JFFS2_RESERVED_BLOCKS_GCMERGE (JFFS2_RESERVED_BLOCKS_BASE) /* ... merge pages when garbage collecting */
++/* How much dirty space before it goes on the very_dirty_list */
++#define VERYDIRTY(c, size) ((size) >= ((c)->sector_size / 2))
+
++/* check if dirty space is more than 255 Byte */
++#define ISDIRTY(size) ((size) > sizeof (struct jffs2_raw_inode) + JFFS2_MIN_DATA_LEN)
+
+ #define PAD(x) (((x)+3)&~3)
+
+@@ -241,43 +339,75 @@
+ return ((struct jffs2_inode_cache *)raw);
+ }
+
++static inline struct jffs2_node_frag *frag_first(struct rb_root *root)
++{
++ struct rb_node *node = root->rb_node;
++
++ if (!node)
++ return NULL;
++ while(node->rb_left)
++ node = node->rb_left;
++ return rb_entry(node, struct jffs2_node_frag, rb);
++}
++#define rb_parent(rb) ((rb)->rb_parent)
++#define frag_next(frag) rb_entry(rb_next(&(frag)->rb), struct jffs2_node_frag, rb)
++#define frag_prev(frag) rb_entry(rb_prev(&(frag)->rb), struct jffs2_node_frag, rb)
++#define frag_parent(frag) rb_entry(rb_parent(&(frag)->rb), struct jffs2_node_frag, rb)
++#define frag_left(frag) rb_entry((frag)->rb.rb_left, struct jffs2_node_frag, rb)
++#define frag_right(frag) rb_entry((frag)->rb.rb_right, struct jffs2_node_frag, rb)
++#define frag_erase(frag, list) rb_erase(&frag->rb, list);
++
+ /* nodelist.c */
+ D1(void jffs2_print_frag_list(struct jffs2_inode_info *f));
+ void jffs2_add_fd_to_list(struct jffs2_sb_info *c, struct jffs2_full_dirent *new, struct jffs2_full_dirent **list);
+-void jffs2_add_tn_to_list(struct jffs2_tmp_dnode_info *tn, struct jffs2_tmp_dnode_info **list);
+ int jffs2_get_inode_nodes(struct jffs2_sb_info *c, ino_t ino, struct jffs2_inode_info *f,
+ struct jffs2_tmp_dnode_info **tnp, struct jffs2_full_dirent **fdp,
+- __u32 *highest_version, __u32 *latest_mctime,
+- __u32 *mctime_ver);
++ uint32_t *highest_version, uint32_t *latest_mctime,
++ uint32_t *mctime_ver);
++void jffs2_set_inocache_state(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic, int state);
+ struct jffs2_inode_cache *jffs2_get_ino_cache(struct jffs2_sb_info *c, uint32_t ino);
+ void jffs2_add_ino_cache (struct jffs2_sb_info *c, struct jffs2_inode_cache *new);
+ void jffs2_del_ino_cache(struct jffs2_sb_info *c, struct jffs2_inode_cache *old);
+ void jffs2_free_ino_caches(struct jffs2_sb_info *c);
+ void jffs2_free_raw_node_refs(struct jffs2_sb_info *c);
++struct jffs2_node_frag *jffs2_lookup_node_frag(struct rb_root *fragtree, uint32_t offset);
++void jffs2_kill_fragtree(struct rb_root *root, struct jffs2_sb_info *c_delete);
++void jffs2_fragtree_insert(struct jffs2_node_frag *newfrag, struct jffs2_node_frag *base);
++struct rb_node *rb_next(struct rb_node *);
++struct rb_node *rb_prev(struct rb_node *);
++void rb_replace_node(struct rb_node *victim, struct rb_node *new, struct rb_root *root);
+
+ /* nodemgmt.c */
+-int jffs2_reserve_space(struct jffs2_sb_info *c, __u32 minsize, __u32 *ofs, __u32 *len, int prio);
+-int jffs2_reserve_space_gc(struct jffs2_sb_info *c, __u32 minsize, __u32 *ofs, __u32 *len);
+-int jffs2_add_physical_node_ref(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *new, __u32 len, int dirty);
++int jffs2_thread_should_wake(struct jffs2_sb_info *c);
++int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len, int prio);
++int jffs2_reserve_space_gc(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len);
++int jffs2_add_physical_node_ref(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *new);
+ void jffs2_complete_reservation(struct jffs2_sb_info *c);
+ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *raw);
++void jffs2_dump_block_lists(struct jffs2_sb_info *c);
+
+ /* write.c */
+-struct inode *jffs2_new_inode (struct inode *dir_i, int mode, struct jffs2_raw_inode *ri);
+-struct jffs2_full_dnode *jffs2_write_dnode(struct inode *inode, struct jffs2_raw_inode *ri, const unsigned char *data, __u32 datalen, __u32 flash_ofs, __u32 *writelen);
+-struct jffs2_full_dirent *jffs2_write_dirent(struct inode *inode, struct jffs2_raw_dirent *rd, const unsigned char *name, __u32 namelen, __u32 flash_ofs, __u32 *writelen);
++int jffs2_do_new_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, uint32_t mode, struct jffs2_raw_inode *ri);
++
++struct jffs2_full_dnode *jffs2_write_dnode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_inode *ri, const unsigned char *data, uint32_t datalen, uint32_t flash_ofs, int alloc_mode);
++struct jffs2_full_dirent *jffs2_write_dirent(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_dirent *rd, const unsigned char *name, uint32_t namelen, uint32_t flash_ofs, int alloc_mode);
++int jffs2_write_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
++ struct jffs2_raw_inode *ri, unsigned char *buf,
++ uint32_t offset, uint32_t writelen, uint32_t *retlen);
++int jffs2_do_create(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, struct jffs2_inode_info *f, struct jffs2_raw_inode *ri, const char *name, int namelen);
++int jffs2_do_unlink(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, const char *name, int namelen, struct jffs2_inode_info *dead_f);
++int jffs2_do_link (struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, uint32_t ino, uint8_t type, const char *name, int namelen);
++
+
+ /* readinode.c */
+-void jffs2_truncate_fraglist (struct jffs2_sb_info *c, struct jffs2_node_frag **list, __u32 size);
+-int jffs2_add_full_dnode_to_fraglist(struct jffs2_sb_info *c, struct jffs2_node_frag **list, struct jffs2_full_dnode *fn);
++void jffs2_truncate_fraglist (struct jffs2_sb_info *c, struct rb_root *list, uint32_t size);
+ int jffs2_add_full_dnode_to_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_full_dnode *fn);
+-void jffs2_read_inode (struct inode *);
+-void jffs2_clear_inode (struct inode *);
++int jffs2_do_read_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
++ uint32_t ino, struct jffs2_raw_inode *latest_node);
++int jffs2_do_crccheck_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic);
++void jffs2_do_clear_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f);
+
+ /* malloc.c */
+-void jffs2_free_tmp_dnode_info_list(struct jffs2_tmp_dnode_info *tn);
+-void jffs2_free_full_dirent_list(struct jffs2_full_dirent *fd);
+-
+ int jffs2_create_slab_caches(void);
+ void jffs2_destroy_slab_caches(void);
+
+@@ -301,54 +431,41 @@
+ /* gc.c */
+ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c);
+
+-/* background.c */
+-int jffs2_start_garbage_collect_thread(struct jffs2_sb_info *c);
+-void jffs2_stop_garbage_collect_thread(struct jffs2_sb_info *c);
+-void jffs2_garbage_collect_trigger(struct jffs2_sb_info *c);
+-
+-/* dir.c */
+-extern struct file_operations jffs2_dir_operations;
+-extern struct inode_operations jffs2_dir_inode_operations;
+-
+-/* file.c */
+-extern struct file_operations jffs2_file_operations;
+-extern struct inode_operations jffs2_file_inode_operations;
+-extern struct address_space_operations jffs2_file_address_operations;
+-int jffs2_null_fsync(struct file *, struct dentry *, int);
+-int jffs2_setattr (struct dentry *dentry, struct iattr *iattr);
+-int jffs2_do_readpage_nolock (struct inode *inode, struct page *pg);
+-int jffs2_do_readpage_unlock (struct inode *inode, struct page *pg);
+-int jffs2_readpage (struct file *, struct page *);
+-int jffs2_prepare_write (struct file *, struct page *, unsigned, unsigned);
+-int jffs2_commit_write (struct file *, struct page *, unsigned, unsigned);
+-
+-/* ioctl.c */
+-int jffs2_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
+-
+ /* read.c */
+ int jffs2_read_dnode(struct jffs2_sb_info *c, struct jffs2_full_dnode *fd, unsigned char *buf, int ofs, int len);
++int jffs2_read_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
++ unsigned char *buf, uint32_t offset, uint32_t len);
++char *jffs2_getlink(struct jffs2_sb_info *c, struct jffs2_inode_info *f);
+
+ /* compr.c */
+-unsigned char jffs2_compress(unsigned char *data_in, unsigned char *cpage_out,
+- __u32 *datalen, __u32 *cdatalen);
++unsigned char jffs2_compress(unsigned char *data_in, unsigned char **cpage_out,
++ uint32_t *datalen, uint32_t *cdatalen);
++void jffs2_free_comprbuf(unsigned char *comprbuf, unsigned char *orig);
+ int jffs2_decompress(unsigned char comprtype, unsigned char *cdata_in,
+- unsigned char *data_out, __u32 cdatalen, __u32 datalen);
++ unsigned char *data_out, uint32_t cdatalen, uint32_t datalen);
+
+ /* scan.c */
+ int jffs2_scan_medium(struct jffs2_sb_info *c);
++void jffs2_rotate_lists(struct jffs2_sb_info *c);
+
+ /* build.c */
+-int jffs2_build_filesystem(struct jffs2_sb_info *c);
+-
+-/* symlink.c */
+-extern struct inode_operations jffs2_symlink_inode_operations;
++int jffs2_do_mount_fs(struct jffs2_sb_info *c);
+
+ /* erase.c */
+ void jffs2_erase_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
+-void jffs2_erase_pending_blocks(struct jffs2_sb_info *c);
+-void jffs2_mark_erased_blocks(struct jffs2_sb_info *c);
+-void jffs2_erase_pending_trigger(struct jffs2_sb_info *c);
++void jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count);
++
++#ifdef CONFIG_JFFS2_FS_NAND
++/* wbuf.c */
++int jffs2_flush_wbuf_gc(struct jffs2_sb_info *c, uint32_t ino);
++int jffs2_flush_wbuf_pad(struct jffs2_sb_info *c);
++int jffs2_check_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
++int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
++int jffs2_nand_read_failcnt(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
++#endif
+
+ /* compr_zlib.c */
+ int jffs2_zlib_init(void);
+ void jffs2_zlib_exit(void);
++
++#endif /* __JFFS2_NODELIST_H__ */
+diff -Nurb linux-mips-2.4.27/fs/jffs2/nodemgmt.c linux/fs/jffs2/nodemgmt.c
+--- linux-mips-2.4.27/fs/jffs2/nodemgmt.c 2002-06-27 00:36:20.000000000 +0200
++++ linux/fs/jffs2/nodemgmt.c 2004-11-19 10:25:12.115166960 +0100
+@@ -1,45 +1,21 @@
+ /*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+- * Copyright (C) 2001 Red Hat, Inc.
++ * Copyright (C) 2001-2003 Red Hat, Inc.
+ *
+- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
++ * Created by David Woodhouse <dwmw2@redhat.com>
+ *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
++ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+- * The contents of this file are subject to the Red Hat eCos Public
+- * License Version 1.1 (the "Licence"); you may not use this file
+- * except in compliance with the Licence. You may obtain a copy of
+- * the Licence at http://www.redhat.com/
+- *
+- * Software distributed under the Licence is distributed on an "AS IS"
+- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+- * See the Licence for the specific language governing rights and
+- * limitations under the Licence.
+- *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
+- *
+- * Alternatively, the contents of this file may be used under the
+- * terms of the GNU General Public License version 2 (the "GPL"), in
+- * which case the provisions of the GPL are applicable instead of the
+- * above. If you wish to allow the use of your version of this file
+- * only under the terms of the GPL and not to allow others to use your
+- * version of this file under the RHEPL, indicate your decision by
+- * deleting the provisions above and replace them with the notice and
+- * other provisions required by the GPL. If you do not delete the
+- * provisions above, a recipient may use your version of this file
+- * under either the RHEPL or the GPL.
+- *
+- * $Id$
++ * $Id$
+ *
+ */
+
+ #include <linux/kernel.h>
+ #include <linux/slab.h>
+-#include <linux/jffs2.h>
+ #include <linux/mtd/mtd.h>
+-#include <linux/interrupt.h>
++#include <linux/compiler.h>
++#include <linux/sched.h> /* For cond_resched() */
+ #include "nodelist.h"
+
+ /**
+@@ -62,53 +38,95 @@
+ * for the requested allocation.
+ */
+
+-static int jffs2_do_reserve_space(struct jffs2_sb_info *c, __u32 minsize, __u32 *ofs, __u32 *len);
++static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len);
+
+-int jffs2_reserve_space(struct jffs2_sb_info *c, __u32 minsize, __u32 *ofs, __u32 *len, int prio)
++int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len, int prio)
+ {
+ int ret = -EAGAIN;
+- int blocksneeded = JFFS2_RESERVED_BLOCKS_WRITE;
++ int blocksneeded = c->resv_blocks_write;
+ /* align it */
+ minsize = PAD(minsize);
+
+- if (prio == ALLOC_DELETION)
+- blocksneeded = JFFS2_RESERVED_BLOCKS_DELETION;
+-
+ D1(printk(KERN_DEBUG "jffs2_reserve_space(): Requested 0x%x bytes\n", minsize));
+ down(&c->alloc_sem);
+
+ D1(printk(KERN_DEBUG "jffs2_reserve_space(): alloc sem got\n"));
+
+- spin_lock_bh(&c->erase_completion_lock);
++ spin_lock(&c->erase_completion_lock);
+
+- /* this needs a little more thought */
++ /* this needs a little more thought (true <tglx> :)) */
+ while(ret == -EAGAIN) {
+ while(c->nr_free_blocks + c->nr_erasing_blocks < blocksneeded) {
+ int ret;
++ uint32_t dirty, avail;
++
++ /* calculate real dirty size
++ * dirty_size contains blocks on erase_pending_list
++ * those blocks are counted in c->nr_erasing_blocks.
++ * If one block is actually erased, it is not longer counted as dirty_space
++ * but it is counted in c->nr_erasing_blocks, so we add it and subtract it
++ * with c->nr_erasing_blocks * c->sector_size again.
++ * Blocks on erasable_list are counted as dirty_size, but not in c->nr_erasing_blocks
++ * This helps us to force gc and pick eventually a clean block to spread the load.
++ * We add unchecked_size here, as we hopefully will find some space to use.
++ * This will affect the sum only once, as gc first finishes checking
++ * of nodes.
++ */
++ dirty = c->dirty_size + c->erasing_size - c->nr_erasing_blocks * c->sector_size + c->unchecked_size;
++ if (dirty < c->nospc_dirty_size) {
++ if (prio == ALLOC_DELETION && c->nr_free_blocks + c->nr_erasing_blocks >= c->resv_blocks_deletion) {
++ printk(KERN_NOTICE "jffs2_reserve_space(): Low on dirty space to GC, but it's a deletion. Allowing...\n");
++ break;
++ }
++ D1(printk(KERN_DEBUG "dirty size 0x%08x + unchecked_size 0x%08x < nospc_dirty_size 0x%08x, returning -ENOSPC\n",
++ dirty, c->unchecked_size, c->sector_size));
++
++ spin_unlock(&c->erase_completion_lock);
++ up(&c->alloc_sem);
++ return -ENOSPC;
++ }
+
++ /* Calc possibly available space. Possibly available means that we
++ * don't know, if unchecked size contains obsoleted nodes, which could give us some
++ * more usable space. This will affect the sum only once, as gc first finishes checking
++ * of nodes.
++ + Return -ENOSPC, if the maximum possibly available space is less or equal than
++ * blocksneeded * sector_size.
++ * This blocks endless gc looping on a filesystem, which is nearly full, even if
++ * the check above passes.
++ */
++ avail = c->free_size + c->dirty_size + c->erasing_size + c->unchecked_size;
++ if ( (avail / c->sector_size) <= blocksneeded) {
++ if (prio == ALLOC_DELETION && c->nr_free_blocks + c->nr_erasing_blocks >= c->resv_blocks_deletion) {
++ printk(KERN_NOTICE "jffs2_reserve_space(): Low on possibly available space, but it's a deletion. Allowing...\n");
++ break;
++ }
++
++ D1(printk(KERN_DEBUG "max. available size 0x%08x < blocksneeded * sector_size 0x%08x, returning -ENOSPC\n",
++ avail, blocksneeded * c->sector_size));
++ spin_unlock(&c->erase_completion_lock);
+ up(&c->alloc_sem);
+- if (c->dirty_size < c->sector_size) {
+- D1(printk(KERN_DEBUG "Short on space, but total dirty size 0x%08x < sector size 0x%08x, so -ENOSPC\n", c->dirty_size, c->sector_size));
+- spin_unlock_bh(&c->erase_completion_lock);
+ return -ENOSPC;
+ }
+- D1(printk(KERN_DEBUG "Triggering GC pass. nr_free_blocks %d, nr_erasing_blocks %d, free_size 0x%08x, dirty_size 0x%08x, used_size 0x%08x, erasing_size 0x%08x, bad_size 0x%08x (total 0x%08x of 0x%08x)\n",
+- c->nr_free_blocks, c->nr_erasing_blocks, c->free_size, c->dirty_size, c->used_size, c->erasing_size, c->bad_size,
+- c->free_size + c->dirty_size + c->used_size + c->erasing_size + c->bad_size, c->flash_size));
+- spin_unlock_bh(&c->erase_completion_lock);
++
++ up(&c->alloc_sem);
++
++ D1(printk(KERN_DEBUG "Triggering GC pass. nr_free_blocks %d, nr_erasing_blocks %d, free_size 0x%08x, dirty_size 0x%08x, wasted_size 0x%08x, used_size 0x%08x, erasing_size 0x%08x, bad_size 0x%08x (total 0x%08x of 0x%08x)\n",
++ c->nr_free_blocks, c->nr_erasing_blocks, c->free_size, c->dirty_size, c->wasted_size, c->used_size, c->erasing_size, c->bad_size,
++ c->free_size + c->dirty_size + c->wasted_size + c->used_size + c->erasing_size + c->bad_size, c->flash_size));
++ spin_unlock(&c->erase_completion_lock);
+
+ ret = jffs2_garbage_collect_pass(c);
+ if (ret)
+ return ret;
+
+- if (current->need_resched)
+- schedule();
++ cond_resched();
+
+ if (signal_pending(current))
+ return -EINTR;
+
+ down(&c->alloc_sem);
+- spin_lock_bh(&c->erase_completion_lock);
++ spin_lock(&c->erase_completion_lock);
+ }
+
+ ret = jffs2_do_reserve_space(c, minsize, ofs, len);
+@@ -116,45 +134,72 @@
+ D1(printk(KERN_DEBUG "jffs2_reserve_space: ret is %d\n", ret));
+ }
+ }
+- spin_unlock_bh(&c->erase_completion_lock);
++ spin_unlock(&c->erase_completion_lock);
+ if (ret)
+ up(&c->alloc_sem);
+ return ret;
+ }
+
+-int jffs2_reserve_space_gc(struct jffs2_sb_info *c, __u32 minsize, __u32 *ofs, __u32 *len)
++int jffs2_reserve_space_gc(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len)
+ {
+ int ret = -EAGAIN;
+ minsize = PAD(minsize);
+
+ D1(printk(KERN_DEBUG "jffs2_reserve_space_gc(): Requested 0x%x bytes\n", minsize));
+
+- spin_lock_bh(&c->erase_completion_lock);
++ spin_lock(&c->erase_completion_lock);
+ while(ret == -EAGAIN) {
+ ret = jffs2_do_reserve_space(c, minsize, ofs, len);
+ if (ret) {
+ D1(printk(KERN_DEBUG "jffs2_reserve_space_gc: looping, ret is %d\n", ret));
+ }
+ }
+- spin_unlock_bh(&c->erase_completion_lock);
++ spin_unlock(&c->erase_completion_lock);
+ return ret;
+ }
+
+ /* Called with alloc sem _and_ erase_completion_lock */
+-static int jffs2_do_reserve_space(struct jffs2_sb_info *c, __u32 minsize, __u32 *ofs, __u32 *len)
++static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len)
+ {
+ struct jffs2_eraseblock *jeb = c->nextblock;
+
+ restart:
+ if (jeb && minsize > jeb->free_size) {
+ /* Skip the end of this block and file it as having some dirty space */
+- c->dirty_size += jeb->free_size;
++ /* If there's a pending write to it, flush now */
++ if (jffs2_wbuf_dirty(c)) {
++ spin_unlock(&c->erase_completion_lock);
++ D1(printk(KERN_DEBUG "jffs2_do_reserve_space: Flushing write buffer\n"));
++ jffs2_flush_wbuf_pad(c);
++ spin_lock(&c->erase_completion_lock);
++ jeb = c->nextblock;
++ goto restart;
++ }
++ c->wasted_size += jeb->free_size;
+ c->free_size -= jeb->free_size;
+- jeb->dirty_size += jeb->free_size;
++ jeb->wasted_size += jeb->free_size;
+ jeb->free_size = 0;
++
++ /* Check, if we have a dirty block now, or if it was dirty already */
++ if (ISDIRTY (jeb->wasted_size + jeb->dirty_size)) {
++ c->dirty_size += jeb->wasted_size;
++ c->wasted_size -= jeb->wasted_size;
++ jeb->dirty_size += jeb->wasted_size;
++ jeb->wasted_size = 0;
++ if (VERYDIRTY(c, jeb->dirty_size)) {
++ D1(printk(KERN_DEBUG "Adding full erase block at 0x%08x to very_dirty_list (free 0x%08x, dirty 0x%08x, used 0x%08x\n",
++ jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size));
++ list_add_tail(&jeb->list, &c->very_dirty_list);
++ } else {
+ D1(printk(KERN_DEBUG "Adding full erase block at 0x%08x to dirty_list (free 0x%08x, dirty 0x%08x, used 0x%08x\n",
+ jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size));
+ list_add_tail(&jeb->list, &c->dirty_list);
++ }
++ } else {
++ D1(printk(KERN_DEBUG "Adding full erase block at 0x%08x to clean_list (free 0x%08x, dirty 0x%08x, used 0x%08x\n",
++ jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size));
++ list_add_tail(&jeb->list, &c->clean_list);
++ }
+ c->nextblock = jeb = NULL;
+ }
+
+@@ -164,33 +209,44 @@
+
+ if (list_empty(&c->free_list)) {
+
+- DECLARE_WAITQUEUE(wait, current);
++ if (!c->nr_erasing_blocks &&
++ !list_empty(&c->erasable_list)) {
++ struct jffs2_eraseblock *ejeb;
++
++ ejeb = list_entry(c->erasable_list.next, struct jffs2_eraseblock, list);
++ list_del(&ejeb->list);
++ list_add_tail(&ejeb->list, &c->erase_pending_list);
++ c->nr_erasing_blocks++;
++ jffs2_erase_pending_trigger(c);
++ D1(printk(KERN_DEBUG "jffs2_do_reserve_space: Triggering erase of erasable block at 0x%08x\n",
++ ejeb->offset));
++ }
++
++ if (!c->nr_erasing_blocks &&
++ !list_empty(&c->erasable_pending_wbuf_list)) {
++ D1(printk(KERN_DEBUG "jffs2_do_reserve_space: Flushing write buffer\n"));
++ /* c->nextblock is NULL, no update to c->nextblock allowed */
++ spin_unlock(&c->erase_completion_lock);
++ jffs2_flush_wbuf_pad(c);
++ spin_lock(&c->erase_completion_lock);
++ /* Have another go. It'll be on the erasable_list now */
++ return -EAGAIN;
++ }
+
+ if (!c->nr_erasing_blocks) {
+-// if (list_empty(&c->erasing_list) && list_empty(&c->erase_pending_list) && list_empty(c->erase_complete_list)) {
+ /* Ouch. We're in GC, or we wouldn't have got here.
+ And there's no space left. At all. */
+- printk(KERN_CRIT "Argh. No free space left for GC. nr_erasing_blocks is %d. nr_free_blocks is %d. (erasingempty: %s, erasependingempty: %s)\n",
+- c->nr_erasing_blocks, c->nr_free_blocks, list_empty(&c->erasing_list)?"yes":"no", list_empty(&c->erase_pending_list)?"yes":"no");
++ printk(KERN_CRIT "Argh. No free space left for GC. nr_erasing_blocks is %d. nr_free_blocks is %d. (erasableempty: %s, erasingempty: %s, erasependingempty: %s)\n",
++ c->nr_erasing_blocks, c->nr_free_blocks, list_empty(&c->erasable_list)?"yes":"no",
++ list_empty(&c->erasing_list)?"yes":"no", list_empty(&c->erase_pending_list)?"yes":"no");
+ return -ENOSPC;
+ }
+- /* Make sure this can't deadlock. Someone has to start the erases
+- of erase_pending blocks */
+- set_current_state(TASK_INTERRUPTIBLE);
+- add_wait_queue(&c->erase_wait, &wait);
+- D1(printk(KERN_DEBUG "Waiting for erases to complete. erasing_blocks is %d. (erasingempty: %s, erasependingempty: %s)\n",
+- c->nr_erasing_blocks, list_empty(&c->erasing_list)?"yes":"no", list_empty(&c->erase_pending_list)?"yes":"no"));
+- if (!list_empty(&c->erase_pending_list)) {
+- D1(printk(KERN_DEBUG "Triggering pending erases\n"));
+- jffs2_erase_pending_trigger(c);
+- }
+- spin_unlock_bh(&c->erase_completion_lock);
+- schedule();
+- remove_wait_queue(&c->erase_wait, &wait);
+- spin_lock_bh(&c->erase_completion_lock);
+- if (signal_pending(current)) {
+- return -EINTR;
+- }
++
++ spin_unlock(&c->erase_completion_lock);
++ /* Don't wait for it; just erase one right now */
++ jffs2_erase_pending_blocks(c, 1);
++ spin_lock(&c->erase_completion_lock);
++
+ /* An erase may have failed, decreasing the
+ amount of free space available. So we must
+ restart from the beginning */
+@@ -201,7 +257,8 @@
+ list_del(next);
+ c->nextblock = jeb = list_entry(next, struct jffs2_eraseblock, list);
+ c->nr_free_blocks--;
+- if (jeb->free_size != c->sector_size - sizeof(struct jffs2_unknown_node)) {
++
++ if (jeb->free_size != c->sector_size - c->cleanmarker_size) {
+ printk(KERN_WARNING "Eep. Block 0x%08x taken from free_list had free_size of 0x%08x!!\n", jeb->offset, jeb->free_size);
+ goto restart;
+ }
+@@ -210,6 +267,20 @@
+ enough space */
+ *ofs = jeb->offset + (c->sector_size - jeb->free_size);
+ *len = jeb->free_size;
++
++ if (c->cleanmarker_size && jeb->used_size == c->cleanmarker_size &&
++ !jeb->first_node->next_in_ino) {
++ /* Only node in it beforehand was a CLEANMARKER node (we think).
++ So mark it obsolete now that there's going to be another node
++ in the block. This will reduce used_size to zero but We've
++ already set c->nextblock so that jffs2_mark_node_obsolete()
++ won't try to refile it to the dirty_list.
++ */
++ spin_unlock(&c->erase_completion_lock);
++ jffs2_mark_node_obsolete(c, jeb->first_node);
++ spin_lock(&c->erase_completion_lock);
++ }
++
+ D1(printk(KERN_DEBUG "jffs2_do_reserve_space(): Giving 0x%x bytes at 0x%x\n", *len, *ofs));
+ return 0;
+ }
+@@ -217,9 +288,9 @@
+ /**
+ * jffs2_add_physical_node_ref - add a physical node reference to the list
+ * @c: superblock info
+- * @ofs: physical location of this physical node
++ * @new: new node reference to add
+ * @len: length of this physical node
+- * @ino: inode number with which this physical node is associated
++ * @dirty: dirty flag for new node
+ *
+ * Should only be used to report nodes for which space has been allocated
+ * by jffs2_reserve_space.
+@@ -227,47 +298,58 @@
+ * Must be called with the alloc_sem held.
+ */
+
+-int jffs2_add_physical_node_ref(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *new, __u32 len, int dirty)
++int jffs2_add_physical_node_ref(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *new)
+ {
+ struct jffs2_eraseblock *jeb;
++ uint32_t len;
++
++ jeb = &c->blocks[new->flash_offset / c->sector_size];
++ len = ref_totlen(c, jeb, new);
+
+- len = PAD(len);
+- jeb = &c->blocks[(new->flash_offset & ~3) / c->sector_size];
+- D1(printk(KERN_DEBUG "jffs2_add_physical_node_ref(): Node at 0x%x, size 0x%x\n", new->flash_offset & ~3, len));
++ D1(printk(KERN_DEBUG "jffs2_add_physical_node_ref(): Node at 0x%x(%d), size 0x%x\n", ref_offset(new), ref_flags(new), len));
+ #if 1
+- if (jeb != c->nextblock || (new->flash_offset & ~3) != jeb->offset + (c->sector_size - jeb->free_size)) {
++ if (jeb != c->nextblock || (ref_offset(new)) != jeb->offset + (c->sector_size - jeb->free_size)) {
+ printk(KERN_WARNING "argh. node added in wrong place\n");
+ jffs2_free_raw_node_ref(new);
+ return -EINVAL;
+ }
+ #endif
++ spin_lock(&c->erase_completion_lock);
++
+ if (!jeb->first_node)
+ jeb->first_node = new;
+ if (jeb->last_node)
+ jeb->last_node->next_phys = new;
+ jeb->last_node = new;
+
+- spin_lock_bh(&c->erase_completion_lock);
+ jeb->free_size -= len;
+ c->free_size -= len;
+- if (dirty) {
+- new->flash_offset |= 1;
++ if (ref_obsolete(new)) {
+ jeb->dirty_size += len;
+ c->dirty_size += len;
+ } else {
+ jeb->used_size += len;
+ c->used_size += len;
+ }
+- spin_unlock_bh(&c->erase_completion_lock);
++
+ if (!jeb->free_size && !jeb->dirty_size) {
+ /* If it lives on the dirty_list, jffs2_reserve_space will put it there */
+ D1(printk(KERN_DEBUG "Adding full erase block at 0x%08x to clean_list (free 0x%08x, dirty 0x%08x, used 0x%08x\n",
+ jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size));
++ if (jffs2_wbuf_dirty(c)) {
++ /* Flush the last write in the block if it's outstanding */
++ spin_unlock(&c->erase_completion_lock);
++ jffs2_flush_wbuf_pad(c);
++ spin_lock(&c->erase_completion_lock);
++ }
++
+ list_add_tail(&jeb->list, &c->clean_list);
+ c->nextblock = NULL;
+ }
+ ACCT_SANITY_CHECK(c,jeb);
+- ACCT_PARANOIA_CHECK(jeb);
++ D1(ACCT_PARANOIA_CHECK(jeb));
++
++ spin_unlock(&c->erase_completion_lock);
+
+ return 0;
+ }
+@@ -280,20 +362,34 @@
+ up(&c->alloc_sem);
+ }
+
++static inline int on_list(struct list_head *obj, struct list_head *head)
++{
++ struct list_head *this;
++
++ list_for_each(this, head) {
++ if (this == obj) {
++ D1(printk("%p is on list at %p\n", obj, head));
++ return 1;
++
++ }
++ }
++ return 0;
++}
++
+ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *ref)
+ {
+ struct jffs2_eraseblock *jeb;
+ int blocknr;
+ struct jffs2_unknown_node n;
+- int ret;
+- ssize_t retlen;
++ int ret, addedsize;
++ size_t retlen;
+
+ if(!ref) {
+ printk(KERN_NOTICE "EEEEEK. jffs2_mark_node_obsolete called with NULL node\n");
+ return;
+ }
+- if (ref->flash_offset & 1) {
+- D1(printk(KERN_DEBUG "jffs2_mark_node_obsolete called with already obsolete node at 0x%08x\n", ref->flash_offset &~3));
++ if (ref_obsolete(ref)) {
++ D1(printk(KERN_DEBUG "jffs2_mark_node_obsolete called with already obsolete node at 0x%08x\n", ref_offset(ref)));
+ return;
+ }
+ blocknr = ref->flash_offset / c->sector_size;
+@@ -302,22 +398,63 @@
+ BUG();
+ }
+ jeb = &c->blocks[blocknr];
+- if (jeb->used_size < ref->totlen) {
++
++ spin_lock(&c->erase_completion_lock);
++
++ if (ref_flags(ref) == REF_UNCHECKED) {
++ D1(if (unlikely(jeb->unchecked_size < ref_totlen(c, jeb, ref))) {
++ printk(KERN_NOTICE "raw unchecked node of size 0x%08x freed from erase block %d at 0x%08x, but unchecked_size was already 0x%08x\n",
++ ref_totlen(c, jeb, ref), blocknr, ref->flash_offset, jeb->used_size);
++ BUG();
++ })
++ D1(printk(KERN_DEBUG "Obsoleting previously unchecked node at 0x%08x of len %x: ", ref_offset(ref), ref_totlen(c, jeb, ref)));
++ jeb->unchecked_size -= ref_totlen(c, jeb, ref);
++ c->unchecked_size -= ref_totlen(c, jeb, ref);
++ } else {
++ D1(if (unlikely(jeb->used_size < ref_totlen(c, jeb, ref))) {
+ printk(KERN_NOTICE "raw node of size 0x%08x freed from erase block %d at 0x%08x, but used_size was already 0x%08x\n",
+- ref->totlen, blocknr, ref->flash_offset, jeb->used_size);
++ ref_totlen(c, jeb, ref), blocknr, ref->flash_offset, jeb->used_size);
+ BUG();
++ })
++ D1(printk(KERN_DEBUG "Obsoleting node at 0x%08x of len %x: ", ref_offset(ref), ref_totlen(c, jeb, ref)));
++ jeb->used_size -= ref_totlen(c, jeb, ref);
++ c->used_size -= ref_totlen(c, jeb, ref);
++ }
++
++ // Take care, that wasted size is taken into concern
++ if ((jeb->dirty_size || ISDIRTY(jeb->wasted_size + ref_totlen(c, jeb, ref))) && jeb != c->nextblock) {
++ D1(printk("Dirtying\n"));
++ addedsize = ref_totlen(c, jeb, ref);
++ jeb->dirty_size += ref_totlen(c, jeb, ref);
++ c->dirty_size += ref_totlen(c, jeb, ref);
++
++ /* Convert wasted space to dirty, if not a bad block */
++ if (jeb->wasted_size) {
++ if (on_list(&jeb->list, &c->bad_used_list)) {
++ D1(printk(KERN_DEBUG "Leaving block at %08x on the bad_used_list\n",
++ jeb->offset));
++ addedsize = 0; /* To fool the refiling code later */
++ } else {
++ D1(printk(KERN_DEBUG "Converting %d bytes of wasted space to dirty in block at %08x\n",
++ jeb->wasted_size, jeb->offset));
++ addedsize += jeb->wasted_size;
++ jeb->dirty_size += jeb->wasted_size;
++ c->dirty_size += jeb->wasted_size;
++ c->wasted_size -= jeb->wasted_size;
++ jeb->wasted_size = 0;
+ }
+-
+- spin_lock_bh(&c->erase_completion_lock);
+- jeb->used_size -= ref->totlen;
+- jeb->dirty_size += ref->totlen;
+- c->used_size -= ref->totlen;
+- c->dirty_size += ref->totlen;
+- ref->flash_offset |= 1;
++ }
++ } else {
++ D1(printk("Wasting\n"));
++ addedsize = 0;
++ jeb->wasted_size += ref_totlen(c, jeb, ref);
++ c->wasted_size += ref_totlen(c, jeb, ref);
++ }
++ ref->flash_offset = ref_offset(ref) | REF_OBSOLETE;
+
+ ACCT_SANITY_CHECK(c, jeb);
+
+- ACCT_PARANOIA_CHECK(jeb);
++ D1(ACCT_PARANOIA_CHECK(jeb));
+
+ if (c->flags & JFFS2_SB_FLAG_MOUNTING) {
+ /* Mount in progress. Don't muck about with the block
+@@ -325,68 +462,280 @@
+ obliterate nodes that look obsolete. If they weren't
+ marked obsolete on the flash at the time they _became_
+ obsolete, there was probably a reason for that. */
+- spin_unlock_bh(&c->erase_completion_lock);
++ spin_unlock(&c->erase_completion_lock);
+ return;
+ }
++
+ if (jeb == c->nextblock) {
+ D2(printk(KERN_DEBUG "Not moving nextblock 0x%08x to dirty/erase_pending list\n", jeb->offset));
+- } else if (jeb == c->gcblock) {
+- D2(printk(KERN_DEBUG "Not moving gcblock 0x%08x to dirty/erase_pending list\n", jeb->offset));
+-#if 0 /* We no longer do this here. It can screw the wear levelling. If you have a lot of static
+- data and a few blocks free, and you just create new files and keep deleting/overwriting
+- them, then you'd keep erasing and reusing those blocks without ever moving stuff around.
+- So we leave completely obsoleted blocks on the dirty_list and let the GC delete them
+- when it finds them there. That way, we still get the 'once in a while, take a clean block'
+- to spread out the flash usage */
+- } else if (!jeb->used_size) {
++ } else if (!jeb->used_size && !jeb->unchecked_size) {
++ if (jeb == c->gcblock) {
++ D1(printk(KERN_DEBUG "gcblock at 0x%08x completely dirtied. Clearing gcblock...\n", jeb->offset));
++ c->gcblock = NULL;
++ } else {
+ D1(printk(KERN_DEBUG "Eraseblock at 0x%08x completely dirtied. Removing from (dirty?) list...\n", jeb->offset));
+ list_del(&jeb->list);
++ }
++ if (jffs2_wbuf_dirty(c)) {
++ D1(printk(KERN_DEBUG "...and adding to erasable_pending_wbuf_list\n"));
++ list_add_tail(&jeb->list, &c->erasable_pending_wbuf_list);
++ } else {
++ if (jiffies & 127) {
++ /* Most of the time, we just erase it immediately. Otherwise we
++ spend ages scanning it on mount, etc. */
+ D1(printk(KERN_DEBUG "...and adding to erase_pending_list\n"));
+ list_add_tail(&jeb->list, &c->erase_pending_list);
+ c->nr_erasing_blocks++;
+ jffs2_erase_pending_trigger(c);
+- // OFNI_BS_2SFFJ(c)->s_dirt = 1;
++ } else {
++ /* Sometimes, however, we leave it elsewhere so it doesn't get
++ immediately reused, and we spread the load a bit. */
++ D1(printk(KERN_DEBUG "...and adding to erasable_list\n"));
++ list_add_tail(&jeb->list, &c->erasable_list);
++ }
++ }
+ D1(printk(KERN_DEBUG "Done OK\n"));
+-#endif
+- } else if (jeb->dirty_size == ref->totlen) {
++ } else if (jeb == c->gcblock) {
++ D2(printk(KERN_DEBUG "Not moving gcblock 0x%08x to dirty_list\n", jeb->offset));
++ } else if (ISDIRTY(jeb->dirty_size) && !ISDIRTY(jeb->dirty_size - addedsize)) {
+ D1(printk(KERN_DEBUG "Eraseblock at 0x%08x is freshly dirtied. Removing from clean list...\n", jeb->offset));
+ list_del(&jeb->list);
+ D1(printk(KERN_DEBUG "...and adding to dirty_list\n"));
+ list_add_tail(&jeb->list, &c->dirty_list);
++ } else if (VERYDIRTY(c, jeb->dirty_size) &&
++ !VERYDIRTY(c, jeb->dirty_size - addedsize)) {
++ D1(printk(KERN_DEBUG "Eraseblock at 0x%08x is now very dirty. Removing from dirty list...\n", jeb->offset));
++ list_del(&jeb->list);
++ D1(printk(KERN_DEBUG "...and adding to very_dirty_list\n"));
++ list_add_tail(&jeb->list, &c->very_dirty_list);
++ } else {
++ D1(printk(KERN_DEBUG "Eraseblock at 0x%08x not moved anywhere. (free 0x%08x, dirty 0x%08x, used 0x%08x)\n",
++ jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size));
+ }
+- spin_unlock_bh(&c->erase_completion_lock);
+
+- if (c->mtd->type != MTD_NORFLASH && c->mtd->type != MTD_RAM)
++ spin_unlock(&c->erase_completion_lock);
++
++ if (!jffs2_can_mark_obsolete(c))
+ return;
+- if (OFNI_BS_2SFFJ(c)->s_flags & MS_RDONLY)
++ if (jffs2_is_readonly(c))
+ return;
+
+- D1(printk(KERN_DEBUG "obliterating obsoleted node at 0x%08x\n", ref->flash_offset &~3));
+- ret = c->mtd->read(c->mtd, ref->flash_offset &~3, sizeof(n), &retlen, (char *)&n);
++ D1(printk(KERN_DEBUG "obliterating obsoleted node at 0x%08x\n", ref_offset(ref)));
++ ret = jffs2_flash_read(c, ref_offset(ref), sizeof(n), &retlen, (char *)&n);
+ if (ret) {
+- printk(KERN_WARNING "Read error reading from obsoleted node at 0x%08x: %d\n", ref->flash_offset &~3, ret);
++ printk(KERN_WARNING "Read error reading from obsoleted node at 0x%08x: %d\n", ref_offset(ref), ret);
+ return;
+ }
+ if (retlen != sizeof(n)) {
+- printk(KERN_WARNING "Short read from obsoleted node at 0x%08x: %d\n", ref->flash_offset &~3, retlen);
++ printk(KERN_WARNING "Short read from obsoleted node at 0x%08x: %zd\n", ref_offset(ref), retlen);
+ return;
+ }
+- if (PAD(n.totlen) != PAD(ref->totlen)) {
+- printk(KERN_WARNING "Node totlen on flash (0x%08x) != totlen in node ref (0x%08x)\n", n.totlen, ref->totlen);
++ if (PAD(je32_to_cpu(n.totlen)) != PAD(ref_totlen(c, jeb, ref))) {
++ printk(KERN_WARNING "Node totlen on flash (0x%08x) != totlen from node ref (0x%08x)\n", je32_to_cpu(n.totlen), ref_totlen(c, jeb, ref));
+ return;
+ }
+- if (!(n.nodetype & JFFS2_NODE_ACCURATE)) {
+- D1(printk(KERN_DEBUG "Node at 0x%08x was already marked obsolete (nodetype 0x%04x\n", ref->flash_offset &~3, n.nodetype));
++ if (!(je16_to_cpu(n.nodetype) & JFFS2_NODE_ACCURATE)) {
++ D1(printk(KERN_DEBUG "Node at 0x%08x was already marked obsolete (nodetype 0x%04x)\n", ref_offset(ref), je16_to_cpu(n.nodetype)));
+ return;
+ }
+- n.nodetype &= ~JFFS2_NODE_ACCURATE;
+- ret = c->mtd->write(c->mtd, ref->flash_offset&~3, sizeof(n), &retlen, (char *)&n);
++ /* XXX FIXME: This is ugly now */
++ n.nodetype = cpu_to_je16(je16_to_cpu(n.nodetype) & ~JFFS2_NODE_ACCURATE);
++ ret = jffs2_flash_write(c, ref_offset(ref), sizeof(n), &retlen, (char *)&n);
+ if (ret) {
+- printk(KERN_WARNING "Write error in obliterating obsoleted node at 0x%08x: %d\n", ref->flash_offset &~3, ret);
++ printk(KERN_WARNING "Write error in obliterating obsoleted node at 0x%08x: %d\n", ref_offset(ref), ret);
+ return;
+ }
+ if (retlen != sizeof(n)) {
+- printk(KERN_WARNING "Short write in obliterating obsoleted node at 0x%08x: %d\n", ref->flash_offset &~3, retlen);
++ printk(KERN_WARNING "Short write in obliterating obsoleted node at 0x%08x: %zd\n", ref_offset(ref), retlen);
+ return;
+ }
+ }
++
++#if CONFIG_JFFS2_FS_DEBUG > 0
++void jffs2_dump_block_lists(struct jffs2_sb_info *c)
++{
++
++
++ printk(KERN_DEBUG "jffs2_dump_block_lists:\n");
++ printk(KERN_DEBUG "flash_size: %08x\n", c->flash_size);
++ printk(KERN_DEBUG "used_size: %08x\n", c->used_size);
++ printk(KERN_DEBUG "dirty_size: %08x\n", c->dirty_size);
++ printk(KERN_DEBUG "wasted_size: %08x\n", c->wasted_size);
++ printk(KERN_DEBUG "unchecked_size: %08x\n", c->unchecked_size);
++ printk(KERN_DEBUG "free_size: %08x\n", c->free_size);
++ printk(KERN_DEBUG "erasing_size: %08x\n", c->erasing_size);
++ printk(KERN_DEBUG "bad_size: %08x\n", c->bad_size);
++ printk(KERN_DEBUG "sector_size: %08x\n", c->sector_size);
++ printk(KERN_DEBUG "jffs2_reserved_blocks size: %08x\n",c->sector_size * c->resv_blocks_write);
++
++ if (c->nextblock) {
++ printk(KERN_DEBUG "nextblock: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n",
++ c->nextblock->offset, c->nextblock->used_size, c->nextblock->dirty_size, c->nextblock->wasted_size, c->nextblock->unchecked_size, c->nextblock->free_size);
++ } else {
++ printk(KERN_DEBUG "nextblock: NULL\n");
++ }
++ if (c->gcblock) {
++ printk(KERN_DEBUG "gcblock: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n",
++ c->gcblock->offset, c->gcblock->used_size, c->gcblock->dirty_size, c->gcblock->wasted_size, c->gcblock->unchecked_size, c->gcblock->free_size);
++ } else {
++ printk(KERN_DEBUG "gcblock: NULL\n");
++ }
++ if (list_empty(&c->clean_list)) {
++ printk(KERN_DEBUG "clean_list: empty\n");
++ } else {
++ struct list_head *this;
++ int numblocks = 0;
++ uint32_t dirty = 0;
++
++ list_for_each(this, &c->clean_list) {
++ struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
++ numblocks ++;
++ dirty += jeb->wasted_size;
++ printk(KERN_DEBUG "clean_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n", jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size);
++ }
++ printk (KERN_DEBUG "Contains %d blocks with total wasted size %u, average wasted size: %u\n", numblocks, dirty, dirty / numblocks);
++ }
++ if (list_empty(&c->very_dirty_list)) {
++ printk(KERN_DEBUG "very_dirty_list: empty\n");
++ } else {
++ struct list_head *this;
++ int numblocks = 0;
++ uint32_t dirty = 0;
++
++ list_for_each(this, &c->very_dirty_list) {
++ struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
++ numblocks ++;
++ dirty += jeb->dirty_size;
++ printk(KERN_DEBUG "very_dirty_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n",
++ jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size);
++ }
++ printk (KERN_DEBUG "Contains %d blocks with total dirty size %u, average dirty size: %u\n",
++ numblocks, dirty, dirty / numblocks);
++ }
++ if (list_empty(&c->dirty_list)) {
++ printk(KERN_DEBUG "dirty_list: empty\n");
++ } else {
++ struct list_head *this;
++ int numblocks = 0;
++ uint32_t dirty = 0;
++
++ list_for_each(this, &c->dirty_list) {
++ struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
++ numblocks ++;
++ dirty += jeb->dirty_size;
++ printk(KERN_DEBUG "dirty_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n",
++ jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size);
++ }
++ printk (KERN_DEBUG "Contains %d blocks with total dirty size %u, average dirty size: %u\n",
++ numblocks, dirty, dirty / numblocks);
++ }
++ if (list_empty(&c->erasable_list)) {
++ printk(KERN_DEBUG "erasable_list: empty\n");
++ } else {
++ struct list_head *this;
++
++ list_for_each(this, &c->erasable_list) {
++ struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
++ printk(KERN_DEBUG "erasable_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n",
++ jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size);
++ }
++ }
++ if (list_empty(&c->erasing_list)) {
++ printk(KERN_DEBUG "erasing_list: empty\n");
++ } else {
++ struct list_head *this;
++
++ list_for_each(this, &c->erasing_list) {
++ struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
++ printk(KERN_DEBUG "erasing_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n",
++ jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size);
++ }
++ }
++ if (list_empty(&c->erase_pending_list)) {
++ printk(KERN_DEBUG "erase_pending_list: empty\n");
++ } else {
++ struct list_head *this;
++
++ list_for_each(this, &c->erase_pending_list) {
++ struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
++ printk(KERN_DEBUG "erase_pending_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n",
++ jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size);
++ }
++ }
++ if (list_empty(&c->erasable_pending_wbuf_list)) {
++ printk(KERN_DEBUG "erasable_pending_wbuf_list: empty\n");
++ } else {
++ struct list_head *this;
++
++ list_for_each(this, &c->erasable_pending_wbuf_list) {
++ struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
++ printk(KERN_DEBUG "erasable_pending_wbuf_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n",
++ jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size);
++ }
++ }
++ if (list_empty(&c->free_list)) {
++ printk(KERN_DEBUG "free_list: empty\n");
++ } else {
++ struct list_head *this;
++
++ list_for_each(this, &c->free_list) {
++ struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
++ printk(KERN_DEBUG "free_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n",
++ jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size);
++ }
++ }
++ if (list_empty(&c->bad_list)) {
++ printk(KERN_DEBUG "bad_list: empty\n");
++ } else {
++ struct list_head *this;
++
++ list_for_each(this, &c->bad_list) {
++ struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
++ printk(KERN_DEBUG "bad_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n",
++ jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size);
++ }
++ }
++ if (list_empty(&c->bad_used_list)) {
++ printk(KERN_DEBUG "bad_used_list: empty\n");
++ } else {
++ struct list_head *this;
++
++ list_for_each(this, &c->bad_used_list) {
++ struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
++ printk(KERN_DEBUG "bad_used_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n",
++ jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size);
++ }
++ }
++}
++#endif /* CONFIG_JFFS2_FS_DEBUG */
++
++int jffs2_thread_should_wake(struct jffs2_sb_info *c)
++{
++ int ret = 0;
++ uint32_t dirty;
++
++ if (c->unchecked_size) {
++ D1(printk(KERN_DEBUG "jffs2_thread_should_wake(): unchecked_size %d, checked_ino #%d\n",
++ c->unchecked_size, c->checked_ino));
++ return 1;
++ }
++
++ /* dirty_size contains blocks on erase_pending_list
++ * those blocks are counted in c->nr_erasing_blocks.
++ * If one block is actually erased, it is not longer counted as dirty_space
++ * but it is counted in c->nr_erasing_blocks, so we add it and subtract it
++ * with c->nr_erasing_blocks * c->sector_size again.
++ * Blocks on erasable_list are counted as dirty_size, but not in c->nr_erasing_blocks
++ * This helps us to force gc and pick eventually a clean block to spread the load.
++ */
++ dirty = c->dirty_size + c->erasing_size - c->nr_erasing_blocks * c->sector_size;
++
++ if (c->nr_free_blocks + c->nr_erasing_blocks < c->resv_blocks_gctrigger &&
++ (dirty > c->nospc_dirty_size))
++ ret = 1;
++
++ D1(printk(KERN_DEBUG "jffs2_thread_should_wake(): nr_free_blocks %d, nr_erasing_blocks %d, dirty_size 0x%x: %s\n",
++ c->nr_free_blocks, c->nr_erasing_blocks, c->dirty_size, ret?"yes":"no"));
++
++ return ret;
++}
+diff -Nurb linux-mips-2.4.27/fs/jffs2/os-linux.h linux/fs/jffs2/os-linux.h
+--- linux-mips-2.4.27/fs/jffs2/os-linux.h 1970-01-01 01:00:00.000000000 +0100
++++ linux/fs/jffs2/os-linux.h 2004-11-19 10:25:12.116166808 +0100
+@@ -0,0 +1,212 @@
++/*
++ * JFFS2 -- Journalling Flash File System, Version 2.
++ *
++ * Copyright (C) 2002-2003 Red Hat, Inc.
++ *
++ * Created by David Woodhouse <dwmw2@redhat.com>
++ *
++ * For licensing information, see the file 'LICENCE' in this directory.
++ *
++ * $Id$
++ *
++ */
++
++#ifndef __JFFS2_OS_LINUX_H__
++#define __JFFS2_OS_LINUX_H__
++#include <linux/version.h>
++
++/* JFFS2 uses Linux mode bits natively -- no need for conversion */
++#define os_to_jffs2_mode(x) (x)
++#define jffs2_to_os_mode(x) (x)
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,73)
++#define kstatfs statfs
++#endif
++
++#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,2)
++#define JFFS2_INODE_INFO(i) (list_entry(i, struct jffs2_inode_info, vfs_inode))
++#define OFNI_EDONI_2SFFJ(f) (&(f)->vfs_inode)
++#define JFFS2_SB_INFO(sb) (sb->s_fs_info)
++#define OFNI_BS_2SFFJ(c) ((struct super_block *)c->os_priv)
++#elif defined(JFFS2_OUT_OF_KERNEL)
++#define JFFS2_INODE_INFO(i) ((struct jffs2_inode_info *) &(i)->u)
++#define OFNI_EDONI_2SFFJ(f) ((struct inode *) ( ((char *)f) - ((char *)(&((struct inode *)NULL)->u)) ) )
++#define JFFS2_SB_INFO(sb) ((struct jffs2_sb_info *) &(sb)->u)
++#define OFNI_BS_2SFFJ(c) ((struct super_block *) ( ((char *)c) - ((char *)(&((struct super_block *)NULL)->u)) ) )
++#else
++#define JFFS2_INODE_INFO(i) (&i->u.jffs2_i)
++#define OFNI_EDONI_2SFFJ(f) ((struct inode *) ( ((char *)f) - ((char *)(&((struct inode *)NULL)->u)) ) )
++#define JFFS2_SB_INFO(sb) (&sb->u.jffs2_sb)
++#define OFNI_BS_2SFFJ(c) ((struct super_block *) ( ((char *)c) - ((char *)(&((struct super_block *)NULL)->u)) ) )
++#endif
++
++
++#define JFFS2_F_I_SIZE(f) (OFNI_EDONI_2SFFJ(f)->i_size)
++#define JFFS2_F_I_MODE(f) (OFNI_EDONI_2SFFJ(f)->i_mode)
++#define JFFS2_F_I_UID(f) (OFNI_EDONI_2SFFJ(f)->i_uid)
++#define JFFS2_F_I_GID(f) (OFNI_EDONI_2SFFJ(f)->i_gid)
++
++#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,1)
++#define JFFS2_F_I_RDEV_MIN(f) (iminor(OFNI_EDONI_2SFFJ(f)))
++#define JFFS2_F_I_RDEV_MAJ(f) (imajor(OFNI_EDONI_2SFFJ(f)))
++#else
++#define JFFS2_F_I_RDEV_MIN(f) (MINOR(to_kdev_t(OFNI_EDONI_2SFFJ(f)->i_rdev)))
++#define JFFS2_F_I_RDEV_MAJ(f) (MAJOR(to_kdev_t(OFNI_EDONI_2SFFJ(f)->i_rdev)))
++#endif
++
++/* Urgh. The things we do to keep the 2.4 build working */
++#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,47)
++#define ITIME(sec) ((struct timespec){sec, 0})
++#define I_SEC(tv) ((tv).tv_sec)
++#define JFFS2_F_I_CTIME(f) (OFNI_EDONI_2SFFJ(f)->i_ctime.tv_sec)
++#define JFFS2_F_I_MTIME(f) (OFNI_EDONI_2SFFJ(f)->i_mtime.tv_sec)
++#define JFFS2_F_I_ATIME(f) (OFNI_EDONI_2SFFJ(f)->i_atime.tv_sec)
++#else
++#define ITIME(x) (x)
++#define I_SEC(x) (x)
++#define JFFS2_F_I_CTIME(f) (OFNI_EDONI_2SFFJ(f)->i_ctime)
++#define JFFS2_F_I_MTIME(f) (OFNI_EDONI_2SFFJ(f)->i_mtime)
++#define JFFS2_F_I_ATIME(f) (OFNI_EDONI_2SFFJ(f)->i_atime)
++#endif
++
++#define sleep_on_spinunlock(wq, s) \
++ do { \
++ DECLARE_WAITQUEUE(__wait, current); \
++ add_wait_queue((wq), &__wait); \
++ set_current_state(TASK_UNINTERRUPTIBLE); \
++ spin_unlock(s); \
++ schedule(); \
++ remove_wait_queue((wq), &__wait); \
++ } while(0)
++
++static inline void jffs2_init_inode_info(struct jffs2_inode_info *f)
++{
++#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,2)
++ f->highest_version = 0;
++ f->fragtree = RB_ROOT;
++ f->metadata = NULL;
++ f->dents = NULL;
++ f->flags = 0;
++ f->usercompr = 0;
++#else
++ memset(f, 0, sizeof(*f));
++ init_MUTEX_LOCKED(&f->sem);
++#endif
++}
++
++#define jffs2_is_readonly(c) (OFNI_BS_2SFFJ(c)->s_flags & MS_RDONLY)
++
++#ifndef CONFIG_JFFS2_FS_NAND
++#define jffs2_can_mark_obsolete(c) (1)
++#define jffs2_cleanmarker_oob(c) (0)
++#define jffs2_write_nand_cleanmarker(c,jeb) (-EIO)
++
++#define jffs2_flash_write(c, ofs, len, retlen, buf) ((c)->mtd->write((c)->mtd, ofs, len, retlen, buf))
++#define jffs2_flash_read(c, ofs, len, retlen, buf) ((c)->mtd->read((c)->mtd, ofs, len, retlen, buf))
++#define jffs2_flush_wbuf_pad(c) ({ (void)(c), 0; })
++#define jffs2_flush_wbuf_gc(c, i) ({ (void)(c), (void) i, 0; })
++#define jffs2_nand_read_failcnt(c,jeb) do { ; } while(0)
++#define jffs2_write_nand_badblock(c,jeb) do { ; } while(0)
++#define jffs2_nand_flash_setup(c) (0)
++#define jffs2_nand_flash_cleanup(c) do {} while(0)
++#define jffs2_wbuf_dirty(c) (0)
++#define jffs2_flash_writev(a,b,c,d,e,f) jffs2_flash_direct_writev(a,b,c,d,e)
++#define jffs2_wbuf_timeout NULL
++#define jffs2_wbuf_process NULL
++
++#else /* NAND support present */
++
++#define jffs2_can_mark_obsolete(c) (c->mtd->type == MTD_NORFLASH || c->mtd->type == MTD_RAM)
++#define jffs2_cleanmarker_oob(c) (c->mtd->type == MTD_NANDFLASH)
++
++#define jffs2_flash_write_oob(c, ofs, len, retlen, buf) ((c)->mtd->write_oob((c)->mtd, ofs, len, retlen, buf))
++#define jffs2_flash_read_oob(c, ofs, len, retlen, buf) ((c)->mtd->read_oob((c)->mtd, ofs, len, retlen, buf))
++#define jffs2_wbuf_dirty(c) (!!(c)->wbuf_len)
++struct kstatfs;
++
++/* wbuf.c */
++int jffs2_flash_writev(struct jffs2_sb_info *c, const struct iovec *vecs, unsigned long count, loff_t to, size_t *retlen, uint32_t ino);
++int jffs2_flash_write(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *retlen, const u_char *buf);
++int jffs2_flash_read(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *retlen, u_char *buf);
++int jffs2_check_oob_empty(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,int mode);
++int jffs2_check_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
++int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
++int jffs2_write_nand_badblock(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
++void jffs2_wbuf_timeout(unsigned long data);
++void jffs2_wbuf_process(void *data);
++int jffs2_nand_flash_setup(struct jffs2_sb_info *c);
++void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c);
++#endif /* NAND */
++
++/* erase.c */
++static inline void jffs2_erase_pending_trigger(struct jffs2_sb_info *c)
++{
++ OFNI_BS_2SFFJ(c)->s_dirt = 1;
++}
++
++/* background.c */
++int jffs2_start_garbage_collect_thread(struct jffs2_sb_info *c);
++void jffs2_stop_garbage_collect_thread(struct jffs2_sb_info *c);
++void jffs2_garbage_collect_trigger(struct jffs2_sb_info *c);
++
++/* dir.c */
++extern struct file_operations jffs2_dir_operations;
++extern struct inode_operations jffs2_dir_inode_operations;
++
++/* file.c */
++extern struct file_operations jffs2_file_operations;
++extern struct inode_operations jffs2_file_inode_operations;
++extern struct address_space_operations jffs2_file_address_operations;
++int jffs2_fsync(struct file *, struct dentry *, int);
++int jffs2_do_readpage_nolock (struct inode *inode, struct page *pg);
++int jffs2_do_readpage_unlock (struct inode *inode, struct page *pg);
++int jffs2_readpage (struct file *, struct page *);
++int jffs2_prepare_write (struct file *, struct page *, unsigned, unsigned);
++int jffs2_commit_write (struct file *, struct page *, unsigned, unsigned);
++
++/* ioctl.c */
++int jffs2_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
++
++/* symlink.c */
++extern struct inode_operations jffs2_symlink_inode_operations;
++
++/* fs.c */
++int jffs2_setattr (struct dentry *, struct iattr *);
++void jffs2_read_inode (struct inode *);
++void jffs2_clear_inode (struct inode *);
++void jffs2_dirty_inode(struct inode *inode);
++struct inode *jffs2_new_inode (struct inode *dir_i, int mode,
++ struct jffs2_raw_inode *ri);
++int jffs2_statfs (struct super_block *, struct kstatfs *);
++void jffs2_write_super (struct super_block *);
++int jffs2_remount_fs (struct super_block *, int *, char *);
++int jffs2_do_fill_super(struct super_block *sb, void *data, int silent);
++void jffs2_gc_release_inode(struct jffs2_sb_info *c,
++ struct jffs2_inode_info *f);
++struct jffs2_inode_info *jffs2_gc_fetch_inode(struct jffs2_sb_info *c,
++ int inum, int nlink);
++
++unsigned char *jffs2_gc_fetch_page(struct jffs2_sb_info *c,
++ struct jffs2_inode_info *f,
++ unsigned long offset,
++ unsigned long *priv);
++void jffs2_gc_release_page(struct jffs2_sb_info *c,
++ unsigned char *pg,
++ unsigned long *priv);
++
++
++/* writev.c */
++int jffs2_flash_direct_writev(struct jffs2_sb_info *c, const struct iovec *vecs,
++ unsigned long count, loff_t to, size_t *retlen);
++
++/* Compression config */
++#define JFFS2_COMPRESSION
++#undef JFFS2_USE_DYNRUBIN /* Disabled 23/9/1. With zlib it hardly ever gets a look in */
++#undef JFFS2_USE_RUBINMIPS /* Disabled 26/2/1. Obsoleted by dynrubin */
++#define JFFS2_USE_ZLIB
++#define JFFS2_USE_RTIME /* rtime does manage to recompress already-compressed data */
++
++
++#endif /* __JFFS2_OS_LINUX_H__ */
++
++
+diff -Nurb linux-mips-2.4.27/fs/jffs2/pushpull.h linux/fs/jffs2/pushpull.h
+--- linux-mips-2.4.27/fs/jffs2/pushpull.h 2001-11-05 21:16:19.000000000 +0100
++++ linux/fs/jffs2/pushpull.h 2004-11-19 10:25:12.118166504 +0100
+@@ -1,42 +1,21 @@
+ /*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+- * Copyright (C) 2001 Red Hat, Inc.
++ * Copyright (C) 2001, 2002 Red Hat, Inc.
+ *
+- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
++ * Created by David Woodhouse <dwmw2@redhat.com>
+ *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
++ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+- * The contents of this file are subject to the Red Hat eCos Public
+- * License Version 1.1 (the "Licence"); you may not use this file
+- * except in compliance with the Licence. You may obtain a copy of
+- * the Licence at http://www.redhat.com/
+- *
+- * Software distributed under the Licence is distributed on an "AS IS"
+- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+- * See the Licence for the specific language governing rights and
+- * limitations under the Licence.
+- *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
+- *
+- * Alternatively, the contents of this file may be used under the
+- * terms of the GNU General Public License version 2 (the "GPL"), in
+- * which case the provisions of the GPL are applicable instead of the
+- * above. If you wish to allow the use of your version of this file
+- * only under the terms of the GPL and not to allow others to use your
+- * version of this file under the RHEPL, indicate your decision by
+- * deleting the provisions above and replace them with the notice and
+- * other provisions required by the GPL. If you do not delete the
+- * provisions above, a recipient may use your version of this file
+- * under either the RHEPL or the GPL.
+- *
+- * $Id$
++ * $Id$
+ *
+ */
+
+ #ifndef __PUSHPULL_H__
+ #define __PUSHPULL_H__
++
++#include <linux/errno.h>
++
+ struct pushpull {
+ unsigned char *buf;
+ unsigned int buflen;
+@@ -44,9 +23,36 @@
+ unsigned int reserve;
+ };
+
+-void init_pushpull(struct pushpull *, char *, unsigned, unsigned, unsigned);
+-int pushbit(struct pushpull *pp, int bit, int use_reserved);
+-int pushedbits(struct pushpull *pp);
++
++static inline void init_pushpull(struct pushpull *pp, char *buf, unsigned buflen, unsigned ofs, unsigned reserve)
++{
++ pp->buf = buf;
++ pp->buflen = buflen;
++ pp->ofs = ofs;
++ pp->reserve = reserve;
++}
++
++static inline int pushbit(struct pushpull *pp, int bit, int use_reserved)
++{
++ if (pp->ofs >= pp->buflen - (use_reserved?0:pp->reserve)) {
++ return -ENOSPC;
++ }
++
++ if (bit) {
++ pp->buf[pp->ofs >> 3] |= (1<<(7-(pp->ofs &7)));
++ }
++ else {
++ pp->buf[pp->ofs >> 3] &= ~(1<<(7-(pp->ofs &7)));
++ }
++ pp->ofs++;
++
++ return 0;
++}
++
++static inline int pushedbits(struct pushpull *pp)
++{
++ return pp->ofs;
++}
+
+ static inline int pullbit(struct pushpull *pp)
+ {
+diff -Nurb linux-mips-2.4.27/fs/jffs2/rbtree.c linux/fs/jffs2/rbtree.c
+--- linux-mips-2.4.27/fs/jffs2/rbtree.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/fs/jffs2/rbtree.c 2004-11-19 10:25:12.120166200 +0100
+@@ -0,0 +1,363 @@
++/*
++ Red Black Trees
++ (C) 1999 Andrea Arcangeli <andrea@suse.de>
++ (C) 2002 David Woodhouse <dwmw2@infradead.org>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License as published by
++ the Free Software Foundation; either version 2 of the License, or
++ (at your option) any later version.
++
++ This program is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ GNU General Public License for more details.
++
++ You should have received a copy of the GNU General Public License
++ along with this program; if not, write to the Free Software
++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++
++ $Id$
++*/
++
++#ifdef __ECOS /* This file is _not_ under the eCos licence; it is pure GPL. */
++#error "Licence problem. eCos has its own rbtree code."
++#endif
++
++#include <linux/version.h>
++#include <linux/rbtree.h>
++
++/* This wasn't present till 2.4.11, wasn't exported till 2.4.19 */
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,11) || \
++ (LINUX_VERSION_CODE < KERNEL_VERSION(2,4,19) && defined(MODULE))
++static void __rb_rotate_left(struct rb_node * node, struct rb_root * root)
++{
++ struct rb_node * right = node->rb_right;
++
++ if ((node->rb_right = right->rb_left))
++ right->rb_left->rb_parent = node;
++ right->rb_left = node;
++
++ if ((right->rb_parent = node->rb_parent))
++ {
++ if (node == node->rb_parent->rb_left)
++ node->rb_parent->rb_left = right;
++ else
++ node->rb_parent->rb_right = right;
++ }
++ else
++ root->rb_node = right;
++ node->rb_parent = right;
++}
++
++static void __rb_rotate_right(struct rb_node * node, struct rb_root * root)
++{
++ struct rb_node * left = node->rb_left;
++
++ if ((node->rb_left = left->rb_right))
++ left->rb_right->rb_parent = node;
++ left->rb_right = node;
++
++ if ((left->rb_parent = node->rb_parent))
++ {
++ if (node == node->rb_parent->rb_right)
++ node->rb_parent->rb_right = left;
++ else
++ node->rb_parent->rb_left = left;
++ }
++ else
++ root->rb_node = left;
++ node->rb_parent = left;
++}
++
++void rb_insert_color(struct rb_node * node, struct rb_root * root)
++{
++ struct rb_node * parent, * gparent;
++
++ while ((parent = node->rb_parent) && parent->rb_color == RB_RED)
++ {
++ gparent = parent->rb_parent;
++
++ if (parent == gparent->rb_left)
++ {
++ {
++ register struct rb_node * uncle = gparent->rb_right;
++ if (uncle && uncle->rb_color == RB_RED)
++ {
++ uncle->rb_color = RB_BLACK;
++ parent->rb_color = RB_BLACK;
++ gparent->rb_color = RB_RED;
++ node = gparent;
++ continue;
++ }
++ }
++
++ if (parent->rb_right == node)
++ {
++ register struct rb_node * tmp;
++ __rb_rotate_left(parent, root);
++ tmp = parent;
++ parent = node;
++ node = tmp;
++ }
++
++ parent->rb_color = RB_BLACK;
++ gparent->rb_color = RB_RED;
++ __rb_rotate_right(gparent, root);
++ } else {
++ {
++ register struct rb_node * uncle = gparent->rb_left;
++ if (uncle && uncle->rb_color == RB_RED)
++ {
++ uncle->rb_color = RB_BLACK;
++ parent->rb_color = RB_BLACK;
++ gparent->rb_color = RB_RED;
++ node = gparent;
++ continue;
++ }
++ }
++
++ if (parent->rb_left == node)
++ {
++ register struct rb_node * tmp;
++ __rb_rotate_right(parent, root);
++ tmp = parent;
++ parent = node;
++ node = tmp;
++ }
++
++ parent->rb_color = RB_BLACK;
++ gparent->rb_color = RB_RED;
++ __rb_rotate_left(gparent, root);
++ }
++ }
++
++ root->rb_node->rb_color = RB_BLACK;
++}
++
++static void __rb_erase_color(struct rb_node * node, struct rb_node * parent,
++ struct rb_root * root)
++{
++ struct rb_node * other;
++
++ while ((!node || node->rb_color == RB_BLACK) && node != root->rb_node)
++ {
++ if (parent->rb_left == node)
++ {
++ other = parent->rb_right;
++ if (other->rb_color == RB_RED)
++ {
++ other->rb_color = RB_BLACK;
++ parent->rb_color = RB_RED;
++ __rb_rotate_left(parent, root);
++ other = parent->rb_right;
++ }
++ if ((!other->rb_left ||
++ other->rb_left->rb_color == RB_BLACK)
++ && (!other->rb_right ||
++ other->rb_right->rb_color == RB_BLACK))
++ {
++ other->rb_color = RB_RED;
++ node = parent;
++ parent = node->rb_parent;
++ }
++ else
++ {
++ if (!other->rb_right ||
++ other->rb_right->rb_color == RB_BLACK)
++ {
++ register struct rb_node * o_left;
++ if ((o_left = other->rb_left))
++ o_left->rb_color = RB_BLACK;
++ other->rb_color = RB_RED;
++ __rb_rotate_right(other, root);
++ other = parent->rb_right;
++ }
++ other->rb_color = parent->rb_color;
++ parent->rb_color = RB_BLACK;
++ if (other->rb_right)
++ other->rb_right->rb_color = RB_BLACK;
++ __rb_rotate_left(parent, root);
++ node = root->rb_node;
++ break;
++ }
++ }
++ else
++ {
++ other = parent->rb_left;
++ if (other->rb_color == RB_RED)
++ {
++ other->rb_color = RB_BLACK;
++ parent->rb_color = RB_RED;
++ __rb_rotate_right(parent, root);
++ other = parent->rb_left;
++ }
++ if ((!other->rb_left ||
++ other->rb_left->rb_color == RB_BLACK)
++ && (!other->rb_right ||
++ other->rb_right->rb_color == RB_BLACK))
++ {
++ other->rb_color = RB_RED;
++ node = parent;
++ parent = node->rb_parent;
++ }
++ else
++ {
++ if (!other->rb_left ||
++ other->rb_left->rb_color == RB_BLACK)
++ {
++ register struct rb_node * o_right;
++ if ((o_right = other->rb_right))
++ o_right->rb_color = RB_BLACK;
++ other->rb_color = RB_RED;
++ __rb_rotate_left(other, root);
++ other = parent->rb_left;
++ }
++ other->rb_color = parent->rb_color;
++ parent->rb_color = RB_BLACK;
++ if (other->rb_left)
++ other->rb_left->rb_color = RB_BLACK;
++ __rb_rotate_right(parent, root);
++ node = root->rb_node;
++ break;
++ }
++ }
++ }
++ if (node)
++ node->rb_color = RB_BLACK;
++}
++
++void rb_erase(struct rb_node * node, struct rb_root * root)
++{
++ struct rb_node * child, * parent;
++ int color;
++
++ if (!node->rb_left)
++ child = node->rb_right;
++ else if (!node->rb_right)
++ child = node->rb_left;
++ else
++ {
++ struct rb_node * old = node, * left;
++
++ node = node->rb_right;
++ while ((left = node->rb_left))
++ node = left;
++ child = node->rb_right;
++ parent = node->rb_parent;
++ color = node->rb_color;
++
++ if (child)
++ child->rb_parent = parent;
++ if (parent)
++ {
++ if (parent->rb_left == node)
++ parent->rb_left = child;
++ else
++ parent->rb_right = child;
++ }
++ else
++ root->rb_node = child;
++
++ if (node->rb_parent == old)
++ parent = node;
++ node->rb_parent = old->rb_parent;
++ node->rb_color = old->rb_color;
++ node->rb_right = old->rb_right;
++ node->rb_left = old->rb_left;
++
++ if (old->rb_parent)
++ {
++ if (old->rb_parent->rb_left == old)
++ old->rb_parent->rb_left = node;
++ else
++ old->rb_parent->rb_right = node;
++ } else
++ root->rb_node = node;
++
++ old->rb_left->rb_parent = node;
++ if (old->rb_right)
++ old->rb_right->rb_parent = node;
++ goto color;
++ }
++
++ parent = node->rb_parent;
++ color = node->rb_color;
++
++ if (child)
++ child->rb_parent = parent;
++ if (parent)
++ {
++ if (parent->rb_left == node)
++ parent->rb_left = child;
++ else
++ parent->rb_right = child;
++ }
++ else
++ root->rb_node = child;
++
++ color:
++ if (color == RB_BLACK)
++ __rb_erase_color(child, parent, root);
++}
++#endif /* Before 2.4.11 */
++
++ /* These routines haven't made it into 2.4 (yet) */
++struct rb_node *rb_next(struct rb_node *node)
++{
++ /* If we have a right-hand child, go down and then left as far
++ as we can. */
++ if (node->rb_right) {
++ node = node->rb_right;
++ while (node->rb_left)
++ node=node->rb_left;
++ return node;
++ }
++
++ /* No right-hand children. Everything down and left is
++ smaller than us, so any 'next' node must be in the general
++ direction of our parent. Go up the tree; any time the
++ ancestor is a right-hand child of its parent, keep going
++ up. First time it's a left-hand child of its parent, said
++ parent is our 'next' node. */
++ while (node->rb_parent && node == node->rb_parent->rb_right)
++ node = node->rb_parent;
++
++ return node->rb_parent;
++}
++
++struct rb_node *rb_prev(struct rb_node *node)
++{
++ if (node->rb_left) {
++ node = node->rb_left;
++ while (node->rb_right)
++ node=node->rb_right;
++ return node;
++ }
++ while (node->rb_parent && node == node->rb_parent->rb_left)
++ node = node->rb_parent;
++
++ return node->rb_parent;
++}
++
++void rb_replace_node(struct rb_node *victim, struct rb_node *new, struct rb_root *root)
++{
++ struct rb_node *parent = victim->rb_parent;
++
++ /* Set the surrounding nodes to point to the replacement */
++ if (parent) {
++ if (victim == parent->rb_left)
++ parent->rb_left = new;
++ else
++ parent->rb_right = new;
++ } else {
++ root->rb_node = new;
++ }
++ if (victim->rb_left)
++ victim->rb_left->rb_parent = new;
++ if (victim->rb_right)
++ victim->rb_right->rb_parent = new;
++
++ /* Copy the pointers/colour from the victim to the replacement */
++ *new = *victim;
++}
+diff -Nurb linux-mips-2.4.27/fs/jffs2/read.c linux/fs/jffs2/read.c
+--- linux-mips-2.4.27/fs/jffs2/read.c 2003-11-17 02:07:44.000000000 +0100
++++ linux/fs/jffs2/read.c 2004-11-19 10:25:12.121166048 +0100
+@@ -1,52 +1,29 @@
+ /*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+- * Copyright (C) 2001 Red Hat, Inc.
++ * Copyright (C) 2001-2003 Red Hat, Inc.
+ *
+- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
++ * Created by David Woodhouse <dwmw2@redhat.com>
+ *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
++ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+- * The contents of this file are subject to the Red Hat eCos Public
+- * License Version 1.1 (the "Licence"); you may not use this file
+- * except in compliance with the Licence. You may obtain a copy of
+- * the Licence at http://www.redhat.com/
+- *
+- * Software distributed under the Licence is distributed on an "AS IS"
+- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+- * See the Licence for the specific language governing rights and
+- * limitations under the Licence.
+- *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
+- *
+- * Alternatively, the contents of this file may be used under the
+- * terms of the GNU General Public License version 2 (the "GPL"), in
+- * which case the provisions of the GPL are applicable instead of the
+- * above. If you wish to allow the use of your version of this file
+- * only under the terms of the GPL and not to allow others to use your
+- * version of this file under the RHEPL, indicate your decision by
+- * deleting the provisions above and replace them with the notice and
+- * other provisions required by the GPL. If you do not delete the
+- * provisions above, a recipient may use your version of this file
+- * under either the RHEPL or the GPL.
+- *
+- * $Id$
++ * $Id$
+ *
+ */
+
+ #include <linux/kernel.h>
+ #include <linux/slab.h>
+-#include <linux/jffs2.h>
++#include <linux/crc32.h>
++#include <linux/pagemap.h>
+ #include <linux/mtd/mtd.h>
++#include <linux/compiler.h>
+ #include "nodelist.h"
+-#include <linux/crc32.h>
+
+ int jffs2_read_dnode(struct jffs2_sb_info *c, struct jffs2_full_dnode *fd, unsigned char *buf, int ofs, int len)
+ {
+ struct jffs2_raw_inode *ri;
+ size_t readlen;
+- __u32 crc;
++ uint32_t crc;
+ unsigned char *decomprbuf = NULL;
+ unsigned char *readbuf = NULL;
+ int ret = 0;
+@@ -55,35 +32,41 @@
+ if (!ri)
+ return -ENOMEM;
+
+- ret = c->mtd->read(c->mtd, fd->raw->flash_offset & ~3, sizeof(*ri), &readlen, (char *)ri);
++ ret = jffs2_flash_read(c, ref_offset(fd->raw), sizeof(*ri), &readlen, (char *)ri);
+ if (ret) {
+ jffs2_free_raw_inode(ri);
+- printk(KERN_WARNING "Error reading node from 0x%08x: %d\n", fd->raw->flash_offset & ~3, ret);
++ printk(KERN_WARNING "Error reading node from 0x%08x: %d\n", ref_offset(fd->raw), ret);
+ return ret;
+ }
+ if (readlen != sizeof(*ri)) {
+ jffs2_free_raw_inode(ri);
+- printk(KERN_WARNING "Short read from 0x%08x: wanted 0x%x bytes, got 0x%x\n",
+- fd->raw->flash_offset & ~3, sizeof(*ri), readlen);
++ printk(KERN_WARNING "Short read from 0x%08x: wanted 0x%zx bytes, got 0x%zx\n",
++ ref_offset(fd->raw), sizeof(*ri), readlen);
+ return -EIO;
+ }
+ crc = crc32(0, ri, sizeof(*ri)-8);
+
+- D1(printk(KERN_DEBUG "Node read from %08x: node_crc %08x, calculated CRC %08x. dsize %x, csize %x, offset %x, buf %p\n", fd->raw->flash_offset & ~3, ri->node_crc, crc, ri->dsize, ri->csize, ri->offset, buf));
+- if (crc != ri->node_crc) {
+- printk(KERN_WARNING "Node CRC %08x != calculated CRC %08x for node at %08x\n", ri->node_crc, crc, fd->raw->flash_offset & ~3);
++ D1(printk(KERN_DEBUG "Node read from %08x: node_crc %08x, calculated CRC %08x. dsize %x, csize %x, offset %x, buf %p\n",
++ ref_offset(fd->raw), je32_to_cpu(ri->node_crc),
++ crc, je32_to_cpu(ri->dsize), je32_to_cpu(ri->csize),
++ je32_to_cpu(ri->offset), buf));
++ if (crc != je32_to_cpu(ri->node_crc)) {
++ printk(KERN_WARNING "Node CRC %08x != calculated CRC %08x for node at %08x\n",
++ je32_to_cpu(ri->node_crc), crc, ref_offset(fd->raw));
+ ret = -EIO;
+ goto out_ri;
+ }
+ /* There was a bug where we wrote hole nodes out with csize/dsize
+ swapped. Deal with it */
+- if (ri->compr == JFFS2_COMPR_ZERO && !ri->dsize && ri->csize) {
++ if (ri->compr == JFFS2_COMPR_ZERO && !je32_to_cpu(ri->dsize) &&
++ je32_to_cpu(ri->csize)) {
+ ri->dsize = ri->csize;
+- ri->csize = 0;
++ ri->csize = cpu_to_je32(0);
+ }
+
+- D1(if(ofs + len > ri->dsize) {
+- printk(KERN_WARNING "jffs2_read_dnode() asked for %d bytes at %d from %d-byte node\n", len, ofs, ri->dsize);
++ D1(if(ofs + len > je32_to_cpu(ri->dsize)) {
++ printk(KERN_WARNING "jffs2_read_dnode() asked for %d bytes at %d from %d-byte node\n",
++ len, ofs, je32_to_cpu(ri->dsize));
+ ret = -EINVAL;
+ goto out_ri;
+ });
+@@ -100,18 +83,18 @@
+ Reading partial node and it's uncompressed - read into readbuf, check CRC, and copy
+ Reading partial node and it's compressed - read into readbuf, check checksum, decompress to decomprbuf and copy
+ */
+- if (ri->compr == JFFS2_COMPR_NONE && len == ri->dsize) {
++ if (ri->compr == JFFS2_COMPR_NONE && len == je32_to_cpu(ri->dsize)) {
+ readbuf = buf;
+ } else {
+- readbuf = kmalloc(ri->csize, GFP_KERNEL);
++ readbuf = kmalloc(je32_to_cpu(ri->csize), GFP_KERNEL);
+ if (!readbuf) {
+ ret = -ENOMEM;
+ goto out_ri;
+ }
+ }
+ if (ri->compr != JFFS2_COMPR_NONE) {
+- if (len < ri->dsize) {
+- decomprbuf = kmalloc(ri->dsize, GFP_KERNEL);
++ if (len < je32_to_cpu(ri->dsize)) {
++ decomprbuf = kmalloc(je32_to_cpu(ri->dsize), GFP_KERNEL);
+ if (!decomprbuf) {
+ ret = -ENOMEM;
+ goto out_readbuf;
+@@ -123,31 +106,35 @@
+ decomprbuf = readbuf;
+ }
+
+- D2(printk(KERN_DEBUG "Read %d bytes to %p\n", ri->csize, readbuf));
+- ret = c->mtd->read(c->mtd, (fd->raw->flash_offset &~3) + sizeof(*ri), ri->csize, &readlen, readbuf);
++ D2(printk(KERN_DEBUG "Read %d bytes to %p\n", je32_to_cpu(ri->csize),
++ readbuf));
++ ret = jffs2_flash_read(c, (ref_offset(fd->raw)) + sizeof(*ri),
++ je32_to_cpu(ri->csize), &readlen, readbuf);
+
+- if (!ret && readlen != ri->csize)
++ if (!ret && readlen != je32_to_cpu(ri->csize))
+ ret = -EIO;
+ if (ret)
+ goto out_decomprbuf;
+
+- crc = crc32(0, readbuf, ri->csize);
+- if (crc != ri->data_crc) {
+- printk(KERN_WARNING "Data CRC %08x != calculated CRC %08x for node at %08x\n", ri->data_crc, crc, fd->raw->flash_offset & ~3);
++ crc = crc32(0, readbuf, je32_to_cpu(ri->csize));
++ if (crc != je32_to_cpu(ri->data_crc)) {
++ printk(KERN_WARNING "Data CRC %08x != calculated CRC %08x for node at %08x\n",
++ je32_to_cpu(ri->data_crc), crc, ref_offset(fd->raw));
+ ret = -EIO;
+ goto out_decomprbuf;
+ }
+ D2(printk(KERN_DEBUG "Data CRC matches calculated CRC %08x\n", crc));
+ if (ri->compr != JFFS2_COMPR_NONE) {
+- D2(printk(KERN_DEBUG "Decompress %d bytes from %p to %d bytes at %p\n", ri->csize, readbuf, ri->dsize, decomprbuf));
+- ret = jffs2_decompress(ri->compr, readbuf, decomprbuf, ri->csize, ri->dsize);
++ D2(printk(KERN_DEBUG "Decompress %d bytes from %p to %d bytes at %p\n",
++ je32_to_cpu(ri->csize), readbuf, je32_to_cpu(ri->dsize), decomprbuf));
++ ret = jffs2_decompress(ri->compr, readbuf, decomprbuf, je32_to_cpu(ri->csize), je32_to_cpu(ri->dsize));
+ if (ret) {
+ printk(KERN_WARNING "Error: jffs2_decompress returned %d\n", ret);
+ goto out_decomprbuf;
+ }
+ }
+
+- if (len < ri->dsize) {
++ if (len < je32_to_cpu(ri->dsize)) {
+ memcpy(buf, decomprbuf+ofs, len);
+ }
+ out_decomprbuf:
+@@ -161,3 +148,96 @@
+
+ return ret;
+ }
++
++int jffs2_read_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
++ unsigned char *buf, uint32_t offset, uint32_t len)
++{
++ uint32_t end = offset + len;
++ struct jffs2_node_frag *frag;
++ int ret;
++
++ D1(printk(KERN_DEBUG "jffs2_read_inode_range: ino #%u, range 0x%08x-0x%08x\n",
++ f->inocache->ino, offset, offset+len));
++
++ frag = jffs2_lookup_node_frag(&f->fragtree, offset);
++
++ /* XXX FIXME: Where a single physical node actually shows up in two
++ frags, we read it twice. Don't do that. */
++ /* Now we're pointing at the first frag which overlaps our page */
++ while(offset < end) {
++ D2(printk(KERN_DEBUG "jffs2_read_inode_range: offset %d, end %d\n", offset, end));
++ if (unlikely(!frag || frag->ofs > offset)) {
++ uint32_t holesize = end - offset;
++ if (frag) {
++ D1(printk(KERN_NOTICE "Eep. Hole in ino #%u fraglist. frag->ofs = 0x%08x, offset = 0x%08x\n", f->inocache->ino, frag->ofs, offset));
++ holesize = min(holesize, frag->ofs - offset);
++ D1(jffs2_print_frag_list(f));
++ }
++ D1(printk(KERN_DEBUG "Filling non-frag hole from %d-%d\n", offset, offset+holesize));
++ memset(buf, 0, holesize);
++ buf += holesize;
++ offset += holesize;
++ continue;
++ } else if (unlikely(!frag->node)) {
++ uint32_t holeend = min(end, frag->ofs + frag->size);
++ D1(printk(KERN_DEBUG "Filling frag hole from %d-%d (frag 0x%x 0x%x)\n", offset, holeend, frag->ofs, frag->ofs + frag->size));
++ memset(buf, 0, holeend - offset);
++ buf += holeend - offset;
++ offset = holeend;
++ frag = frag_next(frag);
++ continue;
++ } else {
++ uint32_t readlen;
++ uint32_t fragofs; /* offset within the frag to start reading */
++
++ fragofs = offset - frag->ofs;
++ readlen = min(frag->size - fragofs, end - offset);
++ D1(printk(KERN_DEBUG "Reading %d-%d from node at 0x%08x (%d)\n",
++ frag->ofs+fragofs, frag->ofs+fragofs+readlen,
++ ref_offset(frag->node->raw), ref_flags(frag->node->raw)));
++ ret = jffs2_read_dnode(c, frag->node, buf, fragofs + frag->ofs - frag->node->ofs, readlen);
++ D2(printk(KERN_DEBUG "node read done\n"));
++ if (ret) {
++ D1(printk(KERN_DEBUG"jffs2_read_inode_range error %d\n",ret));
++ memset(buf, 0, readlen);
++ return ret;
++ }
++ buf += readlen;
++ offset += readlen;
++ frag = frag_next(frag);
++ D2(printk(KERN_DEBUG "node read was OK. Looping\n"));
++ }
++ }
++ return 0;
++}
++
++/* Core function to read symlink target. */
++char *jffs2_getlink(struct jffs2_sb_info *c, struct jffs2_inode_info *f)
++{
++ char *buf;
++ int ret;
++
++ down(&f->sem);
++
++ if (!f->metadata) {
++ printk(KERN_NOTICE "No metadata for symlink inode #%u\n", f->inocache->ino);
++ up(&f->sem);
++ return ERR_PTR(-EINVAL);
++ }
++ buf = kmalloc(f->metadata->size+1, GFP_USER);
++ if (!buf) {
++ up(&f->sem);
++ return ERR_PTR(-ENOMEM);
++ }
++ buf[f->metadata->size]=0;
++
++ ret = jffs2_read_dnode(c, f->metadata, buf, 0, f->metadata->size);
++
++ up(&f->sem);
++
++ if (ret) {
++ kfree(buf);
++ return ERR_PTR(ret);
++ }
++ return buf;
++}
+diff -Nurb linux-mips-2.4.27/fs/jffs2/readinode.c linux/fs/jffs2/readinode.c
+--- linux-mips-2.4.27/fs/jffs2/readinode.c 2003-11-17 02:07:44.000000000 +0100
++++ linux/fs/jffs2/readinode.c 2004-11-19 10:25:12.123165744 +0100
+@@ -1,79 +1,122 @@
+ /*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+- * Copyright (C) 2001 Red Hat, Inc.
++ * Copyright (C) 2001-2003 Red Hat, Inc.
+ *
+- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
++ * Created by David Woodhouse <dwmw2@redhat.com>
+ *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
++ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+- * The contents of this file are subject to the Red Hat eCos Public
+- * License Version 1.1 (the "Licence"); you may not use this file
+- * except in compliance with the Licence. You may obtain a copy of
+- * the Licence at http://www.redhat.com/
+- *
+- * Software distributed under the Licence is distributed on an "AS IS"
+- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+- * See the Licence for the specific language governing rights and
+- * limitations under the Licence.
+- *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
+- *
+- * Alternatively, the contents of this file may be used under the
+- * terms of the GNU General Public License version 2 (the "GPL"), in
+- * which case the provisions of the GPL are applicable instead of the
+- * above. If you wish to allow the use of your version of this file
+- * only under the terms of the GPL and not to allow others to use your
+- * version of this file under the RHEPL, indicate your decision by
+- * deleting the provisions above and replace them with the notice and
+- * other provisions required by the GPL. If you do not delete the
+- * provisions above, a recipient may use your version of this file
+- * under either the RHEPL or the GPL.
+- *
+- * $Id$
++ * $Id$
+ *
+ */
+
+-/* Given an inode, probably with existing list of fragments, add the new node
+- * to the fragment list.
+- */
+ #include <linux/kernel.h>
+ #include <linux/slab.h>
+ #include <linux/fs.h>
++#include <linux/crc32.h>
++#include <linux/pagemap.h>
+ #include <linux/mtd/mtd.h>
+-#include <linux/jffs2.h>
++#include <linux/compiler.h>
+ #include "nodelist.h"
+-#include <linux/crc32.h>
+
++static int jffs2_add_frag_to_fragtree(struct jffs2_sb_info *c, struct rb_root *list, struct jffs2_node_frag *newfrag);
+
+-D1(void jffs2_print_frag_list(struct jffs2_inode_info *f)
++#if CONFIG_JFFS2_FS_DEBUG >= 1
++static void jffs2_print_fragtree(struct rb_root *list, int permitbug)
+ {
+- struct jffs2_node_frag *this = f->fraglist;
++ struct jffs2_node_frag *this = frag_first(list);
++ uint32_t lastofs = 0;
++ int buggy = 0;
+
+ while(this) {
+ if (this->node)
+- printk(KERN_DEBUG "frag %04x-%04x: 0x%08x on flash (*%p->%p)\n", this->ofs, this->ofs+this->size, this->node->raw->flash_offset &~3, this, this->next);
++ printk(KERN_DEBUG "frag %04x-%04x: 0x%08x(%d) on flash (*%p). left (%p), right (%p), parent (%p)\n",
++ this->ofs, this->ofs+this->size, ref_offset(this->node->raw), ref_flags(this->node->raw),
++ this, frag_left(this), frag_right(this), frag_parent(this));
+ else
+- printk(KERN_DEBUG "frag %04x-%04x: hole (*%p->%p)\n", this->ofs, this->ofs+this->size, this, this->next);
+- this = this->next;
++ printk(KERN_DEBUG "frag %04x-%04x: hole (*%p). left (%p} right (%p), parent (%p)\n", this->ofs,
++ this->ofs+this->size, this, frag_left(this), frag_right(this), frag_parent(this));
++ if (this->ofs != lastofs)
++ buggy = 1;
++ lastofs = this->ofs+this->size;
++ this = frag_next(this);
++ }
++ if (buggy && !permitbug) {
++ printk(KERN_CRIT "Frag tree got a hole in it\n");
++ BUG();
+ }
++}
++
++void jffs2_print_frag_list(struct jffs2_inode_info *f)
++{
++ jffs2_print_fragtree(&f->fragtree, 0);
++
+ if (f->metadata) {
+- printk(KERN_DEBUG "metadata at 0x%08x\n", f->metadata->raw->flash_offset &~3);
++ printk(KERN_DEBUG "metadata at 0x%08x\n", ref_offset(f->metadata->raw));
+ }
+-})
+-
++}
+
+-int jffs2_add_full_dnode_to_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_full_dnode *fn)
++static int jffs2_sanitycheck_fragtree(struct jffs2_inode_info *f)
+ {
+- int ret;
+- D1(printk(KERN_DEBUG "jffs2_add_full_dnode_to_inode(ino #%u, f %p, fn %p)\n", f->inocache->ino, f, fn));
++ struct jffs2_node_frag *frag;
++ int bitched = 0;
+
+- ret = jffs2_add_full_dnode_to_fraglist(c, &f->fraglist, fn);
++ for (frag = frag_first(&f->fragtree); frag; frag = frag_next(frag)) {
+
+- D2(jffs2_print_frag_list(f));
+- return ret;
++ struct jffs2_full_dnode *fn = frag->node;
++ if (!fn || !fn->raw)
++ continue;
++
++ if (ref_flags(fn->raw) == REF_PRISTINE) {
++
++ if (fn->frags > 1) {
++ printk(KERN_WARNING "REF_PRISTINE node at 0x%08x had %d frags. Tell dwmw2\n", ref_offset(fn->raw), fn->frags);
++ bitched = 1;
++ }
++ /* A hole node which isn't multi-page should be garbage-collected
++ and merged anyway, so we just check for the frag size here,
++ rather than mucking around with actually reading the node
++ and checking the compression type, which is the real way
++ to tell a hole node. */
++ if (frag->ofs & (PAGE_CACHE_SIZE-1) && frag_prev(frag) && frag_prev(frag)->size < PAGE_CACHE_SIZE && frag_prev(frag)->node) {
++ printk(KERN_WARNING "REF_PRISTINE node at 0x%08x had a previous non-hole frag in the same page. Tell dwmw2\n",
++ ref_offset(fn->raw));
++ bitched = 1;
++ }
++
++ if ((frag->ofs+frag->size) & (PAGE_CACHE_SIZE-1) && frag_next(frag) && frag_next(frag)->size < PAGE_CACHE_SIZE && frag_next(frag)->node) {
++ printk(KERN_WARNING "REF_PRISTINE node at 0x%08x (%08x-%08x) had a following non-hole frag in the same page. Tell dwmw2\n",
++ ref_offset(fn->raw), frag->ofs, frag->ofs+frag->size);
++ bitched = 1;
++ }
++ }
++ }
++
++ if (bitched) {
++ struct jffs2_node_frag *thisfrag;
++
++ printk(KERN_WARNING "Inode is #%u\n", f->inocache->ino);
++ thisfrag = frag_first(&f->fragtree);
++ while (thisfrag) {
++ if (!thisfrag->node) {
++ printk("Frag @0x%x-0x%x; node-less hole\n",
++ thisfrag->ofs, thisfrag->size + thisfrag->ofs);
++ } else if (!thisfrag->node->raw) {
++ printk("Frag @0x%x-0x%x; raw-less hole\n",
++ thisfrag->ofs, thisfrag->size + thisfrag->ofs);
++ } else {
++ printk("Frag @0x%x-0x%x; raw at 0x%08x(%d) (0x%x-0x%x)\n",
++ thisfrag->ofs, thisfrag->size + thisfrag->ofs,
++ ref_offset(thisfrag->node->raw), ref_flags(thisfrag->node->raw),
++ thisfrag->node->ofs, thisfrag->node->ofs+thisfrag->node->size);
++ }
++ thisfrag = frag_next(thisfrag);
++ }
++ }
++ return bitched;
+ }
++#endif /* D1 */
+
+ static void jffs2_obsolete_node_frag(struct jffs2_sb_info *c, struct jffs2_node_frag *this)
+ {
+@@ -82,42 +125,38 @@
+ if (!this->node->frags) {
+ /* The node has no valid frags left. It's totally obsoleted */
+ D2(printk(KERN_DEBUG "Marking old node @0x%08x (0x%04x-0x%04x) obsolete\n",
+- this->node->raw->flash_offset &~3, this->node->ofs, this->node->ofs+this->node->size));
++ ref_offset(this->node->raw), this->node->ofs, this->node->ofs+this->node->size));
+ jffs2_mark_node_obsolete(c, this->node->raw);
+ jffs2_free_full_dnode(this->node);
+ } else {
+- D2(printk(KERN_DEBUG "Not marking old node @0x%08x (0x%04x-0x%04x) obsolete. frags is %d\n",
+- this->node->raw->flash_offset &~3, this->node->ofs, this->node->ofs+this->node->size,
++ D2(printk(KERN_DEBUG "Marking old node @0x%08x (0x%04x-0x%04x) REF_NORMAL. frags is %d\n",
++ ref_offset(this->node->raw), this->node->ofs, this->node->ofs+this->node->size,
+ this->node->frags));
++ mark_ref_normal(this->node->raw);
+ }
+
+ }
+ jffs2_free_node_frag(this);
+ }
+
+-/* Doesn't set inode->i_size */
+-int jffs2_add_full_dnode_to_fraglist(struct jffs2_sb_info *c, struct jffs2_node_frag **list, struct jffs2_full_dnode *fn)
++/* Given an inode, probably with existing list of fragments, add the new node
++ * to the fragment list.
++ */
++int jffs2_add_full_dnode_to_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_full_dnode *fn)
+ {
++ int ret;
++ struct jffs2_node_frag *newfrag;
+
+- struct jffs2_node_frag *this, **prev, *old;
+- struct jffs2_node_frag *newfrag, *newfrag2;
+- __u32 lastend = 0;
+-
++ D1(printk(KERN_DEBUG "jffs2_add_full_dnode_to_inode(ino #%u, f %p, fn %p)\n", f->inocache->ino, f, fn));
+
+ newfrag = jffs2_alloc_node_frag();
+- if (!newfrag) {
++ if (unlikely(!newfrag))
+ return -ENOMEM;
+- }
+-
+- D2(if (fn->raw)
+- printk(KERN_DEBUG "adding node %04x-%04x @0x%08x on flash, newfrag *%p\n", fn->ofs, fn->ofs+fn->size, fn->raw->flash_offset &~3, newfrag);
+- else
+- printk(KERN_DEBUG "adding hole node %04x-%04x on flash, newfrag *%p\n", fn->ofs, fn->ofs+fn->size, newfrag));
+
+- prev = list;
+- this = *list;
++ D2(printk(KERN_DEBUG "adding node %04x-%04x @0x%08x on flash, newfrag *%p\n",
++ fn->ofs, fn->ofs+fn->size, ref_offset(fn->raw), newfrag));
+
+- if (!fn->size) {
++ if (unlikely(!fn->size)) {
+ jffs2_free_node_frag(newfrag);
+ return 0;
+ }
+@@ -126,176 +165,358 @@
+ newfrag->size = fn->size;
+ newfrag->node = fn;
+ newfrag->node->frags = 1;
+- newfrag->next = (void *)0xdeadbeef;
++
++ ret = jffs2_add_frag_to_fragtree(c, &f->fragtree, newfrag);
++ if (ret)
++ return ret;
++
++ /* If we now share a page with other nodes, mark either previous
++ or next node REF_NORMAL, as appropriate. */
++ if (newfrag->ofs & (PAGE_CACHE_SIZE-1)) {
++ struct jffs2_node_frag *prev = frag_prev(newfrag);
++
++ mark_ref_normal(fn->raw);
++ /* If we don't start at zero there's _always_ a previous */
++ if (prev->node)
++ mark_ref_normal(prev->node->raw);
++ }
++
++ if ((newfrag->ofs+newfrag->size) & (PAGE_CACHE_SIZE-1)) {
++ struct jffs2_node_frag *next = frag_next(newfrag);
++
++ if (next) {
++ mark_ref_normal(fn->raw);
++ if (next->node)
++ mark_ref_normal(next->node->raw);
++ }
++ }
++ D2(if (jffs2_sanitycheck_fragtree(f)) {
++ printk(KERN_WARNING "Just added node %04x-%04x @0x%08x on flash, newfrag *%p\n",
++ fn->ofs, fn->ofs+fn->size, ref_offset(fn->raw), newfrag);
++ return 0;
++ })
++ D2(jffs2_print_frag_list(f));
++ return 0;
++}
++
++/* Doesn't set inode->i_size */
++static int jffs2_add_frag_to_fragtree(struct jffs2_sb_info *c, struct rb_root *list, struct jffs2_node_frag *newfrag)
++{
++ struct jffs2_node_frag *this;
++ uint32_t lastend;
+
+ /* Skip all the nodes which are completed before this one starts */
+- while(this && fn->ofs >= this->ofs+this->size) {
+- lastend = this->ofs + this->size;
++ this = jffs2_lookup_node_frag(list, newfrag->node->ofs);
+
+- D2(printk(KERN_DEBUG "j_a_f_d_t_f: skipping frag 0x%04x-0x%04x; phys 0x%08x (*%p->%p)\n",
+- this->ofs, this->ofs+this->size, this->node?(this->node->raw->flash_offset &~3):0xffffffff, this, this->next));
+- prev = &this->next;
+- this = this->next;
++ if (this) {
++ D2(printk(KERN_DEBUG "j_a_f_d_t_f: Lookup gave frag 0x%04x-0x%04x; phys 0x%08x (*%p)\n",
++ this->ofs, this->ofs+this->size, this->node?(ref_offset(this->node->raw)):0xffffffff, this));
++ lastend = this->ofs + this->size;
++ } else {
++ D2(printk(KERN_DEBUG "j_a_f_d_t_f: Lookup gave no frag\n"));
++ lastend = 0;
+ }
+
+ /* See if we ran off the end of the list */
+- if (!this) {
++ if (lastend <= newfrag->ofs) {
+ /* We did */
+- if (lastend < fn->ofs) {
++
++ /* Check if 'this' node was on the same page as the new node.
++ If so, both 'this' and the new node get marked REF_NORMAL so
++ the GC can take a look.
++ */
++ if ((lastend-1) >> PAGE_CACHE_SHIFT == newfrag->ofs >> PAGE_CACHE_SHIFT) {
++ if (this->node)
++ mark_ref_normal(this->node->raw);
++ mark_ref_normal(newfrag->node->raw);
++ }
++
++ if (lastend < newfrag->node->ofs) {
+ /* ... and we need to put a hole in before the new node */
+ struct jffs2_node_frag *holefrag = jffs2_alloc_node_frag();
+- if (!holefrag)
++ if (!holefrag) {
++ jffs2_free_node_frag(newfrag);
+ return -ENOMEM;
++ }
+ holefrag->ofs = lastend;
+- holefrag->size = fn->ofs - lastend;
+- holefrag->next = NULL;
++ holefrag->size = newfrag->node->ofs - lastend;
+ holefrag->node = NULL;
+- *prev = holefrag;
+- prev = &holefrag->next;
++ if (this) {
++ /* By definition, the 'this' node has no right-hand child,
++ because there are no frags with offset greater than it.
++ So that's where we want to put the hole */
++ D2(printk(KERN_DEBUG "Adding hole frag (%p) on right of node at (%p)\n", holefrag, this));
++ rb_link_node(&holefrag->rb, &this->rb, &this->rb.rb_right);
++ } else {
++ D2(printk(KERN_DEBUG "Adding hole frag (%p) at root of tree\n", holefrag));
++ rb_link_node(&holefrag->rb, NULL, &list->rb_node);
++ }
++ rb_insert_color(&holefrag->rb, list);
++ this = holefrag;
+ }
+- newfrag->next = NULL;
+- *prev = newfrag;
++ if (this) {
++ /* By definition, the 'this' node has no right-hand child,
++ because there are no frags with offset greater than it.
++ So that's where we want to put the hole */
++ D2(printk(KERN_DEBUG "Adding new frag (%p) on right of node at (%p)\n", newfrag, this));
++ rb_link_node(&newfrag->rb, &this->rb, &this->rb.rb_right);
++ } else {
++ D2(printk(KERN_DEBUG "Adding new frag (%p) at root of tree\n", newfrag));
++ rb_link_node(&newfrag->rb, NULL, &list->rb_node);
++ }
++ rb_insert_color(&newfrag->rb, list);
+ return 0;
+ }
+
+- D2(printk(KERN_DEBUG "j_a_f_d_t_f: dealing with frag 0x%04x-0x%04x; phys 0x%08x (*%p->%p)\n",
+- this->ofs, this->ofs+this->size, this->node?(this->node->raw->flash_offset &~3):0xffffffff, this, this->next));
++ D2(printk(KERN_DEBUG "j_a_f_d_t_f: dealing with frag 0x%04x-0x%04x; phys 0x%08x (*%p)\n",
++ this->ofs, this->ofs+this->size, this->node?(ref_offset(this->node->raw)):0xffffffff, this));
+
+- /* OK. 'this' is pointing at the first frag that fn->ofs at least partially obsoletes,
+- * - i.e. fn->ofs < this->ofs+this->size && fn->ofs >= this->ofs
++ /* OK. 'this' is pointing at the first frag that newfrag->ofs at least partially obsoletes,
++ * - i.e. newfrag->ofs < this->ofs+this->size && newfrag->ofs >= this->ofs
+ */
+- if (fn->ofs > this->ofs) {
++ if (newfrag->ofs > this->ofs) {
+ /* This node isn't completely obsoleted. The start of it remains valid */
+- if (this->ofs + this->size > fn->ofs + fn->size) {
++
++ /* Mark the new node and the partially covered node REF_NORMAL -- let
++ the GC take a look at them */
++ mark_ref_normal(newfrag->node->raw);
++ if (this->node)
++ mark_ref_normal(this->node->raw);
++
++ if (this->ofs + this->size > newfrag->ofs + newfrag->size) {
+ /* The new node splits 'this' frag into two */
+- newfrag2 = jffs2_alloc_node_frag();
++ struct jffs2_node_frag *newfrag2 = jffs2_alloc_node_frag();
+ if (!newfrag2) {
+ jffs2_free_node_frag(newfrag);
+ return -ENOMEM;
+ }
+- D1(printk(KERN_DEBUG "split old frag 0x%04x-0x%04x -->", this->ofs, this->ofs+this->size);
++ D2(printk(KERN_DEBUG "split old frag 0x%04x-0x%04x -->", this->ofs, this->ofs+this->size);
+ if (this->node)
+- printk("phys 0x%08x\n", this->node->raw->flash_offset &~3);
++ printk("phys 0x%08x\n", ref_offset(this->node->raw));
+ else
+ printk("hole\n");
+ )
+- newfrag2->ofs = fn->ofs + fn->size;
++
++ /* New second frag pointing to this's node */
++ newfrag2->ofs = newfrag->ofs + newfrag->size;
+ newfrag2->size = (this->ofs+this->size) - newfrag2->ofs;
+- newfrag2->next = this->next;
+ newfrag2->node = this->node;
+ if (this->node)
+ this->node->frags++;
+- newfrag->next = newfrag2;
+- this->next = newfrag;
++
++ /* Adjust size of original 'this' */
+ this->size = newfrag->ofs - this->ofs;
++
++ /* Now, we know there's no node with offset
++ greater than this->ofs but smaller than
++ newfrag2->ofs or newfrag->ofs, for obvious
++ reasons. So we can do a tree insert from
++ 'this' to insert newfrag, and a tree insert
++ from newfrag to insert newfrag2. */
++ jffs2_fragtree_insert(newfrag, this);
++ rb_insert_color(&newfrag->rb, list);
++
++ jffs2_fragtree_insert(newfrag2, newfrag);
++ rb_insert_color(&newfrag2->rb, list);
++
+ return 0;
+ }
+ /* New node just reduces 'this' frag in size, doesn't split it */
+- this->size = fn->ofs - this->ofs;
+- newfrag->next = this->next;
+- this->next = newfrag;
+- this = newfrag->next;
++ this->size = newfrag->ofs - this->ofs;
++
++ /* Again, we know it lives down here in the tree */
++ jffs2_fragtree_insert(newfrag, this);
++ rb_insert_color(&newfrag->rb, list);
+ } else {
+- D2(printk(KERN_DEBUG "Inserting newfrag (*%p) in before 'this' (*%p)\n", newfrag, this));
+- *prev = newfrag;
+- newfrag->next = this;
++ /* New frag starts at the same point as 'this' used to. Replace
++ it in the tree without doing a delete and insertion */
++ D2(printk(KERN_DEBUG "Inserting newfrag (*%p),%d-%d in before 'this' (*%p),%d-%d\n",
++ newfrag, newfrag->ofs, newfrag->ofs+newfrag->size,
++ this, this->ofs, this->ofs+this->size));
++
++ rb_replace_node(&this->rb, &newfrag->rb, list);
++
++ if (newfrag->ofs + newfrag->size >= this->ofs+this->size) {
++ D2(printk(KERN_DEBUG "Obsoleting node frag %p (%x-%x)\n", this, this->ofs, this->ofs+this->size));
++ jffs2_obsolete_node_frag(c, this);
++ } else {
++ this->ofs += newfrag->size;
++ this->size -= newfrag->size;
++
++ jffs2_fragtree_insert(this, newfrag);
++ rb_insert_color(&this->rb, list);
++ return 0;
+ }
+- /* OK, now we have newfrag added in the correct place in the list, but
+- newfrag->next points to a fragment which may be overlapping it
++ }
++ /* OK, now we have newfrag added in the correct place in the tree, but
++ frag_next(newfrag) may be a fragment which is overlapped by it
+ */
+- while (this && newfrag->ofs + newfrag->size >= this->ofs + this->size) {
+- /* 'this' frag is obsoleted. */
+- old = this;
+- this = old->next;
+- jffs2_obsolete_node_frag(c, old);
++ while ((this = frag_next(newfrag)) && newfrag->ofs + newfrag->size >= this->ofs + this->size) {
++ /* 'this' frag is obsoleted completely. */
++ D2(printk(KERN_DEBUG "Obsoleting node frag %p (%x-%x) and removing from tree\n", this, this->ofs, this->ofs+this->size));
++ rb_erase(&this->rb, list);
++ jffs2_obsolete_node_frag(c, this);
+ }
+ /* Now we're pointing at the first frag which isn't totally obsoleted by
+ the new frag */
+- newfrag->next = this;
+
+ if (!this || newfrag->ofs + newfrag->size == this->ofs) {
+ return 0;
+ }
+- /* Still some overlap */
++ /* Still some overlap but we don't need to move it in the tree */
+ this->size = (this->ofs + this->size) - (newfrag->ofs + newfrag->size);
+ this->ofs = newfrag->ofs + newfrag->size;
++
++ /* And mark them REF_NORMAL so the GC takes a look at them */
++ if (this->node)
++ mark_ref_normal(this->node->raw);
++ mark_ref_normal(newfrag->node->raw);
++
+ return 0;
+ }
+
+-void jffs2_truncate_fraglist (struct jffs2_sb_info *c, struct jffs2_node_frag **list, __u32 size)
++void jffs2_truncate_fraglist (struct jffs2_sb_info *c, struct rb_root *list, uint32_t size)
+ {
++ struct jffs2_node_frag *frag = jffs2_lookup_node_frag(list, size);
++
+ D1(printk(KERN_DEBUG "Truncating fraglist to 0x%08x bytes\n", size));
+
+- while (*list) {
+- if ((*list)->ofs >= size) {
+- struct jffs2_node_frag *this = *list;
+- *list = this->next;
+- D1(printk(KERN_DEBUG "Removing frag 0x%08x-0x%08x\n", this->ofs, this->ofs+this->size));
+- jffs2_obsolete_node_frag(c, this);
+- continue;
+- } else if ((*list)->ofs + (*list)->size > size) {
+- D1(printk(KERN_DEBUG "Truncating frag 0x%08x-0x%08x\n", (*list)->ofs, (*list)->ofs + (*list)->size));
+- (*list)->size = size - (*list)->ofs;
+- }
+- list = &(*list)->next;
++ /* We know frag->ofs <= size. That's what lookup does for us */
++ if (frag && frag->ofs != size) {
++ if (frag->ofs+frag->size >= size) {
++ D1(printk(KERN_DEBUG "Truncating frag 0x%08x-0x%08x\n", frag->ofs, frag->ofs+frag->size));
++ frag->size = size - frag->ofs;
++ }
++ frag = frag_next(frag);
++ }
++ while (frag && frag->ofs >= size) {
++ struct jffs2_node_frag *next = frag_next(frag);
++
++ D1(printk(KERN_DEBUG "Removing frag 0x%08x-0x%08x\n", frag->ofs, frag->ofs+frag->size));
++ frag_erase(frag, list);
++ jffs2_obsolete_node_frag(c, frag);
++ frag = next;
+ }
+ }
+
+ /* Scan the list of all nodes present for this ino, build map of versions, etc. */
+
+-void jffs2_read_inode (struct inode *inode)
++static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c,
++ struct jffs2_inode_info *f,
++ struct jffs2_raw_inode *latest_node);
++
++int jffs2_do_read_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
++ uint32_t ino, struct jffs2_raw_inode *latest_node)
+ {
+- struct jffs2_tmp_dnode_info *tn_list, *tn;
+- struct jffs2_full_dirent *fd_list;
+- struct jffs2_inode_info *f;
+- struct jffs2_full_dnode *fn = NULL;
+- struct jffs2_sb_info *c;
+- struct jffs2_raw_inode latest_node;
+- __u32 latest_mctime, mctime_ver;
+- __u32 mdata_ver = 0;
+- int ret;
+- ssize_t retlen;
++ D2(printk(KERN_DEBUG "jffs2_do_read_inode(): getting inocache\n"));
+
+- D1(printk(KERN_DEBUG "jffs2_read_inode(): inode->i_ino == %lu\n", inode->i_ino));
++ retry_inocache:
++ spin_lock(&c->inocache_lock);
++ f->inocache = jffs2_get_ino_cache(c, ino);
++
++ D2(printk(KERN_DEBUG "jffs2_do_read_inode(): Got inocache at %p\n", f->inocache));
++
++ if (f->inocache) {
++ /* Check its state. We may need to wait before we can use it */
++ switch(f->inocache->state) {
++ case INO_STATE_UNCHECKED:
++ case INO_STATE_CHECKEDABSENT:
++ f->inocache->state = INO_STATE_READING;
++ break;
+
+- f = JFFS2_INODE_INFO(inode);
+- c = JFFS2_SB_INFO(inode->i_sb);
++ case INO_STATE_CHECKING:
++ case INO_STATE_GC:
++ /* If it's in either of these states, we need
++ to wait for whoever's got it to finish and
++ put it back. */
++ D1(printk(KERN_DEBUG "jffs2_get_ino_cache_read waiting for ino #%u in state %d\n",
++ ino, f->inocache->state));
++ sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock);
++ goto retry_inocache;
++
++ case INO_STATE_READING:
++ case INO_STATE_PRESENT:
++ /* Eep. This should never happen. It can
++ happen if Linux calls read_inode() again
++ before clear_inode() has finished though. */
++ printk(KERN_WARNING "Eep. Trying to read_inode #%u when it's already in state %d!\n", ino, f->inocache->state);
++ /* Fail. That's probably better than allowing it to succeed */
++ f->inocache = NULL;
++ break;
+
+- memset(f, 0, sizeof(*f));
+- D2(printk(KERN_DEBUG "getting inocache\n"));
+- init_MUTEX(&f->sem);
+- f->inocache = jffs2_get_ino_cache(c, inode->i_ino);
+- D2(printk(KERN_DEBUG "jffs2_read_inode(): Got inocache at %p\n", f->inocache));
++ default:
++ BUG();
++ }
++ }
++ spin_unlock(&c->inocache_lock);
+
+- if (!f->inocache && inode->i_ino == 1) {
++ if (!f->inocache && ino == 1) {
+ /* Special case - no root inode on medium */
+ f->inocache = jffs2_alloc_inode_cache();
+ if (!f->inocache) {
+- printk(KERN_CRIT "jffs2_read_inode(): Cannot allocate inocache for root inode\n");
+- make_bad_inode(inode);
+- return;
++ printk(KERN_CRIT "jffs2_do_read_inode(): Cannot allocate inocache for root inode\n");
++ return -ENOMEM;
+ }
+- D1(printk(KERN_DEBUG "jffs2_read_inode(): Creating inocache for root inode\n"));
++ D1(printk(KERN_DEBUG "jffs2_do_read_inode(): Creating inocache for root inode\n"));
+ memset(f->inocache, 0, sizeof(struct jffs2_inode_cache));
+ f->inocache->ino = f->inocache->nlink = 1;
+ f->inocache->nodes = (struct jffs2_raw_node_ref *)f->inocache;
++ f->inocache->state = INO_STATE_READING;
+ jffs2_add_ino_cache(c, f->inocache);
+ }
+ if (!f->inocache) {
+- printk(KERN_WARNING "jffs2_read_inode() on nonexistent ino %lu\n", (unsigned long)inode->i_ino);
+- make_bad_inode(inode);
+- return;
++ printk(KERN_WARNING "jffs2_do_read_inode() on nonexistent ino %u\n", ino);
++ return -ENOENT;
+ }
+- D1(printk(KERN_DEBUG "jffs2_read_inode(): ino #%lu nlink is %d\n", (unsigned long)inode->i_ino, f->inocache->nlink));
+- inode->i_nlink = f->inocache->nlink;
++
++ return jffs2_do_read_inode_internal(c, f, latest_node);
++}
++
++int jffs2_do_crccheck_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic)
++{
++ struct jffs2_raw_inode n;
++ struct jffs2_inode_info *f = kmalloc(sizeof(*f), GFP_KERNEL);
++ int ret;
++
++ if (!f)
++ return -ENOMEM;
++
++ memset(f, 0, sizeof(*f));
++ init_MUTEX_LOCKED(&f->sem);
++ f->inocache = ic;
++
++ ret = jffs2_do_read_inode_internal(c, f, &n);
++ if (!ret) {
++ up(&f->sem);
++ jffs2_do_clear_inode(c, f);
++ }
++ kfree (f);
++ return ret;
++}
++
++static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c,
++ struct jffs2_inode_info *f,
++ struct jffs2_raw_inode *latest_node)
++{
++ struct jffs2_tmp_dnode_info *tn_list, *tn;
++ struct jffs2_full_dirent *fd_list;
++ struct jffs2_full_dnode *fn = NULL;
++ uint32_t crc;
++ uint32_t latest_mctime, mctime_ver;
++ uint32_t mdata_ver = 0;
++ size_t retlen;
++ int ret;
++
++ D1(printk(KERN_DEBUG "jffs2_do_read_inode_internal(): ino #%u nlink is %d\n", f->inocache->ino, f->inocache->nlink));
+
+ /* Grab all nodes relevant to this ino */
+- ret = jffs2_get_inode_nodes(c, inode->i_ino, f, &tn_list, &fd_list, &f->highest_version, &latest_mctime, &mctime_ver);
++ ret = jffs2_get_inode_nodes(c, f->inocache->ino, f, &tn_list, &fd_list, &f->highest_version, &latest_mctime, &mctime_ver);
+
+ if (ret) {
+- printk(KERN_CRIT "jffs2_get_inode_nodes() for ino %lu returned %d\n", inode->i_ino, ret);
+- make_bad_inode(inode);
+- return;
++ printk(KERN_CRIT "jffs2_get_inode_nodes() for ino %u returned %d\n", f->inocache->ino, ret);
++ if (f->inocache->state == INO_STATE_READING)
++ jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT);
++ return ret;
+ }
+ f->dents = fd_list;
+
+@@ -304,205 +525,169 @@
+
+ fn = tn->fn;
+
+- if (f->metadata && tn->version > mdata_ver) {
+- D1(printk(KERN_DEBUG "Obsoleting old metadata at 0x%08x\n", f->metadata->raw->flash_offset &~3));
++ if (f->metadata) {
++ if (likely(tn->version >= mdata_ver)) {
++ D1(printk(KERN_DEBUG "Obsoleting old metadata at 0x%08x\n", ref_offset(f->metadata->raw)));
+ jffs2_mark_node_obsolete(c, f->metadata->raw);
+ jffs2_free_full_dnode(f->metadata);
+ f->metadata = NULL;
+
+ mdata_ver = 0;
++ } else {
++ /* This should never happen. */
++ printk(KERN_WARNING "Er. New metadata at 0x%08x with ver %d is actually older than previous ver %d at 0x%08x\n",
++ ref_offset(fn->raw), tn->version, mdata_ver, ref_offset(f->metadata->raw));
++ jffs2_mark_node_obsolete(c, fn->raw);
++ jffs2_free_full_dnode(fn);
++ /* Fill in latest_node from the metadata, not this one we're about to free... */
++ fn = f->metadata;
++ goto next_tn;
++ }
+ }
+
+ if (fn->size) {
+ jffs2_add_full_dnode_to_inode(c, f, fn);
+ } else {
+ /* Zero-sized node at end of version list. Just a metadata update */
+- D1(printk(KERN_DEBUG "metadata @%08x: ver %d\n", fn->raw->flash_offset &~3, tn->version));
++ D1(printk(KERN_DEBUG "metadata @%08x: ver %d\n", ref_offset(fn->raw), tn->version));
+ f->metadata = fn;
+ mdata_ver = tn->version;
+ }
++ next_tn:
+ tn_list = tn->next;
+ jffs2_free_tmp_dnode_info(tn);
+ }
++ D1(jffs2_sanitycheck_fragtree(f));
++
+ if (!fn) {
+ /* No data nodes for this inode. */
+- if (inode->i_ino != 1) {
+- printk(KERN_WARNING "jffs2_read_inode(): No data nodes found for ino #%lu\n", inode->i_ino);
++ if (f->inocache->ino != 1) {
++ printk(KERN_WARNING "jffs2_do_read_inode(): No data nodes found for ino #%u\n", f->inocache->ino);
+ if (!fd_list) {
+- make_bad_inode(inode);
+- return;
+- }
+- printk(KERN_WARNING "jffs2_read_inode(): But it has children so we fake some modes for it\n");
++ if (f->inocache->state == INO_STATE_READING)
++ jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT);
++ return -EIO;
++ }
++ printk(KERN_WARNING "jffs2_do_read_inode(): But it has children so we fake some modes for it\n");
++ }
++ latest_node->mode = cpu_to_jemode(S_IFDIR|S_IRUGO|S_IWUSR|S_IXUGO);
++ latest_node->version = cpu_to_je32(0);
++ latest_node->atime = latest_node->ctime = latest_node->mtime = cpu_to_je32(0);
++ latest_node->isize = cpu_to_je32(0);
++ latest_node->gid = cpu_to_je16(0);
++ latest_node->uid = cpu_to_je16(0);
++ if (f->inocache->state == INO_STATE_READING)
++ jffs2_set_inocache_state(c, f->inocache, INO_STATE_PRESENT);
++ return 0;
+ }
+- inode->i_mode = S_IFDIR | S_IRUGO | S_IWUSR | S_IXUGO;
+- latest_node.version = 0;
+- inode->i_atime = inode->i_ctime = inode->i_mtime = CURRENT_TIME;
+- inode->i_nlink = f->inocache->nlink;
+- inode->i_size = 0;
+- } else {
+- __u32 crc;
+
+- ret = c->mtd->read(c->mtd, fn->raw->flash_offset & ~3, sizeof(latest_node), &retlen, (void *)&latest_node);
+- if (ret || retlen != sizeof(latest_node)) {
+- printk(KERN_NOTICE "MTD read in jffs2_read_inode() failed: Returned %d, %ld of %d bytes read\n",
+- ret, (long)retlen, sizeof(latest_node));
+- jffs2_clear_inode(inode);
+- make_bad_inode(inode);
+- return;
+- }
+-
+- crc = crc32(0, &latest_node, sizeof(latest_node)-8);
+- if (crc != latest_node.node_crc) {
+- printk(KERN_NOTICE "CRC failed for read_inode of inode %ld at physical location 0x%x\n", inode->i_ino, fn->raw->flash_offset & ~3);
+- jffs2_clear_inode(inode);
+- make_bad_inode(inode);
+- return;
+- }
+-
+- inode->i_mode = latest_node.mode;
+- inode->i_uid = latest_node.uid;
+- inode->i_gid = latest_node.gid;
+- inode->i_size = latest_node.isize;
+- if (S_ISREG(inode->i_mode))
+- jffs2_truncate_fraglist(c, &f->fraglist, latest_node.isize);
+- inode->i_atime = latest_node.atime;
+- inode->i_mtime = latest_node.mtime;
+- inode->i_ctime = latest_node.ctime;
+- }
+-
+- /* OK, now the special cases. Certain inode types should
+- have only one data node, and it's kept as the metadata
+- node */
+- if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode) ||
+- S_ISLNK(inode->i_mode)) {
+- if (f->metadata) {
+- printk(KERN_WARNING "Argh. Special inode #%lu with mode 0%o had metadata node\n", inode->i_ino, inode->i_mode);
+- jffs2_clear_inode(inode);
+- make_bad_inode(inode);
+- return;
+- }
+- if (!f->fraglist) {
+- printk(KERN_WARNING "Argh. Special inode #%lu with mode 0%o has no fragments\n", inode->i_ino, inode->i_mode);
+- jffs2_clear_inode(inode);
+- make_bad_inode(inode);
+- return;
+- }
+- /* ASSERT: f->fraglist != NULL */
+- if (f->fraglist->next) {
+- printk(KERN_WARNING "Argh. Special inode #%lu with mode 0%o had more than one node\n", inode->i_ino, inode->i_mode);
+- /* FIXME: Deal with it - check crc32, check for duplicate node, check times and discard the older one */
+- jffs2_clear_inode(inode);
+- make_bad_inode(inode);
+- return;
+- }
+- /* OK. We're happy */
+- f->metadata = f->fraglist->node;
+- jffs2_free_node_frag(f->fraglist);
+- f->fraglist = NULL;
++ ret = jffs2_flash_read(c, ref_offset(fn->raw), sizeof(*latest_node), &retlen, (void *)latest_node);
++ if (ret || retlen != sizeof(*latest_node)) {
++ printk(KERN_NOTICE "MTD read in jffs2_do_read_inode() failed: Returned %d, %zd of %zd bytes read\n",
++ ret, retlen, sizeof(*latest_node));
++ /* FIXME: If this fails, there seems to be a memory leak. Find it. */
++ up(&f->sem);
++ jffs2_do_clear_inode(c, f);
++ return ret?ret:-EIO;
+ }
+
+- inode->i_blksize = PAGE_SIZE;
+- inode->i_blocks = (inode->i_size + 511) >> 9;
+-
+- switch (inode->i_mode & S_IFMT) {
+- unsigned short rdev;
+-
+- case S_IFLNK:
+- inode->i_op = &jffs2_symlink_inode_operations;
+- /* Hack to work around broken isize in old symlink code.
+- Remove this when dwmw2 comes to his senses and stops
+- symlinks from being an entirely gratuitous special
+- case. */
+- if (!inode->i_size)
+- inode->i_size = latest_node.dsize;
+- break;
++ crc = crc32(0, latest_node, sizeof(*latest_node)-8);
++ if (crc != je32_to_cpu(latest_node->node_crc)) {
++ printk(KERN_NOTICE "CRC failed for read_inode of inode %u at physical location 0x%x\n", f->inocache->ino, ref_offset(fn->raw));
++ up(&f->sem);
++ jffs2_do_clear_inode(c, f);
++ return -EIO;
++ }
+
++ switch(jemode_to_cpu(latest_node->mode) & S_IFMT) {
+ case S_IFDIR:
+- if (mctime_ver > latest_node.version) {
++ if (mctime_ver > je32_to_cpu(latest_node->version)) {
+ /* The times in the latest_node are actually older than
+ mctime in the latest dirent. Cheat. */
+- inode->i_mtime = inode->i_ctime = inode->i_atime =
+- latest_mctime;
++ latest_node->ctime = latest_node->mtime = cpu_to_je32(latest_mctime);
+ }
+- inode->i_op = &jffs2_dir_inode_operations;
+- inode->i_fop = &jffs2_dir_operations;
+ break;
+
++
+ case S_IFREG:
+- inode->i_op = &jffs2_file_inode_operations;
+- inode->i_fop = &jffs2_file_operations;
+- inode->i_mapping->a_ops = &jffs2_file_address_operations;
+- inode->i_mapping->nrpages = 0;
++ /* If it was a regular file, truncate it to the latest node's isize */
++ jffs2_truncate_fraglist(c, &f->fragtree, je32_to_cpu(latest_node->isize));
+ break;
+
++ case S_IFLNK:
++ /* Hack to work around broken isize in old symlink code.
++ Remove this when dwmw2 comes to his senses and stops
++ symlinks from being an entirely gratuitous special
++ case. */
++ if (!je32_to_cpu(latest_node->isize))
++ latest_node->isize = latest_node->dsize;
++ /* fall through... */
++
+ case S_IFBLK:
+ case S_IFCHR:
+- /* Read the device numbers from the media */
+- D1(printk(KERN_DEBUG "Reading device numbers from flash\n"));
+- if (jffs2_read_dnode(c, f->metadata, (char *)&rdev, 0, sizeof(rdev)) < 0) {
+- /* Eep */
+- printk(KERN_NOTICE "Read device numbers for inode %lu failed\n", (unsigned long)inode->i_ino);
+- jffs2_clear_inode(inode);
+- make_bad_inode(inode);
+- return;
++ /* Certain inode types should have only one data node, and it's
++ kept as the metadata node */
++ if (f->metadata) {
++ printk(KERN_WARNING "Argh. Special inode #%u with mode 0%o had metadata node\n",
++ f->inocache->ino, jemode_to_cpu(latest_node->mode));
++ up(&f->sem);
++ jffs2_do_clear_inode(c, f);
++ return -EIO;
+ }
+-
+- case S_IFSOCK:
+- case S_IFIFO:
+- inode->i_op = &jffs2_file_inode_operations;
+- init_special_inode(inode, inode->i_mode, kdev_t_to_nr(MKDEV(rdev>>8, rdev&0xff)));
++ if (!frag_first(&f->fragtree)) {
++ printk(KERN_WARNING "Argh. Special inode #%u with mode 0%o has no fragments\n",
++ f->inocache->ino, jemode_to_cpu(latest_node->mode));
++ up(&f->sem);
++ jffs2_do_clear_inode(c, f);
++ return -EIO;
++ }
++ /* ASSERT: f->fraglist != NULL */
++ if (frag_next(frag_first(&f->fragtree))) {
++ printk(KERN_WARNING "Argh. Special inode #%u with mode 0x%x had more than one node\n",
++ f->inocache->ino, jemode_to_cpu(latest_node->mode));
++ /* FIXME: Deal with it - check crc32, check for duplicate node, check times and discard the older one */
++ up(&f->sem);
++ jffs2_do_clear_inode(c, f);
++ return -EIO;
++ }
++ /* OK. We're happy */
++ f->metadata = frag_first(&f->fragtree)->node;
++ jffs2_free_node_frag(frag_first(&f->fragtree));
++ f->fragtree = RB_ROOT;
+ break;
+-
+- default:
+- printk(KERN_WARNING "jffs2_read_inode(): Bogus imode %o for ino %lu", inode->i_mode, (unsigned long)inode->i_ino);
+ }
+- D1(printk(KERN_DEBUG "jffs2_read_inode() returning\n"));
++ if (f->inocache->state == INO_STATE_READING)
++ jffs2_set_inocache_state(c, f->inocache, INO_STATE_PRESENT);
++
++ return 0;
+ }
+
+-void jffs2_clear_inode (struct inode *inode)
++void jffs2_do_clear_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f)
+ {
+- /* We can forget about this inode for now - drop all
+- * the nodelists associated with it, etc.
+- */
+- struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
+- struct jffs2_node_frag *frag, *frags;
+ struct jffs2_full_dirent *fd, *fds;
+- struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
+ int deleted;
+
+- D1(printk(KERN_DEBUG "jffs2_clear_inode(): ino #%lu mode %o\n", inode->i_ino, inode->i_mode));
+-
+ down(&f->sem);
+ deleted = f->inocache && !f->inocache->nlink;
+
+- frags = f->fraglist;
+- fds = f->dents;
+ if (f->metadata) {
+ if (deleted)
+ jffs2_mark_node_obsolete(c, f->metadata->raw);
+ jffs2_free_full_dnode(f->metadata);
+ }
+
+- while (frags) {
+- frag = frags;
+- frags = frag->next;
+- D2(printk(KERN_DEBUG "jffs2_clear_inode: frag at 0x%x-0x%x: node %p, frags %d--\n", frag->ofs, frag->ofs+frag->size, frag->node, frag->node?frag->node->frags:0));
++ jffs2_kill_fragtree(&f->fragtree, deleted?c:NULL);
+
+- if (frag->node && !(--frag->node->frags)) {
+- /* Not a hole, and it's the final remaining frag of this node. Free the node */
+- if (deleted)
+- jffs2_mark_node_obsolete(c, frag->node->raw);
++ fds = f->dents;
+
+- jffs2_free_full_dnode(frag->node);
+- }
+- jffs2_free_node_frag(frag);
+- }
+ while(fds) {
+ fd = fds;
+ fds = fd->next;
+ jffs2_free_full_dirent(fd);
+ }
+
+- up(&f->sem);
+-};
++ if (f->inocache && f->inocache->state != INO_STATE_CHECKING)
++ jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT);
+
++ up(&f->sem);
++}
+diff -Nurb linux-mips-2.4.27/fs/jffs2/scan.c linux/fs/jffs2/scan.c
+--- linux-mips-2.4.27/fs/jffs2/scan.c 2003-11-17 02:07:44.000000000 +0100
++++ linux/fs/jffs2/scan.c 2004-11-19 10:25:12.124165592 +0100
+@@ -1,47 +1,25 @@
+ /*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+- * Copyright (C) 2001 Red Hat, Inc.
++ * Copyright (C) 2001-2003 Red Hat, Inc.
+ *
+- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
++ * Created by David Woodhouse <dwmw2@redhat.com>
+ *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
++ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+- * The contents of this file are subject to the Red Hat eCos Public
+- * License Version 1.1 (the "Licence"); you may not use this file
+- * except in compliance with the Licence. You may obtain a copy of
+- * the Licence at http://www.redhat.com/
+- *
+- * Software distributed under the Licence is distributed on an "AS IS"
+- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+- * See the Licence for the specific language governing rights and
+- * limitations under the Licence.
+- *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
+- *
+- * Alternatively, the contents of this file may be used under the
+- * terms of the GNU General Public License version 2 (the "GPL"), in
+- * which case the provisions of the GPL are applicable instead of the
+- * above. If you wish to allow the use of your version of this file
+- * only under the terms of the GPL and not to allow others to use your
+- * version of this file under the RHEPL, indicate your decision by
+- * deleting the provisions above and replace them with the notice and
+- * other provisions required by the GPL. If you do not delete the
+- * provisions above, a recipient may use your version of this file
+- * under either the RHEPL or the GPL.
+- *
+- * $Id$
++ * $Id$
+ *
+ */
+ #include <linux/kernel.h>
++#include <linux/sched.h>
+ #include <linux/slab.h>
+-#include <linux/jffs2.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/pagemap.h>
+-#include "nodelist.h"
+ #include <linux/crc32.h>
++#include <linux/compiler.h>
++#include "nodelist.h"
+
++#define EMPTY_SCAN_SIZE 1024
+
+ #define DIRTY_SPACE(x) do { typeof(x) _x = (x); \
+ c->free_size -= _x; c->dirty_size += _x; \
+@@ -51,6 +29,10 @@
+ c->free_size -= _x; c->used_size += _x; \
+ jeb->free_size -= _x ; jeb->used_size += _x; \
+ }while(0)
++#define UNCHECKED_SPACE(x) do { typeof(x) _x = (x); \
++ c->free_size -= _x; c->unchecked_size += _x; \
++ jeb->free_size -= _x ; jeb->unchecked_size += _x; \
++ }while(0)
+
+ #define noisy_printk(noise, args...) do { \
+ if (*(noise)) { \
+@@ -63,39 +45,84 @@
+ } while(0)
+
+ static uint32_t pseudo_random;
+-static void jffs2_rotate_lists(struct jffs2_sb_info *c);
+
+-static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
++static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
++ unsigned char *buf, uint32_t buf_size);
+
+ /* These helper functions _must_ increase ofs and also do the dirty/used space accounting.
+ * Returning an error will abort the mount - bad checksums etc. should just mark the space
+ * as dirty.
+ */
+-static int jffs2_scan_empty(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, __u32 *ofs, int *noise);
+-static int jffs2_scan_inode_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, __u32 *ofs);
+-static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, __u32 *ofs);
++static int jffs2_scan_inode_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
++ struct jffs2_raw_inode *ri, uint32_t ofs);
++static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
++ struct jffs2_raw_dirent *rd, uint32_t ofs);
++
++#define BLK_STATE_ALLFF 0
++#define BLK_STATE_CLEAN 1
++#define BLK_STATE_PARTDIRTY 2
++#define BLK_STATE_CLEANMARKER 3
++#define BLK_STATE_ALLDIRTY 4
++#define BLK_STATE_BADBLOCK 5
+
++static inline int min_free(struct jffs2_sb_info *c)
++{
++ uint32_t min = 2 * sizeof(struct jffs2_raw_inode);
++#ifdef CONFIG_JFFS2_FS_NAND
++ if (!jffs2_can_mark_obsolete(c) && min < c->wbuf_pagesize)
++ return c->wbuf_pagesize;
++#endif
++ return min;
+
++}
+ int jffs2_scan_medium(struct jffs2_sb_info *c)
+ {
+ int i, ret;
+- __u32 empty_blocks = 0;
+-
+- if (!c->blocks) {
+- printk(KERN_WARNING "EEEK! c->blocks is NULL!\n");
+- return -EINVAL;
++ uint32_t empty_blocks = 0, bad_blocks = 0;
++ unsigned char *flashbuf = NULL;
++ uint32_t buf_size = 0;
++#ifndef __ECOS
++ size_t pointlen;
++
++ if (c->mtd->point) {
++ ret = c->mtd->point (c->mtd, 0, c->mtd->size, &pointlen, &flashbuf);
++ if (!ret && pointlen < c->mtd->size) {
++ /* Don't muck about if it won't let us point to the whole flash */
++ D1(printk(KERN_DEBUG "MTD point returned len too short: 0x%zx\n", pointlen));
++ c->mtd->unpoint(c->mtd, flashbuf, 0, c->mtd->size);
++ flashbuf = NULL;
++ }
++ if (ret)
++ D1(printk(KERN_DEBUG "MTD point failed %d\n", ret));
++ }
++#endif
++ if (!flashbuf) {
++ /* For NAND it's quicker to read a whole eraseblock at a time,
++ apparently */
++ if (jffs2_cleanmarker_oob(c))
++ buf_size = c->sector_size;
++ else
++ buf_size = PAGE_SIZE;
++
++ D1(printk(KERN_DEBUG "Allocating readbuf of %d bytes\n", buf_size));
++ flashbuf = kmalloc(buf_size, GFP_KERNEL);
++ if (!flashbuf)
++ return -ENOMEM;
+ }
++
+ for (i=0; i<c->nr_blocks; i++) {
+ struct jffs2_eraseblock *jeb = &c->blocks[i];
+
+- ret = jffs2_scan_eraseblock(c, jeb);
++ ret = jffs2_scan_eraseblock(c, jeb, buf_size?flashbuf:(flashbuf+jeb->offset), buf_size);
++
+ if (ret < 0)
+- return ret;
++ goto out;
+
+ ACCT_PARANOIA_CHECK(jeb);
+
+ /* Now decide which list to put it on */
+- if (ret == 1) {
++ switch(ret) {
++ case BLK_STATE_ALLFF:
+ /*
+ * Empty block. Since we can't be sure it
+ * was entirely erased, we just queue it for erase
+@@ -103,10 +130,12 @@
+ * is complete. Meanwhile we still count it as empty
+ * for later checks.
+ */
+- list_add(&jeb->list, &c->erase_pending_list);
+ empty_blocks++;
++ list_add(&jeb->list, &c->erase_pending_list);
+ c->nr_erasing_blocks++;
+- } else if (jeb->used_size == PAD(sizeof(struct jffs2_unknown_node)) && !jeb->first_node->next_in_ino) {
++ break;
++
++ case BLK_STATE_CLEANMARKER:
+ /* Only a CLEANMARKER node is valid */
+ if (!jeb->dirty_size) {
+ /* It's actually free */
+@@ -118,74 +147,227 @@
+ list_add(&jeb->list, &c->erase_pending_list);
+ c->nr_erasing_blocks++;
+ }
+- } else if (jeb->used_size > c->sector_size - (2*sizeof(struct jffs2_raw_inode))) {
++ break;
++
++ case BLK_STATE_CLEAN:
+ /* Full (or almost full) of clean data. Clean list */
+ list_add(&jeb->list, &c->clean_list);
+- } else if (jeb->used_size) {
++ break;
++
++ case BLK_STATE_PARTDIRTY:
+ /* Some data, but not full. Dirty list. */
+ /* Except that we want to remember the block with most free space,
+ and stick it in the 'nextblock' position to start writing to it.
+ Later when we do snapshots, this must be the most recent block,
+ not the one with most free space.
+ */
+- if (jeb->free_size > 2*sizeof(struct jffs2_raw_inode) &&
++ if (jeb->free_size > min_free(c) &&
+ (!c->nextblock || c->nextblock->free_size < jeb->free_size)) {
+ /* Better candidate for the next writes to go to */
+- if (c->nextblock)
++ if (c->nextblock) {
++ c->nextblock->dirty_size += c->nextblock->free_size + c->nextblock->wasted_size;
++ c->dirty_size += c->nextblock->free_size + c->nextblock->wasted_size;
++ c->free_size -= c->nextblock->free_size;
++ c->wasted_size -= c->nextblock->wasted_size;
++ c->nextblock->free_size = c->nextblock->wasted_size = 0;
++ if (VERYDIRTY(c, c->nextblock->dirty_size)) {
++ list_add(&c->nextblock->list, &c->very_dirty_list);
++ } else {
+ list_add(&c->nextblock->list, &c->dirty_list);
++ }
++ }
+ c->nextblock = jeb;
+ } else {
++ jeb->dirty_size += jeb->free_size + jeb->wasted_size;
++ c->dirty_size += jeb->free_size + jeb->wasted_size;
++ c->free_size -= jeb->free_size;
++ c->wasted_size -= jeb->wasted_size;
++ jeb->free_size = jeb->wasted_size = 0;
++ if (VERYDIRTY(c, jeb->dirty_size)) {
++ list_add(&jeb->list, &c->very_dirty_list);
++ } else {
+ list_add(&jeb->list, &c->dirty_list);
+ }
+- } else {
++ }
++ break;
++
++ case BLK_STATE_ALLDIRTY:
+ /* Nothing valid - not even a clean marker. Needs erasing. */
+ /* For now we just put it on the erasing list. We'll start the erases later */
+- printk(KERN_NOTICE "JFFS2: Erase block at 0x%08x is not formatted. It will be erased\n", jeb->offset);
++ D1(printk(KERN_NOTICE "JFFS2: Erase block at 0x%08x is not formatted. It will be erased\n", jeb->offset));
+ list_add(&jeb->list, &c->erase_pending_list);
+ c->nr_erasing_blocks++;
++ break;
++
++ case BLK_STATE_BADBLOCK:
++ D1(printk(KERN_NOTICE "JFFS2: Block at 0x%08x is bad\n", jeb->offset));
++ list_add(&jeb->list, &c->bad_list);
++ c->bad_size += c->sector_size;
++ c->free_size -= c->sector_size;
++ bad_blocks++;
++ break;
++ default:
++ printk(KERN_WARNING "jffs2_scan_medium(): unknown block state\n");
++ BUG();
+ }
+ }
+- /* Rotate the lists by some number to ensure wear levelling */
+- jffs2_rotate_lists(c);
+
++ /* Nextblock dirty is always seen as wasted, because we cannot recycle it now */
++ if (c->nextblock && (c->nextblock->dirty_size)) {
++ c->nextblock->wasted_size += c->nextblock->dirty_size;
++ c->wasted_size += c->nextblock->dirty_size;
++ c->dirty_size -= c->nextblock->dirty_size;
++ c->nextblock->dirty_size = 0;
++ }
++#ifdef CONFIG_JFFS2_FS_NAND
++ if (!jffs2_can_mark_obsolete(c) && c->nextblock && (c->nextblock->free_size & (c->wbuf_pagesize-1))) {
++ /* If we're going to start writing into a block which already
++ contains data, and the end of the data isn't page-aligned,
++ skip a little and align it. */
++
++ uint32_t skip = c->nextblock->free_size & (c->wbuf_pagesize-1);
++
++ D1(printk(KERN_DEBUG "jffs2_scan_medium(): Skipping %d bytes in nextblock to ensure page alignment\n",
++ skip));
++ c->nextblock->wasted_size += skip;
++ c->wasted_size += skip;
++
++ c->nextblock->free_size -= skip;
++ c->free_size -= skip;
++ }
++#endif
+ if (c->nr_erasing_blocks) {
+- if (!c->used_size && empty_blocks != c->nr_blocks) {
++ if ( !c->used_size && ((empty_blocks+bad_blocks)!= c->nr_blocks || bad_blocks == c->nr_blocks) ) {
+ printk(KERN_NOTICE "Cowardly refusing to erase blocks on filesystem with no valid JFFS2 nodes\n");
+- return -EIO;
++ printk(KERN_NOTICE "empty_blocks %d, bad_blocks %d, c->nr_blocks %d\n",empty_blocks,bad_blocks,c->nr_blocks);
++ ret = -EIO;
++ goto out;
+ }
+ jffs2_erase_pending_trigger(c);
+ }
++ ret = 0;
++ out:
++ if (buf_size)
++ kfree(flashbuf);
++#ifndef __ECOS
++ else
++ c->mtd->unpoint(c->mtd, flashbuf, 0, c->mtd->size);
++#endif
++ return ret;
++}
++
++static int jffs2_fill_scan_buf (struct jffs2_sb_info *c, unsigned char *buf,
++ uint32_t ofs, uint32_t len)
++{
++ int ret;
++ size_t retlen;
++
++ ret = jffs2_flash_read(c, ofs, len, &retlen, buf);
++ if (ret) {
++ D1(printk(KERN_WARNING "mtd->read(0x%x bytes from 0x%x) returned %d\n", len, ofs, ret));
++ return ret;
++ }
++ if (retlen < len) {
++ D1(printk(KERN_WARNING "Read at 0x%x gave only 0x%zx bytes\n", ofs, retlen));
++ return -EIO;
++ }
++ D2(printk(KERN_DEBUG "Read 0x%x bytes from 0x%08x into buf\n", len, ofs));
++ D2(printk(KERN_DEBUG "000: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
++ buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]));
+ return 0;
+ }
+
+-static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) {
+- struct jffs2_unknown_node node;
+- __u32 ofs, prevofs;
+- __u32 hdr_crc, nodetype;
++static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
++ unsigned char *buf, uint32_t buf_size) {
++ struct jffs2_unknown_node *node;
++ struct jffs2_unknown_node crcnode;
++ uint32_t ofs, prevofs;
++ uint32_t hdr_crc, buf_ofs, buf_len;
+ int err;
+ int noise = 0;
++ int wasempty = 0;
++ uint32_t empty_start = 0;
++#ifdef CONFIG_JFFS2_FS_NAND
++ int cleanmarkerfound = 0;
++#endif
+
+ ofs = jeb->offset;
+ prevofs = jeb->offset - 1;
+
+ D1(printk(KERN_DEBUG "jffs2_scan_eraseblock(): Scanning block at 0x%x\n", ofs));
+
+- err = jffs2_scan_empty(c, jeb, &ofs, &noise);
+- if (err) return err;
+- if (ofs == jeb->offset + c->sector_size) {
++#ifdef CONFIG_JFFS2_FS_NAND
++ if (jffs2_cleanmarker_oob(c)) {
++ int ret = jffs2_check_nand_cleanmarker(c, jeb);
++ D2(printk(KERN_NOTICE "jffs_check_nand_cleanmarker returned %d\n",ret));
++ /* Even if it's not found, we still scan to see
++ if the block is empty. We use this information
++ to decide whether to erase it or not. */
++ switch (ret) {
++ case 0: cleanmarkerfound = 1; break;
++ case 1: break;
++ case 2: return BLK_STATE_BADBLOCK;
++ case 3: return BLK_STATE_ALLDIRTY; /* Block has failed to erase min. once */
++ default: return ret;
++ }
++ }
++#endif
++ buf_ofs = jeb->offset;
++
++ if (!buf_size) {
++ buf_len = c->sector_size;
++ } else {
++ buf_len = EMPTY_SCAN_SIZE;
++ err = jffs2_fill_scan_buf(c, buf, buf_ofs, buf_len);
++ if (err)
++ return err;
++ }
++
++ /* We temporarily use 'ofs' as a pointer into the buffer/jeb */
++ ofs = 0;
++
++ /* Scan only 4KiB of 0xFF before declaring it's empty */
++ while(ofs < EMPTY_SCAN_SIZE && *(uint32_t *)(&buf[ofs]) == 0xFFFFFFFF)
++ ofs += 4;
++
++ if (ofs == EMPTY_SCAN_SIZE) {
++#ifdef CONFIG_JFFS2_FS_NAND
++ if (jffs2_cleanmarker_oob(c)) {
++ /* scan oob, take care of cleanmarker */
++ int ret = jffs2_check_oob_empty(c, jeb, cleanmarkerfound);
++ D2(printk(KERN_NOTICE "jffs2_check_oob_empty returned %d\n",ret));
++ switch (ret) {
++ case 0: return cleanmarkerfound ? BLK_STATE_CLEANMARKER : BLK_STATE_ALLFF;
++ case 1: return BLK_STATE_ALLDIRTY;
++ case 2: return BLK_STATE_BADBLOCK; /* case 2/3 are paranoia checks */
++ case 3: return BLK_STATE_ALLDIRTY; /* Block has failed to erase min. once */
++ default: return ret;
++ }
++ }
++#endif
+ D1(printk(KERN_DEBUG "Block at 0x%08x is empty (erased)\n", jeb->offset));
+- return 1; /* special return code */
++ return BLK_STATE_ALLFF; /* OK to erase if all blocks are like this */
++ }
++ if (ofs) {
++ D1(printk(KERN_DEBUG "Free space at %08x ends at %08x\n", jeb->offset,
++ jeb->offset + ofs));
++ DIRTY_SPACE(ofs);
+ }
+
++ /* Now ofs is a complete physical flash offset as it always was... */
++ ofs += jeb->offset;
++
+ noise = 10;
+
+ while(ofs < jeb->offset + c->sector_size) {
+- ssize_t retlen;
+- ACCT_PARANOIA_CHECK(jeb);
++
++ D1(ACCT_PARANOIA_CHECK(jeb));
++
++ cond_resched();
+
+ if (ofs & 3) {
+ printk(KERN_WARNING "Eep. ofs 0x%08x not word-aligned!\n", ofs);
+- ofs = (ofs+3)&~3;
++ ofs = PAD(ofs);
+ continue;
+ }
+ if (ofs == prevofs) {
+@@ -196,102 +378,173 @@
+ }
+ prevofs = ofs;
+
+- if (jeb->offset + c->sector_size < ofs + sizeof(node)) {
+- D1(printk(KERN_DEBUG "Fewer than %d bytes left to end of block. Not reading\n", sizeof(struct jffs2_unknown_node)));
++ if (jeb->offset + c->sector_size < ofs + sizeof(*node)) {
++ D1(printk(KERN_DEBUG "Fewer than %zd bytes left to end of block. (%x+%x<%x+%zx) Not reading\n", sizeof(struct jffs2_unknown_node),
++ jeb->offset, c->sector_size, ofs, sizeof(*node)));
+ DIRTY_SPACE((jeb->offset + c->sector_size)-ofs);
+ break;
+ }
+
+- err = c->mtd->read(c->mtd, ofs, sizeof(node), &retlen, (char *)&node);
+-
+- if (err) {
+- D1(printk(KERN_WARNING "mtd->read(0x%x bytes from 0x%x) returned %d\n", sizeof(node), ofs, err));
++ if (buf_ofs + buf_len < ofs + sizeof(*node)) {
++ buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs);
++ D1(printk(KERN_DEBUG "Fewer than %zd bytes (node header) left to end of buf. Reading 0x%x at 0x%08x\n",
++ sizeof(struct jffs2_unknown_node), buf_len, ofs));
++ err = jffs2_fill_scan_buf(c, buf, ofs, buf_len);
++ if (err)
+ return err;
++ buf_ofs = ofs;
+ }
+- if (retlen < sizeof(node)) {
+- D1(printk(KERN_WARNING "Read at 0x%x gave only 0x%x bytes\n", ofs, retlen));
+- DIRTY_SPACE(retlen);
+- ofs += retlen;
+- continue;
++
++ node = (struct jffs2_unknown_node *)&buf[ofs-buf_ofs];
++
++ if (*(uint32_t *)(&buf[ofs-buf_ofs]) == 0xffffffff) {
++ uint32_t inbuf_ofs = ofs - buf_ofs + 4;
++ uint32_t scanend;
++
++ empty_start = ofs;
++ ofs += 4;
++
++ /* If scanning empty space after only a cleanmarker, don't
++ bother scanning the whole block */
++ if (unlikely(empty_start == jeb->offset + c->cleanmarker_size &&
++ jeb->offset + EMPTY_SCAN_SIZE < buf_ofs + buf_len))
++ scanend = jeb->offset + EMPTY_SCAN_SIZE - buf_ofs;
++ else
++ scanend = buf_len;
++
++ D1(printk(KERN_DEBUG "Found empty flash at 0x%08x\n", ofs));
++ while (inbuf_ofs < scanend) {
++ if (*(uint32_t *)(&buf[inbuf_ofs]) != 0xffffffff)
++ goto emptyends;
++
++ inbuf_ofs+=4;
++ ofs += 4;
+ }
++ /* Ran off end. */
++ D1(printk(KERN_DEBUG "Empty flash ends normally at 0x%08x\n", ofs));
+
+- if (node.magic == JFFS2_EMPTY_BITMASK && node.nodetype == JFFS2_EMPTY_BITMASK) {
+- D1(printk(KERN_DEBUG "Found empty flash at 0x%x\n", ofs));
+- err = jffs2_scan_empty(c, jeb, &ofs, &noise);
+- if (err) return err;
++ if (buf_ofs == jeb->offset && jeb->used_size == PAD(c->cleanmarker_size) &&
++ c->cleanmarker_size && !jeb->first_node->next_in_ino && !jeb->dirty_size)
++ return BLK_STATE_CLEANMARKER;
++ wasempty = 1;
++ continue;
++ } else if (wasempty) {
++ emptyends:
++ printk(KERN_WARNING "Empty flash at 0x%08x ends at 0x%08x\n", empty_start, ofs);
++ DIRTY_SPACE(ofs-empty_start);
++ wasempty = 0;
+ continue;
+ }
+
+- if (ofs == jeb->offset && node.magic == KSAMTIB_CIGAM_2SFFJ) {
++ if (ofs == jeb->offset && je16_to_cpu(node->magic) == KSAMTIB_CIGAM_2SFFJ) {
+ printk(KERN_WARNING "Magic bitmask is backwards at offset 0x%08x. Wrong endian filesystem?\n", ofs);
+ DIRTY_SPACE(4);
+ ofs += 4;
+ continue;
+ }
+- if (node.magic == JFFS2_DIRTY_BITMASK) {
+- D1(printk(KERN_DEBUG "Empty bitmask at 0x%08x\n", ofs));
++ if (je16_to_cpu(node->magic) == JFFS2_DIRTY_BITMASK) {
++ D1(printk(KERN_DEBUG "Dirty bitmask at 0x%08x\n", ofs));
+ DIRTY_SPACE(4);
+ ofs += 4;
+ continue;
+ }
+- if (node.magic == JFFS2_OLD_MAGIC_BITMASK) {
++ if (je16_to_cpu(node->magic) == JFFS2_OLD_MAGIC_BITMASK) {
+ printk(KERN_WARNING "Old JFFS2 bitmask found at 0x%08x\n", ofs);
+ printk(KERN_WARNING "You cannot use older JFFS2 filesystems with newer kernels\n");
+ DIRTY_SPACE(4);
+ ofs += 4;
+ continue;
+ }
+- if (node.magic != JFFS2_MAGIC_BITMASK) {
++ if (je16_to_cpu(node->magic) != JFFS2_MAGIC_BITMASK) {
+ /* OK. We're out of possibilities. Whinge and move on */
+- noisy_printk(&noise, "jffs2_scan_eraseblock(): Magic bitmask 0x%04x not found at 0x%08x: 0x%04x instead\n", JFFS2_MAGIC_BITMASK, ofs, node.magic);
++ noisy_printk(&noise, "jffs2_scan_eraseblock(): Magic bitmask 0x%04x not found at 0x%08x: 0x%04x instead\n",
++ JFFS2_MAGIC_BITMASK, ofs,
++ je16_to_cpu(node->magic));
+ DIRTY_SPACE(4);
+ ofs += 4;
+ continue;
+ }
+ /* We seem to have a node of sorts. Check the CRC */
+- nodetype = node.nodetype;
+- node.nodetype |= JFFS2_NODE_ACCURATE;
+- hdr_crc = crc32(0, &node, sizeof(node)-4);
+- node.nodetype = nodetype;
+- if (hdr_crc != node.hdr_crc) {
++ crcnode.magic = node->magic;
++ crcnode.nodetype = cpu_to_je16( je16_to_cpu(node->nodetype) | JFFS2_NODE_ACCURATE);
++ crcnode.totlen = node->totlen;
++ hdr_crc = crc32(0, &crcnode, sizeof(crcnode)-4);
++
++ if (hdr_crc != je32_to_cpu(node->hdr_crc)) {
+ noisy_printk(&noise, "jffs2_scan_eraseblock(): Node at 0x%08x {0x%04x, 0x%04x, 0x%08x) has invalid CRC 0x%08x (calculated 0x%08x)\n",
+- ofs, node.magic, node.nodetype, node.totlen, node.hdr_crc, hdr_crc);
++ ofs, je16_to_cpu(node->magic),
++ je16_to_cpu(node->nodetype),
++ je32_to_cpu(node->totlen),
++ je32_to_cpu(node->hdr_crc),
++ hdr_crc);
+ DIRTY_SPACE(4);
+ ofs += 4;
+ continue;
+ }
+
+- if (ofs + node.totlen > jeb->offset + c->sector_size) {
++ if (ofs + je32_to_cpu(node->totlen) >
++ jeb->offset + c->sector_size) {
+ /* Eep. Node goes over the end of the erase block. */
+ printk(KERN_WARNING "Node at 0x%08x with length 0x%08x would run over the end of the erase block\n",
+- ofs, node.totlen);
++ ofs, je32_to_cpu(node->totlen));
+ printk(KERN_WARNING "Perhaps the file system was created with the wrong erase size?\n");
+ DIRTY_SPACE(4);
+ ofs += 4;
+ continue;
+ }
+
+- switch(node.nodetype | JFFS2_NODE_ACCURATE) {
++ if (!(je16_to_cpu(node->nodetype) & JFFS2_NODE_ACCURATE)) {
++ /* Wheee. This is an obsoleted node */
++ D2(printk(KERN_DEBUG "Node at 0x%08x is obsolete. Skipping\n", ofs));
++ DIRTY_SPACE(PAD(je32_to_cpu(node->totlen)));
++ ofs += PAD(je32_to_cpu(node->totlen));
++ continue;
++ }
++
++ switch(je16_to_cpu(node->nodetype)) {
+ case JFFS2_NODETYPE_INODE:
+- err = jffs2_scan_inode_node(c, jeb, &ofs);
++ if (buf_ofs + buf_len < ofs + sizeof(struct jffs2_raw_inode)) {
++ buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs);
++ D1(printk(KERN_DEBUG "Fewer than %zd bytes (inode node) left to end of buf. Reading 0x%x at 0x%08x\n",
++ sizeof(struct jffs2_raw_inode), buf_len, ofs));
++ err = jffs2_fill_scan_buf(c, buf, ofs, buf_len);
++ if (err)
++ return err;
++ buf_ofs = ofs;
++ node = (void *)buf;
++ }
++ err = jffs2_scan_inode_node(c, jeb, (void *)node, ofs);
+ if (err) return err;
++ ofs += PAD(je32_to_cpu(node->totlen));
+ break;
+
+ case JFFS2_NODETYPE_DIRENT:
+- err = jffs2_scan_dirent_node(c, jeb, &ofs);
++ if (buf_ofs + buf_len < ofs + je32_to_cpu(node->totlen)) {
++ buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs);
++ D1(printk(KERN_DEBUG "Fewer than %d bytes (dirent node) left to end of buf. Reading 0x%x at 0x%08x\n",
++ je32_to_cpu(node->totlen), buf_len, ofs));
++ err = jffs2_fill_scan_buf(c, buf, ofs, buf_len);
++ if (err)
++ return err;
++ buf_ofs = ofs;
++ node = (void *)buf;
++ }
++ err = jffs2_scan_dirent_node(c, jeb, (void *)node, ofs);
+ if (err) return err;
++ ofs += PAD(je32_to_cpu(node->totlen));
+ break;
+
+ case JFFS2_NODETYPE_CLEANMARKER:
+- if (node.totlen != sizeof(struct jffs2_unknown_node)) {
++ D1(printk(KERN_DEBUG "CLEANMARKER node found at 0x%08x\n", ofs));
++ if (je32_to_cpu(node->totlen) != c->cleanmarker_size) {
+ printk(KERN_NOTICE "CLEANMARKER node found at 0x%08x has totlen 0x%x != normal 0x%x\n",
+- ofs, node.totlen, sizeof(struct jffs2_unknown_node));
++ ofs, je32_to_cpu(node->totlen), c->cleanmarker_size);
+ DIRTY_SPACE(PAD(sizeof(struct jffs2_unknown_node)));
++ ofs += PAD(sizeof(struct jffs2_unknown_node));
+ } else if (jeb->first_node) {
+ printk(KERN_NOTICE "CLEANMARKER node found at 0x%08x, not first node in block (0x%08x)\n", ofs, jeb->offset);
+ DIRTY_SPACE(PAD(sizeof(struct jffs2_unknown_node)));
+ ofs += PAD(sizeof(struct jffs2_unknown_node));
+- continue;
+ } else {
+ struct jffs2_raw_node_ref *marker_ref = jffs2_alloc_raw_node_ref();
+ if (!marker_ref) {
+@@ -300,98 +553,80 @@
+ }
+ marker_ref->next_in_ino = NULL;
+ marker_ref->next_phys = NULL;
+- marker_ref->flash_offset = ofs;
+- marker_ref->totlen = sizeof(struct jffs2_unknown_node);
++ marker_ref->flash_offset = ofs | REF_NORMAL;
++ marker_ref->__totlen = c->cleanmarker_size;
+ jeb->first_node = jeb->last_node = marker_ref;
+
+- USED_SPACE(PAD(sizeof(struct jffs2_unknown_node)));
++ USED_SPACE(PAD(c->cleanmarker_size));
++ ofs += PAD(c->cleanmarker_size);
+ }
+- ofs += PAD(sizeof(struct jffs2_unknown_node));
++ break;
++
++ case JFFS2_NODETYPE_PADDING:
++ DIRTY_SPACE(PAD(je32_to_cpu(node->totlen)));
++ ofs += PAD(je32_to_cpu(node->totlen));
+ break;
+
+ default:
+- switch (node.nodetype & JFFS2_COMPAT_MASK) {
++ switch (je16_to_cpu(node->nodetype) & JFFS2_COMPAT_MASK) {
+ case JFFS2_FEATURE_ROCOMPAT:
+- printk(KERN_NOTICE "Read-only compatible feature node (0x%04x) found at offset 0x%08x\n", node.nodetype, ofs);
++ printk(KERN_NOTICE "Read-only compatible feature node (0x%04x) found at offset 0x%08x\n", je16_to_cpu(node->nodetype), ofs);
+ c->flags |= JFFS2_SB_FLAG_RO;
+- if (!(OFNI_BS_2SFFJ(c)->s_flags & MS_RDONLY))
++ if (!(jffs2_is_readonly(c)))
+ return -EROFS;
+- DIRTY_SPACE(PAD(node.totlen));
+- ofs += PAD(node.totlen);
+- continue;
++ DIRTY_SPACE(PAD(je32_to_cpu(node->totlen)));
++ ofs += PAD(je32_to_cpu(node->totlen));
++ break;
+
+ case JFFS2_FEATURE_INCOMPAT:
+- printk(KERN_NOTICE "Incompatible feature node (0x%04x) found at offset 0x%08x\n", node.nodetype, ofs);
++ printk(KERN_NOTICE "Incompatible feature node (0x%04x) found at offset 0x%08x\n", je16_to_cpu(node->nodetype), ofs);
+ return -EINVAL;
+
+ case JFFS2_FEATURE_RWCOMPAT_DELETE:
+- printk(KERN_NOTICE "Unknown but compatible feature node (0x%04x) found at offset 0x%08x\n", node.nodetype, ofs);
+- DIRTY_SPACE(PAD(node.totlen));
+- ofs += PAD(node.totlen);
++ D1(printk(KERN_NOTICE "Unknown but compatible feature node (0x%04x) found at offset 0x%08x\n", je16_to_cpu(node->nodetype), ofs));
++ DIRTY_SPACE(PAD(je32_to_cpu(node->totlen)));
++ ofs += PAD(je32_to_cpu(node->totlen));
+ break;
+
+ case JFFS2_FEATURE_RWCOMPAT_COPY:
+- printk(KERN_NOTICE "Unknown but compatible feature node (0x%04x) found at offset 0x%08x\n", node.nodetype, ofs);
+- USED_SPACE(PAD(node.totlen));
+- ofs += PAD(node.totlen);
++ D1(printk(KERN_NOTICE "Unknown but compatible feature node (0x%04x) found at offset 0x%08x\n", je16_to_cpu(node->nodetype), ofs));
++ USED_SPACE(PAD(je32_to_cpu(node->totlen)));
++ ofs += PAD(je32_to_cpu(node->totlen));
+ break;
+ }
+ }
+ }
+- D1(printk(KERN_DEBUG "Block at 0x%08x: free 0x%08x, dirty 0x%08x, used 0x%08x\n", jeb->offset,
+- jeb->free_size, jeb->dirty_size, jeb->used_size));
+- return 0;
+-}
+
+-/* We're pointing at the first empty word on the flash. Scan and account for the whole dirty region */
+-static int jffs2_scan_empty(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, __u32 *startofs, int *noise)
+-{
+- __u32 *buf;
+- __u32 scanlen = (jeb->offset + c->sector_size) - *startofs;
+- __u32 curofs = *startofs;
+-
+- buf = kmalloc(min((__u32)PAGE_SIZE, scanlen), GFP_KERNEL);
+- if (!buf) {
+- printk(KERN_WARNING "Scan buffer allocation failed\n");
+- return -ENOMEM;
+- }
+- while(scanlen) {
+- ssize_t retlen;
+- int ret, i;
+-
+- ret = c->mtd->read(c->mtd, curofs, min((__u32)PAGE_SIZE, scanlen), &retlen, (char *)buf);
+- if(ret) {
+- D1(printk(KERN_WARNING "jffs2_scan_empty(): Read 0x%x bytes at 0x%08x returned %d\n", min((__u32)PAGE_SIZE, scanlen), curofs, ret));
+- kfree(buf);
+- return ret;
+- }
+- if (retlen < 4) {
+- D1(printk(KERN_WARNING "Eep. too few bytes read in scan_empty()\n"));
+- kfree(buf);
+- return -EIO;
+- }
+- for (i=0; i<(retlen / 4); i++) {
+- if (buf[i] != 0xffffffff) {
+- curofs += i*4;
+-
+- noisy_printk(noise, "jffs2_scan_empty(): Empty block at 0x%08x ends at 0x%08x (with 0x%08x)! Marking dirty\n", *startofs, curofs, buf[i]);
+- DIRTY_SPACE(curofs - (*startofs));
+- *startofs = curofs;
+- kfree(buf);
+- return 0;
+- }
+- }
+- scanlen -= retlen&~3;
+- curofs += retlen&~3;
+- }
+
+- D1(printk(KERN_DEBUG "Empty flash detected from 0x%08x to 0x%08x\n", *startofs, curofs));
+- kfree(buf);
+- *startofs = curofs;
+- return 0;
++ D1(printk(KERN_DEBUG "Block at 0x%08x: free 0x%08x, dirty 0x%08x, unchecked 0x%08x, used 0x%08x\n", jeb->offset,
++ jeb->free_size, jeb->dirty_size, jeb->unchecked_size, jeb->used_size));
++
++ /* mark_node_obsolete can add to wasted !! */
++ if (jeb->wasted_size) {
++ jeb->dirty_size += jeb->wasted_size;
++ c->dirty_size += jeb->wasted_size;
++ c->wasted_size -= jeb->wasted_size;
++ jeb->wasted_size = 0;
++ }
++
++ if ((jeb->used_size + jeb->unchecked_size) == PAD(c->cleanmarker_size) && !jeb->dirty_size
++ && (!jeb->first_node || jeb->first_node->next_in_ino) )
++ return BLK_STATE_CLEANMARKER;
++
++ /* move blocks with max 4 byte dirty space to cleanlist */
++ else if (!ISDIRTY(c->sector_size - (jeb->used_size + jeb->unchecked_size))) {
++ c->dirty_size -= jeb->dirty_size;
++ c->wasted_size += jeb->dirty_size;
++ jeb->wasted_size += jeb->dirty_size;
++ jeb->dirty_size = 0;
++ return BLK_STATE_CLEAN;
++ } else if (jeb->used_size || jeb->unchecked_size)
++ return BLK_STATE_PARTDIRTY;
++ else
++ return BLK_STATE_ALLDIRTY;
+ }
+
+-static struct jffs2_inode_cache *jffs2_scan_make_ino_cache(struct jffs2_sb_info *c, __u32 ino)
++static struct jffs2_inode_cache *jffs2_scan_make_ino_cache(struct jffs2_sb_info *c, uint32_t ino)
+ {
+ struct jffs2_inode_cache *ic;
+
+@@ -399,137 +634,77 @@
+ if (ic)
+ return ic;
+
++ if (ino > c->highest_ino)
++ c->highest_ino = ino;
++
+ ic = jffs2_alloc_inode_cache();
+ if (!ic) {
+ printk(KERN_NOTICE "jffs2_scan_make_inode_cache(): allocation of inode cache failed\n");
+ return NULL;
+ }
+ memset(ic, 0, sizeof(*ic));
+- ic->scan = kmalloc(sizeof(struct jffs2_scan_info), GFP_KERNEL);
+- if (!ic->scan) {
+- printk(KERN_NOTICE "jffs2_scan_make_inode_cache(): allocation of scan info for inode cache failed\n");
+- jffs2_free_inode_cache(ic);
+- return NULL;
+- }
+- memset(ic->scan, 0, sizeof(*ic->scan));
++
+ ic->ino = ino;
+ ic->nodes = (void *)ic;
+ jffs2_add_ino_cache(c, ic);
+ if (ino == 1)
+- ic->nlink=1;
++ ic->nlink = 1;
+ return ic;
+ }
+
+-static int jffs2_scan_inode_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, __u32 *ofs)
++static int jffs2_scan_inode_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
++ struct jffs2_raw_inode *ri, uint32_t ofs)
+ {
+ struct jffs2_raw_node_ref *raw;
+- struct jffs2_full_dnode *fn;
+- struct jffs2_tmp_dnode_info *tn, **tn_list;
+ struct jffs2_inode_cache *ic;
+- struct jffs2_raw_inode ri;
+- __u32 crc;
+- __u16 oldnodetype;
+- int ret;
+- ssize_t retlen;
+-
+- D1(printk(KERN_DEBUG "jffs2_scan_inode_node(): Node at 0x%08x\n", *ofs));
+-
+- ret = c->mtd->read(c->mtd, *ofs, sizeof(ri), &retlen, (char *)&ri);
+- if (ret) {
+- printk(KERN_NOTICE "jffs2_scan_inode_node(): Read error at 0x%08x: %d\n", *ofs, ret);
+- return ret;
+- }
+- if (retlen != sizeof(ri)) {
+- printk(KERN_NOTICE "Short read: 0x%x bytes at 0x%08x instead of requested %x\n",
+- retlen, *ofs, sizeof(ri));
+- return -EIO;
+- }
++ uint32_t ino = je32_to_cpu(ri->ino);
+
+- /* We sort of assume that the node was accurate when it was
+- first written to the medium :) */
+- oldnodetype = ri.nodetype;
+- ri.nodetype |= JFFS2_NODE_ACCURATE;
+- crc = crc32(0, &ri, sizeof(ri)-8);
+- ri.nodetype = oldnodetype;
++ D1(printk(KERN_DEBUG "jffs2_scan_inode_node(): Node at 0x%08x\n", ofs));
+
+- if(crc != ri.node_crc) {
+- printk(KERN_NOTICE "jffs2_scan_inode_node(): CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
+- *ofs, ri.node_crc, crc);
+- /* FIXME: Why do we believe totlen? */
+- DIRTY_SPACE(4);
+- *ofs += 4;
+- return 0;
+- }
+- /* There was a bug where we wrote hole nodes out with csize/dsize
+- swapped. Deal with it */
+- if (ri.compr == JFFS2_COMPR_ZERO && !ri.dsize && ri.csize) {
+- ri.dsize = ri.csize;
+- ri.csize = 0;
+- }
++ /* We do very little here now. Just check the ino# to which we should attribute
++ this node; we can do all the CRC checking etc. later. There's a tradeoff here --
++ we used to scan the flash once only, reading everything we want from it into
++ memory, then building all our in-core data structures and freeing the extra
++ information. Now we allow the first part of the mount to complete a lot quicker,
++ but we have to go _back_ to the flash in order to finish the CRC checking, etc.
++ Which means that the _full_ amount of time to get to proper write mode with GC
++ operational may actually be _longer_ than before. Sucks to be me. */
+
+- if (ri.csize) {
+- /* Check data CRC too */
+- unsigned char *dbuf;
+- __u32 crc;
+-
+- dbuf = kmalloc(PAGE_CACHE_SIZE, GFP_KERNEL);
+- if (!dbuf) {
+- printk(KERN_NOTICE "jffs2_scan_inode_node(): allocation of temporary data buffer for CRC check failed\n");
+- return -ENOMEM;
+- }
+- ret = c->mtd->read(c->mtd, *ofs+sizeof(ri), ri.csize, &retlen, dbuf);
+- if (ret) {
+- printk(KERN_NOTICE "jffs2_scan_inode_node(): Read error at 0x%08x: %d\n", *ofs+sizeof(ri), ret);
+- kfree(dbuf);
+- return ret;
+- }
+- if (retlen != ri.csize) {
+- printk(KERN_NOTICE "Short read: 0x%x bytes at 0x%08x instead of requested %x\n",
+- retlen, *ofs+ sizeof(ri), ri.csize);
+- kfree(dbuf);
+- return -EIO;
+- }
+- crc = crc32(0, dbuf, ri.csize);
+- kfree(dbuf);
+- if (crc != ri.data_crc) {
+- printk(KERN_NOTICE "jffs2_scan_inode_node(): Data CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
+- *ofs, ri.data_crc, crc);
+- DIRTY_SPACE(PAD(ri.totlen));
+- *ofs += PAD(ri.totlen);
+- return 0;
+- }
+- }
+-
+- /* Wheee. It worked */
+ raw = jffs2_alloc_raw_node_ref();
+ if (!raw) {
+ printk(KERN_NOTICE "jffs2_scan_inode_node(): allocation of node reference failed\n");
+ return -ENOMEM;
+ }
+- tn = jffs2_alloc_tmp_dnode_info();
+- if (!tn) {
+- jffs2_free_raw_node_ref(raw);
+- return -ENOMEM;
+- }
+- fn = jffs2_alloc_full_dnode();
+- if (!fn) {
+- jffs2_free_tmp_dnode_info(tn);
++
++ ic = jffs2_get_ino_cache(c, ino);
++ if (!ic) {
++ /* Inocache get failed. Either we read a bogus ino# or it's just genuinely the
++ first node we found for this inode. Do a CRC check to protect against the former
++ case */
++ uint32_t crc = crc32(0, ri, sizeof(*ri)-8);
++
++ if (crc != je32_to_cpu(ri->node_crc)) {
++ printk(KERN_NOTICE "jffs2_scan_inode_node(): CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
++ ofs, je32_to_cpu(ri->node_crc), crc);
++ /* We believe totlen because the CRC on the node _header_ was OK, just the node itself failed. */
++ DIRTY_SPACE(PAD(je32_to_cpu(ri->totlen)));
+ jffs2_free_raw_node_ref(raw);
+- return -ENOMEM;
++ return 0;
+ }
+- ic = jffs2_scan_make_ino_cache(c, ri.ino);
++ ic = jffs2_scan_make_ino_cache(c, ino);
+ if (!ic) {
+- jffs2_free_full_dnode(fn);
+- jffs2_free_tmp_dnode_info(tn);
+ jffs2_free_raw_node_ref(raw);
+ return -ENOMEM;
+ }
++ }
+
+- /* Build the data structures and file them for later */
+- raw->flash_offset = *ofs;
+- raw->totlen = PAD(ri.totlen);
++ /* Wheee. It worked */
++
++ raw->flash_offset = ofs | REF_UNCHECKED;
++ raw->__totlen = PAD(je32_to_cpu(ri->totlen));
+ raw->next_phys = NULL;
+ raw->next_in_ino = ic->nodes;
++
+ ic->nodes = raw;
+ if (!jeb->first_node)
+ jeb->first_node = raw;
+@@ -538,134 +713,56 @@
+ jeb->last_node = raw;
+
+ D1(printk(KERN_DEBUG "Node is ino #%u, version %d. Range 0x%x-0x%x\n",
+- ri.ino, ri.version, ri.offset, ri.offset+ri.dsize));
+-
+- pseudo_random += ri.version;
+-
+- for (tn_list = &ic->scan->tmpnodes; *tn_list; tn_list = &((*tn_list)->next)) {
+- if ((*tn_list)->version < ri.version)
+- continue;
+- if ((*tn_list)->version > ri.version)
+- break;
+- /* Wheee. We've found another instance of the same version number.
+- We should obsolete one of them.
+- */
+- D1(printk(KERN_DEBUG "Duplicate version %d found in ino #%u. Previous one is at 0x%08x\n", ri.version, ic->ino, (*tn_list)->fn->raw->flash_offset &~3));
+- if (!jeb->used_size) {
+- D1(printk(KERN_DEBUG "No valid nodes yet found in this eraseblock 0x%08x, so obsoleting the new instance at 0x%08x\n",
+- jeb->offset, raw->flash_offset & ~3));
+- ri.nodetype &= ~JFFS2_NODE_ACCURATE;
+- /* Perhaps we could also mark it as such on the medium. Maybe later */
+- }
+- break;
+- }
+-
+- if (ri.nodetype & JFFS2_NODE_ACCURATE) {
+- memset(fn,0,sizeof(*fn));
+-
+- fn->ofs = ri.offset;
+- fn->size = ri.dsize;
+- fn->frags = 0;
+- fn->raw = raw;
+-
+- tn->next = NULL;
+- tn->fn = fn;
+- tn->version = ri.version;
++ je32_to_cpu(ri->ino), je32_to_cpu(ri->version),
++ je32_to_cpu(ri->offset),
++ je32_to_cpu(ri->offset)+je32_to_cpu(ri->dsize)));
+
+- USED_SPACE(PAD(ri.totlen));
+- jffs2_add_tn_to_list(tn, &ic->scan->tmpnodes);
+- /* Make sure the one we just added is the _last_ in the list
+- with this version number, so the older ones get obsoleted */
+- while (tn->next && tn->next->version == tn->version) {
++ pseudo_random += je32_to_cpu(ri->version);
+
+- D1(printk(KERN_DEBUG "Shifting new node at 0x%08x after other node at 0x%08x for version %d in list\n",
+- fn->raw->flash_offset&~3, tn->next->fn->raw->flash_offset &~3, ri.version));
+-
+- if(tn->fn != fn)
+- BUG();
+- tn->fn = tn->next->fn;
+- tn->next->fn = fn;
+- tn = tn->next;
+- }
+- } else {
+- jffs2_free_full_dnode(fn);
+- jffs2_free_tmp_dnode_info(tn);
+- raw->flash_offset |= 1;
+- DIRTY_SPACE(PAD(ri.totlen));
+- }
+- *ofs += PAD(ri.totlen);
++ UNCHECKED_SPACE(PAD(je32_to_cpu(ri->totlen)));
+ return 0;
+ }
+
+-static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, __u32 *ofs)
++static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
++ struct jffs2_raw_dirent *rd, uint32_t ofs)
+ {
+ struct jffs2_raw_node_ref *raw;
+ struct jffs2_full_dirent *fd;
+ struct jffs2_inode_cache *ic;
+- struct jffs2_raw_dirent rd;
+- __u16 oldnodetype;
+- int ret;
+- __u32 crc;
+- ssize_t retlen;
+-
+- D1(printk(KERN_DEBUG "jffs2_scan_dirent_node(): Node at 0x%08x\n", *ofs));
++ uint32_t crc;
+
+- ret = c->mtd->read(c->mtd, *ofs, sizeof(rd), &retlen, (char *)&rd);
+- if (ret) {
+- printk(KERN_NOTICE "jffs2_scan_dirent_node(): Read error at 0x%08x: %d\n", *ofs, ret);
+- return ret;
+- }
+- if (retlen != sizeof(rd)) {
+- printk(KERN_NOTICE "Short read: 0x%x bytes at 0x%08x instead of requested %x\n",
+- retlen, *ofs, sizeof(rd));
+- return -EIO;
+- }
++ D1(printk(KERN_DEBUG "jffs2_scan_dirent_node(): Node at 0x%08x\n", ofs));
+
+- /* We sort of assume that the node was accurate when it was
+- first written to the medium :) */
+- oldnodetype = rd.nodetype;
+- rd.nodetype |= JFFS2_NODE_ACCURATE;
+- crc = crc32(0, &rd, sizeof(rd)-8);
+- rd.nodetype = oldnodetype;
++ /* We don't get here unless the node is still valid, so we don't have to
++ mask in the ACCURATE bit any more. */
++ crc = crc32(0, rd, sizeof(*rd)-8);
+
+- if (crc != rd.node_crc) {
++ if (crc != je32_to_cpu(rd->node_crc)) {
+ printk(KERN_NOTICE "jffs2_scan_dirent_node(): Node CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
+- *ofs, rd.node_crc, crc);
+- /* FIXME: Why do we believe totlen? */
+- DIRTY_SPACE(4);
+- *ofs += 4;
++ ofs, je32_to_cpu(rd->node_crc), crc);
++ /* We believe totlen because the CRC on the node _header_ was OK, just the node itself failed. */
++ DIRTY_SPACE(PAD(je32_to_cpu(rd->totlen)));
+ return 0;
+ }
+
+- pseudo_random += rd.version;
++ pseudo_random += je32_to_cpu(rd->version);
+
+- fd = jffs2_alloc_full_dirent(rd.nsize+1);
++ fd = jffs2_alloc_full_dirent(rd->nsize+1);
+ if (!fd) {
+ return -ENOMEM;
+-}
+- ret = c->mtd->read(c->mtd, *ofs + sizeof(rd), rd.nsize, &retlen, &fd->name[0]);
+- if (ret) {
+- jffs2_free_full_dirent(fd);
+- printk(KERN_NOTICE "jffs2_scan_dirent_node(): Read error at 0x%08x: %d\n",
+- *ofs + sizeof(rd), ret);
+- return ret;
+- }
+- if (retlen != rd.nsize) {
+- jffs2_free_full_dirent(fd);
+- printk(KERN_NOTICE "Short read: 0x%x bytes at 0x%08x instead of requested %x\n",
+- retlen, *ofs + sizeof(rd), rd.nsize);
+- return -EIO;
+ }
+- crc = crc32(0, fd->name, rd.nsize);
+- if (crc != rd.name_crc) {
++ memcpy(&fd->name, rd->name, rd->nsize);
++ fd->name[rd->nsize] = 0;
++
++ crc = crc32(0, fd->name, rd->nsize);
++ if (crc != je32_to_cpu(rd->name_crc)) {
+ printk(KERN_NOTICE "jffs2_scan_dirent_node(): Name CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
+- *ofs, rd.name_crc, crc);
+- fd->name[rd.nsize]=0;
+- D1(printk(KERN_NOTICE "Name for which CRC failed is (now) '%s', ino #%d\n", fd->name, rd.ino));
++ ofs, je32_to_cpu(rd->name_crc), crc);
++ D1(printk(KERN_NOTICE "Name for which CRC failed is (now) '%s', ino #%d\n", fd->name, je32_to_cpu(rd->ino)));
+ jffs2_free_full_dirent(fd);
+ /* FIXME: Why do we believe totlen? */
+- DIRTY_SPACE(PAD(rd.totlen));
+- *ofs += PAD(rd.totlen);
++ /* We believe totlen because the CRC on the node _header_ was OK, just the name failed. */
++ DIRTY_SPACE(PAD(je32_to_cpu(rd->totlen)));
+ return 0;
+ }
+ raw = jffs2_alloc_raw_node_ref();
+@@ -674,15 +771,15 @@
+ printk(KERN_NOTICE "jffs2_scan_dirent_node(): allocation of node reference failed\n");
+ return -ENOMEM;
+ }
+- ic = jffs2_scan_make_ino_cache(c, rd.pino);
++ ic = jffs2_scan_make_ino_cache(c, je32_to_cpu(rd->pino));
+ if (!ic) {
+ jffs2_free_full_dirent(fd);
+ jffs2_free_raw_node_ref(raw);
+ return -ENOMEM;
+ }
+
+- raw->totlen = PAD(rd.totlen);
+- raw->flash_offset = *ofs;
++ raw->__totlen = PAD(je32_to_cpu(rd->totlen));
++ raw->flash_offset = ofs | REF_PRISTINE;
+ raw->next_phys = NULL;
+ raw->next_in_ino = ic->nodes;
+ ic->nodes = raw;
+@@ -692,24 +789,15 @@
+ jeb->last_node->next_phys = raw;
+ jeb->last_node = raw;
+
+- if (rd.nodetype & JFFS2_NODE_ACCURATE) {
+ fd->raw = raw;
+ fd->next = NULL;
+- fd->version = rd.version;
+- fd->ino = rd.ino;
+- fd->name[rd.nsize]=0;
+- fd->nhash = full_name_hash(fd->name, rd.nsize);
+- fd->type = rd.type;
+-
+- USED_SPACE(PAD(rd.totlen));
+- jffs2_add_fd_to_list(c, fd, &ic->scan->dents);
+- } else {
+- raw->flash_offset |= 1;
+- jffs2_free_full_dirent(fd);
++ fd->version = je32_to_cpu(rd->version);
++ fd->ino = je32_to_cpu(rd->ino);
++ fd->nhash = full_name_hash(fd->name, rd->nsize);
++ fd->type = rd->type;
++ USED_SPACE(PAD(je32_to_cpu(rd->totlen)));
++ jffs2_add_fd_to_list(c, fd, &ic->scan_dents);
+
+- DIRTY_SPACE(PAD(rd.totlen));
+- }
+- *ofs += PAD(rd.totlen);
+ return 0;
+ }
+
+@@ -731,26 +819,90 @@
+ struct list_head *n = head->next;
+
+ list_del(head);
+- while(count--)
++ while(count--) {
+ n = n->next;
++ }
+ list_add(head, n);
+ }
+
+-static void jffs2_rotate_lists(struct jffs2_sb_info *c)
++void jffs2_rotate_lists(struct jffs2_sb_info *c)
+ {
+ uint32_t x;
++ uint32_t rotateby;
+
+ x = count_list(&c->clean_list);
+- if (x)
+- rotate_list((&c->clean_list), pseudo_random % x);
++ if (x) {
++ rotateby = pseudo_random % x;
++ D1(printk(KERN_DEBUG "Rotating clean_list by %d\n", rotateby));
++
++ rotate_list((&c->clean_list), rotateby);
++
++ D1(printk(KERN_DEBUG "Erase block at front of clean_list is at %08x\n",
++ list_entry(c->clean_list.next, struct jffs2_eraseblock, list)->offset));
++ } else {
++ D1(printk(KERN_DEBUG "Not rotating empty clean_list\n"));
++ }
++
++ x = count_list(&c->very_dirty_list);
++ if (x) {
++ rotateby = pseudo_random % x;
++ D1(printk(KERN_DEBUG "Rotating very_dirty_list by %d\n", rotateby));
++
++ rotate_list((&c->very_dirty_list), rotateby);
++
++ D1(printk(KERN_DEBUG "Erase block at front of very_dirty_list is at %08x\n",
++ list_entry(c->very_dirty_list.next, struct jffs2_eraseblock, list)->offset));
++ } else {
++ D1(printk(KERN_DEBUG "Not rotating empty very_dirty_list\n"));
++ }
+
+ x = count_list(&c->dirty_list);
+- if (x)
+- rotate_list((&c->dirty_list), pseudo_random % x);
++ if (x) {
++ rotateby = pseudo_random % x;
++ D1(printk(KERN_DEBUG "Rotating dirty_list by %d\n", rotateby));
++
++ rotate_list((&c->dirty_list), rotateby);
++
++ D1(printk(KERN_DEBUG "Erase block at front of dirty_list is at %08x\n",
++ list_entry(c->dirty_list.next, struct jffs2_eraseblock, list)->offset));
++ } else {
++ D1(printk(KERN_DEBUG "Not rotating empty dirty_list\n"));
++ }
++
++ x = count_list(&c->erasable_list);
++ if (x) {
++ rotateby = pseudo_random % x;
++ D1(printk(KERN_DEBUG "Rotating erasable_list by %d\n", rotateby));
+
+- if (c->nr_erasing_blocks)
+- rotate_list((&c->erase_pending_list), pseudo_random % c->nr_erasing_blocks);
++ rotate_list((&c->erasable_list), rotateby);
+
+- if (c->nr_free_blocks) /* Not that it should ever be zero */
+- rotate_list((&c->free_list), pseudo_random % c->nr_free_blocks);
++ D1(printk(KERN_DEBUG "Erase block at front of erasable_list is at %08x\n",
++ list_entry(c->erasable_list.next, struct jffs2_eraseblock, list)->offset));
++ } else {
++ D1(printk(KERN_DEBUG "Not rotating empty erasable_list\n"));
++ }
++
++ if (c->nr_erasing_blocks) {
++ rotateby = pseudo_random % c->nr_erasing_blocks;
++ D1(printk(KERN_DEBUG "Rotating erase_pending_list by %d\n", rotateby));
++
++ rotate_list((&c->erase_pending_list), rotateby);
++
++ D1(printk(KERN_DEBUG "Erase block at front of erase_pending_list is at %08x\n",
++ list_entry(c->erase_pending_list.next, struct jffs2_eraseblock, list)->offset));
++ } else {
++ D1(printk(KERN_DEBUG "Not rotating empty erase_pending_list\n"));
++ }
++
++ if (c->nr_free_blocks) {
++ rotateby = pseudo_random % c->nr_free_blocks;
++ D1(printk(KERN_DEBUG "Rotating free_list by %d\n", rotateby));
++
++ rotate_list((&c->free_list), rotateby);
++
++ D1(printk(KERN_DEBUG "Erase block at front of free_list is at %08x\n",
++ list_entry(c->free_list.next, struct jffs2_eraseblock, list)->offset));
++ } else {
++ D1(printk(KERN_DEBUG "Not rotating empty free_list\n"));
++ }
+ }
+diff -Nurb linux-mips-2.4.27/fs/jffs2/super-v24.c linux/fs/jffs2/super-v24.c
+--- linux-mips-2.4.27/fs/jffs2/super-v24.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/fs/jffs2/super-v24.c 2004-11-19 10:25:12.126165288 +0100
+@@ -0,0 +1,167 @@
++/*
++ * JFFS2 -- Journalling Flash File System, Version 2.
++ *
++ * Copyright (C) 2001-2003 Red Hat, Inc.
++ *
++ * Created by David Woodhouse <dwmw2@redhat.com>
++ *
++ * For licensing information, see the file 'LICENCE' in this directory.
++ *
++ * $Id$
++ *
++ */
++
++#include <linux/config.h>
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/version.h>
++#include <linux/slab.h>
++#include <linux/init.h>
++#include <linux/list.h>
++#include <linux/fs.h>
++#include <linux/jffs2.h>
++#include <linux/pagemap.h>
++#include <linux/mtd/mtd.h>
++#include "nodelist.h"
++
++#ifndef MTD_BLOCK_MAJOR
++#define MTD_BLOCK_MAJOR 31
++#endif
++
++static void jffs2_put_super (struct super_block *);
++
++static struct super_operations jffs2_super_operations =
++{
++ .read_inode = jffs2_read_inode,
++ .put_super = jffs2_put_super,
++ .write_super = jffs2_write_super,
++ .statfs = jffs2_statfs,
++ .remount_fs = jffs2_remount_fs,
++ .clear_inode = jffs2_clear_inode,
++ .dirty_inode = jffs2_dirty_inode,
++};
++
++
++static struct super_block *jffs2_read_super(struct super_block *sb, void *data, int silent)
++{
++ struct jffs2_sb_info *c;
++ int ret;
++
++ D1(printk(KERN_DEBUG "jffs2: read_super for device %s\n", kdevname(sb->s_dev)));
++
++ if (major(sb->s_dev) != MTD_BLOCK_MAJOR) {
++ if (!silent)
++ printk(KERN_DEBUG "jffs2: attempt to mount non-MTD device %s\n", kdevname(sb->s_dev));
++ return NULL;
++ }
++
++ c = JFFS2_SB_INFO(sb);
++ memset(c, 0, sizeof(*c));
++
++ sb->s_op = &jffs2_super_operations;
++
++ c->mtd = get_mtd_device(NULL, minor(sb->s_dev));
++ if (!c->mtd) {
++ D1(printk(KERN_DEBUG "jffs2: MTD device #%u doesn't appear to exist\n", minor(sb->s_dev)));
++ return NULL;
++ }
++
++ ret = jffs2_do_fill_super(sb, data, silent);
++ if (ret) {
++ put_mtd_device(c->mtd);
++ return NULL;
++ }
++
++ return sb;
++}
++
++static void jffs2_put_super (struct super_block *sb)
++{
++ struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
++
++ D2(printk(KERN_DEBUG "jffs2: jffs2_put_super()\n"));
++
++
++ if (!(sb->s_flags & MS_RDONLY))
++ jffs2_stop_garbage_collect_thread(c);
++ down(&c->alloc_sem);
++ jffs2_flush_wbuf_pad(c);
++ up(&c->alloc_sem);
++ jffs2_free_ino_caches(c);
++ jffs2_free_raw_node_refs(c);
++ kfree(c->blocks);
++ jffs2_nand_flash_cleanup(c);
++ kfree(c->inocache_list);
++ if (c->mtd->sync)
++ c->mtd->sync(c->mtd);
++ put_mtd_device(c->mtd);
++
++ D1(printk(KERN_DEBUG "jffs2_put_super returning\n"));
++}
++
++static DECLARE_FSTYPE_DEV(jffs2_fs_type, "jffs2", jffs2_read_super);
++
++static int __init init_jffs2_fs(void)
++{
++ int ret;
++
++ printk(KERN_INFO "JFFS2 version 2.2."
++#ifdef CONFIG_FS_JFFS2_NAND
++ " (NAND)"
++#endif
++ " (C) 2001-2003 Red Hat, Inc.\n");
++
++#ifdef JFFS2_OUT_OF_KERNEL
++ /* sanity checks. Could we do these at compile time? */
++ if (sizeof(struct jffs2_sb_info) > sizeof (((struct super_block *)NULL)->u)) {
++ printk(KERN_ERR "JFFS2 error: struct jffs2_sb_info (%d bytes) doesn't fit in the super_block union (%d bytes)\n",
++ sizeof(struct jffs2_sb_info), sizeof (((struct super_block *)NULL)->u));
++ return -EIO;
++ }
++
++ if (sizeof(struct jffs2_inode_info) > sizeof (((struct inode *)NULL)->u)) {
++ printk(KERN_ERR "JFFS2 error: struct jffs2_inode_info (%d bytes) doesn't fit in the inode union (%d bytes)\n",
++ sizeof(struct jffs2_inode_info), sizeof (((struct inode *)NULL)->u));
++ return -EIO;
++ }
++#endif
++ ret = jffs2_zlib_init();
++ if (ret) {
++ printk(KERN_ERR "JFFS2 error: Failed to initialise zlib workspaces\n");
++ goto out;
++ }
++ ret = jffs2_create_slab_caches();
++ if (ret) {
++ printk(KERN_ERR "JFFS2 error: Failed to initialise slab caches\n");
++ goto out_zlib;
++ }
++ ret = register_filesystem(&jffs2_fs_type);
++ if (ret) {
++ printk(KERN_ERR "JFFS2 error: Failed to register filesystem\n");
++ goto out_slab;
++ }
++ return 0;
++
++ out_slab:
++ jffs2_destroy_slab_caches();
++ out_zlib:
++ jffs2_zlib_exit();
++ out:
++
++ return ret;
++}
++
++static void __exit exit_jffs2_fs(void)
++{
++ jffs2_destroy_slab_caches();
++ jffs2_zlib_exit();
++ unregister_filesystem(&jffs2_fs_type);
++}
++
++module_init(init_jffs2_fs);
++module_exit(exit_jffs2_fs);
++
++MODULE_DESCRIPTION("The Journalling Flash File System, v2");
++MODULE_AUTHOR("Red Hat, Inc.");
++MODULE_LICENSE("GPL"); // Actually dual-licensed, but it doesn't matter for
++ // the sake of this tag. It's Free Software.
+diff -Nurb linux-mips-2.4.27/fs/jffs2/super.c linux/fs/jffs2/super.c
+--- linux-mips-2.4.27/fs/jffs2/super.c 2003-01-11 18:53:17.000000000 +0100
++++ linux/fs/jffs2/super.c 2004-11-19 10:25:12.127165136 +0100
+@@ -1,291 +1,257 @@
+ /*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+- * Copyright (C) 2001 Red Hat, Inc.
++ * Copyright (C) 2001-2003 Red Hat, Inc.
+ *
+- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
++ * Created by David Woodhouse <dwmw2@redhat.com>
+ *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
++ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+- * The contents of this file are subject to the Red Hat eCos Public
+- * License Version 1.1 (the "Licence"); you may not use this file
+- * except in compliance with the Licence. You may obtain a copy of
+- * the Licence at http://www.redhat.com/
+- *
+- * Software distributed under the Licence is distributed on an "AS IS"
+- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+- * See the Licence for the specific language governing rights and
+- * limitations under the Licence.
+- *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
+- *
+- * Alternatively, the contents of this file may be used under the
+- * terms of the GNU General Public License version 2 (the "GPL"), in
+- * which case the provisions of the GPL are applicable instead of the
+- * above. If you wish to allow the use of your version of this file
+- * only under the terms of the GPL and not to allow others to use your
+- * version of this file under the RHEPL, indicate your decision by
+- * deleting the provisions above and replace them with the notice and
+- * other provisions required by the GPL. If you do not delete the
+- * provisions above, a recipient may use your version of this file
+- * under either the RHEPL or the GPL.
+- *
+- * $Id$
++ * $Id$
+ *
+ */
+
+ #include <linux/config.h>
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+-#include <linux/version.h>
+ #include <linux/slab.h>
+ #include <linux/init.h>
+ #include <linux/list.h>
+ #include <linux/fs.h>
++#include <linux/mount.h>
+ #include <linux/jffs2.h>
+ #include <linux/pagemap.h>
+ #include <linux/mtd/mtd.h>
+-#include <linux/interrupt.h>
++#include <linux/ctype.h>
++#include <linux/namei.h>
+ #include "nodelist.h"
+
+-#ifndef MTD_BLOCK_MAJOR
+-#define MTD_BLOCK_MAJOR 31
+-#endif
++static void jffs2_put_super(struct super_block *);
++
++static kmem_cache_t *jffs2_inode_cachep;
++
++static struct inode *jffs2_alloc_inode(struct super_block *sb)
++{
++ struct jffs2_inode_info *ei;
++ ei = (struct jffs2_inode_info *)kmem_cache_alloc(jffs2_inode_cachep, SLAB_KERNEL);
++ if (!ei)
++ return NULL;
++ return &ei->vfs_inode;
++}
++
++static void jffs2_destroy_inode(struct inode *inode)
++{
++ kmem_cache_free(jffs2_inode_cachep, JFFS2_INODE_INFO(inode));
++}
++
++static void jffs2_i_init_once(void * foo, kmem_cache_t * cachep, unsigned long flags)
++{
++ struct jffs2_inode_info *ei = (struct jffs2_inode_info *) foo;
+
+-extern void jffs2_read_inode (struct inode *);
+-void jffs2_put_super (struct super_block *);
+-void jffs2_write_super (struct super_block *);
+-static int jffs2_statfs (struct super_block *, struct statfs *);
+-int jffs2_remount_fs (struct super_block *, int *, char *);
+-extern void jffs2_clear_inode (struct inode *);
++ if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
++ SLAB_CTOR_CONSTRUCTOR) {
++ init_MUTEX_LOCKED(&ei->sem);
++ inode_init_once(&ei->vfs_inode);
++ }
++}
+
+ static struct super_operations jffs2_super_operations =
+ {
+- read_inode: jffs2_read_inode,
+-// delete_inode: jffs2_delete_inode,
+- put_super: jffs2_put_super,
+- write_super: jffs2_write_super,
+- statfs: jffs2_statfs,
+- remount_fs: jffs2_remount_fs,
+- clear_inode: jffs2_clear_inode
++ .alloc_inode = jffs2_alloc_inode,
++ .destroy_inode =jffs2_destroy_inode,
++ .read_inode = jffs2_read_inode,
++ .put_super = jffs2_put_super,
++ .write_super = jffs2_write_super,
++ .statfs = jffs2_statfs,
++ .remount_fs = jffs2_remount_fs,
++ .clear_inode = jffs2_clear_inode,
++ .dirty_inode = jffs2_dirty_inode,
+ };
+
+-static int jffs2_statfs(struct super_block *sb, struct statfs *buf)
++static int jffs2_sb_compare(struct super_block *sb, void *data)
+ {
++ struct jffs2_sb_info *p = data;
+ struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
+- unsigned long avail;
+
+- buf->f_type = JFFS2_SUPER_MAGIC;
+- buf->f_bsize = 1 << PAGE_SHIFT;
+- buf->f_blocks = c->flash_size >> PAGE_SHIFT;
+- buf->f_files = 0;
+- buf->f_ffree = 0;
+- buf->f_namelen = JFFS2_MAX_NAME_LEN;
+-
+- spin_lock_bh(&c->erase_completion_lock);
+-
+- avail = c->dirty_size + c->free_size;
+- if (avail > c->sector_size * JFFS2_RESERVED_BLOCKS_WRITE)
+- avail -= c->sector_size * JFFS2_RESERVED_BLOCKS_WRITE;
+- else
+- avail = 0;
+-
+- buf->f_bavail = buf->f_bfree = avail >> PAGE_SHIFT;
+-
+-#if CONFIG_JFFS2_FS_DEBUG > 0
+- printk(KERN_DEBUG "STATFS:\n");
+- printk(KERN_DEBUG "flash_size: %08x\n", c->flash_size);
+- printk(KERN_DEBUG "used_size: %08x\n", c->used_size);
+- printk(KERN_DEBUG "dirty_size: %08x\n", c->dirty_size);
+- printk(KERN_DEBUG "free_size: %08x\n", c->free_size);
+- printk(KERN_DEBUG "erasing_size: %08x\n", c->erasing_size);
+- printk(KERN_DEBUG "bad_size: %08x\n", c->bad_size);
+- printk(KERN_DEBUG "sector_size: %08x\n", c->sector_size);
+-
+- if (c->nextblock) {
+- printk(KERN_DEBUG "nextblock: 0x%08x\n", c->nextblock->offset);
+- } else {
+- printk(KERN_DEBUG "nextblock: NULL\n");
+- }
+- if (c->gcblock) {
+- printk(KERN_DEBUG "gcblock: 0x%08x\n", c->gcblock->offset);
++ /* The superblocks are considered to be equivalent if the underlying MTD
++ device is the same one */
++ if (c->mtd == p->mtd) {
++ D1(printk(KERN_DEBUG "jffs2_sb_compare: match on device %d (\"%s\")\n", p->mtd->index, p->mtd->name));
++ return 1;
+ } else {
+- printk(KERN_DEBUG "gcblock: NULL\n");
++ D1(printk(KERN_DEBUG "jffs2_sb_compare: No match, device %d (\"%s\"), device %d (\"%s\")\n",
++ c->mtd->index, c->mtd->name, p->mtd->index, p->mtd->name));
++ return 0;
+ }
+- if (list_empty(&c->clean_list)) {
+- printk(KERN_DEBUG "clean_list: empty\n");
+- } else {
+- struct list_head *this;
++}
+
+- list_for_each(this, &c->clean_list) {
+- struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
+- printk(KERN_DEBUG "clean_list: %08x\n", jeb->offset);
+- }
+- }
+- if (list_empty(&c->dirty_list)) {
+- printk(KERN_DEBUG "dirty_list: empty\n");
+- } else {
+- struct list_head *this;
++static int jffs2_sb_set(struct super_block *sb, void *data)
++{
++ struct jffs2_sb_info *p = data;
+
+- list_for_each(this, &c->dirty_list) {
+- struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
+- printk(KERN_DEBUG "dirty_list: %08x\n", jeb->offset);
+- }
+- }
+- if (list_empty(&c->erasing_list)) {
+- printk(KERN_DEBUG "erasing_list: empty\n");
+- } else {
+- struct list_head *this;
++ /* For persistence of NFS exports etc. we use the same s_dev
++ each time we mount the device, don't just use an anonymous
++ device */
++ sb->s_fs_info = p;
++ p->os_priv = sb;
++ sb->s_dev = MKDEV(MTD_BLOCK_MAJOR, p->mtd->index);
+
+- list_for_each(this, &c->erasing_list) {
+- struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
+- printk(KERN_DEBUG "erasing_list: %08x\n", jeb->offset);
+- }
+- }
+- if (list_empty(&c->erase_pending_list)) {
+- printk(KERN_DEBUG "erase_pending_list: empty\n");
+- } else {
+- struct list_head *this;
++ return 0;
++}
+
+- list_for_each(this, &c->erase_pending_list) {
+- struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
+- printk(KERN_DEBUG "erase_pending_list: %08x\n", jeb->offset);
+- }
+- }
+- if (list_empty(&c->free_list)) {
+- printk(KERN_DEBUG "free_list: empty\n");
+- } else {
+- struct list_head *this;
++static struct super_block *jffs2_get_sb_mtd(struct file_system_type *fs_type,
++ int flags, const char *dev_name,
++ void *data, struct mtd_info *mtd)
++{
++ struct super_block *sb;
++ struct jffs2_sb_info *c;
++ int ret;
+
+- list_for_each(this, &c->free_list) {
+- struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
+- printk(KERN_DEBUG "free_list: %08x\n", jeb->offset);
+- }
+- }
+- if (list_empty(&c->bad_list)) {
+- printk(KERN_DEBUG "bad_list: empty\n");
+- } else {
+- struct list_head *this;
++ c = kmalloc(sizeof(*c), GFP_KERNEL);
++ if (!c)
++ return ERR_PTR(-ENOMEM);
++ memset(c, 0, sizeof(*c));
++ c->mtd = mtd;
+
+- list_for_each(this, &c->bad_list) {
+- struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
+- printk(KERN_DEBUG "bad_list: %08x\n", jeb->offset);
+- }
+- }
+- if (list_empty(&c->bad_used_list)) {
+- printk(KERN_DEBUG "bad_used_list: empty\n");
+- } else {
+- struct list_head *this;
++ sb = sget(fs_type, jffs2_sb_compare, jffs2_sb_set, c);
++
++ if (IS_ERR(sb))
++ goto out_put;
+
+- list_for_each(this, &c->bad_used_list) {
+- struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
+- printk(KERN_DEBUG "bad_used_list: %08x\n", jeb->offset);
++ if (sb->s_root) {
++ /* New mountpoint for JFFS2 which is already mounted */
++ D1(printk(KERN_DEBUG "jffs2_get_sb_mtd(): Device %d (\"%s\") is already mounted\n",
++ mtd->index, mtd->name));
++ goto out_put;
+ }
++
++ D1(printk(KERN_DEBUG "jffs2_get_sb_mtd(): New superblock for device %d (\"%s\")\n",
++ mtd->index, mtd->name));
++
++ sb->s_op = &jffs2_super_operations;
++
++ ret = jffs2_do_fill_super(sb, data, (flags&MS_VERBOSE)?1:0);
++
++ if (ret) {
++ /* Failure case... */
++ up_write(&sb->s_umount);
++ deactivate_super(sb);
++ return ERR_PTR(ret);
+ }
+-#endif /* CONFIG_JFFS2_FS_DEBUG */
+
+- spin_unlock_bh(&c->erase_completion_lock);
++ sb->s_flags |= MS_ACTIVE;
++ return sb;
+
++ out_put:
++ kfree(c);
++ put_mtd_device(mtd);
+
+- return 0;
++ return sb;
+ }
+
+-static struct super_block *jffs2_read_super(struct super_block *sb, void *data, int silent)
++static struct super_block *jffs2_get_sb_mtdnr(struct file_system_type *fs_type,
++ int flags, const char *dev_name,
++ void *data, int mtdnr)
+ {
+- struct jffs2_sb_info *c;
+- struct inode *root_i;
+- int i;
+-
+- D1(printk(KERN_DEBUG "jffs2: read_super for device %s\n", kdevname(sb->s_dev)));
++ struct mtd_info *mtd;
+
+- if (MAJOR(sb->s_dev) != MTD_BLOCK_MAJOR) {
+- if (!silent)
+- printk(KERN_DEBUG "jffs2: attempt to mount non-MTD device %s\n", kdevname(sb->s_dev));
+- return NULL;
++ mtd = get_mtd_device(NULL, mtdnr);
++ if (!mtd) {
++ D1(printk(KERN_DEBUG "jffs2: MTD device #%u doesn't appear to exist\n", mtdnr));
++ return ERR_PTR(-EINVAL);
+ }
+
+- c = JFFS2_SB_INFO(sb);
+- memset(c, 0, sizeof(*c));
++ return jffs2_get_sb_mtd(fs_type, flags, dev_name, data, mtd);
++}
+
+- c->mtd = get_mtd_device(NULL, MINOR(sb->s_dev));
+- if (!c->mtd) {
+- D1(printk(KERN_DEBUG "jffs2: MTD device #%u doesn't appear to exist\n", MINOR(sb->s_dev)));
+- return NULL;
++static struct super_block *jffs2_get_sb(struct file_system_type *fs_type,
++ int flags, const char *dev_name,
++ void *data)
++{
++ int err;
++ struct nameidata nd;
++ int mtdnr;
++
++ if (!dev_name)
++ return ERR_PTR(-EINVAL);
++
++ D1(printk(KERN_DEBUG "jffs2_get_sb(): dev_name \"%s\"\n", dev_name));
++
++ /* The preferred way of mounting in future; especially when
++ CONFIG_BLK_DEV is implemented - we specify the underlying
++ MTD device by number or by name, so that we don't require
++ block device support to be present in the kernel. */
++
++ /* FIXME: How to do the root fs this way? */
++
++ if (dev_name[0] == 'm' && dev_name[1] == 't' && dev_name[2] == 'd') {
++ /* Probably mounting without the blkdev crap */
++ if (dev_name[3] == ':') {
++ struct mtd_info *mtd;
++
++ /* Mount by MTD device name */
++ D1(printk(KERN_DEBUG "jffs2_get_sb(): mtd:%%s, name \"%s\"\n", dev_name+4));
++ for (mtdnr = 0; mtdnr < MAX_MTD_DEVICES; mtdnr++) {
++ mtd = get_mtd_device(NULL, mtdnr);
++ if (mtd) {
++ if (!strcmp(mtd->name, dev_name+4))
++ return jffs2_get_sb_mtd(fs_type, flags, dev_name, data, mtd);
++ put_mtd_device(mtd);
+ }
+- c->sector_size = c->mtd->erasesize;
+- c->free_size = c->flash_size = c->mtd->size;
+- c->nr_blocks = c->mtd->size / c->mtd->erasesize;
+- c->blocks = kmalloc(sizeof(struct jffs2_eraseblock) * c->nr_blocks, GFP_KERNEL);
+- if (!c->blocks)
+- goto out_mtd;
+- for (i=0; i<c->nr_blocks; i++) {
+- INIT_LIST_HEAD(&c->blocks[i].list);
+- c->blocks[i].offset = i * c->sector_size;
+- c->blocks[i].free_size = c->sector_size;
+- c->blocks[i].dirty_size = 0;
+- c->blocks[i].used_size = 0;
+- c->blocks[i].first_node = NULL;
+- c->blocks[i].last_node = NULL;
+- }
+-
+- spin_lock_init(&c->nodelist_lock);
+- init_MUTEX(&c->alloc_sem);
+- init_waitqueue_head(&c->erase_wait);
+- spin_lock_init(&c->erase_completion_lock);
+- spin_lock_init(&c->inocache_lock);
+-
+- INIT_LIST_HEAD(&c->clean_list);
+- INIT_LIST_HEAD(&c->dirty_list);
+- INIT_LIST_HEAD(&c->erasing_list);
+- INIT_LIST_HEAD(&c->erase_pending_list);
+- INIT_LIST_HEAD(&c->erase_complete_list);
+- INIT_LIST_HEAD(&c->free_list);
+- INIT_LIST_HEAD(&c->bad_list);
+- INIT_LIST_HEAD(&c->bad_used_list);
+- c->highest_ino = 1;
+-
+- if (jffs2_build_filesystem(c)) {
+- D1(printk(KERN_DEBUG "build_fs failed\n"));
+- goto out_nodes;
+ }
++ printk(KERN_NOTICE "jffs2_get_sb(): MTD device with name \"%s\" not found.\n", dev_name+4);
++ } else if (isdigit(dev_name[3])) {
++ /* Mount by MTD device number name */
++ char *endptr;
+
+- sb->s_op = &jffs2_super_operations;
++ mtdnr = simple_strtoul(dev_name+3, &endptr, 0);
++ if (!*endptr) {
++ /* It was a valid number */
++ D1(printk(KERN_DEBUG "jffs2_get_sb(): mtd%%d, mtdnr %d\n", mtdnr));
++ return jffs2_get_sb_mtdnr(fs_type, flags, dev_name, data, mtdnr);
++ }
++ }
++ }
+
+- D1(printk(KERN_DEBUG "jffs2_read_super(): Getting root inode\n"));
+- root_i = iget(sb, 1);
+- if (is_bad_inode(root_i)) {
+- D1(printk(KERN_WARNING "get root inode failed\n"));
+- goto out_nodes;
++ /* Try the old way - the hack where we allowed users to mount
++ /dev/mtdblock$(n) but didn't actually _use_ the blkdev */
++
++ err = path_lookup(dev_name, LOOKUP_FOLLOW, &nd);
++
++ D1(printk(KERN_DEBUG "jffs2_get_sb(): path_lookup() returned %d, inode %p\n",
++ err, nd.dentry->d_inode));
++
++ if (err)
++ return ERR_PTR(err);
++
++ err = -EINVAL;
++
++ if (!S_ISBLK(nd.dentry->d_inode->i_mode))
++ goto out;
++
++ if (nd.mnt->mnt_flags & MNT_NODEV) {
++ err = -EACCES;
++ goto out;
+ }
+
+- D1(printk(KERN_DEBUG "jffs2_read_super(): d_alloc_root()\n"));
+- sb->s_root = d_alloc_root(root_i);
+- if (!sb->s_root)
+- goto out_root_i;
++ if (imajor(nd.dentry->d_inode) != MTD_BLOCK_MAJOR) {
++ if (!(flags & MS_VERBOSE)) /* Yes I mean this. Strangely */
++ printk(KERN_NOTICE "Attempt to mount non-MTD device \"%s\" as JFFS2\n",
++ dev_name);
++ goto out;
++ }
+
+-#if LINUX_VERSION_CODE >= 0x20403
+- sb->s_maxbytes = 0xFFFFFFFF;
+-#endif
+- sb->s_blocksize = PAGE_CACHE_SIZE;
+- sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
+- sb->s_magic = JFFS2_SUPER_MAGIC;
+- if (!(sb->s_flags & MS_RDONLY))
+- jffs2_start_garbage_collect_thread(c);
+- return sb;
++ mtdnr = iminor(nd.dentry->d_inode);
++ path_release(&nd);
+
+- out_root_i:
+- iput(root_i);
+- out_nodes:
+- jffs2_free_ino_caches(c);
+- jffs2_free_raw_node_refs(c);
+- kfree(c->blocks);
+- out_mtd:
+- put_mtd_device(c->mtd);
+- return NULL;
++ return jffs2_get_sb_mtdnr(fs_type, flags, dev_name, data, mtdnr);
++
++out:
++ path_release(&nd);
++ return ERR_PTR(err);
+ }
+
+-void jffs2_put_super (struct super_block *sb)
++static void jffs2_put_super (struct super_block *sb)
+ {
+ struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
+
+@@ -293,74 +259,53 @@
+
+ if (!(sb->s_flags & MS_RDONLY))
+ jffs2_stop_garbage_collect_thread(c);
++ down(&c->alloc_sem);
++ jffs2_flush_wbuf_pad(c);
++ up(&c->alloc_sem);
+ jffs2_free_ino_caches(c);
+ jffs2_free_raw_node_refs(c);
+ kfree(c->blocks);
++ jffs2_nand_flash_cleanup(c);
++ kfree(c->inocache_list);
+ if (c->mtd->sync)
+ c->mtd->sync(c->mtd);
+- put_mtd_device(c->mtd);
+
+ D1(printk(KERN_DEBUG "jffs2_put_super returning\n"));
+ }
+
+-int jffs2_remount_fs (struct super_block *sb, int *flags, char *data)
++static void jffs2_kill_sb(struct super_block *sb)
+ {
+ struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
+-
+- if (c->flags & JFFS2_SB_FLAG_RO && !(sb->s_flags & MS_RDONLY))
+- return -EROFS;
+-
+- /* We stop if it was running, then restart if it needs to.
+- This also catches the case where it was stopped and this
+- is just a remount to restart it */
+- if (!(sb->s_flags & MS_RDONLY))
+- jffs2_stop_garbage_collect_thread(c);
+-
+- if (!(*flags & MS_RDONLY))
+- jffs2_start_garbage_collect_thread(c);
+-
+- sb->s_flags = (sb->s_flags & ~MS_RDONLY)|(*flags & MS_RDONLY);
+-
+- return 0;
+-}
+-
+-void jffs2_write_super (struct super_block *sb)
+-{
+- struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
+- sb->s_dirt = 0;
+-
+- if (sb->s_flags & MS_RDONLY)
+- return;
+-
+- jffs2_garbage_collect_trigger(c);
+- jffs2_erase_pending_blocks(c);
+- jffs2_mark_erased_blocks(c);
++ generic_shutdown_super(sb);
++ put_mtd_device(c->mtd);
++ kfree(c);
+ }
+
+-
+-static DECLARE_FSTYPE_DEV(jffs2_fs_type, "jffs2", jffs2_read_super);
++static struct file_system_type jffs2_fs_type = {
++ .owner = THIS_MODULE,
++ .name = "jffs2",
++ .get_sb = jffs2_get_sb,
++ .kill_sb = jffs2_kill_sb,
++};
+
+ static int __init init_jffs2_fs(void)
+ {
+ int ret;
+
+- printk(KERN_NOTICE "JFFS2 version 2.1. (C) 2001 Red Hat, Inc., designed by Axis Communications AB.\n");
+-
+-#ifdef JFFS2_OUT_OF_KERNEL
+- /* sanity checks. Could we do these at compile time? */
+- if (sizeof(struct jffs2_sb_info) > sizeof (((struct super_block *)NULL)->u)) {
+- printk(KERN_ERR "JFFS2 error: struct jffs2_sb_info (%d bytes) doesn't fit in the super_block union (%d bytes)\n",
+- sizeof(struct jffs2_sb_info), sizeof (((struct super_block *)NULL)->u));
+- return -EIO;
+- }
+-
+- if (sizeof(struct jffs2_inode_info) > sizeof (((struct inode *)NULL)->u)) {
+- printk(KERN_ERR "JFFS2 error: struct jffs2_inode_info (%d bytes) doesn't fit in the inode union (%d bytes)\n",
+- sizeof(struct jffs2_inode_info), sizeof (((struct inode *)NULL)->u));
+- return -EIO;
+- }
++ printk(KERN_INFO "JFFS2 version 2.2."
++#ifdef CONFIG_FS_JFFS2_NAND
++ " (NAND)"
+ #endif
++ " (C) 2001-2003 Red Hat, Inc.\n");
+
++ jffs2_inode_cachep = kmem_cache_create("jffs2_i",
++ sizeof(struct jffs2_inode_info),
++ 0, SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT,
++ jffs2_i_init_once, NULL);
++ if (!jffs2_inode_cachep) {
++ printk(KERN_ERR "JFFS2 error: Failed to initialise inode cache\n");
++ return -ENOMEM;
++ }
+ ret = jffs2_zlib_init();
+ if (ret) {
+ printk(KERN_ERR "JFFS2 error: Failed to initialise zlib workspaces\n");
+@@ -388,9 +333,10 @@
+
+ static void __exit exit_jffs2_fs(void)
+ {
++ unregister_filesystem(&jffs2_fs_type);
+ jffs2_destroy_slab_caches();
+ jffs2_zlib_exit();
+- unregister_filesystem(&jffs2_fs_type);
++ kmem_cache_destroy(jffs2_inode_cachep);
+ }
+
+ module_init(init_jffs2_fs);
+diff -Nurb linux-mips-2.4.27/fs/jffs2/symlink.c linux/fs/jffs2/symlink.c
+--- linux-mips-2.4.27/fs/jffs2/symlink.c 2002-06-27 00:36:20.000000000 +0200
++++ linux/fs/jffs2/symlink.c 2004-11-19 10:25:12.129164832 +0100
+@@ -3,35 +3,11 @@
+ *
+ * Copyright (C) 2001, 2002 Red Hat, Inc.
+ *
+- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
++ * Created by David Woodhouse <dwmw2@redhat.com>
+ *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
++ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+- * The contents of this file are subject to the Red Hat eCos Public
+- * License Version 1.1 (the "Licence"); you may not use this file
+- * except in compliance with the Licence. You may obtain a copy of
+- * the Licence at http://www.redhat.com/
+- *
+- * Software distributed under the Licence is distributed on an "AS IS"
+- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+- * See the Licence for the specific language governing rights and
+- * limitations under the Licence.
+- *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
+- *
+- * Alternatively, the contents of this file may be used under the
+- * terms of the GNU General Public License version 2 (the "GPL"), in
+- * which case the provisions of the GPL are applicable instead of the
+- * above. If you wish to allow the use of your version of this file
+- * only under the terms of the GPL and not to allow others to use your
+- * version of this file under the RHEPL, indicate your decision by
+- * deleting the provisions above and replace them with the notice and
+- * other provisions required by the GPL. If you do not delete the
+- * provisions above, a recipient may use your version of this file
+- * under either the RHEPL or the GPL.
+- *
+- * $Id$
++ * $Id$
+ *
+ */
+
+@@ -39,7 +15,6 @@
+ #include <linux/kernel.h>
+ #include <linux/slab.h>
+ #include <linux/fs.h>
+-#include <linux/jffs2.h>
+ #include "nodelist.h"
+
+ int jffs2_readlink(struct dentry *dentry, char *buffer, int buflen);
+@@ -47,45 +22,17 @@
+
+ struct inode_operations jffs2_symlink_inode_operations =
+ {
+- readlink: jffs2_readlink,
+- follow_link: jffs2_follow_link,
+- setattr: jffs2_setattr
++ .readlink = jffs2_readlink,
++ .follow_link = jffs2_follow_link,
++ .setattr = jffs2_setattr
+ };
+
+-static char *jffs2_getlink(struct dentry *dentry)
+-{
+- struct jffs2_inode_info *f = JFFS2_INODE_INFO(dentry->d_inode);
+- char *buf;
+- int ret;
+-
+- down(&f->sem);
+- if (!f->metadata) {
+- up(&f->sem);
+- printk(KERN_NOTICE "No metadata for symlink inode #%lu\n", dentry->d_inode->i_ino);
+- return ERR_PTR(-EINVAL);
+- }
+- buf = kmalloc(f->metadata->size+1, GFP_USER);
+- if (!buf) {
+- up(&f->sem);
+- return ERR_PTR(-ENOMEM);
+- }
+- buf[f->metadata->size]=0;
+-
+- ret = jffs2_read_dnode(JFFS2_SB_INFO(dentry->d_inode->i_sb), f->metadata, buf, 0, f->metadata->size);
+- up(&f->sem);
+- if (ret) {
+- kfree(buf);
+- return ERR_PTR(ret);
+- }
+- return buf;
+-
+-}
+ int jffs2_readlink(struct dentry *dentry, char *buffer, int buflen)
+ {
+ unsigned char *kbuf;
+ int ret;
+
+- kbuf = jffs2_getlink(dentry);
++ kbuf = jffs2_getlink(JFFS2_SB_INFO(dentry->d_inode->i_sb), JFFS2_INODE_INFO(dentry->d_inode));
+ if (IS_ERR(kbuf))
+ return PTR_ERR(kbuf);
+
+@@ -99,7 +46,7 @@
+ unsigned char *buf;
+ int ret;
+
+- buf = jffs2_getlink(dentry);
++ buf = jffs2_getlink(JFFS2_SB_INFO(dentry->d_inode->i_sb), JFFS2_INODE_INFO(dentry->d_inode));
+
+ if (IS_ERR(buf))
+ return PTR_ERR(buf);
+diff -Nurb linux-mips-2.4.27/fs/jffs2/wbuf.c linux/fs/jffs2/wbuf.c
+--- linux-mips-2.4.27/fs/jffs2/wbuf.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/fs/jffs2/wbuf.c 2004-11-19 10:25:12.131164528 +0100
+@@ -0,0 +1,1156 @@
++/*
++ * JFFS2 -- Journalling Flash File System, Version 2.
++ *
++ * Copyright (C) 2001-2003 Red Hat, Inc.
++ *
++ * Created by David Woodhouse <dwmw2@redhat.com>
++ *
++ * For licensing information, see the file 'LICENCE' in this directory.
++ *
++ * $Id$
++ *
++ */
++
++#include <linux/kernel.h>
++#include <linux/slab.h>
++#include <linux/mtd/mtd.h>
++#include <linux/crc32.h>
++#include <linux/mtd/nand.h>
++#include "nodelist.h"
++
++/* For testing write failures */
++#undef BREAKME
++#undef BREAKMEHEADER
++
++#ifdef BREAKME
++static unsigned char *brokenbuf;
++#endif
++
++/* max. erase failures before we mark a block bad */
++#define MAX_ERASE_FAILURES 5
++
++/* two seconds timeout for timed wbuf-flushing */
++#define WBUF_FLUSH_TIMEOUT 2 * HZ
++
++struct jffs2_inodirty {
++ uint32_t ino;
++ struct jffs2_inodirty *next;
++};
++
++static struct jffs2_inodirty inodirty_nomem;
++
++static int jffs2_wbuf_pending_for_ino(struct jffs2_sb_info *c, uint32_t ino)
++{
++ struct jffs2_inodirty *this = c->wbuf_inodes;
++
++ /* If a malloc failed, consider _everything_ dirty */
++ if (this == &inodirty_nomem)
++ return 1;
++
++ /* If ino == 0, _any_ non-GC writes mean 'yes' */
++ if (this && !ino)
++ return 1;
++
++ /* Look to see if the inode in question is pending in the wbuf */
++ while (this) {
++ if (this->ino == ino)
++ return 1;
++ this = this->next;
++ }
++ return 0;
++}
++
++static void jffs2_clear_wbuf_ino_list(struct jffs2_sb_info *c)
++{
++ struct jffs2_inodirty *this;
++
++ this = c->wbuf_inodes;
++
++ if (this != &inodirty_nomem) {
++ while (this) {
++ struct jffs2_inodirty *next = this->next;
++ kfree(this);
++ this = next;
++ }
++ }
++ c->wbuf_inodes = NULL;
++}
++
++static void jffs2_wbuf_dirties_inode(struct jffs2_sb_info *c, uint32_t ino)
++{
++ struct jffs2_inodirty *new;
++
++ /* Mark the superblock dirty so that kupdated will flush... */
++ OFNI_BS_2SFFJ(c)->s_dirt = 1;
++
++ if (jffs2_wbuf_pending_for_ino(c, ino))
++ return;
++
++ new = kmalloc(sizeof(*new), GFP_KERNEL);
++ if (!new) {
++ D1(printk(KERN_DEBUG "No memory to allocate inodirty. Fallback to all considered dirty\n"));
++ jffs2_clear_wbuf_ino_list(c);
++ c->wbuf_inodes = &inodirty_nomem;
++ return;
++ }
++ new->ino = ino;
++ new->next = c->wbuf_inodes;
++ c->wbuf_inodes = new;
++ return;
++}
++
++static inline void jffs2_refile_wbuf_blocks(struct jffs2_sb_info *c)
++{
++ struct list_head *this, *next;
++ static int n;
++
++ if (list_empty(&c->erasable_pending_wbuf_list))
++ return;
++
++ list_for_each_safe(this, next, &c->erasable_pending_wbuf_list) {
++ struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
++
++ D1(printk(KERN_DEBUG "Removing eraseblock at 0x%08x from erasable_pending_wbuf_list...\n", jeb->offset));
++ list_del(this);
++ if ((jiffies + (n++)) & 127) {
++ /* Most of the time, we just erase it immediately. Otherwise we
++ spend ages scanning it on mount, etc. */
++ D1(printk(KERN_DEBUG "...and adding to erase_pending_list\n"));
++ list_add_tail(&jeb->list, &c->erase_pending_list);
++ c->nr_erasing_blocks++;
++ jffs2_erase_pending_trigger(c);
++ } else {
++ /* Sometimes, however, we leave it elsewhere so it doesn't get
++ immediately reused, and we spread the load a bit. */
++ D1(printk(KERN_DEBUG "...and adding to erasable_list\n"));
++ list_add_tail(&jeb->list, &c->erasable_list);
++ }
++ }
++}
++
++/* Recover from failure to write wbuf. Recover the nodes up to the
++ * wbuf, not the one which we were starting to try to write. */
++
++static void jffs2_wbuf_recover(struct jffs2_sb_info *c)
++{
++ struct jffs2_eraseblock *jeb, *new_jeb;
++ struct jffs2_raw_node_ref **first_raw, **raw;
++ size_t retlen;
++ int ret;
++ unsigned char *buf;
++ uint32_t start, end, ofs, len;
++
++ spin_lock(&c->erase_completion_lock);
++
++ jeb = &c->blocks[c->wbuf_ofs / c->sector_size];
++
++ D1(printk("About to refile bad block at %08x\n", jeb->offset));
++
++ D2(jffs2_dump_block_lists(c));
++ /* File the existing block on the bad_used_list.... */
++ if (c->nextblock == jeb)
++ c->nextblock = NULL;
++ else /* Not sure this should ever happen... need more coffee */
++ list_del(&jeb->list);
++ if (jeb->first_node) {
++ D1(printk("Refiling block at %08x to bad_used_list\n", jeb->offset));
++ list_add(&jeb->list, &c->bad_used_list);
++ } else {
++ BUG();
++ /* It has to have had some nodes or we couldn't be here */
++ D1(printk("Refiling block at %08x to erase_pending_list\n", jeb->offset));
++ list_add(&jeb->list, &c->erase_pending_list);
++ c->nr_erasing_blocks++;
++ jffs2_erase_pending_trigger(c);
++ }
++ D2(jffs2_dump_block_lists(c));
++
++ /* Adjust its size counts accordingly */
++ c->wasted_size += jeb->free_size;
++ c->free_size -= jeb->free_size;
++ jeb->wasted_size += jeb->free_size;
++ jeb->free_size = 0;
++
++ ACCT_SANITY_CHECK(c,jeb);
++ D1(ACCT_PARANOIA_CHECK(jeb));
++
++ /* Find the first node to be recovered, by skipping over every
++ node which ends before the wbuf starts, or which is obsolete. */
++ first_raw = &jeb->first_node;
++ while (*first_raw &&
++ (ref_obsolete(*first_raw) ||
++ (ref_offset(*first_raw)+ref_totlen(c, jeb, *first_raw)) < c->wbuf_ofs)) {
++ D1(printk(KERN_DEBUG "Skipping node at 0x%08x(%d)-0x%08x which is either before 0x%08x or obsolete\n",
++ ref_offset(*first_raw), ref_flags(*first_raw),
++ (ref_offset(*first_raw) + ref_totlen(c, jeb, *first_raw)),
++ c->wbuf_ofs));
++ first_raw = &(*first_raw)->next_phys;
++ }
++
++ if (!*first_raw) {
++ /* All nodes were obsolete. Nothing to recover. */
++ D1(printk(KERN_DEBUG "No non-obsolete nodes to be recovered. Just filing block bad\n"));
++ spin_unlock(&c->erase_completion_lock);
++ return;
++ }
++
++ start = ref_offset(*first_raw);
++ end = ref_offset(*first_raw) + ref_totlen(c, jeb, *first_raw);
++
++ /* Find the last node to be recovered */
++ raw = first_raw;
++ while ((*raw)) {
++ if (!ref_obsolete(*raw))
++ end = ref_offset(*raw) + ref_totlen(c, jeb, *raw);
++
++ raw = &(*raw)->next_phys;
++ }
++ spin_unlock(&c->erase_completion_lock);
++
++ D1(printk(KERN_DEBUG "wbuf recover %08x-%08x\n", start, end));
++
++ buf = NULL;
++ if (start < c->wbuf_ofs) {
++ /* First affected node was already partially written.
++ * Attempt to reread the old data into our buffer. */
++
++ buf = kmalloc(end - start, GFP_KERNEL);
++ if (!buf) {
++ printk(KERN_CRIT "Malloc failure in wbuf recovery. Data loss ensues.\n");
++
++ goto read_failed;
++ }
++
++ /* Do the read... */
++ ret = c->mtd->read_ecc(c->mtd, start, c->wbuf_ofs - start, &retlen, buf, NULL, c->oobinfo);
++ if (ret == -EIO && retlen == c->wbuf_ofs - start) {
++ /* ECC recovered */
++ ret = 0;
++ }
++ if (ret || retlen != c->wbuf_ofs - start) {
++ printk(KERN_CRIT "Old data are already lost in wbuf recovery. Data loss ensues.\n");
++
++ kfree(buf);
++ buf = NULL;
++ read_failed:
++ first_raw = &(*first_raw)->next_phys;
++ /* If this was the only node to be recovered, give up */
++ if (!(*first_raw))
++ return;
++
++ /* It wasn't. Go on and try to recover nodes complete in the wbuf */
++ start = ref_offset(*first_raw);
++ } else {
++ /* Read succeeded. Copy the remaining data from the wbuf */
++ memcpy(buf + (c->wbuf_ofs - start), c->wbuf, end - c->wbuf_ofs);
++ }
++ }
++ /* OK... we're to rewrite (end-start) bytes of data from first_raw onwards.
++ Either 'buf' contains the data, or we find it in the wbuf */
++
++
++ /* ... and get an allocation of space from a shiny new block instead */
++ ret = jffs2_reserve_space_gc(c, end-start, &ofs, &len);
++ if (ret) {
++ printk(KERN_WARNING "Failed to allocate space for wbuf recovery. Data loss ensues.\n");
++ if (buf)
++ kfree(buf);
++ return;
++ }
++ if (end-start >= c->wbuf_pagesize) {
++ /* Need to do another write immediately. This, btw,
++ means that we'll be writing from 'buf' and not from
++ the wbuf. Since if we're writing from the wbuf there
++ won't be more than a wbuf full of data, now will
++ there? :) */
++
++ uint32_t towrite = (end-start) - ((end-start)%c->wbuf_pagesize);
++
++ D1(printk(KERN_DEBUG "Write 0x%x bytes at 0x%08x in wbuf recover\n",
++ towrite, ofs));
++
++#ifdef BREAKMEHEADER
++ static int breakme;
++ if (breakme++ == 20) {
++ printk(KERN_NOTICE "Faking write error at 0x%08x\n", ofs);
++ breakme = 0;
++ c->mtd->write_ecc(c->mtd, ofs, towrite, &retlen,
++ brokenbuf, NULL, c->oobinfo);
++ ret = -EIO;
++ } else
++#endif
++ ret = c->mtd->write_ecc(c->mtd, ofs, towrite, &retlen,
++ buf, NULL, c->oobinfo);
++
++ if (ret || retlen != towrite) {
++ /* Argh. We tried. Really we did. */
++ printk(KERN_CRIT "Recovery of wbuf failed due to a second write error\n");
++ kfree(buf);
++
++ if (retlen) {
++ struct jffs2_raw_node_ref *raw2;
++
++ raw2 = jffs2_alloc_raw_node_ref();
++ if (!raw2)
++ return;
++
++ raw2->flash_offset = ofs | REF_OBSOLETE;
++ raw2->__totlen = ref_totlen(c, jeb, *first_raw);
++ raw2->next_phys = NULL;
++ raw2->next_in_ino = NULL;
++
++ jffs2_add_physical_node_ref(c, raw2);
++ }
++ return;
++ }
++ printk(KERN_NOTICE "Recovery of wbuf succeeded to %08x\n", ofs);
++
++ c->wbuf_len = (end - start) - towrite;
++ c->wbuf_ofs = ofs + towrite;
++ memcpy(c->wbuf, buf + towrite, c->wbuf_len);
++ /* Don't muck about with c->wbuf_inodes. False positives are harmless. */
++
++ kfree(buf);
++ } else {
++ /* OK, now we're left with the dregs in whichever buffer we're using */
++ if (buf) {
++ memcpy(c->wbuf, buf, end-start);
++ kfree(buf);
++ } else {
++ memmove(c->wbuf, c->wbuf + (start - c->wbuf_ofs), end - start);
++ }
++ c->wbuf_ofs = ofs;
++ c->wbuf_len = end - start;
++ }
++
++ /* Now sort out the jffs2_raw_node_refs, moving them from the old to the next block */
++ new_jeb = &c->blocks[ofs / c->sector_size];
++
++ spin_lock(&c->erase_completion_lock);
++ if (new_jeb->first_node) {
++ /* Odd, but possible with ST flash later maybe */
++ new_jeb->last_node->next_phys = *first_raw;
++ } else {
++ new_jeb->first_node = *first_raw;
++ }
++
++ raw = first_raw;
++ while (*raw) {
++ uint32_t rawlen = ref_totlen(c, jeb, *raw);
++
++ D1(printk(KERN_DEBUG "Refiling block of %08x at %08x(%d) to %08x\n",
++ rawlen, ref_offset(*raw), ref_flags(*raw), ofs));
++
++ if (ref_obsolete(*raw)) {
++ /* Shouldn't really happen much */
++ new_jeb->dirty_size += rawlen;
++ new_jeb->free_size -= rawlen;
++ c->dirty_size += rawlen;
++ } else {
++ new_jeb->used_size += rawlen;
++ new_jeb->free_size -= rawlen;
++ jeb->dirty_size += rawlen;
++ jeb->used_size -= rawlen;
++ c->dirty_size += rawlen;
++ }
++ c->free_size -= rawlen;
++ (*raw)->flash_offset = ofs | ref_flags(*raw);
++ ofs += rawlen;
++ new_jeb->last_node = *raw;
++
++ raw = &(*raw)->next_phys;
++ }
++
++ /* Fix up the original jeb now it's on the bad_list */
++ *first_raw = NULL;
++ if (first_raw == &jeb->first_node) {
++ jeb->last_node = NULL;
++ D1(printk(KERN_DEBUG "Failing block at %08x is now empty. Moving to erase_pending_list\n", jeb->offset));
++ list_del(&jeb->list);
++ list_add(&jeb->list, &c->erase_pending_list);
++ c->nr_erasing_blocks++;
++ jffs2_erase_pending_trigger(c);
++ }
++ else
++ jeb->last_node = container_of(first_raw, struct jffs2_raw_node_ref, next_phys);
++
++ ACCT_SANITY_CHECK(c,jeb);
++ D1(ACCT_PARANOIA_CHECK(jeb));
++
++ ACCT_SANITY_CHECK(c,new_jeb);
++ D1(ACCT_PARANOIA_CHECK(new_jeb));
++
++ spin_unlock(&c->erase_completion_lock);
++
++ D1(printk(KERN_DEBUG "wbuf recovery completed OK\n"));
++}
++
++/* Meaning of pad argument:
++ 0: Do not pad. Probably pointless - we only ever use this when we can't pad anyway.
++ 1: Pad, do not adjust nextblock free_size
++ 2: Pad, adjust nextblock free_size
++*/
++static int __jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad)
++{
++ int ret;
++ size_t retlen;
++
++ /* Nothing to do if not NAND flash. In particular, we shouldn't
++ del_timer() the timer we never initialised. */
++ if (jffs2_can_mark_obsolete(c))
++ return 0;
++
++ if (!down_trylock(&c->alloc_sem)) {
++ up(&c->alloc_sem);
++ printk(KERN_CRIT "jffs2_flush_wbuf() called with alloc_sem not locked!\n");
++ BUG();
++ }
++
++ if(!c->wbuf || !c->wbuf_len)
++ return 0;
++
++ /* claim remaining space on the page
++ this happens, if we have a change to a new block,
++ or if fsync forces us to flush the writebuffer.
++ if we have a switch to next page, we will not have
++ enough remaining space for this.
++ */
++ if (pad) {
++ c->wbuf_len = PAD(c->wbuf_len);
++
++ if ( c->wbuf_len + sizeof(struct jffs2_unknown_node) < c->wbuf_pagesize) {
++ struct jffs2_unknown_node *padnode = (void *)(c->wbuf + c->wbuf_len);
++ padnode->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
++ padnode->nodetype = cpu_to_je16(JFFS2_NODETYPE_PADDING);
++ padnode->totlen = cpu_to_je32(c->wbuf_pagesize - c->wbuf_len);
++ padnode->hdr_crc = cpu_to_je32(crc32(0, padnode, sizeof(*padnode)-4));
++ }
++ }
++ /* else jffs2_flash_writev has actually filled in the rest of the
++ buffer for us, and will deal with the node refs etc. later. */
++
++#ifdef BREAKME
++ static int breakme;
++ if (breakme++ == 20) {
++ printk(KERN_NOTICE "Faking write error at 0x%08x\n", c->wbuf_ofs);
++ breakme = 0;
++ c->mtd->write_ecc(c->mtd, c->wbuf_ofs, c->wbuf_pagesize,
++ &retlen, brokenbuf, NULL, c->oobinfo);
++ ret = -EIO;
++ } else
++#endif
++ ret = c->mtd->write_ecc(c->mtd, c->wbuf_ofs, c->wbuf_pagesize, &retlen, c->wbuf, NULL, c->oobinfo);
++
++
++ if (ret || retlen != c->wbuf_pagesize) {
++ if (ret)
++ printk(KERN_WARNING "jffs2_flush_wbuf(): Write failed with %d\n",ret);
++ else {
++ printk(KERN_WARNING "jffs2_flush_wbuf(): Write was short: %zd instead of %d\n",
++ retlen, c->wbuf_pagesize);
++ ret = -EIO;
++ }
++
++ jffs2_wbuf_recover(c);
++
++ return ret;
++ }
++
++ /* Adjusting free size of next block only, if it's called from fsync ! */
++ if (pad == 2) {
++ D1(printk(KERN_DEBUG "jffs2_flush_wbuf() adjusting free_size of c->nextblock\n"));
++ spin_lock(&c->erase_completion_lock);
++ if (!c->nextblock)
++ BUG();
++ /* wbuf_pagesize - wbuf_len is the amount of space that's to be
++ padded. If there is less free space in the block than that,
++ something screwed up */
++ if (c->nextblock->free_size < (c->wbuf_pagesize - c->wbuf_len)) {
++ printk(KERN_CRIT "jffs2_flush_wbuf(): Accounting error. wbuf at 0x%08x has 0x%03x bytes, 0x%03x left.\n",
++ c->wbuf_ofs, c->wbuf_len, c->wbuf_pagesize-c->wbuf_len);
++ printk(KERN_CRIT "jffs2_flush_wbuf(): But free_size for block at 0x%08x is only 0x%08x\n",
++ c->nextblock->offset, c->nextblock->free_size);
++ BUG();
++ }
++ c->nextblock->free_size -= (c->wbuf_pagesize - c->wbuf_len);
++ c->free_size -= (c->wbuf_pagesize - c->wbuf_len);
++ c->nextblock->wasted_size += (c->wbuf_pagesize - c->wbuf_len);
++ c->wasted_size += (c->wbuf_pagesize - c->wbuf_len);
++ spin_unlock(&c->erase_completion_lock);
++ }
++
++ /* Stick any now-obsoleted blocks on the erase_pending_list */
++ spin_lock(&c->erase_completion_lock);
++ jffs2_refile_wbuf_blocks(c);
++ jffs2_clear_wbuf_ino_list(c);
++ spin_unlock(&c->erase_completion_lock);
++
++ memset(c->wbuf,0xff,c->wbuf_pagesize);
++ /* adjust write buffer offset, else we get a non contiguous write bug */
++ c->wbuf_ofs += c->wbuf_pagesize;
++ c->wbuf_len = 0;
++ return 0;
++}
++
++/* Trigger garbage collection to flush the write-buffer.
++ If ino arg is zero, do it if _any_ real (i.e. not GC) writes are
++ outstanding. If ino arg non-zero, do it only if a write for the
++ given inode is outstanding. */
++int jffs2_flush_wbuf_gc(struct jffs2_sb_info *c, uint32_t ino)
++{
++ uint32_t old_wbuf_ofs;
++ uint32_t old_wbuf_len;
++ int ret = 0;
++
++ D1(printk(KERN_DEBUG "jffs2_flush_wbuf_gc() called for ino #%u...\n", ino));
++
++ down(&c->alloc_sem);
++ if (!jffs2_wbuf_pending_for_ino(c, ino)) {
++ D1(printk(KERN_DEBUG "Ino #%d not pending in wbuf. Returning\n", ino));
++ up(&c->alloc_sem);
++ return 0;
++ }
++
++ old_wbuf_ofs = c->wbuf_ofs;
++ old_wbuf_len = c->wbuf_len;
++
++ if (c->unchecked_size) {
++ /* GC won't make any progress for a while */
++ D1(printk(KERN_DEBUG "jffs2_flush_wbuf_gc() padding. Not finished checking\n"));
++ ret = __jffs2_flush_wbuf(c, 2);
++ } else while (old_wbuf_len &&
++ old_wbuf_ofs == c->wbuf_ofs) {
++
++ up(&c->alloc_sem);
++
++ D1(printk(KERN_DEBUG "jffs2_flush_wbuf_gc() calls gc pass\n"));
++
++ ret = jffs2_garbage_collect_pass(c);
++ if (ret) {
++ /* GC failed. Flush it with padding instead */
++ down(&c->alloc_sem);
++ ret = __jffs2_flush_wbuf(c, 2);
++ break;
++ }
++ down(&c->alloc_sem);
++ }
++
++ D1(printk(KERN_DEBUG "jffs2_flush_wbuf_gc() ends...\n"));
++
++ up(&c->alloc_sem);
++ return ret;
++}
++
++/* Pad write-buffer to end and write it, wasting space. */
++int jffs2_flush_wbuf_pad(struct jffs2_sb_info *c)
++{
++ return __jffs2_flush_wbuf(c, 1);
++}
++
++
++#define PAGE_DIV(x) ( (x) & (~(c->wbuf_pagesize - 1)) )
++#define PAGE_MOD(x) ( (x) & (c->wbuf_pagesize - 1) )
++int jffs2_flash_writev(struct jffs2_sb_info *c, const struct iovec *invecs, unsigned long count, loff_t to, size_t *retlen, uint32_t ino)
++{
++ struct iovec outvecs[3];
++ uint32_t totlen = 0;
++ uint32_t split_ofs = 0;
++ uint32_t old_totlen;
++ int ret, splitvec = -1;
++ int invec, outvec;
++ size_t wbuf_retlen;
++ unsigned char *wbuf_ptr;
++ size_t donelen = 0;
++ uint32_t outvec_to = to;
++
++ /* If not NAND flash, don't bother */
++ if (!c->wbuf)
++ return jffs2_flash_direct_writev(c, invecs, count, to, retlen);
++
++ /* If wbuf_ofs is not initialized, set it to target address */
++ if (c->wbuf_ofs == 0xFFFFFFFF) {
++ c->wbuf_ofs = PAGE_DIV(to);
++ c->wbuf_len = PAGE_MOD(to);
++ memset(c->wbuf,0xff,c->wbuf_pagesize);
++ }
++
++ /* Sanity checks on target address.
++ It's permitted to write at PAD(c->wbuf_len+c->wbuf_ofs),
++ and it's permitted to write at the beginning of a new
++ erase block. Anything else, and you die.
++ New block starts at xxx000c (0-b = block header)
++ */
++ if ( (to & ~(c->sector_size-1)) != (c->wbuf_ofs & ~(c->sector_size-1)) ) {
++ /* It's a write to a new block */
++ if (c->wbuf_len) {
++ D1(printk(KERN_DEBUG "jffs2_flash_writev() to 0x%lx causes flush of wbuf at 0x%08x\n", (unsigned long)to, c->wbuf_ofs));
++ ret = jffs2_flush_wbuf_pad(c);
++ if (ret) {
++ /* the underlying layer has to check wbuf_len to do the cleanup */
++ D1(printk(KERN_WARNING "jffs2_flush_wbuf() called from jffs2_flash_writev() failed %d\n", ret));
++ *retlen = 0;
++ return ret;
++ }
++ }
++ /* set pointer to new block */
++ c->wbuf_ofs = PAGE_DIV(to);
++ c->wbuf_len = PAGE_MOD(to);
++ }
++
++ if (to != PAD(c->wbuf_ofs + c->wbuf_len)) {
++ /* We're not writing immediately after the writebuffer. Bad. */
++ printk(KERN_CRIT "jffs2_flash_writev(): Non-contiguous write to %08lx\n", (unsigned long)to);
++ if (c->wbuf_len)
++ printk(KERN_CRIT "wbuf was previously %08x-%08x\n",
++ c->wbuf_ofs, c->wbuf_ofs+c->wbuf_len);
++ BUG();
++ }
++
++ /* Note outvecs[3] above. We know count is never greater than 2 */
++ if (count > 2) {
++ printk(KERN_CRIT "jffs2_flash_writev(): count is %ld\n", count);
++ BUG();
++ }
++
++ invec = 0;
++ outvec = 0;
++
++
++ /* Fill writebuffer first, if already in use */
++ if (c->wbuf_len) {
++ uint32_t invec_ofs = 0;
++
++ /* adjust alignment offset */
++ if (c->wbuf_len != PAGE_MOD(to)) {
++ c->wbuf_len = PAGE_MOD(to);
++ /* take care of alignment to next page */
++ if (!c->wbuf_len)
++ c->wbuf_len = c->wbuf_pagesize;
++ }
++
++ while(c->wbuf_len < c->wbuf_pagesize) {
++ uint32_t thislen;
++
++ if (invec == count)
++ goto alldone;
++
++ thislen = c->wbuf_pagesize - c->wbuf_len;
++
++ if (thislen >= invecs[invec].iov_len)
++ thislen = invecs[invec].iov_len;
++
++ invec_ofs = thislen;
++
++ memcpy(c->wbuf + c->wbuf_len, invecs[invec].iov_base, thislen);
++ c->wbuf_len += thislen;
++ donelen += thislen;
++ /* Get next invec, if actual did not fill the buffer */
++ if (c->wbuf_len < c->wbuf_pagesize)
++ invec++;
++ }
++
++ /* write buffer is full, flush buffer */
++ ret = __jffs2_flush_wbuf(c, 0);
++ if (ret) {
++ /* the underlying layer has to check wbuf_len to do the cleanup */
++ D1(printk(KERN_WARNING "jffs2_flush_wbuf() called from jffs2_flash_writev() failed %d\n", ret));
++ /* Retlen zero to make sure our caller doesn't mark the space dirty.
++ We've already done everything that's necessary */
++ *retlen = 0;
++ return ret;
++ }
++ outvec_to += donelen;
++ c->wbuf_ofs = outvec_to;
++
++ /* All invecs done ? */
++ if (invec == count)
++ goto alldone;
++
++ /* Set up the first outvec, containing the remainder of the
++ invec we partially used */
++ if (invecs[invec].iov_len > invec_ofs) {
++ outvecs[0].iov_base = invecs[invec].iov_base+invec_ofs;
++ totlen = outvecs[0].iov_len = invecs[invec].iov_len-invec_ofs;
++ if (totlen > c->wbuf_pagesize) {
++ splitvec = outvec;
++ split_ofs = outvecs[0].iov_len - PAGE_MOD(totlen);
++ }
++ outvec++;
++ }
++ invec++;
++ }
++
++ /* OK, now we've flushed the wbuf and the start of the bits
++ we have been asked to write, now to write the rest.... */
++
++ /* totlen holds the amount of data still to be written */
++ old_totlen = totlen;
++ for ( ; invec < count; invec++,outvec++ ) {
++ outvecs[outvec].iov_base = invecs[invec].iov_base;
++ totlen += outvecs[outvec].iov_len = invecs[invec].iov_len;
++ if (PAGE_DIV(totlen) != PAGE_DIV(old_totlen)) {
++ splitvec = outvec;
++ split_ofs = outvecs[outvec].iov_len - PAGE_MOD(totlen);
++ old_totlen = totlen;
++ }
++ }
++
++ /* Now the outvecs array holds all the remaining data to write */
++ /* Up to splitvec,split_ofs is to be written immediately. The rest
++ goes into the (now-empty) wbuf */
++
++ if (splitvec != -1) {
++ uint32_t remainder;
++ int ret;
++
++ remainder = outvecs[splitvec].iov_len - split_ofs;
++ outvecs[splitvec].iov_len = split_ofs;
++
++ /* We did cross a page boundary, so we write some now */
++ ret = c->mtd->writev_ecc(c->mtd, outvecs, splitvec+1, outvec_to, &wbuf_retlen, NULL, c->oobinfo);
++ if (ret < 0 || wbuf_retlen != PAGE_DIV(totlen)) {
++ /* At this point we have no problem,
++ c->wbuf is empty.
++ */
++ *retlen = donelen;
++ return ret;
++ }
++
++ donelen += wbuf_retlen;
++ c->wbuf_ofs = PAGE_DIV(outvec_to) + PAGE_DIV(totlen);
++
++ if (remainder) {
++ outvecs[splitvec].iov_base += split_ofs;
++ outvecs[splitvec].iov_len = remainder;
++ } else {
++ splitvec++;
++ }
++
++ } else {
++ splitvec = 0;
++ }
++
++ /* Now splitvec points to the start of the bits we have to copy
++ into the wbuf */
++ wbuf_ptr = c->wbuf;
++
++ for ( ; splitvec < outvec; splitvec++) {
++ /* Don't copy the wbuf into itself */
++ if (outvecs[splitvec].iov_base == c->wbuf)
++ continue;
++ memcpy(wbuf_ptr, outvecs[splitvec].iov_base, outvecs[splitvec].iov_len);
++ wbuf_ptr += outvecs[splitvec].iov_len;
++ donelen += outvecs[splitvec].iov_len;
++ }
++ c->wbuf_len = wbuf_ptr - c->wbuf;
++
++ /* If there's a remainder in the wbuf and it's a non-GC write,
++ remember that the wbuf affects this ino */
++alldone:
++ *retlen = donelen;
++
++ if (c->wbuf_len && ino)
++ jffs2_wbuf_dirties_inode(c, ino);
++
++ return 0;
++}
++
++/*
++ * This is the entry for flash write.
++ * Check, if we work on NAND FLASH, if so build an iovec and write it via vritev
++*/
++int jffs2_flash_write(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *retlen, const u_char *buf)
++{
++ struct iovec vecs[1];
++
++ if (jffs2_can_mark_obsolete(c))
++ return c->mtd->write(c->mtd, ofs, len, retlen, buf);
++
++ vecs[0].iov_base = (unsigned char *) buf;
++ vecs[0].iov_len = len;
++ return jffs2_flash_writev(c, vecs, 1, ofs, retlen, 0);
++}
++
++/*
++ Handle readback from writebuffer and ECC failure return
++*/
++int jffs2_flash_read(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *retlen, u_char *buf)
++{
++ loff_t orbf = 0, owbf = 0, lwbf = 0;
++ int ret;
++
++ /* Read flash */
++ if (!jffs2_can_mark_obsolete(c)) {
++ ret = c->mtd->read_ecc(c->mtd, ofs, len, retlen, buf, NULL, c->oobinfo);
++
++ if ( (ret == -EIO) && (*retlen == len) ) {
++ printk(KERN_WARNING "mtd->read(0x%zx bytes from 0x%llx) returned ECC error\n",
++ len, ofs);
++ /*
++ * We have the raw data without ECC correction in the buffer, maybe
++ * we are lucky and all data or parts are correct. We check the node.
++ * If data are corrupted node check will sort it out.
++ * We keep this block, it will fail on write or erase and the we
++ * mark it bad. Or should we do that now? But we should give him a chance.
++ * Maybe we had a system crash or power loss before the ecc write or
++ * a erase was completed.
++ * So we return success. :)
++ */
++ ret = 0;
++ }
++ } else
++ return c->mtd->read(c->mtd, ofs, len, retlen, buf);
++
++ /* if no writebuffer available or write buffer empty, return */
++ if (!c->wbuf_pagesize || !c->wbuf_len)
++ return ret;
++
++ /* if we read in a different block, return */
++ if ( (ofs & ~(c->sector_size-1)) != (c->wbuf_ofs & ~(c->sector_size-1)) )
++ return ret;
++
++ if (ofs >= c->wbuf_ofs) {
++ owbf = (ofs - c->wbuf_ofs); /* offset in write buffer */
++ if (owbf > c->wbuf_len) /* is read beyond write buffer ? */
++ return ret;
++ lwbf = c->wbuf_len - owbf; /* number of bytes to copy */
++ if (lwbf > len)
++ lwbf = len;
++ } else {
++ orbf = (c->wbuf_ofs - ofs); /* offset in read buffer */
++ if (orbf > len) /* is write beyond write buffer ? */
++ return ret;
++ lwbf = len - orbf; /* number of bytes to copy */
++ if (lwbf > c->wbuf_len)
++ lwbf = c->wbuf_len;
++ }
++ if (lwbf > 0)
++ memcpy(buf+orbf,c->wbuf+owbf,lwbf);
++
++ return ret;
++}
++
++/*
++ * Check, if the out of band area is empty
++ */
++int jffs2_check_oob_empty( struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, int mode)
++{
++ unsigned char *buf;
++ int ret = 0;
++ int i,len,page;
++ size_t retlen;
++ int oob_size;
++
++ oob_size = c->mtd->oobsize;
++
++ /* allocate a buffer for all oob data in this sector */
++ len = 4 * oob_size;
++ buf = kmalloc(len, GFP_KERNEL);
++ if (!buf) {
++ printk(KERN_NOTICE "jffs2_check_oob_empty(): allocation of temporary data buffer for oob check failed\n");
++ return -ENOMEM;
++ }
++ /*
++ * if mode = 0, we scan for a total empty oob area, else we have
++ * to take care of the cleanmarker in the first page of the block
++ */
++ ret = jffs2_flash_read_oob(c, jeb->offset, len , &retlen, buf);
++ if (ret) {
++ D1(printk(KERN_WARNING "jffs2_check_oob_empty(): Read OOB failed %d for block at %08x\n", ret, jeb->offset));
++ goto out;
++ }
++
++ if (retlen < len) {
++ D1(printk(KERN_WARNING "jffs2_check_oob_empty(): Read OOB return short read "
++ "(%zd bytes not %d) for block at %08x\n", retlen, len, jeb->offset));
++ ret = -EIO;
++ goto out;
++ }
++
++ /* Special check for first two pages */
++ for (page = 0; page < 2 * oob_size; page += oob_size) {
++ /* Check for bad block marker */
++ if (buf[page+c->badblock_pos] != 0xff) {
++ D1(printk(KERN_WARNING "jffs2_check_oob_empty(): Bad or failed block at %08x\n",jeb->offset));
++ /* Return 2 for bad and 3 for failed block
++ bad goes to list_bad and failed to list_erase */
++ ret = (!page) ? 2 : 3;
++ goto out;
++ }
++ for(i = 0; i < oob_size ; i++) {
++ /* Yeah, we know about the cleanmarker. */
++ if (mode && i >= c->fsdata_pos &&
++ i < c->fsdata_pos+c->fsdata_len)
++ continue;
++
++ if (buf[page+i] != 0xFF) {
++ D2(printk(KERN_DEBUG "Found %02x at %x in OOB for %08x\n",
++ buf[page+i], page+i, jeb->offset));
++ ret = 1;
++ goto out;
++ }
++ }
++ /* only the first page can contain a cleanmarker !*/
++ mode = 0;
++ }
++
++ /* we know, we are aligned :) */
++ for (; page < len; page += sizeof(long)) {
++ unsigned long dat = *(unsigned long *)(&buf[page]);
++ if(dat != -1) {
++ ret = 1;
++ goto out;
++ }
++ }
++
++out:
++ kfree(buf);
++
++ return ret;
++}
++
++/*
++* Scan for a valid cleanmarker and for bad blocks
++* For virtual blocks (concatenated physical blocks) check the cleanmarker
++* only in the first page of the first physical block, but scan for bad blocks in all
++* physical blocks
++*/
++int jffs2_check_nand_cleanmarker (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
++{
++ struct jffs2_unknown_node n;
++ unsigned char buf[32];
++ unsigned char *p;
++ int ret, i, cnt, retval = 0;
++ size_t retlen, offset;
++ int oob_size;
++
++ offset = jeb->offset;
++ oob_size = c->mtd->oobsize;
++
++ /* Loop through the physical blocks */
++ for (cnt = 0; cnt < (c->sector_size / c->mtd->erasesize); cnt++) {
++ /*
++ * We read oob data from page 0 and 1 of the block.
++ * page 0 contains cleanmarker and badblock info
++ * page 1 contains failure count of this block
++ */
++ ret = c->mtd->read_oob (c->mtd, offset, oob_size << 1, &retlen, buf);
++
++ if (ret) {
++ D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): Read OOB failed %d for block at %08x\n", ret, jeb->offset));
++ return ret;
++ }
++ if (retlen < (oob_size << 1)) {
++ D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): Read OOB return short read (%zd bytes not %d) for block at %08x\n", retlen, oob_size << 1, jeb->offset));
++ return -EIO;
++ }
++
++ /* Check for bad block marker */
++ if (buf[c->badblock_pos] != 0xff) {
++ D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): Bad block at %08x (has %02x %02x in badblock_pos %d\n",
++ jeb->offset, buf[c->badblock_pos], buf[c->badblock_pos + oob_size], c->badblock_pos));
++ return 2;
++ }
++
++ /* Check for failure counter in the second page */
++ if (buf[c->badblock_pos + oob_size] != 0xff) {
++ D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): Block marked as failed at %08x, fail count:%d\n", jeb->offset, buf[c->badblock_pos + oob_size]));
++ return 3;
++ }
++
++ /* Check cleanmarker only on the first physical block */
++ if (!cnt) {
++ n.magic = cpu_to_je16 (JFFS2_MAGIC_BITMASK);
++ n.nodetype = cpu_to_je16 (JFFS2_NODETYPE_CLEANMARKER);
++ n.totlen = cpu_to_je32 (8);
++ p = (unsigned char *) &n;
++
++ for (i = 0; i < c->fsdata_len; i++) {
++ if (buf[c->fsdata_pos + i] != p[i]) {
++ retval = 1;
++ }
++ }
++ D1(if (retval == 1) {
++ printk(KERN_WARNING "jffs2_check_nand_cleanmarker(): Cleanmarker node not detected in block at %08x\n", jeb->offset);
++ printk(KERN_WARNING "OOB at %08x was ", offset);
++ for (i=0; i < oob_size; i++) {
++ printk("%02x ", buf[i]);
++ }
++ printk("\n");
++ })
++ }
++ offset += c->mtd->erasesize;
++ }
++ return retval;
++}
++
++int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
++{
++ struct jffs2_unknown_node n;
++ int ret;
++ size_t retlen;
++
++ n.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
++ n.nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER);
++ n.totlen = cpu_to_je32(8);
++
++ ret = jffs2_flash_write_oob(c, jeb->offset + c->fsdata_pos, c->fsdata_len, &retlen, (unsigned char *)&n);
++
++ if (ret) {
++ D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): Write failed for block at %08x: error %d\n", jeb->offset, ret));
++ return ret;
++ }
++ if (retlen != c->fsdata_len) {
++ D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): Short write for block at %08x: %zd not %d\n", jeb->offset, retlen, c->fsdata_len));
++ return ret;
++ }
++ return 0;
++}
++
++/*
++ * We try to get the failure count of this block.
++ */
++int jffs2_nand_read_failcnt(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) {
++
++ unsigned char buf[16];
++ int ret;
++ size_t retlen;
++ int oob_size;
++
++ oob_size = c->mtd->oobsize;
++
++ ret = c->mtd->read_oob(c->mtd, jeb->offset + c->mtd->oobblock, oob_size , &retlen, buf);
++
++ if (ret) {
++ D1(printk(KERN_WARNING "jffs2_nand_read_failcnt(): Read OOB failed %d for block at %08x\n", ret, jeb->offset));
++ return ret;
++ }
++
++ if (retlen < oob_size) {
++ D1(printk(KERN_WARNING "jffs2_nand_read_failcnt(): Read OOB return short read (%zd bytes not %d) for block at %08x\n", retlen, oob_size, jeb->offset));
++ return -EIO;
++ }
++
++ jeb->bad_count = buf[c->badblock_pos];
++ return 0;
++}
++
++/*
++ * On NAND we try to mark this block bad. We try to write how often
++ * the block was erased and mark it finaly bad, if the count
++ * is > MAX_ERASE_FAILURES. We read this information on mount !
++ * jeb->bad_count contains the count before this erase.
++ * Don't care about failures. This block remains on the erase-pending
++ * or badblock list as long as nobody manipulates the flash with
++ * a bootloader or something like that.
++ */
++
++int jffs2_write_nand_badblock(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
++{
++ unsigned char buf = 0x0;
++ int ret;
++ size_t retlen;
++
++ /* if the count is < max, we try to write the counter to the 2nd page oob area */
++ if( ++jeb->bad_count < MAX_ERASE_FAILURES) {
++ buf = (unsigned char)jeb->bad_count;
++ c->badblock_pos += c->mtd->oobblock;
++ }
++
++ ret = jffs2_flash_write_oob(c, jeb->offset + c->badblock_pos, 1, &retlen, &buf);
++
++ if (ret) {
++ D1(printk(KERN_WARNING "jffs2_write_nand_badblock(): Write failed for block at %08x: error %d\n", jeb->offset, ret));
++ return ret;
++ }
++ if (retlen != 1) {
++ D1(printk(KERN_WARNING "jffs2_write_nand_badblock(): Short write for block at %08x: %zd not 1\n", jeb->offset, retlen));
++ return ret;
++ }
++ return 0;
++}
++
++#define JFFS2_OOB_ECCPOS0 0
++#define JFFS2_OOB_ECCPOS1 1
++#define JFFS2_OOB_ECCPOS2 2
++#define JFFS2_OOB_ECCPOS3 3
++#define JFFS2_OOB_ECCPOS4 6
++#define JFFS2_OOB_ECCPOS5 7
++
++#define NAND_JFFS2_OOB8_FSDAPOS 6
++#define NAND_JFFS2_OOB16_FSDAPOS 8
++#define NAND_JFFS2_OOB8_FSDALEN 2
++#define NAND_JFFS2_OOB16_FSDALEN 8
++
++static struct nand_oobinfo jffs2_oobinfo_swecc = {
++ .useecc = 1,
++ .eccpos = {JFFS2_OOB_ECCPOS0, JFFS2_OOB_ECCPOS1, JFFS2_OOB_ECCPOS2,
++ JFFS2_OOB_ECCPOS3, JFFS2_OOB_ECCPOS4, JFFS2_OOB_ECCPOS5}
++};
++
++static struct nand_oobinfo jffs2_oobinfo_docecc = {
++ .useecc = 1,
++ .eccpos = {0,1,2,3,4,5}
++};
++
++
++
++int jffs2_nand_flash_setup(struct jffs2_sb_info *c)
++{
++ /* Cleanmarker is out-of-band, so inline size zero */
++ c->cleanmarker_size = 0;
++
++ /* Initialise write buffer */
++ c->wbuf_pagesize = c->mtd->oobblock;
++ c->wbuf_ofs = 0xFFFFFFFF;
++
++ /* FIXME: If we had a generic way of describing the hardware's
++ use of OOB area, we could perhaps make this generic too. */
++ switch(c->mtd->ecctype) {
++ case MTD_ECC_SW:
++ D1(printk(KERN_DEBUG "JFFS2 using software ECC\n"));
++ c->oobinfo = &jffs2_oobinfo_swecc;
++ if (c->mtd->oobsize == 8) {
++ c->fsdata_pos = NAND_JFFS2_OOB8_FSDAPOS;
++ c->fsdata_len = NAND_JFFS2_OOB8_FSDALEN;
++ } else {
++ c->fsdata_pos = NAND_JFFS2_OOB16_FSDAPOS;
++ c->fsdata_len = NAND_JFFS2_OOB16_FSDALEN;
++ }
++ c->badblock_pos = NAND_BADBLOCK_POS;
++ break;
++
++ case MTD_ECC_RS_DiskOnChip:
++ D1(printk(KERN_DEBUG "JFFS2 using DiskOnChip hardware ECC\n"));
++ c->oobinfo = &jffs2_oobinfo_docecc;
++ c->fsdata_pos = 6;
++ c->fsdata_len = NAND_JFFS2_OOB16_FSDALEN;
++ c->badblock_pos = 15;
++ break;
++
++ default:
++ printk("JFFS2 doesn't yet know how to handle ECC type %d\n",
++ c->mtd->ecctype);
++ return -EINVAL;
++ }
++
++ c->wbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL);
++ if (!c->wbuf)
++ return -ENOMEM;
++
++#ifdef BREAKME
++ if (!brokenbuf)
++ brokenbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL);
++ if (!brokenbuf) {
++ kfree(c->wbuf);
++ return -ENOMEM;
++ }
++ memset(brokenbuf, 0xdb, c->wbuf_pagesize);
++#endif
++ return 0;
++}
++
++void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c)
++{
++ kfree(c->wbuf);
++}
+diff -Nurb linux-mips-2.4.27/fs/jffs2/write.c linux/fs/jffs2/write.c
+--- linux-mips-2.4.27/fs/jffs2/write.c 2003-11-17 02:07:44.000000000 +0100
++++ linux/fs/jffs2/write.c 2004-11-19 10:25:12.132164376 +0100
+@@ -1,154 +1,70 @@
+ /*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+- * Copyright (C) 2001 Red Hat, Inc.
++ * Copyright (C) 2001-2003 Red Hat, Inc.
+ *
+- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
++ * Created by David Woodhouse <dwmw2@redhat.com>
+ *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
++ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+- * The contents of this file are subject to the Red Hat eCos Public
+- * License Version 1.1 (the "Licence"); you may not use this file
+- * except in compliance with the Licence. You may obtain a copy of
+- * the Licence at http://www.redhat.com/
+- *
+- * Software distributed under the Licence is distributed on an "AS IS"
+- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+- * See the Licence for the specific language governing rights and
+- * limitations under the Licence.
+- *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
+- *
+- * Alternatively, the contents of this file may be used under the
+- * terms of the GNU General Public License version 2 (the "GPL"), in
+- * which case the provisions of the GPL are applicable instead of the
+- * above. If you wish to allow the use of your version of this file
+- * only under the terms of the GPL and not to allow others to use your
+- * version of this file under the RHEPL, indicate your decision by
+- * deleting the provisions above and replace them with the notice and
+- * other provisions required by the GPL. If you do not delete the
+- * provisions above, a recipient may use your version of this file
+- * under either the RHEPL or the GPL.
+- *
+- * $Id$
++ * $Id$
+ *
+ */
+
+ #include <linux/kernel.h>
+ #include <linux/fs.h>
+-#include <linux/jffs2.h>
++#include <linux/crc32.h>
++#include <linux/slab.h>
++#include <linux/pagemap.h>
+ #include <linux/mtd/mtd.h>
+ #include "nodelist.h"
+-#include <linux/crc32.h>
+
+-/* jffs2_new_inode: allocate a new inode and inocache, add it to the hash,
+- fill in the raw_inode while you're at it. */
+-struct inode *jffs2_new_inode (struct inode *dir_i, int mode, struct jffs2_raw_inode *ri)
++
++int jffs2_do_new_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, uint32_t mode, struct jffs2_raw_inode *ri)
+ {
+- struct inode *inode;
+- struct super_block *sb = dir_i->i_sb;
+ struct jffs2_inode_cache *ic;
+- struct jffs2_sb_info *c;
+- struct jffs2_inode_info *f;
+-
+- D1(printk(KERN_DEBUG "jffs2_new_inode(): dir_i %ld, mode 0x%x\n", dir_i->i_ino, mode));
+-
+- c = JFFS2_SB_INFO(sb);
+- memset(ri, 0, sizeof(*ri));
+
+ ic = jffs2_alloc_inode_cache();
+ if (!ic) {
+- return ERR_PTR(-ENOMEM);
++ return -ENOMEM;
+ }
+- memset(ic, 0, sizeof(*ic));
+-
+- inode = new_inode(sb);
+
+- if (!inode) {
+- jffs2_free_inode_cache(ic);
+- return ERR_PTR(-ENOMEM);
+- }
+-
+- /* Alloc jffs2_inode_info when that's split in 2.5 */
++ memset(ic, 0, sizeof(*ic));
+
+- f = JFFS2_INODE_INFO(inode);
+- memset(f, 0, sizeof(*f));
+ init_MUTEX_LOCKED(&f->sem);
+ f->inocache = ic;
+- inode->i_nlink = f->inocache->nlink = 1;
++ f->inocache->nlink = 1;
+ f->inocache->nodes = (struct jffs2_raw_node_ref *)f->inocache;
+- f->inocache->ino = ri->ino = inode->i_ino = ++c->highest_ino;
+- D1(printk(KERN_DEBUG "jffs2_new_inode(): Assigned ino# %d\n", ri->ino));
+- jffs2_add_ino_cache(c, f->inocache);
++ f->inocache->ino = ++c->highest_ino;
++ f->inocache->state = INO_STATE_PRESENT;
+
+- ri->magic = JFFS2_MAGIC_BITMASK;
+- ri->nodetype = JFFS2_NODETYPE_INODE;
+- ri->totlen = PAD(sizeof(*ri));
+- ri->hdr_crc = crc32(0, ri, sizeof(struct jffs2_unknown_node)-4);
+- ri->mode = mode;
+- f->highest_version = ri->version = 1;
+- ri->uid = current->fsuid;
+- if (dir_i->i_mode & S_ISGID) {
+- ri->gid = dir_i->i_gid;
+- if (S_ISDIR(mode))
+- ri->mode |= S_ISGID;
+- } else {
+- ri->gid = current->fsgid;
+- }
+- inode->i_mode = ri->mode;
+- inode->i_gid = ri->gid;
+- inode->i_uid = ri->uid;
+- inode->i_atime = inode->i_ctime = inode->i_mtime =
+- ri->atime = ri->mtime = ri->ctime = CURRENT_TIME;
+- inode->i_blksize = PAGE_SIZE;
+- inode->i_blocks = 0;
+- inode->i_size = 0;
++ ri->ino = cpu_to_je32(f->inocache->ino);
+
+- insert_inode_hash(inode);
++ D1(printk(KERN_DEBUG "jffs2_do_new_inode(): Assigned ino# %d\n", f->inocache->ino));
++ jffs2_add_ino_cache(c, f->inocache);
+
+- return inode;
+-}
++ ri->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
++ ri->nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE);
++ ri->totlen = cpu_to_je32(PAD(sizeof(*ri)));
++ ri->hdr_crc = cpu_to_je32(crc32(0, ri, sizeof(struct jffs2_unknown_node)-4));
++ ri->mode = cpu_to_jemode(mode);
+
+-/* This ought to be in core MTD code. All registered MTD devices
+- without writev should have this put in place. Bug the MTD
+- maintainer */
+-static int mtd_fake_writev(struct mtd_info *mtd, const struct iovec *vecs, unsigned long count, loff_t to, size_t *retlen)
+-{
+- unsigned long i;
+- size_t totlen = 0, thislen;
+- int ret = 0;
++ f->highest_version = 1;
++ ri->version = cpu_to_je32(f->highest_version);
+
+- for (i=0; i<count; i++) {
+- ret = mtd->write(mtd, to, vecs[i].iov_len, &thislen, vecs[i].iov_base);
+- totlen += thislen;
+- if (ret || thislen != vecs[i].iov_len)
+- break;
+- to += vecs[i].iov_len;
+- }
+- if (retlen)
+- *retlen = totlen;
+- return ret;
+-}
+-
+-
+-static inline int mtd_writev(struct mtd_info *mtd, const struct iovec *vecs, unsigned long count, loff_t to, size_t *retlen)
+-{
+- if (mtd->writev)
+- return mtd->writev(mtd,vecs,count,to,retlen);
+- else
+- return mtd_fake_writev(mtd, vecs, count, to, retlen);
++ return 0;
+ }
+
+-static void writecheck(struct mtd_info *mtd, __u32 ofs)
++#if CONFIG_JFFS2_FS_DEBUG > 0
++static void writecheck(struct jffs2_sb_info *c, uint32_t ofs)
+ {
+ unsigned char buf[16];
+- ssize_t retlen;
++ size_t retlen;
+ int ret, i;
+
+- ret = mtd->read(mtd, ofs, 16, &retlen, buf);
+- if (ret && retlen != 16) {
+- D1(printk(KERN_DEBUG "read failed or short in writecheck(). ret %d, retlen %d\n", ret, retlen));
++ ret = jffs2_flash_read(c, ofs, 16, &retlen, buf);
++ if (ret || (retlen != 16)) {
++ D1(printk(KERN_DEBUG "read failed or short in writecheck(). ret %d, retlen %zd\n", ret, retlen));
+ return;
+ }
+ ret = 0;
+@@ -157,32 +73,31 @@
+ ret = 1;
+ }
+ if (ret) {
+- printk(KERN_WARNING "ARGH. About to write node to 0x%08x on flash, but there's data already there:\n", ofs);
++ printk(KERN_WARNING "ARGH. About to write node to 0x%08x on flash, but there are data already there:\n", ofs);
+ printk(KERN_WARNING "0x%08x: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
+ ofs,
+ buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7],
+ buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]);
+ }
+ }
+-
+-
++#endif
+
+
+ /* jffs2_write_dnode - given a raw_inode, allocate a full_dnode for it,
+ write it to the flash, link it into the existing inode/fragment list */
+
+-struct jffs2_full_dnode *jffs2_write_dnode(struct inode *inode, struct jffs2_raw_inode *ri, const unsigned char *data, __u32 datalen, __u32 flash_ofs, __u32 *writelen)
++struct jffs2_full_dnode *jffs2_write_dnode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_inode *ri, const unsigned char *data, uint32_t datalen, uint32_t flash_ofs, int alloc_mode)
+
+ {
+- struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
+- struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
+ struct jffs2_raw_node_ref *raw;
+ struct jffs2_full_dnode *fn;
+- ssize_t retlen;
++ size_t retlen;
+ struct iovec vecs[2];
+ int ret;
++ int retried = 0;
++ unsigned long cnt = 2;
+
+- D1(if(ri->hdr_crc != crc32(0, ri, sizeof(struct jffs2_unknown_node)-4)) {
++ D1(if(je32_to_cpu(ri->hdr_crc) != crc32(0, ri, sizeof(struct jffs2_unknown_node)-4)) {
+ printk(KERN_CRIT "Eep. CRC not correct in jffs2_write_dnode()\n");
+ BUG();
+ }
+@@ -192,10 +107,10 @@
+ vecs[1].iov_base = (unsigned char *)data;
+ vecs[1].iov_len = datalen;
+
+- writecheck(c->mtd, flash_ofs);
++ D1(writecheck(c, flash_ofs));
+
+- if (ri->totlen != sizeof(*ri) + datalen) {
+- printk(KERN_WARNING "jffs2_write_dnode: ri->totlen (0x%08x) != sizeof(*ri) (0x%08x) + datalen (0x%08x)\n", ri->totlen, sizeof(*ri), datalen);
++ if (je32_to_cpu(ri->totlen) != sizeof(*ri) + datalen) {
++ printk(KERN_WARNING "jffs2_write_dnode: ri->totlen (0x%08x) != sizeof(*ri) (0x%08zx) + datalen (0x%08x)\n", je32_to_cpu(ri->totlen), sizeof(*ri), datalen);
+ }
+ raw = jffs2_alloc_raw_node_ref();
+ if (!raw)
+@@ -206,19 +121,28 @@
+ jffs2_free_raw_node_ref(raw);
+ return ERR_PTR(-ENOMEM);
+ }
+- raw->flash_offset = flash_ofs;
+- raw->totlen = PAD(ri->totlen);
+- raw->next_phys = NULL;
+
+- fn->ofs = ri->offset;
+- fn->size = ri->dsize;
++ fn->ofs = je32_to_cpu(ri->offset);
++ fn->size = je32_to_cpu(ri->dsize);
+ fn->frags = 0;
++
++ /* check number of valid vecs */
++ if (!datalen || !data)
++ cnt = 1;
++ retry:
+ fn->raw = raw;
+
+- ret = mtd_writev(c->mtd, vecs, 2, flash_ofs, &retlen);
++ raw->flash_offset = flash_ofs;
++ raw->__totlen = PAD(sizeof(*ri)+datalen);
++ raw->next_phys = NULL;
++
++ ret = jffs2_flash_writev(c, vecs, cnt, flash_ofs, &retlen,
++ (alloc_mode==ALLOC_GC)?0:f->inocache->ino);
++
+ if (ret || (retlen != sizeof(*ri) + datalen)) {
+- printk(KERN_NOTICE "Write of %d bytes at 0x%08x failed. returned %d, retlen %d\n",
++ printk(KERN_NOTICE "Write of %zd bytes at 0x%08x failed. returned %d, retlen %zd\n",
+ sizeof(*ri)+datalen, flash_ofs, ret, retlen);
++
+ /* Mark the space as dirtied */
+ if (retlen) {
+ /* Doesn't belong to any inode */
+@@ -229,48 +153,96 @@
+ seem corrupted, in which case the scan would skip over
+ any node we write before the original intended end of
+ this node */
+- jffs2_add_physical_node_ref(c, raw, sizeof(*ri)+datalen, 1);
++ raw->flash_offset |= REF_OBSOLETE;
++ jffs2_add_physical_node_ref(c, raw);
+ jffs2_mark_node_obsolete(c, raw);
+ } else {
+ printk(KERN_NOTICE "Not marking the space at 0x%08x as dirty because the flash driver returned retlen zero\n", raw->flash_offset);
+ jffs2_free_raw_node_ref(raw);
+ }
++ if (!retried && alloc_mode != ALLOC_NORETRY && (raw = jffs2_alloc_raw_node_ref())) {
++ /* Try to reallocate space and retry */
++ uint32_t dummy;
++ struct jffs2_eraseblock *jeb = &c->blocks[flash_ofs / c->sector_size];
++
++ retried = 1;
++
++ D1(printk(KERN_DEBUG "Retrying failed write.\n"));
++
++ ACCT_SANITY_CHECK(c,jeb);
++ D1(ACCT_PARANOIA_CHECK(jeb));
++
++ if (alloc_mode == ALLOC_GC) {
++ ret = jffs2_reserve_space_gc(c, sizeof(*ri) + datalen, &flash_ofs, &dummy);
++ } else {
++ /* Locking pain */
++ up(&f->sem);
++ jffs2_complete_reservation(c);
++
++ ret = jffs2_reserve_space(c, sizeof(*ri) + datalen, &flash_ofs, &dummy, alloc_mode);
++ down(&f->sem);
++ }
++
++ if (!ret) {
++ D1(printk(KERN_DEBUG "Allocated space at 0x%08x to retry failed write.\n", flash_ofs));
++
++ ACCT_SANITY_CHECK(c,jeb);
++ D1(ACCT_PARANOIA_CHECK(jeb));
+
++ goto retry;
++ }
++ D1(printk(KERN_DEBUG "Failed to allocate space to retry failed write: %d!\n", ret));
++ jffs2_free_raw_node_ref(raw);
++ }
+ /* Release the full_dnode which is now useless, and return */
+ jffs2_free_full_dnode(fn);
+- if (writelen)
+- *writelen = retlen;
+ return ERR_PTR(ret?ret:-EIO);
+ }
+ /* Mark the space used */
+- jffs2_add_physical_node_ref(c, raw, retlen, 0);
++ /* If node covers at least a whole page, or if it starts at the
++ beginning of a page and runs to the end of the file, or if
++ it's a hole node, mark it REF_PRISTINE, else REF_NORMAL.
++ */
++ if ((je32_to_cpu(ri->dsize) >= PAGE_CACHE_SIZE) ||
++ ( ((je32_to_cpu(ri->offset)&(PAGE_CACHE_SIZE-1))==0) &&
++ (je32_to_cpu(ri->dsize)+je32_to_cpu(ri->offset) == je32_to_cpu(ri->isize)))) {
++ raw->flash_offset |= REF_PRISTINE;
++ } else {
++ raw->flash_offset |= REF_NORMAL;
++ }
++ jffs2_add_physical_node_ref(c, raw);
+
+ /* Link into per-inode list */
+ raw->next_in_ino = f->inocache->nodes;
+ f->inocache->nodes = raw;
+
+- D1(printk(KERN_DEBUG "jffs2_write_dnode wrote node at 0x%08x with dsize 0x%x, csize 0x%x, node_crc 0x%08x, data_crc 0x%08x, totlen 0x%08x\n", flash_ofs, ri->dsize, ri->csize, ri->node_crc, ri->data_crc, ri->totlen));
+- if (writelen)
+- *writelen = retlen;
++ D1(printk(KERN_DEBUG "jffs2_write_dnode wrote node at 0x%08x(%d) with dsize 0x%x, csize 0x%x, node_crc 0x%08x, data_crc 0x%08x, totlen 0x%08x\n",
++ flash_ofs, ref_flags(raw), je32_to_cpu(ri->dsize),
++ je32_to_cpu(ri->csize), je32_to_cpu(ri->node_crc),
++ je32_to_cpu(ri->data_crc), je32_to_cpu(ri->totlen)));
++
++ if (retried) {
++ ACCT_SANITY_CHECK(c,NULL);
++ }
+
+- f->inocache->nodes = raw;
+ return fn;
+ }
+
+-struct jffs2_full_dirent *jffs2_write_dirent(struct inode *inode, struct jffs2_raw_dirent *rd, const unsigned char *name, __u32 namelen, __u32 flash_ofs, __u32 *writelen)
++struct jffs2_full_dirent *jffs2_write_dirent(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_dirent *rd, const unsigned char *name, uint32_t namelen, uint32_t flash_ofs, int alloc_mode)
+ {
+- struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
+- struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
+ struct jffs2_raw_node_ref *raw;
+ struct jffs2_full_dirent *fd;
+- ssize_t retlen;
++ size_t retlen;
+ struct iovec vecs[2];
++ int retried = 0;
+ int ret;
+
+- D1(printk(KERN_DEBUG "jffs2_write_dirent(ino #%u, name at *0x%p \"%s\"->ino #%u, name_crc 0x%08x)\n", rd->pino, name, name, rd->ino, rd->name_crc));
+- writecheck(c->mtd, flash_ofs);
++ D1(printk(KERN_DEBUG "jffs2_write_dirent(ino #%u, name at *0x%p \"%s\"->ino #%u, name_crc 0x%08x)\n",
++ je32_to_cpu(rd->pino), name, name, je32_to_cpu(rd->ino),
++ je32_to_cpu(rd->name_crc)));
++ D1(writecheck(c, flash_ofs));
+
+- D1(if(rd->hdr_crc != crc32(0, rd, sizeof(struct jffs2_unknown_node)-4)) {
++ D1(if(je32_to_cpu(rd->hdr_crc) != crc32(0, rd, sizeof(struct jffs2_unknown_node)-4)) {
+ printk(KERN_CRIT "Eep. CRC not correct in jffs2_write_dirent()\n");
+ BUG();
+ }
+@@ -291,44 +263,414 @@
+ jffs2_free_raw_node_ref(raw);
+ return ERR_PTR(-ENOMEM);
+ }
+- raw->flash_offset = flash_ofs;
+- raw->totlen = PAD(rd->totlen);
+- raw->next_in_ino = f->inocache->nodes;
+- f->inocache->nodes = raw;
+- raw->next_phys = NULL;
+
+- fd->version = rd->version;
+- fd->ino = rd->ino;
++ fd->version = je32_to_cpu(rd->version);
++ fd->ino = je32_to_cpu(rd->ino);
+ fd->nhash = full_name_hash(name, strlen(name));
+ fd->type = rd->type;
+ memcpy(fd->name, name, namelen);
+ fd->name[namelen]=0;
++
++ retry:
+ fd->raw = raw;
+
+- ret = mtd_writev(c->mtd, vecs, 2, flash_ofs, &retlen);
++ raw->flash_offset = flash_ofs;
++ raw->__totlen = PAD(sizeof(*rd)+namelen);
++ raw->next_phys = NULL;
++
++ ret = jffs2_flash_writev(c, vecs, 2, flash_ofs, &retlen,
++ (alloc_mode==ALLOC_GC)?0:je32_to_cpu(rd->pino));
+ if (ret || (retlen != sizeof(*rd) + namelen)) {
+- printk(KERN_NOTICE "Write of %d bytes at 0x%08x failed. returned %d, retlen %d\n",
++ printk(KERN_NOTICE "Write of %zd bytes at 0x%08x failed. returned %d, retlen %zd\n",
+ sizeof(*rd)+namelen, flash_ofs, ret, retlen);
+ /* Mark the space as dirtied */
+ if (retlen) {
+- jffs2_add_physical_node_ref(c, raw, sizeof(*rd)+namelen, 1);
++ raw->next_in_ino = NULL;
++ raw->flash_offset |= REF_OBSOLETE;
++ jffs2_add_physical_node_ref(c, raw);
+ jffs2_mark_node_obsolete(c, raw);
+ } else {
+ printk(KERN_NOTICE "Not marking the space at 0x%08x as dirty because the flash driver returned retlen zero\n", raw->flash_offset);
+ jffs2_free_raw_node_ref(raw);
+ }
++ if (!retried && (raw = jffs2_alloc_raw_node_ref())) {
++ /* Try to reallocate space and retry */
++ uint32_t dummy;
++ struct jffs2_eraseblock *jeb = &c->blocks[flash_ofs / c->sector_size];
++
++ retried = 1;
++
++ D1(printk(KERN_DEBUG "Retrying failed write.\n"));
++
++ ACCT_SANITY_CHECK(c,jeb);
++ D1(ACCT_PARANOIA_CHECK(jeb));
++
++ if (alloc_mode == ALLOC_GC) {
++ ret = jffs2_reserve_space_gc(c, sizeof(*rd) + namelen, &flash_ofs, &dummy);
++ } else {
++ /* Locking pain */
++ up(&f->sem);
++ jffs2_complete_reservation(c);
++
++ ret = jffs2_reserve_space(c, sizeof(*rd) + namelen, &flash_ofs, &dummy, alloc_mode);
++ down(&f->sem);
++ }
+
++ if (!ret) {
++ D1(printk(KERN_DEBUG "Allocated space at 0x%08x to retry failed write.\n", flash_ofs));
++ ACCT_SANITY_CHECK(c,jeb);
++ D1(ACCT_PARANOIA_CHECK(jeb));
++ goto retry;
++ }
++ D1(printk(KERN_DEBUG "Failed to allocate space to retry failed write: %d!\n", ret));
++ jffs2_free_raw_node_ref(raw);
++ }
+ /* Release the full_dnode which is now useless, and return */
+ jffs2_free_full_dirent(fd);
+- if (writelen)
+- *writelen = retlen;
+ return ERR_PTR(ret?ret:-EIO);
+ }
+ /* Mark the space used */
+- jffs2_add_physical_node_ref(c, raw, retlen, 0);
+- if (writelen)
+- *writelen = retlen;
++ raw->flash_offset |= REF_PRISTINE;
++ jffs2_add_physical_node_ref(c, raw);
+
++ raw->next_in_ino = f->inocache->nodes;
+ f->inocache->nodes = raw;
++
++ if (retried) {
++ ACCT_SANITY_CHECK(c,NULL);
++ }
++
+ return fd;
+ }
++
++/* The OS-specific code fills in the metadata in the jffs2_raw_inode for us, so that
++ we don't have to go digging in struct inode or its equivalent. It should set:
++ mode, uid, gid, (starting)isize, atime, ctime, mtime */
++int jffs2_write_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
++ struct jffs2_raw_inode *ri, unsigned char *buf,
++ uint32_t offset, uint32_t writelen, uint32_t *retlen)
++{
++ int ret = 0;
++ uint32_t writtenlen = 0;
++
++ D1(printk(KERN_DEBUG "jffs2_write_inode_range(): Ino #%u, ofs 0x%x, len 0x%x\n",
++ f->inocache->ino, offset, writelen));
++
++ while(writelen) {
++ struct jffs2_full_dnode *fn;
++ unsigned char *comprbuf = NULL;
++ unsigned char comprtype = JFFS2_COMPR_NONE;
++ uint32_t phys_ofs, alloclen;
++ uint32_t datalen, cdatalen;
++ int retried = 0;
++
++ retry:
++ D2(printk(KERN_DEBUG "jffs2_commit_write() loop: 0x%x to write to 0x%x\n", writelen, offset));
++
++ ret = jffs2_reserve_space(c, sizeof(*ri) + JFFS2_MIN_DATA_LEN, &phys_ofs, &alloclen, ALLOC_NORMAL);
++ if (ret) {
++ D1(printk(KERN_DEBUG "jffs2_reserve_space returned %d\n", ret));
++ break;
++ }
++ down(&f->sem);
++ datalen = min_t(uint32_t, writelen, PAGE_CACHE_SIZE - (offset & (PAGE_CACHE_SIZE-1)));
++ cdatalen = min_t(uint32_t, alloclen - sizeof(*ri), datalen);
++
++ comprtype = jffs2_compress(buf, &comprbuf, &datalen, &cdatalen);
++
++ ri->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
++ ri->nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE);
++ ri->totlen = cpu_to_je32(sizeof(*ri) + cdatalen);
++ ri->hdr_crc = cpu_to_je32(crc32(0, ri, sizeof(struct jffs2_unknown_node)-4));
++
++ ri->ino = cpu_to_je32(f->inocache->ino);
++ ri->version = cpu_to_je32(++f->highest_version);
++ ri->isize = cpu_to_je32(max(je32_to_cpu(ri->isize), offset + datalen));
++ ri->offset = cpu_to_je32(offset);
++ ri->csize = cpu_to_je32(cdatalen);
++ ri->dsize = cpu_to_je32(datalen);
++ ri->compr = comprtype;
++ ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
++ ri->data_crc = cpu_to_je32(crc32(0, comprbuf, cdatalen));
++
++ fn = jffs2_write_dnode(c, f, ri, comprbuf, cdatalen, phys_ofs, ALLOC_NORETRY);
++
++ jffs2_free_comprbuf(comprbuf, buf);
++
++ if (IS_ERR(fn)) {
++ ret = PTR_ERR(fn);
++ up(&f->sem);
++ jffs2_complete_reservation(c);
++ if (!retried) {
++ /* Write error to be retried */
++ retried = 1;
++ D1(printk(KERN_DEBUG "Retrying node write in jffs2_write_inode_range()\n"));
++ goto retry;
++ }
++ break;
++ }
++ ret = jffs2_add_full_dnode_to_inode(c, f, fn);
++ if (f->metadata) {
++ jffs2_mark_node_obsolete(c, f->metadata->raw);
++ jffs2_free_full_dnode(f->metadata);
++ f->metadata = NULL;
++ }
++ if (ret) {
++ /* Eep */
++ D1(printk(KERN_DEBUG "Eep. add_full_dnode_to_inode() failed in commit_write, returned %d\n", ret));
++ jffs2_mark_node_obsolete(c, fn->raw);
++ jffs2_free_full_dnode(fn);
++
++ up(&f->sem);
++ jffs2_complete_reservation(c);
++ break;
++ }
++ up(&f->sem);
++ jffs2_complete_reservation(c);
++ if (!datalen) {
++ printk(KERN_WARNING "Eep. We didn't actually write any data in jffs2_write_inode_range()\n");
++ ret = -EIO;
++ break;
++ }
++ D1(printk(KERN_DEBUG "increasing writtenlen by %d\n", datalen));
++ writtenlen += datalen;
++ offset += datalen;
++ writelen -= datalen;
++ buf += datalen;
++ }
++ *retlen = writtenlen;
++ return ret;
++}
++
++int jffs2_do_create(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, struct jffs2_inode_info *f, struct jffs2_raw_inode *ri, const char *name, int namelen)
++{
++ struct jffs2_raw_dirent *rd;
++ struct jffs2_full_dnode *fn;
++ struct jffs2_full_dirent *fd;
++ uint32_t alloclen, phys_ofs;
++ int ret;
++
++ /* Try to reserve enough space for both node and dirent.
++ * Just the node will do for now, though
++ */
++ ret = jffs2_reserve_space(c, sizeof(*ri), &phys_ofs, &alloclen, ALLOC_NORMAL);
++ D1(printk(KERN_DEBUG "jffs2_do_create(): reserved 0x%x bytes\n", alloclen));
++ if (ret) {
++ up(&f->sem);
++ return ret;
++ }
++
++ ri->data_crc = cpu_to_je32(0);
++ ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
++
++ fn = jffs2_write_dnode(c, f, ri, NULL, 0, phys_ofs, ALLOC_NORMAL);
++
++ D1(printk(KERN_DEBUG "jffs2_do_create created file with mode 0x%x\n",
++ jemode_to_cpu(ri->mode)));
++
++ if (IS_ERR(fn)) {
++ D1(printk(KERN_DEBUG "jffs2_write_dnode() failed\n"));
++ /* Eeek. Wave bye bye */
++ up(&f->sem);
++ jffs2_complete_reservation(c);
++ return PTR_ERR(fn);
++ }
++ /* No data here. Only a metadata node, which will be
++ obsoleted by the first data write
++ */
++ f->metadata = fn;
++
++ up(&f->sem);
++ jffs2_complete_reservation(c);
++ ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL);
++
++ if (ret) {
++ /* Eep. */
++ D1(printk(KERN_DEBUG "jffs2_reserve_space() for dirent failed\n"));
++ return ret;
++ }
++
++ rd = jffs2_alloc_raw_dirent();
++ if (!rd) {
++ /* Argh. Now we treat it like a normal delete */
++ jffs2_complete_reservation(c);
++ return -ENOMEM;
++ }
++
++ down(&dir_f->sem);
++
++ rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
++ rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT);
++ rd->totlen = cpu_to_je32(sizeof(*rd) + namelen);
++ rd->hdr_crc = cpu_to_je32(crc32(0, rd, sizeof(struct jffs2_unknown_node)-4));
++
++ rd->pino = cpu_to_je32(dir_f->inocache->ino);
++ rd->version = cpu_to_je32(++dir_f->highest_version);
++ rd->ino = ri->ino;
++ rd->mctime = ri->ctime;
++ rd->nsize = namelen;
++ rd->type = DT_REG;
++ rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8));
++ rd->name_crc = cpu_to_je32(crc32(0, name, namelen));
++
++ fd = jffs2_write_dirent(c, dir_f, rd, name, namelen, phys_ofs, ALLOC_NORMAL);
++
++ jffs2_free_raw_dirent(rd);
++
++ if (IS_ERR(fd)) {
++ /* dirent failed to write. Delete the inode normally
++ as if it were the final unlink() */
++ jffs2_complete_reservation(c);
++ up(&dir_f->sem);
++ return PTR_ERR(fd);
++ }
++
++ /* Link the fd into the inode's list, obsoleting an old
++ one if necessary. */
++ jffs2_add_fd_to_list(c, fd, &dir_f->dents);
++
++ jffs2_complete_reservation(c);
++ up(&dir_f->sem);
++
++ return 0;
++}
++
++
++int jffs2_do_unlink(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f,
++ const char *name, int namelen, struct jffs2_inode_info *dead_f)
++{
++ struct jffs2_raw_dirent *rd;
++ struct jffs2_full_dirent *fd;
++ uint32_t alloclen, phys_ofs;
++ int ret;
++
++ rd = jffs2_alloc_raw_dirent();
++ if (!rd)
++ return -ENOMEM;
++
++ ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_DELETION);
++ if (ret) {
++ jffs2_free_raw_dirent(rd);
++ return ret;
++ }
++
++ down(&dir_f->sem);
++
++ /* Build a deletion node */
++ rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
++ rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT);
++ rd->totlen = cpu_to_je32(sizeof(*rd) + namelen);
++ rd->hdr_crc = cpu_to_je32(crc32(0, rd, sizeof(struct jffs2_unknown_node)-4));
++
++ rd->pino = cpu_to_je32(dir_f->inocache->ino);
++ rd->version = cpu_to_je32(++dir_f->highest_version);
++ rd->ino = cpu_to_je32(0);
++ rd->mctime = cpu_to_je32(get_seconds());
++ rd->nsize = namelen;
++ rd->type = DT_UNKNOWN;
++ rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8));
++ rd->name_crc = cpu_to_je32(crc32(0, name, namelen));
++
++ fd = jffs2_write_dirent(c, dir_f, rd, name, namelen, phys_ofs, ALLOC_DELETION);
++
++ jffs2_free_raw_dirent(rd);
++
++ if (IS_ERR(fd)) {
++ jffs2_complete_reservation(c);
++ up(&dir_f->sem);
++ return PTR_ERR(fd);
++ }
++
++ /* File it. This will mark the old one obsolete. */
++ jffs2_add_fd_to_list(c, fd, &dir_f->dents);
++
++ up(&dir_f->sem);
++
++ /* dead_f is NULL if this was a rename not a real unlink */
++ /* Also catch the !f->inocache case, where there was a dirent
++ pointing to an inode which didn't exist. */
++ if (dead_f && dead_f->inocache) {
++
++ down(&dead_f->sem);
++
++ while (dead_f->dents) {
++ /* There can be only deleted ones */
++ fd = dead_f->dents;
++
++ dead_f->dents = fd->next;
++
++ if (fd->ino) {
++ printk(KERN_WARNING "Deleting inode #%u with active dentry \"%s\"->ino #%u\n",
++ dead_f->inocache->ino, fd->name, fd->ino);
++ } else {
++ D1(printk(KERN_DEBUG "Removing deletion dirent for \"%s\" from dir ino #%u\n", fd->name, dead_f->inocache->ino));
++ }
++ jffs2_mark_node_obsolete(c, fd->raw);
++ jffs2_free_full_dirent(fd);
++ }
++
++ dead_f->inocache->nlink--;
++ /* NB: Caller must set inode nlink if appropriate */
++ up(&dead_f->sem);
++ }
++
++ jffs2_complete_reservation(c);
++
++ return 0;
++}
++
++
++int jffs2_do_link (struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, uint32_t ino, uint8_t type, const char *name, int namelen)
++{
++ struct jffs2_raw_dirent *rd;
++ struct jffs2_full_dirent *fd;
++ uint32_t alloclen, phys_ofs;
++ int ret;
++
++ rd = jffs2_alloc_raw_dirent();
++ if (!rd)
++ return -ENOMEM;
++
++ ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL);
++ if (ret) {
++ jffs2_free_raw_dirent(rd);
++ return ret;
++ }
++
++ down(&dir_f->sem);
++
++ /* Build a deletion node */
++ rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
++ rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT);
++ rd->totlen = cpu_to_je32(sizeof(*rd) + namelen);
++ rd->hdr_crc = cpu_to_je32(crc32(0, rd, sizeof(struct jffs2_unknown_node)-4));
++
++ rd->pino = cpu_to_je32(dir_f->inocache->ino);
++ rd->version = cpu_to_je32(++dir_f->highest_version);
++ rd->ino = cpu_to_je32(ino);
++ rd->mctime = cpu_to_je32(get_seconds());
++ rd->nsize = namelen;
++
++ rd->type = type;
++
++ rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8));
++ rd->name_crc = cpu_to_je32(crc32(0, name, namelen));
++
++ fd = jffs2_write_dirent(c, dir_f, rd, name, namelen, phys_ofs, ALLOC_NORMAL);
++
++ jffs2_free_raw_dirent(rd);
++
++ if (IS_ERR(fd)) {
++ jffs2_complete_reservation(c);
++ up(&dir_f->sem);
++ return PTR_ERR(fd);
++ }
++
++ /* File it. This will mark the old one obsolete. */
++ jffs2_add_fd_to_list(c, fd, &dir_f->dents);
++
++ jffs2_complete_reservation(c);
++ up(&dir_f->sem);
++
++ return 0;
++}
+diff -Nurb linux-mips-2.4.27/fs/jffs2/writev.c linux/fs/jffs2/writev.c
+--- linux-mips-2.4.27/fs/jffs2/writev.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/fs/jffs2/writev.c 2004-11-19 10:25:12.134164072 +0100
+@@ -0,0 +1,50 @@
++/*
++ * JFFS2 -- Journalling Flash File System, Version 2.
++ *
++ * Copyright (C) 2001, 2002 Red Hat, Inc.
++ *
++ * Created by David Woodhouse <dwmw2@redhat.com>
++ *
++ * For licensing information, see the file 'LICENCE' in this directory.
++ *
++ * $Id$
++ *
++ */
++
++#include <linux/kernel.h>
++#include <linux/mtd/mtd.h>
++#include "nodelist.h"
++
++/* This ought to be in core MTD code. All registered MTD devices
++ without writev should have this put in place. Bug the MTD
++ maintainer */
++static inline int mtd_fake_writev(struct mtd_info *mtd, const struct iovec *vecs,
++ unsigned long count, loff_t to, size_t *retlen)
++{
++ unsigned long i;
++ size_t totlen = 0, thislen;
++ int ret = 0;
++
++ for (i=0; i<count; i++) {
++ if (!vecs[i].iov_len)
++ continue;
++ ret = mtd->write(mtd, to, vecs[i].iov_len, &thislen, vecs[i].iov_base);
++ totlen += thislen;
++ if (ret || thislen != vecs[i].iov_len)
++ break;
++ to += vecs[i].iov_len;
++ }
++ if (retlen)
++ *retlen = totlen;
++ return ret;
++}
++
++int jffs2_flash_direct_writev(struct jffs2_sb_info *c, const struct iovec *vecs,
++ unsigned long count, loff_t to, size_t *retlen)
++{
++ if (c->mtd->writev)
++ return c->mtd->writev(c->mtd, vecs, count, to, retlen);
++ else
++ return mtd_fake_writev(c->mtd, vecs, count, to, retlen);
++}
++
+diff -Nurb linux-mips-2.4.27/include/linux/jffs2.h linux/include/linux/jffs2.h
+--- linux-mips-2.4.27/include/linux/jffs2.h 2002-06-27 00:36:46.000000000 +0200
++++ linux/include/linux/jffs2.h 2004-11-19 10:25:12.139163312 +0100
+@@ -1,50 +1,30 @@
+ /*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+- * Copyright (C) 2001 Red Hat, Inc.
++ * Copyright (C) 2001-2003 Red Hat, Inc.
+ *
+- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
++ * Created by David Woodhouse <dwmw2@redhat.com>
+ *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
++ * For licensing information, see the file 'LICENCE' in the
++ * jffs2 directory.
+ *
+- * The contents of this file are subject to the Red Hat eCos Public
+- * License Version 1.1 (the "Licence"); you may not use this file
+- * except in compliance with the Licence. You may obtain a copy of
+- * the Licence at http://www.redhat.com/
+- *
+- * Software distributed under the Licence is distributed on an "AS IS"
+- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+- * See the Licence for the specific language governing rights and
+- * limitations under the Licence.
+- *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
+- *
+- * Alternatively, the contents of this file may be used under the
+- * terms of the GNU General Public License version 2 (the "GPL"), in
+- * which case the provisions of the GPL are applicable instead of the
+- * above. If you wish to allow the use of your version of this file
+- * only under the terms of the GPL and not to allow others to use your
+- * version of this file under the RHEPL, indicate your decision by
+- * deleting the provisions above and replace them with the notice and
+- * other provisions required by the GPL. If you do not delete the
+- * provisions above, a recipient may use your version of this file
+- * under either the RHEPL or the GPL.
+- *
+- * $Id$
++ * $Id$
+ *
+ */
+
+ #ifndef __LINUX_JFFS2_H__
+ #define __LINUX_JFFS2_H__
+
+-#include <asm/types.h>
++/* You must include something which defines the C99 uintXX_t types.
++ We don't do it from here because this file is used in too many
++ different environments. */
++
+ #define JFFS2_SUPER_MAGIC 0x72b6
+
+ /* Values we may expect to find in the 'magic' field */
+ #define JFFS2_OLD_MAGIC_BITMASK 0x1984
+ #define JFFS2_MAGIC_BITMASK 0x1985
+-#define KSAMTIB_CIGAM_2SFFJ 0x5981 /* For detecting wrong-endian fs */
++#define KSAMTIB_CIGAM_2SFFJ 0x8519 /* For detecting wrong-endian fs */
+ #define JFFS2_EMPTY_BITMASK 0xffff
+ #define JFFS2_DIRTY_BITMASK 0x0000
+
+@@ -78,16 +58,12 @@
+ #define JFFS2_NODETYPE_DIRENT (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 1)
+ #define JFFS2_NODETYPE_INODE (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 2)
+ #define JFFS2_NODETYPE_CLEANMARKER (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 3)
++#define JFFS2_NODETYPE_PADDING (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 4)
+
+ // Maybe later...
+ //#define JFFS2_NODETYPE_CHECKPOINT (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 3)
+ //#define JFFS2_NODETYPE_OPTIONS (JFFS2_FEATURE_RWCOMPAT_COPY | JFFS2_NODE_ACCURATE | 4)
+
+-/* Same as the non_ECC versions, but with extra space for real
+- * ECC instead of just the checksum. For use on NAND flash
+- */
+-//#define JFFS2_NODETYPE_DIRENT_ECC (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 5)
+-//#define JFFS2_NODETYPE_INODE_ECC (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 6)
+
+ #define JFFS2_INO_FLAG_PREREAD 1 /* Do read_inode() for this one at
+ mount time, don't wait for it to
+@@ -96,31 +72,79 @@
+ compression type */
+
+
++/* These can go once we've made sure we've caught all uses without
++ byteswapping */
++
++typedef struct {
++ uint32_t v32;
++} __attribute__((packed)) jint32_t;
++
++typedef struct {
++ uint32_t m;
++} __attribute__((packed)) jmode_t;
++
++typedef struct {
++ uint16_t v16;
++} __attribute__((packed)) jint16_t;
++
++#define JFFS2_NATIVE_ENDIAN
++
++/* Note we handle mode bits conversion from JFFS2 (i.e. Linux) to/from
++ whatever OS we're actually running on here too. */
++
++#if defined(JFFS2_NATIVE_ENDIAN)
++#define cpu_to_je16(x) ((jint16_t){x})
++#define cpu_to_je32(x) ((jint32_t){x})
++#define cpu_to_jemode(x) ((jmode_t){os_to_jffs2_mode(x)})
++
++#define je16_to_cpu(x) ((x).v16)
++#define je32_to_cpu(x) ((x).v32)
++#define jemode_to_cpu(x) (jffs2_to_os_mode((x).m))
++#elif defined(JFFS2_BIG_ENDIAN)
++#define cpu_to_je16(x) ((jint16_t){cpu_to_be16(x)})
++#define cpu_to_je32(x) ((jint32_t){cpu_to_be32(x)})
++#define cpu_to_jemode(x) ((jmode_t){cpu_to_be32(os_to_jffs2_mode(x))})
++
++#define je16_to_cpu(x) (be16_to_cpu(x.v16))
++#define je32_to_cpu(x) (be32_to_cpu(x.v32))
++#define jemode_to_cpu(x) (be32_to_cpu(jffs2_to_os_mode((x).m)))
++#elif defined(JFFS2_LITTLE_ENDIAN)
++#define cpu_to_je16(x) ((jint16_t){cpu_to_le16(x)})
++#define cpu_to_je32(x) ((jint32_t){cpu_to_le32(x)})
++#define cpu_to_jemode(x) ((jmode_t){cpu_to_le32(os_to_jffs2_mode(x))})
++
++#define je16_to_cpu(x) (le16_to_cpu(x.v16))
++#define je32_to_cpu(x) (le32_to_cpu(x.v32))
++#define jemode_to_cpu(x) (le32_to_cpu(jffs2_to_os_mode((x).m)))
++#else
++#error wibble
++#endif
++
+ struct jffs2_unknown_node
+ {
+ /* All start like this */
+- __u16 magic;
+- __u16 nodetype;
+- __u32 totlen; /* So we can skip over nodes we don't grok */
+- __u32 hdr_crc;
++ jint16_t magic;
++ jint16_t nodetype;
++ jint32_t totlen; /* So we can skip over nodes we don't grok */
++ jint32_t hdr_crc;
+ } __attribute__((packed));
+
+ struct jffs2_raw_dirent
+ {
+- __u16 magic;
+- __u16 nodetype; /* == JFFS_NODETYPE_DIRENT */
+- __u32 totlen;
+- __u32 hdr_crc;
+- __u32 pino;
+- __u32 version;
+- __u32 ino; /* == zero for unlink */
+- __u32 mctime;
+- __u8 nsize;
+- __u8 type;
+- __u8 unused[2];
+- __u32 node_crc;
+- __u32 name_crc;
+- __u8 name[0];
++ jint16_t magic;
++ jint16_t nodetype; /* == JFFS_NODETYPE_DIRENT */
++ jint32_t totlen;
++ jint32_t hdr_crc;
++ jint32_t pino;
++ jint32_t version;
++ jint32_t ino; /* == zero for unlink */
++ jint32_t mctime;
++ uint8_t nsize;
++ uint8_t type;
++ uint8_t unused[2];
++ jint32_t node_crc;
++ jint32_t name_crc;
++ uint8_t name[0];
+ } __attribute__((packed));
+
+ /* The JFFS2 raw inode structure: Used for storage on physical media. */
+@@ -131,28 +155,28 @@
+ */
+ struct jffs2_raw_inode
+ {
+- __u16 magic; /* A constant magic number. */
+- __u16 nodetype; /* == JFFS_NODETYPE_INODE */
+- __u32 totlen; /* Total length of this node (inc data, etc.) */
+- __u32 hdr_crc;
+- __u32 ino; /* Inode number. */
+- __u32 version; /* Version number. */
+- __u32 mode; /* The file's type or mode. */
+- __u16 uid; /* The file's owner. */
+- __u16 gid; /* The file's group. */
+- __u32 isize; /* Total resultant size of this inode (used for truncations) */
+- __u32 atime; /* Last access time. */
+- __u32 mtime; /* Last modification time. */
+- __u32 ctime; /* Change time. */
+- __u32 offset; /* Where to begin to write. */
+- __u32 csize; /* (Compressed) data size */
+- __u32 dsize; /* Size of the node's data. (after decompression) */
+- __u8 compr; /* Compression algorithm used */
+- __u8 usercompr; /* Compression algorithm requested by the user */
+- __u16 flags; /* See JFFS2_INO_FLAG_* */
+- __u32 data_crc; /* CRC for the (compressed) data. */
+- __u32 node_crc; /* CRC for the raw inode (excluding data) */
+-// __u8 data[dsize];
++ jint16_t magic; /* A constant magic number. */
++ jint16_t nodetype; /* == JFFS_NODETYPE_INODE */
++ jint32_t totlen; /* Total length of this node (inc data, etc.) */
++ jint32_t hdr_crc;
++ jint32_t ino; /* Inode number. */
++ jint32_t version; /* Version number. */
++ jmode_t mode; /* The file's type or mode. */
++ jint16_t uid; /* The file's owner. */
++ jint16_t gid; /* The file's group. */
++ jint32_t isize; /* Total resultant size of this inode (used for truncations) */
++ jint32_t atime; /* Last access time. */
++ jint32_t mtime; /* Last modification time. */
++ jint32_t ctime; /* Change time. */
++ jint32_t offset; /* Where to begin to write. */
++ jint32_t csize; /* (Compressed) data size */
++ jint32_t dsize; /* Size of the node's data. (after decompression) */
++ uint8_t compr; /* Compression algorithm used */
++ uint8_t usercompr; /* Compression algorithm requested by the user */
++ jint16_t flags; /* See JFFS2_INO_FLAG_* */
++ jint32_t data_crc; /* CRC for the (compressed) data. */
++ jint32_t node_crc; /* CRC for the raw inode (excluding data) */
++ uint8_t data[0];
+ } __attribute__((packed));
+
+ union jffs2_node_union {
+diff -Nurb linux-mips-2.4.27/include/linux/jffs2_fs_i.h linux/include/linux/jffs2_fs_i.h
+--- linux-mips-2.4.27/include/linux/jffs2_fs_i.h 2001-10-19 03:25:03.000000000 +0200
++++ linux/include/linux/jffs2_fs_i.h 2004-11-19 10:25:12.141163008 +0100
+@@ -1,22 +1,12 @@
+-/* $Id$ */
++/* $Id$ */
+
+ #ifndef _JFFS2_FS_I
+ #define _JFFS2_FS_I
+
+-/* Include the pipe_inode_info at the beginning so that we can still
+- use the storage space in the inode when we have a pipe inode.
+- This sucks.
+-*/
+-
+-#undef THISSUCKS /* Only for 2.2 */
+-#ifdef THISSUCKS
+-#include <linux/pipe_fs_i.h>
+-#endif
++#include <linux/version.h>
++#include <linux/rbtree.h>
+
+ struct jffs2_inode_info {
+-#ifdef THISSUCKS
+- struct pipe_inode_info pipecrap;
+-#endif
+ /* We need an internal semaphore similar to inode->i_sem.
+ Unfortunately, we can't used the existing one, because
+ either the GC would deadlock, or we'd have to release it
+@@ -26,10 +16,10 @@
+ struct semaphore sem;
+
+ /* The highest (datanode) version number used for this ino */
+- __u32 highest_version;
++ uint32_t highest_version;
+
+ /* List of data fragments which make up the file */
+- struct jffs2_node_frag *fraglist;
++ struct rb_root fragtree;
+
+ /* There may be one datanode which isn't referenced by any of the
+ above fragments, if it contains a metadata update but no actual
+@@ -44,19 +34,13 @@
+ /* Some stuff we just have to keep in-core at all times, for each inode. */
+ struct jffs2_inode_cache *inocache;
+
+- /* Keep a pointer to the last physical node in the list. We don't
+- use the doubly-linked lists because we don't want to increase
+- the memory usage that much. This is simpler */
+- // struct jffs2_raw_node_ref *lastnode;
+- __u16 flags;
+- __u8 usercompr;
+-};
+-
+-#ifdef JFFS2_OUT_OF_KERNEL
+-#define JFFS2_INODE_INFO(i) ((struct jffs2_inode_info *) &(i)->u)
+-#else
+-#define JFFS2_INODE_INFO(i) (&i->u.jffs2_i)
++ uint16_t flags;
++ uint8_t usercompr;
++#if !defined (__ECOS)
++#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,2)
++ struct inode vfs_inode;
+ #endif
++#endif
++};
+
+ #endif /* _JFFS2_FS_I */
+-
+diff -Nurb linux-mips-2.4.27/include/linux/jffs2_fs_sb.h linux/include/linux/jffs2_fs_sb.h
+--- linux-mips-2.4.27/include/linux/jffs2_fs_sb.h 2002-06-27 00:36:46.000000000 +0200
++++ linux/include/linux/jffs2_fs_sb.h 2004-11-19 10:25:12.142162856 +0100
+@@ -1,19 +1,22 @@
+-/* $Id$ */
++/* $Id$ */
+
+ #ifndef _JFFS2_FS_SB
+ #define _JFFS2_FS_SB
+
+ #include <linux/types.h>
+ #include <linux/spinlock.h>
++#include <linux/workqueue.h>
+ #include <linux/completion.h>
+ #include <asm/semaphore.h>
++#include <linux/timer.h>
++#include <linux/wait.h>
+ #include <linux/list.h>
+
+-#define INOCACHE_HASHSIZE 1
+-
+ #define JFFS2_SB_FLAG_RO 1
+ #define JFFS2_SB_FLAG_MOUNTING 2
+
++struct jffs2_inodirty;
++
+ /* A struct for the overall file system control. Pointers to
+ jffs2_sb_info structs are named `c' in the source code.
+ Nee jffs_control
+@@ -21,36 +24,46 @@
+ struct jffs2_sb_info {
+ struct mtd_info *mtd;
+
+- __u32 highest_ino;
++ uint32_t highest_ino;
++ uint32_t checked_ino;
++
+ unsigned int flags;
+- spinlock_t nodelist_lock;
+
+- // pid_t thread_pid; /* GC thread's PID */
+ struct task_struct *gc_task; /* GC task struct */
+ struct semaphore gc_thread_start; /* GC thread start mutex */
+ struct completion gc_thread_exit; /* GC thread exit completion port */
+- // __u32 gc_minfree_threshold; /* GC trigger thresholds */
+- // __u32 gc_maxdirty_threshold;
+
+ struct semaphore alloc_sem; /* Used to protect all the following
+ fields, and also to protect against
+ out-of-order writing of nodes.
+ And GC.
+ */
+- __u32 flash_size;
+- __u32 used_size;
+- __u32 dirty_size;
+- __u32 free_size;
+- __u32 erasing_size;
+- __u32 bad_size;
+- __u32 sector_size;
+- // __u32 min_free_size;
+- // __u32 max_chunk_size;
++ uint32_t cleanmarker_size; /* Size of an _inline_ CLEANMARKER
++ (i.e. zero for OOB CLEANMARKER */
++
++ uint32_t flash_size;
++ uint32_t used_size;
++ uint32_t dirty_size;
++ uint32_t wasted_size;
++ uint32_t free_size;
++ uint32_t erasing_size;
++ uint32_t bad_size;
++ uint32_t sector_size;
++ uint32_t unchecked_size;
++
++ uint32_t nr_free_blocks;
++ uint32_t nr_erasing_blocks;
++
++ /* Number of free blocks there must be before we... */
++ uint8_t resv_blocks_write; /* ... allow a normal filesystem write */
++ uint8_t resv_blocks_deletion; /* ... allow a normal filesystem deletion */
++ uint8_t resv_blocks_gctrigger; /* ... wake up the GC thread */
++ uint8_t resv_blocks_gcbad; /* ... pick a block from the bad_list to GC */
++ uint8_t resv_blocks_gcmerge; /* ... merge pages when garbage collecting */
+
+- __u32 nr_free_blocks;
+- __u32 nr_erasing_blocks;
++ uint32_t nospc_dirty_size;
+
+- __u32 nr_blocks;
++ uint32_t nr_blocks;
+ struct jffs2_eraseblock *blocks; /* The whole array of blocks. Used for getting blocks
+ * from the offset (blocks[ofs / sector_size]) */
+ struct jffs2_eraseblock *nextblock; /* The block we're currently filling */
+@@ -58,9 +71,12 @@
+ struct jffs2_eraseblock *gcblock; /* The block we're currently garbage-collecting */
+
+ struct list_head clean_list; /* Blocks 100% full of clean data */
++ struct list_head very_dirty_list; /* Blocks with lots of dirty space */
+ struct list_head dirty_list; /* Blocks with some dirty space */
++ struct list_head erasable_list; /* Blocks which are completely dirty, and need erasing */
++ struct list_head erasable_pending_wbuf_list; /* Blocks which need erasing but only after the current wbuf is flushed */
+ struct list_head erasing_list; /* Blocks which are currently erasing */
+- struct list_head erase_pending_list; /* Blocks which need erasing */
++ struct list_head erase_pending_list; /* Blocks which need erasing now */
+ struct list_head erase_complete_list; /* Blocks which are erased and need the clean marker written to them */
+ struct list_head free_list; /* Blocks which are free and ready to be used */
+ struct list_head bad_list; /* Bad blocks. */
+@@ -69,16 +85,33 @@
+ spinlock_t erase_completion_lock; /* Protect free_list and erasing_list
+ against erase completion handler */
+ wait_queue_head_t erase_wait; /* For waiting for erases to complete */
+- struct jffs2_inode_cache *inocache_list[INOCACHE_HASHSIZE];
++
++ wait_queue_head_t inocache_wq;
++ struct jffs2_inode_cache **inocache_list;
+ spinlock_t inocache_lock;
+-};
+
+-#ifdef JFFS2_OUT_OF_KERNEL
+-#define JFFS2_SB_INFO(sb) ((struct jffs2_sb_info *) &(sb)->u)
+-#else
+-#define JFFS2_SB_INFO(sb) (&sb->u.jffs2_sb)
++ /* Sem to allow jffs2_garbage_collect_deletion_dirent to
++ drop the erase_completion_lock while it's holding a pointer
++ to an obsoleted node. I don't like this. Alternatives welcomed. */
++ struct semaphore erase_free_sem;
++
++#ifdef CONFIG_JFFS2_FS_NAND
++ /* Write-behind buffer for NAND flash */
++ unsigned char *wbuf;
++ uint32_t wbuf_ofs;
++ uint32_t wbuf_len;
++ uint32_t wbuf_pagesize;
++ struct jffs2_inodirty *wbuf_inodes;
++
++ /* Information about out-of-band area usage... */
++ struct nand_oobinfo *oobinfo;
++ uint32_t badblock_pos;
++ uint32_t fsdata_pos;
++ uint32_t fsdata_len;
+ #endif
+
+-#define OFNI_BS_2SFFJ(c) ((struct super_block *) ( ((char *)c) - ((char *)(&((struct super_block *)NULL)->u)) ) )
++ /* OS-private pointer for getting back to master superblock info */
++ void *os_priv;
++};
+
+ #endif /* _JFFS2_FB_SB */
+diff -Nurb linux-mips-2.4.27/include/linux/mtd/blktrans.h linux/include/linux/mtd/blktrans.h
+--- linux-mips-2.4.27/include/linux/mtd/blktrans.h 1970-01-01 01:00:00.000000000 +0100
++++ linux/include/linux/mtd/blktrans.h 2004-11-19 10:25:12.037178816 +0100
+@@ -0,0 +1,72 @@
++/*
++ * $Id$
++ *
++ * (C) 2003 David Woodhouse <dwmw2@infradead.org>
++ *
++ * Interface to Linux block layer for MTD 'translation layers'.
++ *
++ */
++
++#ifndef __MTD_TRANS_H__
++#define __MTD_TRANS_H__
++
++#include <asm/semaphore.h>
++
++struct hd_geometry;
++struct mtd_info;
++struct mtd_blktrans_ops;
++struct file;
++struct inode;
++
++struct mtd_blktrans_dev {
++ struct mtd_blktrans_ops *tr;
++ struct list_head list;
++ struct mtd_info *mtd;
++ struct semaphore sem;
++ int devnum;
++ int blksize;
++ unsigned long size;
++ int readonly;
++ void *blkcore_priv; /* gendisk in 2.5, devfs_handle in 2.4 */
++};
++
++struct blkcore_priv; /* Differs for 2.4 and 2.5 kernels; private */
++
++struct mtd_blktrans_ops {
++ char *name;
++ int major;
++ int part_bits;
++
++ /* Access functions */
++ int (*readsect)(struct mtd_blktrans_dev *dev,
++ unsigned long block, char *buffer);
++ int (*writesect)(struct mtd_blktrans_dev *dev,
++ unsigned long block, char *buffer);
++
++ /* Block layer ioctls */
++ int (*getgeo)(struct mtd_blktrans_dev *dev, struct hd_geometry *geo);
++ int (*flush)(struct mtd_blktrans_dev *dev);
++
++ /* Called with mtd_table_mutex held; no race with add/remove */
++ int (*open)(struct mtd_blktrans_dev *dev);
++ int (*release)(struct mtd_blktrans_dev *dev);
++
++ /* Called on {de,}registration and on subsequent addition/removal
++ of devices, with mtd_table_mutex held. */
++ void (*add_mtd)(struct mtd_blktrans_ops *tr, struct mtd_info *mtd);
++ void (*remove_dev)(struct mtd_blktrans_dev *dev);
++
++ struct list_head devs;
++ struct list_head list;
++ struct module *owner;
++
++ struct mtd_blkcore_priv *blkcore_priv;
++};
++
++extern int register_mtd_blktrans(struct mtd_blktrans_ops *tr);
++extern int deregister_mtd_blktrans(struct mtd_blktrans_ops *tr);
++extern int add_mtd_blktrans_dev(struct mtd_blktrans_dev *dev);
++extern int del_mtd_blktrans_dev(struct mtd_blktrans_dev *dev);
++
++
++#endif /* __MTD_TRANS_H__ */
+diff -Nurb linux-mips-2.4.27/include/linux/mtd/cfi.h linux/include/linux/mtd/cfi.h
+--- linux-mips-2.4.27/include/linux/mtd/cfi.h 2003-02-26 01:53:51.000000000 +0100
++++ linux/include/linux/mtd/cfi.h 2004-11-19 10:25:12.038178664 +0100
+@@ -1,13 +1,14 @@
+
+ /* Common Flash Interface structures
+ * See http://support.intel.com/design/flash/technote/index.htm
+- * $Id$
++ * $Id$
+ */
+
+ #ifndef __MTD_CFI_H__
+ #define __MTD_CFI_H__
+
+ #include <linux/config.h>
++#include <linux/version.h>
+ #include <linux/delay.h>
+ #include <linux/types.h>
+ #include <linux/interrupt.h>
+@@ -260,7 +261,8 @@
+ __u8 pri[3];
+ __u8 MajorVersion;
+ __u8 MinorVersion;
+- __u32 FeatureSupport;
++ __u32 FeatureSupport; /* if bit 31 is set then an additional __u32 feature
++ block follows - FIXME - not currently supported */
+ __u8 SuspendCmdSupport;
+ __u16 BlkStatusRegMask;
+ __u8 VccOptimal;
+@@ -271,6 +273,25 @@
+ __u8 UserProtRegSize;
+ } __attribute__((packed));
+
++/* Vendor-Specific PRI for AMD/Fujitsu Extended Command Set (0x0002) */
++
++struct cfi_pri_amdstd {
++ __u8 pri[3];
++ __u8 MajorVersion;
++ __u8 MinorVersion;
++ __u8 SiliconRevision; /* bits 1-0: Address Sensitive Unlock */
++ __u8 EraseSuspend;
++ __u8 BlkProt;
++ __u8 TmpBlkUnprotect;
++ __u8 BlkProtUnprot;
++ __u8 SimultaneousOps;
++ __u8 BurstMode;
++ __u8 PageMode;
++ __u8 VppMin;
++ __u8 VppMax;
++ __u8 TopBottom;
++} __attribute__((packed));
++
+ struct cfi_pri_query {
+ __u8 NumFields;
+ __u32 ProtField[1]; /* Not host ordered */
+@@ -314,8 +335,6 @@
+ struct flchip chips[0]; /* per-chip data structure for each chip */
+ };
+
+-#define MAX_CFI_CHIPS 8 /* Entirely arbitrary to avoid realloc() */
+-
+ /*
+ * Returns the command address according to the given geometry.
+ */
+@@ -387,13 +406,13 @@
+ static inline cfi_word cfi_read(struct map_info *map, __u32 addr)
+ {
+ if (cfi_buswidth_is_1()) {
+- return map->read8(map, addr);
++ return map_read8(map, addr);
+ } else if (cfi_buswidth_is_2()) {
+- return map->read16(map, addr);
++ return map_read16(map, addr);
+ } else if (cfi_buswidth_is_4()) {
+- return map->read32(map, addr);
++ return map_read32(map, addr);
+ } else if (cfi_buswidth_is_8()) {
+- return map->read64(map, addr);
++ return map_read64(map, addr);
+ } else {
+ return 0;
+ }
+@@ -406,13 +425,13 @@
+ static inline void cfi_write(struct map_info *map, cfi_word val, __u32 addr)
+ {
+ if (cfi_buswidth_is_1()) {
+- map->write8(map, val, addr);
++ map_write8(map, val, addr);
+ } else if (cfi_buswidth_is_2()) {
+- map->write16(map, val, addr);
++ map_write16(map, val, addr);
+ } else if (cfi_buswidth_is_4()) {
+- map->write32(map, val, addr);
++ map_write32(map, val, addr);
+ } else if (cfi_buswidth_is_8()) {
+- map->write64(map, val, addr);
++ map_write64(map, val, addr);
+ }
+ }
+
+@@ -443,13 +462,13 @@
+ static inline __u8 cfi_read_query(struct map_info *map, __u32 addr)
+ {
+ if (cfi_buswidth_is_1()) {
+- return map->read8(map, addr);
++ return map_read8(map, addr);
+ } else if (cfi_buswidth_is_2()) {
+- return cfi16_to_cpu(map->read16(map, addr));
++ return cfi16_to_cpu(map_read16(map, addr));
+ } else if (cfi_buswidth_is_4()) {
+- return cfi32_to_cpu(map->read32(map, addr));
++ return cfi32_to_cpu(map_read32(map, addr));
+ } else if (cfi_buswidth_is_8()) {
+- return cfi64_to_cpu(map->read64(map, addr));
++ return cfi64_to_cpu(map_read64(map, addr));
+ } else {
+ return 0;
+ }
+@@ -479,5 +498,19 @@
+ spin_unlock_bh(mutex);
+ }
+
++struct cfi_extquery *cfi_read_pri(struct map_info *map, __u16 adr, __u16 size,
++ const char* name);
++
++struct cfi_fixup {
++ __u16 mfr;
++ __u16 id;
++ void (*fixup)(struct map_info *map, void* param);
++ void* param;
++};
++
++#define CFI_MFR_ANY 0xffff
++#define CFI_ID_ANY 0xffff
++
++void cfi_fixup(struct map_info *map, struct cfi_fixup* fixups);
+
+ #endif /* __MTD_CFI_H__ */
+diff -Nurb linux-mips-2.4.27/include/linux/mtd/compatmac.h linux/include/linux/mtd/compatmac.h
+--- linux-mips-2.4.27/include/linux/mtd/compatmac.h 2003-02-26 01:53:51.000000000 +0100
++++ linux/include/linux/mtd/compatmac.h 2004-11-19 10:25:12.041178208 +0100
+@@ -1,573 +1,152 @@
+-
+ /*
+- * mtd/include/compatmac.h
+- *
+- * $Id$
++ * $Id$
+ *
+ * Extensions and omissions from the normal 'linux/compatmac.h'
+ * files. hopefully this will end up empty as the 'real' one
+ * becomes fully-featured.
+ */
+
+-
+-/* First, include the parts which the kernel is good enough to provide
+- * to us
+- */
+-
+ #ifndef __LINUX_MTD_COMPATMAC_H__
+ #define __LINUX_MTD_COMPATMAC_H__
+
+-#include <linux/config.h>
+-#include <linux/module.h>
+-#ifndef LINUX_VERSION_CODE
+ #include <linux/version.h>
+-#endif
+-
+-#ifndef VERSION_CODE
+-# define VERSION_CODE(vers,rel,seq) ( ((vers)<<16) | ((rel)<<8) | (seq) )
+-#endif
+-#ifndef KERNEL_VERSION
+-# define KERNEL_VERSION(a,b,c) VERSION_CODE(a,b,c)
+-#endif
+
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,0,0)
+-# error "This kernel is too old: not supported by this file"
+-#endif
+-
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0)
+-#include <linux/types.h> /* used later in this header */
+-
+-#define memcpy_fromio(a,b,c) memcpy((a),(void *)(b),(c))
+-#define memcpy_toio(a,b,c) memcpy((void *)(a),(b),(c))
+-
+-typedef struct wait_queue * wait_queue_head_t;
+-
+-#define DECLARE_WAITQUEUE(x,y) struct wait_queue x = {y,NULL}
+-#define DECLARE_WAIT_QUEUE_HEAD(x) struct wait_queue *x = NULL
+-#define init_waitqueue_head init_waitqueue
+-#define DECLARE_MUTEX(x) struct semaphore x = MUTEX
+-#define DECLARE_MUTEX_LOCKED(x) struct semaphore x = MUTEX_LOCKED
+-
+-/* from sysdep-2.1.h */
+-# include <asm/segment.h>
+-# define access_ok(t,a,sz) (verify_area((t),(a),(sz)) ? 0 : 1)
+-# define verify_area_20 verify_area
+-# define copy_to_user(t,f,n) (memcpy_tofs(t,f,n), 0)
+-# define __copy_to_user(t,f,n) copy_to_user((t),(f),(n))
+-# define copy_to_user_ret(t,f,n,r) copy_to_user((t),(f),(n))
+-# define copy_from_user(t,f,n) (memcpy_fromfs((t),(f),(n)), 0)
+-# define __copy_from_user(t,f,n) copy_from_user((t),(f),(n))
+-# define copy_from_user_ret(t,f,n,r) copy_from_user((t),(f),(n))
+-//xxx # define PUT_USER(val,add) (put_user((val),(add)), 0)
+-# define Put_user(val,add) (put_user((val),(add)), 0)
+-# define __PUT_USER(val,add) PUT_USER((val),(add))
+-# define PUT_USER_RET(val,add,ret) PUT_USER((val),(add))
+-# define GET_USER(dest,add) ((dest)=get_user((add)), 0)
+-# define __GET_USER(dest,add) GET_USER((dest),(add))
+-# define GET_USER_RET(dest,add,ret) GET_USER((dest),(add))
+-
+-#define ioremap(offset,size) vremap(offset,size)
+-#define iounmap(adr) /* */
+-
+-#define EXPORT_SYMBOL(s) /* */
+-#define EXPORT_SYMBOL_NOVERS(s) /* */
+-
+-/* 2.1.10 and 2.1.43 introduced new functions. They are worth using */
+-
+-#if LINUX_VERSION_CODE < VERSION_CODE(2,1,10)
+-
+-# include <asm/byteorder.h>
+-# ifdef __LITTLE_ENDIAN
+-# define cpu_to_le16(x) (x)
+-# define cpu_to_le32(x) (x)
+-# define cpu_to_be16(x) htons((x))
+-# define cpu_to_be32(x) htonl((x))
+-# else
+-# define cpu_to_be16(x) (x)
+-# define cpu_to_be32(x) (x)
+- extern inline __u16 cpu_to_le16(__u16 x) { return (x<<8) | (x>>8);}
+- extern inline __u32 cpu_to_le32(__u32 x) { return((x>>24) |
+- ((x>>8)&0xff00) | ((x<<8)&0xff0000) | (x<<24));}
+-# endif
+-
+-# define le16_to_cpu(x) cpu_to_le16(x)
+-# define le32_to_cpu(x) cpu_to_le32(x)
+-# define be16_to_cpu(x) cpu_to_be16(x)
+-# define be32_to_cpu(x) cpu_to_be32(x)
+-
+-#endif
+-
+-#if LINUX_VERSION_CODE < VERSION_CODE(2,1,43)
+-# define cpu_to_le16p(addr) (cpu_to_le16(*(addr)))
+-# define cpu_to_le32p(addr) (cpu_to_le32(*(addr)))
+-# define cpu_to_be16p(addr) (cpu_to_be16(*(addr)))
+-# define cpu_to_be32p(addr) (cpu_to_be32(*(addr)))
+-
+- extern inline void cpu_to_le16s(__u16 *a) {*a = cpu_to_le16(*a);}
+- extern inline void cpu_to_le32s(__u16 *a) {*a = cpu_to_le32(*a);}
+- extern inline void cpu_to_be16s(__u16 *a) {*a = cpu_to_be16(*a);}
+- extern inline void cpu_to_be32s(__u16 *a) {*a = cpu_to_be32(*a);}
+-
+-# define le16_to_cpup(x) cpu_to_le16p(x)
+-# define le32_to_cpup(x) cpu_to_le32p(x)
+-# define be16_to_cpup(x) cpu_to_be16p(x)
+-# define be32_to_cpup(x) cpu_to_be32p(x)
+-
+-# define le16_to_cpus(x) cpu_to_le16s(x)
+-# define le32_to_cpus(x) cpu_to_le32s(x)
+-# define be16_to_cpus(x) cpu_to_be16s(x)
+-# define be32_to_cpus(x) cpu_to_be32s(x)
+-#endif
+-
+-// from 2.2, linux/types.h
+-#ifndef __BIT_TYPES_DEFINED__
+-#define __BIT_TYPES_DEFINED__
+-
+-typedef __u8 u_int8_t;
+-typedef __s8 int8_t;
+-typedef __u16 u_int16_t;
+-typedef __s16 int16_t;
+-typedef __u32 u_int32_t;
+-typedef __s32 int32_t;
+-
+-#endif /* !(__BIT_TYPES_DEFINED__) */
+-
+-#if (__GNUC__ > 2) || (__GNUC__ == 2 && __GNUC_MINOR__ >= 8)
+- typedef struct { } spinlock_t;
+- #define SPIN_LOCK_UNLOCKED (spinlock_t) { }
+-#else
+- typedef struct { int gcc_is_buggy; } spinlock_t;
+- #define SPIN_LOCK_UNLOCKED (spinlock_t) { 0 }
+-#endif
+-
+-#define spin_lock_init(lock) do { } while(0)
+-#define spin_lock(lock) (void)(lock) /* Not "unused variable". */
+-#define spin_trylock(lock) (1)
+-#define spin_unlock_wait(lock) do { } while(0)
+-#define spin_unlock(lock) do { } while(0)
+-#define spin_lock_irq(lock) cli()
+-#define spin_unlock_irq(lock) sti()
+-
+-#define spin_lock_irqsave(lock, flags) \
+- do { save_flags(flags); cli(); } while (0)
+-#define spin_unlock_irqrestore(lock, flags) \
+- restore_flags(flags)
+-
+-// Doesn't work when tqueue.h is included.
+-// #define queue_task queue_task_irq_off
+-#define tty_flip_buffer_push(tty) queue_task_irq_off(&tty->flip.tqueue, &tq_timer)
+-#define signal_pending(current) (current->signal & ~current->blocked)
+-#define schedule_timeout(to) do {current->timeout = jiffies + (to);schedule ();} while (0)
+-#define time_after(t1,t2) (((long)t1-t2) > 0)
+-
+-#else
+- #include <linux/compatmac.h>
+-#endif // LINUX_VERSION_CODE < 0x020100
+-
+-
+-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
+-#include <linux/vmalloc.h>
+-#endif
+-
+-/* Modularization issues */
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,18)
+-# define __USE_OLD_SYMTAB__
+-# define EXPORT_NO_SYMBOLS register_symtab(NULL);
+-# define REGISTER_SYMTAB(tab) register_symtab(tab)
+-#else
+-# define REGISTER_SYMTAB(tab) /* nothing */
+-#endif
+-
+-#ifdef __USE_OLD_SYMTAB__
+-# define __MODULE_STRING(s) /* nothing */
+-# define MODULE_PARM(v,t) /* nothing */
+-# define MODULE_PARM_DESC(v,t) /* nothing */
+-# define MODULE_AUTHOR(n) /* nothing */
+-# define MODULE_DESCRIPTION(d) /* nothing */
+-# define MODULE_SUPPORTED_DEVICE(n) /* nothing */
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,10)
++#error "This kernel is too old: not supported by this file"
+ #endif
+
+-/*
+- * "select" changed in 2.1.23. The implementation is twin, but this
+- * header is new
+- */
+-#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,22)
+-# include <linux/poll.h>
+-#else
+-# define __USE_OLD_SELECT__
+-#endif
++ /* O(1) scheduler stuff. */
+
+-/* Other change in the fops are solved using pseudo-types */
+-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
+-# define lseek_t long long
+-# define lseek_off_t long long
+-#else
+-# define lseek_t int
+-# define lseek_off_t off_t
+-#endif
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,5) && !defined(__rh_config_h__)
++#include <linux/sched.h>
++static inline void __recalc_sigpending(void)
++{
++ recalc_sigpending(current);
++}
++#undef recalc_sigpending
++#define recalc_sigpending() __recalc_sigpending ()
+
+-/* changed the prototype of read/write */
++#define set_user_nice(tsk, n) do { (tsk)->nice = n; } while(0)
+
+-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) || defined(__alpha__)
+-# define count_t unsigned long
+-# define read_write_t long
+-#else
+-# define count_t int
+-# define read_write_t int
+ #endif
+
+
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,31)
+-# define release_t void
+-# define release_return(x) return
+-#else
+-# define release_t int
+-# define release_return(x) return (x)
+-#endif
+-
+-#if LINUX_VERSION_CODE < 0x20300
+-#define __exit
+-#endif
+-#if LINUX_VERSION_CODE < 0x20200
+-#define __init
+-#else
+-#include <linux/init.h>
+-#endif
+
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)
+-#define init_MUTEX(x) do {*(x) = MUTEX;} while (0)
+-#define init_MUTEX_LOCKED(x) do {*(x) = MUTEX_LOCKED;} while (0)
+-#endif
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,20)
+
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
+-#define RQFUNC_ARG void
+-#define blkdev_dequeue_request(req) do {CURRENT = req->next;} while (0)
+-#else
+-#define RQFUNC_ARG request_queue_t *q
++#ifndef yield
++#define yield() do { set_current_state(TASK_RUNNING); schedule(); } while(0)
+ #endif
+
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,32)
+-#define blk_cleanup_queue(nr) do {blk_dev[nr].request_fn = 0;} while(0)
+-#define BLK_DEFAULT_QUEUE(nr) (blk_dev[nr].request_fn)
+-#define blk_init_queue(q, rq) do {q = rq;} while(0)
++#ifndef minor
++#define major(d) (MAJOR(to_kdev_t(d)))
++#define minor(d) (MINOR(to_kdev_t(d)))
+ #endif
+
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,0)
+-#ifdef CONFIG_MODULES
+-#define __MOD_INC_USE_COUNT(mod) \
+- (atomic_inc(&(mod)->uc.usecount), (mod)->flags |= MOD_VISITED|MOD_USED_ONCE)
+-#define __MOD_DEC_USE_COUNT(mod) \
+- (atomic_dec(&(mod)->uc.usecount), (mod)->flags |= MOD_VISITED)
+-#else
+-#define __MOD_INC_USE_COUNT(mod)
+-#define __MOD_DEC_USE_COUNT(mod)
+-#endif
++#ifndef mk_kdev
++#define mk_kdev(ma,mi) MKDEV(ma,mi)
++#define kdev_t_to_nr(x) (x)
+ #endif
+
++#define need_resched() (current->need_resched)
++#define cond_resched() do { if need_resched() { yield(); } } while(0)
+
+-#ifndef HAVE_INTER_MODULE
+-static inline void *inter_module_get(char *x) {return NULL;}
+-static inline void *inter_module_get_request(char *x, char *y) {return NULL;}
+-static inline void inter_module_put(const char *x) {}
+-static inline void inter_module_register(const char *x, struct module *y, const void *z) {}
+-static inline void inter_module_unregister(const char *x) {}
++#endif /* < 2.4.20 */
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,73)
++#define iminor(i) minor((i)->i_rdev)
++#define imajor(i) major((i)->i_rdev)
++#define old_encode_dev(d) ( (major(d)<<8) | minor(d) )
++#define old_decode_dev(rdev) (kdev_t_to_nr(mk_kdev((rdev)>>8, (rdev)&0xff)))
++#define old_valid_dev(d) (1)
+ #endif
+
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,61)
+
+-#define DECLARE_WAIT_QUEUE_HEAD(x) struct wait_queue *x = NULL
+-#define init_waitqueue_head init_waitqueue
++#include <linux/sched.h>
+
++#ifdef __rh_config_h__
++#define sigmask_lock sighand->siglock
++#define sig sighand
+ #endif
+
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
+-
+-static inline int try_inc_mod_count(struct module *mod)
++static inline void __daemonize_modvers(void)
+ {
+-#ifdef CONFIG_MODULES
+- if (mod)
+- __MOD_INC_USE_COUNT(mod);
+-#endif
+- return 1;
+-}
+-#endif
++ daemonize();
+
++ spin_lock_irq(&current->sigmask_lock);
++ sigfillset(&current->blocked);
++ recalc_sigpending();
++ spin_unlock_irq(&current->sigmask_lock);
++}
++#undef daemonize
++#define daemonize(fmt, ...) do { \
++ snprintf(current->comm, sizeof(current->comm), fmt ,##__VA_ARGS__); \
++ __daemonize_modvers(); \
++ } while(0)
+
+-/* Yes, I'm aware that it's a fairly ugly hack.
+- Until the __constant_* macros appear in Linus' own kernels, this is
+- the way it has to be done.
+- DW 19/1/00
+- */
+-
+-#include <asm/byteorder.h>
+-
+-#ifndef __constant_cpu_to_le16
+-
+-#ifdef __BIG_ENDIAN
+-#define __constant_cpu_to_le64(x) ___swab64((x))
+-#define __constant_le64_to_cpu(x) ___swab64((x))
+-#define __constant_cpu_to_le32(x) ___swab32((x))
+-#define __constant_le32_to_cpu(x) ___swab32((x))
+-#define __constant_cpu_to_le16(x) ___swab16((x))
+-#define __constant_le16_to_cpu(x) ___swab16((x))
+-#define __constant_cpu_to_be64(x) ((__u64)(x))
+-#define __constant_be64_to_cpu(x) ((__u64)(x))
+-#define __constant_cpu_to_be32(x) ((__u32)(x))
+-#define __constant_be32_to_cpu(x) ((__u32)(x))
+-#define __constant_cpu_to_be16(x) ((__u16)(x))
+-#define __constant_be16_to_cpu(x) ((__u16)(x))
+-#else
+-#ifdef __LITTLE_ENDIAN
+-#define __constant_cpu_to_le64(x) ((__u64)(x))
+-#define __constant_le64_to_cpu(x) ((__u64)(x))
+-#define __constant_cpu_to_le32(x) ((__u32)(x))
+-#define __constant_le32_to_cpu(x) ((__u32)(x))
+-#define __constant_cpu_to_le16(x) ((__u16)(x))
+-#define __constant_le16_to_cpu(x) ((__u16)(x))
+-#define __constant_cpu_to_be64(x) ___swab64((x))
+-#define __constant_be64_to_cpu(x) ___swab64((x))
+-#define __constant_cpu_to_be32(x) ___swab32((x))
+-#define __constant_be32_to_cpu(x) ___swab32((x))
+-#define __constant_cpu_to_be16(x) ___swab16((x))
+-#define __constant_be16_to_cpu(x) ___swab16((x))
+-#else
+-#error No (recognised) endianness defined (unless it,s PDP)
+-#endif /* __LITTLE_ENDIAN */
+-#endif /* __BIG_ENDIAN */
+-
+-#endif /* ifndef __constant_cpu_to_le16 */
+-
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
+- #define mod_init_t int __init
+- #define mod_exit_t void
+-#else
+- #define mod_init_t static int __init
+- #define mod_exit_t static void __exit
+-#endif
+-
+-#ifndef THIS_MODULE
+-#ifdef MODULE
+-#define THIS_MODULE (&__this_module)
+-#else
+-#define THIS_MODULE (NULL)
+-#endif
+-#endif
+-
+-#if LINUX_VERSION_CODE < 0x20300
+-#include <linux/interrupt.h>
+-#define spin_lock_bh(lock) do {start_bh_atomic();spin_lock(lock);}while(0)
+-#define spin_unlock_bh(lock) do {spin_unlock(lock);end_bh_atomic();}while(0)
+-#else
+-#include <asm/softirq.h>
+-#include <linux/spinlock.h>
+-#endif
+-
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)
+-#define set_current_state(state_value) \
+- do { current->state = (state_value); } while (0)
+-#endif
+-
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,0)
+-static inline int invalidate_device(kdev_t dev, int do_sync) {
++static inline int dequeue_signal_lock(struct task_struct *tsk, sigset_t *mask, siginfo_t *info)
++{
++ unsigned long flags;
++ unsigned long ret;
+
+- if (do_sync)
+- fsync_dev(dev);
++ spin_lock_irqsave(&current->sigmask_lock, flags);
++ ret = dequeue_signal(mask, info);
++ spin_unlock_irqrestore(&current->sigmask_lock, flags);
+
+- invalidate_buffers(dev);
+- return 0;
++ return ret;
+ }
+-#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,4,5)
+-static inline int invalidate_device(kdev_t dev, int do_sync) {
+- struct super_block *sb = get_super(dev);
+- int res = 0;
+
+- if (do_sync)
+- fsync_dev(dev);
+-
+- if (sb)
+- res = invalidate_inodes(sb);
++static inline int allow_signal(int sig)
++{
++ if (sig < 1 || sig > _NSIG)
++ return -EINVAL;
+
+- invalidate_buffers(dev);
+- return res;
++ spin_lock_irq(&current->sigmask_lock);
++ sigdelset(&current->blocked, sig);
++ recalc_sigpending();
++ /* Make sure the kernel neither eats it now converts to SIGKILL */
++ current->sig->action[sig-1].sa.sa_handler = (void *)2;
++ spin_unlock_irq(&current->sigmask_lock);
++ return 0;
+ }
+-#endif
+-
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,10)
+-#undef min
+-#undef max
+-#undef min_t
+-#undef max_t
+-/*
+- * min()/max() macros that also do
+- * strict type-checking.. See the
+- * "unnecessary" pointer comparison.
+- */
+-#define min(x,y) ({ \
+- const typeof(x) _x = (x); \
+- const typeof(y) _y = (y); \
+- (void) (&_x == &_y); \
+- _x < _y ? _x : _y; })
+-
+-#define max(x,y) ({ \
+- const typeof(x) _x = (x); \
+- const typeof(y) _y = (y); \
+- (void) (&_x == &_y); \
+- _x > _y ? _x : _y; })
+-
+-/*
+- * ..and if you can't take the strict
+- * types, you can specify one yourself.
+- *
+- * Or not use min/max at all, of course.
+- */
+-#define min_t(type,x,y) \
+- ({ type __x = (x); type __y = (y); __x < __y ? __x: __y; })
+-#define max_t(type,x,y) \
+- ({ type __x = (x); type __y = (y); __x > __y ? __x: __y; })
+-#endif
+-
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,7)
+-struct completion {
+- struct semaphore s;
+-};
+-
+-#define complete(c) up(&(c)->s)
+-#define wait_for_completion(c) down(&(c)->s)
+-#define init_completion(c) init_MUTEX_LOCKED(&(c)->s);
+-
+-#endif
+-
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,9)
+-/* This came later */
+-#define complete_and_exit(c, r) do { complete(c); do_exit(r); } while(0)
+-#endif
+-
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,9) || \
+- (LINUX_VERSION_CODE < KERNEL_VERSION(2,4,10) && !defined(__rh_config_h__))
++static inline int disallow_signal(int sig)
++{
++ if (sig < 1 || sig > _NSIG)
++ return -EINVAL;
+
+-#include <linux/genhd.h>
++ spin_lock_irq(&current->sigmask_lock);
++ sigaddset(&current->blocked, sig);
++ recalc_sigpending();
+
+-static inline void add_gendisk(struct gendisk *gp)
+-{
+- gp->next = gendisk_head;
+- gendisk_head = gp;
++ current->sig->action[sig-1].sa.sa_handler = SIG_DFL;
++ spin_unlock_irq(&current->sigmask_lock);
++ return 0;
+ }
+
+-static inline void del_gendisk(struct gendisk *gp)
+-{
+- struct gendisk *gd, **gdp;
++#undef sighand
++#undef sigmask_lock
+
+- for (gdp = &gendisk_head; *gdp; gdp = &((*gdp)->next))
+- if (*gdp == gp) {
+- gd = *gdp; *gdp = gd->next;
+- break;
+- }
+-}
++#define PF_FREEZE 0
++#define refrigerator(x) do { ; } while(0)
+ #endif
+
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18) && defined(MODULE)
++ /* Module bits */
+
+-#define module_init(func) \
+-mod_init_t init_module(void) { \
+- return func(); \
+-}
+
+-#define module_exit(func) \
+-mod_exit_t cleanup_module(void) { \
+- return func(); \
+-}
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,60)
++#define try_module_get(m) try_inc_mod_count(m)
++#define __module_get(m) do { if (!try_inc_mod_count(m)) BUG(); } while(0)
++#define module_put(m) do { if (m) __MOD_DEC_USE_COUNT((struct module *)(m)); } while(0)
++#define set_module_owner(x) do { x->owner = THIS_MODULE; } while(0)
+ #endif
+
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,9) || \
+- (LINUX_VERSION_CODE < KERNEL_VERSION(2,4,10) && !defined(__rh_config_h__))
+-#define MODULE_LICENSE(x) /* */
+-#endif
+
+-/* Removed for 2.4.21 kernel. This really should have been renamed
+- when it was changed -- this is a PITA */
+-#if 0 && LINUX_VERSION_CODE < KERNEL_VERSION(2,5,5)
+-#include <linux/sched.h>
+-static inline void __recalc_sigpending(void)
+-{
+- recalc_sigpending(current);
+-}
+-#undef recalc_sigpending
+-#define recalc_sigpending() __recalc_sigpending ()
+-#endif
++ /* Random filesystem stuff, only for JFFS2 really */
+
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,5)
+ #define parent_ino(d) ((d)->d_parent->d_inode->i_ino)
+ #endif
+
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,3)
+-#define need_resched() (current->need_resched)
+-#define cond_resched() do { if need_resched() schedule(); } while(0)
+-#endif
+-
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,19)
+-#ifndef yield
+-#define yield() do { set_current_state(TASK_RUNNING); schedule(); } while(0)
+-#endif
+-#ifndef minor
+-#define major(d) (MAJOR(to_kdev_t(d)))
+-#define minor(d) (MINOR(to_kdev_t(d)))
+-#endif
+-#ifndef mk_kdev
+-#define mk_kdev(ma,mi) MKDEV(ma,mi)
+-#define kdev_t_to_nr(x) (x)
+-#endif
+-#endif
+-
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
+- /* Is this right? */
+-#define set_user_nice(tsk, n) do { (tsk)->priority = 20-(n); } while(0)
+-#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,4,21) && !defined(RED_HAT_LINUX_KERNEL)
+-#define set_user_nice(tsk, n) do { (tsk)->nice = n; } while(0)
+-#endif
+-
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,21)
+-#define rq_data_dir(x) ((x)->cmd)
+-#endif
+-
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+-
+-#define IS_REQ_CMD(req) (1)
+-
+-#define QUEUE_LOCK(q) (&io_request_lock)
+-
+-#define BLK_INIT_QUEUE(q, req, lock) blk_init_queue((q), (req))
+-
+-#else /* > 2.5.0 */
+-
+-#define IS_REQ_CMD(req) ((req)->flags & REQ_CMD)
+-
+-#define QUEUE_LOCK(q) ((q)->queue_lock)
+-
+-#define BLK_INIT_QUEUE(q, req, lock) blk_init_queue((q), (req), (lock))
+-
+-#endif
+-
+-/* Removed cos it broke stuff. Where is this required anyway?
+- * #ifndef QUEUE_EMPTY
+- * #define QUEUE_EMPTY (!CURRENT)
+- * #endif
+- */
+-#if LINUX_VERSION_CODE < 0x20300
+-#define QUEUE_PLUGGED (blk_dev[MAJOR_NR].plug_tq.sync)
+-#elif LINUX_VERSION_CODE < 0x20500 //FIXME (Si)
+-#define QUEUE_PLUGGED (blk_dev[MAJOR_NR].request_queue.plugged)
+-#else
+-#define QUEUE_PLUGGED (blk_queue_plugged(QUEUE))
+-#endif
+-
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,14)
+-#define BLK_INC_USE_COUNT MOD_INC_USE_COUNT
+-#define BLK_DEC_USE_COUNT MOD_DEC_USE_COUNT
+-#else
+-#define BLK_INC_USE_COUNT do {} while(0)
+-#define BLK_DEC_USE_COUNT do {} while(0)
+-#endif
+-
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,12)
+ #define PageUptodate(x) Page_Uptodate(x)
+ #endif
+@@ -580,4 +159,31 @@
+ #define generic_file_readonly_mmap generic_file_mmap
+ #endif
+
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,70)
++
++#include <linux/kmod.h>
++#include <linux/string.h>
++
++static inline char *strlcpy(char *dest, const char *src, int len)
++{
++ dest[len-1] = 0;
++ return strncpy(dest, src, len-1);
++}
++
++static inline int do_old_request_module(const char *mod)
++{
++ return request_module(mod);
++}
++#undef request_module
++#define request_module(fmt, ...) \
++ ({ char modname[32]; snprintf(modname, 31, fmt ,##__VA_ARGS__); do_old_request_module(modname); })
++
++#endif /* 2.5.70 */
++
++#ifndef container_of
++#define container_of(ptr, type, member) ({ \
++ const typeof( ((type *)0)->member ) *__mptr = (ptr); \
++ (type *)( (char *)__mptr - offsetof(type,member) );})
++#endif
++
+ #endif /* __LINUX_MTD_COMPATMAC_H__ */
+diff -Nurb linux-mips-2.4.27/include/linux/mtd/doc2000.h linux/include/linux/mtd/doc2000.h
+--- linux-mips-2.4.27/include/linux/mtd/doc2000.h 2001-11-05 21:16:30.000000000 +0100
++++ linux/include/linux/mtd/doc2000.h 2004-11-19 10:25:12.044177752 +0100
+@@ -1,13 +1,21 @@
+-
+-/* Linux driver for Disk-On-Chip 2000 */
+-/* (c) 1999 Machine Vision Holdings, Inc. */
+-/* Author: David Woodhouse <dwmw2@mvhi.com> */
+-/* $Id$ */
++/*
++ * Linux driver for Disk-On-Chip devices
++ *
++ * Copyright (C) 1999 Machine Vision Holdings, Inc.
++ * Copyright (C) 2001-2003 David Woodhouse <dwmw2@infradead.org>
++ * Copyright (C) 2002-2003 Greg Ungerer <gerg@snapgear.com>
++ * Copyright (C) 2002-2003 SnapGear Inc
++ *
++ * $Id$
++ *
++ * Released under GPL
++ */
+
+ #ifndef __MTD_DOC2000_H__
+ #define __MTD_DOC2000_H__
+
+ #include <linux/mtd/mtd.h>
++#include <asm/semaphore.h>
+
+ #define DoC_Sig1 0
+ #define DoC_Sig2 1
+@@ -38,18 +46,47 @@
+ #define DoC_Mil_CDSN_IO 0x0800
+ #define DoC_2k_CDSN_IO 0x1800
+
++#define DoC_Mplus_NOP 0x1002
++#define DoC_Mplus_AliasResolution 0x1004
++#define DoC_Mplus_DOCControl 0x1006
++#define DoC_Mplus_AccessStatus 0x1008
++#define DoC_Mplus_DeviceSelect 0x1008
++#define DoC_Mplus_Configuration 0x100a
++#define DoC_Mplus_OutputControl 0x100c
++#define DoC_Mplus_FlashControl 0x1020
++#define DoC_Mplus_FlashSelect 0x1022
++#define DoC_Mplus_FlashCmd 0x1024
++#define DoC_Mplus_FlashAddress 0x1026
++#define DoC_Mplus_FlashData0 0x1028
++#define DoC_Mplus_FlashData1 0x1029
++#define DoC_Mplus_ReadPipeInit 0x102a
++#define DoC_Mplus_LastDataRead 0x102c
++#define DoC_Mplus_LastDataRead1 0x102d
++#define DoC_Mplus_WritePipeTerm 0x102e
++#define DoC_Mplus_ECCSyndrome0 0x1040
++#define DoC_Mplus_ECCSyndrome1 0x1041
++#define DoC_Mplus_ECCSyndrome2 0x1042
++#define DoC_Mplus_ECCSyndrome3 0x1043
++#define DoC_Mplus_ECCSyndrome4 0x1044
++#define DoC_Mplus_ECCSyndrome5 0x1045
++#define DoC_Mplus_ECCConf 0x1046
++#define DoC_Mplus_Toggle 0x1046
++#define DoC_Mplus_DownloadStatus 0x1074
++#define DoC_Mplus_CtrlConfirm 0x1076
++#define DoC_Mplus_Power 0x1fff
++
+ /* How to access the device?
+ * On ARM, it'll be mmap'd directly with 32-bit wide accesses.
+ * On PPC, it's mmap'd and 16-bit wide.
+ * Others use readb/writeb
+ */
+ #if defined(__arm__)
+-#define ReadDOC_(adr, reg) ((unsigned char)(*(__u32 *)(((unsigned long)adr)+((reg)<<2))))
+-#define WriteDOC_(d, adr, reg) do{ *(__u32 *)(((unsigned long)adr)+((reg)<<2)) = (__u32)d; wmb();} while(0)
++#define ReadDOC_(adr, reg) ((unsigned char)(*(volatile __u32 *)(((unsigned long)adr)+((reg)<<2))))
++#define WriteDOC_(d, adr, reg) do{ *(volatile __u32 *)(((unsigned long)adr)+((reg)<<2)) = (__u32)d; wmb();} while(0)
+ #define DOC_IOREMAP_LEN 0x8000
+ #elif defined(__ppc__)
+-#define ReadDOC_(adr, reg) ((unsigned char)(*(__u16 *)(((unsigned long)adr)+((reg)<<1))))
+-#define WriteDOC_(d, adr, reg) do{ *(__u16 *)(((unsigned long)adr)+((reg)<<1)) = (__u16)d; wmb();} while(0)
++#define ReadDOC_(adr, reg) ((unsigned char)(*(volatile __u16 *)(((unsigned long)adr)+((reg)<<1))))
++#define WriteDOC_(d, adr, reg) do{ *(volatile __u16 *)(((unsigned long)adr)+((reg)<<1)) = (__u16)d; wmb();} while(0)
+ #define DOC_IOREMAP_LEN 0x4000
+ #else
+ #define ReadDOC_(adr, reg) readb(((unsigned long)adr) + (reg))
+@@ -71,13 +108,21 @@
+ #define DOC_MODE_RESERVED1 2
+ #define DOC_MODE_RESERVED2 3
+
+-#define DOC_MODE_MDWREN 4
+ #define DOC_MODE_CLR_ERR 0x80
++#define DOC_MODE_RST_LAT 0x10
++#define DOC_MODE_BDECT 0x08
++#define DOC_MODE_MDWREN 0x04
+
+ #define DOC_ChipID_Doc2k 0x20
++#define DOC_ChipID_Doc2kTSOP 0x21 /* internal number for MTD */
+ #define DOC_ChipID_DocMil 0x30
++#define DOC_ChipID_DocMilPlus32 0x40
++#define DOC_ChipID_DocMilPlus16 0x41
+
+ #define CDSN_CTRL_FR_B 0x80
++#define CDSN_CTRL_FR_B0 0x40
++#define CDSN_CTRL_FR_B1 0x80
++
+ #define CDSN_CTRL_ECC_IO 0x20
+ #define CDSN_CTRL_FLASH_IO 0x10
+ #define CDSN_CTRL_WP 0x08
+@@ -93,6 +138,10 @@
+ #define DOC_ECC_RESV 0x02
+ #define DOC_ECC_IGNORE 0x01
+
++#define DOC_FLASH_CE 0x80
++#define DOC_FLASH_WP 0x40
++#define DOC_FLASH_BANK 0x02
++
+ /* We have to also set the reserved bit 1 for enable */
+ #define DOC_ECC_EN (DOC_ECC__EN | DOC_ECC_RESV)
+ #define DOC_ECC_DIS (DOC_ECC_RESV)
+@@ -107,9 +156,12 @@
+ #define MAX_FLOORS 4
+ #define MAX_CHIPS 4
+
+-#define MAX_FLOORS_MIL 4
++#define MAX_FLOORS_MIL 1
+ #define MAX_CHIPS_MIL 1
+
++#define MAX_FLOORS_MPLUS 2
++#define MAX_CHIPS_MPLUS 1
++
+ #define ADDR_COLUMN 1
+ #define ADDR_PAGE 2
+ #define ADDR_COLUMN_PAGE 3
+@@ -118,7 +170,7 @@
+ unsigned long physadr;
+ unsigned long virtadr;
+ unsigned long totlen;
+- char ChipID; /* Type of DiskOnChip */
++ unsigned char ChipID; /* Type of DiskOnChip */
+ int ioreg;
+
+ unsigned long mfr; /* Flash IDs - only one type of flash per device */
+@@ -126,6 +178,7 @@
+ int chipshift;
+ char page256;
+ char pageadrlen;
++ char interleave; /* Internal interleaving - Millennium Plus style */
+ unsigned long erasesize;
+
+ int curfloor;
+diff -Nurb linux-mips-2.4.27/include/linux/mtd/flashchip.h linux/include/linux/mtd/flashchip.h
+--- linux-mips-2.4.27/include/linux/mtd/flashchip.h 2003-02-26 01:53:51.000000000 +0100
++++ linux/include/linux/mtd/flashchip.h 2004-11-19 10:25:12.045177600 +0100
+@@ -6,7 +6,7 @@
+ *
+ * (C) 2000 Red Hat. GPLd.
+ *
+- * $Id$
++ * $Id$
+ *
+ */
+
+@@ -58,6 +58,11 @@
+ int ref_point_counter;
+ flstate_t state;
+ flstate_t oldstate;
++
++ int write_suspended:1;
++ int erase_suspended:1;
++ unsigned long in_progress_block_addr;
++
+ spinlock_t *mutex;
+ spinlock_t _spinlock; /* We do it like this because sometimes they'll be shared. */
+ wait_queue_head_t wq; /* Wait on here when we're waiting for the chip
+diff -Nurb linux-mips-2.4.27/include/linux/mtd/gen_probe.h linux/include/linux/mtd/gen_probe.h
+--- linux-mips-2.4.27/include/linux/mtd/gen_probe.h 2001-11-05 21:16:30.000000000 +0100
++++ linux/include/linux/mtd/gen_probe.h 2004-11-19 10:25:12.048177144 +0100
+@@ -1,7 +1,7 @@
+ /*
+ * (C) 2001, 2001 Red Hat, Inc.
+ * GPL'd
+- * $Id$
++ * $Id$
+ */
+
+ #ifndef __LINUX_MTD_GEN_PROBE_H__
+@@ -10,12 +10,12 @@
+ #include <linux/mtd/flashchip.h>
+ #include <linux/mtd/map.h>
+ #include <linux/mtd/cfi.h>
++#include <asm/bitops.h>
+
+ struct chip_probe {
+ char *name;
+ int (*probe_chip)(struct map_info *map, __u32 base,
+- struct flchip *chips, struct cfi_private *cfi);
+-
++ unsigned long *chip_map, struct cfi_private *cfi);
+ };
+
+ struct mtd_info *mtd_do_chip_probe(struct map_info *map, struct chip_probe *cp);
+diff -Nurb linux-mips-2.4.27/include/linux/mtd/inftl.h linux/include/linux/mtd/inftl.h
+--- linux-mips-2.4.27/include/linux/mtd/inftl.h 1970-01-01 01:00:00.000000000 +0100
++++ linux/include/linux/mtd/inftl.h 2004-11-19 10:25:12.051176688 +0100
+@@ -0,0 +1,129 @@
++/*
++ * inftl.h -- defines to support the Inverse NAND Flash Translation Layer
++ *
++ * (C) Copyright 2002, Greg Ungerer (gerg@snapgear.com)
++ *
++ * $Id$
++ */
++
++#ifndef __MTD_INFTL_H__
++#define __MTD_INFTL_H__
++
++#include <linux/mtd/blktrans.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/nftl.h>
++
++#define OSAK_VERSION 0x5120
++#define PERCENTUSED 98
++
++#define SECTORSIZE 512
++
++#ifndef INFTL_MAJOR
++#define INFTL_MAJOR 94
++#endif
++#define INFTL_PARTN_BITS 4
++
++/* Block Control Information */
++
++struct inftl_bci {
++ __u8 ECCsig[6];
++ __u8 Status;
++ __u8 Status1;
++} __attribute__((packed));
++
++struct inftl_unithead1 {
++ __u16 virtualUnitNo;
++ __u16 prevUnitNo;
++ __u8 ANAC;
++ __u8 NACs;
++ __u8 parityPerField;
++ __u8 discarded;
++} __attribute__((packed));
++
++struct inftl_unithead2 {
++ __u8 parityPerField;
++ __u8 ANAC;
++ __u16 prevUnitNo;
++ __u16 virtualUnitNo;
++ __u8 NACs;
++ __u8 discarded;
++} __attribute__((packed));
++
++struct inftl_unittail {
++ __u8 Reserved[4];
++ __u16 EraseMark;
++ __u16 EraseMark1;
++} __attribute__((packed));
++
++union inftl_uci {
++ struct inftl_unithead1 a;
++ struct inftl_unithead2 b;
++ struct inftl_unittail c;
++};
++
++struct inftl_oob {
++ struct inftl_bci b;
++ union inftl_uci u;
++};
++
++
++/* INFTL Media Header */
++
++struct INFTLPartition {
++ __u32 virtualUnits;
++ __u32 firstUnit;
++ __u32 lastUnit;
++ __u32 flags;
++ __u32 spareUnits;
++ __u32 Reserved0;
++ __u32 Reserved1;
++} __attribute__((packed));
++
++struct INFTLMediaHeader {
++ char bootRecordID[8];
++ __u32 NoOfBootImageBlocks;
++ __u32 NoOfBinaryPartitions;
++ __u32 NoOfBDTLPartitions;
++ __u32 BlockMultiplierBits;
++ __u32 FormatFlags;
++ __u32 OsakVersion;
++ __u32 PercentUsed;
++ struct INFTLPartition Partitions[4];
++} __attribute__((packed));
++
++/* Partition flag types */
++#define INFTL_BINARY 0x20000000
++#define INFTL_BDTL 0x40000000
++#define INFTL_LAST 0x80000000
++
++
++#ifdef __KERNEL__
++
++struct INFTLrecord {
++ struct mtd_blktrans_dev mbd;
++ __u16 MediaUnit, SpareMediaUnit;
++ __u32 EraseSize;
++ struct INFTLMediaHeader MediaHdr;
++ int usecount;
++ unsigned char heads;
++ unsigned char sectors;
++ unsigned short cylinders;
++ __u16 numvunits;
++ __u16 firstEUN;
++ __u16 lastEUN;
++ __u16 numfreeEUNs;
++ __u16 LastFreeEUN; /* To speed up finding a free EUN */
++ int head,sect,cyl;
++ __u16 *PUtable; /* Physical Unit Table */
++ __u16 *VUtable; /* Virtual Unit Table */
++ unsigned int nb_blocks; /* number of physical blocks */
++ unsigned int nb_boot_blocks; /* number of blocks used by the bios */
++ struct erase_info instr;
++};
++
++int INFTL_mount(struct INFTLrecord *s);
++int INFTL_formatblock(struct INFTLrecord *s, int block);
++
++#endif /* __KERNEL__ */
++
++#endif /* __MTD_INFTL_H__ */
+diff -Nurb linux-mips-2.4.27/include/linux/mtd/jedec.h linux/include/linux/mtd/jedec.h
+--- linux-mips-2.4.27/include/linux/mtd/jedec.h 2001-12-02 12:35:00.000000000 +0100
++++ linux/include/linux/mtd/jedec.h 2004-11-19 10:25:12.052176536 +0100
+@@ -7,14 +7,13 @@
+ *
+ * See the AMD flash databook for information on how to operate the interface.
+ *
+- * $Id$
++ * $Id$
+ */
+
+ #ifndef __LINUX_MTD_JEDEC_H__
+ #define __LINUX_MTD_JEDEC_H__
+
+ #include <linux/types.h>
+-#include <linux/mtd/map.h>
+
+ #define MAX_JEDEC_CHIPS 16
+
+diff -Nurb linux-mips-2.4.27/include/linux/mtd/map.h linux/include/linux/mtd/map.h
+--- linux-mips-2.4.27/include/linux/mtd/map.h 2003-02-26 01:53:51.000000000 +0100
++++ linux/include/linux/mtd/map.h 2004-11-19 10:25:12.054176232 +0100
+@@ -1,14 +1,15 @@
+
+ /* Overhauled routines for dealing with different mmap regions of flash */
+-/* $Id$ */
++/* $Id$ */
+
+ #ifndef __LINUX_MTD_MAP_H__
+ #define __LINUX_MTD_MAP_H__
+
+ #include <linux/config.h>
+ #include <linux/types.h>
+-#include <linux/mtd/mtd.h>
+-#include <linux/slab.h>
++#include <linux/list.h>
++#include <asm/system.h>
++#include <asm/io.h>
+
+ /* The map stuff is very simple. You fill in your struct map_info with
+ a handful of routines for accessing the device, making sure they handle
+@@ -29,39 +30,44 @@
+ struct map_info {
+ char *name;
+ unsigned long size;
++ unsigned long phys;
++#define NO_XIP (-1UL)
++
++ unsigned long virt;
++ void *cached;
++
+ int buswidth; /* in octets */
+- __u8 (*read8)(struct map_info *, unsigned long);
+- __u16 (*read16)(struct map_info *, unsigned long);
+- __u32 (*read32)(struct map_info *, unsigned long);
+- __u64 (*read64)(struct map_info *, unsigned long);
++
++#ifdef CONFIG_MTD_COMPLEX_MAPPINGS
++ u8 (*read8)(struct map_info *, unsigned long);
++ u16 (*read16)(struct map_info *, unsigned long);
++ u32 (*read32)(struct map_info *, unsigned long);
++ u64 (*read64)(struct map_info *, unsigned long);
+ /* If it returned a 'long' I'd call it readl.
+ * It doesn't.
+ * I won't.
+ * dwmw2 */
+
+ void (*copy_from)(struct map_info *, void *, unsigned long, ssize_t);
+- void (*write8)(struct map_info *, __u8, unsigned long);
+- void (*write16)(struct map_info *, __u16, unsigned long);
+- void (*write32)(struct map_info *, __u32, unsigned long);
+- void (*write64)(struct map_info *, __u64, unsigned long);
++ void (*write8)(struct map_info *, u8, unsigned long);
++ void (*write16)(struct map_info *, u16, unsigned long);
++ void (*write32)(struct map_info *, u32, unsigned long);
++ void (*write64)(struct map_info *, u64, unsigned long);
+ void (*copy_to)(struct map_info *, unsigned long, const void *, ssize_t);
+
+- u_char * (*point) (struct map_info *, loff_t, size_t);
+- void (*unpoint) (struct map_info *, u_char *, loff_t, size_t);
+-
++ /* We can perhaps put in 'point' and 'unpoint' methods, if we really
++ want to enable XIP for non-linear mappings. Not yet though. */
++#endif
++ /* set_vpp() must handle being reentered -- enable, enable, disable
++ must leave it enabled. */
+ void (*set_vpp)(struct map_info *, int);
+- /* We put these two here rather than a single void *map_priv,
+- because we want mappers to be able to have quickly-accessible
+- cache for the 'currently-mapped page' without the _extra_
+- redirection that would be necessary. If you need more than
+- two longs, turn the second into a pointer. dwmw2 */
++
+ unsigned long map_priv_1;
+ unsigned long map_priv_2;
+ void *fldrv_priv;
+ struct mtd_chip_driver *fldrv;
+ };
+
+-
+ struct mtd_chip_driver {
+ struct mtd_info *(*probe)(struct map_info *map);
+ void (*destroy)(struct mtd_info *);
+@@ -74,26 +80,93 @@
+ void unregister_mtd_chip_driver(struct mtd_chip_driver *);
+
+ struct mtd_info *do_map_probe(const char *name, struct map_info *map);
++void map_destroy(struct mtd_info *mtd);
++
++#define ENABLE_VPP(map) do { if(map->set_vpp) map->set_vpp(map, 1); } while(0)
++#define DISABLE_VPP(map) do { if(map->set_vpp) map->set_vpp(map, 0); } while(0)
++
++#ifdef CONFIG_MTD_COMPLEX_MAPPINGS
++#define map_read8(map, ofs) (map)->read8(map, ofs)
++#define map_read16(map, ofs) (map)->read16(map, ofs)
++#define map_read32(map, ofs) (map)->read32(map, ofs)
++#define map_read64(map, ofs) (map)->read64(map, ofs)
++#define map_copy_from(map, to, from, len) (map)->copy_from(map, to, from, len)
++#define map_write8(map, datum, ofs) (map)->write8(map, datum, ofs)
++#define map_write16(map, datum, ofs) (map)->write16(map, datum, ofs)
++#define map_write32(map, datum, ofs) (map)->write32(map, datum, ofs)
++#define map_write64(map, datum, ofs) (map)->write64(map, datum, ofs)
++#define map_copy_to(map, to, from, len) (map)->copy_to(map, to, from, len)
+
++extern void simple_map_init(struct map_info *);
++#define map_is_linear(map) (map->phys != NO_XIP)
+
+-/*
+- * Destroy an MTD device which was created for a map device.
+- * Make sure the MTD device is already unregistered before calling this
+- */
+-static inline void map_destroy(struct mtd_info *mtd)
+-{
+- struct map_info *map = mtd->priv;
+-
+- if (map->fldrv->destroy)
+- map->fldrv->destroy(mtd);
+-#ifdef CONFIG_MODULES
+- if (map->fldrv->module)
+- __MOD_DEC_USE_COUNT(map->fldrv->module);
++#else
++static inline u8 map_read8(struct map_info *map, unsigned long ofs)
++{
++ return __raw_readb(map->virt + ofs);
++}
++
++static inline u16 map_read16(struct map_info *map, unsigned long ofs)
++{
++ return __raw_readw(map->virt + ofs);
++}
++
++static inline u32 map_read32(struct map_info *map, unsigned long ofs)
++{
++ return __raw_readl(map->virt + ofs);
++}
++
++static inline u64 map_read64(struct map_info *map, unsigned long ofs)
++{
++#ifndef CONFIG_MTD_CFI_B8 /* 64-bit mappings */
++ BUG();
++ return 0;
++#else
++ return __raw_readll(map->virt + ofs);
+ #endif
+- kfree(mtd);
+ }
+
+-#define ENABLE_VPP(map) do { if(map->set_vpp) map->set_vpp(map, 1); } while(0)
+-#define DISABLE_VPP(map) do { if(map->set_vpp) map->set_vpp(map, 0); } while(0)
++static inline void map_write8(struct map_info *map, u8 datum, unsigned long ofs)
++{
++ __raw_writeb(datum, map->virt + ofs);
++ mb();
++}
++
++static inline void map_write16(struct map_info *map, u16 datum, unsigned long ofs)
++{
++ __raw_writew(datum, map->virt + ofs);
++ mb();
++}
++
++static inline void map_write32(struct map_info *map, u32 datum, unsigned long ofs)
++{
++ __raw_writel(datum, map->virt + ofs);
++ mb();
++}
++
++static inline void map_write64(struct map_info *map, u64 datum, unsigned long ofs)
++{
++#ifndef CONFIG_MTD_CFI_B8 /* 64-bit mappings */
++ BUG();
++#else
++ __raw_writell(datum, map->virt + ofs);
++ mb();
++#endif /* CFI_B8 */
++}
++
++static inline void map_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
++{
++ memcpy_fromio(to, map->virt + from, len);
++}
++
++static inline void map_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
++{
++ memcpy_toio(map->virt + to, from, len);
++}
++
++#define simple_map_init(map) do { } while (0)
++#define map_is_linear(map) (1)
++
++#endif /* !CONFIG_MTD_COMPLEX_MAPPINGS */
+
+ #endif /* __LINUX_MTD_MAP_H__ */
+diff -Nurb linux-mips-2.4.27/include/linux/mtd/mtd.h linux/include/linux/mtd/mtd.h
+--- linux-mips-2.4.27/include/linux/mtd/mtd.h 2003-08-13 19:19:29.000000000 +0200
++++ linux/include/linux/mtd/mtd.h 2004-11-19 10:25:12.055176080 +0100
+@@ -1,5 +1,10 @@
+-
+-/* $Id$ */
++/*
++ * $Id$
++ *
++ * Copyright (C) 1999-2003 David Woodhouse <dwmw2@infradead.org> et al.
++ *
++ * Released under GPL
++ */
+
+ #ifndef __MTD_MTD_H__
+ #define __MTD_MTD_H__
+@@ -9,7 +14,6 @@
+ #include <linux/config.h>
+ #include <linux/version.h>
+ #include <linux/types.h>
+-#include <linux/mtd/compatmac.h>
+ #include <linux/module.h>
+ #include <linux/uio.h>
+
+@@ -26,7 +30,6 @@
+ unsigned char *ptr;
+ };
+
+-
+ #define MTD_CHAR_MAJOR 90
+ #define MTD_BLOCK_MAJOR 31
+ #define MAX_MTD_DEVICES 16
+@@ -93,18 +96,23 @@
+ #define MEMUNLOCK _IOW('M', 6, struct erase_info_user)
+ #define MEMGETREGIONCOUNT _IOR('M', 7, int)
+ #define MEMGETREGIONINFO _IOWR('M', 8, struct region_info_user)
+-#define MEMREADDATA _IOWR('M', 9, struct mtd_oob_buf)
+-#define MEMWRITEDATA _IOWR('M', 10, struct mtd_oob_buf)
++#define MEMSETOOBSEL _IOW('M', 9, struct nand_oobinfo)
++
++struct nand_oobinfo {
++ int useecc;
++ int eccpos[6];
++};
++
+
+ #ifndef __KERNEL__
+
+ typedef struct mtd_info_user mtd_info_t;
+ typedef struct erase_info_user erase_info_t;
+ typedef struct region_info_user region_info_t;
++typedef struct nand_oobinfo nand_oobinfo_t;
+
+ /* User-space ioctl definitions */
+
+-
+ #else /* __KERNEL__ */
+
+
+@@ -150,10 +158,14 @@
+ u_int32_t ecctype;
+ u_int32_t eccsize;
+
++
+ // Kernel-only stuff starts here.
+ char *name;
+ int index;
+
++ // oobinfo is a nand_oobinfo structure, which can be set by iotcl (MEMSETOOBINFO)
++ struct nand_oobinfo oobinfo;
++
+ /* Data for variable erase regions. If numeraseregions is zero,
+ * it means that the whole device has erasesize as given above.
+ */
+@@ -163,7 +175,6 @@
+ /* This really shouldn't be here. It can go away in 2.5 */
+ u_int32_t bank_size;
+
+- struct module *module;
+ int (*erase) (struct mtd_info *mtd, struct erase_info *instr);
+
+ /* This stuff for eXecute-In-Place */
+@@ -176,8 +187,8 @@
+ int (*read) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
+ int (*write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);
+
+- int (*read_ecc) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf, u_char *eccbuf, int oobsel);
+- int (*write_ecc) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf, u_char *eccbuf, int oobsel);
++ int (*read_ecc) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf, u_char *eccbuf, struct nand_oobinfo *oobsel);
++ int (*write_ecc) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf, u_char *eccbuf, struct nand_oobinfo *oobsel);
+
+ int (*read_oob) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
+ int (*write_oob) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);
+@@ -201,10 +212,10 @@
+ */
+ int (*readv) (struct mtd_info *mtd, struct iovec *vecs, unsigned long count, loff_t from, size_t *retlen);
+ int (*readv_ecc) (struct mtd_info *mtd, struct iovec *vecs, unsigned long count, loff_t from,
+- size_t *retlen, u_char *eccbuf, int oobsel);
++ size_t *retlen, u_char *eccbuf, struct nand_oobinfo *oobsel);
+ int (*writev) (struct mtd_info *mtd, const struct iovec *vecs, unsigned long count, loff_t to, size_t *retlen);
+ int (*writev_ecc) (struct mtd_info *mtd, const struct iovec *vecs, unsigned long count, loff_t to,
+- size_t *retlen, u_char *eccbuf, int oobsel);
++ size_t *retlen, u_char *eccbuf, struct nand_oobinfo *oobsel);
+
+ /* Sync */
+ void (*sync) (struct mtd_info *mtd);
+@@ -218,6 +229,9 @@
+ void (*resume) (struct mtd_info *mtd);
+
+ void *priv;
++
++ struct module *owner;
++ int usecount;
+ };
+
+
+@@ -226,31 +240,15 @@
+ extern int add_mtd_device(struct mtd_info *mtd);
+ extern int del_mtd_device (struct mtd_info *mtd);
+
+-extern struct mtd_info *__get_mtd_device(struct mtd_info *mtd, int num);
+-
+-static inline struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num)
+-{
+- struct mtd_info *ret;
++extern struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num);
+
+- ret = __get_mtd_device(mtd, num);
+-
+- if (ret && ret->module && !try_inc_mod_count(ret->module))
+- return NULL;
+-
+- return ret;
+-}
+-
+-static inline void put_mtd_device(struct mtd_info *mtd)
+-{
+- if (mtd->module)
+- __MOD_DEC_USE_COUNT(mtd->module);
+-}
++extern void put_mtd_device(struct mtd_info *mtd);
+
+
+ struct mtd_notifier {
+ void (*add)(struct mtd_info *mtd);
+ void (*remove)(struct mtd_info *mtd);
+- struct mtd_notifier *next;
++ struct list_head list;
+ };
+
+
+@@ -263,7 +261,6 @@
+ int default_mtd_readv(struct mtd_info *mtd, struct iovec *vecs,
+ unsigned long count, loff_t from, size_t *retlen);
+
+-#ifndef MTDC
+ #define MTD_ERASE(mtd, args...) (*(mtd->erase))(mtd, args)
+ #define MTD_POINT(mtd, a,b,c,d) (*(mtd->point))(mtd, a,b,c, (u_char **)(d))
+ #define MTD_UNPOINT(mtd, arg) (*(mtd->unpoint))(mtd, (u_char *)arg)
+@@ -276,7 +273,6 @@
+ #define MTD_READOOB(mtd, args...) (*(mtd->read_oob))(mtd, args)
+ #define MTD_WRITEOOB(mtd, args...) (*(mtd->write_oob))(mtd, args)
+ #define MTD_SYNC(mtd) do { if (mtd->sync) (*(mtd->sync))(mtd); } while (0)
+-#endif /* MTDC */
+
+ /*
+ * Debugging macro and defines
+@@ -293,7 +289,8 @@
+ printk(KERN_INFO args); \
+ } while(0)
+ #else /* CONFIG_MTD_DEBUG */
+-#define DEBUG(n, args...)
++#define DEBUG(n, args...) do { } while(0)
++
+ #endif /* CONFIG_MTD_DEBUG */
+
+ #endif /* __KERNEL__ */
+diff -Nurb linux-mips-2.4.27/include/linux/mtd/nand.h linux/include/linux/mtd/nand.h
+--- linux-mips-2.4.27/include/linux/mtd/nand.h 2003-02-26 01:53:51.000000000 +0100
++++ linux/include/linux/mtd/nand.h 2004-11-19 10:25:12.057175776 +0100
+@@ -2,10 +2,10 @@
+ * linux/include/linux/mtd/nand.h
+ *
+ * Copyright (c) 2000 David Woodhouse <dwmw2@mvhi.com>
+- * Steven J. Hill <sjhill@cotw.com>
++ * Steven J. Hill <sjhill@realitydiluted.com>
+ * Thomas Gleixner <tglx@linutronix.de>
+ *
+- * $Id$
++ * $Id$
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+@@ -49,12 +49,14 @@
+ #define __LINUX_MTD_NAND_H
+
+ #include <linux/config.h>
+-#include <linux/sched.h>
++#include <linux/wait.h>
++#include <linux/spinlock.h>
+
++struct mtd_info;
+ /*
+ * Searches for a NAND device
+ */
+-extern int nand_scan (struct mtd_info *mtd);
++extern int nand_scan (struct mtd_info *mtd, int max_chips);
+
+ /*
+ * Constants for hardware specific CLE/ALE/NCE function
+@@ -65,6 +67,8 @@
+ #define NAND_CTL_CLRCLE 4
+ #define NAND_CTL_SETALE 5
+ #define NAND_CTL_CLRALE 6
++#define NAND_CTL_SETWP 7
++#define NAND_CTL_CLRWP 8
+
+ /*
+ * Standard NAND flash commands
+@@ -160,24 +164,33 @@
+ struct nand_chip {
+ unsigned long IO_ADDR_R;
+ unsigned long IO_ADDR_W;
+- void (*hwcontrol)(int cmd);
+- int (*dev_ready)(void);
++
++ u_char (*read_byte)(struct mtd_info *mtd);
++ void (*write_byte)(struct mtd_info *mtd, u_char byte);
++
++ void (*write_buf)(struct mtd_info *mtd, const u_char *buf, int len);
++ void (*read_buf)(struct mtd_info *mtd, u_char *buf, int len);
++ int (*verify_buf)(struct mtd_info *mtd, const u_char *buf, int len);
++ void (*select_chip)(struct mtd_info *mtd, int chip);
++ int (*block_bad)(struct mtd_info *mtd, unsigned long pos);
++ void (*hwcontrol)(struct mtd_info *mtd, int cmd);
++ int (*dev_ready)(struct mtd_info *mtd);
+ void (*cmdfunc)(struct mtd_info *mtd, unsigned command, int column, int page_addr);
+ int (*waitfunc)(struct mtd_info *mtd, struct nand_chip *this, int state);
+- void (*calculate_ecc)(const u_char *dat, u_char *ecc_code);
+- int (*correct_data)(u_char *dat, u_char *read_ecc, u_char *calc_ecc);
+- void (*enable_hwecc)(int mode);
++ void (*calculate_ecc)(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code);
++ int (*correct_data)(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc);
++ void (*enable_hwecc)(struct mtd_info *mtd, int mode);
+ int eccmode;
+ int eccsize;
+ int chip_delay;
++ int chipshift;
+ spinlock_t chip_lock;
+ wait_queue_head_t wq;
+ nand_state_t state;
+ int page_shift;
+ u_char *data_buf;
+ u_char *data_poi;
+- u_char *data_cache;
+- int cache_page;
++ void *priv;
+ };
+
+ /*
+@@ -241,34 +254,4 @@
+ */
+ #define NAND_BADBLOCK_POS 5
+
+-#define NAND_NONE_OOB 0
+-#define NAND_JFFS2_OOB 1
+-#define NAND_YAFFS_OOB 2
+-
+-#define NAND_NOOB_ECCPOS0 0
+-#define NAND_NOOB_ECCPOS1 1
+-#define NAND_NOOB_ECCPOS2 2
+-#define NAND_NOOB_ECCPOS3 3
+-#define NAND_NOOB_ECCPOS4 6
+-#define NAND_NOOB_ECCPOS5 7
+-
+-#define NAND_JFFS2_OOB_ECCPOS0 0
+-#define NAND_JFFS2_OOB_ECCPOS1 1
+-#define NAND_JFFS2_OOB_ECCPOS2 2
+-#define NAND_JFFS2_OOB_ECCPOS3 3
+-#define NAND_JFFS2_OOB_ECCPOS4 6
+-#define NAND_JFFS2_OOB_ECCPOS5 7
+-
+-#define NAND_YAFFS_OOB_ECCPOS0 8
+-#define NAND_YAFFS_OOB_ECCPOS1 9
+-#define NAND_YAFFS_OOB_ECCPOS2 10
+-#define NAND_YAFFS_OOB_ECCPOS3 13
+-#define NAND_YAFFS_OOB_ECCPOS4 14
+-#define NAND_YAFFS_OOB_ECCPOS5 15
+-
+-#define NAND_JFFS2_OOB8_FSDAPOS 6
+-#define NAND_JFFS2_OOB16_FSDAPOS 8
+-#define NAND_JFFS2_OOB8_FSDALEN 2
+-#define NAND_JFFS2_OOB16_FSDALEN 8
+-
+ #endif /* __LINUX_MTD_NAND_H */
+diff -Nurb linux-mips-2.4.27/include/linux/mtd/nand_ecc.h linux/include/linux/mtd/nand_ecc.h
+--- linux-mips-2.4.27/include/linux/mtd/nand_ecc.h 2001-08-22 05:25:12.000000000 +0200
++++ linux/include/linux/mtd/nand_ecc.h 2004-11-19 10:25:12.058175624 +0100
+@@ -1,9 +1,9 @@
+ /*
+ * drivers/mtd/nand_ecc.h
+ *
+- * Copyright (C) 2000 Steven J. Hill (sjhill@cotw.com)
++ * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
+ *
+- * $Id$
++ * $Id$
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+@@ -12,17 +12,19 @@
+ * This file is the header for the ECC algorithm.
+ */
+
+-/*
+- * Creates non-inverted ECC code from line parity
+- */
+-void nand_trans_result(u_char reg2, u_char reg3, u_char *ecc_code);
++#ifndef __MTD_NAND_ECC_H__
++#define __MTD_NAND_ECC_H__
++
++struct mtd_info;
+
+ /*
+ * Calculate 3 byte ECC code for 256 byte block
+ */
+-void nand_calculate_ecc (const u_char *dat, u_char *ecc_code);
++void nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code);
+
+ /*
+ * Detect and correct a 1 bit error for 256 byte block
+ */
+-int nand_correct_data (u_char *dat, u_char *read_ecc, u_char *calc_ecc);
++int nand_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc);
++
++#endif /* __MTD_NAND_ECC_H__ */
+diff -Nurb linux-mips-2.4.27/include/linux/mtd/nftl.h linux/include/linux/mtd/nftl.h
+--- linux-mips-2.4.27/include/linux/mtd/nftl.h 2003-02-26 01:53:51.000000000 +0100
++++ linux/include/linux/mtd/nftl.h 2004-11-19 10:25:12.060175320 +0100
+@@ -1,15 +1,14 @@
+-
+-/* Defines for NAND Flash Translation Layer */
+-/* (c) 1999 Machine Vision Holdings, Inc. */
+-/* Author: David Woodhouse <dwmw2@mvhi.com> */
+-/* $Id$ */
++/*
++ * $Id$
++ *
++ * (C) 1999-2003 David Woodhouse <dwmw2@infradead.org>
++ */
+
+ #ifndef __MTD_NFTL_H__
+ #define __MTD_NFTL_H__
+
+-#ifndef __BOOT__
+ #include <linux/mtd/mtd.h>
+-#endif
++#include <linux/mtd/blktrans.h>
+
+ /* Block Control Information */
+
+@@ -84,8 +83,7 @@
+ #define BLOCK_RESERVED 0xfffc /* bios block or bad block */
+
+ struct NFTLrecord {
+- struct mtd_info *mtd;
+- struct semaphore mutex;
++ struct mtd_blktrans_dev mbd;
+ __u16 MediaUnit, SpareMediaUnit;
+ __u32 EraseSize;
+ struct NFTLMediaHeader MediaHdr;
+@@ -97,7 +95,6 @@
+ __u16 lastEUN; /* should be suppressed */
+ __u16 numfreeEUNs;
+ __u16 LastFreeEUN; /* To speed up finding a free EUN */
+- __u32 nr_sects;
+ int head,sect,cyl;
+ __u16 *EUNtable; /* [numvunits]: First EUN for each virtual unit */
+ __u16 *ReplUnitTable; /* [numEUNs]: ReplUnitNumber for each */
+@@ -114,7 +111,7 @@
+ #endif
+
+ #define MAX_NFTLS 16
+-#define MAX_SECTORS_PER_UNIT 32
++#define MAX_SECTORS_PER_UNIT 64
+ #define NFTL_PARTN_BITS 4
+
+ #endif /* __KERNEL__ */
+diff -Nurb linux-mips-2.4.27/include/linux/mtd/partitions.h linux/include/linux/mtd/partitions.h
+--- linux-mips-2.4.27/include/linux/mtd/partitions.h 2002-06-27 00:36:47.000000000 +0200
++++ linux/include/linux/mtd/partitions.h 2004-11-19 10:25:12.061175168 +0100
+@@ -5,7 +5,7 @@
+ *
+ * This code is GPL
+ *
+- * $Id$
++ * $Id$
+ */
+
+ #ifndef MTD_PARTITIONS_H
+@@ -41,6 +41,7 @@
+ u_int32_t size; /* partition size */
+ u_int32_t offset; /* offset within the master MTD space */
+ u_int32_t mask_flags; /* master MTD flags to mask out for this partition */
++ struct nand_oobinfo *oobsel; /* out of band layout for this partition (NAND only)*/
+ struct mtd_info **mtdp; /* pointer to store the MTD object */
+ };
+
+@@ -49,8 +50,27 @@
+ #define MTDPART_SIZ_FULL (0)
+
+
+-int add_mtd_partitions(struct mtd_info *, struct mtd_partition *, int);
++int add_mtd_partitions(struct mtd_info *, const struct mtd_partition *, int);
+ int del_mtd_partitions(struct mtd_info *);
+
++/*
++ * Functions dealing with the various ways of partitioning the space
++ */
++
++struct mtd_part_parser {
++ struct list_head list;
++ struct module *owner;
++ const char *name;
++ int (*parse_fn)(struct mtd_info *, struct mtd_partition **, unsigned long);
++};
++
++extern struct mtd_part_parser *get_partition_parser(const char *name);
++extern int register_mtd_parser(struct mtd_part_parser *parser);
++extern int deregister_mtd_parser(struct mtd_part_parser *parser);
++extern int parse_mtd_partitions(struct mtd_info *master, const char **types,
++ struct mtd_partition **pparts, unsigned long origin);
++
++#define put_partition_parser(p) do { module_put((p)->owner); } while(0)
++
+ #endif
+
+diff -Nurb linux-mips-2.4.27/include/linux/mtd/physmap.h linux/include/linux/mtd/physmap.h
+--- linux-mips-2.4.27/include/linux/mtd/physmap.h 1970-01-01 01:00:00.000000000 +0100
++++ linux/include/linux/mtd/physmap.h 2004-11-19 10:25:12.062175016 +0100
+@@ -0,0 +1,59 @@
++/*
++ * For boards with physically mapped flash and using
++ * drivers/mtd/maps/physmap.c mapping driver.
++ *
++ * Copyright (C) 2003 MontaVista Software Inc.
++ * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version.
++ *
++ */
++
++#ifndef __LINUX_MTD_PHYSMAP__
++
++#include <linux/config.h>
++
++#if defined(CONFIG_MTD_PHYSMAP)
++
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/map.h>
++#include <linux/mtd/partitions.h>
++
++/*
++ * The map_info for physmap. Board can override size, buswidth, phys,
++ * (*set_vpp)(), etc in their initial setup routine.
++ */
++extern struct map_info physmap_map;
++
++/*
++ * Board needs to specify the exact mapping during their setup time.
++ */
++static inline void physmap_configure(unsigned long addr, unsigned long size, int buswidth, void (*set_vpp)(struct map_info *, int) )
++{
++ physmap_map.phys = addr;
++ physmap_map.size = size;
++ physmap_map.buswidth = buswidth;
++ physmap_map.set_vpp = set_vpp;
++}
++
++#if defined(CONFIG_MTD_PARTITIONS)
++
++/*
++ * Machines that wish to do flash partition may want to call this function in
++ * their setup routine.
++ *
++ * physmap_set_partitions(mypartitions, num_parts);
++ *
++ * Note that one can always override this hard-coded partition with
++ * command line partition (you need to enable CONFIG_MTD_CMDLINE_PARTS).
++ */
++void physmap_set_partitions(struct mtd_partition *parts, int num_parts);
++
++#endif /* defined(CONFIG_MTD_PARTITIONS) */
++#endif /* defined(CONFIG_MTD) */
++
++#endif /* __LINUX_MTD_PHYSMAP__ */
++
+diff -Nurb linux-mips-2.4.27/include/linux/rbtree-24.h linux/include/linux/rbtree-24.h
+--- linux-mips-2.4.27/include/linux/rbtree-24.h 1970-01-01 01:00:00.000000000 +0100
++++ linux/include/linux/rbtree-24.h 2004-11-19 10:25:12.143162704 +0100
+@@ -0,0 +1,133 @@
++/*
++ Red Black Trees
++ (C) 1999 Andrea Arcangeli <andrea@suse.de>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License as published by
++ the Free Software Foundation; either version 2 of the License, or
++ (at your option) any later version.
++
++ This program is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ GNU General Public License for more details.
++
++ You should have received a copy of the GNU General Public License
++ along with this program; if not, write to the Free Software
++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++
++ linux/include/linux/rbtree.h
++
++ To use rbtrees you'll have to implement your own insert and search cores.
++ This will avoid us to use callbacks and to drop drammatically performances.
++ I know it's not the cleaner way, but in C (not in C++) to get
++ performances and genericity...
++
++ Some example of insert and search follows here. The search is a plain
++ normal search over an ordered tree. The insert instead must be implemented
++ int two steps: as first thing the code must insert the element in
++ order as a red leaf in the tree, then the support library function
++ rb_insert_color() must be called. Such function will do the
++ not trivial work to rebalance the rbtree if necessary.
++
++-----------------------------------------------------------------------
++static inline struct page * rb_search_page_cache(struct inode * inode,
++ unsigned long offset)
++{
++ rb_node_t * n = inode->i_rb_page_cache.rb_node;
++ struct page * page;
++
++ while (n)
++ {
++ page = rb_entry(n, struct page, rb_page_cache);
++
++ if (offset < page->offset)
++ n = n->rb_left;
++ else if (offset > page->offset)
++ n = n->rb_right;
++ else
++ return page;
++ }
++ return NULL;
++}
++
++static inline struct page * __rb_insert_page_cache(struct inode * inode,
++ unsigned long offset,
++ rb_node_t * node)
++{
++ rb_node_t ** p = &inode->i_rb_page_cache.rb_node;
++ rb_node_t * parent = NULL;
++ struct page * page;
++
++ while (*p)
++ {
++ parent = *p;
++ page = rb_entry(parent, struct page, rb_page_cache);
++
++ if (offset < page->offset)
++ p = &(*p)->rb_left;
++ else if (offset > page->offset)
++ p = &(*p)->rb_right;
++ else
++ return page;
++ }
++
++ rb_link_node(node, parent, p);
++
++ return NULL;
++}
++
++static inline struct page * rb_insert_page_cache(struct inode * inode,
++ unsigned long offset,
++ rb_node_t * node)
++{
++ struct page * ret;
++ if ((ret = __rb_insert_page_cache(inode, offset, node)))
++ goto out;
++ rb_insert_color(node, &inode->i_rb_page_cache);
++ out:
++ return ret;
++}
++-----------------------------------------------------------------------
++*/
++
++#ifndef _LINUX_RBTREE_H
++#define _LINUX_RBTREE_H
++
++#include <linux/kernel.h>
++#include <linux/stddef.h>
++
++typedef struct rb_node_s
++{
++ struct rb_node_s * rb_parent;
++ int rb_color;
++#define RB_RED 0
++#define RB_BLACK 1
++ struct rb_node_s * rb_right;
++ struct rb_node_s * rb_left;
++}
++rb_node_t;
++
++typedef struct rb_root_s
++{
++ struct rb_node_s * rb_node;
++}
++rb_root_t;
++
++#define RB_ROOT (rb_root_t) { NULL, }
++#define rb_entry(ptr, type, member) \
++ ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
++
++extern void rb_insert_color(rb_node_t *, rb_root_t *);
++extern void rb_erase(rb_node_t *, rb_root_t *);
++
++static inline void rb_link_node(rb_node_t * node, rb_node_t * parent, rb_node_t ** rb_link)
++{
++ node->rb_parent = parent;
++ node->rb_color = RB_RED;
++ node->rb_left = node->rb_right = NULL;
++
++ *rb_link = node;
++}
++
++#endif /* _LINUX_RBTREE_H */
+diff -Nurb linux-mips-2.4.27/include/linux/rbtree.h linux/include/linux/rbtree.h
+--- linux-mips-2.4.27/include/linux/rbtree.h 2001-10-19 03:25:03.000000000 +0200
++++ linux/include/linux/rbtree.h 2004-11-19 10:25:12.148161944 +0100
+@@ -1,133 +1,25 @@
+ /*
+- Red Black Trees
+- (C) 1999 Andrea Arcangeli <andrea@suse.de>
++ * 2.5 compatibility
++ * $Id$
++ */
++
++#ifndef __MTD_COMPAT_RBTREE_H__
++#define __MTD_COMPAT_RBTREE_H__
++
++#include <linux/version.h>
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,40)
++#include_next <linux/rbtree.h>
++#else
++#define rb_node_s rb_node
++#define rb_root_s rb_root
++
++#include <linux/rbtree-24.h>
++
++/* Find logical next and previous nodes in a tree */
++extern struct rb_node *rb_next(struct rb_node *);
++extern struct rb_node *rb_prev(struct rb_node *);
++extern struct rb_node *rb_first(struct rb_root *);
++#endif
+
+- This program is free software; you can redistribute it and/or modify
+- it under the terms of the GNU General Public License as published by
+- the Free Software Foundation; either version 2 of the License, or
+- (at your option) any later version.
+-
+- This program is distributed in the hope that it will be useful,
+- but WITHOUT ANY WARRANTY; without even the implied warranty of
+- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+- GNU General Public License for more details.
+-
+- You should have received a copy of the GNU General Public License
+- along with this program; if not, write to the Free Software
+- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+-
+- linux/include/linux/rbtree.h
+-
+- To use rbtrees you'll have to implement your own insert and search cores.
+- This will avoid us to use callbacks and to drop drammatically performances.
+- I know it's not the cleaner way, but in C (not in C++) to get
+- performances and genericity...
+-
+- Some example of insert and search follows here. The search is a plain
+- normal search over an ordered tree. The insert instead must be implemented
+- int two steps: as first thing the code must insert the element in
+- order as a red leaf in the tree, then the support library function
+- rb_insert_color() must be called. Such function will do the
+- not trivial work to rebalance the rbtree if necessary.
+-
+------------------------------------------------------------------------
+-static inline struct page * rb_search_page_cache(struct inode * inode,
+- unsigned long offset)
+-{
+- rb_node_t * n = inode->i_rb_page_cache.rb_node;
+- struct page * page;
+-
+- while (n)
+- {
+- page = rb_entry(n, struct page, rb_page_cache);
+-
+- if (offset < page->offset)
+- n = n->rb_left;
+- else if (offset > page->offset)
+- n = n->rb_right;
+- else
+- return page;
+- }
+- return NULL;
+-}
+-
+-static inline struct page * __rb_insert_page_cache(struct inode * inode,
+- unsigned long offset,
+- rb_node_t * node)
+-{
+- rb_node_t ** p = &inode->i_rb_page_cache.rb_node;
+- rb_node_t * parent = NULL;
+- struct page * page;
+-
+- while (*p)
+- {
+- parent = *p;
+- page = rb_entry(parent, struct page, rb_page_cache);
+-
+- if (offset < page->offset)
+- p = &(*p)->rb_left;
+- else if (offset > page->offset)
+- p = &(*p)->rb_right;
+- else
+- return page;
+- }
+-
+- rb_link_node(node, parent, p);
+-
+- return NULL;
+-}
+-
+-static inline struct page * rb_insert_page_cache(struct inode * inode,
+- unsigned long offset,
+- rb_node_t * node)
+-{
+- struct page * ret;
+- if ((ret = __rb_insert_page_cache(inode, offset, node)))
+- goto out;
+- rb_insert_color(node, &inode->i_rb_page_cache);
+- out:
+- return ret;
+-}
+------------------------------------------------------------------------
+-*/
+-
+-#ifndef _LINUX_RBTREE_H
+-#define _LINUX_RBTREE_H
+-
+-#include <linux/kernel.h>
+-#include <linux/stddef.h>
+-
+-typedef struct rb_node_s
+-{
+- struct rb_node_s * rb_parent;
+- int rb_color;
+-#define RB_RED 0
+-#define RB_BLACK 1
+- struct rb_node_s * rb_right;
+- struct rb_node_s * rb_left;
+-}
+-rb_node_t;
+-
+-typedef struct rb_root_s
+-{
+- struct rb_node_s * rb_node;
+-}
+-rb_root_t;
+-
+-#define RB_ROOT (rb_root_t) { NULL, }
+-#define rb_entry(ptr, type, member) \
+- ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
+-
+-extern void rb_insert_color(rb_node_t *, rb_root_t *);
+-extern void rb_erase(rb_node_t *, rb_root_t *);
+-
+-static inline void rb_link_node(rb_node_t * node, rb_node_t * parent, rb_node_t ** rb_link)
+-{
+- node->rb_parent = parent;
+- node->rb_color = RB_RED;
+- node->rb_left = node->rb_right = NULL;
+-
+- *rb_link = node;
+-}
+-
+-#endif /* _LINUX_RBTREE_H */
++#endif /* __MTD_COMPAT_RBTREE_H__ */
+diff -Nurb linux-mips-2.4.27/include/linux/suspend.h linux/include/linux/suspend.h
+--- linux-mips-2.4.27/include/linux/suspend.h 1970-01-01 01:00:00.000000000 +0100
++++ linux/include/linux/suspend.h 2004-11-19 10:25:12.150161640 +0100
+@@ -0,0 +1,10 @@
++/* $Id$ */
++
++#ifndef __MTD_COMPAT_VERSION_H__
++#include <linux/version.h>
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
++#include_next <linux/suspend.h>
++#endif
++
++#endif /* __MTD_COMPAT_VERSION_H__ */
+diff -Nurb linux-mips-2.4.27/include/linux/workqueue.h linux/include/linux/workqueue.h
+--- linux-mips-2.4.27/include/linux/workqueue.h 1970-01-01 01:00:00.000000000 +0100
++++ linux/include/linux/workqueue.h 2004-11-19 10:25:12.152161336 +0100
+@@ -0,0 +1,21 @@
++/*
++ * 2.5 compatibility
++ * $Id$
++ */
++
++#ifndef __MTD_COMPAT_WORKQUEUE_H__
++#define __MTD_COMPAT_WORKQUEUE_H__
++
++#include <linux/version.h>
++
++#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,40)
++#include_next <linux/workqueue.h>
++#else
++#include <linux/tqueue.h>
++#define work_struct tq_struct
++#define schedule_work(x) schedule_task(x)
++#define flush_scheduled_work flush_scheduled_tasks
++#define INIT_WORK(x,y,z) INIT_TQUEUE(x,y,z)
++#endif
++
++#endif /* __MTD_COMPAT_WORKQUEUE_H__ */
diff --git a/linux/linux-mtx-1-2.4.27/02-mtd-mtx-1-map.diff b/linux/linux-mtx-1-2.4.27/02-mtd-mtx-1-map.diff
index e69de29bb2..ba24bb1381 100644
--- a/linux/linux-mtx-1-2.4.27/02-mtd-mtx-1-map.diff
+++ b/linux/linux-mtx-1-2.4.27/02-mtd-mtx-1-map.diff
@@ -0,0 +1,248 @@
+diff -Nurb linux/drivers/mtd/maps/Config.in linux-mips-2.4.24-pre2+mtd-2004-01-27+mtx-map/drivers/mtd/maps/Config.in
+--- linux/drivers/mtd/maps/Config.in 2004-11-17 18:17:59.049312400 +0100
++++ linux-mips-2.4.24-pre2+mtd-2004-01-27+mtx-map/drivers/mtd/maps/Config.in 2004-11-17 18:12:26.000000000 +0100
+@@ -80,6 +80,7 @@
+ bool ' Db1x00 boot flash device' CONFIG_MTD_DB1X00_BOOT
+ bool ' Db1x00 user flash device (2nd bank)' CONFIG_MTD_DB1X00_USER
+ fi
++ dep_tristate ' MTX-1 flash device' CONFIG_MTD_MTX1 $CONFIG_MIPS_MTX1
+ dep_tristate ' Flash chip mapping on ITE QED-4N-S01B, Globespan IVR or custom board' CONFIG_MTD_CSTM_MIPS_IXX $CONFIG_MTD_CFI $CONFIG_MTD_JEDEC $CONFIG_MTD_PARTITIONS
+ if [ "$CONFIG_MTD_CSTM_MIPS_IXX" = "y" -o "$CONFIG_MTD_CSTM_MIPS_IXX" = "m" ]; then
+ hex ' Physical start address of flash mapping' CONFIG_MTD_CSTM_MIPS_IXX_START 0x8000000
+diff -Nurb linux/drivers/mtd/maps/Makefile linux-mips-2.4.24-pre2+mtd-2004-01-27+mtx-map/drivers/mtd/maps/Makefile
+--- linux/drivers/mtd/maps/Makefile 2004-11-17 18:17:59.051312096 +0100
++++ linux-mips-2.4.24-pre2+mtd-2004-01-27+mtx-map/drivers/mtd/maps/Makefile 2004-11-17 18:12:26.000000000 +0100
+@@ -49,6 +49,7 @@
+ obj-$(CONFIG_MTD_PCI) += pci.o
+ obj-$(CONFIG_MTD_PB1XXX) += pb1xxx-flash.o
+ obj-$(CONFIG_MTD_DB1X00) += db1x00-flash.o
++obj-$(CONFIG_MTD_MTX1) += mtx-1.o
+ obj-$(CONFIG_MTD_LASAT) += lasat.o
+ obj-$(CONFIG_MTD_AUTCPU12) += autcpu12-nvram.o
+ obj-$(CONFIG_MTD_EDB7312) += edb7312.o
+diff -Nurb linux/drivers/mtd/maps/mtx-1.c linux-mips-2.4.24-pre2+mtd-2004-01-27+mtx-map/drivers/mtd/maps/mtx-1.c
+--- linux/drivers/mtd/maps/mtx-1.c 2004-11-17 18:17:02.689880336 +0100
++++ linux-mips-2.4.24-pre2+mtd-2004-01-27+mtx-map/drivers/mtd/maps/mtx-1.c 2004-11-17 18:12:26.000000000 +0100
+@@ -1,166 +1,78 @@
+ /*
+ * Flash memory access on 4G Systems MTX-1 board
+ *
+- * (C) 2003 Pete Popov <ppopov@mvista.com>
+- * Bruno Randolf <bruno.randolf@4g-systems.de>
++ * (C) Bruno Randolf (4G Systeme GmbH) <bruno.randolf@4g-systems.biz>
+ */
+
+ #include <linux/config.h>
+ #include <linux/module.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
++#include <linux/init.h>
+
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+ #include <linux/mtd/partitions.h>
+
+ #include <asm/io.h>
+-#include <asm/au1000.h>
+
+-#ifdef DEBUG_RW
+-#define DBG(x...) printk(x)
+-#else
+-#define DBG(x...)
+-#endif
+-
+-#ifdef CONFIG_MIPS_MTX1
+ #define WINDOW_ADDR 0x1E000000
+ #define WINDOW_SIZE 0x2000000
+-#endif
+-
+-__u8 physmap_read8(struct map_info *map, unsigned long ofs)
+-{
+- __u8 ret;
+- ret = __raw_readb(map->map_priv_1 + ofs);
+- DBG("read8 from %x, %x\n", (unsigned)(map->map_priv_1 + ofs), ret);
+- return ret;
+-}
+-
+-__u16 physmap_read16(struct map_info *map, unsigned long ofs)
+-{
+- __u16 ret;
+- ret = __raw_readw(map->map_priv_1 + ofs);
+- DBG("read16 from %x, %x\n", (unsigned)(map->map_priv_1 + ofs), ret);
+- return ret;
+-}
+-
+-__u32 physmap_read32(struct map_info *map, unsigned long ofs)
+-{
+- __u32 ret;
+- ret = __raw_readl(map->map_priv_1 + ofs);
+- DBG("read32 from %x, %x\n", (unsigned)(map->map_priv_1 + ofs), ret);
+- return ret;
+-}
+-
+-void physmap_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+-{
+- DBG("physmap_copy from %x to %x\n", (unsigned)from, (unsigned)to);
+- memcpy_fromio(to, map->map_priv_1 + from, len);
+-}
+-
+-void physmap_write8(struct map_info *map, __u8 d, unsigned long adr)
+-{
+- DBG("write8 at %x, %x\n", (unsigned)(map->map_priv_1 + adr), d);
+- __raw_writeb(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-void physmap_write16(struct map_info *map, __u16 d, unsigned long adr)
+-{
+- DBG("write16 at %x, %x\n", (unsigned)(map->map_priv_1 + adr), d);
+- __raw_writew(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-void physmap_write32(struct map_info *map, __u32 d, unsigned long adr)
+-{
+- DBG("write32 at %x, %x\n", (unsigned)(map->map_priv_1 + adr), d);
+- __raw_writel(d, map->map_priv_1 + adr);
+- mb();
+-}
+-
+-void physmap_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+-{
+- DBG("physmap_copy_to %x from %x\n", (unsigned)to, (unsigned)from);
+- memcpy_toio(map->map_priv_1 + to, from, len);
+-}
+-
+-
++#define BUSWIDTH 4
+
+ static struct map_info mtx1_map = {
+- name: "MTX-1 flash",
+- read8: physmap_read8,
+- read16: physmap_read16,
+- read32: physmap_read32,
+- copy_from: physmap_copy_from,
+- write8: physmap_write8,
+- write16: physmap_write16,
+- write32: physmap_write32,
+- copy_to: physmap_copy_to,
++ .name = "MTX-1 flash",
++ .size = WINDOW_SIZE,
++ .buswidth = BUSWIDTH,
++ .phys = WINDOW_ADDR
+ };
+
+-
+-static unsigned long flash_size = 0x01000000;
+-static unsigned char flash_buswidth = 4;
+ static struct mtd_partition mtx1_partitions[] = {
+ {
+- name: "user fs",
+- size: 0x1c00000,
+- offset: 0,
++ .name = "user fs",
++ .size = 0x1c00000,
++ .offset = 0,
+ },{
+- name: "yamon",
+- size: 0x0100000,
+- offset: MTDPART_OFS_APPEND,
+- mask_flags: MTD_WRITEABLE
++ .name = "yamon",
++ .size = 0x0100000,
++ .offset = MTDPART_OFS_APPEND,
++ .mask_flags = MTD_WRITEABLE /* force read-only */
+ },{
+- name: "raw kernel",
+- size: 0x02c0000,
+- offset: MTDPART_OFS_APPEND,
++ .name = "raw kernel",
++ .size = 0x02c0000,
++ .offset = MTDPART_OFS_APPEND,
+ },{
+- name: "yamon env vars",
+- size: 0x0040000,
+- offset: MTDPART_OFS_APPEND,
+- mask_flags: MTD_WRITEABLE
++ .name = "yamon environment",
++ .size = 0x0040000,
++ .offset = MTDPART_OFS_APPEND,
++ .mask_flags = MTD_WRITEABLE /* force read-only */
+ }
+ };
+
+-
+-#define NB_OF(x) (sizeof(x)/sizeof(x[0]))
+-
+-static struct mtd_partition *parsed_parts;
+ static struct mtd_info *mymtd;
+
+ int __init mtx1_mtd_init(void)
+ {
+- struct mtd_partition *parts;
+- int nb_parts = 0;
+- char *part_type;
++ printk(KERN_NOTICE "MTX-1 flash: probing %d-bit flash bus at %x\n",
++ mtx1_map.buswidth*8, WINDOW_ADDR);
+
+- /* Default flash buswidth */
+- mtx1_map.buswidth = flash_buswidth;
++ mtx1_map.virt = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE);
++ if (!mtx1_map.virt) {
++ printk("mtx_mtd_init: failed to ioremap\n");
++ return -EIO;
++ }
++
++ simple_map_init(&mtx1_map);
+
+- /*
+- * Static partition definition selection
+- */
+- part_type = "static";
+- parts = mtx1_partitions;
+- nb_parts = NB_OF(mtx1_partitions);
+- mtx1_map.size = flash_size;
+-
+- /*
+- * Now let's probe for the actual flash. Do it here since
+- * specific machine settings might have been set above.
+- */
+- printk(KERN_NOTICE "MTX-1 flash: probing %d-bit flash bus\n",
+- mtx1_map.buswidth*8);
+- mtx1_map.map_priv_1 =
+- (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE);
+ mymtd = do_map_probe("cfi_probe", &mtx1_map);
+- if (!mymtd) return -ENXIO;
+- mymtd->module = THIS_MODULE;
++ if (!mymtd) {
++ iounmap(mtx1_map.virt);
++ return -ENXIO;
++ }
++
++ mymtd->owner = THIS_MODULE;
+
+- add_mtd_partitions(mymtd, parts, nb_parts);
+- return 0;
++ return add_mtd_partitions(mymtd, mtx1_partitions, ARRAY_SIZE(mtx1_partitions));
+ }
+
+ static void __exit mtx1_mtd_cleanup(void)
+@@ -168,14 +80,14 @@
+ if (mymtd) {
+ del_mtd_partitions(mymtd);
+ map_destroy(mymtd);
+- if (parsed_parts)
+- kfree(parsed_parts);
+ }
++ if (mtx1_map.virt)
++ iounmap(mtx1_map.virt);
+ }
+
+ module_init(mtx1_mtd_init);
+ module_exit(mtx1_mtd_cleanup);
+
+-MODULE_AUTHOR("Pete Popov");
++MODULE_AUTHOR("Bruno Randolf <bruno.randolf@4g-systems.biz>");
+ MODULE_DESCRIPTION("MTX-1 CFI map driver");
+ MODULE_LICENSE("GPL");
+
diff --git a/linux/linux-mtx-1-2.4.27/03-mtd-erase-compiler-bug.diff b/linux/linux-mtx-1-2.4.27/03-mtd-erase-compiler-bug.diff
index e69de29bb2..9e310bf327 100644
--- a/linux/linux-mtx-1-2.4.27/03-mtd-erase-compiler-bug.diff
+++ b/linux/linux-mtx-1-2.4.27/03-mtd-erase-compiler-bug.diff
@@ -0,0 +1,21 @@
+--- linux-mips-2.4.24-pre2+mtd-2004-01-27/fs/jffs2/erase.c 2004-11-17 18:17:59.000000000 +0100
++++ linux/fs/jffs2/erase.c 2004-11-17 18:44:52.260067088 +0100
+@@ -365,11 +365,13 @@
+ jeb->dirty_size = 0;
+ jeb->wasted_size = 0;
+ } else {
+- struct jffs2_unknown_node marker = {
+- .magic = cpu_to_je16(JFFS2_MAGIC_BITMASK),
+- .nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER),
+- .totlen = cpu_to_je32(c->cleanmarker_size)
+- };
++ /* compiler workaround, structure was not initialized before
++ on mipsel cross compilers
++ fix by Eugene.Wisor@flukenetworks.com */
++ struct jffs2_unknown_node marker;
++ marker.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
++ marker.nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER);
++ marker.totlen = cpu_to_je32(c->cleanmarker_size);
+
+ marker.hdr_crc = cpu_to_je32(crc32(0, &marker, sizeof(struct jffs2_unknown_node)-4));
+
diff --git a/linux/linux-mtx-1-2.4.27/04-mtx-1-board-reset.diff b/linux/linux-mtx-1-2.4.27/04-mtx-1-board-reset.diff
index e69de29bb2..9a13c2a403 100644
--- a/linux/linux-mtx-1-2.4.27/04-mtx-1-board-reset.diff
+++ b/linux/linux-mtx-1-2.4.27/04-mtx-1-board-reset.diff
@@ -0,0 +1,15 @@
+--- linux/arch/mips/au1000/mtx-1/board_setup.c.orig 2004-10-13 19:05:15.340583632 +0200
++++ linux/arch/mips/au1000/mtx-1/board_setup.c 2004-10-13 19:01:03.402883984 +0200
+@@ -48,6 +48,12 @@
+
+ extern struct rtc_ops no_rtc_ops;
+
++void board_reset (void)
++{
++ /* Hit BCSR.SYSTEM_CONTROL[SW_RST] */
++ au_writel(0x00000000, 0xAE00001C);
++}
++
+ void __init board_setup(void)
+ {
+ rtc_ops = &no_rtc_ops;
diff --git a/linux/linux-mtx-1-2.4.27/05-mtx-1-pci-irq.diff b/linux/linux-mtx-1-2.4.27/05-mtx-1-pci-irq.diff
index e69de29bb2..4fc6b9e667 100644
--- a/linux/linux-mtx-1-2.4.27/05-mtx-1-pci-irq.diff
+++ b/linux/linux-mtx-1-2.4.27/05-mtx-1-pci-irq.diff
@@ -0,0 +1,18 @@
+diff -Nurb linux-mips-2.4.27/arch/mips/au1000/mtx-1/irqmap.c linux/arch/mips/au1000/mtx-1/irqmap.c
+--- linux-mips-2.4.27/arch/mips/au1000/mtx-1/irqmap.c 2004-04-02 11:04:00.000000000 +0200
++++ linux/arch/mips/au1000/mtx-1/irqmap.c 2004-11-22 14:15:56.000000000 +0100
+@@ -72,10 +72,10 @@
+ * A B C D
+ */
+ {
+- {INTA, INTB, INTC, INTD}, /* IDSEL 0 */
+- {INTA, INTB, INTC, INTD}, /* IDSEL 1 */
+- {INTA, INTB, INTC, INTD}, /* IDSEL 2 */
+- {INTA, INTB, INTC, INTD}, /* IDSEL 3 */
++ {INTA, INTB, INTX, INTX}, /* IDSEL 0 */
++ {INTB, INTA, INTX, INTX}, /* IDSEL 1 */
++ {INTC, INTD, INTX, INTX}, /* IDSEL 2 */
++ {INTD, INTC, INTX, INTX}, /* IDSEL 3 */
+ };
+ const long min_idsel = 0, max_idsel = 3, irqs_per_slot = 4;
+ return PCI_IRQ_TABLE_LOOKUP;
diff --git a/linux/linux-mtx-1-2.4.27/06-zboot-2.4.26.patch b/linux/linux-mtx-1-2.4.27/06-zboot-2.4.26.patch
index e69de29bb2..573aa7f0cf 100644
--- a/linux/linux-mtx-1-2.4.27/06-zboot-2.4.26.patch
+++ b/linux/linux-mtx-1-2.4.27/06-zboot-2.4.26.patch
@@ -0,0 +1,5308 @@
+diff -Naru linux/arch/mips/Makefile linux.spi/arch/mips/Makefile
+--- linux/arch/mips/Makefile 2004-05-06 15:23:41.000000000 -0400
++++ linux.spi/arch/mips/Makefile 2004-05-11 23:19:24.000000000 -0400
+@@ -29,6 +29,8 @@
+ endif
+
+ MAKEBOOT = $(MAKE) -C arch/$(ARCH)/boot
++MAKEZBOOT = $(MAKE) -C arch/$(ARCH)/zboot
++BOOT_TARGETS = zImage zImage.initrd zImage.flash
+
+ check_gcc = $(shell if $(CC) $(1) -S -o /dev/null -xc /dev/null > /dev/null 2>&1; then echo "$(1)"; else echo "$(2)"; fi)
+
+@@ -757,12 +749,16 @@
+
+ vmlinux.ecoff: vmlinux
+ @$(MAKEBOOT) $@
++
++$(BOOT_TARGETS): vmlinux
++ @$(MAKEZBOOT) $@
+
+ vmlinux.srec: vmlinux
+ @$(MAKEBOOT) $@
+
+ archclean:
+ @$(MAKEBOOT) clean
++ @$(MAKEZBOOT) clean
+ rm -f arch/$(ARCH)/ld.script
+ $(MAKE) -C arch/$(ARCH)/tools clean
+ $(MAKE) -C arch/mips/baget clean
+diff -Naru linux/arch/mips/zboot/common/au1k_uart.c linux.spi/arch/mips/zboot/common/au1k_uart.c
+--- linux/arch/mips/zboot/common/au1k_uart.c 1969-12-31 19:00:00.000000000 -0500
++++ linux.spi/arch/mips/zboot/common/au1k_uart.c 2004-05-11 23:19:24.000000000 -0400
+@@ -0,0 +1,103 @@
++/*
++ * BRIEF MODULE DESCRIPTION
++ * Simple Au1000 uart routines.
++ *
++ * Copyright 2001 MontaVista Software Inc.
++ * Author: MontaVista Software, Inc.
++ * ppopov@mvista.com or source@mvista.com
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version.
++ *
++ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
++ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
++ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
++ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
++ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
++ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
++ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
++ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
++ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++ *
++ * You should have received a copy of the GNU General Public License along
++ * with this program; if not, write to the Free Software Foundation, Inc.,
++ * 675 Mass Ave, Cambridge, MA 02139, USA.
++ */
++#include <linux/config.h>
++#include <asm/io.h>
++#include <asm/au1000.h>
++#include "ns16550.h"
++
++typedef unsigned char uint8;
++typedef unsigned int uint32;
++
++#define UART16550_BAUD_2400 2400
++#define UART16550_BAUD_4800 4800
++#define UART16550_BAUD_9600 9600
++#define UART16550_BAUD_19200 19200
++#define UART16550_BAUD_38400 38400
++#define UART16550_BAUD_57600 57600
++#define UART16550_BAUD_115200 115200
++
++#define UART16550_PARITY_NONE 0
++#define UART16550_PARITY_ODD 0x08
++#define UART16550_PARITY_EVEN 0x18
++#define UART16550_PARITY_MARK 0x28
++#define UART16550_PARITY_SPACE 0x38
++
++#define UART16550_DATA_5BIT 0x0
++#define UART16550_DATA_6BIT 0x1
++#define UART16550_DATA_7BIT 0x2
++#define UART16550_DATA_8BIT 0x3
++
++#define UART16550_STOP_1BIT 0x0
++#define UART16550_STOP_2BIT 0x4
++
++/* It would be nice if we had a better way to do this.
++ * It could be a variable defined in one of the board specific files.
++ */
++#undef UART_BASE
++#ifdef CONFIG_COGENT_CSB250
++#define UART_BASE UART3_ADDR
++#else
++#define UART_BASE UART0_ADDR
++#endif
++
++/* memory-mapped read/write of the port */
++#define UART16550_READ(y) (readl(UART_BASE + y) & 0xff)
++#define UART16550_WRITE(y,z) (writel(z&0xff, UART_BASE + y))
++
++/*
++ * We use uart 0, which is already initialized by
++ * yamon.
++ */
++volatile struct NS16550 *
++serial_init(int chan)
++{
++ volatile struct NS16550 *com_port;
++ com_port = (struct NS16550 *) UART_BASE;
++ return (com_port);
++}
++
++void
++serial_putc(volatile struct NS16550 *com_port, unsigned char c)
++{
++ while ((UART16550_READ(UART_LSR)&0x40) == 0);
++ UART16550_WRITE(UART_TX, c);
++}
++
++unsigned char
++serial_getc(volatile struct NS16550 *com_port)
++{
++ while((UART16550_READ(UART_LSR) & 0x1) == 0);
++ return UART16550_READ(UART_RX);
++}
++
++int
++serial_tstc(volatile struct NS16550 *com_port)
++{
++ return((UART16550_READ(UART_LSR) & LSR_DR) != 0);
++}
+diff -Naru linux/arch/mips/zboot/common/ctype.c linux.spi/arch/mips/zboot/common/ctype.c
+--- linux/arch/mips/zboot/common/ctype.c 1969-12-31 19:00:00.000000000 -0500
++++ linux.spi/arch/mips/zboot/common/ctype.c 2004-05-11 23:19:24.000000000 -0400
+@@ -0,0 +1,35 @@
++/*
++ * linux/lib/ctype.c
++ *
++ * Copyright (C) 1991, 1992 Linus Torvalds
++ */
++
++#include <linux/ctype.h>
++
++unsigned char _ctype[] = {
++_C,_C,_C,_C,_C,_C,_C,_C, /* 0-7 */
++_C,_C|_S,_C|_S,_C|_S,_C|_S,_C|_S,_C,_C, /* 8-15 */
++_C,_C,_C,_C,_C,_C,_C,_C, /* 16-23 */
++_C,_C,_C,_C,_C,_C,_C,_C, /* 24-31 */
++_S|_SP,_P,_P,_P,_P,_P,_P,_P, /* 32-39 */
++_P,_P,_P,_P,_P,_P,_P,_P, /* 40-47 */
++_D,_D,_D,_D,_D,_D,_D,_D, /* 48-55 */
++_D,_D,_P,_P,_P,_P,_P,_P, /* 56-63 */
++_P,_U|_X,_U|_X,_U|_X,_U|_X,_U|_X,_U|_X,_U, /* 64-71 */
++_U,_U,_U,_U,_U,_U,_U,_U, /* 72-79 */
++_U,_U,_U,_U,_U,_U,_U,_U, /* 80-87 */
++_U,_U,_U,_P,_P,_P,_P,_P, /* 88-95 */
++_P,_L|_X,_L|_X,_L|_X,_L|_X,_L|_X,_L|_X,_L, /* 96-103 */
++_L,_L,_L,_L,_L,_L,_L,_L, /* 104-111 */
++_L,_L,_L,_L,_L,_L,_L,_L, /* 112-119 */
++_L,_L,_L,_P,_P,_P,_P,_C, /* 120-127 */
++0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 128-143 */
++0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 144-159 */
++_S|_SP,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P, /* 160-175 */
++_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P, /* 176-191 */
++_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U, /* 192-207 */
++_U,_U,_U,_U,_U,_U,_U,_P,_U,_U,_U,_U,_U,_U,_U,_L, /* 208-223 */
++_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L, /* 224-239 */
++_L,_L,_L,_L,_L,_L,_L,_P,_L,_L,_L,_L,_L,_L,_L,_L}; /* 240-255 */
++
++
+diff -Naru linux/arch/mips/zboot/common/dummy.c linux.spi/arch/mips/zboot/common/dummy.c
+--- linux/arch/mips/zboot/common/dummy.c 1969-12-31 19:00:00.000000000 -0500
++++ linux.spi/arch/mips/zboot/common/dummy.c 2004-05-11 23:19:24.000000000 -0400
+@@ -0,0 +1,4 @@
++int main(void)
++{
++ return 0;
++}
+diff -Naru linux/arch/mips/zboot/common/Makefile linux.spi/arch/mips/zboot/common/Makefile
+--- linux/arch/mips/zboot/common/Makefile 1969-12-31 19:00:00.000000000 -0500
++++ linux.spi/arch/mips/zboot/common/Makefile 2004-05-11 23:19:24.000000000 -0400
+@@ -0,0 +1,27 @@
++#
++# arch/mips/zboot/common/Makefile
++#
++# This file is subject to the terms and conditions of the GNU General Public
++# License. See the file "COPYING" in the main directory of this archive
++# for more details.
++#
++# Tom Rini January 2001
++#
++
++.c.s:
++ $(CC) $(CFLAGS) -S -o $*.s $<
++.s.o:
++ $(AS) -o $*.o $<
++.c.o:
++ $(CC) $(CFLAGS) -c -o $*.o $<
++.S.s:
++ $(CPP) $(AFLAGS) -o $*.o $<
++.S.o:
++ $(CC) $(AFLAGS) -c -o $*.o $<
++
++clean:
++ rm -rf *.o
++
++OBJCOPY_ARGS = -O elf32-tradlittlemips
++
++include $(TOPDIR)/Rules.make
+diff -Naru linux/arch/mips/zboot/common/misc-common.c linux.spi/arch/mips/zboot/common/misc-common.c
+--- linux/arch/mips/zboot/common/misc-common.c 1969-12-31 19:00:00.000000000 -0500
++++ linux.spi/arch/mips/zboot/common/misc-common.c 2004-05-11 23:19:24.000000000 -0400
+@@ -0,0 +1,437 @@
++/*
++ * arch/mips/zboot/common/misc-common.c
++ *
++ * Misc. bootloader code (almost) all platforms can use
++ *
++ * Author: Johnnie Peters <jpeters@mvista.com>
++ * Editor: Tom Rini <trini@mvista.com>
++ *
++ * Derived from arch/ppc/boot/prep/misc.c
++ *
++ * Ported by Pete Popov <ppopov@mvista.com> to
++ * support mips board(s). I also got rid of the vga console
++ * code.
++ *
++ * Copyright 2000-2001 MontaVista Software Inc.
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version.
++ *
++ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
++ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
++ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
++ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
++ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
++ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
++ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
++ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
++ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++ *
++ * You should have received a copy of the GNU General Public License along
++ * with this program; if not, write to the Free Software Foundation, Inc.,
++ * 675 Mass Ave, Cambridge, MA 02139, USA.
++ */
++
++#include <linux/config.h>
++#include "zlib.h"
++#include <stdarg.h>
++
++extern char *avail_ram;
++extern char *end_avail;
++extern char _end[];
++
++void puts(const char *);
++void putc(const char c);
++void puthex(unsigned long val);
++void _bcopy(char *src, char *dst, int len);
++void gunzip(void *, int, unsigned char *, int *);
++static int _cvt(unsigned long val, char *buf, long radix, char *digits);
++
++void _vprintk(void(*)(const char), const char *, va_list ap);
++
++struct NS16550 *com_port;
++
++int serial_tstc(volatile struct NS16550 *);
++unsigned char serial_getc(volatile struct NS16550 *);
++void serial_putc(volatile struct NS16550 *, unsigned char);
++
++void pause(void)
++{
++ puts("pause\n");
++}
++
++void exit(void)
++{
++ puts("exit\n");
++ while(1);
++}
++
++int tstc(void)
++{
++ return (serial_tstc(com_port));
++}
++
++int getc(void)
++{
++ while (1) {
++ if (serial_tstc(com_port))
++ return (serial_getc(com_port));
++ }
++}
++
++void
++putc(const char c)
++{
++ int x,y;
++
++ serial_putc(com_port, c);
++ if ( c == '\n' )
++ serial_putc(com_port, '\r');
++}
++
++void puts(const char *s)
++{
++ char c;
++ while ( ( c = *s++ ) != '\0' ) {
++ serial_putc(com_port, c);
++ if ( c == '\n' ) serial_putc(com_port, '\r');
++ }
++}
++
++void error(char *x)
++{
++ puts("\n\n");
++ puts(x);
++ puts("\n\n -- System halted");
++
++ while(1); /* Halt */
++}
++
++void *zalloc(void *x, unsigned items, unsigned size)
++{
++ void *p = avail_ram;
++
++ size *= items;
++ size = (size + 7) & -8;
++ avail_ram += size;
++ if (avail_ram > end_avail) {
++ puts("oops... out of memory\n");
++ pause();
++ }
++ return p;
++}
++
++void zfree(void *x, void *addr, unsigned nb)
++{
++}
++
++#define HEAD_CRC 2
++#define EXTRA_FIELD 4
++#define ORIG_NAME 8
++#define COMMENT 0x10
++#define RESERVED 0xe0
++
++#define DEFLATED 8
++
++void gunzip(void *dst, int dstlen, unsigned char *src, int *lenp)
++{
++ z_stream s;
++ int r, i, flags;
++
++ /* skip header */
++ i = 10;
++ flags = src[3];
++ if (src[2] != DEFLATED || (flags & RESERVED) != 0) {
++ puts("bad gzipped data\n");
++ exit();
++ }
++ if ((flags & EXTRA_FIELD) != 0)
++ i = 12 + src[10] + (src[11] << 8);
++ if ((flags & ORIG_NAME) != 0)
++ while (src[i++] != 0)
++ ;
++ if ((flags & COMMENT) != 0)
++ while (src[i++] != 0)
++ ;
++ if ((flags & HEAD_CRC) != 0)
++ i += 2;
++ if (i >= *lenp) {
++ puts("gunzip: ran out of data in header\n");
++ exit();
++ }
++
++ s.zalloc = zalloc;
++ s.zfree = zfree;
++ r = inflateInit2(&s, -MAX_WBITS);
++ if (r != Z_OK) {
++ puts("inflateInit2 returned %d\n");
++ exit();
++ }
++ s.next_in = src + i;
++ s.avail_in = *lenp - i;
++ s.next_out = dst;
++ s.avail_out = dstlen;
++ r = inflate(&s, Z_FINISH);
++ if (r != Z_OK && r != Z_STREAM_END) {
++ puts("inflate returned %d\n");
++ exit();
++ }
++ *lenp = s.next_out - (unsigned char *) dst;
++ inflateEnd(&s);
++}
++
++void
++puthex(unsigned long val)
++{
++
++ unsigned char buf[10];
++ int i;
++ for (i = 7; i >= 0; i--)
++ {
++ buf[i] = "0123456789ABCDEF"[val & 0x0F];
++ val >>= 4;
++ }
++ buf[8] = '\0';
++ puts(buf);
++}
++
++#define FALSE 0
++#define TRUE 1
++
++void
++_printk(char const *fmt, ...)
++{
++ va_list ap;
++
++ va_start(ap, fmt);
++ _vprintk(putc, fmt, ap);
++ va_end(ap);
++ return;
++}
++
++#define is_digit(c) ((c >= '0') && (c <= '9'))
++
++void
++_vprintk(void(*putc)(const char), const char *fmt0, va_list ap)
++{
++ char c, sign, *cp = 0;
++ int left_prec, right_prec, zero_fill, length = 0, pad, pad_on_right;
++ char buf[32];
++ long val;
++ while ((c = *fmt0++))
++ {
++ if (c == '%')
++ {
++ c = *fmt0++;
++ left_prec = right_prec = pad_on_right = 0;
++ if (c == '-')
++ {
++ c = *fmt0++;
++ pad_on_right++;
++ }
++ if (c == '0')
++ {
++ zero_fill = TRUE;
++ c = *fmt0++;
++ } else
++ {
++ zero_fill = FALSE;
++ }
++ while (is_digit(c))
++ {
++ left_prec = (left_prec * 10) + (c - '0');
++ c = *fmt0++;
++ }
++ if (c == '.')
++ {
++ c = *fmt0++;
++ zero_fill++;
++ while (is_digit(c))
++ {
++ right_prec = (right_prec * 10) + (c - '0');
++ c = *fmt0++;
++ }
++ } else
++ {
++ right_prec = left_prec;
++ }
++ sign = '\0';
++ switch (c)
++ {
++ case 'd':
++ case 'x':
++ case 'X':
++ val = va_arg(ap, long);
++ switch (c)
++ {
++ case 'd':
++ if (val < 0)
++ {
++ sign = '-';
++ val = -val;
++ }
++ length = _cvt(val, buf, 10, "0123456789");
++ break;
++ case 'x':
++ length = _cvt(val, buf, 16, "0123456789abcdef");
++ break;
++ case 'X':
++ length = _cvt(val, buf, 16, "0123456789ABCDEF");
++ break;
++ }
++ cp = buf;
++ break;
++ case 's':
++ cp = va_arg(ap, char *);
++ length = strlen(cp);
++ break;
++ case 'c':
++ c = va_arg(ap, long /*char*/);
++ (*putc)(c);
++ continue;
++ default:
++ (*putc)('?');
++ }
++ pad = left_prec - length;
++ if (sign != '\0')
++ {
++ pad--;
++ }
++ if (zero_fill)
++ {
++ c = '0';
++ if (sign != '\0')
++ {
++ (*putc)(sign);
++ sign = '\0';
++ }
++ } else
++ {
++ c = ' ';
++ }
++ if (!pad_on_right)
++ {
++ while (pad-- > 0)
++ {
++ (*putc)(c);
++ }
++ }
++ if (sign != '\0')
++ {
++ (*putc)(sign);
++ }
++ while (length-- > 0)
++ {
++ (*putc)(c = *cp++);
++ if (c == '\n')
++ {
++ (*putc)('\r');
++ }
++ }
++ if (pad_on_right)
++ {
++ while (pad-- > 0)
++ {
++ (*putc)(c);
++ }
++ }
++ } else
++ {
++ (*putc)(c);
++ if (c == '\n')
++ {
++ (*putc)('\r');
++ }
++ }
++ }
++}
++
++int
++_cvt(unsigned long val, char *buf, long radix, char *digits)
++{
++ char temp[80];
++ char *cp = temp;
++ int length = 0;
++ if (val == 0)
++ { /* Special case */
++ *cp++ = '0';
++ } else
++ while (val)
++ {
++ *cp++ = digits[val % radix];
++ val /= radix;
++ }
++ while (cp != temp)
++ {
++ *buf++ = *--cp;
++ length++;
++ }
++ *buf = '\0';
++ return (length);
++}
++
++void
++_dump_buf_with_offset(unsigned char *p, int s, unsigned char *base)
++{
++ int i, c;
++ if ((unsigned int)s > (unsigned int)p)
++ {
++ s = (unsigned int)s - (unsigned int)p;
++ }
++ while (s > 0)
++ {
++ if (base)
++ {
++ _printk("%06X: ", (int)p - (int)base);
++ } else
++ {
++ _printk("%06X: ", p);
++ }
++ for (i = 0; i < 16; i++)
++ {
++ if (i < s)
++ {
++ _printk("%02X", p[i] & 0xFF);
++ } else
++ {
++ _printk(" ");
++ }
++ if ((i % 2) == 1) _printk(" ");
++ if ((i % 8) == 7) _printk(" ");
++ }
++ _printk(" |");
++ for (i = 0; i < 16; i++)
++ {
++ if (i < s)
++ {
++ c = p[i] & 0xFF;
++ if ((c < 0x20) || (c >= 0x7F)) c = '.';
++ } else
++ {
++ c = ' ';
++ }
++ _printk("%c", c);
++ }
++ _printk("|\n");
++ s -= 16;
++ p += 16;
++ }
++}
++
++void
++_dump_buf(unsigned char *p, int s)
++{
++ _printk("\n");
++ _dump_buf_with_offset(p, s, 0);
++}
++
++/*
++ * Local variables:
++ * c-indent-level: 8
++ * c-basic-offset: 8
++ * tab-width: 8
++ * End:
++ */
+diff -Naru linux/arch/mips/zboot/common/misc-simple.c linux.spi/arch/mips/zboot/common/misc-simple.c
+--- linux/arch/mips/zboot/common/misc-simple.c 1969-12-31 19:00:00.000000000 -0500
++++ linux.spi/arch/mips/zboot/common/misc-simple.c 2004-05-11 23:19:24.000000000 -0400
+@@ -0,0 +1,127 @@
++/*
++ * arch/mips/zboot/common/misc-simple.c
++ *
++ * Misc. bootloader code for many machines. This assumes you have are using
++ * a 6xx/7xx/74xx CPU in your machine. This assumes the chunk of memory
++ * below 8MB is free. Finally, it assumes you have a NS16550-style uart for
++ * your serial console. If a machine meets these requirements, it can quite
++ * likely use this code during boot.
++ *
++ * Author: Matt Porter <mporter@mvista.com>
++ * Derived from arch/ppc/boot/prep/misc.c
++ *
++ * Copyright 2001 MontaVista Software Inc.
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version.
++ */
++
++#include <linux/types.h>
++#include <linux/elf.h>
++#include <linux/config.h>
++
++#include <asm/page.h>
++#include <asm/processor.h>
++#include <asm/mmu.h>
++
++#include "zlib.h"
++
++extern struct NS16550 *com_port;
++
++char *avail_ram;
++char *end_avail;
++extern char _end[];
++char *zimage_start;
++
++#ifdef CONFIG_CMDLINE
++#define CMDLINE CONFIG_CMDLINE
++#else
++#define CMDLINE ""
++#endif
++char cmd_preset[] = CMDLINE;
++char cmd_buf[256];
++char *cmd_line = cmd_buf;
++
++/* The linker tells us where the image is.
++*/
++extern unsigned char __image_begin, __image_end;
++extern unsigned char __ramdisk_begin, __ramdisk_end;
++unsigned long initrd_size;
++
++extern void puts(const char *);
++extern void putc(const char c);
++extern void puthex(unsigned long val);
++extern void *memcpy(void * __dest, __const void * __src,
++ __kernel_size_t __n);
++extern void gunzip(void *, int, unsigned char *, int *);
++extern void udelay(long delay);
++extern int tstc(void);
++extern int getc(void);
++extern volatile struct NS16550 *serial_init(int chan);
++
++void
++decompress_kernel(unsigned long load_addr, int num_words,
++ unsigned long cksum, unsigned long *sp)
++{
++ int timer = 0;
++ extern unsigned long start;
++ char *cp, ch;
++ int i;
++ int zimage_size;
++
++ com_port = (struct NS16550 *)serial_init(0);
++
++ initrd_size = (unsigned long)(&__ramdisk_end) -
++ (unsigned long)(&__ramdisk_begin);
++
++ /*
++ * Reveal where we were loaded at and where we
++ * were relocated to.
++ */
++ puts("loaded at: "); puthex(load_addr);
++ puts(" "); puthex((unsigned long)(load_addr + (4*num_words))); puts("\n");
++ if ( (unsigned long)load_addr != (unsigned long)&start )
++ {
++ puts("relocated to: "); puthex((unsigned long)&start);
++ puts(" ");
++ puthex((unsigned long)((unsigned long)&start + (4*num_words)));
++ puts("\n");
++ }
++
++ /*
++ * We link ourself to an arbitrary low address. When we run, we
++ * relocate outself to that address. __image_being points to
++ * the part of the image where the zImage is. -- Tom
++ */
++ zimage_start = (char *)(unsigned long)(&__image_begin);
++ zimage_size = (unsigned long)(&__image_end) -
++ (unsigned long)(&__image_begin);
++
++ /*
++ * The zImage and initrd will be between start and _end, so they've
++ * already been moved once. We're good to go now. -- Tom
++ */
++ puts("zimage at: "); puthex((unsigned long)zimage_start);
++ puts(" "); puthex((unsigned long)(zimage_size+zimage_start));
++ puts("\n");
++
++ if ( initrd_size ) {
++ puts("initrd at: ");
++ puthex((unsigned long)(&__ramdisk_begin));
++ puts(" "); puthex((unsigned long)(&__ramdisk_end));puts("\n");
++ }
++
++ /* assume the chunk below 8M is free */
++ avail_ram = (char *)AVAIL_RAM_START;
++ end_avail = (char *)AVAIL_RAM_END;
++
++ /* Display standard Linux/MIPS boot prompt for kernel args */
++ puts("Uncompressing Linux at load address ");
++ puthex(LOADADDR);
++ puts("\n");
++ /* I don't like this hard coded gunzip size (fixme) */
++ gunzip((void *)LOADADDR, 0x400000, zimage_start, &zimage_size);
++ puts("Now booting the kernel\n");
++}
+diff -Naru linux/arch/mips/zboot/common/no_initrd.c linux.spi/arch/mips/zboot/common/no_initrd.c
+--- linux/arch/mips/zboot/common/no_initrd.c 1969-12-31 19:00:00.000000000 -0500
++++ linux.spi/arch/mips/zboot/common/no_initrd.c 2004-05-11 23:19:24.000000000 -0400
+@@ -0,0 +1,2 @@
++char initrd_data[1];
++int initrd_len = 0;
+diff -Naru linux/arch/mips/zboot/common/ns16550.c linux.spi/arch/mips/zboot/common/ns16550.c
+--- linux/arch/mips/zboot/common/ns16550.c 1969-12-31 19:00:00.000000000 -0500
++++ linux.spi/arch/mips/zboot/common/ns16550.c 2004-05-11 23:19:24.000000000 -0400
+@@ -0,0 +1,57 @@
++/*
++ * NS16550 support
++ */
++
++#include <linux/config.h>
++#include <asm/serial.h>
++#include "ns16550.h"
++
++typedef struct NS16550 *NS16550_t;
++
++const NS16550_t COM_PORTS[] = { (NS16550_t) COM1,
++ (NS16550_t) COM2,
++ (NS16550_t) COM3,
++ (NS16550_t) COM4 };
++
++volatile struct NS16550 *
++serial_init(int chan)
++{
++ volatile struct NS16550 *com_port;
++ com_port = (struct NS16550 *) COM_PORTS[chan];
++ /* See if port is present */
++ com_port->lcr = 0x00;
++ com_port->ier = 0xFF;
++#if 0
++ if (com_port->ier != 0x0F) return ((struct NS16550 *)0);
++#endif
++ com_port->ier = 0x00;
++ com_port->lcr = 0x80; /* Access baud rate */
++#ifdef CONFIG_SERIAL_CONSOLE_NONSTD
++ com_port->dll = (BASE_BAUD / CONFIG_SERIAL_CONSOLE_BAUD);
++ com_port->dlm = (BASE_BAUD / CONFIG_SERIAL_CONSOLE_BAUD) >> 8;
++#endif
++ com_port->lcr = 0x03; /* 8 data, 1 stop, no parity */
++ com_port->mcr = 0x03; /* RTS/DTR */
++ com_port->fcr = 0x07; /* Clear & enable FIFOs */
++ return (com_port);
++}
++
++void
++serial_putc(volatile struct NS16550 *com_port, unsigned char c)
++{
++ while ((com_port->lsr & LSR_THRE) == 0) ;
++ com_port->thr = c;
++}
++
++unsigned char
++serial_getc(volatile struct NS16550 *com_port)
++{
++ while ((com_port->lsr & LSR_DR) == 0) ;
++ return (com_port->rbr);
++}
++
++int
++serial_tstc(volatile struct NS16550 *com_port)
++{
++ return ((com_port->lsr & LSR_DR) != 0);
++}
+diff -Naru linux/arch/mips/zboot/common/string.c linux.spi/arch/mips/zboot/common/string.c
+--- linux/arch/mips/zboot/common/string.c 1969-12-31 19:00:00.000000000 -0500
++++ linux.spi/arch/mips/zboot/common/string.c 2004-05-11 23:19:24.000000000 -0400
+@@ -0,0 +1,497 @@
++/*
++ * linux/lib/string.c
++ *
++ * Copyright (C) 1991, 1992 Linus Torvalds
++ */
++
++/*
++ * stupid library routines.. The optimized versions should generally be found
++ * as inline code in <asm-xx/string.h>
++ *
++ * These are buggy as well..
++ *
++ * * Fri Jun 25 1999, Ingo Oeser <ioe@informatik.tu-chemnitz.de>
++ * - Added strsep() which will replace strtok() soon (because strsep() is
++ * reentrant and should be faster). Use only strsep() in new code, please.
++ */
++
++#include <linux/types.h>
++#include <linux/string.h>
++#include <linux/ctype.h>
++
++/**
++ * strnicmp - Case insensitive, length-limited string comparison
++ * @s1: One string
++ * @s2: The other string
++ * @len: the maximum number of characters to compare
++ */
++int strnicmp(const char *s1, const char *s2, size_t len)
++{
++ /* Yes, Virginia, it had better be unsigned */
++ unsigned char c1, c2;
++
++ c1 = 0; c2 = 0;
++ if (len) {
++ do {
++ c1 = *s1; c2 = *s2;
++ s1++; s2++;
++ if (!c1)
++ break;
++ if (!c2)
++ break;
++ if (c1 == c2)
++ continue;
++ c1 = tolower(c1);
++ c2 = tolower(c2);
++ if (c1 != c2)
++ break;
++ } while (--len);
++ }
++ return (int)c1 - (int)c2;
++}
++
++char * ___strtok;
++
++#ifndef __HAVE_ARCH_STRCPY
++/**
++ * strcpy - Copy a %NUL terminated string
++ * @dest: Where to copy the string to
++ * @src: Where to copy the string from
++ */
++char * strcpy(char * dest,const char *src)
++{
++ char *tmp = dest;
++
++ while ((*dest++ = *src++) != '\0')
++ /* nothing */;
++ return tmp;
++}
++#endif
++
++#ifndef __HAVE_ARCH_STRNCPY
++/**
++ * strncpy - Copy a length-limited, %NUL-terminated string
++ * @dest: Where to copy the string to
++ * @src: Where to copy the string from
++ * @count: The maximum number of bytes to copy
++ *
++ * Note that unlike userspace strncpy, this does not %NUL-pad the buffer.
++ * However, the result is not %NUL-terminated if the source exceeds
++ * @count bytes.
++ */
++char * strncpy(char * dest,const char *src,size_t count)
++{
++ char *tmp = dest;
++
++ while (count-- && (*dest++ = *src++) != '\0')
++ /* nothing */;
++
++ return tmp;
++}
++#endif
++
++/**
++ * strcat - Append one %NUL-terminated string to another
++ * @dest: The string to be appended to
++ * @src: The string to append to it
++ */
++char * strcat(char * dest, const char * src)
++{
++ char *tmp = dest;
++
++ while (*dest)
++ dest++;
++ while ((*dest++ = *src++) != '\0')
++ ;
++
++ return tmp;
++}
++
++/**
++ * strncat - Append a length-limited, %NUL-terminated string to another
++ * @dest: The string to be appended to
++ * @src: The string to append to it
++ * @count: The maximum numbers of bytes to copy
++ *
++ * Note that in contrast to strncpy, strncat ensures the result is
++ * terminated.
++ */
++char * strncat(char *dest, const char *src, size_t count)
++{
++ char *tmp = dest;
++
++ if (count) {
++ while (*dest)
++ dest++;
++ while ((*dest++ = *src++)) {
++ if (--count == 0) {
++ *dest = '\0';
++ break;
++ }
++ }
++ }
++
++ return tmp;
++}
++
++#ifndef __HAVE_ARCH_STRCMP
++/**
++ * strcmp - Compare two strings
++ * @cs: One string
++ * @ct: Another string
++ */
++int strcmp(const char * cs,const char * ct)
++{
++ register signed char __res;
++
++ while (1) {
++ if ((__res = *cs - *ct++) != 0 || !*cs++)
++ break;
++ }
++
++ return __res;
++}
++#endif
++
++#ifndef __HAVE_ARCH_STRNCMP
++/**
++ * strncmp - Compare two length-limited strings
++ * @cs: One string
++ * @ct: Another string
++ * @count: The maximum number of bytes to compare
++ */
++int strncmp(const char * cs,const char * ct,size_t count)
++{
++ register signed char __res = 0;
++
++ while (count) {
++ if ((__res = *cs - *ct++) != 0 || !*cs++)
++ break;
++ count--;
++ }
++
++ return __res;
++}
++#endif
++
++/**
++ * strchr - Find the first occurrence of a character in a string
++ * @s: The string to be searched
++ * @c: The character to search for
++ */
++char * strchr(const char * s, int c)
++{
++ for(; *s != (char) c; ++s)
++ if (*s == '\0')
++ return NULL;
++ return (char *) s;
++}
++
++/**
++ * strrchr - Find the last occurrence of a character in a string
++ * @s: The string to be searched
++ * @c: The character to search for
++ */
++char * strrchr(const char * s, int c)
++{
++ const char *p = s + strlen(s);
++ do {
++ if (*p == (char)c)
++ return (char *)p;
++ } while (--p >= s);
++ return NULL;
++}
++
++/**
++ * strlen - Find the length of a string
++ * @s: The string to be sized
++ */
++size_t strlen(const char * s)
++{
++ const char *sc;
++
++ for (sc = s; *sc != '\0'; ++sc)
++ /* nothing */;
++ return sc - s;
++}
++
++/**
++ * strnlen - Find the length of a length-limited string
++ * @s: The string to be sized
++ * @count: The maximum number of bytes to search
++ */
++size_t strnlen(const char * s, size_t count)
++{
++ const char *sc;
++
++ for (sc = s; count-- && *sc != '\0'; ++sc)
++ /* nothing */;
++ return sc - s;
++}
++
++/**
++ * strspn - Calculate the length of the initial substring of @s which only
++ * contain letters in @accept
++ * @s: The string to be searched
++ * @accept: The string to search for
++ */
++size_t strspn(const char *s, const char *accept)
++{
++ const char *p;
++ const char *a;
++ size_t count = 0;
++
++ for (p = s; *p != '\0'; ++p) {
++ for (a = accept; *a != '\0'; ++a) {
++ if (*p == *a)
++ break;
++ }
++ if (*a == '\0')
++ return count;
++ ++count;
++ }
++
++ return count;
++}
++
++/**
++ * strpbrk - Find the first occurrence of a set of characters
++ * @cs: The string to be searched
++ * @ct: The characters to search for
++ */
++char * strpbrk(const char * cs,const char * ct)
++{
++ const char *sc1,*sc2;
++
++ for( sc1 = cs; *sc1 != '\0'; ++sc1) {
++ for( sc2 = ct; *sc2 != '\0'; ++sc2) {
++ if (*sc1 == *sc2)
++ return (char *) sc1;
++ }
++ }
++ return NULL;
++}
++
++/**
++ * strtok - Split a string into tokens
++ * @s: The string to be searched
++ * @ct: The characters to search for
++ *
++ * WARNING: strtok is deprecated, use strsep instead.
++ */
++char * strtok(char * s,const char * ct)
++{
++ char *sbegin, *send;
++
++ sbegin = s ? s : ___strtok;
++ if (!sbegin) {
++ return NULL;
++ }
++ sbegin += strspn(sbegin,ct);
++ if (*sbegin == '\0') {
++ ___strtok = NULL;
++ return( NULL );
++ }
++ send = strpbrk( sbegin, ct);
++ if (send && *send != '\0')
++ *send++ = '\0';
++ ___strtok = send;
++ return (sbegin);
++}
++
++/**
++ * strsep - Split a string into tokens
++ * @s: The string to be searched
++ * @ct: The characters to search for
++ *
++ * strsep() updates @s to point after the token, ready for the next call.
++ *
++ * It returns empty tokens, too, behaving exactly like the libc function
++ * of that name. In fact, it was stolen from glibc2 and de-fancy-fied.
++ * Same semantics, slimmer shape. ;)
++ */
++char * strsep(char **s, const char *ct)
++{
++ char *sbegin = *s, *end;
++
++ if (sbegin == NULL)
++ return NULL;
++
++ end = strpbrk(sbegin, ct);
++ if (end)
++ *end++ = '\0';
++ *s = end;
++
++ return sbegin;
++}
++
++/**
++ * memset - Fill a region of memory with the given value
++ * @s: Pointer to the start of the area.
++ * @c: The byte to fill the area with
++ * @count: The size of the area.
++ *
++ * Do not use memset() to access IO space, use memset_io() instead.
++ */
++void * memset(void * s,int c, size_t count)
++{
++ char *xs = (char *) s;
++
++ while (count--)
++ *xs++ = c;
++
++ return s;
++}
++
++/**
++ * bcopy - Copy one area of memory to another
++ * @src: Where to copy from
++ * @dest: Where to copy to
++ * @count: The size of the area.
++ *
++ * Note that this is the same as memcpy(), with the arguments reversed.
++ * memcpy() is the standard, bcopy() is a legacy BSD function.
++ *
++ * You should not use this function to access IO space, use memcpy_toio()
++ * or memcpy_fromio() instead.
++ */
++char * bcopy(const char * src, char * dest, int count)
++{
++ char *tmp = dest;
++
++ while (count--)
++ *tmp++ = *src++;
++
++ return dest;
++}
++
++/**
++ * memcpy - Copy one area of memory to another
++ * @dest: Where to copy to
++ * @src: Where to copy from
++ * @count: The size of the area.
++ *
++ * You should not use this function to access IO space, use memcpy_toio()
++ * or memcpy_fromio() instead.
++ */
++void * memcpy(void * dest,const void *src,size_t count)
++{
++ char *tmp = (char *) dest, *s = (char *) src;
++
++ while (count--)
++ *tmp++ = *s++;
++
++ return dest;
++}
++
++/**
++ * memmove - Copy one area of memory to another
++ * @dest: Where to copy to
++ * @src: Where to copy from
++ * @count: The size of the area.
++ *
++ * Unlike memcpy(), memmove() copes with overlapping areas.
++ */
++void * memmove(void * dest,const void *src,size_t count)
++{
++ char *tmp, *s;
++
++ if (dest <= src) {
++ tmp = (char *) dest;
++ s = (char *) src;
++ while (count--)
++ *tmp++ = *s++;
++ }
++ else {
++ tmp = (char *) dest + count;
++ s = (char *) src + count;
++ while (count--)
++ *--tmp = *--s;
++ }
++
++ return dest;
++}
++
++/**
++ * memcmp - Compare two areas of memory
++ * @cs: One area of memory
++ * @ct: Another area of memory
++ * @count: The size of the area.
++ */
++int memcmp(const void * cs,const void * ct,size_t count)
++{
++ const unsigned char *su1, *su2;
++ signed char res = 0;
++
++ for( su1 = cs, su2 = ct; 0 < count; ++su1, ++su2, count--)
++ if ((res = *su1 - *su2) != 0)
++ break;
++ return res;
++}
++
++#ifndef __HAVE_ARCH_MEMSCAN
++/**
++ * memscan - Find a character in an area of memory.
++ * @addr: The memory area
++ * @c: The byte to search for
++ * @size: The size of the area.
++ *
++ * returns the address of the first occurrence of @c, or 1 byte past
++ * the area if @c is not found
++ */
++void * memscan(void * addr, int c, size_t size)
++{
++ unsigned char * p = (unsigned char *) addr;
++ unsigned char * e = p + size;
++
++ while (p != e) {
++ if (*p == c)
++ return (void *) p;
++ p++;
++ }
++
++ return (void *) p;
++}
++#endif
++
++/**
++ * strstr - Find the first substring in a %NUL terminated string
++ * @s1: The string to be searched
++ * @s2: The string to search for
++ */
++char * strstr(const char * s1,const char * s2)
++{
++ int l1, l2;
++
++ l2 = strlen(s2);
++ if (!l2)
++ return (char *) s1;
++ l1 = strlen(s1);
++ while (l1 >= l2) {
++ l1--;
++ if (!memcmp(s1,s2,l2))
++ return (char *) s1;
++ s1++;
++ }
++ return NULL;
++}
++
++/**
++ * memchr - Find a character in an area of memory.
++ * @s: The memory area
++ * @c: The byte to search for
++ * @n: The size of the area.
++ *
++ * returns the address of the first occurrence of @c, or %NULL
++ * if @c is not found
++ */
++void *memchr(const void *s, int c, size_t n)
++{
++ const unsigned char *p = s;
++ while (n-- != 0) {
++ if ((unsigned char)c == *p++) {
++ return (void *)(p-1);
++ }
++ }
++ return NULL;
++}
+diff -Naru linux/arch/mips/zboot/csb250/head.S linux.spi/arch/mips/zboot/csb250/head.S
+--- linux/arch/mips/zboot/csb250/head.S 1969-12-31 19:00:00.000000000 -0500
++++ linux.spi/arch/mips/zboot/csb250/head.S 2004-05-11 23:19:24.000000000 -0400
+@@ -0,0 +1,157 @@
++/*
++ * arch/mips/kernel/head.S
++ *
++ * This file is subject to the terms and conditions of the GNU General Public
++ * License. See the file "COPYING" in the main directory of this archive
++ * for more details.
++ *
++ * Copyright (C) 1994, 1995 Waldorf Electronics
++ * Written by Ralf Baechle and Andreas Busse
++ * Copyright (C) 1995 - 1999 Ralf Baechle
++ * Copyright (C) 1996 Paul M. Antoine
++ * Modified for DECStation and hence R3000 support by Paul M. Antoine
++ * Further modifications by David S. Miller and Harald Koerfgen
++ * Copyright (C) 1999 Silicon Graphics, Inc.
++ *
++ * Head.S contains the MIPS exception handler and startup code.
++ *
++ **************************************************************************
++ * 9 Nov, 2000.
++ * Added Cache Error exception handler and SBDDP EJTAG debug exception.
++ *
++ * Kevin Kissell, kevink@mips.com and Carsten Langgaard, carstenl@mips.com
++ * Copyright (C) 2000 MIPS Technologies, Inc. All rights reserved.
++ **************************************************************************
++ */
++#include <linux/config.h>
++#include <linux/threads.h>
++
++#include <asm/asm.h>
++#include <asm/cacheops.h>
++#include <asm/mipsregs.h>
++#include <asm/offset.h>
++#include <asm/cachectl.h>
++#include <asm/regdef.h>
++
++#define IndexInvalidate_I 0x00
++#define IndexWriteBack_D 0x01
++
++ .set noreorder
++ .cprestore
++ LEAF(start)
++start:
++ bal locate
++ nop
++
++ .globl asize /* Someday we'll put the initrd info here. */
++asize: .word 0
++ .word 0
++ .word 0
++ .word 0
++
++locate:
++ subu s8, ra, 8 /* Where we were loaded */
++ la sp, (.stack + 8192)
++
++ move s0, a0 /* Save boot rom start args */
++ move s1, a1
++ move s2, a2
++ move s3, a3
++
++ la a0, start /* Where we were linked to run */
++
++ move a1, s8
++ la a2, _edata
++ subu t1, a2, a0
++ srl t1, t1, 2
++
++ /* copy text section */
++ li t0, 0
++1: lw v0, 0(a1)
++ nop
++ sw v0, 0(a0)
++ xor t0, t0, v0
++ addu a0, 4
++ bne a2, a0, 1b
++ addu a1, 4
++
++ /* Clear BSS */
++ la a0, _edata
++ la a2, _end
++2: sw zero, 0(a0)
++ bne a2, a0, 2b
++ addu a0, 4
++
++ /* push the D-Cache and invalidate I-Cache */
++ li k0, 0x80000000 # start address
++ li k1, 0x80004000 # end address (16KB I-Cache)
++ subu k1, 128
++
++1:
++ .set mips3
++ cache IndexWriteBack_D, 0(k0)
++ cache IndexWriteBack_D, 32(k0)
++ cache IndexWriteBack_D, 64(k0)
++ cache IndexWriteBack_D, 96(k0)
++ cache IndexInvalidate_I, 0(k0)
++ cache IndexInvalidate_I, 32(k0)
++ cache IndexInvalidate_I, 64(k0)
++ cache IndexInvalidate_I, 96(k0)
++ .set mips0
++
++ bne k0, k1, 1b
++ addu k0, k0, 128
++ /* done */
++
++/* move a0, s8 /* load address */
++ subu a0, s8, 0x1000 /* load address */
++ move a1, t1 /* length in words */
++ move a2, t0 /* checksum */
++ move a3, sp
++
++ la ra, 1f
++ la k0, decompress_kernel
++ jr k0
++ nop
++1:
++
++ la a2, __ramdisk_begin
++ la a3, initrd_size
++ lw a0, 0(a2)
++ lw a1, 0(a3)
++ li k0, KERNEL_ENTRY
++ jr k0
++ nop
++3:
++ b 3b
++ END(start)
++
++ LEAF(udelay)
++udelay:
++ END(udelay)
++
++
++ LEAF(FlushCache)
++ li k0, 0x80000000 # start address
++ li k1, 0x80004000 # end address (16KB I-Cache)
++ subu k1, 128
++
++1:
++ .set mips3
++ cache IndexWriteBack_D, 0(k0)
++ cache IndexWriteBack_D, 32(k0)
++ cache IndexWriteBack_D, 64(k0)
++ cache IndexWriteBack_D, 96(k0)
++ cache IndexInvalidate_I, 0(k0)
++ cache IndexInvalidate_I, 32(k0)
++ cache IndexInvalidate_I, 64(k0)
++ cache IndexInvalidate_I, 96(k0)
++ .set mips0
++
++ bne k0, k1, 1b
++ addu k0, k0, 128
++ jr ra
++ nop
++ END(FlushCache)
++
++ .comm .stack,4096*2,4
+diff -Naru linux/arch/mips/zboot/csb250/Makefile linux.spi/arch/mips/zboot/csb250/Makefile
+--- linux/arch/mips/zboot/csb250/Makefile 1969-12-31 19:00:00.000000000 -0500
++++ linux.spi/arch/mips/zboot/csb250/Makefile 2004-05-11 23:19:24.000000000 -0400
+@@ -0,0 +1,90 @@
++# arch/mips/zboot/pb1xxx/Makefile
++#
++# Makefile for Cogent CSB250 Au1500 board.
++# All of the boot loader code was derived from the ppc
++# boot code.
++#
++# This program is free software; you can redistribute it and/or modify it
++# under the terms of the GNU General Public License as published by the
++# Free Software Foundation; either version 2 of the License, or (at your
++# option) any later version.
++
++.c.s:
++ $(CC) $(CFLAGS) -S -o $*.s $<
++.s.o:
++ $(AS) -o $*.o $<
++.c.o:
++ $(CC) $(CFLAGS) -D__BOOTER__ -c -o $*.o $<
++.S.s:
++ $(CPP) $(AFLAGS) -o $*.o $<
++.S.o:
++ $(CC) $(AFLAGS) -c -o $*.o $<
++
++#########################################################################
++# START BOARD SPECIFIC VARIABLES
++BNAME=csb250
++
++# These two variables control where the zImage is stored
++# in flash and loaded in memory. It only controls how the srec
++# file is generated, the code is the same.
++RAM_RUN_ADDR = 0x80a00000
++FLASH_LOAD_ADDR = 0xBFD00000
++
++# These two variables specify the free ram region
++# that can be used for temporary malloc area
++AVAIL_RAM_START=0x80400000
++AVAIL_RAM_END=0x80800000
++
++# This one must match the LOADADDR in arch/mips/Makefile!
++LOADADDR=0x80100000
++# END BOARD SPECIFIC VARIABLES
++#########################################################################
++
++OBJECTS := head.o ../common/misc-common.o ../common/misc-simple.o \
++ ../common/au1k_uart.o ../common/string.o ../common/ctype.o
++LIBS := ../lib/zlib.a
++
++ENTRY := ../utils/entry
++OFFSET := ../utils/offset
++SIZE := ../utils/size
++
++LD_ARGS := -T ../ld.script -Ttext $(RAM_RUN_ADDR) -Bstatic
++OBJCOPY_ARGS = -O elf32-tradbigmips
++
++all: zImage
++
++clean:
++ rm -rf *.o vmlinux* zvmlinux.* ../images/*.srec
++
++head.o: head.S $(TOPDIR)/vmlinux
++ $(CC) $(AFLAGS) \
++ -DKERNEL_ENTRY=$(shell sh $(ENTRY) $(NM) $(TOPDIR)/vmlinux ) \
++ -c -o $*.o $<
++
++../common/misc-simple.o:
++ $(CC) $(CFLAGS) -DINITRD_OFFSET=0 -DINITRD_SIZE=0 -DZIMAGE_OFFSET=0 \
++ -DAVAIL_RAM_START=$(AVAIL_RAM_START) \
++ -DAVAIL_RAM_END=$(AVAIL_RAM_END) \
++ -DLOADADDR=$(LOADADDR) \
++ -DZIMAGE_SIZE=0 -c -o $@ $*.c
++
++zvmlinux: $(OBJECTS) $(LIBS) ../ld.script ../images/vmlinux.gz ../common/dummy.o
++ $(OBJCOPY) \
++ --add-section=.image=../images/vmlinux.gz \
++ --set-section-flags=.image=contents,alloc,load,readonly,data \
++ ../common/dummy.o image.o
++ $(LD) $(LD_ARGS) -o $@ $(OBJECTS) image.o $(LIBS)
++ $(OBJCOPY) $(OBJCOPY_ARGS) $@ $@ -R .comment -R .stab -R .stabstr \
++ -R .initrd -R .sysmap
++
++# Here we manipulate the image in order to get it the necessary
++# srecord file we need.
++zImage: zvmlinux
++ mv zvmlinux ../images/zImage.$(BNAME)
++ $(OBJCOPY) -O binary ../images/zImage.$(BNAME) ../images/$(BNAME).bin
++
++zImage.flash: zImage
++ $(OBJCOPY) -O srec --adjust-vma 0x3ed00000 \
++ ../images/zImage.$(BNAME) ../images/$(BNAME).flash.srec
++
++include $(TOPDIR)/Rules.make
+diff -Naru linux/arch/mips/zboot/images/Makefile linux.spi/arch/mips/zboot/images/Makefile
+--- linux/arch/mips/zboot/images/Makefile 1969-12-31 19:00:00.000000000 -0500
++++ linux.spi/arch/mips/zboot/images/Makefile 2004-05-11 23:19:24.000000000 -0400
+@@ -0,0 +1,10 @@
++
++include $(TOPDIR)/Rules.make
++
++vmlinux.gz: $(TOPDIR)/vmlinux
++ $(OBJCOPY) -S -O binary $(TOPDIR)/vmlinux vmlinux
++ gzip -vf vmlinux
++
++clean:
++ rm -f vmlinux.* zImage.*
++
+diff -Naru linux/arch/mips/zboot/include/nonstdio.h linux.spi/arch/mips/zboot/include/nonstdio.h
+--- linux/arch/mips/zboot/include/nonstdio.h 1969-12-31 19:00:00.000000000 -0500
++++ linux.spi/arch/mips/zboot/include/nonstdio.h 2004-05-11 23:19:24.000000000 -0400
+@@ -0,0 +1,18 @@
++/*
++ * Copyright (C) Paul Mackerras 1997.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version
++ * 2 of the License, or (at your option) any later version.
++ */
++typedef int FILE;
++extern FILE *stdin, *stdout;
++#define NULL ((void *)0)
++#define EOF (-1)
++#define fopen(n, m) NULL
++#define fflush(f) 0
++#define fclose(f) 0
++extern char *fgets();
++
++#define perror(s) printf("%s: no files!\n", (s))
+diff -Naru linux/arch/mips/zboot/include/ns16550.h linux.spi/arch/mips/zboot/include/ns16550.h
+--- linux/arch/mips/zboot/include/ns16550.h 1969-12-31 19:00:00.000000000 -0500
++++ linux.spi/arch/mips/zboot/include/ns16550.h 2004-05-11 23:19:24.000000000 -0400
+@@ -0,0 +1,49 @@
++/*
++ * NS16550 Serial Port
++ */
++
++/*
++ * Figure out which file will have the definitons of COMx
++ */
++#if !defined(CONFIG_AU1X00_UART)
++#error no serial.h
++#endif
++
++/* Some machines have their uart registers 16 bytes apart. Most don't.
++ * TODO: Make this work like drivers/char/serial does - Tom */
++#if !defined(UART_REG_PAD)
++#define UART_REG_PAD(x)
++#endif
++
++struct NS16550
++ {
++ unsigned char rbr; /* 0 */
++ UART_REG_PAD(rbr)
++ unsigned char ier; /* 1 */
++ UART_REG_PAD(ier)
++ unsigned char fcr; /* 2 */
++ UART_REG_PAD(fcr)
++ unsigned char lcr; /* 3 */
++ UART_REG_PAD(lcr)
++ unsigned char mcr; /* 4 */
++ UART_REG_PAD(mcr)
++ unsigned char lsr; /* 5 */
++ UART_REG_PAD(lsr)
++ unsigned char msr; /* 6 */
++ UART_REG_PAD(msr)
++ unsigned char scr; /* 7 */
++ };
++
++#define thr rbr
++#define iir fcr
++#define dll rbr
++#define dlm ier
++
++#define LSR_DR 0x01 /* Data ready */
++#define LSR_OE 0x02 /* Overrun */
++#define LSR_PE 0x04 /* Parity error */
++#define LSR_FE 0x08 /* Framing error */
++#define LSR_BI 0x10 /* Break */
++#define LSR_THRE 0x20 /* Xmit holding register empty */
++#define LSR_TEMT 0x40 /* Xmitter empty */
++#define LSR_ERR 0x80 /* Error */
+diff -Naru linux/arch/mips/zboot/include/pb1000_serial.h linux.spi/arch/mips/zboot/include/pb1000_serial.h
+--- linux/arch/mips/zboot/include/pb1000_serial.h 1969-12-31 19:00:00.000000000 -0500
++++ linux.spi/arch/mips/zboot/include/pb1000_serial.h 2004-05-11 23:19:24.000000000 -0400
+@@ -0,0 +1,20 @@
++/*
++ * arch/ppc/boot/include/sandpoint_serial.h
++ *
++ * Location of the COM ports on Motorola SPS Sandpoint machines
++ *
++ * Author: Mark A. Greer
++ * mgreer@mvista.com
++ *
++ * Copyright 2001 MontaVista Software Inc.
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version.
++ */
++
++#define COM1 0xfe0003f8
++#define COM2 0xfe0002f8
++#define COM3 0x00000000 /* No COM3 */
++#define COM4 0x00000000 /* No COM4 */
+diff -Naru linux/arch/mips/zboot/include/zlib.h linux.spi/arch/mips/zboot/include/zlib.h
+--- linux/arch/mips/zboot/include/zlib.h 1969-12-31 19:00:00.000000000 -0500
++++ linux.spi/arch/mips/zboot/include/zlib.h 2004-05-11 23:19:24.000000000 -0400
+@@ -0,0 +1,432 @@
++/* $Id$ */
++
++/*
++ * This file is derived from zlib.h and zconf.h from the zlib-0.95
++ * distribution by Jean-loup Gailly and Mark Adler, with some additions
++ * by Paul Mackerras to aid in implementing Deflate compression and
++ * decompression for PPP packets.
++ */
++
++/*
++ * ==FILEVERSION 960122==
++ *
++ * This marker is used by the Linux installation script to determine
++ * whether an up-to-date version of this file is already installed.
++ */
++
++/* zlib.h -- interface of the 'zlib' general purpose compression library
++ version 0.95, Aug 16th, 1995.
++
++ Copyright (C) 1995 Jean-loup Gailly and Mark Adler
++
++ This software is provided 'as-is', without any express or implied
++ warranty. In no event will the authors be held liable for any damages
++ arising from the use of this software.
++
++ Permission is granted to anyone to use this software for any purpose,
++ including commercial applications, and to alter it and redistribute it
++ freely, subject to the following restrictions:
++
++ 1. The origin of this software must not be misrepresented; you must not
++ claim that you wrote the original software. If you use this software
++ in a product, an acknowledgment in the product documentation would be
++ appreciated but is not required.
++ 2. Altered source versions must be plainly marked as such, and must not be
++ misrepresented as being the original software.
++ 3. This notice may not be removed or altered from any source distribution.
++
++ Jean-loup Gailly Mark Adler
++ gzip@prep.ai.mit.edu madler@alumni.caltech.edu
++ */
++
++#ifndef _ZLIB_H
++#define _ZLIB_H
++
++/* #include "zconf.h" */ /* included directly here */
++
++/* zconf.h -- configuration of the zlib compression library
++ * Copyright (C) 1995 Jean-loup Gailly.
++ * For conditions of distribution and use, see copyright notice in zlib.h
++ */
++
++/* From: zconf.h,v 1.12 1995/05/03 17:27:12 jloup Exp */
++
++/*
++ The library does not install any signal handler. It is recommended to
++ add at least a handler for SIGSEGV when decompressing; the library checks
++ the consistency of the input data whenever possible but may go nuts
++ for some forms of corrupted input.
++ */
++
++/*
++ * Compile with -DMAXSEG_64K if the alloc function cannot allocate more
++ * than 64k bytes at a time (needed on systems with 16-bit int).
++ * Compile with -DUNALIGNED_OK if it is OK to access shorts or ints
++ * at addresses which are not a multiple of their size.
++ * Under DOS, -DFAR=far or -DFAR=__far may be needed.
++ */
++
++#ifndef STDC
++# if defined(MSDOS) || defined(__STDC__) || defined(__cplusplus)
++# define STDC
++# endif
++#endif
++
++#ifdef __MWERKS__ /* Metrowerks CodeWarrior declares fileno() in unix.h */
++# include <unix.h>
++#endif
++
++/* Maximum value for memLevel in deflateInit2 */
++#ifndef MAX_MEM_LEVEL
++# ifdef MAXSEG_64K
++# define MAX_MEM_LEVEL 8
++# else
++# define MAX_MEM_LEVEL 9
++# endif
++#endif
++
++#ifndef FAR
++# define FAR
++#endif
++
++/* Maximum value for windowBits in deflateInit2 and inflateInit2 */
++#ifndef MAX_WBITS
++# define MAX_WBITS 15 /* 32K LZ77 window */
++#endif
++
++/* The memory requirements for deflate are (in bytes):
++ 1 << (windowBits+2) + 1 << (memLevel+9)
++ that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values)
++ plus a few kilobytes for small objects. For example, if you want to reduce
++ the default memory requirements from 256K to 128K, compile with
++ make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7"
++ Of course this will generally degrade compression (there's no free lunch).
++
++ The memory requirements for inflate are (in bytes) 1 << windowBits
++ that is, 32K for windowBits=15 (default value) plus a few kilobytes
++ for small objects.
++*/
++
++ /* Type declarations */
++
++#ifndef OF /* function prototypes */
++# ifdef STDC
++# define OF(args) args
++# else
++# define OF(args) ()
++# endif
++#endif
++
++typedef unsigned char Byte; /* 8 bits */
++typedef unsigned int uInt; /* 16 bits or more */
++typedef unsigned long uLong; /* 32 bits or more */
++
++typedef Byte FAR Bytef;
++typedef char FAR charf;
++typedef int FAR intf;
++typedef uInt FAR uIntf;
++typedef uLong FAR uLongf;
++
++#ifdef STDC
++ typedef void FAR *voidpf;
++ typedef void *voidp;
++#else
++ typedef Byte FAR *voidpf;
++ typedef Byte *voidp;
++#endif
++
++/* end of original zconf.h */
++
++#define ZLIB_VERSION "0.95P"
++
++/*
++ The 'zlib' compression library provides in-memory compression and
++ decompression functions, including integrity checks of the uncompressed
++ data. This version of the library supports only one compression method
++ (deflation) but other algorithms may be added later and will have the same
++ stream interface.
++
++ For compression the application must provide the output buffer and
++ may optionally provide the input buffer for optimization. For decompression,
++ the application must provide the input buffer and may optionally provide
++ the output buffer for optimization.
++
++ Compression can be done in a single step if the buffers are large
++ enough (for example if an input file is mmap'ed), or can be done by
++ repeated calls of the compression function. In the latter case, the
++ application must provide more input and/or consume the output
++ (providing more output space) before each call.
++*/
++
++typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size));
++typedef void (*free_func) OF((voidpf opaque, voidpf address, uInt nbytes));
++
++struct internal_state;
++
++typedef struct z_stream_s {
++ Bytef *next_in; /* next input byte */
++ uInt avail_in; /* number of bytes available at next_in */
++ uLong total_in; /* total nb of input bytes read so far */
++
++ Bytef *next_out; /* next output byte should be put there */
++ uInt avail_out; /* remaining free space at next_out */
++ uLong total_out; /* total nb of bytes output so far */
++
++ char *msg; /* last error message, NULL if no error */
++ struct internal_state FAR *state; /* not visible by applications */
++
++ alloc_func zalloc; /* used to allocate the internal state */
++ free_func zfree; /* used to free the internal state */
++ voidp opaque; /* private data object passed to zalloc and zfree */
++
++ Byte data_type; /* best guess about the data type: ascii or binary */
++
++} z_stream;
++
++/*
++ The application must update next_in and avail_in when avail_in has
++ dropped to zero. It must update next_out and avail_out when avail_out
++ has dropped to zero. The application must initialize zalloc, zfree and
++ opaque before calling the init function. All other fields are set by the
++ compression library and must not be updated by the application.
++
++ The opaque value provided by the application will be passed as the first
++ parameter for calls of zalloc and zfree. This can be useful for custom
++ memory management. The compression library attaches no meaning to the
++ opaque value.
++
++ zalloc must return Z_NULL if there is not enough memory for the object.
++ On 16-bit systems, the functions zalloc and zfree must be able to allocate
++ exactly 65536 bytes, but will not be required to allocate more than this
++ if the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS,
++ pointers returned by zalloc for objects of exactly 65536 bytes *must*
++ have their offset normalized to zero. The default allocation function
++ provided by this library ensures this (see zutil.c). To reduce memory
++ requirements and avoid any allocation of 64K objects, at the expense of
++ compression ratio, compile the library with -DMAX_WBITS=14 (see zconf.h).
++
++ The fields total_in and total_out can be used for statistics or
++ progress reports. After compression, total_in holds the total size of
++ the uncompressed data and may be saved for use in the decompressor
++ (particularly if the decompressor wants to decompress everything in
++ a single step).
++*/
++
++ /* constants */
++
++#define Z_NO_FLUSH 0
++#define Z_PARTIAL_FLUSH 1
++#define Z_FULL_FLUSH 2
++#define Z_SYNC_FLUSH 3 /* experimental: partial_flush + byte align */
++#define Z_FINISH 4
++#define Z_PACKET_FLUSH 5
++/* See deflate() below for the usage of these constants */
++
++#define Z_OK 0
++#define Z_STREAM_END 1
++#define Z_ERRNO (-1)
++#define Z_STREAM_ERROR (-2)
++#define Z_DATA_ERROR (-3)
++#define Z_MEM_ERROR (-4)
++#define Z_BUF_ERROR (-5)
++/* error codes for the compression/decompression functions */
++
++#define Z_BEST_SPEED 1
++#define Z_BEST_COMPRESSION 9
++#define Z_DEFAULT_COMPRESSION (-1)
++/* compression levels */
++
++#define Z_FILTERED 1
++#define Z_HUFFMAN_ONLY 2
++#define Z_DEFAULT_STRATEGY 0
++
++#define Z_BINARY 0
++#define Z_ASCII 1
++#define Z_UNKNOWN 2
++/* Used to set the data_type field */
++
++#define Z_NULL 0 /* for initializing zalloc, zfree, opaque */
++
++extern char *zlib_version;
++/* The application can compare zlib_version and ZLIB_VERSION for consistency.
++ If the first character differs, the library code actually used is
++ not compatible with the zlib.h header file used by the application.
++ */
++
++ /* basic functions */
++
++extern int inflateInit OF((z_stream *strm));
++/*
++ Initializes the internal stream state for decompression. The fields
++ zalloc and zfree must be initialized before by the caller. If zalloc and
++ zfree are set to Z_NULL, inflateInit updates them to use default allocation
++ functions.
++
++ inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not
++ enough memory. msg is set to null if there is no error message.
++ inflateInit does not perform any decompression: this will be done by
++ inflate().
++*/
++
++
++extern int inflate OF((z_stream *strm, int flush));
++/*
++ Performs one or both of the following actions:
++
++ - Decompress more input starting at next_in and update next_in and avail_in
++ accordingly. If not all input can be processed (because there is not
++ enough room in the output buffer), next_in is updated and processing
++ will resume at this point for the next call of inflate().
++
++ - Provide more output starting at next_out and update next_out and avail_out
++ accordingly. inflate() always provides as much output as possible
++ (until there is no more input data or no more space in the output buffer).
++
++ Before the call of inflate(), the application should ensure that at least
++ one of the actions is possible, by providing more input and/or consuming
++ more output, and updating the next_* and avail_* values accordingly.
++ The application can consume the uncompressed output when it wants, for
++ example when the output buffer is full (avail_out == 0), or after each
++ call of inflate().
++
++ If the parameter flush is set to Z_PARTIAL_FLUSH or Z_PACKET_FLUSH,
++ inflate flushes as much output as possible to the output buffer. The
++ flushing behavior of inflate is not specified for values of the flush
++ parameter other than Z_PARTIAL_FLUSH, Z_PACKET_FLUSH or Z_FINISH, but the
++ current implementation actually flushes as much output as possible
++ anyway. For Z_PACKET_FLUSH, inflate checks that once all the input data
++ has been consumed, it is expecting to see the length field of a stored
++ block; if not, it returns Z_DATA_ERROR.
++
++ inflate() should normally be called until it returns Z_STREAM_END or an
++ error. However if all decompression is to be performed in a single step
++ (a single call of inflate), the parameter flush should be set to
++ Z_FINISH. In this case all pending input is processed and all pending
++ output is flushed; avail_out must be large enough to hold all the
++ uncompressed data. (The size of the uncompressed data may have been saved
++ by the compressor for this purpose.) The next operation on this stream must
++ be inflateEnd to deallocate the decompression state. The use of Z_FINISH
++ is never required, but can be used to inform inflate that a faster routine
++ may be used for the single inflate() call.
++
++ inflate() returns Z_OK if some progress has been made (more input
++ processed or more output produced), Z_STREAM_END if the end of the
++ compressed data has been reached and all uncompressed output has been
++ produced, Z_DATA_ERROR if the input data was corrupted, Z_STREAM_ERROR if
++ the stream structure was inconsistent (for example if next_in or next_out
++ was NULL), Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR if no
++ progress is possible or if there was not enough room in the output buffer
++ when Z_FINISH is used. In the Z_DATA_ERROR case, the application may then
++ call inflateSync to look for a good compression block. */
++
++
++extern int inflateEnd OF((z_stream *strm));
++/*
++ All dynamically allocated data structures for this stream are freed.
++ This function discards any unprocessed input and does not flush any
++ pending output.
++
++ inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state
++ was inconsistent. In the error case, msg may be set but then points to a
++ static string (which must not be deallocated).
++*/
++
++ /* advanced functions */
++
++extern int inflateInit2 OF((z_stream *strm,
++ int windowBits));
++/*
++ This is another version of inflateInit with more compression options. The
++ fields next_out, zalloc and zfree must be initialized before by the caller.
++
++ The windowBits parameter is the base two logarithm of the maximum window
++ size (the size of the history buffer). It should be in the range 8..15 for
++ this version of the library (the value 16 will be allowed soon). The
++ default value is 15 if inflateInit is used instead. If a compressed stream
++ with a larger window size is given as input, inflate() will return with
++ the error code Z_DATA_ERROR instead of trying to allocate a larger window.
++
++ If next_out is not null, the library will use this buffer for the history
++ buffer; the buffer must either be large enough to hold the entire output
++ data, or have at least 1<<windowBits bytes. If next_out is null, the
++ library will allocate its own buffer (and leave next_out null). next_in
++ need not be provided here but must be provided by the application for the
++ next call of inflate().
++
++ If the history buffer is provided by the application, next_out must
++ never be changed by the application since the decompressor maintains
++ history information inside this buffer from call to call; the application
++ can only reset next_out to the beginning of the history buffer when
++ avail_out is zero and all output has been consumed.
++
++ inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was
++ not enough memory, Z_STREAM_ERROR if a parameter is invalid (such as
++ windowBits < 8). msg is set to null if there is no error message.
++ inflateInit2 does not perform any decompression: this will be done by
++ inflate().
++*/
++
++extern int inflateSync OF((z_stream *strm));
++/*
++ Skips invalid compressed data until the special marker (see deflate()
++ above) can be found, or until all available input is skipped. No output
++ is provided.
++
++ inflateSync returns Z_OK if the special marker has been found, Z_BUF_ERROR
++ if no more input was provided, Z_DATA_ERROR if no marker has been found,
++ or Z_STREAM_ERROR if the stream structure was inconsistent. In the success
++ case, the application may save the current current value of total_in which
++ indicates where valid compressed data was found. In the error case, the
++ application may repeatedly call inflateSync, providing more input each time,
++ until success or end of the input data.
++*/
++
++extern int inflateReset OF((z_stream *strm));
++/*
++ This function is equivalent to inflateEnd followed by inflateInit,
++ but does not free and reallocate all the internal decompression state.
++ The stream will keep attributes that may have been set by inflateInit2.
++
++ inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source
++ stream state was inconsistent (such as zalloc or state being NULL).
++*/
++
++extern int inflateIncomp OF((z_stream *strm));
++/*
++ This function adds the data at next_in (avail_in bytes) to the output
++ history without performing any output. There must be no pending output,
++ and the decompressor must be expecting to see the start of a block.
++ Calling this function is equivalent to decompressing a stored block
++ containing the data at next_in (except that the data is not output).
++*/
++
++ /* checksum functions */
++
++/*
++ This function is not related to compression but is exported
++ anyway because it might be useful in applications using the
++ compression library.
++*/
++
++extern uLong adler32 OF((uLong adler, Bytef *buf, uInt len));
++
++/*
++ Update a running Adler-32 checksum with the bytes buf[0..len-1] and
++ return the updated checksum. If buf is NULL, this function returns
++ the required initial value for the checksum.
++ An Adler-32 checksum is almost as reliable as a CRC32 but can be computed
++ much faster. Usage example:
++
++ uLong adler = adler32(0L, Z_NULL, 0);
++
++ while (read_buffer(buffer, length) != EOF) {
++ adler = adler32(adler, buffer, length);
++ }
++ if (adler != original_adler) error();
++*/
++
++#ifndef _Z_UTIL_H
++ struct internal_state {int dummy;}; /* hack for buggy compilers */
++#endif
++
++#endif /* _ZLIB_H */
+diff -Naru linux/arch/mips/zboot/ld.script linux.spi/arch/mips/zboot/ld.script
+--- linux/arch/mips/zboot/ld.script 1969-12-31 19:00:00.000000000 -0500
++++ linux.spi/arch/mips/zboot/ld.script 2004-05-11 23:19:24.000000000 -0400
+@@ -0,0 +1,151 @@
++OUTPUT_ARCH(mips)
++ENTRY(start)
++SECTIONS
++{
++ /* Read-only sections, merged into text segment: */
++ /* . = 0x81000000; */
++ .init : { *(.init) } =0
++ .text :
++ {
++ _ftext = . ;
++ *(.text)
++ *(.rodata)
++ *(.rodata1)
++ /* .gnu.warning sections are handled specially by elf32.em. */
++ *(.gnu.warning)
++ } =0
++ .kstrtab : { *(.kstrtab) }
++
++ . = ALIGN(16); /* Exception table */
++ __start___ex_table = .;
++ __ex_table : { *(__ex_table) }
++ __stop___ex_table = .;
++
++ __start___dbe_table = .; /* Exception table for data bus errors */
++ __dbe_table : { *(__dbe_table) }
++ __stop___dbe_table = .;
++
++ __start___ksymtab = .; /* Kernel symbol table */
++ __ksymtab : { *(__ksymtab) }
++ __stop___ksymtab = .;
++
++ _etext = .;
++
++ . = ALIGN(8192);
++ .data.init_task : { *(.data.init_task) }
++
++ /* Startup code */
++ . = ALIGN(4096);
++ __init_begin = .;
++ .text.init : { *(.text.init) }
++ .data.init : { *(.data.init) }
++ . = ALIGN(16);
++ __setup_start = .;
++ .setup.init : { *(.setup.init) }
++ __setup_end = .;
++ __initcall_start = .;
++ .initcall.init : { *(.initcall.init) }
++ __initcall_end = .;
++ . = ALIGN(4096); /* Align double page for init_task_union */
++ __init_end = .;
++
++ . = ALIGN(4096);
++ .data.page_aligned : { *(.data.idt) }
++
++ . = ALIGN(32);
++ .data.cacheline_aligned : { *(.data.cacheline_aligned) }
++
++ .fini : { *(.fini) } =0
++ .reginfo : { *(.reginfo) }
++ /* Adjust the address for the data segment. We want to adjust up to
++ the same address within the page on the next page up. It would
++ be more correct to do this:
++ . = .;
++ The current expression does not correctly handle the case of a
++ text segment ending precisely at the end of a page; it causes the
++ data segment to skip a page. The above expression does not have
++ this problem, but it will currently (2/95) cause BFD to allocate
++ a single segment, combining both text and data, for this case.
++ This will prevent the text segment from being shared among
++ multiple executions of the program; I think that is more
++ important than losing a page of the virtual address space (note
++ that no actual memory is lost; the page which is skipped can not
++ be referenced). */
++ . = .;
++ .data :
++ {
++ _fdata = . ;
++ *(.data)
++
++ /* Put the compressed image here, so bss is on the end. */
++ __image_begin = .;
++ *(.image)
++ __image_end = .;
++ /* Align the initial ramdisk image (INITRD) on page boundaries. */
++ . = ALIGN(4096);
++ __ramdisk_begin = .;
++ *(.initrd)
++ __ramdisk_end = .;
++ . = ALIGN(4096);
++
++ CONSTRUCTORS
++ }
++ .data1 : { *(.data1) }
++ _gp = . + 0x8000;
++ .lit8 : { *(.lit8) }
++ .lit4 : { *(.lit4) }
++ .ctors : { *(.ctors) }
++ .dtors : { *(.dtors) }
++ .got : { *(.got.plt) *(.got) }
++ .dynamic : { *(.dynamic) }
++ /* We want the small data sections together, so single-instruction offsets
++ can access them all, and initialized data all before uninitialized, so
++ we can shorten the on-disk segment size. */
++ .sdata : { *(.sdata) }
++ . = ALIGN(4);
++ _edata = .;
++ PROVIDE (edata = .);
++
++ __bss_start = .;
++ _fbss = .;
++ .sbss : { *(.sbss) *(.scommon) }
++ .bss :
++ {
++ *(.dynbss)
++ *(.bss)
++ *(COMMON)
++ . = ALIGN(4);
++ _end = . ;
++ PROVIDE (end = .);
++ }
++
++ /* Sections to be discarded */
++ /DISCARD/ :
++ {
++ *(.text.exit)
++ *(.data.exit)
++ *(.exitcall.exit)
++ }
++
++ /* This is the MIPS specific mdebug section. */
++ .mdebug : { *(.mdebug) }
++ /* These are needed for ELF backends which have not yet been
++ converted to the new style linker. */
++ .stab 0 : { *(.stab) }
++ .stabstr 0 : { *(.stabstr) }
++ /* DWARF debug sections.
++ Symbols in the .debug DWARF section are relative to the beginning of the
++ section so we begin .debug at 0. It's not clear yet what needs to happen
++ for the others. */
++ .debug 0 : { *(.debug) }
++ .debug_srcinfo 0 : { *(.debug_srcinfo) }
++ .debug_aranges 0 : { *(.debug_aranges) }
++ .debug_pubnames 0 : { *(.debug_pubnames) }
++ .debug_sfnames 0 : { *(.debug_sfnames) }
++ .line 0 : { *(.line) }
++ /* These must appear regardless of . */
++ .gptab.sdata : { *(.gptab.data) *(.gptab.sdata) }
++ .gptab.sbss : { *(.gptab.bss) *(.gptab.sbss) }
++ .comment : { *(.comment) }
++ .note : { *(.note) }
++}
+diff -Naru linux/arch/mips/zboot/lib/Makefile linux.spi/arch/mips/zboot/lib/Makefile
+--- linux/arch/mips/zboot/lib/Makefile 1969-12-31 19:00:00.000000000 -0500
++++ linux.spi/arch/mips/zboot/lib/Makefile 2004-05-11 23:19:24.000000000 -0400
+@@ -0,0 +1,9 @@
++#
++# Makefile for some libs needed by zImage.
++#
++
++L_TARGET := zlib.a
++
++obj-y := zlib.o
++
++include $(TOPDIR)/Rules.make
+diff -Naru linux/arch/mips/zboot/lib/zlib.c linux.spi/arch/mips/zboot/lib/zlib.c
+--- linux/arch/mips/zboot/lib/zlib.c 1969-12-31 19:00:00.000000000 -0500
++++ linux.spi/arch/mips/zboot/lib/zlib.c 2004-05-11 23:19:24.000000000 -0400
+@@ -0,0 +1,2148 @@
++/*
++ * This file is derived from various .h and .c files from the zlib-0.95
++ * distribution by Jean-loup Gailly and Mark Adler, with some additions
++ * by Paul Mackerras to aid in implementing Deflate compression and
++ * decompression for PPP packets. See zlib.h for conditions of
++ * distribution and use.
++ *
++ * Changes that have been made include:
++ * - changed functions not used outside this file to "local"
++ * - added minCompression parameter to deflateInit2
++ * - added Z_PACKET_FLUSH (see zlib.h for details)
++ * - added inflateIncomp
++ *
++ * $Id$
++ */
++
++/*+++++*/
++/* zutil.h -- internal interface and configuration of the compression library
++ * Copyright (C) 1995 Jean-loup Gailly.
++ * For conditions of distribution and use, see copyright notice in zlib.h
++ */
++
++/* WARNING: this file should *not* be used by applications. It is
++ part of the implementation of the compression library and is
++ subject to change. Applications should only use zlib.h.
++ */
++
++/* From: zutil.h,v 1.9 1995/05/03 17:27:12 jloup Exp */
++
++#define _Z_UTIL_H
++
++#include "zlib.h"
++
++#ifndef local
++# define local static
++#endif
++/* compile with -Dlocal if your debugger can't find static symbols */
++
++#define FAR
++
++typedef unsigned char uch;
++typedef uch FAR uchf;
++typedef unsigned short ush;
++typedef ush FAR ushf;
++typedef unsigned long ulg;
++
++extern char *z_errmsg[]; /* indexed by 1-zlib_error */
++
++#define ERR_RETURN(strm,err) return (strm->msg=z_errmsg[1-err], err)
++/* To be used only when the state is known to be valid */
++
++#ifndef NULL
++#define NULL ((void *) 0)
++#endif
++
++ /* common constants */
++
++#define DEFLATED 8
++
++#ifndef DEF_WBITS
++# define DEF_WBITS MAX_WBITS
++#endif
++/* default windowBits for decompression. MAX_WBITS is for compression only */
++
++#if MAX_MEM_LEVEL >= 8
++# define DEF_MEM_LEVEL 8
++#else
++# define DEF_MEM_LEVEL MAX_MEM_LEVEL
++#endif
++/* default memLevel */
++
++#define STORED_BLOCK 0
++#define STATIC_TREES 1
++#define DYN_TREES 2
++/* The three kinds of block type */
++
++#define MIN_MATCH 3
++#define MAX_MATCH 258
++/* The minimum and maximum match lengths */
++
++ /* functions */
++
++#include <linux/string.h>
++#define zmemcpy memcpy
++#define zmemzero(dest, len) memset(dest, 0, len)
++
++/* Diagnostic functions */
++#ifdef DEBUG_ZLIB
++# include <stdio.h>
++# ifndef verbose
++# define verbose 0
++# endif
++# define Assert(cond,msg) {if(!(cond)) z_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
++
++
++typedef uLong (*check_func) OF((uLong check, Bytef *buf, uInt len));
++
++/* voidpf zcalloc OF((voidpf opaque, unsigned items, unsigned size)); */
++/* void zcfree OF((voidpf opaque, voidpf ptr)); */
++
++#define ZALLOC(strm, items, size) \
++ (*((strm)->zalloc))((strm)->opaque, (items), (size))
++#define ZFREE(strm, addr, size) \
++ (*((strm)->zfree))((strm)->opaque, (voidpf)(addr), (size))
++#define TRY_FREE(s, p, n) {if (p) ZFREE(s, p, n);}
++
++/* deflate.h -- internal compression state
++ * Copyright (C) 1995 Jean-loup Gailly
++ * For conditions of distribution and use, see copyright notice in zlib.h
++ */
++
++/* WARNING: this file should *not* be used by applications. It is
++ part of the implementation of the compression library and is
++ subject to change. Applications should only use zlib.h.
++ */
++
++/*+++++*/
++/* infblock.h -- header to use infblock.c
++ * Copyright (C) 1995 Mark Adler
++ * For conditions of distribution and use, see copyright notice in zlib.h
++ */
++
++/* WARNING: this file should *not* be used by applications. It is
++ part of the implementation of the compression library and is
++ subject to change. Applications should only use zlib.h.
++ */
++
++struct inflate_blocks_state;
++typedef struct inflate_blocks_state FAR inflate_blocks_statef;
++
++local inflate_blocks_statef * inflate_blocks_new OF((
++ z_stream *z,
++ check_func c, /* check function */
++ uInt w)); /* window size */
++
++local int inflate_blocks OF((
++ inflate_blocks_statef *,
++ z_stream *,
++ int)); /* initial return code */
++
++local void inflate_blocks_reset OF((
++ inflate_blocks_statef *,
++ z_stream *,
++ uLongf *)); /* check value on output */
++
++local int inflate_blocks_free OF((
++ inflate_blocks_statef *,
++ z_stream *,
++ uLongf *)); /* check value on output */
++
++local int inflate_addhistory OF((
++ inflate_blocks_statef *,
++ z_stream *));
++
++local int inflate_packet_flush OF((
++ inflate_blocks_statef *));
++
++/*+++++*/
++/* inftrees.h -- header to use inftrees.c
++ * Copyright (C) 1995 Mark Adler
++ * For conditions of distribution and use, see copyright notice in zlib.h
++ */
++
++/* WARNING: this file should *not* be used by applications. It is
++ part of the implementation of the compression library and is
++ subject to change. Applications should only use zlib.h.
++ */
++
++/* Huffman code lookup table entry--this entry is four bytes for machines
++ that have 16-bit pointers (e.g. PC's in the small or medium model). */
++
++typedef struct inflate_huft_s FAR inflate_huft;
++
++struct inflate_huft_s {
++ union {
++ struct {
++ Byte Exop; /* number of extra bits or operation */
++ Byte Bits; /* number of bits in this code or subcode */
++ } what;
++ uInt Nalloc; /* number of these allocated here */
++ Bytef *pad; /* pad structure to a power of 2 (4 bytes for */
++ } word; /* 16-bit, 8 bytes for 32-bit machines) */
++ union {
++ uInt Base; /* literal, length base, or distance base */
++ inflate_huft *Next; /* pointer to next level of table */
++ } more;
++};
++
++#ifdef DEBUG_ZLIB
++ local uInt inflate_hufts;
++#endif
++
++local int inflate_trees_bits OF((
++ uIntf *, /* 19 code lengths */
++ uIntf *, /* bits tree desired/actual depth */
++ inflate_huft * FAR *, /* bits tree result */
++ z_stream *)); /* for zalloc, zfree functions */
++
++local int inflate_trees_dynamic OF((
++ uInt, /* number of literal/length codes */
++ uInt, /* number of distance codes */
++ uIntf *, /* that many (total) code lengths */
++ uIntf *, /* literal desired/actual bit depth */
++ uIntf *, /* distance desired/actual bit depth */
++ inflate_huft * FAR *, /* literal/length tree result */
++ inflate_huft * FAR *, /* distance tree result */
++ z_stream *)); /* for zalloc, zfree functions */
++
++local int inflate_trees_fixed OF((
++ uIntf *, /* literal desired/actual bit depth */
++ uIntf *, /* distance desired/actual bit depth */
++ inflate_huft * FAR *, /* literal/length tree result */
++ inflate_huft * FAR *)); /* distance tree result */
++
++local int inflate_trees_free OF((
++ inflate_huft *, /* tables to free */
++ z_stream *)); /* for zfree function */
++
++
++/*+++++*/
++/* infcodes.h -- header to use infcodes.c
++ * Copyright (C) 1995 Mark Adler
++ * For conditions of distribution and use, see copyright notice in zlib.h
++ */
++
++/* WARNING: this file should *not* be used by applications. It is
++ part of the implementation of the compression library and is
++ subject to change. Applications should only use zlib.h.
++ */
++
++struct inflate_codes_state;
++typedef struct inflate_codes_state FAR inflate_codes_statef;
++
++local inflate_codes_statef *inflate_codes_new OF((
++ uInt, uInt,
++ inflate_huft *, inflate_huft *,
++ z_stream *));
++
++local int inflate_codes OF((
++ inflate_blocks_statef *,
++ z_stream *,
++ int));
++
++local void inflate_codes_free OF((
++ inflate_codes_statef *,
++ z_stream *));
++
++
++/*+++++*/
++/* inflate.c -- zlib interface to inflate modules
++ * Copyright (C) 1995 Mark Adler
++ * For conditions of distribution and use, see copyright notice in zlib.h
++ */
++
++/* inflate private state */
++struct internal_state {
++
++ /* mode */
++ enum {
++ METHOD, /* waiting for method byte */
++ FLAG, /* waiting for flag byte */
++ BLOCKS, /* decompressing blocks */
++ CHECK4, /* four check bytes to go */
++ CHECK3, /* three check bytes to go */
++ CHECK2, /* two check bytes to go */
++ CHECK1, /* one check byte to go */
++ DONE, /* finished check, done */
++ BAD} /* got an error--stay here */
++ mode; /* current inflate mode */
++
++ /* mode dependent information */
++ union {
++ uInt method; /* if FLAGS, method byte */
++ struct {
++ uLong was; /* computed check value */
++ uLong need; /* stream check value */
++ } check; /* if CHECK, check values to compare */
++ uInt marker; /* if BAD, inflateSync's marker bytes count */
++ } sub; /* submode */
++
++ /* mode independent information */
++ int nowrap; /* flag for no wrapper */
++ uInt wbits; /* log2(window size) (8..15, defaults to 15) */
++ inflate_blocks_statef
++ *blocks; /* current inflate_blocks state */
++
++};
++
++
++int inflateReset(z)
++z_stream *z;
++{
++ uLong c;
++
++ if (z == Z_NULL || z->state == Z_NULL)
++ return Z_STREAM_ERROR;
++ z->total_in = z->total_out = 0;
++ z->msg = Z_NULL;
++ z->state->mode = z->state->nowrap ? BLOCKS : METHOD;
++ inflate_blocks_reset(z->state->blocks, z, &c);
++ Trace((stderr, "inflate: reset\n"));
++ return Z_OK;
++}
++
++
++int inflateEnd(z)
++z_stream *z;
++{
++ uLong c;
++
++ if (z == Z_NULL || z->state == Z_NULL || z->zfree == Z_NULL)
++ return Z_STREAM_ERROR;
++ if (z->state->blocks != Z_NULL)
++ inflate_blocks_free(z->state->blocks, z, &c);
++ ZFREE(z, z->state, sizeof(struct internal_state));
++ z->state = Z_NULL;
++ Trace((stderr, "inflate: end\n"));
++ return Z_OK;
++}
++
++
++int inflateInit2(z, w)
++z_stream *z;
++int w;
++{
++ /* initialize state */
++ if (z == Z_NULL)
++ return Z_STREAM_ERROR;
++/* if (z->zalloc == Z_NULL) z->zalloc = zcalloc; */
++/* if (z->zfree == Z_NULL) z->zfree = zcfree; */
++ if ((z->state = (struct internal_state FAR *)
++ ZALLOC(z,1,sizeof(struct internal_state))) == Z_NULL)
++ return Z_MEM_ERROR;
++ z->state->blocks = Z_NULL;
++
++ /* handle undocumented nowrap option (no zlib header or check) */
++ z->state->nowrap = 0;
++ if (w < 0)
++ {
++ w = - w;
++ z->state->nowrap = 1;
++ }
++
++ /* set window size */
++ if (w < 8 || w > 15)
++ {
++ inflateEnd(z);
++ return Z_STREAM_ERROR;
++ }
++ z->state->wbits = (uInt)w;
++
++ /* create inflate_blocks state */
++ if ((z->state->blocks =
++ inflate_blocks_new(z, z->state->nowrap ? Z_NULL : adler32, 1 << w))
++ == Z_NULL)
++ {
++ inflateEnd(z);
++ return Z_MEM_ERROR;
++ }
++ Trace((stderr, "inflate: allocated\n"));
++
++ /* reset state */
++ inflateReset(z);
++ return Z_OK;
++}
++
++
++int inflateInit(z)
++z_stream *z;
++{
++ return inflateInit2(z, DEF_WBITS);
++}
++
++
++#define NEEDBYTE {if(z->avail_in==0)goto empty;r=Z_OK;}
++#define NEXTBYTE (z->avail_in--,z->total_in++,*z->next_in++)
++
++int inflate(z, f)
++z_stream *z;
++int f;
++{
++ int r;
++ uInt b;
++
++ if (z == Z_NULL || z->next_in == Z_NULL)
++ return Z_STREAM_ERROR;
++ r = Z_BUF_ERROR;
++ while (1) switch (z->state->mode)
++ {
++ case METHOD:
++ NEEDBYTE
++ if (((z->state->sub.method = NEXTBYTE) & 0xf) != DEFLATED)
++ {
++ z->state->mode = BAD;
++ z->msg = "unknown compression method";
++ z->state->sub.marker = 5; /* can't try inflateSync */
++ break;
++ }
++ if ((z->state->sub.method >> 4) + 8 > z->state->wbits)
++ {
++ z->state->mode = BAD;
++ z->msg = "invalid window size";
++ z->state->sub.marker = 5; /* can't try inflateSync */
++ break;
++ }
++ z->state->mode = FLAG;
++ case FLAG:
++ NEEDBYTE
++ if ((b = NEXTBYTE) & 0x20)
++ {
++ z->state->mode = BAD;
++ z->msg = "invalid reserved bit";
++ z->state->sub.marker = 5; /* can't try inflateSync */
++ break;
++ }
++ if (((z->state->sub.method << 8) + b) % 31)
++ {
++ z->state->mode = BAD;
++ z->msg = "incorrect header check";
++ z->state->sub.marker = 5; /* can't try inflateSync */
++ break;
++ }
++ Trace((stderr, "inflate: zlib header ok\n"));
++ z->state->mode = BLOCKS;
++ case BLOCKS:
++ r = inflate_blocks(z->state->blocks, z, r);
++ if (f == Z_PACKET_FLUSH && z->avail_in == 0 && z->avail_out != 0)
++ r = inflate_packet_flush(z->state->blocks);
++ if (r == Z_DATA_ERROR)
++ {
++ z->state->mode = BAD;
++ z->state->sub.marker = 0; /* can try inflateSync */
++ break;
++ }
++ if (r != Z_STREAM_END)
++ return r;
++ r = Z_OK;
++ inflate_blocks_reset(z->state->blocks, z, &z->state->sub.check.was);
++ if (z->state->nowrap)
++ {
++ z->state->mode = DONE;
++ break;
++ }
++ z->state->mode = CHECK4;
++ case CHECK4:
++ NEEDBYTE
++ z->state->sub.check.need = (uLong)NEXTBYTE << 24;
++ z->state->mode = CHECK3;
++ case CHECK3:
++ NEEDBYTE
++ z->state->sub.check.need += (uLong)NEXTBYTE << 16;
++ z->state->mode = CHECK2;
++ case CHECK2:
++ NEEDBYTE
++ z->state->sub.check.need += (uLong)NEXTBYTE << 8;
++ z->state->mode = CHECK1;
++ case CHECK1:
++ NEEDBYTE
++ z->state->sub.check.need += (uLong)NEXTBYTE;
++
++ if (z->state->sub.check.was != z->state->sub.check.need)
++ {
++ z->state->mode = BAD;
++ z->msg = "incorrect data check";
++ z->state->sub.marker = 5; /* can't try inflateSync */
++ break;
++ }
++ Trace((stderr, "inflate: zlib check ok\n"));
++ z->state->mode = DONE;
++ case DONE:
++ return Z_STREAM_END;
++ case BAD:
++ return Z_DATA_ERROR;
++ default:
++ return Z_STREAM_ERROR;
++ }
++
++ empty:
++ if (f != Z_PACKET_FLUSH)
++ return r;
++ z->state->mode = BAD;
++ z->state->sub.marker = 0; /* can try inflateSync */
++ return Z_DATA_ERROR;
++}
++
++/*
++ * This subroutine adds the data at next_in/avail_in to the output history
++ * without performing any output. The output buffer must be "caught up";
++ * i.e. no pending output (hence s->read equals s->write), and the state must
++ * be BLOCKS (i.e. we should be willing to see the start of a series of
++ * BLOCKS). On exit, the output will also be caught up, and the checksum
++ * will have been updated if need be.
++ */
++
++int inflateIncomp(z)
++z_stream *z;
++{
++ if (z->state->mode != BLOCKS)
++ return Z_DATA_ERROR;
++ return inflate_addhistory(z->state->blocks, z);
++}
++
++
++int inflateSync(z)
++z_stream *z;
++{
++ uInt n; /* number of bytes to look at */
++ Bytef *p; /* pointer to bytes */
++ uInt m; /* number of marker bytes found in a row */
++ uLong r, w; /* temporaries to save total_in and total_out */
++
++ /* set up */
++ if (z == Z_NULL || z->state == Z_NULL)
++ return Z_STREAM_ERROR;
++ if (z->state->mode != BAD)
++ {
++ z->state->mode = BAD;
++ z->state->sub.marker = 0;
++ }
++ if ((n = z->avail_in) == 0)
++ return Z_BUF_ERROR;
++ p = z->next_in;
++ m = z->state->sub.marker;
++
++ /* search */
++ while (n && m < 4)
++ {
++ if (*p == (Byte)(m < 2 ? 0 : 0xff))
++ m++;
++ else if (*p)
++ m = 0;
++ else
++ m = 4 - m;
++ p++, n--;
++ }
++
++ /* restore */
++ z->total_in += p - z->next_in;
++ z->next_in = p;
++ z->avail_in = n;
++ z->state->sub.marker = m;
++
++ /* return no joy or set up to restart on a new block */
++ if (m != 4)
++ return Z_DATA_ERROR;
++ r = z->total_in; w = z->total_out;
++ inflateReset(z);
++ z->total_in = r; z->total_out = w;
++ z->state->mode = BLOCKS;
++ return Z_OK;
++}
++
++#undef NEEDBYTE
++#undef NEXTBYTE
++
++/*+++++*/
++/* infutil.h -- types and macros common to blocks and codes
++ * Copyright (C) 1995 Mark Adler
++ * For conditions of distribution and use, see copyright notice in zlib.h
++ */
++
++/* WARNING: this file should *not* be used by applications. It is
++ part of the implementation of the compression library and is
++ subject to change. Applications should only use zlib.h.
++ */
++
++/* inflate blocks semi-private state */
++struct inflate_blocks_state {
++
++ /* mode */
++ enum {
++ TYPE, /* get type bits (3, including end bit) */
++ LENS, /* get lengths for stored */
++ STORED, /* processing stored block */
++ TABLE, /* get table lengths */
++ BTREE, /* get bit lengths tree for a dynamic block */
++ DTREE, /* get length, distance trees for a dynamic block */
++ CODES, /* processing fixed or dynamic block */
++ DRY, /* output remaining window bytes */
++ DONEB, /* finished last block, done */
++ BADB} /* got a data error--stuck here */
++ mode; /* current inflate_block mode */
++
++ /* mode dependent information */
++ union {
++ uInt left; /* if STORED, bytes left to copy */
++ struct {
++ uInt table; /* table lengths (14 bits) */
++ uInt index; /* index into blens (or border) */
++ uIntf *blens; /* bit lengths of codes */
++ uInt bb; /* bit length tree depth */
++ inflate_huft *tb; /* bit length decoding tree */
++ int nblens; /* # elements allocated at blens */
++ } trees; /* if DTREE, decoding info for trees */
++ struct {
++ inflate_huft *tl, *td; /* trees to free */
++ inflate_codes_statef
++ *codes;
++ } decode; /* if CODES, current state */
++ } sub; /* submode */
++ uInt last; /* true if this block is the last block */
++
++ /* mode independent information */
++ uInt bitk; /* bits in bit buffer */
++ uLong bitb; /* bit buffer */
++ Bytef *window; /* sliding window */
++ Bytef *end; /* one byte after sliding window */
++ Bytef *read; /* window read pointer */
++ Bytef *write; /* window write pointer */
++ check_func checkfn; /* check function */
++ uLong check; /* check on output */
++
++};
++
++
++/* defines for inflate input/output */
++/* update pointers and return */
++#define UPDBITS {s->bitb=b;s->bitk=k;}
++#define UPDIN {z->avail_in=n;z->total_in+=p-z->next_in;z->next_in=p;}
++#define UPDOUT {s->write=q;}
++#define UPDATE {UPDBITS UPDIN UPDOUT}
++#define LEAVE {UPDATE return inflate_flush(s,z,r);}
++/* get bytes and bits */
++#define LOADIN {p=z->next_in;n=z->avail_in;b=s->bitb;k=s->bitk;}
++#define NEEDBYTE {if(n)r=Z_OK;else LEAVE}
++#define NEXTBYTE (n--,*p++)
++#define NEEDBITS(j) {while(k<(j)){NEEDBYTE;b|=((uLong)NEXTBYTE)<<k;k+=8;}}
++#define DUMPBITS(j) {b>>=(j);k-=(j);}
++/* output bytes */
++#define WAVAIL (q<s->read?s->read-q-1:s->end-q)
++#define LOADOUT {q=s->write;m=WAVAIL;}
++#define WRAP {if(q==s->end&&s->read!=s->window){q=s->window;m=WAVAIL;}}
++#define FLUSH {UPDOUT r=inflate_flush(s,z,r); LOADOUT}
++#define NEEDOUT {if(m==0){WRAP if(m==0){FLUSH WRAP if(m==0) LEAVE}}r=Z_OK;}
++#define OUTBYTE(a) {*q++=(Byte)(a);m--;}
++/* load local pointers */
++#define LOAD {LOADIN LOADOUT}
++
++/*
++ * The IBM 150 firmware munges the data right after _etext[]. This
++ * protects it. -- Cort
++ */
++local uInt protect_mask[] = {0, 0, 0, 0, 0, 0, 0, 0, 0 ,0 ,0 ,0};
++/* And'ing with mask[n] masks the lower n bits */
++local uInt inflate_mask[] = {
++ 0x0000,
++ 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff,
++ 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff
++};
++
++/* copy as much as possible from the sliding window to the output area */
++local int inflate_flush OF((
++ inflate_blocks_statef *,
++ z_stream *,
++ int));
++
++/*+++++*/
++/* inffast.h -- header to use inffast.c
++ * Copyright (C) 1995 Mark Adler
++ * For conditions of distribution and use, see copyright notice in zlib.h
++ */
++
++/* WARNING: this file should *not* be used by applications. It is
++ part of the implementation of the compression library and is
++ subject to change. Applications should only use zlib.h.
++ */
++
++local int inflate_fast OF((
++ uInt,
++ uInt,
++ inflate_huft *,
++ inflate_huft *,
++ inflate_blocks_statef *,
++ z_stream *));
++
++
++/*+++++*/
++/* infblock.c -- interpret and process block types to last block
++ * Copyright (C) 1995 Mark Adler
++ * For conditions of distribution and use, see copyright notice in zlib.h
++ */
++
++/* Table for deflate from PKZIP's appnote.txt. */
++local uInt border[] = { /* Order of the bit length code lengths */
++ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15};
++
++/*
++ Notes beyond the 1.93a appnote.txt:
++
++ 1. Distance pointers never point before the beginning of the output
++ stream.
++ 2. Distance pointers can point back across blocks, up to 32k away.
++ 3. There is an implied maximum of 7 bits for the bit length table and
++ 15 bits for the actual data.
++ 4. If only one code exists, then it is encoded using one bit. (Zero
++ would be more efficient, but perhaps a little confusing.) If two
++ codes exist, they are coded using one bit each (0 and 1).
++ 5. There is no way of sending zero distance codes--a dummy must be
++ sent if there are none. (History: a pre 2.0 version of PKZIP would
++ store blocks with no distance codes, but this was discovered to be
++ too harsh a criterion.) Valid only for 1.93a. 2.04c does allow
++ zero distance codes, which is sent as one code of zero bits in
++ length.
++ 6. There are up to 286 literal/length codes. Code 256 represents the
++ end-of-block. Note however that the static length tree defines
++ 288 codes just to fill out the Huffman codes. Codes 286 and 287
++ cannot be used though, since there is no length base or extra bits
++ defined for them. Similarily, there are up to 30 distance codes.
++ However, static trees define 32 codes (all 5 bits) to fill out the
++ Huffman codes, but the last two had better not show up in the data.
++ 7. Unzip can check dynamic Huffman blocks for complete code sets.
++ The exception is that a single code would not be complete (see #4).
++ 8. The five bits following the block type is really the number of
++ literal codes sent minus 257.
++ 9. Length codes 8,16,16 are interpreted as 13 length codes of 8 bits
++ (1+6+6). Therefore, to output three times the length, you output
++ three codes (1+1+1), whereas to output four times the same length,
++ you only need two codes (1+3). Hmm.
++ 10. In the tree reconstruction algorithm, Code = Code + Increment
++ only if BitLength(i) is not zero. (Pretty obvious.)
++ 11. Correction: 4 Bits: # of Bit Length codes - 4 (4 - 19)
++ 12. Note: length code 284 can represent 227-258, but length code 285
++ really is 258. The last length deserves its own, short code
++ since it gets used a lot in very redundant files. The length
++ 258 is special since 258 - 3 (the min match length) is 255.
++ 13. The literal/length and distance code bit lengths are read as a
++ single stream of lengths. It is possible (and advantageous) for
++ a repeat code (16, 17, or 18) to go across the boundary between
++ the two sets of lengths.
++ */
++
++
++local void inflate_blocks_reset(s, z, c)
++inflate_blocks_statef *s;
++z_stream *z;
++uLongf *c;
++{
++ if (s->checkfn != Z_NULL)
++ *c = s->check;
++ if (s->mode == BTREE || s->mode == DTREE)
++ ZFREE(z, s->sub.trees.blens, s->sub.trees.nblens * sizeof(uInt));
++ if (s->mode == CODES)
++ {
++ inflate_codes_free(s->sub.decode.codes, z);
++ inflate_trees_free(s->sub.decode.td, z);
++ inflate_trees_free(s->sub.decode.tl, z);
++ }
++ s->mode = TYPE;
++ s->bitk = 0;
++ s->bitb = 0;
++ s->read = s->write = s->window;
++ if (s->checkfn != Z_NULL)
++ s->check = (*s->checkfn)(0L, Z_NULL, 0);
++ Trace((stderr, "inflate: blocks reset\n"));
++}
++
++
++local inflate_blocks_statef *inflate_blocks_new(z, c, w)
++z_stream *z;
++check_func c;
++uInt w;
++{
++ inflate_blocks_statef *s;
++
++ if ((s = (inflate_blocks_statef *)ZALLOC
++ (z,1,sizeof(struct inflate_blocks_state))) == Z_NULL)
++ return s;
++ if ((s->window = (Bytef *)ZALLOC(z, 1, w)) == Z_NULL)
++ {
++ ZFREE(z, s, sizeof(struct inflate_blocks_state));
++ return Z_NULL;
++ }
++ s->end = s->window + w;
++ s->checkfn = c;
++ s->mode = TYPE;
++ Trace((stderr, "inflate: blocks allocated\n"));
++ inflate_blocks_reset(s, z, &s->check);
++ return s;
++}
++
++
++local int inflate_blocks(s, z, r)
++inflate_blocks_statef *s;
++z_stream *z;
++int r;
++{
++ uInt t; /* temporary storage */
++ uLong b; /* bit buffer */
++ uInt k; /* bits in bit buffer */
++ Bytef *p; /* input data pointer */
++ uInt n; /* bytes available there */
++ Bytef *q; /* output window write pointer */
++ uInt m; /* bytes to end of window or read pointer */
++
++ /* copy input/output information to locals (UPDATE macro restores) */
++ LOAD
++
++ /* process input based on current state */
++ while (1) switch (s->mode)
++ {
++ case TYPE:
++ NEEDBITS(3)
++ t = (uInt)b & 7;
++ s->last = t & 1;
++ switch (t >> 1)
++ {
++ case 0: /* stored */
++ Trace((stderr, "inflate: stored block%s\n",
++ s->last ? " (last)" : ""));
++ DUMPBITS(3)
++ t = k & 7; /* go to byte boundary */
++ DUMPBITS(t)
++ s->mode = LENS; /* get length of stored block */
++ break;
++ case 1: /* fixed */
++ Trace((stderr, "inflate: fixed codes block%s\n",
++ s->last ? " (last)" : ""));
++ {
++ uInt bl, bd;
++ inflate_huft *tl, *td;
++
++ inflate_trees_fixed(&bl, &bd, &tl, &td);
++ s->sub.decode.codes = inflate_codes_new(bl, bd, tl, td, z);
++ if (s->sub.decode.codes == Z_NULL)
++ {
++ r = Z_MEM_ERROR;
++ LEAVE
++ }
++ s->sub.decode.tl = Z_NULL; /* don't try to free these */
++ s->sub.decode.td = Z_NULL;
++ }
++ DUMPBITS(3)
++ s->mode = CODES;
++ break;
++ case 2: /* dynamic */
++ Trace((stderr, "inflate: dynamic codes block%s\n",
++ s->last ? " (last)" : ""));
++ DUMPBITS(3)
++ s->mode = TABLE;
++ break;
++ case 3: /* illegal */
++ DUMPBITS(3)
++ s->mode = BADB;
++ z->msg = "invalid block type";
++ r = Z_DATA_ERROR;
++ LEAVE
++ }
++ break;
++ case LENS:
++ NEEDBITS(32)
++ if (((~b) >> 16) != (b & 0xffff))
++ {
++ s->mode = BADB;
++ z->msg = "invalid stored block lengths";
++ r = Z_DATA_ERROR;
++ LEAVE
++ }
++ s->sub.left = (uInt)b & 0xffff;
++ b = k = 0; /* dump bits */
++ Tracev((stderr, "inflate: stored length %u\n", s->sub.left));
++ s->mode = s->sub.left ? STORED : TYPE;
++ break;
++ case STORED:
++ if (n == 0)
++ LEAVE
++ NEEDOUT
++ t = s->sub.left;
++ if (t > n) t = n;
++ if (t > m) t = m;
++ zmemcpy(q, p, t);
++ p += t; n -= t;
++ q += t; m -= t;
++ if ((s->sub.left -= t) != 0)
++ break;
++ Tracev((stderr, "inflate: stored end, %lu total out\n",
++ z->total_out + (q >= s->read ? q - s->read :
++ (s->end - s->read) + (q - s->window))));
++ s->mode = s->last ? DRY : TYPE;
++ break;
++ case TABLE:
++ NEEDBITS(14)
++ s->sub.trees.table = t = (uInt)b & 0x3fff;
++#ifndef PKZIP_BUG_WORKAROUND
++ if ((t & 0x1f) > 29 || ((t >> 5) & 0x1f) > 29)
++ {
++ s->mode = BADB;
++ z->msg = "too many length or distance symbols";
++ r = Z_DATA_ERROR;
++ LEAVE
++ }
++#endif
++ t = 258 + (t & 0x1f) + ((t >> 5) & 0x1f);
++ if (t < 19)
++ t = 19;
++ if ((s->sub.trees.blens = (uIntf*)ZALLOC(z, t, sizeof(uInt))) == Z_NULL)
++ {
++ r = Z_MEM_ERROR;
++ LEAVE
++ }
++ s->sub.trees.nblens = t;
++ DUMPBITS(14)
++ s->sub.trees.index = 0;
++ Tracev((stderr, "inflate: table sizes ok\n"));
++ s->mode = BTREE;
++ case BTREE:
++ while (s->sub.trees.index < 4 + (s->sub.trees.table >> 10))
++ {
++ NEEDBITS(3)
++ s->sub.trees.blens[border[s->sub.trees.index++]] = (uInt)b & 7;
++ DUMPBITS(3)
++ }
++ while (s->sub.trees.index < 19)
++ s->sub.trees.blens[border[s->sub.trees.index++]] = 0;
++ s->sub.trees.bb = 7;
++ t = inflate_trees_bits(s->sub.trees.blens, &s->sub.trees.bb,
++ &s->sub.trees.tb, z);
++ if (t != Z_OK)
++ {
++ r = t;
++ if (r == Z_DATA_ERROR)
++ s->mode = BADB;
++ LEAVE
++ }
++ s->sub.trees.index = 0;
++ Tracev((stderr, "inflate: bits tree ok\n"));
++ s->mode = DTREE;
++ case DTREE:
++ while (t = s->sub.trees.table,
++ s->sub.trees.index < 258 + (t & 0x1f) + ((t >> 5) & 0x1f))
++ {
++ inflate_huft *h;
++ uInt i, j, c;
++
++ t = s->sub.trees.bb;
++ NEEDBITS(t)
++ h = s->sub.trees.tb + ((uInt)b & inflate_mask[t]);
++ t = h->word.what.Bits;
++ c = h->more.Base;
++ if (c < 16)
++ {
++ DUMPBITS(t)
++ s->sub.trees.blens[s->sub.trees.index++] = c;
++ }
++ else /* c == 16..18 */
++ {
++ i = c == 18 ? 7 : c - 14;
++ j = c == 18 ? 11 : 3;
++ NEEDBITS(t + i)
++ DUMPBITS(t)
++ j += (uInt)b & inflate_mask[i];
++ DUMPBITS(i)
++ i = s->sub.trees.index;
++ t = s->sub.trees.table;
++ if (i + j > 258 + (t & 0x1f) + ((t >> 5) & 0x1f) ||
++ (c == 16 && i < 1))
++ {
++ s->mode = BADB;
++ z->msg = "invalid bit length repeat";
++ r = Z_DATA_ERROR;
++ LEAVE
++ }
++ c = c == 16 ? s->sub.trees.blens[i - 1] : 0;
++ do {
++ s->sub.trees.blens[i++] = c;
++ } while (--j);
++ s->sub.trees.index = i;
++ }
++ }
++ inflate_trees_free(s->sub.trees.tb, z);
++ s->sub.trees.tb = Z_NULL;
++ {
++ uInt bl, bd;
++ inflate_huft *tl, *td;
++ inflate_codes_statef *c;
++
++ bl = 9; /* must be <= 9 for lookahead assumptions */
++ bd = 6; /* must be <= 9 for lookahead assumptions */
++ t = s->sub.trees.table;
++ t = inflate_trees_dynamic(257 + (t & 0x1f), 1 + ((t >> 5) & 0x1f),
++ s->sub.trees.blens, &bl, &bd, &tl, &td, z);
++ if (t != Z_OK)
++ {
++ if (t == (uInt)Z_DATA_ERROR)
++ s->mode = BADB;
++ r = t;
++ LEAVE
++ }
++ Tracev((stderr, "inflate: trees ok\n"));
++ if ((c = inflate_codes_new(bl, bd, tl, td, z)) == Z_NULL)
++ {
++ inflate_trees_free(td, z);
++ inflate_trees_free(tl, z);
++ r = Z_MEM_ERROR;
++ LEAVE
++ }
++ ZFREE(z, s->sub.trees.blens, s->sub.trees.nblens * sizeof(uInt));
++ s->sub.decode.codes = c;
++ s->sub.decode.tl = tl;
++ s->sub.decode.td = td;
++ }
++ s->mode = CODES;
++ case CODES:
++ UPDATE
++ if ((r = inflate_codes(s, z, r)) != Z_STREAM_END)
++ return inflate_flush(s, z, r);
++ r = Z_OK;
++ inflate_codes_free(s->sub.decode.codes, z);
++ inflate_trees_free(s->sub.decode.td, z);
++ inflate_trees_free(s->sub.decode.tl, z);
++ LOAD
++ Tracev((stderr, "inflate: codes end, %lu total out\n",
++ z->total_out + (q >= s->read ? q - s->read :
++ (s->end - s->read) + (q - s->window))));
++ if (!s->last)
++ {
++ s->mode = TYPE;
++ break;
++ }
++ if (k > 7) /* return unused byte, if any */
++ {
++ Assert(k < 16, "inflate_codes grabbed too many bytes")
++ k -= 8;
++ n++;
++ p--; /* can always return one */
++ }
++ s->mode = DRY;
++ case DRY:
++ FLUSH
++ if (s->read != s->write)
++ LEAVE
++ s->mode = DONEB;
++ case DONEB:
++ r = Z_STREAM_END;
++ LEAVE
++ case BADB:
++ r = Z_DATA_ERROR;
++ LEAVE
++ default:
++ r = Z_STREAM_ERROR;
++ LEAVE
++ }
++}
++
++
++local int inflate_blocks_free(s, z, c)
++inflate_blocks_statef *s;
++z_stream *z;
++uLongf *c;
++{
++ inflate_blocks_reset(s, z, c);
++ ZFREE(z, s->window, s->end - s->window);
++ ZFREE(z, s, sizeof(struct inflate_blocks_state));
++ Trace((stderr, "inflate: blocks freed\n"));
++ return Z_OK;
++}
++
++/*
++ * This subroutine adds the data at next_in/avail_in to the output history
++ * without performing any output. The output buffer must be "caught up";
++ * i.e. no pending output (hence s->read equals s->write), and the state must
++ * be BLOCKS (i.e. we should be willing to see the start of a series of
++ * BLOCKS). On exit, the output will also be caught up, and the checksum
++ * will have been updated if need be.
++ */
++local int inflate_addhistory(s, z)
++inflate_blocks_statef *s;
++z_stream *z;
++{
++ uLong b; /* bit buffer */ /* NOT USED HERE */
++ uInt k; /* bits in bit buffer */ /* NOT USED HERE */
++ uInt t; /* temporary storage */
++ Bytef *p; /* input data pointer */
++ uInt n; /* bytes available there */
++ Bytef *q; /* output window write pointer */
++ uInt m; /* bytes to end of window or read pointer */
++
++ if (s->read != s->write)
++ return Z_STREAM_ERROR;
++ if (s->mode != TYPE)
++ return Z_DATA_ERROR;
++
++ /* we're ready to rock */
++ LOAD
++ /* while there is input ready, copy to output buffer, moving
++ * pointers as needed.
++ */
++ while (n) {
++ t = n; /* how many to do */
++ /* is there room until end of buffer? */
++ if (t > m) t = m;
++ /* update check information */
++ if (s->checkfn != Z_NULL)
++ s->check = (*s->checkfn)(s->check, q, t);
++ zmemcpy(q, p, t);
++ q += t;
++ p += t;
++ n -= t;
++ z->total_out += t;
++ s->read = q; /* drag read pointer forward */
++/* WRAP */ /* expand WRAP macro by hand to handle s->read */
++ if (q == s->end) {
++ s->read = q = s->window;
++ m = WAVAIL;
++ }
++ }
++ UPDATE
++ return Z_OK;
++}
++
++
++/*
++ * At the end of a Deflate-compressed PPP packet, we expect to have seen
++ * a `stored' block type value but not the (zero) length bytes.
++ */
++local int inflate_packet_flush(s)
++ inflate_blocks_statef *s;
++{
++ if (s->mode != LENS)
++ return Z_DATA_ERROR;
++ s->mode = TYPE;
++ return Z_OK;
++}
++
++
++/*+++++*/
++/* inftrees.c -- generate Huffman trees for efficient decoding
++ * Copyright (C) 1995 Mark Adler
++ * For conditions of distribution and use, see copyright notice in zlib.h
++ */
++
++/* simplify the use of the inflate_huft type with some defines */
++#define base more.Base
++#define next more.Next
++#define exop word.what.Exop
++#define bits word.what.Bits
++
++
++local int huft_build OF((
++ uIntf *, /* code lengths in bits */
++ uInt, /* number of codes */
++ uInt, /* number of "simple" codes */
++ uIntf *, /* list of base values for non-simple codes */
++ uIntf *, /* list of extra bits for non-simple codes */
++ inflate_huft * FAR*,/* result: starting table */
++ uIntf *, /* maximum lookup bits (returns actual) */
++ z_stream *)); /* for zalloc function */
++
++local voidpf falloc OF((
++ voidpf, /* opaque pointer (not used) */
++ uInt, /* number of items */
++ uInt)); /* size of item */
++
++local void ffree OF((
++ voidpf q, /* opaque pointer (not used) */
++ voidpf p, /* what to free (not used) */
++ uInt n)); /* number of bytes (not used) */
++
++/* Tables for deflate from PKZIP's appnote.txt. */
++local uInt cplens[] = { /* Copy lengths for literal codes 257..285 */
++ 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
++ 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0};
++ /* actually lengths - 2; also see note #13 above about 258 */
++local uInt cplext[] = { /* Extra bits for literal codes 257..285 */
++ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2,
++ 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 192, 192}; /* 192==invalid */
++local uInt cpdist[] = { /* Copy offsets for distance codes 0..29 */
++ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
++ 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
++ 8193, 12289, 16385, 24577};
++local uInt cpdext[] = { /* Extra bits for distance codes */
++ 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6,
++ 7, 7, 8, 8, 9, 9, 10, 10, 11, 11,
++ 12, 12, 13, 13};
++
++/*
++ Huffman code decoding is performed using a multi-level table lookup.
++ The fastest way to decode is to simply build a lookup table whose
++ size is determined by the longest code. However, the time it takes
++ to build this table can also be a factor if the data being decoded
++ is not very long. The most common codes are necessarily the
++ shortest codes, so those codes dominate the decoding time, and hence
++ the speed. The idea is you can have a shorter table that decodes the
++ shorter, more probable codes, and then point to subsidiary tables for
++ the longer codes. The time it costs to decode the longer codes is
++ then traded against the time it takes to make longer tables.
++
++ This results of this trade are in the variables lbits and dbits
++ below. lbits is the number of bits the first level table for literal/
++ length codes can decode in one step, and dbits is the same thing for
++ the distance codes. Subsequent tables are also less than or equal to
++ those sizes. These values may be adjusted either when all of the
++ codes are shorter than that, in which case the longest code length in
++ bits is used, or when the shortest code is *longer* than the requested
++ table size, in which case the length of the shortest code in bits is
++ used.
++
++ There are two different values for the two tables, since they code a
++ different number of possibilities each. The literal/length table
++ codes 286 possible values, or in a flat code, a little over eight
++ bits. The distance table codes 30 possible values, or a little less
++ than five bits, flat. The optimum values for speed end up being
++ about one bit more than those, so lbits is 8+1 and dbits is 5+1.
++ The optimum values may differ though from machine to machine, and
++ possibly even between compilers. Your mileage may vary.
++ */
++
++
++/* If BMAX needs to be larger than 16, then h and x[] should be uLong. */
++#define BMAX 15 /* maximum bit length of any code */
++#define N_MAX 288 /* maximum number of codes in any set */
++
++#ifdef DEBUG_ZLIB
++ uInt inflate_hufts;
++#endif
++
++local int huft_build(b, n, s, d, e, t, m, zs)
++uIntf *b; /* code lengths in bits (all assumed <= BMAX) */
++uInt n; /* number of codes (assumed <= N_MAX) */
++uInt s; /* number of simple-valued codes (0..s-1) */
++uIntf *d; /* list of base values for non-simple codes */
++uIntf *e; /* list of extra bits for non-simple codes */
++inflate_huft * FAR *t; /* result: starting table */
++uIntf *m; /* maximum lookup bits, returns actual */
++z_stream *zs; /* for zalloc function */
++/* Given a list of code lengths and a maximum table size, make a set of
++ tables to decode that set of codes. Return Z_OK on success, Z_BUF_ERROR
++ if the given code set is incomplete (the tables are still built in this
++ case), Z_DATA_ERROR if the input is invalid (all zero length codes or an
++ over-subscribed set of lengths), or Z_MEM_ERROR if not enough memory. */
++{
++
++ uInt a; /* counter for codes of length k */
++ uInt c[BMAX+1]; /* bit length count table */
++ uInt f; /* i repeats in table every f entries */
++ int g; /* maximum code length */
++ int h; /* table level */
++ register uInt i; /* counter, current code */
++ register uInt j; /* counter */
++ register int k; /* number of bits in current code */
++ int l; /* bits per table (returned in m) */
++ register uIntf *p; /* pointer into c[], b[], or v[] */
++ inflate_huft *q; /* points to current table */
++ struct inflate_huft_s r; /* table entry for structure assignment */
++ inflate_huft *u[BMAX]; /* table stack */
++ uInt v[N_MAX]; /* values in order of bit length */
++ register int w; /* bits before this table == (l * h) */
++ uInt x[BMAX+1]; /* bit offsets, then code stack */
++ uIntf *xp; /* pointer into x */
++ int y; /* number of dummy codes added */
++ uInt z; /* number of entries in current table */
++
++
++ /* Generate counts for each bit length */
++ p = c;
++#define C0 *p++ = 0;
++#define C2 C0 C0 C0 C0
++#define C4 C2 C2 C2 C2
++ C4 /* clear c[]--assume BMAX+1 is 16 */
++ p = b; i = n;
++ do {
++ c[*p++]++; /* assume all entries <= BMAX */
++ } while (--i);
++ if (c[0] == n) /* null input--all zero length codes */
++ {
++ *t = (inflate_huft *)Z_NULL;
++ *m = 0;
++ return Z_OK;
++ }
++
++
++ /* Find minimum and maximum length, bound *m by those */
++ l = *m;
++ for (j = 1; j <= BMAX; j++)
++ if (c[j])
++ break;
++ k = j; /* minimum code length */
++ if ((uInt)l < j)
++ l = j;
++ for (i = BMAX; i; i--)
++ if (c[i])
++ break;
++ g = i; /* maximum code length */
++ if ((uInt)l > i)
++ l = i;
++ *m = l;
++
++
++ /* Adjust last length count to fill out codes, if needed */
++ for (y = 1 << j; j < i; j++, y <<= 1)
++ if ((y -= c[j]) < 0)
++ return Z_DATA_ERROR;
++ if ((y -= c[i]) < 0)
++ return Z_DATA_ERROR;
++ c[i] += y;
++
++
++ /* Generate starting offsets into the value table for each length */
++ x[1] = j = 0;
++ p = c + 1; xp = x + 2;
++ while (--i) { /* note that i == g from above */
++ *xp++ = (j += *p++);
++ }
++
++
++ /* Make a table of values in order of bit lengths */
++ p = b; i = 0;
++ do {
++ if ((j = *p++) != 0)
++ v[x[j]++] = i;
++ } while (++i < n);
++
++
++ /* Generate the Huffman codes and for each, make the table entries */
++ x[0] = i = 0; /* first Huffman code is zero */
++ p = v; /* grab values in bit order */
++ h = -1; /* no tables yet--level -1 */
++ w = -l; /* bits decoded == (l * h) */
++ u[0] = (inflate_huft *)Z_NULL; /* just to keep compilers happy */
++ q = (inflate_huft *)Z_NULL; /* ditto */
++ z = 0; /* ditto */
++
++ /* go through the bit lengths (k already is bits in shortest code) */
++ for (; k <= g; k++)
++ {
++ a = c[k];
++ while (a--)
++ {
++ /* here i is the Huffman code of length k bits for value *p */
++ /* make tables up to required level */
++ while (k > w + l)
++ {
++ h++;
++ w += l; /* previous table always l bits */
++
++ /* compute minimum size table less than or equal to l bits */
++ z = (z = g - w) > (uInt)l ? l : z; /* table size upper limit */
++ if ((f = 1 << (j = k - w)) > a + 1) /* try a k-w bit table */
++ { /* too few codes for k-w bit table */
++ f -= a + 1; /* deduct codes from patterns left */
++ xp = c + k;
++ if (j < z)
++ while (++j < z) /* try smaller tables up to z bits */
++ {
++ if ((f <<= 1) <= *++xp)
++ break; /* enough codes to use up j bits */
++ f -= *xp; /* else deduct codes from patterns */
++ }
++ }
++ z = 1 << j; /* table entries for j-bit table */
++
++ /* allocate and link in new table */
++ if ((q = (inflate_huft *)ZALLOC
++ (zs,z + 1,sizeof(inflate_huft))) == Z_NULL)
++ {
++ if (h)
++ inflate_trees_free(u[0], zs);
++ return Z_MEM_ERROR; /* not enough memory */
++ }
++ q->word.Nalloc = z + 1;
++#ifdef DEBUG_ZLIB
++ inflate_hufts += z + 1;
++#endif
++ *t = q + 1; /* link to list for huft_free() */
++ *(t = &(q->next)) = Z_NULL;
++ u[h] = ++q; /* table starts after link */
++
++ /* connect to last table, if there is one */
++ if (h)
++ {
++ x[h] = i; /* save pattern for backing up */
++ r.bits = (Byte)l; /* bits to dump before this table */
++ r.exop = (Byte)j; /* bits in this table */
++ r.next = q; /* pointer to this table */
++ j = i >> (w - l); /* (get around Turbo C bug) */
++ u[h-1][j] = r; /* connect to last table */
++ }
++ }
++
++ /* set up table entry in r */
++ r.bits = (Byte)(k - w);
++ if (p >= v + n)
++ r.exop = 128 + 64; /* out of values--invalid code */
++ else if (*p < s)
++ {
++ r.exop = (Byte)(*p < 256 ? 0 : 32 + 64); /* 256 is end-of-block */
++ r.base = *p++; /* simple code is just the value */
++ }
++ else
++ {
++ r.exop = (Byte)e[*p - s] + 16 + 64; /* non-simple--look up in lists */
++ r.base = d[*p++ - s];
++ }
++
++ /* fill code-like entries with r */
++ f = 1 << (k - w);
++ for (j = i >> w; j < z; j += f)
++ q[j] = r;
++
++ /* backwards increment the k-bit code i */
++ for (j = 1 << (k - 1); i & j; j >>= 1)
++ i ^= j;
++ i ^= j;
++
++ /* backup over finished tables */
++ while ((i & ((1 << w) - 1)) != x[h])
++ {
++ h--; /* don't need to update q */
++ w -= l;
++ }
++ }
++ }
++
++
++ /* Return Z_BUF_ERROR if we were given an incomplete table */
++ return y != 0 && g != 1 ? Z_BUF_ERROR : Z_OK;
++}
++
++
++local int inflate_trees_bits(c, bb, tb, z)
++uIntf *c; /* 19 code lengths */
++uIntf *bb; /* bits tree desired/actual depth */
++inflate_huft * FAR *tb; /* bits tree result */
++z_stream *z; /* for zfree function */
++{
++ int r;
++
++ r = huft_build(c, 19, 19, (uIntf*)Z_NULL, (uIntf*)Z_NULL, tb, bb, z);
++ if (r == Z_DATA_ERROR)
++ z->msg = "oversubscribed dynamic bit lengths tree";
++ else if (r == Z_BUF_ERROR)
++ {
++ inflate_trees_free(*tb, z);
++ z->msg = "incomplete dynamic bit lengths tree";
++ r = Z_DATA_ERROR;
++ }
++ return r;
++}
++
++
++local int inflate_trees_dynamic(nl, nd, c, bl, bd, tl, td, z)
++uInt nl; /* number of literal/length codes */
++uInt nd; /* number of distance codes */
++uIntf *c; /* that many (total) code lengths */
++uIntf *bl; /* literal desired/actual bit depth */
++uIntf *bd; /* distance desired/actual bit depth */
++inflate_huft * FAR *tl; /* literal/length tree result */
++inflate_huft * FAR *td; /* distance tree result */
++z_stream *z; /* for zfree function */
++{
++ int r;
++
++ /* build literal/length tree */
++ if ((r = huft_build(c, nl, 257, cplens, cplext, tl, bl, z)) != Z_OK)
++ {
++ if (r == Z_DATA_ERROR)
++ z->msg = "oversubscribed literal/length tree";
++ else if (r == Z_BUF_ERROR)
++ {
++ inflate_trees_free(*tl, z);
++ z->msg = "incomplete literal/length tree";
++ r = Z_DATA_ERROR;
++ }
++ return r;
++ }
++
++ /* build distance tree */
++ if ((r = huft_build(c + nl, nd, 0, cpdist, cpdext, td, bd, z)) != Z_OK)
++ {
++ if (r == Z_DATA_ERROR)
++ z->msg = "oversubscribed literal/length tree";
++ else if (r == Z_BUF_ERROR) {
++#ifdef PKZIP_BUG_WORKAROUND
++ r = Z_OK;
++ }
++#else
++ inflate_trees_free(*td, z);
++ z->msg = "incomplete literal/length tree";
++ r = Z_DATA_ERROR;
++ }
++ inflate_trees_free(*tl, z);
++ return r;
++#endif
++ }
++
++ /* done */
++ return Z_OK;
++}
++
++
++/* build fixed tables only once--keep them here */
++local int fixed_lock = 0;
++local int fixed_built = 0;
++#define FIXEDH 530 /* number of hufts used by fixed tables */
++local uInt fixed_left = FIXEDH;
++local inflate_huft fixed_mem[FIXEDH];
++local uInt fixed_bl;
++local uInt fixed_bd;
++local inflate_huft *fixed_tl;
++local inflate_huft *fixed_td;
++
++
++local voidpf falloc(q, n, s)
++voidpf q; /* opaque pointer (not used) */
++uInt n; /* number of items */
++uInt s; /* size of item */
++{
++ Assert(s == sizeof(inflate_huft) && n <= fixed_left,
++ "inflate_trees falloc overflow");
++ if (q) s++; /* to make some compilers happy */
++ fixed_left -= n;
++ return (voidpf)(fixed_mem + fixed_left);
++}
++
++
++local void ffree(q, p, n)
++voidpf q;
++voidpf p;
++uInt n;
++{
++ Assert(0, "inflate_trees ffree called!");
++ if (q) q = p; /* to make some compilers happy */
++}
++
++
++local int inflate_trees_fixed(bl, bd, tl, td)
++uIntf *bl; /* literal desired/actual bit depth */
++uIntf *bd; /* distance desired/actual bit depth */
++inflate_huft * FAR *tl; /* literal/length tree result */
++inflate_huft * FAR *td; /* distance tree result */
++{
++ /* build fixed tables if not built already--lock out other instances */
++ while (++fixed_lock > 1)
++ fixed_lock--;
++ if (!fixed_built)
++ {
++ int k; /* temporary variable */
++ unsigned c[288]; /* length list for huft_build */
++ z_stream z; /* for falloc function */
++
++ /* set up fake z_stream for memory routines */
++ z.zalloc = falloc;
++ z.zfree = ffree;
++ z.opaque = Z_NULL;
++
++ /* literal table */
++ for (k = 0; k < 144; k++)
++ c[k] = 8;
++ for (; k < 256; k++)
++ c[k] = 9;
++ for (; k < 280; k++)
++ c[k] = 7;
++ for (; k < 288; k++)
++ c[k] = 8;
++ fixed_bl = 7;
++ huft_build(c, 288, 257, cplens, cplext, &fixed_tl, &fixed_bl, &z);
++
++ /* distance table */
++ for (k = 0; k < 30; k++)
++ c[k] = 5;
++ fixed_bd = 5;
++ huft_build(c, 30, 0, cpdist, cpdext, &fixed_td, &fixed_bd, &z);
++
++ /* done */
++ fixed_built = 1;
++ }
++ fixed_lock--;
++ *bl = fixed_bl;
++ *bd = fixed_bd;
++ *tl = fixed_tl;
++ *td = fixed_td;
++ return Z_OK;
++}
++
++
++local int inflate_trees_free(t, z)
++inflate_huft *t; /* table to free */
++z_stream *z; /* for zfree function */
++/* Free the malloc'ed tables built by huft_build(), which makes a linked
++ list of the tables it made, with the links in a dummy first entry of
++ each table. */
++{
++ register inflate_huft *p, *q;
++
++ /* Go through linked list, freeing from the malloced (t[-1]) address. */
++ p = t;
++ while (p != Z_NULL)
++ {
++ q = (--p)->next;
++ ZFREE(z, p, p->word.Nalloc * sizeof(inflate_huft));
++ p = q;
++ }
++ return Z_OK;
++}
++
++/*+++++*/
++/* infcodes.c -- process literals and length/distance pairs
++ * Copyright (C) 1995 Mark Adler
++ * For conditions of distribution and use, see copyright notice in zlib.h
++ */
++
++/* simplify the use of the inflate_huft type with some defines */
++#define base more.Base
++#define next more.Next
++#define exop word.what.Exop
++#define bits word.what.Bits
++
++/* inflate codes private state */
++struct inflate_codes_state {
++
++ /* mode */
++ enum { /* waiting for "i:"=input, "o:"=output, "x:"=nothing */
++ START, /* x: set up for LEN */
++ LEN, /* i: get length/literal/eob next */
++ LENEXT, /* i: getting length extra (have base) */
++ DIST, /* i: get distance next */
++ DISTEXT, /* i: getting distance extra */
++ COPY, /* o: copying bytes in window, waiting for space */
++ LIT, /* o: got literal, waiting for output space */
++ WASH, /* o: got eob, possibly still output waiting */
++ END, /* x: got eob and all data flushed */
++ BADCODE} /* x: got error */
++ mode; /* current inflate_codes mode */
++
++ /* mode dependent information */
++ uInt len;
++ union {
++ struct {
++ inflate_huft *tree; /* pointer into tree */
++ uInt need; /* bits needed */
++ } code; /* if LEN or DIST, where in tree */
++ uInt lit; /* if LIT, literal */
++ struct {
++ uInt get; /* bits to get for extra */
++ uInt dist; /* distance back to copy from */
++ } copy; /* if EXT or COPY, where and how much */
++ } sub; /* submode */
++
++ /* mode independent information */
++ Byte lbits; /* ltree bits decoded per branch */
++ Byte dbits; /* dtree bits decoder per branch */
++ inflate_huft *ltree; /* literal/length/eob tree */
++ inflate_huft *dtree; /* distance tree */
++
++};
++
++
++local inflate_codes_statef *inflate_codes_new(bl, bd, tl, td, z)
++uInt bl, bd;
++inflate_huft *tl, *td;
++z_stream *z;
++{
++ inflate_codes_statef *c;
++
++ if ((c = (inflate_codes_statef *)
++ ZALLOC(z,1,sizeof(struct inflate_codes_state))) != Z_NULL)
++ {
++ c->mode = START;
++ c->lbits = (Byte)bl;
++ c->dbits = (Byte)bd;
++ c->ltree = tl;
++ c->dtree = td;
++ Tracev((stderr, "inflate: codes new\n"));
++ }
++ return c;
++}
++
++
++local int inflate_codes(s, z, r)
++inflate_blocks_statef *s;
++z_stream *z;
++int r;
++{
++ uInt j; /* temporary storage */
++ inflate_huft *t; /* temporary pointer */
++ uInt e; /* extra bits or operation */
++ uLong b; /* bit buffer */
++ uInt k; /* bits in bit buffer */
++ Bytef *p; /* input data pointer */
++ uInt n; /* bytes available there */
++ Bytef *q; /* output window write pointer */
++ uInt m; /* bytes to end of window or read pointer */
++ Bytef *f; /* pointer to copy strings from */
++ inflate_codes_statef *c = s->sub.decode.codes; /* codes state */
++
++ /* copy input/output information to locals (UPDATE macro restores) */
++ LOAD
++
++ /* process input and output based on current state */
++ while (1) switch (c->mode)
++ { /* waiting for "i:"=input, "o:"=output, "x:"=nothing */
++ case START: /* x: set up for LEN */
++#ifndef SLOW
++ if (m >= 258 && n >= 10)
++ {
++ UPDATE
++ r = inflate_fast(c->lbits, c->dbits, c->ltree, c->dtree, s, z);
++ LOAD
++ if (r != Z_OK)
++ {
++ c->mode = r == Z_STREAM_END ? WASH : BADCODE;
++ break;
++ }
++ }
++#endif /* !SLOW */
++ c->sub.code.need = c->lbits;
++ c->sub.code.tree = c->ltree;
++ c->mode = LEN;
++ case LEN: /* i: get length/literal/eob next */
++ j = c->sub.code.need;
++ NEEDBITS(j)
++ t = c->sub.code.tree + ((uInt)b & inflate_mask[j]);
++ DUMPBITS(t->bits)
++ e = (uInt)(t->exop);
++ if (e == 0) /* literal */
++ {
++ c->sub.lit = t->base;
++ Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ?
++ "inflate: literal '%c'\n" :
++ "inflate: literal 0x%02x\n", t->base));
++ c->mode = LIT;
++ break;
++ }
++ if (e & 16) /* length */
++ {
++ c->sub.copy.get = e & 15;
++ c->len = t->base;
++ c->mode = LENEXT;
++ break;
++ }
++ if ((e & 64) == 0) /* next table */
++ {
++ c->sub.code.need = e;
++ c->sub.code.tree = t->next;
++ break;
++ }
++ if (e & 32) /* end of block */
++ {
++ Tracevv((stderr, "inflate: end of block\n"));
++ c->mode = WASH;
++ break;
++ }
++ c->mode = BADCODE; /* invalid code */
++ z->msg = "invalid literal/length code";
++ r = Z_DATA_ERROR;
++ LEAVE
++ case LENEXT: /* i: getting length extra (have base) */
++ j = c->sub.copy.get;
++ NEEDBITS(j)
++ c->len += (uInt)b & inflate_mask[j];
++ DUMPBITS(j)
++ c->sub.code.need = c->dbits;
++ c->sub.code.tree = c->dtree;
++ Tracevv((stderr, "inflate: length %u\n", c->len));
++ c->mode = DIST;
++ case DIST: /* i: get distance next */
++ j = c->sub.code.need;
++ NEEDBITS(j)
++ t = c->sub.code.tree + ((uInt)b & inflate_mask[j]);
++ DUMPBITS(t->bits)
++ e = (uInt)(t->exop);
++ if (e & 16) /* distance */
++ {
++ c->sub.copy.get = e & 15;
++ c->sub.copy.dist = t->base;
++ c->mode = DISTEXT;
++ break;
++ }
++ if ((e & 64) == 0) /* next table */
++ {
++ c->sub.code.need = e;
++ c->sub.code.tree = t->next;
++ break;
++ }
++ c->mode = BADCODE; /* invalid code */
++ z->msg = "invalid distance code";
++ r = Z_DATA_ERROR;
++ LEAVE
++ case DISTEXT: /* i: getting distance extra */
++ j = c->sub.copy.get;
++ NEEDBITS(j)
++ c->sub.copy.dist += (uInt)b & inflate_mask[j];
++ DUMPBITS(j)
++ Tracevv((stderr, "inflate: distance %u\n", c->sub.copy.dist));
++ c->mode = COPY;
++ case COPY: /* o: copying bytes in window, waiting for space */
++#ifndef __TURBOC__ /* Turbo C bug for following expression */
++ f = (uInt)(q - s->window) < c->sub.copy.dist ?
++ s->end - (c->sub.copy.dist - (q - s->window)) :
++ q - c->sub.copy.dist;
++#else
++ f = q - c->sub.copy.dist;
++ if ((uInt)(q - s->window) < c->sub.copy.dist)
++ f = s->end - (c->sub.copy.dist - (q - s->window));
++#endif
++ while (c->len)
++ {
++ NEEDOUT
++ OUTBYTE(*f++)
++ if (f == s->end)
++ f = s->window;
++ c->len--;
++ }
++ c->mode = START;
++ break;
++ case LIT: /* o: got literal, waiting for output space */
++ NEEDOUT
++ OUTBYTE(c->sub.lit)
++ c->mode = START;
++ break;
++ case WASH: /* o: got eob, possibly more output */
++ FLUSH
++ if (s->read != s->write)
++ LEAVE
++ c->mode = END;
++ case END:
++ r = Z_STREAM_END;
++ LEAVE
++ case BADCODE: /* x: got error */
++ r = Z_DATA_ERROR;
++ LEAVE
++ default:
++ r = Z_STREAM_ERROR;
++ LEAVE
++ }
++}
++
++
++local void inflate_codes_free(c, z)
++inflate_codes_statef *c;
++z_stream *z;
++{
++ ZFREE(z, c, sizeof(struct inflate_codes_state));
++ Tracev((stderr, "inflate: codes free\n"));
++}
++
++/*+++++*/
++/* inflate_util.c -- data and routines common to blocks and codes
++ * Copyright (C) 1995 Mark Adler
++ * For conditions of distribution and use, see copyright notice in zlib.h
++ */
++
++/* copy as much as possible from the sliding window to the output area */
++local int inflate_flush(s, z, r)
++inflate_blocks_statef *s;
++z_stream *z;
++int r;
++{
++ uInt n;
++ Bytef *p, *q;
++
++ /* local copies of source and destination pointers */
++ p = z->next_out;
++ q = s->read;
++
++ /* compute number of bytes to copy as far as end of window */
++ n = (uInt)((q <= s->write ? s->write : s->end) - q);
++ if (n > z->avail_out) n = z->avail_out;
++ if (n && r == Z_BUF_ERROR) r = Z_OK;
++
++ /* update counters */
++ z->avail_out -= n;
++ z->total_out += n;
++
++ /* update check information */
++ if (s->checkfn != Z_NULL)
++ s->check = (*s->checkfn)(s->check, q, n);
++
++ /* copy as far as end of window */
++ zmemcpy(p, q, n);
++ p += n;
++ q += n;
++
++ /* see if more to copy at beginning of window */
++ if (q == s->end)
++ {
++ /* wrap pointers */
++ q = s->window;
++ if (s->write == s->end)
++ s->write = s->window;
++
++ /* compute bytes to copy */
++ n = (uInt)(s->write - q);
++ if (n > z->avail_out) n = z->avail_out;
++ if (n && r == Z_BUF_ERROR) r = Z_OK;
++
++ /* update counters */
++ z->avail_out -= n;
++ z->total_out += n;
++
++ /* update check information */
++ if (s->checkfn != Z_NULL)
++ s->check = (*s->checkfn)(s->check, q, n);
++
++ /* copy */
++ zmemcpy(p, q, n);
++ p += n;
++ q += n;
++ }
++
++ /* update pointers */
++ z->next_out = p;
++ s->read = q;
++
++ /* done */
++ return r;
++}
++
++
++/*+++++*/
++/* inffast.c -- process literals and length/distance pairs fast
++ * Copyright (C) 1995 Mark Adler
++ * For conditions of distribution and use, see copyright notice in zlib.h
++ */
++
++/* simplify the use of the inflate_huft type with some defines */
++#define base more.Base
++#define next more.Next
++#define exop word.what.Exop
++#define bits word.what.Bits
++
++/* macros for bit input with no checking and for returning unused bytes */
++#define GRABBITS(j) {while(k<(j)){b|=((uLong)NEXTBYTE)<<k;k+=8;}}
++#define UNGRAB {n+=(c=k>>3);p-=c;k&=7;}
++
++/* Called with number of bytes left to write in window at least 258
++ (the maximum string length) and number of input bytes available
++ at least ten. The ten bytes are six bytes for the longest length/
++ distance pair plus four bytes for overloading the bit buffer. */
++
++local int inflate_fast(bl, bd, tl, td, s, z)
++uInt bl, bd;
++inflate_huft *tl, *td;
++inflate_blocks_statef *s;
++z_stream *z;
++{
++ inflate_huft *t; /* temporary pointer */
++ uInt e; /* extra bits or operation */
++ uLong b; /* bit buffer */
++ uInt k; /* bits in bit buffer */
++ Bytef *p; /* input data pointer */
++ uInt n; /* bytes available there */
++ Bytef *q; /* output window write pointer */
++ uInt m; /* bytes to end of window or read pointer */
++ uInt ml; /* mask for literal/length tree */
++ uInt md; /* mask for distance tree */
++ uInt c; /* bytes to copy */
++ uInt d; /* distance back to copy from */
++ Bytef *r; /* copy source pointer */
++
++ /* load input, output, bit values */
++ LOAD
++
++ /* initialize masks */
++ ml = inflate_mask[bl];
++ md = inflate_mask[bd];
++
++ /* do until not enough input or output space for fast loop */
++ do { /* assume called with m >= 258 && n >= 10 */
++ /* get literal/length code */
++ GRABBITS(20) /* max bits for literal/length code */
++ if ((e = (t = tl + ((uInt)b & ml))->exop) == 0)
++ {
++ DUMPBITS(t->bits)
++ Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ?
++ "inflate: * literal '%c'\n" :
++ "inflate: * literal 0x%02x\n", t->base));
++ *q++ = (Byte)t->base;
++ m--;
++ continue;
++ }
++ do {
++ DUMPBITS(t->bits)
++ if (e & 16)
++ {
++ /* get extra bits for length */
++ e &= 15;
++ c = t->base + ((uInt)b & inflate_mask[e]);
++ DUMPBITS(e)
++ Tracevv((stderr, "inflate: * length %u\n", c));
++
++ /* decode distance base of block to copy */
++ GRABBITS(15); /* max bits for distance code */
++ e = (t = td + ((uInt)b & md))->exop;
++ do {
++ DUMPBITS(t->bits)
++ if (e & 16)
++ {
++ /* get extra bits to add to distance base */
++ e &= 15;
++ GRABBITS(e) /* get extra bits (up to 13) */
++ d = t->base + ((uInt)b & inflate_mask[e]);
++ DUMPBITS(e)
++ Tracevv((stderr, "inflate: * distance %u\n", d));
++
++ /* do the copy */
++ m -= c;
++ if ((uInt)(q - s->window) >= d) /* offset before dest */
++ { /* just copy */
++ r = q - d;
++ *q++ = *r++; c--; /* minimum count is three, */
++ *q++ = *r++; c--; /* so unroll loop a little */
++ }
++ else /* else offset after destination */
++ {
++ e = d - (q - s->window); /* bytes from offset to end */
++ r = s->end - e; /* pointer to offset */
++ if (c > e) /* if source crosses, */
++ {
++ c -= e; /* copy to end of window */
++ do {
++ *q++ = *r++;
++ } while (--e);
++ r = s->window; /* copy rest from start of window */
++ }
++ }
++ do { /* copy all or what's left */
++ *q++ = *r++;
++ } while (--c);
++ break;
++ }
++ else if ((e & 64) == 0)
++ e = (t = t->next + ((uInt)b & inflate_mask[e]))->exop;
++ else
++ {
++ z->msg = "invalid distance code";
++ UNGRAB
++ UPDATE
++ return Z_DATA_ERROR;
++ }
++ } while (1);
++ break;
++ }
++ if ((e & 64) == 0)
++ {
++ if ((e = (t = t->next + ((uInt)b & inflate_mask[e]))->exop) == 0)
++ {
++ DUMPBITS(t->bits)
++ Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ?
++ "inflate: * literal '%c'\n" :
++ "inflate: * literal 0x%02x\n", t->base));
++ *q++ = (Byte)t->base;
++ m--;
++ break;
++ }
++ }
++ else if (e & 32)
++ {
++ Tracevv((stderr, "inflate: * end of block\n"));
++ UNGRAB
++ UPDATE
++ return Z_STREAM_END;
++ }
++ else
++ {
++ z->msg = "invalid literal/length code";
++ UNGRAB
++ UPDATE
++ return Z_DATA_ERROR;
++ }
++ } while (1);
++ } while (m >= 258 && n >= 10);
++
++ /* not enough input or output--restore pointers and return */
++ UNGRAB
++ UPDATE
++ return Z_OK;
++}
++
++
++/*+++++*/
++/* zutil.c -- target dependent utility functions for the compression library
++ * Copyright (C) 1995 Jean-loup Gailly.
++ * For conditions of distribution and use, see copyright notice in zlib.h
++ */
++
++/* From: zutil.c,v 1.8 1995/05/03 17:27:12 jloup Exp */
++
++char *zlib_version = ZLIB_VERSION;
++
++char *z_errmsg[] = {
++"stream end", /* Z_STREAM_END 1 */
++"", /* Z_OK 0 */
++"file error", /* Z_ERRNO (-1) */
++"stream error", /* Z_STREAM_ERROR (-2) */
++"data error", /* Z_DATA_ERROR (-3) */
++"insufficient memory", /* Z_MEM_ERROR (-4) */
++"buffer error", /* Z_BUF_ERROR (-5) */
++""};
++
++
++/*+++++*/
++/* adler32.c -- compute the Adler-32 checksum of a data stream
++ * Copyright (C) 1995 Mark Adler
++ * For conditions of distribution and use, see copyright notice in zlib.h
++ */
++
++/* From: adler32.c,v 1.6 1995/05/03 17:27:08 jloup Exp */
++
++#define BASE 65521L /* largest prime smaller than 65536 */
++#define NMAX 5552
++/* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */
++
++#define DO1(buf) {s1 += *buf++; s2 += s1;}
++#define DO2(buf) DO1(buf); DO1(buf);
++#define DO4(buf) DO2(buf); DO2(buf);
++#define DO8(buf) DO4(buf); DO4(buf);
++#define DO16(buf) DO8(buf); DO8(buf);
++
++/* ========================================================================= */
++uLong adler32(adler, buf, len)
++ uLong adler;
++ Bytef *buf;
++ uInt len;
++{
++ unsigned long s1 = adler & 0xffff;
++ unsigned long s2 = (adler >> 16) & 0xffff;
++ int k;
++
++ if (buf == Z_NULL) return 1L;
++
++ while (len > 0) {
++ k = len < NMAX ? len : NMAX;
++ len -= k;
++ while (k >= 16) {
++ DO16(buf);
++ k -= 16;
++ }
++ if (k != 0) do {
++ DO1(buf);
++ } while (--k);
++ s1 %= BASE;
++ s2 %= BASE;
++ }
++ return (s2 << 16) | s1;
++}
+diff -Naru linux/arch/mips/zboot/Makefile linux.spi/arch/mips/zboot/Makefile
+--- linux/arch/mips/zboot/Makefile 1969-12-31 19:00:00.000000000 -0500
++++ linux.spi/arch/mips/zboot/Makefile 2004-05-11 23:19:24.000000000 -0400
+@@ -0,0 +1,84 @@
++#
++# arch/mips/zboot/Makefile
++#
++# This file is subject to the terms and conditions of the GNU General Public
++# License. See the file "COPYING" in the main directory of this archive
++# for more details.
++
++# Adapted for MIPS Pete Popov, Dan Malek
++#
++# Copyright (C) 1994 by Linus Torvalds
++# Adapted for PowerPC by Gary Thomas
++# modified by Cort (cort@cs.nmt.edu)
++#
++
++.c.s:
++ $(CC) $(CFLAGS) -S -o $*.s $<
++.s.o:
++ $(AS) -o $*.o $<
++.c.o:
++ $(CC) $(CFLAGS) -c -o $*.o $<
++.S.s:
++ $(CPP) $(AFLAGS) -o $*.o $<
++.S.o:
++ $(CC) $(AFLAGS) -c -o $*.o $<
++
++GZIP_FLAGS = -v9f
++
++CFLAGS += -D__BOOTER__ -I$(TOPDIR)/arch/$(ARCH)/zboot/include
++AFLAGS += -D__BOOTER__
++
++BOOT_TARGETS = zImage zImage.initrd zImage.flash zImage.initrd.flash
++
++lib/zlib.a:
++ $(MAKE) -C lib
++
++images/vmlinux.gz: $(TOPDIR)/vmlinux
++ $(MAKE) -C images vmlinux.gz
++
++$(BOOT_TARGETS): lib/zlib.a images/vmlinux.gz
++ifdef CONFIG_MIPS_PB1000
++ $(MAKE) -C pb1xxx $@
++endif
++ifdef CONFIG_MIPS_PB1500
++ $(MAKE) -C pb1xxx $@
++endif
++ifdef CONFIG_MIPS_PB1100
++ $(MAKE) -C pb1xxx $@
++endif
++ifdef CONFIG_MIPS_PB1550
++ $(MAKE) -C pb1xxx $@
++endif
++ifdef CONFIG_MIPS_DB1000
++ $(MAKE) -C pb1xxx $@
++endif
++ifdef CONFIG_MIPS_DB1100
++ $(MAKE) -C pb1xxx $@
++endif
++ifdef CONFIG_MIPS_DB1500
++ $(MAKE) -C pb1xxx $@
++endif
++ifdef CONFIG_MIPS_BOSPORUS
++ $(MAKE) -C pb1xxx $@
++endif
++ifdef CONFIG_MIPS_MIRAGE
++ $(MAKE) -C pb1xxx $@
++endif
++ifdef CONFIG_MIPS_MTX1
++ $(MAKE) -C pb1xxx $@
++endif
++ifdef CONFIG_COGENT_CSB250
++ $(MAKE) -C csb250 $@
++endif
++ifdef CONFIG_MIPS_XXS1500
++BOOT_DIR = xxs1500
++endif
++
++# Do the dirs
++clean:
++ $(MAKE) -C common clean
++ $(MAKE) -C images clean
++ $(MAKE) -C pb1xxx clean
++ $(MAKE) -C xxs1500 clean
++
++include $(TOPDIR)/Rules.make
+diff -Naru linux/arch/mips/zboot/pb1xxx/head.S linux.spi/arch/mips/zboot/pb1xxx/head.S
+--- linux/arch/mips/zboot/pb1xxx/head.S 1969-12-31 19:00:00.000000000 -0500
++++ linux.spi/arch/mips/zboot/pb1xxx/head.S 2004-05-11 23:19:24.000000000 -0400
+@@ -0,0 +1,149 @@
++/*
++ * arch/mips/kernel/head.S
++ *
++ * This file is subject to the terms and conditions of the GNU General Public
++ * License. See the file "COPYING" in the main directory of this archive
++ * for more details.
++ *
++ * Copyright (C) 1994, 1995 Waldorf Electronics
++ * Written by Ralf Baechle and Andreas Busse
++ * Copyright (C) 1995 - 1999 Ralf Baechle
++ * Copyright (C) 1996 Paul M. Antoine
++ * Modified for DECStation and hence R3000 support by Paul M. Antoine
++ * Further modifications by David S. Miller and Harald Koerfgen
++ * Copyright (C) 1999 Silicon Graphics, Inc.
++ *
++ * Head.S contains the MIPS exception handler and startup code.
++ *
++ **************************************************************************
++ * 9 Nov, 2000.
++ * Added Cache Error exception handler and SBDDP EJTAG debug exception.
++ *
++ * Kevin Kissell, kevink@mips.com and Carsten Langgaard, carstenl@mips.com
++ * Copyright (C) 2000 MIPS Technologies, Inc. All rights reserved.
++ **************************************************************************
++ */
++#include <linux/config.h>
++#include <linux/threads.h>
++
++#include <asm/asm.h>
++#include <asm/cacheops.h>
++#include <asm/mipsregs.h>
++#include <asm/offset.h>
++#include <asm/cachectl.h>
++#include <asm/regdef.h>
++
++#define IndexInvalidate_I 0x00
++#define IndexWriteBack_D 0x01
++
++ .set noreorder
++ .cprestore
++ LEAF(start)
++start:
++ bal locate
++ nop
++locate:
++ subu s8, ra, 8 /* Where we were loaded */
++ la sp, (.stack + 8192)
++
++ move s0, a0 /* Save boot rom start args */
++ move s1, a1
++ move s2, a2
++ move s3, a3
++
++ la a0, start /* Where we were linked to run */
++
++ move a1, s8
++ la a2, _edata
++ subu t1, a2, a0
++ srl t1, t1, 2
++
++ /* copy text section */
++ li t0, 0
++1: lw v0, 0(a1)
++ nop
++ sw v0, 0(a0)
++ xor t0, t0, v0
++ addu a0, 4
++ bne a2, a0, 1b
++ addu a1, 4
++
++ /* Clear BSS */
++ la a0, _edata
++ la a2, _end
++2: sw zero, 0(a0)
++ bne a2, a0, 2b
++ addu a0, 4
++
++ /* push the D-Cache and invalidate I-Cache */
++ li k0, 0x80000000 # start address
++ li k1, 0x80004000 # end address (16KB I-Cache)
++ subu k1, 128
++
++1:
++ .set mips3
++ cache IndexWriteBack_D, 0(k0)
++ cache IndexWriteBack_D, 32(k0)
++ cache IndexWriteBack_D, 64(k0)
++ cache IndexWriteBack_D, 96(k0)
++ cache IndexInvalidate_I, 0(k0)
++ cache IndexInvalidate_I, 32(k0)
++ cache IndexInvalidate_I, 64(k0)
++ cache IndexInvalidate_I, 96(k0)
++ .set mips0
++
++ bne k0, k1, 1b
++ addu k0, k0, 128
++ /* done */
++
++ move a0, s8 /* load address */
++ move a1, t1 /* length in words */
++ move a2, t0 /* checksum */
++ move a3, sp
++
++ la ra, 1f
++ la k0, decompress_kernel
++ jr k0
++ nop
++1:
++
++ move a0, s0
++ move a1, s1
++ move a2, s2
++ move a3, s3
++ li k0, KERNEL_ENTRY
++ jr k0
++ nop
++3:
++ b 3b
++ END(start)
++
++ LEAF(udelay)
++udelay:
++ END(udelay)
++
++
++ LEAF(FlushCache)
++ li k0, 0x80000000 # start address
++ li k1, 0x80004000 # end address (16KB I-Cache)
++ subu k1, 128
++
++1:
++ .set mips3
++ cache IndexWriteBack_D, 0(k0)
++ cache IndexWriteBack_D, 32(k0)
++ cache IndexWriteBack_D, 64(k0)
++ cache IndexWriteBack_D, 96(k0)
++ cache IndexInvalidate_I, 0(k0)
++ cache IndexInvalidate_I, 32(k0)
++ cache IndexInvalidate_I, 64(k0)
++ cache IndexInvalidate_I, 96(k0)
++ .set mips0
++
++ bne k0, k1, 1b
++ addu k0, k0, 128
++ jr ra
++ nop
++ END(FlushCache)
++
++ .comm .stack,4096*2,4
+diff -Naru linux/arch/mips/zboot/pb1xxx/Makefile linux.spi/arch/mips/zboot/pb1xxx/Makefile
+--- linux/arch/mips/zboot/pb1xxx/Makefile 1969-12-31 19:00:00.000000000 -0500
++++ linux.spi/arch/mips/zboot/pb1xxx/Makefile 2004-05-11 23:19:24.000000000 -0400
+@@ -0,0 +1,135 @@
++# arch/mips/zboot/pb1xxx/Makefile
++#
++# Makefile for Alchemy Semiconductor Pb1[015]00 boards.
++# All of the boot loader code was derived from the ppc
++# boot code.
++#
++# Copyright 2001,2002 MontaVista Software Inc.
++#
++# Author: Mark A. Greer
++# mgreer@mvista.com
++# Ported and modified for mips support by
++# Pete Popov <ppopov@mvista.com>
++#
++# This program is free software; you can redistribute it and/or modify it
++# under the terms of the GNU General Public License as published by the
++# Free Software Foundation; either version 2 of the License, or (at your
++# option) any later version.
++
++.c.s:
++ $(CC) $(CFLAGS) -S -o $*.s $<
++.s.o:
++ $(AS) -o $*.o $<
++.c.o:
++ $(CC) $(CFLAGS) -D__BOOTER__ -c -o $*.o $<
++.S.s:
++ $(CPP) $(AFLAGS) -o $*.o $<
++.S.o:
++ $(CC) $(AFLAGS) -c -o $*.o $<
++
++#########################################################################
++# START BOARD SPECIFIC VARIABLES
++ifdef CONFIG_MIPS_PB1000
++BNAME=pb1000
++endif
++
++ifdef CONFIG_MIPS_PB1100
++BNAME=pb1100
++endif
++
++ifdef CONFIG_MIPS_PB1500
++BNAME=pb1500
++endif
++
++ifdef CONFIG_MIPS_PB1550
++BNAME=pb1550
++endif
++
++ifdef CONFIG_MIPS_DB1000
++BNAME=db1000
++endif
++
++ifdef CONFIG_MIPS_DB1100
++BNAME=db1100
++endif
++
++ifdef CONFIG_MIPS_DB1500
++BNAME=db1500
++endif
++
++ifdef CONFIG_MIPS_BOSPORUS
++BNAME=bosporus
++endif
++
++ifdef CONFIG_MIPS_MIRAGE
++BNAME=mirage
++endif
++
++ifdef CONFIG_MIPS_MTX1
++BNAME=mtx-1
++endif
++
++# These two variables control where the zImage is stored
++# in flash and loaded in memory. It only controls how the srec
++# file is generated, the code is the same.
++RAM_RUN_ADDR = 0x81000000
++FLASH_LOAD_ADDR = 0xBFD00000
++
++# These two variables specify the free ram region
++# that can be used for temporary malloc area
++AVAIL_RAM_START=0x80400000
++AVAIL_RAM_END=0x80800000
++
++# This one must match the LOADADDR in arch/mips/Makefile!
++LOADADDR=0x80100000
++# END BOARD SPECIFIC VARIABLES
++#########################################################################
++
++OBJECTS := head.o ../common/misc-common.o ../common/misc-simple.o \
++ ../common/au1k_uart.o ../common/string.o ../common/ctype.o
++LIBS := ../lib/zlib.a
++
++ENTRY := ../utils/entry
++OFFSET := ../utils/offset
++SIZE := ../utils/size
++
++LD_ARGS := -T ../ld.script -Ttext $(RAM_RUN_ADDR) -Bstatic
++OBJCOPY_ARGS = -O elf32-tradlittlemips
++
++all: zImage
++
++clean:
++ rm -rf *.o vmlinux* zvmlinux.* ../images/*.srec
++
++head.o: head.S $(TOPDIR)/vmlinux
++ $(CC) $(AFLAGS) \
++ -DKERNEL_ENTRY=$(shell sh $(ENTRY) $(NM) $(TOPDIR)/vmlinux ) \
++ -c -o $*.o $<
++
++../common/misc-simple.o:
++ $(CC) $(CFLAGS) -DINITRD_OFFSET=0 -DINITRD_SIZE=0 -DZIMAGE_OFFSET=0 \
++ -DAVAIL_RAM_START=$(AVAIL_RAM_START) \
++ -DAVAIL_RAM_END=$(AVAIL_RAM_END) \
++ -DLOADADDR=$(LOADADDR) \
++ -DZIMAGE_SIZE=0 -c -o $@ $*.c
++
++zvmlinux: $(OBJECTS) $(LIBS) ../ld.script ../images/vmlinux.gz ../common/dummy.o
++ $(OBJCOPY) \
++ --add-section=.image=../images/vmlinux.gz \
++ --set-section-flags=.image=contents,alloc,load,readonly,data \
++ ../common/dummy.o image.o
++ $(LD) $(LD_ARGS) -o $@ $(OBJECTS) image.o $(LIBS)
++ $(OBJCOPY) $(OBJCOPY_ARGS) $@ $@ -R .comment -R .stab -R .stabstr \
++ -R .initrd -R .sysmap
++
++# Here we manipulate the image in order to get it the necessary
++# srecord file we need.
++zImage: zvmlinux
++ mv zvmlinux ../images/zImage.$(BNAME)
++ $(OBJCOPY) -O srec ../images/zImage.$(BNAME) ../images/$(BNAME).srec
++
++zImage.flash: zImage
++ $(OBJCOPY) -O srec --adjust-vma 0x3ed00000 \
++ ../images/zImage.$(BNAME) ../images/$(BNAME).flash.srec
++
++include $(TOPDIR)/Rules.make
+diff -Naru linux/arch/mips/zboot/utils/entry linux.spi/arch/mips/zboot/utils/entry
+--- linux/arch/mips/zboot/utils/entry 1969-12-31 19:00:00.000000000 -0500
++++ linux.spi/arch/mips/zboot/utils/entry 2004-05-11 23:19:24.000000000 -0400
+@@ -0,0 +1,12 @@
++#!/bin/sh
++
++# grab the kernel_entry address from the vmlinux elf image
++entry=`$1 $2 | grep kernel_entry`
++
++fs=`echo $entry | grep ffffffff` # check toolchain output
++
++if [ -n "$fs" ]; then
++ echo "0x"`$1 $2 | grep kernel_entry | cut -c9- | awk '{print $1}'`
++else
++ echo "0x"`$1 $2 | grep kernel_entry | cut -c1- | awk '{print $1}'`
++fi
+diff -Naru linux/arch/mips/zboot/utils/offset linux.spi/arch/mips/zboot/utils/offset
+--- linux/arch/mips/zboot/utils/offset 1969-12-31 19:00:00.000000000 -0500
++++ linux.spi/arch/mips/zboot/utils/offset 2004-05-11 23:19:24.000000000 -0400
+@@ -0,0 +1,3 @@
++#!/bin/sh
++
++echo "0x"`$1 -h $2 | grep $3 | grep -v zvmlinux| awk '{print $6}'`
+diff -Naru linux/arch/mips/zboot/utils/size linux.spi/arch/mips/zboot/utils/size
+--- linux/arch/mips/zboot/utils/size 1969-12-31 19:00:00.000000000 -0500
++++ linux.spi/arch/mips/zboot/utils/size 2004-05-11 23:19:24.000000000 -0400
+@@ -0,0 +1,4 @@
++#!/bin/sh
++
++OFFSET=`$1 -h $2 | grep $3 | grep -v zvmlinux | awk '{print $3}'`
++echo "0x"$OFFSET
+diff -Naru linux/arch/mips/zboot/xxs1500/head.S linux.spi/arch/mips/zboot/xxs1500/head.S
+--- linux/arch/mips/zboot/xxs1500/head.S 1969-12-31 19:00:00.000000000 -0500
++++ linux.spi/arch/mips/zboot/xxs1500/head.S 2004-05-11 23:19:24.000000000 -0400
+@@ -0,0 +1,137 @@
++/*
++ * arch/mips/kernel/head.S
++ *
++ * This file is subject to the terms and conditions of the GNU General Public
++ * License. See the file "COPYING" in the main directory of this archive
++ * for more details.
++ *
++ * Copyright (C) 1994, 1995 Waldorf Electronics
++ * Written by Ralf Baechle and Andreas Busse
++ * Copyright (C) 1995 - 1999 Ralf Baechle
++ * Copyright (C) 1996 Paul M. Antoine
++ * Modified for DECStation and hence R3000 support by Paul M. Antoine
++ * Further modifications by David S. Miller and Harald Koerfgen
++ * Copyright (C) 1999 Silicon Graphics, Inc.
++ *
++ * Head.S contains the MIPS exception handler and startup code.
++ *
++ **************************************************************************
++ * 9 Nov, 2000.
++ * Added Cache Error exception handler and SBDDP EJTAG debug exception.
++ *
++ * Kevin Kissell, kevink@mips.com and Carsten Langgaard, carstenl@mips.com
++ * Copyright (C) 2000 MIPS Technologies, Inc. All rights reserved.
++ **************************************************************************
++ */
++#include <linux/config.h>
++#include <linux/threads.h>
++
++#include <asm/asm.h>
++#include <asm/cacheops.h>
++#include <asm/mipsregs.h>
++#include <asm/offset.h>
++#include <asm/cachectl.h>
++#include <asm/regdef.h>
++
++#define IndexInvalidate_I 0x00
++
++ .set noreorder
++ .cprestore
++ LEAF(start)
++start:
++
++locate:
++ la sp, .stack
++ move s0, a0
++ move s1, a1
++ move s2, a2
++ move s3, a3
++
++ la a0, start
++
++ li a1, FLASH_LOAD_ADDR
++ la a2, _edata
++ subu t1, a2, a0
++ srl t1, t1, 2
++
++ /* copy text section */
++ li t0, 0
++1: lw v0, 0(a1)
++ nop
++ sw v0, 0(a0)
++ xor t0, t0, v0
++ addu a0, 4
++ bne a2, a0, 1b
++ addu a1, 4
++
++ /* Clear BSS */
++ la a0, _edata
++ la a2, _end
++2: sw zero, 0(a0)
++ bne a2, a0, 2b
++ addu a0, 4
++
++ /* flush the I-Cache */
++ li k0, 0x80000000 # start address
++ li k1, 0x80004000 # end address (16KB I-Cache)
++ subu k1, 128
++
++1:
++ .set mips3
++ cache IndexInvalidate_I, 0(k0)
++ cache IndexInvalidate_I, 32(k0)
++ cache IndexInvalidate_I, 64(k0)
++ cache IndexInvalidate_I, 96(k0)
++ .set mips0
++
++ bne k0, k1, 1b
++ addu k0, k0, 128
++ /* done */
++
++ li a0, FLASH_LOAD_ADDR /* load address */
++ move a1, t1 /* length in words */
++ move a2, t0 /* checksum */
++ move a3, sp
++
++ la ra, 1f
++ la k0, decompress_kernel
++ jr k0
++ nop
++1:
++
++ move a0, s0
++ move a1, s1
++ move a2, s2
++ move a3, s3
++ li k0, KERNEL_ENTRY
++ jr k0
++ nop
++3:
++ b 3b
++ END(start)
++
++ LEAF(udelay)
++udelay:
++ END(udelay)
++
++
++ LEAF(FlushCache)
++ li k0, 0x80000000 # start address
++ li k1, 0x80004000 # end address (16KB I-Cache)
++ subu k1, 128
++
++1:
++ .set mips3
++ cache IndexInvalidate_I, 0(k0)
++ cache IndexInvalidate_I, 32(k0)
++ cache IndexInvalidate_I, 64(k0)
++ cache IndexInvalidate_I, 96(k0)
++ .set mips0
++
++ bne k0, k1, 1b
++ addu k0, k0, 128
++ jr ra
++ nop
++ END(FlushCache)
++
++ .comm .stack,4096*2,4
+diff -Naru linux/arch/mips/zboot/xxs1500/ld.script linux.spi/arch/mips/zboot/xxs1500/ld.script
+--- linux/arch/mips/zboot/xxs1500/ld.script 1969-12-31 19:00:00.000000000 -0500
++++ linux.spi/arch/mips/zboot/xxs1500/ld.script 2004-05-11 23:19:24.000000000 -0400
+@@ -0,0 +1,147 @@
++OUTPUT_ARCH(mips)
++ENTRY(start)
++SECTIONS
++{
++ /* Read-only sections, merged into text segment: */
++ /* . = 0x81000000; */
++ .init : { *(.init) } =0
++ .text :
++ {
++ _ftext = . ;
++ *(.text)
++ *(.rodata)
++ *(.rodata1)
++ /* .gnu.warning sections are handled specially by elf32.em. */
++ *(.gnu.warning)
++ } =0
++ .kstrtab : { *(.kstrtab) }
++
++ . = ALIGN(16); /* Exception table */
++ __start___ex_table = .;
++ __ex_table : { *(__ex_table) }
++ __stop___ex_table = .;
++
++ __start___dbe_table = .; /* Exception table for data bus errors */
++ __dbe_table : { *(__dbe_table) }
++ __stop___dbe_table = .;
++
++ __start___ksymtab = .; /* Kernel symbol table */
++ __ksymtab : { *(__ksymtab) }
++ __stop___ksymtab = .;
++
++ _etext = .;
++
++ . = ALIGN(8192);
++ .data.init_task : { *(.data.init_task) }
++
++ /* Startup code */
++ . = ALIGN(4096);
++ __init_begin = .;
++ .text.init : { *(.text.init) }
++ .data.init : { *(.data.init) }
++ . = ALIGN(16);
++ __setup_start = .;
++ .setup.init : { *(.setup.init) }
++ __setup_end = .;
++ __initcall_start = .;
++ .initcall.init : { *(.initcall.init) }
++ __initcall_end = .;
++ . = ALIGN(4096); /* Align double page for init_task_union */
++ __init_end = .;
++
++ . = ALIGN(4096);
++ .data.page_aligned : { *(.data.idt) }
++
++ . = ALIGN(32);
++ .data.cacheline_aligned : { *(.data.cacheline_aligned) }
++
++ .fini : { *(.fini) } =0
++ .reginfo : { *(.reginfo) }
++ /* Adjust the address for the data segment. We want to adjust up to
++ the same address within the page on the next page up. It would
++ be more correct to do this:
++ . = .;
++ The current expression does not correctly handle the case of a
++ text segment ending precisely at the end of a page; it causes the
++ data segment to skip a page. The above expression does not have
++ this problem, but it will currently (2/95) cause BFD to allocate
++ a single segment, combining both text and data, for this case.
++ This will prevent the text segment from being shared among
++ multiple executions of the program; I think that is more
++ important than losing a page of the virtual address space (note
++ that no actual memory is lost; the page which is skipped can not
++ be referenced). */
++ . = .;
++ .data :
++ {
++ _fdata = . ;
++ *(.data)
++
++ /* Align the initial ramdisk image (INITRD) on page boundaries. */
++ . = ALIGN(4096);
++ __rd_start = .;
++ *(.initrd)
++ __rd_end = .;
++ . = ALIGN(4096);
++
++ CONSTRUCTORS
++ }
++ .data1 : { *(.data1) }
++ _gp = . + 0x8000;
++ .lit8 : { *(.lit8) }
++ .lit4 : { *(.lit4) }
++ .ctors : { *(.ctors) }
++ .dtors : { *(.dtors) }
++ .got : { *(.got.plt) *(.got) }
++ .dynamic : { *(.dynamic) }
++ /* We want the small data sections together, so single-instruction offsets
++ can access them all, and initialized data all before uninitialized, so
++ we can shorten the on-disk segment size. */
++ .sdata : { *(.sdata) }
++ . = ALIGN(4);
++ _edata = .;
++ PROVIDE (edata = .);
++
++ __bss_start = .;
++ _fbss = .;
++ .sbss : { *(.sbss) *(.scommon) }
++ .bss :
++ {
++ *(.dynbss)
++ *(.bss)
++ *(COMMON)
++ . = ALIGN(4);
++ _end = . ;
++ PROVIDE (end = .);
++ }
++
++ /* Sections to be discarded */
++ /DISCARD/ :
++ {
++ *(.text.exit)
++ *(.data.exit)
++ *(.exitcall.exit)
++ }
++
++ /* This is the MIPS specific mdebug section. */
++ .mdebug : { *(.mdebug) }
++ /* These are needed for ELF backends which have not yet been
++ converted to the new style linker. */
++ .stab 0 : { *(.stab) }
++ .stabstr 0 : { *(.stabstr) }
++ /* DWARF debug sections.
++ Symbols in the .debug DWARF section are relative to the beginning of the
++ section so we begin .debug at 0. It's not clear yet what needs to happen
++ for the others. */
++ .debug 0 : { *(.debug) }
++ .debug_srcinfo 0 : { *(.debug_srcinfo) }
++ .debug_aranges 0 : { *(.debug_aranges) }
++ .debug_pubnames 0 : { *(.debug_pubnames) }
++ .debug_sfnames 0 : { *(.debug_sfnames) }
++ .line 0 : { *(.line) }
++ /* These must appear regardless of . */
++ .gptab.sdata : { *(.gptab.data) *(.gptab.sdata) }
++ .gptab.sbss : { *(.gptab.bss) *(.gptab.sbss) }
++ .comment : { *(.comment) }
++ .note : { *(.note) }
++}
+diff -Naru linux/arch/mips/zboot/xxs1500/Makefile linux.spi/arch/mips/zboot/xxs1500/Makefile
+--- linux/arch/mips/zboot/xxs1500/Makefile 1969-12-31 19:00:00.000000000 -0500
++++ linux.spi/arch/mips/zboot/xxs1500/Makefile 2004-05-11 23:19:24.000000000 -0400
+@@ -0,0 +1,123 @@
++# arch/mips/compressed/alchemy/Makefile
++#
++# Makefile for Alchemy Semiconductor Pb1[015]00 boards.
++# All of the boot loader code was derived from the ppc
++# boot code.
++#
++# Copyright 2001,2002 MontaVista Software Inc.
++#
++# Author: Mark A. Greer
++# mgreer@mvista.com
++# Ported and modified for mips support by
++# Pete Popov <ppopov@mvista.com>
++#
++# This program is free software; you can redistribute it and/or modify it
++# under the terms of the GNU General Public License as published by the
++# Free Software Foundation; either version 2 of the License, or (at your
++# option) any later version.
++
++.c.s:
++ $(CC) $(CFLAGS) -S -o $*.s $<
++.s.o:
++ $(AS) -o $*.o $<
++.c.o:
++ $(CC) $(CFLAGS) -D__BOOTER__ -c -o $*.o $<
++.S.s:
++ $(CPP) $(AFLAGS) -o $*.o $<
++.S.o:
++ $(CC) $(AFLAGS) -c -o $*.o $<
++
++#########################################################################
++# START BOARD SPECIFIC VARIABLES
++BNAME=xxs1500
++
++
++# These two variables control where the zImage is stored
++# in flash and loaded in memory. If you change either one,
++# be sure to make the appropriate change to the zImage
++# rule.
++RAM_LOAD_ADDR = 0x81000000
++FLASH_LOAD_ADDR = 0xBF000000
++
++# These two variables specify the free ram region
++# that can be used for temporary malloc area
++AVAIL_RAM_START=0x80400000
++AVAIL_RAM_END=0x80800000
++
++# This one must match the LOADADDR in arch/mips/Makefile!
++LOADADDR=0x80100000
++# END BOARD SPECIFIC VARIABLES
++#########################################################################
++
++ZLINKFLAGS = -T ld.script -Ttext $(RAM_LOAD_ADDR)
++
++OBJECTS := head.o ../common/misc-common.o ../common/misc-simple.o \
++ ../common/au1k_uart.o ../common/string.o ../common/ctype.o
++LIBS := ../lib/zlib.a
++
++ENTRY := ../utils/entry
++OFFSET := ../utils/offset
++SIZE := ../utils/size
++
++all: zImage
++
++clean:
++ rm -rf *.o vmlinux* zvmlinux.*
++
++head.o: head.S $(TOPDIR)/vmlinux
++ $(CC) -DFLASH_LOAD_ADDR=$(FLASH_LOAD_ADDR) $(AFLAGS) \
++ -DKERNEL_ENTRY=$(shell sh $(ENTRY) $(NM) $(TOPDIR)/vmlinux ) \
++ -c -o $*.o $<
++
++../common/misc-simple.o:
++ $(CC) $(CFLAGS) -DINITRD_OFFSET=0 -DINITRD_SIZE=0 -DZIMAGE_OFFSET=0 \
++ -DAVAIL_RAM_START=$(AVAIL_RAM_START) \
++ -DAVAIL_RAM_END=$(AVAIL_RAM_END) \
++ -DLOADADDR=$(LOADADDR) \
++ -DZIMAGE_SIZE=0 -c -o $@ $*.c
++
++# This is the first pass at building the boot loader image,
++# without knowing the file offset where the vmlinuz.gz
++# kernel will end up. We build this image, check the offset,
++# and then rebuild it with the correct offset and size
++# passed to mips-simple.c
++zvmlinux.no: $(OBJECTS) $(LIBS) ../images/vmlinux.gz
++ $(LD) $(ZLINKFLAGS) -o $@.tmp $(OBJECTS) $(LIBS)
++ $(OBJCOPY) -R .comment \
++ --add-section=image=../images/vmlinux.gz \
++ $@.tmp $@
++ # rm -f $@.tmp
++
++
++# This is the final image we build, now that we know what
++# the vmlinuz.gz offset is.
++zvmlinux: $(OBJECTS) $(LIBS) ../images/vmlinux.gz zvmlinux.no
++ $(CC) $(CFLAGS) -DINITRD_OFFSET=0 -DINITRD_SIZE=0 \
++ -DZIMAGE_OFFSET=$(shell sh $(OFFSET) $(OBJDUMP) $@.no image) \
++ -DZIMAGE_SIZE=$(shell sh $(SIZE) $(OBJDUMP) $@.no image) \
++ -D__BOOTER__ \
++ -DAVAIL_RAM_START=$(AVAIL_RAM_START) \
++ -DAVAIL_RAM_END=$(AVAIL_RAM_END) \
++ -DLOADADDR=$(LOADADDR) \
++ -c -o ../common/misc-simple.o ../common/misc-simple.c
++ $(LD) $(ZLINKFLAGS) -o $@.tmp $(OBJECTS) $(LIBS)
++ $(OBJCOPY) -R .comment \
++ --add-section=image=../images/vmlinux.gz \
++ $@.tmp $@
++ $(OBJCOPY) --adjust-section-vma=image+$(RAM_LOAD_ADDR) $@
++ $(OBJCOPY) --adjust-section-vma=image+$(shell sh $(OFFSET) \
++ $(OBJDUMP) $@.no image ) $@
++ # rm -f $@.tmp
++ # rm -f $@.no
++
++
++# Here we manipulate the image in order to get it the necessary
++# srecord file we need.
++zImage: zvmlinux
++ mv zvmlinux ../images/$@.$(BNAME)
++ $(OBJCOPY) --set-section-flags=image=alloc,load,code ../images/$@.$(BNAME)
++ $(OBJCOPY) -O srec --adjust-vma 0x3e000000 \
++ ../images/$@.$(BNAME) ../images/$@.$(BNAME).srec
++ # rm ../images/vmlinux.gz
++
++include $(TOPDIR)/Rules.make
diff --git a/linux/linux-mtx-1-2.4.27/07-zboot-zimage-flash-bin.diff b/linux/linux-mtx-1-2.4.27/07-zboot-zimage-flash-bin.diff
index e69de29bb2..dca79a3000 100644
--- a/linux/linux-mtx-1-2.4.27/07-zboot-zimage-flash-bin.diff
+++ b/linux/linux-mtx-1-2.4.27/07-zboot-zimage-flash-bin.diff
@@ -0,0 +1,11 @@
+diff -Nurb oe/tmp/work/linux-mtx-1-2.4.27-r0/linux/arch/mips/zboot/pb1xxx/Makefile linux.m/arch/mips/zboot/pb1xxx/Makefile
+--- oe/tmp/work/linux-mtx-1-2.4.27-r0/linux/arch/mips/zboot/pb1xxx/Makefile 2004-10-13 21:08:49.840408328 +0200
++++ linux.m/arch/mips/zboot/pb1xxx/Makefile 2004-10-13 21:08:29.736464592 +0200
+@@ -131,5 +131,7 @@
+ zImage.flash: zImage
+ $(OBJCOPY) -O srec --adjust-vma 0x3ed00000 \
+ ../images/zImage.$(BNAME) ../images/$(BNAME).flash.srec
++ $(OBJCOPY) -O binary --adjust-vma 0x3ed00000 \
++ ../images/zImage.$(BNAME) ../images/$(BNAME).flash.bin
+
+ include $(TOPDIR)/Rules.make
diff --git a/linux/linux-mtx-1-2.4.27/08-usb-nonpci-2.4.24.patch b/linux/linux-mtx-1-2.4.27/08-usb-nonpci-2.4.24.patch
index e69de29bb2..ca093e1a67 100644
--- a/linux/linux-mtx-1-2.4.27/08-usb-nonpci-2.4.24.patch
+++ b/linux/linux-mtx-1-2.4.27/08-usb-nonpci-2.4.24.patch
@@ -0,0 +1,3185 @@
+diff -Nurb linux-mips-2.4.27/drivers/usb/host/Config.in linux/drivers/usb/host/Config.in
+--- linux-mips-2.4.27/drivers/usb/host/Config.in 2003-11-17 02:07:42.000000000 +0100
++++ linux/drivers/usb/host/Config.in 2004-11-23 11:24:10.599627448 +0100
+@@ -17,3 +17,4 @@
+ dep_tristate ' SL811HS Alternate (x86, StrongARM, isosynchronous mode)' CONFIG_USB_SL811HS_ALT $CONFIG_USB $CONFIG_EXPERIMENTAL
+ dep_tristate ' SL811HS (x86, StrongARM) support, old driver' CONFIG_USB_SL811HS $CONFIG_USB $CONFIG_EXPERIMENTAL
+ fi
++dep_tristate ' Non-PCI OHCI support' CONFIG_USB_NON_PCI_OHCI $CONFIG_USB_OHCI
+diff -Nurb linux-mips-2.4.27/drivers/usb/host/usb-ohci.c linux/drivers/usb/host/usb-ohci.c
+--- linux-mips-2.4.27/drivers/usb/host/usb-ohci.c 2004-04-16 05:14:18.000000000 +0200
++++ linux/drivers/usb/host/usb-ohci.c 2004-11-23 11:24:10.602626992 +0100
+@@ -2564,6 +2564,7 @@
+ hc_release_ohci (ohci);
+ return ret;
+ }
++#ifndef CONFIG_USB_NON_PCI_OHCI
+ ohci->flags = id->driver_data;
+
+ /* Check for NSC87560. We have to look at the bridge (fn1) to identify
+@@ -2582,6 +2583,7 @@
+ printk (KERN_INFO __FILE__ ": Using NSC SuperIO setup\n");
+ if (ohci->flags & OHCI_QUIRK_AMD756)
+ printk (KERN_INFO __FILE__ ": AMD756 erratum 4 workaround\n");
++#endif
+
+ if (hc_reset (ohci) < 0) {
+ hc_release_ohci (ohci);
+@@ -2627,8 +2629,10 @@
+ int temp;
+ int i;
+
++#ifndef CONFIG_USB_NON_PCI_OHCI
+ if (ohci->pci_latency)
+ pci_write_config_byte (ohci->ohci_dev, PCI_LATENCY_TIMER, ohci->pci_latency);
++#endif
+
+ ohci->disabled = 1;
+ ohci->sleeping = 0;
+@@ -2658,6 +2662,7 @@
+
+ /*-------------------------------------------------------------------------*/
+
++#ifndef CONFIG_USB_NON_PCI_OHCI
+ /* configured so that an OHCI device is always provided */
+ /* always called with process context; sleeping is OK */
+
+@@ -2705,6 +2710,88 @@
+ }
+ return status;
+ }
++#else /* CONFIG_USB_NON_PCI_OHCI */
++
++// Boot options
++static int ohci_base=0, ohci_len=0;
++static int ohci_irq=-1;
++
++MODULE_PARM(ohci_base, "i");
++MODULE_PARM(ohci_len, "i");
++MODULE_PARM(ohci_irq, "i");
++MODULE_PARM_DESC(ohci_base, "IO Base address of OHCI Oper. registers");
++MODULE_PARM_DESC(ohci_len, "IO length of OHCI Oper. registers");
++MODULE_PARM_DESC(ohci_irq, "IRQ for OHCI interrupts");
++
++// bogus pci_dev
++static struct pci_dev bogus_pcidev;
++
++static struct pci_driver ohci_pci_driver = {
++ name: "usb-ohci",
++};
++
++static int __devinit
++ohci_non_pci_init (void)
++{
++ void *mem_base;
++
++ if (!ohci_base || !ohci_len || (ohci_irq < 0))
++ return -ENODEV;
++
++ if (!request_mem_region (ohci_base, ohci_len, ohci_pci_driver.name)) {
++ dbg ("controller already in use");
++ return -EBUSY;
++ }
++
++ mem_base = ioremap_nocache (ohci_base, ohci_len);
++ if (!mem_base) {
++ err("Error mapping OHCI memory");
++ return -EFAULT;
++ }
++
++ /*
++ * Fill in the bogus pci_dev. Only those members actually
++ * dereferenced in this driver are initialized.
++ */
++ memset(&bogus_pcidev, 0, sizeof(struct pci_dev));
++ strcpy(bogus_pcidev.name, "non-PCI OHCI");
++ strcpy(bogus_pcidev.slot_name, "builtin");
++ bogus_pcidev.resource[0].name = "OHCI Operational Registers";
++ bogus_pcidev.resource[0].start = ohci_base;
++ bogus_pcidev.resource[0].end = ohci_base + ohci_len;
++ bogus_pcidev.resource[0].flags = 0;
++ bogus_pcidev.irq = ohci_irq;
++
++ return hc_found_ohci (&bogus_pcidev, bogus_pcidev.irq, mem_base, NULL);
++}
++
++#ifndef MODULE
++
++static int __init
++ohci_setup (char* options)
++{
++ char* this_opt;
++
++ if (!options || !*options)
++ return 0;
++
++ for(this_opt=strtok(options,",");this_opt;this_opt=strtok(NULL,",")) {
++ if (!strncmp(this_opt, "base:", 5)) {
++ ohci_base = simple_strtoul(this_opt+5, NULL, 0);
++ } else if (!strncmp(this_opt, "len:", 4)) {
++ ohci_len = simple_strtoul(this_opt+4, NULL, 0);
++ } else if (!strncmp(this_opt, "irq:", 4)) {
++ ohci_irq = simple_strtoul(this_opt+4, NULL, 0);
++ }
++ }
++ return 0;
++}
++
++__setup("usb_ohci=", ohci_setup);
++
++#endif /* !MODULE */
++
++#endif /* CONFIG_USB_NON_PCI_OHCI */
+
+ /*-------------------------------------------------------------------------*/
+
+@@ -2745,6 +2832,7 @@
+ }
+
+
++#ifndef CONFIG_USB_NON_PCI_OHCI
+ #ifdef CONFIG_PM
+
+ /*-------------------------------------------------------------------------*/
+@@ -2983,20 +3071,29 @@
+ resume: ohci_pci_resume,
+ #endif /* PM */
+ };
++#endif /* CONFIG_USB_NON_PCI_OHCI */
+
+
+ /*-------------------------------------------------------------------------*/
+
+ static int __init ohci_hcd_init (void)
+ {
++#ifndef CONFIG_USB_NON_PCI_OHCI
+ return pci_module_init (&ohci_pci_driver);
++#else
++ return ohci_non_pci_init();
++#endif
+ }
+
+ /*-------------------------------------------------------------------------*/
+
+ static void __exit ohci_hcd_cleanup (void)
+ {
++#ifndef CONFIG_USB_NON_PCI_OHCI
+ pci_unregister_driver (&ohci_pci_driver);
++#else
++ ohci_pci_remove(&bogus_pcidev);
++#endif
+ }
+
+ module_init (ohci_hcd_init);
+diff -Nurb linux-mips-2.4.27/drivers/usb/host/usb-ohci.c.orig linux/drivers/usb/host/usb-ohci.c.orig
+--- linux-mips-2.4.27/drivers/usb/host/usb-ohci.c.orig 1970-01-01 01:00:00.000000000 +0100
++++ linux/drivers/usb/host/usb-ohci.c.orig 2004-11-23 11:21:54.984244120 +0100
+@@ -0,0 +1,3008 @@
++/*
++ * URB OHCI HCD (Host Controller Driver) for USB.
++ *
++ * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
++ * (C) Copyright 2000-2001 David Brownell <dbrownell@users.sourceforge.net>
++ *
++ * [ Initialisation is based on Linus' ]
++ * [ uhci code and gregs ohci fragments ]
++ * [ (C) Copyright 1999 Linus Torvalds ]
++ * [ (C) Copyright 1999 Gregory P. Smith]
++ *
++ *
++ * History:
++ *
++ * 2002/10/22 OHCI_USB_OPER for ALi lockup in IBM i1200 (ALEX <thchou@ali>)
++ * 2002/03/08 interrupt unlink fix (Matt Hughes), better cleanup on
++ * load failure (Matthew Frederickson)
++ * 2002/01/20 async unlink fixes: return -EINPROGRESS (per spec) and
++ * make interrupt unlink-in-completion work (db)
++ * 2001/09/19 USB_ZERO_PACKET support (Jean Tourrilhes)
++ * 2001/07/17 power management and pmac cleanup (Benjamin Herrenschmidt)
++ * 2001/03/24 td/ed hashing to remove bus_to_virt (Steve Longerbeam);
++ pci_map_single (db)
++ * 2001/03/21 td and dev/ed allocation uses new pci_pool API (db)
++ * 2001/03/07 hcca allocation uses pci_alloc_consistent (Steve Longerbeam)
++ *
++ * 2000/09/26 fixed races in removing the private portion of the urb
++ * 2000/09/07 disable bulk and control lists when unlinking the last
++ * endpoint descriptor in order to avoid unrecoverable errors on
++ * the Lucent chips. (rwc@sgi)
++ * 2000/08/29 use bandwidth claiming hooks (thanks Randy!), fix some
++ * urb unlink probs, indentation fixes
++ * 2000/08/11 various oops fixes mostly affecting iso and cleanup from
++ * device unplugs.
++ * 2000/06/28 use PCI hotplug framework, for better power management
++ * and for Cardbus support (David Brownell)
++ * 2000/earlier: fixes for NEC/Lucent chips; suspend/resume handling
++ * when the controller loses power; handle UE; cleanup; ...
++ *
++ * v5.2 1999/12/07 URB 3rd preview,
++ * v5.1 1999/11/30 URB 2nd preview, cpia, (usb-scsi)
++ * v5.0 1999/11/22 URB Technical preview, Paul Mackerras powerbook susp/resume
++ * i386: HUB, Keyboard, Mouse, Printer
++ *
++ * v4.3 1999/10/27 multiple HCs, bulk_request
++ * v4.2 1999/09/05 ISO API alpha, new dev alloc, neg Error-codes
++ * v4.1 1999/08/27 Randy Dunlap's - ISO API first impl.
++ * v4.0 1999/08/18
++ * v3.0 1999/06/25
++ * v2.1 1999/05/09 code clean up
++ * v2.0 1999/05/04
++ * v1.0 1999/04/27 initial release
++ */
++
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/pci.h>
++#include <linux/kernel.h>
++#include <linux/delay.h>
++#include <linux/ioport.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/smp_lock.h>
++#include <linux/errno.h>
++#include <linux/init.h>
++#include <linux/timer.h>
++#include <linux/list.h>
++#include <linux/interrupt.h> /* for in_interrupt() */
++#undef DEBUG
++#include <linux/usb.h>
++
++#include <asm/io.h>
++#include <asm/irq.h>
++#include <asm/system.h>
++#include <asm/unaligned.h>
++
++#define OHCI_USE_NPS // force NoPowerSwitching mode
++// #define OHCI_VERBOSE_DEBUG /* not always helpful */
++
++#include "usb-ohci.h"
++
++#include "../hcd.h"
++
++#ifdef CONFIG_PMAC_PBOOK
++#include <asm/machdep.h>
++#include <asm/pmac_feature.h>
++#include <asm/pci-bridge.h>
++#ifndef CONFIG_PM
++#define CONFIG_PM
++#endif
++#endif
++
++
++/*
++ * Version Information
++ */
++#define DRIVER_VERSION "v5.3"
++#define DRIVER_AUTHOR "Roman Weissgaerber <weissg@vienna.at>, David Brownell"
++#define DRIVER_DESC "USB OHCI Host Controller Driver"
++
++/* For initializing controller (mask in an HCFS mode too) */
++#define OHCI_CONTROL_INIT \
++ (OHCI_CTRL_CBSR & 0x3) | OHCI_CTRL_IE | OHCI_CTRL_PLE
++
++#define OHCI_UNLINK_TIMEOUT (HZ / 10)
++
++/*-------------------------------------------------------------------------*/
++
++/* AMD-756 (D2 rev) reports corrupt register contents in some cases.
++ * The erratum (#4) description is incorrect. AMD's workaround waits
++ * till some bits (mostly reserved) are clear; ok for all revs.
++ */
++#define read_roothub(hc, register, mask) ({ \
++ u32 temp = readl (&hc->regs->roothub.register); \
++ if (hc->flags & OHCI_QUIRK_AMD756) \
++ while (temp & mask) \
++ temp = readl (&hc->regs->roothub.register); \
++ temp; })
++
++static u32 roothub_a (struct ohci *hc)
++ { return read_roothub (hc, a, 0xfc0fe000); }
++static inline u32 roothub_b (struct ohci *hc)
++ { return readl (&hc->regs->roothub.b); }
++static inline u32 roothub_status (struct ohci *hc)
++ { return readl (&hc->regs->roothub.status); }
++static u32 roothub_portstatus (struct ohci *hc, int i)
++ { return read_roothub (hc, portstatus [i], 0xffe0fce0); }
++
++
++/*-------------------------------------------------------------------------*
++ * URB support functions
++ *-------------------------------------------------------------------------*/
++
++static void ohci_complete_add(struct ohci *ohci, struct urb *urb)
++{
++
++ if (urb->hcpriv != NULL) {
++ printk("completing with non-null priv!\n");
++ return;
++ }
++
++ if (ohci->complete_tail == NULL) {
++ ohci->complete_head = urb;
++ ohci->complete_tail = urb;
++ } else {
++ ohci->complete_head->hcpriv = urb;
++ ohci->complete_tail = urb;
++ }
++}
++
++static inline struct urb *ohci_complete_get(struct ohci *ohci)
++{
++ struct urb *urb;
++
++ if ((urb = ohci->complete_head) == NULL)
++ return NULL;
++ if (urb == ohci->complete_tail) {
++ ohci->complete_tail = NULL;
++ ohci->complete_head = NULL;
++ } else {
++ ohci->complete_head = urb->hcpriv;
++ }
++ urb->hcpriv = NULL;
++ return urb;
++}
++
++static inline void ohci_complete(struct ohci *ohci)
++{
++ struct urb *urb;
++
++ spin_lock(&ohci->ohci_lock);
++ while ((urb = ohci_complete_get(ohci)) != NULL) {
++ spin_unlock(&ohci->ohci_lock);
++ if (urb->dev) {
++ usb_dec_dev_use (urb->dev);
++ urb->dev = NULL;
++ }
++ if (urb->complete)
++ (*urb->complete)(urb);
++ spin_lock(&ohci->ohci_lock);
++ }
++ spin_unlock(&ohci->ohci_lock);
++}
++
++/* free HCD-private data associated with this URB */
++
++static void urb_free_priv (struct ohci *hc, urb_priv_t * urb_priv)
++{
++ int i;
++ int last = urb_priv->length - 1;
++ int len;
++ int dir;
++ struct td *td;
++
++ if (last >= 0) {
++
++ /* ISOC, BULK, INTR data buffer starts at td 0
++ * CTRL setup starts at td 0 */
++ td = urb_priv->td [0];
++
++ len = td->urb->transfer_buffer_length,
++ dir = usb_pipeout (td->urb->pipe)
++ ? PCI_DMA_TODEVICE
++ : PCI_DMA_FROMDEVICE;
++
++ /* unmap CTRL URB setup */
++ if (usb_pipecontrol (td->urb->pipe)) {
++ pci_unmap_single (hc->ohci_dev,
++ td->data_dma, 8, PCI_DMA_TODEVICE);
++
++ /* CTRL data buffer starts at td 1 if len > 0 */
++ if (len && last > 0)
++ td = urb_priv->td [1];
++ }
++
++ /* unmap data buffer */
++ if (len && td->data_dma)
++ pci_unmap_single (hc->ohci_dev, td->data_dma, len, dir);
++
++ for (i = 0; i <= last; i++) {
++ td = urb_priv->td [i];
++ if (td)
++ td_free (hc, td);
++ }
++ }
++
++ kfree (urb_priv);
++}
++
++static void urb_rm_priv_locked (struct urb * urb)
++{
++ urb_priv_t * urb_priv = urb->hcpriv;
++
++ if (urb_priv) {
++ urb->hcpriv = NULL;
++
++#ifdef DO_TIMEOUTS
++ if (urb->timeout) {
++ list_del (&urb->urb_list);
++ urb->timeout -= jiffies;
++ }
++#endif
++
++ /* Release int/iso bandwidth */
++ if (urb->bandwidth) {
++ switch (usb_pipetype(urb->pipe)) {
++ case PIPE_INTERRUPT:
++ usb_release_bandwidth (urb->dev, urb, 0);
++ break;
++ case PIPE_ISOCHRONOUS:
++ usb_release_bandwidth (urb->dev, urb, 1);
++ break;
++ default:
++ break;
++ }
++ }
++
++ urb_free_priv ((struct ohci *)urb->dev->bus->hcpriv, urb_priv);
++ } else {
++ if (urb->dev != NULL) {
++ err ("Non-null dev at rm_priv time");
++ // urb->dev = NULL;
++ }
++ }
++}
++
++/*-------------------------------------------------------------------------*/
++
++#ifdef DEBUG
++static int sohci_get_current_frame_number (struct usb_device * dev);
++
++/* debug| print the main components of an URB
++ * small: 0) header + data packets 1) just header */
++
++static void urb_print (struct urb * urb, char * str, int small)
++{
++ unsigned int pipe= urb->pipe;
++
++ if (!urb->dev || !urb->dev->bus) {
++ dbg("%s URB: no dev", str);
++ return;
++ }
++
++#ifndef OHCI_VERBOSE_DEBUG
++ if (urb->status != 0)
++#endif
++ dbg("%s URB:[%4x] dev:%2d,ep:%2d-%c,type:%s,flags:%4x,len:%d/%d,stat:%d(%x)",
++ str,
++ sohci_get_current_frame_number (urb->dev),
++ usb_pipedevice (pipe),
++ usb_pipeendpoint (pipe),
++ usb_pipeout (pipe)? 'O': 'I',
++ usb_pipetype (pipe) < 2? (usb_pipeint (pipe)? "INTR": "ISOC"):
++ (usb_pipecontrol (pipe)? "CTRL": "BULK"),
++ urb->transfer_flags,
++ urb->actual_length,
++ urb->transfer_buffer_length,
++ urb->status, urb->status);
++#ifdef OHCI_VERBOSE_DEBUG
++ if (!small) {
++ int i, len;
++
++ if (usb_pipecontrol (pipe)) {
++ printk (KERN_DEBUG __FILE__ ": cmd(8):");
++ for (i = 0; i < 8 ; i++)
++ printk (" %02x", ((__u8 *) urb->setup_packet) [i]);
++ printk ("\n");
++ }
++ if (urb->transfer_buffer_length > 0 && urb->transfer_buffer) {
++ printk (KERN_DEBUG __FILE__ ": data(%d/%d):",
++ urb->actual_length,
++ urb->transfer_buffer_length);
++ len = usb_pipeout (pipe)?
++ urb->transfer_buffer_length: urb->actual_length;
++ for (i = 0; i < 16 && i < len; i++)
++ printk (" %02x", ((__u8 *) urb->transfer_buffer) [i]);
++ printk ("%s stat:%d\n", i < len? "...": "", urb->status);
++ }
++ }
++#endif
++}
++
++/* just for debugging; prints non-empty branches of the int ed tree inclusive iso eds*/
++void ep_print_int_eds (ohci_t * ohci, char * str) {
++ int i, j;
++ __u32 * ed_p;
++ for (i= 0; i < 32; i++) {
++ j = 5;
++ ed_p = &(ohci->hcca->int_table [i]);
++ if (*ed_p == 0)
++ continue;
++ printk (KERN_DEBUG __FILE__ ": %s branch int %2d(%2x):", str, i, i);
++ while (*ed_p != 0 && j--) {
++ ed_t *ed = dma_to_ed (ohci, le32_to_cpup(ed_p));
++ printk (" ed: %4x;", ed->hwINFO);
++ ed_p = &ed->hwNextED;
++ }
++ printk ("\n");
++ }
++}
++
++
++static void ohci_dump_intr_mask (char *label, __u32 mask)
++{
++ dbg ("%s: 0x%08x%s%s%s%s%s%s%s%s%s",
++ label,
++ mask,
++ (mask & OHCI_INTR_MIE) ? " MIE" : "",
++ (mask & OHCI_INTR_OC) ? " OC" : "",
++ (mask & OHCI_INTR_RHSC) ? " RHSC" : "",
++ (mask & OHCI_INTR_FNO) ? " FNO" : "",
++ (mask & OHCI_INTR_UE) ? " UE" : "",
++ (mask & OHCI_INTR_RD) ? " RD" : "",
++ (mask & OHCI_INTR_SF) ? " SF" : "",
++ (mask & OHCI_INTR_WDH) ? " WDH" : "",
++ (mask & OHCI_INTR_SO) ? " SO" : ""
++ );
++}
++
++static void maybe_print_eds (char *label, __u32 value)
++{
++ if (value)
++ dbg ("%s %08x", label, value);
++}
++
++static char *hcfs2string (int state)
++{
++ switch (state) {
++ case OHCI_USB_RESET: return "reset";
++ case OHCI_USB_RESUME: return "resume";
++ case OHCI_USB_OPER: return "operational";
++ case OHCI_USB_SUSPEND: return "suspend";
++ }
++ return "?";
++}
++
++// dump control and status registers
++static void ohci_dump_status (ohci_t *controller)
++{
++ struct ohci_regs *regs = controller->regs;
++ __u32 temp;
++
++ temp = readl (&regs->revision) & 0xff;
++ if (temp != 0x10)
++ dbg ("spec %d.%d", (temp >> 4), (temp & 0x0f));
++
++ temp = readl (&regs->control);
++ dbg ("control: 0x%08x%s%s%s HCFS=%s%s%s%s%s CBSR=%d", temp,
++ (temp & OHCI_CTRL_RWE) ? " RWE" : "",
++ (temp & OHCI_CTRL_RWC) ? " RWC" : "",
++ (temp & OHCI_CTRL_IR) ? " IR" : "",
++ hcfs2string (temp & OHCI_CTRL_HCFS),
++ (temp & OHCI_CTRL_BLE) ? " BLE" : "",
++ (temp & OHCI_CTRL_CLE) ? " CLE" : "",
++ (temp & OHCI_CTRL_IE) ? " IE" : "",
++ (temp & OHCI_CTRL_PLE) ? " PLE" : "",
++ temp & OHCI_CTRL_CBSR
++ );
++
++ temp = readl (&regs->cmdstatus);
++ dbg ("cmdstatus: 0x%08x SOC=%d%s%s%s%s", temp,
++ (temp & OHCI_SOC) >> 16,
++ (temp & OHCI_OCR) ? " OCR" : "",
++ (temp & OHCI_BLF) ? " BLF" : "",
++ (temp & OHCI_CLF) ? " CLF" : "",
++ (temp & OHCI_HCR) ? " HCR" : ""
++ );
++
++ ohci_dump_intr_mask ("intrstatus", readl (&regs->intrstatus));
++ ohci_dump_intr_mask ("intrenable", readl (&regs->intrenable));
++ // intrdisable always same as intrenable
++ // ohci_dump_intr_mask ("intrdisable", readl (&regs->intrdisable));
++
++ maybe_print_eds ("ed_periodcurrent", readl (&regs->ed_periodcurrent));
++
++ maybe_print_eds ("ed_controlhead", readl (&regs->ed_controlhead));
++ maybe_print_eds ("ed_controlcurrent", readl (&regs->ed_controlcurrent));
++
++ maybe_print_eds ("ed_bulkhead", readl (&regs->ed_bulkhead));
++ maybe_print_eds ("ed_bulkcurrent", readl (&regs->ed_bulkcurrent));
++
++ maybe_print_eds ("donehead", readl (&regs->donehead));
++}
++
++static void ohci_dump_roothub (ohci_t *controller, int verbose)
++{
++ __u32 temp, ndp, i;
++
++ temp = roothub_a (controller);
++ if (temp == ~(u32)0)
++ return;
++ ndp = (temp & RH_A_NDP);
++
++ if (verbose) {
++ dbg ("roothub.a: %08x POTPGT=%d%s%s%s%s%s NDP=%d", temp,
++ ((temp & RH_A_POTPGT) >> 24) & 0xff,
++ (temp & RH_A_NOCP) ? " NOCP" : "",
++ (temp & RH_A_OCPM) ? " OCPM" : "",
++ (temp & RH_A_DT) ? " DT" : "",
++ (temp & RH_A_NPS) ? " NPS" : "",
++ (temp & RH_A_PSM) ? " PSM" : "",
++ ndp
++ );
++ temp = roothub_b (controller);
++ dbg ("roothub.b: %08x PPCM=%04x DR=%04x",
++ temp,
++ (temp & RH_B_PPCM) >> 16,
++ (temp & RH_B_DR)
++ );
++ temp = roothub_status (controller);
++ dbg ("roothub.status: %08x%s%s%s%s%s%s",
++ temp,
++ (temp & RH_HS_CRWE) ? " CRWE" : "",
++ (temp & RH_HS_OCIC) ? " OCIC" : "",
++ (temp & RH_HS_LPSC) ? " LPSC" : "",
++ (temp & RH_HS_DRWE) ? " DRWE" : "",
++ (temp & RH_HS_OCI) ? " OCI" : "",
++ (temp & RH_HS_LPS) ? " LPS" : ""
++ );
++ }
++
++ for (i = 0; i < ndp; i++) {
++ temp = roothub_portstatus (controller, i);
++ dbg ("roothub.portstatus [%d] = 0x%08x%s%s%s%s%s%s%s%s%s%s%s%s",
++ i,
++ temp,
++ (temp & RH_PS_PRSC) ? " PRSC" : "",
++ (temp & RH_PS_OCIC) ? " OCIC" : "",
++ (temp & RH_PS_PSSC) ? " PSSC" : "",
++ (temp & RH_PS_PESC) ? " PESC" : "",
++ (temp & RH_PS_CSC) ? " CSC" : "",
++
++ (temp & RH_PS_LSDA) ? " LSDA" : "",
++ (temp & RH_PS_PPS) ? " PPS" : "",
++ (temp & RH_PS_PRS) ? " PRS" : "",
++ (temp & RH_PS_POCI) ? " POCI" : "",
++ (temp & RH_PS_PSS) ? " PSS" : "",
++
++ (temp & RH_PS_PES) ? " PES" : "",
++ (temp & RH_PS_CCS) ? " CCS" : ""
++ );
++ }
++}
++
++static void ohci_dump (ohci_t *controller, int verbose)
++{
++ dbg ("OHCI controller usb-%s state", controller->ohci_dev->slot_name);
++
++ // dumps some of the state we know about
++ ohci_dump_status (controller);
++ if (verbose)
++ ep_print_int_eds (controller, "hcca");
++ dbg ("hcca frame #%04x", controller->hcca->frame_no);
++ ohci_dump_roothub (controller, 1);
++}
++
++
++#endif
++
++/*-------------------------------------------------------------------------*
++ * Interface functions (URB)
++ *-------------------------------------------------------------------------*/
++
++/* return a request to the completion handler */
++
++static int sohci_return_urb (struct ohci *hc, struct urb * urb)
++{
++ urb_priv_t * urb_priv = urb->hcpriv;
++ struct urb * urbt;
++ int i;
++
++ if (!urb_priv)
++ return -1; /* urb already unlinked */
++
++ /* just to be sure */
++ if (!urb->complete) {
++ urb_rm_priv_locked (urb);
++ ohci_complete_add(hc, urb); /* Just usb_dec_dev_use */
++ return -1;
++ }
++
++#ifdef DEBUG
++ urb_print (urb, "RET", usb_pipeout (urb->pipe));
++#endif
++
++ switch (usb_pipetype (urb->pipe)) {
++ case PIPE_INTERRUPT:
++ pci_unmap_single (hc->ohci_dev,
++ urb_priv->td [0]->data_dma,
++ urb->transfer_buffer_length,
++ usb_pipeout (urb->pipe)
++ ? PCI_DMA_TODEVICE
++ : PCI_DMA_FROMDEVICE);
++ if (urb->interval) {
++ urb->complete (urb);
++
++ /* implicitly requeued */
++ urb->actual_length = 0;
++ urb->status = -EINPROGRESS;
++ td_submit_urb (urb);
++ } else {
++ urb_rm_priv_locked (urb);
++ ohci_complete_add(hc, urb);
++ }
++ break;
++
++ case PIPE_ISOCHRONOUS:
++ for (urbt = urb->next; urbt && (urbt != urb); urbt = urbt->next);
++ if (urbt) { /* send the reply and requeue URB */
++ pci_unmap_single (hc->ohci_dev,
++ urb_priv->td [0]->data_dma,
++ urb->transfer_buffer_length,
++ usb_pipeout (urb->pipe)
++ ? PCI_DMA_TODEVICE
++ : PCI_DMA_FROMDEVICE);
++ urb->complete (urb);
++ urb->actual_length = 0;
++ urb->status = USB_ST_URB_PENDING;
++ urb->start_frame = urb_priv->ed->last_iso + 1;
++ if (urb_priv->state != URB_DEL) {
++ for (i = 0; i < urb->number_of_packets; i++) {
++ urb->iso_frame_desc[i].actual_length = 0;
++ urb->iso_frame_desc[i].status = -EXDEV;
++ }
++ td_submit_urb (urb);
++ }
++
++ } else { /* unlink URB, call complete */
++ urb_rm_priv_locked (urb);
++ ohci_complete_add(hc, urb);
++ }
++ break;
++
++ case PIPE_BULK:
++ case PIPE_CONTROL: /* unlink URB, call complete */
++ urb_rm_priv_locked (urb);
++ ohci_complete_add(hc, urb);
++ break;
++ }
++ return 0;
++}
++
++/*-------------------------------------------------------------------------*/
++
++/* get a transfer request */
++
++static int sohci_submit_urb (struct urb * urb)
++{
++ ohci_t * ohci;
++ ed_t * ed;
++ urb_priv_t * urb_priv;
++ unsigned int pipe = urb->pipe;
++ int maxps = usb_maxpacket (urb->dev, pipe, usb_pipeout (pipe));
++ int i, size = 0;
++ unsigned long flags;
++ int bustime = 0;
++ int mem_flags = GFP_ATOMIC;
++
++ if (!urb->dev || !urb->dev->bus)
++ return -ENODEV;
++
++ if (urb->hcpriv) /* urb already in use */
++ return -EINVAL;
++
++// if(usb_endpoint_halted (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe)))
++// return -EPIPE;
++
++ usb_inc_dev_use (urb->dev);
++ ohci = (ohci_t *) urb->dev->bus->hcpriv;
++
++#ifdef DEBUG
++ urb_print (urb, "SUB", usb_pipein (pipe));
++#endif
++
++ /* handle a request to the virtual root hub */
++ if (usb_pipedevice (pipe) == ohci->rh.devnum)
++ return rh_submit_urb (urb);
++
++ spin_lock_irqsave(&ohci->ohci_lock, flags);
++
++ /* when controller's hung, permit only roothub cleanup attempts
++ * such as powering down ports */
++ if (ohci->disabled) {
++ spin_unlock_irqrestore(&ohci->ohci_lock, flags);
++ usb_dec_dev_use (urb->dev);
++ return -ESHUTDOWN;
++ }
++
++ /* every endpoint has a ed, locate and fill it */
++ if (!(ed = ep_add_ed (urb->dev, pipe, urb->interval, 1, mem_flags))) {
++ spin_unlock_irqrestore(&ohci->ohci_lock, flags);
++ usb_dec_dev_use (urb->dev);
++ return -ENOMEM;
++ }
++
++ /* for the private part of the URB we need the number of TDs (size) */
++ switch (usb_pipetype (pipe)) {
++ case PIPE_BULK: /* one TD for every 4096 Byte */
++ size = (urb->transfer_buffer_length - 1) / 4096 + 1;
++
++ /* If the transfer size is multiple of the pipe mtu,
++ * we may need an extra TD to create a empty frame
++ * Jean II */
++ if ((urb->transfer_flags & USB_ZERO_PACKET) &&
++ usb_pipeout (pipe) &&
++ (urb->transfer_buffer_length != 0) &&
++ ((urb->transfer_buffer_length % maxps) == 0))
++ size++;
++ break;
++ case PIPE_ISOCHRONOUS: /* number of packets from URB */
++ size = urb->number_of_packets;
++ if (size <= 0) {
++ spin_unlock_irqrestore(&ohci->ohci_lock, flags);
++ usb_dec_dev_use (urb->dev);
++ return -EINVAL;
++ }
++ for (i = 0; i < urb->number_of_packets; i++) {
++ urb->iso_frame_desc[i].actual_length = 0;
++ urb->iso_frame_desc[i].status = -EXDEV;
++ }
++ break;
++ case PIPE_CONTROL: /* 1 TD for setup, 1 for ACK and 1 for every 4096 B */
++ size = (urb->transfer_buffer_length == 0)? 2:
++ (urb->transfer_buffer_length - 1) / 4096 + 3;
++ break;
++ case PIPE_INTERRUPT: /* one TD */
++ size = 1;
++ break;
++ }
++
++ /* allocate the private part of the URB */
++ urb_priv = kmalloc (sizeof (urb_priv_t) + size * sizeof (td_t *),
++ GFP_ATOMIC);
++ if (!urb_priv) {
++ spin_unlock_irqrestore(&ohci->ohci_lock, flags);
++ usb_dec_dev_use (urb->dev);
++ return -ENOMEM;
++ }
++ memset (urb_priv, 0, sizeof (urb_priv_t) + size * sizeof (td_t *));
++
++ /* fill the private part of the URB */
++ urb_priv->length = size;
++ urb_priv->ed = ed;
++
++ /* allocate the TDs (updating hash chains) */
++ for (i = 0; i < size; i++) {
++ urb_priv->td[i] = td_alloc (ohci, SLAB_ATOMIC);
++ if (!urb_priv->td[i]) {
++ urb_priv->length = i;
++ urb_free_priv (ohci, urb_priv);
++ spin_unlock_irqrestore(&ohci->ohci_lock, flags);
++ usb_dec_dev_use (urb->dev);
++ return -ENOMEM;
++ }
++ }
++
++ if (ed->state == ED_NEW || (ed->state & ED_DEL)) {
++ urb_free_priv (ohci, urb_priv);
++ spin_unlock_irqrestore(&ohci->ohci_lock, flags);
++ usb_dec_dev_use (urb->dev);
++ return -EINVAL;
++ }
++
++ /* allocate and claim bandwidth if needed; ISO
++ * needs start frame index if it was't provided.
++ */
++ switch (usb_pipetype (pipe)) {
++ case PIPE_ISOCHRONOUS:
++ if (urb->transfer_flags & USB_ISO_ASAP) {
++ urb->start_frame = ((ed->state == ED_OPER)
++ ? (ed->last_iso + 1)
++ : (le16_to_cpu (ohci->hcca->frame_no) + 10)) & 0xffff;
++ }
++ /* FALLTHROUGH */
++ case PIPE_INTERRUPT:
++ if (urb->bandwidth == 0) {
++ bustime = usb_check_bandwidth (urb->dev, urb);
++ }
++ if (bustime < 0) {
++ urb_free_priv (ohci, urb_priv);
++ spin_unlock_irqrestore(&ohci->ohci_lock, flags);
++ usb_dec_dev_use (urb->dev);
++ return bustime;
++ }
++ usb_claim_bandwidth (urb->dev, urb, bustime, usb_pipeisoc (urb->pipe));
++#ifdef DO_TIMEOUTS
++ urb->timeout = 0;
++#endif
++ }
++
++ urb->actual_length = 0;
++ urb->hcpriv = urb_priv;
++ urb->status = USB_ST_URB_PENDING;
++
++ /* link the ed into a chain if is not already */
++ if (ed->state != ED_OPER)
++ ep_link (ohci, ed);
++
++ /* fill the TDs and link it to the ed */
++ td_submit_urb (urb);
++
++#ifdef DO_TIMEOUTS
++ /* maybe add to ordered list of timeouts */
++ if (urb->timeout) {
++ struct list_head *entry;
++
++ urb->timeout += jiffies;
++
++ list_for_each (entry, &ohci->timeout_list) {
++ struct urb *next_urb;
++
++ next_urb = list_entry (entry, struct urb, urb_list);
++ if (time_after_eq (urb->timeout, next_urb->timeout))
++ break;
++ }
++ list_add (&urb->urb_list, entry);
++
++ /* drive timeouts by SF (messy, but works) */
++ writel (OHCI_INTR_SF, &ohci->regs->intrenable);
++ (void)readl (&ohci->regs->intrdisable); /* PCI posting flush */
++ }
++#endif
++
++ spin_unlock_irqrestore(&ohci->ohci_lock, flags);
++
++ return 0;
++}
++
++/*-------------------------------------------------------------------------*/
++
++/* deactivate all TDs and remove the private part of the URB */
++/* interrupt callers must use async unlink mode */
++
++static int sohci_unlink_urb (struct urb * urb)
++{
++ unsigned long flags;
++ ohci_t * ohci;
++
++ if (!urb) /* just to be sure */
++ return -EINVAL;
++
++ if (!urb->dev || !urb->dev->bus)
++ return -ENODEV;
++
++ ohci = (ohci_t *) urb->dev->bus->hcpriv;
++
++#ifdef DEBUG
++ urb_print (urb, "UNLINK", 1);
++#endif
++
++ /* handle a request to the virtual root hub */
++ if (usb_pipedevice (urb->pipe) == ohci->rh.devnum)
++ return rh_unlink_urb (urb);
++
++ spin_lock_irqsave(&ohci->ohci_lock, flags);
++ if (urb->hcpriv && (urb->status == USB_ST_URB_PENDING)) {
++ if (!ohci->disabled) {
++ urb_priv_t * urb_priv;
++
++ /* interrupt code may not sleep; it must use
++ * async status return to unlink pending urbs.
++ */
++ if (!(urb->transfer_flags & USB_ASYNC_UNLINK)
++ && in_interrupt ()) {
++ spin_unlock_irqrestore(&ohci->ohci_lock, flags);
++ err ("bug in call from %p; use async!",
++ __builtin_return_address(0));
++ return -EWOULDBLOCK;
++ }
++
++ /* flag the urb and its TDs for deletion in some
++ * upcoming SF interrupt delete list processing
++ */
++ urb_priv = urb->hcpriv;
++
++ if (!urb_priv || (urb_priv->state == URB_DEL)) {
++ spin_unlock_irqrestore(&ohci->ohci_lock, flags);
++ return 0;
++ }
++
++ urb_priv->state = URB_DEL;
++ ep_rm_ed (urb->dev, urb_priv->ed);
++ urb_priv->ed->state |= ED_URB_DEL;
++
++ if (!(urb->transfer_flags & USB_ASYNC_UNLINK)) {
++ DECLARE_WAIT_QUEUE_HEAD (unlink_wakeup);
++ DECLARE_WAITQUEUE (wait, current);
++ int timeout = OHCI_UNLINK_TIMEOUT;
++
++ add_wait_queue (&unlink_wakeup, &wait);
++ urb_priv->wait = &unlink_wakeup;
++ spin_unlock_irqrestore(&ohci->ohci_lock, flags);
++
++ /* wait until all TDs are deleted */
++ set_current_state(TASK_UNINTERRUPTIBLE);
++ while (timeout && (urb->status == USB_ST_URB_PENDING)) {
++ timeout = schedule_timeout (timeout);
++ set_current_state(TASK_UNINTERRUPTIBLE);
++ }
++ set_current_state(TASK_RUNNING);
++ remove_wait_queue (&unlink_wakeup, &wait);
++ if (urb->status == USB_ST_URB_PENDING) {
++ err ("unlink URB timeout");
++ return -ETIMEDOUT;
++ }
++
++ usb_dec_dev_use (urb->dev);
++ urb->dev = NULL;
++ if (urb->complete)
++ urb->complete (urb);
++ } else {
++ /* usb_dec_dev_use done in dl_del_list() */
++ urb->status = -EINPROGRESS;
++ spin_unlock_irqrestore(&ohci->ohci_lock, flags);
++ return -EINPROGRESS;
++ }
++ } else {
++ urb_rm_priv_locked (urb);
++ spin_unlock_irqrestore(&ohci->ohci_lock, flags);
++ usb_dec_dev_use (urb->dev);
++ urb->dev = NULL;
++ if (urb->transfer_flags & USB_ASYNC_UNLINK) {
++ urb->status = -ECONNRESET;
++ if (urb->complete)
++ urb->complete (urb);
++ } else
++ urb->status = -ENOENT;
++ }
++ } else {
++ spin_unlock_irqrestore(&ohci->ohci_lock, flags);
++ }
++ return 0;
++}
++
++/*-------------------------------------------------------------------------*/
++
++/* allocate private data space for a usb device */
++
++static int sohci_alloc_dev (struct usb_device *usb_dev)
++{
++ struct ohci_device * dev;
++
++ dev = dev_alloc ((struct ohci *) usb_dev->bus->hcpriv, ALLOC_FLAGS);
++ if (!dev)
++ return -ENOMEM;
++
++ usb_dev->hcpriv = dev;
++ return 0;
++}
++
++/*-------------------------------------------------------------------------*/
++
++/* may be called from interrupt context */
++/* frees private data space of usb device */
++
++static int sohci_free_dev (struct usb_device * usb_dev)
++{
++ unsigned long flags;
++ int i, cnt = 0;
++ ed_t * ed;
++ struct ohci_device * dev = usb_to_ohci (usb_dev);
++ ohci_t * ohci = usb_dev->bus->hcpriv;
++
++ if (!dev)
++ return 0;
++
++ if (usb_dev->devnum >= 0) {
++
++ /* driver disconnects should have unlinked all urbs
++ * (freeing all the TDs, unlinking EDs) but we need
++ * to defend against bugs that prevent that.
++ */
++ spin_lock_irqsave(&ohci->ohci_lock, flags);
++ for(i = 0; i < NUM_EDS; i++) {
++ ed = &(dev->ed[i]);
++ if (ed->state != ED_NEW) {
++ if (ed->state == ED_OPER) {
++ /* driver on that interface didn't unlink an urb */
++ dbg ("driver usb-%s dev %d ed 0x%x unfreed URB",
++ ohci->ohci_dev->slot_name, usb_dev->devnum, i);
++ ep_unlink (ohci, ed);
++ }
++ ep_rm_ed (usb_dev, ed);
++ ed->state = ED_DEL;
++ cnt++;
++ }
++ }
++ spin_unlock_irqrestore(&ohci->ohci_lock, flags);
++
++ /* if the controller is running, tds for those unlinked
++ * urbs get freed by dl_del_list at the next SF interrupt
++ */
++ if (cnt > 0) {
++
++ if (ohci->disabled) {
++ /* FIXME: Something like this should kick in,
++ * though it's currently an exotic case ...
++ * the controller won't ever be touching
++ * these lists again!!
++ dl_del_list (ohci,
++ le16_to_cpu (ohci->hcca->frame_no) & 1);
++ */
++ warn ("TD leak, %d", cnt);
++
++ } else if (!in_interrupt ()) {
++ DECLARE_WAIT_QUEUE_HEAD (freedev_wakeup);
++ DECLARE_WAITQUEUE (wait, current);
++ int timeout = OHCI_UNLINK_TIMEOUT;
++
++ /* SF interrupt handler calls dl_del_list */
++ add_wait_queue (&freedev_wakeup, &wait);
++ dev->wait = &freedev_wakeup;
++ set_current_state(TASK_UNINTERRUPTIBLE);
++ while (timeout && dev->ed_cnt)
++ timeout = schedule_timeout (timeout);
++ set_current_state(TASK_RUNNING);
++ remove_wait_queue (&freedev_wakeup, &wait);
++ if (dev->ed_cnt) {
++ err ("free device %d timeout", usb_dev->devnum);
++ return -ETIMEDOUT;
++ }
++ } else {
++ /* likely some interface's driver has a refcount bug */
++ err ("bus %s devnum %d deletion in interrupt",
++ ohci->ohci_dev->slot_name, usb_dev->devnum);
++ BUG ();
++ }
++ }
++ }
++
++ /* free device, and associated EDs */
++ dev_free (ohci, dev);
++
++ return 0;
++}
++
++/*-------------------------------------------------------------------------*/
++
++/* tell us the current USB frame number */
++
++static int sohci_get_current_frame_number (struct usb_device *usb_dev)
++{
++ ohci_t * ohci = usb_dev->bus->hcpriv;
++
++ return le16_to_cpu (ohci->hcca->frame_no);
++}
++
++/*-------------------------------------------------------------------------*/
++
++struct usb_operations sohci_device_operations = {
++ sohci_alloc_dev,
++ sohci_free_dev,
++ sohci_get_current_frame_number,
++ sohci_submit_urb,
++ sohci_unlink_urb
++};
++
++/*-------------------------------------------------------------------------*
++ * ED handling functions
++ *-------------------------------------------------------------------------*/
++
++/* search for the right branch to insert an interrupt ed into the int tree
++ * do some load ballancing;
++ * returns the branch and
++ * sets the interval to interval = 2^integer (ld (interval)) */
++
++static int ep_int_ballance (ohci_t * ohci, int interval, int load)
++{
++ int i, branch = 0;
++
++ /* search for the least loaded interrupt endpoint branch of all 32 branches */
++ for (i = 0; i < 32; i++)
++ if (ohci->ohci_int_load [branch] > ohci->ohci_int_load [i]) branch = i;
++
++ branch = branch % interval;
++ for (i = branch; i < 32; i += interval) ohci->ohci_int_load [i] += load;
++
++ return branch;
++}
++
++/*-------------------------------------------------------------------------*/
++
++/* 2^int( ld (inter)) */
++
++static int ep_2_n_interval (int inter)
++{
++ int i;
++ for (i = 0; ((inter >> i) > 1 ) && (i < 5); i++);
++ return 1 << i;
++}
++
++/*-------------------------------------------------------------------------*/
++
++/* the int tree is a binary tree
++ * in order to process it sequentially the indexes of the branches have to be mapped
++ * the mapping reverses the bits of a word of num_bits length */
++
++static int ep_rev (int num_bits, int word)
++{
++ int i, wout = 0;
++
++ for (i = 0; i < num_bits; i++) wout |= (((word >> i) & 1) << (num_bits - i - 1));
++ return wout;
++}
++
++/*-------------------------------------------------------------------------*/
++
++/* link an ed into one of the HC chains */
++
++static int ep_link (ohci_t * ohci, ed_t * edi)
++{
++ int int_branch;
++ int i;
++ int inter;
++ int interval;
++ int load;
++ __u32 * ed_p;
++ volatile ed_t * ed = edi;
++
++ ed->state = ED_OPER;
++
++ switch (ed->type) {
++ case PIPE_CONTROL:
++ ed->hwNextED = 0;
++ if (ohci->ed_controltail == NULL) {
++ writel (ed->dma, &ohci->regs->ed_controlhead);
++ } else {
++ ohci->ed_controltail->hwNextED = cpu_to_le32 (ed->dma);
++ }
++ ed->ed_prev = ohci->ed_controltail;
++ if (!ohci->ed_controltail && !ohci->ed_rm_list[0] &&
++ !ohci->ed_rm_list[1] && !ohci->sleeping) {
++ ohci->hc_control |= OHCI_CTRL_CLE;
++ writel (ohci->hc_control, &ohci->regs->control);
++ }
++ ohci->ed_controltail = edi;
++ break;
++
++ case PIPE_BULK:
++ ed->hwNextED = 0;
++ if (ohci->ed_bulktail == NULL) {
++ writel (ed->dma, &ohci->regs->ed_bulkhead);
++ } else {
++ ohci->ed_bulktail->hwNextED = cpu_to_le32 (ed->dma);
++ }
++ ed->ed_prev = ohci->ed_bulktail;
++ if (!ohci->ed_bulktail && !ohci->ed_rm_list[0] &&
++ !ohci->ed_rm_list[1] && !ohci->sleeping) {
++ ohci->hc_control |= OHCI_CTRL_BLE;
++ writel (ohci->hc_control, &ohci->regs->control);
++ }
++ ohci->ed_bulktail = edi;
++ break;
++
++ case PIPE_INTERRUPT:
++ load = ed->int_load;
++ interval = ep_2_n_interval (ed->int_period);
++ ed->int_interval = interval;
++ int_branch = ep_int_ballance (ohci, interval, load);
++ ed->int_branch = int_branch;
++
++ for (i = 0; i < ep_rev (6, interval); i += inter) {
++ inter = 1;
++ for (ed_p = &(ohci->hcca->int_table[ep_rev (5, i) + int_branch]);
++ (*ed_p != 0) && ((dma_to_ed (ohci, le32_to_cpup (ed_p)))->int_interval >= interval);
++ ed_p = &((dma_to_ed (ohci, le32_to_cpup (ed_p)))->hwNextED))
++ inter = ep_rev (6, (dma_to_ed (ohci, le32_to_cpup (ed_p)))->int_interval);
++ ed->hwNextED = *ed_p;
++ *ed_p = cpu_to_le32 (ed->dma);
++ }
++#ifdef DEBUG
++ ep_print_int_eds (ohci, "LINK_INT");
++#endif
++ break;
++
++ case PIPE_ISOCHRONOUS:
++ ed->hwNextED = 0;
++ ed->int_interval = 1;
++ if (ohci->ed_isotail != NULL) {
++ ohci->ed_isotail->hwNextED = cpu_to_le32 (ed->dma);
++ ed->ed_prev = ohci->ed_isotail;
++ } else {
++ for ( i = 0; i < 32; i += inter) {
++ inter = 1;
++ for (ed_p = &(ohci->hcca->int_table[ep_rev (5, i)]);
++ *ed_p != 0;
++ ed_p = &((dma_to_ed (ohci, le32_to_cpup (ed_p)))->hwNextED))
++ inter = ep_rev (6, (dma_to_ed (ohci, le32_to_cpup (ed_p)))->int_interval);
++ *ed_p = cpu_to_le32 (ed->dma);
++ }
++ ed->ed_prev = NULL;
++ }
++ ohci->ed_isotail = edi;
++#ifdef DEBUG
++ ep_print_int_eds (ohci, "LINK_ISO");
++#endif
++ break;
++ }
++ return 0;
++}
++
++/*-------------------------------------------------------------------------*/
++
++/* scan the periodic table to find and unlink this ED */
++static void periodic_unlink (
++ struct ohci *ohci,
++ struct ed *ed,
++ unsigned index,
++ unsigned period
++) {
++ for (; index < NUM_INTS; index += period) {
++ __u32 *ed_p = &ohci->hcca->int_table [index];
++
++ /* ED might have been unlinked through another path */
++ while (*ed_p != 0) {
++ if ((dma_to_ed (ohci, le32_to_cpup (ed_p))) == ed) {
++ *ed_p = ed->hwNextED;
++ break;
++ }
++ ed_p = & ((dma_to_ed (ohci,
++ le32_to_cpup (ed_p)))->hwNextED);
++ }
++ }
++}
++
++/* unlink an ed from one of the HC chains.
++ * just the link to the ed is unlinked.
++ * the link from the ed still points to another operational ed or 0
++ * so the HC can eventually finish the processing of the unlinked ed */
++
++static int ep_unlink (ohci_t * ohci, ed_t * ed)
++{
++ int i;
++
++ ed->hwINFO |= cpu_to_le32 (OHCI_ED_SKIP);
++
++ switch (ed->type) {
++ case PIPE_CONTROL:
++ if (ed->ed_prev == NULL) {
++ if (!ed->hwNextED) {
++ ohci->hc_control &= ~OHCI_CTRL_CLE;
++ writel (ohci->hc_control, &ohci->regs->control);
++ }
++ writel (le32_to_cpup (&ed->hwNextED), &ohci->regs->ed_controlhead);
++ } else {
++ ed->ed_prev->hwNextED = ed->hwNextED;
++ }
++ if (ohci->ed_controltail == ed) {
++ ohci->ed_controltail = ed->ed_prev;
++ } else {
++ (dma_to_ed (ohci, le32_to_cpup (&ed->hwNextED)))->ed_prev = ed->ed_prev;
++ }
++ break;
++
++ case PIPE_BULK:
++ if (ed->ed_prev == NULL) {
++ if (!ed->hwNextED) {
++ ohci->hc_control &= ~OHCI_CTRL_BLE;
++ writel (ohci->hc_control, &ohci->regs->control);
++ }
++ writel (le32_to_cpup (&ed->hwNextED), &ohci->regs->ed_bulkhead);
++ } else {
++ ed->ed_prev->hwNextED = ed->hwNextED;
++ }
++ if (ohci->ed_bulktail == ed) {
++ ohci->ed_bulktail = ed->ed_prev;
++ } else {
++ (dma_to_ed (ohci, le32_to_cpup (&ed->hwNextED)))->ed_prev = ed->ed_prev;
++ }
++ break;
++
++ case PIPE_INTERRUPT:
++ periodic_unlink (ohci, ed, 0, 1);
++ for (i = ed->int_branch; i < 32; i += ed->int_interval)
++ ohci->ohci_int_load[i] -= ed->int_load;
++#ifdef DEBUG
++ ep_print_int_eds (ohci, "UNLINK_INT");
++#endif
++ break;
++
++ case PIPE_ISOCHRONOUS:
++ if (ohci->ed_isotail == ed)
++ ohci->ed_isotail = ed->ed_prev;
++ if (ed->hwNextED != 0)
++ (dma_to_ed (ohci, le32_to_cpup (&ed->hwNextED)))
++ ->ed_prev = ed->ed_prev;
++
++ if (ed->ed_prev != NULL)
++ ed->ed_prev->hwNextED = ed->hwNextED;
++ else
++ periodic_unlink (ohci, ed, 0, 1);
++#ifdef DEBUG
++ ep_print_int_eds (ohci, "UNLINK_ISO");
++#endif
++ break;
++ }
++ ed->state = ED_UNLINK;
++ return 0;
++}
++
++
++/*-------------------------------------------------------------------------*/
++
++/* add/reinit an endpoint; this should be done once at the usb_set_configuration command,
++ * but the USB stack is a little bit stateless so we do it at every transaction
++ * if the state of the ed is ED_NEW then a dummy td is added and the state is changed to ED_UNLINK
++ * in all other cases the state is left unchanged
++ * the ed info fields are setted anyway even though most of them should not change */
++
++static ed_t * ep_add_ed (
++ struct usb_device * usb_dev,
++ unsigned int pipe,
++ int interval,
++ int load,
++ int mem_flags
++)
++{
++ ohci_t * ohci = usb_dev->bus->hcpriv;
++ td_t * td;
++ ed_t * ed_ret;
++ volatile ed_t * ed;
++
++ ed = ed_ret = &(usb_to_ohci (usb_dev)->ed[(usb_pipeendpoint (pipe) << 1) |
++ (usb_pipecontrol (pipe)? 0: usb_pipeout (pipe))]);
++
++ if ((ed->state & ED_DEL) || (ed->state & ED_URB_DEL)) {
++ /* pending delete request */
++ return NULL;
++ }
++
++ if (ed->state == ED_NEW) {
++ ed->hwINFO = cpu_to_le32 (OHCI_ED_SKIP); /* skip ed */
++ /* dummy td; end of td list for ed */
++ td = td_alloc (ohci, SLAB_ATOMIC);
++ /* hash the ed for later reverse mapping */
++ if (!td || !hash_add_ed (ohci, (ed_t *)ed)) {
++ /* out of memory */
++ if (td)
++ td_free(ohci, td);
++ return NULL;
++ }
++ ed->hwTailP = cpu_to_le32 (td->td_dma);
++ ed->hwHeadP = ed->hwTailP;
++ ed->state = ED_UNLINK;
++ ed->type = usb_pipetype (pipe);
++ usb_to_ohci (usb_dev)->ed_cnt++;
++ }
++
++ ohci->dev[usb_pipedevice (pipe)] = usb_dev;
++
++ ed->hwINFO = cpu_to_le32 (usb_pipedevice (pipe)
++ | usb_pipeendpoint (pipe) << 7
++ | (usb_pipeisoc (pipe)? 0x8000: 0)
++ | (usb_pipecontrol (pipe)? 0: (usb_pipeout (pipe)? 0x800: 0x1000))
++ | usb_pipeslow (pipe) << 13
++ | usb_maxpacket (usb_dev, pipe, usb_pipeout (pipe)) << 16);
++
++ if (ed->type == PIPE_INTERRUPT && ed->state == ED_UNLINK) {
++ ed->int_period = interval;
++ ed->int_load = load;
++ }
++
++ return ed_ret;
++}
++
++/*-------------------------------------------------------------------------*/
++
++/* request the removal of an endpoint
++ * put the ep on the rm_list and request a stop of the bulk or ctrl list
++ * real removal is done at the next start frame (SF) hardware interrupt */
++
++static void ep_rm_ed (struct usb_device * usb_dev, ed_t * ed)
++{
++ unsigned int frame;
++ ohci_t * ohci = usb_dev->bus->hcpriv;
++
++ if ((ed->state & ED_DEL) || (ed->state & ED_URB_DEL))
++ return;
++
++ ed->hwINFO |= cpu_to_le32 (OHCI_ED_SKIP);
++
++ if (!ohci->disabled) {
++ switch (ed->type) {
++ case PIPE_CONTROL: /* stop control list */
++ ohci->hc_control &= ~OHCI_CTRL_CLE;
++ writel (ohci->hc_control, &ohci->regs->control);
++ break;
++ case PIPE_BULK: /* stop bulk list */
++ ohci->hc_control &= ~OHCI_CTRL_BLE;
++ writel (ohci->hc_control, &ohci->regs->control);
++ break;
++ }
++ }
++
++ frame = le16_to_cpu (ohci->hcca->frame_no) & 0x1;
++ ed->ed_rm_list = ohci->ed_rm_list[frame];
++ ohci->ed_rm_list[frame] = ed;
++
++ if (!ohci->disabled && !ohci->sleeping) {
++ /* enable SOF interrupt */
++ writel (OHCI_INTR_SF, &ohci->regs->intrstatus);
++ writel (OHCI_INTR_SF, &ohci->regs->intrenable);
++ (void)readl (&ohci->regs->intrdisable); /* PCI posting flush */
++ }
++}
++
++/*-------------------------------------------------------------------------*
++ * TD handling functions
++ *-------------------------------------------------------------------------*/
++
++/* enqueue next TD for this URB (OHCI spec 5.2.8.2) */
++
++static void
++td_fill (ohci_t * ohci, unsigned int info,
++ dma_addr_t data, int len,
++ struct urb * urb, int index)
++{
++ volatile td_t * td, * td_pt;
++ urb_priv_t * urb_priv = urb->hcpriv;
++
++ if (index >= urb_priv->length) {
++ err("internal OHCI error: TD index > length");
++ return;
++ }
++
++ /* use this td as the next dummy */
++ td_pt = urb_priv->td [index];
++ td_pt->hwNextTD = 0;
++
++ /* fill the old dummy TD */
++ td = urb_priv->td [index] = dma_to_td (ohci,
++ le32_to_cpup (&urb_priv->ed->hwTailP) & ~0xf);
++
++ td->ed = urb_priv->ed;
++ td->next_dl_td = NULL;
++ td->index = index;
++ td->urb = urb;
++ td->data_dma = data;
++ if (!len)
++ data = 0;
++
++ td->hwINFO = cpu_to_le32 (info);
++ if ((td->ed->type) == PIPE_ISOCHRONOUS) {
++ td->hwCBP = cpu_to_le32 (data & 0xFFFFF000);
++ td->ed->last_iso = info & 0xffff;
++ } else {
++ td->hwCBP = cpu_to_le32 (data);
++ }
++ if (data)
++ td->hwBE = cpu_to_le32 (data + len - 1);
++ else
++ td->hwBE = 0;
++ td->hwNextTD = cpu_to_le32 (td_pt->td_dma);
++ td->hwPSW [0] = cpu_to_le16 ((data & 0x0FFF) | 0xE000);
++
++ /* append to queue */
++ wmb();
++ td->ed->hwTailP = td->hwNextTD;
++}
++
++/*-------------------------------------------------------------------------*/
++
++/* prepare all TDs of a transfer */
++
++static void td_submit_urb (struct urb * urb)
++{
++ urb_priv_t * urb_priv = urb->hcpriv;
++ ohci_t * ohci = (ohci_t *) urb->dev->bus->hcpriv;
++ dma_addr_t data;
++ int data_len = urb->transfer_buffer_length;
++ int maxps = usb_maxpacket (urb->dev, urb->pipe, usb_pipeout (urb->pipe));
++ int cnt = 0;
++ __u32 info = 0;
++ unsigned int toggle = 0;
++
++ /* OHCI handles the DATA-toggles itself, we just use the USB-toggle bits for reseting */
++ if(usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe))) {
++ toggle = TD_T_TOGGLE;
++ } else {
++ toggle = TD_T_DATA0;
++ usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe), 1);
++ }
++
++ urb_priv->td_cnt = 0;
++
++ if (data_len) {
++ data = pci_map_single (ohci->ohci_dev,
++ urb->transfer_buffer, data_len,
++ usb_pipeout (urb->pipe)
++ ? PCI_DMA_TODEVICE
++ : PCI_DMA_FROMDEVICE
++ );
++ } else
++ data = 0;
++
++ switch (usb_pipetype (urb->pipe)) {
++ case PIPE_BULK:
++ info = usb_pipeout (urb->pipe)?
++ TD_CC | TD_DP_OUT : TD_CC | TD_DP_IN ;
++ while(data_len > 4096) {
++ td_fill (ohci, info | (cnt? TD_T_TOGGLE:toggle), data, 4096, urb, cnt);
++ data += 4096; data_len -= 4096; cnt++;
++ }
++ info = usb_pipeout (urb->pipe)?
++ TD_CC | TD_DP_OUT : TD_CC | TD_R | TD_DP_IN ;
++ td_fill (ohci, info | (cnt? TD_T_TOGGLE:toggle), data, data_len, urb, cnt);
++ cnt++;
++
++ /* If the transfer size is multiple of the pipe mtu,
++ * we may need an extra TD to create a empty frame
++ * Note : another way to check this condition is
++ * to test if(urb_priv->length > cnt) - Jean II */
++ if ((urb->transfer_flags & USB_ZERO_PACKET) &&
++ usb_pipeout (urb->pipe) &&
++ (urb->transfer_buffer_length != 0) &&
++ ((urb->transfer_buffer_length % maxps) == 0)) {
++ td_fill (ohci, info | (cnt? TD_T_TOGGLE:toggle), 0, 0, urb, cnt);
++ cnt++;
++ }
++
++ if (!ohci->sleeping) {
++ wmb();
++ writel (OHCI_BLF, &ohci->regs->cmdstatus); /* start bulk list */
++ (void)readl (&ohci->regs->intrdisable); /* PCI posting flush */
++ }
++ break;
++
++ case PIPE_INTERRUPT:
++ info = usb_pipeout (urb->pipe)?
++ TD_CC | TD_DP_OUT | toggle: TD_CC | TD_R | TD_DP_IN | toggle;
++ td_fill (ohci, info, data, data_len, urb, cnt++);
++ break;
++
++ case PIPE_CONTROL:
++ info = TD_CC | TD_DP_SETUP | TD_T_DATA0;
++ td_fill (ohci, info,
++ pci_map_single (ohci->ohci_dev,
++ urb->setup_packet, 8,
++ PCI_DMA_TODEVICE),
++ 8, urb, cnt++);
++ if (data_len > 0) {
++ info = usb_pipeout (urb->pipe)?
++ TD_CC | TD_R | TD_DP_OUT | TD_T_DATA1 : TD_CC | TD_R | TD_DP_IN | TD_T_DATA1;
++ /* NOTE: mishandles transfers >8K, some >4K */
++ td_fill (ohci, info, data, data_len, urb, cnt++);
++ }
++ info = usb_pipeout (urb->pipe)?
++ TD_CC | TD_DP_IN | TD_T_DATA1: TD_CC | TD_DP_OUT | TD_T_DATA1;
++ td_fill (ohci, info, data, 0, urb, cnt++);
++ if (!ohci->sleeping) {
++ wmb();
++ writel (OHCI_CLF, &ohci->regs->cmdstatus); /* start Control list */
++ (void)readl (&ohci->regs->intrdisable); /* PCI posting flush */
++ }
++ break;
++
++ case PIPE_ISOCHRONOUS:
++ for (cnt = 0; cnt < urb->number_of_packets; cnt++) {
++ td_fill (ohci, TD_CC|TD_ISO | ((urb->start_frame + cnt) & 0xffff),
++ data + urb->iso_frame_desc[cnt].offset,
++ urb->iso_frame_desc[cnt].length, urb, cnt);
++ }
++ break;
++ }
++ if (urb_priv->length != cnt)
++ dbg("TD LENGTH %d != CNT %d", urb_priv->length, cnt);
++}
++
++/*-------------------------------------------------------------------------*
++ * Done List handling functions
++ *-------------------------------------------------------------------------*/
++
++
++/* calculate the transfer length and update the urb */
++
++static void dl_transfer_length(td_t * td)
++{
++ __u32 tdINFO, tdBE, tdCBP;
++ __u16 tdPSW;
++ struct urb * urb = td->urb;
++ urb_priv_t * urb_priv = urb->hcpriv;
++ int dlen = 0;
++ int cc = 0;
++
++ tdINFO = le32_to_cpup (&td->hwINFO);
++ tdBE = le32_to_cpup (&td->hwBE);
++ tdCBP = le32_to_cpup (&td->hwCBP);
++
++
++ if (tdINFO & TD_ISO) {
++ tdPSW = le16_to_cpu (td->hwPSW[0]);
++ cc = (tdPSW >> 12) & 0xF;
++ if (cc < 0xE) {
++ if (usb_pipeout(urb->pipe)) {
++ dlen = urb->iso_frame_desc[td->index].length;
++ } else {
++ dlen = tdPSW & 0x3ff;
++ }
++ urb->actual_length += dlen;
++ urb->iso_frame_desc[td->index].actual_length = dlen;
++ if (!(urb->transfer_flags & USB_DISABLE_SPD) && (cc == TD_DATAUNDERRUN))
++ cc = TD_CC_NOERROR;
++
++ urb->iso_frame_desc[td->index].status = cc_to_error[cc];
++ }
++ } else { /* BULK, INT, CONTROL DATA */
++ if (!(usb_pipetype (urb->pipe) == PIPE_CONTROL &&
++ ((td->index == 0) || (td->index == urb_priv->length - 1)))) {
++ if (tdBE != 0) {
++ if (td->hwCBP == 0)
++ urb->actual_length += tdBE - td->data_dma + 1;
++ else
++ urb->actual_length += tdCBP - td->data_dma;
++ }
++ }
++ }
++}
++
++/* handle an urb that is being unlinked */
++
++static void dl_del_urb (ohci_t *ohci, struct urb * urb)
++{
++ wait_queue_head_t * wait_head = ((urb_priv_t *)(urb->hcpriv))->wait;
++
++ urb_rm_priv_locked (urb);
++
++ if (urb->transfer_flags & USB_ASYNC_UNLINK) {
++ urb->status = -ECONNRESET;
++ ohci_complete_add(ohci, urb);
++ } else {
++ urb->status = -ENOENT;
++
++ /* unblock sohci_unlink_urb */
++ if (wait_head)
++ wake_up (wait_head);
++ }
++}
++
++/*-------------------------------------------------------------------------*/
++
++/* replies to the request have to be on a FIFO basis so
++ * we reverse the reversed done-list */
++
++static td_t * dl_reverse_done_list (ohci_t * ohci)
++{
++ __u32 td_list_hc;
++ td_t * td_rev = NULL;
++ td_t * td_list = NULL;
++ urb_priv_t * urb_priv = NULL;
++
++ td_list_hc = le32_to_cpup (&ohci->hcca->done_head) & 0xfffffff0;
++ ohci->hcca->done_head = 0;
++
++ while (td_list_hc) {
++ td_list = dma_to_td (ohci, td_list_hc);
++
++ if (TD_CC_GET (le32_to_cpup (&td_list->hwINFO))) {
++ urb_priv = (urb_priv_t *) td_list->urb->hcpriv;
++ dbg(" USB-error/status: %x : %p",
++ TD_CC_GET (le32_to_cpup (&td_list->hwINFO)), td_list);
++ if (td_list->ed->hwHeadP & cpu_to_le32 (0x1)) {
++ if (urb_priv && ((td_list->index + 1) < urb_priv->length)) {
++ td_list->ed->hwHeadP =
++ (urb_priv->td[urb_priv->length - 1]->hwNextTD & cpu_to_le32 (0xfffffff0)) |
++ (td_list->ed->hwHeadP & cpu_to_le32 (0x2));
++ urb_priv->td_cnt += urb_priv->length - td_list->index - 1;
++ } else
++ td_list->ed->hwHeadP &= cpu_to_le32 (0xfffffff2);
++ }
++ }
++
++ td_list->next_dl_td = td_rev;
++ td_rev = td_list;
++ td_list_hc = le32_to_cpup (&td_list->hwNextTD) & 0xfffffff0;
++ }
++ return td_list;
++}
++
++/*-------------------------------------------------------------------------*/
++
++/* there are some pending requests to remove
++ * - some of the eds (if ed->state & ED_DEL (set by sohci_free_dev)
++ * - some URBs/TDs if urb_priv->state == URB_DEL */
++
++static void dl_del_list (ohci_t * ohci, unsigned int frame)
++{
++ ed_t * ed;
++ __u32 edINFO;
++ __u32 tdINFO;
++ td_t * td = NULL, * td_next = NULL, * tdHeadP = NULL, * tdTailP;
++ __u32 * td_p;
++ int ctrl = 0, bulk = 0;
++
++ for (ed = ohci->ed_rm_list[frame]; ed != NULL; ed = ed->ed_rm_list) {
++
++ tdTailP = dma_to_td (ohci, le32_to_cpup (&ed->hwTailP) & 0xfffffff0);
++ tdHeadP = dma_to_td (ohci, le32_to_cpup (&ed->hwHeadP) & 0xfffffff0);
++ edINFO = le32_to_cpup (&ed->hwINFO);
++ td_p = &ed->hwHeadP;
++
++ for (td = tdHeadP; td != tdTailP; td = td_next) {
++ struct urb * urb = td->urb;
++ urb_priv_t * urb_priv = td->urb->hcpriv;
++
++ td_next = dma_to_td (ohci, le32_to_cpup (&td->hwNextTD) & 0xfffffff0);
++ if ((urb_priv->state == URB_DEL) || (ed->state & ED_DEL)) {
++ tdINFO = le32_to_cpup (&td->hwINFO);
++ if (TD_CC_GET (tdINFO) < 0xE)
++ dl_transfer_length (td);
++ *td_p = td->hwNextTD | (*td_p & cpu_to_le32 (0x3));
++
++ /* URB is done; clean up */
++ if (++(urb_priv->td_cnt) == urb_priv->length)
++ dl_del_urb (ohci, urb);
++ } else {
++ td_p = &td->hwNextTD;
++ }
++ }
++
++ if (ed->state & ED_DEL) { /* set by sohci_free_dev */
++ struct ohci_device * dev = usb_to_ohci (ohci->dev[edINFO & 0x7F]);
++ td_free (ohci, tdTailP); /* free dummy td */
++ ed->hwINFO = cpu_to_le32 (OHCI_ED_SKIP);
++ ed->state = ED_NEW;
++ hash_free_ed(ohci, ed);
++ /* if all eds are removed wake up sohci_free_dev */
++ if (!--dev->ed_cnt) {
++ wait_queue_head_t *wait_head = dev->wait;
++
++ dev->wait = 0;
++ if (wait_head)
++ wake_up (wait_head);
++ }
++ } else {
++ ed->state &= ~ED_URB_DEL;
++ tdHeadP = dma_to_td (ohci, le32_to_cpup (&ed->hwHeadP) & 0xfffffff0);
++
++ if (tdHeadP == tdTailP) {
++ if (ed->state == ED_OPER)
++ ep_unlink(ohci, ed);
++ } else
++ ed->hwINFO &= ~cpu_to_le32 (OHCI_ED_SKIP);
++ }
++
++ switch (ed->type) {
++ case PIPE_CONTROL:
++ ctrl = 1;
++ break;
++ case PIPE_BULK:
++ bulk = 1;
++ break;
++ }
++ }
++
++ /* maybe reenable control and bulk lists */
++ if (!ohci->disabled) {
++ if (ctrl) /* reset control list */
++ writel (0, &ohci->regs->ed_controlcurrent);
++ if (bulk) /* reset bulk list */
++ writel (0, &ohci->regs->ed_bulkcurrent);
++ if (!ohci->ed_rm_list[!frame] && !ohci->sleeping) {
++ if (ohci->ed_controltail)
++ ohci->hc_control |= OHCI_CTRL_CLE;
++ if (ohci->ed_bulktail)
++ ohci->hc_control |= OHCI_CTRL_BLE;
++ writel (ohci->hc_control, &ohci->regs->control);
++ }
++ }
++
++ ohci->ed_rm_list[frame] = NULL;
++}
++
++
++
++/*-------------------------------------------------------------------------*/
++
++/* td done list */
++
++static void dl_done_list (ohci_t * ohci, td_t * td_list)
++{
++ td_t * td_list_next = NULL;
++ ed_t * ed;
++ int cc = 0;
++ struct urb * urb;
++ urb_priv_t * urb_priv;
++ __u32 tdINFO, edHeadP, edTailP;
++
++ while (td_list) {
++ td_list_next = td_list->next_dl_td;
++
++ urb = td_list->urb;
++ urb_priv = urb->hcpriv;
++ tdINFO = le32_to_cpup (&td_list->hwINFO);
++
++ ed = td_list->ed;
++
++ dl_transfer_length(td_list);
++
++ /* error code of transfer */
++ cc = TD_CC_GET (tdINFO);
++ if (cc == TD_CC_STALL)
++ usb_endpoint_halt(urb->dev,
++ usb_pipeendpoint(urb->pipe),
++ usb_pipeout(urb->pipe));
++
++ if (!(urb->transfer_flags & USB_DISABLE_SPD)
++ && (cc == TD_DATAUNDERRUN))
++ cc = TD_CC_NOERROR;
++
++ if (++(urb_priv->td_cnt) == urb_priv->length) {
++ if ((ed->state & (ED_OPER | ED_UNLINK))
++ && (urb_priv->state != URB_DEL)) {
++ urb->status = cc_to_error[cc];
++ sohci_return_urb (ohci, urb);
++ } else {
++ dl_del_urb (ohci, urb);
++ }
++ }
++
++ if (ed->state != ED_NEW) {
++ edHeadP = le32_to_cpup (&ed->hwHeadP) & 0xfffffff0;
++ edTailP = le32_to_cpup (&ed->hwTailP);
++
++ /* unlink eds if they are not busy */
++ if ((edHeadP == edTailP) && (ed->state == ED_OPER))
++ ep_unlink (ohci, ed);
++ }
++
++ td_list = td_list_next;
++ }
++}
++
++
++
++
++/*-------------------------------------------------------------------------*
++ * Virtual Root Hub
++ *-------------------------------------------------------------------------*/
++
++/* Device descriptor */
++static __u8 root_hub_dev_des[] =
++{
++ 0x12, /* __u8 bLength; */
++ 0x01, /* __u8 bDescriptorType; Device */
++ 0x10, /* __u16 bcdUSB; v1.1 */
++ 0x01,
++ 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */
++ 0x00, /* __u8 bDeviceSubClass; */
++ 0x00, /* __u8 bDeviceProtocol; */
++ 0x08, /* __u8 bMaxPacketSize0; 8 Bytes */
++ 0x00, /* __u16 idVendor; */
++ 0x00,
++ 0x00, /* __u16 idProduct; */
++ 0x00,
++ 0x00, /* __u16 bcdDevice; */
++ 0x00,
++ 0x00, /* __u8 iManufacturer; */
++ 0x02, /* __u8 iProduct; */
++ 0x01, /* __u8 iSerialNumber; */
++ 0x01 /* __u8 bNumConfigurations; */
++};
++
++
++/* Configuration descriptor */
++static __u8 root_hub_config_des[] =
++{
++ 0x09, /* __u8 bLength; */
++ 0x02, /* __u8 bDescriptorType; Configuration */
++ 0x19, /* __u16 wTotalLength; */
++ 0x00,
++ 0x01, /* __u8 bNumInterfaces; */
++ 0x01, /* __u8 bConfigurationValue; */
++ 0x00, /* __u8 iConfiguration; */
++ 0x40, /* __u8 bmAttributes;
++ Bit 7: Bus-powered, 6: Self-powered, 5 Remote-wakwup, 4..0: resvd */
++ 0x00, /* __u8 MaxPower; */
++
++ /* interface */
++ 0x09, /* __u8 if_bLength; */
++ 0x04, /* __u8 if_bDescriptorType; Interface */
++ 0x00, /* __u8 if_bInterfaceNumber; */
++ 0x00, /* __u8 if_bAlternateSetting; */
++ 0x01, /* __u8 if_bNumEndpoints; */
++ 0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */
++ 0x00, /* __u8 if_bInterfaceSubClass; */
++ 0x00, /* __u8 if_bInterfaceProtocol; */
++ 0x00, /* __u8 if_iInterface; */
++
++ /* endpoint */
++ 0x07, /* __u8 ep_bLength; */
++ 0x05, /* __u8 ep_bDescriptorType; Endpoint */
++ 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */
++ 0x03, /* __u8 ep_bmAttributes; Interrupt */
++ 0x02, /* __u16 ep_wMaxPacketSize; ((MAX_ROOT_PORTS + 1) / 8 */
++ 0x00,
++ 0xff /* __u8 ep_bInterval; 255 ms */
++};
++
++/* Hub class-specific descriptor is constructed dynamically */
++
++
++/*-------------------------------------------------------------------------*/
++
++/* prepare Interrupt pipe data; HUB INTERRUPT ENDPOINT */
++
++static int rh_send_irq (ohci_t * ohci, void * rh_data, int rh_len)
++{
++ int num_ports;
++ int i;
++ int ret;
++ int len;
++
++ __u8 data[8];
++
++ num_ports = roothub_a (ohci) & RH_A_NDP;
++ if (num_ports > MAX_ROOT_PORTS) {
++ err ("bogus NDP=%d for OHCI usb-%s", num_ports,
++ ohci->ohci_dev->slot_name);
++ err ("rereads as NDP=%d",
++ readl (&ohci->regs->roothub.a) & RH_A_NDP);
++ /* retry later; "should not happen" */
++ return 0;
++ }
++ *(__u8 *) data = (roothub_status (ohci) & (RH_HS_LPSC | RH_HS_OCIC))
++ ? 1: 0;
++ ret = *(__u8 *) data;
++
++ for ( i = 0; i < num_ports; i++) {
++ *(__u8 *) (data + (i + 1) / 8) |=
++ ((roothub_portstatus (ohci, i) &
++ (RH_PS_CSC | RH_PS_PESC | RH_PS_PSSC | RH_PS_OCIC | RH_PS_PRSC))
++ ? 1: 0) << ((i + 1) % 8);
++ ret += *(__u8 *) (data + (i + 1) / 8);
++ }
++ len = i/8 + 1;
++
++ if (ret > 0) {
++ memcpy(rh_data, data,
++ min_t(unsigned int, len,
++ min_t(unsigned int, rh_len, sizeof(data))));
++ return len;
++ }
++ return 0;
++}
++
++/*-------------------------------------------------------------------------*/
++
++/* Virtual Root Hub INTs are polled by this timer every "interval" ms */
++
++static void rh_int_timer_do (unsigned long ptr)
++{
++ int len;
++
++ struct urb * urb = (struct urb *) ptr;
++ ohci_t * ohci = urb->dev->bus->hcpriv;
++
++ if (ohci->disabled)
++ return;
++
++ /* ignore timers firing during PM suspend, etc */
++ if ((ohci->hc_control & OHCI_CTRL_HCFS) != OHCI_USB_OPER)
++ goto out;
++
++ if(ohci->rh.send) {
++ len = rh_send_irq (ohci, urb->transfer_buffer, urb->transfer_buffer_length);
++ if (len > 0) {
++ urb->actual_length = len;
++#ifdef DEBUG
++ urb_print (urb, "RET-t(rh)", usb_pipeout (urb->pipe));
++#endif
++ if (urb->complete)
++ urb->complete (urb);
++ }
++ }
++ out:
++ rh_init_int_timer (urb);
++}
++
++/*-------------------------------------------------------------------------*/
++
++/* Root Hub INTs are polled by this timer */
++
++static int rh_init_int_timer (struct urb * urb)
++{
++ ohci_t * ohci = urb->dev->bus->hcpriv;
++
++ ohci->rh.interval = urb->interval;
++ init_timer (&ohci->rh.rh_int_timer);
++ ohci->rh.rh_int_timer.function = rh_int_timer_do;
++ ohci->rh.rh_int_timer.data = (unsigned long) urb;
++ ohci->rh.rh_int_timer.expires =
++ jiffies + (HZ * (urb->interval < 30? 30: urb->interval)) / 1000;
++ add_timer (&ohci->rh.rh_int_timer);
++
++ return 0;
++}
++
++/*-------------------------------------------------------------------------*/
++
++#define OK(x) len = (x); break
++#define WR_RH_STAT(x) writel((x), &ohci->regs->roothub.status)
++#define WR_RH_PORTSTAT(x) writel((x), &ohci->regs->roothub.portstatus[wIndex-1])
++#define RD_RH_STAT roothub_status(ohci)
++#define RD_RH_PORTSTAT roothub_portstatus(ohci,wIndex-1)
++
++/* request to virtual root hub */
++
++static int rh_submit_urb (struct urb * urb)
++{
++ struct usb_device * usb_dev = urb->dev;
++ ohci_t * ohci = usb_dev->bus->hcpriv;
++ unsigned int pipe = urb->pipe;
++ struct usb_ctrlrequest * cmd = (struct usb_ctrlrequest *) urb->setup_packet;
++ void * data = urb->transfer_buffer;
++ int leni = urb->transfer_buffer_length;
++ int len = 0;
++ int status = TD_CC_NOERROR;
++ unsigned long flags;
++
++ __u32 datab[4];
++ __u8 * data_buf = (__u8 *) datab;
++
++ __u16 bmRType_bReq;
++ __u16 wValue;
++ __u16 wIndex;
++ __u16 wLength;
++
++ spin_lock_irqsave(&ohci->ohci_lock, flags);
++
++ if (usb_pipeint(pipe)) {
++ ohci->rh.urb = urb;
++ ohci->rh.send = 1;
++ ohci->rh.interval = urb->interval;
++ rh_init_int_timer(urb);
++ urb->status = cc_to_error [TD_CC_NOERROR];
++
++ spin_unlock_irqrestore(&ohci->ohci_lock, flags);
++ return 0;
++ }
++
++ bmRType_bReq = cmd->bRequestType | (cmd->bRequest << 8);
++ wValue = le16_to_cpu (cmd->wValue);
++ wIndex = le16_to_cpu (cmd->wIndex);
++ wLength = le16_to_cpu (cmd->wLength);
++
++ switch (bmRType_bReq) {
++ /* Request Destination:
++ without flags: Device,
++ RH_INTERFACE: interface,
++ RH_ENDPOINT: endpoint,
++ RH_CLASS means HUB here,
++ RH_OTHER | RH_CLASS almost ever means HUB_PORT here
++ */
++
++ case RH_GET_STATUS:
++ *(__u16 *) data_buf = cpu_to_le16 (1); OK (2);
++ case RH_GET_STATUS | RH_INTERFACE:
++ *(__u16 *) data_buf = cpu_to_le16 (0); OK (2);
++ case RH_GET_STATUS | RH_ENDPOINT:
++ *(__u16 *) data_buf = cpu_to_le16 (0); OK (2);
++ case RH_GET_STATUS | RH_CLASS:
++ *(__u32 *) data_buf = cpu_to_le32 (
++ RD_RH_STAT & ~(RH_HS_CRWE | RH_HS_DRWE));
++ OK (4);
++ case RH_GET_STATUS | RH_OTHER | RH_CLASS:
++ *(__u32 *) data_buf = cpu_to_le32 (RD_RH_PORTSTAT); OK (4);
++
++ case RH_CLEAR_FEATURE | RH_ENDPOINT:
++ switch (wValue) {
++ case (RH_ENDPOINT_STALL): OK (0);
++ }
++ break;
++
++ case RH_CLEAR_FEATURE | RH_CLASS:
++ switch (wValue) {
++ case RH_C_HUB_LOCAL_POWER:
++ OK(0);
++ case (RH_C_HUB_OVER_CURRENT):
++ WR_RH_STAT(RH_HS_OCIC); OK (0);
++ }
++ break;
++
++ case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS:
++ switch (wValue) {
++ case (RH_PORT_ENABLE):
++ WR_RH_PORTSTAT (RH_PS_CCS ); OK (0);
++ case (RH_PORT_SUSPEND):
++ WR_RH_PORTSTAT (RH_PS_POCI); OK (0);
++ case (RH_PORT_POWER):
++ WR_RH_PORTSTAT (RH_PS_LSDA); OK (0);
++ case (RH_C_PORT_CONNECTION):
++ WR_RH_PORTSTAT (RH_PS_CSC ); OK (0);
++ case (RH_C_PORT_ENABLE):
++ WR_RH_PORTSTAT (RH_PS_PESC); OK (0);
++ case (RH_C_PORT_SUSPEND):
++ WR_RH_PORTSTAT (RH_PS_PSSC); OK (0);
++ case (RH_C_PORT_OVER_CURRENT):
++ WR_RH_PORTSTAT (RH_PS_OCIC); OK (0);
++ case (RH_C_PORT_RESET):
++ WR_RH_PORTSTAT (RH_PS_PRSC); OK (0);
++ }
++ break;
++
++ case RH_SET_FEATURE | RH_OTHER | RH_CLASS:
++ switch (wValue) {
++ case (RH_PORT_SUSPEND):
++ WR_RH_PORTSTAT (RH_PS_PSS ); OK (0);
++ case (RH_PORT_RESET): /* BUG IN HUP CODE *********/
++ if (RD_RH_PORTSTAT & RH_PS_CCS)
++ WR_RH_PORTSTAT (RH_PS_PRS);
++ OK (0);
++ case (RH_PORT_POWER):
++ WR_RH_PORTSTAT (RH_PS_PPS ); OK (0);
++ case (RH_PORT_ENABLE): /* BUG IN HUP CODE *********/
++ if (RD_RH_PORTSTAT & RH_PS_CCS)
++ WR_RH_PORTSTAT (RH_PS_PES );
++ OK (0);
++ }
++ break;
++
++ case RH_SET_ADDRESS: ohci->rh.devnum = wValue; OK(0);
++
++ case RH_GET_DESCRIPTOR:
++ switch ((wValue & 0xff00) >> 8) {
++ case (0x01): /* device descriptor */
++ len = min_t(unsigned int,
++ leni,
++ min_t(unsigned int,
++ sizeof (root_hub_dev_des),
++ wLength));
++ data_buf = root_hub_dev_des; OK(len);
++ case (0x02): /* configuration descriptor */
++ len = min_t(unsigned int,
++ leni,
++ min_t(unsigned int,
++ sizeof (root_hub_config_des),
++ wLength));
++ data_buf = root_hub_config_des; OK(len);
++ case (0x03): /* string descriptors */
++ len = usb_root_hub_string (wValue & 0xff,
++ (int)(long) ohci->regs, "OHCI",
++ data, wLength);
++ if (len > 0) {
++ data_buf = data;
++ OK(min_t(int, leni, len));
++ }
++ // else fallthrough
++ default:
++ status = TD_CC_STALL;
++ }
++ break;
++
++ case RH_GET_DESCRIPTOR | RH_CLASS:
++ {
++ __u32 temp = roothub_a (ohci);
++
++ data_buf [0] = 9; // min length;
++ data_buf [1] = 0x29;
++ data_buf [2] = temp & RH_A_NDP;
++ data_buf [3] = 0;
++ if (temp & RH_A_PSM) /* per-port power switching? */
++ data_buf [3] |= 0x1;
++ if (temp & RH_A_NOCP) /* no overcurrent reporting? */
++ data_buf [3] |= 0x10;
++ else if (temp & RH_A_OCPM) /* per-port overcurrent reporting? */
++ data_buf [3] |= 0x8;
++
++ datab [1] = 0;
++ data_buf [5] = (temp & RH_A_POTPGT) >> 24;
++ temp = roothub_b (ohci);
++ data_buf [7] = temp & RH_B_DR;
++ if (data_buf [2] < 7) {
++ data_buf [8] = 0xff;
++ } else {
++ data_buf [0] += 2;
++ data_buf [8] = (temp & RH_B_DR) >> 8;
++ data_buf [10] = data_buf [9] = 0xff;
++ }
++
++ len = min_t(unsigned int, leni,
++ min_t(unsigned int, data_buf [0], wLength));
++ OK (len);
++ }
++
++ case RH_GET_CONFIGURATION: *(__u8 *) data_buf = 0x01; OK (1);
++
++ case RH_SET_CONFIGURATION: WR_RH_STAT (0x10000); OK (0);
++
++ default:
++ dbg ("unsupported root hub command");
++ status = TD_CC_STALL;
++ }
++
++#ifdef DEBUG
++ // ohci_dump_roothub (ohci, 0);
++#endif
++
++ len = min_t(int, len, leni);
++ if (data != data_buf)
++ memcpy (data, data_buf, len);
++ urb->actual_length = len;
++ urb->status = cc_to_error [status];
++
++#ifdef DEBUG
++ urb_print (urb, "RET(rh)", usb_pipeout (urb->pipe));
++#endif
++
++ urb->hcpriv = NULL;
++ spin_unlock_irqrestore(&ohci->ohci_lock, flags);
++ usb_dec_dev_use (usb_dev);
++ urb->dev = NULL;
++ if (urb->complete)
++ urb->complete (urb);
++ return 0;
++}
++
++/*-------------------------------------------------------------------------*/
++
++static int rh_unlink_urb (struct urb * urb)
++{
++ ohci_t * ohci = urb->dev->bus->hcpriv;
++ unsigned int flags;
++
++ spin_lock_irqsave(&ohci->ohci_lock, flags);
++ if (ohci->rh.urb == urb) {
++ ohci->rh.send = 0;
++ del_timer (&ohci->rh.rh_int_timer);
++ ohci->rh.urb = NULL;
++
++ urb->hcpriv = NULL;
++ spin_unlock_irqrestore(&ohci->ohci_lock, flags);
++ usb_dec_dev_use(urb->dev);
++ urb->dev = NULL;
++ if (urb->transfer_flags & USB_ASYNC_UNLINK) {
++ urb->status = -ECONNRESET;
++ if (urb->complete)
++ urb->complete (urb);
++ } else
++ urb->status = -ENOENT;
++ } else {
++ spin_unlock_irqrestore(&ohci->ohci_lock, flags);
++ }
++ return 0;
++}
++
++/*-------------------------------------------------------------------------*
++ * HC functions
++ *-------------------------------------------------------------------------*/
++
++/* reset the HC and BUS */
++
++static int hc_reset (ohci_t * ohci)
++{
++ int timeout = 30;
++ int smm_timeout = 50; /* 0,5 sec */
++
++#ifndef __hppa__
++ /* PA-RISC doesn't have SMM, but PDC might leave IR set */
++ if (readl (&ohci->regs->control) & OHCI_CTRL_IR) { /* SMM owns the HC */
++ writel (OHCI_OCR, &ohci->regs->cmdstatus); /* request ownership */
++ dbg("USB HC TakeOver from SMM");
++ while (readl (&ohci->regs->control) & OHCI_CTRL_IR) {
++ wait_ms (10);
++ if (--smm_timeout == 0) {
++ err("USB HC TakeOver failed!");
++ return -1;
++ }
++ }
++ }
++#endif
++
++ /* Disable HC interrupts */
++ writel (OHCI_INTR_MIE, &ohci->regs->intrdisable);
++
++ dbg("USB HC reset_hc usb-%s: ctrl = 0x%x ;",
++ ohci->ohci_dev->slot_name,
++ readl (&ohci->regs->control));
++
++ /* Reset USB (needed by some controllers) */
++ writel (0, &ohci->regs->control);
++
++ /* Force a state change from USBRESET to USBOPERATIONAL for ALi */
++ (void) readl (&ohci->regs->control); /* PCI posting */
++ writel (ohci->hc_control = OHCI_USB_OPER, &ohci->regs->control);
++
++ /* HC Reset requires max 10 ms delay */
++ writel (OHCI_HCR, &ohci->regs->cmdstatus);
++ while ((readl (&ohci->regs->cmdstatus) & OHCI_HCR) != 0) {
++ if (--timeout == 0) {
++ err("USB HC reset timed out!");
++ return -1;
++ }
++ udelay (1);
++ }
++ return 0;
++}
++
++/*-------------------------------------------------------------------------*/
++
++/* Start an OHCI controller, set the BUS operational
++ * enable interrupts
++ * connect the virtual root hub */
++
++static int hc_start (ohci_t * ohci)
++{
++ __u32 mask;
++ unsigned int fminterval;
++ struct usb_device * usb_dev;
++ struct ohci_device * dev;
++
++ ohci->disabled = 1;
++
++ /* Tell the controller where the control and bulk lists are
++ * The lists are empty now. */
++
++ writel (0, &ohci->regs->ed_controlhead);
++ writel (0, &ohci->regs->ed_bulkhead);
++
++ writel (ohci->hcca_dma, &ohci->regs->hcca); /* a reset clears this */
++
++ fminterval = 0x2edf;
++ writel ((fminterval * 9) / 10, &ohci->regs->periodicstart);
++ fminterval |= ((((fminterval - 210) * 6) / 7) << 16);
++ writel (fminterval, &ohci->regs->fminterval);
++ writel (0x628, &ohci->regs->lsthresh);
++
++ /* start controller operations */
++ ohci->hc_control = OHCI_CONTROL_INIT | OHCI_USB_OPER;
++ ohci->disabled = 0;
++ writel (ohci->hc_control, &ohci->regs->control);
++
++ /* Choose the interrupts we care about now, others later on demand */
++ mask = OHCI_INTR_MIE | OHCI_INTR_UE | OHCI_INTR_WDH | OHCI_INTR_SO;
++ writel (mask, &ohci->regs->intrenable);
++ writel (mask, &ohci->regs->intrstatus);
++
++#ifdef OHCI_USE_NPS
++ if(ohci->flags & OHCI_QUIRK_SUCKYIO)
++ {
++ /* NSC 87560 at least requires different setup .. */
++ writel ((roothub_a (ohci) | RH_A_NOCP) &
++ ~(RH_A_OCPM | RH_A_POTPGT | RH_A_PSM | RH_A_NPS),
++ &ohci->regs->roothub.a);
++ }
++ else
++ {
++ /* required for AMD-756 and some Mac platforms */
++ writel ((roothub_a (ohci) | RH_A_NPS) & ~RH_A_PSM,
++ &ohci->regs->roothub.a);
++ }
++ writel (RH_HS_LPSC, &ohci->regs->roothub.status);
++#endif /* OHCI_USE_NPS */
++
++ (void)readl (&ohci->regs->intrdisable); /* PCI posting flush */
++
++ // POTPGT delay is bits 24-31, in 2 ms units.
++ mdelay ((roothub_a (ohci) >> 23) & 0x1fe);
++
++ /* connect the virtual root hub */
++ ohci->rh.devnum = 0;
++ usb_dev = usb_alloc_dev (NULL, ohci->bus);
++ if (!usb_dev) {
++ ohci->disabled = 1;
++ return -ENOMEM;
++ }
++
++ dev = usb_to_ohci (usb_dev);
++ ohci->bus->root_hub = usb_dev;
++ usb_connect (usb_dev);
++ if (usb_new_device (usb_dev) != 0) {
++ usb_free_dev (usb_dev);
++ ohci->disabled = 1;
++ return -ENODEV;
++ }
++
++ return 0;
++}
++
++/*-------------------------------------------------------------------------*/
++
++/* called only from interrupt handler */
++
++static void check_timeouts (struct ohci *ohci)
++{
++ spin_lock (&ohci->ohci_lock);
++ while (!list_empty (&ohci->timeout_list)) {
++ struct urb *urb;
++
++ urb = list_entry (ohci->timeout_list.next, struct urb, urb_list);
++ if (time_after (jiffies, urb->timeout))
++ break;
++
++ list_del_init (&urb->urb_list);
++ if (urb->status != -EINPROGRESS)
++ continue;
++
++ urb->transfer_flags |= USB_TIMEOUT_KILLED | USB_ASYNC_UNLINK;
++ spin_unlock (&ohci->ohci_lock);
++
++ // outside the interrupt handler (in a timer...)
++ // this reference would race interrupts
++ sohci_unlink_urb (urb);
++
++ spin_lock (&ohci->ohci_lock);
++ }
++ spin_unlock (&ohci->ohci_lock);
++}
++
++
++/*-------------------------------------------------------------------------*/
++
++/* an interrupt happens */
++
++static void hc_interrupt (int irq, void * __ohci, struct pt_regs * r)
++{
++ ohci_t * ohci = __ohci;
++ struct ohci_regs * regs = ohci->regs;
++ int ints;
++
++ spin_lock (&ohci->ohci_lock);
++
++ /* avoid (slow) readl if only WDH happened */
++ if ((ohci->hcca->done_head != 0)
++ && !(le32_to_cpup (&ohci->hcca->done_head) & 0x01)) {
++ ints = OHCI_INTR_WDH;
++
++ /* cardbus/... hardware gone before remove() */
++ } else if ((ints = readl (&regs->intrstatus)) == ~(u32)0) {
++ ohci->disabled++;
++ spin_unlock (&ohci->ohci_lock);
++ err ("%s device removed!", ohci->ohci_dev->slot_name);
++ return;
++
++ /* interrupt for some other device? */
++ } else if ((ints &= readl (&regs->intrenable)) == 0) {
++ spin_unlock (&ohci->ohci_lock);
++ return;
++ }
++
++ // dbg("Interrupt: %x frame: %x", ints, le16_to_cpu (ohci->hcca->frame_no));
++
++ if (ints & OHCI_INTR_UE) {
++ ohci->disabled++;
++ err ("OHCI Unrecoverable Error, controller usb-%s disabled",
++ ohci->ohci_dev->slot_name);
++ // e.g. due to PCI Master/Target Abort
++
++#ifdef DEBUG
++ ohci_dump (ohci, 1);
++#else
++ // FIXME: be optimistic, hope that bug won't repeat often.
++ // Make some non-interrupt context restart the controller.
++ // Count and limit the retries though; either hardware or
++ // software errors can go forever...
++#endif
++ hc_reset (ohci);
++ }
++
++ if (ints & OHCI_INTR_WDH) {
++ writel (OHCI_INTR_WDH, &regs->intrdisable);
++ (void)readl (&regs->intrdisable); /* PCI posting flush */
++ dl_done_list (ohci, dl_reverse_done_list (ohci));
++ writel (OHCI_INTR_WDH, &regs->intrenable);
++ (void)readl (&regs->intrdisable); /* PCI posting flush */
++ }
++
++ if (ints & OHCI_INTR_SO) {
++ dbg("USB Schedule overrun");
++ writel (OHCI_INTR_SO, &regs->intrenable);
++ (void)readl (&regs->intrdisable); /* PCI posting flush */
++ }
++
++ // FIXME: this assumes SOF (1/ms) interrupts don't get lost...
++ if (ints & OHCI_INTR_SF) {
++ unsigned int frame = le16_to_cpu (ohci->hcca->frame_no) & 1;
++ writel (OHCI_INTR_SF, &regs->intrdisable);
++ (void)readl (&regs->intrdisable); /* PCI posting flush */
++ if (ohci->ed_rm_list[!frame] != NULL) {
++ dl_del_list (ohci, !frame);
++ }
++ if (ohci->ed_rm_list[frame] != NULL) {
++ writel (OHCI_INTR_SF, &regs->intrenable);
++ (void)readl (&regs->intrdisable); /* PCI posting flush */
++ }
++ }
++
++ /*
++ * Finally, we are done with trashing about our hardware lists
++ * and other CPUs are allowed in. The festive flipping of the lock
++ * ensues as we struggle with the check_timeouts disaster.
++ */
++ spin_unlock (&ohci->ohci_lock);
++
++ if (!list_empty (&ohci->timeout_list)) {
++ check_timeouts (ohci);
++// FIXME: enable SF as needed in a timer;
++// don't make lots of 1ms interrupts
++// On unloaded USB, think 4k ~= 4-5msec
++ if (!list_empty (&ohci->timeout_list))
++ writel (OHCI_INTR_SF, &regs->intrenable);
++ }
++
++ writel (ints, &regs->intrstatus);
++ writel (OHCI_INTR_MIE, &regs->intrenable);
++ (void)readl (&regs->intrdisable); /* PCI posting flush */
++
++ ohci_complete(ohci);
++}
++
++/*-------------------------------------------------------------------------*/
++
++/* allocate OHCI */
++
++static ohci_t * __devinit hc_alloc_ohci (struct pci_dev *dev, void * mem_base)
++{
++ ohci_t * ohci;
++
++ ohci = (ohci_t *) kmalloc (sizeof *ohci, GFP_KERNEL);
++ if (!ohci)
++ return NULL;
++
++ memset (ohci, 0, sizeof (ohci_t));
++
++ ohci->hcca = pci_alloc_consistent (dev, sizeof *ohci->hcca,
++ &ohci->hcca_dma);
++ if (!ohci->hcca) {
++ kfree (ohci);
++ return NULL;
++ }
++ memset (ohci->hcca, 0, sizeof (struct ohci_hcca));
++
++ ohci->disabled = 1;
++ ohci->sleeping = 0;
++ ohci->irq = -1;
++ ohci->regs = mem_base;
++
++ ohci->ohci_dev = dev;
++ pci_set_drvdata(dev, ohci);
++
++ INIT_LIST_HEAD (&ohci->timeout_list);
++ spin_lock_init(&ohci->ohci_lock);
++
++ ohci->bus = usb_alloc_bus (&sohci_device_operations);
++ if (!ohci->bus) {
++ pci_set_drvdata (dev, NULL);
++ pci_free_consistent (ohci->ohci_dev, sizeof *ohci->hcca,
++ ohci->hcca, ohci->hcca_dma);
++ kfree (ohci);
++ return NULL;
++ }
++ ohci->bus->bus_name = dev->slot_name;
++ ohci->bus->hcpriv = (void *) ohci;
++
++ return ohci;
++}
++
++
++/*-------------------------------------------------------------------------*/
++
++/* De-allocate all resources.. */
++
++static void hc_release_ohci (ohci_t * ohci)
++{
++ dbg ("USB HC release ohci usb-%s", ohci->ohci_dev->slot_name);
++
++ /* disconnect all devices */
++ if (ohci->bus->root_hub)
++ usb_disconnect (&ohci->bus->root_hub);
++
++ if (!ohci->disabled)
++ hc_reset (ohci);
++
++ if (ohci->irq >= 0) {
++ free_irq (ohci->irq, ohci);
++ ohci->irq = -1;
++ }
++ pci_set_drvdata(ohci->ohci_dev, NULL);
++ if (ohci->bus) {
++ if (ohci->bus->busnum != -1)
++ usb_deregister_bus (ohci->bus);
++
++ usb_free_bus (ohci->bus);
++ }
++
++ ohci_mem_cleanup (ohci);
++
++ /* unmap the IO address space */
++ iounmap (ohci->regs);
++
++ pci_free_consistent (ohci->ohci_dev, sizeof *ohci->hcca,
++ ohci->hcca, ohci->hcca_dma);
++ kfree (ohci);
++}
++
++/*-------------------------------------------------------------------------*/
++
++/* Increment the module usage count, start the control thread and
++ * return success. */
++
++static struct pci_driver ohci_pci_driver;
++
++static int __devinit
++hc_found_ohci (struct pci_dev *dev, int irq,
++ void *mem_base, const struct pci_device_id *id)
++{
++ ohci_t * ohci;
++ char buf[8], *bufp = buf;
++ int ret;
++
++#ifndef __sparc__
++ sprintf(buf, "%d", irq);
++#else
++ bufp = __irq_itoa(irq);
++#endif
++ printk(KERN_INFO __FILE__ ": USB OHCI at membase 0x%lx, IRQ %s\n",
++ (unsigned long) mem_base, bufp);
++ printk(KERN_INFO __FILE__ ": usb-%s, %s\n", dev->slot_name, dev->name);
++
++ ohci = hc_alloc_ohci (dev, mem_base);
++ if (!ohci) {
++ return -ENOMEM;
++ }
++ if ((ret = ohci_mem_init (ohci)) < 0) {
++ hc_release_ohci (ohci);
++ return ret;
++ }
++ ohci->flags = id->driver_data;
++
++ /* Check for NSC87560. We have to look at the bridge (fn1) to identify
++ the USB (fn2). This quirk might apply to more or even all NSC stuff
++ I don't know.. */
++
++ if(dev->vendor == PCI_VENDOR_ID_NS)
++ {
++ struct pci_dev *fn1 = pci_find_slot(dev->bus->number, PCI_DEVFN(PCI_SLOT(dev->devfn), 1));
++ if(fn1 && fn1->vendor == PCI_VENDOR_ID_NS && fn1->device == PCI_DEVICE_ID_NS_87560_LIO)
++ ohci->flags |= OHCI_QUIRK_SUCKYIO;
++
++ }
++
++ if (ohci->flags & OHCI_QUIRK_SUCKYIO)
++ printk (KERN_INFO __FILE__ ": Using NSC SuperIO setup\n");
++ if (ohci->flags & OHCI_QUIRK_AMD756)
++ printk (KERN_INFO __FILE__ ": AMD756 erratum 4 workaround\n");
++
++ if (hc_reset (ohci) < 0) {
++ hc_release_ohci (ohci);
++ return -ENODEV;
++ }
++
++ /* FIXME this is a second HC reset; why?? */
++ writel (ohci->hc_control = OHCI_USB_RESET, &ohci->regs->control);
++ (void)readl (&ohci->regs->intrdisable); /* PCI posting flush */
++ wait_ms (10);
++
++ usb_register_bus (ohci->bus);
++
++ if (request_irq (irq, hc_interrupt, SA_SHIRQ,
++ ohci_pci_driver.name, ohci) != 0) {
++ err ("request interrupt %s failed", bufp);
++ hc_release_ohci (ohci);
++ return -EBUSY;
++ }
++ ohci->irq = irq;
++
++ if (hc_start (ohci) < 0) {
++ err ("can't start usb-%s", dev->slot_name);
++ hc_release_ohci (ohci);
++ return -EBUSY;
++ }
++
++#ifdef DEBUG
++ ohci_dump (ohci, 1);
++#endif
++ return 0;
++}
++
++/*-------------------------------------------------------------------------*/
++
++#ifdef CONFIG_PM
++
++/* controller died; cleanup debris, then restart */
++/* must not be called from interrupt context */
++
++static void hc_restart (ohci_t *ohci)
++{
++ int temp;
++ int i;
++
++ if (ohci->pci_latency)
++ pci_write_config_byte (ohci->ohci_dev, PCI_LATENCY_TIMER, ohci->pci_latency);
++
++ ohci->disabled = 1;
++ ohci->sleeping = 0;
++ if (ohci->bus->root_hub)
++ usb_disconnect (&ohci->bus->root_hub);
++
++ /* empty the interrupt branches */
++ for (i = 0; i < NUM_INTS; i++) ohci->ohci_int_load[i] = 0;
++ for (i = 0; i < NUM_INTS; i++) ohci->hcca->int_table[i] = 0;
++
++ /* no EDs to remove */
++ ohci->ed_rm_list [0] = NULL;
++ ohci->ed_rm_list [1] = NULL;
++
++ /* empty control and bulk lists */
++ ohci->ed_isotail = NULL;
++ ohci->ed_controltail = NULL;
++ ohci->ed_bulktail = NULL;
++
++ if ((temp = hc_reset (ohci)) < 0 || (temp = hc_start (ohci)) < 0) {
++ err ("can't restart usb-%s, %d", ohci->ohci_dev->slot_name, temp);
++ } else
++ dbg ("restart usb-%s completed", ohci->ohci_dev->slot_name);
++}
++
++#endif /* CONFIG_PM */
++
++/*-------------------------------------------------------------------------*/
++
++/* configured so that an OHCI device is always provided */
++/* always called with process context; sleeping is OK */
++
++static int __devinit
++ohci_pci_probe (struct pci_dev *dev, const struct pci_device_id *id)
++{
++ unsigned long mem_resource, mem_len;
++ void *mem_base;
++ int status;
++
++ if (pci_enable_device(dev) < 0)
++ return -ENODEV;
++
++ if (!dev->irq) {
++ err("found OHCI device with no IRQ assigned. check BIOS settings!");
++ pci_disable_device (dev);
++ return -ENODEV;
++ }
++
++ /* we read its hardware registers as memory */
++ mem_resource = pci_resource_start(dev, 0);
++ mem_len = pci_resource_len(dev, 0);
++ if (!request_mem_region (mem_resource, mem_len, ohci_pci_driver.name)) {
++ dbg ("controller already in use");
++ pci_disable_device (dev);
++ return -EBUSY;
++ }
++
++ mem_base = ioremap_nocache (mem_resource, mem_len);
++ if (!mem_base) {
++ err("Error mapping OHCI memory");
++ release_mem_region (mem_resource, mem_len);
++ pci_disable_device (dev);
++ return -EFAULT;
++ }
++
++ /* controller writes into our memory */
++ pci_set_master (dev);
++
++ status = hc_found_ohci (dev, dev->irq, mem_base, id);
++ if (status < 0) {
++ iounmap (mem_base);
++ release_mem_region (mem_resource, mem_len);
++ pci_disable_device (dev);
++ }
++ return status;
++}
++
++/*-------------------------------------------------------------------------*/
++
++/* may be called from interrupt context [interface spec] */
++/* may be called without controller present */
++/* may be called with controller, bus, and devices active */
++
++static void __devexit
++ohci_pci_remove (struct pci_dev *dev)
++{
++ ohci_t *ohci = pci_get_drvdata(dev);
++
++ dbg ("remove %s controller usb-%s%s%s",
++ hcfs2string (ohci->hc_control & OHCI_CTRL_HCFS),
++ dev->slot_name,
++ ohci->disabled ? " (disabled)" : "",
++ in_interrupt () ? " in interrupt" : ""
++ );
++#ifdef DEBUG
++ ohci_dump (ohci, 1);
++#endif
++
++ /* don't wake up sleeping controllers, or block in interrupt context */
++ if ((ohci->hc_control & OHCI_CTRL_HCFS) != OHCI_USB_OPER || in_interrupt ()) {
++ dbg ("controller being disabled");
++ ohci->disabled = 1;
++ }
++
++ /* on return, USB will always be reset (if present) */
++ if (ohci->disabled)
++ writel (ohci->hc_control = OHCI_USB_RESET,
++ &ohci->regs->control);
++
++ hc_release_ohci (ohci);
++
++ release_mem_region (pci_resource_start (dev, 0), pci_resource_len (dev, 0));
++ pci_disable_device (dev);
++}
++
++
++#ifdef CONFIG_PM
++
++/*-------------------------------------------------------------------------*/
++
++static int
++ohci_pci_suspend (struct pci_dev *dev, u32 state)
++{
++ ohci_t *ohci = pci_get_drvdata(dev);
++ unsigned long flags;
++ u16 cmd;
++
++ if ((ohci->hc_control & OHCI_CTRL_HCFS) != OHCI_USB_OPER) {
++ dbg ("can't suspend usb-%s (state is %s)", dev->slot_name,
++ hcfs2string (ohci->hc_control & OHCI_CTRL_HCFS));
++ return -EIO;
++ }
++
++ /* act as if usb suspend can always be used */
++ info ("USB suspend: usb-%s", dev->slot_name);
++ ohci->sleeping = 1;
++
++ /* First stop processing */
++ spin_lock_irqsave (&ohci->ohci_lock, flags);
++ ohci->hc_control &= ~(OHCI_CTRL_PLE|OHCI_CTRL_CLE|OHCI_CTRL_BLE|OHCI_CTRL_IE);
++ writel (ohci->hc_control, &ohci->regs->control);
++ writel (OHCI_INTR_SF, &ohci->regs->intrstatus);
++ (void) readl (&ohci->regs->intrstatus);
++ spin_unlock_irqrestore (&ohci->ohci_lock, flags);
++
++ /* Wait a frame or two */
++ mdelay(1);
++ if (!readl (&ohci->regs->intrstatus) & OHCI_INTR_SF)
++ mdelay (1);
++
++#ifdef CONFIG_PMAC_PBOOK
++ if (_machine == _MACH_Pmac)
++ disable_irq (ohci->irq);
++ /* else, 2.4 assumes shared irqs -- don't disable */
++#endif
++ /* Enable remote wakeup */
++ writel (readl(&ohci->regs->intrenable) | OHCI_INTR_RD, &ohci->regs->intrenable);
++
++ /* Suspend chip and let things settle down a bit */
++ ohci->hc_control = OHCI_USB_SUSPEND;
++ writel (ohci->hc_control, &ohci->regs->control);
++ (void) readl (&ohci->regs->control);
++ mdelay (500); /* No schedule here ! */
++ switch (readl (&ohci->regs->control) & OHCI_CTRL_HCFS) {
++ case OHCI_USB_RESET:
++ dbg("Bus in reset phase ???");
++ break;
++ case OHCI_USB_RESUME:
++ dbg("Bus in resume phase ???");
++ break;
++ case OHCI_USB_OPER:
++ dbg("Bus in operational phase ???");
++ break;
++ case OHCI_USB_SUSPEND:
++ dbg("Bus suspended");
++ break;
++ }
++ /* In some rare situations, Apple's OHCI have happily trashed
++ * memory during sleep. We disable it's bus master bit during
++ * suspend
++ */
++ pci_read_config_word (dev, PCI_COMMAND, &cmd);
++ cmd &= ~PCI_COMMAND_MASTER;
++ pci_write_config_word (dev, PCI_COMMAND, cmd);
++#ifdef CONFIG_PMAC_PBOOK
++ {
++ struct device_node *of_node;
++
++ /* Disable USB PAD & cell clock */
++ of_node = pci_device_to_OF_node (ohci->ohci_dev);
++ if (of_node)
++ pmac_call_feature(PMAC_FTR_USB_ENABLE, of_node, 0, 0);
++ }
++#endif
++ return 0;
++}
++
++/*-------------------------------------------------------------------------*/
++
++static int
++ohci_pci_resume (struct pci_dev *dev)
++{
++ ohci_t *ohci = pci_get_drvdata(dev);
++ int temp;
++ unsigned long flags;
++
++ /* guard against multiple resumes */
++ atomic_inc (&ohci->resume_count);
++ if (atomic_read (&ohci->resume_count) != 1) {
++ err ("concurrent PCI resumes for usb-%s", dev->slot_name);
++ atomic_dec (&ohci->resume_count);
++ return 0;
++ }
++
++#ifdef CONFIG_PMAC_PBOOK
++ {
++ struct device_node *of_node;
++
++ /* Re-enable USB PAD & cell clock */
++ of_node = pci_device_to_OF_node (ohci->ohci_dev);
++ if (of_node)
++ pmac_call_feature(PMAC_FTR_USB_ENABLE, of_node, 0, 1);
++ }
++#endif
++
++ /* did we suspend, or were we powered off? */
++ ohci->hc_control = readl (&ohci->regs->control);
++ temp = ohci->hc_control & OHCI_CTRL_HCFS;
++
++#ifdef DEBUG
++ /* the registers may look crazy here */
++ ohci_dump_status (ohci);
++#endif
++
++ /* Re-enable bus mastering */
++ pci_set_master(ohci->ohci_dev);
++
++ switch (temp) {
++
++ case OHCI_USB_RESET: // lost power
++ info ("USB restart: usb-%s", dev->slot_name);
++ hc_restart (ohci);
++ break;
++
++ case OHCI_USB_SUSPEND: // host wakeup
++ case OHCI_USB_RESUME: // remote wakeup
++ info ("USB continue: usb-%s from %s wakeup", dev->slot_name,
++ (temp == OHCI_USB_SUSPEND)
++ ? "host" : "remote");
++ ohci->hc_control = OHCI_USB_RESUME;
++ writel (ohci->hc_control, &ohci->regs->control);
++ (void) readl (&ohci->regs->control);
++ mdelay (20); /* no schedule here ! */
++ /* Some controllers (lucent) need a longer delay here */
++ mdelay (15);
++ temp = readl (&ohci->regs->control);
++ temp = ohci->hc_control & OHCI_CTRL_HCFS;
++ if (temp != OHCI_USB_RESUME) {
++ err ("controller usb-%s won't resume", dev->slot_name);
++ ohci->disabled = 1;
++ return -EIO;
++ }
++
++ /* Some chips likes being resumed first */
++ writel (OHCI_USB_OPER, &ohci->regs->control);
++ (void) readl (&ohci->regs->control);
++ mdelay (3);
++
++ /* Then re-enable operations */
++ spin_lock_irqsave (&ohci->ohci_lock, flags);
++ ohci->disabled = 0;
++ ohci->sleeping = 0;
++ ohci->hc_control = OHCI_CONTROL_INIT | OHCI_USB_OPER;
++ if (!ohci->ed_rm_list[0] && !ohci->ed_rm_list[1]) {
++ if (ohci->ed_controltail)
++ ohci->hc_control |= OHCI_CTRL_CLE;
++ if (ohci->ed_bulktail)
++ ohci->hc_control |= OHCI_CTRL_BLE;
++ }
++ writel (ohci->hc_control, &ohci->regs->control);
++ writel (OHCI_INTR_SF, &ohci->regs->intrstatus);
++ writel (OHCI_INTR_SF, &ohci->regs->intrenable);
++ /* Check for a pending done list */
++ writel (OHCI_INTR_WDH, &ohci->regs->intrdisable);
++ (void) readl (&ohci->regs->intrdisable);
++#ifdef CONFIG_PMAC_PBOOK
++ if (_machine == _MACH_Pmac)
++ enable_irq (ohci->irq);
++#endif
++ if (ohci->hcca->done_head)
++ dl_done_list (ohci, dl_reverse_done_list (ohci));
++ writel (OHCI_INTR_WDH, &ohci->regs->intrenable);
++ writel (OHCI_BLF, &ohci->regs->cmdstatus); /* start bulk list */
++ writel (OHCI_CLF, &ohci->regs->cmdstatus); /* start Control list */
++ spin_unlock_irqrestore (&ohci->ohci_lock, flags);
++ break;
++
++ default:
++ warn ("odd PCI resume for usb-%s", dev->slot_name);
++ }
++
++ /* controller is operational, extra resumes are harmless */
++ atomic_dec (&ohci->resume_count);
++
++ return 0;
++}
++
++#endif /* CONFIG_PM */
++
++
++/*-------------------------------------------------------------------------*/
++
++static const struct pci_device_id __devinitdata ohci_pci_ids [] = { {
++
++ /*
++ * AMD-756 [Viper] USB has a serious erratum when used with
++ * lowspeed devices like mice.
++ */
++ vendor: 0x1022,
++ device: 0x740c,
++ subvendor: PCI_ANY_ID,
++ subdevice: PCI_ANY_ID,
++
++ driver_data: OHCI_QUIRK_AMD756,
++
++} , {
++
++ /* handle any USB OHCI controller */
++ class: ((PCI_CLASS_SERIAL_USB << 8) | 0x10),
++ class_mask: ~0,
++
++ /* no matter who makes it */
++ vendor: PCI_ANY_ID,
++ device: PCI_ANY_ID,
++ subvendor: PCI_ANY_ID,
++ subdevice: PCI_ANY_ID,
++
++ }, { /* end: all zeroes */ }
++};
++
++MODULE_DEVICE_TABLE (pci, ohci_pci_ids);
++
++static struct pci_driver ohci_pci_driver = {
++ name: "usb-ohci",
++ id_table: &ohci_pci_ids [0],
++
++ probe: ohci_pci_probe,
++ remove: __devexit_p(ohci_pci_remove),
++
++#ifdef CONFIG_PM
++ suspend: ohci_pci_suspend,
++ resume: ohci_pci_resume,
++#endif /* PM */
++};
++
++
++/*-------------------------------------------------------------------------*/
++
++static int __init ohci_hcd_init (void)
++{
++ return pci_module_init (&ohci_pci_driver);
++}
++
++/*-------------------------------------------------------------------------*/
++
++static void __exit ohci_hcd_cleanup (void)
++{
++ pci_unregister_driver (&ohci_pci_driver);
++}
++
++module_init (ohci_hcd_init);
++module_exit (ohci_hcd_cleanup);
++
++
++MODULE_AUTHOR( DRIVER_AUTHOR );
++MODULE_DESCRIPTION( DRIVER_DESC );
++MODULE_LICENSE("GPL");
diff --git a/linux/linux-mtx-1-2.4.27/09-au1000-eth-vlan.diff b/linux/linux-mtx-1-2.4.27/09-au1000-eth-vlan.diff
index e69de29bb2..2f09c02d5a 100644
--- a/linux/linux-mtx-1-2.4.27/09-au1000-eth-vlan.diff
+++ b/linux/linux-mtx-1-2.4.27/09-au1000-eth-vlan.diff
@@ -0,0 +1,10 @@
+--- linux/drivers/net/au1000_eth.c.orig 2004-11-18 13:44:52.163605416 +0100
++++ linux/drivers/net/au1000_eth.c 2004-11-18 13:51:42.096286176 +0100
+@@ -1389,6 +1389,7 @@
+ control |= MAC_FULL_DUPLEX;
+ }
+ aup->mac->control = control;
++ aup->mac->vlan1_tag = 0x8100; /* activate vlan support */
+ au_sync();
+
+ spin_unlock_irqrestore(&aup->lock, flags);
diff --git a/linux/linux-mtx-1-2.4.27/10-iw-max-spy-32.diff b/linux/linux-mtx-1-2.4.27/10-iw-max-spy-32.diff
index e69de29bb2..e782d3d3dc 100644
--- a/linux/linux-mtx-1-2.4.27/10-iw-max-spy-32.diff
+++ b/linux/linux-mtx-1-2.4.27/10-iw-max-spy-32.diff
@@ -0,0 +1,11 @@
+--- linux-mips-2.4.24-pre2/include/linux/wireless.h 2004-11-17 18:05:09.000000000 +0100
++++ linux/include/linux/wireless.h 2004-11-17 19:02:44.370081592 +0100
+@@ -334,7 +334,7 @@
+ * a few of them in the struct iw_range. */
+
+ /* Maximum of address that you may set with SPY */
+-#define IW_MAX_SPY 8
++#define IW_MAX_SPY 32
+
+ /* Maximum of address that you may get in the
+ list of access points in range */
diff --git a/linux/linux-mtx-1-2.4.27/11-mtd-proc-partition-rw.diff b/linux/linux-mtx-1-2.4.27/11-mtd-proc-partition-rw.diff
index e69de29bb2..54ba5fff98 100644
--- a/linux/linux-mtx-1-2.4.27/11-mtd-proc-partition-rw.diff
+++ b/linux/linux-mtx-1-2.4.27/11-mtd-proc-partition-rw.diff
@@ -0,0 +1,173 @@
+diff -Nurb linux/drivers/mtd/mtdcore.c linux-mtd-rw/drivers/mtd/mtdcore.c
+--- linux/drivers/mtd/mtdcore.c 2004-11-18 13:16:00.000000000 +0100
++++ linux-mtd-rw/drivers/mtd/mtdcore.c 2004-11-18 15:27:13.130036616 +0100
+@@ -25,6 +25,10 @@
+
+ #include <linux/mtd/mtd.h>
+
++/* this symbol is exported by the procfs. */
++extern struct proc_dir_entry *proc_sys_root;
++
++
+ /* These are exported solely for the purpose of mtd_blkdevs.c. You
+ should not use them for _anything_ else */
+ DECLARE_MUTEX(mtd_table_mutex);
+@@ -336,8 +340,83 @@
+
+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
+ static struct proc_dir_entry *proc_mtd;
++
++static struct proc_dir_entry *proc_sys_mtd;
++static struct proc_dir_entry *proc_sys_mtd_partition[MAX_MTD_DEVICES];
++static struct proc_dir_entry *proc_sys_mtd_partition_rw[MAX_MTD_DEVICES];
+ #endif
+
++/*===================================0
++ * mtdproc_read_partition_access
++ */
++static int mtdproc_read_partition_access ( char *page, char **start, off_t off,int count,
++ int *eof, void *data
++ )
++{
++ int partid = (unsigned int)data;
++ int len = 0;
++
++ // NO RETURN FROM HERE UNTIL "up(&mtd_table_mutex)".
++ down(&mtd_table_mutex);
++
++ if (partid < MAX_MTD_DEVICES)
++ {
++ struct mtd_info *this = mtd_table[partid];
++ if (this)
++ {
++ page[len] = (this->flags & MTD_WRITEABLE) ? '1' : '0';
++ len++;
++ }
++ }
++
++ up(&mtd_table_mutex);
++
++ if (off >= len)
++ return 0;
++ *start = page + off;
++ return ((count < len-off) ? count : len-off);
++}
++
++
++static int mtdproc_write_partition_access (struct file *file, const char *buffer,
++ unsigned long count, void *data)
++{
++ int partid = (unsigned int)data;
++ int len = 0;
++
++ // NO RETURN FROM HERE UNTIL "up(&mtd_table_mutex)".
++ down(&mtd_table_mutex);
++
++ if (partid < MAX_MTD_DEVICES)
++ {
++ struct mtd_info *this = mtd_table[partid];
++ if (this && count > 0)
++ {
++ switch (*buffer)
++ {
++ case '0':
++ this->flags &= ~(this->master_flags & MTD_WRITEABLE);
++ break;
++
++ case '1':
++ this->flags |= ~(this->master_flags & MTD_WRITEABLE);
++ break;
++
++ default:
++ break;
++ }
++ }
++ }
++
++ up(&mtd_table_mutex);
++
++ return count;
++}
++
++
++
++
++
+ static inline int mtd_proc_info (char *buf, int i)
+ {
+ struct mtd_info *this = mtd_table[i];
+@@ -349,6 +428,7 @@
+ this->erasesize, this->name);
+ }
+
++
+ static int mtd_read_proc ( char *page, char **start, off_t off,int count
+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
+ ,int *eof, void *data_unused
+@@ -404,12 +484,31 @@
+ /*====================================================================*/
+ /* Init code */
+
++
+ int __init init_mtd(void)
+ {
+ #ifdef CONFIG_PROC_FS
+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
++ int i;
++
+ if ((proc_mtd = create_proc_entry( "mtd", 0, 0 )))
+ proc_mtd->read_proc = mtd_read_proc;
++
++ proc_sys_mtd = proc_mkdir("mtd", proc_sys_root);
++ for (i=0; i<MAX_MTD_DEVICES; i++)
++ {
++ char partname[10];
++ sprintf (partname, "%d", i);
++
++ proc_sys_mtd_partition[i] = proc_mkdir(partname, proc_sys_mtd);
++ proc_sys_mtd_partition_rw[i] = create_proc_entry ("rw", S_IFREG | S_IRUGO, proc_sys_mtd_partition[i]);
++ if (proc_sys_mtd_partition_rw[i])
++ {
++ proc_sys_mtd_partition_rw[i]->read_proc = mtdproc_read_partition_access;
++ proc_sys_mtd_partition_rw[i]->write_proc = mtdproc_write_partition_access;
++ proc_sys_mtd_partition_rw[i]->data = (void *)i;
++ }
++ }
+ #else
+ proc_register_dynamic(&proc_root,&mtd_proc_entry);
+ #endif
+@@ -425,6 +524,8 @@
+ return 0;
+ }
+
++
++
+ static void __exit cleanup_mtd(void)
+ {
+ #ifdef CONFIG_PM
+diff -Nurb linux/drivers/mtd/mtdpart.c linux-mtd-rw/drivers/mtd/mtdpart.c
+--- linux/drivers/mtd/mtdpart.c 2004-11-18 13:16:00.000000000 +0100
++++ linux-mtd-rw/drivers/mtd/mtdpart.c 2004-11-18 15:27:13.131036464 +0100
+@@ -341,6 +341,9 @@
+ /* set up the MTD object for this partition */
+ slave->mtd.type = master->type;
+ slave->mtd.flags = master->flags & ~parts[i].mask_flags;
++ slave->mtd.master_flags = master->flags;
++ slave->mtd.mask_flags = parts[i].mask_flags;
++
+ slave->mtd.size = parts[i].size;
+ slave->mtd.oobblock = master->oobblock;
+ slave->mtd.oobsize = master->oobsize;
+diff -Nurb linux/include/linux/mtd/mtd.h linux-mtd-rw/include/linux/mtd/mtd.h
+--- linux/include/linux/mtd/mtd.h 2004-11-18 13:16:31.000000000 +0100
++++ linux-mtd-rw/include/linux/mtd/mtd.h 2004-11-18 15:27:13.000000000 +0100
+@@ -232,6 +232,9 @@
+
+ struct module *owner;
+ int usecount;
++
++ u_int32_t master_flags;
++ u_int32_t mask_flags;
+ };
+
+
diff --git a/linux/linux-mtx-1-2.4.27/12-openswan-2.2.0-nat-t.diff b/linux/linux-mtx-1-2.4.27/12-openswan-2.2.0-nat-t.diff
index e69de29bb2..2a18d605bf 100644
--- a/linux/linux-mtx-1-2.4.27/12-openswan-2.2.0-nat-t.diff
+++ b/linux/linux-mtx-1-2.4.27/12-openswan-2.2.0-nat-t.diff
@@ -0,0 +1,143 @@
+diff -Nurb linux-08/include/net/sock.h linux/include/net/sock.h
+--- linux-08/include/net/sock.h 2004-11-19 11:37:23.376715120 +0100
++++ linux/include/net/sock.h 2004-11-19 11:39:29.874484536 +0100
+@@ -256,6 +256,13 @@
+ __u32 end_seq;
+ };
+
++#if 1
++#define UDP_OPT_IN_SOCK 1
++struct udp_opt {
++ __u32 esp_in_udp;
++};
++#endif
++
+ struct tcp_opt {
+ int tcp_header_len; /* Bytes of tcp header to send */
+
+@@ -648,6 +655,9 @@
+ #if defined(CONFIG_SPX) || defined (CONFIG_SPX_MODULE)
+ struct spx_opt af_spx;
+ #endif /* CONFIG_SPX */
++#if 1
++ struct udp_opt af_udp;
++#endif
+
+ } tp_pinfo;
+
+diff -Nurb linux-08/net/Config.in linux/net/Config.in
+--- linux-08/net/Config.in 2004-11-19 11:37:23.770655232 +0100
++++ linux/net/Config.in 2004-11-19 11:39:29.874484536 +0100
+@@ -102,4 +102,6 @@
+ dep_tristate 'Packet Generator (USE WITH CAUTION)' CONFIG_NET_PKTGEN $CONFIG_PROC_FS
+ endmenu
+
++bool 'IPSEC NAT-Traversal' CONFIG_IPSEC_NAT_TRAVERSAL
++
+ endmenu
+diff -Nurb linux-08/net/ipv4/udp.c linux/net/ipv4/udp.c
+--- linux-08/net/ipv4/udp.c 2004-11-19 11:37:23.897635928 +0100
++++ linux/net/ipv4/udp.c 2004-11-19 11:39:29.875484384 +0100
+@@ -810,6 +810,9 @@
+
+ static int udp_queue_rcv_skb(struct sock * sk, struct sk_buff *skb)
+ {
++#ifdef CONFIG_IPSEC_NAT_TRAVERSAL
++ struct udp_opt *tp = &(sk->tp_pinfo.af_udp);
++#endif
+ /*
+ * Charge it to the socket, dropping if the queue is full.
+ */
+@@ -827,6 +830,40 @@
+ }
+ #endif
+
++#ifdef CONFIG_IPSEC_NAT_TRAVERSAL
++ if (tp->esp_in_udp) {
++ /*
++ * Set skb->sk and xmit packet to ipsec_rcv.
++ *
++ * If ret != 0, ipsec_rcv refused the packet (not ESPinUDP),
++ * restore skb->sk and fall back to sock_queue_rcv_skb
++ */
++ struct inet_protocol *esp = NULL;
++
++#if defined(CONFIG_IPSEC) && !defined(CONFIG_IPSEC_MODULE)
++ /* optomize only when we know it is statically linked */
++ extern struct inet_protocol esp_protocol;
++ esp = &esp_protocol;
++#else
++ for (esp = (struct inet_protocol *)inet_protos[IPPROTO_ESP & (MAX_INET_PROTOS - 1)];
++ (esp) && (esp->protocol != IPPROTO_ESP);
++ esp = esp->next);
++#endif
++
++ if (esp && esp->handler) {
++ struct sock *sav_sk = skb->sk;
++ skb->sk = sk;
++ if (esp->handler(skb) == 0) {
++ skb->sk = sav_sk;
++ /*not sure we might count ESPinUDP as UDP...*/
++ UDP_INC_STATS_BH(UdpInDatagrams);
++ return 0;
++ }
++ skb->sk = sav_sk;
++ }
++ }
++#endif
++
+ if (sock_queue_rcv_skb(sk,skb)<0) {
+ UDP_INC_STATS_BH(UdpInErrors);
+ IP_INC_STATS_BH(IpInDiscards);
+@@ -1050,13 +1087,49 @@
+ return len;
+ }
+
++static int udp_setsockopt(struct sock *sk, int level, int optname,
++ char *optval, int optlen)
++{
++ struct udp_opt *tp = &(sk->tp_pinfo.af_udp);
++ int val;
++ int err = 0;
++
++ if (level != SOL_UDP)
++ return ip_setsockopt(sk, level, optname, optval, optlen);
++
++ if(optlen<sizeof(int))
++ return -EINVAL;
++
++ if (get_user(val, (int *)optval))
++ return -EFAULT;
++
++ lock_sock(sk);
++
++ switch(optname) {
++#ifdef CONFIG_IPSEC_NAT_TRAVERSAL
++#ifndef UDP_ESPINUDP
++#define UDP_ESPINUDP 100
++#endif
++ case UDP_ESPINUDP:
++ tp->esp_in_udp = val;
++ break;
++#endif
++ default:
++ err = -ENOPROTOOPT;
++ break;
++ }
++
++ release_sock(sk);
++ return err;
++}
++
+ struct proto udp_prot = {
+ name: "UDP",
+ close: udp_close,
+ connect: udp_connect,
+ disconnect: udp_disconnect,
+ ioctl: udp_ioctl,
+- setsockopt: ip_setsockopt,
++ setsockopt: udp_setsockopt,
+ getsockopt: ip_getsockopt,
+ sendmsg: udp_sendmsg,
+ recvmsg: udp_recvmsg,
+
diff --git a/linux/linux-mtx-1-2.4.27/13-openswan-2.2.0.patch b/linux/linux-mtx-1-2.4.27/13-openswan-2.2.0.patch
index e69de29bb2..db64219eda 100644
--- a/linux/linux-mtx-1-2.4.27/13-openswan-2.2.0.patch
+++ b/linux/linux-mtx-1-2.4.27/13-openswan-2.2.0.patch
@@ -0,0 +1,61573 @@
+make[1]: Entering directory `/data/mtx/oe/tmp/work/openswan-2.2.0-r0/openswan-2.2.0'
+packaging/utils/kernelpatch 2.4
+--- linux/Documentation/Configure.help.orig Fri Dec 21 12:41:53 2001
++++ linux/Documentation/Configure.help Mon Jul 29 16:35:32 2002
+@@ -24237,5 +24237,65 @@
+
+-#
++IP Security Protocol (IPSEC) (EXPERIMENTAL)
++CONFIG_IPSEC
++ This unit is experimental code.
++ Pick 'y' for static linking, 'm' for module support or 'n' for none.
++ This option adds support for network layer packet encryption and/or
++ authentication with participating hosts. The standards start with:
++ RFCs 2411, 2407 and 2401. Others are mentioned where they refer to
++ specific features below. There are more pending which can be found
++ at: ftp://ftp.ietf.org/internet-drafts/draft-ietf-ipsec-*.
++ A description of each document can also be found at:
++ http://ietf.org/ids.by.wg/ipsec.html.
++ Their charter can be found at:
++ http://www.ietf.org/html.charters/ipsec-charter.html
++ Snapshots and releases of the current work can be found at:
++ http://www.freeswan.org/
++
++IPSEC: IP-in-IP encapsulation
++CONFIG_IPSEC_IPIP
++ This option provides support for tunnel mode IPSEC. It is recommended
++ to enable this.
++
++IPSEC: Authentication Header
++CONFIG_IPSEC_AH
++ This option provides support for the IPSEC Authentication Header
++ (IP protocol 51) which provides packet layer sender and content
++ authentication. It is recommended to enable this. RFC2402
++
++HMAC-MD5 algorithm
++CONFIG_IPSEC_AUTH_HMAC_MD5
++ Provides support for authentication using the HMAC MD5
++ algorithm with 96 bits of hash used as the authenticator. RFC2403
++
++HMAC-SHA1 algorithm
++CONFIG_IPSEC_AUTH_HMAC_SHA1
++ Provides support for Authentication Header using the HMAC SHA1
++ algorithm with 96 bits of hash used as the authenticator. RFC2404
++
++IPSEC: Encapsulating Security Payload
++CONFIG_IPSEC_ESP
++ This option provides support for the IPSEC Encapsulation Security
++ Payload (IP protocol 50) which provides packet layer content
++ hiding. It is recommended to enable this. RFC2406
++
++3DES algorithm
++CONFIG_IPSEC_ENC_3DES
++ Provides support for Encapsulation Security Payload protocol, using
++ the triple DES encryption algorithm. RFC2451
++
++IPSEC Debugging Option
++CONFIG_IPSEC_DEBUG
++ Enables IPSEC kernel debugging. It is further controlled by the
++ user space utility 'klipsdebug'.
++
++IPSEC Regression Testing option
++CONFIG_IPSEC_REGRESS
++ Enables IPSEC regression testing. Creates a number of switches in
++ /proc/sys/net/ipsec which cause various failure modes in KLIPS.
++ For more details see FreeSWAN source under
++ testing/doc/regression_options.txt.
++
++#
+ # A couple of things I keep forgetting:
+ # capitalize: AppleTalk, Ethernet, DOS, DMA, FAT, FTP, Internet,
+ # Intel, IRQ, ISDN, Linux, MSDOS, NetWare, NetWinder,
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/README.openswan-2 Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,112 @@
++*
++* RCSID $Id$
++*
++
++ ****************************************
++ * IPSEC for Linux, Release 2.xx series *
++ ****************************************
++
++
++
++1. Files
++
++The contents of linux/net/ipsec/ (see below) join the linux kernel source tree.
++as provided for higher up.
++
++The programs/ directory contains the user-level utilities which you need
++to run IPSEC. See the top-level top/INSTALL to compile and install them.
++
++The testing/ directory contains test scripts.
++
++The doc/ directory contains -- what else -- documentation.
++
++1.1. Kernel files
++
++The following are found in net/ipsec/:
++
++Makefile The Makefile
++Config.in The configuration script for make menuconfig
++defconfig Configuration defaults for first time.
++
++radij.c General-purpose radix-tree operations
++
++ipsec_ipcomp.c IPCOMP encapsulate/decapsulate code.
++ipsec_ah.c Authentication Header (AH) encapsulate/decapsulate code.
++ipsec_esp.c Encapsulated Security Payload (ESP) encap/decap code.
++
++pfkey_v2.c PF_KEYv2 socket interface code.
++pfkey_v2_parser.c PF_KEYv2 message parsing and processing code.
++
++ipsec_init.c Initialization code, /proc interface.
++ipsec_radij.c Interface with the radix tree code.
++ipsec_netlink.c Interface with the netlink code.
++ipsec_xform.c Routines and structures common to transforms.
++ipsec_tunnel.c The outgoing packet processing code.
++ipsec_rcv.c The incoming packet processing code.
++ipsec_md5c.c Somewhat modified RSADSI MD5 C code.
++ipsec_sha1.c Somewhat modified Steve Reid SHA-1 C code.
++
++sysctl_net_ipsec.c /proc/sys/net/ipsec/* variable definitions.
++
++version.c symbolic link to project version.
++
++radij.h Headers for radij.c
++
++ipcomp.h Headers used by IPCOMP code.
++
++ipsec_radij.h Interface with the radix tree code.
++ipsec_netlink.h Headers used by the netlink interface.
++ipsec_encap.h Headers defining encapsulation structures.
++ipsec_xform.h Transform headers.
++ipsec_tunnel.h Headers used by tunneling code.
++ipsec_ipe4.h Headers for the IP-in-IP code.
++ipsec_ah.h Headers common to AH transforms.
++ipsec_md5h.h RSADSI MD5 headers.
++ipsec_sha1.h SHA-1 headers.
++ipsec_esp.h Headers common to ESP transfroms.
++ipsec_rcv.h Headers for incoming packet processing code.
++
++1.2. User-level files.
++
++The following are found in utils/:
++
++eroute.c Create an "extended route" source code
++spi.c Set up Security Associations source code
++spigrp.c Link SPIs together source code.
++tncfg.c Configure the tunneling features of the virtual interface
++ source code
++klipsdebug.c Set/reset klips debugging features source code.
++version.c symbolic link to project version.
++
++eroute.8 Create an "extended route" manual page
++spi.8 Set up Security Associations manual page
++spigrp.8 Link SPIs together manual page
++tncfg.8 Configure the tunneling features of the virtual interface
++ manual page
++klipsdebug.8 Set/reset klips debugging features manual page
++
++eroute.5 /proc/net/ipsec_eroute format manual page
++spi.5 /proc/net/ipsec_spi format manual page
++spigrp.5 /proc/net/ipsec_spigrp format manual page
++tncfg.5 /proc/net/ipsec_tncfg format manual page
++klipsdebug.5 /proc/net/ipsec_klipsdebug format manual page
++version.5 /proc/net/ipsec_version format manual page
++pf_key.5 /proc/net/pf_key format manual page
++
++Makefile Utilities makefile.
++
++*.8 Manpages for the respective utils.
++
++
++1.3. Test files
++
++The test scripts are locate in testing/ and and documentation is found
++at doc/src/umltesting.html. Automated testing via "make check" is available
++provided that the User-Mode-Linux patches are available.
++
++*
++* $Log$
++* Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++* Turn off EOLN_NATIVE flag
++*
++* (Logical change 1.5010)
++*
++* Revision 1.1 2003/12/10 01:07:49 mcr
++* documentation for additions.
++*
++*
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/crypto/ciphers/aes/Makefile.objs Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,13 @@
++
++ASM-$(ARCH_ASM):=1
++ASM_X86:=$(ASM-i586)$(ASM-i686)
++
++ifneq ($(strip $(ASM_X86)),)
++obj-$(CONFIG_IPSEC_ENC_AES) += aes-i586.o
++else
++obj-$(CONFIG_IPSEC_ENC_AES) += aes.o
++endif
++
++obj-$(CONFIG_IPSEC_ENC_AES) += aes_cbc.o
++obj-$(CONFIG_IPSEC_ENC_AES) += aes_xcbc_mac.o
++
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/crypto/ciphers/aes/aes-i586.S Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,892 @@
++//
++// Copyright (c) 2001, Dr Brian Gladman <brg@gladman.uk.net>, Worcester, UK.
++// All rights reserved.
++//
++// TERMS
++//
++// Redistribution and use in source and binary forms, with or without
++// modification, are permitted subject to the following conditions:
++//
++// 1. Redistributions of source code must retain the above copyright
++// notice, this list of conditions and the following disclaimer.
++//
++// 2. Redistributions in binary form must reproduce the above copyright
++// notice, this list of conditions and the following disclaimer in the
++// documentation and/or other materials provided with the distribution.
++//
++// 3. The copyright holder's name must not be used to endorse or promote
++// any products derived from this software without his specific prior
++// written permission.
++//
++// This software is provided 'as is' with no express or implied warranties
++// of correctness or fitness for purpose.
++
++// Modified by Jari Ruusu, December 24 2001
++// - Converted syntax to GNU CPP/assembler syntax
++// - C programming interface converted back to "old" API
++// - Minor portability cleanups and speed optimizations
++
++// An AES (Rijndael) implementation for the Pentium. This version only
++// implements the standard AES block length (128 bits, 16 bytes). This code
++// does not preserve the eax, ecx or edx registers or the artihmetic status
++// flags. However, the ebx, esi, edi, and ebp registers are preserved across
++// calls.
++
++// void aes_set_key(aes_context *cx, const unsigned char key[], const int key_len, const int f)
++// void aes_encrypt(const aes_context *cx, const unsigned char in_blk[], unsigned char out_blk[])
++// void aes_decrypt(const aes_context *cx, const unsigned char in_blk[], unsigned char out_blk[])
++
++#if defined(USE_UNDERLINE)
++# define aes_set_key _aes_set_key
++# define aes_encrypt _aes_encrypt
++# define aes_decrypt _aes_decrypt
++#endif
++#if !defined(ALIGN32BYTES)
++# define ALIGN32BYTES 32
++#endif
++
++ .file "aes-i586.S"
++ .globl aes_set_key
++ .globl aes_encrypt
++ .globl aes_decrypt
++
++#define tlen 1024 // length of each of 4 'xor' arrays (256 32-bit words)
++
++// offsets to parameters with one register pushed onto stack
++
++#define ctx 8 // AES context structure
++#define in_blk 12 // input byte array address parameter
++#define out_blk 16 // output byte array address parameter
++
++// offsets in context structure
++
++#define nkey 0 // key length, size 4
++#define nrnd 4 // number of rounds, size 4
++#define ekey 8 // encryption key schedule base address, size 256
++#define dkey 264 // decryption key schedule base address, size 256
++
++// This macro performs a forward encryption cycle. It is entered with
++// the first previous round column values in %eax, %ebx, %esi and %edi and
++// exits with the final values in the same registers.
++
++#define fwd_rnd(p1,p2) \
++ mov %ebx,(%esp) ;\
++ movzbl %al,%edx ;\
++ mov %eax,%ecx ;\
++ mov p2(%ebp),%eax ;\
++ mov %edi,4(%esp) ;\
++ mov p2+12(%ebp),%edi ;\
++ xor p1(,%edx,4),%eax ;\
++ movzbl %ch,%edx ;\
++ shr $16,%ecx ;\
++ mov p2+4(%ebp),%ebx ;\
++ xor p1+tlen(,%edx,4),%edi ;\
++ movzbl %cl,%edx ;\
++ movzbl %ch,%ecx ;\
++ xor p1+3*tlen(,%ecx,4),%ebx ;\
++ mov %esi,%ecx ;\
++ mov p1+2*tlen(,%edx,4),%esi ;\
++ movzbl %cl,%edx ;\
++ xor p1(,%edx,4),%esi ;\
++ movzbl %ch,%edx ;\
++ shr $16,%ecx ;\
++ xor p1+tlen(,%edx,4),%ebx ;\
++ movzbl %cl,%edx ;\
++ movzbl %ch,%ecx ;\
++ xor p1+2*tlen(,%edx,4),%eax ;\
++ mov (%esp),%edx ;\
++ xor p1+3*tlen(,%ecx,4),%edi ;\
++ movzbl %dl,%ecx ;\
++ xor p2+8(%ebp),%esi ;\
++ xor p1(,%ecx,4),%ebx ;\
++ movzbl %dh,%ecx ;\
++ shr $16,%edx ;\
++ xor p1+tlen(,%ecx,4),%eax ;\
++ movzbl %dl,%ecx ;\
++ movzbl %dh,%edx ;\
++ xor p1+2*tlen(,%ecx,4),%edi ;\
++ mov 4(%esp),%ecx ;\
++ xor p1+3*tlen(,%edx,4),%esi ;\
++ movzbl %cl,%edx ;\
++ xor p1(,%edx,4),%edi ;\
++ movzbl %ch,%edx ;\
++ shr $16,%ecx ;\
++ xor p1+tlen(,%edx,4),%esi ;\
++ movzbl %cl,%edx ;\
++ movzbl %ch,%ecx ;\
++ xor p1+2*tlen(,%edx,4),%ebx ;\
++ xor p1+3*tlen(,%ecx,4),%eax
++
++// This macro performs an inverse encryption cycle. It is entered with
++// the first previous round column values in %eax, %ebx, %esi and %edi and
++// exits with the final values in the same registers.
++
++#define inv_rnd(p1,p2) \
++ movzbl %al,%edx ;\
++ mov %ebx,(%esp) ;\
++ mov %eax,%ecx ;\
++ mov p2(%ebp),%eax ;\
++ mov %edi,4(%esp) ;\
++ mov p2+4(%ebp),%ebx ;\
++ xor p1(,%edx,4),%eax ;\
++ movzbl %ch,%edx ;\
++ shr $16,%ecx ;\
++ mov p2+12(%ebp),%edi ;\
++ xor p1+tlen(,%edx,4),%ebx ;\
++ movzbl %cl,%edx ;\
++ movzbl %ch,%ecx ;\
++ xor p1+3*tlen(,%ecx,4),%edi ;\
++ mov %esi,%ecx ;\
++ mov p1+2*tlen(,%edx,4),%esi ;\
++ movzbl %cl,%edx ;\
++ xor p1(,%edx,4),%esi ;\
++ movzbl %ch,%edx ;\
++ shr $16,%ecx ;\
++ xor p1+tlen(,%edx,4),%edi ;\
++ movzbl %cl,%edx ;\
++ movzbl %ch,%ecx ;\
++ xor p1+2*tlen(,%edx,4),%eax ;\
++ mov (%esp),%edx ;\
++ xor p1+3*tlen(,%ecx,4),%ebx ;\
++ movzbl %dl,%ecx ;\
++ xor p2+8(%ebp),%esi ;\
++ xor p1(,%ecx,4),%ebx ;\
++ movzbl %dh,%ecx ;\
++ shr $16,%edx ;\
++ xor p1+tlen(,%ecx,4),%esi ;\
++ movzbl %dl,%ecx ;\
++ movzbl %dh,%edx ;\
++ xor p1+2*tlen(,%ecx,4),%edi ;\
++ mov 4(%esp),%ecx ;\
++ xor p1+3*tlen(,%edx,4),%eax ;\
++ movzbl %cl,%edx ;\
++ xor p1(,%edx,4),%edi ;\
++ movzbl %ch,%edx ;\
++ shr $16,%ecx ;\
++ xor p1+tlen(,%edx,4),%eax ;\
++ movzbl %cl,%edx ;\
++ movzbl %ch,%ecx ;\
++ xor p1+2*tlen(,%edx,4),%ebx ;\
++ xor p1+3*tlen(,%ecx,4),%esi
++
++// AES (Rijndael) Encryption Subroutine
++
++ .text
++ .align ALIGN32BYTES
++aes_encrypt:
++ push %ebp
++ mov ctx(%esp),%ebp // pointer to context
++ mov in_blk(%esp),%ecx
++ push %ebx
++ push %esi
++ push %edi
++ mov nrnd(%ebp),%edx // number of rounds
++ lea ekey+16(%ebp),%ebp // key pointer
++
++// input four columns and xor in first round key
++
++ mov (%ecx),%eax
++ mov 4(%ecx),%ebx
++ mov 8(%ecx),%esi
++ mov 12(%ecx),%edi
++ xor -16(%ebp),%eax
++ xor -12(%ebp),%ebx
++ xor -8(%ebp),%esi
++ xor -4(%ebp),%edi
++
++ sub $8,%esp // space for register saves on stack
++
++ sub $10,%edx
++ je aes_15
++ add $32,%ebp
++ sub $2,%edx
++ je aes_13
++ add $32,%ebp
++
++ fwd_rnd(aes_ft_tab,-64) // 14 rounds for 256-bit key
++ fwd_rnd(aes_ft_tab,-48)
++aes_13: fwd_rnd(aes_ft_tab,-32) // 12 rounds for 192-bit key
++ fwd_rnd(aes_ft_tab,-16)
++aes_15: fwd_rnd(aes_ft_tab,0) // 10 rounds for 128-bit key
++ fwd_rnd(aes_ft_tab,16)
++ fwd_rnd(aes_ft_tab,32)
++ fwd_rnd(aes_ft_tab,48)
++ fwd_rnd(aes_ft_tab,64)
++ fwd_rnd(aes_ft_tab,80)
++ fwd_rnd(aes_ft_tab,96)
++ fwd_rnd(aes_ft_tab,112)
++ fwd_rnd(aes_ft_tab,128)
++ fwd_rnd(aes_fl_tab,144) // last round uses a different table
++
++// move final values to the output array.
++
++ mov out_blk+20(%esp),%ebp
++ add $8,%esp
++ mov %eax,(%ebp)
++ mov %ebx,4(%ebp)
++ mov %esi,8(%ebp)
++ mov %edi,12(%ebp)
++ pop %edi
++ pop %esi
++ pop %ebx
++ pop %ebp
++ ret
++
++
++// AES (Rijndael) Decryption Subroutine
++
++ .align ALIGN32BYTES
++aes_decrypt:
++ push %ebp
++ mov ctx(%esp),%ebp // pointer to context
++ mov in_blk(%esp),%ecx
++ push %ebx
++ push %esi
++ push %edi
++ mov nrnd(%ebp),%edx // number of rounds
++ lea dkey+16(%ebp),%ebp // key pointer
++
++// input four columns and xor in first round key
++
++ mov (%ecx),%eax
++ mov 4(%ecx),%ebx
++ mov 8(%ecx),%esi
++ mov 12(%ecx),%edi
++ xor -16(%ebp),%eax
++ xor -12(%ebp),%ebx
++ xor -8(%ebp),%esi
++ xor -4(%ebp),%edi
++
++ sub $8,%esp // space for register saves on stack
++
++ sub $10,%edx
++ je aes_25
++ add $32,%ebp
++ sub $2,%edx
++ je aes_23
++ add $32,%ebp
++
++ inv_rnd(aes_it_tab,-64) // 14 rounds for 256-bit key
++ inv_rnd(aes_it_tab,-48)
++aes_23: inv_rnd(aes_it_tab,-32) // 12 rounds for 192-bit key
++ inv_rnd(aes_it_tab,-16)
++aes_25: inv_rnd(aes_it_tab,0) // 10 rounds for 128-bit key
++ inv_rnd(aes_it_tab,16)
++ inv_rnd(aes_it_tab,32)
++ inv_rnd(aes_it_tab,48)
++ inv_rnd(aes_it_tab,64)
++ inv_rnd(aes_it_tab,80)
++ inv_rnd(aes_it_tab,96)
++ inv_rnd(aes_it_tab,112)
++ inv_rnd(aes_it_tab,128)
++ inv_rnd(aes_il_tab,144) // last round uses a different table
++
++// move final values to the output array.
++
++ mov out_blk+20(%esp),%ebp
++ add $8,%esp
++ mov %eax,(%ebp)
++ mov %ebx,4(%ebp)
++ mov %esi,8(%ebp)
++ mov %edi,12(%ebp)
++ pop %edi
++ pop %esi
++ pop %ebx
++ pop %ebp
++ ret
++
++// AES (Rijndael) Key Schedule Subroutine
++
++// input/output parameters
++
++#define aes_cx 12 // AES context
++#define in_key 16 // key input array address
++#define key_ln 20 // key length, bytes (16,24,32) or bits (128,192,256)
++#define ed_flg 24 // 0=create both encr/decr keys, 1=create encr key only
++
++// offsets for locals
++
++#define cnt -4
++#define kpf -8
++#define slen 8
++
++// This macro performs a column mixing operation on an input 32-bit
++// word to give a 32-bit result. It uses each of the 4 bytes in the
++// the input column to index 4 different tables of 256 32-bit words
++// that are xored together to form the output value.
++
++#define mix_col(p1) \
++ movzbl %bl,%ecx ;\
++ mov p1(,%ecx,4),%eax ;\
++ movzbl %bh,%ecx ;\
++ ror $16,%ebx ;\
++ xor p1+tlen(,%ecx,4),%eax ;\
++ movzbl %bl,%ecx ;\
++ xor p1+2*tlen(,%ecx,4),%eax ;\
++ movzbl %bh,%ecx ;\
++ xor p1+3*tlen(,%ecx,4),%eax
++
++// Key Schedule Macros
++
++#define ksc4(p1) \
++ rol $24,%ebx ;\
++ mix_col(aes_fl_tab) ;\
++ ror $8,%ebx ;\
++ xor 4*p1+aes_rcon_tab,%eax ;\
++ xor %eax,%esi ;\
++ xor %esi,%ebp ;\
++ mov %esi,16*p1(%edi) ;\
++ mov %ebp,16*p1+4(%edi) ;\
++ xor %ebp,%edx ;\
++ xor %edx,%ebx ;\
++ mov %edx,16*p1+8(%edi) ;\
++ mov %ebx,16*p1+12(%edi)
++
++#define ksc6(p1) \
++ rol $24,%ebx ;\
++ mix_col(aes_fl_tab) ;\
++ ror $8,%ebx ;\
++ xor 4*p1+aes_rcon_tab,%eax ;\
++ xor 24*p1-24(%edi),%eax ;\
++ mov %eax,24*p1(%edi) ;\
++ xor 24*p1-20(%edi),%eax ;\
++ mov %eax,24*p1+4(%edi) ;\
++ xor %eax,%esi ;\
++ xor %esi,%ebp ;\
++ mov %esi,24*p1+8(%edi) ;\
++ mov %ebp,24*p1+12(%edi) ;\
++ xor %ebp,%edx ;\
++ xor %edx,%ebx ;\
++ mov %edx,24*p1+16(%edi) ;\
++ mov %ebx,24*p1+20(%edi)
++
++#define ksc8(p1) \
++ rol $24,%ebx ;\
++ mix_col(aes_fl_tab) ;\
++ ror $8,%ebx ;\
++ xor 4*p1+aes_rcon_tab,%eax ;\
++ xor 32*p1-32(%edi),%eax ;\
++ mov %eax,32*p1(%edi) ;\
++ xor 32*p1-28(%edi),%eax ;\
++ mov %eax,32*p1+4(%edi) ;\
++ xor 32*p1-24(%edi),%eax ;\
++ mov %eax,32*p1+8(%edi) ;\
++ xor 32*p1-20(%edi),%eax ;\
++ mov %eax,32*p1+12(%edi) ;\
++ push %ebx ;\
++ mov %eax,%ebx ;\
++ mix_col(aes_fl_tab) ;\
++ pop %ebx ;\
++ xor %eax,%esi ;\
++ xor %esi,%ebp ;\
++ mov %esi,32*p1+16(%edi) ;\
++ mov %ebp,32*p1+20(%edi) ;\
++ xor %ebp,%edx ;\
++ xor %edx,%ebx ;\
++ mov %edx,32*p1+24(%edi) ;\
++ mov %ebx,32*p1+28(%edi)
++
++ .align ALIGN32BYTES
++aes_set_key:
++ pushfl
++ push %ebp
++ mov %esp,%ebp
++ sub $slen,%esp
++ push %ebx
++ push %esi
++ push %edi
++
++ mov aes_cx(%ebp),%edx // edx -> AES context
++
++ mov key_ln(%ebp),%ecx // key length
++ cmpl $128,%ecx
++ jb aes_30
++ shr $3,%ecx
++aes_30: cmpl $32,%ecx
++ je aes_32
++ cmpl $24,%ecx
++ je aes_32
++ mov $16,%ecx
++aes_32: shr $2,%ecx
++ mov %ecx,nkey(%edx)
++
++ lea 6(%ecx),%eax // 10/12/14 for 4/6/8 32-bit key length
++ mov %eax,nrnd(%edx)
++
++ mov in_key(%ebp),%esi // key input array
++ lea ekey(%edx),%edi // key position in AES context
++ cld
++ push %ebp
++ mov %ecx,%eax // save key length in eax
++ rep ; movsl // words in the key schedule
++ mov -4(%esi),%ebx // put some values in registers
++ mov -8(%esi),%edx // to allow faster code
++ mov -12(%esi),%ebp
++ mov -16(%esi),%esi
++
++ cmpl $4,%eax // jump on key size
++ je aes_36
++ cmpl $6,%eax
++ je aes_35
++
++ ksc8(0)
++ ksc8(1)
++ ksc8(2)
++ ksc8(3)
++ ksc8(4)
++ ksc8(5)
++ ksc8(6)
++ jmp aes_37
++aes_35: ksc6(0)
++ ksc6(1)
++ ksc6(2)
++ ksc6(3)
++ ksc6(4)
++ ksc6(5)
++ ksc6(6)
++ ksc6(7)
++ jmp aes_37
++aes_36: ksc4(0)
++ ksc4(1)
++ ksc4(2)
++ ksc4(3)
++ ksc4(4)
++ ksc4(5)
++ ksc4(6)
++ ksc4(7)
++ ksc4(8)
++ ksc4(9)
++aes_37: pop %ebp
++ mov aes_cx(%ebp),%edx // edx -> AES context
++ cmpl $0,ed_flg(%ebp)
++ jne aes_39
++
++// compile decryption key schedule from encryption schedule - reverse
++// order and do mix_column operation on round keys except first and last
++
++ mov nrnd(%edx),%eax // kt = cx->d_key + nc * cx->Nrnd
++ shl $2,%eax
++ lea dkey(%edx,%eax,4),%edi
++ lea ekey(%edx),%esi // kf = cx->e_key
++
++ movsl // copy first round key (unmodified)
++ movsl
++ movsl
++ movsl
++ sub $32,%edi
++ movl $1,cnt(%ebp)
++aes_38: // do mix column on each column of
++ lodsl // each round key
++ mov %eax,%ebx
++ mix_col(aes_im_tab)
++ stosl
++ lodsl
++ mov %eax,%ebx
++ mix_col(aes_im_tab)
++ stosl
++ lodsl
++ mov %eax,%ebx
++ mix_col(aes_im_tab)
++ stosl
++ lodsl
++ mov %eax,%ebx
++ mix_col(aes_im_tab)
++ stosl
++ sub $32,%edi
++
++ incl cnt(%ebp)
++ mov cnt(%ebp),%eax
++ cmp nrnd(%edx),%eax
++ jb aes_38
++
++ movsl // copy last round key (unmodified)
++ movsl
++ movsl
++ movsl
++aes_39: pop %edi
++ pop %esi
++ pop %ebx
++ mov %ebp,%esp
++ pop %ebp
++ popfl
++ ret
++
++
++// finite field multiplies by {02}, {04} and {08}
++
++#define f2(x) ((x<<1)^(((x>>7)&1)*0x11b))
++#define f4(x) ((x<<2)^(((x>>6)&1)*0x11b)^(((x>>6)&2)*0x11b))
++#define f8(x) ((x<<3)^(((x>>5)&1)*0x11b)^(((x>>5)&2)*0x11b)^(((x>>5)&4)*0x11b))
++
++// finite field multiplies required in table generation
++
++#define f3(x) (f2(x) ^ x)
++#define f9(x) (f8(x) ^ x)
++#define fb(x) (f8(x) ^ f2(x) ^ x)
++#define fd(x) (f8(x) ^ f4(x) ^ x)
++#define fe(x) (f8(x) ^ f4(x) ^ f2(x))
++
++// These defines generate the forward table entries
++
++#define u0(x) ((f3(x) << 24) | (x << 16) | (x << 8) | f2(x))
++#define u1(x) ((x << 24) | (x << 16) | (f2(x) << 8) | f3(x))
++#define u2(x) ((x << 24) | (f2(x) << 16) | (f3(x) << 8) | x)
++#define u3(x) ((f2(x) << 24) | (f3(x) << 16) | (x << 8) | x)
++
++// These defines generate the inverse table entries
++
++#define v0(x) ((fb(x) << 24) | (fd(x) << 16) | (f9(x) << 8) | fe(x))
++#define v1(x) ((fd(x) << 24) | (f9(x) << 16) | (fe(x) << 8) | fb(x))
++#define v2(x) ((f9(x) << 24) | (fe(x) << 16) | (fb(x) << 8) | fd(x))
++#define v3(x) ((fe(x) << 24) | (fb(x) << 16) | (fd(x) << 8) | f9(x))
++
++// These defines generate entries for the last round tables
++
++#define w0(x) (x)
++#define w1(x) (x << 8)
++#define w2(x) (x << 16)
++#define w3(x) (x << 24)
++
++// macro to generate inverse mix column tables (needed for the key schedule)
++
++#define im_data0(p1) \
++ .long p1(0x00),p1(0x01),p1(0x02),p1(0x03),p1(0x04),p1(0x05),p1(0x06),p1(0x07) ;\
++ .long p1(0x08),p1(0x09),p1(0x0a),p1(0x0b),p1(0x0c),p1(0x0d),p1(0x0e),p1(0x0f) ;\
++ .long p1(0x10),p1(0x11),p1(0x12),p1(0x13),p1(0x14),p1(0x15),p1(0x16),p1(0x17) ;\
++ .long p1(0x18),p1(0x19),p1(0x1a),p1(0x1b),p1(0x1c),p1(0x1d),p1(0x1e),p1(0x1f)
++#define im_data1(p1) \
++ .long p1(0x20),p1(0x21),p1(0x22),p1(0x23),p1(0x24),p1(0x25),p1(0x26),p1(0x27) ;\
++ .long p1(0x28),p1(0x29),p1(0x2a),p1(0x2b),p1(0x2c),p1(0x2d),p1(0x2e),p1(0x2f) ;\
++ .long p1(0x30),p1(0x31),p1(0x32),p1(0x33),p1(0x34),p1(0x35),p1(0x36),p1(0x37) ;\
++ .long p1(0x38),p1(0x39),p1(0x3a),p1(0x3b),p1(0x3c),p1(0x3d),p1(0x3e),p1(0x3f)
++#define im_data2(p1) \
++ .long p1(0x40),p1(0x41),p1(0x42),p1(0x43),p1(0x44),p1(0x45),p1(0x46),p1(0x47) ;\
++ .long p1(0x48),p1(0x49),p1(0x4a),p1(0x4b),p1(0x4c),p1(0x4d),p1(0x4e),p1(0x4f) ;\
++ .long p1(0x50),p1(0x51),p1(0x52),p1(0x53),p1(0x54),p1(0x55),p1(0x56),p1(0x57) ;\
++ .long p1(0x58),p1(0x59),p1(0x5a),p1(0x5b),p1(0x5c),p1(0x5d),p1(0x5e),p1(0x5f)
++#define im_data3(p1) \
++ .long p1(0x60),p1(0x61),p1(0x62),p1(0x63),p1(0x64),p1(0x65),p1(0x66),p1(0x67) ;\
++ .long p1(0x68),p1(0x69),p1(0x6a),p1(0x6b),p1(0x6c),p1(0x6d),p1(0x6e),p1(0x6f) ;\
++ .long p1(0x70),p1(0x71),p1(0x72),p1(0x73),p1(0x74),p1(0x75),p1(0x76),p1(0x77) ;\
++ .long p1(0x78),p1(0x79),p1(0x7a),p1(0x7b),p1(0x7c),p1(0x7d),p1(0x7e),p1(0x7f)
++#define im_data4(p1) \
++ .long p1(0x80),p1(0x81),p1(0x82),p1(0x83),p1(0x84),p1(0x85),p1(0x86),p1(0x87) ;\
++ .long p1(0x88),p1(0x89),p1(0x8a),p1(0x8b),p1(0x8c),p1(0x8d),p1(0x8e),p1(0x8f) ;\
++ .long p1(0x90),p1(0x91),p1(0x92),p1(0x93),p1(0x94),p1(0x95),p1(0x96),p1(0x97) ;\
++ .long p1(0x98),p1(0x99),p1(0x9a),p1(0x9b),p1(0x9c),p1(0x9d),p1(0x9e),p1(0x9f)
++#define im_data5(p1) \
++ .long p1(0xa0),p1(0xa1),p1(0xa2),p1(0xa3),p1(0xa4),p1(0xa5),p1(0xa6),p1(0xa7) ;\
++ .long p1(0xa8),p1(0xa9),p1(0xaa),p1(0xab),p1(0xac),p1(0xad),p1(0xae),p1(0xaf) ;\
++ .long p1(0xb0),p1(0xb1),p1(0xb2),p1(0xb3),p1(0xb4),p1(0xb5),p1(0xb6),p1(0xb7) ;\
++ .long p1(0xb8),p1(0xb9),p1(0xba),p1(0xbb),p1(0xbc),p1(0xbd),p1(0xbe),p1(0xbf)
++#define im_data6(p1) \
++ .long p1(0xc0),p1(0xc1),p1(0xc2),p1(0xc3),p1(0xc4),p1(0xc5),p1(0xc6),p1(0xc7) ;\
++ .long p1(0xc8),p1(0xc9),p1(0xca),p1(0xcb),p1(0xcc),p1(0xcd),p1(0xce),p1(0xcf) ;\
++ .long p1(0xd0),p1(0xd1),p1(0xd2),p1(0xd3),p1(0xd4),p1(0xd5),p1(0xd6),p1(0xd7) ;\
++ .long p1(0xd8),p1(0xd9),p1(0xda),p1(0xdb),p1(0xdc),p1(0xdd),p1(0xde),p1(0xdf)
++#define im_data7(p1) \
++ .long p1(0xe0),p1(0xe1),p1(0xe2),p1(0xe3),p1(0xe4),p1(0xe5),p1(0xe6),p1(0xe7) ;\
++ .long p1(0xe8),p1(0xe9),p1(0xea),p1(0xeb),p1(0xec),p1(0xed),p1(0xee),p1(0xef) ;\
++ .long p1(0xf0),p1(0xf1),p1(0xf2),p1(0xf3),p1(0xf4),p1(0xf5),p1(0xf6),p1(0xf7) ;\
++ .long p1(0xf8),p1(0xf9),p1(0xfa),p1(0xfb),p1(0xfc),p1(0xfd),p1(0xfe),p1(0xff)
++
++// S-box data - 256 entries
++
++#define sb_data0(p1) \
++ .long p1(0x63),p1(0x7c),p1(0x77),p1(0x7b),p1(0xf2),p1(0x6b),p1(0x6f),p1(0xc5) ;\
++ .long p1(0x30),p1(0x01),p1(0x67),p1(0x2b),p1(0xfe),p1(0xd7),p1(0xab),p1(0x76) ;\
++ .long p1(0xca),p1(0x82),p1(0xc9),p1(0x7d),p1(0xfa),p1(0x59),p1(0x47),p1(0xf0) ;\
++ .long p1(0xad),p1(0xd4),p1(0xa2),p1(0xaf),p1(0x9c),p1(0xa4),p1(0x72),p1(0xc0)
++#define sb_data1(p1) \
++ .long p1(0xb7),p1(0xfd),p1(0x93),p1(0x26),p1(0x36),p1(0x3f),p1(0xf7),p1(0xcc) ;\
++ .long p1(0x34),p1(0xa5),p1(0xe5),p1(0xf1),p1(0x71),p1(0xd8),p1(0x31),p1(0x15) ;\
++ .long p1(0x04),p1(0xc7),p1(0x23),p1(0xc3),p1(0x18),p1(0x96),p1(0x05),p1(0x9a) ;\
++ .long p1(0x07),p1(0x12),p1(0x80),p1(0xe2),p1(0xeb),p1(0x27),p1(0xb2),p1(0x75)
++#define sb_data2(p1) \
++ .long p1(0x09),p1(0x83),p1(0x2c),p1(0x1a),p1(0x1b),p1(0x6e),p1(0x5a),p1(0xa0) ;\
++ .long p1(0x52),p1(0x3b),p1(0xd6),p1(0xb3),p1(0x29),p1(0xe3),p1(0x2f),p1(0x84) ;\
++ .long p1(0x53),p1(0xd1),p1(0x00),p1(0xed),p1(0x20),p1(0xfc),p1(0xb1),p1(0x5b) ;\
++ .long p1(0x6a),p1(0xcb),p1(0xbe),p1(0x39),p1(0x4a),p1(0x4c),p1(0x58),p1(0xcf)
++#define sb_data3(p1) \
++ .long p1(0xd0),p1(0xef),p1(0xaa),p1(0xfb),p1(0x43),p1(0x4d),p1(0x33),p1(0x85) ;\
++ .long p1(0x45),p1(0xf9),p1(0x02),p1(0x7f),p1(0x50),p1(0x3c),p1(0x9f),p1(0xa8) ;\
++ .long p1(0x51),p1(0xa3),p1(0x40),p1(0x8f),p1(0x92),p1(0x9d),p1(0x38),p1(0xf5) ;\
++ .long p1(0xbc),p1(0xb6),p1(0xda),p1(0x21),p1(0x10),p1(0xff),p1(0xf3),p1(0xd2)
++#define sb_data4(p1) \
++ .long p1(0xcd),p1(0x0c),p1(0x13),p1(0xec),p1(0x5f),p1(0x97),p1(0x44),p1(0x17) ;\
++ .long p1(0xc4),p1(0xa7),p1(0x7e),p1(0x3d),p1(0x64),p1(0x5d),p1(0x19),p1(0x73) ;\
++ .long p1(0x60),p1(0x81),p1(0x4f),p1(0xdc),p1(0x22),p1(0x2a),p1(0x90),p1(0x88) ;\
++ .long p1(0x46),p1(0xee),p1(0xb8),p1(0x14),p1(0xde),p1(0x5e),p1(0x0b),p1(0xdb)
++#define sb_data5(p1) \
++ .long p1(0xe0),p1(0x32),p1(0x3a),p1(0x0a),p1(0x49),p1(0x06),p1(0x24),p1(0x5c) ;\
++ .long p1(0xc2),p1(0xd3),p1(0xac),p1(0x62),p1(0x91),p1(0x95),p1(0xe4),p1(0x79) ;\
++ .long p1(0xe7),p1(0xc8),p1(0x37),p1(0x6d),p1(0x8d),p1(0xd5),p1(0x4e),p1(0xa9) ;\
++ .long p1(0x6c),p1(0x56),p1(0xf4),p1(0xea),p1(0x65),p1(0x7a),p1(0xae),p1(0x08)
++#define sb_data6(p1) \
++ .long p1(0xba),p1(0x78),p1(0x25),p1(0x2e),p1(0x1c),p1(0xa6),p1(0xb4),p1(0xc6) ;\
++ .long p1(0xe8),p1(0xdd),p1(0x74),p1(0x1f),p1(0x4b),p1(0xbd),p1(0x8b),p1(0x8a) ;\
++ .long p1(0x70),p1(0x3e),p1(0xb5),p1(0x66),p1(0x48),p1(0x03),p1(0xf6),p1(0x0e) ;\
++ .long p1(0x61),p1(0x35),p1(0x57),p1(0xb9),p1(0x86),p1(0xc1),p1(0x1d),p1(0x9e)
++#define sb_data7(p1) \
++ .long p1(0xe1),p1(0xf8),p1(0x98),p1(0x11),p1(0x69),p1(0xd9),p1(0x8e),p1(0x94) ;\
++ .long p1(0x9b),p1(0x1e),p1(0x87),p1(0xe9),p1(0xce),p1(0x55),p1(0x28),p1(0xdf) ;\
++ .long p1(0x8c),p1(0xa1),p1(0x89),p1(0x0d),p1(0xbf),p1(0xe6),p1(0x42),p1(0x68) ;\
++ .long p1(0x41),p1(0x99),p1(0x2d),p1(0x0f),p1(0xb0),p1(0x54),p1(0xbb),p1(0x16)
++
++// Inverse S-box data - 256 entries
++
++#define ib_data0(p1) \
++ .long p1(0x52),p1(0x09),p1(0x6a),p1(0xd5),p1(0x30),p1(0x36),p1(0xa5),p1(0x38) ;\
++ .long p1(0xbf),p1(0x40),p1(0xa3),p1(0x9e),p1(0x81),p1(0xf3),p1(0xd7),p1(0xfb) ;\
++ .long p1(0x7c),p1(0xe3),p1(0x39),p1(0x82),p1(0x9b),p1(0x2f),p1(0xff),p1(0x87) ;\
++ .long p1(0x34),p1(0x8e),p1(0x43),p1(0x44),p1(0xc4),p1(0xde),p1(0xe9),p1(0xcb)
++#define ib_data1(p1) \
++ .long p1(0x54),p1(0x7b),p1(0x94),p1(0x32),p1(0xa6),p1(0xc2),p1(0x23),p1(0x3d) ;\
++ .long p1(0xee),p1(0x4c),p1(0x95),p1(0x0b),p1(0x42),p1(0xfa),p1(0xc3),p1(0x4e) ;\
++ .long p1(0x08),p1(0x2e),p1(0xa1),p1(0x66),p1(0x28),p1(0xd9),p1(0x24),p1(0xb2) ;\
++ .long p1(0x76),p1(0x5b),p1(0xa2),p1(0x49),p1(0x6d),p1(0x8b),p1(0xd1),p1(0x25)
++#define ib_data2(p1) \
++ .long p1(0x72),p1(0xf8),p1(0xf6),p1(0x64),p1(0x86),p1(0x68),p1(0x98),p1(0x16) ;\
++ .long p1(0xd4),p1(0xa4),p1(0x5c),p1(0xcc),p1(0x5d),p1(0x65),p1(0xb6),p1(0x92) ;\
++ .long p1(0x6c),p1(0x70),p1(0x48),p1(0x50),p1(0xfd),p1(0xed),p1(0xb9),p1(0xda) ;\
++ .long p1(0x5e),p1(0x15),p1(0x46),p1(0x57),p1(0xa7),p1(0x8d),p1(0x9d),p1(0x84)
++#define ib_data3(p1) \
++ .long p1(0x90),p1(0xd8),p1(0xab),p1(0x00),p1(0x8c),p1(0xbc),p1(0xd3),p1(0x0a) ;\
++ .long p1(0xf7),p1(0xe4),p1(0x58),p1(0x05),p1(0xb8),p1(0xb3),p1(0x45),p1(0x06) ;\
++ .long p1(0xd0),p1(0x2c),p1(0x1e),p1(0x8f),p1(0xca),p1(0x3f),p1(0x0f),p1(0x02) ;\
++ .long p1(0xc1),p1(0xaf),p1(0xbd),p1(0x03),p1(0x01),p1(0x13),p1(0x8a),p1(0x6b)
++#define ib_data4(p1) \
++ .long p1(0x3a),p1(0x91),p1(0x11),p1(0x41),p1(0x4f),p1(0x67),p1(0xdc),p1(0xea) ;\
++ .long p1(0x97),p1(0xf2),p1(0xcf),p1(0xce),p1(0xf0),p1(0xb4),p1(0xe6),p1(0x73) ;\
++ .long p1(0x96),p1(0xac),p1(0x74),p1(0x22),p1(0xe7),p1(0xad),p1(0x35),p1(0x85) ;\
++ .long p1(0xe2),p1(0xf9),p1(0x37),p1(0xe8),p1(0x1c),p1(0x75),p1(0xdf),p1(0x6e)
++#define ib_data5(p1) \
++ .long p1(0x47),p1(0xf1),p1(0x1a),p1(0x71),p1(0x1d),p1(0x29),p1(0xc5),p1(0x89) ;\
++ .long p1(0x6f),p1(0xb7),p1(0x62),p1(0x0e),p1(0xaa),p1(0x18),p1(0xbe),p1(0x1b) ;\
++ .long p1(0xfc),p1(0x56),p1(0x3e),p1(0x4b),p1(0xc6),p1(0xd2),p1(0x79),p1(0x20) ;\
++ .long p1(0x9a),p1(0xdb),p1(0xc0),p1(0xfe),p1(0x78),p1(0xcd),p1(0x5a),p1(0xf4)
++#define ib_data6(p1) \
++ .long p1(0x1f),p1(0xdd),p1(0xa8),p1(0x33),p1(0x88),p1(0x07),p1(0xc7),p1(0x31) ;\
++ .long p1(0xb1),p1(0x12),p1(0x10),p1(0x59),p1(0x27),p1(0x80),p1(0xec),p1(0x5f) ;\
++ .long p1(0x60),p1(0x51),p1(0x7f),p1(0xa9),p1(0x19),p1(0xb5),p1(0x4a),p1(0x0d) ;\
++ .long p1(0x2d),p1(0xe5),p1(0x7a),p1(0x9f),p1(0x93),p1(0xc9),p1(0x9c),p1(0xef)
++#define ib_data7(p1) \
++ .long p1(0xa0),p1(0xe0),p1(0x3b),p1(0x4d),p1(0xae),p1(0x2a),p1(0xf5),p1(0xb0) ;\
++ .long p1(0xc8),p1(0xeb),p1(0xbb),p1(0x3c),p1(0x83),p1(0x53),p1(0x99),p1(0x61) ;\
++ .long p1(0x17),p1(0x2b),p1(0x04),p1(0x7e),p1(0xba),p1(0x77),p1(0xd6),p1(0x26) ;\
++ .long p1(0xe1),p1(0x69),p1(0x14),p1(0x63),p1(0x55),p1(0x21),p1(0x0c),p1(0x7d)
++
++// The rcon_table (needed for the key schedule)
++//
++// Here is original Dr Brian Gladman's source code:
++// _rcon_tab:
++// %assign x 1
++// %rep 29
++// dd x
++// %assign x f2(x)
++// %endrep
++//
++// Here is precomputed output (it's more portable this way):
++
++ .align ALIGN32BYTES
++aes_rcon_tab:
++ .long 0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80
++ .long 0x1b,0x36,0x6c,0xd8,0xab,0x4d,0x9a,0x2f
++ .long 0x5e,0xbc,0x63,0xc6,0x97,0x35,0x6a,0xd4
++ .long 0xb3,0x7d,0xfa,0xef,0xc5
++
++// The forward xor tables
++
++ .align ALIGN32BYTES
++aes_ft_tab:
++ sb_data0(u0)
++ sb_data1(u0)
++ sb_data2(u0)
++ sb_data3(u0)
++ sb_data4(u0)
++ sb_data5(u0)
++ sb_data6(u0)
++ sb_data7(u0)
++
++ sb_data0(u1)
++ sb_data1(u1)
++ sb_data2(u1)
++ sb_data3(u1)
++ sb_data4(u1)
++ sb_data5(u1)
++ sb_data6(u1)
++ sb_data7(u1)
++
++ sb_data0(u2)
++ sb_data1(u2)
++ sb_data2(u2)
++ sb_data3(u2)
++ sb_data4(u2)
++ sb_data5(u2)
++ sb_data6(u2)
++ sb_data7(u2)
++
++ sb_data0(u3)
++ sb_data1(u3)
++ sb_data2(u3)
++ sb_data3(u3)
++ sb_data4(u3)
++ sb_data5(u3)
++ sb_data6(u3)
++ sb_data7(u3)
++
++ .align ALIGN32BYTES
++aes_fl_tab:
++ sb_data0(w0)
++ sb_data1(w0)
++ sb_data2(w0)
++ sb_data3(w0)
++ sb_data4(w0)
++ sb_data5(w0)
++ sb_data6(w0)
++ sb_data7(w0)
++
++ sb_data0(w1)
++ sb_data1(w1)
++ sb_data2(w1)
++ sb_data3(w1)
++ sb_data4(w1)
++ sb_data5(w1)
++ sb_data6(w1)
++ sb_data7(w1)
++
++ sb_data0(w2)
++ sb_data1(w2)
++ sb_data2(w2)
++ sb_data3(w2)
++ sb_data4(w2)
++ sb_data5(w2)
++ sb_data6(w2)
++ sb_data7(w2)
++
++ sb_data0(w3)
++ sb_data1(w3)
++ sb_data2(w3)
++ sb_data3(w3)
++ sb_data4(w3)
++ sb_data5(w3)
++ sb_data6(w3)
++ sb_data7(w3)
++
++// The inverse xor tables
++
++ .align ALIGN32BYTES
++aes_it_tab:
++ ib_data0(v0)
++ ib_data1(v0)
++ ib_data2(v0)
++ ib_data3(v0)
++ ib_data4(v0)
++ ib_data5(v0)
++ ib_data6(v0)
++ ib_data7(v0)
++
++ ib_data0(v1)
++ ib_data1(v1)
++ ib_data2(v1)
++ ib_data3(v1)
++ ib_data4(v1)
++ ib_data5(v1)
++ ib_data6(v1)
++ ib_data7(v1)
++
++ ib_data0(v2)
++ ib_data1(v2)
++ ib_data2(v2)
++ ib_data3(v2)
++ ib_data4(v2)
++ ib_data5(v2)
++ ib_data6(v2)
++ ib_data7(v2)
++
++ ib_data0(v3)
++ ib_data1(v3)
++ ib_data2(v3)
++ ib_data3(v3)
++ ib_data4(v3)
++ ib_data5(v3)
++ ib_data6(v3)
++ ib_data7(v3)
++
++ .align ALIGN32BYTES
++aes_il_tab:
++ ib_data0(w0)
++ ib_data1(w0)
++ ib_data2(w0)
++ ib_data3(w0)
++ ib_data4(w0)
++ ib_data5(w0)
++ ib_data6(w0)
++ ib_data7(w0)
++
++ ib_data0(w1)
++ ib_data1(w1)
++ ib_data2(w1)
++ ib_data3(w1)
++ ib_data4(w1)
++ ib_data5(w1)
++ ib_data6(w1)
++ ib_data7(w1)
++
++ ib_data0(w2)
++ ib_data1(w2)
++ ib_data2(w2)
++ ib_data3(w2)
++ ib_data4(w2)
++ ib_data5(w2)
++ ib_data6(w2)
++ ib_data7(w2)
++
++ ib_data0(w3)
++ ib_data1(w3)
++ ib_data2(w3)
++ ib_data3(w3)
++ ib_data4(w3)
++ ib_data5(w3)
++ ib_data6(w3)
++ ib_data7(w3)
++
++// The inverse mix column tables
++
++ .align ALIGN32BYTES
++aes_im_tab:
++ im_data0(v0)
++ im_data1(v0)
++ im_data2(v0)
++ im_data3(v0)
++ im_data4(v0)
++ im_data5(v0)
++ im_data6(v0)
++ im_data7(v0)
++
++ im_data0(v1)
++ im_data1(v1)
++ im_data2(v1)
++ im_data3(v1)
++ im_data4(v1)
++ im_data5(v1)
++ im_data6(v1)
++ im_data7(v1)
++
++ im_data0(v2)
++ im_data1(v2)
++ im_data2(v2)
++ im_data3(v2)
++ im_data4(v2)
++ im_data5(v2)
++ im_data6(v2)
++ im_data7(v2)
++
++ im_data0(v3)
++ im_data1(v3)
++ im_data2(v3)
++ im_data3(v3)
++ im_data4(v3)
++ im_data5(v3)
++ im_data6(v3)
++ im_data7(v3)
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/crypto/ciphers/aes/aes.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,1415 @@
++// I retain copyright in this code but I encourage its free use provided
++// that I don't carry any responsibility for the results. I am especially
++// happy to see it used in free and open source software. If you do use
++// it I would appreciate an acknowledgement of its origin in the code or
++// the product that results and I would also appreciate knowing a little
++// about the use to which it is being put. I am grateful to Frank Yellin
++// for some ideas that are used in this implementation.
++//
++// Dr B. R. Gladman <brg@gladman.uk.net> 6th April 2001.
++//
++// This is an implementation of the AES encryption algorithm (Rijndael)
++// designed by Joan Daemen and Vincent Rijmen. This version is designed
++// to provide both fixed and dynamic block and key lengths and can also
++// run with either big or little endian internal byte order (see aes.h).
++// It inputs block and key lengths in bytes with the legal values being
++// 16, 24 and 32.
++
++/*
++ * Modified by Jari Ruusu, May 1 2001
++ * - Fixed some compile warnings, code was ok but gcc warned anyway.
++ * - Changed basic types: byte -> unsigned char, word -> u_int32_t
++ * - Major name space cleanup: Names visible to outside now begin
++ * with "aes_" or "AES_". A lot of stuff moved from aes.h to aes.c
++ * - Removed C++ and DLL support as part of name space cleanup.
++ * - Eliminated unnecessary recomputation of tables. (actual bug fix)
++ * - Merged precomputed constant tables to aes.c file.
++ * - Removed data alignment restrictions for portability reasons.
++ * - Made block and key lengths accept bit count (128/192/256)
++ * as well byte count (16/24/32).
++ * - Removed all error checks. This change also eliminated the need
++ * to preinitialize the context struct to zero.
++ * - Removed some totally unused constants.
++ */
++
++#include "crypto/aes.h"
++
++// CONFIGURATION OPTIONS (see also aes.h)
++//
++// 1. Define UNROLL for full loop unrolling in encryption and decryption.
++// 2. Define PARTIAL_UNROLL to unroll two loops in encryption and decryption.
++// 3. Define FIXED_TABLES for compiled rather than dynamic tables.
++// 4. Define FF_TABLES to use tables for field multiplies and inverses.
++// Do not enable this without understanding stack space requirements.
++// 5. Define ARRAYS to use arrays to hold the local state block. If this
++// is not defined, individually declared 32-bit words are used.
++// 6. Define FAST_VARIABLE if a high speed variable block implementation
++// is needed (essentially three separate fixed block size code sequences)
++// 7. Define either ONE_TABLE or FOUR_TABLES for a fast table driven
++// version using 1 table (2 kbytes of table space) or 4 tables (8
++// kbytes of table space) for higher speed.
++// 8. Define either ONE_LR_TABLE or FOUR_LR_TABLES for a further speed
++// increase by using tables for the last rounds but with more table
++// space (2 or 8 kbytes extra).
++// 9. If neither ONE_TABLE nor FOUR_TABLES is defined, a compact but
++// slower version is provided.
++// 10. If fast decryption key scheduling is needed define ONE_IM_TABLE
++// or FOUR_IM_TABLES for higher speed (2 or 8 kbytes extra).
++
++#define UNROLL
++//#define PARTIAL_UNROLL
++
++#define FIXED_TABLES
++//#define FF_TABLES
++//#define ARRAYS
++#define FAST_VARIABLE
++
++//#define ONE_TABLE
++#define FOUR_TABLES
++
++//#define ONE_LR_TABLE
++#define FOUR_LR_TABLES
++
++//#define ONE_IM_TABLE
++#define FOUR_IM_TABLES
++
++#if defined(UNROLL) && defined (PARTIAL_UNROLL)
++#error both UNROLL and PARTIAL_UNROLL are defined
++#endif
++
++#if defined(ONE_TABLE) && defined (FOUR_TABLES)
++#error both ONE_TABLE and FOUR_TABLES are defined
++#endif
++
++#if defined(ONE_LR_TABLE) && defined (FOUR_LR_TABLES)
++#error both ONE_LR_TABLE and FOUR_LR_TABLES are defined
++#endif
++
++#if defined(ONE_IM_TABLE) && defined (FOUR_IM_TABLES)
++#error both ONE_IM_TABLE and FOUR_IM_TABLES are defined
++#endif
++
++#if defined(AES_BLOCK_SIZE) && AES_BLOCK_SIZE != 16 && AES_BLOCK_SIZE != 24 && AES_BLOCK_SIZE != 32
++#error an illegal block size has been specified
++#endif
++
++// upr(x,n): rotates bytes within words by n positions, moving bytes
++// to higher index positions with wrap around into low positions
++// ups(x,n): moves bytes by n positions to higher index positions in
++// words but without wrap around
++// bval(x,n): extracts a byte from a word
++
++#define upr(x,n) (((x) << 8 * (n)) | ((x) >> (32 - 8 * (n))))
++#define ups(x,n) ((x) << 8 * (n))
++#define bval(x,n) ((unsigned char)((x) >> 8 * (n)))
++#define bytes2word(b0, b1, b2, b3) \
++ ((u_int32_t)(b3) << 24 | (u_int32_t)(b2) << 16 | (u_int32_t)(b1) << 8 | (b0))
++
++
++/* little endian processor without data alignment restrictions: AES_LE_OK */
++/* original code: i386 */
++#if defined(i386) || defined(_I386) || defined(__i386__) || defined(__i386)
++#define AES_LE_OK 1
++/* added (tested): alpha --jjo */
++#elif defined(__alpha__)|| defined (__alpha)
++#define AES_LE_OK 1
++/* added (tested): ia64 --jjo */
++#elif defined(__ia64__)|| defined (__ia64)
++#define AES_LE_OK 1
++#endif
++
++#ifdef AES_LE_OK
++/* little endian processor without data alignment restrictions */
++#define word_in(x) *(u_int32_t*)(x)
++#define const_word_in(x) *(const u_int32_t*)(x)
++#define word_out(x,v) *(u_int32_t*)(x) = (v)
++#define const_word_out(x,v) *(const u_int32_t*)(x) = (v)
++#else
++/* slower but generic big endian or with data alignment restrictions */
++/* some additional "const" touches to stop "gcc -Wcast-qual" complains --jjo */
++#define word_in(x) ((u_int32_t)(((unsigned char *)(x))[0])|((u_int32_t)(((unsigned char *)(x))[1])<<8)|((u_int32_t)(((unsigned char *)(x))[2])<<16)|((u_int32_t)(((unsigned char *)(x))[3])<<24))
++#define const_word_in(x) ((const u_int32_t)(((const unsigned char *)(x))[0])|((const u_int32_t)(((const unsigned char *)(x))[1])<<8)|((const u_int32_t)(((const unsigned char *)(x))[2])<<16)|((const u_int32_t)(((const unsigned char *)(x))[3])<<24))
++#define word_out(x,v) ((unsigned char *)(x))[0]=(v),((unsigned char *)(x))[1]=((v)>>8),((unsigned char *)(x))[2]=((v)>>16),((unsigned char *)(x))[3]=((v)>>24)
++#define const_word_out(x,v) ((const unsigned char *)(x))[0]=(v),((const unsigned char *)(x))[1]=((v)>>8),((const unsigned char *)(x))[2]=((v)>>16),((const unsigned char *)(x))[3]=((v)>>24)
++#endif
++
++// Disable at least some poor combinations of options
++
++#if !defined(ONE_TABLE) && !defined(FOUR_TABLES)
++#define FIXED_TABLES
++#undef UNROLL
++#undef ONE_LR_TABLE
++#undef FOUR_LR_TABLES
++#undef ONE_IM_TABLE
++#undef FOUR_IM_TABLES
++#elif !defined(FOUR_TABLES)
++#ifdef FOUR_LR_TABLES
++#undef FOUR_LR_TABLES
++#define ONE_LR_TABLE
++#endif
++#ifdef FOUR_IM_TABLES
++#undef FOUR_IM_TABLES
++#define ONE_IM_TABLE
++#endif
++#elif !defined(AES_BLOCK_SIZE)
++#if defined(UNROLL)
++#define PARTIAL_UNROLL
++#undef UNROLL
++#endif
++#endif
++
++// the finite field modular polynomial and elements
++
++#define ff_poly 0x011b
++#define ff_hi 0x80
++
++// multiply four bytes in GF(2^8) by 'x' {02} in parallel
++
++#define m1 0x80808080
++#define m2 0x7f7f7f7f
++#define m3 0x0000001b
++#define FFmulX(x) ((((x) & m2) << 1) ^ ((((x) & m1) >> 7) * m3))
++
++// The following defines provide alternative definitions of FFmulX that might
++// give improved performance if a fast 32-bit multiply is not available. Note
++// that a temporary variable u needs to be defined where FFmulX is used.
++
++// #define FFmulX(x) (u = (x) & m1, u |= (u >> 1), ((x) & m2) << 1) ^ ((u >> 3) | (u >> 6))
++// #define m4 0x1b1b1b1b
++// #define FFmulX(x) (u = (x) & m1, ((x) & m2) << 1) ^ ((u - (u >> 7)) & m4)
++
++// perform column mix operation on four bytes in parallel
++
++#define fwd_mcol(x) (f2 = FFmulX(x), f2 ^ upr(x ^ f2,3) ^ upr(x,2) ^ upr(x,1))
++
++#if defined(FIXED_TABLES)
++
++// the S-Box table
++
++static const unsigned char s_box[256] =
++{
++ 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5,
++ 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
++ 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0,
++ 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
++ 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc,
++ 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
++ 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a,
++ 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
++ 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0,
++ 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
++ 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b,
++ 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
++ 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85,
++ 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
++ 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5,
++ 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
++ 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17,
++ 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
++ 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88,
++ 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
++ 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c,
++ 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
++ 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9,
++ 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
++ 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6,
++ 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
++ 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e,
++ 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
++ 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94,
++ 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
++ 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68,
++ 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16
++};
++
++// the inverse S-Box table
++
++static const unsigned char inv_s_box[256] =
++{
++ 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38,
++ 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
++ 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87,
++ 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
++ 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d,
++ 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
++ 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2,
++ 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
++ 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16,
++ 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
++ 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda,
++ 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
++ 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a,
++ 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
++ 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02,
++ 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
++ 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea,
++ 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
++ 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85,
++ 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
++ 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89,
++ 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
++ 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20,
++ 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
++ 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31,
++ 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
++ 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d,
++ 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
++ 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0,
++ 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
++ 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26,
++ 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d
++};
++
++#define w0(p) 0x000000##p
++
++// Number of elements required in this table for different
++// block and key lengths is:
++//
++// Nk = 4 6 8
++// ----------
++// Nb = 4 | 10 8 7
++// 6 | 19 12 11
++// 8 | 29 19 14
++//
++// this table can be a table of bytes if the key schedule
++// code is adjusted accordingly
++
++static const u_int32_t rcon_tab[29] =
++{
++ w0(01), w0(02), w0(04), w0(08),
++ w0(10), w0(20), w0(40), w0(80),
++ w0(1b), w0(36), w0(6c), w0(d8),
++ w0(ab), w0(4d), w0(9a), w0(2f),
++ w0(5e), w0(bc), w0(63), w0(c6),
++ w0(97), w0(35), w0(6a), w0(d4),
++ w0(b3), w0(7d), w0(fa), w0(ef),
++ w0(c5)
++};
++
++#undef w0
++
++#define r0(p,q,r,s) 0x##p##q##r##s
++#define r1(p,q,r,s) 0x##q##r##s##p
++#define r2(p,q,r,s) 0x##r##s##p##q
++#define r3(p,q,r,s) 0x##s##p##q##r
++#define w0(p) 0x000000##p
++#define w1(p) 0x0000##p##00
++#define w2(p) 0x00##p##0000
++#define w3(p) 0x##p##000000
++
++#if defined(FIXED_TABLES) && (defined(ONE_TABLE) || defined(FOUR_TABLES))
++
++// data for forward tables (other than last round)
++
++#define f_table \
++ r(a5,63,63,c6), r(84,7c,7c,f8), r(99,77,77,ee), r(8d,7b,7b,f6),\
++ r(0d,f2,f2,ff), r(bd,6b,6b,d6), r(b1,6f,6f,de), r(54,c5,c5,91),\
++ r(50,30,30,60), r(03,01,01,02), r(a9,67,67,ce), r(7d,2b,2b,56),\
++ r(19,fe,fe,e7), r(62,d7,d7,b5), r(e6,ab,ab,4d), r(9a,76,76,ec),\
++ r(45,ca,ca,8f), r(9d,82,82,1f), r(40,c9,c9,89), r(87,7d,7d,fa),\
++ r(15,fa,fa,ef), r(eb,59,59,b2), r(c9,47,47,8e), r(0b,f0,f0,fb),\
++ r(ec,ad,ad,41), r(67,d4,d4,b3), r(fd,a2,a2,5f), r(ea,af,af,45),\
++ r(bf,9c,9c,23), r(f7,a4,a4,53), r(96,72,72,e4), r(5b,c0,c0,9b),\
++ r(c2,b7,b7,75), r(1c,fd,fd,e1), r(ae,93,93,3d), r(6a,26,26,4c),\
++ r(5a,36,36,6c), r(41,3f,3f,7e), r(02,f7,f7,f5), r(4f,cc,cc,83),\
++ r(5c,34,34,68), r(f4,a5,a5,51), r(34,e5,e5,d1), r(08,f1,f1,f9),\
++ r(93,71,71,e2), r(73,d8,d8,ab), r(53,31,31,62), r(3f,15,15,2a),\
++ r(0c,04,04,08), r(52,c7,c7,95), r(65,23,23,46), r(5e,c3,c3,9d),\
++ r(28,18,18,30), r(a1,96,96,37), r(0f,05,05,0a), r(b5,9a,9a,2f),\
++ r(09,07,07,0e), r(36,12,12,24), r(9b,80,80,1b), r(3d,e2,e2,df),\
++ r(26,eb,eb,cd), r(69,27,27,4e), r(cd,b2,b2,7f), r(9f,75,75,ea),\
++ r(1b,09,09,12), r(9e,83,83,1d), r(74,2c,2c,58), r(2e,1a,1a,34),\
++ r(2d,1b,1b,36), r(b2,6e,6e,dc), r(ee,5a,5a,b4), r(fb,a0,a0,5b),\
++ r(f6,52,52,a4), r(4d,3b,3b,76), r(61,d6,d6,b7), r(ce,b3,b3,7d),\
++ r(7b,29,29,52), r(3e,e3,e3,dd), r(71,2f,2f,5e), r(97,84,84,13),\
++ r(f5,53,53,a6), r(68,d1,d1,b9), r(00,00,00,00), r(2c,ed,ed,c1),\
++ r(60,20,20,40), r(1f,fc,fc,e3), r(c8,b1,b1,79), r(ed,5b,5b,b6),\
++ r(be,6a,6a,d4), r(46,cb,cb,8d), r(d9,be,be,67), r(4b,39,39,72),\
++ r(de,4a,4a,94), r(d4,4c,4c,98), r(e8,58,58,b0), r(4a,cf,cf,85),\
++ r(6b,d0,d0,bb), r(2a,ef,ef,c5), r(e5,aa,aa,4f), r(16,fb,fb,ed),\
++ r(c5,43,43,86), r(d7,4d,4d,9a), r(55,33,33,66), r(94,85,85,11),\
++ r(cf,45,45,8a), r(10,f9,f9,e9), r(06,02,02,04), r(81,7f,7f,fe),\
++ r(f0,50,50,a0), r(44,3c,3c,78), r(ba,9f,9f,25), r(e3,a8,a8,4b),\
++ r(f3,51,51,a2), r(fe,a3,a3,5d), r(c0,40,40,80), r(8a,8f,8f,05),\
++ r(ad,92,92,3f), r(bc,9d,9d,21), r(48,38,38,70), r(04,f5,f5,f1),\
++ r(df,bc,bc,63), r(c1,b6,b6,77), r(75,da,da,af), r(63,21,21,42),\
++ r(30,10,10,20), r(1a,ff,ff,e5), r(0e,f3,f3,fd), r(6d,d2,d2,bf),\
++ r(4c,cd,cd,81), r(14,0c,0c,18), r(35,13,13,26), r(2f,ec,ec,c3),\
++ r(e1,5f,5f,be), r(a2,97,97,35), r(cc,44,44,88), r(39,17,17,2e),\
++ r(57,c4,c4,93), r(f2,a7,a7,55), r(82,7e,7e,fc), r(47,3d,3d,7a),\
++ r(ac,64,64,c8), r(e7,5d,5d,ba), r(2b,19,19,32), r(95,73,73,e6),\
++ r(a0,60,60,c0), r(98,81,81,19), r(d1,4f,4f,9e), r(7f,dc,dc,a3),\
++ r(66,22,22,44), r(7e,2a,2a,54), r(ab,90,90,3b), r(83,88,88,0b),\
++ r(ca,46,46,8c), r(29,ee,ee,c7), r(d3,b8,b8,6b), r(3c,14,14,28),\
++ r(79,de,de,a7), r(e2,5e,5e,bc), r(1d,0b,0b,16), r(76,db,db,ad),\
++ r(3b,e0,e0,db), r(56,32,32,64), r(4e,3a,3a,74), r(1e,0a,0a,14),\
++ r(db,49,49,92), r(0a,06,06,0c), r(6c,24,24,48), r(e4,5c,5c,b8),\
++ r(5d,c2,c2,9f), r(6e,d3,d3,bd), r(ef,ac,ac,43), r(a6,62,62,c4),\
++ r(a8,91,91,39), r(a4,95,95,31), r(37,e4,e4,d3), r(8b,79,79,f2),\
++ r(32,e7,e7,d5), r(43,c8,c8,8b), r(59,37,37,6e), r(b7,6d,6d,da),\
++ r(8c,8d,8d,01), r(64,d5,d5,b1), r(d2,4e,4e,9c), r(e0,a9,a9,49),\
++ r(b4,6c,6c,d8), r(fa,56,56,ac), r(07,f4,f4,f3), r(25,ea,ea,cf),\
++ r(af,65,65,ca), r(8e,7a,7a,f4), r(e9,ae,ae,47), r(18,08,08,10),\
++ r(d5,ba,ba,6f), r(88,78,78,f0), r(6f,25,25,4a), r(72,2e,2e,5c),\
++ r(24,1c,1c,38), r(f1,a6,a6,57), r(c7,b4,b4,73), r(51,c6,c6,97),\
++ r(23,e8,e8,cb), r(7c,dd,dd,a1), r(9c,74,74,e8), r(21,1f,1f,3e),\
++ r(dd,4b,4b,96), r(dc,bd,bd,61), r(86,8b,8b,0d), r(85,8a,8a,0f),\
++ r(90,70,70,e0), r(42,3e,3e,7c), r(c4,b5,b5,71), r(aa,66,66,cc),\
++ r(d8,48,48,90), r(05,03,03,06), r(01,f6,f6,f7), r(12,0e,0e,1c),\
++ r(a3,61,61,c2), r(5f,35,35,6a), r(f9,57,57,ae), r(d0,b9,b9,69),\
++ r(91,86,86,17), r(58,c1,c1,99), r(27,1d,1d,3a), r(b9,9e,9e,27),\
++ r(38,e1,e1,d9), r(13,f8,f8,eb), r(b3,98,98,2b), r(33,11,11,22),\
++ r(bb,69,69,d2), r(70,d9,d9,a9), r(89,8e,8e,07), r(a7,94,94,33),\
++ r(b6,9b,9b,2d), r(22,1e,1e,3c), r(92,87,87,15), r(20,e9,e9,c9),\
++ r(49,ce,ce,87), r(ff,55,55,aa), r(78,28,28,50), r(7a,df,df,a5),\
++ r(8f,8c,8c,03), r(f8,a1,a1,59), r(80,89,89,09), r(17,0d,0d,1a),\
++ r(da,bf,bf,65), r(31,e6,e6,d7), r(c6,42,42,84), r(b8,68,68,d0),\
++ r(c3,41,41,82), r(b0,99,99,29), r(77,2d,2d,5a), r(11,0f,0f,1e),\
++ r(cb,b0,b0,7b), r(fc,54,54,a8), r(d6,bb,bb,6d), r(3a,16,16,2c)
++
++// data for inverse tables (other than last round)
++
++#define i_table \
++ r(50,a7,f4,51), r(53,65,41,7e), r(c3,a4,17,1a), r(96,5e,27,3a),\
++ r(cb,6b,ab,3b), r(f1,45,9d,1f), r(ab,58,fa,ac), r(93,03,e3,4b),\
++ r(55,fa,30,20), r(f6,6d,76,ad), r(91,76,cc,88), r(25,4c,02,f5),\
++ r(fc,d7,e5,4f), r(d7,cb,2a,c5), r(80,44,35,26), r(8f,a3,62,b5),\
++ r(49,5a,b1,de), r(67,1b,ba,25), r(98,0e,ea,45), r(e1,c0,fe,5d),\
++ r(02,75,2f,c3), r(12,f0,4c,81), r(a3,97,46,8d), r(c6,f9,d3,6b),\
++ r(e7,5f,8f,03), r(95,9c,92,15), r(eb,7a,6d,bf), r(da,59,52,95),\
++ r(2d,83,be,d4), r(d3,21,74,58), r(29,69,e0,49), r(44,c8,c9,8e),\
++ r(6a,89,c2,75), r(78,79,8e,f4), r(6b,3e,58,99), r(dd,71,b9,27),\
++ r(b6,4f,e1,be), r(17,ad,88,f0), r(66,ac,20,c9), r(b4,3a,ce,7d),\
++ r(18,4a,df,63), r(82,31,1a,e5), r(60,33,51,97), r(45,7f,53,62),\
++ r(e0,77,64,b1), r(84,ae,6b,bb), r(1c,a0,81,fe), r(94,2b,08,f9),\
++ r(58,68,48,70), r(19,fd,45,8f), r(87,6c,de,94), r(b7,f8,7b,52),\
++ r(23,d3,73,ab), r(e2,02,4b,72), r(57,8f,1f,e3), r(2a,ab,55,66),\
++ r(07,28,eb,b2), r(03,c2,b5,2f), r(9a,7b,c5,86), r(a5,08,37,d3),\
++ r(f2,87,28,30), r(b2,a5,bf,23), r(ba,6a,03,02), r(5c,82,16,ed),\
++ r(2b,1c,cf,8a), r(92,b4,79,a7), r(f0,f2,07,f3), r(a1,e2,69,4e),\
++ r(cd,f4,da,65), r(d5,be,05,06), r(1f,62,34,d1), r(8a,fe,a6,c4),\
++ r(9d,53,2e,34), r(a0,55,f3,a2), r(32,e1,8a,05), r(75,eb,f6,a4),\
++ r(39,ec,83,0b), r(aa,ef,60,40), r(06,9f,71,5e), r(51,10,6e,bd),\
++ r(f9,8a,21,3e), r(3d,06,dd,96), r(ae,05,3e,dd), r(46,bd,e6,4d),\
++ r(b5,8d,54,91), r(05,5d,c4,71), r(6f,d4,06,04), r(ff,15,50,60),\
++ r(24,fb,98,19), r(97,e9,bd,d6), r(cc,43,40,89), r(77,9e,d9,67),\
++ r(bd,42,e8,b0), r(88,8b,89,07), r(38,5b,19,e7), r(db,ee,c8,79),\
++ r(47,0a,7c,a1), r(e9,0f,42,7c), r(c9,1e,84,f8), r(00,00,00,00),\
++ r(83,86,80,09), r(48,ed,2b,32), r(ac,70,11,1e), r(4e,72,5a,6c),\
++ r(fb,ff,0e,fd), r(56,38,85,0f), r(1e,d5,ae,3d), r(27,39,2d,36),\
++ r(64,d9,0f,0a), r(21,a6,5c,68), r(d1,54,5b,9b), r(3a,2e,36,24),\
++ r(b1,67,0a,0c), r(0f,e7,57,93), r(d2,96,ee,b4), r(9e,91,9b,1b),\
++ r(4f,c5,c0,80), r(a2,20,dc,61), r(69,4b,77,5a), r(16,1a,12,1c),\
++ r(0a,ba,93,e2), r(e5,2a,a0,c0), r(43,e0,22,3c), r(1d,17,1b,12),\
++ r(0b,0d,09,0e), r(ad,c7,8b,f2), r(b9,a8,b6,2d), r(c8,a9,1e,14),\
++ r(85,19,f1,57), r(4c,07,75,af), r(bb,dd,99,ee), r(fd,60,7f,a3),\
++ r(9f,26,01,f7), r(bc,f5,72,5c), r(c5,3b,66,44), r(34,7e,fb,5b),\
++ r(76,29,43,8b), r(dc,c6,23,cb), r(68,fc,ed,b6), r(63,f1,e4,b8),\
++ r(ca,dc,31,d7), r(10,85,63,42), r(40,22,97,13), r(20,11,c6,84),\
++ r(7d,24,4a,85), r(f8,3d,bb,d2), r(11,32,f9,ae), r(6d,a1,29,c7),\
++ r(4b,2f,9e,1d), r(f3,30,b2,dc), r(ec,52,86,0d), r(d0,e3,c1,77),\
++ r(6c,16,b3,2b), r(99,b9,70,a9), r(fa,48,94,11), r(22,64,e9,47),\
++ r(c4,8c,fc,a8), r(1a,3f,f0,a0), r(d8,2c,7d,56), r(ef,90,33,22),\
++ r(c7,4e,49,87), r(c1,d1,38,d9), r(fe,a2,ca,8c), r(36,0b,d4,98),\
++ r(cf,81,f5,a6), r(28,de,7a,a5), r(26,8e,b7,da), r(a4,bf,ad,3f),\
++ r(e4,9d,3a,2c), r(0d,92,78,50), r(9b,cc,5f,6a), r(62,46,7e,54),\
++ r(c2,13,8d,f6), r(e8,b8,d8,90), r(5e,f7,39,2e), r(f5,af,c3,82),\
++ r(be,80,5d,9f), r(7c,93,d0,69), r(a9,2d,d5,6f), r(b3,12,25,cf),\
++ r(3b,99,ac,c8), r(a7,7d,18,10), r(6e,63,9c,e8), r(7b,bb,3b,db),\
++ r(09,78,26,cd), r(f4,18,59,6e), r(01,b7,9a,ec), r(a8,9a,4f,83),\
++ r(65,6e,95,e6), r(7e,e6,ff,aa), r(08,cf,bc,21), r(e6,e8,15,ef),\
++ r(d9,9b,e7,ba), r(ce,36,6f,4a), r(d4,09,9f,ea), r(d6,7c,b0,29),\
++ r(af,b2,a4,31), r(31,23,3f,2a), r(30,94,a5,c6), r(c0,66,a2,35),\
++ r(37,bc,4e,74), r(a6,ca,82,fc), r(b0,d0,90,e0), r(15,d8,a7,33),\
++ r(4a,98,04,f1), r(f7,da,ec,41), r(0e,50,cd,7f), r(2f,f6,91,17),\
++ r(8d,d6,4d,76), r(4d,b0,ef,43), r(54,4d,aa,cc), r(df,04,96,e4),\
++ r(e3,b5,d1,9e), r(1b,88,6a,4c), r(b8,1f,2c,c1), r(7f,51,65,46),\
++ r(04,ea,5e,9d), r(5d,35,8c,01), r(73,74,87,fa), r(2e,41,0b,fb),\
++ r(5a,1d,67,b3), r(52,d2,db,92), r(33,56,10,e9), r(13,47,d6,6d),\
++ r(8c,61,d7,9a), r(7a,0c,a1,37), r(8e,14,f8,59), r(89,3c,13,eb),\
++ r(ee,27,a9,ce), r(35,c9,61,b7), r(ed,e5,1c,e1), r(3c,b1,47,7a),\
++ r(59,df,d2,9c), r(3f,73,f2,55), r(79,ce,14,18), r(bf,37,c7,73),\
++ r(ea,cd,f7,53), r(5b,aa,fd,5f), r(14,6f,3d,df), r(86,db,44,78),\
++ r(81,f3,af,ca), r(3e,c4,68,b9), r(2c,34,24,38), r(5f,40,a3,c2),\
++ r(72,c3,1d,16), r(0c,25,e2,bc), r(8b,49,3c,28), r(41,95,0d,ff),\
++ r(71,01,a8,39), r(de,b3,0c,08), r(9c,e4,b4,d8), r(90,c1,56,64),\
++ r(61,84,cb,7b), r(70,b6,32,d5), r(74,5c,6c,48), r(42,57,b8,d0)
++
++// generate the required tables in the desired endian format
++
++#undef r
++#define r r0
++
++#if defined(ONE_TABLE)
++static const u_int32_t ft_tab[256] =
++ { f_table };
++#elif defined(FOUR_TABLES)
++static const u_int32_t ft_tab[4][256] =
++{ { f_table },
++#undef r
++#define r r1
++ { f_table },
++#undef r
++#define r r2
++ { f_table },
++#undef r
++#define r r3
++ { f_table }
++};
++#endif
++
++#undef r
++#define r r0
++#if defined(ONE_TABLE)
++static const u_int32_t it_tab[256] =
++ { i_table };
++#elif defined(FOUR_TABLES)
++static const u_int32_t it_tab[4][256] =
++{ { i_table },
++#undef r
++#define r r1
++ { i_table },
++#undef r
++#define r r2
++ { i_table },
++#undef r
++#define r r3
++ { i_table }
++};
++#endif
++
++#endif
++
++#if defined(FIXED_TABLES) && (defined(ONE_LR_TABLE) || defined(FOUR_LR_TABLES))
++
++// data for inverse tables (last round)
++
++#define li_table \
++ w(52), w(09), w(6a), w(d5), w(30), w(36), w(a5), w(38),\
++ w(bf), w(40), w(a3), w(9e), w(81), w(f3), w(d7), w(fb),\
++ w(7c), w(e3), w(39), w(82), w(9b), w(2f), w(ff), w(87),\
++ w(34), w(8e), w(43), w(44), w(c4), w(de), w(e9), w(cb),\
++ w(54), w(7b), w(94), w(32), w(a6), w(c2), w(23), w(3d),\
++ w(ee), w(4c), w(95), w(0b), w(42), w(fa), w(c3), w(4e),\
++ w(08), w(2e), w(a1), w(66), w(28), w(d9), w(24), w(b2),\
++ w(76), w(5b), w(a2), w(49), w(6d), w(8b), w(d1), w(25),\
++ w(72), w(f8), w(f6), w(64), w(86), w(68), w(98), w(16),\
++ w(d4), w(a4), w(5c), w(cc), w(5d), w(65), w(b6), w(92),\
++ w(6c), w(70), w(48), w(50), w(fd), w(ed), w(b9), w(da),\
++ w(5e), w(15), w(46), w(57), w(a7), w(8d), w(9d), w(84),\
++ w(90), w(d8), w(ab), w(00), w(8c), w(bc), w(d3), w(0a),\
++ w(f7), w(e4), w(58), w(05), w(b8), w(b3), w(45), w(06),\
++ w(d0), w(2c), w(1e), w(8f), w(ca), w(3f), w(0f), w(02),\
++ w(c1), w(af), w(bd), w(03), w(01), w(13), w(8a), w(6b),\
++ w(3a), w(91), w(11), w(41), w(4f), w(67), w(dc), w(ea),\
++ w(97), w(f2), w(cf), w(ce), w(f0), w(b4), w(e6), w(73),\
++ w(96), w(ac), w(74), w(22), w(e7), w(ad), w(35), w(85),\
++ w(e2), w(f9), w(37), w(e8), w(1c), w(75), w(df), w(6e),\
++ w(47), w(f1), w(1a), w(71), w(1d), w(29), w(c5), w(89),\
++ w(6f), w(b7), w(62), w(0e), w(aa), w(18), w(be), w(1b),\
++ w(fc), w(56), w(3e), w(4b), w(c6), w(d2), w(79), w(20),\
++ w(9a), w(db), w(c0), w(fe), w(78), w(cd), w(5a), w(f4),\
++ w(1f), w(dd), w(a8), w(33), w(88), w(07), w(c7), w(31),\
++ w(b1), w(12), w(10), w(59), w(27), w(80), w(ec), w(5f),\
++ w(60), w(51), w(7f), w(a9), w(19), w(b5), w(4a), w(0d),\
++ w(2d), w(e5), w(7a), w(9f), w(93), w(c9), w(9c), w(ef),\
++ w(a0), w(e0), w(3b), w(4d), w(ae), w(2a), w(f5), w(b0),\
++ w(c8), w(eb), w(bb), w(3c), w(83), w(53), w(99), w(61),\
++ w(17), w(2b), w(04), w(7e), w(ba), w(77), w(d6), w(26),\
++ w(e1), w(69), w(14), w(63), w(55), w(21), w(0c), w(7d),
++
++// generate the required tables in the desired endian format
++
++#undef r
++#define r(p,q,r,s) w0(q)
++#if defined(ONE_LR_TABLE)
++static const u_int32_t fl_tab[256] =
++ { f_table };
++#elif defined(FOUR_LR_TABLES)
++static const u_int32_t fl_tab[4][256] =
++{ { f_table },
++#undef r
++#define r(p,q,r,s) w1(q)
++ { f_table },
++#undef r
++#define r(p,q,r,s) w2(q)
++ { f_table },
++#undef r
++#define r(p,q,r,s) w3(q)
++ { f_table }
++};
++#endif
++
++#undef w
++#define w w0
++#if defined(ONE_LR_TABLE)
++static const u_int32_t il_tab[256] =
++ { li_table };
++#elif defined(FOUR_LR_TABLES)
++static const u_int32_t il_tab[4][256] =
++{ { li_table },
++#undef w
++#define w w1
++ { li_table },
++#undef w
++#define w w2
++ { li_table },
++#undef w
++#define w w3
++ { li_table }
++};
++#endif
++
++#endif
++
++#if defined(FIXED_TABLES) && (defined(ONE_IM_TABLE) || defined(FOUR_IM_TABLES))
++
++#define m_table \
++ r(00,00,00,00), r(0b,0d,09,0e), r(16,1a,12,1c), r(1d,17,1b,12),\
++ r(2c,34,24,38), r(27,39,2d,36), r(3a,2e,36,24), r(31,23,3f,2a),\
++ r(58,68,48,70), r(53,65,41,7e), r(4e,72,5a,6c), r(45,7f,53,62),\
++ r(74,5c,6c,48), r(7f,51,65,46), r(62,46,7e,54), r(69,4b,77,5a),\
++ r(b0,d0,90,e0), r(bb,dd,99,ee), r(a6,ca,82,fc), r(ad,c7,8b,f2),\
++ r(9c,e4,b4,d8), r(97,e9,bd,d6), r(8a,fe,a6,c4), r(81,f3,af,ca),\
++ r(e8,b8,d8,90), r(e3,b5,d1,9e), r(fe,a2,ca,8c), r(f5,af,c3,82),\
++ r(c4,8c,fc,a8), r(cf,81,f5,a6), r(d2,96,ee,b4), r(d9,9b,e7,ba),\
++ r(7b,bb,3b,db), r(70,b6,32,d5), r(6d,a1,29,c7), r(66,ac,20,c9),\
++ r(57,8f,1f,e3), r(5c,82,16,ed), r(41,95,0d,ff), r(4a,98,04,f1),\
++ r(23,d3,73,ab), r(28,de,7a,a5), r(35,c9,61,b7), r(3e,c4,68,b9),\
++ r(0f,e7,57,93), r(04,ea,5e,9d), r(19,fd,45,8f), r(12,f0,4c,81),\
++ r(cb,6b,ab,3b), r(c0,66,a2,35), r(dd,71,b9,27), r(d6,7c,b0,29),\
++ r(e7,5f,8f,03), r(ec,52,86,0d), r(f1,45,9d,1f), r(fa,48,94,11),\
++ r(93,03,e3,4b), r(98,0e,ea,45), r(85,19,f1,57), r(8e,14,f8,59),\
++ r(bf,37,c7,73), r(b4,3a,ce,7d), r(a9,2d,d5,6f), r(a2,20,dc,61),\
++ r(f6,6d,76,ad), r(fd,60,7f,a3), r(e0,77,64,b1), r(eb,7a,6d,bf),\
++ r(da,59,52,95), r(d1,54,5b,9b), r(cc,43,40,89), r(c7,4e,49,87),\
++ r(ae,05,3e,dd), r(a5,08,37,d3), r(b8,1f,2c,c1), r(b3,12,25,cf),\
++ r(82,31,1a,e5), r(89,3c,13,eb), r(94,2b,08,f9), r(9f,26,01,f7),\
++ r(46,bd,e6,4d), r(4d,b0,ef,43), r(50,a7,f4,51), r(5b,aa,fd,5f),\
++ r(6a,89,c2,75), r(61,84,cb,7b), r(7c,93,d0,69), r(77,9e,d9,67),\
++ r(1e,d5,ae,3d), r(15,d8,a7,33), r(08,cf,bc,21), r(03,c2,b5,2f),\
++ r(32,e1,8a,05), r(39,ec,83,0b), r(24,fb,98,19), r(2f,f6,91,17),\
++ r(8d,d6,4d,76), r(86,db,44,78), r(9b,cc,5f,6a), r(90,c1,56,64),\
++ r(a1,e2,69,4e), r(aa,ef,60,40), r(b7,f8,7b,52), r(bc,f5,72,5c),\
++ r(d5,be,05,06), r(de,b3,0c,08), r(c3,a4,17,1a), r(c8,a9,1e,14),\
++ r(f9,8a,21,3e), r(f2,87,28,30), r(ef,90,33,22), r(e4,9d,3a,2c),\
++ r(3d,06,dd,96), r(36,0b,d4,98), r(2b,1c,cf,8a), r(20,11,c6,84),\
++ r(11,32,f9,ae), r(1a,3f,f0,a0), r(07,28,eb,b2), r(0c,25,e2,bc),\
++ r(65,6e,95,e6), r(6e,63,9c,e8), r(73,74,87,fa), r(78,79,8e,f4),\
++ r(49,5a,b1,de), r(42,57,b8,d0), r(5f,40,a3,c2), r(54,4d,aa,cc),\
++ r(f7,da,ec,41), r(fc,d7,e5,4f), r(e1,c0,fe,5d), r(ea,cd,f7,53),\
++ r(db,ee,c8,79), r(d0,e3,c1,77), r(cd,f4,da,65), r(c6,f9,d3,6b),\
++ r(af,b2,a4,31), r(a4,bf,ad,3f), r(b9,a8,b6,2d), r(b2,a5,bf,23),\
++ r(83,86,80,09), r(88,8b,89,07), r(95,9c,92,15), r(9e,91,9b,1b),\
++ r(47,0a,7c,a1), r(4c,07,75,af), r(51,10,6e,bd), r(5a,1d,67,b3),\
++ r(6b,3e,58,99), r(60,33,51,97), r(7d,24,4a,85), r(76,29,43,8b),\
++ r(1f,62,34,d1), r(14,6f,3d,df), r(09,78,26,cd), r(02,75,2f,c3),\
++ r(33,56,10,e9), r(38,5b,19,e7), r(25,4c,02,f5), r(2e,41,0b,fb),\
++ r(8c,61,d7,9a), r(87,6c,de,94), r(9a,7b,c5,86), r(91,76,cc,88),\
++ r(a0,55,f3,a2), r(ab,58,fa,ac), r(b6,4f,e1,be), r(bd,42,e8,b0),\
++ r(d4,09,9f,ea), r(df,04,96,e4), r(c2,13,8d,f6), r(c9,1e,84,f8),\
++ r(f8,3d,bb,d2), r(f3,30,b2,dc), r(ee,27,a9,ce), r(e5,2a,a0,c0),\
++ r(3c,b1,47,7a), r(37,bc,4e,74), r(2a,ab,55,66), r(21,a6,5c,68),\
++ r(10,85,63,42), r(1b,88,6a,4c), r(06,9f,71,5e), r(0d,92,78,50),\
++ r(64,d9,0f,0a), r(6f,d4,06,04), r(72,c3,1d,16), r(79,ce,14,18),\
++ r(48,ed,2b,32), r(43,e0,22,3c), r(5e,f7,39,2e), r(55,fa,30,20),\
++ r(01,b7,9a,ec), r(0a,ba,93,e2), r(17,ad,88,f0), r(1c,a0,81,fe),\
++ r(2d,83,be,d4), r(26,8e,b7,da), r(3b,99,ac,c8), r(30,94,a5,c6),\
++ r(59,df,d2,9c), r(52,d2,db,92), r(4f,c5,c0,80), r(44,c8,c9,8e),\
++ r(75,eb,f6,a4), r(7e,e6,ff,aa), r(63,f1,e4,b8), r(68,fc,ed,b6),\
++ r(b1,67,0a,0c), r(ba,6a,03,02), r(a7,7d,18,10), r(ac,70,11,1e),\
++ r(9d,53,2e,34), r(96,5e,27,3a), r(8b,49,3c,28), r(80,44,35,26),\
++ r(e9,0f,42,7c), r(e2,02,4b,72), r(ff,15,50,60), r(f4,18,59,6e),\
++ r(c5,3b,66,44), r(ce,36,6f,4a), r(d3,21,74,58), r(d8,2c,7d,56),\
++ r(7a,0c,a1,37), r(71,01,a8,39), r(6c,16,b3,2b), r(67,1b,ba,25),\
++ r(56,38,85,0f), r(5d,35,8c,01), r(40,22,97,13), r(4b,2f,9e,1d),\
++ r(22,64,e9,47), r(29,69,e0,49), r(34,7e,fb,5b), r(3f,73,f2,55),\
++ r(0e,50,cd,7f), r(05,5d,c4,71), r(18,4a,df,63), r(13,47,d6,6d),\
++ r(ca,dc,31,d7), r(c1,d1,38,d9), r(dc,c6,23,cb), r(d7,cb,2a,c5),\
++ r(e6,e8,15,ef), r(ed,e5,1c,e1), r(f0,f2,07,f3), r(fb,ff,0e,fd),\
++ r(92,b4,79,a7), r(99,b9,70,a9), r(84,ae,6b,bb), r(8f,a3,62,b5),\
++ r(be,80,5d,9f), r(b5,8d,54,91), r(a8,9a,4f,83), r(a3,97,46,8d)
++
++#undef r
++#define r r0
++
++#if defined(ONE_IM_TABLE)
++static const u_int32_t im_tab[256] =
++ { m_table };
++#elif defined(FOUR_IM_TABLES)
++static const u_int32_t im_tab[4][256] =
++{ { m_table },
++#undef r
++#define r r1
++ { m_table },
++#undef r
++#define r r2
++ { m_table },
++#undef r
++#define r r3
++ { m_table }
++};
++#endif
++
++#endif
++
++#else
++
++static int tab_gen = 0;
++
++static unsigned char s_box[256]; // the S box
++static unsigned char inv_s_box[256]; // the inverse S box
++static u_int32_t rcon_tab[AES_RC_LENGTH]; // table of round constants
++
++#if defined(ONE_TABLE)
++static u_int32_t ft_tab[256];
++static u_int32_t it_tab[256];
++#elif defined(FOUR_TABLES)
++static u_int32_t ft_tab[4][256];
++static u_int32_t it_tab[4][256];
++#endif
++
++#if defined(ONE_LR_TABLE)
++static u_int32_t fl_tab[256];
++static u_int32_t il_tab[256];
++#elif defined(FOUR_LR_TABLES)
++static u_int32_t fl_tab[4][256];
++static u_int32_t il_tab[4][256];
++#endif
++
++#if defined(ONE_IM_TABLE)
++static u_int32_t im_tab[256];
++#elif defined(FOUR_IM_TABLES)
++static u_int32_t im_tab[4][256];
++#endif
++
++// Generate the tables for the dynamic table option
++
++#if !defined(FF_TABLES)
++
++// It will generally be sensible to use tables to compute finite
++// field multiplies and inverses but where memory is scarse this
++// code might sometimes be better.
++
++// return 2 ^ (n - 1) where n is the bit number of the highest bit
++// set in x with x in the range 1 < x < 0x00000200. This form is
++// used so that locals within FFinv can be bytes rather than words
++
++static unsigned char hibit(const u_int32_t x)
++{ unsigned char r = (unsigned char)((x >> 1) | (x >> 2));
++
++ r |= (r >> 2);
++ r |= (r >> 4);
++ return (r + 1) >> 1;
++}
++
++// return the inverse of the finite field element x
++
++static unsigned char FFinv(const unsigned char x)
++{ unsigned char p1 = x, p2 = 0x1b, n1 = hibit(x), n2 = 0x80, v1 = 1, v2 = 0;
++
++ if(x < 2) return x;
++
++ for(;;)
++ {
++ if(!n1) return v1;
++
++ while(n2 >= n1)
++ {
++ n2 /= n1; p2 ^= p1 * n2; v2 ^= v1 * n2; n2 = hibit(p2);
++ }
++
++ if(!n2) return v2;
++
++ while(n1 >= n2)
++ {
++ n1 /= n2; p1 ^= p2 * n1; v1 ^= v2 * n1; n1 = hibit(p1);
++ }
++ }
++}
++
++// define the finite field multiplies required for Rijndael
++
++#define FFmul02(x) ((((x) & 0x7f) << 1) ^ ((x) & 0x80 ? 0x1b : 0))
++#define FFmul03(x) ((x) ^ FFmul02(x))
++#define FFmul09(x) ((x) ^ FFmul02(FFmul02(FFmul02(x))))
++#define FFmul0b(x) ((x) ^ FFmul02((x) ^ FFmul02(FFmul02(x))))
++#define FFmul0d(x) ((x) ^ FFmul02(FFmul02((x) ^ FFmul02(x))))
++#define FFmul0e(x) FFmul02((x) ^ FFmul02((x) ^ FFmul02(x)))
++
++#else
++
++#define FFinv(x) ((x) ? pow[255 - log[x]]: 0)
++
++#define FFmul02(x) (x ? pow[log[x] + 0x19] : 0)
++#define FFmul03(x) (x ? pow[log[x] + 0x01] : 0)
++#define FFmul09(x) (x ? pow[log[x] + 0xc7] : 0)
++#define FFmul0b(x) (x ? pow[log[x] + 0x68] : 0)
++#define FFmul0d(x) (x ? pow[log[x] + 0xee] : 0)
++#define FFmul0e(x) (x ? pow[log[x] + 0xdf] : 0)
++
++#endif
++
++// The forward and inverse affine transformations used in the S-box
++
++#define fwd_affine(x) \
++ (w = (u_int32_t)x, w ^= (w<<1)^(w<<2)^(w<<3)^(w<<4), 0x63^(unsigned char)(w^(w>>8)))
++
++#define inv_affine(x) \
++ (w = (u_int32_t)x, w = (w<<1)^(w<<3)^(w<<6), 0x05^(unsigned char)(w^(w>>8)))
++
++static void gen_tabs(void)
++{ u_int32_t i, w;
++
++#if defined(FF_TABLES)
++
++ unsigned char pow[512], log[256];
++
++ // log and power tables for GF(2^8) finite field with
++ // 0x011b as modular polynomial - the simplest primitive
++ // root is 0x03, used here to generate the tables
++
++ i = 0; w = 1;
++ do
++ {
++ pow[i] = (unsigned char)w;
++ pow[i + 255] = (unsigned char)w;
++ log[w] = (unsigned char)i++;
++ w ^= (w << 1) ^ (w & ff_hi ? ff_poly : 0);
++ }
++ while (w != 1);
++
++#endif
++
++ for(i = 0, w = 1; i < AES_RC_LENGTH; ++i)
++ {
++ rcon_tab[i] = bytes2word(w, 0, 0, 0);
++ w = (w << 1) ^ (w & ff_hi ? ff_poly : 0);
++ }
++
++ for(i = 0; i < 256; ++i)
++ { unsigned char b;
++
++ s_box[i] = b = fwd_affine(FFinv((unsigned char)i));
++
++ w = bytes2word(b, 0, 0, 0);
++#if defined(ONE_LR_TABLE)
++ fl_tab[i] = w;
++#elif defined(FOUR_LR_TABLES)
++ fl_tab[0][i] = w;
++ fl_tab[1][i] = upr(w,1);
++ fl_tab[2][i] = upr(w,2);
++ fl_tab[3][i] = upr(w,3);
++#endif
++ w = bytes2word(FFmul02(b), b, b, FFmul03(b));
++#if defined(ONE_TABLE)
++ ft_tab[i] = w;
++#elif defined(FOUR_TABLES)
++ ft_tab[0][i] = w;
++ ft_tab[1][i] = upr(w,1);
++ ft_tab[2][i] = upr(w,2);
++ ft_tab[3][i] = upr(w,3);
++#endif
++ inv_s_box[i] = b = FFinv(inv_affine((unsigned char)i));
++
++ w = bytes2word(b, 0, 0, 0);
++#if defined(ONE_LR_TABLE)
++ il_tab[i] = w;
++#elif defined(FOUR_LR_TABLES)
++ il_tab[0][i] = w;
++ il_tab[1][i] = upr(w,1);
++ il_tab[2][i] = upr(w,2);
++ il_tab[3][i] = upr(w,3);
++#endif
++ w = bytes2word(FFmul0e(b), FFmul09(b), FFmul0d(b), FFmul0b(b));
++#if defined(ONE_TABLE)
++ it_tab[i] = w;
++#elif defined(FOUR_TABLES)
++ it_tab[0][i] = w;
++ it_tab[1][i] = upr(w,1);
++ it_tab[2][i] = upr(w,2);
++ it_tab[3][i] = upr(w,3);
++#endif
++#if defined(ONE_IM_TABLE)
++ im_tab[b] = w;
++#elif defined(FOUR_IM_TABLES)
++ im_tab[0][b] = w;
++ im_tab[1][b] = upr(w,1);
++ im_tab[2][b] = upr(w,2);
++ im_tab[3][b] = upr(w,3);
++#endif
++
++ }
++}
++
++#endif
++
++#define no_table(x,box,vf,rf,c) bytes2word( \
++ box[bval(vf(x,0,c),rf(0,c))], \
++ box[bval(vf(x,1,c),rf(1,c))], \
++ box[bval(vf(x,2,c),rf(2,c))], \
++ box[bval(vf(x,3,c),rf(3,c))])
++
++#define one_table(x,op,tab,vf,rf,c) \
++ ( tab[bval(vf(x,0,c),rf(0,c))] \
++ ^ op(tab[bval(vf(x,1,c),rf(1,c))],1) \
++ ^ op(tab[bval(vf(x,2,c),rf(2,c))],2) \
++ ^ op(tab[bval(vf(x,3,c),rf(3,c))],3))
++
++#define four_tables(x,tab,vf,rf,c) \
++ ( tab[0][bval(vf(x,0,c),rf(0,c))] \
++ ^ tab[1][bval(vf(x,1,c),rf(1,c))] \
++ ^ tab[2][bval(vf(x,2,c),rf(2,c))] \
++ ^ tab[3][bval(vf(x,3,c),rf(3,c))])
++
++#define vf1(x,r,c) (x)
++#define rf1(r,c) (r)
++#define rf2(r,c) ((r-c)&3)
++
++#if defined(FOUR_LR_TABLES)
++#define ls_box(x,c) four_tables(x,fl_tab,vf1,rf2,c)
++#elif defined(ONE_LR_TABLE)
++#define ls_box(x,c) one_table(x,upr,fl_tab,vf1,rf2,c)
++#else
++#define ls_box(x,c) no_table(x,s_box,vf1,rf2,c)
++#endif
++
++#if defined(FOUR_IM_TABLES)
++#define inv_mcol(x) four_tables(x,im_tab,vf1,rf1,0)
++#elif defined(ONE_IM_TABLE)
++#define inv_mcol(x) one_table(x,upr,im_tab,vf1,rf1,0)
++#else
++#define inv_mcol(x) \
++ (f9 = (x),f2 = FFmulX(f9), f4 = FFmulX(f2), f8 = FFmulX(f4), f9 ^= f8, \
++ f2 ^= f4 ^ f8 ^ upr(f2 ^ f9,3) ^ upr(f4 ^ f9,2) ^ upr(f9,1))
++#endif
++
++// Subroutine to set the block size (if variable) in bytes, legal
++// values being 16, 24 and 32.
++
++#if defined(AES_BLOCK_SIZE)
++#define nc (AES_BLOCK_SIZE / 4)
++#else
++#define nc (cx->aes_Ncol)
++
++void aes_set_blk(aes_context *cx, int n_bytes)
++{
++#if !defined(FIXED_TABLES)
++ if(!tab_gen) { gen_tabs(); tab_gen = 1; }
++#endif
++
++ switch(n_bytes) {
++ case 32: /* bytes */
++ case 256: /* bits */
++ nc = 8;
++ break;
++ case 24: /* bytes */
++ case 192: /* bits */
++ nc = 6;
++ break;
++ case 16: /* bytes */
++ case 128: /* bits */
++ default:
++ nc = 4;
++ break;
++ }
++}
++
++#endif
++
++// Initialise the key schedule from the user supplied key. The key
++// length is now specified in bytes - 16, 24 or 32 as appropriate.
++// This corresponds to bit lengths of 128, 192 and 256 bits, and
++// to Nk values of 4, 6 and 8 respectively.
++
++#define mx(t,f) (*t++ = inv_mcol(*f),f++)
++#define cp(t,f) *t++ = *f++
++
++#if AES_BLOCK_SIZE == 16
++#define cpy(d,s) cp(d,s); cp(d,s); cp(d,s); cp(d,s)
++#define mix(d,s) mx(d,s); mx(d,s); mx(d,s); mx(d,s)
++#elif AES_BLOCK_SIZE == 24
++#define cpy(d,s) cp(d,s); cp(d,s); cp(d,s); cp(d,s); \
++ cp(d,s); cp(d,s)
++#define mix(d,s) mx(d,s); mx(d,s); mx(d,s); mx(d,s); \
++ mx(d,s); mx(d,s)
++#elif AES_BLOCK_SIZE == 32
++#define cpy(d,s) cp(d,s); cp(d,s); cp(d,s); cp(d,s); \
++ cp(d,s); cp(d,s); cp(d,s); cp(d,s)
++#define mix(d,s) mx(d,s); mx(d,s); mx(d,s); mx(d,s); \
++ mx(d,s); mx(d,s); mx(d,s); mx(d,s)
++#else
++
++#define cpy(d,s) \
++switch(nc) \
++{ case 8: cp(d,s); cp(d,s); \
++ case 6: cp(d,s); cp(d,s); \
++ case 4: cp(d,s); cp(d,s); \
++ cp(d,s); cp(d,s); \
++}
++
++#define mix(d,s) \
++switch(nc) \
++{ case 8: mx(d,s); mx(d,s); \
++ case 6: mx(d,s); mx(d,s); \
++ case 4: mx(d,s); mx(d,s); \
++ mx(d,s); mx(d,s); \
++}
++
++#endif
++
++void aes_set_key(aes_context *cx, const unsigned char in_key[], int n_bytes, const int f)
++{ u_int32_t *kf, *kt, rci;
++
++#if !defined(FIXED_TABLES)
++ if(!tab_gen) { gen_tabs(); tab_gen = 1; }
++#endif
++
++ switch(n_bytes) {
++ case 32: /* bytes */
++ case 256: /* bits */
++ cx->aes_Nkey = 8;
++ break;
++ case 24: /* bytes */
++ case 192: /* bits */
++ cx->aes_Nkey = 6;
++ break;
++ case 16: /* bytes */
++ case 128: /* bits */
++ default:
++ cx->aes_Nkey = 4;
++ break;
++ }
++
++ cx->aes_Nrnd = (cx->aes_Nkey > nc ? cx->aes_Nkey : nc) + 6;
++
++ cx->aes_e_key[0] = const_word_in(in_key );
++ cx->aes_e_key[1] = const_word_in(in_key + 4);
++ cx->aes_e_key[2] = const_word_in(in_key + 8);
++ cx->aes_e_key[3] = const_word_in(in_key + 12);
++
++ kf = cx->aes_e_key;
++ kt = kf + nc * (cx->aes_Nrnd + 1) - cx->aes_Nkey;
++ rci = 0;
++
++ switch(cx->aes_Nkey)
++ {
++ case 4: do
++ { kf[4] = kf[0] ^ ls_box(kf[3],3) ^ rcon_tab[rci++];
++ kf[5] = kf[1] ^ kf[4];
++ kf[6] = kf[2] ^ kf[5];
++ kf[7] = kf[3] ^ kf[6];
++ kf += 4;
++ }
++ while(kf < kt);
++ break;
++
++ case 6: cx->aes_e_key[4] = const_word_in(in_key + 16);
++ cx->aes_e_key[5] = const_word_in(in_key + 20);
++ do
++ { kf[ 6] = kf[0] ^ ls_box(kf[5],3) ^ rcon_tab[rci++];
++ kf[ 7] = kf[1] ^ kf[ 6];
++ kf[ 8] = kf[2] ^ kf[ 7];
++ kf[ 9] = kf[3] ^ kf[ 8];
++ kf[10] = kf[4] ^ kf[ 9];
++ kf[11] = kf[5] ^ kf[10];
++ kf += 6;
++ }
++ while(kf < kt);
++ break;
++
++ case 8: cx->aes_e_key[4] = const_word_in(in_key + 16);
++ cx->aes_e_key[5] = const_word_in(in_key + 20);
++ cx->aes_e_key[6] = const_word_in(in_key + 24);
++ cx->aes_e_key[7] = const_word_in(in_key + 28);
++ do
++ { kf[ 8] = kf[0] ^ ls_box(kf[7],3) ^ rcon_tab[rci++];
++ kf[ 9] = kf[1] ^ kf[ 8];
++ kf[10] = kf[2] ^ kf[ 9];
++ kf[11] = kf[3] ^ kf[10];
++ kf[12] = kf[4] ^ ls_box(kf[11],0);
++ kf[13] = kf[5] ^ kf[12];
++ kf[14] = kf[6] ^ kf[13];
++ kf[15] = kf[7] ^ kf[14];
++ kf += 8;
++ }
++ while (kf < kt);
++ break;
++ }
++
++ if(!f)
++ { u_int32_t i;
++
++ kt = cx->aes_d_key + nc * cx->aes_Nrnd;
++ kf = cx->aes_e_key;
++
++ cpy(kt, kf); kt -= 2 * nc;
++
++ for(i = 1; i < cx->aes_Nrnd; ++i)
++ {
++#if defined(ONE_TABLE) || defined(FOUR_TABLES)
++#if !defined(ONE_IM_TABLE) && !defined(FOUR_IM_TABLES)
++ u_int32_t f2, f4, f8, f9;
++#endif
++ mix(kt, kf);
++#else
++ cpy(kt, kf);
++#endif
++ kt -= 2 * nc;
++ }
++
++ cpy(kt, kf);
++ }
++}
++
++// y = output word, x = input word, r = row, c = column
++// for r = 0, 1, 2 and 3 = column accessed for row r
++
++#if defined(ARRAYS)
++#define s(x,c) x[c]
++#else
++#define s(x,c) x##c
++#endif
++
++// I am grateful to Frank Yellin for the following constructions
++// which, given the column (c) of the output state variable that
++// is being computed, return the input state variables which are
++// needed for each row (r) of the state
++
++// For the fixed block size options, compilers reduce these two
++// expressions to fixed variable references. For variable block
++// size code conditional clauses will sometimes be returned
++
++#define unused 77 // Sunset Strip
++
++#define fwd_var(x,r,c) \
++ ( r==0 ? \
++ ( c==0 ? s(x,0) \
++ : c==1 ? s(x,1) \
++ : c==2 ? s(x,2) \
++ : c==3 ? s(x,3) \
++ : c==4 ? s(x,4) \
++ : c==5 ? s(x,5) \
++ : c==6 ? s(x,6) \
++ : s(x,7)) \
++ : r==1 ? \
++ ( c==0 ? s(x,1) \
++ : c==1 ? s(x,2) \
++ : c==2 ? s(x,3) \
++ : c==3 ? nc==4 ? s(x,0) : s(x,4) \
++ : c==4 ? s(x,5) \
++ : c==5 ? nc==8 ? s(x,6) : s(x,0) \
++ : c==6 ? s(x,7) \
++ : s(x,0)) \
++ : r==2 ? \
++ ( c==0 ? nc==8 ? s(x,3) : s(x,2) \
++ : c==1 ? nc==8 ? s(x,4) : s(x,3) \
++ : c==2 ? nc==4 ? s(x,0) : nc==8 ? s(x,5) : s(x,4) \
++ : c==3 ? nc==4 ? s(x,1) : nc==8 ? s(x,6) : s(x,5) \
++ : c==4 ? nc==8 ? s(x,7) : s(x,0) \
++ : c==5 ? nc==8 ? s(x,0) : s(x,1) \
++ : c==6 ? s(x,1) \
++ : s(x,2)) \
++ : \
++ ( c==0 ? nc==8 ? s(x,4) : s(x,3) \
++ : c==1 ? nc==4 ? s(x,0) : nc==8 ? s(x,5) : s(x,4) \
++ : c==2 ? nc==4 ? s(x,1) : nc==8 ? s(x,6) : s(x,5) \
++ : c==3 ? nc==4 ? s(x,2) : nc==8 ? s(x,7) : s(x,0) \
++ : c==4 ? nc==8 ? s(x,0) : s(x,1) \
++ : c==5 ? nc==8 ? s(x,1) : s(x,2) \
++ : c==6 ? s(x,2) \
++ : s(x,3)))
++
++#define inv_var(x,r,c) \
++ ( r==0 ? \
++ ( c==0 ? s(x,0) \
++ : c==1 ? s(x,1) \
++ : c==2 ? s(x,2) \
++ : c==3 ? s(x,3) \
++ : c==4 ? s(x,4) \
++ : c==5 ? s(x,5) \
++ : c==6 ? s(x,6) \
++ : s(x,7)) \
++ : r==1 ? \
++ ( c==0 ? nc==4 ? s(x,3) : nc==8 ? s(x,7) : s(x,5) \
++ : c==1 ? s(x,0) \
++ : c==2 ? s(x,1) \
++ : c==3 ? s(x,2) \
++ : c==4 ? s(x,3) \
++ : c==5 ? s(x,4) \
++ : c==6 ? s(x,5) \
++ : s(x,6)) \
++ : r==2 ? \
++ ( c==0 ? nc==4 ? s(x,2) : nc==8 ? s(x,5) : s(x,4) \
++ : c==1 ? nc==4 ? s(x,3) : nc==8 ? s(x,6) : s(x,5) \
++ : c==2 ? nc==8 ? s(x,7) : s(x,0) \
++ : c==3 ? nc==8 ? s(x,0) : s(x,1) \
++ : c==4 ? nc==8 ? s(x,1) : s(x,2) \
++ : c==5 ? nc==8 ? s(x,2) : s(x,3) \
++ : c==6 ? s(x,3) \
++ : s(x,4)) \
++ : \
++ ( c==0 ? nc==4 ? s(x,1) : nc==8 ? s(x,4) : s(x,3) \
++ : c==1 ? nc==4 ? s(x,2) : nc==8 ? s(x,5) : s(x,4) \
++ : c==2 ? nc==4 ? s(x,3) : nc==8 ? s(x,6) : s(x,5) \
++ : c==3 ? nc==8 ? s(x,7) : s(x,0) \
++ : c==4 ? nc==8 ? s(x,0) : s(x,1) \
++ : c==5 ? nc==8 ? s(x,1) : s(x,2) \
++ : c==6 ? s(x,2) \
++ : s(x,3)))
++
++#define si(y,x,k,c) s(y,c) = const_word_in(x + 4 * c) ^ k[c]
++#define so(y,x,c) word_out(y + 4 * c, s(x,c))
++
++#if defined(FOUR_TABLES)
++#define fwd_rnd(y,x,k,c) s(y,c)= (k)[c] ^ four_tables(x,ft_tab,fwd_var,rf1,c)
++#define inv_rnd(y,x,k,c) s(y,c)= (k)[c] ^ four_tables(x,it_tab,inv_var,rf1,c)
++#elif defined(ONE_TABLE)
++#define fwd_rnd(y,x,k,c) s(y,c)= (k)[c] ^ one_table(x,upr,ft_tab,fwd_var,rf1,c)
++#define inv_rnd(y,x,k,c) s(y,c)= (k)[c] ^ one_table(x,upr,it_tab,inv_var,rf1,c)
++#else
++#define fwd_rnd(y,x,k,c) s(y,c) = fwd_mcol(no_table(x,s_box,fwd_var,rf1,c)) ^ (k)[c]
++#define inv_rnd(y,x,k,c) s(y,c) = inv_mcol(no_table(x,inv_s_box,inv_var,rf1,c) ^ (k)[c])
++#endif
++
++#if defined(FOUR_LR_TABLES)
++#define fwd_lrnd(y,x,k,c) s(y,c)= (k)[c] ^ four_tables(x,fl_tab,fwd_var,rf1,c)
++#define inv_lrnd(y,x,k,c) s(y,c)= (k)[c] ^ four_tables(x,il_tab,inv_var,rf1,c)
++#elif defined(ONE_LR_TABLE)
++#define fwd_lrnd(y,x,k,c) s(y,c)= (k)[c] ^ one_table(x,ups,fl_tab,fwd_var,rf1,c)
++#define inv_lrnd(y,x,k,c) s(y,c)= (k)[c] ^ one_table(x,ups,il_tab,inv_var,rf1,c)
++#else
++#define fwd_lrnd(y,x,k,c) s(y,c) = no_table(x,s_box,fwd_var,rf1,c) ^ (k)[c]
++#define inv_lrnd(y,x,k,c) s(y,c) = no_table(x,inv_s_box,inv_var,rf1,c) ^ (k)[c]
++#endif
++
++#if AES_BLOCK_SIZE == 16
++
++#if defined(ARRAYS)
++#define locals(y,x) x[4],y[4]
++#else
++#define locals(y,x) x##0,x##1,x##2,x##3,y##0,y##1,y##2,y##3
++// the following defines prevent the compiler requiring the declaration
++// of generated but unused variables in the fwd_var and inv_var macros
++#define b04 unused
++#define b05 unused
++#define b06 unused
++#define b07 unused
++#define b14 unused
++#define b15 unused
++#define b16 unused
++#define b17 unused
++#endif
++#define l_copy(y, x) s(y,0) = s(x,0); s(y,1) = s(x,1); \
++ s(y,2) = s(x,2); s(y,3) = s(x,3);
++#define state_in(y,x,k) si(y,x,k,0); si(y,x,k,1); si(y,x,k,2); si(y,x,k,3)
++#define state_out(y,x) so(y,x,0); so(y,x,1); so(y,x,2); so(y,x,3)
++#define round(rm,y,x,k) rm(y,x,k,0); rm(y,x,k,1); rm(y,x,k,2); rm(y,x,k,3)
++
++#elif AES_BLOCK_SIZE == 24
++
++#if defined(ARRAYS)
++#define locals(y,x) x[6],y[6]
++#else
++#define locals(y,x) x##0,x##1,x##2,x##3,x##4,x##5, \
++ y##0,y##1,y##2,y##3,y##4,y##5
++#define b06 unused
++#define b07 unused
++#define b16 unused
++#define b17 unused
++#endif
++#define l_copy(y, x) s(y,0) = s(x,0); s(y,1) = s(x,1); \
++ s(y,2) = s(x,2); s(y,3) = s(x,3); \
++ s(y,4) = s(x,4); s(y,5) = s(x,5);
++#define state_in(y,x,k) si(y,x,k,0); si(y,x,k,1); si(y,x,k,2); \
++ si(y,x,k,3); si(y,x,k,4); si(y,x,k,5)
++#define state_out(y,x) so(y,x,0); so(y,x,1); so(y,x,2); \
++ so(y,x,3); so(y,x,4); so(y,x,5)
++#define round(rm,y,x,k) rm(y,x,k,0); rm(y,x,k,1); rm(y,x,k,2); \
++ rm(y,x,k,3); rm(y,x,k,4); rm(y,x,k,5)
++#else
++
++#if defined(ARRAYS)
++#define locals(y,x) x[8],y[8]
++#else
++#define locals(y,x) x##0,x##1,x##2,x##3,x##4,x##5,x##6,x##7, \
++ y##0,y##1,y##2,y##3,y##4,y##5,y##6,y##7
++#endif
++#define l_copy(y, x) s(y,0) = s(x,0); s(y,1) = s(x,1); \
++ s(y,2) = s(x,2); s(y,3) = s(x,3); \
++ s(y,4) = s(x,4); s(y,5) = s(x,5); \
++ s(y,6) = s(x,6); s(y,7) = s(x,7);
++
++#if AES_BLOCK_SIZE == 32
++
++#define state_in(y,x,k) si(y,x,k,0); si(y,x,k,1); si(y,x,k,2); si(y,x,k,3); \
++ si(y,x,k,4); si(y,x,k,5); si(y,x,k,6); si(y,x,k,7)
++#define state_out(y,x) so(y,x,0); so(y,x,1); so(y,x,2); so(y,x,3); \
++ so(y,x,4); so(y,x,5); so(y,x,6); so(y,x,7)
++#define round(rm,y,x,k) rm(y,x,k,0); rm(y,x,k,1); rm(y,x,k,2); rm(y,x,k,3); \
++ rm(y,x,k,4); rm(y,x,k,5); rm(y,x,k,6); rm(y,x,k,7)
++#else
++
++#define state_in(y,x,k) \
++switch(nc) \
++{ case 8: si(y,x,k,7); si(y,x,k,6); \
++ case 6: si(y,x,k,5); si(y,x,k,4); \
++ case 4: si(y,x,k,3); si(y,x,k,2); \
++ si(y,x,k,1); si(y,x,k,0); \
++}
++
++#define state_out(y,x) \
++switch(nc) \
++{ case 8: so(y,x,7); so(y,x,6); \
++ case 6: so(y,x,5); so(y,x,4); \
++ case 4: so(y,x,3); so(y,x,2); \
++ so(y,x,1); so(y,x,0); \
++}
++
++#if defined(FAST_VARIABLE)
++
++#define round(rm,y,x,k) \
++switch(nc) \
++{ case 8: rm(y,x,k,7); rm(y,x,k,6); \
++ rm(y,x,k,5); rm(y,x,k,4); \
++ rm(y,x,k,3); rm(y,x,k,2); \
++ rm(y,x,k,1); rm(y,x,k,0); \
++ break; \
++ case 6: rm(y,x,k,5); rm(y,x,k,4); \
++ rm(y,x,k,3); rm(y,x,k,2); \
++ rm(y,x,k,1); rm(y,x,k,0); \
++ break; \
++ case 4: rm(y,x,k,3); rm(y,x,k,2); \
++ rm(y,x,k,1); rm(y,x,k,0); \
++ break; \
++}
++#else
++
++#define round(rm,y,x,k) \
++switch(nc) \
++{ case 8: rm(y,x,k,7); rm(y,x,k,6); \
++ case 6: rm(y,x,k,5); rm(y,x,k,4); \
++ case 4: rm(y,x,k,3); rm(y,x,k,2); \
++ rm(y,x,k,1); rm(y,x,k,0); \
++}
++
++#endif
++
++#endif
++#endif
++
++void aes_encrypt(const aes_context *cx, const unsigned char in_blk[], unsigned char out_blk[])
++{ u_int32_t locals(b0, b1);
++ const u_int32_t *kp = cx->aes_e_key;
++
++#if !defined(ONE_TABLE) && !defined(FOUR_TABLES)
++ u_int32_t f2;
++#endif
++
++ state_in(b0, in_blk, kp); kp += nc;
++
++#if defined(UNROLL)
++
++ switch(cx->aes_Nrnd)
++ {
++ case 14: round(fwd_rnd, b1, b0, kp );
++ round(fwd_rnd, b0, b1, kp + nc ); kp += 2 * nc;
++ case 12: round(fwd_rnd, b1, b0, kp );
++ round(fwd_rnd, b0, b1, kp + nc ); kp += 2 * nc;
++ case 10: round(fwd_rnd, b1, b0, kp );
++ round(fwd_rnd, b0, b1, kp + nc);
++ round(fwd_rnd, b1, b0, kp + 2 * nc);
++ round(fwd_rnd, b0, b1, kp + 3 * nc);
++ round(fwd_rnd, b1, b0, kp + 4 * nc);
++ round(fwd_rnd, b0, b1, kp + 5 * nc);
++ round(fwd_rnd, b1, b0, kp + 6 * nc);
++ round(fwd_rnd, b0, b1, kp + 7 * nc);
++ round(fwd_rnd, b1, b0, kp + 8 * nc);
++ round(fwd_lrnd, b0, b1, kp + 9 * nc);
++ }
++
++#elif defined(PARTIAL_UNROLL)
++ { u_int32_t rnd;
++
++ for(rnd = 0; rnd < (cx->aes_Nrnd >> 1) - 1; ++rnd)
++ {
++ round(fwd_rnd, b1, b0, kp);
++ round(fwd_rnd, b0, b1, kp + nc); kp += 2 * nc;
++ }
++
++ round(fwd_rnd, b1, b0, kp);
++ round(fwd_lrnd, b0, b1, kp + nc);
++ }
++#else
++ { u_int32_t rnd;
++
++ for(rnd = 0; rnd < cx->aes_Nrnd - 1; ++rnd)
++ {
++ round(fwd_rnd, b1, b0, kp);
++ l_copy(b0, b1); kp += nc;
++ }
++
++ round(fwd_lrnd, b0, b1, kp);
++ }
++#endif
++
++ state_out(out_blk, b0);
++}
++
++void aes_decrypt(const aes_context *cx, const unsigned char in_blk[], unsigned char out_blk[])
++{ u_int32_t locals(b0, b1);
++ const u_int32_t *kp = cx->aes_d_key;
++
++#if !defined(ONE_TABLE) && !defined(FOUR_TABLES)
++ u_int32_t f2, f4, f8, f9;
++#endif
++
++ state_in(b0, in_blk, kp); kp += nc;
++
++#if defined(UNROLL)
++
++ switch(cx->aes_Nrnd)
++ {
++ case 14: round(inv_rnd, b1, b0, kp );
++ round(inv_rnd, b0, b1, kp + nc ); kp += 2 * nc;
++ case 12: round(inv_rnd, b1, b0, kp );
++ round(inv_rnd, b0, b1, kp + nc ); kp += 2 * nc;
++ case 10: round(inv_rnd, b1, b0, kp );
++ round(inv_rnd, b0, b1, kp + nc);
++ round(inv_rnd, b1, b0, kp + 2 * nc);
++ round(inv_rnd, b0, b1, kp + 3 * nc);
++ round(inv_rnd, b1, b0, kp + 4 * nc);
++ round(inv_rnd, b0, b1, kp + 5 * nc);
++ round(inv_rnd, b1, b0, kp + 6 * nc);
++ round(inv_rnd, b0, b1, kp + 7 * nc);
++ round(inv_rnd, b1, b0, kp + 8 * nc);
++ round(inv_lrnd, b0, b1, kp + 9 * nc);
++ }
++
++#elif defined(PARTIAL_UNROLL)
++ { u_int32_t rnd;
++
++ for(rnd = 0; rnd < (cx->aes_Nrnd >> 1) - 1; ++rnd)
++ {
++ round(inv_rnd, b1, b0, kp);
++ round(inv_rnd, b0, b1, kp + nc); kp += 2 * nc;
++ }
++
++ round(inv_rnd, b1, b0, kp);
++ round(inv_lrnd, b0, b1, kp + nc);
++ }
++#else
++ { u_int32_t rnd;
++
++ for(rnd = 0; rnd < cx->aes_Nrnd - 1; ++rnd)
++ {
++ round(inv_rnd, b1, b0, kp);
++ l_copy(b0, b1); kp += nc;
++ }
++
++ round(inv_lrnd, b0, b1, kp);
++ }
++#endif
++
++ state_out(out_blk, b0);
++}
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/crypto/ciphers/aes/aes_cbc.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,43 @@
++/*
++// I retain copyright in this code but I encourage its free use provided
++// that I don't carry any responsibility for the results. I am especially
++// happy to see it used in free and open source software. If you do use
++// it I would appreciate an acknowledgement of its origin in the code or
++// the product that results and I would also appreciate knowing a little
++// about the use to which it is being put. I am grateful to Frank Yellin
++// for some ideas that are used in this implementation.
++//
++// Dr B. R. Gladman <brg@gladman.uk.net> 6th April 2001.
++//
++// This is an implementation of the AES encryption algorithm (Rijndael)
++// designed by Joan Daemen and Vincent Rijmen. This version is designed
++// to provide both fixed and dynamic block and key lengths and can also
++// run with either big or little endian internal byte order (see aes.h).
++// It inputs block and key lengths in bytes with the legal values being
++// 16, 24 and 32.
++*
++*/
++
++#ifdef __KERNEL__
++#include <linux/types.h>
++#else
++#include <sys/types.h>
++#endif
++#include "crypto/aes_cbc.h"
++#include "crypto/cbc_generic.h"
++
++/* returns bool success */
++int AES_set_key(aes_context *aes_ctx, const u_int8_t *key, int keysize) {
++ aes_set_key(aes_ctx, key, keysize, 0);
++ return 1;
++}
++CBC_IMPL_BLK16(AES_cbc_encrypt, aes_context, u_int8_t *, aes_encrypt, aes_decrypt);
++
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.1 2004/04/06 02:48:12 mcr
++ * pullup of AES cipher from alg-branch.
++ *
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/crypto/ciphers/aes/aes_xcbc_mac.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,67 @@
++#ifdef __KERNEL__
++#include <linux/types.h>
++#include <linux/kernel.h>
++#define DEBUG(x)
++#else
++#include <stdio.h>
++#include <sys/types.h>
++#define DEBUG(x) x
++#endif
++
++#include "crypto/aes.h"
++#include "crypto/aes_xcbc_mac.h"
++
++int AES_xcbc_mac_set_key(aes_context_mac *ctxm, const u_int8_t *key, int keylen)
++{
++ int ret=1;
++ aes_block kn[3] = {
++ { 0x01010101, 0x01010101, 0x01010101, 0x01010101 },
++ { 0x02020202, 0x02020202, 0x02020202, 0x02020202 },
++ { 0x03030303, 0x03030303, 0x03030303, 0x03030303 },
++ };
++ aes_set_key(&ctxm->ctx_k1, key, keylen, 0);
++ aes_encrypt(&ctxm->ctx_k1, (u_int8_t *) kn[0], (u_int8_t *) kn[0]);
++ aes_encrypt(&ctxm->ctx_k1, (u_int8_t *) kn[1], (u_int8_t *) ctxm->k2);
++ aes_encrypt(&ctxm->ctx_k1, (u_int8_t *) kn[2], (u_int8_t *) ctxm->k3);
++ aes_set_key(&ctxm->ctx_k1, (u_int8_t *) kn[0], 16, 0);
++ return ret;
++}
++static void do_pad_xor(u_int8_t *out, const u_int8_t *in, int len) {
++ int pos=0;
++ for (pos=1; pos <= 16; pos++, in++, out++) {
++ if (pos <= len)
++ *out ^= *in;
++ if (pos > len) {
++ DEBUG(printf("put 0x80 at pos=%d\n", pos));
++ *out ^= 0x80;
++ break;
++ }
++ }
++}
++static void xor_block(aes_block res, const aes_block op) {
++ res[0] ^= op[0];
++ res[1] ^= op[1];
++ res[2] ^= op[2];
++ res[3] ^= op[3];
++}
++int AES_xcbc_mac_hash(const aes_context_mac *ctxm, const u_int8_t * in, int ilen, u_int8_t hash[16]) {
++ int ret=ilen;
++ u_int32_t out[4] = { 0, 0, 0, 0 };
++ for (; ilen > 16 ; ilen-=16) {
++ xor_block(out, (const u_int32_t*) &in[0]);
++ aes_encrypt(&ctxm->ctx_k1, in, (u_int8_t *)&out[0]);
++ in+=16;
++ }
++ do_pad_xor((u_int8_t *)&out, in, ilen);
++ if (ilen==16) {
++ DEBUG(printf("using k3\n"));
++ xor_block(out, ctxm->k3);
++ }
++ else
++ {
++ DEBUG(printf("using k2\n"));
++ xor_block(out, ctxm->k2);
++ }
++ aes_encrypt(&ctxm->ctx_k1, (u_int8_t *)out, hash);
++ return ret;
++}
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/crypto/ciphers/aes/test_main.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,41 @@
++#include <stdio.h>
++#include <string.h>
++#include <sys/types.h>
++#include "aes_cbc.h"
++#define AES_BLOCK_SIZE 16
++#define KEY_SIZE 128 /* bits */
++#define KEY "1234567890123456"
++#define STR "hola guaso como estaisss ... 012"
++#define STRSZ (sizeof(STR)-1)
++
++#define EMT_AESCBC_BLKLEN AES_BLOCK_SIZE
++#define AES_CONTEXT_T aes_context
++#define EMT_ESPAES_KEY_SZ 16
++int pretty_print(const unsigned char *buf, int count) {
++ int i=0;
++ for (;i<count;i++) {
++ if (i%8==0) putchar(' ');
++ if (i%16==0) putchar('\n');
++ printf ("%02hhx ", buf[i]);
++ }
++ putchar('\n');
++ return i;
++}
++//#define SIZE STRSZ/2
++#define SIZE STRSZ
++int main() {
++ int ret;
++ char buf0[SIZE+1], buf1[SIZE+1];
++ char IV[AES_BLOCK_SIZE]="\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0";
++ aes_context ac;
++ AES_set_key(&ac, KEY, KEY_SIZE);
++ //pretty_print((char *)&ac.aes_e_key, sizeof(ac.aes_e_key));
++ memset(buf0, 0, sizeof (buf0));
++ memset(buf1, 0, sizeof (buf1));
++ ret=AES_cbc_encrypt(&ac, STR, buf0, SIZE, IV, 1);
++ pretty_print(buf0, SIZE);
++ printf("size=%d ret=%d\n%s\n", SIZE, ret, buf0);
++ ret=AES_cbc_encrypt(&ac, buf0, buf1, SIZE, IV, 0);
++ printf("size=%d ret=%d\n%s\n", SIZE, ret, buf1);
++ return 0;
++}
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/crypto/ciphers/aes/test_main_mac.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,30 @@
++#include <stdio.h>
++#include <sys/types.h>
++#include <string.h>
++#include "aes.h"
++#include "aes_xcbc_mac.h"
++#define STR "Hola guasssso c|mo estais ...012"
++void print_hash(const __u8 *hash) {
++ printf("%08x %08x %08x %08x\n",
++ *(__u32*)(&hash[0]),
++ *(__u32*)(&hash[4]),
++ *(__u32*)(&hash[8]),
++ *(__u32*)(&hash[12]));
++}
++int main(int argc, char *argv[]) {
++ aes_block key= { 0xdeadbeef, 0xceedcaca, 0xcafebabe, 0xff010204 };
++ __u8 hash[16];
++ char *str = argv[1];
++ aes_context_mac ctx;
++ if (str==NULL) {
++ fprintf(stderr, "pasame el str\n");
++ return 255;
++ }
++ AES_xcbc_mac_set_key(&ctx, (__u8 *)&key, sizeof(key));
++ AES_xcbc_mac_hash(&ctx, str, strlen(str), hash);
++ print_hash(hash);
++ str[2]='x';
++ AES_xcbc_mac_hash(&ctx, str, strlen(str), hash);
++ print_hash(hash);
++ return 0;
++}
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/crypto/ciphers/des/COPYRIGHT Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,50 @@
++Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com)
++All rights reserved.
++
++This package is an DES implementation written by Eric Young (eay@cryptsoft.com).
++The implementation was written so as to conform with MIT's libdes.
++
++This library is free for commercial and non-commercial use as long as
++the following conditions are aheared to. The following conditions
++apply to all code found in this distribution.
++
++Copyright remains Eric Young's, and as such any Copyright notices in
++the code are not to be removed.
++If this package is used in a product, Eric Young should be given attribution
++as the author of that the SSL library. This can be in the form of a textual
++message at program startup or in documentation (online or textual) provided
++with the package.
++
++Redistribution and use in source and binary forms, with or without
++modification, are permitted provided that the following conditions
++are met:
++1. Redistributions of source code must retain the copyright
++ notice, this list of conditions and the following disclaimer.
++2. Redistributions in binary form must reproduce the above copyright
++ notice, this list of conditions and the following disclaimer in the
++ documentation and/or other materials provided with the distribution.
++3. All advertising materials mentioning features or use of this software
++ must display the following acknowledgement:
++ This product includes software developed by Eric Young (eay@cryptsoft.com)
++
++THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
++ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
++FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++SUCH DAMAGE.
++
++The license and distribution terms for any publically available version or
++derivative of this code cannot be changed. i.e. this code cannot simply be
++copied and put under another distrubution license
++[including the GNU Public License.]
++
++The reason behind this being stated in this direct manner is past
++experience in code simply being copied and the attribution removed
++from it and then being distributed as part of other packages. This
++implementation was a non-trivial and unpaid effort.
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/crypto/ciphers/des/INSTALL Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,69 @@
++Check the CC and CFLAGS lines in the makefile
++
++If your C library does not support the times(3) function, change the
++#define TIMES to
++#undef TIMES in speed.c
++If it does, check the HZ value for the times(3) function.
++If your system does not define CLK_TCK it will be assumed to
++be 100.0.
++
++If possible use gcc v 2.7.?
++Turn on the maximum optimising (normally '-O3 -fomit-frame-pointer' for gcc)
++In recent times, some system compilers give better performace.
++
++type 'make'
++
++run './destest' to check things are ok.
++run './rpw' to check the tty code for reading passwords works.
++run './speed' to see how fast those optimisations make the library run :-)
++run './des_opts' to determin the best compile time options.
++
++The output from des_opts should be put in the makefile options and des_enc.c
++should be rebuilt. For 64 bit computers, do not use the DES_PTR option.
++For the DEC Alpha, edit des.h and change DES_LONG to 'unsigned int'
++and then you can use the 'DES_PTR' option.
++
++The file options.txt has the options listed for best speed on quite a
++few systems. Look and the options (UNROLL, PTR, RISC2 etc) and then
++turn on the relevent option in the Makefile
++
++There are some special Makefile targets that make life easier.
++make cc - standard cc build
++make gcc - standard gcc build
++make x86-elf - x86 assembler (elf), linux-elf.
++make x86-out - x86 assembler (a.out), FreeBSD
++make x86-solaris- x86 assembler
++make x86-bsdi - x86 assembler (a.out with primative assembler).
++
++If at all possible use the assembler (for Windows NT/95, use
++asm/win32.obj to link with). The x86 assembler is very very fast.
++
++A make install will by default install
++libdes.a in /usr/local/lib/libdes.a
++des in /usr/local/bin/des
++des_crypt.man in /usr/local/man/man3/des_crypt.3
++des.man in /usr/local/man/man1/des.1
++des.h in /usr/include/des.h
++
++des(1) should be compatible with sunOS's but I have been unable to
++test it.
++
++These routines should compile on MSDOS, most 32bit and 64bit version
++of Unix (BSD and SYSV) and VMS, without modification.
++The only problems should be #include files that are in the wrong places.
++
++These routines can be compiled under MSDOS.
++I have successfully encrypted files using des(1) under MSDOS and then
++decrypted the files on a SparcStation.
++I have been able to compile and test the routines with
++Microsoft C v 5.1 and Turbo C v 2.0.
++The code in this library is in no way optimised for the 16bit
++operation of MSDOS.
++
++When building for glibc, ignore all of the above and just unpack into
++glibc-1.??/des and then gmake as per normal.
++
++As a final note on performace. Certain CPUs like sparcs and Alpha often give
++a %10 speed difference depending on the link order. It is rather anoying
++when one program reports 'x' DES encrypts a second and another reports
++'x*0.9' the speed.
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/crypto/ciphers/des/Makefile.objs Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,20 @@
++obj-$(CONFIG_IPSEC_ENC_3DES) += cbc_enc.o
++#obj-$(CONFIG_IPSEC_ENC_3DES) += des_opts.o
++obj-$(CONFIG_IPSEC_ENC_3DES) += ecb_enc.o
++#obj-$(CONFIG_IPSEC_ENC_3DES) += fcrypt.o
++obj-$(CONFIG_IPSEC_ENC_3DES) += set_key.o
++
++ifeq ($(strip ${SUBARCH}),)
++SUBARCH:=${ARCH}
++endif
++
++ifeq (${SUBARCH},i386)
++obj-$(CONFIG_IPSEC_ENC_3DES) += dx86unix.o
++else
++obj-$(CONFIG_IPSEC_ENC_3DES) += des_enc.o
++endif
++
++
++
++
++
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/crypto/ciphers/des/README Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,54 @@
++
++ libdes, Version 4.01 10-Jan-97
++
++ Copyright (c) 1997, Eric Young
++ All rights reserved.
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms specified in COPYRIGHT.
++
++--
++The primary ftp site for this library is
++ftp://ftp.psy.uq.oz.au/pub/Crypto/DES/libdes-x.xx.tar.gz
++libdes is now also shipped with SSLeay. Primary ftp site of
++ftp://ftp.psy.uq.oz.au/pub/Crypto/SSL/SSLeay-x.x.x.tar.gz
++
++The best way to build this library is to build it as part of SSLeay.
++
++This kit builds a DES encryption library and a DES encryption program.
++It supports ecb, cbc, ofb, cfb, triple ecb, triple cbc, triple ofb,
++triple cfb, desx, and MIT's pcbc encryption modes and also has a fast
++implementation of crypt(3).
++It contains support routines to read keys from a terminal,
++generate a random key, generate a key from an arbitrary length string,
++read/write encrypted data from/to a file descriptor.
++
++The implementation was written so as to conform with the manual entry
++for the des_crypt(3) library routines from MIT's project Athena.
++
++destest should be run after compilation to test the des routines.
++rpw should be run after compilation to test the read password routines.
++The des program is a replacement for the sun des command. I believe it
++conforms to the sun version.
++
++The Imakefile is setup for use in the kerberos distribution.
++
++These routines are best compiled with gcc or any other good
++optimising compiler.
++Just turn you optimiser up to the highest settings and run destest
++after the build to make sure everything works.
++
++I believe these routines are close to the fastest and most portable DES
++routines that use small lookup tables (4.5k) that are publicly available.
++The fcrypt routine is faster than ufc's fcrypt (when compiling with
++gcc2 -O2) on the sparc 2 (1410 vs 1270) but is not so good on other machines
++(on a sun3/260 168 vs 336). It is a function of CPU on chip cache size.
++[ 10-Jan-97 and a function of an incorrect speed testing program in
++ ufc which gave much better test figures that reality ].
++
++It is worth noting that on sparc and Alpha CPUs, performance of the DES
++library can vary by upto %10 due to the positioning of files after application
++linkage.
++
++Eric Young (eay@cryptsoft.com)
++
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/crypto/ciphers/des/README.freeswan Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,33 @@
++The only changes the FreeS/WAN project has made to libdes-lite 4.04b are:
++
++We #ifdef-ed the declaration of DES_LONG in des.h, so it's more efficient
++on the Alpha, instead of just noting the issue in a comment.
++
++We #ifdef-ed out the des_options() function in ecb_enc.c, because we don't
++use it, and its call to sprintf() can cause subtle difficulties when KLIPS
++is built as a module (depending on details of Linux configuration options).
++
++We changed some instances of CC=$(CC) in the Makefile to CC='$(CC)' to make
++it cope better with Linux kernel Makefile stupidities, and took out an
++explicit CC=gcc (unwise on systems with strange compilers).
++
++We deleted some references to <stdio.h> and <stdlib.h>, and a declaration
++of one function found only in the full libdes (not in libdes-lite), to
++avoid dragging in bits of stdio/stdlib unnecessarily. (Our thanks to Hans
++Schultz for spotting this and pointing out the fixes.)
++
++We deleted a couple of .obj files in the asm subdirectory, which appear to
++have been included in the original library by accident.
++
++We have added an include of our Makefile.inc file, to permit overriding
++things like choice of compiler (although the libdes Makefile would
++probably need some work to make this effective).
++
++
++
++Note that Eric Young is no longer at the email address listed in these
++files, and is (alas) no longer working on free crypto software.
++
++
++
++This file is RCSID $Id$
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/crypto/ciphers/des/VERSION Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,406 @@
++Version 4.04
++ Fixed a few tests in destest. Also added x86 assember for
++ des_ncbc_encrypt() which is the standard cbc mode function.
++ This makes a very very large performace difference.
++ Ariel Glenn ariel@columbia.edu reports that the terminal
++ 'turn echo off' can return (errno == EINVAL) under solaris
++ when redirection is used. So I now catch that as well as ENOTTY.
++
++
++Version 4.03
++ Left a static out of enc_write.c, which caused to buffer to be
++ continiously malloc()ed. Does anyone use these functions? I keep
++ on feeling like removing them since I only had these in there
++ for a version of kerberised login. Anyway, this was pointed out
++ by Theo de Raadt <deraadt@cvs.openbsd.org>
++ The 'n' bit ofb code was wrong, it was not shifting the shift
++ register. It worked correctly for n == 64. Thanks to
++ Gigi Ankeny <Gigi.Ankeny@Eng.Sun.COM> for pointing this one out.
++
++Version 4.02
++ I was doing 'if (memcmp(weak_keys[i],key,sizeof(key)) == 0)'
++ when checking for weak keys which is wrong :-(, pointed out by
++ Markus F.X.J. Oberhumer <markus.oberhumer@jk.uni-linz.ac.at>.
++
++Version 4.01
++ Even faster inner loop in the DES assembler for x86 and a modification
++ for IP/FP which is faster on x86. Both of these changes are
++ from Svend Olaf Mikkelsen <svolaf@inet.uni-c.dk>. His
++ changes make the assembler run %40 faster on a pentium. This is just
++ a case of getting the instruction sequence 'just right'.
++ All credit to 'Svend' :-)
++ Quite a few special x86 'make' targets.
++ A libdes-l (lite) distribution.
++
++Version 4.00
++ After a bit of a pause, I'll up the major version number since this
++ is mostly a performace release. I've added x86 assembler and
++ added more options for performance. A %28 speedup for gcc
++ on a pentium and the assembler is a %50 speedup.
++ MIPS CPU's, sparc and Alpha are the main CPU's with speedups.
++ Run des_opts to work out which options should be used.
++ DES_RISC1/DES_RISC2 use alternative inner loops which use
++ more registers but should give speedups on any CPU that does
++ dual issue (pentium). DES_UNROLL unrolls the inner loop,
++ which costs in code size.
++
++Version 3.26
++ I've finally removed one of the shifts in D_ENCRYPT. This
++ meant I've changed the des_SPtrans table (spr.h), the set_key()
++ function and some things in des_enc.c. This has definitly
++ made things faster :-). I've known about this one for some
++ time but I've been too lazy to follow it up :-).
++ Noticed that in the D_ENCRYPT() macro, we can just do L^=(..)^(..)^..
++ instead of L^=((..)|(..)|(..).. This should save a register at
++ least.
++ Assember for x86. The file to replace is des_enc.c, which is replaced
++ by one of the assembler files found in asm. Look at des/asm/readme
++ for more info.
++
++ /* Modification to fcrypt so it can be compiled to support
++ HPUX 10.x's long password format, define -DLONGCRYPT to use this.
++ Thanks to Jens Kupferschmidt <bt1cu@hpboot.rz.uni-leipzig.de>. */
++
++ SIGWINCH case put in des_read_passwd() so the function does not
++ 'exit' if this function is recieved.
++
++Version 3.25 17/07/96
++ Modified read_pwd.c so that stdin can be read if not a tty.
++ Thanks to Jeff Barber <jeffb@issl.atl.hp.com> for the patches.
++ des_init_random_number_generator() shortened due to VMS linker
++ limits.
++ Added RSA's DESX cbc mode. It is a form of cbc encryption, with 2
++ 8 byte quantites xored before and after encryption.
++ des_xcbc_encryption() - the name is funny to preserve the des_
++ prefix on all functions.
++
++Version 3.24 20/04/96
++ The DES_PTR macro option checked and used by SSLeay configuration
++
++Version 3.23 11/04/96
++ Added DES_LONG. If defined to 'unsigned int' on the DEC Alpha,
++ it gives a %20 speedup :-)
++ Fixed the problem with des.pl under perl5. The patches were
++ sent by Ed Kubaitis (ejk@uiuc.edu).
++ if fcrypt.c, changed values to handle illegal salt values the way
++ normal crypt() implementations do. Some programs apparently use
++ them :-(. The patch was sent by Bjorn Gronvall <bg@sics.se>
++
++Version 3.22 29/11/95
++ Bug in des(1), an error with the uuencoding stuff when the
++ 'data' is small, thanks to Geoff Keating <keagchon@mehta.anu.edu.au>
++ for the patch.
++
++Version 3.21 22/11/95
++ After some emailing back and forth with
++ Colin Plumb <colin@nyx10.cs.du.edu>, I've tweaked a few things
++ and in a future version I will probably put in some of the
++ optimisation he suggested for use with the DES_USE_PTR option.
++ Extra routines from Mark Murray <mark@grondar.za> for use in
++ freeBSD. They mostly involve random number generation for use
++ with kerberos. They involve evil machine specific system calls
++ etc so I would normally suggest pushing this stuff into the
++ application and/or using RAND_seed()/RAND_bytes() if you are
++ using this DES library as part of SSLeay.
++ Redone the read_pw() function so that it is cleaner and
++ supports termios, thanks to Sameer Parekh <sameer@c2.org>
++ for the initial patches for this.
++ Renamed 3ecb_encrypt() to ecb3_encrypt(). This has been
++ done just to make things more consistent.
++ I have also now added triple DES versions of cfb and ofb.
++
++Version 3.20
++ Damn, Damn, Damn, as pointed out by Mike_Spreitzer.PARC@xerox.com,
++ my des_random_seed() function was only copying 4 bytes of the
++ passed seed into the init structure. It is now fixed to copy 8.
++ My own suggestion is to used something like MD5 :-)
++
++Version 3.19
++ While looking at my code one day, I though, why do I keep on
++ calling des_encrypt(in,out,ks,enc) when every function that
++ calls it has in and out the same. So I dropped the 'out'
++ parameter, people should not be using this function.
++
++Version 3.18 30/08/95
++ Fixed a few bit with the distribution and the filenames.
++ 3.17 had been munged via a move to DOS and back again.
++ NO CODE CHANGES
++
++Version 3.17 14/07/95
++ Fixed ede3 cbc which I had broken in 3.16. I have also
++ removed some unneeded variables in 7-8 of the routines.
++
++Version 3.16 26/06/95
++ Added des_encrypt2() which does not use IP/FP, used by triple
++ des routines. Tweaked things a bit elsewhere. %13 speedup on
++ sparc and %6 on a R4400 for ede3 cbc mode.
++
++Version 3.15 06/06/95
++ Added des_ncbc_encrypt(), it is des_cbc mode except that it is
++ 'normal' and copies the new iv value back over the top of the
++ passed parameter.
++ CHANGED des_ede3_cbc_encrypt() so that it too now overwrites
++ the iv. THIS WILL BREAK EXISTING CODE, but since this function
++ only new, I feel I can change it, not so with des_cbc_encrypt :-(.
++ I need to update the documentation.
++
++Version 3.14 31/05/95
++ New release upon the world, as part of my SSL implementation.
++ New copyright and usage stuff. Basically free for all to use
++ as long as you say it came from me :-)
++
++Version 3.13 31/05/95
++ A fix in speed.c, if HZ is not defined, I set it to 100.0
++ which is reasonable for most unixes except SunOS 4.x.
++ I now have a #ifdef sun but timing for SunOS 4.x looked very
++ good :-(. At my last job where I used SunOS 4.x, it was
++ defined to be 60.0 (look at the old INSTALL documentation), at
++ the last release had it changed to 100.0 since I now work with
++ Solaris2 and SVR4 boxes.
++ Thanks to Rory Chisholm <rchishol@math.ethz.ch> for pointing this
++ one out.
++
++Version 3.12 08/05/95
++ As pointed out by The Crypt Keeper <tck@bend.UCSD.EDU>,
++ my D_ENCRYPT macro in crypt() had an un-necessary variable.
++ It has been removed.
++
++Version 3.11 03/05/95
++ Added des_ede3_cbc_encrypt() which is cbc mode des with 3 keys
++ and one iv. It is a standard and I needed it for my SSL code.
++ It makes more sense to use this for triple DES than
++ 3cbc_encrypt(). I have also added (or should I say tested :-)
++ cfb64_encrypt() which is cfb64 but it will encrypt a partial
++ number of bytes - 3 bytes in 3 bytes out. Again this is for
++ my SSL library, as a form of encryption to use with SSL
++ telnet.
++
++Version 3.10 22/03/95
++ Fixed a bug in 3cbc_encrypt() :-(. When making repeated calls
++ to cbc3_encrypt, the 2 iv values that were being returned to
++ be used in the next call were reversed :-(.
++ Many thanks to Bill Wade <wade@Stoner.COM> for pointing out
++ this error.
++
++Version 3.09 01/02/95
++ Fixed des_random_key to far more random, it was rather feeble
++ with regards to picking the initial seed. The problem was
++ pointed out by Olaf Kirch <okir@monad.swb.de>.
++
++Version 3.08 14/12/94
++ Added Makefile.PL so libdes can be built into perl5.
++ Changed des_locl.h so RAND is always defined.
++
++Version 3.07 05/12/94
++ Added GNUmake and stuff so the library can be build with
++ glibc.
++
++Version 3.06 30/08/94
++ Added rpc_enc.c which contains _des_crypt. This is for use in
++ secure_rpc v 4.0
++ Finally fixed the cfb_enc problems.
++ Fixed a few parameter parsing bugs in des (-3 and -b), thanks
++ to Rob McMillan <R.McMillan@its.gu.edu.au>
++
++Version 3.05 21/04/94
++ for unsigned long l; gcc does not produce ((l>>34) == 0)
++ This causes bugs in cfb_enc.
++ Thanks to Hadmut Danisch <danisch@ira.uka.de>
++
++Version 3.04 20/04/94
++ Added a version number to des.c and libdes.a
++
++Version 3.03 12/01/94
++ Fixed a bug in non zero iv in 3cbc_enc.
++
++Version 3.02 29/10/93
++ I now work in a place where there are 6+ architectures and 14+
++ OS versions :-).
++ Fixed TERMIO definition so the most sys V boxes will work :-)
++
++Release upon comp.sources.misc
++Version 3.01 08/10/93
++ Added des_3cbc_encrypt()
++
++Version 3.00 07/10/93
++ Fixed up documentation.
++ quad_cksum definitely compatible with MIT's now.
++
++Version 2.30 24/08/93
++ Triple DES now defaults to triple cbc but can do triple ecb
++ with the -b flag.
++ Fixed some MSDOS uuen/uudecoding problems, thanks to
++ Added prototypes.
++
++Version 2.22 29/06/93
++ Fixed a bug in des_is_weak_key() which stopped it working :-(
++ thanks to engineering@MorningStar.Com.
++
++Version 2.21 03/06/93
++ des(1) with no arguments gives quite a bit of help.
++ Added -c (generate ckecksum) flag to des(1).
++ Added -3 (triple DES) flag to des(1).
++ Added cfb and ofb routines to the library.
++
++Version 2.20 11/03/93
++ Added -u (uuencode) flag to des(1).
++ I have been playing with byte order in quad_cksum to make it
++ compatible with MIT's version. All I can say is avid this
++ function if possible since MIT's output is endian dependent.
++
++Version 2.12 14/10/92
++ Added MSDOS specific macro in ecb_encrypt which gives a %70
++ speed up when the code is compiled with turbo C.
++
++Version 2.11 12/10/92
++ Speedup in set_key (recoding of PC-1)
++ I now do it in 47 simple operations, down from 60.
++ Thanks to John Fletcher (john_fletcher@lccmail.ocf.llnl.gov)
++ for motivating me to look for a faster system :-)
++ The speedup is probably less that 1% but it is still 13
++ instructions less :-).
++
++Version 2.10 06/10/92
++ The code now works on the 64bit ETA10 and CRAY without modifications or
++ #defines. I believe the code should work on any machine that
++ defines long, int or short to be 8 bytes long.
++ Thanks to Shabbir J. Safdar (shabby@mentor.cc.purdue.edu)
++ for helping me fix the code to run on 64bit machines (he had
++ access to an ETA10).
++ Thanks also to John Fletcher <john_fletcher@lccmail.ocf.llnl.gov>
++ for testing the routines on a CRAY.
++ read_password.c has been renamed to read_passwd.c
++ string_to_key.c has been renamed to string2key.c
++
++Version 2.00 14/09/92
++ Made mods so that the library should work on 64bit CPU's.
++ Removed all my uchar and ulong defs. To many different
++ versions of unix define them in their header files in too many
++ different combinations :-)
++ IRIX - Sillicon Graphics mods (mostly in read_password.c).
++ Thanks to Andrew Daviel (advax@erich.triumf.ca)
++
++Version 1.99 26/08/92
++ Fixed a bug or 2 in enc_read.c
++ Fixed a bug in enc_write.c
++ Fixed a pseudo bug in fcrypt.c (very obscure).
++
++Version 1.98 31/07/92
++ Support for the ETA10. This is a strange machine that defines
++ longs and ints as 8 bytes and shorts as 4 bytes.
++ Since I do evil things with long * that assume that they are 4
++ bytes. Look in the Makefile for the option to compile for
++ this machine. quad_cksum appears to have problems but I
++ will don't have the time to fix it right now, and this is not
++ a function that uses DES and so will not effect the main uses
++ of the library.
++
++Version 1.97 20/05/92 eay
++ Fixed the Imakefile and made some changes to des.h to fix some
++ problems when building this package with Kerberos v 4.
++
++Version 1.96 18/05/92 eay
++ Fixed a small bug in string_to_key() where problems could
++ occur if des_check_key was set to true and the string
++ generated a weak key.
++
++Patch2 posted to comp.sources.misc
++Version 1.95 13/05/92 eay
++ Added an alternative version of the D_ENCRYPT macro in
++ ecb_encrypt and fcrypt. Depending on the compiler, one version or the
++ other will be faster. This was inspired by
++ Dana How <how@isl.stanford.edu>, and her pointers about doing the
++ *(ulong *)((uchar *)ptr+(value&0xfc))
++ vs
++ ptr[value&0x3f]
++ to stop the C compiler doing a <<2 to convert the long array index.
++
++Version 1.94 05/05/92 eay
++ Fixed an incompatibility between my string_to_key and the MIT
++ version. When the key is longer than 8 chars, I was wrapping
++ with a different method. To use the old version, define
++ OLD_STR_TO_KEY in the makefile. Thanks to
++ viktor@newsu.shearson.com (Viktor Dukhovni).
++
++Version 1.93 28/04/92 eay
++ Fixed the VMS mods so that echo is now turned off in
++ read_password. Thanks again to brennan@coco.cchs.su.oz.AU.
++ MSDOS support added. The routines can be compiled with
++ Turbo C (v2.0) and MSC (v5.1). Make sure MSDOS is defined.
++
++Patch1 posted to comp.sources.misc
++Version 1.92 13/04/92 eay
++ Changed D_ENCRYPT so that the rotation of R occurs outside of
++ the loop. This required rotating all the longs in sp.h (now
++ called spr.h). Thanks to Richard Outerbridge <71755.204@CompuServe.COM>
++ speed.c has been changed so it will work without SIGALRM. If
++ times(3) is not present it will try to use ftime() instead.
++
++Version 1.91 08/04/92 eay
++ Added -E/-D options to des(1) so it can use string_to_key.
++ Added SVR4 mods suggested by witr@rwwa.COM
++ Added VMS mods suggested by brennan@coco.cchs.su.oz.AU. If
++ anyone knows how to turn of tty echo in VMS please tell me or
++ implement it yourself :-).
++ Changed FILE *IN/*OUT to *DES_IN/*DES_OUT since it appears VMS
++ does not like IN/OUT being used.
++
++Libdes posted to comp.sources.misc
++Version 1.9 24/03/92 eay
++ Now contains a fast small crypt replacement.
++ Added des(1) command.
++ Added des_rw_mode so people can use cbc encryption with
++ enc_read and enc_write.
++
++Version 1.8 15/10/91 eay
++ Bug in cbc_cksum.
++ Many thanks to Keith Reynolds (keithr@sco.COM) for pointing this
++ one out.
++
++Version 1.7 24/09/91 eay
++ Fixed set_key :-)
++ set_key is 4 times faster and takes less space.
++ There are a few minor changes that could be made.
++
++Version 1.6 19/09/1991 eay
++ Finally go IP and FP finished.
++ Now I need to fix set_key.
++ This version is quite a bit faster that 1.51
++
++Version 1.52 15/06/1991 eay
++ 20% speedup in ecb_encrypt by changing the E bit selection
++ to use 2 32bit words. This also required modification of the
++ sp table. There is still a way to speedup the IP and IP-1
++ (hints from outer@sq.com) still working on this one :-(.
++
++Version 1.51 07/06/1991 eay
++ Faster des_encrypt by loop unrolling
++ Fixed bug in quad_cksum.c (thanks to hughes@logos.ucs.indiana.edu)
++
++Version 1.50 28/05/1991 eay
++ Optimised the code a bit more for the sparc. I have improved the
++ speed of the inner des_encrypt by speeding up the initial and
++ final permutations.
++
++Version 1.40 23/10/1990 eay
++ Fixed des_random_key, it did not produce a random key :-(
++
++Version 1.30 2/10/1990 eay
++ Have made des_quad_cksum the same as MIT's, the full package
++ should be compatible with MIT's
++ Have tested on a DECstation 3100
++ Still need to fix des_set_key (make it faster).
++ Does des_cbc_encrypts at 70.5k/sec on a 3100.
++
++Version 1.20 18/09/1990 eay
++ Fixed byte order dependencies.
++ Fixed (I hope) all the word alignment problems.
++ Speedup in des_ecb_encrypt.
++
++Version 1.10 11/09/1990 eay
++ Added des_enc_read and des_enc_write.
++ Still need to fix des_quad_cksum.
++ Still need to document des_enc_read and des_enc_write.
++
++Version 1.00 27/08/1990 eay
++
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/crypto/ciphers/des/asm/crypt586.pl Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,204 @@
++#!/usr/bin/perl
++#
++# The inner loop instruction sequence and the IP/FP modifications are from
++# Svend Olaf Mikkelsen <svolaf@inet.uni-c.dk>
++# I've added the stuff needed for crypt() but I've not worried about making
++# things perfect.
++#
++
++push(@INC,"perlasm","../../perlasm");
++require "x86asm.pl";
++
++&asm_init($ARGV[0],"crypt586.pl");
++
++$L="edi";
++$R="esi";
++
++&external_label("des_SPtrans");
++&fcrypt_body("fcrypt_body");
++&asm_finish();
++
++sub fcrypt_body
++ {
++ local($name,$do_ip)=@_;
++
++ &function_begin($name,"EXTRN _des_SPtrans:DWORD");
++
++ &comment("");
++ &comment("Load the 2 words");
++ $ks="ebp";
++
++ &xor( $L, $L);
++ &xor( $R, $R);
++ &mov($ks,&wparam(1));
++
++ &push(25); # add a variable
++
++ &set_label("start");
++ for ($i=0; $i<16; $i+=2)
++ {
++ &comment("");
++ &comment("Round $i");
++ &D_ENCRYPT($i,$L,$R,$i*2,$ks,"des_SPtrans","eax","ebx","ecx","edx");
++
++ &comment("");
++ &comment("Round ".sprintf("%d",$i+1));
++ &D_ENCRYPT($i+1,$R,$L,($i+1)*2,$ks,"des_SPtrans","eax","ebx","ecx","edx");
++ }
++ &mov("ebx", &swtmp(0));
++ &mov("eax", $L);
++ &dec("ebx");
++ &mov($L, $R);
++ &mov($R, "eax");
++ &mov(&swtmp(0), "ebx");
++ &jnz(&label("start"));
++
++ &comment("");
++ &comment("FP");
++ &mov("edx",&wparam(0));
++
++ &FP_new($R,$L,"eax",3);
++ &mov(&DWP(0,"edx","",0),"eax");
++ &mov(&DWP(4,"edx","",0),$L);
++
++ &pop("ecx"); # remove variable
++
++ &function_end($name);
++ }
++
++sub D_ENCRYPT
++ {
++ local($r,$L,$R,$S,$ks,$desSP,$u,$tmp1,$tmp2,$t)=@_;
++
++ &mov( $u, &wparam(2)); # 2
++ &mov( $t, $R);
++ &shr( $t, 16); # 1
++ &mov( $tmp2, &wparam(3)); # 2
++ &xor( $t, $R); # 1
++
++ &and( $u, $t); # 2
++ &and( $t, $tmp2); # 2
++
++ &mov( $tmp1, $u);
++ &shl( $tmp1, 16); # 1
++ &mov( $tmp2, $t);
++ &shl( $tmp2, 16); # 1
++ &xor( $u, $tmp1); # 2
++ &xor( $t, $tmp2); # 2
++ &mov( $tmp1, &DWP(&n2a($S*4),$ks,"",0)); # 2
++ &xor( $u, $tmp1);
++ &mov( $tmp2, &DWP(&n2a(($S+1)*4),$ks,"",0)); # 2
++ &xor( $u, $R);
++ &xor( $t, $R);
++ &xor( $t, $tmp2);
++
++ &and( $u, "0xfcfcfcfc" ); # 2
++ &xor( $tmp1, $tmp1); # 1
++ &and( $t, "0xcfcfcfcf" ); # 2
++ &xor( $tmp2, $tmp2);
++ &movb( &LB($tmp1), &LB($u) );
++ &movb( &LB($tmp2), &HB($u) );
++ &rotr( $t, 4 );
++ &mov( $ks, &DWP(" $desSP",$tmp1,"",0));
++ &movb( &LB($tmp1), &LB($t) );
++ &xor( $L, $ks);
++ &mov( $ks, &DWP("0x200+$desSP",$tmp2,"",0));
++ &xor( $L, $ks);
++ &movb( &LB($tmp2), &HB($t) );
++ &shr( $u, 16);
++ &mov( $ks, &DWP("0x100+$desSP",$tmp1,"",0));
++ &xor( $L, $ks);
++ &movb( &LB($tmp1), &HB($u) );
++ &shr( $t, 16);
++ &mov( $ks, &DWP("0x300+$desSP",$tmp2,"",0));
++ &xor( $L, $ks);
++ &mov( $ks, &wparam(1));
++ &movb( &LB($tmp2), &HB($t) );
++ &and( $u, "0xff" );
++ &and( $t, "0xff" );
++ &mov( $tmp1, &DWP("0x600+$desSP",$tmp1,"",0));
++ &xor( $L, $tmp1);
++ &mov( $tmp1, &DWP("0x700+$desSP",$tmp2,"",0));
++ &xor( $L, $tmp1);
++ &mov( $tmp1, &DWP("0x400+$desSP",$u,"",0));
++ &xor( $L, $tmp1);
++ &mov( $tmp1, &DWP("0x500+$desSP",$t,"",0));
++ &xor( $L, $tmp1);
++ }
++
++sub n2a
++ {
++ sprintf("%d",$_[0]);
++ }
++
++# now has a side affect of rotating $a by $shift
++sub R_PERM_OP
++ {
++ local($a,$b,$tt,$shift,$mask,$last)=@_;
++
++ &rotl( $a, $shift ) if ($shift != 0);
++ &mov( $tt, $a );
++ &xor( $a, $b );
++ &and( $a, $mask );
++ if ($notlast eq $b)
++ {
++ &xor( $b, $a );
++ &xor( $tt, $a );
++ }
++ else
++ {
++ &xor( $tt, $a );
++ &xor( $b, $a );
++ }
++ &comment("");
++ }
++
++sub IP_new
++ {
++ local($l,$r,$tt,$lr)=@_;
++
++ &R_PERM_OP($l,$r,$tt, 4,"0xf0f0f0f0",$l);
++ &R_PERM_OP($r,$tt,$l,20,"0xfff0000f",$l);
++ &R_PERM_OP($l,$tt,$r,14,"0x33333333",$r);
++ &R_PERM_OP($tt,$r,$l,22,"0x03fc03fc",$r);
++ &R_PERM_OP($l,$r,$tt, 9,"0xaaaaaaaa",$r);
++
++ if ($lr != 3)
++ {
++ if (($lr-3) < 0)
++ { &rotr($tt, 3-$lr); }
++ else { &rotl($tt, $lr-3); }
++ }
++ if ($lr != 2)
++ {
++ if (($lr-2) < 0)
++ { &rotr($r, 2-$lr); }
++ else { &rotl($r, $lr-2); }
++ }
++ }
++
++sub FP_new
++ {
++ local($l,$r,$tt,$lr)=@_;
++
++ if ($lr != 2)
++ {
++ if (($lr-2) < 0)
++ { &rotl($r, 2-$lr); }
++ else { &rotr($r, $lr-2); }
++ }
++ if ($lr != 3)
++ {
++ if (($lr-3) < 0)
++ { &rotl($l, 3-$lr); }
++ else { &rotr($l, $lr-3); }
++ }
++
++ &R_PERM_OP($l,$r,$tt, 0,"0xaaaaaaaa",$r);
++ &R_PERM_OP($tt,$r,$l,23,"0x03fc03fc",$r);
++ &R_PERM_OP($l,$r,$tt,10,"0x33333333",$l);
++ &R_PERM_OP($r,$tt,$l,18,"0xfff0000f",$l);
++ &R_PERM_OP($l,$tt,$r,12,"0xf0f0f0f0",$r);
++ &rotr($tt , 4);
++ }
++
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/crypto/ciphers/des/asm/des-586.pl Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,251 @@
++#!/usr/bin/perl
++#
++# The inner loop instruction sequence and the IP/FP modifications are from
++# Svend Olaf Mikkelsen <svolaf@inet.uni-c.dk>
++#
++
++push(@INC,"perlasm","../../perlasm");
++require "x86asm.pl";
++require "cbc.pl";
++require "desboth.pl";
++
++# base code is in microsft
++# op dest, source
++# format.
++#
++
++&asm_init($ARGV[0],"des-586.pl");
++
++$L="edi";
++$R="esi";
++
++&external_label("des_SPtrans");
++&des_encrypt("des_encrypt",1);
++&des_encrypt("des_encrypt2",0);
++&des_encrypt3("des_encrypt3",1);
++&des_encrypt3("des_decrypt3",0);
++&cbc("des_ncbc_encrypt","des_encrypt","des_encrypt",0,4,5,3,5,-1);
++&cbc("des_ede3_cbc_encrypt","des_encrypt3","des_decrypt3",0,6,7,3,4,5);
++
++&asm_finish();
++
++sub des_encrypt
++ {
++ local($name,$do_ip)=@_;
++
++ &function_begin_B($name,"EXTRN _des_SPtrans:DWORD");
++
++ &push("esi");
++ &push("edi");
++
++ &comment("");
++ &comment("Load the 2 words");
++ $ks="ebp";
++
++ if ($do_ip)
++ {
++ &mov($R,&wparam(0));
++ &xor( "ecx", "ecx" );
++
++ &push("ebx");
++ &push("ebp");
++
++ &mov("eax",&DWP(0,$R,"",0));
++ &mov("ebx",&wparam(2)); # get encrypt flag
++ &mov($L,&DWP(4,$R,"",0));
++ &comment("");
++ &comment("IP");
++ &IP_new("eax",$L,$R,3);
++ }
++ else
++ {
++ &mov("eax",&wparam(0));
++ &xor( "ecx", "ecx" );
++
++ &push("ebx");
++ &push("ebp");
++
++ &mov($R,&DWP(0,"eax","",0));
++ &mov("ebx",&wparam(2)); # get encrypt flag
++ &rotl($R,3);
++ &mov($L,&DWP(4,"eax","",0));
++ &rotl($L,3);
++ }
++
++ &mov( $ks, &wparam(1) );
++ &cmp("ebx","0");
++ &je(&label("start_decrypt"));
++
++ for ($i=0; $i<16; $i+=2)
++ {
++ &comment("");
++ &comment("Round $i");
++ &D_ENCRYPT($i,$L,$R,$i*2,$ks,"des_SPtrans","eax","ebx","ecx","edx");
++
++ &comment("");
++ &comment("Round ".sprintf("%d",$i+1));
++ &D_ENCRYPT($i+1,$R,$L,($i+1)*2,$ks,"des_SPtrans","eax","ebx","ecx","edx");
++ }
++ &jmp(&label("end"));
++
++ &set_label("start_decrypt");
++
++ for ($i=15; $i>0; $i-=2)
++ {
++ &comment("");
++ &comment("Round $i");
++ &D_ENCRYPT(15-$i,$L,$R,$i*2,$ks,"des_SPtrans","eax","ebx","ecx","edx");
++ &comment("");
++ &comment("Round ".sprintf("%d",$i-1));
++ &D_ENCRYPT(15-$i+1,$R,$L,($i-1)*2,$ks,"des_SPtrans","eax","ebx","ecx","edx");
++ }
++
++ &set_label("end");
++
++ if ($do_ip)
++ {
++ &comment("");
++ &comment("FP");
++ &mov("edx",&wparam(0));
++ &FP_new($L,$R,"eax",3);
++
++ &mov(&DWP(0,"edx","",0),"eax");
++ &mov(&DWP(4,"edx","",0),$R);
++ }
++ else
++ {
++ &comment("");
++ &comment("Fixup");
++ &rotr($L,3); # r
++ &mov("eax",&wparam(0));
++ &rotr($R,3); # l
++ &mov(&DWP(0,"eax","",0),$L);
++ &mov(&DWP(4,"eax","",0),$R);
++ }
++
++ &pop("ebp");
++ &pop("ebx");
++ &pop("edi");
++ &pop("esi");
++ &ret();
++
++ &function_end_B($name);
++ }
++
++sub D_ENCRYPT
++ {
++ local($r,$L,$R,$S,$ks,$desSP,$u,$tmp1,$tmp2,$t)=@_;
++
++ &mov( $u, &DWP(&n2a($S*4),$ks,"",0));
++ &xor( $tmp1, $tmp1);
++ &mov( $t, &DWP(&n2a(($S+1)*4),$ks,"",0));
++ &xor( $u, $R);
++ &xor( $t, $R);
++ &and( $u, "0xfcfcfcfc" );
++ &and( $t, "0xcfcfcfcf" );
++ &movb( &LB($tmp1), &LB($u) );
++ &movb( &LB($tmp2), &HB($u) );
++ &rotr( $t, 4 );
++ &mov( $ks, &DWP(" $desSP",$tmp1,"",0));
++ &movb( &LB($tmp1), &LB($t) );
++ &xor( $L, $ks);
++ &mov( $ks, &DWP("0x200+$desSP",$tmp2,"",0));
++ &xor( $L, $ks); ######
++ &movb( &LB($tmp2), &HB($t) );
++ &shr( $u, 16);
++ &mov( $ks, &DWP("0x100+$desSP",$tmp1,"",0));
++ &xor( $L, $ks); ######
++ &movb( &LB($tmp1), &HB($u) );
++ &shr( $t, 16);
++ &mov( $ks, &DWP("0x300+$desSP",$tmp2,"",0));
++ &xor( $L, $ks);
++ &mov( $ks, &wparam(1) );
++ &movb( &LB($tmp2), &HB($t) );
++ &and( $u, "0xff" );
++ &and( $t, "0xff" );
++ &mov( $tmp1, &DWP("0x600+$desSP",$tmp1,"",0));
++ &xor( $L, $tmp1);
++ &mov( $tmp1, &DWP("0x700+$desSP",$tmp2,"",0));
++ &xor( $L, $tmp1);
++ &mov( $tmp1, &DWP("0x400+$desSP",$u,"",0));
++ &xor( $L, $tmp1);
++ &mov( $tmp1, &DWP("0x500+$desSP",$t,"",0));
++ &xor( $L, $tmp1);
++ }
++
++sub n2a
++ {
++ sprintf("%d",$_[0]);
++ }
++
++# now has a side affect of rotating $a by $shift
++sub R_PERM_OP
++ {
++ local($a,$b,$tt,$shift,$mask,$last)=@_;
++
++ &rotl( $a, $shift ) if ($shift != 0);
++ &mov( $tt, $a );
++ &xor( $a, $b );
++ &and( $a, $mask );
++ if (!$last eq $b)
++ {
++ &xor( $b, $a );
++ &xor( $tt, $a );
++ }
++ else
++ {
++ &xor( $tt, $a );
++ &xor( $b, $a );
++ }
++ &comment("");
++ }
++
++sub IP_new
++ {
++ local($l,$r,$tt,$lr)=@_;
++
++ &R_PERM_OP($l,$r,$tt, 4,"0xf0f0f0f0",$l);
++ &R_PERM_OP($r,$tt,$l,20,"0xfff0000f",$l);
++ &R_PERM_OP($l,$tt,$r,14,"0x33333333",$r);
++ &R_PERM_OP($tt,$r,$l,22,"0x03fc03fc",$r);
++ &R_PERM_OP($l,$r,$tt, 9,"0xaaaaaaaa",$r);
++
++ if ($lr != 3)
++ {
++ if (($lr-3) < 0)
++ { &rotr($tt, 3-$lr); }
++ else { &rotl($tt, $lr-3); }
++ }
++ if ($lr != 2)
++ {
++ if (($lr-2) < 0)
++ { &rotr($r, 2-$lr); }
++ else { &rotl($r, $lr-2); }
++ }
++ }
++
++sub FP_new
++ {
++ local($l,$r,$tt,$lr)=@_;
++
++ if ($lr != 2)
++ {
++ if (($lr-2) < 0)
++ { &rotl($r, 2-$lr); }
++ else { &rotr($r, $lr-2); }
++ }
++ if ($lr != 3)
++ {
++ if (($lr-3) < 0)
++ { &rotl($l, 3-$lr); }
++ else { &rotr($l, $lr-3); }
++ }
++
++ &R_PERM_OP($l,$r,$tt, 0,"0xaaaaaaaa",$r);
++ &R_PERM_OP($tt,$r,$l,23,"0x03fc03fc",$r);
++ &R_PERM_OP($l,$r,$tt,10,"0x33333333",$l);
++ &R_PERM_OP($r,$tt,$l,18,"0xfff0000f",$l);
++ &R_PERM_OP($l,$tt,$r,12,"0xf0f0f0f0",$r);
++ &rotr($tt , 4);
++ }
++
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/crypto/ciphers/des/asm/des686.pl Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,230 @@
++#!/usr/bin/perl
++
++$prog="des686.pl";
++
++# base code is in microsft
++# op dest, source
++# format.
++#
++
++# WILL NOT WORK ANYMORE WITH desboth.pl
++require "desboth.pl";
++
++if ( ($ARGV[0] eq "elf"))
++ { require "x86unix.pl"; }
++elsif ( ($ARGV[0] eq "a.out"))
++ { $aout=1; require "x86unix.pl"; }
++elsif ( ($ARGV[0] eq "sol"))
++ { $sol=1; require "x86unix.pl"; }
++elsif ( ($ARGV[0] eq "cpp"))
++ { $cpp=1; require "x86unix.pl"; }
++elsif ( ($ARGV[0] eq "win32"))
++ { require "x86ms.pl"; }
++else
++ {
++ print STDERR <<"EOF";
++Pick one target type from
++ elf - linux, FreeBSD etc
++ a.out - old linux
++ sol - x86 solaris
++ cpp - format so x86unix.cpp can be used
++ win32 - Windows 95/Windows NT
++EOF
++ exit(1);
++ }
++
++&comment("Don't even think of reading this code");
++&comment("It was automatically generated by $prog");
++&comment("Which is a perl program used to generate the x86 assember for");
++&comment("any of elf, a.out, Win32, or Solaris");
++&comment("It can be found in SSLeay 0.6.5+ or in libdes 3.26+");
++&comment("eric <eay\@cryptsoft.com>");
++&comment("");
++
++&file("dx86xxxx");
++
++$L="edi";
++$R="esi";
++
++&des_encrypt("des_encrypt",1);
++&des_encrypt("des_encrypt2",0);
++
++&des_encrypt3("des_encrypt3",1);
++&des_encrypt3("des_decrypt3",0);
++
++&file_end();
++
++sub des_encrypt
++ {
++ local($name,$do_ip)=@_;
++
++ &function_begin($name,"EXTRN _des_SPtrans:DWORD");
++
++ &comment("");
++ &comment("Load the 2 words");
++ &mov("eax",&wparam(0));
++ &mov($L,&DWP(0,"eax","",0));
++ &mov($R,&DWP(4,"eax","",0));
++
++ $ksp=&wparam(1);
++
++ if ($do_ip)
++ {
++ &comment("");
++ &comment("IP");
++ &IP_new($L,$R,"eax");
++ }
++
++ &comment("");
++ &comment("fixup rotate");
++ &rotl($R,3);
++ &rotl($L,3);
++ &exch($L,$R);
++
++ &comment("");
++ &comment("load counter, key_schedule and enc flag");
++ &mov("eax",&wparam(2)); # get encrypt flag
++ &mov("ebp",&wparam(1)); # get ks
++ &cmp("eax","0");
++ &je(&label("start_decrypt"));
++
++ # encrypting part
++
++ for ($i=0; $i<16; $i+=2)
++ {
++ &comment("");
++ &comment("Round $i");
++ &D_ENCRYPT($L,$R,$i*2,"ebp","des_SPtrans","ecx","edx","eax","ebx");
++
++ &comment("");
++ &comment("Round ".sprintf("%d",$i+1));
++ &D_ENCRYPT($R,$L,($i+1)*2,"ebp","des_SPtrans","ecx","edx","eax","ebx");
++ }
++ &jmp(&label("end"));
++
++ &set_label("start_decrypt");
++
++ for ($i=15; $i>0; $i-=2)
++ {
++ &comment("");
++ &comment("Round $i");
++ &D_ENCRYPT($L,$R,$i*2,"ebp","des_SPtrans","ecx","edx","eax","ebx");
++ &comment("");
++ &comment("Round ".sprintf("%d",$i-1));
++ &D_ENCRYPT($R,$L,($i-1)*2,"ebp","des_SPtrans","ecx","edx","eax","ebx");
++ }
++
++ &set_label("end");
++
++ &comment("");
++ &comment("Fixup");
++ &rotr($L,3); # r
++ &rotr($R,3); # l
++
++ if ($do_ip)
++ {
++ &comment("");
++ &comment("FP");
++ &FP_new($R,$L,"eax");
++ }
++
++ &mov("eax",&wparam(0));
++ &mov(&DWP(0,"eax","",0),$L);
++ &mov(&DWP(4,"eax","",0),$R);
++
++ &function_end($name);
++ }
++
++
++# The logic is to load R into 2 registers and operate on both at the same time.
++# We also load the 2 R's into 2 more registers so we can do the 'move word down a byte'
++# while also masking the other copy and doing a lookup. We then also accumulate the
++# L value in 2 registers then combine them at the end.
++sub D_ENCRYPT
++ {
++ local($L,$R,$S,$ks,$desSP,$u,$t,$tmp1,$tmp2,$tmp3)=@_;
++
++ &mov( $u, &DWP(&n2a($S*4),$ks,"",0));
++ &mov( $t, &DWP(&n2a(($S+1)*4),$ks,"",0));
++ &xor( $u, $R );
++ &xor( $t, $R );
++ &rotr( $t, 4 );
++
++ # the numbers at the end of the line are origional instruction order
++ &mov( $tmp2, $u ); # 1 2
++ &mov( $tmp1, $t ); # 1 1
++ &and( $tmp2, "0xfc" ); # 1 4
++ &and( $tmp1, "0xfc" ); # 1 3
++ &shr( $t, 8 ); # 1 5
++ &xor( $L, &DWP("0x100+$desSP",$tmp1,"",0)); # 1 7
++ &shr( $u, 8 ); # 1 6
++ &mov( $tmp1, &DWP(" $desSP",$tmp2,"",0)); # 1 8
++
++ &mov( $tmp2, $u ); # 2 2
++ &xor( $L, $tmp1 ); # 1 9
++ &and( $tmp2, "0xfc" ); # 2 4
++ &mov( $tmp1, $t ); # 2 1
++ &and( $tmp1, "0xfc" ); # 2 3
++ &shr( $t, 8 ); # 2 5
++ &xor( $L, &DWP("0x300+$desSP",$tmp1,"",0)); # 2 7
++ &shr( $u, 8 ); # 2 6
++ &mov( $tmp1, &DWP("0x200+$desSP",$tmp2,"",0)); # 2 8
++ &mov( $tmp2, $u ); # 3 2
++
++ &xor( $L, $tmp1 ); # 2 9
++ &and( $tmp2, "0xfc" ); # 3 4
++
++ &mov( $tmp1, $t ); # 3 1
++ &shr( $u, 8 ); # 3 6
++ &and( $tmp1, "0xfc" ); # 3 3
++ &shr( $t, 8 ); # 3 5
++ &xor( $L, &DWP("0x500+$desSP",$tmp1,"",0)); # 3 7
++ &mov( $tmp1, &DWP("0x400+$desSP",$tmp2,"",0)); # 3 8
++
++ &and( $t, "0xfc" ); # 4 1
++ &xor( $L, $tmp1 ); # 3 9
++
++ &and( $u, "0xfc" ); # 4 2
++ &xor( $L, &DWP("0x700+$desSP",$t,"",0)); # 4 3
++ &xor( $L, &DWP("0x600+$desSP",$u,"",0)); # 4 4
++ }
++
++sub PERM_OP
++ {
++ local($a,$b,$tt,$shift,$mask)=@_;
++
++ &mov( $tt, $a );
++ &shr( $tt, $shift );
++ &xor( $tt, $b );
++ &and( $tt, $mask );
++ &xor( $b, $tt );
++ &shl( $tt, $shift );
++ &xor( $a, $tt );
++ }
++
++sub IP_new
++ {
++ local($l,$r,$tt)=@_;
++
++ &PERM_OP($r,$l,$tt, 4,"0x0f0f0f0f");
++ &PERM_OP($l,$r,$tt,16,"0x0000ffff");
++ &PERM_OP($r,$l,$tt, 2,"0x33333333");
++ &PERM_OP($l,$r,$tt, 8,"0x00ff00ff");
++ &PERM_OP($r,$l,$tt, 1,"0x55555555");
++ }
++
++sub FP_new
++ {
++ local($l,$r,$tt)=@_;
++
++ &PERM_OP($l,$r,$tt, 1,"0x55555555");
++ &PERM_OP($r,$l,$tt, 8,"0x00ff00ff");
++ &PERM_OP($l,$r,$tt, 2,"0x33333333");
++ &PERM_OP($r,$l,$tt,16,"0x0000ffff");
++ &PERM_OP($l,$r,$tt, 4,"0x0f0f0f0f");
++ }
++
++sub n2a
++ {
++ sprintf("%d",$_[0]);
++ }
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/crypto/ciphers/des/asm/desboth.pl Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,79 @@
++#!/usr/bin/perl
++
++$L="edi";
++$R="esi";
++
++sub des_encrypt3
++ {
++ local($name,$enc)=@_;
++
++ &function_begin_B($name,"");
++ &push("ebx");
++ &mov("ebx",&wparam(0));
++
++ &push("ebp");
++ &push("esi");
++
++ &push("edi");
++
++ &comment("");
++ &comment("Load the data words");
++ &mov($L,&DWP(0,"ebx","",0));
++ &mov($R,&DWP(4,"ebx","",0));
++ &stack_push(3);
++
++ &comment("");
++ &comment("IP");
++ &IP_new($L,$R,"edx",0);
++
++ # put them back
++
++ if ($enc)
++ {
++ &mov(&DWP(4,"ebx","",0),$R);
++ &mov("eax",&wparam(1));
++ &mov(&DWP(0,"ebx","",0),"edx");
++ &mov("edi",&wparam(2));
++ &mov("esi",&wparam(3));
++ }
++ else
++ {
++ &mov(&DWP(4,"ebx","",0),$R);
++ &mov("esi",&wparam(1));
++ &mov(&DWP(0,"ebx","",0),"edx");
++ &mov("edi",&wparam(2));
++ &mov("eax",&wparam(3));
++ }
++ &mov(&swtmp(2), (($enc)?"1":"0"));
++ &mov(&swtmp(1), "eax");
++ &mov(&swtmp(0), "ebx");
++ &call("des_encrypt2");
++ &mov(&swtmp(2), (($enc)?"0":"1"));
++ &mov(&swtmp(1), "edi");
++ &mov(&swtmp(0), "ebx");
++ &call("des_encrypt2");
++ &mov(&swtmp(2), (($enc)?"1":"0"));
++ &mov(&swtmp(1), "esi");
++ &mov(&swtmp(0), "ebx");
++ &call("des_encrypt2");
++
++ &stack_pop(3);
++ &mov($L,&DWP(0,"ebx","",0));
++ &mov($R,&DWP(4,"ebx","",0));
++
++ &comment("");
++ &comment("FP");
++ &FP_new($L,$R,"eax",0);
++
++ &mov(&DWP(0,"ebx","",0),"eax");
++ &mov(&DWP(4,"ebx","",0),$R);
++
++ &pop("edi");
++ &pop("esi");
++ &pop("ebp");
++ &pop("ebx");
++ &ret();
++ &function_end_B($name);
++ }
++
++
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/crypto/ciphers/des/asm/perlasm/cbc.pl Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,342 @@
++#!/usr/bin/perl
++
++# void des_ncbc_encrypt(input, output, length, schedule, ivec, enc)
++# des_cblock (*input);
++# des_cblock (*output);
++# long length;
++# des_key_schedule schedule;
++# des_cblock (*ivec);
++# int enc;
++#
++# calls
++# des_encrypt((DES_LONG *)tin,schedule,DES_ENCRYPT);
++#
++
++#&cbc("des_ncbc_encrypt","des_encrypt",0);
++#&cbc("BF_cbc_encrypt","BF_encrypt","BF_encrypt",
++# 1,4,5,3,5,-1);
++#&cbc("des_ncbc_encrypt","des_encrypt","des_encrypt",
++# 0,4,5,3,5,-1);
++#&cbc("des_ede3_cbc_encrypt","des_encrypt3","des_decrypt3",
++# 0,6,7,3,4,5);
++#
++# When doing a cipher that needs bigendian order,
++# for encrypt, the iv is kept in bigendian form,
++# while for decrypt, it is kept in little endian.
++sub cbc
++ {
++ local($name,$enc_func,$dec_func,$swap,$iv_off,$enc_off,$p1,$p2,$p3)=@_;
++ # name is the function name
++ # enc_func and dec_func and the functions to call for encrypt/decrypt
++ # swap is true if byte order needs to be reversed
++ # iv_off is parameter number for the iv
++ # enc_off is parameter number for the encrypt/decrypt flag
++ # p1,p2,p3 are the offsets for parameters to be passed to the
++ # underlying calls.
++
++ &function_begin_B($name,"");
++ &comment("");
++
++ $in="esi";
++ $out="edi";
++ $count="ebp";
++
++ &push("ebp");
++ &push("ebx");
++ &push("esi");
++ &push("edi");
++
++ $data_off=4;
++ $data_off+=4 if ($p1 > 0);
++ $data_off+=4 if ($p2 > 0);
++ $data_off+=4 if ($p3 > 0);
++
++ &mov($count, &wparam(2)); # length
++
++ &comment("getting iv ptr from parameter $iv_off");
++ &mov("ebx", &wparam($iv_off)); # Get iv ptr
++
++ &mov($in, &DWP(0,"ebx","",0));# iv[0]
++ &mov($out, &DWP(4,"ebx","",0));# iv[1]
++
++ &push($out);
++ &push($in);
++ &push($out); # used in decrypt for iv[1]
++ &push($in); # used in decrypt for iv[0]
++
++ &mov("ebx", "esp"); # This is the address of tin[2]
++
++ &mov($in, &wparam(0)); # in
++ &mov($out, &wparam(1)); # out
++
++ # We have loaded them all, how lets push things
++ &comment("getting encrypt flag from parameter $enc_off");
++ &mov("ecx", &wparam($enc_off)); # Get enc flag
++ if ($p3 > 0)
++ {
++ &comment("get and push parameter $p3");
++ if ($enc_off != $p3)
++ { &mov("eax", &wparam($p3)); &push("eax"); }
++ else { &push("ecx"); }
++ }
++ if ($p2 > 0)
++ {
++ &comment("get and push parameter $p2");
++ if ($enc_off != $p2)
++ { &mov("eax", &wparam($p2)); &push("eax"); }
++ else { &push("ecx"); }
++ }
++ if ($p1 > 0)
++ {
++ &comment("get and push parameter $p1");
++ if ($enc_off != $p1)
++ { &mov("eax", &wparam($p1)); &push("eax"); }
++ else { &push("ecx"); }
++ }
++ &push("ebx"); # push data/iv
++
++ &cmp("ecx",0);
++ &jz(&label("decrypt"));
++
++ &and($count,0xfffffff8);
++ &mov("eax", &DWP($data_off,"esp","",0)); # load iv[0]
++ &mov("ebx", &DWP($data_off+4,"esp","",0)); # load iv[1]
++
++ &jz(&label("encrypt_finish"));
++
++ #############################################################
++
++ &set_label("encrypt_loop");
++ # encrypt start
++ # "eax" and "ebx" hold iv (or the last cipher text)
++
++ &mov("ecx", &DWP(0,$in,"",0)); # load first 4 bytes
++ &mov("edx", &DWP(4,$in,"",0)); # second 4 bytes
++
++ &xor("eax", "ecx");
++ &xor("ebx", "edx");
++
++ &bswap("eax") if $swap;
++ &bswap("ebx") if $swap;
++
++ &mov(&DWP($data_off,"esp","",0), "eax"); # put in array for call
++ &mov(&DWP($data_off+4,"esp","",0), "ebx"); #
++
++ &call($enc_func);
++
++ &mov("eax", &DWP($data_off,"esp","",0));
++ &mov("ebx", &DWP($data_off+4,"esp","",0));
++
++ &bswap("eax") if $swap;
++ &bswap("ebx") if $swap;
++
++ &mov(&DWP(0,$out,"",0),"eax");
++ &mov(&DWP(4,$out,"",0),"ebx");
++
++ # eax and ebx are the next iv.
++
++ &add($in, 8);
++ &add($out, 8);
++
++ &sub($count, 8);
++ &jnz(&label("encrypt_loop"));
++
++###################################################################3
++ &set_label("encrypt_finish");
++ &mov($count, &wparam(2)); # length
++ &and($count, 7);
++ &jz(&label("finish"));
++ &xor("ecx","ecx");
++ &xor("edx","edx");
++ &mov($count,&DWP(&label("cbc_enc_jmp_table"),"",$count,4));
++ &jmp_ptr($count);
++
++&set_label("ej7");
++ &xor("edx", "edx") if $ppro; # ppro friendly
++ &movb(&HB("edx"), &BP(6,$in,"",0));
++ &shl("edx",8);
++&set_label("ej6");
++ &movb(&HB("edx"), &BP(5,$in,"",0));
++&set_label("ej5");
++ &movb(&LB("edx"), &BP(4,$in,"",0));
++&set_label("ej4");
++ &mov("ecx", &DWP(0,$in,"",0));
++ &jmp(&label("ejend"));
++&set_label("ej3");
++ &movb(&HB("ecx"), &BP(2,$in,"",0));
++ &xor("ecx", "ecx") if $ppro; # ppro friendly
++ &shl("ecx",8);
++&set_label("ej2");
++ &movb(&HB("ecx"), &BP(1,$in,"",0));
++&set_label("ej1");
++ &movb(&LB("ecx"), &BP(0,$in,"",0));
++&set_label("ejend");
++
++ &xor("eax", "ecx");
++ &xor("ebx", "edx");
++
++ &bswap("eax") if $swap;
++ &bswap("ebx") if $swap;
++
++ &mov(&DWP($data_off,"esp","",0), "eax"); # put in array for call
++ &mov(&DWP($data_off+4,"esp","",0), "ebx"); #
++
++ &call($enc_func);
++
++ &mov("eax", &DWP($data_off,"esp","",0));
++ &mov("ebx", &DWP($data_off+4,"esp","",0));
++
++ &bswap("eax") if $swap;
++ &bswap("ebx") if $swap;
++
++ &mov(&DWP(0,$out,"",0),"eax");
++ &mov(&DWP(4,$out,"",0),"ebx");
++
++ &jmp(&label("finish"));
++
++ #############################################################
++ #############################################################
++ &set_label("decrypt",1);
++ # decrypt start
++ &and($count,0xfffffff8);
++ # The next 2 instructions are only for if the jz is taken
++ &mov("eax", &DWP($data_off+8,"esp","",0)); # get iv[0]
++ &mov("ebx", &DWP($data_off+12,"esp","",0)); # get iv[1]
++ &jz(&label("decrypt_finish"));
++
++ &set_label("decrypt_loop");
++ &mov("eax", &DWP(0,$in,"",0)); # load first 4 bytes
++ &mov("ebx", &DWP(4,$in,"",0)); # second 4 bytes
++
++ &bswap("eax") if $swap;
++ &bswap("ebx") if $swap;
++
++ &mov(&DWP($data_off,"esp","",0), "eax"); # put back
++ &mov(&DWP($data_off+4,"esp","",0), "ebx"); #
++
++ &call($dec_func);
++
++ &mov("eax", &DWP($data_off,"esp","",0)); # get return
++ &mov("ebx", &DWP($data_off+4,"esp","",0)); #
++
++ &bswap("eax") if $swap;
++ &bswap("ebx") if $swap;
++
++ &mov("ecx", &DWP($data_off+8,"esp","",0)); # get iv[0]
++ &mov("edx", &DWP($data_off+12,"esp","",0)); # get iv[1]
++
++ &xor("ecx", "eax");
++ &xor("edx", "ebx");
++
++ &mov("eax", &DWP(0,$in,"",0)); # get old cipher text,
++ &mov("ebx", &DWP(4,$in,"",0)); # next iv actually
++
++ &mov(&DWP(0,$out,"",0),"ecx");
++ &mov(&DWP(4,$out,"",0),"edx");
++
++ &mov(&DWP($data_off+8,"esp","",0), "eax"); # save iv
++ &mov(&DWP($data_off+12,"esp","",0), "ebx"); #
++
++ &add($in, 8);
++ &add($out, 8);
++
++ &sub($count, 8);
++ &jnz(&label("decrypt_loop"));
++############################ ENDIT #######################3
++ &set_label("decrypt_finish");
++ &mov($count, &wparam(2)); # length
++ &and($count, 7);
++ &jz(&label("finish"));
++
++ &mov("eax", &DWP(0,$in,"",0)); # load first 4 bytes
++ &mov("ebx", &DWP(4,$in,"",0)); # second 4 bytes
++
++ &bswap("eax") if $swap;
++ &bswap("ebx") if $swap;
++
++ &mov(&DWP($data_off,"esp","",0), "eax"); # put back
++ &mov(&DWP($data_off+4,"esp","",0), "ebx"); #
++
++ &call($dec_func);
++
++ &mov("eax", &DWP($data_off,"esp","",0)); # get return
++ &mov("ebx", &DWP($data_off+4,"esp","",0)); #
++
++ &bswap("eax") if $swap;
++ &bswap("ebx") if $swap;
++
++ &mov("ecx", &DWP($data_off+8,"esp","",0)); # get iv[0]
++ &mov("edx", &DWP($data_off+12,"esp","",0)); # get iv[1]
++
++ &xor("ecx", "eax");
++ &xor("edx", "ebx");
++
++ # this is for when we exit
++ &mov("eax", &DWP(0,$in,"",0)); # get old cipher text,
++ &mov("ebx", &DWP(4,$in,"",0)); # next iv actually
++
++&set_label("dj7");
++ &rotr("edx", 16);
++ &movb(&BP(6,$out,"",0), &LB("edx"));
++ &shr("edx",16);
++&set_label("dj6");
++ &movb(&BP(5,$out,"",0), &HB("edx"));
++&set_label("dj5");
++ &movb(&BP(4,$out,"",0), &LB("edx"));
++&set_label("dj4");
++ &mov(&DWP(0,$out,"",0), "ecx");
++ &jmp(&label("djend"));
++&set_label("dj3");
++ &rotr("ecx", 16);
++ &movb(&BP(2,$out,"",0), &LB("ecx"));
++ &shl("ecx",16);
++&set_label("dj2");
++ &movb(&BP(1,$in,"",0), &HB("ecx"));
++&set_label("dj1");
++ &movb(&BP(0,$in,"",0), &LB("ecx"));
++&set_label("djend");
++
++ # final iv is still in eax:ebx
++ &jmp(&label("finish"));
++
++
++############################ FINISH #######################3
++ &set_label("finish",1);
++ &mov("ecx", &wparam($iv_off)); # Get iv ptr
++
++ #################################################
++ $total=16+4;
++ $total+=4 if ($p1 > 0);
++ $total+=4 if ($p2 > 0);
++ $total+=4 if ($p3 > 0);
++ &add("esp",$total);
++
++ &mov(&DWP(0,"ecx","",0), "eax"); # save iv
++ &mov(&DWP(4,"ecx","",0), "ebx"); # save iv
++
++ &function_end_A($name);
++
++ &set_label("cbc_enc_jmp_table",1);
++ &data_word("0");
++ &data_word(&label("ej1"));
++ &data_word(&label("ej2"));
++ &data_word(&label("ej3"));
++ &data_word(&label("ej4"));
++ &data_word(&label("ej5"));
++ &data_word(&label("ej6"));
++ &data_word(&label("ej7"));
++ &set_label("cbc_dec_jmp_table",1);
++ &data_word("0");
++ &data_word(&label("dj1"));
++ &data_word(&label("dj2"));
++ &data_word(&label("dj3"));
++ &data_word(&label("dj4"));
++ &data_word(&label("dj5"));
++ &data_word(&label("dj6"));
++ &data_word(&label("dj7"));
++
++ &function_end_B($name);
++
++ }
++
++1;
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/crypto/ciphers/des/asm/perlasm/readme Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,124 @@
++The perl scripts in this directory are my 'hack' to generate
++multiple different assembler formats via the one origional script.
++
++The way to use this library is to start with adding the path to this directory
++and then include it.
++
++push(@INC,"perlasm","../../perlasm");
++require "x86asm.pl";
++
++The first thing we do is setup the file and type of assember
++
++&asm_init($ARGV[0],$0);
++
++The first argument is the 'type'. Currently
++'cpp', 'sol', 'a.out', 'elf' or 'win32'.
++Argument 2 is the file name.
++
++The reciprocal function is
++&asm_finish() which should be called at the end.
++
++There are 2 main 'packages'. x86ms.pl, which is the microsoft assembler,
++and x86unix.pl which is the unix (gas) version.
++
++Functions of interest are:
++&external_label("des_SPtrans"); declare and external variable
++&LB(reg); Low byte for a register
++&HB(reg); High byte for a register
++&BP(off,base,index,scale) Byte pointer addressing
++&DWP(off,base,index,scale) Word pointer addressing
++&stack_push(num) Basically a 'sub esp, num*4' with extra
++&stack_pop(num) inverse of stack_push
++&function_begin(name,extra) Start a function with pushing of
++ edi, esi, ebx and ebp. extra is extra win32
++ external info that may be required.
++&function_begin_B(name,extra) Same as norma function_begin but no pushing.
++&function_end(name) Call at end of function.
++&function_end_A(name) Standard pop and ret, for use inside functions
++&function_end_B(name) Call at end but with poping or 'ret'.
++&swtmp(num) Address on stack temp word.
++&wparam(num) Parameter number num, that was push
++ in C convention. This all works over pushes
++ and pops.
++&comment("hello there") Put in a comment.
++&label("loop") Refer to a label, normally a jmp target.
++&set_label("loop") Set a label at this point.
++&data_word(word) Put in a word of data.
++
++So how does this all hold together? Given
++
++int calc(int len, int *data)
++ {
++ int i,j=0;
++
++ for (i=0; i<len; i++)
++ {
++ j+=other(data[i]);
++ }
++ }
++
++So a very simple version of this function could be coded as
++
++ push(@INC,"perlasm","../../perlasm");
++ require "x86asm.pl";
++
++ &asm_init($ARGV[0],"cacl.pl");
++
++ &external_label("other");
++
++ $tmp1= "eax";
++ $j= "edi";
++ $data= "esi";
++ $i= "ebp";
++
++ &comment("a simple function");
++ &function_begin("calc");
++ &mov( $data, &wparam(1)); # data
++ &xor( $j, $j);
++ &xor( $i, $i);
++
++ &set_label("loop");
++ &cmp( $i, &wparam(0));
++ &jge( &label("end"));
++
++ &mov( $tmp1, &DWP(0,$data,$i,4));
++ &push( $tmp1);
++ &call( "other");
++ &add( $j, "eax");
++ &pop( $tmp1);
++ &inc( $i);
++ &jmp( &label("loop"));
++
++ &set_label("end");
++ &mov( "eax", $j);
++
++ &function_end("calc");
++
++ &asm_finish();
++
++The above example is very very unoptimised but gives an idea of how
++things work.
++
++There is also a cbc mode function generator in cbc.pl
++
++&cbc( $name,
++ $encrypt_function_name,
++ $decrypt_function_name,
++ $true_if_byte_swap_needed,
++ $parameter_number_for_iv,
++ $parameter_number_for_encrypt_flag,
++ $first_parameter_to_pass,
++ $second_parameter_to_pass,
++ $third_parameter_to_pass);
++
++So for example, given
++void BF_encrypt(BF_LONG *data,BF_KEY *key);
++void BF_decrypt(BF_LONG *data,BF_KEY *key);
++void BF_cbc_encrypt(unsigned char *in, unsigned char *out, long length,
++ BF_KEY *ks, unsigned char *iv, int enc);
++
++&cbc("BF_cbc_encrypt","BF_encrypt","BF_encrypt",1,4,5,3,-1,-1);
++
++&cbc("des_ncbc_encrypt","des_encrypt","des_encrypt",0,4,5,3,5,-1);
++&cbc("des_ede3_cbc_encrypt","des_encrypt3","des_decrypt3",0,6,7,3,4,5);
++
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/crypto/ciphers/des/asm/perlasm/x86asm.pl Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,111 @@
++#!/usr/bin/perl
++
++# require 'x86asm.pl';
++# &asm_init("cpp","des-586.pl");
++# XXX
++# XXX
++# main'asm_finish
++
++sub main'asm_finish
++ {
++ &file_end();
++ &asm_finish_cpp() if $cpp;
++ print &asm_get_output();
++ }
++
++sub main'asm_init
++ {
++ ($type,$fn)=@_;
++ $filename=$fn;
++
++ $cpp=$sol=$aout=$win32=0;
++ if ( ($type eq "elf"))
++ { require "x86unix.pl"; }
++ elsif ( ($type eq "a.out"))
++ { $aout=1; require "x86unix.pl"; }
++ elsif ( ($type eq "sol"))
++ { $sol=1; require "x86unix.pl"; }
++ elsif ( ($type eq "cpp"))
++ { $cpp=1; require "x86unix.pl"; }
++ elsif ( ($type eq "win32"))
++ { $win32=1; require "x86ms.pl"; }
++ else
++ {
++ print STDERR <<"EOF";
++Pick one target type from
++ elf - linux, FreeBSD etc
++ a.out - old linux
++ sol - x86 solaris
++ cpp - format so x86unix.cpp can be used
++ win32 - Windows 95/Windows NT
++EOF
++ exit(1);
++ }
++
++ &asm_init_output();
++
++&comment("Don't even think of reading this code");
++&comment("It was automatically generated by $filename");
++&comment("Which is a perl program used to generate the x86 assember for");
++&comment("any of elf, a.out, BSDI,Win32, or Solaris");
++&comment("eric <eay\@cryptsoft.com>");
++&comment("");
++
++ $filename =~ s/\.pl$//;
++ &file($filename);
++ }
++
++sub asm_finish_cpp
++ {
++ return unless $cpp;
++
++ local($tmp,$i);
++ foreach $i (&get_labels())
++ {
++ $tmp.="#define $i _$i\n";
++ }
++ print <<"EOF";
++/* Run the C pre-processor over this file with one of the following defined
++ * ELF - elf object files,
++ * OUT - a.out object files,
++ * BSDI - BSDI style a.out object files
++ * SOL - Solaris style elf
++ */
++
++#define TYPE(a,b) .type a,b
++#define SIZE(a,b) .size a,b
++
++#if defined(OUT) || defined(BSDI)
++$tmp
++#endif
++
++#ifdef OUT
++#define OK 1
++#define ALIGN 4
++#endif
++
++#ifdef BSDI
++#define OK 1
++#define ALIGN 4
++#undef SIZE
++#undef TYPE
++#endif
++
++#if defined(ELF) || defined(SOL)
++#define OK 1
++#define ALIGN 16
++#endif
++
++#ifndef OK
++You need to define one of
++ELF - elf systems - linux-elf, NetBSD and DG-UX
++OUT - a.out systems - linux-a.out and FreeBSD
++SOL - solaris systems, which are elf with strange comment lines
++BSDI - a.out with a very primative version of as.
++#endif
++
++/* Let the Assembler begin :-) */
++EOF
++ }
++
++1;
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/crypto/ciphers/des/asm/perlasm/x86ms.pl Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,345 @@
++#!/usr/bin/perl
++
++package x86ms;
++
++$label="L000";
++
++%lb=( 'eax', 'al',
++ 'ebx', 'bl',
++ 'ecx', 'cl',
++ 'edx', 'dl',
++ 'ax', 'al',
++ 'bx', 'bl',
++ 'cx', 'cl',
++ 'dx', 'dl',
++ );
++
++%hb=( 'eax', 'ah',
++ 'ebx', 'bh',
++ 'ecx', 'ch',
++ 'edx', 'dh',
++ 'ax', 'ah',
++ 'bx', 'bh',
++ 'cx', 'ch',
++ 'dx', 'dh',
++ );
++
++sub main'asm_init_output { @out=(); }
++sub main'asm_get_output { return(@out); }
++sub main'get_labels { return(@labels); }
++sub main'external_label { push(@labels,@_); }
++
++sub main'LB
++ {
++ (defined($lb{$_[0]})) || die "$_[0] does not have a 'low byte'\n";
++ return($lb{$_[0]});
++ }
++
++sub main'HB
++ {
++ (defined($hb{$_[0]})) || die "$_[0] does not have a 'high byte'\n";
++ return($hb{$_[0]});
++ }
++
++sub main'BP
++ {
++ &get_mem("BYTE",@_);
++ }
++
++sub main'DWP
++ {
++ &get_mem("DWORD",@_);
++ }
++
++sub main'stack_push
++ {
++ local($num)=@_;
++ $stack+=$num*4;
++ &main'sub("esp",$num*4);
++ }
++
++sub main'stack_pop
++ {
++ local($num)=@_;
++ $stack-=$num*4;
++ &main'add("esp",$num*4);
++ }
++
++sub get_mem
++ {
++ local($size,$addr,$reg1,$reg2,$idx)=@_;
++ local($t,$post);
++ local($ret)="$size PTR ";
++
++ $addr =~ s/^\s+//;
++ if ($addr =~ /^(.+)\+(.+)$/)
++ {
++ $reg2=&conv($1);
++ $addr="_$2";
++ }
++ elsif ($addr =~ /^[_a-zA-Z]/)
++ {
++ $addr="_$addr";
++ }
++
++ $reg1="$regs{$reg1}" if defined($regs{$reg1});
++ $reg2="$regs{$reg2}" if defined($regs{$reg2});
++ if (($addr ne "") && ($addr ne 0))
++ {
++ if ($addr !~ /^-/)
++ { $ret.=$addr; }
++ else { $post=$addr; }
++ }
++ if ($reg2 ne "")
++ {
++ $t="";
++ $t="*$idx" if ($idx != 0);
++ $reg1="+".$reg1 if ("$reg1$post" ne "");
++ $ret.="[$reg2$t$reg1$post]";
++ }
++ else
++ {
++ $ret.="[$reg1$post]"
++ }
++ return($ret);
++ }
++
++sub main'mov { &out2("mov",@_); }
++sub main'movb { &out2("mov",@_); }
++sub main'and { &out2("and",@_); }
++sub main'or { &out2("or",@_); }
++sub main'shl { &out2("shl",@_); }
++sub main'shr { &out2("shr",@_); }
++sub main'xor { &out2("xor",@_); }
++sub main'xorb { &out2("xor",@_); }
++sub main'add { &out2("add",@_); }
++sub main'adc { &out2("adc",@_); }
++sub main'sub { &out2("sub",@_); }
++sub main'rotl { &out2("rol",@_); }
++sub main'rotr { &out2("ror",@_); }
++sub main'exch { &out2("xchg",@_); }
++sub main'cmp { &out2("cmp",@_); }
++sub main'lea { &out2("lea",@_); }
++sub main'mul { &out1("mul",@_); }
++sub main'div { &out1("div",@_); }
++sub main'dec { &out1("dec",@_); }
++sub main'inc { &out1("inc",@_); }
++sub main'jmp { &out1("jmp",@_); }
++sub main'jmp_ptr { &out1p("jmp",@_); }
++sub main'je { &out1("je",@_); }
++sub main'jle { &out1("jle",@_); }
++sub main'jz { &out1("jz",@_); }
++sub main'jge { &out1("jge",@_); }
++sub main'jl { &out1("jl",@_); }
++sub main'jb { &out1("jb",@_); }
++sub main'jnz { &out1("jnz",@_); }
++sub main'jne { &out1("jne",@_); }
++sub main'push { &out1("push",@_); $stack+=4; }
++sub main'pop { &out1("pop",@_); $stack-=4; }
++sub main'bswap { &out1("bswap",@_); &using486(); }
++sub main'not { &out1("not",@_); }
++sub main'call { &out1("call",'_'.$_[0]); }
++sub main'ret { &out0("ret"); }
++sub main'nop { &out0("nop"); }
++
++sub out2
++ {
++ local($name,$p1,$p2)=@_;
++ local($l,$t);
++
++ push(@out,"\t$name\t");
++ $t=&conv($p1).",";
++ $l=length($t);
++ push(@out,$t);
++ $l=4-($l+9)/8;
++ push(@out,"\t" x $l);
++ push(@out,&conv($p2));
++ push(@out,"\n");
++ }
++
++sub out0
++ {
++ local($name)=@_;
++
++ push(@out,"\t$name\n");
++ }
++
++sub out1
++ {
++ local($name,$p1)=@_;
++ local($l,$t);
++
++ push(@out,"\t$name\t".&conv($p1)."\n");
++ }
++
++sub conv
++ {
++ local($p)=@_;
++
++ $p =~ s/0x([0-9A-Fa-f]+)/0$1h/;
++ return $p;
++ }
++
++sub using486
++ {
++ return if $using486;
++ $using486++;
++ grep(s/\.386/\.486/,@out);
++ }
++
++sub main'file
++ {
++ local($file)=@_;
++
++ local($tmp)=<<"EOF";
++ TITLE $file.asm
++ .386
++.model FLAT
++EOF
++ push(@out,$tmp);
++ }
++
++sub main'function_begin
++ {
++ local($func,$extra)=@_;
++
++ push(@labels,$func);
++
++ local($tmp)=<<"EOF";
++_TEXT SEGMENT
++PUBLIC _$func
++$extra
++_$func PROC NEAR
++ push ebp
++ push ebx
++ push esi
++ push edi
++EOF
++ push(@out,$tmp);
++ $stack=20;
++ }
++
++sub main'function_begin_B
++ {
++ local($func,$extra)=@_;
++
++ local($tmp)=<<"EOF";
++_TEXT SEGMENT
++PUBLIC _$func
++$extra
++_$func PROC NEAR
++EOF
++ push(@out,$tmp);
++ $stack=4;
++ }
++
++sub main'function_end
++ {
++ local($func)=@_;
++
++ local($tmp)=<<"EOF";
++ pop edi
++ pop esi
++ pop ebx
++ pop ebp
++ ret
++_$func ENDP
++_TEXT ENDS
++EOF
++ push(@out,$tmp);
++ $stack=0;
++ %label=();
++ }
++
++sub main'function_end_B
++ {
++ local($func)=@_;
++
++ local($tmp)=<<"EOF";
++_$func ENDP
++_TEXT ENDS
++EOF
++ push(@out,$tmp);
++ $stack=0;
++ %label=();
++ }
++
++sub main'function_end_A
++ {
++ local($func)=@_;
++
++ local($tmp)=<<"EOF";
++ pop edi
++ pop esi
++ pop ebx
++ pop ebp
++ ret
++EOF
++ push(@out,$tmp);
++ }
++
++sub main'file_end
++ {
++ push(@out,"END\n");
++ }
++
++sub main'wparam
++ {
++ local($num)=@_;
++
++ return(&main'DWP($stack+$num*4,"esp","",0));
++ }
++
++sub main'swtmp
++ {
++ return(&main'DWP($_[0]*4,"esp","",0));
++ }
++
++# Should use swtmp, which is above esp. Linix can trash the stack above esp
++#sub main'wtmp
++# {
++# local($num)=@_;
++#
++# return(&main'DWP(-(($num+1)*4),"esp","",0));
++# }
++
++sub main'comment
++ {
++ foreach (@_)
++ {
++ push(@out,"\t; $_\n");
++ }
++ }
++
++sub main'label
++ {
++ if (!defined($label{$_[0]}))
++ {
++ $label{$_[0]}="\$${label}${_[0]}";
++ $label++;
++ }
++ return($label{$_[0]});
++ }
++
++sub main'set_label
++ {
++ if (!defined($label{$_[0]}))
++ {
++ $label{$_[0]}="${label}${_[0]}";
++ $label++;
++ }
++ push(@out,"$label{$_[0]}:\n");
++ }
++
++sub main'data_word
++ {
++ push(@out,"\tDD\t$_[0]\n");
++ }
++
++sub out1p
++ {
++ local($name,$p1)=@_;
++ local($l,$t);
++
++ push(@out,"\t$name\t ".&conv($p1)."\n");
++ }
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/crypto/ciphers/des/asm/perlasm/x86unix.pl Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,403 @@
++#!/usr/bin/perl
++
++package x86unix;
++
++$label="L000";
++
++$align=($main'aout)?"4":"16";
++$under=($main'aout)?"_":"";
++$com_start=($main'sol)?"/":"#";
++
++sub main'asm_init_output { @out=(); }
++sub main'asm_get_output { return(@out); }
++sub main'get_labels { return(@labels); }
++sub main'external_label { push(@labels,@_); }
++
++if ($main'cpp)
++ {
++ $align="ALIGN";
++ $under="";
++ $com_start='/*';
++ $com_end='*/';
++ }
++
++%lb=( 'eax', '%al',
++ 'ebx', '%bl',
++ 'ecx', '%cl',
++ 'edx', '%dl',
++ 'ax', '%al',
++ 'bx', '%bl',
++ 'cx', '%cl',
++ 'dx', '%dl',
++ );
++
++%hb=( 'eax', '%ah',
++ 'ebx', '%bh',
++ 'ecx', '%ch',
++ 'edx', '%dh',
++ 'ax', '%ah',
++ 'bx', '%bh',
++ 'cx', '%ch',
++ 'dx', '%dh',
++ );
++
++%regs=( 'eax', '%eax',
++ 'ebx', '%ebx',
++ 'ecx', '%ecx',
++ 'edx', '%edx',
++ 'esi', '%esi',
++ 'edi', '%edi',
++ 'ebp', '%ebp',
++ 'esp', '%esp',
++ );
++
++%reg_val=(
++ 'eax', 0x00,
++ 'ebx', 0x03,
++ 'ecx', 0x01,
++ 'edx', 0x02,
++ 'esi', 0x06,
++ 'edi', 0x07,
++ 'ebp', 0x05,
++ 'esp', 0x04,
++ );
++
++sub main'LB
++ {
++ (defined($lb{$_[0]})) || die "$_[0] does not have a 'low byte'\n";
++ return($lb{$_[0]});
++ }
++
++sub main'HB
++ {
++ (defined($hb{$_[0]})) || die "$_[0] does not have a 'high byte'\n";
++ return($hb{$_[0]});
++ }
++
++sub main'DWP
++ {
++ local($addr,$reg1,$reg2,$idx)=@_;
++
++ $ret="";
++ $addr =~ s/(^|[+ \t])([A-Za-z_]+)($|[+ \t])/$1$under$2$3/;
++ $reg1="$regs{$reg1}" if defined($regs{$reg1});
++ $reg2="$regs{$reg2}" if defined($regs{$reg2});
++ $ret.=$addr if ($addr ne "") && ($addr ne 0);
++ if ($reg2 ne "")
++ { $ret.="($reg1,$reg2,$idx)"; }
++ else
++ { $ret.="($reg1)" }
++ return($ret);
++ }
++
++sub main'BP
++ {
++ return(&main'DWP(@_));
++ }
++
++#sub main'BP
++# {
++# local($addr,$reg1,$reg2,$idx)=@_;
++#
++# $ret="";
++#
++# $addr =~ s/(^|[+ \t])([A-Za-z_]+)($|[+ \t])/$1$under$2$3/;
++# $reg1="$regs{$reg1}" if defined($regs{$reg1});
++# $reg2="$regs{$reg2}" if defined($regs{$reg2});
++# $ret.=$addr if ($addr ne "") && ($addr ne 0);
++# if ($reg2 ne "")
++# { $ret.="($reg1,$reg2,$idx)"; }
++# else
++# { $ret.="($reg1)" }
++# return($ret);
++# }
++
++sub main'mov { &out2("movl",@_); }
++sub main'movb { &out2("movb",@_); }
++sub main'and { &out2("andl",@_); }
++sub main'or { &out2("orl",@_); }
++sub main'shl { &out2("sall",@_); }
++sub main'shr { &out2("shrl",@_); }
++sub main'xor { &out2("xorl",@_); }
++sub main'xorb { &out2("xorb",@_); }
++sub main'add { &out2("addl",@_); }
++sub main'adc { &out2("adcl",@_); }
++sub main'sub { &out2("subl",@_); }
++sub main'rotl { &out2("roll",@_); }
++sub main'rotr { &out2("rorl",@_); }
++sub main'exch { &out2("xchg",@_); }
++sub main'cmp { &out2("cmpl",@_); }
++sub main'lea { &out2("leal",@_); }
++sub main'mul { &out1("mull",@_); }
++sub main'div { &out1("divl",@_); }
++sub main'jmp { &out1("jmp",@_); }
++sub main'jmp_ptr { &out1p("jmp",@_); }
++sub main'je { &out1("je",@_); }
++sub main'jle { &out1("jle",@_); }
++sub main'jne { &out1("jne",@_); }
++sub main'jnz { &out1("jnz",@_); }
++sub main'jz { &out1("jz",@_); }
++sub main'jge { &out1("jge",@_); }
++sub main'jl { &out1("jl",@_); }
++sub main'jb { &out1("jb",@_); }
++sub main'dec { &out1("decl",@_); }
++sub main'inc { &out1("incl",@_); }
++sub main'push { &out1("pushl",@_); $stack+=4; }
++sub main'pop { &out1("popl",@_); $stack-=4; }
++sub main'bswap { &out1("bswapl",@_); }
++sub main'not { &out1("notl",@_); }
++sub main'call { &out1("call",$under.$_[0]); }
++sub main'ret { &out0("ret"); }
++sub main'nop { &out0("nop"); }
++
++sub out2
++ {
++ local($name,$p1,$p2)=@_;
++ local($l,$ll,$t);
++ local(%special)=( "roll",0xD1C0,"rorl",0xD1C8,
++ "rcll",0xD1D0,"rcrl",0xD1D8,
++ "shll",0xD1E0,"shrl",0xD1E8,
++ "sarl",0xD1F8);
++
++ if ((defined($special{$name})) && defined($regs{$p1}) && ($p2 == 1))
++ {
++ $op=$special{$name}|$reg_val{$p1};
++ $tmp1=sprintf ".byte %d\n",($op>>8)&0xff;
++ $tmp2=sprintf ".byte %d\t",$op &0xff;
++ push(@out,$tmp1);
++ push(@out,$tmp2);
++
++ $p2=&conv($p2);
++ $p1=&conv($p1);
++ &main'comment("$name $p2 $p1");
++ return;
++ }
++
++ push(@out,"\t$name\t");
++ $t=&conv($p2).",";
++ $l=length($t);
++ push(@out,$t);
++ $ll=4-($l+9)/8;
++ $tmp1=sprintf "\t" x $ll;
++ push(@out,$tmp1);
++ push(@out,&conv($p1)."\n");
++ }
++
++sub out1
++ {
++ local($name,$p1)=@_;
++ local($l,$t);
++
++ push(@out,"\t$name\t".&conv($p1)."\n");
++ }
++
++sub out1p
++ {
++ local($name,$p1)=@_;
++ local($l,$t);
++
++ push(@out,"\t$name\t*".&conv($p1)."\n");
++ }
++
++sub out0
++ {
++ push(@out,"\t$_[0]\n");
++ }
++
++sub conv
++ {
++ local($p)=@_;
++
++# $p =~ s/0x([0-9A-Fa-f]+)/0$1h/;
++
++ $p=$regs{$p} if (defined($regs{$p}));
++
++ $p =~ s/^(-{0,1}[0-9A-Fa-f]+)$/\$$1/;
++ $p =~ s/^(0x[0-9A-Fa-f]+)$/\$$1/;
++ return $p;
++ }
++
++sub main'file
++ {
++ local($file)=@_;
++
++ local($tmp)=<<"EOF";
++ .file "$file.s"
++ .version "01.01"
++gcc2_compiled.:
++EOF
++ push(@out,$tmp);
++ }
++
++sub main'function_begin
++ {
++ local($func)=@_;
++
++ $func=$under.$func;
++
++ local($tmp)=<<"EOF";
++.text
++ .align $align
++.globl $func
++EOF
++ push(@out,$tmp);
++ if ($main'cpp)
++ { $tmp=push(@out,"\tTYPE($func,\@function)\n"); }
++ else { $tmp=push(@out,"\t.type\t$func,\@function\n"); }
++ push(@out,"$func:\n");
++ $tmp=<<"EOF";
++ pushl %ebp
++ pushl %ebx
++ pushl %esi
++ pushl %edi
++
++EOF
++ push(@out,$tmp);
++ $stack=20;
++ }
++
++sub main'function_begin_B
++ {
++ local($func,$extra)=@_;
++
++ $func=$under.$func;
++
++ local($tmp)=<<"EOF";
++.text
++ .align $align
++.globl $func
++EOF
++ push(@out,$tmp);
++ if ($main'cpp)
++ { push(@out,"\tTYPE($func,\@function)\n"); }
++ else { push(@out,"\t.type $func,\@function\n"); }
++ push(@out,"$func:\n");
++ $stack=4;
++ }
++
++sub main'function_end
++ {
++ local($func)=@_;
++
++ $func=$under.$func;
++
++ local($tmp)=<<"EOF";
++ popl %edi
++ popl %esi
++ popl %ebx
++ popl %ebp
++ ret
++.${func}_end:
++EOF
++ push(@out,$tmp);
++ if ($main'cpp)
++ { push(@out,"\tSIZE($func,.${func}_end-$func)\n"); }
++ else { push(@out,"\t.size\t$func,.${func}_end-$func\n"); }
++ push(@out,".ident \"$func\"\n");
++ $stack=0;
++ %label=();
++ }
++
++sub main'function_end_A
++ {
++ local($func)=@_;
++
++ local($tmp)=<<"EOF";
++ popl %edi
++ popl %esi
++ popl %ebx
++ popl %ebp
++ ret
++EOF
++ push(@out,$tmp);
++ }
++
++sub main'function_end_B
++ {
++ local($func)=@_;
++
++ $func=$under.$func;
++
++ push(@out,".${func}_end:\n");
++ if ($main'cpp)
++ { push(@out,"\tSIZE($func,.${func}_end-$func)\n"); }
++ else { push(@out,"\t.size\t$func,.${func}_end-$func\n"); }
++ push(@out,".ident \"desasm.pl\"\n");
++ $stack=0;
++ %label=();
++ }
++
++sub main'wparam
++ {
++ local($num)=@_;
++
++ return(&main'DWP($stack+$num*4,"esp","",0));
++ }
++
++sub main'stack_push
++ {
++ local($num)=@_;
++ $stack+=$num*4;
++ &main'sub("esp",$num*4);
++ }
++
++sub main'stack_pop
++ {
++ local($num)=@_;
++ $stack-=$num*4;
++ &main'add("esp",$num*4);
++ }
++
++sub main'swtmp
++ {
++ return(&main'DWP($_[0]*4,"esp","",0));
++ }
++
++# Should use swtmp, which is above esp. Linix can trash the stack above esp
++#sub main'wtmp
++# {
++# local($num)=@_;
++#
++# return(&main'DWP(-($num+1)*4,"esp","",0));
++# }
++
++sub main'comment
++ {
++ foreach (@_)
++ {
++ if (/^\s*$/)
++ { push(@out,"\n"); }
++ else
++ { push(@out,"\t$com_start $_ $com_end\n"); }
++ }
++ }
++
++sub main'label
++ {
++ if (!defined($label{$_[0]}))
++ {
++ $label{$_[0]}=".${label}${_[0]}";
++ $label++;
++ }
++ return($label{$_[0]});
++ }
++
++sub main'set_label
++ {
++ if (!defined($label{$_[0]}))
++ {
++ $label{$_[0]}=".${label}${_[0]}";
++ $label++;
++ }
++ push(@out,".align $align\n") if ($_[1] != 0);
++ push(@out,"$label{$_[0]}:\n");
++ }
++
++sub main'file_end
++ {
++ }
++
++sub main'data_word
++ {
++ push(@out,"\t.long $_[0]\n");
++ }
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/crypto/ciphers/des/asm/readme Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,131 @@
++First up, let me say I don't like writing in assembler. It is not portable,
++dependant on the particular CPU architecture release and is generally a pig
++to debug and get right. Having said that, the x86 architecture is probably
++the most important for speed due to number of boxes and since
++it appears to be the worst architecture to to get
++good C compilers for. So due to this, I have lowered myself to do
++assembler for the inner DES routines in libdes :-).
++
++The file to implement in assembler is des_enc.c. Replace the following
++4 functions
++des_encrypt(DES_LONG data[2],des_key_schedule ks, int encrypt);
++des_encrypt2(DES_LONG data[2],des_key_schedule ks, int encrypt);
++des_encrypt3(DES_LONG data[2],des_key_schedule ks1,ks2,ks3);
++des_decrypt3(DES_LONG data[2],des_key_schedule ks1,ks2,ks3);
++
++They encrypt/decrypt the 64 bits held in 'data' using
++the 'ks' key schedules. The only difference between the 4 functions is that
++des_encrypt2() does not perform IP() or FP() on the data (this is an
++optimization for when doing triple DES and des_encrypt3() and des_decrypt3()
++perform triple des. The triple DES routines are in here because it does
++make a big difference to have them located near the des_encrypt2 function
++at link time..
++
++Now as we all know, there are lots of different operating systems running on
++x86 boxes, and unfortunately they normally try to make sure their assembler
++formating is not the same as the other peoples.
++The 4 main formats I know of are
++Microsoft Windows 95/Windows NT
++Elf Includes Linux and FreeBSD(?).
++a.out The older Linux.
++Solaris Same as Elf but different comments :-(.
++
++Now I was not overly keen to write 4 different copies of the same code,
++so I wrote a few perl routines to output the correct assembler, given
++a target assembler type. This code is ugly and is just a hack.
++The libraries are x86unix.pl and x86ms.pl.
++des586.pl, des686.pl and des-som[23].pl are the programs to actually
++generate the assembler.
++
++So to generate elf assembler
++perl des-som3.pl elf >dx86-elf.s
++For Windows 95/NT
++perl des-som2.pl win32 >win32.asm
++
++[ update 4 Jan 1996 ]
++I have added another way to do things.
++perl des-som3.pl cpp >dx86-cpp.s
++generates a file that will be included by dx86unix.cpp when it is compiled.
++To build for elf, a.out, solaris, bsdi etc,
++cc -E -DELF asm/dx86unix.cpp | as -o asm/dx86-elf.o
++cc -E -DSOL asm/dx86unix.cpp | as -o asm/dx86-sol.o
++cc -E -DOUT asm/dx86unix.cpp | as -o asm/dx86-out.o
++cc -E -DBSDI asm/dx86unix.cpp | as -o asm/dx86bsdi.o
++This was done to cut down the number of files in the distribution.
++
++Now the ugly part. I acquired my copy of Intels
++"Optimization's For Intel's 32-Bit Processors" and found a few interesting
++things. First, the aim of the exersize is to 'extract' one byte at a time
++from a word and do an array lookup. This involves getting the byte from
++the 4 locations in the word and moving it to a new word and doing the lookup.
++The most obvious way to do this is
++xor eax, eax # clear word
++movb al, cl # get low byte
++xor edi DWORD PTR 0x100+des_SP[eax] # xor in word
++movb al, ch # get next byte
++xor edi DWORD PTR 0x300+des_SP[eax] # xor in word
++shr ecx 16
++which seems ok. For the pentium, this system appears to be the best.
++One has to do instruction interleaving to keep both functional units
++operating, but it is basically very efficient.
++
++Now the crunch. When a full register is used after a partial write, eg.
++mov al, cl
++xor edi, DWORD PTR 0x100+des_SP[eax]
++386 - 1 cycle stall
++486 - 1 cycle stall
++586 - 0 cycle stall
++686 - at least 7 cycle stall (page 22 of the above mentioned document).
++
++So the technique that produces the best results on a pentium, according to
++the documentation, will produce hideous results on a pentium pro.
++
++To get around this, des686.pl will generate code that is not as fast on
++a pentium, should be very good on a pentium pro.
++mov eax, ecx # copy word
++shr ecx, 8 # line up next byte
++and eax, 0fch # mask byte
++xor edi DWORD PTR 0x100+des_SP[eax] # xor in array lookup
++mov eax, ecx # get word
++shr ecx 8 # line up next byte
++and eax, 0fch # mask byte
++xor edi DWORD PTR 0x300+des_SP[eax] # xor in array lookup
++
++Due to the execution units in the pentium, this actually works quite well.
++For a pentium pro it should be very good. This is the type of output
++Visual C++ generates.
++
++There is a third option. instead of using
++mov al, ch
++which is bad on the pentium pro, one may be able to use
++movzx eax, ch
++which may not incur the partial write penalty. On the pentium,
++this instruction takes 4 cycles so is not worth using but on the
++pentium pro it appears it may be worth while. I need access to one to
++experiment :-).
++
++eric (20 Oct 1996)
++
++22 Nov 1996 - I have asked people to run the 2 different version on pentium
++pros and it appears that the intel documentation is wrong. The
++mov al,bh is still faster on a pentium pro, so just use the des586.pl
++install des686.pl
++
++3 Dec 1996 - I added des_encrypt3/des_decrypt3 because I have moved these
++functions into des_enc.c because it does make a massive performance
++difference on some boxes to have the functions code located close to
++the des_encrypt2() function.
++
++9 Jan 1997 - des-som2.pl is now the correct perl script to use for
++pentiums. It contains an inner loop from
++Svend Olaf Mikkelsen <svolaf@inet.uni-c.dk> which does raw ecb DES calls at
++273,000 per second. He had a previous version at 250,000 and the best
++I was able to get was 203,000. The content has not changed, this is all
++due to instruction sequencing (and actual instructions choice) which is able
++to keep both functional units of the pentium going.
++We may have lost the ugly register usage restrictions when x86 went 32 bit
++but for the pentium it has been replaced by evil instruction ordering tricks.
++
++13 Jan 1997 - des-som3.pl, more optimizations from Svend Olaf.
++raw DES at 281,000 per second on a pentium 100.
++
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/crypto/ciphers/des/cbc_enc.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,135 @@
++/* crypto/des/cbc_enc.c */
++/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com)
++ * All rights reserved.
++ *
++ * This package is an SSL implementation written
++ * by Eric Young (eay@cryptsoft.com).
++ * The implementation was written so as to conform with Netscapes SSL.
++ *
++ * This library is free for commercial and non-commercial use as long as
++ * the following conditions are aheared to. The following conditions
++ * apply to all code found in this distribution, be it the RC4, RSA,
++ * lhash, DES, etc., code; not just the SSL code. The SSL documentation
++ * included with this distribution is covered by the same copyright terms
++ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
++ *
++ * Copyright remains Eric Young's, and as such any Copyright notices in
++ * the code are not to be removed.
++ * If this package is used in a product, Eric Young should be given attribution
++ * as the author of the parts of the library used.
++ * This can be in the form of a textual message at program startup or
++ * in documentation (online or textual) provided with the package.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ * 1. Redistributions of source code must retain the copyright
++ * notice, this list of conditions and the following disclaimer.
++ * 2. Redistributions in binary form must reproduce the above copyright
++ * notice, this list of conditions and the following disclaimer in the
++ * documentation and/or other materials provided with the distribution.
++ * 3. All advertising materials mentioning features or use of this software
++ * must display the following acknowledgement:
++ * "This product includes cryptographic software written by
++ * Eric Young (eay@cryptsoft.com)"
++ * The word 'cryptographic' can be left out if the rouines from the library
++ * being used are not cryptographic related :-).
++ * 4. If you include any Windows specific code (or a derivative thereof) from
++ * the apps directory (application code) you must include an acknowledgement:
++ * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
++ *
++ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ *
++ * The licence and distribution terms for any publically available version or
++ * derivative of this code cannot be changed. i.e. this code cannot simply be
++ * copied and put under another distribution licence
++ * [including the GNU Public Licence.]
++ */
++
++#include "des_locl.h"
++
++void des_cbc_encrypt(input, output, length, schedule, ivec, enc)
++des_cblock (*input);
++des_cblock (*output);
++long length;
++des_key_schedule schedule;
++des_cblock (*ivec);
++int enc;
++ {
++ register DES_LONG tin0,tin1;
++ register DES_LONG tout0,tout1,xor0,xor1;
++ register unsigned char *in,*out;
++ register long l=length;
++ DES_LONG tin[2];
++ unsigned char *iv;
++
++ in=(unsigned char *)input;
++ out=(unsigned char *)output;
++ iv=(unsigned char *)ivec;
++
++ if (enc)
++ {
++ c2l(iv,tout0);
++ c2l(iv,tout1);
++ for (l-=8; l>=0; l-=8)
++ {
++ c2l(in,tin0);
++ c2l(in,tin1);
++ tin0^=tout0; tin[0]=tin0;
++ tin1^=tout1; tin[1]=tin1;
++ des_encrypt((DES_LONG *)tin,schedule,DES_ENCRYPT);
++ tout0=tin[0]; l2c(tout0,out);
++ tout1=tin[1]; l2c(tout1,out);
++ }
++ if (l != -8)
++ {
++ c2ln(in,tin0,tin1,l+8);
++ tin0^=tout0; tin[0]=tin0;
++ tin1^=tout1; tin[1]=tin1;
++ des_encrypt((DES_LONG *)tin,schedule,DES_ENCRYPT);
++ tout0=tin[0]; l2c(tout0,out);
++ tout1=tin[1]; l2c(tout1,out);
++ }
++ }
++ else
++ {
++ c2l(iv,xor0);
++ c2l(iv,xor1);
++ for (l-=8; l>=0; l-=8)
++ {
++ c2l(in,tin0); tin[0]=tin0;
++ c2l(in,tin1); tin[1]=tin1;
++ des_encrypt((DES_LONG *)tin,schedule,DES_DECRYPT);
++ tout0=tin[0]^xor0;
++ tout1=tin[1]^xor1;
++ l2c(tout0,out);
++ l2c(tout1,out);
++ xor0=tin0;
++ xor1=tin1;
++ }
++ if (l != -8)
++ {
++ c2l(in,tin0); tin[0]=tin0;
++ c2l(in,tin1); tin[1]=tin1;
++ des_encrypt((DES_LONG *)tin,schedule,DES_DECRYPT);
++ tout0=tin[0]^xor0;
++ tout1=tin[1]^xor1;
++ l2cn(tout0,tout1,out,l+8);
++ /* xor0=tin0;
++ xor1=tin1; */
++ }
++ }
++ tin0=tin1=tout0=tout1=xor0=xor1=0;
++ tin[0]=tin[1]=0;
++ }
++
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/crypto/ciphers/des/des.doc Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,505 @@
++The DES library.
++
++Please note that this library was originally written to operate with
++eBones, a version of Kerberos that had had encryption removed when it left
++the USA and then put back in. As such there are some routines that I will
++advise not using but they are still in the library for historical reasons.
++For all calls that have an 'input' and 'output' variables, they can be the
++same.
++
++This library requires the inclusion of 'des.h'.
++
++All of the encryption functions take what is called a des_key_schedule as an
++argument. A des_key_schedule is an expanded form of the des key.
++A des_key is 8 bytes of odd parity, the type used to hold the key is a
++des_cblock. A des_cblock is an array of 8 bytes, often in this library
++description I will refer to input bytes when the function specifies
++des_cblock's as input or output, this just means that the variable should
++be a multiple of 8 bytes.
++
++The define DES_ENCRYPT is passed to specify encryption, DES_DECRYPT to
++specify decryption. The functions and global variable are as follows:
++
++int des_check_key;
++ DES keys are supposed to be odd parity. If this variable is set to
++ a non-zero value, des_set_key() will check that the key has odd
++ parity and is not one of the known weak DES keys. By default this
++ variable is turned off;
++
++void des_set_odd_parity(
++des_cblock *key );
++ This function takes a DES key (8 bytes) and sets the parity to odd.
++
++int des_is_weak_key(
++des_cblock *key );
++ This function returns a non-zero value if the DES key passed is a
++ weak, DES key. If it is a weak key, don't use it, try a different
++ one. If you are using 'random' keys, the chances of hitting a weak
++ key are 1/2^52 so it is probably not worth checking for them.
++
++int des_set_key(
++des_cblock *key,
++des_key_schedule schedule);
++ Des_set_key converts an 8 byte DES key into a des_key_schedule.
++ A des_key_schedule is an expanded form of the key which is used to
++ perform actual encryption. It can be regenerated from the DES key
++ so it only needs to be kept when encryption or decryption is about
++ to occur. Don't save or pass around des_key_schedule's since they
++ are CPU architecture dependent, DES keys are not. If des_check_key
++ is non zero, zero is returned if the key has the wrong parity or
++ the key is a weak key, else 1 is returned.
++
++int des_key_sched(
++des_cblock *key,
++des_key_schedule schedule);
++ An alternative name for des_set_key().
++
++int des_rw_mode; /* defaults to DES_PCBC_MODE */
++ This flag holds either DES_CBC_MODE or DES_PCBC_MODE (default).
++ This specifies the function to use in the enc_read() and enc_write()
++ functions.
++
++void des_encrypt(
++unsigned long *data,
++des_key_schedule ks,
++int enc);
++ This is the DES encryption function that gets called by just about
++ every other DES routine in the library. You should not use this
++ function except to implement 'modes' of DES. I say this because the
++ functions that call this routine do the conversion from 'char *' to
++ long, and this needs to be done to make sure 'non-aligned' memory
++ access do not occur. The characters are loaded 'little endian',
++ have a look at my source code for more details on how I use this
++ function.
++ Data is a pointer to 2 unsigned long's and ks is the
++ des_key_schedule to use. enc, is non zero specifies encryption,
++ zero if decryption.
++
++void des_encrypt2(
++unsigned long *data,
++des_key_schedule ks,
++int enc);
++ This functions is the same as des_encrypt() except that the DES
++ initial permutation (IP) and final permutation (FP) have been left
++ out. As for des_encrypt(), you should not use this function.
++ It is used by the routines in my library that implement triple DES.
++ IP() des_encrypt2() des_encrypt2() des_encrypt2() FP() is the same
++ as des_encrypt() des_encrypt() des_encrypt() except faster :-).
++
++void des_ecb_encrypt(
++des_cblock *input,
++des_cblock *output,
++des_key_schedule ks,
++int enc);
++ This is the basic Electronic Code Book form of DES, the most basic
++ form. Input is encrypted into output using the key represented by
++ ks. If enc is non zero (DES_ENCRYPT), encryption occurs, otherwise
++ decryption occurs. Input is 8 bytes long and output is 8 bytes.
++ (the des_cblock structure is 8 chars).
++
++void des_ecb3_encrypt(
++des_cblock *input,
++des_cblock *output,
++des_key_schedule ks1,
++des_key_schedule ks2,
++des_key_schedule ks3,
++int enc);
++ This is the 3 key EDE mode of ECB DES. What this means is that
++ the 8 bytes of input is encrypted with ks1, decrypted with ks2 and
++ then encrypted again with ks3, before being put into output;
++ C=E(ks3,D(ks2,E(ks1,M))). There is a macro, des_ecb2_encrypt()
++ that only takes 2 des_key_schedules that implements,
++ C=E(ks1,D(ks2,E(ks1,M))) in that the final encrypt is done with ks1.
++
++void des_cbc_encrypt(
++des_cblock *input,
++des_cblock *output,
++long length,
++des_key_schedule ks,
++des_cblock *ivec,
++int enc);
++ This routine implements DES in Cipher Block Chaining mode.
++ Input, which should be a multiple of 8 bytes is encrypted
++ (or decrypted) to output which will also be a multiple of 8 bytes.
++ The number of bytes is in length (and from what I've said above,
++ should be a multiple of 8). If length is not a multiple of 8, I'm
++ not being held responsible :-). ivec is the initialisation vector.
++ This function does not modify this variable. To correctly implement
++ cbc mode, you need to do one of 2 things; copy the last 8 bytes of
++ cipher text for use as the next ivec in your application,
++ or use des_ncbc_encrypt().
++ Only this routine has this problem with updating the ivec, all
++ other routines that are implementing cbc mode update ivec.
++
++void des_ncbc_encrypt(
++des_cblock *input,
++des_cblock *output,
++long length,
++des_key_schedule sk,
++des_cblock *ivec,
++int enc);
++ For historical reasons, des_cbc_encrypt() did not update the
++ ivec with the value requires so that subsequent calls to
++ des_cbc_encrypt() would 'chain'. This was needed so that the same
++ 'length' values would not need to be used when decrypting.
++ des_ncbc_encrypt() does the right thing. It is the same as
++ des_cbc_encrypt accept that ivec is updates with the correct value
++ to pass in subsequent calls to des_ncbc_encrypt(). I advise using
++ des_ncbc_encrypt() instead of des_cbc_encrypt();
++
++void des_xcbc_encrypt(
++des_cblock *input,
++des_cblock *output,
++long length,
++des_key_schedule sk,
++des_cblock *ivec,
++des_cblock *inw,
++des_cblock *outw,
++int enc);
++ This is RSA's DESX mode of DES. It uses inw and outw to
++ 'whiten' the encryption. inw and outw are secret (unlike the iv)
++ and are as such, part of the key. So the key is sort of 24 bytes.
++ This is much better than cbc des.
++
++void des_3cbc_encrypt(
++des_cblock *input,
++des_cblock *output,
++long length,
++des_key_schedule sk1,
++des_key_schedule sk2,
++des_cblock *ivec1,
++des_cblock *ivec2,
++int enc);
++ This function is flawed, do not use it. I have left it in the
++ library because it is used in my des(1) program and will function
++ correctly when used by des(1). If I removed the function, people
++ could end up unable to decrypt files.
++ This routine implements outer triple cbc encryption using 2 ks and
++ 2 ivec's. Use des_ede2_cbc_encrypt() instead.
++
++void des_ede3_cbc_encrypt(
++des_cblock *input,
++des_cblock *output,
++long length,
++des_key_schedule ks1,
++des_key_schedule ks2,
++des_key_schedule ks3,
++des_cblock *ivec,
++int enc);
++ This function implements inner triple CBC DES encryption with 3
++ keys. What this means is that each 'DES' operation
++ inside the cbc mode is really an C=E(ks3,D(ks2,E(ks1,M))).
++ Again, this is cbc mode so an ivec is requires.
++ This mode is used by SSL.
++ There is also a des_ede2_cbc_encrypt() that only uses 2
++ des_key_schedule's, the first being reused for the final
++ encryption. C=E(ks1,D(ks2,E(ks1,M))). This form of triple DES
++ is used by the RSAref library.
++
++void des_pcbc_encrypt(
++des_cblock *input,
++des_cblock *output,
++long length,
++des_key_schedule ks,
++des_cblock *ivec,
++int enc);
++ This is Propagating Cipher Block Chaining mode of DES. It is used
++ by Kerberos v4. It's parameters are the same as des_ncbc_encrypt().
++
++void des_cfb_encrypt(
++unsigned char *in,
++unsigned char *out,
++int numbits,
++long length,
++des_key_schedule ks,
++des_cblock *ivec,
++int enc);
++ Cipher Feedback Back mode of DES. This implementation 'feeds back'
++ in numbit blocks. The input (and output) is in multiples of numbits
++ bits. numbits should to be a multiple of 8 bits. Length is the
++ number of bytes input. If numbits is not a multiple of 8 bits,
++ the extra bits in the bytes will be considered padding. So if
++ numbits is 12, for each 2 input bytes, the 4 high bits of the
++ second byte will be ignored. So to encode 72 bits when using
++ a numbits of 12 take 12 bytes. To encode 72 bits when using
++ numbits of 9 will take 16 bytes. To encode 80 bits when using
++ numbits of 16 will take 10 bytes. etc, etc. This padding will
++ apply to both input and output.
++
++
++void des_cfb64_encrypt(
++unsigned char *in,
++unsigned char *out,
++long length,
++des_key_schedule ks,
++des_cblock *ivec,
++int *num,
++int enc);
++ This is one of the more useful functions in this DES library, it
++ implements CFB mode of DES with 64bit feedback. Why is this
++ useful you ask? Because this routine will allow you to encrypt an
++ arbitrary number of bytes, no 8 byte padding. Each call to this
++ routine will encrypt the input bytes to output and then update ivec
++ and num. num contains 'how far' we are though ivec. If this does
++ not make much sense, read more about cfb mode of DES :-).
++
++void des_ede3_cfb64_encrypt(
++unsigned char *in,
++unsigned char *out,
++long length,
++des_key_schedule ks1,
++des_key_schedule ks2,
++des_key_schedule ks3,
++des_cblock *ivec,
++int *num,
++int enc);
++ Same as des_cfb64_encrypt() accept that the DES operation is
++ triple DES. As usual, there is a macro for
++ des_ede2_cfb64_encrypt() which reuses ks1.
++
++void des_ofb_encrypt(
++unsigned char *in,
++unsigned char *out,
++int numbits,
++long length,
++des_key_schedule ks,
++des_cblock *ivec);
++ This is a implementation of Output Feed Back mode of DES. It is
++ the same as des_cfb_encrypt() in that numbits is the size of the
++ units dealt with during input and output (in bits).
++
++void des_ofb64_encrypt(
++unsigned char *in,
++unsigned char *out,
++long length,
++des_key_schedule ks,
++des_cblock *ivec,
++int *num);
++ The same as des_cfb64_encrypt() except that it is Output Feed Back
++ mode.
++
++void des_ede3_ofb64_encrypt(
++unsigned char *in,
++unsigned char *out,
++long length,
++des_key_schedule ks1,
++des_key_schedule ks2,
++des_key_schedule ks3,
++des_cblock *ivec,
++int *num);
++ Same as des_ofb64_encrypt() accept that the DES operation is
++ triple DES. As usual, there is a macro for
++ des_ede2_ofb64_encrypt() which reuses ks1.
++
++int des_read_pw_string(
++char *buf,
++int length,
++char *prompt,
++int verify);
++ This routine is used to get a password from the terminal with echo
++ turned off. Buf is where the string will end up and length is the
++ size of buf. Prompt is a string presented to the 'user' and if
++ verify is set, the key is asked for twice and unless the 2 copies
++ match, an error is returned. A return code of -1 indicates a
++ system error, 1 failure due to use interaction, and 0 is success.
++
++unsigned long des_cbc_cksum(
++des_cblock *input,
++des_cblock *output,
++long length,
++des_key_schedule ks,
++des_cblock *ivec);
++ This function produces an 8 byte checksum from input that it puts in
++ output and returns the last 4 bytes as a long. The checksum is
++ generated via cbc mode of DES in which only the last 8 byes are
++ kept. I would recommend not using this function but instead using
++ the EVP_Digest routines, or at least using MD5 or SHA. This
++ function is used by Kerberos v4 so that is why it stays in the
++ library.
++
++char *des_fcrypt(
++const char *buf,
++const char *salt
++char *ret);
++ This is my fast version of the unix crypt(3) function. This version
++ takes only a small amount of space relative to other fast
++ crypt() implementations. This is different to the normal crypt
++ in that the third parameter is the buffer that the return value
++ is written into. It needs to be at least 14 bytes long. This
++ function is thread safe, unlike the normal crypt.
++
++char *crypt(
++const char *buf,
++const char *salt);
++ This function calls des_fcrypt() with a static array passed as the
++ third parameter. This emulates the normal non-thread safe semantics
++ of crypt(3).
++
++void des_string_to_key(
++char *str,
++des_cblock *key);
++ This function takes str and converts it into a DES key. I would
++ recommend using MD5 instead and use the first 8 bytes of output.
++ When I wrote the first version of these routines back in 1990, MD5
++ did not exist but I feel these routines are still sound. This
++ routines is compatible with the one in MIT's libdes.
++
++void des_string_to_2keys(
++char *str,
++des_cblock *key1,
++des_cblock *key2);
++ This function takes str and converts it into 2 DES keys.
++ I would recommend using MD5 and using the 16 bytes as the 2 keys.
++ I have nothing against these 2 'string_to_key' routines, it's just
++ that if you say that your encryption key is generated by using the
++ 16 bytes of an MD5 hash, every-one knows how you generated your
++ keys.
++
++int des_read_password(
++des_cblock *key,
++char *prompt,
++int verify);
++ This routine combines des_read_pw_string() with des_string_to_key().
++
++int des_read_2passwords(
++des_cblock *key1,
++des_cblock *key2,
++char *prompt,
++int verify);
++ This routine combines des_read_pw_string() with des_string_to_2key().
++
++void des_random_seed(
++des_cblock key);
++ This routine sets a starting point for des_random_key().
++
++void des_random_key(
++des_cblock ret);
++ This function return a random key. Make sure to 'seed' the random
++ number generator (with des_random_seed()) before using this function.
++ I personally now use a MD5 based random number system.
++
++int des_enc_read(
++int fd,
++char *buf,
++int len,
++des_key_schedule ks,
++des_cblock *iv);
++ This function will write to a file descriptor the encrypted data
++ from buf. This data will be preceded by a 4 byte 'byte count' and
++ will be padded out to 8 bytes. The encryption is either CBC of
++ PCBC depending on the value of des_rw_mode. If it is DES_PCBC_MODE,
++ pcbc is used, if DES_CBC_MODE, cbc is used. The default is to use
++ DES_PCBC_MODE.
++
++int des_enc_write(
++int fd,
++char *buf,
++int len,
++des_key_schedule ks,
++des_cblock *iv);
++ This routines read stuff written by des_enc_read() and decrypts it.
++ I have used these routines quite a lot but I don't believe they are
++ suitable for non-blocking io. If you are after a full
++ authentication/encryption over networks, have a look at SSL instead.
++
++unsigned long des_quad_cksum(
++des_cblock *input,
++des_cblock *output,
++long length,
++int out_count,
++des_cblock *seed);
++ This is a function from Kerberos v4 that is not anything to do with
++ DES but was needed. It is a cksum that is quicker to generate than
++ des_cbc_cksum(); I personally would use MD5 routines now.
++=====
++Modes of DES
++Quite a bit of the following information has been taken from
++ AS 2805.5.2
++ Australian Standard
++ Electronic funds transfer - Requirements for interfaces,
++ Part 5.2: Modes of operation for an n-bit block cipher algorithm
++ Appendix A
++
++There are several different modes in which DES can be used, they are
++as follows.
++
++Electronic Codebook Mode (ECB) (des_ecb_encrypt())
++- 64 bits are enciphered at a time.
++- The order of the blocks can be rearranged without detection.
++- The same plaintext block always produces the same ciphertext block
++ (for the same key) making it vulnerable to a 'dictionary attack'.
++- An error will only affect one ciphertext block.
++
++Cipher Block Chaining Mode (CBC) (des_cbc_encrypt())
++- a multiple of 64 bits are enciphered at a time.
++- The CBC mode produces the same ciphertext whenever the same
++ plaintext is encrypted using the same key and starting variable.
++- The chaining operation makes the ciphertext blocks dependent on the
++ current and all preceding plaintext blocks and therefore blocks can not
++ be rearranged.
++- The use of different starting variables prevents the same plaintext
++ enciphering to the same ciphertext.
++- An error will affect the current and the following ciphertext blocks.
++
++Cipher Feedback Mode (CFB) (des_cfb_encrypt())
++- a number of bits (j) <= 64 are enciphered at a time.
++- The CFB mode produces the same ciphertext whenever the same
++ plaintext is encrypted using the same key and starting variable.
++- The chaining operation makes the ciphertext variables dependent on the
++ current and all preceding variables and therefore j-bit variables are
++ chained together and can not be rearranged.
++- The use of different starting variables prevents the same plaintext
++ enciphering to the same ciphertext.
++- The strength of the CFB mode depends on the size of k (maximal if
++ j == k). In my implementation this is always the case.
++- Selection of a small value for j will require more cycles through
++ the encipherment algorithm per unit of plaintext and thus cause
++ greater processing overheads.
++- Only multiples of j bits can be enciphered.
++- An error will affect the current and the following ciphertext variables.
++
++Output Feedback Mode (OFB) (des_ofb_encrypt())
++- a number of bits (j) <= 64 are enciphered at a time.
++- The OFB mode produces the same ciphertext whenever the same
++ plaintext enciphered using the same key and starting variable. More
++ over, in the OFB mode the same key stream is produced when the same
++ key and start variable are used. Consequently, for security reasons
++ a specific start variable should be used only once for a given key.
++- The absence of chaining makes the OFB more vulnerable to specific attacks.
++- The use of different start variables values prevents the same
++ plaintext enciphering to the same ciphertext, by producing different
++ key streams.
++- Selection of a small value for j will require more cycles through
++ the encipherment algorithm per unit of plaintext and thus cause
++ greater processing overheads.
++- Only multiples of j bits can be enciphered.
++- OFB mode of operation does not extend ciphertext errors in the
++ resultant plaintext output. Every bit error in the ciphertext causes
++ only one bit to be in error in the deciphered plaintext.
++- OFB mode is not self-synchronising. If the two operation of
++ encipherment and decipherment get out of synchronism, the system needs
++ to be re-initialised.
++- Each re-initialisation should use a value of the start variable
++ different from the start variable values used before with the same
++ key. The reason for this is that an identical bit stream would be
++ produced each time from the same parameters. This would be
++ susceptible to a ' known plaintext' attack.
++
++Triple ECB Mode (des_ecb3_encrypt())
++- Encrypt with key1, decrypt with key2 and encrypt with key3 again.
++- As for ECB encryption but increases the key length to 168 bits.
++ There are theoretic attacks that can be used that make the effective
++ key length 112 bits, but this attack also requires 2^56 blocks of
++ memory, not very likely, even for the NSA.
++- If both keys are the same it is equivalent to encrypting once with
++ just one key.
++- If the first and last key are the same, the key length is 112 bits.
++ There are attacks that could reduce the key space to 55 bit's but it
++ requires 2^56 blocks of memory.
++- If all 3 keys are the same, this is effectively the same as normal
++ ecb mode.
++
++Triple CBC Mode (des_ede3_cbc_encrypt())
++- Encrypt with key1, decrypt with key2 and then encrypt with key3.
++- As for CBC encryption but increases the key length to 168 bits with
++ the same restrictions as for triple ecb mode.
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/crypto/ciphers/des/des_crypt.man Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,508 @@
++.TH DES_CRYPT 3
++.SH NAME
++des_read_password, des_read_2password,
++des_string_to_key, des_string_to_2key, des_read_pw_string,
++des_random_key, des_set_key,
++des_key_sched, des_ecb_encrypt, des_ecb3_encrypt, des_cbc_encrypt,
++des_3cbc_encrypt,
++des_pcbc_encrypt, des_cfb_encrypt, des_ofb_encrypt,
++des_cbc_cksum, des_quad_cksum,
++des_enc_read, des_enc_write, des_set_odd_parity,
++des_is_weak_key, crypt \- (non USA) DES encryption
++.SH SYNOPSIS
++.nf
++.nj
++.ft B
++#include <des.h>
++.PP
++.B int des_read_password(key,prompt,verify)
++des_cblock *key;
++char *prompt;
++int verify;
++.PP
++.B int des_read_2password(key1,key2,prompt,verify)
++des_cblock *key1,*key2;
++char *prompt;
++int verify;
++.PP
++.B int des_string_to_key(str,key)
++char *str;
++des_cblock *key;
++.PP
++.B int des_string_to_2keys(str,key1,key2)
++char *str;
++des_cblock *key1,*key2;
++.PP
++.B int des_read_pw_string(buf,length,prompt,verify)
++char *buf;
++int length;
++char *prompt;
++int verify;
++.PP
++.B int des_random_key(key)
++des_cblock *key;
++.PP
++.B int des_set_key(key,schedule)
++des_cblock *key;
++des_key_schedule schedule;
++.PP
++.B int des_key_sched(key,schedule)
++des_cblock *key;
++des_key_schedule schedule;
++.PP
++.B int des_ecb_encrypt(input,output,schedule,encrypt)
++des_cblock *input;
++des_cblock *output;
++des_key_schedule schedule;
++int encrypt;
++.PP
++.B int des_ecb3_encrypt(input,output,ks1,ks2,encrypt)
++des_cblock *input;
++des_cblock *output;
++des_key_schedule ks1,ks2;
++int encrypt;
++.PP
++.B int des_cbc_encrypt(input,output,length,schedule,ivec,encrypt)
++des_cblock *input;
++des_cblock *output;
++long length;
++des_key_schedule schedule;
++des_cblock *ivec;
++int encrypt;
++.PP
++.B int des_3cbc_encrypt(input,output,length,sk1,sk2,ivec1,ivec2,encrypt)
++des_cblock *input;
++des_cblock *output;
++long length;
++des_key_schedule sk1;
++des_key_schedule sk2;
++des_cblock *ivec1;
++des_cblock *ivec2;
++int encrypt;
++.PP
++.B int des_pcbc_encrypt(input,output,length,schedule,ivec,encrypt)
++des_cblock *input;
++des_cblock *output;
++long length;
++des_key_schedule schedule;
++des_cblock *ivec;
++int encrypt;
++.PP
++.B int des_cfb_encrypt(input,output,numbits,length,schedule,ivec,encrypt)
++unsigned char *input;
++unsigned char *output;
++int numbits;
++long length;
++des_key_schedule schedule;
++des_cblock *ivec;
++int encrypt;
++.PP
++.B int des_ofb_encrypt(input,output,numbits,length,schedule,ivec)
++unsigned char *input,*output;
++int numbits;
++long length;
++des_key_schedule schedule;
++des_cblock *ivec;
++.PP
++.B unsigned long des_cbc_cksum(input,output,length,schedule,ivec)
++des_cblock *input;
++des_cblock *output;
++long length;
++des_key_schedule schedule;
++des_cblock *ivec;
++.PP
++.B unsigned long des_quad_cksum(input,output,length,out_count,seed)
++des_cblock *input;
++des_cblock *output;
++long length;
++int out_count;
++des_cblock *seed;
++.PP
++.B int des_check_key;
++.PP
++.B int des_enc_read(fd,buf,len,sched,iv)
++int fd;
++char *buf;
++int len;
++des_key_schedule sched;
++des_cblock *iv;
++.PP
++.B int des_enc_write(fd,buf,len,sched,iv)
++int fd;
++char *buf;
++int len;
++des_key_schedule sched;
++des_cblock *iv;
++.PP
++.B extern int des_rw_mode;
++.PP
++.B void des_set_odd_parity(key)
++des_cblock *key;
++.PP
++.B int des_is_weak_key(key)
++des_cblock *key;
++.PP
++.B char *crypt(passwd,salt)
++char *passwd;
++char *salt;
++.PP
++.fi
++.SH DESCRIPTION
++This library contains a fast implementation of the DES encryption
++algorithm.
++.PP
++There are two phases to the use of DES encryption.
++The first is the generation of a
++.I des_key_schedule
++from a key,
++the second is the actual encryption.
++A des key is of type
++.I des_cblock.
++This type is made from 8 characters with odd parity.
++The least significant bit in the character is the parity bit.
++The key schedule is an expanded form of the key; it is used to speed the
++encryption process.
++.PP
++.I des_read_password
++writes the string specified by prompt to the standard output,
++turns off echo and reads an input string from standard input
++until terminated with a newline.
++If verify is non-zero, it prompts and reads the input again and verifies
++that both entered passwords are the same.
++The entered string is converted into a des key by using the
++.I des_string_to_key
++routine.
++The new key is placed in the
++.I des_cblock
++that was passed (by reference) to the routine.
++If there were no errors,
++.I des_read_password
++returns 0,
++-1 is returned if there was a terminal error and 1 is returned for
++any other error.
++.PP
++.I des_read_2password
++operates in the same way as
++.I des_read_password
++except that it generates 2 keys by using the
++.I des_string_to_2key
++function.
++.PP
++.I des_read_pw_string
++is called by
++.I des_read_password
++to read and verify a string from a terminal device.
++The string is returned in
++.I buf.
++The size of
++.I buf
++is passed to the routine via the
++.I length
++parameter.
++.PP
++.I des_string_to_key
++converts a string into a valid des key.
++.PP
++.I des_string_to_2key
++converts a string into 2 valid des keys.
++This routine is best suited for used to generate keys for use with
++.I des_ecb3_encrypt.
++.PP
++.I des_random_key
++returns a random key that is made of a combination of process id,
++time and an increasing counter.
++.PP
++Before a des key can be used it is converted into a
++.I des_key_schedule
++via the
++.I des_set_key
++routine.
++If the
++.I des_check_key
++flag is non-zero,
++.I des_set_key
++will check that the key passed is of odd parity and is not a week or
++semi-weak key.
++If the parity is wrong,
++then -1 is returned.
++If the key is a weak key,
++then -2 is returned.
++If an error is returned,
++the key schedule is not generated.
++.PP
++.I des_key_sched
++is another name for the
++.I des_set_key
++function.
++.PP
++The following routines mostly operate on an input and output stream of
++.I des_cblock's.
++.PP
++.I des_ecb_encrypt
++is the basic DES encryption routine that encrypts or decrypts a single 8-byte
++.I des_cblock
++in
++.I electronic code book
++mode.
++It always transforms the input data, pointed to by
++.I input,
++into the output data,
++pointed to by the
++.I output
++argument.
++If the
++.I encrypt
++argument is non-zero (DES_ENCRYPT),
++the
++.I input
++(cleartext) is encrypted in to the
++.I output
++(ciphertext) using the key_schedule specified by the
++.I schedule
++argument,
++previously set via
++.I des_set_key.
++If
++.I encrypt
++is zero (DES_DECRYPT),
++the
++.I input
++(now ciphertext)
++is decrypted into the
++.I output
++(now cleartext).
++Input and output may overlap.
++No meaningful value is returned.
++.PP
++.I des_ecb3_encrypt
++encrypts/decrypts the
++.I input
++block by using triple ecb DES encryption.
++This involves encrypting the input with
++.I ks1,
++decryption with the key schedule
++.I ks2,
++and then encryption with the first again.
++This routine greatly reduces the chances of brute force breaking of
++DES and has the advantage of if
++.I ks1
++and
++.I ks2
++are the same, it is equivalent to just encryption using ecb mode and
++.I ks1
++as the key.
++.PP
++.I des_cbc_encrypt
++encrypts/decrypts using the
++.I cipher-block-chaining
++mode of DES.
++If the
++.I encrypt
++argument is non-zero,
++the routine cipher-block-chain encrypts the cleartext data pointed to by the
++.I input
++argument into the ciphertext pointed to by the
++.I output
++argument,
++using the key schedule provided by the
++.I schedule
++argument,
++and initialisation vector provided by the
++.I ivec
++argument.
++If the
++.I length
++argument is not an integral multiple of eight bytes,
++the last block is copied to a temporary area and zero filled.
++The output is always
++an integral multiple of eight bytes.
++To make multiple cbc encrypt calls on a large amount of data appear to
++be one
++.I des_cbc_encrypt
++call, the
++.I ivec
++of subsequent calls should be the last 8 bytes of the output.
++.PP
++.I des_3cbc_encrypt
++encrypts/decrypts the
++.I input
++block by using triple cbc DES encryption.
++This involves encrypting the input with key schedule
++.I ks1,
++decryption with the key schedule
++.I ks2,
++and then encryption with the first again.
++2 initialisation vectors are required,
++.I ivec1
++and
++.I ivec2.
++Unlike
++.I des_cbc_encrypt,
++these initialisation vectors are modified by the subroutine.
++This routine greatly reduces the chances of brute force breaking of
++DES and has the advantage of if
++.I ks1
++and
++.I ks2
++are the same, it is equivalent to just encryption using cbc mode and
++.I ks1
++as the key.
++.PP
++.I des_pcbc_encrypt
++encrypt/decrypts using a modified block chaining mode.
++It provides better error propagation characteristics than cbc
++encryption.
++.PP
++.I des_cfb_encrypt
++encrypt/decrypts using cipher feedback mode. This method takes an
++array of characters as input and outputs and array of characters. It
++does not require any padding to 8 character groups. Note: the ivec
++variable is changed and the new changed value needs to be passed to
++the next call to this function. Since this function runs a complete
++DES ecb encryption per numbits, this function is only suggested for
++use when sending small numbers of characters.
++.PP
++.I des_ofb_encrypt
++encrypt using output feedback mode. This method takes an
++array of characters as input and outputs and array of characters. It
++does not require any padding to 8 character groups. Note: the ivec
++variable is changed and the new changed value needs to be passed to
++the next call to this function. Since this function runs a complete
++DES ecb encryption per numbits, this function is only suggested for
++use when sending small numbers of characters.
++.PP
++.I des_cbc_cksum
++produces an 8 byte checksum based on the input stream (via cbc encryption).
++The last 4 bytes of the checksum is returned and the complete 8 bytes is
++placed in
++.I output.
++.PP
++.I des_quad_cksum
++returns a 4 byte checksum from the input bytes.
++The algorithm can be iterated over the input,
++depending on
++.I out_count,
++1, 2, 3 or 4 times.
++If
++.I output
++is non-NULL,
++the 8 bytes generated by each pass are written into
++.I output.
++.PP
++.I des_enc_write
++is used to write
++.I len
++bytes
++to file descriptor
++.I fd
++from buffer
++.I buf.
++The data is encrypted via
++.I pcbc_encrypt
++(default) using
++.I sched
++for the key and
++.I iv
++as a starting vector.
++The actual data send down
++.I fd
++consists of 4 bytes (in network byte order) containing the length of the
++following encrypted data. The encrypted data then follows, padded with random
++data out to a multiple of 8 bytes.
++.PP
++.I des_enc_read
++is used to read
++.I len
++bytes
++from file descriptor
++.I fd
++into buffer
++.I buf.
++The data being read from
++.I fd
++is assumed to have come from
++.I des_enc_write
++and is decrypted using
++.I sched
++for the key schedule and
++.I iv
++for the initial vector.
++The
++.I des_enc_read/des_enc_write
++pair can be used to read/write to files, pipes and sockets.
++I have used them in implementing a version of rlogin in which all
++data is encrypted.
++.PP
++.I des_rw_mode
++is used to specify the encryption mode to use with
++.I des_enc_read
++and
++.I des_end_write.
++If set to
++.I DES_PCBC_MODE
++(the default), des_pcbc_encrypt is used.
++If set to
++.I DES_CBC_MODE
++des_cbc_encrypt is used.
++These two routines and the variable are not part of the normal MIT library.
++.PP
++.I des_set_odd_parity
++sets the parity of the passed
++.I key
++to odd. This routine is not part of the standard MIT library.
++.PP
++.I des_is_weak_key
++returns 1 is the passed key is a weak key (pick again :-),
++0 if it is ok.
++This routine is not part of the standard MIT library.
++.PP
++.I crypt
++is a replacement for the normal system crypt.
++It is much faster than the system crypt.
++.PP
++.SH FILES
++/usr/include/des.h
++.br
++/usr/lib/libdes.a
++.PP
++The encryption routines have been tested on 16bit, 32bit and 64bit
++machines of various endian and even works under VMS.
++.PP
++.SH BUGS
++.PP
++If you think this manual is sparse,
++read the des_crypt(3) manual from the MIT kerberos (or bones outside
++of the USA) distribution.
++.PP
++.I des_cfb_encrypt
++and
++.I des_ofb_encrypt
++operates on input of 8 bits. What this means is that if you set
++numbits to 12, and length to 2, the first 12 bits will come from the 1st
++input byte and the low half of the second input byte. The second 12
++bits will have the low 8 bits taken from the 3rd input byte and the
++top 4 bits taken from the 4th input byte. The same holds for output.
++This function has been implemented this way because most people will
++be using a multiple of 8 and because once you get into pulling bytes input
++bytes apart things get ugly!
++.PP
++.I des_read_pw_string
++is the most machine/OS dependent function and normally generates the
++most problems when porting this code.
++.PP
++.I des_string_to_key
++is probably different from the MIT version since there are lots
++of fun ways to implement one-way encryption of a text string.
++.PP
++The routines are optimised for 32 bit machines and so are not efficient
++on IBM PCs.
++.PP
++NOTE: extensive work has been done on this library since this document
++was origionally written. Please try to read des.doc from the libdes
++distribution since it is far more upto date and documents more of the
++functions. Libdes is now also being shipped as part of SSLeay, a
++general cryptographic library that amonst other things implements
++netscapes SSL protocoll. The most recent version can be found in
++SSLeay distributions.
++.SH AUTHOR
++Eric Young (eay@cryptsoft.com)
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/crypto/ciphers/des/des_enc.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,502 @@
++/* crypto/des/des_enc.c */
++/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com)
++ * All rights reserved.
++ *
++ * This package is an SSL implementation written
++ * by Eric Young (eay@cryptsoft.com).
++ * The implementation was written so as to conform with Netscapes SSL.
++ *
++ * This library is free for commercial and non-commercial use as long as
++ * the following conditions are aheared to. The following conditions
++ * apply to all code found in this distribution, be it the RC4, RSA,
++ * lhash, DES, etc., code; not just the SSL code. The SSL documentation
++ * included with this distribution is covered by the same copyright terms
++ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
++ *
++ * Copyright remains Eric Young's, and as such any Copyright notices in
++ * the code are not to be removed.
++ * If this package is used in a product, Eric Young should be given attribution
++ * as the author of the parts of the library used.
++ * This can be in the form of a textual message at program startup or
++ * in documentation (online or textual) provided with the package.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ * 1. Redistributions of source code must retain the copyright
++ * notice, this list of conditions and the following disclaimer.
++ * 2. Redistributions in binary form must reproduce the above copyright
++ * notice, this list of conditions and the following disclaimer in the
++ * documentation and/or other materials provided with the distribution.
++ * 3. All advertising materials mentioning features or use of this software
++ * must display the following acknowledgement:
++ * "This product includes cryptographic software written by
++ * Eric Young (eay@cryptsoft.com)"
++ * The word 'cryptographic' can be left out if the rouines from the library
++ * being used are not cryptographic related :-).
++ * 4. If you include any Windows specific code (or a derivative thereof) from
++ * the apps directory (application code) you must include an acknowledgement:
++ * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
++ *
++ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ *
++ * The licence and distribution terms for any publically available version or
++ * derivative of this code cannot be changed. i.e. this code cannot simply be
++ * copied and put under another distribution licence
++ * [including the GNU Public Licence.]
++ */
++
++#include "des_locl.h"
++
++void des_encrypt(data, ks, enc)
++DES_LONG *data;
++des_key_schedule ks;
++int enc;
++ {
++ register DES_LONG l,r,t,u;
++#ifdef DES_PTR
++ register unsigned char *des_SP=(unsigned char *)des_SPtrans;
++#endif
++#ifndef DES_UNROLL
++ register int i;
++#endif
++ register DES_LONG *s;
++
++ r=data[0];
++ l=data[1];
++
++ IP(r,l);
++ /* Things have been modified so that the initial rotate is
++ * done outside the loop. This required the
++ * des_SPtrans values in sp.h to be rotated 1 bit to the right.
++ * One perl script later and things have a 5% speed up on a sparc2.
++ * Thanks to Richard Outerbridge <71755.204@CompuServe.COM>
++ * for pointing this out. */
++ /* clear the top bits on machines with 8byte longs */
++ /* shift left by 2 */
++ r=ROTATE(r,29)&0xffffffffL;
++ l=ROTATE(l,29)&0xffffffffL;
++
++ s=(DES_LONG *)ks;
++ /* I don't know if it is worth the effort of loop unrolling the
++ * inner loop */
++ if (enc)
++ {
++#ifdef DES_UNROLL
++ D_ENCRYPT(l,r, 0); /* 1 */
++ D_ENCRYPT(r,l, 2); /* 2 */
++ D_ENCRYPT(l,r, 4); /* 3 */
++ D_ENCRYPT(r,l, 6); /* 4 */
++ D_ENCRYPT(l,r, 8); /* 5 */
++ D_ENCRYPT(r,l,10); /* 6 */
++ D_ENCRYPT(l,r,12); /* 7 */
++ D_ENCRYPT(r,l,14); /* 8 */
++ D_ENCRYPT(l,r,16); /* 9 */
++ D_ENCRYPT(r,l,18); /* 10 */
++ D_ENCRYPT(l,r,20); /* 11 */
++ D_ENCRYPT(r,l,22); /* 12 */
++ D_ENCRYPT(l,r,24); /* 13 */
++ D_ENCRYPT(r,l,26); /* 14 */
++ D_ENCRYPT(l,r,28); /* 15 */
++ D_ENCRYPT(r,l,30); /* 16 */
++#else
++ for (i=0; i<32; i+=8)
++ {
++ D_ENCRYPT(l,r,i+0); /* 1 */
++ D_ENCRYPT(r,l,i+2); /* 2 */
++ D_ENCRYPT(l,r,i+4); /* 3 */
++ D_ENCRYPT(r,l,i+6); /* 4 */
++ }
++#endif
++ }
++ else
++ {
++#ifdef DES_UNROLL
++ D_ENCRYPT(l,r,30); /* 16 */
++ D_ENCRYPT(r,l,28); /* 15 */
++ D_ENCRYPT(l,r,26); /* 14 */
++ D_ENCRYPT(r,l,24); /* 13 */
++ D_ENCRYPT(l,r,22); /* 12 */
++ D_ENCRYPT(r,l,20); /* 11 */
++ D_ENCRYPT(l,r,18); /* 10 */
++ D_ENCRYPT(r,l,16); /* 9 */
++ D_ENCRYPT(l,r,14); /* 8 */
++ D_ENCRYPT(r,l,12); /* 7 */
++ D_ENCRYPT(l,r,10); /* 6 */
++ D_ENCRYPT(r,l, 8); /* 5 */
++ D_ENCRYPT(l,r, 6); /* 4 */
++ D_ENCRYPT(r,l, 4); /* 3 */
++ D_ENCRYPT(l,r, 2); /* 2 */
++ D_ENCRYPT(r,l, 0); /* 1 */
++#else
++ for (i=30; i>0; i-=8)
++ {
++ D_ENCRYPT(l,r,i-0); /* 16 */
++ D_ENCRYPT(r,l,i-2); /* 15 */
++ D_ENCRYPT(l,r,i-4); /* 14 */
++ D_ENCRYPT(r,l,i-6); /* 13 */
++ }
++#endif
++ }
++
++ /* rotate and clear the top bits on machines with 8byte longs */
++ l=ROTATE(l,3)&0xffffffffL;
++ r=ROTATE(r,3)&0xffffffffL;
++
++ FP(r,l);
++ data[0]=l;
++ data[1]=r;
++ l=r=t=u=0;
++ }
++
++void des_encrypt2(data, ks, enc)
++DES_LONG *data;
++des_key_schedule ks;
++int enc;
++ {
++ register DES_LONG l,r,t,u;
++#ifdef DES_PTR
++ register unsigned char *des_SP=(unsigned char *)des_SPtrans;
++#endif
++#ifndef DES_UNROLL
++ register int i;
++#endif
++ register DES_LONG *s;
++
++ r=data[0];
++ l=data[1];
++
++ /* Things have been modified so that the initial rotate is
++ * done outside the loop. This required the
++ * des_SPtrans values in sp.h to be rotated 1 bit to the right.
++ * One perl script later and things have a 5% speed up on a sparc2.
++ * Thanks to Richard Outerbridge <71755.204@CompuServe.COM>
++ * for pointing this out. */
++ /* clear the top bits on machines with 8byte longs */
++ r=ROTATE(r,29)&0xffffffffL;
++ l=ROTATE(l,29)&0xffffffffL;
++
++ s=(DES_LONG *)ks;
++ /* I don't know if it is worth the effort of loop unrolling the
++ * inner loop */
++ if (enc)
++ {
++#ifdef DES_UNROLL
++ D_ENCRYPT(l,r, 0); /* 1 */
++ D_ENCRYPT(r,l, 2); /* 2 */
++ D_ENCRYPT(l,r, 4); /* 3 */
++ D_ENCRYPT(r,l, 6); /* 4 */
++ D_ENCRYPT(l,r, 8); /* 5 */
++ D_ENCRYPT(r,l,10); /* 6 */
++ D_ENCRYPT(l,r,12); /* 7 */
++ D_ENCRYPT(r,l,14); /* 8 */
++ D_ENCRYPT(l,r,16); /* 9 */
++ D_ENCRYPT(r,l,18); /* 10 */
++ D_ENCRYPT(l,r,20); /* 11 */
++ D_ENCRYPT(r,l,22); /* 12 */
++ D_ENCRYPT(l,r,24); /* 13 */
++ D_ENCRYPT(r,l,26); /* 14 */
++ D_ENCRYPT(l,r,28); /* 15 */
++ D_ENCRYPT(r,l,30); /* 16 */
++#else
++ for (i=0; i<32; i+=8)
++ {
++ D_ENCRYPT(l,r,i+0); /* 1 */
++ D_ENCRYPT(r,l,i+2); /* 2 */
++ D_ENCRYPT(l,r,i+4); /* 3 */
++ D_ENCRYPT(r,l,i+6); /* 4 */
++ }
++#endif
++ }
++ else
++ {
++#ifdef DES_UNROLL
++ D_ENCRYPT(l,r,30); /* 16 */
++ D_ENCRYPT(r,l,28); /* 15 */
++ D_ENCRYPT(l,r,26); /* 14 */
++ D_ENCRYPT(r,l,24); /* 13 */
++ D_ENCRYPT(l,r,22); /* 12 */
++ D_ENCRYPT(r,l,20); /* 11 */
++ D_ENCRYPT(l,r,18); /* 10 */
++ D_ENCRYPT(r,l,16); /* 9 */
++ D_ENCRYPT(l,r,14); /* 8 */
++ D_ENCRYPT(r,l,12); /* 7 */
++ D_ENCRYPT(l,r,10); /* 6 */
++ D_ENCRYPT(r,l, 8); /* 5 */
++ D_ENCRYPT(l,r, 6); /* 4 */
++ D_ENCRYPT(r,l, 4); /* 3 */
++ D_ENCRYPT(l,r, 2); /* 2 */
++ D_ENCRYPT(r,l, 0); /* 1 */
++#else
++ for (i=30; i>0; i-=8)
++ {
++ D_ENCRYPT(l,r,i-0); /* 16 */
++ D_ENCRYPT(r,l,i-2); /* 15 */
++ D_ENCRYPT(l,r,i-4); /* 14 */
++ D_ENCRYPT(r,l,i-6); /* 13 */
++ }
++#endif
++ }
++ /* rotate and clear the top bits on machines with 8byte longs */
++ data[0]=ROTATE(l,3)&0xffffffffL;
++ data[1]=ROTATE(r,3)&0xffffffffL;
++ l=r=t=u=0;
++ }
++
++void des_encrypt3(data,ks1,ks2,ks3)
++DES_LONG *data;
++des_key_schedule ks1;
++des_key_schedule ks2;
++des_key_schedule ks3;
++ {
++ register DES_LONG l,r;
++
++ l=data[0];
++ r=data[1];
++ IP(l,r);
++ data[0]=l;
++ data[1]=r;
++ des_encrypt2((DES_LONG *)data,ks1,DES_ENCRYPT);
++ des_encrypt2((DES_LONG *)data,ks2,DES_DECRYPT);
++ des_encrypt2((DES_LONG *)data,ks3,DES_ENCRYPT);
++ l=data[0];
++ r=data[1];
++ FP(r,l);
++ data[0]=l;
++ data[1]=r;
++ }
++
++void des_decrypt3(data,ks1,ks2,ks3)
++DES_LONG *data;
++des_key_schedule ks1;
++des_key_schedule ks2;
++des_key_schedule ks3;
++ {
++ register DES_LONG l,r;
++
++ l=data[0];
++ r=data[1];
++ IP(l,r);
++ data[0]=l;
++ data[1]=r;
++ des_encrypt2((DES_LONG *)data,ks3,DES_DECRYPT);
++ des_encrypt2((DES_LONG *)data,ks2,DES_ENCRYPT);
++ des_encrypt2((DES_LONG *)data,ks1,DES_DECRYPT);
++ l=data[0];
++ r=data[1];
++ FP(r,l);
++ data[0]=l;
++ data[1]=r;
++ }
++
++#ifndef DES_DEFAULT_OPTIONS
++
++void des_ncbc_encrypt(input, output, length, schedule, ivec, enc)
++des_cblock (*input);
++des_cblock (*output);
++long length;
++des_key_schedule schedule;
++des_cblock (*ivec);
++int enc;
++ {
++ register DES_LONG tin0,tin1;
++ register DES_LONG tout0,tout1,xor0,xor1;
++ register unsigned char *in,*out;
++ register long l=length;
++ DES_LONG tin[2];
++ unsigned char *iv;
++
++ in=(unsigned char *)input;
++ out=(unsigned char *)output;
++ iv=(unsigned char *)ivec;
++
++ if (enc)
++ {
++ c2l(iv,tout0);
++ c2l(iv,tout1);
++ for (l-=8; l>=0; l-=8)
++ {
++ c2l(in,tin0);
++ c2l(in,tin1);
++ tin0^=tout0; tin[0]=tin0;
++ tin1^=tout1; tin[1]=tin1;
++ des_encrypt((DES_LONG *)tin,schedule,DES_ENCRYPT);
++ tout0=tin[0]; l2c(tout0,out);
++ tout1=tin[1]; l2c(tout1,out);
++ }
++ if (l != -8)
++ {
++ c2ln(in,tin0,tin1,l+8);
++ tin0^=tout0; tin[0]=tin0;
++ tin1^=tout1; tin[1]=tin1;
++ des_encrypt((DES_LONG *)tin,schedule,DES_ENCRYPT);
++ tout0=tin[0]; l2c(tout0,out);
++ tout1=tin[1]; l2c(tout1,out);
++ }
++ iv=(unsigned char *)ivec;
++ l2c(tout0,iv);
++ l2c(tout1,iv);
++ }
++ else
++ {
++ c2l(iv,xor0);
++ c2l(iv,xor1);
++ for (l-=8; l>=0; l-=8)
++ {
++ c2l(in,tin0); tin[0]=tin0;
++ c2l(in,tin1); tin[1]=tin1;
++ des_encrypt((DES_LONG *)tin,schedule,DES_DECRYPT);
++ tout0=tin[0]^xor0;
++ tout1=tin[1]^xor1;
++ l2c(tout0,out);
++ l2c(tout1,out);
++ xor0=tin0;
++ xor1=tin1;
++ }
++ if (l != -8)
++ {
++ c2l(in,tin0); tin[0]=tin0;
++ c2l(in,tin1); tin[1]=tin1;
++ des_encrypt((DES_LONG *)tin,schedule,DES_DECRYPT);
++ tout0=tin[0]^xor0;
++ tout1=tin[1]^xor1;
++ l2cn(tout0,tout1,out,l+8);
++ xor0=tin0;
++ xor1=tin1;
++ }
++
++ iv=(unsigned char *)ivec;
++ l2c(xor0,iv);
++ l2c(xor1,iv);
++ }
++ tin0=tin1=tout0=tout1=xor0=xor1=0;
++ tin[0]=tin[1]=0;
++ }
++
++void des_ede3_cbc_encrypt(input, output, length, ks1, ks2, ks3, ivec, enc)
++des_cblock (*input);
++des_cblock (*output);
++long length;
++des_key_schedule ks1;
++des_key_schedule ks2;
++des_key_schedule ks3;
++des_cblock (*ivec);
++int enc;
++ {
++ register DES_LONG tin0,tin1;
++ register DES_LONG tout0,tout1,xor0,xor1;
++ register unsigned char *in,*out;
++ register long l=length;
++ DES_LONG tin[2];
++ unsigned char *iv;
++
++ in=(unsigned char *)input;
++ out=(unsigned char *)output;
++ iv=(unsigned char *)ivec;
++
++ if (enc)
++ {
++ c2l(iv,tout0);
++ c2l(iv,tout1);
++ for (l-=8; l>=0; l-=8)
++ {
++ c2l(in,tin0);
++ c2l(in,tin1);
++ tin0^=tout0;
++ tin1^=tout1;
++
++ tin[0]=tin0;
++ tin[1]=tin1;
++ des_encrypt3((DES_LONG *)tin,ks1,ks2,ks3);
++ tout0=tin[0];
++ tout1=tin[1];
++
++ l2c(tout0,out);
++ l2c(tout1,out);
++ }
++ if (l != -8)
++ {
++ c2ln(in,tin0,tin1,l+8);
++ tin0^=tout0;
++ tin1^=tout1;
++
++ tin[0]=tin0;
++ tin[1]=tin1;
++ des_encrypt3((DES_LONG *)tin,ks1,ks2,ks3);
++ tout0=tin[0];
++ tout1=tin[1];
++
++ l2c(tout0,out);
++ l2c(tout1,out);
++ }
++ iv=(unsigned char *)ivec;
++ l2c(tout0,iv);
++ l2c(tout1,iv);
++ }
++ else
++ {
++ register DES_LONG t0,t1;
++
++ c2l(iv,xor0);
++ c2l(iv,xor1);
++ for (l-=8; l>=0; l-=8)
++ {
++ c2l(in,tin0);
++ c2l(in,tin1);
++
++ t0=tin0;
++ t1=tin1;
++
++ tin[0]=tin0;
++ tin[1]=tin1;
++ des_decrypt3((DES_LONG *)tin,ks1,ks2,ks3);
++ tout0=tin[0];
++ tout1=tin[1];
++
++ tout0^=xor0;
++ tout1^=xor1;
++ l2c(tout0,out);
++ l2c(tout1,out);
++ xor0=t0;
++ xor1=t1;
++ }
++ if (l != -8)
++ {
++ c2l(in,tin0);
++ c2l(in,tin1);
++
++ t0=tin0;
++ t1=tin1;
++
++ tin[0]=tin0;
++ tin[1]=tin1;
++ des_decrypt3((DES_LONG *)tin,ks1,ks2,ks3);
++ tout0=tin[0];
++ tout1=tin[1];
++
++ tout0^=xor0;
++ tout1^=xor1;
++ l2cn(tout0,tout1,out,l+8);
++ xor0=t0;
++ xor1=t1;
++ }
++
++ iv=(unsigned char *)ivec;
++ l2c(xor0,iv);
++ l2c(xor1,iv);
++ }
++ tin0=tin1=tout0=tout1=xor0=xor1=0;
++ tin[0]=tin[1]=0;
++ }
++
++#endif /* DES_DEFAULT_OPTIONS */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/crypto/ciphers/des/des_locl.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,515 @@
++/* crypto/des/des_locl.org */
++/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com)
++ * All rights reserved.
++ *
++ * This package is an SSL implementation written
++ * by Eric Young (eay@cryptsoft.com).
++ * The implementation was written so as to conform with Netscapes SSL.
++ *
++ * This library is free for commercial and non-commercial use as long as
++ * the following conditions are aheared to. The following conditions
++ * apply to all code found in this distribution, be it the RC4, RSA,
++ * lhash, DES, etc., code; not just the SSL code. The SSL documentation
++ * included with this distribution is covered by the same copyright terms
++ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
++ *
++ * Copyright remains Eric Young's, and as such any Copyright notices in
++ * the code are not to be removed.
++ * If this package is used in a product, Eric Young should be given attribution
++ * as the author of the parts of the library used.
++ * This can be in the form of a textual message at program startup or
++ * in documentation (online or textual) provided with the package.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ * 1. Redistributions of source code must retain the copyright
++ * notice, this list of conditions and the following disclaimer.
++ * 2. Redistributions in binary form must reproduce the above copyright
++ * notice, this list of conditions and the following disclaimer in the
++ * documentation and/or other materials provided with the distribution.
++ * 3. All advertising materials mentioning features or use of this software
++ * must display the following acknowledgement:
++ * "This product includes cryptographic software written by
++ * Eric Young (eay@cryptsoft.com)"
++ * The word 'cryptographic' can be left out if the rouines from the library
++ * being used are not cryptographic related :-).
++ * 4. If you include any Windows specific code (or a derivative thereof) from
++ * the apps directory (application code) you must include an acknowledgement:
++ * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
++ *
++ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ *
++ * The licence and distribution terms for any publically available version or
++ * derivative of this code cannot be changed. i.e. this code cannot simply be
++ * copied and put under another distribution licence
++ * [including the GNU Public Licence.]
++ */
++
++/* WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
++ *
++ * Always modify des_locl.org since des_locl.h is automatically generated from
++ * it during SSLeay configuration.
++ *
++ * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
++ */
++
++#ifndef HEADER_DES_LOCL_H
++#define HEADER_DES_LOCL_H
++
++#if defined(WIN32) || defined(WIN16)
++#ifndef MSDOS
++#define MSDOS
++#endif
++#endif
++
++#include "crypto/des.h"
++
++#ifndef DES_DEFAULT_OPTIONS
++/* the following is tweaked from a config script, that is why it is a
++ * protected undef/define */
++#ifndef DES_PTR
++#define DES_PTR
++#endif
++
++/* This helps C compiler generate the correct code for multiple functional
++ * units. It reduces register dependancies at the expense of 2 more
++ * registers */
++#ifndef DES_RISC1
++#define DES_RISC1
++#endif
++
++#ifndef DES_RISC2
++#undef DES_RISC2
++#endif
++
++#if defined(DES_RISC1) && defined(DES_RISC2)
++YOU SHOULD NOT HAVE BOTH DES_RISC1 AND DES_RISC2 DEFINED!!!!!
++#endif
++
++/* Unroll the inner loop, this sometimes helps, sometimes hinders.
++ * Very mucy CPU dependant */
++#ifndef DES_UNROLL
++#define DES_UNROLL
++#endif
++
++/* These default values were supplied by
++ * Peter Gutman <pgut001@cs.auckland.ac.nz>
++ * They are only used if nothing else has been defined */
++#if !defined(DES_PTR) && !defined(DES_RISC1) && !defined(DES_RISC2) && !defined(DES_UNROLL)
++/* Special defines which change the way the code is built depending on the
++ CPU and OS. For SGI machines you can use _MIPS_SZLONG (32 or 64) to find
++ even newer MIPS CPU's, but at the moment one size fits all for
++ optimization options. Older Sparc's work better with only UNROLL, but
++ there's no way to tell at compile time what it is you're running on */
++
++#if defined( sun ) /* Newer Sparc's */
++ #define DES_PTR
++ #define DES_RISC1
++ #define DES_UNROLL
++#elif defined( __ultrix ) /* Older MIPS */
++ #define DES_PTR
++ #define DES_RISC2
++ #define DES_UNROLL
++#elif defined( __osf1__ ) /* Alpha */
++ #define DES_PTR
++ #define DES_RISC2
++#elif defined ( _AIX ) /* RS6000 */
++ /* Unknown */
++#elif defined( __hpux ) /* HP-PA */
++ /* Unknown */
++#elif defined( __aux ) /* 68K */
++ /* Unknown */
++#elif defined( __dgux ) /* 88K (but P6 in latest boxes) */
++ #define DES_UNROLL
++#elif defined( __sgi ) /* Newer MIPS */
++ #define DES_PTR
++ #define DES_RISC2
++ #define DES_UNROLL
++#elif defined( i386 ) /* x86 boxes, should be gcc */
++ #define DES_PTR
++ #define DES_RISC1
++ #define DES_UNROLL
++#endif /* Systems-specific speed defines */
++#endif
++
++#endif /* DES_DEFAULT_OPTIONS */
++
++#ifdef MSDOS /* Visual C++ 2.1 (Windows NT/95) */
++#include <stdlib.h>
++#include <errno.h>
++#include <time.h>
++#include <io.h>
++#ifndef RAND
++#define RAND
++#endif
++#undef NOPROTO
++#endif
++
++#if defined(__STDC__) || defined(VMS) || defined(M_XENIX) || defined(MSDOS)
++#ifndef __KERNEL__
++#include <string.h>
++#else
++#include <linux/string.h>
++#endif
++#endif
++
++#ifndef RAND
++#define RAND
++#endif
++
++#ifdef linux
++#undef RAND
++#endif
++
++#ifdef MSDOS
++#define getpid() 2
++#define RAND
++#undef NOPROTO
++#endif
++
++#if defined(NOCONST)
++#define const
++#endif
++
++#ifdef __STDC__
++#undef NOPROTO
++#endif
++
++#ifdef RAND
++#define srandom(s) srand(s)
++#define random rand
++#endif
++
++#define ITERATIONS 16
++#define HALF_ITERATIONS 8
++
++/* used in des_read and des_write */
++#define MAXWRITE (1024*16)
++#define BSIZE (MAXWRITE+4)
++
++#define c2l(c,l) (l =((DES_LONG)(*((c)++))) , \
++ l|=((DES_LONG)(*((c)++)))<< 8L, \
++ l|=((DES_LONG)(*((c)++)))<<16L, \
++ l|=((DES_LONG)(*((c)++)))<<24L)
++
++/* NOTE - c is not incremented as per c2l */
++#define c2ln(c,l1,l2,n) { \
++ c+=n; \
++ l1=l2=0; \
++ switch (n) { \
++ case 8: l2 =((DES_LONG)(*(--(c))))<<24L; \
++ case 7: l2|=((DES_LONG)(*(--(c))))<<16L; \
++ case 6: l2|=((DES_LONG)(*(--(c))))<< 8L; \
++ case 5: l2|=((DES_LONG)(*(--(c)))); \
++ case 4: l1 =((DES_LONG)(*(--(c))))<<24L; \
++ case 3: l1|=((DES_LONG)(*(--(c))))<<16L; \
++ case 2: l1|=((DES_LONG)(*(--(c))))<< 8L; \
++ case 1: l1|=((DES_LONG)(*(--(c)))); \
++ } \
++ }
++
++#define l2c(l,c) (*((c)++)=(unsigned char)(((l) )&0xff), \
++ *((c)++)=(unsigned char)(((l)>> 8L)&0xff), \
++ *((c)++)=(unsigned char)(((l)>>16L)&0xff), \
++ *((c)++)=(unsigned char)(((l)>>24L)&0xff))
++
++/* replacements for htonl and ntohl since I have no idea what to do
++ * when faced with machines with 8 byte longs. */
++#define HDRSIZE 4
++
++#define n2l(c,l) (l =((DES_LONG)(*((c)++)))<<24L, \
++ l|=((DES_LONG)(*((c)++)))<<16L, \
++ l|=((DES_LONG)(*((c)++)))<< 8L, \
++ l|=((DES_LONG)(*((c)++))))
++
++#define l2n(l,c) (*((c)++)=(unsigned char)(((l)>>24L)&0xff), \
++ *((c)++)=(unsigned char)(((l)>>16L)&0xff), \
++ *((c)++)=(unsigned char)(((l)>> 8L)&0xff), \
++ *((c)++)=(unsigned char)(((l) )&0xff))
++
++/* NOTE - c is not incremented as per l2c */
++#define l2cn(l1,l2,c,n) { \
++ c+=n; \
++ switch (n) { \
++ case 8: *(--(c))=(unsigned char)(((l2)>>24L)&0xff); \
++ case 7: *(--(c))=(unsigned char)(((l2)>>16L)&0xff); \
++ case 6: *(--(c))=(unsigned char)(((l2)>> 8L)&0xff); \
++ case 5: *(--(c))=(unsigned char)(((l2) )&0xff); \
++ case 4: *(--(c))=(unsigned char)(((l1)>>24L)&0xff); \
++ case 3: *(--(c))=(unsigned char)(((l1)>>16L)&0xff); \
++ case 2: *(--(c))=(unsigned char)(((l1)>> 8L)&0xff); \
++ case 1: *(--(c))=(unsigned char)(((l1) )&0xff); \
++ } \
++ }
++
++#if defined(WIN32)
++#define ROTATE(a,n) (_lrotr(a,n))
++#else
++#define ROTATE(a,n) (((a)>>(n))+((a)<<(32-(n))))
++#endif
++
++/* Don't worry about the LOAD_DATA() stuff, that is used by
++ * fcrypt() to add it's little bit to the front */
++
++#ifdef DES_FCRYPT
++
++#define LOAD_DATA_tmp(R,S,u,t,E0,E1) \
++ { DES_LONG tmp; LOAD_DATA(R,S,u,t,E0,E1,tmp); }
++
++#define LOAD_DATA(R,S,u,t,E0,E1,tmp) \
++ t=R^(R>>16L); \
++ u=t&E0; t&=E1; \
++ tmp=(u<<16); u^=R^s[S ]; u^=tmp; \
++ tmp=(t<<16); t^=R^s[S+1]; t^=tmp
++#else
++#define LOAD_DATA_tmp(a,b,c,d,e,f) LOAD_DATA(a,b,c,d,e,f,g)
++#define LOAD_DATA(R,S,u,t,E0,E1,tmp) \
++ u=R^s[S ]; \
++ t=R^s[S+1]
++#endif
++
++/* The changes to this macro may help or hinder, depending on the
++ * compiler and the achitecture. gcc2 always seems to do well :-).
++ * Inspired by Dana How <how@isl.stanford.edu>
++ * DO NOT use the alternative version on machines with 8 byte longs.
++ * It does not seem to work on the Alpha, even when DES_LONG is 4
++ * bytes, probably an issue of accessing non-word aligned objects :-( */
++#ifdef DES_PTR
++
++/* It recently occured to me that 0^0^0^0^0^0^0 == 0, so there
++ * is no reason to not xor all the sub items together. This potentially
++ * saves a register since things can be xored directly into L */
++
++#if defined(DES_RISC1) || defined(DES_RISC2)
++#ifdef DES_RISC1
++#define D_ENCRYPT(LL,R,S) { \
++ unsigned int u1,u2,u3; \
++ LOAD_DATA(R,S,u,t,E0,E1,u1); \
++ u2=(int)u>>8L; \
++ u1=(int)u&0xfc; \
++ u2&=0xfc; \
++ t=ROTATE(t,4); \
++ u>>=16L; \
++ LL^= *(DES_LONG *)((unsigned char *)des_SP +u1); \
++ LL^= *(DES_LONG *)((unsigned char *)des_SP+0x200+u2); \
++ u3=(int)(u>>8L); \
++ u1=(int)u&0xfc; \
++ u3&=0xfc; \
++ LL^= *(DES_LONG *)((unsigned char *)des_SP+0x400+u1); \
++ LL^= *(DES_LONG *)((unsigned char *)des_SP+0x600+u3); \
++ u2=(int)t>>8L; \
++ u1=(int)t&0xfc; \
++ u2&=0xfc; \
++ t>>=16L; \
++ LL^= *(DES_LONG *)((unsigned char *)des_SP+0x100+u1); \
++ LL^= *(DES_LONG *)((unsigned char *)des_SP+0x300+u2); \
++ u3=(int)t>>8L; \
++ u1=(int)t&0xfc; \
++ u3&=0xfc; \
++ LL^= *(DES_LONG *)((unsigned char *)des_SP+0x500+u1); \
++ LL^= *(DES_LONG *)((unsigned char *)des_SP+0x700+u3); }
++#endif
++#ifdef DES_RISC2
++#define D_ENCRYPT(LL,R,S) { \
++ unsigned int u1,u2,s1,s2; \
++ LOAD_DATA(R,S,u,t,E0,E1,u1); \
++ u2=(int)u>>8L; \
++ u1=(int)u&0xfc; \
++ u2&=0xfc; \
++ t=ROTATE(t,4); \
++ LL^= *(DES_LONG *)((unsigned char *)des_SP +u1); \
++ LL^= *(DES_LONG *)((unsigned char *)des_SP+0x200+u2); \
++ s1=(int)(u>>16L); \
++ s2=(int)(u>>24L); \
++ s1&=0xfc; \
++ s2&=0xfc; \
++ LL^= *(DES_LONG *)((unsigned char *)des_SP+0x400+s1); \
++ LL^= *(DES_LONG *)((unsigned char *)des_SP+0x600+s2); \
++ u2=(int)t>>8L; \
++ u1=(int)t&0xfc; \
++ u2&=0xfc; \
++ LL^= *(DES_LONG *)((unsigned char *)des_SP+0x100+u1); \
++ LL^= *(DES_LONG *)((unsigned char *)des_SP+0x300+u2); \
++ s1=(int)(t>>16L); \
++ s2=(int)(t>>24L); \
++ s1&=0xfc; \
++ s2&=0xfc; \
++ LL^= *(DES_LONG *)((unsigned char *)des_SP+0x500+s1); \
++ LL^= *(DES_LONG *)((unsigned char *)des_SP+0x700+s2); }
++#endif
++#else
++#define D_ENCRYPT(LL,R,S) { \
++ LOAD_DATA_tmp(R,S,u,t,E0,E1); \
++ t=ROTATE(t,4); \
++ LL^= \
++ *(DES_LONG *)((unsigned char *)des_SP +((u )&0xfc))^ \
++ *(DES_LONG *)((unsigned char *)des_SP+0x200+((u>> 8L)&0xfc))^ \
++ *(DES_LONG *)((unsigned char *)des_SP+0x400+((u>>16L)&0xfc))^ \
++ *(DES_LONG *)((unsigned char *)des_SP+0x600+((u>>24L)&0xfc))^ \
++ *(DES_LONG *)((unsigned char *)des_SP+0x100+((t )&0xfc))^ \
++ *(DES_LONG *)((unsigned char *)des_SP+0x300+((t>> 8L)&0xfc))^ \
++ *(DES_LONG *)((unsigned char *)des_SP+0x500+((t>>16L)&0xfc))^ \
++ *(DES_LONG *)((unsigned char *)des_SP+0x700+((t>>24L)&0xfc)); }
++#endif
++
++#else /* original version */
++
++#if defined(DES_RISC1) || defined(DES_RISC2)
++#ifdef DES_RISC1
++#define D_ENCRYPT(LL,R,S) {\
++ unsigned int u1,u2,u3; \
++ LOAD_DATA(R,S,u,t,E0,E1,u1); \
++ u>>=2L; \
++ t=ROTATE(t,6); \
++ u2=(int)u>>8L; \
++ u1=(int)u&0x3f; \
++ u2&=0x3f; \
++ u>>=16L; \
++ LL^=des_SPtrans[0][u1]; \
++ LL^=des_SPtrans[2][u2]; \
++ u3=(int)u>>8L; \
++ u1=(int)u&0x3f; \
++ u3&=0x3f; \
++ LL^=des_SPtrans[4][u1]; \
++ LL^=des_SPtrans[6][u3]; \
++ u2=(int)t>>8L; \
++ u1=(int)t&0x3f; \
++ u2&=0x3f; \
++ t>>=16L; \
++ LL^=des_SPtrans[1][u1]; \
++ LL^=des_SPtrans[3][u2]; \
++ u3=(int)t>>8L; \
++ u1=(int)t&0x3f; \
++ u3&=0x3f; \
++ LL^=des_SPtrans[5][u1]; \
++ LL^=des_SPtrans[7][u3]; }
++#endif
++#ifdef DES_RISC2
++#define D_ENCRYPT(LL,R,S) {\
++ unsigned int u1,u2,s1,s2; \
++ LOAD_DATA(R,S,u,t,E0,E1,u1); \
++ u>>=2L; \
++ t=ROTATE(t,6); \
++ u2=(int)u>>8L; \
++ u1=(int)u&0x3f; \
++ u2&=0x3f; \
++ LL^=des_SPtrans[0][u1]; \
++ LL^=des_SPtrans[2][u2]; \
++ s1=(int)u>>16L; \
++ s2=(int)u>>24L; \
++ s1&=0x3f; \
++ s2&=0x3f; \
++ LL^=des_SPtrans[4][s1]; \
++ LL^=des_SPtrans[6][s2]; \
++ u2=(int)t>>8L; \
++ u1=(int)t&0x3f; \
++ u2&=0x3f; \
++ LL^=des_SPtrans[1][u1]; \
++ LL^=des_SPtrans[3][u2]; \
++ s1=(int)t>>16; \
++ s2=(int)t>>24L; \
++ s1&=0x3f; \
++ s2&=0x3f; \
++ LL^=des_SPtrans[5][s1]; \
++ LL^=des_SPtrans[7][s2]; }
++#endif
++
++#else
++
++#define D_ENCRYPT(LL,R,S) {\
++ LOAD_DATA_tmp(R,S,u,t,E0,E1); \
++ t=ROTATE(t,4); \
++ LL^=\
++ des_SPtrans[0][(u>> 2L)&0x3f]^ \
++ des_SPtrans[2][(u>>10L)&0x3f]^ \
++ des_SPtrans[4][(u>>18L)&0x3f]^ \
++ des_SPtrans[6][(u>>26L)&0x3f]^ \
++ des_SPtrans[1][(t>> 2L)&0x3f]^ \
++ des_SPtrans[3][(t>>10L)&0x3f]^ \
++ des_SPtrans[5][(t>>18L)&0x3f]^ \
++ des_SPtrans[7][(t>>26L)&0x3f]; }
++#endif
++#endif
++
++ /* IP and FP
++ * The problem is more of a geometric problem that random bit fiddling.
++ 0 1 2 3 4 5 6 7 62 54 46 38 30 22 14 6
++ 8 9 10 11 12 13 14 15 60 52 44 36 28 20 12 4
++ 16 17 18 19 20 21 22 23 58 50 42 34 26 18 10 2
++ 24 25 26 27 28 29 30 31 to 56 48 40 32 24 16 8 0
++
++ 32 33 34 35 36 37 38 39 63 55 47 39 31 23 15 7
++ 40 41 42 43 44 45 46 47 61 53 45 37 29 21 13 5
++ 48 49 50 51 52 53 54 55 59 51 43 35 27 19 11 3
++ 56 57 58 59 60 61 62 63 57 49 41 33 25 17 9 1
++
++ The output has been subject to swaps of the form
++ 0 1 -> 3 1 but the odd and even bits have been put into
++ 2 3 2 0
++ different words. The main trick is to remember that
++ t=((l>>size)^r)&(mask);
++ r^=t;
++ l^=(t<<size);
++ can be used to swap and move bits between words.
++
++ So l = 0 1 2 3 r = 16 17 18 19
++ 4 5 6 7 20 21 22 23
++ 8 9 10 11 24 25 26 27
++ 12 13 14 15 28 29 30 31
++ becomes (for size == 2 and mask == 0x3333)
++ t = 2^16 3^17 -- -- l = 0 1 16 17 r = 2 3 18 19
++ 6^20 7^21 -- -- 4 5 20 21 6 7 22 23
++ 10^24 11^25 -- -- 8 9 24 25 10 11 24 25
++ 14^28 15^29 -- -- 12 13 28 29 14 15 28 29
++
++ Thanks for hints from Richard Outerbridge - he told me IP&FP
++ could be done in 15 xor, 10 shifts and 5 ands.
++ When I finally started to think of the problem in 2D
++ I first got ~42 operations without xors. When I remembered
++ how to use xors :-) I got it to its final state.
++ */
++#define PERM_OP(a,b,t,n,m) ((t)=((((a)>>(n))^(b))&(m)),\
++ (b)^=(t),\
++ (a)^=((t)<<(n)))
++
++#define IP(l,r) \
++ { \
++ register DES_LONG tt; \
++ PERM_OP(r,l,tt, 4,0x0f0f0f0fL); \
++ PERM_OP(l,r,tt,16,0x0000ffffL); \
++ PERM_OP(r,l,tt, 2,0x33333333L); \
++ PERM_OP(l,r,tt, 8,0x00ff00ffL); \
++ PERM_OP(r,l,tt, 1,0x55555555L); \
++ }
++
++#define FP(l,r) \
++ { \
++ register DES_LONG tt; \
++ PERM_OP(l,r,tt, 1,0x55555555L); \
++ PERM_OP(r,l,tt, 8,0x00ff00ffL); \
++ PERM_OP(l,r,tt, 2,0x33333333L); \
++ PERM_OP(r,l,tt,16,0x0000ffffL); \
++ PERM_OP(l,r,tt, 4,0x0f0f0f0fL); \
++ }
++
++extern const DES_LONG des_SPtrans[8][64];
++
++#ifndef NOPROTO
++void fcrypt_body(DES_LONG *out,des_key_schedule ks,
++ DES_LONG Eswap0, DES_LONG Eswap1);
++#else
++void fcrypt_body();
++#endif
++
++#endif
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/crypto/ciphers/des/des_opts.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,620 @@
++/* crypto/des/des_opts.c */
++/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com)
++ * All rights reserved.
++ *
++ * This package is an SSL implementation written
++ * by Eric Young (eay@cryptsoft.com).
++ * The implementation was written so as to conform with Netscapes SSL.
++ *
++ * This library is free for commercial and non-commercial use as long as
++ * the following conditions are aheared to. The following conditions
++ * apply to all code found in this distribution, be it the RC4, RSA,
++ * lhash, DES, etc., code; not just the SSL code. The SSL documentation
++ * included with this distribution is covered by the same copyright terms
++ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
++ *
++ * Copyright remains Eric Young's, and as such any Copyright notices in
++ * the code are not to be removed.
++ * If this package is used in a product, Eric Young should be given attribution
++ * as the author of the parts of the library used.
++ * This can be in the form of a textual message at program startup or
++ * in documentation (online or textual) provided with the package.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ * 1. Redistributions of source code must retain the copyright
++ * notice, this list of conditions and the following disclaimer.
++ * 2. Redistributions in binary form must reproduce the above copyright
++ * notice, this list of conditions and the following disclaimer in the
++ * documentation and/or other materials provided with the distribution.
++ * 3. All advertising materials mentioning features or use of this software
++ * must display the following acknowledgement:
++ * "This product includes cryptographic software written by
++ * Eric Young (eay@cryptsoft.com)"
++ * The word 'cryptographic' can be left out if the rouines from the library
++ * being used are not cryptographic related :-).
++ * 4. If you include any Windows specific code (or a derivative thereof) from
++ * the apps directory (application code) you must include an acknowledgement:
++ * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
++ *
++ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ *
++ * The licence and distribution terms for any publically available version or
++ * derivative of this code cannot be changed. i.e. this code cannot simply be
++ * copied and put under another distribution licence
++ * [including the GNU Public Licence.]
++ */
++
++/* define PART1, PART2, PART3 or PART4 to build only with a few of the options.
++ * This is for machines with 64k code segment size restrictions. */
++
++#ifndef MSDOS
++#define TIMES
++#endif
++
++#include <stdio.h>
++#ifndef MSDOS
++#include <unistd.h>
++#else
++#include <io.h>
++extern void exit();
++#endif
++#include <signal.h>
++#ifndef VMS
++#ifndef _IRIX
++#include <time.h>
++#endif
++#ifdef TIMES
++#include <sys/types.h>
++#include <sys/times.h>
++#endif
++#else /* VMS */
++#include <types.h>
++struct tms {
++ time_t tms_utime;
++ time_t tms_stime;
++ time_t tms_uchild; /* I dunno... */
++ time_t tms_uchildsys; /* so these names are a guess :-) */
++ }
++#endif
++#ifndef TIMES
++#include <sys/timeb.h>
++#endif
++
++#ifdef sun
++#include <limits.h>
++#include <sys/param.h>
++#endif
++
++#include "des_locl.h"
++#include "spr.h"
++
++#define DES_DEFAULT_OPTIONS
++
++#if !defined(PART1) && !defined(PART2) && !defined(PART3) && !defined(PART4)
++#define PART1
++#define PART2
++#define PART3
++#define PART4
++#endif
++
++#ifdef PART1
++
++#undef DES_UNROLL
++#undef DES_RISC1
++#undef DES_RISC2
++#undef DES_PTR
++#undef D_ENCRYPT
++#define des_encrypt des_encrypt_u4_cisc_idx
++#define des_encrypt2 des_encrypt2_u4_cisc_idx
++#define des_encrypt3 des_encrypt3_u4_cisc_idx
++#define des_decrypt3 des_decrypt3_u4_cisc_idx
++#undef HEADER_DES_LOCL_H
++#include "des_enc.c"
++
++#define DES_UNROLL
++#undef DES_RISC1
++#undef DES_RISC2
++#undef DES_PTR
++#undef D_ENCRYPT
++#undef des_encrypt
++#undef des_encrypt2
++#undef des_encrypt3
++#undef des_decrypt3
++#define des_encrypt des_encrypt_u16_cisc_idx
++#define des_encrypt2 des_encrypt2_u16_cisc_idx
++#define des_encrypt3 des_encrypt3_u16_cisc_idx
++#define des_decrypt3 des_decrypt3_u16_cisc_idx
++#undef HEADER_DES_LOCL_H
++#include "des_enc.c"
++
++#undef DES_UNROLL
++#define DES_RISC1
++#undef DES_RISC2
++#undef DES_PTR
++#undef D_ENCRYPT
++#undef des_encrypt
++#undef des_encrypt2
++#undef des_encrypt3
++#undef des_decrypt3
++#define des_encrypt des_encrypt_u4_risc1_idx
++#define des_encrypt2 des_encrypt2_u4_risc1_idx
++#define des_encrypt3 des_encrypt3_u4_risc1_idx
++#define des_decrypt3 des_decrypt3_u4_risc1_idx
++#undef HEADER_DES_LOCL_H
++#include "des_enc.c"
++
++#endif
++
++#ifdef PART2
++
++#undef DES_UNROLL
++#undef DES_RISC1
++#define DES_RISC2
++#undef DES_PTR
++#undef D_ENCRYPT
++#undef des_encrypt
++#undef des_encrypt2
++#undef des_encrypt3
++#undef des_decrypt3
++#define des_encrypt des_encrypt_u4_risc2_idx
++#define des_encrypt2 des_encrypt2_u4_risc2_idx
++#define des_encrypt3 des_encrypt3_u4_risc2_idx
++#define des_decrypt3 des_decrypt3_u4_risc2_idx
++#undef HEADER_DES_LOCL_H
++#include "des_enc.c"
++
++#define DES_UNROLL
++#define DES_RISC1
++#undef DES_RISC2
++#undef DES_PTR
++#undef D_ENCRYPT
++#undef des_encrypt
++#undef des_encrypt2
++#undef des_encrypt3
++#undef des_decrypt3
++#define des_encrypt des_encrypt_u16_risc1_idx
++#define des_encrypt2 des_encrypt2_u16_risc1_idx
++#define des_encrypt3 des_encrypt3_u16_risc1_idx
++#define des_decrypt3 des_decrypt3_u16_risc1_idx
++#undef HEADER_DES_LOCL_H
++#include "des_enc.c"
++
++#define DES_UNROLL
++#undef DES_RISC1
++#define DES_RISC2
++#undef DES_PTR
++#undef D_ENCRYPT
++#undef des_encrypt
++#undef des_encrypt2
++#undef des_encrypt3
++#undef des_decrypt3
++#define des_encrypt des_encrypt_u16_risc2_idx
++#define des_encrypt2 des_encrypt2_u16_risc2_idx
++#define des_encrypt3 des_encrypt3_u16_risc2_idx
++#define des_decrypt3 des_decrypt3_u16_risc2_idx
++#undef HEADER_DES_LOCL_H
++#include "des_enc.c"
++
++#endif
++
++#ifdef PART3
++
++#undef DES_UNROLL
++#undef DES_RISC1
++#undef DES_RISC2
++#define DES_PTR
++#undef D_ENCRYPT
++#undef des_encrypt
++#undef des_encrypt2
++#undef des_encrypt3
++#undef des_decrypt3
++#define des_encrypt des_encrypt_u4_cisc_ptr
++#define des_encrypt2 des_encrypt2_u4_cisc_ptr
++#define des_encrypt3 des_encrypt3_u4_cisc_ptr
++#define des_decrypt3 des_decrypt3_u4_cisc_ptr
++#undef HEADER_DES_LOCL_H
++#include "des_enc.c"
++
++#define DES_UNROLL
++#undef DES_RISC1
++#undef DES_RISC2
++#define DES_PTR
++#undef D_ENCRYPT
++#undef des_encrypt
++#undef des_encrypt2
++#undef des_encrypt3
++#undef des_decrypt3
++#define des_encrypt des_encrypt_u16_cisc_ptr
++#define des_encrypt2 des_encrypt2_u16_cisc_ptr
++#define des_encrypt3 des_encrypt3_u16_cisc_ptr
++#define des_decrypt3 des_decrypt3_u16_cisc_ptr
++#undef HEADER_DES_LOCL_H
++#include "des_enc.c"
++
++#undef DES_UNROLL
++#define DES_RISC1
++#undef DES_RISC2
++#define DES_PTR
++#undef D_ENCRYPT
++#undef des_encrypt
++#undef des_encrypt2
++#undef des_encrypt3
++#undef des_decrypt3
++#define des_encrypt des_encrypt_u4_risc1_ptr
++#define des_encrypt2 des_encrypt2_u4_risc1_ptr
++#define des_encrypt3 des_encrypt3_u4_risc1_ptr
++#define des_decrypt3 des_decrypt3_u4_risc1_ptr
++#undef HEADER_DES_LOCL_H
++#include "des_enc.c"
++
++#endif
++
++#ifdef PART4
++
++#undef DES_UNROLL
++#undef DES_RISC1
++#define DES_RISC2
++#define DES_PTR
++#undef D_ENCRYPT
++#undef des_encrypt
++#undef des_encrypt2
++#undef des_encrypt3
++#undef des_decrypt3
++#define des_encrypt des_encrypt_u4_risc2_ptr
++#define des_encrypt2 des_encrypt2_u4_risc2_ptr
++#define des_encrypt3 des_encrypt3_u4_risc2_ptr
++#define des_decrypt3 des_decrypt3_u4_risc2_ptr
++#undef HEADER_DES_LOCL_H
++#include "des_enc.c"
++
++#define DES_UNROLL
++#define DES_RISC1
++#undef DES_RISC2
++#define DES_PTR
++#undef D_ENCRYPT
++#undef des_encrypt
++#undef des_encrypt2
++#undef des_encrypt3
++#undef des_decrypt3
++#define des_encrypt des_encrypt_u16_risc1_ptr
++#define des_encrypt2 des_encrypt2_u16_risc1_ptr
++#define des_encrypt3 des_encrypt3_u16_risc1_ptr
++#define des_decrypt3 des_decrypt3_u16_risc1_ptr
++#undef HEADER_DES_LOCL_H
++#include "des_enc.c"
++
++#define DES_UNROLL
++#undef DES_RISC1
++#define DES_RISC2
++#define DES_PTR
++#undef D_ENCRYPT
++#undef des_encrypt
++#undef des_encrypt2
++#undef des_encrypt3
++#undef des_decrypt3
++#define des_encrypt des_encrypt_u16_risc2_ptr
++#define des_encrypt2 des_encrypt2_u16_risc2_ptr
++#define des_encrypt3 des_encrypt3_u16_risc2_ptr
++#define des_decrypt3 des_decrypt3_u16_risc2_ptr
++#undef HEADER_DES_LOCL_H
++#include "des_enc.c"
++
++#endif
++
++/* The following if from times(3) man page. It may need to be changed */
++#ifndef HZ
++# ifndef CLK_TCK
++# ifndef _BSD_CLK_TCK_ /* FreeBSD fix */
++# ifndef VMS
++# define HZ 100.0
++# else /* VMS */
++# define HZ 100.0
++# endif
++# else /* _BSD_CLK_TCK_ */
++# define HZ ((double)_BSD_CLK_TCK_)
++# endif
++# else /* CLK_TCK */
++# define HZ ((double)CLK_TCK)
++# endif
++#endif
++
++#define BUFSIZE ((long)1024)
++long run=0;
++
++#ifndef NOPROTO
++double Time_F(int s);
++#else
++double Time_F();
++#endif
++
++#ifdef SIGALRM
++#if defined(__STDC__) || defined(sgi)
++#define SIGRETTYPE void
++#else
++#define SIGRETTYPE int
++#endif
++
++#ifndef NOPROTO
++SIGRETTYPE sig_done(int sig);
++#else
++SIGRETTYPE sig_done();
++#endif
++
++SIGRETTYPE sig_done(sig)
++int sig;
++ {
++ signal(SIGALRM,sig_done);
++ run=0;
++#ifdef LINT
++ sig=sig;
++#endif
++ }
++#endif
++
++#define START 0
++#define STOP 1
++
++double Time_F(s)
++int s;
++ {
++ double ret;
++#ifdef TIMES
++ static struct tms tstart,tend;
++
++ if (s == START)
++ {
++ times(&tstart);
++ return(0);
++ }
++ else
++ {
++ times(&tend);
++ ret=((double)(tend.tms_utime-tstart.tms_utime))/HZ;
++ return((ret == 0.0)?1e-6:ret);
++ }
++#else /* !times() */
++ static struct timeb tstart,tend;
++ long i;
++
++ if (s == START)
++ {
++ ftime(&tstart);
++ return(0);
++ }
++ else
++ {
++ ftime(&tend);
++ i=(long)tend.millitm-(long)tstart.millitm;
++ ret=((double)(tend.time-tstart.time))+((double)i)/1000.0;
++ return((ret == 0.0)?1e-6:ret);
++ }
++#endif
++ }
++
++#ifdef SIGALRM
++#define print_name(name) fprintf(stderr,"Doing %s's for 10 seconds\n",name); alarm(10);
++#else
++#define print_name(name) fprintf(stderr,"Doing %s %ld times\n",name,cb);
++#endif
++
++#define time_it(func,name,index) \
++ print_name(name); \
++ Time_F(START); \
++ for (count=0,run=1; COND(cb); count++) \
++ { \
++ unsigned long d[2]; \
++ func(d,&(sch[0]),DES_ENCRYPT); \
++ } \
++ tm[index]=Time_F(STOP); \
++ fprintf(stderr,"%ld %s's in %.2f second\n",count,name,tm[index]); \
++ tm[index]=((double)COUNT(cb))/tm[index];
++
++#define print_it(name,index) \
++ fprintf(stderr,"%s bytes per sec = %12.2f (%5.1fuS)\n",name, \
++ tm[index]*8,1.0e6/tm[index]);
++
++int main(argc,argv)
++int argc;
++char **argv;
++ {
++ long count;
++ static unsigned char buf[BUFSIZE];
++ static des_cblock key ={0x12,0x34,0x56,0x78,0x9a,0xbc,0xde,0xf0};
++ static des_cblock key2={0x34,0x56,0x78,0x9a,0xbc,0xde,0xf0,0x12};
++ static des_cblock key3={0x56,0x78,0x9a,0xbc,0xde,0xf0,0x12,0x34};
++ des_key_schedule sch,sch2,sch3;
++ double d,tm[16],max=0;
++ int rank[16];
++ char *str[16];
++ int max_idx=0,i,num=0,j;
++#ifndef SIGALARM
++ long ca,cb,cc,cd,ce;
++#endif
++
++ for (i=0; i<12; i++)
++ {
++ tm[i]=0.0;
++ rank[i]=0;
++ }
++
++#ifndef TIMES
++ fprintf(stderr,"To get the most acurate results, try to run this\n");
++ fprintf(stderr,"program when this computer is idle.\n");
++#endif
++
++ des_set_key((C_Block *)key,sch);
++ des_set_key((C_Block *)key2,sch2);
++ des_set_key((C_Block *)key3,sch3);
++
++#ifndef SIGALRM
++ fprintf(stderr,"First we calculate the approximate speed ...\n");
++ des_set_key((C_Block *)key,sch);
++ count=10;
++ do {
++ long i;
++ unsigned long data[2];
++
++ count*=2;
++ Time_F(START);
++ for (i=count; i; i--)
++ des_encrypt(data,&(sch[0]),DES_ENCRYPT);
++ d=Time_F(STOP);
++ } while (d < 3.0);
++ ca=count;
++ cb=count*3;
++ cc=count*3*8/BUFSIZE+1;
++ cd=count*8/BUFSIZE+1;
++
++ ce=count/20+1;
++#define COND(d) (count != (d))
++#define COUNT(d) (d)
++#else
++#define COND(c) (run)
++#define COUNT(d) (count)
++ signal(SIGALRM,sig_done);
++ alarm(10);
++#endif
++
++#ifdef PART1
++ time_it(des_encrypt_u4_cisc_idx, "des_encrypt_u4_cisc_idx ", 0);
++ time_it(des_encrypt_u16_cisc_idx, "des_encrypt_u16_cisc_idx ", 1);
++ time_it(des_encrypt_u4_risc1_idx, "des_encrypt_u4_risc1_idx ", 2);
++ num+=3;
++#endif
++#ifdef PART2
++ time_it(des_encrypt_u16_risc1_idx,"des_encrypt_u16_risc1_idx", 3);
++ time_it(des_encrypt_u4_risc2_idx, "des_encrypt_u4_risc2_idx ", 4);
++ time_it(des_encrypt_u16_risc2_idx,"des_encrypt_u16_risc2_idx", 5);
++ num+=3;
++#endif
++#ifdef PART3
++ time_it(des_encrypt_u4_cisc_ptr, "des_encrypt_u4_cisc_ptr ", 6);
++ time_it(des_encrypt_u16_cisc_ptr, "des_encrypt_u16_cisc_ptr ", 7);
++ time_it(des_encrypt_u4_risc1_ptr, "des_encrypt_u4_risc1_ptr ", 8);
++ num+=3;
++#endif
++#ifdef PART4
++ time_it(des_encrypt_u16_risc1_ptr,"des_encrypt_u16_risc1_ptr", 9);
++ time_it(des_encrypt_u4_risc2_ptr, "des_encrypt_u4_risc2_ptr ",10);
++ time_it(des_encrypt_u16_risc2_ptr,"des_encrypt_u16_risc2_ptr",11);
++ num+=3;
++#endif
++
++#ifdef PART1
++ str[0]=" 4 c i";
++ print_it("des_encrypt_u4_cisc_idx ",0);
++ max=tm[0];
++ max_idx=0;
++ str[1]="16 c i";
++ print_it("des_encrypt_u16_cisc_idx ",1);
++ if (max < tm[1]) { max=tm[1]; max_idx=1; }
++ str[2]=" 4 r1 i";
++ print_it("des_encrypt_u4_risc1_idx ",2);
++ if (max < tm[2]) { max=tm[2]; max_idx=2; }
++#endif
++#ifdef PART2
++ str[3]="16 r1 i";
++ print_it("des_encrypt_u16_risc1_idx",3);
++ if (max < tm[3]) { max=tm[3]; max_idx=3; }
++ str[4]=" 4 r2 i";
++ print_it("des_encrypt_u4_risc2_idx ",4);
++ if (max < tm[4]) { max=tm[4]; max_idx=4; }
++ str[5]="16 r2 i";
++ print_it("des_encrypt_u16_risc2_idx",5);
++ if (max < tm[5]) { max=tm[5]; max_idx=5; }
++#endif
++#ifdef PART3
++ str[6]=" 4 c p";
++ print_it("des_encrypt_u4_cisc_ptr ",6);
++ if (max < tm[6]) { max=tm[6]; max_idx=6; }
++ str[7]="16 c p";
++ print_it("des_encrypt_u16_cisc_ptr ",7);
++ if (max < tm[7]) { max=tm[7]; max_idx=7; }
++ str[8]=" 4 r1 p";
++ print_it("des_encrypt_u4_risc1_ptr ",8);
++ if (max < tm[8]) { max=tm[8]; max_idx=8; }
++#endif
++#ifdef PART4
++ str[9]="16 r1 p";
++ print_it("des_encrypt_u16_risc1_ptr",9);
++ if (max < tm[9]) { max=tm[9]; max_idx=9; }
++ str[10]=" 4 r2 p";
++ print_it("des_encrypt_u4_risc2_ptr ",10);
++ if (max < tm[10]) { max=tm[10]; max_idx=10; }
++ str[11]="16 r2 p";
++ print_it("des_encrypt_u16_risc2_ptr",11);
++ if (max < tm[11]) { max=tm[11]; max_idx=11; }
++#endif
++ printf("options des ecb/s\n");
++ printf("%s %12.2f 100.0%%\n",str[max_idx],tm[max_idx]);
++ d=tm[max_idx];
++ tm[max_idx]= -2.0;
++ max= -1.0;
++ for (;;)
++ {
++ for (i=0; i<12; i++)
++ {
++ if (max < tm[i]) { max=tm[i]; j=i; }
++ }
++ if (max < 0.0) break;
++ printf("%s %12.2f %4.1f%%\n",str[j],tm[j],tm[j]/d*100.0);
++ tm[j]= -2.0;
++ max= -1.0;
++ }
++
++ switch (max_idx)
++ {
++ case 0:
++ printf("-DDES_DEFAULT_OPTIONS\n");
++ break;
++ case 1:
++ printf("-DDES_UNROLL\n");
++ break;
++ case 2:
++ printf("-DDES_RISC1\n");
++ break;
++ case 3:
++ printf("-DDES_UNROLL -DDES_RISC1\n");
++ break;
++ case 4:
++ printf("-DDES_RISC2\n");
++ break;
++ case 5:
++ printf("-DDES_UNROLL -DDES_RISC2\n");
++ break;
++ case 6:
++ printf("-DDES_PTR\n");
++ break;
++ case 7:
++ printf("-DDES_UNROLL -DDES_PTR\n");
++ break;
++ case 8:
++ printf("-DDES_RISC1 -DDES_PTR\n");
++ break;
++ case 9:
++ printf("-DDES_UNROLL -DDES_RISC1 -DDES_PTR\n");
++ break;
++ case 10:
++ printf("-DDES_RISC2 -DDES_PTR\n");
++ break;
++ case 11:
++ printf("-DDES_UNROLL -DDES_RISC2 -DDES_PTR\n");
++ break;
++ }
++ exit(0);
++#if defined(LINT) || defined(MSDOS)
++ return(0);
++#endif
++ }
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/crypto/ciphers/des/des_ver.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,60 @@
++/* crypto/des/des_ver.h */
++/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com)
++ * All rights reserved.
++ *
++ * This package is an SSL implementation written
++ * by Eric Young (eay@cryptsoft.com).
++ * The implementation was written so as to conform with Netscapes SSL.
++ *
++ * This library is free for commercial and non-commercial use as long as
++ * the following conditions are aheared to. The following conditions
++ * apply to all code found in this distribution, be it the RC4, RSA,
++ * lhash, DES, etc., code; not just the SSL code. The SSL documentation
++ * included with this distribution is covered by the same copyright terms
++ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
++ *
++ * Copyright remains Eric Young's, and as such any Copyright notices in
++ * the code are not to be removed.
++ * If this package is used in a product, Eric Young should be given attribution
++ * as the author of the parts of the library used.
++ * This can be in the form of a textual message at program startup or
++ * in documentation (online or textual) provided with the package.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ * 1. Redistributions of source code must retain the copyright
++ * notice, this list of conditions and the following disclaimer.
++ * 2. Redistributions in binary form must reproduce the above copyright
++ * notice, this list of conditions and the following disclaimer in the
++ * documentation and/or other materials provided with the distribution.
++ * 3. All advertising materials mentioning features or use of this software
++ * must display the following acknowledgement:
++ * "This product includes cryptographic software written by
++ * Eric Young (eay@cryptsoft.com)"
++ * The word 'cryptographic' can be left out if the rouines from the library
++ * being used are not cryptographic related :-).
++ * 4. If you include any Windows specific code (or a derivative thereof) from
++ * the apps directory (application code) you must include an acknowledgement:
++ * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
++ *
++ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ *
++ * The licence and distribution terms for any publically available version or
++ * derivative of this code cannot be changed. i.e. this code cannot simply be
++ * copied and put under another distribution licence
++ * [including the GNU Public Licence.]
++ */
++
++extern char *DES_version; /* SSLeay version string */
++extern char *libdes_version; /* old libdes version string */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/crypto/ciphers/des/destest.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,871 @@
++/* crypto/des/destest.c */
++/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com)
++ * All rights reserved.
++ *
++ * This package is an SSL implementation written
++ * by Eric Young (eay@cryptsoft.com).
++ * The implementation was written so as to conform with Netscapes SSL.
++ *
++ * This library is free for commercial and non-commercial use as long as
++ * the following conditions are aheared to. The following conditions
++ * apply to all code found in this distribution, be it the RC4, RSA,
++ * lhash, DES, etc., code; not just the SSL code. The SSL documentation
++ * included with this distribution is covered by the same copyright terms
++ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
++ *
++ * Copyright remains Eric Young's, and as such any Copyright notices in
++ * the code are not to be removed.
++ * If this package is used in a product, Eric Young should be given attribution
++ * as the author of the parts of the library used.
++ * This can be in the form of a textual message at program startup or
++ * in documentation (online or textual) provided with the package.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ * 1. Redistributions of source code must retain the copyright
++ * notice, this list of conditions and the following disclaimer.
++ * 2. Redistributions in binary form must reproduce the above copyright
++ * notice, this list of conditions and the following disclaimer in the
++ * documentation and/or other materials provided with the distribution.
++ * 3. All advertising materials mentioning features or use of this software
++ * must display the following acknowledgement:
++ * "This product includes cryptographic software written by
++ * Eric Young (eay@cryptsoft.com)"
++ * The word 'cryptographic' can be left out if the rouines from the library
++ * being used are not cryptographic related :-).
++ * 4. If you include any Windows specific code (or a derivative thereof) from
++ * the apps directory (application code) you must include an acknowledgement:
++ * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
++ *
++ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ *
++ * The licence and distribution terms for any publically available version or
++ * derivative of this code cannot be changed. i.e. this code cannot simply be
++ * copied and put under another distribution licence
++ * [including the GNU Public Licence.]
++ */
++
++#if defined(WIN32) || defined(WIN16) || defined(WINDOWS)
++#ifndef MSDOS
++#define MSDOS
++#endif
++#endif
++
++#include <stdio.h>
++#include <stdlib.h>
++#ifndef MSDOS
++#include <unistd.h>
++#else
++#include <io.h>
++#endif
++#include <string.h>
++#include "des_locl.h"
++
++/* tisk tisk - the test keys don't all have odd parity :-( */
++/* test data */
++#define NUM_TESTS 34
++static unsigned char key_data[NUM_TESTS][8]={
++ {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
++ {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF},
++ {0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
++ {0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11},
++ {0x01,0x23,0x45,0x67,0x89,0xAB,0xCD,0xEF},
++ {0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11},
++ {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
++ {0xFE,0xDC,0xBA,0x98,0x76,0x54,0x32,0x10},
++ {0x7C,0xA1,0x10,0x45,0x4A,0x1A,0x6E,0x57},
++ {0x01,0x31,0xD9,0x61,0x9D,0xC1,0x37,0x6E},
++ {0x07,0xA1,0x13,0x3E,0x4A,0x0B,0x26,0x86},
++ {0x38,0x49,0x67,0x4C,0x26,0x02,0x31,0x9E},
++ {0x04,0xB9,0x15,0xBA,0x43,0xFE,0xB5,0xB6},
++ {0x01,0x13,0xB9,0x70,0xFD,0x34,0xF2,0xCE},
++ {0x01,0x70,0xF1,0x75,0x46,0x8F,0xB5,0xE6},
++ {0x43,0x29,0x7F,0xAD,0x38,0xE3,0x73,0xFE},
++ {0x07,0xA7,0x13,0x70,0x45,0xDA,0x2A,0x16},
++ {0x04,0x68,0x91,0x04,0xC2,0xFD,0x3B,0x2F},
++ {0x37,0xD0,0x6B,0xB5,0x16,0xCB,0x75,0x46},
++ {0x1F,0x08,0x26,0x0D,0x1A,0xC2,0x46,0x5E},
++ {0x58,0x40,0x23,0x64,0x1A,0xBA,0x61,0x76},
++ {0x02,0x58,0x16,0x16,0x46,0x29,0xB0,0x07},
++ {0x49,0x79,0x3E,0xBC,0x79,0xB3,0x25,0x8F},
++ {0x4F,0xB0,0x5E,0x15,0x15,0xAB,0x73,0xA7},
++ {0x49,0xE9,0x5D,0x6D,0x4C,0xA2,0x29,0xBF},
++ {0x01,0x83,0x10,0xDC,0x40,0x9B,0x26,0xD6},
++ {0x1C,0x58,0x7F,0x1C,0x13,0x92,0x4F,0xEF},
++ {0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01},
++ {0x1F,0x1F,0x1F,0x1F,0x0E,0x0E,0x0E,0x0E},
++ {0xE0,0xFE,0xE0,0xFE,0xF1,0xFE,0xF1,0xFE},
++ {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
++ {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF},
++ {0x01,0x23,0x45,0x67,0x89,0xAB,0xCD,0xEF},
++ {0xFE,0xDC,0xBA,0x98,0x76,0x54,0x32,0x10}};
++
++static unsigned char plain_data[NUM_TESTS][8]={
++ {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
++ {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF},
++ {0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x01},
++ {0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11},
++ {0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11},
++ {0x01,0x23,0x45,0x67,0x89,0xAB,0xCD,0xEF},
++ {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
++ {0x01,0x23,0x45,0x67,0x89,0xAB,0xCD,0xEF},
++ {0x01,0xA1,0xD6,0xD0,0x39,0x77,0x67,0x42},
++ {0x5C,0xD5,0x4C,0xA8,0x3D,0xEF,0x57,0xDA},
++ {0x02,0x48,0xD4,0x38,0x06,0xF6,0x71,0x72},
++ {0x51,0x45,0x4B,0x58,0x2D,0xDF,0x44,0x0A},
++ {0x42,0xFD,0x44,0x30,0x59,0x57,0x7F,0xA2},
++ {0x05,0x9B,0x5E,0x08,0x51,0xCF,0x14,0x3A},
++ {0x07,0x56,0xD8,0xE0,0x77,0x47,0x61,0xD2},
++ {0x76,0x25,0x14,0xB8,0x29,0xBF,0x48,0x6A},
++ {0x3B,0xDD,0x11,0x90,0x49,0x37,0x28,0x02},
++ {0x26,0x95,0x5F,0x68,0x35,0xAF,0x60,0x9A},
++ {0x16,0x4D,0x5E,0x40,0x4F,0x27,0x52,0x32},
++ {0x6B,0x05,0x6E,0x18,0x75,0x9F,0x5C,0xCA},
++ {0x00,0x4B,0xD6,0xEF,0x09,0x17,0x60,0x62},
++ {0x48,0x0D,0x39,0x00,0x6E,0xE7,0x62,0xF2},
++ {0x43,0x75,0x40,0xC8,0x69,0x8F,0x3C,0xFA},
++ {0x07,0x2D,0x43,0xA0,0x77,0x07,0x52,0x92},
++ {0x02,0xFE,0x55,0x77,0x81,0x17,0xF1,0x2A},
++ {0x1D,0x9D,0x5C,0x50,0x18,0xF7,0x28,0xC2},
++ {0x30,0x55,0x32,0x28,0x6D,0x6F,0x29,0x5A},
++ {0x01,0x23,0x45,0x67,0x89,0xAB,0xCD,0xEF},
++ {0x01,0x23,0x45,0x67,0x89,0xAB,0xCD,0xEF},
++ {0x01,0x23,0x45,0x67,0x89,0xAB,0xCD,0xEF},
++ {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF},
++ {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
++ {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
++ {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}};
++
++static unsigned char cipher_data[NUM_TESTS][8]={
++ {0x8C,0xA6,0x4D,0xE9,0xC1,0xB1,0x23,0xA7},
++ {0x73,0x59,0xB2,0x16,0x3E,0x4E,0xDC,0x58},
++ {0x95,0x8E,0x6E,0x62,0x7A,0x05,0x55,0x7B},
++ {0xF4,0x03,0x79,0xAB,0x9E,0x0E,0xC5,0x33},
++ {0x17,0x66,0x8D,0xFC,0x72,0x92,0x53,0x2D},
++ {0x8A,0x5A,0xE1,0xF8,0x1A,0xB8,0xF2,0xDD},
++ {0x8C,0xA6,0x4D,0xE9,0xC1,0xB1,0x23,0xA7},
++ {0xED,0x39,0xD9,0x50,0xFA,0x74,0xBC,0xC4},
++ {0x69,0x0F,0x5B,0x0D,0x9A,0x26,0x93,0x9B},
++ {0x7A,0x38,0x9D,0x10,0x35,0x4B,0xD2,0x71},
++ {0x86,0x8E,0xBB,0x51,0xCA,0xB4,0x59,0x9A},
++ {0x71,0x78,0x87,0x6E,0x01,0xF1,0x9B,0x2A},
++ {0xAF,0x37,0xFB,0x42,0x1F,0x8C,0x40,0x95},
++ {0x86,0xA5,0x60,0xF1,0x0E,0xC6,0xD8,0x5B},
++ {0x0C,0xD3,0xDA,0x02,0x00,0x21,0xDC,0x09},
++ {0xEA,0x67,0x6B,0x2C,0xB7,0xDB,0x2B,0x7A},
++ {0xDF,0xD6,0x4A,0x81,0x5C,0xAF,0x1A,0x0F},
++ {0x5C,0x51,0x3C,0x9C,0x48,0x86,0xC0,0x88},
++ {0x0A,0x2A,0xEE,0xAE,0x3F,0xF4,0xAB,0x77},
++ {0xEF,0x1B,0xF0,0x3E,0x5D,0xFA,0x57,0x5A},
++ {0x88,0xBF,0x0D,0xB6,0xD7,0x0D,0xEE,0x56},
++ {0xA1,0xF9,0x91,0x55,0x41,0x02,0x0B,0x56},
++ {0x6F,0xBF,0x1C,0xAF,0xCF,0xFD,0x05,0x56},
++ {0x2F,0x22,0xE4,0x9B,0xAB,0x7C,0xA1,0xAC},
++ {0x5A,0x6B,0x61,0x2C,0xC2,0x6C,0xCE,0x4A},
++ {0x5F,0x4C,0x03,0x8E,0xD1,0x2B,0x2E,0x41},
++ {0x63,0xFA,0xC0,0xD0,0x34,0xD9,0xF7,0x93},
++ {0x61,0x7B,0x3A,0x0C,0xE8,0xF0,0x71,0x00},
++ {0xDB,0x95,0x86,0x05,0xF8,0xC8,0xC6,0x06},
++ {0xED,0xBF,0xD1,0xC6,0x6C,0x29,0xCC,0xC7},
++ {0x35,0x55,0x50,0xB2,0x15,0x0E,0x24,0x51},
++ {0xCA,0xAA,0xAF,0x4D,0xEA,0xF1,0xDB,0xAE},
++ {0xD5,0xD4,0x4F,0xF7,0x20,0x68,0x3D,0x0D},
++ {0x2A,0x2B,0xB0,0x08,0xDF,0x97,0xC2,0xF2}};
++
++static unsigned char cipher_ecb2[NUM_TESTS-1][8]={
++ {0x92,0x95,0xB5,0x9B,0xB3,0x84,0x73,0x6E},
++ {0x19,0x9E,0x9D,0x6D,0xF3,0x9A,0xA8,0x16},
++ {0x2A,0x4B,0x4D,0x24,0x52,0x43,0x84,0x27},
++ {0x35,0x84,0x3C,0x01,0x9D,0x18,0xC5,0xB6},
++ {0x4A,0x5B,0x2F,0x42,0xAA,0x77,0x19,0x25},
++ {0xA0,0x6B,0xA9,0xB8,0xCA,0x5B,0x17,0x8A},
++ {0xAB,0x9D,0xB7,0xFB,0xED,0x95,0xF2,0x74},
++ {0x3D,0x25,0x6C,0x23,0xA7,0x25,0x2F,0xD6},
++ {0xB7,0x6F,0xAB,0x4F,0xBD,0xBD,0xB7,0x67},
++ {0x8F,0x68,0x27,0xD6,0x9C,0xF4,0x1A,0x10},
++ {0x82,0x57,0xA1,0xD6,0x50,0x5E,0x81,0x85},
++ {0xA2,0x0F,0x0A,0xCD,0x80,0x89,0x7D,0xFA},
++ {0xCD,0x2A,0x53,0x3A,0xDB,0x0D,0x7E,0xF3},
++ {0xD2,0xC2,0xBE,0x27,0xE8,0x1B,0x68,0xE3},
++ {0xE9,0x24,0xCF,0x4F,0x89,0x3C,0x5B,0x0A},
++ {0xA7,0x18,0xC3,0x9F,0xFA,0x9F,0xD7,0x69},
++ {0x77,0x2C,0x79,0xB1,0xD2,0x31,0x7E,0xB1},
++ {0x49,0xAB,0x92,0x7F,0xD0,0x22,0x00,0xB7},
++ {0xCE,0x1C,0x6C,0x7D,0x85,0xE3,0x4A,0x6F},
++ {0xBE,0x91,0xD6,0xE1,0x27,0xB2,0xE9,0x87},
++ {0x70,0x28,0xAE,0x8F,0xD1,0xF5,0x74,0x1A},
++ {0xAA,0x37,0x80,0xBB,0xF3,0x22,0x1D,0xDE},
++ {0xA6,0xC4,0xD2,0x5E,0x28,0x93,0xAC,0xB3},
++ {0x22,0x07,0x81,0x5A,0xE4,0xB7,0x1A,0xAD},
++ {0xDC,0xCE,0x05,0xE7,0x07,0xBD,0xF5,0x84},
++ {0x26,0x1D,0x39,0x2C,0xB3,0xBA,0xA5,0x85},
++ {0xB4,0xF7,0x0F,0x72,0xFB,0x04,0xF0,0xDC},
++ {0x95,0xBA,0xA9,0x4E,0x87,0x36,0xF2,0x89},
++ {0xD4,0x07,0x3A,0xF1,0x5A,0x17,0x82,0x0E},
++ {0xEF,0x6F,0xAF,0xA7,0x66,0x1A,0x7E,0x89},
++ {0xC1,0x97,0xF5,0x58,0x74,0x8A,0x20,0xE7},
++ {0x43,0x34,0xCF,0xDA,0x22,0xC4,0x86,0xC8},
++ {0x08,0xD7,0xB4,0xFB,0x62,0x9D,0x08,0x85}};
++
++static unsigned char cbc_key [8]={0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef};
++static unsigned char cbc2_key[8]={0xf0,0xe1,0xd2,0xc3,0xb4,0xa5,0x96,0x87};
++static unsigned char cbc3_key[8]={0xfe,0xdc,0xba,0x98,0x76,0x54,0x32,0x10};
++static unsigned char cbc_iv [8]={0xfe,0xdc,0xba,0x98,0x76,0x54,0x32,0x10};
++static char cbc_data[40]="7654321 Now is the time for \0001";
++
++static unsigned char cbc_ok[32]={
++ 0xcc,0xd1,0x73,0xff,0xab,0x20,0x39,0xf4,
++ 0xac,0xd8,0xae,0xfd,0xdf,0xd8,0xa1,0xeb,
++ 0x46,0x8e,0x91,0x15,0x78,0x88,0xba,0x68,
++ 0x1d,0x26,0x93,0x97,0xf7,0xfe,0x62,0xb4};
++
++static unsigned char xcbc_ok[32]={
++ 0x86,0x74,0x81,0x0D,0x61,0xA4,0xA5,0x48,
++ 0xB9,0x93,0x03,0xE1,0xB8,0xBB,0xBD,0xBD,
++ 0x64,0x30,0x0B,0xB9,0x06,0x65,0x81,0x76,
++ 0x04,0x1D,0x77,0x62,0x17,0xCA,0x2B,0xD2,
++ };
++
++static unsigned char cbc3_ok[32]={
++ 0x3F,0xE3,0x01,0xC9,0x62,0xAC,0x01,0xD0,
++ 0x22,0x13,0x76,0x3C,0x1C,0xBD,0x4C,0xDC,
++ 0x79,0x96,0x57,0xC0,0x64,0xEC,0xF5,0xD4,
++ 0x1C,0x67,0x38,0x12,0xCF,0xDE,0x96,0x75};
++
++static unsigned char pcbc_ok[32]={
++ 0xcc,0xd1,0x73,0xff,0xab,0x20,0x39,0xf4,
++ 0x6d,0xec,0xb4,0x70,0xa0,0xe5,0x6b,0x15,
++ 0xae,0xa6,0xbf,0x61,0xed,0x7d,0x9c,0x9f,
++ 0xf7,0x17,0x46,0x3b,0x8a,0xb3,0xcc,0x88};
++
++static unsigned char cfb_key[8]={0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef};
++static unsigned char cfb_iv[8]={0x12,0x34,0x56,0x78,0x90,0xab,0xcd,0xef};
++static unsigned char cfb_buf1[40],cfb_buf2[40],cfb_tmp[8];
++static unsigned char plain[24]=
++ {
++ 0x4e,0x6f,0x77,0x20,0x69,0x73,
++ 0x20,0x74,0x68,0x65,0x20,0x74,
++ 0x69,0x6d,0x65,0x20,0x66,0x6f,
++ 0x72,0x20,0x61,0x6c,0x6c,0x20
++ };
++static unsigned char cfb_cipher8[24]= {
++ 0xf3,0x1f,0xda,0x07,0x01,0x14, 0x62,0xee,0x18,0x7f,0x43,0xd8,
++ 0x0a,0x7c,0xd9,0xb5,0xb0,0xd2, 0x90,0xda,0x6e,0x5b,0x9a,0x87 };
++static unsigned char cfb_cipher16[24]={
++ 0xF3,0x09,0x87,0x87,0x7F,0x57, 0xF7,0x3C,0x36,0xB6,0xDB,0x70,
++ 0xD8,0xD5,0x34,0x19,0xD3,0x86, 0xB2,0x23,0xB7,0xB2,0xAD,0x1B };
++static unsigned char cfb_cipher32[24]={
++ 0xF3,0x09,0x62,0x49,0xA4,0xDF, 0xA4,0x9F,0x33,0xDC,0x7B,0xAD,
++ 0x4C,0xC8,0x9F,0x64,0xE4,0x53, 0xE5,0xEC,0x67,0x20,0xDA,0xB6 };
++static unsigned char cfb_cipher48[24]={
++ 0xF3,0x09,0x62,0x49,0xC7,0xF4, 0x30,0xB5,0x15,0xEC,0xBB,0x85,
++ 0x97,0x5A,0x13,0x8C,0x68,0x60, 0xE2,0x38,0x34,0x3C,0xDC,0x1F };
++static unsigned char cfb_cipher64[24]={
++ 0xF3,0x09,0x62,0x49,0xC7,0xF4, 0x6E,0x51,0xA6,0x9E,0x83,0x9B,
++ 0x1A,0x92,0xF7,0x84,0x03,0x46, 0x71,0x33,0x89,0x8E,0xA6,0x22 };
++
++static unsigned char ofb_key[8]={0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef};
++static unsigned char ofb_iv[8]={0x12,0x34,0x56,0x78,0x90,0xab,0xcd,0xef};
++static unsigned char ofb_buf1[24],ofb_buf2[24],ofb_tmp[8];
++static unsigned char ofb_cipher[24]=
++ {
++ 0xf3,0x09,0x62,0x49,0xc7,0xf4,0x6e,0x51,
++ 0x35,0xf2,0x4a,0x24,0x2e,0xeb,0x3d,0x3f,
++ 0x3d,0x6d,0x5b,0xe3,0x25,0x5a,0xf8,0xc3
++ };
++
++DES_LONG cbc_cksum_ret=0xB462FEF7L;
++unsigned char cbc_cksum_data[8]={0x1D,0x26,0x93,0x97,0xf7,0xfe,0x62,0xb4};
++
++#ifndef NOPROTO
++static char *pt(unsigned char *p);
++static int cfb_test(int bits, unsigned char *cfb_cipher);
++static int cfb64_test(unsigned char *cfb_cipher);
++static int ede_cfb64_test(unsigned char *cfb_cipher);
++#else
++static char *pt();
++static int cfb_test();
++static int cfb64_test();
++static int ede_cfb64_test();
++#endif
++
++int main(argc,argv)
++int argc;
++char *argv[];
++ {
++ int i,j,err=0;
++ des_cblock in,out,outin,iv3;
++ des_key_schedule ks,ks2,ks3;
++ unsigned char cbc_in[40];
++ unsigned char cbc_out[40];
++ DES_LONG cs;
++ unsigned char qret[4][4],cret[8];
++ DES_LONG lqret[4];
++ int num;
++ char *str;
++
++ printf("Doing ecb\n");
++ for (i=0; i<NUM_TESTS; i++)
++ {
++ if ((j=des_key_sched((C_Block *)(key_data[i]),ks)) != 0)
++ {
++ printf("Key error %2d:%d\n",i+1,j);
++ err=1;
++ }
++ memcpy(in,plain_data[i],8);
++ memset(out,0,8);
++ memset(outin,0,8);
++ des_ecb_encrypt((C_Block *)in,(C_Block *)out,ks,DES_ENCRYPT);
++ des_ecb_encrypt((C_Block *)out,(C_Block *)outin,ks,DES_DECRYPT);
++
++ if (memcmp(out,cipher_data[i],8) != 0)
++ {
++ printf("Encryption error %2d\nk=%s p=%s o=%s act=%s\n",
++ i+1,pt(key_data[i]),pt(in),pt(cipher_data[i]),
++ pt(out));
++ err=1;
++ }
++ if (memcmp(in,outin,8) != 0)
++ {
++ printf("Decryption error %2d\nk=%s p=%s o=%s act=%s\n",
++ i+1,pt(key_data[i]),pt(out),pt(in),pt(outin));
++ err=1;
++ }
++ }
++
++#ifndef LIBDES_LIT
++ printf("Doing ede ecb\n");
++ for (i=0; i<(NUM_TESTS-1); i++)
++ {
++ if ((j=des_key_sched((C_Block *)(key_data[i]),ks)) != 0)
++ {
++ err=1;
++ printf("Key error %2d:%d\n",i+1,j);
++ }
++ if ((j=des_key_sched((C_Block *)(key_data[i+1]),ks2)) != 0)
++ {
++ printf("Key error %2d:%d\n",i+2,j);
++ err=1;
++ }
++ if ((j=des_key_sched((C_Block *)(key_data[i+2]),ks3)) != 0)
++ {
++ printf("Key error %2d:%d\n",i+3,j);
++ err=1;
++ }
++ memcpy(in,plain_data[i],8);
++ memset(out,0,8);
++ memset(outin,0,8);
++ des_ecb2_encrypt((C_Block *)in,(C_Block *)out,ks,ks2,
++ DES_ENCRYPT);
++ des_ecb2_encrypt((C_Block *)out,(C_Block *)outin,ks,ks2,
++ DES_DECRYPT);
++
++ if (memcmp(out,cipher_ecb2[i],8) != 0)
++ {
++ printf("Encryption error %2d\nk=%s p=%s o=%s act=%s\n",
++ i+1,pt(key_data[i]),pt(in),pt(cipher_ecb2[i]),
++ pt(out));
++ err=1;
++ }
++ if (memcmp(in,outin,8) != 0)
++ {
++ printf("Decryption error %2d\nk=%s p=%s o=%s act=%s\n",
++ i+1,pt(key_data[i]),pt(out),pt(in),pt(outin));
++ err=1;
++ }
++ }
++#endif
++
++ printf("Doing cbc\n");
++ if ((j=des_key_sched((C_Block *)cbc_key,ks)) != 0)
++ {
++ printf("Key error %d\n",j);
++ err=1;
++ }
++ memset(cbc_out,0,40);
++ memset(cbc_in,0,40);
++ memcpy(iv3,cbc_iv,sizeof(cbc_iv));
++ des_ncbc_encrypt((C_Block *)cbc_data,(C_Block *)cbc_out,
++ (long)strlen((char *)cbc_data)+1,ks,
++ (C_Block *)iv3,DES_ENCRYPT);
++ if (memcmp(cbc_out,cbc_ok,32) != 0)
++ printf("cbc_encrypt encrypt error\n");
++
++ memcpy(iv3,cbc_iv,sizeof(cbc_iv));
++ des_ncbc_encrypt((C_Block *)cbc_out,(C_Block *)cbc_in,
++ (long)strlen((char *)cbc_data)+1,ks,
++ (C_Block *)iv3,DES_DECRYPT);
++ if (memcmp(cbc_in,cbc_data,strlen((char *)cbc_data)) != 0)
++ {
++ printf("cbc_encrypt decrypt error\n");
++ err=1;
++ }
++
++#ifndef LIBDES_LIT
++ printf("Doing desx cbc\n");
++ if ((j=des_key_sched((C_Block *)cbc_key,ks)) != 0)
++ {
++ printf("Key error %d\n",j);
++ err=1;
++ }
++ memset(cbc_out,0,40);
++ memset(cbc_in,0,40);
++ memcpy(iv3,cbc_iv,sizeof(cbc_iv));
++ des_xcbc_encrypt((C_Block *)cbc_data,(C_Block *)cbc_out,
++ (long)strlen((char *)cbc_data)+1,ks,
++ (C_Block *)iv3,
++ (C_Block *)cbc2_key, (C_Block *)cbc3_key, DES_ENCRYPT);
++ if (memcmp(cbc_out,xcbc_ok,32) != 0)
++ {
++ printf("des_xcbc_encrypt encrypt error\n");
++ }
++ memcpy(iv3,cbc_iv,sizeof(cbc_iv));
++ des_xcbc_encrypt((C_Block *)cbc_out,(C_Block *)cbc_in,
++ (long)strlen((char *)cbc_data)+1,ks,
++ (C_Block *)iv3,
++ (C_Block *)cbc2_key, (C_Block *)cbc3_key, DES_DECRYPT);
++ if (memcmp(cbc_in,cbc_data,strlen((char *)cbc_data)+1) != 0)
++ {
++ printf("des_xcbc_encrypt decrypt error\n");
++ err=1;
++ }
++#endif
++
++ printf("Doing ede cbc\n");
++ if ((j=des_key_sched((C_Block *)cbc_key,ks)) != 0)
++ {
++ printf("Key error %d\n",j);
++ err=1;
++ }
++ if ((j=des_key_sched((C_Block *)cbc2_key,ks2)) != 0)
++ {
++ printf("Key error %d\n",j);
++ err=1;
++ }
++ if ((j=des_key_sched((C_Block *)cbc3_key,ks3)) != 0)
++ {
++ printf("Key error %d\n",j);
++ err=1;
++ }
++ memset(cbc_out,0,40);
++ memset(cbc_in,0,40);
++ i=strlen((char *)cbc_data)+1;
++ /* i=((i+7)/8)*8; */
++ memcpy(iv3,cbc_iv,sizeof(cbc_iv));
++
++ des_ede3_cbc_encrypt((C_Block *)cbc_data,(C_Block *)cbc_out,
++ 16L,ks,ks2,ks3,(C_Block *)iv3,DES_ENCRYPT);
++ des_ede3_cbc_encrypt((C_Block *)&(cbc_data[16]),
++ (C_Block *)&(cbc_out[16]),
++ (long)i-16,ks,ks2,ks3,(C_Block *)iv3,DES_ENCRYPT);
++ if (memcmp(cbc_out,cbc3_ok,
++ (unsigned int)(strlen((char *)cbc_data)+1+7)/8*8) != 0)
++ {
++ printf("des_ede3_cbc_encrypt encrypt error\n");
++ err=1;
++ }
++
++ memcpy(iv3,cbc_iv,sizeof(cbc_iv));
++ des_ede3_cbc_encrypt((C_Block *)cbc_out,(C_Block *)cbc_in,
++ (long)i,ks,ks2,ks3,(C_Block *)iv3,DES_DECRYPT);
++ if (memcmp(cbc_in,cbc_data,strlen(cbc_data)+1) != 0)
++ {
++ printf("des_ede3_cbc_encrypt decrypt error\n");
++ err=1;
++ }
++
++#ifndef LIBDES_LIT
++ printf("Doing pcbc\n");
++ if ((j=des_key_sched((C_Block *)cbc_key,ks)) != 0)
++ {
++ printf("Key error %d\n",j);
++ err=1;
++ }
++ memset(cbc_out,0,40);
++ memset(cbc_in,0,40);
++ des_pcbc_encrypt((C_Block *)cbc_data,(C_Block *)cbc_out,
++ (long)strlen(cbc_data)+1,ks,(C_Block *)cbc_iv,DES_ENCRYPT);
++ if (memcmp(cbc_out,pcbc_ok,32) != 0)
++ {
++ printf("pcbc_encrypt encrypt error\n");
++ err=1;
++ }
++ des_pcbc_encrypt((C_Block *)cbc_out,(C_Block *)cbc_in,
++ (long)strlen(cbc_data)+1,ks,(C_Block *)cbc_iv,DES_DECRYPT);
++ if (memcmp(cbc_in,cbc_data,strlen(cbc_data)+1) != 0)
++ {
++ printf("pcbc_encrypt decrypt error\n");
++ err=1;
++ }
++
++ printf("Doing ");
++ printf("cfb8 ");
++ err+=cfb_test(8,cfb_cipher8);
++ printf("cfb16 ");
++ err+=cfb_test(16,cfb_cipher16);
++ printf("cfb32 ");
++ err+=cfb_test(32,cfb_cipher32);
++ printf("cfb48 ");
++ err+=cfb_test(48,cfb_cipher48);
++ printf("cfb64 ");
++ err+=cfb_test(64,cfb_cipher64);
++
++ printf("cfb64() ");
++ err+=cfb64_test(cfb_cipher64);
++
++ memcpy(cfb_tmp,cfb_iv,sizeof(cfb_iv));
++ for (i=0; i<sizeof(plain); i++)
++ des_cfb_encrypt(&(plain[i]),&(cfb_buf1[i]),
++ 8,(long)1,ks,(C_Block *)cfb_tmp,DES_ENCRYPT);
++ if (memcmp(cfb_cipher8,cfb_buf1,sizeof(plain)) != 0)
++ {
++ printf("cfb_encrypt small encrypt error\n");
++ err=1;
++ }
++
++ memcpy(cfb_tmp,cfb_iv,sizeof(cfb_iv));
++ for (i=0; i<sizeof(plain); i++)
++ des_cfb_encrypt(&(cfb_buf1[i]),&(cfb_buf2[i]),
++ 8,(long)1,ks,(C_Block *)cfb_tmp,DES_DECRYPT);
++ if (memcmp(plain,cfb_buf2,sizeof(plain)) != 0)
++ {
++ printf("cfb_encrypt small decrypt error\n");
++ err=1;
++ }
++
++ printf("ede_cfb64() ");
++ err+=ede_cfb64_test(cfb_cipher64);
++
++ printf("done\n");
++
++ printf("Doing ofb\n");
++ des_key_sched((C_Block *)ofb_key,ks);
++ memcpy(ofb_tmp,ofb_iv,sizeof(ofb_iv));
++ des_ofb_encrypt(plain,ofb_buf1,64,(long)sizeof(plain)/8,ks,
++ (C_Block *)ofb_tmp);
++ if (memcmp(ofb_cipher,ofb_buf1,sizeof(ofb_buf1)) != 0)
++ {
++ printf("ofb_encrypt encrypt error\n");
++printf("%02X %02X %02X %02X %02X %02X %02X %02X\n",
++ofb_buf1[8+0], ofb_buf1[8+1], ofb_buf1[8+2], ofb_buf1[8+3],
++ofb_buf1[8+4], ofb_buf1[8+5], ofb_buf1[8+6], ofb_buf1[8+7]);
++printf("%02X %02X %02X %02X %02X %02X %02X %02X\n",
++ofb_buf1[8+0], ofb_cipher[8+1], ofb_cipher[8+2], ofb_cipher[8+3],
++ofb_buf1[8+4], ofb_cipher[8+5], ofb_cipher[8+6], ofb_cipher[8+7]);
++ err=1;
++ }
++ memcpy(ofb_tmp,ofb_iv,sizeof(ofb_iv));
++ des_ofb_encrypt(ofb_buf1,ofb_buf2,64,(long)sizeof(ofb_buf1)/8,ks,
++ (C_Block *)ofb_tmp);
++ if (memcmp(plain,ofb_buf2,sizeof(ofb_buf2)) != 0)
++ {
++ printf("ofb_encrypt decrypt error\n");
++printf("%02X %02X %02X %02X %02X %02X %02X %02X\n",
++ofb_buf2[8+0], ofb_buf2[8+1], ofb_buf2[8+2], ofb_buf2[8+3],
++ofb_buf2[8+4], ofb_buf2[8+5], ofb_buf2[8+6], ofb_buf2[8+7]);
++printf("%02X %02X %02X %02X %02X %02X %02X %02X\n",
++plain[8+0], plain[8+1], plain[8+2], plain[8+3],
++plain[8+4], plain[8+5], plain[8+6], plain[8+7]);
++ err=1;
++ }
++
++ printf("Doing ofb64\n");
++ des_key_sched((C_Block *)ofb_key,ks);
++ memcpy(ofb_tmp,ofb_iv,sizeof(ofb_iv));
++ memset(ofb_buf1,0,sizeof(ofb_buf1));
++ memset(ofb_buf2,0,sizeof(ofb_buf1));
++ num=0;
++ for (i=0; i<sizeof(plain); i++)
++ {
++ des_ofb64_encrypt(&(plain[i]),&(ofb_buf1[i]),1,ks,
++ (C_Block *)ofb_tmp,&num);
++ }
++ if (memcmp(ofb_cipher,ofb_buf1,sizeof(ofb_buf1)) != 0)
++ {
++ printf("ofb64_encrypt encrypt error\n");
++ err=1;
++ }
++ memcpy(ofb_tmp,ofb_iv,sizeof(ofb_iv));
++ num=0;
++ des_ofb64_encrypt(ofb_buf1,ofb_buf2,(long)sizeof(ofb_buf1),ks,
++ (C_Block *)ofb_tmp,&num);
++ if (memcmp(plain,ofb_buf2,sizeof(ofb_buf2)) != 0)
++ {
++ printf("ofb64_encrypt decrypt error\n");
++ err=1;
++ }
++
++ printf("Doing ede_ofb64\n");
++ des_key_sched((C_Block *)ofb_key,ks);
++ memcpy(ofb_tmp,ofb_iv,sizeof(ofb_iv));
++ memset(ofb_buf1,0,sizeof(ofb_buf1));
++ memset(ofb_buf2,0,sizeof(ofb_buf1));
++ num=0;
++ for (i=0; i<sizeof(plain); i++)
++ {
++ des_ede3_ofb64_encrypt(&(plain[i]),&(ofb_buf1[i]),1,ks,ks,ks,
++ (C_Block *)ofb_tmp,&num);
++ }
++ if (memcmp(ofb_cipher,ofb_buf1,sizeof(ofb_buf1)) != 0)
++ {
++ printf("ede_ofb64_encrypt encrypt error\n");
++ err=1;
++ }
++ memcpy(ofb_tmp,ofb_iv,sizeof(ofb_iv));
++ num=0;
++ des_ede3_ofb64_encrypt(ofb_buf1,ofb_buf2,(long)sizeof(ofb_buf1),ks,
++ ks,ks,(C_Block *)ofb_tmp,&num);
++ if (memcmp(plain,ofb_buf2,sizeof(ofb_buf2)) != 0)
++ {
++ printf("ede_ofb64_encrypt decrypt error\n");
++ err=1;
++ }
++
++ printf("Doing cbc_cksum\n");
++ des_key_sched((C_Block *)cbc_key,ks);
++ cs=des_cbc_cksum((C_Block *)cbc_data,(C_Block *)cret,
++ (long)strlen(cbc_data),ks,(C_Block *)cbc_iv);
++ if (cs != cbc_cksum_ret)
++ {
++ printf("bad return value (%08lX), should be %08lX\n",
++ (unsigned long)cs,(unsigned long)cbc_cksum_ret);
++ err=1;
++ }
++ if (memcmp(cret,cbc_cksum_data,8) != 0)
++ {
++ printf("bad cbc_cksum block returned\n");
++ err=1;
++ }
++
++ printf("Doing quad_cksum\n");
++ cs=quad_cksum((C_Block *)cbc_data,(C_Block *)qret,
++ (long)strlen(cbc_data),2,(C_Block *)cbc_iv);
++ for (i=0; i<4; i++)
++ {
++ lqret[i]=0;
++ memcpy(&(lqret[i]),&(qret[i][0]),4);
++ }
++ { /* Big-endian fix */
++ static DES_LONG l=1;
++ static unsigned char *c=(unsigned char *)&l;
++ DES_LONG ll;
++
++ if (!c[0])
++ {
++ ll=lqret[0]^lqret[3];
++ lqret[0]^=ll;
++ lqret[3]^=ll;
++ ll=lqret[1]^lqret[2];
++ lqret[1]^=ll;
++ lqret[2]^=ll;
++ }
++ }
++ if (cs != 0x70d7a63aL)
++ {
++ printf("quad_cksum error, ret %08lx should be 70d7a63a\n",
++ (unsigned long)cs);
++ err=1;
++ }
++ if (lqret[0] != 0x327eba8dL)
++ {
++ printf("quad_cksum error, out[0] %08lx is not %08lx\n",
++ (unsigned long)lqret[0],0x327eba8dL);
++ err=1;
++ }
++ if (lqret[1] != 0x201a49ccL)
++ {
++ printf("quad_cksum error, out[1] %08lx is not %08lx\n",
++ (unsigned long)lqret[1],0x201a49ccL);
++ err=1;
++ }
++ if (lqret[2] != 0x70d7a63aL)
++ {
++ printf("quad_cksum error, out[2] %08lx is not %08lx\n",
++ (unsigned long)lqret[2],0x70d7a63aL);
++ err=1;
++ }
++ if (lqret[3] != 0x501c2c26L)
++ {
++ printf("quad_cksum error, out[3] %08lx is not %08lx\n",
++ (unsigned long)lqret[3],0x501c2c26L);
++ err=1;
++ }
++#endif
++
++ printf("input word alignment test");
++ for (i=0; i<4; i++)
++ {
++ printf(" %d",i);
++ des_ncbc_encrypt((C_Block *)&(cbc_out[i]),(C_Block *)cbc_in,
++ (long)strlen(cbc_data)+1,ks,(C_Block *)cbc_iv,
++ DES_ENCRYPT);
++ }
++ printf("\noutput word alignment test");
++ for (i=0; i<4; i++)
++ {
++ printf(" %d",i);
++ des_ncbc_encrypt((C_Block *)cbc_out,(C_Block *)&(cbc_in[i]),
++ (long)strlen(cbc_data)+1,ks,(C_Block *)cbc_iv,
++ DES_ENCRYPT);
++ }
++ printf("\n");
++ printf("fast crypt test ");
++ str=crypt("testing","ef");
++ if (strcmp("efGnQx2725bI2",str) != 0)
++ {
++ printf("fast crypt error, %s should be efGnQx2725bI2\n",str);
++ err=1;
++ }
++ str=crypt("bca76;23","yA");
++ if (strcmp("yA1Rp/1hZXIJk",str) != 0)
++ {
++ printf("fast crypt error, %s should be yA1Rp/1hZXIJk\n",str);
++ err=1;
++ }
++ printf("\n");
++ exit(err);
++ return(0);
++ }
++
++static char *pt(p)
++unsigned char *p;
++ {
++ static char bufs[10][20];
++ static int bnum=0;
++ char *ret;
++ int i;
++ static char *f="0123456789ABCDEF";
++
++ ret= &(bufs[bnum++][0]);
++ bnum%=10;
++ for (i=0; i<8; i++)
++ {
++ ret[i*2]=f[(p[i]>>4)&0xf];
++ ret[i*2+1]=f[p[i]&0xf];
++ }
++ ret[16]='\0';
++ return(ret);
++ }
++
++#ifndef LIBDES_LIT
++
++static int cfb_test(bits, cfb_cipher)
++int bits;
++unsigned char *cfb_cipher;
++ {
++ des_key_schedule ks;
++ int i,err=0;
++
++ des_key_sched((C_Block *)cfb_key,ks);
++ memcpy(cfb_tmp,cfb_iv,sizeof(cfb_iv));
++ des_cfb_encrypt(plain,cfb_buf1,bits,(long)sizeof(plain),ks,
++ (C_Block *)cfb_tmp,DES_ENCRYPT);
++ if (memcmp(cfb_cipher,cfb_buf1,sizeof(plain)) != 0)
++ {
++ err=1;
++ printf("cfb_encrypt encrypt error\n");
++ for (i=0; i<24; i+=8)
++ printf("%s\n",pt(&(cfb_buf1[i])));
++ }
++ memcpy(cfb_tmp,cfb_iv,sizeof(cfb_iv));
++ des_cfb_encrypt(cfb_buf1,cfb_buf2,bits,(long)sizeof(plain),ks,
++ (C_Block *)cfb_tmp,DES_DECRYPT);
++ if (memcmp(plain,cfb_buf2,sizeof(plain)) != 0)
++ {
++ err=1;
++ printf("cfb_encrypt decrypt error\n");
++ for (i=0; i<24; i+=8)
++ printf("%s\n",pt(&(cfb_buf1[i])));
++ }
++ return(err);
++ }
++
++static int cfb64_test(cfb_cipher)
++unsigned char *cfb_cipher;
++ {
++ des_key_schedule ks;
++ int err=0,i,n;
++
++ des_key_sched((C_Block *)cfb_key,ks);
++ memcpy(cfb_tmp,cfb_iv,sizeof(cfb_iv));
++ n=0;
++ des_cfb64_encrypt(plain,cfb_buf1,(long)12,ks,
++ (C_Block *)cfb_tmp,&n,DES_ENCRYPT);
++ des_cfb64_encrypt(&(plain[12]),&(cfb_buf1[12]),
++ (long)sizeof(plain)-12,ks,
++ (C_Block *)cfb_tmp,&n,DES_ENCRYPT);
++ if (memcmp(cfb_cipher,cfb_buf1,sizeof(plain)) != 0)
++ {
++ err=1;
++ printf("cfb_encrypt encrypt error\n");
++ for (i=0; i<24; i+=8)
++ printf("%s\n",pt(&(cfb_buf1[i])));
++ }
++ memcpy(cfb_tmp,cfb_iv,sizeof(cfb_iv));
++ n=0;
++ des_cfb64_encrypt(cfb_buf1,cfb_buf2,(long)17,ks,
++ (C_Block *)cfb_tmp,&n,DES_DECRYPT);
++ des_cfb64_encrypt(&(cfb_buf1[17]),&(cfb_buf2[17]),
++ (long)sizeof(plain)-17,ks,
++ (C_Block *)cfb_tmp,&n,DES_DECRYPT);
++ if (memcmp(plain,cfb_buf2,sizeof(plain)) != 0)
++ {
++ err=1;
++ printf("cfb_encrypt decrypt error\n");
++ for (i=0; i<24; i+=8)
++ printf("%s\n",pt(&(cfb_buf2[i])));
++ }
++ return(err);
++ }
++
++static int ede_cfb64_test(cfb_cipher)
++unsigned char *cfb_cipher;
++ {
++ des_key_schedule ks;
++ int err=0,i,n;
++
++ des_key_sched((C_Block *)cfb_key,ks);
++ memcpy(cfb_tmp,cfb_iv,sizeof(cfb_iv));
++ n=0;
++ des_ede3_cfb64_encrypt(plain,cfb_buf1,(long)12,ks,ks,ks,
++ (C_Block *)cfb_tmp,&n,DES_ENCRYPT);
++ des_ede3_cfb64_encrypt(&(plain[12]),&(cfb_buf1[12]),
++ (long)sizeof(plain)-12,ks,ks,ks,
++ (C_Block *)cfb_tmp,&n,DES_ENCRYPT);
++ if (memcmp(cfb_cipher,cfb_buf1,sizeof(plain)) != 0)
++ {
++ err=1;
++ printf("ede_cfb_encrypt encrypt error\n");
++ for (i=0; i<24; i+=8)
++ printf("%s\n",pt(&(cfb_buf1[i])));
++ }
++ memcpy(cfb_tmp,cfb_iv,sizeof(cfb_iv));
++ n=0;
++ des_ede3_cfb64_encrypt(cfb_buf1,cfb_buf2,(long)17,ks,ks,ks,
++ (C_Block *)cfb_tmp,&n,DES_DECRYPT);
++ des_ede3_cfb64_encrypt(&(cfb_buf1[17]),&(cfb_buf2[17]),
++ (long)sizeof(plain)-17,ks,ks,ks,
++ (C_Block *)cfb_tmp,&n,DES_DECRYPT);
++ if (memcmp(plain,cfb_buf2,sizeof(plain)) != 0)
++ {
++ err=1;
++ printf("ede_cfb_encrypt decrypt error\n");
++ for (i=0; i<24; i+=8)
++ printf("%s\n",pt(&(cfb_buf2[i])));
++ }
++ return(err);
++ }
++
++#endif
++
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/crypto/ciphers/des/dx86unix.S Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,3160 @@
++/*
++ * This file was originally generated by Michael Richardson <mcr@freeswan.org>
++ * via the perl scripts found in the ASM subdir. It remains copyright of
++ * Eric Young, see the file COPYRIGHT.
++ *
++ * This was last done on October 9, 2002.
++ *
++ * While this file does not need to go through cpp, we pass it through
++ * CPP by naming it dx86unix.S instead of dx86unix.s because there is
++ * a bug in Rules.make for .s builds - specifically it references EXTRA_CFLAGS
++ * which may contain stuff that AS doesn't understand instead of
++ * referencing EXTRA_AFLAGS.
++ */
++
++ .file "dx86unix.S"
++ .version "01.01"
++.text
++ .align 16
++.globl des_encrypt
++ .type des_encrypt , @function
++des_encrypt:
++ pushl %esi
++ pushl %edi
++
++
++ movl 12(%esp), %esi
++ xorl %ecx, %ecx
++ pushl %ebx
++ pushl %ebp
++ movl (%esi), %eax
++ movl 28(%esp), %ebx
++ movl 4(%esi), %edi
++
++
++ roll $4, %eax
++ movl %eax, %esi
++ xorl %edi, %eax
++ andl $0xf0f0f0f0, %eax
++ xorl %eax, %esi
++ xorl %eax, %edi
++
++ roll $20, %edi
++ movl %edi, %eax
++ xorl %esi, %edi
++ andl $0xfff0000f, %edi
++ xorl %edi, %eax
++ xorl %edi, %esi
++
++ roll $14, %eax
++ movl %eax, %edi
++ xorl %esi, %eax
++ andl $0x33333333, %eax
++ xorl %eax, %edi
++ xorl %eax, %esi
++
++ roll $22, %esi
++ movl %esi, %eax
++ xorl %edi, %esi
++ andl $0x03fc03fc, %esi
++ xorl %esi, %eax
++ xorl %esi, %edi
++
++ roll $9, %eax
++ movl %eax, %esi
++ xorl %edi, %eax
++ andl $0xaaaaaaaa, %eax
++ xorl %eax, %esi
++ xorl %eax, %edi
++
++.byte 209
++.byte 199
++ movl 24(%esp), %ebp
++ cmpl $0, %ebx
++ je .L000start_decrypt
++
++
++ movl (%ebp), %eax
++ xorl %ebx, %ebx
++ movl 4(%ebp), %edx
++ xorl %esi, %eax
++ xorl %esi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %edi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %edi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %edi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %edi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %edi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %edi
++
++
++ movl 8(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 12(%ebp), %edx
++ xorl %edi, %eax
++ xorl %edi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %esi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %esi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %esi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %esi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %esi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %esi
++
++
++ movl 16(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 20(%ebp), %edx
++ xorl %esi, %eax
++ xorl %esi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %edi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %edi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %edi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %edi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %edi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %edi
++
++
++ movl 24(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 28(%ebp), %edx
++ xorl %edi, %eax
++ xorl %edi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %esi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %esi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %esi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %esi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %esi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %esi
++
++
++ movl 32(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 36(%ebp), %edx
++ xorl %esi, %eax
++ xorl %esi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %edi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %edi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %edi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %edi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %edi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %edi
++
++
++ movl 40(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 44(%ebp), %edx
++ xorl %edi, %eax
++ xorl %edi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %esi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %esi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %esi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %esi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %esi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %esi
++
++
++ movl 48(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 52(%ebp), %edx
++ xorl %esi, %eax
++ xorl %esi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %edi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %edi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %edi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %edi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %edi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %edi
++
++
++ movl 56(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 60(%ebp), %edx
++ xorl %edi, %eax
++ xorl %edi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %esi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %esi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %esi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %esi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %esi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %esi
++
++
++ movl 64(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 68(%ebp), %edx
++ xorl %esi, %eax
++ xorl %esi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %edi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %edi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %edi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %edi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %edi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %edi
++
++
++ movl 72(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 76(%ebp), %edx
++ xorl %edi, %eax
++ xorl %edi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %esi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %esi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %esi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %esi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %esi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %esi
++
++
++ movl 80(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 84(%ebp), %edx
++ xorl %esi, %eax
++ xorl %esi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %edi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %edi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %edi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %edi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %edi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %edi
++
++
++ movl 88(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 92(%ebp), %edx
++ xorl %edi, %eax
++ xorl %edi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %esi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %esi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %esi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %esi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %esi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %esi
++
++
++ movl 96(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 100(%ebp), %edx
++ xorl %esi, %eax
++ xorl %esi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %edi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %edi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %edi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %edi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %edi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %edi
++
++
++ movl 104(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 108(%ebp), %edx
++ xorl %edi, %eax
++ xorl %edi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %esi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %esi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %esi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %esi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %esi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %esi
++
++
++ movl 112(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 116(%ebp), %edx
++ xorl %esi, %eax
++ xorl %esi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %edi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %edi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %edi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %edi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %edi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %edi
++
++
++ movl 120(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 124(%ebp), %edx
++ xorl %edi, %eax
++ xorl %edi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %esi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %esi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %esi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %esi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %esi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %esi
++ jmp .L001end
++.L000start_decrypt:
++
++
++ movl 120(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 124(%ebp), %edx
++ xorl %esi, %eax
++ xorl %esi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %edi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %edi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %edi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %edi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %edi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %edi
++
++
++ movl 112(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 116(%ebp), %edx
++ xorl %edi, %eax
++ xorl %edi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %esi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %esi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %esi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %esi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %esi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %esi
++
++
++ movl 104(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 108(%ebp), %edx
++ xorl %esi, %eax
++ xorl %esi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %edi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %edi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %edi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %edi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %edi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %edi
++
++
++ movl 96(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 100(%ebp), %edx
++ xorl %edi, %eax
++ xorl %edi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %esi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %esi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %esi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %esi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %esi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %esi
++
++
++ movl 88(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 92(%ebp), %edx
++ xorl %esi, %eax
++ xorl %esi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %edi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %edi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %edi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %edi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %edi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %edi
++
++
++ movl 80(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 84(%ebp), %edx
++ xorl %edi, %eax
++ xorl %edi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %esi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %esi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %esi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %esi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %esi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %esi
++
++
++ movl 72(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 76(%ebp), %edx
++ xorl %esi, %eax
++ xorl %esi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %edi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %edi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %edi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %edi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %edi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %edi
++
++
++ movl 64(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 68(%ebp), %edx
++ xorl %edi, %eax
++ xorl %edi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %esi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %esi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %esi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %esi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %esi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %esi
++
++
++ movl 56(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 60(%ebp), %edx
++ xorl %esi, %eax
++ xorl %esi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %edi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %edi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %edi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %edi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %edi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %edi
++
++
++ movl 48(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 52(%ebp), %edx
++ xorl %edi, %eax
++ xorl %edi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %esi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %esi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %esi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %esi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %esi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %esi
++
++
++ movl 40(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 44(%ebp), %edx
++ xorl %esi, %eax
++ xorl %esi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %edi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %edi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %edi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %edi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %edi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %edi
++
++
++ movl 32(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 36(%ebp), %edx
++ xorl %edi, %eax
++ xorl %edi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %esi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %esi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %esi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %esi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %esi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %esi
++
++
++ movl 24(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 28(%ebp), %edx
++ xorl %esi, %eax
++ xorl %esi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %edi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %edi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %edi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %edi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %edi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %edi
++
++
++ movl 16(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 20(%ebp), %edx
++ xorl %edi, %eax
++ xorl %edi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %esi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %esi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %esi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %esi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %esi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %esi
++
++
++ movl 8(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 12(%ebp), %edx
++ xorl %esi, %eax
++ xorl %esi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %edi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %edi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %edi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %edi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %edi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %edi
++
++
++ movl (%ebp), %eax
++ xorl %ebx, %ebx
++ movl 4(%ebp), %edx
++ xorl %edi, %eax
++ xorl %edi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %esi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %esi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %esi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %esi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %esi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %esi
++.L001end:
++
++
++ movl 20(%esp), %edx
++.byte 209
++.byte 206
++ movl %edi, %eax
++ xorl %esi, %edi
++ andl $0xaaaaaaaa, %edi
++ xorl %edi, %eax
++ xorl %edi, %esi
++
++ roll $23, %eax
++ movl %eax, %edi
++ xorl %esi, %eax
++ andl $0x03fc03fc, %eax
++ xorl %eax, %edi
++ xorl %eax, %esi
++
++ roll $10, %edi
++ movl %edi, %eax
++ xorl %esi, %edi
++ andl $0x33333333, %edi
++ xorl %edi, %eax
++ xorl %edi, %esi
++
++ roll $18, %esi
++ movl %esi, %edi
++ xorl %eax, %esi
++ andl $0xfff0000f, %esi
++ xorl %esi, %edi
++ xorl %esi, %eax
++
++ roll $12, %edi
++ movl %edi, %esi
++ xorl %eax, %edi
++ andl $0xf0f0f0f0, %edi
++ xorl %edi, %esi
++ xorl %edi, %eax
++
++ rorl $4, %eax
++ movl %eax, (%edx)
++ movl %esi, 4(%edx)
++ popl %ebp
++ popl %ebx
++ popl %edi
++ popl %esi
++ ret
++.des_encrypt_end:
++ .size des_encrypt , .des_encrypt_end-des_encrypt
++.ident "desasm.pl"
++.text
++ .align 16
++.globl des_encrypt2
++ .type des_encrypt2 , @function
++des_encrypt2:
++ pushl %esi
++ pushl %edi
++
++
++ movl 12(%esp), %eax
++ xorl %ecx, %ecx
++ pushl %ebx
++ pushl %ebp
++ movl (%eax), %esi
++ movl 28(%esp), %ebx
++ roll $3, %esi
++ movl 4(%eax), %edi
++ roll $3, %edi
++ movl 24(%esp), %ebp
++ cmpl $0, %ebx
++ je .L002start_decrypt
++
++
++ movl (%ebp), %eax
++ xorl %ebx, %ebx
++ movl 4(%ebp), %edx
++ xorl %esi, %eax
++ xorl %esi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %edi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %edi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %edi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %edi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %edi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %edi
++
++
++ movl 8(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 12(%ebp), %edx
++ xorl %edi, %eax
++ xorl %edi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %esi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %esi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %esi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %esi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %esi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %esi
++
++
++ movl 16(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 20(%ebp), %edx
++ xorl %esi, %eax
++ xorl %esi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %edi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %edi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %edi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %edi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %edi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %edi
++
++
++ movl 24(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 28(%ebp), %edx
++ xorl %edi, %eax
++ xorl %edi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %esi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %esi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %esi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %esi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %esi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %esi
++
++
++ movl 32(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 36(%ebp), %edx
++ xorl %esi, %eax
++ xorl %esi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %edi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %edi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %edi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %edi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %edi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %edi
++
++
++ movl 40(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 44(%ebp), %edx
++ xorl %edi, %eax
++ xorl %edi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %esi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %esi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %esi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %esi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %esi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %esi
++
++
++ movl 48(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 52(%ebp), %edx
++ xorl %esi, %eax
++ xorl %esi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %edi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %edi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %edi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %edi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %edi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %edi
++
++
++ movl 56(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 60(%ebp), %edx
++ xorl %edi, %eax
++ xorl %edi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %esi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %esi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %esi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %esi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %esi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %esi
++
++
++ movl 64(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 68(%ebp), %edx
++ xorl %esi, %eax
++ xorl %esi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %edi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %edi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %edi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %edi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %edi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %edi
++
++
++ movl 72(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 76(%ebp), %edx
++ xorl %edi, %eax
++ xorl %edi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %esi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %esi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %esi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %esi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %esi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %esi
++
++
++ movl 80(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 84(%ebp), %edx
++ xorl %esi, %eax
++ xorl %esi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %edi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %edi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %edi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %edi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %edi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %edi
++
++
++ movl 88(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 92(%ebp), %edx
++ xorl %edi, %eax
++ xorl %edi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %esi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %esi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %esi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %esi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %esi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %esi
++
++
++ movl 96(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 100(%ebp), %edx
++ xorl %esi, %eax
++ xorl %esi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %edi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %edi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %edi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %edi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %edi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %edi
++
++
++ movl 104(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 108(%ebp), %edx
++ xorl %edi, %eax
++ xorl %edi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %esi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %esi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %esi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %esi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %esi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %esi
++
++
++ movl 112(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 116(%ebp), %edx
++ xorl %esi, %eax
++ xorl %esi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %edi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %edi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %edi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %edi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %edi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %edi
++
++
++ movl 120(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 124(%ebp), %edx
++ xorl %edi, %eax
++ xorl %edi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %esi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %esi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %esi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %esi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %esi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %esi
++ jmp .L003end
++.L002start_decrypt:
++
++
++ movl 120(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 124(%ebp), %edx
++ xorl %esi, %eax
++ xorl %esi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %edi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %edi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %edi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %edi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %edi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %edi
++
++
++ movl 112(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 116(%ebp), %edx
++ xorl %edi, %eax
++ xorl %edi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %esi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %esi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %esi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %esi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %esi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %esi
++
++
++ movl 104(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 108(%ebp), %edx
++ xorl %esi, %eax
++ xorl %esi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %edi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %edi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %edi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %edi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %edi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %edi
++
++
++ movl 96(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 100(%ebp), %edx
++ xorl %edi, %eax
++ xorl %edi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %esi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %esi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %esi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %esi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %esi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %esi
++
++
++ movl 88(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 92(%ebp), %edx
++ xorl %esi, %eax
++ xorl %esi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %edi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %edi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %edi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %edi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %edi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %edi
++
++
++ movl 80(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 84(%ebp), %edx
++ xorl %edi, %eax
++ xorl %edi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %esi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %esi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %esi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %esi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %esi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %esi
++
++
++ movl 72(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 76(%ebp), %edx
++ xorl %esi, %eax
++ xorl %esi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %edi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %edi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %edi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %edi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %edi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %edi
++
++
++ movl 64(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 68(%ebp), %edx
++ xorl %edi, %eax
++ xorl %edi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %esi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %esi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %esi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %esi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %esi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %esi
++
++
++ movl 56(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 60(%ebp), %edx
++ xorl %esi, %eax
++ xorl %esi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %edi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %edi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %edi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %edi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %edi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %edi
++
++
++ movl 48(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 52(%ebp), %edx
++ xorl %edi, %eax
++ xorl %edi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %esi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %esi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %esi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %esi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %esi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %esi
++
++
++ movl 40(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 44(%ebp), %edx
++ xorl %esi, %eax
++ xorl %esi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %edi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %edi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %edi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %edi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %edi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %edi
++
++
++ movl 32(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 36(%ebp), %edx
++ xorl %edi, %eax
++ xorl %edi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %esi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %esi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %esi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %esi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %esi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %esi
++
++
++ movl 24(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 28(%ebp), %edx
++ xorl %esi, %eax
++ xorl %esi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %edi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %edi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %edi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %edi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %edi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %edi
++
++
++ movl 16(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 20(%ebp), %edx
++ xorl %edi, %eax
++ xorl %edi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %esi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %esi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %esi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %esi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %esi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %esi
++
++
++ movl 8(%ebp), %eax
++ xorl %ebx, %ebx
++ movl 12(%ebp), %edx
++ xorl %esi, %eax
++ xorl %esi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %edi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %edi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %edi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %edi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %edi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %edi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %edi
++
++
++ movl (%ebp), %eax
++ xorl %ebx, %ebx
++ movl 4(%ebp), %edx
++ xorl %edi, %eax
++ xorl %edi, %edx
++ andl $0xfcfcfcfc, %eax
++ andl $0xcfcfcfcf, %edx
++ movb %al, %bl
++ movb %ah, %cl
++ rorl $4, %edx
++ movl des_SPtrans(%ebx),%ebp
++ movb %dl, %bl
++ xorl %ebp, %esi
++ movl 0x200+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movb %dh, %cl
++ shrl $16, %eax
++ movl 0x100+des_SPtrans(%ebx),%ebp
++ xorl %ebp, %esi
++ movb %ah, %bl
++ shrl $16, %edx
++ movl 0x300+des_SPtrans(%ecx),%ebp
++ xorl %ebp, %esi
++ movl 24(%esp), %ebp
++ movb %dh, %cl
++ andl $0xff, %eax
++ andl $0xff, %edx
++ movl 0x600+des_SPtrans(%ebx),%ebx
++ xorl %ebx, %esi
++ movl 0x700+des_SPtrans(%ecx),%ebx
++ xorl %ebx, %esi
++ movl 0x400+des_SPtrans(%eax),%ebx
++ xorl %ebx, %esi
++ movl 0x500+des_SPtrans(%edx),%ebx
++ xorl %ebx, %esi
++.L003end:
++
++
++ rorl $3, %edi
++ movl 20(%esp), %eax
++ rorl $3, %esi
++ movl %edi, (%eax)
++ movl %esi, 4(%eax)
++ popl %ebp
++ popl %ebx
++ popl %edi
++ popl %esi
++ ret
++.des_encrypt2_end:
++ .size des_encrypt2 , .des_encrypt2_end-des_encrypt2
++.ident "desasm.pl"
++.text
++ .align 16
++.globl des_encrypt3
++ .type des_encrypt3 , @function
++des_encrypt3:
++ pushl %ebx
++ movl 8(%esp), %ebx
++ pushl %ebp
++ pushl %esi
++ pushl %edi
++
++
++ movl (%ebx), %edi
++ movl 4(%ebx), %esi
++ subl $12, %esp
++
++
++ roll $4, %edi
++ movl %edi, %edx
++ xorl %esi, %edi
++ andl $0xf0f0f0f0, %edi
++ xorl %edi, %edx
++ xorl %edi, %esi
++
++ roll $20, %esi
++ movl %esi, %edi
++ xorl %edx, %esi
++ andl $0xfff0000f, %esi
++ xorl %esi, %edi
++ xorl %esi, %edx
++
++ roll $14, %edi
++ movl %edi, %esi
++ xorl %edx, %edi
++ andl $0x33333333, %edi
++ xorl %edi, %esi
++ xorl %edi, %edx
++
++ roll $22, %edx
++ movl %edx, %edi
++ xorl %esi, %edx
++ andl $0x03fc03fc, %edx
++ xorl %edx, %edi
++ xorl %edx, %esi
++
++ roll $9, %edi
++ movl %edi, %edx
++ xorl %esi, %edi
++ andl $0xaaaaaaaa, %edi
++ xorl %edi, %edx
++ xorl %edi, %esi
++
++ rorl $3, %edx
++ rorl $2, %esi
++ movl %esi, 4(%ebx)
++ movl 36(%esp), %eax
++ movl %edx, (%ebx)
++ movl 40(%esp), %edi
++ movl 44(%esp), %esi
++ movl $1, 8(%esp)
++ movl %eax, 4(%esp)
++ movl %ebx, (%esp)
++ call des_encrypt2
++ movl $0, 8(%esp)
++ movl %edi, 4(%esp)
++ movl %ebx, (%esp)
++ call des_encrypt2
++ movl $1, 8(%esp)
++ movl %esi, 4(%esp)
++ movl %ebx, (%esp)
++ call des_encrypt2
++ addl $12, %esp
++ movl (%ebx), %edi
++ movl 4(%ebx), %esi
++
++
++ roll $2, %esi
++ roll $3, %edi
++ movl %edi, %eax
++ xorl %esi, %edi
++ andl $0xaaaaaaaa, %edi
++ xorl %edi, %eax
++ xorl %edi, %esi
++
++ roll $23, %eax
++ movl %eax, %edi
++ xorl %esi, %eax
++ andl $0x03fc03fc, %eax
++ xorl %eax, %edi
++ xorl %eax, %esi
++
++ roll $10, %edi
++ movl %edi, %eax
++ xorl %esi, %edi
++ andl $0x33333333, %edi
++ xorl %edi, %eax
++ xorl %edi, %esi
++
++ roll $18, %esi
++ movl %esi, %edi
++ xorl %eax, %esi
++ andl $0xfff0000f, %esi
++ xorl %esi, %edi
++ xorl %esi, %eax
++
++ roll $12, %edi
++ movl %edi, %esi
++ xorl %eax, %edi
++ andl $0xf0f0f0f0, %edi
++ xorl %edi, %esi
++ xorl %edi, %eax
++
++ rorl $4, %eax
++ movl %eax, (%ebx)
++ movl %esi, 4(%ebx)
++ popl %edi
++ popl %esi
++ popl %ebp
++ popl %ebx
++ ret
++.des_encrypt3_end:
++ .size des_encrypt3 , .des_encrypt3_end-des_encrypt3
++.ident "desasm.pl"
++.text
++ .align 16
++.globl des_decrypt3
++ .type des_decrypt3 , @function
++des_decrypt3:
++ pushl %ebx
++ movl 8(%esp), %ebx
++ pushl %ebp
++ pushl %esi
++ pushl %edi
++
++
++ movl (%ebx), %edi
++ movl 4(%ebx), %esi
++ subl $12, %esp
++
++
++ roll $4, %edi
++ movl %edi, %edx
++ xorl %esi, %edi
++ andl $0xf0f0f0f0, %edi
++ xorl %edi, %edx
++ xorl %edi, %esi
++
++ roll $20, %esi
++ movl %esi, %edi
++ xorl %edx, %esi
++ andl $0xfff0000f, %esi
++ xorl %esi, %edi
++ xorl %esi, %edx
++
++ roll $14, %edi
++ movl %edi, %esi
++ xorl %edx, %edi
++ andl $0x33333333, %edi
++ xorl %edi, %esi
++ xorl %edi, %edx
++
++ roll $22, %edx
++ movl %edx, %edi
++ xorl %esi, %edx
++ andl $0x03fc03fc, %edx
++ xorl %edx, %edi
++ xorl %edx, %esi
++
++ roll $9, %edi
++ movl %edi, %edx
++ xorl %esi, %edi
++ andl $0xaaaaaaaa, %edi
++ xorl %edi, %edx
++ xorl %edi, %esi
++
++ rorl $3, %edx
++ rorl $2, %esi
++ movl %esi, 4(%ebx)
++ movl 36(%esp), %esi
++ movl %edx, (%ebx)
++ movl 40(%esp), %edi
++ movl 44(%esp), %eax
++ movl $0, 8(%esp)
++ movl %eax, 4(%esp)
++ movl %ebx, (%esp)
++ call des_encrypt2
++ movl $1, 8(%esp)
++ movl %edi, 4(%esp)
++ movl %ebx, (%esp)
++ call des_encrypt2
++ movl $0, 8(%esp)
++ movl %esi, 4(%esp)
++ movl %ebx, (%esp)
++ call des_encrypt2
++ addl $12, %esp
++ movl (%ebx), %edi
++ movl 4(%ebx), %esi
++
++
++ roll $2, %esi
++ roll $3, %edi
++ movl %edi, %eax
++ xorl %esi, %edi
++ andl $0xaaaaaaaa, %edi
++ xorl %edi, %eax
++ xorl %edi, %esi
++
++ roll $23, %eax
++ movl %eax, %edi
++ xorl %esi, %eax
++ andl $0x03fc03fc, %eax
++ xorl %eax, %edi
++ xorl %eax, %esi
++
++ roll $10, %edi
++ movl %edi, %eax
++ xorl %esi, %edi
++ andl $0x33333333, %edi
++ xorl %edi, %eax
++ xorl %edi, %esi
++
++ roll $18, %esi
++ movl %esi, %edi
++ xorl %eax, %esi
++ andl $0xfff0000f, %esi
++ xorl %esi, %edi
++ xorl %esi, %eax
++
++ roll $12, %edi
++ movl %edi, %esi
++ xorl %eax, %edi
++ andl $0xf0f0f0f0, %edi
++ xorl %edi, %esi
++ xorl %edi, %eax
++
++ rorl $4, %eax
++ movl %eax, (%ebx)
++ movl %esi, 4(%ebx)
++ popl %edi
++ popl %esi
++ popl %ebp
++ popl %ebx
++ ret
++.des_decrypt3_end:
++ .size des_decrypt3 , .des_decrypt3_end-des_decrypt3
++.ident "desasm.pl"
++.text
++ .align 16
++.globl des_ncbc_encrypt
++ .type des_ncbc_encrypt , @function
++des_ncbc_encrypt:
++
++ pushl %ebp
++ pushl %ebx
++ pushl %esi
++ pushl %edi
++ movl 28(%esp), %ebp
++
++ movl 36(%esp), %ebx
++ movl (%ebx), %esi
++ movl 4(%ebx), %edi
++ pushl %edi
++ pushl %esi
++ pushl %edi
++ pushl %esi
++ movl %esp, %ebx
++ movl 36(%esp), %esi
++ movl 40(%esp), %edi
++
++ movl 56(%esp), %ecx
++
++ pushl %ecx
++
++ movl 52(%esp), %eax
++ pushl %eax
++ pushl %ebx
++ cmpl $0, %ecx
++ jz .L004decrypt
++ andl $4294967288, %ebp
++ movl 12(%esp), %eax
++ movl 16(%esp), %ebx
++ jz .L005encrypt_finish
++.L006encrypt_loop:
++ movl (%esi), %ecx
++ movl 4(%esi), %edx
++ xorl %ecx, %eax
++ xorl %edx, %ebx
++ movl %eax, 12(%esp)
++ movl %ebx, 16(%esp)
++ call des_encrypt
++ movl 12(%esp), %eax
++ movl 16(%esp), %ebx
++ movl %eax, (%edi)
++ movl %ebx, 4(%edi)
++ addl $8, %esi
++ addl $8, %edi
++ subl $8, %ebp
++ jnz .L006encrypt_loop
++.L005encrypt_finish:
++ movl 56(%esp), %ebp
++ andl $7, %ebp
++ jz .L007finish
++ xorl %ecx, %ecx
++ xorl %edx, %edx
++ movl .L008cbc_enc_jmp_table(,%ebp,4),%ebp
++ jmp *%ebp
++.L009ej7:
++ movb 6(%esi), %dh
++ sall $8, %edx
++.L010ej6:
++ movb 5(%esi), %dh
++.L011ej5:
++ movb 4(%esi), %dl
++.L012ej4:
++ movl (%esi), %ecx
++ jmp .L013ejend
++.L014ej3:
++ movb 2(%esi), %ch
++ sall $8, %ecx
++.L015ej2:
++ movb 1(%esi), %ch
++.L016ej1:
++ movb (%esi), %cl
++.L013ejend:
++ xorl %ecx, %eax
++ xorl %edx, %ebx
++ movl %eax, 12(%esp)
++ movl %ebx, 16(%esp)
++ call des_encrypt
++ movl 12(%esp), %eax
++ movl 16(%esp), %ebx
++ movl %eax, (%edi)
++ movl %ebx, 4(%edi)
++ jmp .L007finish
++.align 16
++.L004decrypt:
++ andl $4294967288, %ebp
++ movl 20(%esp), %eax
++ movl 24(%esp), %ebx
++ jz .L017decrypt_finish
++.L018decrypt_loop:
++ movl (%esi), %eax
++ movl 4(%esi), %ebx
++ movl %eax, 12(%esp)
++ movl %ebx, 16(%esp)
++ call des_encrypt
++ movl 12(%esp), %eax
++ movl 16(%esp), %ebx
++ movl 20(%esp), %ecx
++ movl 24(%esp), %edx
++ xorl %eax, %ecx
++ xorl %ebx, %edx
++ movl (%esi), %eax
++ movl 4(%esi), %ebx
++ movl %ecx, (%edi)
++ movl %edx, 4(%edi)
++ movl %eax, 20(%esp)
++ movl %ebx, 24(%esp)
++ addl $8, %esi
++ addl $8, %edi
++ subl $8, %ebp
++ jnz .L018decrypt_loop
++.L017decrypt_finish:
++ movl 56(%esp), %ebp
++ andl $7, %ebp
++ jz .L007finish
++ movl (%esi), %eax
++ movl 4(%esi), %ebx
++ movl %eax, 12(%esp)
++ movl %ebx, 16(%esp)
++ call des_encrypt
++ movl 12(%esp), %eax
++ movl 16(%esp), %ebx
++ movl 20(%esp), %ecx
++ movl 24(%esp), %edx
++ xorl %eax, %ecx
++ xorl %ebx, %edx
++ movl (%esi), %eax
++ movl 4(%esi), %ebx
++.L019dj7:
++ rorl $16, %edx
++ movb %dl, 6(%edi)
++ shrl $16, %edx
++.L020dj6:
++ movb %dh, 5(%edi)
++.L021dj5:
++ movb %dl, 4(%edi)
++.L022dj4:
++ movl %ecx, (%edi)
++ jmp .L023djend
++.L024dj3:
++ rorl $16, %ecx
++ movb %cl, 2(%edi)
++ sall $16, %ecx
++.L025dj2:
++ movb %ch, 1(%esi)
++.L026dj1:
++ movb %cl, (%esi)
++.L023djend:
++ jmp .L007finish
++.align 16
++.L007finish:
++ movl 64(%esp), %ecx
++ addl $28, %esp
++ movl %eax, (%ecx)
++ movl %ebx, 4(%ecx)
++ popl %edi
++ popl %esi
++ popl %ebx
++ popl %ebp
++ ret
++.align 16
++.L008cbc_enc_jmp_table:
++ .long 0
++ .long .L016ej1
++ .long .L015ej2
++ .long .L014ej3
++ .long .L012ej4
++ .long .L011ej5
++ .long .L010ej6
++ .long .L009ej7
++.align 16
++.L027cbc_dec_jmp_table:
++ .long 0
++ .long .L026dj1
++ .long .L025dj2
++ .long .L024dj3
++ .long .L022dj4
++ .long .L021dj5
++ .long .L020dj6
++ .long .L019dj7
++.des_ncbc_encrypt_end:
++ .size des_ncbc_encrypt , .des_ncbc_encrypt_end-des_ncbc_encrypt
++.ident "desasm.pl"
++.text
++ .align 16
++.globl des_ede3_cbc_encrypt
++ .type des_ede3_cbc_encrypt , @function
++des_ede3_cbc_encrypt:
++
++ pushl %ebp
++ pushl %ebx
++ pushl %esi
++ pushl %edi
++ movl 28(%esp), %ebp
++
++ movl 44(%esp), %ebx
++ movl (%ebx), %esi
++ movl 4(%ebx), %edi
++ pushl %edi
++ pushl %esi
++ pushl %edi
++ pushl %esi
++ movl %esp, %ebx
++ movl 36(%esp), %esi
++ movl 40(%esp), %edi
++
++ movl 64(%esp), %ecx
++
++ movl 56(%esp), %eax
++ pushl %eax
++
++ movl 56(%esp), %eax
++ pushl %eax
++
++ movl 56(%esp), %eax
++ pushl %eax
++ pushl %ebx
++ cmpl $0, %ecx
++ jz .L028decrypt
++ andl $4294967288, %ebp
++ movl 16(%esp), %eax
++ movl 20(%esp), %ebx
++ jz .L029encrypt_finish
++.L030encrypt_loop:
++ movl (%esi), %ecx
++ movl 4(%esi), %edx
++ xorl %ecx, %eax
++ xorl %edx, %ebx
++ movl %eax, 16(%esp)
++ movl %ebx, 20(%esp)
++ call des_encrypt3
++ movl 16(%esp), %eax
++ movl 20(%esp), %ebx
++ movl %eax, (%edi)
++ movl %ebx, 4(%edi)
++ addl $8, %esi
++ addl $8, %edi
++ subl $8, %ebp
++ jnz .L030encrypt_loop
++.L029encrypt_finish:
++ movl 60(%esp), %ebp
++ andl $7, %ebp
++ jz .L031finish
++ xorl %ecx, %ecx
++ xorl %edx, %edx
++ movl .L032cbc_enc_jmp_table(,%ebp,4),%ebp
++ jmp *%ebp
++.L033ej7:
++ movb 6(%esi), %dh
++ sall $8, %edx
++.L034ej6:
++ movb 5(%esi), %dh
++.L035ej5:
++ movb 4(%esi), %dl
++.L036ej4:
++ movl (%esi), %ecx
++ jmp .L037ejend
++.L038ej3:
++ movb 2(%esi), %ch
++ sall $8, %ecx
++.L039ej2:
++ movb 1(%esi), %ch
++.L040ej1:
++ movb (%esi), %cl
++.L037ejend:
++ xorl %ecx, %eax
++ xorl %edx, %ebx
++ movl %eax, 16(%esp)
++ movl %ebx, 20(%esp)
++ call des_encrypt3
++ movl 16(%esp), %eax
++ movl 20(%esp), %ebx
++ movl %eax, (%edi)
++ movl %ebx, 4(%edi)
++ jmp .L031finish
++.align 16
++.L028decrypt:
++ andl $4294967288, %ebp
++ movl 24(%esp), %eax
++ movl 28(%esp), %ebx
++ jz .L041decrypt_finish
++.L042decrypt_loop:
++ movl (%esi), %eax
++ movl 4(%esi), %ebx
++ movl %eax, 16(%esp)
++ movl %ebx, 20(%esp)
++ call des_decrypt3
++ movl 16(%esp), %eax
++ movl 20(%esp), %ebx
++ movl 24(%esp), %ecx
++ movl 28(%esp), %edx
++ xorl %eax, %ecx
++ xorl %ebx, %edx
++ movl (%esi), %eax
++ movl 4(%esi), %ebx
++ movl %ecx, (%edi)
++ movl %edx, 4(%edi)
++ movl %eax, 24(%esp)
++ movl %ebx, 28(%esp)
++ addl $8, %esi
++ addl $8, %edi
++ subl $8, %ebp
++ jnz .L042decrypt_loop
++.L041decrypt_finish:
++ movl 60(%esp), %ebp
++ andl $7, %ebp
++ jz .L031finish
++ movl (%esi), %eax
++ movl 4(%esi), %ebx
++ movl %eax, 16(%esp)
++ movl %ebx, 20(%esp)
++ call des_decrypt3
++ movl 16(%esp), %eax
++ movl 20(%esp), %ebx
++ movl 24(%esp), %ecx
++ movl 28(%esp), %edx
++ xorl %eax, %ecx
++ xorl %ebx, %edx
++ movl (%esi), %eax
++ movl 4(%esi), %ebx
++.L043dj7:
++ rorl $16, %edx
++ movb %dl, 6(%edi)
++ shrl $16, %edx
++.L044dj6:
++ movb %dh, 5(%edi)
++.L045dj5:
++ movb %dl, 4(%edi)
++.L046dj4:
++ movl %ecx, (%edi)
++ jmp .L047djend
++.L048dj3:
++ rorl $16, %ecx
++ movb %cl, 2(%edi)
++ sall $16, %ecx
++.L049dj2:
++ movb %ch, 1(%esi)
++.L050dj1:
++ movb %cl, (%esi)
++.L047djend:
++ jmp .L031finish
++.align 16
++.L031finish:
++ movl 76(%esp), %ecx
++ addl $32, %esp
++ movl %eax, (%ecx)
++ movl %ebx, 4(%ecx)
++ popl %edi
++ popl %esi
++ popl %ebx
++ popl %ebp
++ ret
++.align 16
++.L032cbc_enc_jmp_table:
++ .long 0
++ .long .L040ej1
++ .long .L039ej2
++ .long .L038ej3
++ .long .L036ej4
++ .long .L035ej5
++ .long .L034ej6
++ .long .L033ej7
++.align 16
++.L051cbc_dec_jmp_table:
++ .long 0
++ .long .L050dj1
++ .long .L049dj2
++ .long .L048dj3
++ .long .L046dj4
++ .long .L045dj5
++ .long .L044dj6
++ .long .L043dj7
++.des_ede3_cbc_encrypt_end:
++ .size des_ede3_cbc_encrypt , .des_ede3_cbc_encrypt_end-des_ede3_cbc_encrypt
++.ident "desasm.pl"
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/crypto/ciphers/des/ecb_enc.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,128 @@
++/* crypto/des/ecb_enc.c */
++/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com)
++ * All rights reserved.
++ *
++ * This package is an SSL implementation written
++ * by Eric Young (eay@cryptsoft.com).
++ * The implementation was written so as to conform with Netscapes SSL.
++ *
++ * This library is free for commercial and non-commercial use as long as
++ * the following conditions are aheared to. The following conditions
++ * apply to all code found in this distribution, be it the RC4, RSA,
++ * lhash, DES, etc., code; not just the SSL code. The SSL documentation
++ * included with this distribution is covered by the same copyright terms
++ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
++ *
++ * Copyright remains Eric Young's, and as such any Copyright notices in
++ * the code are not to be removed.
++ * If this package is used in a product, Eric Young should be given attribution
++ * as the author of the parts of the library used.
++ * This can be in the form of a textual message at program startup or
++ * in documentation (online or textual) provided with the package.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ * 1. Redistributions of source code must retain the copyright
++ * notice, this list of conditions and the following disclaimer.
++ * 2. Redistributions in binary form must reproduce the above copyright
++ * notice, this list of conditions and the following disclaimer in the
++ * documentation and/or other materials provided with the distribution.
++ * 3. All advertising materials mentioning features or use of this software
++ * must display the following acknowledgement:
++ * "This product includes cryptographic software written by
++ * Eric Young (eay@cryptsoft.com)"
++ * The word 'cryptographic' can be left out if the rouines from the library
++ * being used are not cryptographic related :-).
++ * 4. If you include any Windows specific code (or a derivative thereof) from
++ * the apps directory (application code) you must include an acknowledgement:
++ * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
++ *
++ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ *
++ * The licence and distribution terms for any publically available version or
++ * derivative of this code cannot be changed. i.e. this code cannot simply be
++ * copied and put under another distribution licence
++ * [including the GNU Public Licence.]
++ */
++
++#include "des_locl.h"
++#include "spr.h"
++
++char *libdes_version="libdes v 3.24 - 20-Apr-1996 - eay";
++char *DES_version="DES part of SSLeay 0.8.2b 08-Jan-1998";
++
++/* RCSID $Id$ */
++/* This function ifdef'ed out for FreeS/WAN project. */
++#ifdef notdef
++char *des_options()
++ {
++ static int init=1;
++ static char buf[32];
++
++ if (init)
++ {
++ char *ptr,*unroll,*risc,*size;
++
++ init=0;
++#ifdef DES_PTR
++ ptr="ptr";
++#else
++ ptr="idx";
++#endif
++#if defined(DES_RISC1) || defined(DES_RISC2)
++#ifdef DES_RISC1
++ risc="risc1";
++#endif
++#ifdef DES_RISC2
++ risc="risc2";
++#endif
++#else
++ risc="cisc";
++#endif
++#ifdef DES_UNROLL
++ unroll="16";
++#else
++ unroll="4";
++#endif
++ if (sizeof(DES_LONG) != sizeof(long))
++ size="int";
++ else
++ size="long";
++ sprintf(buf,"des(%s,%s,%s,%s)",ptr,risc,unroll,size);
++ }
++ return(buf);
++ }
++#endif
++
++
++void des_ecb_encrypt(input, output, ks, enc)
++des_cblock (*input);
++des_cblock (*output);
++des_key_schedule ks;
++int enc;
++ {
++ register DES_LONG l;
++ register unsigned char *in,*out;
++ DES_LONG ll[2];
++
++ in=(unsigned char *)input;
++ out=(unsigned char *)output;
++ c2l(in,l); ll[0]=l;
++ c2l(in,l); ll[1]=l;
++ des_encrypt(ll,ks,enc);
++ l=ll[0]; l2c(l,out);
++ l=ll[1]; l2c(l,out);
++ l=ll[0]=ll[1]=0;
++ }
++
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/crypto/ciphers/des/fcrypt.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,152 @@
++/* NOCW */
++
++/* This version of crypt has been developed from my MIT compatable
++ * DES library.
++ * The library is available at pub/Crypto/DES at ftp.psy.uq.oz.au
++ * Eric Young (eay@cryptsoft.com)
++ */
++
++/* Modification by Jens Kupferschmidt (Cu)
++ * I have included directive PARA for shared memory computers.
++ * I have included a directive LONGCRYPT to using this routine to cipher
++ * passwords with more then 8 bytes like HP-UX 10.x it used. The MAXPLEN
++ * definition is the maximum of lenght of password and can changed. I have
++ * defined 24.
++ */
++
++#include "des_locl.h"
++
++/* Added more values to handle illegal salt values the way normal
++ * crypt() implementations do. The patch was sent by
++ * Bjorn Gronvall <bg@sics.se>
++ */
++static unsigned const char con_salt[128]={
++0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,
++0xDA,0xDB,0xDC,0xDD,0xDE,0xDF,0xE0,0xE1,
++0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,
++0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,
++0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,
++0xFA,0xFB,0xFC,0xFD,0xFE,0xFF,0x00,0x01,
++0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,
++0x0A,0x0B,0x05,0x06,0x07,0x08,0x09,0x0A,
++0x0B,0x0C,0x0D,0x0E,0x0F,0x10,0x11,0x12,
++0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,
++0x1B,0x1C,0x1D,0x1E,0x1F,0x20,0x21,0x22,
++0x23,0x24,0x25,0x20,0x21,0x22,0x23,0x24,
++0x25,0x26,0x27,0x28,0x29,0x2A,0x2B,0x2C,
++0x2D,0x2E,0x2F,0x30,0x31,0x32,0x33,0x34,
++0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,
++0x3D,0x3E,0x3F,0x40,0x41,0x42,0x43,0x44,
++};
++
++static unsigned const char cov_2char[64]={
++0x2E,0x2F,0x30,0x31,0x32,0x33,0x34,0x35,
++0x36,0x37,0x38,0x39,0x41,0x42,0x43,0x44,
++0x45,0x46,0x47,0x48,0x49,0x4A,0x4B,0x4C,
++0x4D,0x4E,0x4F,0x50,0x51,0x52,0x53,0x54,
++0x55,0x56,0x57,0x58,0x59,0x5A,0x61,0x62,
++0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6A,
++0x6B,0x6C,0x6D,0x6E,0x6F,0x70,0x71,0x72,
++0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7A
++};
++
++#ifndef NOPROTO
++void fcrypt_body(DES_LONG *out,des_key_schedule ks,
++ DES_LONG Eswap0, DES_LONG Eswap1);
++
++#ifdef PERL5
++char *des_crypt(const char *buf,const char *salt);
++#else
++char *crypt(const char *buf,const char *salt);
++#endif
++#else
++void fcrypt_body();
++#ifdef PERL5
++char *des_crypt();
++#else
++char *crypt();
++#endif
++#endif
++
++#ifdef PERL5
++char *des_crypt(buf,salt)
++#else
++char *crypt(buf,salt)
++#endif
++const char *buf;
++const char *salt;
++ {
++ static char buff[14];
++
++ return(des_fcrypt(buf,salt,buff));
++ }
++
++
++char *des_fcrypt(buf,salt,ret)
++const char *buf;
++const char *salt;
++char *ret;
++ {
++ unsigned int i,j,x,y;
++ DES_LONG Eswap0,Eswap1;
++ DES_LONG out[2],ll;
++ des_cblock key;
++ des_key_schedule ks;
++ unsigned char bb[9];
++ unsigned char *b=bb;
++ unsigned char c,u;
++
++ /* eay 25/08/92
++ * If you call crypt("pwd","*") as often happens when you
++ * have * as the pwd field in /etc/passwd, the function
++ * returns *\0XXXXXXXXX
++ * The \0 makes the string look like * so the pwd "*" would
++ * crypt to "*". This was found when replacing the crypt in
++ * our shared libraries. People found that the disbled
++ * accounts effectivly had no passwd :-(. */
++ x=ret[0]=((salt[0] == '\0')?'A':salt[0]);
++ Eswap0=con_salt[x]<<2;
++ x=ret[1]=((salt[1] == '\0')?'A':salt[1]);
++ Eswap1=con_salt[x]<<6;
++
++/* EAY
++r=strlen(buf);
++r=(r+7)/8;
++*/
++ for (i=0; i<8; i++)
++ {
++ c= *(buf++);
++ if (!c) break;
++ key[i]=(c<<1);
++ }
++ for (; i<8; i++)
++ key[i]=0;
++
++ des_set_key((des_cblock *)(key),ks);
++ fcrypt_body(&(out[0]),ks,Eswap0,Eswap1);
++
++ ll=out[0]; l2c(ll,b);
++ ll=out[1]; l2c(ll,b);
++ y=0;
++ u=0x80;
++ bb[8]=0;
++ for (i=2; i<13; i++)
++ {
++ c=0;
++ for (j=0; j<6; j++)
++ {
++ c<<=1;
++ if (bb[y] & u) c|=1;
++ u>>=1;
++ if (!u)
++ {
++ y++;
++ u=0x80;
++ }
++ }
++ ret[i]=cov_2char[c];
++ }
++ ret[13]='\0';
++ return(ret);
++ }
++
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/crypto/ciphers/des/fcrypt_b.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,148 @@
++/* crypto/des/fcrypt_b.c */
++/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com)
++ * All rights reserved.
++ *
++ * This package is an SSL implementation written
++ * by Eric Young (eay@cryptsoft.com).
++ * The implementation was written so as to conform with Netscapes SSL.
++ *
++ * This library is free for commercial and non-commercial use as long as
++ * the following conditions are aheared to. The following conditions
++ * apply to all code found in this distribution, be it the RC4, RSA,
++ * lhash, DES, etc., code; not just the SSL code. The SSL documentation
++ * included with this distribution is covered by the same copyright terms
++ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
++ *
++ * Copyright remains Eric Young's, and as such any Copyright notices in
++ * the code are not to be removed.
++ * If this package is used in a product, Eric Young should be given attribution
++ * as the author of the parts of the library used.
++ * This can be in the form of a textual message at program startup or
++ * in documentation (online or textual) provided with the package.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ * 1. Redistributions of source code must retain the copyright
++ * notice, this list of conditions and the following disclaimer.
++ * 2. Redistributions in binary form must reproduce the above copyright
++ * notice, this list of conditions and the following disclaimer in the
++ * documentation and/or other materials provided with the distribution.
++ * 3. All advertising materials mentioning features or use of this software
++ * must display the following acknowledgement:
++ * "This product includes cryptographic software written by
++ * Eric Young (eay@cryptsoft.com)"
++ * The word 'cryptographic' can be left out if the rouines from the library
++ * being used are not cryptographic related :-).
++ * 4. If you include any Windows specific code (or a derivative thereof) from
++ * the apps directory (application code) you must include an acknowledgement:
++ * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
++ *
++ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ *
++ * The licence and distribution terms for any publically available version or
++ * derivative of this code cannot be changed. i.e. this code cannot simply be
++ * copied and put under another distribution licence
++ * [including the GNU Public Licence.]
++ */
++
++/* #include <stdio.h> */
++
++/* This version of crypt has been developed from my MIT compatable
++ * DES library.
++ * The library is available at pub/Crypto/DES at ftp.psy.uq.oz.au
++ * Eric Young (eay@cryptsoft.com)
++ */
++
++#define DES_FCRYPT
++#include "des_locl.h"
++#undef DES_FCRYPT
++
++#undef PERM_OP
++#define PERM_OP(a,b,t,n,m) ((t)=((((a)>>(n))^(b))&(m)),\
++ (b)^=(t),\
++ (a)^=((t)<<(n)))
++
++#undef HPERM_OP
++#define HPERM_OP(a,t,n,m) ((t)=((((a)<<(16-(n)))^(a))&(m)),\
++ (a)=(a)^(t)^(t>>(16-(n))))\
++
++void fcrypt_body(out, ks, Eswap0, Eswap1)
++DES_LONG *out;
++des_key_schedule ks;
++DES_LONG Eswap0;
++DES_LONG Eswap1;
++ {
++ register DES_LONG l,r,t,u;
++#ifdef DES_PTR
++ register unsigned char *des_SP=(unsigned char *)des_SPtrans;
++#endif
++ register DES_LONG *s;
++ register int j;
++ register DES_LONG E0,E1;
++
++ l=0;
++ r=0;
++
++ s=(DES_LONG *)ks;
++ E0=Eswap0;
++ E1=Eswap1;
++
++ for (j=0; j<25; j++)
++ {
++#ifdef DES_UNROLL
++ register int i;
++
++ for (i=0; i<32; i+=8)
++ {
++ D_ENCRYPT(l,r,i+0); /* 1 */
++ D_ENCRYPT(r,l,i+2); /* 2 */
++ D_ENCRYPT(l,r,i+4); /* 1 */
++ D_ENCRYPT(r,l,i+6); /* 2 */
++ }
++#else
++ D_ENCRYPT(l,r, 0); /* 1 */
++ D_ENCRYPT(r,l, 2); /* 2 */
++ D_ENCRYPT(l,r, 4); /* 3 */
++ D_ENCRYPT(r,l, 6); /* 4 */
++ D_ENCRYPT(l,r, 8); /* 5 */
++ D_ENCRYPT(r,l,10); /* 6 */
++ D_ENCRYPT(l,r,12); /* 7 */
++ D_ENCRYPT(r,l,14); /* 8 */
++ D_ENCRYPT(l,r,16); /* 9 */
++ D_ENCRYPT(r,l,18); /* 10 */
++ D_ENCRYPT(l,r,20); /* 11 */
++ D_ENCRYPT(r,l,22); /* 12 */
++ D_ENCRYPT(l,r,24); /* 13 */
++ D_ENCRYPT(r,l,26); /* 14 */
++ D_ENCRYPT(l,r,28); /* 15 */
++ D_ENCRYPT(r,l,30); /* 16 */
++#endif
++
++ t=l;
++ l=r;
++ r=t;
++ }
++ l=ROTATE(l,3)&0xffffffffL;
++ r=ROTATE(r,3)&0xffffffffL;
++
++ PERM_OP(l,r,t, 1,0x55555555L);
++ PERM_OP(r,l,t, 8,0x00ff00ffL);
++ PERM_OP(l,r,t, 2,0x33333333L);
++ PERM_OP(r,l,t,16,0x0000ffffL);
++ PERM_OP(l,r,t, 4,0x0f0f0f0fL);
++
++ out[0]=r;
++ out[1]=l;
++ }
++
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/crypto/ciphers/des/options.txt Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,39 @@
++Note that the UNROLL option makes the 'inner' des loop unroll all 16 rounds
++instead of the default 4.
++RISC1 and RISC2 are 2 alternatives for the inner loop and
++PTR means to use pointers arithmatic instead of arrays.
++
++FreeBSD - Pentium Pro 200mhz - gcc 2.7.2.2 - assembler 577,000 4620k/s
++IRIX 6.2 - R10000 195mhz - cc (-O3 -n32) - UNROLL RISC2 PTR 496,000 3968k/s
++solaris 2.5.1 usparc 167mhz?? - SC4.0 - UNROLL RISC1 PTR [1] 459,400 3672k/s
++FreeBSD - Pentium Pro 200mhz - gcc 2.7.2.2 - UNROLL RISC1 433,000 3468k/s
++solaris 2.5.1 usparc 167mhz?? - gcc 2.7.2 - UNROLL 380,000 3041k/s
++linux - pentium 100mhz - gcc 2.7.0 - assembler 281,000 2250k/s
++NT 4.0 - pentium 100mhz - VC 4.2 - assembler 281,000 2250k/s
++AIX 4.1? - PPC604 100mhz - cc - UNROLL 275,000 2200k/s
++IRIX 5.3 - R4400 200mhz - gcc 2.6.3 - UNROLL RISC2 PTR 235,300 1882k/s
++IRIX 5.3 - R4400 200mhz - cc - UNROLL RISC2 PTR 233,700 1869k/s
++NT 4.0 - pentium 100mhz - VC 4.2 - UNROLL RISC1 PTR 191,000 1528k/s
++DEC Alpha 165mhz?? - cc - RISC2 PTR [2] 181,000 1448k/s
++linux - pentium 100mhz - gcc 2.7.0 - UNROLL RISC1 PTR 158,500 1268k/s
++HPUX 10 - 9000/887 - cc - UNROLL [3] 148,000 1190k/s
++solaris 2.5.1 - sparc 10 50mhz - gcc 2.7.2 - UNROLL 123,600 989k/s
++IRIX 5.3 - R4000 100mhz - cc - UNROLL RISC2 PTR 101,000 808k/s
++DGUX - 88100 50mhz(?) - gcc 2.6.3 - UNROLL 81,000 648k/s
++solaris 2.4 486 50mhz - gcc 2.6.3 - assembler 65,000 522k/s
++HPUX 10 - 9000/887 - k&r cc (default compiler) - UNROLL PTR 76,000 608k/s
++solaris 2.4 486 50mhz - gcc 2.6.3 - UNROLL RISC2 43,500 344k/s
++AIX - old slow one :-) - cc - 39,000 312k/s
++
++Notes.
++[1] For the ultra sparc, SunC 4.0
++ cc -xtarget=ultra -xarch=v8plus -Xa -xO5, running 'des_opts'
++ gives a speed of 344,000 des/s while 'speed' gives 459,000 des/s.
++ I'll record the higher since it is coming from the library but it
++ is all rather weird.
++[2] Similar to the ultra sparc ([1]), 181,000 for 'des_opts' vs 175,000.
++[3] I was unable to get access to this machine when it was not heavily loaded.
++ As such, my timing program was never able to get more that %30 of the CPU.
++ This would cause the program to give much lower speed numbers because
++ it would be 'fighting' to stay in the cache with the other CPU burning
++ processes.
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/crypto/ciphers/des/podd.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,75 @@
++/* crypto/des/podd.h */
++/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com)
++ * All rights reserved.
++ *
++ * This package is an SSL implementation written
++ * by Eric Young (eay@cryptsoft.com).
++ * The implementation was written so as to conform with Netscapes SSL.
++ *
++ * This library is free for commercial and non-commercial use as long as
++ * the following conditions are aheared to. The following conditions
++ * apply to all code found in this distribution, be it the RC4, RSA,
++ * lhash, DES, etc., code; not just the SSL code. The SSL documentation
++ * included with this distribution is covered by the same copyright terms
++ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
++ *
++ * Copyright remains Eric Young's, and as such any Copyright notices in
++ * the code are not to be removed.
++ * If this package is used in a product, Eric Young should be given attribution
++ * as the author of the parts of the library used.
++ * This can be in the form of a textual message at program startup or
++ * in documentation (online or textual) provided with the package.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ * 1. Redistributions of source code must retain the copyright
++ * notice, this list of conditions and the following disclaimer.
++ * 2. Redistributions in binary form must reproduce the above copyright
++ * notice, this list of conditions and the following disclaimer in the
++ * documentation and/or other materials provided with the distribution.
++ * 3. All advertising materials mentioning features or use of this software
++ * must display the following acknowledgement:
++ * "This product includes cryptographic software written by
++ * Eric Young (eay@cryptsoft.com)"
++ * The word 'cryptographic' can be left out if the rouines from the library
++ * being used are not cryptographic related :-).
++ * 4. If you include any Windows specific code (or a derivative thereof) from
++ * the apps directory (application code) you must include an acknowledgement:
++ * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
++ *
++ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ *
++ * The licence and distribution terms for any publically available version or
++ * derivative of this code cannot be changed. i.e. this code cannot simply be
++ * copied and put under another distribution licence
++ * [including the GNU Public Licence.]
++ */
++
++static const unsigned char odd_parity[256]={
++ 1, 1, 2, 2, 4, 4, 7, 7, 8, 8, 11, 11, 13, 13, 14, 14,
++ 16, 16, 19, 19, 21, 21, 22, 22, 25, 25, 26, 26, 28, 28, 31, 31,
++ 32, 32, 35, 35, 37, 37, 38, 38, 41, 41, 42, 42, 44, 44, 47, 47,
++ 49, 49, 50, 50, 52, 52, 55, 55, 56, 56, 59, 59, 61, 61, 62, 62,
++ 64, 64, 67, 67, 69, 69, 70, 70, 73, 73, 74, 74, 76, 76, 79, 79,
++ 81, 81, 82, 82, 84, 84, 87, 87, 88, 88, 91, 91, 93, 93, 94, 94,
++ 97, 97, 98, 98,100,100,103,103,104,104,107,107,109,109,110,110,
++112,112,115,115,117,117,118,118,121,121,122,122,124,124,127,127,
++128,128,131,131,133,133,134,134,137,137,138,138,140,140,143,143,
++145,145,146,146,148,148,151,151,152,152,155,155,157,157,158,158,
++161,161,162,162,164,164,167,167,168,168,171,171,173,173,174,174,
++176,176,179,179,181,181,182,182,185,185,186,186,188,188,191,191,
++193,193,194,194,196,196,199,199,200,200,203,203,205,205,206,206,
++208,208,211,211,213,213,214,214,217,217,218,218,220,220,223,223,
++224,224,227,227,229,229,230,230,233,233,234,234,236,236,239,239,
++241,241,242,242,244,244,247,247,248,248,251,251,253,253,254,254};
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/crypto/ciphers/des/set_key.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,246 @@
++/* crypto/des/set_key.c */
++/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com)
++ * All rights reserved.
++ *
++ * This package is an SSL implementation written
++ * by Eric Young (eay@cryptsoft.com).
++ * The implementation was written so as to conform with Netscapes SSL.
++ *
++ * This library is free for commercial and non-commercial use as long as
++ * the following conditions are aheared to. The following conditions
++ * apply to all code found in this distribution, be it the RC4, RSA,
++ * lhash, DES, etc., code; not just the SSL code. The SSL documentation
++ * included with this distribution is covered by the same copyright terms
++ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
++ *
++ * Copyright remains Eric Young's, and as such any Copyright notices in
++ * the code are not to be removed.
++ * If this package is used in a product, Eric Young should be given attribution
++ * as the author of the parts of the library used.
++ * This can be in the form of a textual message at program startup or
++ * in documentation (online or textual) provided with the package.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ * 1. Redistributions of source code must retain the copyright
++ * notice, this list of conditions and the following disclaimer.
++ * 2. Redistributions in binary form must reproduce the above copyright
++ * notice, this list of conditions and the following disclaimer in the
++ * documentation and/or other materials provided with the distribution.
++ * 3. All advertising materials mentioning features or use of this software
++ * must display the following acknowledgement:
++ * "This product includes cryptographic software written by
++ * Eric Young (eay@cryptsoft.com)"
++ * The word 'cryptographic' can be left out if the rouines from the library
++ * being used are not cryptographic related :-).
++ * 4. If you include any Windows specific code (or a derivative thereof) from
++ * the apps directory (application code) you must include an acknowledgement:
++ * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
++ *
++ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ *
++ * The licence and distribution terms for any publically available version or
++ * derivative of this code cannot be changed. i.e. this code cannot simply be
++ * copied and put under another distribution licence
++ * [including the GNU Public Licence.]
++ */
++
++/* set_key.c v 1.4 eay 24/9/91
++ * 1.4 Speed up by 400% :-)
++ * 1.3 added register declarations.
++ * 1.2 unrolled make_key_sched a bit more
++ * 1.1 added norm_expand_bits
++ * 1.0 First working version
++ */
++#include "des_locl.h"
++#include "podd.h"
++#include "sk.h"
++
++#ifndef NOPROTO
++static int check_parity(des_cblock (*key));
++#else
++static int check_parity();
++#endif
++
++int des_check_key=0;
++
++void des_set_odd_parity(key)
++des_cblock (*key);
++ {
++ int i;
++
++ for (i=0; i<DES_KEY_SZ; i++)
++ (*key)[i]=odd_parity[(*key)[i]];
++ }
++
++static int check_parity(key)
++des_cblock (*key);
++ {
++ int i;
++
++ for (i=0; i<DES_KEY_SZ; i++)
++ {
++ if ((*key)[i] != odd_parity[(*key)[i]])
++ return(0);
++ }
++ return(1);
++ }
++
++/* Weak and semi week keys as take from
++ * %A D.W. Davies
++ * %A W.L. Price
++ * %T Security for Computer Networks
++ * %I John Wiley & Sons
++ * %D 1984
++ * Many thanks to smb@ulysses.att.com (Steven Bellovin) for the reference
++ * (and actual cblock values).
++ */
++#define NUM_WEAK_KEY 16
++static des_cblock weak_keys[NUM_WEAK_KEY]={
++ /* weak keys */
++ {0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01},
++ {0xFE,0xFE,0xFE,0xFE,0xFE,0xFE,0xFE,0xFE},
++ {0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F},
++ {0xE0,0xE0,0xE0,0xE0,0xE0,0xE0,0xE0,0xE0},
++ /* semi-weak keys */
++ {0x01,0xFE,0x01,0xFE,0x01,0xFE,0x01,0xFE},
++ {0xFE,0x01,0xFE,0x01,0xFE,0x01,0xFE,0x01},
++ {0x1F,0xE0,0x1F,0xE0,0x0E,0xF1,0x0E,0xF1},
++ {0xE0,0x1F,0xE0,0x1F,0xF1,0x0E,0xF1,0x0E},
++ {0x01,0xE0,0x01,0xE0,0x01,0xF1,0x01,0xF1},
++ {0xE0,0x01,0xE0,0x01,0xF1,0x01,0xF1,0x01},
++ {0x1F,0xFE,0x1F,0xFE,0x0E,0xFE,0x0E,0xFE},
++ {0xFE,0x1F,0xFE,0x1F,0xFE,0x0E,0xFE,0x0E},
++ {0x01,0x1F,0x01,0x1F,0x01,0x0E,0x01,0x0E},
++ {0x1F,0x01,0x1F,0x01,0x0E,0x01,0x0E,0x01},
++ {0xE0,0xFE,0xE0,0xFE,0xF1,0xFE,0xF1,0xFE},
++ {0xFE,0xE0,0xFE,0xE0,0xFE,0xF1,0xFE,0xF1}};
++
++int des_is_weak_key(key)
++des_cblock (*key);
++ {
++ int i;
++
++ for (i=0; i<NUM_WEAK_KEY; i++)
++ /* Added == 0 to comparision, I obviously don't run
++ * this section very often :-(, thanks to
++ * engineering@MorningStar.Com for the fix
++ * eay 93/06/29
++ * Another problem, I was comparing only the first 4
++ * bytes, 97/03/18 */
++ if (memcmp(weak_keys[i],key,sizeof(des_cblock)) == 0) return(1);
++ return(0);
++ }
++
++/* NOW DEFINED IN des_local.h
++ * See ecb_encrypt.c for a pseudo description of these macros.
++ * #define PERM_OP(a,b,t,n,m) ((t)=((((a)>>(n))^(b))&(m)),\
++ * (b)^=(t),\
++ * (a)=((a)^((t)<<(n))))
++ */
++
++#define HPERM_OP(a,t,n,m) ((t)=((((a)<<(16-(n)))^(a))&(m)),\
++ (a)=(a)^(t)^(t>>(16-(n))))
++
++/* return 0 if key parity is odd (correct),
++ * return -1 if key parity error,
++ * return -2 if illegal weak key.
++ */
++int des_set_key(key, schedule)
++des_cblock (*key);
++des_key_schedule schedule;
++ {
++ static int shifts2[16]={0,0,1,1,1,1,1,1,0,1,1,1,1,1,1,0};
++ register DES_LONG c,d,t,s,t2;
++ register unsigned char *in;
++ register DES_LONG *k;
++ register int i;
++
++ if (des_check_key)
++ {
++ if (!check_parity(key))
++ return(-1);
++
++ if (des_is_weak_key(key))
++ return(-2);
++ }
++
++ k=(DES_LONG *)schedule;
++ in=(unsigned char *)key;
++
++ c2l(in,c);
++ c2l(in,d);
++
++ /* do PC1 in 60 simple operations */
++/* PERM_OP(d,c,t,4,0x0f0f0f0fL);
++ HPERM_OP(c,t,-2, 0xcccc0000L);
++ HPERM_OP(c,t,-1, 0xaaaa0000L);
++ HPERM_OP(c,t, 8, 0x00ff0000L);
++ HPERM_OP(c,t,-1, 0xaaaa0000L);
++ HPERM_OP(d,t,-8, 0xff000000L);
++ HPERM_OP(d,t, 8, 0x00ff0000L);
++ HPERM_OP(d,t, 2, 0x33330000L);
++ d=((d&0x00aa00aaL)<<7L)|((d&0x55005500L)>>7L)|(d&0xaa55aa55L);
++ d=(d>>8)|((c&0xf0000000L)>>4);
++ c&=0x0fffffffL; */
++
++ /* I now do it in 47 simple operations :-)
++ * Thanks to John Fletcher (john_fletcher@lccmail.ocf.llnl.gov)
++ * for the inspiration. :-) */
++ PERM_OP (d,c,t,4,0x0f0f0f0fL);
++ HPERM_OP(c,t,-2,0xcccc0000L);
++ HPERM_OP(d,t,-2,0xcccc0000L);
++ PERM_OP (d,c,t,1,0x55555555L);
++ PERM_OP (c,d,t,8,0x00ff00ffL);
++ PERM_OP (d,c,t,1,0x55555555L);
++ d= (((d&0x000000ffL)<<16L)| (d&0x0000ff00L) |
++ ((d&0x00ff0000L)>>16L)|((c&0xf0000000L)>>4L));
++ c&=0x0fffffffL;
++
++ for (i=0; i<ITERATIONS; i++)
++ {
++ if (shifts2[i])
++ { c=((c>>2L)|(c<<26L)); d=((d>>2L)|(d<<26L)); }
++ else
++ { c=((c>>1L)|(c<<27L)); d=((d>>1L)|(d<<27L)); }
++ c&=0x0fffffffL;
++ d&=0x0fffffffL;
++ /* could be a few less shifts but I am to lazy at this
++ * point in time to investigate */
++ s= des_skb[0][ (c )&0x3f ]|
++ des_skb[1][((c>> 6)&0x03)|((c>> 7L)&0x3c)]|
++ des_skb[2][((c>>13)&0x0f)|((c>>14L)&0x30)]|
++ des_skb[3][((c>>20)&0x01)|((c>>21L)&0x06) |
++ ((c>>22L)&0x38)];
++ t= des_skb[4][ (d )&0x3f ]|
++ des_skb[5][((d>> 7L)&0x03)|((d>> 8L)&0x3c)]|
++ des_skb[6][ (d>>15L)&0x3f ]|
++ des_skb[7][((d>>21L)&0x0f)|((d>>22L)&0x30)];
++
++ /* table contained 0213 4657 */
++ t2=((t<<16L)|(s&0x0000ffffL))&0xffffffffL;
++ *(k++)=ROTATE(t2,30)&0xffffffffL;
++
++ t2=((s>>16L)|(t&0xffff0000L));
++ *(k++)=ROTATE(t2,26)&0xffffffffL;
++ }
++ return(0);
++ }
++
++int des_key_sched(key, schedule)
++des_cblock (*key);
++des_key_schedule schedule;
++ {
++ return(des_set_key(key,schedule));
++ }
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/crypto/ciphers/des/sk.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,204 @@
++/* crypto/des/sk.h */
++/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com)
++ * All rights reserved.
++ *
++ * This package is an SSL implementation written
++ * by Eric Young (eay@cryptsoft.com).
++ * The implementation was written so as to conform with Netscapes SSL.
++ *
++ * This library is free for commercial and non-commercial use as long as
++ * the following conditions are aheared to. The following conditions
++ * apply to all code found in this distribution, be it the RC4, RSA,
++ * lhash, DES, etc., code; not just the SSL code. The SSL documentation
++ * included with this distribution is covered by the same copyright terms
++ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
++ *
++ * Copyright remains Eric Young's, and as such any Copyright notices in
++ * the code are not to be removed.
++ * If this package is used in a product, Eric Young should be given attribution
++ * as the author of the parts of the library used.
++ * This can be in the form of a textual message at program startup or
++ * in documentation (online or textual) provided with the package.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ * 1. Redistributions of source code must retain the copyright
++ * notice, this list of conditions and the following disclaimer.
++ * 2. Redistributions in binary form must reproduce the above copyright
++ * notice, this list of conditions and the following disclaimer in the
++ * documentation and/or other materials provided with the distribution.
++ * 3. All advertising materials mentioning features or use of this software
++ * must display the following acknowledgement:
++ * "This product includes cryptographic software written by
++ * Eric Young (eay@cryptsoft.com)"
++ * The word 'cryptographic' can be left out if the rouines from the library
++ * being used are not cryptographic related :-).
++ * 4. If you include any Windows specific code (or a derivative thereof) from
++ * the apps directory (application code) you must include an acknowledgement:
++ * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
++ *
++ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ *
++ * The licence and distribution terms for any publically available version or
++ * derivative of this code cannot be changed. i.e. this code cannot simply be
++ * copied and put under another distribution licence
++ * [including the GNU Public Licence.]
++ */
++
++static const DES_LONG des_skb[8][64]={
++{
++/* for C bits (numbered as per FIPS 46) 1 2 3 4 5 6 */
++0x00000000L,0x00000010L,0x20000000L,0x20000010L,
++0x00010000L,0x00010010L,0x20010000L,0x20010010L,
++0x00000800L,0x00000810L,0x20000800L,0x20000810L,
++0x00010800L,0x00010810L,0x20010800L,0x20010810L,
++0x00000020L,0x00000030L,0x20000020L,0x20000030L,
++0x00010020L,0x00010030L,0x20010020L,0x20010030L,
++0x00000820L,0x00000830L,0x20000820L,0x20000830L,
++0x00010820L,0x00010830L,0x20010820L,0x20010830L,
++0x00080000L,0x00080010L,0x20080000L,0x20080010L,
++0x00090000L,0x00090010L,0x20090000L,0x20090010L,
++0x00080800L,0x00080810L,0x20080800L,0x20080810L,
++0x00090800L,0x00090810L,0x20090800L,0x20090810L,
++0x00080020L,0x00080030L,0x20080020L,0x20080030L,
++0x00090020L,0x00090030L,0x20090020L,0x20090030L,
++0x00080820L,0x00080830L,0x20080820L,0x20080830L,
++0x00090820L,0x00090830L,0x20090820L,0x20090830L,
++},{
++/* for C bits (numbered as per FIPS 46) 7 8 10 11 12 13 */
++0x00000000L,0x02000000L,0x00002000L,0x02002000L,
++0x00200000L,0x02200000L,0x00202000L,0x02202000L,
++0x00000004L,0x02000004L,0x00002004L,0x02002004L,
++0x00200004L,0x02200004L,0x00202004L,0x02202004L,
++0x00000400L,0x02000400L,0x00002400L,0x02002400L,
++0x00200400L,0x02200400L,0x00202400L,0x02202400L,
++0x00000404L,0x02000404L,0x00002404L,0x02002404L,
++0x00200404L,0x02200404L,0x00202404L,0x02202404L,
++0x10000000L,0x12000000L,0x10002000L,0x12002000L,
++0x10200000L,0x12200000L,0x10202000L,0x12202000L,
++0x10000004L,0x12000004L,0x10002004L,0x12002004L,
++0x10200004L,0x12200004L,0x10202004L,0x12202004L,
++0x10000400L,0x12000400L,0x10002400L,0x12002400L,
++0x10200400L,0x12200400L,0x10202400L,0x12202400L,
++0x10000404L,0x12000404L,0x10002404L,0x12002404L,
++0x10200404L,0x12200404L,0x10202404L,0x12202404L,
++},{
++/* for C bits (numbered as per FIPS 46) 14 15 16 17 19 20 */
++0x00000000L,0x00000001L,0x00040000L,0x00040001L,
++0x01000000L,0x01000001L,0x01040000L,0x01040001L,
++0x00000002L,0x00000003L,0x00040002L,0x00040003L,
++0x01000002L,0x01000003L,0x01040002L,0x01040003L,
++0x00000200L,0x00000201L,0x00040200L,0x00040201L,
++0x01000200L,0x01000201L,0x01040200L,0x01040201L,
++0x00000202L,0x00000203L,0x00040202L,0x00040203L,
++0x01000202L,0x01000203L,0x01040202L,0x01040203L,
++0x08000000L,0x08000001L,0x08040000L,0x08040001L,
++0x09000000L,0x09000001L,0x09040000L,0x09040001L,
++0x08000002L,0x08000003L,0x08040002L,0x08040003L,
++0x09000002L,0x09000003L,0x09040002L,0x09040003L,
++0x08000200L,0x08000201L,0x08040200L,0x08040201L,
++0x09000200L,0x09000201L,0x09040200L,0x09040201L,
++0x08000202L,0x08000203L,0x08040202L,0x08040203L,
++0x09000202L,0x09000203L,0x09040202L,0x09040203L,
++},{
++/* for C bits (numbered as per FIPS 46) 21 23 24 26 27 28 */
++0x00000000L,0x00100000L,0x00000100L,0x00100100L,
++0x00000008L,0x00100008L,0x00000108L,0x00100108L,
++0x00001000L,0x00101000L,0x00001100L,0x00101100L,
++0x00001008L,0x00101008L,0x00001108L,0x00101108L,
++0x04000000L,0x04100000L,0x04000100L,0x04100100L,
++0x04000008L,0x04100008L,0x04000108L,0x04100108L,
++0x04001000L,0x04101000L,0x04001100L,0x04101100L,
++0x04001008L,0x04101008L,0x04001108L,0x04101108L,
++0x00020000L,0x00120000L,0x00020100L,0x00120100L,
++0x00020008L,0x00120008L,0x00020108L,0x00120108L,
++0x00021000L,0x00121000L,0x00021100L,0x00121100L,
++0x00021008L,0x00121008L,0x00021108L,0x00121108L,
++0x04020000L,0x04120000L,0x04020100L,0x04120100L,
++0x04020008L,0x04120008L,0x04020108L,0x04120108L,
++0x04021000L,0x04121000L,0x04021100L,0x04121100L,
++0x04021008L,0x04121008L,0x04021108L,0x04121108L,
++},{
++/* for D bits (numbered as per FIPS 46) 1 2 3 4 5 6 */
++0x00000000L,0x10000000L,0x00010000L,0x10010000L,
++0x00000004L,0x10000004L,0x00010004L,0x10010004L,
++0x20000000L,0x30000000L,0x20010000L,0x30010000L,
++0x20000004L,0x30000004L,0x20010004L,0x30010004L,
++0x00100000L,0x10100000L,0x00110000L,0x10110000L,
++0x00100004L,0x10100004L,0x00110004L,0x10110004L,
++0x20100000L,0x30100000L,0x20110000L,0x30110000L,
++0x20100004L,0x30100004L,0x20110004L,0x30110004L,
++0x00001000L,0x10001000L,0x00011000L,0x10011000L,
++0x00001004L,0x10001004L,0x00011004L,0x10011004L,
++0x20001000L,0x30001000L,0x20011000L,0x30011000L,
++0x20001004L,0x30001004L,0x20011004L,0x30011004L,
++0x00101000L,0x10101000L,0x00111000L,0x10111000L,
++0x00101004L,0x10101004L,0x00111004L,0x10111004L,
++0x20101000L,0x30101000L,0x20111000L,0x30111000L,
++0x20101004L,0x30101004L,0x20111004L,0x30111004L,
++},{
++/* for D bits (numbered as per FIPS 46) 8 9 11 12 13 14 */
++0x00000000L,0x08000000L,0x00000008L,0x08000008L,
++0x00000400L,0x08000400L,0x00000408L,0x08000408L,
++0x00020000L,0x08020000L,0x00020008L,0x08020008L,
++0x00020400L,0x08020400L,0x00020408L,0x08020408L,
++0x00000001L,0x08000001L,0x00000009L,0x08000009L,
++0x00000401L,0x08000401L,0x00000409L,0x08000409L,
++0x00020001L,0x08020001L,0x00020009L,0x08020009L,
++0x00020401L,0x08020401L,0x00020409L,0x08020409L,
++0x02000000L,0x0A000000L,0x02000008L,0x0A000008L,
++0x02000400L,0x0A000400L,0x02000408L,0x0A000408L,
++0x02020000L,0x0A020000L,0x02020008L,0x0A020008L,
++0x02020400L,0x0A020400L,0x02020408L,0x0A020408L,
++0x02000001L,0x0A000001L,0x02000009L,0x0A000009L,
++0x02000401L,0x0A000401L,0x02000409L,0x0A000409L,
++0x02020001L,0x0A020001L,0x02020009L,0x0A020009L,
++0x02020401L,0x0A020401L,0x02020409L,0x0A020409L,
++},{
++/* for D bits (numbered as per FIPS 46) 16 17 18 19 20 21 */
++0x00000000L,0x00000100L,0x00080000L,0x00080100L,
++0x01000000L,0x01000100L,0x01080000L,0x01080100L,
++0x00000010L,0x00000110L,0x00080010L,0x00080110L,
++0x01000010L,0x01000110L,0x01080010L,0x01080110L,
++0x00200000L,0x00200100L,0x00280000L,0x00280100L,
++0x01200000L,0x01200100L,0x01280000L,0x01280100L,
++0x00200010L,0x00200110L,0x00280010L,0x00280110L,
++0x01200010L,0x01200110L,0x01280010L,0x01280110L,
++0x00000200L,0x00000300L,0x00080200L,0x00080300L,
++0x01000200L,0x01000300L,0x01080200L,0x01080300L,
++0x00000210L,0x00000310L,0x00080210L,0x00080310L,
++0x01000210L,0x01000310L,0x01080210L,0x01080310L,
++0x00200200L,0x00200300L,0x00280200L,0x00280300L,
++0x01200200L,0x01200300L,0x01280200L,0x01280300L,
++0x00200210L,0x00200310L,0x00280210L,0x00280310L,
++0x01200210L,0x01200310L,0x01280210L,0x01280310L,
++},{
++/* for D bits (numbered as per FIPS 46) 22 23 24 25 27 28 */
++0x00000000L,0x04000000L,0x00040000L,0x04040000L,
++0x00000002L,0x04000002L,0x00040002L,0x04040002L,
++0x00002000L,0x04002000L,0x00042000L,0x04042000L,
++0x00002002L,0x04002002L,0x00042002L,0x04042002L,
++0x00000020L,0x04000020L,0x00040020L,0x04040020L,
++0x00000022L,0x04000022L,0x00040022L,0x04040022L,
++0x00002020L,0x04002020L,0x00042020L,0x04042020L,
++0x00002022L,0x04002022L,0x00042022L,0x04042022L,
++0x00000800L,0x04000800L,0x00040800L,0x04040800L,
++0x00000802L,0x04000802L,0x00040802L,0x04040802L,
++0x00002800L,0x04002800L,0x00042800L,0x04042800L,
++0x00002802L,0x04002802L,0x00042802L,0x04042802L,
++0x00000820L,0x04000820L,0x00040820L,0x04040820L,
++0x00000822L,0x04000822L,0x00040822L,0x04040822L,
++0x00002820L,0x04002820L,0x00042820L,0x04042820L,
++0x00002822L,0x04002822L,0x00042822L,0x04042822L,
++}};
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/crypto/ciphers/des/speed.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,329 @@
++/* crypto/des/speed.c */
++/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com)
++ * All rights reserved.
++ *
++ * This package is an SSL implementation written
++ * by Eric Young (eay@cryptsoft.com).
++ * The implementation was written so as to conform with Netscapes SSL.
++ *
++ * This library is free for commercial and non-commercial use as long as
++ * the following conditions are aheared to. The following conditions
++ * apply to all code found in this distribution, be it the RC4, RSA,
++ * lhash, DES, etc., code; not just the SSL code. The SSL documentation
++ * included with this distribution is covered by the same copyright terms
++ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
++ *
++ * Copyright remains Eric Young's, and as such any Copyright notices in
++ * the code are not to be removed.
++ * If this package is used in a product, Eric Young should be given attribution
++ * as the author of the parts of the library used.
++ * This can be in the form of a textual message at program startup or
++ * in documentation (online or textual) provided with the package.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ * 1. Redistributions of source code must retain the copyright
++ * notice, this list of conditions and the following disclaimer.
++ * 2. Redistributions in binary form must reproduce the above copyright
++ * notice, this list of conditions and the following disclaimer in the
++ * documentation and/or other materials provided with the distribution.
++ * 3. All advertising materials mentioning features or use of this software
++ * must display the following acknowledgement:
++ * "This product includes cryptographic software written by
++ * Eric Young (eay@cryptsoft.com)"
++ * The word 'cryptographic' can be left out if the rouines from the library
++ * being used are not cryptographic related :-).
++ * 4. If you include any Windows specific code (or a derivative thereof) from
++ * the apps directory (application code) you must include an acknowledgement:
++ * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
++ *
++ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ *
++ * The licence and distribution terms for any publically available version or
++ * derivative of this code cannot be changed. i.e. this code cannot simply be
++ * copied and put under another distribution licence
++ * [including the GNU Public Licence.]
++ */
++
++/* 11-Sep-92 Andrew Daviel Support for Silicon Graphics IRIX added */
++/* 06-Apr-92 Luke Brennan Support for VMS and add extra signal calls */
++
++#ifndef MSDOS
++#define TIMES
++#endif
++
++#include <stdio.h>
++#ifndef MSDOS
++#include <unistd.h>
++#else
++#include <io.h>
++extern int exit();
++#endif
++#include <signal.h>
++#ifndef VMS
++#ifndef _IRIX
++#include <time.h>
++#endif
++#ifdef TIMES
++#include <sys/types.h>
++#include <sys/times.h>
++#endif
++#else /* VMS */
++#include <types.h>
++struct tms {
++ time_t tms_utime;
++ time_t tms_stime;
++ time_t tms_uchild; /* I dunno... */
++ time_t tms_uchildsys; /* so these names are a guess :-) */
++ }
++#endif
++#ifndef TIMES
++#include <sys/timeb.h>
++#endif
++
++#ifdef sun
++#include <limits.h>
++#include <sys/param.h>
++#endif
++
++#include "des_locl.h"
++
++/* The following if from times(3) man page. It may need to be changed */
++#ifndef HZ
++# ifndef CLK_TCK
++# ifndef _BSD_CLK_TCK_ /* FreeBSD fix */
++# ifndef VMS
++# define HZ 100.0
++# else /* VMS */
++# define HZ 100.0
++# endif
++# else /* _BSD_CLK_TCK_ */
++# define HZ ((double)_BSD_CLK_TCK_)
++# endif
++# else /* CLK_TCK */
++# define HZ ((double)CLK_TCK)
++# endif
++#endif
++
++#define BUFSIZE ((long)1024)
++long run=0;
++
++#ifndef NOPROTO
++double Time_F(int s);
++#else
++double Time_F();
++#endif
++
++#ifdef SIGALRM
++#if defined(__STDC__) || defined(sgi) || defined(_AIX)
++#define SIGRETTYPE void
++#else
++#define SIGRETTYPE int
++#endif
++
++#ifndef NOPROTO
++SIGRETTYPE sig_done(int sig);
++#else
++SIGRETTYPE sig_done();
++#endif
++
++SIGRETTYPE sig_done(sig)
++int sig;
++ {
++ signal(SIGALRM,sig_done);
++ run=0;
++#ifdef LINT
++ sig=sig;
++#endif
++ }
++#endif
++
++#define START 0
++#define STOP 1
++
++double Time_F(s)
++int s;
++ {
++ double ret;
++#ifdef TIMES
++ static struct tms tstart,tend;
++
++ if (s == START)
++ {
++ times(&tstart);
++ return(0);
++ }
++ else
++ {
++ times(&tend);
++ ret=((double)(tend.tms_utime-tstart.tms_utime))/HZ;
++ return((ret == 0.0)?1e-6:ret);
++ }
++#else /* !times() */
++ static struct timeb tstart,tend;
++ long i;
++
++ if (s == START)
++ {
++ ftime(&tstart);
++ return(0);
++ }
++ else
++ {
++ ftime(&tend);
++ i=(long)tend.millitm-(long)tstart.millitm;
++ ret=((double)(tend.time-tstart.time))+((double)i)/1e3;
++ return((ret == 0.0)?1e-6:ret);
++ }
++#endif
++ }
++
++int main(argc,argv)
++int argc;
++char **argv;
++ {
++ long count;
++ static unsigned char buf[BUFSIZE];
++ static des_cblock key ={0x12,0x34,0x56,0x78,0x9a,0xbc,0xde,0xf0};
++ static des_cblock key2={0x34,0x56,0x78,0x9a,0xbc,0xde,0xf0,0x12};
++ static des_cblock key3={0x56,0x78,0x9a,0xbc,0xde,0xf0,0x12,0x34};
++ des_key_schedule sch,sch2,sch3;
++ double a,b,c,d,e;
++#ifndef SIGALRM
++ long ca,cb,cc,cd,ce;
++#endif
++
++#ifndef TIMES
++ printf("To get the most acurate results, try to run this\n");
++ printf("program when this computer is idle.\n");
++#endif
++
++ des_set_key((C_Block *)key2,sch2);
++ des_set_key((C_Block *)key3,sch3);
++
++#ifndef SIGALRM
++ printf("First we calculate the approximate speed ...\n");
++ des_set_key((C_Block *)key,sch);
++ count=10;
++ do {
++ long i;
++ DES_LONG data[2];
++
++ count*=2;
++ Time_F(START);
++ for (i=count; i; i--)
++ des_encrypt(data,&(sch[0]),DES_ENCRYPT);
++ d=Time_F(STOP);
++ } while (d < 3.0);
++ ca=count;
++ cb=count*3;
++ cc=count*3*8/BUFSIZE+1;
++ cd=count*8/BUFSIZE+1;
++ ce=count/20+1;
++ printf("Doing set_key %ld times\n",ca);
++#define COND(d) (count != (d))
++#define COUNT(d) (d)
++#else
++#define COND(c) (run)
++#define COUNT(d) (count)
++ signal(SIGALRM,sig_done);
++ printf("Doing set_key for 10 seconds\n");
++ alarm(10);
++#endif
++
++ Time_F(START);
++ for (count=0,run=1; COND(ca); count++)
++ des_set_key((C_Block *)key,sch);
++ d=Time_F(STOP);
++ printf("%ld set_key's in %.2f seconds\n",count,d);
++ a=((double)COUNT(ca))/d;
++
++#ifdef SIGALRM
++ printf("Doing des_encrypt's for 10 seconds\n");
++ alarm(10);
++#else
++ printf("Doing des_encrypt %ld times\n",cb);
++#endif
++ Time_F(START);
++ for (count=0,run=1; COND(cb); count++)
++ {
++ DES_LONG data[2];
++
++ des_encrypt(data,&(sch[0]),DES_ENCRYPT);
++ }
++ d=Time_F(STOP);
++ printf("%ld des_encrypt's in %.2f second\n",count,d);
++ b=((double)COUNT(cb)*8)/d;
++
++#ifdef SIGALRM
++ printf("Doing des_cbc_encrypt on %ld byte blocks for 10 seconds\n",
++ BUFSIZE);
++ alarm(10);
++#else
++ printf("Doing des_cbc_encrypt %ld times on %ld byte blocks\n",cc,
++ BUFSIZE);
++#endif
++ Time_F(START);
++ for (count=0,run=1; COND(cc); count++)
++ des_ncbc_encrypt((C_Block *)buf,(C_Block *)buf,BUFSIZE,&(sch[0]),
++ (C_Block *)&(key[0]),DES_ENCRYPT);
++ d=Time_F(STOP);
++ printf("%ld des_cbc_encrypt's of %ld byte blocks in %.2f second\n",
++ count,BUFSIZE,d);
++ c=((double)COUNT(cc)*BUFSIZE)/d;
++
++#ifdef SIGALRM
++ printf("Doing des_ede_cbc_encrypt on %ld byte blocks for 10 seconds\n",
++ BUFSIZE);
++ alarm(10);
++#else
++ printf("Doing des_ede_cbc_encrypt %ld times on %ld byte blocks\n",cd,
++ BUFSIZE);
++#endif
++ Time_F(START);
++ for (count=0,run=1; COND(cd); count++)
++ des_ede3_cbc_encrypt((C_Block *)buf,(C_Block *)buf,BUFSIZE,
++ &(sch[0]),
++ &(sch2[0]),
++ &(sch3[0]),
++ (C_Block *)&(key[0]),
++ DES_ENCRYPT);
++ d=Time_F(STOP);
++ printf("%ld des_ede_cbc_encrypt's of %ld byte blocks in %.2f second\n",
++ count,BUFSIZE,d);
++ d=((double)COUNT(cd)*BUFSIZE)/d;
++
++#ifdef SIGALRM
++ printf("Doing crypt for 10 seconds\n");
++ alarm(10);
++#else
++ printf("Doing crypt %ld times\n",ce);
++#endif
++ Time_F(START);
++ for (count=0,run=1; COND(ce); count++)
++ crypt("testing1","ef");
++ e=Time_F(STOP);
++ printf("%ld crypts in %.2f second\n",count,e);
++ e=((double)COUNT(ce))/e;
++
++ printf("set_key per sec = %12.2f (%9.3fuS)\n",a,1.0e6/a);
++ printf("DES raw ecb bytes per sec = %12.2f (%9.3fuS)\n",b,8.0e6/b);
++ printf("DES cbc bytes per sec = %12.2f (%9.3fuS)\n",c,8.0e6/c);
++ printf("DES ede cbc bytes per sec = %12.2f (%9.3fuS)\n",d,8.0e6/d);
++ printf("crypt per sec = %12.2f (%9.3fuS)\n",e,1.0e6/e);
++ exit(0);
++#if defined(LINT) || defined(MSDOS)
++ return(0);
++#endif
++ }
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/crypto/ciphers/des/spr.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,204 @@
++/* crypto/des/spr.h */
++/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com)
++ * All rights reserved.
++ *
++ * This package is an SSL implementation written
++ * by Eric Young (eay@cryptsoft.com).
++ * The implementation was written so as to conform with Netscapes SSL.
++ *
++ * This library is free for commercial and non-commercial use as long as
++ * the following conditions are aheared to. The following conditions
++ * apply to all code found in this distribution, be it the RC4, RSA,
++ * lhash, DES, etc., code; not just the SSL code. The SSL documentation
++ * included with this distribution is covered by the same copyright terms
++ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
++ *
++ * Copyright remains Eric Young's, and as such any Copyright notices in
++ * the code are not to be removed.
++ * If this package is used in a product, Eric Young should be given attribution
++ * as the author of the parts of the library used.
++ * This can be in the form of a textual message at program startup or
++ * in documentation (online or textual) provided with the package.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ * 1. Redistributions of source code must retain the copyright
++ * notice, this list of conditions and the following disclaimer.
++ * 2. Redistributions in binary form must reproduce the above copyright
++ * notice, this list of conditions and the following disclaimer in the
++ * documentation and/or other materials provided with the distribution.
++ * 3. All advertising materials mentioning features or use of this software
++ * must display the following acknowledgement:
++ * "This product includes cryptographic software written by
++ * Eric Young (eay@cryptsoft.com)"
++ * The word 'cryptographic' can be left out if the rouines from the library
++ * being used are not cryptographic related :-).
++ * 4. If you include any Windows specific code (or a derivative thereof) from
++ * the apps directory (application code) you must include an acknowledgement:
++ * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
++ *
++ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ *
++ * The licence and distribution terms for any publically available version or
++ * derivative of this code cannot be changed. i.e. this code cannot simply be
++ * copied and put under another distribution licence
++ * [including the GNU Public Licence.]
++ */
++
++const DES_LONG des_SPtrans[8][64]={
++{
++/* nibble 0 */
++0x02080800L, 0x00080000L, 0x02000002L, 0x02080802L,
++0x02000000L, 0x00080802L, 0x00080002L, 0x02000002L,
++0x00080802L, 0x02080800L, 0x02080000L, 0x00000802L,
++0x02000802L, 0x02000000L, 0x00000000L, 0x00080002L,
++0x00080000L, 0x00000002L, 0x02000800L, 0x00080800L,
++0x02080802L, 0x02080000L, 0x00000802L, 0x02000800L,
++0x00000002L, 0x00000800L, 0x00080800L, 0x02080002L,
++0x00000800L, 0x02000802L, 0x02080002L, 0x00000000L,
++0x00000000L, 0x02080802L, 0x02000800L, 0x00080002L,
++0x02080800L, 0x00080000L, 0x00000802L, 0x02000800L,
++0x02080002L, 0x00000800L, 0x00080800L, 0x02000002L,
++0x00080802L, 0x00000002L, 0x02000002L, 0x02080000L,
++0x02080802L, 0x00080800L, 0x02080000L, 0x02000802L,
++0x02000000L, 0x00000802L, 0x00080002L, 0x00000000L,
++0x00080000L, 0x02000000L, 0x02000802L, 0x02080800L,
++0x00000002L, 0x02080002L, 0x00000800L, 0x00080802L,
++},{
++/* nibble 1 */
++0x40108010L, 0x00000000L, 0x00108000L, 0x40100000L,
++0x40000010L, 0x00008010L, 0x40008000L, 0x00108000L,
++0x00008000L, 0x40100010L, 0x00000010L, 0x40008000L,
++0x00100010L, 0x40108000L, 0x40100000L, 0x00000010L,
++0x00100000L, 0x40008010L, 0x40100010L, 0x00008000L,
++0x00108010L, 0x40000000L, 0x00000000L, 0x00100010L,
++0x40008010L, 0x00108010L, 0x40108000L, 0x40000010L,
++0x40000000L, 0x00100000L, 0x00008010L, 0x40108010L,
++0x00100010L, 0x40108000L, 0x40008000L, 0x00108010L,
++0x40108010L, 0x00100010L, 0x40000010L, 0x00000000L,
++0x40000000L, 0x00008010L, 0x00100000L, 0x40100010L,
++0x00008000L, 0x40000000L, 0x00108010L, 0x40008010L,
++0x40108000L, 0x00008000L, 0x00000000L, 0x40000010L,
++0x00000010L, 0x40108010L, 0x00108000L, 0x40100000L,
++0x40100010L, 0x00100000L, 0x00008010L, 0x40008000L,
++0x40008010L, 0x00000010L, 0x40100000L, 0x00108000L,
++},{
++/* nibble 2 */
++0x04000001L, 0x04040100L, 0x00000100L, 0x04000101L,
++0x00040001L, 0x04000000L, 0x04000101L, 0x00040100L,
++0x04000100L, 0x00040000L, 0x04040000L, 0x00000001L,
++0x04040101L, 0x00000101L, 0x00000001L, 0x04040001L,
++0x00000000L, 0x00040001L, 0x04040100L, 0x00000100L,
++0x00000101L, 0x04040101L, 0x00040000L, 0x04000001L,
++0x04040001L, 0x04000100L, 0x00040101L, 0x04040000L,
++0x00040100L, 0x00000000L, 0x04000000L, 0x00040101L,
++0x04040100L, 0x00000100L, 0x00000001L, 0x00040000L,
++0x00000101L, 0x00040001L, 0x04040000L, 0x04000101L,
++0x00000000L, 0x04040100L, 0x00040100L, 0x04040001L,
++0x00040001L, 0x04000000L, 0x04040101L, 0x00000001L,
++0x00040101L, 0x04000001L, 0x04000000L, 0x04040101L,
++0x00040000L, 0x04000100L, 0x04000101L, 0x00040100L,
++0x04000100L, 0x00000000L, 0x04040001L, 0x00000101L,
++0x04000001L, 0x00040101L, 0x00000100L, 0x04040000L,
++},{
++/* nibble 3 */
++0x00401008L, 0x10001000L, 0x00000008L, 0x10401008L,
++0x00000000L, 0x10400000L, 0x10001008L, 0x00400008L,
++0x10401000L, 0x10000008L, 0x10000000L, 0x00001008L,
++0x10000008L, 0x00401008L, 0x00400000L, 0x10000000L,
++0x10400008L, 0x00401000L, 0x00001000L, 0x00000008L,
++0x00401000L, 0x10001008L, 0x10400000L, 0x00001000L,
++0x00001008L, 0x00000000L, 0x00400008L, 0x10401000L,
++0x10001000L, 0x10400008L, 0x10401008L, 0x00400000L,
++0x10400008L, 0x00001008L, 0x00400000L, 0x10000008L,
++0x00401000L, 0x10001000L, 0x00000008L, 0x10400000L,
++0x10001008L, 0x00000000L, 0x00001000L, 0x00400008L,
++0x00000000L, 0x10400008L, 0x10401000L, 0x00001000L,
++0x10000000L, 0x10401008L, 0x00401008L, 0x00400000L,
++0x10401008L, 0x00000008L, 0x10001000L, 0x00401008L,
++0x00400008L, 0x00401000L, 0x10400000L, 0x10001008L,
++0x00001008L, 0x10000000L, 0x10000008L, 0x10401000L,
++},{
++/* nibble 4 */
++0x08000000L, 0x00010000L, 0x00000400L, 0x08010420L,
++0x08010020L, 0x08000400L, 0x00010420L, 0x08010000L,
++0x00010000L, 0x00000020L, 0x08000020L, 0x00010400L,
++0x08000420L, 0x08010020L, 0x08010400L, 0x00000000L,
++0x00010400L, 0x08000000L, 0x00010020L, 0x00000420L,
++0x08000400L, 0x00010420L, 0x00000000L, 0x08000020L,
++0x00000020L, 0x08000420L, 0x08010420L, 0x00010020L,
++0x08010000L, 0x00000400L, 0x00000420L, 0x08010400L,
++0x08010400L, 0x08000420L, 0x00010020L, 0x08010000L,
++0x00010000L, 0x00000020L, 0x08000020L, 0x08000400L,
++0x08000000L, 0x00010400L, 0x08010420L, 0x00000000L,
++0x00010420L, 0x08000000L, 0x00000400L, 0x00010020L,
++0x08000420L, 0x00000400L, 0x00000000L, 0x08010420L,
++0x08010020L, 0x08010400L, 0x00000420L, 0x00010000L,
++0x00010400L, 0x08010020L, 0x08000400L, 0x00000420L,
++0x00000020L, 0x00010420L, 0x08010000L, 0x08000020L,
++},{
++/* nibble 5 */
++0x80000040L, 0x00200040L, 0x00000000L, 0x80202000L,
++0x00200040L, 0x00002000L, 0x80002040L, 0x00200000L,
++0x00002040L, 0x80202040L, 0x00202000L, 0x80000000L,
++0x80002000L, 0x80000040L, 0x80200000L, 0x00202040L,
++0x00200000L, 0x80002040L, 0x80200040L, 0x00000000L,
++0x00002000L, 0x00000040L, 0x80202000L, 0x80200040L,
++0x80202040L, 0x80200000L, 0x80000000L, 0x00002040L,
++0x00000040L, 0x00202000L, 0x00202040L, 0x80002000L,
++0x00002040L, 0x80000000L, 0x80002000L, 0x00202040L,
++0x80202000L, 0x00200040L, 0x00000000L, 0x80002000L,
++0x80000000L, 0x00002000L, 0x80200040L, 0x00200000L,
++0x00200040L, 0x80202040L, 0x00202000L, 0x00000040L,
++0x80202040L, 0x00202000L, 0x00200000L, 0x80002040L,
++0x80000040L, 0x80200000L, 0x00202040L, 0x00000000L,
++0x00002000L, 0x80000040L, 0x80002040L, 0x80202000L,
++0x80200000L, 0x00002040L, 0x00000040L, 0x80200040L,
++},{
++/* nibble 6 */
++0x00004000L, 0x00000200L, 0x01000200L, 0x01000004L,
++0x01004204L, 0x00004004L, 0x00004200L, 0x00000000L,
++0x01000000L, 0x01000204L, 0x00000204L, 0x01004000L,
++0x00000004L, 0x01004200L, 0x01004000L, 0x00000204L,
++0x01000204L, 0x00004000L, 0x00004004L, 0x01004204L,
++0x00000000L, 0x01000200L, 0x01000004L, 0x00004200L,
++0x01004004L, 0x00004204L, 0x01004200L, 0x00000004L,
++0x00004204L, 0x01004004L, 0x00000200L, 0x01000000L,
++0x00004204L, 0x01004000L, 0x01004004L, 0x00000204L,
++0x00004000L, 0x00000200L, 0x01000000L, 0x01004004L,
++0x01000204L, 0x00004204L, 0x00004200L, 0x00000000L,
++0x00000200L, 0x01000004L, 0x00000004L, 0x01000200L,
++0x00000000L, 0x01000204L, 0x01000200L, 0x00004200L,
++0x00000204L, 0x00004000L, 0x01004204L, 0x01000000L,
++0x01004200L, 0x00000004L, 0x00004004L, 0x01004204L,
++0x01000004L, 0x01004200L, 0x01004000L, 0x00004004L,
++},{
++/* nibble 7 */
++0x20800080L, 0x20820000L, 0x00020080L, 0x00000000L,
++0x20020000L, 0x00800080L, 0x20800000L, 0x20820080L,
++0x00000080L, 0x20000000L, 0x00820000L, 0x00020080L,
++0x00820080L, 0x20020080L, 0x20000080L, 0x20800000L,
++0x00020000L, 0x00820080L, 0x00800080L, 0x20020000L,
++0x20820080L, 0x20000080L, 0x00000000L, 0x00820000L,
++0x20000000L, 0x00800000L, 0x20020080L, 0x20800080L,
++0x00800000L, 0x00020000L, 0x20820000L, 0x00000080L,
++0x00800000L, 0x00020000L, 0x20000080L, 0x20820080L,
++0x00020080L, 0x20000000L, 0x00000000L, 0x00820000L,
++0x20800080L, 0x20020080L, 0x20020000L, 0x00800080L,
++0x20820000L, 0x00000080L, 0x00800080L, 0x20020000L,
++0x20820080L, 0x00800000L, 0x20800000L, 0x20000080L,
++0x00820000L, 0x00020080L, 0x20020080L, 0x20800000L,
++0x00000080L, 0x20820000L, 0x00820080L, 0x00000000L,
++0x20000000L, 0x20800080L, 0x00020000L, 0x00820080L,
++}};
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/include/crypto/aes.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,97 @@
++// I retain copyright in this code but I encourage its free use provided
++// that I don't carry any responsibility for the results. I am especially
++// happy to see it used in free and open source software. If you do use
++// it I would appreciate an acknowledgement of its origin in the code or
++// the product that results and I would also appreciate knowing a little
++// about the use to which it is being put. I am grateful to Frank Yellin
++// for some ideas that are used in this implementation.
++//
++// Dr B. R. Gladman <brg@gladman.uk.net> 6th April 2001.
++//
++// This is an implementation of the AES encryption algorithm (Rijndael)
++// designed by Joan Daemen and Vincent Rijmen. This version is designed
++// to provide both fixed and dynamic block and key lengths and can also
++// run with either big or little endian internal byte order (see aes.h).
++// It inputs block and key lengths in bytes with the legal values being
++// 16, 24 and 32.
++
++/*
++ * Modified by Jari Ruusu, May 1 2001
++ * - Fixed some compile warnings, code was ok but gcc warned anyway.
++ * - Changed basic types: byte -> unsigned char, word -> u_int32_t
++ * - Major name space cleanup: Names visible to outside now begin
++ * with "aes_" or "AES_". A lot of stuff moved from aes.h to aes.c
++ * - Removed C++ and DLL support as part of name space cleanup.
++ * - Eliminated unnecessary recomputation of tables. (actual bug fix)
++ * - Merged precomputed constant tables to aes.c file.
++ * - Removed data alignment restrictions for portability reasons.
++ * - Made block and key lengths accept bit count (128/192/256)
++ * as well byte count (16/24/32).
++ * - Removed all error checks. This change also eliminated the need
++ * to preinitialize the context struct to zero.
++ * - Removed some totally unused constants.
++ */
++
++#ifndef _AES_H
++#define _AES_H
++
++#if defined(__linux__) && defined(__KERNEL__)
++# include <linux/types.h>
++#else
++# include <sys/types.h>
++#endif
++
++// CONFIGURATION OPTIONS (see also aes.c)
++//
++// Define AES_BLOCK_SIZE to set the cipher block size (16, 24 or 32) or
++// leave this undefined for dynamically variable block size (this will
++// result in much slower code).
++// IMPORTANT NOTE: AES_BLOCK_SIZE is in BYTES (16, 24, 32 or undefined). If
++// left undefined a slower version providing variable block length is compiled
++
++#define AES_BLOCK_SIZE 16
++
++// The number of key schedule words for different block and key lengths
++// allowing for method of computation which requires the length to be a
++// multiple of the key length
++//
++// Nk = 4 6 8
++// -------------
++// Nb = 4 | 60 60 64
++// 6 | 96 90 96
++// 8 | 120 120 120
++
++#if !defined(AES_BLOCK_SIZE) || (AES_BLOCK_SIZE == 32)
++#define AES_KS_LENGTH 120
++#define AES_RC_LENGTH 29
++#else
++#define AES_KS_LENGTH 4 * AES_BLOCK_SIZE
++#define AES_RC_LENGTH (9 * AES_BLOCK_SIZE) / 8 - 8
++#endif
++
++typedef struct
++{
++ u_int32_t aes_Nkey; // the number of words in the key input block
++ u_int32_t aes_Nrnd; // the number of cipher rounds
++ u_int32_t aes_e_key[AES_KS_LENGTH]; // the encryption key schedule
++ u_int32_t aes_d_key[AES_KS_LENGTH]; // the decryption key schedule
++#if !defined(AES_BLOCK_SIZE)
++ u_int32_t aes_Ncol; // the number of columns in the cipher state
++#endif
++} aes_context;
++
++// THE CIPHER INTERFACE
++
++#if !defined(AES_BLOCK_SIZE)
++extern void aes_set_blk(aes_context *, const int);
++#endif
++extern void aes_set_key(aes_context *, const unsigned char [], const int, const int);
++extern void aes_encrypt(const aes_context *, const unsigned char [], unsigned char []);
++extern void aes_decrypt(const aes_context *, const unsigned char [], unsigned char []);
++
++// The block length inputs to aes_set_block and aes_set_key are in numbers
++// of bytes or bits. The calls to subroutines must be made in the above
++// order but multiple calls can be made without repeating earlier calls
++// if their parameters have not changed.
++
++#endif // _AES_H
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/include/crypto/aes_cbc.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,4 @@
++/* Glue header */
++#include "aes.h"
++int AES_set_key(aes_context *aes_ctx, const u_int8_t * key, int keysize);
++int AES_cbc_encrypt(aes_context *ctx, const u_int8_t * in, u_int8_t * out, int ilen, const u_int8_t * iv, int encrypt);
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/include/crypto/aes_xcbc_mac.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,12 @@
++#ifndef _AES_XCBC_MAC_H
++#define _AES_XCBC_MAC_H
++
++typedef u_int32_t aes_block[4];
++typedef struct {
++ aes_context ctx_k1;
++ aes_block k2;
++ aes_block k3;
++} aes_context_mac;
++int AES_xcbc_mac_set_key(aes_context_mac *ctxm, const u_int8_t *key, int keylen);
++int AES_xcbc_mac_hash(const aes_context_mac *ctxm, const u_int8_t * in, int ilen, u_int8_t hash[16]);
++#endif /* _AES_XCBC_MAC_H */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/include/crypto/cbc_generic.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,110 @@
++#ifndef _CBC_GENERIC_H
++#define _CBC_GENERIC_H
++/*
++ * CBC macro helpers
++ *
++ * Author: JuanJo Ciarlante <jjo-ipsec@mendoza.gov.ar>
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ */
++
++/*
++ * Heavily inspired in loop_AES
++ */
++#define CBC_IMPL_BLK16(name, ctx_type, addr_type, enc_func, dec_func) \
++int name(ctx_type *ctx, const u_int8_t * in, u_int8_t * out, int ilen, const u_int8_t * iv, int encrypt) { \
++ int ret=ilen, pos; \
++ const u_int32_t *iv_i; \
++ if ((ilen) % 16) return 0; \
++ if (encrypt) { \
++ pos=0; \
++ while(pos<ilen) { \
++ if (pos==0) \
++ iv_i=(const u_int32_t*) iv; \
++ else \
++ iv_i=(const u_int32_t*) (out-16); \
++ *((u_int32_t *)(&out[ 0])) = iv_i[0]^*((const u_int32_t *)(&in[ 0])); \
++ *((u_int32_t *)(&out[ 4])) = iv_i[1]^*((const u_int32_t *)(&in[ 4])); \
++ *((u_int32_t *)(&out[ 8])) = iv_i[2]^*((const u_int32_t *)(&in[ 8])); \
++ *((u_int32_t *)(&out[12])) = iv_i[3]^*((const u_int32_t *)(&in[12])); \
++ enc_func(ctx, (addr_type) out, (addr_type) out); \
++ in+=16; \
++ out+=16; \
++ pos+=16; \
++ } \
++ } else { \
++ pos=ilen-16; \
++ in+=pos; \
++ out+=pos; \
++ while(pos>=0) { \
++ dec_func(ctx, (const addr_type) in, (addr_type) out); \
++ if (pos==0) \
++ iv_i=(const u_int32_t*) (iv); \
++ else \
++ iv_i=(const u_int32_t*) (in-16); \
++ *((u_int32_t *)(&out[ 0])) ^= iv_i[0]; \
++ *((u_int32_t *)(&out[ 4])) ^= iv_i[1]; \
++ *((u_int32_t *)(&out[ 8])) ^= iv_i[2]; \
++ *((u_int32_t *)(&out[12])) ^= iv_i[3]; \
++ in-=16; \
++ out-=16; \
++ pos-=16; \
++ } \
++ } \
++ return ret; \
++}
++#define CBC_IMPL_BLK8(name, ctx_type, addr_type, enc_func, dec_func) \
++int name(ctx_type *ctx, u_int8_t * in, u_int8_t * out, int ilen, const u_int8_t * iv, int encrypt) { \
++ int ret=ilen, pos; \
++ const u_int32_t *iv_i; \
++ if ((ilen) % 8) return 0; \
++ if (encrypt) { \
++ pos=0; \
++ while(pos<ilen) { \
++ if (pos==0) \
++ iv_i=(const u_int32_t*) iv; \
++ else \
++ iv_i=(const u_int32_t*) (out-8); \
++ *((u_int32_t *)(&out[ 0])) = iv_i[0]^*((const u_int32_t *)(&in[ 0])); \
++ *((u_int32_t *)(&out[ 4])) = iv_i[1]^*((const u_int32_t *)(&in[ 4])); \
++ enc_func(ctx, (addr_type)out, (addr_type)out); \
++ in+=8; \
++ out+=8; \
++ pos+=8; \
++ } \
++ } else { \
++ pos=ilen-8; \
++ in+=pos; \
++ out+=pos; \
++ while(pos>=0) { \
++ dec_func(ctx, (const addr_type)in, (addr_type)out); \
++ if (pos==0) \
++ iv_i=(const u_int32_t*) (iv); \
++ else \
++ iv_i=(const u_int32_t*) (in-8); \
++ *((u_int32_t *)(&out[ 0])) ^= iv_i[0]; \
++ *((u_int32_t *)(&out[ 4])) ^= iv_i[1]; \
++ in-=8; \
++ out-=8; \
++ pos-=8; \
++ } \
++ } \
++ return ret; \
++}
++#define CBC_DECL(name, ctx_type) \
++int name(ctx_type *ctx, u_int8_t * in, u_int8_t * out, int ilen, const u_int8_t * iv, int encrypt)
++/*
++Eg.:
++CBC_IMPL_BLK16(AES_cbc_encrypt, aes_context, u_int8_t *, aes_encrypt, aes_decrypt);
++CBC_DECL(AES_cbc_encrypt, aes_context);
++*/
++#endif /* _CBC_GENERIC_H */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/include/crypto/des.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,298 @@
++/* crypto/des/des.org */
++/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com)
++ * All rights reserved.
++ *
++ * This package is an SSL implementation written
++ * by Eric Young (eay@cryptsoft.com).
++ * The implementation was written so as to conform with Netscapes SSL.
++ *
++ * This library is free for commercial and non-commercial use as long as
++ * the following conditions are aheared to. The following conditions
++ * apply to all code found in this distribution, be it the RC4, RSA,
++ * lhash, DES, etc., code; not just the SSL code. The SSL documentation
++ * included with this distribution is covered by the same copyright terms
++ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
++ *
++ * Copyright remains Eric Young's, and as such any Copyright notices in
++ * the code are not to be removed.
++ * If this package is used in a product, Eric Young should be given attribution
++ * as the author of the parts of the library used.
++ * This can be in the form of a textual message at program startup or
++ * in documentation (online or textual) provided with the package.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ * 1. Redistributions of source code must retain the copyright
++ * notice, this list of conditions and the following disclaimer.
++ * 2. Redistributions in binary form must reproduce the above copyright
++ * notice, this list of conditions and the following disclaimer in the
++ * documentation and/or other materials provided with the distribution.
++ * 3. All advertising materials mentioning features or use of this software
++ * must display the following acknowledgement:
++ * "This product includes cryptographic software written by
++ * Eric Young (eay@cryptsoft.com)"
++ * The word 'cryptographic' can be left out if the rouines from the library
++ * being used are not cryptographic related :-).
++ * 4. If you include any Windows specific code (or a derivative thereof) from
++ * the apps directory (application code) you must include an acknowledgement:
++ * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
++ *
++ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ *
++ * The licence and distribution terms for any publically available version or
++ * derivative of this code cannot be changed. i.e. this code cannot simply be
++ * copied and put under another distribution licence
++ * [including the GNU Public Licence.]
++ */
++
++/* WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
++ *
++ * Always modify des.org since des.h is automatically generated from
++ * it during SSLeay configuration.
++ *
++ * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
++ */
++
++#ifndef HEADER_DES_H
++#define HEADER_DES_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++
++/* If this is set to 'unsigned int' on a DEC Alpha, this gives about a
++ * %20 speed up (longs are 8 bytes, int's are 4). */
++/* Must be unsigned int on ia64/Itanium or DES breaks badly */
++
++#ifdef __KERNEL__
++#include <linux/types.h>
++#else
++#include <sys/types.h>
++#endif
++
++#ifndef DES_LONG
++#define DES_LONG u_int32_t
++#endif
++
++typedef unsigned char des_cblock[8];
++typedef struct { des_cblock ks; } des_key_schedule[16];
++
++#define DES_KEY_SZ (sizeof(des_cblock))
++#define DES_SCHEDULE_SZ (sizeof(des_key_schedule))
++
++#define DES_ENCRYPT 1
++#define DES_DECRYPT 0
++
++#define DES_CBC_MODE 0
++#define DES_PCBC_MODE 1
++
++#define des_ecb2_encrypt(i,o,k1,k2,e) \
++ des_ecb3_encrypt((i),(o),(k1),(k2),(k1),(e))
++
++#define des_ede2_cbc_encrypt(i,o,l,k1,k2,iv,e) \
++ des_ede3_cbc_encrypt((i),(o),(l),(k1),(k2),(k1),(iv),(e))
++
++#define des_ede2_cfb64_encrypt(i,o,l,k1,k2,iv,n,e) \
++ des_ede3_cfb64_encrypt((i),(o),(l),(k1),(k2),(k1),(iv),(n),(e))
++
++#define des_ede2_ofb64_encrypt(i,o,l,k1,k2,iv,n) \
++ des_ede3_ofb64_encrypt((i),(o),(l),(k1),(k2),(k1),(iv),(n))
++
++#define C_Block des_cblock
++#define Key_schedule des_key_schedule
++#ifdef KERBEROS
++#define ENCRYPT DES_ENCRYPT
++#define DECRYPT DES_DECRYPT
++#endif
++#define KEY_SZ DES_KEY_SZ
++#define string_to_key des_string_to_key
++#define read_pw_string des_read_pw_string
++#define random_key des_random_key
++#define pcbc_encrypt des_pcbc_encrypt
++#define set_key des_set_key
++#define key_sched des_key_sched
++#define ecb_encrypt des_ecb_encrypt
++#define cbc_encrypt des_cbc_encrypt
++#define ncbc_encrypt des_ncbc_encrypt
++#define xcbc_encrypt des_xcbc_encrypt
++#define cbc_cksum des_cbc_cksum
++#define quad_cksum des_quad_cksum
++
++/* For compatibility with the MIT lib - eay 20/05/92 */
++typedef des_key_schedule bit_64;
++#define des_fixup_key_parity des_set_odd_parity
++#define des_check_key_parity check_parity
++
++extern int des_check_key; /* defaults to false */
++extern int des_rw_mode; /* defaults to DES_PCBC_MODE */
++
++/* The next line is used to disable full ANSI prototypes, if your
++ * compiler has problems with the prototypes, make sure this line always
++ * evaluates to true :-) */
++#if defined(MSDOS) || defined(__STDC__)
++#undef NOPROTO
++#endif
++#ifndef NOPROTO
++char *des_options(void);
++void des_ecb3_encrypt(des_cblock *input,des_cblock *output,
++ des_key_schedule ks1,des_key_schedule ks2,
++ des_key_schedule ks3, int enc);
++DES_LONG des_cbc_cksum(des_cblock *input,des_cblock *output,
++ long length,des_key_schedule schedule,des_cblock *ivec);
++void des_cbc_encrypt(des_cblock *input,des_cblock *output,long length,
++ des_key_schedule schedule,des_cblock *ivec,int enc);
++void des_ncbc_encrypt(des_cblock *input,des_cblock *output,long length,
++ des_key_schedule schedule,des_cblock *ivec,int enc);
++void des_xcbc_encrypt(des_cblock *input,des_cblock *output,long length,
++ des_key_schedule schedule,des_cblock *ivec,
++ des_cblock *inw,des_cblock *outw,int enc);
++void des_cfb_encrypt(unsigned char *in,unsigned char *out,int numbits,
++ long length,des_key_schedule schedule,des_cblock *ivec,int enc);
++void des_ecb_encrypt(des_cblock *input,des_cblock *output,
++ des_key_schedule ks,int enc);
++void des_encrypt(DES_LONG *data,des_key_schedule ks, int enc);
++void des_encrypt2(DES_LONG *data,des_key_schedule ks, int enc);
++void des_encrypt3(DES_LONG *data, des_key_schedule ks1,
++ des_key_schedule ks2, des_key_schedule ks3);
++void des_decrypt3(DES_LONG *data, des_key_schedule ks1,
++ des_key_schedule ks2, des_key_schedule ks3);
++void des_ede3_cbc_encrypt(des_cblock *input, des_cblock *output,
++ long length, des_key_schedule ks1, des_key_schedule ks2,
++ des_key_schedule ks3, des_cblock *ivec, int enc);
++void des_ede3_cfb64_encrypt(unsigned char *in, unsigned char *out,
++ long length, des_key_schedule ks1, des_key_schedule ks2,
++ des_key_schedule ks3, des_cblock *ivec, int *num, int enc);
++void des_ede3_ofb64_encrypt(unsigned char *in, unsigned char *out,
++ long length, des_key_schedule ks1, des_key_schedule ks2,
++ des_key_schedule ks3, des_cblock *ivec, int *num);
++
++void des_xwhite_in2out(des_cblock (*des_key), des_cblock (*in_white),
++ des_cblock (*out_white));
++
++int des_enc_read(int fd,char *buf,int len,des_key_schedule sched,
++ des_cblock *iv);
++int des_enc_write(int fd,char *buf,int len,des_key_schedule sched,
++ des_cblock *iv);
++char *des_fcrypt(const char *buf,const char *salt, char *ret);
++#ifdef PERL5
++char *des_crypt(const char *buf,const char *salt);
++#else
++/* some stupid compilers complain because I have declared char instead
++ * of const char */
++#ifndef __KERNEL__
++#ifdef HEADER_DES_LOCL_H
++char *crypt(const char *buf,const char *salt);
++#else /* HEADER_DES_LOCL_H */
++char *crypt(void);
++#endif /* HEADER_DES_LOCL_H */
++#endif /* __KERNEL__ */
++#endif /* PERL5 */
++void des_ofb_encrypt(unsigned char *in,unsigned char *out,
++ int numbits,long length,des_key_schedule schedule,des_cblock *ivec);
++void des_pcbc_encrypt(des_cblock *input,des_cblock *output,long length,
++ des_key_schedule schedule,des_cblock *ivec,int enc);
++DES_LONG des_quad_cksum(des_cblock *input,des_cblock *output,
++ long length,int out_count,des_cblock *seed);
++void des_random_seed(des_cblock key);
++void des_random_key(des_cblock ret);
++int des_read_password(des_cblock *key,char *prompt,int verify);
++int des_read_2passwords(des_cblock *key1,des_cblock *key2,
++ char *prompt,int verify);
++int des_read_pw_string(char *buf,int length,char *prompt,int verify);
++void des_set_odd_parity(des_cblock *key);
++int des_is_weak_key(des_cblock *key);
++int des_set_key(des_cblock *key,des_key_schedule schedule);
++int des_key_sched(des_cblock *key,des_key_schedule schedule);
++void des_string_to_key(char *str,des_cblock *key);
++void des_string_to_2keys(char *str,des_cblock *key1,des_cblock *key2);
++void des_cfb64_encrypt(unsigned char *in, unsigned char *out, long length,
++ des_key_schedule schedule, des_cblock *ivec, int *num, int enc);
++void des_ofb64_encrypt(unsigned char *in, unsigned char *out, long length,
++ des_key_schedule schedule, des_cblock *ivec, int *num);
++int des_read_pw(char *buf, char *buff, int size, char *prompt, int verify);
++
++/* Extra functions from Mark Murray <mark@grondar.za> */
++/* The following functions are not in the normal unix build or the
++ * SSLeay build. When using the SSLeay build, use RAND_seed()
++ * and RAND_bytes() instead. */
++int des_new_random_key(des_cblock *key);
++void des_init_random_number_generator(des_cblock *key);
++void des_set_random_generator_seed(des_cblock *key);
++void des_set_sequence_number(des_cblock new_sequence_number);
++void des_generate_random_block(des_cblock *block);
++
++#else
++
++char *des_options();
++void des_ecb3_encrypt();
++DES_LONG des_cbc_cksum();
++void des_cbc_encrypt();
++void des_ncbc_encrypt();
++void des_xcbc_encrypt();
++void des_cfb_encrypt();
++void des_ede3_cfb64_encrypt();
++void des_ede3_ofb64_encrypt();
++void des_ecb_encrypt();
++void des_encrypt();
++void des_encrypt2();
++void des_encrypt3();
++void des_decrypt3();
++void des_ede3_cbc_encrypt();
++int des_enc_read();
++int des_enc_write();
++char *des_fcrypt();
++#ifdef PERL5
++char *des_crypt();
++#else
++char *crypt();
++#endif
++void des_ofb_encrypt();
++void des_pcbc_encrypt();
++DES_LONG des_quad_cksum();
++void des_random_seed();
++void des_random_key();
++int des_read_password();
++int des_read_2passwords();
++int des_read_pw_string();
++void des_set_odd_parity();
++int des_is_weak_key();
++int des_set_key();
++int des_key_sched();
++void des_string_to_key();
++void des_string_to_2keys();
++void des_cfb64_encrypt();
++void des_ofb64_encrypt();
++int des_read_pw();
++void des_xwhite_in2out();
++
++/* Extra functions from Mark Murray <mark@grondar.za> */
++/* The following functions are not in the normal unix build or the
++ * SSLeay build. When using the SSLeay build, use RAND_seed()
++ * and RAND_bytes() instead. */
++#ifdef FreeBSD
++int des_new_random_key();
++void des_init_random_number_generator();
++void des_set_random_generator_seed();
++void des_set_sequence_number();
++void des_generate_random_block();
++#endif
++
++#endif
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/include/mast.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,33 @@
++struct mast_callbacks {
++ int (*packet_encap)(struct device *mast, void *context,
++ struct sk_buff *skb, int flowref);
++ int (*link_inquire)(struct device *mast, void *context);
++};
++
++
++struct device *mast_init (int family,
++ struct mast_callbacks *callbacks,
++ unsigned int flags,
++ unsigned int desired_unit,
++ unsigned int max_flowref,
++ void *context);
++
++int mast_destroy(struct device *mast);
++
++int mast_recv(struct device *mast, struct sk_buff *skb, int flowref);
++
++/* free this skb as being useless, increment failure count. */
++int mast_toast(struct device *mast, struct sk_buff *skb, int flowref);
++
++int mast_linkstat (struct device *mast, int flowref,
++ int status);
++
++int mast_setreference (struct device *mast,
++ int defaultSA);
++
++int mast_setneighbor (struct device *mast,
++ struct sockaddr *source,
++ struct sockaddr *destination,
++ int flowref);
++
++
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/include/openswan.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,513 @@
++#ifndef _OPENSWAN_H
++/*
++ * header file for FreeS/WAN library functions
++ * Copyright (C) 1998, 1999, 2000 Henry Spencer.
++ * Copyright (C) 1999, 2000, 2001 Richard Guy Briggs
++ *
++ * This library is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU Library General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/lgpl.txt>.
++ *
++ * This library 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 Library General Public
++ * License for more details.
++ *
++ * RCSID $Id$
++ */
++#define _OPENSWAN_H /* seen it, no need to see it again */
++
++
++
++/*
++ * We've just got to have some datatypes defined... And annoyingly, just
++ * where we get them depends on whether we're in userland or not.
++ */
++/* things that need to come from one place or the other, depending */
++#ifdef __KERNEL__
++#include <linux/types.h>
++#include <linux/socket.h>
++#include <linux/in.h>
++#include <linux/string.h>
++#include <linux/ctype.h>
++#define assert(foo) /* nothing */
++#else
++#include <sys/types.h>
++#include <netinet/in.h>
++#include <string.h>
++#include <ctype.h>
++#include <assert.h>
++#include <stdio.h>
++
++# define uint8_t u_int8_t
++# define uint16_t u_int16_t
++# define uint32_t u_int32_t
++# define uint64_t u_int64_t
++
++
++# define DEBUG_NO_STATIC static
++
++#endif
++
++#include <openswan/ipsec_param.h>
++
++
++/*
++ * Grab the kernel version to see if we have NET_21, and therefore
++ * IPv6. Some of this is repeated from ipsec_kversions.h. Of course,
++ * we aren't really testing if the kernel has IPv6, but rather if the
++ * the include files do.
++ */
++#include <linux/version.h>
++#ifndef KERNEL_VERSION
++#define KERNEL_VERSION(x,y,z) (((x)<<16)+((y)<<8)+(z))
++#endif
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
++#define NET_21
++#endif
++
++#ifndef IPPROTO_COMP
++# define IPPROTO_COMP 108
++#endif /* !IPPROTO_COMP */
++
++#ifndef IPPROTO_INT
++# define IPPROTO_INT 61
++#endif /* !IPPROTO_INT */
++
++#ifdef CONFIG_IPSEC_DEBUG
++#ifndef DEBUG_NO_STATIC
++# define DEBUG_NO_STATIC
++#endif
++#else /* CONFIG_IPSEC_DEBUG */
++#ifndef DEBUG_NO_STATIC
++# define DEBUG_NO_STATIC static
++#endif
++#endif /* CONFIG_IPSEC_DEBUG */
++
++#ifdef CONFIG_IPSEC_NAT_TRAVERSAL /* KERNEL ifdef */
++#ifndef NAT_TRAVERSAL
++#define NAT_TRAVERSAL
++#endif
++#endif
++#ifdef NAT_TRAVERSAL
++#define ESPINUDP_WITH_NON_IKE 1 /* draft-ietf-ipsec-nat-t-ike-00/01 */
++#define ESPINUDP_WITH_NON_ESP 2 /* draft-ietf-ipsec-nat-t-ike-02 */
++#endif
++
++/*
++ * Basic data types for the address-handling functions.
++ * ip_address and ip_subnet are supposed to be opaque types; do not
++ * use their definitions directly, they are subject to change!
++ */
++
++/* first, some quick fakes in case we're on an old system with no IPv6 */
++#ifndef s6_addr16
++struct in6_addr {
++ union
++ {
++ __u8 u6_addr8[16];
++ __u16 u6_addr16[8];
++ __u32 u6_addr32[4];
++ } in6_u;
++#define s6_addr in6_u.u6_addr8
++#define s6_addr16 in6_u.u6_addr16
++#define s6_addr32 in6_u.u6_addr32
++};
++struct sockaddr_in6 {
++ unsigned short int sin6_family; /* AF_INET6 */
++ __u16 sin6_port; /* Transport layer port # */
++ __u32 sin6_flowinfo; /* IPv6 flow information */
++ struct in6_addr sin6_addr; /* IPv6 address */
++ __u32 sin6_scope_id; /* scope id (new in RFC2553) */
++};
++#endif /* !s6_addr16 */
++
++/* then the main types */
++typedef struct {
++ union {
++ struct sockaddr_in v4;
++ struct sockaddr_in6 v6;
++ } u;
++} ip_address;
++typedef struct {
++ ip_address addr;
++ int maskbits;
++} ip_subnet;
++
++/* and the SA ID stuff */
++#ifdef __KERNEL__
++typedef __u32 ipsec_spi_t;
++#else
++typedef u_int32_t ipsec_spi_t;
++#endif
++typedef struct { /* to identify an SA, we need: */
++ ip_address dst; /* A. destination host */
++ ipsec_spi_t spi; /* B. 32-bit SPI, assigned by dest. host */
++# define SPI_PASS 256 /* magic values... */
++# define SPI_DROP 257 /* ...for use... */
++# define SPI_REJECT 258 /* ...with SA_INT */
++# define SPI_HOLD 259
++# define SPI_TRAP 260
++# define SPI_TRAPSUBNET 261
++ int proto; /* C. protocol */
++# define SA_ESP 50 /* IPPROTO_ESP */
++# define SA_AH 51 /* IPPROTO_AH */
++# define SA_IPIP 4 /* IPPROTO_IPIP */
++# define SA_COMP 108 /* IPPROTO_COMP */
++# define SA_INT 61 /* IANA reserved for internal use */
++} ip_said;
++
++/* misc */
++typedef const char *err_t; /* error message, or NULL for success */
++struct prng { /* pseudo-random-number-generator guts */
++ unsigned char sbox[256];
++ int i, j;
++ unsigned long count;
++};
++
++
++/*
++ * definitions for user space, taken from freeswan/ipsec_sa.h
++ */
++typedef uint32_t IPsecSAref_t;
++
++#define IPSEC_SA_REF_FIELD_WIDTH (8 * sizeof(IPsecSAref_t))
++
++#define IPsecSAref2NFmark(x) ((x) << (IPSEC_SA_REF_FIELD_WIDTH - IPSEC_SA_REF_TABLE_IDX_WIDTH))
++#define NFmark2IPsecSAref(x) ((x) >> (IPSEC_SA_REF_FIELD_WIDTH - IPSEC_SA_REF_TABLE_IDX_WIDTH))
++
++#define IPSEC_SAREF_NULL (~((IPsecSAref_t)0))
++
++/* GCC magic for use in function definitions! */
++#ifdef GCC_LINT
++# define PRINTF_LIKE(n) __attribute__ ((format(printf, n, n+1)))
++# define NEVER_RETURNS __attribute__ ((noreturn))
++# define UNUSED __attribute__ ((unused))
++# define BLANK_FORMAT " " /* GCC_LINT whines about empty formats */
++#else
++# define PRINTF_LIKE(n) /* ignore */
++# define NEVER_RETURNS /* ignore */
++# define UNUSED /* ignore */
++# define BLANK_FORMAT ""
++#endif
++
++
++
++
++
++/*
++ * new IPv6-compatible functions
++ */
++
++/* text conversions */
++err_t ttoul(const char *src, size_t srclen, int format, unsigned long *dst);
++size_t ultot(unsigned long src, int format, char *buf, size_t buflen);
++#define ULTOT_BUF (22+1) /* holds 64 bits in octal */
++err_t ttoaddr(const char *src, size_t srclen, int af, ip_address *dst);
++err_t tnatoaddr(const char *src, size_t srclen, int af, ip_address *dst);
++size_t addrtot(const ip_address *src, int format, char *buf, size_t buflen);
++/* RFC 1886 old IPv6 reverse-lookup format is the bulkiest */
++#define ADDRTOT_BUF (32*2 + 3 + 1 + 3 + 1 + 1)
++err_t ttosubnet(const char *src, size_t srclen, int af, ip_subnet *dst);
++size_t subnettot(const ip_subnet *src, int format, char *buf, size_t buflen);
++#define SUBNETTOT_BUF (ADDRTOT_BUF + 1 + 3)
++size_t subnetporttot(const ip_subnet *src, int format, char *buf, size_t buflen);
++#define SUBNETPROTOTOT_BUF (SUBNETTOTO_BUF + ULTOT_BUF)
++err_t ttosa(const char *src, size_t srclen, ip_said *dst);
++size_t satot(const ip_said *src, int format, char *bufptr, size_t buflen);
++#define SATOT_BUF (5 + ULTOA_BUF + 1 + ADDRTOT_BUF)
++err_t ttodata(const char *src, size_t srclen, int base, char *buf,
++ size_t buflen, size_t *needed);
++err_t ttodatav(const char *src, size_t srclen, int base,
++ char *buf, size_t buflen, size_t *needed,
++ char *errp, size_t errlen, unsigned int flags);
++#define TTODATAV_BUF 40 /* ttodatav's largest non-literal message */
++#define TTODATAV_IGNORESPACE (1<<1) /* ignore spaces in base64 encodings*/
++#define TTODATAV_SPACECOUNTS 0 /* do not ignore spaces in base64 */
++
++size_t datatot(const char *src, size_t srclen, int format, char *buf,
++ size_t buflen);
++size_t keyblobtoid(const unsigned char *src, size_t srclen, char *dst,
++ size_t dstlen);
++size_t splitkeytoid(const unsigned char *e, size_t elen, const unsigned char *m,
++ size_t mlen, char *dst, size_t dstlen);
++#define KEYID_BUF 10 /* up to 9 text digits plus NUL */
++err_t ttoprotoport(char *src, size_t src_len, u_int8_t *proto, u_int16_t *port,
++ int *has_port_wildcard);
++
++/* initializations */
++void initsaid(const ip_address *addr, ipsec_spi_t spi, int proto, ip_said *dst);
++err_t loopbackaddr(int af, ip_address *dst);
++err_t unspecaddr(int af, ip_address *dst);
++err_t anyaddr(int af, ip_address *dst);
++err_t initaddr(const unsigned char *src, size_t srclen, int af, ip_address *dst);
++err_t initsubnet(const ip_address *addr, int maskbits, int clash, ip_subnet *dst);
++err_t addrtosubnet(const ip_address *addr, ip_subnet *dst);
++
++/* misc. conversions and related */
++err_t rangetosubnet(const ip_address *from, const ip_address *to, ip_subnet *dst);
++int addrtypeof(const ip_address *src);
++int subnettypeof(const ip_subnet *src);
++size_t addrlenof(const ip_address *src);
++size_t addrbytesptr(const ip_address *src, const unsigned char **dst);
++size_t addrbytesof(const ip_address *src, unsigned char *dst, size_t dstlen);
++int masktocount(const ip_address *src);
++void networkof(const ip_subnet *src, ip_address *dst);
++void maskof(const ip_subnet *src, ip_address *dst);
++
++/* tests */
++int sameaddr(const ip_address *a, const ip_address *b);
++int addrcmp(const ip_address *a, const ip_address *b);
++int samesubnet(const ip_subnet *a, const ip_subnet *b);
++int addrinsubnet(const ip_address *a, const ip_subnet *s);
++int subnetinsubnet(const ip_subnet *a, const ip_subnet *b);
++int subnetishost(const ip_subnet *s);
++int samesaid(const ip_said *a, const ip_said *b);
++int sameaddrtype(const ip_address *a, const ip_address *b);
++int samesubnettype(const ip_subnet *a, const ip_subnet *b);
++int isanyaddr(const ip_address *src);
++int isunspecaddr(const ip_address *src);
++int isloopbackaddr(const ip_address *src);
++
++/* low-level grot */
++int portof(const ip_address *src);
++void setportof(int port, ip_address *dst);
++struct sockaddr *sockaddrof(ip_address *src);
++size_t sockaddrlenof(const ip_address *src);
++
++/* PRNG */
++void prng_init(struct prng *prng, const unsigned char *key, size_t keylen);
++void prng_bytes(struct prng *prng, unsigned char *dst, size_t dstlen);
++unsigned long prng_count(struct prng *prng);
++void prng_final(struct prng *prng);
++
++/* odds and ends */
++const char *ipsec_version_code(void);
++const char *ipsec_version_string(void);
++const char **ipsec_copyright_notice(void);
++
++const char *dns_string_rr(int rr, char *buf, int bufsize);
++const char *dns_string_datetime(time_t seconds,
++ char *buf,
++ int bufsize);
++
++
++/*
++ * old functions, to be deleted eventually
++ */
++
++/* unsigned long */
++const char * /* NULL for success, else string literal */
++atoul(
++ const char *src,
++ size_t srclen, /* 0 means strlen(src) */
++ int base, /* 0 means figure it out */
++ unsigned long *resultp
++);
++size_t /* space needed for full conversion */
++ultoa(
++ unsigned long n,
++ int base,
++ char *dst,
++ size_t dstlen
++);
++#define ULTOA_BUF 21 /* just large enough for largest result, */
++ /* assuming 64-bit unsigned long! */
++
++/* Internet addresses */
++const char * /* NULL for success, else string literal */
++atoaddr(
++ const char *src,
++ size_t srclen, /* 0 means strlen(src) */
++ struct in_addr *addr
++);
++size_t /* space needed for full conversion */
++addrtoa(
++ struct in_addr addr,
++ int format, /* character; 0 means default */
++ char *dst,
++ size_t dstlen
++);
++#define ADDRTOA_BUF 16 /* just large enough for largest result */
++
++/* subnets */
++const char * /* NULL for success, else string literal */
++atosubnet(
++ const char *src,
++ size_t srclen, /* 0 means strlen(src) */
++ struct in_addr *addr,
++ struct in_addr *mask
++);
++size_t /* space needed for full conversion */
++subnettoa(
++ struct in_addr addr,
++ struct in_addr mask,
++ int format, /* character; 0 means default */
++ char *dst,
++ size_t dstlen
++);
++#define SUBNETTOA_BUF 32 /* large enough for worst case result */
++
++/* ranges */
++const char * /* NULL for success, else string literal */
++atoasr(
++ const char *src,
++ size_t srclen, /* 0 means strlen(src) */
++ char *type, /* 'a', 's', 'r' */
++ struct in_addr *addrs /* two-element array */
++);
++size_t /* space needed for full conversion */
++rangetoa(
++ struct in_addr *addrs, /* two-element array */
++ int format, /* character; 0 means default */
++ char *dst,
++ size_t dstlen
++);
++#define RANGETOA_BUF 34 /* large enough for worst case result */
++
++/* data types for SA conversion functions */
++
++/* generic data, e.g. keys */
++const char * /* NULL for success, else string literal */
++atobytes(
++ const char *src,
++ size_t srclen, /* 0 means strlen(src) */
++ char *dst,
++ size_t dstlen,
++ size_t *lenp /* NULL means don't bother telling me */
++);
++size_t /* 0 failure, else true size */
++bytestoa(
++ const char *src,
++ size_t srclen,
++ int format, /* character; 0 means default */
++ char *dst,
++ size_t dstlen
++);
++
++/* old versions of generic-data functions; deprecated */
++size_t /* 0 failure, else true size */
++atodata(
++ const char *src,
++ size_t srclen, /* 0 means strlen(src) */
++ char *dst,
++ size_t dstlen
++);
++size_t /* 0 failure, else true size */
++datatoa(
++ const char *src,
++ size_t srclen,
++ int format, /* character; 0 means default */
++ char *dst,
++ size_t dstlen
++);
++
++/* part extraction and special addresses */
++struct in_addr
++subnetof(
++ struct in_addr addr,
++ struct in_addr mask
++);
++struct in_addr
++hostof(
++ struct in_addr addr,
++ struct in_addr mask
++);
++struct in_addr
++broadcastof(
++ struct in_addr addr,
++ struct in_addr mask
++);
++
++/* mask handling */
++int
++goodmask(
++ struct in_addr mask
++);
++int
++masktobits(
++ struct in_addr mask
++);
++struct in_addr
++bitstomask(
++ int n
++);
++
++
++
++/*
++ * general utilities
++ */
++
++#ifndef __KERNEL__
++/* option pickup from files (userland only because of use of FILE) */
++const char *optionsfrom(const char *filename, int *argcp, char ***argvp,
++ int optind, FILE *errorreport);
++
++/* sanitize a string */
++extern size_t sanitize_string(char *buf, size_t size);
++
++#endif
++
++
++/*
++ * ENUM of klips debugging values. Not currently used in klips.
++ * debug flag is actually 32 -bits, but only one bit is ever used,
++ * so we can actually pack it all into a single 32-bit word.
++ */
++enum klips_debug_flags {
++ KDF_VERBOSE = 0,
++ KDF_XMIT = 1,
++ KDF_NETLINK = 2, /* obsolete */
++ KDF_XFORM = 3,
++ KDF_EROUTE = 4,
++ KDF_SPI = 5,
++ KDF_RADIJ = 6,
++ KDF_ESP = 7,
++ KDF_AH = 8, /* obsolete */
++ KDF_RCV = 9,
++ KDF_TUNNEL = 10,
++ KDF_PFKEY = 11,
++ KDF_COMP = 12
++};
++
++
++/*
++ * Debugging levels for pfkey_lib_debug
++ */
++#define PF_KEY_DEBUG_PARSE_NONE 0
++#define PF_KEY_DEBUG_PARSE_PROBLEM 1
++#define PF_KEY_DEBUG_PARSE_STRUCT 2
++#define PF_KEY_DEBUG_PARSE_FLOW 4
++#define PF_KEY_DEBUG_BUILD 8
++#define PF_KEY_DEBUG_PARSE_MAX 15
++
++extern unsigned int pfkey_lib_debug; /* bits selecting what to report */
++
++/*
++ * pluto and lwdnsq need to know the maximum size of the commands to,
++ * and replies from lwdnsq.
++ */
++
++#define LWDNSQ_CMDBUF_LEN 1024
++#define LWDNSQ_RESULT_LEN_MAX 4096
++
++
++/* syntax for passthrough SA */
++#ifndef PASSTHROUGHNAME
++#define PASSTHROUGHNAME "%passthrough"
++#define PASSTHROUGH4NAME "%passthrough4"
++#define PASSTHROUGH6NAME "%passthrough6"
++#define PASSTHROUGHIS "tun0@0.0.0.0"
++#define PASSTHROUGH4IS "tun0@0.0.0.0"
++#define PASSTHROUGH6IS "tun0@::"
++#define PASSTHROUGHTYPE "tun"
++#define PASSTHROUGHSPI 0
++#define PASSTHROUGHDST 0
++#endif
++
++
++
++#endif /* _OPENSWAN_H */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/include/openswan/ipcomp.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,61 @@
++/*
++ * IPCOMP zlib interface code.
++ * Copyright (C) 2000 Svenning Soerensen <svenning@post5.tele.dk>
++ * Copyright (C) 2000, 2001 Richard Guy Briggs <rgb@conscoop.ottawa.on.ca>
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++
++ RCSID $Id$
++
++ */
++
++/* SSS */
++
++#ifndef _IPCOMP_H
++#define _IPCOMP_H
++
++/* Prefix all global deflate symbols with "ipcomp_" to avoid collisions with ppp_deflate & ext2comp */
++#ifndef IPCOMP_PREFIX
++#define IPCOMP_PREFIX
++#endif /* IPCOMP_PREFIX */
++
++#ifndef IPPROTO_COMP
++#define IPPROTO_COMP 108
++#endif /* IPPROTO_COMP */
++
++#ifdef CONFIG_IPSEC_DEBUG
++extern int sysctl_ipsec_debug_ipcomp;
++#endif /* CONFIG_IPSEC_DEBUG */
++
++struct ipcomphdr { /* IPCOMP header */
++ __u8 ipcomp_nh; /* Next header (protocol) */
++ __u8 ipcomp_flags; /* Reserved, must be 0 */
++ __u16 ipcomp_cpi; /* Compression Parameter Index */
++};
++
++extern struct inet_protocol comp_protocol;
++extern int sysctl_ipsec_debug_ipcomp;
++
++#define IPCOMP_UNCOMPRESSABLE 0x000000001
++#define IPCOMP_COMPRESSIONERROR 0x000000002
++#define IPCOMP_PARMERROR 0x000000004
++#define IPCOMP_DECOMPRESSIONERROR 0x000000008
++
++#define IPCOMP_ADAPT_INITIAL_TRIES 8
++#define IPCOMP_ADAPT_INITIAL_SKIP 4
++#define IPCOMP_ADAPT_SUBSEQ_TRIES 2
++#define IPCOMP_ADAPT_SUBSEQ_SKIP 8
++
++/* Function prototypes */
++struct sk_buff *skb_compress(struct sk_buff *skb, struct ipsec_sa *ips, unsigned int *flags);
++struct sk_buff *skb_decompress(struct sk_buff *skb, struct ipsec_sa *ips, unsigned int *flags);
++
++#endif /* _IPCOMP_H */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/include/openswan/ipsec_ah.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,190 @@
++/*
++ * Authentication Header declarations
++ * Copyright (C) 1996, 1997 John Ioannidis.
++ * Copyright (C) 1998, 1999, 2000, 2001 Richard Guy Briggs.
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ * RCSID $Id$
++ */
++
++#include "ipsec_md5h.h"
++#include "ipsec_sha1.h"
++
++#ifndef IPPROTO_AH
++#define IPPROTO_AH 51
++#endif /* IPPROTO_AH */
++
++#include "ipsec_auth.h"
++
++#ifdef __KERNEL__
++
++extern struct inet_protocol ah_protocol;
++
++struct options;
++
++struct ahhdr /* Generic AH header */
++{
++ __u8 ah_nh; /* Next header (protocol) */
++ __u8 ah_hl; /* AH length, in 32-bit words */
++ __u16 ah_rv; /* reserved, must be 0 */
++ __u32 ah_spi; /* Security Parameters Index */
++ __u32 ah_rpl; /* Replay prevention */
++ __u8 ah_data[AHHMAC_HASHLEN];/* Authentication hash */
++};
++#define AH_BASIC_LEN 8 /* basic AH header is 8 bytes, nh,hl,rv,spi
++ * and the ah_hl, says how many bytes after that
++ * to cover. */
++
++extern struct xform_functions ah_xform_funcs[];
++
++#ifdef CONFIG_IPSEC_DEBUG
++extern int debug_ah;
++#endif /* CONFIG_IPSEC_DEBUG */
++#endif /* __KERNEL__ */
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.23 2004/04/05 19:55:04 mcr
++ * Moved from linux/include/freeswan/ipsec_ah.h,v
++ *
++ * Revision 1.22 2004/04/05 19:41:05 mcr
++ * merged alg-branch code.
++ *
++ * Revision 1.21 2003/12/13 19:10:16 mcr
++ * refactored rcv and xmit code - same as FS 2.05.
++ *
++ * Revision 1.22 2003/12/11 20:14:58 mcr
++ * refactored the xmit code, to move all encapsulation
++ * code into protocol functions. Note that all functions
++ * are essentially done by a single function, which is probably
++ * wrong.
++ * the rcv_functions structures are renamed xform_functions.
++ *
++ * Revision 1.21 2003/12/06 21:21:19 mcr
++ * split up receive path into per-transform files, for
++ * easier later removal.
++ *
++ * Revision 1.20.8.1 2003/12/22 15:25:52 jjo
++ * Merged algo-0.8.1-rc11-test1 into alg-branch
++ *
++ * Revision 1.20 2003/02/06 02:21:34 rgb
++ *
++ * Moved "struct auth_alg" from ipsec_rcv.c to ipsec_ah.h .
++ * Changed "struct ah" to "struct ahhdr" and "struct esp" to "struct esphdr".
++ * Removed "#ifdef INBOUND_POLICY_CHECK_eroute" dead code.
++ *
++ * Revision 1.19 2002/09/16 21:19:13 mcr
++ * fixes for west-ah-icmp-01 - length of AH header must be
++ * calculated properly, and next_header field properly copied.
++ *
++ * Revision 1.18 2002/05/14 02:37:02 rgb
++ * Change reference from _TDB to _IPSA.
++ *
++ * Revision 1.17 2002/04/24 07:36:46 mcr
++ * Moved from ./klips/net/ipsec/ipsec_ah.h,v
++ *
++ * Revision 1.16 2002/02/20 01:27:06 rgb
++ * Ditched a pile of structs only used by the old Netlink interface.
++ *
++ * Revision 1.15 2001/12/11 02:35:57 rgb
++ * Change "struct net_device" to "struct device" for 2.2 compatibility.
++ *
++ * Revision 1.14 2001/11/26 09:23:47 rgb
++ * Merge MCR's ipsec_sa, eroute, proc and struct lifetime changes.
++ *
++ * Revision 1.13.2.1 2001/09/25 02:18:24 mcr
++ * replace "struct device" with "struct netdevice"
++ *
++ * Revision 1.13 2001/06/14 19:35:08 rgb
++ * Update copyright date.
++ *
++ * Revision 1.12 2000/09/12 03:21:20 rgb
++ * Cleared out unused htonq.
++ *
++ * Revision 1.11 2000/09/08 19:12:55 rgb
++ * Change references from DEBUG_IPSEC to CONFIG_IPSEC_DEBUG.
++ *
++ * Revision 1.10 2000/01/21 06:13:10 rgb
++ * Tidied up spacing.
++ * Added macros for HMAC padding magic numbers.(kravietz)
++ *
++ * Revision 1.9 1999/12/07 18:16:23 rgb
++ * Fixed comments at end of #endif lines.
++ *
++ * Revision 1.8 1999/04/11 00:28:56 henry
++ * GPL boilerplate
++ *
++ * Revision 1.7 1999/04/06 04:54:25 rgb
++ * Fix/Add RCSID Id: and Log: bits to make PHMDs happy. This includes
++ * patch shell fixes.
++ *
++ * Revision 1.6 1999/01/26 02:06:01 rgb
++ * Removed CONFIG_IPSEC_ALGO_SWITCH macro.
++ *
++ * Revision 1.5 1999/01/22 06:17:49 rgb
++ * Updated macro comments.
++ * Added context types to support algorithm switch code.
++ * 64-bit clean-up -- converting 'u long long' to __u64.
++ *
++ * Revision 1.4 1998/07/14 15:54:56 rgb
++ * Add #ifdef __KERNEL__ to protect kernel-only structures.
++ *
++ * Revision 1.3 1998/06/30 18:05:16 rgb
++ * Comment out references to htonq.
++ *
++ * Revision 1.2 1998/06/25 19:33:46 rgb
++ * Add prototype for protocol receive function.
++ * Rearrange for more logical layout.
++ *
++ * Revision 1.1 1998/06/18 21:27:43 henry
++ * move sources from klips/src to klips/net/ipsec, to keep stupid
++ * kernel-build scripts happier in the presence of symlinks
++ *
++ * Revision 1.4 1998/05/18 22:28:43 rgb
++ * Disable key printing facilities from /proc/net/ipsec_*.
++ *
++ * Revision 1.3 1998/04/21 21:29:07 rgb
++ * Rearrange debug switches to change on the fly debug output from user
++ * space. Only kernel changes checked in at this time. radij.c was also
++ * changed to temporarily remove buggy debugging code in rj_delete causing
++ * an OOPS and hence, netlink device open errors.
++ *
++ * Revision 1.2 1998/04/12 22:03:17 rgb
++ * Updated ESP-3DES-HMAC-MD5-96,
++ * ESP-DES-HMAC-MD5-96,
++ * AH-HMAC-MD5-96,
++ * AH-HMAC-SHA1-96 since Henry started freeswan cvs repository
++ * from old standards (RFC182[5-9] to new (as of March 1998) drafts.
++ *
++ * Fixed eroute references in /proc/net/ipsec*.
++ *
++ * Started to patch module unloading memory leaks in ipsec_netlink and
++ * radij tree unloading.
++ *
++ * Revision 1.1 1998/04/09 03:05:55 henry
++ * sources moved up from linux/net/ipsec
++ *
++ * Revision 1.1.1.1 1998/04/08 05:35:02 henry
++ * RGB's ipsec-0.8pre2.tar.gz ipsec-0.8
++ *
++ * Revision 0.4 1997/01/15 01:28:15 ji
++ * Added definitions for new AH transforms.
++ *
++ * Revision 0.3 1996/11/20 14:35:48 ji
++ * Minor Cleanup.
++ * Rationalized debugging code.
++ *
++ * Revision 0.2 1996/11/02 00:18:33 ji
++ * First limited release.
++ *
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/include/openswan/ipsec_alg.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,260 @@
++/*
++ * Modular extensions service and registration functions interface
++ *
++ * Author: JuanJo Ciarlante <jjo-ipsec@mendoza.gov.ar>
++ *
++ * ipsec_alg.h,v 1.1.2.1 2003/11/21 18:12:23 jjo Exp
++ *
++ */
++/*
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ */
++#ifndef IPSEC_ALG_H
++#define IPSEC_ALG_H
++
++/*
++ * gcc >= 3.2 has removed __FUNCTION__, replaced by C99 __func__
++ * *BUT* its a compiler variable.
++ */
++#if (__GNUC__ >= 3)
++#ifndef __FUNCTION__
++#define __FUNCTION__ __func__
++#endif
++#endif
++
++/* Version 0.8.1-0 */
++#define IPSEC_ALG_VERSION 0x00080100
++
++#include <linux/types.h>
++#include <linux/list.h>
++#include <asm/atomic.h>
++/*
++ * The following structs are used via pointers in ipsec_alg object to
++ * avoid ipsec_alg.h coupling with freeswan headers, thus simplifying
++ * module development
++ */
++struct ipsec_sa;
++struct esp;
++
++/**************************************
++ *
++ * Main registration object
++ *
++ *************************************/
++#define IPSEC_ALG_VERSION_QUAD(v) \
++ (v>>24),((v>>16)&0xff),((v>>8)&0xff),(v&0xff)
++/*
++ * Main ipsec_alg objects: "OOPrograming wannabe"
++ * Hierachy (carefully handled with _minimal_ cast'ing):
++ *
++ * ipsec_alg+
++ * +->ipsec_alg_enc (ixt_alg_type=SADB_EXT_SUPPORTED_ENCRYPT)
++ * +->ipsec_alg_auth (ixt_alg_type=SADB_EXT_SUPPORTED_AUTH)
++ */
++
++/***************************************************************
++ *
++ * INTERFACE object: struct ipsec_alg
++ *
++ ***************************************************************/
++
++/*
++ * common part for every struct ipsec_alg_*
++ * (sortof poor's man OOP)
++ */
++#define IPSEC_ALG_STRUCT_COMMON \
++ unsigned ixt_version; /* only allow this version (or 'near')*/ \
++ struct list_head ixt_list; /* dlinked list */ \
++ struct module *ixt_module; /* THIS_MODULE */ \
++ unsigned ixt_state; /* state flags */ \
++ atomic_t ixt_refcnt; /* ref. count when pointed from ipsec_sa */ \
++ char ixt_name[16]; /* descriptive short name, eg. "3des" */ \
++ void *ixt_data; /* private for algo implementation */ \
++ uint8_t ixt_blocksize; /* blocksize in bytes */ \
++ \
++ /* THIS IS A COPY of struct supported (lib/pfkey.h) \
++ * please keep in sync until we migrate 'supported' stuff \
++ * to ipsec_alg \
++ */ \
++ uint16_t ixt_alg_type; /* correspond to IPSEC_ALG_{ENCRYPT,AUTH} */ \
++ uint8_t ixt_alg_id; /* enc. alg. number, eg. ESP_3DES */ \
++ uint8_t ixt_ivlen; /* ivlen in bits, expected to be multiple of 8! */ \
++ uint16_t ixt_keyminbits;/* min. keybits (of entropy) */ \
++ uint16_t ixt_keymaxbits;/* max. keybits (of entropy) */
++
++#define ixt_support ixt_alg_type
++
++#define IPSEC_ALG_ST_SUPP 0x01
++#define IPSEC_ALG_ST_REGISTERED 0x02
++#define IPSEC_ALG_ST_EXCL 0x04
++struct ipsec_alg {
++ IPSEC_ALG_STRUCT_COMMON
++};
++/*
++ * Note the const in cbc_encrypt IV arg:
++ * some ciphers like to toast passed IV (eg. 3DES): make a local IV copy
++ */
++struct ipsec_alg_enc {
++ IPSEC_ALG_STRUCT_COMMON
++ unsigned ixt_e_keylen; /* raw key length in bytes */
++ unsigned ixt_e_ctx_size; /* sa_p->key_e_size */
++ int (*ixt_e_set_key)(struct ipsec_alg_enc *alg, __u8 *key_e, const __u8 *key, size_t keysize);
++ __u8 *(*ixt_e_new_key)(struct ipsec_alg_enc *alg, const __u8 *key, size_t keysize);
++ void (*ixt_e_destroy_key)(struct ipsec_alg_enc *alg, __u8 *key_e);
++ int (*ixt_e_cbc_encrypt)(struct ipsec_alg_enc *alg, __u8 *key_e, __u8 *in, int ilen, const __u8 *iv, int encrypt);
++};
++struct ipsec_alg_auth {
++ IPSEC_ALG_STRUCT_COMMON
++ unsigned ixt_a_keylen; /* raw key length in bytes */
++ unsigned ixt_a_ctx_size; /* sa_p->key_a_size */
++ unsigned ixt_a_authlen; /* 'natural' auth. hash len (bytes) */
++ int (*ixt_a_hmac_set_key)(struct ipsec_alg_auth *alg, __u8 *key_a, const __u8 *key, int keylen);
++ int (*ixt_a_hmac_hash)(struct ipsec_alg_auth *alg, __u8 *key_a, const __u8 *dat, int len, __u8 *hash, int hashlen);
++};
++/*
++ * These are _copies_ of SADB_EXT_SUPPORTED_{AUTH,ENCRYPT},
++ * to avoid header coupling for true constants
++ * about headers ... "cp is your friend" --Linus
++ */
++#define IPSEC_ALG_TYPE_AUTH 14
++#define IPSEC_ALG_TYPE_ENCRYPT 15
++
++/***************************************************************
++ *
++ * INTERFACE for module loading,testing, and unloading
++ *
++ ***************************************************************/
++/* - registration calls */
++int register_ipsec_alg(struct ipsec_alg *);
++int unregister_ipsec_alg(struct ipsec_alg *);
++/* - optional (simple test) for algos */
++int ipsec_alg_test(unsigned alg_type, unsigned alg_id, int testparm);
++/* inline wrappers (usefull for type validation */
++static inline int register_ipsec_alg_enc(struct ipsec_alg_enc *ixt) {
++ return register_ipsec_alg((struct ipsec_alg*)ixt);
++}
++static inline int unregister_ipsec_alg_enc(struct ipsec_alg_enc *ixt) {
++ return unregister_ipsec_alg((struct ipsec_alg*)ixt);
++}
++static inline int register_ipsec_alg_auth(struct ipsec_alg_auth *ixt) {
++ return register_ipsec_alg((struct ipsec_alg*)ixt);
++}
++static inline int unregister_ipsec_alg_auth(struct ipsec_alg_auth *ixt) {
++ return unregister_ipsec_alg((struct ipsec_alg*)ixt);
++}
++
++/*****************************************************************
++ *
++ * INTERFACE for ENC services: key creation, encrypt function
++ *
++ *****************************************************************/
++
++#define IPSEC_ALG_ENCRYPT 1
++#define IPSEC_ALG_DECRYPT 0
++
++/* encryption key context creation function */
++int ipsec_alg_enc_key_create(struct ipsec_sa *sa_p);
++/*
++ * ipsec_alg_esp_encrypt(): encrypt ilen bytes in idat returns
++ * 0 or ERR<0
++ */
++int ipsec_alg_esp_encrypt(struct ipsec_sa *sa_p, __u8 *idat, int ilen, const __u8 *iv, int action);
++
++/***************************************************************
++ *
++ * INTERFACE for AUTH services: key creation, hash functions
++ *
++ ***************************************************************/
++int ipsec_alg_auth_key_create(struct ipsec_sa *sa_p);
++int ipsec_alg_sa_esp_hash(const struct ipsec_sa *sa_p, const __u8 *espp, int len, __u8 *hash, int hashlen) ;
++#define ipsec_alg_sa_esp_update(c,k,l) ipsec_alg_sa_esp_hash(c,k,l,NULL,0)
++
++/* only called from ipsec_init.c */
++int ipsec_alg_init(void);
++
++/* algo module glue for static algos */
++void ipsec_alg_static_init(void);
++typedef int (*ipsec_alg_init_func_t) (void);
++
++/**********************************************
++ *
++ * INTERFACE for ipsec_sa init and wipe
++ *
++ **********************************************/
++
++/* returns true if ipsec_sa has ipsec_alg obj attached */
++/*
++ * Initializes ipsec_sa's ipsec_alg object, using already loaded
++ * proto, authalg, encalg.; links ipsec_alg objects (enc, auth)
++ */
++int ipsec_alg_sa_init(struct ipsec_sa *sa_p);
++/*
++ * Destroys ipsec_sa's ipsec_alg object
++ * unlinking ipsec_alg objects
++ */
++int ipsec_alg_sa_wipe(struct ipsec_sa *sa_p);
++
++#define IPSEC_ALG_MODULE_INIT_MOD( func_name ) \
++ static int func_name(void); \
++ module_init(func_name); \
++ static int __init func_name(void)
++#define IPSEC_ALG_MODULE_EXIT_MOD( func_name ) \
++ static void func_name(void); \
++ module_exit(func_name); \
++ static void __exit func_name(void)
++
++#define IPSEC_ALG_MODULE_INIT_STATIC( func_name ) \
++ extern int func_name(void); \
++ int func_name(void)
++#define IPSEC_ALG_MODULE_EXIT_STATIC( func_name ) \
++ extern void func_name(void); \
++ void func_name(void)
++
++/**********************************************
++ *
++ * 2.2 backport for some 2.4 useful module stuff
++ *
++ **********************************************/
++#ifdef MODULE
++#ifndef THIS_MODULE
++#define THIS_MODULE (&__this_module)
++#endif
++#ifndef module_init
++typedef int (*__init_module_func_t)(void);
++typedef void (*__cleanup_module_func_t)(void);
++
++#define module_init(x) \
++ int init_module(void) __attribute__((alias(#x))); \
++ static inline __init_module_func_t __init_module_inline(void) \
++ { return x; }
++#define module_exit(x) \
++ void cleanup_module(void) __attribute__((alias(#x))); \
++ static inline __cleanup_module_func_t __cleanup_module_inline(void) \
++ { return x; }
++#endif
++#define IPSEC_ALG_MODULE_INIT( func_name ) IPSEC_ALG_MODULE_INIT_MOD( func_name )
++#define IPSEC_ALG_MODULE_EXIT( func_name ) IPSEC_ALG_MODULE_EXIT_MOD( func_name )
++
++#else /* not MODULE */
++#ifndef THIS_MODULE
++#define THIS_MODULE NULL
++#endif
++/*
++ * I only want module_init() magic
++ * when algo.c file *is THE MODULE*, in all other
++ * cases, initialization is called explicitely from ipsec_alg_init()
++ */
++#define IPSEC_ALG_MODULE_INIT( func_name ) IPSEC_ALG_MODULE_INIT_STATIC(func_name)
++#define IPSEC_ALG_MODULE_EXIT( func_name ) IPSEC_ALG_MODULE_EXIT_STATIC(func_name)
++#endif
++
++#endif /* IPSEC_ALG_H */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/include/openswan/ipsec_auth.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,100 @@
++/*
++ * Authentication Header declarations
++ * Copyright (C) 2003 Michael Richardson <mcr@sandelman.ottawa.on.ca>
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ * RCSID $Id$
++ */
++
++#include "ipsec_md5h.h"
++#include "ipsec_sha1.h"
++
++#ifndef IPSEC_AUTH_H
++#define IPSEC_AUTH_H
++
++#define AH_FLENGTH 12 /* size of fixed part */
++#define AHMD5_KMAX 64 /* MD5 max 512 bits key */
++#define AHMD5_AMAX 12 /* MD5 96 bits of authenticator */
++
++#define AHMD596_KLEN 16 /* MD5 128 bits key */
++#define AHSHA196_KLEN 20 /* SHA1 160 bits key */
++
++#define AHMD596_ALEN 16 /* MD5 128 bits authentication length */
++#define AHSHA196_ALEN 20 /* SHA1 160 bits authentication length */
++
++#define AHMD596_BLKLEN 64 /* MD5 block length */
++#define AHSHA196_BLKLEN 64 /* SHA1 block length */
++#define AHSHA2_256_BLKLEN 64 /* SHA2-256 block length */
++#define AHSHA2_384_BLKLEN 128 /* SHA2-384 block length (?) */
++#define AHSHA2_512_BLKLEN 128 /* SHA2-512 block length */
++
++#define AH_BLKLEN_MAX 128 /* keep up to date! */
++
++
++#define AH_AMAX AHSHA196_ALEN /* keep up to date! */
++#define AHHMAC_HASHLEN 12 /* authenticator length of 96bits */
++#define AHHMAC_RPLLEN 4 /* 32 bit replay counter */
++
++#define DB_AH_PKTRX 0x0001
++#define DB_AH_PKTRX2 0x0002
++#define DB_AH_DMP 0x0004
++#define DB_AH_IPSA 0x0010
++#define DB_AH_XF 0x0020
++#define DB_AH_INAU 0x0040
++#define DB_AH_REPLAY 0x0100
++
++#ifdef __KERNEL__
++
++/* General HMAC algorithm is described in RFC 2104 */
++
++#define HMAC_IPAD 0x36
++#define HMAC_OPAD 0x5C
++
++struct md5_ctx {
++ MD5_CTX ictx; /* context after H(K XOR ipad) */
++ MD5_CTX octx; /* context after H(K XOR opad) */
++};
++
++struct sha1_ctx {
++ SHA1_CTX ictx; /* context after H(K XOR ipad) */
++ SHA1_CTX octx; /* context after H(K XOR opad) */
++};
++
++struct auth_alg {
++ void (*init)(void *ctx);
++ void (*update)(void *ctx, unsigned char *bytes, __u32 len);
++ void (*final)(unsigned char *hash, void *ctx);
++ int hashlen;
++};
++
++struct options;
++
++#endif /* __KERNEL__ */
++#endif /* IPSEC_AUTH_H */
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.3 2004/04/06 02:49:08 mcr
++ * pullup of algo code from alg-branch.
++ *
++ * Revision 1.2 2004/04/05 19:55:04 mcr
++ * Moved from linux/include/freeswan/ipsec_auth.h,v
++ *
++ * Revision 1.1 2003/12/13 19:10:16 mcr
++ * refactored rcv and xmit code - same as FS 2.05.
++ *
++ * Revision 1.1 2003/12/06 21:21:19 mcr
++ * split up receive path into per-transform files, for
++ * easier later removal.
++ *
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/include/openswan/ipsec_encap.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,149 @@
++/*
++ * declarations relevant to encapsulation-like operations
++ * Copyright (C) 1996, 1997 John Ioannidis.
++ * Copyright (C) 1998, 1999, 2000, 2001 Richard Guy Briggs.
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ * RCSID $Id$
++ */
++
++#ifndef _IPSEC_ENCAP_H_
++
++#define SENT_IP4 16 /* data is two struct in_addr + proto + ports*/
++ /* (2 * sizeof(struct in_addr)) */
++ /* sizeof(struct sockaddr_encap)
++ - offsetof(struct sockaddr_encap, Sen.Sip4.Src) */
++
++struct sockaddr_encap
++{
++ __u8 sen_len; /* length */
++ __u8 sen_family; /* AF_ENCAP */
++ __u16 sen_type; /* see SENT_* */
++ union
++ {
++ struct /* SENT_IP4 */
++ {
++ struct in_addr Src;
++ struct in_addr Dst;
++ __u8 Proto;
++ __u16 Sport;
++ __u16 Dport;
++ } Sip4;
++ } Sen;
++};
++
++#define sen_ip_src Sen.Sip4.Src
++#define sen_ip_dst Sen.Sip4.Dst
++#define sen_proto Sen.Sip4.Proto
++#define sen_sport Sen.Sip4.Sport
++#define sen_dport Sen.Sip4.Dport
++
++#ifndef AF_ENCAP
++#define AF_ENCAP 26
++#endif /* AF_ENCAP */
++
++#define _IPSEC_ENCAP_H_
++#endif /* _IPSEC_ENCAP_H_ */
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.19 2004/04/05 19:55:04 mcr
++ * Moved from linux/include/freeswan/ipsec_encap.h,v
++ *
++ * Revision 1.18 2003/10/31 02:27:05 mcr
++ * pulled up port-selector patches and sa_id elimination.
++ *
++ * Revision 1.17.30.1 2003/09/21 13:59:38 mcr
++ * pre-liminary X.509 patch - does not yet pass tests.
++ *
++ * Revision 1.17 2002/04/24 07:36:46 mcr
++ * Moved from ./klips/net/ipsec/ipsec_encap.h,v
++ *
++ * Revision 1.16 2001/11/26 09:23:47 rgb
++ * Merge MCR's ipsec_sa, eroute, proc and struct lifetime changes.
++ *
++ * Revision 1.15.2.1 2001/09/25 02:18:54 mcr
++ * struct eroute moved to ipsec_eroute.h
++ *
++ * Revision 1.15 2001/09/14 16:58:36 rgb
++ * Added support for storing the first and last packets through a HOLD.
++ *
++ * Revision 1.14 2001/09/08 21:13:31 rgb
++ * Added pfkey ident extension support for ISAKMPd. (NetCelo)
++ *
++ * Revision 1.13 2001/06/14 19:35:08 rgb
++ * Update copyright date.
++ *
++ * Revision 1.12 2001/05/27 06:12:10 rgb
++ * Added structures for pid, packet count and last access time to eroute.
++ * Added packet count to beginning of /proc/net/ipsec_eroute.
++ *
++ * Revision 1.11 2000/09/08 19:12:56 rgb
++ * Change references from DEBUG_IPSEC to CONFIG_IPSEC_DEBUG.
++ *
++ * Revision 1.10 2000/03/22 16:15:36 rgb
++ * Fixed renaming of dev_get (MB).
++ *
++ * Revision 1.9 2000/01/21 06:13:26 rgb
++ * Added a macro for AF_ENCAP
++ *
++ * Revision 1.8 1999/12/31 14:56:55 rgb
++ * MB fix for 2.3 dev-use-count.
++ *
++ * Revision 1.7 1999/11/18 04:09:18 rgb
++ * Replaced all kernel version macros to shorter, readable form.
++ *
++ * Revision 1.6 1999/09/24 00:34:13 rgb
++ * Add Marc Boucher's support for 2.3.xx+.
++ *
++ * Revision 1.5 1999/04/11 00:28:57 henry
++ * GPL boilerplate
++ *
++ * Revision 1.4 1999/04/06 04:54:25 rgb
++ * Fix/Add RCSID Id: and Log: bits to make PHMDs happy. This includes
++ * patch shell fixes.
++ *
++ * Revision 1.3 1998/10/19 14:44:28 rgb
++ * Added inclusion of freeswan.h.
++ * sa_id structure implemented and used: now includes protocol.
++ *
++ * Revision 1.2 1998/07/14 18:19:33 rgb
++ * Added #ifdef __KERNEL__ directives to restrict scope of header.
++ *
++ * Revision 1.1 1998/06/18 21:27:44 henry
++ * move sources from klips/src to klips/net/ipsec, to keep stupid
++ * kernel-build scripts happier in the presence of symlinks
++ *
++ * Revision 1.2 1998/04/21 21:29:10 rgb
++ * Rearrange debug switches to change on the fly debug output from user
++ * space. Only kernel changes checked in at this time. radij.c was also
++ * changed to temporarily remove buggy debugging code in rj_delete causing
++ * an OOPS and hence, netlink device open errors.
++ *
++ * Revision 1.1 1998/04/09 03:05:58 henry
++ * sources moved up from linux/net/ipsec
++ *
++ * Revision 1.1.1.1 1998/04/08 05:35:02 henry
++ * RGB's ipsec-0.8pre2.tar.gz ipsec-0.8
++ *
++ * Revision 0.4 1997/01/15 01:28:15 ji
++ * Minor cosmetic changes.
++ *
++ * Revision 0.3 1996/11/20 14:35:48 ji
++ * Minor Cleanup.
++ * Rationalized debugging code.
++ *
++ * Revision 0.2 1996/11/02 00:18:33 ji
++ * First limited release.
++ *
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/include/openswan/ipsec_eroute.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,112 @@
++/*
++ * @(#) declarations of eroute structures
++ *
++ * Copyright (C) 1996, 1997 John Ioannidis.
++ * Copyright (C) 1998, 1999, 2000, 2001 Richard Guy Briggs <rgb@freeswan.org>
++ * Copyright (C) 2001 Michael Richardson <mcr@freeswan.org>
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ * RCSID $Id$
++ *
++ * derived from ipsec_encap.h 1.15 on 2001/9/18 by mcr.
++ *
++ */
++
++#ifndef _IPSEC_EROUTE_H_
++
++#include "radij.h"
++#include "ipsec_encap.h"
++#include "ipsec_radij.h"
++
++/*
++ * The "type" is really part of the address as far as the routing
++ * system is concerned. By using only one bit in the type field
++ * for each type, we sort-of make sure that different types of
++ * encapsulation addresses won't be matched against the wrong type.
++ */
++
++/*
++ * An entry in the radix tree
++ */
++
++struct rjtentry
++{
++ struct radij_node rd_nodes[2]; /* tree glue, and other values */
++#define rd_key(r) ((struct sockaddr_encap *)((r)->rd_nodes->rj_key))
++#define rd_mask(r) ((struct sockaddr_encap *)((r)->rd_nodes->rj_mask))
++ short rd_flags;
++ short rd_count;
++};
++
++struct ident
++{
++ __u16 type; /* identity type */
++ __u64 id; /* identity id */
++ __u8 len; /* identity len */
++ caddr_t data; /* identity data */
++};
++
++/*
++ * An encapsulation route consists of a pointer to a
++ * radix tree entry and a SAID (a destination_address/SPI/protocol triple).
++ */
++
++struct eroute
++{
++ struct rjtentry er_rjt;
++ ip_said er_said;
++ uint32_t er_pid;
++ uint32_t er_count;
++ uint64_t er_lasttime;
++ struct sockaddr_encap er_eaddr; /* MCR get rid of _encap, it is silly*/
++ struct sockaddr_encap er_emask;
++ struct ident er_ident_s;
++ struct ident er_ident_d;
++ struct sk_buff* er_first;
++ struct sk_buff* er_last;
++};
++
++#define er_dst er_said.dst
++#define er_spi er_said.spi
++
++#define _IPSEC_EROUTE_H_
++#endif /* _IPSEC_EROUTE_H_ */
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.5 2004/04/05 19:55:05 mcr
++ * Moved from linux/include/freeswan/ipsec_eroute.h,v
++ *
++ * Revision 1.4 2003/10/31 02:27:05 mcr
++ * pulled up port-selector patches and sa_id elimination.
++ *
++ * Revision 1.3.30.2 2003/10/29 01:10:19 mcr
++ * elimited "struct sa_id"
++ *
++ * Revision 1.3.30.1 2003/09/21 13:59:38 mcr
++ * pre-liminary X.509 patch - does not yet pass tests.
++ *
++ * Revision 1.3 2002/04/24 07:36:46 mcr
++ * Moved from ./klips/net/ipsec/ipsec_eroute.h,v
++ *
++ * Revision 1.2 2001/11/26 09:16:13 rgb
++ * Merge MCR's ipsec_sa, eroute, proc and struct lifetime changes.
++ *
++ * Revision 1.1.2.1 2001/09/25 02:18:54 mcr
++ * struct eroute moved to ipsec_eroute.h
++ *
++ *
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/include/openswan/ipsec_errs.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,53 @@
++/*
++ * @(#) definition of ipsec_errs structure
++ *
++ * Copyright (C) 2001 Richard Guy Briggs <rgb@freeswan.org>
++ * and Michael Richardson <mcr@freeswan.org>
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ * RCSID $Id$
++ *
++ */
++
++/*
++ * This file describes the errors/statistics that FreeSWAN collects.
++ *
++ */
++
++struct ipsec_errs {
++ __u32 ips_alg_errs; /* number of algorithm errors */
++ __u32 ips_auth_errs; /* # of authentication errors */
++ __u32 ips_encsize_errs; /* # of encryption size errors*/
++ __u32 ips_encpad_errs; /* # of encryption pad errors*/
++ __u32 ips_replaywin_errs; /* # of pkt sequence errors */
++};
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.4 2004/04/05 19:55:05 mcr
++ * Moved from linux/include/freeswan/ipsec_errs.h,v
++ *
++ * Revision 1.3 2002/04/24 07:36:46 mcr
++ * Moved from ./klips/net/ipsec/ipsec_errs.h,v
++ *
++ * Revision 1.2 2001/11/26 09:16:13 rgb
++ * Merge MCR's ipsec_sa, eroute, proc and struct lifetime changes.
++ *
++ * Revision 1.1.2.1 2001/09/25 02:25:57 mcr
++ * lifetime structure created and common functions created.
++ *
++ *
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/include/openswan/ipsec_esp.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,147 @@
++/*
++ * Copyright (C) 1996, 1997 John Ioannidis.
++ * Copyright (C) 1998, 1999, 2000, 2001 Richard Guy Briggs.
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ * RCSID $Id$
++ */
++
++#include "openswan/ipsec_md5h.h"
++#include "openswan/ipsec_sha1.h"
++
++#include "crypto/des.h"
++
++#ifndef IPPROTO_ESP
++#define IPPROTO_ESP 50
++#endif /* IPPROTO_ESP */
++
++#define ESP_HEADER_LEN 8 /* 64 bits header (spi+rpl)*/
++
++#define EMT_ESPDESCBC_ULEN 20 /* coming from user mode */
++#define EMT_ESPDES_KMAX 64 /* 512 bit secret key enough? */
++#define EMT_ESPDES_KEY_SZ 8 /* 56 bit secret key with parity = 64 bits */
++#define EMT_ESP3DES_KEY_SZ 24 /* 168 bit secret key with parity = 192 bits */
++#define EMT_ESPDES_IV_SZ 8 /* IV size */
++#define ESP_DESCBC_BLKLEN 8 /* DES-CBC block size */
++
++#define ESP_IV_MAXSZ 16 /* This is _critical_ */
++#define ESP_IV_MAXSZ_INT (ESP_IV_MAXSZ/sizeof(int))
++
++#define DB_ES_PKTRX 0x0001
++#define DB_ES_PKTRX2 0x0002
++#define DB_ES_IPSA 0x0010
++#define DB_ES_XF 0x0020
++#define DB_ES_IPAD 0x0040
++#define DB_ES_INAU 0x0080
++#define DB_ES_OINFO 0x0100
++#define DB_ES_OINFO2 0x0200
++#define DB_ES_OH 0x0400
++#define DB_ES_REPLAY 0x0800
++
++#ifdef __KERNEL__
++struct des_eks {
++ des_key_schedule ks;
++};
++
++extern struct inet_protocol esp_protocol;
++
++struct options;
++
++struct esphdr
++{
++ __u32 esp_spi; /* Security Parameters Index */
++ __u32 esp_rpl; /* Replay counter */
++ __u8 esp_iv[8]; /* iv */
++};
++
++extern struct xform_functions esp_xform_funcs[];
++
++#ifdef CONFIG_IPSEC_DEBUG
++extern int debug_esp;
++#endif /* CONFIG_IPSEC_DEBUG */
++#endif /* __KERNEL__ */
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.25 2004/04/06 02:49:08 mcr
++ * pullup of algo code from alg-branch.
++ *
++ * Revision 1.24 2004/04/05 19:55:05 mcr
++ * Moved from linux/include/freeswan/ipsec_esp.h,v
++ *
++ * Revision 1.23 2004/04/05 19:41:05 mcr
++ * merged alg-branch code.
++ *
++ * Revision 1.22 2003/12/13 19:10:16 mcr
++ * refactored rcv and xmit code - same as FS 2.05.
++ *
++ * Revision 1.23 2003/12/11 20:14:58 mcr
++ * refactored the xmit code, to move all encapsulation
++ * code into protocol functions. Note that all functions
++ * are essentially done by a single function, which is probably
++ * wrong.
++ * the rcv_functions structures are renamed xform_functions.
++ *
++ * Revision 1.22 2003/12/06 21:21:19 mcr
++ * split up receive path into per-transform files, for
++ * easier later removal.
++ *
++ * Revision 1.21.8.1 2003/12/22 15:25:52 jjo
++ * Merged algo-0.8.1-rc11-test1 into alg-branch
++ *
++ * Revision 1.21 2003/02/06 02:21:34 rgb
++ *
++ * Moved "struct auth_alg" from ipsec_rcv.c to ipsec_ah.h .
++ * Changed "struct ah" to "struct ahhdr" and "struct esp" to "struct esphdr".
++ * Removed "#ifdef INBOUND_POLICY_CHECK_eroute" dead code.
++ *
++ * Revision 1.20 2002/05/14 02:37:02 rgb
++ * Change reference from _TDB to _IPSA.
++ *
++ * Revision 1.19 2002/04/24 07:55:32 mcr
++ * #include patches and Makefiles for post-reorg compilation.
++ *
++ * Revision 1.18 2002/04/24 07:36:46 mcr
++ * Moved from ./klips/net/ipsec/ipsec_esp.h,v
++ *
++ * Revision 1.17 2002/02/20 01:27:07 rgb
++ * Ditched a pile of structs only used by the old Netlink interface.
++ *
++ * Revision 1.16 2001/12/11 02:35:57 rgb
++ * Change "struct net_device" to "struct device" for 2.2 compatibility.
++ *
++ * Revision 1.15 2001/11/26 09:23:48 rgb
++ * Merge MCR's ipsec_sa, eroute, proc and struct lifetime changes.
++ *
++ * Revision 1.14.2.3 2001/10/23 04:16:42 mcr
++ * get definition of des_key_schedule from des.h
++ *
++ * Revision 1.14.2.2 2001/10/22 20:33:13 mcr
++ * use "des_key_schedule" structure instead of cooking our own.
++ *
++ * Revision 1.14.2.1 2001/09/25 02:18:25 mcr
++ * replace "struct device" with "struct netdevice"
++ *
++ * Revision 1.14 2001/06/14 19:35:08 rgb
++ * Update copyright date.
++ *
++ * Revision 1.13 2000/09/08 19:12:56 rgb
++ * Change references from DEBUG_IPSEC to CONFIG_IPSEC_DEBUG.
++ *
++ * Revision 1.12 2000/08/01 14:51:50 rgb
++ * Removed _all_ remaining traces of DES.
++ *
++ * Revision 1.11 2000/01/10 16:36:20 rgb
++ * Ditch last of EME option flags, including initiator.
++ *
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/include/openswan/ipsec_ipcomp.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,91 @@
++/*
++ * IP compression header declations
++ *
++ * Copyright (C) 2003 Michael Richardson <mcr@sandelman.ottawa.on.ca>
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ * RCSID $Id$
++ */
++
++#ifndef IPSEC_IPCOMP_H
++#define IPSEC_IPCOMP_H
++
++#include "openswan/ipsec_auth.h"
++
++/* Prefix all global deflate symbols with "ipcomp_" to avoid collisions with ppp_deflate & ext2comp */
++#ifndef IPCOMP_PREFIX
++#define IPCOMP_PREFIX
++#endif /* IPCOMP_PREFIX */
++
++#ifndef IPPROTO_COMP
++#define IPPROTO_COMP 108
++#endif /* IPPROTO_COMP */
++
++#ifdef CONFIG_IPSEC_DEBUG
++extern int sysctl_ipsec_debug_ipcomp;
++#endif /* CONFIG_IPSEC_DEBUG */
++
++struct ipcomphdr { /* IPCOMP header */
++ __u8 ipcomp_nh; /* Next header (protocol) */
++ __u8 ipcomp_flags; /* Reserved, must be 0 */
++ __u16 ipcomp_cpi; /* Compression Parameter Index */
++};
++
++extern struct inet_protocol comp_protocol;
++extern int sysctl_ipsec_debug_ipcomp;
++
++#define IPCOMP_UNCOMPRESSABLE 0x000000001
++#define IPCOMP_COMPRESSIONERROR 0x000000002
++#define IPCOMP_PARMERROR 0x000000004
++#define IPCOMP_DECOMPRESSIONERROR 0x000000008
++
++#define IPCOMP_ADAPT_INITIAL_TRIES 8
++#define IPCOMP_ADAPT_INITIAL_SKIP 4
++#define IPCOMP_ADAPT_SUBSEQ_TRIES 2
++#define IPCOMP_ADAPT_SUBSEQ_SKIP 8
++
++/* Function prototypes */
++struct sk_buff *skb_compress(struct sk_buff *skb, struct ipsec_sa *ips, unsigned int *flags);
++struct sk_buff *skb_decompress(struct sk_buff *skb, struct ipsec_sa *ips, unsigned int *flags);
++
++extern struct xform_functions ipcomp_xform_funcs[];
++
++#endif /* IPSEC_IPCOMP_H */
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.3 2004/04/06 02:49:08 mcr
++ * pullup of algo code from alg-branch.
++ *
++ * Revision 1.2 2004/04/05 19:55:05 mcr
++ * Moved from linux/include/freeswan/ipsec_ipcomp.h,v
++ *
++ * Revision 1.1 2003/12/13 19:10:16 mcr
++ * refactored rcv and xmit code - same as FS 2.05.
++ *
++ * Revision 1.2 2003/12/11 20:14:58 mcr
++ * refactored the xmit code, to move all encapsulation
++ * code into protocol functions. Note that all functions
++ * are essentially done by a single function, which is probably
++ * wrong.
++ * the rcv_functions structures are renamed xform_functions.
++ *
++ * Revision 1.1 2003/12/06 21:21:19 mcr
++ * split up receive path into per-transform files, for
++ * easier later removal.
++ *
++ *
++ *
++ */
++
++
++
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/include/openswan/ipsec_ipe4.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,68 @@
++/*
++ * IP-in-IP Header declarations
++ * Copyright (C) 1996, 1997 John Ioannidis.
++ * Copyright (C) 1998, 1999, 2000, 2001 Richard Guy Briggs.
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ * RCSID $Id$
++ */
++
++/* The packet header is an IP header! */
++
++struct ipe4_xdata /* transform table data */
++{
++ struct in_addr i4_src;
++ struct in_addr i4_dst;
++};
++
++#define EMT_IPE4_ULEN 8 /* coming from user mode */
++
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.6 2004/04/05 19:55:05 mcr
++ * Moved from linux/include/freeswan/ipsec_ipe4.h,v
++ *
++ * Revision 1.5 2002/04/24 07:36:46 mcr
++ * Moved from ./klips/net/ipsec/ipsec_ipe4.h,v
++ *
++ * Revision 1.4 2001/06/14 19:35:08 rgb
++ * Update copyright date.
++ *
++ * Revision 1.3 1999/04/11 00:28:57 henry
++ * GPL boilerplate
++ *
++ * Revision 1.2 1999/04/06 04:54:25 rgb
++ * Fix/Add RCSID Id: and Log: bits to make PHMDs happy. This includes
++ * patch shell fixes.
++ *
++ * Revision 1.1 1998/06/18 21:27:47 henry
++ * move sources from klips/src to klips/net/ipsec, to keep stupid
++ * kernel-build scripts happier in the presence of symlinks
++ *
++ * Revision 1.1 1998/04/09 03:06:07 henry
++ * sources moved up from linux/net/ipsec
++ *
++ * Revision 1.1.1.1 1998/04/08 05:35:03 henry
++ * RGB's ipsec-0.8pre2.tar.gz ipsec-0.8
++ *
++ * Revision 0.4 1997/01/15 01:28:15 ji
++ * No changes.
++ *
++ * Revision 0.3 1996/11/20 14:48:53 ji
++ * Release update only.
++ *
++ * Revision 0.2 1996/11/02 00:18:33 ji
++ * First limited release.
++ *
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/include/openswan/ipsec_ipip.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,45 @@
++/*
++ * Copyright (C) 2003 Michael Richardson <mcr@sandelman.ottawa.on.ca>
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ * RCSID $Id$
++ */
++
++#ifndef _IPSEC_IPIP_H_
++
++#ifndef IPPROTO_IPIP
++#define IPPROTO_IPIP 4
++#endif /* IPPROTO_ESP */
++
++extern struct xform_functions ipip_xform_funcs[];
++
++#define _IPSEC_IPIP_H_
++
++#endif /* _IPSEC_IPIP_H_ */
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.2 2004/04/05 19:55:05 mcr
++ * Moved from linux/include/freeswan/ipsec_ipip.h,v
++ *
++ * Revision 1.1 2003/12/13 19:10:16 mcr
++ * refactored rcv and xmit code - same as FS 2.05.
++ *
++ * Revision 1.1 2003/12/11 20:14:58 mcr
++ * refactored the xmit code, to move all encapsulation
++ * code into protocol functions. Note that all functions
++ * are essentially done by a single function, which is probably
++ * wrong.
++ * the rcv_functions structures are renamed xform_functions.
++ *
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/include/openswan/ipsec_kversion.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,239 @@
++#ifndef _FREESWAN_KVERSIONS_H
++/*
++ * header file for FreeS/WAN library functions
++ * Copyright (C) 1998, 1999, 2000 Henry Spencer.
++ * Copyright (C) 1999, 2000, 2001 Richard Guy Briggs
++ *
++ * This library is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU Library General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/lgpl.txt>.
++ *
++ * This library 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 Library General Public
++ * License for more details.
++ *
++ * RCSID $Id$
++ */
++#define _FREESWAN_KVERSIONS_H /* seen it, no need to see it again */
++
++/*
++ * this file contains a series of atomic defines that depend upon
++ * kernel version numbers. The kernel versions are arranged
++ * in version-order number (which is often not chronological)
++ * and each clause enables or disables a feature.
++ */
++
++/*
++ * First, assorted kernel-version-dependent trickery.
++ */
++#include <linux/version.h>
++#ifndef KERNEL_VERSION
++#define KERNEL_VERSION(x,y,z) (((x)<<16)+((y)<<8)+(z))
++#endif
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0)
++#define HEADER_CACHE_BIND_21
++#endif
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
++#define SPINLOCK
++#define PROC_FS_21
++#define NETLINK_SOCK
++#define NET_21
++#endif
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,19)
++#define net_device_stats enet_statistics
++#endif
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
++#define SPINLOCK_23
++#define NETDEV_23
++# ifndef CONFIG_IP_ALIAS
++# define CONFIG_IP_ALIAS
++# endif
++#include <linux/socket.h>
++#include <linux/skbuff.h>
++#include <linux/netlink.h>
++# ifdef NETLINK_XFRM
++# define NETDEV_25
++# endif
++#endif
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,25)
++#define PROC_FS_2325
++#undef PROC_FS_21
++#endif
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,30)
++#define PROC_NO_DUMMY
++#endif
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,35)
++#define SKB_COPY_EXPAND
++#endif
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,37)
++#define IP_SELECT_IDENT
++#endif
++
++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,50)) && defined(CONFIG_NETFILTER)
++#define SKB_RESET_NFCT
++#endif
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,2)
++#define IP_SELECT_IDENT_NEW
++#endif
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,4)
++#define IPH_is_SKB_PULLED
++#define SKB_COW_NEW
++#define PROTO_HANDLER_SINGLE_PARM
++#define IP_FRAGMENT_LINEARIZE 1
++#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,4) */
++# ifdef REDHAT_BOGOSITY
++# define IP_SELECT_IDENT_NEW
++# define IPH_is_SKB_PULLED
++# define SKB_COW_NEW
++# define PROTO_HANDLER_SINGLE_PARM
++# endif /* REDHAT_BOGOSITY */
++#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,4) */
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,9)
++#define MALLOC_SLAB
++#define LINUX_KERNEL_HAS_SNPRINTF
++#endif
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
++#define HAVE_NETDEV_PRINTK 1
++#endif
++
++#ifdef NET_21
++# include <linux/in6.h>
++#else
++ /* old kernel in.h has some IPv6 stuff, but not quite enough */
++# define s6_addr16 s6_addr
++# define AF_INET6 10
++# define uint8_t __u8
++# define uint16_t __u16
++# define uint32_t __u32
++# define uint64_t __u64
++#endif
++
++#ifdef NET_21
++# define ipsec_kfree_skb(a) kfree_skb(a)
++#else /* NET_21 */
++# define ipsec_kfree_skb(a) kfree_skb(a, FREE_WRITE)
++#endif /* NET_21 */
++
++#ifdef NETDEV_23
++# define device net_device
++# define ipsec_dev_get dev_get_by_name
++# define __ipsec_dev_get __dev_get_by_name
++# define ipsec_dev_put(x) dev_put(x)
++# define __ipsec_dev_put(x) __dev_put(x)
++# define ipsec_dev_hold(x) dev_hold(x)
++#else /* NETDEV_23 */
++# define ipsec_dev_get dev_get
++# define __ipsec_dev_put(x)
++# define ipsec_dev_put(x)
++# define ipsec_dev_hold(x)
++#endif /* NETDEV_23 */
++
++#ifndef SPINLOCK
++# include <linux/bios32.h>
++ /* simulate spin locks and read/write locks */
++ typedef struct {
++ volatile char lock;
++ } spinlock_t;
++
++ typedef struct {
++ volatile unsigned int lock;
++ } rwlock_t;
++
++# define spin_lock_init(x) { (x)->lock = 0;}
++# define rw_lock_init(x) { (x)->lock = 0; }
++
++# define spin_lock(x) { while ((x)->lock) barrier(); (x)->lock=1;}
++# define spin_lock_irq(x) { cli(); spin_lock(x);}
++# define spin_lock_irqsave(x,flags) { save_flags(flags); spin_lock_irq(x);}
++
++# define spin_unlock(x) { (x)->lock=0;}
++# define spin_unlock_irq(x) { spin_unlock(x); sti();}
++# define spin_unlock_irqrestore(x,flags) { spin_unlock(x); restore_flags(flags);}
++
++# define read_lock(x) spin_lock(x)
++# define read_lock_irq(x) spin_lock_irq(x)
++# define read_lock_irqsave(x,flags) spin_lock_irqsave(x,flags)
++
++# define read_unlock(x) spin_unlock(x)
++# define read_unlock_irq(x) spin_unlock_irq(x)
++# define read_unlock_irqrestore(x,flags) spin_unlock_irqrestore(x,flags)
++
++# define write_lock(x) spin_lock(x)
++# define write_lock_irq(x) spin_lock_irq(x)
++# define write_lock_irqsave(x,flags) spin_lock_irqsave(x,flags)
++
++# define write_unlock(x) spin_unlock(x)
++# define write_unlock_irq(x) spin_unlock_irq(x)
++# define write_unlock_irqrestore(x,flags) spin_unlock_irqrestore(x,flags)
++#endif /* !SPINLOCK */
++
++#ifndef SPINLOCK_23
++# define spin_lock_bh(x) spin_lock_irq(x)
++# define spin_unlock_bh(x) spin_unlock_irq(x)
++
++# define read_lock_bh(x) read_lock_irq(x)
++# define read_unlock_bh(x) read_unlock_irq(x)
++
++# define write_lock_bh(x) write_lock_irq(x)
++# define write_unlock_bh(x) write_unlock_irq(x)
++#endif /* !SPINLOCK_23 */
++
++#ifndef HAVE_NETDEV_PRINTK
++#define netdev_printk(sevlevel, netdev, msglevel, format, arg...) \
++ printk(sevlevel "%s: " format , netdev->name , ## arg)
++#endif
++
++#endif /* _FREESWAN_KVERSIONS_H */
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.9 2004/04/05 19:55:05 mcr
++ * Moved from linux/include/freeswan/ipsec_kversion.h,v
++ *
++ * Revision 1.8 2003/12/13 19:10:16 mcr
++ * refactored rcv and xmit code - same as FS 2.05.
++ *
++ * Revision 1.7 2003/07/31 22:48:08 mcr
++ * derive NET25-ness from presence of NETLINK_XFRM macro.
++ *
++ * Revision 1.6 2003/06/24 20:22:32 mcr
++ * added new global: ipsecdevices[] so that we can keep track of
++ * the ipsecX devices. They will be referenced with dev_hold(),
++ * so 2.2 may need this as well.
++ *
++ * Revision 1.5 2003/04/03 17:38:09 rgb
++ * Centralised ipsec_kfree_skb and ipsec_dev_{get,put}.
++ *
++ * Revision 1.4 2002/04/24 07:36:46 mcr
++ * Moved from ./klips/net/ipsec/ipsec_kversion.h,v
++ *
++ * Revision 1.3 2002/04/12 03:21:17 mcr
++ * three parameter version of ip_select_ident appears first
++ * in 2.4.2 (RH7.1) not 2.4.4.
++ *
++ * Revision 1.2 2002/03/08 21:35:22 rgb
++ * Defined LINUX_KERNEL_HAS_SNPRINTF to shut up compiler warnings after
++ * 2.4.9. (Andreas Piesk).
++ *
++ * Revision 1.1 2002/01/29 02:11:42 mcr
++ * removal of kversions.h - sources that needed it now use ipsec_param.h.
++ * updating of IPv6 structures to match latest in6.h version.
++ * removed dead code from freeswan.h that also duplicated kversions.h
++ * code.
++ *
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/include/openswan/ipsec_life.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,112 @@
++/*
++ * Definitions relevant to IPSEC lifetimes
++ * Copyright (C) 2001 Richard Guy Briggs <rgb@freeswan.org>
++ * and Michael Richardson <mcr@freeswan.org>
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ * RCSID $Id$
++ *
++ * This file derived from ipsec_xform.h on 2001/9/18 by mcr.
++ *
++ */
++
++/*
++ * This file describes the book keeping fields for the
++ * IPsec Security Association Structure. ("ipsec_sa")
++ *
++ * This structure is never allocated directly by kernel code,
++ * (it is always a static/auto or is part of a structure)
++ * so it does not have a reference count.
++ *
++ */
++
++#ifndef _IPSEC_LIFE_H_
++
++/*
++ * _count is total count.
++ * _hard is hard limit (kill SA after this number)
++ * _soft is soft limit (try to renew SA after this number)
++ * _last is used in some special cases.
++ *
++ */
++
++struct ipsec_lifetime64
++{
++ __u64 ipl_count;
++ __u64 ipl_soft;
++ __u64 ipl_hard;
++ __u64 ipl_last;
++};
++
++struct ipsec_lifetimes
++{
++ /* number of bytes processed */
++ struct ipsec_lifetime64 ipl_bytes;
++
++ /* number of packets processed */
++ struct ipsec_lifetime64 ipl_packets;
++
++ /* time since SA was added */
++ struct ipsec_lifetime64 ipl_addtime;
++
++ /* time since SA was first used */
++ struct ipsec_lifetime64 ipl_usetime;
++
++ /* from rfc2367:
++ * For CURRENT, the number of different connections,
++ * endpoints, or flows that the association has been
++ * allocated towards. For HARD and SOFT, the number of
++ * these the association may be allocated towards
++ * before it expires. The concept of a connection,
++ * flow, or endpoint is system specific.
++ *
++ * mcr(2001-9-18) it is unclear what purpose these serve for FreeSWAN.
++ * They are maintained for PF_KEY compatibility.
++ */
++ struct ipsec_lifetime64 ipl_allocations;
++};
++
++enum ipsec_life_alive {
++ ipsec_life_harddied = -1,
++ ipsec_life_softdied = 0,
++ ipsec_life_okay = 1
++};
++
++enum ipsec_life_type {
++ ipsec_life_timebased = 1,
++ ipsec_life_countbased= 0
++};
++
++#define _IPSEC_LIFE_H_
++#endif /* _IPSEC_LIFE_H_ */
++
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.4 2004/04/05 19:55:05 mcr
++ * Moved from linux/include/freeswan/ipsec_life.h,v
++ *
++ * Revision 1.3 2002/04/24 07:36:46 mcr
++ * Moved from ./klips/net/ipsec/ipsec_life.h,v
++ *
++ * Revision 1.2 2001/11/26 09:16:14 rgb
++ * Merge MCR's ipsec_sa, eroute, proc and struct lifetime changes.
++ *
++ * Revision 1.1.2.1 2001/09/25 02:25:58 mcr
++ * lifetime structure created and common functions created.
++ *
++ *
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/include/openswan/ipsec_md5h.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,140 @@
++/*
++ * RCSID $Id$
++ */
++
++/*
++ * The rest of this file is Copyright RSA DSI. See the following comments
++ * for the full Copyright notice.
++ */
++
++#ifndef _IPSEC_MD5H_H_
++#define _IPSEC_MD5H_H_
++
++/* GLOBAL.H - RSAREF types and constants
++ */
++
++/* PROTOTYPES should be set to one if and only if the compiler supports
++ function argument prototyping.
++ The following makes PROTOTYPES default to 0 if it has not already
++ been defined with C compiler flags.
++ */
++#ifndef PROTOTYPES
++#define PROTOTYPES 1
++#endif /* !PROTOTYPES */
++
++/* POINTER defines a generic pointer type */
++typedef __u8 *POINTER;
++
++/* UINT2 defines a two byte word */
++typedef __u16 UINT2;
++
++/* UINT4 defines a four byte word */
++typedef __u32 UINT4;
++
++/* PROTO_LIST is defined depending on how PROTOTYPES is defined above.
++ If using PROTOTYPES, then PROTO_LIST returns the list, otherwise it
++ returns an empty list.
++ */
++
++#if PROTOTYPES
++#define PROTO_LIST(list) list
++#else /* PROTOTYPES */
++#define PROTO_LIST(list) ()
++#endif /* PROTOTYPES */
++
++
++/* MD5.H - header file for MD5C.C
++ */
++
++/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
++rights reserved.
++
++License to copy and use this software is granted provided that it
++is identified as the "RSA Data Security, Inc. MD5 Message-Digest
++Algorithm" in all material mentioning or referencing this software
++or this function.
++
++License is also granted to make and use derivative works provided
++that such works are identified as "derived from the RSA Data
++Security, Inc. MD5 Message-Digest Algorithm" in all material
++mentioning or referencing the derived work.
++
++RSA Data Security, Inc. makes no representations concerning either
++the merchantability of this software or the suitability of this
++software for any particular purpose. It is provided "as is"
++without express or implied warranty of any kind.
++
++These notices must be retained in any copies of any part of this
++documentation and/or software.
++ */
++
++/* MD5 context. */
++typedef struct {
++ UINT4 state[4]; /* state (ABCD) */
++ UINT4 count[2]; /* number of bits, modulo 2^64 (lsb first) */
++ unsigned char buffer[64]; /* input buffer */
++} MD5_CTX;
++
++void MD5Init PROTO_LIST ((void *));
++void MD5Update PROTO_LIST
++ ((void *, unsigned char *, __u32));
++void MD5Final PROTO_LIST ((unsigned char [16], void *));
++
++#endif /* _IPSEC_MD5H_H_ */
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.9 2004/04/05 19:55:05 mcr
++ * Moved from linux/include/freeswan/ipsec_md5h.h,v
++ *
++ * Revision 1.8 2002/09/10 01:45:09 mcr
++ * changed type of MD5_CTX and SHA1_CTX to void * so that
++ * the function prototypes would match, and could be placed
++ * into a pointer to a function.
++ *
++ * Revision 1.7 2002/04/24 07:36:46 mcr
++ * Moved from ./klips/net/ipsec/ipsec_md5h.h,v
++ *
++ * Revision 1.6 1999/12/13 13:59:13 rgb
++ * Quick fix to argument size to Update bugs.
++ *
++ * Revision 1.5 1999/12/07 18:16:23 rgb
++ * Fixed comments at end of #endif lines.
++ *
++ * Revision 1.4 1999/04/06 04:54:26 rgb
++ * Fix/Add RCSID Id: and Log: bits to make PHMDs happy. This includes
++ * patch shell fixes.
++ *
++ * Revision 1.3 1999/01/22 06:19:58 rgb
++ * 64-bit clean-up.
++ *
++ * Revision 1.2 1998/11/30 13:22:54 rgb
++ * Rationalised all the klips kernel file headers. They are much shorter
++ * now and won't conflict under RH5.2.
++ *
++ * Revision 1.1 1998/06/18 21:27:48 henry
++ * move sources from klips/src to klips/net/ipsec, to keep stupid
++ * kernel-build scripts happier in the presence of symlinks
++ *
++ * Revision 1.2 1998/04/23 20:54:03 rgb
++ * Fixed md5 and sha1 include file nesting issues, to be cleaned up when
++ * verified.
++ *
++ * Revision 1.1 1998/04/09 03:04:21 henry
++ * sources moved up from linux/net/ipsec
++ * these two include files modified not to include others except in kernel
++ *
++ * Revision 1.1.1.1 1998/04/08 05:35:03 henry
++ * RGB's ipsec-0.8pre2.tar.gz ipsec-0.8
++ *
++ * Revision 0.4 1997/01/15 01:28:15 ji
++ * No changes.
++ *
++ * Revision 0.3 1996/11/20 14:48:53 ji
++ * Release update only.
++ *
++ * Revision 0.2 1996/11/02 00:18:33 ji
++ * First limited release.
++ *
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/include/openswan/ipsec_param.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,333 @@
++/*
++ * @(#) FreeSWAN tunable paramaters
++ *
++ * Copyright (C) 2001 Richard Guy Briggs <rgb@freeswan.org>
++ * and Michael Richardson <mcr@freeswan.org>
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ * RCSID $Id$
++ *
++ */
++
++/*
++ * This file provides a set of #define's which may be tuned by various
++ * people/configurations. It keeps all compile-time tunables in one place.
++ *
++ * This file should be included before all other IPsec kernel-only files.
++ *
++ */
++
++#ifndef _IPSEC_PARAM_H_
++
++#ifdef __KERNEL__
++#include "ipsec_kversion.h"
++
++/* Set number of ipsecX virtual devices here. */
++/* This must be < exp(field width of IPSEC_DEV_FORMAT) */
++/* It must also be reasonable so as not to overload the memory and CPU */
++/* constraints of the host. */
++#define IPSEC_NUM_IF 4
++/* The field width must be < IF_NAM_SIZ - strlen("ipsec") - 1. */
++/* With "ipsec" being 5 characters, that means 10 is the max field width */
++/* but machine memory and CPU constraints are not likely to tollerate */
++/* more than 3 digits. The default is one digit. */
++/* Update: userland scripts get upset if they can't find "ipsec0", so */
++/* for now, no "0"-padding should be used (which would have been helpful */
++/* to make text-searches work */
++#define IPSEC_DEV_FORMAT "ipsec%d"
++/* For, say, 500 virtual ipsec devices, I would recommend: */
++/* #define IPSEC_NUM_IF 500 */
++/* #define IPSEC_DEV_FORMAT "ipsec%03d" */
++/* Note that the "interfaces=" line in /etc/ipsec.conf would be, um, challenging. */
++
++/* use dynamic ipsecX device allocation */
++#ifndef CONFIG_IPSEC_DYNDEV
++#define CONFIG_IPSEC_DYNDEV 1
++#endif /* CONFIG_IPSEC_DYNDEV */
++
++
++#ifdef CONFIG_IPSEC_BIGGATE
++# define SADB_HASHMOD 8069
++#else /* CONFIG_IPSEC_BIGGATE */
++# define SADB_HASHMOD 257
++#endif /* CONFIG_IPSEC_BIGGATE */
++#endif /* __KERNEL__ */
++
++/*
++ * This is for the SA reference table. This number is related to the
++ * maximum number of SAs that KLIPS can concurrently deal with, plus enough
++ * space for keeping expired SAs around.
++ *
++ * TABLE_MAX_WIDTH is the number of bits that we will use.
++ * MAIN_TABLE_WIDTH is the number of bits used for the primary index table.
++ *
++ */
++#ifndef IPSEC_SA_REF_TABLE_IDX_WIDTH
++# define IPSEC_SA_REF_TABLE_IDX_WIDTH 16
++#endif
++
++#ifndef IPSEC_SA_REF_MAINTABLE_IDX_WIDTH
++# define IPSEC_SA_REF_MAINTABLE_IDX_WIDTH 4
++#endif
++
++#ifndef IPSEC_SA_REF_FREELIST_NUM_ENTRIES
++# define IPSEC_SA_REF_FREELIST_NUM_ENTRIES 256
++#endif
++
++#ifndef IPSEC_SA_REF_CODE
++# define IPSEC_SA_REF_CODE 1
++#endif
++
++#ifdef __KERNEL__
++/* This is defined for 2.4, but not 2.2.... */
++#ifndef ARPHRD_VOID
++# define ARPHRD_VOID 0xFFFF
++#endif
++
++/*
++ * Worry about PROC_FS stuff
++ */
++#if defined(PROC_FS_2325)
++/* kernel 2.4 */
++# define IPSEC_PROC_LAST_ARG ,int *eof,void *data
++# define IPSEC_PROCFS_DEBUG_NO_STATIC
++# define IPSEC_PROC_SUBDIRS
++#else
++/* kernel <2.4 */
++# define IPSEC_PROCFS_DEBUG_NO_STATIC DEBUG_NO_STATIC
++
++# ifndef PROC_NO_DUMMY
++# define IPSEC_PROC_LAST_ARG , int dummy
++# else
++# define IPSEC_PROC_LAST_ARG
++# endif /* !PROC_NO_DUMMY */
++#endif /* PROC_FS_2325 */
++
++#if !defined(LINUX_KERNEL_HAS_SNPRINTF)
++/* GNU CPP specific! */
++# define snprintf(buf, len, fmt...) sprintf(buf, ##fmt)
++#endif /* !LINUX_KERNEL_HAS_SNPRINTF */
++
++#ifdef SPINLOCK
++# ifdef SPINLOCK_23
++# include <linux/spinlock.h> /* *lock* */
++# else /* SPINLOCK_23 */
++# include <asm/spinlock.h> /* *lock* */
++# endif /* SPINLOCK_23 */
++#endif /* SPINLOCK */
++
++#ifndef KLIPS_FIXES_DES_PARITY
++# define KLIPS_FIXES_DES_PARITY 1
++#endif /* !KLIPS_FIXES_DES_PARITY */
++
++/* we don't really want to print these unless there are really big problems */
++#ifndef KLIPS_DIVULGE_CYPHER_KEY
++# define KLIPS_DIVULGE_CYPHER_KEY 0
++#endif /* !KLIPS_DIVULGE_CYPHER_KEY */
++
++#ifndef KLIPS_DIVULGE_HMAC_KEY
++# define KLIPS_DIVULGE_HMAC_KEY 0
++#endif /* !KLIPS_DIVULGE_HMAC_KEY */
++
++#ifndef IPSEC_DISALLOW_IPOPTIONS
++# define IPSEC_DISALLOW_IPOPTIONS 1
++#endif /* !KLIPS_DIVULGE_HMAC_KEY */
++
++/* extra toggles for regression testing */
++#ifdef CONFIG_IPSEC_REGRESS
++
++/*
++ * should pfkey_acquire() become 100% lossy?
++ *
++ */
++extern int sysctl_ipsec_regress_pfkey_lossage;
++#ifndef KLIPS_PFKEY_ACQUIRE_LOSSAGE
++# ifdef CONFIG_IPSEC_PFKEY_ACQUIRE_LOSSAGE
++# define KLIPS_PFKEY_ACQUIRE_LOSSAGE 100
++# else /* CONFIG_IPSEC_PFKEY_ACQUIRE_LOSSAGE */
++/* not by default! */
++# define KLIPS_PFKEY_ACQUIRE_LOSSAGE 0
++# endif /* CONFIG_IPSEC_PFKEY_ACQUIRE_LOSSAGE */
++#endif /* KLIPS_PFKEY_ACQUIRE_LOSSAGE */
++
++#endif /* CONFIG_IPSEC_REGRESS */
++
++
++/*
++ * debugging routines.
++ */
++#ifdef CONFIG_IPSEC_DEBUG
++extern void ipsec_print_ip(struct iphdr *ip);
++
++ #define KLIPS_PRINT(flag, format, args...) \
++ ((flag) ? printk(KERN_INFO format , ## args) : 0)
++ #define KLIPS_PRINTMORE(flag, format, args...) \
++ ((flag) ? printk(format , ## args) : 0)
++ #define KLIPS_IP_PRINT(flag, ip) \
++ ((flag) ? ipsec_print_ip(ip) : 0)
++#else /* CONFIG_IPSEC_DEBUG */
++ #define KLIPS_PRINT(flag, format, args...) do ; while(0)
++ #define KLIPS_PRINTMORE(flag, format, args...) do ; while(0)
++ #define KLIPS_IP_PRINT(flag, ip) do ; while(0)
++#endif /* CONFIG_IPSEC_DEBUG */
++
++
++/*
++ * Stupid kernel API differences in APIs. Not only do some
++ * kernels not have ip_select_ident, but some have differing APIs,
++ * and SuSE has one with one parameter, but no way of checking to
++ * see what is really what.
++ */
++
++#ifdef SUSE_LINUX_2_4_19_IS_STUPID
++#define KLIPS_IP_SELECT_IDENT(iph, skb) ip_select_ident(iph)
++#else
++
++/* simplest case, nothing */
++#if !defined(IP_SELECT_IDENT)
++#define KLIPS_IP_SELECT_IDENT(iph, skb) do { iph->id = htons(ip_id_count++); } while(0)
++#endif
++
++/* kernels > 2.3.37-ish */
++#if defined(IP_SELECT_IDENT) && !defined(IP_SELECT_IDENT_NEW)
++#define KLIPS_IP_SELECT_IDENT(iph, skb) ip_select_ident(iph, skb->dst)
++#endif
++
++/* kernels > 2.4.2 */
++#if defined(IP_SELECT_IDENT) && defined(IP_SELECT_IDENT_NEW)
++#define KLIPS_IP_SELECT_IDENT(iph, skb) ip_select_ident(iph, skb->dst, NULL)
++#endif
++
++#endif /* SUSE_LINUX_2_4_19_IS_STUPID */
++
++/*
++ * make klips fail test:east-espiv-01.
++ * exploit is at testing/attacks/espiv
++ *
++ */
++#define KLIPS_IMPAIRMENT_ESPIV_CBC_ATTACK 0
++
++
++/* IP_FRAGMENT_LINEARIZE is set in freeswan.h if Kernel > 2.4.4 */
++#ifndef IP_FRAGMENT_LINEARIZE
++# define IP_FRAGMENT_LINEARIZE 0
++#endif /* IP_FRAGMENT_LINEARIZE */
++#endif /* __KERNEL__ */
++
++#define _IPSEC_PARAM_H_
++#endif /* _IPSEC_PARAM_H_ */
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.24 2004/04/05 19:55:06 mcr
++ * Moved from linux/include/freeswan/ipsec_param.h,v
++ *
++ * Revision 1.23 2003/12/13 19:10:16 mcr
++ * refactored rcv and xmit code - same as FS 2.05.
++ *
++ * Revision 1.22 2003/10/31 02:27:05 mcr
++ * pulled up port-selector patches and sa_id elimination.
++ *
++ * Revision 1.21.4.1 2003/10/29 01:10:19 mcr
++ * elimited "struct sa_id"
++ *
++ * Revision 1.21 2003/04/03 17:38:18 rgb
++ * Centralised ipsec_kfree_skb and ipsec_dev_{get,put}.
++ * Change indentation for readability.
++ *
++ * Revision 1.20 2003/03/14 08:09:26 rgb
++ * Fixed up CONFIG_IPSEC_DYNDEV definitions.
++ *
++ * Revision 1.19 2003/01/30 02:31:43 rgb
++ *
++ * Rename SAref table macro names for clarity.
++ *
++ * Revision 1.18 2002/09/30 19:06:26 rgb
++ * Reduce default table to 16 bits width.
++ *
++ * Revision 1.17 2002/09/20 15:40:29 rgb
++ * Define switch to activate new SAref code.
++ * Prefix macros with "IPSEC_".
++ * Rework saref freelist.
++ * Restrict some bits to kernel context for use to klips utils.
++ *
++ * Revision 1.16 2002/09/20 05:00:31 rgb
++ * Define switch to divulge hmac keys for debugging.
++ * Added IPOPTIONS switch.
++ *
++ * Revision 1.15 2002/09/19 02:34:24 mcr
++ * define IPSEC_PROC_SUBDIRS if we are 2.4, and use that in ipsec_proc.c
++ * to decide if we are to create /proc/net/ipsec/.
++ *
++ * Revision 1.14 2002/08/30 01:20:54 mcr
++ * reorganized 2.0/2.2/2.4 procfs support macro so match
++ * 2.4 values/typedefs.
++ *
++ * Revision 1.13 2002/07/28 22:03:28 mcr
++ * added some documentation to SA_REF_*
++ * turned on fix for ESPIV attack, now that we have the attack code.
++ *
++ * Revision 1.12 2002/07/26 08:48:31 rgb
++ * Added SA ref table code.
++ *
++ * Revision 1.11 2002/07/23 02:57:45 rgb
++ * Define ARPHRD_VOID for < 2.4 kernels.
++ *
++ * Revision 1.10 2002/05/27 21:37:28 rgb
++ * Set the defaults sanely for those adventurous enough to try more than 1
++ * digit of ipsec devices.
++ *
++ * Revision 1.9 2002/05/27 18:56:07 rgb
++ * Convert to dynamic ipsec device allocation.
++ *
++ * Revision 1.8 2002/04/24 07:36:47 mcr
++ * Moved from ./klips/net/ipsec/ipsec_param.h,v
++ *
++ * Revision 1.7 2002/04/20 00:12:25 rgb
++ * Added esp IV CBC attack fix, disabled.
++ *
++ * Revision 1.6 2002/01/29 02:11:42 mcr
++ * removal of kversions.h - sources that needed it now use ipsec_param.h.
++ * updating of IPv6 structures to match latest in6.h version.
++ * removed dead code from freeswan.h that also duplicated kversions.h
++ * code.
++ *
++ * Revision 1.5 2002/01/28 19:22:01 mcr
++ * by default, turn off LINEARIZE option
++ * (let kversions.h turn it on)
++ *
++ * Revision 1.4 2002/01/20 20:19:36 mcr
++ * renamed option to IP_FRAGMENT_LINEARIZE.
++ *
++ * Revision 1.3 2002/01/12 02:57:25 mcr
++ * first regression test causes acquire messages to be lost
++ * 100% of the time. This is to help testing of pluto.
++ *
++ * Revision 1.2 2001/11/26 09:16:14 rgb
++ * Merge MCR's ipsec_sa, eroute, proc and struct lifetime changes.
++ *
++ * Revision 1.1.2.3 2001/10/23 04:40:16 mcr
++ * added #define for DIVULGING session keys in debug output.
++ *
++ * Revision 1.1.2.2 2001/10/22 20:53:25 mcr
++ * added a define to control forcing of DES parity.
++ *
++ * Revision 1.1.2.1 2001/09/25 02:20:19 mcr
++ * many common kernel configuration questions centralized.
++ * more things remain that should be moved from freeswan.h.
++ *
++ *
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/include/openswan/ipsec_policy.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,216 @@
++#ifndef _IPSEC_POLICY_H
++/*
++ * policy interface file between pluto and applications
++ * Copyright (C) 2003 Michael Richardson <mcr@freeswan.org>
++ *
++ * This library is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU Library General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/lgpl.txt>.
++ *
++ * This library 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 Library General Public
++ * License for more details.
++ *
++ * RCSID $Id$
++ */
++#define _IPSEC_POLICY_H /* seen it, no need to see it again */
++
++
++/*
++ * this file defines an interface between an application (or rather an
++ * application library) and a key/policy daemon. It provides for inquiries
++ * as to the current state of a connected socket, as well as for general
++ * questions.
++ *
++ * In general, the interface is defined as a series of functional interfaces,
++ * and the policy messages should be internal. However, because this is in
++ * fact an ABI between pieces of the system that may get compiled and revised
++ * seperately, this ABI must be public and revision controlled.
++ *
++ * It is expected that the daemon will always support previous versions.
++ */
++
++#define IPSEC_POLICY_MSG_REVISION (unsigned)200305061
++
++enum ipsec_policy_command {
++ IPSEC_CMD_QUERY_FD = 1,
++ IPSEC_CMD_QUERY_HOSTPAIR = 2,
++ IPSEC_CMD_QUERY_DSTONLY = 3,
++};
++
++struct ipsec_policy_msg_head {
++ u_int32_t ipm_version;
++ u_int32_t ipm_msg_len;
++ u_int32_t ipm_msg_type;
++ u_int32_t ipm_msg_seq;
++};
++
++enum ipsec_privacy_quality {
++ IPSEC_PRIVACY_NONE = 0,
++ IPSEC_PRIVACY_INTEGRAL = 4, /* not private at all. AH-like */
++ IPSEC_PRIVACY_UNKNOWN = 8, /* something is claimed, but details unavail */
++ IPSEC_PRIVACY_ROT13 = 12, /* trivially breakable, i.e. 1DES */
++ IPSEC_PRIVACY_GAK = 16, /* known eavesdroppers */
++ IPSEC_PRIVACY_PRIVATE = 32, /* secure for at least a decade */
++ IPSEC_PRIVACY_STRONG = 64, /* ridiculously secure */
++ IPSEC_PRIVACY_TORTOISE = 192, /* even stronger, but very slow */
++ IPSEC_PRIVACY_OTP = 224, /* some kind of *true* one time pad */
++};
++
++enum ipsec_bandwidth_quality {
++ IPSEC_QOS_UNKNOWN = 0, /* unknown bandwidth */
++ IPSEC_QOS_INTERACTIVE = 16, /* reasonably moderate jitter, moderate fast.
++ Good enough for telnet/ssh. */
++ IPSEC_QOS_VOIP = 32, /* faster crypto, predicable jitter */
++ IPSEC_QOS_FTP = 64, /* higher throughput crypto, perhaps hardware
++ offloaded, but latency/jitter may be bad */
++ IPSEC_QOS_WIRESPEED = 128, /* expect to be able to fill your pipe */
++};
++
++/* moved from programs/pluto/constants.h */
++/* IPsec AH transform values
++ * RFC2407 The Internet IP security Domain of Interpretation for ISAKMP 4.4.3
++ * and in http://www.iana.org/assignments/isakmp-registry
++ */
++enum ipsec_authentication_algo {
++ AH_MD5=2,
++ AH_SHA=3,
++ AH_DES=4,
++ AH_SHA2_256=5,
++ AH_SHA2_384=6,
++ AH_SHA2_512=7
++};
++
++/* IPsec ESP transform values
++ * RFC2407 The Internet IP security Domain of Interpretation for ISAKMP 4.4.4
++ * and from http://www.iana.org/assignments/isakmp-registry
++ */
++
++enum ipsec_cipher_algo {
++ ESP_reserved=0,
++ ESP_DES_IV64=1,
++ ESP_DES=2,
++ ESP_3DES=3,
++ ESP_RC5=4,
++ ESP_IDEA=5,
++ ESP_CAST=6,
++ ESP_BLOWFISH=7,
++ ESP_3IDEA=8,
++ ESP_DES_IV32=9,
++ ESP_RC4=10,
++ ESP_NULL=11,
++ ESP_AES=12,
++};
++
++/* IPCOMP transform values
++ * RFC2407 The Internet IP security Domain of Interpretation for ISAKMP 4.4.5
++ */
++
++enum ipsec_comp_algo {
++ IPCOMP_OUI= 1,
++ IPCOMP_DEFLATE= 2,
++ IPCOMP_LZS= 3,
++ IPCOMP_V42BIS= 4
++};
++
++/* Identification type values
++ * RFC 2407 The Internet IP security Domain of Interpretation for ISAKMP 4.6.2.1
++ */
++
++enum ipsec_id_type {
++ ID_IMPOSSIBLE= (-2), /* private to Pluto */
++ ID_MYID= (-1), /* private to Pluto */
++ ID_NONE= 0, /* private to Pluto */
++ ID_IPV4_ADDR= 1,
++ ID_FQDN= 2,
++ ID_USER_FQDN= 3,
++ ID_IPV4_ADDR_SUBNET= 4,
++ ID_IPV6_ADDR= 5,
++ ID_IPV6_ADDR_SUBNET= 6,
++ ID_IPV4_ADDR_RANGE= 7,
++ ID_IPV6_ADDR_RANGE= 8,
++ ID_DER_ASN1_DN= 9,
++ ID_DER_ASN1_GN= 10,
++ ID_KEY_ID= 11
++};
++
++/* Certificate type values
++ * RFC 2408 ISAKMP, chapter 3.9
++ */
++enum ipsec_cert_type {
++ CERT_NONE= 0, /* none, or guess from file contents */
++ CERT_PKCS7_WRAPPED_X509= 1, /* self-signed certificate from disk */
++ CERT_PGP= 2,
++ CERT_DNS_SIGNED_KEY= 3, /* KEY RR from DNS */
++ CERT_X509_SIGNATURE= 4,
++ CERT_X509_KEY_EXCHANGE= 5,
++ CERT_KERBEROS_TOKENS= 6,
++ CERT_CRL= 7,
++ CERT_ARL= 8,
++ CERT_SPKI= 9,
++ CERT_X509_ATTRIBUTE= 10,
++ CERT_RAW_RSA= 11, /* raw RSA from config file */
++};
++
++/* a SIG record in ASCII */
++struct ipsec_dns_sig {
++ char fqdn[256];
++ char dns_sig[768]; /* empty string if not signed */
++};
++
++struct ipsec_raw_key {
++ char id_name[256];
++ char fs_keyid[8];
++};
++
++struct ipsec_identity {
++ enum ipsec_id_type ii_type;
++ enum ipsec_cert_type ii_format;
++ union {
++ struct ipsec_dns_sig ipsec_dns_signed;
++ /* some thing for PGP */
++ /* some thing for PKIX */
++ struct ipsec_raw_key ipsec_raw_key;
++ } ii_credential;
++};
++
++#define IPSEC_MAX_CREDENTIALS 32
++
++struct ipsec_policy_cmd_query {
++ struct ipsec_policy_msg_head head;
++
++ /* Query section */
++ ip_address query_local; /* us */
++ ip_address query_remote; /* them */
++ u_short src_port, dst_port;
++
++ /* Answer section */
++ enum ipsec_privacy_quality strength;
++ enum ipsec_bandwidth_quality bandwidth;
++ enum ipsec_authentication_algo auth_detail;
++ enum ipsec_cipher_algo esp_detail;
++ enum ipsec_comp_algo comp_detail;
++
++ int credential_count;
++
++ struct ipsec_identity credentials[IPSEC_MAX_CREDENTIALS];
++};
++
++#define IPSEC_POLICY_SOCKET "/var/run/pluto.info"
++
++/* prototypes */
++extern err_t ipsec_policy_lookup(int fd, struct ipsec_policy_cmd_query *result);
++extern err_t ipsec_policy_init(void);
++extern err_t ipsec_policy_final(void);
++extern err_t ipsec_policy_readmsg(int policysock,
++ unsigned char *buf, size_t buflen);
++extern err_t ipsec_policy_sendrecv(unsigned char *buf, size_t buflen);
++extern err_t ipsec_policy_cgilookup(struct ipsec_policy_cmd_query *result);
++
++
++extern const char *ipsec_policy_version_code(void);
++extern const char *ipsec_policy_version_string(void);
++
++#endif /* _IPSEC_POLICY_H */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/include/openswan/ipsec_proto.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,151 @@
++/*
++ * @(#) prototypes for FreeSWAN functions
++ *
++ * Copyright (C) 2001 Richard Guy Briggs <rgb@freeswan.org>
++ * and Michael Richardson <mcr@freeswan.org>
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ * RCSID $Id$
++ *
++ */
++
++#ifndef _IPSEC_PROTO_H_
++
++#include "ipsec_param.h"
++
++/*
++ * This file is a kernel only file that declares prototypes for
++ * all intra-module function calls and global data structures.
++ *
++ * Include this file last.
++ *
++ */
++
++/* ipsec_init.c */
++extern struct prng ipsec_prng;
++
++/* ipsec_sa.c */
++extern struct ipsec_sa *ipsec_sadb_hash[SADB_HASHMOD];
++extern spinlock_t tdb_lock;
++extern int ipsec_sadb_init(void);
++
++extern struct ipsec_sa *ipsec_sa_getbyid(ip_said *);
++extern int ipsec_sa_put(struct ipsec_sa *);
++extern /* void */ int ipsec_sa_del(struct ipsec_sa *);
++extern /* void */ int ipsec_sa_delchain(struct ipsec_sa *);
++extern /* void */ int ipsec_sa_add(struct ipsec_sa *);
++
++extern int ipsec_sadb_cleanup(__u8);
++extern int ipsec_sa_wipe(struct ipsec_sa *);
++
++/* debug declarations */
++
++/* ipsec_proc.c */
++extern int ipsec_proc_init(void);
++extern void ipsec_proc_cleanup(void);
++
++/* ipsec_radij.c */
++extern int ipsec_makeroute(struct sockaddr_encap *ea,
++ struct sockaddr_encap *em,
++ ip_said said,
++ uint32_t pid,
++ struct sk_buff *skb,
++ struct ident *ident_s,
++ struct ident *ident_d);
++
++extern int ipsec_breakroute(struct sockaddr_encap *ea,
++ struct sockaddr_encap *em,
++ struct sk_buff **first,
++ struct sk_buff **last);
++
++int ipsec_radijinit(void);
++int ipsec_cleareroutes(void);
++int ipsec_radijcleanup(void);
++
++/* ipsec_life.c */
++extern enum ipsec_life_alive ipsec_lifetime_check(struct ipsec_lifetime64 *il64,
++ const char *lifename,
++ const char *saname,
++ enum ipsec_life_type ilt,
++ enum ipsec_direction idir,
++ struct ipsec_sa *ips);
++
++
++extern int ipsec_lifetime_format(char *buffer,
++ int buflen,
++ char *lifename,
++ enum ipsec_life_type timebaselife,
++ struct ipsec_lifetime64 *lifetime);
++
++extern void ipsec_lifetime_update_hard(struct ipsec_lifetime64 *lifetime,
++ __u64 newvalue);
++
++extern void ipsec_lifetime_update_soft(struct ipsec_lifetime64 *lifetime,
++ __u64 newvalue);
++
++
++
++
++#ifdef CONFIG_IPSEC_DEBUG
++
++extern int debug_xform;
++extern int debug_eroute;
++extern int debug_spi;
++extern int debug_netlink;
++
++#endif /* CONFIG_IPSEC_DEBUG */
++
++
++
++
++#define _IPSEC_PROTO_H
++#endif /* _IPSEC_PROTO_H_ */
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.8 2004/04/05 19:55:06 mcr
++ * Moved from linux/include/freeswan/ipsec_proto.h,v
++ *
++ * Revision 1.7 2003/10/31 02:27:05 mcr
++ * pulled up port-selector patches and sa_id elimination.
++ *
++ * Revision 1.6.30.1 2003/10/29 01:10:19 mcr
++ * elimited "struct sa_id"
++ *
++ * Revision 1.6 2002/05/23 07:13:48 rgb
++ * Added ipsec_sa_put() for releasing an ipsec_sa refcount.
++ *
++ * Revision 1.5 2002/05/14 02:36:40 rgb
++ * Converted reference from ipsec_sa_put to ipsec_sa_add to avoid confusion
++ * with "put" usage in the kernel.
++ *
++ * Revision 1.4 2002/04/24 07:36:47 mcr
++ * Moved from ./klips/net/ipsec/ipsec_proto.h,v
++ *
++ * Revision 1.3 2002/04/20 00:12:25 rgb
++ * Added esp IV CBC attack fix, disabled.
++ *
++ * Revision 1.2 2001/11/26 09:16:15 rgb
++ * Merge MCR's ipsec_sa, eroute, proc and struct lifetime changes.
++ *
++ * Revision 1.1.2.1 2001/09/25 02:21:01 mcr
++ * ipsec_proto.h created to keep prototypes rather than deal with
++ * cyclic dependancies of structures and prototypes in .h files.
++ *
++ *
++ *
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ *
++ */
++
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/include/openswan/ipsec_radij.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,176 @@
++/*
++ * @(#) Definitions relevant to the IPSEC <> radij tree interfacing
++ * Copyright (C) 1996, 1997 John Ioannidis.
++ * Copyright (C) 1998, 1999, 2000, 2001 Richard Guy Briggs.
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ * RCSID $Id$
++ */
++
++#ifndef _IPSEC_RADIJ_H
++
++#include <openswan.h>
++
++int ipsec_walk(char *);
++
++int ipsec_rj_walker_procprint(struct radij_node *, void *);
++int ipsec_rj_walker_delete(struct radij_node *, void *);
++
++/* This structure is used to pass information between
++ * ipsec_eroute_get_info and ipsec_rj_walker_procprint
++ * (through rj_walktree) and between calls of ipsec_rj_walker_procprint.
++ */
++struct wsbuf
++{
++ /* from caller of ipsec_eroute_get_info: */
++ char *const buffer; /* start of buffer provided */
++ const int length; /* length of buffer provided */
++ const off_t offset; /* file position of first character of interest */
++ /* accumulated by ipsec_rj_walker_procprint: */
++ int len; /* number of character filled into buffer */
++ off_t begin; /* file position contained in buffer[0] (<=offset) */
++};
++
++extern struct radij_node_head *rnh;
++extern spinlock_t eroute_lock;
++
++struct eroute * ipsec_findroute(struct sockaddr_encap *);
++
++#define O1(x) (int)(((x)>>24)&0xff)
++#define O2(x) (int)(((x)>>16)&0xff)
++#define O3(x) (int)(((x)>>8)&0xff)
++#define O4(x) (int)(((x))&0xff)
++
++#ifdef CONFIG_IPSEC_DEBUG
++extern int debug_radij;
++void rj_dumptrees(void);
++
++#define DB_RJ_DUMPTREES 0x0001
++#define DB_RJ_FINDROUTE 0x0002
++#endif /* CONFIG_IPSEC_DEBUG */
++
++#define _IPSEC_RADIJ_H
++#endif
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.21 2004/04/29 11:06:42 ken
++ * Last bits from 2.06 procfs updates
++ *
++ * Revision 1.20 2004/04/06 02:49:08 mcr
++ * pullup of algo code from alg-branch.
++ *
++ * Revision 1.19 2004/04/05 19:55:06 mcr
++ * Moved from linux/include/freeswan/ipsec_radij.h,v
++ *
++ * Revision 1.18 2002/04/24 07:36:47 mcr
++ * Moved from ./klips/net/ipsec/ipsec_radij.h,v
++ *
++ * Revision 1.17 2001/11/26 09:23:49 rgb
++ * Merge MCR's ipsec_sa, eroute, proc and struct lifetime changes.
++ *
++ * Revision 1.16.2.1 2001/09/25 02:21:17 mcr
++ * ipsec_proto.h created to keep prototypes rather than deal with
++ * cyclic dependancies of structures and prototypes in .h files.
++ *
++ * Revision 1.16 2001/09/15 16:24:04 rgb
++ * Re-inject first and last HOLD packet when an eroute REPLACE is done.
++ *
++ * Revision 1.15 2001/09/14 16:58:37 rgb
++ * Added support for storing the first and last packets through a HOLD.
++ *
++ * Revision 1.14 2001/09/08 21:13:32 rgb
++ * Added pfkey ident extension support for ISAKMPd. (NetCelo)
++ *
++ * Revision 1.13 2001/06/14 19:35:09 rgb
++ * Update copyright date.
++ *
++ * Revision 1.12 2001/05/27 06:12:11 rgb
++ * Added structures for pid, packet count and last access time to eroute.
++ * Added packet count to beginning of /proc/net/ipsec_eroute.
++ *
++ * Revision 1.11 2000/09/08 19:12:56 rgb
++ * Change references from DEBUG_IPSEC to CONFIG_IPSEC_DEBUG.
++ *
++ * Revision 1.10 1999/11/17 15:53:39 rgb
++ * Changed all occurrences of #include "../../../lib/freeswan.h"
++ * to #include <freeswan.h> which works due to -Ilibfreeswan in the
++ * klips/net/ipsec/Makefile.
++ *
++ * Revision 1.9 1999/10/01 00:01:23 rgb
++ * Added eroute structure locking.
++ *
++ * Revision 1.8 1999/04/11 00:28:59 henry
++ * GPL boilerplate
++ *
++ * Revision 1.7 1999/04/06 04:54:26 rgb
++ * Fix/Add RCSID Id: and Log: bits to make PHMDs happy. This includes
++ * patch shell fixes.
++ *
++ * Revision 1.6 1999/01/22 06:23:26 rgb
++ * Cruft clean-out.
++ *
++ * Revision 1.5 1998/10/25 02:42:08 rgb
++ * Change return type on ipsec_breakroute and ipsec_makeroute and add an
++ * argument to be able to transmit more infomation about errors.
++ *
++ * Revision 1.4 1998/10/19 14:44:29 rgb
++ * Added inclusion of freeswan.h.
++ * sa_id structure implemented and used: now includes protocol.
++ *
++ * Revision 1.3 1998/07/28 00:03:31 rgb
++ * Comment out temporary inet_nto4u() kluge.
++ *
++ * Revision 1.2 1998/07/14 18:22:00 rgb
++ * Add function to clear the eroute table.
++ *
++ * Revision 1.1 1998/06/18 21:27:49 henry
++ * move sources from klips/src to klips/net/ipsec, to keep stupid
++ * kernel-build scripts happier in the presence of symlinks
++ *
++ * Revision 1.5 1998/05/25 20:30:38 rgb
++ * Remove temporary ipsec_walk, rj_deltree and rj_delnodes functions.
++ *
++ * Rename ipsec_rj_walker (ipsec_walk) to ipsec_rj_walker_procprint and
++ * add ipsec_rj_walker_delete.
++ *
++ * Revision 1.4 1998/05/21 13:02:56 rgb
++ * Imported definitions from ipsec_radij.c and radij.c to support /proc 3k
++ * limit fix.
++ *
++ * Revision 1.3 1998/04/21 21:29:09 rgb
++ * Rearrange debug switches to change on the fly debug output from user
++ * space. Only kernel changes checked in at this time. radij.c was also
++ * changed to temporarily remove buggy debugging code in rj_delete causing
++ * an OOPS and hence, netlink device open errors.
++ *
++ * Revision 1.2 1998/04/14 17:30:39 rgb
++ * Fix up compiling errors for radij tree memory reclamation.
++ *
++ * Revision 1.1 1998/04/09 03:06:10 henry
++ * sources moved up from linux/net/ipsec
++ *
++ * Revision 1.1.1.1 1998/04/08 05:35:04 henry
++ * RGB's ipsec-0.8pre2.tar.gz ipsec-0.8
++ *
++ * Revision 0.4 1997/01/15 01:28:15 ji
++ * No changes.
++ *
++ * Revision 0.3 1996/11/20 14:39:04 ji
++ * Minor cleanups.
++ * Rationalized debugging code.
++ *
++ * Revision 0.2 1996/11/02 00:18:33 ji
++ * First limited release.
++ *
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/include/openswan/ipsec_rcv.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,279 @@
++/*
++ *
++ * Copyright (C) 1996, 1997 John Ioannidis.
++ * Copyright (C) 1998, 1999, 2000, 2001 Richard Guy Briggs.
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ * RCSID $Id$
++ */
++
++#ifndef IPSEC_RCV_H
++#define IPSEC_RCV_H
++
++#include "openswan/ipsec_auth.h"
++
++#define DB_RX_PKTRX 0x0001
++#define DB_RX_PKTRX2 0x0002
++#define DB_RX_DMP 0x0004
++#define DB_RX_IPSA 0x0010
++#define DB_RX_XF 0x0020
++#define DB_RX_IPAD 0x0040
++#define DB_RX_INAU 0x0080
++#define DB_RX_OINFO 0x0100
++#define DB_RX_OINFO2 0x0200
++#define DB_RX_OH 0x0400
++#define DB_RX_REPLAY 0x0800
++
++#ifdef __KERNEL__
++/* struct options; */
++
++#define __NO_VERSION__
++#include <linux/module.h>
++#include <linux/config.h> /* for CONFIG_IP_FORWARD */
++#include <linux/version.h>
++#include <openswan.h>
++
++#define IPSEC_BIRTH_TEMPLATE_MAXLEN 256
++
++struct ipsec_birth_reply {
++ int packet_template_len;
++ unsigned char packet_template[IPSEC_BIRTH_TEMPLATE_MAXLEN];
++};
++
++extern struct ipsec_birth_reply ipsec_ipv4_birth_packet;
++extern struct ipsec_birth_reply ipsec_ipv6_birth_packet;
++
++enum ipsec_rcv_value {
++ IPSEC_RCV_LASTPROTO=1,
++ IPSEC_RCV_OK=0,
++ IPSEC_RCV_BADPROTO=-1,
++ IPSEC_RCV_BADLEN=-2,
++ IPSEC_RCV_ESP_BADALG=-3,
++ IPSEC_RCV_3DES_BADBLOCKING=-4,
++ IPSEC_RCV_ESP_DECAPFAIL=-5,
++ IPSEC_RCV_DECAPFAIL=-6,
++ IPSEC_RCV_SAIDNOTFOUND=-7,
++ IPSEC_RCV_IPCOMPALONE=-8,
++ IPSEC_RCV_IPCOMPFAILED=-10,
++ IPSEC_RCV_SAIDNOTLIVE=-11,
++ IPSEC_RCV_FAILEDINBOUND=-12,
++ IPSEC_RCV_LIFETIMEFAILED=-13,
++ IPSEC_RCV_BADAUTH=-14,
++ IPSEC_RCV_REPLAYFAILED=-15,
++ IPSEC_RCV_AUTHFAILED=-16,
++ IPSEC_RCV_REPLAYROLLED=-17,
++ IPSEC_RCV_BAD_DECRYPT=-18
++};
++
++struct ipsec_rcv_state {
++ struct sk_buff *skb;
++ struct net_device_stats *stats;
++ struct iphdr *ipp;
++ struct ipsec_sa *ipsp;
++ int len;
++ int ilen;
++ int authlen;
++ int hard_header_len;
++ int iphlen;
++ struct auth_alg *authfuncs;
++ ip_said said;
++ char sa[SATOT_BUF];
++ size_t sa_len;
++ __u8 next_header;
++ __u8 hash[AH_AMAX];
++ char ipsaddr_txt[ADDRTOA_BUF];
++ char ipdaddr_txt[ADDRTOA_BUF];
++ __u8 *octx;
++ __u8 *ictx;
++ int ictx_len;
++ int octx_len;
++ union {
++ struct {
++ struct esphdr *espp;
++ } espstuff;
++ struct {
++ struct ahhdr *ahp;
++ } ahstuff;
++ struct {
++ struct ipcomphdr *compp;
++ } ipcompstuff;
++ } protostuff;
++#ifdef CONFIG_IPSEC_NAT_TRAVERSAL
++ __u8 natt_type;
++ __u16 natt_sport;
++ __u16 natt_dport;
++ int natt_len;
++#endif
++};
++
++extern int
++#ifdef PROTO_HANDLER_SINGLE_PARM
++ipsec_rcv(struct sk_buff *skb);
++#else /* PROTO_HANDLER_SINGLE_PARM */
++ipsec_rcv(struct sk_buff *skb,
++#ifdef NET_21
++ unsigned short xlen);
++#else /* NET_21 */
++ struct device *dev,
++ struct options *opt,
++ __u32 daddr,
++ unsigned short len,
++ __u32 saddr,
++ int redo,
++ struct inet_protocol *protocol);
++#endif /* NET_21 */
++#endif /* PROTO_HANDLER_SINGLE_PARM */
++
++#ifdef CONFIG_IPSEC_DEBUG
++extern int debug_rcv;
++#endif /* CONFIG_IPSEC_DEBUG */
++#define ipsec_rcv_dmp(_x,_y, _z) if (debug_rcv && sysctl_ipsec_debug_verbose) ipsec_dmp(_x,_y,_z)
++
++extern int sysctl_ipsec_inbound_policy_check;
++#endif /* __KERNEL__ */
++
++#endif /* IPSEC_RCV_H */
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.21 2004/04/06 02:49:08 mcr
++ * pullup of algo code from alg-branch.
++ *
++ * Revision 1.20 2004/04/05 19:55:06 mcr
++ * Moved from linux/include/freeswan/ipsec_rcv.h,v
++ *
++ * Revision 1.19 2003/12/15 18:13:09 mcr
++ * when compiling with NAT traversal, don't assume that the
++ * kernel has been patched, unless CONFIG_IPSEC_NAT_NON_ESP
++ * is set.
++ *
++ * Revision 1.18 2003/12/13 19:10:16 mcr
++ * refactored rcv and xmit code - same as FS 2.05.
++ *
++ * Revision 1.17 2002/09/03 16:32:32 mcr
++ * definitions of ipsec_birth_reply.
++ *
++ * Revision 1.16 2002/05/14 02:36:00 rgb
++ * Change references to _TDB to _IPSA.
++ *
++ * Revision 1.15 2002/04/24 07:36:47 mcr
++ * Moved from ./klips/net/ipsec/ipsec_rcv.h,v
++ *
++ * Revision 1.14 2001/09/07 22:15:48 rgb
++ * Fix for removal of transport layer protocol handler arg in 2.4.4.
++ *
++ * Revision 1.13 2001/06/14 19:35:09 rgb
++ * Update copyright date.
++ *
++ * Revision 1.12 2001/03/16 07:36:44 rgb
++ * Fixed #endif comment to sate compiler.
++ *
++ * Revision 1.11 2000/09/21 04:34:21 rgb
++ * Moved declaration of sysctl_ipsec_inbound_policy_check outside
++ * CONFIG_IPSEC_DEBUG. (MB)
++ *
++ * Revision 1.10 2000/09/18 02:36:10 rgb
++ * Exported sysctl_ipsec_inbound_policy_check for skb_decompress().
++ *
++ * Revision 1.9 2000/09/08 19:12:56 rgb
++ * Change references from DEBUG_IPSEC to CONFIG_IPSEC_DEBUG.
++ *
++ * Revision 1.8 1999/11/18 04:09:19 rgb
++ * Replaced all kernel version macros to shorter, readable form.
++ *
++ * Revision 1.7 1999/05/25 01:45:37 rgb
++ * Fix version macros for 2.0.x as a module.
++ *
++ * Revision 1.6 1999/05/08 21:24:27 rgb
++ * Add includes for 2.2.x include into net/ipv4/protocol.c
++ *
++ * Revision 1.5 1999/05/05 22:02:32 rgb
++ * Add a quick and dirty port to 2.2 kernels by Marc Boucher <marc@mbsi.ca>.
++ *
++ * Revision 1.4 1999/04/11 00:28:59 henry
++ * GPL boilerplate
++ *
++ * Revision 1.3 1999/04/06 04:54:27 rgb
++ * Fix/Add RCSID Id: and Log: bits to make PHMDs happy. This includes
++ * patch shell fixes.
++ *
++ * Revision 1.2 1999/01/22 20:06:59 rgb
++ * Fixed cut-and-paste error from ipsec_esp.h.
++ *
++ * Revision 1.1 1999/01/21 20:29:12 rgb
++ * Converted from transform switching to algorithm switching.
++ *
++ * Log: ipsec_esp.h,v
++ * Revision 1.4 1998/08/12 00:07:32 rgb
++ * Added data structures for new xforms: null, {,3}dessha1.
++ *
++ * Revision 1.3 1998/07/14 15:57:01 rgb
++ * Add #ifdef __KERNEL__ to protect kernel-only structures.
++ *
++ * Revision 1.2 1998/06/25 19:33:46 rgb
++ * Add prototype for protocol receive function.
++ * Rearrange for more logical layout.
++ *
++ * Revision 1.1 1998/06/18 21:27:45 henry
++ * move sources from klips/src to klips/net/ipsec, to keep stupid
++ * kernel-build scripts happier in the presence of symlinks
++ *
++ * Revision 1.6 1998/06/05 02:28:08 rgb
++ * Minor comment fix.
++ *
++ * Revision 1.5 1998/05/27 22:34:00 rgb
++ * Changed structures to accomodate key separation.
++ *
++ * Revision 1.4 1998/05/18 22:28:43 rgb
++ * Disable key printing facilities from /proc/net/ipsec_*.
++ *
++ * Revision 1.3 1998/04/21 21:29:07 rgb
++ * Rearrange debug switches to change on the fly debug output from user
++ * space. Only kernel changes checked in at this time. radij.c was also
++ * changed to temporarily remove buggy debugging code in rj_delete causing
++ * an OOPS and hence, netlink device open errors.
++ *
++ * Revision 1.2 1998/04/12 22:03:20 rgb
++ * Updated ESP-3DES-HMAC-MD5-96,
++ * ESP-DES-HMAC-MD5-96,
++ * AH-HMAC-MD5-96,
++ * AH-HMAC-SHA1-96 since Henry started freeswan cvs repository
++ * from old standards (RFC182[5-9] to new (as of March 1998) drafts.
++ *
++ * Fixed eroute references in /proc/net/ipsec*.
++ *
++ * Started to patch module unloading memory leaks in ipsec_netlink and
++ * radij tree unloading.
++ *
++ * Revision 1.1 1998/04/09 03:06:00 henry
++ * sources moved up from linux/net/ipsec
++ *
++ * Revision 1.1.1.1 1998/04/08 05:35:02 henry
++ * RGB's ipsec-0.8pre2.tar.gz ipsec-0.8
++ *
++ * Revision 0.5 1997/06/03 04:24:48 ji
++ * Added ESP-3DES-MD5-96 transform.
++ *
++ * Revision 0.4 1997/01/15 01:28:15 ji
++ * Added definitions for new ESP transforms.
++ *
++ * Revision 0.3 1996/11/20 14:35:48 ji
++ * Minor Cleanup.
++ * Rationalized debugging code.
++ *
++ * Revision 0.2 1996/11/02 00:18:33 ji
++ * First limited release.
++ *
++ *
++ */
++
++
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/include/openswan/ipsec_sa.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,341 @@
++/*
++ * @(#) Definitions of IPsec Security Association (ipsec_sa)
++ *
++ * Copyright (C) 2001, 2002, 2003
++ * Richard Guy Briggs <rgb@freeswan.org>
++ * and Michael Richardson <mcr@freeswan.org>
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ * RCSID $Id$
++ *
++ * This file derived from ipsec_xform.h on 2001/9/18 by mcr.
++ *
++ */
++
++/*
++ * This file describes the IPsec Security Association Structure.
++ *
++ * This structure keeps track of a single transform that may be done
++ * to a set of packets. It can describe applying the transform or
++ * apply the reverse. (e.g. compression vs expansion). However, it
++ * only describes one at a time. To describe both, two structures would
++ * be used, but since the sides of the transform are performed
++ * on different machines typically it is usual to have only one side
++ * of each association.
++ *
++ */
++
++#ifndef _IPSEC_SA_H_
++
++#ifdef __KERNEL__
++#include "ipsec_stats.h"
++#include "ipsec_life.h"
++#include "ipsec_eroute.h"
++#endif /* __KERNEL__ */
++#include "ipsec_param.h"
++
++
++/* SAs are held in a table.
++ * Entries in this table are referenced by IPsecSAref_t values.
++ * IPsecSAref_t values are conceptually subscripts. Because
++ * we want to allocate the table piece-meal, the subscripting
++ * is implemented with two levels, a bit like paged virtual memory.
++ * This representation mechanism is known as an Iliffe Vector.
++ *
++ * The Main table (AKA the refTable) consists of 2^IPSEC_SA_REF_MAINTABLE_IDX_WIDTH
++ * pointers to subtables.
++ * Each subtable has 2^IPSEC_SA_REF_SUBTABLE_IDX_WIDTH entries, each of which
++ * is a pointer to an SA.
++ *
++ * An IPsecSAref_t contains either an exceptional value (signified by the
++ * high-order bit being on) or a reference to a table entry. A table entry
++ * reference has the subtable subscript in the low-order
++ * IPSEC_SA_REF_SUBTABLE_IDX_WIDTH bits and the Main table subscript
++ * in the next lowest IPSEC_SA_REF_MAINTABLE_IDX_WIDTH bits.
++ *
++ * The Maintable entry for an IPsecSAref_t x, a pointer to its subtable, is
++ * IPsecSAref2table(x). It is of type struct IPsecSArefSubTable *.
++ *
++ * The pointer to the SA for x is IPsecSAref2SA(x). It is of type
++ * struct ipsec_sa*. The macro definition clearly shows the two-level
++ * access needed to find the SA pointer.
++ *
++ * The Maintable is allocated when IPsec is initialized.
++ * Each subtable is allocated when needed, but the first is allocated
++ * when IPsec is initialized.
++ *
++ * IPsecSAref_t is designed to be smaller than an NFmark so that
++ * they can be stored in NFmarks and still leave a few bits for other
++ * purposes. The spare bits are in the low order of the NFmark
++ * but in the high order of the IPsecSAref_t, so conversion is required.
++ * We pick the upper bits of NFmark on the theory that they are less likely to
++ * interfere with more pedestrian uses of nfmark.
++ */
++
++
++typedef unsigned short int IPsecRefTableUnusedCount;
++
++#define IPSEC_SA_REF_TABLE_NUM_ENTRIES (1 << IPSEC_SA_REF_TABLE_IDX_WIDTH)
++
++#ifdef __KERNEL__
++#if ((IPSEC_SA_REF_TABLE_IDX_WIDTH - (1 + IPSEC_SA_REF_MAINTABLE_IDX_WIDTH)) < 0)
++#error "IPSEC_SA_REF_TABLE_IDX_WIDTH("IPSEC_SA_REF_TABLE_IDX_WIDTH") MUST be < 1 + IPSEC_SA_REF_MAINTABLE_IDX_WIDTH("IPSEC_SA_REF_MAINTABLE_IDX_WIDTH")"
++#endif
++
++#define IPSEC_SA_REF_SUBTABLE_IDX_WIDTH (IPSEC_SA_REF_TABLE_IDX_WIDTH - IPSEC_SA_REF_MAINTABLE_IDX_WIDTH)
++
++#define IPSEC_SA_REF_MAINTABLE_NUM_ENTRIES (1 << IPSEC_SA_REF_MAINTABLE_IDX_WIDTH)
++#define IPSEC_SA_REF_SUBTABLE_NUM_ENTRIES (1 << IPSEC_SA_REF_SUBTABLE_IDX_WIDTH)
++
++#ifdef CONFIG_NETFILTER
++#define IPSEC_SA_REF_HOST_FIELD(x) ((struct sk_buff*)(x))->nfmark
++#define IPSEC_SA_REF_HOST_FIELD_TYPE typeof(IPSEC_SA_REF_HOST_FIELD(NULL))
++#else /* CONFIG_NETFILTER */
++/* just make it work for now, it doesn't matter, since there is no nfmark */
++#define IPSEC_SA_REF_HOST_FIELD_TYPE unsigned long
++#endif /* CONFIG_NETFILTER */
++#define IPSEC_SA_REF_HOST_FIELD_WIDTH (8 * sizeof(IPSEC_SA_REF_HOST_FIELD_TYPE))
++#define IPSEC_SA_REF_FIELD_WIDTH (8 * sizeof(IPsecSAref_t))
++
++#define IPSEC_SA_REF_MASK (IPSEC_SAREF_NULL >> (IPSEC_SA_REF_FIELD_WIDTH - IPSEC_SA_REF_TABLE_IDX_WIDTH))
++#define IPSEC_SA_REF_TABLE_MASK ((IPSEC_SAREF_NULL >> (IPSEC_SA_REF_FIELD_WIDTH - IPSEC_SA_REF_MAINTABLE_IDX_WIDTH)) << IPSEC_SA_REF_SUBTABLE_IDX_WIDTH)
++#define IPSEC_SA_REF_ENTRY_MASK (IPSEC_SAREF_NULL >> (IPSEC_SA_REF_FIELD_WIDTH - IPSEC_SA_REF_SUBTABLE_IDX_WIDTH))
++
++#define IPsecSAref2table(x) (((x) & IPSEC_SA_REF_TABLE_MASK) >> IPSEC_SA_REF_SUBTABLE_IDX_WIDTH)
++#define IPsecSAref2entry(x) ((x) & IPSEC_SA_REF_ENTRY_MASK)
++#define IPsecSArefBuild(x,y) (((x) << IPSEC_SA_REF_SUBTABLE_IDX_WIDTH) + (y))
++
++#define IPsecSAref2SA(x) (ipsec_sadb.refTable[IPsecSAref2table(x)]->entry[IPsecSAref2entry(x)])
++#define IPsecSA2SAref(x) ((x)->ips_ref)
++
++#define EMT_INBOUND 0x01 /* SA direction, 1=inbound */
++
++/* 'struct ipsec_sa' should be 64bit aligned when allocated. */
++struct ipsec_sa
++{
++ IPsecSAref_t ips_ref; /* reference table entry number */
++ atomic_t ips_refcount; /* reference count for this struct */
++ struct ipsec_sa *ips_hnext; /* next in hash chain */
++ struct ipsec_sa *ips_inext; /* pointer to next xform */
++ struct ipsec_sa *ips_onext; /* pointer to prev xform */
++
++ struct ifnet *ips_rcvif; /* related rcv encap interface */
++
++ ip_said ips_said; /* SA ID */
++
++ __u32 ips_seq; /* seq num of msg that initiated this SA */
++ __u32 ips_pid; /* PID of process that initiated this SA */
++ __u8 ips_authalg; /* auth algorithm for this SA */
++ __u8 ips_encalg; /* enc algorithm for this SA */
++
++ struct ipsec_stats ips_errs;
++
++ __u8 ips_replaywin; /* replay window size */
++ __u8 ips_state; /* state of SA */
++ __u32 ips_replaywin_lastseq; /* last pkt sequence num */
++ __u64 ips_replaywin_bitmap; /* bitmap of received pkts */
++ __u32 ips_replaywin_maxdiff; /* max pkt sequence difference */
++
++ __u32 ips_flags; /* generic xform flags */
++
++
++ struct ipsec_lifetimes ips_life; /* lifetime records */
++
++ /* selector information */
++ __u8 ips_transport_protocol; /* protocol for this SA, if ports are involved */
++ struct sockaddr*ips_addr_s; /* src sockaddr */
++ struct sockaddr*ips_addr_d; /* dst sockaddr */
++ struct sockaddr*ips_addr_p; /* proxy sockaddr */
++ __u16 ips_addr_s_size;
++ __u16 ips_addr_d_size;
++ __u16 ips_addr_p_size;
++ ip_address ips_flow_s;
++ ip_address ips_flow_d;
++ ip_address ips_mask_s;
++ ip_address ips_mask_d;
++
++ __u16 ips_key_bits_a; /* size of authkey in bits */
++ __u16 ips_auth_bits; /* size of authenticator in bits */
++ __u16 ips_key_bits_e; /* size of enckey in bits */
++ __u16 ips_iv_bits; /* size of IV in bits */
++ __u8 ips_iv_size;
++ __u16 ips_key_a_size;
++ __u16 ips_key_e_size;
++
++ caddr_t ips_key_a; /* authentication key */
++ caddr_t ips_key_e; /* encryption key */
++ caddr_t ips_iv; /* Initialisation Vector */
++
++ struct ident ips_ident_s; /* identity src */
++ struct ident ips_ident_d; /* identity dst */
++
++#ifdef CONFIG_IPSEC_IPCOMP
++ __u16 ips_comp_adapt_tries; /* ipcomp self-adaption tries */
++ __u16 ips_comp_adapt_skip; /* ipcomp self-adaption to-skip */
++ __u64 ips_comp_ratio_cbytes; /* compressed bytes */
++ __u64 ips_comp_ratio_dbytes; /* decompressed (or uncompressed) bytes */
++#endif /* CONFIG_IPSEC_IPCOMP */
++
++#ifdef CONFIG_IPSEC_NAT_TRAVERSAL
++ __u8 ips_natt_type;
++ __u8 ips_natt_reserved[3];
++ __u16 ips_natt_sport;
++ __u16 ips_natt_dport;
++
++ struct sockaddr *ips_natt_oa;
++ __u16 ips_natt_oa_size;
++ __u16 ips_natt_reserved2;
++#endif
++
++#if 0
++ __u32 ips_sens_dpd;
++ __u8 ips_sens_sens_level;
++ __u8 ips_sens_sens_len;
++ __u64* ips_sens_sens_bitmap;
++ __u8 ips_sens_integ_level;
++ __u8 ips_sens_integ_len;
++ __u64* ips_sens_integ_bitmap;
++#endif
++ struct ipsec_alg_enc *ips_alg_enc;
++ struct ipsec_alg_auth *ips_alg_auth;
++ IPsecSAref_t ips_ref_rel;
++};
++
++struct IPsecSArefSubTable
++{
++ struct ipsec_sa* entry[IPSEC_SA_REF_SUBTABLE_NUM_ENTRIES];
++};
++
++struct ipsec_sadb {
++ struct IPsecSArefSubTable* refTable[IPSEC_SA_REF_MAINTABLE_NUM_ENTRIES];
++ IPsecSAref_t refFreeList[IPSEC_SA_REF_FREELIST_NUM_ENTRIES];
++ int refFreeListHead;
++ int refFreeListTail;
++ IPsecSAref_t refFreeListCont;
++ IPsecSAref_t said_hash[SADB_HASHMOD];
++ spinlock_t sadb_lock;
++};
++
++extern struct ipsec_sadb ipsec_sadb;
++
++extern int ipsec_SAref_recycle(void);
++extern int ipsec_SArefSubTable_alloc(unsigned table);
++extern int ipsec_saref_freelist_init(void);
++extern int ipsec_sadb_init(void);
++extern struct ipsec_sa *ipsec_sa_alloc(int*error); /* pass in error var by pointer */
++extern IPsecSAref_t ipsec_SAref_alloc(int*erorr); /* pass in error var by pointer */
++extern int ipsec_sa_free(struct ipsec_sa* ips);
++extern int ipsec_sa_put(struct ipsec_sa *ips);
++extern int ipsec_sa_add(struct ipsec_sa *ips);
++extern int ipsec_sa_del(struct ipsec_sa *ips);
++extern int ipsec_sa_delchain(struct ipsec_sa *ips);
++extern int ipsec_sadb_cleanup(__u8 proto);
++extern int ipsec_sadb_free(void);
++extern int ipsec_sa_wipe(struct ipsec_sa *ips);
++#endif /* __KERNEL__ */
++
++enum ipsec_direction {
++ ipsec_incoming = 1,
++ ipsec_outgoing = 2
++};
++
++#define _IPSEC_SA_H_
++#endif /* _IPSEC_SA_H_ */
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.19 2004/04/05 19:55:06 mcr
++ * Moved from linux/include/freeswan/ipsec_sa.h,v
++ *
++ * Revision 1.18 2004/04/05 19:41:05 mcr
++ * merged alg-branch code.
++ *
++ * Revision 1.17.2.1 2003/12/22 15:25:52 jjo
++ * . Merged algo-0.8.1-rc11-test1 into alg-branch
++ *
++ * Revision 1.17 2003/12/10 01:20:06 mcr
++ * NAT-traversal patches to KLIPS.
++ *
++ * Revision 1.16 2003/10/31 02:27:05 mcr
++ * pulled up port-selector patches and sa_id elimination.
++ *
++ * Revision 1.15.4.1 2003/10/29 01:10:19 mcr
++ * elimited "struct sa_id"
++ *
++ * Revision 1.15 2003/05/11 00:53:09 mcr
++ * IPsecSAref_t and macros were moved to freeswan.h.
++ *
++ * Revision 1.14 2003/02/12 19:31:55 rgb
++ * Fixed bug in "file seen" machinery.
++ * Updated copyright year.
++ *
++ * Revision 1.13 2003/01/30 02:31:52 rgb
++ *
++ * Re-wrote comments describing SAref system for accuracy.
++ * Rename SAref table macro names for clarity.
++ * Convert IPsecSAref_t from signed to unsigned to fix apparent SAref exhaustion bug.
++ * Transmit error code through to caller from callee for better diagnosis of problems.
++ * Enclose all macro arguments in parens to avoid any possible obscrure bugs.
++ *
++ * Revision 1.12 2002/10/07 18:31:19 rgb
++ * Change comment to reflect the flexible nature of the main and sub-table widths.
++ * Added a counter for the number of unused entries in each subtable.
++ * Further break up host field type macro to host field.
++ * Move field width sanity checks to ipsec_sa.c
++ * Define a mask for an entire saref.
++ *
++ * Revision 1.11 2002/09/20 15:40:33 rgb
++ * Re-write most of the SAref macros and types to eliminate any pointer references to Entrys.
++ * Fixed SAref/nfmark macros.
++ * Rework saref freeslist.
++ * Place all ipsec sadb globals into one struct.
++ * Restrict some bits to kernel context for use to klips utils.
++ *
++ * Revision 1.10 2002/09/20 05:00:34 rgb
++ * Update copyright date.
++ *
++ * Revision 1.9 2002/09/17 17:19:29 mcr
++ * make it compile even if there is no netfilter - we lost
++ * functionality, but it works, especially on 2.2.
++ *
++ * Revision 1.8 2002/07/28 22:59:53 mcr
++ * clarified/expanded one comment.
++ *
++ * Revision 1.7 2002/07/26 08:48:31 rgb
++ * Added SA ref table code.
++ *
++ * Revision 1.6 2002/05/31 17:27:48 rgb
++ * Comment fix.
++ *
++ * Revision 1.5 2002/05/27 18:55:03 rgb
++ * Remove final vistiges of tdb references via IPSEC_KLIPS1_COMPAT.
++ *
++ * Revision 1.4 2002/05/23 07:13:36 rgb
++ * Convert "usecount" to "refcount" to remove ambiguity.
++ *
++ * Revision 1.3 2002/04/24 07:36:47 mcr
++ * Moved from ./klips/net/ipsec/ipsec_sa.h,v
++ *
++ * Revision 1.2 2001/11/26 09:16:15 rgb
++ * Merge MCR's ipsec_sa, eroute, proc and struct lifetime changes.
++ *
++ * Revision 1.1.2.1 2001/09/25 02:24:58 mcr
++ * struct tdb -> struct ipsec_sa.
++ * sa(tdb) manipulation functions renamed and moved to ipsec_sa.c
++ * ipsec_xform.c removed. header file still contains useful things.
++ *
++ *
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/include/openswan/ipsec_sha1.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,79 @@
++/*
++ * RCSID $Id$
++ */
++
++/*
++ * Here is the original comment from the distribution:
++
++SHA-1 in C
++By Steve Reid <steve@edmweb.com>
++100% Public Domain
++
++ * Adapted for use by the IPSEC code by John Ioannidis
++ */
++
++
++#ifndef _IPSEC_SHA1_H_
++#define _IPSEC_SHA1_H_
++
++typedef struct
++{
++ __u32 state[5];
++ __u32 count[2];
++ __u8 buffer[64];
++} SHA1_CTX;
++
++void SHA1Transform(__u32 state[5], __u8 buffer[64]);
++void SHA1Init(void *context);
++void SHA1Update(void *context, unsigned char *data, __u32 len);
++void SHA1Final(unsigned char digest[20], void *context);
++
++
++#endif /* _IPSEC_SHA1_H_ */
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.8 2004/04/05 19:55:07 mcr
++ * Moved from linux/include/freeswan/ipsec_sha1.h,v
++ *
++ * Revision 1.7 2002/09/10 01:45:09 mcr
++ * changed type of MD5_CTX and SHA1_CTX to void * so that
++ * the function prototypes would match, and could be placed
++ * into a pointer to a function.
++ *
++ * Revision 1.6 2002/04/24 07:36:47 mcr
++ * Moved from ./klips/net/ipsec/ipsec_sha1.h,v
++ *
++ * Revision 1.5 1999/12/13 13:59:13 rgb
++ * Quick fix to argument size to Update bugs.
++ *
++ * Revision 1.4 1999/12/07 18:16:23 rgb
++ * Fixed comments at end of #endif lines.
++ *
++ * Revision 1.3 1999/04/06 04:54:27 rgb
++ * Fix/Add RCSID Id: and Log: bits to make PHMDs happy. This includes
++ * patch shell fixes.
++ *
++ * Revision 1.2 1998/11/30 13:22:54 rgb
++ * Rationalised all the klips kernel file headers. They are much shorter
++ * now and won't conflict under RH5.2.
++ *
++ * Revision 1.1 1998/06/18 21:27:50 henry
++ * move sources from klips/src to klips/net/ipsec, to keep stupid
++ * kernel-build scripts happier in the presence of symlinks
++ *
++ * Revision 1.2 1998/04/23 20:54:05 rgb
++ * Fixed md5 and sha1 include file nesting issues, to be cleaned up when
++ * verified.
++ *
++ * Revision 1.1 1998/04/09 03:04:21 henry
++ * sources moved up from linux/net/ipsec
++ * these two include files modified not to include others except in kernel
++ *
++ * Revision 1.1.1.1 1998/04/08 05:35:04 henry
++ * RGB's ipsec-0.8pre2.tar.gz ipsec-0.8
++ *
++ * Revision 0.4 1997/01/15 01:28:15 ji
++ * New transform
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/include/openswan/ipsec_stats.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,75 @@
++/*
++ * @(#) definition of ipsec_stats structure
++ *
++ * Copyright (C) 2001 Richard Guy Briggs <rgb@freeswan.org>
++ * and Michael Richardson <mcr@freeswan.org>
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ * RCSID $Id$
++ *
++ */
++
++/*
++ * This file describes the errors/statistics that FreeSWAN collects.
++ */
++
++#ifndef _IPSEC_STATS_H_
++
++struct ipsec_stats {
++ __u32 ips_alg_errs; /* number of algorithm errors */
++ __u32 ips_auth_errs; /* # of authentication errors */
++ __u32 ips_encsize_errs; /* # of encryption size errors*/
++ __u32 ips_encpad_errs; /* # of encryption pad errors*/
++ __u32 ips_replaywin_errs; /* # of pkt sequence errors */
++};
++
++extern int ipsec_snprintf(char * buf, ssize_t size, const char *fmt, ...);
++
++#define _IPSEC_STATS_H_
++#endif /* _IPSEC_STATS_H_ */
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.6 2004/04/05 19:55:07 mcr
++ * Moved from linux/include/freeswan/ipsec_stats.h,v
++ *
++ * Revision 1.5 2004/04/05 19:41:05 mcr
++ * merged alg-branch code.
++ *
++ * Revision 1.4 2004/03/28 20:27:19 paul
++ * Included tested and confirmed fixes mcr made and dhr verified for
++ * snprint statements. Changed one other snprintf to use ipsec_snprintf
++ * so it wouldnt break compatibility with 2.0/2.2 kernels. Verified with
++ * dhr. (thanks dhr!)
++ *
++ * Revision 1.4 2004/03/24 01:58:31 mcr
++ * sprintf->snprintf for formatting into proc buffer.
++ *
++ * Revision 1.3.34.1 2004/04/05 04:30:46 mcr
++ * patches for alg-branch to compile/work with 2.x openswan
++ *
++ * Revision 1.3 2002/04/24 07:36:47 mcr
++ * Moved from ./klips/net/ipsec/ipsec_stats.h,v
++ *
++ * Revision 1.2 2001/11/26 09:16:16 rgb
++ * Merge MCR's ipsec_sa, eroute, proc and struct lifetime changes.
++ *
++ * Revision 1.1.2.1 2001/09/25 02:27:00 mcr
++ * statistics moved to seperate structure.
++ *
++ *
++ *
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/include/openswan/ipsec_tunnel.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,265 @@
++/*
++ * IPSEC tunneling code
++ * Copyright (C) 1996, 1997 John Ioannidis.
++ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 Richard Guy Briggs.
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ * RCSID $Id$
++ */
++
++
++#ifdef NET_21
++# define DEV_QUEUE_XMIT(skb, device, pri) {\
++ skb->dev = device; \
++ neigh_compat_output(skb); \
++ /* skb->dst->output(skb); */ \
++ }
++# define ICMP_SEND(skb_in, type, code, info, dev) \
++ icmp_send(skb_in, type, code, htonl(info))
++# define IP_SEND(skb, dev) \
++ ip_send(skb);
++#else /* NET_21 */
++# define DEV_QUEUE_XMIT(skb, device, pri) {\
++ dev_queue_xmit(skb, device, pri); \
++ }
++# define ICMP_SEND(skb_in, type, code, info, dev) \
++ icmp_send(skb_in, type, code, info, dev)
++# define IP_SEND(skb, dev) \
++ if(ntohs(iph->tot_len) > physmtu) { \
++ ip_fragment(NULL, skb, dev, 0); \
++ ipsec_kfree_skb(skb); \
++ } else { \
++ dev_queue_xmit(skb, dev, SOPRI_NORMAL); \
++ }
++#endif /* NET_21 */
++
++
++/*
++ * Heavily based on drivers/net/new_tunnel.c. Lots
++ * of ideas also taken from the 2.1.x version of drivers/net/shaper.c
++ */
++
++struct ipsectunnelconf
++{
++ __u32 cf_cmd;
++ union
++ {
++ char cfu_name[12];
++ } cf_u;
++#define cf_name cf_u.cfu_name
++};
++
++#define IPSEC_SET_DEV (SIOCDEVPRIVATE)
++#define IPSEC_DEL_DEV (SIOCDEVPRIVATE + 1)
++#define IPSEC_CLR_DEV (SIOCDEVPRIVATE + 2)
++
++#ifdef __KERNEL__
++#include <linux/version.h>
++#ifndef KERNEL_VERSION
++# define KERNEL_VERSION(x,y,z) (((x)<<16)+((y)<<8)+(z))
++#endif
++struct ipsecpriv
++{
++ struct sk_buff_head sendq;
++ struct device *dev;
++ struct wait_queue *wait_queue;
++ char locked;
++ int (*hard_start_xmit) (struct sk_buff *skb,
++ struct device *dev);
++ int (*hard_header) (struct sk_buff *skb,
++ struct device *dev,
++ unsigned short type,
++ void *daddr,
++ void *saddr,
++ unsigned len);
++#ifdef NET_21
++ int (*rebuild_header)(struct sk_buff *skb);
++#else /* NET_21 */
++ int (*rebuild_header)(void *buff, struct device *dev,
++ unsigned long raddr, struct sk_buff *skb);
++#endif /* NET_21 */
++ int (*set_mac_address)(struct device *dev, void *addr);
++#ifndef NET_21
++ void (*header_cache_bind)(struct hh_cache **hhp, struct device *dev,
++ unsigned short htype, __u32 daddr);
++#endif /* !NET_21 */
++ void (*header_cache_update)(struct hh_cache *hh, struct device *dev, unsigned char * haddr);
++ struct net_device_stats *(*get_stats)(struct device *dev);
++ struct net_device_stats mystats;
++ int mtu; /* What is the desired MTU? */
++};
++
++extern char ipsec_tunnel_c_version[];
++
++extern struct device *ipsecdevices[IPSEC_NUM_IF];
++
++int ipsec_tunnel_init_devices(void);
++
++/* void */ int ipsec_tunnel_cleanup_devices(void);
++
++extern /* void */ int ipsec_init(void);
++
++extern int ipsec_tunnel_start_xmit(struct sk_buff *skb, struct device *dev);
++
++#ifdef CONFIG_IPSEC_DEBUG
++extern int debug_tunnel;
++extern int sysctl_ipsec_debug_verbose;
++#endif /* CONFIG_IPSEC_DEBUG */
++#endif /* __KERNEL__ */
++
++#ifdef CONFIG_IPSEC_DEBUG
++#define DB_TN_INIT 0x0001
++#define DB_TN_PROCFS 0x0002
++#define DB_TN_XMIT 0x0010
++#define DB_TN_OHDR 0x0020
++#define DB_TN_CROUT 0x0040
++#define DB_TN_OXFS 0x0080
++#define DB_TN_REVEC 0x0100
++#endif /* CONFIG_IPSEC_DEBUG */
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.29 2004/04/05 19:55:07 mcr
++ * Moved from linux/include/freeswan/ipsec_tunnel.h,v
++ *
++ * Revision 1.28 2003/06/24 20:22:32 mcr
++ * added new global: ipsecdevices[] so that we can keep track of
++ * the ipsecX devices. They will be referenced with dev_hold(),
++ * so 2.2 may need this as well.
++ *
++ * Revision 1.27 2003/04/03 17:38:09 rgb
++ * Centralised ipsec_kfree_skb and ipsec_dev_{get,put}.
++ *
++ * Revision 1.26 2003/02/12 19:32:20 rgb
++ * Updated copyright year.
++ *
++ * Revision 1.25 2002/05/27 18:56:07 rgb
++ * Convert to dynamic ipsec device allocation.
++ *
++ * Revision 1.24 2002/04/24 07:36:48 mcr
++ * Moved from ./klips/net/ipsec/ipsec_tunnel.h,v
++ *
++ * Revision 1.23 2001/11/06 19:50:44 rgb
++ * Moved IP_SEND, ICMP_SEND, DEV_QUEUE_XMIT macros to ipsec_tunnel.h for
++ * use also by pfkey_v2_parser.c
++ *
++ * Revision 1.22 2001/09/15 16:24:05 rgb
++ * Re-inject first and last HOLD packet when an eroute REPLACE is done.
++ *
++ * Revision 1.21 2001/06/14 19:35:10 rgb
++ * Update copyright date.
++ *
++ * Revision 1.20 2000/09/15 11:37:02 rgb
++ * Merge in heavily modified Svenning Soerensen's <svenning@post5.tele.dk>
++ * IPCOMP zlib deflate code.
++ *
++ * Revision 1.19 2000/09/08 19:12:56 rgb
++ * Change references from DEBUG_IPSEC to CONFIG_IPSEC_DEBUG.
++ *
++ * Revision 1.18 2000/07/28 13:50:54 rgb
++ * Changed enet_statistics to net_device_stats and added back compatibility
++ * for pre-2.1.19.
++ *
++ * Revision 1.17 1999/11/19 01:12:15 rgb
++ * Purge unneeded proc_info prototypes, now that static linking uses
++ * dynamic proc_info registration.
++ *
++ * Revision 1.16 1999/11/18 18:51:00 rgb
++ * Changed all device registrations for static linking to
++ * dynamic to reduce the number and size of patches.
++ *
++ * Revision 1.15 1999/11/18 04:14:21 rgb
++ * Replaced all kernel version macros to shorter, readable form.
++ * Added CONFIG_PROC_FS compiler directives in case it is shut off.
++ * Added Marc Boucher's 2.3.25 proc patches.
++ *
++ * Revision 1.14 1999/05/25 02:50:10 rgb
++ * Fix kernel version macros for 2.0.x static linking.
++ *
++ * Revision 1.13 1999/05/25 02:41:06 rgb
++ * Add ipsec_klipsdebug support for static linking.
++ *
++ * Revision 1.12 1999/05/05 22:02:32 rgb
++ * Add a quick and dirty port to 2.2 kernels by Marc Boucher <marc@mbsi.ca>.
++ *
++ * Revision 1.11 1999/04/29 15:19:50 rgb
++ * Add return values to init and cleanup functions.
++ *
++ * Revision 1.10 1999/04/16 16:02:39 rgb
++ * Bump up macro to 4 ipsec I/Fs.
++ *
++ * Revision 1.9 1999/04/15 15:37:25 rgb
++ * Forward check changes from POST1_00 branch.
++ *
++ * Revision 1.5.2.1 1999/04/02 04:26:14 rgb
++ * Backcheck from HEAD, pre1.0.
++ *
++ * Revision 1.8 1999/04/11 00:29:01 henry
++ * GPL boilerplate
++ *
++ * Revision 1.7 1999/04/06 04:54:28 rgb
++ * Fix/Add RCSID Id: and Log: bits to make PHMDs happy. This includes
++ * patch shell fixes.
++ *
++ * Revision 1.6 1999/03/31 05:44:48 rgb
++ * Keep PMTU reduction private.
++ *
++ * Revision 1.5 1999/02/10 22:31:20 rgb
++ * Change rebuild_header member to reflect generality of link layer.
++ *
++ * Revision 1.4 1998/12/01 13:22:04 rgb
++ * Added support for debug printing of version info.
++ *
++ * Revision 1.3 1998/07/29 20:42:46 rgb
++ * Add a macro for clearing all tunnel devices.
++ * Rearrange structures and declarations for sharing with userspace.
++ *
++ * Revision 1.2 1998/06/25 20:01:45 rgb
++ * Make prototypes available for ipsec_init and ipsec proc_dir_entries
++ * for static linking.
++ *
++ * Revision 1.1 1998/06/18 21:27:50 henry
++ * move sources from klips/src to klips/net/ipsec, to keep stupid
++ * kernel-build scripts happier in the presence of symlinks
++ *
++ * Revision 1.3 1998/05/18 21:51:50 rgb
++ * Added macros for num of I/F's and a procfs debug switch.
++ *
++ * Revision 1.2 1998/04/21 21:29:09 rgb
++ * Rearrange debug switches to change on the fly debug output from user
++ * space. Only kernel changes checked in at this time. radij.c was also
++ * changed to temporarily remove buggy debugging code in rj_delete causing
++ * an OOPS and hence, netlink device open errors.
++ *
++ * Revision 1.1 1998/04/09 03:06:13 henry
++ * sources moved up from linux/net/ipsec
++ *
++ * Revision 1.1.1.1 1998/04/08 05:35:05 henry
++ * RGB's ipsec-0.8pre2.tar.gz ipsec-0.8
++ *
++ * Revision 0.5 1997/06/03 04:24:48 ji
++ * Added transport mode.
++ * Changed the way routing is done.
++ * Lots of bug fixes.
++ *
++ * Revision 0.4 1997/01/15 01:28:15 ji
++ * No changes.
++ *
++ * Revision 0.3 1996/11/20 14:39:04 ji
++ * Minor cleanups.
++ * Rationalized debugging code.
++ *
++ * Revision 0.2 1996/11/02 00:18:33 ji
++ * First limited release.
++ *
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/include/openswan/ipsec_xform.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,254 @@
++/*
++ * Definitions relevant to IPSEC transformations
++ * Copyright (C) 1996, 1997 John Ioannidis.
++ * Copyright (C) 1998, 1999, 2000, 2001 Richard Guy Briggs.
++ * COpyright (C) 2003 Michael Richardson <mcr@sandelman.ottawa.on.ca>
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ * RCSID $Id$
++ */
++
++#ifndef _IPSEC_XFORM_H_
++
++#include <openswan.h>
++
++#define XF_NONE 0 /* No transform set */
++#define XF_IP4 1 /* IPv4 inside IPv4 */
++#define XF_AHMD5 2 /* AH MD5 */
++#define XF_AHSHA 3 /* AH SHA */
++#define XF_ESP3DES 5 /* ESP DES3-CBC */
++#define XF_AHHMACMD5 6 /* AH-HMAC-MD5 with opt replay prot */
++#define XF_AHHMACSHA1 7 /* AH-HMAC-SHA1 with opt replay prot */
++#define XF_ESP3DESMD5 9 /* triple DES, HMAC-MD-5, 128-bits of authentication */
++#define XF_ESP3DESMD596 10 /* triple DES, HMAC-MD-5, 96-bits of authentication */
++#define XF_ESPNULLMD596 12 /* NULL, HMAC-MD-5 with 96-bits of authentication */
++#define XF_ESPNULLSHA196 13 /* NULL, HMAC-SHA-1 with 96-bits of authentication */
++#define XF_ESP3DESSHA196 14 /* triple DES, HMAC-SHA-1, 96-bits of authentication */
++#define XF_IP6 15 /* IPv6 inside IPv6 */
++#define XF_COMPDEFLATE 16 /* IPCOMP deflate */
++
++#define XF_CLR 126 /* Clear SA table */
++#define XF_DEL 127 /* Delete SA */
++
++/* IPsec AH transform values
++ * RFC 2407
++ * draft-ietf-ipsec-doi-tc-mib-02.txt
++ */
++
++#define AH_NONE 0
++#define AH_MD5 2
++#define AH_SHA 3
++/* draft-ietf-ipsec-ciph-aes-cbc-03.txt */
++#define AH_SHA2_256 5
++#define AH_SHA2_384 6
++#define AH_SHA2_512 7
++#define AH_RIPEMD 8
++#define AH_MAX 15
++
++/* IPsec ESP transform values */
++
++#define ESP_NONE 0
++#define ESP_DES 2
++#define ESP_3DES 3
++#define ESP_RC5 4
++#define ESP_IDEA 5
++#define ESP_CAST 6
++#define ESP_BLOWFISH 7
++#define ESP_3IDEA 8
++#define ESP_RC4 10
++#define ESP_NULL 11
++#define ESP_AES 12
++
++/* as draft-ietf-ipsec-ciph-aes-cbc-02.txt */
++#define ESP_MARS 249
++#define ESP_RC6 250
++#define ESP_SERPENT 252
++#define ESP_TWOFISH 253
++
++/* IPCOMP transform values */
++
++#define IPCOMP_NONE 0
++#define IPCOMP_OUI 1
++#define IPCOMP_DEFLAT 2
++#define IPCOMP_LZS 3
++#define IPCOMP_V42BIS 4
++
++#define XFT_AUTH 0x0001
++#define XFT_CONF 0x0100
++
++/* available if CONFIG_IPSEC_DEBUG is defined */
++#define DB_XF_INIT 0x0001
++
++#define PROTO2TXT(x) \
++ (x) == IPPROTO_AH ? "AH" : \
++ (x) == IPPROTO_ESP ? "ESP" : \
++ (x) == IPPROTO_IPIP ? "IPIP" : \
++ (x) == IPPROTO_COMP ? "COMP" : \
++ "UNKNOWN_proto"
++static inline const char *enc_name_id (unsigned id) {
++ static char buf[16];
++ snprintf(buf, sizeof(buf), "_ID%d", id);
++ return buf;
++}
++static inline const char *auth_name_id (unsigned id) {
++ static char buf[16];
++ snprintf(buf, sizeof(buf), "_ID%d", id);
++ return buf;
++}
++#define IPS_XFORM_NAME(x) \
++ PROTO2TXT((x)->ips_said.proto), \
++ (x)->ips_said.proto == IPPROTO_COMP ? \
++ ((x)->ips_encalg == SADB_X_CALG_DEFLATE ? \
++ "_DEFLATE" : "_UNKNOWN_comp") : \
++ (x)->ips_encalg == ESP_NONE ? "" : \
++ (x)->ips_encalg == ESP_3DES ? "_3DES" : \
++ (x)->ips_encalg == ESP_AES ? "_AES" : \
++ (x)->ips_encalg == ESP_SERPENT ? "_SERPENT" : \
++ (x)->ips_encalg == ESP_TWOFISH ? "_TWOFISH" : \
++ enc_name_id(x->ips_encalg)/* "_UNKNOWN_encr" */, \
++ (x)->ips_authalg == AH_NONE ? "" : \
++ (x)->ips_authalg == AH_MD5 ? "_HMAC_MD5" : \
++ (x)->ips_authalg == AH_SHA ? "_HMAC_SHA1" : \
++ (x)->ips_authalg == AH_SHA2_256 ? "_HMAC_SHA2_256" : \
++ (x)->ips_authalg == AH_SHA2_384 ? "_HMAC_SHA2_384" : \
++ (x)->ips_authalg == AH_SHA2_512 ? "_HMAC_SHA2_512" : \
++ auth_name_id(x->ips_authalg) /* "_UNKNOWN_auth" */ \
++
++#ifdef __KERNEL__
++struct ipsec_rcv_state;
++struct ipsec_xmit_state;
++
++struct xform_functions {
++ enum ipsec_rcv_value (*rcv_checks)(struct ipsec_rcv_state *irs,
++ struct sk_buff *skb);
++ enum ipsec_rcv_value (*rcv_decrypt)(struct ipsec_rcv_state *irs);
++
++ enum ipsec_rcv_value (*rcv_setup_auth)(struct ipsec_rcv_state *irs,
++ struct sk_buff *skb,
++ __u32 *replay,
++ unsigned char **authenticator);
++ enum ipsec_rcv_value (*rcv_calc_auth)(struct ipsec_rcv_state *irs,
++ struct sk_buff *skb);
++
++ enum ipsec_xmit_value (*xmit_setup)(struct ipsec_xmit_state *ixs);
++ enum ipsec_xmit_value (*xmit_encrypt)(struct ipsec_xmit_state *ixs);
++
++ enum ipsec_xmit_value (*xmit_setup_auth)(struct ipsec_xmit_state *ixs,
++ struct sk_buff *skb,
++ __u32 *replay,
++ unsigned char **authenticator);
++ enum ipsec_xmit_value (*xmit_calc_auth)(struct ipsec_xmit_state *ixs,
++ struct sk_buff *skb);
++ int xmit_headroom;
++ int xmit_needtailroom;
++};
++
++#endif /* __KERNEL__ */
++
++#ifdef CONFIG_IPSEC_DEBUG
++extern void ipsec_dmp(char *s, caddr_t bb, int len);
++#else /* CONFIG_IPSEC_DEBUG */
++#define ipsec_dmp(_x, _y, _z)
++#endif /* CONFIG_IPSEC_DEBUG */
++
++
++#define _IPSEC_XFORM_H_
++#endif /* _IPSEC_XFORM_H_ */
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.40 2004/04/06 02:49:08 mcr
++ * pullup of algo code from alg-branch.
++ *
++ * Revision 1.39 2004/04/05 19:55:07 mcr
++ * Moved from linux/include/freeswan/ipsec_xform.h,v
++ *
++ * Revision 1.38 2004/04/05 19:41:05 mcr
++ * merged alg-branch code.
++ *
++ * Revision 1.37 2003/12/13 19:10:16 mcr
++ * refactored rcv and xmit code - same as FS 2.05.
++ *
++ * Revision 1.36.34.1 2003/12/22 15:25:52 jjo
++ * Merged algo-0.8.1-rc11-test1 into alg-branch
++ *
++ * Revision 1.36 2002/04/24 07:36:48 mcr
++ * Moved from ./klips/net/ipsec/ipsec_xform.h,v
++ *
++ * Revision 1.35 2001/11/26 09:23:51 rgb
++ * Merge MCR's ipsec_sa, eroute, proc and struct lifetime changes.
++ *
++ * Revision 1.33.2.1 2001/09/25 02:24:58 mcr
++ * struct tdb -> struct ipsec_sa.
++ * sa(tdb) manipulation functions renamed and moved to ipsec_sa.c
++ * ipsec_xform.c removed. header file still contains useful things.
++ *
++ * Revision 1.34 2001/11/06 19:47:17 rgb
++ * Changed lifetime_packets to uint32 from uint64.
++ *
++ * Revision 1.33 2001/09/08 21:13:34 rgb
++ * Added pfkey ident extension support for ISAKMPd. (NetCelo)
++ *
++ * Revision 1.32 2001/07/06 07:40:01 rgb
++ * Reformatted for readability.
++ * Added inbound policy checking fields for use with IPIP SAs.
++ *
++ * Revision 1.31 2001/06/14 19:35:11 rgb
++ * Update copyright date.
++ *
++ * Revision 1.30 2001/05/30 08:14:03 rgb
++ * Removed vestiges of esp-null transforms.
++ *
++ * Revision 1.29 2001/01/30 23:42:47 rgb
++ * Allow pfkey msgs from pid other than user context required for ACQUIRE
++ * and subsequent ADD or UDATE.
++ *
++ * Revision 1.28 2000/11/06 04:30:40 rgb
++ * Add Svenning's adaptive content compression.
++ *
++ * Revision 1.27 2000/09/19 00:38:25 rgb
++ * Fixed algorithm name bugs introduced for ipcomp.
++ *
++ * Revision 1.26 2000/09/17 21:36:48 rgb
++ * Added proto2txt macro.
++ *
++ * Revision 1.25 2000/09/17 18:56:47 rgb
++ * Added IPCOMP support.
++ *
++ * Revision 1.24 2000/09/12 19:34:12 rgb
++ * Defined XF_IP6 from Gerhard for ipv6 tunnel support.
++ *
++ * Revision 1.23 2000/09/12 03:23:14 rgb
++ * Cleaned out now unused tdb_xform and tdb_xdata members of struct tdb.
++ *
++ * Revision 1.22 2000/09/08 19:12:56 rgb
++ * Change references from DEBUG_IPSEC to CONFIG_IPSEC_DEBUG.
++ *
++ * Revision 1.21 2000/09/01 18:32:43 rgb
++ * Added (disabled) sensitivity members to tdb struct.
++ *
++ * Revision 1.20 2000/08/30 05:31:01 rgb
++ * Removed all the rest of the references to tdb_spi, tdb_proto, tdb_dst.
++ * Kill remainder of tdb_xform, tdb_xdata, xformsw.
++ *
++ * Revision 1.19 2000/08/01 14:51:52 rgb
++ * Removed _all_ remaining traces of DES.
++ *
++ * Revision 1.18 2000/01/21 06:17:45 rgb
++ * Tidied up spacing.
++ *
++ *
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/include/openswan/ipsec_xmit.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,176 @@
++/*
++ * IPSEC tunneling code
++ * Copyright (C) 1996, 1997 John Ioannidis.
++ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 Richard Guy Briggs.
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ * RCSID $Id$
++ */
++
++#include "openswan/ipsec_sa.h"
++
++enum ipsec_xmit_value
++{
++ IPSEC_XMIT_STOLEN=2,
++ IPSEC_XMIT_PASS=1,
++ IPSEC_XMIT_OK=0,
++ IPSEC_XMIT_ERRMEMALLOC=-1,
++ IPSEC_XMIT_ESP_BADALG=-2,
++ IPSEC_XMIT_BADPROTO=-3,
++ IPSEC_XMIT_ESP_PUSHPULLERR=-4,
++ IPSEC_XMIT_BADLEN=-5,
++ IPSEC_XMIT_AH_BADALG=-6,
++ IPSEC_XMIT_SAIDNOTFOUND=-7,
++ IPSEC_XMIT_SAIDNOTLIVE=-8,
++ IPSEC_XMIT_REPLAYROLLED=-9,
++ IPSEC_XMIT_LIFETIMEFAILED=-10,
++ IPSEC_XMIT_CANNOTFRAG=-11,
++ IPSEC_XMIT_MSSERR=-12,
++ IPSEC_XMIT_ERRSKBALLOC=-13,
++ IPSEC_XMIT_ENCAPFAIL=-14,
++ IPSEC_XMIT_NODEV=-15,
++ IPSEC_XMIT_NOPRIVDEV=-16,
++ IPSEC_XMIT_NOPHYSDEV=-17,
++ IPSEC_XMIT_NOSKB=-18,
++ IPSEC_XMIT_NOIPV6=-19,
++ IPSEC_XMIT_NOIPOPTIONS=-20,
++ IPSEC_XMIT_TTLEXPIRED=-21,
++ IPSEC_XMIT_BADHHLEN=-22,
++ IPSEC_XMIT_PUSHPULLERR=-23,
++ IPSEC_XMIT_ROUTEERR=-24,
++ IPSEC_XMIT_RECURSDETECT=-25,
++ IPSEC_XMIT_IPSENDFAILURE=-26,
++ IPSEC_XMIT_ESPUDP=-27,
++ IPSEC_XMIT_ESPUDP_BADTYPE=-28,
++};
++
++struct ipsec_xmit_state
++{
++ struct sk_buff *skb; /* working skb pointer */
++ struct device *dev; /* working dev pointer */
++ struct ipsecpriv *prv; /* Our device' private space */
++ struct sk_buff *oskb; /* Original skb pointer */
++ struct net_device_stats *stats; /* This device's statistics */
++ struct iphdr *iph; /* Our new IP header */
++ __u32 newdst; /* The other SG's IP address */
++ __u32 orgdst; /* Original IP destination address */
++ __u32 orgedst; /* 1st SG's IP address */
++ __u32 newsrc; /* The new source SG's IP address */
++ __u32 orgsrc; /* Original IP source address */
++ __u32 innersrc; /* Innermost IP source address */
++ int iphlen; /* IP header length */
++ int pyldsz; /* upper protocol payload size */
++ int headroom;
++ int tailroom;
++ int authlen;
++ int max_headroom; /* The extra header space needed */
++ int max_tailroom; /* The extra stuffing needed */
++ int ll_headroom; /* The extra link layer hard_header space needed */
++ int tot_headroom; /* The total header space needed */
++ int tot_tailroom; /* The totalstuffing needed */
++ __u8 *saved_header; /* saved copy of the hard header */
++ unsigned short sport, dport;
++
++ struct sockaddr_encap matcher; /* eroute search key */
++ struct eroute *eroute;
++ struct ipsec_sa *ipsp, *ipsq; /* ipsec_sa pointers */
++ char sa_txt[SATOT_BUF];
++ size_t sa_len;
++ int hard_header_stripped; /* has the hard header been removed yet? */
++ int hard_header_len;
++ struct device *physdev;
++/* struct device *virtdev; */
++ short physmtu;
++ short mtudiff;
++#ifdef NET_21
++ struct rtable *route;
++#endif /* NET_21 */
++ ip_said outgoing_said;
++#ifdef NET_21
++ int pass;
++#endif /* NET_21 */
++ int error;
++ uint32_t eroute_pid;
++ struct ipsec_sa ips;
++#ifdef CONFIG_IPSEC_NAT_TRAVERSAL
++ uint8_t natt_type;
++ uint8_t natt_head;
++ uint16_t natt_sport;
++ uint16_t natt_dport;
++#endif
++};
++
++enum ipsec_xmit_value
++ipsec_xmit_sanity_check_dev(struct ipsec_xmit_state *ixs);
++
++enum ipsec_xmit_value
++ipsec_xmit_sanity_check_skb(struct ipsec_xmit_state *ixs);
++
++enum ipsec_xmit_value
++ipsec_xmit_encap_bundle(struct ipsec_xmit_state *ixs);
++
++extern void ipsec_extract_ports(struct iphdr * iph, struct sockaddr_encap * er);
++
++
++extern int ipsec_xmit_trap_count;
++extern int ipsec_xmit_trap_sendcount;
++
++#ifdef CONFIG_IPSEC_DEBUG
++extern int debug_tunnel;
++extern int sysctl_ipsec_debug_verbose;
++#endif /* CONFIG_IPSEC_DEBUG */
++
++#define debug_xmit debug_tunnel
++
++#define ipsec_xmit_dmp(_x,_y, _z) if (debug_xmit && sysctl_ipsec_debug_verbose) ipsec_dmp(_x,_y,_z)
++
++extern int sysctl_ipsec_icmp;
++extern int sysctl_ipsec_tos;
++
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.9 2004/04/06 02:49:08 mcr
++ * pullup of algo code from alg-branch.
++ *
++ * Revision 1.8 2004/04/05 19:55:07 mcr
++ * Moved from linux/include/freeswan/ipsec_xmit.h,v
++ *
++ * Revision 1.7 2004/02/03 03:11:40 mcr
++ * new xmit type if the UDP encapsulation is wrong.
++ *
++ * Revision 1.6 2003/12/13 19:10:16 mcr
++ * refactored rcv and xmit code - same as FS 2.05.
++ *
++ * Revision 1.5 2003/12/10 01:20:06 mcr
++ * NAT-traversal patches to KLIPS.
++ *
++ * Revision 1.4 2003/12/06 16:37:04 mcr
++ * 1.4.7a X.509 patch applied.
++ *
++ * Revision 1.3 2003/10/31 02:27:05 mcr
++ * pulled up port-selector patches and sa_id elimination.
++ *
++ * Revision 1.2.4.2 2003/10/29 01:10:19 mcr
++ * elimited "struct sa_id"
++ *
++ * Revision 1.2.4.1 2003/09/21 13:59:38 mcr
++ * pre-liminary X.509 patch - does not yet pass tests.
++ *
++ * Revision 1.2 2003/06/20 01:42:13 mcr
++ * added counters to measure how many ACQUIREs we send to pluto,
++ * and how many are successfully sent.
++ *
++ * Revision 1.1 2003/02/12 19:31:03 rgb
++ * Refactored from ipsec_tunnel.c
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/include/openswan/passert.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,64 @@
++/*
++ * sanitize a string into a printable format.
++ *
++ * Copyright (C) 1998-2002 D. Hugh Redelmeier.
++ * Copyright (C) 2003 Michael Richardson <mcr@freeswan.org>
++ *
++ * This library is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU Library General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/lgpl.txt>.
++ *
++ * This library 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 Library General Public
++ * License for more details.
++ *
++ * RCSID $Id$
++ */
++
++#include "openswan.h"
++
++/* our versions of assert: log result */
++
++#ifdef DEBUG
++
++extern void passert_fail(const char *pred_str
++ , const char *file_str, unsigned long line_no) NEVER_RETURNS;
++
++extern void pexpect_log(const char *pred_str
++ , const char *file_str, unsigned long line_no);
++
++# define impossible() passert_fail("impossible", __FILE__, __LINE__)
++
++extern void switch_fail(int n
++ , const char *file_str, unsigned long line_no) NEVER_RETURNS;
++
++# define bad_case(n) switch_fail((int) n, __FILE__, __LINE__)
++
++# define passert(pred) { \
++ if (!(pred)) \
++ passert_fail(#pred, __FILE__, __LINE__); \
++ }
++
++# define pexpect(pred) { \
++ if (!(pred)) \
++ pexpect_log(#pred, __FILE__, __LINE__); \
++ }
++
++/* assert that an err_t is NULL; evaluate exactly once */
++# define happy(x) { \
++ err_t ugh = x; \
++ if (ugh != NULL) \
++ passert_fail(ugh, __FILE__, __LINE__); \
++ }
++
++#else /*!DEBUG*/
++
++# define impossible() abort()
++# define bad_case(n) abort()
++# define passert(pred) { } /* do nothing */
++# define happy(x) { (void) x; } /* evaluate non-judgementally */
++
++#endif /*!DEBUG*/
++
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/include/openswan/pfkey_debug.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,54 @@
++/*
++ * sanitize a string into a printable format.
++ *
++ * Copyright (C) 1998-2002 D. Hugh Redelmeier.
++ * Copyright (C) 2003 Michael Richardson <mcr@freeswan.org>
++ *
++ * This library is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU Library General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/lgpl.txt>.
++ *
++ * This library 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 Library General Public
++ * License for more details.
++ *
++ * RCSID $Id$
++ */
++
++#ifndef _FREESWAN_PFKEY_DEBUG_H
++#define _FREESWAN_PFKEY_DEBUG_H
++
++#ifdef __KERNEL__
++
++/* note, kernel version ignores pfkey levels */
++# define DEBUGGING(level,args...) \
++ KLIPS_PRINT(debug_pfkey, "klips_debug:" args)
++
++# define ERROR(args...) printk(KERN_ERR "klips:" args)
++
++#else
++
++extern unsigned int pfkey_lib_debug;
++
++extern void (*pfkey_debug_func)(const char *message, ...) PRINTF_LIKE(1);
++extern void (*pfkey_error_func)(const char *message, ...) PRINTF_LIKE(1);
++
++#define DEBUGGING(level,args...) if(pfkey_lib_debug & level) { \
++ if(pfkey_debug_func != NULL) { \
++ (*pfkey_debug_func)("pfkey_lib_debug:" args); \
++ } else { \
++ printf("pfkey_lib_debug:" args); \
++ } }
++
++#define ERROR(args...) if(pfkey_error_func != NULL) { \
++ (*pfkey_error_func)("pfkey_lib_debug:" args); \
++ }
++
++# define MALLOC(size) malloc(size)
++# define FREE(obj) free(obj)
++
++#endif
++
++#endif
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/include/openswan/radij.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,280 @@
++/*
++ * RCSID $Id$
++ */
++
++/*
++ * This file is defived from ${SRC}/sys/net/radix.h of BSD 4.4lite
++ *
++ * Variable and procedure names have been modified so that they don't
++ * conflict with the original BSD code, as a small number of modifications
++ * have been introduced and we may want to reuse this code in BSD.
++ *
++ * The `j' in `radij' is pronounced as a voiceless guttural (like a Greek
++ * chi or a German ch sound (as `doch', not as in `milch'), or even a
++ * spanish j as in Juan. It is not as far back in the throat like
++ * the corresponding Hebrew sound, nor is it a soft breath like the English h.
++ * It has nothing to do with the Dutch ij sound.
++ *
++ * Here is the appropriate copyright notice:
++ */
++
++/*
++ * Copyright (c) 1988, 1989, 1993
++ * The Regents of the University of California. All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ * 1. Redistributions of source code must retain the above copyright
++ * notice, this list of conditions and the following disclaimer.
++ * 2. Redistributions in binary form must reproduce the above copyright
++ * notice, this list of conditions and the following disclaimer in the
++ * documentation and/or other materials provided with the distribution.
++ * 3. All advertising materials mentioning features or use of this software
++ * must display the following acknowledgement:
++ * This product includes software developed by the University of
++ * California, Berkeley and its contributors.
++ * 4. Neither the name of the University nor the names of its contributors
++ * may be used to endorse or promote products derived from this software
++ * without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ *
++ * @(#)radix.h 8.1 (Berkeley) 6/10/93
++ */
++
++#ifndef _RADIJ_H_
++#define _RADIJ_H_
++
++/*
++#define RJ_DEBUG
++*/
++
++#ifdef __KERNEL__
++
++#ifndef __P
++#ifdef __STDC__
++#define __P(x) x
++#else
++#define __P(x) ()
++#endif
++#endif
++
++/*
++ * Radix search tree node layout.
++ */
++
++struct radij_node
++{
++ struct radij_mask *rj_mklist; /* list of masks contained in subtree */
++ struct radij_node *rj_p; /* parent */
++ short rj_b; /* bit offset; -1-index(netmask) */
++ char rj_bmask; /* node: mask for bit test*/
++ u_char rj_flags; /* enumerated next */
++#define RJF_NORMAL 1 /* leaf contains normal route */
++#define RJF_ROOT 2 /* leaf is root leaf for tree */
++#define RJF_ACTIVE 4 /* This node is alive (for rtfree) */
++ union {
++ struct { /* leaf only data: */
++ caddr_t rj_Key; /* object of search */
++ caddr_t rj_Mask; /* netmask, if present */
++ struct radij_node *rj_Dupedkey;
++ } rj_leaf;
++ struct { /* node only data: */
++ int rj_Off; /* where to start compare */
++ struct radij_node *rj_L;/* progeny */
++ struct radij_node *rj_R;/* progeny */
++ }rj_node;
++ } rj_u;
++#ifdef RJ_DEBUG
++ int rj_info;
++ struct radij_node *rj_twin;
++ struct radij_node *rj_ybro;
++#endif
++};
++
++#define rj_dupedkey rj_u.rj_leaf.rj_Dupedkey
++#define rj_key rj_u.rj_leaf.rj_Key
++#define rj_mask rj_u.rj_leaf.rj_Mask
++#define rj_off rj_u.rj_node.rj_Off
++#define rj_l rj_u.rj_node.rj_L
++#define rj_r rj_u.rj_node.rj_R
++
++/*
++ * Annotations to tree concerning potential routes applying to subtrees.
++ */
++
++extern struct radij_mask {
++ short rm_b; /* bit offset; -1-index(netmask) */
++ char rm_unused; /* cf. rj_bmask */
++ u_char rm_flags; /* cf. rj_flags */
++ struct radij_mask *rm_mklist; /* more masks to try */
++ caddr_t rm_mask; /* the mask */
++ int rm_refs; /* # of references to this struct */
++} *rj_mkfreelist;
++
++#define MKGet(m) {\
++ if (rj_mkfreelist) {\
++ m = rj_mkfreelist; \
++ rj_mkfreelist = (m)->rm_mklist; \
++ } else \
++ R_Malloc(m, struct radij_mask *, sizeof (*(m))); }\
++
++#define MKFree(m) { (m)->rm_mklist = rj_mkfreelist; rj_mkfreelist = (m);}
++
++struct radij_node_head {
++ struct radij_node *rnh_treetop;
++ int rnh_addrsize; /* permit, but not require fixed keys */
++ int rnh_pktsize; /* permit, but not require fixed keys */
++#if 0
++ struct radij_node *(*rnh_addaddr) /* add based on sockaddr */
++ __P((void *v, void *mask,
++ struct radij_node_head *head, struct radij_node nodes[]));
++#endif
++ int (*rnh_addaddr) /* add based on sockaddr */
++ __P((void *v, void *mask,
++ struct radij_node_head *head, struct radij_node nodes[]));
++ struct radij_node *(*rnh_addpkt) /* add based on packet hdr */
++ __P((void *v, void *mask,
++ struct radij_node_head *head, struct radij_node nodes[]));
++#if 0
++ struct radij_node *(*rnh_deladdr) /* remove based on sockaddr */
++ __P((void *v, void *mask, struct radij_node_head *head));
++#endif
++ int (*rnh_deladdr) /* remove based on sockaddr */
++ __P((void *v, void *mask, struct radij_node_head *head, struct radij_node **node));
++ struct radij_node *(*rnh_delpkt) /* remove based on packet hdr */
++ __P((void *v, void *mask, struct radij_node_head *head));
++ struct radij_node *(*rnh_matchaddr) /* locate based on sockaddr */
++ __P((void *v, struct radij_node_head *head));
++ struct radij_node *(*rnh_matchpkt) /* locate based on packet hdr */
++ __P((void *v, struct radij_node_head *head));
++ int (*rnh_walktree) /* traverse tree */
++ __P((struct radij_node_head *head, int (*f)(struct radij_node *rn, void *w), void *w));
++ struct radij_node rnh_nodes[3]; /* empty tree for common case */
++};
++
++
++#define Bcmp(a, b, n) memcmp(((caddr_t)(b)), ((caddr_t)(a)), (unsigned)(n))
++#define Bcopy(a, b, n) memmove(((caddr_t)(b)), ((caddr_t)(a)), (unsigned)(n))
++#define Bzero(p, n) memset((caddr_t)(p), 0, (unsigned)(n))
++#define R_Malloc(p, t, n) ((p = (t) kmalloc((size_t)(n), GFP_ATOMIC)), Bzero((p),(n)))
++#define Free(p) kfree((caddr_t)p);
++
++void rj_init __P((void));
++int rj_inithead __P((void **, int));
++int rj_refines __P((void *, void *));
++int rj_walktree __P((struct radij_node_head *head, int (*f)(struct radij_node *rn, void *w), void *w));
++struct radij_node
++ *rj_addmask __P((void *, int, int)) /* , rgb */ ;
++int /* * */ rj_addroute __P((void *, void *, struct radij_node_head *,
++ struct radij_node [2])) /* , rgb */ ;
++int /* * */ rj_delete __P((void *, void *, struct radij_node_head *, struct radij_node **)) /* , rgb */ ;
++struct radij_node /* rgb */
++ *rj_insert __P((void *, struct radij_node_head *, int *,
++ struct radij_node [2])),
++ *rj_match __P((void *, struct radij_node_head *)),
++ *rj_newpair __P((void *, int, struct radij_node[2])),
++ *rj_search __P((void *, struct radij_node *)),
++ *rj_search_m __P((void *, struct radij_node *, void *));
++
++void rj_deltree(struct radij_node_head *);
++void rj_delnodes(struct radij_node *);
++void rj_free_mkfreelist(void);
++int radijcleartree(void);
++int radijcleanup(void);
++
++extern struct radij_node_head *mask_rjhead;
++extern int maj_keylen;
++#endif /* __KERNEL__ */
++
++#endif /* _RADIJ_H_ */
++
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.13 2004/04/05 19:55:08 mcr
++ * Moved from linux/include/freeswan/radij.h,v
++ *
++ * Revision 1.12 2002/04/24 07:36:48 mcr
++ * Moved from ./klips/net/ipsec/radij.h,v
++ *
++ * Revision 1.11 2001/09/20 15:33:00 rgb
++ * Min/max cleanup.
++ *
++ * Revision 1.10 1999/11/18 04:09:20 rgb
++ * Replaced all kernel version macros to shorter, readable form.
++ *
++ * Revision 1.9 1999/05/05 22:02:33 rgb
++ * Add a quick and dirty port to 2.2 kernels by Marc Boucher <marc@mbsi.ca>.
++ *
++ * Revision 1.8 1999/04/29 15:24:58 rgb
++ * Add check for existence of macros min/max.
++ *
++ * Revision 1.7 1999/04/11 00:29:02 henry
++ * GPL boilerplate
++ *
++ * Revision 1.6 1999/04/06 04:54:29 rgb
++ * Fix/Add RCSID Id: and Log: bits to make PHMDs happy. This includes
++ * patch shell fixes.
++ *
++ * Revision 1.5 1999/01/22 06:30:32 rgb
++ * 64-bit clean-up.
++ *
++ * Revision 1.4 1998/11/30 13:22:55 rgb
++ * Rationalised all the klips kernel file headers. They are much shorter
++ * now and won't conflict under RH5.2.
++ *
++ * Revision 1.3 1998/10/25 02:43:27 rgb
++ * Change return type on rj_addroute and rj_delete and add and argument
++ * to the latter to be able to transmit more infomation about errors.
++ *
++ * Revision 1.2 1998/07/14 18:09:51 rgb
++ * Add a routine to clear eroute table.
++ * Added #ifdef __KERNEL__ directives to restrict scope of header.
++ *
++ * Revision 1.1 1998/06/18 21:30:22 henry
++ * move sources from klips/src to klips/net/ipsec to keep stupid kernel
++ * build scripts happier about symlinks
++ *
++ * Revision 1.4 1998/05/25 20:34:16 rgb
++ * Remove temporary ipsec_walk, rj_deltree and rj_delnodes functions.
++ *
++ * Rename ipsec_rj_walker (ipsec_walk) to ipsec_rj_walker_procprint and
++ * add ipsec_rj_walker_delete.
++ *
++ * Recover memory for eroute table on unload of module.
++ *
++ * Revision 1.3 1998/04/22 16:51:37 rgb
++ * Tidy up radij debug code from recent rash of modifications to debug code.
++ *
++ * Revision 1.2 1998/04/14 17:30:38 rgb
++ * Fix up compiling errors for radij tree memory reclamation.
++ *
++ * Revision 1.1 1998/04/09 03:06:16 henry
++ * sources moved up from linux/net/ipsec
++ *
++ * Revision 1.1.1.1 1998/04/08 05:35:04 henry
++ * RGB's ipsec-0.8pre2.tar.gz ipsec-0.8
++ *
++ * Revision 0.4 1997/01/15 01:28:15 ji
++ * No changes.
++ *
++ * Revision 0.3 1996/11/20 14:44:45 ji
++ * Release update only.
++ *
++ * Revision 0.2 1996/11/02 00:18:33 ji
++ * First limited release.
++ *
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/include/pfkey.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,509 @@
++/*
++ * FreeS/WAN specific PF_KEY headers
++ * Copyright (C) 1999, 2000, 2001 Richard Guy Briggs.
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ * RCSID $Id$
++ */
++
++#ifndef __NET_IPSEC_PF_KEY_H
++#define __NET_IPSEC_PF_KEY_H
++#ifdef __KERNEL__
++extern struct proto_ops pfkey_proto_ops;
++typedef struct sock pfkey_sock;
++extern int debug_pfkey;
++
++extern /* void */ int pfkey_init(void);
++extern /* void */ int pfkey_cleanup(void);
++
++extern struct sock *pfkey_sock_list;
++struct socket_list
++{
++ struct socket *socketp;
++ struct socket_list *next;
++};
++extern int pfkey_list_insert_socket(struct socket*, struct socket_list**);
++extern int pfkey_list_remove_socket(struct socket*, struct socket_list**);
++extern struct socket_list *pfkey_open_sockets;
++extern struct socket_list *pfkey_registered_sockets[SADB_SATYPE_MAX+1];
++
++struct supported
++{
++ uint16_t supported_alg_exttype;
++ uint8_t supported_alg_id;
++ uint8_t supported_alg_ivlen;
++ uint16_t supported_alg_minbits;
++ uint16_t supported_alg_maxbits;
++};
++
++extern struct supported_list *pfkey_supported_list[SADB_SATYPE_MAX+1];
++struct supported_list
++{
++ struct supported *supportedp;
++ struct supported_list *next;
++};
++extern int pfkey_list_insert_supported(struct supported*, struct supported_list**);
++extern int pfkey_list_remove_supported(struct supported*, struct supported_list**);
++
++struct sockaddr_key
++{
++ uint16_t key_family; /* PF_KEY */
++ uint16_t key_pad; /* not used */
++ uint32_t key_pid; /* process ID */
++};
++
++struct pfkey_extracted_data
++{
++ struct ipsec_sa* ips;
++ struct ipsec_sa* ips2;
++ struct eroute *eroute;
++};
++
++extern int
++pfkey_alloc_eroute(struct eroute** eroute);
++
++extern int
++pfkey_sa_process(struct sadb_ext *pfkey_ext,
++ struct pfkey_extracted_data* extr);
++
++extern int
++pfkey_lifetime_process(struct sadb_ext *pfkey_ext,
++ struct pfkey_extracted_data* extr);
++
++extern int
++pfkey_address_process(struct sadb_ext *pfkey_ext,
++ struct pfkey_extracted_data* extr);
++
++extern int
++pfkey_key_process(struct sadb_ext *pfkey_ext,
++ struct pfkey_extracted_data* extr);
++
++extern int
++pfkey_ident_process(struct sadb_ext *pfkey_ext,
++ struct pfkey_extracted_data* extr);
++
++extern int
++pfkey_sens_process(struct sadb_ext *pfkey_ext,
++ struct pfkey_extracted_data* extr);
++
++extern int
++pfkey_prop_process(struct sadb_ext *pfkey_ext,
++ struct pfkey_extracted_data* extr);
++
++extern int
++pfkey_supported_process(struct sadb_ext *pfkey_ext,
++ struct pfkey_extracted_data* extr);
++
++extern int
++pfkey_spirange_process(struct sadb_ext *pfkey_ext,
++ struct pfkey_extracted_data* extr);
++
++extern int
++pfkey_x_kmprivate_process(struct sadb_ext *pfkey_ext,
++ struct pfkey_extracted_data* extr);
++
++extern int
++pfkey_x_satype_process(struct sadb_ext *pfkey_ext,
++ struct pfkey_extracted_data* extr);
++
++extern int
++pfkey_x_debug_process(struct sadb_ext *pfkey_ext,
++ struct pfkey_extracted_data* extr);
++
++extern int pfkey_upmsg(struct socket *, struct sadb_msg *);
++extern int pfkey_expire(struct ipsec_sa *, int);
++extern int pfkey_acquire(struct ipsec_sa *);
++#else /* ! __KERNEL__ */
++
++extern void (*pfkey_debug_func)(const char *message, ...);
++extern void (*pfkey_error_func)(const char *message, ...);
++extern void pfkey_print(struct sadb_msg *msg, FILE *out);
++
++
++#endif /* __KERNEL__ */
++
++extern uint8_t satype2proto(uint8_t satype);
++extern uint8_t proto2satype(uint8_t proto);
++extern char* satype2name(uint8_t satype);
++extern char* proto2name(uint8_t proto);
++
++struct key_opt
++{
++ uint32_t key_pid; /* process ID */
++ struct sock *sk;
++};
++
++#define key_pid(sk) ((struct key_opt*)&((sk)->protinfo))->key_pid
++
++/* XXX-mcr this is not an alignment, this is because the count is in 64-bit
++ * words.
++ */
++#define IPSEC_PFKEYv2_ALIGN (sizeof(uint64_t)/sizeof(uint8_t))
++#define BITS_PER_OCTET 8
++#define OCTETBITS 8
++#define PFKEYBITS 64
++#define DIVUP(x,y) ((x + y -1) / y) /* divide, rounding upwards */
++#define ALIGN_N(x,y) (DIVUP(x,y) * y) /* align on y boundary */
++
++#define IPSEC_PFKEYv2_LEN(x) ((x) * IPSEC_PFKEYv2_ALIGN)
++#define IPSEC_PFKEYv2_WORDS(x) ((x) / IPSEC_PFKEYv2_ALIGN)
++
++
++#define PFKEYv2_MAX_MSGSIZE 4096
++
++/*
++ * PF_KEYv2 permitted and required extensions in and out bitmaps
++ */
++struct pf_key_ext_parsers_def {
++ int (*parser)(struct sadb_ext*);
++ char *parser_name;
++};
++
++
++extern unsigned int extensions_bitmaps[2/*in/out*/][2/*perm/req*/][SADB_MAX + 1/*ext*/];
++#define EXT_BITS_IN 0
++#define EXT_BITS_OUT 1
++#define EXT_BITS_PERM 0
++#define EXT_BITS_REQ 1
++
++extern void pfkey_extensions_init(struct sadb_ext *extensions[SADB_EXT_MAX + 1]);
++extern void pfkey_extensions_free(struct sadb_ext *extensions[SADB_EXT_MAX + 1]);
++extern void pfkey_msg_free(struct sadb_msg **pfkey_msg);
++
++extern int pfkey_msg_parse(struct sadb_msg *pfkey_msg,
++ struct pf_key_ext_parsers_def *ext_parsers[],
++ struct sadb_ext **extensions,
++ int dir);
++
++extern int pfkey_register_reply(int satype, struct sadb_msg *sadb_msg);
++
++/*
++ * PF_KEYv2 build function prototypes
++ */
++
++int
++pfkey_msg_hdr_build(struct sadb_ext** pfkey_ext,
++ uint8_t msg_type,
++ uint8_t satype,
++ uint8_t msg_errno,
++ uint32_t seq,
++ uint32_t pid);
++
++int
++pfkey_sa_ref_build(struct sadb_ext ** pfkey_ext,
++ uint16_t exttype,
++ uint32_t spi, /* in network order */
++ uint8_t replay_window,
++ uint8_t sa_state,
++ uint8_t auth,
++ uint8_t encrypt,
++ uint32_t flags,
++ uint32_t/*IPsecSAref_t*/ ref);
++
++int
++pfkey_sa_build(struct sadb_ext ** pfkey_ext,
++ uint16_t exttype,
++ uint32_t spi, /* in network order */
++ uint8_t replay_window,
++ uint8_t sa_state,
++ uint8_t auth,
++ uint8_t encrypt,
++ uint32_t flags);
++
++int
++pfkey_lifetime_build(struct sadb_ext ** pfkey_ext,
++ uint16_t exttype,
++ uint32_t allocations,
++ uint64_t bytes,
++ uint64_t addtime,
++ uint64_t usetime,
++ uint32_t packets);
++
++int
++pfkey_address_build(struct sadb_ext** pfkey_ext,
++ uint16_t exttype,
++ uint8_t proto,
++ uint8_t prefixlen,
++ struct sockaddr* address);
++
++int
++pfkey_key_build(struct sadb_ext** pfkey_ext,
++ uint16_t exttype,
++ uint16_t key_bits,
++ char* key);
++
++int
++pfkey_ident_build(struct sadb_ext** pfkey_ext,
++ uint16_t exttype,
++ uint16_t ident_type,
++ uint64_t ident_id,
++ uint8_t ident_len,
++ char* ident_string);
++
++#ifdef NAT_TRAVERSAL
++#ifdef __KERNEL__
++extern int pfkey_nat_t_new_mapping(struct ipsec_sa *, struct sockaddr *, __u16);
++extern int pfkey_x_nat_t_type_process(struct sadb_ext *pfkey_ext, struct pfkey_extracted_data* extr);
++extern int pfkey_x_nat_t_port_process(struct sadb_ext *pfkey_ext, struct pfkey_extracted_data* extr);
++#endif /* __KERNEL__ */
++int
++pfkey_x_nat_t_type_build(struct sadb_ext** pfkey_ext,
++ uint8_t type);
++int
++pfkey_x_nat_t_port_build(struct sadb_ext** pfkey_ext,
++ uint16_t exttype,
++ uint16_t port);
++#endif
++
++int
++pfkey_sens_build(struct sadb_ext** pfkey_ext,
++ uint32_t dpd,
++ uint8_t sens_level,
++ uint8_t sens_len,
++ uint64_t* sens_bitmap,
++ uint8_t integ_level,
++ uint8_t integ_len,
++ uint64_t* integ_bitmap);
++
++int pfkey_x_protocol_build(struct sadb_ext **, uint8_t);
++
++
++int
++pfkey_prop_build(struct sadb_ext** pfkey_ext,
++ uint8_t replay,
++ unsigned int comb_num,
++ struct sadb_comb* comb);
++
++int
++pfkey_supported_build(struct sadb_ext** pfkey_ext,
++ uint16_t exttype,
++ unsigned int alg_num,
++ struct sadb_alg* alg);
++
++int
++pfkey_spirange_build(struct sadb_ext** pfkey_ext,
++ uint16_t exttype,
++ uint32_t min,
++ uint32_t max);
++
++int
++pfkey_x_kmprivate_build(struct sadb_ext** pfkey_ext);
++
++int
++pfkey_x_satype_build(struct sadb_ext** pfkey_ext,
++ uint8_t satype);
++
++int
++pfkey_x_debug_build(struct sadb_ext** pfkey_ext,
++ uint32_t tunnel,
++ uint32_t netlink,
++ uint32_t xform,
++ uint32_t eroute,
++ uint32_t spi,
++ uint32_t radij,
++ uint32_t esp,
++ uint32_t ah,
++ uint32_t rcv,
++ uint32_t pfkey,
++ uint32_t ipcomp,
++ uint32_t verbose);
++
++int
++pfkey_msg_build(struct sadb_msg** pfkey_msg,
++ struct sadb_ext* extensions[],
++ int dir);
++
++/* in pfkey_v2_debug.c - routines to decode numbers -> strings */
++const char *
++pfkey_v2_sadb_ext_string(int extnum);
++
++const char *
++pfkey_v2_sadb_type_string(int sadb_type);
++
++
++#endif /* __NET_IPSEC_PF_KEY_H */
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.45 2004/04/06 02:49:00 mcr
++ * pullup of algo code from alg-branch.
++ *
++ * Revision 1.44 2003/12/10 01:20:01 mcr
++ * NAT-traversal patches to KLIPS.
++ *
++ * Revision 1.43 2003/10/31 02:26:44 mcr
++ * pulled up port-selector patches.
++ *
++ * Revision 1.42.2.2 2003/10/29 01:09:32 mcr
++ * added debugging for pfkey library.
++ *
++ * Revision 1.42.2.1 2003/09/21 13:59:34 mcr
++ * pre-liminary X.509 patch - does not yet pass tests.
++ *
++ * Revision 1.42 2003/08/25 22:08:19 mcr
++ * removed pfkey_proto_init() from pfkey.h for 2.6 support.
++ *
++ * Revision 1.41 2003/05/07 17:28:57 mcr
++ * new function pfkey_debug_func added for us in debugging from
++
++ * pfkey library.
++ *
++ * Revision 1.40 2003/01/30 02:31:34 rgb
++ *
++ * Convert IPsecSAref_t from signed to unsigned to fix apparent SAref exhaustion bug.
++ *
++ * Revision 1.39 2002/09/20 15:40:21 rgb
++ * Switch from pfkey_alloc_ipsec_sa() to ipsec_sa_alloc().
++ * Added ref parameter to pfkey_sa_build().
++ * Cleaned out unused cruft.
++ *
++ * Revision 1.38 2002/05/14 02:37:24 rgb
++ * Change all references to tdb, TDB or Tunnel Descriptor Block to ips,
++ * ipsec_sa or ipsec_sa.
++ * Added function prototypes for the functions moved to
++ * pfkey_v2_ext_process.c.
++ *
++ * Revision 1.37 2002/04/24 07:36:49 mcr
++ * Moved from ./lib/pfkey.h,v
++ *
++ * Revision 1.36 2002/01/20 20:34:49 mcr
++ * added pfkey_v2_sadb_type_string to decode sadb_type to string.
++ *
++ * Revision 1.35 2001/11/27 05:27:47 mcr
++ * pfkey parses are now maintained by a structure
++ * that includes their name for debug purposes.
++ *
++ * Revision 1.34 2001/11/26 09:23:53 rgb
++ * Merge MCR's ipsec_sa, eroute, proc and struct lifetime changes.
++ *
++ * Revision 1.33 2001/11/06 19:47:47 rgb
++ * Added packet parameter to lifetime and comb structures.
++ *
++ * Revision 1.32 2001/09/08 21:13:34 rgb
++ * Added pfkey ident extension support for ISAKMPd. (NetCelo)
++ *
++ * Revision 1.31 2001/06/14 19:35:16 rgb
++ * Update copyright date.
++ *
++ * Revision 1.30 2001/02/27 07:04:52 rgb
++ * Added satype2name prototype.
++ *
++ * Revision 1.29 2001/02/26 19:59:33 rgb
++ * Ditch unused sadb_satype2proto[], replaced by satype2proto().
++ *
++ * Revision 1.28 2000/10/10 20:10:19 rgb
++ * Added support for debug_ipcomp and debug_verbose to klipsdebug.
++ *
++ * Revision 1.27 2000/09/21 04:20:45 rgb
++ * Fixed array size off-by-one error. (Thanks Svenning!)
++ *
++ * Revision 1.26 2000/09/12 03:26:05 rgb
++ * Added pfkey_acquire prototype.
++ *
++ * Revision 1.25 2000/09/08 19:21:28 rgb
++ * Fix pfkey_prop_build() parameter to be only single indirection.
++ *
++ * Revision 1.24 2000/09/01 18:46:42 rgb
++ * Added a supported algorithms array lists, one per satype and registered
++ * existing algorithms.
++ * Fixed pfkey_list_{insert,remove}_{socket,support}() to allow change to
++ * list.
++ *
++ * Revision 1.23 2000/08/27 01:55:26 rgb
++ * Define OCTETBITS and PFKEYBITS to avoid using 'magic' numbers in code.
++ *
++ * Revision 1.22 2000/08/20 21:39:23 rgb
++ * Added kernel prototypes for kernel funcitions pfkey_upmsg() and
++ * pfkey_expire().
++ *
++ * Revision 1.21 2000/08/15 17:29:23 rgb
++ * Fixes from SZI to untested pfkey_prop_build().
++ *
++ * Revision 1.20 2000/05/10 20:14:19 rgb
++ * Fleshed out sensitivity, proposal and supported extensions.
++ *
++ * Revision 1.19 2000/03/16 14:07:23 rgb
++ * Renamed ALIGN macro to avoid fighting with others in kernel.
++ *
++ * Revision 1.18 2000/01/22 23:24:06 rgb
++ * Added prototypes for proto2satype(), satype2proto() and proto2name().
++ *
++ * Revision 1.17 2000/01/21 06:26:59 rgb
++ * Converted from double tdb arguments to one structure (extr)
++ * containing pointers to all temporary information structures.
++ * Added klipsdebug switching capability.
++ * Dropped unused argument to pfkey_x_satype_build().
++ *
++ * Revision 1.16 1999/12/29 21:17:41 rgb
++ * Changed pfkey_msg_build() I/F to include a struct sadb_msg**
++ * parameter for cleaner manipulation of extensions[] and to guard
++ * against potential memory leaks.
++ * Changed the I/F to pfkey_msg_free() for the same reason.
++ *
++ * Revision 1.15 1999/12/09 23:12:54 rgb
++ * Added macro for BITS_PER_OCTET.
++ * Added argument to pfkey_sa_build() to do eroutes.
++ *
++ * Revision 1.14 1999/12/08 20:33:25 rgb
++ * Changed sa_family_t to uint16_t for 2.0.xx compatibility.
++ *
++ * Revision 1.13 1999/12/07 19:53:40 rgb
++ * Removed unused first argument from extension parsers.
++ * Changed __u* types to uint* to avoid use of asm/types.h and
++ * sys/types.h in userspace code.
++ * Added function prototypes for pfkey message and extensions
++ * initialisation and cleanup.
++ *
++ * Revision 1.12 1999/12/01 22:19:38 rgb
++ * Change pfkey_sa_build to accept an SPI in network byte order.
++ *
++ * Revision 1.11 1999/11/27 11:55:26 rgb
++ * Added extern sadb_satype2proto to enable moving protocol lookup table
++ * to lib/pfkey_v2_parse.c.
++ * Delete unused, moved typedefs.
++ * Add argument to pfkey_msg_parse() for direction.
++ * Consolidated the 4 1-d extension bitmap arrays into one 4-d array.
++ *
++ * Revision 1.10 1999/11/23 22:29:21 rgb
++ * This file has been moved in the distribution from klips/net/ipsec to
++ * lib.
++ * Add macros for dealing with alignment and rounding up more opaquely.
++ * The uint<n>_t type defines have been moved to freeswan.h to avoid
++ * chicken-and-egg problems.
++ * Add macros for dealing with alignment and rounding up more opaque.
++ * Added prototypes for using extention header bitmaps.
++ * Added prototypes of all the build functions.
++ *
++ * Revision 1.9 1999/11/20 21:59:48 rgb
++ * Moved socketlist type declarations and prototypes for shared use.
++ * Slightly modified scope of sockaddr_key declaration.
++ *
++ * Revision 1.8 1999/11/17 14:34:25 rgb
++ * Protect sa_family_t from being used in userspace with GLIBC<2.
++ *
++ * Revision 1.7 1999/10/27 19:40:35 rgb
++ * Add a maximum PFKEY packet size macro.
++ *
++ * Revision 1.6 1999/10/26 16:58:58 rgb
++ * Created a sockaddr_key and key_opt socket extension structures.
++ *
++ * Revision 1.5 1999/06/10 05:24:41 rgb
++ * Renamed variables to reduce confusion.
++ *
++ * Revision 1.4 1999/04/29 15:21:11 rgb
++ * Add pfkey support to debugging.
++ * Add return values to init and cleanup functions.
++ *
++ * Revision 1.3 1999/04/15 17:58:07 rgb
++ * Add RCSID labels.
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/include/pfkeyv2.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,467 @@
++/*
++ * RCSID $Id$
++ */
++
++/*
++RFC 2367 PF_KEY Key Management API July 1998
++
++
++Appendix D: Sample Header File
++
++This file defines structures and symbols for the PF_KEY Version 2
++key management interface. It was written at the U.S. Naval Research
++Laboratory. This file is in the public domain. The authors ask that
++you leave this credit intact on any copies of this file.
++*/
++#ifndef __PFKEY_V2_H
++#define __PFKEY_V2_H 1
++
++#define PF_KEY_V2 2
++#define PFKEYV2_REVISION 199806L
++
++#define SADB_RESERVED 0
++#define SADB_GETSPI 1
++#define SADB_UPDATE 2
++#define SADB_ADD 3
++#define SADB_DELETE 4
++#define SADB_GET 5
++#define SADB_ACQUIRE 6
++#define SADB_REGISTER 7
++#define SADB_EXPIRE 8
++#define SADB_FLUSH 9
++#define SADB_DUMP 10
++#define SADB_X_PROMISC 11
++#define SADB_X_PCHANGE 12
++#define SADB_X_GRPSA 13
++#define SADB_X_ADDFLOW 14
++#define SADB_X_DELFLOW 15
++#define SADB_X_DEBUG 16
++#define SADB_X_NAT_T_NEW_MAPPING 17
++#define SADB_MAX 17
++
++struct sadb_msg {
++ uint8_t sadb_msg_version;
++ uint8_t sadb_msg_type;
++ uint8_t sadb_msg_errno;
++ uint8_t sadb_msg_satype;
++ uint16_t sadb_msg_len;
++ uint16_t sadb_msg_reserved;
++ uint32_t sadb_msg_seq;
++ uint32_t sadb_msg_pid;
++};
++
++struct sadb_ext {
++ uint16_t sadb_ext_len;
++ uint16_t sadb_ext_type;
++};
++
++struct sadb_sa {
++ uint16_t sadb_sa_len;
++ uint16_t sadb_sa_exttype;
++ uint32_t sadb_sa_spi;
++ uint8_t sadb_sa_replay;
++ uint8_t sadb_sa_state;
++ uint8_t sadb_sa_auth;
++ uint8_t sadb_sa_encrypt;
++ uint32_t sadb_sa_flags;
++ uint32_t /*IPsecSAref_t*/ sadb_x_sa_ref; /* 32 bits */
++ uint8_t sadb_x_reserved[4];
++};
++
++struct sadb_sa_v1 {
++ uint16_t sadb_sa_len;
++ uint16_t sadb_sa_exttype;
++ uint32_t sadb_sa_spi;
++ uint8_t sadb_sa_replay;
++ uint8_t sadb_sa_state;
++ uint8_t sadb_sa_auth;
++ uint8_t sadb_sa_encrypt;
++ uint32_t sadb_sa_flags;
++};
++
++struct sadb_lifetime {
++ uint16_t sadb_lifetime_len;
++ uint16_t sadb_lifetime_exttype;
++ uint32_t sadb_lifetime_allocations;
++ uint64_t sadb_lifetime_bytes;
++ uint64_t sadb_lifetime_addtime;
++ uint64_t sadb_lifetime_usetime;
++ uint32_t sadb_x_lifetime_packets;
++ uint32_t sadb_x_lifetime_reserved;
++};
++
++struct sadb_address {
++ uint16_t sadb_address_len;
++ uint16_t sadb_address_exttype;
++ uint8_t sadb_address_proto;
++ uint8_t sadb_address_prefixlen;
++ uint16_t sadb_address_reserved;
++};
++
++struct sadb_key {
++ uint16_t sadb_key_len;
++ uint16_t sadb_key_exttype;
++ uint16_t sadb_key_bits;
++ uint16_t sadb_key_reserved;
++};
++
++struct sadb_ident {
++ uint16_t sadb_ident_len;
++ uint16_t sadb_ident_exttype;
++ uint16_t sadb_ident_type;
++ uint16_t sadb_ident_reserved;
++ uint64_t sadb_ident_id;
++};
++
++struct sadb_sens {
++ uint16_t sadb_sens_len;
++ uint16_t sadb_sens_exttype;
++ uint32_t sadb_sens_dpd;
++ uint8_t sadb_sens_sens_level;
++ uint8_t sadb_sens_sens_len;
++ uint8_t sadb_sens_integ_level;
++ uint8_t sadb_sens_integ_len;
++ uint32_t sadb_sens_reserved;
++};
++
++struct sadb_prop {
++ uint16_t sadb_prop_len;
++ uint16_t sadb_prop_exttype;
++ uint8_t sadb_prop_replay;
++ uint8_t sadb_prop_reserved[3];
++};
++
++struct sadb_comb {
++ uint8_t sadb_comb_auth;
++ uint8_t sadb_comb_encrypt;
++ uint16_t sadb_comb_flags;
++ uint16_t sadb_comb_auth_minbits;
++ uint16_t sadb_comb_auth_maxbits;
++ uint16_t sadb_comb_encrypt_minbits;
++ uint16_t sadb_comb_encrypt_maxbits;
++ uint32_t sadb_comb_reserved;
++ uint32_t sadb_comb_soft_allocations;
++ uint32_t sadb_comb_hard_allocations;
++ uint64_t sadb_comb_soft_bytes;
++ uint64_t sadb_comb_hard_bytes;
++ uint64_t sadb_comb_soft_addtime;
++ uint64_t sadb_comb_hard_addtime;
++ uint64_t sadb_comb_soft_usetime;
++ uint64_t sadb_comb_hard_usetime;
++ uint32_t sadb_x_comb_soft_packets;
++ uint32_t sadb_x_comb_hard_packets;
++};
++
++struct sadb_supported {
++ uint16_t sadb_supported_len;
++ uint16_t sadb_supported_exttype;
++ uint32_t sadb_supported_reserved;
++};
++
++struct sadb_alg {
++ uint8_t sadb_alg_id;
++ uint8_t sadb_alg_ivlen;
++ uint16_t sadb_alg_minbits;
++ uint16_t sadb_alg_maxbits;
++ uint16_t sadb_alg_reserved;
++};
++
++struct sadb_spirange {
++ uint16_t sadb_spirange_len;
++ uint16_t sadb_spirange_exttype;
++ uint32_t sadb_spirange_min;
++ uint32_t sadb_spirange_max;
++ uint32_t sadb_spirange_reserved;
++};
++
++struct sadb_x_kmprivate {
++ uint16_t sadb_x_kmprivate_len;
++ uint16_t sadb_x_kmprivate_exttype;
++ uint32_t sadb_x_kmprivate_reserved;
++};
++
++struct sadb_x_satype {
++ uint16_t sadb_x_satype_len;
++ uint16_t sadb_x_satype_exttype;
++ uint8_t sadb_x_satype_satype;
++ uint8_t sadb_x_satype_reserved[3];
++};
++
++struct sadb_x_policy {
++ uint16_t sadb_x_policy_len;
++ uint16_t sadb_x_policy_exttype;
++ uint16_t sadb_x_policy_type;
++ uint8_t sadb_x_policy_dir;
++ uint8_t sadb_x_policy_reserved;
++ uint32_t sadb_x_policy_id;
++ uint32_t sadb_x_policy_reserved2;
++};
++
++struct sadb_x_debug {
++ uint16_t sadb_x_debug_len;
++ uint16_t sadb_x_debug_exttype;
++ uint32_t sadb_x_debug_tunnel;
++ uint32_t sadb_x_debug_netlink;
++ uint32_t sadb_x_debug_xform;
++ uint32_t sadb_x_debug_eroute;
++ uint32_t sadb_x_debug_spi;
++ uint32_t sadb_x_debug_radij;
++ uint32_t sadb_x_debug_esp;
++ uint32_t sadb_x_debug_ah;
++ uint32_t sadb_x_debug_rcv;
++ uint32_t sadb_x_debug_pfkey;
++ uint32_t sadb_x_debug_ipcomp;
++ uint32_t sadb_x_debug_verbose;
++ uint8_t sadb_x_debug_reserved[4];
++};
++
++struct sadb_x_nat_t_type {
++ uint16_t sadb_x_nat_t_type_len;
++ uint16_t sadb_x_nat_t_type_exttype;
++ uint8_t sadb_x_nat_t_type_type;
++ uint8_t sadb_x_nat_t_type_reserved[3];
++};
++struct sadb_x_nat_t_port {
++ uint16_t sadb_x_nat_t_port_len;
++ uint16_t sadb_x_nat_t_port_exttype;
++ uint16_t sadb_x_nat_t_port_port;
++ uint16_t sadb_x_nat_t_port_reserved;
++};
++
++/*
++ * A protocol structure for passing through the transport level
++ * protocol. It contains more fields than are actually used/needed
++ * but it is this way to be compatible with the structure used in
++ * OpenBSD (http://www.openbsd.org/cgi-bin/cvsweb/src/sys/net/pfkeyv2.h)
++ */
++struct sadb_protocol {
++ uint16_t sadb_protocol_len;
++ uint16_t sadb_protocol_exttype;
++ uint8_t sadb_protocol_proto;
++ uint8_t sadb_protocol_direction;
++ uint8_t sadb_protocol_flags;
++ uint8_t sadb_protocol_reserved2;
++};
++
++#define SADB_EXT_RESERVED 0
++#define SADB_EXT_SA 1
++#define SADB_EXT_LIFETIME_CURRENT 2
++#define SADB_EXT_LIFETIME_HARD 3
++#define SADB_EXT_LIFETIME_SOFT 4
++#define SADB_EXT_ADDRESS_SRC 5
++#define SADB_EXT_ADDRESS_DST 6
++#define SADB_EXT_ADDRESS_PROXY 7
++#define SADB_EXT_KEY_AUTH 8
++#define SADB_EXT_KEY_ENCRYPT 9
++#define SADB_EXT_IDENTITY_SRC 10
++#define SADB_EXT_IDENTITY_DST 11
++#define SADB_EXT_SENSITIVITY 12
++#define SADB_EXT_PROPOSAL 13
++#define SADB_EXT_SUPPORTED_AUTH 14
++#define SADB_EXT_SUPPORTED_ENCRYPT 15
++#define SADB_EXT_SPIRANGE 16
++#define SADB_X_EXT_KMPRIVATE 17
++#define SADB_X_EXT_SATYPE2 18
++#ifdef KERNEL26_HAS_KAME_DUPLICATES
++#define SADB_X_EXT_POLICY 18
++#endif
++#define SADB_X_EXT_SA2 19
++#define SADB_X_EXT_ADDRESS_DST2 20
++#define SADB_X_EXT_ADDRESS_SRC_FLOW 21
++#define SADB_X_EXT_ADDRESS_DST_FLOW 22
++#define SADB_X_EXT_ADDRESS_SRC_MASK 23
++#define SADB_X_EXT_ADDRESS_DST_MASK 24
++#define SADB_X_EXT_DEBUG 25
++#define SADB_X_EXT_PROTOCOL 26
++#define SADB_X_EXT_NAT_T_TYPE 27
++#define SADB_X_EXT_NAT_T_SPORT 28
++#define SADB_X_EXT_NAT_T_DPORT 29
++#define SADB_X_EXT_NAT_T_OA 30
++#define SADB_EXT_MAX 30
++
++/* SADB_X_DELFLOW required over and above SADB_X_SAFLAGS_CLEARFLOW */
++#define SADB_X_EXT_ADDRESS_DELFLOW \
++ ( (1<<SADB_X_EXT_ADDRESS_SRC_FLOW) \
++ | (1<<SADB_X_EXT_ADDRESS_DST_FLOW) \
++ | (1<<SADB_X_EXT_ADDRESS_SRC_MASK) \
++ | (1<<SADB_X_EXT_ADDRESS_DST_MASK))
++
++#define SADB_SATYPE_UNSPEC 0
++#define SADB_SATYPE_AH 2
++#define SADB_SATYPE_ESP 3
++#define SADB_SATYPE_RSVP 5
++#define SADB_SATYPE_OSPFV2 6
++#define SADB_SATYPE_RIPV2 7
++#define SADB_SATYPE_MIP 8
++#define SADB_X_SATYPE_IPIP 9
++#ifdef KERNEL26_HAS_KAME_DUPLICATES
++#define SADB_X_SATYPE_IPCOMP 9 /* ICK! */
++#endif
++#define SADB_X_SATYPE_COMP 10
++#define SADB_X_SATYPE_INT 11
++#define SADB_SATYPE_MAX 11
++
++#define SADB_SASTATE_LARVAL 0
++#define SADB_SASTATE_MATURE 1
++#define SADB_SASTATE_DYING 2
++#define SADB_SASTATE_DEAD 3
++#define SADB_SASTATE_MAX 3
++
++#define SADB_SAFLAGS_PFS 1
++#define SADB_X_SAFLAGS_REPLACEFLOW 2
++#define SADB_X_SAFLAGS_CLEARFLOW 4
++#define SADB_X_SAFLAGS_INFLOW 8
++
++/* not obvious, but these are the same values as used in isakmp,
++ * and in freeswan/ipsec_policy.h. If you need to add any, they
++ * should be added as according to
++ * http://www.iana.org/assignments/isakmp-registry
++ *
++ * and if not, then please try to use a private-use value, and
++ * consider asking IANA to assign a value.
++ */
++#define SADB_AALG_NONE 0
++#define SADB_AALG_MD5HMAC 2
++#define SADB_AALG_SHA1HMAC 3
++#define SADB_X_AALG_SHA2_256HMAC 5
++#define SADB_X_AALG_SHA2_384HMAC 6
++#define SADB_X_AALG_SHA2_512HMAC 7
++#define SADB_X_AALG_RIPEMD160HMAC 8
++#define SADB_X_AALG_NULL 251 /* kame */
++#define SADB_AALG_MAX 251
++
++#define SADB_EALG_NONE 0
++#define SADB_EALG_DESCBC 2
++#define SADB_EALG_3DESCBC 3
++#define SADB_X_EALG_CASTCBC 6
++#define SADB_X_EALG_BLOWFISHCBC 7
++#define SADB_EALG_NULL 11
++#define SADB_X_EALG_AESCBC 12
++#define SADB_EALG_MAX 255
++
++#define SADB_X_CALG_NONE 0
++#define SADB_X_CALG_OUI 1
++#define SADB_X_CALG_DEFLATE 2
++#define SADB_X_CALG_LZS 3
++#define SADB_X_CALG_V42BIS 4
++#ifdef KERNEL26_HAS_KAME_DUPLICATES
++#define SADB_X_CALG_LZJH 4
++#endif
++#define SADB_X_CALG_MAX 4
++
++#define SADB_X_TALG_NONE 0
++#define SADB_X_TALG_IPv4_in_IPv4 1
++#define SADB_X_TALG_IPv6_in_IPv4 2
++#define SADB_X_TALG_IPv4_in_IPv6 3
++#define SADB_X_TALG_IPv6_in_IPv6 4
++#define SADB_X_TALG_MAX 4
++
++
++#define SADB_IDENTTYPE_RESERVED 0
++#define SADB_IDENTTYPE_PREFIX 1
++#define SADB_IDENTTYPE_FQDN 2
++#define SADB_IDENTTYPE_USERFQDN 3
++#define SADB_X_IDENTTYPE_CONNECTION 4
++#define SADB_IDENTTYPE_MAX 4
++
++#define SADB_KEY_FLAGS_MAX 0
++#endif /* __PFKEY_V2_H */
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.30 2004/04/06 02:49:00 mcr
++ * pullup of algo code from alg-branch.
++ *
++ * Revision 1.29 2003/12/22 21:35:58 mcr
++ * new patches from Dr{Who}.
++ *
++ * Revision 1.28 2003/12/22 19:33:15 mcr
++ * added 0.6c NAT-T patch.
++ *
++ * Revision 1.27 2003/12/10 01:20:01 mcr
++ * NAT-traversal patches to KLIPS.
++ *
++ * Revision 1.26 2003/10/31 02:26:44 mcr
++ * pulled up port-selector patches.
++ *
++ * Revision 1.25.4.1 2003/09/21 13:59:34 mcr
++ * pre-liminary X.509 patch - does not yet pass tests.
++ *
++ * Revision 1.25 2003/07/31 23:59:17 mcr
++ * re-introduce kernel 2.6 duplicate values for now.
++ * hope to get them changed!
++ *
++ * Revision 1.24 2003/07/31 22:55:27 mcr
++ * added some definitions to keep pfkeyv2.h files in sync.
++ *
++ * Revision 1.23 2003/05/11 00:43:48 mcr
++ * added comment about origin of values used
++ *
++ * Revision 1.22 2003/01/30 02:31:34 rgb
++ *
++ * Convert IPsecSAref_t from signed to unsigned to fix apparent SAref exhaustion bug.
++ *
++ * Revision 1.21 2002/12/16 19:26:49 mcr
++ * added definition of FS 1.xx sadb structure
++ *
++ * Revision 1.20 2002/09/20 15:40:25 rgb
++ * Added sadb_x_sa_ref to struct sadb_sa.
++ *
++ * Revision 1.19 2002/04/24 07:36:49 mcr
++ * Moved from ./lib/pfkeyv2.h,v
++ *
++ * Revision 1.18 2001/11/06 19:47:47 rgb
++ * Added packet parameter to lifetime and comb structures.
++ *
++ * Revision 1.17 2001/09/08 21:13:35 rgb
++ * Added pfkey ident extension support for ISAKMPd. (NetCelo)
++ *
++ * Revision 1.16 2001/07/06 19:49:46 rgb
++ * Added SADB_X_SAFLAGS_INFLOW for supporting incoming policy checks.
++ *
++ * Revision 1.15 2001/02/26 20:00:43 rgb
++ * Added internal IP protocol 61 for magic SAs.
++ *
++ * Revision 1.14 2001/02/08 18:51:05 rgb
++ * Include RFC document title and appendix subsection title.
++ *
++ * Revision 1.13 2000/10/10 20:10:20 rgb
++ * Added support for debug_ipcomp and debug_verbose to klipsdebug.
++ *
++ * Revision 1.12 2000/09/15 06:41:50 rgb
++ * Added V42BIS constant.
++ *
++ * Revision 1.11 2000/09/12 22:35:37 rgb
++ * Restructured to remove unused extensions from CLEARFLOW messages.
++ *
++ * Revision 1.10 2000/09/12 18:50:09 rgb
++ * Added IPIP tunnel types as algo support.
++ *
++ * Revision 1.9 2000/08/21 16:47:19 rgb
++ * Added SADB_X_CALG_* macros for IPCOMP.
++ *
++ * Revision 1.8 2000/08/09 20:43:34 rgb
++ * Fixed bitmask value for SADB_X_SAFLAGS_CLEAREROUTE.
++ *
++ * Revision 1.7 2000/01/21 06:28:37 rgb
++ * Added flow add/delete message type macros.
++ * Added flow address extension type macros.
++ * Tidied up spacing.
++ * Added klipsdebug switching capability.
++ *
++ * Revision 1.6 1999/11/27 11:56:08 rgb
++ * Add SADB_X_SATYPE_COMP for compression, eventually.
++ *
++ * Revision 1.5 1999/11/23 22:23:16 rgb
++ * This file has been moved in the distribution from klips/net/ipsec to
++ * lib.
++ *
++ * Revision 1.4 1999/04/29 15:23:29 rgb
++ * Add GRPSA support.
++ * Add support for a second SATYPE, SA and DST_ADDRESS.
++ * Add IPPROTO_IPIP support.
++ *
++ * Revision 1.3 1999/04/15 17:58:08 rgb
++ * Add RCSID labels.
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/include/zlib/zlib.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,893 @@
++/* zlib.h -- interface of the 'zlib' general purpose compression library
++ version 1.1.4, March 11th, 2002
++
++ Copyright (C) 1995-2002 Jean-loup Gailly and Mark Adler
++
++ This software is provided 'as-is', without any express or implied
++ warranty. In no event will the authors be held liable for any damages
++ arising from the use of this software.
++
++ Permission is granted to anyone to use this software for any purpose,
++ including commercial applications, and to alter it and redistribute it
++ freely, subject to the following restrictions:
++
++ 1. The origin of this software must not be misrepresented; you must not
++ claim that you wrote the original software. If you use this software
++ in a product, an acknowledgment in the product documentation would be
++ appreciated but is not required.
++ 2. Altered source versions must be plainly marked as such, and must not be
++ misrepresented as being the original software.
++ 3. This notice may not be removed or altered from any source distribution.
++
++ Jean-loup Gailly Mark Adler
++ jloup@gzip.org madler@alumni.caltech.edu
++
++
++ The data format used by the zlib library is described by RFCs (Request for
++ Comments) 1950 to 1952 in the files ftp://ds.internic.net/rfc/rfc1950.txt
++ (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format).
++*/
++
++#ifndef _ZLIB_H
++#define _ZLIB_H
++
++#include "zconf.h"
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#define ZLIB_VERSION "1.1.4"
++
++/*
++ The 'zlib' compression library provides in-memory compression and
++ decompression functions, including integrity checks of the uncompressed
++ data. This version of the library supports only one compression method
++ (deflation) but other algorithms will be added later and will have the same
++ stream interface.
++
++ Compression can be done in a single step if the buffers are large
++ enough (for example if an input file is mmap'ed), or can be done by
++ repeated calls of the compression function. In the latter case, the
++ application must provide more input and/or consume the output
++ (providing more output space) before each call.
++
++ The library also supports reading and writing files in gzip (.gz) format
++ with an interface similar to that of stdio.
++
++ The library does not install any signal handler. The decoder checks
++ the consistency of the compressed data, so the library should never
++ crash even in case of corrupted input.
++*/
++
++typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size));
++typedef void (*free_func) OF((voidpf opaque, voidpf address));
++
++struct internal_state;
++
++typedef struct z_stream_s {
++ Bytef *next_in; /* next input byte */
++ uInt avail_in; /* number of bytes available at next_in */
++ uLong total_in; /* total nb of input bytes read so far */
++
++ Bytef *next_out; /* next output byte should be put there */
++ uInt avail_out; /* remaining free space at next_out */
++ uLong total_out; /* total nb of bytes output so far */
++
++ const char *msg; /* last error message, NULL if no error */
++ struct internal_state FAR *state; /* not visible by applications */
++
++ alloc_func zalloc; /* used to allocate the internal state */
++ free_func zfree; /* used to free the internal state */
++ voidpf opaque; /* private data object passed to zalloc and zfree */
++
++ int data_type; /* best guess about the data type: ascii or binary */
++ uLong adler; /* adler32 value of the uncompressed data */
++ uLong reserved; /* reserved for future use */
++} z_stream;
++
++typedef z_stream FAR *z_streamp;
++
++/*
++ The application must update next_in and avail_in when avail_in has
++ dropped to zero. It must update next_out and avail_out when avail_out
++ has dropped to zero. The application must initialize zalloc, zfree and
++ opaque before calling the init function. All other fields are set by the
++ compression library and must not be updated by the application.
++
++ The opaque value provided by the application will be passed as the first
++ parameter for calls of zalloc and zfree. This can be useful for custom
++ memory management. The compression library attaches no meaning to the
++ opaque value.
++
++ zalloc must return Z_NULL if there is not enough memory for the object.
++ If zlib is used in a multi-threaded application, zalloc and zfree must be
++ thread safe.
++
++ On 16-bit systems, the functions zalloc and zfree must be able to allocate
++ exactly 65536 bytes, but will not be required to allocate more than this
++ if the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS,
++ pointers returned by zalloc for objects of exactly 65536 bytes *must*
++ have their offset normalized to zero. The default allocation function
++ provided by this library ensures this (see zutil.c). To reduce memory
++ requirements and avoid any allocation of 64K objects, at the expense of
++ compression ratio, compile the library with -DMAX_WBITS=14 (see zconf.h).
++
++ The fields total_in and total_out can be used for statistics or
++ progress reports. After compression, total_in holds the total size of
++ the uncompressed data and may be saved for use in the decompressor
++ (particularly if the decompressor wants to decompress everything in
++ a single step).
++*/
++
++ /* constants */
++
++#define Z_NO_FLUSH 0
++#define Z_PARTIAL_FLUSH 1 /* will be removed, use Z_SYNC_FLUSH instead */
++#define Z_SYNC_FLUSH 2
++#define Z_FULL_FLUSH 3
++#define Z_FINISH 4
++/* Allowed flush values; see deflate() below for details */
++
++#define Z_OK 0
++#define Z_STREAM_END 1
++#define Z_NEED_DICT 2
++#define Z_ERRNO (-1)
++#define Z_STREAM_ERROR (-2)
++#define Z_DATA_ERROR (-3)
++#define Z_MEM_ERROR (-4)
++#define Z_BUF_ERROR (-5)
++#define Z_VERSION_ERROR (-6)
++/* Return codes for the compression/decompression functions. Negative
++ * values are errors, positive values are used for special but normal events.
++ */
++
++#define Z_NO_COMPRESSION 0
++#define Z_BEST_SPEED 1
++#define Z_BEST_COMPRESSION 9
++#define Z_DEFAULT_COMPRESSION (-1)
++/* compression levels */
++
++#define Z_FILTERED 1
++#define Z_HUFFMAN_ONLY 2
++#define Z_DEFAULT_STRATEGY 0
++/* compression strategy; see deflateInit2() below for details */
++
++#define Z_BINARY 0
++#define Z_ASCII 1
++#define Z_UNKNOWN 2
++/* Possible values of the data_type field */
++
++#define Z_DEFLATED 8
++/* The deflate compression method (the only one supported in this version) */
++
++#define Z_NULL 0 /* for initializing zalloc, zfree, opaque */
++
++#define zlib_version zlibVersion()
++/* for compatibility with versions < 1.0.2 */
++
++ /* basic functions */
++
++ZEXTERN const char * ZEXPORT zlibVersion OF((void));
++/* The application can compare zlibVersion and ZLIB_VERSION for consistency.
++ If the first character differs, the library code actually used is
++ not compatible with the zlib.h header file used by the application.
++ This check is automatically made by deflateInit and inflateInit.
++ */
++
++/*
++ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level));
++
++ Initializes the internal stream state for compression. The fields
++ zalloc, zfree and opaque must be initialized before by the caller.
++ If zalloc and zfree are set to Z_NULL, deflateInit updates them to
++ use default allocation functions.
++
++ The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9:
++ 1 gives best speed, 9 gives best compression, 0 gives no compression at
++ all (the input data is simply copied a block at a time).
++ Z_DEFAULT_COMPRESSION requests a default compromise between speed and
++ compression (currently equivalent to level 6).
++
++ deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not
++ enough memory, Z_STREAM_ERROR if level is not a valid compression level,
++ Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible
++ with the version assumed by the caller (ZLIB_VERSION).
++ msg is set to null if there is no error message. deflateInit does not
++ perform any compression: this will be done by deflate().
++*/
++
++
++ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush));
++/*
++ deflate compresses as much data as possible, and stops when the input
++ buffer becomes empty or the output buffer becomes full. It may introduce some
++ output latency (reading input without producing any output) except when
++ forced to flush.
++
++ The detailed semantics are as follows. deflate performs one or both of the
++ following actions:
++
++ - Compress more input starting at next_in and update next_in and avail_in
++ accordingly. If not all input can be processed (because there is not
++ enough room in the output buffer), next_in and avail_in are updated and
++ processing will resume at this point for the next call of deflate().
++
++ - Provide more output starting at next_out and update next_out and avail_out
++ accordingly. This action is forced if the parameter flush is non zero.
++ Forcing flush frequently degrades the compression ratio, so this parameter
++ should be set only when necessary (in interactive applications).
++ Some output may be provided even if flush is not set.
++
++ Before the call of deflate(), the application should ensure that at least
++ one of the actions is possible, by providing more input and/or consuming
++ more output, and updating avail_in or avail_out accordingly; avail_out
++ should never be zero before the call. The application can consume the
++ compressed output when it wants, for example when the output buffer is full
++ (avail_out == 0), or after each call of deflate(). If deflate returns Z_OK
++ and with zero avail_out, it must be called again after making room in the
++ output buffer because there might be more output pending.
++
++ If the parameter flush is set to Z_SYNC_FLUSH, all pending output is
++ flushed to the output buffer and the output is aligned on a byte boundary, so
++ that the decompressor can get all input data available so far. (In particular
++ avail_in is zero after the call if enough output space has been provided
++ before the call.) Flushing may degrade compression for some compression
++ algorithms and so it should be used only when necessary.
++
++ If flush is set to Z_FULL_FLUSH, all output is flushed as with
++ Z_SYNC_FLUSH, and the compression state is reset so that decompression can
++ restart from this point if previous compressed data has been damaged or if
++ random access is desired. Using Z_FULL_FLUSH too often can seriously degrade
++ the compression.
++
++ If deflate returns with avail_out == 0, this function must be called again
++ with the same value of the flush parameter and more output space (updated
++ avail_out), until the flush is complete (deflate returns with non-zero
++ avail_out).
++
++ If the parameter flush is set to Z_FINISH, pending input is processed,
++ pending output is flushed and deflate returns with Z_STREAM_END if there
++ was enough output space; if deflate returns with Z_OK, this function must be
++ called again with Z_FINISH and more output space (updated avail_out) but no
++ more input data, until it returns with Z_STREAM_END or an error. After
++ deflate has returned Z_STREAM_END, the only possible operations on the
++ stream are deflateReset or deflateEnd.
++
++ Z_FINISH can be used immediately after deflateInit if all the compression
++ is to be done in a single step. In this case, avail_out must be at least
++ 0.1% larger than avail_in plus 12 bytes. If deflate does not return
++ Z_STREAM_END, then it must be called again as described above.
++
++ deflate() sets strm->adler to the adler32 checksum of all input read
++ so far (that is, total_in bytes).
++
++ deflate() may update data_type if it can make a good guess about
++ the input data type (Z_ASCII or Z_BINARY). In doubt, the data is considered
++ binary. This field is only for information purposes and does not affect
++ the compression algorithm in any manner.
++
++ deflate() returns Z_OK if some progress has been made (more input
++ processed or more output produced), Z_STREAM_END if all input has been
++ consumed and all output has been produced (only when flush is set to
++ Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example
++ if next_in or next_out was NULL), Z_BUF_ERROR if no progress is possible
++ (for example avail_in or avail_out was zero).
++*/
++
++
++ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm));
++/*
++ All dynamically allocated data structures for this stream are freed.
++ This function discards any unprocessed input and does not flush any
++ pending output.
++
++ deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the
++ stream state was inconsistent, Z_DATA_ERROR if the stream was freed
++ prematurely (some input or output was discarded). In the error case,
++ msg may be set but then points to a static string (which must not be
++ deallocated).
++*/
++
++
++/*
++ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm));
++
++ Initializes the internal stream state for decompression. The fields
++ next_in, avail_in, zalloc, zfree and opaque must be initialized before by
++ the caller. If next_in is not Z_NULL and avail_in is large enough (the exact
++ value depends on the compression method), inflateInit determines the
++ compression method from the zlib header and allocates all data structures
++ accordingly; otherwise the allocation will be deferred to the first call of
++ inflate. If zalloc and zfree are set to Z_NULL, inflateInit updates them to
++ use default allocation functions.
++
++ inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough
++ memory, Z_VERSION_ERROR if the zlib library version is incompatible with the
++ version assumed by the caller. msg is set to null if there is no error
++ message. inflateInit does not perform any decompression apart from reading
++ the zlib header if present: this will be done by inflate(). (So next_in and
++ avail_in may be modified, but next_out and avail_out are unchanged.)
++*/
++
++
++ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush));
++/*
++ inflate decompresses as much data as possible, and stops when the input
++ buffer becomes empty or the output buffer becomes full. It may some
++ introduce some output latency (reading input without producing any output)
++ except when forced to flush.
++
++ The detailed semantics are as follows. inflate performs one or both of the
++ following actions:
++
++ - Decompress more input starting at next_in and update next_in and avail_in
++ accordingly. If not all input can be processed (because there is not
++ enough room in the output buffer), next_in is updated and processing
++ will resume at this point for the next call of inflate().
++
++ - Provide more output starting at next_out and update next_out and avail_out
++ accordingly. inflate() provides as much output as possible, until there
++ is no more input data or no more space in the output buffer (see below
++ about the flush parameter).
++
++ Before the call of inflate(), the application should ensure that at least
++ one of the actions is possible, by providing more input and/or consuming
++ more output, and updating the next_* and avail_* values accordingly.
++ The application can consume the uncompressed output when it wants, for
++ example when the output buffer is full (avail_out == 0), or after each
++ call of inflate(). If inflate returns Z_OK and with zero avail_out, it
++ must be called again after making room in the output buffer because there
++ might be more output pending.
++
++ If the parameter flush is set to Z_SYNC_FLUSH, inflate flushes as much
++ output as possible to the output buffer. The flushing behavior of inflate is
++ not specified for values of the flush parameter other than Z_SYNC_FLUSH
++ and Z_FINISH, but the current implementation actually flushes as much output
++ as possible anyway.
++
++ inflate() should normally be called until it returns Z_STREAM_END or an
++ error. However if all decompression is to be performed in a single step
++ (a single call of inflate), the parameter flush should be set to
++ Z_FINISH. In this case all pending input is processed and all pending
++ output is flushed; avail_out must be large enough to hold all the
++ uncompressed data. (The size of the uncompressed data may have been saved
++ by the compressor for this purpose.) The next operation on this stream must
++ be inflateEnd to deallocate the decompression state. The use of Z_FINISH
++ is never required, but can be used to inform inflate that a faster routine
++ may be used for the single inflate() call.
++
++ If a preset dictionary is needed at this point (see inflateSetDictionary
++ below), inflate sets strm-adler to the adler32 checksum of the
++ dictionary chosen by the compressor and returns Z_NEED_DICT; otherwise
++ it sets strm->adler to the adler32 checksum of all output produced
++ so far (that is, total_out bytes) and returns Z_OK, Z_STREAM_END or
++ an error code as described below. At the end of the stream, inflate()
++ checks that its computed adler32 checksum is equal to that saved by the
++ compressor and returns Z_STREAM_END only if the checksum is correct.
++
++ inflate() returns Z_OK if some progress has been made (more input processed
++ or more output produced), Z_STREAM_END if the end of the compressed data has
++ been reached and all uncompressed output has been produced, Z_NEED_DICT if a
++ preset dictionary is needed at this point, Z_DATA_ERROR if the input data was
++ corrupted (input stream not conforming to the zlib format or incorrect
++ adler32 checksum), Z_STREAM_ERROR if the stream structure was inconsistent
++ (for example if next_in or next_out was NULL), Z_MEM_ERROR if there was not
++ enough memory, Z_BUF_ERROR if no progress is possible or if there was not
++ enough room in the output buffer when Z_FINISH is used. In the Z_DATA_ERROR
++ case, the application may then call inflateSync to look for a good
++ compression block.
++*/
++
++
++ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm));
++/*
++ All dynamically allocated data structures for this stream are freed.
++ This function discards any unprocessed input and does not flush any
++ pending output.
++
++ inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state
++ was inconsistent. In the error case, msg may be set but then points to a
++ static string (which must not be deallocated).
++*/
++
++ /* Advanced functions */
++
++/*
++ The following functions are needed only in some special applications.
++*/
++
++/*
++ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm,
++ int level,
++ int method,
++ int windowBits,
++ int memLevel,
++ int strategy));
++
++ This is another version of deflateInit with more compression options. The
++ fields next_in, zalloc, zfree and opaque must be initialized before by
++ the caller.
++
++ The method parameter is the compression method. It must be Z_DEFLATED in
++ this version of the library.
++
++ The windowBits parameter is the base two logarithm of the window size
++ (the size of the history buffer). It should be in the range 8..15 for this
++ version of the library. Larger values of this parameter result in better
++ compression at the expense of memory usage. The default value is 15 if
++ deflateInit is used instead.
++
++ The memLevel parameter specifies how much memory should be allocated
++ for the internal compression state. memLevel=1 uses minimum memory but
++ is slow and reduces compression ratio; memLevel=9 uses maximum memory
++ for optimal speed. The default value is 8. See zconf.h for total memory
++ usage as a function of windowBits and memLevel.
++
++ The strategy parameter is used to tune the compression algorithm. Use the
++ value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a
++ filter (or predictor), or Z_HUFFMAN_ONLY to force Huffman encoding only (no
++ string match). Filtered data consists mostly of small values with a
++ somewhat random distribution. In this case, the compression algorithm is
++ tuned to compress them better. The effect of Z_FILTERED is to force more
++ Huffman coding and less string matching; it is somewhat intermediate
++ between Z_DEFAULT and Z_HUFFMAN_ONLY. The strategy parameter only affects
++ the compression ratio but not the correctness of the compressed output even
++ if it is not set appropriately.
++
++ deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
++ memory, Z_STREAM_ERROR if a parameter is invalid (such as an invalid
++ method). msg is set to null if there is no error message. deflateInit2 does
++ not perform any compression: this will be done by deflate().
++*/
++
++ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm,
++ const Bytef *dictionary,
++ uInt dictLength));
++/*
++ Initializes the compression dictionary from the given byte sequence
++ without producing any compressed output. This function must be called
++ immediately after deflateInit, deflateInit2 or deflateReset, before any
++ call of deflate. The compressor and decompressor must use exactly the same
++ dictionary (see inflateSetDictionary).
++
++ The dictionary should consist of strings (byte sequences) that are likely
++ to be encountered later in the data to be compressed, with the most commonly
++ used strings preferably put towards the end of the dictionary. Using a
++ dictionary is most useful when the data to be compressed is short and can be
++ predicted with good accuracy; the data can then be compressed better than
++ with the default empty dictionary.
++
++ Depending on the size of the compression data structures selected by
++ deflateInit or deflateInit2, a part of the dictionary may in effect be
++ discarded, for example if the dictionary is larger than the window size in
++ deflate or deflate2. Thus the strings most likely to be useful should be
++ put at the end of the dictionary, not at the front.
++
++ Upon return of this function, strm->adler is set to the Adler32 value
++ of the dictionary; the decompressor may later use this value to determine
++ which dictionary has been used by the compressor. (The Adler32 value
++ applies to the whole dictionary even if only a subset of the dictionary is
++ actually used by the compressor.)
++
++ deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a
++ parameter is invalid (such as NULL dictionary) or the stream state is
++ inconsistent (for example if deflate has already been called for this stream
++ or if the compression method is bsort). deflateSetDictionary does not
++ perform any compression: this will be done by deflate().
++*/
++
++ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest,
++ z_streamp source));
++/*
++ Sets the destination stream as a complete copy of the source stream.
++
++ This function can be useful when several compression strategies will be
++ tried, for example when there are several ways of pre-processing the input
++ data with a filter. The streams that will be discarded should then be freed
++ by calling deflateEnd. Note that deflateCopy duplicates the internal
++ compression state which can be quite large, so this strategy is slow and
++ can consume lots of memory.
++
++ deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not
++ enough memory, Z_STREAM_ERROR if the source stream state was inconsistent
++ (such as zalloc being NULL). msg is left unchanged in both source and
++ destination.
++*/
++
++ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm));
++/*
++ This function is equivalent to deflateEnd followed by deflateInit,
++ but does not free and reallocate all the internal compression state.
++ The stream will keep the same compression level and any other attributes
++ that may have been set by deflateInit2.
++
++ deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source
++ stream state was inconsistent (such as zalloc or state being NULL).
++*/
++
++ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm,
++ int level,
++ int strategy));
++/*
++ Dynamically update the compression level and compression strategy. The
++ interpretation of level and strategy is as in deflateInit2. This can be
++ used to switch between compression and straight copy of the input data, or
++ to switch to a different kind of input data requiring a different
++ strategy. If the compression level is changed, the input available so far
++ is compressed with the old level (and may be flushed); the new level will
++ take effect only at the next call of deflate().
++
++ Before the call of deflateParams, the stream state must be set as for
++ a call of deflate(), since the currently available input may have to
++ be compressed and flushed. In particular, strm->avail_out must be non-zero.
++
++ deflateParams returns Z_OK if success, Z_STREAM_ERROR if the source
++ stream state was inconsistent or if a parameter was invalid, Z_BUF_ERROR
++ if strm->avail_out was zero.
++*/
++
++/*
++ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm,
++ int windowBits));
++
++ This is another version of inflateInit with an extra parameter. The
++ fields next_in, avail_in, zalloc, zfree and opaque must be initialized
++ before by the caller.
++
++ The windowBits parameter is the base two logarithm of the maximum window
++ size (the size of the history buffer). It should be in the range 8..15 for
++ this version of the library. The default value is 15 if inflateInit is used
++ instead. If a compressed stream with a larger window size is given as
++ input, inflate() will return with the error code Z_DATA_ERROR instead of
++ trying to allocate a larger window.
++
++ inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
++ memory, Z_STREAM_ERROR if a parameter is invalid (such as a negative
++ memLevel). msg is set to null if there is no error message. inflateInit2
++ does not perform any decompression apart from reading the zlib header if
++ present: this will be done by inflate(). (So next_in and avail_in may be
++ modified, but next_out and avail_out are unchanged.)
++*/
++
++ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm,
++ const Bytef *dictionary,
++ uInt dictLength));
++/*
++ Initializes the decompression dictionary from the given uncompressed byte
++ sequence. This function must be called immediately after a call of inflate
++ if this call returned Z_NEED_DICT. The dictionary chosen by the compressor
++ can be determined from the Adler32 value returned by this call of
++ inflate. The compressor and decompressor must use exactly the same
++ dictionary (see deflateSetDictionary).
++
++ inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a
++ parameter is invalid (such as NULL dictionary) or the stream state is
++ inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the
++ expected one (incorrect Adler32 value). inflateSetDictionary does not
++ perform any decompression: this will be done by subsequent calls of
++ inflate().
++*/
++
++ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm));
++/*
++ Skips invalid compressed data until a full flush point (see above the
++ description of deflate with Z_FULL_FLUSH) can be found, or until all
++ available input is skipped. No output is provided.
++
++ inflateSync returns Z_OK if a full flush point has been found, Z_BUF_ERROR
++ if no more input was provided, Z_DATA_ERROR if no flush point has been found,
++ or Z_STREAM_ERROR if the stream structure was inconsistent. In the success
++ case, the application may save the current current value of total_in which
++ indicates where valid compressed data was found. In the error case, the
++ application may repeatedly call inflateSync, providing more input each time,
++ until success or end of the input data.
++*/
++
++ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm));
++/*
++ This function is equivalent to inflateEnd followed by inflateInit,
++ but does not free and reallocate all the internal decompression state.
++ The stream will keep attributes that may have been set by inflateInit2.
++
++ inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source
++ stream state was inconsistent (such as zalloc or state being NULL).
++*/
++
++
++ /* utility functions */
++
++/*
++ The following utility functions are implemented on top of the
++ basic stream-oriented functions. To simplify the interface, some
++ default options are assumed (compression level and memory usage,
++ standard memory allocation functions). The source code of these
++ utility functions can easily be modified if you need special options.
++*/
++
++ZEXTERN int ZEXPORT compress OF((Bytef *dest, uLongf *destLen,
++ const Bytef *source, uLong sourceLen));
++/*
++ Compresses the source buffer into the destination buffer. sourceLen is
++ the byte length of the source buffer. Upon entry, destLen is the total
++ size of the destination buffer, which must be at least 0.1% larger than
++ sourceLen plus 12 bytes. Upon exit, destLen is the actual size of the
++ compressed buffer.
++ This function can be used to compress a whole file at once if the
++ input file is mmap'ed.
++ compress returns Z_OK if success, Z_MEM_ERROR if there was not
++ enough memory, Z_BUF_ERROR if there was not enough room in the output
++ buffer.
++*/
++
++ZEXTERN int ZEXPORT compress2 OF((Bytef *dest, uLongf *destLen,
++ const Bytef *source, uLong sourceLen,
++ int level));
++/*
++ Compresses the source buffer into the destination buffer. The level
++ parameter has the same meaning as in deflateInit. sourceLen is the byte
++ length of the source buffer. Upon entry, destLen is the total size of the
++ destination buffer, which must be at least 0.1% larger than sourceLen plus
++ 12 bytes. Upon exit, destLen is the actual size of the compressed buffer.
++
++ compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
++ memory, Z_BUF_ERROR if there was not enough room in the output buffer,
++ Z_STREAM_ERROR if the level parameter is invalid.
++*/
++
++ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen,
++ const Bytef *source, uLong sourceLen));
++/*
++ Decompresses the source buffer into the destination buffer. sourceLen is
++ the byte length of the source buffer. Upon entry, destLen is the total
++ size of the destination buffer, which must be large enough to hold the
++ entire uncompressed data. (The size of the uncompressed data must have
++ been saved previously by the compressor and transmitted to the decompressor
++ by some mechanism outside the scope of this compression library.)
++ Upon exit, destLen is the actual size of the compressed buffer.
++ This function can be used to decompress a whole file at once if the
++ input file is mmap'ed.
++
++ uncompress returns Z_OK if success, Z_MEM_ERROR if there was not
++ enough memory, Z_BUF_ERROR if there was not enough room in the output
++ buffer, or Z_DATA_ERROR if the input data was corrupted.
++*/
++
++
++typedef voidp gzFile;
++
++ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode));
++/*
++ Opens a gzip (.gz) file for reading or writing. The mode parameter
++ is as in fopen ("rb" or "wb") but can also include a compression level
++ ("wb9") or a strategy: 'f' for filtered data as in "wb6f", 'h' for
++ Huffman only compression as in "wb1h". (See the description
++ of deflateInit2 for more information about the strategy parameter.)
++
++ gzopen can be used to read a file which is not in gzip format; in this
++ case gzread will directly read from the file without decompression.
++
++ gzopen returns NULL if the file could not be opened or if there was
++ insufficient memory to allocate the (de)compression state; errno
++ can be checked to distinguish the two cases (if errno is zero, the
++ zlib error is Z_MEM_ERROR). */
++
++ZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode));
++/*
++ gzdopen() associates a gzFile with the file descriptor fd. File
++ descriptors are obtained from calls like open, dup, creat, pipe or
++ fileno (in the file has been previously opened with fopen).
++ The mode parameter is as in gzopen.
++ The next call of gzclose on the returned gzFile will also close the
++ file descriptor fd, just like fclose(fdopen(fd), mode) closes the file
++ descriptor fd. If you want to keep fd open, use gzdopen(dup(fd), mode).
++ gzdopen returns NULL if there was insufficient memory to allocate
++ the (de)compression state.
++*/
++
++ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy));
++/*
++ Dynamically update the compression level or strategy. See the description
++ of deflateInit2 for the meaning of these parameters.
++ gzsetparams returns Z_OK if success, or Z_STREAM_ERROR if the file was not
++ opened for writing.
++*/
++
++ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len));
++/*
++ Reads the given number of uncompressed bytes from the compressed file.
++ If the input file was not in gzip format, gzread copies the given number
++ of bytes into the buffer.
++ gzread returns the number of uncompressed bytes actually read (0 for
++ end of file, -1 for error). */
++
++ZEXTERN int ZEXPORT gzwrite OF((gzFile file,
++ const voidp buf, unsigned len));
++/*
++ Writes the given number of uncompressed bytes into the compressed file.
++ gzwrite returns the number of uncompressed bytes actually written
++ (0 in case of error).
++*/
++
++ZEXTERN int ZEXPORTVA gzprintf OF((gzFile file, const char *format, ...));
++/*
++ Converts, formats, and writes the args to the compressed file under
++ control of the format string, as in fprintf. gzprintf returns the number of
++ uncompressed bytes actually written (0 in case of error).
++*/
++
++ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s));
++/*
++ Writes the given null-terminated string to the compressed file, excluding
++ the terminating null character.
++ gzputs returns the number of characters written, or -1 in case of error.
++*/
++
++ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len));
++/*
++ Reads bytes from the compressed file until len-1 characters are read, or
++ a newline character is read and transferred to buf, or an end-of-file
++ condition is encountered. The string is then terminated with a null
++ character.
++ gzgets returns buf, or Z_NULL in case of error.
++*/
++
++ZEXTERN int ZEXPORT gzputc OF((gzFile file, int c));
++/*
++ Writes c, converted to an unsigned char, into the compressed file.
++ gzputc returns the value that was written, or -1 in case of error.
++*/
++
++ZEXTERN int ZEXPORT gzgetc OF((gzFile file));
++/*
++ Reads one byte from the compressed file. gzgetc returns this byte
++ or -1 in case of end of file or error.
++*/
++
++ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush));
++/*
++ Flushes all pending output into the compressed file. The parameter
++ flush is as in the deflate() function. The return value is the zlib
++ error number (see function gzerror below). gzflush returns Z_OK if
++ the flush parameter is Z_FINISH and all output could be flushed.
++ gzflush should be called only when strictly necessary because it can
++ degrade compression.
++*/
++
++ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file,
++ z_off_t offset, int whence));
++/*
++ Sets the starting position for the next gzread or gzwrite on the
++ given compressed file. The offset represents a number of bytes in the
++ uncompressed data stream. The whence parameter is defined as in lseek(2);
++ the value SEEK_END is not supported.
++ If the file is opened for reading, this function is emulated but can be
++ extremely slow. If the file is opened for writing, only forward seeks are
++ supported; gzseek then compresses a sequence of zeroes up to the new
++ starting position.
++
++ gzseek returns the resulting offset location as measured in bytes from
++ the beginning of the uncompressed stream, or -1 in case of error, in
++ particular if the file is opened for writing and the new starting position
++ would be before the current position.
++*/
++
++ZEXTERN int ZEXPORT gzrewind OF((gzFile file));
++/*
++ Rewinds the given file. This function is supported only for reading.
++
++ gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET)
++*/
++
++ZEXTERN z_off_t ZEXPORT gztell OF((gzFile file));
++/*
++ Returns the starting position for the next gzread or gzwrite on the
++ given compressed file. This position represents a number of bytes in the
++ uncompressed data stream.
++
++ gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR)
++*/
++
++ZEXTERN int ZEXPORT gzeof OF((gzFile file));
++/*
++ Returns 1 when EOF has previously been detected reading the given
++ input stream, otherwise zero.
++*/
++
++ZEXTERN int ZEXPORT gzclose OF((gzFile file));
++/*
++ Flushes all pending output if necessary, closes the compressed file
++ and deallocates all the (de)compression state. The return value is the zlib
++ error number (see function gzerror below).
++*/
++
++ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum));
++/*
++ Returns the error message for the last error which occurred on the
++ given compressed file. errnum is set to zlib error number. If an
++ error occurred in the file system and not in the compression library,
++ errnum is set to Z_ERRNO and the application may consult errno
++ to get the exact error code.
++*/
++
++ /* checksum functions */
++
++/*
++ These functions are not related to compression but are exported
++ anyway because they might be useful in applications using the
++ compression library.
++*/
++
++ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len));
++
++/*
++ Update a running Adler-32 checksum with the bytes buf[0..len-1] and
++ return the updated checksum. If buf is NULL, this function returns
++ the required initial value for the checksum.
++ An Adler-32 checksum is almost as reliable as a CRC32 but can be computed
++ much faster. Usage example:
++
++ uLong adler = adler32(0L, Z_NULL, 0);
++
++ while (read_buffer(buffer, length) != EOF) {
++ adler = adler32(adler, buffer, length);
++ }
++ if (adler != original_adler) error();
++*/
++
++ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len));
++/*
++ Update a running crc with the bytes buf[0..len-1] and return the updated
++ crc. If buf is NULL, this function returns the required initial value
++ for the crc. Pre- and post-conditioning (one's complement) is performed
++ within this function so it shouldn't be done by the application.
++ Usage example:
++
++ uLong crc = crc32(0L, Z_NULL, 0);
++
++ while (read_buffer(buffer, length) != EOF) {
++ crc = crc32(crc, buffer, length);
++ }
++ if (crc != original_crc) error();
++*/
++
++
++ /* various hacks, don't look :) */
++
++/* deflateInit and inflateInit are macros to allow checking the zlib version
++ * and the compiler's view of z_stream:
++ */
++ZEXTERN int ZEXPORT deflateInit_ OF((z_streamp strm, int level,
++ const char *version, int stream_size));
++ZEXTERN int ZEXPORT inflateInit_ OF((z_streamp strm,
++ const char *version, int stream_size));
++ZEXTERN int ZEXPORT deflateInit2_ OF((z_streamp strm, int level, int method,
++ int windowBits, int memLevel,
++ int strategy, const char *version,
++ int stream_size));
++ZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int windowBits,
++ const char *version, int stream_size));
++#define deflateInit(strm, level) \
++ deflateInit_((strm), (level), ZLIB_VERSION, sizeof(z_stream))
++#define inflateInit(strm) \
++ inflateInit_((strm), ZLIB_VERSION, sizeof(z_stream))
++#define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \
++ deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\
++ (strategy), ZLIB_VERSION, sizeof(z_stream))
++#define inflateInit2(strm, windowBits) \
++ inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream))
++
++
++#if !defined(_Z_UTIL_H) && !defined(NO_DUMMY_DECL)
++ struct internal_state {int dummy;}; /* hack for buggy compilers */
++#endif
++
++ZEXTERN const char * ZEXPORT zError OF((int err));
++ZEXTERN int ZEXPORT inflateSyncPoint OF((z_streamp z));
++ZEXTERN const uLongf * ZEXPORT get_crc_table OF((void));
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif /* _ZLIB_H */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/include/zlib/zutil.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,225 @@
++/* zutil.h -- internal interface and configuration of the compression library
++ * Copyright (C) 1995-2002 Jean-loup Gailly.
++ * For conditions of distribution and use, see copyright notice in zlib.h
++ */
++
++/* WARNING: this file should *not* be used by applications. It is
++ part of the implementation of the compression library and is
++ subject to change. Applications should only use zlib.h.
++ */
++
++/* @(#) $Id$ */
++
++#ifndef _Z_UTIL_H
++#define _Z_UTIL_H
++
++#include "zlib.h"
++
++#include <linux/string.h>
++#define HAVE_MEMCPY
++
++#if 0 // #ifdef STDC
++# include <stddef.h>
++# include <string.h>
++# include <stdlib.h>
++#endif
++#ifndef __KERNEL__
++#ifdef NO_ERRNO_H
++ extern int errno;
++#else
++# include <errno.h>
++#endif
++#endif
++
++#ifndef local
++# define local static
++#endif
++/* compile with -Dlocal if your debugger can't find static symbols */
++
++typedef unsigned char uch;
++typedef uch FAR uchf;
++typedef unsigned short ush;
++typedef ush FAR ushf;
++typedef unsigned long ulg;
++
++extern const char *z_errmsg[10]; /* indexed by 2-zlib_error */
++/* (size given to avoid silly warnings with Visual C++) */
++
++#define ERR_MSG(err) z_errmsg[Z_NEED_DICT-(err)]
++
++#define ERR_RETURN(strm,err) \
++ return (strm->msg = ERR_MSG(err), (err))
++/* To be used only when the state is known to be valid */
++
++ /* common constants */
++
++#ifndef DEF_WBITS
++# define DEF_WBITS MAX_WBITS
++#endif
++/* default windowBits for decompression. MAX_WBITS is for compression only */
++
++#if MAX_MEM_LEVEL >= 8
++# define DEF_MEM_LEVEL 8
++#else
++# define DEF_MEM_LEVEL MAX_MEM_LEVEL
++#endif
++/* default memLevel */
++
++#define STORED_BLOCK 0
++#define STATIC_TREES 1
++#define DYN_TREES 2
++/* The three kinds of block type */
++
++#define MIN_MATCH 3
++#define MAX_MATCH 258
++/* The minimum and maximum match lengths */
++
++#define PRESET_DICT 0x20 /* preset dictionary flag in zlib header */
++
++ /* target dependencies */
++
++#ifdef MSDOS
++# define OS_CODE 0x00
++# if defined(__TURBOC__) || defined(__BORLANDC__)
++# if(__STDC__ == 1) && (defined(__LARGE__) || defined(__COMPACT__))
++ /* Allow compilation with ANSI keywords only enabled */
++ void _Cdecl farfree( void *block );
++ void *_Cdecl farmalloc( unsigned long nbytes );
++# else
++# include <alloc.h>
++# endif
++# else /* MSC or DJGPP */
++# include <malloc.h>
++# endif
++#endif
++
++#ifdef OS2
++# define OS_CODE 0x06
++#endif
++
++#ifdef WIN32 /* Window 95 & Windows NT */
++# define OS_CODE 0x0b
++#endif
++
++#if defined(VAXC) || defined(VMS)
++# define OS_CODE 0x02
++# define F_OPEN(name, mode) \
++ fopen((name), (mode), "mbc=60", "ctx=stm", "rfm=fix", "mrs=512")
++#endif
++
++#ifdef AMIGA
++# define OS_CODE 0x01
++#endif
++
++#if defined(ATARI) || defined(atarist)
++# define OS_CODE 0x05
++#endif
++
++#if defined(MACOS) || defined(TARGET_OS_MAC)
++# define OS_CODE 0x07
++# if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os
++# include <unix.h> /* for fdopen */
++# else
++# ifndef fdopen
++# define fdopen(fd,mode) NULL /* No fdopen() */
++# endif
++# endif
++#endif
++
++#ifdef __50SERIES /* Prime/PRIMOS */
++# define OS_CODE 0x0F
++#endif
++
++#ifdef TOPS20
++# define OS_CODE 0x0a
++#endif
++
++#if defined(_BEOS_) || defined(RISCOS)
++# define fdopen(fd,mode) NULL /* No fdopen() */
++#endif
++
++#if (defined(_MSC_VER) && (_MSC_VER > 600))
++# define fdopen(fd,type) _fdopen(fd,type)
++#endif
++
++
++ /* Common defaults */
++
++#ifndef OS_CODE
++# define OS_CODE 0x03 /* assume Unix */
++#endif
++
++#ifndef F_OPEN
++# define F_OPEN(name, mode) fopen((name), (mode))
++#endif
++
++ /* functions */
++
++#ifdef HAVE_STRERROR
++ extern char *strerror OF((int));
++# define zstrerror(errnum) strerror(errnum)
++#else
++# define zstrerror(errnum) ""
++#endif
++
++#if defined(pyr)
++# define NO_MEMCPY
++#endif
++#if defined(SMALL_MEDIUM) && !defined(_MSC_VER) && !defined(__SC__)
++ /* Use our own functions for small and medium model with MSC <= 5.0.
++ * You may have to use the same strategy for Borland C (untested).
++ * The __SC__ check is for Symantec.
++ */
++# define NO_MEMCPY
++#endif
++#if defined(STDC) && !defined(HAVE_MEMCPY) && !defined(NO_MEMCPY)
++# define HAVE_MEMCPY
++#endif
++#ifdef HAVE_MEMCPY
++# ifdef SMALL_MEDIUM /* MSDOS small or medium model */
++# define zmemcpy _fmemcpy
++# define zmemcmp _fmemcmp
++# define zmemzero(dest, len) _fmemset(dest, 0, len)
++# else
++# define zmemcpy memcpy
++# define zmemcmp memcmp
++# define zmemzero(dest, len) memset(dest, 0, len)
++# endif
++#else
++ extern void zmemcpy OF((Bytef* dest, const Bytef* source, uInt len));
++ extern int zmemcmp OF((const Bytef* s1, const Bytef* s2, uInt len));
++ extern void zmemzero OF((Bytef* dest, uInt len));
++#endif
++
++/* Diagnostic functions */
++#ifdef DEBUG
++# include <stdio.h>
++ extern int z_verbose;
++ extern void z_error OF((char *m));
++# define Assert(cond,msg) {if(!(cond)) z_error(msg);}
++# define Trace(x) {if (z_verbose>=0) fprintf x ;}
++# define Tracev(x) {if (z_verbose>0) fprintf x ;}
++# define Tracevv(x) {if (z_verbose>1) fprintf x ;}
++# define Tracec(c,x) {if (z_verbose>0 && (c)) fprintf x ;}
++# define Tracecv(c,x) {if (z_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
++
++
++typedef uLong (ZEXPORT *check_func) OF((uLong check, const Bytef *buf,
++ uInt len));
++voidpf zcalloc OF((voidpf opaque, unsigned items, unsigned size));
++void zcfree OF((voidpf opaque, voidpf ptr));
++
++#define ZALLOC(strm, items, size) \
++ (*((strm)->zalloc))((strm)->opaque, (items), (size))
++#define ZFREE(strm, addr) (*((strm)->zfree))((strm)->opaque, (voidpf)(addr))
++#define TRY_FREE(s, p) {if (p) ZFREE(s, p);}
++
++#endif /* _Z_UTIL_H */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/libfreeswan/Makefile.objs Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,21 @@
++obj-y += satot.o
++obj-y += addrtot.o
++obj-y += ultot.o
++obj-y += addrtypeof.o
++obj-y += anyaddr.o
++obj-y += initaddr.o
++obj-y += ultoa.o
++obj-y += addrtoa.o
++obj-y += subnettoa.o
++obj-y += subnetof.o
++obj-y += goodmask.o
++obj-y += datatot.o
++obj-y += rangetoa.o
++obj-y += prng.o
++obj-y += pfkey_v2_parse.o
++obj-y += pfkey_v2_build.o
++obj-y += pfkey_v2_debug.o
++obj-y += pfkey_v2_ext_bits.o
++
++#version.c: ${LIBFREESWANDIR}/version.in.c ${OPENSWANSRCDIR}/Makefile.ver
++# sed '/"/s/xxx/$(IPSECVERSION)/' ${LIBFREESWANDIR}/version.in.c >$@
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/libfreeswan/addrtoa.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,67 @@
++/*
++ * addresses to ASCII
++ * Copyright (C) 1998, 1999 Henry Spencer.
++ *
++ * This library is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU Library General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/lgpl.txt>.
++ *
++ * This library 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 Library General Public
++ * License for more details.
++ *
++ * RCSID $Id$
++ */
++#include "openswan.h"
++
++#define NBYTES 4 /* bytes in an address */
++#define PERBYTE 4 /* three digits plus a dot or NUL */
++#define BUFLEN (NBYTES*PERBYTE)
++
++#if BUFLEN != ADDRTOA_BUF
++#error "ADDRTOA_BUF in openswan.h inconsistent with addrtoa() code"
++#endif
++
++/*
++ - addrtoa - convert binary address to ASCII dotted decimal
++ */
++size_t /* space needed for full conversion */
++addrtoa(addr, format, dst, dstlen)
++struct in_addr addr;
++int format; /* character */
++char *dst; /* need not be valid if dstlen is 0 */
++size_t dstlen;
++{
++ unsigned long a = ntohl(addr.s_addr);
++ int i;
++ size_t n;
++ unsigned long byte;
++ char buf[BUFLEN];
++ char *p;
++
++ switch (format) {
++ case 0:
++ break;
++ default:
++ return 0;
++ break;
++ }
++
++ p = buf;
++ for (i = NBYTES-1; i >= 0; i--) {
++ byte = (a >> (i*8)) & 0xff;
++ p += ultoa(byte, 10, p, PERBYTE);
++ if (i != 0)
++ *(p-1) = '.';
++ }
++ n = p - buf;
++
++ if (dstlen > 0) {
++ if (n > dstlen)
++ buf[dstlen - 1] = '\0';
++ strcpy(dst, buf);
++ }
++ return n;
++}
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/libfreeswan/addrtot.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,334 @@
++/*
++ * addresses to text
++ * Copyright (C) 2000 Henry Spencer.
++ *
++ * This library is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU Library General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/lgpl.txt>.
++ *
++ * This library 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 Library General Public
++ * License for more details.
++ *
++ * RCSID $Id$
++ */
++#include "openswan.h"
++
++#define IP4BYTES 4 /* bytes in an IPv4 address */
++#define PERBYTE 4 /* three digits plus a dot or NUL */
++#define IP6BYTES 16 /* bytes in an IPv6 address */
++
++/* forwards */
++static size_t normal4(const unsigned char *s, size_t len, char *b, char **dp);
++static size_t normal6(const unsigned char *s, size_t len, char *b, char **dp, int squish);
++static size_t reverse4(const unsigned char *s, size_t len, char *b, char **dp);
++static size_t reverse6(const unsigned char *s, size_t len, char *b, char **dp);
++
++/*
++ - addrtot - convert binary address to text (dotted decimal or IPv6 string)
++ */
++size_t /* space needed for full conversion */
++addrtot(src, format, dst, dstlen)
++const ip_address *src;
++int format; /* character */
++char *dst; /* need not be valid if dstlen is 0 */
++size_t dstlen;
++{
++ const unsigned char *b;
++ size_t n;
++ char buf[1+ADDRTOT_BUF+1]; /* :address: */
++ char *p;
++ int t = addrtypeof(src);
++# define TF(t, f) (((t)<<8) | (f))
++
++ n = addrbytesptr(src, &b);
++ if (n == 0) {
++ bad:
++ dst[0]='\0';
++ strncat(dst, "<invalid>", dstlen);
++ return sizeof("<invalid>");
++ }
++
++ switch (TF(t, format)) {
++ case TF(AF_INET, 0):
++ n = normal4(b, n, buf, &p);
++ break;
++ case TF(AF_INET6, 0):
++ n = normal6(b, n, buf, &p, 1);
++ break;
++ case TF(AF_INET, 'Q'):
++ n = normal4(b, n, buf, &p);
++ break;
++ case TF(AF_INET6, 'Q'):
++ n = normal6(b, n, buf, &p, 0);
++ break;
++ case TF(AF_INET, 'r'):
++ n = reverse4(b, n, buf, &p);
++ break;
++ case TF(AF_INET6, 'r'):
++ n = reverse6(b, n, buf, &p);
++ break;
++ default: /* including (AF_INET, 'R') */
++ goto bad;
++ break;
++ }
++
++ if (dstlen > 0) {
++ if (dstlen < n)
++ p[dstlen - 1] = '\0';
++ strcpy(dst, p);
++ }
++ return n;
++}
++
++/*
++ - normal4 - normal IPv4 address-text conversion
++ */
++static size_t /* size of text, including NUL */
++normal4(srcp, srclen, buf, dstp)
++const unsigned char *srcp;
++size_t srclen;
++char *buf; /* guaranteed large enough */
++char **dstp; /* where to put result pointer */
++{
++ int i;
++ char *p;
++
++ if (srclen != IP4BYTES) /* "can't happen" */
++ return 0;
++ p = buf;
++ for (i = 0; i < IP4BYTES; i++) {
++ p += ultot(srcp[i], 10, p, PERBYTE);
++ if (i != IP4BYTES - 1)
++ *(p-1) = '.'; /* overwrites the NUL */
++ }
++ *dstp = buf;
++ return p - buf;
++}
++
++/*
++ - normal6 - normal IPv6 address-text conversion
++ */
++static size_t /* size of text, including NUL */
++normal6(srcp, srclen, buf, dstp, squish)
++const unsigned char *srcp;
++size_t srclen;
++char *buf; /* guaranteed large enough, plus 2 */
++char **dstp; /* where to put result pointer */
++int squish; /* whether to squish out 0:0 */
++{
++ int i;
++ unsigned long piece;
++ char *p;
++ char *q;
++
++ if (srclen != IP6BYTES) /* "can't happen" */
++ return 0;
++ p = buf;
++ *p++ = ':';
++ for (i = 0; i < IP6BYTES/2; i++) {
++ piece = (srcp[2*i] << 8) + srcp[2*i + 1];
++ p += ultot(piece, 16, p, 5); /* 5 = abcd + NUL */
++ *(p-1) = ':'; /* overwrites the NUL */
++ }
++ *p = '\0';
++ q = strstr(buf, ":0:0:");
++ if (squish && q != NULL) { /* zero squishing is possible */
++ p = q + 1;
++ while (*p == '0' && *(p+1) == ':')
++ p += 2;
++ q++;
++ *q++ = ':'; /* overwrite first 0 */
++ while (*p != '\0')
++ *q++ = *p++;
++ *q = '\0';
++ if (!(*(q-1) == ':' && *(q-2) == ':'))
++ *--q = '\0'; /* strip final : unless :: */
++ p = buf;
++ if (!(*p == ':' && *(p+1) == ':'))
++ p++; /* skip initial : unless :: */
++ } else {
++ q = p;
++ *--q = '\0'; /* strip final : */
++ p = buf + 1; /* skip initial : */
++ }
++ *dstp = p;
++ return q - p + 1;
++}
++
++/*
++ - reverse4 - IPv4 reverse-lookup conversion
++ */
++static size_t /* size of text, including NUL */
++reverse4(srcp, srclen, buf, dstp)
++const unsigned char *srcp;
++size_t srclen;
++char *buf; /* guaranteed large enough */
++char **dstp; /* where to put result pointer */
++{
++ int i;
++ char *p;
++
++ if (srclen != IP4BYTES) /* "can't happen" */
++ return 0;
++ p = buf;
++ for (i = IP4BYTES-1; i >= 0; i--) {
++ p += ultot(srcp[i], 10, p, PERBYTE);
++ *(p-1) = '.'; /* overwrites the NUL */
++ }
++ strcpy(p, "IN-ADDR.ARPA.");
++ *dstp = buf;
++ return strlen(buf) + 1;
++}
++
++/*
++ - reverse6 - IPv6 reverse-lookup conversion (RFC 1886)
++ * A trifle inefficient, really shouldn't use ultot...
++ */
++static size_t /* size of text, including NUL */
++reverse6(srcp, srclen, buf, dstp)
++const unsigned char *srcp;
++size_t srclen;
++char *buf; /* guaranteed large enough */
++char **dstp; /* where to put result pointer */
++{
++ int i;
++ unsigned long piece;
++ char *p;
++
++ if (srclen != IP6BYTES) /* "can't happen" */
++ return 0;
++ p = buf;
++ for (i = IP6BYTES-1; i >= 0; i--) {
++ piece = srcp[i];
++ p += ultot(piece&0xf, 16, p, 2);
++ *(p-1) = '.';
++ p += ultot(piece>>4, 16, p, 2);
++ *(p-1) = '.';
++ }
++ strcpy(p, "IP6.ARPA.");
++ *dstp = buf;
++ return strlen(buf) + 1;
++}
++
++/*
++ - reverse6 - modern IPv6 reverse-lookup conversion (RFC 2874)
++ * this version removed as it was obsoleted in the end.
++ */
++
++#ifdef ADDRTOT_MAIN
++
++#include <stdio.h>
++#include <sys/socket.h>
++#include <netinet/in.h>
++#include <arpa/inet.h>
++
++void regress(void);
++
++int
++main(int argc, char *argv[])
++{
++ if (argc < 2) {
++ fprintf(stderr, "Usage: %s {addr|net/mask|begin...end|-r}\n",
++ argv[0]);
++ exit(2);
++ }
++
++ if (strcmp(argv[1], "-r") == 0) {
++ regress();
++ fprintf(stderr, "regress() returned?!?\n");
++ exit(1);
++ }
++ exit(0);
++}
++
++struct rtab {
++ char *input;
++ char format;
++ char *output; /* NULL means error expected */
++} rtab[] = {
++ {"1.2.3.0", 0, "1.2.3.0"},
++ {"1:2::3:4", 0, "1:2::3:4"},
++ {"1:2::3:4", 'Q', "1:2:0:0:0:0:3:4"},
++ {"1:2:0:0:3:4:0:0", 0, "1:2::3:4:0:0"},
++ {"1.2.3.4", 'r' , "4.3.2.1.IN-ADDR.ARPA."},
++ /* 0 1 2 3 4 5 6 7 8 9 a b c d e f 0 1 2 3 4 5 6 7 8 9 a b c d e f */
++ {"1:2::3:4", 'r', "4.0.0.0.3.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.2.0.0.0.1.0.0.0.IP6.ARPA."},
++ {NULL, 0, NULL}
++};
++
++void
++regress()
++{
++ struct rtab *r;
++ int status = 0;
++ ip_address a;
++ char in[100];
++ char buf[100];
++ const char *oops;
++ size_t n;
++
++ for (r = rtab; r->input != NULL; r++) {
++ strcpy(in, r->input);
++
++ /* convert it *to* internal format */
++ oops = ttoaddr(in, strlen(in), 0, &a);
++
++ /* now convert it back */
++
++ n = addrtot(&a, r->format, buf, sizeof(buf));
++
++ if (n == 0 && r->output == NULL)
++ {} /* okay, error expected */
++
++ else if (n == 0) {
++ printf("`%s' atoasr failed\n", r->input);
++ status = 1;
++
++ } else if (r->output == NULL) {
++ printf("`%s' atoasr succeeded unexpectedly '%c'\n",
++ r->input, r->format);
++ status = 1;
++ } else {
++ if (strcasecmp(r->output, buf) != 0) {
++ printf("`%s' '%c' gave `%s', expected `%s'\n",
++ r->input, r->format, buf, r->output);
++ status = 1;
++ }
++ }
++ }
++ exit(status);
++}
++
++#endif /* ADDRTOT_MAIN */
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.15 2004/04/11 17:39:25 mcr
++ * removed internal.h requirements.
++ *
++ * Revision 1.14 2004/03/08 01:59:08 ken
++ * freeswan.h -> openswan.h
++ *
++ * Revision 1.13 2004/01/05 23:21:05 mcr
++ * if the address type is invalid, then return length of <invalid>
++ * string!
++ *
++ * Revision 1.12 2003/12/30 06:42:48 mcr
++ * added $Log$
++ * added Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * added Turn off EOLN_NATIVE flag
++ * added
++ * added (Logical change 1.5010)
++ * added
++ * added Revision 1.15 2004/04/11 17:39:25 mcr
++ * added removed internal.h requirements.
++ * added
++ * added Revision 1.14 2004/03/08 01:59:08 ken
++ * added freeswan.h -> openswan.h
++ * added
++ * added Revision 1.13 2004/01/05 23:21:05 mcr
++ * added if the address type is invalid, then return length of <invalid>
++ * added string!
++ * added
++ *
++ *
++ */
++
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/libfreeswan/addrtypeof.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,93 @@
++/*
++ * extract parts of an ip_address
++ * Copyright (C) 2000 Henry Spencer.
++ *
++ * This library is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU Library General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/lgpl.txt>.
++ *
++ * This library 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 Library General Public
++ * License for more details.
++ *
++ * RCSID $Id$
++ */
++#include "openswan.h"
++
++/*
++ - addrtypeof - get the type of an ip_address
++ */
++int
++addrtypeof(src)
++const ip_address *src;
++{
++ return src->u.v4.sin_family;
++}
++
++/*
++ - addrbytesptr - get pointer to the address bytes of an ip_address
++ */
++size_t /* 0 for error */
++addrbytesptr(src, dstp)
++const ip_address *src;
++const unsigned char **dstp; /* NULL means just a size query */
++{
++ const unsigned char *p;
++ size_t n;
++
++ switch (src->u.v4.sin_family) {
++ case AF_INET:
++ p = (const unsigned char *)&src->u.v4.sin_addr.s_addr;
++ n = 4;
++ break;
++ case AF_INET6:
++ p = (const unsigned char *)&src->u.v6.sin6_addr;
++ n = 16;
++ break;
++ default:
++ return 0;
++ break;
++ }
++
++ if (dstp != NULL)
++ *dstp = p;
++ return n;
++}
++
++/*
++ - addrlenof - get length of the address bytes of an ip_address
++ */
++size_t /* 0 for error */
++addrlenof(src)
++const ip_address *src;
++{
++ return addrbytesptr(src, NULL);
++}
++
++/*
++ - addrbytesof - get the address bytes of an ip_address
++ */
++size_t /* 0 for error */
++addrbytesof(src, dst, dstlen)
++const ip_address *src;
++unsigned char *dst;
++size_t dstlen;
++{
++ const unsigned char *p;
++ size_t n;
++ size_t ncopy;
++
++ n = addrbytesptr(src, &p);
++ if (n == 0)
++ return 0;
++
++ if (dstlen > 0) {
++ ncopy = n;
++ if (ncopy > dstlen)
++ ncopy = dstlen;
++ memcpy(dst, p, ncopy);
++ }
++ return n;
++}
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/libfreeswan/anyaddr.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,145 @@
++/*
++ * special addresses
++ * Copyright (C) 2000 Henry Spencer.
++ *
++ * This library is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU Library General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/lgpl.txt>.
++ *
++ * This library 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 Library General Public
++ * License for more details.
++ *
++ * RCSID $Id$
++ */
++#include "openswan.h"
++
++/* these are mostly fallbacks for the no-IPv6-support-in-library case */
++#ifndef IN6ADDR_ANY_INIT
++#define IN6ADDR_ANY_INIT {{{ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }}}
++#endif
++#ifndef IN6ADDR_LOOPBACK_INIT
++#define IN6ADDR_LOOPBACK_INIT {{{ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 }}}
++#endif
++
++static struct in6_addr v6any = IN6ADDR_ANY_INIT;
++static struct in6_addr v6loop = IN6ADDR_LOOPBACK_INIT;
++
++/*
++ - anyaddr - initialize to the any-address value
++ */
++err_t /* NULL for success, else string literal */
++anyaddr(af, dst)
++int af; /* address family */
++ip_address *dst;
++{
++ uint32_t v4any = htonl(INADDR_ANY);
++
++ switch (af) {
++ case AF_INET:
++ return initaddr((unsigned char *)&v4any, sizeof(v4any), af, dst);
++ break;
++ case AF_INET6:
++ return initaddr((unsigned char *)&v6any, sizeof(v6any), af, dst);
++ break;
++ default:
++ return "unknown address family in anyaddr/unspecaddr";
++ break;
++ }
++}
++
++/*
++ - unspecaddr - initialize to the unspecified-address value
++ */
++err_t /* NULL for success, else string literal */
++unspecaddr(af, dst)
++int af; /* address family */
++ip_address *dst;
++{
++ return anyaddr(af, dst);
++}
++
++/*
++ - loopbackaddr - initialize to the loopback-address value
++ */
++err_t /* NULL for success, else string literal */
++loopbackaddr(af, dst)
++int af; /* address family */
++ip_address *dst;
++{
++ uint32_t v4loop = htonl(INADDR_LOOPBACK);
++
++ switch (af) {
++ case AF_INET:
++ return initaddr((unsigned char *)&v4loop, sizeof(v4loop), af, dst);
++ break;
++ case AF_INET6:
++ return initaddr((unsigned char *)&v6loop, sizeof(v6loop), af, dst);
++ break;
++ default:
++ return "unknown address family in loopbackaddr";
++ break;
++ }
++}
++
++/*
++ - isanyaddr - test for the any-address value
++ */
++int
++isanyaddr(src)
++const ip_address *src;
++{
++ uint32_t v4any = htonl(INADDR_ANY);
++ int cmp;
++
++ switch (src->u.v4.sin_family) {
++ case AF_INET:
++ cmp = memcmp(&src->u.v4.sin_addr.s_addr, &v4any, sizeof(v4any));
++ break;
++ case AF_INET6:
++ cmp = memcmp(&src->u.v6.sin6_addr, &v6any, sizeof(v6any));
++ break;
++ default:
++ return 0;
++ break;
++ }
++
++ return (cmp == 0) ? 1 : 0;
++}
++
++/*
++ - isunspecaddr - test for the unspecified-address value
++ */
++int
++isunspecaddr(src)
++const ip_address *src;
++{
++ return isanyaddr(src);
++}
++
++/*
++ - isloopbackaddr - test for the loopback-address value
++ */
++int
++isloopbackaddr(src)
++const ip_address *src;
++{
++ uint32_t v4loop = htonl(INADDR_LOOPBACK);
++ int cmp;
++
++ switch (src->u.v4.sin_family) {
++ case AF_INET:
++ cmp = memcmp(&src->u.v4.sin_addr.s_addr, &v4loop, sizeof(v4loop));
++ break;
++ case AF_INET6:
++ cmp = memcmp(&src->u.v6.sin6_addr, &v6loop, sizeof(v6loop));
++ break;
++ default:
++ return 0;
++ break;
++ }
++
++ return (cmp == 0) ? 1 : 0;
++}
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/libfreeswan/datatot.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,232 @@
++/*
++ * convert from binary data (e.g. key) to text form
++ * Copyright (C) 2000 Henry Spencer.
++ *
++ * This library is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU Library General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/lgpl.txt>.
++ *
++ * This library 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 Library General Public
++ * License for more details.
++ *
++ * RCSID $Id$
++ */
++#include "openswan.h"
++
++static void convert(const char *src, size_t nreal, int format, char *out);
++
++/*
++ - datatot - convert data bytes to text
++ */
++size_t /* true length (with NUL) for success */
++datatot(src, srclen, format, dst, dstlen)
++const char *src;
++size_t srclen;
++int format; /* character indicating what format */
++char *dst; /* need not be valid if dstlen is 0 */
++size_t dstlen;
++{
++ size_t inblocksize; /* process this many bytes at a time */
++ size_t outblocksize; /* producing this many */
++ size_t breakevery; /* add a _ every this many (0 means don't) */
++ size_t sincebreak; /* output bytes since last _ */
++ char breakchar; /* character used to break between groups */
++ char inblock[10]; /* enough for any format */
++ char outblock[10]; /* enough for any format */
++ char fake[1]; /* fake output area for dstlen == 0 */
++ size_t needed; /* return value */
++ char *stop; /* where the terminating NUL will go */
++ size_t ntodo; /* remaining input */
++ size_t nreal;
++ char *out;
++ char *prefix;
++
++ breakevery = 0;
++ breakchar = '_';
++
++ switch (format) {
++ case 0:
++ case 'h':
++ format = 'x';
++ breakevery = 8;
++ /* FALLTHROUGH */
++ case 'x':
++ inblocksize = 1;
++ outblocksize = 2;
++ prefix = "0x";
++ break;
++ case ':':
++ format = 'x';
++ breakevery = 2;
++ breakchar = ':';
++ /* FALLTHROUGH */
++ case 16:
++ inblocksize = 1;
++ outblocksize = 2;
++ prefix = "";
++ format = 'x';
++ break;
++ case 's':
++ inblocksize = 3;
++ outblocksize = 4;
++ prefix = "0s";
++ break;
++ case 64: /* beware, equals ' ' */
++ inblocksize = 3;
++ outblocksize = 4;
++ prefix = "";
++ format = 's';
++ break;
++ default:
++ return 0;
++ break;
++ }
++ assert(inblocksize < sizeof(inblock));
++ assert(outblocksize < sizeof(outblock));
++ assert(breakevery % outblocksize == 0);
++
++ if (srclen == 0)
++ return 0;
++ ntodo = srclen;
++
++ if (dstlen == 0) { /* dispose of awkward special case */
++ dst = fake;
++ dstlen = 1;
++ }
++ stop = dst + dstlen - 1;
++
++ nreal = strlen(prefix);
++ needed = nreal; /* for starters */
++ if (dstlen <= nreal) { /* prefix won't fit */
++ strncpy(dst, prefix, dstlen - 1);
++ dst += dstlen - 1;
++ } else {
++ strcpy(dst, prefix);
++ dst += nreal;
++ }
++ assert(dst <= stop);
++ sincebreak = 0;
++
++ while (ntodo > 0) {
++ if (ntodo < inblocksize) { /* incomplete input */
++ memset(inblock, 0, sizeof(inblock));
++ memcpy(inblock, src, ntodo);
++ src = inblock;
++ nreal = ntodo;
++ ntodo = inblocksize;
++ } else
++ nreal = inblocksize;
++ out = (outblocksize > stop - dst) ? outblock : dst;
++
++ convert(src, nreal, format, out);
++ needed += outblocksize;
++ sincebreak += outblocksize;
++ if (dst < stop) {
++ if (out != dst) {
++ assert(outblocksize > stop - dst);
++ memcpy(dst, out, stop - dst);
++ dst = stop;
++ } else
++ dst += outblocksize;
++ }
++
++ src += inblocksize;
++ ntodo -= inblocksize;
++ if (breakevery != 0 && sincebreak >= breakevery && ntodo > 0) {
++ if (dst < stop)
++ *dst++ = breakchar;
++ needed++;
++ sincebreak = 0;
++ }
++ }
++
++ assert(dst <= stop);
++ *dst++ = '\0';
++ needed++;
++
++ return needed;
++}
++
++/*
++ - convert - convert one input block to one output block
++ */
++static void
++convert(src, nreal, format, out)
++const char *src;
++size_t nreal; /* how much of the input block is real */
++int format;
++char *out;
++{
++ static char hex[] = "0123456789abcdef";
++ static char base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
++ "abcdefghijklmnopqrstuvwxyz"
++ "0123456789+/";
++ unsigned char c;
++ unsigned char c1, c2, c3;
++
++ assert(nreal > 0);
++ switch (format) {
++ case 'x':
++ assert(nreal == 1);
++ c = (unsigned char)*src;
++ *out++ = hex[c >> 4];
++ *out++ = hex[c & 0xf];
++ break;
++ case 's':
++ c1 = (unsigned char)*src++;
++ c2 = (unsigned char)*src++;
++ c3 = (unsigned char)*src++;
++ *out++ = base64[c1 >> 2]; /* top 6 bits of c1 */
++ c = (c1 & 0x3) << 4; /* bottom 2 of c1... */
++ c |= c2 >> 4; /* ...top 4 of c2 */
++ *out++ = base64[c];
++ if (nreal == 1)
++ *out++ = '=';
++ else {
++ c = (c2 & 0xf) << 2; /* bottom 4 of c2... */
++ c |= c3 >> 6; /* ...top 2 of c3 */
++ *out++ = base64[c];
++ }
++ if (nreal <= 2)
++ *out++ = '=';
++ else
++ *out++ = base64[c3 & 0x3f]; /* bottom 6 of c3 */
++ break;
++ default:
++ assert(nreal == 0); /* unknown format */
++ break;
++ }
++}
++
++/*
++ - datatoa - convert data to ASCII
++ * backward-compatibility synonym for datatot
++ */
++size_t /* true length (with NUL) for success */
++datatoa(src, srclen, format, dst, dstlen)
++const char *src;
++size_t srclen;
++int format; /* character indicating what format */
++char *dst; /* need not be valid if dstlen is 0 */
++size_t dstlen;
++{
++ return datatot(src, srclen, format, dst, dstlen);
++}
++
++/*
++ - bytestoa - convert data bytes to ASCII
++ * backward-compatibility synonym for datatot
++ */
++size_t /* true length (with NUL) for success */
++bytestoa(src, srclen, format, dst, dstlen)
++const char *src;
++size_t srclen;
++int format; /* character indicating what format */
++char *dst; /* need not be valid if dstlen is 0 */
++size_t dstlen;
++{
++ return datatot(src, srclen, format, dst, dstlen);
++}
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/libfreeswan/goodmask.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,100 @@
++/*
++ * minor utilities for subnet-mask manipulation
++ * Copyright (C) 1998, 1999 Henry Spencer.
++ *
++ * This library is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU Library General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/lgpl.txt>.
++ *
++ * This library 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 Library General Public
++ * License for more details.
++ *
++ * RCSID $Id$
++ */
++#include "openswan.h"
++
++#ifndef ABITS
++#define ABITS 32 /* bits in an IPv4 address */
++#endif
++
++/*
++ - goodmask - is this a good (^1*0*$) subnet mask?
++ * You are not expected to understand this. See Henry S. Warren Jr,
++ * "Functions realizable with word-parallel logical and two's-complement
++ * addition instructions", CACM 20.6 (June 1977), p.439.
++ */
++int /* predicate */
++goodmask(mask)
++struct in_addr mask;
++{
++ unsigned long x = ntohl(mask.s_addr);
++ /* clear rightmost contiguous string of 1-bits */
++# define CRCS1B(x) (((x|(x-1))+1)&x)
++# define TOPBIT (1UL << 31)
++
++ /* either zero, or has one string of 1-bits which is left-justified */
++ if (x == 0 || (CRCS1B(x) == 0 && (x&TOPBIT)))
++ return 1;
++ return 0;
++}
++
++/*
++ - masktobits - how many bits in this mask?
++ * The algorithm is essentially a binary search, but highly optimized
++ * for this particular task.
++ */
++int /* -1 means !goodmask() */
++masktobits(mask)
++struct in_addr mask;
++{
++ unsigned long m = ntohl(mask.s_addr);
++ int masklen;
++
++ if (!goodmask(mask))
++ return -1;
++
++ if (m&0x00000001UL)
++ return 32;
++ masklen = 0;
++ if (m&(0x0000ffffUL<<1)) { /* <<1 for 1-origin numbering */
++ masklen |= 0x10;
++ m <<= 16;
++ }
++ if (m&(0x00ff0000UL<<1)) {
++ masklen |= 0x08;
++ m <<= 8;
++ }
++ if (m&(0x0f000000UL<<1)) {
++ masklen |= 0x04;
++ m <<= 4;
++ }
++ if (m&(0x30000000UL<<1)) {
++ masklen |= 0x02;
++ m <<= 2;
++ }
++ if (m&(0x40000000UL<<1))
++ masklen |= 0x01;
++
++ return masklen;
++}
++
++/*
++ - bitstomask - return a mask with this many high bits on
++ */
++struct in_addr
++bitstomask(n)
++int n;
++{
++ struct in_addr result;
++
++ if (n > 0 && n <= ABITS)
++ result.s_addr = htonl(~((1UL << (ABITS - n)) - 1));
++ else if (n == 0)
++ result.s_addr = 0;
++ else
++ result.s_addr = 0; /* best error report we can do */
++ return result;
++}
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/libfreeswan/initaddr.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,50 @@
++/*
++ * initialize address structure
++ * Copyright (C) 2000 Henry Spencer.
++ *
++ * This library is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU Library General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/lgpl.txt>.
++ *
++ * This library 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 Library General Public
++ * License for more details.
++ *
++ * RCSID $Id$
++ */
++#include "openswan.h"
++
++/*
++ - initaddr - initialize ip_address from bytes
++ */
++err_t /* NULL for success, else string literal */
++initaddr(src, srclen, af, dst)
++const unsigned char *src;
++size_t srclen;
++int af; /* address family */
++ip_address *dst;
++{
++ switch (af) {
++ case AF_INET:
++ if (srclen != 4)
++ return "IPv4 address must be exactly 4 bytes";
++ dst->u.v4.sin_family = af;
++ dst->u.v4.sin_port = 0; /* unused */
++ memcpy((char *)&dst->u.v4.sin_addr.s_addr, src, srclen);
++ break;
++ case AF_INET6:
++ if (srclen != 16)
++ return "IPv6 address must be exactly 16 bytes";
++ dst->u.v6.sin6_family = af;
++ dst->u.v6.sin6_flowinfo = 0; /* unused */
++ dst->u.v6.sin6_port = 0; /* unused */
++ memcpy((char *)&dst->u.v6.sin6_addr, src, srclen);
++ break;
++ default:
++ return "unknown address family in initaddr";
++ break;
++ }
++ return NULL;
++}
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/libfreeswan/pfkey_v2_build.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,1559 @@
++/*
++ * RFC2367 PF_KEYv2 Key management API message parser
++ * Copyright (C) 1999, 2000, 2001 Richard Guy Briggs.
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ * RCSID $Id$
++ */
++
++/*
++ * Template from klips/net/ipsec/ipsec/ipsec_parser.c.
++ */
++
++char pfkey_v2_build_c_version[] = "$Id$";
++
++/*
++ * Some ugly stuff to allow consistent debugging code for use in the
++ * kernel and in user space
++*/
++
++#ifdef __KERNEL__
++
++# include <linux/kernel.h> /* for printk */
++
++# include "openswan/ipsec_kversion.h" /* for malloc switch */
++# ifdef MALLOC_SLAB
++# include <linux/slab.h> /* kmalloc() */
++# else /* MALLOC_SLAB */
++# include <linux/malloc.h> /* kmalloc() */
++# endif /* MALLOC_SLAB */
++# include <linux/errno.h> /* error codes */
++# include <linux/types.h> /* size_t */
++# include <linux/interrupt.h> /* mark_bh */
++
++# include <linux/netdevice.h> /* struct device, and other headers */
++# include <linux/etherdevice.h> /* eth_type_trans */
++# include <linux/ip.h> /* struct iphdr */
++# if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
++# include <linux/ipv6.h> /* struct ipv6hdr */
++# endif /* if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) */
++
++# define MALLOC(size) kmalloc(size, GFP_ATOMIC)
++# define FREE(obj) kfree(obj)
++# include <openswan.h>
++#else /* __KERNEL__ */
++
++# include <sys/types.h>
++# include <linux/types.h>
++# include <linux/errno.h>
++# include <malloc.h>
++# include <string.h> /* memset */
++
++# include <openswan.h>
++
++#endif /* __KERNEL__ */
++
++#include <pfkeyv2.h>
++#include <pfkey.h>
++
++#ifdef __KERNEL__
++#include "openswan/radij.h" /* rd_nodes */
++#include "openswan/ipsec_encap.h" /* sockaddr_encap */
++#endif /* __KERNEL__ */
++
++
++#include "openswan/ipsec_sa.h" /* IPSEC_SAREF_NULL, IPSEC_SA_REF_TABLE_IDX_WIDTH */
++#include "openswan/pfkey_debug.h"
++
++
++#define SENDERR(_x) do { error = -(_x); goto errlab; } while (0)
++
++void
++pfkey_extensions_init(struct sadb_ext *extensions[SADB_EXT_MAX + 1])
++{
++ int i;
++
++ for (i = 0; i != SADB_EXT_MAX + 1; i++) {
++ extensions[i] = NULL;
++ }
++}
++
++void
++pfkey_extensions_free(struct sadb_ext *extensions[SADB_EXT_MAX + 1])
++{
++ int i;
++
++ if(!extensions) {
++ return;
++ }
++
++ if(extensions[0]) {
++ memset(extensions[0], 0, sizeof(struct sadb_msg));
++ FREE(extensions[0]);
++ extensions[0] = NULL;
++ }
++
++ for (i = 1; i != SADB_EXT_MAX + 1; i++) {
++ if(extensions[i]) {
++ memset(extensions[i], 0, extensions[i]->sadb_ext_len * IPSEC_PFKEYv2_ALIGN);
++ FREE(extensions[i]);
++ extensions[i] = NULL;
++ }
++ }
++}
++
++void
++pfkey_msg_free(struct sadb_msg **pfkey_msg)
++{
++ if(*pfkey_msg) {
++ memset(*pfkey_msg, 0, (*pfkey_msg)->sadb_msg_len * IPSEC_PFKEYv2_ALIGN);
++ FREE(*pfkey_msg);
++ *pfkey_msg = NULL;
++ }
++}
++
++/* Default extension builders taken from the KLIPS code */
++
++int
++pfkey_msg_hdr_build(struct sadb_ext** pfkey_ext,
++ uint8_t msg_type,
++ uint8_t satype,
++ uint8_t msg_errno,
++ uint32_t seq,
++ uint32_t pid)
++{
++ int error = 0;
++ struct sadb_msg *pfkey_msg = (struct sadb_msg *)*pfkey_ext;
++
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_msg_hdr_build:\n");
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_msg_hdr_build: "
++ "on_entry &pfkey_ext=0p%p pfkey_ext=0p%p *pfkey_ext=0p%p.\n",
++ &pfkey_ext,
++ pfkey_ext,
++ *pfkey_ext);
++ /* sanity checks... */
++ if(pfkey_msg) {
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_msg_hdr_build: "
++ "why is pfkey_msg already pointing to something?\n");
++ SENDERR(EINVAL);
++ }
++
++ if(!msg_type) {
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_msg_hdr_build: "
++ "msg type not set, must be non-zero..\n");
++ SENDERR(EINVAL);
++ }
++
++ if(msg_type > SADB_MAX) {
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_msg_hdr_build: "
++ "msg type too large:%d.\n",
++ msg_type);
++ SENDERR(EINVAL);
++ }
++
++ if(satype > SADB_SATYPE_MAX) {
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_msg_hdr_build: "
++ "satype %d > max %d\n",
++ satype, SADB_SATYPE_MAX);
++ SENDERR(EINVAL);
++ }
++
++ if(!(*pfkey_ext = (struct sadb_ext*)
++ pfkey_msg = (struct sadb_msg*)
++ MALLOC(sizeof(struct sadb_msg)))) {
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_msg_hdr_build: "
++ "memory allocation failed\n");
++ SENDERR(ENOMEM);
++ }
++ memset(pfkey_msg, 0, sizeof(struct sadb_msg));
++
++ pfkey_msg->sadb_msg_len = sizeof(struct sadb_msg) / IPSEC_PFKEYv2_ALIGN;
++
++ pfkey_msg->sadb_msg_type = msg_type;
++ pfkey_msg->sadb_msg_satype = satype;
++
++ pfkey_msg->sadb_msg_version = PF_KEY_V2;
++ pfkey_msg->sadb_msg_errno = msg_errno;
++ pfkey_msg->sadb_msg_reserved = 0;
++ pfkey_msg->sadb_msg_seq = seq;
++ pfkey_msg->sadb_msg_pid = pid;
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_msg_hdr_build: "
++ "on_exit &pfkey_ext=0p%p pfkey_ext=0p%p *pfkey_ext=0p%p.\n",
++ &pfkey_ext,
++ pfkey_ext,
++ *pfkey_ext);
++errlab:
++ return error;
++}
++
++int
++pfkey_sa_ref_build(struct sadb_ext ** pfkey_ext,
++ uint16_t exttype,
++ uint32_t spi,
++ uint8_t replay_window,
++ uint8_t sa_state,
++ uint8_t auth,
++ uint8_t encrypt,
++ uint32_t flags,
++ uint32_t/*IPsecSAref_t*/ ref)
++{
++ int error = 0;
++ struct sadb_sa *pfkey_sa = (struct sadb_sa *)*pfkey_ext;
++
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_sa_build: "
++ "spi=%08x replay=%d sa_state=%d auth=%d encrypt=%d flags=%d\n",
++ ntohl(spi), /* in network order */
++ replay_window,
++ sa_state,
++ auth,
++ encrypt,
++ flags);
++ /* sanity checks... */
++ if(pfkey_sa) {
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_sa_build: "
++ "why is pfkey_sa already pointing to something?\n");
++ SENDERR(EINVAL);
++ }
++
++ if(exttype != SADB_EXT_SA &&
++ exttype != SADB_X_EXT_SA2) {
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_sa_build: "
++ "invalid exttype=%d.\n",
++ exttype);
++ SENDERR(EINVAL);
++ }
++
++ if(replay_window > 64) {
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_sa_build: "
++ "replay window size: %d -- must be 0 <= size <= 64\n",
++ replay_window);
++ SENDERR(EINVAL);
++ }
++
++ if(auth > SADB_AALG_MAX) {
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_sa_build: "
++ "auth=%d > SADB_AALG_MAX=%d.\n",
++ auth,
++ SADB_AALG_MAX);
++ SENDERR(EINVAL);
++ }
++
++ if(encrypt > SADB_EALG_MAX) {
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_sa_build: "
++ "encrypt=%d > SADB_EALG_MAX=%d.\n",
++ encrypt,
++ SADB_EALG_MAX);
++ SENDERR(EINVAL);
++ }
++
++ if(sa_state > SADB_SASTATE_MAX) {
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_sa_build: "
++ "sa_state=%d exceeds MAX=%d.\n",
++ sa_state,
++ SADB_SASTATE_MAX);
++ SENDERR(EINVAL);
++ }
++
++ if(sa_state == SADB_SASTATE_DEAD) {
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_sa_build: "
++ "sa_state=%d is DEAD=%d is not allowed.\n",
++ sa_state,
++ SADB_SASTATE_DEAD);
++ SENDERR(EINVAL);
++ }
++
++ if((IPSEC_SAREF_NULL != ref) && (ref >= (1 << IPSEC_SA_REF_TABLE_IDX_WIDTH))) {
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_sa_build: "
++ "SAref=%d must be (SAref == IPSEC_SAREF_NULL(%d) || SAref < IPSEC_SA_REF_TABLE_NUM_ENTRIES(%d)).\n",
++ ref,
++ IPSEC_SAREF_NULL,
++ IPSEC_SA_REF_TABLE_NUM_ENTRIES);
++ SENDERR(EINVAL);
++ }
++
++ if(!(*pfkey_ext = (struct sadb_ext*)
++ pfkey_sa = (struct sadb_sa*)
++ MALLOC(sizeof(struct sadb_sa)))) {
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_sa_build: "
++ "memory allocation failed\n");
++ SENDERR(ENOMEM);
++ }
++ memset(pfkey_sa, 0, sizeof(struct sadb_sa));
++
++ pfkey_sa->sadb_sa_len = sizeof(*pfkey_sa) / IPSEC_PFKEYv2_ALIGN;
++ pfkey_sa->sadb_sa_exttype = exttype;
++ pfkey_sa->sadb_sa_spi = spi;
++ pfkey_sa->sadb_sa_replay = replay_window;
++ pfkey_sa->sadb_sa_state = sa_state;
++ pfkey_sa->sadb_sa_auth = auth;
++ pfkey_sa->sadb_sa_encrypt = encrypt;
++ pfkey_sa->sadb_sa_flags = flags;
++ pfkey_sa->sadb_x_sa_ref = ref;
++
++errlab:
++ return error;
++}
++
++int
++pfkey_sa_build(struct sadb_ext ** pfkey_ext,
++ uint16_t exttype,
++ uint32_t spi,
++ uint8_t replay_window,
++ uint8_t sa_state,
++ uint8_t auth,
++ uint8_t encrypt,
++ uint32_t flags)
++{
++ return pfkey_sa_ref_build(pfkey_ext,
++ exttype,
++ spi,
++ replay_window,
++ sa_state,
++ auth,
++ encrypt,
++ flags,
++ IPSEC_SAREF_NULL);
++}
++
++int
++pfkey_lifetime_build(struct sadb_ext ** pfkey_ext,
++ uint16_t exttype,
++ uint32_t allocations,
++ uint64_t bytes,
++ uint64_t addtime,
++ uint64_t usetime,
++ uint32_t packets)
++{
++ int error = 0;
++ struct sadb_lifetime *pfkey_lifetime = (struct sadb_lifetime *)*pfkey_ext;
++
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_lifetime_build:\n");
++ /* sanity checks... */
++ if(pfkey_lifetime) {
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_lifetime_build: "
++ "why is pfkey_lifetime already pointing to something?\n");
++ SENDERR(EINVAL);
++ }
++
++ if(exttype != SADB_EXT_LIFETIME_CURRENT &&
++ exttype != SADB_EXT_LIFETIME_HARD &&
++ exttype != SADB_EXT_LIFETIME_SOFT) {
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_lifetime_build: "
++ "invalid exttype=%d.\n",
++ exttype);
++ SENDERR(EINVAL);
++ }
++
++ if(!(*pfkey_ext = (struct sadb_ext*)
++ pfkey_lifetime = (struct sadb_lifetime*)
++ MALLOC(sizeof(struct sadb_lifetime)))) {
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_lifetime_build: "
++ "memory allocation failed\n");
++ SENDERR(ENOMEM);
++ }
++ memset(pfkey_lifetime, 0, sizeof(struct sadb_lifetime));
++
++ pfkey_lifetime->sadb_lifetime_len = sizeof(struct sadb_lifetime) / IPSEC_PFKEYv2_ALIGN;
++ pfkey_lifetime->sadb_lifetime_exttype = exttype;
++ pfkey_lifetime->sadb_lifetime_allocations = allocations;
++ pfkey_lifetime->sadb_lifetime_bytes = bytes;
++ pfkey_lifetime->sadb_lifetime_addtime = addtime;
++ pfkey_lifetime->sadb_lifetime_usetime = usetime;
++ pfkey_lifetime->sadb_x_lifetime_packets = packets;
++
++errlab:
++ return error;
++}
++
++int
++pfkey_address_build(struct sadb_ext** pfkey_ext,
++ uint16_t exttype,
++ uint8_t proto,
++ uint8_t prefixlen,
++ struct sockaddr* address)
++{
++ int error = 0;
++ int saddr_len = 0;
++ char ipaddr_txt[ADDRTOT_BUF + 6/*extra for port number*/];
++ struct sadb_address *pfkey_address = (struct sadb_address *)*pfkey_ext;
++
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_address_build: "
++ "exttype=%d proto=%d prefixlen=%d\n",
++ exttype,
++ proto,
++ prefixlen);
++ /* sanity checks... */
++ if(pfkey_address) {
++ ERROR("pfkey_address_build: "
++ "why is pfkey_address already pointing to something?\n");
++ SENDERR(EINVAL);
++ }
++
++ if (!address) {
++ ERROR("pfkey_address_build: " "address is NULL\n");
++ SENDERR(EINVAL);
++ }
++
++ switch(exttype) {
++ case SADB_EXT_ADDRESS_SRC:
++ case SADB_EXT_ADDRESS_DST:
++ case SADB_EXT_ADDRESS_PROXY:
++ case SADB_X_EXT_ADDRESS_DST2:
++ case SADB_X_EXT_ADDRESS_SRC_FLOW:
++ case SADB_X_EXT_ADDRESS_DST_FLOW:
++ case SADB_X_EXT_ADDRESS_SRC_MASK:
++ case SADB_X_EXT_ADDRESS_DST_MASK:
++#ifdef NAT_TRAVERSAL
++ case SADB_X_EXT_NAT_T_OA:
++#endif
++ break;
++ default:
++ ERROR("pfkey_address_build: "
++ "unrecognised ext_type=%d.\n",
++ exttype);
++ SENDERR(EINVAL);
++ }
++
++ switch(address->sa_family) {
++ case AF_INET:
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_address_build: "
++ "found address family AF_INET.\n");
++ saddr_len = sizeof(struct sockaddr_in);
++ sprintf(ipaddr_txt, "%d.%d.%d.%d:%d"
++ , (((struct sockaddr_in*)address)->sin_addr.s_addr >> 0) & 0xFF
++ , (((struct sockaddr_in*)address)->sin_addr.s_addr >> 8) & 0xFF
++ , (((struct sockaddr_in*)address)->sin_addr.s_addr >> 16) & 0xFF
++ , (((struct sockaddr_in*)address)->sin_addr.s_addr >> 24) & 0xFF
++ , ntohs(((struct sockaddr_in*)address)->sin_port));
++ break;
++ case AF_INET6:
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_address_build: "
++ "found address family AF_INET6.\n");
++ saddr_len = sizeof(struct sockaddr_in6);
++ sprintf(ipaddr_txt, "%x:%x:%x:%x:%x:%x:%x:%x-%x"
++ , ntohs(((struct sockaddr_in6*)address)->sin6_addr.s6_addr16[0])
++ , ntohs(((struct sockaddr_in6*)address)->sin6_addr.s6_addr16[1])
++ , ntohs(((struct sockaddr_in6*)address)->sin6_addr.s6_addr16[2])
++ , ntohs(((struct sockaddr_in6*)address)->sin6_addr.s6_addr16[3])
++ , ntohs(((struct sockaddr_in6*)address)->sin6_addr.s6_addr16[4])
++ , ntohs(((struct sockaddr_in6*)address)->sin6_addr.s6_addr16[5])
++ , ntohs(((struct sockaddr_in6*)address)->sin6_addr.s6_addr16[6])
++ , ntohs(((struct sockaddr_in6*)address)->sin6_addr.s6_addr16[7])
++ , ntohs(((struct sockaddr_in6*)address)->sin6_port));
++ break;
++ default:
++ ERROR("pfkey_address_build: "
++ "address->sa_family=%d not supported.\n",
++ address->sa_family);
++ SENDERR(EPFNOSUPPORT);
++ }
++
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_address_build: "
++ "found address=%s.\n",
++ ipaddr_txt);
++ if(prefixlen != 0) {
++ ERROR("pfkey_address_build: "
++ "address prefixes not supported yet.\n");
++ SENDERR(EAFNOSUPPORT); /* not supported yet */
++ }
++
++ /* allocate some memory for the extension */
++ pfkey_address = (struct sadb_address*)
++ MALLOC(ALIGN_N(sizeof(struct sadb_address) + saddr_len, IPSEC_PFKEYv2_ALIGN));
++ *pfkey_ext = (struct sadb_ext*)pfkey_address;
++
++ if(pfkey_address == NULL ) {
++ ERROR("pfkey_lifetime_build: "
++ "memory allocation failed\n");
++ SENDERR(ENOMEM);
++ }
++ memset(pfkey_address,
++ 0,
++ ALIGN_N(sizeof(struct sadb_address) + saddr_len,
++ IPSEC_PFKEYv2_ALIGN));
++
++ pfkey_address->sadb_address_len = DIVUP(sizeof(struct sadb_address) + saddr_len,
++ IPSEC_PFKEYv2_ALIGN);
++
++ pfkey_address->sadb_address_exttype = exttype;
++ pfkey_address->sadb_address_proto = proto;
++ pfkey_address->sadb_address_prefixlen = prefixlen;
++ pfkey_address->sadb_address_reserved = 0;
++
++ memcpy((char*)pfkey_address + sizeof(struct sadb_address),
++ address,
++ saddr_len);
++
++#if 0
++ for(i = 0; i < sizeof(struct sockaddr_in) - offsetof(struct sockaddr_in, sin_zero); i++) {
++ pfkey_address_s_ska.sin_zero[i] = 0;
++ }
++#endif
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_address_build: "
++ "successful created len: %d.\n", pfkey_address->sadb_address_len);
++
++ errlab:
++ return error;
++}
++
++int
++pfkey_key_build(struct sadb_ext** pfkey_ext,
++ uint16_t exttype,
++ uint16_t key_bits,
++ char* key)
++{
++ int error = 0;
++ struct sadb_key *pfkey_key = (struct sadb_key *)*pfkey_ext;
++
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_key_build:\n");
++ /* sanity checks... */
++ if(pfkey_key) {
++ ERROR("pfkey_key_build: "
++ "why is pfkey_key already pointing to something?\n");
++ SENDERR(EINVAL);
++ }
++
++ if(!key_bits) {
++ ERROR("pfkey_key_build: "
++ "key_bits is zero, it must be non-zero.\n");
++ SENDERR(EINVAL);
++ }
++
++ if( !((exttype == SADB_EXT_KEY_AUTH) || (exttype == SADB_EXT_KEY_ENCRYPT))) {
++ ERROR("pfkey_key_build: "
++ "unsupported extension type=%d.\n",
++ exttype);
++ SENDERR(EINVAL);
++ }
++
++ if(!(*pfkey_ext = (struct sadb_ext*)
++ pfkey_key = (struct sadb_key*)
++ MALLOC(sizeof(struct sadb_key) +
++ DIVUP(key_bits, 64) * IPSEC_PFKEYv2_ALIGN))) {
++ ERROR("pfkey_key_build: "
++ "memory allocation failed\n");
++ SENDERR(ENOMEM);
++ }
++ memset(pfkey_key,
++ 0,
++ sizeof(struct sadb_key) +
++ DIVUP(key_bits, 64) * IPSEC_PFKEYv2_ALIGN);
++
++ pfkey_key->sadb_key_len = DIVUP(sizeof(struct sadb_key) * IPSEC_PFKEYv2_ALIGN + key_bits,
++ 64);
++ pfkey_key->sadb_key_exttype = exttype;
++ pfkey_key->sadb_key_bits = key_bits;
++ pfkey_key->sadb_key_reserved = 0;
++ memcpy((char*)pfkey_key + sizeof(struct sadb_key),
++ key,
++ DIVUP(key_bits, 8));
++
++errlab:
++ return error;
++}
++
++int
++pfkey_ident_build(struct sadb_ext** pfkey_ext,
++ uint16_t exttype,
++ uint16_t ident_type,
++ uint64_t ident_id,
++ uint8_t ident_len,
++ char* ident_string)
++{
++ int error = 0;
++ struct sadb_ident *pfkey_ident = (struct sadb_ident *)*pfkey_ext;
++ int data_len = ident_len * IPSEC_PFKEYv2_ALIGN - sizeof(struct sadb_ident);
++
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_ident_build:\n");
++ /* sanity checks... */
++ if(pfkey_ident) {
++ ERROR("pfkey_ident_build: "
++ "why is pfkey_ident already pointing to something?\n");
++ SENDERR(EINVAL);
++ }
++
++ if( ! ((exttype == SADB_EXT_IDENTITY_SRC) ||
++ (exttype == SADB_EXT_IDENTITY_DST))) {
++ ERROR("pfkey_ident_build: "
++ "unsupported extension type=%d.\n",
++ exttype);
++ SENDERR(EINVAL);
++ }
++
++ if((ident_type == SADB_IDENTTYPE_RESERVED)) {
++ ERROR("pfkey_ident_build: "
++ "ident_type must be non-zero.\n");
++ SENDERR(EINVAL);
++ }
++
++ if(ident_type > SADB_IDENTTYPE_MAX) {
++ ERROR("pfkey_ident_build: "
++ "identtype=%d out of range.\n",
++ ident_type);
++ SENDERR(EINVAL);
++ }
++
++ if(((ident_type == SADB_IDENTTYPE_PREFIX) ||
++ (ident_type == SADB_IDENTTYPE_FQDN)) &&
++ !ident_string) {
++ ERROR("pfkey_ident_build: "
++ "string required to allocate size of extension.\n");
++ SENDERR(EINVAL);
++ }
++
++#if 0
++ if((ident_type == SADB_IDENTTYPE_USERFQDN) ) {
++ }
++#endif
++
++ if(!(*pfkey_ext = (struct sadb_ext*)
++ pfkey_ident = (struct sadb_ident*)
++ MALLOC(ident_len * IPSEC_PFKEYv2_ALIGN))) {
++ ERROR("pfkey_ident_build: "
++ "memory allocation failed\n");
++ SENDERR(ENOMEM);
++ }
++ memset(pfkey_ident, 0, ident_len * IPSEC_PFKEYv2_ALIGN);
++
++ pfkey_ident->sadb_ident_len = ident_len;
++ pfkey_ident->sadb_ident_exttype = exttype;
++ pfkey_ident->sadb_ident_type = ident_type;
++ pfkey_ident->sadb_ident_reserved = 0;
++ pfkey_ident->sadb_ident_id = ident_id;
++ memcpy((char*)pfkey_ident + sizeof(struct sadb_ident),
++ ident_string,
++ data_len);
++
++errlab:
++ return error;
++}
++
++int
++pfkey_sens_build(struct sadb_ext** pfkey_ext,
++ uint32_t dpd,
++ uint8_t sens_level,
++ uint8_t sens_len,
++ uint64_t* sens_bitmap,
++ uint8_t integ_level,
++ uint8_t integ_len,
++ uint64_t* integ_bitmap)
++{
++ int error = 0;
++ struct sadb_sens *pfkey_sens = (struct sadb_sens *)*pfkey_ext;
++ int i;
++ uint64_t* bitmap;
++
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_sens_build:\n");
++ /* sanity checks... */
++ if(pfkey_sens) {
++ ERROR("pfkey_sens_build: "
++ "why is pfkey_sens already pointing to something?\n");
++ SENDERR(EINVAL);
++ }
++
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_sens_build: "
++ "Sorry, I can't build exttype=%d yet.\n",
++ (*pfkey_ext)->sadb_ext_type);
++ SENDERR(EINVAL); /* don't process these yet */
++
++ if(!(*pfkey_ext = (struct sadb_ext*)
++ pfkey_sens = (struct sadb_sens*)
++ MALLOC(sizeof(struct sadb_sens) +
++ (sens_len + integ_len) * sizeof(uint64_t)))) {
++ ERROR("pfkey_sens_build: "
++ "memory allocation failed\n");
++ SENDERR(ENOMEM);
++ }
++ memset(pfkey_sens,
++ 0,
++ sizeof(struct sadb_sens) +
++ (sens_len + integ_len) * sizeof(uint64_t));
++
++ pfkey_sens->sadb_sens_len = (sizeof(struct sadb_sens) +
++ (sens_len + integ_len) * sizeof(uint64_t)) / IPSEC_PFKEYv2_ALIGN;
++ pfkey_sens->sadb_sens_exttype = SADB_EXT_SENSITIVITY;
++ pfkey_sens->sadb_sens_dpd = dpd;
++ pfkey_sens->sadb_sens_sens_level = sens_level;
++ pfkey_sens->sadb_sens_sens_len = sens_len;
++ pfkey_sens->sadb_sens_integ_level = integ_level;
++ pfkey_sens->sadb_sens_integ_len = integ_len;
++ pfkey_sens->sadb_sens_reserved = 0;
++
++ bitmap = (uint64_t*)((char*)pfkey_ext + sizeof(struct sadb_sens));
++ for(i = 0; i < sens_len; i++) {
++ *bitmap = sens_bitmap[i];
++ bitmap++;
++ }
++ for(i = 0; i < integ_len; i++) {
++ *bitmap = integ_bitmap[i];
++ bitmap++;
++ }
++
++errlab:
++ return error;
++}
++
++int
++pfkey_prop_build(struct sadb_ext** pfkey_ext,
++ uint8_t replay,
++ unsigned int comb_num,
++ struct sadb_comb* comb)
++{
++ int error = 0;
++ int i;
++ struct sadb_prop *pfkey_prop = (struct sadb_prop *)*pfkey_ext;
++ struct sadb_comb *combp;
++
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_prop_build:\n");
++ /* sanity checks... */
++ if(pfkey_prop) {
++ ERROR("pfkey_prop_build: "
++ "why is pfkey_prop already pointing to something?\n");
++ SENDERR(EINVAL);
++ }
++
++ if(!(*pfkey_ext = (struct sadb_ext*)
++ pfkey_prop = (struct sadb_prop*)
++ MALLOC(sizeof(struct sadb_prop) +
++ comb_num * sizeof(struct sadb_comb)))) {
++ ERROR("pfkey_prop_build: "
++ "memory allocation failed\n");
++ SENDERR(ENOMEM);
++ }
++ memset(pfkey_prop,
++ 0,
++ sizeof(struct sadb_prop) +
++ comb_num * sizeof(struct sadb_comb));
++
++ pfkey_prop->sadb_prop_len = (sizeof(struct sadb_prop) +
++ comb_num * sizeof(struct sadb_comb)) / IPSEC_PFKEYv2_ALIGN;
++
++ pfkey_prop->sadb_prop_exttype = SADB_EXT_PROPOSAL;
++ pfkey_prop->sadb_prop_replay = replay;
++
++ for(i=0; i<3; i++) {
++ pfkey_prop->sadb_prop_reserved[i] = 0;
++ }
++
++ combp = (struct sadb_comb*)((char*)*pfkey_ext + sizeof(struct sadb_prop));
++ for(i = 0; i < comb_num; i++) {
++ memcpy (combp, &(comb[i]), sizeof(struct sadb_comb));
++ combp++;
++ }
++
++#if 0
++ uint8_t sadb_comb_auth;
++ uint8_t sadb_comb_encrypt;
++ uint16_t sadb_comb_flags;
++ uint16_t sadb_comb_auth_minbits;
++ uint16_t sadb_comb_auth_maxbits;
++ uint16_t sadb_comb_encrypt_minbits;
++ uint16_t sadb_comb_encrypt_maxbits;
++ uint32_t sadb_comb_reserved;
++ uint32_t sadb_comb_soft_allocations;
++ uint32_t sadb_comb_hard_allocations;
++ uint64_t sadb_comb_soft_bytes;
++ uint64_t sadb_comb_hard_bytes;
++ uint64_t sadb_comb_soft_addtime;
++ uint64_t sadb_comb_hard_addtime;
++ uint64_t sadb_comb_soft_usetime;
++ uint64_t sadb_comb_hard_usetime;
++ uint32_t sadb_comb_soft_packets;
++ uint32_t sadb_comb_hard_packets;
++#endif
++errlab:
++ return error;
++}
++
++int
++pfkey_supported_build(struct sadb_ext** pfkey_ext,
++ uint16_t exttype,
++ unsigned int alg_num,
++ struct sadb_alg* alg)
++{
++ int error = 0;
++ unsigned int i;
++ struct sadb_supported *pfkey_supported = (struct sadb_supported *)*pfkey_ext;
++ struct sadb_alg *pfkey_alg;
++
++ /* sanity checks... */
++ if(pfkey_supported) {
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_supported_build: "
++ "why is pfkey_supported already pointing to something?\n");
++ SENDERR(EINVAL);
++ }
++
++ if( !((exttype == SADB_EXT_SUPPORTED_AUTH) || (exttype == SADB_EXT_SUPPORTED_ENCRYPT))) {
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_supported_build: "
++ "unsupported extension type=%d.\n",
++ exttype);
++ SENDERR(EINVAL);
++ }
++
++ if(!(*pfkey_ext = (struct sadb_ext*)
++ pfkey_supported = (struct sadb_supported*)
++ MALLOC(sizeof(struct sadb_supported) +
++ alg_num *
++ sizeof(struct sadb_alg)))) {
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_supported_build: "
++ "memory allocation failed\n");
++ SENDERR(ENOMEM);
++ }
++ memset(pfkey_supported,
++ 0,
++ sizeof(struct sadb_supported) +
++ alg_num *
++ sizeof(struct sadb_alg));
++
++ pfkey_supported->sadb_supported_len = (sizeof(struct sadb_supported) +
++ alg_num *
++ sizeof(struct sadb_alg)) /
++ IPSEC_PFKEYv2_ALIGN;
++ pfkey_supported->sadb_supported_exttype = exttype;
++ pfkey_supported->sadb_supported_reserved = 0;
++
++ pfkey_alg = (struct sadb_alg*)((char*)pfkey_supported + sizeof(struct sadb_supported));
++ for(i = 0; i < alg_num; i++) {
++ memcpy (pfkey_alg, &(alg[i]), sizeof(struct sadb_alg));
++ pfkey_alg->sadb_alg_reserved = 0;
++ pfkey_alg++;
++ }
++
++#if 0
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_supported_build: "
++ "Sorry, I can't build exttype=%d yet.\n",
++ (*pfkey_ext)->sadb_ext_type);
++ SENDERR(EINVAL); /* don't process these yet */
++
++ uint8_t sadb_alg_id;
++ uint8_t sadb_alg_ivlen;
++ uint16_t sadb_alg_minbits;
++ uint16_t sadb_alg_maxbits;
++ uint16_t sadb_alg_reserved;
++#endif
++errlab:
++ return error;
++}
++
++int
++pfkey_spirange_build(struct sadb_ext** pfkey_ext,
++ uint16_t exttype,
++ uint32_t min, /* in network order */
++ uint32_t max) /* in network order */
++{
++ int error = 0;
++ struct sadb_spirange *pfkey_spirange = (struct sadb_spirange *)*pfkey_ext;
++
++ /* sanity checks... */
++ if(pfkey_spirange) {
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_spirange_build: "
++ "why is pfkey_spirange already pointing to something?\n");
++ SENDERR(EINVAL);
++ }
++
++ if(ntohl(max) < ntohl(min)) {
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_spirange_build: "
++ "minspi=%08x must be < maxspi=%08x.\n",
++ ntohl(min),
++ ntohl(max));
++ SENDERR(EINVAL);
++ }
++
++ if(ntohl(min) <= 255) {
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_spirange_build: "
++ "minspi=%08x must be > 255.\n",
++ ntohl(min));
++ SENDERR(EEXIST);
++ }
++
++ if(!(*pfkey_ext = (struct sadb_ext*)
++ pfkey_spirange = (struct sadb_spirange*)
++ MALLOC(sizeof(struct sadb_spirange)))) {
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_spirange_build: "
++ "memory allocation failed\n");
++ SENDERR(ENOMEM);
++ }
++ memset(pfkey_spirange,
++ 0,
++ sizeof(struct sadb_spirange));
++
++ pfkey_spirange->sadb_spirange_len = sizeof(struct sadb_spirange) / IPSEC_PFKEYv2_ALIGN;
++
++ pfkey_spirange->sadb_spirange_exttype = SADB_EXT_SPIRANGE;
++ pfkey_spirange->sadb_spirange_min = min;
++ pfkey_spirange->sadb_spirange_max = max;
++ pfkey_spirange->sadb_spirange_reserved = 0;
++ errlab:
++ return error;
++}
++
++int
++pfkey_x_kmprivate_build(struct sadb_ext** pfkey_ext)
++{
++ int error = 0;
++ struct sadb_x_kmprivate *pfkey_x_kmprivate = (struct sadb_x_kmprivate *)*pfkey_ext;
++
++ /* sanity checks... */
++ if(pfkey_x_kmprivate) {
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_x_kmprivate_build: "
++ "why is pfkey_x_kmprivate already pointing to something?\n");
++ SENDERR(EINVAL);
++ }
++
++ pfkey_x_kmprivate->sadb_x_kmprivate_reserved = 0;
++
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_x_kmprivate_build: "
++ "Sorry, I can't build exttype=%d yet.\n",
++ (*pfkey_ext)->sadb_ext_type);
++ SENDERR(EINVAL); /* don't process these yet */
++
++ if(!(*pfkey_ext = (struct sadb_ext*)
++ pfkey_x_kmprivate = (struct sadb_x_kmprivate*)
++ MALLOC(sizeof(struct sadb_x_kmprivate)))) {
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_x_kmprivate_build: "
++ "memory allocation failed\n");
++ SENDERR(ENOMEM);
++ }
++ memset(pfkey_x_kmprivate,
++ 0,
++ sizeof(struct sadb_x_kmprivate));
++
++ pfkey_x_kmprivate->sadb_x_kmprivate_len =
++ sizeof(struct sadb_x_kmprivate) / IPSEC_PFKEYv2_ALIGN;
++
++ pfkey_x_kmprivate->sadb_x_kmprivate_exttype = SADB_X_EXT_KMPRIVATE;
++ pfkey_x_kmprivate->sadb_x_kmprivate_reserved = 0;
++errlab:
++ return error;
++}
++
++int
++pfkey_x_satype_build(struct sadb_ext** pfkey_ext,
++ uint8_t satype)
++{
++ int error = 0;
++ int i;
++ struct sadb_x_satype *pfkey_x_satype = (struct sadb_x_satype *)*pfkey_ext;
++
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_x_satype_build:\n");
++ /* sanity checks... */
++ if(pfkey_x_satype) {
++ ERROR("pfkey_x_satype_build: "
++ "why is pfkey_x_satype already pointing to something?\n");
++ SENDERR(EINVAL);
++ }
++
++ if(!satype) {
++ ERROR("pfkey_x_satype_build: "
++ "SA type not set, must be non-zero.\n");
++ SENDERR(EINVAL);
++ }
++
++ if(satype > SADB_SATYPE_MAX) {
++ ERROR("pfkey_x_satype_build: "
++ "satype %d > max %d\n",
++ satype, SADB_SATYPE_MAX);
++ SENDERR(EINVAL);
++ }
++
++ if(!(*pfkey_ext = (struct sadb_ext*)pfkey_x_satype = (struct sadb_x_satype*)
++ MALLOC(sizeof(struct sadb_x_satype)))) {
++ ERROR("pfkey_x_satype_build: "
++ "memory allocation failed\n");
++ SENDERR(ENOMEM);
++ }
++ memset(pfkey_x_satype,
++ 0,
++ sizeof(struct sadb_x_satype));
++
++ pfkey_x_satype->sadb_x_satype_len = sizeof(struct sadb_x_satype) / IPSEC_PFKEYv2_ALIGN;
++
++ pfkey_x_satype->sadb_x_satype_exttype = SADB_X_EXT_SATYPE2;
++ pfkey_x_satype->sadb_x_satype_satype = satype;
++ for(i=0; i<3; i++) {
++ pfkey_x_satype->sadb_x_satype_reserved[i] = 0;
++ }
++
++errlab:
++ return error;
++}
++
++int
++pfkey_x_debug_build(struct sadb_ext** pfkey_ext,
++ uint32_t tunnel,
++ uint32_t netlink,
++ uint32_t xform,
++ uint32_t eroute,
++ uint32_t spi,
++ uint32_t radij,
++ uint32_t esp,
++ uint32_t ah,
++ uint32_t rcv,
++ uint32_t pfkey,
++ uint32_t ipcomp,
++ uint32_t verbose)
++{
++ int error = 0;
++ int i;
++ struct sadb_x_debug *pfkey_x_debug = (struct sadb_x_debug *)*pfkey_ext;
++
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_x_debug_build:\n");
++ /* sanity checks... */
++ if(pfkey_x_debug) {
++ ERROR("pfkey_x_debug_build: "
++ "why is pfkey_x_debug already pointing to something?\n");
++ SENDERR(EINVAL);
++ }
++
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_x_debug_build: "
++ "tunnel=%x netlink=%x xform=%x eroute=%x spi=%x radij=%x esp=%x ah=%x rcv=%x pfkey=%x ipcomp=%x verbose=%x?\n",
++ tunnel, netlink, xform, eroute, spi, radij, esp, ah, rcv, pfkey, ipcomp, verbose);
++
++ if(!(*pfkey_ext = (struct sadb_ext*)pfkey_x_debug = (struct sadb_x_debug*)
++ MALLOC(sizeof(struct sadb_x_debug)))) {
++ ERROR("pfkey_x_debug_build: "
++ "memory allocation failed\n");
++ SENDERR(ENOMEM);
++ }
++#if 0
++ memset(pfkey_x_debug,
++ 0,
++ sizeof(struct sadb_x_debug));
++#endif
++
++ pfkey_x_debug->sadb_x_debug_len = sizeof(struct sadb_x_debug) / IPSEC_PFKEYv2_ALIGN;
++ pfkey_x_debug->sadb_x_debug_exttype = SADB_X_EXT_DEBUG;
++
++ pfkey_x_debug->sadb_x_debug_tunnel = tunnel;
++ pfkey_x_debug->sadb_x_debug_netlink = netlink;
++ pfkey_x_debug->sadb_x_debug_xform = xform;
++ pfkey_x_debug->sadb_x_debug_eroute = eroute;
++ pfkey_x_debug->sadb_x_debug_spi = spi;
++ pfkey_x_debug->sadb_x_debug_radij = radij;
++ pfkey_x_debug->sadb_x_debug_esp = esp;
++ pfkey_x_debug->sadb_x_debug_ah = ah;
++ pfkey_x_debug->sadb_x_debug_rcv = rcv;
++ pfkey_x_debug->sadb_x_debug_pfkey = pfkey;
++ pfkey_x_debug->sadb_x_debug_ipcomp = ipcomp;
++ pfkey_x_debug->sadb_x_debug_verbose = verbose;
++
++ for(i=0; i<4; i++) {
++ pfkey_x_debug->sadb_x_debug_reserved[i] = 0;
++ }
++
++errlab:
++ return error;
++}
++
++int
++pfkey_x_nat_t_type_build(struct sadb_ext** pfkey_ext,
++ uint8_t type)
++{
++ int error = 0;
++ int i;
++ struct sadb_x_nat_t_type *pfkey_x_nat_t_type = (struct sadb_x_nat_t_type *)*pfkey_ext;
++
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_x_nat_t_type_build:\n");
++ /* sanity checks... */
++ if(pfkey_x_nat_t_type) {
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_x_nat_t_type_build: "
++ "why is pfkey_x_nat_t_type already pointing to something?\n");
++ SENDERR(EINVAL);
++ }
++
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_x_nat_t_type_build: "
++ "type=%d\n", type);
++
++ if(!(*pfkey_ext = (struct sadb_ext*)pfkey_x_nat_t_type = (struct sadb_x_nat_t_type*)
++ MALLOC(sizeof(struct sadb_x_nat_t_type)))) {
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_x_nat_t_type_build: "
++ "memory allocation failed\n");
++ SENDERR(ENOMEM);
++ }
++
++ pfkey_x_nat_t_type->sadb_x_nat_t_type_len = sizeof(struct sadb_x_nat_t_type) / IPSEC_PFKEYv2_ALIGN;
++ pfkey_x_nat_t_type->sadb_x_nat_t_type_exttype = SADB_X_EXT_NAT_T_TYPE;
++ pfkey_x_nat_t_type->sadb_x_nat_t_type_type = type;
++ for(i=0; i<3; i++) {
++ pfkey_x_nat_t_type->sadb_x_nat_t_type_reserved[i] = 0;
++ }
++
++errlab:
++ return error;
++}
++int
++pfkey_x_nat_t_port_build(struct sadb_ext** pfkey_ext,
++ uint16_t exttype,
++ uint16_t port)
++{
++ int error = 0;
++ struct sadb_x_nat_t_port *pfkey_x_nat_t_port = (struct sadb_x_nat_t_port *)*pfkey_ext;
++
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_x_nat_t_port_build:\n");
++ /* sanity checks... */
++ if(pfkey_x_nat_t_port) {
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_x_nat_t_port_build: "
++ "why is pfkey_x_nat_t_port already pointing to something?\n");
++ SENDERR(EINVAL);
++ }
++
++ switch(exttype) {
++ case SADB_X_EXT_NAT_T_SPORT:
++ case SADB_X_EXT_NAT_T_DPORT:
++ break;
++ default:
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_nat_t_port_build: "
++ "unrecognised ext_type=%d.\n",
++ exttype);
++ SENDERR(EINVAL);
++ }
++
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_x_nat_t_port_build: "
++ "ext=%d, port=%d\n", exttype, port);
++
++ if(!(*pfkey_ext = (struct sadb_ext*)pfkey_x_nat_t_port = (struct sadb_x_nat_t_port*)
++ MALLOC(sizeof(struct sadb_x_nat_t_port)))) {
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_x_nat_t_port_build: "
++ "memory allocation failed\n");
++ SENDERR(ENOMEM);
++ }
++
++ pfkey_x_nat_t_port->sadb_x_nat_t_port_len = sizeof(struct sadb_x_nat_t_port) / IPSEC_PFKEYv2_ALIGN;
++ pfkey_x_nat_t_port->sadb_x_nat_t_port_exttype = exttype;
++ pfkey_x_nat_t_port->sadb_x_nat_t_port_port = port;
++ pfkey_x_nat_t_port->sadb_x_nat_t_port_reserved = 0;
++
++errlab:
++ return error;
++}
++
++int pfkey_x_protocol_build(struct sadb_ext **pfkey_ext,
++ uint8_t protocol)
++{
++ int error = 0;
++ struct sadb_protocol * p = (struct sadb_protocol *)*pfkey_ext;
++ DEBUGGING(PF_KEY_DEBUG_BUILD,"pfkey_x_protocol_build: protocol=%u\n", protocol);
++ /* sanity checks... */
++ if (p != 0) {
++ ERROR("pfkey_x_protocol_build: bogus protocol pointer\n");
++ SENDERR(EINVAL);
++ }
++ if ((p = (struct sadb_protocol*)MALLOC(sizeof(*p))) == 0) {
++ ERROR("pfkey_build: memory allocation failed\n");
++ SENDERR(ENOMEM);
++ }
++ *pfkey_ext = (struct sadb_ext *)p;
++ p->sadb_protocol_len = sizeof(*p) / sizeof(uint64_t);
++ p->sadb_protocol_exttype = SADB_X_EXT_PROTOCOL;
++ p->sadb_protocol_proto = protocol;
++ p->sadb_protocol_flags = 0;
++ p->sadb_protocol_reserved2 = 0;
++ errlab:
++ return error;
++}
++
++
++#if I_DONT_THINK_THIS_WILL_BE_USEFUL
++int (*ext_default_builders[SADB_EXT_MAX +1])(struct sadb_msg*, struct sadb_ext*)
++ =
++{
++ NULL, /* pfkey_msg_build, */
++ pfkey_sa_build,
++ pfkey_lifetime_build,
++ pfkey_lifetime_build,
++ pfkey_lifetime_build,
++ pfkey_address_build,
++ pfkey_address_build,
++ pfkey_address_build,
++ pfkey_key_build,
++ pfkey_key_build,
++ pfkey_ident_build,
++ pfkey_ident_build,
++ pfkey_sens_build,
++ pfkey_prop_build,
++ pfkey_supported_build,
++ pfkey_supported_build,
++ pfkey_spirange_build,
++ pfkey_x_kmprivate_build,
++ pfkey_x_satype_build,
++ pfkey_sa_build,
++ pfkey_address_build,
++ pfkey_address_build,
++ pfkey_address_build,
++ pfkey_address_build,
++ pfkey_address_build,
++ pfkey_x_ext_debug_build
++};
++#endif
++
++int
++pfkey_msg_build(struct sadb_msg **pfkey_msg, struct sadb_ext *extensions[], int dir)
++{
++ int error = 0;
++ unsigned ext;
++ unsigned total_size;
++ struct sadb_ext *pfkey_ext;
++ int extensions_seen = 0;
++#ifndef __KERNEL__
++ struct sadb_ext *extensions_check[SADB_EXT_MAX + 1];
++#endif
++
++ if(!extensions[0]) {
++ ERROR("pfkey_msg_build: "
++ "extensions[0] must be specified (struct sadb_msg).\n");
++ SENDERR(EINVAL);
++ }
++
++ /* figure out the total size for all the requested extensions */
++ total_size = IPSEC_PFKEYv2_WORDS(sizeof(struct sadb_msg));
++ for(ext = 1; ext <= SADB_EXT_MAX; ext++) {
++ if(extensions[ext]) {
++ total_size += (extensions[ext])->sadb_ext_len;
++ }
++ }
++
++ /* allocate that much space */
++ *pfkey_msg = (struct sadb_msg*)MALLOC(total_size * IPSEC_PFKEYv2_ALIGN);
++ if(*pfkey_msg == NULL) {
++ ERROR("pfkey_msg_build: "
++ "memory allocation failed\n");
++ SENDERR(ENOMEM);
++ }
++
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_msg_build: "
++ "pfkey_msg=0p%p allocated %lu bytes, &(extensions[0])=0p%p\n",
++ *pfkey_msg,
++ (unsigned long)(total_size * IPSEC_PFKEYv2_ALIGN),
++ &(extensions[0]));
++
++ memcpy(*pfkey_msg,
++ extensions[0],
++ sizeof(struct sadb_msg));
++ (*pfkey_msg)->sadb_msg_len = total_size;
++ (*pfkey_msg)->sadb_msg_reserved = 0;
++ extensions_seen = 1 ;
++
++ /*
++ * point pfkey_ext to immediately after the space for the header,
++ * i.e. at the first extension location.
++ */
++ pfkey_ext = (struct sadb_ext*)(((char*)(*pfkey_msg)) + sizeof(struct sadb_msg));
++
++ for(ext = 1; ext <= SADB_EXT_MAX; ext++) {
++ /* copy from extension[ext] to buffer */
++ if(extensions[ext]) {
++ /* Is this type of extension permitted for this type of message? */
++ if(!(extensions_bitmaps[dir][EXT_BITS_PERM][(*pfkey_msg)->sadb_msg_type] &
++ 1<<ext)) {
++ ERROR("pfkey_msg_build: "
++ "ext type %d not permitted, exts_perm=%08x, 1<<type=%08x\n",
++ ext,
++ extensions_bitmaps[dir][EXT_BITS_PERM][(*pfkey_msg)->sadb_msg_type],
++ 1<<ext);
++ SENDERR(EINVAL);
++ }
++
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_msg_build: "
++ "copying %lu bytes from extensions[%u] (type=%d)\n",
++ (unsigned long)(extensions[ext]->sadb_ext_len * IPSEC_PFKEYv2_ALIGN),
++ ext,
++ extensions[ext]->sadb_ext_type);
++
++ memcpy(pfkey_ext,
++ extensions[ext],
++ (extensions[ext])->sadb_ext_len * IPSEC_PFKEYv2_ALIGN);
++ ((char*)pfkey_ext) += (extensions[ext])->sadb_ext_len * IPSEC_PFKEYv2_ALIGN;
++ /* Mark that we have seen this extension and remember the header location */
++ extensions_seen |= ( 1 << ext );
++ }
++ }
++
++ /* check required extensions */
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_msg_build: "
++ "extensions permitted=%08x, seen=%08x, required=%08x.\n",
++ extensions_bitmaps[dir][EXT_BITS_PERM][(*pfkey_msg)->sadb_msg_type],
++ extensions_seen,
++ extensions_bitmaps[dir][EXT_BITS_REQ][(*pfkey_msg)->sadb_msg_type]);
++
++ if((extensions_seen &
++ extensions_bitmaps[dir][EXT_BITS_REQ][(*pfkey_msg)->sadb_msg_type]) !=
++ extensions_bitmaps[dir][EXT_BITS_REQ][(*pfkey_msg)->sadb_msg_type]) {
++ DEBUGGING(PF_KEY_DEBUG_BUILD,
++ "pfkey_msg_build: "
++ "required extensions missing:%08x.\n",
++ extensions_bitmaps[dir][EXT_BITS_REQ][(*pfkey_msg)->sadb_msg_type] -
++ (extensions_seen &
++ extensions_bitmaps[dir][EXT_BITS_REQ][(*pfkey_msg)->sadb_msg_type]) );
++ SENDERR(EINVAL);
++ }
++
++#ifndef __KERNEL__
++/*
++ * this is silly, there is no need to reparse the message that we just built.
++ *
++ */
++ if((error = pfkey_msg_parse(*pfkey_msg, NULL, extensions_check, dir))) {
++ ERROR(
++ "pfkey_msg_build: "
++ "Trouble parsing newly built pfkey message, error=%d.\n",
++ error);
++ SENDERR(-error);
++ }
++#endif
++
++errlab:
++
++ return error;
++}
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.49 2004/04/12 02:59:06 mcr
++ * erroneously moved pfkey_v2_build.c
++ *
++ * Revision 1.48 2004/04/09 18:00:40 mcr
++ * Moved from linux/lib/libfreeswan/pfkey_v2_build.c,v
++ *
++ * Revision 1.47 2004/03/08 01:59:08 ken
++ * freeswan.h -> openswan.h
++ *
++ * Revision 1.46 2003/12/10 01:20:19 mcr
++ * NAT-traversal patches to KLIPS.
++ *
++ * Revision 1.45 2003/12/04 23:01:12 mcr
++ * removed ipsec_netlink.h
++ *
++ * Revision 1.44 2003/10/31 02:27:12 mcr
++ * pulled up port-selector patches and sa_id elimination.
++ *
++ * Revision 1.43.4.2 2003/10/29 01:11:32 mcr
++ * added debugging for pfkey library.
++ *
++ * Revision 1.43.4.1 2003/09/21 13:59:44 mcr
++ * pre-liminary X.509 patch - does not yet pass tests.
++ *
++ * Revision 1.43 2003/05/07 17:29:17 mcr
++ * new function pfkey_debug_func added for us in debugging from
++ * pfkey library.
++ *
++ * Revision 1.42 2003/01/30 02:32:09 rgb
++ *
++ * Rename SAref table macro names for clarity.
++ * Convert IPsecSAref_t from signed to unsigned to fix apparent SAref exhaustion bug.
++ *
++ * Revision 1.41 2002/12/13 18:16:02 mcr
++ * restored sa_ref code
++ *
++ * Revision 1.40 2002/12/13 18:06:52 mcr
++ * temporarily removed sadb_x_sa_ref reference for 2.xx
++ *
++ * Revision 1.39 2002/12/13 17:43:28 mcr
++ * commented out access to sadb_x_sa_ref for 2.xx branch
++ *
++ * Revision 1.38 2002/10/09 03:12:05 dhr
++ *
++ * [kenb+dhr] 64-bit fixes
++ *
++ * Revision 1.37 2002/09/20 15:40:39 rgb
++ * Added new function pfkey_sa_ref_build() to accomodate saref parameter.
++ *
++ * Revision 1.36 2002/09/20 05:01:22 rgb
++ * Generalise for platform independance: fix (ia64) using unsigned for sizes.
++ *
++ * Revision 1.35 2002/07/24 18:44:54 rgb
++ * Type fiddling to tame ia64 compiler.
++ *
++ * Revision 1.34 2002/05/23 07:14:11 rgb
++ * Cleaned up %p variants to 0p%p for test suite cleanup.
++ *
++ * Revision 1.33 2002/04/24 07:55:32 mcr
++ * #include patches and Makefiles for post-reorg compilation.
++ *
++ * Revision 1.32 2002/04/24 07:36:40 mcr
++ * Moved from ./lib/pfkey_v2_build.c,v
++ *
++ * Revision 1.31 2002/01/29 22:25:35 rgb
++ * Re-add ipsec_kversion.h to keep MALLOC happy.
++ *
++ * Revision 1.30 2002/01/29 01:59:09 mcr
++ * removal of kversions.h - sources that needed it now use ipsec_param.h.
++ * updating of IPv6 structures to match latest in6.h version.
++ * removed dead code from openswan.h that also duplicated kversions.h
++ * code.
++ *
++ * Revision 1.29 2001/12/19 21:06:09 rgb
++ * Added port numbers to pfkey_address_build() debugging.
++ *
++ * Revision 1.28 2001/11/06 19:47:47 rgb
++ * Added packet parameter to lifetime and comb structures.
++ *
++ * Revision 1.27 2001/10/18 04:45:24 rgb
++ * 2.4.9 kernel deprecates linux/malloc.h in favour of linux/slab.h,
++ * lib/openswan.h version macros moved to lib/kversions.h.
++ * Other compiler directive cleanups.
++ *
++ * Revision 1.26 2001/09/08 21:13:34 rgb
++ * Added pfkey ident extension support for ISAKMPd. (NetCelo)
++ *
++ * Revision 1.25 2001/06/14 19:35:16 rgb
++ * Update copyright date.
++ *
++ * Revision 1.24 2001/03/20 03:49:45 rgb
++ * Ditch superfluous debug_pfkey declaration.
++ * Move misplaced openswan.h inclusion for kernel case.
++ *
++ * Revision 1.23 2001/03/16 07:41:50 rgb
++ * Put openswan.h include before pluto includes.
++ *
++ * Revision 1.22 2001/02/27 22:24:56 rgb
++ * Re-formatting debug output (line-splitting, joining, 1arg/line).
++ * Check for satoa() return codes.
++ *
++ * Revision 1.21 2000/11/17 18:10:30 rgb
++ * Fixed bugs mostly relating to spirange, to treat all spi variables as
++ * network byte order since this is the way PF_KEYv2 stored spis.
++ *
++ * Revision 1.20 2000/10/12 00:02:39 rgb
++ * Removed 'format, ##' nonsense from debug macros for RH7.0.
++ *
++ * Revision 1.19 2000/10/10 20:10:20 rgb
++ * Added support for debug_ipcomp and debug_verbose to klipsdebug.
++ *
++ * Revision 1.18 2000/09/12 18:59:54 rgb
++ * Added Gerhard's IPv6 support to pfkey parts of libopenswan.
++ *
++ * Revision 1.17 2000/09/12 03:27:00 rgb
++ * Moved DEBUGGING definition to compile kernel with debug off.
++ *
++ * Revision 1.16 2000/09/08 19:22:12 rgb
++ * Fixed pfkey_prop_build() parameter to be only single indirection.
++ * Fixed struct alg copy.
++ *
++ * Revision 1.15 2000/08/20 21:40:01 rgb
++ * Added an address parameter sanity check to pfkey_address_build().
++ *
++ * Revision 1.14 2000/08/15 17:29:23 rgb
++ * Fixes from SZI to untested pfkey_prop_build().
++ *
++ * Revision 1.13 2000/06/02 22:54:14 rgb
++ * Added Gerhard Gessler's struct sockaddr_storage mods for IPv6 support.
++ *
++ * Revision 1.12 2000/05/10 19:24:01 rgb
++ * Fleshed out sensitivity, proposal and supported extensions.
++ *
++ * Revision 1.11 2000/03/16 14:07:23 rgb
++ * Renamed ALIGN macro to avoid fighting with others in kernel.
++ *
++ * Revision 1.10 2000/01/24 21:14:35 rgb
++ * Added disabled pluto pfkey lib debug flag.
++ *
++ * Revision 1.9 2000/01/21 06:27:32 rgb
++ * Added address cases for eroute flows.
++ * Removed unused code.
++ * Dropped unused argument to pfkey_x_satype_build().
++ * Indented compiler directives for readability.
++ * Added klipsdebug switching capability.
++ * Fixed SADB_EXT_MAX bug not permitting last extension access.
++ *
++ * Revision 1.8 1999/12/29 21:17:41 rgb
++ * Changed pfkey_msg_build() I/F to include a struct sadb_msg**
++ * parameter for cleaner manipulation of extensions[] and to guard
++ * against potential memory leaks.
++ * Changed the I/F to pfkey_msg_free() for the same reason.
++ *
++ * Revision 1.7 1999/12/09 23:12:20 rgb
++ * Removed unused cruft.
++ * Added argument to pfkey_sa_build() to do eroutes.
++ * Fixed exttype check in as yet unused pfkey_lifetime_build().
++ *
++ * Revision 1.6 1999/12/07 19:54:29 rgb
++ * Removed static pluto debug flag.
++ * Added functions for pfkey message and extensions initialisation
++ * and cleanup.
++ *
++ * Revision 1.5 1999/12/01 22:20:06 rgb
++ * Changed pfkey_sa_build to accept an SPI in network byte order.
++ * Added <string.h> to quiet userspace compiler.
++ * Moved pfkey_lib_debug variable into the library.
++ * Removed SATYPE check from pfkey_msg_hdr_build so FLUSH will work.
++ * Added extension assembly debugging.
++ * Isolated assignment with brackets to be sure of scope.
++ *
++ * Revision 1.4 1999/11/27 11:57:35 rgb
++ * Added ipv6 headers.
++ * Remove over-zealous algorithm sanity checkers from pfkey_sa_build.
++ * Debugging error messages added.
++ * Fixed missing auth and encrypt assignment bug.
++ * Add argument to pfkey_msg_parse() for direction.
++ * Move parse-after-build check inside pfkey_msg_build().
++ * Consolidated the 4 1-d extension bitmap arrays into one 4-d array.
++ * Add CVS log entry to bottom of file.
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/libfreeswan/pfkey_v2_debug.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,176 @@
++/*
++ * @(#) pfkey version 2 debugging messages
++ *
++ * Copyright (C) 2001 Richard Guy Briggs <rgb@openswan.org>
++ * and Michael Richardson <mcr@openswan.org>
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ * RCSID $Id$
++ *
++ */
++
++#ifdef __KERNEL__
++
++# include <linux/kernel.h> /* for printk */
++
++# include "openswan/ipsec_kversion.h" /* for malloc switch */
++# ifdef MALLOC_SLAB
++# include <linux/slab.h> /* kmalloc() */
++# else /* MALLOC_SLAB */
++# include <linux/malloc.h> /* kmalloc() */
++# endif /* MALLOC_SLAB */
++# include <linux/errno.h> /* error codes */
++# include <linux/types.h> /* size_t */
++# include <linux/interrupt.h> /* mark_bh */
++
++# include <linux/netdevice.h> /* struct device, and other headers */
++# include <linux/etherdevice.h> /* eth_type_trans */
++extern int debug_pfkey;
++
++#else /* __KERNEL__ */
++
++# include <sys/types.h>
++# include <linux/types.h>
++# include <linux/errno.h>
++
++#endif /* __KERNEL__ */
++
++#include "openswan.h"
++#include "pfkeyv2.h"
++#include "pfkey.h"
++
++/*
++ * This file provides ASCII translations of PF_KEY magic numbers.
++ *
++ */
++
++static char *pfkey_sadb_ext_strings[]={
++ "reserved", /* SADB_EXT_RESERVED 0 */
++ "security-association", /* SADB_EXT_SA 1 */
++ "lifetime-current", /* SADB_EXT_LIFETIME_CURRENT 2 */
++ "lifetime-hard", /* SADB_EXT_LIFETIME_HARD 3 */
++ "lifetime-soft", /* SADB_EXT_LIFETIME_SOFT 4 */
++ "source-address", /* SADB_EXT_ADDRESS_SRC 5 */
++ "destination-address", /* SADB_EXT_ADDRESS_DST 6 */
++ "proxy-address", /* SADB_EXT_ADDRESS_PROXY 7 */
++ "authentication-key", /* SADB_EXT_KEY_AUTH 8 */
++ "cipher-key", /* SADB_EXT_KEY_ENCRYPT 9 */
++ "source-identity", /* SADB_EXT_IDENTITY_SRC 10 */
++ "destination-identity", /* SADB_EXT_IDENTITY_DST 11 */
++ "sensitivity-label", /* SADB_EXT_SENSITIVITY 12 */
++ "proposal", /* SADB_EXT_PROPOSAL 13 */
++ "supported-auth", /* SADB_EXT_SUPPORTED_AUTH 14 */
++ "supported-cipher", /* SADB_EXT_SUPPORTED_ENCRYPT 15 */
++ "spi-range", /* SADB_EXT_SPIRANGE 16 */
++ "X-kmpprivate", /* SADB_X_EXT_KMPRIVATE 17 */
++ "X-satype2", /* SADB_X_EXT_SATYPE2 18 */
++ "X-security-association", /* SADB_X_EXT_SA2 19 */
++ "X-destination-address2", /* SADB_X_EXT_ADDRESS_DST2 20 */
++ "X-source-flow-address", /* SADB_X_EXT_ADDRESS_SRC_FLOW 21 */
++ "X-dest-flow-address", /* SADB_X_EXT_ADDRESS_DST_FLOW 22 */
++ "X-source-mask", /* SADB_X_EXT_ADDRESS_SRC_MASK 23 */
++ "X-dest-mask", /* SADB_X_EXT_ADDRESS_DST_MASK 24 */
++ "X-set-debug", /* SADB_X_EXT_DEBUG 25 */
++#ifdef NAT_TRAVERSAL
++ "X-NAT-T-type", /* SADB_X_EXT_NAT_T_TYPE 26 */
++ "X-NAT-T-sport", /* SADB_X_EXT_NAT_T_SPORT 27 */
++ "X-NAT-T-dport", /* SADB_X_EXT_NAT_T_DPORT 28 */
++ "X-NAT-T-OA", /* SADB_X_EXT_NAT_T_OA 29 */
++#endif
++};
++
++const char *
++pfkey_v2_sadb_ext_string(int ext)
++{
++ if(ext <= SADB_EXT_MAX) {
++ return pfkey_sadb_ext_strings[ext];
++ } else {
++ return "unknown-ext";
++ }
++}
++
++
++static char *pfkey_sadb_type_strings[]={
++ "reserved", /* SADB_RESERVED */
++ "getspi", /* SADB_GETSPI */
++ "update", /* SADB_UPDATE */
++ "add", /* SADB_ADD */
++ "delete", /* SADB_DELETE */
++ "get", /* SADB_GET */
++ "acquire", /* SADB_ACQUIRE */
++ "register", /* SADB_REGISTER */
++ "expire", /* SADB_EXPIRE */
++ "flush", /* SADB_FLUSH */
++ "dump", /* SADB_DUMP */
++ "x-promisc", /* SADB_X_PROMISC */
++ "x-pchange", /* SADB_X_PCHANGE */
++ "x-groupsa", /* SADB_X_GRPSA */
++ "x-addflow(eroute)", /* SADB_X_ADDFLOW */
++ "x-delflow(eroute)", /* SADB_X_DELFLOW */
++ "x-debug", /* SADB_X_DEBUG */
++};
++
++const char *
++pfkey_v2_sadb_type_string(int sadb_type)
++{
++ if(sadb_type <= SADB_MAX) {
++ return pfkey_sadb_type_strings[sadb_type];
++ } else {
++ return "unknown-sadb-type";
++ }
++}
++
++
++
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.9 2004/03/08 01:59:08 ken
++ * freeswan.h -> openswan.h
++ *
++ * Revision 1.8 2003/12/10 01:20:19 mcr
++ * NAT-traversal patches to KLIPS.
++ *
++ * Revision 1.7 2002/09/20 05:01:26 rgb
++ * Fixed limit inclusion error in both type and ext string conversion.
++ *
++ * Revision 1.6 2002/04/24 07:55:32 mcr
++ * #include patches and Makefiles for post-reorg compilation.
++ *
++ * Revision 1.5 2002/04/24 07:36:40 mcr
++ * Moved from ./lib/pfkey_v2_debug.c,v
++ *
++ * Revision 1.4 2002/01/29 22:25:36 rgb
++ * Re-add ipsec_kversion.h to keep MALLOC happy.
++ *
++ * Revision 1.3 2002/01/29 01:59:09 mcr
++ * removal of kversions.h - sources that needed it now use ipsec_param.h.
++ * updating of IPv6 structures to match latest in6.h version.
++ * removed dead code from openswan.h that also duplicated kversions.h
++ * code.
++ *
++ * Revision 1.2 2002/01/20 20:34:50 mcr
++ * added pfkey_v2_sadb_type_string to decode sadb_type to string.
++ *
++ * Revision 1.1 2001/11/27 05:30:06 mcr
++ * initial set of debug strings for pfkey debugging.
++ * this will eventually only be included for debug builds.
++ *
++ * Revision 1.1 2001/09/21 04:12:03 mcr
++ * first compilable version.
++ *
++ *
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/libfreeswan/pfkey_v2_ext_bits.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,808 @@
++/*
++ * RFC2367 PF_KEYv2 Key management API message parser
++ * Copyright (C) 1999, 2000, 2001 Richard Guy Briggs.
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ * RCSID $Id$
++ */
++
++/*
++ * Template from klips/net/ipsec/ipsec/ipsec_parse.c.
++ */
++
++char pfkey_v2_ext_bits_c_version[] = "$Id$";
++
++/*
++ * Some ugly stuff to allow consistent debugging code for use in the
++ * kernel and in user space
++*/
++
++#ifdef __KERNEL__
++
++# include <linux/kernel.h> /* for printk */
++
++# include "openswan/ipsec_kversion.h" /* for malloc switch */
++# ifdef MALLOC_SLAB
++# include <linux/slab.h> /* kmalloc() */
++# else /* MALLOC_SLAB */
++# include <linux/malloc.h> /* kmalloc() */
++# endif /* MALLOC_SLAB */
++# include <linux/errno.h> /* error codes */
++# include <linux/types.h> /* size_t */
++# include <linux/interrupt.h> /* mark_bh */
++
++# include <linux/netdevice.h> /* struct device, and other headers */
++# include <linux/etherdevice.h> /* eth_type_trans */
++# include <linux/ip.h> /* struct iphdr */
++# if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
++# include <linux/ipv6.h>
++# endif /* defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) */
++
++#else /* __KERNEL__ */
++
++# include <sys/types.h>
++# include <linux/types.h>
++# include <linux/errno.h>
++#endif
++
++#include <openswan.h>
++#include <pfkeyv2.h>
++#include <pfkey.h>
++
++unsigned int extensions_bitmaps[2/*in/out*/][2/*perm/req*/][SADB_MAX + 1/*ext*/] = {
++
++/* INBOUND EXTENSIONS */
++{
++
++/* PERMITTED IN */
++{
++/* SADB_RESERVED */
++0
++,
++/* SADB_GETSPI */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_ADDRESS_SRC
++| 1<<SADB_EXT_ADDRESS_DST
++| 1<<SADB_EXT_ADDRESS_PROXY
++| 1<<SADB_EXT_SPIRANGE
++,
++/* SADB_UPDATE */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_EXT_LIFETIME_CURRENT
++| 1<<SADB_EXT_LIFETIME_HARD
++| 1<<SADB_EXT_LIFETIME_SOFT
++| 1<<SADB_EXT_ADDRESS_SRC
++| 1<<SADB_EXT_ADDRESS_DST
++| 1<<SADB_EXT_ADDRESS_PROXY
++| 1<<SADB_EXT_KEY_AUTH
++| 1<<SADB_EXT_KEY_ENCRYPT
++| 1<<SADB_EXT_IDENTITY_SRC
++| 1<<SADB_EXT_IDENTITY_DST
++| 1<<SADB_EXT_SENSITIVITY
++| 1<<SADB_X_EXT_NAT_T_SPORT
++| 1<<SADB_X_EXT_NAT_T_DPORT
++,
++/* SADB_ADD */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_EXT_LIFETIME_HARD
++| 1<<SADB_EXT_LIFETIME_SOFT
++| 1<<SADB_EXT_ADDRESS_SRC
++| 1<<SADB_EXT_ADDRESS_DST
++| 1<<SADB_EXT_ADDRESS_PROXY
++| 1<<SADB_EXT_KEY_AUTH
++| 1<<SADB_EXT_KEY_ENCRYPT
++| 1<<SADB_EXT_IDENTITY_SRC
++| 1<<SADB_EXT_IDENTITY_DST
++| 1<<SADB_EXT_SENSITIVITY
++| 1<<SADB_X_EXT_NAT_T_TYPE
++| 1<<SADB_X_EXT_NAT_T_SPORT
++| 1<<SADB_X_EXT_NAT_T_DPORT
++| 1<<SADB_X_EXT_NAT_T_OA
++,
++/* SADB_DELETE */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_EXT_ADDRESS_SRC
++| 1<<SADB_EXT_ADDRESS_DST
++,
++/* SADB_GET */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_EXT_ADDRESS_SRC
++| 1<<SADB_EXT_ADDRESS_DST
++,
++/* SADB_ACQUIRE */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_ADDRESS_SRC
++| 1<<SADB_EXT_ADDRESS_DST
++| 1<<SADB_EXT_ADDRESS_PROXY
++| 1<<SADB_EXT_IDENTITY_SRC
++| 1<<SADB_EXT_IDENTITY_DST
++| 1<<SADB_EXT_SENSITIVITY
++| 1<<SADB_EXT_PROPOSAL
++,
++/* SADB_REGISTER */
++1<<SADB_EXT_RESERVED
++,
++/* SADB_EXPIRE */
++0
++,
++/* SADB_FLUSH */
++1<<SADB_EXT_RESERVED
++,
++/* SADB_DUMP */
++1<<SADB_EXT_RESERVED
++,
++/* SADB_X_PROMISC */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_EXT_LIFETIME_CURRENT
++| 1<<SADB_EXT_LIFETIME_HARD
++| 1<<SADB_EXT_LIFETIME_SOFT
++| 1<<SADB_EXT_ADDRESS_SRC
++| 1<<SADB_EXT_ADDRESS_DST
++| 1<<SADB_EXT_ADDRESS_PROXY
++| 1<<SADB_EXT_KEY_AUTH
++| 1<<SADB_EXT_KEY_ENCRYPT
++| 1<<SADB_EXT_IDENTITY_SRC
++| 1<<SADB_EXT_IDENTITY_DST
++| 1<<SADB_EXT_SENSITIVITY
++| 1<<SADB_EXT_PROPOSAL
++| 1<<SADB_EXT_SUPPORTED_AUTH
++| 1<<SADB_EXT_SUPPORTED_ENCRYPT
++| 1<<SADB_EXT_SPIRANGE
++| 1<<SADB_X_EXT_KMPRIVATE
++| 1<<SADB_X_EXT_SATYPE2
++| 1<<SADB_X_EXT_SA2
++| 1<<SADB_X_EXT_ADDRESS_DST2
++,
++/* SADB_X_PCHANGE */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_EXT_LIFETIME_CURRENT
++| 1<<SADB_EXT_LIFETIME_HARD
++| 1<<SADB_EXT_LIFETIME_SOFT
++| 1<<SADB_EXT_ADDRESS_SRC
++| 1<<SADB_EXT_ADDRESS_DST
++| 1<<SADB_EXT_ADDRESS_PROXY
++| 1<<SADB_EXT_KEY_AUTH
++| 1<<SADB_EXT_KEY_ENCRYPT
++| 1<<SADB_EXT_IDENTITY_SRC
++| 1<<SADB_EXT_IDENTITY_DST
++| 1<<SADB_EXT_SENSITIVITY
++| 1<<SADB_EXT_PROPOSAL
++| 1<<SADB_EXT_SUPPORTED_AUTH
++| 1<<SADB_EXT_SUPPORTED_ENCRYPT
++| 1<<SADB_EXT_SPIRANGE
++| 1<<SADB_X_EXT_KMPRIVATE
++| 1<<SADB_X_EXT_SATYPE2
++| 1<<SADB_X_EXT_SA2
++| 1<<SADB_X_EXT_ADDRESS_DST2
++,
++/* SADB_X_GRPSA */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_EXT_ADDRESS_DST
++| 1<<SADB_X_EXT_SATYPE2
++| 1<<SADB_X_EXT_SA2
++| 1<<SADB_X_EXT_ADDRESS_DST2
++,
++/* SADB_X_ADDFLOW */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_EXT_ADDRESS_SRC
++| 1<<SADB_EXT_ADDRESS_DST
++| 1<<SADB_X_EXT_ADDRESS_SRC_FLOW
++| 1<<SADB_X_EXT_ADDRESS_DST_FLOW
++| 1<<SADB_X_EXT_ADDRESS_SRC_MASK
++| 1<<SADB_X_EXT_ADDRESS_DST_MASK
++| 1<<SADB_EXT_IDENTITY_SRC
++| 1<<SADB_EXT_IDENTITY_DST
++| 1<<SADB_X_EXT_PROTOCOL
++,
++/* SADB_X_DELFLOW */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_X_EXT_ADDRESS_SRC_FLOW
++| 1<<SADB_X_EXT_ADDRESS_DST_FLOW
++| 1<<SADB_X_EXT_ADDRESS_SRC_MASK
++| 1<<SADB_X_EXT_ADDRESS_DST_MASK
++| 1<<SADB_EXT_IDENTITY_SRC
++| 1<<SADB_EXT_IDENTITY_DST
++| 1<<SADB_X_EXT_PROTOCOL
++,
++/* SADB_X_DEBUG */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_X_EXT_DEBUG
++,
++/* SADB_X_NAT_T_NEW_MAPPING */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_EXT_ADDRESS_SRC
++| 1<<SADB_EXT_ADDRESS_DST
++| 1<<SADB_X_EXT_NAT_T_SPORT
++| 1<<SADB_X_EXT_NAT_T_DPORT
++},
++
++/* REQUIRED IN */
++{
++/* SADB_RESERVED */
++0
++,
++/* SADB_GETSPI */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_ADDRESS_SRC
++| 1<<SADB_EXT_ADDRESS_DST
++| 1<<SADB_EXT_SPIRANGE
++,
++/* SADB_UPDATE */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_EXT_ADDRESS_SRC
++| 1<<SADB_EXT_ADDRESS_DST
++/*| 1<<SADB_EXT_KEY_AUTH*/
++/*| 1<<SADB_EXT_KEY_ENCRYPT*/
++,
++/* SADB_ADD */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_EXT_ADDRESS_SRC
++| 1<<SADB_EXT_ADDRESS_DST
++/*| 1<<SADB_EXT_KEY_AUTH*/
++/*| 1<<SADB_EXT_KEY_ENCRYPT*/
++,
++/* SADB_DELETE */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_EXT_ADDRESS_SRC
++| 1<<SADB_EXT_ADDRESS_DST
++,
++/* SADB_GET */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_EXT_ADDRESS_SRC
++| 1<<SADB_EXT_ADDRESS_DST
++,
++/* SADB_ACQUIRE */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_ADDRESS_SRC
++| 1<<SADB_EXT_ADDRESS_DST
++| 1<<SADB_EXT_PROPOSAL
++,
++/* SADB_REGISTER */
++1<<SADB_EXT_RESERVED
++,
++/* SADB_EXPIRE */
++0
++,
++/* SADB_FLUSH */
++1<<SADB_EXT_RESERVED
++,
++/* SADB_DUMP */
++1<<SADB_EXT_RESERVED
++,
++/* SADB_X_PROMISC */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_EXT_LIFETIME_CURRENT
++| 1<<SADB_EXT_LIFETIME_HARD
++| 1<<SADB_EXT_LIFETIME_SOFT
++| 1<<SADB_EXT_ADDRESS_SRC
++| 1<<SADB_EXT_ADDRESS_DST
++| 1<<SADB_EXT_ADDRESS_PROXY
++| 1<<SADB_EXT_KEY_AUTH
++| 1<<SADB_EXT_KEY_ENCRYPT
++| 1<<SADB_EXT_IDENTITY_SRC
++| 1<<SADB_EXT_IDENTITY_DST
++| 1<<SADB_EXT_SENSITIVITY
++| 1<<SADB_EXT_PROPOSAL
++| 1<<SADB_EXT_SUPPORTED_AUTH
++| 1<<SADB_EXT_SUPPORTED_ENCRYPT
++| 1<<SADB_EXT_SPIRANGE
++| 1<<SADB_X_EXT_KMPRIVATE
++| 1<<SADB_X_EXT_SATYPE2
++| 1<<SADB_X_EXT_SA2
++| 1<<SADB_X_EXT_ADDRESS_DST2
++,
++/* SADB_X_PCHANGE */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_EXT_LIFETIME_CURRENT
++| 1<<SADB_EXT_LIFETIME_HARD
++| 1<<SADB_EXT_LIFETIME_SOFT
++| 1<<SADB_EXT_ADDRESS_SRC
++| 1<<SADB_EXT_ADDRESS_DST
++| 1<<SADB_EXT_ADDRESS_PROXY
++| 1<<SADB_EXT_KEY_AUTH
++| 1<<SADB_EXT_KEY_ENCRYPT
++| 1<<SADB_EXT_IDENTITY_SRC
++| 1<<SADB_EXT_IDENTITY_DST
++| 1<<SADB_EXT_SENSITIVITY
++| 1<<SADB_EXT_PROPOSAL
++| 1<<SADB_EXT_SUPPORTED_AUTH
++| 1<<SADB_EXT_SUPPORTED_ENCRYPT
++| 1<<SADB_EXT_SPIRANGE
++| 1<<SADB_X_EXT_KMPRIVATE
++| 1<<SADB_X_EXT_SATYPE2
++| 1<<SADB_X_EXT_SA2
++| 1<<SADB_X_EXT_ADDRESS_DST2
++,
++/* SADB_X_GRPSA */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_EXT_ADDRESS_DST
++/*| 1<<SADB_X_EXT_SATYPE2*/
++/*| 1<<SADB_X_EXT_SA2*/
++/*| 1<<SADB_X_EXT_ADDRESS_DST2*/
++,
++/* SADB_X_ADDFLOW */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_EXT_ADDRESS_DST
++| 1<<SADB_X_EXT_ADDRESS_SRC_FLOW
++| 1<<SADB_X_EXT_ADDRESS_DST_FLOW
++| 1<<SADB_X_EXT_ADDRESS_SRC_MASK
++| 1<<SADB_X_EXT_ADDRESS_DST_MASK
++,
++/* SADB_X_DELFLOW */
++1<<SADB_EXT_RESERVED
++/*| 1<<SADB_EXT_SA*/
++#if 0 /* SADB_X_CLREROUTE doesn't need all these... */
++| 1<<SADB_X_EXT_ADDRESS_SRC_FLOW
++| 1<<SADB_X_EXT_ADDRESS_DST_FLOW
++| 1<<SADB_X_EXT_ADDRESS_SRC_MASK
++| 1<<SADB_X_EXT_ADDRESS_DST_MASK
++#endif
++,
++/* SADB_X_DEBUG */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_X_EXT_DEBUG
++,
++/* SADB_X_NAT_T_NEW_MAPPING */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_EXT_ADDRESS_SRC
++| 1<<SADB_EXT_ADDRESS_DST
++| 1<<SADB_X_EXT_NAT_T_SPORT
++| 1<<SADB_X_EXT_NAT_T_DPORT
++}
++
++},
++
++/* OUTBOUND EXTENSIONS */
++{
++
++/* PERMITTED OUT */
++{
++/* SADB_RESERVED */
++0
++,
++/* SADB_GETSPI */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_EXT_ADDRESS_SRC
++| 1<<SADB_EXT_ADDRESS_DST
++,
++/* SADB_UPDATE */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_EXT_LIFETIME_CURRENT
++| 1<<SADB_EXT_LIFETIME_HARD
++| 1<<SADB_EXT_LIFETIME_SOFT
++| 1<<SADB_EXT_ADDRESS_SRC
++| 1<<SADB_EXT_ADDRESS_DST
++| 1<<SADB_EXT_ADDRESS_PROXY
++| 1<<SADB_EXT_IDENTITY_SRC
++| 1<<SADB_EXT_IDENTITY_DST
++| 1<<SADB_EXT_SENSITIVITY
++| 1<<SADB_X_EXT_NAT_T_SPORT
++| 1<<SADB_X_EXT_NAT_T_DPORT
++,
++/* SADB_ADD */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_EXT_LIFETIME_HARD
++| 1<<SADB_EXT_LIFETIME_SOFT
++| 1<<SADB_EXT_ADDRESS_SRC
++| 1<<SADB_EXT_ADDRESS_DST
++| 1<<SADB_EXT_IDENTITY_SRC
++| 1<<SADB_EXT_IDENTITY_DST
++| 1<<SADB_EXT_SENSITIVITY
++| 1<<SADB_X_EXT_NAT_T_TYPE
++| 1<<SADB_X_EXT_NAT_T_SPORT
++| 1<<SADB_X_EXT_NAT_T_DPORT
++| 1<<SADB_X_EXT_NAT_T_OA
++,
++/* SADB_DELETE */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_EXT_ADDRESS_SRC
++| 1<<SADB_EXT_ADDRESS_DST
++,
++/* SADB_GET */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_EXT_LIFETIME_CURRENT
++| 1<<SADB_EXT_LIFETIME_HARD
++| 1<<SADB_EXT_LIFETIME_SOFT
++| 1<<SADB_EXT_ADDRESS_SRC
++| 1<<SADB_EXT_ADDRESS_DST
++| 1<<SADB_EXT_ADDRESS_PROXY
++| 1<<SADB_EXT_KEY_AUTH
++| 1<<SADB_EXT_KEY_ENCRYPT
++| 1<<SADB_EXT_IDENTITY_SRC
++| 1<<SADB_EXT_IDENTITY_DST
++| 1<<SADB_EXT_SENSITIVITY
++| 1<<SADB_X_EXT_NAT_T_TYPE
++| 1<<SADB_X_EXT_NAT_T_SPORT
++| 1<<SADB_X_EXT_NAT_T_DPORT
++| 1<<SADB_X_EXT_NAT_T_OA
++,
++/* SADB_ACQUIRE */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_ADDRESS_SRC
++| 1<<SADB_EXT_ADDRESS_DST
++| 1<<SADB_EXT_ADDRESS_PROXY
++| 1<<SADB_EXT_IDENTITY_SRC
++| 1<<SADB_EXT_IDENTITY_DST
++| 1<<SADB_EXT_SENSITIVITY
++| 1<<SADB_EXT_PROPOSAL
++,
++/* SADB_REGISTER */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SUPPORTED_AUTH
++| 1<<SADB_EXT_SUPPORTED_ENCRYPT
++,
++/* SADB_EXPIRE */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_EXT_LIFETIME_CURRENT
++| 1<<SADB_EXT_LIFETIME_HARD
++| 1<<SADB_EXT_LIFETIME_SOFT
++| 1<<SADB_EXT_ADDRESS_SRC
++| 1<<SADB_EXT_ADDRESS_DST
++,
++/* SADB_FLUSH */
++1<<SADB_EXT_RESERVED
++,
++/* SADB_DUMP */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_EXT_LIFETIME_CURRENT
++| 1<<SADB_EXT_LIFETIME_HARD
++| 1<<SADB_EXT_LIFETIME_SOFT
++| 1<<SADB_EXT_ADDRESS_SRC
++| 1<<SADB_EXT_ADDRESS_DST
++| 1<<SADB_EXT_ADDRESS_PROXY
++| 1<<SADB_EXT_KEY_AUTH
++| 1<<SADB_EXT_KEY_ENCRYPT
++| 1<<SADB_EXT_IDENTITY_SRC
++| 1<<SADB_EXT_IDENTITY_DST
++| 1<<SADB_EXT_SENSITIVITY
++| 1<<SADB_X_EXT_NAT_T_TYPE
++| 1<<SADB_X_EXT_NAT_T_SPORT
++| 1<<SADB_X_EXT_NAT_T_DPORT
++| 1<<SADB_X_EXT_NAT_T_OA
++,
++/* SADB_X_PROMISC */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_EXT_LIFETIME_CURRENT
++| 1<<SADB_EXT_LIFETIME_HARD
++| 1<<SADB_EXT_LIFETIME_SOFT
++| 1<<SADB_EXT_ADDRESS_SRC
++| 1<<SADB_EXT_ADDRESS_DST
++| 1<<SADB_EXT_ADDRESS_PROXY
++| 1<<SADB_EXT_KEY_AUTH
++| 1<<SADB_EXT_KEY_ENCRYPT
++| 1<<SADB_EXT_IDENTITY_SRC
++| 1<<SADB_EXT_IDENTITY_DST
++| 1<<SADB_EXT_SENSITIVITY
++| 1<<SADB_EXT_PROPOSAL
++| 1<<SADB_EXT_SUPPORTED_AUTH
++| 1<<SADB_EXT_SUPPORTED_ENCRYPT
++| 1<<SADB_EXT_SPIRANGE
++| 1<<SADB_X_EXT_KMPRIVATE
++| 1<<SADB_X_EXT_SATYPE2
++| 1<<SADB_X_EXT_SA2
++| 1<<SADB_X_EXT_ADDRESS_DST2
++,
++/* SADB_X_PCHANGE */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_EXT_LIFETIME_CURRENT
++| 1<<SADB_EXT_LIFETIME_HARD
++| 1<<SADB_EXT_LIFETIME_SOFT
++| 1<<SADB_EXT_ADDRESS_SRC
++| 1<<SADB_EXT_ADDRESS_DST
++| 1<<SADB_EXT_ADDRESS_PROXY
++| 1<<SADB_EXT_KEY_AUTH
++| 1<<SADB_EXT_KEY_ENCRYPT
++| 1<<SADB_EXT_IDENTITY_SRC
++| 1<<SADB_EXT_IDENTITY_DST
++| 1<<SADB_EXT_SENSITIVITY
++| 1<<SADB_EXT_PROPOSAL
++| 1<<SADB_EXT_SUPPORTED_AUTH
++| 1<<SADB_EXT_SUPPORTED_ENCRYPT
++| 1<<SADB_EXT_SPIRANGE
++| 1<<SADB_X_EXT_KMPRIVATE
++| 1<<SADB_X_EXT_SATYPE2
++| 1<<SADB_X_EXT_SA2
++| 1<<SADB_X_EXT_ADDRESS_DST2
++,
++/* SADB_X_GRPSA */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_EXT_ADDRESS_DST
++| 1<<SADB_X_EXT_SATYPE2
++| 1<<SADB_X_EXT_SA2
++| 1<<SADB_X_EXT_ADDRESS_DST2
++,
++/* SADB_X_ADDFLOW */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_EXT_ADDRESS_SRC
++| 1<<SADB_EXT_ADDRESS_DST
++| 1<<SADB_X_EXT_ADDRESS_SRC_FLOW
++| 1<<SADB_X_EXT_ADDRESS_DST_FLOW
++| 1<<SADB_X_EXT_ADDRESS_SRC_MASK
++| 1<<SADB_X_EXT_ADDRESS_DST_MASK
++| 1<<SADB_X_EXT_PROTOCOL
++,
++/* SADB_X_DELFLOW */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_X_EXT_ADDRESS_SRC_FLOW
++| 1<<SADB_X_EXT_ADDRESS_DST_FLOW
++| 1<<SADB_X_EXT_ADDRESS_SRC_MASK
++| 1<<SADB_X_EXT_ADDRESS_DST_MASK
++| 1<<SADB_X_EXT_PROTOCOL
++,
++/* SADB_X_DEBUG */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_X_EXT_DEBUG
++,
++/* SADB_X_NAT_T_NEW_MAPPING */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_EXT_ADDRESS_SRC
++| 1<<SADB_EXT_ADDRESS_DST
++| 1<<SADB_X_EXT_NAT_T_SPORT
++| 1<<SADB_X_EXT_NAT_T_DPORT
++},
++
++/* REQUIRED OUT */
++{
++/* SADB_RESERVED */
++0
++,
++/* SADB_GETSPI */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_EXT_ADDRESS_SRC
++| 1<<SADB_EXT_ADDRESS_DST
++,
++/* SADB_UPDATE */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_EXT_ADDRESS_SRC
++| 1<<SADB_EXT_ADDRESS_DST
++,
++/* SADB_ADD */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_EXT_ADDRESS_SRC
++| 1<<SADB_EXT_ADDRESS_DST
++,
++/* SADB_DELETE */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_EXT_ADDRESS_SRC
++| 1<<SADB_EXT_ADDRESS_DST
++,
++/* SADB_GET */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_EXT_ADDRESS_SRC
++| 1<<SADB_EXT_ADDRESS_DST
++/* | 1<<SADB_EXT_KEY_AUTH */
++/* | 1<<SADB_EXT_KEY_ENCRYPT */
++,
++/* SADB_ACQUIRE */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_ADDRESS_SRC
++| 1<<SADB_EXT_ADDRESS_DST
++| 1<<SADB_EXT_PROPOSAL
++,
++/* SADB_REGISTER */
++1<<SADB_EXT_RESERVED
++/* | 1<<SADB_EXT_SUPPORTED_AUTH
++ | 1<<SADB_EXT_SUPPORTED_ENCRYPT */
++,
++/* SADB_EXPIRE */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_EXT_LIFETIME_CURRENT
++/* | 1<<SADB_EXT_LIFETIME_HARD
++ | 1<<SADB_EXT_LIFETIME_SOFT */
++| 1<<SADB_EXT_ADDRESS_SRC
++| 1<<SADB_EXT_ADDRESS_DST
++,
++/* SADB_FLUSH */
++1<<SADB_EXT_RESERVED
++,
++/* SADB_DUMP */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_EXT_ADDRESS_SRC
++| 1<<SADB_EXT_ADDRESS_DST
++| 1<<SADB_EXT_KEY_AUTH
++| 1<<SADB_EXT_KEY_ENCRYPT
++,
++/* SADB_X_PROMISC */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_EXT_LIFETIME_CURRENT
++| 1<<SADB_EXT_LIFETIME_HARD
++| 1<<SADB_EXT_LIFETIME_SOFT
++| 1<<SADB_EXT_ADDRESS_SRC
++| 1<<SADB_EXT_ADDRESS_DST
++| 1<<SADB_EXT_ADDRESS_PROXY
++| 1<<SADB_EXT_KEY_AUTH
++| 1<<SADB_EXT_KEY_ENCRYPT
++| 1<<SADB_EXT_IDENTITY_SRC
++| 1<<SADB_EXT_IDENTITY_DST
++| 1<<SADB_EXT_SENSITIVITY
++| 1<<SADB_EXT_PROPOSAL
++| 1<<SADB_EXT_SUPPORTED_AUTH
++| 1<<SADB_EXT_SUPPORTED_ENCRYPT
++| 1<<SADB_EXT_SPIRANGE
++| 1<<SADB_X_EXT_KMPRIVATE
++| 1<<SADB_X_EXT_SATYPE2
++| 1<<SADB_X_EXT_SA2
++| 1<<SADB_X_EXT_ADDRESS_DST2
++,
++/* SADB_X_PCHANGE */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_EXT_LIFETIME_CURRENT
++| 1<<SADB_EXT_LIFETIME_HARD
++| 1<<SADB_EXT_LIFETIME_SOFT
++| 1<<SADB_EXT_ADDRESS_SRC
++| 1<<SADB_EXT_ADDRESS_DST
++| 1<<SADB_EXT_ADDRESS_PROXY
++| 1<<SADB_EXT_KEY_AUTH
++| 1<<SADB_EXT_KEY_ENCRYPT
++| 1<<SADB_EXT_IDENTITY_SRC
++| 1<<SADB_EXT_IDENTITY_DST
++| 1<<SADB_EXT_SENSITIVITY
++| 1<<SADB_EXT_PROPOSAL
++| 1<<SADB_EXT_SUPPORTED_AUTH
++| 1<<SADB_EXT_SUPPORTED_ENCRYPT
++| 1<<SADB_EXT_SPIRANGE
++| 1<<SADB_X_EXT_KMPRIVATE
++| 1<<SADB_X_EXT_SATYPE2
++| 1<<SADB_X_EXT_SA2
++| 1<<SADB_X_EXT_ADDRESS_DST2
++,
++/* SADB_X_GRPSA */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_EXT_ADDRESS_DST
++,
++/* SADB_X_ADDFLOW */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_EXT_ADDRESS_DST
++| 1<<SADB_X_EXT_ADDRESS_SRC_FLOW
++| 1<<SADB_X_EXT_ADDRESS_DST_FLOW
++| 1<<SADB_X_EXT_ADDRESS_SRC_MASK
++| 1<<SADB_X_EXT_ADDRESS_DST_MASK
++,
++/* SADB_X_DELFLOW */
++1<<SADB_EXT_RESERVED
++/*| 1<<SADB_EXT_SA*/
++| 1<<SADB_X_EXT_ADDRESS_SRC_FLOW
++| 1<<SADB_X_EXT_ADDRESS_DST_FLOW
++| 1<<SADB_X_EXT_ADDRESS_SRC_MASK
++| 1<<SADB_X_EXT_ADDRESS_DST_MASK
++,
++/* SADB_X_DEBUG */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_X_EXT_DEBUG
++,
++/* SADB_X_NAT_T_NEW_MAPPING */
++1<<SADB_EXT_RESERVED
++| 1<<SADB_EXT_SA
++| 1<<SADB_EXT_ADDRESS_SRC
++| 1<<SADB_EXT_ADDRESS_DST
++| 1<<SADB_X_EXT_NAT_T_SPORT
++| 1<<SADB_X_EXT_NAT_T_DPORT
++}
++}
++};
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.20 2004/03/08 01:59:08 ken
++ * freeswan.h -> openswan.h
++ *
++ * Revision 1.19 2003/12/22 21:38:13 mcr
++ * removed extraenous #endif.
++ *
++ * Revision 1.18 2003/12/22 19:34:41 mcr
++ * added 0.6c NAT-T patch.
++ *
++ * Revision 1.17 2003/12/10 01:20:19 mcr
++ * NAT-traversal patches to KLIPS.
++ *
++ * Revision 1.16 2003/10/31 02:27:12 mcr
++ * pulled up port-selector patches and sa_id elimination.
++ *
++ * Revision 1.15.30.1 2003/09/21 13:59:44 mcr
++ * pre-liminary X.509 patch - does not yet pass tests.
++ *
++ * Revision 1.15 2002/04/24 07:55:32 mcr
++ * #include patches and Makefiles for post-reorg compilation.
++ *
++ * Revision 1.14 2002/04/24 07:36:40 mcr
++ * Moved from ./lib/pfkey_v2_ext_bits.c,v
++ *
++ * Revision 1.13 2002/01/29 22:25:36 rgb
++ * Re-add ipsec_kversion.h to keep MALLOC happy.
++ *
++ * Revision 1.12 2002/01/29 01:59:10 mcr
++ * removal of kversions.h - sources that needed it now use ipsec_param.h.
++ * updating of IPv6 structures to match latest in6.h version.
++ * removed dead code from openswan.h that also duplicated kversions.h
++ * code.
++ *
++ * Revision 1.11 2001/10/18 04:45:24 rgb
++ * 2.4.9 kernel deprecates linux/malloc.h in favour of linux/slab.h,
++ * lib/openswan.h version macros moved to lib/kversions.h.
++ * Other compiler directive cleanups.
++ *
++ * Revision 1.10 2001/09/08 21:13:35 rgb
++ * Added pfkey ident extension support for ISAKMPd. (NetCelo)
++ *
++ * Revision 1.9 2001/06/14 19:35:16 rgb
++ * Update copyright date.
++ *
++ * Revision 1.8 2001/03/26 23:07:36 rgb
++ * Remove requirement for auth and enc key from UPDATE.
++ *
++ * Revision 1.7 2000/09/12 22:35:37 rgb
++ * Restructured to remove unused extensions from CLEARFLOW messages.
++ *
++ * Revision 1.6 2000/09/09 06:39:01 rgb
++ * Added comments for clarity.
++ *
++ * Revision 1.5 2000/06/02 22:54:14 rgb
++ * Added Gerhard Gessler's struct sockaddr_storage mods for IPv6 support.
++ *
++ * Revision 1.4 2000/01/21 06:27:56 rgb
++ * Added address cases for eroute flows.
++ * Added comments for each message type.
++ * Added klipsdebug switching capability.
++ * Fixed GRPSA bitfields.
++ *
++ * Revision 1.3 1999/12/01 22:20:27 rgb
++ * Remove requirement for a proxy address in an incoming getspi message.
++ *
++ * Revision 1.2 1999/11/27 11:57:06 rgb
++ * Consolidated the 4 1-d extension bitmap arrays into one 4-d array.
++ * Add CVS log entry to bottom of file.
++ * Cleaned out unused bits.
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/libfreeswan/pfkey_v2_parse.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,1820 @@
++/*
++ * RFC2367 PF_KEYv2 Key management API message parser
++ * Copyright (C) 1999, 2000, 2001 Richard Guy Briggs.
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ * RCSID $Id$
++ */
++
++/*
++ * Template from klips/net/ipsec/ipsec/ipsec_parser.c.
++ */
++
++char pfkey_v2_parse_c_version[] = "$Id$";
++
++/*
++ * Some ugly stuff to allow consistent debugging code for use in the
++ * kernel and in user space
++*/
++
++#ifdef __KERNEL__
++
++# include <linux/kernel.h> /* for printk */
++
++#include "openswan/ipsec_kversion.h" /* for malloc switch */
++
++# ifdef MALLOC_SLAB
++# include <linux/slab.h> /* kmalloc() */
++# else /* MALLOC_SLAB */
++# include <linux/malloc.h> /* kmalloc() */
++# endif /* MALLOC_SLAB */
++# include <linux/errno.h> /* error codes */
++# include <linux/types.h> /* size_t */
++# include <linux/interrupt.h> /* mark_bh */
++
++# include <linux/netdevice.h> /* struct device, and other headers */
++# include <linux/etherdevice.h> /* eth_type_trans */
++# include <linux/ip.h> /* struct iphdr */
++# if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
++# include <linux/ipv6.h> /* struct ipv6hdr */
++# endif /* if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) */
++extern int debug_pfkey;
++
++# include <openswan.h>
++
++#include "openswan/ipsec_encap.h"
++
++#else /* __KERNEL__ */
++
++# include <sys/types.h>
++# include <linux/types.h>
++# include <linux/errno.h>
++
++# include <openswan.h>
++# include "constants.h"
++# include "programs/pluto/defs.h" /* for PRINTF_LIKE */
++
++#endif /* __KERNEL__ */
++
++
++#include <pfkeyv2.h>
++#include <pfkey.h>
++
++#include "openswan/ipsec_sa.h" /* IPSEC_SAREF_NULL, IPSEC_SA_REF_TABLE_IDX_WIDTH */
++
++/*
++ * how to handle debugging for pfkey.
++ */
++#include <openswan/pfkey_debug.h>
++
++unsigned int pfkey_lib_debug = PF_KEY_DEBUG_PARSE_NONE;
++void (*pfkey_debug_func)(const char *message, ...) PRINTF_LIKE(1);
++void (*pfkey_error_func)(const char *message, ...) PRINTF_LIKE(1);
++
++
++#define SENDERR(_x) do { error = -(_x); goto errlab; } while (0)
++
++struct satype_tbl {
++ uint8_t proto;
++ uint8_t satype;
++ char* name;
++} static satype_tbl[] = {
++#ifdef __KERNEL__
++ { IPPROTO_ESP, SADB_SATYPE_ESP, "ESP" },
++ { IPPROTO_AH, SADB_SATYPE_AH, "AH" },
++ { IPPROTO_IPIP, SADB_X_SATYPE_IPIP, "IPIP" },
++#ifdef CONFIG_IPSEC_IPCOMP
++ { IPPROTO_COMP, SADB_X_SATYPE_COMP, "COMP" },
++#endif /* CONFIG_IPSEC_IPCOMP */
++ { IPPROTO_INT, SADB_X_SATYPE_INT, "INT" },
++#else /* __KERNEL__ */
++ { SA_ESP, SADB_SATYPE_ESP, "ESP" },
++ { SA_AH, SADB_SATYPE_AH, "AH" },
++ { SA_IPIP, SADB_X_SATYPE_IPIP, "IPIP" },
++ { SA_COMP, SADB_X_SATYPE_COMP, "COMP" },
++ { SA_INT, SADB_X_SATYPE_INT, "INT" },
++#endif /* __KERNEL__ */
++ { 0, 0, "UNKNOWN" }
++};
++
++uint8_t
++satype2proto(uint8_t satype)
++{
++ int i =0;
++
++ while(satype_tbl[i].satype != satype && satype_tbl[i].satype != 0) {
++ i++;
++ }
++ return satype_tbl[i].proto;
++}
++
++uint8_t
++proto2satype(uint8_t proto)
++{
++ int i = 0;
++
++ while(satype_tbl[i].proto != proto && satype_tbl[i].proto != 0) {
++ i++;
++ }
++ return satype_tbl[i].satype;
++}
++
++char*
++satype2name(uint8_t satype)
++{
++ int i = 0;
++
++ while(satype_tbl[i].satype != satype && satype_tbl[i].satype != 0) {
++ i++;
++ }
++ return satype_tbl[i].name;
++}
++
++char*
++proto2name(uint8_t proto)
++{
++ int i = 0;
++
++ while(satype_tbl[i].proto != proto && satype_tbl[i].proto != 0) {
++ i++;
++ }
++ return satype_tbl[i].name;
++}
++
++/* Default extension parsers taken from the KLIPS code */
++
++DEBUG_NO_STATIC int
++pfkey_sa_parse(struct sadb_ext *pfkey_ext)
++{
++ int error = 0;
++ struct sadb_sa *pfkey_sa = (struct sadb_sa *)pfkey_ext;
++#if 0
++ struct sadb_sa sav2;
++#endif
++
++ /* sanity checks... */
++ if(!pfkey_sa) {
++ ERROR("pfkey_sa_parse: "
++ "NULL pointer passed in.\n");
++ SENDERR(EINVAL);
++ }
++
++#if 0
++ /* check if this structure is short, and if so, fix it up.
++ * XXX this is NOT the way to do things.
++ */
++ if(pfkey_sa->sadb_sa_len == sizeof(struct sadb_sa_v1)/IPSEC_PFKEYv2_ALIGN) {
++
++ /* yes, so clear out a temporary structure, and copy first */
++ memset(&sav2, 0, sizeof(sav2));
++ memcpy(&sav2, pfkey_sa, sizeof(struct sadb_sa_v1));
++ sav2.sadb_x_sa_ref=-1;
++ sav2.sadb_sa_len = sizeof(struct sadb_sa) / IPSEC_PFKEYv2_ALIGN;
++
++ pfkey_sa = &sav2;
++ }
++#endif
++
++
++ if(pfkey_sa->sadb_sa_len != sizeof(struct sadb_sa) / IPSEC_PFKEYv2_ALIGN) {
++ ERROR(
++ "pfkey_sa_parse: "
++ "length wrong pfkey_sa->sadb_sa_len=%d sizeof(struct sadb_sa)=%d.\n",
++ pfkey_sa->sadb_sa_len,
++ (int)sizeof(struct sadb_sa));
++ SENDERR(EINVAL);
++ }
++
++ if(pfkey_sa->sadb_sa_encrypt > SADB_EALG_MAX) {
++ ERROR(
++ "pfkey_sa_parse: "
++ "pfkey_sa->sadb_sa_encrypt=%d > SADB_EALG_MAX=%d.\n",
++ pfkey_sa->sadb_sa_encrypt,
++ SADB_EALG_MAX);
++ SENDERR(EINVAL);
++ }
++
++ if(pfkey_sa->sadb_sa_auth > SADB_AALG_MAX) {
++ ERROR(
++ "pfkey_sa_parse: "
++ "pfkey_sa->sadb_sa_auth=%d > SADB_AALG_MAX=%d.\n",
++ pfkey_sa->sadb_sa_auth,
++ SADB_AALG_MAX);
++ SENDERR(EINVAL);
++ }
++
++ if(pfkey_sa->sadb_sa_state > SADB_SASTATE_MAX) {
++ ERROR(
++ "pfkey_sa_parse: "
++ "state=%d exceeds MAX=%d.\n",
++ pfkey_sa->sadb_sa_state,
++ SADB_SASTATE_MAX);
++ SENDERR(EINVAL);
++ }
++
++ if(pfkey_sa->sadb_sa_state == SADB_SASTATE_DEAD) {
++ ERROR(
++ "pfkey_sa_parse: "
++ "state=%d is DEAD=%d.\n",
++ pfkey_sa->sadb_sa_state,
++ SADB_SASTATE_DEAD);
++ SENDERR(EINVAL);
++ }
++
++ if(pfkey_sa->sadb_sa_replay > 64) {
++ ERROR(
++ "pfkey_sa_parse: "
++ "replay window size: %d -- must be 0 <= size <= 64\n",
++ pfkey_sa->sadb_sa_replay);
++ SENDERR(EINVAL);
++ }
++
++ if(! ((pfkey_sa->sadb_sa_exttype == SADB_EXT_SA) ||
++ (pfkey_sa->sadb_sa_exttype == SADB_X_EXT_SA2)))
++ {
++ ERROR(
++ "pfkey_sa_parse: "
++ "unknown exttype=%d, expecting SADB_EXT_SA=%d or SADB_X_EXT_SA2=%d.\n",
++ pfkey_sa->sadb_sa_exttype,
++ SADB_EXT_SA,
++ SADB_X_EXT_SA2);
++ SENDERR(EINVAL);
++ }
++
++ if((IPSEC_SAREF_NULL != pfkey_sa->sadb_x_sa_ref) && (pfkey_sa->sadb_x_sa_ref >= (1 << IPSEC_SA_REF_TABLE_IDX_WIDTH))) {
++ ERROR(
++ "pfkey_sa_parse: "
++ "SAref=%d must be (SAref == IPSEC_SAREF_NULL(%d) || SAref < IPSEC_SA_REF_TABLE_NUM_ENTRIES(%d)).\n",
++ pfkey_sa->sadb_x_sa_ref,
++ IPSEC_SAREF_NULL,
++ IPSEC_SA_REF_TABLE_NUM_ENTRIES);
++ SENDERR(EINVAL);
++ }
++
++ DEBUGGING(PF_KEY_DEBUG_PARSE_STRUCT,
++ "pfkey_sa_parse: "
++ "successfully found len=%d exttype=%d(%s) spi=%08lx replay=%d state=%d auth=%d encrypt=%d flags=%d ref=%d.\n",
++ pfkey_sa->sadb_sa_len,
++ pfkey_sa->sadb_sa_exttype,
++ pfkey_v2_sadb_ext_string(pfkey_sa->sadb_sa_exttype),
++ (long unsigned int)ntohl(pfkey_sa->sadb_sa_spi),
++ pfkey_sa->sadb_sa_replay,
++ pfkey_sa->sadb_sa_state,
++ pfkey_sa->sadb_sa_auth,
++ pfkey_sa->sadb_sa_encrypt,
++ pfkey_sa->sadb_sa_flags,
++ pfkey_sa->sadb_x_sa_ref);
++
++ errlab:
++ return error;
++}
++
++DEBUG_NO_STATIC int
++pfkey_lifetime_parse(struct sadb_ext *pfkey_ext)
++{
++ int error = 0;
++ struct sadb_lifetime *pfkey_lifetime = (struct sadb_lifetime *)pfkey_ext;
++
++ DEBUGGING(PF_KEY_DEBUG_PARSE_FLOW,
++ "pfkey_lifetime_parse:enter\n");
++ /* sanity checks... */
++ if(!pfkey_lifetime) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_lifetime_parse: "
++ "NULL pointer passed in.\n");
++ SENDERR(EINVAL);
++ }
++
++ if(pfkey_lifetime->sadb_lifetime_len !=
++ sizeof(struct sadb_lifetime) / IPSEC_PFKEYv2_ALIGN) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_lifetime_parse: "
++ "length wrong pfkey_lifetime->sadb_lifetime_len=%d sizeof(struct sadb_lifetime)=%d.\n",
++ pfkey_lifetime->sadb_lifetime_len,
++ (int)sizeof(struct sadb_lifetime));
++ SENDERR(EINVAL);
++ }
++
++ if((pfkey_lifetime->sadb_lifetime_exttype != SADB_EXT_LIFETIME_HARD) &&
++ (pfkey_lifetime->sadb_lifetime_exttype != SADB_EXT_LIFETIME_SOFT) &&
++ (pfkey_lifetime->sadb_lifetime_exttype != SADB_EXT_LIFETIME_CURRENT)) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_lifetime_parse: "
++ "unexpected ext_type=%d.\n",
++ pfkey_lifetime->sadb_lifetime_exttype);
++ SENDERR(EINVAL);
++ }
++
++ DEBUGGING(PF_KEY_DEBUG_PARSE_STRUCT,
++ "pfkey_lifetime_parse: "
++ "life_type=%d(%s) alloc=%u bytes=%u add=%u use=%u pkts=%u.\n",
++ pfkey_lifetime->sadb_lifetime_exttype,
++ pfkey_v2_sadb_ext_string(pfkey_lifetime->sadb_lifetime_exttype),
++ pfkey_lifetime->sadb_lifetime_allocations,
++ (unsigned)pfkey_lifetime->sadb_lifetime_bytes,
++ (unsigned)pfkey_lifetime->sadb_lifetime_addtime,
++ (unsigned)pfkey_lifetime->sadb_lifetime_usetime,
++ pfkey_lifetime->sadb_x_lifetime_packets);
++errlab:
++ return error;
++}
++
++DEBUG_NO_STATIC int
++pfkey_address_parse(struct sadb_ext *pfkey_ext)
++{
++ int error = 0;
++ int saddr_len = 0;
++ struct sadb_address *pfkey_address = (struct sadb_address *)pfkey_ext;
++ struct sockaddr* s = (struct sockaddr*)((char*)pfkey_address + sizeof(*pfkey_address));
++ char ipaddr_txt[ADDRTOT_BUF];
++
++ /* sanity checks... */
++ if(!pfkey_address) {
++ ERROR(
++ "pfkey_address_parse: "
++ "NULL pointer passed in.\n");
++ SENDERR(EINVAL);
++ }
++
++ if(pfkey_address->sadb_address_len <
++ (sizeof(struct sadb_address) + sizeof(struct sockaddr))/
++ IPSEC_PFKEYv2_ALIGN) {
++ ERROR("pfkey_address_parse: "
++ "size wrong 1 ext_len=%d, adr_ext_len=%d, saddr_len=%d.\n",
++ pfkey_address->sadb_address_len,
++ (int)sizeof(struct sadb_address),
++ (int)sizeof(struct sockaddr));
++ SENDERR(EINVAL);
++ }
++
++ if(pfkey_address->sadb_address_reserved) {
++ ERROR("pfkey_address_parse: "
++ "res=%d, must be zero.\n",
++ pfkey_address->sadb_address_reserved);
++ SENDERR(EINVAL);
++ }
++
++ switch(pfkey_address->sadb_address_exttype) {
++ case SADB_EXT_ADDRESS_SRC:
++ case SADB_EXT_ADDRESS_DST:
++ case SADB_EXT_ADDRESS_PROXY:
++ case SADB_X_EXT_ADDRESS_DST2:
++ case SADB_X_EXT_ADDRESS_SRC_FLOW:
++ case SADB_X_EXT_ADDRESS_DST_FLOW:
++ case SADB_X_EXT_ADDRESS_SRC_MASK:
++ case SADB_X_EXT_ADDRESS_DST_MASK:
++#ifdef NAT_TRAVERSAL
++ case SADB_X_EXT_NAT_T_OA:
++#endif
++ break;
++ default:
++ ERROR(
++ "pfkey_address_parse: "
++ "unexpected ext_type=%d.\n",
++ pfkey_address->sadb_address_exttype);
++ SENDERR(EINVAL);
++ }
++
++ switch(s->sa_family) {
++ case AF_INET:
++ saddr_len = sizeof(struct sockaddr_in);
++ sprintf(ipaddr_txt, "%d.%d.%d.%d"
++ , (((struct sockaddr_in*)s)->sin_addr.s_addr >> 0) & 0xFF
++ , (((struct sockaddr_in*)s)->sin_addr.s_addr >> 8) & 0xFF
++ , (((struct sockaddr_in*)s)->sin_addr.s_addr >> 16) & 0xFF
++ , (((struct sockaddr_in*)s)->sin_addr.s_addr >> 24) & 0xFF);
++ DEBUGGING(PF_KEY_DEBUG_PARSE_STRUCT,
++ "pfkey_address_parse: "
++ "found exttype=%u(%s) family=%d(AF_INET) address=%s proto=%u port=%u.\n",
++ pfkey_address->sadb_address_exttype,
++ pfkey_v2_sadb_ext_string(pfkey_address->sadb_address_exttype),
++ s->sa_family,
++ ipaddr_txt,
++ pfkey_address->sadb_address_proto,
++ ntohs(((struct sockaddr_in*)s)->sin_port));
++ break;
++ case AF_INET6:
++ saddr_len = sizeof(struct sockaddr_in6);
++ sprintf(ipaddr_txt, "%x:%x:%x:%x:%x:%x:%x:%x"
++ , ntohs(((struct sockaddr_in6*)s)->sin6_addr.s6_addr16[0])
++ , ntohs(((struct sockaddr_in6*)s)->sin6_addr.s6_addr16[1])
++ , ntohs(((struct sockaddr_in6*)s)->sin6_addr.s6_addr16[2])
++ , ntohs(((struct sockaddr_in6*)s)->sin6_addr.s6_addr16[3])
++ , ntohs(((struct sockaddr_in6*)s)->sin6_addr.s6_addr16[4])
++ , ntohs(((struct sockaddr_in6*)s)->sin6_addr.s6_addr16[5])
++ , ntohs(((struct sockaddr_in6*)s)->sin6_addr.s6_addr16[6])
++ , ntohs(((struct sockaddr_in6*)s)->sin6_addr.s6_addr16[7]));
++ DEBUGGING(PF_KEY_DEBUG_PARSE_STRUCT,
++ "pfkey_address_parse: "
++ "found exttype=%u(%s) family=%d(AF_INET6) address=%s proto=%u port=%u.\n",
++ pfkey_address->sadb_address_exttype,
++ pfkey_v2_sadb_ext_string(pfkey_address->sadb_address_exttype),
++ s->sa_family,
++ ipaddr_txt,
++ pfkey_address->sadb_address_proto,
++ ((struct sockaddr_in6*)s)->sin6_port);
++ break;
++ default:
++ ERROR(
++ "pfkey_address_parse: "
++ "s->sa_family=%d not supported.\n",
++ s->sa_family);
++ SENDERR(EPFNOSUPPORT);
++ }
++
++ if(pfkey_address->sadb_address_len !=
++ DIVUP(sizeof(struct sadb_address) + saddr_len, IPSEC_PFKEYv2_ALIGN)) {
++ ERROR(
++ "pfkey_address_parse: "
++ "size wrong 2 ext_len=%d, adr_ext_len=%d, saddr_len=%d.\n",
++ pfkey_address->sadb_address_len,
++ (int)sizeof(struct sadb_address),
++ saddr_len);
++ SENDERR(EINVAL);
++ }
++
++ if(pfkey_address->sadb_address_prefixlen != 0) {
++ ERROR(
++ "pfkey_address_parse: "
++ "address prefixes not supported yet.\n");
++ SENDERR(EAFNOSUPPORT); /* not supported yet */
++ }
++
++ /* XXX check if port!=0 */
++
++ DEBUGGING(PF_KEY_DEBUG_PARSE_FLOW,
++ "pfkey_address_parse: successful.\n");
++ errlab:
++ return error;
++}
++
++DEBUG_NO_STATIC int
++pfkey_key_parse(struct sadb_ext *pfkey_ext)
++{
++ int error = 0;
++ struct sadb_key *pfkey_key = (struct sadb_key *)pfkey_ext;
++
++ /* sanity checks... */
++
++ if(!pfkey_key) {
++ ERROR(
++ "pfkey_key_parse: "
++ "NULL pointer passed in.\n");
++ SENDERR(EINVAL);
++ }
++
++ if(pfkey_key->sadb_key_len < sizeof(struct sadb_key) / IPSEC_PFKEYv2_ALIGN) {
++ ERROR(
++ "pfkey_key_parse: "
++ "size wrong ext_len=%d, key_ext_len=%d.\n",
++ pfkey_key->sadb_key_len,
++ (int)sizeof(struct sadb_key));
++ SENDERR(EINVAL);
++ }
++
++ if(!pfkey_key->sadb_key_bits) {
++ ERROR(
++ "pfkey_key_parse: "
++ "key length set to zero, must be non-zero.\n");
++ SENDERR(EINVAL);
++ }
++
++ if(pfkey_key->sadb_key_len !=
++ DIVUP(sizeof(struct sadb_key) * OCTETBITS + pfkey_key->sadb_key_bits,
++ PFKEYBITS)) {
++ ERROR(
++ "pfkey_key_parse: "
++ "key length=%d does not agree with extension length=%d.\n",
++ pfkey_key->sadb_key_bits,
++ pfkey_key->sadb_key_len);
++ SENDERR(EINVAL);
++ }
++
++ if(pfkey_key->sadb_key_reserved) {
++ ERROR(
++ "pfkey_key_parse: "
++ "res=%d, must be zero.\n",
++ pfkey_key->sadb_key_reserved);
++ SENDERR(EINVAL);
++ }
++
++ if(! ( (pfkey_key->sadb_key_exttype == SADB_EXT_KEY_AUTH) ||
++ (pfkey_key->sadb_key_exttype == SADB_EXT_KEY_ENCRYPT))) {
++ ERROR(
++ "pfkey_key_parse: "
++ "expecting extension type AUTH or ENCRYPT, got %d.\n",
++ pfkey_key->sadb_key_exttype);
++ SENDERR(EINVAL);
++ }
++
++ DEBUGGING(PF_KEY_DEBUG_PARSE_STRUCT,
++ "pfkey_key_parse: "
++ "success, found len=%d exttype=%d(%s) bits=%d reserved=%d.\n",
++ pfkey_key->sadb_key_len,
++ pfkey_key->sadb_key_exttype,
++ pfkey_v2_sadb_ext_string(pfkey_key->sadb_key_exttype),
++ pfkey_key->sadb_key_bits,
++ pfkey_key->sadb_key_reserved);
++
++errlab:
++ return error;
++}
++
++DEBUG_NO_STATIC int
++pfkey_ident_parse(struct sadb_ext *pfkey_ext)
++{
++ int error = 0;
++ struct sadb_ident *pfkey_ident = (struct sadb_ident *)pfkey_ext;
++
++ /* sanity checks... */
++ if(pfkey_ident->sadb_ident_len < sizeof(struct sadb_ident) / IPSEC_PFKEYv2_ALIGN) {
++ ERROR(
++ "pfkey_ident_parse: "
++ "size wrong ext_len=%d, key_ext_len=%d.\n",
++ pfkey_ident->sadb_ident_len,
++ (int)sizeof(struct sadb_ident));
++ SENDERR(EINVAL);
++ }
++
++ if(pfkey_ident->sadb_ident_type > SADB_IDENTTYPE_MAX) {
++ ERROR(
++ "pfkey_ident_parse: "
++ "ident_type=%d out of range, must be less than %d.\n",
++ pfkey_ident->sadb_ident_type,
++ SADB_IDENTTYPE_MAX);
++ SENDERR(EINVAL);
++ }
++
++ if(pfkey_ident->sadb_ident_reserved) {
++ ERROR(
++ "pfkey_ident_parse: "
++ "res=%d, must be zero.\n",
++ pfkey_ident->sadb_ident_reserved);
++ SENDERR(EINVAL);
++ }
++
++ /* string terminator/padding must be zero */
++ if(pfkey_ident->sadb_ident_len > sizeof(struct sadb_ident) / IPSEC_PFKEYv2_ALIGN) {
++ if(*((char*)pfkey_ident + pfkey_ident->sadb_ident_len * IPSEC_PFKEYv2_ALIGN - 1)) {
++ ERROR(
++ "pfkey_ident_parse: "
++ "string padding must be zero, last is 0x%02x.\n",
++ *((char*)pfkey_ident +
++ pfkey_ident->sadb_ident_len * IPSEC_PFKEYv2_ALIGN - 1));
++ SENDERR(EINVAL);
++ }
++ }
++
++ if( ! ((pfkey_ident->sadb_ident_exttype == SADB_EXT_IDENTITY_SRC) ||
++ (pfkey_ident->sadb_ident_exttype == SADB_EXT_IDENTITY_DST))) {
++ ERROR(
++ "pfkey_key_parse: "
++ "expecting extension type IDENTITY_SRC or IDENTITY_DST, got %d.\n",
++ pfkey_ident->sadb_ident_exttype);
++ SENDERR(EINVAL);
++ }
++
++errlab:
++ return error;
++}
++
++DEBUG_NO_STATIC int
++pfkey_sens_parse(struct sadb_ext *pfkey_ext)
++{
++ int error = 0;
++ struct sadb_sens *pfkey_sens = (struct sadb_sens *)pfkey_ext;
++
++ /* sanity checks... */
++ if(pfkey_sens->sadb_sens_len < sizeof(struct sadb_sens) / IPSEC_PFKEYv2_ALIGN) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_sens_parse: "
++ "size wrong ext_len=%d, key_ext_len=%d.\n",
++ pfkey_sens->sadb_sens_len,
++ (int)sizeof(struct sadb_sens));
++ SENDERR(EINVAL);
++ }
++
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_sens_parse: "
++ "Sorry, I can't parse exttype=%d yet.\n",
++ pfkey_ext->sadb_ext_type);
++#if 0
++ SENDERR(EINVAL); /* don't process these yet */
++#endif
++
++errlab:
++ return error;
++}
++
++DEBUG_NO_STATIC int
++pfkey_prop_parse(struct sadb_ext *pfkey_ext)
++{
++ int error = 0;
++ int i, num_comb;
++ struct sadb_prop *pfkey_prop = (struct sadb_prop *)pfkey_ext;
++ struct sadb_comb *pfkey_comb = (struct sadb_comb *)((char*)pfkey_ext + sizeof(struct sadb_prop));
++
++ /* sanity checks... */
++ if((pfkey_prop->sadb_prop_len < sizeof(struct sadb_prop) / IPSEC_PFKEYv2_ALIGN) ||
++ (((pfkey_prop->sadb_prop_len * IPSEC_PFKEYv2_ALIGN) - sizeof(struct sadb_prop)) % sizeof(struct sadb_comb))) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_prop_parse: "
++ "size wrong ext_len=%d, prop_ext_len=%d comb_ext_len=%d.\n",
++ pfkey_prop->sadb_prop_len,
++ (int)sizeof(struct sadb_prop),
++ (int)sizeof(struct sadb_comb));
++ SENDERR(EINVAL);
++ }
++
++ if(pfkey_prop->sadb_prop_replay > 64) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_prop_parse: "
++ "replay window size: %d -- must be 0 <= size <= 64\n",
++ pfkey_prop->sadb_prop_replay);
++ SENDERR(EINVAL);
++ }
++
++ for(i=0; i<3; i++) {
++ if(pfkey_prop->sadb_prop_reserved[i]) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_prop_parse: "
++ "res[%d]=%d, must be zero.\n",
++ i, pfkey_prop->sadb_prop_reserved[i]);
++ SENDERR(EINVAL);
++ }
++ }
++
++ num_comb = ((pfkey_prop->sadb_prop_len * IPSEC_PFKEYv2_ALIGN) - sizeof(struct sadb_prop)) / sizeof(struct sadb_comb);
++
++ for(i = 0; i < num_comb; i++) {
++ if(pfkey_comb->sadb_comb_auth > SADB_AALG_MAX) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_prop_parse: "
++ "pfkey_comb[%d]->sadb_comb_auth=%d > SADB_AALG_MAX=%d.\n",
++ i,
++ pfkey_comb->sadb_comb_auth,
++ SADB_AALG_MAX);
++ SENDERR(EINVAL);
++ }
++
++ if(pfkey_comb->sadb_comb_auth) {
++ if(!pfkey_comb->sadb_comb_auth_minbits) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_prop_parse: "
++ "pfkey_comb[%d]->sadb_comb_auth_minbits=0, fatal.\n",
++ i);
++ SENDERR(EINVAL);
++ }
++ if(!pfkey_comb->sadb_comb_auth_maxbits) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_prop_parse: "
++ "pfkey_comb[%d]->sadb_comb_auth_maxbits=0, fatal.\n",
++ i);
++ SENDERR(EINVAL);
++ }
++ if(pfkey_comb->sadb_comb_auth_minbits > pfkey_comb->sadb_comb_auth_maxbits) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_prop_parse: "
++ "pfkey_comb[%d]->sadb_comb_auth_minbits=%d > maxbits=%d, fatal.\n",
++ i,
++ pfkey_comb->sadb_comb_auth_minbits,
++ pfkey_comb->sadb_comb_auth_maxbits);
++ SENDERR(EINVAL);
++ }
++ } else {
++ if(pfkey_comb->sadb_comb_auth_minbits) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_prop_parse: "
++ "pfkey_comb[%d]->sadb_comb_auth_minbits=%d != 0, fatal.\n",
++ i,
++ pfkey_comb->sadb_comb_auth_minbits);
++ SENDERR(EINVAL);
++ }
++ if(pfkey_comb->sadb_comb_auth_maxbits) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_prop_parse: "
++ "pfkey_comb[%d]->sadb_comb_auth_maxbits=%d != 0, fatal.\n",
++ i,
++ pfkey_comb->sadb_comb_auth_maxbits);
++ SENDERR(EINVAL);
++ }
++ }
++
++ if(pfkey_comb->sadb_comb_encrypt > SADB_EALG_MAX) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_comb_parse: "
++ "pfkey_comb[%d]->sadb_comb_encrypt=%d > SADB_EALG_MAX=%d.\n",
++ i,
++ pfkey_comb->sadb_comb_encrypt,
++ SADB_EALG_MAX);
++ SENDERR(EINVAL);
++ }
++
++ if(pfkey_comb->sadb_comb_encrypt) {
++ if(!pfkey_comb->sadb_comb_encrypt_minbits) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_prop_parse: "
++ "pfkey_comb[%d]->sadb_comb_encrypt_minbits=0, fatal.\n",
++ i);
++ SENDERR(EINVAL);
++ }
++ if(!pfkey_comb->sadb_comb_encrypt_maxbits) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_prop_parse: "
++ "pfkey_comb[%d]->sadb_comb_encrypt_maxbits=0, fatal.\n",
++ i);
++ SENDERR(EINVAL);
++ }
++ if(pfkey_comb->sadb_comb_encrypt_minbits > pfkey_comb->sadb_comb_encrypt_maxbits) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_prop_parse: "
++ "pfkey_comb[%d]->sadb_comb_encrypt_minbits=%d > maxbits=%d, fatal.\n",
++ i,
++ pfkey_comb->sadb_comb_encrypt_minbits,
++ pfkey_comb->sadb_comb_encrypt_maxbits);
++ SENDERR(EINVAL);
++ }
++ } else {
++ if(pfkey_comb->sadb_comb_encrypt_minbits) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_prop_parse: "
++ "pfkey_comb[%d]->sadb_comb_encrypt_minbits=%d != 0, fatal.\n",
++ i,
++ pfkey_comb->sadb_comb_encrypt_minbits);
++ SENDERR(EINVAL);
++ }
++ if(pfkey_comb->sadb_comb_encrypt_maxbits) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_prop_parse: "
++ "pfkey_comb[%d]->sadb_comb_encrypt_maxbits=%d != 0, fatal.\n",
++ i,
++ pfkey_comb->sadb_comb_encrypt_maxbits);
++ SENDERR(EINVAL);
++ }
++ }
++
++ /* XXX do sanity check on flags */
++
++ if(pfkey_comb->sadb_comb_hard_allocations && pfkey_comb->sadb_comb_soft_allocations > pfkey_comb->sadb_comb_hard_allocations) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_prop_parse: "
++ "pfkey_comb[%d]->sadb_comb_soft_allocations=%d > hard_allocations=%d, fatal.\n",
++ i,
++ pfkey_comb->sadb_comb_soft_allocations,
++ pfkey_comb->sadb_comb_hard_allocations);
++ SENDERR(EINVAL);
++ }
++
++ if(pfkey_comb->sadb_comb_hard_bytes && pfkey_comb->sadb_comb_soft_bytes > pfkey_comb->sadb_comb_hard_bytes) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_prop_parse: "
++ "pfkey_comb[%d]->sadb_comb_soft_bytes=%Ld > hard_bytes=%Ld, fatal.\n",
++ i,
++ (unsigned long long int)pfkey_comb->sadb_comb_soft_bytes,
++ (unsigned long long int)pfkey_comb->sadb_comb_hard_bytes);
++ SENDERR(EINVAL);
++ }
++
++ if(pfkey_comb->sadb_comb_hard_addtime && pfkey_comb->sadb_comb_soft_addtime > pfkey_comb->sadb_comb_hard_addtime) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_prop_parse: "
++ "pfkey_comb[%d]->sadb_comb_soft_addtime=%Ld > hard_addtime=%Ld, fatal.\n",
++ i,
++ (unsigned long long int)pfkey_comb->sadb_comb_soft_addtime,
++ (unsigned long long int)pfkey_comb->sadb_comb_hard_addtime);
++ SENDERR(EINVAL);
++ }
++
++ if(pfkey_comb->sadb_comb_hard_usetime && pfkey_comb->sadb_comb_soft_usetime > pfkey_comb->sadb_comb_hard_usetime) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_prop_parse: "
++ "pfkey_comb[%d]->sadb_comb_soft_usetime=%Ld > hard_usetime=%Ld, fatal.\n",
++ i,
++ (unsigned long long int)pfkey_comb->sadb_comb_soft_usetime,
++ (unsigned long long int)pfkey_comb->sadb_comb_hard_usetime);
++ SENDERR(EINVAL);
++ }
++
++ if(pfkey_comb->sadb_x_comb_hard_packets && pfkey_comb->sadb_x_comb_soft_packets > pfkey_comb->sadb_x_comb_hard_packets) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_prop_parse: "
++ "pfkey_comb[%d]->sadb_x_comb_soft_packets=%d > hard_packets=%d, fatal.\n",
++ i,
++ pfkey_comb->sadb_x_comb_soft_packets,
++ pfkey_comb->sadb_x_comb_hard_packets);
++ SENDERR(EINVAL);
++ }
++
++ if(pfkey_comb->sadb_comb_reserved) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_prop_parse: "
++ "comb[%d].res=%d, must be zero.\n",
++ i,
++ pfkey_comb->sadb_comb_reserved);
++ SENDERR(EINVAL);
++ }
++ pfkey_comb++;
++ }
++
++errlab:
++ return error;
++}
++
++DEBUG_NO_STATIC int
++pfkey_supported_parse(struct sadb_ext *pfkey_ext)
++{
++ int error = 0;
++ unsigned int i, num_alg;
++ struct sadb_supported *pfkey_supported = (struct sadb_supported *)pfkey_ext;
++ struct sadb_alg *pfkey_alg = (struct sadb_alg*)((char*)pfkey_ext + sizeof(struct sadb_supported));
++
++ /* sanity checks... */
++ if((pfkey_supported->sadb_supported_len <
++ sizeof(struct sadb_supported) / IPSEC_PFKEYv2_ALIGN) ||
++ (((pfkey_supported->sadb_supported_len * IPSEC_PFKEYv2_ALIGN) -
++ sizeof(struct sadb_supported)) % sizeof(struct sadb_alg))) {
++
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_supported_parse: "
++ "size wrong ext_len=%d, supported_ext_len=%d alg_ext_len=%d.\n",
++ pfkey_supported->sadb_supported_len,
++ (int)sizeof(struct sadb_supported),
++ (int)sizeof(struct sadb_alg));
++ SENDERR(EINVAL);
++ }
++
++ if(pfkey_supported->sadb_supported_reserved) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_supported_parse: "
++ "res=%d, must be zero.\n",
++ pfkey_supported->sadb_supported_reserved);
++ SENDERR(EINVAL);
++ }
++
++ num_alg = ((pfkey_supported->sadb_supported_len * IPSEC_PFKEYv2_ALIGN) - sizeof(struct sadb_supported)) / sizeof(struct sadb_alg);
++
++ for(i = 0; i < num_alg; i++) {
++ /* process algo description */
++ if(pfkey_alg->sadb_alg_reserved) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_supported_parse: "
++ "alg[%d], id=%d, ivlen=%d, minbits=%d, maxbits=%d, res=%d, must be zero.\n",
++ i,
++ pfkey_alg->sadb_alg_id,
++ pfkey_alg->sadb_alg_ivlen,
++ pfkey_alg->sadb_alg_minbits,
++ pfkey_alg->sadb_alg_maxbits,
++ pfkey_alg->sadb_alg_reserved);
++ SENDERR(EINVAL);
++ }
++
++ /* XXX can alg_id auth/enc be determined from info given?
++ Yes, but OpenBSD's method does not iteroperate with rfc2367.
++ rgb, 2000-04-06 */
++
++ switch(pfkey_supported->sadb_supported_exttype) {
++ case SADB_EXT_SUPPORTED_AUTH:
++ if(pfkey_alg->sadb_alg_id > SADB_AALG_MAX) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_supported_parse: "
++ "alg[%d], alg_id=%d > SADB_AALG_MAX=%d, fatal.\n",
++ i,
++ pfkey_alg->sadb_alg_id,
++ SADB_AALG_MAX);
++ SENDERR(EINVAL);
++ }
++ break;
++ case SADB_EXT_SUPPORTED_ENCRYPT:
++ if(pfkey_alg->sadb_alg_id > SADB_EALG_MAX) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_supported_parse: "
++ "alg[%d], alg_id=%d > SADB_EALG_MAX=%d, fatal.\n",
++ i,
++ pfkey_alg->sadb_alg_id,
++ SADB_EALG_MAX);
++ SENDERR(EINVAL);
++ }
++ break;
++ default:
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_supported_parse: "
++ "alg[%d], alg_id=%d > SADB_EALG_MAX=%d, fatal.\n",
++ i,
++ pfkey_alg->sadb_alg_id,
++ SADB_EALG_MAX);
++ SENDERR(EINVAL);
++ }
++ pfkey_alg++;
++ }
++
++ errlab:
++ return error;
++}
++
++DEBUG_NO_STATIC int
++pfkey_spirange_parse(struct sadb_ext *pfkey_ext)
++{
++ int error = 0;
++ struct sadb_spirange *pfkey_spirange = (struct sadb_spirange *)pfkey_ext;
++
++ /* sanity checks... */
++ if(pfkey_spirange->sadb_spirange_len !=
++ sizeof(struct sadb_spirange) / IPSEC_PFKEYv2_ALIGN) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_spirange_parse: "
++ "size wrong ext_len=%d, key_ext_len=%d.\n",
++ pfkey_spirange->sadb_spirange_len,
++ (int)sizeof(struct sadb_spirange));
++ SENDERR(EINVAL);
++ }
++
++ if(pfkey_spirange->sadb_spirange_reserved) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_spirange_parse: "
++ "reserved=%d must be set to zero.\n",
++ pfkey_spirange->sadb_spirange_reserved);
++ SENDERR(EINVAL);
++ }
++
++ if(ntohl(pfkey_spirange->sadb_spirange_max) < ntohl(pfkey_spirange->sadb_spirange_min)) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_spirange_parse: "
++ "minspi=%08x must be < maxspi=%08x.\n",
++ ntohl(pfkey_spirange->sadb_spirange_min),
++ ntohl(pfkey_spirange->sadb_spirange_max));
++ SENDERR(EINVAL);
++ }
++
++ if(ntohl(pfkey_spirange->sadb_spirange_min) <= 255) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_spirange_parse: "
++ "minspi=%08x must be > 255.\n",
++ ntohl(pfkey_spirange->sadb_spirange_min));
++ SENDERR(EEXIST);
++ }
++
++ DEBUGGING(PF_KEY_DEBUG_PARSE_STRUCT,
++ "pfkey_spirange_parse: "
++ "ext_len=%u ext_type=%u(%s) min=%u max=%u res=%u.\n",
++ pfkey_spirange->sadb_spirange_len,
++ pfkey_spirange->sadb_spirange_exttype,
++ pfkey_v2_sadb_ext_string(pfkey_spirange->sadb_spirange_exttype),
++ pfkey_spirange->sadb_spirange_min,
++ pfkey_spirange->sadb_spirange_max,
++ pfkey_spirange->sadb_spirange_reserved);
++ errlab:
++ return error;
++}
++
++DEBUG_NO_STATIC int
++pfkey_x_kmprivate_parse(struct sadb_ext *pfkey_ext)
++{
++ int error = 0;
++ struct sadb_x_kmprivate *pfkey_x_kmprivate = (struct sadb_x_kmprivate *)pfkey_ext;
++
++ /* sanity checks... */
++ if(pfkey_x_kmprivate->sadb_x_kmprivate_len <
++ sizeof(struct sadb_x_kmprivate) / IPSEC_PFKEYv2_ALIGN) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_x_kmprivate_parse: "
++ "size wrong ext_len=%d, key_ext_len=%d.\n",
++ pfkey_x_kmprivate->sadb_x_kmprivate_len,
++ (int)sizeof(struct sadb_x_kmprivate));
++ SENDERR(EINVAL);
++ }
++
++ if(pfkey_x_kmprivate->sadb_x_kmprivate_reserved) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_x_kmprivate_parse: "
++ "reserved=%d must be set to zero.\n",
++ pfkey_x_kmprivate->sadb_x_kmprivate_reserved);
++ SENDERR(EINVAL);
++ }
++
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_x_kmprivate_parse: "
++ "Sorry, I can't parse exttype=%d yet.\n",
++ pfkey_ext->sadb_ext_type);
++ SENDERR(EINVAL); /* don't process these yet */
++
++errlab:
++ return error;
++}
++
++DEBUG_NO_STATIC int
++pfkey_x_satype_parse(struct sadb_ext *pfkey_ext)
++{
++ int error = 0;
++ int i;
++ struct sadb_x_satype *pfkey_x_satype = (struct sadb_x_satype *)pfkey_ext;
++
++ DEBUGGING(PF_KEY_DEBUG_PARSE_FLOW,
++ "pfkey_x_satype_parse: enter\n");
++ /* sanity checks... */
++ if(pfkey_x_satype->sadb_x_satype_len !=
++ sizeof(struct sadb_x_satype) / IPSEC_PFKEYv2_ALIGN) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_x_satype_parse: "
++ "size wrong ext_len=%d, key_ext_len=%d.\n",
++ pfkey_x_satype->sadb_x_satype_len,
++ (int)sizeof(struct sadb_x_satype));
++ SENDERR(EINVAL);
++ }
++
++ if(!pfkey_x_satype->sadb_x_satype_satype) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_x_satype_parse: "
++ "satype is zero, must be non-zero.\n");
++ SENDERR(EINVAL);
++ }
++
++ if(pfkey_x_satype->sadb_x_satype_satype > SADB_SATYPE_MAX) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_x_satype_parse: "
++ "satype %d > max %d, invalid.\n",
++ pfkey_x_satype->sadb_x_satype_satype, SADB_SATYPE_MAX);
++ SENDERR(EINVAL);
++ }
++
++ if(!(satype2proto(pfkey_x_satype->sadb_x_satype_satype))) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_x_satype_parse: "
++ "proto lookup from satype=%d failed.\n",
++ pfkey_x_satype->sadb_x_satype_satype);
++ SENDERR(EINVAL);
++ }
++
++ for(i = 0; i < 3; i++) {
++ if(pfkey_x_satype->sadb_x_satype_reserved[i]) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_x_satype_parse: "
++ "reserved[%d]=%d must be set to zero.\n",
++ i, pfkey_x_satype->sadb_x_satype_reserved[i]);
++ SENDERR(EINVAL);
++ }
++ }
++
++ DEBUGGING(PF_KEY_DEBUG_PARSE_STRUCT,
++ "pfkey_x_satype_parse: "
++ "len=%u ext=%u(%s) satype=%u(%s) res=%u,%u,%u.\n",
++ pfkey_x_satype->sadb_x_satype_len,
++ pfkey_x_satype->sadb_x_satype_exttype,
++ pfkey_v2_sadb_ext_string(pfkey_x_satype->sadb_x_satype_exttype),
++ pfkey_x_satype->sadb_x_satype_satype,
++ satype2name(pfkey_x_satype->sadb_x_satype_satype),
++ pfkey_x_satype->sadb_x_satype_reserved[0],
++ pfkey_x_satype->sadb_x_satype_reserved[1],
++ pfkey_x_satype->sadb_x_satype_reserved[2]);
++errlab:
++ return error;
++}
++
++DEBUG_NO_STATIC int
++pfkey_x_ext_debug_parse(struct sadb_ext *pfkey_ext)
++{
++ int error = 0;
++ int i;
++ struct sadb_x_debug *pfkey_x_debug = (struct sadb_x_debug *)pfkey_ext;
++
++ DEBUGGING(PF_KEY_DEBUG_PARSE_FLOW,
++ "pfkey_x_debug_parse: enter\n");
++ /* sanity checks... */
++ if(pfkey_x_debug->sadb_x_debug_len !=
++ sizeof(struct sadb_x_debug) / IPSEC_PFKEYv2_ALIGN) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_x_debug_parse: "
++ "size wrong ext_len=%d, key_ext_len=%d.\n",
++ pfkey_x_debug->sadb_x_debug_len,
++ (int)sizeof(struct sadb_x_debug));
++ SENDERR(EINVAL);
++ }
++
++ for(i = 0; i < 4; i++) {
++ if(pfkey_x_debug->sadb_x_debug_reserved[i]) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_x_debug_parse: "
++ "reserved[%d]=%d must be set to zero.\n",
++ i, pfkey_x_debug->sadb_x_debug_reserved[i]);
++ SENDERR(EINVAL);
++ }
++ }
++
++errlab:
++ return error;
++}
++
++DEBUG_NO_STATIC int
++pfkey_x_ext_protocol_parse(struct sadb_ext *pfkey_ext)
++{
++ int error = 0;
++ struct sadb_protocol *p = (struct sadb_protocol *)pfkey_ext;
++
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, "pfkey_x_protocol_parse:\n");
++ /* sanity checks... */
++
++ if (p->sadb_protocol_len != sizeof(*p)/IPSEC_PFKEYv2_ALIGN) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_x_protocol_parse: size wrong ext_len=%d, key_ext_len=%d.\n",
++ p->sadb_protocol_len, (int)sizeof(*p));
++ SENDERR(EINVAL);
++ }
++
++ if (p->sadb_protocol_reserved2 != 0) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_protocol_parse: res=%d, must be zero.\n",
++ p->sadb_protocol_reserved2);
++ SENDERR(EINVAL);
++ }
++
++ errlab:
++ return error;
++}
++
++#ifdef NAT_TRAVERSAL
++DEBUG_NO_STATIC int
++pfkey_x_ext_nat_t_type_parse(struct sadb_ext *pfkey_ext)
++{
++ return 0;
++}
++DEBUG_NO_STATIC int
++pfkey_x_ext_nat_t_port_parse(struct sadb_ext *pfkey_ext)
++{
++ return 0;
++}
++#endif
++
++#define DEFINEPARSER(NAME) static struct pf_key_ext_parsers_def NAME##_def={NAME, #NAME};
++
++DEFINEPARSER(pfkey_sa_parse);
++DEFINEPARSER(pfkey_lifetime_parse);
++DEFINEPARSER(pfkey_address_parse);
++DEFINEPARSER(pfkey_key_parse);
++DEFINEPARSER(pfkey_ident_parse);
++DEFINEPARSER(pfkey_sens_parse);
++DEFINEPARSER(pfkey_prop_parse);
++DEFINEPARSER(pfkey_supported_parse);
++DEFINEPARSER(pfkey_spirange_parse);
++DEFINEPARSER(pfkey_x_kmprivate_parse);
++DEFINEPARSER(pfkey_x_satype_parse);
++DEFINEPARSER(pfkey_x_ext_debug_parse);
++DEFINEPARSER(pfkey_x_ext_protocol_parse);
++#ifdef NAT_TRAVERSAL
++DEFINEPARSER(pfkey_x_ext_nat_t_type_parse);
++DEFINEPARSER(pfkey_x_ext_nat_t_port_parse);
++#endif
++
++struct pf_key_ext_parsers_def *ext_default_parsers[]=
++{
++ NULL, /* pfkey_msg_parse, */
++ &pfkey_sa_parse_def,
++ &pfkey_lifetime_parse_def,
++ &pfkey_lifetime_parse_def,
++ &pfkey_lifetime_parse_def,
++ &pfkey_address_parse_def,
++ &pfkey_address_parse_def,
++ &pfkey_address_parse_def,
++ &pfkey_key_parse_def,
++ &pfkey_key_parse_def,
++ &pfkey_ident_parse_def,
++ &pfkey_ident_parse_def,
++ &pfkey_sens_parse_def,
++ &pfkey_prop_parse_def,
++ &pfkey_supported_parse_def,
++ &pfkey_supported_parse_def,
++ &pfkey_spirange_parse_def,
++ &pfkey_x_kmprivate_parse_def,
++ &pfkey_x_satype_parse_def,
++ &pfkey_sa_parse_def,
++ &pfkey_address_parse_def,
++ &pfkey_address_parse_def,
++ &pfkey_address_parse_def,
++ &pfkey_address_parse_def,
++ &pfkey_address_parse_def,
++ &pfkey_x_ext_debug_parse_def,
++ &pfkey_x_ext_protocol_parse_def
++#ifdef NAT_TRAVERSAL
++ ,
++ &pfkey_x_ext_nat_t_type_parse_def,
++ &pfkey_x_ext_nat_t_port_parse_def,
++ &pfkey_x_ext_nat_t_port_parse_def,
++ &pfkey_address_parse_def
++#endif
++};
++
++int
++pfkey_msg_parse(struct sadb_msg *pfkey_msg,
++ struct pf_key_ext_parsers_def *ext_parsers[],
++ struct sadb_ext *extensions[],
++ int dir)
++{
++ int error = 0;
++ int remain;
++ struct sadb_ext *pfkey_ext;
++ int extensions_seen = 0;
++
++ DEBUGGING(PF_KEY_DEBUG_PARSE_STRUCT,
++ "pfkey_msg_parse: "
++ "parsing message ver=%d, type=%d(%s), errno=%d, satype=%d(%s), len=%d, res=%d, seq=%d, pid=%d.\n",
++ pfkey_msg->sadb_msg_version,
++ pfkey_msg->sadb_msg_type,
++ pfkey_v2_sadb_type_string(pfkey_msg->sadb_msg_type),
++ pfkey_msg->sadb_msg_errno,
++ pfkey_msg->sadb_msg_satype,
++ satype2name(pfkey_msg->sadb_msg_satype),
++ pfkey_msg->sadb_msg_len,
++ pfkey_msg->sadb_msg_reserved,
++ pfkey_msg->sadb_msg_seq,
++ pfkey_msg->sadb_msg_pid);
++
++ if(ext_parsers == NULL) ext_parsers = ext_default_parsers;
++
++ pfkey_extensions_init(extensions);
++
++ remain = pfkey_msg->sadb_msg_len;
++ remain -= sizeof(struct sadb_msg) / IPSEC_PFKEYv2_ALIGN;
++
++ pfkey_ext = (struct sadb_ext*)((char*)pfkey_msg +
++ sizeof(struct sadb_msg));
++
++ extensions[0] = (struct sadb_ext *) pfkey_msg;
++
++
++ if(pfkey_msg->sadb_msg_version != PF_KEY_V2) {
++ ERROR("pfkey_msg_parse: "
++ "not PF_KEY_V2 msg, found %d, should be %d.\n",
++ pfkey_msg->sadb_msg_version,
++ PF_KEY_V2);
++ SENDERR(EINVAL);
++ }
++
++ if(!pfkey_msg->sadb_msg_type) {
++ ERROR("pfkey_msg_parse: "
++ "msg type not set, must be non-zero..\n");
++ SENDERR(EINVAL);
++ }
++
++ if(pfkey_msg->sadb_msg_type > SADB_MAX) {
++ ERROR("pfkey_msg_parse: "
++ "msg type=%d > max=%d.\n",
++ pfkey_msg->sadb_msg_type,
++ SADB_MAX);
++ SENDERR(EINVAL);
++ }
++
++ switch(pfkey_msg->sadb_msg_type) {
++ case SADB_GETSPI:
++ case SADB_UPDATE:
++ case SADB_ADD:
++ case SADB_DELETE:
++ case SADB_GET:
++ case SADB_X_GRPSA:
++ case SADB_X_ADDFLOW:
++ if(!satype2proto(pfkey_msg->sadb_msg_satype)) {
++ ERROR("pfkey_msg_parse: "
++ "satype %d conversion to proto failed for msg_type %d (%s).\n",
++ pfkey_msg->sadb_msg_satype,
++ pfkey_msg->sadb_msg_type,
++ pfkey_v2_sadb_type_string(pfkey_msg->sadb_msg_type));
++ SENDERR(EINVAL);
++ } else {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_msg_parse: "
++ "satype %d(%s) conversion to proto gives %d for msg_type %d(%s).\n",
++ pfkey_msg->sadb_msg_satype,
++ satype2name(pfkey_msg->sadb_msg_satype),
++ satype2proto(pfkey_msg->sadb_msg_satype),
++ pfkey_msg->sadb_msg_type,
++ pfkey_v2_sadb_type_string(pfkey_msg->sadb_msg_type));
++ }
++ case SADB_ACQUIRE:
++ case SADB_REGISTER:
++ case SADB_EXPIRE:
++ if(!pfkey_msg->sadb_msg_satype) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_msg_parse: "
++ "satype is zero, must be non-zero for msg_type %d(%s).\n",
++ pfkey_msg->sadb_msg_type,
++ pfkey_v2_sadb_type_string(pfkey_msg->sadb_msg_type));
++ SENDERR(EINVAL);
++ }
++ default:
++ break;
++ }
++
++ /* errno must not be set in downward messages */
++ /* this is not entirely true... a response to an ACQUIRE could return an error */
++ if((dir == EXT_BITS_IN) && (pfkey_msg->sadb_msg_type != SADB_ACQUIRE) && pfkey_msg->sadb_msg_errno) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_msg_parse: "
++ "errno set to %d.\n",
++ pfkey_msg->sadb_msg_errno);
++ SENDERR(EINVAL);
++ }
++
++ DEBUGGING(PF_KEY_DEBUG_PARSE_FLOW,
++ "pfkey_msg_parse: "
++ "remain=%d, ext_type=%d(%s), ext_len=%d.\n",
++ remain,
++ pfkey_ext->sadb_ext_type,
++ pfkey_v2_sadb_ext_string(pfkey_ext->sadb_ext_type),
++ pfkey_ext->sadb_ext_len);
++
++ DEBUGGING(PF_KEY_DEBUG_PARSE_FLOW,
++ "pfkey_msg_parse: "
++ "extensions permitted=%08x, required=%08x.\n",
++ extensions_bitmaps[dir][EXT_BITS_PERM][pfkey_msg->sadb_msg_type],
++ extensions_bitmaps[dir][EXT_BITS_REQ][pfkey_msg->sadb_msg_type]);
++
++ extensions_seen = 1;
++
++ while( (remain * IPSEC_PFKEYv2_ALIGN) >= sizeof(struct sadb_ext) ) {
++ /* Is there enough message left to support another extension header? */
++ if(remain < pfkey_ext->sadb_ext_len) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_msg_parse: "
++ "remain %d less than ext len %d.\n",
++ remain, pfkey_ext->sadb_ext_len);
++ SENDERR(EINVAL);
++ }
++
++ DEBUGGING(PF_KEY_DEBUG_PARSE_FLOW,
++ "pfkey_msg_parse: "
++ "parsing ext type=%d(%s) remain=%d.\n",
++ pfkey_ext->sadb_ext_type,
++ pfkey_v2_sadb_ext_string(pfkey_ext->sadb_ext_type),
++ remain);
++
++ /* Is the extension header type valid? */
++ if((pfkey_ext->sadb_ext_type > SADB_EXT_MAX) || (!pfkey_ext->sadb_ext_type)) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_msg_parse: "
++ "ext type %d(%s) invalid, SADB_EXT_MAX=%d.\n",
++ pfkey_ext->sadb_ext_type,
++ pfkey_v2_sadb_ext_string(pfkey_ext->sadb_ext_type),
++ SADB_EXT_MAX);
++ SENDERR(EINVAL);
++ }
++
++ /* Have we already seen this type of extension? */
++ if((extensions_seen & ( 1 << pfkey_ext->sadb_ext_type )) != 0)
++ {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_msg_parse: "
++ "ext type %d(%s) already seen.\n",
++ pfkey_ext->sadb_ext_type,
++ pfkey_v2_sadb_ext_string(pfkey_ext->sadb_ext_type));
++ SENDERR(EINVAL);
++ }
++
++ /* Do I even know about this type of extension? */
++ if(ext_parsers[pfkey_ext->sadb_ext_type]==NULL) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_msg_parse: "
++ "ext type %d(%s) unknown, ignoring.\n",
++ pfkey_ext->sadb_ext_type,
++ pfkey_v2_sadb_ext_string(pfkey_ext->sadb_ext_type));
++ goto next_ext;
++ }
++
++ /* Is this type of extension permitted for this type of message? */
++ if(!(extensions_bitmaps[dir][EXT_BITS_PERM][pfkey_msg->sadb_msg_type] &
++ 1<<pfkey_ext->sadb_ext_type)) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_msg_parse: "
++ "ext type %d(%s) not permitted, exts_perm_in=%08x, 1<<type=%08x\n",
++ pfkey_ext->sadb_ext_type,
++ pfkey_v2_sadb_ext_string(pfkey_ext->sadb_ext_type),
++ extensions_bitmaps[dir][EXT_BITS_PERM][pfkey_msg->sadb_msg_type],
++ 1<<pfkey_ext->sadb_ext_type);
++ SENDERR(EINVAL);
++ }
++
++ DEBUGGING(PF_KEY_DEBUG_PARSE_STRUCT,
++ "pfkey_msg_parse: "
++ "remain=%d ext_type=%d(%s) ext_len=%d parsing ext 0p%p with parser %s.\n",
++ remain,
++ pfkey_ext->sadb_ext_type,
++ pfkey_v2_sadb_ext_string(pfkey_ext->sadb_ext_type),
++ pfkey_ext->sadb_ext_len,
++ pfkey_ext,
++ ext_parsers[pfkey_ext->sadb_ext_type]->parser_name);
++
++ /* Parse the extension */
++ if((error =
++ (*ext_parsers[pfkey_ext->sadb_ext_type]->parser)(pfkey_ext))) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_msg_parse: "
++ "extension parsing for type %d(%s) failed with error %d.\n",
++ pfkey_ext->sadb_ext_type,
++ pfkey_v2_sadb_ext_string(pfkey_ext->sadb_ext_type),
++ error);
++ SENDERR(-error);
++ }
++ DEBUGGING(PF_KEY_DEBUG_PARSE_FLOW,
++ "pfkey_msg_parse: "
++ "Extension %d(%s) parsed.\n",
++ pfkey_ext->sadb_ext_type,
++ pfkey_v2_sadb_ext_string(pfkey_ext->sadb_ext_type));
++
++ /* Mark that we have seen this extension and remember the header location */
++ extensions_seen |= ( 1 << pfkey_ext->sadb_ext_type );
++ extensions[pfkey_ext->sadb_ext_type] = pfkey_ext;
++
++ next_ext:
++ /* Calculate how much message remains */
++ remain -= pfkey_ext->sadb_ext_len;
++
++ if(!remain) {
++ break;
++ }
++ /* Find the next extension header */
++ pfkey_ext = (struct sadb_ext*)((char*)pfkey_ext +
++ pfkey_ext->sadb_ext_len * IPSEC_PFKEYv2_ALIGN);
++ }
++
++ if(remain) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_msg_parse: "
++ "unexpected remainder of %d.\n",
++ remain);
++ /* why is there still something remaining? */
++ SENDERR(EINVAL);
++ }
++
++ /* check required extensions */
++ DEBUGGING(PF_KEY_DEBUG_PARSE_STRUCT,
++ "pfkey_msg_parse: "
++ "extensions permitted=%08x, seen=%08x, required=%08x.\n",
++ extensions_bitmaps[dir][EXT_BITS_PERM][pfkey_msg->sadb_msg_type],
++ extensions_seen,
++ extensions_bitmaps[dir][EXT_BITS_REQ][pfkey_msg->sadb_msg_type]);
++
++ /* don't check further if it is an error return message since it
++ may not have a body */
++ if(pfkey_msg->sadb_msg_errno) {
++ SENDERR(-error);
++ }
++
++ if((extensions_seen &
++ extensions_bitmaps[dir][EXT_BITS_REQ][pfkey_msg->sadb_msg_type]) !=
++ extensions_bitmaps[dir][EXT_BITS_REQ][pfkey_msg->sadb_msg_type]) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_msg_parse: "
++ "required extensions missing:%08x.\n",
++ extensions_bitmaps[dir][EXT_BITS_REQ][pfkey_msg->sadb_msg_type] -
++ (extensions_seen &
++ extensions_bitmaps[dir][EXT_BITS_REQ][pfkey_msg->sadb_msg_type]));
++ SENDERR(EINVAL);
++ }
++
++ if((dir == EXT_BITS_IN) && (pfkey_msg->sadb_msg_type == SADB_X_DELFLOW)
++ && ((extensions_seen & SADB_X_EXT_ADDRESS_DELFLOW)
++ != SADB_X_EXT_ADDRESS_DELFLOW)
++ && (((extensions_seen & (1<<SADB_EXT_SA)) != (1<<SADB_EXT_SA))
++ || ((((struct sadb_sa*)extensions[SADB_EXT_SA])->sadb_sa_flags
++ & SADB_X_SAFLAGS_CLEARFLOW)
++ != SADB_X_SAFLAGS_CLEARFLOW))) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_msg_parse: "
++ "required SADB_X_DELFLOW extensions missing: either %08x must be present or %08x must be present with SADB_X_SAFLAGS_CLEARFLOW set.\n",
++ SADB_X_EXT_ADDRESS_DELFLOW
++ - (extensions_seen & SADB_X_EXT_ADDRESS_DELFLOW),
++ (1<<SADB_EXT_SA) - (extensions_seen & (1<<SADB_EXT_SA)));
++ SENDERR(EINVAL);
++ }
++
++ switch(pfkey_msg->sadb_msg_type) {
++ case SADB_ADD:
++ case SADB_UPDATE:
++ /* check maturity */
++ if(((struct sadb_sa*)extensions[SADB_EXT_SA])->sadb_sa_state !=
++ SADB_SASTATE_MATURE) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_msg_parse: "
++ "state=%d for add or update should be MATURE=%d.\n",
++ ((struct sadb_sa*)extensions[SADB_EXT_SA])->sadb_sa_state,
++ SADB_SASTATE_MATURE);
++ SENDERR(EINVAL);
++ }
++
++ /* check AH and ESP */
++ switch(((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_satype) {
++ case SADB_SATYPE_AH:
++ if(!(((struct sadb_sa*)extensions[SADB_EXT_SA]) &&
++ ((struct sadb_sa*)extensions[SADB_EXT_SA])->sadb_sa_auth !=
++ SADB_AALG_NONE)) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_msg_parse: "
++ "auth alg is zero, must be non-zero for AH SAs.\n");
++ SENDERR(EINVAL);
++ }
++ if(((struct sadb_sa*)(extensions[SADB_EXT_SA]))->sadb_sa_encrypt !=
++ SADB_EALG_NONE) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_msg_parse: "
++ "AH handed encalg=%d, must be zero.\n",
++ ((struct sadb_sa*)(extensions[SADB_EXT_SA]))->sadb_sa_encrypt);
++ SENDERR(EINVAL);
++ }
++ break;
++ case SADB_SATYPE_ESP:
++ if(!(((struct sadb_sa*)extensions[SADB_EXT_SA]) &&
++ ((struct sadb_sa*)extensions[SADB_EXT_SA])->sadb_sa_encrypt !=
++ SADB_EALG_NONE)) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_msg_parse: "
++ "encrypt alg=%d is zero, must be non-zero for ESP=%d SAs.\n",
++ ((struct sadb_sa*)extensions[SADB_EXT_SA])->sadb_sa_encrypt,
++ ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_satype);
++ SENDERR(EINVAL);
++ }
++ if((((struct sadb_sa*)(extensions[SADB_EXT_SA]))->sadb_sa_encrypt ==
++ SADB_EALG_NULL) &&
++ (((struct sadb_sa*)(extensions[SADB_EXT_SA]))->sadb_sa_auth ==
++ SADB_AALG_NONE) ) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_msg_parse: "
++ "ESP handed encNULL+authNONE, illegal combination.\n");
++ SENDERR(EINVAL);
++ }
++ break;
++ case SADB_X_SATYPE_COMP:
++ if(!(((struct sadb_sa*)extensions[SADB_EXT_SA]) &&
++ ((struct sadb_sa*)extensions[SADB_EXT_SA])->sadb_sa_encrypt !=
++ SADB_EALG_NONE)) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_msg_parse: "
++ "encrypt alg=%d is zero, must be non-zero for COMP=%d SAs.\n",
++ ((struct sadb_sa*)extensions[SADB_EXT_SA])->sadb_sa_encrypt,
++ ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_satype);
++ SENDERR(EINVAL);
++ }
++ if(((struct sadb_sa*)(extensions[SADB_EXT_SA]))->sadb_sa_auth !=
++ SADB_AALG_NONE) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_msg_parse: "
++ "COMP handed auth=%d, must be zero.\n",
++ ((struct sadb_sa*)(extensions[SADB_EXT_SA]))->sadb_sa_auth);
++ SENDERR(EINVAL);
++ }
++ break;
++ default:
++ break;
++ }
++ if(ntohl(((struct sadb_sa*)(extensions[SADB_EXT_SA]))->sadb_sa_spi) <= 255) {
++ DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
++ "pfkey_msg_parse: "
++ "spi=%08x must be > 255.\n",
++ ntohl(((struct sadb_sa*)(extensions[SADB_EXT_SA]))->sadb_sa_spi));
++ SENDERR(EINVAL);
++ }
++ default:
++ break;
++ }
++errlab:
++
++ return error;
++}
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.59 2004/04/18 03:03:49 mcr
++ * renamed common include files from pluto directory.
++ *
++ * Revision 1.58 2004/03/08 01:59:08 ken
++ * freeswan.h -> openswan.h
++ *
++ * Revision 1.57 2003/12/10 01:20:19 mcr
++ * NAT-traversal patches to KLIPS.
++ *
++ * Revision 1.56 2003/12/04 23:01:12 mcr
++ * removed ipsec_netlink.h
++ *
++ * Revision 1.55 2003/11/07 01:30:37 ken
++ * Cast sizeof() to int to keep things 64bit clean
++ *
++ * Revision 1.54 2003/10/31 02:27:12 mcr
++ * pulled up port-selector patches and sa_id elimination.
++ *
++ * Revision 1.53.20.2 2003/10/29 01:11:32 mcr
++ * added debugging for pfkey library.
++ *
++ * Revision 1.53.20.1 2003/09/21 13:59:44 mcr
++ * pre-liminary X.509 patch - does not yet pass tests.
++ *
++ * Revision 1.53 2003/01/30 02:32:09 rgb
++ *
++ * Rename SAref table macro names for clarity.
++ * Convert IPsecSAref_t from signed to unsigned to fix apparent SAref exhaustion bug.
++ *
++ * Revision 1.52 2002/12/30 06:53:07 mcr
++ * deal with short SA structures... #if 0 out for now. Probably
++ * not quite the right way.
++ *
++ * Revision 1.51 2002/12/13 18:16:02 mcr
++ * restored sa_ref code
++ *
++ * Revision 1.50 2002/12/13 18:06:52 mcr
++ * temporarily removed sadb_x_sa_ref reference for 2.xx
++ *
++ * Revision 1.49 2002/10/05 05:02:58 dhr
++ *
++ * C labels go on statements
++ *
++ * Revision 1.48 2002/09/20 15:40:45 rgb
++ * Added sadb_x_sa_ref to struct sadb_sa.
++ *
++ * Revision 1.47 2002/09/20 05:01:31 rgb
++ * Fixed usage of pfkey_lib_debug.
++ * Format for function declaration style consistency.
++ * Added text labels to elucidate numeric values presented.
++ * Re-organised debug output to reduce noise in output.
++ *
++ * Revision 1.46 2002/07/24 18:44:54 rgb
++ * Type fiddling to tame ia64 compiler.
++ *
++ * Revision 1.45 2002/05/23 07:14:11 rgb
++ * Cleaned up %p variants to 0p%p for test suite cleanup.
++ *
++ * Revision 1.44 2002/04/24 07:55:32 mcr
++ * #include patches and Makefiles for post-reorg compilation.
++ *
++ * Revision 1.43 2002/04/24 07:36:40 mcr
++ * Moved from ./lib/pfkey_v2_parse.c,v
++ *
++ * Revision 1.42 2002/01/29 22:25:36 rgb
++ * Re-add ipsec_kversion.h to keep MALLOC happy.
++ *
++ * Revision 1.41 2002/01/29 01:59:10 mcr
++ * removal of kversions.h - sources that needed it now use ipsec_param.h.
++ * updating of IPv6 structures to match latest in6.h version.
++ * removed dead code from openswan.h that also duplicated kversions.h
++ * code.
++ *
++ * Revision 1.40 2002/01/20 20:34:50 mcr
++ * added pfkey_v2_sadb_type_string to decode sadb_type to string.
++ *
++ * Revision 1.39 2001/11/27 05:29:22 mcr
++ * pfkey parses are now maintained by a structure
++ * that includes their name for debug purposes.
++ * DEBUGGING() macro changed so that it takes a debug
++ * level so that pf_key() can use this to decode the
++ * structures without innundanting humans.
++ * Also uses pfkey_v2_sadb_ext_string() in messages.
++ *
++ * Revision 1.38 2001/11/06 19:47:47 rgb
++ * Added packet parameter to lifetime and comb structures.
++ *
++ * Revision 1.37 2001/10/18 04:45:24 rgb
++ * 2.4.9 kernel deprecates linux/malloc.h in favour of linux/slab.h,
++ * lib/openswan.h version macros moved to lib/kversions.h.
++ * Other compiler directive cleanups.
++ *
++ * Revision 1.36 2001/06/14 19:35:16 rgb
++ * Update copyright date.
++ *
++ * Revision 1.35 2001/05/03 19:44:51 rgb
++ * Standardise on SENDERR() macro.
++ *
++ * Revision 1.34 2001/03/16 07:41:51 rgb
++ * Put openswan.h include before pluto includes.
++ *
++ * Revision 1.33 2001/02/27 07:13:51 rgb
++ * Added satype2name() function.
++ * Added text to default satype_tbl entry.
++ * Added satype2name() conversions for most satype debug output.
++ *
++ * Revision 1.32 2001/02/26 20:01:09 rgb
++ * Added internal IP protocol 61 for magic SAs.
++ * Ditch unused sadb_satype2proto[], replaced by satype2proto().
++ * Re-formatted debug output (split lines, consistent spacing).
++ * Removed acquire, register and expire requirements for a known satype.
++ * Changed message type checking to a switch structure.
++ * Verify expected NULL auth for IPCOMP.
++ * Enforced spi > 0x100 requirement, now that pass uses a magic SA for
++ * appropriate message types.
++ *
++ * Revision 1.31 2000/12/01 07:09:00 rgb
++ * Added ipcomp sanity check to require encalgo is set.
++ *
++ * Revision 1.30 2000/11/17 18:10:30 rgb
++ * Fixed bugs mostly relating to spirange, to treat all spi variables as
++ * network byte order since this is the way PF_KEYv2 stored spis.
++ *
++ * Revision 1.29 2000/10/12 00:02:39 rgb
++ * Removed 'format, ##' nonsense from debug macros for RH7.0.
++ *
++ * Revision 1.28 2000/09/20 16:23:04 rgb
++ * Remove over-paranoid extension check in the presence of sadb_msg_errno.
++ *
++ * Revision 1.27 2000/09/20 04:04:21 rgb
++ * Changed static functions to DEBUG_NO_STATIC to reveal function names in
++ * oopsen.
++ *
++ * Revision 1.26 2000/09/15 11:37:02 rgb
++ * Merge in heavily modified Svenning Soerensen's <svenning@post5.tele.dk>
++ * IPCOMP zlib deflate code.
++ *
++ * Revision 1.25 2000/09/12 22:35:37 rgb
++ * Restructured to remove unused extensions from CLEARFLOW messages.
++ *
++ * Revision 1.24 2000/09/12 18:59:54 rgb
++ * Added Gerhard's IPv6 support to pfkey parts of libopenswan.
++ *
++ * Revision 1.23 2000/09/12 03:27:00 rgb
++ * Moved DEBUGGING definition to compile kernel with debug off.
++ *
++ * Revision 1.22 2000/09/09 06:39:27 rgb
++ * Restrict pfkey errno check to downward messages only.
++ *
++ * Revision 1.21 2000/09/08 19:22:34 rgb
++ * Enabled pfkey_sens_parse().
++ * Added check for errno on downward acquire messages only.
++ *
++ * Revision 1.20 2000/09/01 18:48:23 rgb
++ * Fixed reserved check bug and added debug output in
++ * pfkey_supported_parse().
++ * Fixed debug output label bug in pfkey_ident_parse().
++ *
++ * Revision 1.19 2000/08/27 01:55:26 rgb
++ * Define OCTETBITS and PFKEYBITS to avoid using 'magic' numbers in code.
++ *
++ * Revision 1.18 2000/08/24 17:00:36 rgb
++ * Ignore unknown extensions instead of failing.
++ *
++ * Revision 1.17 2000/06/02 22:54:14 rgb
++ * Added Gerhard Gessler's struct sockaddr_storage mods for IPv6 support.
++ *
++ * Revision 1.16 2000/05/10 19:25:11 rgb
++ * Fleshed out proposal and supported extensions.
++ *
++ * Revision 1.15 2000/01/24 21:15:31 rgb
++ * Added disabled pluto pfkey lib debug flag.
++ * Added algo debugging reporting.
++ *
++ * Revision 1.14 2000/01/22 23:24:29 rgb
++ * Added new functions proto2satype() and satype2proto() and lookup
++ * table satype_tbl. Also added proto2name() since it was easy.
++ *
++ * Revision 1.13 2000/01/21 09:43:59 rgb
++ * Cast ntohl(spi) as (unsigned long int) to shut up compiler.
++ *
++ * Revision 1.12 2000/01/21 06:28:19 rgb
++ * Added address cases for eroute flows.
++ * Indented compiler directives for readability.
++ * Added klipsdebug switching capability.
++ *
++ * Revision 1.11 1999/12/29 21:14:59 rgb
++ * Fixed debug text cut and paste typo.
++ *
++ * Revision 1.10 1999/12/10 17:45:24 rgb
++ * Added address debugging.
++ *
++ * Revision 1.9 1999/12/09 23:11:42 rgb
++ * Ditched <string.h> include since we no longer use memset().
++ * Use new pfkey_extensions_init() instead of memset().
++ * Added check for SATYPE in pfkey_msg_build().
++ * Tidy up comments and debugging comments.
++ *
++ * Revision 1.8 1999/12/07 19:55:26 rgb
++ * Removed unused first argument from extension parsers.
++ * Removed static pluto debug flag.
++ * Moved message type and state checking to pfkey_msg_parse().
++ * Changed print[fk] type from lx to x to quiet compiler.
++ * Removed redundant remain check.
++ * Changed __u* types to uint* to avoid use of asm/types.h and
++ * sys/types.h in userspace code.
++ *
++ * Revision 1.7 1999/12/01 22:20:51 rgb
++ * Moved pfkey_lib_debug variable into the library.
++ * Added pfkey version check into header parsing.
++ * Added check for SATYPE only for those extensions that require a
++ * non-zero value.
++ *
++ * Revision 1.6 1999/11/27 11:58:05 rgb
++ * Added ipv6 headers.
++ * Moved sadb_satype2proto protocol lookup table from
++ * klips/net/ipsec/pfkey_v2_parser.c.
++ * Enable lifetime_current checking.
++ * Debugging error messages added.
++ * Add argument to pfkey_msg_parse() for direction.
++ * Consolidated the 4 1-d extension bitmap arrays into one 4-d array.
++ * Add CVS log entry to bottom of file.
++ * Moved auth and enc alg check to pfkey_msg_parse().
++ * Enable accidentally disabled spirange parsing.
++ * Moved protocol/algorithm checks from klips/net/ipsec/pfkey_v2_parser.c
++ *
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/libfreeswan/prng.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,201 @@
++/*
++ * crypto-class pseudorandom number generator
++ * currently uses same algorithm as RC4(TM), from Schneier 2nd ed p397
++ * Copyright (C) 2002 Henry Spencer.
++ *
++ * This library is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU Library General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/lgpl.txt>.
++ *
++ * This library 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 Library General Public
++ * License for more details.
++ *
++ * RCSID $Id$
++ */
++#include "openswan.h"
++
++/*
++ - prng_init - initialize PRNG from a key
++ */
++void
++prng_init(prng, key, keylen)
++struct prng *prng;
++const unsigned char *key;
++size_t keylen;
++{
++ unsigned char k[256];
++ int i, j;
++ unsigned const char *p;
++ unsigned const char *keyend = key + keylen;
++ unsigned char t;
++
++ for (i = 0; i <= 255; i++)
++ prng->sbox[i] = i;
++ p = key;
++ for (i = 0; i <= 255; i++) {
++ k[i] = *p++;
++ if (p >= keyend)
++ p = key;
++ }
++ j = 0;
++ for (i = 0; i <= 255; i++) {
++ j = (j + prng->sbox[i] + k[i]) & 0xff;
++ t = prng->sbox[i];
++ prng->sbox[i] = prng->sbox[j];
++ prng->sbox[j] = t;
++ k[i] = 0; /* clear out key memory */
++ }
++ prng->i = 0;
++ prng->j = 0;
++ prng->count = 0;
++}
++
++/*
++ - prng_bytes - get some pseudorandom bytes from PRNG
++ */
++void
++prng_bytes(prng, dst, dstlen)
++struct prng *prng;
++unsigned char *dst;
++size_t dstlen;
++{
++ int i, j, t;
++ unsigned char *p = dst;
++ size_t remain = dstlen;
++# define MAX 4000000000ul
++
++ while (remain > 0) {
++ i = (prng->i + 1) & 0xff;
++ prng->i = i;
++ j = (prng->j + prng->sbox[i]) & 0xff;
++ prng->j = j;
++ t = prng->sbox[i];
++ prng->sbox[i] = prng->sbox[j];
++ prng->sbox[j] = t;
++ t = (t + prng->sbox[i]) & 0xff;
++ *p++ = prng->sbox[t];
++ remain--;
++ }
++ if (prng->count < MAX - dstlen)
++ prng->count += dstlen;
++ else
++ prng->count = MAX;
++}
++
++/*
++ - prnt_count - how many bytes have been extracted from PRNG so far?
++ */
++unsigned long
++prng_count(prng)
++struct prng *prng;
++{
++ return prng->count;
++}
++
++/*
++ - prng_final - clear out PRNG to ensure nothing left in memory
++ */
++void
++prng_final(prng)
++struct prng *prng;
++{
++ int i;
++
++ for (i = 0; i <= 255; i++)
++ prng->sbox[i] = 0;
++ prng->i = 0;
++ prng->j = 0;
++ prng->count = 0; /* just for good measure */
++}
++
++
++
++#ifdef PRNG_MAIN
++
++#include <stdio.h>
++
++void regress();
++
++int
++main(argc, argv)
++int argc;
++char *argv[];
++{
++ struct prng pr;
++ unsigned char buf[100];
++ unsigned char *p;
++ size_t n;
++
++ if (argc < 2) {
++ fprintf(stderr, "Usage: %s {key|-r}\n", argv[0]);
++ exit(2);
++ }
++
++ if (strcmp(argv[1], "-r") == 0) {
++ regress();
++ fprintf(stderr, "regress() returned?!?\n");
++ exit(1);
++ }
++
++ prng_init(&pr, argv[1], strlen(argv[1]));
++ prng_bytes(&pr, buf, 32);
++ printf("0x");
++ for (p = buf, n = 32; n > 0; p++, n--)
++ printf("%02x", *p);
++ printf("\n%lu bytes\n", prng_count(&pr));
++ prng_final(&pr);
++ exit(0);
++}
++
++void
++regress()
++{
++ struct prng pr;
++ unsigned char buf[100];
++ unsigned char *p;
++ size_t n;
++ /* somewhat non-random sample key */
++ unsigned char key[] = "here we go gathering nuts in May";
++ /* first thirty bytes of output from that key */
++ unsigned char good[] = "\x3f\x02\x8e\x4a\x2a\xea\x23\x18\x92\x7c"
++ "\x09\x52\x83\x61\xaa\x26\xce\xbb\x9d\x71"
++ "\x71\xe5\x10\x22\xaf\x60\x54\x8d\x5b\x28";
++ int nzero, none;
++ int show = 0;
++
++ prng_init(&pr, key, strlen(key));
++ prng_bytes(&pr, buf, sizeof(buf));
++ for (p = buf, n = sizeof(buf); n > 0; p++, n--) {
++ if (*p == 0)
++ nzero++;
++ if (*p == 255)
++ none++;
++ }
++ if (nzero > 3 || none > 3) {
++ fprintf(stderr, "suspiciously non-random output!\n");
++ show = 1;
++ }
++ if (memcmp(buf, good, strlen(good)) != 0) {
++ fprintf(stderr, "incorrect output!\n");
++ show = 1;
++ }
++ if (show) {
++ fprintf(stderr, "0x");
++ for (p = buf, n = sizeof(buf); n > 0; p++, n--)
++ fprintf(stderr, "%02x", *p);
++ fprintf(stderr, "\n");
++ exit(1);
++ }
++ if (prng_count(&pr) != sizeof(buf)) {
++ fprintf(stderr, "got %u bytes, but count is %lu\n",
++ sizeof(buf), prng_count(&pr));
++ exit(1);
++ }
++ prng_final(&pr);
++ exit(0);
++}
++
++#endif /* PRNG_MAIN */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/libfreeswan/rangetoa.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,60 @@
++/*
++ * convert binary form of address range to ASCII
++ * Copyright (C) 1998, 1999 Henry Spencer.
++ *
++ * This library is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU Library General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/lgpl.txt>.
++ *
++ * This library 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 Library General Public
++ * License for more details.
++ *
++ * RCSID $Id$
++ */
++#include "openswan.h"
++
++/*
++ - rangetoa - convert address range to ASCII
++ */
++size_t /* space needed for full conversion */
++rangetoa(addrs, format, dst, dstlen)
++struct in_addr addrs[2];
++int format; /* character */
++char *dst; /* need not be valid if dstlen is 0 */
++size_t dstlen;
++{
++ size_t len;
++ size_t rest;
++ int n;
++ char *p;
++
++ switch (format) {
++ case 0:
++ break;
++ default:
++ return 0;
++ break;
++ }
++
++ len = addrtoa(addrs[0], 0, dst, dstlen);
++ if (len < dstlen)
++ for (p = dst + len - 1, n = 3; len < dstlen && n > 0;
++ p++, len++, n--)
++ *p = '.';
++ else
++ p = NULL;
++ if (len < dstlen)
++ rest = dstlen - len;
++ else {
++ if (dstlen > 0)
++ *(dst + dstlen - 1) = '\0';
++ rest = 0;
++ }
++
++ len += addrtoa(addrs[1], 0, p, rest);
++
++ return len;
++}
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/libfreeswan/satot.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,133 @@
++/*
++ * convert from binary form of SA ID to text
++ * Copyright (C) 2000, 2001 Henry Spencer.
++ *
++ * This library is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU Library General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/lgpl.txt>.
++ *
++ * This library 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 Library General Public
++ * License for more details.
++ *
++ * RCSID $Id$
++ */
++#include "openswan.h"
++
++static struct typename {
++ char type;
++ char *name;
++} typenames[] = {
++ { SA_AH, "ah" },
++ { SA_ESP, "esp" },
++ { SA_IPIP, "tun" },
++ { SA_COMP, "comp" },
++ { SA_INT, "int" },
++ { 0, NULL }
++};
++
++/*
++ - satot - convert SA to text "ah507@1.2.3.4"
++ */
++size_t /* space needed for full conversion */
++satot(sa, format, dst, dstlen)
++const ip_said *sa;
++int format; /* character */
++char *dst; /* need not be valid if dstlen is 0 */
++size_t dstlen;
++{
++ size_t len = 0; /* 0 means "not recognized yet" */
++ int base;
++ int showversion; /* use delimiter to show IP version? */
++ struct typename *tn;
++ char *p;
++ char *pre;
++ char buf[10+1+ULTOT_BUF+ADDRTOT_BUF];
++ char unk[10];
++
++ switch (format) {
++ case 0:
++ base = 16;
++ showversion = 1;
++ break;
++ case 'f':
++ base = 17;
++ showversion = 1;
++ break;
++ case 'x':
++ base = 'x';
++ showversion = 0;
++ break;
++ case 'd':
++ base = 10;
++ showversion = 0;
++ break;
++ default:
++ return 0;
++ break;
++ }
++
++ memset(buf, 0, sizeof(buf));
++
++ pre = NULL;
++ for (tn = typenames; tn->name != NULL; tn++)
++ if (sa->proto == tn->type) {
++ pre = tn->name;
++ break; /* NOTE BREAK OUT */
++ }
++ if (pre == NULL) { /* unknown protocol */
++ strcpy(unk, "unk");
++ (void) ultot((unsigned char)sa->proto, 10, unk+strlen(unk),
++ sizeof(unk)-strlen(unk));
++ pre = unk;
++ }
++
++ if (strcmp(pre, PASSTHROUGHTYPE) == 0 &&
++ sa->spi == PASSTHROUGHSPI &&
++ isunspecaddr(&sa->dst)) {
++ strcpy(buf, (addrtypeof(&sa->dst) == AF_INET) ?
++ PASSTHROUGH4NAME :
++ PASSTHROUGH6NAME);
++ len = strlen(buf);
++ }
++
++ if (sa->proto == SA_INT) {
++ switch (ntohl(sa->spi)) {
++ case SPI_PASS: p = "%pass"; break;
++ case SPI_DROP: p = "%drop"; break;
++ case SPI_REJECT: p = "%reject"; break;
++ case SPI_HOLD: p = "%hold"; break;
++ case SPI_TRAP: p = "%trap"; break;
++ case SPI_TRAPSUBNET: p = "%trapsubnet"; break;
++ default: p = NULL; break;
++ }
++ if (p != NULL) {
++ strcpy(buf, p);
++ len = strlen(buf);
++ }
++ }
++
++ if (len == 0) { /* general case needed */
++ strcpy(buf, pre);
++ len = strlen(buf);
++ if (showversion) {
++ *(buf+len) = (addrtypeof(&sa->dst) == AF_INET) ? '.' :
++ ':';
++ len++;
++ *(buf+len) = '\0';
++ }
++ len += ultot(ntohl(sa->spi), base, buf+len, sizeof(buf)-len);
++ *(buf+len-1) = '@';
++ len += addrtot(&sa->dst, 0, buf+len, sizeof(buf)-len);
++ *(buf+len) = '\0';
++ }
++
++ if (dst != NULL) {
++ if (len > dstlen)
++ *(buf+dstlen-1) = '\0';
++ strcpy(dst, buf);
++ }
++ return len;
++}
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/libfreeswan/subnetof.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,59 @@
++/*
++ * minor network-address manipulation utilities
++ * Copyright (C) 1998, 1999 Henry Spencer.
++ *
++ * This library is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU Library General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/lgpl.txt>.
++ *
++ * This library 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 Library General Public
++ * License for more details.
++ *
++ * RCSID $Id$
++ */
++#include "openswan.h"
++
++/*
++ - subnetof - given address and mask, return subnet part
++ */
++struct in_addr
++subnetof(addr, mask)
++struct in_addr addr;
++struct in_addr mask;
++{
++ struct in_addr result;
++
++ result.s_addr = addr.s_addr & mask.s_addr;
++ return result;
++}
++
++/*
++ - hostof - given address and mask, return host part
++ */
++struct in_addr
++hostof(addr, mask)
++struct in_addr addr;
++struct in_addr mask;
++{
++ struct in_addr result;
++
++ result.s_addr = addr.s_addr & ~mask.s_addr;
++ return result;
++}
++
++/*
++ - broadcastof - given (network) address and mask, return broadcast address
++ */
++struct in_addr
++broadcastof(addr, mask)
++struct in_addr addr;
++struct in_addr mask;
++{
++ struct in_addr result;
++
++ result.s_addr = addr.s_addr | ~mask.s_addr;
++ return result;
++}
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/libfreeswan/subnettoa.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,61 @@
++/*
++ * convert binary form of subnet description to ASCII
++ * Copyright (C) 1998, 1999 Henry Spencer.
++ *
++ * This library is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU Library General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/lgpl.txt>.
++ *
++ * This library 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 Library General Public
++ * License for more details.
++ *
++ * RCSID $Id$
++ */
++#include "openswan.h"
++
++/*
++ - subnettoa - convert address and mask to ASCII "addr/mask"
++ * Output expresses the mask as a bit count if possible, else dotted decimal.
++ */
++size_t /* space needed for full conversion */
++subnettoa(addr, mask, format, dst, dstlen)
++struct in_addr addr;
++struct in_addr mask;
++int format; /* character */
++char *dst; /* need not be valid if dstlen is 0 */
++size_t dstlen;
++{
++ size_t len;
++ size_t rest;
++ int n;
++ char *p;
++
++ switch (format) {
++ case 0:
++ break;
++ default:
++ return 0;
++ break;
++ }
++
++ len = addrtoa(addr, 0, dst, dstlen);
++ if (len < dstlen) {
++ dst[len - 1] = '/';
++ p = dst + len;
++ rest = dstlen - len;
++ } else {
++ p = NULL;
++ rest = 0;
++ }
++
++ n = masktobits(mask);
++ if (n >= 0)
++ len += ultoa((unsigned long)n, 10, p, rest);
++ else
++ len += addrtoa(mask, 0, p, rest);
++
++ return len;
++}
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/libfreeswan/ultoa.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,66 @@
++/*
++ * convert unsigned long to ASCII
++ * Copyright (C) 1998, 1999 Henry Spencer.
++ *
++ * This library is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU Library General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/lgpl.txt>.
++ *
++ * This library 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 Library General Public
++ * License for more details.
++ *
++ * RCSID $Id$
++ */
++#include "openswan.h"
++
++/*
++ - ultoa - convert unsigned long to decimal ASCII
++ */
++size_t /* length required for full conversion */
++ultoa(n, base, dst, dstlen)
++unsigned long n;
++int base;
++char *dst; /* need not be valid if dstlen is 0 */
++size_t dstlen;
++{
++ char buf[3*sizeof(unsigned long) + 1];
++ char *bufend = buf + sizeof(buf);
++ size_t len;
++ char *p;
++ static char hex[] = "0123456789abcdef";
++
++ p = bufend;
++ *--p = '\0';
++ if (base == 10) {
++ do {
++ *--p = n%10 + '0';
++ n /= 10;
++ } while (n != 0);
++ } else if (base == 16) {
++ do {
++ *--p = hex[n&0xf];
++ n >>= 4;
++ } while (n != 0);
++ *--p = 'x';
++ *--p = '0';
++ } else if (base == 8) {
++ do {
++ *--p = (n&07) + '0';
++ n >>= 3;
++ } while (n != 0);
++ *--p = '0';
++ } else
++ *--p = '?';
++
++ len = bufend - p;
++
++ if (dstlen > 0) {
++ if (len > dstlen)
++ *(p + dstlen - 1) = '\0';
++ strcpy(dst, p);
++ }
++ return len;
++}
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/libfreeswan/ultot.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,82 @@
++/*
++ * convert unsigned long to text
++ * Copyright (C) 2000 Henry Spencer.
++ *
++ * This library is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU Library General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/lgpl.txt>.
++ *
++ * This library 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 Library General Public
++ * License for more details.
++ *
++ * RCSID $Id$
++ */
++#include "openswan.h"
++
++/*
++ - ultot - convert unsigned long to text
++ */
++size_t /* length required for full conversion */
++ultot(n, base, dst, dstlen)
++unsigned long n;
++int base;
++char *dst; /* need not be valid if dstlen is 0 */
++size_t dstlen;
++{
++ char buf[3*sizeof(unsigned long) + 1];
++ char *bufend = buf + sizeof(buf);
++ size_t len;
++ char *p;
++ static char hex[] = "0123456789abcdef";
++# define HEX32 (32/4)
++
++ p = bufend;
++ *--p = '\0';
++ switch (base) {
++ case 10:
++ case 'd':
++ do {
++ *--p = n%10 + '0';
++ n /= 10;
++ } while (n != 0);
++ break;
++ case 16:
++ case 17:
++ case 'x':
++ do {
++ *--p = hex[n&0xf];
++ n >>= 4;
++ } while (n != 0);
++ if (base == 17)
++ while (bufend - p < HEX32 + 1)
++ *--p = '0';
++ if (base == 'x') {
++ *--p = 'x';
++ *--p = '0';
++ }
++ break;
++ case 8:
++ case 'o':
++ do {
++ *--p = (n&07) + '0';
++ n >>= 3;
++ } while (n != 0);
++ if (base == 'o')
++ *--p = '0';
++ break;
++ default:
++ return 0;
++ break;
++ }
++
++ len = bufend - p;
++ if (dstlen > 0) {
++ if (len > dstlen)
++ *(p + dstlen - 1) = '\0';
++ strcpy(dst, p);
++ }
++ return len;
++}
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/zlib/Makefile Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,118 @@
++# (kernel) Makefile for IPCOMP zlib deflate code
++# Copyright (C) 1998, 1999, 2000, 2001 Richard Guy Briggs.
++# Copyright (C) 2000 Svenning Soerensen
++#
++# This program is free software; you can redistribute it and/or modify it
++# under the terms of the GNU General Public License as published by the
++# Free Software Foundation; either version 2 of the License, or (at your
++# option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++#
++# This program is distributed in the hope that it will be useful, but
++# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++# for more details.
++#
++# RCSID $Id$
++#
++
++
++
++include ../Makefile.inc
++
++
++
++ifndef TOPDIR
++TOPDIR := /usr/src/linux
++endif
++
++
++L_TARGET := zlib.a
++
++obj-y :=
++
++include Makefile.objs
++
++EXTRA_CFLAGS += $(KLIPSCOMPILE)
++
++EXTRA_CFLAGS += -Wall
++#EXTRA_CFLAGS += -Wconversion
++#EXTRA_CFLAGS += -Wmissing-prototypes
++EXTRA_CFLAGS += -Wpointer-arith
++#EXTRA_CFLAGS += -Wcast-qual
++#EXTRA_CFLAGS += -Wmissing-declarations
++EXTRA_CFLAGS += -Wstrict-prototypes
++#EXTRA_CFLAGS += -pedantic
++#EXTRA_CFLAGS += -W
++#EXTRA_CFLAGS += -Wwrite-strings
++EXTRA_CFLAGS += -Wbad-function-cast
++EXTRA_CFLAGS += -DIPCOMP_PREFIX
++
++.S.o:
++ $(CC) -D__ASSEMBLY__ -DNO_UNDERLINE -traditional -c $< -o $*.o
++
++asm-obj-$(CONFIG_M586) += match586.o
++asm-obj-$(CONFIG_M586TSC) += match586.o
++asm-obj-$(CONFIG_M586MMX) += match586.o
++asm-obj-$(CONFIG_M686) += match686.o
++asm-obj-$(CONFIG_MPENTIUMIII) += match686.o
++asm-obj-$(CONFIG_MPENTIUM4) += match686.o
++asm-obj-$(CONFIG_MK6) += match586.o
++asm-obj-$(CONFIG_MK7) += match686.o
++asm-obj-$(CONFIG_MCRUSOE) += match586.o
++asm-obj-$(CONFIG_MWINCHIPC6) += match586.o
++asm-obj-$(CONFIG_MWINCHIP2) += match686.o
++asm-obj-$(CONFIG_MWINCHIP3D) += match686.o
++
++obj-y += $(asm-obj-y)
++ifneq ($(strip $(asm-obj-y)),)
++ EXTRA_CFLAGS += -DASMV
++endif
++
++active-objs := $(sort $(obj-y) $(obj-m))
++L_OBJS := $(obj-y)
++M_OBJS := $(obj-m)
++MIX_OBJS := $(filter $(export-objs), $(active-objs))
++
++include $(TOPDIR)/Rules.make
++
++$(obj-y) : $(TOPDIR)/include/linux/config.h $(TOPDIR)/include/linux/autoconf.h
++
++
++clean:
++ -rm -f *.o *.a
++
++checkprograms:
++programs: $(L_TARGET)
++
++#
++# $Log$
++# Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++# Turn off EOLN_NATIVE flag
++#
++# (Logical change 1.5010)
++#
++# Revision 1.9 2002/04/24 07:55:32 mcr
++# #include patches and Makefiles for post-reorg compilation.
++#
++# Revision 1.8 2002/04/24 07:36:44 mcr
++# Moved from ./zlib/Makefile,v
++#
++# Revision 1.7 2002/03/27 23:34:35 mcr
++# added programs: target
++#
++# Revision 1.6 2001/12/05 20:19:08 henry
++# use new compile-control variable
++#
++# Revision 1.5 2001/11/27 16:38:08 mcr
++# added new "checkprograms" target to deal with programs that
++# are required for "make check", but that may not be ready to
++# build for every user due to external dependancies.
++#
++# Revision 1.4 2001/10/24 14:46:24 henry
++# Makefile.inc
++#
++# Revision 1.3 2001/04/21 23:05:24 rgb
++# Update asm directives for 2.4 style makefiles.
++#
++# Revision 1.2 2001/01/29 22:22:00 rgb
++# Convert to 2.4 new style with back compat.
++#
++# Revision 1.1.1.1 2000/09/29 18:51:33 rgb
++# zlib_beginnings
++#
++#
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/zlib/Makefile.objs Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,27 @@
++obj-$(CONFIG_IPSEC_IPCOMP) += adler32.o
++obj-$(CONFIG_IPSEC_IPCOMP) += deflate.o
++obj-$(CONFIG_IPSEC_IPCOMP) += infblock.o
++obj-$(CONFIG_IPSEC_IPCOMP) += infcodes.o
++obj-$(CONFIG_IPSEC_IPCOMP) += inffast.o
++obj-$(CONFIG_IPSEC_IPCOMP) += inflate.o
++obj-$(CONFIG_IPSEC_IPCOMP) += inftrees.o
++obj-$(CONFIG_IPSEC_IPCOMP) += infutil.o
++obj-$(CONFIG_IPSEC_IPCOMP) += trees.o
++obj-$(CONFIG_IPSEC_IPCOMP) += zutil.o
++
++asm-obj-$(CONFIG_M586) += ${LIBZLIBSRCDIR}/match586.o
++asm-obj-$(CONFIG_M586TSC) += ${LIBZLIBSRCDIR}/match586.o
++asm-obj-$(CONFIG_M586MMX) += ${LIBZLIBSRCDIR}/match586.o
++asm-obj-$(CONFIG_M686) += ${LIBZLIBSRCDIR}/match686.o
++asm-obj-$(CONFIG_MPENTIUMIII) += ${LIBZLIBSRCDIR}/match686.o
++asm-obj-$(CONFIG_MPENTIUM4) += ${LIBZLIBSRCDIR}/match686.o
++asm-obj-$(CONFIG_MK6) += ${LIBZLIBSRCDIR}/match586.o
++asm-obj-$(CONFIG_MK7) += ${LIBZLIBSRCDIR}/match686.o
++asm-obj-$(CONFIG_MCRUSOE) += ${LIBZLIBSRCDIR}/match586.o
++asm-obj-$(CONFIG_MWINCHIPC6) += ${LIBZLIBSRCDIR}/match586.o
++asm-obj-$(CONFIG_MWINCHIP2) += ${LIBZLIBSRCDIR}/match686.o
++asm-obj-$(CONFIG_MWINCHIP3D) += ${LIBZLIBSRCDIR}/match686.o
++
++EXTRA_CFLAGS += -DIPCOMP_PREFIX
++
++
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/zlib/README Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,147 @@
++zlib 1.1.4 is a general purpose data compression library. All the code
++is thread safe. The data format used by the zlib library
++is described by RFCs (Request for Comments) 1950 to 1952 in the files
++http://www.ietf.org/rfc/rfc1950.txt (zlib format), rfc1951.txt (deflate
++format) and rfc1952.txt (gzip format). These documents are also available in
++other formats from ftp://ftp.uu.net/graphics/png/documents/zlib/zdoc-index.html
++
++All functions of the compression library are documented in the file zlib.h
++(volunteer to write man pages welcome, contact jloup@gzip.org). A usage
++example of the library is given in the file example.c which also tests that
++the library is working correctly. Another example is given in the file
++minigzip.c. The compression library itself is composed of all source files
++except example.c and minigzip.c.
++
++To compile all files and run the test program, follow the instructions
++given at the top of Makefile. In short "make test; make install"
++should work for most machines. For Unix: "./configure; make test; make install"
++For MSDOS, use one of the special makefiles such as Makefile.msc.
++For VMS, use Make_vms.com or descrip.mms.
++
++Questions about zlib should be sent to <zlib@gzip.org>, or to
++Gilles Vollant <info@winimage.com> for the Windows DLL version.
++The zlib home page is http://www.zlib.org or http://www.gzip.org/zlib/
++Before reporting a problem, please check this site to verify that
++you have the latest version of zlib; otherwise get the latest version and
++check whether the problem still exists or not.
++
++PLEASE read the zlib FAQ http://www.gzip.org/zlib/zlib_faq.html
++before asking for help.
++
++Mark Nelson <markn@ieee.org> wrote an article about zlib for the Jan. 1997
++issue of Dr. Dobb's Journal; a copy of the article is available in
++http://dogma.net/markn/articles/zlibtool/zlibtool.htm
++
++The changes made in version 1.1.4 are documented in the file ChangeLog.
++The only changes made since 1.1.3 are bug corrections:
++
++- ZFREE was repeated on same allocation on some error conditions.
++ This creates a security problem described in
++ http://www.zlib.org/advisory-2002-03-11.txt
++- Returned incorrect error (Z_MEM_ERROR) on some invalid data
++- Avoid accesses before window for invalid distances with inflate window
++ less than 32K.
++- force windowBits > 8 to avoid a bug in the encoder for a window size
++ of 256 bytes. (A complete fix will be available in 1.1.5).
++
++The beta version 1.1.5beta includes many more changes. A new official
++version 1.1.5 will be released as soon as extensive testing has been
++completed on it.
++
++
++Unsupported third party contributions are provided in directory "contrib".
++
++A Java implementation of zlib is available in the Java Development Kit
++http://www.javasoft.com/products/JDK/1.1/docs/api/Package-java.util.zip.html
++See the zlib home page http://www.zlib.org for details.
++
++A Perl interface to zlib written by Paul Marquess <pmarquess@bfsec.bt.co.uk>
++is in the CPAN (Comprehensive Perl Archive Network) sites
++http://www.cpan.org/modules/by-module/Compress/
++
++A Python interface to zlib written by A.M. Kuchling <amk@magnet.com>
++is available in Python 1.5 and later versions, see
++http://www.python.org/doc/lib/module-zlib.html
++
++A zlib binding for TCL written by Andreas Kupries <a.kupries@westend.com>
++is availlable at http://www.westend.com/~kupries/doc/trf/man/man.html
++
++An experimental package to read and write files in .zip format,
++written on top of zlib by Gilles Vollant <info@winimage.com>, is
++available at http://www.winimage.com/zLibDll/unzip.html
++and also in the contrib/minizip directory of zlib.
++
++
++Notes for some targets:
++
++- To build a Windows DLL version, include in a DLL project zlib.def, zlib.rc
++ and all .c files except example.c and minigzip.c; compile with -DZLIB_DLL
++ The zlib DLL support was initially done by Alessandro Iacopetti and is
++ now maintained by Gilles Vollant <info@winimage.com>. Check the zlib DLL
++ home page at http://www.winimage.com/zLibDll
++
++ From Visual Basic, you can call the DLL functions which do not take
++ a structure as argument: compress, uncompress and all gz* functions.
++ See contrib/visual-basic.txt for more information, or get
++ http://www.tcfb.com/dowseware/cmp-z-it.zip
++
++- For 64-bit Irix, deflate.c must be compiled without any optimization.
++ With -O, one libpng test fails. The test works in 32 bit mode (with
++ the -n32 compiler flag). The compiler bug has been reported to SGI.
++
++- zlib doesn't work with gcc 2.6.3 on a DEC 3000/300LX under OSF/1 2.1
++ it works when compiled with cc.
++
++- on Digital Unix 4.0D (formely OSF/1) on AlphaServer, the cc option -std1
++ is necessary to get gzprintf working correctly. This is done by configure.
++
++- zlib doesn't work on HP-UX 9.05 with some versions of /bin/cc. It works
++ with other compilers. Use "make test" to check your compiler.
++
++- gzdopen is not supported on RISCOS, BEOS and by some Mac compilers.
++
++- For Turbo C the small model is supported only with reduced performance to
++ avoid any far allocation; it was tested with -DMAX_WBITS=11 -DMAX_MEM_LEVEL=3
++
++- For PalmOs, see http://www.cs.uit.no/~perm/PASTA/pilot/software.html
++ Per Harald Myrvang <perm@stud.cs.uit.no>
++
++
++Acknowledgments:
++
++ The deflate format used by zlib was defined by Phil Katz. The deflate
++ and zlib specifications were written by L. Peter Deutsch. Thanks to all the
++ people who reported problems and suggested various improvements in zlib;
++ they are too numerous to cite here.
++
++Copyright notice:
++
++ (C) 1995-2002 Jean-loup Gailly and Mark Adler
++
++ This software is provided 'as-is', without any express or implied
++ warranty. In no event will the authors be held liable for any damages
++ arising from the use of this software.
++
++ Permission is granted to anyone to use this software for any purpose,
++ including commercial applications, and to alter it and redistribute it
++ freely, subject to the following restrictions:
++
++ 1. The origin of this software must not be misrepresented; you must not
++ claim that you wrote the original software. If you use this software
++ in a product, an acknowledgment in the product documentation would be
++ appreciated but is not required.
++ 2. Altered source versions must be plainly marked as such, and must not be
++ misrepresented as being the original software.
++ 3. This notice may not be removed or altered from any source distribution.
++
++ Jean-loup Gailly Mark Adler
++ jloup@gzip.org madler@alumni.caltech.edu
++
++If you use the zlib library in a product, we would appreciate *not*
++receiving lengthy legal documents to sign. The sources are provided
++for free but without warranty of any kind. The library has been
++entirely written by Jean-loup Gailly and Mark Adler; it does not
++include third-party code.
++
++If you redistribute modified sources, we would appreciate that you include
++in the file ChangeLog history information documenting your changes.
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/zlib/README.freeswan Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,13 @@
++The only changes made to these files for use in FreeS/WAN are:
++
++ - In zconf.h, macros are defined to prefix global symbols with "ipcomp_"
++ (or "_ipcomp"), when compiled with -DIPCOMP_PREFIX.
++ - The copyright strings are defined local (static)
++
++ The above changes are made to avoid name collisions with ppp_deflate
++ and ext2compr.
++
++ - Files not needed for FreeS/WAN have been removed
++
++ See the "README" file for information about where to obtain the complete
++ zlib package.
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/zlib/adler32.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,49 @@
++/* adler32.c -- compute the Adler-32 checksum of a data stream
++ * Copyright (C) 1995-2002 Mark Adler
++ * For conditions of distribution and use, see copyright notice in zlib.h
++ */
++
++/* @(#) $Id$ */
++
++#include <zlib/zlib.h>
++#include "zconf.h"
++
++#define BASE 65521L /* largest prime smaller than 65536 */
++#define NMAX 5552
++/* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */
++
++#define DO1(buf,i) {s1 += buf[i]; s2 += s1;}
++#define DO2(buf,i) DO1(buf,i); DO1(buf,i+1);
++#define DO4(buf,i) DO2(buf,i); DO2(buf,i+2);
++#define DO8(buf,i) DO4(buf,i); DO4(buf,i+4);
++#define DO16(buf) DO8(buf,0); DO8(buf,8);
++
++/* ========================================================================= */
++uLong ZEXPORT adler32(adler, buf, len)
++ uLong adler;
++ const Bytef *buf;
++ uInt len;
++{
++ unsigned long s1 = adler & 0xffff;
++ unsigned long s2 = (adler >> 16) & 0xffff;
++ int k;
++
++ if (buf == Z_NULL) return 1L;
++
++ while (len > 0) {
++ k = len < NMAX ? len : NMAX;
++ len -= k;
++ while (k >= 16) {
++ DO16(buf);
++ buf += 16;
++ k -= 16;
++ }
++ if (k != 0) do {
++ s1 += *buf++;
++ s2 += s1;
++ } while (--k);
++ s1 %= BASE;
++ s2 %= BASE;
++ }
++ return (s2 << 16) | s1;
++}
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/zlib/deflate.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,1351 @@
++/* deflate.c -- compress data using the deflation algorithm
++ * Copyright (C) 1995-2002 Jean-loup Gailly.
++ * For conditions of distribution and use, see copyright notice in zlib.h
++ */
++
++/*
++ * ALGORITHM
++ *
++ * The "deflation" process depends on being able to identify portions
++ * of the input text which are identical to earlier input (within a
++ * sliding window trailing behind the input currently being processed).
++ *
++ * The most straightforward technique turns out to be the fastest for
++ * most input files: try all possible matches and select the longest.
++ * The key feature of this algorithm is that insertions into the string
++ * dictionary are very simple and thus fast, and deletions are avoided
++ * completely. Insertions are performed at each input character, whereas
++ * string matches are performed only when the previous match ends. So it
++ * is preferable to spend more time in matches to allow very fast string
++ * insertions and avoid deletions. The matching algorithm for small
++ * strings is inspired from that of Rabin & Karp. A brute force approach
++ * is used to find longer strings when a small match has been found.
++ * A similar algorithm is used in comic (by Jan-Mark Wams) and freeze
++ * (by Leonid Broukhis).
++ * A previous version of this file used a more sophisticated algorithm
++ * (by Fiala and Greene) which is guaranteed to run in linear amortized
++ * time, but has a larger average cost, uses more memory and is patented.
++ * However the F&G algorithm may be faster for some highly redundant
++ * files if the parameter max_chain_length (described below) is too large.
++ *
++ * ACKNOWLEDGEMENTS
++ *
++ * The idea of lazy evaluation of matches is due to Jan-Mark Wams, and
++ * I found it in 'freeze' written by Leonid Broukhis.
++ * Thanks to many people for bug reports and testing.
++ *
++ * REFERENCES
++ *
++ * Deutsch, L.P.,"DEFLATE Compressed Data Format Specification".
++ * Available in ftp://ds.internic.net/rfc/rfc1951.txt
++ *
++ * A description of the Rabin and Karp algorithm is given in the book
++ * "Algorithms" by R. Sedgewick, Addison-Wesley, p252.
++ *
++ * Fiala,E.R., and Greene,D.H.
++ * Data Compression with Finite Windows, Comm.ACM, 32,4 (1989) 490-595
++ *
++ */
++
++/* @(#) $Id$ */
++
++#include "deflate.h"
++
++local const char deflate_copyright[] =
++ " deflate 1.1.4 Copyright 1995-2002 Jean-loup Gailly ";
++/*
++ If you use the zlib library in a product, an acknowledgment is welcome
++ in the documentation of your product. If for some reason you cannot
++ include such an acknowledgment, I would appreciate that you keep this
++ copyright string in the executable of your product.
++ */
++
++/* ===========================================================================
++ * Function prototypes.
++ */
++typedef enum {
++ need_more, /* block not completed, need more input or more output */
++ block_done, /* block flush performed */
++ finish_started, /* finish started, need only more output at next deflate */
++ finish_done /* finish done, accept no more input or output */
++} block_state;
++
++typedef block_state (*compress_func) OF((deflate_state *s, int flush));
++/* Compression function. Returns the block state after the call. */
++
++local void fill_window OF((deflate_state *s));
++local block_state deflate_stored OF((deflate_state *s, int flush));
++local block_state deflate_fast OF((deflate_state *s, int flush));
++local block_state deflate_slow OF((deflate_state *s, int flush));
++local void lm_init OF((deflate_state *s));
++local void putShortMSB OF((deflate_state *s, uInt b));
++local void flush_pending OF((z_streamp strm));
++local int read_buf OF((z_streamp strm, Bytef *buf, unsigned size));
++#ifdef ASMV
++ void match_init OF((void)); /* asm code initialization */
++ uInt longest_match OF((deflate_state *s, IPos cur_match));
++#else
++local uInt longest_match OF((deflate_state *s, IPos cur_match));
++#endif
++
++#ifdef DEBUG
++local void check_match OF((deflate_state *s, IPos start, IPos match,
++ int length));
++#endif
++
++/* ===========================================================================
++ * Local data
++ */
++
++#define NIL 0
++/* Tail of hash chains */
++
++#ifndef TOO_FAR
++# define TOO_FAR 4096
++#endif
++/* Matches of length 3 are discarded if their distance exceeds TOO_FAR */
++
++#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1)
++/* Minimum amount of lookahead, except at the end of the input file.
++ * See deflate.c for comments about the MIN_MATCH+1.
++ */
++
++/* Values for max_lazy_match, good_match and max_chain_length, depending on
++ * the desired pack level (0..9). The values given below have been tuned to
++ * exclude worst case performance for pathological files. Better values may be
++ * found for specific files.
++ */
++typedef struct config_s {
++ ush good_length; /* reduce lazy search above this match length */
++ ush max_lazy; /* do not perform lazy search above this match length */
++ ush nice_length; /* quit search above this match length */
++ ush max_chain;
++ compress_func func;
++} config;
++
++local const config configuration_table[10] = {
++/* good lazy nice chain */
++/* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */
++/* 1 */ {4, 4, 8, 4, deflate_fast}, /* maximum speed, no lazy matches */
++/* 2 */ {4, 5, 16, 8, deflate_fast},
++/* 3 */ {4, 6, 32, 32, deflate_fast},
++
++/* 4 */ {4, 4, 16, 16, deflate_slow}, /* lazy matches */
++/* 5 */ {8, 16, 32, 32, deflate_slow},
++/* 6 */ {8, 16, 128, 128, deflate_slow},
++/* 7 */ {8, 32, 128, 256, deflate_slow},
++/* 8 */ {32, 128, 258, 1024, deflate_slow},
++/* 9 */ {32, 258, 258, 4096, deflate_slow}}; /* maximum compression */
++
++/* Note: the deflate() code requires max_lazy >= MIN_MATCH and max_chain >= 4
++ * For deflate_fast() (levels <= 3) good is ignored and lazy has a different
++ * meaning.
++ */
++
++#define EQUAL 0
++/* result of memcmp for equal strings */
++
++struct static_tree_desc_s {int dummy;}; /* for buggy compilers */
++
++/* ===========================================================================
++ * Update a hash value with the given input byte
++ * IN assertion: all calls to to UPDATE_HASH are made with consecutive
++ * input characters, so that a running hash key can be computed from the
++ * previous key instead of complete recalculation each time.
++ */
++#define UPDATE_HASH(s,h,c) (h = (((h)<<s->hash_shift) ^ (c)) & s->hash_mask)
++
++
++/* ===========================================================================
++ * Insert string str in the dictionary and set match_head to the previous head
++ * of the hash chain (the most recent string with same hash key). Return
++ * the previous length of the hash chain.
++ * If this file is compiled with -DFASTEST, the compression level is forced
++ * to 1, and no hash chains are maintained.
++ * IN assertion: all calls to to INSERT_STRING are made with consecutive
++ * input characters and the first MIN_MATCH bytes of str are valid
++ * (except for the last MIN_MATCH-1 bytes of the input file).
++ */
++#ifdef FASTEST
++#define INSERT_STRING(s, str, match_head) \
++ (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \
++ match_head = s->head[s->ins_h], \
++ s->head[s->ins_h] = (Pos)(str))
++#else
++#define INSERT_STRING(s, str, match_head) \
++ (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \
++ s->prev[(str) & s->w_mask] = match_head = s->head[s->ins_h], \
++ s->head[s->ins_h] = (Pos)(str))
++#endif
++
++/* ===========================================================================
++ * Initialize the hash table (avoiding 64K overflow for 16 bit systems).
++ * prev[] will be initialized on the fly.
++ */
++#define CLEAR_HASH(s) \
++ s->head[s->hash_size-1] = NIL; \
++ zmemzero((Bytef *)s->head, (unsigned)(s->hash_size-1)*sizeof(*s->head));
++
++/* ========================================================================= */
++int ZEXPORT deflateInit_(strm, level, version, stream_size)
++ z_streamp strm;
++ int level;
++ const char *version;
++ int stream_size;
++{
++ return deflateInit2_(strm, level, Z_DEFLATED, MAX_WBITS, DEF_MEM_LEVEL,
++ Z_DEFAULT_STRATEGY, version, stream_size);
++ /* To do: ignore strm->next_in if we use it as window */
++}
++
++/* ========================================================================= */
++int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy,
++ version, stream_size)
++ z_streamp strm;
++ int level;
++ int method;
++ int windowBits;
++ int memLevel;
++ int strategy;
++ const char *version;
++ int stream_size;
++{
++ deflate_state *s;
++ int noheader = 0;
++ static const char* my_version = ZLIB_VERSION;
++
++ ushf *overlay;
++ /* We overlay pending_buf and d_buf+l_buf. This works since the average
++ * output size for (length,distance) codes is <= 24 bits.
++ */
++
++ if (version == Z_NULL || version[0] != my_version[0] ||
++ stream_size != sizeof(z_stream)) {
++ return Z_VERSION_ERROR;
++ }
++ if (strm == Z_NULL) return Z_STREAM_ERROR;
++
++ strm->msg = Z_NULL;
++ if (strm->zalloc == Z_NULL) {
++ return Z_STREAM_ERROR;
++/* strm->zalloc = zcalloc;
++ strm->opaque = (voidpf)0;*/
++ }
++ if (strm->zfree == Z_NULL) return Z_STREAM_ERROR; /* strm->zfree = zcfree; */
++
++ if (level == Z_DEFAULT_COMPRESSION) level = 6;
++#ifdef FASTEST
++ level = 1;
++#endif
++
++ if (windowBits < 0) { /* undocumented feature: suppress zlib header */
++ noheader = 1;
++ windowBits = -windowBits;
++ }
++ if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method != Z_DEFLATED ||
++ windowBits < 9 || windowBits > 15 || level < 0 || level > 9 ||
++ strategy < 0 || strategy > Z_HUFFMAN_ONLY) {
++ return Z_STREAM_ERROR;
++ }
++ s = (deflate_state *) ZALLOC(strm, 1, sizeof(deflate_state));
++ if (s == Z_NULL) return Z_MEM_ERROR;
++ strm->state = (struct internal_state FAR *)s;
++ s->strm = strm;
++
++ s->noheader = noheader;
++ s->w_bits = windowBits;
++ s->w_size = 1 << s->w_bits;
++ s->w_mask = s->w_size - 1;
++
++ s->hash_bits = memLevel + 7;
++ s->hash_size = 1 << s->hash_bits;
++ s->hash_mask = s->hash_size - 1;
++ s->hash_shift = ((s->hash_bits+MIN_MATCH-1)/MIN_MATCH);
++
++ s->window = (Bytef *) ZALLOC(strm, s->w_size, 2*sizeof(Byte));
++ s->prev = (Posf *) ZALLOC(strm, s->w_size, sizeof(Pos));
++ s->head = (Posf *) ZALLOC(strm, s->hash_size, sizeof(Pos));
++
++ s->lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */
++
++ overlay = (ushf *) ZALLOC(strm, s->lit_bufsize, sizeof(ush)+2);
++ s->pending_buf = (uchf *) overlay;
++ s->pending_buf_size = (ulg)s->lit_bufsize * (sizeof(ush)+2L);
++
++ if (s->window == Z_NULL || s->prev == Z_NULL || s->head == Z_NULL ||
++ s->pending_buf == Z_NULL) {
++ strm->msg = ERR_MSG(Z_MEM_ERROR);
++ deflateEnd (strm);
++ return Z_MEM_ERROR;
++ }
++ s->d_buf = overlay + s->lit_bufsize/sizeof(ush);
++ s->l_buf = s->pending_buf + (1+sizeof(ush))*s->lit_bufsize;
++
++ s->level = level;
++ s->strategy = strategy;
++ s->method = (Byte)method;
++
++ return deflateReset(strm);
++}
++
++/* ========================================================================= */
++int ZEXPORT deflateSetDictionary (strm, dictionary, dictLength)
++ z_streamp strm;
++ const Bytef *dictionary;
++ uInt dictLength;
++{
++ deflate_state *s;
++ uInt length = dictLength;
++ uInt n;
++ IPos hash_head = 0;
++
++ if (strm == Z_NULL || strm->state == Z_NULL || dictionary == Z_NULL ||
++ strm->state->status != INIT_STATE) return Z_STREAM_ERROR;
++
++ s = strm->state;
++ strm->adler = adler32(strm->adler, dictionary, dictLength);
++
++ if (length < MIN_MATCH) return Z_OK;
++ if (length > MAX_DIST(s)) {
++ length = MAX_DIST(s);
++#ifndef USE_DICT_HEAD
++ dictionary += dictLength - length; /* use the tail of the dictionary */
++#endif
++ }
++ zmemcpy(s->window, dictionary, length);
++ s->strstart = length;
++ s->block_start = (long)length;
++
++ /* Insert all strings in the hash table (except for the last two bytes).
++ * s->lookahead stays null, so s->ins_h will be recomputed at the next
++ * call of fill_window.
++ */
++ s->ins_h = s->window[0];
++ UPDATE_HASH(s, s->ins_h, s->window[1]);
++ for (n = 0; n <= length - MIN_MATCH; n++) {
++ INSERT_STRING(s, n, hash_head);
++ }
++ if (hash_head) hash_head = 0; /* to make compiler happy */
++ return Z_OK;
++}
++
++/* ========================================================================= */
++int ZEXPORT deflateReset (strm)
++ z_streamp strm;
++{
++ deflate_state *s;
++
++ if (strm == Z_NULL || strm->state == Z_NULL ||
++ strm->zalloc == Z_NULL || strm->zfree == Z_NULL) return Z_STREAM_ERROR;
++
++ strm->total_in = strm->total_out = 0;
++ strm->msg = Z_NULL; /* use zfree if we ever allocate msg dynamically */
++ strm->data_type = Z_UNKNOWN;
++
++ s = (deflate_state *)strm->state;
++ s->pending = 0;
++ s->pending_out = s->pending_buf;
++
++ if (s->noheader < 0) {
++ s->noheader = 0; /* was set to -1 by deflate(..., Z_FINISH); */
++ }
++ s->status = s->noheader ? BUSY_STATE : INIT_STATE;
++ strm->adler = 1;
++ s->last_flush = Z_NO_FLUSH;
++
++ _tr_init(s);
++ lm_init(s);
++
++ return Z_OK;
++}
++
++/* ========================================================================= */
++int ZEXPORT deflateParams(strm, level, strategy)
++ z_streamp strm;
++ int level;
++ int strategy;
++{
++ deflate_state *s;
++ compress_func func;
++ int err = Z_OK;
++
++ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
++ s = strm->state;
++
++ if (level == Z_DEFAULT_COMPRESSION) {
++ level = 6;
++ }
++ if (level < 0 || level > 9 || strategy < 0 || strategy > Z_HUFFMAN_ONLY) {
++ return Z_STREAM_ERROR;
++ }
++ func = configuration_table[s->level].func;
++
++ if (func != configuration_table[level].func && strm->total_in != 0) {
++ /* Flush the last buffer: */
++ err = deflate(strm, Z_PARTIAL_FLUSH);
++ }
++ if (s->level != level) {
++ s->level = level;
++ s->max_lazy_match = configuration_table[level].max_lazy;
++ s->good_match = configuration_table[level].good_length;
++ s->nice_match = configuration_table[level].nice_length;
++ s->max_chain_length = configuration_table[level].max_chain;
++ }
++ s->strategy = strategy;
++ return err;
++}
++
++/* =========================================================================
++ * Put a short in the pending buffer. The 16-bit value is put in MSB order.
++ * IN assertion: the stream state is correct and there is enough room in
++ * pending_buf.
++ */
++local void putShortMSB (s, b)
++ deflate_state *s;
++ uInt b;
++{
++ put_byte(s, (Byte)(b >> 8));
++ put_byte(s, (Byte)(b & 0xff));
++}
++
++/* =========================================================================
++ * Flush as much pending output as possible. All deflate() output goes
++ * through this function so some applications may wish to modify it
++ * to avoid allocating a large strm->next_out buffer and copying into it.
++ * (See also read_buf()).
++ */
++local void flush_pending(strm)
++ z_streamp strm;
++{
++ unsigned len = strm->state->pending;
++
++ if (len > strm->avail_out) len = strm->avail_out;
++ if (len == 0) return;
++
++ zmemcpy(strm->next_out, strm->state->pending_out, len);
++ strm->next_out += len;
++ strm->state->pending_out += len;
++ strm->total_out += len;
++ strm->avail_out -= len;
++ strm->state->pending -= len;
++ if (strm->state->pending == 0) {
++ strm->state->pending_out = strm->state->pending_buf;
++ }
++}
++
++/* ========================================================================= */
++int ZEXPORT deflate (strm, flush)
++ z_streamp strm;
++ int flush;
++{
++ int old_flush; /* value of flush param for previous deflate call */
++ deflate_state *s;
++
++ if (strm == Z_NULL || strm->state == Z_NULL ||
++ flush > Z_FINISH || flush < 0) {
++ return Z_STREAM_ERROR;
++ }
++ s = strm->state;
++
++ if (strm->next_out == Z_NULL ||
++ (strm->next_in == Z_NULL && strm->avail_in != 0) ||
++ (s->status == FINISH_STATE && flush != Z_FINISH)) {
++ ERR_RETURN(strm, Z_STREAM_ERROR);
++ }
++ if (strm->avail_out == 0) ERR_RETURN(strm, Z_BUF_ERROR);
++
++ s->strm = strm; /* just in case */
++ old_flush = s->last_flush;
++ s->last_flush = flush;
++
++ /* Write the zlib header */
++ if (s->status == INIT_STATE) {
++
++ uInt header = (Z_DEFLATED + ((s->w_bits-8)<<4)) << 8;
++ uInt level_flags = (s->level-1) >> 1;
++
++ if (level_flags > 3) level_flags = 3;
++ header |= (level_flags << 6);
++ if (s->strstart != 0) header |= PRESET_DICT;
++ header += 31 - (header % 31);
++
++ s->status = BUSY_STATE;
++ putShortMSB(s, header);
++
++ /* Save the adler32 of the preset dictionary: */
++ if (s->strstart != 0) {
++ putShortMSB(s, (uInt)(strm->adler >> 16));
++ putShortMSB(s, (uInt)(strm->adler & 0xffff));
++ }
++ strm->adler = 1L;
++ }
++
++ /* Flush as much pending output as possible */
++ if (s->pending != 0) {
++ flush_pending(strm);
++ if (strm->avail_out == 0) {
++ /* Since avail_out is 0, deflate will be called again with
++ * more output space, but possibly with both pending and
++ * avail_in equal to zero. There won't be anything to do,
++ * but this is not an error situation so make sure we
++ * return OK instead of BUF_ERROR at next call of deflate:
++ */
++ s->last_flush = -1;
++ return Z_OK;
++ }
++
++ /* Make sure there is something to do and avoid duplicate consecutive
++ * flushes. For repeated and useless calls with Z_FINISH, we keep
++ * returning Z_STREAM_END instead of Z_BUFF_ERROR.
++ */
++ } else if (strm->avail_in == 0 && flush <= old_flush &&
++ flush != Z_FINISH) {
++ ERR_RETURN(strm, Z_BUF_ERROR);
++ }
++
++ /* User must not provide more input after the first FINISH: */
++ if (s->status == FINISH_STATE && strm->avail_in != 0) {
++ ERR_RETURN(strm, Z_BUF_ERROR);
++ }
++
++ /* Start a new block or continue the current one.
++ */
++ if (strm->avail_in != 0 || s->lookahead != 0 ||
++ (flush != Z_NO_FLUSH && s->status != FINISH_STATE)) {
++ block_state bstate;
++
++ bstate = (*(configuration_table[s->level].func))(s, flush);
++
++ if (bstate == finish_started || bstate == finish_done) {
++ s->status = FINISH_STATE;
++ }
++ if (bstate == need_more || bstate == finish_started) {
++ if (strm->avail_out == 0) {
++ s->last_flush = -1; /* avoid BUF_ERROR next call, see above */
++ }
++ return Z_OK;
++ /* If flush != Z_NO_FLUSH && avail_out == 0, the next call
++ * of deflate should use the same flush parameter to make sure
++ * that the flush is complete. So we don't have to output an
++ * empty block here, this will be done at next call. This also
++ * ensures that for a very small output buffer, we emit at most
++ * one empty block.
++ */
++ }
++ if (bstate == block_done) {
++ if (flush == Z_PARTIAL_FLUSH) {
++ _tr_align(s);
++ } else { /* FULL_FLUSH or SYNC_FLUSH */
++ _tr_stored_block(s, (char*)0, 0L, 0);
++ /* For a full flush, this empty block will be recognized
++ * as a special marker by inflate_sync().
++ */
++ if (flush == Z_FULL_FLUSH) {
++ CLEAR_HASH(s); /* forget history */
++ }
++ }
++ flush_pending(strm);
++ if (strm->avail_out == 0) {
++ s->last_flush = -1; /* avoid BUF_ERROR at next call, see above */
++ return Z_OK;
++ }
++ }
++ }
++ Assert(strm->avail_out > 0, "bug2");
++
++ if (flush != Z_FINISH) return Z_OK;
++ if (s->noheader) return Z_STREAM_END;
++
++ /* Write the zlib trailer (adler32) */
++ putShortMSB(s, (uInt)(strm->adler >> 16));
++ putShortMSB(s, (uInt)(strm->adler & 0xffff));
++ flush_pending(strm);
++ /* If avail_out is zero, the application will call deflate again
++ * to flush the rest.
++ */
++ s->noheader = -1; /* write the trailer only once! */
++ return s->pending != 0 ? Z_OK : Z_STREAM_END;
++}
++
++/* ========================================================================= */
++int ZEXPORT deflateEnd (strm)
++ z_streamp strm;
++{
++ int status;
++
++ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
++
++ status = strm->state->status;
++ if (status != INIT_STATE && status != BUSY_STATE &&
++ status != FINISH_STATE) {
++ return Z_STREAM_ERROR;
++ }
++
++ /* Deallocate in reverse order of allocations: */
++ TRY_FREE(strm, strm->state->pending_buf);
++ TRY_FREE(strm, strm->state->head);
++ TRY_FREE(strm, strm->state->prev);
++ TRY_FREE(strm, strm->state->window);
++
++ ZFREE(strm, strm->state);
++ strm->state = Z_NULL;
++
++ return status == BUSY_STATE ? Z_DATA_ERROR : Z_OK;
++}
++
++/* =========================================================================
++ * Copy the source state to the destination state.
++ * To simplify the source, this is not supported for 16-bit MSDOS (which
++ * doesn't have enough memory anyway to duplicate compression states).
++ */
++int ZEXPORT deflateCopy (dest, source)
++ z_streamp dest;
++ z_streamp source;
++{
++#ifdef MAXSEG_64K
++ return Z_STREAM_ERROR;
++#else
++ deflate_state *ds;
++ deflate_state *ss;
++ ushf *overlay;
++
++
++ if (source == Z_NULL || dest == Z_NULL || source->state == Z_NULL) {
++ return Z_STREAM_ERROR;
++ }
++
++ ss = source->state;
++
++ *dest = *source;
++
++ ds = (deflate_state *) ZALLOC(dest, 1, sizeof(deflate_state));
++ if (ds == Z_NULL) return Z_MEM_ERROR;
++ dest->state = (struct internal_state FAR *) ds;
++ *ds = *ss;
++ ds->strm = dest;
++
++ ds->window = (Bytef *) ZALLOC(dest, ds->w_size, 2*sizeof(Byte));
++ ds->prev = (Posf *) ZALLOC(dest, ds->w_size, sizeof(Pos));
++ ds->head = (Posf *) ZALLOC(dest, ds->hash_size, sizeof(Pos));
++ overlay = (ushf *) ZALLOC(dest, ds->lit_bufsize, sizeof(ush)+2);
++ ds->pending_buf = (uchf *) overlay;
++
++ if (ds->window == Z_NULL || ds->prev == Z_NULL || ds->head == Z_NULL ||
++ ds->pending_buf == Z_NULL) {
++ deflateEnd (dest);
++ return Z_MEM_ERROR;
++ }
++ /* following zmemcpy do not work for 16-bit MSDOS */
++ zmemcpy(ds->window, ss->window, ds->w_size * 2 * sizeof(Byte));
++ zmemcpy(ds->prev, ss->prev, ds->w_size * sizeof(Pos));
++ zmemcpy(ds->head, ss->head, ds->hash_size * sizeof(Pos));
++ zmemcpy(ds->pending_buf, ss->pending_buf, (uInt)ds->pending_buf_size);
++
++ ds->pending_out = ds->pending_buf + (ss->pending_out - ss->pending_buf);
++ ds->d_buf = overlay + ds->lit_bufsize/sizeof(ush);
++ ds->l_buf = ds->pending_buf + (1+sizeof(ush))*ds->lit_bufsize;
++
++ ds->l_desc.dyn_tree = ds->dyn_ltree;
++ ds->d_desc.dyn_tree = ds->dyn_dtree;
++ ds->bl_desc.dyn_tree = ds->bl_tree;
++
++ return Z_OK;
++#endif
++}
++
++/* ===========================================================================
++ * Read a new buffer from the current input stream, update the adler32
++ * and total number of bytes read. All deflate() input goes through
++ * this function so some applications may wish to modify it to avoid
++ * allocating a large strm->next_in buffer and copying from it.
++ * (See also flush_pending()).
++ */
++local int read_buf(strm, buf, size)
++ z_streamp strm;
++ Bytef *buf;
++ unsigned size;
++{
++ unsigned len = strm->avail_in;
++
++ if (len > size) len = size;
++ if (len == 0) return 0;
++
++ strm->avail_in -= len;
++
++ if (!strm->state->noheader) {
++ strm->adler = adler32(strm->adler, strm->next_in, len);
++ }
++ zmemcpy(buf, strm->next_in, len);
++ strm->next_in += len;
++ strm->total_in += len;
++
++ return (int)len;
++}
++
++/* ===========================================================================
++ * Initialize the "longest match" routines for a new zlib stream
++ */
++local void lm_init (s)
++ deflate_state *s;
++{
++ s->window_size = (ulg)2L*s->w_size;
++
++ CLEAR_HASH(s);
++
++ /* Set the default configuration parameters:
++ */
++ s->max_lazy_match = configuration_table[s->level].max_lazy;
++ s->good_match = configuration_table[s->level].good_length;
++ s->nice_match = configuration_table[s->level].nice_length;
++ s->max_chain_length = configuration_table[s->level].max_chain;
++
++ s->strstart = 0;
++ s->block_start = 0L;
++ s->lookahead = 0;
++ s->match_length = s->prev_length = MIN_MATCH-1;
++ s->match_available = 0;
++ s->ins_h = 0;
++#ifdef ASMV
++ match_init(); /* initialize the asm code */
++#endif
++}
++
++/* ===========================================================================
++ * Set match_start to the longest match starting at the given string and
++ * return its length. Matches shorter or equal to prev_length are discarded,
++ * in which case the result is equal to prev_length and match_start is
++ * garbage.
++ * IN assertions: cur_match is the head of the hash chain for the current
++ * string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1
++ * OUT assertion: the match length is not greater than s->lookahead.
++ */
++#ifndef ASMV
++/* For 80x86 and 680x0, an optimized version will be provided in match.asm or
++ * match.S. The code will be functionally equivalent.
++ */
++#ifndef FASTEST
++local uInt longest_match(s, cur_match)
++ deflate_state *s;
++ IPos cur_match; /* current match */
++{
++ unsigned chain_length = s->max_chain_length;/* max hash chain length */
++ register Bytef *scan = s->window + s->strstart; /* current string */
++ register Bytef *match; /* matched string */
++ register int len; /* length of current match */
++ int best_len = s->prev_length; /* best match length so far */
++ int nice_match = s->nice_match; /* stop if match long enough */
++ IPos limit = s->strstart > (IPos)MAX_DIST(s) ?
++ s->strstart - (IPos)MAX_DIST(s) : NIL;
++ /* Stop when cur_match becomes <= limit. To simplify the code,
++ * we prevent matches with the string of window index 0.
++ */
++ Posf *prev = s->prev;
++ uInt wmask = s->w_mask;
++
++#ifdef UNALIGNED_OK
++ /* Compare two bytes at a time. Note: this is not always beneficial.
++ * Try with and without -DUNALIGNED_OK to check.
++ */
++ register Bytef *strend = s->window + s->strstart + MAX_MATCH - 1;
++ register ush scan_start = *(ushf*)scan;
++ register ush scan_end = *(ushf*)(scan+best_len-1);
++#else
++ register Bytef *strend = s->window + s->strstart + MAX_MATCH;
++ register Byte scan_end1 = scan[best_len-1];
++ register Byte scan_end = scan[best_len];
++#endif
++
++ /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16.
++ * It is easy to get rid of this optimization if necessary.
++ */
++ Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever");
++
++ /* Do not waste too much time if we already have a good match: */
++ if (s->prev_length >= s->good_match) {
++ chain_length >>= 2;
++ }
++ /* Do not look for matches beyond the end of the input. This is necessary
++ * to make deflate deterministic.
++ */
++ if ((uInt)nice_match > s->lookahead) nice_match = s->lookahead;
++
++ Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead");
++
++ do {
++ Assert(cur_match < s->strstart, "no future");
++ match = s->window + cur_match;
++
++ /* Skip to next match if the match length cannot increase
++ * or if the match length is less than 2:
++ */
++#if (defined(UNALIGNED_OK) && MAX_MATCH == 258)
++ /* This code assumes sizeof(unsigned short) == 2. Do not use
++ * UNALIGNED_OK if your compiler uses a different size.
++ */
++ if (*(ushf*)(match+best_len-1) != scan_end ||
++ *(ushf*)match != scan_start) continue;
++
++ /* It is not necessary to compare scan[2] and match[2] since they are
++ * always equal when the other bytes match, given that the hash keys
++ * are equal and that HASH_BITS >= 8. Compare 2 bytes at a time at
++ * strstart+3, +5, ... up to strstart+257. We check for insufficient
++ * lookahead only every 4th comparison; the 128th check will be made
++ * at strstart+257. If MAX_MATCH-2 is not a multiple of 8, it is
++ * necessary to put more guard bytes at the end of the window, or
++ * to check more often for insufficient lookahead.
++ */
++ Assert(scan[2] == match[2], "scan[2]?");
++ scan++, match++;
++ do {
++ } while (*(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
++ *(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
++ *(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
++ *(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
++ scan < strend);
++ /* The funny "do {}" generates better code on most compilers */
++
++ /* Here, scan <= window+strstart+257 */
++ Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan");
++ if (*scan == *match) scan++;
++
++ len = (MAX_MATCH - 1) - (int)(strend-scan);
++ scan = strend - (MAX_MATCH-1);
++
++#else /* UNALIGNED_OK */
++
++ if (match[best_len] != scan_end ||
++ match[best_len-1] != scan_end1 ||
++ *match != *scan ||
++ *++match != scan[1]) continue;
++
++ /* The check at best_len-1 can be removed because it will be made
++ * again later. (This heuristic is not always a win.)
++ * It is not necessary to compare scan[2] and match[2] since they
++ * are always equal when the other bytes match, given that
++ * the hash keys are equal and that HASH_BITS >= 8.
++ */
++ scan += 2, match++;
++ Assert(*scan == *match, "match[2]?");
++
++ /* We check for insufficient lookahead only every 8th comparison;
++ * the 256th check will be made at strstart+258.
++ */
++ do {
++ } while (*++scan == *++match && *++scan == *++match &&
++ *++scan == *++match && *++scan == *++match &&
++ *++scan == *++match && *++scan == *++match &&
++ *++scan == *++match && *++scan == *++match &&
++ scan < strend);
++
++ Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan");
++
++ len = MAX_MATCH - (int)(strend - scan);
++ scan = strend - MAX_MATCH;
++
++#endif /* UNALIGNED_OK */
++
++ if (len > best_len) {
++ s->match_start = cur_match;
++ best_len = len;
++ if (len >= nice_match) break;
++#ifdef UNALIGNED_OK
++ scan_end = *(ushf*)(scan+best_len-1);
++#else
++ scan_end1 = scan[best_len-1];
++ scan_end = scan[best_len];
++#endif
++ }
++ } while ((cur_match = prev[cur_match & wmask]) > limit
++ && --chain_length != 0);
++
++ if ((uInt)best_len <= s->lookahead) return (uInt)best_len;
++ return s->lookahead;
++}
++
++#else /* FASTEST */
++/* ---------------------------------------------------------------------------
++ * Optimized version for level == 1 only
++ */
++local uInt longest_match(s, cur_match)
++ deflate_state *s;
++ IPos cur_match; /* current match */
++{
++ register Bytef *scan = s->window + s->strstart; /* current string */
++ register Bytef *match; /* matched string */
++ register int len; /* length of current match */
++ register Bytef *strend = s->window + s->strstart + MAX_MATCH;
++
++ /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16.
++ * It is easy to get rid of this optimization if necessary.
++ */
++ Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever");
++
++ Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead");
++
++ Assert(cur_match < s->strstart, "no future");
++
++ match = s->window + cur_match;
++
++ /* Return failure if the match length is less than 2:
++ */
++ if (match[0] != scan[0] || match[1] != scan[1]) return MIN_MATCH-1;
++
++ /* The check at best_len-1 can be removed because it will be made
++ * again later. (This heuristic is not always a win.)
++ * It is not necessary to compare scan[2] and match[2] since they
++ * are always equal when the other bytes match, given that
++ * the hash keys are equal and that HASH_BITS >= 8.
++ */
++ scan += 2, match += 2;
++ Assert(*scan == *match, "match[2]?");
++
++ /* We check for insufficient lookahead only every 8th comparison;
++ * the 256th check will be made at strstart+258.
++ */
++ do {
++ } while (*++scan == *++match && *++scan == *++match &&
++ *++scan == *++match && *++scan == *++match &&
++ *++scan == *++match && *++scan == *++match &&
++ *++scan == *++match && *++scan == *++match &&
++ scan < strend);
++
++ Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan");
++
++ len = MAX_MATCH - (int)(strend - scan);
++
++ if (len < MIN_MATCH) return MIN_MATCH - 1;
++
++ s->match_start = cur_match;
++ return len <= s->lookahead ? len : s->lookahead;
++}
++#endif /* FASTEST */
++#endif /* ASMV */
++
++#ifdef DEBUG
++/* ===========================================================================
++ * Check that the match at match_start is indeed a match.
++ */
++local void check_match(s, start, match, length)
++ deflate_state *s;
++ IPos start, match;
++ int length;
++{
++ /* check that the match is indeed a match */
++ if (zmemcmp(s->window + match,
++ s->window + start, length) != EQUAL) {
++ fprintf(stderr, " start %u, match %u, length %d\n",
++ start, match, length);
++ do {
++ fprintf(stderr, "%c%c", s->window[match++], s->window[start++]);
++ } while (--length != 0);
++ z_error("invalid match");
++ }
++ if (z_verbose > 1) {
++ fprintf(stderr,"\\[%d,%d]", start-match, length);
++ do { putc(s->window[start++], stderr); } while (--length != 0);
++ }
++}
++#else
++# define check_match(s, start, match, length)
++#endif
++
++/* ===========================================================================
++ * Fill the window when the lookahead becomes insufficient.
++ * Updates strstart and lookahead.
++ *
++ * IN assertion: lookahead < MIN_LOOKAHEAD
++ * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD
++ * At least one byte has been read, or avail_in == 0; reads are
++ * performed for at least two bytes (required for the zip translate_eol
++ * option -- not supported here).
++ */
++local void fill_window(s)
++ deflate_state *s;
++{
++ register unsigned n, m;
++ register Posf *p;
++ unsigned more; /* Amount of free space at the end of the window. */
++ uInt wsize = s->w_size;
++
++ do {
++ more = (unsigned)(s->window_size -(ulg)s->lookahead -(ulg)s->strstart);
++
++ /* Deal with !@#$% 64K limit: */
++ if (more == 0 && s->strstart == 0 && s->lookahead == 0) {
++ more = wsize;
++
++ } else if (more == (unsigned)(-1)) {
++ /* Very unlikely, but possible on 16 bit machine if strstart == 0
++ * and lookahead == 1 (input done one byte at time)
++ */
++ more--;
++
++ /* If the window is almost full and there is insufficient lookahead,
++ * move the upper half to the lower one to make room in the upper half.
++ */
++ } else if (s->strstart >= wsize+MAX_DIST(s)) {
++
++ zmemcpy(s->window, s->window+wsize, (unsigned)wsize);
++ s->match_start -= wsize;
++ s->strstart -= wsize; /* we now have strstart >= MAX_DIST */
++ s->block_start -= (long) wsize;
++
++ /* Slide the hash table (could be avoided with 32 bit values
++ at the expense of memory usage). We slide even when level == 0
++ to keep the hash table consistent if we switch back to level > 0
++ later. (Using level 0 permanently is not an optimal usage of
++ zlib, so we don't care about this pathological case.)
++ */
++ n = s->hash_size;
++ p = &s->head[n];
++ do {
++ m = *--p;
++ *p = (Pos)(m >= wsize ? m-wsize : NIL);
++ } while (--n);
++
++ n = wsize;
++#ifndef FASTEST
++ p = &s->prev[n];
++ do {
++ m = *--p;
++ *p = (Pos)(m >= wsize ? m-wsize : NIL);
++ /* If n is not on any hash chain, prev[n] is garbage but
++ * its value will never be used.
++ */
++ } while (--n);
++#endif
++ more += wsize;
++ }
++ if (s->strm->avail_in == 0) return;
++
++ /* If there was no sliding:
++ * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 &&
++ * more == window_size - lookahead - strstart
++ * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1)
++ * => more >= window_size - 2*WSIZE + 2
++ * In the BIG_MEM or MMAP case (not yet supported),
++ * window_size == input_size + MIN_LOOKAHEAD &&
++ * strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD.
++ * Otherwise, window_size == 2*WSIZE so more >= 2.
++ * If there was sliding, more >= WSIZE. So in all cases, more >= 2.
++ */
++ Assert(more >= 2, "more < 2");
++
++ n = read_buf(s->strm, s->window + s->strstart + s->lookahead, more);
++ s->lookahead += n;
++
++ /* Initialize the hash value now that we have some input: */
++ if (s->lookahead >= MIN_MATCH) {
++ s->ins_h = s->window[s->strstart];
++ UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]);
++#if MIN_MATCH != 3
++ Call UPDATE_HASH() MIN_MATCH-3 more times
++#endif
++ }
++ /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage,
++ * but this is not important since only literal bytes will be emitted.
++ */
++
++ } while (s->lookahead < MIN_LOOKAHEAD && s->strm->avail_in != 0);
++}
++
++/* ===========================================================================
++ * Flush the current block, with given end-of-file flag.
++ * IN assertion: strstart is set to the end of the current match.
++ */
++#define FLUSH_BLOCK_ONLY(s, eof) { \
++ _tr_flush_block(s, (s->block_start >= 0L ? \
++ (charf *)&s->window[(unsigned)s->block_start] : \
++ (charf *)Z_NULL), \
++ (ulg)((long)s->strstart - s->block_start), \
++ (eof)); \
++ s->block_start = s->strstart; \
++ flush_pending(s->strm); \
++ Tracev((stderr,"[FLUSH]")); \
++}
++
++/* Same but force premature exit if necessary. */
++#define FLUSH_BLOCK(s, eof) { \
++ FLUSH_BLOCK_ONLY(s, eof); \
++ if (s->strm->avail_out == 0) return (eof) ? finish_started : need_more; \
++}
++
++/* ===========================================================================
++ * Copy without compression as much as possible from the input stream, return
++ * the current block state.
++ * This function does not insert new strings in the dictionary since
++ * uncompressible data is probably not useful. This function is used
++ * only for the level=0 compression option.
++ * NOTE: this function should be optimized to avoid extra copying from
++ * window to pending_buf.
++ */
++local block_state deflate_stored(s, flush)
++ deflate_state *s;
++ int flush;
++{
++ /* Stored blocks are limited to 0xffff bytes, pending_buf is limited
++ * to pending_buf_size, and each stored block has a 5 byte header:
++ */
++ ulg max_block_size = 0xffff;
++ ulg max_start;
++
++ if (max_block_size > s->pending_buf_size - 5) {
++ max_block_size = s->pending_buf_size - 5;
++ }
++
++ /* Copy as much as possible from input to output: */
++ for (;;) {
++ /* Fill the window as much as possible: */
++ if (s->lookahead <= 1) {
++
++ Assert(s->strstart < s->w_size+MAX_DIST(s) ||
++ s->block_start >= (long)s->w_size, "slide too late");
++
++ fill_window(s);
++ if (s->lookahead == 0 && flush == Z_NO_FLUSH) return need_more;
++
++ if (s->lookahead == 0) break; /* flush the current block */
++ }
++ Assert(s->block_start >= 0L, "block gone");
++
++ s->strstart += s->lookahead;
++ s->lookahead = 0;
++
++ /* Emit a stored block if pending_buf will be full: */
++ max_start = s->block_start + max_block_size;
++ if (s->strstart == 0 || (ulg)s->strstart >= max_start) {
++ /* strstart == 0 is possible when wraparound on 16-bit machine */
++ s->lookahead = (uInt)(s->strstart - max_start);
++ s->strstart = (uInt)max_start;
++ FLUSH_BLOCK(s, 0);
++ }
++ /* Flush if we may have to slide, otherwise block_start may become
++ * negative and the data will be gone:
++ */
++ if (s->strstart - (uInt)s->block_start >= MAX_DIST(s)) {
++ FLUSH_BLOCK(s, 0);
++ }
++ }
++ FLUSH_BLOCK(s, flush == Z_FINISH);
++ return flush == Z_FINISH ? finish_done : block_done;
++}
++
++/* ===========================================================================
++ * Compress as much as possible from the input stream, return the current
++ * block state.
++ * This function does not perform lazy evaluation of matches and inserts
++ * new strings in the dictionary only for unmatched strings or for short
++ * matches. It is used only for the fast compression options.
++ */
++local block_state deflate_fast(s, flush)
++ deflate_state *s;
++ int flush;
++{
++ IPos hash_head = NIL; /* head of the hash chain */
++ int bflush; /* set if current block must be flushed */
++
++ for (;;) {
++ /* Make sure that we always have enough lookahead, except
++ * at the end of the input file. We need MAX_MATCH bytes
++ * for the next match, plus MIN_MATCH bytes to insert the
++ * string following the next match.
++ */
++ if (s->lookahead < MIN_LOOKAHEAD) {
++ fill_window(s);
++ if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) {
++ return need_more;
++ }
++ if (s->lookahead == 0) break; /* flush the current block */
++ }
++
++ /* Insert the string window[strstart .. strstart+2] in the
++ * dictionary, and set hash_head to the head of the hash chain:
++ */
++ if (s->lookahead >= MIN_MATCH) {
++ INSERT_STRING(s, s->strstart, hash_head);
++ }
++
++ /* Find the longest match, discarding those <= prev_length.
++ * At this point we have always match_length < MIN_MATCH
++ */
++ if (hash_head != NIL && s->strstart - hash_head <= MAX_DIST(s)) {
++ /* To simplify the code, we prevent matches with the string
++ * of window index 0 (in particular we have to avoid a match
++ * of the string with itself at the start of the input file).
++ */
++ if (s->strategy != Z_HUFFMAN_ONLY) {
++ s->match_length = longest_match (s, hash_head);
++ }
++ /* longest_match() sets match_start */
++ }
++ if (s->match_length >= MIN_MATCH) {
++ check_match(s, s->strstart, s->match_start, s->match_length);
++
++ _tr_tally_dist(s, s->strstart - s->match_start,
++ s->match_length - MIN_MATCH, bflush);
++
++ s->lookahead -= s->match_length;
++
++ /* Insert new strings in the hash table only if the match length
++ * is not too large. This saves time but degrades compression.
++ */
++#ifndef FASTEST
++ if (s->match_length <= s->max_insert_length &&
++ s->lookahead >= MIN_MATCH) {
++ s->match_length--; /* string at strstart already in hash table */
++ do {
++ s->strstart++;
++ INSERT_STRING(s, s->strstart, hash_head);
++ /* strstart never exceeds WSIZE-MAX_MATCH, so there are
++ * always MIN_MATCH bytes ahead.
++ */
++ } while (--s->match_length != 0);
++ s->strstart++;
++ } else
++#endif
++ {
++ s->strstart += s->match_length;
++ s->match_length = 0;
++ s->ins_h = s->window[s->strstart];
++ UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]);
++#if MIN_MATCH != 3
++ Call UPDATE_HASH() MIN_MATCH-3 more times
++#endif
++ /* If lookahead < MIN_MATCH, ins_h is garbage, but it does not
++ * matter since it will be recomputed at next deflate call.
++ */
++ }
++ } else {
++ /* No match, output a literal byte */
++ Tracevv((stderr,"%c", s->window[s->strstart]));
++ _tr_tally_lit (s, s->window[s->strstart], bflush);
++ s->lookahead--;
++ s->strstart++;
++ }
++ if (bflush) FLUSH_BLOCK(s, 0);
++ }
++ FLUSH_BLOCK(s, flush == Z_FINISH);
++ return flush == Z_FINISH ? finish_done : block_done;
++}
++
++/* ===========================================================================
++ * Same as above, but achieves better compression. We use a lazy
++ * evaluation for matches: a match is finally adopted only if there is
++ * no better match at the next window position.
++ */
++local block_state deflate_slow(s, flush)
++ deflate_state *s;
++ int flush;
++{
++ IPos hash_head = NIL; /* head of hash chain */
++ int bflush; /* set if current block must be flushed */
++
++ /* Process the input block. */
++ for (;;) {
++ /* Make sure that we always have enough lookahead, except
++ * at the end of the input file. We need MAX_MATCH bytes
++ * for the next match, plus MIN_MATCH bytes to insert the
++ * string following the next match.
++ */
++ if (s->lookahead < MIN_LOOKAHEAD) {
++ fill_window(s);
++ if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) {
++ return need_more;
++ }
++ if (s->lookahead == 0) break; /* flush the current block */
++ }
++
++ /* Insert the string window[strstart .. strstart+2] in the
++ * dictionary, and set hash_head to the head of the hash chain:
++ */
++ if (s->lookahead >= MIN_MATCH) {
++ INSERT_STRING(s, s->strstart, hash_head);
++ }
++
++ /* Find the longest match, discarding those <= prev_length.
++ */
++ s->prev_length = s->match_length, s->prev_match = s->match_start;
++ s->match_length = MIN_MATCH-1;
++
++ if (hash_head != NIL && s->prev_length < s->max_lazy_match &&
++ s->strstart - hash_head <= MAX_DIST(s)) {
++ /* To simplify the code, we prevent matches with the string
++ * of window index 0 (in particular we have to avoid a match
++ * of the string with itself at the start of the input file).
++ */
++ if (s->strategy != Z_HUFFMAN_ONLY) {
++ s->match_length = longest_match (s, hash_head);
++ }
++ /* longest_match() sets match_start */
++
++ if (s->match_length <= 5 && (s->strategy == Z_FILTERED ||
++ (s->match_length == MIN_MATCH &&
++ s->strstart - s->match_start > TOO_FAR))) {
++
++ /* If prev_match is also MIN_MATCH, match_start is garbage
++ * but we will ignore the current match anyway.
++ */
++ s->match_length = MIN_MATCH-1;
++ }
++ }
++ /* If there was a match at the previous step and the current
++ * match is not better, output the previous match:
++ */
++ if (s->prev_length >= MIN_MATCH && s->match_length <= s->prev_length) {
++ uInt max_insert = s->strstart + s->lookahead - MIN_MATCH;
++ /* Do not insert strings in hash table beyond this. */
++
++ check_match(s, s->strstart-1, s->prev_match, s->prev_length);
++
++ _tr_tally_dist(s, s->strstart -1 - s->prev_match,
++ s->prev_length - MIN_MATCH, bflush);
++
++ /* Insert in hash table all strings up to the end of the match.
++ * strstart-1 and strstart are already inserted. If there is not
++ * enough lookahead, the last two strings are not inserted in
++ * the hash table.
++ */
++ s->lookahead -= s->prev_length-1;
++ s->prev_length -= 2;
++ do {
++ if (++s->strstart <= max_insert) {
++ INSERT_STRING(s, s->strstart, hash_head);
++ }
++ } while (--s->prev_length != 0);
++ s->match_available = 0;
++ s->match_length = MIN_MATCH-1;
++ s->strstart++;
++
++ if (bflush) FLUSH_BLOCK(s, 0);
++
++ } else if (s->match_available) {
++ /* If there was no match at the previous position, output a
++ * single literal. If there was a match but the current match
++ * is longer, truncate the previous match to a single literal.
++ */
++ Tracevv((stderr,"%c", s->window[s->strstart-1]));
++ _tr_tally_lit(s, s->window[s->strstart-1], bflush);
++ if (bflush) {
++ FLUSH_BLOCK_ONLY(s, 0);
++ }
++ s->strstart++;
++ s->lookahead--;
++ if (s->strm->avail_out == 0) return need_more;
++ } else {
++ /* There is no previous match to compare with, wait for
++ * the next step to decide.
++ */
++ s->match_available = 1;
++ s->strstart++;
++ s->lookahead--;
++ }
++ }
++ Assert (flush != Z_NO_FLUSH, "no flush?");
++ if (s->match_available) {
++ Tracevv((stderr,"%c", s->window[s->strstart-1]));
++ _tr_tally_lit(s, s->window[s->strstart-1], bflush);
++ s->match_available = 0;
++ }
++ FLUSH_BLOCK(s, flush == Z_FINISH);
++ return flush == Z_FINISH ? finish_done : block_done;
++}
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/zlib/deflate.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,318 @@
++/* deflate.h -- internal compression state
++ * Copyright (C) 1995-2002 Jean-loup Gailly
++ * For conditions of distribution and use, see copyright notice in zlib.h
++ */
++
++/* WARNING: this file should *not* be used by applications. It is
++ part of the implementation of the compression library and is
++ subject to change. Applications should only use zlib.h.
++ */
++
++/* @(#) $Id$ */
++
++#ifndef _DEFLATE_H
++#define _DEFLATE_H
++
++#include "zlib/zutil.h"
++
++/* ===========================================================================
++ * Internal compression state.
++ */
++
++#define LENGTH_CODES 29
++/* number of length codes, not counting the special END_BLOCK code */
++
++#define LITERALS 256
++/* number of literal bytes 0..255 */
++
++#define L_CODES (LITERALS+1+LENGTH_CODES)
++/* number of Literal or Length codes, including the END_BLOCK code */
++
++#define D_CODES 30
++/* number of distance codes */
++
++#define BL_CODES 19
++/* number of codes used to transfer the bit lengths */
++
++#define HEAP_SIZE (2*L_CODES+1)
++/* maximum heap size */
++
++#define MAX_BITS 15
++/* All codes must not exceed MAX_BITS bits */
++
++#define INIT_STATE 42
++#define BUSY_STATE 113
++#define FINISH_STATE 666
++/* Stream status */
++
++
++/* Data structure describing a single value and its code string. */
++typedef struct ct_data_s {
++ union {
++ ush freq; /* frequency count */
++ ush code; /* bit string */
++ } fc;
++ union {
++ ush dad; /* father node in Huffman tree */
++ ush len; /* length of bit string */
++ } dl;
++} FAR ct_data;
++
++#define Freq fc.freq
++#define Code fc.code
++#define Dad dl.dad
++#define Len dl.len
++
++typedef struct static_tree_desc_s static_tree_desc;
++
++typedef struct tree_desc_s {
++ ct_data *dyn_tree; /* the dynamic tree */
++ int max_code; /* largest code with non zero frequency */
++ static_tree_desc *stat_desc; /* the corresponding static tree */
++} FAR tree_desc;
++
++typedef ush Pos;
++typedef Pos FAR Posf;
++typedef unsigned IPos;
++
++/* A Pos is an index in the character window. We use short instead of int to
++ * save space in the various tables. IPos is used only for parameter passing.
++ */
++
++typedef struct internal_state {
++ z_streamp strm; /* pointer back to this zlib stream */
++ int status; /* as the name implies */
++ Bytef *pending_buf; /* output still pending */
++ ulg pending_buf_size; /* size of pending_buf */
++ Bytef *pending_out; /* next pending byte to output to the stream */
++ int pending; /* nb of bytes in the pending buffer */
++ int noheader; /* suppress zlib header and adler32 */
++ Byte data_type; /* UNKNOWN, BINARY or ASCII */
++ Byte method; /* STORED (for zip only) or DEFLATED */
++ int last_flush; /* value of flush param for previous deflate call */
++
++ /* used by deflate.c: */
++
++ uInt w_size; /* LZ77 window size (32K by default) */
++ uInt w_bits; /* log2(w_size) (8..16) */
++ uInt w_mask; /* w_size - 1 */
++
++ Bytef *window;
++ /* Sliding window. Input bytes are read into the second half of the window,
++ * and move to the first half later to keep a dictionary of at least wSize
++ * bytes. With this organization, matches are limited to a distance of
++ * wSize-MAX_MATCH bytes, but this ensures that IO is always
++ * performed with a length multiple of the block size. Also, it limits
++ * the window size to 64K, which is quite useful on MSDOS.
++ * To do: use the user input buffer as sliding window.
++ */
++
++ ulg window_size;
++ /* Actual size of window: 2*wSize, except when the user input buffer
++ * is directly used as sliding window.
++ */
++
++ Posf *prev;
++ /* Link to older string with same hash index. To limit the size of this
++ * array to 64K, this link is maintained only for the last 32K strings.
++ * An index in this array is thus a window index modulo 32K.
++ */
++
++ Posf *head; /* Heads of the hash chains or NIL. */
++
++ uInt ins_h; /* hash index of string to be inserted */
++ uInt hash_size; /* number of elements in hash table */
++ uInt hash_bits; /* log2(hash_size) */
++ uInt hash_mask; /* hash_size-1 */
++
++ uInt hash_shift;
++ /* Number of bits by which ins_h must be shifted at each input
++ * step. It must be such that after MIN_MATCH steps, the oldest
++ * byte no longer takes part in the hash key, that is:
++ * hash_shift * MIN_MATCH >= hash_bits
++ */
++
++ long block_start;
++ /* Window position at the beginning of the current output block. Gets
++ * negative when the window is moved backwards.
++ */
++
++ uInt match_length; /* length of best match */
++ IPos prev_match; /* previous match */
++ int match_available; /* set if previous match exists */
++ uInt strstart; /* start of string to insert */
++ uInt match_start; /* start of matching string */
++ uInt lookahead; /* number of valid bytes ahead in window */
++
++ uInt prev_length;
++ /* Length of the best match at previous step. Matches not greater than this
++ * are discarded. This is used in the lazy match evaluation.
++ */
++
++ uInt max_chain_length;
++ /* To speed up deflation, hash chains are never searched beyond this
++ * length. A higher limit improves compression ratio but degrades the
++ * speed.
++ */
++
++ uInt max_lazy_match;
++ /* Attempt to find a better match only when the current match is strictly
++ * smaller than this value. This mechanism is used only for compression
++ * levels >= 4.
++ */
++# define max_insert_length max_lazy_match
++ /* Insert new strings in the hash table only if the match length is not
++ * greater than this length. This saves time but degrades compression.
++ * max_insert_length is used only for compression levels <= 3.
++ */
++
++ int level; /* compression level (1..9) */
++ int strategy; /* favor or force Huffman coding*/
++
++ uInt good_match;
++ /* Use a faster search when the previous match is longer than this */
++
++ int nice_match; /* Stop searching when current match exceeds this */
++
++ /* used by trees.c: */
++ /* Didn't use ct_data typedef below to supress compiler warning */
++ struct ct_data_s dyn_ltree[HEAP_SIZE]; /* literal and length tree */
++ struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */
++ struct ct_data_s bl_tree[2*BL_CODES+1]; /* Huffman tree for bit lengths */
++
++ struct tree_desc_s l_desc; /* desc. for literal tree */
++ struct tree_desc_s d_desc; /* desc. for distance tree */
++ struct tree_desc_s bl_desc; /* desc. for bit length tree */
++
++ ush bl_count[MAX_BITS+1];
++ /* number of codes at each bit length for an optimal tree */
++
++ int heap[2*L_CODES+1]; /* heap used to build the Huffman trees */
++ int heap_len; /* number of elements in the heap */
++ int heap_max; /* element of largest frequency */
++ /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used.
++ * The same heap array is used to build all trees.
++ */
++
++ uch depth[2*L_CODES+1];
++ /* Depth of each subtree used as tie breaker for trees of equal frequency
++ */
++
++ uchf *l_buf; /* buffer for literals or lengths */
++
++ uInt lit_bufsize;
++ /* Size of match buffer for literals/lengths. There are 4 reasons for
++ * limiting lit_bufsize to 64K:
++ * - frequencies can be kept in 16 bit counters
++ * - if compression is not successful for the first block, all input
++ * data is still in the window so we can still emit a stored block even
++ * when input comes from standard input. (This can also be done for
++ * all blocks if lit_bufsize is not greater than 32K.)
++ * - if compression is not successful for a file smaller than 64K, we can
++ * even emit a stored file instead of a stored block (saving 5 bytes).
++ * This is applicable only for zip (not gzip or zlib).
++ * - creating new Huffman trees less frequently may not provide fast
++ * adaptation to changes in the input data statistics. (Take for
++ * example a binary file with poorly compressible code followed by
++ * a highly compressible string table.) Smaller buffer sizes give
++ * fast adaptation but have of course the overhead of transmitting
++ * trees more frequently.
++ * - I can't count above 4
++ */
++
++ uInt last_lit; /* running index in l_buf */
++
++ ushf *d_buf;
++ /* Buffer for distances. To simplify the code, d_buf and l_buf have
++ * the same number of elements. To use different lengths, an extra flag
++ * array would be necessary.
++ */
++
++ ulg opt_len; /* bit length of current block with optimal trees */
++ ulg static_len; /* bit length of current block with static trees */
++ uInt matches; /* number of string matches in current block */
++ int last_eob_len; /* bit length of EOB code for last block */
++
++#ifdef DEBUG
++ ulg compressed_len; /* total bit length of compressed file mod 2^32 */
++ ulg bits_sent; /* bit length of compressed data sent mod 2^32 */
++#endif
++
++ ush bi_buf;
++ /* Output buffer. bits are inserted starting at the bottom (least
++ * significant bits).
++ */
++ int bi_valid;
++ /* Number of valid bits in bi_buf. All bits above the last valid bit
++ * are always zero.
++ */
++
++} FAR deflate_state;
++
++/* Output a byte on the stream.
++ * IN assertion: there is enough room in pending_buf.
++ */
++#define put_byte(s, c) {s->pending_buf[s->pending++] = (c);}
++
++
++#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1)
++/* Minimum amount of lookahead, except at the end of the input file.
++ * See deflate.c for comments about the MIN_MATCH+1.
++ */
++
++#define MAX_DIST(s) ((s)->w_size-MIN_LOOKAHEAD)
++/* In order to simplify the code, particularly on 16 bit machines, match
++ * distances are limited to MAX_DIST instead of WSIZE.
++ */
++
++ /* in trees.c */
++void _tr_init OF((deflate_state *s));
++int _tr_tally OF((deflate_state *s, unsigned dist, unsigned lc));
++void _tr_flush_block OF((deflate_state *s, charf *buf, ulg stored_len,
++ int eof));
++void _tr_align OF((deflate_state *s));
++void _tr_stored_block OF((deflate_state *s, charf *buf, ulg stored_len,
++ int eof));
++
++#define d_code(dist) \
++ ((dist) < 256 ? _dist_code[dist] : _dist_code[256+((dist)>>7)])
++/* Mapping from a distance to a distance code. dist is the distance - 1 and
++ * must not have side effects. _dist_code[256] and _dist_code[257] are never
++ * used.
++ */
++
++#ifndef DEBUG
++/* Inline versions of _tr_tally for speed: */
++
++#if defined(GEN_TREES_H) || !defined(STDC)
++ extern uch _length_code[];
++ extern uch _dist_code[];
++#else
++ extern const uch _length_code[];
++ extern const uch _dist_code[];
++#endif
++
++# define _tr_tally_lit(s, c, flush) \
++ { uch cc = (c); \
++ s->d_buf[s->last_lit] = 0; \
++ s->l_buf[s->last_lit++] = cc; \
++ s->dyn_ltree[cc].Freq++; \
++ flush = (s->last_lit == s->lit_bufsize-1); \
++ }
++# define _tr_tally_dist(s, distance, length, flush) \
++ { uch len = (length); \
++ ush dist = (distance); \
++ s->d_buf[s->last_lit] = dist; \
++ s->l_buf[s->last_lit++] = len; \
++ dist--; \
++ s->dyn_ltree[_length_code[len]+LITERALS+1].Freq++; \
++ s->dyn_dtree[d_code(dist)].Freq++; \
++ flush = (s->last_lit == s->lit_bufsize-1); \
++ }
++#else
++# define _tr_tally_lit(s, c, flush) flush = _tr_tally(s, 0, c)
++# define _tr_tally_dist(s, distance, length, flush) \
++ flush = _tr_tally(s, distance, length)
++#endif
++
++#endif /* _DEFLATE_H */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/zlib/infblock.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,403 @@
++/* infblock.c -- interpret and process block types to last block
++ * Copyright (C) 1995-2002 Mark Adler
++ * For conditions of distribution and use, see copyright notice in zlib.h
++ */
++
++#include <zlib/zutil.h>
++#include "infblock.h"
++#include "inftrees.h"
++#include "infcodes.h"
++#include "infutil.h"
++
++struct inflate_codes_state {int dummy;}; /* for buggy compilers */
++
++/* simplify the use of the inflate_huft type with some defines */
++#define exop word.what.Exop
++#define bits word.what.Bits
++
++/* Table for deflate from PKZIP's appnote.txt. */
++local const uInt border[] = { /* Order of the bit length code lengths */
++ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15};
++
++/*
++ Notes beyond the 1.93a appnote.txt:
++
++ 1. Distance pointers never point before the beginning of the output
++ stream.
++ 2. Distance pointers can point back across blocks, up to 32k away.
++ 3. There is an implied maximum of 7 bits for the bit length table and
++ 15 bits for the actual data.
++ 4. If only one code exists, then it is encoded using one bit. (Zero
++ would be more efficient, but perhaps a little confusing.) If two
++ codes exist, they are coded using one bit each (0 and 1).
++ 5. There is no way of sending zero distance codes--a dummy must be
++ sent if there are none. (History: a pre 2.0 version of PKZIP would
++ store blocks with no distance codes, but this was discovered to be
++ too harsh a criterion.) Valid only for 1.93a. 2.04c does allow
++ zero distance codes, which is sent as one code of zero bits in
++ length.
++ 6. There are up to 286 literal/length codes. Code 256 represents the
++ end-of-block. Note however that the static length tree defines
++ 288 codes just to fill out the Huffman codes. Codes 286 and 287
++ cannot be used though, since there is no length base or extra bits
++ defined for them. Similarily, there are up to 30 distance codes.
++ However, static trees define 32 codes (all 5 bits) to fill out the
++ Huffman codes, but the last two had better not show up in the data.
++ 7. Unzip can check dynamic Huffman blocks for complete code sets.
++ The exception is that a single code would not be complete (see #4).
++ 8. The five bits following the block type is really the number of
++ literal codes sent minus 257.
++ 9. Length codes 8,16,16 are interpreted as 13 length codes of 8 bits
++ (1+6+6). Therefore, to output three times the length, you output
++ three codes (1+1+1), whereas to output four times the same length,
++ you only need two codes (1+3). Hmm.
++ 10. In the tree reconstruction algorithm, Code = Code + Increment
++ only if BitLength(i) is not zero. (Pretty obvious.)
++ 11. Correction: 4 Bits: # of Bit Length codes - 4 (4 - 19)
++ 12. Note: length code 284 can represent 227-258, but length code 285
++ really is 258. The last length deserves its own, short code
++ since it gets used a lot in very redundant files. The length
++ 258 is special since 258 - 3 (the min match length) is 255.
++ 13. The literal/length and distance code bit lengths are read as a
++ single stream of lengths. It is possible (and advantageous) for
++ a repeat code (16, 17, or 18) to go across the boundary between
++ the two sets of lengths.
++ */
++
++
++void inflate_blocks_reset(s, z, c)
++inflate_blocks_statef *s;
++z_streamp z;
++uLongf *c;
++{
++ if (c != Z_NULL)
++ *c = s->check;
++ if (s->mode == BTREE || s->mode == DTREE)
++ ZFREE(z, s->sub.trees.blens);
++ if (s->mode == CODES)
++ inflate_codes_free(s->sub.decode.codes, z);
++ s->mode = TYPE;
++ s->bitk = 0;
++ s->bitb = 0;
++ s->read = s->write = s->window;
++ if (s->checkfn != Z_NULL)
++ z->adler = s->check = (*s->checkfn)(0L, (const Bytef *)Z_NULL, 0);
++ Tracev((stderr, "inflate: blocks reset\n"));
++}
++
++
++inflate_blocks_statef *inflate_blocks_new(z, c, w)
++z_streamp z;
++check_func c;
++uInt w;
++{
++ inflate_blocks_statef *s;
++
++ if ((s = (inflate_blocks_statef *)ZALLOC
++ (z,1,sizeof(struct inflate_blocks_state))) == Z_NULL)
++ return s;
++ if ((s->hufts =
++ (inflate_huft *)ZALLOC(z, sizeof(inflate_huft), MANY)) == Z_NULL)
++ {
++ ZFREE(z, s);
++ return Z_NULL;
++ }
++ if ((s->window = (Bytef *)ZALLOC(z, 1, w)) == Z_NULL)
++ {
++ ZFREE(z, s->hufts);
++ ZFREE(z, s);
++ return Z_NULL;
++ }
++ s->end = s->window + w;
++ s->checkfn = c;
++ s->mode = TYPE;
++ Tracev((stderr, "inflate: blocks allocated\n"));
++ inflate_blocks_reset(s, z, Z_NULL);
++ return s;
++}
++
++
++int inflate_blocks(s, z, r)
++inflate_blocks_statef *s;
++z_streamp z;
++int r;
++{
++ uInt t; /* temporary storage */
++ uLong b; /* bit buffer */
++ uInt k; /* bits in bit buffer */
++ Bytef *p; /* input data pointer */
++ uInt n; /* bytes available there */
++ Bytef *q; /* output window write pointer */
++ uInt m; /* bytes to end of window or read pointer */
++
++ /* copy input/output information to locals (UPDATE macro restores) */
++ LOAD
++
++ /* process input based on current state */
++ while (1) switch (s->mode)
++ {
++ case TYPE:
++ NEEDBITS(3)
++ t = (uInt)b & 7;
++ s->last = t & 1;
++ switch (t >> 1)
++ {
++ case 0: /* stored */
++ Tracev((stderr, "inflate: stored block%s\n",
++ s->last ? " (last)" : ""));
++ DUMPBITS(3)
++ t = k & 7; /* go to byte boundary */
++ DUMPBITS(t)
++ s->mode = LENS; /* get length of stored block */
++ break;
++ case 1: /* fixed */
++ Tracev((stderr, "inflate: fixed codes block%s\n",
++ s->last ? " (last)" : ""));
++ {
++ uInt bl, bd;
++ inflate_huft *tl, *td;
++
++ inflate_trees_fixed(&bl, &bd, &tl, &td, z);
++ s->sub.decode.codes = inflate_codes_new(bl, bd, tl, td, z);
++ if (s->sub.decode.codes == Z_NULL)
++ {
++ r = Z_MEM_ERROR;
++ LEAVE
++ }
++ }
++ DUMPBITS(3)
++ s->mode = CODES;
++ break;
++ case 2: /* dynamic */
++ Tracev((stderr, "inflate: dynamic codes block%s\n",
++ s->last ? " (last)" : ""));
++ DUMPBITS(3)
++ s->mode = TABLE;
++ break;
++ case 3: /* illegal */
++ DUMPBITS(3)
++ s->mode = BAD;
++ z->msg = (char*)"invalid block type";
++ r = Z_DATA_ERROR;
++ LEAVE
++ }
++ break;
++ case LENS:
++ NEEDBITS(32)
++ if ((((~b) >> 16) & 0xffff) != (b & 0xffff))
++ {
++ s->mode = BAD;
++ z->msg = (char*)"invalid stored block lengths";
++ r = Z_DATA_ERROR;
++ LEAVE
++ }
++ s->sub.left = (uInt)b & 0xffff;
++ b = k = 0; /* dump bits */
++ Tracev((stderr, "inflate: stored length %u\n", s->sub.left));
++ s->mode = s->sub.left ? STORED : (s->last ? DRY : TYPE);
++ break;
++ case STORED:
++ if (n == 0)
++ LEAVE
++ NEEDOUT
++ t = s->sub.left;
++ if (t > n) t = n;
++ if (t > m) t = m;
++ zmemcpy(q, p, t);
++ p += t; n -= t;
++ q += t; m -= t;
++ if ((s->sub.left -= t) != 0)
++ break;
++ Tracev((stderr, "inflate: stored end, %lu total out\n",
++ z->total_out + (q >= s->read ? q - s->read :
++ (s->end - s->read) + (q - s->window))));
++ s->mode = s->last ? DRY : TYPE;
++ break;
++ case TABLE:
++ NEEDBITS(14)
++ s->sub.trees.table = t = (uInt)b & 0x3fff;
++#ifndef PKZIP_BUG_WORKAROUND
++ if ((t & 0x1f) > 29 || ((t >> 5) & 0x1f) > 29)
++ {
++ s->mode = BAD;
++ z->msg = (char*)"too many length or distance symbols";
++ r = Z_DATA_ERROR;
++ LEAVE
++ }
++#endif
++ t = 258 + (t & 0x1f) + ((t >> 5) & 0x1f);
++ if ((s->sub.trees.blens = (uIntf*)ZALLOC(z, t, sizeof(uInt))) == Z_NULL)
++ {
++ r = Z_MEM_ERROR;
++ LEAVE
++ }
++ DUMPBITS(14)
++ s->sub.trees.index = 0;
++ Tracev((stderr, "inflate: table sizes ok\n"));
++ s->mode = BTREE;
++ case BTREE:
++ while (s->sub.trees.index < 4 + (s->sub.trees.table >> 10))
++ {
++ NEEDBITS(3)
++ s->sub.trees.blens[border[s->sub.trees.index++]] = (uInt)b & 7;
++ DUMPBITS(3)
++ }
++ while (s->sub.trees.index < 19)
++ s->sub.trees.blens[border[s->sub.trees.index++]] = 0;
++ s->sub.trees.bb = 7;
++ t = inflate_trees_bits(s->sub.trees.blens, &s->sub.trees.bb,
++ &s->sub.trees.tb, s->hufts, z);
++ if (t != Z_OK)
++ {
++ r = t;
++ if (r == Z_DATA_ERROR)
++ {
++ ZFREE(z, s->sub.trees.blens);
++ s->mode = BAD;
++ }
++ LEAVE
++ }
++ s->sub.trees.index = 0;
++ Tracev((stderr, "inflate: bits tree ok\n"));
++ s->mode = DTREE;
++ case DTREE:
++ while (t = s->sub.trees.table,
++ s->sub.trees.index < 258 + (t & 0x1f) + ((t >> 5) & 0x1f))
++ {
++ inflate_huft *h;
++ uInt i, j, c;
++
++ t = s->sub.trees.bb;
++ NEEDBITS(t)
++ h = s->sub.trees.tb + ((uInt)b & inflate_mask[t]);
++ t = h->bits;
++ c = h->base;
++ if (c < 16)
++ {
++ DUMPBITS(t)
++ s->sub.trees.blens[s->sub.trees.index++] = c;
++ }
++ else /* c == 16..18 */
++ {
++ i = c == 18 ? 7 : c - 14;
++ j = c == 18 ? 11 : 3;
++ NEEDBITS(t + i)
++ DUMPBITS(t)
++ j += (uInt)b & inflate_mask[i];
++ DUMPBITS(i)
++ i = s->sub.trees.index;
++ t = s->sub.trees.table;
++ if (i + j > 258 + (t & 0x1f) + ((t >> 5) & 0x1f) ||
++ (c == 16 && i < 1))
++ {
++ ZFREE(z, s->sub.trees.blens);
++ s->mode = BAD;
++ z->msg = (char*)"invalid bit length repeat";
++ r = Z_DATA_ERROR;
++ LEAVE
++ }
++ c = c == 16 ? s->sub.trees.blens[i - 1] : 0;
++ do {
++ s->sub.trees.blens[i++] = c;
++ } while (--j);
++ s->sub.trees.index = i;
++ }
++ }
++ s->sub.trees.tb = Z_NULL;
++ {
++ uInt bl, bd;
++ inflate_huft *tl, *td;
++ inflate_codes_statef *c;
++
++ bl = 9; /* must be <= 9 for lookahead assumptions */
++ bd = 6; /* must be <= 9 for lookahead assumptions */
++ t = s->sub.trees.table;
++ t = inflate_trees_dynamic(257 + (t & 0x1f), 1 + ((t >> 5) & 0x1f),
++ s->sub.trees.blens, &bl, &bd, &tl, &td,
++ s->hufts, z);
++ if (t != Z_OK)
++ {
++ if (t == (uInt)Z_DATA_ERROR)
++ {
++ ZFREE(z, s->sub.trees.blens);
++ s->mode = BAD;
++ }
++ r = t;
++ LEAVE
++ }
++ Tracev((stderr, "inflate: trees ok\n"));
++ if ((c = inflate_codes_new(bl, bd, tl, td, z)) == Z_NULL)
++ {
++ r = Z_MEM_ERROR;
++ LEAVE
++ }
++ s->sub.decode.codes = c;
++ }
++ ZFREE(z, s->sub.trees.blens);
++ s->mode = CODES;
++ case CODES:
++ UPDATE
++ if ((r = inflate_codes(s, z, r)) != Z_STREAM_END)
++ return inflate_flush(s, z, r);
++ r = Z_OK;
++ inflate_codes_free(s->sub.decode.codes, z);
++ LOAD
++ Tracev((stderr, "inflate: codes end, %lu total out\n",
++ z->total_out + (q >= s->read ? q - s->read :
++ (s->end - s->read) + (q - s->window))));
++ if (!s->last)
++ {
++ s->mode = TYPE;
++ break;
++ }
++ s->mode = DRY;
++ case DRY:
++ FLUSH
++ if (s->read != s->write)
++ LEAVE
++ s->mode = DONE;
++ case DONE:
++ r = Z_STREAM_END;
++ LEAVE
++ case BAD:
++ r = Z_DATA_ERROR;
++ LEAVE
++ default:
++ r = Z_STREAM_ERROR;
++ LEAVE
++ }
++}
++
++
++int inflate_blocks_free(s, z)
++inflate_blocks_statef *s;
++z_streamp z;
++{
++ inflate_blocks_reset(s, z, Z_NULL);
++ ZFREE(z, s->window);
++ ZFREE(z, s->hufts);
++ ZFREE(z, s);
++ Tracev((stderr, "inflate: blocks freed\n"));
++ return Z_OK;
++}
++
++
++void inflate_set_dictionary(s, d, n)
++inflate_blocks_statef *s;
++const Bytef *d;
++uInt n;
++{
++ zmemcpy(s->window, d, n);
++ s->read = s->write = s->window + n;
++}
++
++
++/* Returns true if inflate is currently at the end of a block generated
++ * by Z_SYNC_FLUSH or Z_FULL_FLUSH.
++ * IN assertion: s != Z_NULL
++ */
++int inflate_blocks_sync_point(s)
++inflate_blocks_statef *s;
++{
++ return s->mode == LENS;
++}
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/zlib/infblock.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,39 @@
++/* infblock.h -- header to use infblock.c
++ * Copyright (C) 1995-2002 Mark Adler
++ * For conditions of distribution and use, see copyright notice in zlib.h
++ */
++
++/* WARNING: this file should *not* be used by applications. It is
++ part of the implementation of the compression library and is
++ subject to change. Applications should only use zlib.h.
++ */
++
++struct inflate_blocks_state;
++typedef struct inflate_blocks_state FAR inflate_blocks_statef;
++
++extern inflate_blocks_statef * inflate_blocks_new OF((
++ z_streamp z,
++ check_func c, /* check function */
++ uInt w)); /* window size */
++
++extern int inflate_blocks OF((
++ inflate_blocks_statef *,
++ z_streamp ,
++ int)); /* initial return code */
++
++extern void inflate_blocks_reset OF((
++ inflate_blocks_statef *,
++ z_streamp ,
++ uLongf *)); /* check value on output */
++
++extern int inflate_blocks_free OF((
++ inflate_blocks_statef *,
++ z_streamp));
++
++extern void inflate_set_dictionary OF((
++ inflate_blocks_statef *s,
++ const Bytef *d, /* dictionary */
++ uInt n)); /* dictionary length */
++
++extern int inflate_blocks_sync_point OF((
++ inflate_blocks_statef *s));
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/zlib/infcodes.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,251 @@
++/* infcodes.c -- process literals and length/distance pairs
++ * Copyright (C) 1995-2002 Mark Adler
++ * For conditions of distribution and use, see copyright notice in zlib.h
++ */
++
++#include <zlib/zutil.h>
++#include "inftrees.h"
++#include "infblock.h"
++#include "infcodes.h"
++#include "infutil.h"
++#include "inffast.h"
++
++/* simplify the use of the inflate_huft type with some defines */
++#define exop word.what.Exop
++#define bits word.what.Bits
++
++typedef enum { /* waiting for "i:"=input, "o:"=output, "x:"=nothing */
++ START, /* x: set up for LEN */
++ LEN, /* i: get length/literal/eob next */
++ LENEXT, /* i: getting length extra (have base) */
++ DIST, /* i: get distance next */
++ DISTEXT, /* i: getting distance extra */
++ COPY, /* o: copying bytes in window, waiting for space */
++ LIT, /* o: got literal, waiting for output space */
++ WASH, /* o: got eob, possibly still output waiting */
++ END, /* x: got eob and all data flushed */
++ BADCODE} /* x: got error */
++inflate_codes_mode;
++
++/* inflate codes private state */
++struct inflate_codes_state {
++
++ /* mode */
++ inflate_codes_mode mode; /* current inflate_codes mode */
++
++ /* mode dependent information */
++ uInt len;
++ union {
++ struct {
++ inflate_huft *tree; /* pointer into tree */
++ uInt need; /* bits needed */
++ } code; /* if LEN or DIST, where in tree */
++ uInt lit; /* if LIT, literal */
++ struct {
++ uInt get; /* bits to get for extra */
++ uInt dist; /* distance back to copy from */
++ } copy; /* if EXT or COPY, where and how much */
++ } sub; /* submode */
++
++ /* mode independent information */
++ Byte lbits; /* ltree bits decoded per branch */
++ Byte dbits; /* dtree bits decoder per branch */
++ inflate_huft *ltree; /* literal/length/eob tree */
++ inflate_huft *dtree; /* distance tree */
++
++};
++
++
++inflate_codes_statef *inflate_codes_new(bl, bd, tl, td, z)
++uInt bl, bd;
++inflate_huft *tl;
++inflate_huft *td; /* need separate declaration for Borland C++ */
++z_streamp z;
++{
++ inflate_codes_statef *c;
++
++ if ((c = (inflate_codes_statef *)
++ ZALLOC(z,1,sizeof(struct inflate_codes_state))) != Z_NULL)
++ {
++ c->mode = START;
++ c->lbits = (Byte)bl;
++ c->dbits = (Byte)bd;
++ c->ltree = tl;
++ c->dtree = td;
++ Tracev((stderr, "inflate: codes new\n"));
++ }
++ return c;
++}
++
++
++int inflate_codes(s, z, r)
++inflate_blocks_statef *s;
++z_streamp z;
++int r;
++{
++ uInt j; /* temporary storage */
++ inflate_huft *t; /* temporary pointer */
++ uInt e; /* extra bits or operation */
++ uLong b; /* bit buffer */
++ uInt k; /* bits in bit buffer */
++ Bytef *p; /* input data pointer */
++ uInt n; /* bytes available there */
++ Bytef *q; /* output window write pointer */
++ uInt m; /* bytes to end of window or read pointer */
++ Bytef *f; /* pointer to copy strings from */
++ inflate_codes_statef *c = s->sub.decode.codes; /* codes state */
++
++ /* copy input/output information to locals (UPDATE macro restores) */
++ LOAD
++
++ /* process input and output based on current state */
++ while (1) switch (c->mode)
++ { /* waiting for "i:"=input, "o:"=output, "x:"=nothing */
++ case START: /* x: set up for LEN */
++#ifndef SLOW
++ if (m >= 258 && n >= 10)
++ {
++ UPDATE
++ r = inflate_fast(c->lbits, c->dbits, c->ltree, c->dtree, s, z);
++ LOAD
++ if (r != Z_OK)
++ {
++ c->mode = r == Z_STREAM_END ? WASH : BADCODE;
++ break;
++ }
++ }
++#endif /* !SLOW */
++ c->sub.code.need = c->lbits;
++ c->sub.code.tree = c->ltree;
++ c->mode = LEN;
++ case LEN: /* i: get length/literal/eob next */
++ j = c->sub.code.need;
++ NEEDBITS(j)
++ t = c->sub.code.tree + ((uInt)b & inflate_mask[j]);
++ DUMPBITS(t->bits)
++ e = (uInt)(t->exop);
++ if (e == 0) /* literal */
++ {
++ c->sub.lit = t->base;
++ Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ?
++ "inflate: literal '%c'\n" :
++ "inflate: literal 0x%02x\n", t->base));
++ c->mode = LIT;
++ break;
++ }
++ if (e & 16) /* length */
++ {
++ c->sub.copy.get = e & 15;
++ c->len = t->base;
++ c->mode = LENEXT;
++ break;
++ }
++ if ((e & 64) == 0) /* next table */
++ {
++ c->sub.code.need = e;
++ c->sub.code.tree = t + t->base;
++ break;
++ }
++ if (e & 32) /* end of block */
++ {
++ Tracevv((stderr, "inflate: end of block\n"));
++ c->mode = WASH;
++ break;
++ }
++ c->mode = BADCODE; /* invalid code */
++ z->msg = (char*)"invalid literal/length code";
++ r = Z_DATA_ERROR;
++ LEAVE
++ case LENEXT: /* i: getting length extra (have base) */
++ j = c->sub.copy.get;
++ NEEDBITS(j)
++ c->len += (uInt)b & inflate_mask[j];
++ DUMPBITS(j)
++ c->sub.code.need = c->dbits;
++ c->sub.code.tree = c->dtree;
++ Tracevv((stderr, "inflate: length %u\n", c->len));
++ c->mode = DIST;
++ case DIST: /* i: get distance next */
++ j = c->sub.code.need;
++ NEEDBITS(j)
++ t = c->sub.code.tree + ((uInt)b & inflate_mask[j]);
++ DUMPBITS(t->bits)
++ e = (uInt)(t->exop);
++ if (e & 16) /* distance */
++ {
++ c->sub.copy.get = e & 15;
++ c->sub.copy.dist = t->base;
++ c->mode = DISTEXT;
++ break;
++ }
++ if ((e & 64) == 0) /* next table */
++ {
++ c->sub.code.need = e;
++ c->sub.code.tree = t + t->base;
++ break;
++ }
++ c->mode = BADCODE; /* invalid code */
++ z->msg = (char*)"invalid distance code";
++ r = Z_DATA_ERROR;
++ LEAVE
++ case DISTEXT: /* i: getting distance extra */
++ j = c->sub.copy.get;
++ NEEDBITS(j)
++ c->sub.copy.dist += (uInt)b & inflate_mask[j];
++ DUMPBITS(j)
++ Tracevv((stderr, "inflate: distance %u\n", c->sub.copy.dist));
++ c->mode = COPY;
++ case COPY: /* o: copying bytes in window, waiting for space */
++ f = q - c->sub.copy.dist;
++ while (f < s->window) /* modulo window size-"while" instead */
++ f += s->end - s->window; /* of "if" handles invalid distances */
++ while (c->len)
++ {
++ NEEDOUT
++ OUTBYTE(*f++)
++ if (f == s->end)
++ f = s->window;
++ c->len--;
++ }
++ c->mode = START;
++ break;
++ case LIT: /* o: got literal, waiting for output space */
++ NEEDOUT
++ OUTBYTE(c->sub.lit)
++ c->mode = START;
++ break;
++ case WASH: /* o: got eob, possibly more output */
++ if (k > 7) /* return unused byte, if any */
++ {
++ Assert(k < 16, "inflate_codes grabbed too many bytes")
++ k -= 8;
++ n++;
++ p--; /* can always return one */
++ }
++ FLUSH
++ if (s->read != s->write)
++ LEAVE
++ c->mode = END;
++ case END:
++ r = Z_STREAM_END;
++ LEAVE
++ case BADCODE: /* x: got error */
++ r = Z_DATA_ERROR;
++ LEAVE
++ default:
++ r = Z_STREAM_ERROR;
++ LEAVE
++ }
++#ifdef NEED_DUMMY_RETURN
++ return Z_STREAM_ERROR; /* Some dumb compilers complain without this */
++#endif
++}
++
++
++void inflate_codes_free(c, z)
++inflate_codes_statef *c;
++z_streamp z;
++{
++ ZFREE(z, c);
++ Tracev((stderr, "inflate: codes free\n"));
++}
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/zlib/infcodes.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,31 @@
++/* infcodes.h -- header to use infcodes.c
++ * Copyright (C) 1995-2002 Mark Adler
++ * For conditions of distribution and use, see copyright notice in zlib.h
++ */
++
++/* WARNING: this file should *not* be used by applications. It is
++ part of the implementation of the compression library and is
++ subject to change. Applications should only use zlib.h.
++ */
++
++#ifndef _INFCODES_H
++#define _INFCODES_H
++
++struct inflate_codes_state;
++typedef struct inflate_codes_state FAR inflate_codes_statef;
++
++extern inflate_codes_statef *inflate_codes_new OF((
++ uInt, uInt,
++ inflate_huft *, inflate_huft *,
++ z_streamp ));
++
++extern int inflate_codes OF((
++ inflate_blocks_statef *,
++ z_streamp ,
++ int));
++
++extern void inflate_codes_free OF((
++ inflate_codes_statef *,
++ z_streamp ));
++
++#endif /* _INFCODES_H */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/zlib/inffast.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,183 @@
++/* inffast.c -- process literals and length/distance pairs fast
++ * Copyright (C) 1995-2002 Mark Adler
++ * For conditions of distribution and use, see copyright notice in zlib.h
++ */
++
++#include <zlib/zutil.h>
++#include "inftrees.h"
++#include "infblock.h"
++#include "infcodes.h"
++#include "infutil.h"
++#include "inffast.h"
++
++struct inflate_codes_state {int dummy;}; /* for buggy compilers */
++
++/* simplify the use of the inflate_huft type with some defines */
++#define exop word.what.Exop
++#define bits word.what.Bits
++
++/* macros for bit input with no checking and for returning unused bytes */
++#define GRABBITS(j) {while(k<(j)){b|=((uLong)NEXTBYTE)<<k;k+=8;}}
++#define UNGRAB {c=z->avail_in-n;c=(k>>3)<c?k>>3:c;n+=c;p-=c;k-=c<<3;}
++
++/* Called with number of bytes left to write in window at least 258
++ (the maximum string length) and number of input bytes available
++ at least ten. The ten bytes are six bytes for the longest length/
++ distance pair plus four bytes for overloading the bit buffer. */
++
++int inflate_fast(bl, bd, tl, td, s, z)
++uInt bl, bd;
++inflate_huft *tl;
++inflate_huft *td; /* need separate declaration for Borland C++ */
++inflate_blocks_statef *s;
++z_streamp z;
++{
++ inflate_huft *t; /* temporary pointer */
++ uInt e; /* extra bits or operation */
++ uLong b; /* bit buffer */
++ uInt k; /* bits in bit buffer */
++ Bytef *p; /* input data pointer */
++ uInt n; /* bytes available there */
++ Bytef *q; /* output window write pointer */
++ uInt m; /* bytes to end of window or read pointer */
++ uInt ml; /* mask for literal/length tree */
++ uInt md; /* mask for distance tree */
++ uInt c; /* bytes to copy */
++ uInt d; /* distance back to copy from */
++ Bytef *r; /* copy source pointer */
++
++ /* load input, output, bit values */
++ LOAD
++
++ /* initialize masks */
++ ml = inflate_mask[bl];
++ md = inflate_mask[bd];
++
++ /* do until not enough input or output space for fast loop */
++ do { /* assume called with m >= 258 && n >= 10 */
++ /* get literal/length code */
++ GRABBITS(20) /* max bits for literal/length code */
++ if ((e = (t = tl + ((uInt)b & ml))->exop) == 0)
++ {
++ DUMPBITS(t->bits)
++ Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ?
++ "inflate: * literal '%c'\n" :
++ "inflate: * literal 0x%02x\n", t->base));
++ *q++ = (Byte)t->base;
++ m--;
++ continue;
++ }
++ do {
++ DUMPBITS(t->bits)
++ if (e & 16)
++ {
++ /* get extra bits for length */
++ e &= 15;
++ c = t->base + ((uInt)b & inflate_mask[e]);
++ DUMPBITS(e)
++ Tracevv((stderr, "inflate: * length %u\n", c));
++
++ /* decode distance base of block to copy */
++ GRABBITS(15); /* max bits for distance code */
++ e = (t = td + ((uInt)b & md))->exop;
++ do {
++ DUMPBITS(t->bits)
++ if (e & 16)
++ {
++ /* get extra bits to add to distance base */
++ e &= 15;
++ GRABBITS(e) /* get extra bits (up to 13) */
++ d = t->base + ((uInt)b & inflate_mask[e]);
++ DUMPBITS(e)
++ Tracevv((stderr, "inflate: * distance %u\n", d));
++
++ /* do the copy */
++ m -= c;
++ r = q - d;
++ if (r < s->window) /* wrap if needed */
++ {
++ do {
++ r += s->end - s->window; /* force pointer in window */
++ } while (r < s->window); /* covers invalid distances */
++ e = s->end - r;
++ if (c > e)
++ {
++ c -= e; /* wrapped copy */
++ do {
++ *q++ = *r++;
++ } while (--e);
++ r = s->window;
++ do {
++ *q++ = *r++;
++ } while (--c);
++ }
++ else /* normal copy */
++ {
++ *q++ = *r++; c--;
++ *q++ = *r++; c--;
++ do {
++ *q++ = *r++;
++ } while (--c);
++ }
++ }
++ else /* normal copy */
++ {
++ *q++ = *r++; c--;
++ *q++ = *r++; c--;
++ do {
++ *q++ = *r++;
++ } while (--c);
++ }
++ break;
++ }
++ else if ((e & 64) == 0)
++ {
++ t += t->base;
++ e = (t += ((uInt)b & inflate_mask[e]))->exop;
++ }
++ else
++ {
++ z->msg = (char*)"invalid distance code";
++ UNGRAB
++ UPDATE
++ return Z_DATA_ERROR;
++ }
++ } while (1);
++ break;
++ }
++ if ((e & 64) == 0)
++ {
++ t += t->base;
++ if ((e = (t += ((uInt)b & inflate_mask[e]))->exop) == 0)
++ {
++ DUMPBITS(t->bits)
++ Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ?
++ "inflate: * literal '%c'\n" :
++ "inflate: * literal 0x%02x\n", t->base));
++ *q++ = (Byte)t->base;
++ m--;
++ break;
++ }
++ }
++ else if (e & 32)
++ {
++ Tracevv((stderr, "inflate: * end of block\n"));
++ UNGRAB
++ UPDATE
++ return Z_STREAM_END;
++ }
++ else
++ {
++ z->msg = (char*)"invalid literal/length code";
++ UNGRAB
++ UPDATE
++ return Z_DATA_ERROR;
++ }
++ } while (1);
++ } while (m >= 258 && n >= 10);
++
++ /* not enough input or output--restore pointers and return */
++ UNGRAB
++ UPDATE
++ return Z_OK;
++}
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/zlib/inffast.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,22 @@
++/* inffast.h -- header to use inffast.c
++ * Copyright (C) 1995-2002 Mark Adler
++ * For conditions of distribution and use, see copyright notice in zlib.h
++ */
++
++/* WARNING: this file should *not* be used by applications. It is
++ part of the implementation of the compression library and is
++ subject to change. Applications should only use zlib.h.
++ */
++
++#ifndef _INFFAST_H
++#define _INFFAST_H
++
++extern int inflate_fast OF((
++ uInt,
++ uInt,
++ inflate_huft *,
++ inflate_huft *,
++ inflate_blocks_statef *,
++ z_streamp ));
++
++#endif /* _INFFAST_H */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/zlib/inffixed.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,151 @@
++/* inffixed.h -- table for decoding fixed codes
++ * Generated automatically by the maketree.c program
++ */
++
++/* WARNING: this file should *not* be used by applications. It is
++ part of the implementation of the compression library and is
++ subject to change. Applications should only use zlib.h.
++ */
++
++local uInt fixed_bl = 9;
++local uInt fixed_bd = 5;
++local inflate_huft fixed_tl[] = {
++ {{{96,7}},256}, {{{0,8}},80}, {{{0,8}},16}, {{{84,8}},115},
++ {{{82,7}},31}, {{{0,8}},112}, {{{0,8}},48}, {{{0,9}},192},
++ {{{80,7}},10}, {{{0,8}},96}, {{{0,8}},32}, {{{0,9}},160},
++ {{{0,8}},0}, {{{0,8}},128}, {{{0,8}},64}, {{{0,9}},224},
++ {{{80,7}},6}, {{{0,8}},88}, {{{0,8}},24}, {{{0,9}},144},
++ {{{83,7}},59}, {{{0,8}},120}, {{{0,8}},56}, {{{0,9}},208},
++ {{{81,7}},17}, {{{0,8}},104}, {{{0,8}},40}, {{{0,9}},176},
++ {{{0,8}},8}, {{{0,8}},136}, {{{0,8}},72}, {{{0,9}},240},
++ {{{80,7}},4}, {{{0,8}},84}, {{{0,8}},20}, {{{85,8}},227},
++ {{{83,7}},43}, {{{0,8}},116}, {{{0,8}},52}, {{{0,9}},200},
++ {{{81,7}},13}, {{{0,8}},100}, {{{0,8}},36}, {{{0,9}},168},
++ {{{0,8}},4}, {{{0,8}},132}, {{{0,8}},68}, {{{0,9}},232},
++ {{{80,7}},8}, {{{0,8}},92}, {{{0,8}},28}, {{{0,9}},152},
++ {{{84,7}},83}, {{{0,8}},124}, {{{0,8}},60}, {{{0,9}},216},
++ {{{82,7}},23}, {{{0,8}},108}, {{{0,8}},44}, {{{0,9}},184},
++ {{{0,8}},12}, {{{0,8}},140}, {{{0,8}},76}, {{{0,9}},248},
++ {{{80,7}},3}, {{{0,8}},82}, {{{0,8}},18}, {{{85,8}},163},
++ {{{83,7}},35}, {{{0,8}},114}, {{{0,8}},50}, {{{0,9}},196},
++ {{{81,7}},11}, {{{0,8}},98}, {{{0,8}},34}, {{{0,9}},164},
++ {{{0,8}},2}, {{{0,8}},130}, {{{0,8}},66}, {{{0,9}},228},
++ {{{80,7}},7}, {{{0,8}},90}, {{{0,8}},26}, {{{0,9}},148},
++ {{{84,7}},67}, {{{0,8}},122}, {{{0,8}},58}, {{{0,9}},212},
++ {{{82,7}},19}, {{{0,8}},106}, {{{0,8}},42}, {{{0,9}},180},
++ {{{0,8}},10}, {{{0,8}},138}, {{{0,8}},74}, {{{0,9}},244},
++ {{{80,7}},5}, {{{0,8}},86}, {{{0,8}},22}, {{{192,8}},0},
++ {{{83,7}},51}, {{{0,8}},118}, {{{0,8}},54}, {{{0,9}},204},
++ {{{81,7}},15}, {{{0,8}},102}, {{{0,8}},38}, {{{0,9}},172},
++ {{{0,8}},6}, {{{0,8}},134}, {{{0,8}},70}, {{{0,9}},236},
++ {{{80,7}},9}, {{{0,8}},94}, {{{0,8}},30}, {{{0,9}},156},
++ {{{84,7}},99}, {{{0,8}},126}, {{{0,8}},62}, {{{0,9}},220},
++ {{{82,7}},27}, {{{0,8}},110}, {{{0,8}},46}, {{{0,9}},188},
++ {{{0,8}},14}, {{{0,8}},142}, {{{0,8}},78}, {{{0,9}},252},
++ {{{96,7}},256}, {{{0,8}},81}, {{{0,8}},17}, {{{85,8}},131},
++ {{{82,7}},31}, {{{0,8}},113}, {{{0,8}},49}, {{{0,9}},194},
++ {{{80,7}},10}, {{{0,8}},97}, {{{0,8}},33}, {{{0,9}},162},
++ {{{0,8}},1}, {{{0,8}},129}, {{{0,8}},65}, {{{0,9}},226},
++ {{{80,7}},6}, {{{0,8}},89}, {{{0,8}},25}, {{{0,9}},146},
++ {{{83,7}},59}, {{{0,8}},121}, {{{0,8}},57}, {{{0,9}},210},
++ {{{81,7}},17}, {{{0,8}},105}, {{{0,8}},41}, {{{0,9}},178},
++ {{{0,8}},9}, {{{0,8}},137}, {{{0,8}},73}, {{{0,9}},242},
++ {{{80,7}},4}, {{{0,8}},85}, {{{0,8}},21}, {{{80,8}},258},
++ {{{83,7}},43}, {{{0,8}},117}, {{{0,8}},53}, {{{0,9}},202},
++ {{{81,7}},13}, {{{0,8}},101}, {{{0,8}},37}, {{{0,9}},170},
++ {{{0,8}},5}, {{{0,8}},133}, {{{0,8}},69}, {{{0,9}},234},
++ {{{80,7}},8}, {{{0,8}},93}, {{{0,8}},29}, {{{0,9}},154},
++ {{{84,7}},83}, {{{0,8}},125}, {{{0,8}},61}, {{{0,9}},218},
++ {{{82,7}},23}, {{{0,8}},109}, {{{0,8}},45}, {{{0,9}},186},
++ {{{0,8}},13}, {{{0,8}},141}, {{{0,8}},77}, {{{0,9}},250},
++ {{{80,7}},3}, {{{0,8}},83}, {{{0,8}},19}, {{{85,8}},195},
++ {{{83,7}},35}, {{{0,8}},115}, {{{0,8}},51}, {{{0,9}},198},
++ {{{81,7}},11}, {{{0,8}},99}, {{{0,8}},35}, {{{0,9}},166},
++ {{{0,8}},3}, {{{0,8}},131}, {{{0,8}},67}, {{{0,9}},230},
++ {{{80,7}},7}, {{{0,8}},91}, {{{0,8}},27}, {{{0,9}},150},
++ {{{84,7}},67}, {{{0,8}},123}, {{{0,8}},59}, {{{0,9}},214},
++ {{{82,7}},19}, {{{0,8}},107}, {{{0,8}},43}, {{{0,9}},182},
++ {{{0,8}},11}, {{{0,8}},139}, {{{0,8}},75}, {{{0,9}},246},
++ {{{80,7}},5}, {{{0,8}},87}, {{{0,8}},23}, {{{192,8}},0},
++ {{{83,7}},51}, {{{0,8}},119}, {{{0,8}},55}, {{{0,9}},206},
++ {{{81,7}},15}, {{{0,8}},103}, {{{0,8}},39}, {{{0,9}},174},
++ {{{0,8}},7}, {{{0,8}},135}, {{{0,8}},71}, {{{0,9}},238},
++ {{{80,7}},9}, {{{0,8}},95}, {{{0,8}},31}, {{{0,9}},158},
++ {{{84,7}},99}, {{{0,8}},127}, {{{0,8}},63}, {{{0,9}},222},
++ {{{82,7}},27}, {{{0,8}},111}, {{{0,8}},47}, {{{0,9}},190},
++ {{{0,8}},15}, {{{0,8}},143}, {{{0,8}},79}, {{{0,9}},254},
++ {{{96,7}},256}, {{{0,8}},80}, {{{0,8}},16}, {{{84,8}},115},
++ {{{82,7}},31}, {{{0,8}},112}, {{{0,8}},48}, {{{0,9}},193},
++ {{{80,7}},10}, {{{0,8}},96}, {{{0,8}},32}, {{{0,9}},161},
++ {{{0,8}},0}, {{{0,8}},128}, {{{0,8}},64}, {{{0,9}},225},
++ {{{80,7}},6}, {{{0,8}},88}, {{{0,8}},24}, {{{0,9}},145},
++ {{{83,7}},59}, {{{0,8}},120}, {{{0,8}},56}, {{{0,9}},209},
++ {{{81,7}},17}, {{{0,8}},104}, {{{0,8}},40}, {{{0,9}},177},
++ {{{0,8}},8}, {{{0,8}},136}, {{{0,8}},72}, {{{0,9}},241},
++ {{{80,7}},4}, {{{0,8}},84}, {{{0,8}},20}, {{{85,8}},227},
++ {{{83,7}},43}, {{{0,8}},116}, {{{0,8}},52}, {{{0,9}},201},
++ {{{81,7}},13}, {{{0,8}},100}, {{{0,8}},36}, {{{0,9}},169},
++ {{{0,8}},4}, {{{0,8}},132}, {{{0,8}},68}, {{{0,9}},233},
++ {{{80,7}},8}, {{{0,8}},92}, {{{0,8}},28}, {{{0,9}},153},
++ {{{84,7}},83}, {{{0,8}},124}, {{{0,8}},60}, {{{0,9}},217},
++ {{{82,7}},23}, {{{0,8}},108}, {{{0,8}},44}, {{{0,9}},185},
++ {{{0,8}},12}, {{{0,8}},140}, {{{0,8}},76}, {{{0,9}},249},
++ {{{80,7}},3}, {{{0,8}},82}, {{{0,8}},18}, {{{85,8}},163},
++ {{{83,7}},35}, {{{0,8}},114}, {{{0,8}},50}, {{{0,9}},197},
++ {{{81,7}},11}, {{{0,8}},98}, {{{0,8}},34}, {{{0,9}},165},
++ {{{0,8}},2}, {{{0,8}},130}, {{{0,8}},66}, {{{0,9}},229},
++ {{{80,7}},7}, {{{0,8}},90}, {{{0,8}},26}, {{{0,9}},149},
++ {{{84,7}},67}, {{{0,8}},122}, {{{0,8}},58}, {{{0,9}},213},
++ {{{82,7}},19}, {{{0,8}},106}, {{{0,8}},42}, {{{0,9}},181},
++ {{{0,8}},10}, {{{0,8}},138}, {{{0,8}},74}, {{{0,9}},245},
++ {{{80,7}},5}, {{{0,8}},86}, {{{0,8}},22}, {{{192,8}},0},
++ {{{83,7}},51}, {{{0,8}},118}, {{{0,8}},54}, {{{0,9}},205},
++ {{{81,7}},15}, {{{0,8}},102}, {{{0,8}},38}, {{{0,9}},173},
++ {{{0,8}},6}, {{{0,8}},134}, {{{0,8}},70}, {{{0,9}},237},
++ {{{80,7}},9}, {{{0,8}},94}, {{{0,8}},30}, {{{0,9}},157},
++ {{{84,7}},99}, {{{0,8}},126}, {{{0,8}},62}, {{{0,9}},221},
++ {{{82,7}},27}, {{{0,8}},110}, {{{0,8}},46}, {{{0,9}},189},
++ {{{0,8}},14}, {{{0,8}},142}, {{{0,8}},78}, {{{0,9}},253},
++ {{{96,7}},256}, {{{0,8}},81}, {{{0,8}},17}, {{{85,8}},131},
++ {{{82,7}},31}, {{{0,8}},113}, {{{0,8}},49}, {{{0,9}},195},
++ {{{80,7}},10}, {{{0,8}},97}, {{{0,8}},33}, {{{0,9}},163},
++ {{{0,8}},1}, {{{0,8}},129}, {{{0,8}},65}, {{{0,9}},227},
++ {{{80,7}},6}, {{{0,8}},89}, {{{0,8}},25}, {{{0,9}},147},
++ {{{83,7}},59}, {{{0,8}},121}, {{{0,8}},57}, {{{0,9}},211},
++ {{{81,7}},17}, {{{0,8}},105}, {{{0,8}},41}, {{{0,9}},179},
++ {{{0,8}},9}, {{{0,8}},137}, {{{0,8}},73}, {{{0,9}},243},
++ {{{80,7}},4}, {{{0,8}},85}, {{{0,8}},21}, {{{80,8}},258},
++ {{{83,7}},43}, {{{0,8}},117}, {{{0,8}},53}, {{{0,9}},203},
++ {{{81,7}},13}, {{{0,8}},101}, {{{0,8}},37}, {{{0,9}},171},
++ {{{0,8}},5}, {{{0,8}},133}, {{{0,8}},69}, {{{0,9}},235},
++ {{{80,7}},8}, {{{0,8}},93}, {{{0,8}},29}, {{{0,9}},155},
++ {{{84,7}},83}, {{{0,8}},125}, {{{0,8}},61}, {{{0,9}},219},
++ {{{82,7}},23}, {{{0,8}},109}, {{{0,8}},45}, {{{0,9}},187},
++ {{{0,8}},13}, {{{0,8}},141}, {{{0,8}},77}, {{{0,9}},251},
++ {{{80,7}},3}, {{{0,8}},83}, {{{0,8}},19}, {{{85,8}},195},
++ {{{83,7}},35}, {{{0,8}},115}, {{{0,8}},51}, {{{0,9}},199},
++ {{{81,7}},11}, {{{0,8}},99}, {{{0,8}},35}, {{{0,9}},167},
++ {{{0,8}},3}, {{{0,8}},131}, {{{0,8}},67}, {{{0,9}},231},
++ {{{80,7}},7}, {{{0,8}},91}, {{{0,8}},27}, {{{0,9}},151},
++ {{{84,7}},67}, {{{0,8}},123}, {{{0,8}},59}, {{{0,9}},215},
++ {{{82,7}},19}, {{{0,8}},107}, {{{0,8}},43}, {{{0,9}},183},
++ {{{0,8}},11}, {{{0,8}},139}, {{{0,8}},75}, {{{0,9}},247},
++ {{{80,7}},5}, {{{0,8}},87}, {{{0,8}},23}, {{{192,8}},0},
++ {{{83,7}},51}, {{{0,8}},119}, {{{0,8}},55}, {{{0,9}},207},
++ {{{81,7}},15}, {{{0,8}},103}, {{{0,8}},39}, {{{0,9}},175},
++ {{{0,8}},7}, {{{0,8}},135}, {{{0,8}},71}, {{{0,9}},239},
++ {{{80,7}},9}, {{{0,8}},95}, {{{0,8}},31}, {{{0,9}},159},
++ {{{84,7}},99}, {{{0,8}},127}, {{{0,8}},63}, {{{0,9}},223},
++ {{{82,7}},27}, {{{0,8}},111}, {{{0,8}},47}, {{{0,9}},191},
++ {{{0,8}},15}, {{{0,8}},143}, {{{0,8}},79}, {{{0,9}},255}
++ };
++local inflate_huft fixed_td[] = {
++ {{{80,5}},1}, {{{87,5}},257}, {{{83,5}},17}, {{{91,5}},4097},
++ {{{81,5}},5}, {{{89,5}},1025}, {{{85,5}},65}, {{{93,5}},16385},
++ {{{80,5}},3}, {{{88,5}},513}, {{{84,5}},33}, {{{92,5}},8193},
++ {{{82,5}},9}, {{{90,5}},2049}, {{{86,5}},129}, {{{192,5}},24577},
++ {{{80,5}},2}, {{{87,5}},385}, {{{83,5}},25}, {{{91,5}},6145},
++ {{{81,5}},7}, {{{89,5}},1537}, {{{85,5}},97}, {{{93,5}},24577},
++ {{{80,5}},4}, {{{88,5}},769}, {{{84,5}},49}, {{{92,5}},12289},
++ {{{82,5}},13}, {{{90,5}},3073}, {{{86,5}},193}, {{{192,5}},24577}
++ };
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/zlib/inflate.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,368 @@
++/* inflate.c -- zlib interface to inflate modules
++ * Copyright (C) 1995-2002 Mark Adler
++ * For conditions of distribution and use, see copyright notice in zlib.h
++ */
++
++#include <zlib/zutil.h>
++#include "infblock.h"
++
++struct inflate_blocks_state {int dummy;}; /* for buggy compilers */
++
++typedef enum {
++ METHOD, /* waiting for method byte */
++ FLAG, /* waiting for flag byte */
++ DICT4, /* four dictionary check bytes to go */
++ DICT3, /* three dictionary check bytes to go */
++ DICT2, /* two dictionary check bytes to go */
++ DICT1, /* one dictionary check byte to go */
++ DICT0, /* waiting for inflateSetDictionary */
++ BLOCKS, /* decompressing blocks */
++ CHECK4, /* four check bytes to go */
++ CHECK3, /* three check bytes to go */
++ CHECK2, /* two check bytes to go */
++ CHECK1, /* one check byte to go */
++ DONE, /* finished check, done */
++ BAD} /* got an error--stay here */
++inflate_mode;
++
++/* inflate private state */
++struct internal_state {
++
++ /* mode */
++ inflate_mode mode; /* current inflate mode */
++
++ /* mode dependent information */
++ union {
++ uInt method; /* if FLAGS, method byte */
++ struct {
++ uLong was; /* computed check value */
++ uLong need; /* stream check value */
++ } check; /* if CHECK, check values to compare */
++ uInt marker; /* if BAD, inflateSync's marker bytes count */
++ } sub; /* submode */
++
++ /* mode independent information */
++ int nowrap; /* flag for no wrapper */
++ uInt wbits; /* log2(window size) (8..15, defaults to 15) */
++ inflate_blocks_statef
++ *blocks; /* current inflate_blocks state */
++
++};
++
++
++int ZEXPORT inflateReset(z)
++z_streamp z;
++{
++ if (z == Z_NULL || z->state == Z_NULL)
++ return Z_STREAM_ERROR;
++ z->total_in = z->total_out = 0;
++ z->msg = Z_NULL;
++ z->state->mode = z->state->nowrap ? BLOCKS : METHOD;
++ inflate_blocks_reset(z->state->blocks, z, Z_NULL);
++ Tracev((stderr, "inflate: reset\n"));
++ return Z_OK;
++}
++
++
++int ZEXPORT inflateEnd(z)
++z_streamp z;
++{
++ if (z == Z_NULL || z->state == Z_NULL || z->zfree == Z_NULL)
++ return Z_STREAM_ERROR;
++ if (z->state->blocks != Z_NULL)
++ inflate_blocks_free(z->state->blocks, z);
++ ZFREE(z, z->state);
++ z->state = Z_NULL;
++ Tracev((stderr, "inflate: end\n"));
++ return Z_OK;
++}
++
++
++int ZEXPORT inflateInit2_(z, w, version, stream_size)
++z_streamp z;
++int w;
++const char *version;
++int stream_size;
++{
++ if (version == Z_NULL || version[0] != ZLIB_VERSION[0] ||
++ stream_size != sizeof(z_stream))
++ return Z_VERSION_ERROR;
++
++ /* initialize state */
++ if (z == Z_NULL)
++ return Z_STREAM_ERROR;
++ z->msg = Z_NULL;
++ if (z->zalloc == Z_NULL)
++ {
++ return Z_STREAM_ERROR;
++/* z->zalloc = zcalloc;
++ z->opaque = (voidpf)0;
++*/
++ }
++ if (z->zfree == Z_NULL) return Z_STREAM_ERROR; /* z->zfree = zcfree; */
++ if ((z->state = (struct internal_state FAR *)
++ ZALLOC(z,1,sizeof(struct internal_state))) == Z_NULL)
++ return Z_MEM_ERROR;
++ z->state->blocks = Z_NULL;
++
++ /* handle undocumented nowrap option (no zlib header or check) */
++ z->state->nowrap = 0;
++ if (w < 0)
++ {
++ w = - w;
++ z->state->nowrap = 1;
++ }
++
++ /* set window size */
++ if (w < 8 || w > 15)
++ {
++ inflateEnd(z);
++ return Z_STREAM_ERROR;
++ }
++ z->state->wbits = (uInt)w;
++
++ /* create inflate_blocks state */
++ if ((z->state->blocks =
++ inflate_blocks_new(z, z->state->nowrap ? Z_NULL : adler32, (uInt)1 << w))
++ == Z_NULL)
++ {
++ inflateEnd(z);
++ return Z_MEM_ERROR;
++ }
++ Tracev((stderr, "inflate: allocated\n"));
++
++ /* reset state */
++ inflateReset(z);
++ return Z_OK;
++}
++
++
++int ZEXPORT inflateInit_(z, version, stream_size)
++z_streamp z;
++const char *version;
++int stream_size;
++{
++ return inflateInit2_(z, DEF_WBITS, version, stream_size);
++}
++
++
++#define NEEDBYTE {if(z->avail_in==0)return r;r=f;}
++#define NEXTBYTE (z->avail_in--,z->total_in++,*z->next_in++)
++
++int ZEXPORT inflate(z, f)
++z_streamp z;
++int f;
++{
++ int r;
++ uInt b;
++
++ if (z == Z_NULL || z->state == Z_NULL || z->next_in == Z_NULL)
++ return Z_STREAM_ERROR;
++ f = f == Z_FINISH ? Z_BUF_ERROR : Z_OK;
++ r = Z_BUF_ERROR;
++ while (1) switch (z->state->mode)
++ {
++ case METHOD:
++ NEEDBYTE
++ if (((z->state->sub.method = NEXTBYTE) & 0xf) != Z_DEFLATED)
++ {
++ z->state->mode = BAD;
++ z->msg = (char*)"unknown compression method";
++ z->state->sub.marker = 5; /* can't try inflateSync */
++ break;
++ }
++ if ((z->state->sub.method >> 4) + 8 > z->state->wbits)
++ {
++ z->state->mode = BAD;
++ z->msg = (char*)"invalid window size";
++ z->state->sub.marker = 5; /* can't try inflateSync */
++ break;
++ }
++ z->state->mode = FLAG;
++ case FLAG:
++ NEEDBYTE
++ b = NEXTBYTE;
++ if (((z->state->sub.method << 8) + b) % 31)
++ {
++ z->state->mode = BAD;
++ z->msg = (char*)"incorrect header check";
++ z->state->sub.marker = 5; /* can't try inflateSync */
++ break;
++ }
++ Tracev((stderr, "inflate: zlib header ok\n"));
++ if (!(b & PRESET_DICT))
++ {
++ z->state->mode = BLOCKS;
++ break;
++ }
++ z->state->mode = DICT4;
++ case DICT4:
++ NEEDBYTE
++ z->state->sub.check.need = (uLong)NEXTBYTE << 24;
++ z->state->mode = DICT3;
++ case DICT3:
++ NEEDBYTE
++ z->state->sub.check.need += (uLong)NEXTBYTE << 16;
++ z->state->mode = DICT2;
++ case DICT2:
++ NEEDBYTE
++ z->state->sub.check.need += (uLong)NEXTBYTE << 8;
++ z->state->mode = DICT1;
++ case DICT1:
++ NEEDBYTE
++ z->state->sub.check.need += (uLong)NEXTBYTE;
++ z->adler = z->state->sub.check.need;
++ z->state->mode = DICT0;
++ return Z_NEED_DICT;
++ case DICT0:
++ z->state->mode = BAD;
++ z->msg = (char*)"need dictionary";
++ z->state->sub.marker = 0; /* can try inflateSync */
++ return Z_STREAM_ERROR;
++ case BLOCKS:
++ r = inflate_blocks(z->state->blocks, z, r);
++ if (r == Z_DATA_ERROR)
++ {
++ z->state->mode = BAD;
++ z->state->sub.marker = 0; /* can try inflateSync */
++ break;
++ }
++ if (r == Z_OK)
++ r = f;
++ if (r != Z_STREAM_END)
++ return r;
++ r = f;
++ inflate_blocks_reset(z->state->blocks, z, &z->state->sub.check.was);
++ if (z->state->nowrap)
++ {
++ z->state->mode = DONE;
++ break;
++ }
++ z->state->mode = CHECK4;
++ case CHECK4:
++ NEEDBYTE
++ z->state->sub.check.need = (uLong)NEXTBYTE << 24;
++ z->state->mode = CHECK3;
++ case CHECK3:
++ NEEDBYTE
++ z->state->sub.check.need += (uLong)NEXTBYTE << 16;
++ z->state->mode = CHECK2;
++ case CHECK2:
++ NEEDBYTE
++ z->state->sub.check.need += (uLong)NEXTBYTE << 8;
++ z->state->mode = CHECK1;
++ case CHECK1:
++ NEEDBYTE
++ z->state->sub.check.need += (uLong)NEXTBYTE;
++
++ if (z->state->sub.check.was != z->state->sub.check.need)
++ {
++ z->state->mode = BAD;
++ z->msg = (char*)"incorrect data check";
++ z->state->sub.marker = 5; /* can't try inflateSync */
++ break;
++ }
++ Tracev((stderr, "inflate: zlib check ok\n"));
++ z->state->mode = DONE;
++ case DONE:
++ return Z_STREAM_END;
++ case BAD:
++ return Z_DATA_ERROR;
++ default:
++ return Z_STREAM_ERROR;
++ }
++#ifdef NEED_DUMMY_RETURN
++ return Z_STREAM_ERROR; /* Some dumb compilers complain without this */
++#endif
++}
++
++
++int ZEXPORT inflateSetDictionary(z, dictionary, dictLength)
++z_streamp z;
++const Bytef *dictionary;
++uInt dictLength;
++{
++ uInt length = dictLength;
++
++ if (z == Z_NULL || z->state == Z_NULL || z->state->mode != DICT0)
++ return Z_STREAM_ERROR;
++
++ if (adler32(1L, dictionary, dictLength) != z->adler) return Z_DATA_ERROR;
++ z->adler = 1L;
++
++ if (length >= ((uInt)1<<z->state->wbits))
++ {
++ length = (1<<z->state->wbits)-1;
++ dictionary += dictLength - length;
++ }
++ inflate_set_dictionary(z->state->blocks, dictionary, length);
++ z->state->mode = BLOCKS;
++ return Z_OK;
++}
++
++
++int ZEXPORT inflateSync(z)
++z_streamp z;
++{
++ uInt n; /* number of bytes to look at */
++ Bytef *p; /* pointer to bytes */
++ uInt m; /* number of marker bytes found in a row */
++ uLong r, w; /* temporaries to save total_in and total_out */
++
++ /* set up */
++ if (z == Z_NULL || z->state == Z_NULL)
++ return Z_STREAM_ERROR;
++ if (z->state->mode != BAD)
++ {
++ z->state->mode = BAD;
++ z->state->sub.marker = 0;
++ }
++ if ((n = z->avail_in) == 0)
++ return Z_BUF_ERROR;
++ p = z->next_in;
++ m = z->state->sub.marker;
++
++ /* search */
++ while (n && m < 4)
++ {
++ static const Byte mark[4] = {0, 0, 0xff, 0xff};
++ if (*p == mark[m])
++ m++;
++ else if (*p)
++ m = 0;
++ else
++ m = 4 - m;
++ p++, n--;
++ }
++
++ /* restore */
++ z->total_in += p - z->next_in;
++ z->next_in = p;
++ z->avail_in = n;
++ z->state->sub.marker = m;
++
++ /* return no joy or set up to restart on a new block */
++ if (m != 4)
++ return Z_DATA_ERROR;
++ r = z->total_in; w = z->total_out;
++ inflateReset(z);
++ z->total_in = r; z->total_out = w;
++ z->state->mode = BLOCKS;
++ return Z_OK;
++}
++
++
++/* Returns true if inflate is currently at the end of a block generated
++ * by Z_SYNC_FLUSH or Z_FULL_FLUSH. This function is used by one PPP
++ * implementation to provide an additional safety check. PPP uses Z_SYNC_FLUSH
++ * but removes the length bytes of the resulting empty stored block. When
++ * decompressing, PPP checks that at the end of input packet, inflate is
++ * waiting for these length bytes.
++ */
++int ZEXPORT inflateSyncPoint(z)
++z_streamp z;
++{
++ if (z == Z_NULL || z->state == Z_NULL || z->state->blocks == Z_NULL)
++ return Z_STREAM_ERROR;
++ return inflate_blocks_sync_point(z->state->blocks);
++}
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/zlib/inftrees.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,454 @@
++/* inftrees.c -- generate Huffman trees for efficient decoding
++ * Copyright (C) 1995-2002 Mark Adler
++ * For conditions of distribution and use, see copyright notice in zlib.h
++ */
++
++#include <zlib/zutil.h>
++#include "inftrees.h"
++
++#if !defined(BUILDFIXED) && !defined(STDC)
++# define BUILDFIXED /* non ANSI compilers may not accept inffixed.h */
++#endif
++
++local const char inflate_copyright[] =
++ " inflate 1.1.4 Copyright 1995-2002 Mark Adler ";
++/*
++ If you use the zlib library in a product, an acknowledgment is welcome
++ in the documentation of your product. If for some reason you cannot
++ include such an acknowledgment, I would appreciate that you keep this
++ copyright string in the executable of your product.
++ */
++struct internal_state {int dummy;}; /* for buggy compilers */
++
++/* simplify the use of the inflate_huft type with some defines */
++#define exop word.what.Exop
++#define bits word.what.Bits
++
++
++local int huft_build OF((
++ uIntf *, /* code lengths in bits */
++ uInt, /* number of codes */
++ uInt, /* number of "simple" codes */
++ const uIntf *, /* list of base values for non-simple codes */
++ const uIntf *, /* list of extra bits for non-simple codes */
++ inflate_huft * FAR*,/* result: starting table */
++ uIntf *, /* maximum lookup bits (returns actual) */
++ inflate_huft *, /* space for trees */
++ uInt *, /* hufts used in space */
++ uIntf * )); /* space for values */
++
++/* Tables for deflate from PKZIP's appnote.txt. */
++local const uInt cplens[31] = { /* Copy lengths for literal codes 257..285 */
++ 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
++ 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0};
++ /* see note #13 above about 258 */
++local const uInt cplext[31] = { /* Extra bits for literal codes 257..285 */
++ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2,
++ 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 112, 112}; /* 112==invalid */
++local const uInt cpdist[30] = { /* Copy offsets for distance codes 0..29 */
++ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
++ 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
++ 8193, 12289, 16385, 24577};
++local const uInt cpdext[30] = { /* Extra bits for distance codes */
++ 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6,
++ 7, 7, 8, 8, 9, 9, 10, 10, 11, 11,
++ 12, 12, 13, 13};
++
++/*
++ Huffman code decoding is performed using a multi-level table lookup.
++ The fastest way to decode is to simply build a lookup table whose
++ size is determined by the longest code. However, the time it takes
++ to build this table can also be a factor if the data being decoded
++ is not very long. The most common codes are necessarily the
++ shortest codes, so those codes dominate the decoding time, and hence
++ the speed. The idea is you can have a shorter table that decodes the
++ shorter, more probable codes, and then point to subsidiary tables for
++ the longer codes. The time it costs to decode the longer codes is
++ then traded against the time it takes to make longer tables.
++
++ This results of this trade are in the variables lbits and dbits
++ below. lbits is the number of bits the first level table for literal/
++ length codes can decode in one step, and dbits is the same thing for
++ the distance codes. Subsequent tables are also less than or equal to
++ those sizes. These values may be adjusted either when all of the
++ codes are shorter than that, in which case the longest code length in
++ bits is used, or when the shortest code is *longer* than the requested
++ table size, in which case the length of the shortest code in bits is
++ used.
++
++ There are two different values for the two tables, since they code a
++ different number of possibilities each. The literal/length table
++ codes 286 possible values, or in a flat code, a little over eight
++ bits. The distance table codes 30 possible values, or a little less
++ than five bits, flat. The optimum values for speed end up being
++ about one bit more than those, so lbits is 8+1 and dbits is 5+1.
++ The optimum values may differ though from machine to machine, and
++ possibly even between compilers. Your mileage may vary.
++ */
++
++
++/* If BMAX needs to be larger than 16, then h and x[] should be uLong. */
++#define BMAX 15 /* maximum bit length of any code */
++
++local int huft_build(b, n, s, d, e, t, m, hp, hn, v)
++uIntf *b; /* code lengths in bits (all assumed <= BMAX) */
++uInt n; /* number of codes (assumed <= 288) */
++uInt s; /* number of simple-valued codes (0..s-1) */
++const uIntf *d; /* list of base values for non-simple codes */
++const uIntf *e; /* list of extra bits for non-simple codes */
++inflate_huft * FAR *t; /* result: starting table */
++uIntf *m; /* maximum lookup bits, returns actual */
++inflate_huft *hp; /* space for trees */
++uInt *hn; /* hufts used in space */
++uIntf *v; /* working area: values in order of bit length */
++/* Given a list of code lengths and a maximum table size, make a set of
++ tables to decode that set of codes. Return Z_OK on success, Z_BUF_ERROR
++ if the given code set is incomplete (the tables are still built in this
++ case), or Z_DATA_ERROR if the input is invalid. */
++{
++
++ uInt a; /* counter for codes of length k */
++ uInt c[BMAX+1]; /* bit length count table */
++ uInt f; /* i repeats in table every f entries */
++ int g; /* maximum code length */
++ int h; /* table level */
++ register uInt i; /* counter, current code */
++ register uInt j; /* counter */
++ register int k; /* number of bits in current code */
++ int l; /* bits per table (returned in m) */
++ uInt mask; /* (1 << w) - 1, to avoid cc -O bug on HP */
++ register uIntf *p; /* pointer into c[], b[], or v[] */
++ inflate_huft *q; /* points to current table */
++ struct inflate_huft_s r; /* table entry for structure assignment */
++ inflate_huft *u[BMAX]; /* table stack */
++ register int w; /* bits before this table == (l * h) */
++ uInt x[BMAX+1]; /* bit offsets, then code stack */
++ uIntf *xp; /* pointer into x */
++ int y; /* number of dummy codes added */
++ uInt z; /* number of entries in current table */
++
++
++ /* Generate counts for each bit length */
++ p = c;
++#define C0 *p++ = 0;
++#define C2 C0 C0 C0 C0
++#define C4 C2 C2 C2 C2
++ C4 /* clear c[]--assume BMAX+1 is 16 */
++ p = b; i = n;
++ do {
++ c[*p++]++; /* assume all entries <= BMAX */
++ } while (--i);
++ if (c[0] == n) /* null input--all zero length codes */
++ {
++ *t = (inflate_huft *)Z_NULL;
++ *m = 0;
++ return Z_OK;
++ }
++
++
++ /* Find minimum and maximum length, bound *m by those */
++ l = *m;
++ for (j = 1; j <= BMAX; j++)
++ if (c[j])
++ break;
++ k = j; /* minimum code length */
++ if ((uInt)l < j)
++ l = j;
++ for (i = BMAX; i; i--)
++ if (c[i])
++ break;
++ g = i; /* maximum code length */
++ if ((uInt)l > i)
++ l = i;
++ *m = l;
++
++
++ /* Adjust last length count to fill out codes, if needed */
++ for (y = 1 << j; j < i; j++, y <<= 1)
++ if ((y -= c[j]) < 0)
++ return Z_DATA_ERROR;
++ if ((y -= c[i]) < 0)
++ return Z_DATA_ERROR;
++ c[i] += y;
++
++
++ /* Generate starting offsets into the value table for each length */
++ x[1] = j = 0;
++ p = c + 1; xp = x + 2;
++ while (--i) { /* note that i == g from above */
++ *xp++ = (j += *p++);
++ }
++
++
++ /* Make a table of values in order of bit lengths */
++ p = b; i = 0;
++ do {
++ if ((j = *p++) != 0)
++ v[x[j]++] = i;
++ } while (++i < n);
++ n = x[g]; /* set n to length of v */
++
++
++ /* Generate the Huffman codes and for each, make the table entries */
++ x[0] = i = 0; /* first Huffman code is zero */
++ p = v; /* grab values in bit order */
++ h = -1; /* no tables yet--level -1 */
++ w = -l; /* bits decoded == (l * h) */
++ u[0] = (inflate_huft *)Z_NULL; /* just to keep compilers happy */
++ q = (inflate_huft *)Z_NULL; /* ditto */
++ z = 0; /* ditto */
++
++ /* go through the bit lengths (k already is bits in shortest code) */
++ for (; k <= g; k++)
++ {
++ a = c[k];
++ while (a--)
++ {
++ /* here i is the Huffman code of length k bits for value *p */
++ /* make tables up to required level */
++ while (k > w + l)
++ {
++ h++;
++ w += l; /* previous table always l bits */
++
++ /* compute minimum size table less than or equal to l bits */
++ z = g - w;
++ z = z > (uInt)l ? l : z; /* table size upper limit */
++ if ((f = 1 << (j = k - w)) > a + 1) /* try a k-w bit table */
++ { /* too few codes for k-w bit table */
++ f -= a + 1; /* deduct codes from patterns left */
++ xp = c + k;
++ if (j < z)
++ while (++j < z) /* try smaller tables up to z bits */
++ {
++ if ((f <<= 1) <= *++xp)
++ break; /* enough codes to use up j bits */
++ f -= *xp; /* else deduct codes from patterns */
++ }
++ }
++ z = 1 << j; /* table entries for j-bit table */
++
++ /* allocate new table */
++ if (*hn + z > MANY) /* (note: doesn't matter for fixed) */
++ return Z_DATA_ERROR; /* overflow of MANY */
++ u[h] = q = hp + *hn;
++ *hn += z;
++
++ /* connect to last table, if there is one */
++ if (h)
++ {
++ x[h] = i; /* save pattern for backing up */
++ r.bits = (Byte)l; /* bits to dump before this table */
++ r.exop = (Byte)j; /* bits in this table */
++ j = i >> (w - l);
++ r.base = (uInt)(q - u[h-1] - j); /* offset to this table */
++ u[h-1][j] = r; /* connect to last table */
++ }
++ else
++ *t = q; /* first table is returned result */
++ }
++
++ /* set up table entry in r */
++ r.bits = (Byte)(k - w);
++ if (p >= v + n)
++ r.exop = 128 + 64; /* out of values--invalid code */
++ else if (*p < s)
++ {
++ r.exop = (Byte)(*p < 256 ? 0 : 32 + 64); /* 256 is end-of-block */
++ r.base = *p++; /* simple code is just the value */
++ }
++ else
++ {
++ r.exop = (Byte)(e[*p - s] + 16 + 64);/* non-simple--look up in lists */
++ r.base = d[*p++ - s];
++ }
++
++ /* fill code-like entries with r */
++ f = 1 << (k - w);
++ for (j = i >> w; j < z; j += f)
++ q[j] = r;
++
++ /* backwards increment the k-bit code i */
++ for (j = 1 << (k - 1); i & j; j >>= 1)
++ i ^= j;
++ i ^= j;
++
++ /* backup over finished tables */
++ mask = (1 << w) - 1; /* needed on HP, cc -O bug */
++ while ((i & mask) != x[h])
++ {
++ h--; /* don't need to update q */
++ w -= l;
++ mask = (1 << w) - 1;
++ }
++ }
++ }
++
++
++ /* Return Z_BUF_ERROR if we were given an incomplete table */
++ return y != 0 && g != 1 ? Z_BUF_ERROR : Z_OK;
++}
++
++
++int inflate_trees_bits(c, bb, tb, hp, z)
++uIntf *c; /* 19 code lengths */
++uIntf *bb; /* bits tree desired/actual depth */
++inflate_huft * FAR *tb; /* bits tree result */
++inflate_huft *hp; /* space for trees */
++z_streamp z; /* for messages */
++{
++ int r;
++ uInt hn = 0; /* hufts used in space */
++ uIntf *v; /* work area for huft_build */
++
++ if ((v = (uIntf*)ZALLOC(z, 19, sizeof(uInt))) == Z_NULL)
++ return Z_MEM_ERROR;
++ r = huft_build(c, 19, 19, (uIntf*)Z_NULL, (uIntf*)Z_NULL,
++ tb, bb, hp, &hn, v);
++ if (r == Z_DATA_ERROR)
++ z->msg = (char*)"oversubscribed dynamic bit lengths tree";
++ else if (r == Z_BUF_ERROR || *bb == 0)
++ {
++ z->msg = (char*)"incomplete dynamic bit lengths tree";
++ r = Z_DATA_ERROR;
++ }
++ ZFREE(z, v);
++ return r;
++}
++
++
++int inflate_trees_dynamic(nl, nd, c, bl, bd, tl, td, hp, z)
++uInt nl; /* number of literal/length codes */
++uInt nd; /* number of distance codes */
++uIntf *c; /* that many (total) code lengths */
++uIntf *bl; /* literal desired/actual bit depth */
++uIntf *bd; /* distance desired/actual bit depth */
++inflate_huft * FAR *tl; /* literal/length tree result */
++inflate_huft * FAR *td; /* distance tree result */
++inflate_huft *hp; /* space for trees */
++z_streamp z; /* for messages */
++{
++ int r;
++ uInt hn = 0; /* hufts used in space */
++ uIntf *v; /* work area for huft_build */
++
++ /* allocate work area */
++ if ((v = (uIntf*)ZALLOC(z, 288, sizeof(uInt))) == Z_NULL)
++ return Z_MEM_ERROR;
++
++ /* build literal/length tree */
++ r = huft_build(c, nl, 257, cplens, cplext, tl, bl, hp, &hn, v);
++ if (r != Z_OK || *bl == 0)
++ {
++ if (r == Z_DATA_ERROR)
++ z->msg = (char*)"oversubscribed literal/length tree";
++ else if (r != Z_MEM_ERROR)
++ {
++ z->msg = (char*)"incomplete literal/length tree";
++ r = Z_DATA_ERROR;
++ }
++ ZFREE(z, v);
++ return r;
++ }
++
++ /* build distance tree */
++ r = huft_build(c + nl, nd, 0, cpdist, cpdext, td, bd, hp, &hn, v);
++ if (r != Z_OK || (*bd == 0 && nl > 257))
++ {
++ if (r == Z_DATA_ERROR)
++ z->msg = (char*)"oversubscribed distance tree";
++ else if (r == Z_BUF_ERROR) {
++#ifdef PKZIP_BUG_WORKAROUND
++ r = Z_OK;
++ }
++#else
++ z->msg = (char*)"incomplete distance tree";
++ r = Z_DATA_ERROR;
++ }
++ else if (r != Z_MEM_ERROR)
++ {
++ z->msg = (char*)"empty distance tree with lengths";
++ r = Z_DATA_ERROR;
++ }
++ ZFREE(z, v);
++ return r;
++#endif
++ }
++
++ /* done */
++ ZFREE(z, v);
++ return Z_OK;
++}
++
++
++/* build fixed tables only once--keep them here */
++#ifdef BUILDFIXED
++local int fixed_built = 0;
++#define FIXEDH 544 /* number of hufts used by fixed tables */
++local inflate_huft fixed_mem[FIXEDH];
++local uInt fixed_bl;
++local uInt fixed_bd;
++local inflate_huft *fixed_tl;
++local inflate_huft *fixed_td;
++#else
++#include "inffixed.h"
++#endif
++
++
++int inflate_trees_fixed(bl, bd, tl, td, z)
++uIntf *bl; /* literal desired/actual bit depth */
++uIntf *bd; /* distance desired/actual bit depth */
++inflate_huft * FAR *tl; /* literal/length tree result */
++inflate_huft * FAR *td; /* distance tree result */
++z_streamp z; /* for memory allocation */
++{
++#ifdef BUILDFIXED
++ /* build fixed tables if not already */
++ if (!fixed_built)
++ {
++ int k; /* temporary variable */
++ uInt f = 0; /* number of hufts used in fixed_mem */
++ uIntf *c; /* length list for huft_build */
++ uIntf *v; /* work area for huft_build */
++
++ /* allocate memory */
++ if ((c = (uIntf*)ZALLOC(z, 288, sizeof(uInt))) == Z_NULL)
++ return Z_MEM_ERROR;
++ if ((v = (uIntf*)ZALLOC(z, 288, sizeof(uInt))) == Z_NULL)
++ {
++ ZFREE(z, c);
++ return Z_MEM_ERROR;
++ }
++
++ /* literal table */
++ for (k = 0; k < 144; k++)
++ c[k] = 8;
++ for (; k < 256; k++)
++ c[k] = 9;
++ for (; k < 280; k++)
++ c[k] = 7;
++ for (; k < 288; k++)
++ c[k] = 8;
++ fixed_bl = 9;
++ huft_build(c, 288, 257, cplens, cplext, &fixed_tl, &fixed_bl,
++ fixed_mem, &f, v);
++
++ /* distance table */
++ for (k = 0; k < 30; k++)
++ c[k] = 5;
++ fixed_bd = 5;
++ huft_build(c, 30, 0, cpdist, cpdext, &fixed_td, &fixed_bd,
++ fixed_mem, &f, v);
++
++ /* done */
++ ZFREE(z, v);
++ ZFREE(z, c);
++ fixed_built = 1;
++ }
++#endif
++ *bl = fixed_bl;
++ *bd = fixed_bd;
++ *tl = fixed_tl;
++ *td = fixed_td;
++ return Z_OK;
++}
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/zlib/inftrees.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,63 @@
++/* inftrees.h -- header to use inftrees.c
++ * Copyright (C) 1995-2002 Mark Adler
++ * For conditions of distribution and use, see copyright notice in zlib.h
++ */
++
++/* WARNING: this file should *not* be used by applications. It is
++ part of the implementation of the compression library and is
++ subject to change. Applications should only use zlib.h.
++ */
++
++/* Huffman code lookup table entry--this entry is four bytes for machines
++ that have 16-bit pointers (e.g. PC's in the small or medium model). */
++
++#ifndef _INFTREES_H
++#define _INFTREES_H
++
++typedef struct inflate_huft_s FAR inflate_huft;
++
++struct inflate_huft_s {
++ union {
++ struct {
++ Byte Exop; /* number of extra bits or operation */
++ Byte Bits; /* number of bits in this code or subcode */
++ } what;
++ uInt pad; /* pad structure to a power of 2 (4 bytes for */
++ } word; /* 16-bit, 8 bytes for 32-bit int's) */
++ uInt base; /* literal, length base, distance base,
++ or table offset */
++};
++
++/* Maximum size of dynamic tree. The maximum found in a long but non-
++ exhaustive search was 1004 huft structures (850 for length/literals
++ and 154 for distances, the latter actually the result of an
++ exhaustive search). The actual maximum is not known, but the
++ value below is more than safe. */
++#define MANY 1440
++
++extern int inflate_trees_bits OF((
++ uIntf *, /* 19 code lengths */
++ uIntf *, /* bits tree desired/actual depth */
++ inflate_huft * FAR *, /* bits tree result */
++ inflate_huft *, /* space for trees */
++ z_streamp)); /* for messages */
++
++extern int inflate_trees_dynamic OF((
++ uInt, /* number of literal/length codes */
++ uInt, /* number of distance codes */
++ uIntf *, /* that many (total) code lengths */
++ uIntf *, /* literal desired/actual bit depth */
++ uIntf *, /* distance desired/actual bit depth */
++ inflate_huft * FAR *, /* literal/length tree result */
++ inflate_huft * FAR *, /* distance tree result */
++ inflate_huft *, /* space for trees */
++ z_streamp)); /* for messages */
++
++extern int inflate_trees_fixed OF((
++ uIntf *, /* literal desired/actual bit depth */
++ uIntf *, /* distance desired/actual bit depth */
++ inflate_huft * FAR *, /* literal/length tree result */
++ inflate_huft * FAR *, /* distance tree result */
++ z_streamp)); /* for memory allocation */
++
++#endif /* _INFTREES_H */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/zlib/infutil.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,87 @@
++/* inflate_util.c -- data and routines common to blocks and codes
++ * Copyright (C) 1995-2002 Mark Adler
++ * For conditions of distribution and use, see copyright notice in zlib.h
++ */
++
++#include <zlib/zutil.h>
++#include "infblock.h"
++#include "inftrees.h"
++#include "infcodes.h"
++#include "infutil.h"
++
++struct inflate_codes_state {int dummy;}; /* for buggy compilers */
++
++/* And'ing with mask[n] masks the lower n bits */
++uInt inflate_mask[17] = {
++ 0x0000,
++ 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff,
++ 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff
++};
++
++
++/* copy as much as possible from the sliding window to the output area */
++int inflate_flush(s, z, r)
++inflate_blocks_statef *s;
++z_streamp z;
++int r;
++{
++ uInt n;
++ Bytef *p;
++ Bytef *q;
++
++ /* local copies of source and destination pointers */
++ p = z->next_out;
++ q = s->read;
++
++ /* compute number of bytes to copy as far as end of window */
++ n = (uInt)((q <= s->write ? s->write : s->end) - q);
++ if (n > z->avail_out) n = z->avail_out;
++ if (n && r == Z_BUF_ERROR) r = Z_OK;
++
++ /* update counters */
++ z->avail_out -= n;
++ z->total_out += n;
++
++ /* update check information */
++ if (s->checkfn != Z_NULL)
++ z->adler = s->check = (*s->checkfn)(s->check, q, n);
++
++ /* copy as far as end of window */
++ zmemcpy(p, q, n);
++ p += n;
++ q += n;
++
++ /* see if more to copy at beginning of window */
++ if (q == s->end)
++ {
++ /* wrap pointers */
++ q = s->window;
++ if (s->write == s->end)
++ s->write = s->window;
++
++ /* compute bytes to copy */
++ n = (uInt)(s->write - q);
++ if (n > z->avail_out) n = z->avail_out;
++ if (n && r == Z_BUF_ERROR) r = Z_OK;
++
++ /* update counters */
++ z->avail_out -= n;
++ z->total_out += n;
++
++ /* update check information */
++ if (s->checkfn != Z_NULL)
++ z->adler = s->check = (*s->checkfn)(s->check, q, n);
++
++ /* copy */
++ zmemcpy(p, q, n);
++ p += n;
++ q += n;
++ }
++
++ /* update pointers */
++ z->next_out = p;
++ s->read = q;
++
++ /* done */
++ return r;
++}
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/zlib/infutil.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,98 @@
++/* infutil.h -- types and macros common to blocks and codes
++ * Copyright (C) 1995-2002 Mark Adler
++ * For conditions of distribution and use, see copyright notice in zlib.h
++ */
++
++/* WARNING: this file should *not* be used by applications. It is
++ part of the implementation of the compression library and is
++ subject to change. Applications should only use zlib.h.
++ */
++
++#ifndef _INFUTIL_H
++#define _INFUTIL_H
++
++typedef enum {
++ TYPE, /* get type bits (3, including end bit) */
++ LENS, /* get lengths for stored */
++ STORED, /* processing stored block */
++ TABLE, /* get table lengths */
++ BTREE, /* get bit lengths tree for a dynamic block */
++ DTREE, /* get length, distance trees for a dynamic block */
++ CODES, /* processing fixed or dynamic block */
++ DRY, /* output remaining window bytes */
++ DONE, /* finished last block, done */
++ BAD} /* got a data error--stuck here */
++inflate_block_mode;
++
++/* inflate blocks semi-private state */
++struct inflate_blocks_state {
++
++ /* mode */
++ inflate_block_mode mode; /* current inflate_block mode */
++
++ /* mode dependent information */
++ union {
++ uInt left; /* if STORED, bytes left to copy */
++ struct {
++ uInt table; /* table lengths (14 bits) */
++ uInt index; /* index into blens (or border) */
++ uIntf *blens; /* bit lengths of codes */
++ uInt bb; /* bit length tree depth */
++ inflate_huft *tb; /* bit length decoding tree */
++ } trees; /* if DTREE, decoding info for trees */
++ struct {
++ inflate_codes_statef
++ *codes;
++ } decode; /* if CODES, current state */
++ } sub; /* submode */
++ uInt last; /* true if this block is the last block */
++
++ /* mode independent information */
++ uInt bitk; /* bits in bit buffer */
++ uLong bitb; /* bit buffer */
++ inflate_huft *hufts; /* single malloc for tree space */
++ Bytef *window; /* sliding window */
++ Bytef *end; /* one byte after sliding window */
++ Bytef *read; /* window read pointer */
++ Bytef *write; /* window write pointer */
++ check_func checkfn; /* check function */
++ uLong check; /* check on output */
++
++};
++
++
++/* defines for inflate input/output */
++/* update pointers and return */
++#define UPDBITS {s->bitb=b;s->bitk=k;}
++#define UPDIN {z->avail_in=n;z->total_in+=p-z->next_in;z->next_in=p;}
++#define UPDOUT {s->write=q;}
++#define UPDATE {UPDBITS UPDIN UPDOUT}
++#define LEAVE {UPDATE return inflate_flush(s,z,r);}
++/* get bytes and bits */
++#define LOADIN {p=z->next_in;n=z->avail_in;b=s->bitb;k=s->bitk;}
++#define NEEDBYTE {if(n)r=Z_OK;else LEAVE}
++#define NEXTBYTE (n--,*p++)
++#define NEEDBITS(j) {while(k<(j)){NEEDBYTE;b|=((uLong)NEXTBYTE)<<k;k+=8;}}
++#define DUMPBITS(j) {b>>=(j);k-=(j);}
++/* output bytes */
++#define WAVAIL (uInt)(q<s->read?s->read-q-1:s->end-q)
++#define LOADOUT {q=s->write;m=(uInt)WAVAIL;}
++#define WRAP {if(q==s->end&&s->read!=s->window){q=s->window;m=(uInt)WAVAIL;}}
++#define FLUSH {UPDOUT r=inflate_flush(s,z,r); LOADOUT}
++#define NEEDOUT {if(m==0){WRAP if(m==0){FLUSH WRAP if(m==0) LEAVE}}r=Z_OK;}
++#define OUTBYTE(a) {*q++=(Byte)(a);m--;}
++/* load local pointers */
++#define LOAD {LOADIN LOADOUT}
++
++/* masks for lower bits (size given to avoid silly warnings with Visual C++) */
++extern uInt inflate_mask[17];
++
++/* copy as much as possible from the sliding window to the output area */
++extern int inflate_flush OF((
++ inflate_blocks_statef *,
++ z_streamp ,
++ int));
++
++struct internal_state {int dummy;}; /* for buggy compilers */
++
++#endif /* _INFUTIL_H */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/zlib/match586.S Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,357 @@
++/* match.s -- Pentium-optimized version of longest_match()
++ * Written for zlib 1.1.2
++ * Copyright (C) 1998 Brian Raiter <breadbox@muppetlabs.com>
++ *
++ * This is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License.
++ */
++
++#ifndef NO_UNDERLINE
++#define match_init _ipcomp_match_init
++#define longest_match _ipcomp_longest_match
++#else
++#define match_init ipcomp_match_init
++#define longest_match ipcomp_longest_match
++#endif
++
++#define MAX_MATCH (258)
++#define MIN_MATCH (3)
++#define MIN_LOOKAHEAD (MAX_MATCH + MIN_MATCH + 1)
++#define MAX_MATCH_8 ((MAX_MATCH + 7) & ~7)
++
++/* stack frame offsets */
++
++#define wmask 0 /* local copy of s->wmask */
++#define window 4 /* local copy of s->window */
++#define windowbestlen 8 /* s->window + bestlen */
++#define chainlenscanend 12 /* high word: current chain len */
++ /* low word: last bytes sought */
++#define scanstart 16 /* first two bytes of string */
++#define scanalign 20 /* dword-misalignment of string */
++#define nicematch 24 /* a good enough match size */
++#define bestlen 28 /* size of best match so far */
++#define scan 32 /* ptr to string wanting match */
++
++#define LocalVarsSize (36)
++/* saved ebx 36 */
++/* saved edi 40 */
++/* saved esi 44 */
++/* saved ebp 48 */
++/* return address 52 */
++#define deflatestate 56 /* the function arguments */
++#define curmatch 60
++
++/* Offsets for fields in the deflate_state structure. These numbers
++ * are calculated from the definition of deflate_state, with the
++ * assumption that the compiler will dword-align the fields. (Thus,
++ * changing the definition of deflate_state could easily cause this
++ * program to crash horribly, without so much as a warning at
++ * compile time. Sigh.)
++ */
++#define dsWSize 36
++#define dsWMask 44
++#define dsWindow 48
++#define dsPrev 56
++#define dsMatchLen 88
++#define dsPrevMatch 92
++#define dsStrStart 100
++#define dsMatchStart 104
++#define dsLookahead 108
++#define dsPrevLen 112
++#define dsMaxChainLen 116
++#define dsGoodMatch 132
++#define dsNiceMatch 136
++
++
++.file "match.S"
++
++.globl match_init, longest_match
++
++.text
++
++/* uInt longest_match(deflate_state *deflatestate, IPos curmatch) */
++
++longest_match:
++
++/* Save registers that the compiler may be using, and adjust %esp to */
++/* make room for our stack frame. */
++
++ pushl %ebp
++ pushl %edi
++ pushl %esi
++ pushl %ebx
++ subl $LocalVarsSize, %esp
++
++/* Retrieve the function arguments. %ecx will hold cur_match */
++/* throughout the entire function. %edx will hold the pointer to the */
++/* deflate_state structure during the function's setup (before */
++/* entering the main loop). */
++
++ movl deflatestate(%esp), %edx
++ movl curmatch(%esp), %ecx
++
++/* if ((uInt)nice_match > s->lookahead) nice_match = s->lookahead; */
++
++ movl dsNiceMatch(%edx), %eax
++ movl dsLookahead(%edx), %ebx
++ cmpl %eax, %ebx
++ jl LookaheadLess
++ movl %eax, %ebx
++LookaheadLess: movl %ebx, nicematch(%esp)
++
++/* register Bytef *scan = s->window + s->strstart; */
++
++ movl dsWindow(%edx), %esi
++ movl %esi, window(%esp)
++ movl dsStrStart(%edx), %ebp
++ lea (%esi,%ebp), %edi
++ movl %edi, scan(%esp)
++
++/* Determine how many bytes the scan ptr is off from being */
++/* dword-aligned. */
++
++ movl %edi, %eax
++ negl %eax
++ andl $3, %eax
++ movl %eax, scanalign(%esp)
++
++/* IPos limit = s->strstart > (IPos)MAX_DIST(s) ? */
++/* s->strstart - (IPos)MAX_DIST(s) : NIL; */
++
++ movl dsWSize(%edx), %eax
++ subl $MIN_LOOKAHEAD, %eax
++ subl %eax, %ebp
++ jg LimitPositive
++ xorl %ebp, %ebp
++LimitPositive:
++
++/* unsigned chain_length = s->max_chain_length; */
++/* if (s->prev_length >= s->good_match) { */
++/* chain_length >>= 2; */
++/* } */
++
++ movl dsPrevLen(%edx), %eax
++ movl dsGoodMatch(%edx), %ebx
++ cmpl %ebx, %eax
++ movl dsMaxChainLen(%edx), %ebx
++ jl LastMatchGood
++ shrl $2, %ebx
++LastMatchGood:
++
++/* chainlen is decremented once beforehand so that the function can */
++/* use the sign flag instead of the zero flag for the exit test. */
++/* It is then shifted into the high word, to make room for the scanend */
++/* scanend value, which it will always accompany. */
++
++ decl %ebx
++ shll $16, %ebx
++
++/* int best_len = s->prev_length; */
++
++ movl dsPrevLen(%edx), %eax
++ movl %eax, bestlen(%esp)
++
++/* Store the sum of s->window + best_len in %esi locally, and in %esi. */
++
++ addl %eax, %esi
++ movl %esi, windowbestlen(%esp)
++
++/* register ush scan_start = *(ushf*)scan; */
++/* register ush scan_end = *(ushf*)(scan+best_len-1); */
++
++ movw (%edi), %bx
++ movw %bx, scanstart(%esp)
++ movw -1(%edi,%eax), %bx
++ movl %ebx, chainlenscanend(%esp)
++
++/* Posf *prev = s->prev; */
++/* uInt wmask = s->w_mask; */
++
++ movl dsPrev(%edx), %edi
++ movl dsWMask(%edx), %edx
++ mov %edx, wmask(%esp)
++
++/* Jump into the main loop. */
++
++ jmp LoopEntry
++
++.balign 16
++
++/* do {
++ * match = s->window + cur_match;
++ * if (*(ushf*)(match+best_len-1) != scan_end ||
++ * *(ushf*)match != scan_start) continue;
++ * [...]
++ * } while ((cur_match = prev[cur_match & wmask]) > limit
++ * && --chain_length != 0);
++ *
++ * Here is the inner loop of the function. The function will spend the
++ * majority of its time in this loop, and majority of that time will
++ * be spent in the first ten instructions.
++ *
++ * Within this loop:
++ * %ebx = chainlenscanend - i.e., ((chainlen << 16) | scanend)
++ * %ecx = curmatch
++ * %edx = curmatch & wmask
++ * %esi = windowbestlen - i.e., (window + bestlen)
++ * %edi = prev
++ * %ebp = limit
++ *
++ * Two optimization notes on the choice of instructions:
++ *
++ * The first instruction uses a 16-bit address, which costs an extra,
++ * unpairable cycle. This is cheaper than doing a 32-bit access and
++ * zeroing the high word, due to the 3-cycle misalignment penalty which
++ * would occur half the time. This also turns out to be cheaper than
++ * doing two separate 8-bit accesses, as the memory is so rarely in the
++ * L1 cache.
++ *
++ * The window buffer, however, apparently spends a lot of time in the
++ * cache, and so it is faster to retrieve the word at the end of the
++ * match string with two 8-bit loads. The instructions that test the
++ * word at the beginning of the match string, however, are executed
++ * much less frequently, and there it was cheaper to use 16-bit
++ * instructions, which avoided the necessity of saving off and
++ * subsequently reloading one of the other registers.
++ */
++LookupLoop:
++ /* 1 U & V */
++ movw (%edi,%edx,2), %cx /* 2 U pipe */
++ movl wmask(%esp), %edx /* 2 V pipe */
++ cmpl %ebp, %ecx /* 3 U pipe */
++ jbe LeaveNow /* 3 V pipe */
++ subl $0x00010000, %ebx /* 4 U pipe */
++ js LeaveNow /* 4 V pipe */
++LoopEntry: movb -1(%esi,%ecx), %al /* 5 U pipe */
++ andl %ecx, %edx /* 5 V pipe */
++ cmpb %bl, %al /* 6 U pipe */
++ jnz LookupLoop /* 6 V pipe */
++ movb (%esi,%ecx), %ah
++ cmpb %bh, %ah
++ jnz LookupLoop
++ movl window(%esp), %eax
++ movw (%eax,%ecx), %ax
++ cmpw scanstart(%esp), %ax
++ jnz LookupLoop
++
++/* Store the current value of chainlen. */
++
++ movl %ebx, chainlenscanend(%esp)
++
++/* Point %edi to the string under scrutiny, and %esi to the string we */
++/* are hoping to match it up with. In actuality, %esi and %edi are */
++/* both pointed (MAX_MATCH_8 - scanalign) bytes ahead, and %edx is */
++/* initialized to -(MAX_MATCH_8 - scanalign). */
++
++ movl window(%esp), %esi
++ movl scan(%esp), %edi
++ addl %ecx, %esi
++ movl scanalign(%esp), %eax
++ movl $(-MAX_MATCH_8), %edx
++ lea MAX_MATCH_8(%edi,%eax), %edi
++ lea MAX_MATCH_8(%esi,%eax), %esi
++
++/* Test the strings for equality, 8 bytes at a time. At the end,
++ * adjust %edx so that it is offset to the exact byte that mismatched.
++ *
++ * We already know at this point that the first three bytes of the
++ * strings match each other, and they can be safely passed over before
++ * starting the compare loop. So what this code does is skip over 0-3
++ * bytes, as much as necessary in order to dword-align the %edi
++ * pointer. (%esi will still be misaligned three times out of four.)
++ *
++ * It should be confessed that this loop usually does not represent
++ * much of the total running time. Replacing it with a more
++ * straightforward "rep cmpsb" would not drastically degrade
++ * performance.
++ */
++LoopCmps:
++ movl (%esi,%edx), %eax
++ movl (%edi,%edx), %ebx
++ xorl %ebx, %eax
++ jnz LeaveLoopCmps
++ movl 4(%esi,%edx), %eax
++ movl 4(%edi,%edx), %ebx
++ xorl %ebx, %eax
++ jnz LeaveLoopCmps4
++ addl $8, %edx
++ jnz LoopCmps
++ jmp LenMaximum
++LeaveLoopCmps4: addl $4, %edx
++LeaveLoopCmps: testl $0x0000FFFF, %eax
++ jnz LenLower
++ addl $2, %edx
++ shrl $16, %eax
++LenLower: subb $1, %al
++ adcl $0, %edx
++
++/* Calculate the length of the match. If it is longer than MAX_MATCH, */
++/* then automatically accept it as the best possible match and leave. */
++
++ lea (%edi,%edx), %eax
++ movl scan(%esp), %edi
++ subl %edi, %eax
++ cmpl $MAX_MATCH, %eax
++ jge LenMaximum
++
++/* If the length of the match is not longer than the best match we */
++/* have so far, then forget it and return to the lookup loop. */
++
++ movl deflatestate(%esp), %edx
++ movl bestlen(%esp), %ebx
++ cmpl %ebx, %eax
++ jg LongerMatch
++ movl chainlenscanend(%esp), %ebx
++ movl windowbestlen(%esp), %esi
++ movl dsPrev(%edx), %edi
++ movl wmask(%esp), %edx
++ andl %ecx, %edx
++ jmp LookupLoop
++
++/* s->match_start = cur_match; */
++/* best_len = len; */
++/* if (len >= nice_match) break; */
++/* scan_end = *(ushf*)(scan+best_len-1); */
++
++LongerMatch: movl nicematch(%esp), %ebx
++ movl %eax, bestlen(%esp)
++ movl %ecx, dsMatchStart(%edx)
++ cmpl %ebx, %eax
++ jge LeaveNow
++ movl window(%esp), %esi
++ addl %eax, %esi
++ movl %esi, windowbestlen(%esp)
++ movl chainlenscanend(%esp), %ebx
++ movw -1(%edi,%eax), %bx
++ movl dsPrev(%edx), %edi
++ movl %ebx, chainlenscanend(%esp)
++ movl wmask(%esp), %edx
++ andl %ecx, %edx
++ jmp LookupLoop
++
++/* Accept the current string, with the maximum possible length. */
++
++LenMaximum: movl deflatestate(%esp), %edx
++ movl $MAX_MATCH, bestlen(%esp)
++ movl %ecx, dsMatchStart(%edx)
++
++/* if ((uInt)best_len <= s->lookahead) return (uInt)best_len; */
++/* return s->lookahead; */
++
++LeaveNow:
++ movl deflatestate(%esp), %edx
++ movl bestlen(%esp), %ebx
++ movl dsLookahead(%edx), %eax
++ cmpl %eax, %ebx
++ jg LookaheadRet
++ movl %ebx, %eax
++LookaheadRet:
++
++/* Restore the stack and return from whence we came. */
++
++ addl $LocalVarsSize, %esp
++ popl %ebx
++ popl %esi
++ popl %edi
++ popl %ebp
++match_init: ret
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/zlib/match686.S Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,330 @@
++/* match.s -- Pentium-Pro-optimized version of longest_match()
++ * Written for zlib 1.1.2
++ * Copyright (C) 1998 Brian Raiter <breadbox@muppetlabs.com>
++ *
++ * This is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License.
++ */
++
++#ifndef NO_UNDERLINE
++#define match_init _ipcomp_match_init
++#define longest_match _ipcomp_longest_match
++#else
++#define match_init ipcomp_match_init
++#define longest_match ipcomp_longest_match
++#endif
++
++#define MAX_MATCH (258)
++#define MIN_MATCH (3)
++#define MIN_LOOKAHEAD (MAX_MATCH + MIN_MATCH + 1)
++#define MAX_MATCH_8 ((MAX_MATCH + 7) & ~7)
++
++/* stack frame offsets */
++
++#define chainlenwmask 0 /* high word: current chain len */
++ /* low word: s->wmask */
++#define window 4 /* local copy of s->window */
++#define windowbestlen 8 /* s->window + bestlen */
++#define scanstart 16 /* first two bytes of string */
++#define scanend 12 /* last two bytes of string */
++#define scanalign 20 /* dword-misalignment of string */
++#define nicematch 24 /* a good enough match size */
++#define bestlen 28 /* size of best match so far */
++#define scan 32 /* ptr to string wanting match */
++
++#define LocalVarsSize (36)
++/* saved ebx 36 */
++/* saved edi 40 */
++/* saved esi 44 */
++/* saved ebp 48 */
++/* return address 52 */
++#define deflatestate 56 /* the function arguments */
++#define curmatch 60
++
++/* Offsets for fields in the deflate_state structure. These numbers
++ * are calculated from the definition of deflate_state, with the
++ * assumption that the compiler will dword-align the fields. (Thus,
++ * changing the definition of deflate_state could easily cause this
++ * program to crash horribly, without so much as a warning at
++ * compile time. Sigh.)
++ */
++#define dsWSize 36
++#define dsWMask 44
++#define dsWindow 48
++#define dsPrev 56
++#define dsMatchLen 88
++#define dsPrevMatch 92
++#define dsStrStart 100
++#define dsMatchStart 104
++#define dsLookahead 108
++#define dsPrevLen 112
++#define dsMaxChainLen 116
++#define dsGoodMatch 132
++#define dsNiceMatch 136
++
++
++.file "match.S"
++
++.globl match_init, longest_match
++
++.text
++
++/* uInt longest_match(deflate_state *deflatestate, IPos curmatch) */
++
++longest_match:
++
++/* Save registers that the compiler may be using, and adjust %esp to */
++/* make room for our stack frame. */
++
++ pushl %ebp
++ pushl %edi
++ pushl %esi
++ pushl %ebx
++ subl $LocalVarsSize, %esp
++
++/* Retrieve the function arguments. %ecx will hold cur_match */
++/* throughout the entire function. %edx will hold the pointer to the */
++/* deflate_state structure during the function's setup (before */
++/* entering the main loop). */
++
++ movl deflatestate(%esp), %edx
++ movl curmatch(%esp), %ecx
++
++/* uInt wmask = s->w_mask; */
++/* unsigned chain_length = s->max_chain_length; */
++/* if (s->prev_length >= s->good_match) { */
++/* chain_length >>= 2; */
++/* } */
++
++ movl dsPrevLen(%edx), %eax
++ movl dsGoodMatch(%edx), %ebx
++ cmpl %ebx, %eax
++ movl dsWMask(%edx), %eax
++ movl dsMaxChainLen(%edx), %ebx
++ jl LastMatchGood
++ shrl $2, %ebx
++LastMatchGood:
++
++/* chainlen is decremented once beforehand so that the function can */
++/* use the sign flag instead of the zero flag for the exit test. */
++/* It is then shifted into the high word, to make room for the wmask */
++/* value, which it will always accompany. */
++
++ decl %ebx
++ shll $16, %ebx
++ orl %eax, %ebx
++ movl %ebx, chainlenwmask(%esp)
++
++/* if ((uInt)nice_match > s->lookahead) nice_match = s->lookahead; */
++
++ movl dsNiceMatch(%edx), %eax
++ movl dsLookahead(%edx), %ebx
++ cmpl %eax, %ebx
++ jl LookaheadLess
++ movl %eax, %ebx
++LookaheadLess: movl %ebx, nicematch(%esp)
++
++/* register Bytef *scan = s->window + s->strstart; */
++
++ movl dsWindow(%edx), %esi
++ movl %esi, window(%esp)
++ movl dsStrStart(%edx), %ebp
++ lea (%esi,%ebp), %edi
++ movl %edi, scan(%esp)
++
++/* Determine how many bytes the scan ptr is off from being */
++/* dword-aligned. */
++
++ movl %edi, %eax
++ negl %eax
++ andl $3, %eax
++ movl %eax, scanalign(%esp)
++
++/* IPos limit = s->strstart > (IPos)MAX_DIST(s) ? */
++/* s->strstart - (IPos)MAX_DIST(s) : NIL; */
++
++ movl dsWSize(%edx), %eax
++ subl $MIN_LOOKAHEAD, %eax
++ subl %eax, %ebp
++ jg LimitPositive
++ xorl %ebp, %ebp
++LimitPositive:
++
++/* int best_len = s->prev_length; */
++
++ movl dsPrevLen(%edx), %eax
++ movl %eax, bestlen(%esp)
++
++/* Store the sum of s->window + best_len in %esi locally, and in %esi. */
++
++ addl %eax, %esi
++ movl %esi, windowbestlen(%esp)
++
++/* register ush scan_start = *(ushf*)scan; */
++/* register ush scan_end = *(ushf*)(scan+best_len-1); */
++/* Posf *prev = s->prev; */
++
++ movzwl (%edi), %ebx
++ movl %ebx, scanstart(%esp)
++ movzwl -1(%edi,%eax), %ebx
++ movl %ebx, scanend(%esp)
++ movl dsPrev(%edx), %edi
++
++/* Jump into the main loop. */
++
++ movl chainlenwmask(%esp), %edx
++ jmp LoopEntry
++
++.balign 16
++
++/* do {
++ * match = s->window + cur_match;
++ * if (*(ushf*)(match+best_len-1) != scan_end ||
++ * *(ushf*)match != scan_start) continue;
++ * [...]
++ * } while ((cur_match = prev[cur_match & wmask]) > limit
++ * && --chain_length != 0);
++ *
++ * Here is the inner loop of the function. The function will spend the
++ * majority of its time in this loop, and majority of that time will
++ * be spent in the first ten instructions.
++ *
++ * Within this loop:
++ * %ebx = scanend
++ * %ecx = curmatch
++ * %edx = chainlenwmask - i.e., ((chainlen << 16) | wmask)
++ * %esi = windowbestlen - i.e., (window + bestlen)
++ * %edi = prev
++ * %ebp = limit
++ */
++LookupLoop:
++ andl %edx, %ecx
++ movzwl (%edi,%ecx,2), %ecx
++ cmpl %ebp, %ecx
++ jbe LeaveNow
++ subl $0x00010000, %edx
++ js LeaveNow
++LoopEntry: movzwl -1(%esi,%ecx), %eax
++ cmpl %ebx, %eax
++ jnz LookupLoop
++ movl window(%esp), %eax
++ movzwl (%eax,%ecx), %eax
++ cmpl scanstart(%esp), %eax
++ jnz LookupLoop
++
++/* Store the current value of chainlen. */
++
++ movl %edx, chainlenwmask(%esp)
++
++/* Point %edi to the string under scrutiny, and %esi to the string we */
++/* are hoping to match it up with. In actuality, %esi and %edi are */
++/* both pointed (MAX_MATCH_8 - scanalign) bytes ahead, and %edx is */
++/* initialized to -(MAX_MATCH_8 - scanalign). */
++
++ movl window(%esp), %esi
++ movl scan(%esp), %edi
++ addl %ecx, %esi
++ movl scanalign(%esp), %eax
++ movl $(-MAX_MATCH_8), %edx
++ lea MAX_MATCH_8(%edi,%eax), %edi
++ lea MAX_MATCH_8(%esi,%eax), %esi
++
++/* Test the strings for equality, 8 bytes at a time. At the end,
++ * adjust %edx so that it is offset to the exact byte that mismatched.
++ *
++ * We already know at this point that the first three bytes of the
++ * strings match each other, and they can be safely passed over before
++ * starting the compare loop. So what this code does is skip over 0-3
++ * bytes, as much as necessary in order to dword-align the %edi
++ * pointer. (%esi will still be misaligned three times out of four.)
++ *
++ * It should be confessed that this loop usually does not represent
++ * much of the total running time. Replacing it with a more
++ * straightforward "rep cmpsb" would not drastically degrade
++ * performance.
++ */
++LoopCmps:
++ movl (%esi,%edx), %eax
++ xorl (%edi,%edx), %eax
++ jnz LeaveLoopCmps
++ movl 4(%esi,%edx), %eax
++ xorl 4(%edi,%edx), %eax
++ jnz LeaveLoopCmps4
++ addl $8, %edx
++ jnz LoopCmps
++ jmp LenMaximum
++LeaveLoopCmps4: addl $4, %edx
++LeaveLoopCmps: testl $0x0000FFFF, %eax
++ jnz LenLower
++ addl $2, %edx
++ shrl $16, %eax
++LenLower: subb $1, %al
++ adcl $0, %edx
++
++/* Calculate the length of the match. If it is longer than MAX_MATCH, */
++/* then automatically accept it as the best possible match and leave. */
++
++ lea (%edi,%edx), %eax
++ movl scan(%esp), %edi
++ subl %edi, %eax
++ cmpl $MAX_MATCH, %eax
++ jge LenMaximum
++
++/* If the length of the match is not longer than the best match we */
++/* have so far, then forget it and return to the lookup loop. */
++
++ movl deflatestate(%esp), %edx
++ movl bestlen(%esp), %ebx
++ cmpl %ebx, %eax
++ jg LongerMatch
++ movl windowbestlen(%esp), %esi
++ movl dsPrev(%edx), %edi
++ movl scanend(%esp), %ebx
++ movl chainlenwmask(%esp), %edx
++ jmp LookupLoop
++
++/* s->match_start = cur_match; */
++/* best_len = len; */
++/* if (len >= nice_match) break; */
++/* scan_end = *(ushf*)(scan+best_len-1); */
++
++LongerMatch: movl nicematch(%esp), %ebx
++ movl %eax, bestlen(%esp)
++ movl %ecx, dsMatchStart(%edx)
++ cmpl %ebx, %eax
++ jge LeaveNow
++ movl window(%esp), %esi
++ addl %eax, %esi
++ movl %esi, windowbestlen(%esp)
++ movzwl -1(%edi,%eax), %ebx
++ movl dsPrev(%edx), %edi
++ movl %ebx, scanend(%esp)
++ movl chainlenwmask(%esp), %edx
++ jmp LookupLoop
++
++/* Accept the current string, with the maximum possible length. */
++
++LenMaximum: movl deflatestate(%esp), %edx
++ movl $MAX_MATCH, bestlen(%esp)
++ movl %ecx, dsMatchStart(%edx)
++
++/* if ((uInt)best_len <= s->lookahead) return (uInt)best_len; */
++/* return s->lookahead; */
++
++LeaveNow:
++ movl deflatestate(%esp), %edx
++ movl bestlen(%esp), %ebx
++ movl dsLookahead(%edx), %eax
++ cmpl %eax, %ebx
++ jg LookaheadRet
++ movl %ebx, %eax
++LookaheadRet:
++
++/* Restore the stack and return from whence we came. */
++
++ addl $LocalVarsSize, %esp
++ popl %ebx
++ popl %esi
++ popl %edi
++ popl %ebp
++match_init: ret
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/zlib/trees.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,1214 @@
++/* trees.c -- output deflated data using Huffman coding
++ * Copyright (C) 1995-2002 Jean-loup Gailly
++ * For conditions of distribution and use, see copyright notice in zlib.h
++ */
++
++/*
++ * ALGORITHM
++ *
++ * The "deflation" process uses several Huffman trees. The more
++ * common source values are represented by shorter bit sequences.
++ *
++ * Each code tree is stored in a compressed form which is itself
++ * a Huffman encoding of the lengths of all the code strings (in
++ * ascending order by source values). The actual code strings are
++ * reconstructed from the lengths in the inflate process, as described
++ * in the deflate specification.
++ *
++ * REFERENCES
++ *
++ * Deutsch, L.P.,"'Deflate' Compressed Data Format Specification".
++ * Available in ftp.uu.net:/pub/archiving/zip/doc/deflate-1.1.doc
++ *
++ * Storer, James A.
++ * Data Compression: Methods and Theory, pp. 49-50.
++ * Computer Science Press, 1988. ISBN 0-7167-8156-5.
++ *
++ * Sedgewick, R.
++ * Algorithms, p290.
++ * Addison-Wesley, 1983. ISBN 0-201-06672-6.
++ */
++
++/* @(#) $Id$ */
++
++/* #define GEN_TREES_H */
++
++#include "deflate.h"
++
++#ifdef DEBUG
++# include <ctype.h>
++#endif
++
++/* ===========================================================================
++ * Constants
++ */
++
++#define MAX_BL_BITS 7
++/* Bit length codes must not exceed MAX_BL_BITS bits */
++
++#define END_BLOCK 256
++/* end of block literal code */
++
++#define REP_3_6 16
++/* repeat previous bit length 3-6 times (2 bits of repeat count) */
++
++#define REPZ_3_10 17
++/* repeat a zero length 3-10 times (3 bits of repeat count) */
++
++#define REPZ_11_138 18
++/* repeat a zero length 11-138 times (7 bits of repeat count) */
++
++local const int extra_lbits[LENGTH_CODES] /* extra bits for each length code */
++ = {0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0};
++
++local const int extra_dbits[D_CODES] /* extra bits for each distance code */
++ = {0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13};
++
++local const int extra_blbits[BL_CODES]/* extra bits for each bit length code */
++ = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7};
++
++local const uch bl_order[BL_CODES]
++ = {16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15};
++/* The lengths of the bit length codes are sent in order of decreasing
++ * probability, to avoid transmitting the lengths for unused bit length codes.
++ */
++
++#define Buf_size (8 * 2*sizeof(char))
++/* Number of bits used within bi_buf. (bi_buf might be implemented on
++ * more than 16 bits on some systems.)
++ */
++
++/* ===========================================================================
++ * Local data. These are initialized only once.
++ */
++
++#define DIST_CODE_LEN 512 /* see definition of array dist_code below */
++
++#if defined(GEN_TREES_H) || !defined(STDC)
++/* non ANSI compilers may not accept trees.h */
++
++local ct_data static_ltree[L_CODES+2];
++/* The static literal tree. Since the bit lengths are imposed, there is no
++ * need for the L_CODES extra codes used during heap construction. However
++ * The codes 286 and 287 are needed to build a canonical tree (see _tr_init
++ * below).
++ */
++
++local ct_data static_dtree[D_CODES];
++/* The static distance tree. (Actually a trivial tree since all codes use
++ * 5 bits.)
++ */
++
++uch _dist_code[DIST_CODE_LEN];
++/* Distance codes. The first 256 values correspond to the distances
++ * 3 .. 258, the last 256 values correspond to the top 8 bits of
++ * the 15 bit distances.
++ */
++
++uch _length_code[MAX_MATCH-MIN_MATCH+1];
++/* length code for each normalized match length (0 == MIN_MATCH) */
++
++local int base_length[LENGTH_CODES];
++/* First normalized length for each code (0 = MIN_MATCH) */
++
++local int base_dist[D_CODES];
++/* First normalized distance for each code (0 = distance of 1) */
++
++#else
++# include "trees.h"
++#endif /* GEN_TREES_H */
++
++struct static_tree_desc_s {
++ const ct_data *static_tree; /* static tree or NULL */
++ const intf *extra_bits; /* extra bits for each code or NULL */
++ int extra_base; /* base index for extra_bits */
++ int elems; /* max number of elements in the tree */
++ int max_length; /* max bit length for the codes */
++};
++
++local static_tree_desc static_l_desc =
++{static_ltree, extra_lbits, LITERALS+1, L_CODES, MAX_BITS};
++
++local static_tree_desc static_d_desc =
++{static_dtree, extra_dbits, 0, D_CODES, MAX_BITS};
++
++local static_tree_desc static_bl_desc =
++{(const ct_data *)0, extra_blbits, 0, BL_CODES, MAX_BL_BITS};
++
++/* ===========================================================================
++ * Local (static) routines in this file.
++ */
++
++local void tr_static_init OF((void));
++local void init_block OF((deflate_state *s));
++local void pqdownheap OF((deflate_state *s, ct_data *tree, int k));
++local void gen_bitlen OF((deflate_state *s, tree_desc *desc));
++local void gen_codes OF((ct_data *tree, int max_code, ushf *bl_count));
++local void build_tree OF((deflate_state *s, tree_desc *desc));
++local void scan_tree OF((deflate_state *s, ct_data *tree, int max_code));
++local void send_tree OF((deflate_state *s, ct_data *tree, int max_code));
++local int build_bl_tree OF((deflate_state *s));
++local void send_all_trees OF((deflate_state *s, int lcodes, int dcodes,
++ int blcodes));
++local void compress_block OF((deflate_state *s, const ct_data *ltree,
++ const ct_data *dtree));
++local void set_data_type OF((deflate_state *s));
++local unsigned bi_reverse OF((unsigned value, int length));
++local void bi_windup OF((deflate_state *s));
++local void bi_flush OF((deflate_state *s));
++local void copy_block OF((deflate_state *s, charf *buf, unsigned len,
++ int header));
++
++#ifdef GEN_TREES_H
++local void gen_trees_header OF((void));
++#endif
++
++#ifndef DEBUG
++# define send_code(s, c, tree) send_bits(s, tree[c].Code, tree[c].Len)
++ /* Send a code of the given tree. c and tree must not have side effects */
++
++#else /* DEBUG */
++# define send_code(s, c, tree) \
++ { if (z_verbose>2) fprintf(stderr,"\ncd %3d ",(c)); \
++ send_bits(s, tree[c].Code, tree[c].Len); }
++#endif
++
++/* ===========================================================================
++ * Output a short LSB first on the stream.
++ * IN assertion: there is enough room in pendingBuf.
++ */
++#define put_short(s, w) { \
++ put_byte(s, (uch)((w) & 0xff)); \
++ put_byte(s, (uch)((ush)(w) >> 8)); \
++}
++
++/* ===========================================================================
++ * Send a value on a given number of bits.
++ * IN assertion: length <= 16 and value fits in length bits.
++ */
++#ifdef DEBUG
++local void send_bits OF((deflate_state *s, int value, int length));
++
++local void send_bits(s, value, length)
++ deflate_state *s;
++ int value; /* value to send */
++ int length; /* number of bits */
++{
++ Tracevv((stderr," l %2d v %4x ", length, value));
++ Assert(length > 0 && length <= 15, "invalid length");
++ s->bits_sent += (ulg)length;
++
++ /* If not enough room in bi_buf, use (valid) bits from bi_buf and
++ * (16 - bi_valid) bits from value, leaving (width - (16-bi_valid))
++ * unused bits in value.
++ */
++ if (s->bi_valid > (int)Buf_size - length) {
++ s->bi_buf |= (value << s->bi_valid);
++ put_short(s, s->bi_buf);
++ s->bi_buf = (ush)value >> (Buf_size - s->bi_valid);
++ s->bi_valid += length - Buf_size;
++ } else {
++ s->bi_buf |= value << s->bi_valid;
++ s->bi_valid += length;
++ }
++}
++#else /* !DEBUG */
++
++#define send_bits(s, value, length) \
++{ int len = length;\
++ if (s->bi_valid > (int)Buf_size - len) {\
++ int val = value;\
++ s->bi_buf |= (val << s->bi_valid);\
++ put_short(s, s->bi_buf);\
++ s->bi_buf = (ush)val >> (Buf_size - s->bi_valid);\
++ s->bi_valid += len - Buf_size;\
++ } else {\
++ s->bi_buf |= (value) << s->bi_valid;\
++ s->bi_valid += len;\
++ }\
++}
++#endif /* DEBUG */
++
++
++#define MAX(a,b) (a >= b ? a : b)
++/* the arguments must not have side effects */
++
++/* ===========================================================================
++ * Initialize the various 'constant' tables.
++ */
++local void tr_static_init()
++{
++#if defined(GEN_TREES_H) || !defined(STDC)
++ static int static_init_done = 0;
++ int n; /* iterates over tree elements */
++ int bits; /* bit counter */
++ int length; /* length value */
++ int code; /* code value */
++ int dist; /* distance index */
++ ush bl_count[MAX_BITS+1];
++ /* number of codes at each bit length for an optimal tree */
++
++ if (static_init_done) return;
++
++ /* For some embedded targets, global variables are not initialized: */
++ static_l_desc.static_tree = static_ltree;
++ static_l_desc.extra_bits = extra_lbits;
++ static_d_desc.static_tree = static_dtree;
++ static_d_desc.extra_bits = extra_dbits;
++ static_bl_desc.extra_bits = extra_blbits;
++
++ /* Initialize the mapping length (0..255) -> length code (0..28) */
++ length = 0;
++ for (code = 0; code < LENGTH_CODES-1; code++) {
++ base_length[code] = length;
++ for (n = 0; n < (1<<extra_lbits[code]); n++) {
++ _length_code[length++] = (uch)code;
++ }
++ }
++ Assert (length == 256, "tr_static_init: length != 256");
++ /* Note that the length 255 (match length 258) can be represented
++ * in two different ways: code 284 + 5 bits or code 285, so we
++ * overwrite length_code[255] to use the best encoding:
++ */
++ _length_code[length-1] = (uch)code;
++
++ /* Initialize the mapping dist (0..32K) -> dist code (0..29) */
++ dist = 0;
++ for (code = 0 ; code < 16; code++) {
++ base_dist[code] = dist;
++ for (n = 0; n < (1<<extra_dbits[code]); n++) {
++ _dist_code[dist++] = (uch)code;
++ }
++ }
++ Assert (dist == 256, "tr_static_init: dist != 256");
++ dist >>= 7; /* from now on, all distances are divided by 128 */
++ for ( ; code < D_CODES; code++) {
++ base_dist[code] = dist << 7;
++ for (n = 0; n < (1<<(extra_dbits[code]-7)); n++) {
++ _dist_code[256 + dist++] = (uch)code;
++ }
++ }
++ Assert (dist == 256, "tr_static_init: 256+dist != 512");
++
++ /* Construct the codes of the static literal tree */
++ for (bits = 0; bits <= MAX_BITS; bits++) bl_count[bits] = 0;
++ n = 0;
++ while (n <= 143) static_ltree[n++].Len = 8, bl_count[8]++;
++ while (n <= 255) static_ltree[n++].Len = 9, bl_count[9]++;
++ while (n <= 279) static_ltree[n++].Len = 7, bl_count[7]++;
++ while (n <= 287) static_ltree[n++].Len = 8, bl_count[8]++;
++ /* Codes 286 and 287 do not exist, but we must include them in the
++ * tree construction to get a canonical Huffman tree (longest code
++ * all ones)
++ */
++ gen_codes((ct_data *)static_ltree, L_CODES+1, bl_count);
++
++ /* The static distance tree is trivial: */
++ for (n = 0; n < D_CODES; n++) {
++ static_dtree[n].Len = 5;
++ static_dtree[n].Code = bi_reverse((unsigned)n, 5);
++ }
++ static_init_done = 1;
++
++# ifdef GEN_TREES_H
++ gen_trees_header();
++# endif
++#endif /* defined(GEN_TREES_H) || !defined(STDC) */
++}
++
++/* ===========================================================================
++ * Genererate the file trees.h describing the static trees.
++ */
++#ifdef GEN_TREES_H
++# ifndef DEBUG
++# include <stdio.h>
++# endif
++
++# define SEPARATOR(i, last, width) \
++ ((i) == (last)? "\n};\n\n" : \
++ ((i) % (width) == (width)-1 ? ",\n" : ", "))
++
++void gen_trees_header()
++{
++ FILE *header = fopen("trees.h", "w");
++ int i;
++
++ Assert (header != NULL, "Can't open trees.h");
++ fprintf(header,
++ "/* header created automatically with -DGEN_TREES_H */\n\n");
++
++ fprintf(header, "local const ct_data static_ltree[L_CODES+2] = {\n");
++ for (i = 0; i < L_CODES+2; i++) {
++ fprintf(header, "{{%3u},{%3u}}%s", static_ltree[i].Code,
++ static_ltree[i].Len, SEPARATOR(i, L_CODES+1, 5));
++ }
++
++ fprintf(header, "local const ct_data static_dtree[D_CODES] = {\n");
++ for (i = 0; i < D_CODES; i++) {
++ fprintf(header, "{{%2u},{%2u}}%s", static_dtree[i].Code,
++ static_dtree[i].Len, SEPARATOR(i, D_CODES-1, 5));
++ }
++
++ fprintf(header, "const uch _dist_code[DIST_CODE_LEN] = {\n");
++ for (i = 0; i < DIST_CODE_LEN; i++) {
++ fprintf(header, "%2u%s", _dist_code[i],
++ SEPARATOR(i, DIST_CODE_LEN-1, 20));
++ }
++
++ fprintf(header, "const uch _length_code[MAX_MATCH-MIN_MATCH+1]= {\n");
++ for (i = 0; i < MAX_MATCH-MIN_MATCH+1; i++) {
++ fprintf(header, "%2u%s", _length_code[i],
++ SEPARATOR(i, MAX_MATCH-MIN_MATCH, 20));
++ }
++
++ fprintf(header, "local const int base_length[LENGTH_CODES] = {\n");
++ for (i = 0; i < LENGTH_CODES; i++) {
++ fprintf(header, "%1u%s", base_length[i],
++ SEPARATOR(i, LENGTH_CODES-1, 20));
++ }
++
++ fprintf(header, "local const int base_dist[D_CODES] = {\n");
++ for (i = 0; i < D_CODES; i++) {
++ fprintf(header, "%5u%s", base_dist[i],
++ SEPARATOR(i, D_CODES-1, 10));
++ }
++
++ fclose(header);
++}
++#endif /* GEN_TREES_H */
++
++/* ===========================================================================
++ * Initialize the tree data structures for a new zlib stream.
++ */
++void _tr_init(s)
++ deflate_state *s;
++{
++ tr_static_init();
++
++ s->l_desc.dyn_tree = s->dyn_ltree;
++ s->l_desc.stat_desc = &static_l_desc;
++
++ s->d_desc.dyn_tree = s->dyn_dtree;
++ s->d_desc.stat_desc = &static_d_desc;
++
++ s->bl_desc.dyn_tree = s->bl_tree;
++ s->bl_desc.stat_desc = &static_bl_desc;
++
++ s->bi_buf = 0;
++ s->bi_valid = 0;
++ s->last_eob_len = 8; /* enough lookahead for inflate */
++#ifdef DEBUG
++ s->compressed_len = 0L;
++ s->bits_sent = 0L;
++#endif
++
++ /* Initialize the first block of the first file: */
++ init_block(s);
++}
++
++/* ===========================================================================
++ * Initialize a new block.
++ */
++local void init_block(s)
++ deflate_state *s;
++{
++ int n; /* iterates over tree elements */
++
++ /* Initialize the trees. */
++ for (n = 0; n < L_CODES; n++) s->dyn_ltree[n].Freq = 0;
++ for (n = 0; n < D_CODES; n++) s->dyn_dtree[n].Freq = 0;
++ for (n = 0; n < BL_CODES; n++) s->bl_tree[n].Freq = 0;
++
++ s->dyn_ltree[END_BLOCK].Freq = 1;
++ s->opt_len = s->static_len = 0L;
++ s->last_lit = s->matches = 0;
++}
++
++#define SMALLEST 1
++/* Index within the heap array of least frequent node in the Huffman tree */
++
++
++/* ===========================================================================
++ * Remove the smallest element from the heap and recreate the heap with
++ * one less element. Updates heap and heap_len.
++ */
++#define pqremove(s, tree, top) \
++{\
++ top = s->heap[SMALLEST]; \
++ s->heap[SMALLEST] = s->heap[s->heap_len--]; \
++ pqdownheap(s, tree, SMALLEST); \
++}
++
++/* ===========================================================================
++ * Compares to subtrees, using the tree depth as tie breaker when
++ * the subtrees have equal frequency. This minimizes the worst case length.
++ */
++#define smaller(tree, n, m, depth) \
++ (tree[n].Freq < tree[m].Freq || \
++ (tree[n].Freq == tree[m].Freq && depth[n] <= depth[m]))
++
++/* ===========================================================================
++ * Restore the heap property by moving down the tree starting at node k,
++ * exchanging a node with the smallest of its two sons if necessary, stopping
++ * when the heap property is re-established (each father smaller than its
++ * two sons).
++ */
++local void pqdownheap(s, tree, k)
++ deflate_state *s;
++ ct_data *tree; /* the tree to restore */
++ int k; /* node to move down */
++{
++ int v = s->heap[k];
++ int j = k << 1; /* left son of k */
++ while (j <= s->heap_len) {
++ /* Set j to the smallest of the two sons: */
++ if (j < s->heap_len &&
++ smaller(tree, s->heap[j+1], s->heap[j], s->depth)) {
++ j++;
++ }
++ /* Exit if v is smaller than both sons */
++ if (smaller(tree, v, s->heap[j], s->depth)) break;
++
++ /* Exchange v with the smallest son */
++ s->heap[k] = s->heap[j]; k = j;
++
++ /* And continue down the tree, setting j to the left son of k */
++ j <<= 1;
++ }
++ s->heap[k] = v;
++}
++
++/* ===========================================================================
++ * Compute the optimal bit lengths for a tree and update the total bit length
++ * for the current block.
++ * IN assertion: the fields freq and dad are set, heap[heap_max] and
++ * above are the tree nodes sorted by increasing frequency.
++ * OUT assertions: the field len is set to the optimal bit length, the
++ * array bl_count contains the frequencies for each bit length.
++ * The length opt_len is updated; static_len is also updated if stree is
++ * not null.
++ */
++local void gen_bitlen(s, desc)
++ deflate_state *s;
++ tree_desc *desc; /* the tree descriptor */
++{
++ ct_data *tree = desc->dyn_tree;
++ int max_code = desc->max_code;
++ const ct_data *stree = desc->stat_desc->static_tree;
++ const intf *extra = desc->stat_desc->extra_bits;
++ int base = desc->stat_desc->extra_base;
++ int max_length = desc->stat_desc->max_length;
++ int h; /* heap index */
++ int n, m; /* iterate over the tree elements */
++ int bits; /* bit length */
++ int xbits; /* extra bits */
++ ush f; /* frequency */
++ int overflow = 0; /* number of elements with bit length too large */
++
++ for (bits = 0; bits <= MAX_BITS; bits++) s->bl_count[bits] = 0;
++
++ /* In a first pass, compute the optimal bit lengths (which may
++ * overflow in the case of the bit length tree).
++ */
++ tree[s->heap[s->heap_max]].Len = 0; /* root of the heap */
++
++ for (h = s->heap_max+1; h < HEAP_SIZE; h++) {
++ n = s->heap[h];
++ bits = tree[tree[n].Dad].Len + 1;
++ if (bits > max_length) bits = max_length, overflow++;
++ tree[n].Len = (ush)bits;
++ /* We overwrite tree[n].Dad which is no longer needed */
++
++ if (n > max_code) continue; /* not a leaf node */
++
++ s->bl_count[bits]++;
++ xbits = 0;
++ if (n >= base) xbits = extra[n-base];
++ f = tree[n].Freq;
++ s->opt_len += (ulg)f * (bits + xbits);
++ if (stree) s->static_len += (ulg)f * (stree[n].Len + xbits);
++ }
++ if (overflow == 0) return;
++
++ Trace((stderr,"\nbit length overflow\n"));
++ /* This happens for example on obj2 and pic of the Calgary corpus */
++
++ /* Find the first bit length which could increase: */
++ do {
++ bits = max_length-1;
++ while (s->bl_count[bits] == 0) bits--;
++ s->bl_count[bits]--; /* move one leaf down the tree */
++ s->bl_count[bits+1] += 2; /* move one overflow item as its brother */
++ s->bl_count[max_length]--;
++ /* The brother of the overflow item also moves one step up,
++ * but this does not affect bl_count[max_length]
++ */
++ overflow -= 2;
++ } while (overflow > 0);
++
++ /* Now recompute all bit lengths, scanning in increasing frequency.
++ * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all
++ * lengths instead of fixing only the wrong ones. This idea is taken
++ * from 'ar' written by Haruhiko Okumura.)
++ */
++ for (bits = max_length; bits != 0; bits--) {
++ n = s->bl_count[bits];
++ while (n != 0) {
++ m = s->heap[--h];
++ if (m > max_code) continue;
++ if (tree[m].Len != (unsigned) bits) {
++ Trace((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits));
++ s->opt_len += ((long)bits - (long)tree[m].Len)
++ *(long)tree[m].Freq;
++ tree[m].Len = (ush)bits;
++ }
++ n--;
++ }
++ }
++}
++
++/* ===========================================================================
++ * Generate the codes for a given tree and bit counts (which need not be
++ * optimal).
++ * IN assertion: the array bl_count contains the bit length statistics for
++ * the given tree and the field len is set for all tree elements.
++ * OUT assertion: the field code is set for all tree elements of non
++ * zero code length.
++ */
++local void gen_codes (tree, max_code, bl_count)
++ ct_data *tree; /* the tree to decorate */
++ int max_code; /* largest code with non zero frequency */
++ ushf *bl_count; /* number of codes at each bit length */
++{
++ ush next_code[MAX_BITS+1]; /* next code value for each bit length */
++ ush code = 0; /* running code value */
++ int bits; /* bit index */
++ int n; /* code index */
++
++ /* The distribution counts are first used to generate the code values
++ * without bit reversal.
++ */
++ for (bits = 1; bits <= MAX_BITS; bits++) {
++ next_code[bits] = code = (code + bl_count[bits-1]) << 1;
++ }
++ /* Check that the bit counts in bl_count are consistent. The last code
++ * must be all ones.
++ */
++ Assert (code + bl_count[MAX_BITS]-1 == (1<<MAX_BITS)-1,
++ "inconsistent bit counts");
++ Tracev((stderr,"\ngen_codes: max_code %d ", max_code));
++
++ for (n = 0; n <= max_code; n++) {
++ int len = tree[n].Len;
++ if (len == 0) continue;
++ /* Now reverse the bits */
++ tree[n].Code = bi_reverse(next_code[len]++, len);
++
++ Tracecv(tree != static_ltree, (stderr,"\nn %3d %c l %2d c %4x (%x) ",
++ n, (isgraph(n) ? n : ' '), len, tree[n].Code, next_code[len]-1));
++ }
++}
++
++/* ===========================================================================
++ * Construct one Huffman tree and assigns the code bit strings and lengths.
++ * Update the total bit length for the current block.
++ * IN assertion: the field freq is set for all tree elements.
++ * OUT assertions: the fields len and code are set to the optimal bit length
++ * and corresponding code. The length opt_len is updated; static_len is
++ * also updated if stree is not null. The field max_code is set.
++ */
++local void build_tree(s, desc)
++ deflate_state *s;
++ tree_desc *desc; /* the tree descriptor */
++{
++ ct_data *tree = desc->dyn_tree;
++ const ct_data *stree = desc->stat_desc->static_tree;
++ int elems = desc->stat_desc->elems;
++ int n, m; /* iterate over heap elements */
++ int max_code = -1; /* largest code with non zero frequency */
++ int node; /* new node being created */
++
++ /* Construct the initial heap, with least frequent element in
++ * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1].
++ * heap[0] is not used.
++ */
++ s->heap_len = 0, s->heap_max = HEAP_SIZE;
++
++ for (n = 0; n < elems; n++) {
++ if (tree[n].Freq != 0) {
++ s->heap[++(s->heap_len)] = max_code = n;
++ s->depth[n] = 0;
++ } else {
++ tree[n].Len = 0;
++ }
++ }
++
++ /* The pkzip format requires that at least one distance code exists,
++ * and that at least one bit should be sent even if there is only one
++ * possible code. So to avoid special checks later on we force at least
++ * two codes of non zero frequency.
++ */
++ while (s->heap_len < 2) {
++ node = s->heap[++(s->heap_len)] = (max_code < 2 ? ++max_code : 0);
++ tree[node].Freq = 1;
++ s->depth[node] = 0;
++ s->opt_len--; if (stree) s->static_len -= stree[node].Len;
++ /* node is 0 or 1 so it does not have extra bits */
++ }
++ desc->max_code = max_code;
++
++ /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree,
++ * establish sub-heaps of increasing lengths:
++ */
++ for (n = s->heap_len/2; n >= 1; n--) pqdownheap(s, tree, n);
++
++ /* Construct the Huffman tree by repeatedly combining the least two
++ * frequent nodes.
++ */
++ node = elems; /* next internal node of the tree */
++ do {
++ pqremove(s, tree, n); /* n = node of least frequency */
++ m = s->heap[SMALLEST]; /* m = node of next least frequency */
++
++ s->heap[--(s->heap_max)] = n; /* keep the nodes sorted by frequency */
++ s->heap[--(s->heap_max)] = m;
++
++ /* Create a new node father of n and m */
++ tree[node].Freq = tree[n].Freq + tree[m].Freq;
++ s->depth[node] = (uch) (MAX(s->depth[n], s->depth[m]) + 1);
++ tree[n].Dad = tree[m].Dad = (ush)node;
++#ifdef DUMP_BL_TREE
++ if (tree == s->bl_tree) {
++ fprintf(stderr,"\nnode %d(%d), sons %d(%d) %d(%d)",
++ node, tree[node].Freq, n, tree[n].Freq, m, tree[m].Freq);
++ }
++#endif
++ /* and insert the new node in the heap */
++ s->heap[SMALLEST] = node++;
++ pqdownheap(s, tree, SMALLEST);
++
++ } while (s->heap_len >= 2);
++
++ s->heap[--(s->heap_max)] = s->heap[SMALLEST];
++
++ /* At this point, the fields freq and dad are set. We can now
++ * generate the bit lengths.
++ */
++ gen_bitlen(s, (tree_desc *)desc);
++
++ /* The field len is now set, we can generate the bit codes */
++ gen_codes ((ct_data *)tree, max_code, s->bl_count);
++}
++
++/* ===========================================================================
++ * Scan a literal or distance tree to determine the frequencies of the codes
++ * in the bit length tree.
++ */
++local void scan_tree (s, tree, max_code)
++ deflate_state *s;
++ ct_data *tree; /* the tree to be scanned */
++ int max_code; /* and its largest code of non zero frequency */
++{
++ int n; /* iterates over all tree elements */
++ int prevlen = -1; /* last emitted length */
++ int curlen; /* length of current code */
++ int nextlen = tree[0].Len; /* length of next code */
++ int count = 0; /* repeat count of the current code */
++ int max_count = 7; /* max repeat count */
++ int min_count = 4; /* min repeat count */
++
++ if (nextlen == 0) max_count = 138, min_count = 3;
++ tree[max_code+1].Len = (ush)0xffff; /* guard */
++
++ for (n = 0; n <= max_code; n++) {
++ curlen = nextlen; nextlen = tree[n+1].Len;
++ if (++count < max_count && curlen == nextlen) {
++ continue;
++ } else if (count < min_count) {
++ s->bl_tree[curlen].Freq += count;
++ } else if (curlen != 0) {
++ if (curlen != prevlen) s->bl_tree[curlen].Freq++;
++ s->bl_tree[REP_3_6].Freq++;
++ } else if (count <= 10) {
++ s->bl_tree[REPZ_3_10].Freq++;
++ } else {
++ s->bl_tree[REPZ_11_138].Freq++;
++ }
++ count = 0; prevlen = curlen;
++ if (nextlen == 0) {
++ max_count = 138, min_count = 3;
++ } else if (curlen == nextlen) {
++ max_count = 6, min_count = 3;
++ } else {
++ max_count = 7, min_count = 4;
++ }
++ }
++}
++
++/* ===========================================================================
++ * Send a literal or distance tree in compressed form, using the codes in
++ * bl_tree.
++ */
++local void send_tree (s, tree, max_code)
++ deflate_state *s;
++ ct_data *tree; /* the tree to be scanned */
++ int max_code; /* and its largest code of non zero frequency */
++{
++ int n; /* iterates over all tree elements */
++ int prevlen = -1; /* last emitted length */
++ int curlen; /* length of current code */
++ int nextlen = tree[0].Len; /* length of next code */
++ int count = 0; /* repeat count of the current code */
++ int max_count = 7; /* max repeat count */
++ int min_count = 4; /* min repeat count */
++
++ /* tree[max_code+1].Len = -1; */ /* guard already set */
++ if (nextlen == 0) max_count = 138, min_count = 3;
++
++ for (n = 0; n <= max_code; n++) {
++ curlen = nextlen; nextlen = tree[n+1].Len;
++ if (++count < max_count && curlen == nextlen) {
++ continue;
++ } else if (count < min_count) {
++ do { send_code(s, curlen, s->bl_tree); } while (--count != 0);
++
++ } else if (curlen != 0) {
++ if (curlen != prevlen) {
++ send_code(s, curlen, s->bl_tree); count--;
++ }
++ Assert(count >= 3 && count <= 6, " 3_6?");
++ send_code(s, REP_3_6, s->bl_tree); send_bits(s, count-3, 2);
++
++ } else if (count <= 10) {
++ send_code(s, REPZ_3_10, s->bl_tree); send_bits(s, count-3, 3);
++
++ } else {
++ send_code(s, REPZ_11_138, s->bl_tree); send_bits(s, count-11, 7);
++ }
++ count = 0; prevlen = curlen;
++ if (nextlen == 0) {
++ max_count = 138, min_count = 3;
++ } else if (curlen == nextlen) {
++ max_count = 6, min_count = 3;
++ } else {
++ max_count = 7, min_count = 4;
++ }
++ }
++}
++
++/* ===========================================================================
++ * Construct the Huffman tree for the bit lengths and return the index in
++ * bl_order of the last bit length code to send.
++ */
++local int build_bl_tree(s)
++ deflate_state *s;
++{
++ int max_blindex; /* index of last bit length code of non zero freq */
++
++ /* Determine the bit length frequencies for literal and distance trees */
++ scan_tree(s, (ct_data *)s->dyn_ltree, s->l_desc.max_code);
++ scan_tree(s, (ct_data *)s->dyn_dtree, s->d_desc.max_code);
++
++ /* Build the bit length tree: */
++ build_tree(s, (tree_desc *)(&(s->bl_desc)));
++ /* opt_len now includes the length of the tree representations, except
++ * the lengths of the bit lengths codes and the 5+5+4 bits for the counts.
++ */
++
++ /* Determine the number of bit length codes to send. The pkzip format
++ * requires that at least 4 bit length codes be sent. (appnote.txt says
++ * 3 but the actual value used is 4.)
++ */
++ for (max_blindex = BL_CODES-1; max_blindex >= 3; max_blindex--) {
++ if (s->bl_tree[bl_order[max_blindex]].Len != 0) break;
++ }
++ /* Update opt_len to include the bit length tree and counts */
++ s->opt_len += 3*(max_blindex+1) + 5+5+4;
++ Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld",
++ s->opt_len, s->static_len));
++
++ return max_blindex;
++}
++
++/* ===========================================================================
++ * Send the header for a block using dynamic Huffman trees: the counts, the
++ * lengths of the bit length codes, the literal tree and the distance tree.
++ * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4.
++ */
++local void send_all_trees(s, lcodes, dcodes, blcodes)
++ deflate_state *s;
++ int lcodes, dcodes, blcodes; /* number of codes for each tree */
++{
++ int rank; /* index in bl_order */
++
++ Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes");
++ Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES,
++ "too many codes");
++ Tracev((stderr, "\nbl counts: "));
++ send_bits(s, lcodes-257, 5); /* not +255 as stated in appnote.txt */
++ send_bits(s, dcodes-1, 5);
++ send_bits(s, blcodes-4, 4); /* not -3 as stated in appnote.txt */
++ for (rank = 0; rank < blcodes; rank++) {
++ Tracev((stderr, "\nbl code %2d ", bl_order[rank]));
++ send_bits(s, s->bl_tree[bl_order[rank]].Len, 3);
++ }
++ Tracev((stderr, "\nbl tree: sent %ld", s->bits_sent));
++
++ send_tree(s, (ct_data *)s->dyn_ltree, lcodes-1); /* literal tree */
++ Tracev((stderr, "\nlit tree: sent %ld", s->bits_sent));
++
++ send_tree(s, (ct_data *)s->dyn_dtree, dcodes-1); /* distance tree */
++ Tracev((stderr, "\ndist tree: sent %ld", s->bits_sent));
++}
++
++/* ===========================================================================
++ * Send a stored block
++ */
++void _tr_stored_block(s, buf, stored_len, eof)
++ deflate_state *s;
++ charf *buf; /* input block */
++ ulg stored_len; /* length of input block */
++ int eof; /* true if this is the last block for a file */
++{
++ send_bits(s, (STORED_BLOCK<<1)+eof, 3); /* send block type */
++#ifdef DEBUG
++ s->compressed_len = (s->compressed_len + 3 + 7) & (ulg)~7L;
++ s->compressed_len += (stored_len + 4) << 3;
++#endif
++ copy_block(s, buf, (unsigned)stored_len, 1); /* with header */
++}
++
++/* ===========================================================================
++ * Send one empty static block to give enough lookahead for inflate.
++ * This takes 10 bits, of which 7 may remain in the bit buffer.
++ * The current inflate code requires 9 bits of lookahead. If the
++ * last two codes for the previous block (real code plus EOB) were coded
++ * on 5 bits or less, inflate may have only 5+3 bits of lookahead to decode
++ * the last real code. In this case we send two empty static blocks instead
++ * of one. (There are no problems if the previous block is stored or fixed.)
++ * To simplify the code, we assume the worst case of last real code encoded
++ * on one bit only.
++ */
++void _tr_align(s)
++ deflate_state *s;
++{
++ send_bits(s, STATIC_TREES<<1, 3);
++ send_code(s, END_BLOCK, static_ltree);
++#ifdef DEBUG
++ s->compressed_len += 10L; /* 3 for block type, 7 for EOB */
++#endif
++ bi_flush(s);
++ /* Of the 10 bits for the empty block, we have already sent
++ * (10 - bi_valid) bits. The lookahead for the last real code (before
++ * the EOB of the previous block) was thus at least one plus the length
++ * of the EOB plus what we have just sent of the empty static block.
++ */
++ if (1 + s->last_eob_len + 10 - s->bi_valid < 9) {
++ send_bits(s, STATIC_TREES<<1, 3);
++ send_code(s, END_BLOCK, static_ltree);
++#ifdef DEBUG
++ s->compressed_len += 10L;
++#endif
++ bi_flush(s);
++ }
++ s->last_eob_len = 7;
++}
++
++/* ===========================================================================
++ * Determine the best encoding for the current block: dynamic trees, static
++ * trees or store, and output the encoded block to the zip file.
++ */
++void _tr_flush_block(s, buf, stored_len, eof)
++ deflate_state *s;
++ charf *buf; /* input block, or NULL if too old */
++ ulg stored_len; /* length of input block */
++ int eof; /* true if this is the last block for a file */
++{
++ ulg opt_lenb, static_lenb; /* opt_len and static_len in bytes */
++ int max_blindex = 0; /* index of last bit length code of non zero freq */
++
++ /* Build the Huffman trees unless a stored block is forced */
++ if (s->level > 0) {
++
++ /* Check if the file is ascii or binary */
++ if (s->data_type == Z_UNKNOWN) set_data_type(s);
++
++ /* Construct the literal and distance trees */
++ build_tree(s, (tree_desc *)(&(s->l_desc)));
++ Tracev((stderr, "\nlit data: dyn %ld, stat %ld", s->opt_len,
++ s->static_len));
++
++ build_tree(s, (tree_desc *)(&(s->d_desc)));
++ Tracev((stderr, "\ndist data: dyn %ld, stat %ld", s->opt_len,
++ s->static_len));
++ /* At this point, opt_len and static_len are the total bit lengths of
++ * the compressed block data, excluding the tree representations.
++ */
++
++ /* Build the bit length tree for the above two trees, and get the index
++ * in bl_order of the last bit length code to send.
++ */
++ max_blindex = build_bl_tree(s);
++
++ /* Determine the best encoding. Compute first the block length in bytes*/
++ opt_lenb = (s->opt_len+3+7)>>3;
++ static_lenb = (s->static_len+3+7)>>3;
++
++ Tracev((stderr, "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u ",
++ opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len,
++ s->last_lit));
++
++ if (static_lenb <= opt_lenb) opt_lenb = static_lenb;
++
++ } else {
++ Assert(buf != (char*)0, "lost buf");
++ opt_lenb = static_lenb = stored_len + 5; /* force a stored block */
++ }
++
++#ifdef FORCE_STORED
++ if (buf != (char*)0) { /* force stored block */
++#else
++ if (stored_len+4 <= opt_lenb && buf != (char*)0) {
++ /* 4: two words for the lengths */
++#endif
++ /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE.
++ * Otherwise we can't have processed more than WSIZE input bytes since
++ * the last block flush, because compression would have been
++ * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to
++ * transform a block into a stored block.
++ */
++ _tr_stored_block(s, buf, stored_len, eof);
++
++#ifdef FORCE_STATIC
++ } else if (static_lenb >= 0) { /* force static trees */
++#else
++ } else if (static_lenb == opt_lenb) {
++#endif
++ send_bits(s, (STATIC_TREES<<1)+eof, 3);
++ compress_block(s, static_ltree, static_dtree);
++#ifdef DEBUG
++ s->compressed_len += 3 + s->static_len;
++#endif
++ } else {
++ send_bits(s, (DYN_TREES<<1)+eof, 3);
++ send_all_trees(s, s->l_desc.max_code+1, s->d_desc.max_code+1,
++ max_blindex+1);
++ compress_block(s, s->dyn_ltree, s->dyn_dtree);
++#ifdef DEBUG
++ s->compressed_len += 3 + s->opt_len;
++#endif
++ }
++ Assert (s->compressed_len == s->bits_sent, "bad compressed size");
++ /* The above check is made mod 2^32, for files larger than 512 MB
++ * and uLong implemented on 32 bits.
++ */
++ init_block(s);
++
++ if (eof) {
++ bi_windup(s);
++#ifdef DEBUG
++ s->compressed_len += 7; /* align on byte boundary */
++#endif
++ }
++ Tracev((stderr,"\ncomprlen %lu(%lu) ", s->compressed_len>>3,
++ s->compressed_len-7*eof));
++}
++
++/* ===========================================================================
++ * Save the match info and tally the frequency counts. Return true if
++ * the current block must be flushed.
++ */
++int _tr_tally (s, dist, lc)
++ deflate_state *s;
++ unsigned dist; /* distance of matched string */
++ unsigned lc; /* match length-MIN_MATCH or unmatched char (if dist==0) */
++{
++ s->d_buf[s->last_lit] = (ush)dist;
++ s->l_buf[s->last_lit++] = (uch)lc;
++ if (dist == 0) {
++ /* lc is the unmatched char */
++ s->dyn_ltree[lc].Freq++;
++ } else {
++ s->matches++;
++ /* Here, lc is the match length - MIN_MATCH */
++ dist--; /* dist = match distance - 1 */
++ Assert((ush)dist < (ush)MAX_DIST(s) &&
++ (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) &&
++ (ush)d_code(dist) < (ush)D_CODES, "_tr_tally: bad match");
++
++ s->dyn_ltree[_length_code[lc]+LITERALS+1].Freq++;
++ s->dyn_dtree[d_code(dist)].Freq++;
++ }
++
++#ifdef TRUNCATE_BLOCK
++ /* Try to guess if it is profitable to stop the current block here */
++ if ((s->last_lit & 0x1fff) == 0 && s->level > 2) {
++ /* Compute an upper bound for the compressed length */
++ ulg out_length = (ulg)s->last_lit*8L;
++ ulg in_length = (ulg)((long)s->strstart - s->block_start);
++ int dcode;
++ for (dcode = 0; dcode < D_CODES; dcode++) {
++ out_length += (ulg)s->dyn_dtree[dcode].Freq *
++ (5L+extra_dbits[dcode]);
++ }
++ out_length >>= 3;
++ Tracev((stderr,"\nlast_lit %u, in %ld, out ~%ld(%ld%%) ",
++ s->last_lit, in_length, out_length,
++ 100L - out_length*100L/in_length));
++ if (s->matches < s->last_lit/2 && out_length < in_length/2) return 1;
++ }
++#endif
++ return (s->last_lit == s->lit_bufsize-1);
++ /* We avoid equality with lit_bufsize because of wraparound at 64K
++ * on 16 bit machines and because stored blocks are restricted to
++ * 64K-1 bytes.
++ */
++}
++
++/* ===========================================================================
++ * Send the block data compressed using the given Huffman trees
++ */
++local void compress_block(s, ltree, dtree)
++ deflate_state *s;
++ const ct_data *ltree; /* literal tree */
++ const ct_data *dtree; /* distance tree */
++{
++ unsigned dist; /* distance of matched string */
++ int lc; /* match length or unmatched char (if dist == 0) */
++ unsigned lx = 0; /* running index in l_buf */
++ unsigned code; /* the code to send */
++ int extra; /* number of extra bits to send */
++
++ if (s->last_lit != 0) do {
++ dist = s->d_buf[lx];
++ lc = s->l_buf[lx++];
++ if (dist == 0) {
++ send_code(s, lc, ltree); /* send a literal byte */
++ Tracecv(isgraph(lc), (stderr," '%c' ", lc));
++ } else {
++ /* Here, lc is the match length - MIN_MATCH */
++ code = _length_code[lc];
++ send_code(s, code+LITERALS+1, ltree); /* send the length code */
++ extra = extra_lbits[code];
++ if (extra != 0) {
++ lc -= base_length[code];
++ send_bits(s, lc, extra); /* send the extra length bits */
++ }
++ dist--; /* dist is now the match distance - 1 */
++ code = d_code(dist);
++ Assert (code < D_CODES, "bad d_code");
++
++ send_code(s, code, dtree); /* send the distance code */
++ extra = extra_dbits[code];
++ if (extra != 0) {
++ dist -= base_dist[code];
++ send_bits(s, dist, extra); /* send the extra distance bits */
++ }
++ } /* literal or match pair ? */
++
++ /* Check that the overlay between pending_buf and d_buf+l_buf is ok: */
++ Assert(s->pending < s->lit_bufsize + 2*lx, "pendingBuf overflow");
++
++ } while (lx < s->last_lit);
++
++ send_code(s, END_BLOCK, ltree);
++ s->last_eob_len = ltree[END_BLOCK].Len;
++}
++
++/* ===========================================================================
++ * Set the data type to ASCII or BINARY, using a crude approximation:
++ * binary if more than 20% of the bytes are <= 6 or >= 128, ascii otherwise.
++ * IN assertion: the fields freq of dyn_ltree are set and the total of all
++ * frequencies does not exceed 64K (to fit in an int on 16 bit machines).
++ */
++local void set_data_type(s)
++ deflate_state *s;
++{
++ int n = 0;
++ unsigned ascii_freq = 0;
++ unsigned bin_freq = 0;
++ while (n < 7) bin_freq += s->dyn_ltree[n++].Freq;
++ while (n < 128) ascii_freq += s->dyn_ltree[n++].Freq;
++ while (n < LITERALS) bin_freq += s->dyn_ltree[n++].Freq;
++ s->data_type = (Byte)(bin_freq > (ascii_freq >> 2) ? Z_BINARY : Z_ASCII);
++}
++
++/* ===========================================================================
++ * Reverse the first len bits of a code, using straightforward code (a faster
++ * method would use a table)
++ * IN assertion: 1 <= len <= 15
++ */
++local unsigned bi_reverse(code, len)
++ unsigned code; /* the value to invert */
++ int len; /* its bit length */
++{
++ register unsigned res = 0;
++ do {
++ res |= code & 1;
++ code >>= 1, res <<= 1;
++ } while (--len > 0);
++ return res >> 1;
++}
++
++/* ===========================================================================
++ * Flush the bit buffer, keeping at most 7 bits in it.
++ */
++local void bi_flush(s)
++ deflate_state *s;
++{
++ if (s->bi_valid == 16) {
++ put_short(s, s->bi_buf);
++ s->bi_buf = 0;
++ s->bi_valid = 0;
++ } else if (s->bi_valid >= 8) {
++ put_byte(s, (Byte)s->bi_buf);
++ s->bi_buf >>= 8;
++ s->bi_valid -= 8;
++ }
++}
++
++/* ===========================================================================
++ * Flush the bit buffer and align the output on a byte boundary
++ */
++local void bi_windup(s)
++ deflate_state *s;
++{
++ if (s->bi_valid > 8) {
++ put_short(s, s->bi_buf);
++ } else if (s->bi_valid > 0) {
++ put_byte(s, (Byte)s->bi_buf);
++ }
++ s->bi_buf = 0;
++ s->bi_valid = 0;
++#ifdef DEBUG
++ s->bits_sent = (s->bits_sent+7) & ~7;
++#endif
++}
++
++/* ===========================================================================
++ * Copy a stored block, storing first the length and its
++ * one's complement if requested.
++ */
++local void copy_block(s, buf, len, header)
++ deflate_state *s;
++ charf *buf; /* the input data */
++ unsigned len; /* its length */
++ int header; /* true if block header must be written */
++{
++ bi_windup(s); /* align on byte boundary */
++ s->last_eob_len = 8; /* enough lookahead for inflate */
++
++ if (header) {
++ put_short(s, (ush)len);
++ put_short(s, (ush)~len);
++#ifdef DEBUG
++ s->bits_sent += 2*16;
++#endif
++ }
++#ifdef DEBUG
++ s->bits_sent += (ulg)len<<3;
++#endif
++ while (len--) {
++ put_byte(s, *buf++);
++ }
++}
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/zlib/trees.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,128 @@
++/* header created automatically with -DGEN_TREES_H */
++
++local const ct_data static_ltree[L_CODES+2] = {
++{{ 12},{ 8}}, {{140},{ 8}}, {{ 76},{ 8}}, {{204},{ 8}}, {{ 44},{ 8}},
++{{172},{ 8}}, {{108},{ 8}}, {{236},{ 8}}, {{ 28},{ 8}}, {{156},{ 8}},
++{{ 92},{ 8}}, {{220},{ 8}}, {{ 60},{ 8}}, {{188},{ 8}}, {{124},{ 8}},
++{{252},{ 8}}, {{ 2},{ 8}}, {{130},{ 8}}, {{ 66},{ 8}}, {{194},{ 8}},
++{{ 34},{ 8}}, {{162},{ 8}}, {{ 98},{ 8}}, {{226},{ 8}}, {{ 18},{ 8}},
++{{146},{ 8}}, {{ 82},{ 8}}, {{210},{ 8}}, {{ 50},{ 8}}, {{178},{ 8}},
++{{114},{ 8}}, {{242},{ 8}}, {{ 10},{ 8}}, {{138},{ 8}}, {{ 74},{ 8}},
++{{202},{ 8}}, {{ 42},{ 8}}, {{170},{ 8}}, {{106},{ 8}}, {{234},{ 8}},
++{{ 26},{ 8}}, {{154},{ 8}}, {{ 90},{ 8}}, {{218},{ 8}}, {{ 58},{ 8}},
++{{186},{ 8}}, {{122},{ 8}}, {{250},{ 8}}, {{ 6},{ 8}}, {{134},{ 8}},
++{{ 70},{ 8}}, {{198},{ 8}}, {{ 38},{ 8}}, {{166},{ 8}}, {{102},{ 8}},
++{{230},{ 8}}, {{ 22},{ 8}}, {{150},{ 8}}, {{ 86},{ 8}}, {{214},{ 8}},
++{{ 54},{ 8}}, {{182},{ 8}}, {{118},{ 8}}, {{246},{ 8}}, {{ 14},{ 8}},
++{{142},{ 8}}, {{ 78},{ 8}}, {{206},{ 8}}, {{ 46},{ 8}}, {{174},{ 8}},
++{{110},{ 8}}, {{238},{ 8}}, {{ 30},{ 8}}, {{158},{ 8}}, {{ 94},{ 8}},
++{{222},{ 8}}, {{ 62},{ 8}}, {{190},{ 8}}, {{126},{ 8}}, {{254},{ 8}},
++{{ 1},{ 8}}, {{129},{ 8}}, {{ 65},{ 8}}, {{193},{ 8}}, {{ 33},{ 8}},
++{{161},{ 8}}, {{ 97},{ 8}}, {{225},{ 8}}, {{ 17},{ 8}}, {{145},{ 8}},
++{{ 81},{ 8}}, {{209},{ 8}}, {{ 49},{ 8}}, {{177},{ 8}}, {{113},{ 8}},
++{{241},{ 8}}, {{ 9},{ 8}}, {{137},{ 8}}, {{ 73},{ 8}}, {{201},{ 8}},
++{{ 41},{ 8}}, {{169},{ 8}}, {{105},{ 8}}, {{233},{ 8}}, {{ 25},{ 8}},
++{{153},{ 8}}, {{ 89},{ 8}}, {{217},{ 8}}, {{ 57},{ 8}}, {{185},{ 8}},
++{{121},{ 8}}, {{249},{ 8}}, {{ 5},{ 8}}, {{133},{ 8}}, {{ 69},{ 8}},
++{{197},{ 8}}, {{ 37},{ 8}}, {{165},{ 8}}, {{101},{ 8}}, {{229},{ 8}},
++{{ 21},{ 8}}, {{149},{ 8}}, {{ 85},{ 8}}, {{213},{ 8}}, {{ 53},{ 8}},
++{{181},{ 8}}, {{117},{ 8}}, {{245},{ 8}}, {{ 13},{ 8}}, {{141},{ 8}},
++{{ 77},{ 8}}, {{205},{ 8}}, {{ 45},{ 8}}, {{173},{ 8}}, {{109},{ 8}},
++{{237},{ 8}}, {{ 29},{ 8}}, {{157},{ 8}}, {{ 93},{ 8}}, {{221},{ 8}},
++{{ 61},{ 8}}, {{189},{ 8}}, {{125},{ 8}}, {{253},{ 8}}, {{ 19},{ 9}},
++{{275},{ 9}}, {{147},{ 9}}, {{403},{ 9}}, {{ 83},{ 9}}, {{339},{ 9}},
++{{211},{ 9}}, {{467},{ 9}}, {{ 51},{ 9}}, {{307},{ 9}}, {{179},{ 9}},
++{{435},{ 9}}, {{115},{ 9}}, {{371},{ 9}}, {{243},{ 9}}, {{499},{ 9}},
++{{ 11},{ 9}}, {{267},{ 9}}, {{139},{ 9}}, {{395},{ 9}}, {{ 75},{ 9}},
++{{331},{ 9}}, {{203},{ 9}}, {{459},{ 9}}, {{ 43},{ 9}}, {{299},{ 9}},
++{{171},{ 9}}, {{427},{ 9}}, {{107},{ 9}}, {{363},{ 9}}, {{235},{ 9}},
++{{491},{ 9}}, {{ 27},{ 9}}, {{283},{ 9}}, {{155},{ 9}}, {{411},{ 9}},
++{{ 91},{ 9}}, {{347},{ 9}}, {{219},{ 9}}, {{475},{ 9}}, {{ 59},{ 9}},
++{{315},{ 9}}, {{187},{ 9}}, {{443},{ 9}}, {{123},{ 9}}, {{379},{ 9}},
++{{251},{ 9}}, {{507},{ 9}}, {{ 7},{ 9}}, {{263},{ 9}}, {{135},{ 9}},
++{{391},{ 9}}, {{ 71},{ 9}}, {{327},{ 9}}, {{199},{ 9}}, {{455},{ 9}},
++{{ 39},{ 9}}, {{295},{ 9}}, {{167},{ 9}}, {{423},{ 9}}, {{103},{ 9}},
++{{359},{ 9}}, {{231},{ 9}}, {{487},{ 9}}, {{ 23},{ 9}}, {{279},{ 9}},
++{{151},{ 9}}, {{407},{ 9}}, {{ 87},{ 9}}, {{343},{ 9}}, {{215},{ 9}},
++{{471},{ 9}}, {{ 55},{ 9}}, {{311},{ 9}}, {{183},{ 9}}, {{439},{ 9}},
++{{119},{ 9}}, {{375},{ 9}}, {{247},{ 9}}, {{503},{ 9}}, {{ 15},{ 9}},
++{{271},{ 9}}, {{143},{ 9}}, {{399},{ 9}}, {{ 79},{ 9}}, {{335},{ 9}},
++{{207},{ 9}}, {{463},{ 9}}, {{ 47},{ 9}}, {{303},{ 9}}, {{175},{ 9}},
++{{431},{ 9}}, {{111},{ 9}}, {{367},{ 9}}, {{239},{ 9}}, {{495},{ 9}},
++{{ 31},{ 9}}, {{287},{ 9}}, {{159},{ 9}}, {{415},{ 9}}, {{ 95},{ 9}},
++{{351},{ 9}}, {{223},{ 9}}, {{479},{ 9}}, {{ 63},{ 9}}, {{319},{ 9}},
++{{191},{ 9}}, {{447},{ 9}}, {{127},{ 9}}, {{383},{ 9}}, {{255},{ 9}},
++{{511},{ 9}}, {{ 0},{ 7}}, {{ 64},{ 7}}, {{ 32},{ 7}}, {{ 96},{ 7}},
++{{ 16},{ 7}}, {{ 80},{ 7}}, {{ 48},{ 7}}, {{112},{ 7}}, {{ 8},{ 7}},
++{{ 72},{ 7}}, {{ 40},{ 7}}, {{104},{ 7}}, {{ 24},{ 7}}, {{ 88},{ 7}},
++{{ 56},{ 7}}, {{120},{ 7}}, {{ 4},{ 7}}, {{ 68},{ 7}}, {{ 36},{ 7}},
++{{100},{ 7}}, {{ 20},{ 7}}, {{ 84},{ 7}}, {{ 52},{ 7}}, {{116},{ 7}},
++{{ 3},{ 8}}, {{131},{ 8}}, {{ 67},{ 8}}, {{195},{ 8}}, {{ 35},{ 8}},
++{{163},{ 8}}, {{ 99},{ 8}}, {{227},{ 8}}
++};
++
++local const ct_data static_dtree[D_CODES] = {
++{{ 0},{ 5}}, {{16},{ 5}}, {{ 8},{ 5}}, {{24},{ 5}}, {{ 4},{ 5}},
++{{20},{ 5}}, {{12},{ 5}}, {{28},{ 5}}, {{ 2},{ 5}}, {{18},{ 5}},
++{{10},{ 5}}, {{26},{ 5}}, {{ 6},{ 5}}, {{22},{ 5}}, {{14},{ 5}},
++{{30},{ 5}}, {{ 1},{ 5}}, {{17},{ 5}}, {{ 9},{ 5}}, {{25},{ 5}},
++{{ 5},{ 5}}, {{21},{ 5}}, {{13},{ 5}}, {{29},{ 5}}, {{ 3},{ 5}},
++{{19},{ 5}}, {{11},{ 5}}, {{27},{ 5}}, {{ 7},{ 5}}, {{23},{ 5}}
++};
++
++const uch _dist_code[DIST_CODE_LEN] = {
++ 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8,
++ 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10,
++10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
++11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
++12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13,
++13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
++13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
++14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
++14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
++14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15,
++15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
++15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
++15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 16, 17,
++18, 18, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22,
++23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
++24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
++26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
++26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27,
++27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
++27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
++28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
++28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
++28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
++29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
++29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
++29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29
++};
++
++const uch _length_code[MAX_MATCH-MIN_MATCH+1]= {
++ 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, 12,
++13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16,
++17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19,
++19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
++21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22,
++22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23,
++23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
++24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
++25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
++25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26,
++26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
++26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
++27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28
++};
++
++local const int base_length[LENGTH_CODES] = {
++0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56,
++64, 80, 96, 112, 128, 160, 192, 224, 0
++};
++
++local const int base_dist[D_CODES] = {
++ 0, 1, 2, 3, 4, 6, 8, 12, 16, 24,
++ 32, 48, 64, 96, 128, 192, 256, 384, 512, 768,
++ 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576
++};
++
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/zlib/zconf.h Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,309 @@
++/* zconf.h -- configuration of the zlib compression library
++ * Copyright (C) 1995-2002 Jean-loup Gailly.
++ * For conditions of distribution and use, see copyright notice in zlib.h
++ */
++
++/* @(#) $Id$ */
++
++#ifndef _ZCONF_H
++#define _ZCONF_H
++
++/*
++ * If you *really* need a unique prefix for all types and library functions,
++ * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it.
++ */
++#ifdef IPCOMP_PREFIX
++# define deflateInit_ ipcomp_deflateInit_
++# define deflate ipcomp_deflate
++# define deflateEnd ipcomp_deflateEnd
++# define inflateInit_ ipcomp_inflateInit_
++# define inflate ipcomp_inflate
++# define inflateEnd ipcomp_inflateEnd
++# define deflateInit2_ ipcomp_deflateInit2_
++# define deflateSetDictionary ipcomp_deflateSetDictionary
++# define deflateCopy ipcomp_deflateCopy
++# define deflateReset ipcomp_deflateReset
++# define deflateParams ipcomp_deflateParams
++# define inflateInit2_ ipcomp_inflateInit2_
++# define inflateSetDictionary ipcomp_inflateSetDictionary
++# define inflateSync ipcomp_inflateSync
++# define inflateSyncPoint ipcomp_inflateSyncPoint
++# define inflateReset ipcomp_inflateReset
++# define compress ipcomp_compress
++# define compress2 ipcomp_compress2
++# define uncompress ipcomp_uncompress
++# define adler32 ipcomp_adler32
++# define crc32 ipcomp_crc32
++# define get_crc_table ipcomp_get_crc_table
++/* SSS: these also need to be prefixed to avoid clash with ppp_deflate and ext2compression */
++# define inflate_blocks ipcomp_deflate_blocks
++# define inflate_blocks_free ipcomp_deflate_blocks_free
++# define inflate_blocks_new ipcomp_inflate_blocks_new
++# define inflate_blocks_reset ipcomp_inflate_blocks_reset
++# define inflate_blocks_sync_point ipcomp_inflate_blocks_sync_point
++# define inflate_set_dictionary ipcomp_inflate_set_dictionary
++# define inflate_codes ipcomp_inflate_codes
++# define inflate_codes_free ipcomp_inflate_codes_free
++# define inflate_codes_new ipcomp_inflate_codes_new
++# define inflate_fast ipcomp_inflate_fast
++# define inflate_trees_bits ipcomp_inflate_trees_bits
++# define inflate_trees_dynamic ipcomp_inflate_trees_dynamic
++# define inflate_trees_fixed ipcomp_inflate_trees_fixed
++# define inflate_flush ipcomp_inflate_flush
++# define inflate_mask ipcomp_inflate_mask
++# define _dist_code _ipcomp_dist_code
++# define _length_code _ipcomp_length_code
++# define _tr_align _ipcomp_tr_align
++# define _tr_flush_block _ipcomp_tr_flush_block
++# define _tr_init _ipcomp_tr_init
++# define _tr_stored_block _ipcomp_tr_stored_block
++# define _tr_tally _ipcomp_tr_tally
++# define zError ipcomp_zError
++# define z_errmsg ipcomp_z_errmsg
++# define zlibVersion ipcomp_zlibVersion
++# define match_init ipcomp_match_init
++# define longest_match ipcomp_longest_match
++#endif
++
++#ifdef Z_PREFIX
++# define Byte z_Byte
++# define uInt z_uInt
++# define uLong z_uLong
++# define Bytef z_Bytef
++# define charf z_charf
++# define intf z_intf
++# define uIntf z_uIntf
++# define uLongf z_uLongf
++# define voidpf z_voidpf
++# define voidp z_voidp
++#endif
++
++#if (defined(_WIN32) || defined(__WIN32__)) && !defined(WIN32)
++# define WIN32
++#endif
++#if defined(__GNUC__) || defined(WIN32) || defined(__386__) || defined(i386)
++# ifndef __32BIT__
++# define __32BIT__
++# endif
++#endif
++#if defined(__MSDOS__) && !defined(MSDOS)
++# define MSDOS
++#endif
++
++/*
++ * Compile with -DMAXSEG_64K if the alloc function cannot allocate more
++ * than 64k bytes at a time (needed on systems with 16-bit int).
++ */
++#if defined(MSDOS) && !defined(__32BIT__)
++# define MAXSEG_64K
++#endif
++#ifdef MSDOS
++# define UNALIGNED_OK
++#endif
++
++#if (defined(MSDOS) || defined(_WINDOWS) || defined(WIN32)) && !defined(STDC)
++# define STDC
++#endif
++#if defined(__STDC__) || defined(__cplusplus) || defined(__OS2__)
++# ifndef STDC
++# define STDC
++# endif
++#endif
++
++#ifndef STDC
++# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */
++# define const
++# endif
++#endif
++
++/* Some Mac compilers merge all .h files incorrectly: */
++#if defined(__MWERKS__) || defined(applec) ||defined(THINK_C) ||defined(__SC__)
++# define NO_DUMMY_DECL
++#endif
++
++/* Old Borland C incorrectly complains about missing returns: */
++#if defined(__BORLANDC__) && (__BORLANDC__ < 0x500)
++# define NEED_DUMMY_RETURN
++#endif
++
++
++/* Maximum value for memLevel in deflateInit2 */
++#ifndef MAX_MEM_LEVEL
++# ifdef MAXSEG_64K
++# define MAX_MEM_LEVEL 8
++# else
++# define MAX_MEM_LEVEL 9
++# endif
++#endif
++
++/* Maximum value for windowBits in deflateInit2 and inflateInit2.
++ * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files
++ * created by gzip. (Files created by minigzip can still be extracted by
++ * gzip.)
++ */
++#ifndef MAX_WBITS
++# define MAX_WBITS 15 /* 32K LZ77 window */
++#endif
++
++/* The memory requirements for deflate are (in bytes):
++ (1 << (windowBits+2)) + (1 << (memLevel+9))
++ that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values)
++ plus a few kilobytes for small objects. For example, if you want to reduce
++ the default memory requirements from 256K to 128K, compile with
++ make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7"
++ Of course this will generally degrade compression (there's no free lunch).
++
++ The memory requirements for inflate are (in bytes) 1 << windowBits
++ that is, 32K for windowBits=15 (default value) plus a few kilobytes
++ for small objects.
++*/
++
++ /* Type declarations */
++
++#ifndef OF /* function prototypes */
++# ifdef STDC
++# define OF(args) args
++# else
++# define OF(args) ()
++# endif
++#endif
++
++/* The following definitions for FAR are needed only for MSDOS mixed
++ * model programming (small or medium model with some far allocations).
++ * This was tested only with MSC; for other MSDOS compilers you may have
++ * to define NO_MEMCPY in zutil.h. If you don't need the mixed model,
++ * just define FAR to be empty.
++ */
++#if (defined(M_I86SM) || defined(M_I86MM)) && !defined(__32BIT__)
++ /* MSC small or medium model */
++# define SMALL_MEDIUM
++# ifdef _MSC_VER
++# define FAR _far
++# else
++# define FAR far
++# endif
++#endif
++#if defined(__BORLANDC__) && (defined(__SMALL__) || defined(__MEDIUM__))
++# ifndef __32BIT__
++# define SMALL_MEDIUM
++# define FAR _far
++# endif
++#endif
++
++/* Compile with -DZLIB_DLL for Windows DLL support */
++#if defined(ZLIB_DLL)
++# if defined(_WINDOWS) || defined(WINDOWS)
++# ifdef FAR
++# undef FAR
++# endif
++# include <windows.h>
++# define ZEXPORT WINAPI
++# ifdef WIN32
++# define ZEXPORTVA WINAPIV
++# else
++# define ZEXPORTVA FAR _cdecl _export
++# endif
++# endif
++# if defined (__BORLANDC__)
++# if (__BORLANDC__ >= 0x0500) && defined (WIN32)
++# include <windows.h>
++# define ZEXPORT __declspec(dllexport) WINAPI
++# define ZEXPORTRVA __declspec(dllexport) WINAPIV
++# else
++# if defined (_Windows) && defined (__DLL__)
++# define ZEXPORT _export
++# define ZEXPORTVA _export
++# endif
++# endif
++# endif
++#endif
++
++#if defined (__BEOS__)
++# if defined (ZLIB_DLL)
++# define ZEXTERN extern __declspec(dllexport)
++# else
++# define ZEXTERN extern __declspec(dllimport)
++# endif
++#endif
++
++#ifndef ZEXPORT
++# define ZEXPORT
++#endif
++#ifndef ZEXPORTVA
++# define ZEXPORTVA
++#endif
++#ifndef ZEXTERN
++# define ZEXTERN extern
++#endif
++
++#ifndef FAR
++# define FAR
++#endif
++
++#if !defined(MACOS) && !defined(TARGET_OS_MAC)
++typedef unsigned char Byte; /* 8 bits */
++#endif
++typedef unsigned int uInt; /* 16 bits or more */
++typedef unsigned long uLong; /* 32 bits or more */
++
++#ifdef SMALL_MEDIUM
++ /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */
++# define Bytef Byte FAR
++#else
++ typedef Byte FAR Bytef;
++#endif
++typedef char FAR charf;
++typedef int FAR intf;
++typedef uInt FAR uIntf;
++typedef uLong FAR uLongf;
++
++#ifdef STDC
++ typedef void FAR *voidpf;
++ typedef void *voidp;
++#else
++ typedef Byte FAR *voidpf;
++ typedef Byte *voidp;
++#endif
++
++#ifdef HAVE_UNISTD_H
++# include <sys/types.h> /* for off_t */
++# include <unistd.h> /* for SEEK_* and off_t */
++# define z_off_t off_t
++#endif
++#ifndef SEEK_SET
++# define SEEK_SET 0 /* Seek from beginning of file. */
++# define SEEK_CUR 1 /* Seek from current position. */
++# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */
++#endif
++#ifndef z_off_t
++# define z_off_t long
++#endif
++
++/* MVS linker does not support external names larger than 8 bytes */
++#if defined(__MVS__)
++# pragma map(deflateInit_,"DEIN")
++# pragma map(deflateInit2_,"DEIN2")
++# pragma map(deflateEnd,"DEEND")
++# pragma map(inflateInit_,"ININ")
++# pragma map(inflateInit2_,"ININ2")
++# pragma map(inflateEnd,"INEND")
++# pragma map(inflateSync,"INSY")
++# pragma map(inflateSetDictionary,"INSEDI")
++# pragma map(inflate_blocks,"INBL")
++# pragma map(inflate_blocks_new,"INBLNE")
++# pragma map(inflate_blocks_free,"INBLFR")
++# pragma map(inflate_blocks_reset,"INBLRE")
++# pragma map(inflate_codes_free,"INCOFR")
++# pragma map(inflate_codes,"INCO")
++# pragma map(inflate_fast,"INFA")
++# pragma map(inflate_flush,"INFLU")
++# pragma map(inflate_mask,"INMA")
++# pragma map(inflate_set_dictionary,"INSEDI2")
++# pragma map(ipcomp_inflate_copyright,"INCOPY")
++# pragma map(inflate_trees_bits,"INTRBI")
++# pragma map(inflate_trees_dynamic,"INTRDY")
++# pragma map(inflate_trees_fixed,"INTRFI")
++# pragma map(inflate_trees_free,"INTRFR")
++#endif
++
++#endif /* _ZCONF_H */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/lib/zlib/zutil.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,227 @@
++/* zutil.c -- target dependent utility functions for the compression library
++ * Copyright (C) 1995-2002 Jean-loup Gailly.
++ * For conditions of distribution and use, see copyright notice in zlib.h
++ */
++
++/* @(#) $Id$ */
++
++#include <zlib/zutil.h>
++
++#define MY_ZCALLOC
++
++struct internal_state {int dummy;}; /* for buggy compilers */
++
++#ifndef STDC
++extern void exit OF((int));
++#endif
++
++const char *z_errmsg[10] = {
++"need dictionary", /* Z_NEED_DICT 2 */
++"stream end", /* Z_STREAM_END 1 */
++"", /* Z_OK 0 */
++"file error", /* Z_ERRNO (-1) */
++"stream error", /* Z_STREAM_ERROR (-2) */
++"data error", /* Z_DATA_ERROR (-3) */
++"insufficient memory", /* Z_MEM_ERROR (-4) */
++"buffer error", /* Z_BUF_ERROR (-5) */
++"incompatible version",/* Z_VERSION_ERROR (-6) */
++""};
++
++
++const char * ZEXPORT zlibVersion()
++{
++ return ZLIB_VERSION;
++}
++
++#ifdef DEBUG
++
++# ifndef verbose
++# define verbose 0
++# endif
++int z_verbose = verbose;
++
++void z_error (m)
++ char *m;
++{
++ fprintf(stderr, "%s\n", m);
++ exit(1);
++}
++#endif
++
++/* exported to allow conversion of error code to string for compress() and
++ * uncompress()
++ */
++const char * ZEXPORT zError(err)
++ int err;
++{
++ return ERR_MSG(err);
++}
++
++
++#ifndef HAVE_MEMCPY
++
++void zmemcpy(dest, source, len)
++ Bytef* dest;
++ const Bytef* source;
++ uInt len;
++{
++ if (len == 0) return;
++ do {
++ *dest++ = *source++; /* ??? to be unrolled */
++ } while (--len != 0);
++}
++
++int zmemcmp(s1, s2, len)
++ const Bytef* s1;
++ const Bytef* s2;
++ uInt len;
++{
++ uInt j;
++
++ for (j = 0; j < len; j++) {
++ if (s1[j] != s2[j]) return 2*(s1[j] > s2[j])-1;
++ }
++ return 0;
++}
++
++void zmemzero(dest, len)
++ Bytef* dest;
++ uInt len;
++{
++ if (len == 0) return;
++ do {
++ *dest++ = 0; /* ??? to be unrolled */
++ } while (--len != 0);
++}
++#endif
++
++#ifdef __TURBOC__
++#if (defined( __BORLANDC__) || !defined(SMALL_MEDIUM)) && !defined(__32BIT__)
++/* Small and medium model in Turbo C are for now limited to near allocation
++ * with reduced MAX_WBITS and MAX_MEM_LEVEL
++ */
++# define MY_ZCALLOC
++
++/* Turbo C malloc() does not allow dynamic allocation of 64K bytes
++ * and farmalloc(64K) returns a pointer with an offset of 8, so we
++ * must fix the pointer. Warning: the pointer must be put back to its
++ * original form in order to free it, use zcfree().
++ */
++
++#define MAX_PTR 10
++/* 10*64K = 640K */
++
++local int next_ptr = 0;
++
++typedef struct ptr_table_s {
++ voidpf org_ptr;
++ voidpf new_ptr;
++} ptr_table;
++
++local ptr_table table[MAX_PTR];
++/* This table is used to remember the original form of pointers
++ * to large buffers (64K). Such pointers are normalized with a zero offset.
++ * Since MSDOS is not a preemptive multitasking OS, this table is not
++ * protected from concurrent access. This hack doesn't work anyway on
++ * a protected system like OS/2. Use Microsoft C instead.
++ */
++
++voidpf zcalloc (voidpf opaque, unsigned items, unsigned size)
++{
++ voidpf buf = opaque; /* just to make some compilers happy */
++ ulg bsize = (ulg)items*size;
++
++ /* If we allocate less than 65520 bytes, we assume that farmalloc
++ * will return a usable pointer which doesn't have to be normalized.
++ */
++ if (bsize < 65520L) {
++ buf = farmalloc(bsize);
++ if (*(ush*)&buf != 0) return buf;
++ } else {
++ buf = farmalloc(bsize + 16L);
++ }
++ if (buf == NULL || next_ptr >= MAX_PTR) return NULL;
++ table[next_ptr].org_ptr = buf;
++
++ /* Normalize the pointer to seg:0 */
++ *((ush*)&buf+1) += ((ush)((uch*)buf-0) + 15) >> 4;
++ *(ush*)&buf = 0;
++ table[next_ptr++].new_ptr = buf;
++ return buf;
++}
++
++void zcfree (voidpf opaque, voidpf ptr)
++{
++ int n;
++ if (*(ush*)&ptr != 0) { /* object < 64K */
++ farfree(ptr);
++ return;
++ }
++ /* Find the original pointer */
++ for (n = 0; n < next_ptr; n++) {
++ if (ptr != table[n].new_ptr) continue;
++
++ farfree(table[n].org_ptr);
++ while (++n < next_ptr) {
++ table[n-1] = table[n];
++ }
++ next_ptr--;
++ return;
++ }
++ ptr = opaque; /* just to make some compilers happy */
++ Assert(0, "zcfree: ptr not found");
++}
++#endif
++#endif /* __TURBOC__ */
++
++
++#if defined(M_I86) && !defined(__32BIT__)
++/* Microsoft C in 16-bit mode */
++
++# define MY_ZCALLOC
++
++#if (!defined(_MSC_VER) || (_MSC_VER <= 600))
++# define _halloc halloc
++# define _hfree hfree
++#endif
++
++voidpf zcalloc (voidpf opaque, unsigned items, unsigned size)
++{
++ if (opaque) opaque = 0; /* to make compiler happy */
++ return _halloc((long)items, size);
++}
++
++void zcfree (voidpf opaque, voidpf ptr)
++{
++ if (opaque) opaque = 0; /* to make compiler happy */
++ _hfree(ptr);
++}
++
++#endif /* MSC */
++
++
++#ifndef MY_ZCALLOC /* Any system without a special alloc function */
++
++#ifndef STDC
++extern voidp calloc OF((uInt items, uInt size));
++extern void free OF((voidpf ptr));
++#endif
++
++voidpf zcalloc (opaque, items, size)
++ voidpf opaque;
++ unsigned items;
++ unsigned size;
++{
++ if (opaque) items += size - size; /* make compiler happy */
++ return (voidpf)calloc(items, size);
++}
++
++void zcfree (opaque, ptr)
++ voidpf opaque;
++ voidpf ptr;
++{
++ free(ptr);
++ if (opaque) return; /* make compiler happy */
++}
++
++#endif /* MY_ZCALLOC */
+--- linux/net/Config.in.orig Fri Feb 9 14:34:13 2001
++++ linux/net/Config.in Thu Feb 22 19:40:08 2001
+@@ -88,4 +88,9 @@
+ #bool 'Network code profiler' CONFIG_NET_PROFILE
+ endmenu
+
++tristate 'IP Security Protocol (FreeS/WAN IPSEC)' CONFIG_IPSEC
++if [ "$CONFIG_IPSEC" != "n" ]; then
++ source net/ipsec/Config.in
++fi
++
+ endmenu
+RCSID $Id$
+--- linux/net/Makefile.preipsec Mon Jun 11 22:15:27 2001
++++ linux/net/Makefile Tue Nov 6 21:07:43 2001
+@@ -17,6 +17,7 @@
+ subdir-$(CONFIG_NET) += 802 sched
+ subdir-$(CONFIG_INET) += ipv4
+ subdir-$(CONFIG_NETFILTER) += ipv4/netfilter
++subdir-$(CONFIG_IPSEC) += ipsec
+ subdir-$(CONFIG_UNIX) += unix
+ subdir-$(CONFIG_IPV6) += ipv6
+
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/net/ipsec/Config.in Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,76 @@
++#
++# IPSEC configuration
++# Copyright (C) 1998, 1999, 2000,2001 Richard Guy Briggs.
++#
++# This program is free software; you can redistribute it and/or modify it
++# under the terms of the GNU General Public License as published by the
++# Free Software Foundation; either version 2 of the License, or (at your
++# option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++#
++# This program is distributed in the hope that it will be useful, but
++# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++# for more details.
++#
++# RCSID $Id$
++
++comment 'IPSec options (Openswan)'
++
++bool ' IPSEC: IP-in-IP encapsulation (tunnel mode)' CONFIG_IPSEC_IPIP
++
++bool ' IPSEC: Authentication Header' CONFIG_IPSEC_AH
++if [ "$CONFIG_IPSEC_AH" = "y" -o "$CONFIG_IPSEC_ESP" = "y" ]; then
++ bool ' HMAC-MD5 authentication algorithm' CONFIG_IPSEC_AUTH_HMAC_MD5
++ bool ' HMAC-SHA1 authentication algorithm' CONFIG_IPSEC_AUTH_HMAC_SHA1
++fi
++
++bool ' IPSEC: Encapsulating Security Payload' CONFIG_IPSEC_ESP
++if [ "$CONFIG_IPSEC_ESP" = "y" ]; then
++ bool ' 3DES encryption algorithm' CONFIG_IPSEC_ENC_3DES
++ bool ' AES encryption algorithm' CONFIG_IPSEC_ENC_AES
++fi
++
++bool ' IPSEC Modular Extensions' CONFIG_IPSEC_ALG
++if [ "$CONFIG_IPSEC_ALG" != "n" ]; then
++ source net/ipsec/alg/Config.in
++fi
++
++bool ' IPSEC: IP Compression' CONFIG_IPSEC_IPCOMP
++
++bool ' IPSEC Debugging Option' CONFIG_IPSEC_DEBUG
++
++#
++#
++# $Log$
++# Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++# Turn off EOLN_NATIVE flag
++#
++# (Logical change 1.5010)
++#
++# Revision 1.30 2004/06/23 09:49:37 ken
++# Free -> Open
++#
++# Revision 1.29 2004/04/06 02:49:25 mcr
++# pullup of algo code from alg-branch.
++#
++# Revision 1.28 2004/02/03 03:12:26 mcr
++# remove NAT-traversal option from IPsec config,
++# as it should be in the kernel configuration if
++# the NAT-T patch is installed.
++#
++# Revision 1.27.2.2 2004/04/05 04:30:46 mcr
++# patches for alg-branch to compile/work with 2.x openswan
++#
++# Revision 1.27.2.1 2003/12/23 12:48:25 jjo
++# Added missing alg part to linux/net/ipsec/Config.in
++#
++# Revision 1.27 2003/12/10 01:14:27 mcr
++# NAT-traversal patches to KLIPS.
++#
++# Revision 1.26 2002/04/24 07:36:26 mcr
++# Moved from ./klips/net/ipsec/Config.in,v
++#
++# Revision 1.25 2002/02/21 19:55:12 mcr
++# removed all traces of IPSEC_CONFIG_REGRESS because it
++# screwed up 2.2's "make menuconfig" scripts.
++#
++# Revision 1.24 2002/01/28 20:24:31 mcr
++# commented out IPSEC_REGRESS option from user visible config.
++#
++#
++
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/net/ipsec/Makefile Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,337 @@
++# Makefile for KLIPS kernel code as a module
++# Copyright (C) 1998, 1999, 2000,2001 Richard Guy Briggs.
++# Copyright (C) 2002 Michael Richardson <mcr@freeswan.org>
++#
++# This program is free software; you can redistribute it and/or modify it
++# under the terms of the GNU General Public License as published by the
++# Free Software Foundation; either version 2 of the License, or (at your
++# option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++#
++# This program is distributed in the hope that it will be useful, but
++# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++# for more details.
++#
++# RCSID $Id$
++#
++# Note! Dependencies are done automagically by 'make dep', which also
++# removes any old dependencies. DON'T put your own dependencies here
++# unless it's something special (ie not a .c file).
++#
++
++ifeq ($(strip $(KLIPSMODULE)),)
++OPENSWANSRCDIR=.
++else
++OPENSWANSRCDIR=../../..
++endif
++-include ${OPENSWANSRCDIR}/Makefile.ver
++
++ifeq ($(strip $(KLIPS_TOP)),)
++KLIPS_TOP=../..
++endif
++
++ifneq ($(strip $(KLIPSMODULE)),)
++
++ifndef TOPDIR
++TOPDIR:=/usr/src/linux
++endif
++export TOPDIR
++
++endif
++
++#
++# This magic from User-Mode-Linux list. It gets list of -I options, as
++# UML needs some extra, that varry by revision.
++#
++KERNEL_CFLAGS= $(shell $(MAKE) -C $(TOPDIR) --no-print-directory -s -f Makefile ARCH=$(ARCH) MAKEFLAGS= script SCRIPT='@echo $$(CFLAGS)' )
++
++MODULE_CFLAGS= $(shell $(MAKE) -C $(TOPDIR) --no-print-directory -s -f Makefile ARCH=$(ARCH) MAKEFLAGS= script SCRIPT='@echo $$(MODFLAGS)' )
++
++subdir- :=
++subdir-n :=
++subdir-y :=
++subdir-m :=
++
++
++MOD_DESTDIR:=net/ipsec
++
++export TOPDIR
++
++all: ipsec.o
++
++foo:
++ echo KERNEL: ${KERNEL_CFLAGS}
++ echo MODULE: ${MODULE_CFLAGS}
++
++ipsec.o: foo
++
++O_TARGET := ipsec.o
++obj-y := ipsec_init.o ipsec_sa.o ipsec_radij.o radij.o
++obj-y += ipsec_life.o ipsec_proc.o
++obj-y += ipsec_tunnel.o ipsec_xmit.o ipsec_rcv.o ipsec_ipip.o
++obj-y += sysctl_net_ipsec.o
++obj-y += pfkey_v2.o pfkey_v2_parser.o pfkey_v2_ext_process.o
++obj-y += version.o
++obj-$(CONFIG_IPSEC_AH) += ipsec_ah.o
++obj-$(CONFIG_IPSEC_ESP) += ipsec_esp.o
++obj-$(CONFIG_IPSEC_IPCOMP)+= ipsec_ipcomp.o
++
++CFLAGS_ipsec_alg.o += -DEXPORT_SYMTAB
++obj-$(CONFIG_IPSEC_ALG) += ipsec_alg.o
++obj-$(CONFIG_IPSEC_ENC_AES) += ipsec_alg_aes.o
++obj-$(CONFIG_IPSEC_ENC_CRYPTOAPI) += ipsec_alg_cryptoapi.o
++
++export-objs += ipsec_alg.o
++
++
++LIBDESDIR=${KLIPS_TOP}/crypto/ciphers/des
++VPATH+= ${LIBDESDIR}
++
++include ${LIBDESDIR}/Makefile.objs
++
++LIBFREESWANDIR=${KLIPS_TOP}/lib/libfreeswan
++VPATH+=${LIBFREESWANDIR}
++
++include ${LIBFREESWANDIR}/Makefile.objs
++
++# IPcomp stuff
++obj-$(CONFIG_IPSEC_IPCOMP) += ipcomp.o
++
++LIBZLIBSRCDIR=${KLIPS_TOP}/lib/zlib
++VPATH+=${LIBZLIBSRCDIR}
++
++LIBAESDIR=$(KLIPS_TOP)/crypto/ciphers/aes
++VPATH+=${LIBAESDIR}
++include ${LIBAESDIR}/Makefile.objs
++
++# CFLAGS='$(CFLAGS)' \
++# MODULE_CFLAGS='$(MODULE_CFLAGS)' KERNEL_CFLAGS='$(KERNEL_CFLAGS)' \
++#
++include ${LIBZLIBSRCDIR}/Makefile.objs
++
++export-objs := radij.o
++
++EXTRA_CFLAGS += $(ALGO_FLAGS)
++
++
++# include file with .h-style macros that would otherwise be created by
++# config. Must occur before other includes.
++ifneq ($(strip $(MODULE_DEF_INCLUDE)),)
++EXTRA_CFLAGS += -include ${MODULE_DEF_INCLUDE}
++endif
++
++# 'override CFLAGS' should really be 'EXTRA_CFLAGS'
++#EXTRA_CFLAGS += -nostdinc
++EXTRA_CFLAGS += -I${KLIPS_TOP}/include
++
++EXTRA_CFLAGS += -I${TOPDIR}/include
++EXTRA_CFLAGS += -I${LIBZLIBSRCDIR}
++
++ifeq ($(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION),2.4.2-2)
++EXTRA_CFLAGS += -DREDHAT_BOGOSITY
++endif
++
++ifeq ($(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION),2.4.3-12)
++EXTRA_CFLAGS += -DREDHAT_BOGOSITY
++endif
++
++
++#ifeq ($(CONFIG_IPSEC_DEBUG),y)
++#EXTRA_CFLAGS += -g
++#endif
++
++#ifeq ($(CONFIG_IPSEC_ALG), y)
++EXTRA_CFLAGS += -DCONFIG_IPSEC_ALG
++#endif
++# MOST of these flags are in KERNEL_CFLAGS already!
++
++EXTRA_CFLAGS += $(KLIPSCOMPILE)
++EXTRA_CFLAGS += -Wall
++#EXTRA_CFLAGS += -Werror
++#EXTRA_CFLAGS += -Wconversion
++#EXTRA_CFLAGS += -Wmissing-prototypes
++# cannot use both -Wpointer-arith and -Werror with CONFIG_HIGHMEM
++# include/linux/highmem.h has an inline function definition that uses void* arithmentic.
++ifeq ($(CONFIG_NOHIGHMEM),y)
++EXTRA_CFLAGS += -Wpointer-arith
++endif
++#EXTRA_CFLAGS += -Wcast-qual
++#EXTRA_CFLAGS += -Wmissing-declarations
++#EXTRA_CFLAGS += -Wstrict-prototypes
++#EXTRA_CFLAGS += -pedantic
++#EXTRA_CFLAGS += -O3
++#EXTRA_CFLAGS += -W
++#EXTRA_CFLAGS += -Wwrite-strings
++#EXTRA_CFLAGS += -Wbad-function-cast
++
++ifneq ($(strip $(KLIPSMODULE)),)
++# for when we aren't building in the kernel tree
++EXTRA_CFLAGS += -DARCH=${ARCH}
++EXTRA_CFLAGS += -DMODVERSIONS
++EXTRA_CFLAGS += -include ${TOPDIR}/include/linux/modversions.h
++EXTRA_CFLAGS += ${MODULE_CFLAGS}
++endif
++
++EXTRA_CFLAGS += ${KERNEL_CFLAGS}
++
++#EXTRA_CFLAGS += -DRJ_DEBUG -DRJ_DEBUG2
++
++
++# GCC 3.2 (and we presume any other 3.x) wants -falign-functions
++# in place of the traditional -malign-functions. Getting this
++# wrong leads to a warning, which is fatal due to our use of -Werror.
++ifeq ($(patsubst 3.%,3,$(shell $(CC) -dumpversion)),3)
++override CFLAGS:=$(subst -malign-functions=,-falign-functions=,$(CFLAGS))
++endif
++
++
++obj-$(CONFIG_IPSEC_AUTH_HMAC_MD5) += ipsec_md5c.o
++obj-$(CONFIG_IPSEC_AUTH_HMAC_SHA1) += ipsec_sha1.o
++
++###
++### Pre Rules.make
++###
++# undo O_TARGET, obj-y if no static
++ifneq ($(CONFIG_IPSEC),y)
++O_TARGET :=
++ipsec_obj-y := $(obj-y)
++obj-y :=
++subdir-y :=
++endif
++
++# Define obj-m if modular ipsec
++ifeq ($(CONFIG_IPSEC),m)
++obj-m += ipsec.o
++endif
++
++
++# These rules translate from new to old makefile rules
++# Translate to Rules.make lists.
++multi-used := $(filter $(list-multi), $(obj-y) $(obj-m))
++multi-objs := $(foreach m, $(multi-used), $($(basename $(m))-objs))
++active-objs := $(sort $(multi-objs) $(obj-y) $(obj-m))
++O_OBJS := $(obj-y)
++M_OBJS := $(obj-m)
++MIX_OBJS := $(filter $(export-objs), $(active-objs))
++OX_OBJS := $(export-objs)
++SUB_DIRS := $(subdir-y)
++ALL_SUB_DIRS := $(subdir-y) $(subdir-m)
++MOD_SUB_DIRS := $(subdir-m)
++
++# dunno why, but some 2.2 setups may need explicit -DEXPORT_SYMTAB
++# uncomment next line if ipsec_alg.c compilation fails with
++# "parse error before `EXPORT_SYMTAB_not_defined'" --Juanjo
++
++include $(TOPDIR)/Rules.make
++
++###
++### Post Rules.make
++###
++# for modular ipsec, no O_TARGET defined => define ipsec.o creation rules
++ifeq ($(CONFIG_IPSEC),m)
++ipsec.o : $(ipsec_obj-y)
++ rm -f $@
++ $(LD) $(LD_EXTRAFLAGS) -r $(ipsec_obj-y) -o $@
++endif
++
++$(ipsec_obj-y) $(obj-y) $(obj-m): $(TOPDIR)/include/linux/config.h $(TOPDIR)/include/linux/autoconf.h
++
++#$(obj-y) $(obj-m): $(TOPDIR)/include/linux/config.h $(TOPDIR)/include/linux/autoconf.h
++
++USE_STANDARD_AS_RULE=true
++
++clean:
++ $(MAKE) -C alg clean
++ -rm -f *.o
++ -rm -f .*.o.flags
++ -rm -f version.c
++
++tags TAGS: *.c *.h libfreeswan/*.c libfreeswan/*.h
++ etags *.c ../../include/*.h ../../include/freeswan/*.h
++ ctags *.c ../../include/*.h ../../include/freeswan/*.h
++
++tar:
++ tar -cvf /dev/f1 .
++
++#
++# $Log$
++# Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++# Turn off EOLN_NATIVE flag
++#
++# (Logical change 1.5010)
++#
++# Revision 1.72 2004/06/22 14:44:07 ken
++# Merge nice version of Nate's CryptoAPI patch
++#
++# Revision 1.71 2004/04/18 03:04:21 mcr
++# removed duplicate version.o reference.
++#
++# Revision 1.70 2004/04/14 05:09:39 ken
++# We need to link version.o
++#
++# Revision 1.69 2004/04/12 04:02:39 ken
++# version.o no longer exists
++#
++# Revision 1.68 2004/04/11 17:08:41 mcr
++# moved PASSTHROUGH definitions to openswan.h
++# requirement for internal.h removed.
++# version.c is now generated by patch at patch-time.
++#
++# Revision 1.67 2004/04/06 02:49:25 mcr
++# pullup of algo code from alg-branch.
++#
++# Revision 1.66 2004/04/03 19:44:41 ken
++# FREESWANSRCDIR -> OPENSWANSRCDIR (patch by folken)
++#
++# Revision 1.65 2004/02/09 16:22:07 paul
++# Added -f to rm version.c in clean target to prevent bogus error
++#
++# Revision 1.64 2003/12/22 19:40:57 mcr
++# NAT-T patches 0.6c.
++#
++# Revision 1.63 2003/12/13 19:10:21 mcr
++# refactored rcv and xmit code - same as FS 2.05.
++#
++# Revision 1.62.4.2 2004/04/05 04:30:46 mcr
++# patches for alg-branch to compile/work with 2.x openswan
++#
++# Revision 1.62.4.1 2003/12/22 15:25:52 jjo
++# Merged algo-0.8.1-rc11-test1 into alg-branch
++#
++# Revision 1.62 2003/10/31 02:27:55 mcr
++# pulled up port-selector patches and sa_id elimination.
++#
++# Revision 1.61.4.1 2003/10/29 01:30:41 mcr
++# elimited "struct sa_id".
++#
++# Revision 1.61 2003/06/22 21:07:46 mcr
++# adjusted TAGS target in makefile to be useful in 2.00 source layout.
++#
++# Revision 1.60 2003/05/03 23:45:23 mcr
++# rm .o.flags and generated version.c file.
++#
++# Revision 1.59 2003/02/12 19:32:47 rgb
++# Added ipsec_xmit to the list of object files.
++#
++# Revision 1.58 2003/01/03 00:36:44 rgb
++#
++# Added emacs compile-command.
++#
++# Revision 1.57 2002/11/08 23:49:53 mcr
++# use KERNEL_CFLAGS and MODULE_CFLAGS to get proper list
++# of include directories.
++# This also eliminates some of the guesswork in the kernel
++# configuration file.
++#
++# Revision 1.56 2002/11/08 23:23:18 mcr
++# attempt to guess kernel compilation flags (i.e. list of -I)
++# by using some magic targets in the kernel makefile.
++#
++# Revision 1.55 2002/11/08 10:13:33 mcr
++# added additional include directories for module builds for 2.4.19.
++#
++# Revision 1.54 2002/10/20 06:10:30 build
++# CONFIG_NOHIGHMEM for -Wpointer-arith RPM building issues.
++#
++# (elided rest of log)
++#
++# Local Variables:
++# compile-command: "(cd ../../.. && source umlsetup.sh && make -C ${POOLSPACE} module/ipsec.o)"
++# End Variables:
++#
++
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/net/ipsec/alg/Config.alg_aes.in Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,3 @@
++if [ "$CONFIG_IPSEC_ALG" = "y" ]; then
++ tristate ' AES encryption algorithm' CONFIG_IPSEC_ALG_AES
++fi
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/net/ipsec/alg/Config.alg_cryptoapi.in Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,6 @@
++if [ "$CONFIG_IPSEC_ALG" = "y" ]; then
++ dep_tristate ' CRYPTOAPI ciphers support (needs cryptoapi patch)' CONFIG_IPSEC_ALG_CRYPTOAPI $CONFIG_CRYPTO
++ if [ "$CONFIG_IPSEC_ALG_CRYPTOAPI" != "n" ]; then
++ bool ' CRYPTOAPI proprietary ciphers ' CONFIG_IPSEC_ALG_NON_LIBRE
++ fi
++fi
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/net/ipsec/alg/Config.in Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,3 @@
++#Placeholder
++source net/ipsec/alg/Config.alg_aes.in
++source net/ipsec/alg/Config.alg_cryptoapi.in
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/net/ipsec/alg/Makefile Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,112 @@
++# Makefile,v 1.1.2.1 2003/11/21 18:12:23 jjo Exp
++ifeq ($(strip $(KLIPSMODULE)),)
++FREESWANSRCDIR=.
++else
++FREESWANSRCDIR=../../../..
++endif
++ifeq ($(strip $(KLIPS_TOP)),)
++KLIPS_TOP=../../..
++override EXTRA_CFLAGS += -I$(KLIPS_TOP)/include
++endif
++
++ifeq ($(CONFIG_IPSEC_DEBUG),y)
++override EXTRA_CFLAGS += -g
++endif
++
++# LIBCRYPTO normally comes as an argument from "parent" Makefile
++# (this applies both to FS' "make module" and eg. Linux' "make modules"
++# But make dep doest follow same evaluations, so we need this default:
++LIBCRYPTO=$(TOPDIR)/lib/libcrypto
++
++override EXTRA_CFLAGS += -I$(LIBCRYPTO)/include
++override EXTRA_CFLAGS += -Wall -Wpointer-arith -Wstrict-prototypes
++
++MOD_LIST_NAME := NET_MISC_MODULES
++
++#O_TARGET := static_init.o
++
++subdir- :=
++subdir-n :=
++subdir-y :=
++subdir-m :=
++
++obj-y := static_init.o
++
++ARCH_ASM-y :=
++ARCH_ASM-$(CONFIG_M586) := i586
++ARCH_ASM-$(CONFIG_M586TSC) := i586
++ARCH_ASM-$(CONFIG_M586MMX) := i586
++ARCH_ASM-$(CONFIG_MK6) := i586
++ARCH_ASM-$(CONFIG_M686) := i686
++ARCH_ASM-$(CONFIG_MPENTIUMIII) := i686
++ARCH_ASM-$(CONFIG_MPENTIUM4) := i686
++ARCH_ASM-$(CONFIG_MK7) := i686
++ARCH_ASM-$(CONFIG_MCRUSOE) := i586
++ARCH_ASM-$(CONFIG_MWINCHIPC6) := i586
++ARCH_ASM-$(CONFIG_MWINCHIP2) := i586
++ARCH_ASM-$(CONFIG_MWINCHIP3D) := i586
++ARCH_ASM-$(CONFIG_USERMODE) := i586
++
++ARCH_ASM :=$(ARCH_ASM-y)
++ifdef NO_ASM
++ARCH_ASM :=
++endif
++
++# The algorithm makefiles may put dependences, short-circuit them
++null:
++
++makefiles=$(filter-out %.preipsec, $(wildcard Makefile.alg_*))
++ifneq ($(makefiles),)
++#include Makefile.alg_aes
++#include Makefile.alg_aes-opt
++include $(makefiles)
++endif
++
++# These rules translate from new to old makefile rules
++# Translate to Rules.make lists.
++multi-used := $(filter $(list-multi), $(obj-y) $(obj-m))
++multi-objs := $(foreach m, $(multi-used), $($(basename $(m))-objs))
++active-objs := $(sort $(multi-objs) $(obj-y) $(obj-m))
++O_OBJS := $(obj-y)
++M_OBJS := $(obj-m)
++MIX_OBJS := $(filter $(export-objs), $(active-objs))
++#OX_OBJS := $(export-objs)
++SUB_DIRS := $(subdir-y)
++ALL_SUB_DIRS := $(subdir-y) $(subdir-m)
++MOD_SUB_DIRS := $(subdir-m)
++
++
++static_init_mod.o: $(obj-y)
++ rm -f $@
++ $(LD) $(LD_EXTRAFLAGS) $(obj-y) -r -o $@
++
++perlasm: $(LIBCRYPTO)/perlasm
++ ln -sf $? $@
++
++$(obj-y) $(obj-m): $(TOPDIR)/include/linux/config.h $(TOPDIR)/include/linux/autoconf.h $(KLIPS_TOP)/include/freeswan/ipsec_alg.h
++$(alg_obj-y) $(alg_obj-m): perlasm $(TOPDIR)/include/linux/config.h $(TOPDIR)/include/linux/autoconf.h $(KLIPS_TOP)/include/freeswan/ipsec_alg.h
++
++
++all_alg_modules: perlasm $(ALG_MODULES)
++ @echo "ALG_MODULES=$(ALG_MODULES)"
++
++
++#
++# Construct alg. init. function: call ipsec_ALGO_init() for every static algo
++# Needed when there are static algos (with static or modular ipsec.o)
++#
++static_init.c: $(TOPDIR)/include/linux/autoconf.h Makefile $(makefiles) scripts/mk-static_init.c.sh
++ @echo "Re-creating $@"
++ $(SHELL) scripts/mk-static_init.c.sh $(static_init-func-y) > $@
++
++clean:
++ @for i in $(ALG_SUBDIRS);do test -d $$i && make -C $$i clean;done;exit 0
++ @find . -type l -exec rm -f {} \;
++ -rm -f perlasm
++ -rm -rf $(ALG_SUBDIRS)
++ -rm -f *.o static_init.c
++
++ifdef TOPDIR
++include $(TOPDIR)/Rules.make
++endif
++
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/net/ipsec/alg/Makefile.alg_aes Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,23 @@
++MOD_AES := ipsec_aes.o
++
++ALG_MODULES += $(MOD_AES)
++ALG_SUBDIRS += libaes
++
++obj-$(CONFIG_IPSEC_ALG_AES) += $(MOD_AES)
++static_init-func-$(CONFIG_IPSEC_ALG_AES)+= ipsec_aes_init
++alg_obj-$(CONFIG_IPSEC_ALG_AES) += ipsec_alg_aes.o
++
++AES_OBJS := ipsec_alg_aes.o libaes/libaes.a
++
++$(MOD_AES): libaes $(AES_OBJS)
++ $(LD) $(EXTRA_LDFLAGS) -r $(AES_OBJS) -o $@
++
++libaes: $(LIBCRYPTO)/libaes
++ test -d $@ || mkdir $@ ;exit 0
++ test -d $@/asm || mkdir $@/asm;exit 0
++ cd $@ && ln -sf $?/Makefile $?/*.[chS] .
++ cd $@/asm && ln -sf $?/asm/*.S .
++
++libaes/libaes.a: libaes
++ ( cd libaes && \
++ $(MAKE) CC='$(CC)' 'ARCH_ASM=$(ARCH_ASM)' CFLAGS='$(CFLAGS) $(EXTRA_CFLAGS)' libaes.a ;)
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/net/ipsec/alg/Makefile.alg_cryptoapi Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,14 @@
++MOD_CRYPTOAPI := ipsec_cryptoapi.o
++
++ifneq ($(wildcard $(TOPDIR)/include/linux/crypto.h),)
++ALG_MODULES += $(MOD_CRYPTOAPI)
++obj-$(CONFIG_IPSEC_ALG_CRYPTOAPI) += $(MOD_CRYPTOAPI)
++static_init-func-$(CONFIG_IPSEC_ALG_CRYPTOAPI)+= ipsec_cryptoapi_init
++alg_obj-$(CONFIG_IPSEC_ALG_CRYPTOAPI) += ipsec_alg_cryptoapi.o
++else
++$(warning "Linux CryptoAPI (2.4.22+ or 2.6.x) not found, not building ipsec_cryptoapi.o")
++endif
++
++CRYPTOAPI_OBJS := ipsec_alg_cryptoapi.o
++$(MOD_CRYPTOAPI): $(CRYPTOAPI_OBJS)
++ $(LD) -r $(CRYPTOAPI_OBJS) -o $@
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/net/ipsec/alg/ipsec_alg_aes.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,253 @@
++/*
++ * ipsec_alg AES cipher stubs
++ *
++ * Author: JuanJo Ciarlante <jjo-ipsec@mendoza.gov.ar>
++ *
++ * ipsec_alg_aes.c,v 1.1.2.1 2003/11/21 18:12:23 jjo Exp
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ * Fixes by:
++ * PK: Pawel Krawczyk <kravietz@aba.krakow.pl>
++ * Fixes list:
++ * PK: make XCBC comply with latest draft (keylength)
++ *
++ */
++#include <linux/config.h>
++#include <linux/version.h>
++
++/*
++ * special case: ipsec core modular with this static algo inside:
++ * must avoid MODULE magic for this file
++ */
++#if CONFIG_IPSEC_MODULE && CONFIG_IPSEC_ALG_AES
++#undef MODULE
++#endif
++
++#include <linux/module.h>
++#include <linux/init.h>
++
++#include <linux/kernel.h> /* printk() */
++#include <linux/errno.h> /* error codes */
++#include <linux/types.h> /* size_t */
++#include <linux/string.h>
++
++/* Check if __exit is defined, if not null it */
++#ifndef __exit
++#define __exit
++#endif
++
++/* Low freeswan header coupling */
++#include "freeswan/ipsec_alg.h"
++#include "libaes/aes_cbc.h"
++
++#define CONFIG_IPSEC_ALG_AES_MAC 1
++
++#define AES_CONTEXT_T aes_context
++MODULE_AUTHOR("JuanJo Ciarlante <jjo-ipsec@mendoza.gov.ar>");
++static int debug=0;
++MODULE_PARM(debug, "i");
++static int test=0;
++MODULE_PARM(test, "i");
++static int excl=0;
++MODULE_PARM(excl, "i");
++static int keyminbits=0;
++MODULE_PARM(keyminbits, "i");
++static int keymaxbits=0;
++MODULE_PARM(keymaxbits, "i");
++
++#if CONFIG_IPSEC_ALG_AES_MAC
++#include "libaes/aes_xcbc_mac.h"
++
++/*
++ * Not IANA number yet (draft-ietf-ipsec-ciph-aes-xcbc-mac-00.txt).
++ * We use 9 for non-modular algorithm and none for modular, thus
++ * forcing user to specify one on module load. -kravietz
++ */
++#ifdef MODULE
++static int auth_id=0;
++#else
++static int auth_id=9;
++#endif
++MODULE_PARM(auth_id, "i");
++#endif
++
++#define ESP_AES 12 /* truely _constant_ :) */
++
++/* 128, 192 or 256 */
++#define ESP_AES_KEY_SZ_MIN 16 /* 128 bit secret key */
++#define ESP_AES_KEY_SZ_MAX 32 /* 256 bit secret key */
++#define ESP_AES_CBC_BLK_LEN 16 /* AES-CBC block size */
++
++/* Values according to draft-ietf-ipsec-ciph-aes-xcbc-mac-02.txt
++ * -kravietz
++ */
++#define ESP_AES_MAC_KEY_SZ 16 /* 128 bit MAC key */
++#define ESP_AES_MAC_BLK_LEN 16 /* 128 bit block */
++
++static int _aes_set_key(struct ipsec_alg_enc *alg, __u8 * key_e, const __u8 * key, size_t keysize) {
++ int ret;
++ AES_CONTEXT_T *ctx=(AES_CONTEXT_T*)key_e;
++ ret=AES_set_key(ctx, key, keysize)!=0? 0: -EINVAL;
++ if (debug > 0)
++ printk(KERN_DEBUG "klips_debug:_aes_set_key:"
++ "ret=%d key_e=%p key=%p keysize=%d\n",
++ ret, key_e, key, keysize);
++ return ret;
++}
++static int _aes_cbc_encrypt(struct ipsec_alg_enc *alg, __u8 * key_e, __u8 * in, int ilen, const __u8 * iv, int encrypt) {
++ AES_CONTEXT_T *ctx=(AES_CONTEXT_T*)key_e;
++ if (debug > 0)
++ printk(KERN_DEBUG "klips_debug:_aes_cbc_encrypt:"
++ "key_e=%p in=%p ilen=%d iv=%p encrypt=%d\n",
++ key_e, in, ilen, iv, encrypt);
++ return AES_cbc_encrypt(ctx, in, in, ilen, iv, encrypt);
++}
++#if CONFIG_IPSEC_ALG_AES_MAC
++static int _aes_mac_set_key(struct ipsec_alg_auth *alg, __u8 * key_a, const __u8 * key, int keylen) {
++ aes_context_mac *ctxm=(aes_context_mac *)key_a;
++ return AES_xcbc_mac_set_key(ctxm, key, keylen)? 0 : -EINVAL;
++}
++static int _aes_mac_hash(struct ipsec_alg_auth *alg, __u8 * key_a, const __u8 * dat, int len, __u8 * hash, int hashlen) {
++ int ret;
++ char hash_buf[16];
++ aes_context_mac *ctxm=(aes_context_mac *)key_a;
++ ret=AES_xcbc_mac_hash(ctxm, dat, len, hash_buf);
++ memcpy(hash, hash_buf, hashlen);
++ return ret;
++}
++static struct ipsec_alg_auth ipsec_alg_AES_MAC = {
++ ixt_version: IPSEC_ALG_VERSION,
++ ixt_module: THIS_MODULE,
++ ixt_refcnt: ATOMIC_INIT(0),
++ ixt_alg_type: IPSEC_ALG_TYPE_AUTH,
++ ixt_alg_id: 0,
++ ixt_name: "aes_mac",
++ ixt_blocksize: ESP_AES_MAC_BLK_LEN,
++ ixt_keyminbits: ESP_AES_MAC_KEY_SZ*8,
++ ixt_keymaxbits: ESP_AES_MAC_KEY_SZ*8,
++ ixt_a_keylen: ESP_AES_MAC_KEY_SZ,
++ ixt_a_ctx_size: sizeof(aes_context_mac),
++ ixt_a_hmac_set_key: _aes_mac_set_key,
++ ixt_a_hmac_hash:_aes_mac_hash,
++};
++#endif /* CONFIG_IPSEC_ALG_AES_MAC */
++static struct ipsec_alg_enc ipsec_alg_AES = {
++ ixt_version: IPSEC_ALG_VERSION,
++ ixt_module: THIS_MODULE,
++ ixt_refcnt: ATOMIC_INIT(0),
++ ixt_alg_type: IPSEC_ALG_TYPE_ENCRYPT,
++ ixt_alg_id: ESP_AES,
++ ixt_name: "aes",
++ ixt_blocksize: ESP_AES_CBC_BLK_LEN,
++ ixt_keyminbits: ESP_AES_KEY_SZ_MIN*8,
++ ixt_keymaxbits: ESP_AES_KEY_SZ_MAX*8,
++ ixt_e_keylen: ESP_AES_KEY_SZ_MAX,
++ ixt_e_ctx_size: sizeof(AES_CONTEXT_T),
++ ixt_e_set_key: _aes_set_key,
++ ixt_e_cbc_encrypt:_aes_cbc_encrypt,
++};
++
++IPSEC_ALG_MODULE_INIT( ipsec_aes_init )
++{
++ int ret, test_ret;
++ if (keyminbits)
++ ipsec_alg_AES.ixt_keyminbits=keyminbits;
++ if (keymaxbits) {
++ ipsec_alg_AES.ixt_keymaxbits=keymaxbits;
++ if (keymaxbits*8>ipsec_alg_AES.ixt_keymaxbits)
++ ipsec_alg_AES.ixt_e_keylen=keymaxbits*8;
++ }
++ if (excl) ipsec_alg_AES.ixt_state |= IPSEC_ALG_ST_EXCL;
++ ret=register_ipsec_alg_enc(&ipsec_alg_AES);
++ printk("ipsec_aes_init(alg_type=%d alg_id=%d name=%s): ret=%d\n",
++ ipsec_alg_AES.ixt_alg_type,
++ ipsec_alg_AES.ixt_alg_id,
++ ipsec_alg_AES.ixt_name,
++ ret);
++ if (ret==0 && test) {
++ test_ret=ipsec_alg_test(
++ ipsec_alg_AES.ixt_alg_type,
++ ipsec_alg_AES.ixt_alg_id,
++ test);
++ printk("ipsec_aes_init(alg_type=%d alg_id=%d): test_ret=%d\n",
++ ipsec_alg_AES.ixt_alg_type,
++ ipsec_alg_AES.ixt_alg_id,
++ test_ret);
++ }
++#if CONFIG_IPSEC_ALG_AES_MAC
++ if (auth_id!=0){
++ int ret;
++ ipsec_alg_AES_MAC.ixt_alg_id=auth_id;
++ ret=register_ipsec_alg_auth(&ipsec_alg_AES_MAC);
++ printk("ipsec_aes_init(alg_type=%d alg_id=%d name=%s): ret=%d\n",
++ ipsec_alg_AES_MAC.ixt_alg_type,
++ ipsec_alg_AES_MAC.ixt_alg_id,
++ ipsec_alg_AES_MAC.ixt_name,
++ ret);
++ if (ret==0 && test) {
++ test_ret=ipsec_alg_test(
++ ipsec_alg_AES_MAC.ixt_alg_type,
++ ipsec_alg_AES_MAC.ixt_alg_id,
++ test);
++ printk("ipsec_aes_init(alg_type=%d alg_id=%d): test_ret=%d\n",
++ ipsec_alg_AES_MAC.ixt_alg_type,
++ ipsec_alg_AES_MAC.ixt_alg_id,
++ test_ret);
++ }
++ } else {
++ printk(KERN_DEBUG "klips_debug: experimental ipsec_alg_AES_MAC not registered [Ok] (auth_id=%d)\n", auth_id);
++ }
++#endif /* CONFIG_IPSEC_ALG_AES_MAC */
++ return ret;
++}
++IPSEC_ALG_MODULE_EXIT( ipsec_aes_fini )
++{
++#if CONFIG_IPSEC_ALG_AES_MAC
++ if (auth_id) unregister_ipsec_alg_auth(&ipsec_alg_AES_MAC);
++#endif /* CONFIG_IPSEC_ALG_AES_MAC */
++ unregister_ipsec_alg_enc(&ipsec_alg_AES);
++ return;
++}
++#ifdef MODULE_LICENSE
++MODULE_LICENSE("GPL");
++#endif
++
++#if 0+NOT_YET
++#ifndef MODULE
++/*
++ * This is intended for static module setups, currently
++ * doesn't work for modular ipsec.o with static algos inside
++ */
++static int setup_keybits(const char *str)
++{
++ unsigned aux;
++ char *end;
++
++ aux = simple_strtoul(str,&end,0);
++ if (aux != 128 && aux != 192 && aux != 256)
++ return 0;
++ keyminbits = aux;
++
++ if (*end == 0 || *end != ',')
++ return 1;
++ str=end+1;
++ aux = simple_strtoul(str, NULL, 0);
++ if (aux != 128 && aux != 192 && aux != 256)
++ return 0;
++ if (aux >= keyminbits)
++ keymaxbits = aux;
++ return 1;
++}
++__setup("ipsec_aes_keybits=", setup_keybits);
++#endif
++#endif
++EXPORT_NO_SYMBOLS;
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/net/ipsec/alg/ipsec_alg_cryptoapi.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,421 @@
++/*
++ * ipsec_alg to linux cryptoapi GLUE
++ *
++ * Authors: CODE.ar TEAM
++ * Harpo MAxx <harpo@linuxmendoza.org.ar>
++ * JuanJo Ciarlante <jjo-ipsec@mendoza.gov.ar>
++ * Luciano Ruete <docemeses@softhome.net>
++ *
++ * ipsec_alg_cryptoapi.c,v 1.1.2.1 2003/11/21 18:12:23 jjo Exp
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ * Example usage:
++ * modinfo -p ipsec_cryptoapi (quite useful info, including supported algos)
++ * modprobe ipsec_cryptoapi
++ * modprobe ipsec_cryptoapi test=1
++ * modprobe ipsec_cryptoapi excl=1 (exclusive cipher/algo)
++ * modprobe ipsec_cryptoapi noauto=1 aes=1 twofish=1 (only these ciphers)
++ * modprobe ipsec_cryptoapi aes=128,128 (force these keylens)
++ * modprobe ipsec_cryptoapi des_ede3=0 (everything but 3DES)
++ */
++#include <linux/config.h>
++#include <linux/version.h>
++
++/*
++ * special case: ipsec core modular with this static algo inside:
++ * must avoid MODULE magic for this file
++ */
++#if CONFIG_IPSEC_MODULE && CONFIG_IPSEC_ALG_CRYPTOAPI
++#undef MODULE
++#endif
++
++#include <linux/module.h>
++#include <linux/init.h>
++
++#include <linux/kernel.h> /* printk() */
++#include <linux/errno.h> /* error codes */
++#include <linux/types.h> /* size_t */
++#include <linux/string.h>
++
++/* Check if __exit is defined, if not null it */
++#ifndef __exit
++#define __exit
++#endif
++
++/* warn the innocent */
++#if !defined (CONFIG_CRYPTO) && !defined (CONFIG_CRYPTO_MODULE)
++#warning "No linux CryptoAPI found, install 2.4.22+ or 2.6.x"
++#define NO_CRYPTOAPI_SUPPORT
++#endif
++/* Low freeswan header coupling */
++#include "freeswan/ipsec_alg.h"
++
++#include <linux/crypto.h>
++#ifdef CRYPTO_API_VERSION_CODE
++#warning "Old CryptoAPI is not supported. Only linux-2.4.22+ or linux-2.6.x are supported"
++#define NO_CRYPTOAPI_SUPPORT
++#endif
++
++#ifdef NO_CRYPTOAPI_SUPPORT
++#warning "Building an unusable module :P"
++/* Catch old CryptoAPI by not allowing module to load */
++IPSEC_ALG_MODULE_INIT( ipsec_cryptoapi_init )
++{
++ printk(KERN_WARNING "ipsec_cryptoapi.o was not built on stock Linux CryptoAPI (2.4.22+ or 2.6.x), not loading.\n");
++ return -EINVAL;
++}
++#else
++#include <asm/scatterlist.h>
++#include <asm/pgtable.h>
++#include <linux/mm.h>
++
++#define CIPHERNAME_AES "aes"
++#define CIPHERNAME_3DES "des3_ede"
++#define CIPHERNAME_BLOWFISH "blowfish"
++#define CIPHERNAME_CAST "cast5"
++#define CIPHERNAME_SERPENT "serpent"
++#define CIPHERNAME_TWOFISH "twofish"
++
++#define ESP_3DES 3
++#define ESP_AES 12
++#define ESP_BLOWFISH 7 /* truely _constant_ :) */
++#define ESP_CAST 6 /* quite constant :) */
++#define ESP_SERPENT 252 /* from ipsec drafts */
++#define ESP_TWOFISH 253 /* from ipsec drafts */
++
++#define AH_MD5 2
++#define AH_SHA 3
++#define DIGESTNAME_MD5 "md5"
++#define DIGESTNAME_SHA1 "sha1"
++
++MODULE_AUTHOR("Juanjo Ciarlante, Harpo MAxx, Luciano Ruete");
++static int debug=0;
++MODULE_PARM(debug, "i");
++static int test=0;
++MODULE_PARM(test, "i");
++static int excl=0;
++MODULE_PARM(excl, "i");
++
++static int noauto = 0;
++MODULE_PARM(noauto,"i");
++MODULE_PARM_DESC(noauto, "Dont try all known algos, just setup enabled ones");
++
++static int des_ede3[] = {-1, -1};
++static int aes[] = {-1, -1};
++static int blowfish[] = {-1, -1};
++static int cast[] = {-1, -1};
++static int serpent[] = {-1, -1};
++static int twofish[] = {-1, -1};
++
++MODULE_PARM(des_ede3,"1-2i");
++MODULE_PARM(aes,"1-2i");
++MODULE_PARM(blowfish,"1-2i");
++MODULE_PARM(cast,"1-2i");
++MODULE_PARM(serpent,"1-2i");
++MODULE_PARM(twofish,"1-2i");
++MODULE_PARM_DESC(des_ede3, "0: disable | 1: force_enable | min,max: dontuse");
++MODULE_PARM_DESC(aes, "0: disable | 1: force_enable | min,max: keybitlens");
++MODULE_PARM_DESC(blowfish, "0: disable | 1: force_enable | min,max: keybitlens");
++MODULE_PARM_DESC(cast, "0: disable | 1: force_enable | min,max: keybitlens");
++MODULE_PARM_DESC(serpent, "0: disable | 1: force_enable | min,max: keybitlens");
++MODULE_PARM_DESC(twofish, "0: disable | 1: force_enable | min,max: keybitlens");
++
++struct ipsec_alg_capi_cipher {
++ const char *ciphername; /* cryptoapi's ciphername */
++ unsigned blocksize;
++ unsigned short minbits;
++ unsigned short maxbits;
++ int *parm; /* lkm param for this cipher */
++ struct ipsec_alg_enc alg; /* note it's not a pointer */
++};
++static struct ipsec_alg_capi_cipher alg_capi_carray[] = {
++ { CIPHERNAME_AES , 16, 128, 256, aes , { ixt_alg_id: ESP_AES, }},
++ { CIPHERNAME_TWOFISH , 16, 128, 256, twofish, { ixt_alg_id: ESP_TWOFISH, }},
++ { CIPHERNAME_SERPENT , 16, 128, 256, serpent, { ixt_alg_id: ESP_SERPENT, }},
++ { CIPHERNAME_CAST , 8, 128, 128, cast , { ixt_alg_id: ESP_CAST, }},
++ { CIPHERNAME_BLOWFISH , 8, 96, 448, blowfish,{ ixt_alg_id: ESP_BLOWFISH, }},
++ { CIPHERNAME_3DES , 8, 192, 192, des_ede3,{ ixt_alg_id: ESP_3DES, }},
++ { NULL, 0, 0, 0, NULL, {} }
++};
++#ifdef NOT_YET
++struct ipsec_alg_capi_digest {
++ const char *digestname; /* cryptoapi's digestname */
++ struct digest_implementation *di;
++ struct ipsec_alg_auth alg; /* note it's not a pointer */
++};
++static struct ipsec_alg_capi_cipher alg_capi_darray[] = {
++ { DIGESTNAME_MD5, NULL, { ixt_alg_id: AH_MD5, }},
++ { DIGESTNAME_SHA1, NULL, { ixt_alg_id: AH_SHA, }},
++ { NULL, NULL, {} }
++};
++#endif
++/*
++ * "generic" linux cryptoapi setup_cipher() function
++ */
++int setup_cipher(const char *ciphername)
++{
++ return crypto_alg_available(ciphername, 0);
++}
++
++/*
++ * setups ipsec_alg_capi_cipher "hyper" struct components, calling
++ * register_ipsec_alg for cointaned ipsec_alg object
++ */
++static void _capi_destroy_key (struct ipsec_alg_enc *alg, __u8 *key_e);
++static __u8 * _capi_new_key (struct ipsec_alg_enc *alg, const __u8 *key, size_t keylen);
++static int _capi_cbc_encrypt(struct ipsec_alg_enc *alg, __u8 * key_e, __u8 * in, int ilen, const __u8 * iv, int encrypt);
++
++static int
++setup_ipsec_alg_capi_cipher(struct ipsec_alg_capi_cipher *cptr)
++{
++ int ret;
++ cptr->alg.ixt_version = IPSEC_ALG_VERSION;
++ cptr->alg.ixt_module = THIS_MODULE;
++ atomic_set (& cptr->alg.ixt_refcnt, 0);
++ strncpy (cptr->alg.ixt_name , cptr->ciphername, sizeof (cptr->alg.ixt_name));
++
++ cptr->alg.ixt_blocksize=cptr->blocksize;
++ cptr->alg.ixt_keyminbits=cptr->minbits;
++ cptr->alg.ixt_keymaxbits=cptr->maxbits;
++ cptr->alg.ixt_state = 0;
++ if (excl) cptr->alg.ixt_state |= IPSEC_ALG_ST_EXCL;
++ cptr->alg.ixt_e_keylen=cptr->alg.ixt_keymaxbits/8;
++ cptr->alg.ixt_e_ctx_size = 0;
++ cptr->alg.ixt_alg_type = IPSEC_ALG_TYPE_ENCRYPT;
++ cptr->alg.ixt_e_new_key = _capi_new_key;
++ cptr->alg.ixt_e_destroy_key = _capi_destroy_key;
++ cptr->alg.ixt_e_cbc_encrypt = _capi_cbc_encrypt;
++ cptr->alg.ixt_data = cptr;
++
++ ret=register_ipsec_alg_enc(&cptr->alg);
++ printk("setup_ipsec_alg_capi_cipher(): "
++ "alg_type=%d alg_id=%d name=%s "
++ "keyminbits=%d keymaxbits=%d, ret=%d\n",
++ cptr->alg.ixt_alg_type,
++ cptr->alg.ixt_alg_id,
++ cptr->alg.ixt_name,
++ cptr->alg.ixt_keyminbits,
++ cptr->alg.ixt_keymaxbits,
++ ret);
++ return ret;
++}
++/*
++ * called in ipsec_sa_wipe() time, will destroy key contexts
++ * and do 1 unbind()
++ */
++static void
++_capi_destroy_key (struct ipsec_alg_enc *alg, __u8 *key_e)
++{
++ struct crypto_tfm *tfm=(struct crypto_tfm*)key_e;
++
++ if (debug > 0)
++ printk(KERN_DEBUG "klips_debug: _capi_destroy_key:"
++ "name=%s key_e=%p \n",
++ alg->ixt_name, key_e);
++ if (!key_e) {
++ printk(KERN_ERR "klips_debug: _capi_destroy_key:"
++ "name=%s NULL key_e!\n",
++ alg->ixt_name);
++ return;
++ }
++ crypto_free_tfm(tfm);
++}
++
++/*
++ * create new key context, need alg->ixt_data to know which
++ * (of many) cipher inside this module is the target
++ */
++static __u8 *
++_capi_new_key (struct ipsec_alg_enc *alg, const __u8 *key, size_t keylen)
++{
++ struct ipsec_alg_capi_cipher *cptr;
++ struct crypto_tfm *tfm=NULL;
++
++ cptr = alg->ixt_data;
++ if (!cptr) {
++ printk(KERN_ERR "_capi_new_key(): "
++ "NULL ixt_data (?!) for \"%s\" algo\n"
++ , alg->ixt_name);
++ goto err;
++ }
++ if (debug > 0)
++ printk(KERN_DEBUG "klips_debug:_capi_new_key:"
++ "name=%s cptr=%p key=%p keysize=%d\n",
++ alg->ixt_name, cptr, key, keylen);
++
++ /*
++ * alloc tfm
++ */
++ tfm = crypto_alloc_tfm(cptr->ciphername, CRYPTO_TFM_MODE_CBC);
++ if (!tfm) {
++ printk(KERN_ERR "_capi_new_key(): "
++ "NULL tfm for \"%s\" cryptoapi (\"%s\") algo\n"
++ , alg->ixt_name, cptr->ciphername);
++ goto err;
++ }
++ if (crypto_cipher_setkey(tfm, key, keylen) < 0) {
++ printk(KERN_ERR "_capi_new_key(): "
++ "failed new_key() for \"%s\" cryptoapi algo (keylen=%d)\n"
++ , alg->ixt_name, keylen);
++ crypto_free_tfm(tfm);
++ tfm=NULL;
++ }
++err:
++ if (debug > 0)
++ printk(KERN_DEBUG "klips_debug:_capi_new_key:"
++ "name=%s key=%p keylen=%d tfm=%p\n",
++ alg->ixt_name, key, keylen, tfm);
++ return (__u8 *) tfm;
++}
++/*
++ * core encryption function: will use cx->ci to call actual cipher's
++ * cbc function
++ */
++static int
++_capi_cbc_encrypt(struct ipsec_alg_enc *alg, __u8 * key_e, __u8 * in, int ilen, const __u8 * iv, int encrypt) {
++ int error =0;
++ struct crypto_tfm *tfm=(struct crypto_tfm *)key_e;
++ struct scatterlist sg = {
++ .page = virt_to_page(in),
++ .offset = (unsigned long)(in) % PAGE_SIZE,
++ .length=ilen,
++ };
++ if (debug > 1)
++ printk(KERN_DEBUG "klips_debug:_capi_cbc_encrypt:"
++ "key_e=%p "
++ "in=%p out=%p ilen=%d iv=%p encrypt=%d\n"
++ , key_e
++ , in, in, ilen, iv, encrypt);
++ crypto_cipher_set_iv(tfm, iv, crypto_tfm_alg_ivsize(tfm));
++ if (encrypt)
++ error = crypto_cipher_encrypt (tfm, &sg, &sg, ilen);
++ else
++ error = crypto_cipher_decrypt (tfm, &sg, &sg, ilen);
++ if (debug > 1)
++ printk(KERN_DEBUG "klips_debug:_capi_cbc_encrypt:"
++ "error=%d\n"
++ , error);
++ return (error<0)? error : ilen;
++}
++/*
++ * main initialization loop: for each cipher in list, do
++ * 1) setup cryptoapi cipher else continue
++ * 2) register ipsec_alg object
++ */
++static int
++setup_cipher_list (struct ipsec_alg_capi_cipher* clist)
++{
++ struct ipsec_alg_capi_cipher *cptr;
++ /* foreach cipher in list ... */
++ for (cptr=clist;cptr->ciphername;cptr++) {
++ /*
++ * see if cipher has been disabled (0) or
++ * if noauto set and not enabled (1)
++ */
++ if (cptr->parm[0] == 0 || (noauto && cptr->parm[0] < 0)) {
++ if (debug>0)
++ printk(KERN_INFO "setup_cipher_list(): "
++ "ciphername=%s skipped at user request: "
++ "noauto=%d parm[0]=%d parm[1]=%d\n"
++ , cptr->ciphername
++ , noauto
++ , cptr->parm[0]
++ , cptr->parm[1]);
++ continue;
++ }
++ /*
++ * use a local ci to avoid touching cptr->ci,
++ * if register ipsec_alg success then bind cipher
++ */
++ if( setup_cipher(cptr->ciphername) ) {
++ if (debug > 0)
++ printk(KERN_DEBUG "klips_debug:"
++ "setup_cipher_list():"
++ "ciphername=%s found\n"
++ , cptr->ciphername);
++ if (setup_ipsec_alg_capi_cipher(cptr) == 0) {
++
++
++ } else {
++ printk(KERN_ERR "klips_debug:"
++ "setup_cipher_list():"
++ "ciphername=%s failed ipsec_alg_register\n"
++ , cptr->ciphername);
++ }
++ } else {
++ if (debug>0)
++ printk(KERN_INFO "setup_cipher_list(): lookup for ciphername=%s: not found \n",
++ cptr->ciphername);
++ }
++ }
++ return 0;
++}
++/*
++ * deregister ipsec_alg objects and unbind ciphers
++ */
++static int
++unsetup_cipher_list (struct ipsec_alg_capi_cipher* clist)
++{
++ struct ipsec_alg_capi_cipher *cptr;
++ /* foreach cipher in list ... */
++ for (cptr=clist;cptr->ciphername;cptr++) {
++ if (cptr->alg.ixt_state & IPSEC_ALG_ST_REGISTERED) {
++ unregister_ipsec_alg_enc(&cptr->alg);
++ }
++ }
++ return 0;
++}
++/*
++ * test loop for registered algos
++ */
++static int
++test_cipher_list (struct ipsec_alg_capi_cipher* clist)
++{
++ int test_ret;
++ struct ipsec_alg_capi_cipher *cptr;
++ /* foreach cipher in list ... */
++ for (cptr=clist;cptr->ciphername;cptr++) {
++ if (cptr->alg.ixt_state & IPSEC_ALG_ST_REGISTERED) {
++ test_ret=ipsec_alg_test(
++ cptr->alg.ixt_alg_type,
++ cptr->alg.ixt_alg_id,
++ test);
++ printk("test_cipher_list(alg_type=%d alg_id=%d): test_ret=%d\n",
++ cptr->alg.ixt_alg_type,
++ cptr->alg.ixt_alg_id,
++ test_ret);
++ }
++ }
++ return 0;
++}
++
++IPSEC_ALG_MODULE_INIT( ipsec_cryptoapi_init )
++{
++ int ret, test_ret;
++ if ((ret=setup_cipher_list(alg_capi_carray)) < 0)
++ return -EPROTONOSUPPORT;
++ if (ret==0 && test) {
++ test_ret=test_cipher_list(alg_capi_carray);
++ }
++ return ret;
++}
++IPSEC_ALG_MODULE_EXIT( ipsec_cryptoapi_fini )
++{
++ unsetup_cipher_list(alg_capi_carray);
++ return;
++}
++#ifdef MODULE_LICENSE
++MODULE_LICENSE("GPL");
++#endif
++
++EXPORT_NO_SYMBOLS;
++#endif /* NO_CRYPTOAPI_SUPPORT */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/net/ipsec/alg/scripts/mk-static_init.c.sh Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,18 @@
++#!/bin/sh
++cat << EOF
++#include <linux/kernel.h>
++#include <linux/list.h>
++#include "freeswan/ipsec_alg.h"
++$(for i in $*; do
++ test -z "$i" && continue
++ echo "extern int $i(void);"
++done)
++void ipsec_alg_static_init(void){
++ int __attribute__ ((unused)) err=0;
++$(for i in $*; do
++ test -z "$i" && continue
++ echo " if ((err=$i()) < 0)"
++ echo " printk(KERN_WARNING \"$i() returned %d\", err);"
++done)
++}
++EOF
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/net/ipsec/defconfig Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,146 @@
++
++#
++# RCSID $Id$
++#
++
++#
++# FreeS/WAN IPSec implementation, KLIPS kernel config defaults
++#
++
++#
++# First, lets override stuff already set or not in the kernel config.
++#
++# We can't even think about leaving this off...
++CONFIG_INET=y
++
++#
++# This must be on for subnet protection.
++CONFIG_IP_FORWARD=y
++
++# Shut off IPSEC masquerading if it has been enabled, since it will
++# break the compile. IPPROTO_ESP and IPPROTO_AH were included in
++# net/ipv4/ip_masq.c when they should have gone into include/linux/in.h.
++CONFIG_IP_MASQUERADE_IPSEC=n
++
++#
++# Next, lets set the recommended FreeS/WAN configuration.
++#
++
++# To config as static (preferred), 'y'. To config as module, 'm'.
++CONFIG_IPSEC=y
++
++# To do tunnel mode IPSec, this must be enabled.
++CONFIG_IPSEC_IPIP=y
++
++# To enable authentication, say 'y'. (Highly recommended)
++CONFIG_IPSEC_AH=y
++
++# Authentication algorithm(s):
++CONFIG_IPSEC_AUTH_HMAC_MD5=y
++CONFIG_IPSEC_AUTH_HMAC_SHA1=y
++
++# To enable encryption, say 'y'. (Highly recommended)
++CONFIG_IPSEC_ESP=y
++
++# Encryption algorithm(s):
++CONFIG_IPSEC_ENC_3DES=y
++CONFIG_IPSEC_ENC_AES=y
++
++# modular algo extensions (and new ALGOs)
++CONFIG_IPSEC_ALG=y
++CONFIG_IPSEC_ENC_3DES=y
++CONFIG_IPSEC_ENC_AES=y
++
++CONFIG_IPSEC_ALG_TWOFISH=m
++CONFIG_IPSEC_ALG_BLOWFISH=m
++CONFIG_IPSEC_ALG_SERPENT=m
++CONFIG_IPSEC_ALG_MD5=m
++CONFIG_IPSEC_ALG_SHA1=m
++CONFIG_IPSEC_ALG_SHA2=m
++#CONFIG_IPSEC_ALG_CAST=n
++#CONFIG_IPSEC_ALG_NULL=n
++
++# Use CryptoAPI for ALG? - by default, no.
++CONFIG_IPSEC_ENC_CRYPTOAPI=n
++
++
++# IP Compression: new, probably still has minor bugs.
++CONFIG_IPSEC_IPCOMP=y
++
++# To enable userspace-switchable KLIPS debugging, say 'y'.
++CONFIG_IPSEC_DEBUG=y
++
++# NAT Traversal
++CONFIG_IPSEC_NAT_TRAVERSAL=y
++
++#
++#
++# $Log$
++# Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++# Turn off EOLN_NATIVE flag
++#
++# (Logical change 1.5010)
++#
++# Revision 1.25 2004/07/05 01:03:53 mcr
++# fix for adding cryptoapi code.
++# keep it off for now, since UMLs do not have it yet.
++#
++# Revision 1.24 2004/04/06 02:49:25 mcr
++# pullup of algo code from alg-branch.
++#
++# Revision 1.23.2.2 2004/04/05 04:30:46 mcr
++# patches for alg-branch to compile/work with 2.x openswan
++#
++# Revision 1.23.2.1 2003/12/22 15:25:52 jjo
++# . Merged algo-0.8.1-rc11-test1 into alg-branch
++#
++# Revision 1.23 2003/12/10 01:14:27 mcr
++# NAT-traversal patches to KLIPS.
++#
++# Revision 1.22 2003/02/24 19:37:27 mcr
++# changed default compilation mode to static.
++#
++# Revision 1.21 2002/04/24 07:36:27 mcr
++# Moved from ./klips/net/ipsec/defconfig,v
++#
++# Revision 1.20 2002/04/02 04:07:40 mcr
++# default build is now 'm'odule for KLIPS
++#
++# Revision 1.19 2002/03/08 18:57:17 rgb
++# Added a blank line at the beginning of the file to make it easier for
++# other projects to patch ./arch/i386/defconfig, for example
++# LIDS+grSecurity requested by Jason Pattie.
++#
++# Revision 1.18 2000/11/30 17:26:56 rgb
++# Cleaned out unused options and enabled ipcomp by default.
++#
++# Revision 1.17 2000/09/15 11:37:01 rgb
++# Merge in heavily modified Svenning Soerensen's <svenning@post5.tele.dk>
++# IPCOMP zlib deflate code.
++#
++# Revision 1.16 2000/09/08 19:12:55 rgb
++# Change references from DEBUG_IPSEC to CONFIG_IPSEC_DEBUG.
++#
++# Revision 1.15 2000/05/24 19:37:13 rgb
++# *** empty log message ***
++#
++# Revision 1.14 2000/05/11 21:14:57 henry
++# just commenting the FOOBAR=y lines out is not enough
++#
++# Revision 1.13 2000/05/10 20:17:58 rgb
++# Comment out netlink defaults, which are no longer needed.
++#
++# Revision 1.12 2000/05/10 19:13:38 rgb
++# Added configure option to shut off no eroute passthrough.
++#
++# Revision 1.11 2000/03/16 07:09:46 rgb
++# Hardcode PF_KEYv2 support.
++# Disable IPSEC_ICMP by default.
++# Remove DES config option from defaults file.
++#
++# Revision 1.10 2000/01/11 03:09:42 rgb
++# Added a default of 'y' to PF_KEYv2 keying I/F.
++#
++# Revision 1.9 1999/05/08 21:23:12 rgb
++# Added support for 2.2.x kernels.
++#
++# Revision 1.8 1999/04/06 04:54:25 rgb
++# Fix/Add RCSID Id: and Log: bits to make PHMDs happy. This includes
++# patch shell fixes.
++#
++#
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/net/ipsec/ipcomp.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,725 @@
++/*
++ * IPCOMP zlib interface code.
++ * Copyright (C) 2000 Svenning Soerensen <svenning@post5.tele.dk>
++ * Copyright (C) 2000, 2001 Richard Guy Briggs <rgb@conscoop.ottawa.on.ca>
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ */
++
++char ipcomp_c_version[] = "RCSID $Id$";
++
++/* SSS */
++
++#include <linux/config.h>
++#include <linux/version.h>
++
++#define __NO_VERSION__
++#include <linux/module.h>
++#include <linux/kernel.h> /* printk() */
++
++#include "openswan/ipsec_param.h"
++
++#ifdef MALLOC_SLAB
++# include <linux/slab.h> /* kmalloc() */
++#else /* MALLOC_SLAB */
++# include <linux/malloc.h> /* kmalloc() */
++#endif /* MALLOC_SLAB */
++#include <linux/errno.h> /* error codes */
++#include <linux/types.h>
++#include <linux/netdevice.h>
++#include <linux/ip.h>
++#include <linux/skbuff.h>
++
++#include <linux/netdevice.h> /* struct device, and other headers */
++#include <linux/etherdevice.h> /* eth_type_trans */
++#include <linux/ip.h> /* struct iphdr */
++#include <linux/skbuff.h>
++
++#include <openswan.h>
++
++#ifdef NET_21
++# include <net/dst.h>
++# include <asm/uaccess.h>
++# include <linux/in6.h>
++# define proto_priv cb
++#endif /* NET21 */
++#include <asm/checksum.h>
++#include <net/ip.h>
++
++#include "openswan/radij.h"
++#include "openswan/ipsec_encap.h"
++#include "openswan/ipsec_sa.h"
++
++#include "openswan/ipsec_xform.h"
++#include "openswan/ipsec_tunnel.h"
++#include "openswan/ipsec_rcv.h" /* sysctl_ipsec_inbound_policy_check */
++#include "openswan/ipcomp.h"
++#include "zlib/zlib.h"
++#include "zlib/zutil.h"
++
++#include <pfkeyv2.h> /* SADB_X_CALG_DEFLATE */
++
++#ifdef CONFIG_IPSEC_DEBUG
++int sysctl_ipsec_debug_ipcomp = 0;
++#endif /* CONFIG_IPSEC_DEBUG */
++
++static
++struct sk_buff *skb_copy_ipcomp(struct sk_buff *skb, int data_growth, int gfp_mask);
++
++static
++voidpf my_zcalloc(voidpf opaque, uInt items, uInt size)
++{
++ return (voidpf) kmalloc(items*size, GFP_ATOMIC);
++}
++
++static
++void my_zfree(voidpf opaque, voidpf address)
++{
++ kfree(address);
++}
++
++struct sk_buff *skb_compress(struct sk_buff *skb, struct ipsec_sa *ips, unsigned int *flags)
++{
++ struct iphdr *iph;
++ unsigned int iphlen, pyldsz, cpyldsz;
++ unsigned char *buffer;
++ z_stream zs;
++ int zresult;
++
++ KLIPS_PRINT(sysctl_ipsec_debug_ipcomp,
++ "klips_debug:skb_compress: .\n");
++
++ if(skb == NULL) {
++ KLIPS_PRINT(sysctl_ipsec_debug_ipcomp,
++ "klips_debug:skb_compress: "
++ "passed in NULL skb, returning ERROR.\n");
++ if(flags != NULL) {
++ *flags |= IPCOMP_PARMERROR;
++ }
++ return skb;
++ }
++
++ if(ips == NULL) {
++ KLIPS_PRINT(sysctl_ipsec_debug_ipcomp,
++ "klips_debug:skb_compress: "
++ "passed in NULL ipsec_sa needed for cpi, returning ERROR.\n");
++ if(flags) {
++ *flags |= IPCOMP_PARMERROR;
++ }
++ return skb;
++ }
++
++ if (flags == NULL) {
++ KLIPS_PRINT(sysctl_ipsec_debug_ipcomp,
++ "klips_debug:skb_compress: "
++ "passed in NULL flags, returning ERROR.\n");
++ ipsec_kfree_skb(skb);
++ return NULL;
++ }
++
++#ifdef NET_21
++ iph = skb->nh.iph;
++#else /* NET_21 */
++ iph = skb->ip_hdr;
++#endif /* NET_21 */
++
++ switch (iph->protocol) {
++ case IPPROTO_COMP:
++ case IPPROTO_AH:
++ case IPPROTO_ESP:
++ KLIPS_PRINT(sysctl_ipsec_debug_ipcomp,
++ "klips_debug:skb_compress: "
++ "skipping compression of packet with ip protocol %d.\n",
++ iph->protocol);
++ *flags |= IPCOMP_UNCOMPRESSABLE;
++ return skb;
++ }
++
++ /* Don't compress packets already fragmented */
++ if (iph->frag_off & __constant_htons(IP_MF | IP_OFFSET)) {
++ KLIPS_PRINT(sysctl_ipsec_debug_ipcomp,
++ "klips_debug:skb_compress: "
++ "skipping compression of fragmented packet.\n");
++ *flags |= IPCOMP_UNCOMPRESSABLE;
++ return skb;
++ }
++
++ iphlen = iph->ihl << 2;
++ pyldsz = ntohs(iph->tot_len) - iphlen;
++
++ /* Don't compress less than 90 bytes (rfc 2394) */
++ if (pyldsz < 90) {
++ KLIPS_PRINT(sysctl_ipsec_debug_ipcomp,
++ "klips_debug:skb_compress: "
++ "skipping compression of tiny packet, len=%d.\n",
++ pyldsz);
++ *flags |= IPCOMP_UNCOMPRESSABLE;
++ return skb;
++ }
++
++ /* Adaptive decision */
++ if (ips->ips_comp_adapt_skip) {
++ KLIPS_PRINT(sysctl_ipsec_debug_ipcomp,
++ "klips_debug:skb_compress: "
++ "skipping compression: ips_comp_adapt_skip=%d.\n",
++ ips->ips_comp_adapt_skip);
++ ips->ips_comp_adapt_skip--;
++ *flags |= IPCOMP_UNCOMPRESSABLE;
++ return skb;
++ }
++
++ zs.zalloc = my_zcalloc;
++ zs.zfree = my_zfree;
++ zs.opaque = 0;
++
++ /* We want to use deflateInit2 because we don't want the adler
++ header. */
++ zresult = deflateInit2(&zs, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -11,
++ DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
++ if (zresult != Z_OK) {
++ KLIPS_PRINT(sysctl_ipsec_debug_ipcomp,
++ "klips_error:skb_compress: "
++ "deflateInit2() returned error %d (%s), "
++ "skipping compression.\n",
++ zresult,
++ zs.msg ? zs.msg : zError(zresult));
++ *flags |= IPCOMP_COMPRESSIONERROR;
++ return skb;
++ }
++
++
++ /* Max output size. Result should be max this size.
++ * Implementation specific tweak:
++ * If it's not at least 32 bytes and 6.25% smaller than
++ * the original packet, it's probably not worth wasting
++ * the receiver's CPU cycles decompressing it.
++ * Your mileage may vary.
++ */
++ cpyldsz = pyldsz - sizeof(struct ipcomphdr) - (pyldsz <= 512 ? 32 : pyldsz >> 4);
++
++ buffer = kmalloc(cpyldsz, GFP_ATOMIC);
++ if (!buffer) {
++ KLIPS_PRINT(sysctl_ipsec_debug_ipcomp,
++ "klips_error:skb_compress: "
++ "unable to kmalloc(%d, GFP_ATOMIC), "
++ "skipping compression.\n",
++ cpyldsz);
++ *flags |= IPCOMP_COMPRESSIONERROR;
++ deflateEnd(&zs);
++ return skb;
++ }
++
++#ifdef CONFIG_IPSEC_DEBUG
++ if(sysctl_ipsec_debug_ipcomp && sysctl_ipsec_debug_verbose) {
++ __u8 *c;
++ int i;
++
++ c = (__u8*)iph + iphlen;
++ for(i = 0; i < pyldsz; i++, c++) {
++ if(!(i % 16)) {
++ printk(KERN_INFO "skb_compress: before:");
++ }
++ printk("%02x ", *c);
++ if(!((i + 1) % 16)) {
++ printk("\n");
++ }
++ }
++ if(i % 16) {
++ printk("\n");
++ }
++ }
++#endif /* CONFIG_IPSEC_DEBUG */
++
++ zs.next_in = (char *) iph + iphlen; /* start of payload */
++ zs.avail_in = pyldsz;
++ zs.next_out = buffer; /* start of compressed payload */
++ zs.avail_out = cpyldsz;
++
++ /* Finish compression in one step */
++ zresult = deflate(&zs, Z_FINISH);
++
++ /* Free all dynamically allocated buffers */
++ deflateEnd(&zs);
++ if (zresult != Z_STREAM_END) {
++ *flags |= IPCOMP_UNCOMPRESSABLE;
++ kfree(buffer);
++
++ /* Adjust adaptive counters */
++ if (++(ips->ips_comp_adapt_tries) == IPCOMP_ADAPT_INITIAL_TRIES) {
++ KLIPS_PRINT(sysctl_ipsec_debug_ipcomp,
++ "klips_debug:skb_compress: "
++ "first %d packets didn't compress, "
++ "skipping next %d\n",
++ IPCOMP_ADAPT_INITIAL_TRIES,
++ IPCOMP_ADAPT_INITIAL_SKIP);
++ ips->ips_comp_adapt_skip = IPCOMP_ADAPT_INITIAL_SKIP;
++ }
++ else if (ips->ips_comp_adapt_tries == IPCOMP_ADAPT_INITIAL_TRIES + IPCOMP_ADAPT_SUBSEQ_TRIES) {
++ KLIPS_PRINT(sysctl_ipsec_debug_ipcomp,
++ "klips_debug:skb_compress: "
++ "next %d packets didn't compress, "
++ "skipping next %d\n",
++ IPCOMP_ADAPT_SUBSEQ_TRIES,
++ IPCOMP_ADAPT_SUBSEQ_SKIP);
++ ips->ips_comp_adapt_skip = IPCOMP_ADAPT_SUBSEQ_SKIP;
++ ips->ips_comp_adapt_tries = IPCOMP_ADAPT_INITIAL_TRIES;
++ }
++
++ return skb;
++ }
++
++ /* resulting compressed size */
++ cpyldsz -= zs.avail_out;
++
++ /* Insert IPCOMP header */
++ ((struct ipcomphdr*) ((char*) iph + iphlen))->ipcomp_nh = iph->protocol;
++ ((struct ipcomphdr*) ((char*) iph + iphlen))->ipcomp_flags = 0;
++ /* use the bottom 16 bits of the spi for the cpi. The top 16 bits are
++ for internal reference only. */
++ ((struct ipcomphdr*) (((char*)iph) + iphlen))->ipcomp_cpi = htons((__u16)(ntohl(ips->ips_said.spi) & 0x0000ffff));
++ KLIPS_PRINT(sysctl_ipsec_debug_ipcomp,
++ "klips_debug:skb_compress: "
++ "spi=%08x, spi&0xffff=%04x, cpi=%04x, payload size: raw=%d, comp=%d.\n",
++ ntohl(ips->ips_said.spi),
++ ntohl(ips->ips_said.spi) & 0x0000ffff,
++ ntohs(((struct ipcomphdr*)(((char*)iph)+iphlen))->ipcomp_cpi),
++ pyldsz,
++ cpyldsz);
++
++ /* Update IP header */
++ iph->protocol = IPPROTO_COMP;
++ iph->tot_len = htons(iphlen + sizeof(struct ipcomphdr) + cpyldsz);
++#if 1 /* XXX checksum is done by ipsec_tunnel ? */
++ iph->check = 0;
++ iph->check = ip_fast_csum((char *) iph, iph->ihl);
++#endif
++
++ /* Copy compressed payload */
++ memcpy((char *) iph + iphlen + sizeof(struct ipcomphdr),
++ buffer,
++ cpyldsz);
++ kfree(buffer);
++
++ /* Update skb length/tail by "unputting" the shrinkage */
++ skb_put(skb,
++ cpyldsz + sizeof(struct ipcomphdr) - pyldsz);
++
++#ifdef CONFIG_IPSEC_DEBUG
++ if(sysctl_ipsec_debug_ipcomp && sysctl_ipsec_debug_verbose) {
++ __u8 *c;
++ int i;
++
++ c = (__u8*)iph + iphlen + sizeof(struct ipcomphdr);
++ for(i = 0; i < cpyldsz; i++, c++) {
++ if(!(i % 16)) {
++ printk(KERN_INFO "skb_compress: result:");
++ }
++ printk("%02x ", *c);
++ if(!((i + 1) % 16)) {
++ printk("\n");
++ }
++ }
++ if(i % 16) {
++ printk("\n");
++ }
++ }
++#endif /* CONFIG_IPSEC_DEBUG */
++
++ ips->ips_comp_adapt_skip = 0;
++ ips->ips_comp_adapt_tries = 0;
++
++ return skb;
++}
++
++struct sk_buff *skb_decompress(struct sk_buff *skb, struct ipsec_sa *ips, unsigned int *flags)
++{
++ struct sk_buff *nskb = NULL;
++
++ /* original ip header */
++ struct iphdr *oiph, *iph;
++ unsigned int iphlen, pyldsz, cpyldsz;
++ z_stream zs;
++ int zresult;
++
++ KLIPS_PRINT(sysctl_ipsec_debug_ipcomp,
++ "klips_debug:skb_decompress: .\n");
++
++ if(!skb) {
++ KLIPS_PRINT(sysctl_ipsec_debug_ipcomp,
++ "klips_error:skb_decompress: "
++ "passed in NULL skb, returning ERROR.\n");
++ if (flags) *flags |= IPCOMP_PARMERROR;
++ return skb;
++ }
++
++ if(!ips && sysctl_ipsec_inbound_policy_check) {
++ KLIPS_PRINT(sysctl_ipsec_debug_ipcomp,
++ "klips_error:skb_decompress: "
++ "passed in NULL ipsec_sa needed for comp alg, returning ERROR.\n");
++ if (flags) *flags |= IPCOMP_PARMERROR;
++ return skb;
++ }
++
++ if (!flags) {
++ KLIPS_PRINT(sysctl_ipsec_debug_ipcomp,
++ "klips_error:skb_decompress: "
++ "passed in NULL flags, returning ERROR.\n");
++ ipsec_kfree_skb(skb);
++ return NULL;
++ }
++
++#ifdef NET_21
++ oiph = skb->nh.iph;
++#else /* NET_21 */
++ oiph = skb->ip_hdr;
++#endif /* NET_21 */
++
++ iphlen = oiph->ihl << 2;
++
++ if (oiph->protocol != IPPROTO_COMP) {
++ KLIPS_PRINT(sysctl_ipsec_debug_ipcomp,
++ "klips_error:skb_decompress: "
++ "called with non-IPCOMP packet (protocol=%d),"
++ "skipping decompression.\n",
++ oiph->protocol);
++ *flags |= IPCOMP_PARMERROR;
++ return skb;
++ }
++
++ if ( (((struct ipcomphdr*)((char*) oiph + iphlen))->ipcomp_flags != 0)
++ || ((((struct ipcomphdr*) ((char*) oiph + iphlen))->ipcomp_cpi
++ != htons(SADB_X_CALG_DEFLATE))
++ && sysctl_ipsec_inbound_policy_check
++ && (!ips || (ips && (ips->ips_encalg != SADB_X_CALG_DEFLATE)))) ) {
++ KLIPS_PRINT(sysctl_ipsec_debug_ipcomp,
++ "klips_error:skb_decompress: "
++ "called with incompatible IPCOMP packet (flags=%d, "
++ "cpi=%d), ips-compalg=%d, skipping decompression.\n",
++ ntohs(((struct ipcomphdr*) ((char*) oiph + iphlen))->ipcomp_flags),
++ ntohs(((struct ipcomphdr*) ((char*) oiph + iphlen))->ipcomp_cpi),
++ ips ? ips->ips_encalg : 0);
++ *flags |= IPCOMP_PARMERROR;
++
++ return skb;
++ }
++
++ if (ntohs(oiph->frag_off) & ~0x4000) {
++ KLIPS_PRINT(sysctl_ipsec_debug_ipcomp,
++ "klips_error:skb_decompress: "
++ "called with fragmented IPCOMP packet, "
++ "skipping decompression.\n");
++ *flags |= IPCOMP_PARMERROR;
++ return skb;
++ }
++
++ /* original compressed payload size */
++ cpyldsz = ntohs(oiph->tot_len) - iphlen - sizeof(struct ipcomphdr);
++
++ zs.zalloc = my_zcalloc;
++ zs.zfree = my_zfree;
++ zs.opaque = 0;
++
++ zs.next_in = (char *) oiph + iphlen + sizeof(struct ipcomphdr);
++ zs.avail_in = cpyldsz;
++
++ /* Maybe we should be a bit conservative about memory
++ requirements and use inflateInit2 */
++ /* Beware, that this might make us unable to decompress packets
++ from other implementations - HINT: check PGPnet source code */
++ /* We want to use inflateInit2 because we don't want the adler
++ header. */
++ zresult = inflateInit2(&zs, -15);
++ if (zresult != Z_OK) {
++ KLIPS_PRINT(sysctl_ipsec_debug_ipcomp,
++ "klips_error:skb_decompress: "
++ "inflateInit2() returned error %d (%s), "
++ "skipping decompression.\n",
++ zresult,
++ zs.msg ? zs.msg : zError(zresult));
++ *flags |= IPCOMP_DECOMPRESSIONERROR;
++
++ return skb;
++ }
++
++ /* We have no way of knowing the exact length of the resulting
++ decompressed output before we have actually done the decompression.
++ For now, we guess that the packet will not be bigger than the
++ attached ipsec device's mtu or 16260, whichever is biggest.
++ This may be wrong, since the sender's mtu may be bigger yet.
++ XXX This must be dealt with later XXX
++ */
++
++ /* max payload size */
++ pyldsz = skb->dev ? (skb->dev->mtu < 16260 ? 16260 : skb->dev->mtu)
++ : (65520 - iphlen);
++ KLIPS_PRINT(sysctl_ipsec_debug_ipcomp,
++ "klips_debug:skb_decompress: "
++ "max payload size: %d\n", pyldsz);
++
++ while (pyldsz > (cpyldsz + sizeof(struct ipcomphdr)) &&
++ (nskb = skb_copy_ipcomp(skb,
++ pyldsz - cpyldsz - sizeof(struct ipcomphdr),
++ GFP_ATOMIC)) == NULL) {
++ KLIPS_PRINT(sysctl_ipsec_debug_ipcomp,
++ "klips_error:skb_decompress: "
++ "unable to skb_copy_ipcomp(skb, %d, GFP_ATOMIC), "
++ "trying with less payload size.\n",
++ (int)(pyldsz - cpyldsz - sizeof(struct ipcomphdr)));
++ pyldsz >>=1;
++ }
++
++ if (!nskb) {
++ KLIPS_PRINT(sysctl_ipsec_debug_ipcomp,
++ "klips_error:skb_decompress: "
++ "unable to allocate memory, dropping packet.\n");
++ *flags |= IPCOMP_DECOMPRESSIONERROR;
++ inflateEnd(&zs);
++
++ return skb;
++ }
++
++#ifdef CONFIG_IPSEC_DEBUG
++ if(sysctl_ipsec_debug_ipcomp && sysctl_ipsec_debug_verbose) {
++ __u8 *c;
++ int i;
++
++ c = (__u8*)oiph + iphlen + sizeof(struct ipcomphdr);
++ for(i = 0; i < cpyldsz; i++, c++) {
++ if(!(i % 16)) {
++ printk(KERN_INFO "skb_decompress: before:");
++ }
++ printk("%02x ", *c);
++ if(!((i + 1) % 16)) {
++ printk("\n");
++ }
++ }
++ if(i % 16) {
++ printk("\n");
++ }
++ }
++#endif /* CONFIG_IPSEC_DEBUG */
++
++#ifdef NET_21
++ iph = nskb->nh.iph;
++#else /* NET_21 */
++ iph = nskb->ip_hdr;
++#endif /* NET_21 */
++ zs.next_out = (char *)iph + iphlen;
++ zs.avail_out = pyldsz;
++
++ zresult = inflate(&zs, Z_SYNC_FLUSH);
++
++ /* work around a bug in zlib, which sometimes wants to taste an extra
++ * byte when being used in the (undocumented) raw deflate mode.
++ */
++ if (zresult == Z_OK && !zs.avail_in && zs.avail_out) {
++ __u8 zerostuff = 0;
++
++ zs.next_in = &zerostuff;
++ zs.avail_in = 1;
++ zresult = inflate(&zs, Z_FINISH);
++ }
++
++ inflateEnd(&zs);
++ if (zresult != Z_STREAM_END) {
++ KLIPS_PRINT(sysctl_ipsec_debug_ipcomp,
++ "klips_error:skb_decompress: "
++ "inflate() returned error %d (%s), "
++ "skipping decompression.\n",
++ zresult,
++ zs.msg ? zs.msg : zError(zresult));
++ *flags |= IPCOMP_DECOMPRESSIONERROR;
++ ipsec_kfree_skb(nskb);
++
++ return skb;
++ }
++
++ /* Update IP header */
++ /* resulting decompressed size */
++ pyldsz -= zs.avail_out;
++ iph->tot_len = htons(iphlen + pyldsz);
++ iph->protocol = ((struct ipcomphdr*) ((char*) oiph + iphlen))->ipcomp_nh;
++ KLIPS_PRINT(sysctl_ipsec_debug_ipcomp,
++ "klips_debug:skb_decompress: "
++ "spi=%08x, spi&0xffff=%04x, cpi=%04x, payload size: comp=%d, raw=%d, nh=%d.\n",
++ ips ? ntohl(ips->ips_said.spi) : 0,
++ ips ? ntohl(ips->ips_said.spi) & 0x0000ffff : 0,
++ ntohs(((struct ipcomphdr*)(((char*)oiph)+iphlen))->ipcomp_cpi),
++ cpyldsz,
++ pyldsz,
++ iph->protocol);
++
++#if 1 /* XXX checksum is done by ipsec_rcv ? */
++ iph->check = 0;
++ iph->check = ip_fast_csum((char*) iph, iph->ihl);
++#endif
++
++ /* Update skb length/tail by "unputting" the unused data area */
++ skb_put(nskb, -zs.avail_out);
++
++ ipsec_kfree_skb(skb);
++
++ if (iph->protocol == IPPROTO_COMP)
++ {
++#ifdef CONFIG_IPSEC_DEBUG
++ if(sysctl_ipsec_debug_ipcomp)
++ KLIPS_PRINT(sysctl_ipsec_debug_ipcomp,
++ "klips_debug:skb_decompress: "
++ "Eh? inner packet is also compressed, dropping.\n");
++#endif /* CONFIG_IPSEC_DEBUG */
++
++ ipsec_kfree_skb(nskb);
++ return NULL;
++ }
++
++#ifdef CONFIG_IPSEC_DEBUG
++ if(sysctl_ipsec_debug_ipcomp && sysctl_ipsec_debug_verbose) {
++ __u8 *c;
++ int i;
++
++ c = (__u8*)iph + iphlen;
++ for(i = 0; i < pyldsz; i++, c++) {
++ if(!(i % 16)) {
++ printk(KERN_INFO "skb_decompress: result:");
++ }
++ printk("%02x ", *c);
++ if(!((i + 1) % 16)) {
++ printk("\n");
++ }
++ }
++ if(i % 16) {
++ printk("\n");
++ }
++ }
++#endif /* CONFIG_IPSEC_DEBUG */
++
++ return nskb;
++}
++
++
++/* this is derived from skb_copy() in linux 2.2.14 */
++/* May be incompatible with other kernel versions!! */
++static
++struct sk_buff *skb_copy_ipcomp(struct sk_buff *skb, int data_growth, int gfp_mask)
++{
++ struct sk_buff *n;
++ struct iphdr *iph;
++ unsigned long offset;
++ unsigned int iphlen;
++
++ if(!skb) {
++ KLIPS_PRINT(sysctl_ipsec_debug_ipcomp,
++ "klips_debug:skb_copy_ipcomp: "
++ "passed in NULL skb, returning NULL.\n");
++ return NULL;
++ }
++
++ /*
++ * Allocate the copy buffer
++ */
++
++#ifdef NET_21
++ iph = skb->nh.iph;
++#else /* NET_21 */
++ iph = skb->ip_hdr;
++#endif /* NET_21 */
++ if (!iph) return NULL;
++ iphlen = iph->ihl << 2;
++
++ n=alloc_skb(skb->end - skb->head + data_growth, gfp_mask);
++ if(n==NULL)
++ return NULL;
++
++ /*
++ * Shift between the two data areas in bytes
++ */
++
++ offset=n->head-skb->head;
++
++ /* Set the data pointer */
++ skb_reserve(n,skb->data-skb->head);
++ /* Set the tail pointer and length */
++ skb_put(n,skb->len+data_growth);
++ /* Copy the bytes up to and including the ip header */
++ memcpy(n->head,
++ skb->head,
++ ((char *)iph - (char *)skb->head) + iphlen);
++ n->list=NULL;
++ n->next=NULL;
++ n->prev=NULL;
++ n->sk=NULL;
++ n->dev=skb->dev;
++ if (skb->h.raw)
++ n->h.raw=skb->h.raw+offset;
++ else
++ n->h.raw=NULL;
++ n->protocol=skb->protocol;
++#ifdef NET_21
++ n->csum = 0;
++ n->priority=skb->priority;
++ n->dst=dst_clone(skb->dst);
++ n->nh.raw=skb->nh.raw+offset;
++#ifndef NETDEV_23
++ n->is_clone=0;
++#endif /* NETDEV_23 */
++ atomic_set(&n->users, 1);
++ n->destructor = NULL;
++ n->security=skb->security;
++ memcpy(n->cb, skb->cb, sizeof(skb->cb));
++#ifdef CONFIG_IP_FIREWALL
++ n->fwmark = skb->fwmark;
++#endif
++#else /* NET_21 */
++ n->link3=NULL;
++ n->when=skb->when;
++ n->ip_hdr=(struct iphdr *)(((char *)skb->ip_hdr)+offset);
++ n->saddr=skb->saddr;
++ n->daddr=skb->daddr;
++ n->raddr=skb->raddr;
++ n->seq=skb->seq;
++ n->end_seq=skb->end_seq;
++ n->ack_seq=skb->ack_seq;
++ n->acked=skb->acked;
++ n->free=1;
++ n->arp=skb->arp;
++ n->tries=0;
++ n->lock=0;
++ n->users=0;
++ memcpy(n->proto_priv, skb->proto_priv, sizeof(skb->proto_priv));
++#endif /* NET_21 */
++ if (skb->mac.raw)
++ n->mac.raw=skb->mac.raw+offset;
++ else
++ n->mac.raw=NULL;
++#ifndef NETDEV_23
++ n->used=skb->used;
++#endif /* !NETDEV_23 */
++ n->pkt_type=skb->pkt_type;
++#ifndef NETDEV_23
++ n->pkt_bridged=skb->pkt_bridged;
++#endif /* NETDEV_23 */
++ n->ip_summed=0;
++ n->stamp=skb->stamp;
++#ifndef NETDEV_23 /* this seems to have been removed in 2.4 */
++#if defined(CONFIG_SHAPER) || defined(CONFIG_SHAPER_MODULE)
++ n->shapelatency=skb->shapelatency; /* Latency on frame */
++ n->shapeclock=skb->shapeclock; /* Time it should go out */
++ n->shapelen=skb->shapelen; /* Frame length in clocks */
++ n->shapestamp=skb->shapestamp; /* Stamp for shaper */
++ n->shapepend=skb->shapepend; /* Pending */
++#endif /* defined(CONFIG_SHAPER) || defined(CONFIG_SHAPER_MODULE) */
++#endif /* NETDEV_23 */
++#ifdef CONFIG_HIPPI
++ n->private.ifield=skb->private.ifield;
++#endif /* CONFIG_HIPPI */
++
++ return n;
++}
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/net/ipsec/ipsec_ah.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,363 @@
++/*
++ * processing code for AH
++ * Copyright (C) 2003-2004 Michael Richardson <mcr@xelerance.com>
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ */
++
++char ipsec_ah_c_version[] = "RCSID $Id$";
++#include <linux/config.h>
++#include <linux/version.h>
++
++#define __NO_VERSION__
++#include <linux/module.h>
++#include <linux/kernel.h> /* printk() */
++
++#include "openswan/ipsec_param.h"
++
++#ifdef MALLOC_SLAB
++# include <linux/slab.h> /* kmalloc() */
++#else /* MALLOC_SLAB */
++# include <linux/malloc.h> /* kmalloc() */
++#endif /* MALLOC_SLAB */
++#include <linux/errno.h> /* error codes */
++#include <linux/types.h> /* size_t */
++#include <linux/interrupt.h> /* mark_bh */
++
++#include <linux/netdevice.h> /* struct device, and other headers */
++#include <linux/etherdevice.h> /* eth_type_trans */
++#include <linux/ip.h> /* struct iphdr */
++#include <linux/skbuff.h>
++#include <openswan.h>
++#ifdef SPINLOCK
++# ifdef SPINLOCK_23
++# include <linux/spinlock.h> /* *lock* */
++# else /* SPINLOCK_23 */
++# include <asm/spinlock.h> /* *lock* */
++# endif /* SPINLOCK_23 */
++#endif /* SPINLOCK */
++#ifdef NET_21
++# include <asm/uaccess.h>
++# include <linux/in6.h>
++# define proto_priv cb
++#endif /* NET21 */
++#include <asm/checksum.h>
++#include <net/ip.h>
++
++#include "openswan/radij.h"
++#include "openswan/ipsec_encap.h"
++#include "openswan/ipsec_sa.h"
++
++#include "openswan/ipsec_radij.h"
++#include "openswan/ipsec_xform.h"
++#include "openswan/ipsec_tunnel.h"
++#include "openswan/ipsec_rcv.h"
++#include "openswan/ipsec_xmit.h"
++
++#include "openswan/ipsec_auth.h"
++
++#ifdef CONFIG_IPSEC_AH
++#include "openswan/ipsec_ah.h"
++#endif /* CONFIG_IPSEC_AH */
++
++#include "openswan/ipsec_proto.h"
++
++#ifdef CONFIG_IPSEC_DEBUG
++int debug_ah = 0;
++#endif /* CONFIG_IPSEC_DEBUG */
++
++__u32 zeroes[AH_AMAX];
++
++#ifdef CONFIG_IPSEC_AH
++enum ipsec_rcv_value
++ipsec_rcv_ah_checks(struct ipsec_rcv_state *irs,
++ struct sk_buff *skb)
++{
++ int ahminlen;
++
++ ahminlen = irs->hard_header_len + sizeof(struct iphdr);
++
++ /* take care not to deref this pointer until we check the minlen though */
++ irs->protostuff.ahstuff.ahp = (struct ahhdr *) (skb->data + irs->iphlen);
++
++ if((skb->len < ahminlen+sizeof(struct ahhdr)) ||
++ (skb->len < ahminlen+(irs->protostuff.ahstuff.ahp->ah_hl << 2))) {
++ KLIPS_PRINT(debug_rcv & DB_RX_INAU,
++ "klips_debug:ipsec_rcv: "
++ "runt ah packet of skb->len=%d received from %s, dropped.\n",
++ skb->len,
++ irs->ipsaddr_txt);
++ if(irs->stats) {
++ irs->stats->rx_errors++;
++ }
++ return IPSEC_RCV_BADLEN;
++ }
++
++ irs->said.spi = irs->protostuff.ahstuff.ahp->ah_spi;
++
++ /* XXX we only support the one 12-byte authenticator for now */
++ if(irs->protostuff.ahstuff.ahp->ah_hl != ((AHHMAC_HASHLEN+AHHMAC_RPLLEN) >> 2)) {
++ KLIPS_PRINT(debug_rcv & DB_RX_INAU,
++ "klips_debug:ipsec_rcv: "
++ "bad authenticator length %ld, expected %lu from %s.\n",
++ (long)(irs->protostuff.ahstuff.ahp->ah_hl << 2),
++ (unsigned long) sizeof(struct ahhdr),
++ irs->ipsaddr_txt);
++ if(irs->stats) {
++ irs->stats->rx_errors++;
++ }
++ return IPSEC_RCV_BADLEN;
++ }
++
++ return IPSEC_RCV_OK;
++}
++
++
++enum ipsec_rcv_value
++ipsec_rcv_ah_setup_auth(struct ipsec_rcv_state *irs,
++ struct sk_buff *skb,
++ __u32 *replay,
++ unsigned char **authenticator)
++{
++ struct ahhdr *ahp = irs->protostuff.ahstuff.ahp;
++
++ *replay = ntohl(ahp->ah_rpl);
++ *authenticator = ahp->ah_data;
++
++ return IPSEC_RCV_OK;
++}
++
++enum ipsec_rcv_value
++ipsec_rcv_ah_authcalc(struct ipsec_rcv_state *irs,
++ struct sk_buff *skb)
++{
++ struct auth_alg *aa;
++ struct ahhdr *ahp = irs->protostuff.ahstuff.ahp;
++ union {
++ MD5_CTX md5;
++ SHA1_CTX sha1;
++ } tctx;
++ struct iphdr ipo;
++ int ahhlen;
++
++ aa = irs->authfuncs;
++
++ /* copy the initialized keying material */
++ memcpy(&tctx, irs->ictx, irs->ictx_len);
++
++ ipo = *irs->ipp;
++ ipo.tos = 0; /* mutable RFC 2402 3.3.3.1.1.1 */
++ ipo.frag_off = 0;
++ ipo.ttl = 0;
++ ipo.check = 0;
++
++
++ /* do the sanitized header */
++ (*aa->update)((void*)&tctx, (caddr_t)&ipo, sizeof(struct iphdr));
++
++ /* XXX we didn't do the options here! */
++
++ /* now do the AH header itself */
++ ahhlen = AH_BASIC_LEN + (ahp->ah_hl << 2);
++ (*aa->update)((void*)&tctx, (caddr_t)ahp, ahhlen - AHHMAC_HASHLEN);
++
++ /* now, do some zeroes */
++ (*aa->update)((void*)&tctx, (caddr_t)zeroes, AHHMAC_HASHLEN);
++
++ /* finally, do the packet contents themselves */
++ (*aa->update)((void*)&tctx,
++ (caddr_t)skb->data + irs->iphlen + ahhlen,
++ skb->len - irs->iphlen - ahhlen);
++
++ (*aa->final)(irs->hash, (void *)&tctx);
++
++ memcpy(&tctx, irs->octx, irs->octx_len);
++
++ (*aa->update)((void *)&tctx, irs->hash, aa->hashlen);
++ (*aa->final)(irs->hash, (void *)&tctx);
++
++ return IPSEC_RCV_OK;
++}
++
++enum ipsec_rcv_value
++ipsec_rcv_ah_decap(struct ipsec_rcv_state *irs)
++{
++ struct ahhdr *ahp = irs->protostuff.ahstuff.ahp;
++ struct sk_buff *skb;
++ int ahhlen;
++
++ skb=irs->skb;
++
++ ahhlen = AH_BASIC_LEN + (ahp->ah_hl << 2);
++
++ irs->ipp->tot_len = htons(ntohs(irs->ipp->tot_len) - ahhlen);
++ irs->next_header = ahp->ah_nh;
++
++ /*
++ * move the IP header forward by the size of the AH header, which
++ * will remove the the AH header from the packet.
++ */
++ memmove((void *)(skb->data + ahhlen),
++ (void *)(skb->data), irs->iphlen);
++
++ ipsec_rcv_dmp("ah postmove", skb->data, skb->len);
++
++ /* skb_pull below, will move up by ahhlen */
++
++ /* XXX not clear how this can happen, as the message indicates */
++ if(skb->len < ahhlen) {
++ printk(KERN_WARNING
++ "klips_error:ipsec_rcv: "
++ "tried to skb_pull ahhlen=%d, %d available. This should never happen, please report.\n",
++ ahhlen,
++ (int)(skb->len));
++ return IPSEC_RCV_DECAPFAIL;
++ }
++ skb_pull(skb, ahhlen);
++
++ irs->ipp = (struct iphdr *)skb->data;
++
++ ipsec_rcv_dmp("ah postpull", skb->data, skb->len);
++
++ return IPSEC_RCV_OK;
++}
++
++enum ipsec_xmit_value
++ipsec_xmit_ah_setup(struct ipsec_xmit_state *ixs)
++{
++ struct iphdr ipo;
++ struct ahhdr *ahp;
++ __u8 hash[AH_AMAX];
++ union {
++#ifdef CONFIG_IPSEC_AUTH_HMAC_MD5
++ MD5_CTX md5;
++#endif /* CONFIG_IPSEC_AUTH_HMAC_MD5 */
++#ifdef CONFIG_IPSEC_AUTH_HMAC_SHA1
++ SHA1_CTX sha1;
++#endif /* CONFIG_IPSEC_AUTH_HMAC_SHA1 */
++ } tctx;
++ unsigned char *dat = (unsigned char *)ixs->iph;
++
++ ahp = (struct ahhdr *)(dat + ixs->iphlen);
++ ahp->ah_spi = ixs->ipsp->ips_said.spi;
++ ahp->ah_rpl = htonl(++(ixs->ipsp->ips_replaywin_lastseq));
++ ahp->ah_rv = 0;
++ ahp->ah_nh = ixs->iph->protocol;
++ ahp->ah_hl = (sizeof(struct ahhdr) >> 2) - sizeof(__u64)/sizeof(__u32);
++ ixs->iph->protocol = IPPROTO_AH;
++ ipsec_xmit_dmp("ahp", (char*)ahp, sizeof(*ahp));
++
++ ipo = *ixs->iph;
++ ipo.tos = 0;
++ ipo.frag_off = 0;
++ ipo.ttl = 0;
++ ipo.check = 0;
++ ipsec_xmit_dmp("ipo", (char*)&ipo, sizeof(ipo));
++
++ switch(ixs->ipsp->ips_authalg) {
++#ifdef CONFIG_IPSEC_AUTH_HMAC_MD5
++ case AH_MD5:
++ tctx.md5 = ((struct md5_ctx*)(ixs->ipsp->ips_key_a))->ictx;
++ ipsec_xmit_dmp("ictx", (char*)&tctx.md5, sizeof(tctx.md5));
++ MD5Update(&tctx.md5, (unsigned char *)&ipo, sizeof (struct iphdr));
++ ipsec_xmit_dmp("ictx+ipo", (char*)&tctx.md5, sizeof(tctx.md5));
++ MD5Update(&tctx.md5, (unsigned char *)ahp,
++ sizeof(struct ahhdr) - sizeof(ahp->ah_data));
++ ipsec_xmit_dmp("ictx+ahp", (char*)&tctx.md5, sizeof(tctx.md5));
++ MD5Update(&tctx.md5, (unsigned char *)zeroes, AHHMAC_HASHLEN);
++ ipsec_xmit_dmp("ictx+zeroes", (char*)&tctx.md5, sizeof(tctx.md5));
++ MD5Update(&tctx.md5, dat + ixs->iphlen + sizeof(struct ahhdr),
++ ixs->skb->len - ixs->iphlen - sizeof(struct ahhdr));
++ ipsec_xmit_dmp("ictx+dat", (char*)&tctx.md5, sizeof(tctx.md5));
++ MD5Final(hash, &tctx.md5);
++ ipsec_xmit_dmp("ictx hash", (char*)&hash, sizeof(hash));
++ tctx.md5 = ((struct md5_ctx*)(ixs->ipsp->ips_key_a))->octx;
++ ipsec_xmit_dmp("octx", (char*)&tctx.md5, sizeof(tctx.md5));
++ MD5Update(&tctx.md5, hash, AHMD596_ALEN);
++ ipsec_xmit_dmp("octx+hash", (char*)&tctx.md5, sizeof(tctx.md5));
++ MD5Final(hash, &tctx.md5);
++ ipsec_xmit_dmp("octx hash", (char*)&hash, sizeof(hash));
++
++ memcpy(ahp->ah_data, hash, AHHMAC_HASHLEN);
++
++ /* paranoid */
++ memset((caddr_t)&tctx.md5, 0, sizeof(tctx.md5));
++ memset((caddr_t)hash, 0, sizeof(*hash));
++ break;
++#endif /* CONFIG_IPSEC_AUTH_HMAC_MD5 */
++#ifdef CONFIG_IPSEC_AUTH_HMAC_SHA1
++ case AH_SHA:
++ tctx.sha1 = ((struct sha1_ctx*)(ixs->ipsp->ips_key_a))->ictx;
++ SHA1Update(&tctx.sha1, (unsigned char *)&ipo, sizeof (struct iphdr));
++ SHA1Update(&tctx.sha1, (unsigned char *)ahp, sizeof(struct ahhdr) - sizeof(ahp->ah_data));
++ SHA1Update(&tctx.sha1, (unsigned char *)zeroes, AHHMAC_HASHLEN);
++ SHA1Update(&tctx.sha1, dat + ixs->iphlen + sizeof(struct ahhdr),
++ ixs->skb->len - ixs->iphlen - sizeof(struct ahhdr));
++ SHA1Final(hash, &tctx.sha1);
++ tctx.sha1 = ((struct sha1_ctx*)(ixs->ipsp->ips_key_a))->octx;
++ SHA1Update(&tctx.sha1, hash, AHSHA196_ALEN);
++ SHA1Final(hash, &tctx.sha1);
++
++ memcpy(ahp->ah_data, hash, AHHMAC_HASHLEN);
++
++ /* paranoid */
++ memset((caddr_t)&tctx.sha1, 0, sizeof(tctx.sha1));
++ memset((caddr_t)hash, 0, sizeof(*hash));
++ break;
++#endif /* CONFIG_IPSEC_AUTH_HMAC_SHA1 */
++ default:
++ ixs->stats->tx_errors++;
++ return IPSEC_XMIT_AH_BADALG;
++ }
++#ifdef NET_21
++ ixs->skb->h.raw = (unsigned char*)ahp;
++#endif /* NET_21 */
++
++ return IPSEC_XMIT_OK;
++}
++
++struct xform_functions ah_xform_funcs[]={
++ { rcv_checks: ipsec_rcv_ah_checks,
++ rcv_setup_auth: ipsec_rcv_ah_setup_auth,
++ rcv_calc_auth: ipsec_rcv_ah_authcalc,
++ rcv_decrypt: ipsec_rcv_ah_decap,
++
++ xmit_setup: ipsec_xmit_ah_setup,
++ xmit_headroom: sizeof(struct ahhdr),
++ xmit_needtailroom: 0,
++ },
++};
++
++struct inet_protocol ah_protocol =
++{
++ ipsec_rcv, /* AH handler */
++ NULL, /* TUNNEL error control */
++#ifdef NETDEV_25
++ 1, /* no policy */
++#else
++ 0, /* next */
++ IPPROTO_AH, /* protocol ID */
++ 0, /* copy */
++ NULL, /* data */
++ "AH" /* name */
++#endif
++};
++
++#endif /* CONFIG_IPSEC_AH */
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.2 2004/04/06 02:49:25 mcr
++ * pullup of algo code from alg-branch.
++ *
++ *
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/net/ipsec/ipsec_alg.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,953 @@
++/*
++ * Modular extensions service and registration functions
++ *
++ * Author: JuanJo Ciarlante <jjo-ipsec@mendoza.gov.ar>
++ *
++ * Version: 0.8.1
++ *
++ * ipsec_alg.c,v 1.1.2.1 2003/11/21 18:12:23 jjo Exp
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ */
++#ifdef CONFIG_IPSEC_ALG
++#define __NO_VERSION__
++#include <linux/module.h>
++#include <linux/kernel.h> /* printk() */
++
++#include <linux/netdevice.h> /* struct device, and other headers */
++#include <linux/etherdevice.h> /* eth_type_trans */
++#include <linux/ip.h> /* struct iphdr */
++#include <linux/skbuff.h>
++#include <linux/socket.h>
++#include <linux/in.h>
++#include <linux/types.h>
++#include <linux/string.h> /* memcmp() */
++#include <linux/random.h> /* get_random_bytes() */
++#include <linux/errno.h> /* error codes */
++#ifdef SPINLOCK
++# ifdef SPINLOCK_23
++# include <linux/spinlock.h> /* *lock* */
++# else /* SPINLOCK_23 */
++# include <asm/spinlock.h> /* *lock* */
++# endif /* SPINLOCK_23 */
++#endif /* SPINLOCK */
++#ifdef NET_21
++# include <asm/uaccess.h>
++# include <linux/in6.h>
++# define proto_priv cb
++#endif /* NET21 */
++#include "openswan/ipsec_param.h"
++#include <openswan.h>
++#include "openswan/ipsec_sa.h"
++#include "openswan/radij.h"
++#include "openswan/ipsec_encap.h"
++#include "openswan/ipsec_radij.h"
++#include "openswan/ipsec_xform.h"
++#include "openswan/ipsec_tunnel.h"
++#include "openswan/ipsec_rcv.h"
++#if defined(CONFIG_IPSEC_ESP) || defined(CONFIG_IPSEC_AH)
++# include "openswan/ipsec_ah.h"
++#endif /* defined(CONFIG_IPSEC_ESP) || defined(CONFIG_IPSEC_AH) */
++#ifdef CONFIG_IPSEC_ESP
++# include "openswan/ipsec_esp.h"
++#endif /* !CONFIG_IPSEC_ESP */
++#ifdef CONFIG_IPSEC_IPCOMP
++# include "openswan/ipcomp.h"
++#endif /* CONFIG_IPSEC_COMP */
++
++#include <pfkeyv2.h>
++#include <pfkey.h>
++
++#include "openswan/ipsec_alg.h"
++
++#ifndef CONFIG_IPSEC_ALG
++#error This file _MUST_ be compiled with CONFIG_IPSEC_ALG enabled !
++#endif
++#if SADB_EALG_MAX < 255
++#warning Compiling with limited ESP support ( SADB_EALG_MAX < 256 )
++#endif
++
++static rwlock_t ipsec_alg_lock = RW_LOCK_UNLOCKED;
++#define IPSEC_ALG_HASHSZ 16 /* must be power of 2, even 2^0=1 */
++static struct list_head ipsec_alg_hash_table[IPSEC_ALG_HASHSZ];
++
++/* Old gcc's will fail here */
++#define barf_out(fmt, args...) do { printk(KERN_ERR "%s: (%s) " fmt, __FUNCTION__, ixt->ixt_name , ## args)\
++ ; goto out; } while(0)
++
++/*
++ * Must be already protected by lock
++ */
++static void __ipsec_alg_usage_inc(struct ipsec_alg *ixt) {
++ if (ixt->ixt_module)
++ __MOD_INC_USE_COUNT(ixt->ixt_module);
++ atomic_inc(&ixt->ixt_refcnt);
++}
++static void __ipsec_alg_usage_dec(struct ipsec_alg *ixt) {
++ atomic_dec(&ixt->ixt_refcnt);
++ if (ixt->ixt_module)
++ __MOD_DEC_USE_COUNT(ixt->ixt_module);
++}
++/*
++ * simple hash function, optimized for 0-hash (1 list) special
++ * case
++ */
++#if IPSEC_ALG_HASHSZ > 1
++static inline unsigned ipsec_alg_hashfn(int alg_type, int alg_id) {
++ return ((alg_type^alg_id)&(IPSEC_ALG_HASHSZ-1));
++}
++#else
++#define ipsec_alg_hashfn(x,y) (0)
++#endif
++
++/*****************************************************************
++ *
++ * INTERNAL table handling: insert, delete, find
++ *
++ *****************************************************************/
++
++/*
++ * hash table initialization, called from ipsec_alg_init()
++ */
++static void ipsec_alg_hash_init(void) {
++ struct list_head *head = ipsec_alg_hash_table;
++ int i = IPSEC_ALG_HASHSZ;
++ do {
++ INIT_LIST_HEAD(head);
++ head++;
++ i--;
++ } while (i);
++}
++/*
++ * hash list lookup by {alg_type, alg_id} and table head,
++ * must be already protected by lock
++ */
++static struct ipsec_alg *__ipsec_alg_find(unsigned alg_type, unsigned alg_id, struct list_head * head) {
++ struct list_head *p;
++ struct ipsec_alg *ixt=NULL;
++ for (p=head->next; p!=head; p=p->next) {
++ ixt = list_entry(p, struct ipsec_alg, ixt_list);
++ if (ixt->ixt_alg_type == alg_type && ixt->ixt_alg_id==alg_id) {
++ goto out;
++ }
++ }
++ ixt=NULL;
++out:
++ return ixt;
++}
++/*
++ * inserts (in front) a new entry in hash table,
++ * called from ipsec_alg_register() when new algorithm is registered.
++ */
++static int ipsec_alg_insert(struct ipsec_alg *ixt) {
++ int ret=-EINVAL;
++ unsigned hashval=ipsec_alg_hashfn(ixt->ixt_alg_type, ixt->ixt_alg_id);
++ struct list_head *head= ipsec_alg_hash_table + hashval;
++ struct ipsec_alg *ixt_cur;
++ /* new element must be virgin ... */
++ if (ixt->ixt_list.next != &ixt->ixt_list ||
++ ixt->ixt_list.prev != &ixt->ixt_list) {
++ printk(KERN_ERR "ipsec_alg_insert: ixt object \"%s\" "
++ "list head not initialized\n",
++ ixt->ixt_name);
++ return ret;
++ }
++ write_lock_bh(&ipsec_alg_lock);
++ ixt_cur = __ipsec_alg_find(ixt->ixt_alg_type, ixt->ixt_alg_id, head);
++ /* if previous (current) ipsec_alg found check excl flag of _anyone_ */
++ if (ixt_cur && ((ixt->ixt_state|ixt_cur->ixt_state) & IPSEC_ALG_ST_EXCL))
++ barf_out("ipsec_alg for alg_type=%d, alg_id=%d already exist. "
++ "Not loaded (ret=%d).\n",
++ ixt->ixt_alg_type,
++ ixt->ixt_alg_id, ret=-EEXIST);
++ list_add(&ixt->ixt_list, head);
++ ixt->ixt_state |= IPSEC_ALG_ST_REGISTERED;
++ ret=0;
++out:
++ write_unlock_bh(&ipsec_alg_lock);
++ return ret;
++}
++/*
++ * deletes an existing entry in hash table,
++ * called from ipsec_alg_unregister() when algorithm is unregistered.
++ */
++static int ipsec_alg_delete(struct ipsec_alg *ixt) {
++ write_lock_bh(&ipsec_alg_lock);
++ list_del(&ixt->ixt_list);
++ write_unlock_bh(&ipsec_alg_lock);
++ return 0;
++}
++/*
++ * here @user context (read-only when @kernel bh context)
++ * -> no bh disabling
++ *
++ * called from ipsec_sa_init() -> ipsec_alg_sa_init()
++ */
++static struct ipsec_alg *ipsec_alg_get(int alg_type, int alg_id) {
++ unsigned hashval=ipsec_alg_hashfn(alg_type, alg_id);
++ struct list_head *head= ipsec_alg_hash_table + hashval;
++ struct ipsec_alg *ixt;
++ read_lock(&ipsec_alg_lock);
++ ixt=__ipsec_alg_find(alg_type, alg_id, head);
++ if (ixt) __ipsec_alg_usage_inc(ixt);
++ read_unlock(&ipsec_alg_lock);
++ return ixt;
++}
++
++static void ipsec_alg_put(struct ipsec_alg *ixt) {
++ __ipsec_alg_usage_dec((struct ipsec_alg *)ixt);
++}
++
++/*****************************************************************
++ *
++ * INTERFACE for ENC services: key creation, encrypt function
++ *
++ *****************************************************************/
++
++/*
++ * main encrypt service entry point
++ * called from ipsec_rcv() with encrypt=IPSEC_ALG_DECRYPT and
++ * ipsec_tunnel_start_xmit with encrypt=IPSEC_ALG_ENCRYPT
++ */
++int ipsec_alg_esp_encrypt(struct ipsec_sa *sa_p, __u8 * idat, int ilen, const __u8 * iv, int encrypt) {
++ int ret;
++ struct ipsec_alg_enc *ixt_e=sa_p->ips_alg_enc;
++ KLIPS_PRINT(debug_rcv||debug_tunnel,
++ "klips_debug:ipsec_alg_esp_encrypt: "
++ "entering with encalg=%d, ixt_e=%p\n",
++ sa_p->ips_encalg, ixt_e);
++ if (!ixt_e) {
++ KLIPS_PRINT(debug_rcv||debug_tunnel,
++ "klips_debug:ipsec_alg_esp_encrypt: "
++ "NULL ipsec_alg_enc object\n");
++ return -1;
++ }
++ KLIPS_PRINT(debug_rcv||debug_tunnel,
++ "klips_debug:ipsec_alg_esp_encrypt: "
++ "calling cbc_encrypt encalg=%d "
++ "ips_key_e=%p idat=%p ilen=%d iv=%p, encrypt=%d\n",
++ sa_p->ips_encalg,
++ sa_p->ips_key_e, idat, ilen, iv, encrypt);
++ ret=ixt_e->ixt_e_cbc_encrypt(ixt_e, sa_p->ips_key_e, idat, ilen, iv, encrypt);
++ KLIPS_PRINT(debug_rcv||debug_tunnel,
++ "klips_debug:ipsec_alg_esp_encrypt: "
++ "returned ret=%d\n",
++ ret);
++ return ret;
++}
++/*
++ * encryption key context creation function
++ * called from pfkey_v2_parser.c:pfkey_ips_init()
++ */
++int ipsec_alg_enc_key_create(struct ipsec_sa *sa_p) {
++ int ret=-EINVAL;
++ int keyminbits, keymaxbits;
++ caddr_t ekp;
++ struct ipsec_alg_enc *ixt_e=sa_p->ips_alg_enc;
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:ipsec_alg_enc_key_create: "
++ "entering with encalg=%d ixt_e=%p\n",
++ sa_p->ips_encalg, ixt_e);
++ if (!ixt_e) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:ipsec_alg_enc_key_create: "
++ "NULL ipsec_alg_enc object\n");
++ return -EPROTO;
++ }
++
++ /*
++ * grRRR... DES 7bits jurassic stuff ... f*ckk --jjo
++ */
++ switch(ixt_e->ixt_alg_id) {
++ case ESP_3DES:
++ keyminbits=keymaxbits=192;break;
++ case ESP_DES:
++ keyminbits=keymaxbits=64;break;
++ default:
++ keyminbits=ixt_e->ixt_keyminbits;
++ keymaxbits=ixt_e->ixt_keymaxbits;
++ }
++ if(sa_p->ips_key_bits_e<keyminbits ||
++ sa_p->ips_key_bits_e>keymaxbits) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:ipsec_alg_enc_key_create: "
++ "incorrect encryption key size for id=%d: %d bits -- "
++ "must be between %d,%d bits\n" /*octets (bytes)\n"*/,
++ ixt_e->ixt_alg_id,
++ sa_p->ips_key_bits_e, keyminbits, keymaxbits);
++ ret=-EINVAL;
++ goto ixt_out;
++ }
++ /* save encryption key pointer */
++ ekp = sa_p->ips_key_e;
++
++
++ if (ixt_e->ixt_e_new_key) {
++ sa_p->ips_key_e = ixt_e->ixt_e_new_key(ixt_e,
++ ekp, sa_p->ips_key_bits_e/8);
++ ret = (sa_p->ips_key_e)? 0 : -EINVAL;
++ } else {
++ if((sa_p->ips_key_e = (caddr_t)
++ kmalloc((sa_p->ips_key_e_size = ixt_e->ixt_e_ctx_size),
++ GFP_ATOMIC)) == NULL) {
++ ret=-ENOMEM;
++ goto ixt_out;
++ }
++ /* zero-out key_e */
++ memset(sa_p->ips_key_e, 0, sa_p->ips_key_e_size);
++
++ /* I cast here to allow more decoupling in alg module */
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:ipsec_alg_enc_key_create: about to call:"
++ "set_key(key_e=%p, ekp=%p, key_size=%d)\n",
++ (caddr_t)sa_p->ips_key_e, ekp, sa_p->ips_key_bits_e/8);
++ ret = ixt_e->ixt_e_set_key(ixt_e, (caddr_t)sa_p->ips_key_e, ekp, sa_p->ips_key_bits_e/8);
++ }
++ /* paranoid */
++ memset(ekp, 0, sa_p->ips_key_bits_e/8);
++ kfree(ekp);
++ixt_out:
++ return ret;
++}
++
++/***************************************************************
++ *
++ * INTERFACE for AUTH services: key creation, hash functions
++ *
++ ***************************************************************/
++
++/*
++ * auth key context creation function
++ * called from pfkey_v2_parser.c:pfkey_ips_init()
++ */
++int ipsec_alg_auth_key_create(struct ipsec_sa *sa_p) {
++ int ret=-EINVAL;
++ struct ipsec_alg_auth *ixt_a=sa_p->ips_alg_auth;
++ int keyminbits, keymaxbits;
++ unsigned char *akp;
++ unsigned int aks;
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:ipsec_alg_auth_key_create: "
++ "entering with authalg=%d ixt_a=%p\n",
++ sa_p->ips_authalg, ixt_a);
++ if (!ixt_a) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:ipsec_alg_auth_key_create: "
++ "NULL ipsec_alg_auth object\n");
++ return -EPROTO;
++ }
++ keyminbits=ixt_a->ixt_keyminbits;
++ keymaxbits=ixt_a->ixt_keymaxbits;
++ if(sa_p->ips_key_bits_a<keyminbits || sa_p->ips_key_bits_a>keymaxbits) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:ipsec_alg_auth_key_create: incorrect auth"
++ "key size: %d bits -- must be between %d,%d bits\n"/*octets (bytes)\n"*/,
++ sa_p->ips_key_bits_a, keyminbits, keymaxbits);
++ ret=-EINVAL;
++ goto ixt_out;
++ }
++ /* save auth key pointer */
++ sa_p->ips_auth_bits = ixt_a->ixt_a_keylen * 8; /* XXX XXX */
++ akp = sa_p->ips_key_a;
++ aks = sa_p->ips_key_a_size;
++
++ /* will hold: 2 ctx and a blocksize buffer: kb */
++ sa_p->ips_key_a_size = ixt_a->ixt_a_ctx_size;
++ if((sa_p->ips_key_a =
++ (caddr_t) kmalloc(sa_p->ips_key_a_size, GFP_ATOMIC)) == NULL) {
++ ret=-ENOMEM;
++ goto ixt_out;
++ }
++ ixt_a->ixt_a_hmac_set_key(ixt_a, sa_p->ips_key_a, akp, sa_p->ips_key_bits_a/8); /* XXX XXX */
++ ret=0;
++ memset(akp, 0, aks);
++ kfree(akp);
++
++ixt_out:
++ return ret;
++}
++
++
++int ipsec_alg_sa_esp_hash(const struct ipsec_sa *sa_p, const __u8 *espp, int len, __u8 *hash, int hashlen) {
++ struct ipsec_alg_auth *ixt_a=sa_p->ips_alg_auth;
++ if (!ixt_a) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:ipsec_sa_esp_hash: "
++ "NULL ipsec_alg_auth object\n");
++ return -EPROTO;
++ }
++ KLIPS_PRINT(debug_tunnel|debug_rcv,
++ "klips_debug:ipsec_sa_esp_hash: "
++ "hashing %p (%d bytes) to %p (%d bytes)\n",
++ espp, len,
++ hash, hashlen);
++ ixt_a->ixt_a_hmac_hash(ixt_a,
++ sa_p->ips_key_a,
++ espp, len,
++ hash, hashlen);
++ return 0;
++}
++
++/***************************************************************
++ *
++ * INTERFACE for module loading,testing, and unloading
++ *
++ ***************************************************************/
++
++/* validation for registering (enc) module */
++static int check_enc(struct ipsec_alg_enc *ixt) {
++ int ret=-EINVAL;
++ if (ixt->ixt_alg_id==0 || ixt->ixt_alg_id > SADB_EALG_MAX)
++ barf_out("invalid alg_id=%d >= %d\n", ixt->ixt_alg_id, SADB_EALG_MAX);
++ if (ixt->ixt_blocksize==0) /* || ixt->ixt_blocksize%2) need for ESP_NULL */
++ barf_out(KERN_ERR "invalid blocksize=%d\n", ixt->ixt_blocksize);
++ if (ixt->ixt_keyminbits==0 && ixt->ixt_keymaxbits==0 && ixt->ixt_e_keylen==0)
++ goto zero_key_ok;
++ if (ixt->ixt_keyminbits==0)
++ barf_out(KERN_ERR "invalid keyminbits=%d\n", ixt->ixt_keyminbits);
++ if (ixt->ixt_keymaxbits==0)
++ barf_out(KERN_ERR "invalid keymaxbits=%d\n", ixt->ixt_keymaxbits);
++ if (ixt->ixt_e_keylen==0)
++ barf_out(KERN_ERR "invalid keysize=%d\n", ixt->ixt_e_keylen);
++zero_key_ok:
++ if (ixt->ixt_e_ctx_size==0 && ixt->ixt_e_new_key == NULL)
++ barf_out(KERN_ERR "invalid key_e_size=%d and ixt_e_new_key=NULL\n", ixt->ixt_e_ctx_size);
++ if (ixt->ixt_e_cbc_encrypt==NULL)
++ barf_out(KERN_ERR "e_cbc_encrypt() must be not NULL\n");
++ ret=0;
++out:
++ return ret;
++}
++
++/* validation for registering (auth) module */
++static int check_auth(struct ipsec_alg_auth *ixt)
++{
++ int ret=-EINVAL;
++ if (ixt->ixt_alg_id==0 || ixt->ixt_alg_id > SADB_AALG_MAX)
++ barf_out("invalid alg_id=%d > %d (SADB_AALG_MAX)\n", ixt->ixt_alg_id, SADB_AALG_MAX);
++ if (ixt->ixt_blocksize==0 || ixt->ixt_blocksize%2)
++ barf_out(KERN_ERR "invalid blocksize=%d\n", ixt->ixt_blocksize);
++ if (ixt->ixt_blocksize>AH_BLKLEN_MAX)
++ barf_out(KERN_ERR "sorry blocksize=%d > %d. "
++ "Please increase AH_BLKLEN_MAX and recompile\n",
++ ixt->ixt_blocksize,
++ AH_BLKLEN_MAX);
++ if (ixt->ixt_keyminbits==0 && ixt->ixt_keymaxbits==0 && ixt->ixt_a_keylen==0)
++ goto zero_key_ok;
++ if (ixt->ixt_keyminbits==0)
++ barf_out(KERN_ERR "invalid keyminbits=%d\n", ixt->ixt_keyminbits);
++ if (ixt->ixt_keymaxbits==0)
++ barf_out(KERN_ERR "invalid keymaxbits=%d\n", ixt->ixt_keymaxbits);
++ if (ixt->ixt_keymaxbits!=ixt->ixt_keyminbits)
++ barf_out(KERN_ERR "keymaxbits must equal keyminbits (not sure).\n");
++ if (ixt->ixt_a_keylen==0)
++ barf_out(KERN_ERR "invalid keysize=%d\n", ixt->ixt_a_keylen);
++zero_key_ok:
++ if (ixt->ixt_a_ctx_size==0)
++ barf_out(KERN_ERR "invalid a_ctx_size=%d\n", ixt->ixt_a_ctx_size);
++ if (ixt->ixt_a_hmac_set_key==NULL)
++ barf_out(KERN_ERR "a_hmac_set_key() must be not NULL\n");
++ if (ixt->ixt_a_hmac_hash==NULL)
++ barf_out(KERN_ERR "a_hmac_hash() must be not NULL\n");
++ ret=0;
++out:
++ return ret;
++}
++
++/*
++ * Generic (enc, auth) registration entry point
++ */
++int register_ipsec_alg(struct ipsec_alg *ixt) {
++ int ret=-EINVAL;
++ /* Validation */
++ if (ixt==NULL)
++ barf_out("NULL ipsec_alg object passed\n");
++ if ((ixt->ixt_version&0xffffff00) != (IPSEC_ALG_VERSION&0xffffff00))
++ barf_out("incorrect version: %d.%d.%d-%d, "
++ "must be %d.%d.%d[-%d]\n",
++ IPSEC_ALG_VERSION_QUAD(ixt->ixt_version),
++ IPSEC_ALG_VERSION_QUAD(IPSEC_ALG_VERSION));
++ switch(ixt->ixt_alg_type) {
++ case IPSEC_ALG_TYPE_AUTH:
++ if ((ret=check_auth((struct ipsec_alg_auth *)ixt)<0))
++ goto out;
++ break;
++ case IPSEC_ALG_TYPE_ENCRYPT:
++ if ((ret=check_enc((struct ipsec_alg_enc *)ixt)<0))
++ goto out;
++ /*
++ * Adapted two lines below:
++ * ivlen == 0 is possible (NULL enc has blocksize==1)
++ *
++ * fixed NULL support by David De Reu <DeReu@tComLabs.com>
++ */
++ if (ixt->ixt_ivlen == 0 && ixt->ixt_blocksize > 1)
++ ixt->ixt_ivlen = ixt->ixt_blocksize*8;
++ break;
++ default:
++ barf_out("alg_type=%d not supported\n", ixt->ixt_alg_type);
++ }
++ INIT_LIST_HEAD(&ixt->ixt_list);
++ ret = ipsec_alg_insert(ixt);
++ if (ret<0)
++ barf_out(KERN_WARNING "ipsec_alg for alg_id=%d failed."
++ "Not loaded (ret=%d).\n",
++ ixt->ixt_alg_id, ret);
++
++ ret = pfkey_list_insert_supported((struct supported *)&ixt->ixt_support, &(pfkey_supported_list[SADB_SATYPE_ESP]));
++ if (ret==0) {
++ ixt->ixt_state |= IPSEC_ALG_ST_SUPP;
++ /* send register event to userspace */
++ pfkey_register_reply(SADB_SATYPE_ESP, NULL);
++ } else
++ printk(KERN_ERR "pfkey_list_insert_supported returned %d. "
++ "Loading anyway.\n", ret);
++ ret=0;
++out:
++ return ret;
++}
++
++/*
++ * unregister ipsec_alg object from own tables, if
++ * success => calls pfkey_list_remove_supported()
++ */
++int unregister_ipsec_alg(struct ipsec_alg *ixt) {
++ int ret= -EINVAL;
++ switch(ixt->ixt_alg_type) {
++ case IPSEC_ALG_TYPE_AUTH:
++ case IPSEC_ALG_TYPE_ENCRYPT:
++ break;
++ default:
++ /* this is not a typo :) */
++ barf_out("frog found in list (\"%s\"): ixt_p=NULL\n",
++ ixt->ixt_name);
++ }
++
++ ret=ipsec_alg_delete(ixt);
++ if (ixt->ixt_state&IPSEC_ALG_ST_SUPP) {
++ ixt->ixt_state &= ~IPSEC_ALG_ST_SUPP;
++ pfkey_list_remove_supported((struct supported *)&ixt->ixt_support, &(pfkey_supported_list[SADB_SATYPE_ESP]));
++ /* send register event to userspace */
++ pfkey_register_reply(SADB_SATYPE_ESP, NULL);
++ }
++
++out:
++ return ret;
++}
++
++/*
++ * Must be called from user context
++ * used at module load type for testing algo implementation
++ */
++static int ipsec_alg_test_encrypt(int enc_alg, int test) {
++ int ret;
++ caddr_t buf = NULL;
++ int iv_size, keysize, key_e_size;
++ struct ipsec_alg_enc *ixt_e;
++ void *tmp_key_e = NULL;
++ #define BUFSZ 1024
++ #define MARGIN 0
++ #define test_enc (buf+MARGIN)
++ #define test_dec (test_enc+BUFSZ+MARGIN)
++ #define test_tmp (test_dec+BUFSZ+MARGIN)
++ #define test_key_e (test_tmp+BUFSZ+MARGIN)
++ #define test_iv (test_key_e+key_e_size+MARGIN)
++ #define test_key (test_iv+iv_size+MARGIN)
++ #define test_size (BUFSZ*3+key_e_size+iv_size+keysize+MARGIN*7)
++ ixt_e=(struct ipsec_alg_enc *)ipsec_alg_get(IPSEC_ALG_TYPE_ENCRYPT, enc_alg);
++ if (ixt_e==NULL) {
++ KLIPS_PRINT(1,
++ "klips_debug: ipsec_alg_test_encrypt: "
++ "encalg=%d object not found\n",
++ enc_alg);
++ ret=-EINVAL;
++ goto out;
++ }
++ iv_size=ixt_e->ixt_ivlen / 8;
++ key_e_size=ixt_e->ixt_e_ctx_size;
++ keysize=ixt_e->ixt_e_keylen;
++ KLIPS_PRINT(1,
++ "klips_debug: ipsec_alg_test_encrypt: "
++ "enc_alg=%d blocksize=%d key_e_size=%d keysize=%d\n",
++ enc_alg, iv_size, key_e_size, keysize);
++ if ((buf=kmalloc (test_size, GFP_KERNEL)) == NULL) {
++ ret= -ENOMEM;
++ goto out;
++ }
++ get_random_bytes(test_key, keysize);
++ get_random_bytes(test_iv, iv_size);
++ if (ixt_e->ixt_e_new_key) {
++ tmp_key_e = ixt_e->ixt_e_new_key(ixt_e, test_key, keysize);
++ ret = tmp_key_e ? 0 : -EINVAL;
++ } else {
++ tmp_key_e = test_key_e;
++ ret = ixt_e->ixt_e_set_key(ixt_e, test_key_e, test_key, keysize);
++ }
++ if (ret < 0)
++ goto out;
++ get_random_bytes(test_enc, BUFSZ);
++ memcpy(test_tmp, test_enc, BUFSZ);
++ ret=ixt_e->ixt_e_cbc_encrypt(ixt_e, tmp_key_e, test_enc, BUFSZ, test_iv, 1);
++ printk(KERN_INFO
++ "klips_info: ipsec_alg_test_encrypt: "
++ "cbc_encrypt=1 ret=%d\n",
++ ret);
++ ret=memcmp(test_enc, test_tmp, BUFSZ);
++ printk(KERN_INFO
++ "klips_info: ipsec_alg_test_encrypt: "
++ "memcmp(enc, tmp) ret=%d: %s\n", ret,
++ ret!=0? "OK. (encr->DIFFers)" : "FAIL! (encr->SAME)" );
++ memcpy(test_dec, test_enc, BUFSZ);
++ ret=ixt_e->ixt_e_cbc_encrypt(ixt_e, tmp_key_e, test_dec, BUFSZ, test_iv, 0);
++ printk(KERN_INFO
++ "klips_info: ipsec_alg_test_encrypt: "
++ "cbc_encrypt=0 ret=%d\n", ret);
++ ret=memcmp(test_dec, test_tmp, BUFSZ);
++ printk(KERN_INFO
++ "klips_info: ipsec_alg_test_encrypt: "
++ "memcmp(dec,tmp) ret=%d: %s\n", ret,
++ ret==0? "OK. (encr->decr->SAME)" : "FAIL! (encr->decr->DIFFers)" );
++ {
++ /* Shamelessly taken from drivers/md sources O:) */
++ unsigned long now;
++ int i, count, max=0;
++ int encrypt, speed;
++ for (encrypt=0; encrypt <2;encrypt ++) {
++ for (i = 0; i < 5; i++) {
++ now = jiffies;
++ count = 0;
++ while (jiffies == now) {
++ mb();
++ ixt_e->ixt_e_cbc_encrypt(ixt_e,
++ tmp_key_e, test_tmp,
++ BUFSZ, test_iv, encrypt);
++ mb();
++ count++;
++ mb();
++ }
++ if (count > max)
++ max = count;
++ }
++ speed = max * (HZ * BUFSZ / 1024);
++ printk(KERN_INFO
++ "klips_info: ipsec_alg_test_encrypt: "
++ "%s %s speed=%d KB/s\n",
++ ixt_e->ixt_name,
++ encrypt? "encrypt": "decrypt", speed);
++ }
++ }
++out:
++ if (tmp_key_e && ixt_e->ixt_e_destroy_key) ixt_e->ixt_e_destroy_key(ixt_e, tmp_key_e);
++ if (buf) kfree(buf);
++ if (ixt_e) ipsec_alg_put((struct ipsec_alg *)ixt_e);
++ return ret;
++ #undef test_enc
++ #undef test_dec
++ #undef test_tmp
++ #undef test_key_e
++ #undef test_iv
++ #undef test_key
++ #undef test_size
++}
++
++/*
++ * Must be called from user context
++ * used at module load type for testing algo implementation
++ */
++static int ipsec_alg_test_auth(int auth_alg, int test) {
++ int ret;
++ caddr_t buf = NULL;
++ int blocksize, keysize, key_a_size;
++ struct ipsec_alg_auth *ixt_a;
++ #define BUFSZ 1024
++ #define MARGIN 0
++ #define test_auth (buf+MARGIN)
++ #define test_key_a (test_auth+BUFSZ+MARGIN)
++ #define test_key (test_key_a+key_a_size+MARGIN)
++ #define test_hash (test_key+keysize+MARGIN)
++ #define test_size (BUFSZ+key_a_size+keysize+AHHMAC_HASHLEN+MARGIN*4)
++ ixt_a=(struct ipsec_alg_auth *)ipsec_alg_get(IPSEC_ALG_TYPE_AUTH, auth_alg);
++ if (ixt_a==NULL) {
++ KLIPS_PRINT(1,
++ "klips_debug: ipsec_alg_test_auth: "
++ "encalg=%d object not found\n",
++ auth_alg);
++ ret=-EINVAL;
++ goto out;
++ }
++ blocksize=ixt_a->ixt_blocksize;
++ key_a_size=ixt_a->ixt_a_ctx_size;
++ keysize=ixt_a->ixt_a_keylen;
++ KLIPS_PRINT(1,
++ "klips_debug: ipsec_alg_test_auth: "
++ "auth_alg=%d blocksize=%d key_a_size=%d keysize=%d\n",
++ auth_alg, blocksize, key_a_size, keysize);
++ if ((buf=kmalloc (test_size, GFP_KERNEL)) == NULL) {
++ ret= -ENOMEM;
++ goto out;
++ }
++ get_random_bytes(test_key, keysize);
++ ret = ixt_a->ixt_a_hmac_set_key(ixt_a, test_key_a, test_key, keysize);
++ if (ret < 0 )
++ goto out;
++ get_random_bytes(test_auth, BUFSZ);
++ ret=ixt_a->ixt_a_hmac_hash(ixt_a, test_key_a, test_auth, BUFSZ, test_hash, AHHMAC_HASHLEN);
++ printk(KERN_INFO
++ "klips_info: ipsec_alg_test_auth: "
++ "ret=%d\n", ret);
++ {
++ /* Shamelessly taken from drivers/md sources O:) */
++ unsigned long now;
++ int i, count, max=0;
++ int speed;
++ for (i = 0; i < 5; i++) {
++ now = jiffies;
++ count = 0;
++ while (jiffies == now) {
++ mb();
++ ixt_a->ixt_a_hmac_hash(ixt_a, test_key_a, test_auth, BUFSZ, test_hash, AHHMAC_HASHLEN);
++ mb();
++ count++;
++ mb();
++ }
++ if (count > max)
++ max = count;
++ }
++ speed = max * (HZ * BUFSZ / 1024);
++ printk(KERN_INFO
++ "klips_info: ipsec_alg_test_auth: "
++ "%s hash speed=%d KB/s\n",
++ ixt_a->ixt_name,
++ speed);
++ }
++out:
++ if (buf) kfree(buf);
++ if (ixt_a) ipsec_alg_put((struct ipsec_alg *)ixt_a);
++ return ret;
++ #undef test_auth
++ #undef test_key_a
++ #undef test_key
++ #undef test_hash
++ #undef test_size
++}
++
++int ipsec_alg_test(unsigned alg_type, unsigned alg_id, int test) {
++ switch(alg_type) {
++ case IPSEC_ALG_TYPE_ENCRYPT:
++ return ipsec_alg_test_encrypt(alg_id, test);
++ break;
++ case IPSEC_ALG_TYPE_AUTH:
++ return ipsec_alg_test_auth(alg_id, test);
++ break;
++ }
++ printk(KERN_ERR "klips_info: ipsec_alg_test() called incorrectly: "
++ "alg_type=%d alg_id=%d\n",
++ alg_type, alg_id);
++ return -EINVAL;
++}
++
++int ipsec_alg_init(void) {
++ KLIPS_PRINT(1, "klips_info:ipsec_alg_init: "
++ "KLIPS alg v=%d.%d.%d-%d (EALG_MAX=%d, AALG_MAX=%d)\n",
++ IPSEC_ALG_VERSION_QUAD(IPSEC_ALG_VERSION),
++ SADB_EALG_MAX, SADB_AALG_MAX);
++ /* Initialize tables */
++ write_lock_bh(&ipsec_alg_lock);
++ ipsec_alg_hash_init();
++ write_unlock_bh(&ipsec_alg_lock);
++
++ /* Initialize static algos */
++ KLIPS_PRINT(1, "klips_info:ipsec_alg_init: "
++ "calling ipsec_alg_static_init()\n");
++
++ /* If we are suppose to use our AES, and don't have CryptoAPI enabled... */
++#if defined(CONFIG_IPSEC_ENC_AES) && CONFIG_IPSEC_ENC_AES && !defined(CONFIG_IPSEC_ENC_AES_MODULE) && !CONFIG_IPSEC_ENC_CRYPTOAPI && !defined(CONFIG_IPSEC_ENC_CRYPTOAPI_MODULE)
++ {
++ extern int ipsec_aes_init(void);
++ ipsec_aes_init();
++ }
++#endif
++
++ /* If we are doing CryptoAPI, then init */
++#if defined(CONFIG_IPSEC_ENC_CRYPTOAPI) && CONFIG_IPSEC_ENC_CRYPTOAPI && !defined(CONFIG_IPSEC_ENC_CRYPTOAPI_MODULE)
++ {
++ extern int ipsec_cryptoapi_init(void);
++ ipsec_cryptoapi_init();
++ }
++#endif
++
++
++ return 0;
++}
++
++/**********************************************
++ *
++ * INTERFACE for ipsec_sa init and wipe
++ *
++ **********************************************/
++
++/*
++ * Called from pluto -> pfkey_v2_parser.c:pfkey_ipsec_sa_init()
++ */
++int ipsec_alg_sa_init(struct ipsec_sa *sa_p) {
++ struct ipsec_alg_enc *ixt_e;
++ struct ipsec_alg_auth *ixt_a;
++
++ /* Only ESP for now ... */
++ if (sa_p->ips_said.proto != IPPROTO_ESP)
++ return -EPROTONOSUPPORT;
++ KLIPS_PRINT(debug_pfkey, "klips_debug: ipsec_alg_sa_init() :"
++ "entering for encalg=%d, authalg=%d\n",
++ sa_p->ips_encalg, sa_p->ips_authalg);
++ if ((ixt_e=(struct ipsec_alg_enc *)
++ ipsec_alg_get(IPSEC_ALG_TYPE_ENCRYPT, sa_p->ips_encalg))) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug: ipsec_alg_sa_init() :"
++ "found ipsec_alg (ixt_e=%p) for encalg=%d\n",
++ ixt_e, sa_p->ips_encalg);
++ sa_p->ips_alg_enc=ixt_e;
++ }
++ if ((ixt_a=(struct ipsec_alg_auth *)
++ ipsec_alg_get(IPSEC_ALG_TYPE_AUTH, sa_p->ips_authalg))) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug: ipsec_alg_sa_init() :"
++ "found ipsec_alg (ixt_a=%p) for auth=%d\n",
++ ixt_a, sa_p->ips_authalg);
++ sa_p->ips_alg_auth=ixt_a;
++ }
++ return 0;
++}
++
++/*
++ * Called from pluto -> ipsec_sa.c:ipsec_sa_delchain()
++ */
++int ipsec_alg_sa_wipe(struct ipsec_sa *sa_p) {
++ struct ipsec_alg *ixt;
++ if ((ixt=(struct ipsec_alg *)sa_p->ips_alg_enc)) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug: ipsec_alg_sa_wipe() :"
++ "unlinking for encalg=%d\n",
++ ixt->ixt_alg_id);
++ ipsec_alg_put(ixt);
++ }
++ if ((ixt=(struct ipsec_alg *)sa_p->ips_alg_auth)) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug: ipsec_alg_sa_wipe() :"
++ "unlinking for authalg=%d\n",
++ ixt->ixt_alg_id);
++ ipsec_alg_put(ixt);
++ }
++ return 0;
++}
++
++IPSEC_PROCFS_DEBUG_NO_STATIC
++int
++ipsec_xform_get_info(char *buffer,
++ char **start,
++ off_t offset,
++ int length IPSEC_PROC_LAST_ARG)
++{
++ int len = 0;
++ off_t begin = 0;
++ int i;
++ struct list_head *head;
++ struct ipsec_alg *ixt;
++
++ KLIPS_PRINT(debug_tunnel & DB_TN_PROCFS,
++ "klips_debug:ipsec_tncfg_get_info: "
++ "buffer=0p%p, *start=0p%p, offset=%d, length=%d\n",
++ buffer,
++ *start,
++ (int)offset,
++ length);
++
++ for(i = 0, head = ipsec_alg_hash_table; i< IPSEC_ALG_HASHSZ; i++, head++)
++ {
++ struct list_head *p;
++ for (p=head->next; p!=head; p=p->next)
++ {
++ ixt = list_entry(p, struct ipsec_alg, ixt_list);
++ len += ipsec_snprintf(buffer+len, length-len,
++ "VERSION=%d TYPE=%d ID=%d NAME=%s REFCNT=%d ",
++ ixt->ixt_version, ixt->ixt_alg_type, ixt->ixt_alg_id,
++ ixt->ixt_name, ixt->ixt_refcnt);
++
++ len += ipsec_snprintf(buffer+len, length-len,
++ "STATE=%08x BLOCKSIZE=%d IVLEN=%d KEYMINBITS=%d KEYMAXBITS=%d ",
++ ixt->ixt_state, ixt->ixt_blocksize,
++ ixt->ixt_ivlen, ixt->ixt_keyminbits, ixt->ixt_keymaxbits);
++
++ len += ipsec_snprintf(buffer+len, length-len,
++ "IVLEN=%d KEYMINBITS=%d KEYMAXBITS=%d ",
++ ixt->ixt_ivlen, ixt->ixt_keyminbits, ixt->ixt_keymaxbits);
++
++ switch(ixt->ixt_alg_type)
++ {
++ case IPSEC_ALG_TYPE_AUTH:
++ {
++ struct ipsec_alg_auth *auth = (struct ipsec_alg_auth *)ixt;
++
++ len += ipsec_snprintf(buffer+len, length-len,
++ "KEYLEN=%d CTXSIZE=%d AUTHLEN=%d ",
++ auth->ixt_a_keylen, auth->ixt_a_ctx_size,
++ auth->ixt_a_authlen);
++ break;
++ }
++ case IPSEC_ALG_TYPE_ENCRYPT:
++ {
++ struct ipsec_alg_enc *enc = (struct ipsec_alg_enc *)ixt;
++ len += ipsec_snprintf(buffer+len, length-len,
++ "KEYLEN=%d CTXSIZE=%d ",
++ enc->ixt_e_keylen, enc->ixt_e_ctx_size);
++
++ break;
++ }
++ }
++
++ len += ipsec_snprintf(buffer+len, length-len, "\n");
++ }
++ }
++
++ *start = buffer + (offset - begin); /* Start of wanted data */
++ len -= (offset - begin); /* Start slop */
++ if (len > length)
++ len = length;
++ return len;
++}
++
++
++/*
++ * As the author of this module, I ONLY ALLOW using it from
++ * GPL (or same LICENSE TERMS as kernel source) modules.
++ *
++ * In respect to hardware crypto engines this means:
++ * * Closed-source device drivers ARE NOT ALLOWED to use
++ * this interface.
++ * * Closed-source VHDL/Verilog firmware running on
++ * the crypto hardware device IS ALLOWED to use this interface
++ * via a GPL (or same LICENSE TERMS as kernel source) device driver.
++ * --Juan Jose Ciarlante 20/03/2002 (thanks RGB for the correct wording)
++ */
++
++/*
++ * These symbols can only be used from GPL modules
++ * for now, I'm disabling this because it creates false
++ * symbol problems for old modutils.
++ */
++
++/* #ifndef EXPORT_SYMBOL_GPL */
++#undef EXPORT_SYMBOL_GPL
++#define EXPORT_SYMBOL_GPL EXPORT_SYMBOL
++/* #endif */
++EXPORT_SYMBOL_GPL(register_ipsec_alg);
++EXPORT_SYMBOL_GPL(unregister_ipsec_alg);
++EXPORT_SYMBOL_GPL(ipsec_alg_test);
++#endif /* CONFIG_IPSEC_ALG */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/net/ipsec/ipsec_alg_aes.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,263 @@
++/*
++ * ipsec_alg AES cipher stubs
++ *
++ * Author: JuanJo Ciarlante <jjo-ipsec@mendoza.gov.ar>
++ *
++ * ipsec_alg_aes.c,v 1.1.2.1 2003/11/21 18:12:23 jjo Exp
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ * Fixes by:
++ * PK: Pawel Krawczyk <kravietz@aba.krakow.pl>
++ * Fixes list:
++ * PK: make XCBC comply with latest draft (keylength)
++ *
++ */
++#include <linux/config.h>
++#include <linux/version.h>
++
++/*
++ * special case: ipsec core modular with this static algo inside:
++ * must avoid MODULE magic for this file
++ */
++#if CONFIG_IPSEC_MODULE && CONFIG_IPSEC_ALG_AES
++#undef MODULE
++#endif
++
++#include <linux/module.h>
++#include <linux/init.h>
++
++#include <linux/kernel.h> /* printk() */
++#include <linux/errno.h> /* error codes */
++#include <linux/types.h> /* size_t */
++#include <linux/string.h>
++
++/* Check if __exit is defined, if not null it */
++#ifndef __exit
++#define __exit
++#endif
++
++/* Low freeswan header coupling */
++#include "openswan/ipsec_alg.h"
++#include "crypto/aes_cbc.h"
++
++#define CONFIG_IPSEC_ALG_AES_MAC 1
++
++#define AES_CONTEXT_T aes_context
++MODULE_AUTHOR("JuanJo Ciarlante <jjo-ipsec@mendoza.gov.ar>");
++static int debug=0;
++MODULE_PARM(debug, "i");
++static int test=0;
++MODULE_PARM(test, "i");
++static int excl=0;
++MODULE_PARM(excl, "i");
++static int keyminbits=0;
++MODULE_PARM(keyminbits, "i");
++static int keymaxbits=0;
++MODULE_PARM(keymaxbits, "i");
++
++#if CONFIG_IPSEC_ALG_AES_MAC
++#include "crypto/aes_xcbc_mac.h"
++
++/*
++ * Not IANA number yet (draft-ietf-ipsec-ciph-aes-xcbc-mac-00.txt).
++ * We use 9 for non-modular algorithm and none for modular, thus
++ * forcing user to specify one on module load. -kravietz
++ */
++#ifdef MODULE
++static int auth_id=0;
++#else
++static int auth_id=9;
++#endif
++MODULE_PARM(auth_id, "i");
++#endif
++
++#define ESP_AES 12 /* truely _constant_ :) */
++
++/* 128, 192 or 256 */
++#define ESP_AES_KEY_SZ_MIN 16 /* 128 bit secret key */
++#define ESP_AES_KEY_SZ_MAX 32 /* 256 bit secret key */
++#define ESP_AES_CBC_BLK_LEN 16 /* AES-CBC block size */
++
++/* Values according to draft-ietf-ipsec-ciph-aes-xcbc-mac-02.txt
++ * -kravietz
++ */
++#define ESP_AES_MAC_KEY_SZ 16 /* 128 bit MAC key */
++#define ESP_AES_MAC_BLK_LEN 16 /* 128 bit block */
++
++static int _aes_set_key(struct ipsec_alg_enc *alg, __u8 * key_e, const __u8 * key, size_t keysize) {
++ int ret;
++ AES_CONTEXT_T *ctx=(AES_CONTEXT_T*)key_e;
++ ret=AES_set_key(ctx, key, keysize)!=0? 0: -EINVAL;
++ if (debug > 0)
++ printk(KERN_DEBUG "klips_debug:_aes_set_key:"
++ "ret=%d key_e=%p key=%p keysize=%d\n",
++ ret, key_e, key, keysize);
++ return ret;
++}
++static int _aes_cbc_encrypt(struct ipsec_alg_enc *alg, __u8 * key_e, __u8 * in, int ilen, const __u8 * iv, int encrypt) {
++ AES_CONTEXT_T *ctx=(AES_CONTEXT_T*)key_e;
++ if (debug > 0)
++ printk(KERN_DEBUG "klips_debug:_aes_cbc_encrypt:"
++ "key_e=%p in=%p ilen=%d iv=%p encrypt=%d\n",
++ key_e, in, ilen, iv, encrypt);
++ return AES_cbc_encrypt(ctx, in, in, ilen, iv, encrypt);
++}
++#if CONFIG_IPSEC_ALG_AES_MAC
++static int _aes_mac_set_key(struct ipsec_alg_auth *alg, __u8 * key_a, const __u8 * key, int keylen) {
++ aes_context_mac *ctxm=(aes_context_mac *)key_a;
++ return AES_xcbc_mac_set_key(ctxm, key, keylen)? 0 : -EINVAL;
++}
++static int _aes_mac_hash(struct ipsec_alg_auth *alg, __u8 * key_a, const __u8 * dat, int len, __u8 * hash, int hashlen) {
++ int ret;
++ char hash_buf[16];
++ aes_context_mac *ctxm=(aes_context_mac *)key_a;
++ ret=AES_xcbc_mac_hash(ctxm, dat, len, hash_buf);
++ memcpy(hash, hash_buf, hashlen);
++ return ret;
++}
++static struct ipsec_alg_auth ipsec_alg_AES_MAC = {
++ ixt_version: IPSEC_ALG_VERSION,
++ ixt_module: THIS_MODULE,
++ ixt_refcnt: ATOMIC_INIT(0),
++ ixt_alg_type: IPSEC_ALG_TYPE_AUTH,
++ ixt_alg_id: 0,
++ ixt_name: "aes_mac",
++ ixt_blocksize: ESP_AES_MAC_BLK_LEN,
++ ixt_keyminbits: ESP_AES_MAC_KEY_SZ*8,
++ ixt_keymaxbits: ESP_AES_MAC_KEY_SZ*8,
++ ixt_a_keylen: ESP_AES_MAC_KEY_SZ,
++ ixt_a_ctx_size: sizeof(aes_context_mac),
++ ixt_a_hmac_set_key: _aes_mac_set_key,
++ ixt_a_hmac_hash:_aes_mac_hash,
++};
++#endif /* CONFIG_IPSEC_ALG_AES_MAC */
++static struct ipsec_alg_enc ipsec_alg_AES = {
++ ixt_version: IPSEC_ALG_VERSION,
++ ixt_module: THIS_MODULE,
++ ixt_refcnt: ATOMIC_INIT(0),
++ ixt_alg_type: IPSEC_ALG_TYPE_ENCRYPT,
++ ixt_alg_id: ESP_AES,
++ ixt_name: "aes",
++ ixt_blocksize: ESP_AES_CBC_BLK_LEN,
++ ixt_keyminbits: ESP_AES_KEY_SZ_MIN*8,
++ ixt_keymaxbits: ESP_AES_KEY_SZ_MAX*8,
++ ixt_e_keylen: ESP_AES_KEY_SZ_MAX,
++ ixt_e_ctx_size: sizeof(AES_CONTEXT_T),
++ ixt_e_set_key: _aes_set_key,
++ ixt_e_cbc_encrypt:_aes_cbc_encrypt,
++};
++
++#if defined(CONFIG_IPSEC_ENC_AES_MODULE)
++IPSEC_ALG_MODULE_INIT_MOD( ipsec_aes_init )
++#else
++IPSEC_ALG_MODULE_INIT_STATIC( ipsec_aes_init )
++#endif
++{
++ int ret, test_ret;
++
++ if (keyminbits)
++ ipsec_alg_AES.ixt_keyminbits=keyminbits;
++ if (keymaxbits) {
++ ipsec_alg_AES.ixt_keymaxbits=keymaxbits;
++ if (keymaxbits*8>ipsec_alg_AES.ixt_keymaxbits)
++ ipsec_alg_AES.ixt_e_keylen=keymaxbits*8;
++ }
++ if (excl) ipsec_alg_AES.ixt_state |= IPSEC_ALG_ST_EXCL;
++ ret=register_ipsec_alg_enc(&ipsec_alg_AES);
++ printk("ipsec_aes_init(alg_type=%d alg_id=%d name=%s): ret=%d\n",
++ ipsec_alg_AES.ixt_alg_type,
++ ipsec_alg_AES.ixt_alg_id,
++ ipsec_alg_AES.ixt_name,
++ ret);
++ if (ret==0 && test) {
++ test_ret=ipsec_alg_test(
++ ipsec_alg_AES.ixt_alg_type,
++ ipsec_alg_AES.ixt_alg_id,
++ test);
++ printk("ipsec_aes_init(alg_type=%d alg_id=%d): test_ret=%d\n",
++ ipsec_alg_AES.ixt_alg_type,
++ ipsec_alg_AES.ixt_alg_id,
++ test_ret);
++ }
++#if CONFIG_IPSEC_ALG_AES_MAC
++ if (auth_id!=0){
++ int ret;
++ ipsec_alg_AES_MAC.ixt_alg_id=auth_id;
++ ret=register_ipsec_alg_auth(&ipsec_alg_AES_MAC);
++ printk("ipsec_aes_init(alg_type=%d alg_id=%d name=%s): ret=%d\n",
++ ipsec_alg_AES_MAC.ixt_alg_type,
++ ipsec_alg_AES_MAC.ixt_alg_id,
++ ipsec_alg_AES_MAC.ixt_name,
++ ret);
++ if (ret==0 && test) {
++ test_ret=ipsec_alg_test(
++ ipsec_alg_AES_MAC.ixt_alg_type,
++ ipsec_alg_AES_MAC.ixt_alg_id,
++ test);
++ printk("ipsec_aes_init(alg_type=%d alg_id=%d): test_ret=%d\n",
++ ipsec_alg_AES_MAC.ixt_alg_type,
++ ipsec_alg_AES_MAC.ixt_alg_id,
++ test_ret);
++ }
++ } else {
++ printk(KERN_DEBUG "klips_debug: experimental ipsec_alg_AES_MAC not registered [Ok] (auth_id=%d)\n", auth_id);
++ }
++#endif /* CONFIG_IPSEC_ALG_AES_MAC */
++ return ret;
++}
++
++#if defined(CONFIG_IPSEC_ENC_AES_MODULE)
++IPSEC_ALG_MODULE_EXIT_MOD( ipsec_aes_fini )
++#else
++IPSEC_ALG_MODULE_EXIT_STATIC( ipsec_aes_fini )
++#endif
++{
++#if CONFIG_IPSEC_ALG_AES_MAC
++ if (auth_id) unregister_ipsec_alg_auth(&ipsec_alg_AES_MAC);
++#endif /* CONFIG_IPSEC_ALG_AES_MAC */
++ unregister_ipsec_alg_enc(&ipsec_alg_AES);
++ return;
++}
++#ifdef MODULE_LICENSE
++MODULE_LICENSE("GPL");
++#endif
++
++#if 0+NOT_YET
++#ifndef MODULE
++/*
++ * This is intended for static module setups, currently
++ * doesn't work for modular ipsec.o with static algos inside
++ */
++static int setup_keybits(const char *str)
++{
++ unsigned aux;
++ char *end;
++
++ aux = simple_strtoul(str,&end,0);
++ if (aux != 128 && aux != 192 && aux != 256)
++ return 0;
++ keyminbits = aux;
++
++ if (*end == 0 || *end != ',')
++ return 1;
++ str=end+1;
++ aux = simple_strtoul(str, NULL, 0);
++ if (aux != 128 && aux != 192 && aux != 256)
++ return 0;
++ if (aux >= keyminbits)
++ keymaxbits = aux;
++ return 1;
++}
++__setup("ipsec_aes_keybits=", setup_keybits);
++#endif
++#endif
++EXPORT_NO_SYMBOLS;
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/net/ipsec/ipsec_alg_cryptoapi.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,421 @@
++/*
++ * ipsec_alg to linux cryptoapi GLUE
++ *
++ * Authors: CODE.ar TEAM
++ * Harpo MAxx <harpo@linuxmendoza.org.ar>
++ * JuanJo Ciarlante <jjo-ipsec@mendoza.gov.ar>
++ * Luciano Ruete <docemeses@softhome.net>
++ *
++ * ipsec_alg_cryptoapi.c,v 1.1.2.1 2003/11/21 18:12:23 jjo Exp
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ * Example usage:
++ * modinfo -p ipsec_cryptoapi (quite useful info, including supported algos)
++ * modprobe ipsec_cryptoapi
++ * modprobe ipsec_cryptoapi test=1
++ * modprobe ipsec_cryptoapi excl=1 (exclusive cipher/algo)
++ * modprobe ipsec_cryptoapi noauto=1 aes=1 twofish=1 (only these ciphers)
++ * modprobe ipsec_cryptoapi aes=128,128 (force these keylens)
++ * modprobe ipsec_cryptoapi des_ede3=0 (everything but 3DES)
++ */
++#include <linux/config.h>
++#include <linux/version.h>
++
++/*
++ * special case: ipsec core modular with this static algo inside:
++ * must avoid MODULE magic for this file
++ */
++#if CONFIG_IPSEC_MODULE && CONFIG_IPSEC_ALG_CRYPTOAPI
++#undef MODULE
++#endif
++
++#include <linux/module.h>
++#include <linux/init.h>
++
++#include <linux/kernel.h> /* printk() */
++#include <linux/errno.h> /* error codes */
++#include <linux/types.h> /* size_t */
++#include <linux/string.h>
++
++/* Check if __exit is defined, if not null it */
++#ifndef __exit
++#define __exit
++#endif
++
++/* warn the innocent */
++#if !defined (CONFIG_CRYPTO) && !defined (CONFIG_CRYPTO_MODULE)
++#warning "No linux CryptoAPI found, install 2.4.22+ or 2.6.x"
++#define NO_CRYPTOAPI_SUPPORT
++#endif
++/* Low Openswan header coupling */
++#include "openswan/ipsec_alg.h"
++
++#include <linux/crypto.h>
++#ifdef CRYPTO_API_VERSION_CODE
++#warning "Old CryptoAPI is not supported. Only linux-2.4.22+ or linux-2.6.x are supported"
++#define NO_CRYPTOAPI_SUPPORT
++#endif
++
++#ifdef NO_CRYPTOAPI_SUPPORT
++#warning "Building an unusable module :P"
++/* Catch old CryptoAPI by not allowing module to load */
++IPSEC_ALG_MODULE_INIT_STATIC( ipsec_cryptoapi_init )
++{
++ printk(KERN_WARNING "ipsec_cryptoapi.o was not built on stock Linux CryptoAPI (2.4.22+ or 2.6.x), not loading.\n");
++ return -EINVAL;
++}
++#else
++#include <asm/scatterlist.h>
++#include <asm/pgtable.h>
++#include <linux/mm.h>
++
++#define CIPHERNAME_AES "aes"
++#define CIPHERNAME_3DES "des3_ede"
++#define CIPHERNAME_BLOWFISH "blowfish"
++#define CIPHERNAME_CAST "cast5"
++#define CIPHERNAME_SERPENT "serpent"
++#define CIPHERNAME_TWOFISH "twofish"
++
++#define ESP_3DES 3
++#define ESP_AES 12
++#define ESP_BLOWFISH 7 /* truely _constant_ :) */
++#define ESP_CAST 6 /* quite constant :) */
++#define ESP_SERPENT 252 /* from ipsec drafts */
++#define ESP_TWOFISH 253 /* from ipsec drafts */
++
++#define AH_MD5 2
++#define AH_SHA 3
++#define DIGESTNAME_MD5 "md5"
++#define DIGESTNAME_SHA1 "sha1"
++
++MODULE_AUTHOR("Juanjo Ciarlante, Harpo MAxx, Luciano Ruete");
++static int debug=0;
++MODULE_PARM(debug, "i");
++static int test=0;
++MODULE_PARM(test, "i");
++static int excl=0;
++MODULE_PARM(excl, "i");
++
++static int noauto = 0;
++MODULE_PARM(noauto,"i");
++MODULE_PARM_DESC(noauto, "Dont try all known algos, just setup enabled ones");
++
++static int des_ede3[] = {-1, -1};
++static int aes[] = {-1, -1};
++static int blowfish[] = {-1, -1};
++static int cast[] = {-1, -1};
++static int serpent[] = {-1, -1};
++static int twofish[] = {-1, -1};
++
++MODULE_PARM(des_ede3,"1-2i");
++MODULE_PARM(aes,"1-2i");
++MODULE_PARM(blowfish,"1-2i");
++MODULE_PARM(cast,"1-2i");
++MODULE_PARM(serpent,"1-2i");
++MODULE_PARM(twofish,"1-2i");
++MODULE_PARM_DESC(des_ede3, "0: disable | 1: force_enable | min,max: dontuse");
++MODULE_PARM_DESC(aes, "0: disable | 1: force_enable | min,max: keybitlens");
++MODULE_PARM_DESC(blowfish, "0: disable | 1: force_enable | min,max: keybitlens");
++MODULE_PARM_DESC(cast, "0: disable | 1: force_enable | min,max: keybitlens");
++MODULE_PARM_DESC(serpent, "0: disable | 1: force_enable | min,max: keybitlens");
++MODULE_PARM_DESC(twofish, "0: disable | 1: force_enable | min,max: keybitlens");
++
++struct ipsec_alg_capi_cipher {
++ const char *ciphername; /* cryptoapi's ciphername */
++ unsigned blocksize;
++ unsigned short minbits;
++ unsigned short maxbits;
++ int *parm; /* lkm param for this cipher */
++ struct ipsec_alg_enc alg; /* note it's not a pointer */
++};
++static struct ipsec_alg_capi_cipher alg_capi_carray[] = {
++ { CIPHERNAME_AES , 16, 128, 256, aes , { ixt_alg_id: ESP_AES, }},
++ { CIPHERNAME_TWOFISH , 16, 128, 256, twofish, { ixt_alg_id: ESP_TWOFISH, }},
++ { CIPHERNAME_SERPENT , 16, 128, 256, serpent, { ixt_alg_id: ESP_SERPENT, }},
++ { CIPHERNAME_CAST , 8, 128, 128, cast , { ixt_alg_id: ESP_CAST, }},
++ { CIPHERNAME_BLOWFISH , 8, 96, 448, blowfish,{ ixt_alg_id: ESP_BLOWFISH, }},
++ { CIPHERNAME_3DES , 8, 192, 192, des_ede3,{ ixt_alg_id: ESP_3DES, }},
++ { NULL, 0, 0, 0, NULL, {} }
++};
++#ifdef NOT_YET
++struct ipsec_alg_capi_digest {
++ const char *digestname; /* cryptoapi's digestname */
++ struct digest_implementation *di;
++ struct ipsec_alg_auth alg; /* note it's not a pointer */
++};
++static struct ipsec_alg_capi_cipher alg_capi_darray[] = {
++ { DIGESTNAME_MD5, NULL, { ixt_alg_id: AH_MD5, }},
++ { DIGESTNAME_SHA1, NULL, { ixt_alg_id: AH_SHA, }},
++ { NULL, NULL, {} }
++};
++#endif
++/*
++ * "generic" linux cryptoapi setup_cipher() function
++ */
++int setup_cipher(const char *ciphername)
++{
++ return crypto_alg_available(ciphername, 0);
++}
++
++/*
++ * setups ipsec_alg_capi_cipher "hyper" struct components, calling
++ * register_ipsec_alg for cointaned ipsec_alg object
++ */
++static void _capi_destroy_key (struct ipsec_alg_enc *alg, __u8 *key_e);
++static __u8 * _capi_new_key (struct ipsec_alg_enc *alg, const __u8 *key, size_t keylen);
++static int _capi_cbc_encrypt(struct ipsec_alg_enc *alg, __u8 * key_e, __u8 * in, int ilen, const __u8 * iv, int encrypt);
++
++static int
++setup_ipsec_alg_capi_cipher(struct ipsec_alg_capi_cipher *cptr)
++{
++ int ret;
++ cptr->alg.ixt_version = IPSEC_ALG_VERSION;
++ cptr->alg.ixt_module = THIS_MODULE;
++ atomic_set (& cptr->alg.ixt_refcnt, 0);
++ strncpy (cptr->alg.ixt_name , cptr->ciphername, sizeof (cptr->alg.ixt_name));
++
++ cptr->alg.ixt_blocksize=cptr->blocksize;
++ cptr->alg.ixt_keyminbits=cptr->minbits;
++ cptr->alg.ixt_keymaxbits=cptr->maxbits;
++ cptr->alg.ixt_state = 0;
++ if (excl) cptr->alg.ixt_state |= IPSEC_ALG_ST_EXCL;
++ cptr->alg.ixt_e_keylen=cptr->alg.ixt_keymaxbits/8;
++ cptr->alg.ixt_e_ctx_size = 0;
++ cptr->alg.ixt_alg_type = IPSEC_ALG_TYPE_ENCRYPT;
++ cptr->alg.ixt_e_new_key = _capi_new_key;
++ cptr->alg.ixt_e_destroy_key = _capi_destroy_key;
++ cptr->alg.ixt_e_cbc_encrypt = _capi_cbc_encrypt;
++ cptr->alg.ixt_data = cptr;
++
++ ret=register_ipsec_alg_enc(&cptr->alg);
++ printk("setup_ipsec_alg_capi_cipher(): "
++ "alg_type=%d alg_id=%d name=%s "
++ "keyminbits=%d keymaxbits=%d, ret=%d\n",
++ cptr->alg.ixt_alg_type,
++ cptr->alg.ixt_alg_id,
++ cptr->alg.ixt_name,
++ cptr->alg.ixt_keyminbits,
++ cptr->alg.ixt_keymaxbits,
++ ret);
++ return ret;
++}
++/*
++ * called in ipsec_sa_wipe() time, will destroy key contexts
++ * and do 1 unbind()
++ */
++static void
++_capi_destroy_key (struct ipsec_alg_enc *alg, __u8 *key_e)
++{
++ struct crypto_tfm *tfm=(struct crypto_tfm*)key_e;
++
++ if (debug > 0)
++ printk(KERN_DEBUG "klips_debug: _capi_destroy_key:"
++ "name=%s key_e=%p \n",
++ alg->ixt_name, key_e);
++ if (!key_e) {
++ printk(KERN_ERR "klips_debug: _capi_destroy_key:"
++ "name=%s NULL key_e!\n",
++ alg->ixt_name);
++ return;
++ }
++ crypto_free_tfm(tfm);
++}
++
++/*
++ * create new key context, need alg->ixt_data to know which
++ * (of many) cipher inside this module is the target
++ */
++static __u8 *
++_capi_new_key (struct ipsec_alg_enc *alg, const __u8 *key, size_t keylen)
++{
++ struct ipsec_alg_capi_cipher *cptr;
++ struct crypto_tfm *tfm=NULL;
++
++ cptr = alg->ixt_data;
++ if (!cptr) {
++ printk(KERN_ERR "_capi_new_key(): "
++ "NULL ixt_data (?!) for \"%s\" algo\n"
++ , alg->ixt_name);
++ goto err;
++ }
++ if (debug > 0)
++ printk(KERN_DEBUG "klips_debug:_capi_new_key:"
++ "name=%s cptr=%p key=%p keysize=%d\n",
++ alg->ixt_name, cptr, key, keylen);
++
++ /*
++ * alloc tfm
++ */
++ tfm = crypto_alloc_tfm(cptr->ciphername, CRYPTO_TFM_MODE_CBC);
++ if (!tfm) {
++ printk(KERN_ERR "_capi_new_key(): "
++ "NULL tfm for \"%s\" cryptoapi (\"%s\") algo\n"
++ , alg->ixt_name, cptr->ciphername);
++ goto err;
++ }
++ if (crypto_cipher_setkey(tfm, key, keylen) < 0) {
++ printk(KERN_ERR "_capi_new_key(): "
++ "failed new_key() for \"%s\" cryptoapi algo (keylen=%d)\n"
++ , alg->ixt_name, keylen);
++ crypto_free_tfm(tfm);
++ tfm=NULL;
++ }
++err:
++ if (debug > 0)
++ printk(KERN_DEBUG "klips_debug:_capi_new_key:"
++ "name=%s key=%p keylen=%d tfm=%p\n",
++ alg->ixt_name, key, keylen, tfm);
++ return (__u8 *) tfm;
++}
++/*
++ * core encryption function: will use cx->ci to call actual cipher's
++ * cbc function
++ */
++static int
++_capi_cbc_encrypt(struct ipsec_alg_enc *alg, __u8 * key_e, __u8 * in, int ilen, const __u8 * iv, int encrypt) {
++ int error =0;
++ struct crypto_tfm *tfm=(struct crypto_tfm *)key_e;
++ struct scatterlist sg = {
++ .page = virt_to_page(in),
++ .offset = (unsigned long)(in) % PAGE_SIZE,
++ .length=ilen,
++ };
++ if (debug > 1)
++ printk(KERN_DEBUG "klips_debug:_capi_cbc_encrypt:"
++ "key_e=%p "
++ "in=%p out=%p ilen=%d iv=%p encrypt=%d\n"
++ , key_e
++ , in, in, ilen, iv, encrypt);
++ crypto_cipher_set_iv(tfm, iv, crypto_tfm_alg_ivsize(tfm));
++ if (encrypt)
++ error = crypto_cipher_encrypt (tfm, &sg, &sg, ilen);
++ else
++ error = crypto_cipher_decrypt (tfm, &sg, &sg, ilen);
++ if (debug > 1)
++ printk(KERN_DEBUG "klips_debug:_capi_cbc_encrypt:"
++ "error=%d\n"
++ , error);
++ return (error<0)? error : ilen;
++}
++/*
++ * main initialization loop: for each cipher in list, do
++ * 1) setup cryptoapi cipher else continue
++ * 2) register ipsec_alg object
++ */
++static int
++setup_cipher_list (struct ipsec_alg_capi_cipher* clist)
++{
++ struct ipsec_alg_capi_cipher *cptr;
++ /* foreach cipher in list ... */
++ for (cptr=clist;cptr->ciphername;cptr++) {
++ /*
++ * see if cipher has been disabled (0) or
++ * if noauto set and not enabled (1)
++ */
++ if (cptr->parm[0] == 0 || (noauto && cptr->parm[0] < 0)) {
++ if (debug>0)
++ printk(KERN_INFO "setup_cipher_list(): "
++ "ciphername=%s skipped at user request: "
++ "noauto=%d parm[0]=%d parm[1]=%d\n"
++ , cptr->ciphername
++ , noauto
++ , cptr->parm[0]
++ , cptr->parm[1]);
++ continue;
++ }
++ /*
++ * use a local ci to avoid touching cptr->ci,
++ * if register ipsec_alg success then bind cipher
++ */
++ if( setup_cipher(cptr->ciphername) ) {
++ if (debug > 0)
++ printk(KERN_DEBUG "klips_debug:"
++ "setup_cipher_list():"
++ "ciphername=%s found\n"
++ , cptr->ciphername);
++ if (setup_ipsec_alg_capi_cipher(cptr) == 0) {
++
++
++ } else {
++ printk(KERN_ERR "klips_debug:"
++ "setup_cipher_list():"
++ "ciphername=%s failed ipsec_alg_register\n"
++ , cptr->ciphername);
++ }
++ } else {
++ if (debug>0)
++ printk(KERN_INFO "setup_cipher_list(): lookup for ciphername=%s: not found \n",
++ cptr->ciphername);
++ }
++ }
++ return 0;
++}
++/*
++ * deregister ipsec_alg objects and unbind ciphers
++ */
++static int
++unsetup_cipher_list (struct ipsec_alg_capi_cipher* clist)
++{
++ struct ipsec_alg_capi_cipher *cptr;
++ /* foreach cipher in list ... */
++ for (cptr=clist;cptr->ciphername;cptr++) {
++ if (cptr->alg.ixt_state & IPSEC_ALG_ST_REGISTERED) {
++ unregister_ipsec_alg_enc(&cptr->alg);
++ }
++ }
++ return 0;
++}
++/*
++ * test loop for registered algos
++ */
++static int
++test_cipher_list (struct ipsec_alg_capi_cipher* clist)
++{
++ int test_ret;
++ struct ipsec_alg_capi_cipher *cptr;
++ /* foreach cipher in list ... */
++ for (cptr=clist;cptr->ciphername;cptr++) {
++ if (cptr->alg.ixt_state & IPSEC_ALG_ST_REGISTERED) {
++ test_ret=ipsec_alg_test(
++ cptr->alg.ixt_alg_type,
++ cptr->alg.ixt_alg_id,
++ test);
++ printk("test_cipher_list(alg_type=%d alg_id=%d): test_ret=%d\n",
++ cptr->alg.ixt_alg_type,
++ cptr->alg.ixt_alg_id,
++ test_ret);
++ }
++ }
++ return 0;
++}
++
++IPSEC_ALG_MODULE_INIT_STATIC( ipsec_cryptoapi_init )
++{
++ int ret, test_ret;
++ if ((ret=setup_cipher_list(alg_capi_carray)) < 0)
++ return -EPROTONOSUPPORT;
++ if (ret==0 && test) {
++ test_ret=test_cipher_list(alg_capi_carray);
++ }
++ return ret;
++}
++IPSEC_ALG_MODULE_EXIT_STATIC( ipsec_cryptoapi_fini )
++{
++ unsetup_cipher_list(alg_capi_carray);
++ return;
++}
++#ifdef MODULE_LICENSE
++MODULE_LICENSE("GPL");
++#endif
++
++EXPORT_NO_SYMBOLS;
++#endif /* NO_CRYPTOAPI_SUPPORT */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/net/ipsec/ipsec_esp.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,558 @@
++/*
++ * processing code for ESP
++ * Copyright (C) 2003 Michael Richardson <mcr@sandelman.ottawa.on.ca>
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ */
++
++char ipsec_esp_c_version[] = "RCSID $Id$";
++#include <linux/config.h>
++#include <linux/version.h>
++
++#define __NO_VERSION__
++#include <linux/module.h>
++#include <linux/kernel.h> /* printk() */
++
++#include "openswan/ipsec_param.h"
++
++#ifdef MALLOC_SLAB
++# include <linux/slab.h> /* kmalloc() */
++#else /* MALLOC_SLAB */
++# include <linux/malloc.h> /* kmalloc() */
++#endif /* MALLOC_SLAB */
++#include <linux/errno.h> /* error codes */
++#include <linux/types.h> /* size_t */
++#include <linux/interrupt.h> /* mark_bh */
++
++#include <linux/netdevice.h> /* struct device, and other headers */
++#include <linux/etherdevice.h> /* eth_type_trans */
++#include <linux/ip.h> /* struct iphdr */
++#include <linux/skbuff.h>
++#include <openswan.h>
++#ifdef SPINLOCK
++# ifdef SPINLOCK_23
++# include <linux/spinlock.h> /* *lock* */
++# else /* SPINLOCK_23 */
++# include <asm/spinlock.h> /* *lock* */
++# endif /* SPINLOCK_23 */
++#endif /* SPINLOCK */
++#ifdef NET_21
++# include <asm/uaccess.h>
++# include <linux/in6.h>
++# define proto_priv cb
++#endif /* NET21 */
++#include <asm/checksum.h>
++#include <net/ip.h>
++
++#include "openswan/radij.h"
++#include "openswan/ipsec_encap.h"
++#include "openswan/ipsec_sa.h"
++
++#include "openswan/ipsec_radij.h"
++#include "openswan/ipsec_xform.h"
++#include "openswan/ipsec_tunnel.h"
++#include "openswan/ipsec_rcv.h"
++#include "openswan/ipsec_xmit.h"
++
++#include "openswan/ipsec_auth.h"
++
++#ifdef CONFIG_IPSEC_ESP
++#include "openswan/ipsec_esp.h"
++#endif /* CONFIG_IPSEC_ESP */
++
++#include "openswan/ipsec_proto.h"
++#include "openswan/ipsec_alg.h"
++
++#ifdef CONFIG_IPSEC_DEBUG
++int debug_esp = 0;
++#endif /* CONFIG_IPSEC_DEBUG */
++
++
++#ifdef CONFIG_IPSEC_ESP
++enum ipsec_rcv_value
++ipsec_rcv_esp_checks(struct ipsec_rcv_state *irs,
++ struct sk_buff *skb)
++{
++ __u8 proto;
++ int len; /* packet length */
++
++ len = skb->len;
++ proto = irs->ipp->protocol;
++
++ /* XXX this will need to be 8 for IPv6 */
++ if ((proto == IPPROTO_ESP) && ((len - irs->iphlen) % 4)) {
++ printk("klips_error:ipsec_rcv: "
++ "got packet with content length = %d from %s -- should be on 4 octet boundary, packet dropped\n",
++ len - irs->iphlen,
++ irs->ipsaddr_txt);
++ if(irs->stats) {
++ irs->stats->rx_errors++;
++ }
++ return IPSEC_RCV_BADLEN;
++ }
++
++ if(skb->len < (irs->hard_header_len + sizeof(struct iphdr) + sizeof(struct esphdr))) {
++ KLIPS_PRINT(debug_rcv & DB_RX_INAU,
++ "klips_debug:ipsec_rcv: "
++ "runt esp packet of skb->len=%d received from %s, dropped.\n",
++ skb->len,
++ irs->ipsaddr_txt);
++ if(irs->stats) {
++ irs->stats->rx_errors++;
++ }
++ return IPSEC_RCV_BADLEN;
++ }
++
++ irs->protostuff.espstuff.espp = (struct esphdr *)(skb->data + irs->iphlen);
++ irs->said.spi = irs->protostuff.espstuff.espp->esp_spi;
++
++ return IPSEC_RCV_OK;
++}
++
++enum ipsec_rcv_value
++ipsec_rcv_esp_decrypt_setup(struct ipsec_rcv_state *irs,
++ struct sk_buff *skb,
++ __u32 *replay,
++ unsigned char **authenticator)
++{
++ struct esphdr *espp = irs->protostuff.espstuff.espp;
++
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "packet from %s received with seq=%d (iv)=0x%08x%08x iplen=%d esplen=%d sa=%s\n",
++ irs->ipsaddr_txt,
++ (__u32)ntohl(espp->esp_rpl),
++ (__u32)ntohl(*((__u32 *)(espp->esp_iv) )),
++ (__u32)ntohl(*((__u32 *)(espp->esp_iv) + 1)),
++ irs->len,
++ irs->ilen,
++ irs->sa_len ? irs->sa : " (error)");
++
++ *replay = ntohl(espp->esp_rpl);
++ *authenticator = &(skb->data[irs->len - irs->authlen]);
++
++ return IPSEC_RCV_OK;
++}
++
++enum ipsec_rcv_value
++ipsec_rcv_esp_authcalc(struct ipsec_rcv_state *irs,
++ struct sk_buff *skb)
++{
++ struct auth_alg *aa;
++ struct esphdr *espp = irs->protostuff.espstuff.espp;
++ union {
++ MD5_CTX md5;
++ SHA1_CTX sha1;
++ } tctx;
++
++#ifdef CONFIG_IPSEC_ALG
++ if (irs->ipsp->ips_alg_auth) {
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "ipsec_alg hashing proto=%d... ",
++ irs->said.proto);
++ if(irs->said.proto == IPPROTO_ESP) {
++ ipsec_alg_sa_esp_hash(irs->ipsp,
++ (caddr_t)espp, irs->ilen,
++ irs->hash, AHHMAC_HASHLEN);
++ return IPSEC_RCV_OK;
++ }
++ return IPSEC_RCV_BADPROTO;
++ }
++#endif
++ aa = irs->authfuncs;
++
++ /* copy the initialized keying material */
++ memcpy(&tctx, irs->ictx, irs->ictx_len);
++
++ (*aa->update)((void *)&tctx, (caddr_t)espp, irs->ilen);
++
++ (*aa->final)(irs->hash, (void *)&tctx);
++
++ memcpy(&tctx, irs->octx, irs->octx_len);
++
++ (*aa->update)((void *)&tctx, irs->hash, aa->hashlen);
++ (*aa->final)(irs->hash, (void *)&tctx);
++
++ return IPSEC_RCV_OK;
++}
++
++
++enum ipsec_rcv_value
++ipsec_rcv_esp_decrypt(struct ipsec_rcv_state *irs)
++{
++ struct ipsec_sa *ipsp = irs->ipsp;
++ struct esphdr *espp = irs->protostuff.espstuff.espp;
++ int esphlen = 0;
++ __u8 *idat; /* pointer to content to be decrypted/authenticated */
++ __u32 iv[2];
++ int pad = 0, padlen;
++ int badpad = 0;
++ int i;
++ struct sk_buff *skb;
++#ifdef CONFIG_IPSEC_ALG
++ struct ipsec_alg_enc *ixt_e=NULL;
++#endif /* CONFIG_IPSEC_ALG */
++
++ skb=irs->skb;
++
++ idat = skb->data + irs->iphlen;
++
++#ifdef CONFIG_IPSEC_ALG
++ if ((ixt_e=ipsp->ips_alg_enc)) {
++ esphlen = ESP_HEADER_LEN + ixt_e->ixt_ivlen/8;
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "encalg=%d esphlen=%d\n",
++ ipsp->ips_encalg, esphlen);
++ } else
++#endif /* CONFIG_IPSEC_ALG */
++ switch(ipsp->ips_encalg) {
++ case ESP_3DES:
++ iv[0] = *((__u32 *)(espp->esp_iv) );
++ iv[1] = *((__u32 *)(espp->esp_iv) + 1);
++ esphlen = sizeof(struct esphdr);
++ break;
++ default:
++ ipsp->ips_errs.ips_alg_errs += 1;
++ if(irs->stats) {
++ irs->stats->rx_errors++;
++ }
++ return IPSEC_RCV_ESP_BADALG;
++ }
++
++ idat += esphlen;
++ irs->ilen -= esphlen;
++
++#ifdef CONFIG_IPSEC_ALG
++ if (ixt_e)
++ {
++ if (ipsec_alg_esp_encrypt(ipsp,
++ idat, irs->ilen, espp->esp_iv,
++ IPSEC_ALG_DECRYPT) <= 0)
++ {
++ printk("klips_error:ipsec_rcv: "
++ "got packet with esplen = %d "
++ "from %s -- should be on "
++ "ENC(%d) octet boundary, "
++ "packet dropped\n",
++ irs->ilen,
++ irs->ipsaddr_txt,
++ ipsp->ips_encalg);
++ if(irs->stats) {
++ irs->stats->rx_errors++;
++ }
++ return IPSEC_RCV_BAD_DECRYPT;
++ }
++ } else
++#endif /* CONFIG_IPSEC_ALG */
++ switch(ipsp->ips_encalg) {
++ case ESP_3DES:
++ if ((irs->ilen) % 8) {
++ ipsp->ips_errs.ips_encsize_errs += 1;
++ printk("klips_error:ipsec_rcv: "
++ "got packet with esplen = %d from %s -- should be on 8 octet boundary, packet dropped\n",
++ irs->ilen,
++ irs->ipsaddr_txt);
++ if(irs->stats) {
++ irs->stats->rx_errors++;
++ }
++ return IPSEC_RCV_3DES_BADBLOCKING;
++ }
++ des_ede3_cbc_encrypt((des_cblock *)idat,
++ (des_cblock *)idat,
++ irs->ilen,
++ ((struct des_eks *)(ipsp->ips_key_e))[0].ks,
++ ((struct des_eks *)(ipsp->ips_key_e))[1].ks,
++ ((struct des_eks *)(ipsp->ips_key_e))[2].ks,
++ (des_cblock *)iv, 0);
++ break;
++ }
++
++ ipsec_rcv_dmp("postdecrypt", skb->data, skb->len);
++
++ irs->next_header = idat[irs->ilen - 1];
++ padlen = idat[irs->ilen - 2];
++ pad = padlen + 2 + irs->authlen;
++
++ KLIPS_PRINT(debug_rcv & DB_RX_IPAD,
++ "klips_debug:ipsec_rcv: "
++ "padlen=%d, contents: 0x<offset>: 0x<value> 0x<value> ...\n",
++ padlen);
++
++ for (i = 1; i <= padlen; i++) {
++ if((i % 16) == 1) {
++ KLIPS_PRINT(debug_rcv & DB_RX_IPAD,
++ "klips_debug: %02x:",
++ i - 1);
++ }
++ KLIPS_PRINTMORE(debug_rcv & DB_RX_IPAD,
++ " %02x",
++ idat[irs->ilen - 2 - padlen + i - 1]);
++ if(i != idat[irs->ilen - 2 - padlen + i - 1]) {
++ badpad = 1;
++ }
++ if((i % 16) == 0) {
++ KLIPS_PRINTMORE(debug_rcv & DB_RX_IPAD,
++ "\n");
++ }
++ }
++ if((i % 16) != 1) {
++ KLIPS_PRINTMORE(debug_rcv & DB_RX_IPAD,
++ "\n");
++ }
++ if(badpad) {
++ KLIPS_PRINT(debug_rcv & DB_RX_IPAD,
++ "klips_debug:ipsec_rcv: "
++ "warning, decrypted packet from %s has bad padding\n",
++ irs->ipsaddr_txt);
++ KLIPS_PRINT(debug_rcv & DB_RX_IPAD,
++ "klips_debug:ipsec_rcv: "
++ "...may be bad decryption -- not dropped\n");
++ ipsp->ips_errs.ips_encpad_errs += 1;
++ }
++
++ KLIPS_PRINT(debug_rcv & DB_RX_IPAD,
++ "klips_debug:ipsec_rcv: "
++ "packet decrypted from %s: next_header = %d, padding = %d\n",
++ irs->ipsaddr_txt,
++ irs->next_header,
++ pad - 2 - irs->authlen);
++
++ irs->ipp->tot_len = htons(ntohs(irs->ipp->tot_len) - (esphlen + pad));
++
++ /*
++ * move the IP header forward by the size of the ESP header, which
++ * will remove the the ESP header from the packet.
++ */
++ memmove((void *)(skb->data + esphlen),
++ (void *)(skb->data), irs->iphlen);
++
++ ipsec_rcv_dmp("esp postmove", skb->data, skb->len);
++
++ /* skb_pull below, will move up by esphlen */
++
++ /* XXX not clear how this can happen, as the message indicates */
++ if(skb->len < esphlen) {
++ printk(KERN_WARNING
++ "klips_error:ipsec_rcv: "
++ "tried to skb_pull esphlen=%d, %d available. This should never happen, please report.\n",
++ esphlen, (int)(skb->len));
++ return IPSEC_RCV_ESP_DECAPFAIL;
++ }
++ skb_pull(skb, esphlen);
++
++ irs->ipp = (struct iphdr *)skb->data;
++
++ ipsec_rcv_dmp("esp postpull", skb->data, skb->len);
++
++ /* now, trip off the padding from the end */
++ KLIPS_PRINT(debug_rcv & DB_RX_PKTRX,
++ "klips_debug:ipsec_rcv: "
++ "trimming to %d.\n",
++ irs->len - esphlen - pad);
++ if(pad + esphlen <= irs->len) {
++ skb_trim(skb, irs->len - esphlen - pad);
++ } else {
++ KLIPS_PRINT(debug_rcv & DB_RX_PKTRX,
++ "klips_debug:ipsec_rcv: "
++ "bogus packet, size is zero or negative, dropping.\n");
++ return IPSEC_RCV_DECAPFAIL;
++ }
++
++ return IPSEC_RCV_OK;
++}
++
++enum ipsec_xmit_value
++ipsec_xmit_esp_setup(struct ipsec_xmit_state *ixs)
++{
++ __u32 iv[2];
++ struct esphdr *espp;
++ int ilen = 0;
++ int padlen = 0, i;
++ unsigned char *dat;
++ unsigned char *idat, *pad;
++ __u8 hash[AH_AMAX];
++ union {
++#ifdef CONFIG_IPSEC_AUTH_HMAC_MD5
++ MD5_CTX md5;
++#endif /* CONFIG_IPSEC_AUTH_HMAC_MD5 */
++#ifdef CONFIG_IPSEC_AUTH_HMAC_SHA1
++ SHA1_CTX sha1;
++#endif /* CONFIG_IPSEC_AUTH_HMAC_SHA1 */
++ } tctx;
++
++ dat = (unsigned char *)ixs->iph;
++
++ espp = (struct esphdr *)(dat + ixs->iphlen);
++ espp->esp_spi = ixs->ipsp->ips_said.spi;
++ espp->esp_rpl = htonl(++(ixs->ipsp->ips_replaywin_lastseq));
++
++ switch(ixs->ipsp->ips_encalg) {
++#if defined(CONFIG_IPSEC_ENC_3DES)
++#ifdef CONFIG_IPSEC_ENC_3DES
++ case ESP_3DES:
++#endif /* CONFIG_IPSEC_ENC_3DES */
++ iv[0] = *((__u32*)&(espp->esp_iv) ) =
++ ((__u32*)(ixs->ipsp->ips_iv))[0];
++ iv[1] = *((__u32*)&(espp->esp_iv) + 1) =
++ ((__u32*)(ixs->ipsp->ips_iv))[1];
++ break;
++#endif /* defined(CONFIG_IPSEC_ENC_3DES) */
++ default:
++ ixs->stats->tx_errors++;
++ return IPSEC_XMIT_ESP_BADALG;
++ }
++
++ idat = dat + ixs->iphlen + sizeof(struct esphdr);
++ ilen = ixs->skb->len - (ixs->iphlen + sizeof(struct esphdr) + ixs->authlen);
++
++ /* Self-describing padding */
++ pad = &dat[ixs->skb->len - ixs->tailroom];
++ padlen = ixs->tailroom - 2 - ixs->authlen;
++ for (i = 0; i < padlen; i++) {
++ pad[i] = i + 1;
++ }
++ dat[ixs->skb->len - ixs->authlen - 2] = padlen;
++
++ dat[ixs->skb->len - ixs->authlen - 1] = ixs->iph->protocol;
++ ixs->iph->protocol = IPPROTO_ESP;
++
++ switch(ixs->ipsp->ips_encalg) {
++#ifdef CONFIG_IPSEC_ENC_3DES
++ case ESP_3DES:
++ des_ede3_cbc_encrypt((des_cblock *)idat,
++ (des_cblock *)idat,
++ ilen,
++ ((struct des_eks *)(ixs->ipsp->ips_key_e))[0].ks,
++ ((struct des_eks *)(ixs->ipsp->ips_key_e))[1].ks,
++ ((struct des_eks *)(ixs->ipsp->ips_key_e))[2].ks,
++ (des_cblock *)iv, 1);
++ break;
++#endif /* CONFIG_IPSEC_ENC_3DES */
++ default:
++ ixs->stats->tx_errors++;
++ return IPSEC_XMIT_ESP_BADALG;
++ }
++
++ switch(ixs->ipsp->ips_encalg) {
++#if defined(CONFIG_IPSEC_ENC_3DES)
++#ifdef CONFIG_IPSEC_ENC_3DES
++ case ESP_3DES:
++#endif /* CONFIG_IPSEC_ENC_3DES */
++ /* XXX update IV with the last 8 octets of the encryption */
++#if KLIPS_IMPAIRMENT_ESPIV_CBC_ATTACK
++ ((__u32*)(ixs->ipsp->ips_iv))[0] =
++ ((__u32 *)(idat))[(ilen >> 2) - 2];
++ ((__u32*)(ixs->ipsp->ips_iv))[1] =
++ ((__u32 *)(idat))[(ilen >> 2) - 1];
++#else /* KLIPS_IMPAIRMENT_ESPIV_CBC_ATTACK */
++ prng_bytes(&ipsec_prng, (char *)ixs->ipsp->ips_iv, EMT_ESPDES_IV_SZ);
++#endif /* KLIPS_IMPAIRMENT_ESPIV_CBC_ATTACK */
++ break;
++#endif /* defined(CONFIG_IPSEC_ENC_3DES) */
++ default:
++ ixs->stats->tx_errors++;
++ return IPSEC_XMIT_ESP_BADALG;
++ }
++
++ switch(ixs->ipsp->ips_authalg) {
++#ifdef CONFIG_IPSEC_AUTH_HMAC_MD5
++ case AH_MD5:
++ ipsec_xmit_dmp("espp", (char*)espp, ixs->skb->len - ixs->iphlen - ixs->authlen);
++ tctx.md5 = ((struct md5_ctx*)(ixs->ipsp->ips_key_a))->ictx;
++ ipsec_xmit_dmp("ictx", (char*)&tctx.md5, sizeof(tctx.md5));
++ MD5Update(&tctx.md5, (caddr_t)espp, ixs->skb->len - ixs->iphlen - ixs->authlen);
++ ipsec_xmit_dmp("ictx+dat", (char*)&tctx.md5, sizeof(tctx.md5));
++ MD5Final(hash, &tctx.md5);
++ ipsec_xmit_dmp("ictx hash", (char*)&hash, sizeof(hash));
++ tctx.md5 = ((struct md5_ctx*)(ixs->ipsp->ips_key_a))->octx;
++ ipsec_xmit_dmp("octx", (char*)&tctx.md5, sizeof(tctx.md5));
++ MD5Update(&tctx.md5, hash, AHMD596_ALEN);
++ ipsec_xmit_dmp("octx+hash", (char*)&tctx.md5, sizeof(tctx.md5));
++ MD5Final(hash, &tctx.md5);
++ ipsec_xmit_dmp("octx hash", (char*)&hash, sizeof(hash));
++ memcpy(&(dat[ixs->skb->len - ixs->authlen]), hash, ixs->authlen);
++
++ /* paranoid */
++ memset((caddr_t)&tctx.md5, 0, sizeof(tctx.md5));
++ memset((caddr_t)hash, 0, sizeof(*hash));
++ break;
++#endif /* CONFIG_IPSEC_AUTH_HMAC_MD5 */
++#ifdef CONFIG_IPSEC_AUTH_HMAC_SHA1
++ case AH_SHA:
++ tctx.sha1 = ((struct sha1_ctx*)(ixs->ipsp->ips_key_a))->ictx;
++ SHA1Update(&tctx.sha1, (caddr_t)espp, ixs->skb->len - ixs->iphlen - ixs->authlen);
++ SHA1Final(hash, &tctx.sha1);
++ tctx.sha1 = ((struct sha1_ctx*)(ixs->ipsp->ips_key_a))->octx;
++ SHA1Update(&tctx.sha1, hash, AHSHA196_ALEN);
++ SHA1Final(hash, &tctx.sha1);
++ memcpy(&(dat[ixs->skb->len - ixs->authlen]), hash, ixs->authlen);
++
++ /* paranoid */
++ memset((caddr_t)&tctx.sha1, 0, sizeof(tctx.sha1));
++ memset((caddr_t)hash, 0, sizeof(*hash));
++ break;
++#endif /* CONFIG_IPSEC_AUTH_HMAC_SHA1 */
++ case AH_NONE:
++ break;
++ default:
++ ixs->stats->tx_errors++;
++ return IPSEC_XMIT_AH_BADALG;
++ }
++#ifdef NET_21
++ ixs->skb->h.raw = (unsigned char*)espp;
++#endif /* NET_21 */
++
++ return IPSEC_XMIT_OK;
++}
++
++
++struct xform_functions esp_xform_funcs[]={
++ { rcv_checks: ipsec_rcv_esp_checks,
++ rcv_setup_auth: ipsec_rcv_esp_decrypt_setup,
++ rcv_calc_auth: ipsec_rcv_esp_authcalc,
++ rcv_decrypt: ipsec_rcv_esp_decrypt,
++
++ xmit_setup: ipsec_xmit_esp_setup,
++ xmit_headroom: sizeof(struct esphdr),
++ xmit_needtailroom: 1,
++ },
++};
++
++struct inet_protocol esp_protocol =
++{
++ ipsec_rcv, /* ESP handler */
++ NULL, /* TUNNEL error control */
++#ifdef NETDEV_25
++ 1, /* no policy */
++#else
++ 0, /* next */
++ IPPROTO_ESP, /* protocol ID */
++ 0, /* copy */
++ NULL, /* data */
++ "ESP" /* name */
++#endif
++};
++
++
++
++#endif /* !CONFIG_IPSEC_ESP */
++
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.2 2004/04/06 02:49:25 mcr
++ * pullup of algo code from alg-branch.
++ *
++ *
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/net/ipsec/ipsec_init.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,531 @@
++/*
++ * @(#) Initialization code.
++ * Copyright (C) 1996, 1997 John Ioannidis.
++ * Copyright (C) 1998 - 2002 Richard Guy Briggs <rgb@freeswan.org>
++ * 2001 - 2004 Michael Richardson <mcr@xelerance.com>
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ * /proc system code was split out into ipsec_proc.c after rev. 1.70.
++ *
++ */
++
++char ipsec_init_c_version[] = "RCSID $Id$";
++
++#include <linux/config.h>
++#include <linux/version.h>
++#include <linux/module.h>
++#include <linux/kernel.h> /* printk() */
++
++#include "openswan/ipsec_param.h"
++
++#ifdef MALLOC_SLAB
++# include <linux/slab.h> /* kmalloc() */
++#else /* MALLOC_SLAB */
++# include <linux/malloc.h> /* kmalloc() */
++#endif /* MALLOC_SLAB */
++#include <linux/errno.h> /* error codes */
++#include <linux/types.h> /* size_t */
++#include <linux/interrupt.h> /* mark_bh */
++
++#include <linux/netdevice.h> /* struct device, and other headers */
++#include <linux/etherdevice.h> /* eth_type_trans */
++#include <linux/ip.h> /* struct iphdr */
++#include <linux/in.h> /* struct sockaddr_in */
++#include <linux/skbuff.h>
++#include <linux/random.h> /* get_random_bytes() */
++#include <openswan.h>
++
++#ifdef SPINLOCK
++# ifdef SPINLOCK_23
++# include <linux/spinlock.h> /* *lock* */
++# else /* 23_SPINLOCK */
++# include <asm/spinlock.h> /* *lock* */
++# endif /* 23_SPINLOCK */
++#endif /* SPINLOCK */
++
++#ifdef NET_21
++# include <asm/uaccess.h>
++# include <linux/in6.h>
++#endif /* NET_21 */
++
++#include <asm/checksum.h>
++#include <net/ip.h>
++
++#ifdef CONFIG_PROC_FS
++# include <linux/proc_fs.h>
++#endif /* CONFIG_PROC_FS */
++
++#ifdef NETLINK_SOCK
++# include <linux/netlink.h>
++#else
++# include <net/netlink.h>
++#endif
++
++#include "openswan/radij.h"
++
++#include "openswan/ipsec_life.h"
++#include "openswan/ipsec_stats.h"
++#include "openswan/ipsec_sa.h"
++
++#include "openswan/ipsec_encap.h"
++#include "openswan/ipsec_radij.h"
++#include "openswan/ipsec_xform.h"
++#include "openswan/ipsec_tunnel.h"
++
++#include "openswan/ipsec_rcv.h"
++#include "openswan/ipsec_ah.h"
++#include "openswan/ipsec_esp.h"
++
++#ifdef CONFIG_IPSEC_IPCOMP
++# include "openswan/ipcomp.h"
++#endif /* CONFIG_IPSEC_IPCOMP */
++
++#include "openswan/ipsec_proto.h"
++#include "openswan/ipsec_alg.h"
++
++#include <pfkeyv2.h>
++#include <pfkey.h>
++
++#if !defined(CONFIG_IPSEC_ESP) && !defined(CONFIG_IPSEC_AH)
++#error "kernel configuration must include ESP or AH"
++#endif
++
++/*
++ * seems to be present in 2.4.10 (Linus), but also in some RH and other
++ * distro kernels of a lower number.
++ */
++#ifdef MODULE_LICENSE
++MODULE_LICENSE("GPL");
++#endif
++
++#ifdef CONFIG_IPSEC_DEBUG
++int debug_eroute = 0;
++int debug_spi = 0;
++int debug_netlink = 0;
++#endif /* CONFIG_IPSEC_DEBUG */
++
++struct prng ipsec_prng;
++
++extern int ipsec_device_event(struct notifier_block *dnot, unsigned long event, void *ptr);
++/*
++ * the following structure is required so that we receive
++ * event notifications when network devices are enabled and
++ * disabled (ifconfig up and down).
++ */
++static struct notifier_block ipsec_dev_notifier={
++ ipsec_device_event,
++ NULL,
++ 0
++};
++
++#ifdef CONFIG_SYSCTL
++extern int ipsec_sysctl_register(void);
++extern void ipsec_sysctl_unregister(void);
++#endif
++
++static inline int
++openswan_inet_add_protocol(struct inet_protocol *prot, unsigned protocol)
++{
++#ifdef NETDEV_25
++ return inet_add_protocol(prot, protocol);
++#else
++ inet_add_protocol(prot);
++ return 0;
++#endif
++}
++
++static inline int
++openswan_inet_del_protocol(struct inet_protocol *prot, unsigned protocol)
++{
++#ifdef NETDEV_25
++ return inet_del_protocol(prot, protocol);
++#else
++ inet_del_protocol(prot);
++ return 0;
++#endif
++}
++
++/* void */
++int
++ipsec_init(void)
++{
++ int error = 0;
++ unsigned char seed[256];
++#ifdef CONFIG_IPSEC_ENC_3DES
++ extern int des_check_key;
++
++ /* turn off checking of keys */
++ des_check_key=0;
++#endif /* CONFIG_IPSEC_ENC_3DES */
++
++ KLIPS_PRINT(1, "klips_info:ipsec_init: "
++ "KLIPS startup, Openswan KLIPS IPsec stack version: %s\n",
++ ipsec_version_code());
++
++ error |= ipsec_proc_init();
++
++#ifdef SPINLOCK
++ ipsec_sadb.sadb_lock = SPIN_LOCK_UNLOCKED;
++#else /* SPINLOCK */
++ ipsec_sadb.sadb_lock = 0;
++#endif /* SPINLOCK */
++
++#ifndef SPINLOCK
++ tdb_lock.lock = 0;
++ eroute_lock.lock = 0;
++#endif /* !SPINLOCK */
++
++ error |= ipsec_sadb_init();
++ error |= ipsec_radijinit();
++
++ error |= pfkey_init();
++
++ error |= register_netdevice_notifier(&ipsec_dev_notifier);
++
++#ifdef CONFIG_IPSEC_ESP
++ openswan_inet_add_protocol(&esp_protocol, IPPROTO_ESP);
++#endif /* CONFIG_IPSEC_ESP */
++
++#ifdef CONFIG_IPSEC_AH
++ openswan_inet_add_protocol(&ah_protocol, IPPROTO_AH);
++#endif /* CONFIG_IPSEC_AH */
++
++/* we never actually link IPCOMP to the stack */
++#ifdef IPCOMP_USED_ALONE
++#ifdef CONFIG_IPSEC_IPCOMP
++ openswan_inet_add_protocol(&comp_protocol, IPPROTO_COMP);
++#endif /* CONFIG_IPSEC_IPCOMP */
++#endif
++
++ error |= ipsec_tunnel_init_devices();
++
++
++#ifdef CONFIG_SYSCTL
++ error |= ipsec_sysctl_register();
++#endif
++
++#ifdef CONFIG_IPSEC_ALG
++ ipsec_alg_init();
++#endif
++
++ get_random_bytes((void *)seed, sizeof(seed));
++ prng_init(&ipsec_prng, seed, sizeof(seed));
++
++ return error;
++}
++
++
++/* void */
++int
++ipsec_cleanup(void)
++{
++ int error = 0;
++
++#ifdef CONFIG_SYSCTL
++ ipsec_sysctl_unregister();
++#endif
++ KLIPS_PRINT(debug_netlink, /* debug_tunnel & DB_TN_INIT, */
++ "klips_debug:ipsec_cleanup: "
++ "calling ipsec_tunnel_cleanup_devices.\n");
++ error |= ipsec_tunnel_cleanup_devices();
++
++ KLIPS_PRINT(debug_netlink, "called ipsec_tunnel_cleanup_devices");
++
++/* we never actually link IPCOMP to the stack */
++#ifdef IPCOMP_USED_ALONE
++#ifdef CONFIG_IPSEC_IPCOMP
++ if (openswan_inet_del_protocol(&comp_protocol, IPPROTO_COMP) < 0)
++ printk(KERN_INFO "klips_debug:ipsec_cleanup: "
++ "comp close: can't remove protocol\n");
++#endif /* CONFIG_IPSEC_IPCOMP */
++#endif /* IPCOMP_USED_ALONE */
++
++#ifdef CONFIG_IPSEC_AH
++ if (openswan_inet_del_protocol(&ah_protocol, IPPROTO_AH) < 0)
++ printk(KERN_INFO "klips_debug:ipsec_cleanup: "
++ "ah close: can't remove protocol\n");
++#endif /* CONFIG_IPSEC_AH */
++
++#ifdef CONFIG_IPSEC_ESP
++ if (openswan_inet_del_protocol(&esp_protocol, IPPROTO_ESP) < 0)
++ printk(KERN_INFO "klips_debug:ipsec_cleanup: "
++ "esp close: can't remove protocol\n");
++#endif /* CONFIG_IPSEC_ESP */
++
++ error |= unregister_netdevice_notifier(&ipsec_dev_notifier);
++
++ KLIPS_PRINT(debug_netlink, /* debug_tunnel & DB_TN_INIT, */
++ "klips_debug:ipsec_cleanup: "
++ "calling ipsec_sadb_cleanup.\n");
++ error |= ipsec_sadb_cleanup(0);
++ error |= ipsec_sadb_free();
++
++ KLIPS_PRINT(debug_netlink, /* debug_tunnel & DB_TN_INIT, */
++ "klips_debug:ipsec_cleanup: "
++ "calling ipsec_radijcleanup.\n");
++ error |= ipsec_radijcleanup();
++
++ KLIPS_PRINT(debug_pfkey, /* debug_tunnel & DB_TN_INIT, */
++ "klips_debug:ipsec_cleanup: "
++ "calling pfkey_cleanup.\n");
++ error |= pfkey_cleanup();
++
++ ipsec_proc_cleanup();
++
++ prng_final(&ipsec_prng);
++
++ return error;
++}
++
++#ifdef MODULE
++int
++init_module(void)
++{
++ int error = 0;
++
++ error |= ipsec_init();
++
++ return error;
++}
++
++int
++cleanup_module(void)
++{
++ int error = 0;
++
++ KLIPS_PRINT(debug_netlink, /* debug_tunnel & DB_TN_INIT, */
++ "klips_debug:cleanup_module: "
++ "calling ipsec_cleanup.\n");
++
++ error |= ipsec_cleanup();
++
++ KLIPS_PRINT(1, "klips_info:cleanup_module: "
++ "ipsec module unloaded.\n");
++
++ return error;
++}
++#endif /* MODULE */
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.93 2004/04/06 02:49:26 mcr
++ * pullup of algo code from alg-branch.
++ *
++ * Revision 1.92 2004/03/30 15:30:39 ken
++ * Proper Capitalization
++ *
++ * Revision 1.91 2004/03/22 01:51:51 ken
++ * We are open
++ *
++ * Revision 1.90.4.2 2004/04/05 04:30:46 mcr
++ * patches for alg-branch to compile/work with 2.x openswan
++ *
++ * Revision 1.90.4.1 2003/12/22 15:25:52 jjo
++ * Merged algo-0.8.1-rc11-test1 into alg-branch
++ *
++ * Revision 1.90 2003/10/31 02:27:55 mcr
++ * pulled up port-selector patches and sa_id elimination.
++ *
++ * Revision 1.89.4.1 2003/10/29 01:30:41 mcr
++ * elimited "struct sa_id".
++ *
++ * Revision 1.89 2003/07/31 22:47:16 mcr
++ * preliminary (untested by FS-team) 2.5 patches.
++ *
++ * Revision 1.88 2003/06/22 20:05:36 mcr
++ * clarified why IPCOMP was not being registered, and put a new
++ * #ifdef in rather than #if 0.
++ *
++ * Revision 1.87 2002/09/20 15:40:51 rgb
++ * Added a lock to the global ipsec_sadb struct for future use.
++ * Split ipsec_sadb_cleanup from new funciton ipsec_sadb_free to avoid problem
++ * of freeing newly created structures when clearing the reftable upon startup
++ * to start from a known state.
++ *
++ * Revision 1.86 2002/08/15 18:39:15 rgb
++ * Move ipsec_prng outside debug code.
++ *
++ * Revision 1.85 2002/05/14 02:35:29 rgb
++ * Change reference to tdb to ipsa.
++ *
++ * Revision 1.84 2002/04/24 07:55:32 mcr
++ * #include patches and Makefiles for post-reorg compilation.
++ *
++ * Revision 1.83 2002/04/24 07:36:28 mcr
++ * Moved from ./klips/net/ipsec/ipsec_init.c,v
++ *
++ * Revision 1.82 2002/04/20 00:12:25 rgb
++ * Added esp IV CBC attack fix, disabled.
++ *
++ * Revision 1.81 2002/04/09 16:13:32 mcr
++ * switch license to straight GPL.
++ *
++ * Revision 1.80 2002/03/24 07:34:08 rgb
++ * Sanity check for at least one of AH or ESP configured.
++ *
++ * Revision 1.79 2002/02/05 22:55:15 mcr
++ * added MODULE_LICENSE declaration.
++ * This macro does not appear in all kernel versions (see comment).
++ *
++ * Revision 1.78 2002/01/29 17:17:55 mcr
++ * moved include of ipsec_param.h to after include of linux/kernel.h
++ * otherwise, it seems that some option that is set in ipsec_param.h
++ * screws up something subtle in the include path to kernel.h, and
++ * it complains on the snprintf() prototype.
++ *
++ * Revision 1.77 2002/01/29 04:00:51 mcr
++ * more excise of kversions.h header.
++ *
++ * Revision 1.76 2002/01/29 02:13:17 mcr
++ * introduction of ipsec_kversion.h means that include of
++ * ipsec_param.h must preceed any decisions about what files to
++ * include to deal with differences in kernel source.
++ *
++ * Revision 1.75 2001/11/26 09:23:48 rgb
++ * Merge MCR's ipsec_sa, eroute, proc and struct lifetime changes.
++ *
++ * Revision 1.74 2001/11/22 05:44:11 henry
++ * new version stuff
++ *
++ * Revision 1.71.2.2 2001/10/22 20:51:00 mcr
++ * explicitely set des_check_key.
++ *
++ * Revision 1.71.2.1 2001/09/25 02:19:39 mcr
++ * /proc manipulation code moved to new ipsec_proc.c
++ *
++ * Revision 1.73 2001/11/06 19:47:17 rgb
++ * Changed lifetime_packets to uint32 from uint64.
++ *
++ * Revision 1.72 2001/10/18 04:45:19 rgb
++ * 2.4.9 kernel deprecates linux/malloc.h in favour of linux/slab.h,
++ * lib/freeswan.h version macros moved to lib/kversions.h.
++ * Other compiler directive cleanups.
++ *
++ * Revision 1.71 2001/09/20 15:32:45 rgb
++ * Minor pfkey lifetime fixes.
++ *
++ * Revision 1.70 2001/07/06 19:51:21 rgb
++ * Added inbound policy checking code for IPIP SAs.
++ *
++ * Revision 1.69 2001/06/14 19:33:26 rgb
++ * Silence startup message for console, but allow it to be logged.
++ * Update copyright date.
++ *
++ * Revision 1.68 2001/05/29 05:14:36 rgb
++ * Added PMTU to /proc/net/ipsec_tncfg output. See 'man 5 ipsec_tncfg'.
++ *
++ * Revision 1.67 2001/05/04 16:34:52 rgb
++ * Rremove erroneous checking of return codes for proc_net_* in 2.4.
++ *
++ * Revision 1.66 2001/05/03 19:40:34 rgb
++ * Check error return codes in startup and shutdown.
++ *
++ * Revision 1.65 2001/02/28 05:03:27 rgb
++ * Clean up and rationalise startup messages.
++ *
++ * Revision 1.64 2001/02/27 22:24:53 rgb
++ * Re-formatting debug output (line-splitting, joining, 1arg/line).
++ * Check for satoa() return codes.
++ *
++ * Revision 1.63 2000/11/29 20:14:06 rgb
++ * Add src= to the output of /proc/net/ipsec_spi and delete dst from IPIP.
++ *
++ * Revision 1.62 2000/11/06 04:31:24 rgb
++ * Ditched spin_lock_irqsave in favour of spin_lock_bh.
++ * Fixed longlong for pre-2.4 kernels (Svenning).
++ * Add Svenning's adaptive content compression.
++ * Disabled registration of ipcomp handler.
++ *
++ * Revision 1.61 2000/10/11 13:37:54 rgb
++ * #ifdef out debug print that causes proc/net/ipsec_version to oops.
++ *
++ * Revision 1.60 2000/09/20 03:59:01 rgb
++ * Change static info functions to DEBUG_NO_STATIC to reveal function names
++ * in oopsen.
++ *
++ * Revision 1.59 2000/09/16 01:06:26 rgb
++ * Added cast of var to silence compiler warning about long fed to int
++ * format.
++ *
++ * Revision 1.58 2000/09/15 11:37:01 rgb
++ * Merge in heavily modified Svenning Soerensen's <svenning@post5.tele.dk>
++ * IPCOMP zlib deflate code.
++ *
++ * Revision 1.57 2000/09/12 03:21:50 rgb
++ * Moved radij_c_version printing to ipsec_version_get_info().
++ * Reformatted ipsec_version_get_info().
++ * Added sysctl_{,un}register() calls.
++ *
++ * Revision 1.56 2000/09/08 19:16:50 rgb
++ * Change references from DEBUG_IPSEC to CONFIG_IPSEC_DEBUG.
++ * Removed all references to CONFIG_IPSEC_PFKEYv2.
++ *
++ * Revision 1.55 2000/08/30 05:19:03 rgb
++ * Cleaned up no longer used spi_next, netlink register/unregister, other
++ * minor cleanup.
++ * Removed cruft replaced by TDB_XFORM_NAME.
++ * Removed all the rest of the references to tdb_spi, tdb_proto, tdb_dst.
++ * Moved debug version strings to printk when /proc/net/ipsec_version is
++ * called.
++ *
++ * Revision 1.54 2000/08/20 18:31:05 rgb
++ * Changed cosmetic alignment in spi_info.
++ * Changed addtime and usetime to use actual value which is relative
++ * anyways, as intended. (Momchil)
++ *
++ * Revision 1.53 2000/08/18 17:37:03 rgb
++ * Added an (int) cast to shut up the compiler...
++ *
++ * Revision 1.52 2000/08/01 14:51:50 rgb
++ * Removed _all_ remaining traces of DES.
++ *
++ * Revision 1.51 2000/07/25 20:41:22 rgb
++ * Removed duplicate parameter in spi_getinfo.
++ *
++ * Revision 1.50 2000/07/17 03:21:45 rgb
++ * Removed /proc/net/ipsec_spinew.
++ *
++ * Revision 1.49 2000/06/28 05:46:51 rgb
++ * Renamed ivlen to iv_bits for consistency.
++ * Changed output of add and use times to be relative to now.
++ *
++ * Revision 1.48 2000/05/11 18:26:10 rgb
++ * Commented out calls to netlink_attach/detach to avoid activating netlink
++ * in the kenrel config.
++ *
++ * Revision 1.47 2000/05/10 22:35:26 rgb
++ * Comment out most of the startup version information.
++ *
++ * Revision 1.46 2000/03/22 16:15:36 rgb
++ * Fixed renaming of dev_get (MB).
++ *
++ * Revision 1.45 2000/03/16 06:40:48 rgb
++ * Hardcode PF_KEYv2 support.
++ *
++ * Revision 1.44 2000/01/22 23:19:20 rgb
++ * Simplified code to use existing macro TDB_XFORM_NAME().
++ *
++ * Revision 1.43 2000/01/21 06:14:04 rgb
++ * Print individual stats only if non-zero.
++ * Removed 'bits' from each keylength for brevity.
++ * Shortened lifetimes legend for brevity.
++ * Changed wording from 'last_used' to the clearer 'idle'.
++ *
++ * Revision 1.42 1999/12/31 14:57:19 rgb
++ * MB fix for new dummy-less proc_get_info in 2.3.35.
++ *
++ *
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/net/ipsec/ipsec_ipcomp.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,276 @@
++/*
++ * processing code for IPCOMP
++ * Copyright (C) 2003 Michael Richardson <mcr@sandelman.ottawa.on.ca>
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ */
++
++char ipsec_ipcomp_c_version[] = "RCSID $Id$";
++#include <linux/config.h>
++#include <linux/version.h>
++
++#define __NO_VERSION__
++#include <linux/module.h>
++#include <linux/kernel.h> /* printk() */
++
++#include "openswan/ipsec_param.h"
++
++#ifdef MALLOC_SLAB
++# include <linux/slab.h> /* kmalloc() */
++#else /* MALLOC_SLAB */
++# include <linux/malloc.h> /* kmalloc() */
++#endif /* MALLOC_SLAB */
++#include <linux/errno.h> /* error codes */
++#include <linux/types.h> /* size_t */
++#include <linux/interrupt.h> /* mark_bh */
++
++#include <linux/netdevice.h> /* struct device, and other headers */
++#include <linux/etherdevice.h> /* eth_type_trans */
++#include <linux/ip.h> /* struct iphdr */
++#include <linux/skbuff.h>
++#include <openswan.h>
++#ifdef SPINLOCK
++# ifdef SPINLOCK_23
++# include <linux/spinlock.h> /* *lock* */
++# else /* SPINLOCK_23 */
++# include <asm/spinlock.h> /* *lock* */
++# endif /* SPINLOCK_23 */
++#endif /* SPINLOCK */
++#ifdef NET_21
++# include <asm/uaccess.h>
++# include <linux/in6.h>
++# define proto_priv cb
++#endif /* NET21 */
++#include <asm/checksum.h>
++#include <net/ip.h>
++
++#include "openswan/radij.h"
++#include "openswan/ipsec_encap.h"
++#include "openswan/ipsec_sa.h"
++
++#include "openswan/ipsec_radij.h"
++#include "openswan/ipsec_xform.h"
++#include "openswan/ipsec_tunnel.h"
++#include "openswan/ipsec_rcv.h"
++#include "openswan/ipsec_xmit.h"
++
++#include "openswan/ipsec_auth.h"
++
++#ifdef CONFIG_IPSEC_IPCOMP
++#include "openswan/ipsec_ipcomp.h"
++#endif /* CONFIG_IPSEC_IPCOMP */
++
++#include "openswan/ipsec_proto.h"
++
++#ifdef CONFIG_IPSEC_DEBUG
++int debug_ipcomp = 0;
++#endif /* CONFIG_IPSEC_DEBUG */
++
++
++#ifdef CONFIG_IPSEC_IPCOMP
++enum ipsec_rcv_value
++ipsec_rcv_ipcomp_checks(struct ipsec_rcv_state *irs,
++ struct sk_buff *skb)
++{
++ int ipcompminlen;
++
++ ipcompminlen = irs->hard_header_len + sizeof(struct iphdr);
++
++ if(skb->len < (ipcompminlen + sizeof(struct ipcomphdr))) {
++ KLIPS_PRINT(debug_rcv & DB_RX_INAU,
++ "klips_debug:ipsec_rcv: "
++ "runt comp packet of skb->len=%d received from %s, dropped.\n",
++ skb->len,
++ irs->ipsaddr_txt);
++ if(irs->stats) {
++ irs->stats->rx_errors++;
++ }
++ return IPSEC_RCV_BADLEN;
++ }
++
++ irs->protostuff.ipcompstuff.compp = (struct ipcomphdr *)(skb->data + irs->iphlen);
++ irs->said.spi = htonl((__u32)ntohs(irs->protostuff.ipcompstuff.compp->ipcomp_cpi));
++ return IPSEC_RCV_OK;
++}
++
++enum ipsec_rcv_value
++ipsec_rcv_ipcomp_decomp(struct ipsec_rcv_state *irs)
++{
++ unsigned int flags = 0;
++ struct ipsec_sa *ipsp = irs->ipsp;
++ struct sk_buff *skb;
++
++ skb=irs->skb;
++
++ ipsec_xmit_dmp("ipcomp", skb->data, skb->len);
++
++ if(ipsp == NULL) {
++ return IPSEC_RCV_SAIDNOTFOUND;
++ }
++
++#if 0
++ /* we want to check that this wasn't the first SA on the list, because
++ * we don't support bare IPCOMP, for unexplained reasons. MCR
++ */
++ if (ipsp->ips_onext != NULL) {
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "Incoming packet with outer IPCOMP header SA:%s: not yet supported by KLIPS, dropped\n",
++ irs->sa_len ? irs->sa : " (error)");
++ if(irs->stats) {
++ irs->stats->rx_dropped++;
++ }
++
++ return IPSEC_RCV_IPCOMPALONE;
++ }
++#endif
++
++ if(sysctl_ipsec_inbound_policy_check &&
++ ((((ntohl(ipsp->ips_said.spi) & 0x0000ffff) != ntohl(irs->said.spi)) &&
++ (ipsp->ips_encalg != ntohl(irs->said.spi)) /* this is a workaround for peer non-compliance with rfc2393 */
++ ))) {
++ char sa2[SATOT_BUF];
++ size_t sa_len2 = 0;
++
++ sa_len2 = satot(&ipsp->ips_said, 0, sa2, sizeof(sa2));
++
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "Incoming packet with SA(IPCA):%s does not match policy SA(IPCA):%s cpi=%04x cpi->spi=%08x spi=%08x, spi->cpi=%04x for SA grouping, dropped.\n",
++ irs->sa_len ? irs->sa : " (error)",
++ ipsp != NULL ? (sa_len2 ? sa2 : " (error)") : "NULL",
++ ntohs(irs->protostuff.ipcompstuff.compp->ipcomp_cpi),
++ (__u32)ntohl(irs->said.spi),
++ ipsp != NULL ? (__u32)ntohl((ipsp->ips_said.spi)) : 0,
++ ipsp != NULL ? (__u16)(ntohl(ipsp->ips_said.spi) & 0x0000ffff) : 0);
++ if(irs->stats) {
++ irs->stats->rx_dropped++;
++ }
++ return IPSEC_RCV_SAIDNOTFOUND;
++ }
++
++ ipsp->ips_comp_ratio_cbytes += ntohs(irs->ipp->tot_len);
++ irs->next_header = irs->protostuff.ipcompstuff.compp->ipcomp_nh;
++
++ skb = skb_decompress(skb, ipsp, &flags);
++ if (!skb || flags) {
++ spin_unlock(&tdb_lock);
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "skb_decompress() returned error flags=%x, dropped.\n",
++ flags);
++ if (irs->stats) {
++ if (flags)
++ irs->stats->rx_errors++;
++ else
++ irs->stats->rx_dropped++;
++ }
++ return IPSEC_RCV_IPCOMPFAILED;
++ }
++
++ /* make sure we update the pointer */
++ irs->skb = skb;
++
++#ifdef NET_21
++ irs->ipp = skb->nh.iph;
++#else /* NET_21 */
++ irs->ipp = skb->ip_hdr;
++#endif /* NET_21 */
++
++ ipsp->ips_comp_ratio_dbytes += ntohs(irs->ipp->tot_len);
++
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "packet decompressed SA(IPCA):%s cpi->spi=%08x spi=%08x, spi->cpi=%04x, nh=%d.\n",
++ irs->sa_len ? irs->sa : " (error)",
++ (__u32)ntohl(irs->said.spi),
++ ipsp != NULL ? (__u32)ntohl((ipsp->ips_said.spi)) : 0,
++ ipsp != NULL ? (__u16)(ntohl(ipsp->ips_said.spi) & 0x0000ffff) : 0,
++ irs->next_header);
++ KLIPS_IP_PRINT(debug_rcv & DB_RX_PKTRX, irs->ipp);
++
++ return IPSEC_RCV_OK;
++}
++
++enum ipsec_xmit_value
++ipsec_xmit_ipcomp_setup(struct ipsec_xmit_state *ixs)
++{
++ unsigned int flags = 0;
++#ifdef CONFIG_IPSEC_DEBUG
++ unsigned int old_tot_len = ntohs(ixs->iph->tot_len);
++#endif /* CONFIG_IPSEC_DEBUG */
++
++ ixs->ipsp->ips_comp_ratio_dbytes += ntohs(ixs->iph->tot_len);
++
++ ixs->skb = skb_compress(ixs->skb, ixs->ipsp, &flags);
++
++#ifdef NET_21
++ ixs->iph = ixs->skb->nh.iph;
++#else /* NET_21 */
++ ixs->iph = ixs->skb->ip_hdr;
++#endif /* NET_21 */
++
++ ixs->ipsp->ips_comp_ratio_cbytes += ntohs(ixs->iph->tot_len);
++
++#ifdef CONFIG_IPSEC_DEBUG
++ if (debug_tunnel & DB_TN_CROUT)
++ {
++ if (old_tot_len > ntohs(ixs->iph->tot_len))
++ KLIPS_PRINT(debug_tunnel & DB_TN_CROUT,
++ "klips_debug:ipsec_xmit_encap_once: "
++ "packet shrunk from %d to %d bytes after compression, cpi=%04x (should be from spi=%08x, spi&0xffff=%04x.\n",
++ old_tot_len, ntohs(ixs->iph->tot_len),
++ ntohs(((struct ipcomphdr*)(((char*)ixs->iph) + ((ixs->iph->ihl) << 2)))->ipcomp_cpi),
++ ntohl(ixs->ipsp->ips_said.spi),
++ (__u16)(ntohl(ixs->ipsp->ips_said.spi) & 0x0000ffff));
++ else
++ KLIPS_PRINT(debug_tunnel & DB_TN_CROUT,
++ "klips_debug:ipsec_xmit_encap_once: "
++ "packet did not compress (flags = %d).\n",
++ flags);
++ }
++#endif /* CONFIG_IPSEC_DEBUG */
++
++ return IPSEC_XMIT_OK;
++}
++
++struct xform_functions ipcomp_xform_funcs[]={
++ {rcv_checks: ipsec_rcv_ipcomp_checks,
++ rcv_decrypt: ipsec_rcv_ipcomp_decomp,
++ xmit_setup: ipsec_xmit_ipcomp_setup,
++ xmit_headroom: 0,
++ xmit_needtailroom: 0,
++ },
++};
++
++#if 0
++/* We probably don't want to install a pure IPCOMP protocol handler, but
++ only want to handle IPCOMP if it is encapsulated inside an ESP payload
++ (which is already handled) */
++#ifdef CONFIG_IPSEC_IPCOMP
++struct inet_protocol comp_protocol =
++{
++ ipsec_rcv, /* COMP handler */
++ NULL, /* COMP error control */
++#ifdef NETDEV_25
++ 1, /* no policy */
++#else
++ 0, /* next */
++ IPPROTO_COMP, /* protocol ID */
++ 0, /* copy */
++ NULL, /* data */
++ "COMP" /* name */
++#endif
++};
++#endif /* CONFIG_IPSEC_IPCOMP */
++#endif
++
++#endif /* CONFIG_IPSEC_IPCOMP */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/net/ipsec/ipsec_ipip.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,133 @@
++/*
++ * processing code for IPIP
++ * Copyright (C) 2003 Michael Richardson <mcr@sandelman.ottawa.on.ca>
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ */
++
++char ipsec_ipip_c_version[] = "RCSID $Id$";
++#include <linux/config.h>
++#include <linux/version.h>
++
++#define __NO_VERSION__
++#include <linux/module.h>
++#include <linux/kernel.h> /* printk() */
++
++#include "openswan/ipsec_param.h"
++
++#ifdef MALLOC_SLAB
++# include <linux/slab.h> /* kmalloc() */
++#else /* MALLOC_SLAB */
++# include <linux/malloc.h> /* kmalloc() */
++#endif /* MALLOC_SLAB */
++#include <linux/errno.h> /* error codes */
++#include <linux/types.h> /* size_t */
++#include <linux/interrupt.h> /* mark_bh */
++
++#include <linux/netdevice.h> /* struct device, and other headers */
++#include <linux/etherdevice.h> /* eth_type_trans */
++#include <linux/ip.h> /* struct iphdr */
++#include <linux/skbuff.h>
++#include <openswan.h>
++#ifdef SPINLOCK
++# ifdef SPINLOCK_23
++# include <linux/spinlock.h> /* *lock* */
++# else /* SPINLOCK_23 */
++# include <asm/spinlock.h> /* *lock* */
++# endif /* SPINLOCK_23 */
++#endif /* SPINLOCK */
++#ifdef NET_21
++# include <asm/uaccess.h>
++# include <linux/in6.h>
++# define proto_priv cb
++#endif /* NET21 */
++#include <asm/checksum.h>
++#include <net/ip.h>
++
++#include "openswan/radij.h"
++#include "openswan/ipsec_encap.h"
++#include "openswan/ipsec_sa.h"
++
++#include "openswan/ipsec_radij.h"
++#include "openswan/ipsec_xform.h"
++#include "openswan/ipsec_tunnel.h"
++#include "openswan/ipsec_rcv.h"
++#include "openswan/ipsec_xmit.h"
++
++#include "openswan/ipsec_auth.h"
++#include "openswan/ipsec_ipip.h"
++#include "openswan/ipsec_param.h"
++
++#include "openswan/ipsec_proto.h"
++
++enum ipsec_xmit_value
++ipsec_xmit_ipip_setup(struct ipsec_xmit_state *ixs)
++{
++ ixs->iph->version = 4;
++
++ switch(sysctl_ipsec_tos) {
++ case 0:
++#ifdef NET_21
++ ixs->iph->tos = ixs->skb->nh.iph->tos;
++#else /* NET_21 */
++ ixs->iph->tos = ixs->skb->ip_hdr->tos;
++#endif /* NET_21 */
++ break;
++ case 1:
++ ixs->iph->tos = 0;
++ break;
++ default:
++ break;
++ }
++#ifdef NET_21
++#ifdef NETDEV_23
++ ixs->iph->ttl = sysctl_ip_default_ttl;
++#else /* NETDEV_23 */
++ ixs->iph->ttl = ip_statistics.IpDefaultTTL;
++#endif /* NETDEV_23 */
++#else /* NET_21 */
++ ixs->iph->ttl = 64; /* ip_statistics.IpDefaultTTL; */
++#endif /* NET_21 */
++ ixs->iph->frag_off = 0;
++ ixs->iph->saddr = ((struct sockaddr_in*)(ixs->ipsp->ips_addr_s))->sin_addr.s_addr;
++ ixs->iph->daddr = ((struct sockaddr_in*)(ixs->ipsp->ips_addr_d))->sin_addr.s_addr;
++ ixs->iph->protocol = IPPROTO_IPIP;
++ ixs->iph->ihl = sizeof(struct iphdr) >> 2;
++
++ KLIPS_IP_SELECT_IDENT(ixs->iph, ixs->skb);
++
++ ixs->newdst = (__u32)ixs->iph->daddr;
++ ixs->newsrc = (__u32)ixs->iph->saddr;
++
++#ifdef NET_21
++ ixs->skb->h.ipiph = ixs->skb->nh.iph;
++#endif /* NET_21 */
++ return IPSEC_XMIT_OK;
++}
++
++struct xform_functions ipip_xform_funcs[]={
++ { rcv_checks: NULL,
++ rcv_setup_auth: NULL,
++ rcv_calc_auth: NULL,
++ rcv_decrypt: NULL,
++
++ xmit_setup: ipsec_xmit_ipip_setup,
++ xmit_headroom: sizeof(struct iphdr),
++ xmit_needtailroom: 0,
++ },
++};
++
++
++
++
++
++
++
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/net/ipsec/ipsec_life.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,263 @@
++/*
++ * @(#) lifetime structure utilities
++ *
++ * Copyright (C) 2001 Richard Guy Briggs <rgb@freeswan.org>
++ * and Michael Richardson <mcr@freeswan.org>
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ * RCSID $Id$
++ *
++ */
++
++/*
++ * This provides series of utility functions for dealing with lifetime
++ * structures.
++ *
++ * ipsec_check_lifetime - returns -1 hard lifetime exceeded
++ * 0 soft lifetime exceeded
++ * 1 everything is okay
++ * based upon whether or not the count exceeds hard/soft
++ *
++ */
++
++#define __NO_VERSION__
++#include <linux/module.h>
++#include <linux/config.h> /* for CONFIG_IP_FORWARD */
++#include <linux/version.h>
++#include <linux/kernel.h> /* printk() */
++
++#include "openswan/ipsec_param.h"
++
++#include <linux/netdevice.h> /* struct device, struct net_device_stats and other headers */
++#include <linux/etherdevice.h> /* eth_type_trans */
++#include <linux/skbuff.h>
++#include <openswan.h>
++
++#include "openswan/radij.h"
++#include "openswan/ipsec_life.h"
++#include "openswan/ipsec_xform.h"
++#include "openswan/ipsec_eroute.h"
++#include "openswan/ipsec_encap.h"
++#include "openswan/ipsec_radij.h"
++
++#include "openswan/ipsec_sa.h"
++#include "openswan/ipsec_tunnel.h"
++#include "openswan/ipsec_ipe4.h"
++#include "openswan/ipsec_ah.h"
++#include "openswan/ipsec_esp.h"
++
++#ifdef CONFIG_IPSEC_IPCOMP
++#include "openswan/ipcomp.h"
++#endif /* CONFIG_IPSEC_IPCOMP */
++
++#include <pfkeyv2.h>
++#include <pfkey.h>
++
++#include "openswan/ipsec_proto.h"
++
++
++enum ipsec_life_alive
++ipsec_lifetime_check(struct ipsec_lifetime64 *il64,
++ const char *lifename,
++ const char *saname,
++ enum ipsec_life_type ilt,
++ enum ipsec_direction idir,
++ struct ipsec_sa *ips)
++{
++ __u64 count;
++ const char *dir;
++
++ if(saname == NULL) {
++ saname = "unknown-SA";
++ }
++
++ if(idir == ipsec_incoming) {
++ dir = "incoming";
++ } else {
++ dir = "outgoing";
++ }
++
++
++ if(ilt == ipsec_life_timebased) {
++ count = jiffies/HZ - il64->ipl_count;
++ } else {
++ count = il64->ipl_count;
++ }
++
++ if(il64->ipl_hard &&
++ (count > il64->ipl_hard)) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
++ "klips_debug:ipsec_lifetime_check: "
++ "hard %s lifetime of SA:<%s%s%s> %s has been reached, SA expired, "
++ "%s packet dropped.\n",
++ lifename,
++ IPS_XFORM_NAME(ips),
++ saname,
++ dir);
++
++ pfkey_expire(ips, 1);
++ return ipsec_life_harddied;
++ }
++
++ if(il64->ipl_soft &&
++ (count > il64->ipl_soft)) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
++ "klips_debug:ipsec_lifetime_check: "
++ "soft %s lifetime of SA:<%s%s%s> %s has been reached, SA expiring, "
++ "soft expire message sent up, %s packet still processed.\n",
++ lifename,
++ IPS_XFORM_NAME(ips),
++ saname,
++ dir);
++
++ if(ips->ips_state != SADB_SASTATE_DYING) {
++ pfkey_expire(ips, 0);
++ }
++ ips->ips_state = SADB_SASTATE_DYING;
++
++ return ipsec_life_softdied;
++ }
++ return ipsec_life_okay;
++}
++
++
++/*
++ * This function takes a buffer (with length), a lifetime name and type,
++ * and formats a string to represent the current values of the lifetime.
++ *
++ * It returns the number of bytes that the format took (or would take,
++ * if the buffer were large enough: snprintf semantics).
++ * This is used in /proc routines and in debug output.
++ */
++int
++ipsec_lifetime_format(char *buffer,
++ int buflen,
++ char *lifename,
++ enum ipsec_life_type timebaselife,
++ struct ipsec_lifetime64 *lifetime)
++{
++ int len = 0;
++ __u64 count;
++
++ if(timebaselife == ipsec_life_timebased) {
++ count = jiffies/HZ - lifetime->ipl_count;
++ } else {
++ count = lifetime->ipl_count;
++ }
++
++ if(lifetime->ipl_count > 1 ||
++ lifetime->ipl_soft ||
++ lifetime->ipl_hard) {
++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0))
++ len = ipsec_snprintf(buffer, buflen,
++ "%s(%Lu,%Lu,%Lu)",
++ lifename,
++ count,
++ lifetime->ipl_soft,
++ lifetime->ipl_hard);
++#else /* XXX high 32 bits are not displayed */
++ len = ipsec_snprintf(buffer, buflen,
++ "%s(%lu,%lu,%lu)",
++ lifename,
++ (unsigned long)count,
++ (unsigned long)lifetime->ipl_soft,
++ (unsigned long)lifetime->ipl_hard);
++#endif
++ }
++
++ return len;
++}
++
++void
++ipsec_lifetime_update_hard(struct ipsec_lifetime64 *lifetime,
++ __u64 newvalue)
++{
++ if(newvalue &&
++ (!lifetime->ipl_hard ||
++ (newvalue < lifetime->ipl_hard))) {
++ lifetime->ipl_hard = newvalue;
++
++ if(!lifetime->ipl_soft &&
++ (lifetime->ipl_hard < lifetime->ipl_soft)) {
++ lifetime->ipl_soft = lifetime->ipl_hard;
++ }
++ }
++}
++
++void
++ipsec_lifetime_update_soft(struct ipsec_lifetime64 *lifetime,
++ __u64 newvalue)
++{
++ if(newvalue &&
++ (!lifetime->ipl_soft ||
++ (newvalue < lifetime->ipl_soft))) {
++ lifetime->ipl_soft = newvalue;
++
++ if(lifetime->ipl_hard &&
++ (lifetime->ipl_hard < lifetime->ipl_soft)) {
++ lifetime->ipl_soft = lifetime->ipl_hard;
++ }
++ }
++}
++
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.12 2004/04/23 20:44:35 ken
++ * Update comments
++ *
++ * Revision 1.11 2004/04/06 02:49:26 mcr
++ * pullup of algo code from alg-branch.
++ *
++ * Revision 1.10 2004/03/30 11:03:10 paul
++ * two more occurances of snprintf, found by Sam from a users oops msg.
++ *
++ * Revision 1.9 2003/10/31 02:27:55 mcr
++ * pulled up port-selector patches and sa_id elimination.
++ *
++ * Revision 1.8.4.1 2003/10/29 01:30:41 mcr
++ * elimited "struct sa_id".
++ *
++ * Revision 1.8 2003/02/06 02:00:10 rgb
++ * Fixed incorrect debugging text label
++ *
++ * Revision 1.7 2002/05/23 07:16:26 rgb
++ * Fixed absolute/relative reference to lifetime count printout.
++ *
++ * Revision 1.6 2002/04/24 07:55:32 mcr
++ * #include patches and Makefiles for post-reorg compilation.
++ *
++ * Revision 1.5 2002/04/24 07:36:28 mcr
++ * Moved from ./klips/net/ipsec/ipsec_life.c,v
++ *
++ * Revision 1.4 2002/01/29 17:17:55 mcr
++ * moved include of ipsec_param.h to after include of linux/kernel.h
++ * otherwise, it seems that some option that is set in ipsec_param.h
++ * screws up something subtle in the include path to kernel.h, and
++ * it complains on the snprintf() prototype.
++ *
++ * Revision 1.3 2002/01/29 02:13:17 mcr
++ * introduction of ipsec_kversion.h means that include of
++ * ipsec_param.h must preceed any decisions about what files to
++ * include to deal with differences in kernel source.
++ *
++ * Revision 1.2 2001/11/26 09:16:14 rgb
++ * Merge MCR's ipsec_sa, eroute, proc and struct lifetime changes.
++ *
++ * Revision 1.1.2.1 2001/09/25 02:25:57 mcr
++ * lifetime structure created and common functions created.
++ *
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/net/ipsec/ipsec_mast.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,1080 @@
++/*
++ * IPSEC MAST code.
++ * Copyright (C) 1996, 1997 John Ioannidis.
++ * Copyright (C) 1998, 1999, 2000, 2001, 2002 Richard Guy Briggs.
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ */
++
++char ipsec_mast_c_version[] = "RCSID $Id$";
++
++#define __NO_VERSION__
++#include <linux/module.h>
++#include <linux/config.h> /* for CONFIG_IP_FORWARD */
++#include <linux/version.h>
++#include <linux/kernel.h> /* printk() */
++
++#include "freeswan/ipsec_param.h"
++
++#ifdef MALLOC_SLAB
++# include <linux/slab.h> /* kmalloc() */
++#else /* MALLOC_SLAB */
++# include <linux/malloc.h> /* kmalloc() */
++#endif /* MALLOC_SLAB */
++#include <linux/errno.h> /* error codes */
++#include <linux/types.h> /* size_t */
++#include <linux/interrupt.h> /* mark_bh */
++
++#include <linux/netdevice.h> /* struct device, struct net_device_stats, dev_queue_xmit() and other headers */
++#include <linux/etherdevice.h> /* eth_type_trans */
++#include <linux/ip.h> /* struct iphdr */
++#include <linux/tcp.h> /* struct tcphdr */
++#include <linux/udp.h> /* struct udphdr */
++#include <linux/skbuff.h>
++#include <freeswan.h>
++#include <asm/uaccess.h>
++#include <linux/in6.h>
++#include <net/dst.h>
++#undef dev_kfree_skb
++#define dev_kfree_skb(a,b) kfree_skb(a)
++#define PHYSDEV_TYPE
++#include <asm/checksum.h>
++#include <net/icmp.h> /* icmp_send() */
++#include <net/ip.h>
++#include <linux/netfilter_ipv4.h>
++
++#include <linux/if_arp.h>
++
++#include "freeswan/radij.h"
++#include "freeswan/ipsec_life.h"
++#include "freeswan/ipsec_xform.h"
++#include "freeswan/ipsec_eroute.h"
++#include "freeswan/ipsec_encap.h"
++#include "freeswan/ipsec_radij.h"
++#include "freeswan/ipsec_sa.h"
++#include "freeswan/ipsec_tunnel.h"
++#include "freeswan/ipsec_mast.h"
++#include "freeswan/ipsec_ipe4.h"
++#include "freeswan/ipsec_ah.h"
++#include "freeswan/ipsec_esp.h"
++
++#include <pfkeyv2.h>
++#include <pfkey.h>
++
++#include "freeswan/ipsec_proto.h"
++
++int ipsec_maxdevice_count = -1;
++
++DEBUG_NO_STATIC int
++ipsec_mast_open(struct device *dev)
++{
++ struct ipsecpriv *prv = dev->priv;
++
++ /*
++ * Can't open until attached.
++ */
++
++ KLIPS_PRINT(debug_mast & DB_MAST_INIT,
++ "klips_debug:ipsec_mast_open: "
++ "dev = %s, prv->dev = %s\n",
++ dev->name, prv->dev?prv->dev->name:"NONE");
++
++ if (prv->dev == NULL)
++ return -ENODEV;
++
++ MOD_INC_USE_COUNT;
++ return 0;
++}
++
++DEBUG_NO_STATIC int
++ipsec_mast_close(struct device *dev)
++{
++ MOD_DEC_USE_COUNT;
++ return 0;
++}
++
++static inline int ipsec_mast_xmit2(struct sk_buff *skb)
++{
++ return ip_send(skb);
++}
++
++enum ipsec_xmit_value
++ipsec_mast_send(struct ipsec_xmit_state*ixs)
++{
++ /* new route/dst cache code from James Morris */
++ ixs->skb->dev = ixs->physdev;
++ /*skb_orphan(ixs->skb);*/
++ if((ixs->error = ip_route_output(&ixs->route,
++ ixs->skb->nh.iph->daddr,
++ ixs->pass ? 0 : ixs->skb->nh.iph->saddr,
++ RT_TOS(ixs->skb->nh.iph->tos),
++ ixs->physdev->iflink /* rgb: should this be 0? */))) {
++ ixs->stats->tx_errors++;
++ KLIPS_PRINT(debug_mast & DB_MAST_XMIT,
++ "klips_debug:ipsec_xmit_send: "
++ "ip_route_output failed with error code %d, rt->u.dst.dev=%s, dropped\n",
++ ixs->error,
++ ixs->route->u.dst.dev->name);
++ return IPSEC_XMIT_ROUTEERR;
++ }
++ if(ixs->dev == ixs->route->u.dst.dev) {
++ ip_rt_put(ixs->route);
++ /* This is recursion, drop it. */
++ ixs->stats->tx_errors++;
++ KLIPS_PRINT(debug_mast & DB_MAST_XMIT,
++ "klips_debug:ipsec_xmit_send: "
++ "suspect recursion, dev=rt->u.dst.dev=%s, dropped\n",
++ ixs->dev->name);
++ return IPSEC_XMIT_RECURSDETECT;
++ }
++ dst_release(ixs->skb->dst);
++ ixs->skb->dst = &ixs->route->u.dst;
++ ixs->stats->tx_bytes += ixs->skb->len;
++ if(ixs->skb->len < ixs->skb->nh.raw - ixs->skb->data) {
++ ixs->stats->tx_errors++;
++ printk(KERN_WARNING
++ "klips_error:ipsec_xmit_send: "
++ "tried to __skb_pull nh-data=%ld, %d available. This should never happen, please report.\n",
++ (unsigned long)(ixs->skb->nh.raw - ixs->skb->data),
++ ixs->skb->len);
++ return IPSEC_XMIT_PUSHPULLERR;
++ }
++ __skb_pull(ixs->skb, ixs->skb->nh.raw - ixs->skb->data);
++#ifdef SKB_RESET_NFCT
++ nf_conntrack_put(ixs->skb->nfct);
++ ixs->skb->nfct = NULL;
++#ifdef CONFIG_NETFILTER_DEBUG
++ ixs->skb->nf_debug = 0;
++#endif /* CONFIG_NETFILTER_DEBUG */
++#endif /* SKB_RESET_NFCT */
++ KLIPS_PRINT(debug_mast & DB_MAST_XMIT,
++ "klips_debug:ipsec_xmit_send: "
++ "...done, calling ip_send() on device:%s\n",
++ ixs->skb->dev ? ixs->skb->dev->name : "NULL");
++ KLIPS_IP_PRINT(debug_mast & DB_MAST_XMIT, ixs->skb->nh.iph);
++ {
++ int err;
++
++ err = NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, ixs->skb, NULL, ixs->route->u.dst.dev,
++ ipsec_mast_xmit2);
++ if(err != NET_XMIT_SUCCESS && err != NET_XMIT_CN) {
++ if(net_ratelimit())
++ printk(KERN_ERR
++ "klips_error:ipsec_xmit_send: "
++ "ip_send() failed, err=%d\n",
++ -err);
++ ixs->stats->tx_errors++;
++ ixs->stats->tx_aborted_errors++;
++ ixs->skb = NULL;
++ return IPSEC_XMIT_IPSENDFAILURE;
++ }
++ }
++ ixs->stats->tx_packets++;
++
++ ixs->skb = NULL;
++
++ return IPSEC_XMIT_OK;
++}
++
++void
++ipsec_mast_cleanup(struct ipsec_xmit_state*ixs)
++{
++#if defined(HAS_NETIF_QUEUE) || defined (HAVE_NETIF_QUEUE)
++ netif_wake_queue(ixs->dev);
++#else /* defined(HAS_NETIF_QUEUE) || defined (HAVE_NETIF_QUEUE) */
++ ixs->dev->tbusy = 0;
++#endif /* defined(HAS_NETIF_QUEUE) || defined (HAVE_NETIF_QUEUE) */
++ if(ixs->saved_header) {
++ kfree(ixs->saved_header);
++ }
++ if(ixs->skb) {
++ dev_kfree_skb(ixs->skb, FREE_WRITE);
++ }
++ if(ixs->oskb) {
++ dev_kfree_skb(ixs->oskb, FREE_WRITE);
++ }
++ if (ixs->ips.ips_ident_s.data) {
++ kfree(ixs->ips.ips_ident_s.data);
++ }
++ if (ixs->ips.ips_ident_d.data) {
++ kfree(ixs->ips.ips_ident_d.data);
++ }
++}
++
++#if 0
++/*
++ * This function assumes it is being called from dev_queue_xmit()
++ * and that skb is filled properly by that function.
++ */
++int
++ipsec_mast_start_xmit(struct sk_buff *skb, struct device *dev, IPsecSAref_t SAref)
++{
++ struct ipsec_xmit_state ixs_mem;
++ struct ipsec_xmit_state *ixs = &ixs_mem;
++ enum ipsec_xmit_value stat = IPSEC_XMIT_OK;
++
++ /* dev could be a mast device, but should be optional, I think... */
++ /* SAref is also optional, but one of the two must be present. */
++ /* I wonder if it could accept no device or saref and guess? */
++
++/* ipsec_xmit_sanity_check_dev(ixs); */
++
++ ipsec_xmit_sanity_check_skb(ixs);
++
++ ipsec_xmit_adjust_hard_header(ixs);
++
++ stat = ipsec_xmit_encap_bundle(ixs);
++ if(stat != IPSEC_XMIT_OK) {
++ /* SA processing failed */
++ }
++
++ ipsec_xmit_hard_header_restore();
++}
++#endif
++
++DEBUG_NO_STATIC struct net_device_stats *
++ipsec_mast_get_stats(struct device *dev)
++{
++ return &(((struct ipsecpriv *)(dev->priv))->mystats);
++}
++
++/*
++ * Revectored calls.
++ * For each of these calls, a field exists in our private structure.
++ */
++
++DEBUG_NO_STATIC int
++ipsec_mast_hard_header(struct sk_buff *skb, struct device *dev,
++ unsigned short type, void *daddr, void *saddr, unsigned len)
++{
++ struct ipsecpriv *prv = dev->priv;
++ struct device *tmp;
++ int ret;
++ struct net_device_stats *stats; /* This device's statistics */
++
++ if(skb == NULL) {
++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC,
++ "klips_debug:ipsec_mast_hard_header: "
++ "no skb...\n");
++ return -ENODATA;
++ }
++
++ if(dev == NULL) {
++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC,
++ "klips_debug:ipsec_mast_hard_header: "
++ "no device...\n");
++ return -ENODEV;
++ }
++
++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC,
++ "klips_debug:ipsec_mast_hard_header: "
++ "skb->dev=%s dev=%s.\n",
++ skb->dev ? skb->dev->name : "NULL",
++ dev->name);
++
++ if(prv == NULL) {
++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC,
++ "klips_debug:ipsec_mast_hard_header: "
++ "no private space associated with dev=%s\n",
++ dev->name ? dev->name : "NULL");
++ return -ENODEV;
++ }
++
++ stats = (struct net_device_stats *) &(prv->mystats);
++
++ if(prv->dev == NULL) {
++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC,
++ "klips_debug:ipsec_mast_hard_header: "
++ "no physical device associated with dev=%s\n",
++ dev->name ? dev->name : "NULL");
++ stats->tx_dropped++;
++ return -ENODEV;
++ }
++
++ /* check if we have to send a IPv6 packet. It might be a Router
++ Solicitation, where the building of the packet happens in
++ reverse order:
++ 1. ll hdr,
++ 2. IPv6 hdr,
++ 3. ICMPv6 hdr
++ -> skb->nh.raw is still uninitialized when this function is
++ called!! If this is no IPv6 packet, we can print debugging
++ messages, otherwise we skip all debugging messages and just
++ build the ll header */
++ if(type != ETH_P_IPV6) {
++ /* execute this only, if we don't have to build the
++ header for a IPv6 packet */
++ if(!prv->hard_header) {
++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC,
++ "klips_debug:ipsec_mast_hard_header: "
++ "physical device has been detached, packet dropped 0p%p->0p%p len=%d type=%d dev=%s->NULL ",
++ saddr,
++ daddr,
++ len,
++ type,
++ dev->name);
++ KLIPS_PRINTMORE(debug_mast & DB_MAST_REVEC,
++ "ip=%08x->%08x\n",
++ (__u32)ntohl(skb->nh.iph->saddr),
++ (__u32)ntohl(skb->nh.iph->daddr) );
++ stats->tx_dropped++;
++ return -ENODEV;
++ }
++
++#define da ((struct device *)(prv->dev))->dev_addr
++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC,
++ "klips_debug:ipsec_mast_hard_header: "
++ "Revectored 0p%p->0p%p len=%d type=%d dev=%s->%s dev_addr=%02x:%02x:%02x:%02x:%02x:%02x ",
++ saddr,
++ daddr,
++ len,
++ type,
++ dev->name,
++ prv->dev->name,
++ da[0], da[1], da[2], da[3], da[4], da[5]);
++ KLIPS_PRINTMORE(debug_mast & DB_MAST_REVEC,
++ "ip=%08x->%08x\n",
++ (__u32)ntohl(skb->nh.iph->saddr),
++ (__u32)ntohl(skb->nh.iph->daddr) );
++ } else {
++ KLIPS_PRINT(debug_mast,
++ "klips_debug:ipsec_mast_hard_header: "
++ "is IPv6 packet, skip debugging messages, only revector and build linklocal header.\n");
++ }
++ tmp = skb->dev;
++ skb->dev = prv->dev;
++ ret = prv->hard_header(skb, prv->dev, type, (void *)daddr, (void *)saddr, len);
++ skb->dev = tmp;
++ return ret;
++}
++
++DEBUG_NO_STATIC int
++ipsec_mast_rebuild_header(struct sk_buff *skb)
++{
++ struct ipsecpriv *prv = skb->dev->priv;
++ struct device *tmp;
++ int ret;
++ struct net_device_stats *stats; /* This device's statistics */
++
++ if(skb->dev == NULL) {
++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC,
++ "klips_debug:ipsec_mast_rebuild_header: "
++ "no device...");
++ return -ENODEV;
++ }
++
++ if(prv == NULL) {
++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC,
++ "klips_debug:ipsec_mast_rebuild_header: "
++ "no private space associated with dev=%s",
++ skb->dev->name ? skb->dev->name : "NULL");
++ return -ENODEV;
++ }
++
++ stats = (struct net_device_stats *) &(prv->mystats);
++
++ if(prv->dev == NULL) {
++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC,
++ "klips_debug:ipsec_mast_rebuild_header: "
++ "no physical device associated with dev=%s",
++ skb->dev->name ? skb->dev->name : "NULL");
++ stats->tx_dropped++;
++ return -ENODEV;
++ }
++
++ if(!prv->rebuild_header) {
++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC,
++ "klips_debug:ipsec_mast_rebuild_header: "
++ "physical device has been detached, packet dropped skb->dev=%s->NULL ",
++ skb->dev->name);
++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC,
++ "ip=%08x->%08x\n",
++ (__u32)ntohl(skb->nh.iph->saddr),
++ (__u32)ntohl(skb->nh.iph->daddr) );
++ stats->tx_dropped++;
++ return -ENODEV;
++ }
++
++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC,
++ "klips_debug:ipsec_mast: "
++ "Revectored rebuild_header dev=%s->%s ",
++ skb->dev->name, prv->dev->name);
++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC,
++ "ip=%08x->%08x\n",
++ (__u32)ntohl(skb->nh.iph->saddr),
++ (__u32)ntohl(skb->nh.iph->daddr) );
++ tmp = skb->dev;
++ skb->dev = prv->dev;
++
++ ret = prv->rebuild_header(skb);
++ skb->dev = tmp;
++ return ret;
++}
++
++DEBUG_NO_STATIC int
++ipsec_mast_set_mac_address(struct device *dev, void *addr)
++{
++ struct ipsecpriv *prv = dev->priv;
++
++ struct net_device_stats *stats; /* This device's statistics */
++
++ if(dev == NULL) {
++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC,
++ "klips_debug:ipsec_mast_set_mac_address: "
++ "no device...");
++ return -ENODEV;
++ }
++
++ if(prv == NULL) {
++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC,
++ "klips_debug:ipsec_mast_set_mac_address: "
++ "no private space associated with dev=%s",
++ dev->name ? dev->name : "NULL");
++ return -ENODEV;
++ }
++
++ stats = (struct net_device_stats *) &(prv->mystats);
++
++ if(prv->dev == NULL) {
++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC,
++ "klips_debug:ipsec_mast_set_mac_address: "
++ "no physical device associated with dev=%s",
++ dev->name ? dev->name : "NULL");
++ stats->tx_dropped++;
++ return -ENODEV;
++ }
++
++ if(!prv->set_mac_address) {
++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC,
++ "klips_debug:ipsec_mast_set_mac_address: "
++ "physical device has been detached, cannot set - skb->dev=%s->NULL\n",
++ dev->name);
++ return -ENODEV;
++ }
++
++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC,
++ "klips_debug:ipsec_mast_set_mac_address: "
++ "Revectored dev=%s->%s addr=0p%p\n",
++ dev->name, prv->dev->name, addr);
++ return prv->set_mac_address(prv->dev, addr);
++
++}
++
++DEBUG_NO_STATIC void
++ipsec_mast_cache_update(struct hh_cache *hh, struct device *dev, unsigned char * haddr)
++{
++ struct ipsecpriv *prv = dev->priv;
++
++ struct net_device_stats *stats; /* This device's statistics */
++
++ if(dev == NULL) {
++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC,
++ "klips_debug:ipsec_mast_cache_update: "
++ "no device...");
++ return;
++ }
++
++ if(prv == NULL) {
++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC,
++ "klips_debug:ipsec_mast_cache_update: "
++ "no private space associated with dev=%s",
++ dev->name ? dev->name : "NULL");
++ return;
++ }
++
++ stats = (struct net_device_stats *) &(prv->mystats);
++
++ if(prv->dev == NULL) {
++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC,
++ "klips_debug:ipsec_mast_cache_update: "
++ "no physical device associated with dev=%s",
++ dev->name ? dev->name : "NULL");
++ stats->tx_dropped++;
++ return;
++ }
++
++ if(!prv->header_cache_update) {
++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC,
++ "klips_debug:ipsec_mast_cache_update: "
++ "physical device has been detached, cannot set - skb->dev=%s->NULL\n",
++ dev->name);
++ return;
++ }
++
++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC,
++ "klips_debug:ipsec_mast: "
++ "Revectored cache_update\n");
++ prv->header_cache_update(hh, prv->dev, haddr);
++ return;
++}
++
++DEBUG_NO_STATIC int
++ipsec_mast_neigh_setup(struct neighbour *n)
++{
++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC,
++ "klips_debug:ipsec_mast_neigh_setup:\n");
++
++ if (n->nud_state == NUD_NONE) {
++ n->ops = &arp_broken_ops;
++ n->output = n->ops->output;
++ }
++ return 0;
++}
++
++DEBUG_NO_STATIC int
++ipsec_mast_neigh_setup_dev(struct device *dev, struct neigh_parms *p)
++{
++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC,
++ "klips_debug:ipsec_mast_neigh_setup_dev: "
++ "setting up %s\n",
++ dev ? dev->name : "NULL");
++
++ if (p->tbl->family == AF_INET) {
++ p->neigh_setup = ipsec_mast_neigh_setup;
++ p->ucast_probes = 0;
++ p->mcast_probes = 0;
++ }
++ return 0;
++}
++
++/*
++ * We call the attach routine to attach another device.
++ */
++
++DEBUG_NO_STATIC int
++ipsec_mast_attach(struct device *dev, struct device *physdev)
++{
++ int i;
++ struct ipsecpriv *prv = dev->priv;
++
++ if(dev == NULL) {
++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC,
++ "klips_debug:ipsec_mast_attach: "
++ "no device...");
++ return -ENODEV;
++ }
++
++ if(prv == NULL) {
++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC,
++ "klips_debug:ipsec_mast_attach: "
++ "no private space associated with dev=%s",
++ dev->name ? dev->name : "NULL");
++ return -ENODATA;
++ }
++
++ prv->dev = physdev;
++ prv->hard_start_xmit = physdev->hard_start_xmit;
++ prv->get_stats = physdev->get_stats;
++
++ if (physdev->hard_header) {
++ prv->hard_header = physdev->hard_header;
++ dev->hard_header = ipsec_mast_hard_header;
++ } else
++ dev->hard_header = NULL;
++
++ if (physdev->rebuild_header) {
++ prv->rebuild_header = physdev->rebuild_header;
++ dev->rebuild_header = ipsec_mast_rebuild_header;
++ } else
++ dev->rebuild_header = NULL;
++
++ if (physdev->set_mac_address) {
++ prv->set_mac_address = physdev->set_mac_address;
++ dev->set_mac_address = ipsec_mast_set_mac_address;
++ } else
++ dev->set_mac_address = NULL;
++
++ if (physdev->header_cache_update) {
++ prv->header_cache_update = physdev->header_cache_update;
++ dev->header_cache_update = ipsec_mast_cache_update;
++ } else
++ dev->header_cache_update = NULL;
++
++ dev->hard_header_len = physdev->hard_header_len;
++
++/* prv->neigh_setup = physdev->neigh_setup; */
++ dev->neigh_setup = ipsec_mast_neigh_setup_dev;
++ dev->mtu = 16260; /* 0xfff0; */ /* dev->mtu; */
++ prv->mtu = physdev->mtu;
++
++#ifdef PHYSDEV_TYPE
++ dev->type = physdev->type; /* ARPHRD_MAST; */
++#endif /* PHYSDEV_TYPE */
++
++ dev->addr_len = physdev->addr_len;
++ for (i=0; i<dev->addr_len; i++) {
++ dev->dev_addr[i] = physdev->dev_addr[i];
++ }
++#ifdef CONFIG_IPSEC_DEBUG
++ if(debug_mast & DB_MAST_INIT) {
++ printk(KERN_INFO "klips_debug:ipsec_mast_attach: "
++ "physical device %s being attached has HW address: %2x",
++ physdev->name, physdev->dev_addr[0]);
++ for (i=1; i < physdev->addr_len; i++) {
++ printk(":%02x", physdev->dev_addr[i]);
++ }
++ printk("\n");
++ }
++#endif /* CONFIG_IPSEC_DEBUG */
++
++ return 0;
++}
++
++/*
++ * We call the detach routine to detach the ipsec mast from another device.
++ */
++
++DEBUG_NO_STATIC int
++ipsec_mast_detach(struct device *dev)
++{
++ int i;
++ struct ipsecpriv *prv = dev->priv;
++
++ if(dev == NULL) {
++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC,
++ "klips_debug:ipsec_mast_detach: "
++ "no device...");
++ return -ENODEV;
++ }
++
++ if(prv == NULL) {
++ KLIPS_PRINT(debug_mast & DB_MAST_REVEC,
++ "klips_debug:ipsec_mast_detach: "
++ "no private space associated with dev=%s",
++ dev->name ? dev->name : "NULL");
++ return -ENODATA;
++ }
++
++ KLIPS_PRINT(debug_mast & DB_MAST_INIT,
++ "klips_debug:ipsec_mast_detach: "
++ "physical device %s being detached from virtual device %s\n",
++ prv->dev ? prv->dev->name : "NULL",
++ dev->name);
++
++ prv->dev = NULL;
++ prv->hard_start_xmit = NULL;
++ prv->get_stats = NULL;
++
++ prv->hard_header = NULL;
++#ifdef DETACH_AND_DOWN
++ dev->hard_header = NULL;
++#endif /* DETACH_AND_DOWN */
++
++ prv->rebuild_header = NULL;
++#ifdef DETACH_AND_DOWN
++ dev->rebuild_header = NULL;
++#endif /* DETACH_AND_DOWN */
++
++ prv->set_mac_address = NULL;
++#ifdef DETACH_AND_DOWN
++ dev->set_mac_address = NULL;
++#endif /* DETACH_AND_DOWN */
++
++ prv->header_cache_update = NULL;
++#ifdef DETACH_AND_DOWN
++ dev->header_cache_update = NULL;
++#endif /* DETACH_AND_DOWN */
++
++#ifdef DETACH_AND_DOWN
++ dev->neigh_setup = NULL;
++#endif /* DETACH_AND_DOWN */
++
++ dev->hard_header_len = 0;
++#ifdef DETACH_AND_DOWN
++ dev->mtu = 0;
++#endif /* DETACH_AND_DOWN */
++ prv->mtu = 0;
++ for (i=0; i<MAX_ADDR_LEN; i++) {
++ dev->dev_addr[i] = 0;
++ }
++ dev->addr_len = 0;
++#ifdef PHYSDEV_TYPE
++ dev->type = ARPHRD_VOID; /* ARPHRD_MAST; */
++#endif /* PHYSDEV_TYPE */
++
++ return 0;
++}
++
++/*
++ * We call the clear routine to detach all ipsec masts from other devices.
++ */
++DEBUG_NO_STATIC int
++ipsec_mast_clear(void)
++{
++ int i;
++ struct device *ipsecdev = NULL, *prvdev;
++ struct ipsecpriv *prv;
++ char name[9];
++ int ret;
++
++ KLIPS_PRINT(debug_mast & DB_MAST_INIT,
++ "klips_debug:ipsec_mast_clear: .\n");
++
++ for(i = 0; i < IPSEC_NUM_IF; i++) {
++ sprintf(name, IPSEC_DEV_FORMAT, i);
++ if((ipsecdev = ipsec_dev_get(name)) != NULL) {
++ if((prv = (struct ipsecpriv *)(ipsecdev->priv))) {
++ prvdev = (struct device *)(prv->dev);
++ if(prvdev) {
++ KLIPS_PRINT(debug_mast & DB_MAST_INIT,
++ "klips_debug:ipsec_mast_clear: "
++ "physical device for device %s is %s\n",
++ name, prvdev->name);
++ if((ret = ipsec_mast_detach(ipsecdev))) {
++ KLIPS_PRINT(debug_mast & DB_MAST_INIT,
++ "klips_debug:ipsec_mast_clear: "
++ "error %d detatching device %s from device %s.\n",
++ ret, name, prvdev->name);
++ return ret;
++ }
++ }
++ }
++ }
++ }
++ return 0;
++}
++
++DEBUG_NO_STATIC int
++ipsec_mast_ioctl(struct device *dev, struct ifreq *ifr, int cmd)
++{
++ struct ipsecmastconf *cf = (struct ipsecmastconf *)&ifr->ifr_data;
++ struct ipsecpriv *prv = dev->priv;
++ struct device *them; /* physical device */
++#ifdef CONFIG_IP_ALIAS
++ char *colon;
++ char realphysname[IFNAMSIZ];
++#endif /* CONFIG_IP_ALIAS */
++
++ if(dev == NULL) {
++ KLIPS_PRINT(debug_mast & DB_MAST_INIT,
++ "klips_debug:ipsec_mast_ioctl: "
++ "device not supplied.\n");
++ return -ENODEV;
++ }
++
++ KLIPS_PRINT(debug_mast & DB_MAST_INIT,
++ "klips_debug:ipsec_mast_ioctl: "
++ "tncfg service call #%d for dev=%s\n",
++ cmd,
++ dev->name ? dev->name : "NULL");
++ switch (cmd) {
++ /* attach a virtual ipsec? device to a physical device */
++ case IPSEC_SET_DEV:
++ KLIPS_PRINT(debug_mast & DB_MAST_INIT,
++ "klips_debug:ipsec_mast_ioctl: "
++ "calling ipsec_mast_attatch...\n");
++#ifdef CONFIG_IP_ALIAS
++ /* If this is an IP alias interface, get its real physical name */
++ strncpy(realphysname, cf->cf_name, IFNAMSIZ);
++ realphysname[IFNAMSIZ-1] = 0;
++ colon = strchr(realphysname, ':');
++ if (colon) *colon = 0;
++ them = ipsec_dev_get(realphysname);
++#else /* CONFIG_IP_ALIAS */
++ them = ipsec_dev_get(cf->cf_name);
++#endif /* CONFIG_IP_ALIAS */
++
++ if (them == NULL) {
++ KLIPS_PRINT(debug_mast & DB_MAST_INIT,
++ "klips_debug:ipsec_mast_ioctl: "
++ "physical device %s requested is null\n",
++ cf->cf_name);
++ return -ENXIO;
++ }
++
++#if 0
++ if (them->flags & IFF_UP) {
++ KLIPS_PRINT(debug_mast & DB_MAST_INIT,
++ "klips_debug:ipsec_mast_ioctl: "
++ "physical device %s requested is not up.\n",
++ cf->cf_name);
++ return -ENXIO;
++ }
++#endif
++
++ if (prv && prv->dev) {
++ KLIPS_PRINT(debug_mast & DB_MAST_INIT,
++ "klips_debug:ipsec_mast_ioctl: "
++ "virtual device is already connected to %s.\n",
++ prv->dev->name ? prv->dev->name : "NULL");
++ return -EBUSY;
++ }
++ return ipsec_mast_attach(dev, them);
++
++ case IPSEC_DEL_DEV:
++ KLIPS_PRINT(debug_mast & DB_MAST_INIT,
++ "klips_debug:ipsec_mast_ioctl: "
++ "calling ipsec_mast_detatch.\n");
++ if (! prv->dev) {
++ KLIPS_PRINT(debug_mast & DB_MAST_INIT,
++ "klips_debug:ipsec_mast_ioctl: "
++ "physical device not connected.\n");
++ return -ENODEV;
++ }
++ return ipsec_mast_detach(dev);
++
++ case IPSEC_CLR_DEV:
++ KLIPS_PRINT(debug_mast & DB_MAST_INIT,
++ "klips_debug:ipsec_mast_ioctl: "
++ "calling ipsec_mast_clear.\n");
++ return ipsec_mast_clear();
++
++ default:
++ KLIPS_PRINT(debug_mast & DB_MAST_INIT,
++ "klips_debug:ipsec_mast_ioctl: "
++ "unknown command %d.\n",
++ cmd);
++ return -EOPNOTSUPP;
++ }
++}
++
++int
++ipsec_mast_device_event(struct notifier_block *unused, unsigned long event, void *ptr)
++{
++ struct device *dev = ptr;
++ struct device *ipsec_dev;
++ struct ipsecpriv *priv;
++ char name[9];
++ int i;
++
++ if (dev == NULL) {
++ KLIPS_PRINT(debug_mast & DB_MAST_INIT,
++ "klips_debug:ipsec_mast_device_event: "
++ "dev=NULL for event type %ld.\n",
++ event);
++ return(NOTIFY_DONE);
++ }
++
++ /* check for loopback devices */
++ if (dev && (dev->flags & IFF_LOOPBACK)) {
++ return(NOTIFY_DONE);
++ }
++
++ switch (event) {
++ case NETDEV_DOWN:
++ /* look very carefully at the scope of these compiler
++ directives before changing anything... -- RGB */
++
++ case NETDEV_UNREGISTER:
++ switch (event) {
++ case NETDEV_DOWN:
++ KLIPS_PRINT(debug_mast & DB_MAST_INIT,
++ "klips_debug:ipsec_mast_device_event: "
++ "NETDEV_DOWN dev=%s flags=%x\n",
++ dev->name,
++ dev->flags);
++ if(strncmp(dev->name, "ipsec", strlen("ipsec")) == 0) {
++ printk(KERN_CRIT "IPSEC EVENT: KLIPS device %s shut down.\n",
++ dev->name);
++ }
++ break;
++ case NETDEV_UNREGISTER:
++ KLIPS_PRINT(debug_mast & DB_MAST_INIT,
++ "klips_debug:ipsec_mast_device_event: "
++ "NETDEV_UNREGISTER dev=%s flags=%x\n",
++ dev->name,
++ dev->flags);
++ break;
++ }
++
++ /* find the attached physical device and detach it. */
++ for(i = 0; i < IPSEC_NUM_IF; i++) {
++ sprintf(name, IPSEC_DEV_FORMAT, i);
++ ipsec_dev = ipsec_dev_get(name);
++ if(ipsec_dev) {
++ priv = (struct ipsecpriv *)(ipsec_dev->priv);
++ if(priv) {
++ ;
++ if(((struct device *)(priv->dev)) == dev) {
++ /* dev_close(ipsec_dev); */
++ /* return */ ipsec_mast_detach(ipsec_dev);
++ KLIPS_PRINT(debug_mast & DB_MAST_INIT,
++ "klips_debug:ipsec_mast_device_event: "
++ "device '%s' has been detached.\n",
++ ipsec_dev->name);
++ break;
++ }
++ } else {
++ KLIPS_PRINT(debug_mast & DB_MAST_INIT,
++ "klips_debug:ipsec_mast_device_event: "
++ "device '%s' has no private data space!\n",
++ ipsec_dev->name);
++ }
++ }
++ }
++ break;
++ case NETDEV_UP:
++ KLIPS_PRINT(debug_mast & DB_MAST_INIT,
++ "klips_debug:ipsec_mast_device_event: "
++ "NETDEV_UP dev=%s\n",
++ dev->name);
++ break;
++ case NETDEV_REBOOT:
++ KLIPS_PRINT(debug_mast & DB_MAST_INIT,
++ "klips_debug:ipsec_mast_device_event: "
++ "NETDEV_REBOOT dev=%s\n",
++ dev->name);
++ break;
++ case NETDEV_CHANGE:
++ KLIPS_PRINT(debug_mast & DB_MAST_INIT,
++ "klips_debug:ipsec_mast_device_event: "
++ "NETDEV_CHANGE dev=%s flags=%x\n",
++ dev->name,
++ dev->flags);
++ break;
++ case NETDEV_REGISTER:
++ KLIPS_PRINT(debug_mast & DB_MAST_INIT,
++ "klips_debug:ipsec_mast_device_event: "
++ "NETDEV_REGISTER dev=%s\n",
++ dev->name);
++ break;
++ case NETDEV_CHANGEMTU:
++ KLIPS_PRINT(debug_mast & DB_MAST_INIT,
++ "klips_debug:ipsec_mast_device_event: "
++ "NETDEV_CHANGEMTU dev=%s to mtu=%d\n",
++ dev->name,
++ dev->mtu);
++ break;
++ case NETDEV_CHANGEADDR:
++ KLIPS_PRINT(debug_mast & DB_MAST_INIT,
++ "klips_debug:ipsec_mast_device_event: "
++ "NETDEV_CHANGEADDR dev=%s\n",
++ dev->name);
++ break;
++ case NETDEV_GOING_DOWN:
++ KLIPS_PRINT(debug_mast & DB_MAST_INIT,
++ "klips_debug:ipsec_mast_device_event: "
++ "NETDEV_GOING_DOWN dev=%s\n",
++ dev->name);
++ break;
++ case NETDEV_CHANGENAME:
++ KLIPS_PRINT(debug_mast & DB_MAST_INIT,
++ "klips_debug:ipsec_mast_device_event: "
++ "NETDEV_CHANGENAME dev=%s\n",
++ dev->name);
++ break;
++ default:
++ KLIPS_PRINT(debug_mast & DB_MAST_INIT,
++ "klips_debug:ipsec_mast_device_event: "
++ "event type %ld unrecognised for dev=%s\n",
++ event,
++ dev->name);
++ break;
++ }
++ return NOTIFY_DONE;
++}
++
++/*
++ * Called when an ipsec mast device is initialized.
++ * The ipsec mast device structure is passed to us.
++ */
++
++int
++ipsec_mast_init(struct device *dev)
++{
++ int i;
++
++ KLIPS_PRINT(debug_mast,
++ "klips_debug:ipsec_mast_init: "
++ "allocating %lu bytes initialising device: %s\n",
++ (unsigned long) sizeof(struct ipsecpriv),
++ dev->name ? dev->name : "NULL");
++
++ /* Add our mast functions to the device */
++ dev->open = ipsec_mast_open;
++ dev->stop = ipsec_mast_close;
++ dev->hard_start_xmit = ipsec_mast_start_xmit;
++ dev->get_stats = ipsec_mast_get_stats;
++
++ dev->priv = kmalloc(sizeof(struct ipsecpriv), GFP_KERNEL);
++ if (dev->priv == NULL)
++ return -ENOMEM;
++ memset((caddr_t)(dev->priv), 0, sizeof(struct ipsecpriv));
++
++ for(i = 0; i < sizeof(zeroes); i++) {
++ ((__u8*)(zeroes))[i] = 0;
++ }
++
++ dev->set_multicast_list = NULL;
++ dev->do_ioctl = ipsec_mast_ioctl;
++ dev->hard_header = NULL;
++ dev->rebuild_header = NULL;
++ dev->set_mac_address = NULL;
++ dev->header_cache_update= NULL;
++ dev->neigh_setup = ipsec_mast_neigh_setup_dev;
++ dev->hard_header_len = 0;
++ dev->mtu = 0;
++ dev->addr_len = 0;
++ dev->type = ARPHRD_VOID; /* ARPHRD_MAST; */ /* ARPHRD_ETHER; */
++ dev->tx_queue_len = 10; /* Small queue */
++ memset((caddr_t)(dev->broadcast),0xFF, ETH_ALEN); /* what if this is not attached to ethernet? */
++
++ /* New-style flags. */
++ dev->flags = IFF_NOARP /* 0 */ /* Petr Novak */;
++ dev_init_buffers(dev);
++
++ /* We're done. Have I forgotten anything? */
++ return 0;
++}
++
++/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
++/* Module specific interface (but it links with the rest of IPSEC) */
++/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
++
++int
++ipsec_mast_probe(struct device *dev)
++{
++ ipsec_mast_init(dev);
++ return 0;
++}
++
++int
++ipsec_mast_init_devices(void)
++{
++ return 0;
++}
++
++/* void */
++int
++ipsec_mast_cleanup_devices(void)
++{
++ int error = 0;
++ int i;
++ char name[10];
++ struct device *dev_mast;
++
++ for(i = 0; i < ipsec_mastdevice_count; i++) {
++ sprintf(name, MAST_DEV_FORMAT, i);
++ if((dev_mast = ipsec_dev_get(name)) == NULL) {
++ break;
++ }
++ unregister_netdev(dev_mast);
++ kfree(dev_mast->priv);
++ dev_mast->priv=NULL;
++ }
++ return error;
++}
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.3 2003/10/31 02:27:55 mcr
++ * pulled up port-selector patches and sa_id elimination.
++ *
++ * Revision 1.2.4.1 2003/10/29 01:30:41 mcr
++ * elimited "struct sa_id".
++ *
++ * Revision 1.2 2003/06/22 20:06:17 mcr
++ * refactored mast code still had lots of ipsecX junk in it.
++ *
++ * Revision 1.1 2003/02/12 19:31:12 rgb
++ * Refactored from ipsec_tunnel.c
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/net/ipsec/ipsec_md5c.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,448 @@
++/*
++ * RCSID $Id$
++ */
++
++/*
++ * The rest of the code is derived from MD5C.C by RSADSI. Minor cosmetic
++ * changes to accomodate it in the kernel by ji.
++ */
++
++#include <asm/byteorder.h>
++#include <linux/string.h>
++
++#include "openswan/ipsec_md5h.h"
++
++/* MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm
++ */
++
++/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
++rights reserved.
++
++License to copy and use this software is granted provided that it
++is identified as the "RSA Data Security, Inc. MD5 Message-Digest
++Algorithm" in all material mentioning or referencing this software
++or this function.
++
++License is also granted to make and use derivative works provided
++that such works are identified as "derived from the RSA Data
++Security, Inc. MD5 Message-Digest Algorithm" in all material
++mentioning or referencing the derived work.
++
++RSA Data Security, Inc. makes no representations concerning either
++the merchantability of this software or the suitability of this
++software for any particular purpose. It is provided "as is"
++without express or implied warranty of any kind.
++
++These notices must be retained in any copies of any part of this
++documentation and/or software.
++ */
++
++/*
++ * Additions by JI
++ *
++ * HAVEMEMCOPY is defined if mem* routines are available
++ *
++ * HAVEHTON is defined if htons() and htonl() can be used
++ * for big/little endian conversions
++ *
++ */
++
++#define HAVEMEMCOPY
++#ifdef __LITTLE_ENDIAN
++#define LITTLENDIAN
++#endif
++#ifdef __BIG_ENDIAN
++#define BIGENDIAN
++#endif
++
++/* Constants for MD5Transform routine.
++ */
++
++#define S11 7
++#define S12 12
++#define S13 17
++#define S14 22
++#define S21 5
++#define S22 9
++#define S23 14
++#define S24 20
++#define S31 4
++#define S32 11
++#define S33 16
++#define S34 23
++#define S41 6
++#define S42 10
++#define S43 15
++#define S44 21
++
++static void MD5Transform PROTO_LIST ((UINT4 [4], unsigned char [64]));
++
++#ifdef LITTLEENDIAN
++#define Encode MD5_memcpy
++#define Decode MD5_memcpy
++#else
++static void Encode PROTO_LIST
++ ((unsigned char *, UINT4 *, unsigned int));
++static void Decode PROTO_LIST
++ ((UINT4 *, unsigned char *, unsigned int));
++#endif
++
++#ifdef HAVEMEMCOPY
++/* no need to include <memory.h> here; <linux/string.h> defines these */
++#define MD5_memcpy memcpy
++#define MD5_memset memset
++#else
++#ifdef HAVEBCOPY
++#define MD5_memcpy(_a,_b,_c) bcopy((_b),(_a),(_c))
++#define MD5_memset(_a,_b,_c) bzero((_a),(_c))
++#else
++static void MD5_memcpy PROTO_LIST ((POINTER, POINTER, unsigned int));
++static void MD5_memset PROTO_LIST ((POINTER, int, unsigned int));
++#endif
++#endif
++static unsigned char PADDING[64] = {
++ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
++};
++
++/* F, G, H and I are basic MD5 functions.
++ */
++#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
++#define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
++#define H(x, y, z) ((x) ^ (y) ^ (z))
++#define I(x, y, z) ((y) ^ ((x) | (~z)))
++
++/* ROTATE_LEFT rotates x left n bits.
++ */
++#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
++
++/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
++Rotation is separate from addition to prevent recomputation.
++ */
++#define FF(a, b, c, d, x, s, ac) { \
++ (a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \
++ (a) = ROTATE_LEFT ((a), (s)); \
++ (a) += (b); \
++ }
++#define GG(a, b, c, d, x, s, ac) { \
++ (a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \
++ (a) = ROTATE_LEFT ((a), (s)); \
++ (a) += (b); \
++ }
++#define HH(a, b, c, d, x, s, ac) { \
++ (a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \
++ (a) = ROTATE_LEFT ((a), (s)); \
++ (a) += (b); \
++ }
++#define II(a, b, c, d, x, s, ac) { \
++ (a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \
++ (a) = ROTATE_LEFT ((a), (s)); \
++ (a) += (b); \
++ }
++
++/*
++ * MD5 initialization. Begins an MD5 operation, writing a new context.
++ */
++void MD5Init(void *vcontext)
++{
++ MD5_CTX *context = vcontext;
++
++ context->count[0] = context->count[1] = 0;
++ /* Load magic initialization constants.
++*/
++ context->state[0] = 0x67452301;
++ context->state[1] = 0xefcdab89;
++ context->state[2] = 0x98badcfe;
++ context->state[3] = 0x10325476;
++}
++
++/* MD5 block update operation. Continues an MD5 message-digest
++ operation, processing another message block, and updating the
++ context.
++ */
++void MD5Update (vcontext, input, inputLen)
++ void *vcontext;
++ unsigned char *input; /* input block */
++ __u32 inputLen; /* length of input block */
++{
++ MD5_CTX *context = vcontext;
++ __u32 i;
++ unsigned int index, partLen;
++
++ /* Compute number of bytes mod 64 */
++ index = (unsigned int)((context->count[0] >> 3) & 0x3F);
++
++ /* Update number of bits */
++ if ((context->count[0] += ((UINT4)inputLen << 3))
++ < ((UINT4)inputLen << 3))
++ context->count[1]++;
++ context->count[1] += ((UINT4)inputLen >> 29);
++
++ partLen = 64 - index;
++
++ /* Transform as many times as possible.
++*/
++ if (inputLen >= partLen) {
++ MD5_memcpy
++ ((POINTER)&context->buffer[index], (POINTER)input, partLen);
++ MD5Transform (context->state, context->buffer);
++
++ for (i = partLen; i + 63 < inputLen; i += 64)
++ MD5Transform (context->state, &input[i]);
++
++ index = 0;
++ }
++ else
++ i = 0;
++
++ /* Buffer remaining input */
++ MD5_memcpy
++ ((POINTER)&context->buffer[index], (POINTER)&input[i],
++ inputLen-i);
++}
++
++/* MD5 finalization. Ends an MD5 message-digest operation, writing the
++ the message digest and zeroizing the context.
++ */
++void MD5Final (digest, vcontext)
++unsigned char digest[16]; /* message digest */
++void *vcontext; /* context */
++{
++ MD5_CTX *context = vcontext;
++ unsigned char bits[8];
++ unsigned int index, padLen;
++
++ /* Save number of bits */
++ Encode (bits, context->count, 8);
++
++ /* Pad out to 56 mod 64.
++*/
++ index = (unsigned int)((context->count[0] >> 3) & 0x3f);
++ padLen = (index < 56) ? (56 - index) : (120 - index);
++ MD5Update (context, PADDING, padLen);
++
++ /* Append length (before padding) */
++ MD5Update (context, bits, 8);
++
++ if (digest != NULL) /* Bill Simpson's padding */
++ {
++ /* store state in digest */
++ Encode (digest, context->state, 16);
++
++ /* Zeroize sensitive information.
++ */
++ MD5_memset ((POINTER)context, 0, sizeof (*context));
++ }
++}
++
++/* MD5 basic transformation. Transforms state based on block.
++ */
++static void MD5Transform (state, block)
++UINT4 state[4];
++unsigned char block[64];
++{
++ UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16];
++
++ Decode (x, block, 64);
++
++ /* Round 1 */
++ FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */
++ FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */
++ FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */
++ FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */
++ FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */
++ FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */
++ FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */
++ FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */
++ FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */
++ FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */
++ FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */
++ FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */
++ FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */
++ FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */
++ FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */
++ FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */
++
++ /* Round 2 */
++ GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */
++ GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */
++ GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */
++ GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */
++ GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */
++ GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */
++ GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */
++ GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */
++ GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */
++ GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */
++ GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */
++ GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */
++ GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */
++ GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */
++ GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */
++ GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */
++
++ /* Round 3 */
++ HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */
++ HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */
++ HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */
++ HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */
++ HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */
++ HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */
++ HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */
++ HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */
++ HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */
++ HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */
++ HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */
++ HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */
++ HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */
++ HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */
++ HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */
++ HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */
++
++ /* Round 4 */
++ II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */
++ II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */
++ II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */
++ II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */
++ II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */
++ II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */
++ II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */
++ II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */
++ II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */
++ II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */
++ II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */
++ II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */
++ II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */
++ II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */
++ II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */
++ II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */
++
++ state[0] += a;
++ state[1] += b;
++ state[2] += c;
++ state[3] += d;
++
++ /* Zeroize sensitive information.
++*/
++ MD5_memset ((POINTER)x, 0, sizeof (x));
++}
++
++#ifndef LITTLEENDIAN
++
++/* Encodes input (UINT4) into output (unsigned char). Assumes len is
++ a multiple of 4.
++ */
++static void Encode (output, input, len)
++unsigned char *output;
++UINT4 *input;
++unsigned int len;
++{
++ unsigned int i, j;
++
++ for (i = 0, j = 0; j < len; i++, j += 4) {
++ output[j] = (unsigned char)(input[i] & 0xff);
++ output[j+1] = (unsigned char)((input[i] >> 8) & 0xff);
++ output[j+2] = (unsigned char)((input[i] >> 16) & 0xff);
++ output[j+3] = (unsigned char)((input[i] >> 24) & 0xff);
++ }
++}
++
++/* Decodes input (unsigned char) into output (UINT4). Assumes len is
++ a multiple of 4.
++ */
++static void Decode (output, input, len)
++UINT4 *output;
++unsigned char *input;
++unsigned int len;
++{
++ unsigned int i, j;
++
++ for (i = 0, j = 0; j < len; i++, j += 4)
++ output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) |
++ (((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24);
++}
++
++#endif
++
++#ifndef HAVEMEMCOPY
++#ifndef HAVEBCOPY
++/* Note: Replace "for loop" with standard memcpy if possible.
++ */
++
++static void MD5_memcpy (output, input, len)
++POINTER output;
++POINTER input;
++unsigned int len;
++{
++ unsigned int i;
++
++ for (i = 0; i < len; i++)
++
++ output[i] = input[i];
++}
++
++/* Note: Replace "for loop" with standard memset if possible.
++ */
++
++static void MD5_memset (output, value, len)
++POINTER output;
++int value;
++unsigned int len;
++{
++ unsigned int i;
++
++ for (i = 0; i < len; i++)
++ ((char *)output)[i] = (char)value;
++}
++#endif
++#endif
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.8 2004/04/06 02:49:26 mcr
++ * pullup of algo code from alg-branch.
++ *
++ * Revision 1.7 2002/09/10 01:45:14 mcr
++ * changed type of MD5_CTX and SHA1_CTX to void * so that
++ * the function prototypes would match, and could be placed
++ * into a pointer to a function.
++ *
++ * Revision 1.6 2002/04/24 07:55:32 mcr
++ * #include patches and Makefiles for post-reorg compilation.
++ *
++ * Revision 1.5 2002/04/24 07:36:28 mcr
++ * Moved from ./klips/net/ipsec/ipsec_md5c.c,v
++ *
++ * Revision 1.4 1999/12/13 13:59:12 rgb
++ * Quick fix to argument size to Update bugs.
++ *
++ * Revision 1.3 1999/05/21 18:09:28 henry
++ * unnecessary <memory.h> include causes trouble in 2.2
++ *
++ * Revision 1.2 1999/04/06 04:54:26 rgb
++ * Fix/Add RCSID Id: and Log: bits to make PHMDs happy. This includes
++ * patch shell fixes.
++ *
++ * Revision 1.1 1998/06/18 21:27:48 henry
++ * move sources from klips/src to klips/net/ipsec, to keep stupid
++ * kernel-build scripts happier in the presence of symlinks
++ *
++ * Revision 1.2 1998/04/23 20:54:02 rgb
++ * Fixed md5 and sha1 include file nesting issues, to be cleaned up when
++ * verified.
++ *
++ * Revision 1.1 1998/04/09 03:06:08 henry
++ * sources moved up from linux/net/ipsec
++ *
++ * Revision 1.1.1.1 1998/04/08 05:35:04 henry
++ * RGB's ipsec-0.8pre2.tar.gz ipsec-0.8
++ *
++ * Revision 0.3 1996/11/20 14:48:53 ji
++ * Release update only.
++ *
++ * Revision 0.2 1996/11/02 00:18:33 ji
++ * First limited release.
++ *
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/net/ipsec/ipsec_proc.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,1127 @@
++/*
++ * @(#) /proc file system interface code.
++ *
++ * Copyright (C) 1996, 1997 John Ioannidis.
++ * Copyright (C) 1998, 1999, 2000, 2001 Richard Guy Briggs <rgb@freeswan.org>
++ * 2001 Michael Richardson <mcr@freeswan.org>
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ * Split out from ipsec_init.c version 1.70.
++ */
++
++char ipsec_proc_c_version[] = "RCSID $Id$";
++
++
++#include <linux/config.h>
++#include <linux/version.h>
++#define __NO_VERSION__
++#include <linux/module.h>
++#include <linux/kernel.h> /* printk() */
++
++#include "openswan/ipsec_param.h"
++
++#ifdef MALLOC_SLAB
++# include <linux/slab.h> /* kmalloc() */
++#else /* MALLOC_SLAB */
++# include <linux/malloc.h> /* kmalloc() */
++#endif /* MALLOC_SLAB */
++#include <linux/errno.h> /* error codes */
++#include <linux/types.h> /* size_t */
++#include <linux/interrupt.h> /* mark_bh */
++
++#include <linux/netdevice.h> /* struct device, and other headers */
++#include <linux/etherdevice.h> /* eth_type_trans */
++#include <linux/ip.h> /* struct iphdr */
++#include <linux/in.h> /* struct sockaddr_in */
++#include <linux/skbuff.h>
++#include <openswan.h>
++#ifdef SPINLOCK
++#ifdef SPINLOCK_23
++#include <linux/spinlock.h> /* *lock* */
++#else /* SPINLOCK_23 */
++#include <asm/spinlock.h> /* *lock* */
++#endif /* SPINLOCK_23 */
++#endif /* SPINLOCK */
++#ifdef NET_21
++#include <asm/uaccess.h>
++#include <linux/in6.h>
++#endif /* NET_21 */
++#include <asm/checksum.h>
++#include <net/ip.h>
++#ifdef CONFIG_PROC_FS
++#include <linux/proc_fs.h>
++#endif /* CONFIG_PROC_FS */
++#ifdef NETLINK_SOCK
++#include <linux/netlink.h>
++#else
++#include <net/netlink.h>
++#endif
++
++#include "openswan/radij.h"
++
++#include "openswan/ipsec_life.h"
++#include "openswan/ipsec_stats.h"
++#include "openswan/ipsec_sa.h"
++
++#include "openswan/ipsec_encap.h"
++#include "openswan/ipsec_radij.h"
++#include "openswan/ipsec_xform.h"
++#include "openswan/ipsec_tunnel.h"
++#include "openswan/ipsec_xmit.h"
++
++#include "openswan/ipsec_rcv.h"
++#include "openswan/ipsec_ah.h"
++#include "openswan/ipsec_esp.h"
++
++#ifdef CONFIG_IPSEC_IPCOMP
++#include "openswan/ipcomp.h"
++#endif /* CONFIG_IPSEC_IPCOMP */
++
++#include "openswan/ipsec_proto.h"
++
++#include <pfkeyv2.h>
++#include <pfkey.h>
++
++#ifdef CONFIG_PROC_FS
++
++#ifdef IPSEC_PROC_SUBDIRS
++static struct proc_dir_entry *proc_net_ipsec_dir = NULL;
++static struct proc_dir_entry *proc_eroute_dir = NULL;
++static struct proc_dir_entry *proc_spi_dir = NULL;
++static struct proc_dir_entry *proc_spigrp_dir = NULL;
++static struct proc_dir_entry *proc_birth_dir = NULL;
++static struct proc_dir_entry *proc_stats_dir = NULL;
++#endif
++
++struct ipsec_birth_reply ipsec_ipv4_birth_packet;
++struct ipsec_birth_reply ipsec_ipv6_birth_packet;
++
++#define DECREMENT_UNSIGNED(X, amount) ((amount < (X)) ? (X)-amount : 0)
++
++extern int ipsec_xform_get_info(char *buffer, char **start,
++ off_t offset, int length IPSEC_PROC_LAST_ARG);
++
++
++/* ipsec_snprintf: like snprintf except
++ * - size is signed and a negative value is treated as if it were 0
++ * - the returned result is never negative --
++ * an error generates a "?" or null output (depending on space).
++ * (Our callers are too lazy to check for an error return.)
++ *
++ * @param buf String buffer
++ * @param size Size of the string
++ * @param fmt printf string
++ * @param ... Variables to be displayed in fmt
++ * @return int Return code
++ */
++int ipsec_snprintf(char *buf, ssize_t size, const char *fmt, ...)
++{
++ va_list args;
++ int i;
++ size_t possize = size < 0? 0 : size;
++ va_start(args, fmt);
++ i = vsnprintf(buf,possize,fmt,args);
++ va_end(args);
++ if (i < 0) {
++ /* create empty output in place of error */
++ i = 0;
++ if (size > 0) {
++ *buf = '\0';
++ }
++ }
++ return i;
++}
++
++
++IPSEC_PROCFS_DEBUG_NO_STATIC
++int
++ipsec_eroute_get_info(char *buffer,
++ char **start,
++ off_t offset,
++ int length IPSEC_PROC_LAST_ARG)
++{
++ struct wsbuf w = {buffer, length, offset, 0, 0};
++
++#ifdef CONFIG_IPSEC_DEBUG
++ if (debug_radij & DB_RJ_DUMPTREES)
++ rj_dumptrees(); /* XXXXXXXXX */
++#endif /* CONFIG_IPSEC_DEBUG */
++
++ KLIPS_PRINT(debug_tunnel & DB_TN_PROCFS,
++ "klips_debug:ipsec_eroute_get_info: "
++ "buffer=0p%p, *start=0p%p, offset=%d, length=%d\n",
++ buffer,
++ *start,
++ (int)offset,
++ length);
++
++ spin_lock_bh(&eroute_lock);
++
++ rj_walktree(rnh, ipsec_rj_walker_procprint, &w);
++/* rj_walktree(mask_rjhead, ipsec_rj_walker_procprint, &w); */
++
++ spin_unlock_bh(&eroute_lock);
++
++ *start = buffer + (offset - w.begin); /* Start of wanted data */
++ return w.len - (offset - w.begin);
++}
++
++IPSEC_PROCFS_DEBUG_NO_STATIC
++int
++ipsec_spi_get_info(char *buffer,
++ char **start,
++ off_t offset,
++ int length IPSEC_PROC_LAST_ARG)
++{
++ const int max_content = length > 0? length-1 : 0;
++ int len = 0;
++ off_t begin = 0;
++ int i;
++ struct ipsec_sa *sa_p;
++ char sa[SATOT_BUF];
++ char buf_s[SUBNETTOA_BUF];
++ char buf_d[SUBNETTOA_BUF];
++ size_t sa_len;
++
++ KLIPS_PRINT(debug_tunnel & DB_TN_PROCFS,
++ "klips_debug:ipsec_spi_get_info: "
++ "buffer=0p%p, *start=0p%p, offset=%d, length=%d\n",
++ buffer,
++ *start,
++ (int)offset,
++ length);
++
++ spin_lock_bh(&tdb_lock);
++
++ for (i = 0; i < SADB_HASHMOD; i++) {
++ for (sa_p = ipsec_sadb_hash[i];
++ sa_p;
++ sa_p = sa_p->ips_hnext) {
++ atomic_inc(&sa_p->ips_refcount);
++ sa_len = satot(&sa_p->ips_said, 'x', sa, sizeof(sa));
++ len += ipsec_snprintf(buffer+len, length-len, "%s ",
++ sa_len ? sa : " (error)");
++
++ len += ipsec_snprintf(buffer+len, length-len, "%s%s%s",
++ IPS_XFORM_NAME(sa_p));
++
++ len += ipsec_snprintf(buffer+len, length-len, ": dir=%s",
++ (sa_p->ips_flags & EMT_INBOUND) ?
++ "in " : "out");
++
++ if(sa_p->ips_addr_s) {
++ addrtoa(((struct sockaddr_in*)(sa_p->ips_addr_s))->sin_addr,
++ 0, buf_s, sizeof(buf_s));
++ len += ipsec_snprintf(buffer+len, length-len, " src=%s",
++ buf_s);
++ }
++
++ if((sa_p->ips_said.proto == IPPROTO_IPIP)
++ && (sa_p->ips_flags & SADB_X_SAFLAGS_INFLOW)) {
++ subnettoa(sa_p->ips_flow_s.u.v4.sin_addr,
++ sa_p->ips_mask_s.u.v4.sin_addr,
++ 0,
++ buf_s,
++ sizeof(buf_s));
++
++ subnettoa(sa_p->ips_flow_d.u.v4.sin_addr,
++ sa_p->ips_mask_d.u.v4.sin_addr,
++ 0,
++ buf_d,
++ sizeof(buf_d));
++
++ len += ipsec_snprintf(buffer+len, length-len, " policy=%s->%s",
++ buf_s, buf_d);
++ }
++
++ if(sa_p->ips_iv_bits) {
++ int j;
++ len += ipsec_snprintf(buffer+len, length-len, " iv_bits=%dbits iv=0x",
++ sa_p->ips_iv_bits);
++
++ for(j = 0; j < sa_p->ips_iv_bits / 8; j++) {
++ len += ipsec_snprintf(buffer+len, length-len, "%02x",
++ (__u32)((__u8*)(sa_p->ips_iv))[j]);
++ }
++ }
++
++ if(sa_p->ips_encalg || sa_p->ips_authalg) {
++ if(sa_p->ips_replaywin) {
++ len += ipsec_snprintf(buffer+len, length-len, " ooowin=%d",
++ sa_p->ips_replaywin);
++ }
++ if(sa_p->ips_errs.ips_replaywin_errs) {
++ len += ipsec_snprintf(buffer+len, length-len, " ooo_errs=%d",
++ sa_p->ips_errs.ips_replaywin_errs);
++ }
++ if(sa_p->ips_replaywin_lastseq) {
++ len += ipsec_snprintf(buffer+len, length-len, " seq=%d",
++ sa_p->ips_replaywin_lastseq);
++ }
++ if(sa_p->ips_replaywin_bitmap) {
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
++ len += ipsec_snprintf(buffer+len, length-len, " bit=0x%Lx",
++ sa_p->ips_replaywin_bitmap);
++#else
++ len += ipsec_snprintf(buffer+len, length-len, " bit=0x%x%08x",
++ (__u32)(sa_p->ips_replaywin_bitmap >> 32),
++ (__u32)sa_p->ips_replaywin_bitmap);
++#endif
++ }
++ if(sa_p->ips_replaywin_maxdiff) {
++ len += ipsec_snprintf(buffer+len, length-len, " max_seq_diff=%d",
++ sa_p->ips_replaywin_maxdiff);
++ }
++ }
++ if(sa_p->ips_flags & ~EMT_INBOUND) {
++ len += ipsec_snprintf(buffer+len, length-len, " flags=0x%x",
++ sa_p->ips_flags & ~EMT_INBOUND);
++ len += ipsec_snprintf(buffer+len, length-len, "<");
++ /* flag printing goes here */
++ len += ipsec_snprintf(buffer+len, length-len, ">");
++ }
++ if(sa_p->ips_auth_bits) {
++ len += ipsec_snprintf(buffer+len, length-len, " alen=%d",
++ sa_p->ips_auth_bits);
++ }
++ if(sa_p->ips_key_bits_a) {
++ len += ipsec_snprintf(buffer+len, length-len, " aklen=%d",
++ sa_p->ips_key_bits_a);
++ }
++ if(sa_p->ips_errs.ips_auth_errs) {
++ len += ipsec_snprintf(buffer+len, length-len, " auth_errs=%d",
++ sa_p->ips_errs.ips_auth_errs);
++ }
++ if(sa_p->ips_key_bits_e) {
++ len += ipsec_snprintf(buffer+len, length-len, " eklen=%d",
++ sa_p->ips_key_bits_e);
++ }
++ if(sa_p->ips_errs.ips_encsize_errs) {
++ len += ipsec_snprintf(buffer+len, length-len, " encr_size_errs=%d",
++ sa_p->ips_errs.ips_encsize_errs);
++ }
++ if(sa_p->ips_errs.ips_encpad_errs) {
++ len += ipsec_snprintf(buffer+len, length-len, " encr_pad_errs=%d",
++ sa_p->ips_errs.ips_encpad_errs);
++ }
++
++ len += ipsec_snprintf(buffer+len, length-len, " life(c,s,h)=");
++
++ len += ipsec_lifetime_format(buffer + len,
++ length - len,
++ "alloc",
++ ipsec_life_countbased,
++ &sa_p->ips_life.ipl_allocations);
++
++ len += ipsec_lifetime_format(buffer + len,
++ length - len,
++ "bytes",
++ ipsec_life_countbased,
++ &sa_p->ips_life.ipl_bytes);
++
++ len += ipsec_lifetime_format(buffer + len,
++ length - len,
++ "addtime",
++ ipsec_life_timebased,
++ &sa_p->ips_life.ipl_addtime);
++
++ len += ipsec_lifetime_format(buffer + len,
++ length - len,
++ "usetime",
++ ipsec_life_timebased,
++ &sa_p->ips_life.ipl_usetime);
++
++ len += ipsec_lifetime_format(buffer + len,
++ length - len,
++ "packets",
++ ipsec_life_countbased,
++ &sa_p->ips_life.ipl_packets);
++
++ if(sa_p->ips_life.ipl_usetime.ipl_last) { /* XXX-MCR should be last? */
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
++ len += ipsec_snprintf(buffer+len, length-len, " idle=%Ld",
++ jiffies / HZ - sa_p->ips_life.ipl_usetime.ipl_last);
++#else
++ len += ipsec_snprintf(buffer+len, length-len, " idle=%lu",
++ jiffies / HZ - (unsigned long)sa_p->ips_life.ipl_usetime.ipl_last);
++#endif
++ }
++
++#ifdef CONFIG_IPSEC_IPCOMP
++ if(sa_p->ips_said.proto == IPPROTO_COMP &&
++ (sa_p->ips_comp_ratio_dbytes ||
++ sa_p->ips_comp_ratio_cbytes)) {
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
++ len += ipsec_snprintf(buffer+len, length-len, " ratio=%Ld:%Ld",
++ sa_p->ips_comp_ratio_dbytes,
++ sa_p->ips_comp_ratio_cbytes);
++#else
++ len += ipsec_snprintf(buffer+len, length-len, " ratio=%lu:%lu",
++ (unsigned long)sa_p->ips_comp_ratio_dbytes,
++ (unsigned long)sa_p->ips_comp_ratio_cbytes);
++#endif
++ }
++#endif /* CONFIG_IPSEC_IPCOMP */
++
++#ifdef CONFIG_IPSEC_NAT_TRAVERSAL
++ if(sa_p->ips_natt_type != 0) {
++ char *natttype_name;
++
++ switch(sa_p->ips_natt_type)
++ {
++ case ESPINUDP_WITH_NON_IKE:
++ natttype_name="nonike";
++ break;
++ case ESPINUDP_WITH_NON_ESP:
++ natttype_name="nonesp";
++ break;
++ default:
++ natttype_name = "unknown";
++ break;
++ }
++
++ len += ipsec_snprintf(buffer + len, length-len, " natencap=%s",
++ natttype_name);
++
++ len += ipsec_snprintf(buffer + len, length-len, " natsport=%d",
++ sa_p->ips_natt_sport);
++
++ len += ipsec_snprintf(buffer + len,length-len, " natdport=%d",
++ sa_p->ips_natt_dport);
++ }
++#endif /* CONFIG_IPSEC_NAT_TRAVERSAL */
++
++ len += ipsec_snprintf(buffer + len,length-len, " refcount=%d",
++ atomic_read(&sa_p->ips_refcount));
++
++ len += ipsec_snprintf(buffer+len, length-len, " ref=%d",
++ sa_p->ips_ref);
++#ifdef CONFIG_IPSEC_DEBUG
++ if(debug_xform) {
++ len += ipsec_snprintf(buffer+len, length-len, " reftable=%lu refentry=%lu",
++ (unsigned long)IPsecSAref2table(sa_p->ips_ref),
++ (unsigned long)IPsecSAref2entry(sa_p->ips_ref));
++ }
++#endif /* CONFIG_IPSEC_DEBUG */
++
++ len += ipsec_snprintf(buffer+len, length-len, "\n");
++
++ atomic_dec(&sa_p->ips_refcount);
++
++ if (len >= max_content) {
++ /* we've done all that can fit -- stop loops */
++ len = max_content; /* truncate crap */
++ goto done_spi_i;
++ } else {
++ const off_t pos = begin + len; /* file position of end of what we've generated */
++
++ if (pos <= offset) {
++ /* all is before first interesting character:
++ * discard, but note where we are.
++ */
++ len = 0;
++ begin = pos;
++ }
++ }
++ }
++ }
++
++done_spi_i:
++ spin_unlock_bh(&tdb_lock);
++
++ *start = buffer + (offset - begin); /* Start of wanted data */
++ return len - (offset - begin);
++}
++
++IPSEC_PROCFS_DEBUG_NO_STATIC
++int
++ipsec_spigrp_get_info(char *buffer,
++ char **start,
++ off_t offset,
++ int length IPSEC_PROC_LAST_ARG)
++{
++ /* Limit of useful snprintf output */
++ const int max_content = length > 0? length-1 : 0;
++
++ int len = 0;
++ off_t begin = 0;
++ int i;
++ struct ipsec_sa *sa_p, *sa_p2;
++ char sa[SATOT_BUF];
++ size_t sa_len;
++
++ KLIPS_PRINT(debug_tunnel & DB_TN_PROCFS,
++ "klips_debug:ipsec_spigrp_get_info: "
++ "buffer=0p%p, *start=0p%p, offset=%d, length=%d\n",
++ buffer,
++ *start,
++ (int)offset,
++ length);
++
++ spin_lock_bh(&tdb_lock);
++
++ for (i = 0; i < SADB_HASHMOD; i++) {
++ for (sa_p = ipsec_sadb_hash[i];
++ sa_p != NULL;
++ sa_p = sa_p->ips_hnext)
++ {
++ atomic_inc(&sa_p->ips_refcount);
++ if(sa_p->ips_inext == NULL) {
++ sa_p2 = sa_p;
++ while(sa_p2 != NULL) {
++ atomic_inc(&sa_p2->ips_refcount);
++ sa_len = satot(&sa_p2->ips_said,
++ 'x', sa, sizeof(sa));
++
++ len += ipsec_snprintf(buffer+len, length-len, "%s ",
++ sa_len ? sa : " (error)");
++ atomic_dec(&sa_p2->ips_refcount);
++ sa_p2 = sa_p2->ips_onext;
++ }
++ len += ipsec_snprintf(buffer+len, length-len, "\n");
++ }
++
++ atomic_dec(&sa_p->ips_refcount);
++
++ if (len >= max_content) {
++ /* we've done all that can fit -- stop loops */
++ len = max_content; /* truncate crap */
++ goto done_spigrp_i;
++ } else {
++ const off_t pos = begin + len;
++
++ if (pos <= offset) {
++ /* all is before first interesting character:
++ * discard, but note where we are.
++ */
++ len = 0;
++ begin = pos;
++ }
++ }
++ }
++ }
++
++done_spigrp_i:
++ spin_unlock_bh(&tdb_lock);
++
++ *start = buffer + (offset - begin); /* Start of wanted data */
++ return len - (offset - begin);
++}
++
++
++IPSEC_PROCFS_DEBUG_NO_STATIC
++int
++ipsec_tncfg_get_info(char *buffer,
++ char **start,
++ off_t offset,
++ int length IPSEC_PROC_LAST_ARG)
++{
++ /* limit of useful snprintf output */
++ const int max_content = length > 0? length-1 : 0;
++ int len = 0;
++ off_t begin = 0;
++ int i;
++ char name[9];
++ struct device *dev, *privdev;
++ struct ipsecpriv *priv;
++
++ KLIPS_PRINT(debug_tunnel & DB_TN_PROCFS,
++ "klips_debug:ipsec_tncfg_get_info: "
++ "buffer=0p%p, *start=0p%p, offset=%d, length=%d\n",
++ buffer,
++ *start,
++ (int)offset,
++ length);
++
++ for(i = 0; i < IPSEC_NUM_IF; i++) {
++ ipsec_snprintf(name, (ssize_t) sizeof(name), IPSEC_DEV_FORMAT, i);
++ dev = __ipsec_dev_get(name);
++ if(dev) {
++ priv = (struct ipsecpriv *)(dev->priv);
++ len += ipsec_snprintf(buffer+len, length-len, "%s",
++ dev->name);
++ if(priv) {
++ privdev = (struct device *)(priv->dev);
++ len += ipsec_snprintf(buffer+len, length-len, " -> %s",
++ privdev ? privdev->name : "NULL");
++ len += ipsec_snprintf(buffer+len, length-len, " mtu=%d(%d) -> %d",
++ dev->mtu,
++ priv->mtu,
++ privdev ? privdev->mtu : 0);
++ } else {
++ KLIPS_PRINT(debug_tunnel & DB_TN_PROCFS,
++ "klips_debug:ipsec_tncfg_get_info: device '%s' has no private data space!\n",
++ dev->name);
++ }
++ len += ipsec_snprintf(buffer+len, length-len, "\n");
++
++ if (len >= max_content) {
++ /* we've done all that can fit -- stop loop */
++ len = max_content; /* truncate crap */
++ break;
++ } else {
++ const off_t pos = begin + len;
++ if (pos <= offset) {
++ len = 0;
++ begin = pos;
++ }
++ }
++ }
++ }
++ *start = buffer + (offset - begin); /* Start of wanted data */
++ len -= (offset - begin); /* Start slop */
++ if (len > length)
++ len = length;
++ return len;
++}
++
++IPSEC_PROCFS_DEBUG_NO_STATIC
++int
++ipsec_version_get_info(char *buffer,
++ char **start,
++ off_t offset,
++ int length IPSEC_PROC_LAST_ARG)
++{
++ int len = 0;
++ off_t begin = 0;
++
++ KLIPS_PRINT(debug_tunnel & DB_TN_PROCFS,
++ "klips_debug:ipsec_version_get_info: "
++ "buffer=0p%p, *start=0p%p, offset=%d, length=%d\n",
++ buffer,
++ *start,
++ (int)offset,
++ length);
++
++ len += ipsec_snprintf(buffer + len,length-len, "Openswan version: %s\n",
++ ipsec_version_code());
++#if 0
++ KLIPS_PRINT(debug_tunnel & DB_TN_PROCFS,
++ "klips_debug:ipsec_version_get_info: "
++ "ipsec_init version: %s\n",
++ ipsec_init_c_version);
++ KLIPS_PRINT(debug_tunnel & DB_TN_PROCFS,
++ "klips_debug:ipsec_version_get_info: "
++ "ipsec_tunnel version: %s\n",
++ ipsec_tunnel_c_version);
++ KLIPS_PRINT(debug_tunnel & DB_TN_PROCFS,
++ "klips_debug:ipsec_version_get_info: "
++ "ipsec_netlink version: %s\n",
++ ipsec_netlink_c_version);
++ KLIPS_PRINT(debug_tunnel & DB_TN_PROCFS,
++ "klips_debug:ipsec_version_get_info: "
++ "radij_c_version: %s\n",
++ radij_c_version);
++#endif
++
++
++ *start = buffer + (offset - begin); /* Start of wanted data */
++ len -= (offset - begin); /* Start slop */
++ if (len > length)
++ len = length;
++ return len;
++}
++
++IPSEC_PROCFS_DEBUG_NO_STATIC
++int
++ipsec_birth_info(char *page,
++ char **start,
++ off_t offset,
++ int count,
++ int *eof,
++ void *data)
++{
++ struct ipsec_birth_reply *ibr = (struct ipsec_birth_reply *)data;
++ int len;
++
++ if(offset >= ibr->packet_template_len) {
++ if(eof) {
++ *eof=1;
++ }
++ return 0;
++ }
++
++ len = ibr->packet_template_len;
++ len -= offset;
++ if (len > count)
++ len = count;
++
++ memcpy(page + offset, ibr->packet_template+offset, len);
++
++ return len;
++}
++
++IPSEC_PROCFS_DEBUG_NO_STATIC
++int
++ipsec_birth_set(struct file *file, const char *buffer,
++ unsigned long count, void *data)
++{
++ struct ipsec_birth_reply *ibr = (struct ipsec_birth_reply *)data;
++ int len;
++
++ MOD_INC_USE_COUNT;
++ if(count > IPSEC_BIRTH_TEMPLATE_MAXLEN) {
++ len = IPSEC_BIRTH_TEMPLATE_MAXLEN;
++ } else {
++ len = count;
++ }
++
++ if(copy_from_user(ibr->packet_template, buffer, len)) {
++ MOD_DEC_USE_COUNT;
++ return -EFAULT;
++ }
++ ibr->packet_template_len = len;
++
++ MOD_DEC_USE_COUNT;
++
++ return len;
++}
++
++
++#ifdef CONFIG_IPSEC_DEBUG
++IPSEC_PROCFS_DEBUG_NO_STATIC
++int
++ipsec_klipsdebug_get_info(char *buffer,
++ char **start,
++ off_t offset,
++ int length IPSEC_PROC_LAST_ARG)
++{
++ int len = 0;
++ off_t begin = 0;
++
++ KLIPS_PRINT(debug_tunnel & DB_TN_PROCFS,
++ "klips_debug:ipsec_klipsdebug_get_info: "
++ "buffer=0p%p, *start=0p%p, offset=%d, length=%d\n",
++ buffer,
++ *start,
++ (int)offset,
++ length);
++
++ len += ipsec_snprintf(buffer+len, length-len, "debug_tunnel=%08x.\n", debug_tunnel);
++ len += ipsec_snprintf(buffer+len, length-len, "debug_xform=%08x.\n", debug_xform);
++ len += ipsec_snprintf(buffer+len, length-len, "debug_eroute=%08x.\n", debug_eroute);
++ len += ipsec_snprintf(buffer+len, length-len, "debug_spi=%08x.\n", debug_spi);
++ len += ipsec_snprintf(buffer+len, length-len, "debug_radij=%08x.\n", debug_radij);
++ len += ipsec_snprintf(buffer+len, length-len, "debug_esp=%08x.\n", debug_esp);
++ len += ipsec_snprintf(buffer+len, length-len, "debug_ah=%08x.\n", debug_ah);
++ len += ipsec_snprintf(buffer+len, length-len, "debug_rcv=%08x.\n", debug_rcv);
++ len += ipsec_snprintf(buffer+len, length-len, "debug_pfkey=%08x.\n", debug_pfkey);
++
++ *start = buffer + (offset - begin); /* Start of wanted data */
++ len -= (offset - begin); /* Start slop */
++ if (len > length)
++ len = length;
++ return len;
++}
++#endif /* CONFIG_IPSEC_DEBUG */
++
++IPSEC_PROCFS_DEBUG_NO_STATIC
++int
++ipsec_stats_get_int_info(char *buffer,
++ char **start,
++ off_t offset,
++ int length,
++ int *eof,
++ void *data)
++{
++
++ const int max_content = length > 0? length-1 : 0;
++ int len = 0;
++ int *thing;
++
++ thing = (int *)data;
++
++ len = ipsec_snprintf(buffer+len, length-len, "%08x\n", *thing);
++
++ if (len >= max_content)
++ len = max_content; /* truncate crap */
++
++ *start = buffer + offset; /* Start of wanted data */
++ return len > offset? len - offset : 0;
++
++}
++
++#ifndef PROC_FS_2325
++struct proc_dir_entry ipsec_eroute =
++{
++ 0,
++ 12, "ipsec_eroute",
++ S_IFREG | S_IRUGO, 1, 0, 0, 0,
++ &proc_net_inode_operations,
++ ipsec_eroute_get_info,
++ NULL, NULL, NULL, NULL, NULL
++};
++
++struct proc_dir_entry ipsec_spi =
++{
++ 0,
++ 9, "ipsec_spi",
++ S_IFREG | S_IRUGO, 1, 0, 0, 0,
++ &proc_net_inode_operations,
++ ipsec_spi_get_info,
++ NULL, NULL, NULL, NULL, NULL
++};
++
++struct proc_dir_entry ipsec_spigrp =
++{
++ 0,
++ 12, "ipsec_spigrp",
++ S_IFREG | S_IRUGO, 1, 0, 0, 0,
++ &proc_net_inode_operations,
++ ipsec_spigrp_get_info,
++ NULL, NULL, NULL, NULL, NULL
++};
++
++struct proc_dir_entry ipsec_tncfg =
++{
++ 0,
++ 11, "ipsec_tncfg",
++ S_IFREG | S_IRUGO, 1, 0, 0, 0,
++ &proc_net_inode_operations,
++ ipsec_tncfg_get_info,
++ NULL, NULL, NULL, NULL, NULL
++};
++
++struct proc_dir_entry ipsec_version =
++{
++ 0,
++ 13, "ipsec_version",
++ S_IFREG | S_IRUGO, 1, 0, 0, 0,
++ &proc_net_inode_operations,
++ ipsec_version_get_info,
++ NULL, NULL, NULL, NULL, NULL
++};
++
++#ifdef CONFIG_IPSEC_DEBUG
++struct proc_dir_entry ipsec_klipsdebug =
++{
++ 0,
++ 16, "ipsec_klipsdebug",
++ S_IFREG | S_IRUGO, 1, 0, 0, 0,
++ &proc_net_inode_operations,
++ ipsec_klipsdebug_get_info,
++ NULL, NULL, NULL, NULL, NULL
++};
++#endif /* CONFIG_IPSEC_DEBUG */
++#endif /* !PROC_FS_2325 */
++#endif /* CONFIG_PROC_FS */
++
++#if defined(PROC_FS_2325)
++struct ipsec_proc_list {
++ char *name;
++ struct proc_dir_entry **parent;
++ struct proc_dir_entry **dir;
++ read_proc_t *readthing;
++ write_proc_t *writething;
++ void *data;
++};
++static struct ipsec_proc_list proc_items[]={
++#ifdef CONFIG_IPSEC_DEBUG
++ {"klipsdebug", &proc_net_ipsec_dir, NULL, ipsec_klipsdebug_get_info, NULL, NULL},
++#endif
++ {"eroute", &proc_net_ipsec_dir, &proc_eroute_dir, NULL, NULL, NULL},
++ {"all", &proc_eroute_dir, NULL, ipsec_eroute_get_info, NULL, NULL},
++ {"spi", &proc_net_ipsec_dir, &proc_spi_dir, NULL, NULL, NULL},
++ {"all", &proc_spi_dir, NULL, ipsec_spi_get_info, NULL, NULL},
++ {"spigrp", &proc_net_ipsec_dir, &proc_spigrp_dir, NULL, NULL, NULL},
++ {"all", &proc_spigrp_dir, NULL, ipsec_spigrp_get_info, NULL, NULL},
++ {"birth", &proc_net_ipsec_dir, &proc_birth_dir, NULL, NULL, NULL},
++ {"ipv4", &proc_birth_dir, NULL, ipsec_birth_info, ipsec_birth_set, (void *)&ipsec_ipv4_birth_packet},
++ {"ipv6", &proc_birth_dir, NULL, ipsec_birth_info, ipsec_birth_set, (void *)&ipsec_ipv6_birth_packet},
++ {"tncfg", &proc_net_ipsec_dir, NULL, ipsec_tncfg_get_info, NULL, NULL},
++ {"xforms", &proc_net_ipsec_dir, NULL, ipsec_xform_get_info, NULL, NULL},
++ {"stats", &proc_net_ipsec_dir, &proc_stats_dir, NULL, NULL, NULL},
++ {"trap_count", &proc_stats_dir, NULL, ipsec_stats_get_int_info, NULL, &ipsec_xmit_trap_count},
++ {"trap_sendcount", &proc_stats_dir, NULL, ipsec_stats_get_int_info, NULL, &ipsec_xmit_trap_sendcount},
++ {"version", &proc_net_ipsec_dir, NULL, ipsec_version_get_info, NULL, NULL},
++ {NULL, NULL, NULL, NULL, NULL, NULL}
++};
++#endif
++
++int
++ipsec_proc_init()
++{
++ int error = 0;
++#ifdef IPSEC_PROC_SUBDIRS
++ struct proc_dir_entry *item;
++#endif
++
++ /*
++ * just complain because pluto won't run without /proc!
++ */
++#ifndef CONFIG_PROC_FS
++#error You must have PROC_FS built in to use KLIPS
++#endif
++
++ /* for 2.0 kernels */
++#if !defined(PROC_FS_2325) && !defined(PROC_FS_21)
++ error |= proc_register_dynamic(&proc_net, &ipsec_eroute);
++ error |= proc_register_dynamic(&proc_net, &ipsec_spi);
++ error |= proc_register_dynamic(&proc_net, &ipsec_spigrp);
++ error |= proc_register_dynamic(&proc_net, &ipsec_tncfg);
++ error |= proc_register_dynamic(&proc_net, &ipsec_version);
++#ifdef CONFIG_IPSEC_DEBUG
++ error |= proc_register_dynamic(&proc_net, &ipsec_klipsdebug);
++#endif /* CONFIG_IPSEC_DEBUG */
++#endif
++
++ /* for 2.2 kernels */
++#if !defined(PROC_FS_2325) && defined(PROC_FS_21)
++ error |= proc_register(proc_net, &ipsec_eroute);
++ error |= proc_register(proc_net, &ipsec_spi);
++ error |= proc_register(proc_net, &ipsec_spigrp);
++ error |= proc_register(proc_net, &ipsec_tncfg);
++ error |= proc_register(proc_net, &ipsec_version);
++#ifdef CONFIG_IPSEC_DEBUG
++ error |= proc_register(proc_net, &ipsec_klipsdebug);
++#endif /* CONFIG_IPSEC_DEBUG */
++#endif
++
++ /* for 2.4 kernels */
++#if defined(PROC_FS_2325)
++ /* create /proc/net/ipsec */
++
++ /* zero these out before we initialize /proc/net/ipsec/birth/stuff */
++ memset(&ipsec_ipv4_birth_packet, 0, sizeof(struct ipsec_birth_reply));
++ memset(&ipsec_ipv6_birth_packet, 0, sizeof(struct ipsec_birth_reply));
++
++ proc_net_ipsec_dir = proc_mkdir("ipsec", proc_net);
++ if(proc_net_ipsec_dir == NULL) {
++ /* no point in continuing */
++ return 1;
++ }
++
++ {
++ struct ipsec_proc_list *it;
++
++ it=proc_items;
++ while(it->name!=NULL) {
++ if(it->dir) {
++ /* make a dir instead */
++ item = proc_mkdir(it->name, *it->parent);
++ *it->dir = item;
++ } else {
++ item = create_proc_entry(it->name, 0400, *it->parent);
++ }
++ if(item) {
++ item->read_proc = it->readthing;
++ item->write_proc = it->writething;
++ item->data = it->data;
++#ifdef MODULE
++ item->owner = THIS_MODULE;
++#endif
++ } else {
++ error |= 1;
++ }
++ it++;
++ }
++ }
++
++ /* now create some symlinks to provide compatibility */
++ proc_symlink("ipsec_eroute", proc_net, "ipsec/eroute/all");
++ proc_symlink("ipsec_spi", proc_net, "ipsec/spi/all");
++ proc_symlink("ipsec_spigrp", proc_net, "ipsec/spigrp/all");
++ proc_symlink("ipsec_tncfg", proc_net, "ipsec/tncfg");
++ proc_symlink("ipsec_version",proc_net, "ipsec/version");
++ proc_symlink("ipsec_klipsdebug",proc_net,"ipsec/klipsdebug");
++
++#endif /* !PROC_FS_2325 */
++
++ return error;
++}
++
++void
++ipsec_proc_cleanup()
++{
++
++ /* for 2.0 and 2.2 kernels */
++#if !defined(PROC_FS_2325)
++
++#ifdef CONFIG_IPSEC_DEBUG
++ if (proc_net_unregister(ipsec_klipsdebug.low_ino) != 0)
++ printk("klips_debug:ipsec_cleanup: "
++ "cannot unregister /proc/net/ipsec_klipsdebug\n");
++#endif /* CONFIG_IPSEC_DEBUG */
++
++ if (proc_net_unregister(ipsec_version.low_ino) != 0)
++ printk("klips_debug:ipsec_cleanup: "
++ "cannot unregister /proc/net/ipsec_version\n");
++ if (proc_net_unregister(ipsec_eroute.low_ino) != 0)
++ printk("klips_debug:ipsec_cleanup: "
++ "cannot unregister /proc/net/ipsec_eroute\n");
++ if (proc_net_unregister(ipsec_spi.low_ino) != 0)
++ printk("klips_debug:ipsec_cleanup: "
++ "cannot unregister /proc/net/ipsec_spi\n");
++ if (proc_net_unregister(ipsec_spigrp.low_ino) != 0)
++ printk("klips_debug:ipsec_cleanup: "
++ "cannot unregister /proc/net/ipsec_spigrp\n");
++ if (proc_net_unregister(ipsec_tncfg.low_ino) != 0)
++ printk("klips_debug:ipsec_cleanup: "
++ "cannot unregister /proc/net/ipsec_tncfg\n");
++#endif
++
++ /* for 2.4 kernels */
++#if defined(PROC_FS_2325)
++ {
++ struct ipsec_proc_list *it;
++
++ /* find end of list */
++ it=proc_items;
++ while(it->name!=NULL) {
++ it++;
++ }
++ it--;
++
++ do {
++ remove_proc_entry(it->name, *it->parent);
++ it--;
++ } while(it > proc_items);
++ }
++
++
++#ifdef CONFIG_IPSEC_DEBUG
++ remove_proc_entry("ipsec_klipsdebug", proc_net);
++#endif /* CONFIG_IPSEC_DEBUG */
++ remove_proc_entry("ipsec_eroute", proc_net);
++ remove_proc_entry("ipsec_spi", proc_net);
++ remove_proc_entry("ipsec_spigrp", proc_net);
++ remove_proc_entry("ipsec_tncfg", proc_net);
++ remove_proc_entry("ipsec_version", proc_net);
++ remove_proc_entry("ipsec", proc_net);
++#endif /* 2.4 kernel */
++}
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.30 2004/04/25 21:23:11 ken
++ * Pull in dhr's changes from FreeS/WAN 2.06
++ *
++ * Revision 1.29 2004/04/06 02:49:26 mcr
++ * pullup of algo code from alg-branch.
++ *
++ * Revision 1.28 2004/03/28 20:29:58 paul
++ * <hugh_> ssize_t, not ssized_t
++ *
++ * Revision 1.27 2004/03/28 20:27:20 paul
++ * Included tested and confirmed fixes mcr made and dhr verified for
++ * snprint statements. Changed one other snprintf to use ipsec_snprintf
++ * so it wouldnt break compatibility with 2.0/2.2 kernels. Verified with
++ * dhr. (thanks dhr!)
++ *
++ * Revision 1.26 2004/02/09 22:07:06 mcr
++ * added information about nat-traversal setting to spi-output.
++ *
++ * Revision 1.25.4.1 2004/04/05 04:30:46 mcr
++ * patches for alg-branch to compile/work with 2.x openswan
++ *
++ * Revision 1.25 2003/10/31 02:27:55 mcr
++ * pulled up port-selector patches and sa_id elimination.
++ *
++ * Revision 1.24.4.1 2003/10/29 01:30:41 mcr
++ * elimited "struct sa_id".
++ *
++ * Revision 1.24 2003/06/20 01:42:21 mcr
++ * added counters to measure how many ACQUIREs we send to pluto,
++ * and how many are successfully sent.
++ *
++ * Revision 1.23 2003/04/03 17:38:09 rgb
++ * Centralised ipsec_kfree_skb and ipsec_dev_{get,put}.
++ *
++ * Revision 1.22 2002/09/20 15:40:57 rgb
++ * Renamed saref macros for consistency and brevity.
++ *
++ * Revision 1.21 2002/09/20 05:01:35 rgb
++ * Print ref and reftable, refentry seperately.
++ *
++ * Revision 1.20 2002/09/19 02:35:39 mcr
++ * do not define structures needed by /proc/net/ipsec/ if we
++ * aren't going create that directory.
++ *
++ * Revision 1.19 2002/09/10 01:43:25 mcr
++ * fixed problem in /-* comment.
++ *
++ * Revision 1.18 2002/09/03 16:22:11 mcr
++ * fixed initialization of birth/stuff values - some simple
++ * screw ups in the code.
++ * removed debugging that was left in by mistake.
++ *
++ * Revision 1.17 2002/09/02 17:54:53 mcr
++ * changed how the table driven /proc entries are created so that
++ * making subdirs is now explicit rather than implicit.
++ *
++ * Revision 1.16 2002/08/30 01:23:37 mcr
++ * reorganized /proc creating code to clear up ifdefs,
++ * make the 2.4 code table driven, and put things into
++ * /proc/net/ipsec subdir. Symlinks are left for compatibility.
++ *
++ * Revision 1.15 2002/08/13 19:01:25 mcr
++ * patches from kenb to permit compilation of FreeSWAN on ia64.
++ * des library patched to use proper DES_LONG type for ia64.
++ *
++ * Revision 1.14 2002/07/26 08:48:31 rgb
++ * Added SA ref table code.
++ *
++ * Revision 1.13 2002/07/24 18:44:54 rgb
++ * Type fiddling to tame ia64 compiler.
++ *
++ * Revision 1.12 2002/05/27 18:56:07 rgb
++ * Convert to dynamic ipsec device allocation.
++ *
++ * Revision 1.11 2002/05/23 07:14:50 rgb
++ * Added refcount code.
++ * Cleaned up %p variants to 0p%p for test suite cleanup.
++ * Convert "usecount" to "refcount" to remove ambiguity.
++ *
++ * Revision 1.10 2002/04/24 07:55:32 mcr
++ * #include patches and Makefiles for post-reorg compilation.
++ *
++ * Revision 1.9 2002/04/24 07:36:28 mcr
++ * Moved from ./klips/net/ipsec/ipsec_proc.c,v
++ *
++ * Revision 1.8 2002/01/29 17:17:55 mcr
++ * moved include of ipsec_param.h to after include of linux/kernel.h
++ * otherwise, it seems that some option that is set in ipsec_param.h
++ * screws up something subtle in the include path to kernel.h, and
++ * it complains on the snprintf() prototype.
++ *
++ * Revision 1.7 2002/01/29 04:00:52 mcr
++ * more excise of kversions.h header.
++ *
++ * Revision 1.6 2002/01/29 02:13:17 mcr
++ * introduction of ipsec_kversion.h means that include of
++ * ipsec_param.h must preceed any decisions about what files to
++ * include to deal with differences in kernel source.
++ *
++ * Revision 1.5 2002/01/12 02:54:30 mcr
++ * beginnings of /proc/net/ipsec dir.
++ *
++ * Revision 1.4 2001/12/11 02:21:05 rgb
++ * Don't include module version here, fixing 2.2 compile bug.
++ *
++ * Revision 1.3 2001/12/05 07:19:44 rgb
++ * Fixed extraneous #include "version.c" bug causing modular KLIPS failure.
++ *
++ * Revision 1.2 2001/11/26 09:16:14 rgb
++ * Merge MCR's ipsec_sa, eroute, proc and struct lifetime changes.
++ *
++ * Revision 1.74 2001/11/22 05:44:11 henry
++ * new version stuff
++ *
++ * Revision 1.1.2.1 2001/09/25 02:19:40 mcr
++ * /proc manipulation code moved to new ipsec_proc.c
++ *
++ *
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/net/ipsec/ipsec_radij.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,869 @@
++/*
++ * Interface between the IPSEC code and the radix (radij) tree code
++ * Copyright (C) 1996, 1997 John Ioannidis.
++ * Copyright (C) 1998, 1999, 2000, 2001 Richard Guy Briggs.
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ * RCSID $Id$
++ */
++
++#include <linux/config.h>
++#include <linux/version.h>
++#include <linux/kernel.h> /* printk() */
++
++#include "openswan/ipsec_param.h"
++
++#ifdef MALLOC_SLAB
++# include <linux/slab.h> /* kmalloc() */
++#else /* MALLOC_SLAB */
++# include <linux/malloc.h> /* kmalloc() */
++#endif /* MALLOC_SLAB */
++#include <linux/errno.h> /* error codes */
++#include <linux/types.h> /* size_t */
++#include <linux/interrupt.h> /* mark_bh */
++
++#include <linux/netdevice.h> /* struct device, struct net_device_stats and other headers */
++#include <linux/etherdevice.h> /* eth_type_trans */
++#include <linux/ip.h> /* struct iphdr */
++#include <linux/skbuff.h>
++#include <openswan.h>
++#ifdef SPINLOCK
++# ifdef SPINLOCK_23
++# include <linux/spinlock.h> /* *lock* */
++# else /* 23_SPINLOCK */
++# include <asm/spinlock.h> /* *lock* */
++# endif /* 23_SPINLOCK */
++#endif /* SPINLOCK */
++#ifdef NET_21
++# include <asm/uaccess.h>
++# include <linux/in6.h>
++#endif
++#include <asm/checksum.h>
++#include <net/ip.h>
++
++#include "openswan/ipsec_eroute.h"
++#include "openswan/ipsec_sa.h"
++
++#include "openswan/radij.h"
++#include "openswan/ipsec_encap.h"
++#include "openswan/radij.h"
++#include "openswan/ipsec_encap.h"
++#include "openswan/ipsec_radij.h"
++#include "openswan/ipsec_tunnel.h" /* struct ipsecpriv */
++#include "openswan/ipsec_xform.h"
++
++#include <pfkeyv2.h>
++#include <pfkey.h>
++
++#include "openswan/ipsec_proto.h"
++
++#ifdef CONFIG_IPSEC_DEBUG
++int debug_radij = 0;
++#endif /* CONFIG_IPSEC_DEBUG */
++
++struct radij_node_head *rnh = NULL;
++#ifdef SPINLOCK
++spinlock_t eroute_lock = SPIN_LOCK_UNLOCKED;
++#else /* SPINLOCK */
++spinlock_t eroute_lock;
++#endif /* SPINLOCK */
++
++int
++ipsec_radijinit(void)
++{
++ maj_keylen = sizeof (struct sockaddr_encap);
++
++ rj_init();
++
++ if (rj_inithead((void **)&rnh, /*16*/offsetof(struct sockaddr_encap, sen_type) * sizeof(__u8)) == 0) /* 16 is bit offset of sen_type */
++ return -1;
++ return 0;
++}
++
++int
++ipsec_radijcleanup(void)
++{
++ int error;
++
++ spin_lock_bh(&eroute_lock);
++
++ error = radijcleanup();
++
++ spin_unlock_bh(&eroute_lock);
++
++ return error;
++}
++
++int
++ipsec_cleareroutes(void)
++{
++ int error;
++
++ spin_lock_bh(&eroute_lock);
++
++ error = radijcleartree();
++
++ spin_unlock_bh(&eroute_lock);
++
++ return error;
++}
++
++int
++ipsec_breakroute(struct sockaddr_encap *eaddr,
++ struct sockaddr_encap *emask,
++ struct sk_buff **first,
++ struct sk_buff **last)
++{
++ struct eroute *ro;
++ struct radij_node *rn;
++ int error;
++#ifdef CONFIG_IPSEC_DEBUG
++
++ if (debug_eroute) {
++ char buf1[SUBNETTOA_BUF], buf2[SUBNETTOA_BUF];
++ subnettoa(eaddr->sen_ip_src, emask->sen_ip_src, 0, buf1, sizeof(buf1));
++ subnettoa(eaddr->sen_ip_dst, emask->sen_ip_dst, 0, buf2, sizeof(buf2));
++ KLIPS_PRINT(debug_eroute,
++ "klips_debug:ipsec_breakroute: "
++ "attempting to delete eroute for %s:%d->%s:%d %d\n",
++ buf1, ntohs(eaddr->sen_sport),
++ buf2, ntohs(eaddr->sen_dport), eaddr->sen_proto);
++ }
++#endif /* CONFIG_IPSEC_DEBUG */
++
++ spin_lock_bh(&eroute_lock);
++
++ if ((error = rj_delete(eaddr, emask, rnh, &rn)) != 0) {
++ spin_unlock_bh(&eroute_lock);
++ KLIPS_PRINT(debug_eroute,
++ "klips_debug:ipsec_breakroute: "
++ "node not found, eroute delete failed.\n");
++ return error;
++ }
++
++ spin_unlock_bh(&eroute_lock);
++
++ ro = (struct eroute *)rn;
++
++ KLIPS_PRINT(debug_eroute,
++ "klips_debug:ipsec_breakroute: "
++ "deleted eroute=0p%p, ident=0p%p->0p%p, first=0p%p, last=0p%p\n",
++ ro,
++ ro->er_ident_s.data,
++ ro->er_ident_d.data,
++ ro->er_first,
++ ro->er_last);
++
++ if (ro->er_ident_s.data != NULL) {
++ kfree(ro->er_ident_s.data);
++ }
++ if (ro->er_ident_d.data != NULL) {
++ kfree(ro->er_ident_d.data);
++ }
++ if (ro->er_first != NULL) {
++#if 0
++ struct net_device_stats *stats = (struct net_device_stats *) &(((struct ipsecpriv *)(ro->er_first->dev->priv))->mystats);
++ stats->tx_dropped--;
++#endif
++ *first = ro->er_first;
++ }
++ if (ro->er_last != NULL) {
++#if 0
++ struct net_device_stats *stats = (struct net_device_stats *) &(((struct ipsecpriv *)(ro->er_last->dev->priv))->mystats);
++ stats->tx_dropped--;
++#endif
++ *last = ro->er_last;
++ }
++
++ if (rn->rj_flags & (RJF_ACTIVE | RJF_ROOT))
++ panic ("ipsec_breakroute RMT_DELEROUTE root or active node\n");
++ memset((caddr_t)rn, 0, sizeof (struct eroute));
++ kfree(rn);
++
++ return 0;
++}
++
++int
++ipsec_makeroute(struct sockaddr_encap *eaddr,
++ struct sockaddr_encap *emask,
++ ip_said said,
++ uint32_t pid,
++ struct sk_buff *skb,
++ struct ident *ident_s,
++ struct ident *ident_d)
++{
++ struct eroute *retrt;
++ int error;
++ char sa[SATOT_BUF];
++ size_t sa_len;
++
++#ifdef CONFIG_IPSEC_DEBUG
++
++ if (debug_eroute) {
++
++ {
++ char buf1[SUBNETTOA_BUF], buf2[SUBNETTOA_BUF];
++
++ subnettoa(eaddr->sen_ip_src, emask->sen_ip_src, 0, buf1, sizeof(buf1));
++ subnettoa(eaddr->sen_ip_dst, emask->sen_ip_dst, 0, buf2, sizeof(buf2));
++ sa_len = satot(&said, 0, sa, sizeof(sa));
++ KLIPS_PRINT(debug_eroute,
++ "klips_debug:ipsec_makeroute: "
++ "attempting to allocate %lu bytes to insert eroute for %s->%s, SA: %s, PID:%d, skb=0p%p, ident:%s->%s\n",
++ (unsigned long) sizeof(struct eroute),
++ buf1,
++ buf2,
++ sa_len ? sa : " (error)",
++ pid,
++ skb,
++ (ident_s ? (ident_s->data ? ident_s->data : "NULL") : "NULL"),
++ (ident_d ? (ident_d->data ? ident_d->data : "NULL") : "NULL"));
++ }
++ {
++ char buf1[sizeof(struct sockaddr_encap)*2 + 1],
++ buf2[sizeof(struct sockaddr_encap)*2 + 1];
++ int i;
++ unsigned char *b1 = buf1,
++ *b2 = buf2,
++ *ea = (unsigned char *)eaddr,
++ *em = (unsigned char *)emask;
++
++
++ for (i=0; i<sizeof(struct sockaddr_encap); i++) {
++ sprintf(b1, "%02x", ea[i]);
++ sprintf(b2, "%02x", em[i]);
++ b1+=2;
++ b2+=2;
++ }
++ KLIPS_PRINT(debug_eroute, "klips_debug:ipsec_makeroute: %s / %s \n", buf1, buf2);
++ }
++
++ }
++#endif /* CONFIG_IPSEC_DEBUG */
++
++ retrt = (struct eroute *)kmalloc(sizeof (struct eroute), GFP_ATOMIC);
++ if (retrt == NULL) {
++ printk("klips_error:ipsec_makeroute: "
++ "not able to allocate kernel memory");
++ return -ENOMEM;
++ }
++ memset((caddr_t)retrt, 0, sizeof (struct eroute));
++
++ retrt->er_eaddr = *eaddr;
++ retrt->er_emask = *emask;
++ retrt->er_said = said;
++ retrt->er_pid = pid;
++ retrt->er_count = 0;
++ retrt->er_lasttime = jiffies/HZ;
++ rd_key((&(retrt->er_rjt))) = &(retrt->er_eaddr);
++
++ if (ident_s && ident_s->type != SADB_IDENTTYPE_RESERVED) {
++ int data_len = ident_s->len * IPSEC_PFKEYv2_ALIGN - sizeof(struct sadb_ident);
++
++ retrt->er_ident_s.type = ident_s->type;
++ retrt->er_ident_s.id = ident_s->id;
++ retrt->er_ident_s.len = ident_s->len;
++ if(data_len) {
++ KLIPS_PRINT(debug_eroute,
++ "klips_debug:ipsec_makeroute: "
++ "attempting to allocate %u bytes for ident_s.\n",
++ data_len);
++ if(!(retrt->er_ident_s.data = kmalloc(data_len, GFP_KERNEL))) {
++ kfree(retrt);
++ printk("klips_error:ipsec_makeroute: not able to allocate kernel memory (%d)\n", data_len);
++ return ENOMEM;
++ }
++ memcpy(retrt->er_ident_s.data, ident_s->data, data_len);
++ } else {
++ retrt->er_ident_s.data = NULL;
++ }
++ }
++
++ if (ident_d && ident_d->type != SADB_IDENTTYPE_RESERVED) {
++ int data_len = ident_d->len * IPSEC_PFKEYv2_ALIGN - sizeof(struct sadb_ident);
++
++ retrt->er_ident_d.type = ident_d->type;
++ retrt->er_ident_d.id = ident_d->id;
++ retrt->er_ident_d.len = ident_d->len;
++ if(data_len) {
++ KLIPS_PRINT(debug_eroute,
++ "klips_debug:ipsec_makeroute: "
++ "attempting to allocate %u bytes for ident_d.\n",
++ data_len);
++ if(!(retrt->er_ident_d.data = kmalloc(data_len, GFP_KERNEL))) {
++ if (retrt->er_ident_s.data)
++ kfree(retrt->er_ident_s.data);
++ kfree(retrt);
++ printk("klips_error:ipsec_makeroute: not able to allocate kernel memory (%d)\n", data_len);
++ return ENOMEM;
++ }
++ memcpy(retrt->er_ident_d.data, ident_d->data, data_len);
++ } else {
++ retrt->er_ident_d.data = NULL;
++ }
++ }
++ retrt->er_first = skb;
++ retrt->er_last = NULL;
++
++ KLIPS_PRINT(debug_eroute,
++ "klips_debug:ipsec_makeroute: "
++ "calling rj_addroute now\n");
++
++ spin_lock_bh(&eroute_lock);
++
++ error = rj_addroute(&(retrt->er_eaddr), &(retrt->er_emask),
++ rnh, retrt->er_rjt.rd_nodes);
++
++ spin_unlock_bh(&eroute_lock);
++
++ if(error) {
++ sa_len = satot(&said, 0, sa, sizeof(sa));
++ KLIPS_PRINT(debug_eroute,
++ "klips_debug:ipsec_makeroute: "
++ "rj_addroute not able to insert eroute for SA:%s (error:%d)\n",
++ sa_len ? sa : " (error)", error);
++ if (retrt->er_ident_s.data)
++ kfree(retrt->er_ident_s.data);
++ if (retrt->er_ident_d.data)
++ kfree(retrt->er_ident_d.data);
++
++ kfree(retrt);
++
++ return error;
++ }
++
++#ifdef CONFIG_IPSEC_DEBUG
++ if (debug_eroute) {
++ char buf1[SUBNETTOA_BUF], buf2[SUBNETTOA_BUF];
++/*
++ subnettoa(eaddr->sen_ip_src, emask->sen_ip_src, 0, buf1, sizeof(buf1));
++ subnettoa(eaddr->sen_ip_dst, emask->sen_ip_dst, 0, buf2, sizeof(buf2));
++*/
++ subnettoa(rd_key((&(retrt->er_rjt)))->sen_ip_src, rd_mask((&(retrt->er_rjt)))->sen_ip_src, 0, buf1, sizeof(buf1));
++ subnettoa(rd_key((&(retrt->er_rjt)))->sen_ip_dst, rd_mask((&(retrt->er_rjt)))->sen_ip_dst, 0, buf2, sizeof(buf2));
++ sa_len = satot(&retrt->er_said, 0, sa, sizeof(sa));
++
++ KLIPS_PRINT(debug_eroute,
++ "klips_debug:ipsec_makeroute: "
++ "pid=%05d "
++ "count=%10d "
++ "lasttime=%6d "
++ "%-18s -> %-18s => %s\n",
++ retrt->er_pid,
++ retrt->er_count,
++ (int)(jiffies/HZ - retrt->er_lasttime),
++ buf1,
++ buf2,
++ sa_len ? sa : " (error)");
++ }
++#endif /* CONFIG_IPSEC_DEBUG */
++ KLIPS_PRINT(debug_eroute,
++ "klips_debug:ipsec_makeroute: "
++ "succeeded.\n");
++ return 0;
++}
++
++struct eroute *
++ipsec_findroute(struct sockaddr_encap *eaddr)
++{
++ struct radij_node *rn;
++#ifdef CONFIG_IPSEC_DEBUG
++ char buf1[ADDRTOA_BUF], buf2[ADDRTOA_BUF];
++
++ if (debug_radij & DB_RJ_FINDROUTE) {
++ addrtoa(eaddr->sen_ip_src, 0, buf1, sizeof(buf1));
++ addrtoa(eaddr->sen_ip_dst, 0, buf2, sizeof(buf2));
++ KLIPS_PRINT(debug_eroute,
++ "klips_debug:ipsec_findroute: "
++ "%s:%d->%s:%d %d\n",
++ buf1, ntohs(eaddr->sen_sport),
++ buf2, ntohs(eaddr->sen_dport),
++ eaddr->sen_proto);
++ }
++#endif /* CONFIG_IPSEC_DEBUG */
++ rn = rj_match((caddr_t)eaddr, rnh);
++ if(rn) {
++ KLIPS_PRINT(debug_eroute && sysctl_ipsec_debug_verbose,
++ "klips_debug:ipsec_findroute: "
++ "found, points to proto=%d, spi=%x, dst=%x.\n",
++ ((struct eroute*)rn)->er_said.proto,
++ ntohl(((struct eroute*)rn)->er_said.spi),
++ ntohl(((struct eroute*)rn)->er_said.dst.u.v4.sin_addr.s_addr));
++ }
++ return (struct eroute *)rn;
++}
++
++#ifdef CONFIG_PROC_FS
++/** ipsec_rj_walker_procprint: print one line of eroute table output.
++ *
++ * Theoretical BUG: if w->length is less than the length
++ * of some line we should produce, that line will never
++ * be finished. In effect, the "file" will stop part way
++ * through that line.
++ */
++int
++ipsec_rj_walker_procprint(struct radij_node *rn, void *w0)
++{
++ struct eroute *ro = (struct eroute *)rn;
++ struct rjtentry *rd = (struct rjtentry *)rn;
++ struct wsbuf *w = (struct wsbuf *)w0;
++ char buf1[SUBNETTOA_BUF], buf2[SUBNETTOA_BUF];
++ char buf3[16];
++ char sa[SATOT_BUF];
++ size_t sa_len, buf_len;
++ struct sockaddr_encap *key, *mask;
++
++ KLIPS_PRINT(debug_radij,
++ "klips_debug:ipsec_rj_walker_procprint: "
++ "rn=0p%p, w0=0p%p\n",
++ rn,
++ w0);
++ if (rn->rj_b >= 0) {
++ return 0;
++ }
++
++ key = rd_key(rd);
++ mask = rd_mask(rd);
++
++ if (key == NULL || mask == NULL) {
++ return 0;
++ }
++
++ buf_len = subnettoa(key->sen_ip_src, mask->sen_ip_src, 0, buf1, sizeof(buf1));
++ if(key->sen_sport != 0) {
++ sprintf(buf1+buf_len-1, ":%d", ntohs(key->sen_sport));
++ }
++
++ buf_len = subnettoa(key->sen_ip_dst, mask->sen_ip_dst, 0, buf2, sizeof(buf2));
++ if(key->sen_dport != 0) {
++ sprintf(buf2+buf_len-1, ":%d", ntohs(key->sen_dport));
++ }
++
++ buf3[0]='\0';
++ if(key->sen_proto != 0) {
++ sprintf(buf3, ":%d", key->sen_proto);
++ }
++
++ sa_len = satot(&ro->er_said, 'x', sa, sizeof(sa));
++ w->len += ipsec_snprintf(w->buffer + w->len,
++ w->length - w->len,
++ "%-10d "
++ "%-18s -> %-18s => %s%s\n",
++ ro->er_count,
++ buf1,
++ buf2,
++ sa_len ? sa : " (error)",
++ buf3);
++
++ {
++ /* snprintf can only fill the last character with NUL
++ * so the maximum useful character is w->length-1.
++ * However, if w->length == 0, we cannot go back.
++ * (w->length surely cannot be negative.)
++ */
++ int max_content = w->length > 0? w->length-1 : 0;
++
++ if (w->len >= max_content) {
++ /* we've done all that can fit -- stop treewalking */
++ w->len = max_content; /* truncate crap */
++ return -ENOBUFS;
++ } else {
++ const off_t pos = w->begin + w->len; /* file position of end of what we've generated */
++
++ if (pos <= w->offset) {
++ /* all is before first interesting character:
++ * discard, but note where we are.
++ */
++ w->len = 0;
++ w->begin = pos;
++ }
++ return 0;
++ }
++ }
++}
++#endif /* CONFIG_PROC_FS */
++
++int
++ipsec_rj_walker_delete(struct radij_node *rn, void *w0)
++{
++ struct eroute *ro;
++ struct rjtentry *rd = (struct rjtentry *)rn;
++ struct radij_node *rn2;
++ int error;
++ struct sockaddr_encap *key, *mask;
++
++ key = rd_key(rd);
++ mask = rd_mask(rd);
++
++ if(!key || !mask) {
++ return -ENODATA;
++ }
++#ifdef CONFIG_IPSEC_DEBUG
++ if(debug_radij) {
++ char buf1[SUBNETTOA_BUF], buf2[SUBNETTOA_BUF];
++ subnettoa(key->sen_ip_src, mask->sen_ip_src, 0, buf1, sizeof(buf1));
++ subnettoa(key->sen_ip_dst, mask->sen_ip_dst, 0, buf2, sizeof(buf2));
++ KLIPS_PRINT(debug_radij,
++ "klips_debug:ipsec_rj_walker_delete: "
++ "deleting: %s -> %s\n",
++ buf1,
++ buf2);
++ }
++#endif /* CONFIG_IPSEC_DEBUG */
++
++ if((error = rj_delete(key, mask, rnh, &rn2))) {
++ KLIPS_PRINT(debug_radij,
++ "klips_debug:ipsec_rj_walker_delete: "
++ "rj_delete failed with error=%d.\n", error);
++ return error;
++ }
++
++ if(rn2 != rn) {
++ printk("klips_debug:ipsec_rj_walker_delete: "
++ "tried to delete a different node?!? This should never happen!\n");
++ }
++
++ ro = (struct eroute *)rn;
++
++ if (ro->er_ident_s.data)
++ kfree(ro->er_ident_s.data);
++ if (ro->er_ident_d.data)
++ kfree(ro->er_ident_d.data);
++
++ memset((caddr_t)rn, 0, sizeof (struct eroute));
++ kfree(rn);
++
++ return 0;
++}
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.70 2004/04/25 21:10:52 ken
++ * Pull in dhr's changes from FreeS/WAN 2.06
++ *
++ * Revision 1.69 2004/04/06 02:49:26 mcr
++ * pullup of algo code from alg-branch.
++ *
++ * Revision 1.68 2004/03/28 20:27:20 paul
++ * Included tested and confirmed fixes mcr made and dhr verified for
++ * snprint statements. Changed one other snprintf to use ipsec_snprintf
++ * so it wouldnt break compatibility with 2.0/2.2 kernels. Verified with
++ * dhr. (thanks dhr!)
++ *
++ * Revision 1.67.4.1 2004/04/05 04:30:46 mcr
++ * patches for alg-branch to compile/work with 2.x openswan
++ *
++ * Revision 1.67 2003/10/31 02:27:55 mcr
++ * pulled up port-selector patches and sa_id elimination.
++ *
++ * Revision 1.66.24.2 2003/10/29 01:30:41 mcr
++ * elimited "struct sa_id".
++ *
++ * Revision 1.66.24.1 2003/09/21 13:59:56 mcr
++ * pre-liminary X.509 patch - does not yet pass tests.
++ *
++ * Revision 1.66 2002/10/12 23:11:53 dhr
++ *
++ * [KenB + DHR] more 64-bit cleanup
++ *
++ * Revision 1.65 2002/09/20 05:01:40 rgb
++ * Added memory allocation debugging.
++ *
++ * Revision 1.64 2002/05/31 01:46:05 mcr
++ * added && sysctl_ipsec_debug_verbose verbose to ipsec_findroute
++ * as requested in PR#14.
++ *
++ * Revision 1.63 2002/05/23 07:14:11 rgb
++ * Cleaned up %p variants to 0p%p for test suite cleanup.
++ *
++ * Revision 1.62 2002/04/24 07:55:32 mcr
++ * #include patches and Makefiles for post-reorg compilation.
++ *
++ * Revision 1.61 2002/04/24 07:36:29 mcr
++ * Moved from ./klips/net/ipsec/ipsec_radij.c,v
++ *
++ * Revision 1.60 2002/02/19 23:59:45 rgb
++ * Removed redundant compiler directives.
++ *
++ * Revision 1.59 2002/02/06 04:13:47 mcr
++ * missing #ifdef CONFIG_IPSEC_DEBUG.
++ *
++ * Revision 1.58 2002/01/29 17:17:56 mcr
++ * moved include of ipsec_param.h to after include of linux/kernel.h
++ * otherwise, it seems that some option that is set in ipsec_param.h
++ * screws up something subtle in the include path to kernel.h, and
++ * it complains on the snprintf() prototype.
++ *
++ * Revision 1.57 2002/01/29 04:00:52 mcr
++ * more excise of kversions.h header.
++ *
++ * Revision 1.56 2002/01/29 02:13:17 mcr
++ * introduction of ipsec_kversion.h means that include of
++ * ipsec_param.h must preceed any decisions about what files to
++ * include to deal with differences in kernel source.
++ *
++ * Revision 1.55 2001/11/26 09:23:48 rgb
++ * Merge MCR's ipsec_sa, eroute, proc and struct lifetime changes.
++ *
++ * Revision 1.53.2.1 2001/09/25 02:26:32 mcr
++ * headers adjusted for new usage.
++ *
++ * Revision 1.54 2001/10/18 04:45:20 rgb
++ * 2.4.9 kernel deprecates linux/malloc.h in favour of linux/slab.h,
++ * lib/freeswan.h version macros moved to lib/kversions.h.
++ * Other compiler directive cleanups.
++ *
++ * Revision 1.53 2001/09/19 17:19:40 rgb
++ * Debug output bugfix for NetCelo's PF_KEY ident patch.
++ *
++ * Revision 1.52 2001/09/19 16:33:37 rgb
++ * Temporarily disable ident fields to /proc/net/ipsec_eroute.
++ *
++ * Revision 1.51 2001/09/15 16:24:04 rgb
++ * Re-inject first and last HOLD packet when an eroute REPLACE is done.
++ *
++ * Revision 1.50 2001/09/14 16:58:36 rgb
++ * Added support for storing the first and last packets through a HOLD.
++ *
++ * Revision 1.49 2001/09/08 21:13:32 rgb
++ * Added pfkey ident extension support for ISAKMPd. (NetCelo)
++ *
++ * Revision 1.48 2001/06/15 04:12:56 rgb
++ * Fixed kernel memory allocation error return code polarity bug.
++ *
++ * Revision 1.47 2001/06/14 19:35:09 rgb
++ * Update copyright date.
++ *
++ * Revision 1.46 2001/06/08 08:47:18 rgb
++ * Fixed for debug disabled.
++ *
++ * Revision 1.45 2001/05/27 06:12:11 rgb
++ * Added structures for pid, packet count and last access time to eroute.
++ * Added packet count to beginning of /proc/net/ipsec_eroute.
++ *
++ * Revision 1.44 2001/05/03 19:41:01 rgb
++ * Initialise error return variable.
++ * Use more appropriate return value for ipsec_rj_walker_delete().
++ *
++ * Revision 1.43 2001/02/27 22:24:54 rgb
++ * Re-formatting debug output (line-splitting, joining, 1arg/line).
++ * Check for satoa() return codes.
++ *
++ * Revision 1.42 2001/02/27 06:21:57 rgb
++ * Added findroute success instrumentation.
++ *
++ * Revision 1.41 2000/11/06 04:32:08 rgb
++ * Ditched spin_lock_irqsave in favour of spin_lock_bh.
++ *
++ * Revision 1.40 2000/09/08 19:12:56 rgb
++ * Change references from DEBUG_IPSEC to CONFIG_IPSEC_DEBUG.
++ *
++ * Revision 1.39 2000/08/30 05:25:20 rgb
++ * Correct debug text in ipsec_breakroute() from incorrect
++ * "ipsec_callback".
++ *
++ * Revision 1.38 2000/07/28 14:58:31 rgb
++ * Changed kfree_s to kfree, eliminating extra arg to fix 2.4.0-test5.
++ *
++ * Revision 1.37 2000/03/16 14:02:50 rgb
++ * Fixed debug scope to enable compilation with debug off.
++ *
++ * Revision 1.36 2000/01/21 06:14:46 rgb
++ * Added debugging text to ipsec_rj_walker_delete().
++ * Set return code to negative for consistency.
++ *
++ * Revision 1.35 1999/11/23 23:05:24 rgb
++ * Use provided macro ADDRTOA_BUF instead of hardcoded value.
++ *
++ * Revision 1.34 1999/11/18 04:13:56 rgb
++ * Replaced all kernel version macros to shorter, readable form.
++ * Added CONFIG_PROC_FS compiler directives in case it is shut off.
++ *
++ * Revision 1.33 1999/11/17 15:53:39 rgb
++ * Changed all occurrences of #include "../../../lib/freeswan.h"
++ * to #include <freeswan.h> which works due to -Ilibfreeswan in the
++ * klips/net/ipsec/Makefile.
++ *
++ * Revision 1.32 1999/10/26 13:58:33 rgb
++ * Put spinlock flags variable declaration outside the debug compiler
++ * directive to enable compilation with debug shut off.
++ *
++ * Revision 1.31 1999/10/15 22:13:29 rgb
++ * Clean out cruft.
++ * Align /proc/net/ipsec_eroute output for easier readability.
++ * Fix double linefeed in radij debug output.
++ * Fix double locking bug that locks up 2.0.36 but not 2.0.38.
++ *
++ * Revision 1.30 1999/10/08 18:37:33 rgb
++ * Fix end-of-line spacing to sate whining PHMs.
++ *
++ * Revision 1.29 1999/10/03 18:52:45 rgb
++ * Spinlock support for 2.0.xx.
++ * Dumb return code spin_unlock fix.
++ *
++ * Revision 1.28 1999/10/01 16:22:24 rgb
++ * Switch from assignment init. to functional init. of spinlocks.
++ *
++ * Revision 1.27 1999/10/01 15:44:53 rgb
++ * Move spinlock header include to 2.1> scope.
++ *
++ * Revision 1.26 1999/10/01 00:01:23 rgb
++ * Added eroute structure locking.
++ *
++ * Revision 1.25 1999/06/10 16:07:30 rgb
++ * Silence delete eroute on no debug.
++ *
++ * Revision 1.24 1999/05/09 03:25:36 rgb
++ * Fix bug introduced by 2.2 quick-and-dirty patch.
++ *
++ * Revision 1.23 1999/05/05 22:02:31 rgb
++ * Add a quick and dirty port to 2.2 kernels by Marc Boucher <marc@mbsi.ca>.
++ *
++ * Revision 1.22 1999/04/29 15:17:23 rgb
++ * Add return values to init and cleanup functions.
++ * Add sanity checking for null pointer arguments.
++ *
++ * Revision 1.21 1999/04/11 00:28:58 henry
++ * GPL boilerplate
++ *
++ * Revision 1.20 1999/04/06 04:54:26 rgb
++ * Fix/Add RCSID Id: and Log: bits to make PHMDs happy. This includes
++ * patch shell fixes.
++ *
++ * Revision 1.19 1999/02/17 16:50:35 rgb
++ * Clean out unused cruft.
++ * Consolidate for space and speed efficiency.
++ * Convert DEBUG_IPSEC to KLIPS_PRINT
++ *
++ * Revision 1.18 1999/01/22 06:22:06 rgb
++ * Cruft clean-out.
++ * 64-bit clean-up.
++ *
++ * Revision 1.17 1998/12/02 03:09:39 rgb
++ * Clean up debug printing conditionals to compile with debugging off.
++ *
++ * Revision 1.16 1998/12/01 13:49:39 rgb
++ * Wrap version info printing in debug switches.
++ *
++ * Revision 1.15 1998/11/30 13:22:54 rgb
++ * Rationalised all the klips kernel file headers. They are much shorter
++ * now and won't conflict under RH5.2.
++ *
++ * Revision 1.14 1998/10/31 06:48:17 rgb
++ * Fixed up comments in #endif directives.
++ *
++ * Revision 1.13 1998/10/27 13:48:09 rgb
++ * Cleaned up /proc/net/ipsec_* filesystem for easy parsing by scripts.
++ * Fixed less(1) truncated output bug.
++ * Code clean-up.
++ *
++ * Revision 1.12 1998/10/25 02:41:36 rgb
++ * Change return type on ipsec_breakroute and ipsec_makeroute and add an
++ * argument to be able to transmit more infomation about errors.
++ * Fix cut-and-paste debug statement identifier.
++ *
++ * Revision 1.11 1998/10/22 06:45:39 rgb
++ * Cleaned up cruft.
++ * Convert to use satoa for printk.
++ *
++ * Revision 1.10 1998/10/19 14:44:28 rgb
++ * Added inclusion of freeswan.h.
++ * sa_id structure implemented and used: now includes protocol.
++ *
++ * Revision 1.9 1998/10/09 04:30:52 rgb
++ * Added 'klips_debug' prefix to all klips printk debug statements.
++ * Deleted old commented out cruft.
++ *
++ * Revision 1.8 1998/08/06 17:24:23 rgb
++ * Fix addrtoa return code bug from stale manpage advice preventing packets
++ * from being erouted.
++ *
++ * Revision 1.7 1998/08/06 07:44:59 rgb
++ * Fixed /proc/net/ipsec_eroute subnettoa and addrtoa return value bug that
++ * ended up in nothing being printed.
++ *
++ * Revision 1.6 1998/08/05 22:16:41 rgb
++ * Cleanup to prevent cosmetic errors (ie. debug output) from being fatal.
++ *
++ * Revision 1.5 1998/07/29 20:38:44 rgb
++ * Debug and fix subnettoa and addrtoa output.
++ *
++ * Revision 1.4 1998/07/28 00:02:39 rgb
++ * Converting to exclusive use of addrtoa.
++ * Fix eroute delete.
++ *
++ * Revision 1.3 1998/07/14 18:21:26 rgb
++ * Add function to clear the eroute table.
++ *
++ * Revision 1.2 1998/06/23 02:59:14 rgb
++ * Added debugging output to eroute add/delete routines.
++ *
++ * Revision 1.9 1998/06/18 21:29:06 henry
++ * move sources from klips/src to klips/net/ipsec, to keep stupid kernel
++ * build scripts happier in presence of symbolic links
++ *
++ * Revision 1.8 1998/06/05 02:32:26 rgb
++ * Fix spi ntoh kernel debug output.
++ *
++ * Revision 1.7 1998/05/25 20:30:37 rgb
++ * Remove temporary ipsec_walk, rj_deltree and rj_delnodes functions.
++ *
++ * Rename ipsec_rj_walker (ipsec_walk) to ipsec_rj_walker_procprint and
++ * add ipsec_rj_walker_delete.
++ *
++ * Revision 1.6 1998/05/21 13:08:57 rgb
++ * Rewrote procinfo subroutines to avoid *bad things* when more that 3k of
++ * information is available for printout.
++ *
++ * Revision 1.5 1998/05/18 21:35:55 rgb
++ * Clean up output for numerical consistency and readability. Zero freed
++ * eroute memory.
++ *
++ * Revision 1.4 1998/04/21 21:28:58 rgb
++ * Rearrange debug switches to change on the fly debug output from user
++ * space. Only kernel changes checked in at this time. radij.c was also
++ * changed to temporarily remove buggy debugging code in rj_delete causing
++ * an OOPS and hence, netlink device open errors.
++ *
++ * Revision 1.3 1998/04/14 17:30:39 rgb
++ * Fix up compiling errors for radij tree memory reclamation.
++ *
++ * Revision 1.2 1998/04/12 22:03:23 rgb
++ * Updated ESP-3DES-HMAC-MD5-96,
++ * ESP-DES-HMAC-MD5-96,
++ * AH-HMAC-MD5-96,
++ * AH-HMAC-SHA1-96 since Henry started freeswan cvs repository
++ * from old standards (RFC182[5-9] to new (as of March 1998) drafts.
++ *
++ * Fixed eroute references in /proc/net/ipsec*.
++ *
++ * Started to patch module unloading memory leaks in ipsec_netlink and
++ * radij tree unloading.
++ *
++ * Revision 1.1 1998/04/09 03:06:10 henry
++ * sources moved up from linux/net/ipsec
++ *
++ * Revision 1.1.1.1 1998/04/08 05:35:03 henry
++ * RGB's ipsec-0.8pre2.tar.gz ipsec-0.8
++ *
++ * Revision 0.4 1997/01/15 01:28:15 ji
++ * No changes.
++ *
++ * Revision 0.3 1996/11/20 14:39:04 ji
++ * Minor cleanups.
++ * Rationalized debugging code.
++ *
++ * Revision 0.2 1996/11/02 00:18:33 ji
++ * First limited release.
++ *
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/net/ipsec/ipsec_rcv.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,1922 @@
++/*
++ * receive code
++ * Copyright (C) 1996, 1997 John Ioannidis.
++ * Copyright (C) 1998-2003 Richard Guy Briggs.
++ * Copyright (C) 2004 Michael Richardson <mcr@xelerance.com>
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ */
++
++char ipsec_rcv_c_version[] = "RCSID $Id$";
++
++#include <linux/config.h>
++#include <linux/version.h>
++
++#define __NO_VERSION__
++#include <linux/module.h>
++#include <linux/kernel.h> /* printk() */
++
++#include "openswan/ipsec_param.h"
++
++#ifdef MALLOC_SLAB
++# include <linux/slab.h> /* kmalloc() */
++#else /* MALLOC_SLAB */
++# include <linux/malloc.h> /* kmalloc() */
++#endif /* MALLOC_SLAB */
++#include <linux/errno.h> /* error codes */
++#include <linux/types.h> /* size_t */
++#include <linux/interrupt.h> /* mark_bh */
++
++#include <net/udp.h>
++
++#include <linux/netdevice.h> /* struct device, and other headers */
++#include <linux/etherdevice.h> /* eth_type_trans */
++#include <linux/ip.h> /* struct iphdr */
++#include <linux/skbuff.h>
++#include <openswan.h>
++#ifdef SPINLOCK
++# ifdef SPINLOCK_23
++# include <linux/spinlock.h> /* *lock* */
++# else /* SPINLOCK_23 */
++# include <asm/spinlock.h> /* *lock* */
++# endif /* SPINLOCK_23 */
++#endif /* SPINLOCK */
++#ifdef NET_21
++# include <asm/uaccess.h>
++# include <linux/in6.h>
++# define proto_priv cb
++#endif /* NET21 */
++#include <asm/checksum.h>
++#include <net/ip.h>
++
++#include "openswan/radij.h"
++#include "openswan/ipsec_encap.h"
++#include "openswan/ipsec_sa.h"
++
++#include "openswan/ipsec_radij.h"
++#include "openswan/ipsec_xform.h"
++#include "openswan/ipsec_tunnel.h"
++#include "openswan/ipsec_rcv.h"
++
++#include "openswan/ipsec_auth.h"
++
++#include "openswan/ipsec_esp.h"
++
++#ifdef CONFIG_IPSEC_AH
++#include "openswan/ipsec_ah.h"
++#endif /* CONFIG_IPSEC_AH */
++
++#ifdef CONFIG_IPSEC_IPCOMP
++#include "openswan/ipsec_ipcomp.h"
++#endif /* CONFIG_IPSEC_COMP */
++
++#include <pfkeyv2.h>
++#include <pfkey.h>
++
++#include "openswan/ipsec_proto.h"
++#include "openswan/ipsec_alg.h"
++
++#ifdef CONFIG_IPSEC_DEBUG
++int debug_rcv = 0;
++#endif /* CONFIG_IPSEC_DEBUG */
++
++int sysctl_ipsec_inbound_policy_check = 1;
++
++/* This is a private use protocol, and AT&T should be ashamed. They should have
++ * used protocol # 59, which is "no next header" instead of 0xFE.
++ */
++#ifndef IPPROTO_ATT_HEARTBEAT
++#define IPPROTO_ATT_HEARTBEAT 0xFE
++#endif
++
++#ifdef CONFIG_IPSEC_DEBUG
++void
++ipsec_dmp(char *s, caddr_t bb, int len)
++{
++ int i;
++ unsigned char *b = bb;
++
++
++ printk(KERN_INFO "klips_debug:ipsec_tunnel_:dmp: "
++ "at %s, len=%d:",
++ s,
++ len);
++ for (i=0; i < len; i++) {
++ if(!(i%16)){
++ printk("\nklips_debug: ");
++ }
++ printk(" %02x", *b++);
++ }
++ printk("\n");
++}
++#endif /* CONFIG_IPSEC_DEBUG */
++
++/*
++ * Check-replay-window routine, adapted from the original
++ * by J. Hughes, from draft-ietf-ipsec-esp-des-md5-03.txt
++ *
++ * This is a routine that implements a 64 packet window. This is intend-
++ * ed on being an implementation sample.
++ */
++
++DEBUG_NO_STATIC int
++ipsec_checkreplaywindow(struct ipsec_sa*ipsp, __u32 seq)
++{
++ __u32 diff;
++
++ if (ipsp->ips_replaywin == 0) /* replay shut off */
++ return 1;
++ if (seq == 0)
++ return 0; /* first == 0 or wrapped */
++
++ /* new larger sequence number */
++ if (seq > ipsp->ips_replaywin_lastseq) {
++ return 1; /* larger is good */
++ }
++ diff = ipsp->ips_replaywin_lastseq - seq;
++
++ /* too old or wrapped */ /* if wrapped, kill off SA? */
++ if (diff >= ipsp->ips_replaywin) {
++ return 0;
++ }
++ /* this packet already seen */
++ if (ipsp->ips_replaywin_bitmap & (1 << diff))
++ return 0;
++ return 1; /* out of order but good */
++}
++
++DEBUG_NO_STATIC int
++ipsec_updatereplaywindow(struct ipsec_sa*ipsp, __u32 seq)
++{
++ __u32 diff;
++
++ if (ipsp->ips_replaywin == 0) /* replay shut off */
++ return 1;
++ if (seq == 0)
++ return 0; /* first == 0 or wrapped */
++
++ /* new larger sequence number */
++ if (seq > ipsp->ips_replaywin_lastseq) {
++ diff = seq - ipsp->ips_replaywin_lastseq;
++
++ /* In win, set bit for this pkt */
++ if (diff < ipsp->ips_replaywin)
++ ipsp->ips_replaywin_bitmap =
++ (ipsp->ips_replaywin_bitmap << diff) | 1;
++ else
++ /* This packet has way larger seq num */
++ ipsp->ips_replaywin_bitmap = 1;
++
++ if(seq - ipsp->ips_replaywin_lastseq - 1 > ipsp->ips_replaywin_maxdiff) {
++ ipsp->ips_replaywin_maxdiff = seq - ipsp->ips_replaywin_lastseq - 1;
++ }
++ ipsp->ips_replaywin_lastseq = seq;
++ return 1; /* larger is good */
++ }
++ diff = ipsp->ips_replaywin_lastseq - seq;
++
++ /* too old or wrapped */ /* if wrapped, kill off SA? */
++ if (diff >= ipsp->ips_replaywin) {
++/*
++ if(seq < 0.25*max && ipsp->ips_replaywin_lastseq > 0.75*max) {
++ ipsec_sa_delchain(ipsp);
++ }
++*/
++ return 0;
++ }
++ /* this packet already seen */
++ if (ipsp->ips_replaywin_bitmap & (1 << diff))
++ return 0;
++ ipsp->ips_replaywin_bitmap |= (1 << diff); /* mark as seen */
++ return 1; /* out of order but good */
++}
++
++#ifdef CONFIG_IPSEC_AUTH_HMAC_MD5
++struct auth_alg ipsec_rcv_md5[]={
++ {MD5Init, MD5Update, MD5Final, AHMD596_ALEN}
++};
++
++#endif /* CONFIG_IPSEC_AUTH_HMAC_MD5 */
++
++#ifdef CONFIG_IPSEC_AUTH_HMAC_SHA1
++struct auth_alg ipsec_rcv_sha1[]={
++ {SHA1Init, SHA1Update, SHA1Final, AHSHA196_ALEN}
++};
++#endif /* CONFIG_IPSEC_AUTH_HMAC_MD5 */
++
++enum ipsec_rcv_value
++ipsec_rcv_decap_once(struct ipsec_rcv_state *irs, struct xform_functions *proto_funcs)
++{
++ int iphlen;
++ unsigned char *dat;
++ __u8 proto;
++ struct in_addr ipsaddr;
++ struct in_addr ipdaddr;
++ int replay = 0; /* replay value in AH or ESP packet */
++ struct ipsec_sa* ipsnext = NULL; /* next SA towards inside of packet */
++ struct ipsec_sa *newipsp;
++ struct iphdr *ipp;
++ struct sk_buff *skb;
++#ifdef CONFIG_IPSEC_ALG
++ struct ipsec_alg_auth *ixt_a=NULL;
++#endif /* CONFIG_IPSEC_ALG */
++
++ skb = irs->skb;
++ irs->len = skb->len;
++ dat = skb->data;
++ ipp = irs->ipp;
++ proto = ipp->protocol;
++ ipsaddr.s_addr = ipp->saddr;
++ addrtoa(ipsaddr, 0, irs->ipsaddr_txt, sizeof(irs->ipsaddr_txt));
++ ipdaddr.s_addr = ipp->daddr;
++ addrtoa(ipdaddr, 0, irs->ipdaddr_txt, sizeof(irs->ipdaddr_txt));
++
++ iphlen = ipp->ihl << 2;
++ irs->iphlen=iphlen;
++ ipp->check = 0; /* we know the sum is good */
++
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv_decap_once: "
++ "decap (%d) from %s -> %s\n",
++ proto, irs->ipsaddr_txt, irs->ipdaddr_txt);
++
++ /*
++ * Find tunnel control block and (indirectly) call the
++ * appropriate tranform routine. The resulting sk_buf
++ * is a valid IP packet ready to go through input processing.
++ */
++
++ irs->said.dst.u.v4.sin_addr.s_addr = ipp->daddr;
++ irs->said.dst.u.v4.sin_family = AF_INET;
++
++ if(proto_funcs->rcv_checks) {
++ enum ipsec_rcv_value retval =
++ (*proto_funcs->rcv_checks)(irs, skb);
++
++ if(retval < 0) {
++ return retval;
++ }
++ }
++
++ irs->said.proto = proto;
++ irs->sa_len = satot(&irs->said, 0, irs->sa, sizeof(irs->sa));
++ if(irs->sa_len == 0) {
++ strcpy(irs->sa, "(error)");
++ }
++
++ newipsp = ipsec_sa_getbyid(&irs->said);
++ if (newipsp == NULL) {
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "no ipsec_sa for SA:%s: incoming packet with no SA dropped\n",
++ irs->sa_len ? irs->sa : " (error)");
++ if(irs->stats) {
++ irs->stats->rx_dropped++;
++ }
++ return IPSEC_RCV_SAIDNOTFOUND;
++ }
++
++ /* MCR - XXX this is bizarre. ipsec_sa_getbyid returned it, having incremented the refcount,
++ * why in the world would we decrement it here?
++
++ ipsec_sa_put(irs->ipsp);*/ /* incomplete */
++
++ /* If it is in larval state, drop the packet, we cannot process yet. */
++ if(newipsp->ips_state == SADB_SASTATE_LARVAL) {
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "ipsec_sa in larval state, cannot be used yet, dropping packet.\n");
++ if(irs->stats) {
++ irs->stats->rx_dropped++;
++ }
++ ipsec_sa_put(newipsp);
++ return IPSEC_RCV_SAIDNOTLIVE;
++ }
++
++ if(newipsp->ips_state == SADB_SASTATE_DEAD) {
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "ipsec_sa in dead state, cannot be used any more, dropping packet.\n");
++ if(irs->stats) {
++ irs->stats->rx_dropped++;
++ }
++ ipsec_sa_put(newipsp);
++ return IPSEC_RCV_SAIDNOTLIVE;
++ }
++
++ if(sysctl_ipsec_inbound_policy_check) {
++ if(irs->ipp->saddr != ((struct sockaddr_in*)(newipsp->ips_addr_s))->sin_addr.s_addr) {
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "SA:%s, src=%s of pkt does not agree with expected SA source address policy.\n",
++ irs->sa_len ? irs->sa : " (error)",
++ irs->ipsaddr_txt);
++ if(irs->stats) {
++ irs->stats->rx_dropped++;
++ }
++ ipsec_sa_put(newipsp);
++ return IPSEC_RCV_FAILEDINBOUND;
++ }
++
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "SA:%s, src=%s of pkt agrees with expected SA source address policy.\n",
++ irs->sa_len ? irs->sa : " (error)",
++ irs->ipsaddr_txt);
++
++ /*
++ * at this point, we have looked up a new SA, and we want to make sure that if this
++ * isn't the first SA in the list, that the previous SA actually points at this one.
++ */
++ if(irs->ipsp) {
++ if(irs->ipsp->ips_inext != newipsp) {
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "unexpected SA:%s: does not agree with ips->inext policy, dropped\n",
++ irs->sa_len ? irs->sa : " (error)");
++ if(irs->stats) {
++ irs->stats->rx_dropped++;
++ }
++ ipsec_sa_put(newipsp);
++ return IPSEC_RCV_FAILEDINBOUND;
++ }
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "SA:%s grouping from previous SA is OK.\n",
++ irs->sa_len ? irs->sa : " (error)");
++ } else {
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "SA:%s First SA in group.\n",
++ irs->sa_len ? irs->sa : " (error)");
++ }
++
++ /*
++ * previously, at this point, we checked if the back pointer from the new SA that
++ * we just found matched the back pointer. But, we won't do this check anymore,
++ * because we want to be able to nest SAs
++ */
++#ifdef CONFIG_IPSEC_NAT_TRAVERSAL
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "natt_type=%u tdbp->ips_natt_type=%u : %s\n",
++ irs->natt_type, newipsp->ips_natt_type,
++ (irs->natt_type==newipsp->ips_natt_type)?"ok":"bad");
++ if (irs->natt_type != newipsp->ips_natt_type) {
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "SA:%s does not agree with expected NAT-T policy.\n",
++ irs->sa_len ? irs->sa : " (error)");
++ if(irs->stats) {
++ irs->stats->rx_dropped++;
++ }
++ ipsec_sa_put(newipsp);
++ return IPSEC_RCV_FAILEDINBOUND;
++ }
++#endif
++ }
++
++ /* okay, SA checks out, so free any previous SA, and record a new one */
++
++ if(irs->ipsp) {
++ ipsec_sa_put(irs->ipsp);
++ }
++ irs->ipsp=newipsp;
++
++ /* note that the outer code will free the irs->ipsp if there is an error */
++
++
++ /* now check the lifetimes */
++ if(ipsec_lifetime_check(&irs->ipsp->ips_life.ipl_bytes, "bytes",
++ irs->sa, ipsec_life_countbased, ipsec_incoming,
++ irs->ipsp) == ipsec_life_harddied ||
++ ipsec_lifetime_check(&irs->ipsp->ips_life.ipl_addtime, "addtime",
++ irs->sa, ipsec_life_timebased, ipsec_incoming,
++ irs->ipsp) == ipsec_life_harddied ||
++ ipsec_lifetime_check(&irs->ipsp->ips_life.ipl_addtime, "usetime",
++ irs->sa, ipsec_life_timebased, ipsec_incoming,
++ irs->ipsp) == ipsec_life_harddied ||
++ ipsec_lifetime_check(&irs->ipsp->ips_life.ipl_packets, "packets",
++ irs->sa, ipsec_life_countbased, ipsec_incoming,
++ irs->ipsp) == ipsec_life_harddied) {
++ ipsec_sa_delchain(irs->ipsp);
++ if(irs->stats) {
++ irs->stats->rx_dropped++;
++ }
++
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv_decap_once: "
++ "decap (%d) failed lifetime check\n",
++ proto);
++
++ return IPSEC_RCV_LIFETIMEFAILED;
++ }
++
++#ifdef CONFIG_IPSEC_NAT_TRAVERSAL
++ if ((irs->natt_type) &&
++ ( (irs->ipp->saddr != (((struct sockaddr_in*)(newipsp->ips_addr_s))->sin_addr.s_addr)) ||
++ (irs->natt_sport != newipsp->ips_natt_sport)
++ )) {
++ struct sockaddr sipaddr;
++ /** Advertise NAT-T addr change to pluto **/
++ sipaddr.sa_family = AF_INET;
++ ((struct sockaddr_in*)&sipaddr)->sin_addr.s_addr = irs->ipp->saddr;
++ ((struct sockaddr_in*)&sipaddr)->sin_port = htons(irs->natt_sport);
++ pfkey_nat_t_new_mapping(newipsp, &sipaddr, irs->natt_sport);
++ /**
++ * Then allow or block packet depending on
++ * sysctl_ipsec_inbound_policy_check.
++ *
++ * In all cases, pluto will update SA if new mapping is
++ * accepted.
++ */
++ if (sysctl_ipsec_inbound_policy_check) {
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "SA:%s, src=%s:%u of pkt does not agree with expected "
++ "SA source address policy (pluto has been informed).\n",
++ irs->sa_len ? irs->sa : " (error)",
++ irs->ipsaddr_txt, irs->natt_sport);
++ if(irs->stats) {
++ irs->stats->rx_dropped++;
++ }
++ ipsec_sa_put(newipsp);
++ return IPSEC_RCV_FAILEDINBOUND;
++ }
++ }
++#endif
++
++ irs->authfuncs=NULL;
++ /* authenticate, if required */
++#ifdef CONFIG_IPSEC_ALG
++ if ((ixt_a=irs->ipsp->ips_alg_auth)) {
++ irs->authlen = AHHMAC_HASHLEN;
++ irs->authfuncs = NULL;
++ irs->ictx = NULL;
++ irs->octx = NULL;
++ irs->ictx_len = 0;
++ irs->octx_len = 0;
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "authalg=%d authlen=%d\n",
++ irs->ipsp->ips_authalg,
++ irs->authlen);
++ } else
++#endif /* CONFIG_IPSEC_ALG */
++ switch(irs->ipsp->ips_authalg) {
++#ifdef CONFIG_IPSEC_AUTH_HMAC_MD5
++ case AH_MD5:
++ irs->authlen = AHHMAC_HASHLEN;
++ irs->authfuncs = ipsec_rcv_md5;
++ irs->ictx = (void *)&((struct md5_ctx*)(irs->ipsp->ips_key_a))->ictx;
++ irs->octx = (void *)&((struct md5_ctx*)(irs->ipsp->ips_key_a))->octx;
++ irs->ictx_len = sizeof(((struct md5_ctx*)(irs->ipsp->ips_key_a))->ictx);
++ irs->octx_len = sizeof(((struct md5_ctx*)(irs->ipsp->ips_key_a))->octx);
++ break;
++#endif /* CONFIG_IPSEC_AUTH_HMAC_MD5 */
++#ifdef CONFIG_IPSEC_AUTH_HMAC_SHA1
++ case AH_SHA:
++ irs->authlen = AHHMAC_HASHLEN;
++ irs->authfuncs = ipsec_rcv_sha1;
++ irs->ictx = (void *)&((struct sha1_ctx*)(irs->ipsp->ips_key_a))->ictx;
++ irs->octx = (void *)&((struct sha1_ctx*)(irs->ipsp->ips_key_a))->octx;
++ irs->ictx_len = sizeof(((struct sha1_ctx*)(irs->ipsp->ips_key_a))->ictx);
++ irs->octx_len = sizeof(((struct sha1_ctx*)(irs->ipsp->ips_key_a))->octx);
++ break;
++#endif /* CONFIG_IPSEC_AUTH_HMAC_SHA1 */
++ case AH_NONE:
++ irs->authlen = 0;
++ irs->authfuncs = NULL;
++ irs->ictx = NULL;
++ irs->octx = NULL;
++ irs->ictx_len = 0;
++ irs->octx_len = 0;
++ break;
++ default:
++ irs->ipsp->ips_errs.ips_alg_errs += 1;
++ if(irs->stats) {
++ irs->stats->rx_errors++;
++ }
++ return IPSEC_RCV_BADAUTH;
++ }
++
++ irs->ilen = irs->len - iphlen - irs->authlen;
++ if(irs->ilen <= 0) {
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "runt %s packet with no data, dropping.\n",
++ (proto == IPPROTO_ESP ? "esp" : "ah"));
++ if(irs->stats) {
++ irs->stats->rx_dropped++;
++ }
++ return IPSEC_RCV_BADLEN;
++ }
++
++#ifdef CONFIG_IPSEC_ALG
++ if(irs->authfuncs || ixt_a) {
++#else
++ if(irs->authfuncs) {
++#endif
++ unsigned char *authenticator = NULL;
++
++ if(proto_funcs->rcv_setup_auth) {
++ enum ipsec_rcv_value retval
++ = (*proto_funcs->rcv_setup_auth)(irs, skb,
++ &replay,
++ &authenticator);
++ if(retval < 0) {
++ return retval;
++ }
++ }
++
++ if(!authenticator) {
++ irs->ipsp->ips_errs.ips_auth_errs += 1;
++ if(irs->stats) {
++ irs->stats->rx_dropped++;
++ }
++ return IPSEC_RCV_BADAUTH;
++ }
++
++ if(!ipsec_checkreplaywindow(irs->ipsp, replay)) {
++ irs->ipsp->ips_errs.ips_replaywin_errs += 1;
++ KLIPS_PRINT(debug_rcv & DB_RX_REPLAY,
++ "klips_debug:ipsec_rcv: "
++ "duplicate frame from %s, packet dropped\n",
++ irs->ipsaddr_txt);
++ if(irs->stats) {
++ irs->stats->rx_dropped++;
++ }
++ return IPSEC_RCV_REPLAYFAILED;
++ }
++
++ /*
++ * verify authenticator
++ */
++
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "encalg = %d, authalg = %d.\n",
++ irs->ipsp->ips_encalg,
++ irs->ipsp->ips_authalg);
++
++ /* calculate authenticator */
++ if(proto_funcs->rcv_calc_auth == NULL) {
++ return IPSEC_RCV_BADAUTH;
++ }
++ (*proto_funcs->rcv_calc_auth)(irs, skb);
++
++ if (memcmp(irs->hash, authenticator, irs->authlen)) {
++ irs->ipsp->ips_errs.ips_auth_errs += 1;
++ KLIPS_PRINT(debug_rcv & DB_RX_INAU,
++ "klips_debug:ipsec_rcv: "
++ "auth failed on incoming packet from %s: hash=%08x%08x%08x auth=%08x%08x%08x, dropped\n",
++ irs->ipsaddr_txt,
++ ntohl(*(__u32*)&irs->hash[0]),
++ ntohl(*(__u32*)&irs->hash[4]),
++ ntohl(*(__u32*)&irs->hash[8]),
++ ntohl(*(__u32*)authenticator),
++ ntohl(*((__u32*)authenticator + 1)),
++ ntohl(*((__u32*)authenticator + 2)));
++ if(irs->stats) {
++ irs->stats->rx_dropped++;
++ }
++ return IPSEC_RCV_AUTHFAILED;
++ } else {
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "authentication successful.\n");
++ }
++
++ /* Crypto hygiene: clear memory used to calculate autheticator.
++ * The length varies with the algorithm.
++ */
++ memset(irs->hash, 0, irs->authlen);
++
++ /* If the sequence number == 0, expire SA, it had rolled */
++ if(irs->ipsp->ips_replaywin && !replay /* !irs->ipsp->ips_replaywin_lastseq */) {
++ ipsec_sa_delchain(irs->ipsp);
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "replay window counter rolled, expiring SA.\n");
++ if(irs->stats) {
++ irs->stats->rx_dropped++;
++ }
++ return IPSEC_RCV_REPLAYROLLED;
++ }
++
++ /* now update the replay counter */
++ if (!ipsec_updatereplaywindow(irs->ipsp, replay)) {
++ irs->ipsp->ips_errs.ips_replaywin_errs += 1;
++ KLIPS_PRINT(debug_rcv & DB_RX_REPLAY,
++ "klips_debug:ipsec_rcv: "
++ "duplicate frame from %s, packet dropped\n",
++ irs->ipsaddr_txt);
++ if(irs->stats) {
++ irs->stats->rx_dropped++;
++ }
++ return IPSEC_RCV_REPLAYROLLED;
++ }
++ }
++
++ if(proto_funcs->rcv_decrypt) {
++ enum ipsec_rcv_value retval =
++ (*proto_funcs->rcv_decrypt)(irs);
++
++ if(retval != IPSEC_RCV_OK) {
++ return retval;
++ }
++ }
++
++ /*
++ * Adjust pointers
++ */
++ skb = irs->skb;
++ irs->len = skb->len;
++ dat = skb->data;
++
++#ifdef NET_21
++/* skb->h.ipiph=(struct iphdr *)skb->data; */
++ skb->nh.raw = skb->data;
++ skb->h.raw = skb->nh.raw + (skb->nh.iph->ihl << 2);
++
++ memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options));
++#else /* NET_21 */
++ skb->h.iph=(struct iphdr *)skb->data;
++ skb->ip_hdr=(struct iphdr *)skb->data;
++ memset(skb->proto_priv, 0, sizeof(struct options));
++#endif /* NET_21 */
++
++ ipp = (struct iphdr *)dat;
++ ipsaddr.s_addr = ipp->saddr;
++ addrtoa(ipsaddr, 0, irs->ipsaddr_txt, sizeof(irs->ipsaddr_txt));
++ ipdaddr.s_addr = ipp->daddr;
++ addrtoa(ipdaddr, 0, irs->ipdaddr_txt, sizeof(irs->ipdaddr_txt));
++ /*
++ * Discard the original ESP/AH header
++ */
++ ipp->protocol = irs->next_header;
++
++ ipp->check = 0; /* NOTE: this will be included in checksum */
++ ipp->check = ip_fast_csum((unsigned char *)dat, iphlen >> 2);
++
++ KLIPS_PRINT(debug_rcv & DB_RX_PKTRX,
++ "klips_debug:ipsec_rcv: "
++ "after <%s%s%s>, SA:%s:\n",
++ IPS_XFORM_NAME(irs->ipsp),
++ irs->sa_len ? irs->sa : " (error)");
++ KLIPS_IP_PRINT(debug_rcv & DB_RX_PKTRX, ipp);
++
++ skb->protocol = htons(ETH_P_IP);
++ skb->ip_summed = 0;
++
++ ipsnext = irs->ipsp->ips_inext;
++ if(sysctl_ipsec_inbound_policy_check) {
++ if(ipsnext) {
++ if(
++ ipp->protocol != IPPROTO_AH
++ && ipp->protocol != IPPROTO_ESP
++#ifdef CONFIG_IPSEC_IPCOMP
++ && ipp->protocol != IPPROTO_COMP
++ && (ipsnext->ips_said.proto != IPPROTO_COMP
++ || ipsnext->ips_inext)
++#endif /* CONFIG_IPSEC_IPCOMP */
++ && ipp->protocol != IPPROTO_IPIP
++ && ipp->protocol != 0xFE /* added to support heartbeats to AT&T SIG/GIG */
++ ) {
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "packet with incomplete policy dropped, last successful SA:%s.\n",
++ irs->sa_len ? irs->sa : " (error)");
++ if(irs->stats) {
++ irs->stats->rx_dropped++;
++ }
++ return IPSEC_RCV_FAILEDINBOUND;
++ }
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "SA:%s, Another IPSEC header to process.\n",
++ irs->sa_len ? irs->sa : " (error)");
++ } else {
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "No ips_inext from this SA:%s.\n",
++ irs->sa_len ? irs->sa : " (error)");
++ }
++ }
++
++#ifdef CONFIG_IPSEC_IPCOMP
++ /* update ipcomp ratio counters, even if no ipcomp packet is present */
++ if (ipsnext
++ && ipsnext->ips_said.proto == IPPROTO_COMP
++ && ipp->protocol != IPPROTO_COMP) {
++ ipsnext->ips_comp_ratio_cbytes += ntohs(ipp->tot_len);
++ ipsnext->ips_comp_ratio_dbytes += ntohs(ipp->tot_len);
++ }
++#endif /* CONFIG_IPSEC_IPCOMP */
++
++ irs->ipsp->ips_life.ipl_bytes.ipl_count += irs->len;
++ irs->ipsp->ips_life.ipl_bytes.ipl_last = irs->len;
++
++ if(!irs->ipsp->ips_life.ipl_usetime.ipl_count) {
++ irs->ipsp->ips_life.ipl_usetime.ipl_count = jiffies / HZ;
++ }
++ irs->ipsp->ips_life.ipl_usetime.ipl_last = jiffies / HZ;
++ irs->ipsp->ips_life.ipl_packets.ipl_count += 1;
++
++#ifdef CONFIG_NETFILTER
++ if(proto == IPPROTO_ESP || proto == IPPROTO_AH) {
++ skb->nfmark = (skb->nfmark & (~(IPsecSAref2NFmark(IPSEC_SA_REF_MASK))))
++ | IPsecSAref2NFmark(IPsecSA2SAref(irs->ipsp));
++ KLIPS_PRINT(debug_rcv & DB_RX_PKTRX,
++ "klips_debug:ipsec_rcv: "
++ "%s SA sets skb->nfmark=0x%x.\n",
++ proto == IPPROTO_ESP ? "ESP" : "AH",
++ (unsigned)skb->nfmark);
++ }
++#endif /* CONFIG_NETFILTER */
++
++ return IPSEC_RCV_OK;
++}
++
++
++int
++#ifdef PROTO_HANDLER_SINGLE_PARM
++ipsec_rcv(struct sk_buff *skb)
++#else /* PROTO_HANDLER_SINGLE_PARM */
++#ifdef NET_21
++ipsec_rcv(struct sk_buff *skb, unsigned short xlen)
++#else /* NET_21 */
++ipsec_rcv(struct sk_buff *skb, struct device *dev, struct options *opt,
++ __u32 daddr_unused, unsigned short xlen, __u32 saddr,
++ int redo, struct inet_protocol *protocol)
++#endif /* NET_21 */
++#endif /* PROTO_HANDLER_SINGLE_PARM */
++{
++#ifdef NET_21
++#ifdef CONFIG_IPSEC_DEBUG
++ struct device *dev = skb->dev;
++#endif /* CONFIG_IPSEC_DEBUG */
++#endif /* NET_21 */
++ unsigned char protoc;
++ struct iphdr *ipp;
++ struct ipsec_sa *ipsp = NULL;
++ struct net_device_stats *stats = NULL; /* This device's statistics */
++ struct device *ipsecdev = NULL, *prvdev;
++ struct ipsecpriv *prv;
++ char name[9];
++ int i;
++ struct in_addr ipsaddr;
++ struct in_addr ipdaddr;
++
++ struct ipsec_sa* ipsnext = NULL; /* next SA towards inside of packet */
++ struct ipsec_rcv_state irs;
++
++ /* Don't unlink in the middle of a turnaround */
++ MOD_INC_USE_COUNT;
++
++ memset(&irs, 0, sizeof(struct ipsec_rcv_state));
++
++ if (skb == NULL) {
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "NULL skb passed in.\n");
++ goto rcvleave;
++ }
++
++ if (skb->data == NULL) {
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "NULL skb->data passed in, packet is bogus, dropping.\n");
++ goto rcvleave;
++ }
++
++#ifdef CONFIG_IPSEC_NAT_TRAVERSAL
++ if (skb->sk && skb->nh.iph && skb->nh.iph->protocol==IPPROTO_UDP) {
++ /**
++ * Packet comes from udp_queue_rcv_skb so it is already defrag,
++ * checksum verified, ... (ie safe to use)
++ *
++ * If the packet is not for us, return -1 and udp_queue_rcv_skb
++ * will continue to handle it (do not kfree skb !!).
++ */
++#ifndef UDP_OPT_IN_SOCK
++ struct udp_opt {
++ __u32 esp_in_udp;
++ };
++ struct udp_opt *tp = (struct udp_opt *)&(skb->sk->tp_pinfo.af_tcp);
++#else
++ struct udp_opt *tp = &(skb->sk->tp_pinfo.af_udp);
++#endif
++ struct iphdr *ip = (struct iphdr *)skb->nh.iph;
++ struct udphdr *udp = (struct udphdr *)((__u32 *)ip+ip->ihl);
++ __u8 *udpdata = (__u8 *)udp + sizeof(struct udphdr);
++ __u32 *udpdata32 = (__u32 *)udpdata;
++
++ irs.natt_sport = ntohs(udp->source);
++ irs.natt_dport = ntohs(udp->dest);
++
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "suspected ESPinUDP packet (NAT-Traversal) [%d].\n",
++ tp->esp_in_udp);
++ KLIPS_IP_PRINT(debug_rcv, ip);
++
++ if (udpdata < skb->tail) {
++ unsigned int len = skb->tail - udpdata;
++ if ((len==1) && (udpdata[0]==0xff)) {
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ /* not IPv6 compliant message */
++ "NAT-keepalive from %d.%d.%d.%d.\n", NIPQUAD(ip->saddr));
++ goto rcvleave;
++ }
++ else if ( (tp->esp_in_udp == ESPINUDP_WITH_NON_IKE) &&
++ (len > (2*sizeof(__u32) + sizeof(struct esphdr))) &&
++ (udpdata32[0]==0) && (udpdata32[1]==0) ) {
++ /* ESP Packet with Non-IKE header */
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "ESPinUDP pkt with Non-IKE - spi=0x%x\n",
++ ntohl(udpdata32[2]));
++ irs.natt_type = ESPINUDP_WITH_NON_IKE;
++ irs.natt_len = sizeof(struct udphdr)+(2*sizeof(__u32));
++ }
++ else if ( (tp->esp_in_udp == ESPINUDP_WITH_NON_ESP) &&
++ (len > sizeof(struct esphdr)) &&
++ (udpdata32[0]!=0) ) {
++ /* ESP Packet without Non-ESP header */
++ irs.natt_type = ESPINUDP_WITH_NON_ESP;
++ irs.natt_len = sizeof(struct udphdr);
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "ESPinUDP pkt without Non-ESP - spi=0x%x\n",
++ ntohl(udpdata32[0]));
++ }
++ else {
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "IKE packet - not handled here\n");
++ MOD_DEC_USE_COUNT;
++ return -1;
++ }
++ }
++ else {
++ MOD_DEC_USE_COUNT;
++ return -1;
++ }
++ }
++#endif
++
++#ifdef IPH_is_SKB_PULLED
++ /* In Linux 2.4.4, the IP header has been skb_pull()ed before the
++ packet is passed to us. So we'll skb_push() to get back to it. */
++ if (skb->data == skb->h.raw) {
++ skb_push(skb, skb->h.raw - skb->nh.raw);
++ }
++#endif /* IPH_is_SKB_PULLED */
++
++ /* dev->hard_header_len is unreliable and should not be used */
++ irs.hard_header_len = skb->mac.raw ? (skb->data - skb->mac.raw) : 0;
++ if((irs.hard_header_len < 0) || (irs.hard_header_len > skb_headroom(skb)))
++ irs.hard_header_len = 0;
++
++#ifdef NET_21
++ /* if skb was cloned (most likely due to a packet sniffer such as
++ tcpdump being momentarily attached to the interface), make
++ a copy of our own to modify */
++ if(skb_cloned(skb)) {
++ /* include any mac header while copying.. */
++ if(skb_headroom(skb) < irs.hard_header_len) {
++ printk(KERN_WARNING "klips_error:ipsec_rcv: "
++ "tried to skb_push hhlen=%d, %d available. This should never happen, please report.\n",
++ irs.hard_header_len,
++ skb_headroom(skb));
++ goto rcvleave;
++ }
++ skb_push(skb, irs.hard_header_len);
++ if
++#ifdef SKB_COW_NEW
++ (skb_cow(skb, skb_headroom(skb)) != 0)
++#else /* SKB_COW_NEW */
++ ((skb = skb_cow(skb, skb_headroom(skb))) == NULL)
++#endif /* SKB_COW_NEW */
++ {
++ goto rcvleave;
++ }
++ if(skb->len < irs.hard_header_len) {
++ printk(KERN_WARNING "klips_error:ipsec_rcv: "
++ "tried to skb_pull hhlen=%d, %d available. This should never happen, please report.\n",
++ irs.hard_header_len,
++ skb->len);
++ goto rcvleave;
++ }
++ skb_pull(skb, irs.hard_header_len);
++ }
++
++#endif /* NET_21 */
++
++#if IP_FRAGMENT_LINEARIZE
++ /* In Linux 2.4.4, we may have to reassemble fragments. They are
++ not assembled automatically to save TCP from having to copy
++ twice.
++ */
++ if (skb_is_nonlinear(skb)) {
++ if (skb_linearize(skb, GFP_ATOMIC) != 0) {
++ goto rcvleave;
++ }
++ }
++#endif /* IP_FRAGMENT_LINEARIZE */
++
++#ifdef CONFIG_IPSEC_NAT_TRAVERSAL
++ if (irs.natt_len) {
++ /**
++ * Now, we are sure packet is ESPinUDP. Remove natt_len bytes from
++ * packet and modify protocol to ESP.
++ */
++ if (((unsigned char *)skb->data > (unsigned char *)skb->nh.iph) &&
++ ((unsigned char *)skb->nh.iph > (unsigned char *)skb->head)) {
++ unsigned int _len = (unsigned char *)skb->data -
++ (unsigned char *)skb->nh.iph;
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: adjusting skb: skb_push(%u)\n",
++ _len);
++ skb_push(skb, _len);
++ }
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "removing %d bytes from ESPinUDP packet\n", irs.natt_len);
++ ipp = (struct iphdr *)skb->data;
++ irs.iphlen = ipp->ihl << 2;
++ ipp->tot_len = htons(ntohs(ipp->tot_len) - irs.natt_len);
++ if (skb->len < irs.iphlen + irs.natt_len) {
++ printk(KERN_WARNING
++ "klips_error:ipsec_rcv: "
++ "ESPinUDP packet is too small (%d < %d+%d). "
++ "This should never happen, please report.\n",
++ (int)(skb->len), irs.iphlen, irs.natt_len);
++ goto rcvleave;
++ }
++ memmove(skb->data + irs.natt_len, skb->data, irs.iphlen);
++ skb_pull(skb, irs.natt_len);
++
++ /* update nh.iph */
++ ipp = skb->nh.iph = (struct iphdr *)skb->data;
++
++ /* modify protocol */
++ ipp->protocol = IPPROTO_ESP;
++
++ skb->sk = NULL;
++
++ KLIPS_IP_PRINT(debug_rcv, skb->nh.iph);
++ }
++#endif
++
++ ipp = skb->nh.iph;
++ ipsaddr.s_addr = ipp->saddr;
++ addrtoa(ipsaddr, 0, irs.ipsaddr_txt, sizeof(irs.ipsaddr_txt));
++ ipdaddr.s_addr = ipp->daddr;
++ addrtoa(ipdaddr, 0, irs.ipdaddr_txt, sizeof(irs.ipdaddr_txt));
++ irs.iphlen = ipp->ihl << 2;
++
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "<<< Info -- ");
++ KLIPS_PRINTMORE(debug_rcv && skb->dev, "skb->dev=%s ",
++ skb->dev->name ? skb->dev->name : "NULL");
++ KLIPS_PRINTMORE(debug_rcv && dev, "dev=%s ",
++ dev->name ? dev->name : "NULL");
++ KLIPS_PRINTMORE(debug_rcv, "\n");
++
++ KLIPS_PRINT(debug_rcv && !(skb->dev && dev && (skb->dev == dev)),
++ "klips_debug:ipsec_rcv: "
++ "Informational -- **if this happens, find out why** skb->dev:%s is not equal to dev:%s\n",
++ skb->dev ? (skb->dev->name ? skb->dev->name : "NULL") : "NULL",
++ dev ? (dev->name ? dev->name : "NULL") : "NULL");
++
++ protoc = ipp->protocol;
++#ifndef NET_21
++ if((!protocol) || (protocol->protocol != protoc)) {
++ KLIPS_PRINT(debug_rcv & DB_RX_IPSA,
++ "klips_debug:ipsec_rcv: "
++ "protocol arg is NULL or unequal to the packet contents, this is odd, using value in packet.\n");
++ }
++#endif /* !NET_21 */
++
++ if( (protoc != IPPROTO_AH) &&
++#ifdef CONFIG_IPSEC_IPCOMP_disabled_until_we_register_IPCOMP_HANDLER
++ (protoc != IPPROTO_COMP) &&
++#endif /* CONFIG_IPSEC_IPCOMP */
++ (protoc != IPPROTO_ESP) ) {
++ KLIPS_PRINT(debug_rcv & DB_RX_IPSA,
++ "klips_debug:ipsec_rcv: Why the hell is someone "
++ "passing me a non-ipsec protocol = %d packet? -- dropped.\n",
++ protoc);
++ goto rcvleave;
++ }
++
++ if(skb->dev) {
++ for(i = 0; i < IPSEC_NUM_IF; i++) {
++ sprintf(name, IPSEC_DEV_FORMAT, i);
++ if(!strcmp(name, skb->dev->name)) {
++ prv = (struct ipsecpriv *)(skb->dev->priv);
++ if(prv) {
++ stats = (struct net_device_stats *) &(prv->mystats);
++ }
++ ipsecdev = skb->dev;
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "Info -- pkt already proc'ed a group of ipsec headers, processing next group of ipsec headers.\n");
++ break;
++ }
++ if((ipsecdev = __ipsec_dev_get(name)) == NULL) {
++ KLIPS_PRINT(debug_rcv,
++ "klips_error:ipsec_rcv: "
++ "device %s does not exist\n",
++ name);
++ }
++ prv = ipsecdev ? (struct ipsecpriv *)(ipsecdev->priv) : NULL;
++ prvdev = prv ? (struct device *)(prv->dev) : NULL;
++
++#if 0
++ KLIPS_PRINT(debug_rcv && prvdev,
++ "klips_debug:ipsec_rcv: "
++ "physical device for device %s is %s\n",
++ name,
++ prvdev->name);
++#endif
++ if(prvdev && skb->dev &&
++ !strcmp(prvdev->name, skb->dev->name)) {
++ stats = prv ? ((struct net_device_stats *) &(prv->mystats)) : NULL;
++ skb->dev = ipsecdev;
++ KLIPS_PRINT(debug_rcv && prvdev,
++ "klips_debug:ipsec_rcv: "
++ "assigning packet ownership to virtual device %s from physical device %s.\n",
++ name, prvdev->name);
++ if(stats) {
++ stats->rx_packets++;
++ }
++ break;
++ }
++ }
++ } else {
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "device supplied with skb is NULL\n");
++ }
++
++ if(stats == NULL) {
++ KLIPS_PRINT((debug_rcv),
++ "klips_error:ipsec_rcv: "
++ "packet received from physical I/F (%s) not connected to ipsec I/F. Cannot record stats. May not have SA for decoding. Is IPSEC traffic expected on this I/F? Check routing.\n",
++ skb->dev ? (skb->dev->name ? skb->dev->name : "NULL") : "NULL");
++ }
++
++ KLIPS_IP_PRINT(debug_rcv, ipp);
++
++ /* begin decapsulating loop here */
++
++ /*
++ The spinlock is to prevent any other process from
++ accessing or deleting the ipsec_sa hash table or any of the
++ ipsec_sa s while we are using and updating them.
++
++ This is not optimal, but was relatively straightforward
++ at the time. A better way to do it has been planned for
++ more than a year, to lock the hash table and put reference
++ counts on each ipsec_sa instead. This is not likely to happen
++ in KLIPS1 unless a volunteer contributes it, but will be
++ designed into KLIPS2.
++ */
++ spin_lock(&tdb_lock);
++
++ /* set up for decap loop */
++ irs.stats= stats;
++ irs.ipp = ipp;
++ irs.ipsp = NULL;
++ irs.ilen = 0;
++ irs.authlen=0;
++ irs.authfuncs=NULL;
++ irs.skb = skb;
++
++ do {
++ int decap_stat;
++ struct xform_functions *proto_funcs;
++
++ switch(irs.ipp->protocol) {
++ case IPPROTO_ESP:
++ proto_funcs = esp_xform_funcs;
++ break;
++
++#ifdef CONFIG_IPSEC_AH
++ case IPPROTO_AH:
++ proto_funcs = ah_xform_funcs;
++ break;
++#endif /* !CONFIG_IPSEC_AH */
++
++#ifdef CONFIG_IPSEC_IPCOMP
++ case IPPROTO_COMP:
++ proto_funcs = ipcomp_xform_funcs;
++ break;
++#endif /* !CONFIG_IPSEC_IPCOMP */
++ default:
++ if(irs.stats) {
++ irs.stats->rx_errors++;
++ }
++ decap_stat = IPSEC_RCV_BADPROTO;
++ goto rcvleave;
++ }
++
++ decap_stat = ipsec_rcv_decap_once(&irs, proto_funcs);
++
++ if(decap_stat != IPSEC_RCV_OK) {
++ spin_unlock(&tdb_lock);
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: decap_once failed: %d\n",
++ decap_stat);
++
++ goto rcvleave;
++ }
++ /* end decapsulation loop here */
++ } while( (irs.ipp->protocol == IPPROTO_ESP )
++ || (irs.ipp->protocol == IPPROTO_AH )
++#ifdef CONFIG_IPSEC_IPCOMP
++ || (irs.ipp->protocol == IPPROTO_COMP)
++#endif /* CONFIG_IPSEC_IPCOMP */
++ );
++
++ /* set up for decap loop */
++ ipp =irs.ipp;
++ ipsp =irs.ipsp;
++ ipsnext = ipsp->ips_inext;
++ skb = irs.skb;
++
++ /* if there is an IPCOMP, but we don't have an IPPROTO_COMP,
++ * then we can just skip it
++ */
++#ifdef CONFIG_IPSEC_IPCOMP
++ if(ipsnext && ipsnext->ips_said.proto == IPPROTO_COMP) {
++ ipsp = ipsnext;
++ ipsnext = ipsp->ips_inext;
++ }
++#endif /* CONFIG_IPSEC_IPCOMP */
++
++#ifdef CONFIG_IPSEC_NAT_TRAVERSAL
++ if ((irs.natt_type) && (ipp->protocol != IPPROTO_IPIP)) {
++ /**
++ * NAT-Traversal and Transport Mode:
++ * we need to correct TCP/UDP checksum
++ *
++ * If we've got NAT-OA, we can fix checksum without recalculation.
++ */
++ __u32 natt_oa = ipsp->ips_natt_oa ?
++ ((struct sockaddr_in*)(ipsp->ips_natt_oa))->sin_addr.s_addr : 0;
++ __u16 pkt_len = skb->tail - (unsigned char *)ipp;
++ __u16 data_len = pkt_len - (ipp->ihl << 2);
++
++ switch (ipp->protocol) {
++ case IPPROTO_TCP:
++ if (data_len >= sizeof(struct tcphdr)) {
++ struct tcphdr *tcp = (struct tcphdr *)((__u32 *)ipp+ipp->ihl);
++ if (natt_oa) {
++ __u32 buff[2] = { ~natt_oa, ipp->saddr };
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "NAT-T & TRANSPORT: "
++ "fix TCP checksum using NAT-OA\n");
++ tcp->check = csum_fold(
++ csum_partial((unsigned char *)buff, sizeof(buff),
++ tcp->check^0xffff));
++ }
++ else {
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "NAT-T & TRANSPORT: recalc TCP checksum\n");
++ if (pkt_len > (ntohs(ipp->tot_len)))
++ data_len -= (pkt_len - ntohs(ipp->tot_len));
++ tcp->check = 0;
++ tcp->check = csum_tcpudp_magic(ipp->saddr, ipp->daddr,
++ data_len, IPPROTO_TCP,
++ csum_partial((unsigned char *)tcp, data_len, 0));
++ }
++ }
++ else {
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "NAT-T & TRANSPORT: can't fix TCP checksum\n");
++ }
++ break;
++ case IPPROTO_UDP:
++ if (data_len >= sizeof(struct udphdr)) {
++ struct udphdr *udp = (struct udphdr *)((__u32 *)ipp+ipp->ihl);
++ if (udp->check == 0) {
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "NAT-T & TRANSPORT: UDP checksum already 0\n");
++ }
++ else if (natt_oa) {
++ __u32 buff[2] = { ~natt_oa, ipp->saddr };
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "NAT-T & TRANSPORT: "
++ "fix UDP checksum using NAT-OA\n");
++ udp->check = csum_fold(
++ csum_partial((unsigned char *)buff, sizeof(buff),
++ udp->check^0xffff));
++ }
++ else {
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "NAT-T & TRANSPORT: zero UDP checksum\n");
++ udp->check = 0;
++ }
++ }
++ else {
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "NAT-T & TRANSPORT: can't fix UDP checksum\n");
++ }
++ break;
++ default:
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "NAT-T & TRANSPORT: non TCP/UDP packet -- do nothing\n");
++ break;
++ }
++ }
++#endif
++
++ /*
++ * XXX this needs to be locked from when it was first looked
++ * up in the decapsulation loop. Perhaps it is better to put
++ * the IPIP decap inside the loop.
++ */
++ if(ipsnext) {
++ ipsp = ipsnext;
++ irs.sa_len = satot(&irs.said, 0, irs.sa, sizeof(irs.sa));
++ if((ipp->protocol != IPPROTO_IPIP) &&
++ ( 0xFE != ipp->protocol)) { /* added to support AT&T heartbeats to SIG/GIG */
++ spin_unlock(&tdb_lock);
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "SA:%s, Hey! How did this get through? Dropped.\n",
++ irs.sa_len ? irs.sa : " (error)");
++ if(stats) {
++ stats->rx_dropped++;
++ }
++ goto rcvleave;
++ }
++ if(sysctl_ipsec_inbound_policy_check) {
++ if((ipsnext = ipsp->ips_inext)) {
++ char sa2[SATOT_BUF];
++ size_t sa_len2;
++ sa_len2 = satot(&ipsnext->ips_said, 0, sa2, sizeof(sa2));
++ spin_unlock(&tdb_lock);
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "unexpected SA:%s after IPIP SA:%s\n",
++ sa_len2 ? sa2 : " (error)",
++ irs.sa_len ? irs.sa : " (error)");
++ if(stats) {
++ stats->rx_dropped++;
++ }
++ goto rcvleave;
++ }
++ if(ipp->saddr != ((struct sockaddr_in*)(ipsp->ips_addr_s))->sin_addr.s_addr) {
++ spin_unlock(&tdb_lock);
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "SA:%s, src=%s of pkt does not agree with expected SA source address policy.\n",
++ irs.sa_len ? irs.sa : " (error)",
++ irs.ipsaddr_txt);
++ if(stats) {
++ stats->rx_dropped++;
++ }
++ goto rcvleave;
++ }
++ }
++
++ if(ipp->protocol == IPPROTO_IPIP) /* added to support AT&T heartbeats to SIG/GIG */
++ {
++ /*
++ * XXX this needs to be locked from when it was first looked
++ * up in the decapsulation loop. Perhaps it is better to put
++ * the IPIP decap inside the loop.
++ */
++ ipsp->ips_life.ipl_bytes.ipl_count += skb->len;
++ ipsp->ips_life.ipl_bytes.ipl_last = skb->len;
++
++ if(!ipsp->ips_life.ipl_usetime.ipl_count) {
++ ipsp->ips_life.ipl_usetime.ipl_count = jiffies / HZ;
++ }
++ ipsp->ips_life.ipl_usetime.ipl_last = jiffies / HZ;
++ ipsp->ips_life.ipl_packets.ipl_count += 1;
++
++ if(skb->len < irs.iphlen) {
++ spin_unlock(&tdb_lock);
++ printk(KERN_WARNING "klips_debug:ipsec_rcv: "
++ "tried to skb_pull iphlen=%d, %d available. This should never happen, please report.\n",
++ irs.iphlen,
++ (int)(skb->len));
++
++ goto rcvleave;
++ }
++ skb_pull(skb, irs.iphlen);
++
++#ifdef NET_21
++ ipp = (struct iphdr *)skb->nh.raw = skb->data;
++ skb->h.raw = skb->nh.raw + (skb->nh.iph->ihl << 2);
++
++ memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options));
++#else /* NET_21 */
++ ipp = skb->ip_hdr = skb->h.iph = (struct iphdr *)skb->data;
++
++ memset(skb->proto_priv, 0, sizeof(struct options));
++#endif /* NET_21 */
++ ipsaddr.s_addr = ipp->saddr;
++ addrtoa(ipsaddr, 0, irs.ipsaddr_txt, sizeof(irs.ipsaddr_txt));
++ ipdaddr.s_addr = ipp->daddr;
++ addrtoa(ipdaddr, 0, irs.ipdaddr_txt, sizeof(irs.ipdaddr_txt));
++
++ skb->protocol = htons(ETH_P_IP);
++ skb->ip_summed = 0;
++ KLIPS_PRINT(debug_rcv & DB_RX_PKTRX,
++ "klips_debug:ipsec_rcv: "
++ "IPIP tunnel stripped.\n");
++ KLIPS_IP_PRINT(debug_rcv & DB_RX_PKTRX, ipp);
++ }
++
++ if(sysctl_ipsec_inbound_policy_check
++ /*
++ Note: "xor" (^) logically replaces "not equal"
++ (!=) and "bitwise or" (|) logically replaces
++ "boolean or" (||). This is done to speed up
++ execution by doing only bitwise operations and
++ no branch operations
++ */
++ && (((ipp->saddr & ipsp->ips_mask_s.u.v4.sin_addr.s_addr)
++ ^ ipsp->ips_flow_s.u.v4.sin_addr.s_addr)
++ | ((ipp->daddr & ipsp->ips_mask_d.u.v4.sin_addr.s_addr)
++ ^ ipsp->ips_flow_d.u.v4.sin_addr.s_addr)) )
++ {
++ char sflow_txt[SUBNETTOA_BUF], dflow_txt[SUBNETTOA_BUF];
++
++ subnettoa(ipsp->ips_flow_s.u.v4.sin_addr,
++ ipsp->ips_mask_s.u.v4.sin_addr,
++ 0, sflow_txt, sizeof(sflow_txt));
++ subnettoa(ipsp->ips_flow_d.u.v4.sin_addr,
++ ipsp->ips_mask_d.u.v4.sin_addr,
++ 0, dflow_txt, sizeof(dflow_txt));
++ spin_unlock(&tdb_lock);
++ KLIPS_PRINT(debug_rcv,
++ "klips_debug:ipsec_rcv: "
++ "SA:%s, inner tunnel policy [%s -> %s] does not agree with pkt contents [%s -> %s].\n",
++ irs.sa_len ? irs.sa : " (error)",
++ sflow_txt,
++ dflow_txt,
++ irs.ipsaddr_txt,
++ irs.ipdaddr_txt);
++ if(stats) {
++ stats->rx_dropped++;
++ }
++ goto rcvleave;
++ }
++#ifdef CONFIG_NETFILTER
++ skb->nfmark = (skb->nfmark & (~(IPsecSAref2NFmark(IPSEC_SA_REF_TABLE_MASK))))
++ | IPsecSAref2NFmark(IPsecSA2SAref(ipsp));
++ KLIPS_PRINT(debug_rcv & DB_RX_PKTRX,
++ "klips_debug:ipsec_rcv: "
++ "IPIP SA sets skb->nfmark=0x%x.\n",
++ (unsigned)skb->nfmark);
++#endif /* CONFIG_NETFILTER */
++ }
++
++ spin_unlock(&tdb_lock);
++
++#ifdef NET_21
++ if(stats) {
++ stats->rx_bytes += skb->len;
++ }
++ if(skb->dst) {
++ dst_release(skb->dst);
++ skb->dst = NULL;
++ }
++ skb->pkt_type = PACKET_HOST;
++ if(irs.hard_header_len &&
++ (skb->mac.raw != (skb->data - irs.hard_header_len)) &&
++ (irs.hard_header_len <= skb_headroom(skb))) {
++ /* copy back original MAC header */
++ memmove(skb->data - irs.hard_header_len, skb->mac.raw, irs.hard_header_len);
++ skb->mac.raw = skb->data - irs.hard_header_len;
++ }
++#endif /* NET_21 */
++
++#ifdef CONFIG_IPSEC_IPCOMP
++ if(ipp->protocol == IPPROTO_COMP) {
++ unsigned int flags = 0;
++
++ if(sysctl_ipsec_inbound_policy_check) {
++ KLIPS_PRINT(debug_rcv & DB_RX_PKTRX,
++ "klips_debug:ipsec_rcv: "
++ "inbound policy checking enabled, IPCOMP follows IPIP, dropped.\n");
++ if (stats) {
++ stats->rx_errors++;
++ }
++ goto rcvleave;
++ }
++ /*
++ XXX need a ipsec_sa for updating ratio counters but it is not
++ following policy anyways so it is not a priority
++ */
++ skb = skb_decompress(skb, NULL, &flags);
++ if (!skb || flags) {
++ KLIPS_PRINT(debug_rcv & DB_RX_PKTRX,
++ "klips_debug:ipsec_rcv: "
++ "skb_decompress() returned error flags: %d, dropped.\n",
++ flags);
++ if (stats) {
++ stats->rx_errors++;
++ }
++ goto rcvleave;
++ }
++ }
++#endif /* CONFIG_IPSEC_IPCOMP */
++
++#ifdef SKB_RESET_NFCT
++ nf_conntrack_put(skb->nfct);
++ skb->nfct = NULL;
++#ifdef CONFIG_NETFILTER_DEBUG
++ skb->nf_debug = 0;
++#endif /* CONFIG_NETFILTER_DEBUG */
++#endif /* SKB_RESET_NFCT */
++ KLIPS_PRINT(debug_rcv & DB_RX_PKTRX,
++ "klips_debug:ipsec_rcv: "
++ "netif_rx() called.\n");
++ netif_rx(skb);
++
++ MOD_DEC_USE_COUNT;
++ return(0);
++
++ rcvleave:
++ if(skb) {
++ ipsec_kfree_skb(skb);
++ }
++
++ MOD_DEC_USE_COUNT;
++ return(0);
++}
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.143.4.2 2004/08/22 03:29:06 mcr
++ * include udp.h regardless of nat-t support.
++ *
++ * Revision 1.143.4.1 2004/08/21 02:14:58 ken
++ * Patch from Jochen Eisinger for AT&T MTS Heartbeat packet support
++ *
++ * Revision 1.143 2004/05/10 22:27:00 mcr
++ * fix for ESP-3DES-noauth test case.
++ *
++ * Revision 1.142 2004/05/10 22:25:57 mcr
++ * reformat of calls to ipsec_lifetime_check().
++ *
++ * Revision 1.141 2004/04/06 02:49:26 mcr
++ * pullup of algo code from alg-branch.
++ *
++ * Revision 1.140 2004/02/03 03:12:53 mcr
++ * removed erroneously, double patched code.
++ *
++ * Revision 1.139 2004/01/05 23:21:29 mcr
++ * initialize sin_family in ipsec_rcv.c
++ *
++ * Revision 1.138 2003/12/24 19:46:52 mcr
++ * if sock.h patch has not been applied, then define appropriate
++ * structure so we can use it. This is serious inferior, and
++ * depends upon the concept that the structure in question is
++ * smaller than the other members of that union.
++ * getting rid of differing methods is a better solution.
++ *
++ * Revision 1.137 2003/12/22 19:40:57 mcr
++ * NAT-T patches 0.6c.
++ *
++ * Revision 1.136 2003/12/15 18:13:12 mcr
++ * when compiling with NAT traversal, don't assume that the
++ * kernel has been patched, unless CONFIG_IPSEC_NAT_NON_ESP
++ * is set.
++ *
++ * Revision 1.135 2003/12/13 19:10:21 mcr
++ * refactored rcv and xmit code - same as FS 2.05.
++ *
++ * Revision 1.134.2.1 2003/12/22 15:25:52 jjo
++ * Merged algo-0.8.1-rc11-test1 into alg-branch
++ *
++ * Revision 1.134 2003/12/10 01:14:27 mcr
++ * NAT-traversal patches to KLIPS.
++ *
++ * Revision 1.133 2003/10/31 02:27:55 mcr
++ * pulled up port-selector patches and sa_id elimination.
++ *
++ * Revision 1.132.2.1 2003/10/29 01:30:41 mcr
++ * elimited "struct sa_id".
++ *
++ * Revision 1.132 2003/09/02 19:51:48 mcr
++ * fixes for PR#252.
++ *
++ * Revision 1.131 2003/07/31 22:47:16 mcr
++ * preliminary (untested by FS-team) 2.5 patches.
++ *
++ * Revision 1.130 2003/04/03 17:38:25 rgb
++ * Centralised ipsec_kfree_skb and ipsec_dev_{get,put}.
++ * Clarified logic for non-connected devices.
++ *
++ * Revision 1.129 2003/02/06 02:21:34 rgb
++ *
++ * Moved "struct auth_alg" from ipsec_rcv.c to ipsec_ah.h .
++ * Changed "struct ah" to "struct ahhdr" and "struct esp" to "struct esphdr".
++ * Removed "#ifdef INBOUND_POLICY_CHECK_eroute" dead code.
++ *
++ * Revision 1.128 2002/12/13 20:58:03 rgb
++ * Relegated MCR's recent "_dmp" routine to debug_verbose.
++ * Cleaned up printing of source and destination addresses in debug output.
++ *
++ * Revision 1.127 2002/12/04 16:00:16 rgb
++ *
++ * Fixed AH decapsulation pointer update bug and added some comments and
++ * debugging.
++ * This bug was caught by west-ah-0[12].
++ *
++ * Revision 1.126 2002/11/04 05:03:43 mcr
++ * fixes for IPCOMP. There were two problems:
++ * 1) the irs->ipp pointer was not being updated properly after
++ * the ESP descryption. The meant nothing for IPIP, as the
++ * later IP header overwrote the earlier one.
++ * 2) the more serious problem was that skb_decompress will
++ * usually allocate a new SKB, so we have to make sure that
++ * it doesn't get lost.
++ * #2 meant removing the skb argument from the ->decrypt routine
++ * and moving it to the irs->skb, so it could be value/result.
++ *
++ * Revision 1.125 2002/11/01 01:53:35 dhr
++ *
++ * fix typo
++ *
++ * Revision 1.124 2002/10/31 22:49:01 dhr
++ *
++ * - eliminate unused variable "hash"
++ * - reduce scope of variable "authenticator"
++ * - add comment on a couple of tricky bits
++ *
++ * Revision 1.123 2002/10/31 22:39:56 dhr
++ *
++ * use correct type for result of function calls
++ *
++ * Revision 1.122 2002/10/31 22:36:25 dhr
++ *
++ * simplify complex test
++ *
++ * Revision 1.121 2002/10/31 22:34:04 dhr
++ *
++ * ipsprev is never used: ditch it
++ *
++ * Revision 1.120 2002/10/31 22:30:21 dhr
++ *
++ * eliminate redundant assignments
++ *
++ * Revision 1.119 2002/10/31 22:27:43 dhr
++ *
++ * make whitespace canonical
++ *
++ * Revision 1.118 2002/10/30 05:47:17 rgb
++ * Fixed cut-and-paste error mis-identifying comp runt as ah.
++ *
++ * Revision 1.117 2002/10/17 16:37:45 rgb
++ * Remove compp intermediate variable and in-line its contents
++ * where used
++ *
++ * Revision 1.116 2002/10/12 23:11:53 dhr
++ *
++ * [KenB + DHR] more 64-bit cleanup
++ *
++ * Revision 1.115 2002/10/07 19:06:58 rgb
++ * Minor fixups and activation to west-rcv-nfmark-set-01 test to check for SA reference properly set on incoming.
++ *
++ * Revision 1.114 2002/10/07 18:31:31 rgb
++ * Set saref on incoming packets.
++ *
++ * Revision 1.113 2002/09/16 21:28:12 mcr
++ * adjust hash length for HMAC calculation - must look at whether
++ * it is MD5 or SHA1.
++ *
++ * Revision 1.112 2002/09/16 21:19:15 mcr
++ * fixes for west-ah-icmp-01 - length of AH header must be
++ * calculated properly, and next_header field properly copied.
++ *
++ * Revision 1.111 2002/09/10 02:45:56 mcr
++ * re-factored the ipsec_rcv function into several functions,
++ * ipsec_rcv_decap_once, and a set of functions for AH, ESP and IPCOMP.
++ * In addition, the MD5 and SHA1 functions are replaced with pointers.
++ *
++ * Revision 1.110 2002/08/30 06:34:33 rgb
++ * Fix scope of shift in AH header length check.
++ *
++ * Revision 1.109 2002/08/27 16:49:20 rgb
++ * Fixed ESP short packet DOS (and AH and IPCOMP).
++ *
++ * Revision 1.108 2002/07/24 18:44:54 rgb
++ * Type fiddling to tame ia64 compiler.
++ *
++ * Revision 1.107 2002/05/27 18:58:18 rgb
++ * Convert to dynamic ipsec device allocation.
++ * Remove final vistiges of tdb references via IPSEC_KLIPS1_COMPAT.
++ *
++ * Revision 1.106 2002/05/23 07:15:21 rgb
++ * Pointer clean-up.
++ * Added refcount code.
++ *
++ * Revision 1.105 2002/05/14 02:35:06 rgb
++ * Change all references to tdb, TDB or Tunnel Descriptor Block to ips,
++ * ipsec_sa or ipsec_sa.
++ * Change references to _TDB to _IPSA.
++ *
++ * Revision 1.104 2002/04/24 07:55:32 mcr
++ * #include patches and Makefiles for post-reorg compilation.
++ *
++ * Revision 1.103 2002/04/24 07:36:30 mcr
++ * Moved from ./klips/net/ipsec/ipsec_rcv.c,v
++ *
++ * Revision 1.102 2002/01/29 17:17:56 mcr
++ * moved include of ipsec_param.h to after include of linux/kernel.h
++ * otherwise, it seems that some option that is set in ipsec_param.h
++ * screws up something subtle in the include path to kernel.h, and
++ * it complains on the snprintf() prototype.
++ *
++ * Revision 1.101 2002/01/29 04:00:52 mcr
++ * more excise of kversions.h header.
++ *
++ * Revision 1.100 2002/01/29 02:13:17 mcr
++ * introduction of ipsec_kversion.h means that include of
++ * ipsec_param.h must preceed any decisions about what files to
++ * include to deal with differences in kernel source.
++ *
++ * Revision 1.99 2002/01/28 21:40:59 mcr
++ * should use #if to test boolean option rather than #ifdef.
++ *
++ * Revision 1.98 2002/01/20 20:19:36 mcr
++ * renamed option to IP_FRAGMENT_LINEARIZE.
++ *
++ * Revision 1.97 2002/01/12 02:55:36 mcr
++ * fix for post-2.4.4 to linearize skb's when ESP packet
++ * was assembled from fragments.
++ *
++ * Revision 1.96 2001/11/26 09:23:49 rgb
++ * Merge MCR's ipsec_sa, eroute, proc and struct lifetime changes.
++ *
++ * Revision 1.93.2.2 2001/10/22 20:54:07 mcr
++ * include des.h, removed phony prototypes and fixed calling
++ * conventions to match real prototypes.
++ *
++ * Revision 1.93.2.1 2001/09/25 02:22:22 mcr
++ * struct tdb -> struct ipsec_sa.
++ * lifetime checks moved to ipsec_life.c
++ * some sa(tdb) manipulation functions renamed.
++ *
++ * Revision 1.95 2001/11/06 19:49:07 rgb
++ * Added variable descriptions.
++ * Removed unauthenticated sequence==0 check to prevent DoS.
++ *
++ * Revision 1.94 2001/10/18 04:45:20 rgb
++ * 2.4.9 kernel deprecates linux/malloc.h in favour of linux/slab.h,
++ * lib/freeswan.h version macros moved to lib/kversions.h.
++ * Other compiler directive cleanups.
++ *
++ * Revision 1.93 2001/09/07 22:17:24 rgb
++ * Fix for removal of transport layer protocol handler arg in 2.4.4.
++ * Fix to accomodate peer non-conformance to IPCOMP rfc2393.
++ *
++ * Revision 1.92 2001/08/27 19:44:41 rgb
++ * Fix error in comment.
++ *
++ * Revision 1.91 2001/07/20 19:31:48 dhr
++ * [DHR] fix source and destination subnets of policy in diagnostic
++ *
++ * Revision 1.90 2001/07/06 19:51:09 rgb
++ * Added inbound policy checking code for IPIP SAs.
++ * Renamed unused function argument for ease and intuitive naming.
++ *
++ * Revision 1.89 2001/06/22 19:35:23 rgb
++ * Disable ipcomp processing if we are handed a ipcomp packet with no esp
++ * or ah header.
++ * Print protocol if we are handed a non-ipsec packet.
++ *
++ * Revision 1.88 2001/06/20 06:30:47 rgb
++ * Fixed transport mode IPCOMP policy check bug.
++ *
++ * Revision 1.87 2001/06/13 20:58:40 rgb
++ * Added parentheses around assignment used as truth value to silence
++ * compiler.
++ *
++ * Revision 1.86 2001/06/07 22:25:23 rgb
++ * Added a source address policy check for tunnel mode. It still does
++ * not check client addresses and masks.
++ * Only decapsulate IPIP if it is expected.
++ *
++ * Revision 1.85 2001/05/30 08:14:02 rgb
++ * Removed vestiges of esp-null transforms.
++ *
++ * Revision 1.84 2001/05/27 06:12:11 rgb
++ * Added structures for pid, packet count and last access time to eroute.
++ * Added packet count to beginning of /proc/net/ipsec_eroute.
++ *
++ * Revision 1.83 2001/05/04 16:45:47 rgb
++ * Remove unneeded code. ipp is not used after this point.
++ *
++ * Revision 1.82 2001/05/04 16:36:00 rgb
++ * Fix skb_cow() call for 2.4.4. (SS)
++ *
++ * Revision 1.81 2001/05/02 14:46:53 rgb
++ * Fix typo for compiler directive to pull IPH back.
++ *
++ * Revision 1.80 2001/04/30 19:46:34 rgb
++ * Update for 2.4.4. We now receive the skb with skb->data pointing to
++ * h.raw.
++ *
++ * Revision 1.79 2001/04/23 15:01:15 rgb
++ * Added spin_lock() check to prevent double-locking for multiple
++ * transforms and hence kernel lock-ups with SMP kernels.
++ * Minor spin_unlock() adjustments to unlock before non-dependant prints
++ * and IPSEC device stats updates.
++ *
++ * Revision 1.78 2001/04/21 23:04:24 rgb
++ * Check if soft expire has already been sent before sending another to
++ * prevent ACQUIRE flooding.
++ *
++ * Revision 1.77 2001/03/16 07:35:20 rgb
++ * Ditch extra #if 1 around now permanent policy checking code.
++ *
++ * Revision 1.76 2001/02/27 22:24:54 rgb
++ * Re-formatting debug output (line-splitting, joining, 1arg/line).
++ * Check for satoa() return codes.
++ *
++ * Revision 1.75 2001/02/19 22:28:30 rgb
++ * Minor change to virtual device discovery code to assert which I/F has
++ * been found.
++ *
++ * Revision 1.74 2000/11/25 03:50:36 rgb
++ * Oops fix by minor re-arrangement of code to avoid accessing a freed tdb.
++ *
++ * Revision 1.73 2000/11/09 20:52:15 rgb
++ * More spinlock shuffling, locking earlier and unlocking later in rcv to
++ * include ipcomp and prevent races, renaming some tdb variables that got
++ * forgotten, moving some unlocks to include tdbs and adding a missing
++ * unlock. Thanks to Svenning for some of these.
++ *
++ * Revision 1.72 2000/11/09 20:11:22 rgb
++ * Minor shuffles to fix non-standard kernel config option selection.
++ *
++ * Revision 1.71 2000/11/06 04:36:18 rgb
++ * Ditched spin_lock_irqsave in favour of spin_lock.
++ * Minor initial protocol check rewrite.
++ * Clean up debug printing.
++ * Clean up tdb handling on ipcomp.
++ * Fixed transport mode null pointer de-reference without ipcomp.
++ * Add Svenning's adaptive content compression.
++ * Disabled registration of ipcomp handler.
++ *
++ * Revision 1.70 2000/10/30 23:41:43 henry
++ * Hans-Joerg Hoexer's null-pointer fix
++ *
++ * Revision 1.69 2000/10/10 18:54:16 rgb
++ * Added a fix for incoming policy check with ipcomp enabled but
++ * uncompressible.
++ *
++ * Revision 1.68 2000/09/22 17:53:12 rgb
++ * Fixed ipcomp tdb pointers update for policy checking.
++ *
++ * Revision 1.67 2000/09/21 03:40:58 rgb
++ * Added more debugging to try and track down the cpi outward copy problem.
++ *
++ * Revision 1.66 2000/09/20 04:00:10 rgb
++ * Changed static functions to DEBUG_NO_STATIC to reveal function names for
++ * debugging oopsen.
++ *
++ * Revision 1.65 2000/09/19 07:07:16 rgb
++ * Added debugging to inbound policy check for ipcomp.
++ * Added missing spin_unlocks (thanks Svenning!).
++ * Fixed misplaced tdbnext pointers causing mismatched ipip policy check.
++ * Protect ipcomp policy check following ipip decap with sysctl switch.
++ *
++ * Revision 1.64 2000/09/18 21:27:29 rgb
++ * 2.0 fixes.
++ *
++ * Revision 1.63 2000/09/18 02:35:50 rgb
++ * Added policy checking to ipcomp and re-enabled policy checking by
++ * default.
++ * Optimised satoa calls.
++ *
++ * Revision 1.62 2000/09/17 21:02:32 rgb
++ * Clean up debugging, removing slow timestamp debug code.
++ *
++ * Revision 1.61 2000/09/16 01:07:55 rgb
++ * Fixed erroneous ref from struct ipcomp to struct ipcomphdr.
++ *
++ * Revision 1.60 2000/09/15 11:37:01 rgb
++ * Merge in heavily modified Svenning Soerensen's <svenning@post5.tele.dk>
++ * IPCOMP zlib deflate code.
++ *
++ * Revision 1.59 2000/09/15 04:56:20 rgb
++ * Remove redundant satoa() call, reformat comment.
++ *
++ * Revision 1.58 2000/09/13 08:00:52 rgb
++ * Flick on inbound policy checking.
++ *
++ * Revision 1.57 2000/09/12 03:22:19 rgb
++ * Converted inbound_policy_check to sysctl.
++ * Re-enabled policy backcheck.
++ * Moved policy checks to top and within tdb lock.
++ *
++ * Revision 1.56 2000/09/08 19:12:56 rgb
++ * Change references from DEBUG_IPSEC to CONFIG_IPSEC_DEBUG.
++ *
++ * Revision 1.55 2000/08/28 18:15:46 rgb
++ * Added MB's nf-debug reset patch.
++ *
++ * Revision 1.54 2000/08/27 01:41:26 rgb
++ * More minor tweaks to the bad padding debug code.
++ *
++ * Revision 1.53 2000/08/24 16:54:16 rgb
++ * Added KLIPS_PRINTMORE macro to continue lines without KERN_INFO level
++ * info.
++ * Tidied up device reporting at the start of ipsec_rcv.
++ * Tidied up bad padding debugging and processing.
++ *
++ * Revision 1.52 2000/08/20 21:36:03 rgb
++ * Activated pfkey_expire() calls.
++ * Added a hard/soft expiry parameter to pfkey_expire().
++ * Added sanity checking to avoid propagating zero or smaller-length skbs
++ * from a bogus decryption.
++ * Re-arranged the order of soft and hard expiry to conform to RFC2367.
++ * Clean up references to CONFIG_IPSEC_PFKEYv2.
++ *
++ * Revision 1.51 2000/08/18 21:23:30 rgb
++ * Improve bad padding warning so that the printk buffer doesn't get
++ * trampled.
++ *
++ * Revision 1.50 2000/08/01 14:51:51 rgb
++ * Removed _all_ remaining traces of DES.
++ *
++ * Revision 1.49 2000/07/28 13:50:53 rgb
++ * Changed enet_statistics to net_device_stats and added back compatibility
++ * for pre-2.1.19.
++ *
++ * Revision 1.48 2000/05/10 19:14:40 rgb
++ * Only check usetime against soft and hard limits if the tdb has been
++ * used.
++ * Cast output of ntohl so that the broken prototype doesn't make our
++ * compile noisy.
++ *
++ * Revision 1.47 2000/05/09 17:45:43 rgb
++ * Fix replay bitmap corruption bug upon receipt of bogus packet
++ * with correct SPI. This was a DoS.
++ *
++ * Revision 1.46 2000/03/27 02:31:58 rgb
++ * Fixed authentication failure printout bug.
++ *
++ * Revision 1.45 2000/03/22 16:15:37 rgb
++ * Fixed renaming of dev_get (MB).
++ *
++ * Revision 1.44 2000/03/16 08:17:24 rgb
++ * Hardcode PF_KEYv2 support.
++ * Fixed minor bug checking AH header length.
++ *
++ * Revision 1.43 2000/03/14 12:26:59 rgb
++ * Added skb->nfct support for clearing netfilter conntrack bits (MB).
++ *
++ * Revision 1.42 2000/01/26 10:04:04 rgb
++ * Fixed inbound policy checking on transport mode bug.
++ * Fixed noisy 2.0 printk arguments.
++ *
++ * Revision 1.41 2000/01/24 20:58:02 rgb
++ * Improve debugging/reporting support for (disabled) inbound
++ * policy checking.
++ *
++ * Revision 1.40 2000/01/22 23:20:10 rgb
++ * Fixed up inboud policy checking code.
++ * Cleaned out unused crud.
++ *
++ * Revision 1.39 2000/01/21 06:15:29 rgb
++ * Added sanity checks on skb_push(), skb_pull() to prevent panics.
++ * Fixed cut-and-paste debug_tunnel to debug_rcv.
++ * Added inbound policy checking code, disabled.
++ * Simplified output code by updating ipp to post-IPIP decapsulation.
++ *
++ * elided pre-2000 comments. Use "cvs log"
++ *
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/net/ipsec/ipsec_sa.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,1383 @@
++/*
++ * Common routines for IPsec SA maintenance routines.
++ *
++ * Copyright (C) 1996, 1997 John Ioannidis.
++ * Copyright (C) 1998, 1999, 2000, 2001, 2002 Richard Guy Briggs.
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ * RCSID $Id$
++ *
++ * This is the file formerly known as "ipsec_xform.h"
++ *
++ */
++
++#include <linux/config.h>
++#include <linux/version.h>
++#include <linux/kernel.h> /* printk() */
++
++#include "openswan/ipsec_param.h"
++
++#ifdef MALLOC_SLAB
++# include <linux/slab.h> /* kmalloc() */
++#else /* MALLOC_SLAB */
++# include <linux/malloc.h> /* kmalloc() */
++#endif /* MALLOC_SLAB */
++#include <linux/vmalloc.h> /* vmalloc() */
++#include <linux/errno.h> /* error codes */
++#include <linux/types.h> /* size_t */
++#include <linux/interrupt.h> /* mark_bh */
++
++#include <linux/netdevice.h> /* struct device, and other headers */
++#include <linux/etherdevice.h> /* eth_type_trans */
++#include <linux/ip.h> /* struct iphdr */
++#include <linux/skbuff.h>
++#include <openswan.h>
++#ifdef SPINLOCK
++#ifdef SPINLOCK_23
++#include <linux/spinlock.h> /* *lock* */
++#else /* SPINLOCK_23 */
++#include <asm/spinlock.h> /* *lock* */
++#endif /* SPINLOCK_23 */
++#endif /* SPINLOCK */
++#ifdef NET_21
++#include <asm/uaccess.h>
++#include <linux/in6.h>
++#endif
++#include <asm/checksum.h>
++#include <net/ip.h>
++
++#include "openswan/radij.h"
++
++#include "openswan/ipsec_stats.h"
++#include "openswan/ipsec_life.h"
++#include "openswan/ipsec_sa.h"
++#include "openswan/ipsec_xform.h"
++
++#include "openswan/ipsec_encap.h"
++#include "openswan/ipsec_radij.h"
++#include "openswan/ipsec_xform.h"
++#include "openswan/ipsec_ipe4.h"
++#include "openswan/ipsec_ah.h"
++#include "openswan/ipsec_esp.h"
++
++#include <pfkeyv2.h>
++#include <pfkey.h>
++
++#include "openswan/ipsec_proto.h"
++#include "openswan/ipsec_alg.h"
++
++
++#ifdef CONFIG_IPSEC_DEBUG
++int debug_xform = 0;
++#endif /* CONFIG_IPSEC_DEBUG */
++
++#define SENDERR(_x) do { error = -(_x); goto errlab; } while (0)
++
++struct ipsec_sa *ipsec_sadb_hash[SADB_HASHMOD];
++#ifdef SPINLOCK
++spinlock_t tdb_lock = SPIN_LOCK_UNLOCKED;
++#else /* SPINLOCK */
++spinlock_t tdb_lock;
++#endif /* SPINLOCK */
++
++struct ipsec_sadb ipsec_sadb;
++
++#if IPSEC_SA_REF_CODE
++
++/* the sub table must be narrower (or equal) in bits than the variable type
++ in the main table to count the number of unused entries in it. */
++typedef struct {
++ int testSizeOf_refSubTable :
++ ((sizeof(IPsecRefTableUnusedCount) * 8) < IPSEC_SA_REF_SUBTABLE_IDX_WIDTH ? -1 : 1);
++} dummy;
++
++
++/* The field where the saref will be hosted in the skb must be wide enough to
++ accomodate the information it needs to store. */
++typedef struct {
++ int testSizeOf_refField :
++ (IPSEC_SA_REF_HOST_FIELD_WIDTH < IPSEC_SA_REF_TABLE_IDX_WIDTH ? -1 : 1 );
++} dummy2;
++
++
++#define IPS_HASH(said) (((said)->spi + (said)->dst.u.v4.sin_addr.s_addr + (said)->proto) % SADB_HASHMOD)
++
++
++void
++ipsec_SAtest(void)
++{
++ IPsecSAref_t SAref = 258;
++ struct ipsec_sa ips;
++ ips.ips_ref = 772;
++
++ printk("klips_debug:ipsec_SAtest: "
++ "IPSEC_SA_REF_SUBTABLE_IDX_WIDTH=%u\n"
++ "IPSEC_SA_REF_MAINTABLE_NUM_ENTRIES=%u\n"
++ "IPSEC_SA_REF_SUBTABLE_NUM_ENTRIES=%u\n"
++ "IPSEC_SA_REF_HOST_FIELD_WIDTH=%lu\n"
++ "IPSEC_SA_REF_TABLE_MASK=%x\n"
++ "IPSEC_SA_REF_ENTRY_MASK=%x\n"
++ "IPsecSAref2table(%d)=%u\n"
++ "IPsecSAref2entry(%d)=%u\n"
++ "IPsecSAref2NFmark(%d)=%u\n"
++ "IPsecSAref2SA(%d)=%p\n"
++ "IPsecSA2SAref(%p)=%d\n"
++ ,
++ IPSEC_SA_REF_SUBTABLE_IDX_WIDTH,
++ IPSEC_SA_REF_MAINTABLE_NUM_ENTRIES,
++ IPSEC_SA_REF_SUBTABLE_NUM_ENTRIES,
++ (unsigned long) IPSEC_SA_REF_HOST_FIELD_WIDTH,
++ IPSEC_SA_REF_TABLE_MASK,
++ IPSEC_SA_REF_ENTRY_MASK,
++ SAref, IPsecSAref2table(SAref),
++ SAref, IPsecSAref2entry(SAref),
++ SAref, IPsecSAref2NFmark(SAref),
++ SAref, IPsecSAref2SA(SAref),
++ (&ips), IPsecSA2SAref((&ips))
++ );
++ return;
++}
++
++int
++ipsec_SAref_recycle(void)
++{
++ int table;
++ int entry;
++ int error = 0;
++
++ ipsec_sadb.refFreeListHead = -1;
++ ipsec_sadb.refFreeListTail = -1;
++
++ if(ipsec_sadb.refFreeListCont == IPSEC_SA_REF_MAINTABLE_NUM_ENTRIES * IPSEC_SA_REF_SUBTABLE_NUM_ENTRIES) {
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_SAref_recycle: "
++ "end of table reached, continuing at start..\n");
++ ipsec_sadb.refFreeListCont = 0;
++ }
++
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_SAref_recycle: "
++ "recycling, continuing from SAref=%d (0p%p), table=%d, entry=%d.\n",
++ ipsec_sadb.refFreeListCont,
++ (ipsec_sadb.refTable[IPsecSAref2table(ipsec_sadb.refFreeListCont)] != NULL) ? IPsecSAref2SA(ipsec_sadb.refFreeListCont) : NULL,
++ IPsecSAref2table(ipsec_sadb.refFreeListCont),
++ IPsecSAref2entry(ipsec_sadb.refFreeListCont));
++
++ for(table = IPsecSAref2table(ipsec_sadb.refFreeListCont);
++ table < IPSEC_SA_REF_MAINTABLE_NUM_ENTRIES;
++ table++) {
++ if(ipsec_sadb.refTable[table] == NULL) {
++ error = ipsec_SArefSubTable_alloc(table);
++ if(error) {
++ return error;
++ }
++ }
++ for(entry = IPsecSAref2entry(ipsec_sadb.refFreeListCont);
++ entry < IPSEC_SA_REF_SUBTABLE_NUM_ENTRIES;
++ entry++) {
++ if(ipsec_sadb.refTable[table]->entry[entry] == NULL) {
++ ipsec_sadb.refFreeList[++ipsec_sadb.refFreeListTail] = IPsecSArefBuild(table, entry);
++ if(ipsec_sadb.refFreeListTail == (IPSEC_SA_REF_FREELIST_NUM_ENTRIES - 1)) {
++ ipsec_sadb.refFreeListHead = 0;
++ ipsec_sadb.refFreeListCont = ipsec_sadb.refFreeList[ipsec_sadb.refFreeListTail] + 1;
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_SAref_recycle: "
++ "SArefFreeList refilled.\n");
++ return 0;
++ }
++ }
++ }
++ }
++
++ if(ipsec_sadb.refFreeListTail == -1) {
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_SAref_recycle: "
++ "out of room in the SArefTable.\n");
++
++ return(-ENOSPC);
++ }
++
++ ipsec_sadb.refFreeListHead = 0;
++ ipsec_sadb.refFreeListCont = ipsec_sadb.refFreeList[ipsec_sadb.refFreeListTail] + 1;
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_SAref_recycle: "
++ "SArefFreeList partly refilled to %d of %d.\n",
++ ipsec_sadb.refFreeListTail,
++ IPSEC_SA_REF_FREELIST_NUM_ENTRIES);
++ return 0;
++}
++
++int
++ipsec_SArefSubTable_alloc(unsigned table)
++{
++ unsigned entry;
++ struct IPsecSArefSubTable* SArefsub;
++
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_SArefSubTable_alloc: "
++ "allocating %lu bytes for table %u of %u.\n",
++ (unsigned long) (IPSEC_SA_REF_SUBTABLE_NUM_ENTRIES * sizeof(struct ipsec_sa *)),
++ table,
++ IPSEC_SA_REF_MAINTABLE_NUM_ENTRIES);
++
++ /* allocate another sub-table */
++ SArefsub = vmalloc(IPSEC_SA_REF_SUBTABLE_NUM_ENTRIES * sizeof(struct ipsec_sa *));
++ if(SArefsub == NULL) {
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_SArefSubTable_alloc: "
++ "error allocating memory for table %u of %u!\n",
++ table,
++ IPSEC_SA_REF_MAINTABLE_NUM_ENTRIES);
++ return -ENOMEM;
++ }
++
++ /* add this sub-table to the main table */
++ ipsec_sadb.refTable[table] = SArefsub;
++
++ /* initialise each element to NULL */
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_SArefSubTable_alloc: "
++ "initialising %u elements (2 ^ %u) of table %u.\n",
++ IPSEC_SA_REF_SUBTABLE_NUM_ENTRIES,
++ IPSEC_SA_REF_SUBTABLE_IDX_WIDTH,
++ table);
++ for(entry = 0; entry < IPSEC_SA_REF_SUBTABLE_NUM_ENTRIES; entry++) {
++ SArefsub->entry[entry] = NULL;
++ }
++
++ return 0;
++}
++#endif /* IPSEC_SA_REF_CODE */
++
++int
++ipsec_saref_freelist_init(void)
++{
++ int i;
++
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_saref_freelist_init: "
++ "initialising %u elements of FreeList.\n",
++ IPSEC_SA_REF_FREELIST_NUM_ENTRIES);
++
++ for(i = 0; i < IPSEC_SA_REF_FREELIST_NUM_ENTRIES; i++) {
++ ipsec_sadb.refFreeList[i] = IPSEC_SAREF_NULL;
++ }
++ ipsec_sadb.refFreeListHead = -1;
++ ipsec_sadb.refFreeListCont = 0;
++ ipsec_sadb.refFreeListTail = -1;
++
++ return 0;
++}
++
++int
++ipsec_sadb_init(void)
++{
++ int error = 0;
++ unsigned i;
++
++ for(i = 0; i < SADB_HASHMOD; i++) {
++ ipsec_sadb_hash[i] = NULL;
++ }
++ /* parts above are for the old style SADB hash table */
++
++
++#if IPSEC_SA_REF_CODE
++ /* initialise SA reference table */
++
++ /* initialise the main table */
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_sadb_init: "
++ "initialising main table of size %u (2 ^ %u).\n",
++ IPSEC_SA_REF_MAINTABLE_NUM_ENTRIES,
++ IPSEC_SA_REF_MAINTABLE_IDX_WIDTH);
++ {
++ unsigned table;
++ for(table = 0; table < IPSEC_SA_REF_MAINTABLE_NUM_ENTRIES; table++) {
++ ipsec_sadb.refTable[table] = NULL;
++ }
++ }
++
++ /* allocate the first sub-table */
++ error = ipsec_SArefSubTable_alloc(0);
++ if(error) {
++ return error;
++ }
++
++ error = ipsec_saref_freelist_init();
++#endif /* IPSEC_SA_REF_CODE */
++ return error;
++}
++
++#if IPSEC_SA_REF_CODE
++IPsecSAref_t
++ipsec_SAref_alloc(int*error) /* pass in error var by pointer */
++{
++ IPsecSAref_t SAref;
++
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_SAref_alloc: "
++ "SAref requested... head=%d, cont=%d, tail=%d, listsize=%d.\n",
++ ipsec_sadb.refFreeListHead,
++ ipsec_sadb.refFreeListCont,
++ ipsec_sadb.refFreeListTail,
++ IPSEC_SA_REF_FREELIST_NUM_ENTRIES);
++
++ if(ipsec_sadb.refFreeListHead == -1) {
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_SAref_alloc: "
++ "FreeList empty, recycling...\n");
++ *error = ipsec_SAref_recycle();
++ if(*error) {
++ return IPSEC_SAREF_NULL;
++ }
++ }
++
++ SAref = ipsec_sadb.refFreeList[ipsec_sadb.refFreeListHead];
++ if(SAref == IPSEC_SAREF_NULL) {
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_SAref_alloc: "
++ "unexpected error, refFreeListHead = %d points to invalid entry.\n",
++ ipsec_sadb.refFreeListHead);
++ *error = -ESPIPE;
++ return IPSEC_SAREF_NULL;
++ }
++
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_SAref_alloc: "
++ "allocating SAref=%d, table=%u, entry=%u of %u.\n",
++ SAref,
++ IPsecSAref2table(SAref),
++ IPsecSAref2entry(SAref),
++ IPSEC_SA_REF_MAINTABLE_NUM_ENTRIES * IPSEC_SA_REF_SUBTABLE_NUM_ENTRIES);
++
++ ipsec_sadb.refFreeList[ipsec_sadb.refFreeListHead] = IPSEC_SAREF_NULL;
++ ipsec_sadb.refFreeListHead++;
++ if(ipsec_sadb.refFreeListHead > ipsec_sadb.refFreeListTail) {
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_SAref_alloc: "
++ "last FreeList entry allocated, resetting list head to empty.\n");
++ ipsec_sadb.refFreeListHead = -1;
++ }
++
++ return SAref;
++}
++#endif /* IPSEC_SA_REF_CODE */
++
++int
++ipsec_sa_print(struct ipsec_sa *ips)
++{
++ char sa[SATOT_BUF];
++ size_t sa_len;
++
++ printk(KERN_INFO "klips_debug: SA:");
++ if(ips == NULL) {
++ printk("NULL\n");
++ return -ENOENT;
++ }
++ printk(" ref=%d", ips->ips_ref);
++ printk(" refcount=%d", atomic_read(&ips->ips_refcount));
++ if(ips->ips_hnext != NULL) {
++ printk(" hnext=0p%p", ips->ips_hnext);
++ }
++ if(ips->ips_inext != NULL) {
++ printk(" inext=0p%p", ips->ips_inext);
++ }
++ if(ips->ips_onext != NULL) {
++ printk(" onext=0p%p", ips->ips_onext);
++ }
++ sa_len = satot(&ips->ips_said, 0, sa, sizeof(sa));
++ printk(" said=%s", sa_len ? sa : " (error)");
++ if(ips->ips_seq) {
++ printk(" seq=%u", ips->ips_seq);
++ }
++ if(ips->ips_pid) {
++ printk(" pid=%u", ips->ips_pid);
++ }
++ if(ips->ips_authalg) {
++ printk(" authalg=%u", ips->ips_authalg);
++ }
++ if(ips->ips_encalg) {
++ printk(" encalg=%u", ips->ips_encalg);
++ }
++ printk(" XFORM=%s%s%s", IPS_XFORM_NAME(ips));
++ if(ips->ips_replaywin) {
++ printk(" ooowin=%u", ips->ips_replaywin);
++ }
++ if(ips->ips_flags) {
++ printk(" flags=%u", ips->ips_flags);
++ }
++ if(ips->ips_addr_s) {
++ char buf[SUBNETTOA_BUF];
++ addrtoa(((struct sockaddr_in*)(ips->ips_addr_s))->sin_addr,
++ 0, buf, sizeof(buf));
++ printk(" src=%s", buf);
++ }
++ if(ips->ips_addr_d) {
++ char buf[SUBNETTOA_BUF];
++ addrtoa(((struct sockaddr_in*)(ips->ips_addr_s))->sin_addr,
++ 0, buf, sizeof(buf));
++ printk(" dst=%s", buf);
++ }
++ if(ips->ips_addr_p) {
++ char buf[SUBNETTOA_BUF];
++ addrtoa(((struct sockaddr_in*)(ips->ips_addr_p))->sin_addr,
++ 0, buf, sizeof(buf));
++ printk(" proxy=%s", buf);
++ }
++ if(ips->ips_key_bits_a) {
++ printk(" key_bits_a=%u", ips->ips_key_bits_a);
++ }
++ if(ips->ips_key_bits_e) {
++ printk(" key_bits_e=%u", ips->ips_key_bits_e);
++ }
++
++ printk("\n");
++ return 0;
++}
++
++struct ipsec_sa*
++ipsec_sa_alloc(int*error) /* pass in error var by pointer */
++{
++ struct ipsec_sa* ips;
++
++ if((ips = kmalloc(sizeof(*ips), GFP_ATOMIC) ) == NULL) {
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_sa_alloc: "
++ "memory allocation error\n");
++ *error = -ENOMEM;
++ return NULL;
++ }
++ memset((caddr_t)ips, 0, sizeof(*ips));
++#if IPSEC_SA_REF_CODE
++ ips->ips_ref = ipsec_SAref_alloc(error); /* pass in error return by pointer */
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_sa_alloc: "
++ "allocated %lu bytes for ipsec_sa struct=0p%p ref=%d.\n",
++ (unsigned long) sizeof(*ips),
++ ips,
++ ips->ips_ref);
++ if(ips->ips_ref == IPSEC_SAREF_NULL) {
++ kfree(ips);
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_sa_alloc: "
++ "SAref allocation error\n");
++ return NULL;
++ }
++
++ atomic_inc(&ips->ips_refcount);
++ IPsecSAref2SA(ips->ips_ref) = ips;
++#endif /* IPSEC_SA_REF_CODE */
++
++ *error = 0;
++ return(ips);
++}
++
++int
++ipsec_sa_free(struct ipsec_sa* ips)
++{
++ return ipsec_sa_wipe(ips);
++}
++
++struct ipsec_sa *
++ipsec_sa_getbyid(ip_said *said)
++{
++ int hashval;
++ struct ipsec_sa *ips;
++ char sa[SATOT_BUF];
++ size_t sa_len;
++
++ if(said == NULL) {
++ KLIPS_PRINT(debug_xform,
++ "klips_error:ipsec_sa_getbyid: "
++ "null pointer passed in!\n");
++ return NULL;
++ }
++
++ sa_len = satot(said, 0, sa, sizeof(sa));
++
++ hashval = IPS_HASH(said);
++
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_sa_getbyid: "
++ "linked entry in ipsec_sa table for hash=%d of SA:%s requested.\n",
++ hashval,
++ sa_len ? sa : " (error)");
++
++ if((ips = ipsec_sadb_hash[hashval]) == NULL) {
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_sa_getbyid: "
++ "no entries in ipsec_sa table for hash=%d of SA:%s.\n",
++ hashval,
++ sa_len ? sa : " (error)");
++ return NULL;
++ }
++
++ for (; ips; ips = ips->ips_hnext) {
++ if ((ips->ips_said.spi == said->spi) &&
++ (ips->ips_said.dst.u.v4.sin_addr.s_addr == said->dst.u.v4.sin_addr.s_addr) &&
++ (ips->ips_said.proto == said->proto)) {
++ atomic_inc(&ips->ips_refcount);
++ return ips;
++ }
++ }
++
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_sa_getbyid: "
++ "no entry in linked list for hash=%d of SA:%s.\n",
++ hashval,
++ sa_len ? sa : " (error)");
++ return NULL;
++}
++
++int
++ipsec_sa_put(struct ipsec_sa *ips)
++{
++ char sa[SATOT_BUF];
++ size_t sa_len;
++
++ if(ips == NULL) {
++ KLIPS_PRINT(debug_xform,
++ "klips_error:ipsec_sa_put: "
++ "null pointer passed in!\n");
++ return -1;
++ }
++
++ sa_len = satot(&ips->ips_said, 0, sa, sizeof(sa));
++
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_sa_put: "
++ "ipsec_sa SA:%s, ref:%d reference count decremented.\n",
++ sa_len ? sa : " (error)",
++ ips->ips_ref);
++
++ atomic_dec(&ips->ips_refcount);
++
++ return 0;
++}
++
++/*
++ The ipsec_sa table better *NOT* be locked before it is handed in, or SMP locks will happen
++*/
++int
++ipsec_sa_add(struct ipsec_sa *ips)
++{
++ int error = 0;
++ unsigned int hashval;
++
++ if(ips == NULL) {
++ KLIPS_PRINT(debug_xform,
++ "klips_error:ipsec_sa_add: "
++ "null pointer passed in!\n");
++ return -ENODATA;
++ }
++ hashval = IPS_HASH(&ips->ips_said);
++
++ atomic_inc(&ips->ips_refcount);
++ spin_lock_bh(&tdb_lock);
++
++ ips->ips_hnext = ipsec_sadb_hash[hashval];
++ ipsec_sadb_hash[hashval] = ips;
++
++ spin_unlock_bh(&tdb_lock);
++
++ return error;
++}
++
++/*
++ The ipsec_sa table better be locked before it is handed in, or races might happen
++*/
++int
++ipsec_sa_del(struct ipsec_sa *ips)
++{
++ unsigned int hashval;
++ struct ipsec_sa *ipstp;
++ char sa[SATOT_BUF];
++ size_t sa_len;
++
++ if(ips == NULL) {
++ KLIPS_PRINT(debug_xform,
++ "klips_error:ipsec_sa_del: "
++ "null pointer passed in!\n");
++ return -ENODATA;
++ }
++
++ sa_len = satot(&ips->ips_said, 0, sa, sizeof(sa));
++ if(ips->ips_inext || ips->ips_onext) {
++ KLIPS_PRINT(debug_xform,
++ "klips_error:ipsec_sa_del: "
++ "SA:%s still linked!\n",
++ sa_len ? sa : " (error)");
++ return -EMLINK;
++ }
++
++ hashval = IPS_HASH(&ips->ips_said);
++
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_sa_del: "
++ "deleting SA:%s, hashval=%d.\n",
++ sa_len ? sa : " (error)",
++ hashval);
++ if(ipsec_sadb_hash[hashval] == NULL) {
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_sa_del: "
++ "no entries in ipsec_sa table for hash=%d of SA:%s.\n",
++ hashval,
++ sa_len ? sa : " (error)");
++ return -ENOENT;
++ }
++
++ if (ips == ipsec_sadb_hash[hashval]) {
++ ipsec_sadb_hash[hashval] = ipsec_sadb_hash[hashval]->ips_hnext;
++ ips->ips_hnext = NULL;
++ atomic_dec(&ips->ips_refcount);
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_sa_del: "
++ "successfully deleted first ipsec_sa in chain.\n");
++ return 0;
++ } else {
++ for (ipstp = ipsec_sadb_hash[hashval];
++ ipstp;
++ ipstp = ipstp->ips_hnext) {
++ if (ipstp->ips_hnext == ips) {
++ ipstp->ips_hnext = ips->ips_hnext;
++ ips->ips_hnext = NULL;
++ atomic_dec(&ips->ips_refcount);
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_sa_del: "
++ "successfully deleted link in ipsec_sa chain.\n");
++ return 0;
++ }
++ }
++ }
++
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_sa_del: "
++ "no entries in linked list for hash=%d of SA:%s.\n",
++ hashval,
++ sa_len ? sa : " (error)");
++ return -ENOENT;
++}
++
++/*
++ The ipsec_sa table better be locked before it is handed in, or races
++ might happen
++*/
++int
++ipsec_sa_delchain(struct ipsec_sa *ips)
++{
++ struct ipsec_sa *ipsdel;
++ int error = 0;
++ char sa[SATOT_BUF];
++ size_t sa_len;
++
++ if(ips == NULL) {
++ KLIPS_PRINT(debug_xform,
++ "klips_error:ipsec_sa_delchain: "
++ "null pointer passed in!\n");
++ return -ENODATA;
++ }
++
++ sa_len = satot(&ips->ips_said, 0, sa, sizeof(sa));
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_sa_delchain: "
++ "passed SA:%s\n",
++ sa_len ? sa : " (error)");
++ while(ips->ips_onext != NULL) {
++ ips = ips->ips_onext;
++ }
++
++ while(ips) {
++ /* XXX send a pfkey message up to advise of deleted ipsec_sa */
++ sa_len = satot(&ips->ips_said, 0, sa, sizeof(sa));
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_sa_delchain: "
++ "unlinking and delting SA:%s",
++ sa_len ? sa : " (error)");
++ ipsdel = ips;
++ ips = ips->ips_inext;
++ if(ips != NULL) {
++ sa_len = satot(&ips->ips_said, 0, sa, sizeof(sa));
++ KLIPS_PRINT(debug_xform,
++ ", inext=%s",
++ sa_len ? sa : " (error)");
++ atomic_dec(&ipsdel->ips_refcount);
++ ipsdel->ips_inext = NULL;
++ atomic_dec(&ips->ips_refcount);
++ ips->ips_onext = NULL;
++ }
++ KLIPS_PRINT(debug_xform,
++ ".\n");
++ if((error = ipsec_sa_del(ipsdel))) {
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_sa_delchain: "
++ "ipsec_sa_del returned error %d.\n", -error);
++ return error;
++ }
++ if((error = ipsec_sa_wipe(ipsdel))) {
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_sa_delchain: "
++ "ipsec_sa_wipe returned error %d.\n", -error);
++ return error;
++ }
++ }
++ return error;
++}
++
++int
++ipsec_sadb_cleanup(__u8 proto)
++{
++ unsigned i;
++ int error = 0;
++ struct ipsec_sa *ips, **ipsprev, *ipsdel;
++ char sa[SATOT_BUF];
++ size_t sa_len;
++
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_sadb_cleanup: "
++ "cleaning up proto=%d.\n",
++ proto);
++
++ spin_lock_bh(&tdb_lock);
++
++ for (i = 0; i < SADB_HASHMOD; i++) {
++ ipsprev = &(ipsec_sadb_hash[i]);
++ ips = ipsec_sadb_hash[i];
++ if(ips != NULL) {
++ atomic_inc(&ips->ips_refcount);
++ }
++ for(; ips != NULL;) {
++ sa_len = satot(&ips->ips_said, 0, sa, sizeof(sa));
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_sadb_cleanup: "
++ "checking SA:%s, hash=%d, ref=%d",
++ sa_len ? sa : " (error)",
++ i,
++ ips->ips_ref);
++ ipsdel = ips;
++ ips = ipsdel->ips_hnext;
++ if(ips != NULL) {
++ atomic_inc(&ips->ips_refcount);
++ sa_len = satot(&ips->ips_said, 0, sa, sizeof(sa));
++ KLIPS_PRINT(debug_xform,
++ ", hnext=%s",
++ sa_len ? sa : " (error)");
++ }
++ if(*ipsprev != NULL) {
++ sa_len = satot(&(*ipsprev)->ips_said, 0, sa, sizeof(sa));
++ KLIPS_PRINT(debug_xform,
++ ", *ipsprev=%s",
++ sa_len ? sa : " (error)");
++ if((*ipsprev)->ips_hnext) {
++ sa_len = satot(&(*ipsprev)->ips_hnext->ips_said, 0, sa, sizeof(sa));
++ KLIPS_PRINT(debug_xform,
++ ", *ipsprev->ips_hnext=%s",
++ sa_len ? sa : " (error)");
++ }
++ }
++ KLIPS_PRINT(debug_xform,
++ ".\n");
++ if(proto == 0 || (proto == ipsdel->ips_said.proto)) {
++ sa_len = satot(&ipsdel->ips_said, 0, sa, sizeof(sa));
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_sadb_cleanup: "
++ "deleting SA chain:%s.\n",
++ sa_len ? sa : " (error)");
++ if((error = ipsec_sa_delchain(ipsdel))) {
++ SENDERR(-error);
++ }
++ ipsprev = &(ipsec_sadb_hash[i]);
++ ips = ipsec_sadb_hash[i];
++
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_sadb_cleanup: "
++ "deleted SA chain:%s",
++ sa_len ? sa : " (error)");
++ if(ips != NULL) {
++ sa_len = satot(&ips->ips_said, 0, sa, sizeof(sa));
++ KLIPS_PRINT(debug_xform,
++ ", ipsec_sadb_hash[%d]=%s",
++ i,
++ sa_len ? sa : " (error)");
++ }
++ if(*ipsprev != NULL) {
++ sa_len = satot(&(*ipsprev)->ips_said, 0, sa, sizeof(sa));
++ KLIPS_PRINT(debug_xform,
++ ", *ipsprev=%s",
++ sa_len ? sa : " (error)");
++ if((*ipsprev)->ips_hnext != NULL) {
++ sa_len = satot(&(*ipsprev)->ips_hnext->ips_said, 0, sa, sizeof(sa));
++ KLIPS_PRINT(debug_xform,
++ ", *ipsprev->ips_hnext=%s",
++ sa_len ? sa : " (error)");
++ }
++ }
++ KLIPS_PRINT(debug_xform,
++ ".\n");
++ } else {
++ ipsprev = &ipsdel;
++ }
++ if(ipsdel != NULL) {
++ ipsec_sa_put(ipsdel);
++ }
++ }
++ }
++ errlab:
++
++ spin_unlock_bh(&tdb_lock);
++
++
++#if IPSEC_SA_REF_CODE
++ /* clean up SA reference table */
++
++ /* go through the ref table and clean out all the SAs */
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_sadb_cleanup: "
++ "removing SAref entries and tables.");
++ {
++ unsigned table, entry;
++ for(table = 0; table < IPSEC_SA_REF_MAINTABLE_NUM_ENTRIES; table++) {
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_sadb_cleanup: "
++ "cleaning SAref table=%u.\n",
++ table);
++ if(ipsec_sadb.refTable[table] == NULL) {
++ printk("\n");
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_sadb_cleanup: "
++ "cleaned %u used refTables.\n",
++ table);
++ break;
++ }
++ for(entry = 0; entry < IPSEC_SA_REF_SUBTABLE_NUM_ENTRIES; entry++) {
++ if(ipsec_sadb.refTable[table]->entry[entry] != NULL) {
++ ipsec_sa_delchain(ipsec_sadb.refTable[table]->entry[entry]);
++ ipsec_sadb.refTable[table]->entry[entry] = NULL;
++ }
++ }
++ }
++ }
++#endif /* IPSEC_SA_REF_CODE */
++
++ return(error);
++}
++
++int
++ipsec_sadb_free(void)
++{
++ int error = 0;
++
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_sadb_free: "
++ "freeing SArefTable memory.\n");
++
++ /* clean up SA reference table */
++
++ /* go through the ref table and clean out all the SAs if any are
++ left and free table memory */
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_sadb_free: "
++ "removing SAref entries and tables.\n");
++ {
++ unsigned table, entry;
++ for(table = 0; table < IPSEC_SA_REF_MAINTABLE_NUM_ENTRIES; table++) {
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_sadb_free: "
++ "removing SAref table=%u.\n",
++ table);
++ if(ipsec_sadb.refTable[table] == NULL) {
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_sadb_free: "
++ "removed %u used refTables.\n",
++ table);
++ break;
++ }
++ for(entry = 0; entry < IPSEC_SA_REF_SUBTABLE_NUM_ENTRIES; entry++) {
++ if(ipsec_sadb.refTable[table]->entry[entry] != NULL) {
++ ipsec_sa_delchain(ipsec_sadb.refTable[table]->entry[entry]);
++ ipsec_sadb.refTable[table]->entry[entry] = NULL;
++ }
++ }
++ vfree(ipsec_sadb.refTable[table]);
++ ipsec_sadb.refTable[table] = NULL;
++ }
++ }
++
++ return(error);
++}
++
++int
++ipsec_sa_wipe(struct ipsec_sa *ips)
++{
++ if(ips == NULL) {
++ return -ENODATA;
++ }
++
++ /* if(atomic_dec_and_test(ips)) {
++ }; */
++
++#if IPSEC_SA_REF_CODE
++ /* remove me from the SArefTable */
++ {
++ char sa[SATOT_BUF];
++ size_t sa_len;
++ sa_len = satot(&ips->ips_said, 0, sa, sizeof(sa));
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_sa_wipe: "
++ "removing SA=%s(0p%p), SAref=%d, table=%d(0p%p), entry=%d from the refTable.\n",
++ sa_len ? sa : " (error)",
++ ips,
++ ips->ips_ref,
++ IPsecSAref2table(IPsecSA2SAref(ips)),
++ ipsec_sadb.refTable[IPsecSAref2table(IPsecSA2SAref(ips))],
++ IPsecSAref2entry(IPsecSA2SAref(ips)));
++ }
++ if(ips->ips_ref == IPSEC_SAREF_NULL) {
++ KLIPS_PRINT(debug_xform,
++ "klips_debug:ipsec_sa_wipe: "
++ "why does this SA not have a valid SAref?.\n");
++ }
++ ipsec_sadb.refTable[IPsecSAref2table(IPsecSA2SAref(ips))]->entry[IPsecSAref2entry(IPsecSA2SAref(ips))] = NULL;
++ ips->ips_ref = IPSEC_SAREF_NULL;
++ ipsec_sa_put(ips);
++#endif /* IPSEC_SA_REF_CODE */
++
++ /* paranoid clean up */
++ if(ips->ips_addr_s != NULL) {
++ memset((caddr_t)(ips->ips_addr_s), 0, ips->ips_addr_s_size);
++ kfree(ips->ips_addr_s);
++ }
++ ips->ips_addr_s = NULL;
++
++ if(ips->ips_addr_d != NULL) {
++ memset((caddr_t)(ips->ips_addr_d), 0, ips->ips_addr_d_size);
++ kfree(ips->ips_addr_d);
++ }
++ ips->ips_addr_d = NULL;
++
++ if(ips->ips_addr_p != NULL) {
++ memset((caddr_t)(ips->ips_addr_p), 0, ips->ips_addr_p_size);
++ kfree(ips->ips_addr_p);
++ }
++ ips->ips_addr_p = NULL;
++
++#ifdef CONFIG_IPSEC_NAT_TRAVERSAL
++ if(ips->ips_natt_oa) {
++ memset((caddr_t)(ips->ips_natt_oa), 0, ips->ips_natt_oa_size);
++ kfree(ips->ips_natt_oa);
++ }
++ ips->ips_natt_oa = NULL;
++#endif
++
++ if(ips->ips_key_a != NULL) {
++ memset((caddr_t)(ips->ips_key_a), 0, ips->ips_key_a_size);
++ kfree(ips->ips_key_a);
++ }
++ ips->ips_key_a = NULL;
++
++ if(ips->ips_key_e != NULL) {
++#ifdef CONFIG_IPSEC_ALG
++ if (ips->ips_alg_enc&&ips->ips_alg_enc->ixt_e_destroy_key) {
++ ips->ips_alg_enc->ixt_e_destroy_key(ips->ips_alg_enc,
++ ips->ips_key_e);
++ } else {
++#endif /* CONFIG_IPSEC_ALG */
++ memset((caddr_t)(ips->ips_key_e), 0, ips->ips_key_e_size);
++ kfree(ips->ips_key_e);
++#ifdef CONFIG_IPSEC_ALG
++ }
++#endif /* CONFIG_IPSEC_ALG */
++ }
++ ips->ips_key_e = NULL;
++
++ if(ips->ips_iv != NULL) {
++ memset((caddr_t)(ips->ips_iv), 0, ips->ips_iv_size);
++ kfree(ips->ips_iv);
++ }
++ ips->ips_iv = NULL;
++
++ if(ips->ips_ident_s.data != NULL) {
++ memset((caddr_t)(ips->ips_ident_s.data),
++ 0,
++ ips->ips_ident_s.len * IPSEC_PFKEYv2_ALIGN - sizeof(struct sadb_ident));
++ kfree(ips->ips_ident_s.data);
++ }
++ ips->ips_ident_s.data = NULL;
++
++ if(ips->ips_ident_d.data != NULL) {
++ memset((caddr_t)(ips->ips_ident_d.data),
++ 0,
++ ips->ips_ident_d.len * IPSEC_PFKEYv2_ALIGN - sizeof(struct sadb_ident));
++ kfree(ips->ips_ident_d.data);
++ }
++ ips->ips_ident_d.data = NULL;
++
++#ifdef CONFIG_IPSEC_ALG
++ if (ips->ips_alg_enc||ips->ips_alg_auth) {
++ ipsec_alg_sa_wipe(ips);
++ }
++#endif /* CONFIG_IPSEC_ALG */
++
++ memset((caddr_t)ips, 0, sizeof(*ips));
++ kfree(ips);
++ ips = NULL;
++
++ return 0;
++}
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.23 2004/04/06 02:49:26 mcr
++ * pullup of algo code from alg-branch.
++ *
++ * Revision 1.22.2.1 2003/12/22 15:25:52 jjo
++ * . Merged algo-0.8.1-rc11-test1 into alg-branch
++ *
++ * Revision 1.22 2003/12/10 01:14:27 mcr
++ * NAT-traversal patches to KLIPS.
++ *
++ * Revision 1.21 2003/10/31 02:27:55 mcr
++ * pulled up port-selector patches and sa_id elimination.
++ *
++ * Revision 1.20.4.1 2003/10/29 01:30:41 mcr
++ * elimited "struct sa_id".
++ *
++ * Revision 1.20 2003/02/06 01:50:34 rgb
++ * Fixed initialisation bug for first sadb hash bucket that would only manifest itself on platforms where NULL != 0.
++ *
++ * Revision 1.19 2003/01/30 02:32:22 rgb
++ *
++ * Rename SAref table macro names for clarity.
++ * Transmit error code through to caller from callee for better diagnosis of problems.
++ * Convert IPsecSAref_t from signed to unsigned to fix apparent SAref exhaustion bug.
++ *
++ * Revision 1.18 2002/10/12 23:11:53 dhr
++ *
++ * [KenB + DHR] more 64-bit cleanup
++ *
++ * Revision 1.17 2002/10/07 18:31:43 rgb
++ * Move field width sanity checks to ipsec_sa.c
++ *
++ * Revision 1.16 2002/09/20 15:41:02 rgb
++ * Re-wrote most of the SAref code to eliminate Entry pointers.
++ * Added SAref code compiler directive switch.
++ * Added a saref test function for testing macros.
++ * Switch from pfkey_alloc_ipsec_sa() to ipsec_sa_alloc().
++ * Split ipsec_sadb_cleanup from new funciton ipsec_sadb_free to avoid problem
++ * of freeing newly created structures when clearing the reftable upon startup
++ * to start from a known state.
++ * Place all ipsec sadb globals into one struct.
++ * Rework saref freelist.
++ * Added memory allocation debugging.
++ *
++ * Revision 1.15 2002/09/20 05:01:44 rgb
++ * Update copyright date.
++ *
++ * Revision 1.14 2002/08/13 19:01:25 mcr
++ * patches from kenb to permit compilation of FreeSWAN on ia64.
++ * des library patched to use proper DES_LONG type for ia64.
++ *
++ * Revision 1.13 2002/07/29 03:06:20 mcr
++ * get rid of variable not used warnings.
++ *
++ * Revision 1.12 2002/07/26 08:48:31 rgb
++ * Added SA ref table code.
++ *
++ * Revision 1.11 2002/06/04 16:48:49 rgb
++ * Tidied up pointer code for processor independance.
++ *
++ * Revision 1.10 2002/05/23 07:16:17 rgb
++ * Added ipsec_sa_put() for releasing an ipsec_sa refcount.
++ * Pointer clean-up.
++ * Added refcount code.
++ * Convert "usecount" to "refcount" to remove ambiguity.
++ *
++ * Revision 1.9 2002/05/14 02:34:49 rgb
++ * Converted reference from ipsec_sa_put to ipsec_sa_add to avoid confusion
++ * with "put" usage in the kernel.
++ * Change all references to tdb, TDB or Tunnel Descriptor Block to ips,
++ * ipsec_sa or ipsec_sa.
++ * Added some preliminary refcount code.
++ *
++ * Revision 1.8 2002/04/24 07:55:32 mcr
++ * #include patches and Makefiles for post-reorg compilation.
++ *
++ * Revision 1.7 2002/04/24 07:36:30 mcr
++ * Moved from ./klips/net/ipsec/ipsec_sa.c,v
++ *
++ * Revision 1.6 2002/04/20 00:12:25 rgb
++ * Added esp IV CBC attack fix, disabled.
++ *
++ * Revision 1.5 2002/01/29 17:17:56 mcr
++ * moved include of ipsec_param.h to after include of linux/kernel.h
++ * otherwise, it seems that some option that is set in ipsec_param.h
++ * screws up something subtle in the include path to kernel.h, and
++ * it complains on the snprintf() prototype.
++ *
++ * Revision 1.4 2002/01/29 04:00:52 mcr
++ * more excise of kversions.h header.
++ *
++ * Revision 1.3 2002/01/29 02:13:18 mcr
++ * introduction of ipsec_kversion.h means that include of
++ * ipsec_param.h must preceed any decisions about what files to
++ * include to deal with differences in kernel source.
++ *
++ * Revision 1.2 2001/11/26 09:16:15 rgb
++ * Merge MCR's ipsec_sa, eroute, proc and struct lifetime changes.
++ *
++ * Revision 1.1.2.2 2001/10/22 21:05:41 mcr
++ * removed phony prototype for des_set_key.
++ *
++ * Revision 1.1.2.1 2001/09/25 02:24:57 mcr
++ * struct tdb -> struct ipsec_sa.
++ * sa(tdb) manipulation functions renamed and moved to ipsec_sa.c
++ * ipsec_xform.c removed. header file still contains useful things.
++ *
++ *
++ *
++ * CLONED from ipsec_xform.c:
++ * Revision 1.53 2001/09/08 21:13:34 rgb
++ * Added pfkey ident extension support for ISAKMPd. (NetCelo)
++ *
++ * Revision 1.52 2001/06/14 19:35:11 rgb
++ * Update copyright date.
++ *
++ * Revision 1.51 2001/05/30 08:14:03 rgb
++ * Removed vestiges of esp-null transforms.
++ *
++ * Revision 1.50 2001/05/03 19:43:18 rgb
++ * Initialise error return variable.
++ * Update SENDERR macro.
++ * Fix sign of error return code for ipsec_tdbcleanup().
++ * Use more appropriate return code for ipsec_tdbwipe().
++ *
++ * Revision 1.49 2001/04/19 18:56:17 rgb
++ * Fixed tdb table locking comments.
++ *
++ * Revision 1.48 2001/02/27 22:24:55 rgb
++ * Re-formatting debug output (line-splitting, joining, 1arg/line).
++ * Check for satoa() return codes.
++ *
++ * Revision 1.47 2000/11/06 04:32:08 rgb
++ * Ditched spin_lock_irqsave in favour of spin_lock_bh.
++ *
++ * Revision 1.46 2000/09/20 16:21:57 rgb
++ * Cleaned up ident string alloc/free.
++ *
++ * Revision 1.45 2000/09/08 19:16:51 rgb
++ * Change references from DEBUG_IPSEC to CONFIG_IPSEC_DEBUG.
++ * Removed all references to CONFIG_IPSEC_PFKEYv2.
++ *
++ * Revision 1.44 2000/08/30 05:29:04 rgb
++ * Compiler-define out no longer used tdb_init() in ipsec_xform.c.
++ *
++ * Revision 1.43 2000/08/18 21:30:41 rgb
++ * Purged all tdb_spi, tdb_proto and tdb_dst macros. They are unclear.
++ *
++ * Revision 1.42 2000/08/01 14:51:51 rgb
++ * Removed _all_ remaining traces of DES.
++ *
++ * Revision 1.41 2000/07/28 14:58:31 rgb
++ * Changed kfree_s to kfree, eliminating extra arg to fix 2.4.0-test5.
++ *
++ * Revision 1.40 2000/06/28 05:50:11 rgb
++ * Actually set iv_bits.
++ *
++ * Revision 1.39 2000/05/10 23:11:09 rgb
++ * Added netlink debugging output.
++ * Added a cast to quiet down the ntohl bug.
++ *
++ * Revision 1.38 2000/05/10 19:18:42 rgb
++ * Cast output of ntohl so that the broken prototype doesn't make our
++ * compile noisy.
++ *
++ * Revision 1.37 2000/03/16 14:04:59 rgb
++ * Hardwired CONFIG_IPSEC_PFKEYv2 on.
++ *
++ * Revision 1.36 2000/01/26 10:11:28 rgb
++ * Fixed spacing in error text causing run-in words.
++ *
++ * Revision 1.35 2000/01/21 06:17:16 rgb
++ * Tidied up compiler directive indentation for readability.
++ * Added ictx,octx vars for simplification.(kravietz)
++ * Added macros for HMAC padding magic numbers.(kravietz)
++ * Fixed missing key length reporting bug.
++ * Fixed bug in tdbwipe to return immediately on NULL tdbp passed in.
++ *
++ * Revision 1.34 1999/12/08 00:04:19 rgb
++ * Fixed SA direction overwriting bug for netlink users.
++ *
++ * Revision 1.33 1999/12/01 22:16:44 rgb
++ * Minor formatting changes in ESP MD5 initialisation.
++ *
++ * Revision 1.32 1999/11/25 09:06:36 rgb
++ * Fixed error return messages, should be returning negative numbers.
++ * Implemented SENDERR macro for propagating error codes.
++ * Added debug message and separate error code for algorithms not compiled
++ * in.
++ *
++ * Revision 1.31 1999/11/23 23:06:26 rgb
++ * Sort out pfkey and freeswan headers, putting them in a library path.
++ *
++ * Revision 1.30 1999/11/18 04:09:20 rgb
++ * Replaced all kernel version macros to shorter, readable form.
++ *
++ * Revision 1.29 1999/11/17 15:53:40 rgb
++ * Changed all occurrences of #include "../../../lib/freeswan.h"
++ * to #include <freeswan.h> which works due to -Ilibfreeswan in the
++ * klips/net/ipsec/Makefile.
++ *
++ * Revision 1.28 1999/10/18 20:04:01 rgb
++ * Clean-out unused cruft.
++ *
++ * Revision 1.27 1999/10/03 19:01:03 rgb
++ * Spinlock support for 2.3.xx and 2.0.xx kernels.
++ *
++ * Revision 1.26 1999/10/01 16:22:24 rgb
++ * Switch from assignment init. to functional init. of spinlocks.
++ *
++ * Revision 1.25 1999/10/01 15:44:54 rgb
++ * Move spinlock header include to 2.1> scope.
++ *
++ * Revision 1.24 1999/10/01 00:03:46 rgb
++ * Added tdb structure locking.
++ * Minor formatting changes.
++ * Add function to initialize tdb hash table.
++ *
++ * Revision 1.23 1999/05/25 22:42:12 rgb
++ * Add deltdbchain() debugging.
++ *
++ * Revision 1.22 1999/05/25 21:24:31 rgb
++ * Add debugging statements to deltdbchain().
++ *
++ * Revision 1.21 1999/05/25 03:51:48 rgb
++ * Refix error return code.
++ *
++ * Revision 1.20 1999/05/25 03:34:07 rgb
++ * Fix error return for flush.
++ *
++ * Revision 1.19 1999/05/09 03:25:37 rgb
++ * Fix bug introduced by 2.2 quick-and-dirty patch.
++ *
++ * Revision 1.18 1999/05/05 22:02:32 rgb
++ * Add a quick and dirty port to 2.2 kernels by Marc Boucher <marc@mbsi.ca>.
++ *
++ * Revision 1.17 1999/04/29 15:20:16 rgb
++ * Change gettdb parameter to a pointer to reduce stack loading and
++ * facilitate parameter sanity checking.
++ * Add sanity checking for null pointer arguments.
++ * Add debugging instrumentation.
++ * Add function deltdbchain() which will take care of unlinking,
++ * zeroing and deleting a chain of tdbs.
++ * Add a parameter to tdbcleanup to be able to delete a class of SAs.
++ * tdbwipe now actually zeroes the tdb as well as any of its pointed
++ * structures.
++ *
++ * Revision 1.16 1999/04/16 15:36:29 rgb
++ * Fix cut-and-paste error causing a memory leak in IPIP TDB freeing.
++ *
++ * Revision 1.15 1999/04/11 00:29:01 henry
++ * GPL boilerplate
++ *
++ * Revision 1.14 1999/04/06 04:54:28 rgb
++ * Fix/Add RCSID Id: and Log: bits to make PHMDs happy. This includes
++ * patch shell fixes.
++ *
++ * Revision 1.13 1999/02/19 18:23:01 rgb
++ * Nix debug off compile warning.
++ *
++ * Revision 1.12 1999/02/17 16:52:16 rgb
++ * Consolidate satoa()s for space and speed efficiency.
++ * Convert DEBUG_IPSEC to KLIPS_PRINT
++ * Clean out unused cruft.
++ * Ditch NET_IPIP dependancy.
++ * Loop for 3des key setting.
++ *
++ * Revision 1.11 1999/01/26 02:09:05 rgb
++ * Remove ah/esp/IPIP switching on include files.
++ * Removed CONFIG_IPSEC_ALGO_SWITCH macro.
++ * Removed dead code.
++ * Clean up debug code when switched off.
++ * Remove references to INET_GET_PROTOCOL.
++ * Added code exclusion macros to reduce code from unused algorithms.
++ *
++ * Revision 1.10 1999/01/22 06:28:55 rgb
++ * Cruft clean-out.
++ * Put random IV generation in kernel.
++ * Added algorithm switch code.
++ * Enhanced debugging.
++ * 64-bit clean-up.
++ *
++ * Revision 1.9 1998/11/30 13:22:55 rgb
++ * Rationalised all the klips kernel file headers. They are much shorter
++ * now and won't conflict under RH5.2.
++ *
++ * Revision 1.8 1998/11/25 04:59:06 rgb
++ * Add conditionals for no IPIP tunnel code.
++ * Delete commented out code.
++ *
++ * Revision 1.7 1998/10/31 06:50:41 rgb
++ * Convert xform ASCII names to no spaces.
++ * Fixed up comments in #endif directives.
++ *
++ * Revision 1.6 1998/10/19 14:44:28 rgb
++ * Added inclusion of freeswan.h.
++ * sa_id structure implemented and used: now includes protocol.
++ *
++ * Revision 1.5 1998/10/09 04:32:19 rgb
++ * Added 'klips_debug' prefix to all klips printk debug statements.
++ *
++ * Revision 1.4 1998/08/12 00:11:31 rgb
++ * Added new xform functions to the xform table.
++ * Fixed minor debug output spelling error.
++ *
++ * Revision 1.3 1998/07/09 17:45:31 rgb
++ * Clarify algorithm not available message.
++ *
++ * Revision 1.2 1998/06/23 03:00:51 rgb
++ * Check for presence of IPIP protocol if it is setup one way (we don't
++ * know what has been set up the other way and can only assume it will be
++ * symmetrical with the exception of keys).
++ *
++ * Revision 1.1 1998/06/18 21:27:51 henry
++ * move sources from klips/src to klips/net/ipsec, to keep stupid
++ * kernel-build scripts happier in the presence of symlinks
++ *
++ * Revision 1.3 1998/06/11 05:54:59 rgb
++ * Added transform version string pointer to xformsw initialisations.
++ *
++ * Revision 1.2 1998/04/21 21:28:57 rgb
++ * Rearrange debug switches to change on the fly debug output from user
++ * space. Only kernel changes checked in at this time. radij.c was also
++ * changed to temporarily remove buggy debugging code in rj_delete causing
++ * an OOPS and hence, netlink device open errors.
++ *
++ * Revision 1.1 1998/04/09 03:06:13 henry
++ * sources moved up from linux/net/ipsec
++ *
++ * Revision 1.1.1.1 1998/04/08 05:35:02 henry
++ * RGB's ipsec-0.8pre2.tar.gz ipsec-0.8
++ *
++ * Revision 0.5 1997/06/03 04:24:48 ji
++ * Added ESP-3DES-MD5-96
++ *
++ * Revision 0.4 1997/01/15 01:28:15 ji
++ * Added new transforms.
++ *
++ * Revision 0.3 1996/11/20 14:39:04 ji
++ * Minor cleanups.
++ * Rationalized debugging code.
++ *
++ * Revision 0.2 1996/11/02 00:18:33 ji
++ * First limited release.
++ *
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/net/ipsec/ipsec_sha1.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,219 @@
++/*
++ * RCSID $Id$
++ */
++
++/*
++ * The rest of the code is derived from sha1.c by Steve Reid, which is
++ * public domain.
++ * Minor cosmetic changes to accomodate it in the Linux kernel by ji.
++ */
++
++#include <asm/byteorder.h>
++#include <linux/string.h>
++
++#include "openswan/ipsec_sha1.h"
++
++#if defined(rol)
++#undef rol
++#endif
++
++#define SHA1HANDSOFF
++
++#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
++
++/* blk0() and blk() perform the initial expand. */
++/* I got the idea of expanding during the round function from SSLeay */
++#ifdef __LITTLE_ENDIAN
++#define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \
++ |(rol(block->l[i],8)&0x00FF00FF))
++#else
++#define blk0(i) block->l[i]
++#endif
++#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
++ ^block->l[(i+2)&15]^block->l[i&15],1))
++
++/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
++#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
++#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
++#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
++#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
++#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
++
++
++/* Hash a single 512-bit block. This is the core of the algorithm. */
++
++void SHA1Transform(__u32 state[5], __u8 buffer[64])
++{
++__u32 a, b, c, d, e;
++typedef union {
++ unsigned char c[64];
++ __u32 l[16];
++} CHAR64LONG16;
++CHAR64LONG16* block;
++#ifdef SHA1HANDSOFF
++static unsigned char workspace[64];
++ block = (CHAR64LONG16*)workspace;
++ memcpy(block, buffer, 64);
++#else
++ block = (CHAR64LONG16*)buffer;
++#endif
++ /* Copy context->state[] to working vars */
++ a = state[0];
++ b = state[1];
++ c = state[2];
++ d = state[3];
++ e = state[4];
++ /* 4 rounds of 20 operations each. Loop unrolled. */
++ R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
++ R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
++ R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
++ R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
++ R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
++ R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
++ R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
++ R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
++ R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
++ R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
++ R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
++ R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
++ R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
++ R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
++ R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
++ R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
++ R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
++ R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
++ R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
++ R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
++ /* Add the working vars back into context.state[] */
++ state[0] += a;
++ state[1] += b;
++ state[2] += c;
++ state[3] += d;
++ state[4] += e;
++ /* Wipe variables */
++ a = b = c = d = e = 0;
++}
++
++
++/* SHA1Init - Initialize new context */
++
++void SHA1Init(void *vcontext)
++{
++ SHA1_CTX* context = vcontext;
++
++ /* SHA1 initialization constants */
++ context->state[0] = 0x67452301;
++ context->state[1] = 0xEFCDAB89;
++ context->state[2] = 0x98BADCFE;
++ context->state[3] = 0x10325476;
++ context->state[4] = 0xC3D2E1F0;
++ context->count[0] = context->count[1] = 0;
++}
++
++
++/* Run your data through this. */
++
++void SHA1Update(void *vcontext, unsigned char* data, __u32 len)
++{
++ SHA1_CTX* context = vcontext;
++ __u32 i, j;
++
++ j = context->count[0];
++ if ((context->count[0] += len << 3) < j)
++ context->count[1]++;
++ context->count[1] += (len>>29);
++ j = (j >> 3) & 63;
++ if ((j + len) > 63) {
++ memcpy(&context->buffer[j], data, (i = 64-j));
++ SHA1Transform(context->state, context->buffer);
++ for ( ; i + 63 < len; i += 64) {
++ SHA1Transform(context->state, &data[i]);
++ }
++ j = 0;
++ }
++ else i = 0;
++ memcpy(&context->buffer[j], &data[i], len - i);
++}
++
++
++/* Add padding and return the message digest. */
++
++void SHA1Final(unsigned char digest[20], void *vcontext)
++{
++ __u32 i, j;
++ unsigned char finalcount[8];
++ SHA1_CTX* context = vcontext;
++
++ for (i = 0; i < 8; i++) {
++ finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
++ >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */
++ }
++ SHA1Update(context, (unsigned char *)"\200", 1);
++ while ((context->count[0] & 504) != 448) {
++ SHA1Update(context, (unsigned char *)"\0", 1);
++ }
++ SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */
++ for (i = 0; i < 20; i++) {
++ digest[i] = (unsigned char)
++ ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
++ }
++ /* Wipe variables */
++ i = j = 0;
++ memset(context->buffer, 0, 64);
++ memset(context->state, 0, 20);
++ memset(context->count, 0, 8);
++ memset(&finalcount, 0, 8);
++#ifdef SHA1HANDSOFF /* make SHA1Transform overwrite its own static vars */
++ SHA1Transform(context->state, context->buffer);
++#endif
++}
++
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.9 2004/04/06 02:49:26 mcr
++ * pullup of algo code from alg-branch.
++ *
++ * Revision 1.8 2002/09/10 01:45:14 mcr
++ * changed type of MD5_CTX and SHA1_CTX to void * so that
++ * the function prototypes would match, and could be placed
++ * into a pointer to a function.
++ *
++ * Revision 1.7 2002/04/24 07:55:32 mcr
++ * #include patches and Makefiles for post-reorg compilation.
++ *
++ * Revision 1.6 2002/04/24 07:36:30 mcr
++ * Moved from ./klips/net/ipsec/ipsec_sha1.c,v
++ *
++ * Revision 1.5 1999/12/13 13:59:13 rgb
++ * Quick fix to argument size to Update bugs.
++ *
++ * Revision 1.4 1999/04/11 00:29:00 henry
++ * GPL boilerplate
++ *
++ * Revision 1.3 1999/04/06 04:54:27 rgb
++ * Fix/Add RCSID Id: and Log: bits to make PHMDs happy. This includes
++ * patch shell fixes.
++ *
++ * Revision 1.2 1999/01/22 06:55:50 rgb
++ * 64-bit clean-up.
++ *
++ * Revision 1.1 1998/06/18 21:27:50 henry
++ * move sources from klips/src to klips/net/ipsec, to keep stupid
++ * kernel-build scripts happier in the presence of symlinks
++ *
++ * Revision 1.2 1998/04/23 20:54:04 rgb
++ * Fixed md5 and sha1 include file nesting issues, to be cleaned up when
++ * verified.
++ *
++ * Revision 1.1 1998/04/09 03:06:11 henry
++ * sources moved up from linux/net/ipsec
++ *
++ * Revision 1.1.1.1 1998/04/08 05:35:05 henry
++ * RGB's ipsec-0.8pre2.tar.gz ipsec-0.8
++ *
++ * Revision 0.4 1997/01/15 01:28:15 ji
++ * New transform
++ *
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/net/ipsec/ipsec_tunnel.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,2645 @@
++/*
++ * IPSEC Tunneling code. Heavily based on drivers/net/new_tunnel.c
++ * Copyright (C) 1996, 1997 John Ioannidis.
++ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 Richard Guy Briggs.
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ */
++
++char ipsec_tunnel_c_version[] = "RCSID $Id$";
++
++#define __NO_VERSION__
++#include <linux/module.h>
++#include <linux/config.h> /* for CONFIG_IP_FORWARD */
++#include <linux/version.h>
++#include <linux/kernel.h> /* printk() */
++
++#include "openswan/ipsec_param.h"
++
++#ifdef MALLOC_SLAB
++# include <linux/slab.h> /* kmalloc() */
++#else /* MALLOC_SLAB */
++# include <linux/malloc.h> /* kmalloc() */
++#endif /* MALLOC_SLAB */
++#include <linux/errno.h> /* error codes */
++#include <linux/types.h> /* size_t */
++#include <linux/interrupt.h> /* mark_bh */
++
++#include <linux/netdevice.h> /* struct device, struct net_device_stats, dev_queue_xmit() and other headers */
++#include <linux/etherdevice.h> /* eth_type_trans */
++#include <linux/ip.h> /* struct iphdr */
++#include <linux/tcp.h> /* struct tcphdr */
++#include <linux/udp.h> /* struct udphdr */
++#include <linux/skbuff.h>
++#include <openswan.h>
++#ifdef NET_21
++# include <asm/uaccess.h>
++# include <linux/in6.h>
++# define ip_chk_addr inet_addr_type
++# define IS_MYADDR RTN_LOCAL
++# include <net/dst.h>
++# undef dev_kfree_skb
++# define dev_kfree_skb(a,b) kfree_skb(a)
++# define PHYSDEV_TYPE
++#endif /* NET_21 */
++#include <asm/checksum.h>
++#include <net/icmp.h> /* icmp_send() */
++#include <net/ip.h>
++#ifdef NETDEV_23
++# include <linux/netfilter_ipv4.h>
++#endif /* NETDEV_23 */
++
++#include <linux/if_arp.h>
++
++#include "openswan/radij.h"
++#include "openswan/ipsec_life.h"
++#include "openswan/ipsec_xform.h"
++#include "openswan/ipsec_eroute.h"
++#include "openswan/ipsec_encap.h"
++#include "openswan/ipsec_radij.h"
++#include "openswan/ipsec_sa.h"
++#include "openswan/ipsec_tunnel.h"
++#include "openswan/ipsec_xmit.h"
++#include "openswan/ipsec_ipe4.h"
++#include "openswan/ipsec_ah.h"
++#include "openswan/ipsec_esp.h"
++
++#include <pfkeyv2.h>
++#include <pfkey.h>
++
++#include "openswan/ipsec_proto.h"
++#ifdef CONFIG_IPSEC_NAT_TRAVERSAL
++#include <linux/udp.h>
++#endif
++
++static __u32 zeroes[64];
++
++#ifdef CONFIG_IPSEC_DEBUG
++int debug_tunnel = 0;
++#endif /* CONFIG_IPSEC_DEBUG */
++
++DEBUG_NO_STATIC int
++ipsec_tunnel_open(struct device *dev)
++{
++ struct ipsecpriv *prv = dev->priv;
++
++ /*
++ * Can't open until attached.
++ */
++
++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
++ "klips_debug:ipsec_tunnel_open: "
++ "dev = %s, prv->dev = %s\n",
++ dev->name, prv->dev?prv->dev->name:"NONE");
++
++ if (prv->dev == NULL)
++ return -ENODEV;
++
++ MOD_INC_USE_COUNT;
++ return 0;
++}
++
++DEBUG_NO_STATIC int
++ipsec_tunnel_close(struct device *dev)
++{
++ MOD_DEC_USE_COUNT;
++ return 0;
++}
++
++#ifdef NETDEV_23
++static inline int ipsec_tunnel_xmit2(struct sk_buff *skb)
++{
++#ifdef NETDEV_25 /* 2.6 kernels */
++ return dst_output(skb);
++#else
++ return ip_send(skb);
++#endif
++}
++#endif /* NETDEV_23 */
++
++enum ipsec_xmit_value
++ipsec_tunnel_strip_hard_header(struct ipsec_xmit_state *ixs)
++{
++ /* ixs->physdev->hard_header_len is unreliable and should not be used */
++ ixs->hard_header_len = (unsigned char *)(ixs->iph) - ixs->skb->data;
++
++ if(ixs->hard_header_len < 0) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
++ "klips_error:ipsec_xmit_strip_hard_header: "
++ "Negative hard_header_len (%d)?!\n", ixs->hard_header_len);
++ ixs->stats->tx_dropped++;
++ return IPSEC_XMIT_BADHHLEN;
++ }
++
++ /* while ixs->physdev->hard_header_len is unreliable and
++ * should not be trusted, it accurate and required for ATM, GRE and
++ * some other interfaces to work. Thanks to Willy Tarreau
++ * <willy@w.ods.org>.
++ */
++ if(ixs->hard_header_len == 0) { /* no hard header present */
++ ixs->hard_header_stripped = 1;
++ ixs->hard_header_len = ixs->physdev->hard_header_len;
++ }
++
++#ifdef CONFIG_IPSEC_DEBUG
++ if (debug_tunnel & DB_TN_XMIT) {
++ int i;
++ char c;
++
++ printk(KERN_INFO "klips_debug:ipsec_xmit_strip_hard_header: "
++ ">>> skb->len=%ld hard_header_len:%d",
++ (unsigned long int)ixs->skb->len, ixs->hard_header_len);
++ c = ' ';
++ for (i=0; i < ixs->hard_header_len; i++) {
++ printk("%c%02x", c, ixs->skb->data[i]);
++ c = ':';
++ }
++ printk(" \n");
++ }
++#endif /* CONFIG_IPSEC_DEBUG */
++
++ KLIPS_IP_PRINT(debug_tunnel & DB_TN_XMIT, ixs->iph);
++
++ KLIPS_PRINT(debug_tunnel & DB_TN_CROUT,
++ "klips_debug:ipsec_xmit_strip_hard_header: "
++ "Original head,tailroom: %d,%d\n",
++ skb_headroom(ixs->skb), skb_tailroom(ixs->skb));
++
++ return IPSEC_XMIT_OK;
++}
++
++enum ipsec_xmit_value
++ipsec_tunnel_SAlookup(struct ipsec_xmit_state *ixs)
++{
++ /*
++ * First things first -- look us up in the erouting tables.
++ */
++ ixs->matcher.sen_len = sizeof (struct sockaddr_encap);
++ ixs->matcher.sen_family = AF_ENCAP;
++ ixs->matcher.sen_type = SENT_IP4;
++ ixs->matcher.sen_ip_src.s_addr = ixs->iph->saddr;
++ ixs->matcher.sen_ip_dst.s_addr = ixs->iph->daddr;
++ ixs->matcher.sen_proto = ixs->iph->protocol;
++ ipsec_extract_ports(ixs->iph, &ixs->matcher);
++
++ /*
++ * The spinlock is to prevent any other process from accessing or deleting
++ * the eroute while we are using and updating it.
++ */
++ spin_lock(&eroute_lock);
++
++ ixs->eroute = ipsec_findroute(&ixs->matcher);
++
++ if(ixs->iph->protocol == IPPROTO_UDP) {
++ if(ixs->skb->sk) {
++ ixs->sport=ntohs(ixs->skb->sk->sport);
++ ixs->dport=ntohs(ixs->skb->sk->dport);
++ } else if((ntohs(ixs->iph->frag_off) & IP_OFFSET) == 0 &&
++ ((ixs->skb->len - ixs->hard_header_len) >=
++ ((ixs->iph->ihl << 2) + sizeof(struct udphdr)))) {
++ ixs->sport=ntohs(((struct udphdr*)((caddr_t)ixs->iph+(ixs->iph->ihl<<2)))->source);
++ ixs->dport=ntohs(((struct udphdr*)((caddr_t)ixs->iph + (ixs->iph->ihl<<2)))->dest);
++ } else {
++ ixs->sport=0; ixs->dport=0;
++ }
++ }
++
++ /* default to a %drop eroute */
++ ixs->outgoing_said.proto = IPPROTO_INT;
++ ixs->outgoing_said.spi = htonl(SPI_DROP);
++ ixs->outgoing_said.dst.u.v4.sin_addr.s_addr = INADDR_ANY;
++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
++ "klips_debug:ipsec_xmit_SAlookup: "
++ "checking for local udp/500 IKE packet "
++ "saddr=%x, er=0p%p, daddr=%x, er_dst=%x, proto=%d sport=%d dport=%d\n",
++ ntohl((unsigned int)ixs->iph->saddr),
++ ixs->eroute,
++ ntohl((unsigned int)ixs->iph->daddr),
++ ixs->eroute ? ntohl((unsigned int)ixs->eroute->er_said.dst.u.v4.sin_addr.s_addr) : 0,
++ ixs->iph->protocol,
++ ixs->sport,
++ ixs->dport);
++
++ /*
++ * Quick cheat for now...are we udp/500? If so, let it through
++ * without interference since it is most likely an IKE packet.
++ */
++
++ if (ip_chk_addr((unsigned long)ixs->iph->saddr) == IS_MYADDR
++ && (!ixs->eroute
++ || ixs->iph->daddr == ixs->eroute->er_said.dst.u.v4.sin_addr.s_addr
++ || INADDR_ANY == ixs->eroute->er_said.dst.u.v4.sin_addr.s_addr)
++
++ && ((ixs->sport == 500) || (ixs->sport == 4500))) {
++ /* Whatever the eroute, this is an IKE message
++ * from us (i.e. not being forwarded).
++ * Furthermore, if there is a tunnel eroute,
++ * the destination is the peer for this eroute.
++ * So %pass the packet: modify the default %drop.
++ */
++ ixs->outgoing_said.spi = htonl(SPI_PASS);
++ if(!(ixs->skb->sk) && ((ntohs(ixs->iph->frag_off) & IP_MF) != 0)) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
++ "klips_debug:ipsec_xmit_SAlookup: "
++ "local UDP/500 (probably IKE) passthrough: base fragment, rest of fragments will probably get filtered.\n");
++ }
++ } else if (ixs->eroute) {
++ ixs->eroute->er_count++;
++ ixs->eroute->er_lasttime = jiffies/HZ;
++ if(ixs->eroute->er_said.proto==IPPROTO_INT
++ && ixs->eroute->er_said.spi==htonl(SPI_HOLD)) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
++ "klips_debug:ipsec_xmit_SAlookup: "
++ "shunt SA of HOLD: skb stored in HOLD.\n");
++ if(ixs->eroute->er_last != NULL) {
++ kfree_skb(ixs->eroute->er_last);
++ }
++ ixs->eroute->er_last = ixs->skb;
++ ixs->skb = NULL;
++ ixs->stats->tx_dropped++;
++ spin_unlock(&eroute_lock);
++ return IPSEC_XMIT_STOLEN;
++ }
++ ixs->outgoing_said = ixs->eroute->er_said;
++ ixs->eroute_pid = ixs->eroute->er_pid;
++ /* Copy of the ident for the TRAP/TRAPSUBNET eroutes */
++ if(ixs->outgoing_said.proto==IPPROTO_INT
++ && (ixs->outgoing_said.spi==htonl(SPI_TRAP)
++ || (ixs->outgoing_said.spi==htonl(SPI_TRAPSUBNET)))) {
++ int len;
++
++ ixs->ips.ips_ident_s.type = ixs->eroute->er_ident_s.type;
++ ixs->ips.ips_ident_s.id = ixs->eroute->er_ident_s.id;
++ ixs->ips.ips_ident_s.len = ixs->eroute->er_ident_s.len;
++ if (ixs->ips.ips_ident_s.len) {
++ len = ixs->ips.ips_ident_s.len * IPSEC_PFKEYv2_ALIGN - sizeof(struct sadb_ident);
++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
++ "klips_debug:ipsec_xmit_SAlookup: "
++ "allocating %d bytes for ident_s shunt SA of HOLD: skb stored in HOLD.\n",
++ len);
++ if ((ixs->ips.ips_ident_s.data = kmalloc(len, GFP_ATOMIC)) == NULL) {
++ printk(KERN_WARNING "klips_debug:ipsec_xmit_SAlookup: "
++ "Failed, tried to allocate %d bytes for source ident.\n",
++ len);
++ ixs->stats->tx_dropped++;
++ spin_unlock(&eroute_lock);
++ return IPSEC_XMIT_ERRMEMALLOC;
++ }
++ memcpy(ixs->ips.ips_ident_s.data, ixs->eroute->er_ident_s.data, len);
++ }
++ ixs->ips.ips_ident_d.type = ixs->eroute->er_ident_d.type;
++ ixs->ips.ips_ident_d.id = ixs->eroute->er_ident_d.id;
++ ixs->ips.ips_ident_d.len = ixs->eroute->er_ident_d.len;
++ if (ixs->ips.ips_ident_d.len) {
++ len = ixs->ips.ips_ident_d.len * IPSEC_PFKEYv2_ALIGN - sizeof(struct sadb_ident);
++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
++ "klips_debug:ipsec_xmit_SAlookup: "
++ "allocating %d bytes for ident_d shunt SA of HOLD: skb stored in HOLD.\n",
++ len);
++ if ((ixs->ips.ips_ident_d.data = kmalloc(len, GFP_ATOMIC)) == NULL) {
++ printk(KERN_WARNING "klips_debug:ipsec_xmit_SAlookup: "
++ "Failed, tried to allocate %d bytes for dest ident.\n",
++ len);
++ ixs->stats->tx_dropped++;
++ spin_unlock(&eroute_lock);
++ return IPSEC_XMIT_ERRMEMALLOC;
++ }
++ memcpy(ixs->ips.ips_ident_d.data, ixs->eroute->er_ident_d.data, len);
++ }
++ }
++ }
++
++ spin_unlock(&eroute_lock);
++ return IPSEC_XMIT_OK;
++}
++
++enum ipsec_xmit_value
++ipsec_tunnel_restore_hard_header(struct ipsec_xmit_state*ixs)
++{
++ KLIPS_PRINT(debug_tunnel & DB_TN_CROUT,
++ "klips_debug:ipsec_xmit_restore_hard_header: "
++ "After recursive xforms -- head,tailroom: %d,%d\n",
++ skb_headroom(ixs->skb),
++ skb_tailroom(ixs->skb));
++
++ if(ixs->saved_header) {
++ if(skb_headroom(ixs->skb) < ixs->hard_header_len) {
++ printk(KERN_WARNING
++ "klips_error:ipsec_xmit_restore_hard_header: "
++ "tried to skb_push hhlen=%d, %d available. This should never happen, please report.\n",
++ ixs->hard_header_len,
++ skb_headroom(ixs->skb));
++ ixs->stats->tx_errors++;
++ return IPSEC_XMIT_PUSHPULLERR;
++
++ }
++ skb_push(ixs->skb, ixs->hard_header_len);
++ {
++ int i;
++ for (i = 0; i < ixs->hard_header_len; i++) {
++ ixs->skb->data[i] = ixs->saved_header[i];
++ }
++ }
++ }
++#ifdef CONFIG_IPSEC_NAT_TRAVERSAL
++ if (ixs->natt_type && ixs->natt_head) {
++ struct iphdr *ipp = ixs->skb->nh.iph;
++ struct udphdr *udp;
++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
++ "klips_debug:ipsec_tunnel_start_xmit: "
++ "encapsuling packet into UDP (NAT-Traversal) (%d %d)\n",
++ ixs->natt_type, ixs->natt_head);
++
++ ixs->iphlen = ipp->ihl << 2;
++ ipp->tot_len =
++ htons(ntohs(ipp->tot_len) + ixs->natt_head);
++ if(skb_tailroom(ixs->skb) < ixs->natt_head) {
++ printk(KERN_WARNING "klips_error:ipsec_tunnel_start_xmit: "
++ "tried to skb_put %d, %d available. "
++ "This should never happen, please report.\n",
++ ixs->natt_head,
++ skb_tailroom(ixs->skb));
++ ixs->stats->tx_errors++;
++ return IPSEC_XMIT_ESPUDP;
++ }
++ skb_put(ixs->skb, ixs->natt_head);
++
++ udp = (struct udphdr *)((char *)ipp + ixs->iphlen);
++
++ /* move ESP hdr after UDP hdr */
++ memmove((void *)((char *)udp + ixs->natt_head),
++ (void *)(udp),
++ ntohs(ipp->tot_len) - ixs->iphlen - ixs->natt_head);
++
++ /* clear UDP & Non-IKE Markers (if any) */
++ memset(udp, 0, ixs->natt_head);
++
++ /* fill UDP with usefull informations ;-) */
++ udp->source = htons(ixs->natt_sport);
++ udp->dest = htons(ixs->natt_dport);
++ udp->len = htons(ntohs(ipp->tot_len) - ixs->iphlen);
++
++ /* set protocol */
++ ipp->protocol = IPPROTO_UDP;
++
++ /* fix IP checksum */
++ ipp->check = 0;
++ ipp->check = ip_fast_csum((unsigned char *)ipp, ipp->ihl);
++ }
++#endif
++ KLIPS_PRINT(debug_tunnel & DB_TN_CROUT,
++ "klips_debug:ipsec_xmit_restore_hard_header: "
++ "With hard_header, final head,tailroom: %d,%d\n",
++ skb_headroom(ixs->skb),
++ skb_tailroom(ixs->skb));
++
++ return IPSEC_XMIT_OK;
++}
++
++enum ipsec_xmit_value
++ipsec_tunnel_send(struct ipsec_xmit_state*ixs)
++{
++#ifdef NETDEV_25
++ struct flowi fl;
++#endif
++
++#ifdef NET_21 /* 2.2 and 2.4 kernels */
++ /* new route/dst cache code from James Morris */
++ ixs->skb->dev = ixs->physdev;
++#ifdef NETDEV_25
++ fl.oif = ixs->physdev->iflink;
++ fl.nl_u.ip4_u.daddr = ixs->skb->nh.iph->daddr;
++ fl.nl_u.ip4_u.saddr = ixs->pass ? 0 : ixs->skb->nh.iph->saddr;
++ fl.nl_u.ip4_u.tos = RT_TOS(ixs->skb->nh.iph->tos);
++ fl.proto = ixs->skb->nh.iph->protocol;
++ if ((ixs->error = ip_route_output_key(&ixs->route, &fl))) {
++#else
++ /*skb_orphan(ixs->skb);*/
++ if((ixs->error = ip_route_output(&ixs->route,
++ ixs->skb->nh.iph->daddr,
++ ixs->pass ? 0 : ixs->skb->nh.iph->saddr,
++ RT_TOS(ixs->skb->nh.iph->tos),
++ /* mcr->rgb: should this be 0 instead? */
++ ixs->physdev->iflink))) {
++#endif
++ ixs->stats->tx_errors++;
++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
++ "klips_debug:ipsec_xmit_send: "
++ "ip_route_output failed with error code %d, rt->u.dst.dev=%s, dropped\n",
++ ixs->error,
++ ixs->route->u.dst.dev->name);
++ return IPSEC_XMIT_ROUTEERR;
++ }
++ if(ixs->dev == ixs->route->u.dst.dev) {
++ ip_rt_put(ixs->route);
++ /* This is recursion, drop it. */
++ ixs->stats->tx_errors++;
++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
++ "klips_debug:ipsec_xmit_send: "
++ "suspect recursion, dev=rt->u.dst.dev=%s, dropped\n",
++ ixs->dev->name);
++ return IPSEC_XMIT_RECURSDETECT;
++ }
++ dst_release(ixs->skb->dst);
++ ixs->skb->dst = &ixs->route->u.dst;
++ ixs->stats->tx_bytes += ixs->skb->len;
++ if(ixs->skb->len < ixs->skb->nh.raw - ixs->skb->data) {
++ ixs->stats->tx_errors++;
++ printk(KERN_WARNING
++ "klips_error:ipsec_xmit_send: "
++ "tried to __skb_pull nh-data=%ld, %d available. This should never happen, please report.\n",
++ (unsigned long)(ixs->skb->nh.raw - ixs->skb->data),
++ ixs->skb->len);
++ return IPSEC_XMIT_PUSHPULLERR;
++ }
++ __skb_pull(ixs->skb, ixs->skb->nh.raw - ixs->skb->data);
++#ifdef SKB_RESET_NFCT
++ if(!ixs->pass) {
++ nf_conntrack_put(ixs->skb->nfct);
++ ixs->skb->nfct = NULL;
++ }
++#ifdef CONFIG_NETFILTER_DEBUG
++ ixs->skb->nf_debug = 0;
++#endif /* CONFIG_NETFILTER_DEBUG */
++#endif /* SKB_RESET_NFCT */
++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
++ "klips_debug:ipsec_xmit_send: "
++ "...done, calling ip_send() on device:%s\n",
++ ixs->skb->dev ? ixs->skb->dev->name : "NULL");
++ KLIPS_IP_PRINT(debug_tunnel & DB_TN_XMIT, ixs->skb->nh.iph);
++#ifdef NETDEV_23 /* 2.4 kernels */
++ {
++ int err;
++
++ err = NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, ixs->skb, NULL, ixs->route->u.dst.dev,
++ ipsec_tunnel_xmit2);
++ if(err != NET_XMIT_SUCCESS && err != NET_XMIT_CN) {
++ if(net_ratelimit())
++ printk(KERN_ERR
++ "klips_error:ipsec_xmit_send: "
++ "ip_send() failed, err=%d\n",
++ -err);
++ ixs->stats->tx_errors++;
++ ixs->stats->tx_aborted_errors++;
++ ixs->skb = NULL;
++ return IPSEC_XMIT_IPSENDFAILURE;
++ }
++ }
++#else /* NETDEV_23 */ /* 2.2 kernels */
++ ip_send(ixs->skb);
++#endif /* NETDEV_23 */
++#else /* NET_21 */ /* 2.0 kernels */
++ ixs->skb->arp = 1;
++ /* ISDN/ASYNC PPP from Matjaz Godec. */
++ /* skb->protocol = htons(ETH_P_IP); */
++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
++ "klips_debug:ipsec_xmit_send: "
++ "...done, calling dev_queue_xmit() or ip_fragment().\n");
++ IP_SEND(ixs->skb, ixs->physdev);
++#endif /* NET_21 */
++ ixs->stats->tx_packets++;
++
++ ixs->skb = NULL;
++
++ return IPSEC_XMIT_OK;
++}
++
++void
++ipsec_tunnel_cleanup(struct ipsec_xmit_state*ixs)
++{
++#if defined(HAS_NETIF_QUEUE) || defined (HAVE_NETIF_QUEUE)
++ netif_wake_queue(ixs->dev);
++#else /* defined(HAS_NETIF_QUEUE) || defined (HAVE_NETIF_QUEUE) */
++ ixs->dev->tbusy = 0;
++#endif /* defined(HAS_NETIF_QUEUE) || defined (HAVE_NETIF_QUEUE) */
++ if(ixs->saved_header) {
++ kfree(ixs->saved_header);
++ }
++ if(ixs->skb) {
++ dev_kfree_skb(ixs->skb, FREE_WRITE);
++ }
++ if(ixs->oskb) {
++ dev_kfree_skb(ixs->oskb, FREE_WRITE);
++ }
++ if (ixs->ips.ips_ident_s.data) {
++ kfree(ixs->ips.ips_ident_s.data);
++ }
++ if (ixs->ips.ips_ident_d.data) {
++ kfree(ixs->ips.ips_ident_d.data);
++ }
++}
++
++/*
++ * This function assumes it is being called from dev_queue_xmit()
++ * and that skb is filled properly by that function.
++ */
++int
++ipsec_tunnel_start_xmit(struct sk_buff *skb, struct device *dev)
++{
++ struct ipsec_xmit_state ixs_mem;
++ struct ipsec_xmit_state *ixs = &ixs_mem;
++ enum ipsec_xmit_value stat;
++
++#ifdef CONFIG_IPSEC_NAT_TRAVERSAL
++ ixs->natt_type = 0, ixs->natt_head = 0;
++ ixs->natt_sport = 0, ixs->natt_dport = 0;
++#endif
++
++ memset((caddr_t)ixs, 0, sizeof(*ixs));
++ ixs->oskb = NULL;
++ ixs->saved_header = NULL; /* saved copy of the hard header */
++ ixs->route = NULL;
++ memset((caddr_t)&(ixs->ips), 0, sizeof(ixs->ips));
++ ixs->dev = dev;
++ ixs->skb = skb;
++
++ stat = ipsec_xmit_sanity_check_dev(ixs);
++ if(stat != IPSEC_XMIT_OK) {
++ goto cleanup;
++ }
++
++ stat = ipsec_xmit_sanity_check_skb(ixs);
++ if(stat != IPSEC_XMIT_OK) {
++ goto cleanup;
++ }
++
++ stat = ipsec_tunnel_strip_hard_header(ixs);
++ if(stat != IPSEC_XMIT_OK) {
++ goto cleanup;
++ }
++
++ stat = ipsec_tunnel_SAlookup(ixs);
++ if(stat != IPSEC_XMIT_OK) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
++ "klips_debug:ipsec_tunnel_start_xmit: SAlookup failed: %d\n",
++ stat);
++ goto cleanup;
++ }
++
++ ixs->innersrc = ixs->iph->saddr;
++ /* start encapsulation loop here XXX */
++ do {
++ stat = ipsec_xmit_encap_bundle(ixs);
++ if(stat != IPSEC_XMIT_OK) {
++ if(stat == IPSEC_XMIT_PASS) {
++ goto bypass;
++ }
++
++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
++ "klips_debug:ipsec_tunnel_start_xmit: encap_bundle failed: %d\n",
++ stat);
++ goto cleanup;
++ }
++
++ ixs->matcher.sen_ip_src.s_addr = ixs->iph->saddr;
++ ixs->matcher.sen_ip_dst.s_addr = ixs->iph->daddr;
++ ixs->matcher.sen_proto = ixs->iph->protocol;
++ ipsec_extract_ports(ixs->iph, &ixs->matcher);
++
++ spin_lock(&eroute_lock);
++ ixs->eroute = ipsec_findroute(&ixs->matcher);
++ if(ixs->eroute) {
++ ixs->outgoing_said = ixs->eroute->er_said;
++ ixs->eroute_pid = ixs->eroute->er_pid;
++ ixs->eroute->er_count++;
++ ixs->eroute->er_lasttime = jiffies/HZ;
++ }
++ spin_unlock(&eroute_lock);
++
++ KLIPS_PRINT((debug_tunnel & DB_TN_XMIT) &&
++ /* ((ixs->orgdst != ixs->newdst) || (ixs->orgsrc != ixs->newsrc)) */
++ (ixs->orgedst != ixs->outgoing_said.dst.u.v4.sin_addr.s_addr) &&
++ ixs->outgoing_said.dst.u.v4.sin_addr.s_addr &&
++ ixs->eroute,
++ "klips_debug:ipsec_tunnel_start_xmit: "
++ "We are recursing here.\n");
++
++ } while(/*((ixs->orgdst != ixs->newdst) || (ixs->orgsrc != ixs->newsrc))*/
++ (ixs->orgedst != ixs->outgoing_said.dst.u.v4.sin_addr.s_addr) &&
++ ixs->outgoing_said.dst.u.v4.sin_addr.s_addr &&
++ ixs->eroute);
++
++ stat = ipsec_tunnel_restore_hard_header(ixs);
++ if(stat != IPSEC_XMIT_OK) {
++ goto cleanup;
++ }
++
++ bypass:
++ stat = ipsec_tunnel_send(ixs);
++
++ cleanup:
++ ipsec_tunnel_cleanup(ixs);
++
++ return 0;
++}
++
++DEBUG_NO_STATIC struct net_device_stats *
++ipsec_tunnel_get_stats(struct device *dev)
++{
++ return &(((struct ipsecpriv *)(dev->priv))->mystats);
++}
++
++/*
++ * Revectored calls.
++ * For each of these calls, a field exists in our private structure.
++ */
++
++DEBUG_NO_STATIC int
++ipsec_tunnel_hard_header(struct sk_buff *skb, struct device *dev,
++ unsigned short type, void *daddr, void *saddr, unsigned len)
++{
++ struct ipsecpriv *prv = dev->priv;
++ struct device *tmp;
++ int ret;
++ struct net_device_stats *stats; /* This device's statistics */
++
++ if(skb == NULL) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
++ "klips_debug:ipsec_tunnel_hard_header: "
++ "no skb...\n");
++ return -ENODATA;
++ }
++
++ if(dev == NULL) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
++ "klips_debug:ipsec_tunnel_hard_header: "
++ "no device...\n");
++ return -ENODEV;
++ }
++
++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
++ "klips_debug:ipsec_tunnel_hard_header: "
++ "skb->dev=%s dev=%s.\n",
++ skb->dev ? skb->dev->name : "NULL",
++ dev->name);
++
++ if(prv == NULL) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
++ "klips_debug:ipsec_tunnel_hard_header: "
++ "no private space associated with dev=%s\n",
++ dev->name ? dev->name : "NULL");
++ return -ENODEV;
++ }
++
++ stats = (struct net_device_stats *) &(prv->mystats);
++
++ if(prv->dev == NULL) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
++ "klips_debug:ipsec_tunnel_hard_header: "
++ "no physical device associated with dev=%s\n",
++ dev->name ? dev->name : "NULL");
++ stats->tx_dropped++;
++ return -ENODEV;
++ }
++
++ /* check if we have to send a IPv6 packet. It might be a Router
++ Solicitation, where the building of the packet happens in
++ reverse order:
++ 1. ll hdr,
++ 2. IPv6 hdr,
++ 3. ICMPv6 hdr
++ -> skb->nh.raw is still uninitialized when this function is
++ called!! If this is no IPv6 packet, we can print debugging
++ messages, otherwise we skip all debugging messages and just
++ build the ll header */
++ if(type != ETH_P_IPV6) {
++ /* execute this only, if we don't have to build the
++ header for a IPv6 packet */
++ if(!prv->hard_header) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
++ "klips_debug:ipsec_tunnel_hard_header: "
++ "physical device has been detached, packet dropped 0p%p->0p%p len=%d type=%d dev=%s->NULL ",
++ saddr,
++ daddr,
++ len,
++ type,
++ dev->name);
++#ifdef NET_21
++ KLIPS_PRINTMORE(debug_tunnel & DB_TN_REVEC,
++ "ip=%08x->%08x\n",
++ (__u32)ntohl(skb->nh.iph->saddr),
++ (__u32)ntohl(skb->nh.iph->daddr) );
++#else /* NET_21 */
++ KLIPS_PRINTMORE(debug_tunnel & DB_TN_REVEC,
++ "ip=%08x->%08x\n",
++ (__u32)ntohl(skb->ip_hdr->saddr),
++ (__u32)ntohl(skb->ip_hdr->daddr) );
++#endif /* NET_21 */
++ stats->tx_dropped++;
++ return -ENODEV;
++ }
++
++#define da ((struct device *)(prv->dev))->dev_addr
++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
++ "klips_debug:ipsec_tunnel_hard_header: "
++ "Revectored 0p%p->0p%p len=%d type=%d dev=%s->%s dev_addr=%02x:%02x:%02x:%02x:%02x:%02x ",
++ saddr,
++ daddr,
++ len,
++ type,
++ dev->name,
++ prv->dev->name,
++ da[0], da[1], da[2], da[3], da[4], da[5]);
++#ifdef NET_21
++ KLIPS_PRINTMORE(debug_tunnel & DB_TN_REVEC,
++ "ip=%08x->%08x\n",
++ (__u32)ntohl(skb->nh.iph->saddr),
++ (__u32)ntohl(skb->nh.iph->daddr) );
++#else /* NET_21 */
++ KLIPS_PRINTMORE(debug_tunnel & DB_TN_REVEC,
++ "ip=%08x->%08x\n",
++ (__u32)ntohl(skb->ip_hdr->saddr),
++ (__u32)ntohl(skb->ip_hdr->daddr) );
++#endif /* NET_21 */
++ } else {
++ KLIPS_PRINT(debug_tunnel,
++ "klips_debug:ipsec_tunnel_hard_header: "
++ "is IPv6 packet, skip debugging messages, only revector and build linklocal header.\n");
++ }
++ tmp = skb->dev;
++ skb->dev = prv->dev;
++ ret = prv->hard_header(skb, prv->dev, type, (void *)daddr, (void *)saddr, len);
++ skb->dev = tmp;
++ return ret;
++}
++
++DEBUG_NO_STATIC int
++#ifdef NET_21
++ipsec_tunnel_rebuild_header(struct sk_buff *skb)
++#else /* NET_21 */
++ipsec_tunnel_rebuild_header(void *buff, struct device *dev,
++ unsigned long raddr, struct sk_buff *skb)
++#endif /* NET_21 */
++{
++ struct ipsecpriv *prv = skb->dev->priv;
++ struct device *tmp;
++ int ret;
++ struct net_device_stats *stats; /* This device's statistics */
++
++ if(skb->dev == NULL) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
++ "klips_debug:ipsec_tunnel_rebuild_header: "
++ "no device...");
++ return -ENODEV;
++ }
++
++ if(prv == NULL) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
++ "klips_debug:ipsec_tunnel_rebuild_header: "
++ "no private space associated with dev=%s",
++ skb->dev->name ? skb->dev->name : "NULL");
++ return -ENODEV;
++ }
++
++ stats = (struct net_device_stats *) &(prv->mystats);
++
++ if(prv->dev == NULL) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
++ "klips_debug:ipsec_tunnel_rebuild_header: "
++ "no physical device associated with dev=%s",
++ skb->dev->name ? skb->dev->name : "NULL");
++ stats->tx_dropped++;
++ return -ENODEV;
++ }
++
++ if(!prv->rebuild_header) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
++ "klips_debug:ipsec_tunnel_rebuild_header: "
++ "physical device has been detached, packet dropped skb->dev=%s->NULL ",
++ skb->dev->name);
++#ifdef NET_21
++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
++ "ip=%08x->%08x\n",
++ (__u32)ntohl(skb->nh.iph->saddr),
++ (__u32)ntohl(skb->nh.iph->daddr) );
++#else /* NET_21 */
++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
++ "ip=%08x->%08x\n",
++ (__u32)ntohl(skb->ip_hdr->saddr),
++ (__u32)ntohl(skb->ip_hdr->daddr) );
++#endif /* NET_21 */
++ stats->tx_dropped++;
++ return -ENODEV;
++ }
++
++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
++ "klips_debug:ipsec_tunnel: "
++ "Revectored rebuild_header dev=%s->%s ",
++ skb->dev->name, prv->dev->name);
++#ifdef NET_21
++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
++ "ip=%08x->%08x\n",
++ (__u32)ntohl(skb->nh.iph->saddr),
++ (__u32)ntohl(skb->nh.iph->daddr) );
++#else /* NET_21 */
++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
++ "ip=%08x->%08x\n",
++ (__u32)ntohl(skb->ip_hdr->saddr),
++ (__u32)ntohl(skb->ip_hdr->daddr) );
++#endif /* NET_21 */
++ tmp = skb->dev;
++ skb->dev = prv->dev;
++
++#ifdef NET_21
++ ret = prv->rebuild_header(skb);
++#else /* NET_21 */
++ ret = prv->rebuild_header(buff, prv->dev, raddr, skb);
++#endif /* NET_21 */
++ skb->dev = tmp;
++ return ret;
++}
++
++DEBUG_NO_STATIC int
++ipsec_tunnel_set_mac_address(struct device *dev, void *addr)
++{
++ struct ipsecpriv *prv = dev->priv;
++
++ struct net_device_stats *stats; /* This device's statistics */
++
++ if(dev == NULL) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
++ "klips_debug:ipsec_tunnel_set_mac_address: "
++ "no device...");
++ return -ENODEV;
++ }
++
++ if(prv == NULL) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
++ "klips_debug:ipsec_tunnel_set_mac_address: "
++ "no private space associated with dev=%s",
++ dev->name ? dev->name : "NULL");
++ return -ENODEV;
++ }
++
++ stats = (struct net_device_stats *) &(prv->mystats);
++
++ if(prv->dev == NULL) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
++ "klips_debug:ipsec_tunnel_set_mac_address: "
++ "no physical device associated with dev=%s",
++ dev->name ? dev->name : "NULL");
++ stats->tx_dropped++;
++ return -ENODEV;
++ }
++
++ if(!prv->set_mac_address) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
++ "klips_debug:ipsec_tunnel_set_mac_address: "
++ "physical device has been detached, cannot set - skb->dev=%s->NULL\n",
++ dev->name);
++ return -ENODEV;
++ }
++
++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
++ "klips_debug:ipsec_tunnel_set_mac_address: "
++ "Revectored dev=%s->%s addr=0p%p\n",
++ dev->name, prv->dev->name, addr);
++ return prv->set_mac_address(prv->dev, addr);
++
++}
++
++#ifndef NET_21
++DEBUG_NO_STATIC void
++ipsec_tunnel_cache_bind(struct hh_cache **hhp, struct device *dev,
++ unsigned short htype, __u32 daddr)
++{
++ struct ipsecpriv *prv = dev->priv;
++
++ struct net_device_stats *stats; /* This device's statistics */
++
++ if(dev == NULL) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
++ "klips_debug:ipsec_tunnel_cache_bind: "
++ "no device...");
++ return;
++ }
++
++ if(prv == NULL) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
++ "klips_debug:ipsec_tunnel_cache_bind: "
++ "no private space associated with dev=%s",
++ dev->name ? dev->name : "NULL");
++ return;
++ }
++
++ stats = (struct net_device_stats *) &(prv->mystats);
++
++ if(prv->dev == NULL) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
++ "klips_debug:ipsec_tunnel_cache_bind: "
++ "no physical device associated with dev=%s",
++ dev->name ? dev->name : "NULL");
++ stats->tx_dropped++;
++ return;
++ }
++
++ if(!prv->header_cache_bind) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
++ "klips_debug:ipsec_tunnel_cache_bind: "
++ "physical device has been detached, cannot set - skb->dev=%s->NULL\n",
++ dev->name);
++ stats->tx_dropped++;
++ return;
++ }
++
++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
++ "klips_debug:ipsec_tunnel_cache_bind: "
++ "Revectored \n");
++ prv->header_cache_bind(hhp, prv->dev, htype, daddr);
++ return;
++}
++#endif /* !NET_21 */
++
++
++DEBUG_NO_STATIC void
++ipsec_tunnel_cache_update(struct hh_cache *hh, struct device *dev, unsigned char * haddr)
++{
++ struct ipsecpriv *prv = dev->priv;
++
++ struct net_device_stats *stats; /* This device's statistics */
++
++ if(dev == NULL) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
++ "klips_debug:ipsec_tunnel_cache_update: "
++ "no device...");
++ return;
++ }
++
++ if(prv == NULL) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
++ "klips_debug:ipsec_tunnel_cache_update: "
++ "no private space associated with dev=%s",
++ dev->name ? dev->name : "NULL");
++ return;
++ }
++
++ stats = (struct net_device_stats *) &(prv->mystats);
++
++ if(prv->dev == NULL) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
++ "klips_debug:ipsec_tunnel_cache_update: "
++ "no physical device associated with dev=%s",
++ dev->name ? dev->name : "NULL");
++ stats->tx_dropped++;
++ return;
++ }
++
++ if(!prv->header_cache_update) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
++ "klips_debug:ipsec_tunnel_cache_update: "
++ "physical device has been detached, cannot set - skb->dev=%s->NULL\n",
++ dev->name);
++ return;
++ }
++
++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
++ "klips_debug:ipsec_tunnel: "
++ "Revectored cache_update\n");
++ prv->header_cache_update(hh, prv->dev, haddr);
++ return;
++}
++
++#ifdef NET_21
++DEBUG_NO_STATIC int
++ipsec_tunnel_neigh_setup(struct neighbour *n)
++{
++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
++ "klips_debug:ipsec_tunnel_neigh_setup:\n");
++
++ if (n->nud_state == NUD_NONE) {
++ n->ops = &arp_broken_ops;
++ n->output = n->ops->output;
++ }
++ return 0;
++}
++
++DEBUG_NO_STATIC int
++ipsec_tunnel_neigh_setup_dev(struct device *dev, struct neigh_parms *p)
++{
++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
++ "klips_debug:ipsec_tunnel_neigh_setup_dev: "
++ "setting up %s\n",
++ dev ? dev->name : "NULL");
++
++ if (p->tbl->family == AF_INET) {
++ p->neigh_setup = ipsec_tunnel_neigh_setup;
++ p->ucast_probes = 0;
++ p->mcast_probes = 0;
++ }
++ return 0;
++}
++#endif /* NET_21 */
++
++/*
++ * We call the attach routine to attach another device.
++ */
++
++DEBUG_NO_STATIC int
++ipsec_tunnel_attach(struct device *dev, struct device *physdev)
++{
++ int i;
++ struct ipsecpriv *prv = dev->priv;
++
++ if(dev == NULL) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
++ "klips_debug:ipsec_tunnel_attach: "
++ "no device...");
++ return -ENODEV;
++ }
++
++ if(prv == NULL) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
++ "klips_debug:ipsec_tunnel_attach: "
++ "no private space associated with dev=%s",
++ dev->name ? dev->name : "NULL");
++ return -ENODATA;
++ }
++
++ prv->dev = physdev;
++ prv->hard_start_xmit = physdev->hard_start_xmit;
++ prv->get_stats = physdev->get_stats;
++
++ if (physdev->hard_header) {
++ prv->hard_header = physdev->hard_header;
++ dev->hard_header = ipsec_tunnel_hard_header;
++ } else
++ dev->hard_header = NULL;
++
++ if (physdev->rebuild_header) {
++ prv->rebuild_header = physdev->rebuild_header;
++ dev->rebuild_header = ipsec_tunnel_rebuild_header;
++ } else
++ dev->rebuild_header = NULL;
++
++ if (physdev->set_mac_address) {
++ prv->set_mac_address = physdev->set_mac_address;
++ dev->set_mac_address = ipsec_tunnel_set_mac_address;
++ } else
++ dev->set_mac_address = NULL;
++
++#ifndef NET_21
++ if (physdev->header_cache_bind) {
++ prv->header_cache_bind = physdev->header_cache_bind;
++ dev->header_cache_bind = ipsec_tunnel_cache_bind;
++ } else
++ dev->header_cache_bind = NULL;
++#endif /* !NET_21 */
++
++ if (physdev->header_cache_update) {
++ prv->header_cache_update = physdev->header_cache_update;
++ dev->header_cache_update = ipsec_tunnel_cache_update;
++ } else
++ dev->header_cache_update = NULL;
++
++ dev->hard_header_len = physdev->hard_header_len;
++
++#ifdef NET_21
++/* prv->neigh_setup = physdev->neigh_setup; */
++ dev->neigh_setup = ipsec_tunnel_neigh_setup_dev;
++#endif /* NET_21 */
++ dev->mtu = 16260; /* 0xfff0; */ /* dev->mtu; */
++ prv->mtu = physdev->mtu;
++
++#ifdef PHYSDEV_TYPE
++ dev->type = physdev->type; /* ARPHRD_TUNNEL; */
++#endif /* PHYSDEV_TYPE */
++
++ dev->addr_len = physdev->addr_len;
++ for (i=0; i<dev->addr_len; i++) {
++ dev->dev_addr[i] = physdev->dev_addr[i];
++ }
++#ifdef CONFIG_IPSEC_DEBUG
++ if(debug_tunnel & DB_TN_INIT) {
++ printk(KERN_INFO "klips_debug:ipsec_tunnel_attach: "
++ "physical device %s being attached has HW address: %2x",
++ physdev->name, physdev->dev_addr[0]);
++ for (i=1; i < physdev->addr_len; i++) {
++ printk(":%02x", physdev->dev_addr[i]);
++ }
++ printk("\n");
++ }
++#endif /* CONFIG_IPSEC_DEBUG */
++
++ return 0;
++}
++
++/*
++ * We call the detach routine to detach the ipsec tunnel from another device.
++ */
++
++DEBUG_NO_STATIC int
++ipsec_tunnel_detach(struct device *dev)
++{
++ int i;
++ struct ipsecpriv *prv = dev->priv;
++
++ if(dev == NULL) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
++ "klips_debug:ipsec_tunnel_detach: "
++ "no device...");
++ return -ENODEV;
++ }
++
++ if(prv == NULL) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
++ "klips_debug:ipsec_tunnel_detach: "
++ "no private space associated with dev=%s",
++ dev->name ? dev->name : "NULL");
++ return -ENODATA;
++ }
++
++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
++ "klips_debug:ipsec_tunnel_detach: "
++ "physical device %s being detached from virtual device %s\n",
++ prv->dev ? prv->dev->name : "NULL",
++ dev->name);
++
++ ipsec_dev_put(prv->dev);
++ prv->dev = NULL;
++ prv->hard_start_xmit = NULL;
++ prv->get_stats = NULL;
++
++ prv->hard_header = NULL;
++#ifdef DETACH_AND_DOWN
++ dev->hard_header = NULL;
++#endif /* DETACH_AND_DOWN */
++
++ prv->rebuild_header = NULL;
++#ifdef DETACH_AND_DOWN
++ dev->rebuild_header = NULL;
++#endif /* DETACH_AND_DOWN */
++
++ prv->set_mac_address = NULL;
++#ifdef DETACH_AND_DOWN
++ dev->set_mac_address = NULL;
++#endif /* DETACH_AND_DOWN */
++
++#ifndef NET_21
++ prv->header_cache_bind = NULL;
++#ifdef DETACH_AND_DOWN
++ dev->header_cache_bind = NULL;
++#endif /* DETACH_AND_DOWN */
++#endif /* !NET_21 */
++
++ prv->header_cache_update = NULL;
++#ifdef DETACH_AND_DOWN
++ dev->header_cache_update = NULL;
++#endif /* DETACH_AND_DOWN */
++
++#ifdef NET_21
++/* prv->neigh_setup = NULL; */
++#ifdef DETACH_AND_DOWN
++ dev->neigh_setup = NULL;
++#endif /* DETACH_AND_DOWN */
++#endif /* NET_21 */
++ dev->hard_header_len = 0;
++#ifdef DETACH_AND_DOWN
++ dev->mtu = 0;
++#endif /* DETACH_AND_DOWN */
++ prv->mtu = 0;
++ for (i=0; i<MAX_ADDR_LEN; i++) {
++ dev->dev_addr[i] = 0;
++ }
++ dev->addr_len = 0;
++#ifdef PHYSDEV_TYPE
++ dev->type = ARPHRD_VOID; /* ARPHRD_TUNNEL; */
++#endif /* PHYSDEV_TYPE */
++
++ return 0;
++}
++
++/*
++ * We call the clear routine to detach all ipsec tunnels from other devices.
++ */
++DEBUG_NO_STATIC int
++ipsec_tunnel_clear(void)
++{
++ int i;
++ struct device *ipsecdev = NULL, *prvdev;
++ struct ipsecpriv *prv;
++ char name[9];
++ int ret;
++
++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
++ "klips_debug:ipsec_tunnel_clear: .\n");
++
++ for(i = 0; i < IPSEC_NUM_IF; i++) {
++ ipsecdev = ipsecdevices[i];
++ if(ipsecdev != NULL) {
++ if((prv = (struct ipsecpriv *)(ipsecdev->priv))) {
++ prvdev = (struct device *)(prv->dev);
++ if(prvdev) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
++ "klips_debug:ipsec_tunnel_clear: "
++ "physical device for device %s is %s\n",
++ name, prvdev->name);
++ if((ret = ipsec_tunnel_detach(ipsecdev))) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
++ "klips_debug:ipsec_tunnel_clear: "
++ "error %d detatching device %s from device %s.\n",
++ ret, name, prvdev->name);
++ return ret;
++ }
++ }
++ }
++ }
++ }
++ return 0;
++}
++
++DEBUG_NO_STATIC int
++ipsec_tunnel_ioctl(struct device *dev, struct ifreq *ifr, int cmd)
++{
++ struct ipsectunnelconf *cf = (struct ipsectunnelconf *)&ifr->ifr_data;
++ struct ipsecpriv *prv = dev->priv;
++ struct device *them; /* physical device */
++#ifdef CONFIG_IP_ALIAS
++ char *colon;
++ char realphysname[IFNAMSIZ];
++#endif /* CONFIG_IP_ALIAS */
++
++ if(dev == NULL) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
++ "klips_debug:ipsec_tunnel_ioctl: "
++ "device not supplied.\n");
++ return -ENODEV;
++ }
++
++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
++ "klips_debug:ipsec_tunnel_ioctl: "
++ "tncfg service call #%d for dev=%s\n",
++ cmd,
++ dev->name ? dev->name : "NULL");
++ switch (cmd) {
++ /* attach a virtual ipsec? device to a physical device */
++ case IPSEC_SET_DEV:
++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
++ "klips_debug:ipsec_tunnel_ioctl: "
++ "calling ipsec_tunnel_attatch...\n");
++#ifdef CONFIG_IP_ALIAS
++ /* If this is an IP alias interface, get its real physical name */
++ strncpy(realphysname, cf->cf_name, IFNAMSIZ);
++ realphysname[IFNAMSIZ-1] = 0;
++ colon = strchr(realphysname, ':');
++ if (colon) *colon = 0;
++ them = ipsec_dev_get(realphysname);
++#else /* CONFIG_IP_ALIAS */
++ them = ipsec_dev_get(cf->cf_name);
++#endif /* CONFIG_IP_ALIAS */
++
++ if (them == NULL) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
++ "klips_debug:ipsec_tunnel_ioctl: "
++ "physical device %s requested is null\n",
++ cf->cf_name);
++ ipsec_dev_put(them);
++ return -ENXIO;
++ }
++
++#if 0
++ if (them->flags & IFF_UP) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
++ "klips_debug:ipsec_tunnel_ioctl: "
++ "physical device %s requested is not up.\n",
++ cf->cf_name);
++ ipsec_dev_put(them);
++ return -ENXIO;
++ }
++#endif
++
++ if (prv && prv->dev) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
++ "klips_debug:ipsec_tunnel_ioctl: "
++ "virtual device is already connected to %s.\n",
++ prv->dev->name ? prv->dev->name : "NULL");
++ ipsec_dev_put(them);
++ return -EBUSY;
++ }
++ return ipsec_tunnel_attach(dev, them);
++
++ case IPSEC_DEL_DEV:
++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
++ "klips_debug:ipsec_tunnel_ioctl: "
++ "calling ipsec_tunnel_detatch.\n");
++ if (! prv->dev) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
++ "klips_debug:ipsec_tunnel_ioctl: "
++ "physical device not connected.\n");
++ return -ENODEV;
++ }
++ return ipsec_tunnel_detach(dev);
++
++ case IPSEC_CLR_DEV:
++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
++ "klips_debug:ipsec_tunnel_ioctl: "
++ "calling ipsec_tunnel_clear.\n");
++ return ipsec_tunnel_clear();
++
++ default:
++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
++ "klips_debug:ipsec_tunnel_ioctl: "
++ "unknown command %d.\n",
++ cmd);
++ return -EOPNOTSUPP;
++ }
++}
++
++int
++ipsec_device_event(struct notifier_block *unused, unsigned long event, void *ptr)
++{
++ struct device *dev = ptr;
++ struct device *ipsec_dev;
++ struct ipsecpriv *priv;
++ int i;
++
++ if (dev == NULL) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
++ "klips_debug:ipsec_device_event: "
++ "dev=NULL for event type %ld.\n",
++ event);
++ return(NOTIFY_DONE);
++ }
++
++ /* check for loopback devices */
++ if (dev && (dev->flags & IFF_LOOPBACK)) {
++ return(NOTIFY_DONE);
++ }
++
++ switch (event) {
++ case NETDEV_DOWN:
++ /* look very carefully at the scope of these compiler
++ directives before changing anything... -- RGB */
++#ifdef NET_21
++ case NETDEV_UNREGISTER:
++ switch (event) {
++ case NETDEV_DOWN:
++#endif /* NET_21 */
++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
++ "klips_debug:ipsec_device_event: "
++ "NETDEV_DOWN dev=%s flags=%x\n",
++ dev->name,
++ dev->flags);
++ if(strncmp(dev->name, "ipsec", strlen("ipsec")) == 0) {
++ printk(KERN_CRIT "IPSEC EVENT: KLIPS device %s shut down.\n",
++ dev->name);
++ }
++#ifdef NET_21
++ break;
++ case NETDEV_UNREGISTER:
++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
++ "klips_debug:ipsec_device_event: "
++ "NETDEV_UNREGISTER dev=%s flags=%x\n",
++ dev->name,
++ dev->flags);
++ break;
++ }
++#endif /* NET_21 */
++
++ /* find the attached physical device and detach it. */
++ for(i = 0; i < IPSEC_NUM_IF; i++) {
++ ipsec_dev = ipsecdevices[i];
++
++ if(ipsec_dev) {
++ priv = (struct ipsecpriv *)(ipsec_dev->priv);
++ if(priv) {
++ ;
++ if(((struct device *)(priv->dev)) == dev) {
++ /* dev_close(ipsec_dev); */
++ /* return */ ipsec_tunnel_detach(ipsec_dev);
++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
++ "klips_debug:ipsec_device_event: "
++ "device '%s' has been detached.\n",
++ ipsec_dev->name);
++ break;
++ }
++ } else {
++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
++ "klips_debug:ipsec_device_event: "
++ "device '%s' has no private data space!\n",
++ ipsec_dev->name);
++ }
++ }
++ }
++ break;
++ case NETDEV_UP:
++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
++ "klips_debug:ipsec_device_event: "
++ "NETDEV_UP dev=%s\n",
++ dev->name);
++ break;
++#ifdef NET_21
++ case NETDEV_REBOOT:
++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
++ "klips_debug:ipsec_device_event: "
++ "NETDEV_REBOOT dev=%s\n",
++ dev->name);
++ break;
++ case NETDEV_CHANGE:
++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
++ "klips_debug:ipsec_device_event: "
++ "NETDEV_CHANGE dev=%s flags=%x\n",
++ dev->name,
++ dev->flags);
++ break;
++ case NETDEV_REGISTER:
++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
++ "klips_debug:ipsec_device_event: "
++ "NETDEV_REGISTER dev=%s\n",
++ dev->name);
++ break;
++ case NETDEV_CHANGEMTU:
++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
++ "klips_debug:ipsec_device_event: "
++ "NETDEV_CHANGEMTU dev=%s to mtu=%d\n",
++ dev->name,
++ dev->mtu);
++ break;
++ case NETDEV_CHANGEADDR:
++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
++ "klips_debug:ipsec_device_event: "
++ "NETDEV_CHANGEADDR dev=%s\n",
++ dev->name);
++ break;
++ case NETDEV_GOING_DOWN:
++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
++ "klips_debug:ipsec_device_event: "
++ "NETDEV_GOING_DOWN dev=%s\n",
++ dev->name);
++ break;
++ case NETDEV_CHANGENAME:
++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
++ "klips_debug:ipsec_device_event: "
++ "NETDEV_CHANGENAME dev=%s\n",
++ dev->name);
++ break;
++#endif /* NET_21 */
++ default:
++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
++ "klips_debug:ipsec_device_event: "
++ "event type %ld unrecognised for dev=%s\n",
++ event,
++ dev->name);
++ break;
++ }
++ return NOTIFY_DONE;
++}
++
++/*
++ * Called when an ipsec tunnel device is initialized.
++ * The ipsec tunnel device structure is passed to us.
++ */
++
++int
++ipsec_tunnel_init(struct device *dev)
++{
++ int i;
++
++ KLIPS_PRINT(debug_tunnel,
++ "klips_debug:ipsec_tunnel_init: "
++ "allocating %lu bytes initialising device: %s\n",
++ (unsigned long) sizeof(struct ipsecpriv),
++ dev->name ? dev->name : "NULL");
++
++ /* Add our tunnel functions to the device */
++ dev->open = ipsec_tunnel_open;
++ dev->stop = ipsec_tunnel_close;
++ dev->hard_start_xmit = ipsec_tunnel_start_xmit;
++ dev->get_stats = ipsec_tunnel_get_stats;
++
++ dev->priv = kmalloc(sizeof(struct ipsecpriv), GFP_KERNEL);
++ if (dev->priv == NULL)
++ return -ENOMEM;
++ memset((caddr_t)(dev->priv), 0, sizeof(struct ipsecpriv));
++
++ for(i = 0; i < sizeof(zeroes); i++) {
++ ((__u8*)(zeroes))[i] = 0;
++ }
++
++#ifndef NET_21
++ /* Initialize the tunnel device structure */
++ for (i = 0; i < DEV_NUMBUFFS; i++)
++ skb_queue_head_init(&dev->buffs[i]);
++#endif /* !NET_21 */
++
++ dev->set_multicast_list = NULL;
++ dev->do_ioctl = ipsec_tunnel_ioctl;
++ dev->hard_header = NULL;
++ dev->rebuild_header = NULL;
++ dev->set_mac_address = NULL;
++#ifndef NET_21
++ dev->header_cache_bind = NULL;
++#endif /* !NET_21 */
++ dev->header_cache_update= NULL;
++
++#ifdef NET_21
++/* prv->neigh_setup = NULL; */
++ dev->neigh_setup = ipsec_tunnel_neigh_setup_dev;
++#endif /* NET_21 */
++ dev->hard_header_len = 0;
++ dev->mtu = 0;
++ dev->addr_len = 0;
++ dev->type = ARPHRD_VOID; /* ARPHRD_TUNNEL; */ /* ARPHRD_ETHER; */
++ dev->tx_queue_len = 10; /* Small queue */
++ memset((caddr_t)(dev->broadcast),0xFF, ETH_ALEN); /* what if this is not attached to ethernet? */
++
++ /* New-style flags. */
++ dev->flags = IFF_NOARP /* 0 */ /* Petr Novak */;
++#ifdef NET_21
++ dev_init_buffers(dev);
++#else /* NET_21 */
++ dev->family = AF_INET;
++ dev->pa_addr = 0;
++ dev->pa_brdaddr = 0;
++ dev->pa_mask = 0;
++ dev->pa_alen = 4;
++#endif /* NET_21 */
++
++ /* We're done. Have I forgotten anything? */
++ return 0;
++}
++
++/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
++/* Module specific interface (but it links with the rest of IPSEC) */
++/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
++
++int
++ipsec_tunnel_probe(struct device *dev)
++{
++ ipsec_tunnel_init(dev);
++ return 0;
++}
++
++struct device *ipsecdevices[IPSEC_NUM_IF];
++
++int
++ipsec_tunnel_init_devices(void)
++{
++ int i;
++ char name[IFNAMSIZ];
++ struct device *dev_ipsec;
++
++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
++ "klips_debug:ipsec_tunnel_init_devices: "
++ "creating and registering IPSEC_NUM_IF=%u devices, allocating %lu per device, IFNAMSIZ=%u.\n",
++ IPSEC_NUM_IF,
++ (unsigned long) (sizeof(struct device) + IFNAMSIZ),
++ IFNAMSIZ);
++
++ for(i = 0; i < IPSEC_NUM_IF; i++) {
++ sprintf(name, IPSEC_DEV_FORMAT, i);
++ dev_ipsec = (struct device*)kmalloc(sizeof(struct device), GFP_KERNEL);
++ if (dev_ipsec == NULL) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
++ "klips_debug:ipsec_tunnel_init_devices: "
++ "failed to allocate memory for device %s, quitting device init.\n",
++ name);
++ return -ENOMEM;
++ }
++ memset((caddr_t)dev_ipsec, 0, sizeof(struct device));
++#ifdef NETDEV_23
++ strncpy(dev_ipsec->name, name, sizeof(dev_ipsec->name));
++#else /* NETDEV_23 */
++ dev_ipsec->name = (char*)kmalloc(IFNAMSIZ, GFP_KERNEL);
++ if (dev_ipsec->name == NULL) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
++ "klips_debug:ipsec_tunnel_init_devices: "
++ "failed to allocate memory for device %s name, quitting device init.\n",
++ name);
++ return -ENOMEM;
++ }
++ memset((caddr_t)dev_ipsec->name, 0, IFNAMSIZ);
++ strncpy(dev_ipsec->name, name, IFNAMSIZ);
++#endif /* NETDEV_23 */
++ dev_ipsec->next = NULL;
++ dev_ipsec->init = &ipsec_tunnel_probe;
++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
++ "klips_debug:ipsec_tunnel_init_devices: "
++ "registering device %s\n",
++ dev_ipsec->name);
++
++ /* reference and hold the device reference */
++ dev_hold(dev_ipsec);
++ ipsecdevices[i]=dev_ipsec;
++
++ if (register_netdev(dev_ipsec) != 0) {
++ KLIPS_PRINT(1 || debug_tunnel & DB_TN_INIT,
++ "klips_debug:ipsec_tunnel_init_devices: "
++ "registering device %s failed, quitting device init.\n",
++ dev_ipsec->name);
++ return -EIO;
++ } else {
++ KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
++ "klips_debug:ipsec_tunnel_init_devices: "
++ "registering device %s succeeded, continuing...\n",
++ dev_ipsec->name);
++ }
++ }
++ return 0;
++}
++
++/* void */
++int
++ipsec_tunnel_cleanup_devices(void)
++{
++ int error = 0;
++ int i;
++ char name[32];
++ struct device *dev_ipsec;
++
++ for(i = 0; i < IPSEC_NUM_IF; i++) {
++ dev_ipsec = ipsecdevices[i];
++ if(dev_ipsec == NULL) {
++ continue;
++ }
++
++ /* release reference */
++ ipsecdevices[i]=NULL;
++ ipsec_dev_put(dev_ipsec);
++
++ KLIPS_PRINT(debug_tunnel, "Unregistering %s (refcnt=%d)\n",
++ name,
++ atomic_read(&dev_ipsec->refcnt));
++ unregister_netdev(dev_ipsec);
++ KLIPS_PRINT(debug_tunnel, "Unregisted %s\n", name);
++#ifndef NETDEV_23
++ kfree(dev_ipsec->name);
++ dev_ipsec->name=NULL;
++#endif /* !NETDEV_23 */
++ kfree(dev_ipsec->priv);
++ dev_ipsec->priv=NULL;
++ }
++ return error;
++}
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.220 2004/04/06 02:49:26 mcr
++ * pullup of algo code from alg-branch.
++ *
++ * Revision 1.219 2004/02/03 03:13:17 mcr
++ * minor edits for readability, and error reporting.
++ *
++ * Revision 1.218 2004/01/27 20:29:20 mcr
++ * fix for unregister_netdev() problem for underlying eth0.
++ *
++ * Revision 1.217 2003/12/10 01:14:27 mcr
++ * NAT-traversal patches to KLIPS.
++ *
++ * Revision 1.216 2003/12/04 23:01:17 mcr
++ * removed ipsec_netlink.h
++ *
++ * Revision 1.215 2003/12/04 16:35:16 ken
++ * Fix for ATM devices where physdev->hard_header_len *is* correct
++ *
++ * Revision 1.214 2003/11/25 23:52:37 mcr
++ * fix typo in patch - ixs-> needed.
++ *
++ * Revision 1.213 2003/11/24 18:25:49 mcr
++ * patch from willy@w.ods.org to fix problems with ATM interfaces.
++ *
++ * Revision 1.212 2003/10/31 02:27:55 mcr
++ * pulled up port-selector patches and sa_id elimination.
++ *
++ * Revision 1.211.2.2 2003/10/29 01:30:41 mcr
++ * elimited "struct sa_id".
++ *
++ * Revision 1.211.2.1 2003/09/21 13:59:56 mcr
++ * pre-liminary X.509 patch - does not yet pass tests.
++ *
++ * Revision 1.211 2003/09/10 16:46:30 mcr
++ * patches for 2.4 backport/2.6 existence.
++ *
++ * Revision 1.210 2003/07/31 22:47:16 mcr
++ * preliminary (untested by FS-team) 2.5 patches.
++ *
++ * Revision 1.209 2003/06/22 21:28:43 mcr
++ * inability to unload module was caused by calls to dev_get
++ * (ipsec_dev_get), to gather a device from a name. There is
++ * simply no reason to look the devices up - they should be kept
++ * in a nice array, ready for use.
++ *
++ * Revision 1.208 2003/06/22 21:25:07 mcr
++ * all staticly counted ipsecXXX device support removed.
++ *
++ * Revision 1.207 2003/04/02 20:15:37 mcr
++ * fix for PR#204 - do not clear connection tracking info if we
++ * the packet is being sent in the clear.
++ *
++ * Revision 1.206 2003/02/12 19:32:51 rgb
++ * Refactored file to:
++ * ipsec_xmit.c
++ * ipsec_xmit.h
++ * ipsec_mast.c
++ *
++ * Revision 1.205 2003/02/06 17:47:00 rgb
++ *
++ * Remove unused ipsec_tunnel_lock() and ipsec_tunnel_unlock() code.
++ * Refactor ipsec_tunnel_start_xmit() further into:
++ * ipsec_xmit_sanity_check_dev()
++ * ipsec_xmit_sanity_check_skb()
++ * ipsec_xmit_strip_hard_header()
++ * ipsec_xmit_restore_hard_header()
++ * ipsec_xmit_send()
++ * ipsec_xmit_cleanup()
++ * and start a skeletal ipsec_mast_start_xmit() .
++ *
++ * Revision 1.204 2003/02/06 06:43:46 rgb
++ *
++ * Refactor ipsec_tunnel_start_xmit, bringing out:
++ * ipsec_xmit_SAlookup
++ * ipsec_xmit_encap_once
++ * ipsec_xmit_encap_bundle
++ *
++ * Revision 1.203 2003/02/06 02:21:34 rgb
++ *
++ * Moved "struct auth_alg" from ipsec_rcv.c to ipsec_ah.h .
++ * Changed "struct ah" to "struct ahhdr" and "struct esp" to "struct esphdr".
++ * Removed "#ifdef INBOUND_POLICY_CHECK_eroute" dead code.
++ *
++ * Revision 1.202 2003/01/03 07:38:01 rgb
++ *
++ * Start to refactor ipsec_tunnel_start_xmit() by putting local variables
++ * into struct ipsec_xmit_state and renaming a few variables to give more
++ * unique or searchable names.
++ *
++ * Revision 1.201 2003/01/03 00:31:28 rgb
++ *
++ * Clean up memset usage, including fixing 2 places where keys were not
++ * properly wiped.
++ *
++ * Revision 1.200 2002/12/06 02:24:02 mcr
++ * patches for compiling against SUSE 8.1 kernels. Requires
++ * an additional -DSUSE_LINUX_2_4_19_IS_STUPID.
++ *
++ * Revision 1.199 2002/10/12 23:11:53 dhr
++ *
++ * [KenB + DHR] more 64-bit cleanup
++ *
++ * Revision 1.198 2002/10/05 05:02:58 dhr
++ *
++ * C labels go on statements
++ *
++ * Revision 1.197 2002/09/20 05:01:50 rgb
++ * Added compiler directive to switch on IP options and fix IP options bug.
++ * Make ip->ihl treatment consistent using shifts rather than multiplications.
++ * Check for large enough packet before accessing udp header for IKE bypass.
++ * Added memory allocation debugging.
++ * Fixed potential memory allocation failure-induced oops.
++ *
++ * Revision 1.196 2002/07/24 18:44:54 rgb
++ * Type fiddling to tame ia64 compiler.
++ *
++ * Revision 1.195 2002/07/23 03:36:07 rgb
++ * Fixed 2.2 device initialisation hang.
++ *
++ * Revision 1.194 2002/05/27 21:40:34 rgb
++ * Set unused ipsec devices to ARPHRD_VOID to avoid confusing iproute2.
++ * Cleaned up intermediate step to dynamic device allocation.
++ *
++ * Revision 1.193 2002/05/27 19:31:36 rgb
++ * Convert to dynamic ipsec device allocation.
++ * Remove final vistiges of tdb references via IPSEC_KLIPS1_COMPAT.
++ *
++ * Revision 1.192 2002/05/23 07:14:28 rgb
++ * Added refcount code.
++ * Cleaned up %p variants to 0p%p for test suite cleanup.
++ *
++ * Revision 1.191 2002/05/14 02:34:37 rgb
++ * Change all references to tdb, TDB or Tunnel Descriptor Block to ips,
++ * ipsec_sa or ipsec_sa.
++ *
++ * Revision 1.190 2002/04/24 07:55:32 mcr
++ * #include patches and Makefiles for post-reorg compilation.
++ *
++ * Revision 1.189 2002/04/24 07:36:32 mcr
++ * Moved from ./klips/net/ipsec/ipsec_tunnel.c,v
++ *
++ * Revision 1.188 2002/04/20 00:12:25 rgb
++ * Added esp IV CBC attack fix, disabled.
++ *
++ * Revision 1.187 2002/03/23 19:55:17 rgb
++ * Fix for 2.2 local IKE fragmentation blackhole. Still won't work if
++ * iptraf or another pcap app is running.
++ *
++ * Revision 1.186 2002/03/19 03:26:22 rgb
++ * Applied DHR's tunnel patch to streamline IKE/specialSA processing.
++ *
++ * Revision 1.185 2002/02/20 04:13:05 rgb
++ * Send back ICMP_PKT_FILTERED upon %reject.
++ *
++ * Revision 1.184 2002/01/29 17:17:56 mcr
++ * moved include of ipsec_param.h to after include of linux/kernel.h
++ * otherwise, it seems that some option that is set in ipsec_param.h
++ * screws up something subtle in the include path to kernel.h, and
++ * it complains on the snprintf() prototype.
++ *
++ * Revision 1.183 2002/01/29 04:00:53 mcr
++ * more excise of kversions.h header.
++ *
++ * Revision 1.182 2002/01/29 02:13:18 mcr
++ * introduction of ipsec_kversion.h means that include of
++ * ipsec_param.h must preceed any decisions about what files to
++ * include to deal with differences in kernel source.
++ *
++ * Revision 1.181 2002/01/07 20:00:33 rgb
++ * Added IKE destination port debugging.
++ *
++ * Revision 1.180 2001/12/21 21:49:54 rgb
++ * Fixed bug as a result of moving IKE bypass above %trap/%hold code.
++ *
++ * Revision 1.179 2001/12/19 21:08:14 rgb
++ * Added transport protocol ports to ipsec_print_ip().
++ * Update eroute info for non-SA targets.
++ * Added obey DF code disabled.
++ * Fixed formatting bugs in ipsec_tunnel_hard_header().
++ *
++ * Revision 1.178 2001/12/05 09:36:10 rgb
++ * Moved the UDP/500 IKE check just above the %hold/%trap checks to avoid
++ * IKE packets being stolen by the %hold (and returned to the sending KMd
++ * in an ACQUIRE, ironically ;-).
++ *
++ * Revision 1.177 2001/11/26 09:23:50 rgb
++ * Merge MCR's ipsec_sa, eroute, proc and struct lifetime changes.
++ *
++ * Revision 1.170.2.1 2001/09/25 02:28:27 mcr
++ * struct tdb -> struct ipsec_sa.
++ * lifetime checks moved to common routines.
++ * cleaned up includes.
++ *
++ * Revision 1.170.2.2 2001/10/22 21:08:01 mcr
++ * include des.h, removed phony prototypes and fixed calling
++ * conventions to match real prototypes.
++ *
++ * Revision 1.176 2001/11/09 18:32:31 rgb
++ * Added Hans Schultz' fragmented UDP/500 IKE socket port selector.
++ *
++ * Revision 1.175 2001/11/06 20:47:00 rgb
++ * Added Eric Espie's TRAPSUBNET fix, minus spin-lock-bh dabbling.
++ *
++ * Revision 1.174 2001/11/06 19:50:43 rgb
++ * Moved IP_SEND, ICMP_SEND, DEV_QUEUE_XMIT macros to ipsec_tunnel.h for
++ * use also by pfkey_v2_parser.c
++ *
++ * Revision 1.173 2001/10/29 21:53:44 henry
++ * tone down the device-down message slightly, until we can make it smarter
++ *
++ * Revision 1.172 2001/10/26 04:59:37 rgb
++ * Added a critical level syslog message if an ipsec device goes down.
++ *
++ * Revision 1.171 2001/10/18 04:45:21 rgb
++ * 2.4.9 kernel deprecates linux/malloc.h in favour of linux/slab.h,
++ * lib/freeswan.h version macros moved to lib/kversions.h.
++ * Other compiler directive cleanups.
++ *
++ * Revision 1.170 2001/09/25 00:09:50 rgb
++ * Added NetCelo's TRAPSUBNET code to convert a new type TRAPSUBNET into a
++ * HOLD.
++ *
++ * Revision 1.169 2001/09/15 16:24:05 rgb
++ * Re-inject first and last HOLD packet when an eroute REPLACE is done.
++ *
++ * Revision 1.168 2001/09/14 16:58:37 rgb
++ * Added support for storing the first and last packets through a HOLD.
++ *
++ * Revision 1.167 2001/09/08 21:13:33 rgb
++ * Added pfkey ident extension support for ISAKMPd. (NetCelo)
++ *
++ * Revision 1.166 2001/08/27 19:47:59 rgb
++ * Clear tdb before usage.
++ * Added comment: clear IF before calling routing?
++ *
++ * Revision 1.165 2001/07/03 01:23:53 rgb
++ * Send back ICMP iff DF set, !ICMP, offset==0, sysctl_icmp, iph->tot_len >
++ * emtu, and don't drop.
++ *
++ * Revision 1.164 2001/06/14 19:35:10 rgb
++ * Update copyright date.
++ *
++ * Revision 1.163 2001/06/06 20:28:51 rgb
++ * Added sanity checks for NULL skbs and devices.
++ * Added more debugging output to various functions.
++ * Removed redundant dev->priv argument to ipsec_tunnel_{at,de}tach().
++ * Renamed ipsec_tunnel_attach() virtual and physical device arguments.
++ * Corrected neigh_setup() device function assignment.
++ * Keep valid pointers to ipsec_tunnel_*() on detach.
++ * Set dev->type to the originally-initiallised value.
++ *
++ * Revision 1.162 2001/06/01 07:28:04 rgb
++ * Added sanity checks for detached devices. Don't down virtual devices
++ * to prevent packets going out in the clear if the detached device comes
++ * back up.
++ *
++ * Revision 1.161 2001/05/30 08:14:52 rgb
++ * Removed vestiges of esp-null transforms.
++ * NetDev Notifier instrumentation to track down disappearing devices.
++ *
++ * Revision 1.160 2001/05/29 05:15:12 rgb
++ * Added SS' PMTU patch which notifies sender if packet doesn't fit
++ * physical MTU (if it wasn't ICMP) and then drops it.
++ *
++ * Revision 1.159 2001/05/27 06:12:12 rgb
++ * Added structures for pid, packet count and last access time to eroute.
++ * Added packet count to beginning of /proc/net/ipsec_eroute.
++ *
++ * Revision 1.158 2001/05/24 05:39:33 rgb
++ * Applied source zeroing to 2.2 ip_route_output() call as well to enable
++ * PASS eroutes for opportunism.
++ *
++ * Revision 1.157 2001/05/23 22:35:28 rgb
++ * 2.4 source override simplification.
++ *
++ * Revision 1.156 2001/05/23 21:41:31 rgb
++ * Added error return code printing on ip_route_output().
++ *
++ * Revision 1.155 2001/05/23 05:09:13 rgb
++ * Fixed incorrect ip_route_output() failure message.
++ *
++ * Revision 1.154 2001/05/21 14:53:31 rgb
++ * Added debug statement for case when ip_route_output() fails, causing
++ * packet to be dropped, but log looked ok.
++ *
++ * Revision 1.153 2001/05/19 02:37:54 rgb
++ * Fixed missing comment termination.
++ *
++ * Revision 1.152 2001/05/19 02:35:50 rgb
++ * Debug code optimisation for non-debug speed.
++ * Kernel version compiler define comments.
++ * 2.2 and 2.4 kernel ip_send device and ip debug output added.
++ *
++ * Revision 1.151 2001/05/18 16:17:35 rgb
++ * Changed reference from "magic" to "shunt" SAs.
++ *
++ * Revision 1.150 2001/05/18 16:12:19 rgb
++ * Changed UDP/500 bypass test from 3 nested ifs to one anded if.
++ *
++ * Revision 1.149 2001/05/16 04:39:33 rgb
++ * Add default == eroute.dest to IKE bypass conditions for magic eroutes.
++ *
++ * Revision 1.148 2001/05/05 03:31:41 rgb
++ * IP frag debugging updates and enhancements.
++ *
++ * Revision 1.147 2001/05/03 19:41:40 rgb
++ * Added SS' skb_cow fix for 2.4.4.
++ *
++ * Revision 1.146 2001/04/30 19:28:16 rgb
++ * Update for 2.4.4. ip_select_ident() now has 3 args.
++ *
++ * Revision 1.145 2001/04/23 14:56:10 rgb
++ * Added spin_lock() check to prevent double-locking for multiple
++ * transforms and hence kernel lock-ups with SMP kernels.
++ *
++ * Revision 1.144 2001/04/21 23:04:45 rgb
++ * Define out skb->used for 2.4 kernels.
++ * Check if soft expire has already been sent before sending another to
++ * prevent ACQUIRE flooding.
++ *
++ * Revision 1.143 2001/03/16 07:37:21 rgb
++ * Added comments to all #endifs.
++ *
++ * Revision 1.142 2001/02/28 05:03:27 rgb
++ * Clean up and rationalise startup messages.
++ *
++ * Revision 1.141 2001/02/27 22:24:54 rgb
++ * Re-formatting debug output (line-splitting, joining, 1arg/line).
++ * Check for satoa() return codes.
++ *
++ * Revision 1.140 2001/02/27 06:40:12 rgb
++ * Fixed TRAP->HOLD eroute byte order.
++ *
++ * Revision 1.139 2001/02/26 20:38:59 rgb
++ * Added compiler defines for 2.4.x-specific code.
++ *
++ * Revision 1.138 2001/02/26 19:57:27 rgb
++ * Implement magic SAs %drop, %reject, %trap, %hold, %pass as part
++ * of the new SPD and to support opportunistic.
++ * Drop sysctl_ipsec_{no_eroute_pass,opportunistic}, replaced by magic SAs.
++ *
++ * Revision 1.137 2001/02/19 22:29:49 rgb
++ * Fixes for presence of active ipv6 segments which share ipsec physical
++ * device (gg).
++ *
++ * Revision 1.136 2001/01/29 22:30:38 rgb
++ * Fixed minor acquire debug printing bug.
++ *
++ * Revision 1.135 2001/01/29 22:19:45 rgb
++ * Zero source address for 2.4 bypass route lookup.
++ *
++ * Revision 1.134 2001/01/23 20:19:49 rgb
++ * 2.4 fix to remove removed is_clone member.
++ *
++ * Revision 1.133 2000/12/09 22:08:35 rgb
++ * Fix NET_23 bug, should be NETDEV_23.
++ *
++ * Revision 1.132 2000/12/01 06:54:50 rgb
++ * Fix for new 2.4 IP TTL default variable name.
++ *
++ * Revision 1.131 2000/11/09 20:52:15 rgb
++ * More spinlock shuffling, locking earlier and unlocking later in rcv to
++ * include ipcomp and prevent races, renaming some tdb variables that got
++ * forgotten, moving some unlocks to include tdbs and adding a missing
++ * unlock. Thanks to Svenning for some of these.
++ *
++ * Revision 1.130 2000/11/09 20:11:22 rgb
++ * Minor shuffles to fix non-standard kernel config option selection.
++ *
++ * Revision 1.129 2000/11/06 04:32:49 rgb
++ * Clean up debug printing.
++ * Copy skb->protocol for all kernel versions.
++ * Ditched spin_lock_irqsave in favour of spin_lock.
++ * Disabled TTL decrement, done in ip_forward.
++ * Added debug printing before pfkey_acquire().
++ * Fixed printk-deltdbchain-spin_lock races (Svenning).
++ * Use defaultTTL for 2.1+ kernels.
++ * Add Svenning's adaptive content compression.
++ * Fix up debug display arguments.
++ *
++ * Revision 1.128 2000/09/28 00:58:57 rgb
++ * Moved the IKE passthrough check after the eroute lookup so we can pass
++ * IKE through intermediate tunnels.
++ *
++ * Revision 1.127 2000/09/22 17:52:11 rgb
++ * Fixed misleading ipcomp debug output.
++ *
++ * Revision 1.126 2000/09/22 04:22:56 rgb
++ * Fixed dumb spi->cpi conversion error.
++ *
++ * Revision 1.125 2000/09/21 04:34:48 rgb
++ * A few debug-specific things should be hidden under
++ * CONFIG_IPSEC_DEBUG.(MB)
++ * Improved ip_send() error handling.(MB)
++ *
++ * Revision 1.124 2000/09/21 03:40:58 rgb
++ * Added more debugging to try and track down the cpi outward copy problem.
++ *
++ * Revision 1.123 2000/09/19 07:08:49 rgb
++ * Added debugging to outgoing compression report.
++ *
++ * Revision 1.122 2000/09/18 19:21:26 henry
++ * RGB-supplied fix for RH5.2 problem
++ *
++ * Revision 1.121 2000/09/17 21:05:09 rgb
++ * Added tdb to skb_compress call to write in cpi.
++ *
++ * Revision 1.120 2000/09/17 16:57:16 rgb
++ * Added Svenning's patch to remove restriction of ipcomp to innermost
++ * transform.
++ *
++ * Revision 1.119 2000/09/15 11:37:01 rgb
++ * Merge in heavily modified Svenning Soerensen's <svenning@post5.tele.dk>
++ * IPCOMP zlib deflate code.
++ *
++ * Revision 1.118 2000/09/15 04:57:16 rgb
++ * Moved debug output after sanity check.
++ * Added tos copy sysctl.
++ *
++ * Revision 1.117 2000/09/12 03:22:51 rgb
++ * Converted ipsec_icmp, no_eroute_pass, opportunistic and #if0 debugs to
++ * sysctl.
++ *
++ * Revision 1.116 2000/09/08 19:18:19 rgb
++ * Change references from DEBUG_IPSEC to CONFIG_IPSEC_DEBUG.
++ * Added outgoing opportunistic hook, ifdef'ed out.
++ *
++ * Revision 1.115 2000/08/30 05:27:29 rgb
++ * Removed all the rest of the references to tdb_spi, tdb_proto, tdb_dst.
++ * Kill remainder of tdb_xform, tdb_xdata, xformsw.
++ *
++ * Revision 1.114 2000/08/28 18:15:46 rgb
++ * Added MB's nf-debug reset patch.
++ *
++ * Revision 1.113 2000/08/27 02:26:40 rgb
++ * Send all no-eroute-bypass, pluto-bypass and passthrough packets through
++ * fragmentation machinery for 2.0, 2.2 and 2.4 kernels.
++ *
++ * Revision 1.112 2000/08/20 21:37:33 rgb
++ * Activated pfkey_expire() calls.
++ * Added a hard/soft expiry parameter to pfkey_expire(). (Momchil)
++ * Re-arranged the order of soft and hard expiry to conform to RFC2367.
++ * Clean up references to CONFIG_IPSEC_PFKEYv2.
++ *
++ * Revision 1.111 2000/08/01 14:51:51 rgb
++ * Removed _all_ remaining traces of DES.
++ *
++ * Revision 1.110 2000/07/28 14:58:31 rgb
++ * Changed kfree_s to kfree, eliminating extra arg to fix 2.4.0-test5.
++ *
++ * Revision 1.109 2000/07/28 13:50:54 rgb
++ * Changed enet_statistics to net_device_stats and added back compatibility
++ * for pre-2.1.19.
++ *
++ * Revision 1.108 2000/05/16 03:03:11 rgb
++ * Updates for 2.3.99pre8 from MB.
++ *
++ * Revision 1.107 2000/05/10 23:08:21 rgb
++ * Print a debug warning about bogus packets received by the outgoing
++ * processing machinery only when klipsdebug is not set to none.
++ * Comment out the device initialisation informational messages.
++ *
++ * Revision 1.106 2000/05/10 19:17:14 rgb
++ * Define an IP_SEND macro, intending to have all packet passthroughs
++ * use fragmentation. This didn't quite work, but is a step in the
++ * right direction.
++ * Added buffer allocation debugging statements.
++ * Added configure option to shut off no eroute passthrough.
++ * Only check usetime against soft and hard limits if the tdb has been
++ * used.
++ * Cast output of ntohl so that the broken prototype doesn't make our
++ * compile noisy.
++ *
++ * Revision 1.105 2000/03/22 16:15:37 rgb
++ * Fixed renaming of dev_get (MB).
++ *
++ * Revision 1.104 2000/03/16 14:04:15 rgb
++ * Indented headers for readability.
++ * Fixed debug scope to enable compilation with debug off.
++ * Added macros for ip_chk_addr and IS_MYADDR for identifying self.
++ *
++ * Revision 1.103 2000/03/16 07:11:07 rgb
++ * Hardcode PF_KEYv2 support.
++ * Fixed bug which allowed UDP/500 packet from another machine
++ * through in the clear.
++ * Added disabled skb->protocol fix for ISDN/ASYNC PPP from Matjaz Godec.
++ *
++ * Revision 1.102 2000/03/14 12:26:59 rgb
++ * Added skb->nfct support for clearing netfilter conntrack bits (MB).
++ *
++ * Revision 1.101 2000/02/14 21:05:22 rgb
++ * Added MB's netif_queue fix for kernels 2.3.43+.
++ *
++ * Revision 1.100 2000/01/26 10:04:57 rgb
++ * Fixed noisy 2.0 printk arguments.
++ *
++ * Revision 1.99 2000/01/21 06:16:25 rgb
++ * Added sanity checks on skb_push(), skb_pull() to prevent panics.
++ * Switched to AF_ENCAP macro.
++ * Shortened debug output per packet and re-arranging debug_tunnel
++ * bitmap flags, while retaining necessary information to avoid
++ * trampling the kernel print ring buffer.
++ * Reformatted recursion switch code.
++ * Changed all references to tdb_proto to tdb_said.proto for clarity.
++ *
++ * Revision 1.98 2000/01/13 08:09:31 rgb
++ * Shuffled debug_tunnel switches to focus output.
++ * Fixed outgoing recursion bug, limiting to recursing only if the remote
++ * SG changes and if it is valid, ie. not passthrough.
++ * Clarified a number of debug messages.
++ *
++ * Revision 1.97 2000/01/10 16:37:16 rgb
++ * MB support for new ip_select_ident() upon disappearance of
++ * ip_id_count in 2.3.36+.
++ *
++ * Revision 1.96 1999/12/31 14:59:08 rgb
++ * MB fix to use new skb_copy_expand in kernel 2.3.35.
++ *
++ * Revision 1.95 1999/12/29 21:15:44 rgb
++ * Fix tncfg to aliased device bug.
++ *
++ * Revision 1.94 1999/12/22 04:26:06 rgb
++ * Converted all 'static' functions to 'DEBUG_NO_STATIC' to enable
++ * debugging by providing external labels to all functions with debugging
++ * turned on.
++ *
++ * Revision 1.93 1999/12/13 13:30:14 rgb
++ * Changed MTU reports and HW address reporting back to debug only.
++ *
++ * Revision 1.92 1999/12/07 18:57:56 rgb
++ * Fix PFKEY symbol compile error (SADB_*) without pfkey enabled.
++ *
++ * Revision 1.91 1999/12/01 22:15:36 rgb
++ * Add checks for LARVAL and DEAD SAs.
++ * Change state of SA from MATURE to DYING when a soft lifetime is
++ * reached and print debug warning.
++ *
++ * Revision 1.90 1999/11/23 23:04:04 rgb
++ * Use provided macro ADDRTOA_BUF instead of hardcoded value.
++ * Sort out pfkey and freeswan headers, putting them in a library path.
++ *
++ * Revision 1.89 1999/11/18 18:50:59 rgb
++ * Changed all device registrations for static linking to
++ * dynamic to reduce the number and size of patches.
++ *
++ * Revision 1.88 1999/11/18 04:09:19 rgb
++ * Replaced all kernel version macros to shorter, readable form.
++ *
++ * Revision 1.87 1999/11/17 15:53:40 rgb
++ * Changed all occurrences of #include "../../../lib/freeswan.h"
++ * to #include <freeswan.h> which works due to -Ilibfreeswan in the
++ * klips/net/ipsec/Makefile.
++ *
++ * Revision 1.86 1999/10/16 18:25:37 rgb
++ * Moved SA lifetime expiry checks before packet processing.
++ * Expire SA on replay counter rollover.
++ *
++ * Revision 1.85 1999/10/16 04:24:31 rgb
++ * Add stats for time since last packet.
++ *
++ * Revision 1.84 1999/10/16 00:30:47 rgb
++ * Added SA lifetime counting.
++ *
++ * Revision 1.83 1999/10/15 22:15:57 rgb
++ * Clean out cruft.
++ * Add debugging.
++ *
++ * Revision 1.82 1999/10/08 18:26:19 rgb
++ * Fix 2.0.3x outgoing fragmented packet memory leak.
++ *
++ * Revision 1.81 1999/10/05 02:38:54 rgb
++ * Lower the default mtu of virtual devices to 16260.
++ *
++ * Revision 1.80 1999/10/03 18:56:41 rgb
++ * Spinlock support for 2.3.xx.
++ * Don't forget to undo spinlocks on error!
++ * Check for valid eroute before copying the structure.
++ *
++ * Revision 1.79 1999/10/01 15:44:53 rgb
++ * Move spinlock header include to 2.1> scope.
++ *
++ * Revision 1.78 1999/10/01 00:02:43 rgb
++ * Added tdb structure locking.
++ * Added eroute structure locking.
++ *
++ * Revision 1.77 1999/09/30 02:52:29 rgb
++ * Add Marc Boucher's Copy-On-Write code (same as ipsec_rcv.c).
++ *
++ * Revision 1.76 1999/09/25 19:31:27 rgb
++ * Refine MSS hack to affect SYN, but not SYN+ACK packets.
++ *
++ * Revision 1.75 1999/09/24 22:52:38 rgb
++ * Fix two things broken in 2.0.38 by trying to fix network notifiers.
++ *
++ * Revision 1.74 1999/09/24 00:30:37 rgb
++ * Add test for changed source as well as destination to check for
++ * recursion.
++ *
++ * Revision 1.73 1999/09/23 20:52:24 rgb
++ * Add James Morris' MSS hack patch, disabled.
++ *
++ * Revision 1.72 1999/09/23 20:22:40 rgb
++ * Enable, tidy and fix network notifier code.
++ *
++ * Revision 1.71 1999/09/23 18:09:05 rgb
++ * Clean up 2.2.x fragmenting traces.
++ * Disable dev->type switching, forcing ARPHRD_TUNNEL.
++ *
++ * Revision 1.70 1999/09/22 14:14:24 rgb
++ * Add sanity checks for revectored calls to prevent calling a downed I/F.
++ *
++ * Revision 1.69 1999/09/21 15:00:57 rgb
++ * Add Marc Boucher's packet size check.
++ * Flesh out network device notifier code.
++ *
++ * Revision 1.68 1999/09/18 11:39:57 rgb
++ * Start to add (disabled) netdevice notifier code.
++ *
++ * Revision 1.67 1999/09/17 23:44:40 rgb
++ * Add a comment warning potential code hackers to stay away from mac.raw.
++ *
++ * Revision 1.66 1999/09/17 18:04:02 rgb
++ * Add fix for unpredictable hard_header_len for ISDN folks (thanks MB).
++ * Ditch TTL decrement in 2.2 (MB).
++ *
++ * Revision 1.65 1999/09/15 23:15:35 henry
++ * Marc Boucher's PPP fixes
++ *
++ * Revision 1.64 1999/09/07 13:40:53 rgb
++ * Ditch unreliable references to skb->mac.raw.
++ *
++ * Revision 1.63 1999/08/28 11:33:09 rgb
++ * Check for null skb->mac pointer.
++ *
++ * Revision 1.62 1999/08/28 02:02:30 rgb
++ * Add Marc Boucher's fix for properly dealing with skb->sk.
++ *
++ * Revision 1.61 1999/08/27 05:23:05 rgb
++ * Clean up skb->data/raw/nh/h manipulation.
++ * Add Marc Boucher's mods to aid tcpdump.
++ * Add sanity checks to skb->raw/nh/h pointer copies in skb_copy_expand.
++ * Re-order hard_header stripping -- might be able to remove it...
++ *
++ * Revision 1.60 1999/08/26 20:01:02 rgb
++ * Tidy up compiler directives and macros.
++ * Re-enable ICMP for tunnels where inner_dst != outer_dst.
++ * Remove unnecessary skb->dev = physdev assignment affecting 2.2.x.
++ *
++ * Revision 1.59 1999/08/25 15:44:41 rgb
++ * Clean up from 2.2.x instrumenting for compilation under 2.0.36.
++ *
++ * Revision 1.58 1999/08/25 15:00:54 rgb
++ * Add dst cache code for 2.2.xx.
++ * Add sanity check for skb packet header pointers.
++ * Add/modify debugging instrumentation to *_start_xmit, *_hard_header and
++ * *_rebuild_header.
++ * Add neigh_* cache code.
++ * Change dev->type back to ARPHRD_TUNNEL.
++ *
++ * Revision 1.57 1999/08/17 21:50:23 rgb
++ * Fixed minor debug output bugs.
++ * Regrouped error recovery exit code.
++ * Added compiler directives to remove unwanted code and symbols.
++ * Shut off ICMP messages: to be refined to only send ICMP to remote systems.
++ * Add debugging code for output function addresses.
++ * Fix minor bug in (possibly unused) header_cache_bind function.
++ * Add device neighbour caching code.
++ * Change dev->type from ARPHRD_TUNNEL to physdev->type.
++ *
++ * Revision 1.56 1999/08/03 17:22:56 rgb
++ * Debug output clarification using KERN_* macros. Other inactive changes
++ * added.
++ *
++ * Revision 1.55 1999/08/03 16:58:46 rgb
++ * Fix skb_copy_expand size bug. Was getting incorrect size.
++ *
++ * Revision 1.54 1999/07/14 19:32:38 rgb
++ * Fix oversize packet crash and ssh stalling in 2.2.x kernels.
++ *
++ * Revision 1.53 1999/06/10 15:44:02 rgb
++ * Minor reformatting and clean-up.
++ *
++ * Revision 1.52 1999/05/09 03:25:36 rgb
++ * Fix bug introduced by 2.2 quick-and-dirty patch.
++ *
++ * Revision 1.51 1999/05/08 21:24:59 rgb
++ * Add casting to silence the 2.2.x compile.
++ *
++ * Revision 1.50 1999/05/05 22:02:32 rgb
++ * Add a quick and dirty port to 2.2 kernels by Marc Boucher <marc@mbsi.ca>.
++ *
++ * Revision 1.49 1999/04/29 15:18:52 rgb
++ * Change gettdb parameter to a pointer to reduce stack loading and
++ * facilitate parameter sanity checking.
++ * Fix undetected bug that might have tried to access a null pointer.
++ * Eliminate unnessessary usage of tdb_xform member to further switch
++ * away from the transform switch to the algorithm switch.
++ * Add return values to init and cleanup functions.
++ *
++ * Revision 1.48 1999/04/16 15:38:00 rgb
++ * Minor rearrangement of freeing code to avoid memory leaks with impossible or
++ * rare situations.
++ *
++ * Revision 1.47 1999/04/15 15:37:25 rgb
++ * Forward check changes from POST1_00 branch.
++ *
++ * Revision 1.32.2.4 1999/04/13 21:00:18 rgb
++ * Ditch 'things I wish I had known before...'.
++ *
++ * Revision 1.32.2.3 1999/04/13 20:34:38 rgb
++ * Free skb after fragmentation.
++ * Use stats more effectively.
++ * Add I/F to mtu notch-down reporting.
++ *
++ * Revision 1.32.2.2 1999/04/02 04:26:14 rgb
++ * Backcheck from HEAD, pre1.0.
++ *
++ * Revision 1.46 1999/04/11 00:29:00 henry
++ * GPL boilerplate
++ *
++ * Revision 1.45 1999/04/07 15:42:01 rgb
++ * Fix mtu/ping bug AGAIN!
++ *
++ * Revision 1.44 1999/04/06 04:54:27 rgb
++ * Fix/Add RCSID Id: and Log: bits to make PHMDs happy. This includes
++ * patch shell fixes.
++ *
++ * Revision 1.43 1999/04/04 03:57:07 rgb
++ * ip_fragment() doesn't free the supplied skb. Freed.
++ *
++ * Revision 1.42 1999/04/01 23:27:15 rgb
++ * Preload size of virtual mtu.
++ *
++ * Revision 1.41 1999/04/01 09:31:23 rgb
++ * Invert meaning of ICMP PMTUD config option and clarify.
++ * Code clean-up.
++ *
++ * Revision 1.40 1999/04/01 04:37:17 rgb
++ * SSH stalling bug fix.
++ *
++ * Revision 1.39 1999/03/31 23:44:28 rgb
++ * Don't send ICMP on DF and frag_off.
++ *
++ * Revision 1.38 1999/03/31 15:20:10 rgb
++ * Quiet down debugging.
++ *
++ * Revision 1.37 1999/03/31 08:30:31 rgb
++ * Add switch to shut off ICMP PMTUD packets.
++ *
++ * Revision 1.36 1999/03/31 05:44:47 rgb
++ * Keep PMTU reduction private.
++ *
++ * Revision 1.35 1999/03/27 15:13:02 rgb
++ * PMTU/fragmentation bug fix.
++ *
++ * Revision 1.34 1999/03/17 21:19:26 rgb
++ * Fix kmalloc nonatomic bug.
++ *
++ * Revision 1.33 1999/03/17 15:38:42 rgb
++ * Code clean-up.
++ * ESP_NULL IV bug fix.
++ *
++ * Revision 1.32 1999/03/01 20:44:25 rgb
++ * Code clean-up.
++ * Memory leak bug fix.
++ *
++ * Revision 1.31 1999/02/27 00:02:09 rgb
++ * Tune to report the MTU reduction once, rather than after every recursion
++ * through the encapsulating code, preventing tcp stream stalling.
++ *
++ * Revision 1.30 1999/02/24 20:21:01 rgb
++ * Reformat debug printk's.
++ * Fix recursive encapsulation, dynamic MTU bugs and add debugging code.
++ * Clean-up.
++ *
++ * Revision 1.29 1999/02/22 17:08:14 rgb
++ * Fix recursive encapsulation code.
++ *
++ * Revision 1.28 1999/02/19 18:27:02 rgb
++ * Improve DF, fragmentation and PMTU behaviour and add dynamic MTU discovery.
++ *
++ * Revision 1.27 1999/02/17 16:51:37 rgb
++ * Clean out unused cruft.
++ * Temporarily tone down volume of debug output.
++ * Temporarily shut off fragment rejection.
++ * Disabled temporary failed recursive encapsulation loop.
++ *
++ * Revision 1.26 1999/02/12 21:21:26 rgb
++ * Move KLIPS_PRINT to ipsec_netlink.h for accessibility.
++ *
++ * Revision 1.25 1999/02/11 19:38:27 rgb
++ * More clean-up.
++ * Add sanity checking for skb_copy_expand() to prevent kernel panics on
++ * skb_put() values out of range.
++ * Fix head/tailroom calculation causing skb_put() out-of-range values.
++ * Fix return values to prevent 'nonatomic alloc_skb' warnings.
++ * Allocate new skb iff needed.
++ * Added more debug statements.
++ * Make headroom depend on structure, not hard-coded values.
++ *
++ * Revision 1.24 1999/02/10 23:20:33 rgb
++ * Shut up annoying 'statement has no effect' compiler warnings with
++ * debugging compiled out.
++ *
++ * Revision 1.23 1999/02/10 22:36:30 rgb
++ * Clean-up obsolete, unused and messy code.
++ * Converted most IPSEC_DEBUG statements to KLIPS_PRINT macros.
++ * Rename ipsec_tunnel_do_xmit to ipsec_tunnel_start_xmit and eliminated
++ * original ipsec_tunnel_start_xmit.
++ * Send all packet with different inner and outer destinations directly to
++ * the attached physical device, rather than back through ip_forward,
++ * preventing disappearing routes problems.
++ * Do sanity checking before investing too much CPU in allocating new
++ * structures.
++ * Fail on IP header options: We cannot process them yet.
++ * Add some helpful comments.
++ * Use virtual device for parameters instead of physical device.
++ *
++ * Revision 1.22 1999/02/10 03:03:02 rgb
++ * Duh. Fixed the TTL bug: forgot to update the checksum.
++ *
++ * Revision 1.21 1999/02/09 23:17:53 rgb
++ * Add structure members to ipsec_print_ip debug function.
++ * Temporarily fix TTL bug preventing tunnel mode from functioning.
++ *
++ * Revision 1.20 1999/02/09 00:14:25 rgb
++ * Add KLIPSPRINT macro. (Not used yet, though.)
++ * Delete old ip_tunnel code (BADCODE).
++ * Decrement TTL in outgoing packet.
++ * Set TTL on new IPIP_TUNNEL to default, not existing packet TTL.
++ * Delete ethernet only feature and fix hard-coded hard_header_len.
++ *
++ * Revision 1.19 1999/01/29 17:56:22 rgb
++ * 64-bit re-fix submitted by Peter Onion.
++ *
++ * Revision 1.18 1999/01/28 22:43:24 rgb
++ * Fixed bug in ipsec_print_ip that caused an OOPS, found by P.Onion.
++ *
++ * Revision 1.17 1999/01/26 02:08:16 rgb
++ * Removed CONFIG_IPSEC_ALGO_SWITCH macro.
++ * Removed dead code.
++ *
++ * Revision 1.16 1999/01/22 06:25:26 rgb
++ * Cruft clean-out.
++ * Added algorithm switch code.
++ * 64-bit clean-up.
++ * Passthrough on IPIP protocol, spi 0x0 fix.
++ * Enhanced debugging.
++ *
++ * Revision 1.15 1998/12/01 13:22:04 rgb
++ * Added support for debug printing of version info.
++ *
++ * Revision 1.14 1998/11/30 13:22:55 rgb
++ * Rationalised all the klips kernel file headers. They are much shorter
++ * now and won't conflict under RH5.2.
++ *
++ * Revision 1.13 1998/11/17 21:13:52 rgb
++ * Put IKE port bypass debug output in user-switched debug statements.
++ *
++ * Revision 1.12 1998/11/13 13:20:25 rgb
++ * Fixed ntohs bug in udp/500 hole for IKE.
++ *
++ * Revision 1.11 1998/11/10 08:01:19 rgb
++ * Kill tcp/500 hole, keep udp/500 hole.
++ *
++ * Revision 1.10 1998/11/09 21:29:26 rgb
++ * If no eroute is found, discard packet and incr. tx_error.
++ *
++ * Revision 1.9 1998/10/31 06:50:00 rgb
++ * Add tcp/udp/500 bypass.
++ * Fixed up comments in #endif directives.
++ *
++ * Revision 1.8 1998/10/27 00:34:31 rgb
++ * Reformat debug output of IP headers.
++ * Newlines added before calls to ipsec_print_ip.
++ *
++ * Revision 1.7 1998/10/19 14:44:28 rgb
++ * Added inclusion of freeswan.h.
++ * sa_id structure implemented and used: now includes protocol.
++ *
++ * Revision 1.6 1998/10/09 04:31:35 rgb
++ * Added 'klips_debug' prefix to all klips printk debug statements.
++ *
++ * Revision 1.5 1998/08/28 03:09:51 rgb
++ * Prevent kernel log spam with default route through ipsec.
++ *
++ * Revision 1.4 1998/08/05 22:23:09 rgb
++ * Change setdev return code to ENXIO for a non-existant physical device.
++ *
++ * Revision 1.3 1998/07/29 20:41:11 rgb
++ * Add ipsec_tunnel_clear to clear all tunnel attachments.
++ *
++ * Revision 1.2 1998/06/25 20:00:33 rgb
++ * Clean up #endif comments.
++ * Rename dev_ipsec to dev_ipsec0 for consistency.
++ * Document ipsec device fields.
++ * Make ipsec_tunnel_probe visible from rest of kernel for static linking.
++ * Get debugging report for *every* ipsec device initialisation.
++ * Comment out redundant code.
++ *
++ * Revision 1.1 1998/06/18 21:27:50 henry
++ * move sources from klips/src to klips/net/ipsec, to keep stupid
++ * kernel-build scripts happier in the presence of symlinks
++ *
++ * Revision 1.8 1998/06/14 23:49:40 rgb
++ * Clarify version reporting on module loading.
++ *
++ * Revision 1.7 1998/05/27 23:19:20 rgb
++ * Added version reporting.
++ *
++ * Revision 1.6 1998/05/18 21:56:23 rgb
++ * Clean up for numerical consistency of output and cleaning up debug code.
++ *
++ * Revision 1.5 1998/05/12 02:44:23 rgb
++ * Clarifying 'no e-route to host' message.
++ *
++ * Revision 1.4 1998/04/30 15:34:35 rgb
++ * Enclosed most remaining debugging statements in #ifdef's to make it quieter.
++ *
++ * Revision 1.3 1998/04/21 21:28:54 rgb
++ * Rearrange debug switches to change on the fly debug output from user
++ * space. Only kernel changes checked in at this time. radij.c was also
++ * changed to temporarily remove buggy debugging code in rj_delete causing
++ * an OOPS and hence, netlink device open errors.
++ *
++ * Revision 1.2 1998/04/12 22:03:24 rgb
++ * Updated ESP-3DES-HMAC-MD5-96,
++ * ESP-DES-HMAC-MD5-96,
++ * AH-HMAC-MD5-96,
++ * AH-HMAC-SHA1-96 since Henry started freeswan cvs repository
++ * from old standards (RFC182[5-9] to new (as of March 1998) drafts.
++ *
++ * Fixed eroute references in /proc/net/ipsec*.
++ *
++ * Started to patch module unloading memory leaks in ipsec_netlink and
++ * radij tree unloading.
++ *
++ * Revision 1.1 1998/04/09 03:06:12 henry
++ * sources moved up from linux/net/ipsec
++ *
++ * Revision 1.1.1.1 1998/04/08 05:35:04 henry
++ * RGB's ipsec-0.8pre2.tar.gz ipsec-0.8
++ *
++ * Revision 0.5 1997/06/03 04:24:48 ji
++ * Added transport mode.
++ * Changed the way routing is done.
++ * Lots of bug fixes.
++ *
++ * Revision 0.4 1997/01/15 01:28:15 ji
++ * No changes.
++ *
++ * Revision 0.3 1996/11/20 14:39:04 ji
++ * Minor cleanups.
++ * Rationalized debugging code.
++ *
++ * Revision 0.2 1996/11/02 00:18:33 ji
++ * First limited release.
++ *
++ * Local Variables:
++ * c-style: linux
++ * End:
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/net/ipsec/ipsec_xform.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,351 @@
++/*
++ * Common routines for IPSEC transformations.
++ * Copyright (C) 1996, 1997 John Ioannidis.
++ * Copyright (C) 1998, 1999, 2000, 2001 Richard Guy Briggs.
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ * RCSID $Id$
++ */
++
++#include <linux/config.h>
++#include <linux/version.h>
++#include <linux/kernel.h> /* printk() */
++
++#include "freeswan/ipsec_param.h"
++
++#ifdef MALLOC_SLAB
++# include <linux/slab.h> /* kmalloc() */
++#else /* MALLOC_SLAB */
++# include <linux/malloc.h> /* kmalloc() */
++#endif /* MALLOC_SLAB */
++#include <linux/errno.h> /* error codes */
++#include <linux/types.h> /* size_t */
++#include <linux/interrupt.h> /* mark_bh */
++
++#include <linux/netdevice.h> /* struct device, and other headers */
++#include <linux/etherdevice.h> /* eth_type_trans */
++#include <linux/ip.h> /* struct iphdr */
++#include <linux/skbuff.h>
++#include <linux/random.h> /* get_random_bytes() */
++#include <freeswan.h>
++#ifdef SPINLOCK
++# ifdef SPINLOCK_23
++# include <linux/spinlock.h> /* *lock* */
++# else /* SPINLOCK_23 */
++# include <asm/spinlock.h> /* *lock* */
++# endif /* SPINLOCK_23 */
++#endif /* SPINLOCK */
++#ifdef NET_21
++# include <asm/uaccess.h>
++# include <linux/in6.h>
++#endif
++#include <asm/checksum.h>
++#include <net/ip.h>
++
++#include "freeswan/radij.h"
++#include "freeswan/ipsec_encap.h"
++#include "freeswan/ipsec_radij.h"
++#include "freeswan/ipsec_xform.h"
++#include "freeswan/ipsec_ipe4.h"
++#include "freeswan/ipsec_ah.h"
++#include "freeswan/ipsec_esp.h"
++
++#include <pfkeyv2.h>
++#include <pfkey.h>
++
++#ifdef CONFIG_IPSEC_DEBUG
++int debug_xform = 0;
++#endif /* CONFIG_IPSEC_DEBUG */
++
++#ifdef SPINLOCK
++spinlock_t tdb_lock = SPIN_LOCK_UNLOCKED;
++#else /* SPINLOCK */
++spinlock_t tdb_lock;
++#endif /* SPINLOCK */
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.63 2003/10/31 02:27:55 mcr
++ * pulled up port-selector patches and sa_id elimination.
++ *
++ * Revision 1.62.30.1 2003/10/29 01:30:41 mcr
++ * elimited "struct sa_id".
++ *
++ * Revision 1.62 2002/05/14 02:34:21 rgb
++ * Delete stale code.
++ *
++ * Revision 1.61 2002/04/24 07:55:32 mcr
++ * #include patches and Makefiles for post-reorg compilation.
++ *
++ * Revision 1.60 2002/04/24 07:36:33 mcr
++ * Moved from ./klips/net/ipsec/ipsec_xform.c,v
++ *
++ * Revision 1.59 2002/03/29 15:01:36 rgb
++ * Delete decommissioned code.
++ *
++ * Revision 1.58 2002/01/29 17:17:57 mcr
++ * moved include of ipsec_param.h to after include of linux/kernel.h
++ * otherwise, it seems that some option that is set in ipsec_param.h
++ * screws up something subtle in the include path to kernel.h, and
++ * it complains on the snprintf() prototype.
++ *
++ * Revision 1.57 2002/01/29 04:00:53 mcr
++ * more excise of kversions.h header.
++ *
++ * Revision 1.56 2001/11/27 05:17:22 mcr
++ * turn off the worst of the per-packet debugging.
++ *
++ * Revision 1.55 2001/11/26 09:23:50 rgb
++ * Merge MCR's ipsec_sa, eroute, proc and struct lifetime changes.
++ *
++ * Revision 1.54 2001/10/18 04:45:21 rgb
++ * 2.4.9 kernel deprecates linux/malloc.h in favour of linux/slab.h,
++ * lib/freeswan.h version macros moved to lib/kversions.h.
++ * Other compiler directive cleanups.
++ *
++ * Revision 1.53 2001/09/08 21:13:34 rgb
++ * Added pfkey ident extension support for ISAKMPd. (NetCelo)
++ *
++ * Revision 1.52 2001/06/14 19:35:11 rgb
++ * Update copyright date.
++ *
++ * Revision 1.51 2001/05/30 08:14:03 rgb
++ * Removed vestiges of esp-null transforms.
++ *
++ * Revision 1.50 2001/05/03 19:43:18 rgb
++ * Initialise error return variable.
++ * Update SENDERR macro.
++ * Fix sign of error return code for ipsec_tdbcleanup().
++ * Use more appropriate return code for ipsec_tdbwipe().
++ *
++ * Revision 1.49 2001/04/19 18:56:17 rgb
++ * Fixed tdb table locking comments.
++ *
++ * Revision 1.48 2001/02/27 22:24:55 rgb
++ * Re-formatting debug output (line-splitting, joining, 1arg/line).
++ * Check for satoa() return codes.
++ *
++ * Revision 1.47 2000/11/06 04:32:08 rgb
++ * Ditched spin_lock_irqsave in favour of spin_lock_bh.
++ *
++ * Revision 1.46 2000/09/20 16:21:57 rgb
++ * Cleaned up ident string alloc/free.
++ *
++ * Revision 1.45 2000/09/08 19:16:51 rgb
++ * Change references from DEBUG_IPSEC to CONFIG_IPSEC_DEBUG.
++ * Removed all references to CONFIG_IPSEC_PFKEYv2.
++ *
++ * Revision 1.44 2000/08/30 05:29:04 rgb
++ * Compiler-define out no longer used tdb_init() in ipsec_xform.c.
++ *
++ * Revision 1.43 2000/08/18 21:30:41 rgb
++ * Purged all tdb_spi, tdb_proto and tdb_dst macros. They are unclear.
++ *
++ * Revision 1.42 2000/08/01 14:51:51 rgb
++ * Removed _all_ remaining traces of DES.
++ *
++ * Revision 1.41 2000/07/28 14:58:31 rgb
++ * Changed kfree_s to kfree, eliminating extra arg to fix 2.4.0-test5.
++ *
++ * Revision 1.40 2000/06/28 05:50:11 rgb
++ * Actually set iv_bits.
++ *
++ * Revision 1.39 2000/05/10 23:11:09 rgb
++ * Added netlink debugging output.
++ * Added a cast to quiet down the ntohl bug.
++ *
++ * Revision 1.38 2000/05/10 19:18:42 rgb
++ * Cast output of ntohl so that the broken prototype doesn't make our
++ * compile noisy.
++ *
++ * Revision 1.37 2000/03/16 14:04:59 rgb
++ * Hardwired CONFIG_IPSEC_PFKEYv2 on.
++ *
++ * Revision 1.36 2000/01/26 10:11:28 rgb
++ * Fixed spacing in error text causing run-in words.
++ *
++ * Revision 1.35 2000/01/21 06:17:16 rgb
++ * Tidied up compiler directive indentation for readability.
++ * Added ictx,octx vars for simplification.(kravietz)
++ * Added macros for HMAC padding magic numbers.(kravietz)
++ * Fixed missing key length reporting bug.
++ * Fixed bug in tdbwipe to return immediately on NULL tdbp passed in.
++ *
++ * Revision 1.34 1999/12/08 00:04:19 rgb
++ * Fixed SA direction overwriting bug for netlink users.
++ *
++ * Revision 1.33 1999/12/01 22:16:44 rgb
++ * Minor formatting changes in ESP MD5 initialisation.
++ *
++ * Revision 1.32 1999/11/25 09:06:36 rgb
++ * Fixed error return messages, should be returning negative numbers.
++ * Implemented SENDERR macro for propagating error codes.
++ * Added debug message and separate error code for algorithms not compiled
++ * in.
++ *
++ * Revision 1.31 1999/11/23 23:06:26 rgb
++ * Sort out pfkey and freeswan headers, putting them in a library path.
++ *
++ * Revision 1.30 1999/11/18 04:09:20 rgb
++ * Replaced all kernel version macros to shorter, readable form.
++ *
++ * Revision 1.29 1999/11/17 15:53:40 rgb
++ * Changed all occurrences of #include "../../../lib/freeswan.h"
++ * to #include <freeswan.h> which works due to -Ilibfreeswan in the
++ * klips/net/ipsec/Makefile.
++ *
++ * Revision 1.28 1999/10/18 20:04:01 rgb
++ * Clean-out unused cruft.
++ *
++ * Revision 1.27 1999/10/03 19:01:03 rgb
++ * Spinlock support for 2.3.xx and 2.0.xx kernels.
++ *
++ * Revision 1.26 1999/10/01 16:22:24 rgb
++ * Switch from assignment init. to functional init. of spinlocks.
++ *
++ * Revision 1.25 1999/10/01 15:44:54 rgb
++ * Move spinlock header include to 2.1> scope.
++ *
++ * Revision 1.24 1999/10/01 00:03:46 rgb
++ * Added tdb structure locking.
++ * Minor formatting changes.
++ * Add function to initialize tdb hash table.
++ *
++ * Revision 1.23 1999/05/25 22:42:12 rgb
++ * Add deltdbchain() debugging.
++ *
++ * Revision 1.22 1999/05/25 21:24:31 rgb
++ * Add debugging statements to deltdbchain().
++ *
++ * Revision 1.21 1999/05/25 03:51:48 rgb
++ * Refix error return code.
++ *
++ * Revision 1.20 1999/05/25 03:34:07 rgb
++ * Fix error return for flush.
++ *
++ * Revision 1.19 1999/05/09 03:25:37 rgb
++ * Fix bug introduced by 2.2 quick-and-dirty patch.
++ *
++ * Revision 1.18 1999/05/05 22:02:32 rgb
++ * Add a quick and dirty port to 2.2 kernels by Marc Boucher <marc@mbsi.ca>.
++ *
++ * Revision 1.17 1999/04/29 15:20:16 rgb
++ * Change gettdb parameter to a pointer to reduce stack loading and
++ * facilitate parameter sanity checking.
++ * Add sanity checking for null pointer arguments.
++ * Add debugging instrumentation.
++ * Add function deltdbchain() which will take care of unlinking,
++ * zeroing and deleting a chain of tdbs.
++ * Add a parameter to tdbcleanup to be able to delete a class of SAs.
++ * tdbwipe now actually zeroes the tdb as well as any of its pointed
++ * structures.
++ *
++ * Revision 1.16 1999/04/16 15:36:29 rgb
++ * Fix cut-and-paste error causing a memory leak in IPIP TDB freeing.
++ *
++ * Revision 1.15 1999/04/11 00:29:01 henry
++ * GPL boilerplate
++ *
++ * Revision 1.14 1999/04/06 04:54:28 rgb
++ * Fix/Add RCSID Id: and Log: bits to make PHMDs happy. This includes
++ * patch shell fixes.
++ *
++ * Revision 1.13 1999/02/19 18:23:01 rgb
++ * Nix debug off compile warning.
++ *
++ * Revision 1.12 1999/02/17 16:52:16 rgb
++ * Consolidate satoa()s for space and speed efficiency.
++ * Convert DEBUG_IPSEC to KLIPS_PRINT
++ * Clean out unused cruft.
++ * Ditch NET_IPIP dependancy.
++ * Loop for 3des key setting.
++ *
++ * Revision 1.11 1999/01/26 02:09:05 rgb
++ * Remove ah/esp/IPIP switching on include files.
++ * Removed CONFIG_IPSEC_ALGO_SWITCH macro.
++ * Removed dead code.
++ * Clean up debug code when switched off.
++ * Remove references to INET_GET_PROTOCOL.
++ * Added code exclusion macros to reduce code from unused algorithms.
++ *
++ * Revision 1.10 1999/01/22 06:28:55 rgb
++ * Cruft clean-out.
++ * Put random IV generation in kernel.
++ * Added algorithm switch code.
++ * Enhanced debugging.
++ * 64-bit clean-up.
++ *
++ * Revision 1.9 1998/11/30 13:22:55 rgb
++ * Rationalised all the klips kernel file headers. They are much shorter
++ * now and won't conflict under RH5.2.
++ *
++ * Revision 1.8 1998/11/25 04:59:06 rgb
++ * Add conditionals for no IPIP tunnel code.
++ * Delete commented out code.
++ *
++ * Revision 1.7 1998/10/31 06:50:41 rgb
++ * Convert xform ASCII names to no spaces.
++ * Fixed up comments in #endif directives.
++ *
++ * Revision 1.6 1998/10/19 14:44:28 rgb
++ * Added inclusion of freeswan.h.
++ * sa_id structure implemented and used: now includes protocol.
++ *
++ * Revision 1.5 1998/10/09 04:32:19 rgb
++ * Added 'klips_debug' prefix to all klips printk debug statements.
++ *
++ * Revision 1.4 1998/08/12 00:11:31 rgb
++ * Added new xform functions to the xform table.
++ * Fixed minor debug output spelling error.
++ *
++ * Revision 1.3 1998/07/09 17:45:31 rgb
++ * Clarify algorithm not available message.
++ *
++ * Revision 1.2 1998/06/23 03:00:51 rgb
++ * Check for presence of IPIP protocol if it is setup one way (we don't
++ * know what has been set up the other way and can only assume it will be
++ * symmetrical with the exception of keys).
++ *
++ * Revision 1.1 1998/06/18 21:27:51 henry
++ * move sources from klips/src to klips/net/ipsec, to keep stupid
++ * kernel-build scripts happier in the presence of symlinks
++ *
++ * Revision 1.3 1998/06/11 05:54:59 rgb
++ * Added transform version string pointer to xformsw initialisations.
++ *
++ * Revision 1.2 1998/04/21 21:28:57 rgb
++ * Rearrange debug switches to change on the fly debug output from user
++ * space. Only kernel changes checked in at this time. radij.c was also
++ * changed to temporarily remove buggy debugging code in rj_delete causing
++ * an OOPS and hence, netlink device open errors.
++ *
++ * Revision 1.1 1998/04/09 03:06:13 henry
++ * sources moved up from linux/net/ipsec
++ *
++ * Revision 1.1.1.1 1998/04/08 05:35:02 henry
++ * RGB's ipsec-0.8pre2.tar.gz ipsec-0.8
++ *
++ * Revision 0.5 1997/06/03 04:24:48 ji
++ * Added ESP-3DES-MD5-96
++ *
++ * Revision 0.4 1997/01/15 01:28:15 ji
++ * Added new transforms.
++ *
++ * Revision 0.3 1996/11/20 14:39:04 ji
++ * Minor cleanups.
++ * Rationalized debugging code.
++ *
++ * Revision 0.2 1996/11/02 00:18:33 ji
++ * First limited release.
++ *
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/net/ipsec/ipsec_xmit.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,1869 @@
++/*
++ * IPSEC Transmit code.
++ * Copyright (C) 1996, 1997 John Ioannidis.
++ * Copyright (C) 1998-2003 Richard Guy Briggs.
++ * Copyright (C) 2004 Michael Richardson <mcr@xelerance.com>
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ */
++
++char ipsec_xmit_c_version[] = "RCSID $Id$";
++
++#define __NO_VERSION__
++#include <linux/module.h>
++#include <linux/config.h> /* for CONFIG_IP_FORWARD */
++#include <linux/version.h>
++#include <linux/kernel.h> /* printk() */
++
++#include "openswan/ipsec_param.h"
++
++#ifdef MALLOC_SLAB
++# include <linux/slab.h> /* kmalloc() */
++#else /* MALLOC_SLAB */
++# include <linux/malloc.h> /* kmalloc() */
++#endif /* MALLOC_SLAB */
++#include <linux/errno.h> /* error codes */
++#include <linux/types.h> /* size_t */
++#include <linux/interrupt.h> /* mark_bh */
++
++#include <linux/netdevice.h> /* struct device, struct net_device_stats, dev_queue_xmit() and other headers */
++#include <linux/etherdevice.h> /* eth_type_trans */
++#include <linux/ip.h> /* struct iphdr */
++#include <linux/tcp.h> /* struct tcphdr */
++#include <linux/udp.h> /* struct udphdr */
++#include <linux/skbuff.h>
++#include <openswan.h>
++#ifdef NET_21
++# define MSS_HACK_ /* experimental */
++# include <asm/uaccess.h>
++# include <linux/in6.h>
++# include <net/dst.h>
++# define proto_priv cb
++#endif /* NET_21 */
++#include <asm/checksum.h>
++#include <net/icmp.h> /* icmp_send() */
++#include <net/ip.h>
++#ifdef NETDEV_23
++# include <linux/netfilter_ipv4.h>
++#endif /* NETDEV_23 */
++
++#include <linux/if_arp.h>
++#ifdef MSS_HACK
++# include <net/tcp.h> /* TCP options */
++#endif /* MSS_HACK */
++
++#include "openswan/radij.h"
++#include "openswan/ipsec_life.h"
++#include "openswan/ipsec_xform.h"
++#include "openswan/ipsec_eroute.h"
++#include "openswan/ipsec_encap.h"
++#include "openswan/ipsec_radij.h"
++#include "openswan/ipsec_xmit.h"
++#include "openswan/ipsec_sa.h"
++#include "openswan/ipsec_tunnel.h"
++#include "openswan/ipsec_ipe4.h"
++#include "openswan/ipsec_ah.h"
++#include "openswan/ipsec_esp.h"
++
++#ifdef CONFIG_IPSEC_IPCOMP
++#include "openswan/ipcomp.h"
++#endif /* CONFIG_IPSEC_IPCOMP */
++
++#include <pfkeyv2.h>
++#include <pfkey.h>
++
++#include "openswan/ipsec_proto.h"
++#include "openswan/ipsec_alg.h"
++
++
++/*
++ * Stupid kernel API differences in APIs. Not only do some
++ * kernels not have ip_select_ident, but some have differing APIs,
++ * and SuSE has one with one parameter, but no way of checking to
++ * see what is really what.
++ */
++
++#ifdef SUSE_LINUX_2_4_19_IS_STUPID
++#define KLIPS_IP_SELECT_IDENT(iph, skb) ip_select_ident(iph)
++#else
++
++/* simplest case, nothing */
++#if !defined(IP_SELECT_IDENT)
++#define KLIPS_IP_SELECT_IDENT(iph, skb) do { iph->id = htons(ip_id_count++); } while(0)
++#endif
++
++/* kernels > 2.3.37-ish */
++#if defined(IP_SELECT_IDENT) && !defined(IP_SELECT_IDENT_NEW)
++#define KLIPS_IP_SELECT_IDENT(iph, skb) ip_select_ident(iph, skb->dst)
++#endif
++
++/* kernels > 2.4.2 */
++#if defined(IP_SELECT_IDENT) && defined(IP_SELECT_IDENT_NEW)
++#define KLIPS_IP_SELECT_IDENT(iph, skb) ip_select_ident(iph, skb->dst, NULL)
++#endif
++
++#endif /* SUSE_LINUX_2_4_19_IS_STUPID */
++
++
++static __u32 zeroes[64];
++
++#ifdef CONFIG_IPSEC_DEBUG
++int sysctl_ipsec_debug_verbose = 0;
++#endif /* CONFIG_IPSEC_DEBUG */
++
++int ipsec_xmit_trap_count = 0;
++int ipsec_xmit_trap_sendcount = 0;
++
++int sysctl_ipsec_icmp = 0;
++int sysctl_ipsec_tos = 0;
++
++#ifdef CONFIG_IPSEC_DEBUG_
++DEBUG_NO_STATIC void
++dmp(char *s, caddr_t bb, int len)
++{
++ int i;
++ unsigned char *b = bb;
++
++ if (debug_tunnel) {
++ printk(KERN_INFO "klips_debug:ipsec_tunnel_:dmp: "
++ "at %s, len=%d:",
++ s,
++ len);
++ for (i=0; i < len; i++) {
++ if(!(i%16)){
++ printk("\nklips_debug: ");
++ }
++ printk(" %02x", *b++);
++ }
++ printk("\n");
++ }
++}
++#else /* CONFIG_IPSEC_DEBUG */
++#define dmp(_x, _y, _z)
++#endif /* CONFIG_IPSEC_DEBUG */
++
++#ifndef SKB_COPY_EXPAND
++/*
++ * This is mostly skbuff.c:skb_copy().
++ */
++struct sk_buff *
++skb_copy_expand(struct sk_buff *skb, int headroom, int tailroom, int priority)
++{
++ struct sk_buff *n;
++ unsigned long offset;
++
++ /*
++ * Do sanity checking
++ */
++ if((headroom < 0) || (tailroom < 0) || ((headroom+tailroom) < 0)) {
++ printk(KERN_WARNING
++ "klips_error:skb_copy_expand: "
++ "Illegal negative head,tailroom %d,%d\n",
++ headroom,
++ tailroom);
++ return NULL;
++ }
++ /*
++ * Allocate the copy buffer
++ */
++
++#ifndef NET_21
++ IS_SKB(skb);
++#endif /* !NET_21 */
++
++
++ n=alloc_skb(skb->end - skb->head + headroom + tailroom, priority);
++
++ KLIPS_PRINT(debug_tunnel & DB_TN_CROUT,
++ "klips_debug:skb_copy_expand: "
++ "allocating %d bytes, head=0p%p data=0p%p tail=0p%p end=0p%p end-head=%d tail-data=%d\n",
++ skb->end - skb->head + headroom + tailroom,
++ skb->head,
++ skb->data,
++ skb->tail,
++ skb->end,
++ skb->end - skb->head,
++ skb->tail - skb->data);
++
++ if(n==NULL)
++ return NULL;
++
++ /*
++ * Shift between the two data areas in bytes
++ */
++
++ /* Set the data pointer */
++ skb_reserve(n,skb->data-skb->head+headroom);
++ /* Set the tail pointer and length */
++ if(skb_tailroom(n) < skb->len) {
++ printk(KERN_WARNING "klips_error:skb_copy_expand: "
++ "tried to skb_put %ld, %d available. This should never happen, please report.\n",
++ (unsigned long int)skb->len,
++ skb_tailroom(n));
++ ipsec_kfree_skb(n);
++ return NULL;
++ }
++ skb_put(n,skb->len);
++
++ offset=n->head + headroom - skb->head;
++
++ /* Copy the bytes */
++ memcpy(n->head + headroom, skb->head,skb->end-skb->head);
++#ifdef NET_21
++ n->csum=skb->csum;
++ n->priority=skb->priority;
++ n->dst=dst_clone(skb->dst);
++ if(skb->nh.raw)
++ n->nh.raw=skb->nh.raw+offset;
++#ifndef NETDEV_23
++ n->is_clone=0;
++#endif /* NETDEV_23 */
++ atomic_set(&n->users, 1);
++ n->destructor = NULL;
++ n->security=skb->security;
++#else /* NET_21 */
++ n->link3=NULL;
++ n->when=skb->when;
++ if(skb->ip_hdr)
++ n->ip_hdr=(struct iphdr *)(((char *)skb->ip_hdr)+offset);
++ n->saddr=skb->saddr;
++ n->daddr=skb->daddr;
++ n->raddr=skb->raddr;
++ n->seq=skb->seq;
++ n->end_seq=skb->end_seq;
++ n->ack_seq=skb->ack_seq;
++ n->acked=skb->acked;
++ n->free=1;
++ n->arp=skb->arp;
++ n->tries=0;
++ n->lock=0;
++ n->users=0;
++#endif /* NET_21 */
++ n->protocol=skb->protocol;
++ n->list=NULL;
++ n->sk=NULL;
++ n->dev=skb->dev;
++ if(skb->h.raw)
++ n->h.raw=skb->h.raw+offset;
++ if(skb->mac.raw)
++ n->mac.raw=skb->mac.raw+offset;
++ memcpy(n->proto_priv, skb->proto_priv, sizeof(skb->proto_priv));
++#ifndef NETDEV_23
++ n->used=skb->used;
++#endif /* !NETDEV_23 */
++ n->pkt_type=skb->pkt_type;
++ n->stamp=skb->stamp;
++
++#ifndef NET_21
++ IS_SKB(n);
++#endif /* !NET_21 */
++ return n;
++}
++#endif /* !SKB_COPY_EXPAND */
++
++#ifdef CONFIG_IPSEC_DEBUG
++void
++ipsec_print_ip(struct iphdr *ip)
++{
++ char buf[ADDRTOA_BUF];
++
++ printk(KERN_INFO "klips_debug: IP:");
++ printk(" ihl:%d", ip->ihl << 2);
++ printk(" ver:%d", ip->version);
++ printk(" tos:%d", ip->tos);
++ printk(" tlen:%d", ntohs(ip->tot_len));
++ printk(" id:%d", ntohs(ip->id));
++ printk(" %s%s%sfrag_off:%d",
++ ip->frag_off & __constant_htons(IP_CE) ? "CE " : "",
++ ip->frag_off & __constant_htons(IP_DF) ? "DF " : "",
++ ip->frag_off & __constant_htons(IP_MF) ? "MF " : "",
++ (ntohs(ip->frag_off) & IP_OFFSET) << 3);
++ printk(" ttl:%d", ip->ttl);
++ printk(" proto:%d", ip->protocol);
++ if(ip->protocol == IPPROTO_UDP)
++ printk(" (UDP)");
++ if(ip->protocol == IPPROTO_TCP)
++ printk(" (TCP)");
++ if(ip->protocol == IPPROTO_ICMP)
++ printk(" (ICMP)");
++ printk(" chk:%d", ntohs(ip->check));
++ addrtoa(*((struct in_addr*)(&ip->saddr)), 0, buf, sizeof(buf));
++ printk(" saddr:%s", buf);
++ if(ip->protocol == IPPROTO_UDP)
++ printk(":%d",
++ ntohs(((struct udphdr*)((caddr_t)ip + (ip->ihl << 2)))->source));
++ if(ip->protocol == IPPROTO_TCP)
++ printk(":%d",
++ ntohs(((struct tcphdr*)((caddr_t)ip + (ip->ihl << 2)))->source));
++ addrtoa(*((struct in_addr*)(&ip->daddr)), 0, buf, sizeof(buf));
++ printk(" daddr:%s", buf);
++ if(ip->protocol == IPPROTO_UDP)
++ printk(":%d",
++ ntohs(((struct udphdr*)((caddr_t)ip + (ip->ihl << 2)))->dest));
++ if(ip->protocol == IPPROTO_TCP)
++ printk(":%d",
++ ntohs(((struct tcphdr*)((caddr_t)ip + (ip->ihl << 2)))->dest));
++ if(ip->protocol == IPPROTO_ICMP)
++ printk(" type:code=%d:%d",
++ ((struct icmphdr*)((caddr_t)ip + (ip->ihl << 2)))->type,
++ ((struct icmphdr*)((caddr_t)ip + (ip->ihl << 2)))->code);
++ printk("\n");
++
++ if(sysctl_ipsec_debug_verbose) {
++ __u8 *c;
++ int i;
++
++ c = ((__u8*)ip) + ip->ihl*4;
++ for(i = 0; i < ntohs(ip->tot_len) - ip->ihl*4; i++ /*, c++*/) {
++ if(!(i % 16)) {
++ printk(KERN_INFO
++ "klips_debug: @%03x:",
++ i);
++ }
++ printk(" %02x", /***/c[i]);
++ if(!((i + 1) % 16)) {
++ printk("\n");
++ }
++ }
++ if(i % 16) {
++ printk("\n");
++ }
++ }
++}
++#endif /* CONFIG_IPSEC_DEBUG */
++
++#ifdef MSS_HACK
++/*
++ * Issues:
++ * 1) Fragments arriving in the tunnel should probably be rejected.
++ * 2) How does this affect syncookies, mss_cache, dst cache ?
++ * 3) Path MTU discovery handling needs to be reviewed. For example,
++ * if we receive an ICMP 'packet too big' message from an intermediate
++ * router specifying it's next hop MTU, our stack may process this and
++ * adjust the MSS without taking our AH/ESP overheads into account.
++ */
++
++
++/*
++ * Recaclulate checksum using differences between changed datum,
++ * borrowed from netfilter.
++ */
++DEBUG_NO_STATIC u_int16_t
++ipsec_fast_csum(u_int32_t oldvalinv, u_int32_t newval, u_int16_t oldcheck)
++{
++ u_int32_t diffs[] = { oldvalinv, newval };
++ return csum_fold(csum_partial((char *)diffs, sizeof(diffs),
++ oldcheck^0xFFFF));
++}
++
++/*
++ * Determine effective MSS.
++ *
++ * Note that we assume that there is always an MSS option for our own
++ * SYN segments, which is mentioned in tcp_syn_build_options(), kernel 2.2.x.
++ * This could change, and we should probably parse TCP options instead.
++ *
++ */
++DEBUG_NO_STATIC u_int8_t
++ipsec_adjust_mss(struct sk_buff *skb, struct tcphdr *tcph, u_int16_t mtu)
++{
++ u_int16_t oldmss, newmss;
++ u_int32_t *mssp;
++ struct sock *sk = skb->sk;
++
++ newmss = tcp_sync_mss(sk, mtu);
++ printk(KERN_INFO "klips: setting mss to %u\n", newmss);
++ mssp = (u_int32_t *)tcph + sizeof(struct tcphdr) / sizeof(u_int32_t);
++ oldmss = ntohl(*mssp) & 0x0000FFFF;
++ *mssp = htonl((TCPOPT_MSS << 24) | (TCPOLEN_MSS << 16) | newmss);
++ tcph->check = ipsec_fast_csum(htons(~oldmss),
++ htons(newmss), tcph->check);
++ return 1;
++}
++#endif /* MSS_HACK */
++
++/*
++ * Sanity checks
++ */
++enum ipsec_xmit_value
++ipsec_xmit_sanity_check_dev(struct ipsec_xmit_state *ixs)
++{
++
++ if (ixs->dev == NULL) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
++ "klips_error:ipsec_xmit_sanity_check_dev: "
++ "No device associated with skb!\n" );
++ return IPSEC_XMIT_NODEV;
++ }
++
++ ixs->prv = ixs->dev->priv;
++ if (ixs->prv == NULL) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
++ "klips_error:ipsec_xmit_sanity_check_dev: "
++ "Device has no private structure!\n" );
++ return IPSEC_XMIT_NOPRIVDEV;
++ }
++
++ ixs->physdev = ixs->prv->dev;
++ if (ixs->physdev == NULL) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
++ "klips_error:ipsec_xmit_sanity_check_dev: "
++ "Device is not attached to physical device!\n" );
++ return IPSEC_XMIT_NOPHYSDEV;
++ }
++
++ ixs->physmtu = ixs->physdev->mtu;
++
++ ixs->stats = (struct net_device_stats *) &(ixs->prv->mystats);
++
++ return IPSEC_XMIT_OK;
++}
++
++enum ipsec_xmit_value
++ipsec_xmit_sanity_check_skb(struct ipsec_xmit_state *ixs)
++{
++ /*
++ * Return if there is nothing to do. (Does this ever happen?) XXX
++ */
++ if (ixs->skb == NULL) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
++ "klips_error:ipsec_xmit_sanity_check_skb: "
++ "Nothing to do!\n" );
++ return IPSEC_XMIT_NOSKB;
++ }
++#ifdef NET_21
++ /* if skb was cloned (most likely due to a packet sniffer such as
++ tcpdump being momentarily attached to the interface), make
++ a copy of our own to modify */
++ if(skb_cloned(ixs->skb)) {
++ if
++#ifdef SKB_COW_NEW
++ (skb_cow(ixs->skb, skb_headroom(ixs->skb)) != 0)
++#else /* SKB_COW_NEW */
++ ((ixs->skb = skb_cow(ixs->skb, skb_headroom(ixs->skb))) == NULL)
++#endif /* SKB_COW_NEW */
++ {
++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
++ "klips_error:ipsec_xmit_sanity_check_skb: "
++ "skb_cow failed to allocate buffer, dropping.\n" );
++ ixs->stats->tx_dropped++;
++ return IPSEC_XMIT_ERRSKBALLOC;
++ }
++ }
++#endif /* NET_21 */
++
++#ifdef NET_21
++ ixs->iph = ixs->skb->nh.iph;
++#else /* NET_21 */
++ ixs->iph = ixs->skb->ip_hdr;
++#endif /* NET_21 */
++
++ /* sanity check for IP version as we can't handle IPv6 right now */
++ if (ixs->iph->version != 4) {
++ KLIPS_PRINT(debug_tunnel,
++ "klips_debug:ipsec_xmit_sanity_check_skb: "
++ "found IP Version %d but cannot process other IP versions than v4.\n",
++ ixs->iph->version); /* XXX */
++ ixs->stats->tx_dropped++;
++ return IPSEC_XMIT_NOIPV6;
++ }
++
++#if IPSEC_DISALLOW_IPOPTIONS
++ if ((ixs->iph->ihl << 2) != sizeof (struct iphdr)) {
++ KLIPS_PRINT(debug_tunnel,
++ "klips_debug:ipsec_xmit_sanity_check_skb: "
++ "cannot process IP header options yet. May be mal-formed packet.\n"); /* XXX */
++ ixs->stats->tx_dropped++;
++ return IPSEC_XMIT_NOIPOPTIONS;
++ }
++#endif /* IPSEC_DISALLOW_IPOPTIONS */
++
++#ifndef NET_21
++ if (ixs->iph->ttl <= 0) {
++ /* Tell the sender its packet died... */
++ ICMP_SEND(ixs->skb, ICMP_TIME_EXCEEDED, ICMP_EXC_TTL, 0, ixs->physdev);
++
++ KLIPS_PRINT(debug_tunnel, "klips_debug:ipsec_xmit_sanity_check_skb: "
++ "TTL=0, too many hops!\n");
++ ixs->stats->tx_dropped++;
++ return IPSEC_XMIT_TTLEXPIRED;
++ }
++#endif /* !NET_21 */
++
++ return IPSEC_XMIT_OK;
++}
++
++enum ipsec_xmit_value
++ipsec_xmit_encap_once(struct ipsec_xmit_state *ixs)
++{
++#ifdef CONFIG_IPSEC_ESP
++ struct esphdr *espp;
++#ifdef CONFIG_IPSEC_ENC_3DES
++ __u32 iv[ESP_IV_MAXSZ_INT];
++#endif /* !CONFIG_IPSEC_ENC_3DES */
++ unsigned char *idat, *pad;
++ int authlen = 0, padlen = 0, i;
++#endif /* !CONFIG_IPSEC_ESP */
++#ifdef CONFIG_IPSEC_AH
++ struct iphdr ipo;
++ struct ahhdr *ahp;
++#endif /* CONFIG_IPSEC_AH */
++#if defined(CONFIG_IPSEC_AUTH_HMAC_MD5) || defined(CONFIG_IPSEC_AUTH_HMAC_SHA1)
++ union {
++#ifdef CONFIG_IPSEC_AUTH_HMAC_MD5
++ MD5_CTX md5;
++#endif /* CONFIG_IPSEC_AUTH_HMAC_MD5 */
++#ifdef CONFIG_IPSEC_AUTH_HMAC_SHA1
++ SHA1_CTX sha1;
++#endif /* CONFIG_IPSEC_AUTH_HMAC_SHA1 */
++ } tctx;
++ __u8 hash[AH_AMAX];
++#endif /* defined(CONFIG_IPSEC_AUTH_HMAC_MD5) || defined(CONFIG_IPSEC_AUTH_HMAC_SHA1) */
++ int headroom = 0, tailroom = 0, ilen = 0, len = 0;
++ unsigned char *dat;
++ int blocksize = 8; /* XXX: should be inside ixs --jjo */
++#ifdef CONFIG_IPSEC_ALG
++ struct ipsec_alg_enc *ixt_e = NULL;
++ struct ipsec_alg_auth *ixt_a = NULL;
++#endif /* CONFIG_IPSEC_ALG */
++
++ ixs->iphlen = ixs->iph->ihl << 2;
++ ixs->pyldsz = ntohs(ixs->iph->tot_len) - ixs->iphlen;
++ ixs->sa_len = satot(&ixs->ipsp->ips_said, 0, ixs->sa_txt, SATOT_BUF);
++ KLIPS_PRINT(debug_tunnel & DB_TN_OXFS,
++ "klips_debug:ipsec_xmit_encap_once: "
++ "calling output for <%s%s%s>, SA:%s\n",
++ IPS_XFORM_NAME(ixs->ipsp),
++ ixs->sa_len ? ixs->sa_txt : " (error)");
++
++ switch(ixs->ipsp->ips_said.proto) {
++#ifdef CONFIG_IPSEC_AH
++ case IPPROTO_AH:
++ headroom += sizeof(struct ahhdr);
++ break;
++#endif /* CONFIG_IPSEC_AH */
++#ifdef CONFIG_IPSEC_ESP
++ case IPPROTO_ESP:
++#ifdef CONFIG_IPSEC_ALG
++ if ((ixt_e=ixs->ipsp->ips_alg_enc)) {
++ blocksize = ixt_e->ixt_blocksize;
++ headroom += ESP_HEADER_LEN + ixt_e->ixt_ivlen/8;
++ } else
++#endif /* CONFIG_IPSEC_ALG */
++ switch(ixs->ipsp->ips_encalg) {
++#ifdef CONFIG_IPSEC_ENC_3DES
++ case ESP_3DES:
++ headroom += sizeof(struct esphdr);
++ break;
++#endif /* CONFIG_IPSEC_ENC_3DES */
++ default:
++ ixs->stats->tx_errors++;
++ return IPSEC_XMIT_ESP_BADALG;
++ }
++#ifdef CONFIG_IPSEC_ALG
++ if ((ixt_a=ixs->ipsp->ips_alg_auth)) {
++ tailroom += AHHMAC_HASHLEN;
++ } else
++#endif /* CONFIG_IPSEC_ALG */
++ switch(ixs->ipsp->ips_authalg) {
++#ifdef CONFIG_IPSEC_AUTH_HMAC_MD5
++ case AH_MD5:
++ authlen = AHHMAC_HASHLEN;
++ break;
++#endif /* CONFIG_IPSEC_AUTH_HMAC_MD5 */
++#ifdef CONFIG_IPSEC_AUTH_HMAC_SHA1
++ case AH_SHA:
++ authlen = AHHMAC_HASHLEN;
++ break;
++#endif /* CONFIG_IPSEC_AUTH_HMAC_SHA1 */
++ case AH_NONE:
++ break;
++ default:
++ ixs->stats->tx_errors++;
++ return IPSEC_XMIT_ESP_BADALG;
++ }
++#ifdef CONFIG_IPSEC_ALG
++ tailroom += blocksize != 1 ?
++ ((blocksize - ((ixs->pyldsz + 2) % blocksize)) % blocksize) + 2 :
++ ((4 - ((ixs->pyldsz + 2) % 4)) % 4) + 2;
++#else
++ tailroom += ((8 - ((ixs->pyldsz + 2 * sizeof(unsigned char)) % 8)) % 8) + 2;
++#endif /* CONFIG_IPSEC_ALG */
++ tailroom += authlen;
++ break;
++#endif /* !CONFIG_IPSEC_ESP */
++#ifdef CONFIG_IPSEC_IPIP
++ case IPPROTO_IPIP:
++ headroom += sizeof(struct iphdr);
++ ixs->iphlen = sizeof(struct iphdr);
++ break;
++#endif /* !CONFIG_IPSEC_IPIP */
++#ifdef CONFIG_IPSEC_IPCOMP
++ case IPPROTO_COMP:
++ break;
++#endif /* CONFIG_IPSEC_IPCOMP */
++ default:
++ ixs->stats->tx_errors++;
++ return IPSEC_XMIT_BADPROTO;
++ }
++
++ KLIPS_PRINT(debug_tunnel & DB_TN_CROUT,
++ "klips_debug:ipsec_xmit_encap_once: "
++ "pushing %d bytes, putting %d, proto %d.\n",
++ headroom, tailroom, ixs->ipsp->ips_said.proto);
++ if(skb_headroom(ixs->skb) < headroom) {
++ printk(KERN_WARNING
++ "klips_error:ipsec_xmit_encap_once: "
++ "tried to skb_push headroom=%d, %d available. This should never happen, please report.\n",
++ headroom, skb_headroom(ixs->skb));
++ ixs->stats->tx_errors++;
++ return IPSEC_XMIT_ESP_PUSHPULLERR;
++ }
++ dat = skb_push(ixs->skb, headroom);
++ ilen = ixs->skb->len - tailroom;
++ if(skb_tailroom(ixs->skb) < tailroom) {
++ printk(KERN_WARNING
++ "klips_error:ipsec_xmit_encap_once: "
++ "tried to skb_put %d, %d available. This should never happen, please report.\n",
++ tailroom, skb_tailroom(ixs->skb));
++ ixs->stats->tx_errors++;
++ return IPSEC_XMIT_ESP_PUSHPULLERR;
++ }
++ skb_put(ixs->skb, tailroom);
++ KLIPS_PRINT(debug_tunnel & DB_TN_CROUT,
++ "klips_debug:ipsec_xmit_encap_once: "
++ "head,tailroom: %d,%d before xform.\n",
++ skb_headroom(ixs->skb), skb_tailroom(ixs->skb));
++ len = ixs->skb->len;
++ if(len > 0xfff0) {
++ printk(KERN_WARNING "klips_error:ipsec_xmit_encap_once: "
++ "tot_len (%d) > 65520. This should never happen, please report.\n",
++ len);
++ ixs->stats->tx_errors++;
++ return IPSEC_XMIT_BADLEN;
++ }
++ memmove((void *)dat, (void *)(dat + headroom), ixs->iphlen);
++ ixs->iph = (struct iphdr *)dat;
++ ixs->iph->tot_len = htons(ixs->skb->len);
++
++ switch(ixs->ipsp->ips_said.proto) {
++#ifdef CONFIG_IPSEC_ESP
++ case IPPROTO_ESP:
++ espp = (struct esphdr *)(dat + ixs->iphlen);
++ espp->esp_spi = ixs->ipsp->ips_said.spi;
++ espp->esp_rpl = htonl(++(ixs->ipsp->ips_replaywin_lastseq));
++
++#ifdef CONFIG_IPSEC_ALG
++ if (!ixt_e)
++#endif /* CONFIG_IPSEC_ALG */
++ switch(ixs->ipsp->ips_encalg) {
++#if defined(CONFIG_IPSEC_ENC_3DES)
++#ifdef CONFIG_IPSEC_ENC_3DES
++ case ESP_3DES:
++#endif /* CONFIG_IPSEC_ENC_3DES */
++ iv[0] = *((__u32*)&(espp->esp_iv) ) =
++ ((__u32*)(ixs->ipsp->ips_iv))[0];
++ iv[1] = *((__u32*)&(espp->esp_iv) + 1) =
++ ((__u32*)(ixs->ipsp->ips_iv))[1];
++ break;
++#endif /* defined(CONFIG_IPSEC_ENC_3DES) */
++ default:
++ ixs->stats->tx_errors++;
++ return IPSEC_XMIT_ESP_BADALG;
++ }
++
++ idat = dat + ixs->iphlen + headroom;
++ ilen = len - (ixs->iphlen + headroom + authlen);
++
++ /* Self-describing padding */
++ pad = &dat[len - tailroom];
++ padlen = tailroom - 2 - authlen;
++ for (i = 0; i < padlen; i++) {
++ pad[i] = i + 1;
++ }
++ dat[len - authlen - 2] = padlen;
++
++ dat[len - authlen - 1] = ixs->iph->protocol;
++ ixs->iph->protocol = IPPROTO_ESP;
++
++#ifdef CONFIG_IPSEC_ALG
++ /* Do all operations here:
++ * copy IV->ESP, encrypt, update ips IV
++ */
++ if (ixt_e) {
++ int ret;
++ memcpy(espp->esp_iv,
++ ixs->ipsp->ips_iv,
++ ixt_e->ixt_ivlen/8);
++ ret=ipsec_alg_esp_encrypt(ixs->ipsp,
++ idat, ilen, espp->esp_iv,
++ IPSEC_ALG_ENCRYPT);
++ memcpy(ixs->ipsp->ips_iv,
++ idat + ilen - ixt_e->ixt_ivlen/8,
++ ixt_e->ixt_ivlen/8);
++ } else
++#endif /* CONFIG_IPSEC_ALG */
++ switch(ixs->ipsp->ips_encalg) {
++#ifdef CONFIG_IPSEC_ENC_3DES
++ case ESP_3DES:
++ des_ede3_cbc_encrypt((des_cblock *)idat,
++ (des_cblock *)idat,
++ ilen,
++ ((struct des_eks *)(ixs->ipsp->ips_key_e))[0].ks,
++ ((struct des_eks *)(ixs->ipsp->ips_key_e))[1].ks,
++ ((struct des_eks *)(ixs->ipsp->ips_key_e))[2].ks,
++ (des_cblock *)iv, 1);
++ break;
++#endif /* CONFIG_IPSEC_ENC_3DES */
++ default:
++ ixs->stats->tx_errors++;
++ return IPSEC_XMIT_ESP_BADALG;
++ }
++
++#ifdef CONFIG_IPSEC_ALG
++ if (!ixt_e)
++#endif /* CONFIG_IPSEC_ALG */
++ switch(ixs->ipsp->ips_encalg) {
++#if defined(CONFIG_IPSEC_ENC_3DES)
++#ifdef CONFIG_IPSEC_ENC_3DES
++ case ESP_3DES:
++#endif /* CONFIG_IPSEC_ENC_3DES */
++ /* XXX update IV with the last 8 octets of the encryption */
++#if KLIPS_IMPAIRMENT_ESPIV_CBC_ATTACK
++ ((__u32*)(ixs->ipsp->ips_iv))[0] =
++ ((__u32 *)(idat))[(ilen >> 2) - 2];
++ ((__u32*)(ixs->ipsp->ips_iv))[1] =
++ ((__u32 *)(idat))[(ilen >> 2) - 1];
++#else /* KLIPS_IMPAIRMENT_ESPIV_CBC_ATTACK */
++ prng_bytes(&ipsec_prng, (char *)ixs->ipsp->ips_iv, EMT_ESPDES_IV_SZ);
++#endif /* KLIPS_IMPAIRMENT_ESPIV_CBC_ATTACK */
++ break;
++#endif /* defined(CONFIG_IPSEC_ENC_3DES) */
++ default:
++ ixs->stats->tx_errors++;
++ return IPSEC_XMIT_ESP_BADALG;
++ }
++
++#ifdef CONFIG_IPSEC_ALG
++ if (ixt_a) {
++ ipsec_alg_sa_esp_hash(ixs->ipsp,
++ (caddr_t)espp, len - ixs->iphlen - authlen,
++ &(dat[len - authlen]), authlen);
++
++ } else
++#endif /* CONFIG_IPSEC_ALG */
++ switch(ixs->ipsp->ips_authalg) {
++#ifdef CONFIG_IPSEC_AUTH_HMAC_MD5
++ case AH_MD5:
++ dmp("espp", (char*)espp, len - ixs->iphlen - authlen);
++ tctx.md5 = ((struct md5_ctx*)(ixs->ipsp->ips_key_a))->ictx;
++ dmp("ictx", (char*)&tctx.md5, sizeof(tctx.md5));
++ MD5Update(&tctx.md5, (caddr_t)espp, len - ixs->iphlen - authlen);
++ dmp("ictx+dat", (char*)&tctx.md5, sizeof(tctx.md5));
++ MD5Final(hash, &tctx.md5);
++ dmp("ictx hash", (char*)&hash, sizeof(hash));
++ tctx.md5 = ((struct md5_ctx*)(ixs->ipsp->ips_key_a))->octx;
++ dmp("octx", (char*)&tctx.md5, sizeof(tctx.md5));
++ MD5Update(&tctx.md5, hash, AHMD596_ALEN);
++ dmp("octx+hash", (char*)&tctx.md5, sizeof(tctx.md5));
++ MD5Final(hash, &tctx.md5);
++ dmp("octx hash", (char*)&hash, sizeof(hash));
++ memcpy(&(dat[len - authlen]), hash, authlen);
++
++ /* paranoid */
++ memset((caddr_t)&tctx.md5, 0, sizeof(tctx.md5));
++ memset((caddr_t)hash, 0, sizeof(*hash));
++ break;
++#endif /* CONFIG_IPSEC_AUTH_HMAC_MD5 */
++#ifdef CONFIG_IPSEC_AUTH_HMAC_SHA1
++ case AH_SHA:
++ tctx.sha1 = ((struct sha1_ctx*)(ixs->ipsp->ips_key_a))->ictx;
++ SHA1Update(&tctx.sha1, (caddr_t)espp, len - ixs->iphlen - authlen);
++ SHA1Final(hash, &tctx.sha1);
++ tctx.sha1 = ((struct sha1_ctx*)(ixs->ipsp->ips_key_a))->octx;
++ SHA1Update(&tctx.sha1, hash, AHSHA196_ALEN);
++ SHA1Final(hash, &tctx.sha1);
++ memcpy(&(dat[len - authlen]), hash, authlen);
++
++ /* paranoid */
++ memset((caddr_t)&tctx.sha1, 0, sizeof(tctx.sha1));
++ memset((caddr_t)hash, 0, sizeof(*hash));
++ break;
++#endif /* CONFIG_IPSEC_AUTH_HMAC_SHA1 */
++ case AH_NONE:
++ break;
++ default:
++ ixs->stats->tx_errors++;
++ return IPSEC_XMIT_AH_BADALG;
++ }
++#ifdef NET_21
++ ixs->skb->h.raw = (unsigned char*)espp;
++#endif /* NET_21 */
++ break;
++#endif /* !CONFIG_IPSEC_ESP */
++#ifdef CONFIG_IPSEC_AH
++ case IPPROTO_AH:
++ ahp = (struct ahhdr *)(dat + ixs->iphlen);
++ ahp->ah_spi = ixs->ipsp->ips_said.spi;
++ ahp->ah_rpl = htonl(++(ixs->ipsp->ips_replaywin_lastseq));
++ ahp->ah_rv = 0;
++ ahp->ah_nh = ixs->iph->protocol;
++ ahp->ah_hl = (headroom >> 2) - sizeof(__u64)/sizeof(__u32);
++ ixs->iph->protocol = IPPROTO_AH;
++ dmp("ahp", (char*)ahp, sizeof(*ahp));
++
++ ipo = *ixs->iph;
++ ipo.tos = 0;
++ ipo.frag_off = 0;
++ ipo.ttl = 0;
++ ipo.check = 0;
++ dmp("ipo", (char*)&ipo, sizeof(ipo));
++
++ switch(ixs->ipsp->ips_authalg) {
++#ifdef CONFIG_IPSEC_AUTH_HMAC_MD5
++ case AH_MD5:
++ tctx.md5 = ((struct md5_ctx*)(ixs->ipsp->ips_key_a))->ictx;
++ dmp("ictx", (char*)&tctx.md5, sizeof(tctx.md5));
++ MD5Update(&tctx.md5, (unsigned char *)&ipo, sizeof (struct iphdr));
++ dmp("ictx+ipo", (char*)&tctx.md5, sizeof(tctx.md5));
++ MD5Update(&tctx.md5, (unsigned char *)ahp, headroom - sizeof(ahp->ah_data));
++ dmp("ictx+ahp", (char*)&tctx.md5, sizeof(tctx.md5));
++ MD5Update(&tctx.md5, (unsigned char *)zeroes, AHHMAC_HASHLEN);
++ dmp("ictx+zeroes", (char*)&tctx.md5, sizeof(tctx.md5));
++ MD5Update(&tctx.md5, dat + ixs->iphlen + headroom, len - ixs->iphlen - headroom);
++ dmp("ictx+dat", (char*)&tctx.md5, sizeof(tctx.md5));
++ MD5Final(hash, &tctx.md5);
++ dmp("ictx hash", (char*)&hash, sizeof(hash));
++ tctx.md5 = ((struct md5_ctx*)(ixs->ipsp->ips_key_a))->octx;
++ dmp("octx", (char*)&tctx.md5, sizeof(tctx.md5));
++ MD5Update(&tctx.md5, hash, AHMD596_ALEN);
++ dmp("octx+hash", (char*)&tctx.md5, sizeof(tctx.md5));
++ MD5Final(hash, &tctx.md5);
++ dmp("octx hash", (char*)&hash, sizeof(hash));
++
++ memcpy(ahp->ah_data, hash, AHHMAC_HASHLEN);
++
++ /* paranoid */
++ memset((caddr_t)&tctx.md5, 0, sizeof(tctx.md5));
++ memset((caddr_t)hash, 0, sizeof(*hash));
++ break;
++#endif /* CONFIG_IPSEC_AUTH_HMAC_MD5 */
++#ifdef CONFIG_IPSEC_AUTH_HMAC_SHA1
++ case AH_SHA:
++ tctx.sha1 = ((struct sha1_ctx*)(ixs->ipsp->ips_key_a))->ictx;
++ SHA1Update(&tctx.sha1, (unsigned char *)&ipo, sizeof (struct iphdr));
++ SHA1Update(&tctx.sha1, (unsigned char *)ahp, headroom - sizeof(ahp->ah_data));
++ SHA1Update(&tctx.sha1, (unsigned char *)zeroes, AHHMAC_HASHLEN);
++ SHA1Update(&tctx.sha1, dat + ixs->iphlen + headroom, len - ixs->iphlen - headroom);
++ SHA1Final(hash, &tctx.sha1);
++ tctx.sha1 = ((struct sha1_ctx*)(ixs->ipsp->ips_key_a))->octx;
++ SHA1Update(&tctx.sha1, hash, AHSHA196_ALEN);
++ SHA1Final(hash, &tctx.sha1);
++
++ memcpy(ahp->ah_data, hash, AHHMAC_HASHLEN);
++
++ /* paranoid */
++ memset((caddr_t)&tctx.sha1, 0, sizeof(tctx.sha1));
++ memset((caddr_t)hash, 0, sizeof(*hash));
++ break;
++#endif /* CONFIG_IPSEC_AUTH_HMAC_SHA1 */
++ default:
++ ixs->stats->tx_errors++;
++ return IPSEC_XMIT_AH_BADALG;
++ }
++#ifdef NET_21
++ ixs->skb->h.raw = (unsigned char*)ahp;
++#endif /* NET_21 */
++ break;
++#endif /* CONFIG_IPSEC_AH */
++#ifdef CONFIG_IPSEC_IPIP
++ case IPPROTO_IPIP:
++ ixs->iph->version = 4;
++ switch(sysctl_ipsec_tos) {
++ case 0:
++#ifdef NET_21
++ ixs->iph->tos = ixs->skb->nh.iph->tos;
++#else /* NET_21 */
++ ixs->iph->tos = ixs->skb->ip_hdr->tos;
++#endif /* NET_21 */
++ break;
++ case 1:
++ ixs->iph->tos = 0;
++ break;
++ default:
++ break;
++ }
++#ifdef NET_21
++#ifdef NETDEV_23
++ ixs->iph->ttl = sysctl_ip_default_ttl;
++#else /* NETDEV_23 */
++ ixs->iph->ttl = ip_statistics.IpDefaultTTL;
++#endif /* NETDEV_23 */
++#else /* NET_21 */
++ ixs->iph->ttl = 64; /* ip_statistics.IpDefaultTTL; */
++#endif /* NET_21 */
++ ixs->iph->frag_off = 0;
++ ixs->iph->saddr = ((struct sockaddr_in*)(ixs->ipsp->ips_addr_s))->sin_addr.s_addr;
++ ixs->iph->daddr = ((struct sockaddr_in*)(ixs->ipsp->ips_addr_d))->sin_addr.s_addr;
++ ixs->iph->protocol = IPPROTO_IPIP;
++ ixs->iph->ihl = sizeof(struct iphdr) >> 2;
++
++ KLIPS_IP_SELECT_IDENT(ixs->iph, ixs->skb);
++
++ ixs->newdst = (__u32)ixs->iph->daddr;
++ ixs->newsrc = (__u32)ixs->iph->saddr;
++
++#ifdef NET_21
++ ixs->skb->h.ipiph = ixs->skb->nh.iph;
++#endif /* NET_21 */
++ break;
++#endif /* !CONFIG_IPSEC_IPIP */
++#ifdef CONFIG_IPSEC_IPCOMP
++ case IPPROTO_COMP:
++ {
++ unsigned int flags = 0;
++#ifdef CONFIG_IPSEC_DEBUG
++ unsigned int old_tot_len = ntohs(ixs->iph->tot_len);
++#endif /* CONFIG_IPSEC_DEBUG */
++ ixs->ipsp->ips_comp_ratio_dbytes += ntohs(ixs->iph->tot_len);
++
++ ixs->skb = skb_compress(ixs->skb, ixs->ipsp, &flags);
++
++#ifdef NET_21
++ ixs->iph = ixs->skb->nh.iph;
++#else /* NET_21 */
++ ixs->iph = ixs->skb->ip_hdr;
++#endif /* NET_21 */
++
++ ixs->ipsp->ips_comp_ratio_cbytes += ntohs(ixs->iph->tot_len);
++
++#ifdef CONFIG_IPSEC_DEBUG
++ if (debug_tunnel & DB_TN_CROUT)
++ {
++ if (old_tot_len > ntohs(ixs->iph->tot_len))
++ KLIPS_PRINT(debug_tunnel & DB_TN_CROUT,
++ "klips_debug:ipsec_xmit_encap_once: "
++ "packet shrunk from %d to %d bytes after compression, cpi=%04x (should be from spi=%08x, spi&0xffff=%04x.\n",
++ old_tot_len, ntohs(ixs->iph->tot_len),
++ ntohs(((struct ipcomphdr*)(((char*)ixs->iph) + ((ixs->iph->ihl) << 2)))->ipcomp_cpi),
++ ntohl(ixs->ipsp->ips_said.spi),
++ (__u16)(ntohl(ixs->ipsp->ips_said.spi) & 0x0000ffff));
++ else
++ KLIPS_PRINT(debug_tunnel & DB_TN_CROUT,
++ "klips_debug:ipsec_xmit_encap_once: "
++ "packet did not compress (flags = %d).\n",
++ flags);
++ }
++#endif /* CONFIG_IPSEC_DEBUG */
++ }
++ break;
++#endif /* CONFIG_IPSEC_IPCOMP */
++ default:
++ ixs->stats->tx_errors++;
++ return IPSEC_XMIT_BADPROTO;
++ }
++
++#ifdef NET_21
++ ixs->skb->nh.raw = ixs->skb->data;
++#else /* NET_21 */
++ ixs->skb->ip_hdr = ixs->skb->h.iph = (struct iphdr *) ixs->skb->data;
++#endif /* NET_21 */
++ ixs->iph->check = 0;
++ ixs->iph->check = ip_fast_csum((unsigned char *)ixs->iph, ixs->iph->ihl);
++
++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
++ "klips_debug:ipsec_xmit_encap_once: "
++ "after <%s%s%s>, SA:%s:\n",
++ IPS_XFORM_NAME(ixs->ipsp),
++ ixs->sa_len ? ixs->sa_txt : " (error)");
++ KLIPS_IP_PRINT(debug_tunnel & DB_TN_XMIT, ixs->iph);
++
++ ixs->ipsp->ips_life.ipl_bytes.ipl_count += len;
++ ixs->ipsp->ips_life.ipl_bytes.ipl_last = len;
++
++ if(!ixs->ipsp->ips_life.ipl_usetime.ipl_count) {
++ ixs->ipsp->ips_life.ipl_usetime.ipl_count = jiffies / HZ;
++ }
++ ixs->ipsp->ips_life.ipl_usetime.ipl_last = jiffies / HZ;
++ ixs->ipsp->ips_life.ipl_packets.ipl_count++;
++
++ ixs->ipsp = ixs->ipsp->ips_onext;
++
++ return IPSEC_XMIT_OK;
++}
++
++/*
++ * If the IP packet (iph) is a carrying TCP/UDP, then set the encaps
++ * source and destination ports to those from the TCP/UDP header.
++ */
++void ipsec_extract_ports(struct iphdr * iph, struct sockaddr_encap * er)
++{
++ struct udphdr *udp;
++
++ switch (iph->protocol) {
++ case IPPROTO_UDP:
++ case IPPROTO_TCP:
++ /*
++ * The ports are at the same offsets in a TCP and UDP
++ * header so hack it ...
++ */
++ udp = (struct udphdr*)(((char*)iph)+(iph->ihl<<2));
++ er->sen_sport = udp->source;
++ er->sen_dport = udp->dest;
++ break;
++ default:
++ er->sen_sport = 0;
++ er->sen_dport = 0;
++ break;
++ }
++}
++
++/*
++ * A TRAP eroute is installed and we want to replace it with a HOLD
++ * eroute.
++ */
++static int create_hold_eroute(struct eroute *origtrap,
++ struct sk_buff * skb, struct iphdr * iph,
++ uint32_t eroute_pid)
++{
++ struct eroute hold_eroute;
++ ip_said hold_said;
++ struct sk_buff *first, *last;
++ int error;
++
++ first = last = NULL;
++ memset((caddr_t)&hold_eroute, 0, sizeof(hold_eroute));
++ memset((caddr_t)&hold_said, 0, sizeof(hold_said));
++
++ hold_said.proto = IPPROTO_INT;
++ hold_said.spi = htonl(SPI_HOLD);
++ hold_said.dst.u.v4.sin_addr.s_addr = INADDR_ANY;
++
++ hold_eroute.er_eaddr.sen_len = sizeof(struct sockaddr_encap);
++ hold_eroute.er_emask.sen_len = sizeof(struct sockaddr_encap);
++ hold_eroute.er_eaddr.sen_family = AF_ENCAP;
++ hold_eroute.er_emask.sen_family = AF_ENCAP;
++ hold_eroute.er_eaddr.sen_type = SENT_IP4;
++ hold_eroute.er_emask.sen_type = 255;
++
++ hold_eroute.er_eaddr.sen_ip_src.s_addr = iph->saddr;
++ hold_eroute.er_eaddr.sen_ip_dst.s_addr = iph->daddr;
++ hold_eroute.er_emask.sen_ip_src.s_addr = INADDR_BROADCAST;
++ hold_eroute.er_emask.sen_ip_dst.s_addr = INADDR_BROADCAST;
++ hold_eroute.er_emask.sen_sport = 0;
++ hold_eroute.er_emask.sen_dport = 0;
++ hold_eroute.er_pid = eroute_pid;
++ hold_eroute.er_count = 0;
++ hold_eroute.er_lasttime = jiffies/HZ;
++
++ /*
++ * if it wasn't captured by a wildcard, then don't record it as
++ * a wildcard.
++ */
++ if(origtrap->er_eaddr.sen_proto != 0) {
++ hold_eroute.er_eaddr.sen_proto = iph->protocol;
++
++ if((iph->protocol == IPPROTO_TCP ||
++ iph->protocol == IPPROTO_UDP) &&
++ (origtrap->er_eaddr.sen_sport != 0 ||
++ origtrap->er_eaddr.sen_dport != 0)) {
++
++ if(origtrap->er_eaddr.sen_sport != 0)
++ hold_eroute.er_emask.sen_sport = ~0;
++
++ if(origtrap->er_eaddr.sen_dport != 0)
++ hold_eroute.er_emask.sen_dport = ~0;
++
++ ipsec_extract_ports(iph, &hold_eroute.er_eaddr);
++ }
++ }
++
++#ifdef CONFIG_IPSEC_DEBUG
++ if (debug_pfkey) {
++ char buf1[64], buf2[64];
++ subnettoa(hold_eroute.er_eaddr.sen_ip_src,
++ hold_eroute.er_emask.sen_ip_src, 0, buf1, sizeof(buf1));
++ subnettoa(hold_eroute.er_eaddr.sen_ip_dst,
++ hold_eroute.er_emask.sen_ip_dst, 0, buf2, sizeof(buf2));
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:ipsec_tunnel_start_xmit: "
++ "calling breakeroute and makeroute for %s:%d->%s:%d %d HOLD eroute.\n",
++ buf1, ntohs(hold_eroute.er_eaddr.sen_sport),
++ buf2, ntohs(hold_eroute.er_eaddr.sen_dport),
++ hold_eroute.er_eaddr.sen_proto);
++ }
++#endif /* CONFIG_IPSEC_DEBUG */
++
++ if (ipsec_breakroute(&(hold_eroute.er_eaddr), &(hold_eroute.er_emask),
++ &first, &last)) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:ipsec_tunnel_start_xmit: "
++ "HOLD breakeroute found nothing.\n");
++ } else {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:ipsec_tunnel_start_xmit: "
++ "HOLD breakroute deleted %u.%u.%u.%u:%u -> %u.%u.%u.%u:%u %u\n",
++ NIPQUAD(hold_eroute.er_eaddr.sen_ip_src),
++ ntohs(hold_eroute.er_eaddr.sen_sport),
++ NIPQUAD(hold_eroute.er_eaddr.sen_ip_dst),
++ ntohs(hold_eroute.er_eaddr.sen_dport),
++ hold_eroute.er_eaddr.sen_proto);
++ }
++ if (first != NULL)
++ kfree_skb(first);
++ if (last != NULL)
++ kfree_skb(last);
++
++ error = ipsec_makeroute(&(hold_eroute.er_eaddr),
++ &(hold_eroute.er_emask),
++ hold_said, eroute_pid, skb, NULL, NULL);
++ if (error) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:ipsec_tunnel_start_xmit: "
++ "HOLD makeroute returned %d, failed.\n", error);
++ } else {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:ipsec_tunnel_start_xmit: "
++ "HOLD makeroute call successful.\n");
++ }
++ return (error == 0);
++}
++
++enum ipsec_xmit_value
++ipsec_xmit_encap_bundle(struct ipsec_xmit_state *ixs)
++{
++#ifdef CONFIG_IPSEC_ALG
++ struct ipsec_alg_enc *ixt_e = NULL;
++ struct ipsec_alg_auth *ixt_a = NULL;
++ int blocksize = 8;
++#endif /* CONFIG_IPSEC_ALG */
++ enum ipsec_xmit_value bundle_stat = IPSEC_XMIT_OK;
++
++ ixs->newdst = ixs->orgdst = ixs->iph->daddr;
++ ixs->newsrc = ixs->orgsrc = ixs->iph->saddr;
++ ixs->orgedst = ixs->outgoing_said.dst.u.v4.sin_addr.s_addr;
++ ixs->iphlen = ixs->iph->ihl << 2;
++ ixs->pyldsz = ntohs(ixs->iph->tot_len) - ixs->iphlen;
++ ixs->max_headroom = ixs->max_tailroom = 0;
++
++ if (ixs->outgoing_said.proto == IPPROTO_INT) {
++ switch (ntohl(ixs->outgoing_said.spi)) {
++ case SPI_DROP:
++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
++ "klips_debug:ipsec_xmit_encap_bundle: "
++ "shunt SA of DROP or no eroute: dropping.\n");
++ ixs->stats->tx_dropped++;
++ break;
++
++ case SPI_REJECT:
++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
++ "klips_debug:ipsec_xmit_encap_bundle: "
++ "shunt SA of REJECT: notifying and dropping.\n");
++ ICMP_SEND(ixs->skb,
++ ICMP_DEST_UNREACH,
++ ICMP_PKT_FILTERED,
++ 0,
++ ixs->physdev);
++ ixs->stats->tx_dropped++;
++ break;
++
++ case SPI_PASS:
++#ifdef NET_21
++ ixs->pass = 1;
++#endif /* NET_21 */
++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
++ "klips_debug:ipsec_xmit_encap_bundle: "
++ "PASS: calling dev_queue_xmit\n");
++ return IPSEC_XMIT_PASS;
++ goto cleanup;
++
++ case SPI_HOLD:
++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
++ "klips_debug:ipsec_xmit_encap_bundle: "
++ "shunt SA of HOLD: this does not make sense here, dropping.\n");
++ ixs->stats->tx_dropped++;
++ break;
++
++ case SPI_TRAP:
++ case SPI_TRAPSUBNET:
++ {
++ struct sockaddr_in src, dst;
++#ifdef CONFIG_IPSEC_DEBUG
++ char bufsrc[ADDRTOA_BUF], bufdst[ADDRTOA_BUF];
++#endif /* CONFIG_IPSEC_DEBUG */
++
++ /* Signal all listening KMds with a PF_KEY ACQUIRE */
++
++ src.sin_family = AF_INET;
++ dst.sin_family = AF_INET;
++ src.sin_addr.s_addr = ixs->iph->saddr;
++ dst.sin_addr.s_addr = ixs->iph->daddr;
++
++ ixs->ips.ips_transport_protocol = 0;
++ src.sin_port = 0;
++ dst.sin_port = 0;
++ {
++ int i;
++ for(i = 0;
++ i < sizeof(struct sockaddr_in)
++ - offsetof(struct sockaddr_in, sin_zero);
++ i++) {
++ src.sin_zero[i] = 0;
++ dst.sin_zero[i] = 0;
++ }
++ }
++
++ if(ixs->eroute->er_eaddr.sen_proto != 0) {
++ ixs->ips.ips_transport_protocol = ixs->iph->protocol;
++
++ if(ixs->eroute->er_eaddr.sen_sport != 0) {
++ src.sin_port =
++ (ixs->iph->protocol == IPPROTO_UDP
++ ? ((struct udphdr*) (((caddr_t)ixs->iph) + (ixs->iph->ihl << 2)))->source
++ : (ixs->iph->protocol == IPPROTO_TCP
++ ? ((struct tcphdr*)((caddr_t)ixs->iph + (ixs->iph->ihl << 2)))->source
++ : 0));
++ }
++ if(ixs->eroute->er_eaddr.sen_dport != 0) {
++ dst.sin_port =
++ (ixs->iph->protocol == IPPROTO_UDP
++ ? ((struct udphdr*) (((caddr_t)ixs->iph) + (ixs->iph->ihl << 2)))->dest
++ : (ixs->iph->protocol == IPPROTO_TCP
++ ? ((struct tcphdr*)((caddr_t)ixs->iph + (ixs->iph->ihl << 2)))->dest
++ : 0));
++ }
++ }
++
++ ixs->ips.ips_addr_s = (struct sockaddr*)(&src);
++ ixs->ips.ips_addr_d = (struct sockaddr*)(&dst);
++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
++ "klips_debug:ipsec_xmit_encap_bundle: "
++ "SADB_ACQUIRE sent with src=%s:%d, dst=%s:%d, proto=%d.\n",
++ addrtoa(((struct sockaddr_in*)(ixs->ips.ips_addr_s))->sin_addr, 0, bufsrc, sizeof(bufsrc)) <= ADDRTOA_BUF ? bufsrc : "BAD_ADDR",
++ ntohs(((struct sockaddr_in*)(ixs->ips.ips_addr_s))->sin_port),
++ addrtoa(((struct sockaddr_in*)(ixs->ips.ips_addr_d))->sin_addr, 0, bufdst, sizeof(bufdst)) <= ADDRTOA_BUF ? bufdst : "BAD_ADDR",
++ ntohs(((struct sockaddr_in*)(ixs->ips.ips_addr_d))->sin_port),
++ ixs->ips.ips_said.proto);
++
++ /* increment count of total traps needed */
++ ipsec_xmit_trap_count++;
++
++ if (pfkey_acquire(&ixs->ips) == 0) {
++
++ /* note that we succeeded */
++ ipsec_xmit_trap_sendcount++;
++
++ if (ixs->outgoing_said.spi==htonl(SPI_TRAPSUBNET)) {
++ /*
++ * The spinlock is to prevent any other
++ * process from accessing or deleting
++ * the eroute while we are using and
++ * updating it.
++ */
++ spin_lock(&eroute_lock);
++ ixs->eroute = ipsec_findroute(&ixs->matcher);
++ if(ixs->eroute) {
++ ixs->eroute->er_said.spi = htonl(SPI_HOLD);
++ ixs->eroute->er_first = ixs->skb;
++ ixs->skb = NULL;
++ }
++ spin_unlock(&eroute_lock);
++ } else if (create_hold_eroute(ixs->eroute,
++ ixs->skb,
++ ixs->iph,
++ ixs->eroute_pid)) {
++ ixs->skb = NULL;
++ }
++ /* whether or not the above succeeded, we continue */
++
++ }
++ ixs->stats->tx_dropped++;
++ }
++ default:
++ /* XXX what do we do with an unknown shunt spi? */
++ break;
++ } /* switch (ntohl(ixs->outgoing_said.spi)) */
++ return IPSEC_XMIT_STOLEN;
++ } /* if (ixs->outgoing_said.proto == IPPROTO_INT) */
++
++ /*
++ The spinlock is to prevent any other process from
++ accessing or deleting the ipsec_sa hash table or any of the
++ ipsec_sa s while we are using and updating them.
++
++ This is not optimal, but was relatively straightforward
++ at the time. A better way to do it has been planned for
++ more than a year, to lock the hash table and put reference
++ counts on each ipsec_sa instead. This is not likely to happen
++ in KLIPS1 unless a volunteer contributes it, but will be
++ designed into KLIPS2.
++ */
++ spin_lock(&tdb_lock);
++
++ ixs->ipsp = ipsec_sa_getbyid(&ixs->outgoing_said);
++ ixs->sa_len = satot(&ixs->outgoing_said, 0, ixs->sa_txt, sizeof(ixs->sa_txt));
++
++ if (ixs->ipsp == NULL) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
++ "klips_debug:ipsec_xmit_encap_bundle: "
++ "no ipsec_sa for SA%s: outgoing packet with no SA, dropped.\n",
++ ixs->sa_len ? ixs->sa_txt : " (error)");
++ ixs->stats->tx_dropped++;
++ bundle_stat = IPSEC_XMIT_SAIDNOTFOUND;
++ goto cleanup;
++ }
++
++ ipsec_sa_put(ixs->ipsp); /* incomplete */
++
++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
++ "klips_debug:ipsec_xmit_encap_bundle: "
++ "found ipsec_sa -- SA:<%s%s%s> %s\n",
++ IPS_XFORM_NAME(ixs->ipsp),
++ ixs->sa_len ? ixs->sa_txt : " (error)");
++
++ /*
++ * How much headroom do we need to be able to apply
++ * all the grouped transforms?
++ */
++ ixs->ipsq = ixs->ipsp; /* save the head of the ipsec_sa chain */
++ while (ixs->ipsp) {
++ ixs->sa_len = satot(&ixs->ipsp->ips_said, 0, ixs->sa_txt, sizeof(ixs->sa_txt));
++ if(ixs->sa_len == 0) {
++ strcpy(ixs->sa_txt, "(error)");
++ }
++
++ /* If it is in larval state, drop the packet, we cannot process yet. */
++ if(ixs->ipsp->ips_state == SADB_SASTATE_LARVAL) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
++ "klips_debug:ipsec_xmit_encap_bundle: "
++ "ipsec_sa in larval state for SA:<%s%s%s> %s, cannot be used yet, dropping packet.\n",
++ IPS_XFORM_NAME(ixs->ipsp),
++ ixs->sa_len ? ixs->sa_txt : " (error)");
++ ixs->stats->tx_errors++;
++ bundle_stat = IPSEC_XMIT_SAIDNOTLIVE;
++ goto cleanup;
++ }
++
++ if(ixs->ipsp->ips_state == SADB_SASTATE_DEAD) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
++ "klips_debug:ipsec_xmit_encap_bundle: "
++ "ipsec_sa in dead state for SA:<%s%s%s> %s, can no longer be used, dropping packet.\n",
++ IPS_XFORM_NAME(ixs->ipsp),
++ ixs->sa_len ? ixs->sa_txt : " (error)");
++ ixs->stats->tx_errors++;
++ bundle_stat = IPSEC_XMIT_SAIDNOTLIVE;
++ goto cleanup;
++ }
++
++ /* If the replay window counter == -1, expire SA, it will roll */
++ if(ixs->ipsp->ips_replaywin && ixs->ipsp->ips_replaywin_lastseq == -1) {
++ pfkey_expire(ixs->ipsp, 1);
++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
++ "klips_debug:ipsec_xmit_encap_bundle: "
++ "replay window counter rolled for SA:<%s%s%s> %s, packet dropped, expiring SA.\n",
++ IPS_XFORM_NAME(ixs->ipsp),
++ ixs->sa_len ? ixs->sa_txt : " (error)");
++ ipsec_sa_delchain(ixs->ipsp);
++ ixs->stats->tx_errors++;
++ bundle_stat = IPSEC_XMIT_REPLAYROLLED;
++ goto cleanup;
++ }
++
++ /*
++ * if this is the first time we are using this SA, mark start time,
++ * and offset hard/soft counters by "now" for later checking.
++ */
++#if 0
++ if(ixs->ipsp->ips_life.ipl_usetime.count == 0) {
++ ixs->ipsp->ips_life.ipl_usetime.count = jiffies;
++ ixs->ipsp->ips_life.ipl_usetime.hard += jiffies;
++ ixs->ipsp->ips_life.ipl_usetime.soft += jiffies;
++ }
++#endif
++
++
++ if(ipsec_lifetime_check(&ixs->ipsp->ips_life.ipl_bytes, "bytes", ixs->sa_txt,
++ ipsec_life_countbased, ipsec_outgoing, ixs->ipsp) == ipsec_life_harddied ||
++ ipsec_lifetime_check(&ixs->ipsp->ips_life.ipl_addtime, "addtime",ixs->sa_txt,
++ ipsec_life_timebased, ipsec_outgoing, ixs->ipsp) == ipsec_life_harddied ||
++ ipsec_lifetime_check(&ixs->ipsp->ips_life.ipl_usetime, "usetime",ixs->sa_txt,
++ ipsec_life_timebased, ipsec_outgoing, ixs->ipsp) == ipsec_life_harddied ||
++ ipsec_lifetime_check(&ixs->ipsp->ips_life.ipl_packets, "packets",ixs->sa_txt,
++ ipsec_life_countbased, ipsec_outgoing, ixs->ipsp) == ipsec_life_harddied) {
++
++ ipsec_sa_delchain(ixs->ipsp);
++ ixs->stats->tx_errors++;
++ bundle_stat = IPSEC_XMIT_LIFETIMEFAILED;
++ goto cleanup;
++ }
++
++
++ ixs->headroom = ixs->tailroom = 0;
++ KLIPS_PRINT(debug_tunnel & DB_TN_CROUT,
++ "klips_debug:ipsec_xmit_encap_bundle: "
++ "calling room for <%s%s%s>, SA:%s\n",
++ IPS_XFORM_NAME(ixs->ipsp),
++ ixs->sa_len ? ixs->sa_txt : " (error)");
++ switch(ixs->ipsp->ips_said.proto) {
++#ifdef CONFIG_IPSEC_AH
++ case IPPROTO_AH:
++ ixs->headroom += sizeof(struct ahhdr);
++ break;
++#endif /* CONFIG_IPSEC_AH */
++#ifdef CONFIG_IPSEC_ESP
++ case IPPROTO_ESP:
++#ifdef CONFIG_IPSEC_ALG
++ if ((ixt_e=ixs->ipsp->ips_alg_enc)) {
++ blocksize = ixt_e->ixt_blocksize;
++ ixs->headroom += ESP_HEADER_LEN + ixt_e->ixt_ivlen/8;
++ } else
++#endif /* CONFIG_IPSEC_ALG */
++ switch(ixs->ipsp->ips_encalg) {
++#ifdef CONFIG_IPSEC_ENC_3DES
++ case ESP_3DES:
++ ixs->headroom += sizeof(struct esphdr);
++ break;
++#endif /* CONFIG_IPSEC_ENC_3DES */
++ default:
++ ixs->stats->tx_errors++;
++ bundle_stat = IPSEC_XMIT_ESP_BADALG;
++ goto cleanup;
++ }
++#ifdef CONFIG_IPSEC_ALG
++ if ((ixt_a=ixs->ipsp->ips_alg_auth)) {
++ ixs->tailroom += AHHMAC_HASHLEN;
++ } else
++#endif /* CONFIG_IPSEC_ALG */
++ switch(ixs->ipsp->ips_authalg) {
++#ifdef CONFIG_IPSEC_AUTH_HMAC_MD5
++ case AH_MD5:
++ ixs->tailroom += AHHMAC_HASHLEN;
++ break;
++#endif /* CONFIG_IPSEC_AUTH_HMAC_MD5 */
++#ifdef CONFIG_IPSEC_AUTH_HMAC_SHA1
++ case AH_SHA:
++ ixs->tailroom += AHHMAC_HASHLEN;
++ break;
++#endif /* CONFIG_IPSEC_AUTH_HMAC_SHA1 */
++ case AH_NONE:
++ break;
++ default:
++ ixs->stats->tx_errors++;
++ bundle_stat = IPSEC_XMIT_AH_BADALG;
++ goto cleanup;
++ }
++#ifdef CONFIG_IPSEC_ALG
++ ixs->tailroom += blocksize != 1 ?
++ ((blocksize - ((ixs->pyldsz + 2) % blocksize)) % blocksize) + 2 :
++ ((4 - ((ixs->pyldsz + 2) % 4)) % 4) + 2;
++#else
++ ixs->tailroom += ((8 - ((ixs->pyldsz + 2 * sizeof(unsigned char)) % 8)) % 8) + 2;
++#endif /* CONFIG_IPSEC_ALG */
++#ifdef CONFIG_IPSEC_NAT_TRAVERSAL
++ if ((ixs->ipsp->ips_natt_type) && (!ixs->natt_type)) {
++ ixs->natt_type = ixs->ipsp->ips_natt_type;
++ ixs->natt_sport = ixs->ipsp->ips_natt_sport;
++ ixs->natt_dport = ixs->ipsp->ips_natt_dport;
++ switch (ixs->natt_type) {
++ case ESPINUDP_WITH_NON_IKE:
++ ixs->natt_head = sizeof(struct udphdr)+(2*sizeof(__u32));
++ break;
++
++ case ESPINUDP_WITH_NON_ESP:
++ ixs->natt_head = sizeof(struct udphdr);
++ break;
++
++ default:
++ KLIPS_PRINT(debug_tunnel & DB_TN_CROUT
++ , "klips_xmit: invalid nat-t type %d"
++ , ixs->natt_type);
++ bundle_stat = IPSEC_XMIT_ESPUDP_BADTYPE;
++ goto cleanup;
++
++ break;
++ }
++ ixs->tailroom += ixs->natt_head;
++ }
++#endif
++ break;
++#endif /* !CONFIG_IPSEC_ESP */
++#ifdef CONFIG_IPSEC_IPIP
++ case IPPROTO_IPIP:
++ ixs->headroom += sizeof(struct iphdr);
++ break;
++#endif /* !CONFIG_IPSEC_IPIP */
++ case IPPROTO_COMP:
++#ifdef CONFIG_IPSEC_IPCOMP
++ /*
++ We can't predict how much the packet will
++ shrink without doing the actual compression.
++ We could do it here, if we were the first
++ encapsulation in the chain. That might save
++ us a skb_copy_expand, since we might fit
++ into the existing skb then. However, this
++ would be a bit unclean (and this hack has
++ bit us once), so we better not do it. After
++ all, the skb_copy_expand is cheap in
++ comparison to the actual compression.
++ At least we know the packet will not grow.
++ */
++ break;
++#endif /* CONFIG_IPSEC_IPCOMP */
++ default:
++ ixs->stats->tx_errors++;
++ bundle_stat = IPSEC_XMIT_BADPROTO;
++ goto cleanup;
++ }
++ ixs->ipsp = ixs->ipsp->ips_onext;
++ KLIPS_PRINT(debug_tunnel & DB_TN_CROUT,
++ "klips_debug:ipsec_xmit_encap_bundle: "
++ "Required head,tailroom: %d,%d\n",
++ ixs->headroom, ixs->tailroom);
++ ixs->max_headroom += ixs->headroom;
++ ixs->max_tailroom += ixs->tailroom;
++ ixs->pyldsz += (ixs->headroom + ixs->tailroom);
++ }
++ ixs->ipsp = ixs->ipsq; /* restore the head of the ipsec_sa chain */
++
++ KLIPS_PRINT(debug_tunnel & DB_TN_CROUT,
++ "klips_debug:ipsec_xmit_encap_bundle: "
++ "existing head,tailroom: %d,%d before applying xforms with head,tailroom: %d,%d .\n",
++ skb_headroom(ixs->skb), skb_tailroom(ixs->skb),
++ ixs->max_headroom, ixs->max_tailroom);
++
++ ixs->tot_headroom += ixs->max_headroom;
++ ixs->tot_tailroom += ixs->max_tailroom;
++
++ ixs->mtudiff = ixs->prv->mtu + ixs->tot_headroom + ixs->tot_tailroom - ixs->physmtu;
++
++ KLIPS_PRINT(debug_tunnel & DB_TN_CROUT,
++ "klips_debug:ipsec_xmit_encap_bundle: "
++ "mtu:%d physmtu:%d tothr:%d tottr:%d mtudiff:%d ippkttotlen:%d\n",
++ ixs->prv->mtu, ixs->physmtu,
++ ixs->tot_headroom, ixs->tot_tailroom, ixs->mtudiff, ntohs(ixs->iph->tot_len));
++ if(ixs->mtudiff > 0) {
++ int newmtu = ixs->physmtu - (ixs->tot_headroom + ((ixs->tot_tailroom + 2) & ~7) + 5);
++
++ KLIPS_PRINT(debug_tunnel & DB_TN_CROUT,
++ "klips_info:ipsec_xmit_encap_bundle: "
++ "dev %s mtu of %d decreased by %d to %d\n",
++ ixs->dev->name,
++ ixs->prv->mtu,
++ ixs->prv->mtu - newmtu,
++ newmtu);
++ ixs->prv->mtu = newmtu;
++#ifdef NET_21
++#if 0
++ ixs->skb->dst->pmtu = ixs->prv->mtu; /* RGB */
++#endif /* 0 */
++#else /* NET_21 */
++#if 0
++ ixs->dev->mtu = ixs->prv->mtu; /* RGB */
++#endif /* 0 */
++#endif /* NET_21 */
++ }
++
++ /*
++ If the sender is doing PMTU discovery, and the
++ packet doesn't fit within ixs->prv->mtu, notify him
++ (unless it was an ICMP packet, or it was not the
++ zero-offset packet) and send it anyways.
++
++ Note: buggy firewall configuration may prevent the
++ ICMP packet from getting back.
++ */
++ if(sysctl_ipsec_icmp
++ && ixs->prv->mtu < ntohs(ixs->iph->tot_len)
++ && (ixs->iph->frag_off & __constant_htons(IP_DF)) ) {
++ int notify = ixs->iph->protocol != IPPROTO_ICMP
++ && (ixs->iph->frag_off & __constant_htons(IP_OFFSET)) == 0;
++
++#ifdef IPSEC_obey_DF
++ KLIPS_PRINT(debug_tunnel & DB_TN_CROUT,
++ "klips_debug:ipsec_xmit_encap_bundle: "
++ "fragmentation needed and DF set; %sdropping packet\n",
++ notify ? "sending ICMP and " : "");
++ if (notify)
++ ICMP_SEND(ixs->skb,
++ ICMP_DEST_UNREACH,
++ ICMP_FRAG_NEEDED,
++ ixs->prv->mtu,
++ ixs->physdev);
++ ixs->stats->tx_errors++;
++ bundle_stat = IPSEC_XMIT_CANNOTFRAG;
++ goto cleanup;
++#else /* IPSEC_obey_DF */
++ KLIPS_PRINT(debug_tunnel & DB_TN_CROUT,
++ "klips_debug:ipsec_xmit_encap_bundle: "
++ "fragmentation needed and DF set; %spassing packet\n",
++ notify ? "sending ICMP and " : "");
++ if (notify)
++ ICMP_SEND(ixs->skb,
++ ICMP_DEST_UNREACH,
++ ICMP_FRAG_NEEDED,
++ ixs->prv->mtu,
++ ixs->physdev);
++#endif /* IPSEC_obey_DF */
++ }
++
++#ifdef MSS_HACK
++ /*
++ * If this is a transport mode TCP packet with
++ * SYN set, determine an effective MSS based on
++ * AH/ESP overheads determined above.
++ */
++ if (ixs->iph->protocol == IPPROTO_TCP
++ && ixs->outgoing_said.proto != IPPROTO_IPIP) {
++ struct tcphdr *tcph = ixs->skb->h.th;
++ if (tcph->syn && !tcph->ack) {
++ if(!ipsec_adjust_mss(ixs->skb, tcph, ixs->prv->mtu)) {
++ printk(KERN_WARNING
++ "klips_warning:ipsec_xmit_encap_bundle: "
++ "ipsec_adjust_mss() failed\n");
++ ixs->stats->tx_errors++;
++ bundle_stat = IPSEC_XMIT_MSSERR;
++ goto cleanup;
++ }
++ }
++ }
++#endif /* MSS_HACK */
++
++#ifdef CONFIG_IPSEC_NAT_TRAVERSAL
++ if ((ixs->natt_type) && (ixs->outgoing_said.proto != IPPROTO_IPIP)) {
++ /**
++ * NAT-Traversal and Transport Mode:
++ * we need to correct TCP/UDP checksum
++ *
++ * If we've got NAT-OA, we can fix checksum without recalculation.
++ * If we don't we can zero udp checksum.
++ */
++ __u32 natt_oa = ixs->ipsp->ips_natt_oa ?
++ ((struct sockaddr_in*)(ixs->ipsp->ips_natt_oa))->sin_addr.s_addr : 0;
++ __u16 pkt_len = ixs->skb->tail - (unsigned char *)ixs->iph;
++ __u16 data_len = pkt_len - (ixs->iph->ihl << 2);
++ switch (ixs->iph->protocol) {
++ case IPPROTO_TCP:
++ if (data_len >= sizeof(struct tcphdr)) {
++ struct tcphdr *tcp = (struct tcphdr *)((__u32 *)ixs->iph+ixs->iph->ihl);
++ if (natt_oa) {
++ __u32 buff[2] = { ~ixs->iph->daddr, natt_oa };
++ KLIPS_PRINT(debug_tunnel,
++ "klips_debug:ipsec_tunnel_start_xmit: "
++ "NAT-T & TRANSPORT: "
++ "fix TCP checksum using NAT-OA\n");
++ tcp->check = csum_fold(
++ csum_partial((unsigned char *)buff, sizeof(buff),
++ tcp->check^0xffff));
++ }
++ else {
++ KLIPS_PRINT(debug_tunnel,
++ "klips_debug:ipsec_tunnel_start_xmit: "
++ "NAT-T & TRANSPORT: do not recalc TCP checksum\n");
++ }
++ }
++ else {
++ KLIPS_PRINT(debug_tunnel,
++ "klips_debug:ipsec_tunnel_start_xmit: "
++ "NAT-T & TRANSPORT: can't fix TCP checksum\n");
++ }
++ break;
++ case IPPROTO_UDP:
++ if (data_len >= sizeof(struct udphdr)) {
++ struct udphdr *udp = (struct udphdr *)((__u32 *)ixs->iph+ixs->iph->ihl);
++ if (udp->check == 0) {
++ KLIPS_PRINT(debug_tunnel,
++ "klips_debug:ipsec_tunnel_start_xmit: "
++ "NAT-T & TRANSPORT: UDP checksum already 0\n");
++ }
++ else if (natt_oa) {
++ __u32 buff[2] = { ~ixs->iph->daddr, natt_oa };
++ KLIPS_PRINT(debug_tunnel,
++ "klips_debug:ipsec_tunnel_start_xmit: "
++ "NAT-T & TRANSPORT: "
++ "fix UDP checksum using NAT-OA\n");
++ udp->check = csum_fold(
++ csum_partial((unsigned char *)buff, sizeof(buff),
++ udp->check^0xffff));
++ }
++ else {
++ KLIPS_PRINT(debug_tunnel,
++ "klips_debug:ipsec_tunnel_start_xmit: "
++ "NAT-T & TRANSPORT: zero UDP checksum\n");
++ udp->check = 0;
++ }
++ }
++ else {
++ KLIPS_PRINT(debug_tunnel,
++ "klips_debug:ipsec_tunnel_start_xmit: "
++ "NAT-T & TRANSPORT: can't fix UDP checksum\n");
++ }
++ break;
++ default:
++ KLIPS_PRINT(debug_tunnel,
++ "klips_debug:ipsec_tunnel_start_xmit: "
++ "NAT-T & TRANSPORT: non TCP/UDP packet -- do nothing\n");
++ break;
++ }
++ }
++#endif /* CONFIG_IPSEC_NAT_TRAVERSAL */
++
++ if(!ixs->hard_header_stripped) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
++ "klips_debug:ipsec_xmit_encap_bundle: "
++ "allocating %d bytes for hardheader.\n",
++ ixs->hard_header_len);
++ if((ixs->saved_header = kmalloc(ixs->hard_header_len, GFP_ATOMIC)) == NULL) {
++ printk(KERN_WARNING "klips_debug:ipsec_xmit_encap_bundle: "
++ "Failed, tried to allocate %d bytes for temp hard_header.\n",
++ ixs->hard_header_len);
++ ixs->stats->tx_errors++;
++ bundle_stat = IPSEC_XMIT_ERRMEMALLOC;
++ goto cleanup;
++ }
++ {
++ int i;
++ for (i = 0; i < ixs->hard_header_len; i++) {
++ ixs->saved_header[i] = ixs->skb->data[i];
++ }
++ }
++ if(ixs->skb->len < ixs->hard_header_len) {
++ printk(KERN_WARNING "klips_error:ipsec_xmit_encap_bundle: "
++ "tried to skb_pull hhlen=%d, %d available. This should never happen, please report.\n",
++ ixs->hard_header_len, (int)(ixs->skb->len));
++ ixs->stats->tx_errors++;
++ bundle_stat = IPSEC_XMIT_ESP_PUSHPULLERR;
++ goto cleanup;
++ }
++ skb_pull(ixs->skb, ixs->hard_header_len);
++ ixs->hard_header_stripped = 1;
++
++/* ixs->iph = (struct iphdr *) (ixs->skb->data); */
++ KLIPS_PRINT(debug_tunnel & DB_TN_CROUT,
++ "klips_debug:ipsec_xmit_encap_bundle: "
++ "head,tailroom: %d,%d after hard_header stripped.\n",
++ skb_headroom(ixs->skb), skb_tailroom(ixs->skb));
++ KLIPS_IP_PRINT(debug_tunnel & DB_TN_CROUT, ixs->iph);
++ } else {
++ KLIPS_PRINT(debug_tunnel & DB_TN_CROUT,
++ "klips_debug:ipsec_xmit_encap_bundle: "
++ "hard header already stripped.\n");
++ }
++
++ ixs->ll_headroom = (ixs->hard_header_len + 15) & ~15;
++
++ if ((skb_headroom(ixs->skb) >= ixs->max_headroom + 2 * ixs->ll_headroom) &&
++ (skb_tailroom(ixs->skb) >= ixs->max_tailroom)
++#ifndef NET_21
++ && ixs->skb->free
++#endif /* !NET_21 */
++ ) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_CROUT,
++ "klips_debug:ipsec_xmit_encap_bundle: "
++ "data fits in existing skb\n");
++ } else {
++ struct sk_buff* tskb;
++
++ if(!ixs->oskb) {
++ ixs->oskb = ixs->skb;
++ }
++
++ tskb = skb_copy_expand(ixs->skb,
++ /* The need for 2 * link layer length here remains unexplained...RGB */
++ ixs->max_headroom + 2 * ixs->ll_headroom,
++ ixs->max_tailroom,
++ GFP_ATOMIC);
++#ifdef NET_21
++ if(tskb && ixs->skb->sk) {
++ skb_set_owner_w(tskb, ixs->skb->sk);
++ }
++#endif /* NET_21 */
++ if(ixs->skb != ixs->oskb) {
++ ipsec_kfree_skb(ixs->skb);
++ }
++ ixs->skb = tskb;
++ if (!ixs->skb) {
++ printk(KERN_WARNING
++ "klips_debug:ipsec_xmit_encap_bundle: "
++ "Failed, tried to allocate %d head and %d tailroom\n",
++ ixs->max_headroom, ixs->max_tailroom);
++ ixs->stats->tx_errors++;
++ bundle_stat = IPSEC_XMIT_ERRSKBALLOC;
++ goto cleanup;
++ }
++ KLIPS_PRINT(debug_tunnel & DB_TN_CROUT,
++ "klips_debug:ipsec_xmit_encap_bundle: "
++ "head,tailroom: %d,%d after allocation\n",
++ skb_headroom(ixs->skb), skb_tailroom(ixs->skb));
++ }
++
++ /*
++ * Apply grouped transforms to packet
++ */
++ while (ixs->ipsp) {
++ enum ipsec_xmit_value encap_stat = IPSEC_XMIT_OK;
++
++ encap_stat = ipsec_xmit_encap_once(ixs);
++ if(encap_stat != IPSEC_XMIT_OK) {
++ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
++ "klips_debug:ipsec_xmit_encap_bundle: encap_once failed: %d\n",
++ encap_stat);
++
++ bundle_stat = IPSEC_XMIT_ENCAPFAIL;
++ goto cleanup;
++ }
++ }
++ /* end encapsulation loop here XXX */
++ cleanup:
++ spin_unlock(&tdb_lock);
++ return bundle_stat;
++}
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.8 2004/04/06 02:49:26 mcr
++ * pullup of algo code from alg-branch.
++ *
++ * Revision 1.7 2004/02/03 03:13:41 mcr
++ * mark invalid encapsulation states.
++ *
++ * Revision 1.6.2.1 2003/12/22 15:25:52 jjo
++ * Merged algo-0.8.1-rc11-test1 into alg-branch
++ *
++ * Revision 1.6 2003/12/10 01:14:27 mcr
++ * NAT-traversal patches to KLIPS.
++ *
++ * Revision 1.5 2003/10/31 02:27:55 mcr
++ * pulled up port-selector patches and sa_id elimination.
++ *
++ * Revision 1.4.4.2 2003/10/29 01:37:39 mcr
++ * when creating %hold from %trap, only make the %hold as
++ * specific as the %trap was - so if the protocol and ports
++ * were wildcards, then the %hold will be too.
++ *
++ * Revision 1.4.4.1 2003/09/21 13:59:56 mcr
++ * pre-liminary X.509 patch - does not yet pass tests.
++ *
++ * Revision 1.4 2003/06/20 02:28:10 mcr
++ * misstype of variable name, not detected by module build.
++ *
++ * Revision 1.3 2003/06/20 01:42:21 mcr
++ * added counters to measure how many ACQUIREs we send to pluto,
++ * and how many are successfully sent.
++ *
++ * Revision 1.2 2003/04/03 17:38:35 rgb
++ * Centralised ipsec_kfree_skb and ipsec_dev_{get,put}.
++ * Normalised coding style.
++ * Simplified logic and reduced duplication of code.
++ *
++ * Revision 1.1 2003/02/12 19:31:23 rgb
++ * Refactored from ipsec_tunnel.c
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/net/ipsec/pfkey_v2.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,2126 @@
++/*
++ * @(#) RFC2367 PF_KEYv2 Key management API domain socket I/F
++ * Copyright (C) 1999, 2000, 2001 Richard Guy Briggs.
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ * RCSID $Id$
++ */
++
++/*
++ * Template from /usr/src/linux-2.0.36/net/unix/af_unix.c.
++ * Hints from /usr/src/linux-2.0.36/net/ipv4/udp.c.
++ */
++
++#define __NO_VERSION__
++#include <linux/module.h>
++#include <linux/version.h>
++#include <linux/config.h>
++#include <linux/kernel.h>
++
++#include "openswan/ipsec_param.h"
++
++#include <linux/major.h>
++#include <linux/signal.h>
++#include <linux/sched.h>
++#include <linux/errno.h>
++#include <linux/string.h>
++#include <linux/stat.h>
++#include <linux/socket.h>
++#include <linux/un.h>
++#include <linux/fcntl.h>
++#include <linux/termios.h>
++#include <linux/socket.h>
++#include <linux/sockios.h>
++#include <linux/net.h> /* struct socket */
++#include <linux/in.h>
++#include <linux/fs.h>
++#ifdef MALLOC_SLAB
++# include <linux/slab.h> /* kmalloc() */
++#else /* MALLOC_SLAB */
++# include <linux/malloc.h> /* kmalloc() */
++#endif /* MALLOC_SLAB */
++#include <asm/segment.h>
++#include <linux/skbuff.h>
++#include <linux/netdevice.h>
++#include <net/sock.h> /* struct sock */
++/* #include <net/tcp.h> */
++#include <net/af_unix.h>
++#ifdef CONFIG_PROC_FS
++# include <linux/proc_fs.h>
++#endif /* CONFIG_PROC_FS */
++
++#include <linux/types.h>
++
++#include <openswan.h>
++#ifdef NET_21
++# include <asm/uaccess.h>
++# include <linux/in6.h>
++#endif /* NET_21 */
++
++#include "openswan/radij.h"
++#include "openswan/ipsec_encap.h"
++#include "openswan/ipsec_sa.h"
++
++#include <pfkeyv2.h>
++#include <pfkey.h>
++
++#include "openswan/ipsec_proto.h"
++
++#ifdef CONFIG_IPSEC_DEBUG
++int debug_pfkey = 0;
++extern int sysctl_ipsec_debug_verbose;
++#endif /* CONFIG_IPSEC_DEBUG */
++
++#define SENDERR(_x) do { error = -(_x); goto errlab; } while (0)
++
++#ifndef SOCKOPS_WRAPPED
++#define SOCKOPS_WRAPPED(name) name
++#endif /* SOCKOPS_WRAPPED */
++
++extern struct proto_ops pfkey_ops;
++struct sock *pfkey_sock_list = NULL;
++struct supported_list *pfkey_supported_list[SADB_SATYPE_MAX+1];
++
++struct socket_list *pfkey_open_sockets = NULL;
++struct socket_list *pfkey_registered_sockets[SADB_SATYPE_MAX+1];
++
++int pfkey_msg_interp(struct sock *, struct sadb_msg *, struct sadb_msg **);
++
++int
++pfkey_list_remove_socket(struct socket *socketp, struct socket_list **sockets)
++{
++ struct socket_list *socket_listp,*prev;
++
++ if(!socketp) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_list_remove_socket: "
++ "NULL socketp handed in, failed.\n");
++ return -EINVAL;
++ }
++
++ if(!sockets) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_list_remove_socket: "
++ "NULL sockets list handed in, failed.\n");
++ return -EINVAL;
++ }
++
++ socket_listp = *sockets;
++ prev = NULL;
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_list_remove_socket: "
++ "removing sock=0p%p\n",
++ socketp);
++
++ while(socket_listp != NULL) {
++ if(socket_listp->socketp == socketp) {
++ if(prev != NULL) {
++ prev->next = socket_listp->next;
++ } else {
++ *sockets = socket_listp->next;
++ }
++
++ kfree((void*)socket_listp);
++
++ break;
++ }
++ prev = socket_listp;
++ socket_listp = socket_listp->next;
++ }
++
++ return 0;
++}
++
++int
++pfkey_list_insert_socket(struct socket *socketp, struct socket_list **sockets)
++{
++ struct socket_list *socket_listp;
++
++ if(!socketp) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_list_insert_socket: "
++ "NULL socketp handed in, failed.\n");
++ return -EINVAL;
++ }
++
++ if(!sockets) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_list_insert_socket: "
++ "NULL sockets list handed in, failed.\n");
++ return -EINVAL;
++ }
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_list_insert_socket: "
++ "allocating %lu bytes for socketp=0p%p\n",
++ (unsigned long) sizeof(struct socket_list),
++ socketp);
++
++ if((socket_listp = (struct socket_list *)kmalloc(sizeof(struct socket_list), GFP_KERNEL)) == NULL) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_list_insert_socket: "
++ "memory allocation error.\n");
++ return -ENOMEM;
++ }
++
++ socket_listp->socketp = socketp;
++ socket_listp->next = *sockets;
++ *sockets = socket_listp;
++
++ return 0;
++}
++
++int
++pfkey_list_remove_supported(struct supported *supported, struct supported_list **supported_list)
++{
++ struct supported_list *supported_listp = *supported_list, *prev = NULL;
++
++ if(!supported) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_list_remove_supported: "
++ "NULL supported handed in, failed.\n");
++ return -EINVAL;
++ }
++
++ if(!supported_list) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_list_remove_supported: "
++ "NULL supported_list handed in, failed.\n");
++ return -EINVAL;
++ }
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_list_remove_supported: "
++ "removing supported=0p%p\n",
++ supported);
++
++ while(supported_listp != NULL) {
++ if(supported_listp->supportedp == supported) {
++ if(prev != NULL) {
++ prev->next = supported_listp->next;
++ } else {
++ *supported_list = supported_listp->next;
++ }
++
++ kfree((void*)supported_listp);
++
++ break;
++ }
++ prev = supported_listp;
++ supported_listp = supported_listp->next;
++ }
++
++ return 0;
++}
++
++int
++pfkey_list_insert_supported(struct supported *supported, struct supported_list **supported_list)
++{
++ struct supported_list *supported_listp;
++
++ if(!supported) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_list_insert_supported: "
++ "NULL supported handed in, failed.\n");
++ return -EINVAL;
++ }
++
++ if(!supported_list) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_list_insert_supported: "
++ "NULL supported_list handed in, failed.\n");
++ return -EINVAL;
++ }
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_list_insert_supported: "
++ "allocating %lu bytes for incoming, supported=0p%p, supported_list=0p%p\n",
++ (unsigned long) sizeof(struct supported_list),
++ supported,
++ supported_list);
++
++ supported_listp = (struct supported_list *)kmalloc(sizeof(struct supported_list), GFP_KERNEL);
++ if(supported_listp == NULL) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_list_insert_supported: "
++ "memory allocation error.\n");
++ return -ENOMEM;
++ }
++
++ supported_listp->supportedp = supported;
++ supported_listp->next = *supported_list;
++ *supported_list = supported_listp;
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_list_insert_supported: "
++ "outgoing, supported=0p%p, supported_list=0p%p\n",
++ supported,
++ supported_list);
++
++ return 0;
++}
++
++#ifndef NET_21
++DEBUG_NO_STATIC void
++pfkey_state_change(struct sock *sk)
++{
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_state_change: .\n");
++ if(!sk->dead) {
++ wake_up_interruptible(sk->sleep);
++ }
++}
++#endif /* !NET_21 */
++
++#ifndef NET_21
++DEBUG_NO_STATIC void
++pfkey_data_ready(struct sock *sk, int len)
++{
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_data_ready: "
++ "sk=0p%p len=%d\n",
++ sk,
++ len);
++ if(!sk->dead) {
++ wake_up_interruptible(sk->sleep);
++ sock_wake_async(sk->socket, 1);
++ }
++}
++
++DEBUG_NO_STATIC void
++pfkey_write_space(struct sock *sk)
++{
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_write_space: .\n");
++ if(!sk->dead) {
++ wake_up_interruptible(sk->sleep);
++ sock_wake_async(sk->socket, 2);
++ }
++}
++#endif /* !NET_21 */
++
++DEBUG_NO_STATIC void
++pfkey_insert_socket(struct sock *sk)
++{
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_insert_socket: "
++ "sk=0p%p\n",
++ sk);
++ cli();
++ sk->next=pfkey_sock_list;
++ pfkey_sock_list=sk;
++ sti();
++}
++
++DEBUG_NO_STATIC void
++pfkey_remove_socket(struct sock *sk)
++{
++ struct sock **s;
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_remove_socket: .\n");
++ cli();
++ s=&pfkey_sock_list;
++
++ while(*s!=NULL) {
++ if(*s==sk) {
++ *s=sk->next;
++ sk->next=NULL;
++ sti();
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_remove_socket: "
++ "succeeded.\n");
++ return;
++ }
++ s=&((*s)->next);
++ }
++ sti();
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_remove_socket: "
++ "not found.\n");
++ return;
++}
++
++DEBUG_NO_STATIC void
++pfkey_destroy_socket(struct sock *sk)
++{
++ struct sk_buff *skb;
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_destroy_socket: .\n");
++ pfkey_remove_socket(sk);
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_destroy_socket: "
++ "pfkey_remove_socket called.\n");
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_destroy_socket: "
++ "sk(0p%p)->(&0p%p)receive_queue.{next=0p%p,prev=0p%p}.\n",
++ sk,
++ &(sk->receive_queue),
++ sk->receive_queue.next,
++ sk->receive_queue.prev);
++ while(sk && ((skb=skb_dequeue(&(sk->receive_queue)))!=NULL)) {
++#ifdef NET_21
++#ifdef CONFIG_IPSEC_DEBUG
++ if(debug_pfkey && sysctl_ipsec_debug_verbose) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_destroy_socket: "
++ "skb=0p%p dequeued.\n", skb);
++ printk(KERN_INFO "klips_debug:pfkey_destroy_socket: "
++ "pfkey_skb contents:");
++ printk(" next:0p%p", skb->next);
++ printk(" prev:0p%p", skb->prev);
++ printk(" list:0p%p", skb->list);
++ printk(" sk:0p%p", skb->sk);
++ printk(" stamp:%ld.%ld", skb->stamp.tv_sec, skb->stamp.tv_usec);
++ printk(" dev:0p%p", skb->dev);
++ if(skb->dev) {
++ if(skb->dev->name) {
++ printk(" dev->name:%s", skb->dev->name);
++ } else {
++ printk(" dev->name:NULL?");
++ }
++ } else {
++ printk(" dev:NULL");
++ }
++ printk(" h:0p%p", skb->h.raw);
++ printk(" nh:0p%p", skb->nh.raw);
++ printk(" mac:0p%p", skb->mac.raw);
++ printk(" dst:0p%p", skb->dst);
++ if(sysctl_ipsec_debug_verbose) {
++ int i;
++
++ printk(" cb");
++ for(i=0; i<48; i++) {
++ printk(":%2x", skb->cb[i]);
++ }
++ }
++ printk(" len:%d", skb->len);
++ printk(" csum:%d", skb->csum);
++#ifndef NETDEV_23
++ printk(" used:%d", skb->used);
++ printk(" is_clone:%d", skb->is_clone);
++#endif /* NETDEV_23 */
++ printk(" cloned:%d", skb->cloned);
++ printk(" pkt_type:%d", skb->pkt_type);
++ printk(" ip_summed:%d", skb->ip_summed);
++ printk(" priority:%d", skb->priority);
++ printk(" protocol:%d", skb->protocol);
++ printk(" security:%d", skb->security);
++ printk(" truesize:%d", skb->truesize);
++ printk(" head:0p%p", skb->head);
++ printk(" data:0p%p", skb->data);
++ printk(" tail:0p%p", skb->tail);
++ printk(" end:0p%p", skb->end);
++ if(sysctl_ipsec_debug_verbose) {
++ unsigned char* i;
++ printk(" data");
++ for(i = skb->head; i < skb->end; i++) {
++ printk(":%2x", (unsigned char)(*(i)));
++ }
++ }
++ printk(" destructor:0p%p", skb->destructor);
++ printk("\n");
++ }
++#endif /* CONFIG_IPSEC_DEBUG */
++#endif /* NET_21 */
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_destroy_socket: "
++ "skb=0p%p freed.\n",
++ skb);
++ ipsec_kfree_skb(skb);
++ }
++
++ sk->dead = 1;
++ sk_free(sk);
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_destroy_socket: destroyed.\n");
++}
++
++int
++pfkey_upmsg(struct socket *sock, struct sadb_msg *pfkey_msg)
++{
++ int error = 0;
++ struct sk_buff * skb = NULL;
++ struct sock *sk;
++
++ if(sock == NULL) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_upmsg: "
++ "NULL socket passed in.\n");
++ return -EINVAL;
++ }
++
++ if(pfkey_msg == NULL) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_upmsg: "
++ "NULL pfkey_msg passed in.\n");
++ return -EINVAL;
++ }
++
++#ifdef NET_21
++ sk = sock->sk;
++#else /* NET_21 */
++ sk = sock->data;
++#endif /* NET_21 */
++
++ if(sk == NULL) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_upmsg: "
++ "NULL sock passed in.\n");
++ return -EINVAL;
++ }
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_upmsg: "
++ "allocating %d bytes...\n",
++ (int)(pfkey_msg->sadb_msg_len * IPSEC_PFKEYv2_ALIGN));
++ if(!(skb = alloc_skb(pfkey_msg->sadb_msg_len * IPSEC_PFKEYv2_ALIGN, GFP_ATOMIC) )) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_upmsg: "
++ "no buffers left to send up a message.\n");
++ return -ENOBUFS;
++ }
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_upmsg: "
++ "...allocated at 0p%p.\n",
++ skb);
++
++ skb->dev = NULL;
++
++ if(skb_tailroom(skb) < pfkey_msg->sadb_msg_len * IPSEC_PFKEYv2_ALIGN) {
++ printk(KERN_WARNING "klips_error:pfkey_upmsg: "
++ "tried to skb_put %ld, %d available. This should never happen, please report.\n",
++ (unsigned long int)pfkey_msg->sadb_msg_len * IPSEC_PFKEYv2_ALIGN,
++ skb_tailroom(skb));
++ ipsec_kfree_skb(skb);
++ return -ENOBUFS;
++ }
++ skb->h.raw = skb_put(skb, pfkey_msg->sadb_msg_len * IPSEC_PFKEYv2_ALIGN);
++ memcpy(skb->h.raw, pfkey_msg, pfkey_msg->sadb_msg_len * IPSEC_PFKEYv2_ALIGN);
++
++#ifndef NET_21
++ skb->free = 1;
++#endif /* !NET_21 */
++
++ if((error = sock_queue_rcv_skb(sk, skb)) < 0) {
++ skb->sk=NULL;
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_upmsg: "
++ "error=%d calling sock_queue_rcv_skb with skb=0p%p.\n",
++ error,
++ skb);
++ ipsec_kfree_skb(skb);
++ return error;
++ }
++ return error;
++}
++
++DEBUG_NO_STATIC int
++pfkey_create(struct socket *sock, int protocol)
++{
++ struct sock *sk;
++
++ if(sock == NULL) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_create: "
++ "socket NULL.\n");
++ return -EINVAL;
++ }
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_create: "
++ "sock=0p%p type:%d state:%d flags:%ld protocol:%d\n",
++ sock,
++ sock->type,
++ (unsigned int)(sock->state),
++ sock->flags, protocol);
++
++ if(sock->type != SOCK_RAW) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_create: "
++ "only SOCK_RAW supported.\n");
++ return -ESOCKTNOSUPPORT;
++ }
++
++ if(protocol != PF_KEY_V2) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_create: "
++ "protocol not PF_KEY_V2.\n");
++ return -EPROTONOSUPPORT;
++ }
++
++ if((current->uid != 0)) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_create: "
++ "must be root to open pfkey sockets.\n");
++ return -EACCES;
++ }
++
++#ifdef NET_21
++ sock->state = SS_UNCONNECTED;
++#endif /* NET_21 */
++ MOD_INC_USE_COUNT;
++#ifdef NET_21
++ if((sk=(struct sock *)sk_alloc(PF_KEY, GFP_KERNEL, 1)) == NULL)
++#else /* NET_21 */
++ if((sk=(struct sock *)sk_alloc(GFP_KERNEL)) == NULL)
++#endif /* NET_21 */
++ {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_create: "
++ "Out of memory trying to allocate.\n");
++ MOD_DEC_USE_COUNT;
++ return -ENOMEM;
++ }
++
++#ifndef NET_21
++ memset(sk, 0, sizeof(*sk));
++#endif /* !NET_21 */
++
++#ifdef NET_21
++ sock_init_data(sock, sk);
++
++ sk->destruct = NULL;
++ sk->reuse = 1;
++ sock->ops = &pfkey_ops;
++
++ sk->zapped=0;
++ sk->family = PF_KEY;
++/* sk->num = protocol; */
++ sk->protocol = protocol;
++ key_pid(sk) = current->pid;
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_create: "
++ "sock->fasync_list=0p%p sk->sleep=0p%p.\n",
++ sock->fasync_list,
++ sk->sleep);
++#else /* NET_21 */
++ sk->type=sock->type;
++ init_timer(&sk->timer);
++ skb_queue_head_init(&sk->write_queue);
++ skb_queue_head_init(&sk->receive_queue);
++ skb_queue_head_init(&sk->back_log);
++ sk->rcvbuf=SK_RMEM_MAX;
++ sk->sndbuf=SK_WMEM_MAX;
++ sk->allocation=GFP_KERNEL;
++ sk->state=TCP_CLOSE;
++ sk->priority=SOPRI_NORMAL;
++ sk->state_change=pfkey_state_change;
++ sk->data_ready=pfkey_data_ready;
++ sk->write_space=pfkey_write_space;
++ sk->error_report=pfkey_state_change;
++ sk->mtu=4096;
++ sk->socket=sock;
++ sock->data=(void *)sk;
++ sk->sleep=sock->wait;
++#endif /* NET_21 */
++
++ pfkey_insert_socket(sk);
++ pfkey_list_insert_socket(sock, &pfkey_open_sockets);
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_create: "
++ "Socket sock=0p%p sk=0p%p initialised.\n", sock, sk);
++ return 0;
++}
++
++#ifndef NET_21
++DEBUG_NO_STATIC int
++pfkey_dup(struct socket *newsock, struct socket *oldsock)
++{
++ struct sock *sk;
++
++ if(newsock==NULL) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_dup: "
++ "No new socket attached.\n");
++ return -EINVAL;
++ }
++
++ if(oldsock==NULL) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_dup: "
++ "No old socket attached.\n");
++ return -EINVAL;
++ }
++
++#ifdef NET_21
++ sk=oldsock->sk;
++#else /* NET_21 */
++ sk=oldsock->data;
++#endif /* NET_21 */
++
++ /* May not have data attached */
++ if(sk==NULL) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_dup: "
++ "No sock attached to old socket.\n");
++ return -EINVAL;
++ }
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_dup: .\n");
++
++ return pfkey_create(newsock, sk->protocol);
++}
++#endif /* !NET_21 */
++
++DEBUG_NO_STATIC int
++#ifdef NETDEV_23
++pfkey_release(struct socket *sock)
++#else /* NETDEV_23 */
++pfkey_release(struct socket *sock, struct socket *peersock)
++#endif /* NETDEV_23 */
++{
++ struct sock *sk;
++ int i;
++
++ if(sock==NULL) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_release: "
++ "No socket attached.\n");
++ return 0; /* -EINVAL; */
++ }
++
++#ifdef NET_21
++ sk=sock->sk;
++#else /* NET_21 */
++ sk=sock->data;
++#endif /* NET_21 */
++
++ /* May not have data attached */
++ if(sk==NULL) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_release: "
++ "No sk attached to sock=0p%p.\n", sock);
++ return 0; /* -EINVAL; */
++ }
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_release: "
++ "sock=0p%p sk=0p%p\n", sock, sk);
++
++#ifdef NET_21
++ if(!sk->dead)
++#endif /* NET_21 */
++ if(sk->state_change) {
++ sk->state_change(sk);
++ }
++
++#ifdef NET_21
++ sock->sk = NULL;
++#else /* NET_21 */
++ sock->data = NULL;
++#endif /* NET_21 */
++
++ /* Try to flush out this socket. Throw out buffers at least */
++ pfkey_destroy_socket(sk);
++ pfkey_list_remove_socket(sock, &pfkey_open_sockets);
++ for(i = SADB_SATYPE_UNSPEC; i <= SADB_SATYPE_MAX; i++) {
++ pfkey_list_remove_socket(sock, &(pfkey_registered_sockets[i]));
++ }
++
++ MOD_DEC_USE_COUNT;
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_release: "
++ "succeeded.\n");
++
++ return 0;
++}
++
++#ifndef NET_21
++DEBUG_NO_STATIC int
++pfkey_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
++{
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_bind: "
++ "operation not supported.\n");
++ return -EINVAL;
++}
++
++DEBUG_NO_STATIC int
++pfkey_connect(struct socket *sock, struct sockaddr *uaddr, int addr_len, int flags)
++{
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_connect: "
++ "operation not supported.\n");
++ return -EINVAL;
++}
++
++DEBUG_NO_STATIC int
++pfkey_socketpair(struct socket *a, struct socket *b)
++{
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_socketpair: "
++ "operation not supported.\n");
++ return -EINVAL;
++}
++
++DEBUG_NO_STATIC int
++pfkey_accept(struct socket *sock, struct socket *newsock, int flags)
++{
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_aaccept: "
++ "operation not supported.\n");
++ return -EINVAL;
++}
++
++DEBUG_NO_STATIC int
++pfkey_getname(struct socket *sock, struct sockaddr *uaddr, int *uaddr_len,
++ int peer)
++{
++ struct sockaddr *ska = (struct sockaddr*)uaddr;
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_getname: .\n");
++ ska->sa_family = PF_KEY;
++ *uaddr_len = sizeof(*ska);
++ return 0;
++}
++
++DEBUG_NO_STATIC int
++pfkey_select(struct socket *sock, int sel_type, select_table *wait)
++{
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_select: "
++ ".sock=0p%p sk=0p%p sel_type=%d\n",
++ sock,
++ sock->data,
++ sel_type);
++ if(sock == NULL) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_select: "
++ "Null socket passed in.\n");
++ return -EINVAL;
++ }
++ return datagram_select(sock->data, sel_type, wait);
++}
++
++DEBUG_NO_STATIC int
++pfkey_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
++{
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_ioctl: "
++ "not supported.\n");
++ return -EINVAL;
++}
++
++DEBUG_NO_STATIC int
++pfkey_listen(struct socket *sock, int backlog)
++{
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_listen: "
++ "not supported.\n");
++ return -EINVAL;
++}
++#endif /* !NET_21 */
++
++DEBUG_NO_STATIC int
++pfkey_shutdown(struct socket *sock, int mode)
++{
++ struct sock *sk;
++
++ if(sock == NULL) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_shutdown: "
++ "NULL socket passed in.\n");
++ return -EINVAL;
++ }
++
++#ifdef NET_21
++ sk=sock->sk;
++#else /* NET_21 */
++ sk=sock->data;
++#endif /* NET_21 */
++
++ if(sk == NULL) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_shutdown: "
++ "No sock attached to socket.\n");
++ return -EINVAL;
++ }
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_shutdown: "
++ "mode=%x.\n", mode);
++ mode++;
++
++ if(mode&SEND_SHUTDOWN) {
++ sk->shutdown|=SEND_SHUTDOWN;
++ sk->state_change(sk);
++ }
++
++ if(mode&RCV_SHUTDOWN) {
++ sk->shutdown|=RCV_SHUTDOWN;
++ sk->state_change(sk);
++ }
++ return 0;
++}
++
++#ifndef NET_21
++DEBUG_NO_STATIC int
++pfkey_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen)
++{
++#ifndef NET_21
++ struct sock *sk;
++
++ if(sock == NULL) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_setsockopt: "
++ "Null socket passed in.\n");
++ return -EINVAL;
++ }
++
++ sk=sock->data;
++
++ if(sk == NULL) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_setsockopt: "
++ "Null sock passed in.\n");
++ return -EINVAL;
++ }
++#endif /* !NET_21 */
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_setsockopt: .\n");
++ if(level!=SOL_SOCKET) {
++ return -EOPNOTSUPP;
++ }
++#ifdef NET_21
++ return sock_setsockopt(sock, level, optname, optval, optlen);
++#else /* NET_21 */
++ return sock_setsockopt(sk, level, optname, optval, optlen);
++#endif /* NET_21 */
++}
++
++DEBUG_NO_STATIC int
++pfkey_getsockopt(struct socket *sock, int level, int optname, char *optval, int *optlen)
++{
++#ifndef NET_21
++ struct sock *sk;
++
++ if(sock == NULL) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_setsockopt: "
++ "Null socket passed in.\n");
++ return -EINVAL;
++ }
++
++ sk=sock->data;
++
++ if(sk == NULL) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_setsockopt: "
++ "Null sock passed in.\n");
++ return -EINVAL;
++ }
++#endif /* !NET_21 */
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_getsockopt: .\n");
++ if(level!=SOL_SOCKET) {
++ return -EOPNOTSUPP;
++ }
++#ifdef NET_21
++ return sock_getsockopt(sock, level, optname, optval, optlen);
++#else /* NET_21 */
++ return sock_getsockopt(sk, level, optname, optval, optlen);
++#endif /* NET_21 */
++}
++
++DEBUG_NO_STATIC int
++pfkey_fcntl(struct socket *sock, unsigned int cmd, unsigned long arg)
++{
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_fcntl: "
++ "not supported.\n");
++ return -EINVAL;
++}
++#endif /* !NET_21 */
++
++/*
++ * Send PF_KEY data down.
++ */
++
++DEBUG_NO_STATIC int
++#ifdef NET_21
++pfkey_sendmsg(struct socket *sock, struct msghdr *msg, int len, struct scm_cookie *scm)
++#else /* NET_21 */
++pfkey_sendmsg(struct socket *sock, struct msghdr *msg, int len, int nonblock, int flags)
++#endif /* NET_21 */
++{
++ struct sock *sk;
++ int error = 0;
++ struct sadb_msg *pfkey_msg = NULL, *pfkey_reply = NULL;
++
++ if(sock == NULL) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_sendmsg: "
++ "Null socket passed in.\n");
++ SENDERR(EINVAL);
++ }
++
++#ifdef NET_21
++ sk = sock->sk;
++#else /* NET_21 */
++ sk = sock->data;
++#endif /* NET_21 */
++
++ if(sk == NULL) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_sendmsg: "
++ "Null sock passed in.\n");
++ SENDERR(EINVAL);
++ }
++
++ if(msg == NULL) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_sendmsg: "
++ "Null msghdr passed in.\n");
++ SENDERR(EINVAL);
++ }
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_sendmsg: .\n");
++ if(sk->err) {
++ error = sock_error(sk);
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_sendmsg: "
++ "sk->err is non-zero, returns %d.\n",
++ error);
++ SENDERR(-error);
++ }
++
++ if((current->uid != 0)) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_sendmsg: "
++ "must be root to send messages to pfkey sockets.\n");
++ SENDERR(EACCES);
++ }
++
++#ifdef NET_21
++ if(msg->msg_control)
++#else /* NET_21 */
++ if(flags || msg->msg_control)
++#endif /* NET_21 */
++ {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_sendmsg: "
++ "can't set flags or set msg_control.\n");
++ SENDERR(EINVAL);
++ }
++
++ if(sk->shutdown & SEND_SHUTDOWN) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_sendmsg: "
++ "shutdown.\n");
++ send_sig(SIGPIPE, current, 0);
++ SENDERR(EPIPE);
++ }
++
++ if(len < sizeof(struct sadb_msg)) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_sendmsg: "
++ "bogus msg len of %d, too small.\n", len);
++ SENDERR(EMSGSIZE);
++ }
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_sendmsg: "
++ "allocating %d bytes for downward message.\n",
++ len);
++ if((pfkey_msg = (struct sadb_msg*)kmalloc(len, GFP_KERNEL)) == NULL) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_sendmsg: "
++ "memory allocation error.\n");
++ SENDERR(ENOBUFS);
++ }
++
++ memcpy_fromiovec((void *)pfkey_msg, msg->msg_iov, len);
++
++ if(pfkey_msg->sadb_msg_version != PF_KEY_V2) {
++ KLIPS_PRINT(1 || debug_pfkey,
++ "klips_debug:pfkey_sendmsg: "
++ "not PF_KEY_V2 msg, found %d, should be %d.\n",
++ pfkey_msg->sadb_msg_version,
++ PF_KEY_V2);
++ kfree((void*)pfkey_msg);
++ return -EINVAL;
++ }
++
++ if(len != pfkey_msg->sadb_msg_len * IPSEC_PFKEYv2_ALIGN) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_sendmsg: "
++ "bogus msg len of %d, not %d byte aligned.\n",
++ len, (int)IPSEC_PFKEYv2_ALIGN);
++ SENDERR(EMSGSIZE);
++ }
++
++#if 0
++ /* This check is questionable, since a downward message could be
++ the result of an ACQUIRE either from kernel (PID==0) or
++ userspace (some other PID). */
++ /* check PID */
++ if(pfkey_msg->sadb_msg_pid != current->pid) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_sendmsg: "
++ "pid (%d) does not equal sending process pid (%d).\n",
++ pfkey_msg->sadb_msg_pid, current->pid);
++ SENDERR(EINVAL);
++ }
++#endif
++
++ if(pfkey_msg->sadb_msg_reserved) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_sendmsg: "
++ "reserved field must be zero, set to %d.\n",
++ pfkey_msg->sadb_msg_reserved);
++ SENDERR(EINVAL);
++ }
++
++ if((pfkey_msg->sadb_msg_type > SADB_MAX) || (!pfkey_msg->sadb_msg_type)){
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_sendmsg: "
++ "msg type too large or small:%d.\n",
++ pfkey_msg->sadb_msg_type);
++ SENDERR(EINVAL);
++ }
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_sendmsg: "
++ "msg sent for parsing.\n");
++
++ if((error = pfkey_msg_interp(sk, pfkey_msg, &pfkey_reply))) {
++ struct socket_list *pfkey_socketsp;
++
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_sendmsg: "
++ "pfkey_msg_parse returns %d.\n",
++ error);
++
++ if((pfkey_reply = (struct sadb_msg*)kmalloc(sizeof(struct sadb_msg), GFP_KERNEL)) == NULL) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_sendmsg: "
++ "memory allocation error.\n");
++ SENDERR(ENOBUFS);
++ }
++ memcpy((void*)pfkey_reply, (void*)pfkey_msg, sizeof(struct sadb_msg));
++ pfkey_reply->sadb_msg_errno = -error;
++ pfkey_reply->sadb_msg_len = sizeof(struct sadb_msg) / IPSEC_PFKEYv2_ALIGN;
++
++ for(pfkey_socketsp = pfkey_open_sockets;
++ pfkey_socketsp;
++ pfkey_socketsp = pfkey_socketsp->next) {
++ int error_upmsg = 0;
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_sendmsg: "
++ "sending up error=%d message=0p%p to socket=0p%p.\n",
++ error,
++ pfkey_reply,
++ pfkey_socketsp->socketp);
++ if((error_upmsg = pfkey_upmsg(pfkey_socketsp->socketp, pfkey_reply))) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_sendmsg: "
++ "sending up error message to socket=0p%p failed with error=%d.\n",
++ pfkey_socketsp->socketp,
++ error_upmsg);
++ /* pfkey_msg_free(&pfkey_reply); */
++ /* SENDERR(-error); */
++ }
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_sendmsg: "
++ "sending up error message to socket=0p%p succeeded.\n",
++ pfkey_socketsp->socketp);
++ }
++
++ pfkey_msg_free(&pfkey_reply);
++
++ SENDERR(-error);
++ }
++
++ errlab:
++ if (pfkey_msg) {
++ kfree((void*)pfkey_msg);
++ }
++
++ if(error) {
++ return error;
++ } else {
++ return len;
++ }
++}
++
++/*
++ * Receive PF_KEY data up.
++ */
++
++DEBUG_NO_STATIC int
++#ifdef NET_21
++pfkey_recvmsg(struct socket *sock, struct msghdr *msg, int size, int flags, struct scm_cookie *scm)
++#else /* NET_21 */
++pfkey_recvmsg(struct socket *sock, struct msghdr *msg, int size, int noblock, int flags, int *addr_len)
++#endif /* NET_21 */
++{
++ struct sock *sk;
++#ifdef NET_21
++ int noblock = flags & MSG_DONTWAIT;
++#endif /* NET_21 */
++ struct sk_buff *skb;
++ int error;
++
++ if(sock == NULL) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_recvmsg: "
++ "Null socket passed in.\n");
++ return -EINVAL;
++ }
++
++#ifdef NET_21
++ sk = sock->sk;
++#else /* NET_21 */
++ sk = sock->data;
++#endif /* NET_21 */
++
++ if(sk == NULL) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_recvmsg: "
++ "Null sock passed in for sock=0p%p.\n", sock);
++ return -EINVAL;
++ }
++
++ if(msg == NULL) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_recvmsg: "
++ "Null msghdr passed in for sock=0p%p, sk=0p%p.\n",
++ sock, sk);
++ return -EINVAL;
++ }
++
++ KLIPS_PRINT(debug_pfkey && sysctl_ipsec_debug_verbose,
++ "klips_debug:pfkey_recvmsg: sock=0p%p sk=0p%p msg=0p%p size=%d.\n",
++ sock, sk, msg, size);
++ if(flags & ~MSG_PEEK) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_sendmsg: "
++ "flags (%d) other than MSG_PEEK not supported.\n",
++ flags);
++ return -EOPNOTSUPP;
++ }
++
++#ifdef NET_21
++ msg->msg_namelen = 0; /* sizeof(*ska); */
++#else /* NET_21 */
++ if(addr_len) {
++ *addr_len = 0; /* sizeof(*ska); */
++ }
++#endif /* NET_21 */
++
++ if(sk->err) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_sendmsg: "
++ "sk->err=%d.\n", sk->err);
++ return sock_error(sk);
++ }
++
++ if((skb = skb_recv_datagram(sk, flags, noblock, &error) ) == NULL) {
++ return error;
++ }
++
++ if(size > skb->len) {
++ size = skb->len;
++ }
++#ifdef NET_21
++ else if(size <skb->len) {
++ msg->msg_flags |= MSG_TRUNC;
++ }
++#endif /* NET_21 */
++
++ skb_copy_datagram_iovec(skb, 0, msg->msg_iov, size);
++ sk->stamp=skb->stamp;
++
++ skb_free_datagram(sk, skb);
++ return size;
++}
++
++#ifdef NET_21
++struct net_proto_family pfkey_family_ops = {
++ PF_KEY,
++ pfkey_create
++};
++
++struct proto_ops SOCKOPS_WRAPPED(pfkey_ops) = {
++#ifdef NETDEV_23
++ family: PF_KEY,
++ release: pfkey_release,
++ bind: sock_no_bind,
++ connect: sock_no_connect,
++ socketpair: sock_no_socketpair,
++ accept: sock_no_accept,
++ getname: sock_no_getname,
++ poll: datagram_poll,
++ ioctl: sock_no_ioctl,
++ listen: sock_no_listen,
++ shutdown: pfkey_shutdown,
++ setsockopt: sock_no_setsockopt,
++ getsockopt: sock_no_getsockopt,
++ sendmsg: pfkey_sendmsg,
++ recvmsg: pfkey_recvmsg,
++ mmap: sock_no_mmap,
++#else /* NETDEV_23 */
++ PF_KEY,
++ sock_no_dup,
++ pfkey_release,
++ sock_no_bind,
++ sock_no_connect,
++ sock_no_socketpair,
++ sock_no_accept,
++ sock_no_getname,
++ datagram_poll,
++ sock_no_ioctl,
++ sock_no_listen,
++ pfkey_shutdown,
++ sock_no_setsockopt,
++ sock_no_getsockopt,
++ sock_no_fcntl,
++ pfkey_sendmsg,
++ pfkey_recvmsg
++#endif /* NETDEV_23 */
++};
++
++#ifdef NETDEV_23
++#include <linux/smp_lock.h>
++SOCKOPS_WRAP(pfkey, PF_KEY);
++#endif /* NETDEV_23 */
++
++#else /* NET_21 */
++struct proto_ops pfkey_proto_ops = {
++ PF_KEY,
++ pfkey_create,
++ pfkey_dup,
++ pfkey_release,
++ pfkey_bind,
++ pfkey_connect,
++ pfkey_socketpair,
++ pfkey_accept,
++ pfkey_getname,
++ pfkey_select,
++ pfkey_ioctl,
++ pfkey_listen,
++ pfkey_shutdown,
++ pfkey_setsockopt,
++ pfkey_getsockopt,
++ pfkey_fcntl,
++ pfkey_sendmsg,
++ pfkey_recvmsg
++};
++#endif /* NET_21 */
++
++#ifdef CONFIG_PROC_FS
++#ifndef PROC_FS_2325
++DEBUG_NO_STATIC
++#endif /* PROC_FS_2325 */
++int
++pfkey_get_info(char *buffer, char **start, off_t offset, int length
++#ifndef PROC_NO_DUMMY
++, int dummy
++#endif /* !PROC_NO_DUMMY */
++)
++{
++ const int max_content = length > 0? length-1 : 0;
++ off_t begin=0;
++ int len=0;
++ struct sock *sk=pfkey_sock_list;
++
++#ifdef CONFIG_IPSEC_DEBUG
++ if(!sysctl_ipsec_debug_verbose) {
++#endif /* CONFIG_IPSEC_DEBUG */
++ len+= snprintf(buffer,length,
++ " sock pid socket next prev e n p sndbf Flags Type St\n");
++#ifdef CONFIG_IPSEC_DEBUG
++ } else {
++ len+= snprintf(buffer,length,
++ " sock pid d sleep socket next prev e r z n p sndbf stamp Flags Type St\n");
++ }
++#endif /* CONFIG_IPSEC_DEBUG */
++
++ while(sk!=NULL) {
++#ifdef CONFIG_IPSEC_DEBUG
++ if(!sysctl_ipsec_debug_verbose) {
++#endif /* CONFIG_IPSEC_DEBUG */
++ len += ipsec_snprintf(buffer+len, length-len,
++ "%8p %5d %8p %8p %8p %d %d %d %5d %08lX %8X %2X\n",
++ sk,
++ key_pid(sk),
++ sk->socket,
++ sk->next,
++ sk->prev,
++ sk->err,
++ sk->num,
++ sk->protocol,
++ sk->sndbuf,
++ sk->socket->flags,
++ sk->socket->type,
++ sk->socket->state);
++#ifdef CONFIG_IPSEC_DEBUG
++ } else {
++ len += ipsec_snprintf(buffer+len, length-len,
++ "%8p %5d %d %8p %8p %8p %8p %d %d %d %d %d %5d %d.%06d %08lX %8X %2X\n",
++ sk,
++ key_pid(sk),
++ sk->dead,
++ sk->sleep,
++ sk->socket,
++ sk->next,
++ sk->prev,
++ sk->err,
++ sk->reuse,
++ sk->zapped,
++ sk->num,
++ sk->protocol,
++ sk->sndbuf,
++ (unsigned int)sk->stamp.tv_sec,
++ (unsigned int)sk->stamp.tv_usec,
++ sk->socket->flags,
++ sk->socket->type,
++ sk->socket->state);
++ }
++#endif /* CONFIG_IPSEC_DEBUG */
++
++ if (len >= max_content) {
++ /* we've done all that can fit -- stop loop */
++ len = max_content; /* truncate crap */
++ break;
++ } else {
++
++ const off_t pos = begin + len; /* file position of end of what we've generated */
++
++ if (pos <= offset) {
++ /* all is before first interesting character:
++ * discard, but note where we are.
++ */
++ len = 0;
++ begin = pos;
++ }
++ }
++ sk=sk->next;
++
++ }
++
++ *start = buffer + (offset - begin); /* Start of wanted data */
++ return len - (offset - begin);
++
++}
++
++#ifndef PROC_FS_2325
++DEBUG_NO_STATIC
++#endif /* PROC_FS_2325 */
++int
++pfkey_supported_get_info(char *buffer, char **start, off_t offset, int length
++#ifndef PROC_NO_DUMMY
++, int dummy
++#endif /* !PROC_NO_DUMMY */
++)
++{
++ const int max_content = length > 0? length-1 : 0;
++ off_t begin=0;
++ int len=0;
++ int satype;
++ struct supported_list *pfkey_supported_p;
++
++ len += ipsec_snprintf(buffer, length,
++ "satype exttype alg_id ivlen minbits maxbits\n");
++
++ for(satype = SADB_SATYPE_UNSPEC; satype <= SADB_SATYPE_MAX; satype++) {
++ pfkey_supported_p = pfkey_supported_list[satype];
++ while(pfkey_supported_p) {
++ len += ipsec_snprintf(buffer+len, length-len,
++ " %2d %2d %2d %3d %3d %3d\n",
++ satype,
++ pfkey_supported_p->supportedp->supported_alg_exttype,
++ pfkey_supported_p->supportedp->supported_alg_id,
++ pfkey_supported_p->supportedp->supported_alg_ivlen,
++ pfkey_supported_p->supportedp->supported_alg_minbits,
++ pfkey_supported_p->supportedp->supported_alg_maxbits);
++
++ if (len >= max_content) {
++ /* we've done all that can fit -- stop loop */
++ len = max_content; /* truncate crap */
++ break;
++ } else {
++ const off_t pos = begin + len; /* file position of end of what we've generated */
++
++ if (pos <= offset) {
++ /* all is before first interesting character:
++ * discard, but note where we are.
++ */
++ len = 0;
++ begin = pos;
++ }
++ }
++
++ pfkey_supported_p = pfkey_supported_p->next;
++ }
++ }
++
++ *start = buffer + (offset - begin); /* Start of wanted data */
++ return len - (offset - begin);
++
++}
++
++#ifndef PROC_FS_2325
++DEBUG_NO_STATIC
++#endif /* PROC_FS_2325 */
++int
++pfkey_registered_get_info(char *buffer, char **start, off_t offset, int length
++#ifndef PROC_NO_DUMMY
++, int dummy
++#endif /* !PROC_NO_DUMMY */
++)
++{
++ const int max_content = length > 0? length-1 : 0;
++ off_t begin=0;
++ int len=0;
++ int satype;
++ struct socket_list *pfkey_sockets;
++
++ len += ipsec_snprintf(buffer, length,
++ "satype socket pid sk\n");
++
++ for(satype = SADB_SATYPE_UNSPEC; satype <= SADB_SATYPE_MAX; satype++) {
++ pfkey_sockets = pfkey_registered_sockets[satype];
++ while(pfkey_sockets) {
++#ifdef NET_21
++ len += ipsec_snprintf(buffer+len, length-len,
++ " %2d %8p %5d %8p\n",
++ satype,
++ pfkey_sockets->socketp,
++ key_pid(pfkey_sockets->socketp->sk),
++ pfkey_sockets->socketp->sk);
++#else /* NET_21 */
++ len += ipsec_snprintf(buffer+len, length-len,
++ " %2d %8p N/A %8p\n",
++ satype,
++ pfkey_sockets->socketp,
++#if 0
++ key_pid((pfkey_sockets->socketp)->data),
++#endif
++ (pfkey_sockets->socketp)->data);
++#endif /* NET_21 */
++
++ if (len >= max_content) {
++ /* we've done all that can fit -- stop loop (could stop two) */
++ len = max_content; /* truncate crap */
++ break;
++ } else {
++ const off_t pos = begin + len; /* file position of end of what we've generated */
++
++ if (pos <= offset) {
++ /* all is before first interesting character:
++ * discard, but note where we are.
++ */
++ len = 0;
++ begin = pos;
++ }
++ }
++
++
++ pfkey_sockets = pfkey_sockets->next;
++ }
++ }
++ *start = buffer + (offset - begin); /* Start of wanted data */
++ return len - (offset - begin);
++}
++
++#ifndef PROC_FS_2325
++struct proc_dir_entry proc_net_pfkey =
++{
++ 0,
++ 6, "pf_key",
++ S_IFREG | S_IRUGO, 1, 0, 0,
++ 0, &proc_net_inode_operations,
++ pfkey_get_info
++};
++struct proc_dir_entry proc_net_pfkey_supported =
++{
++ 0,
++ 16, "pf_key_supported",
++ S_IFREG | S_IRUGO, 1, 0, 0,
++ 0, &proc_net_inode_operations,
++ pfkey_supported_get_info
++};
++struct proc_dir_entry proc_net_pfkey_registered =
++{
++ 0,
++ 17, "pf_key_registered",
++ S_IFREG | S_IRUGO, 1, 0, 0,
++ 0, &proc_net_inode_operations,
++ pfkey_registered_get_info
++};
++#endif /* !PROC_FS_2325 */
++#endif /* CONFIG_PROC_FS */
++
++DEBUG_NO_STATIC int
++supported_add_all(int satype, struct supported supported[], int size)
++{
++ int i;
++ int error = 0;
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:init_pfkey: "
++ "sizeof(supported_init_<satype=%d>)[%d]/sizeof(struct supported)[%d]=%d.\n",
++ satype,
++ size,
++ (int)sizeof(struct supported),
++ (int)(size/sizeof(struct supported)));
++
++ for(i = 0; i < size / sizeof(struct supported); i++) {
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:init_pfkey: "
++ "i=%d inserting satype=%d exttype=%d id=%d ivlen=%d minbits=%d maxbits=%d.\n",
++ i,
++ satype,
++ supported[i].supported_alg_exttype,
++ supported[i].supported_alg_id,
++ supported[i].supported_alg_ivlen,
++ supported[i].supported_alg_minbits,
++ supported[i].supported_alg_maxbits);
++
++ error |= pfkey_list_insert_supported(&(supported[i]),
++ &(pfkey_supported_list[satype]));
++ }
++ return error;
++}
++
++DEBUG_NO_STATIC int
++supported_remove_all(int satype)
++{
++ int error = 0;
++ struct supported*supportedp;
++
++ while(pfkey_supported_list[satype]) {
++ supportedp = pfkey_supported_list[satype]->supportedp;
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:init_pfkey: "
++ "removing satype=%d exttype=%d id=%d ivlen=%d minbits=%d maxbits=%d.\n",
++ satype,
++ supportedp->supported_alg_exttype,
++ supportedp->supported_alg_id,
++ supportedp->supported_alg_ivlen,
++ supportedp->supported_alg_minbits,
++ supportedp->supported_alg_maxbits);
++
++ error |= pfkey_list_remove_supported(supportedp,
++ &(pfkey_supported_list[satype]));
++ }
++ return error;
++}
++
++int
++pfkey_init(void)
++{
++ int error = 0;
++ int i;
++
++ static struct supported supported_init_ah[] = {
++#ifdef CONFIG_IPSEC_AUTH_HMAC_MD5
++ {SADB_EXT_SUPPORTED_AUTH, SADB_AALG_MD5HMAC, 0, 128, 128},
++#endif /* CONFIG_IPSEC_AUTH_HMAC_MD5 */
++#ifdef CONFIG_IPSEC_AUTH_HMAC_SHA1
++ {SADB_EXT_SUPPORTED_AUTH, SADB_AALG_SHA1HMAC, 0, 160, 160}
++#endif /* CONFIG_IPSEC_AUTH_HMAC_SHA1 */
++ };
++ static struct supported supported_init_esp[] = {
++#ifdef CONFIG_IPSEC_AUTH_HMAC_MD5
++ {SADB_EXT_SUPPORTED_AUTH, SADB_AALG_MD5HMAC, 0, 128, 128},
++#endif /* CONFIG_IPSEC_AUTH_HMAC_MD5 */
++#ifdef CONFIG_IPSEC_AUTH_HMAC_SHA1
++ {SADB_EXT_SUPPORTED_AUTH, SADB_AALG_SHA1HMAC, 0, 160, 160},
++#endif /* CONFIG_IPSEC_AUTH_HMAC_SHA1 */
++#ifdef CONFIG_IPSEC_ENC_3DES
++ {SADB_EXT_SUPPORTED_ENCRYPT, SADB_EALG_3DESCBC, 64, 168, 168},
++#endif /* CONFIG_IPSEC_ENC_3DES */
++ };
++ static struct supported supported_init_ipip[] = {
++ {SADB_EXT_SUPPORTED_ENCRYPT, SADB_X_TALG_IPv4_in_IPv4, 0, 32, 32}
++#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
++ , {SADB_EXT_SUPPORTED_ENCRYPT, SADB_X_TALG_IPv6_in_IPv4, 0, 128, 32}
++ , {SADB_EXT_SUPPORTED_ENCRYPT, SADB_X_TALG_IPv4_in_IPv6, 0, 32, 128}
++ , {SADB_EXT_SUPPORTED_ENCRYPT, SADB_X_TALG_IPv6_in_IPv6, 0, 128, 128}
++#endif /* defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) */
++ };
++#ifdef CONFIG_IPSEC_IPCOMP
++ static struct supported supported_init_ipcomp[] = {
++ {SADB_EXT_SUPPORTED_ENCRYPT, SADB_X_CALG_DEFLATE, 0, 1, 1}
++ };
++#endif /* CONFIG_IPSEC_IPCOMP */
++
++#if 0
++ printk(KERN_INFO
++ "klips_info:pfkey_init: "
++ "FreeS/WAN: initialising PF_KEYv2 domain sockets.\n");
++#endif
++
++ for(i = SADB_SATYPE_UNSPEC; i <= SADB_SATYPE_MAX; i++) {
++ pfkey_registered_sockets[i] = NULL;
++ pfkey_supported_list[i] = NULL;
++ }
++
++ error |= supported_add_all(SADB_SATYPE_AH, supported_init_ah, sizeof(supported_init_ah));
++ error |= supported_add_all(SADB_SATYPE_ESP, supported_init_esp, sizeof(supported_init_esp));
++#ifdef CONFIG_IPSEC_IPCOMP
++ error |= supported_add_all(SADB_X_SATYPE_COMP, supported_init_ipcomp, sizeof(supported_init_ipcomp));
++#endif /* CONFIG_IPSEC_IPCOMP */
++ error |= supported_add_all(SADB_X_SATYPE_IPIP, supported_init_ipip, sizeof(supported_init_ipip));
++
++#ifdef NET_21
++ error |= sock_register(&pfkey_family_ops);
++#else /* NET_21 */
++ error |= sock_register(pfkey_proto_ops.family, &pfkey_proto_ops);
++#endif /* NET_21 */
++
++#ifdef CONFIG_PROC_FS
++# ifndef PROC_FS_2325
++# ifdef PROC_FS_21
++ error |= proc_register(proc_net, &proc_net_pfkey);
++ error |= proc_register(proc_net, &proc_net_pfkey_supported);
++ error |= proc_register(proc_net, &proc_net_pfkey_registered);
++# else /* PROC_FS_21 */
++ error |= proc_register_dynamic(&proc_net, &proc_net_pfkey);
++ error |= proc_register_dynamic(&proc_net, &proc_net_pfkey_supported);
++ error |= proc_register_dynamic(&proc_net, &proc_net_pfkey_registered);
++# endif /* PROC_FS_21 */
++# else /* !PROC_FS_2325 */
++ proc_net_create ("pf_key", 0, pfkey_get_info);
++ proc_net_create ("pf_key_supported", 0, pfkey_supported_get_info);
++ proc_net_create ("pf_key_registered", 0, pfkey_registered_get_info);
++# endif /* !PROC_FS_2325 */
++#endif /* CONFIG_PROC_FS */
++
++ return error;
++}
++
++int
++pfkey_cleanup(void)
++{
++ int error = 0;
++
++ printk(KERN_INFO "klips_info:pfkey_cleanup: "
++ "shutting down PF_KEY domain sockets.\n");
++#ifdef NET_21
++ error |= sock_unregister(PF_KEY);
++#else /* NET_21 */
++ error |= sock_unregister(pfkey_proto_ops.family);
++#endif /* NET_21 */
++
++ error |= supported_remove_all(SADB_SATYPE_AH);
++ error |= supported_remove_all(SADB_SATYPE_ESP);
++#ifdef CONFIG_IPSEC_IPCOMP
++ error |= supported_remove_all(SADB_X_SATYPE_COMP);
++#endif /* CONFIG_IPSEC_IPCOMP */
++ error |= supported_remove_all(SADB_X_SATYPE_IPIP);
++
++#ifdef CONFIG_PROC_FS
++# ifndef PROC_FS_2325
++ if (proc_net_unregister(proc_net_pfkey.low_ino) != 0)
++ printk("klips_debug:pfkey_cleanup: "
++ "cannot unregister /proc/net/pf_key\n");
++ if (proc_net_unregister(proc_net_pfkey_supported.low_ino) != 0)
++ printk("klips_debug:pfkey_cleanup: "
++ "cannot unregister /proc/net/pf_key_supported\n");
++ if (proc_net_unregister(proc_net_pfkey_registered.low_ino) != 0)
++ printk("klips_debug:pfkey_cleanup: "
++ "cannot unregister /proc/net/pf_key_registered\n");
++# else /* !PROC_FS_2325 */
++ proc_net_remove ("pf_key");
++ proc_net_remove ("pf_key_supported");
++ proc_net_remove ("pf_key_registered");
++# endif /* !PROC_FS_2325 */
++#endif /* CONFIG_PROC_FS */
++
++ /* other module unloading cleanup happens here */
++ return error;
++}
++
++#ifdef MODULE
++#if 0
++int
++init_module(void)
++{
++ pfkey_init();
++ return 0;
++}
++
++void
++cleanup_module(void)
++{
++ pfkey_cleanup();
++}
++#endif /* 0 */
++#else /* MODULE */
++void
++pfkey_proto_init(struct net_proto *pro)
++{
++ pfkey_init();
++}
++#endif /* MODULE */
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.81 2004/04/25 21:23:11 ken
++ * Pull in dhr's changes from FreeS/WAN 2.06
++ *
++ * Revision 1.80 2004/04/06 02:49:26 mcr
++ * pullup of algo code from alg-branch.
++ *
++ * Revision 1.79.4.1 2003/12/22 15:25:52 jjo
++ * . Merged algo-0.8.1-rc11-test1 into alg-branch
++ *
++ * Revision 1.79 2003/10/31 02:27:55 mcr
++ * pulled up port-selector patches and sa_id elimination.
++ *
++ * Revision 1.78.4.1 2003/10/29 01:30:41 mcr
++ * elimited "struct sa_id".
++ *
++ * Revision 1.78 2003/04/03 17:38:09 rgb
++ * Centralised ipsec_kfree_skb and ipsec_dev_{get,put}.
++ *
++ * Revision 1.77 2002/10/17 16:49:36 mcr
++ * sock->ops should reference the unwrapped options so that
++ * we get hacked in locking on SMP systems.
++ *
++ * Revision 1.76 2002/10/12 23:11:53 dhr
++ *
++ * [KenB + DHR] more 64-bit cleanup
++ *
++ * Revision 1.75 2002/09/20 05:01:57 rgb
++ * Added memory allocation debugging.
++ *
++ * Revision 1.74 2002/09/19 02:42:50 mcr
++ * do not define the pfkey_ops function for now.
++ *
++ * Revision 1.73 2002/09/17 17:29:23 mcr
++ * #if 0 out some dead code - pfkey_ops is never used as written.
++ *
++ * Revision 1.72 2002/07/24 18:44:54 rgb
++ * Type fiddling to tame ia64 compiler.
++ *
++ * Revision 1.71 2002/05/23 07:14:11 rgb
++ * Cleaned up %p variants to 0p%p for test suite cleanup.
++ *
++ * Revision 1.70 2002/04/24 07:55:32 mcr
++ * #include patches and Makefiles for post-reorg compilation.
++ *
++ * Revision 1.69 2002/04/24 07:36:33 mcr
++ * Moved from ./klips/net/ipsec/pfkey_v2.c,v
++ *
++ * Revision 1.68 2002/03/08 01:15:17 mcr
++ * put some internal structure only debug messages behind
++ * && sysctl_ipsec_debug_verbose.
++ *
++ * Revision 1.67 2002/01/29 17:17:57 mcr
++ * moved include of ipsec_param.h to after include of linux/kernel.h
++ * otherwise, it seems that some option that is set in ipsec_param.h
++ * screws up something subtle in the include path to kernel.h, and
++ * it complains on the snprintf() prototype.
++ *
++ * Revision 1.66 2002/01/29 04:00:54 mcr
++ * more excise of kversions.h header.
++ *
++ * Revision 1.65 2002/01/29 02:13:18 mcr
++ * introduction of ipsec_kversion.h means that include of
++ * ipsec_param.h must preceed any decisions about what files to
++ * include to deal with differences in kernel source.
++ *
++ * Revision 1.64 2001/11/26 09:23:51 rgb
++ * Merge MCR's ipsec_sa, eroute, proc and struct lifetime changes.
++ *
++ * Revision 1.61.2.1 2001/09/25 02:28:44 mcr
++ * cleaned up includes.
++ *
++ * Revision 1.63 2001/11/12 19:38:00 rgb
++ * Continue trying other sockets even if one fails and return only original
++ * error.
++ *
++ * Revision 1.62 2001/10/18 04:45:22 rgb
++ * 2.4.9 kernel deprecates linux/malloc.h in favour of linux/slab.h,
++ * lib/freeswan.h version macros moved to lib/kversions.h.
++ * Other compiler directive cleanups.
++ *
++ * Revision 1.61 2001/09/20 15:32:59 rgb
++ * Min/max cleanup.
++ *
++ * Revision 1.60 2001/06/14 19:35:12 rgb
++ * Update copyright date.
++ *
++ * Revision 1.59 2001/06/13 15:35:48 rgb
++ * Fixed #endif comments.
++ *
++ * Revision 1.58 2001/05/04 16:37:24 rgb
++ * Remove erroneous checking of return codes for proc_net_* in 2.4.
++ *
++ * Revision 1.57 2001/05/03 19:43:36 rgb
++ * Initialise error return variable.
++ * Check error return codes in startup and shutdown.
++ * Standardise on SENDERR() macro.
++ *
++ * Revision 1.56 2001/04/21 23:05:07 rgb
++ * Define out skb->used for 2.4 kernels.
++ *
++ * Revision 1.55 2001/02/28 05:03:28 rgb
++ * Clean up and rationalise startup messages.
++ *
++ * Revision 1.54 2001/02/27 22:24:55 rgb
++ * Re-formatting debug output (line-splitting, joining, 1arg/line).
++ * Check for satoa() return codes.
++ *
++ * Revision 1.53 2001/02/27 06:48:18 rgb
++ * Fixed pfkey socket unregister log message to reflect type and function.
++ *
++ * Revision 1.52 2001/02/26 22:34:38 rgb
++ * Fix error return code that was getting overwritten by the error return
++ * code of an upmsg.
++ *
++ * Revision 1.51 2001/01/30 23:42:47 rgb
++ * Allow pfkey msgs from pid other than user context required for ACQUIRE
++ * and subsequent ADD or UDATE.
++ *
++ * Revision 1.50 2001/01/23 20:22:59 rgb
++ * 2.4 fix to remove removed is_clone member.
++ *
++ * Revision 1.49 2000/11/06 04:33:47 rgb
++ * Changed non-exported functions to DEBUG_NO_STATIC.
++ *
++ * Revision 1.48 2000/09/29 19:47:41 rgb
++ * Update copyright.
++ *
++ * Revision 1.47 2000/09/22 04:23:04 rgb
++ * Added more debugging to pfkey_upmsg() call from pfkey_sendmsg() error.
++ *
++ * Revision 1.46 2000/09/21 04:20:44 rgb
++ * Fixed array size off-by-one error. (Thanks Svenning!)
++ *
++ * Revision 1.45 2000/09/20 04:01:26 rgb
++ * Changed static functions to DEBUG_NO_STATIC for revealing function names
++ * in oopsen.
++ *
++ * Revision 1.44 2000/09/19 00:33:17 rgb
++ * 2.0 fixes.
++ *
++ * Revision 1.43 2000/09/16 01:28:13 rgb
++ * Fixed use of 0 in p format warning.
++ *
++ * Revision 1.42 2000/09/16 01:09:41 rgb
++ * Fixed debug format warning for pointers that was expecting ints.
++ *
++ * Revision 1.41 2000/09/13 15:54:00 rgb
++ * Rewrote pfkey_get_info(), added pfkey_{supported,registered}_get_info().
++ * Moved supported algos add and remove to functions.
++ *
++ * Revision 1.40 2000/09/12 18:49:28 rgb
++ * Added IPIP tunnel and IPCOMP register support.
++ *
++ * Revision 1.39 2000/09/12 03:23:49 rgb
++ * Converted #if0 debugs to sysctl.
++ * Removed debug_pfkey initialisations that prevented no_debug loading or
++ * linking.
++ *
++ * Revision 1.38 2000/09/09 06:38:02 rgb
++ * Return positive errno in pfkey_reply error message.
++ *
++ * Revision 1.37 2000/09/08 19:19:09 rgb
++ * Change references from DEBUG_IPSEC to CONFIG_IPSEC_DEBUG.
++ * Clean-up of long-unused crud...
++ * Create pfkey error message on on failure.
++ * Give pfkey_list_{insert,remove}_{socket,supported}() some error
++ * checking.
++ *
++ * Revision 1.36 2000/09/01 18:49:38 rgb
++ * Reap experimental NET_21_ bits.
++ * Turned registered sockets list into an array of one list per satype.
++ * Remove references to deprecated sklist_{insert,remove}_socket.
++ * Removed leaking socket debugging code.
++ * Removed duplicate pfkey_insert_socket in pfkey_create.
++ * Removed all references to pfkey msg->msg_name, since it is not used for
++ * pfkey.
++ * Added a supported algorithms array lists, one per satype and registered
++ * existing algorithms.
++ * Fixed pfkey_list_{insert,remove}_{socket,support}() to allow change to
++ * list.
++ * Only send pfkey_expire() messages to sockets registered for that satype.
++ *
++ * Revision 1.35 2000/08/24 17:03:00 rgb
++ * Corrected message size error return code for PF_KEYv2.
++ * Removed downward error prohibition.
++ *
++ * Revision 1.34 2000/08/21 16:32:26 rgb
++ * Re-formatted for cosmetic consistency and readability.
++ *
++ * Revision 1.33 2000/08/20 21:38:24 rgb
++ * Added a pfkey_reply parameter to pfkey_msg_interp(). (Momchil)
++ * Extended the upward message initiation of pfkey_sendmsg(). (Momchil)
++ *
++ * Revision 1.32 2000/07/28 14:58:31 rgb
++ * Changed kfree_s to kfree, eliminating extra arg to fix 2.4.0-test5.
++ *
++ * Revision 1.31 2000/05/16 03:04:00 rgb
++ * Updates for 2.3.99pre8 from MB.
++ *
++ * Revision 1.30 2000/05/10 19:22:21 rgb
++ * Use sklist private functions for 2.3.xx compatibility.
++ *
++ * Revision 1.29 2000/03/22 16:17:03 rgb
++ * Fixed SOCKOPS_WRAPPED macro for SMP (MB).
++ *
++ * Revision 1.28 2000/02/21 19:30:45 rgb
++ * Removed references to pkt_bridged for 2.3.47 compatibility.
++ *
++ * Revision 1.27 2000/02/14 21:07:00 rgb
++ * Fixed /proc/net/pf-key legend spacing.
++ *
++ * Revision 1.26 2000/01/22 03:46:59 rgb
++ * Fixed pfkey error return mechanism so that we are able to free the
++ * local copy of the pfkey_msg, plugging a memory leak and silencing
++ * the bad object free complaints.
++ *
++ * Revision 1.25 2000/01/21 06:19:44 rgb
++ * Moved pfkey_list_remove_socket() calls to before MOD_USE_DEC_COUNT.
++ * Added debugging to pfkey_upmsg.
++ *
++ * Revision 1.24 2000/01/10 16:38:23 rgb
++ * MB fixups for 2.3.x.
++ *
++ * Revision 1.23 1999/12/09 23:22:16 rgb
++ * Added more instrumentation for debugging 2.0 socket
++ * selection/reading.
++ * Removed erroneous 2.0 wait==NULL check bug in select.
++ *
++ * Revision 1.22 1999/12/08 20:32:16 rgb
++ * Tidied up 2.0.xx support, after major pfkey work, eliminating
++ * msg->msg_name twiddling in the process, since it is not defined
++ * for PF_KEYv2.
++ *
++ * Revision 1.21 1999/12/01 22:17:19 rgb
++ * Set skb->dev to zero on new skb in case it is a reused skb.
++ * Added check for skb_put overflow and freeing to avoid upmsg on error.
++ * Added check for wrong pfkey version and freeing to avoid upmsg on
++ * error.
++ * Shut off content dumping in pfkey_destroy.
++ * Added debugging message for size of buffer allocated for upmsg.
++ *
++ * Revision 1.20 1999/11/27 12:11:00 rgb
++ * Minor clean-up, enabling quiet operation of pfkey if desired.
++ *
++ * Revision 1.19 1999/11/25 19:04:21 rgb
++ * Update proc_fs code for pfkey to use dynamic registration.
++ *
++ * Revision 1.18 1999/11/25 09:07:17 rgb
++ * Implemented SENDERR macro for propagating error codes.
++ * Fixed error return code bug.
++ *
++ * Revision 1.17 1999/11/23 23:07:20 rgb
++ * Change name of pfkey_msg_parser to pfkey_msg_interp since it no longer
++ * parses. (PJO)
++ * Sort out pfkey and freeswan headers, putting them in a library path.
++ *
++ * Revision 1.16 1999/11/20 22:00:22 rgb
++ * Moved socketlist type declarations and prototypes for shared use.
++ * Renamed reformatted and generically extended for use by other socket
++ * lists pfkey_{del,add}_open_socket to pfkey_list_{remove,insert}_socket.
++ *
++ * Revision 1.15 1999/11/18 04:15:09 rgb
++ * Make pfkey_data_ready temporarily available for 2.2.x testing.
++ * Clean up pfkey_destroy_socket() debugging statements.
++ * Add Peter Onion's code to send messages up to all listening sockets.
++ * Changed all occurrences of #include "../../../lib/freeswan.h"
++ * to #include <freeswan.h> which works due to -Ilibfreeswan in the
++ * klips/net/ipsec/Makefile.
++ * Replaced all kernel version macros to shorter, readable form.
++ * Added CONFIG_PROC_FS compiler directives in case it is shut off.
++ *
++ * Revision 1.14 1999/11/17 16:01:00 rgb
++ * Make pfkey_data_ready temporarily available for 2.2.x testing.
++ * Clean up pfkey_destroy_socket() debugging statements.
++ * Add Peter Onion's code to send messages up to all listening sockets.
++ * Changed #include "../../../lib/freeswan.h" to #include <freeswan.h>
++ * which works due to -Ilibfreeswan in the klips/net/ipsec/Makefile.
++ *
++ * Revision 1.13 1999/10/27 19:59:51 rgb
++ * Removed af_unix comments that are no longer relevant.
++ * Added debug prink statements.
++ * Added to the /proc output in pfkey_get_info.
++ * Made most functions non-static to enable oops tracing.
++ * Re-enable skb dequeueing and freeing.
++ * Fix skb_alloc() and skb_put() size bug in pfkey_upmsg().
++ *
++ * Revision 1.12 1999/10/26 17:05:42 rgb
++ * Complete re-ordering based on proto_ops structure order.
++ * Separated out proto_ops structures for 2.0.x and 2.2.x for clarity.
++ * Simplification to use built-in socket ops where possible for 2.2.x.
++ * Add shorter macros for compiler directives to visually clean-up.
++ * Add lots of sk skb dequeueing debugging statements.
++ * Added to the /proc output in pfkey_get_info.
++ *
++ * Revision 1.11 1999/09/30 02:55:10 rgb
++ * Bogus skb detection.
++ * Fix incorrect /proc/net/ipsec-eroute printk message.
++ *
++ * Revision 1.10 1999/09/21 15:22:13 rgb
++ * Temporary fix while I figure out the right way to destroy sockets.
++ *
++ * Revision 1.9 1999/07/08 19:19:44 rgb
++ * Fix pointer format warning.
++ * Fix missing member error under 2.0.xx kernels.
++ *
++ * Revision 1.8 1999/06/13 07:24:04 rgb
++ * Add more debugging.
++ *
++ * Revision 1.7 1999/06/10 05:24:17 rgb
++ * Clarified compiler directives.
++ * Renamed variables to reduce confusion.
++ * Used sklist_*_socket() kernel functions to simplify 2.2.x socket support.
++ * Added lots of sanity checking.
++ *
++ * Revision 1.6 1999/06/03 18:59:50 rgb
++ * More updates to 2.2.x socket support. Almost works, oops at end of call.
++ *
++ * Revision 1.5 1999/05/25 22:44:05 rgb
++ * Start fixing 2.2 sockets.
++ *
++ * Revision 1.4 1999/04/29 15:21:34 rgb
++ * Move log to the end of the file.
++ * Eliminate min/max redefinition in #include <net/tcp.h>.
++ * Correct path for pfkey #includes
++ * Standardise an error return method.
++ * Add debugging instrumentation.
++ * Move message type checking to pfkey_msg_parse().
++ * Add check for errno incorrectly set.
++ * Add check for valid PID.
++ * Add check for reserved illegally set.
++ * Add check for message out of bounds.
++ *
++ * Revision 1.3 1999/04/15 17:58:07 rgb
++ * Add RCSID labels.
++ *
++ * Revision 1.2 1999/04/15 15:37:26 rgb
++ * Forward check changes from POST1_00 branch.
++ *
++ * Revision 1.1.2.2 1999/04/13 20:37:12 rgb
++ * Header Title correction.
++ *
++ * Revision 1.1.2.1 1999/03/26 20:58:55 rgb
++ * Add pfkeyv2 support to KLIPS.
++ *
++ *
++ * RFC 2367
++ * PF_KEY_v2 Key Management API
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/net/ipsec/pfkey_v2_ext_process.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,922 @@
++/*
++ * @(#) RFC2367 PF_KEYv2 Key management API message parser
++ * Copyright (C) 1998-2003 Richard Guy Briggs.
++ * Copyright (C) 2004 Michael Richardson <mcr@xelerance.com>
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ * RCSID $Id$
++ */
++
++/*
++ * Template from klips/net/ipsec/ipsec/ipsec_netlink.c.
++ */
++
++char pfkey_v2_ext_process_c_version[] = "$Id$";
++
++#include <linux/config.h>
++#include <linux/version.h>
++#include <linux/kernel.h> /* printk() */
++
++#include "openswan/ipsec_param.h"
++
++#ifdef MALLOC_SLAB
++# include <linux/slab.h> /* kmalloc() */
++#else /* MALLOC_SLAB */
++# include <linux/malloc.h> /* kmalloc() */
++#endif /* MALLOC_SLAB */
++#include <linux/errno.h> /* error codes */
++#include <linux/types.h> /* size_t */
++#include <linux/interrupt.h> /* mark_bh */
++
++#include <linux/netdevice.h> /* struct device, and other headers */
++#include <linux/etherdevice.h> /* eth_type_trans */
++#include <linux/ip.h> /* struct iphdr */
++#include <linux/skbuff.h>
++
++#include <openswan.h>
++
++#include <crypto/des.h>
++
++#ifdef SPINLOCK
++# ifdef SPINLOCK_23
++# include <linux/spinlock.h> /* *lock* */
++# else /* SPINLOCK_23 */
++# include <asm/spinlock.h> /* *lock* */
++# endif /* SPINLOCK_23 */
++#endif /* SPINLOCK */
++#ifdef NET_21
++# include <asm/uaccess.h>
++# include <linux/in6.h>
++# define ip_chk_addr inet_addr_type
++# define IS_MYADDR RTN_LOCAL
++#endif
++#include <asm/checksum.h>
++#include <net/ip.h>
++#ifdef NETLINK_SOCK
++# include <linux/netlink.h>
++#else
++# include <net/netlink.h>
++#endif
++
++#include <linux/random.h> /* get_random_bytes() */
++
++#include "openswan/radij.h"
++#include "openswan/ipsec_encap.h"
++#include "openswan/ipsec_sa.h"
++
++#include "openswan/ipsec_radij.h"
++#include "openswan/ipsec_xform.h"
++#include "openswan/ipsec_ah.h"
++#include "openswan/ipsec_esp.h"
++#include "openswan/ipsec_tunnel.h"
++#include "openswan/ipsec_rcv.h"
++#include "openswan/ipcomp.h"
++
++#include <pfkeyv2.h>
++#include <pfkey.h>
++
++#include "openswan/ipsec_proto.h"
++#include "openswan/ipsec_alg.h"
++
++#define SENDERR(_x) do { error = -(_x); goto errlab; } while (0)
++
++int
++pfkey_sa_process(struct sadb_ext *pfkey_ext, struct pfkey_extracted_data* extr)
++{
++ struct sadb_sa *pfkey_sa = (struct sadb_sa *)pfkey_ext;
++ int error = 0;
++ struct ipsec_sa* ipsp;
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_sa_process: .\n");
++
++ if(!extr || !extr->ips) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_sa_process: "
++ "extr or extr->ips is NULL, fatal\n");
++ SENDERR(EINVAL);
++ }
++
++ switch(pfkey_ext->sadb_ext_type) {
++ case SADB_EXT_SA:
++ ipsp = extr->ips;
++ break;
++ case SADB_X_EXT_SA2:
++ if(extr->ips2 == NULL) {
++ extr->ips2 = ipsec_sa_alloc(&error); /* pass error var by pointer */
++ }
++ if(extr->ips2 == NULL) {
++ SENDERR(-error);
++ }
++ ipsp = extr->ips2;
++ break;
++ default:
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_sa_process: "
++ "invalid exttype=%d.\n",
++ pfkey_ext->sadb_ext_type);
++ SENDERR(EINVAL);
++ }
++
++ ipsp->ips_said.spi = pfkey_sa->sadb_sa_spi;
++ ipsp->ips_replaywin = pfkey_sa->sadb_sa_replay;
++ ipsp->ips_state = pfkey_sa->sadb_sa_state;
++ ipsp->ips_flags = pfkey_sa->sadb_sa_flags;
++ ipsp->ips_replaywin_lastseq = ipsp->ips_replaywin_bitmap = 0;
++ ipsp->ips_ref_rel = pfkey_sa->sadb_x_sa_ref;
++
++ switch(ipsp->ips_said.proto) {
++ case IPPROTO_AH:
++ ipsp->ips_authalg = pfkey_sa->sadb_sa_auth;
++ ipsp->ips_encalg = SADB_EALG_NONE;
++ break;
++ case IPPROTO_ESP:
++ ipsp->ips_authalg = pfkey_sa->sadb_sa_auth;
++ ipsp->ips_encalg = pfkey_sa->sadb_sa_encrypt;
++#ifdef CONFIG_IPSEC_ALG
++ ipsec_alg_sa_init(ipsp);
++#endif /* CONFIG_IPSEC_ALG */
++ break;
++ case IPPROTO_IPIP:
++ ipsp->ips_authalg = AH_NONE;
++ ipsp->ips_encalg = ESP_NONE;
++ break;
++#ifdef CONFIG_IPSEC_IPCOMP
++ case IPPROTO_COMP:
++ ipsp->ips_authalg = AH_NONE;
++ ipsp->ips_encalg = pfkey_sa->sadb_sa_encrypt;
++ break;
++#endif /* CONFIG_IPSEC_IPCOMP */
++ case IPPROTO_INT:
++ ipsp->ips_authalg = AH_NONE;
++ ipsp->ips_encalg = ESP_NONE;
++ break;
++ case 0:
++ break;
++ default:
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_sa_process: "
++ "unknown proto=%d.\n",
++ ipsp->ips_said.proto);
++ SENDERR(EINVAL);
++ }
++
++errlab:
++ return error;
++}
++
++int
++pfkey_lifetime_process(struct sadb_ext *pfkey_ext, struct pfkey_extracted_data* extr)
++{
++ int error = 0;
++ struct sadb_lifetime *pfkey_lifetime = (struct sadb_lifetime *)pfkey_ext;
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_lifetime_process: .\n");
++
++ if(!extr || !extr->ips) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_lifetime_process: "
++ "extr or extr->ips is NULL, fatal\n");
++ SENDERR(EINVAL);
++ }
++
++ switch(pfkey_lifetime->sadb_lifetime_exttype) {
++ case SADB_EXT_LIFETIME_CURRENT:
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_lifetime_process: "
++ "lifetime_current not supported yet.\n");
++ SENDERR(EINVAL);
++ break;
++ case SADB_EXT_LIFETIME_HARD:
++ ipsec_lifetime_update_hard(&extr->ips->ips_life.ipl_allocations,
++ pfkey_lifetime->sadb_lifetime_allocations);
++
++ ipsec_lifetime_update_hard(&extr->ips->ips_life.ipl_bytes,
++ pfkey_lifetime->sadb_lifetime_bytes);
++
++ ipsec_lifetime_update_hard(&extr->ips->ips_life.ipl_addtime,
++ pfkey_lifetime->sadb_lifetime_addtime);
++
++ ipsec_lifetime_update_hard(&extr->ips->ips_life.ipl_usetime,
++ pfkey_lifetime->sadb_lifetime_usetime);
++
++ break;
++
++ case SADB_EXT_LIFETIME_SOFT:
++ ipsec_lifetime_update_soft(&extr->ips->ips_life.ipl_allocations,
++ pfkey_lifetime->sadb_lifetime_allocations);
++
++ ipsec_lifetime_update_soft(&extr->ips->ips_life.ipl_bytes,
++ pfkey_lifetime->sadb_lifetime_bytes);
++
++ ipsec_lifetime_update_soft(&extr->ips->ips_life.ipl_addtime,
++ pfkey_lifetime->sadb_lifetime_addtime);
++
++ ipsec_lifetime_update_soft(&extr->ips->ips_life.ipl_usetime,
++ pfkey_lifetime->sadb_lifetime_usetime);
++
++ break;
++ default:
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_lifetime_process: "
++ "invalid exttype=%d.\n",
++ pfkey_ext->sadb_ext_type);
++ SENDERR(EINVAL);
++ }
++
++errlab:
++ return error;
++}
++
++int
++pfkey_address_process(struct sadb_ext *pfkey_ext, struct pfkey_extracted_data* extr)
++{
++ int error = 0;
++ int saddr_len = 0;
++ char ipaddr_txt[ADDRTOA_BUF];
++ unsigned char **sap;
++ unsigned short * portp = 0;
++ struct sadb_address *pfkey_address = (struct sadb_address *)pfkey_ext;
++ struct sockaddr* s = (struct sockaddr*)((char*)pfkey_address + sizeof(*pfkey_address));
++ struct ipsec_sa* ipsp;
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_address_process:\n");
++
++ if(!extr || !extr->ips) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_address_process: "
++ "extr or extr->ips is NULL, fatal\n");
++ SENDERR(EINVAL);
++ }
++
++ switch(s->sa_family) {
++ case AF_INET:
++ saddr_len = sizeof(struct sockaddr_in);
++ addrtoa(((struct sockaddr_in*)s)->sin_addr, 0, ipaddr_txt, sizeof(ipaddr_txt));
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_address_process: "
++ "found address family=%d, AF_INET, %s.\n",
++ s->sa_family,
++ ipaddr_txt);
++ break;
++#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
++ case AF_INET6:
++ saddr_len = sizeof(struct sockaddr_in6);
++ break;
++#endif /* defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) */
++ default:
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_address_process: "
++ "s->sa_family=%d not supported.\n",
++ s->sa_family);
++ SENDERR(EPFNOSUPPORT);
++ }
++
++ switch(pfkey_address->sadb_address_exttype) {
++ case SADB_EXT_ADDRESS_SRC:
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_address_process: "
++ "found src address.\n");
++ sap = (unsigned char **)&(extr->ips->ips_addr_s);
++ extr->ips->ips_addr_s_size = saddr_len;
++ break;
++ case SADB_EXT_ADDRESS_DST:
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_address_process: "
++ "found dst address.\n");
++ sap = (unsigned char **)&(extr->ips->ips_addr_d);
++ extr->ips->ips_addr_d_size = saddr_len;
++ break;
++ case SADB_EXT_ADDRESS_PROXY:
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_address_process: "
++ "found proxy address.\n");
++ sap = (unsigned char **)&(extr->ips->ips_addr_p);
++ extr->ips->ips_addr_p_size = saddr_len;
++ break;
++ case SADB_X_EXT_ADDRESS_DST2:
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_address_process: "
++ "found 2nd dst address.\n");
++ if(extr->ips2 == NULL) {
++ extr->ips2 = ipsec_sa_alloc(&error); /* pass error var by pointer */
++ }
++ if(extr->ips2 == NULL) {
++ SENDERR(-error);
++ }
++ sap = (unsigned char **)&(extr->ips2->ips_addr_d);
++ extr->ips2->ips_addr_d_size = saddr_len;
++ break;
++ case SADB_X_EXT_ADDRESS_SRC_FLOW:
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_address_process: "
++ "found src flow address.\n");
++ if(pfkey_alloc_eroute(&(extr->eroute)) == ENOMEM) {
++ SENDERR(ENOMEM);
++ }
++ sap = (unsigned char **)&(extr->eroute->er_eaddr.sen_ip_src);
++ portp = &(extr->eroute->er_eaddr.sen_sport);
++ break;
++ case SADB_X_EXT_ADDRESS_DST_FLOW:
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_address_process: "
++ "found dst flow address.\n");
++ if(pfkey_alloc_eroute(&(extr->eroute)) == ENOMEM) {
++ SENDERR(ENOMEM);
++ }
++ sap = (unsigned char **)&(extr->eroute->er_eaddr.sen_ip_dst);
++ portp = &(extr->eroute->er_eaddr.sen_dport);
++ break;
++ case SADB_X_EXT_ADDRESS_SRC_MASK:
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_address_process: "
++ "found src mask address.\n");
++ if(pfkey_alloc_eroute(&(extr->eroute)) == ENOMEM) {
++ SENDERR(ENOMEM);
++ }
++ sap = (unsigned char **)&(extr->eroute->er_emask.sen_ip_src);
++ portp = &(extr->eroute->er_emask.sen_sport);
++ break;
++ case SADB_X_EXT_ADDRESS_DST_MASK:
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_address_process: "
++ "found dst mask address.\n");
++ if(pfkey_alloc_eroute(&(extr->eroute)) == ENOMEM) {
++ SENDERR(ENOMEM);
++ }
++ sap = (unsigned char **)&(extr->eroute->er_emask.sen_ip_dst);
++ portp = &(extr->eroute->er_emask.sen_dport);
++ break;
++#ifdef NAT_TRAVERSAL
++ case SADB_X_EXT_NAT_T_OA:
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_address_process: "
++ "found NAT-OA address.\n");
++ sap = (unsigned char **)&(extr->ips->ips_natt_oa);
++ extr->ips->ips_natt_oa_size = saddr_len;
++ break;
++#endif
++ default:
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_address_process: "
++ "unrecognised ext_type=%d.\n",
++ pfkey_address->sadb_address_exttype);
++ SENDERR(EINVAL);
++ }
++
++ switch(pfkey_address->sadb_address_exttype) {
++ case SADB_EXT_ADDRESS_SRC:
++ case SADB_EXT_ADDRESS_DST:
++ case SADB_EXT_ADDRESS_PROXY:
++ case SADB_X_EXT_ADDRESS_DST2:
++#ifdef NAT_TRAVERSAL
++ case SADB_X_EXT_NAT_T_OA:
++#endif
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_address_process: "
++ "allocating %d bytes for saddr.\n",
++ saddr_len);
++ if(!(*sap = kmalloc(saddr_len, GFP_KERNEL))) {
++ SENDERR(ENOMEM);
++ }
++ memcpy(*sap, s, saddr_len);
++ break;
++ default:
++ if(s->sa_family != AF_INET) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_address_process: "
++ "s->sa_family=%d not supported.\n",
++ s->sa_family);
++ SENDERR(EPFNOSUPPORT);
++ }
++ (unsigned long)(*sap) = ((struct sockaddr_in*)s)->sin_addr.s_addr;
++ if (portp != 0)
++ *portp = ((struct sockaddr_in*)s)->sin_port;
++#ifdef CONFIG_IPSEC_DEBUG
++ if(extr->eroute) {
++ char buf1[64], buf2[64];
++ if (debug_pfkey) {
++ subnettoa(extr->eroute->er_eaddr.sen_ip_src,
++ extr->eroute->er_emask.sen_ip_src, 0, buf1, sizeof(buf1));
++ subnettoa(extr->eroute->er_eaddr.sen_ip_dst,
++ extr->eroute->er_emask.sen_ip_dst, 0, buf2, sizeof(buf2));
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_address_parse: "
++ "extr->eroute set to %s:%d->%s:%d\n",
++ buf1,
++ ntohs(extr->eroute->er_eaddr.sen_sport),
++ buf2,
++ ntohs(extr->eroute->er_eaddr.sen_dport));
++ }
++ }
++#endif /* CONFIG_IPSEC_DEBUG */
++ }
++
++ ipsp = extr->ips;
++ switch(pfkey_address->sadb_address_exttype) {
++ case SADB_X_EXT_ADDRESS_DST2:
++ ipsp = extr->ips2;
++ case SADB_EXT_ADDRESS_DST:
++ if(s->sa_family == AF_INET) {
++ ipsp->ips_said.dst.u.v4.sin_addr.s_addr = ((struct sockaddr_in*)(ipsp->ips_addr_d))->sin_addr.s_addr;
++ ipsp->ips_said.dst.u.v4.sin_family = AF_INET;
++ addrtoa(((struct sockaddr_in*)(ipsp->ips_addr_d))->sin_addr,
++ 0,
++ ipaddr_txt,
++ sizeof(ipaddr_txt));
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_address_process: "
++ "ips_said.dst set to %s.\n",
++ ipaddr_txt);
++ } else {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_address_process: "
++ "uh, ips_said.dst doesn't do address family=%d yet, said will be invalid.\n",
++ s->sa_family);
++ }
++ default:
++ break;
++ }
++
++ /* XXX check if port!=0 */
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_address_process: successful.\n");
++ errlab:
++ return error;
++}
++
++int
++pfkey_key_process(struct sadb_ext *pfkey_ext, struct pfkey_extracted_data* extr)
++{
++ int error = 0;
++ struct sadb_key *pfkey_key = (struct sadb_key *)pfkey_ext;
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_key_process: .\n");
++
++ if(!extr || !extr->ips) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_key_process: "
++ "extr or extr->ips is NULL, fatal\n");
++ SENDERR(EINVAL);
++ }
++
++ switch(pfkey_key->sadb_key_exttype) {
++ case SADB_EXT_KEY_AUTH:
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_key_process: "
++ "allocating %d bytes for authkey.\n",
++ DIVUP(pfkey_key->sadb_key_bits, 8));
++ if(!(extr->ips->ips_key_a = kmalloc(DIVUP(pfkey_key->sadb_key_bits, 8), GFP_KERNEL))) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_key_process: "
++ "memory allocation error.\n");
++ SENDERR(ENOMEM);
++ }
++ extr->ips->ips_key_bits_a = pfkey_key->sadb_key_bits;
++ extr->ips->ips_key_a_size = DIVUP(pfkey_key->sadb_key_bits, 8);
++ memcpy(extr->ips->ips_key_a,
++ (char*)pfkey_key + sizeof(struct sadb_key),
++ extr->ips->ips_key_a_size);
++ break;
++ case SADB_EXT_KEY_ENCRYPT: /* Key(s) */
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_key_process: "
++ "allocating %d bytes for enckey.\n",
++ DIVUP(pfkey_key->sadb_key_bits, 8));
++ if(!(extr->ips->ips_key_e = kmalloc(DIVUP(pfkey_key->sadb_key_bits, 8), GFP_KERNEL))) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_key_process: "
++ "memory allocation error.\n");
++ SENDERR(ENOMEM);
++ }
++ extr->ips->ips_key_bits_e = pfkey_key->sadb_key_bits;
++ extr->ips->ips_key_e_size = DIVUP(pfkey_key->sadb_key_bits, 8);
++ memcpy(extr->ips->ips_key_e,
++ (char*)pfkey_key + sizeof(struct sadb_key),
++ extr->ips->ips_key_e_size);
++ break;
++ default:
++ SENDERR(EINVAL);
++ }
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_key_process: "
++ "success.\n");
++errlab:
++ return error;
++}
++
++int
++pfkey_ident_process(struct sadb_ext *pfkey_ext, struct pfkey_extracted_data* extr)
++{
++ int error = 0;
++ struct sadb_ident *pfkey_ident = (struct sadb_ident *)pfkey_ext;
++ int data_len;
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_ident_process: .\n");
++
++ if(!extr || !extr->ips) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_ident_process: "
++ "extr or extr->ips is NULL, fatal\n");
++ SENDERR(EINVAL);
++ }
++
++ switch(pfkey_ident->sadb_ident_exttype) {
++ case SADB_EXT_IDENTITY_SRC:
++ data_len = pfkey_ident->sadb_ident_len * IPSEC_PFKEYv2_ALIGN - sizeof(struct sadb_ident);
++
++ extr->ips->ips_ident_s.type = pfkey_ident->sadb_ident_type;
++ extr->ips->ips_ident_s.id = pfkey_ident->sadb_ident_id;
++ extr->ips->ips_ident_s.len = pfkey_ident->sadb_ident_len;
++ if(data_len) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_ident_process: "
++ "allocating %d bytes for ident_s.\n",
++ data_len);
++ if(!(extr->ips->ips_ident_s.data
++ = kmalloc(data_len, GFP_KERNEL))) {
++ SENDERR(ENOMEM);
++ }
++ memcpy(extr->ips->ips_ident_s.data,
++ (char*)pfkey_ident + sizeof(struct sadb_ident),
++ data_len);
++ } else {
++ extr->ips->ips_ident_s.data = NULL;
++ }
++ break;
++ case SADB_EXT_IDENTITY_DST: /* Identity(ies) */
++ data_len = pfkey_ident->sadb_ident_len * IPSEC_PFKEYv2_ALIGN - sizeof(struct sadb_ident);
++
++ extr->ips->ips_ident_d.type = pfkey_ident->sadb_ident_type;
++ extr->ips->ips_ident_d.id = pfkey_ident->sadb_ident_id;
++ extr->ips->ips_ident_d.len = pfkey_ident->sadb_ident_len;
++ if(data_len) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_ident_process: "
++ "allocating %d bytes for ident_d.\n",
++ data_len);
++ if(!(extr->ips->ips_ident_d.data
++ = kmalloc(data_len, GFP_KERNEL))) {
++ SENDERR(ENOMEM);
++ }
++ memcpy(extr->ips->ips_ident_d.data,
++ (char*)pfkey_ident + sizeof(struct sadb_ident),
++ data_len);
++ } else {
++ extr->ips->ips_ident_d.data = NULL;
++ }
++ break;
++ default:
++ SENDERR(EINVAL);
++ }
++errlab:
++ return error;
++}
++
++int
++pfkey_sens_process(struct sadb_ext *pfkey_ext, struct pfkey_extracted_data* extr)
++{
++ int error = 0;
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_sens_process: "
++ "Sorry, I can't process exttype=%d yet.\n",
++ pfkey_ext->sadb_ext_type);
++ SENDERR(EINVAL); /* don't process these yet */
++ errlab:
++ return error;
++}
++
++int
++pfkey_prop_process(struct sadb_ext *pfkey_ext, struct pfkey_extracted_data* extr)
++{
++ int error = 0;
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_prop_process: "
++ "Sorry, I can't process exttype=%d yet.\n",
++ pfkey_ext->sadb_ext_type);
++ SENDERR(EINVAL); /* don't process these yet */
++
++ errlab:
++ return error;
++}
++
++int
++pfkey_supported_process(struct sadb_ext *pfkey_ext, struct pfkey_extracted_data* extr)
++{
++ int error = 0;
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_supported_process: "
++ "Sorry, I can't process exttype=%d yet.\n",
++ pfkey_ext->sadb_ext_type);
++ SENDERR(EINVAL); /* don't process these yet */
++
++errlab:
++ return error;
++}
++
++int
++pfkey_spirange_process(struct sadb_ext *pfkey_ext, struct pfkey_extracted_data* extr)
++{
++ int error = 0;
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_spirange_process: .\n");
++/* errlab: */
++ return error;
++}
++
++int
++pfkey_x_kmprivate_process(struct sadb_ext *pfkey_ext, struct pfkey_extracted_data* extr)
++{
++ int error = 0;
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_kmprivate_process: "
++ "Sorry, I can't process exttype=%d yet.\n",
++ pfkey_ext->sadb_ext_type);
++ SENDERR(EINVAL); /* don't process these yet */
++
++errlab:
++ return error;
++}
++
++int
++pfkey_x_satype_process(struct sadb_ext *pfkey_ext, struct pfkey_extracted_data* extr)
++{
++ int error = 0;
++ struct sadb_x_satype *pfkey_x_satype = (struct sadb_x_satype *)pfkey_ext;
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_satype_process: .\n");
++
++ if(!extr || !extr->ips) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_satype_process: "
++ "extr or extr->ips is NULL, fatal\n");
++ SENDERR(EINVAL);
++ }
++
++ if(extr->ips2 == NULL) {
++ extr->ips2 = ipsec_sa_alloc(&error); /* pass error var by pointer */
++ }
++ if(extr->ips2 == NULL) {
++ SENDERR(-error);
++ }
++ if(!(extr->ips2->ips_said.proto = satype2proto(pfkey_x_satype->sadb_x_satype_satype))) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_satype_process: "
++ "proto lookup from satype=%d failed.\n",
++ pfkey_x_satype->sadb_x_satype_satype);
++ SENDERR(EINVAL);
++ }
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_satype_process: "
++ "protocol==%d decoded from satype==%d(%s).\n",
++ extr->ips2->ips_said.proto,
++ pfkey_x_satype->sadb_x_satype_satype,
++ satype2name(pfkey_x_satype->sadb_x_satype_satype));
++
++errlab:
++ return error;
++}
++
++
++#ifdef CONFIG_IPSEC_NAT_TRAVERSAL
++int
++pfkey_x_nat_t_type_process(struct sadb_ext *pfkey_ext, struct pfkey_extracted_data* extr)
++{
++ int error = 0;
++ struct sadb_x_nat_t_type *pfkey_x_nat_t_type = (struct sadb_x_nat_t_type *)pfkey_ext;
++
++ if(!pfkey_x_nat_t_type) {
++ printk("klips_debug:pfkey_x_nat_t_type_process: "
++ "null pointer passed in\n");
++ SENDERR(EINVAL);
++ }
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_nat_t_type_process: %d.\n",
++ pfkey_x_nat_t_type->sadb_x_nat_t_type_type);
++
++ if(!extr || !extr->ips) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_nat_t_type_process: "
++ "extr or extr->ips is NULL, fatal\n");
++ SENDERR(EINVAL);
++ }
++
++ switch(pfkey_x_nat_t_type->sadb_x_nat_t_type_type) {
++ case ESPINUDP_WITH_NON_IKE: /* with Non-IKE (older version) */
++ case ESPINUDP_WITH_NON_ESP: /* with Non-ESP */
++
++ extr->ips->ips_natt_type = pfkey_x_nat_t_type->sadb_x_nat_t_type_type;
++ break;
++ default:
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_nat_t_type_process: "
++ "unknown type %d.\n",
++ pfkey_x_nat_t_type->sadb_x_nat_t_type_type);
++ SENDERR(EINVAL);
++ break;
++ }
++
++errlab:
++ return error;
++}
++
++int
++pfkey_x_nat_t_port_process(struct sadb_ext *pfkey_ext, struct pfkey_extracted_data* extr)
++{
++ int error = 0;
++ struct sadb_x_nat_t_port *pfkey_x_nat_t_port = (struct sadb_x_nat_t_port *)pfkey_ext;
++
++ if(!pfkey_x_nat_t_port) {
++ printk("klips_debug:pfkey_x_nat_t_port_process: "
++ "null pointer passed in\n");
++ SENDERR(EINVAL);
++ }
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_nat_t_port_process: %d/%d.\n",
++ pfkey_x_nat_t_port->sadb_x_nat_t_port_exttype,
++ pfkey_x_nat_t_port->sadb_x_nat_t_port_port);
++
++ if(!extr || !extr->ips) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_nat_t_type_process: "
++ "extr or extr->ips is NULL, fatal\n");
++ SENDERR(EINVAL);
++ }
++
++ switch(pfkey_x_nat_t_port->sadb_x_nat_t_port_exttype) {
++ case SADB_X_EXT_NAT_T_SPORT:
++ extr->ips->ips_natt_sport = pfkey_x_nat_t_port->sadb_x_nat_t_port_port;
++ break;
++ case SADB_X_EXT_NAT_T_DPORT:
++ extr->ips->ips_natt_dport = pfkey_x_nat_t_port->sadb_x_nat_t_port_port;
++ break;
++ default:
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_nat_t_port_process: "
++ "unknown exttype %d.\n",
++ pfkey_x_nat_t_port->sadb_x_nat_t_port_exttype);
++ SENDERR(EINVAL);
++ break;
++ }
++
++errlab:
++ return error;
++}
++#endif
++
++int
++pfkey_x_debug_process(struct sadb_ext *pfkey_ext, struct pfkey_extracted_data* extr)
++{
++ int error = 0;
++ struct sadb_x_debug *pfkey_x_debug = (struct sadb_x_debug *)pfkey_ext;
++
++ if(!pfkey_x_debug) {
++ printk("klips_debug:pfkey_x_debug_process: "
++ "null pointer passed in\n");
++ SENDERR(EINVAL);
++ }
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_debug_process: .\n");
++
++#ifdef CONFIG_IPSEC_DEBUG
++ if(pfkey_x_debug->sadb_x_debug_netlink >>
++ (sizeof(pfkey_x_debug->sadb_x_debug_netlink) * 8 - 1)) {
++ pfkey_x_debug->sadb_x_debug_netlink &=
++ ~(1 << (sizeof(pfkey_x_debug->sadb_x_debug_netlink) * 8 -1));
++ debug_tunnel |= pfkey_x_debug->sadb_x_debug_tunnel;
++ debug_netlink |= pfkey_x_debug->sadb_x_debug_netlink;
++ debug_xform |= pfkey_x_debug->sadb_x_debug_xform;
++ debug_eroute |= pfkey_x_debug->sadb_x_debug_eroute;
++ debug_spi |= pfkey_x_debug->sadb_x_debug_spi;
++ debug_radij |= pfkey_x_debug->sadb_x_debug_radij;
++ debug_esp |= pfkey_x_debug->sadb_x_debug_esp;
++ debug_ah |= pfkey_x_debug->sadb_x_debug_ah;
++ debug_rcv |= pfkey_x_debug->sadb_x_debug_rcv;
++ debug_pfkey |= pfkey_x_debug->sadb_x_debug_pfkey;
++#ifdef CONFIG_IPSEC_IPCOMP
++ sysctl_ipsec_debug_ipcomp |= pfkey_x_debug->sadb_x_debug_ipcomp;
++#endif /* CONFIG_IPSEC_IPCOMP */
++ sysctl_ipsec_debug_verbose |= pfkey_x_debug->sadb_x_debug_verbose;
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_debug_process: "
++ "set\n");
++ } else {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_debug_process: "
++ "unset\n");
++ debug_tunnel &= pfkey_x_debug->sadb_x_debug_tunnel;
++ debug_netlink &= pfkey_x_debug->sadb_x_debug_netlink;
++ debug_xform &= pfkey_x_debug->sadb_x_debug_xform;
++ debug_eroute &= pfkey_x_debug->sadb_x_debug_eroute;
++ debug_spi &= pfkey_x_debug->sadb_x_debug_spi;
++ debug_radij &= pfkey_x_debug->sadb_x_debug_radij;
++ debug_esp &= pfkey_x_debug->sadb_x_debug_esp;
++ debug_ah &= pfkey_x_debug->sadb_x_debug_ah;
++ debug_rcv &= pfkey_x_debug->sadb_x_debug_rcv;
++ debug_pfkey &= pfkey_x_debug->sadb_x_debug_pfkey;
++#ifdef CONFIG_IPSEC_IPCOMP
++ sysctl_ipsec_debug_ipcomp &= pfkey_x_debug->sadb_x_debug_ipcomp;
++#endif /* CONFIG_IPSEC_IPCOMP */
++ sysctl_ipsec_debug_verbose &= pfkey_x_debug->sadb_x_debug_verbose;
++ }
++#else /* CONFIG_IPSEC_DEBUG */
++ printk("klips_debug:pfkey_x_debug_process: "
++ "debugging not enabled\n");
++ SENDERR(EINVAL);
++#endif /* CONFIG_IPSEC_DEBUG */
++
++errlab:
++ return error;
++}
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.15 2004/04/06 02:49:26 mcr
++ * pullup of algo code from alg-branch.
++ *
++ * Revision 1.14 2004/02/03 03:13:59 mcr
++ * no longer #ifdef out NON_ESP mode. That was a mistake.
++ *
++ * Revision 1.13 2003/12/15 18:13:12 mcr
++ * when compiling with NAT traversal, don't assume that the
++ * kernel has been patched, unless CONFIG_IPSEC_NAT_NON_ESP
++ * is set.
++ *
++ * Revision 1.12.2.1 2003/12/22 15:25:52 jjo
++ * Merged algo-0.8.1-rc11-test1 into alg-branch
++ *
++ * Revision 1.12 2003/12/10 01:14:27 mcr
++ * NAT-traversal patches to KLIPS.
++ *
++ * Revision 1.11 2003/10/31 02:27:55 mcr
++ * pulled up port-selector patches and sa_id elimination.
++ *
++ * Revision 1.10.4.2 2003/10/29 01:30:41 mcr
++ * elimited "struct sa_id".
++ *
++ * Revision 1.10.4.1 2003/09/21 13:59:56 mcr
++ * pre-liminary X.509 patch - does not yet pass tests.
++ *
++ * Revision 1.10 2003/02/06 01:51:41 rgb
++ * Removed no longer relevant comment
++ *
++ * Revision 1.9 2003/01/30 02:32:44 rgb
++ *
++ * Transmit error code through to caller from callee for better diagnosis of problems.
++ *
++ * Revision 1.8 2002/12/13 22:42:22 mcr
++ * restored sa_ref code
++ *
++ * Revision 1.7 2002/12/13 22:40:48 mcr
++ * temporarily removed sadb_x_sa_ref reference for 2.xx
++ *
++ * Revision 1.6 2002/10/05 05:02:58 dhr
++ *
++ * C labels go on statements
++ *
++ * Revision 1.5 2002/09/20 15:41:08 rgb
++ * Switch from pfkey_alloc_ipsec_sa() to ipsec_sa_alloc().
++ * Added sadb_x_sa_ref to struct sadb_sa.
++ *
++ * Revision 1.4 2002/09/20 05:02:02 rgb
++ * Added memory allocation debugging.
++ *
++ * Revision 1.3 2002/07/24 18:44:54 rgb
++ * Type fiddling to tame ia64 compiler.
++ *
++ * Revision 1.2 2002/05/27 18:55:03 rgb
++ * Remove final vistiges of tdb references via IPSEC_KLIPS1_COMPAT.
++ *
++ * Revision 1.1 2002/05/14 02:33:51 rgb
++ * Moved all the extension processing functions to pfkey_v2_ext_process.c.
++ *
++ *
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/net/ipsec/pfkey_v2_parser.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,4018 @@
++/*
++ * @(#) RFC2367 PF_KEYv2 Key management API message parser
++ * Copyright (C) 1999, 2000, 2001 Richard Guy Briggs <rgb@freeswan.org>
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ * RCSID $Id$
++ */
++
++/*
++ * Template from klips/net/ipsec/ipsec/ipsec_netlink.c.
++ */
++
++char pfkey_v2_parser_c_version[] = "$Id$";
++
++#include <linux/config.h>
++#include <linux/version.h>
++#include <linux/kernel.h> /* printk() */
++
++#include "openswan/ipsec_param.h"
++
++#ifdef MALLOC_SLAB
++# include <linux/slab.h> /* kmalloc() */
++#else /* MALLOC_SLAB */
++# include <linux/malloc.h> /* kmalloc() */
++#endif /* MALLOC_SLAB */
++#include <linux/errno.h> /* error codes */
++#include <linux/types.h> /* size_t */
++#include <linux/interrupt.h> /* mark_bh */
++
++#include <linux/netdevice.h> /* struct device, and other headers */
++#include <linux/etherdevice.h> /* eth_type_trans */
++#include <linux/ip.h> /* struct iphdr */
++#include <linux/skbuff.h>
++
++#include <openswan.h>
++
++#include <crypto/des.h>
++
++#ifdef SPINLOCK
++# ifdef SPINLOCK_23
++# include <linux/spinlock.h> /* *lock* */
++# else /* SPINLOCK_23 */
++# include <asm/spinlock.h> /* *lock* */
++# endif /* SPINLOCK_23 */
++#endif /* SPINLOCK */
++#ifdef NET_21
++# include <asm/uaccess.h>
++# include <linux/in6.h>
++# define ip_chk_addr inet_addr_type
++# define IS_MYADDR RTN_LOCAL
++#endif
++#include <asm/checksum.h>
++#include <net/ip.h>
++#ifdef NETLINK_SOCK
++# include <linux/netlink.h>
++#else
++# include <net/netlink.h>
++#endif
++
++#include <linux/random.h> /* get_random_bytes() */
++
++#include "openswan/radij.h"
++#include "openswan/ipsec_encap.h"
++#include "openswan/ipsec_sa.h"
++
++#include "openswan/ipsec_radij.h"
++#include "openswan/ipsec_xform.h"
++#include "openswan/ipsec_ah.h"
++#include "openswan/ipsec_esp.h"
++#include "openswan/ipsec_tunnel.h"
++#include "openswan/ipsec_rcv.h"
++#include "openswan/ipcomp.h"
++
++#include <pfkeyv2.h>
++#include <pfkey.h>
++
++#include "openswan/ipsec_proto.h"
++#include "openswan/ipsec_alg.h"
++
++
++#define SENDERR(_x) do { error = -(_x); goto errlab; } while (0)
++
++struct sklist_t {
++ struct socket *sk;
++ struct sklist_t* next;
++} pfkey_sklist_head, *pfkey_sklist, *pfkey_sklist_prev;
++
++__u32 pfkey_msg_seq = 0;
++
++
++#if 0
++#define DUMP_SAID dump_said(&extr->ips->ips_said, __LINE__)
++#define DUMP_SAID2 dump_said(&extr.ips->ips_said, __LINE__)
++static void dump_said(ip_said *s, int line)
++{
++ char msa[SATOT_BUF];
++ size_t msa_len;
++
++ msa_len = satot(s, 0, msa, sizeof(msa));
++
++ printk("line: %d msa: %s\n", line, msa);
++}
++#endif
++
++
++int
++pfkey_alloc_eroute(struct eroute** eroute)
++{
++ int error = 0;
++ if(*eroute) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_alloc_eroute: "
++ "eroute struct already allocated\n");
++ SENDERR(EEXIST);
++ }
++
++ if((*eroute = kmalloc(sizeof(**eroute), GFP_ATOMIC) ) == NULL) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_alloc_eroute: "
++ "memory allocation error\n");
++ SENDERR(ENOMEM);
++ }
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_alloc_eroute: "
++ "allocating %lu bytes for an eroute at 0p%p\n",
++ (unsigned long) sizeof(**eroute), *eroute);
++
++ memset((caddr_t)*eroute, 0, sizeof(**eroute));
++ (*eroute)->er_eaddr.sen_len =
++ (*eroute)->er_emask.sen_len = sizeof(struct sockaddr_encap);
++ (*eroute)->er_eaddr.sen_family =
++ (*eroute)->er_emask.sen_family = AF_ENCAP;
++ (*eroute)->er_eaddr.sen_type = SENT_IP4;
++ (*eroute)->er_emask.sen_type = 255;
++ (*eroute)->er_pid = 0;
++ (*eroute)->er_count = 0;
++ (*eroute)->er_lasttime = jiffies/HZ;
++
++ errlab:
++ return(error);
++}
++
++DEBUG_NO_STATIC int
++pfkey_x_protocol_process(struct sadb_ext *pfkey_ext,
++ struct pfkey_extracted_data *extr)
++{
++ int error = 0;
++ struct sadb_protocol * p = (struct sadb_protocol *)pfkey_ext;
++
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_x_protocol_process: %p\n", extr);
++
++ if (extr == 0) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_protocol_process:"
++ "extr is NULL, fatal\n");
++ SENDERR(EINVAL);
++ }
++ if (extr->eroute == 0) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_protocol_process:"
++ "extr->eroute is NULL, fatal\n");
++ SENDERR(EINVAL);
++ }
++
++ extr->eroute->er_eaddr.sen_proto = p->sadb_protocol_proto;
++ extr->eroute->er_emask.sen_proto = p->sadb_protocol_proto ? ~0:0;
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_protocol_process: protocol = %d.\n",
++ p->sadb_protocol_proto);
++ errlab:
++ return error;
++}
++
++DEBUG_NO_STATIC int
++pfkey_ipsec_sa_init(struct ipsec_sa *ipsp, struct sadb_ext **extensions)
++{
++ int i;
++ int error = 0;
++ char sa[SATOT_BUF];
++ size_t sa_len;
++ char ipaddr_txt[ADDRTOA_BUF];
++ char ipaddr2_txt[ADDRTOA_BUF];
++#if defined (CONFIG_IPSEC_AUTH_HMAC_MD5) || defined (CONFIG_IPSEC_AUTH_HMAC_SHA1)
++ unsigned char kb[AHMD596_BLKLEN];
++#endif
++#ifdef CONFIG_IPSEC_ALG
++ struct ipsec_alg_enc *ixt_e = NULL;
++ struct ipsec_alg_auth *ixt_a = NULL;
++#endif /* CONFIG_IPSEC_ALG */
++
++ if(ipsp == NULL) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_ipsec_sa_init: "
++ "ipsp is NULL, fatal\n");
++ SENDERR(EINVAL);
++ }
++
++ sa_len = satot(&ipsp->ips_said, 0, sa, sizeof(sa));
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_ipsec_sa_init: "
++ "(pfkey defined) called for SA:%s\n",
++ sa_len ? sa : " (error)");
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_ipsec_sa_init: "
++ "calling init routine of %s%s%s\n",
++ IPS_XFORM_NAME(ipsp));
++
++ switch(ipsp->ips_said.proto) {
++
++#ifdef CONFIG_IPSEC_IPIP
++ case IPPROTO_IPIP: {
++ addrtoa(((struct sockaddr_in*)(ipsp->ips_addr_s))->sin_addr,
++ 0,
++ ipaddr_txt, sizeof(ipaddr_txt));
++ addrtoa(((struct sockaddr_in*)(ipsp->ips_addr_d))->sin_addr,
++ 0,
++ ipaddr2_txt, sizeof(ipaddr_txt));
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_ipsec_sa_init: "
++ "(pfkey defined) IPIP ipsec_sa set for %s->%s.\n",
++ ipaddr_txt,
++ ipaddr2_txt);
++ }
++ break;
++#endif /* !CONFIG_IPSEC_IPIP */
++#ifdef CONFIG_IPSEC_AH
++ case IPPROTO_AH:
++ switch(ipsp->ips_authalg) {
++# ifdef CONFIG_IPSEC_AUTH_HMAC_MD5
++ case AH_MD5: {
++ unsigned char *akp;
++ unsigned int aks;
++ MD5_CTX *ictx;
++ MD5_CTX *octx;
++
++ if(ipsp->ips_key_bits_a != (AHMD596_KLEN * 8)) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_ipsec_sa_init: "
++ "incorrect key size: %d bits -- must be %d bits\n"/*octets (bytes)\n"*/,
++ ipsp->ips_key_bits_a, AHMD596_KLEN * 8);
++ SENDERR(EINVAL);
++ }
++
++# if KLIPS_DIVULGE_HMAC_KEY
++ KLIPS_PRINT(debug_pfkey && sysctl_ipsec_debug_verbose,
++ "klips_debug:pfkey_ipsec_sa_init: "
++ "hmac md5-96 key is 0x%08x %08x %08x %08x\n",
++ ntohl(*(((__u32 *)ipsp->ips_key_a)+0)),
++ ntohl(*(((__u32 *)ipsp->ips_key_a)+1)),
++ ntohl(*(((__u32 *)ipsp->ips_key_a)+2)),
++ ntohl(*(((__u32 *)ipsp->ips_key_a)+3)));
++# endif /* KLIPS_DIVULGE_HMAC_KEY */
++
++ ipsp->ips_auth_bits = AHMD596_ALEN * 8;
++
++ /* save the pointer to the key material */
++ akp = ipsp->ips_key_a;
++ aks = ipsp->ips_key_a_size;
++
++ KLIPS_PRINT(debug_pfkey && sysctl_ipsec_debug_verbose,
++ "klips_debug:pfkey_ipsec_sa_init: "
++ "allocating %lu bytes for md5_ctx.\n",
++ (unsigned long) sizeof(struct md5_ctx));
++ if((ipsp->ips_key_a = (caddr_t)
++ kmalloc(sizeof(struct md5_ctx), GFP_ATOMIC)) == NULL) {
++ ipsp->ips_key_a = akp;
++ SENDERR(ENOMEM);
++ }
++ ipsp->ips_key_a_size = sizeof(struct md5_ctx);
++
++ for (i = 0; i < DIVUP(ipsp->ips_key_bits_a, 8); i++) {
++ kb[i] = akp[i] ^ HMAC_IPAD;
++ }
++ for (; i < AHMD596_BLKLEN; i++) {
++ kb[i] = HMAC_IPAD;
++ }
++
++ ictx = &(((struct md5_ctx*)(ipsp->ips_key_a))->ictx);
++ MD5Init(ictx);
++ MD5Update(ictx, kb, AHMD596_BLKLEN);
++
++ for (i = 0; i < AHMD596_BLKLEN; i++) {
++ kb[i] ^= (HMAC_IPAD ^ HMAC_OPAD);
++ }
++
++ octx = &(((struct md5_ctx*)(ipsp->ips_key_a))->octx);
++ MD5Init(octx);
++ MD5Update(octx, kb, AHMD596_BLKLEN);
++
++# if KLIPS_DIVULGE_HMAC_KEY
++ KLIPS_PRINT(debug_pfkey && sysctl_ipsec_debug_verbose,
++ "klips_debug:pfkey_ipsec_sa_init: "
++ "MD5 ictx=0x%08x %08x %08x %08x octx=0x%08x %08x %08x %08x\n",
++ ((__u32*)ictx)[0],
++ ((__u32*)ictx)[1],
++ ((__u32*)ictx)[2],
++ ((__u32*)ictx)[3],
++ ((__u32*)octx)[0],
++ ((__u32*)octx)[1],
++ ((__u32*)octx)[2],
++ ((__u32*)octx)[3] );
++# endif /* KLIPS_DIVULGE_HMAC_KEY */
++
++ /* zero key buffer -- paranoid */
++ memset(akp, 0, aks);
++ kfree(akp);
++ }
++ break;
++# endif /* CONFIG_IPSEC_AUTH_HMAC_MD5 */
++# ifdef CONFIG_IPSEC_AUTH_HMAC_SHA1
++ case AH_SHA: {
++ unsigned char *akp;
++ unsigned int aks;
++ SHA1_CTX *ictx;
++ SHA1_CTX *octx;
++
++ if(ipsp->ips_key_bits_a != (AHSHA196_KLEN * 8)) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_ipsec_sa_init: "
++ "incorrect key size: %d bits -- must be %d bits\n"/*octets (bytes)\n"*/,
++ ipsp->ips_key_bits_a, AHSHA196_KLEN * 8);
++ SENDERR(EINVAL);
++ }
++
++# if KLIPS_DIVULGE_HMAC_KEY
++ KLIPS_PRINT(debug_pfkey && sysctl_ipsec_debug_verbose,
++ "klips_debug:pfkey_ipsec_sa_init: "
++ "hmac sha1-96 key is 0x%08x %08x %08x %08x\n",
++ ntohl(*(((__u32 *)ipsp->ips_key_a)+0)),
++ ntohl(*(((__u32 *)ipsp->ips_key_a)+1)),
++ ntohl(*(((__u32 *)ipsp->ips_key_a)+2)),
++ ntohl(*(((__u32 *)ipsp->ips_key_a)+3)));
++# endif /* KLIPS_DIVULGE_HMAC_KEY */
++
++ ipsp->ips_auth_bits = AHSHA196_ALEN * 8;
++
++ /* save the pointer to the key material */
++ akp = ipsp->ips_key_a;
++ aks = ipsp->ips_key_a_size;
++
++ KLIPS_PRINT(debug_pfkey && sysctl_ipsec_debug_verbose,
++ "klips_debug:pfkey_ipsec_sa_init: "
++ "allocating %lu bytes for sha1_ctx.\n",
++ (unsigned long) sizeof(struct sha1_ctx));
++ if((ipsp->ips_key_a = (caddr_t)
++ kmalloc(sizeof(struct sha1_ctx), GFP_ATOMIC)) == NULL) {
++ ipsp->ips_key_a = akp;
++ SENDERR(ENOMEM);
++ }
++ ipsp->ips_key_a_size = sizeof(struct sha1_ctx);
++
++ for (i = 0; i < DIVUP(ipsp->ips_key_bits_a, 8); i++) {
++ kb[i] = akp[i] ^ HMAC_IPAD;
++ }
++ for (; i < AHMD596_BLKLEN; i++) {
++ kb[i] = HMAC_IPAD;
++ }
++
++ ictx = &(((struct sha1_ctx*)(ipsp->ips_key_a))->ictx);
++ SHA1Init(ictx);
++ SHA1Update(ictx, kb, AHSHA196_BLKLEN);
++
++ for (i = 0; i < AHSHA196_BLKLEN; i++) {
++ kb[i] ^= (HMAC_IPAD ^ HMAC_OPAD);
++ }
++
++ octx = &(((struct sha1_ctx*)(ipsp->ips_key_a))->octx);
++ SHA1Init(octx);
++ SHA1Update(octx, kb, AHSHA196_BLKLEN);
++
++# if KLIPS_DIVULGE_HMAC_KEY
++ KLIPS_PRINT(debug_pfkey && sysctl_ipsec_debug_verbose,
++ "klips_debug:pfkey_ipsec_sa_init: "
++ "SHA1 ictx=0x%08x %08x %08x %08x octx=0x%08x %08x %08x %08x\n",
++ ((__u32*)ictx)[0],
++ ((__u32*)ictx)[1],
++ ((__u32*)ictx)[2],
++ ((__u32*)ictx)[3],
++ ((__u32*)octx)[0],
++ ((__u32*)octx)[1],
++ ((__u32*)octx)[2],
++ ((__u32*)octx)[3] );
++# endif /* KLIPS_DIVULGE_HMAC_KEY */
++ /* zero key buffer -- paranoid */
++ memset(akp, 0, aks);
++ kfree(akp);
++ }
++ break;
++# endif /* CONFIG_IPSEC_AUTH_HMAC_SHA1 */
++ default:
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_ipsec_sa_init: "
++ "authalg=%d support not available in the kernel",
++ ipsp->ips_authalg);
++ SENDERR(EINVAL);
++ }
++ break;
++#endif /* CONFIG_IPSEC_AH */
++#ifdef CONFIG_IPSEC_ESP
++ case IPPROTO_ESP: {
++#if defined (CONFIG_IPSEC_AUTH_HMAC_MD5) || defined (CONFIG_IPSEC_AUTH_HMAC_SHA1)
++ unsigned char *akp;
++ unsigned int aks;
++#endif
++#if defined (CONFIG_IPSEC_ENC_3DES)
++ unsigned char *ekp;
++ unsigned int eks;
++#endif
++
++ ipsp->ips_iv_size = 0;
++#ifdef CONFIG_IPSEC_ALG
++ if ((ixt_e=ipsp->ips_alg_enc)) {
++ ipsp->ips_iv_size = ixt_e->ixt_ivlen/8;
++ } else
++#endif /* CONFIG_IPSEC_ALG */
++ switch(ipsp->ips_encalg) {
++# ifdef CONFIG_IPSEC_ENC_3DES
++ case ESP_3DES:
++# endif /* CONFIG_IPSEC_ENC_3DES */
++# if defined(CONFIG_IPSEC_ENC_3DES)
++ KLIPS_PRINT(debug_pfkey && sysctl_ipsec_debug_verbose,
++ "klips_debug:pfkey_ipsec_sa_init: "
++ "allocating %u bytes for iv.\n",
++ EMT_ESPDES_IV_SZ);
++ if((ipsp->ips_iv = (caddr_t)
++ kmalloc((ipsp->ips_iv_size = EMT_ESPDES_IV_SZ), GFP_ATOMIC)) == NULL) {
++ SENDERR(ENOMEM);
++ }
++ prng_bytes(&ipsec_prng, (char *)ipsp->ips_iv, EMT_ESPDES_IV_SZ);
++ ipsp->ips_iv_bits = ipsp->ips_iv_size * 8;
++ ipsp->ips_iv_size = EMT_ESPDES_IV_SZ;
++ break;
++# endif /* defined(CONFIG_IPSEC_ENC_3DES) */
++ case ESP_NONE:
++ break;
++ default:
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_ipsec_sa_init: "
++ "encalg=%d support not available in the kernel",
++ ipsp->ips_encalg);
++ SENDERR(EINVAL);
++ }
++
++ /* Create IV */
++ if (ipsp->ips_iv_size) {
++ if((ipsp->ips_iv = (caddr_t)
++ kmalloc(ipsp->ips_iv_size, GFP_ATOMIC)) == NULL) {
++ SENDERR(ENOMEM);
++ }
++ prng_bytes(&ipsec_prng, (char *)ipsp->ips_iv, ipsp->ips_iv_size);
++ ipsp->ips_iv_bits = ipsp->ips_iv_size * 8;
++ }
++
++#ifdef CONFIG_IPSEC_ALG
++ if (ixt_e) {
++ if ((error=ipsec_alg_enc_key_create(ipsp)) < 0)
++ SENDERR(-error);
++ } else
++#endif /* CONFIG_IPSEC_ALG */
++ switch(ipsp->ips_encalg) {
++# ifdef CONFIG_IPSEC_ENC_3DES
++ case ESP_3DES:
++ if(ipsp->ips_key_bits_e != (EMT_ESP3DES_KEY_SZ * 8)) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_ipsec_sa_init: "
++ "incorrect encryption key size: %d bits -- must be %d bits\n"/*octets (bytes)\n"*/,
++ ipsp->ips_key_bits_e, EMT_ESP3DES_KEY_SZ * 8);
++ SENDERR(EINVAL);
++ }
++
++ /* save encryption key pointer */
++ ekp = ipsp->ips_key_e;
++ eks = ipsp->ips_key_e_size;
++
++ KLIPS_PRINT(debug_pfkey && sysctl_ipsec_debug_verbose,
++ "klips_debug:pfkey_ipsec_sa_init: "
++ "allocating %lu bytes for 3des.\n",
++ (unsigned long) (3 * sizeof(struct des_eks)));
++ if((ipsp->ips_key_e = (caddr_t)
++ kmalloc(3 * sizeof(struct des_eks), GFP_ATOMIC)) == NULL) {
++ ipsp->ips_key_e = ekp;
++ SENDERR(ENOMEM);
++ }
++ ipsp->ips_key_e_size = 3 * sizeof(struct des_eks);
++
++ for(i = 0; i < 3; i++) {
++#if KLIPS_DIVULGE_CYPHER_KEY
++ KLIPS_PRINT(debug_pfkey && sysctl_ipsec_debug_verbose,
++ "klips_debug:pfkey_ipsec_sa_init: "
++ "3des key %d/3 is 0x%08x%08x\n",
++ i + 1,
++ ntohl(*((__u32 *)ekp + i * 2)),
++ ntohl(*((__u32 *)ekp + i * 2 + 1)));
++# endif
++#if KLIPS_FIXES_DES_PARITY
++ /* force parity */
++ des_set_odd_parity((des_cblock *)(ekp + EMT_ESPDES_KEY_SZ * i));
++#endif
++ error = des_set_key((des_cblock *)(ekp + EMT_ESPDES_KEY_SZ * i),
++ ((struct des_eks *)(ipsp->ips_key_e))[i].ks);
++ if (error == -1)
++ printk("klips_debug:pfkey_ipsec_sa_init: "
++ "parity error in des key %d/3\n",
++ i + 1);
++ else if (error == -2)
++ printk("klips_debug:pfkey_ipsec_sa_init: "
++ "illegal weak des key %d/3\n", i + 1);
++ if (error) {
++ memset(ekp, 0, eks);
++ kfree(ekp);
++ SENDERR(EINVAL);
++ }
++ }
++
++ /* paranoid */
++ memset(ekp, 0, eks);
++ kfree(ekp);
++ break;
++# endif /* CONFIG_IPSEC_ENC_3DES */
++ case ESP_NONE:
++ break;
++ default:
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_ipsec_sa_init: "
++ "encalg=%d support not available in the kernel",
++ ipsp->ips_encalg);
++ SENDERR(EINVAL);
++ }
++
++#ifdef CONFIG_IPSEC_ALG
++ if ((ixt_a=ipsp->ips_alg_auth)) {
++ if ((error=ipsec_alg_auth_key_create(ipsp)) < 0)
++ SENDERR(-error);
++ } else
++#endif /* CONFIG_IPSEC_ALG */
++
++ switch(ipsp->ips_authalg) {
++# ifdef CONFIG_IPSEC_AUTH_HMAC_MD5
++ case AH_MD5: {
++ MD5_CTX *ictx;
++ MD5_CTX *octx;
++
++ if(ipsp->ips_key_bits_a != (AHMD596_KLEN * 8)) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_ipsec_sa_init: "
++ "incorrect authorisation key size: %d bits -- must be %d bits\n"/*octets (bytes)\n"*/,
++ ipsp->ips_key_bits_a,
++ AHMD596_KLEN * 8);
++ SENDERR(EINVAL);
++ }
++
++# if KLIPS_DIVULGE_HMAC_KEY
++ KLIPS_PRINT(debug_pfkey && sysctl_ipsec_debug_verbose,
++ "klips_debug:pfkey_ipsec_sa_init: "
++ "hmac md5-96 key is 0x%08x %08x %08x %08x\n",
++ ntohl(*(((__u32 *)(ipsp->ips_key_a))+0)),
++ ntohl(*(((__u32 *)(ipsp->ips_key_a))+1)),
++ ntohl(*(((__u32 *)(ipsp->ips_key_a))+2)),
++ ntohl(*(((__u32 *)(ipsp->ips_key_a))+3)));
++# endif /* KLIPS_DIVULGE_HMAC_KEY */
++ ipsp->ips_auth_bits = AHMD596_ALEN * 8;
++
++ /* save the pointer to the key material */
++ akp = ipsp->ips_key_a;
++ aks = ipsp->ips_key_a_size;
++
++ KLIPS_PRINT(debug_pfkey && sysctl_ipsec_debug_verbose,
++ "klips_debug:pfkey_ipsec_sa_init: "
++ "allocating %lu bytes for md5_ctx.\n",
++ (unsigned long) sizeof(struct md5_ctx));
++ if((ipsp->ips_key_a = (caddr_t)
++ kmalloc(sizeof(struct md5_ctx), GFP_ATOMIC)) == NULL) {
++ ipsp->ips_key_a = akp;
++ SENDERR(ENOMEM);
++ }
++ ipsp->ips_key_a_size = sizeof(struct md5_ctx);
++
++ for (i = 0; i < DIVUP(ipsp->ips_key_bits_a, 8); i++) {
++ kb[i] = akp[i] ^ HMAC_IPAD;
++ }
++ for (; i < AHMD596_BLKLEN; i++) {
++ kb[i] = HMAC_IPAD;
++ }
++
++ ictx = &(((struct md5_ctx*)(ipsp->ips_key_a))->ictx);
++ MD5Init(ictx);
++ MD5Update(ictx, kb, AHMD596_BLKLEN);
++
++ for (i = 0; i < AHMD596_BLKLEN; i++) {
++ kb[i] ^= (HMAC_IPAD ^ HMAC_OPAD);
++ }
++
++ octx = &(((struct md5_ctx*)(ipsp->ips_key_a))->octx);
++ MD5Init(octx);
++ MD5Update(octx, kb, AHMD596_BLKLEN);
++
++# if KLIPS_DIVULGE_HMAC_KEY
++ KLIPS_PRINT(debug_pfkey && sysctl_ipsec_debug_verbose,
++ "klips_debug:pfkey_ipsec_sa_init: "
++ "MD5 ictx=0x%08x %08x %08x %08x octx=0x%08x %08x %08x %08x\n",
++ ((__u32*)ictx)[0],
++ ((__u32*)ictx)[1],
++ ((__u32*)ictx)[2],
++ ((__u32*)ictx)[3],
++ ((__u32*)octx)[0],
++ ((__u32*)octx)[1],
++ ((__u32*)octx)[2],
++ ((__u32*)octx)[3] );
++# endif /* KLIPS_DIVULGE_HMAC_KEY */
++ /* paranoid */
++ memset(akp, 0, aks);
++ kfree(akp);
++ break;
++ }
++# endif /* CONFIG_IPSEC_AUTH_HMAC_MD5 */
++# ifdef CONFIG_IPSEC_AUTH_HMAC_SHA1
++ case AH_SHA: {
++ SHA1_CTX *ictx;
++ SHA1_CTX *octx;
++
++ if(ipsp->ips_key_bits_a != (AHSHA196_KLEN * 8)) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_ipsec_sa_init: "
++ "incorrect authorisation key size: %d bits -- must be %d bits\n"/*octets (bytes)\n"*/,
++ ipsp->ips_key_bits_a,
++ AHSHA196_KLEN * 8);
++ SENDERR(EINVAL);
++ }
++
++# if KLIPS_DIVULGE_HMAC_KEY
++ KLIPS_PRINT(debug_pfkey && sysctl_ipsec_debug_verbose,
++ "klips_debug:pfkey_ipsec_sa_init: "
++ "hmac sha1-96 key is 0x%08x %08x %08x %08x\n",
++ ntohl(*(((__u32 *)ipsp->ips_key_a)+0)),
++ ntohl(*(((__u32 *)ipsp->ips_key_a)+1)),
++ ntohl(*(((__u32 *)ipsp->ips_key_a)+2)),
++ ntohl(*(((__u32 *)ipsp->ips_key_a)+3)));
++# endif /* KLIPS_DIVULGE_HMAC_KEY */
++ ipsp->ips_auth_bits = AHSHA196_ALEN * 8;
++
++ /* save the pointer to the key material */
++ akp = ipsp->ips_key_a;
++ aks = ipsp->ips_key_a_size;
++
++ KLIPS_PRINT(debug_pfkey && sysctl_ipsec_debug_verbose,
++ "klips_debug:pfkey_ipsec_sa_init: "
++ "allocating %lu bytes for sha1_ctx.\n",
++ (unsigned long) sizeof(struct sha1_ctx));
++ if((ipsp->ips_key_a = (caddr_t)
++ kmalloc(sizeof(struct sha1_ctx), GFP_ATOMIC)) == NULL) {
++ ipsp->ips_key_a = akp;
++ SENDERR(ENOMEM);
++ }
++ ipsp->ips_key_a_size = sizeof(struct sha1_ctx);
++
++ for (i = 0; i < DIVUP(ipsp->ips_key_bits_a, 8); i++) {
++ kb[i] = akp[i] ^ HMAC_IPAD;
++ }
++ for (; i < AHMD596_BLKLEN; i++) {
++ kb[i] = HMAC_IPAD;
++ }
++
++ ictx = &(((struct sha1_ctx*)(ipsp->ips_key_a))->ictx);
++ SHA1Init(ictx);
++ SHA1Update(ictx, kb, AHSHA196_BLKLEN);
++
++ for (i = 0; i < AHSHA196_BLKLEN; i++) {
++ kb[i] ^= (HMAC_IPAD ^ HMAC_OPAD);
++ }
++
++ octx = &((struct sha1_ctx*)(ipsp->ips_key_a))->octx;
++ SHA1Init(octx);
++ SHA1Update(octx, kb, AHSHA196_BLKLEN);
++
++# if KLIPS_DIVULGE_HMAC_KEY
++ KLIPS_PRINT(debug_pfkey && sysctl_ipsec_debug_verbose,
++ "klips_debug:pfkey_ipsec_sa_init: "
++ "SHA1 ictx=0x%08x %08x %08x %08x octx=0x%08x %08x %08x %08x\n",
++ ((__u32*)ictx)[0],
++ ((__u32*)ictx)[1],
++ ((__u32*)ictx)[2],
++ ((__u32*)ictx)[3],
++ ((__u32*)octx)[0],
++ ((__u32*)octx)[1],
++ ((__u32*)octx)[2],
++ ((__u32*)octx)[3] );
++# endif /* KLIPS_DIVULGE_HMAC_KEY */
++ memset(akp, 0, aks);
++ kfree(akp);
++ break;
++ }
++# endif /* CONFIG_IPSEC_AUTH_HMAC_SHA1 */
++ case AH_NONE:
++ break;
++ default:
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_ipsec_sa_init: "
++ "authalg=%d support not available in the kernel.\n",
++ ipsp->ips_authalg);
++ SENDERR(EINVAL);
++ }
++ }
++ break;
++#endif /* !CONFIG_IPSEC_ESP */
++#ifdef CONFIG_IPSEC_IPCOMP
++ case IPPROTO_COMP:
++ ipsp->ips_comp_adapt_tries = 0;
++ ipsp->ips_comp_adapt_skip = 0;
++ ipsp->ips_comp_ratio_cbytes = 0;
++ ipsp->ips_comp_ratio_dbytes = 0;
++ break;
++#endif /* CONFIG_IPSEC_IPCOMP */
++ default:
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_ipsec_sa_init: "
++ "proto=%d unknown.\n",
++ ipsp->ips_said.proto);
++ SENDERR(EINVAL);
++ }
++
++ errlab:
++ return(error);
++}
++
++
++int
++pfkey_safe_build(int error, struct sadb_ext *extensions[SADB_MAX+1])
++{
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_safe_build: "
++ "error=%d\n",
++ error);
++ if (!error) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_safe_build:"
++ "success.\n");
++ return 1;
++ } else {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_safe_build:"
++ "caught error %d\n",
++ error);
++ pfkey_extensions_free(extensions);
++ return 0;
++ }
++}
++
++
++DEBUG_NO_STATIC int
++pfkey_getspi_parse(struct sock *sk, struct sadb_ext **extensions, struct pfkey_extracted_data* extr)
++{
++ int error = 0;
++ ipsec_spi_t minspi = htonl(256), maxspi = htonl(-1L);
++ int found_avail = 0;
++ struct ipsec_sa *ipsq;
++ char sa[SATOT_BUF];
++ size_t sa_len;
++ struct sadb_ext *extensions_reply[SADB_EXT_MAX+1];
++ struct sadb_msg *pfkey_reply = NULL;
++ struct socket_list *pfkey_socketsp;
++ uint8_t satype = ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_satype;
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_getspi_parse: .\n");
++
++ pfkey_extensions_init(extensions_reply);
++
++ if(extr == NULL || extr->ips == NULL) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_getspi_parse: "
++ "error, extr or extr->ipsec_sa pointer NULL\n");
++ SENDERR(EINVAL);
++ }
++
++ if(extensions[SADB_EXT_SPIRANGE]) {
++ minspi = ((struct sadb_spirange *)extensions[SADB_EXT_SPIRANGE])->sadb_spirange_min;
++ maxspi = ((struct sadb_spirange *)extensions[SADB_EXT_SPIRANGE])->sadb_spirange_max;
++ }
++
++ if(maxspi == minspi) {
++ extr->ips->ips_said.spi = maxspi;
++ ipsq = ipsec_sa_getbyid(&(extr->ips->ips_said));
++ if(ipsq != NULL) {
++ sa_len = satot(&extr->ips->ips_said, 0, sa, sizeof(sa));
++ ipsec_sa_put(ipsq);
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_getspi_parse: "
++ "EMT_GETSPI found an old ipsec_sa for SA: %s, delete it first.\n",
++ sa_len ? sa : " (error)");
++ SENDERR(EEXIST);
++ } else {
++ found_avail = 1;
++ }
++ } else {
++ int i = 0;
++ __u32 rand_val;
++ __u32 spi_diff;
++ while( ( i < (spi_diff = (ntohl(maxspi) - ntohl(minspi)))) && !found_avail ) {
++ prng_bytes(&ipsec_prng, (char *) &(rand_val),
++ ( (spi_diff < (2^8)) ? 1 :
++ ( (spi_diff < (2^16)) ? 2 :
++ ( (spi_diff < (2^24)) ? 3 :
++ 4 ) ) ) );
++ extr->ips->ips_said.spi = htonl(ntohl(minspi) +
++ (rand_val %
++ (spi_diff + 1)));
++ i++;
++ ipsq = ipsec_sa_getbyid(&(extr->ips->ips_said));
++ if(ipsq == NULL) {
++ found_avail = 1;
++ } else {
++ ipsec_sa_put(ipsq);
++ }
++ }
++ }
++
++ sa_len = satot(&extr->ips->ips_said, 0, sa, sizeof(sa));
++
++ if (!found_avail) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_getspi_parse: "
++ "found an old ipsec_sa for SA: %s, delete it first.\n",
++ sa_len ? sa : " (error)");
++ SENDERR(EEXIST);
++ }
++
++ if(ip_chk_addr((unsigned long)extr->ips->ips_said.dst.u.v4.sin_addr.s_addr) == IS_MYADDR) {
++ extr->ips->ips_flags |= EMT_INBOUND;
++ }
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_getspi_parse: "
++ "existing ipsec_sa not found (this is good) for SA: %s, %s-bound, allocating.\n",
++ sa_len ? sa : " (error)",
++ extr->ips->ips_flags & EMT_INBOUND ? "in" : "out");
++
++ /* XXX extr->ips->ips_rcvif = &(enc_softc[em->em_if].enc_if);*/
++ extr->ips->ips_rcvif = NULL;
++ extr->ips->ips_life.ipl_addtime.ipl_count = jiffies/HZ;
++
++ extr->ips->ips_state = SADB_SASTATE_LARVAL;
++
++ if(!extr->ips->ips_life.ipl_allocations.ipl_count) {
++ extr->ips->ips_life.ipl_allocations.ipl_count += 1;
++ }
++
++ if(!(pfkey_safe_build(error = pfkey_msg_hdr_build(&extensions_reply[0],
++ SADB_GETSPI,
++ satype,
++ 0,
++ ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_seq,
++ ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_pid),
++ extensions_reply)
++ && pfkey_safe_build(error = pfkey_sa_ref_build(&extensions_reply[SADB_EXT_SA],
++ SADB_EXT_SA,
++ extr->ips->ips_said.spi,
++ 0,
++ SADB_SASTATE_LARVAL,
++ 0,
++ 0,
++ 0,
++ extr->ips->ips_ref),
++ extensions_reply)
++ && pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_EXT_ADDRESS_SRC],
++ SADB_EXT_ADDRESS_SRC,
++ 0, /*extr->ips->ips_said.proto,*/
++ 0,
++ extr->ips->ips_addr_s),
++ extensions_reply)
++ && pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_EXT_ADDRESS_DST],
++ SADB_EXT_ADDRESS_DST,
++ 0, /*extr->ips->ips_said.proto,*/
++ 0,
++ extr->ips->ips_addr_d),
++ extensions_reply) )) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_getspi_parse: "
++ "failed to build the getspi reply message extensions\n");
++ goto errlab;
++ }
++
++ if((error = pfkey_msg_build(&pfkey_reply, extensions_reply, EXT_BITS_OUT))) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_getspi_parse: "
++ "failed to build the getspi reply message\n");
++ SENDERR(-error);
++ }
++ for(pfkey_socketsp = pfkey_open_sockets;
++ pfkey_socketsp;
++ pfkey_socketsp = pfkey_socketsp->next) {
++ if((error = pfkey_upmsg(pfkey_socketsp->socketp, pfkey_reply))) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_getspi_parse: "
++ "sending up getspi reply message for satype=%d(%s) to socket=0p%p failed with error=%d.\n",
++ satype,
++ satype2name(satype),
++ pfkey_socketsp->socketp,
++ error);
++ SENDERR(-error);
++ }
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_getspi_parse: "
++ "sending up getspi reply message for satype=%d(%s) to socket=0p%p succeeded.\n",
++ satype,
++ satype2name(satype),
++ pfkey_socketsp->socketp);
++ }
++
++ if((error = ipsec_sa_add(extr->ips))) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_getspi_parse: "
++ "failed to add the larval SA=%s with error=%d.\n",
++ sa_len ? sa : " (error)",
++ error);
++ SENDERR(-error);
++ }
++ extr->ips = NULL;
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_getspi_parse: "
++ "successful for SA: %s\n",
++ sa_len ? sa : " (error)");
++
++ errlab:
++ if (pfkey_reply) {
++ pfkey_msg_free(&pfkey_reply);
++ }
++ pfkey_extensions_free(extensions_reply);
++ return error;
++}
++
++DEBUG_NO_STATIC int
++pfkey_update_parse(struct sock *sk, struct sadb_ext **extensions, struct pfkey_extracted_data* extr)
++{
++ int error = 0;
++ struct ipsec_sa* ipsq;
++ char sa[SATOT_BUF];
++ size_t sa_len;
++ struct sadb_ext *extensions_reply[SADB_EXT_MAX+1];
++ struct sadb_msg *pfkey_reply = NULL;
++ struct socket_list *pfkey_socketsp;
++ uint8_t satype = ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_satype;
++#ifdef CONFIG_IPSEC_NAT_TRAVERSAL
++ struct ipsec_sa *nat_t_ips_saved = NULL;
++#endif
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_update_parse: .\n");
++
++ pfkey_extensions_init(extensions_reply);
++
++ if(((struct sadb_sa*)extensions[SADB_EXT_SA])->sadb_sa_state != SADB_SASTATE_MATURE) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_update_parse: "
++ "error, sa_state=%d must be MATURE=%d\n",
++ ((struct sadb_sa*)extensions[SADB_EXT_SA])->sadb_sa_state,
++ SADB_SASTATE_MATURE);
++ SENDERR(EINVAL);
++ }
++
++ if(extr == NULL || extr->ips == NULL) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_update_parse: "
++ "error, extr or extr->ips pointer NULL\n");
++ SENDERR(EINVAL);
++ }
++
++ sa_len = satot(&extr->ips->ips_said, 0, sa, sizeof(sa));
++
++ spin_lock_bh(&tdb_lock);
++
++ ipsq = ipsec_sa_getbyid(&(extr->ips->ips_said));
++ if (ipsq == NULL) {
++ spin_unlock_bh(&tdb_lock);
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_update_parse: "
++ "reserved ipsec_sa for SA: %s not found. Call SADB_GETSPI first or call SADB_ADD instead.\n",
++ sa_len ? sa : " (error)");
++ SENDERR(ENOENT);
++ }
++
++ if(ip_chk_addr((unsigned long)extr->ips->ips_said.dst.u.v4.sin_addr.s_addr) == IS_MYADDR) {
++ extr->ips->ips_flags |= EMT_INBOUND;
++ }
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_update_parse: "
++ "existing ipsec_sa found (this is good) for SA: %s, %s-bound, updating.\n",
++ sa_len ? sa : " (error)",
++ extr->ips->ips_flags & EMT_INBOUND ? "in" : "out");
++
++#ifdef CONFIG_IPSEC_NAT_TRAVERSAL
++ if (extr->ips->ips_natt_sport || extr->ips->ips_natt_dport) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_update_parse: only updating NAT-T ports "
++ "(%u:%u -> %u:%u)\n",
++ ipsq->ips_natt_sport, ipsq->ips_natt_dport,
++ extr->ips->ips_natt_sport, extr->ips->ips_natt_dport);
++
++ if (extr->ips->ips_natt_sport) {
++ ipsq->ips_natt_sport = extr->ips->ips_natt_sport;
++ if (ipsq->ips_addr_s->sa_family == AF_INET) {
++ ((struct sockaddr_in *)(ipsq->ips_addr_s))->sin_port = htons(extr->ips->ips_natt_sport);
++ }
++ }
++
++ if (extr->ips->ips_natt_dport) {
++ ipsq->ips_natt_dport = extr->ips->ips_natt_dport;
++ if (ipsq->ips_addr_d->sa_family == AF_INET) {
++ ((struct sockaddr_in *)(ipsq->ips_addr_d))->sin_port = htons(extr->ips->ips_natt_dport);
++ }
++ }
++
++ nat_t_ips_saved = extr->ips;
++ extr->ips = ipsq;
++ }
++ else {
++#endif
++
++ /* XXX extr->ips->ips_rcvif = &(enc_softc[em->em_if].enc_if);*/
++ extr->ips->ips_rcvif = NULL;
++ if ((error = pfkey_ipsec_sa_init(extr->ips, extensions))) {
++ ipsec_sa_put(ipsq);
++ spin_unlock_bh(&tdb_lock);
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_update_parse: "
++ "not successful for SA: %s, deleting.\n",
++ sa_len ? sa : " (error)");
++ SENDERR(-error);
++ }
++
++ extr->ips->ips_life.ipl_addtime.ipl_count = ipsq->ips_life.ipl_addtime.ipl_count;
++ ipsec_sa_put(ipsq);
++ if((error = ipsec_sa_delchain(ipsq))) {
++ spin_unlock_bh(&tdb_lock);
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_update_parse: "
++ "error=%d, trouble deleting intermediate ipsec_sa for SA=%s.\n",
++ error,
++ sa_len ? sa : " (error)");
++ SENDERR(-error);
++ }
++#ifdef CONFIG_IPSEC_NAT_TRAVERSAL
++ }
++#endif
++
++ spin_unlock_bh(&tdb_lock);
++
++ if(!(pfkey_safe_build(error = pfkey_msg_hdr_build(&extensions_reply[0],
++ SADB_UPDATE,
++ satype,
++ 0,
++ ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_seq,
++ ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_pid),
++ extensions_reply)
++ && pfkey_safe_build(error = pfkey_sa_ref_build(&extensions_reply[SADB_EXT_SA],
++ SADB_EXT_SA,
++ extr->ips->ips_said.spi,
++ extr->ips->ips_replaywin,
++ extr->ips->ips_state,
++ extr->ips->ips_authalg,
++ extr->ips->ips_encalg,
++ extr->ips->ips_flags,
++ extr->ips->ips_ref),
++ extensions_reply)
++ /* The 3 lifetime extentions should only be sent if non-zero. */
++ && (extensions[SADB_EXT_LIFETIME_HARD]
++ ? pfkey_safe_build(error = pfkey_lifetime_build(&extensions_reply[SADB_EXT_LIFETIME_HARD],
++ SADB_EXT_LIFETIME_HARD,
++ extr->ips->ips_life.ipl_allocations.ipl_hard,
++ extr->ips->ips_life.ipl_bytes.ipl_hard,
++ extr->ips->ips_life.ipl_addtime.ipl_hard,
++ extr->ips->ips_life.ipl_usetime.ipl_hard,
++ extr->ips->ips_life.ipl_packets.ipl_hard),
++ extensions_reply) : 1)
++ && (extensions[SADB_EXT_LIFETIME_SOFT]
++ ? pfkey_safe_build(error = pfkey_lifetime_build(&extensions_reply[SADB_EXT_LIFETIME_SOFT],
++ SADB_EXT_LIFETIME_SOFT,
++ extr->ips->ips_life.ipl_allocations.ipl_count,
++ extr->ips->ips_life.ipl_bytes.ipl_count,
++ extr->ips->ips_life.ipl_addtime.ipl_count,
++ extr->ips->ips_life.ipl_usetime.ipl_count,
++ extr->ips->ips_life.ipl_packets.ipl_count),
++ extensions_reply) : 1)
++ && (extr->ips->ips_life.ipl_allocations.ipl_count
++ || extr->ips->ips_life.ipl_bytes.ipl_count
++ || extr->ips->ips_life.ipl_addtime.ipl_count
++ || extr->ips->ips_life.ipl_usetime.ipl_count
++ || extr->ips->ips_life.ipl_packets.ipl_count
++
++ ? pfkey_safe_build(error = pfkey_lifetime_build(&extensions_reply[SADB_EXT_LIFETIME_CURRENT],
++ SADB_EXT_LIFETIME_CURRENT,
++ extr->ips->ips_life.ipl_allocations.ipl_count,
++ extr->ips->ips_life.ipl_bytes.ipl_count,
++ extr->ips->ips_life.ipl_addtime.ipl_count,
++ extr->ips->ips_life.ipl_usetime.ipl_count,
++ extr->ips->ips_life.ipl_packets.ipl_count),
++ extensions_reply) : 1)
++ && pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_EXT_ADDRESS_SRC],
++ SADB_EXT_ADDRESS_SRC,
++ 0, /*extr->ips->ips_said.proto,*/
++ 0,
++ extr->ips->ips_addr_s),
++ extensions_reply)
++ && pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_EXT_ADDRESS_DST],
++ SADB_EXT_ADDRESS_DST,
++ 0, /*extr->ips->ips_said.proto,*/
++ 0,
++ extr->ips->ips_addr_d),
++ extensions_reply)
++ && (extr->ips->ips_ident_s.data
++ ? pfkey_safe_build(error = pfkey_ident_build(&extensions_reply[SADB_EXT_IDENTITY_SRC],
++ SADB_EXT_IDENTITY_SRC,
++ extr->ips->ips_ident_s.type,
++ extr->ips->ips_ident_s.id,
++ extr->ips->ips_ident_s.len,
++ extr->ips->ips_ident_s.data),
++ extensions_reply) : 1)
++ && (extr->ips->ips_ident_d.data
++ ? pfkey_safe_build(error = pfkey_ident_build(&extensions_reply[SADB_EXT_IDENTITY_DST],
++ SADB_EXT_IDENTITY_DST,
++ extr->ips->ips_ident_d.type,
++ extr->ips->ips_ident_d.id,
++ extr->ips->ips_ident_d.len,
++ extr->ips->ips_ident_d.data),
++ extensions_reply) : 1)
++#if 0
++ /* FIXME: This won't work yet because I have not finished
++ it. */
++ && (extr->ips->ips_sens_
++ ? pfkey_safe_build(error = pfkey_sens_build(&extensions_reply[SADB_EXT_SENSITIVITY],
++ extr->ips->ips_sens_dpd,
++ extr->ips->ips_sens_sens_level,
++ extr->ips->ips_sens_sens_len,
++ extr->ips->ips_sens_sens_bitmap,
++ extr->ips->ips_sens_integ_level,
++ extr->ips->ips_sens_integ_len,
++ extr->ips->ips_sens_integ_bitmap),
++ extensions_reply) : 1)
++#endif
++ )) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_update_parse: "
++ "failed to build the update reply message extensions\n");
++ SENDERR(-error);
++ }
++
++ if((error = pfkey_msg_build(&pfkey_reply, extensions_reply, EXT_BITS_OUT))) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_update_parse: "
++ "failed to build the update reply message\n");
++ SENDERR(-error);
++ }
++ for(pfkey_socketsp = pfkey_open_sockets;
++ pfkey_socketsp;
++ pfkey_socketsp = pfkey_socketsp->next) {
++ if((error = pfkey_upmsg(pfkey_socketsp->socketp, pfkey_reply))) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_update_parse: "
++ "sending up update reply message for satype=%d(%s) to socket=0p%p failed with error=%d.\n",
++ satype,
++ satype2name(satype),
++ pfkey_socketsp->socketp,
++ error);
++ SENDERR(-error);
++ }
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_update_parse: "
++ "sending up update reply message for satype=%d(%s) to socket=0p%p succeeded.\n",
++ satype,
++ satype2name(satype),
++ pfkey_socketsp->socketp);
++ }
++
++#ifdef CONFIG_IPSEC_NAT_TRAVERSAL
++ if (nat_t_ips_saved) {
++ /**
++ * As we _really_ update existing SA, we keep tdbq and need to delete
++ * parsed ips (nat_t_ips_saved, was extr->ips).
++ *
++ * goto errlab with extr->ips = nat_t_ips_saved will free it.
++ */
++
++ extr->ips = nat_t_ips_saved;
++
++ error = 0;
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_update_parse (NAT-T ports): "
++ "successful for SA: %s\n",
++ sa_len ? sa : " (error)");
++
++ goto errlab;
++ }
++#endif
++
++ if((error = ipsec_sa_add(extr->ips))) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_update_parse: "
++ "failed to update the mature SA=%s with error=%d.\n",
++ sa_len ? sa : " (error)",
++ error);
++ SENDERR(-error);
++ }
++ extr->ips = NULL;
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_update_parse: "
++ "successful for SA: %s\n",
++ sa_len ? sa : " (error)");
++
++ errlab:
++ if (pfkey_reply) {
++ pfkey_msg_free(&pfkey_reply);
++ }
++ pfkey_extensions_free(extensions_reply);
++ return error;
++}
++
++DEBUG_NO_STATIC int
++pfkey_add_parse(struct sock *sk, struct sadb_ext **extensions, struct pfkey_extracted_data* extr)
++{
++ int error = 0;
++ struct ipsec_sa* ipsq;
++ char sa[SATOT_BUF];
++ size_t sa_len;
++ struct sadb_ext *extensions_reply[SADB_EXT_MAX+1];
++ struct sadb_msg *pfkey_reply = NULL;
++ struct socket_list *pfkey_socketsp;
++ uint8_t satype = ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_satype;
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_add_parse: .\n");
++
++ pfkey_extensions_init(extensions_reply);
++
++ if(((struct sadb_sa*)extensions[SADB_EXT_SA])->sadb_sa_state != SADB_SASTATE_MATURE) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_add_parse: "
++ "error, sa_state=%d must be MATURE=%d\n",
++ ((struct sadb_sa*)extensions[SADB_EXT_SA])->sadb_sa_state,
++ SADB_SASTATE_MATURE);
++ SENDERR(EINVAL);
++ }
++
++ if(!extr || !extr->ips) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_add_parse: "
++ "extr or extr->ips pointer NULL\n");
++ SENDERR(EINVAL);
++ }
++
++ sa_len = satot(&extr->ips->ips_said, 0, sa, sizeof(sa));
++
++ ipsq = ipsec_sa_getbyid(&(extr->ips->ips_said));
++ if(ipsq != NULL) {
++ ipsec_sa_put(ipsq);
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_add_parse: "
++ "found an old ipsec_sa for SA%s, delete it first.\n",
++ sa_len ? sa : " (error)");
++ SENDERR(EEXIST);
++ }
++
++ if(ip_chk_addr((unsigned long)extr->ips->ips_said.dst.u.v4.sin_addr.s_addr) == IS_MYADDR) {
++ extr->ips->ips_flags |= EMT_INBOUND;
++ }
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_add_parse: "
++ "existing ipsec_sa not found (this is good) for SA%s, %s-bound, allocating.\n",
++ sa_len ? sa : " (error)",
++ extr->ips->ips_flags & EMT_INBOUND ? "in" : "out");
++
++ /* XXX extr->ips->ips_rcvif = &(enc_softc[em->em_if].enc_if);*/
++ extr->ips->ips_rcvif = NULL;
++
++ if ((error = pfkey_ipsec_sa_init(extr->ips, extensions))) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_add_parse: "
++ "not successful for SA: %s, deleting.\n",
++ sa_len ? sa : " (error)");
++ SENDERR(-error);
++ }
++
++ extr->ips->ips_life.ipl_addtime.ipl_count = jiffies / HZ;
++ if(!extr->ips->ips_life.ipl_allocations.ipl_count) {
++ extr->ips->ips_life.ipl_allocations.ipl_count += 1;
++ }
++
++ if(!(pfkey_safe_build(error = pfkey_msg_hdr_build(&extensions_reply[0],
++ SADB_ADD,
++ satype,
++ 0,
++ ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_seq,
++ ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_pid),
++ extensions_reply)
++ && pfkey_safe_build(error = pfkey_sa_ref_build(&extensions_reply[SADB_EXT_SA],
++ SADB_EXT_SA,
++ extr->ips->ips_said.spi,
++ extr->ips->ips_replaywin,
++ extr->ips->ips_state,
++ extr->ips->ips_authalg,
++ extr->ips->ips_encalg,
++ extr->ips->ips_flags,
++ extr->ips->ips_ref),
++ extensions_reply)
++ /* The 3 lifetime extentions should only be sent if non-zero. */
++ && (extensions[SADB_EXT_LIFETIME_HARD]
++ ? pfkey_safe_build(error = pfkey_lifetime_build(&extensions_reply[SADB_EXT_LIFETIME_HARD],
++ SADB_EXT_LIFETIME_HARD,
++ extr->ips->ips_life.ipl_allocations.ipl_hard,
++ extr->ips->ips_life.ipl_bytes.ipl_hard,
++ extr->ips->ips_life.ipl_addtime.ipl_hard,
++ extr->ips->ips_life.ipl_usetime.ipl_hard,
++ extr->ips->ips_life.ipl_packets.ipl_hard),
++ extensions_reply) : 1)
++ && (extensions[SADB_EXT_LIFETIME_SOFT]
++ ? pfkey_safe_build(error = pfkey_lifetime_build(&extensions_reply[SADB_EXT_LIFETIME_SOFT],
++ SADB_EXT_LIFETIME_SOFT,
++ extr->ips->ips_life.ipl_allocations.ipl_soft,
++ extr->ips->ips_life.ipl_bytes.ipl_soft,
++ extr->ips->ips_life.ipl_addtime.ipl_soft,
++ extr->ips->ips_life.ipl_usetime.ipl_soft,
++ extr->ips->ips_life.ipl_packets.ipl_soft),
++ extensions_reply) : 1)
++ && pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_EXT_ADDRESS_SRC],
++ SADB_EXT_ADDRESS_SRC,
++ 0, /*extr->ips->ips_said.proto,*/
++ 0,
++ extr->ips->ips_addr_s),
++ extensions_reply)
++ && pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_EXT_ADDRESS_DST],
++ SADB_EXT_ADDRESS_DST,
++ 0, /*extr->ips->ips_said.proto,*/
++ 0,
++ extr->ips->ips_addr_d),
++ extensions_reply)
++ && (extr->ips->ips_ident_s.data
++ ? pfkey_safe_build(error = pfkey_ident_build(&extensions_reply[SADB_EXT_IDENTITY_SRC],
++ SADB_EXT_IDENTITY_SRC,
++ extr->ips->ips_ident_s.type,
++ extr->ips->ips_ident_s.id,
++ extr->ips->ips_ident_s.len,
++ extr->ips->ips_ident_s.data),
++ extensions_reply) : 1)
++ && (extr->ips->ips_ident_d.data
++ ? pfkey_safe_build(error = pfkey_ident_build(&extensions_reply[SADB_EXT_IDENTITY_DST],
++ SADB_EXT_IDENTITY_DST,
++ extr->ips->ips_ident_d.type,
++ extr->ips->ips_ident_d.id,
++ extr->ips->ips_ident_d.len,
++ extr->ips->ips_ident_d.data),
++ extensions_reply) : 1)
++#if 0
++ /* FIXME: This won't work yet because I have not finished
++ it. */
++ && (extr->ips->ips_sens_
++ ? pfkey_safe_build(error = pfkey_sens_build(&extensions_reply[SADB_EXT_SENSITIVITY],
++ extr->ips->ips_sens_dpd,
++ extr->ips->ips_sens_sens_level,
++ extr->ips->ips_sens_sens_len,
++ extr->ips->ips_sens_sens_bitmap,
++ extr->ips->ips_sens_integ_level,
++ extr->ips->ips_sens_integ_len,
++ extr->ips->ips_sens_integ_bitmap),
++ extensions_reply) : 1)
++#endif
++ )) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_add_parse: "
++ "failed to build the add reply message extensions\n");
++ SENDERR(-error);
++ }
++
++ if((error = pfkey_msg_build(&pfkey_reply, extensions_reply, EXT_BITS_OUT))) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_add_parse: "
++ "failed to build the add reply message\n");
++ SENDERR(-error);
++ }
++ for(pfkey_socketsp = pfkey_open_sockets;
++ pfkey_socketsp;
++ pfkey_socketsp = pfkey_socketsp->next) {
++ if((error = pfkey_upmsg(pfkey_socketsp->socketp, pfkey_reply))) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_add_parse: "
++ "sending up add reply message for satype=%d(%s) to socket=0p%p failed with error=%d.\n",
++ satype,
++ satype2name(satype),
++ pfkey_socketsp->socketp,
++ error);
++ SENDERR(-error);
++ }
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_add_parse: "
++ "sending up add reply message for satype=%d(%s) to socket=0p%p succeeded.\n",
++ satype,
++ satype2name(satype),
++ pfkey_socketsp->socketp);
++ }
++
++ if((error = ipsec_sa_add(extr->ips))) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_add_parse: "
++ "failed to add the mature SA=%s with error=%d.\n",
++ sa_len ? sa : " (error)",
++ error);
++ SENDERR(-error);
++ }
++ extr->ips = NULL;
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_add_parse: "
++ "successful for SA: %s\n",
++ sa_len ? sa : " (error)");
++
++ errlab:
++ if (pfkey_reply) {
++ pfkey_msg_free(&pfkey_reply);
++ }
++ pfkey_extensions_free(extensions_reply);
++ return error;
++}
++
++DEBUG_NO_STATIC int
++pfkey_delete_parse(struct sock *sk, struct sadb_ext **extensions, struct pfkey_extracted_data* extr)
++{
++ struct ipsec_sa *ipsp;
++ char sa[SATOT_BUF];
++ size_t sa_len;
++ int error = 0;
++ struct sadb_ext *extensions_reply[SADB_EXT_MAX+1];
++ struct sadb_msg *pfkey_reply = NULL;
++ struct socket_list *pfkey_socketsp;
++ uint8_t satype = ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_satype;
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_delete_parse: .\n");
++
++ pfkey_extensions_init(extensions_reply);
++
++ if(!extr || !extr->ips) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_delete_parse: "
++ "extr or extr->ips pointer NULL, fatal\n");
++ SENDERR(EINVAL);
++ }
++
++ sa_len = satot(&extr->ips->ips_said, 0, sa, sizeof(sa));
++
++ spin_lock_bh(&tdb_lock);
++
++ ipsp = ipsec_sa_getbyid(&(extr->ips->ips_said));
++ if (ipsp == NULL) {
++ spin_unlock_bh(&tdb_lock);
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_delete_parse: "
++ "ipsec_sa not found for SA:%s, could not delete.\n",
++ sa_len ? sa : " (error)");
++ SENDERR(ESRCH);
++ }
++
++ ipsec_sa_put(ipsp);
++ if((error = ipsec_sa_delchain(ipsp))) {
++ spin_unlock_bh(&tdb_lock);
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_delete_parse: "
++ "error=%d returned trying to delete ipsec_sa for SA:%s.\n",
++ error,
++ sa_len ? sa : " (error)");
++ SENDERR(-error);
++ }
++ spin_unlock_bh(&tdb_lock);
++
++ if(!(pfkey_safe_build(error = pfkey_msg_hdr_build(&extensions_reply[0],
++ SADB_DELETE,
++ satype,
++ 0,
++ ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_seq,
++ ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_pid),
++ extensions_reply)
++ && pfkey_safe_build(error = pfkey_sa_ref_build(&extensions_reply[SADB_EXT_SA],
++ SADB_EXT_SA,
++ extr->ips->ips_said.spi,
++ 0,
++ 0,
++ 0,
++ 0,
++ 0,
++ extr->ips->ips_ref),
++ extensions_reply)
++ && pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_EXT_ADDRESS_SRC],
++ SADB_EXT_ADDRESS_SRC,
++ 0, /*extr->ips->ips_said.proto,*/
++ 0,
++ extr->ips->ips_addr_s),
++ extensions_reply)
++ && pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_EXT_ADDRESS_DST],
++ SADB_EXT_ADDRESS_DST,
++ 0, /*extr->ips->ips_said.proto,*/
++ 0,
++ extr->ips->ips_addr_d),
++ extensions_reply)
++ )) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_delete_parse: "
++ "failed to build the delete reply message extensions\n");
++ SENDERR(-error);
++ }
++
++ if((error = pfkey_msg_build(&pfkey_reply, extensions_reply, EXT_BITS_OUT))) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_delete_parse: "
++ "failed to build the delete reply message\n");
++ SENDERR(-error);
++ }
++ for(pfkey_socketsp = pfkey_open_sockets;
++ pfkey_socketsp;
++ pfkey_socketsp = pfkey_socketsp->next) {
++ if((error = pfkey_upmsg(pfkey_socketsp->socketp, pfkey_reply))) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_delete_parse: "
++ "sending up delete reply message for satype=%d(%s) to socket=0p%p failed with error=%d.\n",
++ satype,
++ satype2name(satype),
++ pfkey_socketsp->socketp,
++ error);
++ SENDERR(-error);
++ }
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_delete_parse: "
++ "sending up delete reply message for satype=%d(%s) to socket=0p%p succeeded.\n",
++ satype,
++ satype2name(satype),
++ pfkey_socketsp->socketp);
++ }
++
++ errlab:
++ if (pfkey_reply) {
++ pfkey_msg_free(&pfkey_reply);
++ }
++ pfkey_extensions_free(extensions_reply);
++ return error;
++}
++
++DEBUG_NO_STATIC int
++pfkey_get_parse(struct sock *sk, struct sadb_ext **extensions, struct pfkey_extracted_data* extr)
++{
++ int error = 0;
++ struct ipsec_sa *ipsp;
++ char sa[SATOT_BUF];
++ size_t sa_len;
++ struct sadb_ext *extensions_reply[SADB_EXT_MAX+1];
++ struct sadb_msg *pfkey_reply = NULL;
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_get_parse: .\n");
++
++ pfkey_extensions_init(extensions_reply);
++
++ if(!extr || !extr->ips) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_get_parse: "
++ "extr or extr->ips pointer NULL, fatal\n");
++ SENDERR(EINVAL);
++ }
++
++ sa_len = satot(&extr->ips->ips_said, 0, sa, sizeof(sa));
++
++ spin_lock_bh(&tdb_lock);
++
++ ipsp = ipsec_sa_getbyid(&(extr->ips->ips_said));
++ if (ipsp == NULL) {
++ spin_unlock_bh(&tdb_lock);
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_get_parse: "
++ "ipsec_sa not found for SA=%s, could not get.\n",
++ sa_len ? sa : " (error)");
++ SENDERR(ESRCH);
++ }
++
++ if(!(pfkey_safe_build(error = pfkey_msg_hdr_build(&extensions_reply[0],
++ SADB_GET,
++ ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_satype,
++ 0,
++ ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_seq,
++ ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_pid),
++ extensions_reply)
++ && pfkey_safe_build(error = pfkey_sa_ref_build(&extensions_reply[SADB_EXT_SA],
++ SADB_EXT_SA,
++ extr->ips->ips_said.spi,
++ extr->ips->ips_replaywin,
++ extr->ips->ips_state,
++ extr->ips->ips_authalg,
++ extr->ips->ips_encalg,
++ extr->ips->ips_flags,
++ extr->ips->ips_ref),
++ extensions_reply)
++ /* The 3 lifetime extentions should only be sent if non-zero. */
++ && (ipsp->ips_life.ipl_allocations.ipl_count
++ || ipsp->ips_life.ipl_bytes.ipl_count
++ || ipsp->ips_life.ipl_addtime.ipl_count
++ || ipsp->ips_life.ipl_usetime.ipl_count
++ || ipsp->ips_life.ipl_packets.ipl_count
++ ? pfkey_safe_build(error = pfkey_lifetime_build(&extensions_reply[SADB_EXT_LIFETIME_CURRENT],
++ SADB_EXT_LIFETIME_CURRENT,
++ ipsp->ips_life.ipl_allocations.ipl_count,
++ ipsp->ips_life.ipl_bytes.ipl_count,
++ ipsp->ips_life.ipl_addtime.ipl_count,
++ ipsp->ips_life.ipl_usetime.ipl_count,
++ ipsp->ips_life.ipl_packets.ipl_count),
++ extensions_reply) : 1)
++ && (ipsp->ips_life.ipl_allocations.ipl_hard
++ || ipsp->ips_life.ipl_bytes.ipl_hard
++ || ipsp->ips_life.ipl_addtime.ipl_hard
++ || ipsp->ips_life.ipl_usetime.ipl_hard
++ || ipsp->ips_life.ipl_packets.ipl_hard
++ ? pfkey_safe_build(error = pfkey_lifetime_build(&extensions_reply[SADB_EXT_LIFETIME_HARD],
++ SADB_EXT_LIFETIME_HARD,
++ ipsp->ips_life.ipl_allocations.ipl_hard,
++ ipsp->ips_life.ipl_bytes.ipl_hard,
++ ipsp->ips_life.ipl_addtime.ipl_hard,
++ ipsp->ips_life.ipl_usetime.ipl_hard,
++ ipsp->ips_life.ipl_packets.ipl_hard),
++ extensions_reply) : 1)
++ && (ipsp->ips_life.ipl_allocations.ipl_soft
++ || ipsp->ips_life.ipl_bytes.ipl_soft
++ || ipsp->ips_life.ipl_addtime.ipl_soft
++ || ipsp->ips_life.ipl_usetime.ipl_soft
++ || ipsp->ips_life.ipl_packets.ipl_soft
++ ? pfkey_safe_build(error = pfkey_lifetime_build(&extensions_reply[SADB_EXT_LIFETIME_SOFT],
++ SADB_EXT_LIFETIME_SOFT,
++ ipsp->ips_life.ipl_allocations.ipl_soft,
++ ipsp->ips_life.ipl_bytes.ipl_soft,
++ ipsp->ips_life.ipl_addtime.ipl_soft,
++ ipsp->ips_life.ipl_usetime.ipl_soft,
++ ipsp->ips_life.ipl_packets.ipl_soft),
++ extensions_reply) : 1)
++ && pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_EXT_ADDRESS_SRC],
++ SADB_EXT_ADDRESS_SRC,
++ 0, /*extr->ips->ips_said.proto,*/
++ 0,
++ extr->ips->ips_addr_s),
++ extensions_reply)
++ && pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_EXT_ADDRESS_DST],
++ SADB_EXT_ADDRESS_DST,
++ 0, /*extr->ips->ips_said.proto,*/
++ 0,
++ extr->ips->ips_addr_d),
++ extensions_reply)
++ && (extr->ips->ips_addr_p
++ ? pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_EXT_ADDRESS_PROXY],
++ SADB_EXT_ADDRESS_PROXY,
++ 0, /*extr->ips->ips_said.proto,*/
++ 0,
++ extr->ips->ips_addr_p),
++ extensions_reply) : 1)
++#if 0
++ /* FIXME: This won't work yet because the keys are not
++ stored directly in the ipsec_sa. They are stored as
++ contexts. */
++ && (extr->ips->ips_key_a_size
++ ? pfkey_safe_build(error = pfkey_key_build(&extensions_reply[SADB_EXT_KEY_AUTH],
++ SADB_EXT_KEY_AUTH,
++ extr->ips->ips_key_a_size * 8,
++ extr->ips->ips_key_a),
++ extensions_reply) : 1)
++ /* FIXME: This won't work yet because the keys are not
++ stored directly in the ipsec_sa. They are stored as
++ key schedules. */
++ && (extr->ips->ips_key_e_size
++ ? pfkey_safe_build(error = pfkey_key_build(&extensions_reply[SADB_EXT_KEY_ENCRYPT],
++ SADB_EXT_KEY_ENCRYPT,
++ extr->ips->ips_key_e_size * 8,
++ extr->ips->ips_key_e),
++ extensions_reply) : 1)
++#endif
++ && (extr->ips->ips_ident_s.data
++ ? pfkey_safe_build(error = pfkey_ident_build(&extensions_reply[SADB_EXT_IDENTITY_SRC],
++ SADB_EXT_IDENTITY_SRC,
++ extr->ips->ips_ident_s.type,
++ extr->ips->ips_ident_s.id,
++ extr->ips->ips_ident_s.len,
++ extr->ips->ips_ident_s.data),
++ extensions_reply) : 1)
++ && (extr->ips->ips_ident_d.data
++ ? pfkey_safe_build(error = pfkey_ident_build(&extensions_reply[SADB_EXT_IDENTITY_DST],
++ SADB_EXT_IDENTITY_DST,
++ extr->ips->ips_ident_d.type,
++ extr->ips->ips_ident_d.id,
++ extr->ips->ips_ident_d.len,
++ extr->ips->ips_ident_d.data),
++ extensions_reply) : 1)
++#if 0
++ /* FIXME: This won't work yet because I have not finished
++ it. */
++ && (extr->ips->ips_sens_
++ ? pfkey_safe_build(error = pfkey_sens_build(&extensions_reply[SADB_EXT_SENSITIVITY],
++ extr->ips->ips_sens_dpd,
++ extr->ips->ips_sens_sens_level,
++ extr->ips->ips_sens_sens_len,
++ extr->ips->ips_sens_sens_bitmap,
++ extr->ips->ips_sens_integ_level,
++ extr->ips->ips_sens_integ_len,
++ extr->ips->ips_sens_integ_bitmap),
++ extensions_reply) : 1)
++#endif
++ )) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_get_parse: "
++ "failed to build the get reply message extensions\n");
++ ipsec_sa_put(ipsp);
++ spin_unlock_bh(&tdb_lock);
++ SENDERR(-error);
++ }
++
++ ipsec_sa_put(ipsp);
++ spin_unlock_bh(&tdb_lock);
++
++ if((error = pfkey_msg_build(&pfkey_reply, extensions_reply, EXT_BITS_OUT))) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_get_parse: "
++ "failed to build the get reply message\n");
++ SENDERR(-error);
++ }
++
++ if((error = pfkey_upmsg(sk->socket, pfkey_reply))) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_get_parse: "
++ "failed to send the get reply message\n");
++ SENDERR(-error);
++ }
++
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_get_parse: "
++ "succeeded in sending get reply message.\n");
++
++ errlab:
++ if (pfkey_reply) {
++ pfkey_msg_free(&pfkey_reply);
++ }
++ pfkey_extensions_free(extensions_reply);
++ return error;
++}
++
++DEBUG_NO_STATIC int
++pfkey_acquire_parse(struct sock *sk, struct sadb_ext **extensions, struct pfkey_extracted_data* extr)
++{
++ int error = 0;
++ struct socket_list *pfkey_socketsp;
++ uint8_t satype = ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_satype;
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_acquire_parse: .\n");
++
++ /* XXX I don't know if we want an upper bound, since userspace may
++ want to register itself for an satype > SADB_SATYPE_MAX. */
++ if((satype == 0) || (satype > SADB_SATYPE_MAX)) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_acquire_parse: "
++ "SATYPE=%d invalid.\n",
++ satype);
++ SENDERR(EINVAL);
++ }
++
++ if(!(pfkey_registered_sockets[satype])) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_acquire_parse: "
++ "no sockets registered for SAtype=%d(%s).\n",
++ satype,
++ satype2name(satype));
++ SENDERR(EPROTONOSUPPORT);
++ }
++
++ for(pfkey_socketsp = pfkey_registered_sockets[satype];
++ pfkey_socketsp;
++ pfkey_socketsp = pfkey_socketsp->next) {
++ if((error = pfkey_upmsg(pfkey_socketsp->socketp,
++ ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])))) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_acquire_parse: "
++ "sending up acquire reply message for satype=%d(%s) to socket=0p%p failed with error=%d.\n",
++ satype,
++ satype2name(satype),
++ pfkey_socketsp->socketp,
++ error);
++ SENDERR(-error);
++ }
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_acquire_parse: "
++ "sending up acquire reply message for satype=%d(%s) to socket=0p%p succeeded.\n",
++ satype,
++ satype2name(satype),
++ pfkey_socketsp->socketp);
++ }
++
++ errlab:
++ return error;
++}
++
++DEBUG_NO_STATIC int
++pfkey_register_parse(struct sock *sk, struct sadb_ext **extensions, struct pfkey_extracted_data* extr)
++{
++ int error = 0;
++ uint8_t satype = ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_satype;
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_register_parse: .\n");
++
++ /* XXX I don't know if we want an upper bound, since userspace may
++ want to register itself for an satype > SADB_SATYPE_MAX. */
++ if((satype == 0) || (satype > SADB_SATYPE_MAX)) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_register_parse: "
++ "SATYPE=%d invalid.\n",
++ satype);
++ SENDERR(EINVAL);
++ }
++
++ if(!pfkey_list_insert_socket(sk->socket,
++ &(pfkey_registered_sockets[satype]))) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_register_parse: "
++ "SATYPE=%02d(%s) successfully registered by KMd (pid=%d).\n",
++ satype,
++ satype2name(satype),
++ key_pid(sk));
++ };
++
++ /* send up register msg with supported SATYPE algos */
++
++ error=pfkey_register_reply(satype, (struct sadb_msg*)extensions[SADB_EXT_RESERVED]);
++ errlab:
++ return error;
++}
++
++int
++pfkey_register_reply(int satype, struct sadb_msg *sadb_msg)
++{
++ struct sadb_ext *extensions_reply[SADB_EXT_MAX+1];
++ struct sadb_msg *pfkey_reply = NULL;
++ struct socket_list *pfkey_socketsp;
++ struct supported_list *pfkey_supported_listp;
++ unsigned int alg_num_a = 0, alg_num_e = 0;
++ struct sadb_alg *alg_a = NULL, *alg_e = NULL, *alg_ap = NULL, *alg_ep = NULL;
++ int error = 0;
++
++ pfkey_extensions_init(extensions_reply);
++
++ if((satype == 0) || (satype > SADB_SATYPE_MAX)) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_register_reply: "
++ "SAtype=%d unspecified or unknown.\n",
++ satype);
++ SENDERR(EINVAL);
++ }
++ if(!(pfkey_registered_sockets[satype])) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_register_reply: "
++ "no sockets registered for SAtype=%d(%s).\n",
++ satype,
++ satype2name(satype));
++ SENDERR(EPROTONOSUPPORT);
++ }
++ /* send up register msg with supported SATYPE algos */
++ pfkey_supported_listp = pfkey_supported_list[satype];
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_register_reply: "
++ "pfkey_supported_list[%d]=0p%p\n",
++ satype,
++ pfkey_supported_list[satype]);
++ while(pfkey_supported_listp) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_register_reply: "
++ "checking supported=0p%p\n",
++ pfkey_supported_listp);
++ if(pfkey_supported_listp->supportedp->supported_alg_exttype == SADB_EXT_SUPPORTED_AUTH) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_register_reply: "
++ "adding auth alg.\n");
++ alg_num_a++;
++ }
++ if(pfkey_supported_listp->supportedp->supported_alg_exttype == SADB_EXT_SUPPORTED_ENCRYPT) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_register_reply: "
++ "adding encrypt alg.\n");
++ alg_num_e++;
++ }
++ pfkey_supported_listp = pfkey_supported_listp->next;
++ }
++
++ if(alg_num_a) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_register_reply: "
++ "allocating %lu bytes for auth algs.\n",
++ (unsigned long) (alg_num_a * sizeof(struct sadb_alg)));
++ if((alg_a = kmalloc(alg_num_a * sizeof(struct sadb_alg), GFP_ATOMIC) ) == NULL) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_register_reply: "
++ "auth alg memory allocation error\n");
++ SENDERR(ENOMEM);
++ }
++ alg_ap = alg_a;
++ }
++
++ if(alg_num_e) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_register_reply: "
++ "allocating %lu bytes for enc algs.\n",
++ (unsigned long) (alg_num_e * sizeof(struct sadb_alg)));
++ if((alg_e = kmalloc(alg_num_e * sizeof(struct sadb_alg), GFP_ATOMIC) ) == NULL) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_register_reply: "
++ "enc alg memory allocation error\n");
++ SENDERR(ENOMEM);
++ }
++ alg_ep = alg_e;
++ }
++
++ pfkey_supported_listp = pfkey_supported_list[satype];
++ while(pfkey_supported_listp) {
++ if(alg_num_a) {
++ if(pfkey_supported_listp->supportedp->supported_alg_exttype == SADB_EXT_SUPPORTED_AUTH) {
++ alg_ap->sadb_alg_id = pfkey_supported_listp->supportedp->supported_alg_id;
++ alg_ap->sadb_alg_ivlen = pfkey_supported_listp->supportedp->supported_alg_ivlen;
++ alg_ap->sadb_alg_minbits = pfkey_supported_listp->supportedp->supported_alg_minbits;
++ alg_ap->sadb_alg_maxbits = pfkey_supported_listp->supportedp->supported_alg_maxbits;
++ alg_ap->sadb_alg_reserved = 0;
++ KLIPS_PRINT(debug_pfkey && sysctl_ipsec_debug_verbose,
++ "klips_debug:pfkey_register_reply: "
++ "adding auth=0p%p\n",
++ alg_ap);
++ alg_ap++;
++ }
++ }
++ if(alg_num_e) {
++ if(pfkey_supported_listp->supportedp->supported_alg_exttype == SADB_EXT_SUPPORTED_ENCRYPT) {
++ alg_ep->sadb_alg_id = pfkey_supported_listp->supportedp->supported_alg_id;
++ alg_ep->sadb_alg_ivlen = pfkey_supported_listp->supportedp->supported_alg_ivlen;
++ alg_ep->sadb_alg_minbits = pfkey_supported_listp->supportedp->supported_alg_minbits;
++ alg_ep->sadb_alg_maxbits = pfkey_supported_listp->supportedp->supported_alg_maxbits;
++ alg_ep->sadb_alg_reserved = 0;
++ KLIPS_PRINT(debug_pfkey && sysctl_ipsec_debug_verbose,
++ "klips_debug:pfkey_register_reply: "
++ "adding encrypt=0p%p\n",
++ alg_ep);
++ alg_ep++;
++ }
++ }
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_register_reply: "
++ "found satype=%d(%s) exttype=%d id=%d ivlen=%d minbits=%d maxbits=%d.\n",
++ satype,
++ satype2name(satype),
++ pfkey_supported_listp->supportedp->supported_alg_exttype,
++ pfkey_supported_listp->supportedp->supported_alg_id,
++ pfkey_supported_listp->supportedp->supported_alg_ivlen,
++ pfkey_supported_listp->supportedp->supported_alg_minbits,
++ pfkey_supported_listp->supportedp->supported_alg_maxbits);
++ pfkey_supported_listp = pfkey_supported_listp->next;
++ }
++ if(!(pfkey_safe_build(error = pfkey_msg_hdr_build(&extensions_reply[0],
++ SADB_REGISTER,
++ satype,
++ 0,
++ sadb_msg? sadb_msg->sadb_msg_seq : ++pfkey_msg_seq,
++ sadb_msg? sadb_msg->sadb_msg_pid: current->pid),
++ extensions_reply) &&
++ (alg_num_a ? pfkey_safe_build(error = pfkey_supported_build(&extensions_reply[SADB_EXT_SUPPORTED_AUTH],
++ SADB_EXT_SUPPORTED_AUTH,
++ alg_num_a,
++ alg_a),
++ extensions_reply) : 1) &&
++ (alg_num_e ? pfkey_safe_build(error = pfkey_supported_build(&extensions_reply[SADB_EXT_SUPPORTED_ENCRYPT],
++ SADB_EXT_SUPPORTED_ENCRYPT,
++ alg_num_e,
++ alg_e),
++ extensions_reply) : 1))) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_register_reply: "
++ "failed to build the register message extensions_reply\n");
++ SENDERR(-error);
++ }
++
++ if((error = pfkey_msg_build(&pfkey_reply, extensions_reply, EXT_BITS_OUT))) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_register_reply: "
++ "failed to build the register message\n");
++ SENDERR(-error);
++ }
++ /* this should go to all registered sockets for that satype only */
++ for(pfkey_socketsp = pfkey_registered_sockets[satype];
++ pfkey_socketsp;
++ pfkey_socketsp = pfkey_socketsp->next) {
++ if((error = pfkey_upmsg(pfkey_socketsp->socketp, pfkey_reply))) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_register_reply: "
++ "sending up acquire message for satype=%d(%s) to socket=0p%p failed with error=%d.\n",
++ satype,
++ satype2name(satype),
++ pfkey_socketsp->socketp,
++ error);
++ SENDERR(-error);
++ }
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_register_reply: "
++ "sending up register message for satype=%d(%s) to socket=0p%p succeeded.\n",
++ satype,
++ satype2name(satype),
++ pfkey_socketsp->socketp);
++ }
++
++ errlab:
++ if(alg_a) {
++ kfree(alg_a);
++ }
++ if(alg_e) {
++ kfree(alg_e);
++ }
++ if (pfkey_reply) {
++ pfkey_msg_free(&pfkey_reply);
++ }
++ pfkey_extensions_free(extensions_reply);
++ return error;
++}
++
++DEBUG_NO_STATIC int
++pfkey_expire_parse(struct sock *sk, struct sadb_ext **extensions, struct pfkey_extracted_data* extr)
++{
++ int error = 0;
++ struct socket_list *pfkey_socketsp;
++#ifdef CONFIG_IPSEC_DEBUG
++ uint8_t satype = ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_satype;
++#endif /* CONFIG_IPSEC_DEBUG */
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_expire_parse: .\n");
++
++ if(pfkey_open_sockets) {
++ for(pfkey_socketsp = pfkey_open_sockets;
++ pfkey_socketsp;
++ pfkey_socketsp = pfkey_socketsp->next) {
++ if((error = pfkey_upmsg(pfkey_socketsp->socketp,
++ ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])))) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_expire_parse: "
++ "sending up expire reply message for satype=%d(%s) to socket=0p%p failed with error=%d.\n",
++ satype,
++ satype2name(satype),
++ pfkey_socketsp->socketp,
++ error);
++ SENDERR(-error);
++ }
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_expire_parse: "
++ "sending up expire reply message for satype=%d(%s) to socket=0p%p succeeded.\n",
++ satype,
++ satype2name(satype),
++ pfkey_socketsp->socketp);
++ }
++ }
++
++ errlab:
++ return error;
++}
++
++DEBUG_NO_STATIC int
++pfkey_flush_parse(struct sock *sk, struct sadb_ext **extensions, struct pfkey_extracted_data* extr)
++{
++ int error = 0;
++ struct socket_list *pfkey_socketsp;
++ uint8_t satype = ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_satype;
++ uint8_t proto = 0;
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_flush_parse: "
++ "flushing type %d SAs\n",
++ satype);
++
++ if(satype && !(proto = satype2proto(satype))) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_flush_parse: "
++ "satype %d lookup failed.\n",
++ ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_satype);
++ SENDERR(EINVAL);
++ }
++
++ if ((error = ipsec_sadb_cleanup(proto))) {
++ SENDERR(-error);
++ }
++
++ if(pfkey_open_sockets) {
++ for(pfkey_socketsp = pfkey_open_sockets;
++ pfkey_socketsp;
++ pfkey_socketsp = pfkey_socketsp->next) {
++ if((error = pfkey_upmsg(pfkey_socketsp->socketp,
++ ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])))) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_flush_parse: "
++ "sending up flush reply message for satype=%d(%s) (proto=%d) to socket=0p%p failed with error=%d.\n",
++ satype,
++ satype2name(satype),
++ proto,
++ pfkey_socketsp->socketp,
++ error);
++ SENDERR(-error);
++ }
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_flush_parse: "
++ "sending up flush reply message for satype=%d(%s) to socket=0p%p succeeded.\n",
++ satype,
++ satype2name(satype),
++ pfkey_socketsp->socketp);
++ }
++ }
++
++ errlab:
++ return error;
++}
++
++DEBUG_NO_STATIC int
++pfkey_dump_parse(struct sock *sk, struct sadb_ext **extensions, struct pfkey_extracted_data* extr)
++{
++ int error = 0;
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_dump_parse: .\n");
++
++ SENDERR(ENOSYS);
++ errlab:
++ return error;
++}
++
++DEBUG_NO_STATIC int
++pfkey_x_promisc_parse(struct sock *sk, struct sadb_ext **extensions, struct pfkey_extracted_data* extr)
++{
++ int error = 0;
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_promisc_parse: .\n");
++
++ SENDERR(ENOSYS);
++ errlab:
++ return error;
++}
++
++DEBUG_NO_STATIC int
++pfkey_x_pchange_parse(struct sock *sk, struct sadb_ext **extensions, struct pfkey_extracted_data* extr)
++{
++ int error = 0;
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_pchange_parse: .\n");
++
++ SENDERR(ENOSYS);
++ errlab:
++ return error;
++}
++
++DEBUG_NO_STATIC int
++pfkey_x_grpsa_parse(struct sock *sk, struct sadb_ext **extensions, struct pfkey_extracted_data* extr)
++{
++ struct ipsec_sa *ips1p, *ips2p, *ipsp;
++ struct sadb_ext *extensions_reply[SADB_EXT_MAX+1];
++ struct sadb_msg *pfkey_reply = NULL;
++ struct socket_list *pfkey_socketsp;
++ uint8_t satype = ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_satype;
++ char sa1[SATOT_BUF], sa2[SATOT_BUF];
++ size_t sa_len1, sa_len2 = 0;
++ int error = 0;
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_grpsa_parse: .\n");
++
++ pfkey_extensions_init(extensions_reply);
++
++ if(extr == NULL || extr->ips == NULL) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_grpsa_parse: "
++ "extr or extr->ips is NULL, fatal.\n");
++ SENDERR(EINVAL);
++ }
++
++ sa_len1 = satot(&extr->ips->ips_said, 0, sa1, sizeof(sa1));
++ if(extr->ips2 != NULL) {
++ sa_len2 = satot(&extr->ips2->ips_said, 0, sa2, sizeof(sa2));
++ }
++
++ spin_lock_bh(&tdb_lock);
++
++ ips1p = ipsec_sa_getbyid(&(extr->ips->ips_said));
++ if(ips1p == NULL) {
++ spin_unlock_bh(&tdb_lock);
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_grpsa_parse: "
++ "reserved ipsec_sa for SA1: %s not found. Call SADB_ADD/UPDATE first.\n",
++ sa_len1 ? sa1 : " (error)");
++ SENDERR(ENOENT);
++ }
++ if(extr->ips2) { /* GRPSA */
++ ips2p = ipsec_sa_getbyid(&(extr->ips2->ips_said));
++ if(ips2p == NULL) {
++ ipsec_sa_put(ips1p);
++ spin_unlock_bh(&tdb_lock);
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_grpsa_parse: "
++ "reserved ipsec_sa for SA2: %s not found. Call SADB_ADD/UPDATE first.\n",
++ sa_len2 ? sa2 : " (error)");
++ SENDERR(ENOENT);
++ }
++
++ /* Is either one already linked? */
++ if(ips1p->ips_onext) {
++ ipsec_sa_put(ips1p);
++ ipsec_sa_put(ips2p);
++ spin_unlock_bh(&tdb_lock);
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_grpsa_parse: "
++ "ipsec_sa for SA: %s is already linked.\n",
++ sa_len1 ? sa1 : " (error)");
++ SENDERR(EEXIST);
++ }
++ if(ips2p->ips_inext) {
++ ipsec_sa_put(ips1p);
++ ipsec_sa_put(ips2p);
++ spin_unlock_bh(&tdb_lock);
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_grpsa_parse: "
++ "ipsec_sa for SA: %s is already linked.\n",
++ sa_len2 ? sa2 : " (error)");
++ SENDERR(EEXIST);
++ }
++
++ /* Is extr->ips already linked to extr->ips2? */
++ ipsp = ips2p;
++ while(ipsp) {
++ if(ipsp == ips1p) {
++ ipsec_sa_put(ips1p);
++ ipsec_sa_put(ips2p);
++ spin_unlock_bh(&tdb_lock);
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_grpsa_parse: "
++ "ipsec_sa for SA: %s is already linked to %s.\n",
++ sa_len1 ? sa1 : " (error)",
++ sa_len2 ? sa2 : " (error)");
++ SENDERR(EEXIST);
++ }
++ ipsp = ipsp->ips_onext;
++ }
++
++ /* link 'em */
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_grpsa_parse: "
++ "linking ipsec_sa SA: %s with %s.\n",
++ sa_len1 ? sa1 : " (error)",
++ sa_len2 ? sa2 : " (error)");
++ ips1p->ips_onext = ips2p;
++ ips2p->ips_inext = ips1p;
++ } else { /* UNGRPSA */
++ ipsec_sa_put(ips1p);
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_grpsa_parse: "
++ "unlinking ipsec_sa SA: %s.\n",
++ sa_len1 ? sa1 : " (error)");
++ while(ips1p->ips_onext) {
++ ips1p = ips1p->ips_onext;
++ }
++ while(ips1p->ips_inext) {
++ ipsp = ips1p;
++ ips1p = ips1p->ips_inext;
++ ipsec_sa_put(ips1p);
++ ipsp->ips_inext = NULL;
++ ipsec_sa_put(ipsp);
++ ips1p->ips_onext = NULL;
++ }
++ }
++
++ spin_unlock_bh(&tdb_lock);
++
++ if(!(pfkey_safe_build(error = pfkey_msg_hdr_build(&extensions_reply[0],
++ SADB_X_GRPSA,
++ satype,
++ 0,
++ ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_seq,
++ ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_pid),
++ extensions_reply)
++ && pfkey_safe_build(error = pfkey_sa_ref_build(&extensions_reply[SADB_EXT_SA],
++ SADB_EXT_SA,
++ extr->ips->ips_said.spi,
++ extr->ips->ips_replaywin,
++ extr->ips->ips_state,
++ extr->ips->ips_authalg,
++ extr->ips->ips_encalg,
++ extr->ips->ips_flags,
++ extr->ips->ips_ref),
++ extensions_reply)
++ && pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_EXT_ADDRESS_DST],
++ SADB_EXT_ADDRESS_DST,
++ 0, /*extr->ips->ips_said.proto,*/
++ 0,
++ extr->ips->ips_addr_d),
++ extensions_reply)
++ && (extr->ips2
++ ? (pfkey_safe_build(error = pfkey_x_satype_build(&extensions_reply[SADB_X_EXT_SATYPE2],
++ ((struct sadb_x_satype*)extensions[SADB_X_EXT_SATYPE2])->sadb_x_satype_satype
++ /* proto2satype(extr->ips2->ips_said.proto) */),
++ extensions_reply)
++ && pfkey_safe_build(error = pfkey_sa_ref_build(&extensions_reply[SADB_X_EXT_SA2],
++ SADB_X_EXT_SA2,
++ extr->ips2->ips_said.spi,
++ extr->ips2->ips_replaywin,
++ extr->ips2->ips_state,
++ extr->ips2->ips_authalg,
++ extr->ips2->ips_encalg,
++ extr->ips2->ips_flags,
++ extr->ips2->ips_ref),
++ extensions_reply)
++ && pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_X_EXT_ADDRESS_DST2],
++ SADB_X_EXT_ADDRESS_DST2,
++ 0, /*extr->ips->ips_said.proto,*/
++ 0,
++ extr->ips2->ips_addr_d),
++ extensions_reply) ) : 1 )
++ )) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_x_grpsa_parse: "
++ "failed to build the x_grpsa reply message extensions\n");
++ SENDERR(-error);
++ }
++
++ if((error = pfkey_msg_build(&pfkey_reply, extensions_reply, EXT_BITS_OUT))) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_x_grpsa_parse: "
++ "failed to build the x_grpsa reply message\n");
++ SENDERR(-error);
++ }
++
++ for(pfkey_socketsp = pfkey_open_sockets;
++ pfkey_socketsp;
++ pfkey_socketsp = pfkey_socketsp->next) {
++ if((error = pfkey_upmsg(pfkey_socketsp->socketp, pfkey_reply))) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_x_grpsa_parse: "
++ "sending up x_grpsa reply message for satype=%d(%s) to socket=0p%p failed with error=%d.\n",
++ satype,
++ satype2name(satype),
++ pfkey_socketsp->socketp,
++ error);
++ SENDERR(-error);
++ }
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_x_grpsa_parse: "
++ "sending up x_grpsa reply message for satype=%d(%s) to socket=0p%p succeeded.\n",
++ satype,
++ satype2name(satype),
++ pfkey_socketsp->socketp);
++ }
++
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_x_grpsa_parse: "
++ "succeeded in sending x_grpsa reply message.\n");
++
++ errlab:
++ if (pfkey_reply) {
++ pfkey_msg_free(&pfkey_reply);
++ }
++ pfkey_extensions_free(extensions_reply);
++ return error;
++}
++
++DEBUG_NO_STATIC int
++pfkey_x_addflow_parse(struct sock *sk, struct sadb_ext **extensions, struct pfkey_extracted_data* extr)
++{
++ int error = 0;
++#ifdef CONFIG_IPSEC_DEBUG
++ char buf1[64], buf2[64];
++#endif /* CONFIG_IPSEC_DEBUG */
++ struct sadb_ext *extensions_reply[SADB_EXT_MAX+1];
++ struct sadb_msg *pfkey_reply = NULL;
++ struct socket_list *pfkey_socketsp;
++ uint8_t satype = ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_satype;
++ ip_address srcflow, dstflow, srcmask, dstmask;
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_addflow_parse: .\n");
++
++ pfkey_extensions_init(extensions_reply);
++
++ memset((caddr_t)&srcflow, 0, sizeof(srcflow));
++ memset((caddr_t)&dstflow, 0, sizeof(dstflow));
++ memset((caddr_t)&srcmask, 0, sizeof(srcmask));
++ memset((caddr_t)&dstmask, 0, sizeof(dstmask));
++
++ if(!extr || !(extr->ips) || !(extr->eroute)) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_addflow_parse: "
++ "missing extr, ipsec_sa or eroute data.\n");
++ SENDERR(EINVAL);
++ }
++
++ srcflow.u.v4.sin_family = AF_INET;
++ dstflow.u.v4.sin_family = AF_INET;
++ srcmask.u.v4.sin_family = AF_INET;
++ dstmask.u.v4.sin_family = AF_INET;
++ srcflow.u.v4.sin_addr = extr->eroute->er_eaddr.sen_ip_src;
++ dstflow.u.v4.sin_addr = extr->eroute->er_eaddr.sen_ip_dst;
++ srcmask.u.v4.sin_addr = extr->eroute->er_emask.sen_ip_src;
++ dstmask.u.v4.sin_addr = extr->eroute->er_emask.sen_ip_dst;
++
++#ifdef CONFIG_IPSEC_DEBUG
++ if (debug_pfkey) {
++ subnettoa(extr->eroute->er_eaddr.sen_ip_src,
++ extr->eroute->er_emask.sen_ip_src, 0, buf1, sizeof(buf1));
++ subnettoa(extr->eroute->er_eaddr.sen_ip_dst,
++ extr->eroute->er_emask.sen_ip_dst, 0, buf2, sizeof(buf2));
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_addflow_parse: "
++ "calling breakeroute and/or makeroute for %s->%s\n",
++ buf1, buf2);
++ }
++#endif /* CONFIG_IPSEC_DEBUG */
++ if(extr->ips->ips_flags & SADB_X_SAFLAGS_INFLOW) {
++/* if(ip_chk_addr((unsigned long)extr->ips->ips_said.dst.u.v4.sin_addr.s_addr) == IS_MYADDR) */
++ struct ipsec_sa *ipsp, *ipsq;
++ char sa[SATOT_BUF];
++ size_t sa_len;
++
++ ipsq = ipsec_sa_getbyid(&(extr->ips->ips_said));
++ if(ipsq == NULL) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_addflow_parse: "
++ "ipsec_sa not found, cannot set incoming policy.\n");
++ SENDERR(ENOENT);
++ }
++
++ ipsp = ipsq;
++ while(ipsp && ipsp->ips_said.proto != IPPROTO_IPIP) {
++ ipsp = ipsp->ips_inext;
++ }
++
++ if(ipsp == NULL) {
++ ipsec_sa_put(ipsq);
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_addflow_parse: "
++ "SA chain does not have an IPIP SA, cannot set incoming policy.\n");
++ SENDERR(ENOENT);
++ }
++
++ sa_len = satot(&extr->ips->ips_said, 0, sa, sizeof(sa));
++
++ ipsp->ips_flags |= SADB_X_SAFLAGS_INFLOW;
++ ipsp->ips_flow_s = srcflow;
++ ipsp->ips_flow_d = dstflow;
++ ipsp->ips_mask_s = srcmask;
++ ipsp->ips_mask_d = dstmask;
++
++ ipsec_sa_put(ipsq);
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_addflow_parse: "
++ "inbound eroute, setting incoming policy information in IPIP ipsec_sa for SA: %s.\n",
++ sa_len ? sa : " (error)");
++ } else {
++ struct sk_buff *first = NULL, *last = NULL;
++
++ if(extr->ips->ips_flags & SADB_X_SAFLAGS_REPLACEFLOW) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_addflow_parse: "
++ "REPLACEFLOW flag set, calling breakeroute.\n");
++ if ((error = ipsec_breakroute(&(extr->eroute->er_eaddr),
++ &(extr->eroute->er_emask),
++ &first, &last))) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_addflow_parse: "
++ "breakeroute returned %d. first=0p%p, last=0p%p\n",
++ error,
++ first,
++ last);
++ if(first != NULL) {
++ ipsec_kfree_skb(first);
++ }
++ if(last != NULL) {
++ ipsec_kfree_skb(last);
++ }
++ SENDERR(-error);
++ }
++ }
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_addflow_parse: "
++ "calling makeroute.\n");
++
++ if ((error = ipsec_makeroute(&(extr->eroute->er_eaddr),
++ &(extr->eroute->er_emask),
++ extr->ips->ips_said,
++ ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_pid,
++ NULL,
++ &(extr->ips->ips_ident_s),
++ &(extr->ips->ips_ident_d)))) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_addflow_parse: "
++ "makeroute returned %d.\n", error);
++ SENDERR(-error);
++ }
++ if(first != NULL) {
++ KLIPS_PRINT(debug_eroute,
++ "klips_debug:pfkey_x_addflow_parse: "
++ "first=0p%p HOLD packet re-injected.\n",
++ first);
++ DEV_QUEUE_XMIT(first, first->dev, SOPRI_NORMAL);
++ }
++ if(last != NULL) {
++ KLIPS_PRINT(debug_eroute,
++ "klips_debug:pfkey_x_addflow_parse: "
++ "last=0p%p HOLD packet re-injected.\n",
++ last);
++ DEV_QUEUE_XMIT(last, last->dev, SOPRI_NORMAL);
++ }
++ }
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_addflow_parse: "
++ "makeroute call successful.\n");
++
++ if(!(pfkey_safe_build(error = pfkey_msg_hdr_build(&extensions_reply[0],
++ SADB_X_ADDFLOW,
++ satype,
++ 0,
++ ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_seq,
++ ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_pid),
++ extensions_reply)
++ && pfkey_safe_build(error = pfkey_sa_ref_build(&extensions_reply[SADB_EXT_SA],
++ SADB_EXT_SA,
++ extr->ips->ips_said.spi,
++ extr->ips->ips_replaywin,
++ extr->ips->ips_state,
++ extr->ips->ips_authalg,
++ extr->ips->ips_encalg,
++ extr->ips->ips_flags,
++ extr->ips->ips_ref),
++ extensions_reply)
++ && (extensions[SADB_EXT_ADDRESS_SRC]
++ ? pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_EXT_ADDRESS_SRC],
++ SADB_EXT_ADDRESS_SRC,
++ 0, /*extr->ips->ips_said.proto,*/
++ 0,
++ extr->ips->ips_addr_s),
++ extensions_reply) : 1)
++ && (extensions[SADB_EXT_ADDRESS_DST]
++ ? pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_EXT_ADDRESS_DST],
++ SADB_EXT_ADDRESS_DST,
++ 0, /*extr->ips->ips_said.proto,*/
++ 0,
++ extr->ips->ips_addr_d),
++ extensions_reply) : 1)
++ && pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_X_EXT_ADDRESS_SRC_FLOW],
++ SADB_X_EXT_ADDRESS_SRC_FLOW,
++ 0, /*extr->ips->ips_said.proto,*/
++ 0,
++ (struct sockaddr*)&srcflow),
++ extensions_reply)
++ && pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_X_EXT_ADDRESS_DST_FLOW],
++ SADB_X_EXT_ADDRESS_DST_FLOW,
++ 0, /*extr->ips->ips_said.proto,*/
++ 0,
++ (struct sockaddr*)&dstflow),
++ extensions_reply)
++ && pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_X_EXT_ADDRESS_SRC_MASK],
++ SADB_X_EXT_ADDRESS_SRC_MASK,
++ 0, /*extr->ips->ips_said.proto,*/
++ 0,
++ (struct sockaddr*)&srcmask),
++ extensions_reply)
++ && pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_X_EXT_ADDRESS_DST_MASK],
++ SADB_X_EXT_ADDRESS_DST_MASK,
++ 0, /*extr->ips->ips_said.proto,*/
++ 0,
++ (struct sockaddr*)&dstmask),
++ extensions_reply)
++ )) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_x_addflow_parse: "
++ "failed to build the x_addflow reply message extensions\n");
++ SENDERR(-error);
++ }
++
++ if((error = pfkey_msg_build(&pfkey_reply, extensions_reply, EXT_BITS_OUT))) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_x_addflow_parse: "
++ "failed to build the x_addflow reply message\n");
++ SENDERR(-error);
++ }
++
++ for(pfkey_socketsp = pfkey_open_sockets;
++ pfkey_socketsp;
++ pfkey_socketsp = pfkey_socketsp->next) {
++ if((error = pfkey_upmsg(pfkey_socketsp->socketp, pfkey_reply))) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_x_addflow_parse: "
++ "sending up x_addflow reply message for satype=%d(%s) to socket=0p%p failed with error=%d.\n",
++ satype,
++ satype2name(satype),
++ pfkey_socketsp->socketp,
++ error);
++ SENDERR(-error);
++ }
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_x_addflow_parse: "
++ "sending up x_addflow reply message for satype=%d(%s) (proto=%d) to socket=0p%p succeeded.\n",
++ satype,
++ satype2name(satype),
++ extr->ips->ips_said.proto,
++ pfkey_socketsp->socketp);
++ }
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_addflow_parse: "
++ "extr->ips cleaned up and freed.\n");
++
++ errlab:
++ if (pfkey_reply) {
++ pfkey_msg_free(&pfkey_reply);
++ }
++ pfkey_extensions_free(extensions_reply);
++ return error;
++}
++
++DEBUG_NO_STATIC int
++pfkey_x_delflow_parse(struct sock *sk, struct sadb_ext **extensions, struct pfkey_extracted_data* extr)
++{
++ int error = 0;
++#ifdef CONFIG_IPSEC_DEBUG
++ char buf1[64], buf2[64];
++#endif /* CONFIG_IPSEC_DEBUG */
++ struct sadb_ext *extensions_reply[SADB_EXT_MAX+1];
++ struct sadb_msg *pfkey_reply = NULL;
++ struct socket_list *pfkey_socketsp;
++ uint8_t satype = ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_satype;
++ ip_address srcflow, dstflow, srcmask, dstmask;
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_delflow_parse: .\n");
++
++ pfkey_extensions_init(extensions_reply);
++
++ memset((caddr_t)&srcflow, 0, sizeof(srcflow));
++ memset((caddr_t)&dstflow, 0, sizeof(dstflow));
++ memset((caddr_t)&srcmask, 0, sizeof(srcmask));
++ memset((caddr_t)&dstmask, 0, sizeof(dstmask));
++
++ if(!extr || !(extr->ips)) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_delflow_parse: "
++ "extr, or extr->ips is NULL, fatal\n");
++ SENDERR(EINVAL);
++ }
++
++ if(extr->ips->ips_flags & SADB_X_SAFLAGS_CLEARFLOW) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_delflow_parse: "
++ "CLEARFLOW flag set, calling cleareroutes.\n");
++ if ((error = ipsec_cleareroutes()))
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_delflow_parse: "
++ "cleareroutes returned %d.\n", error);
++ SENDERR(-error);
++ } else {
++ struct sk_buff *first = NULL, *last = NULL;
++
++ if(!(extr->eroute)) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_delflow_parse: "
++ "extr->eroute is NULL, fatal.\n");
++ SENDERR(EINVAL);
++ }
++
++ srcflow.u.v4.sin_family = AF_INET;
++ dstflow.u.v4.sin_family = AF_INET;
++ srcmask.u.v4.sin_family = AF_INET;
++ dstmask.u.v4.sin_family = AF_INET;
++ srcflow.u.v4.sin_addr = extr->eroute->er_eaddr.sen_ip_src;
++ dstflow.u.v4.sin_addr = extr->eroute->er_eaddr.sen_ip_dst;
++ srcmask.u.v4.sin_addr = extr->eroute->er_emask.sen_ip_src;
++ dstmask.u.v4.sin_addr = extr->eroute->er_emask.sen_ip_dst;
++
++#ifdef CONFIG_IPSEC_DEBUG
++ if (debug_pfkey) {
++ subnettoa(extr->eroute->er_eaddr.sen_ip_src,
++ extr->eroute->er_emask.sen_ip_src, 0, buf1, sizeof(buf1));
++ subnettoa(extr->eroute->er_eaddr.sen_ip_dst,
++ extr->eroute->er_emask.sen_ip_dst, 0, buf2, sizeof(buf2));
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_delflow_parse: "
++ "calling breakeroute for %s->%s\n",
++ buf1, buf2);
++ }
++#endif /* CONFIG_IPSEC_DEBUG */
++ error = ipsec_breakroute(&(extr->eroute->er_eaddr),
++ &(extr->eroute->er_emask),
++ &first, &last);
++ if(error) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_delflow_parse: "
++ "breakeroute returned %d. first=0p%p, last=0p%p\n",
++ error,
++ first,
++ last);
++ }
++ if(first != NULL) {
++ ipsec_kfree_skb(first);
++ }
++ if(last != NULL) {
++ ipsec_kfree_skb(last);
++ }
++ if(error) {
++ SENDERR(-error);
++ }
++ }
++
++ if(!(pfkey_safe_build(error = pfkey_msg_hdr_build(&extensions_reply[0],
++ SADB_X_DELFLOW,
++ satype,
++ 0,
++ ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_seq,
++ ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_pid),
++ extensions_reply)
++ && pfkey_safe_build(error = pfkey_sa_ref_build(&extensions_reply[SADB_EXT_SA],
++ SADB_EXT_SA,
++ extr->ips->ips_said.spi,
++ extr->ips->ips_replaywin,
++ extr->ips->ips_state,
++ extr->ips->ips_authalg,
++ extr->ips->ips_encalg,
++ extr->ips->ips_flags,
++ extr->ips->ips_ref),
++ extensions_reply)
++ && pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_X_EXT_ADDRESS_SRC_FLOW],
++ SADB_X_EXT_ADDRESS_SRC_FLOW,
++ 0, /*extr->ips->ips_said.proto,*/
++ 0,
++ (struct sockaddr*)&srcflow),
++ extensions_reply)
++ && pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_X_EXT_ADDRESS_DST_FLOW],
++ SADB_X_EXT_ADDRESS_DST_FLOW,
++ 0, /*extr->ips->ips_said.proto,*/
++ 0,
++ (struct sockaddr*)&dstflow),
++ extensions_reply)
++ && pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_X_EXT_ADDRESS_SRC_MASK],
++ SADB_X_EXT_ADDRESS_SRC_MASK,
++ 0, /*extr->ips->ips_said.proto,*/
++ 0,
++ (struct sockaddr*)&srcmask),
++ extensions_reply)
++ && pfkey_safe_build(error = pfkey_address_build(&extensions_reply[SADB_X_EXT_ADDRESS_DST_MASK],
++ SADB_X_EXT_ADDRESS_DST_MASK,
++ 0, /*extr->ips->ips_said.proto,*/
++ 0,
++ (struct sockaddr*)&dstmask),
++ extensions_reply)
++ )) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_x_delflow_parse: "
++ "failed to build the x_delflow reply message extensions\n");
++ SENDERR(-error);
++ }
++
++ if((error = pfkey_msg_build(&pfkey_reply, extensions_reply, EXT_BITS_OUT))) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_x_delflow_parse: "
++ "failed to build the x_delflow reply message\n");
++ SENDERR(-error);
++ }
++
++ for(pfkey_socketsp = pfkey_open_sockets;
++ pfkey_socketsp;
++ pfkey_socketsp = pfkey_socketsp->next) {
++ if((error = pfkey_upmsg(pfkey_socketsp->socketp, pfkey_reply))) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_x_delflow_parse: "
++ "sending up x_delflow reply message for satype=%d(%s) to socket=0p%p failed with error=%d.\n",
++ satype,
++ satype2name(satype),
++ pfkey_socketsp->socketp,
++ error);
++ SENDERR(-error);
++ }
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_x_delflow_parse: "
++ "sending up x_delflow reply message for satype=%d(%s) to socket=0p%p succeeded.\n",
++ satype,
++ satype2name(satype),
++ pfkey_socketsp->socketp);
++ }
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_delflow_parse: "
++ "extr->ips cleaned up and freed.\n");
++
++ errlab:
++ if (pfkey_reply) {
++ pfkey_msg_free(&pfkey_reply);
++ }
++ pfkey_extensions_free(extensions_reply);
++ return error;
++}
++
++DEBUG_NO_STATIC int
++pfkey_x_msg_debug_parse(struct sock *sk, struct sadb_ext **extensions, struct pfkey_extracted_data* extr)
++{
++ int error = 0;
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_x_msg_debug_parse: .\n");
++
++/* errlab:*/
++ return error;
++}
++
++/* pfkey_expire expects the ipsec_sa table to be locked before being called. */
++int
++pfkey_expire(struct ipsec_sa *ipsp, int hard)
++{
++ struct sadb_ext *extensions[SADB_EXT_MAX+1];
++ struct sadb_msg *pfkey_msg = NULL;
++ struct socket_list *pfkey_socketsp;
++ int error = 0;
++ uint8_t satype;
++
++ pfkey_extensions_init(extensions);
++
++ if(!(satype = proto2satype(ipsp->ips_said.proto))) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_expire: "
++ "satype lookup for protocol %d lookup failed.\n",
++ ipsp->ips_said.proto);
++ SENDERR(EINVAL);
++ }
++
++ if(!pfkey_open_sockets) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_expire: "
++ "no sockets listening.\n");
++ SENDERR(EPROTONOSUPPORT);
++ }
++
++ if (!(pfkey_safe_build(error = pfkey_msg_hdr_build(&extensions[0],
++ SADB_EXPIRE,
++ satype,
++ 0,
++ ++pfkey_msg_seq,
++ 0),
++ extensions)
++ && pfkey_safe_build(error = pfkey_sa_ref_build(&extensions[SADB_EXT_SA],
++ SADB_EXT_SA,
++ ipsp->ips_said.spi,
++ ipsp->ips_replaywin,
++ ipsp->ips_state,
++ ipsp->ips_authalg,
++ ipsp->ips_encalg,
++ ipsp->ips_flags,
++ ipsp->ips_ref),
++ extensions)
++ && pfkey_safe_build(error = pfkey_lifetime_build(&extensions[SADB_EXT_LIFETIME_CURRENT],
++ SADB_EXT_LIFETIME_CURRENT,
++ ipsp->ips_life.ipl_allocations.ipl_count,
++ ipsp->ips_life.ipl_bytes.ipl_count,
++ ipsp->ips_life.ipl_addtime.ipl_count,
++ ipsp->ips_life.ipl_usetime.ipl_count,
++ ipsp->ips_life.ipl_packets.ipl_count),
++ extensions)
++ && (hard ?
++ pfkey_safe_build(error = pfkey_lifetime_build(&extensions[SADB_EXT_LIFETIME_HARD],
++ SADB_EXT_LIFETIME_HARD,
++ ipsp->ips_life.ipl_allocations.ipl_hard,
++ ipsp->ips_life.ipl_bytes.ipl_hard,
++ ipsp->ips_life.ipl_addtime.ipl_hard,
++ ipsp->ips_life.ipl_usetime.ipl_hard,
++ ipsp->ips_life.ipl_packets.ipl_hard),
++ extensions)
++ : pfkey_safe_build(error = pfkey_lifetime_build(&extensions[SADB_EXT_LIFETIME_SOFT],
++ SADB_EXT_LIFETIME_SOFT,
++ ipsp->ips_life.ipl_allocations.ipl_soft,
++ ipsp->ips_life.ipl_bytes.ipl_soft,
++ ipsp->ips_life.ipl_addtime.ipl_soft,
++ ipsp->ips_life.ipl_usetime.ipl_soft,
++ ipsp->ips_life.ipl_packets.ipl_soft),
++ extensions))
++ && pfkey_safe_build(error = pfkey_address_build(&extensions[SADB_EXT_ADDRESS_SRC],
++ SADB_EXT_ADDRESS_SRC,
++ 0, /* ipsp->ips_said.proto, */
++ 0,
++ ipsp->ips_addr_s),
++ extensions)
++ && pfkey_safe_build(error = pfkey_address_build(&extensions[SADB_EXT_ADDRESS_DST],
++ SADB_EXT_ADDRESS_DST,
++ 0, /* ipsp->ips_said.proto, */
++ 0,
++ ipsp->ips_addr_d),
++ extensions))) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_expire: "
++ "failed to build the expire message extensions\n");
++ spin_unlock(&tdb_lock);
++ goto errlab;
++ }
++
++ if ((error = pfkey_msg_build(&pfkey_msg, extensions, EXT_BITS_OUT))) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_expire: "
++ "failed to build the expire message\n");
++ SENDERR(-error);
++ }
++
++ for(pfkey_socketsp = pfkey_open_sockets;
++ pfkey_socketsp;
++ pfkey_socketsp = pfkey_socketsp->next) {
++ if((error = pfkey_upmsg(pfkey_socketsp->socketp, pfkey_msg))) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_expire: "
++ "sending up expire message for satype=%d(%s) to socket=0p%p failed with error=%d.\n",
++ satype,
++ satype2name(satype),
++ pfkey_socketsp->socketp,
++ error);
++ SENDERR(-error);
++ }
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_expire: "
++ "sending up expire message for satype=%d(%s) (proto=%d) to socket=0p%p succeeded.\n",
++ satype,
++ satype2name(satype),
++ ipsp->ips_said.proto,
++ pfkey_socketsp->socketp);
++ }
++
++ errlab:
++ if (pfkey_msg) {
++ pfkey_msg_free(&pfkey_msg);
++ }
++ pfkey_extensions_free(extensions);
++ return error;
++}
++
++int
++pfkey_acquire(struct ipsec_sa *ipsp)
++{
++ struct sadb_ext *extensions[SADB_EXT_MAX+1];
++ struct sadb_msg *pfkey_msg = NULL;
++ struct socket_list *pfkey_socketsp;
++ int error = 0;
++ struct sadb_comb comb[] = {
++ /* auth; encrypt; flags; */
++ /* auth_minbits; auth_maxbits; encrypt_minbits; encrypt_maxbits; */
++ /* reserved; soft_allocations; hard_allocations; soft_bytes; hard_bytes; */
++ /* soft_addtime; hard_addtime; soft_usetime; hard_usetime; */
++ /* soft_packets; hard_packets; */
++ { SADB_AALG_MD5HMAC, SADB_EALG_3DESCBC, SADB_SAFLAGS_PFS,
++ 128, 128, 168, 168,
++ 0, 0, 0, 0, 0,
++ 57600, 86400, 57600, 86400,
++ 0, 0 },
++ { SADB_AALG_SHA1HMAC, SADB_EALG_3DESCBC, SADB_SAFLAGS_PFS,
++ 160, 160, 168, 168,
++ 0, 0, 0, 0, 0,
++ 57600, 86400, 57600, 86400,
++ 0, 0 }
++ };
++
++ /* XXX This should not be hard-coded. It should be taken from the spdb */
++ uint8_t satype = SADB_SATYPE_ESP;
++
++ pfkey_extensions_init(extensions);
++
++ if((satype == 0) || (satype > SADB_SATYPE_MAX)) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_acquire: "
++ "SAtype=%d unspecified or unknown.\n",
++ satype);
++ SENDERR(EINVAL);
++ }
++
++ if(!(pfkey_registered_sockets[satype])) {
++ KLIPS_PRINT(1|debug_pfkey, "klips_debug:pfkey_acquire: "
++ "no sockets registered for SAtype=%d(%s).\n",
++ satype,
++ satype2name(satype));
++ SENDERR(EPROTONOSUPPORT);
++ }
++
++ if (!(pfkey_safe_build(error = pfkey_msg_hdr_build(&extensions[0],
++ SADB_ACQUIRE,
++ satype,
++ 0,
++ ++pfkey_msg_seq,
++ 0),
++ extensions)
++ && pfkey_safe_build(error = pfkey_address_build(&extensions[SADB_EXT_ADDRESS_SRC],
++ SADB_EXT_ADDRESS_SRC,
++ ipsp->ips_transport_protocol,
++ 0,
++ ipsp->ips_addr_s),
++ extensions)
++ && pfkey_safe_build(error = pfkey_address_build(&extensions[SADB_EXT_ADDRESS_DST],
++ SADB_EXT_ADDRESS_DST,
++ ipsp->ips_transport_protocol,
++ 0,
++ ipsp->ips_addr_d),
++ extensions)
++#if 0
++ && (ipsp->ips_addr_p
++ ? pfkey_safe_build(error = pfkey_address_build(&extensions[SADB_EXT_ADDRESS_PROXY],
++ SADB_EXT_ADDRESS_PROXY,
++ ipsp->ips_transport_protocol,
++ 0,
++ ipsp->ips_addr_p),
++ extensions) : 1)
++#endif
++ && (ipsp->ips_ident_s.type != SADB_IDENTTYPE_RESERVED
++ ? pfkey_safe_build(error = pfkey_ident_build(&extensions[SADB_EXT_IDENTITY_SRC],
++ SADB_EXT_IDENTITY_SRC,
++ ipsp->ips_ident_s.type,
++ ipsp->ips_ident_s.id,
++ ipsp->ips_ident_s.len,
++ ipsp->ips_ident_s.data),
++ extensions) : 1)
++
++ && (ipsp->ips_ident_d.type != SADB_IDENTTYPE_RESERVED
++ ? pfkey_safe_build(error = pfkey_ident_build(&extensions[SADB_EXT_IDENTITY_DST],
++ SADB_EXT_IDENTITY_DST,
++ ipsp->ips_ident_d.type,
++ ipsp->ips_ident_d.id,
++ ipsp->ips_ident_d.len,
++ ipsp->ips_ident_d.data),
++ extensions) : 1)
++#if 0
++ /* FIXME: This won't work yet because I have not finished
++ it. */
++ && (ipsp->ips_sens_
++ ? pfkey_safe_build(error = pfkey_sens_build(&extensions[SADB_EXT_SENSITIVITY],
++ ipsp->ips_sens_dpd,
++ ipsp->ips_sens_sens_level,
++ ipsp->ips_sens_sens_len,
++ ipsp->ips_sens_sens_bitmap,
++ ipsp->ips_sens_integ_level,
++ ipsp->ips_sens_integ_len,
++ ipsp->ips_sens_integ_bitmap),
++ extensions) : 1)
++#endif
++ && pfkey_safe_build(error = pfkey_prop_build(&extensions[SADB_EXT_PROPOSAL],
++ 64, /* replay */
++ sizeof(comb)/sizeof(struct sadb_comb),
++ &(comb[0])),
++ extensions)
++ )) {
++ KLIPS_PRINT(1|debug_pfkey, "klips_debug:pfkey_acquire: "
++ "failed to build the acquire message extensions\n");
++ SENDERR(-error);
++ }
++
++ if ((error = pfkey_msg_build(&pfkey_msg, extensions, EXT_BITS_OUT))) {
++ KLIPS_PRINT(1|debug_pfkey, "klips_debug:pfkey_acquire: "
++ "failed to build the acquire message\n");
++ SENDERR(-error);
++ }
++
++#if KLIPS_PFKEY_ACQUIRE_LOSSAGE > 0
++ if(sysctl_ipsec_regress_pfkey_lossage) {
++ return(0);
++ }
++#endif
++
++ /* this should go to all registered sockets for that satype only */
++ for(pfkey_socketsp = pfkey_registered_sockets[satype];
++ pfkey_socketsp;
++ pfkey_socketsp = pfkey_socketsp->next) {
++ if((error = pfkey_upmsg(pfkey_socketsp->socketp, pfkey_msg))) {
++ KLIPS_PRINT(1|debug_pfkey, "klips_debug:pfkey_acquire: "
++ "sending up acquire message for satype=%d(%s) to socket=0p%p failed with error=%d.\n",
++ satype,
++ satype2name(satype),
++ pfkey_socketsp->socketp,
++ error);
++ SENDERR(-error);
++ }
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_acquire: "
++ "sending up acquire message for satype=%d(%s) to socket=0p%p succeeded.\n",
++ satype,
++ satype2name(satype),
++ pfkey_socketsp->socketp);
++ }
++
++ errlab:
++ if (pfkey_msg) {
++ pfkey_msg_free(&pfkey_msg);
++ }
++ pfkey_extensions_free(extensions);
++ return error;
++}
++
++#ifdef CONFIG_IPSEC_NAT_TRAVERSAL
++int
++pfkey_nat_t_new_mapping(struct ipsec_sa *ipsp, struct sockaddr *ipaddr,
++ __u16 sport)
++{
++ struct sadb_ext *extensions[SADB_EXT_MAX+1];
++ struct sadb_msg *pfkey_msg = NULL;
++ struct socket_list *pfkey_socketsp;
++ int error = 0;
++ uint8_t satype = (ipsp->ips_said.proto==IPPROTO_ESP) ? SADB_SATYPE_ESP : 0;
++
++ /* Construct SADB_X_NAT_T_NEW_MAPPING message */
++
++ pfkey_extensions_init(extensions);
++
++ if((satype == 0) || (satype > SADB_SATYPE_MAX)) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_nat_t_new_mapping: "
++ "SAtype=%d unspecified or unknown.\n",
++ satype);
++ SENDERR(EINVAL);
++ }
++
++ if(!(pfkey_registered_sockets[satype])) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_nat_t_new_mapping: "
++ "no sockets registered for SAtype=%d(%s).\n",
++ satype,
++ satype2name(satype));
++ SENDERR(EPROTONOSUPPORT);
++ }
++
++ if (!(pfkey_safe_build
++ (error = pfkey_msg_hdr_build(&extensions[0], SADB_X_NAT_T_NEW_MAPPING,
++ satype, 0, ++pfkey_msg_seq, 0), extensions)
++ /* SA */
++ && pfkey_safe_build
++ (error = pfkey_sa_build(&extensions[SADB_EXT_SA],
++ SADB_EXT_SA, ipsp->ips_said.spi, 0, 0, 0, 0, 0), extensions)
++ /* ADDRESS_SRC = old addr */
++ && pfkey_safe_build
++ (error = pfkey_address_build(&extensions[SADB_EXT_ADDRESS_SRC],
++ SADB_EXT_ADDRESS_SRC, ipsp->ips_said.proto, 0, ipsp->ips_addr_s),
++ extensions)
++ /* NAT_T_SPORT = old port */
++ && pfkey_safe_build
++ (error = pfkey_x_nat_t_port_build(&extensions[SADB_X_EXT_NAT_T_SPORT],
++ SADB_X_EXT_NAT_T_SPORT, ipsp->ips_natt_sport), extensions)
++ /* ADDRESS_DST = new addr */
++ && pfkey_safe_build
++ (error = pfkey_address_build(&extensions[SADB_EXT_ADDRESS_DST],
++ SADB_EXT_ADDRESS_DST, ipsp->ips_said.proto, 0, ipaddr), extensions)
++ /* NAT_T_DPORT = new port */
++ && pfkey_safe_build
++ (error = pfkey_x_nat_t_port_build(&extensions[SADB_X_EXT_NAT_T_DPORT],
++ SADB_X_EXT_NAT_T_DPORT, sport), extensions)
++ )) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_nat_t_new_mapping: "
++ "failed to build the nat_t_new_mapping message extensions\n");
++ SENDERR(-error);
++ }
++
++ if ((error = pfkey_msg_build(&pfkey_msg, extensions, EXT_BITS_OUT))) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_nat_t_new_mapping: "
++ "failed to build the nat_t_new_mapping message\n");
++ SENDERR(-error);
++ }
++
++ /* this should go to all registered sockets for that satype only */
++ for(pfkey_socketsp = pfkey_registered_sockets[satype];
++ pfkey_socketsp;
++ pfkey_socketsp = pfkey_socketsp->next) {
++ if((error = pfkey_upmsg(pfkey_socketsp->socketp, pfkey_msg))) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_nat_t_new_mapping: "
++ "sending up nat_t_new_mapping message for satype=%d(%s) to socket=%p failed with error=%d.\n",
++ satype,
++ satype2name(satype),
++ pfkey_socketsp->socketp,
++ error);
++ SENDERR(-error);
++ }
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_nat_t_new_mapping: "
++ "sending up nat_t_new_mapping message for satype=%d(%s) to socket=%p succeeded.\n",
++ satype,
++ satype2name(satype),
++ pfkey_socketsp->socketp);
++ }
++
++ errlab:
++ if (pfkey_msg) {
++ pfkey_msg_free(&pfkey_msg);
++ }
++ pfkey_extensions_free(extensions);
++ return error;
++}
++
++DEBUG_NO_STATIC int
++pfkey_x_nat_t_new_mapping_parse(struct sock *sk, struct sadb_ext **extensions, struct pfkey_extracted_data* extr)
++{
++ /* SADB_X_NAT_T_NEW_MAPPING not used in kernel */
++ return -EINVAL;
++}
++#endif
++
++DEBUG_NO_STATIC int (*ext_processors[SADB_EXT_MAX+1])(struct sadb_ext *pfkey_ext, struct pfkey_extracted_data* extr) =
++{
++ NULL, /* pfkey_msg_process, */
++ pfkey_sa_process,
++ pfkey_lifetime_process,
++ pfkey_lifetime_process,
++ pfkey_lifetime_process,
++ pfkey_address_process,
++ pfkey_address_process,
++ pfkey_address_process,
++ pfkey_key_process,
++ pfkey_key_process,
++ pfkey_ident_process,
++ pfkey_ident_process,
++ pfkey_sens_process,
++ pfkey_prop_process,
++ pfkey_supported_process,
++ pfkey_supported_process,
++ pfkey_spirange_process,
++ pfkey_x_kmprivate_process,
++ pfkey_x_satype_process,
++ pfkey_sa_process,
++ pfkey_address_process,
++ pfkey_address_process,
++ pfkey_address_process,
++ pfkey_address_process,
++ pfkey_address_process,
++ pfkey_x_debug_process,
++ pfkey_x_protocol_process
++#ifdef CONFIG_IPSEC_NAT_TRAVERSAL
++ ,
++ pfkey_x_nat_t_type_process,
++ pfkey_x_nat_t_port_process,
++ pfkey_x_nat_t_port_process,
++ pfkey_address_process
++#endif
++};
++
++
++DEBUG_NO_STATIC int (*msg_parsers[SADB_MAX +1])(struct sock *sk, struct sadb_ext *extensions[], struct pfkey_extracted_data* extr)
++ =
++{
++ NULL, /* RESERVED */
++ pfkey_getspi_parse,
++ pfkey_update_parse,
++ pfkey_add_parse,
++ pfkey_delete_parse,
++ pfkey_get_parse,
++ pfkey_acquire_parse,
++ pfkey_register_parse,
++ pfkey_expire_parse,
++ pfkey_flush_parse,
++ pfkey_dump_parse,
++ pfkey_x_promisc_parse,
++ pfkey_x_pchange_parse,
++ pfkey_x_grpsa_parse,
++ pfkey_x_addflow_parse,
++ pfkey_x_delflow_parse,
++ pfkey_x_msg_debug_parse
++#ifdef CONFIG_IPSEC_NAT_TRAVERSAL
++ , pfkey_x_nat_t_new_mapping_parse
++#endif
++};
++
++int
++pfkey_build_reply(struct sadb_msg *pfkey_msg, struct pfkey_extracted_data *extr,
++ struct sadb_msg **pfkey_reply)
++{
++ struct sadb_ext *extensions[SADB_EXT_MAX+1];
++ int error = 0;
++ int msg_type = pfkey_msg->sadb_msg_type;
++ int seq = pfkey_msg->sadb_msg_seq;
++
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_build_reply: "
++ "building reply with type: %d\n",
++ msg_type);
++ pfkey_extensions_init(extensions);
++ if (!extr || !extr->ips) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_build_reply: "
++ "bad ipsec_sa passed\n");
++ return EINVAL;
++ }
++ error = pfkey_safe_build(pfkey_msg_hdr_build(&extensions[0],
++ msg_type,
++ proto2satype(extr->ips->ips_said.proto),
++ 0,
++ seq,
++ pfkey_msg->sadb_msg_pid),
++ extensions) &&
++ (!(extensions_bitmaps[EXT_BITS_OUT][EXT_BITS_REQ][msg_type] &
++ 1 << SADB_EXT_SA)
++ || pfkey_safe_build(pfkey_sa_ref_build(&extensions[SADB_EXT_SA],
++ SADB_EXT_SA,
++ extr->ips->ips_said.spi,
++ extr->ips->ips_replaywin,
++ extr->ips->ips_state,
++ extr->ips->ips_authalg,
++ extr->ips->ips_encalg,
++ extr->ips->ips_flags,
++ extr->ips->ips_ref),
++ extensions)) &&
++ (!(extensions_bitmaps[EXT_BITS_OUT][EXT_BITS_REQ][msg_type] &
++ 1 << SADB_EXT_LIFETIME_CURRENT)
++ || pfkey_safe_build(pfkey_lifetime_build(&extensions
++ [SADB_EXT_LIFETIME_CURRENT],
++ SADB_EXT_LIFETIME_CURRENT,
++ extr->ips->ips_life.ipl_allocations.ipl_count,
++ extr->ips->ips_life.ipl_bytes.ipl_count,
++ extr->ips->ips_life.ipl_addtime.ipl_count,
++ extr->ips->ips_life.ipl_usetime.ipl_count,
++ extr->ips->ips_life.ipl_packets.ipl_count),
++ extensions)) &&
++ (!(extensions_bitmaps[EXT_BITS_OUT][EXT_BITS_REQ][msg_type] &
++ 1 << SADB_EXT_ADDRESS_SRC)
++ || pfkey_safe_build(pfkey_address_build(&extensions[SADB_EXT_ADDRESS_SRC],
++ SADB_EXT_ADDRESS_SRC,
++ extr->ips->ips_said.proto,
++ 0,
++ extr->ips->ips_addr_s),
++ extensions)) &&
++ (!(extensions_bitmaps[EXT_BITS_OUT][EXT_BITS_REQ][msg_type] &
++ 1 << SADB_EXT_ADDRESS_DST)
++ || pfkey_safe_build(pfkey_address_build(&extensions[SADB_EXT_ADDRESS_DST],
++ SADB_EXT_ADDRESS_DST,
++ extr->ips->ips_said.proto,
++ 0,
++ extr->ips->ips_addr_d),
++ extensions));
++
++ if (error == 0) {
++ KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_build_reply: "
++ "building extensions failed\n");
++ return EINVAL;
++ }
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_build_reply: "
++ "built extensions, proceed to build the message\n");
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_build_reply: "
++ "extensions[1]=0p%p\n",
++ extensions[1]);
++ error = pfkey_msg_build(pfkey_reply, extensions, EXT_BITS_OUT);
++ pfkey_extensions_free(extensions);
++
++ return error;
++}
++
++int
++pfkey_msg_interp(struct sock *sk, struct sadb_msg *pfkey_msg,
++ struct sadb_msg **pfkey_reply)
++{
++ int error = 0;
++ int i;
++ struct sadb_ext *extensions[SADB_EXT_MAX+1];
++ struct pfkey_extracted_data extr = {NULL, NULL, NULL};
++
++ pfkey_extensions_init(extensions);
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_msg_interp: "
++ "parsing message ver=%d, type=%d, errno=%d, satype=%d(%s), len=%d, res=%d, seq=%d, pid=%d.\n",
++ pfkey_msg->sadb_msg_version,
++ pfkey_msg->sadb_msg_type,
++ pfkey_msg->sadb_msg_errno,
++ pfkey_msg->sadb_msg_satype,
++ satype2name(pfkey_msg->sadb_msg_satype),
++ pfkey_msg->sadb_msg_len,
++ pfkey_msg->sadb_msg_reserved,
++ pfkey_msg->sadb_msg_seq,
++ pfkey_msg->sadb_msg_pid);
++
++ extr.ips = ipsec_sa_alloc(&error); /* pass in error var by pointer */
++ if(extr.ips == NULL) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_msg_interp: "
++ "memory allocation error.\n");
++ SENDERR(-error);
++ }
++
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_msg_interp: "
++ "allocated extr->ips=0p%p.\n",
++ extr.ips);
++
++ if(pfkey_msg->sadb_msg_satype > SADB_SATYPE_MAX) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_msg_interp: "
++ "satype %d > max %d\n",
++ pfkey_msg->sadb_msg_satype,
++ SADB_SATYPE_MAX);
++ SENDERR(EINVAL);
++ }
++
++ switch(pfkey_msg->sadb_msg_type) {
++ case SADB_GETSPI:
++ case SADB_UPDATE:
++ case SADB_ADD:
++ case SADB_DELETE:
++ case SADB_X_GRPSA:
++ case SADB_X_ADDFLOW:
++
++ if(!(extr.ips->ips_said.proto = satype2proto(pfkey_msg->sadb_msg_satype))) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_msg_interp: "
++ "satype %d lookup failed.\n",
++ pfkey_msg->sadb_msg_satype);
++ SENDERR(EINVAL);
++ } else {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_msg_interp: "
++ "satype %d lookups to proto=%d.\n",
++ pfkey_msg->sadb_msg_satype,
++ extr.ips->ips_said.proto);
++ }
++ break;
++ default:
++ break;
++ }
++
++ /* The NULL below causes the default extension parsers to be used */
++ /* Parse the extensions */
++ if((error = pfkey_msg_parse(pfkey_msg, NULL, extensions, EXT_BITS_IN)))
++ {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_msg_interp: "
++ "message parsing failed with error %d.\n",
++ error);
++ SENDERR(-error);
++ }
++
++ /* Process the extensions */
++ for(i=1; i <= SADB_EXT_MAX;i++) {
++ if(extensions[i] != NULL) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_msg_interp: "
++ "processing ext %d 0p%p with processor 0p%p.\n",
++ i, extensions[i], ext_processors[i]);
++ if((error = ext_processors[i](extensions[i], &extr))) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_msg_interp: "
++ "extension processing for type %d failed with error %d.\n",
++ i,
++ error);
++ SENDERR(-error);
++ }
++
++ }
++
++ }
++
++ /* Parse the message types */
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_msg_interp: "
++ "parsing message type %d(%s) with msg_parser 0p%p.\n",
++ pfkey_msg->sadb_msg_type,
++ pfkey_v2_sadb_type_string(pfkey_msg->sadb_msg_type),
++ msg_parsers[pfkey_msg->sadb_msg_type]);
++ if((error = msg_parsers[pfkey_msg->sadb_msg_type](sk, extensions, &extr))) {
++ KLIPS_PRINT(debug_pfkey,
++ "klips_debug:pfkey_msg_interp: "
++ "message parsing failed with error %d.\n",
++ error);
++ SENDERR(-error);
++ }
++
++#if 0
++ error = pfkey_build_reply(pfkey_msg, &extr, pfkey_reply);
++ if (error) {
++ *pfkey_reply = NULL;
++ }
++#endif
++ errlab:
++ if(extr.ips != NULL) {
++ ipsec_sa_wipe(extr.ips);
++ }
++ if(extr.ips2 != NULL) {
++ ipsec_sa_wipe(extr.ips2);
++ }
++ if (extr.eroute != NULL) {
++ kfree(extr.eroute);
++ }
++ return(error);
++}
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.123 2004/04/06 02:49:26 mcr
++ * pullup of algo code from alg-branch.
++ *
++ * Revision 1.122.2.2 2004/04/05 04:30:46 mcr
++ * patches for alg-branch to compile/work with 2.x openswan
++ *
++ * Revision 1.122.2.1 2003/12/22 15:25:52 jjo
++ * . Merged algo-0.8.1-rc11-test1 into alg-branch
++ *
++ * Revision 1.122 2003/12/10 01:14:27 mcr
++ * NAT-traversal patches to KLIPS.
++ *
++ * Revision 1.121 2003/10/31 02:27:55 mcr
++ * pulled up port-selector patches and sa_id elimination.
++ *
++ * Revision 1.120.4.2 2003/10/29 01:30:41 mcr
++ * elimited "struct sa_id".
++ *
++ * Revision 1.120.4.1 2003/09/21 13:59:56 mcr
++ * pre-liminary X.509 patch - does not yet pass tests.
++ *
++ * Revision 1.120 2003/04/03 17:38:09 rgb
++ * Centralised ipsec_kfree_skb and ipsec_dev_{get,put}.
++ *
++ * Revision 1.119 2003/02/06 01:52:37 rgb
++ * Removed no longer relevant comment
++ *
++ * Revision 1.118 2003/01/30 02:32:44 rgb
++ *
++ * Transmit error code through to caller from callee for better diagnosis of problems.
++ *
++ * Revision 1.117 2003/01/16 18:48:13 rgb
++ *
++ * Fixed sign bug in error return from an sa allocation call in
++ * pfkey_msg_interp.
++ *
++ * Revision 1.116 2002/10/17 16:38:01 rgb
++ * Change pfkey_alloc_eroute() to never static since its consumers
++ * have been moved outside the file.
++ *
++ * Revision 1.115 2002/10/12 23:11:53 dhr
++ *
++ * [KenB + DHR] more 64-bit cleanup
++ *
++ * Revision 1.114 2002/10/05 05:02:58 dhr
++ *
++ * C labels go on statements
++ *
++ * Revision 1.113 2002/09/30 19:11:22 rgb
++ * Turn on debugging for upgoing acquire messages to test for reliability.
++ *
++ * Revision 1.112 2002/09/20 15:41:16 rgb
++ * Switch from pfkey_alloc_ipsec_sa() to ipsec_sa_alloc().
++ * Added sadb_x_sa_ref to struct sadb_sa.
++ * Added ref parameter to pfkey_sa_build().
++ *
++ * Revision 1.111 2002/09/20 05:02:08 rgb
++ * Added memory allocation debugging.
++ * Convert to switch to divulge hmac keys for debugging.
++ * Added text labels to elucidate numeric values presented.
++ *
++ * Revision 1.110 2002/08/03 18:03:05 mcr
++ * loop that checks for SPI's to have been already linked
++ * fails to actually step to next pointer, but continuously
++ * resets to head of list. Wrong pointer used.
++ * test east-icmp-02 revealed this.
++ *
++ * Revision 1.109 2002/07/26 08:48:31 rgb
++ * Added SA ref table code.
++ *
++ * Revision 1.108 2002/05/27 18:55:03 rgb
++ * Remove final vistiges of tdb references via IPSEC_KLIPS1_COMPAT.
++ *
++ * Revision 1.107 2002/05/23 07:16:08 rgb
++ * Added ipsec_sa_put() for releasing an ipsec_sa refcount.
++ * Pointer clean-up.
++ * Added refcount code.
++ *
++ * Revision 1.106 2002/05/14 02:34:13 rgb
++ * Converted reference from ipsec_sa_put to ipsec_sa_add to avoid confusion
++ * with "put" usage in the kernel.
++ * Change all references to tdb, TDB or Tunnel Descriptor Block to ips,
++ * ipsec_sa or ipsec_sa.
++ * Moved all the extension parsing functions to pfkey_v2_ext_process.c.
++ *
++ * Revision 1.105 2002/04/24 07:55:32 mcr
++ * #include patches and Makefiles for post-reorg compilation.
++ *
++ * Revision 1.104 2002/04/24 07:36:34 mcr
++ * Moved from ./klips/net/ipsec/pfkey_v2_parser.c,v
++ *
++ * Revision 1.103 2002/04/20 00:12:25 rgb
++ * Added esp IV CBC attack fix, disabled.
++ *
++ * Revision 1.102 2002/03/08 01:15:17 mcr
++ * put some internal structure only debug messages behind
++ * && sysctl_ipsec_debug_verbose.
++ *
++ * Revision 1.101 2002/01/29 17:17:57 mcr
++ * moved include of ipsec_param.h to after include of linux/kernel.h
++ * otherwise, it seems that some option that is set in ipsec_param.h
++ * screws up something subtle in the include path to kernel.h, and
++ * it complains on the snprintf() prototype.
++ *
++ * Revision 1.100 2002/01/29 04:00:54 mcr
++ * more excise of kversions.h header.
++ *
++ * Revision 1.99 2002/01/29 02:13:19 mcr
++ * introduction of ipsec_kversion.h means that include of
++ * ipsec_param.h must preceed any decisions about what files to
++ * include to deal with differences in kernel source.
++ *
++ * Revision 1.98 2002/01/12 02:57:57 mcr
++ * first regression test causes acquire messages to be lost
++ * 100% of the time. This is to help testing of pluto.
++ *
++ * Revision 1.97 2001/11/26 09:23:52 rgb
++ * Merge MCR's ipsec_sa, eroute, proc and struct lifetime changes.
++ *
++ * Revision 1.93.2.4 2001/10/23 04:20:27 mcr
++ * parity was forced on wrong structure! prototypes help here.
++ *
++ * Revision 1.93.2.3 2001/10/22 21:14:59 mcr
++ * include des.h, removed phony prototypes and fixed calling
++ * conventions to match real prototypes.
++ *
++ * Revision 1.93.2.2 2001/10/15 05:39:03 mcr
++ * %08lx is not the right format for u32. Use %08x. 64-bit safe? ha.
++ *
++ * Revision 1.93.2.1 2001/09/25 02:30:14 mcr
++ * struct tdb -> struct ipsec_sa.
++ * use new lifetime structure. common format routines for debug.
++ *
++ * Revision 1.96 2001/11/06 20:47:54 rgb
++ * Fixed user context call to ipsec_dev_start_xmit() bug. Call
++ * dev_queue_xmit() instead.
++ *
++ * Revision 1.95 2001/11/06 19:47:46 rgb
++ * Added packet parameter to lifetime and comb structures.
++ *
++ * Revision 1.94 2001/10/18 04:45:23 rgb
++ * 2.4.9 kernel deprecates linux/malloc.h in favour of linux/slab.h,
++ * lib/freeswan.h version macros moved to lib/kversions.h.
++ * Other compiler directive cleanups.
++ *
++ * Revision 1.93 2001/09/20 15:32:59 rgb
++ * Min/max cleanup.
++ *
++ * Revision 1.92 2001/09/19 16:35:48 rgb
++ * PF_KEY ident fix for getspi from NetCelo (puttdb duplication).
++ *
++ * Revision 1.91 2001/09/15 16:24:06 rgb
++ * Re-inject first and last HOLD packet when an eroute REPLACE is done.
++ *
++ * Revision 1.90 2001/09/14 16:58:38 rgb
++ * Added support for storing the first and last packets through a HOLD.
++ *
++ * Revision 1.89 2001/09/08 21:14:07 rgb
++ * Added pfkey ident extension support for ISAKMPd. (NetCelo)
++ * Better state coherency (error management) between pf_key and IKE daemon.
++ * (NetCelo)
++ *
++ * Revision 1.88 2001/08/27 19:42:44 rgb
++ * Fix memory leak of encrypt and auth structs in pfkey register.
++ *
++ * Revision 1.87 2001/07/06 19:50:46 rgb
++ * Removed unused debugging code.
++ * Added inbound policy checking code for IPIP SAs.
++ *
++ * Revision 1.86 2001/06/20 06:26:04 rgb
++ * Changed missing SA errors from EEXIST to ENOENT and added debug output
++ * for already linked SAs.
++ *
++ * Revision 1.85 2001/06/15 04:57:02 rgb
++ * Remove single error return condition check and check for all errors in
++ * the case of a replace eroute delete operation. This means that
++ * applications must expect to be deleting something before replacing it
++ * and if nothing is found, complain.
++ *
++ * Revision 1.84 2001/06/14 19:35:12 rgb
++ * Update copyright date.
++ *
++ * Revision 1.83 2001/06/12 00:03:19 rgb
++ * Silence debug set/unset under normal conditions.
++ *
++ * Revision 1.82 2001/05/30 08:14:04 rgb
++ * Removed vestiges of esp-null transforms.
++ *
++ * Revision 1.81 2001/05/27 06:12:12 rgb
++ * Added structures for pid, packet count and last access time to eroute.
++ * Added packet count to beginning of /proc/net/ipsec_eroute.
++ *
++ * Revision 1.80 2001/05/03 19:43:59 rgb
++ * Check error return codes for all build function calls.
++ * Standardise on SENDERR() macro.
++ *
++ * Revision 1.79 2001/04/20 21:09:16 rgb
++ * Cleaned up fixed tdbwipes.
++ * Free pfkey_reply and clean up extensions_reply for grpsa, addflow and
++ * delflow (Per Cederqvist) plugging memleaks.
++ *
++ * Revision 1.78 2001/04/19 19:02:39 rgb
++ * Fixed extr.tdb freeing, stealing it for getspi, update and add.
++ * Refined a couple of spinlocks, fixed the one in update.
++ *
++ * Revision 1.77 2001/04/18 20:26:16 rgb
++ * Wipe/free eroute and both tdbs from extr at end of pfkey_msg_interp()
++ * instead of inside each message type parser. This fixes two memleaks.
++ *
++ * Revision 1.76 2001/04/17 23:51:18 rgb
++ * Quiet down pfkey_x_debug_process().
++ *
++ * Revision 1.75 2001/03/29 01:55:05 rgb
++ * Fixed pfkey key init memleak.
++ * Fixed pfkey encryption key debug output.
++ *
++ * Revision 1.74 2001/03/27 05:29:14 rgb
++ * Debug output cleanup/silencing.
++ *
++ * Revision 1.73 2001/02/28 05:03:28 rgb
++ * Clean up and rationalise startup messages.
++ *
++ * Revision 1.72 2001/02/27 22:24:56 rgb
++ * Re-formatting debug output (line-splitting, joining, 1arg/line).
++ * Check for satoa() return codes.
++ *
++ * Revision 1.71 2001/02/27 06:59:30 rgb
++ * Added satype2name() conversions most places satype is debug printed.
++ *
++ * Revision 1.70 2001/02/26 22:37:08 rgb
++ * Fixed 'unknown proto' INT bug in new code.
++ * Added satype to protocol debugging instrumentation.
++ *
++ * Revision 1.69 2001/02/26 19:57:51 rgb
++ * Re-formatted debug output (split lines, consistent spacing).
++ * Fixed as yet undetected FLUSH bug which called ipsec_tdbcleanup()
++ * with an satype instead of proto.
++ * Checked for satype consistency and fixed minor bugs.
++ * Fixed undetected ungrpspi bug that tried to upmsg a second tdb.
++ * Check for satype sanity in pfkey_expire().
++ * Added satype sanity check to addflow.
++ *
++ * Revision 1.68 2001/02/12 23:14:40 rgb
++ * Remove double spin lock in pfkey_expire().
++ *
++ * Revision 1.67 2001/01/31 19:23:40 rgb
++ * Fixed double-unlock bug introduced by grpsa upmsg (found by Lars Heete).
++ *
++ * Revision 1.66 2001/01/29 22:20:04 rgb
++ * Fix minor add upmsg lifetime bug.
++ *
++ * Revision 1.65 2001/01/24 06:12:33 rgb
++ * Fixed address extension compile bugs just introduced.
++ *
++ * Revision 1.64 2001/01/24 00:31:15 rgb
++ * Added upmsg for addflow/delflow.
++ *
++ * Revision 1.63 2001/01/23 22:02:55 rgb
++ * Added upmsg to x_grpsa.
++ * Fixed lifetimes extentions to add/update/get upmsg.
++ *
++ * Revision 1.62 2000/11/30 21:47:51 rgb
++ * Fix error return bug after returning from pfkey_tdb_init().
++ *
++ * Revision 1.61 2000/11/17 18:10:29 rgb
++ * Fixed bugs mostly relating to spirange, to treat all spi variables as
++ * network byte order since this is the way PF_KEYv2 stored spis.
++ *
++ * Revision 1.60 2000/11/06 04:34:53 rgb
++ * Changed non-exported functions to DEBUG_NO_STATIC.
++ * Add Svenning's adaptive content compression.
++ * Ditched spin_lock_irqsave in favour of spin_lock/_bh.
++ * Fixed double unlock bug (Svenning).
++ * Fixed pfkey_msg uninitialized bug in pfkey_{expire,acquire}().
++ * Fixed incorrect extension type (prop) in pfkey)acquire().
++ *
++ * Revision 1.59 2000/10/11 15:25:12 rgb
++ * Fixed IPCOMP disabled compile bug.
++ *
++ * Revision 1.58 2000/10/11 14:54:03 rgb
++ * Fixed pfkey_acquire() satype to SADB_SATYPE_ESP and removed pfkey
++ * protocol violations of setting pfkey_address_build() protocol parameter
++ * to non-zero except in the case of pfkey_acquire().
++ *
++ * Revision 1.57 2000/10/10 20:10:18 rgb
++ * Added support for debug_ipcomp and debug_verbose to klipsdebug.
++ *
++ * Revision 1.56 2000/10/06 20:24:36 rgb
++ * Fixes to pfkey_acquire to initialize extensions[] and use correct
++ * ipproto.
++ *
++ * Revision 1.55 2000/10/03 03:20:57 rgb
++ * Added brackets to get a?b:c scope right for pfkey_register reply.
++ *
++ * Revision 1.54 2000/09/29 19:49:30 rgb
++ * As-yet-unused-bits cleanup.
++ *
++ * Revision 1.53 2000/09/28 00:35:45 rgb
++ * Padded SATYPE printout in pfkey_register for vertical alignment.
++ *
++ * Revision 1.52 2000/09/20 16:21:58 rgb
++ * Cleaned up ident string alloc/free.
++ *
++ * Revision 1.51 2000/09/20 04:04:20 rgb
++ * Changed static functions to DEBUG_NO_STATIC to reveal function names in
++ * oopsen.
++ *
++ * Revision 1.50 2000/09/16 01:10:53 rgb
++ * Fixed unused var warning with debug off.
++ *
++ * Revision 1.49 2000/09/15 11:37:02 rgb
++ * Merge in heavily modified Svenning Soerensen's <svenning@post5.tele.dk>
++ * IPCOMP zlib deflate code.
++ *
++ * Revision 1.48 2000/09/15 04:57:57 rgb
++ * Cleaned up existing IPCOMP code before svenning addition.
++ * Initialize pfkey_reply and extensions_reply in case of early error in
++ * message parsing functions (thanks Kai!).
++ *
++ * Revision 1.47 2000/09/13 08:02:56 rgb
++ * Added KMd registration notification.
++ *
++ * Revision 1.46 2000/09/12 22:35:36 rgb
++ * Restructured to remove unused extensions from CLEARFLOW messages.
++ *
++ * Revision 1.45 2000/09/12 03:24:23 rgb
++ * Converted #if0 debugs to sysctl.
++ *
++ * Revision 1.44 2000/09/09 06:38:39 rgb
++ * Correct SADB message type for update, add and delete.
++ *
++ * Revision 1.43 2000/09/08 19:19:56 rgb
++ * Change references from DEBUG_IPSEC to CONFIG_IPSEC_DEBUG.
++ * Removed all references to CONFIG_IPSEC_PFKEYv2.
++ * Put in sanity checks in most msg type parsers to catch invalid satypes
++ * and empty socket lists.
++ * Moved spin-locks in pfkey_get_parse() to simplify.
++ * Added pfkey_acquire().
++ * Added upwards messages to update, add, delete, acquire_parse,
++ * expire_parse and flush.
++ * Fix pfkey_prop_build() parameter to be only single indirection.
++ * Changed all replies to use pfkey_reply.
++ * Check return code on puttdb() and deltdbchain() in getspi, update,
++ * add, delete.
++ * Fixed up all pfkey replies to open and registered sockets.
++ *
++ * Revision 1.42 2000/09/01 18:50:26 rgb
++ * Added a supported algorithms array lists, one per satype and registered
++ * existing algorithms.
++ * Fixed pfkey_list_{insert,remove}_{socket,support}() to allow change to
++ * list.
++ * Only send pfkey_expire() messages to sockets registered for that satype.
++ * Added reply to pfkey_getspi_parse().
++ * Added reply to pfkey_get_parse().
++ * Fixed debug output label bug in pfkey_lifetime_process().
++ * Cleaned up pfkey_sa_process a little.
++ * Moved pfkey_safe_build() above message type parsers to make it available
++ * for creating replies.
++ * Added comments for future work in pfkey_acquire_parse().
++ * Fleshed out guts of pfkey_register_parse().
++ *
++ * Revision 1.41 2000/08/24 16:58:11 rgb
++ * Fixed key debugging variables.
++ * Fixed error return code for a failed search.
++ * Changed order of pfkey_get operations.
++ *
++ * Revision 1.40 2000/08/21 16:32:27 rgb
++ * Re-formatted for cosmetic consistency and readability.
++ *
++ * Revision 1.39 2000/08/20 21:38:57 rgb
++ * Bugfixes to as-yet-unused pfkey_update_parse() and
++ * pfkey_register_parse(). (Momchil)
++ * Added functions pfkey_safe_build(), pfkey_expire() and
++ * pfkey_build_reply(). (Momchil)
++ * Added a pfkey_reply parameter to pfkey_msg_interp(). (Momchil)
++ *
++ * Revision 1.38 2000/08/18 21:30:41 rgb
++ * Purged all tdb_spi, tdb_proto and tdb_dst macros. They are unclear.
++ *
++ * Revision 1.37 2000/08/18 18:18:02 rgb
++ * Cosmetic and descriptive changes made to debug test.
++ * getspi and update fixes from Momchil.
++ *
++ * Revision 1.36 2000/08/15 15:41:55 rgb
++ * Fixed the (as yet unused and untested) pfkey_getspi() routine.
++ *
++ * Revision 1.35 2000/08/01 14:51:52 rgb
++ * Removed _all_ remaining traces of DES.
++ *
++ * Revision 1.34 2000/07/28 14:58:32 rgb
++ * Changed kfree_s to kfree, eliminating extra arg to fix 2.4.0-test5.
++ *
++ * Revision 1.33 2000/06/28 05:50:11 rgb
++ * Actually set iv_bits.
++ *
++ * Revision 1.32 2000/05/30 18:36:56 rgb
++ * Fix AH auth hash setup bug. This breaks interop with previous PF_KEY
++ * FreeS/WAN, but fixes interop with other implementations.
++ *
++ * Revision 1.31 2000/03/16 14:05:48 rgb
++ * Fixed brace scope preventing non-debug compile.
++ * Added null parameter check for pfkey_x_debug().
++ *
++ * Revision 1.30 2000/01/22 23:21:13 rgb
++ * Use new function satype2proto().
++ *
++ * Revision 1.29 2000/01/22 08:40:21 rgb
++ * Invert condition to known value to avoid AF_INET6 in 2.0.36.
++ *
++ * Revision 1.28 2000/01/22 07:58:57 rgb
++ * Fixed REPLACEFLOW bug, missing braces around KLIPS_PRINT *and* SENDERR.
++ *
++ * Revision 1.27 2000/01/22 03:48:01 rgb
++ * Added extr pointer component debugging.
++ *
++ * Revision 1.26 2000/01/21 09:41:25 rgb
++ * Changed a (void*) to (char*) cast to do proper pointer math.
++ * Don't call tdbwipe if tdb2 is NULL.
++ *
++ * Revision 1.25 2000/01/21 06:21:01 rgb
++ * Added address cases for eroute flows.
++ * Tidied up compiler directive indentation for readability.
++ * Added ictx,octx vars for simplification.
++ * Added macros for HMAC padding magic numbers.
++ * Converted from double tdb arguments to one structure (extr)
++ * containing pointers to all temporary information structures
++ * and checking for valid arguments to all ext processors and
++ * msg type parsers.
++ * Added spiungrp'ing.
++ * Added klipsdebug switching capability.
++ * Removed sa_process() check for zero protocol.
++ * Added address case for DST2 for grouping.
++ * Added/changed minor debugging instrumentation.
++ * Fixed spigrp for single said, ungrouping case.
++ * Added code to parse addflow and delflow messages.
++ * Removed redundant statements duplicating tdbwipe() functionality
++ * and causing double kfrees.
++ * Permit addflow to have a protocol of 0.
++ *
++ * Revision 1.24 1999/12/09 23:23:00 rgb
++ * Added check to pfkey_sa_process() to do eroutes.
++ * Converted to DIVUP() macro.
++ * Converted if() to switch() in pfkey_register_parse().
++ * Use new pfkey_extensions_init() instead of memset().
++ *
++ * Revision 1.23 1999/12/01 22:18:13 rgb
++ * Preset minspi and maxspi values in case and spirange extension is not
++ * included and check for the presence of an spirange extension before
++ * using it. Initialise tdb_sastate to LARVAL.
++ * Fixed debugging output typo.
++ * Fixed authentication context initialisation bugs (4 places).
++ *
++ * Revision 1.22 1999/11/27 11:53:08 rgb
++ * Moved pfkey_msg_parse prototype to pfkey.h
++ * Moved exts_permitted/required prototype to pfkey.h.
++ * Moved sadb_satype2proto protocol lookup table to lib/pfkey_v2_parse.c.
++ * Deleted SADB_X_EXT_SA2 code from pfkey_sa_process() since it will never
++ * be called.
++ * Moved protocol/algorithm checks to lib/pfkey_v2_parse.c
++ * Debugging error messages added.
++ * Enable lifetime_current checking.
++ * Remove illegal requirement for SA extension to be present in an
++ * originating GETSPI call.
++ * Re-instate requirement for UPDATE or ADD message to be MATURE.
++ * Add argument to pfkey_msg_parse() for direction.
++ * Fixed IPIP dst address bug and purged redundant, leaky code.
++ *
++ * Revision 1.21 1999/11/24 05:24:20 rgb
++ * hanged 'void*extensions' to 'struct sadb_ext*extensions'.
++ * Fixed indention.
++ * Ditched redundant replay check.
++ * Fixed debug message text from 'parse' to 'process'.
++ * Added more debug output.
++ * Forgot to zero extensions array causing bug, fixed.
++ *
++ * Revision 1.20 1999/11/23 23:08:13 rgb
++ * Move all common parsing code to lib/pfkey_v2_parse.c and rename
++ * remaining bits to *_process. (PJO)
++ * Add macros for dealing with alignment and rounding up more opaquely.
++ * Use provided macro ADDRTOA_BUF instead of hardcoded value.
++ * Sort out pfkey and freeswan headers, putting them in a library path.
++ * Corrected a couple of bugs in as-yet-inactive code.
++ *
++ * Revision 1.19 1999/11/20 22:01:10 rgb
++ * Add more descriptive error messages for non-zero reserved fields.
++ * Add more descriptive error message for spirange parsing.
++ * Start on supported extension parsing.
++ * Start on register and get message parsing.
++ *
++ * Revision 1.18 1999/11/18 04:09:20 rgb
++ * Replaced all kernel version macros to shorter, readable form.
++ *
++ * Revision 1.17 1999/11/17 15:53:41 rgb
++ * Changed all occurrences of #include "../../../lib/freeswan.h"
++ * to #include <freeswan.h> which works due to -Ilibfreeswan in the
++ * klips/net/ipsec/Makefile.
++ *
++ * Revision 1.16 1999/10/26 16:57:43 rgb
++ * Add shorter macros for compiler directives to visually clean-up.
++ * Give ipv6 code meaningful compiler directive.
++ * Add comments to other #if 0 debug code.
++ * Remove unused *_bh_atomic() calls.
++ * Fix mis-placed spinlock.
++ *
++ * Revision 1.15 1999/10/16 18:27:10 rgb
++ * Clean-up unused cruft.
++ * Fix-up lifetime_allocations_c and lifetime_addtime_c initialisations.
++ *
++ * Revision 1.14 1999/10/08 18:37:34 rgb
++ * Fix end-of-line spacing to sate whining PHMs.
++ *
++ * Revision 1.13 1999/10/03 18:49:12 rgb
++ * Spinlock fixes for 2.0.xx and 2.3.xx.
++ *
++ * Revision 1.12 1999/10/01 15:44:54 rgb
++ * Move spinlock header include to 2.1> scope.
++ *
++ * Revision 1.11 1999/10/01 00:05:45 rgb
++ * Added tdb structure locking.
++ * Use 'jiffies' instead of do_get_timeofday().
++ * Fix lifetime assignments.
++ *
++ * Revision 1.10 1999/09/21 15:24:45 rgb
++ * Rework spirange code to save entropy and prevent endless loops.
++ *
++ * Revision 1.9 1999/09/16 12:10:21 rgb
++ * Minor fixes to random spi selection for correctness and entropy conservation.
++ *
++ * Revision 1.8 1999/05/25 22:54:46 rgb
++ * Fix comparison that should be an assignment in an if.
++ *
++ * Revision 1.7 1999/05/09 03:25:37 rgb
++ * Fix bug introduced by 2.2 quick-and-dirty patch.
++ *
++ * Revision 1.6 1999/05/08 21:32:30 rgb
++ * Fix error return reporting.
++ *
++ * Revision 1.5 1999/05/05 22:02:33 rgb
++ * Add a quick and dirty port to 2.2 kernels by Marc Boucher <marc@mbsi.ca>.
++ *
++ * Revision 1.4 1999/04/29 15:22:40 rgb
++ * Standardise an error return method.
++ * Add debugging instrumentation.
++ * Add check for existence of macros min/max.
++ * Add extensions permitted/required in/out filters.
++ * Add satype-to-protocol table.
++ * Add a second tdb pointer to each parser to accomodate GRPSA.
++ * Move AH & no_algo_set to GETSPI, UPDATE and ADD.
++ * Add OOO window check.
++ * Add support for IPPROTO_IPIP and hooks for IPPROTO_COMP.
++ * Add timestamp to lifetime parse.
++ * Fix address structure length checking bug.
++ * Fix address structure allocation bug (forgot to kmalloc!).
++ * Add checks for extension lengths.
++ * Add checks for extension reserved illegal values.
++ * Add check for spirange legal values.
++ * Add an extension type for parsing a second satype, SA and
++ * DST_ADDRESS.
++ * Make changes to tdb_init() template to get pfkey_tdb_init(),
++ * eliminating any mention of xformsw.
++ * Implement getspi, update and grpsa (not tested).
++ * Add stubs for as yet unimplemented message types.
++ * Add table of message parsers to substitute for msg_parse switch.
++ *
++ * Revision 1.3 1999/04/15 17:58:07 rgb
++ * Add RCSID labels.
++ *
++ * Revision 1.2 1999/04/15 15:37:26 rgb
++ * Forward check changes from POST1_00 branch.
++ *
++ * Revision 1.1.2.1 1999/03/26 20:58:56 rgb
++ * Add pfkeyv2 support to KLIPS.
++ *
++ * Local variables:
++ * c-file-style: "linux"
++ * End:
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/net/ipsec/radij.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,1225 @@
++char radij_c_version[] = "RCSID $Id$";
++
++/*
++ * This file is defived from ${SRC}/sys/net/radix.c of BSD 4.4lite
++ *
++ * Variable and procedure names have been modified so that they don't
++ * conflict with the original BSD code, as a small number of modifications
++ * have been introduced and we may want to reuse this code in BSD.
++ *
++ * The `j' in `radij' is pronounced as a voiceless guttural (like a Greek
++ * chi or a German ch sound (as `doch', not as in `milch'), or even a
++ * spanish j as in Juan. It is not as far back in the throat like
++ * the corresponding Hebrew sound, nor is it a soft breath like the English h.
++ * It has nothing to do with the Dutch ij sound.
++ *
++ * Here is the appropriate copyright notice:
++ */
++
++/*
++ * Copyright (c) 1988, 1989, 1993
++ * The Regents of the University of California. All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ * 1. Redistributions of source code must retain the above copyright
++ * notice, this list of conditions and the following disclaimer.
++ * 2. Redistributions in binary form must reproduce the above copyright
++ * notice, this list of conditions and the following disclaimer in the
++ * documentation and/or other materials provided with the distribution.
++ * 3. All advertising materials mentioning features or use of this software
++ * must display the following acknowledgement:
++ * This product includes software developed by the University of
++ * California, Berkeley and its contributors.
++ * 4. Neither the name of the University nor the names of its contributors
++ * may be used to endorse or promote products derived from this software
++ * without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ *
++ * @(#)radix.c 8.2 (Berkeley) 1/4/94
++ */
++
++/*
++ * Routines to build and maintain radix trees for routing lookups.
++ */
++
++#include <linux/config.h>
++#include <linux/version.h>
++#include <linux/kernel.h> /* printk() */
++
++#include "openswan/ipsec_param.h"
++
++#ifdef MALLOC_SLAB
++# include <linux/slab.h> /* kmalloc() */
++#else /* MALLOC_SLAB */
++# include <linux/malloc.h> /* kmalloc() */
++#endif /* MALLOC_SLAB */
++#include <linux/errno.h> /* error codes */
++#include <linux/types.h> /* size_t */
++#include <linux/interrupt.h> /* mark_bh */
++
++#include <linux/netdevice.h> /* struct device, and other headers */
++#include <linux/etherdevice.h> /* eth_type_trans */
++#include <linux/ip.h> /* struct iphdr */
++#include <linux/skbuff.h>
++#ifdef NET_21
++# include <asm/uaccess.h>
++# include <linux/in6.h>
++#endif /* NET_21 */
++#include <asm/checksum.h>
++#include <net/ip.h>
++
++#include <openswan.h>
++
++#include "openswan/radij.h"
++#include "openswan/ipsec_encap.h"
++#include "openswan/ipsec_radij.h"
++
++int maj_keylen;
++struct radij_mask *rj_mkfreelist;
++struct radij_node_head *mask_rjhead;
++static int gotOddMasks;
++static char *maskedKey;
++static char *rj_zeroes, *rj_ones;
++
++#define rj_masktop (mask_rjhead->rnh_treetop)
++#ifdef Bcmp
++# undef Bcmp
++#endif /* Bcmp */
++#define Bcmp(a, b, l) (l == 0 ? 0 : memcmp((caddr_t)(b), (caddr_t)(a), (size_t)l))
++/*
++ * The data structure for the keys is a radix tree with one way
++ * branching removed. The index rj_b at an internal node n represents a bit
++ * position to be tested. The tree is arranged so that all descendants
++ * of a node n have keys whose bits all agree up to position rj_b - 1.
++ * (We say the index of n is rj_b.)
++ *
++ * There is at least one descendant which has a one bit at position rj_b,
++ * and at least one with a zero there.
++ *
++ * A route is determined by a pair of key and mask. We require that the
++ * bit-wise logical and of the key and mask to be the key.
++ * We define the index of a route to associated with the mask to be
++ * the first bit number in the mask where 0 occurs (with bit number 0
++ * representing the highest order bit).
++ *
++ * We say a mask is normal if every bit is 0, past the index of the mask.
++ * If a node n has a descendant (k, m) with index(m) == index(n) == rj_b,
++ * and m is a normal mask, then the route applies to every descendant of n.
++ * If the index(m) < rj_b, this implies the trailing last few bits of k
++ * before bit b are all 0, (and hence consequently true of every descendant
++ * of n), so the route applies to all descendants of the node as well.
++ *
++ * The present version of the code makes no use of normal routes,
++ * but similar logic shows that a non-normal mask m such that
++ * index(m) <= index(n) could potentially apply to many children of n.
++ * Thus, for each non-host route, we attach its mask to a list at an internal
++ * node as high in the tree as we can go.
++ */
++
++struct radij_node *
++rj_search(v_arg, head)
++ void *v_arg;
++ struct radij_node *head;
++{
++ register struct radij_node *x;
++ register caddr_t v;
++
++ for (x = head, v = v_arg; x->rj_b >= 0;) {
++ if (x->rj_bmask & v[x->rj_off])
++ x = x->rj_r;
++ else
++ x = x->rj_l;
++ }
++ return (x);
++};
++
++struct radij_node *
++rj_search_m(v_arg, head, m_arg)
++ struct radij_node *head;
++ void *v_arg, *m_arg;
++{
++ register struct radij_node *x;
++ register caddr_t v = v_arg, m = m_arg;
++
++ for (x = head; x->rj_b >= 0;) {
++ if ((x->rj_bmask & m[x->rj_off]) &&
++ (x->rj_bmask & v[x->rj_off]))
++ x = x->rj_r;
++ else
++ x = x->rj_l;
++ }
++ return x;
++};
++
++int
++rj_refines(m_arg, n_arg)
++ void *m_arg, *n_arg;
++{
++ register caddr_t m = m_arg, n = n_arg;
++ register caddr_t lim, lim2 = lim = n + *(u_char *)n;
++ int longer = (*(u_char *)n++) - (int)(*(u_char *)m++);
++ int masks_are_equal = 1;
++
++ if (longer > 0)
++ lim -= longer;
++ while (n < lim) {
++ if (*n & ~(*m))
++ return 0;
++ if (*n++ != *m++)
++ masks_are_equal = 0;
++
++ }
++ while (n < lim2)
++ if (*n++)
++ return 0;
++ if (masks_are_equal && (longer < 0))
++ for (lim2 = m - longer; m < lim2; )
++ if (*m++)
++ return 1;
++ return (!masks_are_equal);
++}
++
++
++struct radij_node *
++rj_match(v_arg, head)
++ void *v_arg;
++ struct radij_node_head *head;
++{
++ caddr_t v = v_arg;
++ register struct radij_node *t = head->rnh_treetop, *x;
++ register caddr_t cp = v, cp2, cp3;
++ caddr_t cplim, mstart;
++ struct radij_node *saved_t, *top = t;
++ int off = t->rj_off, vlen = *(u_char *)cp, matched_off;
++
++ /*
++ * Open code rj_search(v, top) to avoid overhead of extra
++ * subroutine call.
++ */
++ for (; t->rj_b >= 0; ) {
++ if (t->rj_bmask & cp[t->rj_off])
++ t = t->rj_r;
++ else
++ t = t->rj_l;
++ }
++ /*
++ * See if we match exactly as a host destination
++ */
++ KLIPS_PRINT(debug_radij,
++ "klips_debug:rj_match: "
++ "* See if we match exactly as a host destination\n");
++
++ cp += off; cp2 = t->rj_key + off; cplim = v + vlen;
++ for (; cp < cplim; cp++, cp2++)
++ if (*cp != *cp2)
++ goto on1;
++ /*
++ * This extra grot is in case we are explicitly asked
++ * to look up the default. Ugh!
++ */
++ if ((t->rj_flags & RJF_ROOT) && t->rj_dupedkey)
++ t = t->rj_dupedkey;
++ return t;
++on1:
++ matched_off = cp - v;
++ saved_t = t;
++ KLIPS_PRINT(debug_radij,
++ "klips_debug:rj_match: "
++ "** try to match a leaf, t=0p%p\n", t);
++ do {
++ if (t->rj_mask) {
++ /*
++ * Even if we don't match exactly as a hosts;
++ * we may match if the leaf we wound up at is
++ * a route to a net.
++ */
++ cp3 = matched_off + t->rj_mask;
++ cp2 = matched_off + t->rj_key;
++ for (; cp < cplim; cp++)
++ if ((*cp2++ ^ *cp) & *cp3++)
++ break;
++ if (cp == cplim)
++ return t;
++ cp = matched_off + v;
++ }
++ } while ((t = t->rj_dupedkey));
++ t = saved_t;
++ /* start searching up the tree */
++ KLIPS_PRINT(debug_radij,
++ "klips_debug:rj_match: "
++ "*** start searching up the tree, t=0p%p\n",
++ t);
++ do {
++ register struct radij_mask *m;
++
++ t = t->rj_p;
++ KLIPS_PRINT(debug_radij,
++ "klips_debug:rj_match: "
++ "**** t=0p%p\n",
++ t);
++ if ((m = t->rj_mklist)) {
++ /*
++ * After doing measurements here, it may
++ * turn out to be faster to open code
++ * rj_search_m here instead of always
++ * copying and masking.
++ */
++ /* off = min(t->rj_off, matched_off); */
++ off = t->rj_off;
++ if (matched_off < off)
++ off = matched_off;
++ mstart = maskedKey + off;
++ do {
++ cp2 = mstart;
++ cp3 = m->rm_mask + off;
++ KLIPS_PRINT(debug_radij,
++ "klips_debug:rj_match: "
++ "***** cp2=0p%p cp3=0p%p\n",
++ cp2, cp3);
++ for (cp = v + off; cp < cplim;)
++ *cp2++ = *cp++ & *cp3++;
++ x = rj_search(maskedKey, t);
++ while (x && x->rj_mask != m->rm_mask)
++ x = x->rj_dupedkey;
++ if (x &&
++ (Bcmp(mstart, x->rj_key + off,
++ vlen - off) == 0))
++ return x;
++ } while ((m = m->rm_mklist));
++ }
++ } while (t != top);
++ KLIPS_PRINT(debug_radij,
++ "klips_debug:rj_match: "
++ "***** not found.\n");
++ return 0;
++};
++
++#ifdef RJ_DEBUG
++int rj_nodenum;
++struct radij_node *rj_clist;
++int rj_saveinfo;
++DEBUG_NO_STATIC void traverse(struct radij_node *);
++#ifdef RJ_DEBUG2
++int rj_debug = 1;
++#else
++int rj_debug = 0;
++#endif /* RJ_DEBUG2 */
++#endif /* RJ_DEBUG */
++
++struct radij_node *
++rj_newpair(v, b, nodes)
++ void *v;
++ int b;
++ struct radij_node nodes[2];
++{
++ register struct radij_node *tt = nodes, *t = tt + 1;
++ t->rj_b = b; t->rj_bmask = 0x80 >> (b & 7);
++ t->rj_l = tt; t->rj_off = b >> 3;
++ tt->rj_b = -1; tt->rj_key = (caddr_t)v; tt->rj_p = t;
++ tt->rj_flags = t->rj_flags = RJF_ACTIVE;
++#ifdef RJ_DEBUG
++ tt->rj_info = rj_nodenum++; t->rj_info = rj_nodenum++;
++ tt->rj_twin = t; tt->rj_ybro = rj_clist; rj_clist = tt;
++#endif /* RJ_DEBUG */
++ return t;
++}
++
++struct radij_node *
++rj_insert(v_arg, head, dupentry, nodes)
++ void *v_arg;
++ struct radij_node_head *head;
++ int *dupentry;
++ struct radij_node nodes[2];
++{
++ caddr_t v = v_arg;
++ struct radij_node *top = head->rnh_treetop;
++ int head_off = top->rj_off, vlen = (int)*((u_char *)v);
++ register struct radij_node *t = rj_search(v_arg, top);
++ register caddr_t cp = v + head_off;
++ register int b;
++ struct radij_node *tt;
++ /*
++ *find first bit at which v and t->rj_key differ
++ */
++ {
++ register caddr_t cp2 = t->rj_key + head_off;
++ register int cmp_res;
++ caddr_t cplim = v + vlen;
++
++ while (cp < cplim)
++ if (*cp2++ != *cp++)
++ goto on1;
++ *dupentry = 1;
++ return t;
++on1:
++ *dupentry = 0;
++ cmp_res = (cp[-1] ^ cp2[-1]) & 0xff;
++ for (b = (cp - v) << 3; cmp_res; b--)
++ cmp_res >>= 1;
++ }
++ {
++ register struct radij_node *p, *x = top;
++ cp = v;
++ do {
++ p = x;
++ if (cp[x->rj_off] & x->rj_bmask)
++ x = x->rj_r;
++ else x = x->rj_l;
++ } while (b > (unsigned) x->rj_b); /* x->rj_b < b && x->rj_b >= 0 */
++#ifdef RJ_DEBUG
++ if (rj_debug)
++ printk("klips_debug:rj_insert: Going In:\n"), traverse(p);
++#endif /* RJ_DEBUG */
++ t = rj_newpair(v_arg, b, nodes); tt = t->rj_l;
++ if ((cp[p->rj_off] & p->rj_bmask) == 0)
++ p->rj_l = t;
++ else
++ p->rj_r = t;
++ x->rj_p = t; t->rj_p = p; /* frees x, p as temp vars below */
++ if ((cp[t->rj_off] & t->rj_bmask) == 0) {
++ t->rj_r = x;
++ } else {
++ t->rj_r = tt; t->rj_l = x;
++ }
++#ifdef RJ_DEBUG
++ if (rj_debug)
++ printk("klips_debug:rj_insert: Coming out:\n"), traverse(p);
++#endif /* RJ_DEBUG */
++ }
++ return (tt);
++}
++
++struct radij_node *
++rj_addmask(n_arg, search, skip)
++ int search, skip;
++ void *n_arg;
++{
++ caddr_t netmask = (caddr_t)n_arg;
++ register struct radij_node *x;
++ register caddr_t cp, cplim;
++ register int b, mlen, j;
++ int maskduplicated;
++
++ mlen = *(u_char *)netmask;
++ if (search) {
++ x = rj_search(netmask, rj_masktop);
++ mlen = *(u_char *)netmask;
++ if (Bcmp(netmask, x->rj_key, mlen) == 0)
++ return (x);
++ }
++ R_Malloc(x, struct radij_node *, maj_keylen + 2 * sizeof (*x));
++ if (x == 0)
++ return (0);
++ Bzero(x, maj_keylen + 2 * sizeof (*x));
++ cp = (caddr_t)(x + 2);
++ Bcopy(netmask, cp, mlen);
++ netmask = cp;
++ x = rj_insert(netmask, mask_rjhead, &maskduplicated, x);
++ /*
++ * Calculate index of mask.
++ */
++ cplim = netmask + mlen;
++ for (cp = netmask + skip; cp < cplim; cp++)
++ if (*(u_char *)cp != 0xff)
++ break;
++ b = (cp - netmask) << 3;
++ if (cp != cplim) {
++ if (*cp != 0) {
++ gotOddMasks = 1;
++ for (j = 0x80; j; b++, j >>= 1)
++ if ((j & *cp) == 0)
++ break;
++ }
++ }
++ x->rj_b = -1 - b;
++ return (x);
++}
++
++#if 0
++struct radij_node *
++#endif
++int
++rj_addroute(v_arg, n_arg, head, treenodes)
++ void *v_arg, *n_arg;
++ struct radij_node_head *head;
++ struct radij_node treenodes[2];
++{
++ caddr_t v = (caddr_t)v_arg, netmask = (caddr_t)n_arg;
++ register struct radij_node *t, *x=NULL, *tt;
++ struct radij_node *saved_tt, *top = head->rnh_treetop;
++ short b = 0, b_leaf;
++ int mlen, keyduplicated;
++ caddr_t cplim;
++ struct radij_mask *m, **mp;
++
++ /*
++ * In dealing with non-contiguous masks, there may be
++ * many different routes which have the same mask.
++ * We will find it useful to have a unique pointer to
++ * the mask to speed avoiding duplicate references at
++ * nodes and possibly save time in calculating indices.
++ */
++ if (netmask) {
++ x = rj_search(netmask, rj_masktop);
++ mlen = *(u_char *)netmask;
++ if (Bcmp(netmask, x->rj_key, mlen) != 0) {
++ x = rj_addmask(netmask, 0, top->rj_off);
++ if (x == 0)
++ return -ENOMEM; /* (0) rgb */
++ }
++ netmask = x->rj_key;
++ b = -1 - x->rj_b;
++ }
++ /*
++ * Deal with duplicated keys: attach node to previous instance
++ */
++ saved_tt = tt = rj_insert(v, head, &keyduplicated, treenodes);
++#ifdef RJ_DEBUG
++ printk("addkey: duplicated: %d\n", keyduplicated);
++#endif
++ if (keyduplicated) {
++ do {
++ if (tt->rj_mask == netmask)
++ return -EEXIST; /* -ENXIO; (0) rgb */
++ t = tt;
++ if (netmask == 0 ||
++ (tt->rj_mask && rj_refines(netmask, tt->rj_mask)))
++ break;
++ } while ((tt = tt->rj_dupedkey));
++ /*
++ * If the mask is not duplicated, we wouldn't
++ * find it among possible duplicate key entries
++ * anyway, so the above test doesn't hurt.
++ *
++ * We sort the masks for a duplicated key the same way as
++ * in a masklist -- most specific to least specific.
++ * This may require the unfortunate nuisance of relocating
++ * the head of the list.
++ */
++ if (tt && t == saved_tt) {
++ struct radij_node *xx = x;
++ /* link in at head of list */
++ (tt = treenodes)->rj_dupedkey = t;
++ tt->rj_flags = t->rj_flags;
++ tt->rj_p = x = t->rj_p;
++ if (x->rj_l == t) x->rj_l = tt; else x->rj_r = tt;
++ saved_tt = tt; x = xx;
++ } else {
++ (tt = treenodes)->rj_dupedkey = t->rj_dupedkey;
++ t->rj_dupedkey = tt;
++ }
++#ifdef RJ_DEBUG
++ t=tt+1; tt->rj_info = rj_nodenum++; t->rj_info = rj_nodenum++;
++ tt->rj_twin = t; tt->rj_ybro = rj_clist; rj_clist = tt;
++#endif /* RJ_DEBUG */
++ t = saved_tt;
++ tt->rj_key = (caddr_t) v;
++ tt->rj_b = -1;
++ tt->rj_flags = t->rj_flags & ~RJF_ROOT;
++ }
++ /*
++ * Put mask in tree.
++ */
++ if (netmask) {
++ tt->rj_mask = netmask;
++ tt->rj_b = x->rj_b;
++ }
++ t = saved_tt->rj_p;
++ b_leaf = -1 - t->rj_b;
++ if (t->rj_r == saved_tt) x = t->rj_l; else x = t->rj_r;
++ /* Promote general routes from below */
++ if (x->rj_b < 0) {
++ if (x->rj_mask && (x->rj_b >= b_leaf) && x->rj_mklist == 0) {
++ MKGet(m);
++ if (m) {
++ Bzero(m, sizeof *m);
++ m->rm_b = x->rj_b;
++ m->rm_mask = x->rj_mask;
++ x->rj_mklist = t->rj_mklist = m;
++ }
++ }
++ } else if (x->rj_mklist) {
++ /*
++ * Skip over masks whose index is > that of new node
++ */
++ for (mp = &x->rj_mklist; (m = *mp); mp = &m->rm_mklist)
++ if (m->rm_b >= b_leaf)
++ break;
++ t->rj_mklist = m; *mp = 0;
++ }
++ /* Add new route to highest possible ancestor's list */
++ if ((netmask == 0) || (b > t->rj_b )) {
++#ifdef RJ_DEBUG
++ printk("klips:radij.c: netmask = %p or b(%d)>t->rjb(%d)\n", netmask, b, t->rj_b);
++#endif
++ return 0; /* tt rgb */ /* can't lift at all */
++ }
++ b_leaf = tt->rj_b;
++ do {
++ x = t;
++ t = t->rj_p;
++ } while (b <= t->rj_b && x != top);
++ /*
++ * Search through routes associated with node to
++ * insert new route according to index.
++ * For nodes of equal index, place more specific
++ * masks first.
++ */
++ cplim = netmask + mlen;
++ for (mp = &x->rj_mklist; (m = *mp); mp = &m->rm_mklist) {
++ if (m->rm_b < b_leaf)
++ continue;
++ if (m->rm_b > b_leaf)
++ break;
++ if (m->rm_mask == netmask) {
++ m->rm_refs++;
++ tt->rj_mklist = m;
++#ifdef RJ_DEBUG
++ printk("klips:radij.c: m->rm_mask %p == netmask\n", netmask);
++#endif
++ return 0; /* tt rgb */
++ }
++ if (rj_refines(netmask, m->rm_mask))
++ break;
++ }
++ MKGet(m);
++ if (m == 0) {
++ printk("klips_debug:rj_addroute: "
++ "Mask for route not entered\n");
++ return 0; /* (tt) rgb */
++ }
++ Bzero(m, sizeof *m);
++ m->rm_b = b_leaf;
++ m->rm_mask = netmask;
++ m->rm_mklist = *mp;
++ *mp = m;
++ tt->rj_mklist = m;
++#ifdef RJ_DEBUG
++ printk("klips:radij.c: addroute done\n");
++#endif
++ return 0; /* tt rgb */
++}
++
++int
++rj_delete(v_arg, netmask_arg, head, node)
++ void *v_arg, *netmask_arg;
++ struct radij_node_head *head;
++ struct radij_node **node;
++{
++ register struct radij_node *t, *p, *x, *tt;
++ struct radij_mask *m, *saved_m, **mp;
++ struct radij_node *dupedkey, *saved_tt, *top;
++ caddr_t v, netmask;
++ int b, head_off, vlen;
++
++ v = v_arg;
++ netmask = netmask_arg;
++ x = head->rnh_treetop;
++ tt = rj_search(v, x);
++ head_off = x->rj_off;
++ vlen = *(u_char *)v;
++ saved_tt = tt;
++ top = x;
++ if (tt == 0 ||
++ Bcmp(v + head_off, tt->rj_key + head_off, vlen - head_off))
++ return -EFAULT; /* (0) rgb */
++ /*
++ * Delete our route from mask lists.
++ */
++ if ((dupedkey = tt->rj_dupedkey)) {
++ if (netmask)
++ netmask = rj_search(netmask, rj_masktop)->rj_key;
++ while (tt->rj_mask != netmask)
++ if ((tt = tt->rj_dupedkey) == 0)
++ return -ENOENT; /* -ENXIO; (0) rgb */
++ }
++ if (tt->rj_mask == 0 || (saved_m = m = tt->rj_mklist) == 0)
++ goto on1;
++ if (m->rm_mask != tt->rj_mask) {
++ printk("klips_debug:rj_delete: "
++ "inconsistent annotation\n");
++ goto on1;
++ }
++ if (--m->rm_refs >= 0)
++ goto on1;
++ b = -1 - tt->rj_b;
++ t = saved_tt->rj_p;
++ if (b > t->rj_b)
++ goto on1; /* Wasn't lifted at all */
++ do {
++ x = t;
++ t = t->rj_p;
++ } while (b <= t->rj_b && x != top);
++ for (mp = &x->rj_mklist; (m = *mp); mp = &m->rm_mklist)
++ if (m == saved_m) {
++ *mp = m->rm_mklist;
++ MKFree(m);
++ break;
++ }
++ if (m == 0)
++ printk("klips_debug:rj_delete: "
++ "couldn't find our annotation\n");
++on1:
++ /*
++ * Eliminate us from tree
++ */
++ if (tt->rj_flags & RJF_ROOT)
++ return -EFAULT; /* (0) rgb */
++#ifdef RJ_DEBUG
++ /* Get us out of the creation list */
++ for (t = rj_clist; t && t->rj_ybro != tt; t = t->rj_ybro) {}
++ if (t) t->rj_ybro = tt->rj_ybro;
++#endif /* RJ_DEBUG */
++ t = tt->rj_p;
++ if (dupedkey) {
++ if (tt == saved_tt) {
++ x = dupedkey; x->rj_p = t;
++ if (t->rj_l == tt) t->rj_l = x; else t->rj_r = x;
++ } else {
++ for (x = p = saved_tt; p && p->rj_dupedkey != tt;)
++ p = p->rj_dupedkey;
++ if (p) p->rj_dupedkey = tt->rj_dupedkey;
++ else printk("klips_debug:rj_delete: "
++ "couldn't find node that we started with\n");
++ }
++ t = tt + 1;
++ if (t->rj_flags & RJF_ACTIVE) {
++#ifndef RJ_DEBUG
++ *++x = *t; p = t->rj_p;
++#else
++ b = t->rj_info; *++x = *t; t->rj_info = b; p = t->rj_p;
++#endif /* RJ_DEBUG */
++ if (p->rj_l == t) p->rj_l = x; else p->rj_r = x;
++ x->rj_l->rj_p = x; x->rj_r->rj_p = x;
++ }
++ goto out;
++ }
++ if (t->rj_l == tt) x = t->rj_r; else x = t->rj_l;
++ p = t->rj_p;
++ if (p->rj_r == t) p->rj_r = x; else p->rj_l = x;
++ x->rj_p = p;
++ /*
++ * Demote routes attached to us.
++ */
++ if (t->rj_mklist) {
++ if (x->rj_b >= 0) {
++ for (mp = &x->rj_mklist; (m = *mp);)
++ mp = &m->rm_mklist;
++ *mp = t->rj_mklist;
++ } else {
++ for (m = t->rj_mklist; m;) {
++ struct radij_mask *mm = m->rm_mklist;
++ if (m == x->rj_mklist && (--(m->rm_refs) < 0)) {
++ x->rj_mklist = 0;
++ MKFree(m);
++ } else
++ printk("klips_debug:rj_delete: "
++ "Orphaned Mask 0p%p at 0p%p\n", m, x);
++ m = mm;
++ }
++ }
++ }
++ /*
++ * We may be holding an active internal node in the tree.
++ */
++ x = tt + 1;
++ if (t != x) {
++#ifndef RJ_DEBUG
++ *t = *x;
++#else
++ b = t->rj_info; *t = *x; t->rj_info = b;
++#endif /* RJ_DEBUG */
++ t->rj_l->rj_p = t; t->rj_r->rj_p = t;
++ p = x->rj_p;
++ if (p->rj_l == x) p->rj_l = t; else p->rj_r = t;
++ }
++out:
++ tt->rj_flags &= ~RJF_ACTIVE;
++ tt[1].rj_flags &= ~RJF_ACTIVE;
++ *node = tt;
++ return 0; /* (tt) rgb */
++}
++
++int
++rj_walktree(h, f, w)
++ struct radij_node_head *h;
++ register int (*f)(struct radij_node *,void *);
++ void *w;
++{
++ int error;
++ struct radij_node *base, *next;
++ register struct radij_node *rn;
++
++ if(!h || !f /* || !w */) {
++ return -ENODATA;
++ }
++
++ rn = h->rnh_treetop;
++ /*
++ * This gets complicated because we may delete the node
++ * while applying the function f to it, so we need to calculate
++ * the successor node in advance.
++ */
++ /* First time through node, go left */
++ while (rn->rj_b >= 0)
++ rn = rn->rj_l;
++ for (;;) {
++#ifdef CONFIG_IPSEC_DEBUG
++ if(debug_radij) {
++ printk("klips_debug:rj_walktree: "
++ "for: rn=0p%p rj_b=%d rj_flags=%x",
++ rn,
++ rn->rj_b,
++ rn->rj_flags);
++ rn->rj_b >= 0 ?
++ printk(" node off=%x\n",
++ rn->rj_off) :
++ printk(" leaf key = %08x->%08x\n",
++ (u_int)ntohl(((struct sockaddr_encap *)rn->rj_key)->sen_ip_src.s_addr),
++ (u_int)ntohl(((struct sockaddr_encap *)rn->rj_key)->sen_ip_dst.s_addr))
++ ;
++ }
++#endif /* CONFIG_IPSEC_DEBUG */
++ base = rn;
++ /* If at right child go back up, otherwise, go right */
++ while (rn->rj_p->rj_r == rn && (rn->rj_flags & RJF_ROOT) == 0)
++ rn = rn->rj_p;
++ /* Find the next *leaf* since next node might vanish, too */
++ for (rn = rn->rj_p->rj_r; rn->rj_b >= 0;)
++ rn = rn->rj_l;
++ next = rn;
++#ifdef CONFIG_IPSEC_DEBUG
++ if(debug_radij) {
++ printk("klips_debug:rj_walktree: "
++ "processing leaves, rn=0p%p rj_b=%d rj_flags=%x",
++ rn,
++ rn->rj_b,
++ rn->rj_flags);
++ rn->rj_b >= 0 ?
++ printk(" node off=%x\n",
++ rn->rj_off) :
++ printk(" leaf key = %08x->%08x\n",
++ (u_int)ntohl(((struct sockaddr_encap *)rn->rj_key)->sen_ip_src.s_addr),
++ (u_int)ntohl(((struct sockaddr_encap *)rn->rj_key)->sen_ip_dst.s_addr))
++ ;
++ }
++#endif /* CONFIG_IPSEC_DEBUG */
++ /* Process leaves */
++ while ((rn = base)) {
++ base = rn->rj_dupedkey;
++#ifdef CONFIG_IPSEC_DEBUG
++ if(debug_radij) {
++ printk("klips_debug:rj_walktree: "
++ "while: base=0p%p rn=0p%p rj_b=%d rj_flags=%x",
++ base,
++ rn,
++ rn->rj_b,
++ rn->rj_flags);
++ rn->rj_b >= 0 ?
++ printk(" node off=%x\n",
++ rn->rj_off) :
++ printk(" leaf key = %08x->%08x\n",
++ (u_int)ntohl(((struct sockaddr_encap *)rn->rj_key)->sen_ip_src.s_addr),
++ (u_int)ntohl(((struct sockaddr_encap *)rn->rj_key)->sen_ip_dst.s_addr))
++ ;
++ }
++#endif /* CONFIG_IPSEC_DEBUG */
++ if (!(rn->rj_flags & RJF_ROOT) && (error = (*f)(rn, w)))
++ return (-error);
++ }
++ rn = next;
++ if (rn->rj_flags & RJF_ROOT)
++ return (0);
++ }
++ /* NOTREACHED */
++}
++
++int
++rj_inithead(head, off)
++ void **head;
++ int off;
++{
++ register struct radij_node_head *rnh;
++ register struct radij_node *t, *tt, *ttt;
++ if (*head)
++ return (1);
++ R_Malloc(rnh, struct radij_node_head *, sizeof (*rnh));
++ if (rnh == NULL)
++ return (0);
++ Bzero(rnh, sizeof (*rnh));
++ *head = rnh;
++ t = rj_newpair(rj_zeroes, off, rnh->rnh_nodes);
++ ttt = rnh->rnh_nodes + 2;
++ t->rj_r = ttt;
++ t->rj_p = t;
++ tt = t->rj_l;
++ tt->rj_flags = t->rj_flags = RJF_ROOT | RJF_ACTIVE;
++ tt->rj_b = -1 - off;
++ *ttt = *tt;
++ ttt->rj_key = rj_ones;
++ rnh->rnh_addaddr = rj_addroute;
++ rnh->rnh_deladdr = rj_delete;
++ rnh->rnh_matchaddr = rj_match;
++ rnh->rnh_walktree = rj_walktree;
++ rnh->rnh_treetop = t;
++ return (1);
++}
++
++void
++rj_init()
++{
++ char *cp, *cplim;
++
++ if (maj_keylen == 0) {
++ printk("klips_debug:rj_init: "
++ "radij functions require maj_keylen be set\n");
++ return;
++ }
++ R_Malloc(rj_zeroes, char *, 3 * maj_keylen);
++ if (rj_zeroes == NULL)
++ panic("rj_init");
++ Bzero(rj_zeroes, 3 * maj_keylen);
++ rj_ones = cp = rj_zeroes + maj_keylen;
++ maskedKey = cplim = rj_ones + maj_keylen;
++ while (cp < cplim)
++ *cp++ = -1;
++ if (rj_inithead((void **)&mask_rjhead, 0) == 0)
++ panic("rj_init 2");
++}
++
++void
++rj_preorder(struct radij_node *rn, int l)
++{
++ int i;
++
++ if (rn == NULL){
++ printk("klips_debug:rj_preorder: "
++ "NULL pointer\n");
++ return;
++ }
++
++ if (rn->rj_b >= 0){
++ rj_preorder(rn->rj_l, l+1);
++ rj_preorder(rn->rj_r, l+1);
++ printk("klips_debug:");
++ for (i=0; i<l; i++)
++ printk("*");
++ printk(" off = %d\n",
++ rn->rj_off);
++ } else {
++ printk("klips_debug:");
++ for (i=0; i<l; i++)
++ printk("@");
++ printk(" flags = %x",
++ (u_int)rn->rj_flags);
++ if (rn->rj_flags & RJF_ACTIVE) {
++ printk(" @key=0p%p",
++ rn->rj_key);
++ printk(" key = %08x->%08x",
++ (u_int)ntohl(((struct sockaddr_encap *)rn->rj_key)->sen_ip_src.s_addr),
++ (u_int)ntohl(((struct sockaddr_encap *)rn->rj_key)->sen_ip_dst.s_addr));
++ printk(" @mask=0p%p",
++ rn->rj_mask);
++ if (rn->rj_mask)
++ printk(" mask = %08x->%08x",
++ (u_int)ntohl(((struct sockaddr_encap *)rn->rj_mask)->sen_ip_src.s_addr),
++ (u_int)ntohl(((struct sockaddr_encap *)rn->rj_mask)->sen_ip_dst.s_addr));
++ if (rn->rj_dupedkey)
++ printk(" dupedkey = 0p%p",
++ rn->rj_dupedkey);
++ }
++ printk("\n");
++ }
++}
++
++#ifdef RJ_DEBUG
++DEBUG_NO_STATIC void traverse(struct radij_node *p)
++{
++ rj_preorder(p, 0);
++}
++#endif /* RJ_DEBUG */
++
++void
++rj_dumptrees(void)
++{
++ rj_preorder(rnh->rnh_treetop, 0);
++}
++
++void
++rj_free_mkfreelist(void)
++{
++ struct radij_mask *mknp, *mknp2;
++
++ mknp = rj_mkfreelist;
++ while(mknp)
++ {
++ mknp2 = mknp;
++ mknp = mknp->rm_mklist;
++ kfree(mknp2);
++ }
++}
++
++int
++radijcleartree(void)
++{
++ return rj_walktree(rnh, ipsec_rj_walker_delete, NULL);
++}
++
++int
++radijcleanup(void)
++{
++ int error = 0;
++
++ error = radijcleartree();
++
++ rj_free_mkfreelist();
++
++/* rj_walktree(mask_rjhead, ipsec_rj_walker_delete, NULL); */
++ if(mask_rjhead) {
++ kfree(mask_rjhead);
++ }
++
++ if(rj_zeroes) {
++ kfree(rj_zeroes);
++ }
++
++ if(rnh) {
++ kfree(rnh);
++ }
++
++ return error;
++}
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.46 2004/04/06 02:49:26 mcr
++ * pullup of algo code from alg-branch.
++ *
++ * Revision 1.45 2003/10/31 02:27:55 mcr
++ * pulled up port-selector patches and sa_id elimination.
++ *
++ * Revision 1.44.30.1 2003/10/29 01:30:41 mcr
++ * elimited "struct sa_id".
++ *
++ * Revision 1.44 2002/07/24 18:44:54 rgb
++ * Type fiddling to tame ia64 compiler.
++ *
++ * Revision 1.43 2002/05/23 07:14:11 rgb
++ * Cleaned up %p variants to 0p%p for test suite cleanup.
++ *
++ * Revision 1.42 2002/04/24 07:55:32 mcr
++ * #include patches and Makefiles for post-reorg compilation.
++ *
++ * Revision 1.41 2002/04/24 07:36:35 mcr
++ * Moved from ./klips/net/ipsec/radij.c,v
++ *
++ * Revision 1.40 2002/01/29 17:17:58 mcr
++ * moved include of ipsec_param.h to after include of linux/kernel.h
++ * otherwise, it seems that some option that is set in ipsec_param.h
++ * screws up something subtle in the include path to kernel.h, and
++ * it complains on the snprintf() prototype.
++ *
++ * Revision 1.39 2002/01/29 04:00:55 mcr
++ * more excise of kversions.h header.
++ *
++ * Revision 1.38 2002/01/29 02:13:19 mcr
++ * introduction of ipsec_kversion.h means that include of
++ * ipsec_param.h must preceed any decisions about what files to
++ * include to deal with differences in kernel source.
++ *
++ * Revision 1.37 2001/10/18 04:45:23 rgb
++ * 2.4.9 kernel deprecates linux/malloc.h in favour of linux/slab.h,
++ * lib/freeswan.h version macros moved to lib/kversions.h.
++ * Other compiler directive cleanups.
++ *
++ * Revision 1.36 2001/08/22 13:43:51 henry
++ * eliminate the single use of min() to avoid problems with Linus changing it
++ *
++ * Revision 1.35 2001/06/15 04:57:29 rgb
++ * Clarified error return codes.
++ * Changed mask add already exists to EEXIST.
++ * Changed mask delete did not exist to ENOENT.
++ *
++ * Revision 1.34 2001/05/03 19:44:26 rgb
++ * Fix sign of error return codes for rj_addroute().
++ *
++ * Revision 1.33 2001/02/27 22:24:56 rgb
++ * Re-formatting debug output (line-splitting, joining, 1arg/line).
++ * Check for satoa() return codes.
++ *
++ * Revision 1.32 2001/02/27 06:23:15 rgb
++ * Debug line splitting.
++ *
++ * Revision 1.31 2000/11/06 04:35:21 rgb
++ * Clear table *before* releasing other items in radijcleanup.
++ *
++ * Revision 1.30 2000/09/20 04:07:40 rgb
++ * Changed static functions to DEBUG_NO_STATIC to reveal function names in
++ * oopsen.
++ *
++ * Revision 1.29 2000/09/12 03:25:02 rgb
++ * Moved radij_c_version printing to ipsec_version_get_info().
++ *
++ * Revision 1.28 2000/09/08 19:12:56 rgb
++ * Change references from DEBUG_IPSEC to CONFIG_IPSEC_DEBUG.
++ *
++ * Revision 1.27 2000/07/28 14:58:32 rgb
++ * Changed kfree_s to kfree, eliminating extra arg to fix 2.4.0-test5.
++ *
++ * Revision 1.26 2000/05/10 23:11:37 rgb
++ * Comment out most of the startup version information.
++ *
++ * Revision 1.25 2000/01/21 06:21:47 rgb
++ * Change return codes to negative on error.
++ *
++ * Revision 1.24 1999/11/18 04:09:20 rgb
++ * Replaced all kernel version macros to shorter, readable form.
++ *
++ * Revision 1.23 1999/11/17 15:53:41 rgb
++ * Changed all occurrences of #include "../../../lib/freeswan.h"
++ * to #include <freeswan.h> which works due to -Ilibfreeswan in the
++ * klips/net/ipsec/Makefile.
++ *
++ * Revision 1.22 1999/10/15 22:17:28 rgb
++ * Modify radijcleanup() to call radijcleartree().
++ *
++ * Revision 1.21 1999/10/08 18:37:34 rgb
++ * Fix end-of-line spacing to sate whining PHMs.
++ *
++ * Revision 1.20 1999/10/01 15:44:54 rgb
++ * Move spinlock header include to 2.1> scope.
++ *
++ * Revision 1.19 1999/10/01 08:35:52 rgb
++ * Add spinlock include to shut up compiler for 2.0.38.
++ *
++ * Revision 1.18 1999/09/23 18:02:52 rgb
++ * De-alarm the search failure message so it doesn't sound so grave.
++ *
++ * Revision 1.17 1999/05/25 21:26:01 rgb
++ * Fix rj_walktree() sanity checking bug.
++ *
++ * Revision 1.16 1999/05/09 03:25:38 rgb
++ * Fix bug introduced by 2.2 quick-and-dirty patch.
++ *
++ * Revision 1.15 1999/05/05 22:02:33 rgb
++ * Add a quick and dirty port to 2.2 kernels by Marc Boucher <marc@mbsi.ca>.
++ *
++ * Revision 1.14 1999/04/29 15:24:15 rgb
++ * Add sanity checking for null pointer arguments.
++ * Standardise an error return method.
++ *
++ * Revision 1.13 1999/04/11 00:29:02 henry
++ * GPL boilerplate
++ *
++ * Revision 1.12 1999/04/06 04:54:28 rgb
++ * Fix/Add RCSID Id: and Log: bits to make PHMDs happy. This includes
++ * patch shell fixes.
++ *
++ * Revision 1.11 1999/02/17 16:52:53 rgb
++ * Convert DEBUG_IPSEC to KLIPS_PRINT
++ * Clean out unused cruft.
++ *
++ * Revision 1.10 1999/01/22 06:30:05 rgb
++ * Cruft clean-out.
++ * 64-bit clean-up.
++ *
++ * Revision 1.9 1998/12/01 13:22:04 rgb
++ * Added support for debug printing of version info.
++ *
++ * Revision 1.8 1998/11/30 13:22:55 rgb
++ * Rationalised all the klips kernel file headers. They are much shorter
++ * now and won't conflict under RH5.2.
++ *
++ * Revision 1.7 1998/10/25 02:43:26 rgb
++ * Change return type on rj_addroute and rj_delete and add and argument
++ * to the latter to be able to transmit more infomation about errors.
++ *
++ * Revision 1.6 1998/10/19 14:30:06 rgb
++ * Added inclusion of freeswan.h.
++ *
++ * Revision 1.5 1998/10/09 04:33:27 rgb
++ * Added 'klips_debug' prefix to all klips printk debug statements.
++ * Fixed output formatting slightly.
++ *
++ * Revision 1.4 1998/07/28 00:06:59 rgb
++ * Add debug detail to tree traversing.
++ *
++ * Revision 1.3 1998/07/14 18:07:58 rgb
++ * Add a routine to clear the eroute tree.
++ *
++ * Revision 1.2 1998/06/25 20:03:22 rgb
++ * Cleanup #endif comments. Debug output for rj_init.
++ *
++ * Revision 1.1 1998/06/18 21:30:22 henry
++ * move sources from klips/src to klips/net/ipsec to keep stupid kernel
++ * build scripts happier about symlinks
++ *
++ * Revision 1.8 1998/05/25 20:34:15 rgb
++ * Remove temporary ipsec_walk, rj_deltree and rj_delnodes functions.
++ *
++ * Rename ipsec_rj_walker (ipsec_walk) to ipsec_rj_walker_procprint and
++ * add ipsec_rj_walker_delete.
++ *
++ * Recover memory for eroute table on unload of module.
++ *
++ * Revision 1.7 1998/05/21 12:58:58 rgb
++ * Moved 'extern' definitions to ipsec_radij.h to support /proc 3k limit fix.
++ *
++ * Revision 1.6 1998/04/23 20:57:29 rgb
++ * Cleaned up compiler warnings for unused debugging functions.
++ *
++ * Revision 1.5 1998/04/22 16:51:38 rgb
++ * Tidy up radij debug code from recent rash of modifications to debug code.
++ *
++ * Revision 1.4 1998/04/21 21:28:56 rgb
++ * Rearrange debug switches to change on the fly debug output from user
++ * space. Only kernel changes checked in at this time. radij.c was also
++ * changed to temporarily remove buggy debugging code in rj_delete causing
++ * an OOPS and hence, netlink device open errors.
++ *
++ * Revision 1.3 1998/04/14 17:30:37 rgb
++ * Fix up compiling errors for radij tree memory reclamation.
++ *
++ * Revision 1.2 1998/04/12 22:03:25 rgb
++ * Updated ESP-3DES-HMAC-MD5-96,
++ * ESP-DES-HMAC-MD5-96,
++ * AH-HMAC-MD5-96,
++ * AH-HMAC-SHA1-96 since Henry started freeswan cvs repository
++ * from old standards (RFC182[5-9] to new (as of March 1998) drafts.
++ *
++ * Fixed eroute references in /proc/net/ipsec*.
++ *
++ * Started to patch module unloading memory leaks in ipsec_netlink and
++ * radij tree unloading.
++ *
++ * Revision 1.1 1998/04/09 03:06:15 henry
++ * sources moved up from linux/net/ipsec
++ *
++ * Revision 1.1.1.1 1998/04/08 05:35:03 henry
++ * RGB's ipsec-0.8pre2.tar.gz ipsec-0.8
++ *
++ * Revision 0.4 1997/01/15 01:28:15 ji
++ * No changes.
++ *
++ * Revision 0.3 1996/11/20 14:39:04 ji
++ * Minor cleanups.
++ * Rationalized debugging code.
++ *
++ * Revision 0.2 1996/11/02 00:18:33 ji
++ * First limited release.
++ *
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/net/ipsec/sysctl_net_ipsec.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,196 @@
++/*
++ * sysctl interface to net IPSEC subsystem.
++ * Copyright (C) 1998, 1999, 2000, 2001 Richard Guy Briggs.
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ * RCSID $Id$
++ */
++
++/* -*- linux-c -*-
++ *
++ * Initiated April 3, 1998, Richard Guy Briggs <rgb@conscoop.ottawa.on.ca>
++ */
++
++#include <linux/mm.h>
++#include <linux/sysctl.h>
++
++#include "openswan/ipsec_param.h"
++
++#ifdef CONFIG_SYSCTL
++
++#define NET_IPSEC 2112 /* Random number */
++#ifdef CONFIG_IPSEC_DEBUG
++extern int debug_ah;
++extern int debug_esp;
++extern int debug_tunnel;
++extern int debug_eroute;
++extern int debug_spi;
++extern int debug_radij;
++extern int debug_netlink;
++extern int debug_xform;
++extern int debug_rcv;
++extern int debug_pfkey;
++extern int sysctl_ipsec_debug_verbose;
++#ifdef CONFIG_IPSEC_IPCOMP
++extern int sysctl_ipsec_debug_ipcomp;
++#endif /* CONFIG_IPSEC_IPCOMP */
++#endif /* CONFIG_IPSEC_DEBUG */
++
++extern int sysctl_ipsec_icmp;
++extern int sysctl_ipsec_inbound_policy_check;
++extern int sysctl_ipsec_tos;
++int sysctl_ipsec_regress_pfkey_lossage;
++
++enum {
++#ifdef CONFIG_IPSEC_DEBUG
++ NET_IPSEC_DEBUG_AH=1,
++ NET_IPSEC_DEBUG_ESP=2,
++ NET_IPSEC_DEBUG_TUNNEL=3,
++ NET_IPSEC_DEBUG_EROUTE=4,
++ NET_IPSEC_DEBUG_SPI=5,
++ NET_IPSEC_DEBUG_RADIJ=6,
++ NET_IPSEC_DEBUG_NETLINK=7,
++ NET_IPSEC_DEBUG_XFORM=8,
++ NET_IPSEC_DEBUG_RCV=9,
++ NET_IPSEC_DEBUG_PFKEY=10,
++ NET_IPSEC_DEBUG_VERBOSE=11,
++ NET_IPSEC_DEBUG_IPCOMP=12,
++#endif /* CONFIG_IPSEC_DEBUG */
++ NET_IPSEC_ICMP=13,
++ NET_IPSEC_INBOUND_POLICY_CHECK=14,
++ NET_IPSEC_TOS=15,
++ NET_IPSEC_REGRESS_PFKEY_LOSSAGE=16,
++};
++
++static ctl_table ipsec_table[] = {
++#ifdef CONFIG_IPSEC_DEBUG
++ { NET_IPSEC_DEBUG_AH, "debug_ah", &debug_ah,
++ sizeof(int), 0644, NULL, &proc_dointvec},
++ { NET_IPSEC_DEBUG_ESP, "debug_esp", &debug_esp,
++ sizeof(int), 0644, NULL, &proc_dointvec},
++ { NET_IPSEC_DEBUG_TUNNEL, "debug_tunnel", &debug_tunnel,
++ sizeof(int), 0644, NULL, &proc_dointvec},
++ { NET_IPSEC_DEBUG_EROUTE, "debug_eroute", &debug_eroute,
++ sizeof(int), 0644, NULL, &proc_dointvec},
++ { NET_IPSEC_DEBUG_SPI, "debug_spi", &debug_spi,
++ sizeof(int), 0644, NULL, &proc_dointvec},
++ { NET_IPSEC_DEBUG_RADIJ, "debug_radij", &debug_radij,
++ sizeof(int), 0644, NULL, &proc_dointvec},
++ { NET_IPSEC_DEBUG_NETLINK, "debug_netlink", &debug_netlink,
++ sizeof(int), 0644, NULL, &proc_dointvec},
++ { NET_IPSEC_DEBUG_XFORM, "debug_xform", &debug_xform,
++ sizeof(int), 0644, NULL, &proc_dointvec},
++ { NET_IPSEC_DEBUG_RCV, "debug_rcv", &debug_rcv,
++ sizeof(int), 0644, NULL, &proc_dointvec},
++ { NET_IPSEC_DEBUG_PFKEY, "debug_pfkey", &debug_pfkey,
++ sizeof(int), 0644, NULL, &proc_dointvec},
++ { NET_IPSEC_DEBUG_VERBOSE, "debug_verbose",&sysctl_ipsec_debug_verbose,
++ sizeof(int), 0644, NULL, &proc_dointvec},
++#ifdef CONFIG_IPSEC_IPCOMP
++ { NET_IPSEC_DEBUG_IPCOMP, "debug_ipcomp", &sysctl_ipsec_debug_ipcomp,
++ sizeof(int), 0644, NULL, &proc_dointvec},
++#endif /* CONFIG_IPSEC_IPCOMP */
++
++#ifdef CONFIG_IPSEC_REGRESS
++ { NET_IPSEC_REGRESS_PFKEY_LOSSAGE, "pfkey_lossage",
++ &sysctl_ipsec_regress_pfkey_lossage,
++ sizeof(int), 0644, NULL, &proc_dointvec},
++#endif /* CONFIG_IPSEC_REGRESS */
++
++#endif /* CONFIG_IPSEC_DEBUG */
++ { NET_IPSEC_ICMP, "icmp", &sysctl_ipsec_icmp,
++ sizeof(int), 0644, NULL, &proc_dointvec},
++ { NET_IPSEC_INBOUND_POLICY_CHECK, "inbound_policy_check", &sysctl_ipsec_inbound_policy_check,
++ sizeof(int), 0644, NULL, &proc_dointvec},
++ { NET_IPSEC_TOS, "tos", &sysctl_ipsec_tos,
++ sizeof(int), 0644, NULL, &proc_dointvec},
++ {0}
++};
++
++static ctl_table ipsec_net_table[] = {
++ { NET_IPSEC, "ipsec", NULL, 0, 0555, ipsec_table },
++ { 0 }
++};
++
++static ctl_table ipsec_root_table[] = {
++ { CTL_NET, "net", NULL, 0, 0555, ipsec_net_table },
++ { 0 }
++};
++
++static struct ctl_table_header *ipsec_table_header;
++
++int ipsec_sysctl_register(void)
++{
++ ipsec_table_header = register_sysctl_table(ipsec_root_table, 0);
++ if (!ipsec_table_header) {
++ return -ENOMEM;
++ }
++ return 0;
++}
++
++void ipsec_sysctl_unregister(void)
++{
++ unregister_sysctl_table(ipsec_table_header);
++}
++
++#endif /* CONFIG_SYSCTL */
++
++/*
++ * $Log$
++ * Revision 1.3 2004-11-25 10:19:50 kergoth.com!kergoth
++ * Turn off EOLN_NATIVE flag
++ *
++ * (Logical change 1.5010)
++ *
++ * Revision 1.16 2004/04/06 02:49:26 mcr
++ * pullup of algo code from alg-branch.
++ *
++ * Revision 1.15 2002/04/24 07:55:32 mcr
++ * #include patches and Makefiles for post-reorg compilation.
++ *
++ * Revision 1.14 2002/04/24 07:36:35 mcr
++ * Moved from ./klips/net/ipsec/sysctl_net_ipsec.c,v
++ *
++ * Revision 1.13 2002/01/12 02:58:32 mcr
++ * first regression test causes acquire messages to be lost
++ * 100% of the time. This is to help testing of pluto.
++ *
++ * Revision 1.12 2001/06/14 19:35:13 rgb
++ * Update copyright date.
++ *
++ * Revision 1.11 2001/02/26 19:58:13 rgb
++ * Drop sysctl_ipsec_{no_eroute_pass,opportunistic}, replaced by magic SAs.
++ *
++ * Revision 1.10 2000/09/16 01:50:15 rgb
++ * Protect sysctl_ipsec_debug_ipcomp with compiler defines too so that the
++ * linker won't blame rj_delete() for missing symbols. ;-> Damn statics...
++ *
++ * Revision 1.9 2000/09/15 23:17:51 rgb
++ * Moved stuff around to compile with debug off.
++ *
++ * Revision 1.8 2000/09/15 11:37:02 rgb
++ * Merge in heavily modified Svenning Soerensen's <svenning@post5.tele.dk>
++ * IPCOMP zlib deflate code.
++ *
++ * Revision 1.7 2000/09/15 07:37:15 rgb
++ * Munged silly log comment that was causing a warning.
++ *
++ * Revision 1.6 2000/09/15 04:58:23 rgb
++ * Added tos runtime switch.
++ * Removed 'sysctl_ipsec_' prefix from /proc/sys/net/ipsec/ filenames.
++ *
++ * Revision 1.5 2000/09/12 03:25:28 rgb
++ * Filled in and implemented sysctl.
++ *
++ * Revision 1.4 1999/04/11 00:29:03 henry
++ * GPL boilerplate
++ *
++ * Revision 1.3 1999/04/06 04:54:29 rgb
++ * Fix/Add RCSID Id: and Log: bits to make PHMDs happy. This includes
++ * patch shell fixes.
++ *
++ */
+--- /dev/null Tue Mar 11 13:02:56 2003
++++ linux/net/ipsec/version.c Mon Feb 9 13:51:03 2004
+@@ -0,0 +1,44 @@
++/*
++ * return IPsec version information
++ * Copyright (C) 2001 Henry Spencer.
++ *
++ * This library is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU Library General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version. See <http://www.fsf.org/copyleft/lgpl.txt>.
++ *
++ * This library 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 Library General Public
++ * License for more details.
++ *
++ * RCSID $Id$
++ */
++
++#ifdef __KERNEL__
++#include <linux/netdevice.h>
++#endif
++
++#include "openswan.h"
++
++#define V "cvs2002Mar12_01:19:03" /* substituted in by Makefile */
++static const char openswan_number[] = V;
++static const char openswan_string[] = "Openswan " V;
++
++/*
++ - ipsec_version_code - return IPsec version number/code, as string
++ */
++const char *
++ipsec_version_code()
++{
++ return openswan_number;
++}
++
++/*
++ - ipsec_version_string - return full version string
++ */
++const char *
++ipsec_version_string()
++{
++ return openswan_string;
++}
+RCSID $Id$
+--- ./net/ipv4/af_inet.c.preipsec Wed Apr 26 15:13:17 2000
++++ ./net/ipv4/af_inet.c Fri Jun 30 15:01:27 2000
+@@ -1019,6 +1019,17 @@
+ ip_mr_init();
+ #endif
+
++#if defined(CONFIG_IPSEC)
++ {
++ extern /* void */ int ipsec_init(void);
++ /*
++ * Initialise AF_INET ESP and AH protocol support including
++ * e-routing and SA tables
++ */
++ ipsec_init();
++ }
++#endif /* CONFIG_IPSEC */
++
+ /*
+ * Create all the /proc entries.
+ */
+--- /dev/null Fri May 10 13:59:54 2002
++++ linux/net/ipsec/Makefile.ver Sun Jul 28 22:10:40 2002
+@@ -0,0 +1 @@
++IPSECVERSION=cvs2002Mar12_01:19:03
+make[1]: Leaving directory `/data/mtx/oe/tmp/work/openswan-2.2.0-r0/openswan-2.2.0'
diff --git a/linux/linux-mtx-1-2.4.27/14-au1000-eth-link-beat.diff b/linux/linux-mtx-1-2.4.27/14-au1000-eth-link-beat.diff
index e69de29bb2..f848d2a224 100644
--- a/linux/linux-mtx-1-2.4.27/14-au1000-eth-link-beat.diff
+++ b/linux/linux-mtx-1-2.4.27/14-au1000-eth-link-beat.diff
@@ -0,0 +1,64 @@
+--- linux/drivers/net/au1000_eth.c.orig 2004-11-23 12:01:00.551663672 +0100
++++ linux/drivers/net/au1000_eth.c 2004-11-23 12:08:36.795304096 +0100
+@@ -6,7 +6,9 @@
+ * Copyright 2002 TimeSys Corp.
+ * Author: MontaVista Software, Inc.
+ * ppopov@mvista.com or source@mvista.com
+- *
++ * Bjoern Riemer 2004
++ * riemer@fokus.fraunhofer.de or riemer@riemer-nt.de
++ * // fixed the link beat detection with ioctls (SIOCGMIIPHY)
+ * ########################################################################
+ *
+ * This program is free software; you can distribute it and/or modify it
+@@ -1383,6 +1385,10 @@
+ aup->phy_ops->phy_status(dev, aup->phy_addr, &link, &speed);
+ control = MAC_DISABLE_RX_OWN | MAC_RX_ENABLE | MAC_TX_ENABLE;
+ #ifndef CONFIG_CPU_LITTLE_ENDIAN
++ /*riemer: fix for startup without cable */
++ if (!link)
++ dev->flags &= ~IFF_RUNNING;
++
+ control |= MAC_BIG_ENDIAN;
+ #endif
+ if (link && (dev->if_port == IF_PORT_100BASEFX)) {
+@@ -1841,17 +1847,35 @@
+
+ static int au1000_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+ {
+- //u16 *data = (u16 *)&rq->ifr_data;
++/*
++// This structure is used in all SIOCxMIIxxx ioctl calls
++struct mii_ioctl_data {
++ 0 u16 phy_id;
++ 1 u16 reg_num;
++ 2 u16 val_in;
++ 3 u16 val_out;
++};*/
++ u16 *data = (u16 *)&rq->ifr_data;
++ struct au1000_private *aup = (struct au1000_private *) dev->priv;
++ //struct mii_ioctl_data *data = (struct mii_ioctl_data *) & rq->ifr_data;
+
+ /* fixme */
+ switch(cmd) {
+ case SIOCDEVPRIVATE: /* Get the address of the PHY in use. */
+- //data[0] = PHY_ADDRESS;
++ case SIOCGMIIPHY:
++ if (!netif_running(dev))
++ return -EINVAL;
++ data[0] = aup->phy_addr;
+ case SIOCDEVPRIVATE+1: /* Read the specified MII register. */
+- //data[3] = mdio_read(ioaddr, data[0], data[1]);
++ case SIOCGMIIREG:
++ data[3] = mdio_read(dev, data[0], data[1]);
++ //data->val_out = mdio_read(dev,data->phy_id,data->reg_num);
+ return 0;
+ case SIOCDEVPRIVATE+2: /* Write the specified MII register */
+- //mdio_write(ioaddr, data[0], data[1], data[2]);
++ case SIOCSMIIREG:
++ if (!capable(CAP_NET_ADMIN))
++ return -EPERM;
++ mdio_write(dev, data[0], data[1],data[2]);
+ return 0;
+ default:
+ return -EOPNOTSUPP;
diff --git a/linux/openslug-kernel-2.6.9/nslu2-io.c b/linux/openslug-kernel-2.6.9/nslu2-io.c
index e69de29bb2..ad5bdc221d 100644
--- a/linux/openslug-kernel-2.6.9/nslu2-io.c
+++ b/linux/openslug-kernel-2.6.9/nslu2-io.c
@@ -0,0 +1,651 @@
+//=============================================================================
+//
+// nslu2-io.c version 0.1.0
+// Author: Karen Spearel <kas11 at tampabay.rr.com>
+// please report problems/bugs directly to the address above
+//
+// NOTE: THIS IS INCOMPLETE. INCLUDED ONLY TO KEEP FROM BREAKING THE BUILD,
+// IT BEEPS AND SENDS A MESSAGE TO /proc/poweroff. EVENTUALLY IT
+// WILL TALK TO THE n2_pbuttond DAEMON. EVENTUALLY THE LED DRIVER
+// WILL TALK TO SOME USERLAND APP BUT ***NOT*** THE NASTY SETLEDS.
+//
+//=============================================================================
+// GPIO Function State
+// 0 Red LED Status
+// 1 Green LED Ready = 1
+// 2 Disk 2 LED On = 0
+// 3 Disk 1 LED On = 0
+// 4 Buzzer
+// 5 Power Button Pressed = 1
+// 8 Power Down Output = 1 powers down N2
+// 12 Reset Pressed = 0
+//=============================================================================
+// this driver is N2 specific and is purposely designed to do the minimum
+// necessary to provide the necessary services given the limited memory resources
+// of the N2. As OpenSlug develops, addition features will be added as
+// suggested by community leadership.
+//
+// The Userland apps such as SetLeds are just to crufty to bother with.
+// This driver makes no attempt to do so...one day a Userland app will appear
+// ...until then, this does very little.
+//=============================================================================
+
+#include <linux/config.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/utsname.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/proc_fs.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/moduleparam.h>
+#include <linux/timer.h>
+
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#include <asm/hardware.h>
+#include <asm-arm/irq.h>
+#include <asm-arm/delay.h>
+
+// Print kernel error
+#define P_ERROR(args...) \
+ printk(KERN_ERR DRV_NAME ": " args)
+// Print kernel warning
+#define P_WARN(args...) \
+ printk(KERN_WARNING DRV_NAME ": " args)
+// Print kernel notice
+#define P_NOTICE(args...) \
+ printk(KERN_NOTICE DRV_NAME ": " args)
+// Print kernel info
+#define P_INFO(args...) \
+ printk(KERN_INFO DRV_NAME ": " args)
+// Print verbose message. Enabled/disabled by 'log_level' param
+#define P_VERBOSE(args...) \
+ if (log_level >= 1) printk(DRV_NAME ": " args)
+// Print debug message. Enabled/disabled by 'log_level' param
+#define P_DEBUG(args...) \
+ if (log_level >= 2) { \
+ printk("%s: %s()\n", DRV_NAME, __FUNCTION__); \
+ printk(args); }
+
+#ifdef DEBUG
+// Print trace message
+#define TRACE \
+ if (log_level >= 2) printk("%s: %s(): line %d\n", \
+ DRV_NAME, __FUNCTION__, __LINE__)
+#else
+// no trace
+#define TRACE
+#endif
+
+#define VERSION "0.1.1"
+
+#define N2RB_MAJOR 60
+#define N2PB_MAJOR 61
+#define N2BZ_MAJOR 62
+#define N2LM_MAJOR 126
+
+#define N2PB_IRQ 22 //gpio5
+#define N2RB_IRQ 29 //gpio12
+
+#define N2_BEEP_DUR_LONG 2000
+#define N2_BEEP_DUR_MED 400
+#define N2_BEEP_DUR_SHORT 100
+#define N2_BEEP_PITCH_HIGH 250
+#define N2_BEEP_PITCH_MED 500
+#define N2_BEEP_PITCH_LOW 1000
+#define N2_LONG_DELAY 30000
+
+#define N2_BZ_GPIO 4
+#define N2_PB_GPIO 5
+#define N2_PO_GPIO 8 //power off
+#define N2_RB_GPIO 12
+
+#define GPIO_BZ_BM 0x0010 //b0000 0000 0001 0000
+#define GPIO_PB_BM 0x0020 //b0000 0000 0010 0000
+#define GPIO_PO_BM 0x0100 //b0000 0001 0000 0000
+#define GPIO_RB_BM 0x1000 //b0001 0000 0000 0000
+
+#define NOERR 0
+
+#define RB_DELAY 50
+#define PB_DELAY 20
+
+#define PWR_OFF_STR "poweroff"
+
+
+// ioctls -- THESE NEED TO BE PROPERLY DEFINED
+
+#define N2LM_ON 0
+#define N2LM_OFF 1
+#define N2LM_BLINK 2
+#define N2LM_ALT 3
+#define N2LM_ALL_ON 4
+#define N2LM_ALL_OFF 5
+
+#define PHYS_LEDS 4
+#define BLINK_DELAY 25
+
+static int n2lm_ioctl(struct inode * inode, struct file * file, unsigned int cmd, unsigned long arg);
+
+
+
+// OR Masks to turn these LEDs ON
+
+#define RS_RED_ON 0x00000001 //0b0000 0000 0000 0010
+#define RS_GRN_ON 0x00000002 //0b0000 0000 0000 0001
+#define RS_YEL_ON 0x00000003 //0b0000 0000 0000 0011
+
+// AND Masks to turn these LEDs OFF
+
+#define RS_RED_OFF 0xfffffffe //0b1111 1111 1111 1101
+#define RS_GRN_OFF 0xfffffffd //0b1111 1111 1111 1110
+#define RS_YEL_OFF 0xfffffffc //0b1111 1111 1111 1100
+
+// AND Masks to turn these LEDs ON
+
+#define DISK1_ON 0xfffffff7 //0b1111 1111 1111 0111
+#define DISK2_ON 0xfffffffb //0b1111 1111 1111 1011
+
+// Or Masks to turn these LEDs OFF
+
+#define DISK1_OFF 0x00000008 //0b0000 0000 0000 1000
+#define DISK2_OFF 0x00000004 //0b0000 0000 0000 0100
+
+// EOR masks for toggling LEDs on/off
+
+#define RS_RG_ALT 0x00000003 //eor mask to toggle rs rg bits
+#define RS_GRN_TGL 0x00000002
+#define RS_RED_TGL 0x00000001
+#define DISK1_TGL 0x00000008
+#define DISK2_TGL 0x00000004
+
+// The LED names for switches
+
+#define LED_RS_RED 0
+#define LED_RS_GRN 1
+#define LED_DISK1 2
+#define LED_DISK2 3
+#define LED_ALL 4
+
+static long init_jiffy = 0; //jiffies at init time
+static int rb_presses = 0; //number of reset button presses
+
+DECLARE_WAIT_QUEUE_HEAD(n2rb_waitq);
+DECLARE_WAIT_QUEUE_HEAD(n2pb_waitq);
+
+static struct timer_list n2lm_rsg_timer; //rs green
+static struct timer_list n2lm_rsr_timer; //rs red
+static struct timer_list n2lm_d1_timer; //drive 1
+static struct timer_list n2lm_d2_timer; //drive 2
+static struct timer_list n2rb_timer;
+static struct timer_list n2pb_timer;
+
+
+//==================================================================================================
+//
+// Blinking is handled entirely by the 4 timer handlers. On timeout, the bit in the
+// GPIO output register is xor'd with a mask corresponding to the selected led which simply
+// flips that bit. No record of what any of the other leds is doing is needed.
+//
+//==================================================================================================
+// this blinks rs green or green/yellow if rs red is on
+static void n2lm_rsg_handler(unsigned long data)
+{
+ *IXP4XX_GPIO_GPOUTR ^= RS_GRN_TGL; //flip the led
+ n2lm_rsg_timer.expires = jiffies + BLINK_DELAY; //next timeout
+ add_timer(&n2lm_rsg_timer); //reinit timer
+ return;
+}
+
+// this blinks or alternates rs red green... inited wit green on/red off
+static void n2lm_rsr_handler(unsigned long data)
+{
+ *IXP4XX_GPIO_GPOUTR ^= n2lm_rsr_timer.data;
+ n2lm_rsr_timer.expires = jiffies + BLINK_DELAY;
+ add_timer(&n2lm_rsr_timer);
+ return;
+}
+// blinks disk 1
+static void n2lm_d1_handler(unsigned long data)
+{
+ *IXP4XX_GPIO_GPOUTR ^= DISK1_TGL;
+ n2lm_d1_timer.expires = jiffies + BLINK_DELAY;
+ add_timer(&n2lm_d1_timer);
+ return;
+}
+// blinks disk 2
+static void n2lm_d2_handler(unsigned long data)
+{
+ *IXP4XX_GPIO_GPOUTR ^= DISK2_TGL;
+ n2lm_d2_timer.expires = jiffies + BLINK_DELAY;
+ add_timer(&n2lm_d2_timer);
+ return;
+}
+
+//==================================================================================================
+
+static void n2lm_timer_start(unsigned long led)
+{
+
+ printk(KERN_DEBUG "timer: %ld\n",led);
+
+ switch(led) {
+ case LED_RS_RED:
+ n2lm_rsr_timer.expires = jiffies + BLINK_DELAY;
+ n2lm_rsr_timer.function = n2lm_rsr_handler;
+ add_timer(&n2lm_rsr_timer);
+ break;
+
+ case LED_RS_GRN:
+ n2lm_rsg_timer.expires = jiffies + BLINK_DELAY;
+ n2lm_rsg_timer.function = n2lm_rsg_handler;
+ add_timer(&n2lm_rsg_timer);
+ break;
+
+ case LED_DISK1:
+ n2lm_d1_timer.expires = jiffies + BLINK_DELAY;
+ n2lm_d1_timer.function = n2lm_d1_handler;
+ add_timer(&n2lm_d1_timer);
+ break;
+
+ case LED_DISK2:
+ n2lm_d2_timer.expires = jiffies + BLINK_DELAY;
+ n2lm_d2_timer.function = n2lm_d2_handler;
+ add_timer(&n2lm_d2_timer);
+ break;
+
+ default:
+ break;
+ }
+ return;
+}
+
+//==================================================================================================
+
+static void n2lm_timer_stop(unsigned long led)
+{
+ switch (led) {
+ case LED_RS_RED:
+ del_timer(&n2lm_rsr_timer);
+ break;
+ case LED_RS_GRN:
+ del_timer(&n2lm_rsg_timer);
+ break;
+ case LED_DISK1:
+ del_timer(&n2lm_d1_timer);
+ break;
+ case LED_DISK2:
+ del_timer(&n2lm_d2_timer);
+ break;
+ default:
+ break;
+ }
+ return;
+}
+
+//--------------------------------------------------------------------------------------------------
+
+static void n2lm_timer_stop_all(void)
+{
+ del_timer(&n2lm_rsg_timer);
+ del_timer(&n2lm_rsr_timer);
+ del_timer(&n2lm_d1_timer);
+ del_timer(&n2lm_d2_timer);
+ return;
+}
+//--------------------------------------------------------------------------------------------------
+
+static void n2lm_ledon(unsigned long led)
+{
+
+ printk("ledon: %ld\n", led);
+
+ switch (led) {
+ case LED_RS_RED:
+ *IXP4XX_GPIO_GPOUTR |= RS_RED_ON; //1
+ return;
+ case LED_RS_GRN:
+ *IXP4XX_GPIO_GPOUTR |= RS_GRN_ON; //2
+ return;
+ case LED_DISK1:
+ *IXP4XX_GPIO_GPOUTR &= DISK1_ON; //0xfffffffb
+ return;
+ case LED_DISK2:
+ *IXP4XX_GPIO_GPOUTR &= DISK2_ON; //0xfffffff7
+ return;
+ case LED_ALL: //all green
+ *IXP4XX_GPIO_GPOUTR |= RS_GRN_ON;
+ *IXP4XX_GPIO_GPOUTR &= (DISK1_ON & DISK2_ON);
+ return;
+ }
+}
+
+//--------------------------------------------------------------------------------------------------
+
+static void n2lm_ledoff(unsigned long led)
+{
+
+ switch (led) {
+ case LED_RS_RED:
+ *IXP4XX_GPIO_GPOUTR &= RS_RED_OFF; //0xffffffffe
+ return;
+ case LED_RS_GRN:
+ *IXP4XX_GPIO_GPOUTR &= RS_GRN_OFF; //0xfffffffd
+ return;
+ case LED_DISK1:
+ *IXP4XX_GPIO_GPOUTR |= DISK1_OFF; //0x00000004
+ return;
+ case LED_DISK2:
+ *IXP4XX_GPIO_GPOUTR |= DISK2_OFF; //0x00000008
+ return;
+ case LED_ALL:
+ *IXP4XX_GPIO_GPOUTR &= (RS_GRN_OFF & RS_RED_OFF);
+ *IXP4XX_GPIO_GPOUTR |= (DISK1_OFF | DISK2_OFF);
+ }
+}
+
+//==================================================================================================
+
+static int n2lm_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long led)
+{
+
+ printk(KERN_DEBUG "cmd=%d, led=%ld\n", cmd, led);
+
+ if (led < 0 || led >= PHYS_LEDS)
+ return -EINVAL;
+
+ switch (cmd ) {
+ case N2LM_ON:
+ n2lm_timer_stop(led);
+ n2lm_ledon(led);
+ break;
+
+ case N2LM_OFF:
+ n2lm_timer_stop(led);
+ n2lm_ledoff(led);
+ break;
+
+ case N2LM_BLINK:
+ n2lm_ledon(led);
+ if (led == LED_RS_RED)
+ n2lm_rsr_timer.data = RS_RED_TGL;
+ if (led == LED_RS_GRN)
+ n2lm_rsr_timer.data = RS_GRN_TGL;
+ n2lm_timer_start(led);
+ break;
+
+ case N2LM_ALT:
+ if (led == LED_RS_RED)
+ {
+ n2lm_ledon(LED_RS_GRN);
+ n2lm_ledoff(LED_RS_RED);
+ n2lm_rsr_timer.data = RS_RG_ALT;
+ n2lm_timer_start(LED_RS_RED);
+ break;
+ } else
+ return -EINVAL;
+
+ case N2LM_ALL_ON:
+ n2lm_timer_stop_all();
+ n2lm_ledon(LED_ALL);
+ break;
+
+ case N2LM_ALL_OFF:
+ n2lm_timer_stop_all();
+ n2lm_ledoff(LED_ALL);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return NOERR;
+}
+
+static struct file_operations n2lm_fops = {
+ .owner = THIS_MODULE,
+ .ioctl = n2lm_ioctl,
+};
+//==================================================================================================
+// We can't do anything fancy here since the system tick rate is far below that required to
+// generate a desirable tone. Therefore we haven't much choice but to use a busy loop until
+// I get up to speed on the timers. The saving grace is that for the normal uses, nothing
+// important should be haprepening.
+//==================================================================================================
+
+static void n2_beep(int tone_delay, int duration)
+{
+ int i;
+
+ *IXP4XX_GPIO_GPOER &= ~GPIO_BZ_BM;
+
+ for (i = 1; i < duration; i++) {
+ *IXP4XX_GPIO_GPOUTR &= ~GPIO_BZ_BM;
+ udelay(tone_delay);
+ *IXP4XX_GPIO_GPOUTR |= GPIO_BZ_BM;
+ udelay(tone_delay);
+ }
+ *IXP4XX_GPIO_GPOER |= GPIO_BZ_BM;
+
+ return;
+}
+
+//==================================================================================================
+
+static irqreturn_t n2pb_handler (int irq, void *dev_id, struct pt_regs *regs)
+{
+ void *ret;
+
+ wake_up(&n2pb_waitq);
+ remove_proc_entry(PWR_OFF_STR, NULL); //no parent
+ n2_beep(N2_BEEP_PITCH_MED, N2_BEEP_DUR_MED);
+ ret = create_proc_entry(PWR_OFF_STR, 0, NULL);
+
+// WARNING: This is RUDE...it unconditionally pulls the power plug
+// your data will be at risk...since this is just a test system
+// I am leaving it enabled...eventually userland needs to get the
+// message, do an orderly shutdown and use an ioctl or something in
+// /proc/powerdowm to actually have us pull the plug.
+
+ *IXP4XX_GPIO_GPOER &= ~GPIO_PO_BM; // enable the pwr cntl gpio
+ *IXP4XX_GPIO_GPOUTR |= GPIO_PO_BM; // do the deed
+
+ printk(KERN_DEBUG "cpe ret = %p\n", ret);
+ return IRQ_HANDLED;
+}
+
+//==================================================================================================
+//
+//static void do_rb_timeout(unsigned long data)
+//{
+// int i;
+//
+// for (i = 0; i < rb_presses; i++)
+// n2_beep(N2_BEEP_PITCH_MED,N2_BEEP_DUR_SHORT);
+// return;
+//}
+//
+//==================================================================================================
+// does nothing -- waiting for userland to define
+// This thing is sorta braindead...edge triggered IRQs aren't available in the drivers yet...so
+// we hang in a loop until the button is no longer pressed
+static irqreturn_t n2rb_handler (int irq, void *dev_id, struct pt_regs *regs)
+{
+
+ unsigned long test[] = { 5,0, 0,0, 1,0, 0,1, 1,1, 0,2, 1,2, 0,3, 1,3, 2,0, 1,0, 2,1, 1,1, 2,2, 1,2, 2,3, 1,3, 5,0, 3,1, 5,0, 4,0 };
+
+ printk(KERN_DEBUG "Reset Entry IRQ=%d Presses= %d Jiffies= %08lx\n", irq, rb_presses, jiffies);
+
+ wake_up(&n2rb_waitq);
+ while ((*IXP4XX_GPIO_GPINR & GPIO_RB_BM) == 0)
+ ; //wait for button release
+
+ if (rb_presses == 21) {
+ rb_presses = 0;
+ }
+ n2lm_ioctl(NULL,NULL,test[rb_presses*2], test[rb_presses*2+1]);
+ rb_presses++;
+
+// if (rb_presses == 0) {
+// init_jiffy = jiffies;
+// init_timer (&n2rb_timer);
+// n2rb_timer.function = do_rb_timeout;
+// };
+//
+// if (rb_presses == 8)
+// rb_presses = 0;
+// if (rb_presses & 1)
+// n2lm_ledon(test[rb_presses]);
+// else
+// n2lm_ledoff(test[rb_presses]);
+//
+// n2rb_timer.expires = (jiffies + RB_DELAY);
+// add_timer (&n2rb_timer);
+// if (rb_presses < 5) {
+// if (rb_presses > 0)
+// n2lm_ledoff(rb_presses);
+// n2lm_ledon(++rb_presses);
+// n2lm_timer_start(rb_presses);
+// };
+
+ printk(KERN_DEBUG "Reset Exit IRQ=%d Presses= %d Jiffies= %08lx\n", irq, rb_presses, jiffies);
+ return IRQ_HANDLED;
+}
+
+//==================================================================================================
+// What to do here is majorly undetermined...
+
+static int n2rb_read (struct file *filp, char __user *buffer, size_t count, loff_t *ppos)
+{
+ printk(KERN_DEBUG "Reset Button Wait\n");
+ interruptible_sleep_on(&n2rb_waitq);
+ return copy_to_user(buffer, "reset", 5) ? -EFAULT : 5;
+
+}
+
+//==================================================================================================
+// What to do here is majorly undetermined...
+
+static int n2pb_read (struct file *filp, char __user *buffer, size_t count, loff_t *ppos)
+{
+ printk(KERN_DEBUG "Power Button Wait\n");
+ interruptible_sleep_on(&n2pb_waitq);
+ return copy_to_user(buffer, "poweroff", 8) ? -EFAULT : 8;
+
+}
+
+//--------------------------------------------------------------------------------------------------
+
+static struct file_operations n2rb_fops = {
+ .owner = THIS_MODULE,
+ .read = n2rb_read,
+};
+
+//--------------------------------------------------------------------------------------------------
+
+static struct file_operations n2pb_fops = {
+ .owner = THIS_MODULE,
+ .read = n2pb_read,
+};
+
+//==================================================================================================
+
+static void n2iom_initarch(void)
+{
+ printk(KERN_DEBUG "setup_interrupts - jiffies=%ld init_jiffy=%ld\n", jiffies, init_jiffy);
+
+ *IXP4XX_GPIO_GPISR = 0x20400000; // read the 2 irqs to clr
+ gpio_line_config(N2_RB_GPIO, IXP4XX_GPIO_IN | IXP4XX_GPIO_ACTIVE_LOW);
+ gpio_line_isr_clear(N2_RB_GPIO);
+ gpio_line_config(N2_PB_GPIO, IXP4XX_GPIO_IN | IXP4XX_GPIO_ACTIVE_HIGH);
+ gpio_line_isr_clear(N2_PB_GPIO);
+
+ init_timer(&n2lm_rsg_timer);
+ init_timer(&n2lm_rsr_timer);
+ init_timer(&n2lm_d1_timer);
+ init_timer(&n2lm_d2_timer);
+ init_timer(&n2rb_timer);
+ init_timer(&n2pb_timer);
+
+ *IXP4XX_GPIO_GPOER &= 0xfffffff0; //enable gpio 0-3
+ *IXP4XX_GPIO_GPOUTR |= 0x00000003; //turn off the leds
+ *IXP4XX_GPIO_GPOUTR &= 0xfffffffc;
+ n2lm_ledon(LED_ALL);
+ n2_beep(N2_BEEP_PITCH_MED, N2_BEEP_DUR_SHORT);
+ n2lm_ledoff(LED_ALL);
+
+ return;
+}
+
+//==================================================================================================
+
+static int __init n2iom_init(void)
+{
+ printk(KERN_INFO "NSLU2 Misc I/O Driver Version %s (C) Karen Spearel\n", VERSION);
+
+ init_jiffy = jiffies;
+ printk(KERN_DEBUG "init_jiffy=%ld\n",init_jiffy);
+ n2iom_initarch();
+
+ if (register_chrdev(N2RB_MAJOR, "n2_rb", &n2pb_fops) < NOERR) {
+ printk(KERN_DEBUG "Reset Button Major %d not available\n", N2RB_MAJOR);
+ return -EBUSY;
+ }
+ if (register_chrdev(N2PB_MAJOR, "n2_pb", &n2rb_fops) < NOERR) {
+ printk(KERN_DEBUG "Power Button Major %d not available\n", N2PB_MAJOR);
+ return -EBUSY;
+ }
+ if (register_chrdev(N2LM_MAJOR, "n2_leds", &n2lm_fops) < NOERR) {
+ printk(KERN_DEBUG "Led Manager Major %d not available\n", N2LM_MAJOR);
+ return -EBUSY;
+ }
+ if (request_irq(N2RB_IRQ, &n2rb_handler, SA_INTERRUPT, "n2_rb", NULL) < NOERR) {
+ printk(KERN_DEBUG "Reset Button IRQ %d not available\n", N2RB_IRQ);
+ return -EIO;
+ }
+ if (request_irq(N2PB_IRQ, &n2pb_handler, SA_INTERRUPT, "n2_pb", NULL) < NOERR) {
+ printk(KERN_DEBUG "Power Button IRQ %d not available\n", N2PB_IRQ);
+ return -EIO;
+ }
+
+ enable_irq(N2PB_IRQ);
+ enable_irq(N2RB_IRQ);
+ return (NOERR);
+}
+
+//==================================================================================================
+
+static void __exit n2iom_exit(void)
+{
+ remove_proc_entry(PWR_OFF_STR, NULL);
+ del_timer(&n2rb_timer);
+ free_irq(N2RB_IRQ,NULL);
+ unregister_chrdev(N2PB_MAJOR, "n2pb");
+ del_timer(&n2pb_timer);
+ free_irq(N2PB_IRQ, NULL);
+ unregister_chrdev(N2RB_MAJOR, "n2rb" );
+ del_timer(&n2lm_rsg_timer);
+ del_timer(&n2lm_rsr_timer);
+ del_timer(&n2lm_d1_timer);
+ del_timer(&n2lm_d2_timer);
+ unregister_chrdev(N2LM_MAJOR, "n2lm" );
+}
+
+module_init (n2iom_init);
+module_exit (n2iom_exit);
+
+MODULE_AUTHOR("Karen Spearel <kas11@tampabay.rr.com>");
+MODULE_DESCRIPTION("NSLU2 Buttons/LEDs IO Driver");
+MODULE_LICENSE("GPL");
+static int debug = 7;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Debugging enabled = 1");
+
diff --git a/linux/openslug-kernel-2.6.9/nslu2-part.c b/linux/openslug-kernel-2.6.9/nslu2-part.c
index e69de29bb2..6fbf952e2a 100644
--- a/linux/openslug-kernel-2.6.9/nslu2-part.c
+++ b/linux/openslug-kernel-2.6.9/nslu2-part.c
@@ -0,0 +1,120 @@
+/*
+ * nslu2-part.c
+ *
+ * Maintainers: http://www.nslu2-linux.org/
+ * Initial port: Mark Rakes <mrakes AT mac.com>
+ *
+ * "Parse" the fixed partition table of the Linksys NSLU2 and
+ * produce a Linux partition array to match.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/vmalloc.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+
+/* info we know about the NSLU2's flash setup:
+ *
+ * Num Partition offset size
+ * --- --------- ---------- -----------
+ * 0 RedBoot 0x00000000 0x00040000
+ * 1 System Configuration 0x00040000 0x00020000
+ * 2 Kernel 0x00060000 0x00100000
+ * 3 Ramdisk 0x00160000 0x006a0000
+ */
+ #define NSLU2_NUM_FLASH_PARTITIONS 4
+ #define NSLU2_FLASH_PART0_NAME "RedBoot"
+ #define NSLU2_FLASH_PART0_OFFSET 0x00000000
+ #define NSLU2_FLASH_PART0_SIZE 0x00040000
+ #define NSLU2_FLASH_PART1_NAME "System Configuration"
+ #define NSLU2_FLASH_PART1_OFFSET (NSLU2_FLASH_PART0_OFFSET + NSLU2_FLASH_PART0_SIZE)
+ #define NSLU2_FLASH_PART1_SIZE 0x00020000
+ #define NSLU2_FLASH_PART2_NAME "Kernel"
+ #define NSLU2_FLASH_PART2_OFFSET (NSLU2_FLASH_PART1_OFFSET + NSLU2_FLASH_PART1_SIZE)
+ #define NSLU2_FLASH_PART2_SIZE 0x00100000
+ #define NSLU2_FLASH_PART3_NAME "Ramdisk"
+ #define NSLU2_FLASH_PART3_OFFSET (NSLU2_FLASH_PART2_OFFSET + NSLU2_FLASH_PART2_SIZE)
+ #define NSLU2_FLASH_PART3_SIZE 0x006a0000
+
+static int parse_nslu2_partitions(struct mtd_info *master,
+ struct mtd_partition **pparts,
+ unsigned long flash_start)
+{
+ struct mtd_partition *parts;
+ int ret = 0, namelen = 0;
+ char *names;
+
+ namelen = strlen(NSLU2_FLASH_PART0_NAME) +
+ strlen(NSLU2_FLASH_PART1_NAME) +
+ strlen(NSLU2_FLASH_PART2_NAME) +
+ strlen(NSLU2_FLASH_PART3_NAME) +
+ NSLU2_NUM_FLASH_PARTITIONS; /*4 strings + each terminator */
+
+ parts = kmalloc(sizeof(*parts)*NSLU2_NUM_FLASH_PARTITIONS + namelen, GFP_KERNEL);
+ if (!parts) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ memset(parts, 0, sizeof(*parts)*NSLU2_NUM_FLASH_PARTITIONS + namelen);
+ names = (char *)&parts[NSLU2_NUM_FLASH_PARTITIONS];
+
+ /* RedBoot partition */
+ parts[0].size = NSLU2_FLASH_PART0_SIZE;
+ parts[0].offset = NSLU2_FLASH_PART0_OFFSET;
+ parts[0].name = NSLU2_FLASH_PART0_NAME;
+ parts[0].mask_flags = MTD_WRITEABLE; /* readonly */
+ strcpy(names, NSLU2_FLASH_PART0_NAME);
+ names += strlen(names)+1;
+ /* System Configuration */
+ parts[1].size = NSLU2_FLASH_PART1_SIZE;
+ parts[1].offset = NSLU2_FLASH_PART1_OFFSET;
+ parts[1].name = NSLU2_FLASH_PART1_NAME;
+ parts[1].mask_flags = MTD_WRITEABLE; /* readonly */
+ strcpy(names, NSLU2_FLASH_PART1_NAME);
+ names += strlen(names)+1;
+ /* Kernel */
+ parts[2].size = NSLU2_FLASH_PART2_SIZE;
+ parts[2].offset = NSLU2_FLASH_PART2_OFFSET;
+ parts[2].name = NSLU2_FLASH_PART2_NAME;
+ parts[2].mask_flags = MTD_WRITEABLE; /* readonly */
+ strcpy(names, NSLU2_FLASH_PART2_NAME);
+ names += strlen(names)+1;
+ /* Ramdisk */
+ parts[3].size = NSLU2_FLASH_PART3_SIZE;
+ parts[3].offset = NSLU2_FLASH_PART3_OFFSET;
+ parts[3].name = NSLU2_FLASH_PART3_NAME;
+ parts[3].mask_flags = MTD_WRITEABLE; /* readonly */
+ strcpy(names, NSLU2_FLASH_PART3_NAME);
+ names += strlen(names)+1;
+
+ ret = NSLU2_NUM_FLASH_PARTITIONS;
+ *pparts = parts;
+ out:
+ return ret;
+}
+
+static struct mtd_part_parser nslu2_parser = {
+ .owner = THIS_MODULE,
+ .parse_fn = parse_nslu2_partitions,
+ .name = "NSLU2",
+};
+
+static int __init nslu2_parser_init(void)
+{
+ return register_mtd_parser(&nslu2_parser);
+}
+
+static void __exit nslu2_parser_exit(void)
+{
+ deregister_mtd_parser(&nslu2_parser);
+}
+
+module_init(nslu2_parser_init);
+module_exit(nslu2_parser_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mark Rakes");
+MODULE_DESCRIPTION("Parsing code for NSLU2 flash tables");
diff --git a/linux/openslug-kernel-2.6.9/nslu2-pci.c b/linux/openslug-kernel-2.6.9/nslu2-pci.c
index e69de29bb2..7327c65a4f 100644
--- a/linux/openslug-kernel-2.6.9/nslu2-pci.c
+++ b/linux/openslug-kernel-2.6.9/nslu2-pci.c
@@ -0,0 +1,87 @@
+/*
+ * arch/arm/mach-ixp4xx/nslu2-pci.c
+ *
+ * NSLU2 board-level PCI initialization
+ *
+ * based on ixdp425-pci.c:
+ * Copyright (C) 2002 Intel Corporation.
+ * Copyright (C) 2003-2004 MontaVista Software, Inc.
+ *
+ * Maintainer: http://www.nslu2-linux.org/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+// GPIO 8 is used as the power input so is not free for use as a PCI IRQ
+// However, all the common PCI setup code presumes the standard 4 PCI
+// interrupts are available. So we compromise...we don't enable the
+// IRQ on Pin 8 but we let
+
+#include <linux/config.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+
+#include <asm/mach/pci.h>
+#include <asm/irq.h>
+#include <asm/hardware.h>
+#include <asm/mach-types.h>
+
+void __init nslu2_pci_preinit(void)
+{
+ gpio_line_config(NSLU2_PCI_INTA_PIN,
+ IXP4XX_GPIO_IN | IXP4XX_GPIO_ACTIVE_LOW);
+ gpio_line_config(NSLU2_PCI_INTB_PIN,
+ IXP4XX_GPIO_IN | IXP4XX_GPIO_ACTIVE_LOW);
+ gpio_line_config(NSLU2_PCI_INTC_PIN,
+ IXP4XX_GPIO_IN | IXP4XX_GPIO_ACTIVE_LOW);
+// gpio_line_config(NSLU2_PCI_INTD_PIN,
+// IXP4XX_GPIO_IN | IXP4XX_GPIO_ACTIVE_LOW);
+
+ gpio_line_isr_clear(NSLU2_PCI_INTA_PIN);
+ gpio_line_isr_clear(NSLU2_PCI_INTB_PIN);
+ gpio_line_isr_clear(NSLU2_PCI_INTC_PIN);
+// gpio_line_isr_clear(NSLU2_PCI_INTD_PIN);
+
+ ixp4xx_pci_preinit();
+}
+
+static int __init nslu2_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+{
+ static int pci_irq_table[NSLU2_PCI_IRQ_LINES] = {
+ IRQ_NSLU2_PCI_INTA,
+ IRQ_NSLU2_PCI_INTB,
+ IRQ_NSLU2_PCI_INTC,
+// IRQ_NSLU2_PCI_INTD
+ };
+
+ int irq = -1;
+
+ if (slot >= 1 && slot <= NSLU2_PCI_MAX_DEV &&
+ pin >= 1 && pin <= NSLU2_PCI_IRQ_LINES) {
+ irq = pci_irq_table[(slot + pin - 2) % 3]; // ! % 4 kas11
+ }
+
+ return irq;
+}
+
+struct hw_pci __initdata nslu2_pci = {
+ .nr_controllers = 1,
+ .preinit = nslu2_pci_preinit,
+ .swizzle = pci_std_swizzle,
+ .setup = ixp4xx_setup,
+ .scan = ixp4xx_scan_bus,
+ .map_irq = nslu2_map_irq,
+};
+
+int __init nslu2_pci_init(void) //monkey see, monkey do
+{
+ if (machine_is_nslu2())
+ pci_common_init(&nslu2_pci);
+ return 0;
+}
+
+subsys_initcall(nslu2_pci_init);
+
diff --git a/linux/openslug-kernel-2.6.9/nslu2-setup.c b/linux/openslug-kernel-2.6.9/nslu2-setup.c
index e69de29bb2..5698ea9813 100644
--- a/linux/openslug-kernel-2.6.9/nslu2-setup.c
+++ b/linux/openslug-kernel-2.6.9/nslu2-setup.c
@@ -0,0 +1,132 @@
+/*
+ * arch/arm/mach-ixp4xx/nslu2-setup.c
+ *
+ * NSLU2 board-setup
+ *
+ * based ixdp425-setup.c:
+ * Copyright (C) 2003-2004 MontaVista Software, Inc.
+ *
+ * Author: Mark Rakes <mrakes at mac.com>
+ * Maintainers: http://www.nslu2-linux.org/
+ *
+ * Fixed missing init_time in MACHINE_START kas11 10/22/04
+ * Changed to conform to new style __init ixdp425 kas11 10/22/04
+ */
+
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/serial.h>
+#include <linux/tty.h>
+#include <linux/serial_core.h>
+
+#include <asm/types.h>
+#include <asm/setup.h>
+#include <asm/memory.h>
+#include <asm/hardware.h>
+#include <asm/mach-types.h>
+#include <asm/irq.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/flash.h>
+
+#ifdef __ARMEB__
+#define REG_OFFSET 3
+#else
+#define REG_OFFSET 0
+#endif
+
+/*
+ * NSLU2 uses only one serial port
+ */
+static struct uart_port nslu2_serial_ports[] = {
+ {
+ .membase = (char*)(IXP4XX_UART1_BASE_VIRT + REG_OFFSET),
+ .mapbase = (IXP4XX_UART1_BASE_PHYS),
+ .irq = IRQ_IXP4XX_UART1,
+ .flags = UPF_SKIP_TEST,
+ .iotype = UPIO_MEM,
+ .regshift = 2,
+ .uartclk = IXP4XX_UART_XTAL,
+ .line = 0,
+ .type = PORT_XSCALE,
+ .fifosize = 32
+ }
+#if 0
+ , {
+ .membase = (char*)(IXP4XX_UART2_BASE_VIRT + REG_OFFSET),
+ .mapbase = (IXP4XX_UART2_BASE_PHYS),
+ .irq = IRQ_IXP4XX_UART2,
+ .flags = UPF_SKIP_TEST,
+ .iotype = UPIO_MEM,
+ .regshift = 2,
+ .uartclk = IXP4XX_UART_XTAL,
+ .line = 1,
+ .type = PORT_XSCALE,
+ .fifosize = 32
+ }
+#endif
+};
+
+void __init nslu2_map_io(void)
+{
+ early_serial_setup(&nslu2_serial_ports[0]);
+#if 0
+ early_serial_setup(&nslu2_serial_ports[1]);
+#endif
+ ixp4xx_map_io();
+}
+
+static struct flash_platform_data nslu2_flash_data = {
+ .map_name = "cfi_probe",
+ .width = 2,
+};
+
+static struct resource nslu2_flash_resource = {
+ .start = NSLU2_FLASH_BASE,
+ .end = NSLU2_FLASH_BASE + NSLU2_FLASH_SIZE,
+ .flags = IORESOURCE_MEM,
+};
+
+static struct platform_device nslu2_flash = {
+ .name = "IXP4XX-Flash",
+ .id = 0,
+ .dev = {
+ .platform_data = &nslu2_flash_data,
+ },
+ .num_resources = 1,
+ .resource = &nslu2_flash_resource,
+};
+
+static struct ixp4xx_i2c_pins nslu2_i2c_gpio_pins = {
+ .sda_pin = NSLU2_SDA_PIN,
+ .scl_pin = NSLU2_SCL_PIN,
+};
+
+static struct platform_device nslu2_i2c_controller = {
+ .name = "IXP4XX-I2C",
+ .id = 0,
+ .dev = {
+ .platform_data = &nslu2_i2c_gpio_pins,
+ },
+ .num_resources = 0
+};
+
+static struct platform_device *nslu2_devices[] __initdata = {
+ &nslu2_i2c_controller,
+ &nslu2_flash
+};
+
+static void __init nslu2_init(void)
+{
+ platform_add_devices(&nslu2_devices, ARRAY_SIZE(nslu2_devices));
+}
+
+MACHINE_START(NSLU2, "Linksys NSLU2")
+ MAINTAINER("www.nslu2-linux.org")
+ BOOT_MEM(PHYS_OFFSET, IXP4XX_PERIPHERAL_BASE_PHYS,
+ IXP4XX_PERIPHERAL_BASE_VIRT)
+ MAPIO(nslu2_map_io)
+ INITIRQ(ixp4xx_init_irq) //FIXME: all irq are off here
+ INITTIME(ixp4xx_init_time) //this was missing in 2.6.7 code ...soft reboot needed?
+ BOOT_PARAMS(0x0100)
+ INIT_MACHINE(nslu2_init)
+MACHINE_END
diff --git a/linux/openslug-kernel-2.6.9/nslu2.h b/linux/openslug-kernel-2.6.9/nslu2.h
index e69de29bb2..bb79aaa007 100644
--- a/linux/openslug-kernel-2.6.9/nslu2.h
+++ b/linux/openslug-kernel-2.6.9/nslu2.h
@@ -0,0 +1,43 @@
+/*
+ * include/asm-arm/arch-ixp4xx/nslu2.h
+ *
+ * NSLU2 platform specific definitions
+ *
+ * Author: Mark Rakes <mrakes AT mac.com>
+ * Maintainers: http://www.nslu2-linux.org
+ *
+ * based on ixdp425.h:
+ * Copyright 2004 (c) MontaVista, Software, Inc.
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+// GPIO 8 is used as the power input so is not free for use as a PCI IRQ
+// kas11 11-2-04
+
+#ifndef __ASM_ARCH_HARDWARE_H__
+#error "Do not include this directly, instead #include <asm/hardware.h>"
+#endif
+
+#define NSLU2_FLASH_BASE IXP4XX_EXP_BUS_CS0_BASE_PHYS
+#define NSLU2_FLASH_SIZE IXP4XX_EXP_BUS_CSX_REGION_SIZE
+
+#define NSLU2_SDA_PIN 7
+#define NSLU2_SCL_PIN 6
+
+/*
+ * NSLU2 PCI IRQs
+ */
+#define NSLU2_PCI_MAX_DEV 3
+#define NSLU2_PCI_IRQ_LINES 3
+
+
+/* PCI controller GPIO to IRQ pin mappings */
+#define NSLU2_PCI_INTA_PIN 11
+#define NSLU2_PCI_INTB_PIN 10
+#define NSLU2_PCI_INTC_PIN 9
+//#define NSLU2_PCI_INTD_PIN 8
+
+
diff --git a/linux/openslug-kernel-2.6.9/x1205-rtc.c b/linux/openslug-kernel-2.6.9/x1205-rtc.c
index e69de29bb2..c194608ad6 100644
--- a/linux/openslug-kernel-2.6.9/x1205-rtc.c
+++ b/linux/openslug-kernel-2.6.9/x1205-rtc.c
@@ -0,0 +1,548 @@
+/*
+ x1205 - an 12c driver for the Xicor X1205 RTC
+ Copyright 2004 Karen Spearel
+
+ please send all reports to:
+ kas11 at tampabay dot rr dot com
+
+ based on linux/drivers/acron/char/pcf8583.h
+ Copyright (C) 2000 Russell King
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+/*
+
+ * i2c_adapter is the structure used to identify a physical i2c bus along
+ * with the access algorithms necessary to access it.
+
+struct i2c_adapter {
+ struct module *owner;
+ unsigned int id; == is algo->id | hwdep.struct->id, for registered values see below
+ unsigned int class;
+ struct i2c_algorithm *algo; the algorithm to access the bus
+ void *algo_data;
+
+ --- administration stuff.
+ int (*client_register)(struct i2c_client *);
+ int (*client_unregister)(struct i2c_client *);
+
+ data fields that are valid for all devices
+ struct semaphore bus_lock;
+ struct semaphore clist_lock;
+
+ int timeout;
+ int retries;
+ struct device dev; the adapter device
+ struct class_device class_dev; the class device
+
+#ifdef CONFIG_PROC_FS
+ No need to set this when you initialize the adapter
+ int inode;
+#endif def CONFIG_PROC_FS
+
+ int nr;
+ struct list_head clients;
+ struct list_head list;
+ char name[I2C_NAME_SIZE];
+ struct completion dev_released;
+ struct completion class_dev_released;
+};
+*/
+
+
+/*========== Driver for the X1205 on the Linksys NSLU2 ==================*/
+
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/bcd.h>
+#include <linux/rtc.h>
+#include <linux/fs.h>
+#include <linux/proc_fs.h>
+#include <linux/miscdevice.h>
+#include <linux/device.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <linux/moduleparam.h>
+
+#define RTC_GETDATETIME 0
+#define RTC_SETTIME 1
+#define RTC_SETDATETIME 2
+
+#define I2C_M_WR 0 // just for consistancy
+
+// offsets into read buf - add 2 for write buf
+#define CCR_SEC 0
+#define CCR_MIN 1
+#define CCR_HOUR 2
+#define CCR_MDAY 3
+#define CCR_MONTH 4
+#define CCR_YEAR 5
+#define CCR_WDAY 6
+#define CCR_Y2K 7
+
+#define X1205_I2C_BUS_ADDR 0x6f // hardwired into x1205
+#define X1205_ALM0_BASE 0x00 // Base address of the ALM0
+#define X1205_CCR_BASE 0x30 // Base address of the CCR
+#define X1205_SR_ADDR 0x3f // Status Register
+#define X1205_SR_WEL 0x02 // Write Enable Latch bit
+#define X1205_SR_RWEL 0x04 // Register Write Enable Bit
+#define X1205_MILBIT 0x80 // this bit set in ccr.hour for 24 hr mode
+#define NOERR 0
+#define RTC_NODATE 0
+#define RTC_DATETOO 1
+
+// comment out next line is your x1205 can't do page writes
+//#define X1205PAGEWRITE 1
+#ifdef X1205PAGEWRITE
+#define DRIVERNAME "Xicor x1205 RTC Driver v0.9.3.3"
+#else
+#define DRIVERNAME "Xicor x1205 RTC Dvr v0.9.3.3NPW"
+#endif
+
+#define DEBUG KERN_DEBUG
+
+
+static int x1205_get_datetime(struct i2c_client *client, struct rtc_time *tm, u8 reg_base);
+static int x1205_set_datetime(struct i2c_client *client, struct rtc_time *tm, int datetoo, u8 reg_base);
+static int x1205_attach(struct i2c_adapter *adapter);
+static int x1205_detach(struct i2c_client *client);
+static int x1205_validate_tm(struct rtc_time *tm);
+static int x1205_command(struct i2c_client *client, unsigned int cmd, void *arg);
+static int x1205_sync_rtc(void);
+static int x1205_read(struct file *file, char *buf, size_t count, loff_t *ptr);
+static int x1205_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg);
+static int x1205_read_proc(char *buf, char **start, off_t off, int len, int *eof, void *data);
+
+static struct i2c_driver x1205_driver = {
+ .owner = THIS_MODULE,
+ .name = DRIVERNAME,
+ .id = I2C_DRIVERID_X1205,
+ .flags = I2C_DF_NOTIFY,
+ .attach_adapter = &x1205_attach, //we don't need to probe...x1205 is hardwired @ 0x6f
+ .detach_client = &x1205_detach,
+ .command = &x1205_command, //this prolly never gets called...used internally tho
+};
+
+static struct i2c_client x1205_i2c_client = {
+ .id = I2C_DRIVERID_X1205,
+ .flags = 0,
+ .addr = X1205_I2C_BUS_ADDR, // chip address - NOTE: 7bit
+ .adapter = NULL, // the adapter we sit on assigned in attach
+ .driver = &x1205_driver, // and our access routines
+ .usage_count = 0, // How many accesses currently to this client
+ .dev = {}, // the device structure
+ .list = {},
+ .name = DRIVERNAME,
+ .released = {},
+};
+
+static struct file_operations rtc_fops = {
+ owner: THIS_MODULE,
+ ioctl: x1205_ioctl,
+ read: x1205_read,
+};
+
+static struct miscdevice x1205_miscdev = {
+ .minor = RTC_MINOR,
+ .name = "rtc",
+ .fops = &rtc_fops,
+};
+extern int (*set_rtc)(void);
+static unsigned epoch = 1900; //coresponds to year 0
+static unsigned rtc_epoch = 2000;
+static const unsigned char days_in_mo[] =
+{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+
+//===================================CODE======================================
+// in the routines that deal directly with the x1205 hardware, we use
+// rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch
+// Epoch is inited as 2000. Time is set to UT
+//=============================================================================
+static int x1205_get_datetime(struct i2c_client *client, struct rtc_time *tm, u8 reg_base)
+{
+ static unsigned char addr[2] = { 0,} ;
+ unsigned char buf[8];
+ struct i2c_msg msgs[2] = {
+ { client->addr, I2C_M_WR, 2, addr }, //msg 1 = send base address
+ { client->addr, I2C_M_RD, 8, buf }, //msg 2 = read sequential data
+ };
+ addr[1] = reg_base;
+ if ((i2c_transfer(client->adapter, msgs, 2)) == 2) { //did we read 2 messages?
+ printk(KERN_DEBUG "raw x1205 read data - sec-%02x min-%02x hr-%02x mday-%02x mon-%02x year-%02x wday-%02x y2k-%02x\n",
+ buf[0],buf[1],buf[2],buf[3],buf[4],buf[5],buf[6], buf[7]);
+ tm->tm_sec = BCD2BIN(buf[CCR_SEC]);
+ tm->tm_min = BCD2BIN(buf[CCR_MIN]);
+ buf[CCR_HOUR] &= ~X1205_MILBIT;
+ tm->tm_hour = BCD2BIN(buf[CCR_HOUR]); //hr is 0-23
+ tm->tm_mday = BCD2BIN(buf[CCR_MDAY]);
+ tm->tm_mon = BCD2BIN(buf[CCR_MONTH]);
+ rtc_epoch = BCD2BIN(buf[CCR_Y2K]) * 100;
+ tm->tm_year = BCD2BIN(buf[CCR_YEAR]) + rtc_epoch - epoch;
+ tm->tm_wday = buf[CCR_WDAY];
+ printk(KERN_DEBUG "rtc_time output data - sec-%02d min-%02d hr-%02d mday-%02d mon-%02d year-%02d wday-%02d epoch-%d rtc_epoch-%d\n",
+ tm->tm_sec,tm->tm_min,tm->tm_hour,tm->tm_mday,tm->tm_mon,tm->tm_year,tm->tm_wday,epoch, rtc_epoch);
+ } else {
+ printk(KERN_DEBUG "i2c_transfer Read Error\n");
+ return -EIO;
+ }
+
+ return NOERR;
+}
+// x1205pagewrite allows writing a block of registers in msg3 even though the x1205 says
+// nothing about this in its spec.
+// it needs more testing as it is possible some x1205s are actually not-completely-
+// functional x1226s and there is a reason for the multiple write to not be in the spec.
+// anyhow, it is enabled for the time being...and we even push out luck by sending 10 bytes
+
+static int x1205_set_datetime(struct i2c_client *client, struct rtc_time *tm, int datetoo, u8 reg_base)
+{
+ static unsigned char wel[3] = { 0, X1205_SR_ADDR, X1205_SR_WEL };
+ static unsigned char rwel[3] = { 0, X1205_SR_ADDR, X1205_SR_WEL | X1205_SR_RWEL };
+ static unsigned char diswe[3] = { 0, X1205_SR_ADDR, 0 };
+
+#ifdef X1205PAGEWRITE
+
+ static unsigned char buf[10] = { 0, X1205_CCR_BASE, };
+ struct i2c_msg msgs[4] = {
+ { client->addr, I2C_M_WR, 3, wel }, //msg 1 = write WEL to to ccr sr
+ { client->addr, I2C_M_WR, 3, rwel }, //msg 2 = write RWEL to ccr sr
+ { client->addr, I2C_M_WR, 10, buf }, //msg 3 = write ccr base addr +seq data
+ { client->addr, I2C_M_WR, 3, diswe }, //msg 4 = 0 to ccr sr to disable writes
+ };
+
+ msgs[2].len = 5; // 5 bytes + addr to set time only
+ buf [1] = reg_base;
+ buf[CCR_SEC+2] = BIN2BCD(tm->tm_sec);
+ buf[CCR_MIN+2] = BIN2BCD(tm->tm_min);
+ buf[CCR_HOUR+2] = BIN2BCD(tm->tm_hour) | X1205_MILBIT; // set 24 hour format
+ if (datetoo == 1) {
+ buf[CCR_MDAY+2] = BIN2BCD(tm->tm_mday);
+ buf[CCR_MONTH+2] = BIN2BCD(tm->tm_mon); // input is 0-11
+ buf[CCR_YEAR+2] = BIN2BCD((tm->tm_year + epoch - rtc_epoch)); // input is yrs since 1900
+ buf[CCR_WDAY+2] = tm->tm_wday & 7;
+ buf[CCR_Y2K+2] = BIN2BCD((rtc_epoch/100));
+ msgs[2].len += 5; //5 more bytes to set date
+ }
+ printk(KERN_DEBUG "rtc_time input - sec-%02d min-%02d hour-%02d mday-%02d mon-%02d year-%02d wday-%02d epoch-%d rtc_epoch-%d\n",
+ tm->tm_sec,tm->tm_min,tm->tm_hour,tm->tm_mday,tm->tm_mon,tm->tm_year,tm->tm_wday, epoch, rtc_epoch);
+ printk(KERN_DEBUG "BCD write data - sec-%02x min-%02x hour-%02x mday-%02x mon-%02x year-%02x wday-%02x y2k-%02x\n",
+ buf[2],buf[3],buf[4],buf[5],buf[6], buf[7], buf[8], buf[9]);
+
+ if ((i2c_transfer(client->adapter, msgs, 4)) != 4)
+ return -EIO;
+ return NOERR;
+
+#else //do this if page writes aren't working
+
+ int i,xfer;
+ static unsigned char data[3] = { 0,};
+ static unsigned char buf[8];
+
+ buf[CCR_SEC] = BIN2BCD(tm->tm_sec);
+ buf[CCR_MIN] = BIN2BCD(tm->tm_min);
+ buf[CCR_HOUR] = BIN2BCD(tm->tm_hour) | X1205_MILBIT; // set 24 hour format
+ if (datetoo == 1) {
+ buf[CCR_MDAY] = BIN2BCD(tm->tm_mday);
+ buf[CCR_MONTH] = BIN2BCD(tm->tm_mon); // input is 0-11
+ buf[CCR_YEAR] = BIN2BCD((tm->tm_year + epoch - rtc_epoch)); // input is yrs since 1900
+ buf[CCR_WDAY] = tm->tm_wday & 7;
+ buf[CCR_Y2K] = BIN2BCD((rtc_epoch/100));
+ }
+ printk(KERN_DEBUG "rtc_time input - sec-%02d min-%02d hour-%02d mday-%02d mon-%02d year-%02d wday-%02d epoch-%d rtc_epoch-%d\n",
+ tm->tm_sec,tm->tm_min,tm->tm_hour,tm->tm_mday,tm->tm_mon,tm->tm_year,tm->tm_wday, epoch, rtc_epoch);
+
+ xfer = i2c_master_send(client, wel, 3);
+ printk(KERN_DEBUG "wen - %x\n", xfer);
+ if (xfer != 3)
+ return -EIO;
+
+ xfer = i2c_master_send(client, rwel, 3);
+ printk(KERN_DEBUG "wenb - %x\n", xfer);
+ if (xfer != 3)
+ return -EIO;
+
+ for (i = 0; i < 8; i++) {
+ data[1] = i + reg_base;
+ data[2] = buf[i];
+ xfer = i2c_master_send(client, data, 3);
+ printk(KERN_DEBUG "xfer - %d addr - %02x data - %02x\n", xfer, data[1], data[2]);
+ if (xfer != 3)
+ return -EIO;
+ };
+
+ xfer = i2c_master_send(client, diswe, 3);
+ printk(KERN_DEBUG "wdis - %x\n", xfer);
+ if (xfer != 3)
+ return -EIO;
+ return NOERR;
+#endif
+}
+//=============================================================================
+
+static int x1205_attach(struct i2c_adapter *adapter)
+{
+ struct rtc_time tm;
+ struct timespec tv;
+ int errno;
+
+ x1205_i2c_client.adapter = adapter;
+ x1205_i2c_client.id++;
+
+ if ((x1205_get_datetime(&x1205_i2c_client, &tm, X1205_CCR_BASE)) != NOERR) //test for functional driver
+ return -EIO;
+
+ if ((errno = i2c_attach_client(&x1205_i2c_client)) != NOERR)
+ return errno;
+
+ tv.tv_nsec = tm.tm_sec * 10000000;
+ tv.tv_sec = mktime(tm.tm_year+epoch, tm.tm_mon+1, tm.tm_mday, tm.tm_hour,
+ tm.tm_min, tm.tm_sec);
+ do_settimeofday(&tv);
+ set_rtc = x1205_sync_rtc;
+
+ printk(KERN_DEBUG "%s attached on adapter %s\n",x1205_i2c_client.name,
+ x1205_i2c_client.adapter->name); //why is this name a null string?
+
+ return NOERR;
+}
+
+static int x1205_detach(struct i2c_client *client)
+{
+ int errno;
+
+ if ((errno = i2c_detach_client(client)) != 0) {
+ printk(KERN_DEBUG "i2c_detach failed - errno = %d\n", errno);
+ return errno;
+ }
+
+ return NOERR;
+}
+
+// make sure the rtc_time values are in bounds
+static int x1205_validate_tm(struct rtc_time *tm)
+{
+ tm->tm_year += 1900;
+
+ if (tm->tm_year < 1970)
+ return -EINVAL;
+
+ if ((tm->tm_mon > 11) || (tm->tm_mday == 0))
+ return -EINVAL;
+
+ if (tm->tm_mday > (days_in_mo[tm->tm_mon] + ( (tm->tm_mon == 1) &&
+ ((!(tm->tm_year % 4) && (tm->tm_year % 100) ) || !(tm->tm_year % 400)))))
+ return -EINVAL;
+
+ if ((tm->tm_year -= epoch) > 255)
+ return -EINVAL;
+
+ if ((tm->tm_hour >= 24) || (tm->tm_min >= 60) || (tm->tm_sec >= 60))
+ return -EINVAL;
+ return NOERR;
+}
+
+static int x1205_command(struct i2c_client *client, unsigned int cmd, void *tm)
+{
+ int errno, dodate = RTC_DATETOO;
+
+ if (client == NULL || tm == NULL)
+ return -EINVAL;
+ if (!capable(CAP_SYS_TIME))
+ return -EACCES;
+
+ printk(KERN_DEBUG "x1205_command %d\n", cmd);
+
+ switch (cmd) {
+ case RTC_GETDATETIME:
+ return x1205_get_datetime(client, tm, X1205_CCR_BASE);
+
+ case RTC_SETTIME: // note fall thru
+ dodate = RTC_NODATE;
+ case RTC_SETDATETIME:
+ if ((errno = x1205_validate_tm(tm)) < NOERR)
+ return errno;
+ return x1205_set_datetime(client, tm, dodate, X1205_CCR_BASE);
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int x1205_sync_rtc(void)
+{
+ struct rtc_time new_tm, old_tm;
+ unsigned long cur_secs = xtime.tv_sec;
+
+ printk(KERN_DEBUG "x1205_sync_rtc entry\n");
+
+ if (x1205_command(&x1205_i2c_client, RTC_GETDATETIME, &old_tm))
+ return 0;
+
+// xtime.tv_nsec = old_tm.tm_sec * 10000000; //FIXME:
+ new_tm.tm_sec = cur_secs % 60;
+ cur_secs /= 60;
+ new_tm.tm_min = cur_secs % 60;
+ cur_secs /= 60;
+ new_tm.tm_hour = cur_secs % 24;
+
+ /*
+ * avoid writing when we're going to change the day
+ * of the month. We will retry in the next minute.
+ * This basically means that if the RTC must not drift
+ * by more than 1 minute in 11 minutes.
+ */
+ if ((old_tm.tm_hour == 23 && old_tm.tm_min == 59) ||
+ (new_tm.tm_hour == 23 && new_tm.tm_min == 59))
+ return 1;
+ printk(KERN_DEBUG "x1205_sync_rtc exit\n");
+
+ return x1205_command(&x1205_i2c_client, RTC_SETTIME, &new_tm);
+}
+
+static int x1205_read(struct file *file, char *buf, size_t count, loff_t *ptr)
+{
+ struct rtc_time tm;
+
+ if ((x1205_get_datetime(&x1205_i2c_client, &tm, X1205_CCR_BASE)) < NOERR)
+ return -EIO;
+ return copy_to_user(buf, &tm, sizeof(tm)) ? -EFAULT : NOERR;
+}
+
+//==============================================================================
+
+static int x1205_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct rtc_time tm;
+ int errno;
+
+ printk(KERN_DEBUG "ioctl = %x\n", cmd);
+
+ switch (cmd) {
+ case RTC_RD_TIME:
+ if ((x1205_get_datetime(&x1205_i2c_client, &tm, X1205_CCR_BASE)) < NOERR)
+ return -EIO;
+ break;
+
+ case RTC_SET_TIME:
+ if (!capable(CAP_SYS_TIME))
+ return -EACCES;
+
+ if (copy_from_user(&tm, (struct rtc_time *) arg, sizeof(struct rtc_time)))
+ return -EFAULT;
+ if ((errno = x1205_validate_tm(&tm)) < NOERR)
+ return errno;
+ return x1205_set_datetime(&x1205_i2c_client, &tm, RTC_DATETOO, X1205_CCR_BASE);
+
+ case RTC_ALM_SET: //FIXME: set Control Regs
+ if (copy_from_user(&tm, (struct rtc_time *) arg, sizeof(struct rtc_time)))
+ return -EFAULT;
+ return x1205_set_datetime(&x1205_i2c_client, &tm, RTC_DATETOO, X1205_ALM0_BASE);
+
+ case RTC_ALM_READ:
+ if ((x1205_get_datetime(&x1205_i2c_client, &tm, X1205_ALM0_BASE)) < NOERR)
+ return -EIO;
+ break;
+
+ case RTC_EPOCH_READ:
+
+ return put_user (epoch, (unsigned long __user *)arg);
+
+ case RTC_EPOCH_SET:
+ if (arg < 1900)
+ return -EINVAL;
+
+ if (!capable(CAP_SYS_TIME))
+ return -EACCES;
+
+ epoch = arg;
+ return 0;
+
+ default:
+ return -ENOTTY;
+ }
+ return copy_to_user((void __user *)arg, &tm, sizeof tm) ? -EFAULT : 0;
+
+}
+
+static int x1205_read_proc(char *buf, char **start, off_t off, int len, int *eof, void *data)
+{
+ struct rtc_time tm;
+ int slen, errno;
+
+ if ((errno = x1205_get_datetime(&x1205_i2c_client, &tm, X1205_CCR_BASE)) < NOERR)
+ return errno;
+
+// here we return the real year and the month as 1-12 since it is human-readable
+ slen = sprintf(buf, "rtc_time\t: %02d:%02d:%02d\nrtc_date\t: %04d-%02d-%02d\n",
+ tm.tm_hour, tm.tm_min, tm.tm_sec, tm.tm_year + 1900, tm.tm_mon+1, tm.tm_mday);
+ printk(KERN_DEBUG "raw rtc_time\t: %02d:%02d:%02d\nraw rtc_date\t: %04d-%02d-%02d\n",
+ tm.tm_hour, tm.tm_min, tm.tm_sec, tm.tm_year, tm.tm_mon, tm.tm_mday);
+
+ if (slen <= off + len)
+ *eof = 1;
+ *start = buf + off;
+ slen -= off;
+ if (slen > len)
+ slen = len;
+ if ( slen < 0 )
+ slen = 0;
+
+ return slen;
+}
+
+static int __init x1205_init(void)
+{
+ struct rtc_time tm;
+ int errno;
+ printk(KERN_INFO "LOADED %s\n", DRIVERNAME);
+
+ if ((errno = i2c_add_driver(&x1205_driver)) != NOERR) {
+ dev_dbg(x1205_i2c_client.dev, "x1205_init failed - errno = %d\n", errno);
+ return (errno);
+ }
+ if ((errno = misc_register(&x1205_miscdev)) != NOERR) {
+ dev_dbg(x1205_i2c_client.dev, "Register Misc Driver failed - errno = %d\n", errno);
+ i2c_del_driver(&x1205_driver);
+ return errno;
+ }
+ if (create_proc_read_entry("driver/rtc", 0, NULL, x1205_read_proc, NULL) < NOERR)
+ return -ENOMEM;
+ if ((x1205_get_datetime(&x1205_i2c_client, &tm, X1205_CCR_BASE)) != NOERR) //test for functionality
+ return -EIO;
+
+ return NOERR;
+}
+
+static void __exit x1205_exit(void)
+{
+ remove_proc_entry("driver/rtc", NULL);
+ misc_deregister(&x1205_miscdev);
+ i2c_del_driver(&x1205_driver);
+ set_rtc = NULL;
+}
+
+MODULE_AUTHOR("Karen Spearel <kas11@tampabay.rr.com>");
+MODULE_DESCRIPTION("Xicor X1205-RTC Driver");
+MODULE_LICENSE("GPL");
+static int debug = 7;
+module_param(debug, bool, 0644);
+MODULE_PARM_DESC(debug, "Debugging enabled = 1");
+
+module_init(x1205_init);
+module_exit(x1205_exit);
diff --git a/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/1764-1.patch b/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/1764-1.patch
index e69de29bb2..0b660f3521 100644
--- a/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/1764-1.patch
+++ b/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/1764-1.patch
@@ -0,0 +1,16 @@
+__arch_strncpy_from_user needs to be exported if you build the framebuffer console driver as a module.
+
+Cheers,
+
+Ian.
+
+--- linux-2.6-bkpxa.orig/arch/arm/kernel/armksyms.c 2004-02-27 10:35:29.000000000 +0000
++++ linux-2.6-bkpxa/arch/arm/kernel/armksyms.c 2004-02-27 14:55:02.000000000 +0000
+@@ -187,6 +187,7 @@
+ EXPORT_SYMBOL(__arch_copy_to_user);
+ EXPORT_SYMBOL(__arch_clear_user);
+ EXPORT_SYMBOL(__arch_strnlen_user);
++EXPORT_SYMBOL(__arch_strncpy_from_user);
+
+ /* consistent area handling */
+ EXPORT_SYMBOL(consistent_alloc);
diff --git a/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/bluetooth-2.4.18-mh15.patch b/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/bluetooth-2.4.18-mh15.patch
index e69de29bb2..1a7fd98653 100644
--- a/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/bluetooth-2.4.18-mh15.patch
+++ b/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/bluetooth-2.4.18-mh15.patch
@@ -0,0 +1,32759 @@
+diff -urN linux-2.4.18/arch/alpha/config.in linux-2.4.18-mh15/arch/alpha/config.in
+--- linux-2.4.18/arch/alpha/config.in 2001-11-21 00:49:31.000000000 +0100
++++ linux-2.4.18-mh15/arch/alpha/config.in 2004-08-01 16:26:22.000000000 +0200
+@@ -371,9 +371,7 @@
+ source drivers/usb/Config.in
+ source drivers/input/Config.in
+
+-if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+- source net/bluetooth/Config.in
+-fi
++source net/bluetooth/Config.in
+
+ mainmenu_option next_comment
+ comment 'Kernel hacking'
+diff -urN linux-2.4.18/arch/arm/config.in linux-2.4.18-mh15/arch/arm/config.in
+--- linux-2.4.18/arch/arm/config.in 2001-11-09 22:58:02.000000000 +0100
++++ linux-2.4.18-mh15/arch/arm/config.in 2004-08-01 16:26:22.000000000 +0200
+@@ -584,9 +584,7 @@
+
+ source drivers/usb/Config.in
+
+-if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+- source net/bluetooth/Config.in
+-fi
++source net/bluetooth/Config.in
+
+ mainmenu_option next_comment
+ comment 'Kernel hacking'
+diff -urN linux-2.4.18/arch/i386/config.in linux-2.4.18-mh15/arch/i386/config.in
+--- linux-2.4.18/arch/i386/config.in 2002-02-25 20:37:52.000000000 +0100
++++ linux-2.4.18-mh15/arch/i386/config.in 2004-08-01 16:26:22.000000000 +0200
+@@ -407,9 +407,7 @@
+
+ source drivers/usb/Config.in
+
+-if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+- source net/bluetooth/Config.in
+-fi
++source net/bluetooth/Config.in
+
+ mainmenu_option next_comment
+ comment 'Kernel hacking'
+diff -urN linux-2.4.18/arch/ppc/config.in linux-2.4.18-mh15/arch/ppc/config.in
+--- linux-2.4.18/arch/ppc/config.in 2002-02-25 20:37:55.000000000 +0100
++++ linux-2.4.18-mh15/arch/ppc/config.in 2004-08-01 16:26:22.000000000 +0200
+@@ -389,9 +389,7 @@
+
+ source drivers/usb/Config.in
+
+-if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+- source net/bluetooth/Config.in
+-fi
++source net/bluetooth/Config.in
+
+ mainmenu_option next_comment
+ comment 'Kernel hacking'
+diff -urN linux-2.4.18/arch/sparc/config.in linux-2.4.18-mh15/arch/sparc/config.in
+--- linux-2.4.18/arch/sparc/config.in 2001-06-12 04:15:27.000000000 +0200
++++ linux-2.4.18-mh15/arch/sparc/config.in 2004-08-01 16:26:22.000000000 +0200
+@@ -251,9 +251,7 @@
+
+ source fs/Config.in
+
+-if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+- source net/bluetooth/Config.in
+-fi
++source net/bluetooth/Config.in
+
+ mainmenu_option next_comment
+ comment 'Watchdog'
+diff -urN linux-2.4.18/arch/sparc64/config.in linux-2.4.18-mh15/arch/sparc64/config.in
+--- linux-2.4.18/arch/sparc64/config.in 2001-12-21 18:41:53.000000000 +0100
++++ linux-2.4.18-mh15/arch/sparc64/config.in 2004-08-01 16:26:22.000000000 +0200
+@@ -283,9 +283,7 @@
+
+ source drivers/usb/Config.in
+
+-if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+- source net/bluetooth/Config.in
+-fi
++source net/bluetooth/Config.in
+
+ mainmenu_option next_comment
+ comment 'Watchdog'
+diff -urN linux-2.4.18/arch/sparc64/kernel/ioctl32.c linux-2.4.18-mh15/arch/sparc64/kernel/ioctl32.c
+--- linux-2.4.18/arch/sparc64/kernel/ioctl32.c 2002-02-25 20:37:56.000000000 +0100
++++ linux-2.4.18-mh15/arch/sparc64/kernel/ioctl32.c 2004-08-01 16:26:23.000000000 +0200
+@@ -92,6 +92,7 @@
+
+ #include <net/bluetooth/bluetooth.h>
+ #include <net/bluetooth/hci.h>
++#include <net/bluetooth/rfcomm.h>
+
+ #include <linux/usb.h>
+ #include <linux/usbdevice_fs.h>
+@@ -3822,6 +3823,15 @@
+ return err;
+ }
+
++/* Bluetooth ioctls */
++#define HCIUARTSETPROTO _IOW('U', 200, int)
++#define HCIUARTGETPROTO _IOR('U', 201, int)
++
++#define BNEPCONNADD _IOW('B', 200, int)
++#define BNEPCONNDEL _IOW('B', 201, int)
++#define BNEPGETCONNLIST _IOR('B', 210, int)
++#define BNEPGETCONNINFO _IOR('B', 211, int)
++
+ struct mtd_oob_buf32 {
+ u32 start;
+ u32 length;
+@@ -3878,6 +3888,16 @@
+ return ((0 == ret) ? 0 : -EFAULT);
+ }
+
++#define CMTPCONNADD _IOW('C', 200, int)
++#define CMTPCONNDEL _IOW('C', 201, int)
++#define CMTPGETCONNLIST _IOR('C', 210, int)
++#define CMTPGETCONNINFO _IOR('C', 211, int)
++
++#define HIDPCONNADD _IOW('H', 200, int)
++#define HIDPCONNDEL _IOW('H', 201, int)
++#define HIDPGETCONNLIST _IOR('H', 210, int)
++#define HIDPGETCONNINFO _IOR('H', 211, int)
++
+ struct ioctl_trans {
+ unsigned int cmd;
+ unsigned int handler;
+@@ -4540,6 +4560,25 @@
+ COMPATIBLE_IOCTL(HCISETSCAN)
+ COMPATIBLE_IOCTL(HCISETAUTH)
+ COMPATIBLE_IOCTL(HCIINQUIRY)
++COMPATIBLE_IOCTL(HCIUARTSETPROTO)
++COMPATIBLE_IOCTL(HCIUARTGETPROTO)
++COMPATIBLE_IOCTL(RFCOMMCREATEDEV)
++COMPATIBLE_IOCTL(RFCOMMRELEASEDEV)
++COMPATIBLE_IOCTL(RFCOMMGETDEVLIST)
++COMPATIBLE_IOCTL(RFCOMMGETDEVINFO)
++COMPATIBLE_IOCTL(RFCOMMSTEALDLC)
++COMPATIBLE_IOCTL(BNEPCONNADD)
++COMPATIBLE_IOCTL(BNEPCONNDEL)
++COMPATIBLE_IOCTL(BNEPGETCONNLIST)
++COMPATIBLE_IOCTL(BNEPGETCONNINFO)
++COMPATIBLE_IOCTL(CMTPCONNADD)
++COMPATIBLE_IOCTL(CMTPCONNDEL)
++COMPATIBLE_IOCTL(CMTPGETCONNLIST)
++COMPATIBLE_IOCTL(CMTPGETCONNINFO)
++COMPATIBLE_IOCTL(HIDPCONNADD)
++COMPATIBLE_IOCTL(HIDPCONNDEL)
++COMPATIBLE_IOCTL(HIDPGETCONNLIST)
++COMPATIBLE_IOCTL(HIDPGETCONNINFO)
+ /* Misc. */
+ COMPATIBLE_IOCTL(0x41545900) /* ATYIO_CLKR */
+ COMPATIBLE_IOCTL(0x41545901) /* ATYIO_CLKW */
+diff -urN linux-2.4.18/CREDITS linux-2.4.18-mh15/CREDITS
+--- linux-2.4.18/CREDITS 2002-02-25 20:37:50.000000000 +0100
++++ linux-2.4.18-mh15/CREDITS 2004-08-01 16:26:23.000000000 +0200
+@@ -1317,6 +1317,16 @@
+ S: Provo, Utah 84606-5607
+ S: USA
+
++N: Marcel Holtmann
++E: marcel@holtmann.org
++W: http://www.holtmann.org
++D: Maintainer of the Linux Bluetooth Subsystem
++D: Author and maintainer of the various Bluetooth HCI drivers
++D: Author and maintainer of the CAPI message transport protocol driver
++D: Author and maintainer of the Bluetooth HID protocol driver
++D: Various other Bluetooth related patches, cleanups and fixes
++S: Germany
++
+ N: Rob W. W. Hooft
+ E: hooft@EMBL-Heidelberg.DE
+ D: Shared libs for graphics-tools and for the f2c compiler
+@@ -2546,6 +2556,7 @@
+ N: Aristeu Sergio Rozanski Filho
+ E: aris@conectiva.com.br
+ D: Support for EtherExpress 10 ISA (i82595) in eepro driver
++D: User level driver support for input
+ S: Conectiva S.A.
+ S: R. Tocantins, 89 - Cristo Rei
+ S: 80050-430 - Curitiba - Paraná
+diff -urN linux-2.4.18/Documentation/Configure.help linux-2.4.18-mh15/Documentation/Configure.help
+--- linux-2.4.18/Documentation/Configure.help 2002-02-25 20:37:51.000000000 +0100
++++ linux-2.4.18-mh15/Documentation/Configure.help 2004-08-01 16:26:23.000000000 +0200
+@@ -2824,14 +2824,6 @@
+
+ If unsure, say N.
+
+-HCI EMU (virtual device) driver
+-CONFIG_BLUEZ_HCIEMU
+- Bluetooth Virtual HCI device driver.
+- This driver is required if you want to use HCI Emulation software.
+-
+- Say Y here to compile support for Virtual HCI devices into the
+- kernel or say M to compile it as module (hci_usb.o).
+-
+ # Choice: alphatype
+ Alpha system type
+ CONFIG_ALPHA_GENERIC
+@@ -11037,6 +11029,12 @@
+
+ If unsure, say N.
+
++Hotplug firmware loading support (EXPERIMENTAL)
++CONFIG_FW_LOADER
++ This option is provided for the case where no in-kernel-tree modules require
++ hotplug firmware loading support, but a module built outside the kernel tree
++ does.
++
+ Use PCI shared memory for NIC registers
+ CONFIG_TULIP_MMIO
+ Use PCI shared memory for the NIC registers, rather than going through
+@@ -12896,6 +12894,15 @@
+ accessible under char device 13:64+ - /dev/input/eventX in a generic
+ way. This is the future ...
+
++CONFIG_INPUT_UINPUT
++ Say Y here if you want to support user level drivers for input
++ subsystem accessible under char device 10:223 - /dev/input/uinput.
++
++ This driver 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 uinput.o. If you want to compile it as a
++ module, say M here and read <file:Documentation/modules.txt>.
++
+ USB Scanner support
+ CONFIG_USB_SCANNER
+ Say Y here if you want to connect a USB scanner to your computer's
+@@ -19870,19 +19877,22 @@
+ Bluetooth can be found at <http://www.bluetooth.com/>.
+
+ Linux Bluetooth subsystem consist of several layers:
+- HCI Core (device and connection manager, scheduler)
+- HCI Device drivers (interface to the hardware)
+- L2CAP Module (L2CAP protocol)
++ BlueZ Core (HCI device and connection manager, scheduler)
++ HCI Device drivers (Interface to the hardware)
++ SCO Module (SCO audio links)
++ L2CAP Module (Logical Link Control and Adaptation Protocol)
++ RFCOMM Module (RFCOMM Protocol)
++ BNEP Module (Bluetooth Network Encapsulation Protocol)
++ CMTP Module (CAPI Message Transport Protocol)
++ HIDP Module (Human Interface Device Protocol)
+
+- Say Y here to enable Linux Bluetooth support and to build HCI Core
+- layer.
++ Say Y here to compile Bluetooth support into the kernel or say M to
++ compile it as module (bluez.o).
+
+ To use Linux Bluetooth subsystem, you will need several user-space
+ utilities like hciconfig and hcid. These utilities and updates to
+ Bluetooth kernel modules are provided in the BlueZ package.
+- For more information, see <http://bluez.sourceforge.net/>.
+-
+- If you want to compile HCI Core as module (hci.o) say M here.
++ For more information, see <http://www.bluez.org/>.
+
+ L2CAP protocol support
+ CONFIG_BLUEZ_L2CAP
+@@ -19893,15 +19903,96 @@
+ Say Y here to compile L2CAP support into the kernel or say M to
+ compile it as module (l2cap.o).
+
++SCO links support
++CONFIG_BLUEZ_SCO
++ SCO link provides voice transport over Bluetooth. SCO support is
++ required for voice applications like Headset and Audio.
++
++ Say Y here to compile SCO support into the kernel or say M to
++ compile it as module (sco.o).
++
++RFCOMM protocol support
++CONFIG_BLUEZ_RFCOMM
++ RFCOMM provides connection oriented stream transport. RFCOMM
++ support is required for Dialup Networking, OBEX and other Bluetooth
++ applications.
++
++ Say Y here to compile RFCOMM support into the kernel or say M to
++ compile it as module (rfcomm.o).
++
++RFCOMM TTY emulation support
++CONFIG_BLUEZ_RFCOMM_TTY
++ This option enables TTY emulation support for RFCOMM channels.
++
++BNEP protocol support
++CONFIG_BLUEZ_BNEP
++ BNEP (Bluetooth Network Encapsulation Protocol) is Ethernet
++ emulation layer on top of Bluetooth. BNEP is required for
++ Bluetooth PAN (Personal Area Network).
++
++ Say Y here to compile BNEP support into the kernel or say M to
++ compile it as module (bnep.o).
++
++BNEP multicast filter support
++CONFIG_BLUEZ_BNEP_MC_FILTER
++ This option enables the multicast filter support for BNEP.
++
++BNEP protocol filter support
++CONFIG_BLUEZ_BNEP_PROTO_FILTER
++ This option enables the protocol filter support for BNEP.
++
++CMTP protocol support
++CONFIG_BLUEZ_CMTP
++ CMTP (CAPI Message Transport Protocol) is a transport layer
++ for CAPI messages. CMTP is required for the Bluetooth Common
++ ISDN Access Profile.
++
++ Say Y here to compile CMTP support into the kernel or say M to
++ compile it as module (cmtp.o).
++
++HIDP protocol support
++CONFIG_BLUEZ_HIDP
++ HIDP (Human Interface Device Protocol) is a transport layer
++ for HID reports. HIDP is required for the Bluetooth Human
++ Interface Device Profile.
++
++ Say Y here to compile HIDP support into the kernel or say M to
++ compile it as module (hidp.o).
++
+ HCI UART driver
+ CONFIG_BLUEZ_HCIUART
+ Bluetooth HCI UART driver.
+ This driver is required if you want to use Bluetooth devices with
+- serial port interface.
++ serial port interface. You will also need this driver if you have
++ UART based Bluetooth PCMCIA and CF devices like Xircom Credit Card
++ adapter and BrainBoxes Bluetooth PC Card.
+
+ Say Y here to compile support for Bluetooth UART devices into the
+ kernel or say M to compile it as module (hci_uart.o).
+
++HCI UART (H4) protocol support
++CONFIG_BLUEZ_HCIUART_H4
++ UART (H4) is serial protocol for communication between Bluetooth
++ device and host. This protocol is required for most Bluetooth devices
++ with UART interface, including PCMCIA and CF cards.
++
++ Say Y here to compile support for HCI UART (H4) protocol.
++
++HCI BCSP protocol support
++CONFIG_BLUEZ_HCIUART_BCSP
++ BCSP (BlueCore Serial Protocol) is serial protocol for communication
++ between Bluetooth device and host. This protocol is required for non
++ USB Bluetooth devices based on CSR BlueCore chip, including PCMCIA and
++ CF cards.
++
++ Say Y here to compile support for HCI BCSP protocol.
++
++HCI BCSP transmit CRC with every BCSP packet
++CONFIG_BLUEZ_HCIUART_BCSP_TXCRC
++ If you say Y here, a 16-bit CRC checksum will be transmitted along with
++ every BCSP (BlueCore Serial Protocol) packet sent to the Bluetooth chip.
++ This increases reliability, but slightly reduces efficiency.
++
+ HCI USB driver
+ CONFIG_BLUEZ_HCIUSB
+ Bluetooth HCI USB driver.
+@@ -19911,7 +20002,16 @@
+ Say Y here to compile support for Bluetooth USB devices into the
+ kernel or say M to compile it as module (hci_usb.o).
+
+-HCI VHCI virtual HCI device driver
++HCI USB SCO (voice) support
++CONFIG_BLUEZ_HCIUSB_SCO
++ This option enables the SCO support in the HCI USB driver. You need this
++ to transmit voice data with your Bluetooth USB device. And your device
++ must also support sending SCO data over the HCI layer, because some of
++ them sends the SCO data to an internal PCM adapter.
++
++ Say Y here to compile support for HCI SCO data.
++
++HCI VHCI Virtual HCI device driver
+ CONFIG_BLUEZ_HCIVHCI
+ Bluetooth Virtual HCI device driver.
+ This driver is required if you want to use HCI Emulation software.
+@@ -19919,6 +20019,63 @@
+ Say Y here to compile support for virtual HCI devices into the
+ kernel or say M to compile it as module (hci_vhci.o).
+
++HCI BFUSB device driver
++CONFIG_BLUEZ_HCIBFUSB
++ Bluetooth HCI BlueFRITZ! USB driver.
++ This driver provides support for Bluetooth USB devices with AVM
++ interface:
++ AVM BlueFRITZ! USB
++
++ Say Y here to compile support for HCI BFUSB devices into the
++ kernel or say M to compile it as module (bfusb.o).
++
++HCI DTL1 (PC Card) device driver
++CONFIG_BLUEZ_HCIDTL1
++ Bluetooth HCI DTL1 (PC Card) driver.
++ This driver provides support for Bluetooth PCMCIA devices with
++ Nokia DTL1 interface:
++ Nokia Bluetooth Card
++ Socket Bluetooth CF Card
++
++ Say Y here to compile support for HCI DTL1 devices into the
++ kernel or say M to compile it as module (dtl1_cs.o).
++
++HCI BT3C (PC Card) device driver
++CONFIG_BLUEZ_HCIBT3C
++ Bluetooth HCI BT3C (PC Card) driver.
++ This driver provides support for Bluetooth PCMCIA devices with
++ 3Com BT3C interface:
++ 3Com Bluetooth Card (3CRWB6096)
++ HP Bluetooth Card
++
++ Say Y here to compile support for HCI BT3C devices into the
++ kernel or say M to compile it as module (bt3c_cs.o).
++
++HCI BlueCard (PC Card) device driver
++CONFIG_BLUEZ_HCIBLUECARD
++ Bluetooth HCI BlueCard (PC Card) driver.
++ This driver provides support for Bluetooth PCMCIA devices with
++ Anycom BlueCard interface:
++ Anycom Bluetooth PC Card
++ Anycom Bluetooth CF Card
++
++ Say Y here to compile support for HCI BlueCard devices into the
++ kernel or say M to compile it as module (bluecard_cs.o).
++
++HCI UART (PC Card) device driver
++CONFIG_BLUEZ_HCIBTUART
++ Bluetooth HCI UART (PC Card) driver.
++ This driver provides support for Bluetooth PCMCIA devices with
++ an UART interface:
++ Xircom CreditCard Bluetooth Adapter
++ Xircom RealPort2 Bluetooth Adapter
++ Sphinx PICO Card
++ H-Soft blue+Card
++ Cyber-blue Compact Flash Card
++
++ Say Y here to compile support for HCI UART devices into the
++ kernel or say M to compile it as module (btuart_cs.o).
++
+ # The following options are for Linux when running on the Hitachi
+ # SuperH family of RISC microprocessors.
+
+diff -urN linux-2.4.18/Documentation/devices.txt linux-2.4.18-mh15/Documentation/devices.txt
+--- linux-2.4.18/Documentation/devices.txt 2001-11-07 23:46:01.000000000 +0100
++++ linux-2.4.18-mh15/Documentation/devices.txt 2004-08-01 16:26:23.000000000 +0200
+@@ -419,6 +419,7 @@
+ 220 = /dev/mptctl Message passing technology (MPT) control
+ 221 = /dev/mvista/hssdsi Montavista PICMG hot swap system driver
+ 222 = /dev/mvista/hasi Montavista PICMG high availability
++ 223 = /dev/input/uinput User level driver support for input
+ 240-255 Reserved for local use
+
+ 11 char Raw keyboard device
+diff -urN linux-2.4.18/Documentation/firmware_class/firmware_sample_driver.c linux-2.4.18-mh15/Documentation/firmware_class/firmware_sample_driver.c
+--- linux-2.4.18/Documentation/firmware_class/firmware_sample_driver.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/Documentation/firmware_class/firmware_sample_driver.c 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,121 @@
++/*
++ * firmware_sample_driver.c -
++ *
++ * Copyright (c) 2003 Manuel Estrada Sainz <ranty@debian.org>
++ *
++ * Sample code on how to use request_firmware() from drivers.
++ *
++ * Note that register_firmware() is currently useless.
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/string.h>
++
++#include "linux/firmware.h"
++
++#define WE_CAN_NEED_FIRMWARE_BEFORE_USERSPACE_IS_AVAILABLE
++#ifdef WE_CAN_NEED_FIRMWARE_BEFORE_USERSPACE_IS_AVAILABLE
++char __init inkernel_firmware[] = "let's say that this is firmware\n";
++#endif
++
++static char ghost_device[] = "ghost0";
++
++static void sample_firmware_load(char *firmware, int size)
++{
++ u8 buf[size+1];
++ memcpy(buf, firmware, size);
++ buf[size] = '\0';
++ printk("firmware_sample_driver: firmware: %s\n", buf);
++}
++
++static void sample_probe_default(void)
++{
++ /* uses the default method to get the firmware */
++ const struct firmware *fw_entry;
++ printk("firmware_sample_driver: a ghost device got inserted :)\n");
++
++ if(request_firmware(&fw_entry, "sample_driver_fw", ghost_device)!=0)
++ {
++ printk(KERN_ERR
++ "firmware_sample_driver: Firmware not available\n");
++ return;
++ }
++
++ sample_firmware_load(fw_entry->data, fw_entry->size);
++
++ release_firmware(fw_entry);
++
++ /* finish setting up the device */
++}
++static void sample_probe_specific(void)
++{
++ /* Uses some specific hotplug support to get the firmware from
++ * userspace directly into the hardware, or via some sysfs file */
++
++ /* NOTE: This currently doesn't work */
++
++ printk("firmware_sample_driver: a ghost device got inserted :)\n");
++
++ if(request_firmware(NULL, "sample_driver_fw", ghost_device)!=0)
++ {
++ printk(KERN_ERR
++ "firmware_sample_driver: Firmware load failed\n");
++ return;
++ }
++
++ /* request_firmware blocks until userspace finished, so at
++ * this point the firmware should be already in the device */
++
++ /* finish setting up the device */
++}
++static void sample_probe_async_cont(const struct firmware *fw, void *context)
++{
++ if(!fw){
++ printk(KERN_ERR
++ "firmware_sample_driver: firmware load failed\n");
++ return;
++ }
++
++ printk("firmware_sample_driver: device pointer \"%s\"\n",
++ (char *)context);
++ sample_firmware_load(fw->data, fw->size);
++}
++static void sample_probe_async(void)
++{
++ /* Let's say that I can't sleep */
++ int error;
++ error = request_firmware_nowait (THIS_MODULE,
++ "sample_driver_fw", ghost_device,
++ "my device pointer",
++ sample_probe_async_cont);
++ if(error){
++ printk(KERN_ERR
++ "firmware_sample_driver:"
++ " request_firmware_nowait failed\n");
++ }
++}
++
++static int sample_init(void)
++{
++#ifdef WE_CAN_NEED_FIRMWARE_BEFORE_USERSPACE_IS_AVAILABLE
++ register_firmware("sample_driver_fw", inkernel_firmware,
++ sizeof(inkernel_firmware));
++#endif
++ /* since there is no real hardware insertion I just call the
++ * sample probe functions here */
++ sample_probe_specific();
++ sample_probe_default();
++ sample_probe_async();
++ return 0;
++}
++static void __exit sample_exit(void)
++{
++}
++
++module_init (sample_init);
++module_exit (sample_exit);
++
++MODULE_LICENSE("GPL");
+diff -urN linux-2.4.18/Documentation/firmware_class/hotplug-script linux-2.4.18-mh15/Documentation/firmware_class/hotplug-script
+--- linux-2.4.18/Documentation/firmware_class/hotplug-script 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/Documentation/firmware_class/hotplug-script 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,16 @@
++#!/bin/sh
++
++# Simple hotplug script sample:
++#
++# Both $DEVPATH and $FIRMWARE are already provided in the environment.
++
++HOTPLUG_FW_DIR=/usr/lib/hotplug/firmware/
++
++echo 1 > /sysfs/$DEVPATH/loading
++cat $HOTPLUG_FW_DIR/$FIRMWARE > /sysfs/$DEVPATH/data
++echo 0 > /sysfs/$DEVPATH/loading
++
++# To cancel the load in case of error:
++#
++# echo -1 > /sysfs/$DEVPATH/loading
++#
+diff -urN linux-2.4.18/Documentation/firmware_class/README linux-2.4.18-mh15/Documentation/firmware_class/README
+--- linux-2.4.18/Documentation/firmware_class/README 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/Documentation/firmware_class/README 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,58 @@
++
++ request_firmware() hotplug interface:
++ ------------------------------------
++ Copyright (C) 2003 Manuel Estrada Sainz <ranty@debian.org>
++
++ Why:
++ ---
++
++ Today, the most extended way to use firmware in the Linux kernel is linking
++ it statically in a header file. Which has political and technical issues:
++
++ 1) Some firmware is not legal to redistribute.
++ 2) The firmware occupies memory permanently, even though it often is just
++ used once.
++ 3) Some people, like the Debian crowd, don't consider some firmware free
++ enough and remove entire drivers (e.g.: keyspan).
++
++ about in-kernel persistence:
++ ---------------------------
++ Under some circumstances, as explained below, it would be interesting to keep
++ firmware images in non-swappable kernel memory or even in the kernel image
++ (probably within initramfs).
++
++ Note that this functionality has not been implemented.
++
++ - Why OPTIONAL in-kernel persistence may be a good idea sometimes:
++
++ - If the device that needs the firmware is needed to access the
++ filesystem. When upon some error the device has to be reset and the
++ firmware reloaded, it won't be possible to get it from userspace.
++ e.g.:
++ - A diskless client with a network card that needs firmware.
++ - The filesystem is stored in a disk behind an scsi device
++ that needs firmware.
++ - Replacing buggy DSDT/SSDT ACPI tables on boot.
++ Note: this would require the persistent objects to be included
++ within the kernel image, probably within initramfs.
++
++ And the same device can be needed to access the filesystem or not depending
++ on the setup, so I think that the choice on what firmware to make
++ persistent should be left to userspace.
++
++ - Why register_firmware()+__init can be useful:
++ - For boot devices needing firmware.
++ - To make the transition easier:
++ The firmware can be declared __init and register_firmware()
++ called on module_init. Then the firmware is warranted to be
++ there even if "firmware hotplug userspace" is not there yet or
++ it doesn't yet provide the needed firmware.
++ Once the firmware is widely available in userspace, it can be
++ removed from the kernel. Or made optional (CONFIG_.*_FIRMWARE).
++
++ In either case, if firmware hotplug support is there, it can move the
++ firmware out of kernel memory into the real filesystem for later
++ usage.
++
++ Note: If persistence is implemented on top of initramfs,
++ register_firmware() may not be appropriate.
+diff -urN linux-2.4.18/drivers/bluetooth/bfusb.c linux-2.4.18-mh15/drivers/bluetooth/bfusb.c
+--- linux-2.4.18/drivers/bluetooth/bfusb.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/drivers/bluetooth/bfusb.c 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,782 @@
++/*
++ *
++ * AVM BlueFRITZ! USB driver
++ *
++ * Copyright (C) 2003 Marcel Holtmann <marcel@holtmann.org>
++ *
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ *
++ */
++
++#include <linux/config.h>
++#include <linux/module.h>
++
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/slab.h>
++#include <linux/types.h>
++#include <linux/sched.h>
++#include <linux/errno.h>
++#include <linux/skbuff.h>
++
++#include <linux/firmware.h>
++#include <linux/usb.h>
++
++#include <net/bluetooth/bluetooth.h>
++#include <net/bluetooth/hci_core.h>
++
++#ifndef CONFIG_BLUEZ_HCIBFUSB_DEBUG
++#undef BT_DBG
++#define BT_DBG(D...)
++#endif
++
++#define VERSION "1.1"
++
++static struct usb_device_id bfusb_table[] = {
++ /* AVM BlueFRITZ! USB */
++ { USB_DEVICE(0x057c, 0x2200) },
++
++ { } /* Terminating entry */
++};
++
++MODULE_DEVICE_TABLE(usb, bfusb_table);
++
++
++#define BFUSB_MAX_BLOCK_SIZE 256
++
++#define BFUSB_BLOCK_TIMEOUT (HZ * 3)
++
++#define BFUSB_TX_PROCESS 1
++#define BFUSB_TX_WAKEUP 2
++
++#define BFUSB_MAX_BULK_TX 1
++#define BFUSB_MAX_BULK_RX 1
++
++struct bfusb {
++ struct hci_dev hdev;
++
++ unsigned long state;
++
++ struct usb_device *udev;
++
++ unsigned int bulk_in_ep;
++ unsigned int bulk_out_ep;
++ unsigned int bulk_pkt_size;
++
++ rwlock_t lock;
++
++ struct sk_buff_head transmit_q;
++
++ struct sk_buff *reassembly;
++
++ atomic_t pending_tx;
++ struct sk_buff_head pending_q;
++ struct sk_buff_head completed_q;
++};
++
++struct bfusb_scb {
++ struct urb *urb;
++};
++
++static void bfusb_tx_complete(struct urb *urb);
++static void bfusb_rx_complete(struct urb *urb);
++
++static struct urb *bfusb_get_completed(struct bfusb *bfusb)
++{
++ struct sk_buff *skb;
++ struct urb *urb = NULL;
++
++ BT_DBG("bfusb %p", bfusb);
++
++ skb = skb_dequeue(&bfusb->completed_q);
++ if (skb) {
++ urb = ((struct bfusb_scb *) skb->cb)->urb;
++ kfree_skb(skb);
++ }
++
++ return urb;
++}
++
++static inline void bfusb_unlink_urbs(struct bfusb *bfusb)
++{
++ struct sk_buff *skb;
++ struct urb *urb;
++
++ BT_DBG("bfusb %p", bfusb);
++
++ while ((skb = skb_dequeue(&bfusb->pending_q))) {
++ urb = ((struct bfusb_scb *) skb->cb)->urb;
++ usb_unlink_urb(urb);
++ skb_queue_tail(&bfusb->completed_q, skb);
++ }
++
++ while ((urb = bfusb_get_completed(bfusb)))
++ usb_free_urb(urb);
++}
++
++
++static int bfusb_send_bulk(struct bfusb *bfusb, struct sk_buff *skb)
++{
++ struct bfusb_scb *scb = (void *) skb->cb;
++ struct urb *urb = bfusb_get_completed(bfusb);
++ int err, pipe;
++
++ BT_DBG("bfusb %p skb %p len %d", bfusb, skb, skb->len);
++
++ if (!urb && !(urb = usb_alloc_urb(0)))
++ return -ENOMEM;
++
++ pipe = usb_sndbulkpipe(bfusb->udev, bfusb->bulk_out_ep);
++
++ FILL_BULK_URB(urb, bfusb->udev, pipe, skb->data, skb->len,
++ bfusb_tx_complete, skb);
++
++ urb->transfer_flags = USB_QUEUE_BULK;
++
++ scb->urb = urb;
++
++ skb_queue_tail(&bfusb->pending_q, skb);
++
++ err = usb_submit_urb(urb);
++ if (err) {
++ BT_ERR("%s bulk tx submit failed urb %p err %d",
++ bfusb->hdev.name, urb, err);
++ skb_unlink(skb);
++ usb_free_urb(urb);
++ } else
++ atomic_inc(&bfusb->pending_tx);
++
++ return err;
++}
++
++static void bfusb_tx_wakeup(struct bfusb *bfusb)
++{
++ struct sk_buff *skb;
++
++ BT_DBG("bfusb %p", bfusb);
++
++ if (test_and_set_bit(BFUSB_TX_PROCESS, &bfusb->state)) {
++ set_bit(BFUSB_TX_WAKEUP, &bfusb->state);
++ return;
++ }
++
++ do {
++ clear_bit(BFUSB_TX_WAKEUP, &bfusb->state);
++
++ while ((atomic_read(&bfusb->pending_tx) < BFUSB_MAX_BULK_TX) &&
++ (skb = skb_dequeue(&bfusb->transmit_q))) {
++ if (bfusb_send_bulk(bfusb, skb) < 0) {
++ skb_queue_head(&bfusb->transmit_q, skb);
++ break;
++ }
++ }
++
++ } while (test_bit(BFUSB_TX_WAKEUP, &bfusb->state));
++
++ clear_bit(BFUSB_TX_PROCESS, &bfusb->state);
++}
++
++static void bfusb_tx_complete(struct urb *urb)
++{
++ struct sk_buff *skb = (struct sk_buff *) urb->context;
++ struct bfusb *bfusb = (struct bfusb *) skb->dev;
++
++ BT_DBG("bfusb %p urb %p skb %p len %d", bfusb, urb, skb, skb->len);
++
++ atomic_dec(&bfusb->pending_tx);
++
++ if (!test_bit(HCI_RUNNING, &bfusb->hdev.flags))
++ return;
++
++ if (!urb->status)
++ bfusb->hdev.stat.byte_tx += skb->len;
++ else
++ bfusb->hdev.stat.err_tx++;
++
++ read_lock(&bfusb->lock);
++
++ skb_unlink(skb);
++ skb_queue_tail(&bfusb->completed_q, skb);
++
++ bfusb_tx_wakeup(bfusb);
++
++ read_unlock(&bfusb->lock);
++}
++
++
++static int bfusb_rx_submit(struct bfusb *bfusb, struct urb *urb)
++{
++ struct bfusb_scb *scb;
++ struct sk_buff *skb;
++ int err, pipe, size = HCI_MAX_FRAME_SIZE + 32;
++
++ BT_DBG("bfusb %p urb %p", bfusb, urb);
++
++ if (!urb && !(urb = usb_alloc_urb(0)))
++ return -ENOMEM;
++
++ if (!(skb = bluez_skb_alloc(size, GFP_ATOMIC))) {
++ usb_free_urb(urb);
++ return -ENOMEM;
++ }
++
++ skb->dev = (void *) bfusb;
++
++ scb = (struct bfusb_scb *) skb->cb;
++ scb->urb = urb;
++
++ pipe = usb_rcvbulkpipe(bfusb->udev, bfusb->bulk_in_ep);
++
++ FILL_BULK_URB(urb, bfusb->udev, pipe, skb->data, size,
++ bfusb_rx_complete, skb);
++
++ urb->transfer_flags = USB_QUEUE_BULK;
++
++ skb_queue_tail(&bfusb->pending_q, skb);
++
++ err = usb_submit_urb(urb);
++ if (err) {
++ BT_ERR("%s bulk rx submit failed urb %p err %d",
++ bfusb->hdev.name, urb, err);
++ skb_unlink(skb);
++ kfree_skb(skb);
++ usb_free_urb(urb);
++ }
++
++ return err;
++}
++
++static inline int bfusb_recv_block(struct bfusb *bfusb, int hdr, unsigned char *data, int len)
++{
++ BT_DBG("bfusb %p hdr 0x%02x data %p len %d", bfusb, hdr, data, len);
++
++ if (hdr & 0x10) {
++ BT_ERR("%s error in block", bfusb->hdev.name);
++ if (bfusb->reassembly)
++ kfree_skb(bfusb->reassembly);
++ bfusb->reassembly = NULL;
++ return -EIO;
++ }
++
++ if (hdr & 0x04) {
++ struct sk_buff *skb;
++ unsigned char pkt_type;
++ int pkt_len = 0;
++
++ if (bfusb->reassembly) {
++ BT_ERR("%s unexpected start block", bfusb->hdev.name);
++ kfree_skb(bfusb->reassembly);
++ bfusb->reassembly = NULL;
++ }
++
++ if (len < 1) {
++ BT_ERR("%s no packet type found", bfusb->hdev.name);
++ return -EPROTO;
++ }
++
++ pkt_type = *data++; len--;
++
++ switch (pkt_type) {
++ case HCI_EVENT_PKT:
++ if (len >= HCI_EVENT_HDR_SIZE) {
++ hci_event_hdr *hdr = (hci_event_hdr *) data;
++ pkt_len = HCI_EVENT_HDR_SIZE + hdr->plen;
++ } else {
++ BT_ERR("%s event block is too short", bfusb->hdev.name);
++ return -EILSEQ;
++ }
++ break;
++
++ case HCI_ACLDATA_PKT:
++ if (len >= HCI_ACL_HDR_SIZE) {
++ hci_acl_hdr *hdr = (hci_acl_hdr *) data;
++ pkt_len = HCI_ACL_HDR_SIZE + __le16_to_cpu(hdr->dlen);
++ } else {
++ BT_ERR("%s data block is too short", bfusb->hdev.name);
++ return -EILSEQ;
++ }
++ break;
++
++ case HCI_SCODATA_PKT:
++ if (len >= HCI_SCO_HDR_SIZE) {
++ hci_sco_hdr *hdr = (hci_sco_hdr *) data;
++ pkt_len = HCI_SCO_HDR_SIZE + hdr->dlen;
++ } else {
++ BT_ERR("%s audio block is too short", bfusb->hdev.name);
++ return -EILSEQ;
++ }
++ break;
++ }
++
++ skb = bluez_skb_alloc(pkt_len, GFP_ATOMIC);
++ if (!skb) {
++ BT_ERR("%s no memory for the packet", bfusb->hdev.name);
++ return -ENOMEM;
++ }
++
++ skb->dev = (void *) &bfusb->hdev;
++ skb->pkt_type = pkt_type;
++
++ bfusb->reassembly = skb;
++ } else {
++ if (!bfusb->reassembly) {
++ BT_ERR("%s unexpected continuation block", bfusb->hdev.name);
++ return -EIO;
++ }
++ }
++
++ if (len > 0)
++ memcpy(skb_put(bfusb->reassembly, len), data, len);
++
++ if (hdr & 0x08) {
++ hci_recv_frame(bfusb->reassembly);
++ bfusb->reassembly = NULL;
++ }
++
++ return 0;
++}
++
++static void bfusb_rx_complete(struct urb *urb)
++{
++ struct sk_buff *skb = (struct sk_buff *) urb->context;
++ struct bfusb *bfusb = (struct bfusb *) skb->dev;
++ unsigned char *buf = urb->transfer_buffer;
++ int count = urb->actual_length;
++ int err, hdr, len;
++
++ BT_DBG("bfusb %p urb %p skb %p len %d", bfusb, urb, skb, skb->len);
++
++ read_lock(&bfusb->lock);
++
++ if (!test_bit(HCI_RUNNING, &bfusb->hdev.flags))
++ goto unlock;
++
++ if (urb->status || !count)
++ goto resubmit;
++
++ bfusb->hdev.stat.byte_rx += count;
++
++ skb_put(skb, count);
++
++ while (count) {
++ hdr = buf[0] | (buf[1] << 8);
++
++ if (hdr & 0x4000) {
++ len = 0;
++ count -= 2;
++ buf += 2;
++ } else {
++ len = (buf[2] == 0) ? 256 : buf[2];
++ count -= 3;
++ buf += 3;
++ }
++
++ if (count < len) {
++ BT_ERR("%s block extends over URB buffer ranges",
++ bfusb->hdev.name);
++ }
++
++ if ((hdr & 0xe1) == 0xc1)
++ bfusb_recv_block(bfusb, hdr, buf, len);
++
++ count -= len;
++ buf += len;
++ }
++
++ skb_unlink(skb);
++ kfree_skb(skb);
++
++ bfusb_rx_submit(bfusb, urb);
++
++ read_unlock(&bfusb->lock);
++
++ return;
++
++resubmit:
++ urb->dev = bfusb->udev;
++
++ err = usb_submit_urb(urb);
++ if (err) {
++ BT_ERR("%s bulk resubmit failed urb %p err %d",
++ bfusb->hdev.name, urb, err);
++ }
++
++unlock:
++ read_unlock(&bfusb->lock);
++}
++
++
++static int bfusb_open(struct hci_dev *hdev)
++{
++ struct bfusb *bfusb = (struct bfusb *) hdev->driver_data;
++ unsigned long flags;
++ int i, err;
++
++ BT_DBG("hdev %p bfusb %p", hdev, bfusb);
++
++ if (test_and_set_bit(HCI_RUNNING, &hdev->flags))
++ return 0;
++
++ MOD_INC_USE_COUNT;
++
++ write_lock_irqsave(&bfusb->lock, flags);
++
++ err = bfusb_rx_submit(bfusb, NULL);
++ if (!err) {
++ for (i = 1; i < BFUSB_MAX_BULK_RX; i++)
++ bfusb_rx_submit(bfusb, NULL);
++ } else {
++ clear_bit(HCI_RUNNING, &hdev->flags);
++ MOD_DEC_USE_COUNT;
++ }
++
++ write_unlock_irqrestore(&bfusb->lock, flags);
++
++ return err;
++}
++
++static int bfusb_flush(struct hci_dev *hdev)
++{
++ struct bfusb *bfusb = (struct bfusb *) hdev->driver_data;
++
++ BT_DBG("hdev %p bfusb %p", hdev, bfusb);
++
++ skb_queue_purge(&bfusb->transmit_q);
++
++ return 0;
++}
++
++static int bfusb_close(struct hci_dev *hdev)
++{
++ struct bfusb *bfusb = (struct bfusb *) hdev->driver_data;
++ unsigned long flags;
++
++ BT_DBG("hdev %p bfusb %p", hdev, bfusb);
++
++ if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
++ return 0;
++
++ write_lock_irqsave(&bfusb->lock, flags);
++
++ bfusb_unlink_urbs(bfusb);
++ bfusb_flush(hdev);
++
++ write_unlock_irqrestore(&bfusb->lock, flags);
++
++ MOD_DEC_USE_COUNT;
++
++ return 0;
++}
++
++static int bfusb_send_frame(struct sk_buff *skb)
++{
++ struct hci_dev *hdev = (struct hci_dev *) skb->dev;
++ struct bfusb *bfusb;
++ struct sk_buff *nskb;
++ unsigned char buf[3];
++ int sent = 0, size, count;
++
++ BT_DBG("hdev %p skb %p type %d len %d", hdev, skb, skb->pkt_type, skb->len);
++
++ if (!hdev) {
++ BT_ERR("Frame for unknown HCI device (hdev=NULL)");
++ return -ENODEV;
++ }
++
++ if (!test_bit(HCI_RUNNING, &hdev->flags))
++ return -EBUSY;
++
++ bfusb = (struct bfusb *) hdev->driver_data;
++
++ switch (skb->pkt_type) {
++ case HCI_COMMAND_PKT:
++ hdev->stat.cmd_tx++;
++ break;
++ case HCI_ACLDATA_PKT:
++ hdev->stat.acl_tx++;
++ break;
++ case HCI_SCODATA_PKT:
++ hdev->stat.sco_tx++;
++ break;
++ };
++
++ /* Prepend skb with frame type */
++ memcpy(skb_push(skb, 1), &(skb->pkt_type), 1);
++
++ count = skb->len;
++
++ /* Max HCI frame size seems to be 1511 + 1 */
++ if (!(nskb = bluez_skb_alloc(count + 32, GFP_ATOMIC))) {
++ BT_ERR("Can't allocate memory for new packet");
++ return -ENOMEM;
++ }
++
++ nskb->dev = (void *) bfusb;
++
++ while (count) {
++ size = min_t(uint, count, BFUSB_MAX_BLOCK_SIZE);
++
++ buf[0] = 0xc1 | ((sent == 0) ? 0x04 : 0) | ((count == size) ? 0x08 : 0);
++ buf[1] = 0x00;
++ buf[2] = (size == BFUSB_MAX_BLOCK_SIZE) ? 0 : size;
++
++ memcpy(skb_put(nskb, 3), buf, 3);
++ memcpy(skb_put(nskb, size), skb->data + sent, size);
++
++ sent += size;
++ count -= size;
++ }
++
++ /* Don't send frame with multiple size of bulk max packet */
++ if ((nskb->len % bfusb->bulk_pkt_size) == 0) {
++ buf[0] = 0xdd;
++ buf[1] = 0x00;
++ memcpy(skb_put(nskb, 2), buf, 2);
++ }
++
++ read_lock(&bfusb->lock);
++
++ skb_queue_tail(&bfusb->transmit_q, nskb);
++ bfusb_tx_wakeup(bfusb);
++
++ read_unlock(&bfusb->lock);
++
++ kfree_skb(skb);
++
++ return 0;
++}
++
++static void bfusb_destruct(struct hci_dev *hdev)
++{
++ struct bfusb *bfusb = (struct bfusb *) hdev->driver_data;
++
++ BT_DBG("hdev %p bfusb %p", hdev, bfusb);
++
++ kfree(bfusb);
++}
++
++static int bfusb_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg)
++{
++ return -ENOIOCTLCMD;
++}
++
++
++static int bfusb_load_firmware(struct bfusb *bfusb, unsigned char *firmware, int count)
++{
++ unsigned char *buf;
++ int err, pipe, len, size, sent = 0;
++
++ BT_DBG("bfusb %p udev %p firmware %p count %d", bfusb, bfusb->udev, firmware, count);
++
++ BT_INFO("BlueFRITZ! USB loading firmware");
++
++ if (usb_set_configuration(bfusb->udev, 1) < 0) {
++ BT_ERR("Can't change to loading configuration");
++ return -EBUSY;
++ }
++
++ buf = kmalloc(BFUSB_MAX_BLOCK_SIZE + 3, GFP_ATOMIC);
++ if (!buf) {
++ BT_ERR("Can't allocate memory chunk for firmware");
++ return -ENOMEM;
++ }
++
++ pipe = usb_sndbulkpipe(bfusb->udev, bfusb->bulk_out_ep);
++
++ while (count) {
++ size = min_t(uint, count, BFUSB_MAX_BLOCK_SIZE + 3);
++
++ memcpy(buf, firmware + sent, size);
++
++ err = usb_bulk_msg(bfusb->udev, pipe, buf, size,
++ &len, BFUSB_BLOCK_TIMEOUT);
++
++ if (err || (len != size)) {
++ BT_ERR("Error in firmware loading");
++ goto error;
++ }
++
++ sent += size;
++ count -= size;
++ }
++
++ if ((err = usb_bulk_msg(bfusb->udev, pipe, NULL, 0,
++ &len, BFUSB_BLOCK_TIMEOUT)) < 0) {
++ BT_ERR("Error in null packet request");
++ goto error;
++ }
++
++ if ((err = usb_set_configuration(bfusb->udev, 2)) < 0) {
++ BT_ERR("Can't change to running configuration");
++ goto error;
++ }
++
++ BT_INFO("BlueFRITZ! USB device ready");
++
++ kfree(buf);
++ return 0;
++
++error:
++ kfree(buf);
++
++ pipe = usb_sndctrlpipe(bfusb->udev, 0);
++
++ usb_control_msg(bfusb->udev, pipe, USB_REQ_SET_CONFIGURATION,
++ 0, 0, 0, NULL, 0, BFUSB_BLOCK_TIMEOUT);
++
++ return err;
++}
++
++static void *bfusb_probe(struct usb_device *udev, unsigned int ifnum, const struct usb_device_id *id)
++{
++ const struct firmware *firmware;
++ char device[16];
++ struct usb_interface *iface;
++ struct usb_interface_descriptor *iface_desc;
++ struct usb_endpoint_descriptor *bulk_out_ep;
++ struct usb_endpoint_descriptor *bulk_in_ep;
++ struct hci_dev *hdev;
++ struct bfusb *bfusb;
++
++ BT_DBG("udev %p ifnum %d id %p", udev, ifnum, id);
++
++ /* Check number of endpoints */
++ iface = &udev->actconfig->interface[0];
++ iface_desc = &iface->altsetting[0];
++
++ if (iface_desc->bNumEndpoints < 2)
++ return NULL;
++
++ bulk_out_ep = &iface_desc->endpoint[0];
++ bulk_in_ep = &iface_desc->endpoint[1];
++
++ if (!bulk_out_ep || !bulk_in_ep) {
++ BT_ERR("Bulk endpoints not found");
++ goto done;
++ }
++
++ /* Initialize control structure and load firmware */
++ if (!(bfusb = kmalloc(sizeof(struct bfusb), GFP_KERNEL))) {
++ BT_ERR("Can't allocate memory for control structure");
++ goto done;
++ }
++
++ memset(bfusb, 0, sizeof(struct bfusb));
++
++ bfusb->udev = udev;
++ bfusb->bulk_in_ep = bulk_in_ep->bEndpointAddress;
++ bfusb->bulk_out_ep = bulk_out_ep->bEndpointAddress;
++ bfusb->bulk_pkt_size = bulk_out_ep->wMaxPacketSize;
++
++ bfusb->lock = RW_LOCK_UNLOCKED;
++
++ bfusb->reassembly = NULL;
++
++ skb_queue_head_init(&bfusb->transmit_q);
++ skb_queue_head_init(&bfusb->pending_q);
++ skb_queue_head_init(&bfusb->completed_q);
++
++ snprintf(device, sizeof(device), "bfusb%3.3d%3.3d", udev->bus->busnum, udev->devnum);
++
++ if (request_firmware(&firmware, "bfubase.frm", device) < 0) {
++ BT_ERR("Firmware request failed");
++ goto error;
++ }
++
++ if (bfusb_load_firmware(bfusb, firmware->data, firmware->size) < 0) {
++ BT_ERR("Firmware loading failed");
++ goto release;
++ }
++
++ release_firmware(firmware);
++
++ /* Initialize and register HCI device */
++ hdev = &bfusb->hdev;
++
++ hdev->type = HCI_USB;
++ hdev->driver_data = bfusb;
++
++ hdev->open = bfusb_open;
++ hdev->close = bfusb_close;
++ hdev->flush = bfusb_flush;
++ hdev->send = bfusb_send_frame;
++ hdev->destruct = bfusb_destruct;
++ hdev->ioctl = bfusb_ioctl;
++
++ if (hci_register_dev(hdev) < 0) {
++ BT_ERR("Can't register HCI device");
++ goto error;
++ }
++
++ return bfusb;
++
++release:
++ release_firmware(firmware);
++
++error:
++ kfree(bfusb);
++
++done:
++ return NULL;
++}
++
++static void bfusb_disconnect(struct usb_device *udev, void *ptr)
++{
++ struct bfusb *bfusb = (struct bfusb *) ptr;
++ struct hci_dev *hdev = &bfusb->hdev;
++
++ BT_DBG("udev %p ptr %p", udev, ptr);
++
++ if (!hdev)
++ return;
++
++ bfusb_close(hdev);
++
++ if (hci_unregister_dev(hdev) < 0)
++ BT_ERR("Can't unregister HCI device %s", hdev->name);
++}
++
++static struct usb_driver bfusb_driver = {
++ name: "bfusb",
++ probe: bfusb_probe,
++ disconnect: bfusb_disconnect,
++ id_table: bfusb_table,
++};
++
++static int __init bfusb_init(void)
++{
++ int err;
++
++ BT_INFO("BlueFRITZ! USB driver ver %s", VERSION);
++ BT_INFO("Copyright (C) 2003 Marcel Holtmann <marcel@holtmann.org>");
++
++ if ((err = usb_register(&bfusb_driver)) < 0)
++ BT_ERR("Failed to register BlueFRITZ! USB driver");
++
++ return err;
++}
++
++static void __exit bfusb_cleanup(void)
++{
++ usb_deregister(&bfusb_driver);
++}
++
++module_init(bfusb_init);
++module_exit(bfusb_cleanup);
++
++MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
++MODULE_DESCRIPTION("BlueFRITZ! USB driver ver " VERSION);
++MODULE_LICENSE("GPL");
+diff -urN linux-2.4.18/drivers/bluetooth/bluecard_cs.c linux-2.4.18-mh15/drivers/bluetooth/bluecard_cs.c
+--- linux-2.4.18/drivers/bluetooth/bluecard_cs.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/drivers/bluetooth/bluecard_cs.c 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,1116 @@
++/*
++ *
++ * Bluetooth driver for the Anycom BlueCard (LSE039/LSE041)
++ *
++ * Copyright (C) 2001-2002 Marcel Holtmann <marcel@holtmann.org>
++ *
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation;
++ *
++ * Software distributed under the License is distributed on an "AS
++ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
++ * implied. See the License for the specific language governing
++ * rights and limitations under the License.
++ *
++ * The initial developer of the original code is David A. Hinds
++ * <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
++ * are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
++ *
++ */
++
++#include <linux/config.h>
++#include <linux/module.h>
++
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/slab.h>
++#include <linux/types.h>
++#include <linux/sched.h>
++#include <linux/timer.h>
++#include <linux/errno.h>
++#include <linux/ptrace.h>
++#include <linux/ioport.h>
++#include <linux/spinlock.h>
++#include <linux/skbuff.h>
++#include <asm/io.h>
++
++#include <pcmcia/version.h>
++#include <pcmcia/cs_types.h>
++#include <pcmcia/cs.h>
++#include <pcmcia/cistpl.h>
++#include <pcmcia/ciscode.h>
++#include <pcmcia/ds.h>
++#include <pcmcia/cisreg.h>
++
++#include <net/bluetooth/bluetooth.h>
++#include <net/bluetooth/hci_core.h>
++
++
++
++/* ======================== Module parameters ======================== */
++
++
++/* Bit map of interrupts to choose from */
++static u_int irq_mask = 0x86bc;
++static int irq_list[4] = { -1 };
++
++MODULE_PARM(irq_mask, "i");
++MODULE_PARM(irq_list, "1-4i");
++
++MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
++MODULE_DESCRIPTION("BlueZ driver for the Anycom BlueCard (LSE039/LSE041)");
++MODULE_LICENSE("GPL");
++
++
++
++/* ======================== Local structures ======================== */
++
++
++typedef struct bluecard_info_t {
++ dev_link_t link;
++ dev_node_t node;
++
++ struct hci_dev hdev;
++
++ spinlock_t lock; /* For serializing operations */
++ struct timer_list timer; /* For LED control */
++
++ struct sk_buff_head txq;
++ unsigned long tx_state;
++
++ unsigned long rx_state;
++ unsigned long rx_count;
++ struct sk_buff *rx_skb;
++
++ unsigned char ctrl_reg;
++ unsigned long hw_state; /* Status of the hardware and LED control */
++} bluecard_info_t;
++
++
++void bluecard_config(dev_link_t *link);
++void bluecard_release(u_long arg);
++int bluecard_event(event_t event, int priority, event_callback_args_t *args);
++
++static dev_info_t dev_info = "bluecard_cs";
++
++dev_link_t *bluecard_attach(void);
++void bluecard_detach(dev_link_t *);
++
++static dev_link_t *dev_list = NULL;
++
++
++/* Default baud rate: 57600, 115200, 230400 or 460800 */
++#define DEFAULT_BAUD_RATE 230400
++
++
++/* Hardware states */
++#define CARD_READY 1
++#define CARD_HAS_PCCARD_ID 4
++#define CARD_HAS_POWER_LED 5
++#define CARD_HAS_ACTIVITY_LED 6
++
++/* Transmit states */
++#define XMIT_SENDING 1
++#define XMIT_WAKEUP 2
++#define XMIT_BUFFER_NUMBER 5 /* unset = buffer one, set = buffer two */
++#define XMIT_BUF_ONE_READY 6
++#define XMIT_BUF_TWO_READY 7
++#define XMIT_SENDING_READY 8
++
++/* Receiver states */
++#define RECV_WAIT_PACKET_TYPE 0
++#define RECV_WAIT_EVENT_HEADER 1
++#define RECV_WAIT_ACL_HEADER 2
++#define RECV_WAIT_SCO_HEADER 3
++#define RECV_WAIT_DATA 4
++
++/* Special packet types */
++#define PKT_BAUD_RATE_57600 0x80
++#define PKT_BAUD_RATE_115200 0x81
++#define PKT_BAUD_RATE_230400 0x82
++#define PKT_BAUD_RATE_460800 0x83
++
++
++/* These are the register offsets */
++#define REG_COMMAND 0x20
++#define REG_INTERRUPT 0x21
++#define REG_CONTROL 0x22
++#define REG_RX_CONTROL 0x24
++#define REG_CARD_RESET 0x30
++#define REG_LED_CTRL 0x30
++
++/* REG_COMMAND */
++#define REG_COMMAND_TX_BUF_ONE 0x01
++#define REG_COMMAND_TX_BUF_TWO 0x02
++#define REG_COMMAND_RX_BUF_ONE 0x04
++#define REG_COMMAND_RX_BUF_TWO 0x08
++#define REG_COMMAND_RX_WIN_ONE 0x00
++#define REG_COMMAND_RX_WIN_TWO 0x10
++
++/* REG_CONTROL */
++#define REG_CONTROL_BAUD_RATE_57600 0x00
++#define REG_CONTROL_BAUD_RATE_115200 0x01
++#define REG_CONTROL_BAUD_RATE_230400 0x02
++#define REG_CONTROL_BAUD_RATE_460800 0x03
++#define REG_CONTROL_RTS 0x04
++#define REG_CONTROL_BT_ON 0x08
++#define REG_CONTROL_BT_RESET 0x10
++#define REG_CONTROL_BT_RES_PU 0x20
++#define REG_CONTROL_INTERRUPT 0x40
++#define REG_CONTROL_CARD_RESET 0x80
++
++/* REG_RX_CONTROL */
++#define RTS_LEVEL_SHIFT_BITS 0x02
++
++
++
++/* ======================== LED handling routines ======================== */
++
++
++void bluecard_activity_led_timeout(u_long arg)
++{
++ bluecard_info_t *info = (bluecard_info_t *)arg;
++ unsigned int iobase = info->link.io.BasePort1;
++
++ if (test_bit(CARD_HAS_ACTIVITY_LED, &(info->hw_state))) {
++ /* Disable activity LED */
++ outb(0x08 | 0x20, iobase + 0x30);
++ } else {
++ /* Disable power LED */
++ outb(0x00, iobase + 0x30);
++ }
++}
++
++
++static void bluecard_enable_activity_led(bluecard_info_t *info)
++{
++ unsigned int iobase = info->link.io.BasePort1;
++
++ if (test_bit(CARD_HAS_ACTIVITY_LED, &(info->hw_state))) {
++ /* Enable activity LED */
++ outb(0x10 | 0x40, iobase + 0x30);
++
++ /* Stop the LED after HZ/4 */
++ mod_timer(&(info->timer), jiffies + HZ / 4);
++ } else {
++ /* Enable power LED */
++ outb(0x08 | 0x20, iobase + 0x30);
++
++ /* Stop the LED after HZ/2 */
++ mod_timer(&(info->timer), jiffies + HZ / 2);
++ }
++}
++
++
++
++/* ======================== Interrupt handling ======================== */
++
++
++static int bluecard_write(unsigned int iobase, unsigned int offset, __u8 *buf, int len)
++{
++ int i, actual;
++
++ actual = (len > 15) ? 15 : len;
++
++ outb_p(actual, iobase + offset);
++
++ for (i = 0; i < actual; i++)
++ outb_p(buf[i], iobase + offset + i + 1);
++
++ return actual;
++}
++
++
++static void bluecard_write_wakeup(bluecard_info_t *info)
++{
++ if (!info) {
++ printk(KERN_WARNING "bluecard_cs: Call of write_wakeup for unknown device.\n");
++ return;
++ }
++
++ if (!test_bit(XMIT_SENDING_READY, &(info->tx_state)))
++ return;
++
++ if (test_and_set_bit(XMIT_SENDING, &(info->tx_state))) {
++ set_bit(XMIT_WAKEUP, &(info->tx_state));
++ return;
++ }
++
++ do {
++ register unsigned int iobase = info->link.io.BasePort1;
++ register unsigned int offset;
++ register unsigned char command;
++ register unsigned long ready_bit;
++ register struct sk_buff *skb;
++ register int len;
++
++ clear_bit(XMIT_WAKEUP, &(info->tx_state));
++
++ if (!(info->link.state & DEV_PRESENT))
++ return;
++
++ if (test_bit(XMIT_BUFFER_NUMBER, &(info->tx_state))) {
++ if (!test_bit(XMIT_BUF_TWO_READY, &(info->tx_state)))
++ break;
++ offset = 0x10;
++ command = REG_COMMAND_TX_BUF_TWO;
++ ready_bit = XMIT_BUF_TWO_READY;
++ } else {
++ if (!test_bit(XMIT_BUF_ONE_READY, &(info->tx_state)))
++ break;
++ offset = 0x00;
++ command = REG_COMMAND_TX_BUF_ONE;
++ ready_bit = XMIT_BUF_ONE_READY;
++ }
++
++ if (!(skb = skb_dequeue(&(info->txq))))
++ break;
++
++ if (skb->pkt_type & 0x80) {
++ /* Disable RTS */
++ info->ctrl_reg |= REG_CONTROL_RTS;
++ outb(info->ctrl_reg, iobase + REG_CONTROL);
++ }
++
++ /* Activate LED */
++ bluecard_enable_activity_led(info);
++
++ /* Send frame */
++ len = bluecard_write(iobase, offset, skb->data, skb->len);
++
++ /* Tell the FPGA to send the data */
++ outb_p(command, iobase + REG_COMMAND);
++
++ /* Mark the buffer as dirty */
++ clear_bit(ready_bit, &(info->tx_state));
++
++ if (skb->pkt_type & 0x80) {
++
++ wait_queue_head_t wait;
++ unsigned char baud_reg;
++
++ switch (skb->pkt_type) {
++ case PKT_BAUD_RATE_460800:
++ baud_reg = REG_CONTROL_BAUD_RATE_460800;
++ break;
++ case PKT_BAUD_RATE_230400:
++ baud_reg = REG_CONTROL_BAUD_RATE_230400;
++ break;
++ case PKT_BAUD_RATE_115200:
++ baud_reg = REG_CONTROL_BAUD_RATE_115200;
++ break;
++ case PKT_BAUD_RATE_57600:
++ /* Fall through... */
++ default:
++ baud_reg = REG_CONTROL_BAUD_RATE_57600;
++ break;
++ }
++
++ /* Wait until the command reaches the baseband */
++ init_waitqueue_head(&wait);
++ interruptible_sleep_on_timeout(&wait, HZ / 10);
++
++ /* Set baud on baseband */
++ info->ctrl_reg &= ~0x03;
++ info->ctrl_reg |= baud_reg;
++ outb(info->ctrl_reg, iobase + REG_CONTROL);
++
++ /* Enable RTS */
++ info->ctrl_reg &= ~REG_CONTROL_RTS;
++ outb(info->ctrl_reg, iobase + REG_CONTROL);
++
++ /* Wait before the next HCI packet can be send */
++ interruptible_sleep_on_timeout(&wait, HZ);
++
++ }
++
++ if (len == skb->len) {
++ kfree_skb(skb);
++ } else {
++ skb_pull(skb, len);
++ skb_queue_head(&(info->txq), skb);
++ }
++
++ info->hdev.stat.byte_tx += len;
++
++ /* Change buffer */
++ change_bit(XMIT_BUFFER_NUMBER, &(info->tx_state));
++
++ } while (test_bit(XMIT_WAKEUP, &(info->tx_state)));
++
++ clear_bit(XMIT_SENDING, &(info->tx_state));
++}
++
++
++static int bluecard_read(unsigned int iobase, unsigned int offset, __u8 *buf, int size)
++{
++ int i, n, len;
++
++ outb(REG_COMMAND_RX_WIN_ONE, iobase + REG_COMMAND);
++
++ len = inb(iobase + offset);
++ n = 0;
++ i = 1;
++
++ while (n < len) {
++
++ if (i == 16) {
++ outb(REG_COMMAND_RX_WIN_TWO, iobase + REG_COMMAND);
++ i = 0;
++ }
++
++ buf[n] = inb(iobase + offset + i);
++
++ n++;
++ i++;
++
++ }
++
++ return len;
++}
++
++
++static void bluecard_receive(bluecard_info_t *info, unsigned int offset)
++{
++ unsigned int iobase;
++ unsigned char buf[31];
++ int i, len;
++
++ if (!info) {
++ printk(KERN_WARNING "bluecard_cs: Call of receive for unknown device.\n");
++ return;
++ }
++
++ iobase = info->link.io.BasePort1;
++
++ if (test_bit(XMIT_SENDING_READY, &(info->tx_state)))
++ bluecard_enable_activity_led(info);
++
++ len = bluecard_read(iobase, offset, buf, sizeof(buf));
++
++ for (i = 0; i < len; i++) {
++
++ /* Allocate packet */
++ if (info->rx_skb == NULL) {
++ info->rx_state = RECV_WAIT_PACKET_TYPE;
++ info->rx_count = 0;
++ if (!(info->rx_skb = bluez_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC))) {
++ printk(KERN_WARNING "bluecard_cs: Can't allocate mem for new packet.\n");
++ return;
++ }
++ }
++
++ if (info->rx_state == RECV_WAIT_PACKET_TYPE) {
++
++ info->rx_skb->dev = (void *)&(info->hdev);
++ info->rx_skb->pkt_type = buf[i];
++
++ switch (info->rx_skb->pkt_type) {
++
++ case 0x00:
++ /* init packet */
++ if (offset != 0x00) {
++ set_bit(XMIT_BUF_ONE_READY, &(info->tx_state));
++ set_bit(XMIT_BUF_TWO_READY, &(info->tx_state));
++ set_bit(XMIT_SENDING_READY, &(info->tx_state));
++ bluecard_write_wakeup(info);
++ }
++
++ kfree_skb(info->rx_skb);
++ info->rx_skb = NULL;
++ break;
++
++ case HCI_EVENT_PKT:
++ info->rx_state = RECV_WAIT_EVENT_HEADER;
++ info->rx_count = HCI_EVENT_HDR_SIZE;
++ break;
++
++ case HCI_ACLDATA_PKT:
++ info->rx_state = RECV_WAIT_ACL_HEADER;
++ info->rx_count = HCI_ACL_HDR_SIZE;
++ break;
++
++ case HCI_SCODATA_PKT:
++ info->rx_state = RECV_WAIT_SCO_HEADER;
++ info->rx_count = HCI_SCO_HDR_SIZE;
++ break;
++
++ default:
++ /* unknown packet */
++ printk(KERN_WARNING "bluecard_cs: Unknown HCI packet with type 0x%02x received.\n", info->rx_skb->pkt_type);
++ info->hdev.stat.err_rx++;
++
++ kfree_skb(info->rx_skb);
++ info->rx_skb = NULL;
++ break;
++
++ }
++
++ } else {
++
++ *skb_put(info->rx_skb, 1) = buf[i];
++ info->rx_count--;
++
++ if (info->rx_count == 0) {
++
++ int dlen;
++ hci_event_hdr *eh;
++ hci_acl_hdr *ah;
++ hci_sco_hdr *sh;
++
++ switch (info->rx_state) {
++
++ case RECV_WAIT_EVENT_HEADER:
++ eh = (hci_event_hdr *)(info->rx_skb->data);
++ info->rx_state = RECV_WAIT_DATA;
++ info->rx_count = eh->plen;
++ break;
++
++ case RECV_WAIT_ACL_HEADER:
++ ah = (hci_acl_hdr *)(info->rx_skb->data);
++ dlen = __le16_to_cpu(ah->dlen);
++ info->rx_state = RECV_WAIT_DATA;
++ info->rx_count = dlen;
++ break;
++
++ case RECV_WAIT_SCO_HEADER:
++ sh = (hci_sco_hdr *)(info->rx_skb->data);
++ info->rx_state = RECV_WAIT_DATA;
++ info->rx_count = sh->dlen;
++ break;
++
++ case RECV_WAIT_DATA:
++ hci_recv_frame(info->rx_skb);
++ info->rx_skb = NULL;
++ break;
++
++ }
++
++ }
++
++ }
++
++
++ }
++
++ info->hdev.stat.byte_rx += len;
++}
++
++
++void bluecard_interrupt(int irq, void *dev_inst, struct pt_regs *regs)
++{
++ bluecard_info_t *info = dev_inst;
++ unsigned int iobase;
++ unsigned char reg;
++
++ if (!info) {
++ printk(KERN_WARNING "bluecard_cs: Call of irq %d for unknown device.\n", irq);
++ return;
++ }
++
++ if (!test_bit(CARD_READY, &(info->hw_state)))
++ return;
++
++ iobase = info->link.io.BasePort1;
++
++ spin_lock(&(info->lock));
++
++ /* Disable interrupt */
++ info->ctrl_reg &= ~REG_CONTROL_INTERRUPT;
++ outb(info->ctrl_reg, iobase + REG_CONTROL);
++
++ reg = inb(iobase + REG_INTERRUPT);
++
++ if ((reg != 0x00) && (reg != 0xff)) {
++
++ if (reg & 0x04) {
++ bluecard_receive(info, 0x00);
++ outb(0x04, iobase + REG_INTERRUPT);
++ outb(REG_COMMAND_RX_BUF_ONE, iobase + REG_COMMAND);
++ }
++
++ if (reg & 0x08) {
++ bluecard_receive(info, 0x10);
++ outb(0x08, iobase + REG_INTERRUPT);
++ outb(REG_COMMAND_RX_BUF_TWO, iobase + REG_COMMAND);
++ }
++
++ if (reg & 0x01) {
++ set_bit(XMIT_BUF_ONE_READY, &(info->tx_state));
++ outb(0x01, iobase + REG_INTERRUPT);
++ bluecard_write_wakeup(info);
++ }
++
++ if (reg & 0x02) {
++ set_bit(XMIT_BUF_TWO_READY, &(info->tx_state));
++ outb(0x02, iobase + REG_INTERRUPT);
++ bluecard_write_wakeup(info);
++ }
++
++ }
++
++ /* Enable interrupt */
++ info->ctrl_reg |= REG_CONTROL_INTERRUPT;
++ outb(info->ctrl_reg, iobase + REG_CONTROL);
++
++ spin_unlock(&(info->lock));
++}
++
++
++
++/* ======================== Device specific HCI commands ======================== */
++
++
++static int bluecard_hci_set_baud_rate(struct hci_dev *hdev, int baud)
++{
++ bluecard_info_t *info = (bluecard_info_t *)(hdev->driver_data);
++ struct sk_buff *skb;
++
++ /* Ericsson baud rate command */
++ unsigned char cmd[] = { HCI_COMMAND_PKT, 0x09, 0xfc, 0x01, 0x03 };
++
++ if (!(skb = bluez_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC))) {
++ printk(KERN_WARNING "bluecard_cs: Can't allocate mem for new packet.\n");
++ return -1;
++ }
++
++ switch (baud) {
++ case 460800:
++ cmd[4] = 0x00;
++ skb->pkt_type = PKT_BAUD_RATE_460800;
++ break;
++ case 230400:
++ cmd[4] = 0x01;
++ skb->pkt_type = PKT_BAUD_RATE_230400;
++ break;
++ case 115200:
++ cmd[4] = 0x02;
++ skb->pkt_type = PKT_BAUD_RATE_115200;
++ break;
++ case 57600:
++ /* Fall through... */
++ default:
++ cmd[4] = 0x03;
++ skb->pkt_type = PKT_BAUD_RATE_57600;
++ break;
++ }
++
++ memcpy(skb_put(skb, sizeof(cmd)), cmd, sizeof(cmd));
++
++ skb_queue_tail(&(info->txq), skb);
++
++ bluecard_write_wakeup(info);
++
++ return 0;
++}
++
++
++
++/* ======================== HCI interface ======================== */
++
++
++static int bluecard_hci_flush(struct hci_dev *hdev)
++{
++ bluecard_info_t *info = (bluecard_info_t *)(hdev->driver_data);
++
++ /* Drop TX queue */
++ skb_queue_purge(&(info->txq));
++
++ return 0;
++}
++
++
++static int bluecard_hci_open(struct hci_dev *hdev)
++{
++ bluecard_info_t *info = (bluecard_info_t *)(hdev->driver_data);
++ unsigned int iobase = info->link.io.BasePort1;
++
++ bluecard_hci_set_baud_rate(hdev, DEFAULT_BAUD_RATE);
++
++ if (test_and_set_bit(HCI_RUNNING, &(hdev->flags)))
++ return 0;
++
++ /* Enable LED */
++ outb(0x08 | 0x20, iobase + 0x30);
++
++ return 0;
++}
++
++
++static int bluecard_hci_close(struct hci_dev *hdev)
++{
++ bluecard_info_t *info = (bluecard_info_t *)(hdev->driver_data);
++ unsigned int iobase = info->link.io.BasePort1;
++
++ if (!test_and_clear_bit(HCI_RUNNING, &(hdev->flags)))
++ return 0;
++
++ bluecard_hci_flush(hdev);
++
++ /* Disable LED */
++ outb(0x00, iobase + 0x30);
++
++ return 0;
++}
++
++
++static int bluecard_hci_send_frame(struct sk_buff *skb)
++{
++ bluecard_info_t *info;
++ struct hci_dev *hdev = (struct hci_dev *)(skb->dev);
++
++ if (!hdev) {
++ printk(KERN_WARNING "bluecard_cs: Frame for unknown HCI device (hdev=NULL).");
++ return -ENODEV;
++ }
++
++ info = (bluecard_info_t *)(hdev->driver_data);
++
++ switch (skb->pkt_type) {
++ case HCI_COMMAND_PKT:
++ hdev->stat.cmd_tx++;
++ break;
++ case HCI_ACLDATA_PKT:
++ hdev->stat.acl_tx++;
++ break;
++ case HCI_SCODATA_PKT:
++ hdev->stat.sco_tx++;
++ break;
++ };
++
++ /* Prepend skb with frame type */
++ memcpy(skb_push(skb, 1), &(skb->pkt_type), 1);
++ skb_queue_tail(&(info->txq), skb);
++
++ bluecard_write_wakeup(info);
++
++ return 0;
++}
++
++
++static void bluecard_hci_destruct(struct hci_dev *hdev)
++{
++}
++
++
++static int bluecard_hci_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg)
++{
++ return -ENOIOCTLCMD;
++}
++
++
++
++/* ======================== Card services HCI interaction ======================== */
++
++
++int bluecard_open(bluecard_info_t *info)
++{
++ unsigned int iobase = info->link.io.BasePort1;
++ struct hci_dev *hdev;
++ unsigned char id;
++
++ spin_lock_init(&(info->lock));
++
++ init_timer(&(info->timer));
++ info->timer.function = &bluecard_activity_led_timeout;
++ info->timer.data = (u_long)info;
++
++ skb_queue_head_init(&(info->txq));
++
++ info->rx_state = RECV_WAIT_PACKET_TYPE;
++ info->rx_count = 0;
++ info->rx_skb = NULL;
++
++ id = inb(iobase + 0x30);
++
++ if ((id & 0x0f) == 0x02)
++ set_bit(CARD_HAS_PCCARD_ID, &(info->hw_state));
++
++ if (id & 0x10)
++ set_bit(CARD_HAS_POWER_LED, &(info->hw_state));
++
++ if (id & 0x20)
++ set_bit(CARD_HAS_ACTIVITY_LED, &(info->hw_state));
++
++ /* Reset card */
++ info->ctrl_reg = REG_CONTROL_BT_RESET | REG_CONTROL_CARD_RESET;
++ outb(info->ctrl_reg, iobase + REG_CONTROL);
++
++ /* Turn FPGA off */
++ outb(0x80, iobase + 0x30);
++
++ /* Wait some time */
++ set_current_state(TASK_INTERRUPTIBLE);
++ schedule_timeout(HZ / 100);
++
++ /* Turn FPGA on */
++ outb(0x00, iobase + 0x30);
++
++ /* Activate card */
++ info->ctrl_reg = REG_CONTROL_BT_ON | REG_CONTROL_BT_RES_PU;
++ outb(info->ctrl_reg, iobase + REG_CONTROL);
++
++ /* Enable interrupt */
++ outb(0xff, iobase + REG_INTERRUPT);
++ info->ctrl_reg |= REG_CONTROL_INTERRUPT;
++ outb(info->ctrl_reg, iobase + REG_CONTROL);
++
++ /* Start the RX buffers */
++ outb(REG_COMMAND_RX_BUF_ONE, iobase + REG_COMMAND);
++ outb(REG_COMMAND_RX_BUF_TWO, iobase + REG_COMMAND);
++
++ /* Signal that the hardware is ready */
++ set_bit(CARD_READY, &(info->hw_state));
++
++ /* Drop TX queue */
++ skb_queue_purge(&(info->txq));
++
++ /* Control the point at which RTS is enabled */
++ outb((0x0f << RTS_LEVEL_SHIFT_BITS) | 1, iobase + REG_RX_CONTROL);
++
++ /* Timeout before it is safe to send the first HCI packet */
++ set_current_state(TASK_INTERRUPTIBLE);
++ schedule_timeout((HZ * 5) / 4); // or set it to 3/2
++
++
++ /* Initialize and register HCI device */
++
++ hdev = &(info->hdev);
++
++ hdev->type = HCI_PCCARD;
++ hdev->driver_data = info;
++
++ hdev->open = bluecard_hci_open;
++ hdev->close = bluecard_hci_close;
++ hdev->flush = bluecard_hci_flush;
++ hdev->send = bluecard_hci_send_frame;
++ hdev->destruct = bluecard_hci_destruct;
++ hdev->ioctl = bluecard_hci_ioctl;
++
++ if (hci_register_dev(hdev) < 0) {
++ printk(KERN_WARNING "bluecard_cs: Can't register HCI device %s.\n", hdev->name);
++ return -ENODEV;
++ }
++
++ return 0;
++}
++
++
++int bluecard_close(bluecard_info_t *info)
++{
++ unsigned int iobase = info->link.io.BasePort1;
++ struct hci_dev *hdev = &(info->hdev);
++
++ if (info->link.state & DEV_CONFIG_PENDING)
++ return -ENODEV;
++
++ bluecard_hci_close(hdev);
++
++ clear_bit(CARD_READY, &(info->hw_state));
++
++ /* Reset card */
++ info->ctrl_reg = REG_CONTROL_BT_RESET | REG_CONTROL_CARD_RESET;
++ outb(info->ctrl_reg, iobase + REG_CONTROL);
++
++ /* Turn FPGA off */
++ outb(0x80, iobase + 0x30);
++
++ if (hci_unregister_dev(hdev) < 0)
++ printk(KERN_WARNING "bluecard_cs: Can't unregister HCI device %s.\n", hdev->name);
++
++ return 0;
++}
++
++
++
++/* ======================== Card services ======================== */
++
++
++static void cs_error(client_handle_t handle, int func, int ret)
++{
++ error_info_t err = { func, ret };
++
++ CardServices(ReportError, handle, &err);
++}
++
++
++dev_link_t *bluecard_attach(void)
++{
++ bluecard_info_t *info;
++ client_reg_t client_reg;
++ dev_link_t *link;
++ int i, ret;
++
++ /* Create new info device */
++ info = kmalloc(sizeof(*info), GFP_KERNEL);
++ if (!info)
++ return NULL;
++ memset(info, 0, sizeof(*info));
++
++ link = &info->link;
++ link->priv = info;
++
++ link->release.function = &bluecard_release;
++ link->release.data = (u_long)link;
++ link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
++ link->io.NumPorts1 = 8;
++ link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT;
++ link->irq.IRQInfo1 = IRQ_INFO2_VALID | IRQ_LEVEL_ID;
++
++ if (irq_list[0] == -1)
++ link->irq.IRQInfo2 = irq_mask;
++ else
++ for (i = 0; i < 4; i++)
++ link->irq.IRQInfo2 |= 1 << irq_list[i];
++
++ link->irq.Handler = bluecard_interrupt;
++ link->irq.Instance = info;
++
++ link->conf.Attributes = CONF_ENABLE_IRQ;
++ link->conf.Vcc = 50;
++ link->conf.IntType = INT_MEMORY_AND_IO;
++
++ /* Register with Card Services */
++ link->next = dev_list;
++ dev_list = link;
++ client_reg.dev_info = &dev_info;
++ client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE;
++ client_reg.EventMask =
++ CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
++ CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
++ CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
++ client_reg.event_handler = &bluecard_event;
++ client_reg.Version = 0x0210;
++ client_reg.event_callback_args.client_data = link;
++
++ ret = CardServices(RegisterClient, &link->handle, &client_reg);
++ if (ret != CS_SUCCESS) {
++ cs_error(link->handle, RegisterClient, ret);
++ bluecard_detach(link);
++ return NULL;
++ }
++
++ return link;
++}
++
++
++void bluecard_detach(dev_link_t *link)
++{
++ bluecard_info_t *info = link->priv;
++ dev_link_t **linkp;
++ int ret;
++
++ /* Locate device structure */
++ for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
++ if (*linkp == link)
++ break;
++
++ if (*linkp == NULL)
++ return;
++
++ del_timer(&link->release);
++ if (link->state & DEV_CONFIG)
++ bluecard_release((u_long)link);
++
++ if (link->handle) {
++ ret = CardServices(DeregisterClient, link->handle);
++ if (ret != CS_SUCCESS)
++ cs_error(link->handle, DeregisterClient, ret);
++ }
++
++ /* Unlink device structure, free bits */
++ *linkp = link->next;
++
++ kfree(info);
++}
++
++
++static int get_tuple(int fn, client_handle_t handle, tuple_t *tuple, cisparse_t *parse)
++{
++ int i;
++
++ i = CardServices(fn, handle, tuple);
++ if (i != CS_SUCCESS)
++ return CS_NO_MORE_ITEMS;
++
++ i = CardServices(GetTupleData, handle, tuple);
++ if (i != CS_SUCCESS)
++ return i;
++
++ return CardServices(ParseTuple, handle, tuple, parse);
++}
++
++
++#define first_tuple(a, b, c) get_tuple(GetFirstTuple, a, b, c)
++#define next_tuple(a, b, c) get_tuple(GetNextTuple, a, b, c)
++
++void bluecard_config(dev_link_t *link)
++{
++ client_handle_t handle = link->handle;
++ bluecard_info_t *info = link->priv;
++ tuple_t tuple;
++ u_short buf[256];
++ cisparse_t parse;
++ config_info_t config;
++ int i, n, last_ret, last_fn;
++
++ tuple.TupleData = (cisdata_t *)buf;
++ tuple.TupleOffset = 0;
++ tuple.TupleDataMax = 255;
++ tuple.Attributes = 0;
++
++ /* Get configuration register information */
++ tuple.DesiredTuple = CISTPL_CONFIG;
++ last_ret = first_tuple(handle, &tuple, &parse);
++ if (last_ret != CS_SUCCESS) {
++ last_fn = ParseTuple;
++ goto cs_failed;
++ }
++ link->conf.ConfigBase = parse.config.base;
++ link->conf.Present = parse.config.rmask[0];
++
++ /* Configure card */
++ link->state |= DEV_CONFIG;
++ i = CardServices(GetConfigurationInfo, handle, &config);
++ link->conf.Vcc = config.Vcc;
++
++ link->conf.ConfigIndex = 0x20;
++ link->io.NumPorts1 = 64;
++ link->io.IOAddrLines = 6;
++
++ for (n = 0; n < 0x400; n += 0x40) {
++ link->io.BasePort1 = n ^ 0x300;
++ i = CardServices(RequestIO, link->handle, &link->io);
++ if (i == CS_SUCCESS)
++ break;
++ }
++
++ if (i != CS_SUCCESS) {
++ cs_error(link->handle, RequestIO, i);
++ goto failed;
++ }
++
++ i = CardServices(RequestIRQ, link->handle, &link->irq);
++ if (i != CS_SUCCESS) {
++ cs_error(link->handle, RequestIRQ, i);
++ link->irq.AssignedIRQ = 0;
++ }
++
++ i = CardServices(RequestConfiguration, link->handle, &link->conf);
++ if (i != CS_SUCCESS) {
++ cs_error(link->handle, RequestConfiguration, i);
++ goto failed;
++ }
++
++ MOD_INC_USE_COUNT;
++
++ if (bluecard_open(info) != 0)
++ goto failed;
++
++ strcpy(info->node.dev_name, info->hdev.name);
++ link->dev = &info->node;
++ link->state &= ~DEV_CONFIG_PENDING;
++
++ return;
++
++cs_failed:
++ cs_error(link->handle, last_fn, last_ret);
++
++failed:
++ bluecard_release((u_long)link);
++}
++
++
++void bluecard_release(u_long arg)
++{
++ dev_link_t *link = (dev_link_t *)arg;
++ bluecard_info_t *info = link->priv;
++
++ if (link->state & DEV_PRESENT)
++ bluecard_close(info);
++
++ MOD_DEC_USE_COUNT;
++
++ link->dev = NULL;
++
++ CardServices(ReleaseConfiguration, link->handle);
++ CardServices(ReleaseIO, link->handle, &link->io);
++ CardServices(ReleaseIRQ, link->handle, &link->irq);
++
++ link->state &= ~DEV_CONFIG;
++}
++
++
++int bluecard_event(event_t event, int priority, event_callback_args_t *args)
++{
++ dev_link_t *link = args->client_data;
++ bluecard_info_t *info = link->priv;
++
++ switch (event) {
++ case CS_EVENT_CARD_REMOVAL:
++ link->state &= ~DEV_PRESENT;
++ if (link->state & DEV_CONFIG) {
++ bluecard_close(info);
++ mod_timer(&link->release, jiffies + HZ / 20);
++ }
++ break;
++ case CS_EVENT_CARD_INSERTION:
++ link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
++ bluecard_config(link);
++ break;
++ case CS_EVENT_PM_SUSPEND:
++ link->state |= DEV_SUSPEND;
++ /* Fall through... */
++ case CS_EVENT_RESET_PHYSICAL:
++ if (link->state & DEV_CONFIG)
++ CardServices(ReleaseConfiguration, link->handle);
++ break;
++ case CS_EVENT_PM_RESUME:
++ link->state &= ~DEV_SUSPEND;
++ /* Fall through... */
++ case CS_EVENT_CARD_RESET:
++ if (DEV_OK(link))
++ CardServices(RequestConfiguration, link->handle, &link->conf);
++ break;
++ }
++
++ return 0;
++}
++
++
++
++/* ======================== Module initialization ======================== */
++
++
++int __init init_bluecard_cs(void)
++{
++ servinfo_t serv;
++ int err;
++
++ CardServices(GetCardServicesInfo, &serv);
++ if (serv.Revision != CS_RELEASE_CODE) {
++ printk(KERN_NOTICE "bluecard_cs: Card Services release does not match!\n");
++ return -1;
++ }
++
++ err = register_pccard_driver(&dev_info, &bluecard_attach, &bluecard_detach);
++
++ return err;
++}
++
++
++void __exit exit_bluecard_cs(void)
++{
++ unregister_pccard_driver(&dev_info);
++
++ while (dev_list != NULL)
++ bluecard_detach(dev_list);
++}
++
++
++module_init(init_bluecard_cs);
++module_exit(exit_bluecard_cs);
++
++EXPORT_NO_SYMBOLS;
+diff -urN linux-2.4.18/drivers/bluetooth/bt3c_cs.c linux-2.4.18-mh15/drivers/bluetooth/bt3c_cs.c
+--- linux-2.4.18/drivers/bluetooth/bt3c_cs.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/drivers/bluetooth/bt3c_cs.c 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,986 @@
++/*
++ *
++ * Driver for the 3Com Bluetooth PCMCIA card
++ *
++ * Copyright (C) 2001-2002 Marcel Holtmann <marcel@holtmann.org>
++ * Jose Orlando Pereira <jop@di.uminho.pt>
++ *
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation;
++ *
++ * Software distributed under the License is distributed on an "AS
++ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
++ * implied. See the License for the specific language governing
++ * rights and limitations under the License.
++ *
++ * The initial developer of the original code is David A. Hinds
++ * <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
++ * are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
++ *
++ */
++
++#include <linux/config.h>
++#include <linux/module.h>
++
++#include <linux/kernel.h>
++#include <linux/kmod.h>
++#include <linux/init.h>
++#include <linux/slab.h>
++#include <linux/types.h>
++#include <linux/sched.h>
++#include <linux/delay.h>
++#include <linux/timer.h>
++#include <linux/errno.h>
++#include <linux/unistd.h>
++#include <linux/ptrace.h>
++#include <linux/ioport.h>
++#include <linux/spinlock.h>
++
++#include <linux/skbuff.h>
++#include <linux/string.h>
++#include <linux/serial.h>
++#include <linux/serial_reg.h>
++#include <asm/system.h>
++#include <asm/bitops.h>
++#include <asm/io.h>
++
++#include <linux/firmware.h>
++
++#include <pcmcia/version.h>
++#include <pcmcia/cs_types.h>
++#include <pcmcia/cs.h>
++#include <pcmcia/cistpl.h>
++#include <pcmcia/ciscode.h>
++#include <pcmcia/ds.h>
++#include <pcmcia/cisreg.h>
++
++#include <net/bluetooth/bluetooth.h>
++#include <net/bluetooth/hci_core.h>
++
++
++
++/* ======================== Module parameters ======================== */
++
++
++/* Bit map of interrupts to choose from */
++static u_int irq_mask = 0xffff;
++static int irq_list[4] = { -1 };
++
++MODULE_PARM(irq_mask, "i");
++MODULE_PARM(irq_list, "1-4i");
++
++MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>, Jose Orlando Pereira <jop@di.uminho.pt>");
++MODULE_DESCRIPTION("BlueZ driver for the 3Com Bluetooth PCMCIA card");
++MODULE_LICENSE("GPL");
++
++
++
++/* ======================== Local structures ======================== */
++
++
++typedef struct bt3c_info_t {
++ dev_link_t link;
++ dev_node_t node;
++
++ struct hci_dev hdev;
++
++ spinlock_t lock; /* For serializing operations */
++
++ struct sk_buff_head txq;
++ unsigned long tx_state;
++
++ unsigned long rx_state;
++ unsigned long rx_count;
++ struct sk_buff *rx_skb;
++} bt3c_info_t;
++
++
++void bt3c_config(dev_link_t *link);
++void bt3c_release(u_long arg);
++int bt3c_event(event_t event, int priority, event_callback_args_t *args);
++
++static dev_info_t dev_info = "bt3c_cs";
++
++dev_link_t *bt3c_attach(void);
++void bt3c_detach(dev_link_t *);
++
++static dev_link_t *dev_list = NULL;
++
++
++/* Transmit states */
++#define XMIT_SENDING 1
++#define XMIT_WAKEUP 2
++#define XMIT_WAITING 8
++
++/* Receiver states */
++#define RECV_WAIT_PACKET_TYPE 0
++#define RECV_WAIT_EVENT_HEADER 1
++#define RECV_WAIT_ACL_HEADER 2
++#define RECV_WAIT_SCO_HEADER 3
++#define RECV_WAIT_DATA 4
++
++
++
++/* ======================== Special I/O functions ======================== */
++
++
++#define DATA_L 0
++#define DATA_H 1
++#define ADDR_L 2
++#define ADDR_H 3
++#define CONTROL 4
++
++
++inline void bt3c_address(unsigned int iobase, unsigned short addr)
++{
++ outb(addr & 0xff, iobase + ADDR_L);
++ outb((addr >> 8) & 0xff, iobase + ADDR_H);
++}
++
++
++inline void bt3c_put(unsigned int iobase, unsigned short value)
++{
++ outb(value & 0xff, iobase + DATA_L);
++ outb((value >> 8) & 0xff, iobase + DATA_H);
++}
++
++
++inline void bt3c_io_write(unsigned int iobase, unsigned short addr, unsigned short value)
++{
++ bt3c_address(iobase, addr);
++ bt3c_put(iobase, value);
++}
++
++
++inline unsigned short bt3c_get(unsigned int iobase)
++{
++ unsigned short value = inb(iobase + DATA_L);
++
++ value |= inb(iobase + DATA_H) << 8;
++
++ return value;
++}
++
++
++inline unsigned short bt3c_read(unsigned int iobase, unsigned short addr)
++{
++ bt3c_address(iobase, addr);
++
++ return bt3c_get(iobase);
++}
++
++
++
++/* ======================== Interrupt handling ======================== */
++
++
++static int bt3c_write(unsigned int iobase, int fifo_size, __u8 *buf, int len)
++{
++ int actual = 0;
++
++ bt3c_address(iobase, 0x7080);
++
++ /* Fill FIFO with current frame */
++ while (actual < len) {
++ /* Transmit next byte */
++ bt3c_put(iobase, buf[actual]);
++ actual++;
++ }
++
++ bt3c_io_write(iobase, 0x7005, actual);
++
++ return actual;
++}
++
++
++static void bt3c_write_wakeup(bt3c_info_t *info, int from)
++{
++ unsigned long flags;
++
++ if (!info) {
++ printk(KERN_WARNING "bt3c_cs: Call of write_wakeup for unknown device.\n");
++ return;
++ }
++
++ if (test_and_set_bit(XMIT_SENDING, &(info->tx_state)))
++ return;
++
++ spin_lock_irqsave(&(info->lock), flags);
++
++ do {
++ register unsigned int iobase = info->link.io.BasePort1;
++ register struct sk_buff *skb;
++ register int len;
++
++ if (!(info->link.state & DEV_PRESENT))
++ break;
++
++
++ if (!(skb = skb_dequeue(&(info->txq)))) {
++ clear_bit(XMIT_SENDING, &(info->tx_state));
++ break;
++ }
++
++ /* Send frame */
++ len = bt3c_write(iobase, 256, skb->data, skb->len);
++
++ if (len != skb->len) {
++ printk(KERN_WARNING "bt3c_cs: very strange\n");
++ }
++
++ kfree_skb(skb);
++
++ info->hdev.stat.byte_tx += len;
++
++ } while (0);
++
++ spin_unlock_irqrestore(&(info->lock), flags);
++}
++
++
++static void bt3c_receive(bt3c_info_t *info)
++{
++ unsigned int iobase;
++ int size = 0, avail;
++
++ if (!info) {
++ printk(KERN_WARNING "bt3c_cs: Call of receive for unknown device.\n");
++ return;
++ }
++
++ iobase = info->link.io.BasePort1;
++
++ avail = bt3c_read(iobase, 0x7006);
++ //printk("bt3c_cs: receiving %d bytes\n", avail);
++
++ bt3c_address(iobase, 0x7480);
++ while (size < avail) {
++ size++;
++ info->hdev.stat.byte_rx++;
++
++ /* Allocate packet */
++ if (info->rx_skb == NULL) {
++ info->rx_state = RECV_WAIT_PACKET_TYPE;
++ info->rx_count = 0;
++ if (!(info->rx_skb = bluez_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC))) {
++ printk(KERN_WARNING "bt3c_cs: Can't allocate mem for new packet.\n");
++ return;
++ }
++ }
++
++
++ if (info->rx_state == RECV_WAIT_PACKET_TYPE) {
++
++ info->rx_skb->dev = (void *)&(info->hdev);
++ info->rx_skb->pkt_type = inb(iobase + DATA_L);
++ inb(iobase + DATA_H);
++ //printk("bt3c: PACKET_TYPE=%02x\n", info->rx_skb->pkt_type);
++
++ switch (info->rx_skb->pkt_type) {
++
++ case HCI_EVENT_PKT:
++ info->rx_state = RECV_WAIT_EVENT_HEADER;
++ info->rx_count = HCI_EVENT_HDR_SIZE;
++ break;
++
++ case HCI_ACLDATA_PKT:
++ info->rx_state = RECV_WAIT_ACL_HEADER;
++ info->rx_count = HCI_ACL_HDR_SIZE;
++ break;
++
++ case HCI_SCODATA_PKT:
++ info->rx_state = RECV_WAIT_SCO_HEADER;
++ info->rx_count = HCI_SCO_HDR_SIZE;
++ break;
++
++ default:
++ /* Unknown packet */
++ printk(KERN_WARNING "bt3c_cs: Unknown HCI packet with type 0x%02x received.\n", info->rx_skb->pkt_type);
++ info->hdev.stat.err_rx++;
++ clear_bit(HCI_RUNNING, &(info->hdev.flags));
++
++ kfree_skb(info->rx_skb);
++ info->rx_skb = NULL;
++ break;
++
++ }
++
++ } else {
++
++ __u8 x = inb(iobase + DATA_L);
++
++ *skb_put(info->rx_skb, 1) = x;
++ inb(iobase + DATA_H);
++ info->rx_count--;
++
++ if (info->rx_count == 0) {
++
++ int dlen;
++ hci_event_hdr *eh;
++ hci_acl_hdr *ah;
++ hci_sco_hdr *sh;
++
++ switch (info->rx_state) {
++
++ case RECV_WAIT_EVENT_HEADER:
++ eh = (hci_event_hdr *)(info->rx_skb->data);
++ info->rx_state = RECV_WAIT_DATA;
++ info->rx_count = eh->plen;
++ break;
++
++ case RECV_WAIT_ACL_HEADER:
++ ah = (hci_acl_hdr *)(info->rx_skb->data);
++ dlen = __le16_to_cpu(ah->dlen);
++ info->rx_state = RECV_WAIT_DATA;
++ info->rx_count = dlen;
++ break;
++
++ case RECV_WAIT_SCO_HEADER:
++ sh = (hci_sco_hdr *)(info->rx_skb->data);
++ info->rx_state = RECV_WAIT_DATA;
++ info->rx_count = sh->dlen;
++ break;
++
++ case RECV_WAIT_DATA:
++ hci_recv_frame(info->rx_skb);
++ info->rx_skb = NULL;
++ break;
++
++ }
++
++ }
++
++ }
++
++ }
++
++ bt3c_io_write(iobase, 0x7006, 0x0000);
++}
++
++
++void bt3c_interrupt(int irq, void *dev_inst, struct pt_regs *regs)
++{
++ bt3c_info_t *info = dev_inst;
++ unsigned int iobase;
++ int iir;
++
++ if (!info) {
++ printk(KERN_WARNING "bt3c_cs: Call of irq %d for unknown device.\n", irq);
++ return;
++ }
++
++ iobase = info->link.io.BasePort1;
++
++ spin_lock(&(info->lock));
++
++ iir = inb(iobase + CONTROL);
++ if (iir & 0x80) {
++ int stat = bt3c_read(iobase, 0x7001);
++
++ if ((stat & 0xff) == 0x7f) {
++ printk(KERN_WARNING "bt3c_cs: STRANGE stat=%04x\n", stat);
++ } else if ((stat & 0xff) != 0xff) {
++ if (stat & 0x0020) {
++ int stat = bt3c_read(iobase, 0x7002) & 0x10;
++ printk(KERN_WARNING "bt3c_cs: antena %s\n", stat ? "OUT" : "IN");
++ }
++ if (stat & 0x0001)
++ bt3c_receive(info);
++ if (stat & 0x0002) {
++ //printk("bt3c_cs: ACK %04x\n", stat);
++ clear_bit(XMIT_SENDING, &(info->tx_state));
++ bt3c_write_wakeup(info, 1);
++ }
++
++ bt3c_io_write(iobase, 0x7001, 0x0000);
++
++ outb(iir, iobase + CONTROL);
++ }
++ }
++
++ spin_unlock(&(info->lock));
++}
++
++
++
++
++/* ======================== HCI interface ======================== */
++
++
++static int bt3c_hci_flush(struct hci_dev *hdev)
++{
++ bt3c_info_t *info = (bt3c_info_t *)(hdev->driver_data);
++
++ /* Drop TX queue */
++ skb_queue_purge(&(info->txq));
++
++ return 0;
++}
++
++
++static int bt3c_hci_open(struct hci_dev *hdev)
++{
++ set_bit(HCI_RUNNING, &(hdev->flags));
++
++ return 0;
++}
++
++
++static int bt3c_hci_close(struct hci_dev *hdev)
++{
++ if (!test_and_clear_bit(HCI_RUNNING, &(hdev->flags)))
++ return 0;
++
++ bt3c_hci_flush(hdev);
++
++ return 0;
++}
++
++
++static int bt3c_hci_send_frame(struct sk_buff *skb)
++{
++ bt3c_info_t *info;
++ struct hci_dev *hdev = (struct hci_dev *)(skb->dev);
++
++ if (!hdev) {
++ printk(KERN_WARNING "bt3c_cs: Frame for unknown HCI device (hdev=NULL).");
++ return -ENODEV;
++ }
++
++ info = (bt3c_info_t *) (hdev->driver_data);
++
++ switch (skb->pkt_type) {
++ case HCI_COMMAND_PKT:
++ hdev->stat.cmd_tx++;
++ break;
++ case HCI_ACLDATA_PKT:
++ hdev->stat.acl_tx++;
++ break;
++ case HCI_SCODATA_PKT:
++ hdev->stat.sco_tx++;
++ break;
++ };
++
++ /* Prepend skb with frame type */
++ memcpy(skb_push(skb, 1), &(skb->pkt_type), 1);
++ skb_queue_tail(&(info->txq), skb);
++
++ bt3c_write_wakeup(info, 0);
++
++ return 0;
++}
++
++
++static void bt3c_hci_destruct(struct hci_dev *hdev)
++{
++}
++
++
++static int bt3c_hci_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg)
++{
++ return -ENOIOCTLCMD;
++}
++
++
++
++/* ======================== Card services HCI interaction ======================== */
++
++
++static int bt3c_load_firmware(bt3c_info_t *info, unsigned char *firmware, int count)
++{
++ char *ptr = (char *) firmware;
++ char b[9];
++ unsigned int iobase, size, addr, fcs, tmp;
++ int i, err = 0;
++
++ iobase = info->link.io.BasePort1;
++
++ /* Reset */
++
++ bt3c_io_write(iobase, 0x8040, 0x0404);
++ bt3c_io_write(iobase, 0x8040, 0x0400);
++
++ udelay(1);
++
++ bt3c_io_write(iobase, 0x8040, 0x0404);
++
++ udelay(17);
++
++ /* Load */
++
++ while (count) {
++ if (ptr[0] != 'S') {
++ printk(KERN_WARNING "bt3c_cs: Bad address in firmware.\n");
++ err = -EFAULT;
++ goto error;
++ }
++
++ memset(b, 0, sizeof(b));
++ memcpy(b, ptr + 2, 2);
++ size = simple_strtol(b, NULL, 16);
++
++ memset(b, 0, sizeof(b));
++ memcpy(b, ptr + 4, 8);
++ addr = simple_strtol(b, NULL, 16);
++
++ memset(b, 0, sizeof(b));
++ memcpy(b, ptr + (size * 2) + 2, 2);
++ fcs = simple_strtol(b, NULL, 16);
++
++ memset(b, 0, sizeof(b));
++ for (tmp = 0, i = 0; i < size; i++) {
++ memcpy(b, ptr + (i * 2) + 2, 2);
++ tmp += simple_strtol(b, NULL, 16);
++ }
++
++ if (((tmp + fcs) & 0xff) != 0xff) {
++ printk(KERN_WARNING "bt3c_cs: Checksum error in firmware.\n");
++ err = -EILSEQ;
++ goto error;
++ }
++
++ if (ptr[1] == '3') {
++ bt3c_address(iobase, addr);
++
++ memset(b, 0, sizeof(b));
++ for (i = 0; i < (size - 4) / 2; i++) {
++ memcpy(b, ptr + (i * 4) + 12, 4);
++ tmp = simple_strtol(b, NULL, 16);
++ bt3c_put(iobase, tmp);
++ }
++ }
++
++ ptr += (size * 2) + 6;
++ count -= (size * 2) + 6;
++ }
++
++ udelay(17);
++
++ /* Boot */
++
++ bt3c_address(iobase, 0x3000);
++ outb(inb(iobase + CONTROL) | 0x40, iobase + CONTROL);
++
++error:
++ udelay(17);
++
++ /* Clear */
++
++ bt3c_io_write(iobase, 0x7006, 0x0000);
++ bt3c_io_write(iobase, 0x7005, 0x0000);
++ bt3c_io_write(iobase, 0x7001, 0x0000);
++
++ return err;
++}
++
++
++int bt3c_open(bt3c_info_t *info)
++{
++ const struct firmware *firmware;
++ char device[16];
++ struct hci_dev *hdev;
++ int err;
++
++ spin_lock_init(&(info->lock));
++
++ skb_queue_head_init(&(info->txq));
++
++ info->rx_state = RECV_WAIT_PACKET_TYPE;
++ info->rx_count = 0;
++ info->rx_skb = NULL;
++
++ /* Load firmware */
++
++ snprintf(device, sizeof(device), "bt3c%4.4x", info->link.io.BasePort1);
++
++ err = request_firmware(&firmware, "BT3CPCC.bin", device);
++ if (err < 0) {
++ printk(KERN_WARNING "bt3c_cs: Firmware request failed.\n");
++ return err;
++ }
++
++ err = bt3c_load_firmware(info, firmware->data, firmware->size);
++
++ release_firmware(firmware);
++
++ if (err < 0) {
++ printk(KERN_WARNING "bt3c_cs: Firmware loading failed.\n");
++ return err;
++ }
++
++ /* Timeout before it is safe to send the first HCI packet */
++
++ set_current_state(TASK_INTERRUPTIBLE);
++ schedule_timeout(HZ);
++
++
++ /* Initialize and register HCI device */
++
++ hdev = &(info->hdev);
++
++ hdev->type = HCI_PCCARD;
++ hdev->driver_data = info;
++
++ hdev->open = bt3c_hci_open;
++ hdev->close = bt3c_hci_close;
++ hdev->flush = bt3c_hci_flush;
++ hdev->send = bt3c_hci_send_frame;
++ hdev->destruct = bt3c_hci_destruct;
++ hdev->ioctl = bt3c_hci_ioctl;
++
++ if (hci_register_dev(hdev) < 0) {
++ printk(KERN_WARNING "bt3c_cs: Can't register HCI device %s.\n", hdev->name);
++ return -ENODEV;
++ }
++
++ return 0;
++}
++
++
++int bt3c_close(bt3c_info_t *info)
++{
++ struct hci_dev *hdev = &(info->hdev);
++
++ if (info->link.state & DEV_CONFIG_PENDING)
++ return -ENODEV;
++
++ bt3c_hci_close(hdev);
++
++ if (hci_unregister_dev(hdev) < 0)
++ printk(KERN_WARNING "bt3c_cs: Can't unregister HCI device %s.\n", hdev->name);
++
++ return 0;
++}
++
++
++
++/* ======================== Card services ======================== */
++
++
++static void cs_error(client_handle_t handle, int func, int ret)
++{
++ error_info_t err = { func, ret };
++
++ CardServices(ReportError, handle, &err);
++}
++
++
++dev_link_t *bt3c_attach(void)
++{
++ bt3c_info_t *info;
++ client_reg_t client_reg;
++ dev_link_t *link;
++ int i, ret;
++
++ /* Create new info device */
++ info = kmalloc(sizeof(*info), GFP_KERNEL);
++ if (!info)
++ return NULL;
++ memset(info, 0, sizeof(*info));
++
++ link = &info->link;
++ link->priv = info;
++
++ link->release.function = &bt3c_release;
++ link->release.data = (u_long)link;
++ link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
++ link->io.NumPorts1 = 8;
++ link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT;
++ link->irq.IRQInfo1 = IRQ_INFO2_VALID | IRQ_LEVEL_ID;
++
++ if (irq_list[0] == -1)
++ link->irq.IRQInfo2 = irq_mask;
++ else
++ for (i = 0; i < 4; i++)
++ link->irq.IRQInfo2 |= 1 << irq_list[i];
++
++ link->irq.Handler = bt3c_interrupt;
++ link->irq.Instance = info;
++
++ link->conf.Attributes = CONF_ENABLE_IRQ;
++ link->conf.Vcc = 50;
++ link->conf.IntType = INT_MEMORY_AND_IO;
++
++ /* Register with Card Services */
++ link->next = dev_list;
++ dev_list = link;
++ client_reg.dev_info = &dev_info;
++ client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE;
++ client_reg.EventMask =
++ CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
++ CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
++ CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
++ client_reg.event_handler = &bt3c_event;
++ client_reg.Version = 0x0210;
++ client_reg.event_callback_args.client_data = link;
++
++ ret = CardServices(RegisterClient, &link->handle, &client_reg);
++ if (ret != CS_SUCCESS) {
++ cs_error(link->handle, RegisterClient, ret);
++ bt3c_detach(link);
++ return NULL;
++ }
++
++ return link;
++}
++
++
++void bt3c_detach(dev_link_t *link)
++{
++ bt3c_info_t *info = link->priv;
++ dev_link_t **linkp;
++ int ret;
++
++ /* Locate device structure */
++ for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
++ if (*linkp == link)
++ break;
++
++ if (*linkp == NULL)
++ return;
++
++ del_timer(&link->release);
++
++ if (link->state & DEV_CONFIG)
++ bt3c_release((u_long)link);
++
++ if (link->handle) {
++ ret = CardServices(DeregisterClient, link->handle);
++ if (ret != CS_SUCCESS)
++ cs_error(link->handle, DeregisterClient, ret);
++ }
++
++ /* Unlink device structure, free bits */
++ *linkp = link->next;
++
++ kfree(info);
++}
++
++
++static int get_tuple(int fn, client_handle_t handle, tuple_t *tuple, cisparse_t *parse)
++{
++ int i;
++
++ i = CardServices(fn, handle, tuple);
++ if (i != CS_SUCCESS)
++ return CS_NO_MORE_ITEMS;
++
++ i = CardServices(GetTupleData, handle, tuple);
++ if (i != CS_SUCCESS)
++ return i;
++
++ return CardServices(ParseTuple, handle, tuple, parse);
++}
++
++
++#define first_tuple(a, b, c) get_tuple(GetFirstTuple, a, b, c)
++#define next_tuple(a, b, c) get_tuple(GetNextTuple, a, b, c)
++
++void bt3c_config(dev_link_t *link)
++{
++ static ioaddr_t base[5] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, 0x0 };
++ client_handle_t handle = link->handle;
++ bt3c_info_t *info = link->priv;
++ tuple_t tuple;
++ u_short buf[256];
++ cisparse_t parse;
++ cistpl_cftable_entry_t *cf = &parse.cftable_entry;
++ config_info_t config;
++ int i, j, try, last_ret, last_fn;
++
++ tuple.TupleData = (cisdata_t *)buf;
++ tuple.TupleOffset = 0;
++ tuple.TupleDataMax = 255;
++ tuple.Attributes = 0;
++
++ /* Get configuration register information */
++ tuple.DesiredTuple = CISTPL_CONFIG;
++ last_ret = first_tuple(handle, &tuple, &parse);
++ if (last_ret != CS_SUCCESS) {
++ last_fn = ParseTuple;
++ goto cs_failed;
++ }
++ link->conf.ConfigBase = parse.config.base;
++ link->conf.Present = parse.config.rmask[0];
++
++ /* Configure card */
++ link->state |= DEV_CONFIG;
++ i = CardServices(GetConfigurationInfo, handle, &config);
++ link->conf.Vcc = config.Vcc;
++
++ /* First pass: look for a config entry that looks normal. */
++ tuple.TupleData = (cisdata_t *)buf;
++ tuple.TupleOffset = 0;
++ tuple.TupleDataMax = 255;
++ tuple.Attributes = 0;
++ tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
++ /* Two tries: without IO aliases, then with aliases */
++ for (try = 0; try < 2; try++) {
++ i = first_tuple(handle, &tuple, &parse);
++ while (i != CS_NO_MORE_ITEMS) {
++ if (i != CS_SUCCESS)
++ goto next_entry;
++ if (cf->vpp1.present & (1 << CISTPL_POWER_VNOM))
++ link->conf.Vpp1 = link->conf.Vpp2 = cf->vpp1.param[CISTPL_POWER_VNOM] / 10000;
++ if ((cf->io.nwin > 0) && (cf->io.win[0].len == 8) && (cf->io.win[0].base != 0)) {
++ link->conf.ConfigIndex = cf->index;
++ link->io.BasePort1 = cf->io.win[0].base;
++ link->io.IOAddrLines = (try == 0) ? 16 : cf->io.flags & CISTPL_IO_LINES_MASK;
++ i = CardServices(RequestIO, link->handle, &link->io);
++ if (i == CS_SUCCESS)
++ goto found_port;
++ }
++next_entry:
++ i = next_tuple(handle, &tuple, &parse);
++ }
++ }
++
++ /* Second pass: try to find an entry that isn't picky about
++ its base address, then try to grab any standard serial port
++ address, and finally try to get any free port. */
++ i = first_tuple(handle, &tuple, &parse);
++ while (i != CS_NO_MORE_ITEMS) {
++ if ((i == CS_SUCCESS) && (cf->io.nwin > 0) && ((cf->io.flags & CISTPL_IO_LINES_MASK) <= 3)) {
++ link->conf.ConfigIndex = cf->index;
++ for (j = 0; j < 5; j++) {
++ link->io.BasePort1 = base[j];
++ link->io.IOAddrLines = base[j] ? 16 : 3;
++ i = CardServices(RequestIO, link->handle, &link->io);
++ if (i == CS_SUCCESS)
++ goto found_port;
++ }
++ }
++ i = next_tuple(handle, &tuple, &parse);
++ }
++
++found_port:
++ if (i != CS_SUCCESS) {
++ printk(KERN_NOTICE "bt3c_cs: No usable port range found. Giving up.\n");
++ cs_error(link->handle, RequestIO, i);
++ goto failed;
++ }
++
++ i = CardServices(RequestIRQ, link->handle, &link->irq);
++ if (i != CS_SUCCESS) {
++ cs_error(link->handle, RequestIRQ, i);
++ link->irq.AssignedIRQ = 0;
++ }
++
++ i = CardServices(RequestConfiguration, link->handle, &link->conf);
++ if (i != CS_SUCCESS) {
++ cs_error(link->handle, RequestConfiguration, i);
++ goto failed;
++ }
++
++ MOD_INC_USE_COUNT;
++
++ if (bt3c_open(info) != 0)
++ goto failed;
++
++ strcpy(info->node.dev_name, info->hdev.name);
++ link->dev = &info->node;
++ link->state &= ~DEV_CONFIG_PENDING;
++
++ return;
++
++cs_failed:
++ cs_error(link->handle, last_fn, last_ret);
++
++failed:
++ bt3c_release((u_long)link);
++}
++
++
++void bt3c_release(u_long arg)
++{
++ dev_link_t *link = (dev_link_t *)arg;
++ bt3c_info_t *info = link->priv;
++
++ if (link->state & DEV_PRESENT)
++ bt3c_close(info);
++
++ MOD_DEC_USE_COUNT;
++
++ link->dev = NULL;
++
++ CardServices(ReleaseConfiguration, link->handle);
++ CardServices(ReleaseIO, link->handle, &link->io);
++ CardServices(ReleaseIRQ, link->handle, &link->irq);
++
++ link->state &= ~DEV_CONFIG;
++}
++
++
++int bt3c_event(event_t event, int priority, event_callback_args_t *args)
++{
++ dev_link_t *link = args->client_data;
++ bt3c_info_t *info = link->priv;
++
++ switch (event) {
++ case CS_EVENT_CARD_REMOVAL:
++ link->state &= ~DEV_PRESENT;
++ if (link->state & DEV_CONFIG) {
++ bt3c_close(info);
++ mod_timer(&link->release, jiffies + HZ / 20);
++ }
++ break;
++ case CS_EVENT_CARD_INSERTION:
++ link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
++ bt3c_config(link);
++ break;
++ case CS_EVENT_PM_SUSPEND:
++ link->state |= DEV_SUSPEND;
++ /* Fall through... */
++ case CS_EVENT_RESET_PHYSICAL:
++ if (link->state & DEV_CONFIG)
++ CardServices(ReleaseConfiguration, link->handle);
++ break;
++ case CS_EVENT_PM_RESUME:
++ link->state &= ~DEV_SUSPEND;
++ /* Fall through... */
++ case CS_EVENT_CARD_RESET:
++ if (DEV_OK(link))
++ CardServices(RequestConfiguration, link->handle, &link->conf);
++ break;
++ }
++
++ return 0;
++}
++
++
++
++/* ======================== Module initialization ======================== */
++
++
++int __init init_bt3c_cs(void)
++{
++ servinfo_t serv;
++ int err;
++
++ CardServices(GetCardServicesInfo, &serv);
++ if (serv.Revision != CS_RELEASE_CODE) {
++ printk(KERN_NOTICE "bt3c_cs: Card Services release does not match!\n");
++ return -1;
++ }
++
++ err = register_pccard_driver(&dev_info, &bt3c_attach, &bt3c_detach);
++
++ return err;
++}
++
++
++void __exit exit_bt3c_cs(void)
++{
++ unregister_pccard_driver(&dev_info);
++
++ while (dev_list != NULL)
++ bt3c_detach(dev_list);
++}
++
++
++module_init(init_bt3c_cs);
++module_exit(exit_bt3c_cs);
++
++EXPORT_NO_SYMBOLS;
+diff -urN linux-2.4.18/drivers/bluetooth/btuart_cs.c linux-2.4.18-mh15/drivers/bluetooth/btuart_cs.c
+--- linux-2.4.18/drivers/bluetooth/btuart_cs.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/drivers/bluetooth/btuart_cs.c 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,909 @@
++/*
++ *
++ * Driver for Bluetooth PCMCIA cards with HCI UART interface
++ *
++ * Copyright (C) 2001-2002 Marcel Holtmann <marcel@holtmann.org>
++ *
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation;
++ *
++ * Software distributed under the License is distributed on an "AS
++ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
++ * implied. See the License for the specific language governing
++ * rights and limitations under the License.
++ *
++ * The initial developer of the original code is David A. Hinds
++ * <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
++ * are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
++ *
++ */
++
++#include <linux/config.h>
++#include <linux/module.h>
++
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/slab.h>
++#include <linux/types.h>
++#include <linux/sched.h>
++#include <linux/timer.h>
++#include <linux/errno.h>
++#include <linux/ptrace.h>
++#include <linux/ioport.h>
++#include <linux/spinlock.h>
++
++#include <linux/skbuff.h>
++#include <linux/string.h>
++#include <linux/serial.h>
++#include <linux/serial_reg.h>
++#include <asm/system.h>
++#include <asm/bitops.h>
++#include <asm/io.h>
++
++#include <pcmcia/version.h>
++#include <pcmcia/cs_types.h>
++#include <pcmcia/cs.h>
++#include <pcmcia/cistpl.h>
++#include <pcmcia/ciscode.h>
++#include <pcmcia/ds.h>
++#include <pcmcia/cisreg.h>
++
++#include <net/bluetooth/bluetooth.h>
++#include <net/bluetooth/hci_core.h>
++
++
++
++/* ======================== Module parameters ======================== */
++
++
++/* Bit map of interrupts to choose from */
++static u_int irq_mask = 0xffff;
++static int irq_list[4] = { -1 };
++
++MODULE_PARM(irq_mask, "i");
++MODULE_PARM(irq_list, "1-4i");
++
++MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
++MODULE_DESCRIPTION("BlueZ driver for Bluetooth PCMCIA cards with HCI UART interface");
++MODULE_LICENSE("GPL");
++
++
++
++/* ======================== Local structures ======================== */
++
++
++typedef struct btuart_info_t {
++ dev_link_t link;
++ dev_node_t node;
++
++ struct hci_dev hdev;
++
++ spinlock_t lock; /* For serializing operations */
++
++ struct sk_buff_head txq;
++ unsigned long tx_state;
++
++ unsigned long rx_state;
++ unsigned long rx_count;
++ struct sk_buff *rx_skb;
++} btuart_info_t;
++
++
++void btuart_config(dev_link_t *link);
++void btuart_release(u_long arg);
++int btuart_event(event_t event, int priority, event_callback_args_t *args);
++
++static dev_info_t dev_info = "btuart_cs";
++
++dev_link_t *btuart_attach(void);
++void btuart_detach(dev_link_t *);
++
++static dev_link_t *dev_list = NULL;
++
++
++/* Maximum baud rate */
++#define SPEED_MAX 115200
++
++/* Default baud rate: 57600, 115200, 230400 or 460800 */
++#define DEFAULT_BAUD_RATE 115200
++
++
++/* Transmit states */
++#define XMIT_SENDING 1
++#define XMIT_WAKEUP 2
++#define XMIT_WAITING 8
++
++/* Receiver states */
++#define RECV_WAIT_PACKET_TYPE 0
++#define RECV_WAIT_EVENT_HEADER 1
++#define RECV_WAIT_ACL_HEADER 2
++#define RECV_WAIT_SCO_HEADER 3
++#define RECV_WAIT_DATA 4
++
++
++
++/* ======================== Interrupt handling ======================== */
++
++
++static int btuart_write(unsigned int iobase, int fifo_size, __u8 *buf, int len)
++{
++ int actual = 0;
++
++ /* Tx FIFO should be empty */
++ if (!(inb(iobase + UART_LSR) & UART_LSR_THRE))
++ return 0;
++
++ /* Fill FIFO with current frame */
++ while ((fifo_size-- > 0) && (actual < len)) {
++ /* Transmit next byte */
++ outb(buf[actual], iobase + UART_TX);
++ actual++;
++ }
++
++ return actual;
++}
++
++
++static void btuart_write_wakeup(btuart_info_t *info)
++{
++ if (!info) {
++ printk(KERN_WARNING "btuart_cs: Call of write_wakeup for unknown device.\n");
++ return;
++ }
++
++ if (test_and_set_bit(XMIT_SENDING, &(info->tx_state))) {
++ set_bit(XMIT_WAKEUP, &(info->tx_state));
++ return;
++ }
++
++ do {
++ register unsigned int iobase = info->link.io.BasePort1;
++ register struct sk_buff *skb;
++ register int len;
++
++ clear_bit(XMIT_WAKEUP, &(info->tx_state));
++
++ if (!(info->link.state & DEV_PRESENT))
++ return;
++
++ if (!(skb = skb_dequeue(&(info->txq))))
++ break;
++
++ /* Send frame */
++ len = btuart_write(iobase, 16, skb->data, skb->len);
++ set_bit(XMIT_WAKEUP, &(info->tx_state));
++
++ if (len == skb->len) {
++ kfree_skb(skb);
++ } else {
++ skb_pull(skb, len);
++ skb_queue_head(&(info->txq), skb);
++ }
++
++ info->hdev.stat.byte_tx += len;
++
++ } while (test_bit(XMIT_WAKEUP, &(info->tx_state)));
++
++ clear_bit(XMIT_SENDING, &(info->tx_state));
++}
++
++
++static void btuart_receive(btuart_info_t *info)
++{
++ unsigned int iobase;
++ int boguscount = 0;
++
++ if (!info) {
++ printk(KERN_WARNING "btuart_cs: Call of receive for unknown device.\n");
++ return;
++ }
++
++ iobase = info->link.io.BasePort1;
++
++ do {
++ info->hdev.stat.byte_rx++;
++
++ /* Allocate packet */
++ if (info->rx_skb == NULL) {
++ info->rx_state = RECV_WAIT_PACKET_TYPE;
++ info->rx_count = 0;
++ if (!(info->rx_skb = bluez_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC))) {
++ printk(KERN_WARNING "btuart_cs: Can't allocate mem for new packet.\n");
++ return;
++ }
++ }
++
++ if (info->rx_state == RECV_WAIT_PACKET_TYPE) {
++
++ info->rx_skb->dev = (void *)&(info->hdev);
++ info->rx_skb->pkt_type = inb(iobase + UART_RX);
++
++ switch (info->rx_skb->pkt_type) {
++
++ case HCI_EVENT_PKT:
++ info->rx_state = RECV_WAIT_EVENT_HEADER;
++ info->rx_count = HCI_EVENT_HDR_SIZE;
++ break;
++
++ case HCI_ACLDATA_PKT:
++ info->rx_state = RECV_WAIT_ACL_HEADER;
++ info->rx_count = HCI_ACL_HDR_SIZE;
++ break;
++
++ case HCI_SCODATA_PKT:
++ info->rx_state = RECV_WAIT_SCO_HEADER;
++ info->rx_count = HCI_SCO_HDR_SIZE;
++ break;
++
++ default:
++ /* Unknown packet */
++ printk(KERN_WARNING "btuart_cs: Unknown HCI packet with type 0x%02x received.\n", info->rx_skb->pkt_type);
++ info->hdev.stat.err_rx++;
++ clear_bit(HCI_RUNNING, &(info->hdev.flags));
++
++ kfree_skb(info->rx_skb);
++ info->rx_skb = NULL;
++ break;
++
++ }
++
++ } else {
++
++ *skb_put(info->rx_skb, 1) = inb(iobase + UART_RX);
++ info->rx_count--;
++
++ if (info->rx_count == 0) {
++
++ int dlen;
++ hci_event_hdr *eh;
++ hci_acl_hdr *ah;
++ hci_sco_hdr *sh;
++
++
++ switch (info->rx_state) {
++
++ case RECV_WAIT_EVENT_HEADER:
++ eh = (hci_event_hdr *)(info->rx_skb->data);
++ info->rx_state = RECV_WAIT_DATA;
++ info->rx_count = eh->plen;
++ break;
++
++ case RECV_WAIT_ACL_HEADER:
++ ah = (hci_acl_hdr *)(info->rx_skb->data);
++ dlen = __le16_to_cpu(ah->dlen);
++ info->rx_state = RECV_WAIT_DATA;
++ info->rx_count = dlen;
++ break;
++
++ case RECV_WAIT_SCO_HEADER:
++ sh = (hci_sco_hdr *)(info->rx_skb->data);
++ info->rx_state = RECV_WAIT_DATA;
++ info->rx_count = sh->dlen;
++ break;
++
++ case RECV_WAIT_DATA:
++ hci_recv_frame(info->rx_skb);
++ info->rx_skb = NULL;
++ break;
++
++ }
++
++ }
++
++ }
++
++ /* Make sure we don't stay here to long */
++ if (boguscount++ > 16)
++ break;
++
++ } while (inb(iobase + UART_LSR) & UART_LSR_DR);
++}
++
++
++void btuart_interrupt(int irq, void *dev_inst, struct pt_regs *regs)
++{
++ btuart_info_t *info = dev_inst;
++ unsigned int iobase;
++ int boguscount = 0;
++ int iir, lsr;
++
++ if (!info) {
++ printk(KERN_WARNING "btuart_cs: Call of irq %d for unknown device.\n", irq);
++ return;
++ }
++
++ iobase = info->link.io.BasePort1;
++
++ spin_lock(&(info->lock));
++
++ iir = inb(iobase + UART_IIR) & UART_IIR_ID;
++ while (iir) {
++
++ /* Clear interrupt */
++ lsr = inb(iobase + UART_LSR);
++
++ switch (iir) {
++ case UART_IIR_RLSI:
++ printk(KERN_NOTICE "btuart_cs: RLSI\n");
++ break;
++ case UART_IIR_RDI:
++ /* Receive interrupt */
++ btuart_receive(info);
++ break;
++ case UART_IIR_THRI:
++ if (lsr & UART_LSR_THRE) {
++ /* Transmitter ready for data */
++ btuart_write_wakeup(info);
++ }
++ break;
++ default:
++ printk(KERN_NOTICE "btuart_cs: Unhandled IIR=%#x\n", iir);
++ break;
++ }
++
++ /* Make sure we don't stay here to long */
++ if (boguscount++ > 100)
++ break;
++
++ iir = inb(iobase + UART_IIR) & UART_IIR_ID;
++
++ }
++
++ spin_unlock(&(info->lock));
++}
++
++
++static void btuart_change_speed(btuart_info_t *info, unsigned int speed)
++{
++ unsigned long flags;
++ unsigned int iobase;
++ int fcr; /* FIFO control reg */
++ int lcr; /* Line control reg */
++ int divisor;
++
++ if (!info) {
++ printk(KERN_WARNING "btuart_cs: Call of change speed for unknown device.\n");
++ return;
++ }
++
++ iobase = info->link.io.BasePort1;
++
++ spin_lock_irqsave(&(info->lock), flags);
++
++ /* Turn off interrupts */
++ outb(0, iobase + UART_IER);
++
++ divisor = SPEED_MAX / speed;
++
++ fcr = UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT;
++
++ /*
++ * Use trigger level 1 to avoid 3 ms. timeout delay at 9600 bps, and
++ * almost 1,7 ms at 19200 bps. At speeds above that we can just forget
++ * about this timeout since it will always be fast enough.
++ */
++
++ if (speed < 38400)
++ fcr |= UART_FCR_TRIGGER_1;
++ else
++ fcr |= UART_FCR_TRIGGER_14;
++
++ /* Bluetooth cards use 8N1 */
++ lcr = UART_LCR_WLEN8;
++
++ outb(UART_LCR_DLAB | lcr, iobase + UART_LCR); /* Set DLAB */
++ outb(divisor & 0xff, iobase + UART_DLL); /* Set speed */
++ outb(divisor >> 8, iobase + UART_DLM);
++ outb(lcr, iobase + UART_LCR); /* Set 8N1 */
++ outb(fcr, iobase + UART_FCR); /* Enable FIFO's */
++
++ /* Turn on interrups */
++ outb(UART_IER_RLSI | UART_IER_RDI | UART_IER_THRI, iobase + UART_IER);
++
++ spin_unlock_irqrestore(&(info->lock), flags);
++}
++
++
++
++/* ======================== HCI interface ======================== */
++
++
++static int btuart_hci_flush(struct hci_dev *hdev)
++{
++ btuart_info_t *info = (btuart_info_t *)(hdev->driver_data);
++
++ /* Drop TX queue */
++ skb_queue_purge(&(info->txq));
++
++ return 0;
++}
++
++
++static int btuart_hci_open(struct hci_dev *hdev)
++{
++ set_bit(HCI_RUNNING, &(hdev->flags));
++
++ return 0;
++}
++
++
++static int btuart_hci_close(struct hci_dev *hdev)
++{
++ if (!test_and_clear_bit(HCI_RUNNING, &(hdev->flags)))
++ return 0;
++
++ btuart_hci_flush(hdev);
++
++ return 0;
++}
++
++
++static int btuart_hci_send_frame(struct sk_buff *skb)
++{
++ btuart_info_t *info;
++ struct hci_dev *hdev = (struct hci_dev *)(skb->dev);
++
++ if (!hdev) {
++ printk(KERN_WARNING "btuart_cs: Frame for unknown HCI device (hdev=NULL).");
++ return -ENODEV;
++ }
++
++ info = (btuart_info_t *)(hdev->driver_data);
++
++ switch (skb->pkt_type) {
++ case HCI_COMMAND_PKT:
++ hdev->stat.cmd_tx++;
++ break;
++ case HCI_ACLDATA_PKT:
++ hdev->stat.acl_tx++;
++ break;
++ case HCI_SCODATA_PKT:
++ hdev->stat.sco_tx++;
++ break;
++ };
++
++ /* Prepend skb with frame type */
++ memcpy(skb_push(skb, 1), &(skb->pkt_type), 1);
++ skb_queue_tail(&(info->txq), skb);
++
++ btuart_write_wakeup(info);
++
++ return 0;
++}
++
++
++static void btuart_hci_destruct(struct hci_dev *hdev)
++{
++}
++
++
++static int btuart_hci_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg)
++{
++ return -ENOIOCTLCMD;
++}
++
++
++
++/* ======================== Card services HCI interaction ======================== */
++
++
++int btuart_open(btuart_info_t *info)
++{
++ unsigned long flags;
++ unsigned int iobase = info->link.io.BasePort1;
++ struct hci_dev *hdev;
++
++ spin_lock_init(&(info->lock));
++
++ skb_queue_head_init(&(info->txq));
++
++ info->rx_state = RECV_WAIT_PACKET_TYPE;
++ info->rx_count = 0;
++ info->rx_skb = NULL;
++
++ spin_lock_irqsave(&(info->lock), flags);
++
++ /* Reset UART */
++ outb(0, iobase + UART_MCR);
++
++ /* Turn off interrupts */
++ outb(0, iobase + UART_IER);
++
++ /* Initialize UART */
++ outb(UART_LCR_WLEN8, iobase + UART_LCR); /* Reset DLAB */
++ outb((UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2), iobase + UART_MCR);
++
++ /* Turn on interrupts */
++ // outb(UART_IER_RLSI | UART_IER_RDI | UART_IER_THRI, iobase + UART_IER);
++
++ spin_unlock_irqrestore(&(info->lock), flags);
++
++ btuart_change_speed(info, DEFAULT_BAUD_RATE);
++
++ /* Timeout before it is safe to send the first HCI packet */
++ set_current_state(TASK_INTERRUPTIBLE);
++ schedule_timeout(HZ);
++
++
++ /* Initialize and register HCI device */
++
++ hdev = &(info->hdev);
++
++ hdev->type = HCI_PCCARD;
++ hdev->driver_data = info;
++
++ hdev->open = btuart_hci_open;
++ hdev->close = btuart_hci_close;
++ hdev->flush = btuart_hci_flush;
++ hdev->send = btuart_hci_send_frame;
++ hdev->destruct = btuart_hci_destruct;
++ hdev->ioctl = btuart_hci_ioctl;
++
++ if (hci_register_dev(hdev) < 0) {
++ printk(KERN_WARNING "btuart_cs: Can't register HCI device %s.\n", hdev->name);
++ return -ENODEV;
++ }
++
++ return 0;
++}
++
++
++int btuart_close(btuart_info_t *info)
++{
++ unsigned long flags;
++ unsigned int iobase = info->link.io.BasePort1;
++ struct hci_dev *hdev = &(info->hdev);
++
++ if (info->link.state & DEV_CONFIG_PENDING)
++ return -ENODEV;
++
++ btuart_hci_close(hdev);
++
++ spin_lock_irqsave(&(info->lock), flags);
++
++ /* Reset UART */
++ outb(0, iobase + UART_MCR);
++
++ /* Turn off interrupts */
++ outb(0, iobase + UART_IER);
++
++ spin_unlock_irqrestore(&(info->lock), flags);
++
++ if (hci_unregister_dev(hdev) < 0)
++ printk(KERN_WARNING "btuart_cs: Can't unregister HCI device %s.\n", hdev->name);
++
++ return 0;
++}
++
++
++
++/* ======================== Card services ======================== */
++
++
++static void cs_error(client_handle_t handle, int func, int ret)
++{
++ error_info_t err = { func, ret };
++
++ CardServices(ReportError, handle, &err);
++}
++
++
++dev_link_t *btuart_attach(void)
++{
++ btuart_info_t *info;
++ client_reg_t client_reg;
++ dev_link_t *link;
++ int i, ret;
++
++ /* Create new info device */
++ info = kmalloc(sizeof(*info), GFP_KERNEL);
++ if (!info)
++ return NULL;
++ memset(info, 0, sizeof(*info));
++
++ link = &info->link;
++ link->priv = info;
++
++ link->release.function = &btuart_release;
++ link->release.data = (u_long)link;
++ link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
++ link->io.NumPorts1 = 8;
++ link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT;
++ link->irq.IRQInfo1 = IRQ_INFO2_VALID | IRQ_LEVEL_ID;
++
++ if (irq_list[0] == -1)
++ link->irq.IRQInfo2 = irq_mask;
++ else
++ for (i = 0; i < 4; i++)
++ link->irq.IRQInfo2 |= 1 << irq_list[i];
++
++ link->irq.Handler = btuart_interrupt;
++ link->irq.Instance = info;
++
++ link->conf.Attributes = CONF_ENABLE_IRQ;
++ link->conf.Vcc = 50;
++ link->conf.IntType = INT_MEMORY_AND_IO;
++
++ /* Register with Card Services */
++ link->next = dev_list;
++ dev_list = link;
++ client_reg.dev_info = &dev_info;
++ client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE;
++ client_reg.EventMask =
++ CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
++ CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
++ CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
++ client_reg.event_handler = &btuart_event;
++ client_reg.Version = 0x0210;
++ client_reg.event_callback_args.client_data = link;
++
++ ret = CardServices(RegisterClient, &link->handle, &client_reg);
++ if (ret != CS_SUCCESS) {
++ cs_error(link->handle, RegisterClient, ret);
++ btuart_detach(link);
++ return NULL;
++ }
++
++ return link;
++}
++
++
++void btuart_detach(dev_link_t *link)
++{
++ btuart_info_t *info = link->priv;
++ dev_link_t **linkp;
++ int ret;
++
++ /* Locate device structure */
++ for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
++ if (*linkp == link)
++ break;
++
++ if (*linkp == NULL)
++ return;
++
++ del_timer(&link->release);
++ if (link->state & DEV_CONFIG)
++ btuart_release((u_long)link);
++
++ if (link->handle) {
++ ret = CardServices(DeregisterClient, link->handle);
++ if (ret != CS_SUCCESS)
++ cs_error(link->handle, DeregisterClient, ret);
++ }
++
++ /* Unlink device structure, free bits */
++ *linkp = link->next;
++
++ kfree(info);
++}
++
++
++static int get_tuple(int fn, client_handle_t handle, tuple_t *tuple, cisparse_t *parse)
++{
++ int i;
++
++ i = CardServices(fn, handle, tuple);
++ if (i != CS_SUCCESS)
++ return CS_NO_MORE_ITEMS;
++
++ i = CardServices(GetTupleData, handle, tuple);
++ if (i != CS_SUCCESS)
++ return i;
++
++ return CardServices(ParseTuple, handle, tuple, parse);
++}
++
++
++#define first_tuple(a, b, c) get_tuple(GetFirstTuple, a, b, c)
++#define next_tuple(a, b, c) get_tuple(GetNextTuple, a, b, c)
++
++void btuart_config(dev_link_t *link)
++{
++ static ioaddr_t base[5] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, 0x0 };
++ client_handle_t handle = link->handle;
++ btuart_info_t *info = link->priv;
++ tuple_t tuple;
++ u_short buf[256];
++ cisparse_t parse;
++ cistpl_cftable_entry_t *cf = &parse.cftable_entry;
++ config_info_t config;
++ int i, j, try, last_ret, last_fn;
++
++ tuple.TupleData = (cisdata_t *)buf;
++ tuple.TupleOffset = 0;
++ tuple.TupleDataMax = 255;
++ tuple.Attributes = 0;
++
++ /* Get configuration register information */
++ tuple.DesiredTuple = CISTPL_CONFIG;
++ last_ret = first_tuple(handle, &tuple, &parse);
++ if (last_ret != CS_SUCCESS) {
++ last_fn = ParseTuple;
++ goto cs_failed;
++ }
++ link->conf.ConfigBase = parse.config.base;
++ link->conf.Present = parse.config.rmask[0];
++
++ /* Configure card */
++ link->state |= DEV_CONFIG;
++ i = CardServices(GetConfigurationInfo, handle, &config);
++ link->conf.Vcc = config.Vcc;
++
++ /* First pass: look for a config entry that looks normal. */
++ tuple.TupleData = (cisdata_t *) buf;
++ tuple.TupleOffset = 0;
++ tuple.TupleDataMax = 255;
++ tuple.Attributes = 0;
++ tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
++ /* Two tries: without IO aliases, then with aliases */
++ for (try = 0; try < 2; try++) {
++ i = first_tuple(handle, &tuple, &parse);
++ while (i != CS_NO_MORE_ITEMS) {
++ if (i != CS_SUCCESS)
++ goto next_entry;
++ if (cf->vpp1.present & (1 << CISTPL_POWER_VNOM))
++ link->conf.Vpp1 = link->conf.Vpp2 = cf->vpp1.param[CISTPL_POWER_VNOM] / 10000;
++ if ((cf->io.nwin > 0) && (cf->io.win[0].len == 8) && (cf->io.win[0].base != 0)) {
++ link->conf.ConfigIndex = cf->index;
++ link->io.BasePort1 = cf->io.win[0].base;
++ link->io.IOAddrLines = (try == 0) ? 16 : cf->io.flags & CISTPL_IO_LINES_MASK;
++ i = CardServices(RequestIO, link->handle, &link->io);
++ if (i == CS_SUCCESS)
++ goto found_port;
++ }
++next_entry:
++ i = next_tuple(handle, &tuple, &parse);
++ }
++ }
++
++ /* Second pass: try to find an entry that isn't picky about
++ its base address, then try to grab any standard serial port
++ address, and finally try to get any free port. */
++ i = first_tuple(handle, &tuple, &parse);
++ while (i != CS_NO_MORE_ITEMS) {
++ if ((i == CS_SUCCESS) && (cf->io.nwin > 0)
++ && ((cf->io.flags & CISTPL_IO_LINES_MASK) <= 3)) {
++ link->conf.ConfigIndex = cf->index;
++ for (j = 0; j < 5; j++) {
++ link->io.BasePort1 = base[j];
++ link->io.IOAddrLines = base[j] ? 16 : 3;
++ i = CardServices(RequestIO, link->handle, &link->io);
++ if (i == CS_SUCCESS)
++ goto found_port;
++ }
++ }
++ i = next_tuple(handle, &tuple, &parse);
++ }
++
++found_port:
++ if (i != CS_SUCCESS) {
++ printk(KERN_NOTICE "btuart_cs: No usable port range found. Giving up.\n");
++ cs_error(link->handle, RequestIO, i);
++ goto failed;
++ }
++
++ i = CardServices(RequestIRQ, link->handle, &link->irq);
++ if (i != CS_SUCCESS) {
++ cs_error(link->handle, RequestIRQ, i);
++ link->irq.AssignedIRQ = 0;
++ }
++
++ i = CardServices(RequestConfiguration, link->handle, &link->conf);
++ if (i != CS_SUCCESS) {
++ cs_error(link->handle, RequestConfiguration, i);
++ goto failed;
++ }
++
++ MOD_INC_USE_COUNT;
++
++ if (btuart_open(info) != 0)
++ goto failed;
++
++ strcpy(info->node.dev_name, info->hdev.name);
++ link->dev = &info->node;
++ link->state &= ~DEV_CONFIG_PENDING;
++
++ return;
++
++cs_failed:
++ cs_error(link->handle, last_fn, last_ret);
++
++failed:
++ btuart_release((u_long) link);
++}
++
++
++void btuart_release(u_long arg)
++{
++ dev_link_t *link = (dev_link_t *)arg;
++ btuart_info_t *info = link->priv;
++
++ if (link->state & DEV_PRESENT)
++ btuart_close(info);
++
++ MOD_DEC_USE_COUNT;
++
++ link->dev = NULL;
++
++ CardServices(ReleaseConfiguration, link->handle);
++ CardServices(ReleaseIO, link->handle, &link->io);
++ CardServices(ReleaseIRQ, link->handle, &link->irq);
++
++ link->state &= ~DEV_CONFIG;
++}
++
++
++int btuart_event(event_t event, int priority, event_callback_args_t *args)
++{
++ dev_link_t *link = args->client_data;
++ btuart_info_t *info = link->priv;
++
++ switch (event) {
++ case CS_EVENT_CARD_REMOVAL:
++ link->state &= ~DEV_PRESENT;
++ if (link->state & DEV_CONFIG) {
++ btuart_close(info);
++ mod_timer(&link->release, jiffies + HZ / 20);
++ }
++ break;
++ case CS_EVENT_CARD_INSERTION:
++ link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
++ btuart_config(link);
++ break;
++ case CS_EVENT_PM_SUSPEND:
++ link->state |= DEV_SUSPEND;
++ /* Fall through... */
++ case CS_EVENT_RESET_PHYSICAL:
++ if (link->state & DEV_CONFIG)
++ CardServices(ReleaseConfiguration, link->handle);
++ break;
++ case CS_EVENT_PM_RESUME:
++ link->state &= ~DEV_SUSPEND;
++ /* Fall through... */
++ case CS_EVENT_CARD_RESET:
++ if (DEV_OK(link))
++ CardServices(RequestConfiguration, link->handle, &link->conf);
++ break;
++ }
++
++ return 0;
++}
++
++
++
++/* ======================== Module initialization ======================== */
++
++
++int __init init_btuart_cs(void)
++{
++ servinfo_t serv;
++ int err;
++
++ CardServices(GetCardServicesInfo, &serv);
++ if (serv.Revision != CS_RELEASE_CODE) {
++ printk(KERN_NOTICE "btuart_cs: Card Services release does not match!\n");
++ return -1;
++ }
++
++ err = register_pccard_driver(&dev_info, &btuart_attach, &btuart_detach);
++
++ return err;
++}
++
++
++void __exit exit_btuart_cs(void)
++{
++ unregister_pccard_driver(&dev_info);
++
++ while (dev_list != NULL)
++ btuart_detach(dev_list);
++}
++
++
++module_init(init_btuart_cs);
++module_exit(exit_btuart_cs);
++
++EXPORT_NO_SYMBOLS;
+diff -urN linux-2.4.18/drivers/bluetooth/Config.in linux-2.4.18-mh15/drivers/bluetooth/Config.in
+--- linux-2.4.18/drivers/bluetooth/Config.in 2001-09-07 18:28:38.000000000 +0200
++++ linux-2.4.18-mh15/drivers/bluetooth/Config.in 2004-08-01 16:26:23.000000000 +0200
+@@ -1,8 +1,33 @@
++#
++# Bluetooth HCI device drivers configuration
++#
++
+ mainmenu_option next_comment
+ comment 'Bluetooth device drivers'
+
+ dep_tristate 'HCI USB driver' CONFIG_BLUEZ_HCIUSB $CONFIG_BLUEZ $CONFIG_USB
++if [ "$CONFIG_BLUEZ_HCIUSB" != "n" ]; then
++ bool ' SCO (voice) support' CONFIG_BLUEZ_HCIUSB_SCO
++fi
++
+ dep_tristate 'HCI UART driver' CONFIG_BLUEZ_HCIUART $CONFIG_BLUEZ
+-dep_tristate 'HCI VHCI virtual HCI device driver' CONFIG_BLUEZ_HCIVHCI $CONFIG_BLUEZ
++if [ "$CONFIG_BLUEZ_HCIUART" != "n" ]; then
++ bool ' UART (H4) protocol support' CONFIG_BLUEZ_HCIUART_H4
++ bool ' BCSP protocol support' CONFIG_BLUEZ_HCIUART_BCSP
++ dep_bool ' Transmit CRC with every BCSP packet' CONFIG_BLUEZ_HCIUART_BCSP_TXCRC $CONFIG_BLUEZ_HCIUART_BCSP
++fi
++
++dep_tristate 'HCI BlueFRITZ! USB driver' CONFIG_BLUEZ_HCIBFUSB $CONFIG_BLUEZ $CONFIG_USB
++
++dep_tristate 'HCI DTL1 (PC Card) driver' CONFIG_BLUEZ_HCIDTL1 $CONFIG_PCMCIA $CONFIG_BLUEZ
++
++dep_tristate 'HCI BT3C (PC Card) driver' CONFIG_BLUEZ_HCIBT3C $CONFIG_PCMCIA $CONFIG_BLUEZ
++
++dep_tristate 'HCI BlueCard (PC Card) driver' CONFIG_BLUEZ_HCIBLUECARD $CONFIG_PCMCIA $CONFIG_BLUEZ
++
++dep_tristate 'HCI UART (PC Card) driver' CONFIG_BLUEZ_HCIBTUART $CONFIG_PCMCIA $CONFIG_BLUEZ
++
++dep_tristate 'HCI VHCI (Virtual HCI device) driver' CONFIG_BLUEZ_HCIVHCI $CONFIG_BLUEZ
+
+ endmenu
++
+diff -urN linux-2.4.18/drivers/bluetooth/dtl1_cs.c linux-2.4.18-mh15/drivers/bluetooth/dtl1_cs.c
+--- linux-2.4.18/drivers/bluetooth/dtl1_cs.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/drivers/bluetooth/dtl1_cs.c 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,861 @@
++/*
++ *
++ * A driver for Nokia Connectivity Card DTL-1 devices
++ *
++ * Copyright (C) 2001-2002 Marcel Holtmann <marcel@holtmann.org>
++ *
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation;
++ *
++ * Software distributed under the License is distributed on an "AS
++ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
++ * implied. See the License for the specific language governing
++ * rights and limitations under the License.
++ *
++ * The initial developer of the original code is David A. Hinds
++ * <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
++ * are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
++ *
++ */
++
++#include <linux/config.h>
++#include <linux/module.h>
++
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/slab.h>
++#include <linux/types.h>
++#include <linux/sched.h>
++#include <linux/timer.h>
++#include <linux/errno.h>
++#include <linux/ptrace.h>
++#include <linux/ioport.h>
++#include <linux/spinlock.h>
++
++#include <linux/skbuff.h>
++#include <linux/string.h>
++#include <linux/serial.h>
++#include <linux/serial_reg.h>
++#include <asm/system.h>
++#include <asm/bitops.h>
++#include <asm/io.h>
++
++#include <pcmcia/version.h>
++#include <pcmcia/cs_types.h>
++#include <pcmcia/cs.h>
++#include <pcmcia/cistpl.h>
++#include <pcmcia/ciscode.h>
++#include <pcmcia/ds.h>
++#include <pcmcia/cisreg.h>
++
++#include <net/bluetooth/bluetooth.h>
++#include <net/bluetooth/hci_core.h>
++
++
++
++/* ======================== Module parameters ======================== */
++
++
++/* Bit map of interrupts to choose from */
++static u_int irq_mask = 0xffff;
++static int irq_list[4] = { -1 };
++
++MODULE_PARM(irq_mask, "i");
++MODULE_PARM(irq_list, "1-4i");
++
++MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
++MODULE_DESCRIPTION("BlueZ driver for Nokia Connectivity Card DTL-1");
++MODULE_LICENSE("GPL");
++
++
++
++/* ======================== Local structures ======================== */
++
++
++typedef struct dtl1_info_t {
++ dev_link_t link;
++ dev_node_t node;
++
++ struct hci_dev hdev;
++
++ spinlock_t lock; /* For serializing operations */
++
++ unsigned long flowmask; /* HCI flow mask */
++ int ri_latch;
++
++ struct sk_buff_head txq;
++ unsigned long tx_state;
++
++ unsigned long rx_state;
++ unsigned long rx_count;
++ struct sk_buff *rx_skb;
++} dtl1_info_t;
++
++
++void dtl1_config(dev_link_t *link);
++void dtl1_release(u_long arg);
++int dtl1_event(event_t event, int priority, event_callback_args_t *args);
++
++static dev_info_t dev_info = "dtl1_cs";
++
++dev_link_t *dtl1_attach(void);
++void dtl1_detach(dev_link_t *);
++
++static dev_link_t *dev_list = NULL;
++
++
++/* Transmit states */
++#define XMIT_SENDING 1
++#define XMIT_WAKEUP 2
++#define XMIT_WAITING 8
++
++/* Receiver States */
++#define RECV_WAIT_NSH 0
++#define RECV_WAIT_DATA 1
++
++
++typedef struct {
++ u8 type;
++ u8 zero;
++ u16 len;
++} __attribute__ ((packed)) nsh_t; /* Nokia Specific Header */
++
++#define NSHL 4 /* Nokia Specific Header Length */
++
++
++
++/* ======================== Interrupt handling ======================== */
++
++
++static int dtl1_write(unsigned int iobase, int fifo_size, __u8 *buf, int len)
++{
++ int actual = 0;
++
++ /* Tx FIFO should be empty */
++ if (!(inb(iobase + UART_LSR) & UART_LSR_THRE))
++ return 0;
++
++ /* Fill FIFO with current frame */
++ while ((fifo_size-- > 0) && (actual < len)) {
++ /* Transmit next byte */
++ outb(buf[actual], iobase + UART_TX);
++ actual++;
++ }
++
++ return actual;
++}
++
++
++static void dtl1_write_wakeup(dtl1_info_t *info)
++{
++ if (!info) {
++ printk(KERN_WARNING "dtl1_cs: Call of write_wakeup for unknown device.\n");
++ return;
++ }
++
++ if (test_bit(XMIT_WAITING, &(info->tx_state))) {
++ set_bit(XMIT_WAKEUP, &(info->tx_state));
++ return;
++ }
++
++ if (test_and_set_bit(XMIT_SENDING, &(info->tx_state))) {
++ set_bit(XMIT_WAKEUP, &(info->tx_state));
++ return;
++ }
++
++ do {
++ register unsigned int iobase = info->link.io.BasePort1;
++ register struct sk_buff *skb;
++ register int len;
++
++ clear_bit(XMIT_WAKEUP, &(info->tx_state));
++
++ if (!(info->link.state & DEV_PRESENT))
++ return;
++
++ if (!(skb = skb_dequeue(&(info->txq))))
++ break;
++
++ /* Send frame */
++ len = dtl1_write(iobase, 32, skb->data, skb->len);
++
++ if (len == skb->len) {
++ set_bit(XMIT_WAITING, &(info->tx_state));
++ kfree_skb(skb);
++ } else {
++ skb_pull(skb, len);
++ skb_queue_head(&(info->txq), skb);
++ }
++
++ info->hdev.stat.byte_tx += len;
++
++ } while (test_bit(XMIT_WAKEUP, &(info->tx_state)));
++
++ clear_bit(XMIT_SENDING, &(info->tx_state));
++}
++
++
++static void dtl1_control(dtl1_info_t *info, struct sk_buff *skb)
++{
++ u8 flowmask = *(u8 *)skb->data;
++ int i;
++
++ printk(KERN_INFO "dtl1_cs: Nokia control data = ");
++ for (i = 0; i < skb->len; i++) {
++ printk("%02x ", skb->data[i]);
++ }
++ printk("\n");
++
++ /* transition to active state */
++ if (((info->flowmask & 0x07) == 0) && ((flowmask & 0x07) != 0)) {
++ clear_bit(XMIT_WAITING, &(info->tx_state));
++ dtl1_write_wakeup(info);
++ }
++
++ info->flowmask = flowmask;
++
++ kfree_skb(skb);
++}
++
++
++static void dtl1_receive(dtl1_info_t *info)
++{
++ unsigned int iobase;
++ nsh_t *nsh;
++ int boguscount = 0;
++
++ if (!info) {
++ printk(KERN_WARNING "dtl1_cs: Call of receive for unknown device.\n");
++ return;
++ }
++
++ iobase = info->link.io.BasePort1;
++
++ do {
++ info->hdev.stat.byte_rx++;
++
++ /* Allocate packet */
++ if (info->rx_skb == NULL)
++ if (!(info->rx_skb = bluez_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC))) {
++ printk(KERN_WARNING "dtl1_cs: Can't allocate mem for new packet.\n");
++ info->rx_state = RECV_WAIT_NSH;
++ info->rx_count = NSHL;
++ return;
++ }
++
++ *skb_put(info->rx_skb, 1) = inb(iobase + UART_RX);
++ nsh = (nsh_t *)info->rx_skb->data;
++
++ info->rx_count--;
++
++ if (info->rx_count == 0) {
++
++ switch (info->rx_state) {
++ case RECV_WAIT_NSH:
++ info->rx_state = RECV_WAIT_DATA;
++ info->rx_count = nsh->len + (nsh->len & 0x0001);
++ break;
++ case RECV_WAIT_DATA:
++ info->rx_skb->pkt_type = nsh->type;
++
++ /* remove PAD byte if it exists */
++ if (nsh->len & 0x0001) {
++ info->rx_skb->tail--;
++ info->rx_skb->len--;
++ }
++
++ /* remove NSH */
++ skb_pull(info->rx_skb, NSHL);
++
++ switch (info->rx_skb->pkt_type) {
++ case 0x80:
++ /* control data for the Nokia Card */
++ dtl1_control(info, info->rx_skb);
++ break;
++ case 0x82:
++ case 0x83:
++ case 0x84:
++ /* send frame to the HCI layer */
++ info->rx_skb->dev = (void *)&(info->hdev);
++ info->rx_skb->pkt_type &= 0x0f;
++ hci_recv_frame(info->rx_skb);
++ break;
++ default:
++ /* unknown packet */
++ printk(KERN_WARNING "dtl1_cs: Unknown HCI packet with type 0x%02x received.\n", info->rx_skb->pkt_type);
++ kfree_skb(info->rx_skb);
++ break;
++ }
++
++ info->rx_state = RECV_WAIT_NSH;
++ info->rx_count = NSHL;
++ info->rx_skb = NULL;
++ break;
++ }
++
++ }
++
++ /* Make sure we don't stay here to long */
++ if (boguscount++ > 32)
++ break;
++
++ } while (inb(iobase + UART_LSR) & UART_LSR_DR);
++}
++
++
++void dtl1_interrupt(int irq, void *dev_inst, struct pt_regs *regs)
++{
++ dtl1_info_t *info = dev_inst;
++ unsigned int iobase;
++ unsigned char msr;
++ int boguscount = 0;
++ int iir, lsr;
++
++ if (!info) {
++ printk(KERN_WARNING "dtl1_cs: Call of irq %d for unknown device.\n", irq);
++ return;
++ }
++
++ iobase = info->link.io.BasePort1;
++
++ spin_lock(&(info->lock));
++
++ iir = inb(iobase + UART_IIR) & UART_IIR_ID;
++ while (iir) {
++
++ /* Clear interrupt */
++ lsr = inb(iobase + UART_LSR);
++
++ switch (iir) {
++ case UART_IIR_RLSI:
++ printk(KERN_NOTICE "dtl1_cs: RLSI\n");
++ break;
++ case UART_IIR_RDI:
++ /* Receive interrupt */
++ dtl1_receive(info);
++ break;
++ case UART_IIR_THRI:
++ if (lsr & UART_LSR_THRE) {
++ /* Transmitter ready for data */
++ dtl1_write_wakeup(info);
++ }
++ break;
++ default:
++ printk(KERN_NOTICE "dtl1_cs: Unhandled IIR=%#x\n", iir);
++ break;
++ }
++
++ /* Make sure we don't stay here to long */
++ if (boguscount++ > 100)
++ break;
++
++ iir = inb(iobase + UART_IIR) & UART_IIR_ID;
++
++ }
++
++ msr = inb(iobase + UART_MSR);
++
++ if (info->ri_latch ^ (msr & UART_MSR_RI)) {
++ info->ri_latch = msr & UART_MSR_RI;
++ clear_bit(XMIT_WAITING, &(info->tx_state));
++ dtl1_write_wakeup(info);
++ }
++
++ spin_unlock(&(info->lock));
++}
++
++
++
++/* ======================== HCI interface ======================== */
++
++
++static int dtl1_hci_open(struct hci_dev *hdev)
++{
++ set_bit(HCI_RUNNING, &(hdev->flags));
++
++ return 0;
++}
++
++
++static int dtl1_hci_flush(struct hci_dev *hdev)
++{
++ dtl1_info_t *info = (dtl1_info_t *)(hdev->driver_data);
++
++ /* Drop TX queue */
++ skb_queue_purge(&(info->txq));
++
++ return 0;
++}
++
++
++static int dtl1_hci_close(struct hci_dev *hdev)
++{
++ if (!test_and_clear_bit(HCI_RUNNING, &(hdev->flags)))
++ return 0;
++
++ dtl1_hci_flush(hdev);
++
++ return 0;
++}
++
++
++static int dtl1_hci_send_frame(struct sk_buff *skb)
++{
++ dtl1_info_t *info;
++ struct hci_dev *hdev = (struct hci_dev *)(skb->dev);
++ struct sk_buff *s;
++ nsh_t nsh;
++
++ if (!hdev) {
++ printk(KERN_WARNING "dtl1_cs: Frame for unknown HCI device (hdev=NULL).");
++ return -ENODEV;
++ }
++
++ info = (dtl1_info_t *)(hdev->driver_data);
++
++ switch (skb->pkt_type) {
++ case HCI_COMMAND_PKT:
++ hdev->stat.cmd_tx++;
++ nsh.type = 0x81;
++ break;
++ case HCI_ACLDATA_PKT:
++ hdev->stat.acl_tx++;
++ nsh.type = 0x82;
++ break;
++ case HCI_SCODATA_PKT:
++ hdev->stat.sco_tx++;
++ nsh.type = 0x83;
++ break;
++ };
++
++ nsh.zero = 0;
++ nsh.len = skb->len;
++
++ s = bluez_skb_alloc(NSHL + skb->len + 1, GFP_ATOMIC);
++ skb_reserve(s, NSHL);
++ memcpy(skb_put(s, skb->len), skb->data, skb->len);
++ if (skb->len & 0x0001)
++ *skb_put(s, 1) = 0; /* PAD */
++
++ /* Prepend skb with Nokia frame header and queue */
++ memcpy(skb_push(s, NSHL), &nsh, NSHL);
++ skb_queue_tail(&(info->txq), s);
++
++ dtl1_write_wakeup(info);
++
++ kfree_skb(skb);
++
++ return 0;
++}
++
++
++static void dtl1_hci_destruct(struct hci_dev *hdev)
++{
++}
++
++
++static int dtl1_hci_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg)
++{
++ return -ENOIOCTLCMD;
++}
++
++
++
++/* ======================== Card services HCI interaction ======================== */
++
++
++int dtl1_open(dtl1_info_t *info)
++{
++ unsigned long flags;
++ unsigned int iobase = info->link.io.BasePort1;
++ struct hci_dev *hdev;
++
++ spin_lock_init(&(info->lock));
++
++ skb_queue_head_init(&(info->txq));
++
++ info->rx_state = RECV_WAIT_NSH;
++ info->rx_count = NSHL;
++ info->rx_skb = NULL;
++
++ set_bit(XMIT_WAITING, &(info->tx_state));
++
++ spin_lock_irqsave(&(info->lock), flags);
++
++ /* Reset UART */
++ outb(0, iobase + UART_MCR);
++
++ /* Turn off interrupts */
++ outb(0, iobase + UART_IER);
++
++ /* Initialize UART */
++ outb(UART_LCR_WLEN8, iobase + UART_LCR); /* Reset DLAB */
++ outb((UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2), iobase + UART_MCR);
++
++ info->ri_latch = inb(info->link.io.BasePort1 + UART_MSR) & UART_MSR_RI;
++
++ /* Turn on interrupts */
++ outb(UART_IER_RLSI | UART_IER_RDI | UART_IER_THRI, iobase + UART_IER);
++
++ spin_unlock_irqrestore(&(info->lock), flags);
++
++ /* Timeout before it is safe to send the first HCI packet */
++ set_current_state(TASK_INTERRUPTIBLE);
++ schedule_timeout(HZ * 2);
++
++
++ /* Initialize and register HCI device */
++
++ hdev = &(info->hdev);
++
++ hdev->type = HCI_PCCARD;
++ hdev->driver_data = info;
++
++ hdev->open = dtl1_hci_open;
++ hdev->close = dtl1_hci_close;
++ hdev->flush = dtl1_hci_flush;
++ hdev->send = dtl1_hci_send_frame;
++ hdev->destruct = dtl1_hci_destruct;
++ hdev->ioctl = dtl1_hci_ioctl;
++
++ if (hci_register_dev(hdev) < 0) {
++ printk(KERN_WARNING "dtl1_cs: Can't register HCI device %s.\n", hdev->name);
++ return -ENODEV;
++ }
++
++ return 0;
++}
++
++
++int dtl1_close(dtl1_info_t *info)
++{
++ unsigned long flags;
++ unsigned int iobase = info->link.io.BasePort1;
++ struct hci_dev *hdev = &(info->hdev);
++
++ if (info->link.state & DEV_CONFIG_PENDING)
++ return -ENODEV;
++
++ dtl1_hci_close(hdev);
++
++ spin_lock_irqsave(&(info->lock), flags);
++
++ /* Reset UART */
++ outb(0, iobase + UART_MCR);
++
++ /* Turn off interrupts */
++ outb(0, iobase + UART_IER);
++
++ spin_unlock_irqrestore(&(info->lock), flags);
++
++ if (hci_unregister_dev(hdev) < 0)
++ printk(KERN_WARNING "dtl1_cs: Can't unregister HCI device %s.\n", hdev->name);
++
++ return 0;
++}
++
++
++
++/* ======================== Card services ======================== */
++
++
++static void cs_error(client_handle_t handle, int func, int ret)
++{
++ error_info_t err = { func, ret };
++
++ CardServices(ReportError, handle, &err);
++}
++
++
++dev_link_t *dtl1_attach(void)
++{
++ dtl1_info_t *info;
++ client_reg_t client_reg;
++ dev_link_t *link;
++ int i, ret;
++
++ /* Create new info device */
++ info = kmalloc(sizeof(*info), GFP_KERNEL);
++ if (!info)
++ return NULL;
++ memset(info, 0, sizeof(*info));
++
++ link = &info->link;
++ link->priv = info;
++
++ link->release.function = &dtl1_release;
++ link->release.data = (u_long)link;
++ link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
++ link->io.NumPorts1 = 8;
++ link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT;
++ link->irq.IRQInfo1 = IRQ_INFO2_VALID | IRQ_LEVEL_ID;
++
++ if (irq_list[0] == -1)
++ link->irq.IRQInfo2 = irq_mask;
++ else
++ for (i = 0; i < 4; i++)
++ link->irq.IRQInfo2 |= 1 << irq_list[i];
++
++ link->irq.Handler = dtl1_interrupt;
++ link->irq.Instance = info;
++
++ link->conf.Attributes = CONF_ENABLE_IRQ;
++ link->conf.Vcc = 50;
++ link->conf.IntType = INT_MEMORY_AND_IO;
++
++ /* Register with Card Services */
++ link->next = dev_list;
++ dev_list = link;
++ client_reg.dev_info = &dev_info;
++ client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE;
++ client_reg.EventMask =
++ CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
++ CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
++ CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
++ client_reg.event_handler = &dtl1_event;
++ client_reg.Version = 0x0210;
++ client_reg.event_callback_args.client_data = link;
++
++ ret = CardServices(RegisterClient, &link->handle, &client_reg);
++ if (ret != CS_SUCCESS) {
++ cs_error(link->handle, RegisterClient, ret);
++ dtl1_detach(link);
++ return NULL;
++ }
++
++ return link;
++}
++
++
++void dtl1_detach(dev_link_t *link)
++{
++ dtl1_info_t *info = link->priv;
++ dev_link_t **linkp;
++ int ret;
++
++ /* Locate device structure */
++ for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
++ if (*linkp == link)
++ break;
++
++ if (*linkp == NULL)
++ return;
++
++ del_timer(&link->release);
++ if (link->state & DEV_CONFIG)
++ dtl1_release((u_long)link);
++
++ if (link->handle) {
++ ret = CardServices(DeregisterClient, link->handle);
++ if (ret != CS_SUCCESS)
++ cs_error(link->handle, DeregisterClient, ret);
++ }
++
++ /* Unlink device structure, free bits */
++ *linkp = link->next;
++
++ kfree(info);
++}
++
++
++static int get_tuple(int fn, client_handle_t handle, tuple_t *tuple, cisparse_t *parse)
++{
++ int i;
++
++ i = CardServices(fn, handle, tuple);
++ if (i != CS_SUCCESS)
++ return CS_NO_MORE_ITEMS;
++
++ i = CardServices(GetTupleData, handle, tuple);
++ if (i != CS_SUCCESS)
++ return i;
++
++ return CardServices(ParseTuple, handle, tuple, parse);
++}
++
++
++#define first_tuple(a, b, c) get_tuple(GetFirstTuple, a, b, c)
++#define next_tuple(a, b, c) get_tuple(GetNextTuple, a, b, c)
++
++void dtl1_config(dev_link_t *link)
++{
++ client_handle_t handle = link->handle;
++ dtl1_info_t *info = link->priv;
++ tuple_t tuple;
++ u_short buf[256];
++ cisparse_t parse;
++ cistpl_cftable_entry_t *cf = &parse.cftable_entry;
++ config_info_t config;
++ int i, last_ret, last_fn;
++
++ tuple.TupleData = (cisdata_t *)buf;
++ tuple.TupleOffset = 0;
++ tuple.TupleDataMax = 255;
++ tuple.Attributes = 0;
++
++ /* Get configuration register information */
++ tuple.DesiredTuple = CISTPL_CONFIG;
++ last_ret = first_tuple(handle, &tuple, &parse);
++ if (last_ret != CS_SUCCESS) {
++ last_fn = ParseTuple;
++ goto cs_failed;
++ }
++ link->conf.ConfigBase = parse.config.base;
++ link->conf.Present = parse.config.rmask[0];
++
++ /* Configure card */
++ link->state |= DEV_CONFIG;
++ i = CardServices(GetConfigurationInfo, handle, &config);
++ link->conf.Vcc = config.Vcc;
++
++ tuple.TupleData = (cisdata_t *)buf;
++ tuple.TupleOffset = 0;
++ tuple.TupleDataMax = 255;
++ tuple.Attributes = 0;
++ tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
++
++ /* Look for a generic full-sized window */
++ link->io.NumPorts1 = 8;
++ i = first_tuple(handle, &tuple, &parse);
++ while (i != CS_NO_MORE_ITEMS) {
++ if ((i == CS_SUCCESS) && (cf->io.nwin == 1) && (cf->io.win[0].len > 8)) {
++ link->conf.ConfigIndex = cf->index;
++ link->io.BasePort1 = cf->io.win[0].base;
++ link->io.NumPorts1 = cf->io.win[0].len; /*yo */
++ link->io.IOAddrLines = cf->io.flags & CISTPL_IO_LINES_MASK;
++ i = CardServices(RequestIO, link->handle, &link->io);
++ if (i == CS_SUCCESS)
++ break;
++ }
++ i = next_tuple(handle, &tuple, &parse);
++ }
++
++ if (i != CS_SUCCESS) {
++ cs_error(link->handle, RequestIO, i);
++ goto failed;
++ }
++
++ i = CardServices(RequestIRQ, link->handle, &link->irq);
++ if (i != CS_SUCCESS) {
++ cs_error(link->handle, RequestIRQ, i);
++ link->irq.AssignedIRQ = 0;
++ }
++
++ i = CardServices(RequestConfiguration, link->handle, &link->conf);
++ if (i != CS_SUCCESS) {
++ cs_error(link->handle, RequestConfiguration, i);
++ goto failed;
++ }
++
++ MOD_INC_USE_COUNT;
++
++ if (dtl1_open(info) != 0)
++ goto failed;
++
++ strcpy(info->node.dev_name, info->hdev.name);
++ link->dev = &info->node;
++ link->state &= ~DEV_CONFIG_PENDING;
++
++ return;
++
++cs_failed:
++ cs_error(link->handle, last_fn, last_ret);
++
++failed:
++ dtl1_release((u_long)link);
++}
++
++
++void dtl1_release(u_long arg)
++{
++ dev_link_t *link = (dev_link_t *)arg;
++ dtl1_info_t *info = link->priv;
++
++ if (link->state & DEV_PRESENT)
++ dtl1_close(info);
++
++ MOD_DEC_USE_COUNT;
++
++ link->dev = NULL;
++
++ CardServices(ReleaseConfiguration, link->handle);
++ CardServices(ReleaseIO, link->handle, &link->io);
++ CardServices(ReleaseIRQ, link->handle, &link->irq);
++
++ link->state &= ~DEV_CONFIG;
++}
++
++
++int dtl1_event(event_t event, int priority, event_callback_args_t *args)
++{
++ dev_link_t *link = args->client_data;
++ dtl1_info_t *info = link->priv;
++
++ switch (event) {
++ case CS_EVENT_CARD_REMOVAL:
++ link->state &= ~DEV_PRESENT;
++ if (link->state & DEV_CONFIG) {
++ dtl1_close(info);
++ mod_timer(&link->release, jiffies + HZ / 20);
++ }
++ break;
++ case CS_EVENT_CARD_INSERTION:
++ link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
++ dtl1_config(link);
++ break;
++ case CS_EVENT_PM_SUSPEND:
++ link->state |= DEV_SUSPEND;
++ /* Fall through... */
++ case CS_EVENT_RESET_PHYSICAL:
++ if (link->state & DEV_CONFIG)
++ CardServices(ReleaseConfiguration, link->handle);
++ break;
++ case CS_EVENT_PM_RESUME:
++ link->state &= ~DEV_SUSPEND;
++ /* Fall through... */
++ case CS_EVENT_CARD_RESET:
++ if (DEV_OK(link))
++ CardServices(RequestConfiguration, link->handle, &link->conf);
++ break;
++ }
++
++ return 0;
++}
++
++
++
++/* ======================== Module initialization ======================== */
++
++
++int __init init_dtl1_cs(void)
++{
++ servinfo_t serv;
++ int err;
++
++ CardServices(GetCardServicesInfo, &serv);
++ if (serv.Revision != CS_RELEASE_CODE) {
++ printk(KERN_NOTICE "dtl1_cs: Card Services release does not match!\n");
++ return -1;
++ }
++
++ err = register_pccard_driver(&dev_info, &dtl1_attach, &dtl1_detach);
++
++ return err;
++}
++
++
++void __exit exit_dtl1_cs(void)
++{
++ unregister_pccard_driver(&dev_info);
++
++ while (dev_list != NULL)
++ dtl1_detach(dev_list);
++}
++
++
++module_init(init_dtl1_cs);
++module_exit(exit_dtl1_cs);
++
++EXPORT_NO_SYMBOLS;
+diff -urN linux-2.4.18/drivers/bluetooth/hci_bcsp.c linux-2.4.18-mh15/drivers/bluetooth/hci_bcsp.c
+--- linux-2.4.18/drivers/bluetooth/hci_bcsp.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/drivers/bluetooth/hci_bcsp.c 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,710 @@
++/*
++ BlueCore Serial Protocol (BCSP) for Linux Bluetooth stack (BlueZ).
++ Copyright 2002 by Fabrizio Gennari <fabrizio.gennari@philips.com>
++
++ Based on
++ hci_h4.c by Maxim Krasnyansky <maxk@qualcomm.com>
++ ABCSP by Carl Orsborn <cjo@csr.com>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License version 2 as
++ published by the Free Software Foundation;
++
++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++
++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
++ SOFTWARE IS DISCLAIMED.
++*/
++
++/*
++ * $Id$
++ */
++
++#define VERSION "0.1"
++
++#include <linux/config.h>
++#include <linux/module.h>
++
++#include <linux/version.h>
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/sched.h>
++#include <linux/types.h>
++#include <linux/fcntl.h>
++#include <linux/interrupt.h>
++#include <linux/ptrace.h>
++#include <linux/poll.h>
++
++#include <linux/slab.h>
++#include <linux/tty.h>
++#include <linux/errno.h>
++#include <linux/string.h>
++#include <linux/signal.h>
++#include <linux/ioctl.h>
++#include <linux/skbuff.h>
++
++#include <net/bluetooth/bluetooth.h>
++#include <net/bluetooth/hci_core.h>
++#include "hci_uart.h"
++#include "hci_bcsp.h"
++
++#ifndef HCI_UART_DEBUG
++#undef BT_DBG
++#define BT_DBG( A... )
++#undef BT_DMP
++#define BT_DMP( A... )
++#endif
++
++/* ---- BCSP CRC calculation ---- */
++
++/* Table for calculating CRC for polynomial 0x1021, LSB processed first,
++initial value 0xffff, bits shifted in reverse order. */
++
++static const u16 crc_table[] = {
++ 0x0000, 0x1081, 0x2102, 0x3183,
++ 0x4204, 0x5285, 0x6306, 0x7387,
++ 0x8408, 0x9489, 0xa50a, 0xb58b,
++ 0xc60c, 0xd68d, 0xe70e, 0xf78f
++};
++
++/* Initialise the crc calculator */
++#define BCSP_CRC_INIT(x) x = 0xffff
++
++/*
++ Update crc with next data byte
++
++ Implementation note
++ The data byte is treated as two nibbles. The crc is generated
++ in reverse, i.e., bits are fed into the register from the top.
++*/
++static void bcsp_crc_update(u16 *crc, u8 d)
++{
++ u16 reg = *crc;
++
++ reg = (reg >> 4) ^ crc_table[(reg ^ d) & 0x000f];
++ reg = (reg >> 4) ^ crc_table[(reg ^ (d >> 4)) & 0x000f];
++
++ *crc = reg;
++}
++
++/*
++ Get reverse of generated crc
++
++ Implementation note
++ The crc generator (bcsp_crc_init() and bcsp_crc_update())
++ creates a reversed crc, so it needs to be swapped back before
++ being passed on.
++*/
++static u16 bcsp_crc_reverse(u16 crc)
++{
++ u16 b, rev;
++
++ for (b = 0, rev = 0; b < 16; b++) {
++ rev = rev << 1;
++ rev |= (crc & 1);
++ crc = crc >> 1;
++ }
++ return (rev);
++}
++
++/* ---- BCSP core ---- */
++
++static void bcsp_slip_msgdelim(struct sk_buff *skb)
++{
++ const char pkt_delim = 0xc0;
++ memcpy(skb_put(skb, 1), &pkt_delim, 1);
++}
++
++static void bcsp_slip_one_byte(struct sk_buff *skb, u8 c)
++{
++ const char esc_c0[2] = { 0xdb, 0xdc };
++ const char esc_db[2] = { 0xdb, 0xdd };
++
++ switch (c) {
++ case 0xc0:
++ memcpy(skb_put(skb, 2), &esc_c0, 2);
++ break;
++ case 0xdb:
++ memcpy(skb_put(skb, 2), &esc_db, 2);
++ break;
++ default:
++ memcpy(skb_put(skb, 1), &c, 1);
++ }
++}
++
++static int bcsp_enqueue(struct hci_uart *hu, struct sk_buff *skb)
++{
++ struct bcsp_struct *bcsp = hu->priv;
++
++ if (skb->len > 0xFFF) {
++ BT_ERR("Packet too long");
++ kfree_skb(skb);
++ return 0;
++ }
++
++ switch (skb->pkt_type) {
++ case HCI_ACLDATA_PKT:
++ case HCI_COMMAND_PKT:
++ skb_queue_tail(&bcsp->rel, skb);
++ break;
++
++ case HCI_SCODATA_PKT:
++ skb_queue_tail(&bcsp->unrel, skb);
++ break;
++
++ default:
++ BT_ERR("Unknown packet type");
++ kfree_skb(skb);
++ break;
++ }
++ return 0;
++}
++
++static struct sk_buff *bcsp_prepare_pkt(struct bcsp_struct *bcsp, u8 *data,
++ int len, int pkt_type)
++{
++ struct sk_buff *nskb;
++ u8 hdr[4], chan;
++ int rel, i;
++
++#ifdef CONFIG_BLUEZ_HCIUART_BCSP_TXCRC
++ u16 BCSP_CRC_INIT(bcsp_txmsg_crc);
++#endif
++
++ switch (pkt_type) {
++ case HCI_ACLDATA_PKT:
++ chan = 6; /* BCSP ACL channel */
++ rel = 1; /* reliable channel */
++ break;
++ case HCI_COMMAND_PKT:
++ chan = 5; /* BCSP cmd/evt channel */
++ rel = 1; /* reliable channel */
++ break;
++ case HCI_SCODATA_PKT:
++ chan = 7; /* BCSP SCO channel */
++ rel = 0; /* unreliable channel */
++ break;
++ case BCSP_LE_PKT:
++ chan = 1; /* BCSP LE channel */
++ rel = 0; /* unreliable channel */
++ break;
++ case BCSP_ACK_PKT:
++ chan = 0; /* BCSP internal channel */
++ rel = 0; /* unreliable channel */
++ break;
++ default:
++ BT_ERR("Unknown packet type");
++ return NULL;
++ }
++
++ /* Max len of packet: (original len +4(bcsp hdr) +2(crc))*2
++ (because bytes 0xc0 and 0xdb are escaped, worst case is
++ when the packet is all made of 0xc0 and 0xdb :) )
++ + 2 (0xc0 delimiters at start and end). */
++
++ nskb = alloc_skb((len + 6) * 2 + 2, GFP_ATOMIC);
++ if (!nskb)
++ return NULL;
++
++ nskb->pkt_type = pkt_type;
++
++ bcsp_slip_msgdelim(nskb);
++
++ hdr[0] = bcsp->rxseq_txack << 3;
++ bcsp->txack_req = 0;
++ BT_DBG("We request packet no %u to card", bcsp->rxseq_txack);
++
++ if (rel) {
++ hdr[0] |= 0x80 + bcsp->msgq_txseq;
++ BT_DBG("Sending packet with seqno %u", bcsp->msgq_txseq);
++ bcsp->msgq_txseq = ++(bcsp->msgq_txseq) & 0x07;
++ }
++#ifdef CONFIG_BLUEZ_HCIUART_BCSP_TXCRC
++ hdr[0] |= 0x40;
++#endif
++
++ hdr[1] = (len << 4) & 0xFF;
++ hdr[1] |= chan;
++ hdr[2] = len >> 4;
++ hdr[3] = ~(hdr[0] + hdr[1] + hdr[2]);
++
++ /* Put BCSP header */
++ for (i = 0; i < 4; i++) {
++ bcsp_slip_one_byte(nskb, hdr[i]);
++#ifdef CONFIG_BLUEZ_HCIUART_BCSP_TXCRC
++ bcsp_crc_update(&bcsp_txmsg_crc, hdr[i]);
++#endif
++ }
++
++ /* Put payload */
++ for (i = 0; i < len; i++) {
++ bcsp_slip_one_byte(nskb, data[i]);
++#ifdef CONFIG_BLUEZ_HCIUART_BCSP_TXCRC
++ bcsp_crc_update(&bcsp_txmsg_crc, data[i]);
++#endif
++ }
++
++#ifdef CONFIG_BLUEZ_HCIUART_BCSP_TXCRC
++ /* Put CRC */
++ bcsp_txmsg_crc = bcsp_crc_reverse(bcsp_txmsg_crc);
++ bcsp_slip_one_byte(nskb, (u8) ((bcsp_txmsg_crc >> 8) & 0x00ff));
++ bcsp_slip_one_byte(nskb, (u8) (bcsp_txmsg_crc & 0x00ff));
++#endif
++
++ bcsp_slip_msgdelim(nskb);
++ return nskb;
++}
++
++/* This is a rewrite of pkt_avail in ABCSP */
++static struct sk_buff *bcsp_dequeue(struct hci_uart *hu)
++{
++ struct bcsp_struct *bcsp = (struct bcsp_struct *) hu->priv;
++ unsigned long flags;
++ struct sk_buff *skb;
++
++ /* First of all, check for unreliable messages in the queue,
++ since they have priority */
++
++ if ((skb = skb_dequeue(&bcsp->unrel)) != NULL) {
++ struct sk_buff *nskb = bcsp_prepare_pkt(bcsp, skb->data, skb->len, skb->pkt_type);
++ if (nskb) {
++ kfree_skb(skb);
++ return nskb;
++ } else {
++ skb_queue_head(&bcsp->unrel, skb);
++ BT_ERR("Could not dequeue pkt because alloc_skb failed");
++ }
++ }
++
++ /* Now, try to send a reliable pkt. We can only send a
++ reliable packet if the number of packets sent but not yet ack'ed
++ is < than the winsize */
++
++ spin_lock_irqsave(&bcsp->unack.lock, flags);
++
++ if (bcsp->unack.qlen < BCSP_TXWINSIZE && (skb = skb_dequeue(&bcsp->rel)) != NULL) {
++ struct sk_buff *nskb = bcsp_prepare_pkt(bcsp, skb->data, skb->len, skb->pkt_type);
++ if (nskb) {
++ __skb_queue_tail(&bcsp->unack, skb);
++ mod_timer(&bcsp->tbcsp, jiffies + HZ / 4);
++ spin_unlock_irqrestore(&bcsp->unack.lock, flags);
++ return nskb;
++ } else {
++ skb_queue_head(&bcsp->rel, skb);
++ BT_ERR("Could not dequeue pkt because alloc_skb failed");
++ }
++ }
++
++ spin_unlock_irqrestore(&bcsp->unack.lock, flags);
++
++
++ /* We could not send a reliable packet, either because there are
++ none or because there are too many unack'ed pkts. Did we receive
++ any packets we have not acknowledged yet ? */
++
++ if (bcsp->txack_req) {
++ /* if so, craft an empty ACK pkt and send it on BCSP unreliable
++ channel 0 */
++ struct sk_buff *nskb = bcsp_prepare_pkt(bcsp, NULL, 0, BCSP_ACK_PKT);
++ return nskb;
++ }
++
++ /* We have nothing to send */
++ return NULL;
++}
++
++static int bcsp_flush(struct hci_uart *hu)
++{
++ BT_DBG("hu %p", hu);
++ return 0;
++}
++
++/* Remove ack'ed packets */
++static void bcsp_pkt_cull(struct bcsp_struct *bcsp)
++{
++ unsigned long flags;
++ struct sk_buff *skb;
++ int i, pkts_to_be_removed;
++ u8 seqno;
++
++ spin_lock_irqsave(&bcsp->unack.lock, flags);
++
++ pkts_to_be_removed = bcsp->unack.qlen;
++ seqno = bcsp->msgq_txseq;
++
++ while (pkts_to_be_removed) {
++ if (bcsp->rxack == seqno)
++ break;
++ pkts_to_be_removed--;
++ seqno = (seqno - 1) & 0x07;
++ }
++
++ if (bcsp->rxack != seqno)
++ BT_ERR("Peer acked invalid packet");
++
++ BT_DBG("Removing %u pkts out of %u, up to seqno %u",
++ pkts_to_be_removed, bcsp->unack.qlen, (seqno - 1) & 0x07);
++
++ for (i = 0, skb = ((struct sk_buff *) &bcsp->unack)->next; i < pkts_to_be_removed
++ && skb != (struct sk_buff *) &bcsp->unack; i++) {
++ struct sk_buff *nskb;
++
++ nskb = skb->next;
++ __skb_unlink(skb, &bcsp->unack);
++ kfree_skb(skb);
++ skb = nskb;
++ }
++ if (bcsp->unack.qlen == 0)
++ del_timer(&bcsp->tbcsp);
++ spin_unlock_irqrestore(&bcsp->unack.lock, flags);
++
++ if (i != pkts_to_be_removed)
++ BT_ERR("Removed only %u out of %u pkts", i, pkts_to_be_removed);
++}
++
++/* Handle BCSP link-establishment packets. When we
++ detect a "sync" packet, symptom that the BT module has reset,
++ we do nothing :) (yet) */
++static void bcsp_handle_le_pkt(struct hci_uart *hu)
++{
++ struct bcsp_struct *bcsp = hu->priv;
++ u8 conf_pkt[4] = { 0xad, 0xef, 0xac, 0xed };
++ u8 conf_rsp_pkt[4] = { 0xde, 0xad, 0xd0, 0xd0 };
++ u8 sync_pkt[4] = { 0xda, 0xdc, 0xed, 0xed };
++
++ /* spot "conf" pkts and reply with a "conf rsp" pkt */
++ if (bcsp->rx_skb->data[1] >> 4 == 4 && bcsp->rx_skb->data[2] == 0 &&
++ !memcmp(&bcsp->rx_skb->data[4], conf_pkt, 4)) {
++ struct sk_buff *nskb = alloc_skb(4, GFP_ATOMIC);
++
++ BT_DBG("Found a LE conf pkt");
++ if (!nskb)
++ return;
++ memcpy(skb_put(nskb, 4), conf_rsp_pkt, 4);
++ nskb->pkt_type = BCSP_LE_PKT;
++
++ skb_queue_head(&bcsp->unrel, nskb);
++ hci_uart_tx_wakeup(hu);
++ }
++ /* Spot "sync" pkts. If we find one...disaster! */
++ else if (bcsp->rx_skb->data[1] >> 4 == 4 && bcsp->rx_skb->data[2] == 0 &&
++ !memcmp(&bcsp->rx_skb->data[4], sync_pkt, 4)) {
++ BT_ERR("Found a LE sync pkt, card has reset");
++ }
++}
++
++static inline void bcsp_unslip_one_byte(struct bcsp_struct *bcsp, unsigned char byte)
++{
++ const u8 c0 = 0xc0, db = 0xdb;
++
++ switch (bcsp->rx_esc_state) {
++ case BCSP_ESCSTATE_NOESC:
++ switch (byte) {
++ case 0xdb:
++ bcsp->rx_esc_state = BCSP_ESCSTATE_ESC;
++ break;
++ default:
++ memcpy(skb_put(bcsp->rx_skb, 1), &byte, 1);
++ if ((bcsp->rx_skb-> data[0] & 0x40) != 0 &&
++ bcsp->rx_state != BCSP_W4_CRC)
++ bcsp_crc_update(&bcsp->message_crc, byte);
++ bcsp->rx_count--;
++ }
++ break;
++
++ case BCSP_ESCSTATE_ESC:
++ switch (byte) {
++ case 0xdc:
++ memcpy(skb_put(bcsp->rx_skb, 1), &c0, 1);
++ if ((bcsp->rx_skb-> data[0] & 0x40) != 0 &&
++ bcsp->rx_state != BCSP_W4_CRC)
++ bcsp_crc_update(&bcsp-> message_crc, 0xc0);
++ bcsp->rx_esc_state = BCSP_ESCSTATE_NOESC;
++ bcsp->rx_count--;
++ break;
++
++ case 0xdd:
++ memcpy(skb_put(bcsp->rx_skb, 1), &db, 1);
++ if ((bcsp->rx_skb-> data[0] & 0x40) != 0 &&
++ bcsp->rx_state != BCSP_W4_CRC)
++ bcsp_crc_update(&bcsp-> message_crc, 0xdb);
++ bcsp->rx_esc_state = BCSP_ESCSTATE_NOESC;
++ bcsp->rx_count--;
++ break;
++
++ default:
++ BT_ERR ("Invalid byte %02x after esc byte", byte);
++ kfree_skb(bcsp->rx_skb);
++ bcsp->rx_skb = NULL;
++ bcsp->rx_state = BCSP_W4_PKT_DELIMITER;
++ bcsp->rx_count = 0;
++ }
++ }
++}
++
++static inline void bcsp_complete_rx_pkt(struct hci_uart *hu)
++{
++ struct bcsp_struct *bcsp = hu->priv;
++ int pass_up;
++
++ if (bcsp->rx_skb->data[0] & 0x80) { /* reliable pkt */
++ BT_DBG("Received seqno %u from card", bcsp->rxseq_txack);
++ bcsp->rxseq_txack++;
++ bcsp->rxseq_txack %= 0x8;
++ bcsp->txack_req = 1;
++
++ /* If needed, transmit an ack pkt */
++ hci_uart_tx_wakeup(hu);
++ }
++
++ bcsp->rxack = (bcsp->rx_skb->data[0] >> 3) & 0x07;
++ BT_DBG("Request for pkt %u from card", bcsp->rxack);
++
++ bcsp_pkt_cull(bcsp);
++ if ((bcsp->rx_skb->data[1] & 0x0f) == 6 &&
++ bcsp->rx_skb->data[0] & 0x80) {
++ bcsp->rx_skb->pkt_type = HCI_ACLDATA_PKT;
++ pass_up = 1;
++ } else if ((bcsp->rx_skb->data[1] & 0x0f) == 5 &&
++ bcsp->rx_skb->data[0] & 0x80) {
++ bcsp->rx_skb->pkt_type = HCI_EVENT_PKT;
++ pass_up = 1;
++ } else if ((bcsp->rx_skb->data[1] & 0x0f) == 7) {
++ bcsp->rx_skb->pkt_type = HCI_SCODATA_PKT;
++ pass_up = 1;
++ } else if ((bcsp->rx_skb->data[1] & 0x0f) == 1 &&
++ !(bcsp->rx_skb->data[0] & 0x80)) {
++ bcsp_handle_le_pkt(hu);
++ pass_up = 0;
++ } else
++ pass_up = 0;
++
++ if (!pass_up) {
++ if ((bcsp->rx_skb->data[1] & 0x0f) != 0 &&
++ (bcsp->rx_skb->data[1] & 0x0f) != 1) {
++ BT_ERR ("Packet for unknown channel (%u %s)",
++ bcsp->rx_skb->data[1] & 0x0f,
++ bcsp->rx_skb->data[0] & 0x80 ?
++ "reliable" : "unreliable");
++ }
++ kfree_skb(bcsp->rx_skb);
++ } else {
++ /* Pull out BCSP hdr */
++ skb_pull(bcsp->rx_skb, 4);
++
++ hci_recv_frame(bcsp->rx_skb);
++ }
++ bcsp->rx_state = BCSP_W4_PKT_DELIMITER;
++ bcsp->rx_skb = NULL;
++}
++
++/* Recv data */
++static int bcsp_recv(struct hci_uart *hu, void *data, int count)
++{
++ struct bcsp_struct *bcsp = hu->priv;
++ register unsigned char *ptr;
++
++ BT_DBG("hu %p count %d rx_state %ld rx_count %ld",
++ hu, count, bcsp->rx_state, bcsp->rx_count);
++
++ ptr = data;
++ while (count) {
++ if (bcsp->rx_count) {
++ if (*ptr == 0xc0) {
++ BT_ERR("Short BCSP packet");
++ kfree_skb(bcsp->rx_skb);
++ bcsp->rx_state = BCSP_W4_PKT_START;
++ bcsp->rx_count = 0;
++ } else
++ bcsp_unslip_one_byte(bcsp, *ptr);
++
++ ptr++; count--;
++ continue;
++ }
++
++ switch (bcsp->rx_state) {
++ case BCSP_W4_BCSP_HDR:
++ if ((0xff & (u8) ~ (bcsp->rx_skb->data[0] + bcsp->rx_skb->data[1] +
++ bcsp->rx_skb->data[2])) != bcsp->rx_skb->data[3]) {
++ BT_ERR("Error in BCSP hdr checksum");
++ kfree_skb(bcsp->rx_skb);
++ bcsp->rx_state = BCSP_W4_PKT_DELIMITER;
++ bcsp->rx_count = 0;
++ continue;
++ }
++ if (bcsp->rx_skb->data[0] & 0x80 /* reliable pkt */
++ && (bcsp->rx_skb->data[0] & 0x07) != bcsp->rxseq_txack) {
++ BT_ERR ("Out-of-order packet arrived, got %u expected %u",
++ bcsp->rx_skb->data[0] & 0x07, bcsp->rxseq_txack);
++
++ kfree_skb(bcsp->rx_skb);
++ bcsp->rx_state = BCSP_W4_PKT_DELIMITER;
++ bcsp->rx_count = 0;
++ continue;
++ }
++ bcsp->rx_state = BCSP_W4_DATA;
++ bcsp->rx_count = (bcsp->rx_skb->data[1] >> 4) +
++ (bcsp->rx_skb->data[2] << 4); /* May be 0 */
++ continue;
++
++ case BCSP_W4_DATA:
++ if (bcsp->rx_skb->data[0] & 0x40) { /* pkt with crc */
++ bcsp->rx_state = BCSP_W4_CRC;
++ bcsp->rx_count = 2;
++ } else
++ bcsp_complete_rx_pkt(hu);
++ continue;
++
++ case BCSP_W4_CRC:
++ if (bcsp_crc_reverse(bcsp->message_crc) !=
++ (bcsp->rx_skb->data[bcsp->rx_skb->len - 2] << 8) +
++ bcsp->rx_skb->data[bcsp->rx_skb->len - 1]) {
++
++ BT_ERR ("Checksum failed: computed %04x received %04x",
++ bcsp_crc_reverse(bcsp->message_crc),
++ (bcsp->rx_skb-> data[bcsp->rx_skb->len - 2] << 8) +
++ bcsp->rx_skb->data[bcsp->rx_skb->len - 1]);
++
++ kfree_skb(bcsp->rx_skb);
++ bcsp->rx_state = BCSP_W4_PKT_DELIMITER;
++ bcsp->rx_count = 0;
++ continue;
++ }
++ skb_trim(bcsp->rx_skb, bcsp->rx_skb->len - 2);
++ bcsp_complete_rx_pkt(hu);
++ continue;
++
++ case BCSP_W4_PKT_DELIMITER:
++ switch (*ptr) {
++ case 0xc0:
++ bcsp->rx_state = BCSP_W4_PKT_START;
++ break;
++ default:
++ /*BT_ERR("Ignoring byte %02x", *ptr);*/
++ break;
++ }
++ ptr++; count--;
++ break;
++
++ case BCSP_W4_PKT_START:
++ switch (*ptr) {
++ case 0xc0:
++ ptr++; count--;
++ break;
++
++ default:
++ bcsp->rx_state = BCSP_W4_BCSP_HDR;
++ bcsp->rx_count = 4;
++ bcsp->rx_esc_state = BCSP_ESCSTATE_NOESC;
++ BCSP_CRC_INIT(bcsp->message_crc);
++
++ /* Do not increment ptr or decrement count
++ * Allocate packet. Max len of a BCSP pkt=
++ * 0xFFF (payload) +4 (header) +2 (crc) */
++
++ bcsp->rx_skb = bluez_skb_alloc(0x1005, GFP_ATOMIC);
++ if (!bcsp->rx_skb) {
++ BT_ERR("Can't allocate mem for new packet");
++ bcsp->rx_state = BCSP_W4_PKT_DELIMITER;
++ bcsp->rx_count = 0;
++ return 0;
++ }
++ bcsp->rx_skb->dev = (void *) &hu->hdev;
++ break;
++ }
++ break;
++ }
++ }
++ return count;
++}
++
++ /* Arrange to retransmit all messages in the relq. */
++static void bcsp_timed_event(unsigned long arg)
++{
++ struct hci_uart *hu = (struct hci_uart *) arg;
++ struct bcsp_struct *bcsp = (struct bcsp_struct *) hu->priv;
++ struct sk_buff *skb;
++ unsigned long flags;
++
++ BT_DBG("hu %p retransmitting %u pkts", hu, bcsp->unack.qlen);
++
++ spin_lock_irqsave(&bcsp->unack.lock, flags);
++
++ while ((skb = __skb_dequeue_tail(&bcsp->unack)) != NULL) {
++ bcsp->msgq_txseq = (bcsp->msgq_txseq - 1) & 0x07;
++ skb_queue_head(&bcsp->rel, skb);
++ }
++
++ spin_unlock_irqrestore(&bcsp->unack.lock, flags);
++
++ hci_uart_tx_wakeup(hu);
++}
++
++static int bcsp_open(struct hci_uart *hu)
++{
++ struct bcsp_struct *bcsp;
++
++ BT_DBG("hu %p", hu);
++
++ bcsp = kmalloc(sizeof(*bcsp), GFP_ATOMIC);
++ if (!bcsp)
++ return -ENOMEM;
++ memset(bcsp, 0, sizeof(*bcsp));
++
++ hu->priv = bcsp;
++ skb_queue_head_init(&bcsp->unack);
++ skb_queue_head_init(&bcsp->rel);
++ skb_queue_head_init(&bcsp->unrel);
++
++ init_timer(&bcsp->tbcsp);
++ bcsp->tbcsp.function = bcsp_timed_event;
++ bcsp->tbcsp.data = (u_long) hu;
++
++ bcsp->rx_state = BCSP_W4_PKT_DELIMITER;
++
++ return 0;
++}
++
++static int bcsp_close(struct hci_uart *hu)
++{
++ struct bcsp_struct *bcsp = hu->priv;
++ hu->priv = NULL;
++
++ BT_DBG("hu %p", hu);
++
++ skb_queue_purge(&bcsp->unack);
++ skb_queue_purge(&bcsp->rel);
++ skb_queue_purge(&bcsp->unrel);
++ del_timer(&bcsp->tbcsp);
++
++ kfree(bcsp);
++ return 0;
++}
++
++static struct hci_uart_proto bcsp = {
++ id: HCI_UART_BCSP,
++ open: bcsp_open,
++ close: bcsp_close,
++ enqueue: bcsp_enqueue,
++ dequeue: bcsp_dequeue,
++ recv: bcsp_recv,
++ flush: bcsp_flush
++};
++
++int bcsp_init(void)
++{
++ return hci_uart_register_proto(&bcsp);
++}
++
++int bcsp_deinit(void)
++{
++ return hci_uart_unregister_proto(&bcsp);
++}
+diff -urN linux-2.4.18/drivers/bluetooth/hci_bcsp.h linux-2.4.18-mh15/drivers/bluetooth/hci_bcsp.h
+--- linux-2.4.18/drivers/bluetooth/hci_bcsp.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/drivers/bluetooth/hci_bcsp.h 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,70 @@
++/*
++ BlueCore Serial Protocol (BCSP) for Linux Bluetooth stack (BlueZ).
++ Copyright 2002 by Fabrizio Gennari <fabrizio.gennari@philips.com>
++
++ Based on
++ hci_h4.c by Maxim Krasnyansky <maxk@qualcomm.com>
++ ABCSP by Carl Orsborn <cjo@csr.com>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License version 2 as
++ published by the Free Software Foundation;
++
++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++
++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
++ SOFTWARE IS DISCLAIMED.
++*/
++
++/*
++ * $Id$
++ */
++
++#ifndef __HCI_BCSP_H__
++#define __HCI_BCSP_H__
++
++#define BCSP_TXWINSIZE 4
++
++#define BCSP_ACK_PKT 0x05
++#define BCSP_LE_PKT 0x06
++
++struct bcsp_struct {
++ struct sk_buff_head unack; /* Unack'ed packets queue */
++ struct sk_buff_head rel; /* Reliable packets queue */
++ struct sk_buff_head unrel; /* Unreliable packets queue */
++
++ unsigned long rx_count;
++ struct sk_buff *rx_skb;
++ u8 rxseq_txack; /* rxseq == txack. */
++ u8 rxack; /* Last packet sent by us that the peer ack'ed */
++ struct timer_list tbcsp;
++
++ enum {
++ BCSP_W4_PKT_DELIMITER,
++ BCSP_W4_PKT_START,
++ BCSP_W4_BCSP_HDR,
++ BCSP_W4_DATA,
++ BCSP_W4_CRC
++ } rx_state;
++
++ enum {
++ BCSP_ESCSTATE_NOESC,
++ BCSP_ESCSTATE_ESC
++ } rx_esc_state;
++
++ u16 message_crc;
++ u8 txack_req; /* Do we need to send ack's to the peer? */
++
++ /* Reliable packet sequence number - used to assign seq to each rel pkt. */
++ u8 msgq_txseq;
++};
++
++#endif /* __HCI_BCSP_H__ */
+diff -urN linux-2.4.18/drivers/bluetooth/hci_h4.c linux-2.4.18-mh15/drivers/bluetooth/hci_h4.c
+--- linux-2.4.18/drivers/bluetooth/hci_h4.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/drivers/bluetooth/hci_h4.c 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,277 @@
++/*
++ BlueZ - Bluetooth protocol stack for Linux
++ Copyright (C) 2000-2001 Qualcomm Incorporated
++
++ Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License version 2 as
++ published by the Free Software Foundation;
++
++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++
++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
++ SOFTWARE IS DISCLAIMED.
++*/
++
++/*
++ * BlueZ HCI UART(H4) protocol.
++ *
++ * $Id$
++ */
++#define VERSION "1.2"
++
++#include <linux/config.h>
++#include <linux/module.h>
++
++#include <linux/version.h>
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/sched.h>
++#include <linux/types.h>
++#include <linux/fcntl.h>
++#include <linux/interrupt.h>
++#include <linux/ptrace.h>
++#include <linux/poll.h>
++
++#include <linux/slab.h>
++#include <linux/tty.h>
++#include <linux/errno.h>
++#include <linux/string.h>
++#include <linux/signal.h>
++#include <linux/ioctl.h>
++#include <linux/skbuff.h>
++
++#include <net/bluetooth/bluetooth.h>
++#include <net/bluetooth/hci_core.h>
++#include "hci_uart.h"
++#include "hci_h4.h"
++
++#ifndef HCI_UART_DEBUG
++#undef BT_DBG
++#define BT_DBG( A... )
++#undef BT_DMP
++#define BT_DMP( A... )
++#endif
++
++/* Initialize protocol */
++static int h4_open(struct hci_uart *hu)
++{
++ struct h4_struct *h4;
++
++ BT_DBG("hu %p", hu);
++
++ h4 = kmalloc(sizeof(*h4), GFP_ATOMIC);
++ if (!h4)
++ return -ENOMEM;
++ memset(h4, 0, sizeof(*h4));
++
++ skb_queue_head_init(&h4->txq);
++
++ hu->priv = h4;
++ return 0;
++}
++
++/* Flush protocol data */
++static int h4_flush(struct hci_uart *hu)
++{
++ struct h4_struct *h4 = hu->priv;
++
++ BT_DBG("hu %p", hu);
++ skb_queue_purge(&h4->txq);
++ return 0;
++}
++
++/* Close protocol */
++static int h4_close(struct hci_uart *hu)
++{
++ struct h4_struct *h4 = hu->priv;
++ hu->priv = NULL;
++
++ BT_DBG("hu %p", hu);
++
++ skb_queue_purge(&h4->txq);
++ if (h4->rx_skb)
++ kfree_skb(h4->rx_skb);
++
++ hu->priv = NULL;
++ kfree(h4);
++ return 0;
++}
++
++/* Enqueue frame for transmittion (padding, crc, etc) */
++static int h4_enqueue(struct hci_uart *hu, struct sk_buff *skb)
++{
++ struct h4_struct *h4 = hu->priv;
++
++ BT_DBG("hu %p skb %p", hu, skb);
++
++ /* Prepend skb with frame type */
++ memcpy(skb_push(skb, 1), &skb->pkt_type, 1);
++ skb_queue_tail(&h4->txq, skb);
++ return 0;
++}
++
++static inline int h4_check_data_len(struct h4_struct *h4, int len)
++{
++ register int room = skb_tailroom(h4->rx_skb);
++
++ BT_DBG("len %d room %d", len, room);
++ if (!len) {
++ BT_DMP(h4->rx_skb->data, h4->rx_skb->len);
++ hci_recv_frame(h4->rx_skb);
++ } else if (len > room) {
++ BT_ERR("Data length is too large");
++ kfree_skb(h4->rx_skb);
++ } else {
++ h4->rx_state = H4_W4_DATA;
++ h4->rx_count = len;
++ return len;
++ }
++
++ h4->rx_state = H4_W4_PACKET_TYPE;
++ h4->rx_skb = NULL;
++ h4->rx_count = 0;
++ return 0;
++}
++
++/* Recv data */
++static int h4_recv(struct hci_uart *hu, void *data, int count)
++{
++ struct h4_struct *h4 = hu->priv;
++ register char *ptr;
++ hci_event_hdr *eh;
++ hci_acl_hdr *ah;
++ hci_sco_hdr *sh;
++ register int len, type, dlen;
++
++ BT_DBG("hu %p count %d rx_state %ld rx_count %ld",
++ hu, count, h4->rx_state, h4->rx_count);
++
++ ptr = data;
++ while (count) {
++ if (h4->rx_count) {
++ len = MIN(h4->rx_count, count);
++ memcpy(skb_put(h4->rx_skb, len), ptr, len);
++ h4->rx_count -= len; count -= len; ptr += len;
++
++ if (h4->rx_count)
++ continue;
++
++ switch (h4->rx_state) {
++ case H4_W4_DATA:
++ BT_DBG("Complete data");
++
++ BT_DMP(h4->rx_skb->data, h4->rx_skb->len);
++
++ hci_recv_frame(h4->rx_skb);
++
++ h4->rx_state = H4_W4_PACKET_TYPE;
++ h4->rx_skb = NULL;
++ continue;
++
++ case H4_W4_EVENT_HDR:
++ eh = (hci_event_hdr *) h4->rx_skb->data;
++
++ BT_DBG("Event header: evt 0x%2.2x plen %d", eh->evt, eh->plen);
++
++ h4_check_data_len(h4, eh->plen);
++ continue;
++
++ case H4_W4_ACL_HDR:
++ ah = (hci_acl_hdr *) h4->rx_skb->data;
++ dlen = __le16_to_cpu(ah->dlen);
++
++ BT_DBG("ACL header: dlen %d", dlen);
++
++ h4_check_data_len(h4, dlen);
++ continue;
++
++ case H4_W4_SCO_HDR:
++ sh = (hci_sco_hdr *) h4->rx_skb->data;
++
++ BT_DBG("SCO header: dlen %d", sh->dlen);
++
++ h4_check_data_len(h4, sh->dlen);
++ continue;
++ }
++ }
++
++ /* H4_W4_PACKET_TYPE */
++ switch (*ptr) {
++ case HCI_EVENT_PKT:
++ BT_DBG("Event packet");
++ h4->rx_state = H4_W4_EVENT_HDR;
++ h4->rx_count = HCI_EVENT_HDR_SIZE;
++ type = HCI_EVENT_PKT;
++ break;
++
++ case HCI_ACLDATA_PKT:
++ BT_DBG("ACL packet");
++ h4->rx_state = H4_W4_ACL_HDR;
++ h4->rx_count = HCI_ACL_HDR_SIZE;
++ type = HCI_ACLDATA_PKT;
++ break;
++
++ case HCI_SCODATA_PKT:
++ BT_DBG("SCO packet");
++ h4->rx_state = H4_W4_SCO_HDR;
++ h4->rx_count = HCI_SCO_HDR_SIZE;
++ type = HCI_SCODATA_PKT;
++ break;
++
++ default:
++ BT_ERR("Unknown HCI packet type %2.2x", (__u8)*ptr);
++ hu->hdev.stat.err_rx++;
++ ptr++; count--;
++ continue;
++ };
++ ptr++; count--;
++
++ /* Allocate packet */
++ h4->rx_skb = bluez_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC);
++ if (!h4->rx_skb) {
++ BT_ERR("Can't allocate mem for new packet");
++ h4->rx_state = H4_W4_PACKET_TYPE;
++ h4->rx_count = 0;
++ return 0;
++ }
++ h4->rx_skb->dev = (void *) &hu->hdev;
++ h4->rx_skb->pkt_type = type;
++ }
++ return count;
++}
++
++static struct sk_buff *h4_dequeue(struct hci_uart *hu)
++{
++ struct h4_struct *h4 = hu->priv;
++ return skb_dequeue(&h4->txq);
++}
++
++static struct hci_uart_proto h4p = {
++ id: HCI_UART_H4,
++ open: h4_open,
++ close: h4_close,
++ recv: h4_recv,
++ enqueue: h4_enqueue,
++ dequeue: h4_dequeue,
++ flush: h4_flush,
++};
++
++int h4_init(void)
++{
++ return hci_uart_register_proto(&h4p);
++}
++
++int h4_deinit(void)
++{
++ return hci_uart_unregister_proto(&h4p);
++}
+diff -urN linux-2.4.18/drivers/bluetooth/hci_h4.h linux-2.4.18-mh15/drivers/bluetooth/hci_h4.h
+--- linux-2.4.18/drivers/bluetooth/hci_h4.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/drivers/bluetooth/hci_h4.h 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,44 @@
++/*
++ BlueZ - Bluetooth protocol stack for Linux
++ Copyright (C) 2000-2001 Qualcomm Incorporated
++
++ Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License version 2 as
++ published by the Free Software Foundation;
++
++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++
++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
++ SOFTWARE IS DISCLAIMED.
++*/
++
++/*
++ * $Id$
++ */
++
++#ifdef __KERNEL__
++struct h4_struct {
++ unsigned long rx_state;
++ unsigned long rx_count;
++ struct sk_buff *rx_skb;
++ struct sk_buff_head txq;
++};
++
++/* H4 receiver States */
++#define H4_W4_PACKET_TYPE 0
++#define H4_W4_EVENT_HDR 1
++#define H4_W4_ACL_HDR 2
++#define H4_W4_SCO_HDR 3
++#define H4_W4_DATA 4
++
++#endif /* __KERNEL__ */
+diff -urN linux-2.4.18/drivers/bluetooth/hci_ldisc.c linux-2.4.18-mh15/drivers/bluetooth/hci_ldisc.c
+--- linux-2.4.18/drivers/bluetooth/hci_ldisc.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/drivers/bluetooth/hci_ldisc.c 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,579 @@
++/*
++ BlueZ - Bluetooth protocol stack for Linux
++ Copyright (C) 2000-2001 Qualcomm Incorporated
++
++ Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License version 2 as
++ published by the Free Software Foundation;
++
++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++
++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
++ SOFTWARE IS DISCLAIMED.
++*/
++
++/*
++ * BlueZ HCI UART driver.
++ *
++ * $Id$
++ */
++#define VERSION "2.1"
++
++#include <linux/config.h>
++#include <linux/module.h>
++
++#include <linux/version.h>
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/sched.h>
++#include <linux/types.h>
++#include <linux/fcntl.h>
++#include <linux/interrupt.h>
++#include <linux/ptrace.h>
++#include <linux/poll.h>
++
++#include <linux/slab.h>
++#include <linux/tty.h>
++#include <linux/errno.h>
++#include <linux/string.h>
++#include <linux/signal.h>
++#include <linux/ioctl.h>
++#include <linux/skbuff.h>
++
++#include <net/bluetooth/bluetooth.h>
++#include <net/bluetooth/hci_core.h>
++#include "hci_uart.h"
++
++#ifndef HCI_UART_DEBUG
++#undef BT_DBG
++#define BT_DBG( A... )
++#undef BT_DMP
++#define BT_DMP( A... )
++#endif
++
++static struct hci_uart_proto *hup[HCI_UART_MAX_PROTO];
++
++int hci_uart_register_proto(struct hci_uart_proto *p)
++{
++ if (p->id >= HCI_UART_MAX_PROTO)
++ return -EINVAL;
++
++ if (hup[p->id])
++ return -EEXIST;
++
++ hup[p->id] = p;
++ return 0;
++}
++
++int hci_uart_unregister_proto(struct hci_uart_proto *p)
++{
++ if (p->id >= HCI_UART_MAX_PROTO)
++ return -EINVAL;
++
++ if (!hup[p->id])
++ return -EINVAL;
++
++ hup[p->id] = NULL;
++ return 0;
++}
++
++static struct hci_uart_proto *hci_uart_get_proto(unsigned int id)
++{
++ if (id >= HCI_UART_MAX_PROTO)
++ return NULL;
++ return hup[id];
++}
++
++static inline void hci_uart_tx_complete(struct hci_uart *hu, int pkt_type)
++{
++ struct hci_dev *hdev = &hu->hdev;
++
++ /* Update HCI stat counters */
++ switch (pkt_type) {
++ case HCI_COMMAND_PKT:
++ hdev->stat.cmd_tx++;
++ break;
++
++ case HCI_ACLDATA_PKT:
++ hdev->stat.acl_tx++;
++ break;
++
++ case HCI_SCODATA_PKT:
++ hdev->stat.cmd_tx++;
++ break;
++ }
++}
++
++static inline struct sk_buff *hci_uart_dequeue(struct hci_uart *hu)
++{
++ struct sk_buff *skb = hu->tx_skb;
++ if (!skb)
++ skb = hu->proto->dequeue(hu);
++ else
++ hu->tx_skb = NULL;
++ return skb;
++}
++
++int hci_uart_tx_wakeup(struct hci_uart *hu)
++{
++ struct tty_struct *tty = hu->tty;
++ struct hci_dev *hdev = &hu->hdev;
++ struct sk_buff *skb;
++
++ if (test_and_set_bit(HCI_UART_SENDING, &hu->tx_state)) {
++ set_bit(HCI_UART_TX_WAKEUP, &hu->tx_state);
++ return 0;
++ }
++
++ BT_DBG("");
++
++restart:
++ clear_bit(HCI_UART_TX_WAKEUP, &hu->tx_state);
++
++ while ((skb = hci_uart_dequeue(hu))) {
++ int len;
++
++ set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
++ len = tty->driver.write(tty, 0, skb->data, skb->len);
++ hdev->stat.byte_tx += len;
++
++ skb_pull(skb, len);
++ if (skb->len) {
++ hu->tx_skb = skb;
++ break;
++ }
++
++ hci_uart_tx_complete(hu, skb->pkt_type);
++ kfree_skb(skb);
++ }
++
++ if (test_bit(HCI_UART_TX_WAKEUP, &hu->tx_state))
++ goto restart;
++
++ clear_bit(HCI_UART_SENDING, &hu->tx_state);
++ return 0;
++}
++
++/* ------- Interface to HCI layer ------ */
++/* Initialize device */
++static int hci_uart_open(struct hci_dev *hdev)
++{
++ BT_DBG("%s %p", hdev->name, hdev);
++
++ /* Nothing to do for UART driver */
++
++ set_bit(HCI_RUNNING, &hdev->flags);
++ return 0;
++}
++
++/* Reset device */
++static int hci_uart_flush(struct hci_dev *hdev)
++{
++ struct hci_uart *hu = (struct hci_uart *) hdev->driver_data;
++ struct tty_struct *tty = hu->tty;
++
++ BT_DBG("hdev %p tty %p", hdev, tty);
++
++ if (hu->tx_skb) {
++ kfree_skb(hu->tx_skb); hu->tx_skb = NULL;
++ }
++
++ /* Flush any pending characters in the driver and discipline. */
++ if (tty->ldisc.flush_buffer)
++ tty->ldisc.flush_buffer(tty);
++
++ if (tty->driver.flush_buffer)
++ tty->driver.flush_buffer(tty);
++
++ if (test_bit(HCI_UART_PROTO_SET, &hu->flags))
++ hu->proto->flush(hu);
++
++ return 0;
++}
++
++/* Close device */
++static int hci_uart_close(struct hci_dev *hdev)
++{
++ BT_DBG("hdev %p", hdev);
++
++ if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
++ return 0;
++
++ hci_uart_flush(hdev);
++ return 0;
++}
++
++/* Send frames from HCI layer */
++static int hci_uart_send_frame(struct sk_buff *skb)
++{
++ struct hci_dev* hdev = (struct hci_dev *) skb->dev;
++ struct tty_struct *tty;
++ struct hci_uart *hu;
++
++ if (!hdev) {
++ BT_ERR("Frame for uknown device (hdev=NULL)");
++ return -ENODEV;
++ }
++
++ if (!test_bit(HCI_RUNNING, &hdev->flags))
++ return -EBUSY;
++
++ hu = (struct hci_uart *) hdev->driver_data;
++ tty = hu->tty;
++
++ BT_DBG("%s: type %d len %d", hdev->name, skb->pkt_type, skb->len);
++
++ hu->proto->enqueue(hu, skb);
++
++ hci_uart_tx_wakeup(hu);
++ return 0;
++}
++
++static void hci_uart_destruct(struct hci_dev *hdev)
++{
++ struct hci_uart *hu;
++
++ if (!hdev) return;
++
++ BT_DBG("%s", hdev->name);
++
++ hu = (struct hci_uart *) hdev->driver_data;
++ kfree(hu);
++
++ MOD_DEC_USE_COUNT;
++}
++
++/* ------ LDISC part ------ */
++/* hci_uart_tty_open
++ *
++ * Called when line discipline changed to HCI_UART.
++ *
++ * Arguments:
++ * tty pointer to tty info structure
++ * Return Value:
++ * 0 if success, otherwise error code
++ */
++static int hci_uart_tty_open(struct tty_struct *tty)
++{
++ struct hci_uart *hu = (void *) tty->disc_data;
++
++ BT_DBG("tty %p", tty);
++
++ if (hu)
++ return -EEXIST;
++
++ if (!(hu = kmalloc(sizeof(struct hci_uart), GFP_KERNEL))) {
++ BT_ERR("Can't allocate controll structure");
++ return -ENFILE;
++ }
++ memset(hu, 0, sizeof(struct hci_uart));
++
++ tty->disc_data = hu;
++ hu->tty = tty;
++
++ spin_lock_init(&hu->rx_lock);
++
++ /* Flush any pending characters in the driver and line discipline */
++ if (tty->ldisc.flush_buffer)
++ tty->ldisc.flush_buffer(tty);
++
++ if (tty->driver.flush_buffer)
++ tty->driver.flush_buffer(tty);
++
++ MOD_INC_USE_COUNT;
++ return 0;
++}
++
++/* hci_uart_tty_close()
++ *
++ * Called when the line discipline is changed to something
++ * else, the tty is closed, or the tty detects a hangup.
++ */
++static void hci_uart_tty_close(struct tty_struct *tty)
++{
++ struct hci_uart *hu = (void *)tty->disc_data;
++
++ BT_DBG("tty %p", tty);
++
++ /* Detach from the tty */
++ tty->disc_data = NULL;
++
++ if (hu) {
++ struct hci_dev *hdev = &hu->hdev;
++ hci_uart_close(hdev);
++
++ if (test_and_clear_bit(HCI_UART_PROTO_SET, &hu->flags)) {
++ hu->proto->close(hu);
++ hci_unregister_dev(hdev);
++ }
++
++ MOD_DEC_USE_COUNT;
++ }
++}
++
++/* hci_uart_tty_wakeup()
++ *
++ * Callback for transmit wakeup. Called when low level
++ * device driver can accept more send data.
++ *
++ * Arguments: tty pointer to associated tty instance data
++ * Return Value: None
++ */
++static void hci_uart_tty_wakeup(struct tty_struct *tty)
++{
++ struct hci_uart *hu = (void *)tty->disc_data;
++
++ BT_DBG("");
++
++ if (!hu)
++ return;
++
++ clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
++
++ if (tty != hu->tty)
++ return;
++
++ if (test_bit(HCI_UART_PROTO_SET, &hu->flags))
++ hci_uart_tx_wakeup(hu);
++}
++
++/* hci_uart_tty_room()
++ *
++ * Callback function from tty driver. Return the amount of
++ * space left in the receiver's buffer to decide if remote
++ * transmitter is to be throttled.
++ *
++ * Arguments: tty pointer to associated tty instance data
++ * Return Value: number of bytes left in receive buffer
++ */
++static int hci_uart_tty_room (struct tty_struct *tty)
++{
++ return 65536;
++}
++
++/* hci_uart_tty_receive()
++ *
++ * Called by tty low level driver when receive data is
++ * available.
++ *
++ * Arguments: tty pointer to tty isntance data
++ * data pointer to received data
++ * flags pointer to flags for data
++ * count count of received data in bytes
++ *
++ * Return Value: None
++ */
++static void hci_uart_tty_receive(struct tty_struct *tty, const __u8 *data, char *flags, int count)
++{
++ struct hci_uart *hu = (void *)tty->disc_data;
++
++ if (!hu || tty != hu->tty)
++ return;
++
++ if (!test_bit(HCI_UART_PROTO_SET, &hu->flags))
++ return;
++
++ spin_lock(&hu->rx_lock);
++ hu->proto->recv(hu, (void *) data, count);
++ hu->hdev.stat.byte_rx += count;
++ spin_unlock(&hu->rx_lock);
++
++ if (test_and_clear_bit(TTY_THROTTLED,&tty->flags) && tty->driver.unthrottle)
++ tty->driver.unthrottle(tty);
++}
++
++static int hci_uart_register_dev(struct hci_uart *hu)
++{
++ struct hci_dev *hdev;
++
++ BT_DBG("");
++
++ /* Initialize and register HCI device */
++ hdev = &hu->hdev;
++
++ hdev->type = HCI_UART;
++ hdev->driver_data = hu;
++
++ hdev->open = hci_uart_open;
++ hdev->close = hci_uart_close;
++ hdev->flush = hci_uart_flush;
++ hdev->send = hci_uart_send_frame;
++ hdev->destruct = hci_uart_destruct;
++
++ if (hci_register_dev(hdev) < 0) {
++ BT_ERR("Can't register HCI device %s", hdev->name);
++ return -ENODEV;
++ }
++ MOD_INC_USE_COUNT;
++ return 0;
++}
++
++static int hci_uart_set_proto(struct hci_uart *hu, int id)
++{
++ struct hci_uart_proto *p;
++ int err;
++
++ p = hci_uart_get_proto(id);
++ if (!p)
++ return -EPROTONOSUPPORT;
++
++ err = p->open(hu);
++ if (err)
++ return err;
++
++ hu->proto = p;
++
++ err = hci_uart_register_dev(hu);
++ if (err) {
++ p->close(hu);
++ return err;
++ }
++ return 0;
++}
++
++/* hci_uart_tty_ioctl()
++ *
++ * Process IOCTL system call for the tty device.
++ *
++ * Arguments:
++ *
++ * tty pointer to tty instance data
++ * file pointer to open file object for device
++ * cmd IOCTL command code
++ * arg argument for IOCTL call (cmd dependent)
++ *
++ * Return Value: Command dependent
++ */
++static int hci_uart_tty_ioctl(struct tty_struct *tty, struct file * file,
++ unsigned int cmd, unsigned long arg)
++{
++ struct hci_uart *hu = (void *)tty->disc_data;
++ int err = 0;
++
++ BT_DBG("");
++
++ /* Verify the status of the device */
++ if (!hu)
++ return -EBADF;
++
++ switch (cmd) {
++ case HCIUARTSETPROTO:
++ if (!test_and_set_bit(HCI_UART_PROTO_SET, &hu->flags)) {
++ err = hci_uart_set_proto(hu, arg);
++ if (err) {
++ clear_bit(HCI_UART_PROTO_SET, &hu->flags);
++ return err;
++ }
++ tty->low_latency = 1;
++ } else
++ return -EBUSY;
++
++ case HCIUARTGETPROTO:
++ if (test_bit(HCI_UART_PROTO_SET, &hu->flags))
++ return hu->proto->id;
++ return -EUNATCH;
++
++ default:
++ err = n_tty_ioctl(tty, file, cmd, arg);
++ break;
++ };
++
++ return err;
++}
++
++/*
++ * We don't provide read/write/poll interface for user space.
++ */
++static ssize_t hci_uart_tty_read(struct tty_struct *tty, struct file *file, unsigned char *buf, size_t nr)
++{
++ return 0;
++}
++static ssize_t hci_uart_tty_write(struct tty_struct *tty, struct file *file, const unsigned char *data, size_t count)
++{
++ return 0;
++}
++static unsigned int hci_uart_tty_poll(struct tty_struct *tty, struct file *filp, poll_table *wait)
++{
++ return 0;
++}
++
++#ifdef CONFIG_BLUEZ_HCIUART_H4
++int h4_init(void);
++int h4_deinit(void);
++#endif
++#ifdef CONFIG_BLUEZ_HCIUART_BCSP
++int bcsp_init(void);
++int bcsp_deinit(void);
++#endif
++
++int __init hci_uart_init(void)
++{
++ static struct tty_ldisc hci_uart_ldisc;
++ int err;
++
++ BT_INFO("BlueZ HCI UART driver ver %s Copyright (C) 2000,2001 Qualcomm Inc",
++ VERSION);
++ BT_INFO("Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>");
++
++ /* Register the tty discipline */
++
++ memset(&hci_uart_ldisc, 0, sizeof (hci_uart_ldisc));
++ hci_uart_ldisc.magic = TTY_LDISC_MAGIC;
++ hci_uart_ldisc.name = "n_hci";
++ hci_uart_ldisc.open = hci_uart_tty_open;
++ hci_uart_ldisc.close = hci_uart_tty_close;
++ hci_uart_ldisc.read = hci_uart_tty_read;
++ hci_uart_ldisc.write = hci_uart_tty_write;
++ hci_uart_ldisc.ioctl = hci_uart_tty_ioctl;
++ hci_uart_ldisc.poll = hci_uart_tty_poll;
++ hci_uart_ldisc.receive_room= hci_uart_tty_room;
++ hci_uart_ldisc.receive_buf = hci_uart_tty_receive;
++ hci_uart_ldisc.write_wakeup= hci_uart_tty_wakeup;
++
++ if ((err = tty_register_ldisc(N_HCI, &hci_uart_ldisc))) {
++ BT_ERR("Can't register HCI line discipline (%d)", err);
++ return err;
++ }
++
++#ifdef CONFIG_BLUEZ_HCIUART_H4
++ h4_init();
++#endif
++#ifdef CONFIG_BLUEZ_HCIUART_BCSP
++ bcsp_init();
++#endif
++
++ return 0;
++}
++
++void hci_uart_cleanup(void)
++{
++ int err;
++
++#ifdef CONFIG_BLUEZ_HCIUART_H4
++ h4_deinit();
++#endif
++#ifdef CONFIG_BLUEZ_HCIUART_BCSP
++ bcsp_deinit();
++#endif
++
++ /* Release tty registration of line discipline */
++ if ((err = tty_register_ldisc(N_HCI, NULL)))
++ BT_ERR("Can't unregister HCI line discipline (%d)", err);
++}
++
++module_init(hci_uart_init);
++module_exit(hci_uart_cleanup);
++
++MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>");
++MODULE_DESCRIPTION("BlueZ HCI UART driver ver " VERSION);
++MODULE_LICENSE("GPL");
+diff -urN linux-2.4.18/drivers/bluetooth/hci_uart.c linux-2.4.18-mh15/drivers/bluetooth/hci_uart.c
+--- linux-2.4.18/drivers/bluetooth/hci_uart.c 2001-09-07 18:28:38.000000000 +0200
++++ linux-2.4.18-mh15/drivers/bluetooth/hci_uart.c 1970-01-01 01:00:00.000000000 +0100
+@@ -1,580 +0,0 @@
+-/*
+- BlueZ - Bluetooth protocol stack for Linux
+- Copyright (C) 2000-2001 Qualcomm Incorporated
+-
+- Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
+-
+- This program is free software; you can redistribute it and/or modify
+- it under the terms of the GNU General Public License version 2 as
+- published by the Free Software Foundation;
+-
+- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+- OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+- IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+- CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
+- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+-
+- ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
+- COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
+- SOFTWARE IS DISCLAIMED.
+-*/
+-
+-/*
+- * BlueZ HCI UART driver.
+- *
+- * $Id$
+- */
+-#define VERSION "1.0"
+-
+-#include <linux/config.h>
+-#include <linux/module.h>
+-
+-#include <linux/version.h>
+-#include <linux/config.h>
+-#include <linux/kernel.h>
+-#include <linux/init.h>
+-#include <linux/sched.h>
+-#include <linux/types.h>
+-#include <linux/fcntl.h>
+-#include <linux/interrupt.h>
+-#include <linux/ptrace.h>
+-#include <linux/poll.h>
+-
+-#include <linux/slab.h>
+-#include <linux/tty.h>
+-#include <linux/errno.h>
+-#include <linux/string.h>
+-#include <linux/signal.h>
+-#include <linux/ioctl.h>
+-#include <linux/skbuff.h>
+-
+-#include <net/bluetooth/bluetooth.h>
+-#include <net/bluetooth/bluez.h>
+-#include <net/bluetooth/hci_core.h>
+-#include <net/bluetooth/hci_uart.h>
+-
+-#ifndef HCI_UART_DEBUG
+-#undef DBG
+-#define DBG( A... )
+-#undef DMP
+-#define DMP( A... )
+-#endif
+-
+-/* ------- Interface to HCI layer ------ */
+-/* Initialize device */
+-int n_hci_open(struct hci_dev *hdev)
+-{
+- DBG("%s %p", hdev->name, hdev);
+-
+- /* Nothing to do for UART driver */
+-
+- hdev->flags |= HCI_RUNNING;
+-
+- return 0;
+-}
+-
+-/* Reset device */
+-int n_hci_flush(struct hci_dev *hdev)
+-{
+- struct n_hci *n_hci = (struct n_hci *) hdev->driver_data;
+- struct tty_struct *tty = n_hci->tty;
+-
+- DBG("hdev %p tty %p", hdev, tty);
+-
+- /* Drop TX queue */
+- skb_queue_purge(&n_hci->txq);
+-
+- /* Flush any pending characters in the driver and discipline. */
+- if (tty->ldisc.flush_buffer)
+- tty->ldisc.flush_buffer(tty);
+-
+- if (tty->driver.flush_buffer)
+- tty->driver.flush_buffer(tty);
+-
+- return 0;
+-}
+-
+-/* Close device */
+-int n_hci_close(struct hci_dev *hdev)
+-{
+- DBG("hdev %p", hdev);
+-
+- hdev->flags &= ~HCI_RUNNING;
+-
+- n_hci_flush(hdev);
+-
+- return 0;
+-}
+-
+-int n_hci_tx_wakeup(struct n_hci *n_hci)
+-{
+- register struct tty_struct *tty = n_hci->tty;
+-
+- if (test_and_set_bit(TRANS_SENDING, &n_hci->tx_state)) {
+- set_bit(TRANS_WAKEUP, &n_hci->tx_state);
+- return 0;
+- }
+-
+- DBG("");
+- do {
+- register struct sk_buff *skb;
+- register int len;
+-
+- clear_bit(TRANS_WAKEUP, &n_hci->tx_state);
+-
+- if (!(skb = skb_dequeue(&n_hci->txq)))
+- break;
+-
+- DMP(skb->data, skb->len);
+-
+- /* Send frame to TTY driver */
+- tty->flags |= (1 << TTY_DO_WRITE_WAKEUP);
+- len = tty->driver.write(tty, 0, skb->data, skb->len);
+-
+- n_hci->hdev.stat.byte_tx += len;
+-
+- DBG("sent %d", len);
+-
+- if (len == skb->len) {
+- /* Full frame was sent */
+- kfree_skb(skb);
+- } else {
+- /* Subtract sent part and requeue */
+- skb_pull(skb, len);
+- skb_queue_head(&n_hci->txq, skb);
+- }
+- } while (test_bit(TRANS_WAKEUP, &n_hci->tx_state));
+- clear_bit(TRANS_SENDING, &n_hci->tx_state);
+-
+- return 0;
+-}
+-
+-/* Send frames from HCI layer */
+-int n_hci_send_frame(struct sk_buff *skb)
+-{
+- struct hci_dev* hdev = (struct hci_dev *) skb->dev;
+- struct tty_struct *tty;
+- struct n_hci *n_hci;
+-
+- if (!hdev) {
+- ERR("Frame for uknown device (hdev=NULL)");
+- return -ENODEV;
+- }
+-
+- if (!(hdev->flags & HCI_RUNNING))
+- return -EBUSY;
+-
+- n_hci = (struct n_hci *) hdev->driver_data;
+- tty = n_hci2tty(n_hci);
+-
+- DBG("%s: type %d len %d", hdev->name, skb->pkt_type, skb->len);
+-
+- switch (skb->pkt_type) {
+- case HCI_COMMAND_PKT:
+- hdev->stat.cmd_tx++;
+- break;
+-
+- case HCI_ACLDATA_PKT:
+- hdev->stat.acl_tx++;
+- break;
+-
+- case HCI_SCODATA_PKT:
+- hdev->stat.cmd_tx++;
+- break;
+- };
+-
+- /* Prepend skb with frame type and queue */
+- memcpy(skb_push(skb, 1), &skb->pkt_type, 1);
+- skb_queue_tail(&n_hci->txq, skb);
+-
+- n_hci_tx_wakeup(n_hci);
+-
+- return 0;
+-}
+-
+-/* ------ LDISC part ------ */
+-
+-/* n_hci_tty_open
+- *
+- * Called when line discipline changed to N_HCI.
+- *
+- * Arguments:
+- * tty pointer to tty info structure
+- * Return Value:
+- * 0 if success, otherwise error code
+- */
+-static int n_hci_tty_open(struct tty_struct *tty)
+-{
+- struct n_hci *n_hci = tty2n_hci(tty);
+- struct hci_dev *hdev;
+-
+- DBG("tty %p", tty);
+-
+- if (n_hci)
+- return -EEXIST;
+-
+- if (!(n_hci = kmalloc(sizeof(struct n_hci), GFP_KERNEL))) {
+- ERR("Can't allocate controll structure");
+- return -ENFILE;
+- }
+- memset(n_hci, 0, sizeof(struct n_hci));
+-
+- /* Initialize and register HCI device */
+- hdev = &n_hci->hdev;
+-
+- hdev->type = HCI_UART;
+- hdev->driver_data = n_hci;
+-
+- hdev->open = n_hci_open;
+- hdev->close = n_hci_close;
+- hdev->flush = n_hci_flush;
+- hdev->send = n_hci_send_frame;
+-
+- if (hci_register_dev(hdev) < 0) {
+- ERR("Can't register HCI device %s", hdev->name);
+- kfree(n_hci);
+- return -ENODEV;
+- }
+-
+- tty->disc_data = n_hci;
+- n_hci->tty = tty;
+-
+- spin_lock_init(&n_hci->rx_lock);
+- n_hci->rx_state = WAIT_PACKET_TYPE;
+-
+- skb_queue_head_init(&n_hci->txq);
+-
+- MOD_INC_USE_COUNT;
+-
+- /* Flush any pending characters in the driver and discipline. */
+- if (tty->ldisc.flush_buffer)
+- tty->ldisc.flush_buffer(tty);
+-
+- if (tty->driver.flush_buffer)
+- tty->driver.flush_buffer(tty);
+-
+- return 0;
+-}
+-
+-/* n_hci_tty_close()
+- *
+- * Called when the line discipline is changed to something
+- * else, the tty is closed, or the tty detects a hangup.
+- */
+-static void n_hci_tty_close(struct tty_struct *tty)
+-{
+- struct n_hci *n_hci = tty2n_hci(tty);
+- struct hci_dev *hdev = &n_hci->hdev;
+-
+- DBG("tty %p hdev %p", tty, hdev);
+-
+- if (n_hci != NULL) {
+- n_hci_close(hdev);
+-
+- if (hci_unregister_dev(hdev) < 0) {
+- ERR("Can't unregister HCI device %s",hdev->name);
+- }
+-
+- hdev->driver_data = NULL;
+- tty->disc_data = NULL;
+- kfree(n_hci);
+-
+- MOD_DEC_USE_COUNT;
+- }
+-}
+-
+-/* n_hci_tty_wakeup()
+- *
+- * Callback for transmit wakeup. Called when low level
+- * device driver can accept more send data.
+- *
+- * Arguments: tty pointer to associated tty instance data
+- * Return Value: None
+- */
+-static void n_hci_tty_wakeup( struct tty_struct *tty )
+-{
+- struct n_hci *n_hci = tty2n_hci(tty);
+-
+- DBG("");
+-
+- if (!n_hci)
+- return;
+-
+- tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
+-
+- if (tty != n_hci->tty)
+- return;
+-
+- n_hci_tx_wakeup(n_hci);
+-}
+-
+-/* n_hci_tty_room()
+- *
+- * Callback function from tty driver. Return the amount of
+- * space left in the receiver's buffer to decide if remote
+- * transmitter is to be throttled.
+- *
+- * Arguments: tty pointer to associated tty instance data
+- * Return Value: number of bytes left in receive buffer
+- */
+-static int n_hci_tty_room (struct tty_struct *tty)
+-{
+- return 65536;
+-}
+-
+-static inline int n_hci_check_data_len(struct n_hci *n_hci, int len)
+-{
+- register int room = skb_tailroom(n_hci->rx_skb);
+-
+- DBG("len %d room %d", len, room);
+- if (!len) {
+- DMP(n_hci->rx_skb->data, n_hci->rx_skb->len);
+- hci_recv_frame(n_hci->rx_skb);
+- } else if (len > room) {
+- ERR("Data length is to large");
+- kfree_skb(n_hci->rx_skb);
+- n_hci->hdev.stat.err_rx++;
+- } else {
+- n_hci->rx_state = WAIT_DATA;
+- n_hci->rx_count = len;
+- return len;
+- }
+-
+- n_hci->rx_state = WAIT_PACKET_TYPE;
+- n_hci->rx_skb = NULL;
+- n_hci->rx_count = 0;
+- return 0;
+-}
+-
+-static inline void n_hci_rx(struct n_hci *n_hci, const __u8 * data, char *flags, int count)
+-{
+- register const char *ptr;
+- hci_event_hdr *eh;
+- hci_acl_hdr *ah;
+- hci_sco_hdr *sh;
+- register int len, type, dlen;
+-
+- DBG("count %d state %ld rx_count %ld", count, n_hci->rx_state, n_hci->rx_count);
+-
+- n_hci->hdev.stat.byte_rx += count;
+-
+- ptr = data;
+- while (count) {
+- if (n_hci->rx_count) {
+- len = MIN(n_hci->rx_count, count);
+- memcpy(skb_put(n_hci->rx_skb, len), ptr, len);
+- n_hci->rx_count -= len; count -= len; ptr += len;
+-
+- if (n_hci->rx_count)
+- continue;
+-
+- switch (n_hci->rx_state) {
+- case WAIT_DATA:
+- DBG("Complete data");
+-
+- DMP(n_hci->rx_skb->data, n_hci->rx_skb->len);
+-
+- hci_recv_frame(n_hci->rx_skb);
+-
+- n_hci->rx_state = WAIT_PACKET_TYPE;
+- n_hci->rx_skb = NULL;
+- continue;
+-
+- case WAIT_EVENT_HDR:
+- eh = (hci_event_hdr *) n_hci->rx_skb->data;
+-
+- DBG("Event header: evt 0x%2.2x plen %d", eh->evt, eh->plen);
+-
+- n_hci_check_data_len(n_hci, eh->plen);
+- continue;
+-
+- case WAIT_ACL_HDR:
+- ah = (hci_acl_hdr *) n_hci->rx_skb->data;
+- dlen = __le16_to_cpu(ah->dlen);
+-
+- DBG("ACL header: dlen %d", dlen);
+-
+- n_hci_check_data_len(n_hci, dlen);
+- continue;
+-
+- case WAIT_SCO_HDR:
+- sh = (hci_sco_hdr *) n_hci->rx_skb->data;
+-
+- DBG("SCO header: dlen %d", sh->dlen);
+-
+- n_hci_check_data_len(n_hci, sh->dlen);
+- continue;
+- };
+- }
+-
+- /* WAIT_PACKET_TYPE */
+- switch (*ptr) {
+- case HCI_EVENT_PKT:
+- DBG("Event packet");
+- n_hci->rx_state = WAIT_EVENT_HDR;
+- n_hci->rx_count = HCI_EVENT_HDR_SIZE;
+- type = HCI_EVENT_PKT;
+- break;
+-
+- case HCI_ACLDATA_PKT:
+- DBG("ACL packet");
+- n_hci->rx_state = WAIT_ACL_HDR;
+- n_hci->rx_count = HCI_ACL_HDR_SIZE;
+- type = HCI_ACLDATA_PKT;
+- break;
+-
+- case HCI_SCODATA_PKT:
+- DBG("SCO packet");
+- n_hci->rx_state = WAIT_SCO_HDR;
+- n_hci->rx_count = HCI_SCO_HDR_SIZE;
+- type = HCI_SCODATA_PKT;
+- break;
+-
+- default:
+- ERR("Unknown HCI packet type %2.2x", (__u8)*ptr);
+- n_hci->hdev.stat.err_rx++;
+- ptr++; count--;
+- continue;
+- };
+- ptr++; count--;
+-
+- /* Allocate packet */
+- if (!(n_hci->rx_skb = bluez_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC))) {
+- ERR("Can't allocate mem for new packet");
+-
+- n_hci->rx_state = WAIT_PACKET_TYPE;
+- n_hci->rx_count = 0;
+- return;
+- }
+- n_hci->rx_skb->dev = (void *) &n_hci->hdev;
+- n_hci->rx_skb->pkt_type = type;
+- }
+-}
+-
+-/* n_hci_tty_receive()
+- *
+- * Called by tty low level driver when receive data is
+- * available.
+- *
+- * Arguments: tty pointer to tty isntance data
+- * data pointer to received data
+- * flags pointer to flags for data
+- * count count of received data in bytes
+- *
+- * Return Value: None
+- */
+-static void n_hci_tty_receive(struct tty_struct *tty, const __u8 * data, char *flags, int count)
+-{
+- struct n_hci *n_hci = tty2n_hci(tty);
+-
+- if (!n_hci || tty != n_hci->tty)
+- return;
+-
+- spin_lock(&n_hci->rx_lock);
+- n_hci_rx(n_hci, data, flags, count);
+- spin_unlock(&n_hci->rx_lock);
+-
+- if (test_and_clear_bit(TTY_THROTTLED,&tty->flags) && tty->driver.unthrottle)
+- tty->driver.unthrottle(tty);
+-}
+-
+-/* n_hci_tty_ioctl()
+- *
+- * Process IOCTL system call for the tty device.
+- *
+- * Arguments:
+- *
+- * tty pointer to tty instance data
+- * file pointer to open file object for device
+- * cmd IOCTL command code
+- * arg argument for IOCTL call (cmd dependent)
+- *
+- * Return Value: Command dependent
+- */
+-static int n_hci_tty_ioctl (struct tty_struct *tty, struct file * file,
+- unsigned int cmd, unsigned long arg)
+-{
+- struct n_hci *n_hci = tty2n_hci(tty);
+- int error = 0;
+-
+- DBG("");
+-
+- /* Verify the status of the device */
+- if (!n_hci)
+- return -EBADF;
+-
+- switch (cmd) {
+- default:
+- error = n_tty_ioctl(tty, file, cmd, arg);
+- break;
+- };
+-
+- return error;
+-}
+-
+-/*
+- * We don't provide read/write/poll interface for user space.
+- */
+-static ssize_t n_hci_tty_read(struct tty_struct *tty, struct file *file, unsigned char *buf, size_t nr)
+-{
+- return 0;
+-}
+-static ssize_t n_hci_tty_write(struct tty_struct *tty, struct file *file, const unsigned char *data, size_t count)
+-{
+- return 0;
+-}
+-static unsigned int n_hci_tty_poll(struct tty_struct *tty, struct file *filp, poll_table *wait)
+-{
+- return 0;
+-}
+-
+-int __init n_hci_init(void)
+-{
+- static struct tty_ldisc n_hci_ldisc;
+- int err;
+-
+- INF("BlueZ HCI UART driver ver %s Copyright (C) 2000,2001 Qualcomm Inc",
+- VERSION);
+- INF("Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>");
+-
+- /* Register the tty discipline */
+-
+- memset(&n_hci_ldisc, 0, sizeof (n_hci_ldisc));
+- n_hci_ldisc.magic = TTY_LDISC_MAGIC;
+- n_hci_ldisc.name = "n_hci";
+- n_hci_ldisc.open = n_hci_tty_open;
+- n_hci_ldisc.close = n_hci_tty_close;
+- n_hci_ldisc.read = n_hci_tty_read;
+- n_hci_ldisc.write = n_hci_tty_write;
+- n_hci_ldisc.ioctl = n_hci_tty_ioctl;
+- n_hci_ldisc.poll = n_hci_tty_poll;
+- n_hci_ldisc.receive_room= n_hci_tty_room;
+- n_hci_ldisc.receive_buf = n_hci_tty_receive;
+- n_hci_ldisc.write_wakeup= n_hci_tty_wakeup;
+-
+- if ((err = tty_register_ldisc(N_HCI, &n_hci_ldisc))) {
+- ERR("Can't register HCI line discipline (%d)", err);
+- return err;
+- }
+-
+- return 0;
+-}
+-
+-void n_hci_cleanup(void)
+-{
+- int err;
+-
+- /* Release tty registration of line discipline */
+- if ((err = tty_register_ldisc(N_HCI, NULL)))
+- ERR("Can't unregister HCI line discipline (%d)", err);
+-}
+-
+-module_init(n_hci_init);
+-module_exit(n_hci_cleanup);
+-
+-MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>");
+-MODULE_DESCRIPTION("BlueZ HCI UART driver ver " VERSION);
+-MODULE_LICENSE("GPL");
+diff -urN linux-2.4.18/drivers/bluetooth/hci_uart.h linux-2.4.18-mh15/drivers/bluetooth/hci_uart.h
+--- linux-2.4.18/drivers/bluetooth/hci_uart.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/drivers/bluetooth/hci_uart.h 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,82 @@
++/*
++ BlueZ - Bluetooth protocol stack for Linux
++ Copyright (C) 2000-2001 Qualcomm Incorporated
++
++ Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License version 2 as
++ published by the Free Software Foundation;
++
++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++
++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
++ SOFTWARE IS DISCLAIMED.
++*/
++
++/*
++ * $Id$
++ */
++
++#ifndef N_HCI
++#define N_HCI 15
++#endif
++
++/* Ioctls */
++#define HCIUARTSETPROTO _IOW('U', 200, int)
++#define HCIUARTGETPROTO _IOR('U', 201, int)
++
++/* UART protocols */
++#define HCI_UART_MAX_PROTO 4
++
++#define HCI_UART_H4 0
++#define HCI_UART_BCSP 1
++#define HCI_UART_3WIRE 2
++#define HCI_UART_H4DS 3
++
++#ifdef __KERNEL__
++struct hci_uart;
++
++struct hci_uart_proto {
++ unsigned int id;
++ int (*open)(struct hci_uart *hu);
++ int (*close)(struct hci_uart *hu);
++ int (*flush)(struct hci_uart *hu);
++ int (*recv)(struct hci_uart *hu, void *data, int len);
++ int (*enqueue)(struct hci_uart *hu, struct sk_buff *skb);
++ struct sk_buff *(*dequeue)(struct hci_uart *hu);
++};
++
++struct hci_uart {
++ struct tty_struct *tty;
++ struct hci_dev hdev;
++ unsigned long flags;
++
++ struct hci_uart_proto *proto;
++ void *priv;
++
++ struct sk_buff *tx_skb;
++ unsigned long tx_state;
++ spinlock_t rx_lock;
++};
++
++/* HCI_UART flag bits */
++#define HCI_UART_PROTO_SET 0
++
++/* TX states */
++#define HCI_UART_SENDING 1
++#define HCI_UART_TX_WAKEUP 2
++
++int hci_uart_register_proto(struct hci_uart_proto *p);
++int hci_uart_unregister_proto(struct hci_uart_proto *p);
++int hci_uart_tx_wakeup(struct hci_uart *hu);
++
++#endif /* __KERNEL__ */
+diff -urN linux-2.4.18/drivers/bluetooth/hci_usb.c linux-2.4.18-mh15/drivers/bluetooth/hci_usb.c
+--- linux-2.4.18/drivers/bluetooth/hci_usb.c 2001-09-07 18:28:38.000000000 +0200
++++ linux-2.4.18-mh15/drivers/bluetooth/hci_usb.c 2004-08-01 16:26:23.000000000 +0200
+@@ -1,9 +1,10 @@
+ /*
+- BlueZ - Bluetooth protocol stack for Linux
++ HCI USB driver for Linux Bluetooth protocol stack (BlueZ)
+ Copyright (C) 2000-2001 Qualcomm Incorporated
+-
+ Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
+
++ Copyright (C) 2003 Maxim Krasnyansky <maxk@qualcomm.com>
++
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License version 2 as
+ published by the Free Software Foundation;
+@@ -23,598 +24,938 @@
+ */
+
+ /*
+- * BlueZ HCI USB driver.
+ * Based on original USB Bluetooth driver for Linux kernel
+ * Copyright (c) 2000 Greg Kroah-Hartman <greg@kroah.com>
+ * Copyright (c) 2000 Mark Douglas Corner <mcorner@umich.edu>
+ *
+- * $Id$
++ * $Id$
+ */
+-#define VERSION "1.0"
++#define VERSION "2.7"
+
+ #include <linux/config.h>
+ #include <linux/module.h>
+
+ #include <linux/version.h>
+-#include <linux/config.h>
+ #include <linux/kernel.h>
+ #include <linux/init.h>
+ #include <linux/sched.h>
++#include <linux/unistd.h>
+ #include <linux/types.h>
+-#include <linux/fcntl.h>
+ #include <linux/interrupt.h>
+-#include <linux/ptrace.h>
+-#include <linux/poll.h>
+
+ #include <linux/slab.h>
+-#include <linux/tty.h>
+ #include <linux/errno.h>
+ #include <linux/string.h>
+-#include <linux/signal.h>
+-#include <linux/ioctl.h>
+ #include <linux/skbuff.h>
+
+ #include <linux/usb.h>
+
+ #include <net/bluetooth/bluetooth.h>
+-#include <net/bluetooth/bluez.h>
+ #include <net/bluetooth/hci_core.h>
+-#include <net/bluetooth/hci_usb.h>
++
++#include "hci_usb.h"
+
+ #ifndef HCI_USB_DEBUG
+-#undef DBG
+-#define DBG( A... )
+-#undef DMP
+-#define DMP( A... )
++#undef BT_DBG
++#define BT_DBG( A... )
++#undef BT_DMP
++#define BT_DMP( A... )
+ #endif
+
+-static struct usb_device_id usb_bluetooth_ids [] = {
++#ifndef CONFIG_BLUEZ_HCIUSB_ZERO_PACKET
++#undef USB_ZERO_PACKET
++#define USB_ZERO_PACKET 0
++#endif
++
++static struct usb_driver hci_usb_driver;
++
++static struct usb_device_id bluetooth_ids[] = {
++ /* Generic Bluetooth USB device */
+ { USB_DEVICE_INFO(HCI_DEV_CLASS, HCI_DEV_SUBCLASS, HCI_DEV_PROTOCOL) },
++
++ /* AVM BlueFRITZ! USB v2.0 */
++ { USB_DEVICE(0x057c, 0x3800) },
++
++ /* Bluetooth Ultraport Module from IBM */
++ { USB_DEVICE(0x04bf, 0x030a) },
++
++ /* ALPS Modules with non-standard id */
++ { USB_DEVICE(0x044e, 0x3001) },
++ { USB_DEVICE(0x044e, 0x3002) },
++
++ /* Ericsson with non-standard id */
++ { USB_DEVICE(0x0bdb, 0x1002) },
++
+ { } /* Terminating entry */
+ };
+
+-MODULE_DEVICE_TABLE (usb, usb_bluetooth_ids);
++MODULE_DEVICE_TABLE (usb, bluetooth_ids);
+
+-static int hci_usb_ctrl_msg(struct hci_usb *husb, struct sk_buff *skb);
+-static int hci_usb_write_msg(struct hci_usb *husb, struct sk_buff *skb);
++static struct usb_device_id blacklist_ids[] = {
++ /* Broadcom BCM2033 without firmware */
++ { USB_DEVICE(0x0a5c, 0x2033), driver_info: HCI_IGNORE },
+
+-static void hci_usb_unlink_urbs(struct hci_usb *husb)
++ /* Broadcom BCM2035 */
++ { USB_DEVICE(0x0a5c, 0x200a), driver_info: HCI_RESET },
++
++ /* ISSC Bluetooth Adapter v3.1 */
++ { USB_DEVICE(0x1131, 0x1001), driver_info: HCI_RESET },
++
++ /* Digianswer device */
++ { USB_DEVICE(0x08fd, 0x0001), driver_info: HCI_DIGIANSWER },
++
++ /* RTX Telecom based adapter with buggy SCO support */
++ { USB_DEVICE(0x0400, 0x0807), driver_info: HCI_BROKEN_ISOC },
++
++ { } /* Terminating entry */
++};
++
++struct _urb *_urb_alloc(int isoc, int gfp)
+ {
+- usb_unlink_urb(husb->read_urb);
+- usb_unlink_urb(husb->intr_urb);
+- usb_unlink_urb(husb->ctrl_urb);
+- usb_unlink_urb(husb->write_urb);
++ struct _urb *_urb = kmalloc(sizeof(struct _urb) +
++ sizeof(iso_packet_descriptor_t) * isoc, gfp);
++ if (_urb) {
++ memset(_urb, 0, sizeof(*_urb));
++ spin_lock_init(&_urb->urb.lock);
++ }
++ return _urb;
++}
++
++struct _urb *_urb_dequeue(struct _urb_queue *q)
++{
++ struct _urb *_urb = NULL;
++ unsigned long flags;
++ spin_lock_irqsave(&q->lock, flags);
++ {
++ struct list_head *head = &q->head;
++ struct list_head *next = head->next;
++ if (next != head) {
++ _urb = list_entry(next, struct _urb, list);
++ list_del(next); _urb->queue = NULL;
++ }
++ }
++ spin_unlock_irqrestore(&q->lock, flags);
++ return _urb;
+ }
+
+-static void hci_usb_free_bufs(struct hci_usb *husb)
++static void hci_usb_rx_complete(struct urb *urb);
++static void hci_usb_tx_complete(struct urb *urb);
++
++#define __pending_tx(husb, type) (&husb->pending_tx[type-1])
++#define __pending_q(husb, type) (&husb->pending_q[type-1])
++#define __completed_q(husb, type) (&husb->completed_q[type-1])
++#define __transmit_q(husb, type) (&husb->transmit_q[type-1])
++#define __reassembly(husb, type) (husb->reassembly[type-1])
++
++static inline struct _urb *__get_completed(struct hci_usb *husb, int type)
+ {
+- if (husb->read_urb) {
+- if (husb->read_urb->transfer_buffer)
+- kfree(husb->read_urb->transfer_buffer);
+- usb_free_urb(husb->read_urb);
+- }
++ return _urb_dequeue(__completed_q(husb, type));
++}
+
+- if (husb->intr_urb) {
+- if (husb->intr_urb->transfer_buffer)
+- kfree(husb->intr_urb->transfer_buffer);
+- usb_free_urb(husb->intr_urb);
++#ifdef CONFIG_BLUEZ_HCIUSB_SCO
++static void __fill_isoc_desc(struct urb *urb, int len, int mtu)
++{
++ int offset = 0, i;
++
++ BT_DBG("len %d mtu %d", len, mtu);
++
++ for (i=0; i < HCI_MAX_ISOC_FRAMES && len >= mtu; i++, offset += mtu, len -= mtu) {
++ urb->iso_frame_desc[i].offset = offset;
++ urb->iso_frame_desc[i].length = mtu;
++ BT_DBG("desc %d offset %d len %d", i, offset, mtu);
++ }
++ if (len && i < HCI_MAX_ISOC_FRAMES) {
++ urb->iso_frame_desc[i].offset = offset;
++ urb->iso_frame_desc[i].length = len;
++ BT_DBG("desc %d offset %d len %d", i, offset, len);
++ i++;
+ }
++ urb->number_of_packets = i;
++}
++#endif
+
+- if (husb->ctrl_urb)
+- usb_free_urb(husb->ctrl_urb);
++static int hci_usb_intr_rx_submit(struct hci_usb *husb)
++{
++ struct _urb *_urb;
++ struct urb *urb;
++ int err, pipe, interval, size;
++ void *buf;
++
++ BT_DBG("%s", husb->hdev.name);
++
++ size = husb->intr_in_ep->wMaxPacketSize;
++
++ buf = kmalloc(size, GFP_ATOMIC);
++ if (!buf)
++ return -ENOMEM;
++
++ _urb = _urb_alloc(0, GFP_ATOMIC);
++ if (!_urb) {
++ kfree(buf);
++ return -ENOMEM;
++ }
++ _urb->type = HCI_EVENT_PKT;
++ _urb_queue_tail(__pending_q(husb, _urb->type), _urb);
++
++ urb = &_urb->urb;
++ pipe = usb_rcvintpipe(husb->udev, husb->intr_in_ep->bEndpointAddress);
++ interval = husb->intr_in_ep->bInterval;
++ FILL_INT_URB(urb, husb->udev, pipe, buf, size, hci_usb_rx_complete, husb, interval);
++
++ err = usb_submit_urb(urb);
++ if (err) {
++ BT_ERR("%s intr rx submit failed urb %p err %d",
++ husb->hdev.name, urb, err);
++ _urb_unlink(_urb);
++ _urb_free(_urb);
++ kfree(buf);
++ }
++ return err;
++}
+
+- if (husb->write_urb)
+- usb_free_urb(husb->write_urb);
++static int hci_usb_bulk_rx_submit(struct hci_usb *husb)
++{
++ struct _urb *_urb;
++ struct urb *urb;
++ int err, pipe, size = HCI_MAX_FRAME_SIZE;
++ void *buf;
++
++ buf = kmalloc(size, GFP_ATOMIC);
++ if (!buf)
++ return -ENOMEM;
++
++ _urb = _urb_alloc(0, GFP_ATOMIC);
++ if (!_urb) {
++ kfree(buf);
++ return -ENOMEM;
++ }
++ _urb->type = HCI_ACLDATA_PKT;
++ _urb_queue_tail(__pending_q(husb, _urb->type), _urb);
++
++ urb = &_urb->urb;
++ pipe = usb_rcvbulkpipe(husb->udev, husb->bulk_in_ep->bEndpointAddress);
++ FILL_BULK_URB(urb, husb->udev, pipe, buf, size, hci_usb_rx_complete, husb);
++ urb->transfer_flags = USB_QUEUE_BULK;
++
++ BT_DBG("%s urb %p", husb->hdev.name, urb);
++
++ err = usb_submit_urb(urb);
++ if (err) {
++ BT_ERR("%s bulk rx submit failed urb %p err %d",
++ husb->hdev.name, urb, err);
++ _urb_unlink(_urb);
++ _urb_free(_urb);
++ kfree(buf);
++ }
++ return err;
++}
+
+- if (husb->intr_skb)
+- kfree_skb(husb->intr_skb);
++#ifdef CONFIG_BLUEZ_HCIUSB_SCO
++static int hci_usb_isoc_rx_submit(struct hci_usb *husb)
++{
++ struct _urb *_urb;
++ struct urb *urb;
++ int err, mtu, size;
++ void *buf;
++
++ mtu = husb->isoc_in_ep->wMaxPacketSize;
++ size = mtu * HCI_MAX_ISOC_FRAMES;
++
++ buf = kmalloc(size, GFP_ATOMIC);
++ if (!buf)
++ return -ENOMEM;
++
++ _urb = _urb_alloc(HCI_MAX_ISOC_FRAMES, GFP_ATOMIC);
++ if (!_urb) {
++ kfree(buf);
++ return -ENOMEM;
++ }
++ _urb->type = HCI_SCODATA_PKT;
++ _urb_queue_tail(__pending_q(husb, _urb->type), _urb);
++
++ urb = &_urb->urb;
++
++ urb->context = husb;
++ urb->dev = husb->udev;
++ urb->pipe = usb_rcvisocpipe(husb->udev, husb->isoc_in_ep->bEndpointAddress);
++ urb->complete = hci_usb_rx_complete;
++
++ urb->transfer_buffer_length = size;
++ urb->transfer_buffer = buf;
++ urb->transfer_flags = USB_ISO_ASAP;
++
++ __fill_isoc_desc(urb, size, mtu);
++
++ BT_DBG("%s urb %p", husb->hdev.name, urb);
++
++ err = usb_submit_urb(urb);
++ if (err) {
++ BT_ERR("%s isoc rx submit failed urb %p err %d",
++ husb->hdev.name, urb, err);
++ _urb_unlink(_urb);
++ _urb_free(_urb);
++ kfree(buf);
++ }
++ return err;
+ }
++#endif
+
+-/* ------- Interface to HCI layer ------ */
+ /* Initialize device */
+-int hci_usb_open(struct hci_dev *hdev)
++static int hci_usb_open(struct hci_dev *hdev)
+ {
+ struct hci_usb *husb = (struct hci_usb *) hdev->driver_data;
+- int status;
++ int i, err;
++ unsigned long flags;
++
++ BT_DBG("%s", hdev->name);
+
+- DBG("%s", hdev->name);
++ if (test_and_set_bit(HCI_RUNNING, &hdev->flags))
++ return 0;
+
+- husb->read_urb->dev = husb->udev;
+- if ((status = usb_submit_urb(husb->read_urb)))
+- DBG("read submit failed. %d", status);
++ MOD_INC_USE_COUNT;
+
+- husb->intr_urb->dev = husb->udev;
+- if ((status = usb_submit_urb(husb->intr_urb)))
+- DBG("interrupt submit failed. %d", status);
++ write_lock_irqsave(&husb->completion_lock, flags);
+
+- hdev->flags |= HCI_RUNNING;
++ err = hci_usb_intr_rx_submit(husb);
++ if (!err) {
++ for (i = 0; i < HCI_MAX_BULK_RX; i++)
++ hci_usb_bulk_rx_submit(husb);
++
++#ifdef CONFIG_BLUEZ_HCIUSB_SCO
++ if (husb->isoc_iface)
++ for (i = 0; i < HCI_MAX_ISOC_RX; i++)
++ hci_usb_isoc_rx_submit(husb);
++#endif
++ } else {
++ clear_bit(HCI_RUNNING, &hdev->flags);
++ MOD_DEC_USE_COUNT;
++ }
+
+- return 0;
++ write_unlock_irqrestore(&husb->completion_lock, flags);
++ return err;
+ }
+
+ /* Reset device */
+-int hci_usb_flush(struct hci_dev *hdev)
++static int hci_usb_flush(struct hci_dev *hdev)
+ {
+ struct hci_usb *husb = (struct hci_usb *) hdev->driver_data;
++ int i;
+
+- DBG("%s", hdev->name);
+-
+- /* Drop TX queues */
+- skb_queue_purge(&husb->tx_ctrl_q);
+- skb_queue_purge(&husb->tx_write_q);
++ BT_DBG("%s", hdev->name);
+
++ for (i=0; i < 4; i++)
++ skb_queue_purge(&husb->transmit_q[i]);
+ return 0;
+ }
+
+-/* Close device */
+-int hci_usb_close(struct hci_dev *hdev)
++static void hci_usb_unlink_urbs(struct hci_usb *husb)
+ {
+- struct hci_usb *husb = (struct hci_usb *) hdev->driver_data;
++ int i;
+
+- DBG("%s", hdev->name);
++ BT_DBG("%s", husb->hdev.name);
+
+- hdev->flags &= ~HCI_RUNNING;
+- hci_usb_unlink_urbs(husb);
++ for (i=0; i < 4; i++) {
++ struct _urb *_urb;
++ struct urb *urb;
++
++ /* Kill pending requests */
++ while ((_urb = _urb_dequeue(&husb->pending_q[i]))) {
++ urb = &_urb->urb;
++ BT_DBG("%s unlinking _urb %p type %d urb %p",
++ husb->hdev.name, _urb, _urb->type, urb);
++ usb_unlink_urb(urb);
++ _urb_queue_tail(__completed_q(husb, _urb->type), _urb);
++ }
+
+- hci_usb_flush(hdev);
++ /* Release completed requests */
++ while ((_urb = _urb_dequeue(&husb->completed_q[i]))) {
++ urb = &_urb->urb;
++ BT_DBG("%s freeing _urb %p type %d urb %p",
++ husb->hdev.name, _urb, _urb->type, urb);
++ if (urb->setup_packet)
++ kfree(urb->setup_packet);
++ if (urb->transfer_buffer)
++ kfree(urb->transfer_buffer);
++ _urb_free(_urb);
++ }
+
+- return 0;
++ /* Release reassembly buffers */
++ if (husb->reassembly[i]) {
++ kfree_skb(husb->reassembly[i]);
++ husb->reassembly[i] = NULL;
++ }
++ }
+ }
+
+-void hci_usb_ctrl_wakeup(struct hci_usb *husb)
++/* Close device */
++static int hci_usb_close(struct hci_dev *hdev)
+ {
+- struct sk_buff *skb;
++ struct hci_usb *husb = (struct hci_usb *) hdev->driver_data;
++ unsigned long flags;
++
++ if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
++ return 0;
+
+- if (test_and_set_bit(HCI_TX_CTRL, &husb->tx_state))
+- return;
++ BT_DBG("%s", hdev->name);
+
+- DBG("%s", husb->hdev.name);
++ write_lock_irqsave(&husb->completion_lock, flags);
++
++ hci_usb_unlink_urbs(husb);
++ hci_usb_flush(hdev);
+
+- if (!(skb = skb_dequeue(&husb->tx_ctrl_q)))
+- goto done;
++ write_unlock_irqrestore(&husb->completion_lock, flags);
+
+- if (hci_usb_ctrl_msg(husb, skb)){
+- kfree_skb(skb);
+- goto done;
+- }
++ MOD_DEC_USE_COUNT;
++ return 0;
++}
+
+- DMP(skb->data, skb->len);
++static int __tx_submit(struct hci_usb *husb, struct _urb *_urb)
++{
++ struct urb *urb = &_urb->urb;
++ int err;
+
+- husb->hdev.stat.byte_tx += skb->len;
+- return;
++ BT_DBG("%s urb %p type %d", husb->hdev.name, urb, _urb->type);
++
++ _urb_queue_tail(__pending_q(husb, _urb->type), _urb);
++ err = usb_submit_urb(urb);
++ if (err) {
++ BT_ERR("%s tx submit failed urb %p type %d err %d",
++ husb->hdev.name, urb, _urb->type, err);
++ _urb_unlink(_urb);
++ _urb_queue_tail(__completed_q(husb, _urb->type), _urb);
++ } else
++ atomic_inc(__pending_tx(husb, _urb->type));
+
+-done:
+- clear_bit(HCI_TX_CTRL, &husb->tx_state);
+- return;
++ return err;
+ }
+
+-void hci_usb_write_wakeup(struct hci_usb *husb)
++static inline int hci_usb_send_ctrl(struct hci_usb *husb, struct sk_buff *skb)
+ {
+- struct sk_buff *skb;
++ struct _urb *_urb = __get_completed(husb, skb->pkt_type);
++ devrequest *dr;
++ struct urb *urb;
++
++ if (!_urb) {
++ _urb = _urb_alloc(0, GFP_ATOMIC);
++ if (!_urb)
++ return -ENOMEM;
++ _urb->type = skb->pkt_type;
++
++ dr = kmalloc(sizeof(*dr), GFP_ATOMIC);
++ if (!dr) {
++ _urb_free(_urb);
++ return -ENOMEM;
++ }
++ } else
++ dr = (void *) _urb->urb.setup_packet;
+
+- if (test_and_set_bit(HCI_TX_WRITE, &husb->tx_state))
+- return;
++ dr->requesttype = husb->ctrl_req;
++ dr->request = 0;
++ dr->index = 0;
++ dr->value = 0;
++ dr->length = __cpu_to_le16(skb->len);
+
+- DBG("%s", husb->hdev.name);
++ urb = &_urb->urb;
++ FILL_CONTROL_URB(urb, husb->udev, usb_sndctrlpipe(husb->udev, 0),
++ (void *) dr, skb->data, skb->len, hci_usb_tx_complete, husb);
+
+- if (!(skb = skb_dequeue(&husb->tx_write_q)))
+- goto done;
++ BT_DBG("%s skb %p len %d", husb->hdev.name, skb, skb->len);
++
++ _urb->priv = skb;
++ return __tx_submit(husb, _urb);
++}
+
+- if (hci_usb_write_msg(husb, skb)) {
+- skb_queue_head(&husb->tx_write_q, skb);
+- goto done;
++static inline int hci_usb_send_bulk(struct hci_usb *husb, struct sk_buff *skb)
++{
++ struct _urb *_urb = __get_completed(husb, skb->pkt_type);
++ struct urb *urb;
++ int pipe;
++
++ if (!_urb) {
++ _urb = _urb_alloc(0, GFP_ATOMIC);
++ if (!_urb)
++ return -ENOMEM;
++ _urb->type = skb->pkt_type;
+ }
+
+- DMP(skb->data, skb->len);
++ urb = &_urb->urb;
++ pipe = usb_sndbulkpipe(husb->udev, husb->bulk_out_ep->bEndpointAddress);
++ FILL_BULK_URB(urb, husb->udev, pipe, skb->data, skb->len,
++ hci_usb_tx_complete, husb);
++ urb->transfer_flags = USB_QUEUE_BULK | USB_ZERO_PACKET;
+
+- husb->hdev.stat.byte_tx += skb->len;
+- return;
++ BT_DBG("%s skb %p len %d", husb->hdev.name, skb, skb->len);
+
+-done:
+- clear_bit(HCI_TX_WRITE, &husb->tx_state);
+- return;
++ _urb->priv = skb;
++ return __tx_submit(husb, _urb);
+ }
+
+-/* Send frames from HCI layer */
+-int hci_usb_send_frame(struct sk_buff *skb)
++#ifdef CONFIG_BLUEZ_HCIUSB_SCO
++static inline int hci_usb_send_isoc(struct hci_usb *husb, struct sk_buff *skb)
+ {
+- struct hci_dev *hdev = (struct hci_dev *) skb->dev;
+- struct hci_usb *husb;
+-
+- if (!hdev) {
+- ERR("frame for uknown device (hdev=NULL)");
+- return -ENODEV;
++ struct _urb *_urb = __get_completed(husb, skb->pkt_type);
++ struct urb *urb;
++
++ if (!_urb) {
++ _urb = _urb_alloc(HCI_MAX_ISOC_FRAMES, GFP_ATOMIC);
++ if (!_urb)
++ return -ENOMEM;
++ _urb->type = skb->pkt_type;
+ }
+
+- if (!(hdev->flags & HCI_RUNNING))
+- return 0;
++ BT_DBG("%s skb %p len %d", husb->hdev.name, skb, skb->len);
+
+- husb = (struct hci_usb *) hdev->driver_data;
++ urb = &_urb->urb;
++
++ urb->context = husb;
++ urb->dev = husb->udev;
++ urb->pipe = usb_sndisocpipe(husb->udev, husb->isoc_out_ep->bEndpointAddress);
++ urb->complete = hci_usb_tx_complete;
++ urb->transfer_flags = USB_ISO_ASAP;
+
+- DBG("%s type %d len %d", hdev->name, skb->pkt_type, skb->len);
++ urb->transfer_buffer = skb->data;
++ urb->transfer_buffer_length = skb->len;
++
++ __fill_isoc_desc(urb, skb->len, husb->isoc_out_ep->wMaxPacketSize);
+
+- switch (skb->pkt_type) {
+- case HCI_COMMAND_PKT:
+- skb_queue_tail(&husb->tx_ctrl_q, skb);
+- hci_usb_ctrl_wakeup(husb);
+- hdev->stat.cmd_tx++;
+- return 0;
+-
+- case HCI_ACLDATA_PKT:
+- skb_queue_tail(&husb->tx_write_q, skb);
+- hci_usb_write_wakeup(husb);
+- hdev->stat.acl_tx++;
+- return 0;
+-
+- case HCI_SCODATA_PKT:
+- return -EOPNOTSUPP;
+- };
+-
+- return 0;
++ _urb->priv = skb;
++ return __tx_submit(husb, _urb);
+ }
++#endif
+
+-/* ---------- USB ------------- */
+-
+-static void hci_usb_ctrl(struct urb *urb)
++static void hci_usb_tx_process(struct hci_usb *husb)
+ {
+- struct sk_buff *skb = (struct sk_buff *) urb->context;
+- struct hci_dev *hdev;
+- struct hci_usb *husb;
++ struct sk_buff_head *q;
++ struct sk_buff *skb;
+
+- if (!skb)
+- return;
+- hdev = (struct hci_dev *) skb->dev;
+- husb = (struct hci_usb *) hdev->driver_data;
++ BT_DBG("%s", husb->hdev.name);
+
+- DBG("%s", hdev->name);
++ do {
++ clear_bit(HCI_USB_TX_WAKEUP, &husb->state);
+
+- if (urb->status)
+- DBG("%s ctrl status: %d", hdev->name, urb->status);
++ /* Process command queue */
++ q = __transmit_q(husb, HCI_COMMAND_PKT);
++ if (!atomic_read(__pending_tx(husb, HCI_COMMAND_PKT)) &&
++ (skb = skb_dequeue(q))) {
++ if (hci_usb_send_ctrl(husb, skb) < 0)
++ skb_queue_head(q, skb);
++ }
+
+- clear_bit(HCI_TX_CTRL, &husb->tx_state);
+- kfree_skb(skb);
++#ifdef CONFIG_BLUEZ_HCIUSB_SCO
++ /* Process SCO queue */
++ q = __transmit_q(husb, HCI_SCODATA_PKT);
++ if (atomic_read(__pending_tx(husb, HCI_SCODATA_PKT)) < HCI_MAX_ISOC_TX &&
++ (skb = skb_dequeue(q))) {
++ if (hci_usb_send_isoc(husb, skb) < 0)
++ skb_queue_head(q, skb);
++ }
++#endif
++
++ /* Process ACL queue */
++ q = __transmit_q(husb, HCI_ACLDATA_PKT);
++ while (atomic_read(__pending_tx(husb, HCI_ACLDATA_PKT)) < HCI_MAX_BULK_TX &&
++ (skb = skb_dequeue(q))) {
++ if (hci_usb_send_bulk(husb, skb) < 0) {
++ skb_queue_head(q, skb);
++ break;
++ }
++ }
++ } while(test_bit(HCI_USB_TX_WAKEUP, &husb->state));
++}
+
+- /* Wake up device */
+- hci_usb_ctrl_wakeup(husb);
++static inline void hci_usb_tx_wakeup(struct hci_usb *husb)
++{
++ /* Serialize TX queue processing to avoid data reordering */
++ if (!test_and_set_bit(HCI_USB_TX_PROCESS, &husb->state)) {
++ hci_usb_tx_process(husb);
++ clear_bit(HCI_USB_TX_PROCESS, &husb->state);
++ } else
++ set_bit(HCI_USB_TX_WAKEUP, &husb->state);
+ }
+
+-static void hci_usb_bulk_write(struct urb *urb)
++/* Send frames from HCI layer */
++static int hci_usb_send_frame(struct sk_buff *skb)
+ {
+- struct sk_buff *skb = (struct sk_buff *) urb->context;
+- struct hci_dev *hdev;
++ struct hci_dev *hdev = (struct hci_dev *) skb->dev;
+ struct hci_usb *husb;
+
+- if (!skb)
+- return;
+- hdev = (struct hci_dev *) skb->dev;
+- husb = (struct hci_usb *) hdev->driver_data;
++ if (!hdev) {
++ BT_ERR("frame for uknown device (hdev=NULL)");
++ return -ENODEV;
++ }
+
+- DBG("%s", hdev->name);
++ if (!test_bit(HCI_RUNNING, &hdev->flags))
++ return -EBUSY;
+
+- if (urb->status)
+- DBG("%s bulk write status: %d", hdev->name, urb->status);
++ BT_DBG("%s type %d len %d", hdev->name, skb->pkt_type, skb->len);
+
+- clear_bit(HCI_TX_WRITE, &husb->tx_state);
+- kfree_skb(skb);
++ husb = (struct hci_usb *) hdev->driver_data;
+
+- /* Wake up device */
+- hci_usb_write_wakeup(husb);
++ switch (skb->pkt_type) {
++ case HCI_COMMAND_PKT:
++ hdev->stat.cmd_tx++;
++ break;
++
++ case HCI_ACLDATA_PKT:
++ hdev->stat.acl_tx++;
++ break;
++
++#ifdef CONFIG_BLUEZ_HCIUSB_SCO
++ case HCI_SCODATA_PKT:
++ hdev->stat.sco_tx++;
++ break;
++#endif
+
+- return;
+-}
++ default:
++ kfree_skb(skb);
++ return 0;
++ }
+
+-static void hci_usb_intr(struct urb *urb)
+-{
+- struct hci_usb *husb = (struct hci_usb *) urb->context;
+- unsigned char *data = urb->transfer_buffer;
+- register int count = urb->actual_length;
+- register struct sk_buff *skb = husb->intr_skb;
+- hci_event_hdr *eh;
+- register int len;
++ read_lock(&husb->completion_lock);
+
+- if (!husb)
+- return;
++ skb_queue_tail(__transmit_q(husb, skb->pkt_type), skb);
++ hci_usb_tx_wakeup(husb);
+
+- DBG("%s count %d", husb->hdev.name, count);
++ read_unlock(&husb->completion_lock);
++ return 0;
++}
+
+- if (urb->status || !count) {
+- DBG("%s intr status %d, count %d", husb->hdev.name, urb->status, count);
+- return;
+- }
++static inline int __recv_frame(struct hci_usb *husb, int type, void *data, int count)
++{
++ BT_DBG("%s type %d data %p count %d", husb->hdev.name, type, data, count);
+
+- /* Do we really have to handle continuations here ? */
+- if (!skb) {
+- /* New frame */
+- if (count < HCI_EVENT_HDR_SIZE) {
+- DBG("%s bad frame len %d", husb->hdev.name, count);
+- return;
+- }
++ husb->hdev.stat.byte_rx += count;
+
+- eh = (hci_event_hdr *) data;
+- len = eh->plen + HCI_EVENT_HDR_SIZE;
++ while (count) {
++ struct sk_buff *skb = __reassembly(husb, type);
++ struct { int expect; } *scb;
++ int len = 0;
++
++ if (!skb) {
++ /* Start of the frame */
++
++ switch (type) {
++ case HCI_EVENT_PKT:
++ if (count >= HCI_EVENT_HDR_SIZE) {
++ hci_event_hdr *h = data;
++ len = HCI_EVENT_HDR_SIZE + h->plen;
++ } else
++ return -EILSEQ;
++ break;
+
+- if (count > len) {
+- DBG("%s corrupted frame, len %d", husb->hdev.name, count);
+- return;
+- }
++ case HCI_ACLDATA_PKT:
++ if (count >= HCI_ACL_HDR_SIZE) {
++ hci_acl_hdr *h = data;
++ len = HCI_ACL_HDR_SIZE + __le16_to_cpu(h->dlen);
++ } else
++ return -EILSEQ;
++ break;
++#ifdef CONFIG_BLUEZ_HCIUSB_SCO
++ case HCI_SCODATA_PKT:
++ if (count >= HCI_SCO_HDR_SIZE) {
++ hci_sco_hdr *h = data;
++ len = HCI_SCO_HDR_SIZE + h->dlen;
++ } else
++ return -EILSEQ;
++ break;
++#endif
++ }
++ BT_DBG("new packet len %d", len);
+
+- /* Allocate skb */
+- if (!(skb = bluez_skb_alloc(len, GFP_ATOMIC))) {
+- ERR("Can't allocate mem for new packet");
+- return;
++ skb = bluez_skb_alloc(len, GFP_ATOMIC);
++ if (!skb) {
++ BT_ERR("%s no memory for the packet", husb->hdev.name);
++ return -ENOMEM;
++ }
++ skb->dev = (void *) &husb->hdev;
++ skb->pkt_type = type;
++
++ __reassembly(husb, type) = skb;
++
++ scb = (void *) skb->cb;
++ scb->expect = len;
++ } else {
++ /* Continuation */
++ scb = (void *) skb->cb;
++ len = scb->expect;
+ }
+- skb->dev = (void *) &husb->hdev;
+- skb->pkt_type = HCI_EVENT_PKT;
+-
+- husb->intr_skb = skb;
+- husb->intr_count = len;
+- } else {
+- /* Continuation */
+- if (count > husb->intr_count) {
+- ERR("%s bad frame len %d (expected %d)", husb->hdev.name, count, husb->intr_count);
+
+- kfree_skb(skb);
+- husb->intr_skb = NULL;
+- husb->intr_count = 0;
+- return;
++ len = min(len, count);
++
++ memcpy(skb_put(skb, len), data, len);
++
++ scb->expect -= len;
++ if (!scb->expect) {
++ /* Complete frame */
++ __reassembly(husb, type) = NULL;
++ hci_recv_frame(skb);
+ }
+- }
+-
+- memcpy(skb_put(skb, count), data, count);
+- husb->intr_count -= count;
+-
+- DMP(data, count);
+-
+- if (!husb->intr_count) {
+- /* Got complete frame */
+
+- husb->hdev.stat.byte_rx += skb->len;
+- hci_recv_frame(skb);
+-
+- husb->intr_skb = NULL;
++ count -= len; data += len;
+ }
++ return 0;
+ }
+
+-static void hci_usb_bulk_read(struct urb *urb)
++static void hci_usb_rx_complete(struct urb *urb)
+ {
+- struct hci_usb *husb = (struct hci_usb *) urb->context;
+- unsigned char *data = urb->transfer_buffer;
+- int count = urb->actual_length, status;
+- struct sk_buff *skb;
+- hci_acl_hdr *ah;
+- register __u16 dlen;
+-
+- if (!husb)
+- return;
++ struct _urb *_urb = container_of(urb, struct _urb, urb);
++ struct hci_usb *husb = (void *) urb->context;
++ struct hci_dev *hdev = &husb->hdev;
++ int err, count = urb->actual_length;
+
+- DBG("%s status %d, count %d, flags %x", husb->hdev.name, urb->status, count, urb->transfer_flags);
++ BT_DBG("%s urb %p type %d status %d count %d flags %x", hdev->name, urb,
++ _urb->type, urb->status, count, urb->transfer_flags);
+
+- if (urb->status) {
+- /* Do not re-submit URB on critical errors */
+- switch (urb->status) {
+- case -ENOENT:
+- return;
+- default:
+- goto resubmit;
+- };
+- }
+- if (!count)
+- goto resubmit;
+-
+- DMP(data, count);
++ read_lock(&husb->completion_lock);
+
+- ah = (hci_acl_hdr *) data;
+- dlen = le16_to_cpu(ah->dlen);
++ if (!test_bit(HCI_RUNNING, &hdev->flags))
++ goto unlock;
+
+- /* Verify frame len and completeness */
+- if ((count - HCI_ACL_HDR_SIZE) != dlen) {
+- ERR("%s corrupted ACL packet: count %d, plen %d", husb->hdev.name, count, dlen);
++ if (urb->status || !count)
+ goto resubmit;
+- }
+
+- /* Allocate packet */
+- if (!(skb = bluez_skb_alloc(count, GFP_ATOMIC))) {
+- ERR("Can't allocate mem for new packet");
+- goto resubmit;
++ if (_urb->type == HCI_SCODATA_PKT) {
++#ifdef CONFIG_BLUEZ_HCIUSB_SCO
++ int i;
++ for (i=0; i < urb->number_of_packets; i++) {
++ BT_DBG("desc %d status %d offset %d len %d", i,
++ urb->iso_frame_desc[i].status,
++ urb->iso_frame_desc[i].offset,
++ urb->iso_frame_desc[i].actual_length);
++
++ if (!urb->iso_frame_desc[i].status)
++ __recv_frame(husb, _urb->type,
++ urb->transfer_buffer + urb->iso_frame_desc[i].offset,
++ urb->iso_frame_desc[i].actual_length);
++ }
++#else
++ ;
++#endif
++ } else {
++ err = __recv_frame(husb, _urb->type, urb->transfer_buffer, count);
++ if (err < 0) {
++ BT_ERR("%s corrupted packet: type %d count %d",
++ husb->hdev.name, _urb->type, count);
++ hdev->stat.err_rx++;
++ }
+ }
+
+- memcpy(skb_put(skb, count), data, count);
+- skb->dev = (void *) &husb->hdev;
+- skb->pkt_type = HCI_ACLDATA_PKT;
+-
+- husb->hdev.stat.byte_rx += skb->len;
+-
+- hci_recv_frame(skb);
+-
+ resubmit:
+- husb->read_urb->dev = husb->udev;
+- if ((status = usb_submit_urb(husb->read_urb)))
+- DBG("%s read URB submit failed %d", husb->hdev.name, status);
++ if (_urb->type != HCI_EVENT_PKT) {
++ urb->dev = husb->udev;
++ err = usb_submit_urb(urb);
++ BT_DBG("%s urb %p type %d resubmit status %d", hdev->name, urb,
++ _urb->type, err);
++ }
+
+- DBG("%s read URB re-submited", husb->hdev.name);
++unlock:
++ read_unlock(&husb->completion_lock);
+ }
+
+-static int hci_usb_ctrl_msg(struct hci_usb *husb, struct sk_buff *skb)
++static void hci_usb_tx_complete(struct urb *urb)
+ {
+- struct urb *urb = husb->ctrl_urb;
+- devrequest *dr = &husb->dev_req;
+- int pipe, status;
++ struct _urb *_urb = container_of(urb, struct _urb, urb);
++ struct hci_usb *husb = (void *) urb->context;
++ struct hci_dev *hdev = &husb->hdev;
+
+- DBG("%s len %d", husb->hdev.name, skb->len);
++ BT_DBG("%s urb %p status %d flags %x", hdev->name, urb,
++ urb->status, urb->transfer_flags);
+
+- pipe = usb_sndctrlpipe(husb->udev, 0);
++ atomic_dec(__pending_tx(husb, _urb->type));
+
+- dr->requesttype = HCI_CTRL_REQ;
+- dr->request = 0;
+- dr->index = 0;
+- dr->value = 0;
+- dr->length = cpu_to_le16(skb->len);
+-
+- FILL_CONTROL_URB(urb, husb->udev, pipe, (void*)dr, skb->data, skb->len,
+- hci_usb_ctrl, skb);
++ urb->transfer_buffer = NULL;
++ kfree_skb((struct sk_buff *) _urb->priv);
+
+- if ((status = usb_submit_urb(urb))) {
+- DBG("%s control URB submit failed %d", husb->hdev.name, status);
+- return status;
+- }
++ if (!test_bit(HCI_RUNNING, &hdev->flags))
++ return;
+
+- return 0;
+-}
++ if (!urb->status)
++ hdev->stat.byte_tx += urb->transfer_buffer_length;
++ else
++ hdev->stat.err_tx++;
+
+-static int hci_usb_write_msg(struct hci_usb *husb, struct sk_buff *skb)
+-{
+- struct urb *urb = husb->write_urb;
+- int pipe, status;
++ read_lock(&husb->completion_lock);
+
+- DBG("%s len %d", husb->hdev.name, skb->len);
++ _urb_unlink(_urb);
++ _urb_queue_tail(__completed_q(husb, _urb->type), _urb);
+
+- pipe = usb_sndbulkpipe(husb->udev, husb->bulk_out_ep_addr);
++ hci_usb_tx_wakeup(husb);
++
++ read_unlock(&husb->completion_lock);
++}
+
+- FILL_BULK_URB(urb, husb->udev, pipe, skb->data, skb->len,
+- hci_usb_bulk_write, skb);
+- urb->transfer_flags |= USB_QUEUE_BULK;
++static void hci_usb_destruct(struct hci_dev *hdev)
++{
++ struct hci_usb *husb = (struct hci_usb *) hdev->driver_data;
+
+- if ((status = usb_submit_urb(urb))) {
+- DBG("%s write URB submit failed %d", husb->hdev.name, status);
+- return status;
+- }
++ BT_DBG("%s", hdev->name);
+
+- return 0;
++ kfree(husb);
+ }
+
+-static void * hci_usb_probe(struct usb_device *udev, unsigned int ifnum, const struct usb_device_id *id)
++static void *hci_usb_probe(struct usb_device *udev, unsigned int ifnum, const struct usb_device_id *id)
+ {
+- struct usb_endpoint_descriptor *bulk_out_ep, *intr_in_ep, *bulk_in_ep;
++ struct usb_endpoint_descriptor *bulk_out_ep[HCI_MAX_IFACE_NUM];
++ struct usb_endpoint_descriptor *isoc_out_ep[HCI_MAX_IFACE_NUM];
++ struct usb_endpoint_descriptor *bulk_in_ep[HCI_MAX_IFACE_NUM];
++ struct usb_endpoint_descriptor *isoc_in_ep[HCI_MAX_IFACE_NUM];
++ struct usb_endpoint_descriptor *intr_in_ep[HCI_MAX_IFACE_NUM];
+ struct usb_interface_descriptor *uif;
+ struct usb_endpoint_descriptor *ep;
++ struct usb_interface *iface, *isoc_iface;
+ struct hci_usb *husb;
+ struct hci_dev *hdev;
+- int i, size, pipe;
+- __u8 * buf;
++ int i, a, e, size, ifn, isoc_ifnum, isoc_alts;
+
+- DBG("udev %p ifnum %d", udev, ifnum);
++ BT_DBG("udev %p ifnum %d", udev, ifnum);
+
+- /* Check device signature */
+- if ((udev->descriptor.bDeviceClass != HCI_DEV_CLASS) ||
+- (udev->descriptor.bDeviceSubClass != HCI_DEV_SUBCLASS)||
+- (udev->descriptor.bDeviceProtocol != HCI_DEV_PROTOCOL) )
+- return NULL;
++ iface = &udev->actconfig->interface[0];
+
+- MOD_INC_USE_COUNT;
+-
+- uif = &udev->actconfig->interface[ifnum].altsetting[0];
++ if (!id->driver_info) {
++ const struct usb_device_id *match;
++ match = usb_match_id(udev, iface, blacklist_ids);
++ if (match)
++ id = match;
++ }
+
+- if (uif->bNumEndpoints != 3) {
+- DBG("Wrong number of endpoints %d", uif->bNumEndpoints);
+- MOD_DEC_USE_COUNT;
++ if (id->driver_info & HCI_IGNORE)
+ return NULL;
+- }
+
+- bulk_out_ep = intr_in_ep = bulk_in_ep = NULL;
++ /* Check number of endpoints */
++ if (udev->actconfig->interface[ifnum].altsetting[0].bNumEndpoints < 3)
++ return NULL;
+
++ memset(bulk_out_ep, 0, sizeof(bulk_out_ep));
++ memset(isoc_out_ep, 0, sizeof(isoc_out_ep));
++ memset(bulk_in_ep, 0, sizeof(bulk_in_ep));
++ memset(isoc_in_ep, 0, sizeof(isoc_in_ep));
++ memset(intr_in_ep, 0, sizeof(intr_in_ep));
++
++ size = 0;
++ isoc_iface = NULL;
++ isoc_alts = isoc_ifnum = 0;
++
+ /* Find endpoints that we need */
+- for ( i = 0; i < uif->bNumEndpoints; ++i) {
+- ep = &uif->endpoint[i];
+
+- switch (ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
+- case USB_ENDPOINT_XFER_BULK:
+- if (ep->bEndpointAddress & USB_DIR_IN)
+- bulk_in_ep = ep;
+- else
+- bulk_out_ep = ep;
+- break;
++ ifn = MIN(udev->actconfig->bNumInterfaces, HCI_MAX_IFACE_NUM);
++ for (i = 0; i < ifn; i++) {
++ iface = &udev->actconfig->interface[i];
++ for (a = 0; a < iface->num_altsetting; a++) {
++ uif = &iface->altsetting[a];
++ for (e = 0; e < uif->bNumEndpoints; e++) {
++ ep = &uif->endpoint[e];
++
++ switch (ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
++ case USB_ENDPOINT_XFER_INT:
++ if (ep->bEndpointAddress & USB_DIR_IN)
++ intr_in_ep[i] = ep;
++ break;
++
++ case USB_ENDPOINT_XFER_BULK:
++ if (ep->bEndpointAddress & USB_DIR_IN)
++ bulk_in_ep[i] = ep;
++ else
++ bulk_out_ep[i] = ep;
++ break;
++
++#ifdef CONFIG_BLUEZ_HCIUSB_SCO
++ case USB_ENDPOINT_XFER_ISOC:
++ if (ep->wMaxPacketSize < size || a > 2)
++ break;
++ size = ep->wMaxPacketSize;
++
++ isoc_iface = iface;
++ isoc_alts = a;
++ isoc_ifnum = i;
++
++ if (ep->bEndpointAddress & USB_DIR_IN)
++ isoc_in_ep[i] = ep;
++ else
++ isoc_out_ep[i] = ep;
++ break;
++#endif
++ }
++ }
++ }
++ }
+
+- case USB_ENDPOINT_XFER_INT:
+- intr_in_ep = ep;
+- break;
+- };
++ if (!bulk_in_ep[0] || !bulk_out_ep[0] || !intr_in_ep[0]) {
++ BT_DBG("Bulk endpoints not found");
++ goto done;
+ }
+
+- if (!bulk_in_ep || !bulk_out_ep || !intr_in_ep) {
+- DBG("Endpoints not found: %p %p %p", bulk_in_ep, bulk_out_ep, intr_in_ep);
+- MOD_DEC_USE_COUNT;
+- return NULL;
++#ifdef CONFIG_BLUEZ_HCIUSB_SCO
++ if (id->driver_info & HCI_BROKEN_ISOC || !isoc_in_ep[1] || !isoc_out_ep[1]) {
++ BT_DBG("Isoc endpoints not found");
++ isoc_iface = NULL;
+ }
++#endif
+
+ if (!(husb = kmalloc(sizeof(struct hci_usb), GFP_KERNEL))) {
+- ERR("Can't allocate: control structure");
+- MOD_DEC_USE_COUNT;
+- return NULL;
++ BT_ERR("Can't allocate: control structure");
++ goto done;
+ }
+
+ memset(husb, 0, sizeof(struct hci_usb));
+
+ husb->udev = udev;
+- husb->bulk_out_ep_addr = bulk_out_ep->bEndpointAddress;
+-
+- if (!(husb->ctrl_urb = usb_alloc_urb(0))) {
+- ERR("Can't allocate: control URB");
+- goto probe_error;
+- }
+-
+- if (!(husb->write_urb = usb_alloc_urb(0))) {
+- ERR("Can't allocate: write URB");
+- goto probe_error;
+- }
+-
+- if (!(husb->read_urb = usb_alloc_urb(0))) {
+- ERR("Can't allocate: read URB");
+- goto probe_error;
+- }
+-
+- ep = bulk_in_ep;
+- pipe = usb_rcvbulkpipe(udev, ep->bEndpointAddress);
+- size = HCI_MAX_FRAME_SIZE;
+-
+- if (!(buf = kmalloc(size, GFP_KERNEL))) {
+- ERR("Can't allocate: read buffer");
+- goto probe_error;
+- }
+-
+- FILL_BULK_URB(husb->read_urb, udev, pipe, buf, size, hci_usb_bulk_read, husb);
+- husb->read_urb->transfer_flags |= USB_QUEUE_BULK;
+-
+- ep = intr_in_ep;
+- pipe = usb_rcvintpipe(udev, ep->bEndpointAddress);
+- size = usb_maxpacket(udev, pipe, usb_pipeout(pipe));
+-
+- if (!(husb->intr_urb = usb_alloc_urb(0))) {
+- ERR("Can't allocate: interrupt URB");
+- goto probe_error;
++ husb->bulk_out_ep = bulk_out_ep[0];
++ husb->bulk_in_ep = bulk_in_ep[0];
++ husb->intr_in_ep = intr_in_ep[0];
++
++ if (id->driver_info & HCI_DIGIANSWER)
++ husb->ctrl_req = HCI_DIGI_REQ;
++ else
++ husb->ctrl_req = HCI_CTRL_REQ;
++
++#ifdef CONFIG_BLUEZ_HCIUSB_SCO
++ if (isoc_iface) {
++ BT_DBG("isoc ifnum %d alts %d", isoc_ifnum, isoc_alts);
++ if (usb_set_interface(udev, isoc_ifnum, isoc_alts)) {
++ BT_ERR("Can't set isoc interface settings");
++ isoc_iface = NULL;
++ }
++ usb_driver_claim_interface(&hci_usb_driver, isoc_iface, husb);
++ husb->isoc_iface = isoc_iface;
++ husb->isoc_in_ep = isoc_in_ep[isoc_ifnum];
++ husb->isoc_out_ep = isoc_out_ep[isoc_ifnum];
+ }
++#endif
++
++ husb->completion_lock = RW_LOCK_UNLOCKED;
+
+- if (!(buf = kmalloc(size, GFP_KERNEL))) {
+- ERR("Can't allocate: interrupt buffer");
+- goto probe_error;
++ for (i = 0; i < 4; i++) {
++ skb_queue_head_init(&husb->transmit_q[i]);
++ _urb_queue_init(&husb->pending_q[i]);
++ _urb_queue_init(&husb->completed_q[i]);
+ }
+
+- FILL_INT_URB(husb->intr_urb, udev, pipe, buf, size, hci_usb_intr, husb, ep->bInterval);
+-
+- skb_queue_head_init(&husb->tx_ctrl_q);
+- skb_queue_head_init(&husb->tx_write_q);
+-
+ /* Initialize and register HCI device */
+ hdev = &husb->hdev;
+
+- hdev->type = HCI_USB;
++ hdev->type = HCI_USB;
+ hdev->driver_data = husb;
+
+ hdev->open = hci_usb_open;
+ hdev->close = hci_usb_close;
+ hdev->flush = hci_usb_flush;
+- hdev->send = hci_usb_send_frame;
++ hdev->send = hci_usb_send_frame;
++ hdev->destruct = hci_usb_destruct;
++
++ if (id->driver_info & HCI_RESET)
++ set_bit(HCI_QUIRK_RESET_ON_INIT, &hdev->quirks);
+
+ if (hci_register_dev(hdev) < 0) {
+- ERR("Can't register HCI device %s", hdev->name);
++ BT_ERR("Can't register HCI device");
+ goto probe_error;
+ }
+
+ return husb;
+
+ probe_error:
+- hci_usb_free_bufs(husb);
+ kfree(husb);
+- MOD_DEC_USE_COUNT;
++
++done:
+ return NULL;
+ }
+
+@@ -626,38 +967,34 @@
+ if (!husb)
+ return;
+
+- DBG("%s", hdev->name);
++ BT_DBG("%s", hdev->name);
+
+ hci_usb_close(hdev);
+
+- if (hci_unregister_dev(hdev) < 0) {
+- ERR("Can't unregister HCI device %s", hdev->name);
+- }
++ if (husb->isoc_iface)
++ usb_driver_release_interface(&hci_usb_driver, husb->isoc_iface);
+
+- hci_usb_free_bufs(husb);
+- kfree(husb);
+-
+- MOD_DEC_USE_COUNT;
++ if (hci_unregister_dev(hdev) < 0)
++ BT_ERR("Can't unregister HCI device %s", hdev->name);
+ }
+
+-static struct usb_driver hci_usb_driver =
+-{
++static struct usb_driver hci_usb_driver = {
+ name: "hci_usb",
+ probe: hci_usb_probe,
+ disconnect: hci_usb_disconnect,
+- id_table: usb_bluetooth_ids,
++ id_table: bluetooth_ids,
+ };
+
+ int hci_usb_init(void)
+ {
+ int err;
+
+- INF("BlueZ HCI USB driver ver %s Copyright (C) 2000,2001 Qualcomm Inc",
++ BT_INFO("BlueZ HCI USB driver ver %s Copyright (C) 2000,2001 Qualcomm Inc",
+ VERSION);
+- INF("Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>");
++ BT_INFO("Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>");
+
+ if ((err = usb_register(&hci_usb_driver)) < 0)
+- ERR("Failed to register HCI USB driver");
++ BT_ERR("Failed to register HCI USB driver");
+
+ return err;
+ }
+@@ -670,6 +1007,6 @@
+ module_init(hci_usb_init);
+ module_exit(hci_usb_cleanup);
+
+-MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>");
++MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>, Marcel Holtmann <marcel@holtmann.org>");
+ MODULE_DESCRIPTION("BlueZ HCI USB driver ver " VERSION);
+ MODULE_LICENSE("GPL");
+diff -urN linux-2.4.18/drivers/bluetooth/hci_usb.h linux-2.4.18-mh15/drivers/bluetooth/hci_usb.h
+--- linux-2.4.18/drivers/bluetooth/hci_usb.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/drivers/bluetooth/hci_usb.h 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,147 @@
++/*
++ HCI USB driver for Linux Bluetooth protocol stack (BlueZ)
++ Copyright (C) 2000-2001 Qualcomm Incorporated
++ Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
++
++ Copyright (C) 2003 Maxim Krasnyansky <maxk@qualcomm.com>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License version 2 as
++ published by the Free Software Foundation;
++
++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++
++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
++ SOFTWARE IS DISCLAIMED.
++*/
++
++/*
++ * $Id$
++ */
++
++#ifdef __KERNEL__
++
++/* Class, SubClass, and Protocol codes that describe a Bluetooth device */
++#define HCI_DEV_CLASS 0xe0 /* Wireless class */
++#define HCI_DEV_SUBCLASS 0x01 /* RF subclass */
++#define HCI_DEV_PROTOCOL 0x01 /* Bluetooth programming protocol */
++
++#define HCI_CTRL_REQ 0x20
++#define HCI_DIGI_REQ 0x40
++
++#define HCI_IGNORE 0x01
++#define HCI_RESET 0x02
++#define HCI_DIGIANSWER 0x04
++#define HCI_BROKEN_ISOC 0x08
++
++#define HCI_MAX_IFACE_NUM 3
++
++#define HCI_MAX_BULK_TX 4
++#define HCI_MAX_BULK_RX 1
++
++#define HCI_MAX_ISOC_RX 2
++#define HCI_MAX_ISOC_TX 2
++
++#define HCI_MAX_ISOC_FRAMES 10
++
++struct _urb_queue {
++ struct list_head head;
++ spinlock_t lock;
++};
++
++struct _urb {
++ struct list_head list;
++ struct _urb_queue *queue;
++ int type;
++ void *priv;
++ struct urb urb;
++};
++
++struct _urb *_urb_alloc(int isoc, int gfp);
++
++static inline void _urb_free(struct _urb *_urb)
++{
++ kfree(_urb);
++}
++
++static inline void _urb_queue_init(struct _urb_queue *q)
++{
++ INIT_LIST_HEAD(&q->head);
++ spin_lock_init(&q->lock);
++}
++
++static inline void _urb_queue_head(struct _urb_queue *q, struct _urb *_urb)
++{
++ unsigned long flags;
++ spin_lock_irqsave(&q->lock, flags);
++ list_add(&_urb->list, &q->head); _urb->queue = q;
++ spin_unlock_irqrestore(&q->lock, flags);
++}
++
++static inline void _urb_queue_tail(struct _urb_queue *q, struct _urb *_urb)
++{
++ unsigned long flags;
++ spin_lock_irqsave(&q->lock, flags);
++ list_add_tail(&_urb->list, &q->head); _urb->queue = q;
++ spin_unlock_irqrestore(&q->lock, flags);
++}
++
++static inline void _urb_unlink(struct _urb *_urb)
++{
++ struct _urb_queue *q = _urb->queue;
++ unsigned long flags;
++ if (q) {
++ spin_lock_irqsave(&q->lock, flags);
++ list_del(&_urb->list); _urb->queue = NULL;
++ spin_unlock_irqrestore(&q->lock, flags);
++ }
++}
++
++struct _urb *_urb_dequeue(struct _urb_queue *q);
++
++#ifndef container_of
++#define container_of(ptr, type, member) ({ \
++ const typeof( ((type *)0)->member ) *__mptr = (ptr); \
++ (type *)( (char *)__mptr - offsetof(type,member) );})
++#endif
++
++struct hci_usb {
++ struct hci_dev hdev;
++
++ unsigned long state;
++
++ struct usb_device *udev;
++
++ struct usb_endpoint_descriptor *bulk_in_ep;
++ struct usb_endpoint_descriptor *bulk_out_ep;
++ struct usb_endpoint_descriptor *intr_in_ep;
++
++ struct usb_interface *isoc_iface;
++ struct usb_endpoint_descriptor *isoc_out_ep;
++ struct usb_endpoint_descriptor *isoc_in_ep;
++
++ __u8 ctrl_req;
++
++ struct sk_buff_head transmit_q[4];
++ struct sk_buff *reassembly[4]; // Reassembly buffers
++
++ rwlock_t completion_lock;
++
++ atomic_t pending_tx[4]; // Number of pending requests
++ struct _urb_queue pending_q[4]; // Pending requests
++ struct _urb_queue completed_q[4]; // Completed requests
++};
++
++/* States */
++#define HCI_USB_TX_PROCESS 1
++#define HCI_USB_TX_WAKEUP 2
++
++#endif /* __KERNEL__ */
+diff -urN linux-2.4.18/drivers/bluetooth/hci_vhci.c linux-2.4.18-mh15/drivers/bluetooth/hci_vhci.c
+--- linux-2.4.18/drivers/bluetooth/hci_vhci.c 2001-09-07 18:28:38.000000000 +0200
++++ linux-2.4.18-mh15/drivers/bluetooth/hci_vhci.c 2004-08-01 16:26:23.000000000 +0200
+@@ -25,9 +25,9 @@
+ /*
+ * BlueZ HCI virtual device driver.
+ *
+- * $Id$
++ * $Id$
+ */
+-#define VERSION "1.0"
++#define VERSION "1.1"
+
+ #include <linux/config.h>
+ #include <linux/module.h>
+@@ -49,43 +49,56 @@
+ #include <asm/uaccess.h>
+
+ #include <net/bluetooth/bluetooth.h>
+-#include <net/bluetooth/bluez.h>
+ #include <net/bluetooth/hci_core.h>
+-#include <net/bluetooth/hci_vhci.h>
++#include "hci_vhci.h"
+
+ /* HCI device part */
+
+-int hci_vhci_open(struct hci_dev *hdev)
++static int hci_vhci_open(struct hci_dev *hdev)
+ {
+- hdev->flags |= HCI_RUNNING;
++ set_bit(HCI_RUNNING, &hdev->flags);
+ return 0;
+ }
+
+-int hci_vhci_flush(struct hci_dev *hdev)
++static int hci_vhci_flush(struct hci_dev *hdev)
+ {
+ struct hci_vhci_struct *hci_vhci = (struct hci_vhci_struct *) hdev->driver_data;
+ skb_queue_purge(&hci_vhci->readq);
+ return 0;
+ }
+
+-int hci_vhci_close(struct hci_dev *hdev)
++static int hci_vhci_close(struct hci_dev *hdev)
+ {
+- hdev->flags &= ~HCI_RUNNING;
++ if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
++ return 0;
++
+ hci_vhci_flush(hdev);
+ return 0;
+ }
+
+-int hci_vhci_send_frame(struct sk_buff *skb)
++static void hci_vhci_destruct(struct hci_dev *hdev)
++{
++ struct hci_vhci_struct *vhci;
++
++ if (!hdev) return;
++
++ vhci = (struct hci_vhci_struct *) hdev->driver_data;
++ kfree(vhci);
++
++ MOD_DEC_USE_COUNT;
++}
++
++static int hci_vhci_send_frame(struct sk_buff *skb)
+ {
+ struct hci_dev* hdev = (struct hci_dev *) skb->dev;
+ struct hci_vhci_struct *hci_vhci;
+
+ if (!hdev) {
+- ERR("Frame for uknown device (hdev=NULL)");
++ BT_ERR("Frame for uknown device (hdev=NULL)");
+ return -ENODEV;
+ }
+
+- if (!(hdev->flags & HCI_RUNNING))
++ if (!test_bit(HCI_RUNNING, &hdev->flags))
+ return -EBUSY;
+
+ hci_vhci = (struct hci_vhci_struct *) hdev->driver_data;
+@@ -188,7 +201,7 @@
+
+ add_wait_queue(&hci_vhci->read_wait, &wait);
+ while (count) {
+- current->state = TASK_INTERRUPTIBLE;
++ set_current_state(TASK_INTERRUPTIBLE);
+
+ /* Read frames from device queue */
+ if (!(skb = skb_dequeue(&hci_vhci->readq))) {
+@@ -214,8 +227,7 @@
+ kfree_skb(skb);
+ break;
+ }
+-
+- current->state = TASK_RUNNING;
++ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&hci_vhci->read_wait, &wait);
+
+ return ret;
+@@ -270,11 +282,13 @@
+ hdev->close = hci_vhci_close;
+ hdev->flush = hci_vhci_flush;
+ hdev->send = hci_vhci_send_frame;
++ hdev->destruct = hci_vhci_destruct;
+
+ if (hci_register_dev(hdev) < 0) {
+ kfree(hci_vhci);
+ return -EBUSY;
+ }
++ MOD_INC_USE_COUNT;
+
+ file->private_data = hci_vhci;
+ return 0;
+@@ -285,12 +299,10 @@
+ struct hci_vhci_struct *hci_vhci = (struct hci_vhci_struct *) file->private_data;
+
+ if (hci_unregister_dev(&hci_vhci->hdev) < 0) {
+- ERR("Can't unregister HCI device %s", hci_vhci->hdev.name);
++ BT_ERR("Can't unregister HCI device %s", hci_vhci->hdev.name);
+ }
+
+- kfree(hci_vhci);
+ file->private_data = NULL;
+-
+ return 0;
+ }
+
+@@ -315,12 +327,12 @@
+
+ int __init hci_vhci_init(void)
+ {
+- INF("BlueZ VHCI driver ver %s Copyright (C) 2000,2001 Qualcomm Inc",
++ BT_INFO("BlueZ VHCI driver ver %s Copyright (C) 2000,2001 Qualcomm Inc",
+ VERSION);
+- INF("Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>");
++ BT_INFO("Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>");
+
+ if (misc_register(&hci_vhci_miscdev)) {
+- ERR("Can't register misc device %d\n", VHCI_MINOR);
++ BT_ERR("Can't register misc device %d\n", VHCI_MINOR);
+ return -EIO;
+ }
+
+@@ -337,4 +349,4 @@
+
+ MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>");
+ MODULE_DESCRIPTION("BlueZ VHCI driver ver " VERSION);
+-MODULE_LICENSE("GPL");
++MODULE_LICENSE("GPL");
+diff -urN linux-2.4.18/drivers/bluetooth/hci_vhci.h linux-2.4.18-mh15/drivers/bluetooth/hci_vhci.h
+--- linux-2.4.18/drivers/bluetooth/hci_vhci.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/drivers/bluetooth/hci_vhci.h 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,50 @@
++/*
++ BlueZ - Bluetooth protocol stack for Linux
++ Copyright (C) 2000-2001 Qualcomm Incorporated
++
++ Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License version 2 as
++ published by the Free Software Foundation;
++
++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++
++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
++ SOFTWARE IS DISCLAIMED.
++*/
++
++/*
++ * $Id$
++ */
++
++#ifndef __HCI_VHCI_H
++#define __HCI_VHCI_H
++
++#ifdef __KERNEL__
++
++struct hci_vhci_struct {
++ struct hci_dev hdev;
++ __u32 flags;
++ wait_queue_head_t read_wait;
++ struct sk_buff_head readq;
++ struct fasync_struct *fasync;
++};
++
++/* VHCI device flags */
++#define VHCI_FASYNC 0x0010
++
++#endif /* __KERNEL__ */
++
++#define VHCI_DEV "/dev/vhci"
++#define VHCI_MINOR 250
++
++#endif /* __HCI_VHCI_H */
+diff -urN linux-2.4.18/drivers/bluetooth/Makefile linux-2.4.18-mh15/drivers/bluetooth/Makefile
+--- linux-2.4.18/drivers/bluetooth/Makefile 2001-09-07 18:28:38.000000000 +0200
++++ linux-2.4.18-mh15/drivers/bluetooth/Makefile 2004-08-01 16:26:23.000000000 +0200
+@@ -1,11 +1,27 @@
+ #
+-# Makefile for Bluetooth HCI device drivers.
++# Makefile for the Linux Bluetooth HCI device drivers
+ #
+
+ O_TARGET := bluetooth.o
+
++list-multi := hci_uart.o
++
+ obj-$(CONFIG_BLUEZ_HCIUSB) += hci_usb.o
+-obj-$(CONFIG_BLUEZ_HCIUART) += hci_uart.o
+ obj-$(CONFIG_BLUEZ_HCIVHCI) += hci_vhci.o
+
++obj-$(CONFIG_BLUEZ_HCIUART) += hci_uart.o
++uart-y := hci_ldisc.o
++uart-$(CONFIG_BLUEZ_HCIUART_H4) += hci_h4.o
++uart-$(CONFIG_BLUEZ_HCIUART_BCSP) += hci_bcsp.o
++
++obj-$(CONFIG_BLUEZ_HCIBFUSB) += bfusb.o
++
++obj-$(CONFIG_BLUEZ_HCIDTL1) += dtl1_cs.o
++obj-$(CONFIG_BLUEZ_HCIBT3C) += bt3c_cs.o
++obj-$(CONFIG_BLUEZ_HCIBLUECARD) += bluecard_cs.o
++obj-$(CONFIG_BLUEZ_HCIBTUART) += btuart_cs.o
++
+ include $(TOPDIR)/Rules.make
++
++hci_uart.o: $(uart-y)
++ $(LD) -r -o $@ $(uart-y)
+diff -urN linux-2.4.18/drivers/bluetooth/Makefile.lib linux-2.4.18-mh15/drivers/bluetooth/Makefile.lib
+--- linux-2.4.18/drivers/bluetooth/Makefile.lib 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/drivers/bluetooth/Makefile.lib 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,2 @@
++obj-$(CONFIG_BLUEZ_HCIBFUSB) += firmware_class.o
++obj-$(CONFIG_BLUEZ_HCIBT3C) += firmware_class.o
+diff -urN linux-2.4.18/drivers/char/pcmcia/serial_cs.c linux-2.4.18-mh15/drivers/char/pcmcia/serial_cs.c
+--- linux-2.4.18/drivers/char/pcmcia/serial_cs.c 2001-12-21 18:41:54.000000000 +0100
++++ linux-2.4.18-mh15/drivers/char/pcmcia/serial_cs.c 2004-08-01 16:26:23.000000000 +0200
+@@ -2,7 +2,7 @@
+
+ A driver for PCMCIA serial devices
+
+- serial_cs.c 1.128 2001/10/18 12:18:35
++ serial_cs.c 1.138 2002/10/25 06:24:52
+
+ The contents of this file are subject to the Mozilla Public
+ License Version 1.1 (the "License"); you may not use this file
+@@ -69,14 +69,14 @@
+ static int irq_list[4] = { -1 };
+ MODULE_PARM(irq_list, "1-4i");
+
+-/* Enable the speaker? */
+-INT_MODULE_PARM(do_sound, 1);
++INT_MODULE_PARM(do_sound, 1); /* Enable the speaker? */
++INT_MODULE_PARM(buggy_uart, 0); /* Skip strict UART tests? */
+
+ #ifdef PCMCIA_DEBUG
+ INT_MODULE_PARM(pc_debug, PCMCIA_DEBUG);
+ #define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
+ static char *version =
+-"serial_cs.c 1.128 2001/10/18 12:18:35 (David Hinds)";
++"serial_cs.c 1.138 2002/10/25 06:24:52 (David Hinds)";
+ #else
+ #define DEBUG(n, args...)
+ #endif
+@@ -95,6 +95,7 @@
+ { MANFID_OMEGA, PRODID_OMEGA_QSP_100, 4 },
+ { MANFID_QUATECH, PRODID_QUATECH_DUAL_RS232, 2 },
+ { MANFID_QUATECH, PRODID_QUATECH_DUAL_RS232_D1, 2 },
++ { MANFID_QUATECH, PRODID_QUATECH_DUAL_RS232_D2, 2 },
+ { MANFID_QUATECH, PRODID_QUATECH_QUAD_RS232, 4 },
+ { MANFID_QUATECH, PRODID_QUATECH_DUAL_RS422, 2 },
+ { MANFID_QUATECH, PRODID_QUATECH_QUAD_RS422, 4 },
+@@ -148,7 +149,7 @@
+ client_reg_t client_reg;
+ dev_link_t *link;
+ int i, ret;
+-
++
+ DEBUG(0, "serial_attach()\n");
+
+ /* Create new serial device */
+@@ -160,7 +161,7 @@
+ link->release.function = &serial_release;
+ link->release.data = (u_long)link;
+ link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+- link->io.NumPorts1 = 8;
++ link->io.Attributes2 = IO_DATA_PATH_WIDTH_8;
+ link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
+ link->irq.IRQInfo1 = IRQ_INFO2_VALID|IRQ_LEVEL_ID;
+ if (irq_list[0] == -1)
+@@ -169,13 +170,12 @@
+ for (i = 0; i < 4; i++)
+ link->irq.IRQInfo2 |= 1 << irq_list[i];
+ link->conf.Attributes = CONF_ENABLE_IRQ;
+- link->conf.Vcc = 50;
+ if (do_sound) {
+ link->conf.Attributes |= CONF_ENABLE_SPKR;
+ link->conf.Status = CCSR_AUDIO_ENA;
+ }
+ link->conf.IntType = INT_MEMORY_AND_IO;
+-
++
+ /* Register with Card Services */
+ link->next = dev_list;
+ dev_list = link;
+@@ -194,7 +194,7 @@
+ serial_detach(link);
+ return NULL;
+ }
+-
++
+ return link;
+ } /* serial_attach */
+
+@@ -214,7 +214,7 @@
+ int ret;
+
+ DEBUG(0, "serial_detach(0x%p)\n", link);
+-
++
+ /* Locate device structure */
+ for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
+ if (*linkp == link) break;
+@@ -224,17 +224,17 @@
+ del_timer(&link->release);
+ if (link->state & DEV_CONFIG)
+ serial_release((u_long)link);
+-
++
+ if (link->handle) {
+ ret = CardServices(DeregisterClient, link->handle);
+ if (ret != CS_SUCCESS)
+ cs_error(link->handle, DeregisterClient, ret);
+ }
+-
++
+ /* Unlink device structure, free bits */
+ *linkp = link->next;
+ kfree(info);
+-
++
+ } /* serial_detach */
+
+ /*====================================================================*/
+@@ -243,18 +243,20 @@
+ {
+ struct serial_struct serial;
+ int line;
+-
++
+ memset(&serial, 0, sizeof(serial));
+ serial.port = port;
+ serial.irq = irq;
+ serial.flags = ASYNC_SKIP_TEST | ASYNC_SHARE_IRQ;
++ if (buggy_uart)
++ serial.flags |= ASYNC_BUGGY_UART;
+ line = register_serial(&serial);
+ if (line < 0) {
+ printk(KERN_NOTICE "serial_cs: register_serial() at 0x%04lx,"
+ " irq %d failed\n", (u_long)serial.port, serial.irq);
+ return -1;
+ }
+-
++
+ info->line[info->ndev] = line;
+ sprintf(info->node[info->ndev].dev_name, "ttyS%d", line);
+ info->node[info->ndev].major = TTY_MAJOR;
+@@ -262,7 +264,7 @@
+ if (info->ndev > 0)
+ info->node[info->ndev-1].next = &info->node[info->ndev];
+ info->ndev++;
+-
++
+ return 0;
+ }
+
+@@ -313,7 +315,10 @@
+ return setup_serial(info, port, config.AssignedIRQ);
+ }
+ link->conf.Vcc = config.Vcc;
+-
++
++ link->io.NumPorts1 = 8;
++ link->io.NumPorts2 = 0;
++
+ /* First pass: look for a config entry that looks normal. */
+ tuple.TupleData = (cisdata_t *)buf;
+ tuple.TupleOffset = 0; tuple.TupleDataMax = 255;
+@@ -340,7 +345,7 @@
+ i = next_tuple(handle, &tuple, &parse);
+ }
+ }
+-
++
+ /* Second pass: try to find an entry that isn't picky about
+ its base address, then try to grab any standard serial port
+ address, and finally try to get any free port. */
+@@ -352,8 +357,7 @@
+ for (j = 0; j < 5; j++) {
+ link->io.BasePort1 = base[j];
+ link->io.IOAddrLines = base[j] ? 16 : 3;
+- i = CardServices(RequestIO, link->handle,
+- &link->io);
++ i = CardServices(RequestIO, link->handle, &link->io);
+ if (i == CS_SUCCESS) goto found_port;
+ }
+ }
+@@ -365,7 +369,7 @@
+ cs_error(link->handle, RequestIO, i);
+ return -1;
+ }
+-
++
+ i = CardServices(RequestIRQ, link->handle, &link->irq);
+ if (i != CS_SUCCESS) {
+ cs_error(link->handle, RequestIRQ, i);
+@@ -390,8 +394,12 @@
+ u_char buf[256];
+ cisparse_t parse;
+ cistpl_cftable_entry_t *cf = &parse.cftable_entry;
++ config_info_t config;
+ int i, base2 = 0;
+
++ CardServices(GetConfigurationInfo, handle, &config);
++ link->conf.Vcc = config.Vcc;
++
+ tuple.TupleData = (cisdata_t *)buf;
+ tuple.TupleOffset = 0; tuple.TupleDataMax = 255;
+ tuple.Attributes = 0;
+@@ -433,12 +441,12 @@
+ i = next_tuple(handle, &tuple, &parse);
+ }
+ }
+-
++
+ if (i != CS_SUCCESS) {
+- cs_error(link->handle, RequestIO, i);
+- return -1;
++ /* At worst, try to configure as a single port */
++ return simple_config(link);
+ }
+-
++
+ i = CardServices(RequestIRQ, link->handle, &link->irq);
+ if (i != CS_SUCCESS) {
+ cs_error(link->handle, RequestIRQ, i);
+@@ -454,14 +462,27 @@
+ cs_error(link->handle, RequestConfiguration, i);
+ return -1;
+ }
+-
++
++ /* The Oxford Semiconductor OXCF950 cards are in fact single-port:
++ 8 registers are for the UART, the others are extra registers */
++ if (info->manfid == MANFID_OXSEMI) {
++ if (cf->index == 1 || cf->index == 3) {
++ setup_serial(info, base2, link->irq.AssignedIRQ);
++ outb(12,link->io.BasePort1+1);
++ } else {
++ setup_serial(info, link->io.BasePort1, link->irq.AssignedIRQ);
++ outb(12,base2+1);
++ }
++ return 0;
++ }
++
+ setup_serial(info, link->io.BasePort1, link->irq.AssignedIRQ);
+ /* The Nokia cards are not really multiport cards */
+ if (info->manfid == MANFID_NOKIA)
+ return 0;
+ for (i = 0; i < info->multi-1; i++)
+ setup_serial(info, base2+(8*i), link->irq.AssignedIRQ);
+-
++
+ return 0;
+ }
+
+@@ -500,7 +521,7 @@
+ }
+ link->conf.ConfigBase = parse.config.base;
+ link->conf.Present = parse.config.rmask[0];
+-
++
+ /* Configure card */
+ link->state |= DEV_CONFIG;
+
+@@ -508,8 +529,8 @@
+ tuple.DesiredTuple = CISTPL_LONGLINK_MFC;
+ tuple.Attributes = TUPLE_RETURN_COMMON | TUPLE_RETURN_LINK;
+ info->multi = (first_tuple(handle, &tuple, &parse) == CS_SUCCESS);
+-
+- /* Is this a multiport card? */
++
++ /* Scan list of known multiport card ID's */
+ tuple.DesiredTuple = CISTPL_MANFID;
+ if (first_tuple(handle, &tuple, &parse) == CS_SUCCESS) {
+ info->manfid = le16_to_cpu(buf[0]);
+@@ -537,15 +558,15 @@
+ info->multi = 2;
+ }
+ }
+-
++
+ if (info->multi > 1)
+ multi_config(link);
+ else
+ simple_config(link);
+-
++
+ if (info->ndev == 0)
+ goto failed;
+-
++
+ if (info->manfid == MANFID_IBM) {
+ conf_reg_t reg = { 0, CS_READ, 0x800, 0 };
+ CS_CHECK(AccessConfigurationRegister, link->handle, &reg);
+@@ -562,6 +583,7 @@
+ cs_error(link->handle, last_fn, last_ret);
+ failed:
+ serial_release((u_long)link);
++ link->state &= ~DEV_CONFIG_PENDING;
+
+ } /* serial_config */
+
+@@ -569,7 +591,7 @@
+
+ After a card is removed, serial_release() will unregister the net
+ device, and release the PCMCIA configuration.
+-
++
+ ======================================================================*/
+
+ void serial_release(u_long arg)
+@@ -577,7 +599,7 @@
+ dev_link_t *link = (dev_link_t *)arg;
+ serial_info_t *info = link->priv;
+ int i;
+-
++
+ DEBUG(0, "serial_release(0x%p)\n", link);
+
+ for (i = 0; i < info->ndev; i++) {
+@@ -590,7 +612,7 @@
+ CardServices(ReleaseIO, link->handle, &link->io);
+ CardServices(ReleaseIRQ, link->handle, &link->irq);
+ }
+-
++
+ link->state &= ~DEV_CONFIG;
+
+ } /* serial_release */
+@@ -601,7 +623,7 @@
+ stuff to run after an event is received. A CARD_REMOVAL event
+ also sets some flags to discourage the serial drivers from
+ talking to the ports.
+-
++
+ ======================================================================*/
+
+ static int serial_event(event_t event, int priority,
+@@ -609,9 +631,9 @@
+ {
+ dev_link_t *link = args->client_data;
+ serial_info_t *info = link->priv;
+-
++
+ DEBUG(1, "serial_event(0x%06x)\n", event);
+-
++
+ switch (event) {
+ case CS_EVENT_CARD_REMOVAL:
+ link->state &= ~DEV_PRESENT;
+@@ -650,7 +672,7 @@
+ if (serv.Revision != CS_RELEASE_CODE) {
+ printk(KERN_NOTICE "serial_cs: Card Services release "
+ "does not match!\n");
+- return -1;
++ return -EINVAL;
+ }
+ register_pccard_driver(&dev_info, &serial_attach, &serial_detach);
+ return 0;
+diff -urN linux-2.4.18/drivers/input/Config.in linux-2.4.18-mh15/drivers/input/Config.in
+--- linux-2.4.18/drivers/input/Config.in 2001-09-13 00:34:06.000000000 +0200
++++ linux-2.4.18-mh15/drivers/input/Config.in 2004-08-01 16:26:23.000000000 +0200
+@@ -14,5 +14,6 @@
+ fi
+ dep_tristate ' Joystick support' CONFIG_INPUT_JOYDEV $CONFIG_INPUT
+ dep_tristate ' Event interface support' CONFIG_INPUT_EVDEV $CONFIG_INPUT
++dep_tristate ' User level driver support' CONFIG_INPUT_UINPUT $CONFIG_INPUT
+
+ endmenu
+diff -urN linux-2.4.18/drivers/input/keybdev.c linux-2.4.18-mh15/drivers/input/keybdev.c
+--- linux-2.4.18/drivers/input/keybdev.c 2001-10-11 18:14:32.000000000 +0200
++++ linux-2.4.18-mh15/drivers/input/keybdev.c 2004-08-01 16:26:23.000000000 +0200
+@@ -154,16 +154,18 @@
+
+ static struct input_handler keybdev_handler;
+
++static unsigned int ledstate = 0xff;
++
+ void keybdev_ledfunc(unsigned int led)
+ {
+ struct input_handle *handle;
+
+- for (handle = keybdev_handler.handle; handle; handle = handle->hnext) {
++ ledstate = led;
+
++ for (handle = keybdev_handler.handle; handle; handle = handle->hnext) {
+ input_event(handle->dev, EV_LED, LED_SCROLLL, !!(led & 0x01));
+ input_event(handle->dev, EV_LED, LED_NUML, !!(led & 0x02));
+ input_event(handle->dev, EV_LED, LED_CAPSL, !!(led & 0x04));
+-
+ }
+ }
+
+@@ -202,6 +204,12 @@
+
+ // printk(KERN_INFO "keybdev.c: Adding keyboard: input%d\n", dev->number);
+
++ if (ledstate != 0xff) {
++ input_event(dev, EV_LED, LED_SCROLLL, !!(ledstate & 0x01));
++ input_event(dev, EV_LED, LED_NUML, !!(ledstate & 0x02));
++ input_event(dev, EV_LED, LED_CAPSL, !!(ledstate & 0x04));
++ }
++
+ return handle;
+ }
+
+diff -urN linux-2.4.18/drivers/input/Makefile linux-2.4.18-mh15/drivers/input/Makefile
+--- linux-2.4.18/drivers/input/Makefile 2000-12-29 23:07:22.000000000 +0100
++++ linux-2.4.18-mh15/drivers/input/Makefile 2004-08-01 16:26:23.000000000 +0200
+@@ -24,6 +24,7 @@
+ obj-$(CONFIG_INPUT_MOUSEDEV) += mousedev.o
+ obj-$(CONFIG_INPUT_JOYDEV) += joydev.o
+ obj-$(CONFIG_INPUT_EVDEV) += evdev.o
++obj-$(CONFIG_INPUT_UINPUT) += uinput.o
+
+ # The global Rules.make.
+
+diff -urN linux-2.4.18/drivers/input/uinput.c linux-2.4.18-mh15/drivers/input/uinput.c
+--- linux-2.4.18/drivers/input/uinput.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/drivers/input/uinput.c 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,428 @@
++/*
++ * User level driver support for input subsystem
++ *
++ * Heavily based on evdev.c by Vojtech Pavlik
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ *
++ * Author: Aristeu Sergio Rozanski Filho <aris@cathedrallabs.org>
++ *
++ * Changes/Revisions:
++ * 0.1 20/06/2002
++ * - first public version
++ */
++
++#include <linux/poll.h>
++#include <linux/slab.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/input.h>
++#include <linux/smp_lock.h>
++#include <linux/fs.h>
++#include <linux/miscdevice.h>
++#include <linux/uinput.h>
++
++static int uinput_dev_open(struct input_dev *dev)
++{
++ return 0;
++}
++
++static void uinput_dev_close(struct input_dev *dev)
++{
++}
++
++static int uinput_dev_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
++{
++ struct uinput_device *udev;
++
++ udev = (struct uinput_device *)dev->private;
++
++ udev->buff[udev->head].type = type;
++ udev->buff[udev->head].code = code;
++ udev->buff[udev->head].value = value;
++ do_gettimeofday(&udev->buff[udev->head].time);
++ udev->head = (udev->head + 1) % UINPUT_BUFFER_SIZE;
++
++ wake_up_interruptible(&udev->waitq);
++
++ return 0;
++}
++
++static int uinput_dev_upload_effect(struct input_dev *dev, struct ff_effect *effect)
++{
++ return 0;
++}
++
++static int uinput_dev_erase_effect(struct input_dev *dev, int effect_id)
++{
++ return 0;
++}
++
++static int uinput_create_device(struct uinput_device *udev)
++{
++ if (!udev->dev->name) {
++ printk(KERN_DEBUG "%s: write device info first\n", UINPUT_NAME);
++ return -EINVAL;
++ }
++
++ udev->dev->open = uinput_dev_open;
++ udev->dev->close = uinput_dev_close;
++ udev->dev->event = uinput_dev_event;
++ udev->dev->upload_effect = uinput_dev_upload_effect;
++ udev->dev->erase_effect = uinput_dev_erase_effect;
++ udev->dev->private = udev;
++
++ init_waitqueue_head(&(udev->waitq));
++
++ input_register_device(udev->dev);
++
++ set_bit(UIST_CREATED, &(udev->state));
++
++ return 0;
++}
++
++static int uinput_destroy_device(struct uinput_device *udev)
++{
++ if (!test_bit(UIST_CREATED, &(udev->state))) {
++ printk(KERN_WARNING "%s: create the device first\n", UINPUT_NAME);
++ return -EINVAL;
++ }
++
++ input_unregister_device(udev->dev);
++
++ clear_bit(UIST_CREATED, &(udev->state));
++
++ return 0;
++}
++
++static int uinput_open(struct inode *inode, struct file *file)
++{
++ struct uinput_device *newdev;
++ struct input_dev *newinput;
++
++ newdev = kmalloc(sizeof(struct uinput_device), GFP_KERNEL);
++ if (!newdev)
++ goto error;
++ memset(newdev, 0, sizeof(struct uinput_device));
++
++ newinput = kmalloc(sizeof(struct input_dev), GFP_KERNEL);
++ if (!newinput)
++ goto cleanup;
++ memset(newinput, 0, sizeof(struct input_dev));
++
++ newdev->dev = newinput;
++
++ file->private_data = newdev;
++
++ return 0;
++cleanup:
++ kfree(newdev);
++error:
++ return -ENOMEM;
++}
++
++static int uinput_validate_absbits(struct input_dev *dev)
++{
++ unsigned int cnt;
++ int retval = 0;
++
++ for (cnt = 0; cnt < ABS_MAX; cnt++) {
++ if (!test_bit(cnt, dev->absbit))
++ continue;
++
++ if (/*!dev->absmin[cnt] || !dev->absmax[cnt] || */
++ (dev->absmax[cnt] <= dev->absmin[cnt])) {
++ printk(KERN_DEBUG
++ "%s: invalid abs[%02x] min:%d max:%d\n",
++ UINPUT_NAME, cnt,
++ dev->absmin[cnt], dev->absmax[cnt]);
++ retval = -EINVAL;
++ break;
++ }
++
++ if ((dev->absflat[cnt] < dev->absmin[cnt]) ||
++ (dev->absflat[cnt] > dev->absmax[cnt])) {
++ printk(KERN_DEBUG
++ "%s: absflat[%02x] out of range: %d "
++ "(min:%d/max:%d)\n",
++ UINPUT_NAME, cnt, dev->absflat[cnt],
++ dev->absmin[cnt], dev->absmax[cnt]);
++ retval = -EINVAL;
++ break;
++ }
++ }
++ return retval;
++}
++
++static int uinput_alloc_device(struct file *file, const char *buffer, size_t count)
++{
++ struct uinput_user_dev *user_dev;
++ struct input_dev *dev;
++ struct uinput_device *udev;
++ int size,
++ retval;
++
++ retval = count;
++
++ udev = (struct uinput_device *)file->private_data;
++ dev = udev->dev;
++
++ user_dev = kmalloc(sizeof(*user_dev), GFP_KERNEL);
++ if (!user_dev) {
++ retval = -ENOMEM;
++ goto exit;
++ }
++
++ if (copy_from_user(user_dev, buffer, sizeof(struct uinput_user_dev))) {
++ retval = -EFAULT;
++ goto exit;
++ }
++
++ if (NULL != dev->name)
++ kfree(dev->name);
++
++ size = strnlen(user_dev->name, UINPUT_MAX_NAME_SIZE) + 1;
++ dev->name = kmalloc(size, GFP_KERNEL);
++ if (!dev->name) {
++ retval = -ENOMEM;
++ goto exit;
++ }
++
++ strncpy(dev->name, user_dev->name, size);
++ dev->idbus = user_dev->idbus;
++ dev->idvendor = user_dev->idvendor;
++ dev->idproduct = user_dev->idproduct;
++ dev->idversion = user_dev->idversion;
++ dev->ff_effects_max = user_dev->ff_effects_max;
++
++ size = sizeof(int) * (ABS_MAX + 1);
++ memcpy(dev->absmax, user_dev->absmax, size);
++ memcpy(dev->absmin, user_dev->absmin, size);
++ memcpy(dev->absfuzz, user_dev->absfuzz, size);
++ memcpy(dev->absflat, user_dev->absflat, size);
++
++ /* check if absmin/absmax/absfuzz/absflat are filled as
++ * told in Documentation/input/input-programming.txt */
++ if (test_bit(EV_ABS, dev->evbit)) {
++ retval = uinput_validate_absbits(dev);
++ if (retval < 0)
++ kfree(dev->name);
++ }
++
++exit:
++ kfree(user_dev);
++ return retval;
++}
++
++static ssize_t uinput_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
++{
++ struct uinput_device *udev = file->private_data;
++
++ if (test_bit(UIST_CREATED, &(udev->state))) {
++ struct input_event ev;
++
++ if (copy_from_user(&ev, buffer, sizeof(struct input_event)))
++ return -EFAULT;
++ input_event(udev->dev, ev.type, ev.code, ev.value);
++ }
++ else
++ count = uinput_alloc_device(file, buffer, count);
++
++ return count;
++}
++
++static ssize_t uinput_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
++{
++ struct uinput_device *udev = file->private_data;
++ int retval = 0;
++
++ if (!test_bit(UIST_CREATED, &(udev->state)))
++ return -ENODEV;
++
++ if ((udev->head == udev->tail) && (file->f_flags & O_NONBLOCK))
++ return -EAGAIN;
++
++ retval = wait_event_interruptible(udev->waitq,
++ (udev->head != udev->tail) ||
++ !test_bit(UIST_CREATED, &(udev->state)));
++
++ if (retval)
++ return retval;
++
++ if (!test_bit(UIST_CREATED, &(udev->state)))
++ return -ENODEV;
++
++ while ((udev->head != udev->tail) &&
++ (retval + sizeof(struct input_event) <= count)) {
++ if (copy_to_user(buffer + retval, &(udev->buff[udev->tail]),
++ sizeof(struct input_event))) return -EFAULT;
++ udev->tail = (udev->tail + 1) % UINPUT_BUFFER_SIZE;
++ retval += sizeof(struct input_event);
++ }
++
++ return retval;
++}
++
++static unsigned int uinput_poll(struct file *file, poll_table *wait)
++{
++ struct uinput_device *udev = file->private_data;
++
++ poll_wait(file, &udev->waitq, wait);
++
++ if (udev->head != udev->tail)
++ return POLLIN | POLLRDNORM;
++
++ return 0;
++}
++
++static int uinput_burn_device(struct uinput_device *udev)
++{
++ if (test_bit(UIST_CREATED, &(udev->state)))
++ uinput_destroy_device(udev);
++
++ kfree(udev->dev);
++ kfree(udev);
++
++ return 0;
++}
++
++static int uinput_close(struct inode *inode, struct file *file)
++{
++ return uinput_burn_device((struct uinput_device *)file->private_data);
++}
++
++static int uinput_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
++{
++ int retval = 0;
++ struct uinput_device *udev;
++
++ udev = (struct uinput_device *)file->private_data;
++
++ /* device attributes can not be changed after the device is created */
++ if (cmd >= UI_SET_EVBIT && test_bit(UIST_CREATED, &(udev->state)))
++ return -EINVAL;
++
++ switch (cmd) {
++ case UI_DEV_CREATE:
++ retval = uinput_create_device(udev);
++ break;
++
++ case UI_DEV_DESTROY:
++ retval = uinput_destroy_device(udev);
++ break;
++
++ case UI_SET_EVBIT:
++ if (arg > EV_MAX) {
++ retval = -EINVAL;
++ break;
++ }
++ set_bit(arg, udev->dev->evbit);
++ break;
++
++ case UI_SET_KEYBIT:
++ if (arg > KEY_MAX) {
++ retval = -EINVAL;
++ break;
++ }
++ set_bit(arg, udev->dev->keybit);
++ break;
++
++ case UI_SET_RELBIT:
++ if (arg > REL_MAX) {
++ retval = -EINVAL;
++ break;
++ }
++ set_bit(arg, udev->dev->relbit);
++ break;
++
++ case UI_SET_ABSBIT:
++ if (arg > ABS_MAX) {
++ retval = -EINVAL;
++ break;
++ }
++ set_bit(arg, udev->dev->absbit);
++ break;
++
++ case UI_SET_MSCBIT:
++ if (arg > MSC_MAX) {
++ retval = -EINVAL;
++ break;
++ }
++ set_bit(arg, udev->dev->mscbit);
++ break;
++
++ case UI_SET_LEDBIT:
++ if (arg > LED_MAX) {
++ retval = -EINVAL;
++ break;
++ }
++ set_bit(arg, udev->dev->ledbit);
++ break;
++
++ case UI_SET_SNDBIT:
++ if (arg > SND_MAX) {
++ retval = -EINVAL;
++ break;
++ }
++ set_bit(arg, udev->dev->sndbit);
++ break;
++
++ case UI_SET_FFBIT:
++ if (arg > FF_MAX) {
++ retval = -EINVAL;
++ break;
++ }
++ set_bit(arg, udev->dev->ffbit);
++ break;
++
++ default:
++ retval = -EFAULT;
++ }
++ return retval;
++}
++
++struct file_operations uinput_fops = {
++ owner: THIS_MODULE,
++ open: uinput_open,
++ release: uinput_close,
++ read: uinput_read,
++ write: uinput_write,
++ poll: uinput_poll,
++ ioctl: uinput_ioctl,
++};
++
++static struct miscdevice uinput_misc = {
++ fops: &uinput_fops,
++ minor: UINPUT_MINOR,
++ name: UINPUT_NAME,
++};
++
++static int __init uinput_init(void)
++{
++ return misc_register(&uinput_misc);
++}
++
++static void __exit uinput_exit(void)
++{
++ misc_deregister(&uinput_misc);
++}
++
++MODULE_AUTHOR("Aristeu Sergio Rozanski Filho");
++MODULE_DESCRIPTION("User level driver support for input subsystem");
++MODULE_LICENSE("GPL");
++
++module_init(uinput_init);
++module_exit(uinput_exit);
++
+diff -urN linux-2.4.18/drivers/isdn/avmb1/capidrv.c linux-2.4.18-mh15/drivers/isdn/avmb1/capidrv.c
+--- linux-2.4.18/drivers/isdn/avmb1/capidrv.c 2001-12-21 18:41:54.000000000 +0100
++++ linux-2.4.18-mh15/drivers/isdn/avmb1/capidrv.c 2004-08-01 16:26:23.000000000 +0200
+@@ -514,13 +514,25 @@
+
+ static void send_message(capidrv_contr * card, _cmsg * cmsg)
+ {
+- struct sk_buff *skb;
+- size_t len;
++ struct sk_buff *skb;
++ size_t len;
++ u16 err;
++
+ capi_cmsg2message(cmsg, cmsg->buf);
+ len = CAPIMSG_LEN(cmsg->buf);
+ skb = alloc_skb(len, GFP_ATOMIC);
++ if(!skb) {
++ printk(KERN_ERR "no skb len(%d) memory\n", len);
++ return;
++ }
+ memcpy(skb_put(skb, len), cmsg->buf, len);
+- (*capifuncs->capi_put_message) (global.appid, skb);
++ err = (*capifuncs->capi_put_message) (global.appid, skb);
++ if (err) {
++ printk(KERN_WARNING "%s: capi_put_message error: %04x\n",
++ __FUNCTION__, err);
++ kfree_skb(skb);
++ return;
++ }
+ global.nsentctlpkt++;
+ }
+
+@@ -2179,10 +2191,10 @@
+ free_ncci(card, card->bchans[card->nbchan-1].nccip);
+ if (card->bchans[card->nbchan-1].plcip)
+ free_plci(card, card->bchans[card->nbchan-1].plcip);
+- if (card->plci_list)
+- printk(KERN_ERR "capidrv: bug in free_plci()\n");
+ card->nbchan--;
+ }
++ if (card->plci_list)
++ printk(KERN_ERR "capidrv: bug in free_plci()\n");
+ kfree(card->bchans);
+ card->bchans = 0;
+
+diff -urN linux-2.4.18/drivers/isdn/avmb1/kcapi.c linux-2.4.18-mh15/drivers/isdn/avmb1/kcapi.c
+--- linux-2.4.18/drivers/isdn/avmb1/kcapi.c 2001-12-21 18:41:54.000000000 +0100
++++ linux-2.4.18-mh15/drivers/isdn/avmb1/kcapi.c 2004-08-01 16:26:23.000000000 +0200
+@@ -545,7 +545,13 @@
+ static void notify_up(__u32 contr)
+ {
+ struct capi_interface_user *p;
++ __u16 appl;
+
++ for (appl = 1; appl <= CAPI_MAXAPPL; appl++) {
++ if (!VALID_APPLID(appl)) continue;
++ if (APPL(appl)->releasing) continue;
++ CARD(contr)->driver->register_appl(CARD(contr), appl, &APPL(appl)->rparam);
++ }
+ printk(KERN_NOTICE "kcapi: notify up contr %d\n", contr);
+ spin_lock(&capi_users_lock);
+ for (p = capi_users; p; p = p->next) {
+@@ -705,12 +711,16 @@
+ nextpp = &(*pp)->next;
+ }
+ }
+- APPL(appl)->releasing--;
+- if (APPL(appl)->releasing <= 0) {
+- APPL(appl)->signal = 0;
+- APPL_MARK_FREE(appl);
+- printk(KERN_INFO "kcapi: appl %d down\n", appl);
+- }
++ if (APPL(appl)->releasing) { /* only release if the application was marked for release */
++ printk(KERN_DEBUG "kcapi: appl %d releasing(%d)\n", appl, APPL(appl)->releasing);
++ APPL(appl)->releasing--;
++ if (APPL(appl)->releasing <= 0) {
++ APPL(appl)->signal = 0;
++ APPL_MARK_FREE(appl);
++ printk(KERN_INFO "kcapi: appl %d down\n", appl);
++ }
++ } else
++ printk(KERN_WARNING "kcapi: appl %d card%d released without request\n", appl, card->cnr);
+ }
+ /*
+ * ncci management
+@@ -863,16 +873,7 @@
+
+ static void controllercb_ready(struct capi_ctr * card)
+ {
+- __u16 appl;
+-
+ card->cardstate = CARD_RUNNING;
+-
+- for (appl = 1; appl <= CAPI_MAXAPPL; appl++) {
+- if (!VALID_APPLID(appl)) continue;
+- if (APPL(appl)->releasing) continue;
+- card->driver->register_appl(card, appl, &APPL(appl)->rparam);
+- }
+-
+ printk(KERN_NOTICE "kcapi: card %d \"%s\" ready.\n",
+ CARDNR(card), card->name);
+
+diff -urN linux-2.4.18/drivers/usb/Config.in linux-2.4.18-mh15/drivers/usb/Config.in
+--- linux-2.4.18/drivers/usb/Config.in 2002-02-25 20:38:07.000000000 +0100
++++ linux-2.4.18-mh15/drivers/usb/Config.in 2004-08-01 16:26:23.000000000 +0200
+@@ -31,7 +31,13 @@
+
+ comment 'USB Device Class drivers'
+ dep_tristate ' USB Audio support' CONFIG_USB_AUDIO $CONFIG_USB $CONFIG_SOUND
+-dep_tristate ' USB Bluetooth support (EXPERIMENTAL)' CONFIG_USB_BLUETOOTH $CONFIG_USB $CONFIG_EXPERIMENTAL
++if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
++ if [ "$CONFIG_BLUEZ" = "n" ]; then
++ dep_tristate ' USB Bluetooth support (EXPERIMENTAL)' CONFIG_USB_BLUETOOTH $CONFIG_USB
++ else
++ comment ' USB Bluetooth can only be used with disabled Bluetooth subsystem'
++ fi
++fi
+ if [ "$CONFIG_SCSI" = "n" ]; then
+ comment ' SCSI support is needed for USB Storage'
+ fi
+diff -urN linux-2.4.18/drivers/usb/hid-core.c linux-2.4.18-mh15/drivers/usb/hid-core.c
+--- linux-2.4.18/drivers/usb/hid-core.c 2001-12-21 18:41:55.000000000 +0100
++++ linux-2.4.18-mh15/drivers/usb/hid-core.c 2004-08-01 16:26:23.000000000 +0200
+@@ -217,6 +217,8 @@
+
+ offset = report->size;
+ report->size += parser->global.report_size * parser->global.report_count;
++ if (usages < parser->global.report_count)
++ usages = parser->global.report_count;
+
+ if (usages == 0)
+ return 0; /* ignore padding fields */
+diff -urN linux-2.4.18/include/linux/firmware.h linux-2.4.18-mh15/include/linux/firmware.h
+--- linux-2.4.18/include/linux/firmware.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/include/linux/firmware.h 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,20 @@
++#ifndef _LINUX_FIRMWARE_H
++#define _LINUX_FIRMWARE_H
++#include <linux/module.h>
++#include <linux/types.h>
++#define FIRMWARE_NAME_MAX 30
++struct firmware {
++ size_t size;
++ u8 *data;
++};
++int request_firmware (const struct firmware **fw, const char *name,
++ const char *device);
++int request_firmware_nowait (
++ struct module *module,
++ const char *name, const char *device, void *context,
++ void (*cont)(const struct firmware *fw, void *context));
++/* On 2.5 'device' is 'struct device *' */
++
++void release_firmware (const struct firmware *fw);
++void register_firmware (const char *name, const u8 *data, size_t size);
++#endif
+diff -urN linux-2.4.18/include/linux/input.h linux-2.4.18-mh15/include/linux/input.h
+--- linux-2.4.18/include/linux/input.h 2001-09-13 00:34:06.000000000 +0200
++++ linux-2.4.18-mh15/include/linux/input.h 2004-08-01 16:26:23.000000000 +0200
+@@ -468,6 +468,8 @@
+ #define BUS_PCI 0x01
+ #define BUS_ISAPNP 0x02
+ #define BUS_USB 0x03
++#define BUS_HIL 0x04
++#define BUS_BLUETOOTH 0x05
+
+ #define BUS_ISA 0x10
+ #define BUS_I8042 0x11
+diff -urN linux-2.4.18/include/linux/kernel.h linux-2.4.18-mh15/include/linux/kernel.h
+--- linux-2.4.18/include/linux/kernel.h 2002-02-25 20:38:13.000000000 +0100
++++ linux-2.4.18-mh15/include/linux/kernel.h 2004-08-01 16:26:23.000000000 +0200
+@@ -11,6 +11,7 @@
+ #include <linux/linkage.h>
+ #include <linux/stddef.h>
+ #include <linux/types.h>
++#include <linux/compiler.h>
+
+ /* Optimization barrier */
+ /* The "volatile" is due to gcc bugs */
+@@ -181,4 +182,6 @@
+ char _f[20-2*sizeof(long)-sizeof(int)]; /* Padding: libc5 uses this.. */
+ };
+
+-#endif
++#define BUG_ON(condition) do { if (unlikely((condition)!=0)) BUG(); } while(0)
++
++#endif /* _LINUX_KERNEL_H */
+diff -urN linux-2.4.18/include/linux/net.h linux-2.4.18-mh15/include/linux/net.h
+--- linux-2.4.18/include/linux/net.h 2001-11-22 20:46:19.000000000 +0100
++++ linux-2.4.18-mh15/include/linux/net.h 2004-08-01 16:26:23.000000000 +0200
+@@ -139,6 +139,7 @@
+ extern int sock_recvmsg(struct socket *, struct msghdr *m, int len, int flags);
+ extern int sock_readv_writev(int type, struct inode * inode, struct file * file,
+ const struct iovec * iov, long count, long size);
++extern struct socket *sockfd_lookup(int fd, int *err);
+
+ extern int net_ratelimit(void);
+ extern unsigned long net_random(void);
+diff -urN linux-2.4.18/include/linux/uinput.h linux-2.4.18-mh15/include/linux/uinput.h
+--- linux-2.4.18/include/linux/uinput.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/include/linux/uinput.h 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,79 @@
++/*
++ * User level driver support for input subsystem
++ *
++ * Heavily based on evdev.c by Vojtech Pavlik
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ *
++ * Author: Aristeu Sergio Rozanski Filho <aris@cathedrallabs.org>
++ *
++ * Changes/Revisions:
++ * 0.1 20/06/2002
++ * - first public version
++ */
++
++#ifndef __UINPUT_H_
++#define __UINPUT_H_
++
++#ifdef __KERNEL__
++#define UINPUT_MINOR 223
++#define UINPUT_NAME "uinput"
++#define UINPUT_BUFFER_SIZE 16
++
++/* state flags => bit index for {set|clear|test}_bit ops */
++#define UIST_CREATED 0
++
++struct uinput_device {
++ struct input_dev *dev;
++ unsigned long state;
++ wait_queue_head_t waitq;
++ unsigned char ready,
++ head,
++ tail;
++ struct input_event buff[UINPUT_BUFFER_SIZE];
++};
++#endif /* __KERNEL__ */
++
++/* ioctl */
++#define UINPUT_IOCTL_BASE 'U'
++#define UI_DEV_CREATE _IO(UINPUT_IOCTL_BASE, 1)
++#define UI_DEV_DESTROY _IO(UINPUT_IOCTL_BASE, 2)
++#define UI_SET_EVBIT _IOW(UINPUT_IOCTL_BASE, 100, int)
++#define UI_SET_KEYBIT _IOW(UINPUT_IOCTL_BASE, 101, int)
++#define UI_SET_RELBIT _IOW(UINPUT_IOCTL_BASE, 102, int)
++#define UI_SET_ABSBIT _IOW(UINPUT_IOCTL_BASE, 103, int)
++#define UI_SET_MSCBIT _IOW(UINPUT_IOCTL_BASE, 104, int)
++#define UI_SET_LEDBIT _IOW(UINPUT_IOCTL_BASE, 105, int)
++#define UI_SET_SNDBIT _IOW(UINPUT_IOCTL_BASE, 106, int)
++#define UI_SET_FFBIT _IOW(UINPUT_IOCTL_BASE, 107, int)
++
++#ifndef NBITS
++#define NBITS(x) ((((x)-1)/(sizeof(long)*8))+1)
++#endif /* NBITS */
++
++#define UINPUT_MAX_NAME_SIZE 80
++struct uinput_user_dev {
++ char name[UINPUT_MAX_NAME_SIZE];
++ unsigned short idbus;
++ unsigned short idvendor;
++ unsigned short idproduct;
++ unsigned short idversion;
++ int ff_effects_max;
++ int absmax[ABS_MAX + 1];
++ int absmin[ABS_MAX + 1];
++ int absfuzz[ABS_MAX + 1];
++ int absflat[ABS_MAX + 1];
++};
++#endif /* __UINPUT_H_ */
+diff -urN linux-2.4.18/include/net/bluetooth/bluetooth.h linux-2.4.18-mh15/include/net/bluetooth/bluetooth.h
+--- linux-2.4.18/include/net/bluetooth/bluetooth.h 2001-09-07 18:28:38.000000000 +0200
++++ linux-2.4.18-mh15/include/net/bluetooth/bluetooth.h 2004-08-01 16:26:23.000000000 +0200
+@@ -23,7 +23,7 @@
+ */
+
+ /*
+- * $Id$
++ * $Id$
+ */
+
+ #ifndef __BLUETOOTH_H
+@@ -31,17 +31,64 @@
+
+ #include <asm/types.h>
+ #include <asm/byteorder.h>
++#include <linux/poll.h>
++#include <net/sock.h>
+
+ #ifndef AF_BLUETOOTH
+ #define AF_BLUETOOTH 31
+ #define PF_BLUETOOTH AF_BLUETOOTH
+ #endif
+
++/* Reserv for core and drivers use */
++#define BLUEZ_SKB_RESERVE 8
++
++#ifndef MIN
++#define MIN(a,b) ((a) < (b) ? (a) : (b))
++#endif
++
+ #define BTPROTO_L2CAP 0
+ #define BTPROTO_HCI 1
++#define BTPROTO_SCO 2
++#define BTPROTO_RFCOMM 3
++#define BTPROTO_BNEP 4
++#define BTPROTO_CMTP 5
++#define BTPROTO_HIDP 6
+
+ #define SOL_HCI 0
+ #define SOL_L2CAP 6
++#define SOL_SCO 17
++#define SOL_RFCOMM 18
++
++/* Debugging */
++#ifdef CONFIG_BLUEZ_DEBUG
++
++#define HCI_CORE_DEBUG 1
++#define HCI_SOCK_DEBUG 1
++#define HCI_UART_DEBUG 1
++#define HCI_USB_DEBUG 1
++//#define HCI_DATA_DUMP 1
++
++#define L2CAP_DEBUG 1
++#define SCO_DEBUG 1
++#define AF_BLUETOOTH_DEBUG 1
++
++#endif /* CONFIG_BLUEZ_DEBUG */
++
++extern void bluez_dump(char *pref, __u8 *buf, int count);
++
++#if __GNUC__ <= 2 && __GNUC_MINOR__ < 95
++#define __func__ __FUNCTION__
++#endif
++
++#define BT_INFO(fmt, arg...) printk(KERN_INFO fmt "\n" , ## arg)
++#define BT_DBG(fmt, arg...) printk(KERN_INFO "%s: " fmt "\n" , __func__ , ## arg)
++#define BT_ERR(fmt, arg...) printk(KERN_ERR "%s: " fmt "\n" , __func__ , ## arg)
++
++#ifdef HCI_DATA_DUMP
++#define BT_DMP(buf, len) bluez_dump(__func__, buf, len)
++#else
++#define BT_DMP(D...)
++#endif
+
+ /* Connection and socket states */
+ enum {
+@@ -50,6 +97,7 @@
+ BT_BOUND,
+ BT_LISTEN,
+ BT_CONNECT,
++ BT_CONNECT2,
+ BT_CONFIG,
+ BT_DISCONN,
+ BT_CLOSED
+@@ -66,7 +114,8 @@
+ __u8 b[6];
+ } __attribute__((packed)) bdaddr_t;
+
+-#define BDADDR_ANY ((bdaddr_t *)"\000\000\000\000\000")
++#define BDADDR_ANY (&(bdaddr_t) {{0, 0, 0, 0, 0, 0}})
++#define BDADDR_LOCAL (&(bdaddr_t) {{0, 0, 0, 0xff, 0xff, 0xff}})
+
+ /* Copy, swap, convert BD Address */
+ static inline int bacmp(bdaddr_t *ba1, bdaddr_t *ba2)
+@@ -82,6 +131,91 @@
+ char *batostr(bdaddr_t *ba);
+ bdaddr_t *strtoba(char *str);
+
++/* Common socket structures and functions */
++
++#define bluez_pi(sk) ((struct bluez_pinfo *) &sk->protinfo)
++#define bluez_sk(pi) ((struct sock *) \
++ ((void *)pi - (unsigned long)(&((struct sock *)0)->protinfo)))
++
++struct bluez_pinfo {
++ bdaddr_t src;
++ bdaddr_t dst;
++
++ struct list_head accept_q;
++ struct sock *parent;
++};
++
++struct bluez_sock_list {
++ struct sock *head;
++ rwlock_t lock;
++};
++
++int bluez_sock_register(int proto, struct net_proto_family *ops);
++int bluez_sock_unregister(int proto);
++void bluez_sock_init(struct socket *sock, struct sock *sk);
++void bluez_sock_link(struct bluez_sock_list *l, struct sock *s);
++void bluez_sock_unlink(struct bluez_sock_list *l, struct sock *s);
++int bluez_sock_recvmsg(struct socket *sock, struct msghdr *msg, int len, int flags, struct scm_cookie *scm);
++uint bluez_sock_poll(struct file * file, struct socket *sock, poll_table *wait);
++int bluez_sock_wait_state(struct sock *sk, int state, unsigned long timeo);
++
++void bluez_accept_enqueue(struct sock *parent, struct sock *sk);
++struct sock * bluez_accept_dequeue(struct sock *parent, struct socket *newsock);
++
++/* Skb helpers */
++struct bluez_skb_cb {
++ int incomming;
++};
++#define bluez_cb(skb) ((struct bluez_skb_cb *)(skb->cb))
++
++static inline struct sk_buff *bluez_skb_alloc(unsigned int len, int how)
++{
++ struct sk_buff *skb;
++
++ if ((skb = alloc_skb(len + BLUEZ_SKB_RESERVE, how))) {
++ skb_reserve(skb, BLUEZ_SKB_RESERVE);
++ bluez_cb(skb)->incomming = 0;
++ }
++ return skb;
++}
++
++static inline struct sk_buff *bluez_skb_send_alloc(struct sock *sk, unsigned long len,
++ int nb, int *err)
++{
++ struct sk_buff *skb;
++
++ if ((skb = sock_alloc_send_skb(sk, len + BLUEZ_SKB_RESERVE, nb, err))) {
++ skb_reserve(skb, BLUEZ_SKB_RESERVE);
++ bluez_cb(skb)->incomming = 0;
++ }
++
++ return skb;
++}
++
++static inline int skb_frags_no(struct sk_buff *skb)
++{
++ register struct sk_buff *frag = skb_shinfo(skb)->frag_list;
++ register int n = 1;
++
++ for (; frag; frag=frag->next, n++);
++ return n;
++}
++
++int hci_core_init(void);
++int hci_core_cleanup(void);
++int hci_sock_init(void);
++int hci_sock_cleanup(void);
++
+ int bterr(__u16 code);
+
++#ifndef MODULE_LICENSE
++#define MODULE_LICENSE(x)
++#endif
++
++#ifndef list_for_each_safe
++#define list_for_each_safe(pos, n, head) \
++ for (pos = (head)->next, n = pos->next; pos != (head); \
++ pos = n, n = pos->next)
++#endif
++
+ #endif /* __BLUETOOTH_H */
+diff -urN linux-2.4.18/include/net/bluetooth/bluez.h linux-2.4.18-mh15/include/net/bluetooth/bluez.h
+--- linux-2.4.18/include/net/bluetooth/bluez.h 2001-09-07 18:28:38.000000000 +0200
++++ linux-2.4.18-mh15/include/net/bluetooth/bluez.h 1970-01-01 01:00:00.000000000 +0100
+@@ -1,124 +0,0 @@
+-/*
+- BlueZ - Bluetooth protocol stack for Linux
+- Copyright (C) 2000-2001 Qualcomm Incorporated
+-
+- Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
+-
+- This program is free software; you can redistribute it and/or modify
+- it under the terms of the GNU General Public License version 2 as
+- published by the Free Software Foundation;
+-
+- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+- OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+- IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+- CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
+- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+-
+- ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
+- COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
+- SOFTWARE IS DISCLAIMED.
+-*/
+-
+-/*
+- * $Id$
+- */
+-
+-#ifndef __IF_BLUEZ_H
+-#define __IF_BLUEZ_H
+-
+-#include <net/sock.h>
+-
+-#define BLUEZ_MAX_PROTO 2
+-
+-/* Reserv for core and drivers use */
+-#define BLUEZ_SKB_RESERVE 8
+-
+-#ifndef MIN
+-#define MIN(a,b) ((a) < (b) ? (a) : (b))
+-#endif
+-
+-/* Debugging */
+-#ifdef BLUEZ_DEBUG
+-
+-#define HCI_CORE_DEBUG 1
+-#define HCI_SOCK_DEBUG 1
+-#define HCI_UART_DEBUG 1
+-#define HCI_USB_DEBUG 1
+-//#define HCI_DATA_DUMP 1
+-
+-#define L2CAP_DEBUG 1
+-
+-#endif /* BLUEZ_DEBUG */
+-
+-extern void bluez_dump(char *pref, __u8 *buf, int count);
+-
+-#define INF(fmt, arg...) printk(KERN_INFO fmt "\n" , ## arg)
+-#define DBG(fmt, arg...) printk(KERN_INFO __FUNCTION__ ": " fmt "\n" , ## arg)
+-#define ERR(fmt, arg...) printk(KERN_ERR __FUNCTION__ ": " fmt "\n" , ## arg)
+-
+-#ifdef HCI_DATA_DUMP
+-#define DMP(buf, len) bluez_dump(__FUNCTION__, buf, len)
+-#else
+-#define DMP(D...)
+-#endif
+-
+-/* ----- Sockets ------ */
+-struct bluez_sock_list {
+- struct sock *head;
+- rwlock_t lock;
+-};
+-
+-extern int bluez_sock_register(int proto, struct net_proto_family *ops);
+-extern int bluez_sock_unregister(int proto);
+-
+-extern void bluez_sock_link(struct bluez_sock_list *l, struct sock *s);
+-extern void bluez_sock_unlink(struct bluez_sock_list *l, struct sock *s);
+-
+-/* ----- SKB helpers ----- */
+-struct bluez_skb_cb {
+- int incomming;
+-};
+-#define bluez_cb(skb) ((struct bluez_skb_cb *)(skb->cb))
+-
+-static inline struct sk_buff *bluez_skb_alloc(unsigned int len, int how)
+-{
+- struct sk_buff *skb;
+-
+- if ((skb = alloc_skb(len + BLUEZ_SKB_RESERVE, how))) {
+- skb_reserve(skb, BLUEZ_SKB_RESERVE);
+- bluez_cb(skb)->incomming = 0;
+- }
+- return skb;
+-}
+-
+-static inline struct sk_buff *bluez_skb_send_alloc(struct sock *sk, unsigned long len,
+- int nb, int *err)
+-{
+- struct sk_buff *skb;
+-
+- if ((skb = sock_alloc_send_skb(sk, len + BLUEZ_SKB_RESERVE, nb, err))) {
+- skb_reserve(skb, BLUEZ_SKB_RESERVE);
+- bluez_cb(skb)->incomming = 0;
+- }
+-
+- return skb;
+-}
+-
+-static inline int skb_frags_no(struct sk_buff *skb)
+-{
+- register struct sk_buff *frag = skb_shinfo(skb)->frag_list;
+- register int n = 1;
+-
+- for (; frag; frag=frag->next, n++);
+- return n;
+-}
+-
+-extern int hci_core_init(void);
+-extern int hci_core_cleanup(void);
+-extern int hci_sock_init(void);
+-extern int hci_sock_cleanup(void);
+-
+-#endif /* __IF_BLUEZ_H */
+diff -urN linux-2.4.18/include/net/bluetooth/hci_core.h linux-2.4.18-mh15/include/net/bluetooth/hci_core.h
+--- linux-2.4.18/include/net/bluetooth/hci_core.h 2001-09-07 18:28:38.000000000 +0200
++++ linux-2.4.18-mh15/include/net/bluetooth/hci_core.h 2004-08-01 16:26:23.000000000 +0200
+@@ -23,7 +23,7 @@
+ */
+
+ /*
+- * $Id$
++ * $Id$
+ */
+
+ #ifndef __HCI_CORE_H
+@@ -32,14 +32,12 @@
+ #include <net/bluetooth/hci.h>
+
+ /* HCI upper protocols */
+-#define HCI_MAX_PROTO 1
+ #define HCI_PROTO_L2CAP 0
++#define HCI_PROTO_SCO 1
+
+ #define HCI_INIT_TIMEOUT (HZ * 10)
+
+-/* ----- Inquiry cache ----- */
+-#define INQUIRY_CACHE_AGE_MAX (HZ*5) // 5 seconds
+-#define INQUIRY_ENTRY_AGE_MAX (HZ*60) // 60 seconds
++/* HCI Core structures */
+
+ struct inquiry_entry {
+ struct inquiry_entry *next;
+@@ -53,111 +51,188 @@
+ struct inquiry_entry *list;
+ };
+
+-static inline void inquiry_cache_init(struct inquiry_cache *cache)
+-{
+- spin_lock_init(&cache->lock);
+- cache->list = NULL;
+-}
++struct conn_hash {
++ struct list_head list;
++ spinlock_t lock;
++ unsigned int num;
++};
+
+-static inline void inquiry_cache_lock(struct inquiry_cache *cache)
+-{
+- spin_lock(&cache->lock);
+-}
++struct hci_dev {
++ struct list_head list;
++ spinlock_t lock;
++ atomic_t refcnt;
+
+-static inline void inquiry_cache_unlock(struct inquiry_cache *cache)
+-{
+- spin_unlock(&cache->lock);
+-}
++ char name[8];
++ unsigned long flags;
++ __u16 id;
++ __u8 type;
++ bdaddr_t bdaddr;
++ __u8 features[8];
+
+-static inline void inquiry_cache_lock_bh(struct inquiry_cache *cache)
+-{
+- spin_lock_bh(&cache->lock);
+-}
++ __u16 pkt_type;
++ __u16 link_policy;
++ __u16 link_mode;
+
+-static inline void inquiry_cache_unlock_bh(struct inquiry_cache *cache)
+-{
+- spin_unlock_bh(&cache->lock);
+-}
++ unsigned long quirks;
+
+-static inline long inquiry_cache_age(struct inquiry_cache *cache)
+-{
+- return jiffies - cache->timestamp;
+-}
++ atomic_t cmd_cnt;
++ unsigned int acl_cnt;
++ unsigned int sco_cnt;
+
+-static inline long inquiry_entry_age(struct inquiry_entry *e)
+-{
+- return jiffies - e->timestamp;
+-}
+-extern void inquiry_cache_flush(struct inquiry_cache *cache);
++ unsigned int acl_mtu;
++ unsigned int sco_mtu;
++ unsigned int acl_pkts;
++ unsigned int sco_pkts;
+
+-struct hci_dev;
++ unsigned long cmd_last_tx;
++ unsigned long acl_last_tx;
++ unsigned long sco_last_tx;
++
++ struct tasklet_struct cmd_task;
++ struct tasklet_struct rx_task;
++ struct tasklet_struct tx_task;
++
++ struct sk_buff_head rx_q;
++ struct sk_buff_head raw_q;
++ struct sk_buff_head cmd_q;
++
++ struct sk_buff *sent_cmd;
++
++ struct semaphore req_lock;
++ wait_queue_head_t req_wait_q;
++ __u32 req_status;
++ __u32 req_result;
++
++ struct inquiry_cache inq_cache;
++ struct conn_hash conn_hash;
++
++ struct hci_dev_stats stat;
++
++ void *driver_data;
++ void *core_data;
++
++ atomic_t promisc;
++
++ int (*open)(struct hci_dev *hdev);
++ int (*close)(struct hci_dev *hdev);
++ int (*flush)(struct hci_dev *hdev);
++ int (*send)(struct sk_buff *skb);
++ void (*destruct)(struct hci_dev *hdev);
++ int (*ioctl)(struct hci_dev *hdev, unsigned int cmd, unsigned long arg);
++};
+
+-/* ----- HCI Connections ----- */
+ struct hci_conn {
+ struct list_head list;
++
++ atomic_t refcnt;
++ spinlock_t lock;
++
+ bdaddr_t dst;
+ __u16 handle;
++ __u16 state;
+ __u8 type;
+- unsigned int sent;
++ __u8 out;
++ __u32 link_mode;
++ unsigned long pend;
++
++ unsigned int sent;
++
++ struct sk_buff_head data_q;
+
++ struct timer_list timer;
++
+ struct hci_dev *hdev;
+ void *l2cap_data;
++ void *sco_data;
+ void *priv;
+
+- struct sk_buff_head data_q;
++ struct hci_conn *link;
+ };
+
+-struct conn_hash {
+- struct list_head list;
+- spinlock_t lock;
+- unsigned int num;
+-};
++extern struct hci_proto *hci_proto[];
++extern struct list_head hdev_list;
++extern rwlock_t hdev_list_lock;
++
++/* ----- Inquiry cache ----- */
++#define INQUIRY_CACHE_AGE_MAX (HZ*30) // 30 seconds
++#define INQUIRY_ENTRY_AGE_MAX (HZ*60) // 60 seconds
++
++#define inquiry_cache_lock(c) spin_lock(&c->lock)
++#define inquiry_cache_unlock(c) spin_unlock(&c->lock)
++#define inquiry_cache_lock_bh(c) spin_lock_bh(&c->lock)
++#define inquiry_cache_unlock_bh(c) spin_unlock_bh(&c->lock)
+
+-static inline void conn_hash_init(struct conn_hash *h)
++static inline void inquiry_cache_init(struct hci_dev *hdev)
+ {
+- INIT_LIST_HEAD(&h->list);
+- spin_lock_init(&h->lock);
+- h->num = 0;
++ struct inquiry_cache *c = &hdev->inq_cache;
++ spin_lock_init(&c->lock);
++ c->list = NULL;
+ }
+
+-static inline void conn_hash_lock(struct conn_hash *h)
++static inline int inquiry_cache_empty(struct hci_dev *hdev)
+ {
+- spin_lock(&h->lock);
++ struct inquiry_cache *c = &hdev->inq_cache;
++ return (c->list == NULL);
+ }
+
+-static inline void conn_hash_unlock(struct conn_hash *h)
++static inline long inquiry_cache_age(struct hci_dev *hdev)
+ {
+- spin_unlock(&h->lock);
++ struct inquiry_cache *c = &hdev->inq_cache;
++ return jiffies - c->timestamp;
+ }
+
+-static inline void __conn_hash_add(struct conn_hash *h, __u16 handle, struct hci_conn *c)
++static inline long inquiry_entry_age(struct inquiry_entry *e)
+ {
+- list_add(&c->list, &h->list);
+- h->num++;
++ return jiffies - e->timestamp;
+ }
+
+-static inline void conn_hash_add(struct conn_hash *h, __u16 handle, struct hci_conn *c)
++struct inquiry_entry *inquiry_cache_lookup(struct hci_dev *hdev, bdaddr_t *bdaddr);
++void inquiry_cache_update(struct hci_dev *hdev, inquiry_info *info);
++void inquiry_cache_flush(struct hci_dev *hdev);
++int inquiry_cache_dump(struct hci_dev *hdev, int num, __u8 *buf);
++
++/* ----- HCI Connections ----- */
++enum {
++ HCI_CONN_AUTH_PEND,
++ HCI_CONN_ENCRYPT_PEND
++};
++
++#define hci_conn_lock(c) spin_lock(&c->lock)
++#define hci_conn_unlock(c) spin_unlock(&c->lock)
++#define hci_conn_lock_bh(c) spin_lock_bh(&c->lock)
++#define hci_conn_unlock_bh(c) spin_unlock_bh(&c->lock)
++
++#define conn_hash_lock(d) spin_lock(&d->conn_hash->lock)
++#define conn_hash_unlock(d) spin_unlock(&d->conn_hash->lock)
++#define conn_hash_lock_bh(d) spin_lock_bh(&d->conn_hash->lock)
++#define conn_hash_unlock_bh(d) spin_unlock_bh(&d->conn_hash->lock)
++
++static inline void conn_hash_init(struct hci_dev *hdev)
+ {
+- conn_hash_lock(h);
+- __conn_hash_add(h, handle, c);
+- conn_hash_unlock(h);
++ struct conn_hash *h = &hdev->conn_hash;
++ INIT_LIST_HEAD(&h->list);
++ spin_lock_init(&h->lock);
++ h->num = 0;
+ }
+
+-static inline void __conn_hash_del(struct conn_hash *h, struct hci_conn *c)
++static inline void conn_hash_add(struct hci_dev *hdev, struct hci_conn *c)
+ {
+- list_del(&c->list);
+- h->num--;
++ struct conn_hash *h = &hdev->conn_hash;
++ list_add(&c->list, &h->list);
++ h->num++;
+ }
+
+-static inline void conn_hash_del(struct conn_hash *h, struct hci_conn *c)
++static inline void conn_hash_del(struct hci_dev *hdev, struct hci_conn *c)
+ {
+- conn_hash_lock(h);
+- __conn_hash_del(h, c);
+- conn_hash_unlock(h);
++ struct conn_hash *h = &hdev->conn_hash;
++ list_del(&c->list);
++ h->num--;
+ }
+
+-static inline struct hci_conn *__conn_hash_lookup(struct conn_hash *h, __u16 handle)
++static inline struct hci_conn *conn_hash_lookup_handle(struct hci_dev *hdev,
++ __u16 handle)
+ {
++ register struct conn_hash *h = &hdev->conn_hash;
+ register struct list_head *p;
+ register struct hci_conn *c;
+
+@@ -169,101 +244,97 @@
+ return NULL;
+ }
+
+-static inline struct hci_conn *conn_hash_lookup(struct conn_hash *h, __u16 handle)
++static inline struct hci_conn *conn_hash_lookup_ba(struct hci_dev *hdev,
++ __u8 type, bdaddr_t *ba)
+ {
+- struct hci_conn *conn;
++ register struct conn_hash *h = &hdev->conn_hash;
++ register struct list_head *p;
++ register struct hci_conn *c;
+
+- conn_hash_lock(h);
+- conn = __conn_hash_lookup(h, handle);
+- conn_hash_unlock(h);
+- return conn;
++ list_for_each(p, &h->list) {
++ c = list_entry(p, struct hci_conn, list);
++ if (c->type == type && !bacmp(&c->dst, ba))
++ return c;
++ }
++ return NULL;
+ }
+
+-/* ----- HCI Devices ----- */
+-struct hci_dev {
+- atomic_t refcnt;
+-
+- char name[8];
+- __u32 flags;
+- __u16 id;
+- __u8 type;
+- bdaddr_t bdaddr;
+- __u8 features[8];
+-
+- __u16 pkt_type;
+-
+- atomic_t cmd_cnt;
+- unsigned int acl_cnt;
+- unsigned int sco_cnt;
+-
+- unsigned int acl_mtu;
+- unsigned int sco_mtu;
+- unsigned int acl_max;
+- unsigned int sco_max;
+-
+- void *driver_data;
+- void *l2cap_data;
+- void *priv;
+-
+- struct tasklet_struct cmd_task;
+- struct tasklet_struct rx_task;
+- struct tasklet_struct tx_task;
+-
+- struct sk_buff_head rx_q;
+- struct sk_buff_head raw_q;
+- struct sk_buff_head cmd_q;
+-
+- struct sk_buff *sent_cmd;
++void hci_acl_connect(struct hci_conn *conn);
++void hci_acl_disconn(struct hci_conn *conn, __u8 reason);
++void hci_add_sco(struct hci_conn *conn, __u16 handle);
+
+- struct semaphore req_lock;
+- wait_queue_head_t req_wait_q;
+- __u32 req_status;
+- __u32 req_result;
++struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst);
++int hci_conn_del(struct hci_conn *conn);
++void hci_conn_hash_flush(struct hci_dev *hdev);
+
+- struct inquiry_cache inq_cache;
++struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *src);
++int hci_conn_auth(struct hci_conn *conn);
++int hci_conn_encrypt(struct hci_conn *conn);
+
+- struct conn_hash conn_hash;
+-
+- struct hci_dev_stats stat;
+-
+- int (*open)(struct hci_dev *hdev);
+- int (*close)(struct hci_dev *hdev);
+- int (*flush)(struct hci_dev *hdev);
+- int (*send)(struct sk_buff *skb);
+-};
++static inline void hci_conn_set_timer(struct hci_conn *conn, long timeout)
++{
++ mod_timer(&conn->timer, jiffies + timeout);
++}
+
+-static inline void hci_dev_hold(struct hci_dev *hdev)
++static inline void hci_conn_del_timer(struct hci_conn *conn)
+ {
+- atomic_inc(&hdev->refcnt);
++ del_timer(&conn->timer);
+ }
+
+-static inline void hci_dev_put(struct hci_dev *hdev)
++static inline void hci_conn_hold(struct hci_conn *conn)
+ {
+- atomic_dec(&hdev->refcnt);
++ atomic_inc(&conn->refcnt);
++ hci_conn_del_timer(conn);
+ }
+
+-extern struct hci_dev *hci_dev_get(int index);
+-extern int hci_register_dev(struct hci_dev *hdev);
+-extern int hci_unregister_dev(struct hci_dev *hdev);
+-extern int hci_dev_open(__u16 dev);
+-extern int hci_dev_close(__u16 dev);
+-extern int hci_dev_reset(__u16 dev);
+-extern int hci_dev_reset_stat(__u16 dev);
+-extern int hci_dev_info(unsigned long arg);
+-extern int hci_dev_list(unsigned long arg);
+-extern int hci_dev_setscan(unsigned long arg);
+-extern int hci_dev_setauth(unsigned long arg);
+-extern int hci_dev_setptype(unsigned long arg);
+-extern int hci_conn_list(unsigned long arg);
+-extern int hci_inquiry(unsigned long arg);
++static inline void hci_conn_put(struct hci_conn *conn)
++{
++ if (atomic_dec_and_test(&conn->refcnt)) {
++ if (conn->type == ACL_LINK) {
++ unsigned long timeo = (conn->out) ?
++ HCI_DISCONN_TIMEOUT : HCI_DISCONN_TIMEOUT * 2;
++ hci_conn_set_timer(conn, timeo);
++ } else
++ hci_conn_set_timer(conn, HZ / 100);
++ }
++}
+
+-extern __u32 hci_dev_setmode(struct hci_dev *hdev, __u32 mode);
+-extern __u32 hci_dev_getmode(struct hci_dev *hdev);
++/* ----- HCI Devices ----- */
++static inline void hci_dev_put(struct hci_dev *d)
++{
++ if (atomic_dec_and_test(&d->refcnt))
++ d->destruct(d);
++}
++#define hci_dev_hold(d) atomic_inc(&d->refcnt)
++
++#define hci_dev_lock(d) spin_lock(&d->lock)
++#define hci_dev_unlock(d) spin_unlock(&d->lock)
++#define hci_dev_lock_bh(d) spin_lock_bh(&d->lock)
++#define hci_dev_unlock_bh(d) spin_unlock_bh(&d->lock)
++
++struct hci_dev *hci_dev_get(int index);
++struct hci_dev *hci_get_route(bdaddr_t *src, bdaddr_t *dst);
++int hci_register_dev(struct hci_dev *hdev);
++int hci_unregister_dev(struct hci_dev *hdev);
++int hci_suspend_dev(struct hci_dev *hdev);
++int hci_resume_dev(struct hci_dev *hdev);
++int hci_dev_open(__u16 dev);
++int hci_dev_close(__u16 dev);
++int hci_dev_reset(__u16 dev);
++int hci_dev_reset_stat(__u16 dev);
++int hci_dev_cmd(unsigned int cmd, unsigned long arg);
++int hci_get_dev_list(unsigned long arg);
++int hci_get_dev_info(unsigned long arg);
++int hci_get_conn_list(unsigned long arg);
++int hci_get_conn_info(struct hci_dev *hdev, unsigned long arg);
++int hci_inquiry(unsigned long arg);
+
+-extern int hci_recv_frame(struct sk_buff *skb);
++int hci_recv_frame(struct sk_buff *skb);
++void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb);
+
+ /* ----- LMP capabilities ----- */
+ #define lmp_rswitch_capable(dev) (dev->features[0] & LMP_RSWITCH)
++#define lmp_encrypt_capable(dev) (dev->features[0] & LMP_ENCRYPT)
+
+ /* ----- HCI tasks ----- */
+ static inline void hci_sched_cmd(struct hci_dev *hdev)
+@@ -284,43 +355,130 @@
+ /* ----- HCI protocols ----- */
+ struct hci_proto {
+ char *name;
+- __u32 id;
+- __u32 flags;
++ unsigned int id;
++ unsigned long flags;
+
+ void *priv;
+
+- int (*connect_ind) (struct hci_dev *hdev, bdaddr_t *bdaddr);
+- int (*connect_cfm) (struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 status, struct hci_conn *conn);
++ int (*connect_ind) (struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 type);
++ int (*connect_cfm) (struct hci_conn *conn, __u8 status);
+ int (*disconn_ind) (struct hci_conn *conn, __u8 reason);
+- int (*recv_acldata) (struct hci_conn *conn, struct sk_buff *skb , __u16 flags);
++ int (*recv_acldata) (struct hci_conn *conn, struct sk_buff *skb, __u16 flags);
+ int (*recv_scodata) (struct hci_conn *conn, struct sk_buff *skb);
++ int (*auth_cfm) (struct hci_conn *conn, __u8 status);
++ int (*encrypt_cfm) (struct hci_conn *conn, __u8 status);
+ };
+
+-extern int hci_register_proto(struct hci_proto *hproto);
+-extern int hci_unregister_proto(struct hci_proto *hproto);
+-extern int hci_register_notifier(struct notifier_block *nb);
+-extern int hci_unregister_notifier(struct notifier_block *nb);
+-extern int hci_connect(struct hci_dev * hdev, bdaddr_t * bdaddr);
+-extern int hci_disconnect(struct hci_conn *conn, __u8 reason);
+-extern int hci_send_cmd(struct hci_dev *hdev, __u16 ogf, __u16 ocf, __u32 plen, void * param);
+-extern int hci_send_raw(struct sk_buff *skb);
+-extern int hci_send_acl(struct hci_conn *conn, struct sk_buff *skb, __u16 flags);
+-extern int hci_send_sco(struct hci_conn *conn, struct sk_buff *skb);
++static inline int hci_proto_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 type)
++{
++ register struct hci_proto *hp;
++ int mask = 0;
++
++ hp = hci_proto[HCI_PROTO_L2CAP];
++ if (hp && hp->connect_ind)
++ mask |= hp->connect_ind(hdev, bdaddr, type);
++
++ hp = hci_proto[HCI_PROTO_SCO];
++ if (hp && hp->connect_ind)
++ mask |= hp->connect_ind(hdev, bdaddr, type);
++
++ return mask;
++}
++
++static inline void hci_proto_connect_cfm(struct hci_conn *conn, __u8 status)
++{
++ register struct hci_proto *hp;
++
++ hp = hci_proto[HCI_PROTO_L2CAP];
++ if (hp && hp->connect_cfm)
++ hp->connect_cfm(conn, status);
++
++ hp = hci_proto[HCI_PROTO_SCO];
++ if (hp && hp->connect_cfm)
++ hp->connect_cfm(conn, status);
++}
++
++static inline void hci_proto_disconn_ind(struct hci_conn *conn, __u8 reason)
++{
++ register struct hci_proto *hp;
++
++ hp = hci_proto[HCI_PROTO_L2CAP];
++ if (hp && hp->disconn_ind)
++ hp->disconn_ind(conn, reason);
++
++ hp = hci_proto[HCI_PROTO_SCO];
++ if (hp && hp->disconn_ind)
++ hp->disconn_ind(conn, reason);
++}
++
++static inline void hci_proto_auth_cfm(struct hci_conn *conn, __u8 status)
++{
++ register struct hci_proto *hp;
++
++ hp = hci_proto[HCI_PROTO_L2CAP];
++ if (hp && hp->auth_cfm)
++ hp->auth_cfm(conn, status);
++
++ hp = hci_proto[HCI_PROTO_SCO];
++ if (hp && hp->auth_cfm)
++ hp->auth_cfm(conn, status);
++}
++
++static inline void hci_proto_encrypt_cfm(struct hci_conn *conn, __u8 status)
++{
++ register struct hci_proto *hp;
++
++ hp = hci_proto[HCI_PROTO_L2CAP];
++ if (hp && hp->encrypt_cfm)
++ hp->encrypt_cfm(conn, status);
++
++ hp = hci_proto[HCI_PROTO_SCO];
++ if (hp && hp->encrypt_cfm)
++ hp->encrypt_cfm(conn, status);
++}
++
++int hci_register_proto(struct hci_proto *hproto);
++int hci_unregister_proto(struct hci_proto *hproto);
++int hci_register_notifier(struct notifier_block *nb);
++int hci_unregister_notifier(struct notifier_block *nb);
++
++int hci_send_cmd(struct hci_dev *hdev, __u16 ogf, __u16 ocf, __u32 plen, void *param);
++int hci_send_acl(struct hci_conn *conn, struct sk_buff *skb, __u16 flags);
++int hci_send_sco(struct hci_conn *conn, struct sk_buff *skb);
++
++void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 ogf, __u16 ocf);
++
++void hci_si_event(struct hci_dev *hdev, int type, int dlen, void *data);
+
+ /* ----- HCI Sockets ----- */
+-extern void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb);
++void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb);
+
+ /* HCI info for socket */
+-#define hci_pi(sk) ((struct hci_pinfo *) &sk->protinfo)
++#define hci_pi(sk) ((struct hci_pinfo *) &sk->tp_pinfo)
+ struct hci_pinfo {
+ struct hci_dev *hdev;
+ struct hci_filter filter;
+ __u32 cmsg_mask;
+ };
+
++/* HCI security filter */
++#define HCI_SFLT_MAX_OGF 5
++
++struct hci_sec_filter {
++ __u32 type_mask;
++ __u32 event_mask[2];
++ __u32 ocf_mask[HCI_SFLT_MAX_OGF + 1][4];
++};
++
+ /* ----- HCI requests ----- */
+ #define HCI_REQ_DONE 0
+ #define HCI_REQ_PEND 1
+ #define HCI_REQ_CANCELED 2
+
++#define hci_req_lock(d) down(&d->req_lock)
++#define hci_req_unlock(d) up(&d->req_lock)
++
++void hci_req_complete(struct hci_dev *hdev, int result);
++void hci_req_cancel(struct hci_dev *hdev, int err);
++
+ #endif /* __HCI_CORE_H */
+diff -urN linux-2.4.18/include/net/bluetooth/hci.h linux-2.4.18-mh15/include/net/bluetooth/hci.h
+--- linux-2.4.18/include/net/bluetooth/hci.h 2001-09-07 18:28:38.000000000 +0200
++++ linux-2.4.18-mh15/include/net/bluetooth/hci.h 2004-08-01 16:26:23.000000000 +0200
+@@ -23,59 +23,80 @@
+ */
+
+ /*
+- * $Id$
++ * $Id$
+ */
+
+ #ifndef __HCI_H
+ #define __HCI_H
+
+-#include <asm/byteorder.h>
+-
+-#define HCI_MAX_DEV 8
+-#define HCI_MAX_FRAME_SIZE 2048
++#define HCI_MAX_ACL_SIZE 1024
++#define HCI_MAX_SCO_SIZE 255
++#define HCI_MAX_EVENT_SIZE 260
++#define HCI_MAX_FRAME_SIZE (HCI_MAX_ACL_SIZE + 4)
+
+ /* HCI dev events */
+ #define HCI_DEV_REG 1
+ #define HCI_DEV_UNREG 2
+ #define HCI_DEV_UP 3
+ #define HCI_DEV_DOWN 4
++#define HCI_DEV_SUSPEND 5
++#define HCI_DEV_RESUME 6
+
+ /* HCI device types */
+-#define HCI_UART 0
++#define HCI_VHCI 0
+ #define HCI_USB 1
+-#define HCI_VHCI 2
+-
+-/* HCI device modes */
+-#define HCI_NORMAL 0x0001
+-#define HCI_RAW 0x0002
+-#define HCI_MODE_MASK (HCI_NORMAL | HCI_RAW)
+-#define HCI_SOCK 0x1000
+-
+-/* HCI device states */
+-#define HCI_INIT 0x0010
+-#define HCI_UP 0x0020
+-#define HCI_RUNNING 0x0040
++#define HCI_PCCARD 2
++#define HCI_UART 3
++#define HCI_RS232 4
++#define HCI_PCI 5
++
++/* HCI device quirks */
++enum {
++ HCI_QUIRK_RESET_ON_INIT
++};
+
+ /* HCI device flags */
+-#define HCI_PSCAN 0x0100
+-#define HCI_ISCAN 0x0200
+-#define HCI_AUTH 0x0400
++enum {
++ HCI_UP,
++ HCI_INIT,
++ HCI_RUNNING,
++
++ HCI_PSCAN,
++ HCI_ISCAN,
++ HCI_AUTH,
++ HCI_ENCRYPT,
++ HCI_INQUIRY,
++
++ HCI_RAW
++};
+
+-/* HCI Ioctl defines */
++/* HCI ioctl defines */
+ #define HCIDEVUP _IOW('H', 201, int)
+ #define HCIDEVDOWN _IOW('H', 202, int)
+ #define HCIDEVRESET _IOW('H', 203, int)
+-#define HCIRESETSTAT _IOW('H', 204, int)
+-#define HCIGETINFO _IOR('H', 205, int)
+-#define HCIGETDEVLIST _IOR('H', 206, int)
+-#define HCISETRAW _IOW('H', 207, int)
+-#define HCISETSCAN _IOW('H', 208, int)
+-#define HCISETAUTH _IOW('H', 209, int)
+-#define HCIINQUIRY _IOR('H', 210, int)
+-#define HCISETPTYPE _IOW('H', 211, int)
++#define HCIDEVRESTAT _IOW('H', 204, int)
++
++#define HCIGETDEVLIST _IOR('H', 210, int)
++#define HCIGETDEVINFO _IOR('H', 211, int)
+ #define HCIGETCONNLIST _IOR('H', 212, int)
++#define HCIGETCONNINFO _IOR('H', 213, int)
+
+-#ifndef __NO_HCI_DEFS
++#define HCISETRAW _IOW('H', 220, int)
++#define HCISETSCAN _IOW('H', 221, int)
++#define HCISETAUTH _IOW('H', 222, int)
++#define HCISETENCRYPT _IOW('H', 223, int)
++#define HCISETPTYPE _IOW('H', 224, int)
++#define HCISETLINKPOL _IOW('H', 225, int)
++#define HCISETLINKMODE _IOW('H', 226, int)
++#define HCISETACLMTU _IOW('H', 227, int)
++#define HCISETSCOMTU _IOW('H', 228, int)
++
++#define HCIINQUIRY _IOR('H', 240, int)
++
++/* HCI timeouts */
++#define HCI_CONN_TIMEOUT (HZ * 40)
++#define HCI_DISCONN_TIMEOUT (HZ * 2)
++#define HCI_CONN_IDLE_TIMEOUT (HZ * 60)
+
+ /* HCI Packet types */
+ #define HCI_COMMAND_PKT 0x01
+@@ -92,11 +113,18 @@
+ #define HCI_DH3 0x0800
+ #define HCI_DH5 0x8000
+
++#define HCI_HV1 0x0020
++#define HCI_HV2 0x0040
++#define HCI_HV3 0x0080
++
++#define SCO_PTYPE_MASK (HCI_HV1 | HCI_HV2 | HCI_HV3)
++#define ACL_PTYPE_MASK (~SCO_PTYPE_MASK)
++
+ /* ACL flags */
+-#define ACL_CONT 0x0001
+-#define ACL_START 0x0002
+-#define ACL_ACTIVE_BCAST 0x0010
+-#define ACL_PICO_BCAST 0x0020
++#define ACL_CONT 0x01
++#define ACL_START 0x02
++#define ACL_ACTIVE_BCAST 0x04
++#define ACL_PICO_BCAST 0x08
+
+ /* Baseband links */
+ #define SCO_LINK 0x00
+@@ -125,6 +153,20 @@
+ #define LMP_PSCHEME 0x02
+ #define LMP_PCONTROL 0x04
+
++/* Link policies */
++#define HCI_LP_RSWITCH 0x0001
++#define HCI_LP_HOLD 0x0002
++#define HCI_LP_SNIFF 0x0004
++#define HCI_LP_PARK 0x0008
++
++/* Link mode */
++#define HCI_LM_ACCEPT 0x8000
++#define HCI_LM_MASTER 0x0001
++#define HCI_LM_AUTH 0x0002
++#define HCI_LM_ENCRYPT 0x0004
++#define HCI_LM_TRUSTED 0x0008
++#define HCI_LM_RELIABLE 0x0010
++
+ /* ----- HCI Commands ----- */
+ /* OGF & OCF values */
+
+@@ -137,9 +179,10 @@
+ __u8 hci_ver;
+ __u16 hci_rev;
+ __u8 lmp_ver;
+- __u16 man_name;
+- __u16 lmp_sub;
++ __u16 manufacturer;
++ __u16 lmp_subver;
+ } __attribute__ ((packed)) read_local_version_rp;
++#define READ_LOCAL_VERSION_RP_SIZE 9
+
+ #define OCF_READ_LOCAL_FEATURES 0x0003
+ typedef struct {
+@@ -165,18 +208,24 @@
+ /* Host Controller and Baseband */
+ #define OGF_HOST_CTL 0x03
+ #define OCF_RESET 0x0003
++#define OCF_READ_AUTH_ENABLE 0x001F
+ #define OCF_WRITE_AUTH_ENABLE 0x0020
+- #define AUTH_DISABLED 0x00
+- #define AUTH_ENABLED 0x01
++ #define AUTH_DISABLED 0x00
++ #define AUTH_ENABLED 0x01
++
++#define OCF_READ_ENCRYPT_MODE 0x0021
++#define OCF_WRITE_ENCRYPT_MODE 0x0022
++ #define ENCRYPT_DISABLED 0x00
++ #define ENCRYPT_P2P 0x01
++ #define ENCRYPT_BOTH 0x02
+
+ #define OCF_WRITE_CA_TIMEOUT 0x0016
+ #define OCF_WRITE_PG_TIMEOUT 0x0018
+
+ #define OCF_WRITE_SCAN_ENABLE 0x001A
+- #define SCANS_DISABLED 0x00
+- #define IS_ENA_PS_DIS 0x01
+- #define IS_DIS_PS_ENA 0x02
+- #define IS_ENA_PS_ENA 0x03
++ #define SCAN_DISABLED 0x00
++ #define SCAN_INQUIRY 0x01
++ #define SCAN_PAGE 0x02
+
+ #define OCF_SET_EVENT_FLT 0x0005
+ typedef struct {
+@@ -226,9 +275,18 @@
+ } __attribute__ ((packed)) write_class_of_dev_cp;
+ #define WRITE_CLASS_OF_DEV_CP_SIZE 3
+
++#define OCF_HOST_BUFFER_SIZE 0x0033
++typedef struct {
++ __u16 acl_mtu;
++ __u8 sco_mtu;
++ __u16 acl_max_pkt;
++ __u16 sco_max_pkt;
++} __attribute__ ((packed)) host_buffer_size_cp;
++#define HOST_BUFFER_SIZE_CP_SIZE 7
++
+ /* Link Control */
+ #define OGF_LINK_CTL 0x01
+-#define OCF_CREATE_CONN 0x0005
++#define OCF_CREATE_CONN 0x0005
+ typedef struct {
+ bdaddr_t bdaddr;
+ __u16 pkt_type;
+@@ -246,6 +304,13 @@
+ } __attribute__ ((packed)) accept_conn_req_cp;
+ #define ACCEPT_CONN_REQ_CP_SIZE 7
+
++#define OCF_REJECT_CONN_REQ 0x000a
++typedef struct {
++ bdaddr_t bdaddr;
++ __u8 reason;
++} __attribute__ ((packed)) reject_conn_req_cp;
++#define REJECT_CONN_REQ_CP_SIZE 7
++
+ #define OCF_DISCONNECT 0x0006
+ typedef struct {
+ __u16 handle;
+@@ -253,17 +318,142 @@
+ } __attribute__ ((packed)) disconnect_cp;
+ #define DISCONNECT_CP_SIZE 3
+
++#define OCF_ADD_SCO 0x0007
++typedef struct {
++ __u16 handle;
++ __u16 pkt_type;
++} __attribute__ ((packed)) add_sco_cp;
++#define ADD_SCO_CP_SIZE 4
++
+ #define OCF_INQUIRY 0x0001
+ typedef struct {
+ __u8 lap[3];
+- __u8 lenght;
++ __u8 length;
+ __u8 num_rsp;
+ } __attribute__ ((packed)) inquiry_cp;
+ #define INQUIRY_CP_SIZE 5
+
+-#define OGF_LINK_POLICY 0x02 /* Link Policy */
++typedef struct {
++ __u8 status;
++ bdaddr_t bdaddr;
++} __attribute__ ((packed)) status_bdaddr_rp;
++#define STATUS_BDADDR_RP_SIZE 7
++
++#define OCF_INQUIRY_CANCEL 0x0002
++
++#define OCF_LINK_KEY_REPLY 0x000B
++#define OCF_LINK_KEY_NEG_REPLY 0x000C
++typedef struct {
++ bdaddr_t bdaddr;
++ __u8 link_key[16];
++} __attribute__ ((packed)) link_key_reply_cp;
++#define LINK_KEY_REPLY_CP_SIZE 22
++
++#define OCF_PIN_CODE_REPLY 0x000D
++#define OCF_PIN_CODE_NEG_REPLY 0x000E
++typedef struct {
++ bdaddr_t bdaddr;
++ __u8 pin_len;
++ __u8 pin_code[16];
++} __attribute__ ((packed)) pin_code_reply_cp;
++#define PIN_CODE_REPLY_CP_SIZE 23
++
++#define OCF_CHANGE_CONN_PTYPE 0x000F
++typedef struct {
++ __u16 handle;
++ __u16 pkt_type;
++} __attribute__ ((packed)) change_conn_ptype_cp;
++#define CHANGE_CONN_PTYPE_CP_SIZE 4
++
++#define OCF_AUTH_REQUESTED 0x0011
++typedef struct {
++ __u16 handle;
++} __attribute__ ((packed)) auth_requested_cp;
++#define AUTH_REQUESTED_CP_SIZE 2
++
++#define OCF_SET_CONN_ENCRYPT 0x0013
++typedef struct {
++ __u16 handle;
++ __u8 encrypt;
++} __attribute__ ((packed)) set_conn_encrypt_cp;
++#define SET_CONN_ENCRYPT_CP_SIZE 3
++
++#define OCF_REMOTE_NAME_REQ 0x0019
++typedef struct {
++ bdaddr_t bdaddr;
++ __u8 pscan_rep_mode;
++ __u8 pscan_mode;
++ __u16 clock_offset;
++} __attribute__ ((packed)) remote_name_req_cp;
++#define REMOTE_NAME_REQ_CP_SIZE 10
++
++#define OCF_READ_REMOTE_FEATURES 0x001B
++typedef struct {
++ __u16 handle;
++} __attribute__ ((packed)) read_remote_features_cp;
++#define READ_REMOTE_FEATURES_CP_SIZE 2
++
++#define OCF_READ_REMOTE_VERSION 0x001D
++typedef struct {
++ __u16 handle;
++} __attribute__ ((packed)) read_remote_version_cp;
++#define READ_REMOTE_VERSION_CP_SIZE 2
++
++/* Link Policy */
++#define OGF_LINK_POLICY 0x02
++#define OCF_ROLE_DISCOVERY 0x0009
++typedef struct {
++ __u16 handle;
++} __attribute__ ((packed)) role_discovery_cp;
++#define ROLE_DISCOVERY_CP_SIZE 2
++typedef struct {
++ __u8 status;
++ __u16 handle;
++ __u8 role;
++} __attribute__ ((packed)) role_discovery_rp;
++#define ROLE_DISCOVERY_RP_SIZE 4
++
++#define OCF_READ_LINK_POLICY 0x000C
++typedef struct {
++ __u16 handle;
++} __attribute__ ((packed)) read_link_policy_cp;
++#define READ_LINK_POLICY_CP_SIZE 2
++typedef struct {
++ __u8 status;
++ __u16 handle;
++ __u16 policy;
++} __attribute__ ((packed)) read_link_policy_rp;
++#define READ_LINK_POLICY_RP_SIZE 5
+
+-/* --------- HCI Events --------- */
++#define OCF_SWITCH_ROLE 0x000B
++typedef struct {
++ bdaddr_t bdaddr;
++ __u8 role;
++} __attribute__ ((packed)) switch_role_cp;
++#define SWITCH_ROLE_CP_SIZE 7
++
++#define OCF_WRITE_LINK_POLICY 0x000D
++typedef struct {
++ __u16 handle;
++ __u16 policy;
++} __attribute__ ((packed)) write_link_policy_cp;
++#define WRITE_LINK_POLICY_CP_SIZE 4
++typedef struct {
++ __u8 status;
++ __u16 handle;
++} __attribute__ ((packed)) write_link_policy_rp;
++#define WRITE_LINK_POLICY_RP_SIZE 3
++
++/* Status params */
++#define OGF_STATUS_PARAM 0x05
++
++/* Testing commands */
++#define OGF_TESTING_CMD 0x3e
++
++/* Vendor specific commands */
++#define OGF_VENDOR_CMD 0x3f
++
++/* ---- HCI Events ---- */
+ #define EVT_INQUIRY_COMPLETE 0x01
+
+ #define EVT_INQUIRY_RESULT 0x02
+@@ -272,11 +462,22 @@
+ __u8 pscan_rep_mode;
+ __u8 pscan_period_mode;
+ __u8 pscan_mode;
+- __u8 class[3];
++ __u8 dev_class[3];
+ __u16 clock_offset;
+ } __attribute__ ((packed)) inquiry_info;
+ #define INQUIRY_INFO_SIZE 14
+
++#define EVT_INQUIRY_RESULT_WITH_RSSI 0x22
++typedef struct {
++ bdaddr_t bdaddr;
++ __u8 pscan_rep_mode;
++ __u8 pscan_period_mode;
++ __u8 dev_class[3];
++ __u16 clock_offset;
++ __s8 rssi;
++} __attribute__ ((packed)) inquiry_info_with_rssi;
++#define INQUIRY_INFO_WITH_RSSI_SIZE 14
++
+ #define EVT_CONN_COMPLETE 0x03
+ typedef struct {
+ __u8 status;
+@@ -303,6 +504,44 @@
+ } __attribute__ ((packed)) evt_disconn_complete;
+ #define EVT_DISCONN_COMPLETE_SIZE 4
+
++#define EVT_AUTH_COMPLETE 0x06
++typedef struct {
++ __u8 status;
++ __u16 handle;
++} __attribute__ ((packed)) evt_auth_complete;
++#define EVT_AUTH_COMPLETE_SIZE 3
++
++#define EVT_REMOTE_NAME_REQ_COMPLETE 0x07
++typedef struct {
++ __u8 status;
++ bdaddr_t bdaddr;
++ __u8 name[248];
++} __attribute__ ((packed)) evt_remote_name_req_complete;
++#define EVT_REMOTE_NAME_REQ_COMPLETE_SIZE 255
++
++#define EVT_ENCRYPT_CHANGE 0x08
++typedef struct {
++ __u8 status;
++ __u16 handle;
++ __u8 encrypt;
++} __attribute__ ((packed)) evt_encrypt_change;
++#define EVT_ENCRYPT_CHANGE_SIZE 5
++
++#define EVT_QOS_SETUP_COMPLETE 0x0D
++typedef struct {
++ __u8 service_type;
++ __u32 token_rate;
++ __u32 peak_bandwidth;
++ __u32 latency;
++ __u32 delay_variation;
++} __attribute__ ((packed)) hci_qos;
++typedef struct {
++ __u8 status;
++ __u16 handle;
++ hci_qos qos;
++} __attribute__ ((packed)) evt_qos_setup_complete;
++#define EVT_QOS_SETUP_COMPLETE_SIZE 20
++
+ #define EVT_CMD_COMPLETE 0x0e
+ typedef struct {
+ __u8 ncmd;
+@@ -321,16 +560,78 @@
+ #define EVT_NUM_COMP_PKTS 0x13
+ typedef struct {
+ __u8 num_hndl;
+- /* variable lenght part */
++ /* variable length part */
+ } __attribute__ ((packed)) evt_num_comp_pkts;
+ #define EVT_NUM_COMP_PKTS_SIZE 1
+
+-#define EVT_HCI_DEV_EVENT 0xfd
++#define EVT_ROLE_CHANGE 0x12
++typedef struct {
++ __u8 status;
++ bdaddr_t bdaddr;
++ __u8 role;
++} __attribute__ ((packed)) evt_role_change;
++#define EVT_ROLE_CHANGE_SIZE 8
++
++#define EVT_PIN_CODE_REQ 0x16
++typedef struct {
++ bdaddr_t bdaddr;
++} __attribute__ ((packed)) evt_pin_code_req;
++#define EVT_PIN_CODE_REQ_SIZE 6
++
++#define EVT_LINK_KEY_REQ 0x17
++typedef struct {
++ bdaddr_t bdaddr;
++} __attribute__ ((packed)) evt_link_key_req;
++#define EVT_LINK_KEY_REQ_SIZE 6
++
++#define EVT_LINK_KEY_NOTIFY 0x18
++typedef struct {
++ bdaddr_t bdaddr;
++ __u8 link_key[16];
++ __u8 key_type;
++} __attribute__ ((packed)) evt_link_key_notify;
++#define EVT_LINK_KEY_NOTIFY_SIZE 23
++
++#define EVT_READ_REMOTE_FEATURES_COMPLETE 0x0B
++typedef struct {
++ __u8 status;
++ __u16 handle;
++ __u8 features[8];
++} __attribute__ ((packed)) evt_read_remote_features_complete;
++#define EVT_READ_REMOTE_FEATURES_COMPLETE_SIZE 11
++
++#define EVT_READ_REMOTE_VERSION_COMPLETE 0x0C
++typedef struct {
++ __u8 status;
++ __u16 handle;
++ __u8 lmp_ver;
++ __u16 manufacturer;
++ __u16 lmp_subver;
++} __attribute__ ((packed)) evt_read_remote_version_complete;
++#define EVT_READ_REMOTE_VERSION_COMPLETE_SIZE 8
++
++/* Internal events generated by BlueZ stack */
++#define EVT_STACK_INTERNAL 0xfd
++typedef struct {
++ __u16 type;
++ __u8 data[0];
++} __attribute__ ((packed)) evt_stack_internal;
++#define EVT_STACK_INTERNAL_SIZE 2
++
++#define EVT_SI_DEVICE 0x01
++typedef struct {
++ __u16 event;
++ __u16 dev_id;
++} __attribute__ ((packed)) evt_si_device;
++#define EVT_SI_DEVICE_SIZE 4
++
++#define EVT_SI_SECURITY 0x02
+ typedef struct {
+ __u16 event;
+- __u16 param;
+-} __attribute__ ((packed)) evt_hci_dev_event;
+-#define EVT_HCI_DEV_EVENT_SIZE 4
++ __u16 proto;
++ __u16 subproto;
++ __u8 incomming;
++} __attribute__ ((packed)) evt_si_security;
+
+ /* -------- HCI Packet structures -------- */
+ #define HCI_TYPE_LEN 1
+@@ -369,14 +670,14 @@
+ #define acl_handle(h) (h & 0x0fff)
+ #define acl_flags(h) (h >> 12)
+
+-#endif /* _NO_HCI_DEFS */
+-
+ /* HCI Socket options */
+-#define HCI_DATA_DIR 0x0001
+-#define HCI_FILTER 0x0002
++#define HCI_DATA_DIR 1
++#define HCI_FILTER 2
++#define HCI_TIME_STAMP 3
+
+ /* HCI CMSG flags */
+ #define HCI_CMSG_DIR 0x0001
++#define HCI_CMSG_TSTAMP 0x0002
+
+ struct sockaddr_hci {
+ sa_family_t hci_family;
+@@ -387,27 +688,29 @@
+ struct hci_filter {
+ __u32 type_mask;
+ __u32 event_mask[2];
++ __u16 opcode;
+ };
+
+-struct hci_dev_req {
+- __u16 dev_id;
+- __u32 dev_opt;
+-};
+-
+-struct hci_dev_list_req {
+- __u16 dev_num;
+- struct hci_dev_req dev_req[0]; /* hci_dev_req structures */
+-};
+-
+-struct hci_inquiry_req {
+- __u16 dev_id;
+- __u16 flags;
+- __u8 lap[3];
+- __u8 length;
+- __u8 num_rsp;
+-};
+-#define IREQ_CACHE_FLUSH 0x0001
++#define HCI_FLT_TYPE_BITS 31
++#define HCI_FLT_EVENT_BITS 63
++#define HCI_FLT_OGF_BITS 63
++#define HCI_FLT_OCF_BITS 127
++
++#if BITS_PER_LONG == 64
++static inline void hci_set_bit(int nr, void *addr)
++{
++ *((__u32 *) addr + (nr >> 5)) |= ((__u32) 1 << (nr & 31));
++}
++static inline int hci_test_bit(int nr, void *addr)
++{
++ return *((__u32 *) addr + (nr >> 5)) & ((__u32) 1 << (nr & 31));
++}
++#else
++#define hci_set_bit set_bit
++#define hci_test_bit test_bit
++#endif
+
++/* Ioctl requests structures */
+ struct hci_dev_stats {
+ __u32 err_rx;
+ __u32 err_tx;
+@@ -433,11 +736,13 @@
+ __u8 features[8];
+
+ __u32 pkt_type;
++ __u32 link_policy;
++ __u32 link_mode;
+
+ __u16 acl_mtu;
+- __u16 acl_max;
++ __u16 acl_pkts;
+ __u16 sco_mtu;
+- __u16 sco_max;
++ __u16 sco_pkts;
+
+ struct hci_dev_stats stat;
+ };
+@@ -445,6 +750,20 @@
+ struct hci_conn_info {
+ __u16 handle;
+ bdaddr_t bdaddr;
++ __u8 type;
++ __u8 out;
++ __u16 state;
++ __u32 link_mode;
++};
++
++struct hci_dev_req {
++ __u16 dev_id;
++ __u32 dev_opt;
++};
++
++struct hci_dev_list_req {
++ __u16 dev_num;
++ struct hci_dev_req dev_req[0]; /* hci_dev_req structures */
+ };
+
+ struct hci_conn_list_req {
+@@ -453,4 +772,26 @@
+ struct hci_conn_info conn_info[0];
+ };
+
++struct hci_conn_info_req {
++ bdaddr_t bdaddr;
++ __u8 type;
++ struct hci_conn_info conn_info[0];
++};
++
++struct hci_inquiry_req {
++ __u16 dev_id;
++ __u16 flags;
++ __u8 lap[3];
++ __u8 length;
++ __u8 num_rsp;
++};
++#define IREQ_CACHE_FLUSH 0x0001
++
++struct hci_remotename_req {
++ __u16 dev_id;
++ __u16 flags;
++ bdaddr_t bdaddr;
++ __u8 name[248];
++};
++
+ #endif /* __HCI_H */
+diff -urN linux-2.4.18/include/net/bluetooth/hci_uart.h linux-2.4.18-mh15/include/net/bluetooth/hci_uart.h
+--- linux-2.4.18/include/net/bluetooth/hci_uart.h 2001-09-07 18:28:38.000000000 +0200
++++ linux-2.4.18-mh15/include/net/bluetooth/hci_uart.h 1970-01-01 01:00:00.000000000 +0100
+@@ -1,62 +0,0 @@
+-/*
+- BlueZ - Bluetooth protocol stack for Linux
+- Copyright (C) 2000-2001 Qualcomm Incorporated
+-
+- Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
+-
+- This program is free software; you can redistribute it and/or modify
+- it under the terms of the GNU General Public License version 2 as
+- published by the Free Software Foundation;
+-
+- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+- OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+- IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+- CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
+- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+-
+- ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
+- COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
+- SOFTWARE IS DISCLAIMED.
+-*/
+-
+-/*
+- * $Id$
+- */
+-
+-#ifndef N_HCI
+-#define N_HCI 15
+-#endif
+-
+-#ifdef __KERNEL__
+-
+-#define tty2n_hci(tty) ((struct n_hci *)((tty)->disc_data))
+-#define n_hci2tty(n_hci) ((n_hci)->tty)
+-
+-struct n_hci {
+- struct tty_struct *tty;
+- struct hci_dev hdev;
+-
+- struct sk_buff_head txq;
+- unsigned long tx_state;
+-
+- spinlock_t rx_lock;
+- unsigned long rx_state;
+- unsigned long rx_count;
+- struct sk_buff *rx_skb;
+-};
+-
+-/* Transmit states */
+-#define TRANS_SENDING 1
+-#define TRANS_WAKEUP 2
+-
+-/* Receiver States */
+-#define WAIT_PACKET_TYPE 0
+-#define WAIT_EVENT_HDR 1
+-#define WAIT_ACL_HDR 2
+-#define WAIT_SCO_HDR 3
+-#define WAIT_DATA 4
+-
+-#endif /* __KERNEL__ */
+diff -urN linux-2.4.18/include/net/bluetooth/hci_usb.h linux-2.4.18-mh15/include/net/bluetooth/hci_usb.h
+--- linux-2.4.18/include/net/bluetooth/hci_usb.h 2001-09-07 18:28:38.000000000 +0200
++++ linux-2.4.18-mh15/include/net/bluetooth/hci_usb.h 1970-01-01 01:00:00.000000000 +0100
+@@ -1,68 +0,0 @@
+-/*
+- BlueZ - Bluetooth protocol stack for Linux
+- Copyright (C) 2000-2001 Qualcomm Incorporated
+-
+- Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
+-
+- This program is free software; you can redistribute it and/or modify
+- it under the terms of the GNU General Public License version 2 as
+- published by the Free Software Foundation;
+-
+- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+- OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+- IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+- CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
+- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+-
+- ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
+- COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
+- SOFTWARE IS DISCLAIMED.
+-*/
+-
+-/*
+- * $Id$
+- */
+-
+-#ifdef __KERNEL__
+-
+-/* Class, SubClass, and Protocol codes that describe a Bluetooth device */
+-#define HCI_DEV_CLASS 0xe0 /* Wireless class */
+-#define HCI_DEV_SUBCLASS 0x01 /* RF subclass */
+-#define HCI_DEV_PROTOCOL 0x01 /* Bluetooth programming protocol */
+-
+-#define HCI_CTRL_REQ 0x20
+-
+-struct hci_usb {
+- struct usb_device *udev;
+-
+- devrequest dev_req;
+- struct urb *ctrl_urb;
+- struct urb *intr_urb;
+- struct urb *read_urb;
+- struct urb *write_urb;
+-
+- __u8 *read_buf;
+- __u8 *intr_buf;
+- struct sk_buff *intr_skb;
+- int intr_count;
+-
+- __u8 bulk_out_ep_addr;
+- __u8 bulk_in_ep_addr;
+- __u8 intr_in_ep_addr;
+- __u8 intr_in_interval;
+-
+- struct hci_dev hdev;
+-
+- unsigned long tx_state;
+- struct sk_buff_head tx_ctrl_q;
+- struct sk_buff_head tx_write_q;
+-};
+-
+-/* Transmit states */
+-#define HCI_TX_CTRL 1
+-#define HCI_TX_WRITE 2
+-
+-#endif /* __KERNEL__ */
+diff -urN linux-2.4.18/include/net/bluetooth/hci_vhci.h linux-2.4.18-mh15/include/net/bluetooth/hci_vhci.h
+--- linux-2.4.18/include/net/bluetooth/hci_vhci.h 2001-09-07 18:28:38.000000000 +0200
++++ linux-2.4.18-mh15/include/net/bluetooth/hci_vhci.h 1970-01-01 01:00:00.000000000 +0100
+@@ -1,50 +0,0 @@
+-/*
+- BlueZ - Bluetooth protocol stack for Linux
+- Copyright (C) 2000-2001 Qualcomm Incorporated
+-
+- Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
+-
+- This program is free software; you can redistribute it and/or modify
+- it under the terms of the GNU General Public License version 2 as
+- published by the Free Software Foundation;
+-
+- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+- OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+- IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+- CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
+- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+-
+- ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
+- COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
+- SOFTWARE IS DISCLAIMED.
+-*/
+-
+-/*
+- * $Id$
+- */
+-
+-#ifndef __HCI_VHCI_H
+-#define __HCI_VHCI_H
+-
+-#ifdef __KERNEL__
+-
+-struct hci_vhci_struct {
+- struct hci_dev hdev;
+- __u32 flags;
+- wait_queue_head_t read_wait;
+- struct sk_buff_head readq;
+- struct fasync_struct *fasync;
+-};
+-
+-/* VHCI device flags */
+-#define VHCI_FASYNC 0x0010
+-
+-#endif /* __KERNEL__ */
+-
+-#define VHCI_DEV "/dev/vhci"
+-#define VHCI_MINOR 250
+-
+-#endif /* __HCI_VHCI_H */
+diff -urN linux-2.4.18/include/net/bluetooth/l2cap_core.h linux-2.4.18-mh15/include/net/bluetooth/l2cap_core.h
+--- linux-2.4.18/include/net/bluetooth/l2cap_core.h 2001-09-07 18:28:38.000000000 +0200
++++ linux-2.4.18-mh15/include/net/bluetooth/l2cap_core.h 1970-01-01 01:00:00.000000000 +0100
+@@ -1,144 +0,0 @@
+-/*
+- BlueZ - Bluetooth protocol stack for Linux
+- Copyright (C) 2000-2001 Qualcomm Incorporated
+-
+- Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
+-
+- This program is free software; you can redistribute it and/or modify
+- it under the terms of the GNU General Public License version 2 as
+- published by the Free Software Foundation;
+-
+- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+- OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+- IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+- CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
+- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+-
+- ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
+- COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
+- SOFTWARE IS DISCLAIMED.
+-*/
+-
+-/*
+- * $Id$
+- */
+-
+-#ifndef __L2CAP_CORE_H
+-#define __L2CAP_CORE_H
+-
+-#ifdef __KERNEL__
+-
+-/* ----- L2CAP interface ----- */
+-struct l2cap_iff {
+- struct list_head list;
+- struct hci_dev *hdev;
+- bdaddr_t *bdaddr;
+- __u16 mtu;
+- spinlock_t lock;
+- struct list_head conn_list;
+-};
+-
+-static inline void l2cap_iff_lock(struct l2cap_iff *iff)
+-{
+- spin_lock(&iff->lock);
+-}
+-
+-static inline void l2cap_iff_unlock(struct l2cap_iff *iff)
+-{
+- spin_unlock(&iff->lock);
+-}
+-
+-/* ----- L2CAP connections ----- */
+-struct l2cap_chan_list {
+- struct sock *head;
+- rwlock_t lock;
+- long num;
+-};
+-
+-struct l2cap_conn {
+- struct l2cap_iff *iff;
+- struct list_head list;
+-
+- struct hci_conn *hconn;
+-
+- __u16 state;
+- __u8 out;
+- bdaddr_t src;
+- bdaddr_t dst;
+-
+- spinlock_t lock;
+- atomic_t refcnt;
+-
+- struct sk_buff *rx_skb;
+- __u32 rx_len;
+- __u8 rx_ident;
+- __u8 tx_ident;
+-
+- struct l2cap_chan_list chan_list;
+-
+- struct timer_list timer;
+-};
+-
+-static inline void __l2cap_conn_link(struct l2cap_iff *iff, struct l2cap_conn *c)
+-{
+- list_add(&c->list, &iff->conn_list);
+-}
+-
+-static inline void __l2cap_conn_unlink(struct l2cap_iff *iff, struct l2cap_conn *c)
+-{
+- list_del(&c->list);
+-}
+-
+-/* ----- L2CAP channel and socket info ----- */
+-#define l2cap_pi(sk) ((struct l2cap_pinfo *) &sk->protinfo)
+-
+-struct l2cap_accept_q {
+- struct sock *head;
+- struct sock *tail;
+-};
+-
+-struct l2cap_pinfo {
+- bdaddr_t src;
+- bdaddr_t dst;
+- __u16 psm;
+- __u16 dcid;
+- __u16 scid;
+- __u32 flags;
+-
+- __u16 imtu;
+- __u16 omtu;
+- __u16 flush_to;
+-
+- __u8 conf_state;
+- __u16 conf_mtu;
+-
+- __u8 ident;
+-
+- struct l2cap_conn *conn;
+- struct sock *next_c;
+- struct sock *prev_c;
+-
+- struct sock *parent;
+- struct sock *next_q;
+- struct sock *prev_q;
+-
+- struct l2cap_accept_q accept_q;
+-};
+-
+-#define CONF_REQ_SENT 0x01
+-#define CONF_INPUT_DONE 0x02
+-#define CONF_OUTPUT_DONE 0x04
+-
+-extern struct bluez_sock_list l2cap_sk_list;
+-extern struct list_head l2cap_iff_list;
+-extern rwlock_t l2cap_rt_lock;
+-
+-extern void l2cap_register_proc(void);
+-extern void l2cap_unregister_proc(void);
+-
+-#endif /* __KERNEL__ */
+-
+-#endif /* __L2CAP_CORE_H */
+diff -urN linux-2.4.18/include/net/bluetooth/l2cap.h linux-2.4.18-mh15/include/net/bluetooth/l2cap.h
+--- linux-2.4.18/include/net/bluetooth/l2cap.h 2001-09-07 18:28:38.000000000 +0200
++++ linux-2.4.18-mh15/include/net/bluetooth/l2cap.h 2004-08-01 16:26:23.000000000 +0200
+@@ -23,22 +23,17 @@
+ */
+
+ /*
+- * $Id$
++ * $Id$
+ */
+
+ #ifndef __L2CAP_H
+ #define __L2CAP_H
+
+-#include <asm/types.h>
+-#include <asm/byteorder.h>
+-
+ /* L2CAP defaults */
+ #define L2CAP_DEFAULT_MTU 672
+ #define L2CAP_DEFAULT_FLUSH_TO 0xFFFF
+
+ #define L2CAP_CONN_TIMEOUT (HZ * 40)
+-#define L2CAP_DISCONN_TIMEOUT (HZ * 2)
+-#define L2CAP_CONN_IDLE_TIMEOUT (HZ * 60)
+
+ /* L2CAP socket address */
+ struct sockaddr_l2 {
+@@ -47,17 +42,12 @@
+ bdaddr_t l2_bdaddr;
+ };
+
+-/* set/get sockopt defines */
+-#define L2CAP_OPTIONS 0x01
++/* Socket options */
++#define L2CAP_OPTIONS 0x01
+ struct l2cap_options {
+ __u16 omtu;
+ __u16 imtu;
+ __u16 flush_to;
+- __u32 token_rate;
+- __u32 bucket_size;
+- __u32 pick_band;
+- __u32 latency;
+- __u32 delay_var;
+ };
+
+ #define L2CAP_CONNINFO 0x02
+@@ -65,6 +55,27 @@
+ __u16 hci_handle;
+ };
+
++#define L2CAP_LM 0x03
++#define L2CAP_LM_MASTER 0x0001
++#define L2CAP_LM_AUTH 0x0002
++#define L2CAP_LM_ENCRYPT 0x0004
++#define L2CAP_LM_TRUSTED 0x0008
++#define L2CAP_LM_RELIABLE 0x0010
++
++#define L2CAP_QOS 0x04
++struct l2cap_qos {
++ __u16 service_type;
++ __u32 token_rate;
++ __u32 token_bucket_size;
++ __u32 peak_bandwidth;
++ __u32 latency;
++ __u32 delay_variation;
++};
++
++#define L2CAP_SERV_NO_TRAFFIC 0x00
++#define L2CAP_SERV_BEST_EFFORT 0x01
++#define L2CAP_SERV_GUARANTEED 0x02
++
+ /* L2CAP command codes */
+ #define L2CAP_COMMAND_REJ 0x01
+ #define L2CAP_CONN_REQ 0x02
+@@ -79,7 +90,6 @@
+ #define L2CAP_INFO_RSP 0x0b
+
+ /* L2CAP structures */
+-
+ typedef struct {
+ __u16 len;
+ __u16 cid;
+@@ -112,11 +122,17 @@
+ } __attribute__ ((packed)) l2cap_conn_rsp;
+ #define L2CAP_CONN_RSP_SIZE 8
+
+-#define L2CAP_CONN_SUCCESS 0x0000
+-#define L2CAP_CONN_PEND 0x0001
+-#define L2CAP_CONN_BAD_PSM 0x0002
+-#define L2CAP_CONN_SEC_BLOCK 0x0003
+-#define L2CAP_CONN_NO_MEM 0x0004
++/* connect result */
++#define L2CAP_CR_SUCCESS 0x0000
++#define L2CAP_CR_PEND 0x0001
++#define L2CAP_CR_BAD_PSM 0x0002
++#define L2CAP_CR_SEC_BLOCK 0x0003
++#define L2CAP_CR_NO_MEM 0x0004
++
++/* connect status */
++#define L2CAP_CS_NO_INFO 0x0000
++#define L2CAP_CS_AUTHEN_PEND 0x0001
++#define L2CAP_CS_AUTHOR_PEND 0x0002
+
+ typedef struct {
+ __u16 dcid;
+@@ -147,6 +163,8 @@
+ #define L2CAP_CONF_FLUSH_TO 0x02
+ #define L2CAP_CONF_QOS 0x03
+
++#define L2CAP_CONF_MAX_SIZE 22
++
+ typedef struct {
+ __u16 dcid;
+ __u16 scid;
+@@ -159,4 +177,82 @@
+ } __attribute__ ((packed)) l2cap_disconn_rsp;
+ #define L2CAP_DISCONN_RSP_SIZE 4
+
++typedef struct {
++ __u16 type;
++ __u8 data[0];
++} __attribute__ ((packed)) l2cap_info_req;
++#define L2CAP_INFO_REQ_SIZE 2
++
++typedef struct {
++ __u16 type;
++ __u16 result;
++ __u8 data[0];
++} __attribute__ ((packed)) l2cap_info_rsp;
++#define L2CAP_INFO_RSP_SIZE 4
++
++/* info type */
++#define L2CAP_IT_CL_MTU 0x0001
++#define L2CAP_IT_FEAT_MASK 0x0002
++
++/* info result */
++#define L2CAP_IR_SUCCESS 0x0000
++#define L2CAP_IR_NOTSUPP 0x0001
++
++/* ----- L2CAP connections ----- */
++struct l2cap_chan_list {
++ struct sock *head;
++ rwlock_t lock;
++ long num;
++};
++
++struct l2cap_conn {
++ struct hci_conn *hcon;
++
++ bdaddr_t *dst;
++ bdaddr_t *src;
++
++ unsigned int mtu;
++
++ spinlock_t lock;
++
++ struct sk_buff *rx_skb;
++ __u32 rx_len;
++ __u8 rx_ident;
++ __u8 tx_ident;
++
++ struct l2cap_chan_list chan_list;
++};
++
++/* ----- L2CAP channel and socket info ----- */
++#define l2cap_pi(sk) ((struct l2cap_pinfo *) &sk->tp_pinfo)
++
++struct l2cap_pinfo {
++ __u16 psm;
++ __u16 dcid;
++ __u16 scid;
++
++ __u16 imtu;
++ __u16 omtu;
++ __u16 flush_to;
++
++ __u32 link_mode;
++
++ __u8 conf_state;
++ __u8 conf_retry;
++ __u16 conf_mtu;
++
++ __u8 ident;
++
++ struct l2cap_conn *conn;
++ struct sock *next_c;
++ struct sock *prev_c;
++};
++
++#define L2CAP_CONF_REQ_SENT 0x01
++#define L2CAP_CONF_INPUT_DONE 0x02
++#define L2CAP_CONF_OUTPUT_DONE 0x04
++#define L2CAP_CONF_MAX_RETRIES 2
++
++void l2cap_load(void);
++
+ #endif /* __L2CAP_H */
+diff -urN linux-2.4.18/include/net/bluetooth/rfcomm.h linux-2.4.18-mh15/include/net/bluetooth/rfcomm.h
+--- linux-2.4.18/include/net/bluetooth/rfcomm.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/include/net/bluetooth/rfcomm.h 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,361 @@
++/*
++ RFCOMM implementation for Linux Bluetooth stack (BlueZ).
++ Copyright (C) 2002 Maxim Krasnyansky <maxk@qualcomm.com>
++ Copyright (C) 2002 Marcel Holtmann <marcel@holtmann.org>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License version 2 as
++ published by the Free Software Foundation;
++
++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++
++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
++ SOFTWARE IS DISCLAIMED.
++*/
++
++/*
++ RPN support - Dirk Husemann <hud@zurich.ibm.com>
++*/
++
++/*
++ * $Id$
++ */
++
++#ifndef __RFCOMM_H
++#define __RFCOMM_H
++
++#define RFCOMM_PSM 3
++
++#define RFCOMM_CONN_TIMEOUT (HZ * 30)
++#define RFCOMM_DISC_TIMEOUT (HZ * 20)
++
++#define RFCOMM_DEFAULT_MTU 127
++#define RFCOMM_DEFAULT_CREDITS 7
++
++#define RFCOMM_MAX_L2CAP_MTU 1024
++#define RFCOMM_MAX_CREDITS 40
++
++#define RFCOMM_SKB_HEAD_RESERVE 8
++#define RFCOMM_SKB_TAIL_RESERVE 2
++#define RFCOMM_SKB_RESERVE (RFCOMM_SKB_HEAD_RESERVE + RFCOMM_SKB_TAIL_RESERVE)
++
++#define RFCOMM_SABM 0x2f
++#define RFCOMM_DISC 0x43
++#define RFCOMM_UA 0x63
++#define RFCOMM_DM 0x0f
++#define RFCOMM_UIH 0xef
++
++#define RFCOMM_TEST 0x08
++#define RFCOMM_FCON 0x28
++#define RFCOMM_FCOFF 0x18
++#define RFCOMM_MSC 0x38
++#define RFCOMM_RPN 0x24
++#define RFCOMM_RLS 0x14
++#define RFCOMM_PN 0x20
++#define RFCOMM_NSC 0x04
++
++#define RFCOMM_V24_FC 0x02
++#define RFCOMM_V24_RTC 0x04
++#define RFCOMM_V24_RTR 0x08
++#define RFCOMM_V24_IC 0x40
++#define RFCOMM_V24_DV 0x80
++
++#define RFCOMM_RPN_BR_2400 0x0
++#define RFCOMM_RPN_BR_4800 0x1
++#define RFCOMM_RPN_BR_7200 0x2
++#define RFCOMM_RPN_BR_9600 0x3
++#define RFCOMM_RPN_BR_19200 0x4
++#define RFCOMM_RPN_BR_38400 0x5
++#define RFCOMM_RPN_BR_57600 0x6
++#define RFCOMM_RPN_BR_115200 0x7
++#define RFCOMM_RPN_BR_230400 0x8
++
++#define RFCOMM_RPN_DATA_5 0x0
++#define RFCOMM_RPN_DATA_6 0x1
++#define RFCOMM_RPN_DATA_7 0x2
++#define RFCOMM_RPN_DATA_8 0x3
++
++#define RFCOMM_RPN_STOP_1 0
++#define RFCOMM_RPN_STOP_15 1
++
++#define RFCOMM_RPN_PARITY_NONE 0x0
++#define RFCOMM_RPN_PARITY_ODD 0x4
++#define RFCOMM_RPN_PARITY_EVEN 0x5
++#define RFCOMM_RPN_PARITY_MARK 0x6
++#define RFCOMM_RPN_PARITY_SPACE 0x7
++
++#define RFCOMM_RPN_FLOW_NONE 0x00
++
++#define RFCOMM_RPN_XON_CHAR 0x11
++#define RFCOMM_RPN_XOFF_CHAR 0x13
++
++#define RFCOMM_RPN_PM_BITRATE 0x0001
++#define RFCOMM_RPN_PM_DATA 0x0002
++#define RFCOMM_RPN_PM_STOP 0x0004
++#define RFCOMM_RPN_PM_PARITY 0x0008
++#define RFCOMM_RPN_PM_PARITY_TYPE 0x0010
++#define RFCOMM_RPN_PM_XON 0x0020
++#define RFCOMM_RPN_PM_XOFF 0x0040
++#define RFCOMM_RPN_PM_FLOW 0x3F00
++
++#define RFCOMM_RPN_PM_ALL 0x3F7F
++
++struct rfcomm_hdr {
++ u8 addr;
++ u8 ctrl;
++ u8 len; // Actual size can be 2 bytes
++} __attribute__ ((packed));
++
++struct rfcomm_cmd {
++ u8 addr;
++ u8 ctrl;
++ u8 len;
++ u8 fcs;
++} __attribute__ ((packed));
++
++struct rfcomm_mcc {
++ u8 type;
++ u8 len;
++} __attribute__ ((packed));
++
++struct rfcomm_pn {
++ u8 dlci;
++ u8 flow_ctrl;
++ u8 priority;
++ u8 ack_timer;
++ u16 mtu;
++ u8 max_retrans;
++ u8 credits;
++} __attribute__ ((packed));
++
++struct rfcomm_rpn {
++ u8 dlci;
++ u8 bit_rate;
++ u8 line_settings;
++ u8 flow_ctrl;
++ u8 xon_char;
++ u8 xoff_char;
++ u16 param_mask;
++} __attribute__ ((packed));
++
++struct rfcomm_rls {
++ u8 dlci;
++ u8 status;
++} __attribute__ ((packed));
++
++struct rfcomm_msc {
++ u8 dlci;
++ u8 v24_sig;
++} __attribute__ ((packed));
++
++/* ---- Core structures, flags etc ---- */
++
++struct rfcomm_session {
++ struct list_head list;
++ struct socket *sock;
++ unsigned long state;
++ unsigned long flags;
++ atomic_t refcnt;
++ int initiator;
++
++ /* Default DLC parameters */
++ int cfc;
++ uint mtu;
++
++ struct list_head dlcs;
++};
++
++struct rfcomm_dlc {
++ struct list_head list;
++ struct rfcomm_session *session;
++ struct sk_buff_head tx_queue;
++ struct timer_list timer;
++
++ spinlock_t lock;
++ unsigned long state;
++ unsigned long flags;
++ atomic_t refcnt;
++ u8 dlci;
++ u8 addr;
++ u8 priority;
++ u8 v24_sig;
++ u8 mscex;
++
++ uint mtu;
++ uint cfc;
++ uint rx_credits;
++ uint tx_credits;
++
++ void *owner;
++
++ void (*data_ready)(struct rfcomm_dlc *d, struct sk_buff *skb);
++ void (*state_change)(struct rfcomm_dlc *d, int err);
++ void (*modem_status)(struct rfcomm_dlc *d, u8 v24_sig);
++};
++
++/* DLC and session flags */
++#define RFCOMM_RX_THROTTLED 0
++#define RFCOMM_TX_THROTTLED 1
++#define RFCOMM_MSC_PENDING 2
++#define RFCOMM_TIMED_OUT 3
++
++/* Scheduling flags and events */
++#define RFCOMM_SCHED_STATE 0
++#define RFCOMM_SCHED_RX 1
++#define RFCOMM_SCHED_TX 2
++#define RFCOMM_SCHED_TIMEO 3
++#define RFCOMM_SCHED_WAKEUP 31
++
++/* MSC exchange flags */
++#define RFCOMM_MSCEX_TX 1
++#define RFCOMM_MSCEX_RX 2
++#define RFCOMM_MSCEX_OK (RFCOMM_MSCEX_TX + RFCOMM_MSCEX_RX)
++
++/* CFC states */
++#define RFCOMM_CFC_UNKNOWN -1
++#define RFCOMM_CFC_DISABLED 0
++#define RFCOMM_CFC_ENABLED RFCOMM_MAX_CREDITS
++
++extern struct task_struct *rfcomm_thread;
++extern unsigned long rfcomm_event;
++
++static inline void rfcomm_schedule(uint event)
++{
++ if (!rfcomm_thread)
++ return;
++ set_bit(RFCOMM_SCHED_WAKEUP, &rfcomm_event);
++ wake_up_process(rfcomm_thread);
++}
++
++extern struct semaphore rfcomm_sem;
++#define rfcomm_lock() down(&rfcomm_sem);
++#define rfcomm_unlock() up(&rfcomm_sem);
++
++/* ---- RFCOMM DLCs (channels) ---- */
++struct rfcomm_dlc *rfcomm_dlc_alloc(int prio);
++void rfcomm_dlc_free(struct rfcomm_dlc *d);
++int rfcomm_dlc_open(struct rfcomm_dlc *d, bdaddr_t *src, bdaddr_t *dst, u8 channel);
++int rfcomm_dlc_close(struct rfcomm_dlc *d, int reason);
++int rfcomm_dlc_send(struct rfcomm_dlc *d, struct sk_buff *skb);
++int rfcomm_dlc_set_modem_status(struct rfcomm_dlc *d, u8 v24_sig);
++int rfcomm_dlc_get_modem_status(struct rfcomm_dlc *d, u8 *v24_sig);
++
++#define rfcomm_dlc_lock(d) spin_lock(&d->lock)
++#define rfcomm_dlc_unlock(d) spin_unlock(&d->lock)
++
++static inline void rfcomm_dlc_hold(struct rfcomm_dlc *d)
++{
++ atomic_inc(&d->refcnt);
++}
++
++static inline void rfcomm_dlc_put(struct rfcomm_dlc *d)
++{
++ if (atomic_dec_and_test(&d->refcnt))
++ rfcomm_dlc_free(d);
++}
++
++extern void FASTCALL(__rfcomm_dlc_throttle(struct rfcomm_dlc *d));
++extern void FASTCALL(__rfcomm_dlc_unthrottle(struct rfcomm_dlc *d));
++
++static inline void rfcomm_dlc_throttle(struct rfcomm_dlc *d)
++{
++ if (!test_and_set_bit(RFCOMM_RX_THROTTLED, &d->flags))
++ __rfcomm_dlc_throttle(d);
++}
++
++static inline void rfcomm_dlc_unthrottle(struct rfcomm_dlc *d)
++{
++ if (test_and_clear_bit(RFCOMM_RX_THROTTLED, &d->flags))
++ __rfcomm_dlc_unthrottle(d);
++}
++
++/* ---- RFCOMM sessions ---- */
++struct rfcomm_session *rfcomm_session_add(struct socket *sock, int state);
++struct rfcomm_session *rfcomm_session_get(bdaddr_t *src, bdaddr_t *dst);
++struct rfcomm_session *rfcomm_session_create(bdaddr_t *src, bdaddr_t *dst, int *err);
++void rfcomm_session_del(struct rfcomm_session *s);
++void rfcomm_session_close(struct rfcomm_session *s, int err);
++void rfcomm_session_getaddr(struct rfcomm_session *s, bdaddr_t *src, bdaddr_t *dst);
++
++static inline void rfcomm_session_hold(struct rfcomm_session *s)
++{
++ atomic_inc(&s->refcnt);
++}
++
++static inline void rfcomm_session_put(struct rfcomm_session *s)
++{
++ if (atomic_dec_and_test(&s->refcnt))
++ rfcomm_session_del(s);
++}
++
++/* ---- RFCOMM chechsum ---- */
++extern u8 rfcomm_crc_table[];
++
++/* ---- RFCOMM sockets ---- */
++struct sockaddr_rc {
++ sa_family_t rc_family;
++ bdaddr_t rc_bdaddr;
++ u8 rc_channel;
++};
++
++#define rfcomm_pi(sk) ((struct rfcomm_pinfo *) &sk->tp_pinfo)
++
++struct rfcomm_pinfo {
++ struct rfcomm_dlc *dlc;
++ u8 channel;
++};
++
++int rfcomm_init_sockets(void);
++void rfcomm_cleanup_sockets(void);
++
++int rfcomm_connect_ind(struct rfcomm_session *s, u8 channel, struct rfcomm_dlc **d);
++
++/* ---- RFCOMM TTY ---- */
++#define RFCOMM_MAX_DEV 256
++
++#define RFCOMMCREATEDEV _IOW('R', 200, int)
++#define RFCOMMRELEASEDEV _IOW('R', 201, int)
++#define RFCOMMGETDEVLIST _IOR('R', 210, int)
++#define RFCOMMGETDEVINFO _IOR('R', 211, int)
++#define RFCOMMSTEALDLC _IOW('R', 220, int)
++
++#define RFCOMM_REUSE_DLC 0
++#define RFCOMM_RELEASE_ONHUP 1
++#define RFCOMM_HANGUP_NOW 2
++#define RFCOMM_TTY_ATTACHED 3
++
++struct rfcomm_dev_req {
++ s16 dev_id;
++ u32 flags;
++ bdaddr_t src;
++ bdaddr_t dst;
++ u8 channel;
++};
++
++struct rfcomm_dev_info {
++ s16 id;
++ u32 flags;
++ u16 state;
++ bdaddr_t src;
++ bdaddr_t dst;
++ u8 channel;
++};
++
++struct rfcomm_dev_list_req {
++ u16 dev_num;
++ struct rfcomm_dev_info dev_info[0];
++};
++
++int rfcomm_dev_ioctl(struct sock *sk, unsigned int cmd, unsigned long arg);
++int rfcomm_init_ttys(void);
++void rfcomm_cleanup_ttys(void);
++
++#endif /* __RFCOMM_H */
+diff -urN linux-2.4.18/include/net/bluetooth/sco.h linux-2.4.18-mh15/include/net/bluetooth/sco.h
+--- linux-2.4.18/include/net/bluetooth/sco.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/include/net/bluetooth/sco.h 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,81 @@
++/*
++ BlueZ - Bluetooth protocol stack for Linux
++ Copyright (C) 2000-2001 Qualcomm Incorporated
++
++ Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License version 2 as
++ published by the Free Software Foundation;
++
++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++
++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
++ SOFTWARE IS DISCLAIMED.
++*/
++
++/*
++ * $Id$
++ */
++
++#ifndef __SCO_H
++#define __SCO_H
++
++/* SCO defaults */
++#define SCO_DEFAULT_MTU 500
++#define SCO_DEFAULT_FLUSH_TO 0xFFFF
++
++#define SCO_CONN_TIMEOUT (HZ * 40)
++#define SCO_DISCONN_TIMEOUT (HZ * 2)
++#define SCO_CONN_IDLE_TIMEOUT (HZ * 60)
++
++/* SCO socket address */
++struct sockaddr_sco {
++ sa_family_t sco_family;
++ bdaddr_t sco_bdaddr;
++};
++
++/* set/get sockopt defines */
++#define SCO_OPTIONS 0x01
++struct sco_options {
++ __u16 mtu;
++};
++
++#define SCO_CONNINFO 0x02
++struct sco_conninfo {
++ __u16 hci_handle;
++};
++
++/* ---- SCO connections ---- */
++struct sco_conn {
++ struct hci_conn *hcon;
++
++ bdaddr_t *dst;
++ bdaddr_t *src;
++
++ spinlock_t lock;
++ struct sock *sk;
++
++ unsigned int mtu;
++};
++
++#define sco_conn_lock(c) spin_lock(&c->lock);
++#define sco_conn_unlock(c) spin_unlock(&c->lock);
++
++/* ----- SCO socket info ----- */
++#define sco_pi(sk) ((struct sco_pinfo *) &sk->tp_pinfo)
++
++struct sco_pinfo {
++ __u32 flags;
++ struct sco_conn *conn;
++};
++
++#endif /* __SCO_H */
+diff -urN linux-2.4.18/include/pcmcia/ciscode.h linux-2.4.18-mh15/include/pcmcia/ciscode.h
+--- linux-2.4.18/include/pcmcia/ciscode.h 2001-12-21 18:42:04.000000000 +0100
++++ linux-2.4.18-mh15/include/pcmcia/ciscode.h 2004-08-01 16:26:23.000000000 +0200
+@@ -1,5 +1,5 @@
+ /*
+- * ciscode.h 1.48 2001/08/24 12:16:12
++ * ciscode.h 1.57 2002/11/03 20:38:14
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+@@ -60,6 +60,10 @@
+ #define PRODID_INTEL_DUAL_RS232 0x0301
+ #define PRODID_INTEL_2PLUS 0x8422
+
++#define MANFID_KME 0x0032
++#define PRODID_KME_KXLC005_A 0x0704
++#define PRODID_KME_KXLC005_B 0x2904
++
+ #define MANFID_LINKSYS 0x0143
+ #define PRODID_LINKSYS_PCMLM28 0xc0ab
+ #define PRODID_LINKSYS_3400 0x3341
+@@ -94,6 +98,8 @@
+ #define PRODID_OSITECH_JACK_336 0x0007
+ #define PRODID_OSITECH_SEVEN 0x0008
+
++#define MANFID_OXSEMI 0x0279
++
+ #define MANFID_PIONEER 0x000b
+
+ #define MANFID_PSION 0x016c
+@@ -103,6 +109,7 @@
+ #define PRODID_QUATECH_SPP100 0x0003
+ #define PRODID_QUATECH_DUAL_RS232 0x0012
+ #define PRODID_QUATECH_DUAL_RS232_D1 0x0007
++#define PRODID_QUATECH_DUAL_RS232_D2 0x0052
+ #define PRODID_QUATECH_QUAD_RS232 0x001b
+ #define PRODID_QUATECH_DUAL_RS422 0x000e
+ #define PRODID_QUATECH_QUAD_RS422 0x0045
+@@ -120,9 +127,12 @@
+
+ #define MANFID_TDK 0x0105
+ #define PRODID_TDK_CF010 0x0900
++#define PRODID_TDK_GN3410 0x4815
+
+ #define MANFID_TOSHIBA 0x0098
+
++#define MANFID_UNGERMANN 0x02c0
++
+ #define MANFID_XIRCOM 0x0105
+
+ #endif /* _LINUX_CISCODE_H */
+diff -urN linux-2.4.18/kernel/ksyms.c linux-2.4.18-mh15/kernel/ksyms.c
+--- linux-2.4.18/kernel/ksyms.c 2002-02-25 20:38:13.000000000 +0100
++++ linux-2.4.18-mh15/kernel/ksyms.c 2004-08-01 16:26:23.000000000 +0200
+@@ -47,6 +47,7 @@
+ #include <linux/in6.h>
+ #include <linux/completion.h>
+ #include <linux/seq_file.h>
++#include <linux/firmware.h>
+ #include <asm/checksum.h>
+
+ #if defined(CONFIG_PROC_FS)
+@@ -538,6 +539,13 @@
+ EXPORT_SYMBOL(strspn);
+ EXPORT_SYMBOL(strsep);
+
++#ifdef CONFIG_FW_LOADER
++EXPORT_SYMBOL(release_firmware);
++EXPORT_SYMBOL(request_firmware);
++EXPORT_SYMBOL(request_firmware_nowait);
++EXPORT_SYMBOL(register_firmware);
++#endif
++
+ /* software interrupts */
+ EXPORT_SYMBOL(tasklet_hi_vec);
+ EXPORT_SYMBOL(tasklet_vec);
+diff -urN linux-2.4.18/lib/Config.in linux-2.4.18-mh15/lib/Config.in
+--- linux-2.4.18/lib/Config.in 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/lib/Config.in 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,12 @@
++#
++# Library configuration
++#
++mainmenu_option next_comment
++comment 'Library routines'
++
++if [ "$CONFIG_EXPERIMENTAL" = "y" -a \
++ "$CONFIG_HOTPLUG" = "y" ]; then
++ tristate 'Hotplug firmware loading support (EXPERIMENTAL)' CONFIG_FW_LOADER
++fi
++
++endmenu
+diff -urN linux-2.4.18/lib/firmware_class.c linux-2.4.18-mh15/lib/firmware_class.c
+--- linux-2.4.18/lib/firmware_class.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/lib/firmware_class.c 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,573 @@
++/*
++ * firmware_class.c - Multi purpose firmware loading support
++ *
++ * Copyright (c) 2003 Manuel Estrada Sainz <ranty@debian.org>
++ *
++ * Please see Documentation/firmware_class/ for more information.
++ *
++ */
++/*
++ * Based on kernel/kmod.c and drivers/usb/usb.c
++ */
++/*
++ kernel/kmod.c
++ Kirk Petersen
++
++ Reorganized not to be a daemon by Adam Richter, with guidance
++ from Greg Zornetzer.
++
++ Modified to avoid chroot and file sharing problems.
++ Mikael Pettersson
++
++ Limit the concurrent number of kmod modprobes to catch loops from
++ "modprobe needs a service that is in a module".
++ Keith Owens <kaos@ocs.com.au> December 1999
++
++ Unblock all signals when we exec a usermode process.
++ Shuu Yamaguchi <shuu@wondernetworkresources.com> December 2000
++*/
++/*
++ * drivers/usb/usb.c
++ *
++ * (C) Copyright Linus Torvalds 1999
++ * (C) Copyright Johannes Erdfelt 1999-2001
++ * (C) Copyright Andreas Gal 1999
++ * (C) Copyright Gregory P. Smith 1999
++ * (C) Copyright Deti Fliegl 1999 (new USB architecture)
++ * (C) Copyright Randy Dunlap 2000
++ * (C) Copyright David Brownell 2000 (kernel hotplug, usb_device_id)
++ * (C) Copyright Yggdrasil Computing, Inc. 2000
++ * (usb_device_id matching changes by Adam J. Richter)
++ */
++
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/string.h>
++#include <linux/types.h>
++#include <linux/init.h>
++#include <linux/slab.h>
++#include <linux/kmod.h>
++#include <linux/proc_fs.h>
++#include <linux/vmalloc.h>
++#include <asm/hardirq.h>
++
++#include "linux/firmware.h"
++
++MODULE_AUTHOR("Manuel Estrada Sainz <ranty@debian.org>");
++MODULE_DESCRIPTION("Multi purpose firmware loading support");
++MODULE_LICENSE("GPL");
++
++#define err(format, arg...) \
++ printk(KERN_ERR "%s:%s: " format "\n",__FILE__, __FUNCTION__ , ## arg)
++#define warn(format, arg...) \
++ printk(KERN_WARNING "%s:%s: " format "\n",__FILE__, __FUNCTION__ , ## arg)
++#define dbg(format, arg...) \
++ printk(KERN_DEBUG "%s:%s: " format "\n",__FILE__, __FUNCTION__ , ## arg)
++
++static int loading_timeout = 10; /* In seconds */
++static struct proc_dir_entry *proc_dir_timeout;
++static struct proc_dir_entry *proc_dir;
++
++#ifdef CONFIG_HOTPLUG
++
++static int
++call_helper(char *verb, const char *name, const char *device)
++{
++ char *argv[3], **envp, *buf, *scratch;
++ int i = 0;
++
++ int retval = 0;
++
++ if (!hotplug_path[0])
++ return -ENOENT;
++ if (in_interrupt()) {
++ err("in_interrupt");
++ return -EFAULT;
++ }
++ if (!current->fs->root) {
++ warn("call_policy %s -- no FS yet", verb);
++ return -EPERM;
++ }
++
++ if (!(envp = (char **) kmalloc(20 * sizeof (char *), GFP_KERNEL))) {
++ err("unable to allocate envp");
++ return -ENOMEM;
++ }
++ if (!(buf = kmalloc(256, GFP_KERNEL))) {
++ kfree(envp);
++ err("unable to allocate buf");
++ return -ENOMEM;
++ }
++
++ /* only one standardized param to hotplug command: type */
++ argv[0] = hotplug_path;
++ argv[1] = "firmware";
++ argv[2] = 0;
++
++ /* minimal command environment */
++ envp[i++] = "HOME=/";
++ envp[i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
++
++#ifdef DEBUG
++ /* hint that policy agent should enter no-stdout debug mode */
++ envp[i++] = "DEBUG=kernel";
++#endif
++ scratch = buf;
++
++ if (device) {
++ envp[i++] = scratch;
++ scratch += snprintf(scratch, FIRMWARE_NAME_MAX+25,
++ "DEVPATH=/driver/firmware/%s", device) + 1;
++ }
++
++ envp[i++] = scratch;
++ scratch += sprintf(scratch, "ACTION=%s", verb) + 1;
++
++ envp[i++] = scratch;
++ scratch += snprintf(scratch, FIRMWARE_NAME_MAX,
++ "FIRMWARE=%s", name) + 1;
++
++ envp[i++] = 0;
++
++#ifdef DEBUG
++ dbg("firmware: %s %s %s", argv[0], argv[1], verb);
++#endif
++
++ retval = call_usermodehelper(argv[0], argv, envp);
++ if (retval) {
++ printk("call_usermodehelper return %d\n", retval);
++ }
++
++ kfree(buf);
++ kfree(envp);
++ return retval;
++}
++#else
++
++static inline int
++call_helper(char *verb, const char *name, const char *device)
++{
++ return -ENOENT;
++}
++
++#endif /* CONFIG_HOTPLUG */
++
++struct firmware_priv {
++ struct completion completion;
++ struct proc_dir_entry *proc_dir;
++ struct proc_dir_entry *attr_data;
++ struct proc_dir_entry *attr_loading;
++ struct firmware *fw;
++ int loading;
++ int abort;
++ int alloc_size;
++ struct timer_list timeout;
++};
++
++static int
++firmware_timeout_show(char *buf, char **start, off_t off,
++ int count, int *eof, void *data)
++{
++ return sprintf(buf, "%d\n", loading_timeout);
++}
++
++/**
++ * firmware_timeout_store:
++ * Description:
++ * Sets the number of seconds to wait for the firmware. Once
++ * this expires an error will be return to the driver and no
++ * firmware will be provided.
++ *
++ * Note: zero means 'wait for ever'
++ *
++ **/
++static int
++firmware_timeout_store(struct file *file, const char *buf,
++ unsigned long count, void *data)
++{
++ loading_timeout = simple_strtol(buf, NULL, 10);
++ return count;
++}
++
++static int
++firmware_loading_show(char *buf, char **start, off_t off,
++ int count, int *eof, void *data)
++{
++ struct firmware_priv *fw_priv = data;
++ return sprintf(buf, "%d\n", fw_priv->loading);
++}
++
++/**
++ * firmware_loading_store: - loading control file
++ * Description:
++ * The relevant values are:
++ *
++ * 1: Start a load, discarding any previous partial load.
++ * 0: Conclude the load and handle the data to the driver code.
++ * -1: Conclude the load with an error and discard any written data.
++ **/
++static int
++firmware_loading_store(struct file *file, const char *buf,
++ unsigned long count, void *data)
++{
++ struct firmware_priv *fw_priv = data;
++ int prev_loading = fw_priv->loading;
++
++ fw_priv->loading = simple_strtol(buf, NULL, 10);
++
++ switch (fw_priv->loading) {
++ case -1:
++ fw_priv->abort = 1;
++ wmb();
++ complete(&fw_priv->completion);
++ break;
++ case 1:
++ kfree(fw_priv->fw->data);
++ fw_priv->fw->data = NULL;
++ fw_priv->fw->size = 0;
++ fw_priv->alloc_size = 0;
++ break;
++ case 0:
++ if (prev_loading == 1)
++ complete(&fw_priv->completion);
++ break;
++ }
++
++ return count;
++}
++
++static int
++firmware_data_read(char *buffer, char **start, off_t offset,
++ int count, int *eof, void *data)
++{
++ struct firmware_priv *fw_priv = data;
++ struct firmware *fw = fw_priv->fw;
++
++ if (offset > fw->size)
++ return 0;
++ if (offset + count > fw->size)
++ count = fw->size - offset;
++
++ memcpy(buffer, fw->data + offset, count);
++ *start = (void *) ((long) count);
++ return count;
++}
++static int
++fw_realloc_buffer(struct firmware_priv *fw_priv, int min_size)
++{
++ u8 *new_data;
++ int new_size;
++
++ if (min_size <= fw_priv->alloc_size)
++ return 0;
++ if((min_size % PAGE_SIZE) == 0)
++ new_size = min_size;
++ else
++ new_size = (min_size + PAGE_SIZE) & PAGE_MASK;
++ new_data = vmalloc(new_size);
++ if (!new_data) {
++ printk(KERN_ERR "%s: unable to alloc buffer\n", __FUNCTION__);
++ /* Make sure that we don't keep incomplete data */
++ fw_priv->abort = 1;
++ return -ENOMEM;
++ }
++ fw_priv->alloc_size = new_size;
++ if (fw_priv->fw->data) {
++ memcpy(new_data, fw_priv->fw->data, fw_priv->fw->size);
++ vfree(fw_priv->fw->data);
++ }
++ fw_priv->fw->data = new_data;
++ BUG_ON(min_size > fw_priv->alloc_size);
++ return 0;
++}
++
++/**
++ * firmware_data_write:
++ *
++ * Description:
++ *
++ * Data written to the 'data' attribute will be later handled to
++ * the driver as a firmware image.
++ **/
++static int
++firmware_data_write(struct file *file, const char *buffer,
++ unsigned long count, void *data)
++{
++ struct firmware_priv *fw_priv = data;
++ struct firmware *fw = fw_priv->fw;
++ int offset = file->f_pos;
++ int retval;
++
++ retval = fw_realloc_buffer(fw_priv, offset + count);
++ if (retval) {
++ printk("%s: retval:%d\n", __FUNCTION__, retval);
++ return retval;
++ }
++
++ memcpy(fw->data + offset, buffer, count);
++
++ fw->size = max_t(size_t, offset + count, fw->size);
++ file->f_pos += count;
++ return count;
++}
++
++static void
++firmware_class_timeout(u_long data)
++{
++ struct firmware_priv *fw_priv = (struct firmware_priv *) data;
++ fw_priv->abort = 1;
++ wmb();
++ complete(&fw_priv->completion);
++}
++static int
++fw_setup_class_device(struct firmware_priv **fw_priv_p,
++ const char *fw_name, const char *device)
++{
++ int retval;
++ struct firmware_priv *fw_priv = kmalloc(sizeof (struct firmware_priv),
++ GFP_KERNEL);
++ *fw_priv_p = fw_priv;
++ if (!fw_priv) {
++ retval = -ENOMEM;
++ goto out;
++ }
++ memset(fw_priv, 0, sizeof (*fw_priv));
++
++ init_completion(&fw_priv->completion);
++
++ fw_priv->timeout.function = firmware_class_timeout;
++ fw_priv->timeout.data = (u_long) fw_priv;
++ init_timer(&fw_priv->timeout);
++
++ retval = -EAGAIN;
++ fw_priv->proc_dir = create_proc_entry(device, 0644 | S_IFDIR, proc_dir);
++ if (!fw_priv->proc_dir)
++ goto err_free_fw_priv;
++
++ fw_priv->attr_data = create_proc_entry("data", 0644 | S_IFREG,
++ fw_priv->proc_dir);
++ if (!fw_priv->attr_data)
++ goto err_remove_dir;
++
++ fw_priv->attr_data->read_proc = firmware_data_read;
++ fw_priv->attr_data->write_proc = firmware_data_write;
++ fw_priv->attr_data->data = fw_priv;
++
++ fw_priv->attr_loading = create_proc_entry("loading", 0644 | S_IFREG,
++ fw_priv->proc_dir);
++ if (!fw_priv->attr_loading)
++ goto err_remove_data;
++
++ fw_priv->attr_loading->read_proc = firmware_loading_show;
++ fw_priv->attr_loading->write_proc = firmware_loading_store;
++ fw_priv->attr_loading->data = fw_priv;
++
++ retval = 0;
++ fw_priv->fw = kmalloc(sizeof (struct firmware), GFP_KERNEL);
++ if (!fw_priv->fw) {
++ printk(KERN_ERR "%s: kmalloc(struct firmware) failed\n",
++ __FUNCTION__);
++ retval = -ENOMEM;
++ goto err_remove_loading;
++ }
++ memset(fw_priv->fw, 0, sizeof (*fw_priv->fw));
++
++ goto out;
++
++err_remove_loading:
++ remove_proc_entry("loading", fw_priv->proc_dir);
++err_remove_data:
++ remove_proc_entry("data", fw_priv->proc_dir);
++err_remove_dir:
++ remove_proc_entry(device, proc_dir);
++err_free_fw_priv:
++ kfree(fw_priv);
++out:
++ return retval;
++}
++static void
++fw_remove_class_device(struct firmware_priv *fw_priv)
++{
++ remove_proc_entry("loading", fw_priv->proc_dir);
++ remove_proc_entry("data", fw_priv->proc_dir);
++ remove_proc_entry(fw_priv->proc_dir->name, proc_dir);
++}
++
++/**
++ * request_firmware: - request firmware to hotplug and wait for it
++ * Description:
++ * @firmware will be used to return a firmware image by the name
++ * of @name for device @device.
++ *
++ * Should be called from user context where sleeping is allowed.
++ *
++ * @name will be use as $FIRMWARE in the hotplug environment and
++ * should be distinctive enough not to be confused with any other
++ * firmware image for this or any other device.
++ **/
++int
++request_firmware(const struct firmware **firmware, const char *name,
++ const char *device)
++{
++ struct firmware_priv *fw_priv;
++ int retval;
++
++ if (!firmware) {
++ retval = -EINVAL;
++ goto out;
++ }
++ *firmware = NULL;
++
++ retval = fw_setup_class_device(&fw_priv, name, device);
++ if (retval)
++ goto out;
++
++ retval = call_helper("add", name, device);
++ if (retval)
++ goto out;
++ if (loading_timeout) {
++ fw_priv->timeout.expires = jiffies + loading_timeout * HZ;
++ add_timer(&fw_priv->timeout);
++ }
++
++ wait_for_completion(&fw_priv->completion);
++
++ del_timer(&fw_priv->timeout);
++ fw_remove_class_device(fw_priv);
++
++ if (fw_priv->fw->size && !fw_priv->abort) {
++ *firmware = fw_priv->fw;
++ } else {
++ retval = -ENOENT;
++ vfree(fw_priv->fw->data);
++ kfree(fw_priv->fw);
++ }
++out:
++ kfree(fw_priv);
++ return retval;
++}
++
++void
++release_firmware(const struct firmware *fw)
++{
++ if (fw) {
++ vfree(fw->data);
++ kfree(fw);
++ }
++}
++
++/**
++ * register_firmware: - provide a firmware image for later usage
++ *
++ * Description:
++ * Make sure that @data will be available by requesting firmware @name.
++ *
++ * Note: This will not be possible until some kind of persistence
++ * is available.
++ **/
++void
++register_firmware(const char *name, const u8 *data, size_t size)
++{
++ /* This is meaningless without firmware caching, so until we
++ * decide if firmware caching is reasonable just leave it as a
++ * noop */
++}
++
++/* Async support */
++struct firmware_work {
++ struct tq_struct work;
++ struct module *module;
++ const char *name;
++ const char *device;
++ void *context;
++ void (*cont)(const struct firmware *fw, void *context);
++};
++
++static void
++request_firmware_work_func(void *arg)
++{
++ struct firmware_work *fw_work = arg;
++ const struct firmware *fw;
++ if (!arg)
++ return;
++ request_firmware(&fw, fw_work->name, fw_work->device);
++ fw_work->cont(fw, fw_work->context);
++ release_firmware(fw);
++ __MOD_DEC_USE_COUNT(fw_work->module);
++ kfree(fw_work);
++}
++
++/**
++ * request_firmware_nowait:
++ *
++ * Description:
++ * Asynchronous variant of request_firmware() for contexts where
++ * it is not possible to sleep.
++ *
++ * @cont will be called asynchronously when the firmware request is over.
++ *
++ * @context will be passed over to @cont.
++ *
++ * @fw may be %NULL if firmware request fails.
++ *
++ **/
++int
++request_firmware_nowait(
++ struct module *module,
++ const char *name, const char *device, void *context,
++ void (*cont)(const struct firmware *fw, void *context))
++{
++ struct firmware_work *fw_work = kmalloc(sizeof (struct firmware_work),
++ GFP_ATOMIC);
++ if (!fw_work)
++ return -ENOMEM;
++ if (!try_inc_mod_count(module)) {
++ kfree(fw_work);
++ return -EFAULT;
++ }
++
++ *fw_work = (struct firmware_work) {
++ .module = module,
++ .name = name,
++ .device = device,
++ .context = context,
++ .cont = cont,
++ };
++ INIT_TQUEUE(&fw_work->work, request_firmware_work_func, fw_work);
++
++ schedule_task(&fw_work->work);
++ return 0;
++}
++
++static int __init
++firmware_class_init(void)
++{
++ proc_dir = create_proc_entry("driver/firmware", 0755 | S_IFDIR, NULL);
++ if (!proc_dir)
++ return -EAGAIN;
++ proc_dir_timeout = create_proc_entry("timeout",
++ 0644 | S_IFREG, proc_dir);
++ if (!proc_dir_timeout) {
++ remove_proc_entry("driver/firmware", NULL);
++ return -EAGAIN;
++ }
++ proc_dir_timeout->read_proc = firmware_timeout_show;
++ proc_dir_timeout->write_proc = firmware_timeout_store;
++ return 0;
++}
++static void __exit
++firmware_class_exit(void)
++{
++ remove_proc_entry("timeout", proc_dir);
++ remove_proc_entry("driver/firmware", NULL);
++}
++
++module_init(firmware_class_init);
++module_exit(firmware_class_exit);
++
++#ifndef CONFIG_FW_LOADER
++EXPORT_SYMBOL(release_firmware);
++EXPORT_SYMBOL(request_firmware);
++EXPORT_SYMBOL(request_firmware_nowait);
++EXPORT_SYMBOL(register_firmware);
++#endif
+diff -urN linux-2.4.18/lib/Makefile linux-2.4.18-mh15/lib/Makefile
+--- linux-2.4.18/lib/Makefile 2001-09-18 00:31:15.000000000 +0200
++++ linux-2.4.18-mh15/lib/Makefile 2004-08-01 16:26:23.000000000 +0200
+@@ -8,13 +8,17 @@
+
+ L_TARGET := lib.a
+
+-export-objs := cmdline.o dec_and_lock.o rwsem-spinlock.o rwsem.o
++export-objs := cmdline.o dec_and_lock.o rwsem-spinlock.o rwsem.o \
++ firmware_class.o
+
+ obj-y := errno.o ctype.o string.o vsprintf.o brlock.o cmdline.o bust_spinlocks.o rbtree.o
+
++obj-$(CONFIG_FW_LOADER) += firmware_class.o
+ obj-$(CONFIG_RWSEM_GENERIC_SPINLOCK) += rwsem-spinlock.o
+ obj-$(CONFIG_RWSEM_XCHGADD_ALGORITHM) += rwsem.o
+
++include $(TOPDIR)/drivers/bluetooth/Makefile.lib
++
+ ifneq ($(CONFIG_HAVE_DEC_LOCK),y)
+ obj-y += dec_and_lock.o
+ endif
+diff -urN linux-2.4.18/MAINTAINERS linux-2.4.18-mh15/MAINTAINERS
+--- linux-2.4.18/MAINTAINERS 2002-02-25 20:37:52.000000000 +0100
++++ linux-2.4.18-mh15/MAINTAINERS 2004-08-01 16:26:23.000000000 +0200
+@@ -252,10 +252,88 @@
+ L: linux-kernel@vger.kernel.org
+ S: Maintained
+
+-BLUETOOTH SUBSYSTEM (BlueZ)
++BLUETOOTH SUBSYSTEM
++P: Marcel Holtmann
++M: marcel@holtmann.org
+ P: Maxim Krasnyansky
+ M: maxk@qualcomm.com
++L: bluez-devel@lists.sf.net
+ W: http://bluez.sf.net
++W: http://www.bluez.org
++W: http://www.holtmann.org/linux/bluetooth/
++S: Maintained
++
++BLUETOOTH RFCOMM LAYER
++P: Marcel Holtmann
++M: marcel@holtmann.org
++P: Maxim Krasnyansky
++M: maxk@qualcomm.com
++S: Maintained
++
++BLUETOOTH BNEP LAYER
++P: Marcel Holtmann
++M: marcel@holtmann.org
++P: Maxim Krasnyansky
++M: maxk@qualcomm.com
++S: Maintained
++
++BLUETOOTH CMTP LAYER
++P: Marcel Holtmann
++M: marcel@holtmann.org
++S: Maintained
++
++BLUETOOTH HIDP LAYER
++P: Marcel Holtmann
++M: marcel@holtmann.org
++S: Maintained
++
++BLUETOOTH HCI UART DRIVER
++P: Marcel Holtmann
++M: marcel@holtmann.org
++P: Maxim Krasnyansky
++M: maxk@qualcomm.com
++S: Maintained
++
++BLUETOOTH HCI USB DRIVER
++P: Marcel Holtmann
++M: marcel@holtmann.org
++P: Maxim Krasnyansky
++M: maxk@qualcomm.com
++S: Maintained
++
++BLUETOOTH HCI BCM203X DRIVER
++P: Marcel Holtmann
++M: marcel@holtmann.org
++S: Maintained
++
++BLUETOOTH HCI BFUSB DRIVER
++P: Marcel Holtmann
++M: marcel@holtmann.org
++S: Maintained
++
++BLUETOOTH HCI DTL1 DRIVER
++P: Marcel Holtmann
++M: marcel@holtmann.org
++S: Maintained
++
++BLUETOOTH HCI BLUECARD DRIVER
++P: Marcel Holtmann
++M: marcel@holtmann.org
++S: Maintained
++
++BLUETOOTH HCI BT3C DRIVER
++P: Marcel Holtmann
++M: marcel@holtmann.org
++S: Maintained
++
++BLUETOOTH HCI BTUART DRIVER
++P: Marcel Holtmann
++M: marcel@holtmann.org
++S: Maintained
++
++BLUETOOTH HCI VHCI DRIVER
++P: Maxim Krasnyansky
++M: maxk@qualcomm.com
+ S: Maintained
+
+ BTTV VIDEO4LINUX DRIVER
+diff -urN linux-2.4.18/net/bluetooth/af_bluetooth.c linux-2.4.18-mh15/net/bluetooth/af_bluetooth.c
+--- linux-2.4.18/net/bluetooth/af_bluetooth.c 2001-09-07 18:28:38.000000000 +0200
++++ linux-2.4.18-mh15/net/bluetooth/af_bluetooth.c 2004-08-01 16:26:23.000000000 +0200
+@@ -25,14 +25,15 @@
+ /*
+ * BlueZ Bluetooth address family and sockets.
+ *
+- * $Id$
++ * $Id$
+ */
+-#define VERSION "1.1"
++#define VERSION "2.4"
+
+ #include <linux/config.h>
+ #include <linux/module.h>
+
+ #include <linux/types.h>
++#include <linux/list.h>
+ #include <linux/errno.h>
+ #include <linux/kernel.h>
+ #include <linux/major.h>
+@@ -40,6 +41,7 @@
+ #include <linux/slab.h>
+ #include <linux/skbuff.h>
+ #include <linux/init.h>
++#include <linux/poll.h>
+ #include <linux/proc_fs.h>
+ #include <net/sock.h>
+
+@@ -48,70 +50,79 @@
+ #endif
+
+ #include <net/bluetooth/bluetooth.h>
+-#include <net/bluetooth/bluez.h>
++
++#ifndef AF_BLUETOOTH_DEBUG
++#undef BT_DBG
++#define BT_DBG( A... )
++#endif
+
+ /* Bluetooth sockets */
+-static struct net_proto_family *bluez_sock[BLUEZ_MAX_PROTO];
++#define BLUEZ_MAX_PROTO 7
++static struct net_proto_family *bluez_proto[BLUEZ_MAX_PROTO];
+
+ int bluez_sock_register(int proto, struct net_proto_family *ops)
+ {
+- if (proto > BLUEZ_MAX_PROTO)
++ if (proto >= BLUEZ_MAX_PROTO)
+ return -EINVAL;
+
+- if (bluez_sock[proto])
++ if (bluez_proto[proto])
+ return -EEXIST;
+
+- bluez_sock[proto] = ops;
++ bluez_proto[proto] = ops;
+ return 0;
+ }
+
+ int bluez_sock_unregister(int proto)
+ {
+- if (proto > BLUEZ_MAX_PROTO)
++ if (proto >= BLUEZ_MAX_PROTO)
+ return -EINVAL;
+
+- if (!bluez_sock[proto])
++ if (!bluez_proto[proto])
+ return -ENOENT;
+
+- bluez_sock[proto] = NULL;
++ bluez_proto[proto] = NULL;
+ return 0;
+ }
+
+ static int bluez_sock_create(struct socket *sock, int proto)
+ {
+- if (proto > BLUEZ_MAX_PROTO)
++ if (proto >= BLUEZ_MAX_PROTO)
+ return -EINVAL;
+
+ #if defined(CONFIG_KMOD)
+- if (!bluez_sock[proto]) {
++ if (!bluez_proto[proto]) {
+ char module_name[30];
+ sprintf(module_name, "bt-proto-%d", proto);
+ request_module(module_name);
+ }
+ #endif
+
+- if (!bluez_sock[proto])
++ if (!bluez_proto[proto])
+ return -ENOENT;
+
+- return bluez_sock[proto]->create(sock, proto);
++ return bluez_proto[proto]->create(sock, proto);
++}
++
++void bluez_sock_init(struct socket *sock, struct sock *sk)
++{
++ sock_init_data(sock, sk);
++ INIT_LIST_HEAD(&bluez_pi(sk)->accept_q);
+ }
+
+ void bluez_sock_link(struct bluez_sock_list *l, struct sock *sk)
+ {
+- write_lock(&l->lock);
+-
++ write_lock_bh(&l->lock);
+ sk->next = l->head;
+ l->head = sk;
+ sock_hold(sk);
+-
+- write_unlock(&l->lock);
++ write_unlock_bh(&l->lock);
+ }
+
+ void bluez_sock_unlink(struct bluez_sock_list *l, struct sock *sk)
+ {
+ struct sock **skp;
+
+- write_lock(&l->lock);
++ write_lock_bh(&l->lock);
+ for (skp = &l->head; *skp; skp = &((*skp)->next)) {
+ if (*skp == sk) {
+ *skp = sk->next;
+@@ -119,7 +130,163 @@
+ break;
+ }
+ }
+- write_unlock(&l->lock);
++ write_unlock_bh(&l->lock);
++}
++
++void bluez_accept_enqueue(struct sock *parent, struct sock *sk)
++{
++ BT_DBG("parent %p, sk %p", parent, sk);
++
++ sock_hold(sk);
++ list_add_tail(&bluez_pi(sk)->accept_q, &bluez_pi(parent)->accept_q);
++ bluez_pi(sk)->parent = parent;
++ parent->ack_backlog++;
++}
++
++static void bluez_accept_unlink(struct sock *sk)
++{
++ BT_DBG("sk %p state %d", sk, sk->state);
++
++ list_del_init(&bluez_pi(sk)->accept_q);
++ bluez_pi(sk)->parent->ack_backlog--;
++ bluez_pi(sk)->parent = NULL;
++ sock_put(sk);
++}
++
++struct sock *bluez_accept_dequeue(struct sock *parent, struct socket *newsock)
++{
++ struct list_head *p, *n;
++ struct bluez_pinfo *pi;
++ struct sock *sk;
++
++ BT_DBG("parent %p", parent);
++
++ list_for_each_safe(p, n, &bluez_pi(parent)->accept_q) {
++ pi = list_entry(p, struct bluez_pinfo, accept_q);
++ sk = bluez_sk(pi);
++
++ lock_sock(sk);
++ if (sk->state == BT_CLOSED) {
++ release_sock(sk);
++ bluez_accept_unlink(sk);
++ continue;
++ }
++
++ if (sk->state == BT_CONNECTED || !newsock) {
++ bluez_accept_unlink(sk);
++ if (newsock)
++ sock_graft(sk, newsock);
++ release_sock(sk);
++ return sk;
++ }
++ release_sock(sk);
++ }
++ return NULL;
++}
++
++int bluez_sock_recvmsg(struct socket *sock, struct msghdr *msg, int len, int flags, struct scm_cookie *scm)
++{
++ int noblock = flags & MSG_DONTWAIT;
++ struct sock *sk = sock->sk;
++ struct sk_buff *skb;
++ int copied, err;
++
++ BT_DBG("sock %p sk %p len %d", sock, sk, len);
++
++ if (flags & (MSG_OOB))
++ return -EOPNOTSUPP;
++
++ if (!(skb = skb_recv_datagram(sk, flags, noblock, &err))) {
++ if (sk->shutdown & RCV_SHUTDOWN)
++ return 0;
++ return err;
++ }
++
++ msg->msg_namelen = 0;
++
++ copied = skb->len;
++ if (len < copied) {
++ msg->msg_flags |= MSG_TRUNC;
++ copied = len;
++ }
++
++ skb->h.raw = skb->data;
++ err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
++
++ skb_free_datagram(sk, skb);
++
++ return err ? : copied;
++}
++
++unsigned int bluez_sock_poll(struct file * file, struct socket *sock, poll_table *wait)
++{
++ struct sock *sk = sock->sk;
++ unsigned int mask = 0;
++
++ BT_DBG("sock %p, sk %p", sock, sk);
++
++ poll_wait(file, sk->sleep, wait);
++
++ if (sk->err || !skb_queue_empty(&sk->error_queue))
++ mask |= POLLERR;
++
++ if (sk->shutdown == SHUTDOWN_MASK)
++ mask |= POLLHUP;
++
++ if (!skb_queue_empty(&sk->receive_queue) ||
++ !list_empty(&bluez_pi(sk)->accept_q) ||
++ (sk->shutdown & RCV_SHUTDOWN))
++ mask |= POLLIN | POLLRDNORM;
++
++ if (sk->state == BT_CLOSED)
++ mask |= POLLHUP;
++
++ if (sk->state == BT_CONNECT ||
++ sk->state == BT_CONNECT2 ||
++ sk->state == BT_CONFIG)
++ return mask;
++
++ if (sock_writeable(sk))
++ mask |= POLLOUT | POLLWRNORM | POLLWRBAND;
++ else
++ set_bit(SOCK_ASYNC_NOSPACE, &sk->socket->flags);
++
++ return mask;
++}
++
++int bluez_sock_wait_state(struct sock *sk, int state, unsigned long timeo)
++{
++ DECLARE_WAITQUEUE(wait, current);
++ int err = 0;
++
++ BT_DBG("sk %p", sk);
++
++ add_wait_queue(sk->sleep, &wait);
++ while (sk->state != state) {
++ set_current_state(TASK_INTERRUPTIBLE);
++
++ if (!timeo) {
++ err = -EAGAIN;
++ break;
++ }
++
++ if (signal_pending(current)) {
++ err = sock_intr_errno(timeo);
++ break;
++ }
++
++ release_sock(sk);
++ timeo = schedule_timeout(timeo);
++ lock_sock(sk);
++
++ if (sk->err) {
++ err = sock_error(sk);
++ break;
++ }
++ }
++ set_current_state(TASK_RUNNING);
++ remove_wait_queue(sk->sleep, &wait);
++ return err;
+ }
+
+ struct net_proto_family bluez_sock_family_ops =
+@@ -129,9 +296,9 @@
+
+ int bluez_init(void)
+ {
+- INF("BlueZ HCI Core ver %s Copyright (C) 2000,2001 Qualcomm Inc",
++ BT_INFO("BlueZ Core ver %s Copyright (C) 2000,2001 Qualcomm Inc",
+ VERSION);
+- INF("Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>");
++ BT_INFO("Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>");
+
+ proc_mkdir("bluetooth", NULL);
+
+@@ -164,5 +331,6 @@
+ module_exit(bluez_cleanup);
+
+ MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>");
+-MODULE_DESCRIPTION("BlueZ HCI Core ver " VERSION);
++MODULE_DESCRIPTION("BlueZ Core ver " VERSION);
++MODULE_LICENSE("GPL");
+ #endif
+diff -urN linux-2.4.18/net/bluetooth/bnep/bnep.h linux-2.4.18-mh15/net/bluetooth/bnep/bnep.h
+--- linux-2.4.18/net/bluetooth/bnep/bnep.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/net/bluetooth/bnep/bnep.h 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,185 @@
++/*
++ BNEP protocol definition for Linux Bluetooth stack (BlueZ).
++ Copyright (C) 2002 Maxim Krasnyansky <maxk@qualcomm.com>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License, version 2, as
++ published by the Free Software Foundation.
++
++ This program is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ GNU General Public License for more details.
++
++ You should have received a copy of the GNU General Public License
++ along with this program; if not, write to the Free Software
++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++*/
++
++/*
++ * $Id$
++ */
++
++#ifndef _BNEP_H
++#define _BNEP_H
++
++#include <linux/types.h>
++#include <net/bluetooth/bluetooth.h>
++
++#include "crc32.h"
++
++// Limits
++#define BNEP_MAX_PROTO_FILTERS 5
++#define BNEP_MAX_MULTICAST_FILTERS 20
++
++// UUIDs
++#define BNEP_BASE_UUID 0x0000000000001000800000805F9B34FB
++#define BNEP_UUID16 0x02
++#define BNEP_UUID32 0x04
++#define BNEP_UUID128 0x16
++
++#define BNEP_SVC_PANU 0x1115
++#define BNEP_SVC_NAP 0x1116
++#define BNEP_SVC_GN 0x1117
++
++// Packet types
++#define BNEP_GENERAL 0x00
++#define BNEP_CONTROL 0x01
++#define BNEP_COMPRESSED 0x02
++#define BNEP_COMPRESSED_SRC_ONLY 0x03
++#define BNEP_COMPRESSED_DST_ONLY 0x04
++
++// Control types
++#define BNEP_CMD_NOT_UNDERSTOOD 0x00
++#define BNEP_SETUP_CONN_REQ 0x01
++#define BNEP_SETUP_CONN_RSP 0x02
++#define BNEP_FILTER_NET_TYPE_SET 0x03
++#define BNEP_FILTER_NET_TYPE_RSP 0x04
++#define BNEP_FILTER_MULTI_ADDR_SET 0x05
++#define BNEP_FILTER_MULTI_ADDR_RSP 0x06
++
++// Extension types
++#define BNEP_EXT_CONTROL 0x00
++
++// Response messages
++#define BNEP_SUCCESS 0x00
++
++#define BNEP_CONN_INVALID_DST 0x01
++#define BNEP_CONN_INVALID_SRC 0x02
++#define BNEP_CONN_INVALID_SVC 0x03
++#define BNEP_CONN_NOT_ALLOWED 0x04
++
++#define BNEP_FILTER_UNSUPPORTED_REQ 0x01
++#define BNEP_FILTER_INVALID_RANGE 0x02
++#define BNEP_FILTER_INVALID_MCADDR 0x02
++#define BNEP_FILTER_LIMIT_REACHED 0x03
++#define BNEP_FILTER_DENIED_SECURITY 0x04
++
++// L2CAP settings
++#define BNEP_MTU 1691
++#define BNEP_PSM 0x0f
++#define BNEP_FLUSH_TO 0xffff
++#define BNEP_CONNECT_TO 15
++#define BNEP_FILTER_TO 15
++
++// Headers
++#define BNEP_TYPE_MASK 0x7f
++#define BNEP_EXT_HEADER 0x80
++
++struct bnep_setup_conn_req {
++ __u8 type;
++ __u8 ctrl;
++ __u8 uuid_size;
++ __u8 service[0];
++} __attribute__((packed));
++
++struct bnep_set_filter_req {
++ __u8 type;
++ __u8 ctrl;
++ __u16 len;
++ __u8 list[0];
++} __attribute__((packed));
++
++struct bnep_control_rsp {
++ __u8 type;
++ __u8 ctrl;
++ __u16 resp;
++} __attribute__((packed));
++
++struct bnep_ext_hdr {
++ __u8 type;
++ __u8 len;
++ __u8 data[0];
++} __attribute__((packed));
++
++/* BNEP ioctl defines */
++#define BNEPCONNADD _IOW('B', 200, int)
++#define BNEPCONNDEL _IOW('B', 201, int)
++#define BNEPGETCONNLIST _IOR('B', 210, int)
++#define BNEPGETCONNINFO _IOR('B', 211, int)
++
++struct bnep_connadd_req {
++ int sock; // Connected socket
++ __u32 flags;
++ __u16 role;
++ char device[16]; // Name of the Ethernet device
++};
++
++struct bnep_conndel_req {
++ __u32 flags;
++ __u8 dst[ETH_ALEN];
++};
++
++struct bnep_conninfo {
++ __u32 flags;
++ __u16 role;
++ __u16 state;
++ __u8 dst[ETH_ALEN];
++ char device[16];
++};
++
++struct bnep_connlist_req {
++ __u32 cnum;
++ struct bnep_conninfo *ci;
++};
++
++struct bnep_proto_filter {
++ __u16 start;
++ __u16 end;
++};
++
++int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock);
++int bnep_del_connection(struct bnep_conndel_req *req);
++int bnep_get_connlist(struct bnep_connlist_req *req);
++int bnep_get_conninfo(struct bnep_conninfo *ci);
++
++// BNEP sessions
++struct bnep_session {
++ struct list_head list;
++
++ unsigned int role;
++ unsigned long state;
++ unsigned long flags;
++ atomic_t killed;
++
++ struct ethhdr eh;
++ struct msghdr msg;
++
++ struct bnep_proto_filter proto_filter[BNEP_MAX_PROTO_FILTERS];
++ u64 mc_filter;
++
++ struct socket *sock;
++ struct net_device dev;
++ struct net_device_stats stats;
++};
++
++int bnep_net_init(struct net_device *dev);
++int bnep_sock_init(void);
++int bnep_sock_cleanup(void);
++
++static inline int bnep_mc_hash(__u8 *addr)
++{
++ return (bnep_crc32(~0, addr, ETH_ALEN) >> 26);
++}
++
++#endif
+diff -urN linux-2.4.18/net/bluetooth/bnep/Config.in linux-2.4.18-mh15/net/bluetooth/bnep/Config.in
+--- linux-2.4.18/net/bluetooth/bnep/Config.in 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/net/bluetooth/bnep/Config.in 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,11 @@
++#
++# Bluetooth BNEP layer configuration
++#
++
++dep_tristate 'BNEP protocol support' CONFIG_BLUEZ_BNEP $CONFIG_BLUEZ_L2CAP
++
++if [ "$CONFIG_BLUEZ_BNEP" != "n" ]; then
++ bool ' Multicast filter support' CONFIG_BLUEZ_BNEP_MC_FILTER
++ bool ' Protocol filter support' CONFIG_BLUEZ_BNEP_PROTO_FILTER
++fi
++
+diff -urN linux-2.4.18/net/bluetooth/bnep/core.c linux-2.4.18-mh15/net/bluetooth/bnep/core.c
+--- linux-2.4.18/net/bluetooth/bnep/core.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/net/bluetooth/bnep/core.c 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,718 @@
++/*
++ BNEP implementation for Linux Bluetooth stack (BlueZ).
++ Copyright (C) 2001-2002 Inventel Systemes
++ Written 2001-2002 by
++ Clément Moreau <clement.moreau@inventel.fr>
++ David Libault <david.libault@inventel.fr>
++
++ Copyright (C) 2002 Maxim Krasnyanskiy <maxk@qualcomm.com>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License version 2 as
++ published by the Free Software Foundation;
++
++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++
++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
++ SOFTWARE IS DISCLAIMED.
++*/
++
++/*
++ * $Id$
++ */
++
++#define __KERNEL_SYSCALLS__
++
++#include <linux/config.h>
++#include <linux/module.h>
++
++#include <linux/kernel.h>
++#include <linux/sched.h>
++#include <linux/signal.h>
++#include <linux/init.h>
++#include <linux/wait.h>
++#include <linux/errno.h>
++#include <linux/smp_lock.h>
++#include <linux/net.h>
++#include <net/sock.h>
++
++#include <linux/socket.h>
++#include <linux/file.h>
++
++#include <linux/netdevice.h>
++#include <linux/etherdevice.h>
++#include <linux/skbuff.h>
++
++#include <asm/unaligned.h>
++
++#include <net/bluetooth/bluetooth.h>
++#include <net/bluetooth/l2cap.h>
++
++#include "bnep.h"
++
++#ifndef CONFIG_BLUEZ_BNEP_DEBUG
++#undef BT_DBG
++#define BT_DBG(D...)
++#endif
++
++#define VERSION "1.2"
++
++static LIST_HEAD(bnep_session_list);
++static DECLARE_RWSEM(bnep_session_sem);
++
++static struct bnep_session *__bnep_get_session(u8 *dst)
++{
++ struct bnep_session *s;
++ struct list_head *p;
++
++ BT_DBG("");
++
++ list_for_each(p, &bnep_session_list) {
++ s = list_entry(p, struct bnep_session, list);
++ if (!memcmp(dst, s->eh.h_source, ETH_ALEN))
++ return s;
++ }
++ return NULL;
++}
++
++static void __bnep_link_session(struct bnep_session *s)
++{
++ MOD_INC_USE_COUNT;
++ list_add(&s->list, &bnep_session_list);
++}
++
++static void __bnep_unlink_session(struct bnep_session *s)
++{
++ list_del(&s->list);
++ MOD_DEC_USE_COUNT;
++}
++
++static int bnep_send(struct bnep_session *s, void *data, size_t len)
++{
++ struct socket *sock = s->sock;
++ struct iovec iv = { data, len };
++ s->msg.msg_iov = &iv;
++ s->msg.msg_iovlen = 1;
++ return sock->ops->sendmsg(sock, &s->msg, len, NULL);
++}
++
++static int bnep_send_rsp(struct bnep_session *s, u8 ctrl, u16 resp)
++{
++ struct bnep_control_rsp rsp;
++ rsp.type = BNEP_CONTROL;
++ rsp.ctrl = ctrl;
++ rsp.resp = htons(resp);
++ return bnep_send(s, &rsp, sizeof(rsp));
++}
++
++#ifdef CONFIG_BLUEZ_BNEP_PROTO_FILTER
++static inline void bnep_set_default_proto_filter(struct bnep_session *s)
++{
++ /* (IPv4, ARP) */
++ s->proto_filter[0].start = htons(0x0800);
++ s->proto_filter[0].end = htons(0x0806);
++ /* (RARP, AppleTalk) */
++ s->proto_filter[1].start = htons(0x8035);
++ s->proto_filter[1].end = htons(0x80F3);
++ /* (IPX, IPv6) */
++ s->proto_filter[2].start = htons(0x8137);
++ s->proto_filter[2].end = htons(0x86DD);
++}
++#endif
++
++static int bnep_ctrl_set_netfilter(struct bnep_session *s, u16 *data, int len)
++{
++ int n;
++
++ if (len < 2)
++ return -EILSEQ;
++
++ n = ntohs(get_unaligned(data));
++ data++; len -= 2;
++
++ if (len < n)
++ return -EILSEQ;
++
++ BT_DBG("filter len %d", n);
++
++#ifdef CONFIG_BLUEZ_BNEP_PROTO_FILTER
++ n /= 4;
++ if (n <= BNEP_MAX_PROTO_FILTERS) {
++ struct bnep_proto_filter *f = s->proto_filter;
++ int i;
++
++ for (i = 0; i < n; i++) {
++ f[i].start = get_unaligned(data++);
++ f[i].end = get_unaligned(data++);
++
++ BT_DBG("proto filter start %d end %d",
++ f[i].start, f[i].end);
++ }
++
++ if (i < BNEP_MAX_PROTO_FILTERS)
++ memset(f + i, 0, sizeof(*f));
++
++ if (n == 0)
++ bnep_set_default_proto_filter(s);
++
++ bnep_send_rsp(s, BNEP_FILTER_NET_TYPE_RSP, BNEP_SUCCESS);
++ } else {
++ bnep_send_rsp(s, BNEP_FILTER_NET_TYPE_RSP, BNEP_FILTER_LIMIT_REACHED);
++ }
++#else
++ bnep_send_rsp(s, BNEP_FILTER_NET_TYPE_RSP, BNEP_FILTER_UNSUPPORTED_REQ);
++#endif
++ return 0;
++}
++
++static int bnep_ctrl_set_mcfilter(struct bnep_session *s, u8 *data, int len)
++{
++ int n;
++
++ if (len < 2)
++ return -EILSEQ;
++
++ n = ntohs(get_unaligned((u16 *) data));
++ data += 2; len -= 2;
++
++ if (len < n)
++ return -EILSEQ;
++
++ BT_DBG("filter len %d", n);
++
++#ifdef CONFIG_BLUEZ_BNEP_MC_FILTER
++ n /= (ETH_ALEN * 2);
++
++ if (n > 0) {
++ s->mc_filter = 0;
++
++ /* Always send broadcast */
++ set_bit(bnep_mc_hash(s->dev.broadcast), &s->mc_filter);
++
++ /* Add address ranges to the multicast hash */
++ for (; n > 0; n--) {
++ u8 a1[6], *a2;
++
++ memcpy(a1, data, ETH_ALEN); data += ETH_ALEN;
++ a2 = data; data += ETH_ALEN;
++
++ BT_DBG("mc filter %s -> %s",
++ batostr((void *) a1), batostr((void *) a2));
++
++ #define INCA(a) { int i = 5; while (i >=0 && ++a[i--] == 0); }
++
++ /* Iterate from a1 to a2 */
++ set_bit(bnep_mc_hash(a1), &s->mc_filter);
++ while (memcmp(a1, a2, 6) < 0 && s->mc_filter != ~0LL) {
++ INCA(a1);
++ set_bit(bnep_mc_hash(a1), &s->mc_filter);
++ }
++ }
++ }
++
++ BT_DBG("mc filter hash 0x%llx", s->mc_filter);
++
++ bnep_send_rsp(s, BNEP_FILTER_MULTI_ADDR_RSP, BNEP_SUCCESS);
++#else
++ bnep_send_rsp(s, BNEP_FILTER_MULTI_ADDR_RSP, BNEP_FILTER_UNSUPPORTED_REQ);
++#endif
++ return 0;
++}
++
++static int bnep_rx_control(struct bnep_session *s, void *data, int len)
++{
++ u8 cmd = *(u8 *)data;
++ int err = 0;
++
++ data++; len--;
++
++ switch (cmd) {
++ case BNEP_CMD_NOT_UNDERSTOOD:
++ case BNEP_SETUP_CONN_REQ:
++ case BNEP_SETUP_CONN_RSP:
++ case BNEP_FILTER_NET_TYPE_RSP:
++ case BNEP_FILTER_MULTI_ADDR_RSP:
++ /* Ignore these for now */
++ break;
++
++ case BNEP_FILTER_NET_TYPE_SET:
++ err = bnep_ctrl_set_netfilter(s, data, len);
++ break;
++
++ case BNEP_FILTER_MULTI_ADDR_SET:
++ err = bnep_ctrl_set_mcfilter(s, data, len);
++ break;
++
++ default: {
++ u8 pkt[3];
++ pkt[0] = BNEP_CONTROL;
++ pkt[1] = BNEP_CMD_NOT_UNDERSTOOD;
++ pkt[2] = cmd;
++ bnep_send(s, pkt, sizeof(pkt));
++ }
++ break;
++ }
++
++ return err;
++}
++
++static int bnep_rx_extension(struct bnep_session *s, struct sk_buff *skb)
++{
++ struct bnep_ext_hdr *h;
++ int err = 0;
++
++ do {
++ h = (void *) skb->data;
++ if (!skb_pull(skb, sizeof(*h))) {
++ err = -EILSEQ;
++ break;
++ }
++
++ BT_DBG("type 0x%x len %d", h->type, h->len);
++
++ switch (h->type & BNEP_TYPE_MASK) {
++ case BNEP_EXT_CONTROL:
++ bnep_rx_control(s, skb->data, skb->len);
++ break;
++
++ default:
++ /* Unknown extension, skip it. */
++ break;
++ }
++
++ if (!skb_pull(skb, h->len)) {
++ err = -EILSEQ;
++ break;
++ }
++ } while (!err && (h->type & BNEP_EXT_HEADER));
++
++ return err;
++}
++
++static u8 __bnep_rx_hlen[] = {
++ ETH_HLEN, /* BNEP_GENERAL */
++ 0, /* BNEP_CONTROL */
++ 2, /* BNEP_COMPRESSED */
++ ETH_ALEN + 2, /* BNEP_COMPRESSED_SRC_ONLY */
++ ETH_ALEN + 2 /* BNEP_COMPRESSED_DST_ONLY */
++};
++#define BNEP_RX_TYPES (sizeof(__bnep_rx_hlen) - 1)
++
++static inline int bnep_rx_frame(struct bnep_session *s, struct sk_buff *skb)
++{
++ struct net_device *dev = &s->dev;
++ struct sk_buff *nskb;
++ u8 type;
++
++ dev->last_rx = jiffies;
++ s->stats.rx_bytes += skb->len;
++
++ type = *(u8 *) skb->data; skb_pull(skb, 1);
++
++ if ((type & BNEP_TYPE_MASK) > BNEP_RX_TYPES)
++ goto badframe;
++
++ if ((type & BNEP_TYPE_MASK) == BNEP_CONTROL) {
++ bnep_rx_control(s, skb->data, skb->len);
++ kfree_skb(skb);
++ return 0;
++ }
++
++ skb->mac.raw = skb->data;
++
++ /* Verify and pull out header */
++ if (!skb_pull(skb, __bnep_rx_hlen[type & BNEP_TYPE_MASK]))
++ goto badframe;
++
++ s->eh.h_proto = get_unaligned((u16 *) (skb->data - 2));
++
++ if (type & BNEP_EXT_HEADER) {
++ if (bnep_rx_extension(s, skb) < 0)
++ goto badframe;
++ }
++
++ /* Strip 802.1p header */
++ if (ntohs(s->eh.h_proto) == 0x8100) {
++ if (!skb_pull(skb, 4))
++ goto badframe;
++ s->eh.h_proto = get_unaligned((u16 *) (skb->data - 2));
++ }
++
++ /* We have to alloc new skb and copy data here :(. Because original skb
++ * may not be modified and because of the alignment requirements. */
++ nskb = alloc_skb(2 + ETH_HLEN + skb->len, GFP_KERNEL);
++ if (!nskb) {
++ s->stats.rx_dropped++;
++ kfree_skb(skb);
++ return -ENOMEM;
++ }
++ skb_reserve(nskb, 2);
++
++ /* Decompress header and construct ether frame */
++ switch (type & BNEP_TYPE_MASK) {
++ case BNEP_COMPRESSED:
++ memcpy(__skb_put(nskb, ETH_HLEN), &s->eh, ETH_HLEN);
++ break;
++
++ case BNEP_COMPRESSED_SRC_ONLY:
++ memcpy(__skb_put(nskb, ETH_ALEN), s->eh.h_dest, ETH_ALEN);
++ memcpy(__skb_put(nskb, ETH_ALEN), skb->mac.raw, ETH_ALEN);
++ put_unaligned(s->eh.h_proto, (u16 *) __skb_put(nskb, 2));
++ break;
++
++ case BNEP_COMPRESSED_DST_ONLY:
++ memcpy(__skb_put(nskb, ETH_ALEN), skb->mac.raw, ETH_ALEN);
++ memcpy(__skb_put(nskb, ETH_ALEN + 2), s->eh.h_source, ETH_ALEN + 2);
++ break;
++
++ case BNEP_GENERAL:
++ memcpy(__skb_put(nskb, ETH_ALEN * 2), skb->mac.raw, ETH_ALEN * 2);
++ put_unaligned(s->eh.h_proto, (u16 *) __skb_put(nskb, 2));
++ break;
++ }
++
++ memcpy(__skb_put(nskb, skb->len), skb->data, skb->len);
++ kfree_skb(skb);
++
++ s->stats.rx_packets++;
++ nskb->dev = dev;
++ nskb->ip_summed = CHECKSUM_UNNECESSARY;
++ nskb->protocol = eth_type_trans(nskb, dev);
++ netif_rx_ni(nskb);
++ return 0;
++
++badframe:
++ s->stats.rx_errors++;
++ kfree_skb(skb);
++ return 0;
++}
++
++static u8 __bnep_tx_types[] = {
++ BNEP_GENERAL,
++ BNEP_COMPRESSED_SRC_ONLY,
++ BNEP_COMPRESSED_DST_ONLY,
++ BNEP_COMPRESSED
++};
++
++static inline int bnep_tx_frame(struct bnep_session *s, struct sk_buff *skb)
++{
++ struct ethhdr *eh = (void *) skb->data;
++ struct socket *sock = s->sock;
++ struct iovec iv[3];
++ int len = 0, il = 0;
++ u8 type = 0;
++
++ BT_DBG("skb %p dev %p type %d", skb, skb->dev, skb->pkt_type);
++
++ if (!skb->dev) {
++ /* Control frame sent by us */
++ goto send;
++ }
++
++ iv[il++] = (struct iovec) { &type, 1 };
++ len++;
++
++ if (!memcmp(eh->h_dest, s->eh.h_source, ETH_ALEN))
++ type |= 0x01;
++
++ if (!memcmp(eh->h_source, s->eh.h_dest, ETH_ALEN))
++ type |= 0x02;
++
++ if (type)
++ skb_pull(skb, ETH_ALEN * 2);
++
++ type = __bnep_tx_types[type];
++ switch (type) {
++ case BNEP_COMPRESSED_SRC_ONLY:
++ iv[il++] = (struct iovec) { eh->h_source, ETH_ALEN };
++ len += ETH_ALEN;
++ break;
++
++ case BNEP_COMPRESSED_DST_ONLY:
++ iv[il++] = (struct iovec) { eh->h_dest, ETH_ALEN };
++ len += ETH_ALEN;
++ break;
++ }
++
++send:
++ iv[il++] = (struct iovec) { skb->data, skb->len };
++ len += skb->len;
++
++ /* FIXME: linearize skb */
++
++ s->msg.msg_iov = iv;
++ s->msg.msg_iovlen = il;
++ len = sock->ops->sendmsg(sock, &s->msg, len, NULL);
++ kfree_skb(skb);
++
++ if (len > 0) {
++ s->stats.tx_bytes += len;
++ s->stats.tx_packets++;
++ return 0;
++ }
++
++ return len;
++}
++
++static int bnep_session(void *arg)
++{
++ struct bnep_session *s = arg;
++ struct net_device *dev = &s->dev;
++ struct sock *sk = s->sock->sk;
++ struct sk_buff *skb;
++ wait_queue_t wait;
++
++ BT_DBG("");
++
++ daemonize(); reparent_to_init();
++
++ sprintf(current->comm, "kbnepd %s", dev->name);
++
++ sigfillset(&current->blocked);
++ flush_signals(current);
++
++ current->nice = -15;
++
++ set_fs(KERNEL_DS);
++
++ init_waitqueue_entry(&wait, current);
++ add_wait_queue(sk->sleep, &wait);
++ while (!atomic_read(&s->killed)) {
++ set_current_state(TASK_INTERRUPTIBLE);
++
++ // RX
++ while ((skb = skb_dequeue(&sk->receive_queue))) {
++ skb_orphan(skb);
++ bnep_rx_frame(s, skb);
++ }
++
++ if (sk->state != BT_CONNECTED)
++ break;
++
++ // TX
++ while ((skb = skb_dequeue(&sk->write_queue)))
++ if (bnep_tx_frame(s, skb))
++ break;
++ netif_wake_queue(dev);
++
++ schedule();
++ }
++ set_current_state(TASK_RUNNING);
++ remove_wait_queue(sk->sleep, &wait);
++
++ /* Cleanup session */
++ down_write(&bnep_session_sem);
++
++ /* Delete network device */
++ unregister_netdev(dev);
++
++ /* Release the socket */
++ fput(s->sock->file);
++
++ __bnep_unlink_session(s);
++
++ up_write(&bnep_session_sem);
++ kfree(s);
++ return 0;
++}
++
++int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock)
++{
++ struct net_device *dev;
++ struct bnep_session *s, *ss;
++ u8 dst[ETH_ALEN], src[ETH_ALEN];
++ int err;
++
++ BT_DBG("");
++
++ baswap((void *) dst, &bluez_pi(sock->sk)->dst);
++ baswap((void *) src, &bluez_pi(sock->sk)->src);
++
++ s = kmalloc(sizeof(struct bnep_session), GFP_KERNEL);
++ if (!s)
++ return -ENOMEM;
++ memset(s, 0, sizeof(struct bnep_session));
++
++ down_write(&bnep_session_sem);
++
++ ss = __bnep_get_session(dst);
++ if (ss && ss->state == BT_CONNECTED) {
++ err = -EEXIST;
++ goto failed;
++ }
++
++ dev = &s->dev;
++
++ if (*req->device)
++ strcpy(dev->name, req->device);
++ else
++ strcpy(dev->name, "bnep%d");
++
++ memset(dev->broadcast, 0xff, ETH_ALEN);
++
++ /* This is rx header therefor addresses are swaped.
++ * ie eh.h_dest is our local address. */
++ memcpy(s->eh.h_dest, &src, ETH_ALEN);
++ memcpy(s->eh.h_source, &dst, ETH_ALEN);
++
++ s->sock = sock;
++ s->role = req->role;
++ s->state = BT_CONNECTED;
++
++ s->msg.msg_flags = MSG_NOSIGNAL;
++
++#ifdef CONFIG_BLUEZ_BNEP_MC_FILTER
++ /* Set default mc filter */
++ set_bit(bnep_mc_hash(dev->broadcast), &s->mc_filter);
++#endif
++
++#ifdef CONFIG_BLUEZ_BNEP_PROTO_FILTER
++ /* Set default protocol filter */
++ bnep_set_default_proto_filter(s);
++#endif
++
++ dev->init = bnep_net_init;
++ dev->priv = s;
++ err = register_netdev(dev);
++ if (err) {
++ goto failed;
++ }
++
++ __bnep_link_session(s);
++
++ err = kernel_thread(bnep_session, s, CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
++ if (err < 0) {
++ /* Session thread start failed, gotta cleanup. */
++ unregister_netdev(dev);
++ __bnep_unlink_session(s);
++ goto failed;
++ }
++
++ up_write(&bnep_session_sem);
++ strcpy(req->device, dev->name);
++ return 0;
++
++failed:
++ up_write(&bnep_session_sem);
++ kfree(s);
++ return err;
++}
++
++int bnep_del_connection(struct bnep_conndel_req *req)
++{
++ struct bnep_session *s;
++ int err = 0;
++
++ BT_DBG("");
++
++ down_read(&bnep_session_sem);
++
++ s = __bnep_get_session(req->dst);
++ if (s) {
++ /* Wakeup user-space which is polling for socket errors.
++ * This is temporary hack untill we have shutdown in L2CAP */
++ s->sock->sk->err = EUNATCH;
++
++ /* Kill session thread */
++ atomic_inc(&s->killed);
++ wake_up_interruptible(s->sock->sk->sleep);
++ } else
++ err = -ENOENT;
++
++ up_read(&bnep_session_sem);
++ return err;
++}
++
++static void __bnep_copy_ci(struct bnep_conninfo *ci, struct bnep_session *s)
++{
++ memcpy(ci->dst, s->eh.h_source, ETH_ALEN);
++ strcpy(ci->device, s->dev.name);
++ ci->flags = s->flags;
++ ci->state = s->state;
++ ci->role = s->role;
++}
++
++int bnep_get_connlist(struct bnep_connlist_req *req)
++{
++ struct list_head *p;
++ int err = 0, n = 0;
++
++ down_read(&bnep_session_sem);
++
++ list_for_each(p, &bnep_session_list) {
++ struct bnep_session *s;
++ struct bnep_conninfo ci;
++
++ s = list_entry(p, struct bnep_session, list);
++
++ __bnep_copy_ci(&ci, s);
++
++ if (copy_to_user(req->ci, &ci, sizeof(ci))) {
++ err = -EFAULT;
++ break;
++ }
++
++ if (++n >= req->cnum)
++ break;
++
++ req->ci++;
++ }
++ req->cnum = n;
++
++ up_read(&bnep_session_sem);
++ return err;
++}
++
++int bnep_get_conninfo(struct bnep_conninfo *ci)
++{
++ struct bnep_session *s;
++ int err = 0;
++
++ down_read(&bnep_session_sem);
++
++ s = __bnep_get_session(ci->dst);
++ if (s)
++ __bnep_copy_ci(ci, s);
++ else
++ err = -ENOENT;
++
++ up_read(&bnep_session_sem);
++ return err;
++}
++
++static int __init bnep_init_module(void)
++{
++ l2cap_load();
++
++ bnep_crc32_init();
++ bnep_sock_init();
++
++ BT_INFO("BlueZ BNEP ver %s", VERSION);
++ BT_INFO("Copyright (C) 2001,2002 Inventel Systemes");
++ BT_INFO("Written 2001,2002 by Clement Moreau <clement.moreau@inventel.fr>");
++ BT_INFO("Written 2001,2002 by David Libault <david.libault@inventel.fr>");
++ BT_INFO("Copyright (C) 2002 Maxim Krasnyanskiy <maxk@qualcomm.com>");
++
++ return 0;
++}
++
++static void __exit bnep_cleanup_module(void)
++{
++ bnep_sock_cleanup();
++ bnep_crc32_cleanup();
++}
++
++module_init(bnep_init_module);
++module_exit(bnep_cleanup_module);
++
++MODULE_DESCRIPTION("BlueZ BNEP ver " VERSION);
++MODULE_AUTHOR("David Libault <david.libault@inventel.fr>, Maxim Krasnyanskiy <maxk@qualcomm.com>");
++MODULE_LICENSE("GPL");
+diff -urN linux-2.4.18/net/bluetooth/bnep/crc32.c linux-2.4.18-mh15/net/bluetooth/bnep/crc32.c
+--- linux-2.4.18/net/bluetooth/bnep/crc32.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/net/bluetooth/bnep/crc32.c 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,59 @@
++/*
++ * Based on linux-2.5/lib/crc32 by Matt Domsch <Matt_Domsch@dell.com>
++ *
++ * FIXME: Remove in 2.5
++ */
++
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/slab.h>
++#include <linux/init.h>
++#include <asm/atomic.h>
++
++#include "crc32.h"
++
++#define CRCPOLY_BE 0x04c11db7
++#define CRC_BE_BITS 8
++
++static u32 *bnep_crc32_table;
++
++/*
++ * This code is in the public domain; copyright abandoned.
++ * Liability for non-performance of this code is limited to the amount
++ * you paid for it. Since it is distributed for free, your refund will
++ * be very very small. If it breaks, you get to keep both pieces.
++ */
++u32 bnep_crc32(u32 crc, unsigned char const *p, size_t len)
++{
++ while (len--)
++ crc = (crc << 8) ^ bnep_crc32_table[(crc >> 24) ^ *p++];
++
++ return crc;
++}
++
++int __init bnep_crc32_init(void)
++{
++ unsigned i, j;
++ u32 crc = 0x80000000;
++
++ bnep_crc32_table = kmalloc((1 << CRC_BE_BITS) * sizeof(u32), GFP_KERNEL);
++ if (!bnep_crc32_table)
++ return -ENOMEM;
++
++ bnep_crc32_table[0] = 0;
++
++ for (i = 1; i < 1 << CRC_BE_BITS; i <<= 1) {
++ crc = (crc << 1) ^ ((crc & 0x80000000) ? CRCPOLY_BE : 0);
++ for (j = 0; j < i; j++)
++ bnep_crc32_table[i + j] = crc ^ bnep_crc32_table[j];
++ }
++ return 0;
++}
++
++void __exit bnep_crc32_cleanup(void)
++{
++ if (bnep_crc32_table)
++ kfree(bnep_crc32_table);
++ bnep_crc32_table = NULL;
++}
+diff -urN linux-2.4.18/net/bluetooth/bnep/crc32.h linux-2.4.18-mh15/net/bluetooth/bnep/crc32.h
+--- linux-2.4.18/net/bluetooth/bnep/crc32.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/net/bluetooth/bnep/crc32.h 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,10 @@
++/*
++ * crc32.h
++ * See crc32.c for license and changes
++ *
++ * FIXME: Remove in 2.5
++ */
++
++int bnep_crc32_init(void);
++void bnep_crc32_cleanup(void);
++u32 bnep_crc32(u32 crc, unsigned char const *p, size_t len);
+diff -urN linux-2.4.18/net/bluetooth/bnep/Makefile linux-2.4.18-mh15/net/bluetooth/bnep/Makefile
+--- linux-2.4.18/net/bluetooth/bnep/Makefile 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/net/bluetooth/bnep/Makefile 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,10 @@
++#
++# Makefile for the Linux Bluetooth BNEP layer
++#
++
++O_TARGET := bnep.o
++
++obj-y := core.o sock.o netdev.o crc32.o
++obj-m += $(O_TARGET)
++
++include $(TOPDIR)/Rules.make
+diff -urN linux-2.4.18/net/bluetooth/bnep/netdev.c linux-2.4.18-mh15/net/bluetooth/bnep/netdev.c
+--- linux-2.4.18/net/bluetooth/bnep/netdev.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/net/bluetooth/bnep/netdev.c 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,254 @@
++/*
++ BNEP implementation for Linux Bluetooth stack (BlueZ).
++ Copyright (C) 2001-2002 Inventel Systemes
++ Written 2001-2002 by
++ Clément Moreau <clement.moreau@inventel.fr>
++ David Libault <david.libault@inventel.fr>
++
++ Copyright (C) 2002 Maxim Krasnyanskiy <maxk@qualcomm.com>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License version 2 as
++ published by the Free Software Foundation;
++
++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++
++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
++ SOFTWARE IS DISCLAIMED.
++*/
++
++/*
++ * $Id$
++ */
++
++#include <linux/config.h>
++#include <linux/module.h>
++
++#include <linux/socket.h>
++#include <linux/netdevice.h>
++#include <linux/etherdevice.h>
++#include <linux/skbuff.h>
++#include <linux/wait.h>
++
++#include <asm/unaligned.h>
++
++#include <net/bluetooth/bluetooth.h>
++#include <net/bluetooth/hci_core.h>
++#include <net/bluetooth/l2cap.h>
++
++#include "bnep.h"
++
++#ifndef CONFIG_BLUEZ_BNEP_DEBUG
++#undef BT_DBG
++#define BT_DBG( A... )
++#endif
++
++#define BNEP_TX_QUEUE_LEN 20
++
++static int bnep_net_open(struct net_device *dev)
++{
++ netif_start_queue(dev);
++ return 0;
++}
++
++static int bnep_net_close(struct net_device *dev)
++{
++ netif_stop_queue(dev);
++ return 0;
++}
++
++static struct net_device_stats *bnep_net_get_stats(struct net_device *dev)
++{
++ struct bnep_session *s = dev->priv;
++ return &s->stats;
++}
++
++static void bnep_net_set_mc_list(struct net_device *dev)
++{
++#ifdef CONFIG_BLUEZ_BNEP_MC_FILTER
++ struct bnep_session *s = dev->priv;
++ struct sock *sk = s->sock->sk;
++ struct bnep_set_filter_req *r;
++ struct sk_buff *skb;
++ int size;
++
++ BT_DBG("%s mc_count %d", dev->name, dev->mc_count);
++
++ size = sizeof(*r) + (BNEP_MAX_MULTICAST_FILTERS + 1) * ETH_ALEN * 2;
++ skb = alloc_skb(size, GFP_ATOMIC);
++ if (!skb) {
++ BT_ERR("%s Multicast list allocation failed", dev->name);
++ return;
++ }
++
++ r = (void *) skb->data;
++ __skb_put(skb, sizeof(*r));
++
++ r->type = BNEP_CONTROL;
++ r->ctrl = BNEP_FILTER_MULTI_ADDR_SET;
++
++ if (dev->flags & (IFF_PROMISC | IFF_ALLMULTI)) {
++ u8 start[ETH_ALEN] = { 0x01 };
++
++ /* Request all addresses */
++ memcpy(__skb_put(skb, ETH_ALEN), start, ETH_ALEN);
++ memcpy(__skb_put(skb, ETH_ALEN), dev->broadcast, ETH_ALEN);
++ r->len = htons(ETH_ALEN * 2);
++ } else {
++ struct dev_mc_list *dmi = dev->mc_list;
++ int i, len = skb->len;
++
++ if (dev->flags & IFF_BROADCAST) {
++ memcpy(__skb_put(skb, ETH_ALEN), dev->broadcast, ETH_ALEN);
++ memcpy(__skb_put(skb, ETH_ALEN), dev->broadcast, ETH_ALEN);
++ }
++
++ /* FIXME: We should group addresses here. */
++
++ for (i = 0; i < dev->mc_count && i < BNEP_MAX_MULTICAST_FILTERS; i++) {
++ memcpy(__skb_put(skb, ETH_ALEN), dmi->dmi_addr, ETH_ALEN);
++ memcpy(__skb_put(skb, ETH_ALEN), dmi->dmi_addr, ETH_ALEN);
++ dmi = dmi->next;
++ }
++ r->len = htons(skb->len - len);
++ }
++
++ skb_queue_tail(&sk->write_queue, skb);
++ wake_up_interruptible(sk->sleep);
++#endif
++}
++
++static int bnep_net_set_mac_addr(struct net_device *dev, void *arg)
++{
++ BT_DBG("%s", dev->name);
++ return 0;
++}
++
++static void bnep_net_timeout(struct net_device *dev)
++{
++ BT_DBG("net_timeout");
++ netif_wake_queue(dev);
++}
++
++static int bnep_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
++{
++ return -EINVAL;
++}
++
++#ifdef CONFIG_BLUEZ_BNEP_MC_FILTER
++static inline int bnep_net_mc_filter(struct sk_buff *skb, struct bnep_session *s)
++{
++ struct ethhdr *eh = (void *) skb->data;
++
++ if ((eh->h_dest[0] & 1) && !test_bit(bnep_mc_hash(eh->h_dest), &s->mc_filter)) {
++ BT_DBG("BNEP: filtered skb %p, dst %.2x:%.2x:%.2x:%.2x:%.2x:%.2x", skb,
++ eh->h_dest[0], eh->h_dest[1], eh->h_dest[2],
++ eh->h_dest[3], eh->h_dest[4], eh->h_dest[5]);
++ return 1;
++ }
++ return 0;
++}
++#endif
++
++#ifdef CONFIG_BLUEZ_BNEP_PROTO_FILTER
++/* Determine ether protocol. Based on eth_type_trans. */
++static inline u16 bnep_net_eth_proto(struct sk_buff *skb)
++{
++ struct ethhdr *eh = (void *) skb->data;
++
++ if (ntohs(eh->h_proto) >= 1536)
++ return eh->h_proto;
++
++ if (get_unaligned((u16 *) skb->data) == 0xFFFF)
++ return htons(ETH_P_802_3);
++
++ return htons(ETH_P_802_2);
++}
++
++static inline int bnep_net_proto_filter(struct sk_buff *skb, struct bnep_session *s)
++{
++ u16 proto = bnep_net_eth_proto(skb);
++ struct bnep_proto_filter *f = s->proto_filter;
++ int i;
++
++ for (i = 0; i < BNEP_MAX_PROTO_FILTERS && f[i].end; i++) {
++ if (proto >= f[i].start && proto <= f[i].end)
++ return 0;
++ }
++
++ BT_DBG("BNEP: filtered skb %p, proto 0x%.4x", skb, proto);
++ return 1;
++}
++#endif
++
++static int bnep_net_xmit(struct sk_buff *skb, struct net_device *dev)
++{
++ struct bnep_session *s = dev->priv;
++ struct sock *sk = s->sock->sk;
++
++ BT_DBG("skb %p, dev %p", skb, dev);
++
++#ifdef CONFIG_BLUEZ_BNEP_MC_FILTER
++ if (bnep_net_mc_filter(skb, s)) {
++ kfree_skb(skb);
++ return 0;
++ }
++#endif
++
++#ifdef CONFIG_BLUEZ_BNEP_PROTO_FILTER
++ if (bnep_net_proto_filter(skb, s)) {
++ kfree_skb(skb);
++ return 0;
++ }
++#endif
++
++ /*
++ * We cannot send L2CAP packets from here as we are potentially in a bh.
++ * So we have to queue them and wake up session thread which is sleeping
++ * on the sk->sleep.
++ */
++ dev->trans_start = jiffies;
++ skb_queue_tail(&sk->write_queue, skb);
++ wake_up_interruptible(sk->sleep);
++
++ if (skb_queue_len(&sk->write_queue) >= BNEP_TX_QUEUE_LEN) {
++ BT_DBG("tx queue is full");
++
++ /* Stop queuing.
++ * Session thread will do netif_wake_queue() */
++ netif_stop_queue(dev);
++ }
++
++ return 0;
++}
++
++int bnep_net_init(struct net_device *dev)
++{
++ struct bnep_session *s = dev->priv;
++
++ memcpy(dev->dev_addr, s->eh.h_dest, ETH_ALEN);
++ dev->addr_len = ETH_ALEN;
++
++ ether_setup(dev);
++
++ dev->open = bnep_net_open;
++ dev->stop = bnep_net_close;
++ dev->hard_start_xmit = bnep_net_xmit;
++ dev->get_stats = bnep_net_get_stats;
++ dev->do_ioctl = bnep_net_ioctl;
++ dev->set_mac_address = bnep_net_set_mac_addr;
++ dev->set_multicast_list = bnep_net_set_mc_list;
++
++ dev->watchdog_timeo = HZ * 2;
++ dev->tx_timeout = bnep_net_timeout;
++
++ return 0;
++}
+diff -urN linux-2.4.18/net/bluetooth/bnep/sock.c linux-2.4.18-mh15/net/bluetooth/bnep/sock.c
+--- linux-2.4.18/net/bluetooth/bnep/sock.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/net/bluetooth/bnep/sock.c 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,210 @@
++/*
++ BNEP implementation for Linux Bluetooth stack (BlueZ).
++ Copyright (C) 2001-2002 Inventel Systemes
++ Written 2001-2002 by
++ David Libault <david.libault@inventel.fr>
++
++ Copyright (C) 2002 Maxim Krasnyanskiy <maxk@qualcomm.com>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License version 2 as
++ published by the Free Software Foundation;
++
++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++
++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
++ SOFTWARE IS DISCLAIMED.
++*/
++
++/*
++ * $Id$
++ */
++
++#include <linux/config.h>
++#include <linux/module.h>
++
++#include <linux/types.h>
++#include <linux/errno.h>
++#include <linux/kernel.h>
++#include <linux/major.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/poll.h>
++#include <linux/fcntl.h>
++#include <linux/skbuff.h>
++#include <linux/socket.h>
++#include <linux/ioctl.h>
++#include <linux/file.h>
++#include <net/sock.h>
++
++#include <asm/system.h>
++#include <asm/uaccess.h>
++
++#include "bnep.h"
++
++#ifndef CONFIG_BLUEZ_BNEP_DEBUG
++#undef BT_DBG
++#define BT_DBG( A... )
++#endif
++
++static int bnep_sock_release(struct socket *sock)
++{
++ struct sock *sk = sock->sk;
++
++ BT_DBG("sock %p sk %p", sock, sk);
++
++ if (!sk)
++ return 0;
++
++ sock_orphan(sk);
++ sock_put(sk);
++
++ MOD_DEC_USE_COUNT;
++ return 0;
++}
++
++static int bnep_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
++{
++ struct bnep_connlist_req cl;
++ struct bnep_connadd_req ca;
++ struct bnep_conndel_req cd;
++ struct bnep_conninfo ci;
++ struct socket *nsock;
++ int err;
++
++ BT_DBG("cmd %x arg %lx", cmd, arg);
++
++ switch (cmd) {
++ case BNEPCONNADD:
++ if (!capable(CAP_NET_ADMIN))
++ return -EACCES;
++
++ if (copy_from_user(&ca, (void *) arg, sizeof(ca)))
++ return -EFAULT;
++
++ nsock = sockfd_lookup(ca.sock, &err);
++ if (!nsock)
++ return err;
++
++ if (nsock->sk->state != BT_CONNECTED) {
++ fput(nsock->file);
++ return -EBADFD;
++ }
++
++ err = bnep_add_connection(&ca, nsock);
++ if (!err) {
++ if (copy_to_user((void *) arg, &ca, sizeof(ca)))
++ err = -EFAULT;
++ } else
++ fput(nsock->file);
++
++ return err;
++
++ case BNEPCONNDEL:
++ if (!capable(CAP_NET_ADMIN))
++ return -EACCES;
++
++ if (copy_from_user(&cd, (void *) arg, sizeof(cd)))
++ return -EFAULT;
++
++ return bnep_del_connection(&cd);
++
++ case BNEPGETCONNLIST:
++ if (copy_from_user(&cl, (void *) arg, sizeof(cl)))
++ return -EFAULT;
++
++ if (cl.cnum <= 0)
++ return -EINVAL;
++
++ err = bnep_get_connlist(&cl);
++ if (!err && copy_to_user((void *) arg, &cl, sizeof(cl)))
++ return -EFAULT;
++
++ return err;
++
++ case BNEPGETCONNINFO:
++ if (copy_from_user(&ci, (void *) arg, sizeof(ci)))
++ return -EFAULT;
++
++ err = bnep_get_conninfo(&ci);
++ if (!err && copy_to_user((void *) arg, &ci, sizeof(ci)))
++ return -EFAULT;
++
++ return err;
++
++ default:
++ return -EINVAL;
++ }
++
++ return 0;
++}
++
++static struct proto_ops bnep_sock_ops = {
++ family: PF_BLUETOOTH,
++ release: bnep_sock_release,
++ ioctl: bnep_sock_ioctl,
++ bind: sock_no_bind,
++ getname: sock_no_getname,
++ sendmsg: sock_no_sendmsg,
++ recvmsg: sock_no_recvmsg,
++ poll: sock_no_poll,
++ listen: sock_no_listen,
++ shutdown: sock_no_shutdown,
++ setsockopt: sock_no_setsockopt,
++ getsockopt: sock_no_getsockopt,
++ connect: sock_no_connect,
++ socketpair: sock_no_socketpair,
++ accept: sock_no_accept,
++ mmap: sock_no_mmap
++};
++
++static int bnep_sock_create(struct socket *sock, int protocol)
++{
++ struct sock *sk;
++
++ BT_DBG("sock %p", sock);
++
++ if (sock->type != SOCK_RAW)
++ return -ESOCKTNOSUPPORT;
++
++ sock->ops = &bnep_sock_ops;
++
++ if (!(sk = sk_alloc(PF_BLUETOOTH, GFP_KERNEL, 1)))
++ return -ENOMEM;
++
++ MOD_INC_USE_COUNT;
++
++ sock->state = SS_UNCONNECTED;
++ sock_init_data(sock, sk);
++
++ sk->destruct = NULL;
++ sk->protocol = protocol;
++
++ return 0;
++}
++
++static struct net_proto_family bnep_sock_family_ops = {
++ family: PF_BLUETOOTH,
++ create: bnep_sock_create
++};
++
++int bnep_sock_init(void)
++{
++ bluez_sock_register(BTPROTO_BNEP, &bnep_sock_family_ops);
++ return 0;
++}
++
++int bnep_sock_cleanup(void)
++{
++ if (bluez_sock_unregister(BTPROTO_BNEP))
++ BT_ERR("Can't unregister BNEP socket");
++ return 0;
++}
+diff -urN linux-2.4.18/net/bluetooth/cmtp/capi.c linux-2.4.18-mh15/net/bluetooth/cmtp/capi.c
+--- linux-2.4.18/net/bluetooth/cmtp/capi.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/net/bluetooth/cmtp/capi.c 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,707 @@
++/*
++ CMTP implementation for Linux Bluetooth stack (BlueZ).
++ Copyright (C) 2002-2003 Marcel Holtmann <marcel@holtmann.org>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License version 2 as
++ published by the Free Software Foundation;
++
++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++
++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
++ SOFTWARE IS DISCLAIMED.
++*/
++
++#include <linux/config.h>
++#include <linux/module.h>
++
++#include <linux/types.h>
++#include <linux/errno.h>
++#include <linux/kernel.h>
++#include <linux/major.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/poll.h>
++#include <linux/fcntl.h>
++#include <linux/skbuff.h>
++#include <linux/socket.h>
++#include <linux/ioctl.h>
++#include <linux/file.h>
++#include <net/sock.h>
++
++#include <linux/capi.h>
++
++#include "../drivers/isdn/avmb1/capilli.h"
++#include "../drivers/isdn/avmb1/capicmd.h"
++#include "../drivers/isdn/avmb1/capiutil.h"
++
++#include "cmtp.h"
++
++#ifndef CONFIG_BLUEZ_CMTP_DEBUG
++#undef BT_DBG
++#define BT_DBG(D...)
++#endif
++
++#define REVISION "1.0"
++
++#define CAPI_INTEROPERABILITY 0x20
++
++#define CAPI_INTEROPERABILITY_REQ CAPICMD(CAPI_INTEROPERABILITY, CAPI_REQ)
++#define CAPI_INTEROPERABILITY_CONF CAPICMD(CAPI_INTEROPERABILITY, CAPI_CONF)
++#define CAPI_INTEROPERABILITY_IND CAPICMD(CAPI_INTEROPERABILITY, CAPI_IND)
++#define CAPI_INTEROPERABILITY_RESP CAPICMD(CAPI_INTEROPERABILITY, CAPI_RESP)
++
++#define CAPI_INTEROPERABILITY_REQ_LEN (CAPI_MSG_BASELEN + 2)
++#define CAPI_INTEROPERABILITY_CONF_LEN (CAPI_MSG_BASELEN + 4)
++#define CAPI_INTEROPERABILITY_IND_LEN (CAPI_MSG_BASELEN + 2)
++#define CAPI_INTEROPERABILITY_RESP_LEN (CAPI_MSG_BASELEN + 2)
++
++#define CAPI_FUNCTION_REGISTER 0
++#define CAPI_FUNCTION_RELEASE 1
++#define CAPI_FUNCTION_GET_PROFILE 2
++#define CAPI_FUNCTION_GET_MANUFACTURER 3
++#define CAPI_FUNCTION_GET_VERSION 4
++#define CAPI_FUNCTION_GET_SERIAL_NUMBER 5
++#define CAPI_FUNCTION_MANUFACTURER 6
++#define CAPI_FUNCTION_LOOPBACK 7
++
++static struct capi_driver_interface *di;
++
++
++#define CMTP_MSGNUM 1
++#define CMTP_APPLID 2
++#define CMTP_MAPPING 3
++
++static struct cmtp_application *cmtp_application_add(struct cmtp_session *session, __u16 appl)
++{
++ struct cmtp_application *app = kmalloc(sizeof(*app), GFP_KERNEL);
++
++ BT_DBG("session %p application %p appl %d", session, app, appl);
++
++ if (!app)
++ return NULL;
++
++ memset(app, 0, sizeof(*app));
++
++ app->state = BT_OPEN;
++ app->appl = appl;
++
++ list_add_tail(&app->list, &session->applications);
++
++ return app;
++}
++
++static void cmtp_application_del(struct cmtp_session *session, struct cmtp_application *app)
++{
++ BT_DBG("session %p application %p", session, app);
++
++ if (app) {
++ list_del(&app->list);
++ kfree(app);
++ }
++}
++
++static struct cmtp_application *cmtp_application_get(struct cmtp_session *session, int pattern, __u16 value)
++{
++ struct cmtp_application *app;
++ struct list_head *p, *n;
++
++ list_for_each_safe(p, n, &session->applications) {
++ app = list_entry(p, struct cmtp_application, list);
++ switch (pattern) {
++ case CMTP_MSGNUM:
++ if (app->msgnum == value)
++ return app;
++ break;
++ case CMTP_APPLID:
++ if (app->appl == value)
++ return app;
++ break;
++ case CMTP_MAPPING:
++ if (app->mapping == value)
++ return app;
++ break;
++ }
++ }
++
++ return NULL;
++}
++
++static int cmtp_msgnum_get(struct cmtp_session *session)
++{
++ session->msgnum++;
++
++ if ((session->msgnum & 0xff) > 200)
++ session->msgnum = CMTP_INITIAL_MSGNUM + 1;
++
++ return session->msgnum;
++}
++
++
++static void cmtp_send_interopmsg(struct cmtp_session *session,
++ __u8 subcmd, __u16 appl, __u16 msgnum,
++ __u16 function, unsigned char *buf, int len)
++{
++ struct sk_buff *skb;
++ unsigned char *s;
++
++ BT_DBG("session %p subcmd 0x%02x appl %d msgnum %d", session, subcmd, appl, msgnum);
++
++ if (!(skb = alloc_skb(CAPI_MSG_BASELEN + 6 + len, GFP_ATOMIC))) {
++ BT_ERR("Can't allocate memory for interoperability packet");
++ return;
++ }
++
++ s = skb_put(skb, CAPI_MSG_BASELEN + 6 + len);
++
++ capimsg_setu16(s, 0, CAPI_MSG_BASELEN + 6 + len);
++ capimsg_setu16(s, 2, appl);
++ capimsg_setu8 (s, 4, CAPI_INTEROPERABILITY);
++ capimsg_setu8 (s, 5, subcmd);
++ capimsg_setu16(s, 6, msgnum);
++
++ /* Interoperability selector (Bluetooth Device Management) */
++ capimsg_setu16(s, 8, 0x0001);
++
++ capimsg_setu8 (s, 10, 3 + len);
++ capimsg_setu16(s, 11, function);
++ capimsg_setu8 (s, 13, len);
++
++ if (len > 0)
++ memcpy(s + 14, buf, len);
++
++ cmtp_send_capimsg(session, skb);
++}
++
++static void cmtp_recv_interopmsg(struct cmtp_session *session, struct sk_buff *skb)
++{
++ struct capi_ctr *ctrl = session->ctrl;
++ struct cmtp_application *application;
++ __u16 appl, msgnum, func, info;
++ __u32 controller;
++
++ BT_DBG("session %p skb %p len %d", session, skb, skb->len);
++
++ switch (CAPIMSG_SUBCOMMAND(skb->data)) {
++ case CAPI_CONF:
++ func = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 5);
++ info = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 8);
++
++ switch (func) {
++ case CAPI_FUNCTION_REGISTER:
++ msgnum = CAPIMSG_MSGID(skb->data);
++
++ application = cmtp_application_get(session, CMTP_MSGNUM, msgnum);
++ if (application) {
++ application->state = BT_CONNECTED;
++ application->msgnum = 0;
++ application->mapping = CAPIMSG_APPID(skb->data);
++ wake_up_interruptible(&session->wait);
++ }
++
++ break;
++
++ case CAPI_FUNCTION_RELEASE:
++ appl = CAPIMSG_APPID(skb->data);
++
++ application = cmtp_application_get(session, CMTP_MAPPING, appl);
++ if (application) {
++ application->state = BT_CLOSED;
++ application->msgnum = 0;
++ wake_up_interruptible(&session->wait);
++ }
++
++ break;
++
++ case CAPI_FUNCTION_GET_PROFILE:
++ controller = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 11);
++ msgnum = CAPIMSG_MSGID(skb->data);
++
++ if (!info && (msgnum == CMTP_INITIAL_MSGNUM)) {
++ session->ncontroller = controller;
++ wake_up_interruptible(&session->wait);
++ break;
++ }
++
++ if (!info && ctrl) {
++ memcpy(&ctrl->profile,
++ skb->data + CAPI_MSG_BASELEN + 11,
++ sizeof(capi_profile));
++ session->state = BT_CONNECTED;
++ ctrl->ready(ctrl);
++ }
++
++ break;
++
++ case CAPI_FUNCTION_GET_MANUFACTURER:
++ controller = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 10);
++
++ if (!info && ctrl) {
++ strncpy(ctrl->manu,
++ skb->data + CAPI_MSG_BASELEN + 15,
++ skb->data[CAPI_MSG_BASELEN + 14]);
++ }
++
++ break;
++
++ case CAPI_FUNCTION_GET_VERSION:
++ controller = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 12);
++
++ if (!info && ctrl) {
++ ctrl->version.majorversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 16);
++ ctrl->version.minorversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 20);
++ ctrl->version.majormanuversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 24);
++ ctrl->version.minormanuversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 28);
++ }
++
++ break;
++
++ case CAPI_FUNCTION_GET_SERIAL_NUMBER:
++ controller = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 12);
++
++ if (!info && ctrl) {
++ memset(ctrl->serial, 0, CAPI_SERIAL_LEN);
++ strncpy(ctrl->serial,
++ skb->data + CAPI_MSG_BASELEN + 17,
++ skb->data[CAPI_MSG_BASELEN + 16]);
++ }
++
++ break;
++ }
++
++ break;
++
++ case CAPI_IND:
++ func = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 3);
++
++ if (func == CAPI_FUNCTION_LOOPBACK) {
++ appl = CAPIMSG_APPID(skb->data);
++ msgnum = CAPIMSG_MSGID(skb->data);
++ cmtp_send_interopmsg(session, CAPI_RESP, appl, msgnum, func,
++ skb->data + CAPI_MSG_BASELEN + 6,
++ skb->data[CAPI_MSG_BASELEN + 5]);
++ }
++
++ break;
++ }
++
++ kfree_skb(skb);
++}
++
++void cmtp_recv_capimsg(struct cmtp_session *session, struct sk_buff *skb)
++{
++ struct capi_ctr *ctrl = session->ctrl;
++ struct cmtp_application *application;
++ __u16 cmd, appl, info;
++ __u32 ncci, contr;
++
++ BT_DBG("session %p skb %p len %d", session, skb, skb->len);
++
++ if (CAPIMSG_COMMAND(skb->data) == CAPI_INTEROPERABILITY) {
++ cmtp_recv_interopmsg(session, skb);
++ return;
++ }
++
++ if (session->flags & (1 << CMTP_LOOPBACK)) {
++ kfree_skb(skb);
++ return;
++ }
++
++ cmd = CAPICMD(CAPIMSG_COMMAND(skb->data), CAPIMSG_SUBCOMMAND(skb->data));
++ appl = CAPIMSG_APPID(skb->data);
++ contr = CAPIMSG_CONTROL(skb->data);
++
++ application = cmtp_application_get(session, CMTP_MAPPING, appl);
++ if (application) {
++ appl = application->appl;
++ CAPIMSG_SETAPPID(skb->data, appl);
++ } else {
++ BT_ERR("Can't find application with id %d", appl);
++ kfree_skb(skb);
++ return;
++ }
++
++ if ((contr & 0x7f) == 0x01) {
++ contr = (contr & 0xffffff80) | session->num;
++ CAPIMSG_SETCONTROL(skb->data, contr);
++ }
++
++ if (!ctrl) {
++ BT_ERR("Can't find controller %d for message", session->num);
++ kfree_skb(skb);
++ return;
++ }
++
++ switch (cmd) {
++ case CAPI_CONNECT_B3_CONF:
++ ncci = CAPIMSG_NCCI(skb->data);
++ info = CAPIMSG_U16(skb->data, 12);
++
++ BT_DBG("CONNECT_B3_CONF ncci 0x%02x info 0x%02x", ncci, info);
++
++ if (info == 0)
++ ctrl->new_ncci(ctrl, appl, ncci, 8);
++
++ ctrl->handle_capimsg(ctrl, appl, skb);
++ break;
++
++ case CAPI_CONNECT_B3_IND:
++ ncci = CAPIMSG_NCCI(skb->data);
++
++ BT_DBG("CONNECT_B3_IND ncci 0x%02x", ncci);
++
++ ctrl->new_ncci(ctrl, appl, ncci, 8);
++ ctrl->handle_capimsg(ctrl, appl, skb);
++ break;
++
++ case CAPI_DISCONNECT_B3_IND:
++ ncci = CAPIMSG_NCCI(skb->data);
++
++ BT_DBG("DISCONNECT_B3_IND ncci 0x%02x", ncci);
++
++ if (ncci == 0xffffffff)
++ BT_ERR("DISCONNECT_B3_IND with ncci 0xffffffff");
++
++ ctrl->handle_capimsg(ctrl, appl, skb);
++ ctrl->free_ncci(ctrl, appl, ncci);
++ break;
++
++ default:
++ ctrl->handle_capimsg(ctrl, appl, skb);
++ break;
++ }
++}
++
++void cmtp_send_capimsg(struct cmtp_session *session, struct sk_buff *skb)
++{
++ struct cmtp_scb *scb = (void *) skb->cb;
++
++ BT_DBG("session %p skb %p len %d", session, skb, skb->len);
++
++ scb->id = -1;
++ scb->data = (CAPIMSG_COMMAND(skb->data) == CAPI_DATA_B3);
++
++ skb_queue_tail(&session->transmit, skb);
++
++ cmtp_schedule(session);
++}
++
++
++static int cmtp_load_firmware(struct capi_ctr *ctrl, capiloaddata *data)
++{
++ BT_DBG("ctrl %p data %p", ctrl, data);
++
++ return -EIO;
++}
++
++static void cmtp_reset_ctr(struct capi_ctr *ctrl)
++{
++ BT_DBG("ctrl %p", ctrl);
++
++ ctrl->reseted(ctrl);
++}
++
++static void cmtp_remove_ctr(struct capi_ctr *ctrl)
++{
++ struct cmtp_session *session = ctrl->driverdata;
++
++ BT_DBG("ctrl %p", ctrl);
++
++ ctrl->suspend_output(ctrl);
++
++ atomic_inc(&session->terminate);
++ cmtp_schedule(session);
++}
++
++static void cmtp_register_appl(struct capi_ctr *ctrl, __u16 appl, capi_register_params *rp)
++{
++ DECLARE_WAITQUEUE(wait, current);
++ struct cmtp_session *session = ctrl->driverdata;
++ struct cmtp_application *application;
++ unsigned long timeo = CMTP_INTEROP_TIMEOUT;
++ unsigned char buf[8];
++ int err = 0, nconn, want = rp->level3cnt;
++
++ BT_DBG("ctrl %p appl %d level3cnt %d datablkcnt %d datablklen %d",
++ ctrl, appl, rp->level3cnt, rp->datablkcnt, rp->datablklen);
++
++ application = cmtp_application_add(session, appl);
++ if (!application) {
++ BT_ERR("Can't allocate memory for new application");
++ ctrl->appl_released(ctrl, appl);
++ return;
++ }
++
++ if (want < 0)
++ nconn = ctrl->profile.nbchannel * -want;
++ else
++ nconn = want;
++
++ if (nconn == 0)
++ nconn = ctrl->profile.nbchannel;
++
++ capimsg_setu16(buf, 0, nconn);
++ capimsg_setu16(buf, 2, rp->datablkcnt);
++ capimsg_setu16(buf, 4, rp->datablklen);
++
++ application->state = BT_CONFIG;
++ application->msgnum = cmtp_msgnum_get(session);
++
++ cmtp_send_interopmsg(session, CAPI_REQ, 0x0000, application->msgnum,
++ CAPI_FUNCTION_REGISTER, buf, 6);
++
++ add_wait_queue(&session->wait, &wait);
++ while (1) {
++ set_current_state(TASK_INTERRUPTIBLE);
++
++ if (!timeo) {
++ err = -EAGAIN;
++ break;
++ }
++
++ if (application->state == BT_CLOSED) {
++ err = -application->err;
++ break;
++ }
++
++ if (application->state == BT_CONNECTED)
++ break;
++
++ if (signal_pending(current)) {
++ err = -EINTR;
++ break;
++ }
++
++ timeo = schedule_timeout(timeo);
++ }
++ set_current_state(TASK_RUNNING);
++ remove_wait_queue(&session->wait, &wait);
++
++ if (err) {
++ ctrl->appl_released(ctrl, appl);
++ cmtp_application_del(session, application);
++ return;
++ }
++
++ ctrl->appl_registered(ctrl, appl);
++}
++
++static void cmtp_release_appl(struct capi_ctr *ctrl, __u16 appl)
++{
++ DECLARE_WAITQUEUE(wait, current);
++ struct cmtp_session *session = ctrl->driverdata;
++ struct cmtp_application *application;
++ unsigned long timeo = CMTP_INTEROP_TIMEOUT;
++
++ BT_DBG("ctrl %p appl %d", ctrl, appl);
++
++ application = cmtp_application_get(session, CMTP_APPLID, appl);
++ if (!application) {
++ BT_ERR("Can't find application");
++ return;
++ }
++
++ application->msgnum = cmtp_msgnum_get(session);
++
++ cmtp_send_interopmsg(session, CAPI_REQ, application->mapping, application->msgnum,
++ CAPI_FUNCTION_RELEASE, NULL, 0);
++
++ add_wait_queue(&session->wait, &wait);
++ while (timeo) {
++ set_current_state(TASK_INTERRUPTIBLE);
++
++ if (application->state == BT_CLOSED)
++ break;
++
++ if (signal_pending(current))
++ break;
++
++ timeo = schedule_timeout(timeo);
++ }
++ set_current_state(TASK_RUNNING);
++ remove_wait_queue(&session->wait, &wait);
++
++ cmtp_application_del(session, application);
++ ctrl->appl_released(ctrl, appl);
++}
++
++static void cmtp_send_message(struct capi_ctr *ctrl, struct sk_buff *skb)
++{
++ struct cmtp_session *session = ctrl->driverdata;
++ struct cmtp_application *application;
++ __u16 appl;
++ __u32 contr;
++
++ BT_DBG("ctrl %p skb %p", ctrl, skb);
++
++ appl = CAPIMSG_APPID(skb->data);
++ contr = CAPIMSG_CONTROL(skb->data);
++
++ application = cmtp_application_get(session, CMTP_APPLID, appl);
++ if ((!application) || (application->state != BT_CONNECTED)) {
++ BT_ERR("Can't find application with id %d", appl);
++ kfree_skb(skb);
++ return;
++ }
++
++ CAPIMSG_SETAPPID(skb->data, application->mapping);
++
++ if ((contr & 0x7f) == session->num) {
++ contr = (contr & 0xffffff80) | 0x01;
++ CAPIMSG_SETCONTROL(skb->data, contr);
++ }
++
++ cmtp_send_capimsg(session, skb);
++}
++
++static char *cmtp_procinfo(struct capi_ctr *ctrl)
++{
++ return "CAPI Message Transport Protocol";
++}
++
++static int cmtp_ctr_read_proc(char *page, char **start, off_t off, int count, int *eof, struct capi_ctr *ctrl)
++{
++ struct cmtp_session *session = ctrl->driverdata;
++ struct cmtp_application *app;
++ struct list_head *p, *n;
++ int len = 0;
++
++ len += sprintf(page + len, "%s (Revision %s)\n\n", cmtp_procinfo(ctrl), REVISION);
++ len += sprintf(page + len, "addr %s\n", session->name);
++ len += sprintf(page + len, "ctrl %d\n", session->num);
++
++ list_for_each_safe(p, n, &session->applications) {
++ app = list_entry(p, struct cmtp_application, list);
++ len += sprintf(page + len, "appl %d -> %d\n", app->appl, app->mapping);
++ }
++
++ if (off + count >= len)
++ *eof = 1;
++
++ if (len < off)
++ return 0;
++
++ *start = page + off;
++
++ return ((count < len - off) ? count : len - off);
++}
++
++static struct capi_driver cmtp_driver = {
++ name: "cmtp",
++ revision: REVISION,
++ load_firmware: cmtp_load_firmware,
++ reset_ctr: cmtp_reset_ctr,
++ remove_ctr: cmtp_remove_ctr,
++ register_appl: cmtp_register_appl,
++ release_appl: cmtp_release_appl,
++ send_message: cmtp_send_message,
++ procinfo: cmtp_procinfo,
++ ctr_read_proc: cmtp_ctr_read_proc,
++
++ driver_read_proc: 0,
++ add_card: 0,
++};
++
++
++int cmtp_attach_device(struct cmtp_session *session)
++{
++ DECLARE_WAITQUEUE(wait, current);
++ unsigned long timeo = CMTP_INTEROP_TIMEOUT;
++ unsigned char buf[4];
++
++ BT_DBG("session %p", session);
++
++ capimsg_setu32(buf, 0, 0);
++
++ cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, CMTP_INITIAL_MSGNUM,
++ CAPI_FUNCTION_GET_PROFILE, buf, 4);
++
++ add_wait_queue(&session->wait, &wait);
++ while (timeo) {
++ set_current_state(TASK_INTERRUPTIBLE);
++
++ if (session->ncontroller)
++ break;
++
++ if (signal_pending(current))
++ break;
++
++ timeo = schedule_timeout(timeo);
++ }
++ set_current_state(TASK_RUNNING);
++ remove_wait_queue(&session->wait, &wait);
++
++ BT_INFO("Found %d CAPI controller(s) on device %s", session->ncontroller, session->name);
++
++ if (!timeo)
++ return -ETIMEDOUT;
++
++ if (!session->ncontroller)
++ return -ENODEV;
++
++
++ if (session->ncontroller > 1)
++ BT_INFO("Setting up only CAPI controller 1");
++
++ if (!(session->ctrl = di->attach_ctr(&cmtp_driver, session->name, session))) {
++ BT_ERR("Can't attach new controller");
++ return -EBUSY;
++ }
++
++ session->num = session->ctrl->cnr;
++
++ BT_DBG("session %p ctrl %p num %d", session, session->ctrl, session->num);
++
++ capimsg_setu32(buf, 0, 1);
++
++ cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session),
++ CAPI_FUNCTION_GET_MANUFACTURER, buf, 4);
++
++ cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session),
++ CAPI_FUNCTION_GET_VERSION, buf, 4);
++
++ cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session),
++ CAPI_FUNCTION_GET_SERIAL_NUMBER, buf, 4);
++
++ cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session),
++ CAPI_FUNCTION_GET_PROFILE, buf, 4);
++
++ return 0;
++}
++
++void cmtp_detach_device(struct cmtp_session *session)
++{
++ struct capi_ctr *ctrl = session->ctrl;
++
++ BT_DBG("session %p ctrl %p", session, ctrl);
++
++ if (!ctrl)
++ return;
++
++ ctrl->reseted(ctrl);
++
++ di->detach_ctr(ctrl);
++}
++
++int cmtp_init_capi(void)
++{
++ if (!(di = attach_capi_driver(&cmtp_driver))) {
++ BT_ERR("Can't attach CAPI driver");
++ return -EIO;
++ }
++
++ return 0;
++}
++
++void cmtp_cleanup_capi(void)
++{
++ detach_capi_driver(&cmtp_driver);
++}
+diff -urN linux-2.4.18/net/bluetooth/cmtp/cmtp.h linux-2.4.18-mh15/net/bluetooth/cmtp/cmtp.h
+--- linux-2.4.18/net/bluetooth/cmtp/cmtp.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/net/bluetooth/cmtp/cmtp.h 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,138 @@
++/*
++ CMTP implementation for Linux Bluetooth stack (BlueZ).
++ Copyright (C) 2002-2003 Marcel Holtmann <marcel@holtmann.org>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License version 2 as
++ published by the Free Software Foundation;
++
++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++
++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
++ SOFTWARE IS DISCLAIMED.
++*/
++
++#ifndef __CMTP_H
++#define __CMTP_H
++
++#include <linux/types.h>
++#include <net/bluetooth/bluetooth.h>
++
++#define BTNAMSIZ 18
++
++/* CMTP ioctl defines */
++#define CMTPCONNADD _IOW('C', 200, int)
++#define CMTPCONNDEL _IOW('C', 201, int)
++#define CMTPGETCONNLIST _IOR('C', 210, int)
++#define CMTPGETCONNINFO _IOR('C', 211, int)
++
++#define CMTP_LOOPBACK 0
++
++struct cmtp_connadd_req {
++ int sock; // Connected socket
++ __u32 flags;
++};
++
++struct cmtp_conndel_req {
++ bdaddr_t bdaddr;
++ __u32 flags;
++};
++
++struct cmtp_conninfo {
++ bdaddr_t bdaddr;
++ __u32 flags;
++ __u16 state;
++ int num;
++};
++
++struct cmtp_connlist_req {
++ __u32 cnum;
++ struct cmtp_conninfo *ci;
++};
++
++int cmtp_add_connection(struct cmtp_connadd_req *req, struct socket *sock);
++int cmtp_del_connection(struct cmtp_conndel_req *req);
++int cmtp_get_connlist(struct cmtp_connlist_req *req);
++int cmtp_get_conninfo(struct cmtp_conninfo *ci);
++
++/* CMTP session defines */
++#define CMTP_INTEROP_TIMEOUT (HZ * 5)
++#define CMTP_INITIAL_MSGNUM 0xff00
++
++struct cmtp_session {
++ struct list_head list;
++
++ struct socket *sock;
++
++ bdaddr_t bdaddr;
++
++ unsigned long state;
++ unsigned long flags;
++
++ uint mtu;
++
++ char name[BTNAMSIZ];
++
++ atomic_t terminate;
++
++ wait_queue_head_t wait;
++
++ int ncontroller;
++ int num;
++ struct capi_ctr *ctrl;
++
++ struct list_head applications;
++
++ unsigned long blockids;
++ int msgnum;
++
++ struct sk_buff_head transmit;
++
++ struct sk_buff *reassembly[16];
++};
++
++struct cmtp_application {
++ struct list_head list;
++
++ unsigned long state;
++ int err;
++
++ __u16 appl;
++ __u16 mapping;
++
++ __u16 msgnum;
++};
++
++struct cmtp_scb {
++ int id;
++ int data;
++};
++
++int cmtp_attach_device(struct cmtp_session *session);
++void cmtp_detach_device(struct cmtp_session *session);
++
++void cmtp_recv_capimsg(struct cmtp_session *session, struct sk_buff *skb);
++void cmtp_send_capimsg(struct cmtp_session *session, struct sk_buff *skb);
++
++static inline void cmtp_schedule(struct cmtp_session *session)
++{
++ struct sock *sk = session->sock->sk;
++
++ wake_up_interruptible(sk->sleep);
++}
++
++/* CMTP init defines */
++int cmtp_init_capi(void);
++int cmtp_init_sockets(void);
++void cmtp_cleanup_capi(void);
++void cmtp_cleanup_sockets(void);
++
++#endif /* __CMTP_H */
+diff -urN linux-2.4.18/net/bluetooth/cmtp/Config.in linux-2.4.18-mh15/net/bluetooth/cmtp/Config.in
+--- linux-2.4.18/net/bluetooth/cmtp/Config.in 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/net/bluetooth/cmtp/Config.in 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,7 @@
++#
++# Bluetooth CMTP layer configuration
++#
++
++if [ "$CONFIG_ISDN" = "y" -o "$CONFIG_ISDN" = "m" ]; then
++ dep_tristate 'CMTP protocol support' CONFIG_BLUEZ_CMTP $CONFIG_ISDN_CAPI $CONFIG_BLUEZ_L2CAP
++fi
+diff -urN linux-2.4.18/net/bluetooth/cmtp/core.c linux-2.4.18-mh15/net/bluetooth/cmtp/core.c
+--- linux-2.4.18/net/bluetooth/cmtp/core.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/net/bluetooth/cmtp/core.c 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,515 @@
++/*
++ CMTP implementation for Linux Bluetooth stack (BlueZ).
++ Copyright (C) 2002-2003 Marcel Holtmann <marcel@holtmann.org>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License version 2 as
++ published by the Free Software Foundation;
++
++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++
++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
++ SOFTWARE IS DISCLAIMED.
++*/
++
++#include <linux/config.h>
++#include <linux/module.h>
++
++#include <linux/types.h>
++#include <linux/errno.h>
++#include <linux/kernel.h>
++#include <linux/major.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/poll.h>
++#include <linux/fcntl.h>
++#include <linux/skbuff.h>
++#include <linux/socket.h>
++#include <linux/ioctl.h>
++#include <linux/file.h>
++#include <linux/init.h>
++#include <net/sock.h>
++
++#include <net/bluetooth/bluetooth.h>
++#include <net/bluetooth/l2cap.h>
++
++#include "cmtp.h"
++
++#ifndef CONFIG_BLUEZ_CMTP_DEBUG
++#undef BT_DBG
++#define BT_DBG(D...)
++#endif
++
++#define VERSION "1.0"
++
++static DECLARE_RWSEM(cmtp_session_sem);
++static LIST_HEAD(cmtp_session_list);
++
++static struct cmtp_session *__cmtp_get_session(bdaddr_t *bdaddr)
++{
++ struct cmtp_session *session;
++ struct list_head *p;
++
++ BT_DBG("");
++
++ list_for_each(p, &cmtp_session_list) {
++ session = list_entry(p, struct cmtp_session, list);
++ if (!bacmp(bdaddr, &session->bdaddr))
++ return session;
++ }
++ return NULL;
++}
++
++static void __cmtp_link_session(struct cmtp_session *session)
++{
++ MOD_INC_USE_COUNT;
++ list_add(&session->list, &cmtp_session_list);
++}
++
++static void __cmtp_unlink_session(struct cmtp_session *session)
++{
++ list_del(&session->list);
++ MOD_DEC_USE_COUNT;
++}
++
++static void __cmtp_copy_session(struct cmtp_session *session, struct cmtp_conninfo *ci)
++{
++ bacpy(&ci->bdaddr, &session->bdaddr);
++
++ ci->flags = session->flags;
++ ci->state = session->state;
++
++ ci->num = session->num;
++}
++
++
++static inline int cmtp_alloc_block_id(struct cmtp_session *session)
++{
++ int i, id = -1;
++
++ for (i = 0; i < 16; i++)
++ if (!test_and_set_bit(i, &session->blockids)) {
++ id = i;
++ break;
++ }
++
++ return id;
++}
++
++static inline void cmtp_free_block_id(struct cmtp_session *session, int id)
++{
++ clear_bit(id, &session->blockids);
++}
++
++static inline void cmtp_add_msgpart(struct cmtp_session *session, int id, const unsigned char *buf, int count)
++{
++ struct sk_buff *skb = session->reassembly[id], *nskb;
++ int size;
++
++ BT_DBG("session %p buf %p count %d", session, buf, count);
++
++ size = (skb) ? skb->len + count : count;
++
++ if (!(nskb = alloc_skb(size, GFP_ATOMIC))) {
++ BT_ERR("Can't allocate memory for CAPI message");
++ return;
++ }
++
++ if (skb && (skb->len > 0))
++ memcpy(skb_put(nskb, skb->len), skb->data, skb->len);
++
++ memcpy(skb_put(nskb, count), buf, count);
++
++ session->reassembly[id] = nskb;
++
++ if (skb)
++ kfree_skb(skb);
++}
++
++static inline int cmtp_recv_frame(struct cmtp_session *session, struct sk_buff *skb)
++{
++ __u8 hdr, hdrlen, id;
++ __u16 len;
++
++ BT_DBG("session %p skb %p len %d", session, skb, skb->len);
++
++ while (skb->len > 0) {
++ hdr = skb->data[0];
++
++ switch (hdr & 0xc0) {
++ case 0x40:
++ hdrlen = 2;
++ len = skb->data[1];
++ break;
++ case 0x80:
++ hdrlen = 3;
++ len = skb->data[1] | (skb->data[2] << 8);
++ break;
++ default:
++ hdrlen = 1;
++ len = 0;
++ break;
++ }
++
++ id = (hdr & 0x3c) >> 2;
++
++ BT_DBG("hdr 0x%02x hdrlen %d len %d id %d", hdr, hdrlen, len, id);
++
++ if (hdrlen + len > skb->len) {
++ BT_ERR("Wrong size or header information in CMTP frame");
++ break;
++ }
++
++ if (len == 0) {
++ skb_pull(skb, hdrlen);
++ continue;
++ }
++
++ switch (hdr & 0x03) {
++ case 0x00:
++ cmtp_add_msgpart(session, id, skb->data + hdrlen, len);
++ cmtp_recv_capimsg(session, session->reassembly[id]);
++ session->reassembly[id] = NULL;
++ break;
++ case 0x01:
++ cmtp_add_msgpart(session, id, skb->data + hdrlen, len);
++ break;
++ default:
++ if (session->reassembly[id] != NULL)
++ kfree_skb(session->reassembly[id]);
++ session->reassembly[id] = NULL;
++ break;
++ }
++
++ skb_pull(skb, hdrlen + len);
++ }
++
++ kfree_skb(skb);
++ return 0;
++}
++
++static int cmtp_send_frame(struct cmtp_session *session, unsigned char *data, int len)
++{
++ struct socket *sock = session->sock;
++ struct iovec iv = { data, len };
++ struct msghdr msg;
++ int err;
++
++ BT_DBG("session %p data %p len %d", session, data, len);
++
++ if (!len)
++ return 0;
++
++ memset(&msg, 0, sizeof(msg));
++ msg.msg_iovlen = 1;
++ msg.msg_iov = &iv;
++
++ err = sock->ops->sendmsg(sock, &msg, len, 0);
++ return err;
++}
++
++static int cmtp_process_transmit(struct cmtp_session *session)
++{
++ struct sk_buff *skb, *nskb;
++ unsigned char *hdr;
++ unsigned int size, tail;
++
++ BT_DBG("session %p", session);
++
++ if (!(nskb = alloc_skb(session->mtu, GFP_ATOMIC))) {
++ BT_ERR("Can't allocate memory for new frame");
++ return -ENOMEM;
++ }
++
++ while ((skb = skb_dequeue(&session->transmit))) {
++ struct cmtp_scb *scb = (void *) skb->cb;
++
++ if ((tail = (session->mtu - nskb->len)) < 5) {
++ cmtp_send_frame(session, nskb->data, nskb->len);
++ skb_trim(nskb, 0);
++ tail = session->mtu;
++ }
++
++ size = min_t(uint, ((tail < 258) ? (tail - 2) : (tail - 3)), skb->len);
++
++ if ((scb->id < 0) && ((scb->id = cmtp_alloc_block_id(session)) < 0)) {
++ skb_queue_head(&session->transmit, skb);
++ break;
++ }
++
++ if (size < 256) {
++ hdr = skb_put(nskb, 2);
++ hdr[0] = 0x40
++ | ((scb->id << 2) & 0x3c)
++ | ((skb->len == size) ? 0x00 : 0x01);
++ hdr[1] = size;
++ } else {
++ hdr = skb_put(nskb, 3);
++ hdr[0] = 0x80
++ | ((scb->id << 2) & 0x3c)
++ | ((skb->len == size) ? 0x00 : 0x01);
++ hdr[1] = size & 0xff;
++ hdr[2] = size >> 8;
++ }
++
++ memcpy(skb_put(nskb, size), skb->data, size);
++ skb_pull(skb, size);
++
++ if (skb->len > 0) {
++ skb_queue_head(&session->transmit, skb);
++ } else {
++ cmtp_free_block_id(session, scb->id);
++ if (scb->data) {
++ cmtp_send_frame(session, nskb->data, nskb->len);
++ skb_trim(nskb, 0);
++ }
++ kfree_skb(skb);
++ }
++ }
++
++ cmtp_send_frame(session, nskb->data, nskb->len);
++
++ kfree_skb(nskb);
++
++ return skb_queue_len(&session->transmit);
++}
++
++static int cmtp_session(void *arg)
++{
++ struct cmtp_session *session = arg;
++ struct sock *sk = session->sock->sk;
++ struct sk_buff *skb;
++ wait_queue_t wait;
++
++ BT_DBG("session %p", session);
++
++ daemonize(); reparent_to_init();
++
++ sprintf(current->comm, "kcmtpd_ctr_%d", session->num);
++
++ sigfillset(&current->blocked);
++ flush_signals(current);
++
++ current->nice = -15;
++
++ set_fs(KERNEL_DS);
++
++ init_waitqueue_entry(&wait, current);
++ add_wait_queue(sk->sleep, &wait);
++ while (!atomic_read(&session->terminate)) {
++ set_current_state(TASK_INTERRUPTIBLE);
++
++ if (sk->state != BT_CONNECTED)
++ break;
++
++ while ((skb = skb_dequeue(&sk->receive_queue))) {
++ skb_orphan(skb);
++ cmtp_recv_frame(session, skb);
++ }
++
++ cmtp_process_transmit(session);
++
++ schedule();
++ }
++ set_current_state(TASK_RUNNING);
++ remove_wait_queue(sk->sleep, &wait);
++
++ down_write(&cmtp_session_sem);
++
++ if (!(session->flags & (1 << CMTP_LOOPBACK)))
++ cmtp_detach_device(session);
++
++ fput(session->sock->file);
++
++ __cmtp_unlink_session(session);
++
++ up_write(&cmtp_session_sem);
++
++ kfree(session);
++ return 0;
++}
++
++int cmtp_add_connection(struct cmtp_connadd_req *req, struct socket *sock)
++{
++ struct cmtp_session *session, *s;
++ bdaddr_t src, dst;
++ int i, err;
++
++ BT_DBG("");
++
++ baswap(&src, &bluez_pi(sock->sk)->src);
++ baswap(&dst, &bluez_pi(sock->sk)->dst);
++
++ session = kmalloc(sizeof(struct cmtp_session), GFP_KERNEL);
++ if (!session)
++ return -ENOMEM;
++ memset(session, 0, sizeof(struct cmtp_session));
++
++ down_write(&cmtp_session_sem);
++
++ s = __cmtp_get_session(&bluez_pi(sock->sk)->dst);
++ if (s && s->state == BT_CONNECTED) {
++ err = -EEXIST;
++ goto failed;
++ }
++
++ bacpy(&session->bdaddr, &bluez_pi(sock->sk)->dst);
++
++ session->mtu = min_t(uint, l2cap_pi(sock->sk)->omtu, l2cap_pi(sock->sk)->imtu);
++
++ BT_DBG("mtu %d", session->mtu);
++
++ sprintf(session->name, "%s", batostr(&dst));
++
++ session->sock = sock;
++ session->state = BT_CONFIG;
++
++ init_waitqueue_head(&session->wait);
++
++ session->ctrl = NULL;
++ session->msgnum = CMTP_INITIAL_MSGNUM;
++
++ INIT_LIST_HEAD(&session->applications);
++
++ skb_queue_head_init(&session->transmit);
++
++ for (i = 0; i < 16; i++)
++ session->reassembly[i] = NULL;
++
++ session->flags = req->flags;
++
++ __cmtp_link_session(session);
++
++ err = kernel_thread(cmtp_session, session, CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
++ if (err < 0)
++ goto unlink;
++
++ if (!(session->flags & (1 << CMTP_LOOPBACK))) {
++ err = cmtp_attach_device(session);
++ if (err < 0)
++ goto detach;
++ }
++
++ up_write(&cmtp_session_sem);
++ return 0;
++
++detach:
++ cmtp_detach_device(session);
++
++unlink:
++ __cmtp_unlink_session(session);
++
++failed:
++ up_write(&cmtp_session_sem);
++ kfree(session);
++ return err;
++}
++
++int cmtp_del_connection(struct cmtp_conndel_req *req)
++{
++ struct cmtp_session *session;
++ int err = 0;
++
++ BT_DBG("");
++
++ down_read(&cmtp_session_sem);
++
++ session = __cmtp_get_session(&req->bdaddr);
++ if (session) {
++ /* Flush the transmit queue */
++ skb_queue_purge(&session->transmit);
++
++ /* Kill session thread */
++ atomic_inc(&session->terminate);
++ cmtp_schedule(session);
++ } else
++ err = -ENOENT;
++
++ up_read(&cmtp_session_sem);
++ return err;
++}
++
++int cmtp_get_connlist(struct cmtp_connlist_req *req)
++{
++ struct list_head *p;
++ int err = 0, n = 0;
++
++ BT_DBG("");
++
++ down_read(&cmtp_session_sem);
++
++ list_for_each(p, &cmtp_session_list) {
++ struct cmtp_session *session;
++ struct cmtp_conninfo ci;
++
++ session = list_entry(p, struct cmtp_session, list);
++
++ __cmtp_copy_session(session, &ci);
++
++ if (copy_to_user(req->ci, &ci, sizeof(ci))) {
++ err = -EFAULT;
++ break;
++ }
++
++ if (++n >= req->cnum)
++ break;
++
++ req->ci++;
++ }
++ req->cnum = n;
++
++ up_read(&cmtp_session_sem);
++ return err;
++}
++
++int cmtp_get_conninfo(struct cmtp_conninfo *ci)
++{
++ struct cmtp_session *session;
++ int err = 0;
++
++ down_read(&cmtp_session_sem);
++
++ session = __cmtp_get_session(&ci->bdaddr);
++ if (session)
++ __cmtp_copy_session(session, ci);
++ else
++ err = -ENOENT;
++
++ up_read(&cmtp_session_sem);
++ return err;
++}
++
++
++int __init init_cmtp(void)
++{
++ l2cap_load();
++
++ cmtp_init_capi();
++ cmtp_init_sockets();
++
++ BT_INFO("BlueZ CMTP ver %s", VERSION);
++ BT_INFO("Copyright (C) 2002-2003 Marcel Holtmann <marcel@holtmann.org>");
++
++ return 0;
++}
++
++void __exit exit_cmtp(void)
++{
++ cmtp_cleanup_sockets();
++ cmtp_cleanup_capi();
++}
++
++module_init(init_cmtp);
++module_exit(exit_cmtp);
++
++MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
++MODULE_DESCRIPTION("BlueZ CMTP ver " VERSION);
++MODULE_LICENSE("GPL");
+diff -urN linux-2.4.18/net/bluetooth/cmtp/Makefile linux-2.4.18-mh15/net/bluetooth/cmtp/Makefile
+--- linux-2.4.18/net/bluetooth/cmtp/Makefile 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/net/bluetooth/cmtp/Makefile 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,10 @@
++#
++# Makefile for the Linux Bluetooth CMTP layer
++#
++
++O_TARGET := cmtp.o
++
++obj-y := core.o sock.o capi.o
++obj-m += $(O_TARGET)
++
++include $(TOPDIR)/Rules.make
+diff -urN linux-2.4.18/net/bluetooth/cmtp/sock.c linux-2.4.18-mh15/net/bluetooth/cmtp/sock.c
+--- linux-2.4.18/net/bluetooth/cmtp/sock.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/net/bluetooth/cmtp/sock.c 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,208 @@
++/*
++ CMTP implementation for Linux Bluetooth stack (BlueZ).
++ Copyright (C) 2002-2003 Marcel Holtmann <marcel@holtmann.org>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License version 2 as
++ published by the Free Software Foundation;
++
++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++
++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
++ SOFTWARE IS DISCLAIMED.
++*/
++
++#include <linux/config.h>
++#include <linux/module.h>
++
++#include <linux/types.h>
++#include <linux/errno.h>
++#include <linux/kernel.h>
++#include <linux/major.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/poll.h>
++#include <linux/fcntl.h>
++#include <linux/skbuff.h>
++#include <linux/socket.h>
++#include <linux/ioctl.h>
++#include <linux/file.h>
++#include <net/sock.h>
++
++#include <asm/system.h>
++#include <asm/uaccess.h>
++
++#include "cmtp.h"
++
++#ifndef CONFIG_BLUEZ_CMTP_DEBUG
++#undef BT_DBG
++#define BT_DBG(D...)
++#endif
++
++static int cmtp_sock_release(struct socket *sock)
++{
++ struct sock *sk = sock->sk;
++
++ BT_DBG("sock %p sk %p", sock, sk);
++
++ if (!sk)
++ return 0;
++
++ sock_orphan(sk);
++ sock_put(sk);
++
++ MOD_DEC_USE_COUNT;
++ return 0;
++}
++
++static int cmtp_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
++{
++ struct cmtp_connadd_req ca;
++ struct cmtp_conndel_req cd;
++ struct cmtp_connlist_req cl;
++ struct cmtp_conninfo ci;
++ struct socket *nsock;
++ int err;
++
++ BT_DBG("cmd %x arg %lx", cmd, arg);
++
++ switch (cmd) {
++ case CMTPCONNADD:
++ if (!capable(CAP_NET_ADMIN))
++ return -EACCES;
++
++ if (copy_from_user(&ca, (void *) arg, sizeof(ca)))
++ return -EFAULT;
++
++ nsock = sockfd_lookup(ca.sock, &err);
++ if (!nsock)
++ return err;
++
++ if (nsock->sk->state != BT_CONNECTED) {
++ fput(nsock->file);
++ return -EBADFD;
++ }
++
++ err = cmtp_add_connection(&ca, nsock);
++ if (!err) {
++ if (copy_to_user((void *) arg, &ca, sizeof(ca)))
++ err = -EFAULT;
++ } else
++ fput(nsock->file);
++
++ return err;
++
++ case CMTPCONNDEL:
++ if (!capable(CAP_NET_ADMIN))
++ return -EACCES;
++
++ if (copy_from_user(&cd, (void *) arg, sizeof(cd)))
++ return -EFAULT;
++
++ return cmtp_del_connection(&cd);
++
++ case CMTPGETCONNLIST:
++ if (copy_from_user(&cl, (void *) arg, sizeof(cl)))
++ return -EFAULT;
++
++ if (cl.cnum <= 0)
++ return -EINVAL;
++
++ err = cmtp_get_connlist(&cl);
++ if (!err && copy_to_user((void *) arg, &cl, sizeof(cl)))
++ return -EFAULT;
++
++ return err;
++
++ case CMTPGETCONNINFO:
++ if (copy_from_user(&ci, (void *) arg, sizeof(ci)))
++ return -EFAULT;
++
++ err = cmtp_get_conninfo(&ci);
++ if (!err && copy_to_user((void *) arg, &ci, sizeof(ci)))
++ return -EFAULT;
++
++ return err;
++ }
++
++ return -EINVAL;
++}
++
++static struct proto_ops cmtp_sock_ops = {
++ family: PF_BLUETOOTH,
++ release: cmtp_sock_release,
++ ioctl: cmtp_sock_ioctl,
++ bind: sock_no_bind,
++ getname: sock_no_getname,
++ sendmsg: sock_no_sendmsg,
++ recvmsg: sock_no_recvmsg,
++ poll: sock_no_poll,
++ listen: sock_no_listen,
++ shutdown: sock_no_shutdown,
++ setsockopt: sock_no_setsockopt,
++ getsockopt: sock_no_getsockopt,
++ connect: sock_no_connect,
++ socketpair: sock_no_socketpair,
++ accept: sock_no_accept,
++ mmap: sock_no_mmap
++};
++
++static int cmtp_sock_create(struct socket *sock, int protocol)
++{
++ struct sock *sk;
++
++ BT_DBG("sock %p", sock);
++
++ if (sock->type != SOCK_RAW)
++ return -ESOCKTNOSUPPORT;
++
++ sock->ops = &cmtp_sock_ops;
++
++ if (!(sk = sk_alloc(PF_BLUETOOTH, GFP_KERNEL, 1)))
++ return -ENOMEM;
++
++ MOD_INC_USE_COUNT;
++
++ sock->state = SS_UNCONNECTED;
++ sock_init_data(sock, sk);
++
++ sk->destruct = NULL;
++ sk->protocol = protocol;
++
++ return 0;
++}
++
++static struct net_proto_family cmtp_sock_family_ops = {
++ family: PF_BLUETOOTH,
++ create: cmtp_sock_create
++};
++
++int cmtp_init_sockets(void)
++{
++ int err;
++
++ if ((err = bluez_sock_register(BTPROTO_CMTP, &cmtp_sock_family_ops))) {
++ BT_ERR("Can't register CMTP socket layer (%d)", err);
++ return err;
++ }
++
++ return 0;
++}
++
++void cmtp_cleanup_sockets(void)
++{
++ int err;
++
++ if ((err = bluez_sock_unregister(BTPROTO_CMTP)))
++ BT_ERR("Can't unregister CMTP socket layer (%d)", err);
++
++ return;
++}
+diff -urN linux-2.4.18/net/bluetooth/Config.in linux-2.4.18-mh15/net/bluetooth/Config.in
+--- linux-2.4.18/net/bluetooth/Config.in 2001-06-12 04:15:27.000000000 +0200
++++ linux-2.4.18-mh15/net/bluetooth/Config.in 2004-08-01 16:26:23.000000000 +0200
+@@ -1,16 +1,23 @@
+ #
+-# Bluetooth configuration
++# Bluetooth subsystem configuration
+ #
+
+ if [ "$CONFIG_NET" != "n" ]; then
++
+ mainmenu_option next_comment
+ comment 'Bluetooth support'
+ dep_tristate 'Bluetooth subsystem support' CONFIG_BLUEZ $CONFIG_NET
+
+ if [ "$CONFIG_BLUEZ" != "n" ]; then
+ dep_tristate 'L2CAP protocol support' CONFIG_BLUEZ_L2CAP $CONFIG_BLUEZ
++ dep_tristate 'SCO links support' CONFIG_BLUEZ_SCO $CONFIG_BLUEZ
++ source net/bluetooth/rfcomm/Config.in
++ source net/bluetooth/bnep/Config.in
++ source net/bluetooth/cmtp/Config.in
++ source net/bluetooth/hidp/Config.in
+ source drivers/bluetooth/Config.in
+ fi
++
+ endmenu
+ fi
+
+diff -urN linux-2.4.18/net/bluetooth/hci_conn.c linux-2.4.18-mh15/net/bluetooth/hci_conn.c
+--- linux-2.4.18/net/bluetooth/hci_conn.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/net/bluetooth/hci_conn.c 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,435 @@
++/*
++ BlueZ - Bluetooth protocol stack for Linux
++ Copyright (C) 2000-2001 Qualcomm Incorporated
++
++ Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License version 2 as
++ published by the Free Software Foundation;
++
++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++
++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
++ SOFTWARE IS DISCLAIMED.
++*/
++
++/*
++ * HCI Connection handling.
++ *
++ * $Id$
++ */
++
++#include <linux/config.h>
++#include <linux/module.h>
++
++#include <linux/types.h>
++#include <linux/errno.h>
++#include <linux/kernel.h>
++#include <linux/major.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/poll.h>
++#include <linux/fcntl.h>
++#include <linux/init.h>
++#include <linux/skbuff.h>
++#include <linux/interrupt.h>
++#include <linux/notifier.h>
++#include <net/sock.h>
++
++#include <asm/system.h>
++#include <asm/uaccess.h>
++#include <asm/unaligned.h>
++
++#include <net/bluetooth/bluetooth.h>
++#include <net/bluetooth/hci_core.h>
++
++#ifndef HCI_CORE_DEBUG
++#undef BT_DBG
++#define BT_DBG( A... )
++#endif
++
++void hci_acl_connect(struct hci_conn *conn)
++{
++ struct hci_dev *hdev = conn->hdev;
++ struct inquiry_entry *ie;
++ create_conn_cp cp;
++
++ BT_DBG("%p", conn);
++
++ conn->state = BT_CONNECT;
++ conn->out = 1;
++ conn->link_mode = HCI_LM_MASTER;
++
++ memset(&cp, 0, sizeof(cp));
++ bacpy(&cp.bdaddr, &conn->dst);
++ cp.pscan_rep_mode = 0x02;
++
++ if ((ie = inquiry_cache_lookup(hdev, &conn->dst)) &&
++ inquiry_entry_age(ie) <= INQUIRY_ENTRY_AGE_MAX) {
++ cp.pscan_rep_mode = ie->info.pscan_rep_mode;
++ cp.pscan_mode = ie->info.pscan_mode;
++ cp.clock_offset = ie->info.clock_offset | __cpu_to_le16(0x8000);
++ }
++
++ cp.pkt_type = __cpu_to_le16(hdev->pkt_type & ACL_PTYPE_MASK);
++ if (lmp_rswitch_capable(hdev) && !(hdev->link_mode & HCI_LM_MASTER))
++ cp.role_switch = 0x01;
++ else
++ cp.role_switch = 0x00;
++
++ hci_send_cmd(hdev, OGF_LINK_CTL, OCF_CREATE_CONN,
++ CREATE_CONN_CP_SIZE, &cp);
++}
++
++void hci_acl_disconn(struct hci_conn *conn, __u8 reason)
++{
++ disconnect_cp cp;
++
++ BT_DBG("%p", conn);
++
++ conn->state = BT_DISCONN;
++
++ cp.handle = __cpu_to_le16(conn->handle);
++ cp.reason = reason;
++ hci_send_cmd(conn->hdev, OGF_LINK_CTL, OCF_DISCONNECT,
++ DISCONNECT_CP_SIZE, &cp);
++}
++
++void hci_add_sco(struct hci_conn *conn, __u16 handle)
++{
++ struct hci_dev *hdev = conn->hdev;
++ add_sco_cp cp;
++
++ BT_DBG("%p", conn);
++
++ conn->state = BT_CONNECT;
++ conn->out = 1;
++
++ cp.pkt_type = __cpu_to_le16(hdev->pkt_type & SCO_PTYPE_MASK);
++ cp.handle = __cpu_to_le16(handle);
++
++ hci_send_cmd(hdev, OGF_LINK_CTL, OCF_ADD_SCO, ADD_SCO_CP_SIZE, &cp);
++}
++
++static void hci_conn_timeout(unsigned long arg)
++{
++ struct hci_conn *conn = (void *)arg;
++ struct hci_dev *hdev = conn->hdev;
++
++ BT_DBG("conn %p state %d", conn, conn->state);
++
++ if (atomic_read(&conn->refcnt))
++ return;
++
++ hci_dev_lock(hdev);
++ if (conn->state == BT_CONNECTED)
++ hci_acl_disconn(conn, 0x13);
++ else
++ conn->state = BT_CLOSED;
++ hci_dev_unlock(hdev);
++ return;
++}
++
++static void hci_conn_init_timer(struct hci_conn *conn)
++{
++ init_timer(&conn->timer);
++ conn->timer.function = hci_conn_timeout;
++ conn->timer.data = (unsigned long)conn;
++}
++
++struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst)
++{
++ struct hci_conn *conn;
++
++ BT_DBG("%s dst %s", hdev->name, batostr(dst));
++
++ if (!(conn = kmalloc(sizeof(struct hci_conn), GFP_ATOMIC)))
++ return NULL;
++ memset(conn, 0, sizeof(struct hci_conn));
++
++ bacpy(&conn->dst, dst);
++ conn->type = type;
++ conn->hdev = hdev;
++ conn->state = BT_OPEN;
++
++ skb_queue_head_init(&conn->data_q);
++ hci_conn_init_timer(conn);
++
++ atomic_set(&conn->refcnt, 0);
++
++ hci_dev_hold(hdev);
++
++ tasklet_disable(&hdev->tx_task);
++ conn_hash_add(hdev, conn);
++ tasklet_enable(&hdev->tx_task);
++
++ return conn;
++}
++
++int hci_conn_del(struct hci_conn *conn)
++{
++ struct hci_dev *hdev = conn->hdev;
++
++ BT_DBG("%s conn %p handle %d", hdev->name, conn, conn->handle);
++
++ hci_conn_del_timer(conn);
++
++ if (conn->type == SCO_LINK) {
++ struct hci_conn *acl = conn->link;
++ if (acl) {
++ acl->link = NULL;
++ hci_conn_put(acl);
++ }
++ } else {
++ struct hci_conn *sco = conn->link;
++ if (sco)
++ sco->link = NULL;
++
++ /* Unacked frames */
++ hdev->acl_cnt += conn->sent;
++ }
++
++ tasklet_disable(&hdev->tx_task);
++ conn_hash_del(hdev, conn);
++ tasklet_enable(&hdev->tx_task);
++
++ skb_queue_purge(&conn->data_q);
++
++ hci_dev_put(hdev);
++
++ kfree(conn);
++ return 0;
++}
++
++struct hci_dev *hci_get_route(bdaddr_t *dst, bdaddr_t *src)
++{
++ int use_src = bacmp(src, BDADDR_ANY);
++ struct hci_dev *hdev = NULL;
++ struct list_head *p;
++
++ BT_DBG("%s -> %s", batostr(src), batostr(dst));
++
++ read_lock_bh(&hdev_list_lock);
++
++ list_for_each(p, &hdev_list) {
++ struct hci_dev *d;
++ d = list_entry(p, struct hci_dev, list);
++
++ if (!test_bit(HCI_UP, &d->flags))
++ continue;
++
++ /* Simple routing:
++ * No source address - find interface with bdaddr != dst
++ * Source address - find interface with bdaddr == src
++ */
++
++ if (use_src) {
++ if (!bacmp(&d->bdaddr, src)) {
++ hdev = d; break;
++ }
++ } else {
++ if (bacmp(&d->bdaddr, dst)) {
++ hdev = d; break;
++ }
++ }
++ }
++
++ if (hdev)
++ hci_dev_hold(hdev);
++
++ read_unlock_bh(&hdev_list_lock);
++ return hdev;
++}
++
++/* Create SCO or ACL connection.
++ * Device _must_ be locked */
++struct hci_conn * hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst)
++{
++ struct hci_conn *acl;
++
++ BT_DBG("%s dst %s", hdev->name, batostr(dst));
++
++ if (!(acl = conn_hash_lookup_ba(hdev, ACL_LINK, dst))) {
++ if (!(acl = hci_conn_add(hdev, ACL_LINK, dst)))
++ return NULL;
++ }
++
++ hci_conn_hold(acl);
++
++ if (acl->state == BT_OPEN || acl->state == BT_CLOSED)
++ hci_acl_connect(acl);
++
++ if (type == SCO_LINK) {
++ struct hci_conn *sco;
++
++ if (!(sco = conn_hash_lookup_ba(hdev, SCO_LINK, dst))) {
++ if (!(sco = hci_conn_add(hdev, SCO_LINK, dst))) {
++ hci_conn_put(acl);
++ return NULL;
++ }
++ }
++ acl->link = sco;
++ sco->link = acl;
++
++ hci_conn_hold(sco);
++
++ if (acl->state == BT_CONNECTED &&
++ (sco->state == BT_OPEN || sco->state == BT_CLOSED))
++ hci_add_sco(sco, acl->handle);
++
++ return sco;
++ } else {
++ return acl;
++ }
++}
++
++/* Authenticate remote device */
++int hci_conn_auth(struct hci_conn *conn)
++{
++ BT_DBG("conn %p", conn);
++
++ if (conn->link_mode & HCI_LM_AUTH)
++ return 1;
++
++ if (!test_and_set_bit(HCI_CONN_AUTH_PEND, &conn->pend)) {
++ auth_requested_cp ar;
++ ar.handle = __cpu_to_le16(conn->handle);
++ hci_send_cmd(conn->hdev, OGF_LINK_CTL, OCF_AUTH_REQUESTED,
++ AUTH_REQUESTED_CP_SIZE, &ar);
++ }
++ return 0;
++}
++
++/* Enable encryption */
++int hci_conn_encrypt(struct hci_conn *conn)
++{
++ BT_DBG("conn %p", conn);
++
++ if (conn->link_mode & HCI_LM_ENCRYPT)
++ return 1;
++
++ if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend))
++ return 0;
++
++ if (hci_conn_auth(conn)) {
++ set_conn_encrypt_cp ce;
++ ce.handle = __cpu_to_le16(conn->handle);
++ ce.encrypt = 1;
++ hci_send_cmd(conn->hdev, OGF_LINK_CTL, OCF_SET_CONN_ENCRYPT,
++ SET_CONN_ENCRYPT_CP_SIZE, &ce);
++ }
++ return 0;
++}
++
++/* Drop all connection on the device */
++void hci_conn_hash_flush(struct hci_dev *hdev)
++{
++ struct conn_hash *h = &hdev->conn_hash;
++ struct list_head *p;
++
++ BT_DBG("hdev %s", hdev->name);
++
++ p = h->list.next;
++ while (p != &h->list) {
++ struct hci_conn *c;
++
++ c = list_entry(p, struct hci_conn, list);
++ p = p->next;
++
++ c->state = BT_CLOSED;
++
++ hci_proto_disconn_ind(c, 0x16);
++ hci_conn_del(c);
++ }
++}
++
++int hci_get_conn_list(unsigned long arg)
++{
++ struct hci_conn_list_req req, *cl;
++ struct hci_conn_info *ci;
++ struct hci_dev *hdev;
++ struct list_head *p;
++ int n = 0, size, err;
++
++ if (copy_from_user(&req, (void *) arg, sizeof(req)))
++ return -EFAULT;
++
++ if (!req.conn_num || req.conn_num > (PAGE_SIZE * 2) / sizeof(*ci))
++ return -EINVAL;
++
++ size = sizeof(req) + req.conn_num * sizeof(*ci);
++
++ if (!(cl = (void *) kmalloc(size, GFP_KERNEL)))
++ return -ENOMEM;
++
++ if (!(hdev = hci_dev_get(req.dev_id))) {
++ kfree(cl);
++ return -ENODEV;
++ }
++
++ ci = cl->conn_info;
++
++ hci_dev_lock_bh(hdev);
++ list_for_each(p, &hdev->conn_hash.list) {
++ register struct hci_conn *c;
++ c = list_entry(p, struct hci_conn, list);
++
++ bacpy(&(ci + n)->bdaddr, &c->dst);
++ (ci + n)->handle = c->handle;
++ (ci + n)->type = c->type;
++ (ci + n)->out = c->out;
++ (ci + n)->state = c->state;
++ (ci + n)->link_mode = c->link_mode;
++ if (++n >= req.conn_num)
++ break;
++ }
++ hci_dev_unlock_bh(hdev);
++
++ cl->dev_id = hdev->id;
++ cl->conn_num = n;
++ size = sizeof(req) + n * sizeof(*ci);
++
++ hci_dev_put(hdev);
++
++ err = copy_to_user((void *) arg, cl, size);
++ kfree(cl);
++
++ return err ? -EFAULT : 0;
++}
++
++int hci_get_conn_info(struct hci_dev *hdev, unsigned long arg)
++{
++ struct hci_conn_info_req req;
++ struct hci_conn_info ci;
++ struct hci_conn *conn;
++ char *ptr = (void *) arg + sizeof(req);
++
++ if (copy_from_user(&req, (void *) arg, sizeof(req)))
++ return -EFAULT;
++
++ hci_dev_lock_bh(hdev);
++ conn = conn_hash_lookup_ba(hdev, req.type, &req.bdaddr);
++ if (conn) {
++ bacpy(&ci.bdaddr, &conn->dst);
++ ci.handle = conn->handle;
++ ci.type = conn->type;
++ ci.out = conn->out;
++ ci.state = conn->state;
++ ci.link_mode = conn->link_mode;
++ }
++ hci_dev_unlock_bh(hdev);
++
++ if (!conn)
++ return -ENOENT;
++
++ return copy_to_user(ptr, &ci, sizeof(ci)) ? -EFAULT : 0;
++}
+diff -urN linux-2.4.18/net/bluetooth/hci_core.c linux-2.4.18-mh15/net/bluetooth/hci_core.c
+--- linux-2.4.18/net/bluetooth/hci_core.c 2001-11-09 23:21:21.000000000 +0100
++++ linux-2.4.18-mh15/net/bluetooth/hci_core.c 2004-08-01 16:26:23.000000000 +0200
+@@ -25,11 +25,12 @@
+ /*
+ * BlueZ HCI Core.
+ *
+- * $Id$
++ * $Id$
+ */
+
+ #include <linux/config.h>
+ #include <linux/module.h>
++#include <linux/kmod.h>
+
+ #include <linux/types.h>
+ #include <linux/errno.h>
+@@ -50,12 +51,11 @@
+ #include <asm/unaligned.h>
+
+ #include <net/bluetooth/bluetooth.h>
+-#include <net/bluetooth/bluez.h>
+ #include <net/bluetooth/hci_core.h>
+
+ #ifndef HCI_CORE_DEBUG
+-#undef DBG
+-#define DBG( A... )
++#undef BT_DBG
++#define BT_DBG( A... )
+ #endif
+
+ static void hci_cmd_task(unsigned long arg);
+@@ -63,279 +63,69 @@
+ static void hci_tx_task(unsigned long arg);
+ static void hci_notify(struct hci_dev *hdev, int event);
+
+-static rwlock_t hci_task_lock = RW_LOCK_UNLOCKED;
++rwlock_t hci_task_lock = RW_LOCK_UNLOCKED;
+
+ /* HCI device list */
+-struct hci_dev *hdev_list[HCI_MAX_DEV];
+-spinlock_t hdev_list_lock;
+-#define GET_HDEV(a) (hdev_list[a])
+-
+-/* HCI protocol list */
+-struct hci_proto *hproto_list[HCI_MAX_PROTO];
+-#define GET_HPROTO(a) (hproto_list[a])
++LIST_HEAD(hdev_list);
++rwlock_t hdev_list_lock = RW_LOCK_UNLOCKED;
+
+-/* HCI notifiers list */
+-struct notifier_block *hci_dev_notifier;
+-
+-/* HCI device notifications */
+-int hci_register_notifier(struct notifier_block *nb)
+-{
+- int err, i;
+- struct hci_dev *hdev;
+-
+- if ((err = notifier_chain_register(&hci_dev_notifier, nb)))
+- return err;
+-
+- /* Notify about already registered devices */
+- spin_lock(&hdev_list_lock);
+- for (i = 0; i < HCI_MAX_DEV; i++) {
+- if (!(hdev = GET_HDEV(i)))
+- continue;
+- if (hdev->flags & HCI_UP)
+- (*nb->notifier_call)(nb, HCI_DEV_UP, hdev);
+- }
+- spin_unlock(&hdev_list_lock);
+-
+- return 0;
+-}
+-
+-int hci_unregister_notifier(struct notifier_block *nb)
+-{
+- return notifier_chain_unregister(&hci_dev_notifier, nb);
+-}
+-
+-static inline void hci_notify(struct hci_dev *hdev, int event)
+-{
+- notifier_call_chain(&hci_dev_notifier, event, hdev);
+-}
+-
+-/* Get HCI device by index (device is locked on return)*/
+-struct hci_dev *hci_dev_get(int index)
+-{
+- struct hci_dev *hdev;
+- DBG("%d", index);
+-
+- if (index < 0 || index >= HCI_MAX_DEV)
+- return NULL;
+-
+- spin_lock(&hdev_list_lock);
+- if ((hdev = GET_HDEV(index)))
+- hci_dev_hold(hdev);
+- spin_unlock(&hdev_list_lock);
+-
+- return hdev;
+-}
+-
+-/* Flush inquiry cache */
+-void inquiry_cache_flush(struct inquiry_cache *cache)
+-{
+- struct inquiry_entry *next = cache->list, *e;
+-
+- DBG("cache %p", cache);
+-
+- cache->list = NULL;
+- while ((e = next)) {
+- next = e->next;
+- kfree(e);
+- }
+-}
+-
+-/* Lookup by bdaddr.
+- * Cache must be locked. */
+-static struct inquiry_entry * __inquiry_cache_lookup(struct inquiry_cache *cache, bdaddr_t *bdaddr)
+-{
+- struct inquiry_entry *e;
+-
+- DBG("cache %p, %s", cache, batostr(bdaddr));
+-
+- for (e = cache->list; e; e = e->next)
+- if (!bacmp(&e->info.bdaddr, bdaddr))
+- break;
+-
+- return e;
+-}
+-
+-static void inquiry_cache_update(struct inquiry_cache *cache, inquiry_info *info)
+-{
+- struct inquiry_entry *e;
+-
+- DBG("cache %p, %s", cache, batostr(&info->bdaddr));
+-
+- inquiry_cache_lock(cache);
+-
+- if (!(e = __inquiry_cache_lookup(cache, &info->bdaddr))) {
+- /* Entry not in the cache. Add new one. */
+- if (!(e = kmalloc(sizeof(struct inquiry_entry), GFP_ATOMIC)))
+- goto unlock;
+- memset(e, 0, sizeof(struct inquiry_entry));
+- e->next = cache->list;
+- cache->list = e;
+- }
+-
+- memcpy(&e->info, info, sizeof(inquiry_info));
+- e->timestamp = jiffies;
+- cache->timestamp = jiffies;
+-unlock:
+- inquiry_cache_unlock(cache);
+-}
+-
+-static int inquiry_cache_dump(struct inquiry_cache *cache, int num, __u8 *buf)
+-{
+- inquiry_info *info = (inquiry_info *) buf;
+- struct inquiry_entry *e;
+- int copied = 0;
++/* HCI protocols */
++#define HCI_MAX_PROTO 2
++struct hci_proto *hci_proto[HCI_MAX_PROTO];
+
+- inquiry_cache_lock(cache);
+-
+- for (e = cache->list; e && copied < num; e = e->next, copied++)
+- memcpy(info++, &e->info, sizeof(inquiry_info));
++/* HCI notifiers list */
++static struct notifier_block *hci_notifier;
+
+- inquiry_cache_unlock(cache);
+
+- DBG("cache %p, copied %d", cache, copied);
+- return copied;
+-}
++/* ---- HCI notifications ---- */
+
+-/* --------- BaseBand connections --------- */
+-static struct hci_conn *hci_conn_add(struct hci_dev *hdev, __u16 handle, __u8 type, bdaddr_t *dst)
++int hci_register_notifier(struct notifier_block *nb)
+ {
+- struct hci_conn *conn;
+-
+- DBG("%s handle %d dst %s", hdev->name, handle, batostr(dst));
+-
+- if ( conn_hash_lookup(&hdev->conn_hash, handle)) {
+- ERR("%s handle 0x%x already exists", hdev->name, handle);
+- return NULL;
+- }
+-
+- if (!(conn = kmalloc(sizeof(struct hci_conn), GFP_ATOMIC)))
+- return NULL;
+- memset(conn, 0, sizeof(struct hci_conn));
+-
+- bacpy(&conn->dst, dst);
+- conn->handle = handle;
+- conn->type = type;
+- conn->hdev = hdev;
+-
+- skb_queue_head_init(&conn->data_q);
+-
+- hci_dev_hold(hdev);
+- conn_hash_add(&hdev->conn_hash, handle, conn);
+-
+- return conn;
++ return notifier_chain_register(&hci_notifier, nb);
+ }
+
+-static int hci_conn_del(struct hci_dev *hdev, struct hci_conn *conn)
++int hci_unregister_notifier(struct notifier_block *nb)
+ {
+- DBG("%s conn %p handle %d", hdev->name, conn, conn->handle);
+-
+- conn_hash_del(&hdev->conn_hash, conn);
+- hci_dev_put(hdev);
+-
+- /* Unacked frames */
+- hdev->acl_cnt += conn->sent;
+-
+- skb_queue_purge(&conn->data_q);
+-
+- kfree(conn);
+- return 0;
++ return notifier_chain_unregister(&hci_notifier, nb);
+ }
+
+-/* Drop all connection on the device */
+-static void hci_conn_hash_flush(struct hci_dev *hdev)
++void hci_notify(struct hci_dev *hdev, int event)
+ {
+- struct conn_hash *h = &hdev->conn_hash;
+- struct hci_proto *hp;
+- struct list_head *p;
+-
+- DBG("hdev %s", hdev->name);
+-
+- p = h->list.next;
+- while (p != &h->list) {
+- struct hci_conn *c;
+-
+- c = list_entry(p, struct hci_conn, list);
+- p = p->next;
+-
+- if (c->type == ACL_LINK) {
+- /* ACL link notify L2CAP layer */
+- if ((hp = GET_HPROTO(HCI_PROTO_L2CAP)) && hp->disconn_ind)
+- hp->disconn_ind(c, 0x16);
+- } else {
+- /* SCO link (no notification) */
+- }
+-
+- hci_conn_del(hdev, c);
+- }
++ notifier_call_chain(&hci_notifier, event, hdev);
+ }
+
+-int hci_connect(struct hci_dev *hdev, bdaddr_t *bdaddr)
+-{
+- struct inquiry_cache *cache = &hdev->inq_cache;
+- struct inquiry_entry *e;
+- create_conn_cp cc;
+- __u16 clock_offset;
+-
+- DBG("%s bdaddr %s", hdev->name, batostr(bdaddr));
+-
+- if (!(hdev->flags & HCI_UP))
+- return -ENODEV;
+-
+- inquiry_cache_lock_bh(cache);
+-
+- if (!(e = __inquiry_cache_lookup(cache, bdaddr)) || inquiry_entry_age(e) > INQUIRY_ENTRY_AGE_MAX) {
+- cc.pscan_rep_mode = 0;
+- cc.pscan_mode = 0;
+- clock_offset = 0;
+- } else {
+- cc.pscan_rep_mode = e->info.pscan_rep_mode;
+- cc.pscan_mode = e->info.pscan_mode;
+- clock_offset = __le16_to_cpu(e->info.clock_offset) & 0x8000;
+- }
+-
+- inquiry_cache_unlock_bh(cache);
+-
+- bacpy(&cc.bdaddr, bdaddr);
+- cc.pkt_type = __cpu_to_le16(hdev->pkt_type);
+- cc.clock_offset = __cpu_to_le16(clock_offset);
+-
+- if (lmp_rswitch_capable(hdev))
+- cc.role_switch = 0x01;
+- else
+- cc.role_switch = 0x00;
+-
+- hci_send_cmd(hdev, OGF_LINK_CTL, OCF_CREATE_CONN, CREATE_CONN_CP_SIZE, &cc);
++/* ---- HCI hotplug support ---- */
+
+- return 0;
+-}
++#ifdef CONFIG_HOTPLUG
+
+-int hci_disconnect(struct hci_conn *conn, __u8 reason)
++static int hci_run_hotplug(char *dev, char *action)
+ {
+- disconnect_cp dc;
+-
+- DBG("conn %p handle %d", conn, conn->handle);
++ char *argv[3], *envp[5], dstr[20], astr[32];
+
+- dc.handle = __cpu_to_le16(conn->handle);
+- dc.reason = reason;
+- hci_send_cmd(conn->hdev, OGF_LINK_CTL, OCF_DISCONNECT, DISCONNECT_CP_SIZE, &dc);
++ sprintf(dstr, "DEVICE=%s", dev);
++ sprintf(astr, "ACTION=%s", action);
+
+- return 0;
+-}
++ argv[0] = hotplug_path;
++ argv[1] = "bluetooth";
++ argv[2] = NULL;
+
+-/* --------- HCI request handling ------------ */
+-static inline void hci_req_lock(struct hci_dev *hdev)
+-{
+- down(&hdev->req_lock);
++ envp[0] = "HOME=/";
++ envp[1] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
++ envp[2] = dstr;
++ envp[3] = astr;
++ envp[4] = NULL;
++
++ return call_usermodehelper(argv[0], argv, envp);
+ }
++#else
++#define hci_run_hotplug(A...)
++#endif
+
+-static inline void hci_req_unlock(struct hci_dev *hdev)
+-{
+- up(&hdev->req_lock);
+-}
++/* ---- HCI requests ---- */
+
+-static inline void hci_req_complete(struct hci_dev *hdev, int result)
++void hci_req_complete(struct hci_dev *hdev, int result)
+ {
+- DBG("%s result 0x%2.2x", hdev->name, result);
++ BT_DBG("%s result 0x%2.2x", hdev->name, result);
+
+ if (hdev->req_status == HCI_REQ_PEND) {
+ hdev->req_result = result;
+@@ -344,9 +134,9 @@
+ }
+ }
+
+-static inline void hci_req_cancel(struct hci_dev *hdev, int err)
++void hci_req_cancel(struct hci_dev *hdev, int err)
+ {
+- DBG("%s err 0x%2.2x", hdev->name, err);
++ BT_DBG("%s err 0x%2.2x", hdev->name, err);
+
+ if (hdev->req_status == HCI_REQ_PEND) {
+ hdev->req_result = err;
+@@ -356,23 +146,22 @@
+ }
+
+ /* Execute request and wait for completion. */
+-static int __hci_request(struct hci_dev *hdev, void (*req)(struct hci_dev *hdev, unsigned long opt),
+- unsigned long opt, __u32 timeout)
++static int __hci_request(struct hci_dev *hdev, void (*req)(struct hci_dev *hdev, unsigned long opt), unsigned long opt, __u32 timeout)
+ {
+ DECLARE_WAITQUEUE(wait, current);
+ int err = 0;
+
+- DBG("%s start", hdev->name);
++ BT_DBG("%s start", hdev->name);
+
+ hdev->req_status = HCI_REQ_PEND;
+
+ add_wait_queue(&hdev->req_wait_q, &wait);
+- current->state = TASK_INTERRUPTIBLE;
++ set_current_state(TASK_INTERRUPTIBLE);
+
+ req(hdev, opt);
+ schedule_timeout(timeout);
+
+- current->state = TASK_RUNNING;
++ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&hdev->req_wait_q, &wait);
+
+ if (signal_pending(current))
+@@ -394,7 +183,7 @@
+
+ hdev->req_status = hdev->req_result = 0;
+
+- DBG("%s end: err %d", hdev->name, err);
++ BT_DBG("%s end: err %d", hdev->name, err);
+
+ return err;
+ }
+@@ -412,10 +201,9 @@
+ return ret;
+ }
+
+-/* --------- HCI requests ---------- */
+ static void hci_reset_req(struct hci_dev *hdev, unsigned long opt)
+ {
+- DBG("%s %ld", hdev->name, opt);
++ BT_DBG("%s %ld", hdev->name, opt);
+
+ /* Reset device */
+ hci_send_cmd(hdev, OGF_HOST_CTL, OCF_RESET, 0, NULL);
+@@ -423,27 +211,44 @@
+
+ static void hci_init_req(struct hci_dev *hdev, unsigned long opt)
+ {
+- set_event_flt_cp ec;
++ set_event_flt_cp ef;
+ __u16 param;
+
+- DBG("%s %ld", hdev->name, opt);
++ BT_DBG("%s %ld", hdev->name, opt);
+
+ /* Mandatory initialization */
+
++ /* Reset */
++ if (test_bit(HCI_QUIRK_RESET_ON_INIT, &hdev->quirks))
++ hci_send_cmd(hdev, OGF_HOST_CTL, OCF_RESET, 0, NULL);
++
+ /* Read Local Supported Features */
+ hci_send_cmd(hdev, OGF_INFO_PARAM, OCF_READ_LOCAL_FEATURES, 0, NULL);
+
+ /* Read Buffer Size (ACL mtu, max pkt, etc.) */
+ hci_send_cmd(hdev, OGF_INFO_PARAM, OCF_READ_BUFFER_SIZE, 0, NULL);
+
++#if 0
++ /* Host buffer size */
++ {
++ host_buffer_size_cp bs;
++ bs.acl_mtu = __cpu_to_le16(HCI_MAX_ACL_SIZE);
++ bs.sco_mtu = HCI_MAX_SCO_SIZE;
++ bs.acl_max_pkt = __cpu_to_le16(0xffff);
++ bs.sco_max_pkt = __cpu_to_le16(0xffff);
++ hci_send_cmd(hdev, OGF_HOST_CTL, OCF_HOST_BUFFER_SIZE,
++ HOST_BUFFER_SIZE_CP_SIZE, &bs);
++ }
++#endif
++
+ /* Read BD Address */
+ hci_send_cmd(hdev, OGF_INFO_PARAM, OCF_READ_BD_ADDR, 0, NULL);
+
+ /* Optional initialization */
+
+ /* Clear Event Filters */
+- ec.flt_type = FLT_CLEAR_ALL;
+- hci_send_cmd(hdev, OGF_HOST_CTL, OCF_SET_EVENT_FLT, 1, &ec);
++ ef.flt_type = FLT_CLEAR_ALL;
++ hci_send_cmd(hdev, OGF_HOST_CTL, OCF_SET_EVENT_FLT, 1, &ef);
+
+ /* Page timeout ~20 secs */
+ param = __cpu_to_le16(0x8000);
+@@ -458,7 +263,7 @@
+ {
+ __u8 scan = opt;
+
+- DBG("%s %x", hdev->name, scan);
++ BT_DBG("%s %x", hdev->name, scan);
+
+ /* Inquiry and Page scans */
+ hci_send_cmd(hdev, OGF_HOST_CTL, OCF_WRITE_SCAN_ENABLE, 1, &scan);
+@@ -468,116 +273,273 @@
+ {
+ __u8 auth = opt;
+
+- DBG("%s %x", hdev->name, auth);
++ BT_DBG("%s %x", hdev->name, auth);
+
+ /* Authentication */
+ hci_send_cmd(hdev, OGF_HOST_CTL, OCF_WRITE_AUTH_ENABLE, 1, &auth);
+ }
+
+-static void hci_inq_req(struct hci_dev *hdev, unsigned long opt)
++static void hci_encrypt_req(struct hci_dev *hdev, unsigned long opt)
+ {
+- struct hci_inquiry_req *ir = (struct hci_inquiry_req *) opt;
+- inquiry_cp ic;
++ __u8 encrypt = opt;
+
+- DBG("%s", hdev->name);
++ BT_DBG("%s %x", hdev->name, encrypt);
+
+- /* Start Inquiry */
+- memcpy(&ic.lap, &ir->lap, 3);
+- ic.lenght = ir->length;
+- ic.num_rsp = ir->num_rsp;
+- hci_send_cmd(hdev, OGF_LINK_CTL, OCF_INQUIRY, INQUIRY_CP_SIZE, &ic);
++ /* Authentication */
++ hci_send_cmd(hdev, OGF_HOST_CTL, OCF_WRITE_ENCRYPT_MODE, 1, &encrypt);
+ }
+
+-/* HCI ioctl helpers */
+-int hci_dev_open(__u16 dev)
++/* Get HCI device by index.
++ * Device is locked on return. */
++struct hci_dev *hci_dev_get(int index)
+ {
+ struct hci_dev *hdev;
+- int ret = 0;
+-
+- if (!(hdev = hci_dev_get(dev)))
+- return -ENODEV;
++ struct list_head *p;
+
+- DBG("%s %p", hdev->name, hdev);
++ BT_DBG("%d", index);
+
+- hci_req_lock(hdev);
++ if (index < 0)
++ return NULL;
+
+- if (hdev->flags & HCI_UP) {
+- ret = -EALREADY;
+- goto done;
++ read_lock(&hdev_list_lock);
++ list_for_each(p, &hdev_list) {
++ hdev = list_entry(p, struct hci_dev, list);
++ if (hdev->id == index) {
++ hci_dev_hold(hdev);
++ goto done;
++ }
+ }
++ hdev = NULL;
++done:
++ read_unlock(&hdev_list_lock);
++ return hdev;
++}
+
+- if (hdev->open(hdev)) {
+- ret = -EIO;
+- goto done;
+- }
++/* ---- Inquiry support ---- */
++void inquiry_cache_flush(struct hci_dev *hdev)
++{
++ struct inquiry_cache *cache = &hdev->inq_cache;
++ struct inquiry_entry *next = cache->list, *e;
+
+- if (hdev->flags & HCI_NORMAL) {
+- atomic_set(&hdev->cmd_cnt, 1);
+- hdev->flags |= HCI_INIT;
++ BT_DBG("cache %p", cache);
+
+- //__hci_request(hdev, hci_reset_req, 0, HZ);
+- ret = __hci_request(hdev, hci_init_req, 0, HCI_INIT_TIMEOUT);
+-
+- hdev->flags &= ~HCI_INIT;
++ cache->list = NULL;
++ while ((e = next)) {
++ next = e->next;
++ kfree(e);
+ }
++}
+
+- if (!ret) {
+- hdev->flags |= HCI_UP;
+- hci_notify(hdev, HCI_DEV_UP);
+- } else {
+- /* Init failed, cleanup */
+- tasklet_kill(&hdev->rx_task);
+- tasklet_kill(&hdev->tx_task);
+- tasklet_kill(&hdev->cmd_task);
++struct inquiry_entry *inquiry_cache_lookup(struct hci_dev *hdev, bdaddr_t *bdaddr)
++{
++ struct inquiry_cache *cache = &hdev->inq_cache;
++ struct inquiry_entry *e;
+
+- skb_queue_purge(&hdev->cmd_q);
+- skb_queue_purge(&hdev->rx_q);
++ BT_DBG("cache %p, %s", cache, batostr(bdaddr));
+
+- if (hdev->flush)
+- hdev->flush(hdev);
++ for (e = cache->list; e; e = e->next)
++ if (!bacmp(&e->info.bdaddr, bdaddr))
++ break;
++ return e;
++}
+
+- if (hdev->sent_cmd) {
+- kfree_skb(hdev->sent_cmd);
+- hdev->sent_cmd = NULL;
+- }
++void inquiry_cache_update(struct hci_dev *hdev, inquiry_info *info)
++{
++ struct inquiry_cache *cache = &hdev->inq_cache;
++ struct inquiry_entry *e;
+
+- hdev->close(hdev);
+- }
++ BT_DBG("cache %p, %s", cache, batostr(&info->bdaddr));
+
+-done:
+- hci_req_unlock(hdev);
+- hci_dev_put(hdev);
++ if (!(e = inquiry_cache_lookup(hdev, &info->bdaddr))) {
++ /* Entry not in the cache. Add new one. */
++ if (!(e = kmalloc(sizeof(struct inquiry_entry), GFP_ATOMIC)))
++ return;
++ memset(e, 0, sizeof(struct inquiry_entry));
++ e->next = cache->list;
++ cache->list = e;
++ }
+
+- return ret;
++ memcpy(&e->info, info, sizeof(inquiry_info));
++ e->timestamp = jiffies;
++ cache->timestamp = jiffies;
+ }
+
+-int hci_dev_close(__u16 dev)
++int inquiry_cache_dump(struct hci_dev *hdev, int num, __u8 *buf)
+ {
+- struct hci_dev *hdev;
+-
+- if (!(hdev = hci_dev_get(dev)))
+- return -ENODEV;
++ struct inquiry_cache *cache = &hdev->inq_cache;
++ inquiry_info *info = (inquiry_info *) buf;
++ struct inquiry_entry *e;
++ int copied = 0;
+
+- DBG("%s %p", hdev->name, hdev);
++ for (e = cache->list; e && copied < num; e = e->next, copied++)
++ memcpy(info++, &e->info, sizeof(inquiry_info));
+
+- hci_req_cancel(hdev, ENODEV);
+- hci_req_lock(hdev);
++ BT_DBG("cache %p, copied %d", cache, copied);
++ return copied;
++}
+
+- if (!(hdev->flags & HCI_UP))
+- goto done;
++static void hci_inq_req(struct hci_dev *hdev, unsigned long opt)
++{
++ struct hci_inquiry_req *ir = (struct hci_inquiry_req *) opt;
++ inquiry_cp ic;
+
+- /* Kill RX and TX tasks */
+- tasklet_kill(&hdev->rx_task);
+- tasklet_kill(&hdev->tx_task);
++ BT_DBG("%s", hdev->name);
+
+- inquiry_cache_flush(&hdev->inq_cache);
++ if (test_bit(HCI_INQUIRY, &hdev->flags))
++ return;
+
+- hci_conn_hash_flush(hdev);
++ /* Start Inquiry */
++ memcpy(&ic.lap, &ir->lap, 3);
++ ic.length = ir->length;
++ ic.num_rsp = ir->num_rsp;
++ hci_send_cmd(hdev, OGF_LINK_CTL, OCF_INQUIRY, INQUIRY_CP_SIZE, &ic);
++}
+
+- /* Clear flags */
+- hdev->flags &= HCI_SOCK;
+- hdev->flags |= HCI_NORMAL;
++int hci_inquiry(unsigned long arg)
++{
++ struct hci_inquiry_req ir;
++ struct hci_dev *hdev;
++ int err = 0, do_inquiry = 0, max_rsp;
++ long timeo;
++ __u8 *buf, *ptr;
+
++ ptr = (void *) arg;
++ if (copy_from_user(&ir, ptr, sizeof(ir)))
++ return -EFAULT;
++
++ if (!(hdev = hci_dev_get(ir.dev_id)))
++ return -ENODEV;
++
++ hci_dev_lock_bh(hdev);
++ if (inquiry_cache_age(hdev) > INQUIRY_CACHE_AGE_MAX ||
++ inquiry_cache_empty(hdev) ||
++ ir.flags & IREQ_CACHE_FLUSH) {
++ inquiry_cache_flush(hdev);
++ do_inquiry = 1;
++ }
++ hci_dev_unlock_bh(hdev);
++
++ timeo = ir.length * 2 * HZ;
++ if (do_inquiry && (err = hci_request(hdev, hci_inq_req, (unsigned long)&ir, timeo)) < 0)
++ goto done;
++
++ /* for unlimited number of responses we will use buffer with 255 entries */
++ max_rsp = (ir.num_rsp == 0) ? 255 : ir.num_rsp;
++
++ /* cache_dump can't sleep. Therefore we allocate temp buffer and then
++ * copy it to the user space.
++ */
++ if (!(buf = kmalloc(sizeof(inquiry_info) * max_rsp, GFP_KERNEL))) {
++ err = -ENOMEM;
++ goto done;
++ }
++
++ hci_dev_lock_bh(hdev);
++ ir.num_rsp = inquiry_cache_dump(hdev, max_rsp, buf);
++ hci_dev_unlock_bh(hdev);
++
++ BT_DBG("num_rsp %d", ir.num_rsp);
++
++ if (!verify_area(VERIFY_WRITE, ptr, sizeof(ir) +
++ (sizeof(inquiry_info) * ir.num_rsp))) {
++ copy_to_user(ptr, &ir, sizeof(ir));
++ ptr += sizeof(ir);
++ copy_to_user(ptr, buf, sizeof(inquiry_info) * ir.num_rsp);
++ } else
++ err = -EFAULT;
++
++ kfree(buf);
++
++done:
++ hci_dev_put(hdev);
++ return err;
++}
++
++/* ---- HCI ioctl helpers ---- */
++
++int hci_dev_open(__u16 dev)
++{
++ struct hci_dev *hdev;
++ int ret = 0;
++
++ if (!(hdev = hci_dev_get(dev)))
++ return -ENODEV;
++
++ BT_DBG("%s %p", hdev->name, hdev);
++
++ hci_req_lock(hdev);
++
++ if (test_bit(HCI_UP, &hdev->flags)) {
++ ret = -EALREADY;
++ goto done;
++ }
++
++ if (hdev->open(hdev)) {
++ ret = -EIO;
++ goto done;
++ }
++
++ if (!test_bit(HCI_RAW, &hdev->flags)) {
++ atomic_set(&hdev->cmd_cnt, 1);
++ set_bit(HCI_INIT, &hdev->flags);
++
++ //__hci_request(hdev, hci_reset_req, 0, HZ);
++ ret = __hci_request(hdev, hci_init_req, 0, HCI_INIT_TIMEOUT);
++
++ clear_bit(HCI_INIT, &hdev->flags);
++ }
++
++ if (!ret) {
++ set_bit(HCI_UP, &hdev->flags);
++ hci_notify(hdev, HCI_DEV_UP);
++ } else {
++ /* Init failed, cleanup */
++ tasklet_kill(&hdev->rx_task);
++ tasklet_kill(&hdev->tx_task);
++ tasklet_kill(&hdev->cmd_task);
++
++ skb_queue_purge(&hdev->cmd_q);
++ skb_queue_purge(&hdev->rx_q);
++
++ if (hdev->flush)
++ hdev->flush(hdev);
++
++ if (hdev->sent_cmd) {
++ kfree_skb(hdev->sent_cmd);
++ hdev->sent_cmd = NULL;
++ }
++
++ hdev->close(hdev);
++ hdev->flags = 0;
++ }
++
++done:
++ hci_req_unlock(hdev);
++ hci_dev_put(hdev);
++ return ret;
++}
++
++static int hci_dev_do_close(struct hci_dev *hdev)
++{
++ BT_DBG("%s %p", hdev->name, hdev);
++
++ hci_req_cancel(hdev, ENODEV);
++ hci_req_lock(hdev);
++
++ if (!test_and_clear_bit(HCI_UP, &hdev->flags)) {
++ hci_req_unlock(hdev);
++ return 0;
++ }
++
++ /* Kill RX and TX tasks */
++ tasklet_kill(&hdev->rx_task);
++ tasklet_kill(&hdev->tx_task);
++
++ hci_dev_lock_bh(hdev);
++ inquiry_cache_flush(hdev);
++ hci_conn_hash_flush(hdev);
++ hci_dev_unlock_bh(hdev);
++
+ hci_notify(hdev, HCI_DEV_DOWN);
+
+ if (hdev->flush)
+@@ -586,9 +548,9 @@
+ /* Reset device */
+ skb_queue_purge(&hdev->cmd_q);
+ atomic_set(&hdev->cmd_cnt, 1);
+- hdev->flags |= HCI_INIT;
+- __hci_request(hdev, hci_reset_req, 0, HZ);
+- hdev->flags &= ~HCI_INIT;
++ set_bit(HCI_INIT, &hdev->flags);
++ __hci_request(hdev, hci_reset_req, 0, HZ/4);
++ clear_bit(HCI_INIT, &hdev->flags);
+
+ /* Kill cmd task */
+ tasklet_kill(&hdev->cmd_task);
+@@ -605,17 +567,28 @@
+ }
+
+ /* After this point our queues are empty
+- * and no tasks are scheduled.
+- */
++ * and no tasks are scheduled. */
+ hdev->close(hdev);
+
+-done:
+- hci_req_unlock(hdev);
+- hci_dev_put(hdev);
++ /* Clear flags */
++ hdev->flags = 0;
+
++ hci_req_unlock(hdev);
+ return 0;
+ }
+
++int hci_dev_close(__u16 dev)
++{
++ struct hci_dev *hdev;
++ int err;
++
++ if (!(hdev = hci_dev_get(dev)))
++ return -ENODEV;
++ err = hci_dev_do_close(hdev);
++ hci_dev_put(hdev);
++ return err;
++}
++
+ int hci_dev_reset(__u16 dev)
+ {
+ struct hci_dev *hdev;
+@@ -627,16 +600,17 @@
+ hci_req_lock(hdev);
+ tasklet_disable(&hdev->tx_task);
+
+- if (!(hdev->flags & HCI_UP))
++ if (!test_bit(HCI_UP, &hdev->flags))
+ goto done;
+
+ /* Drop queues */
+ skb_queue_purge(&hdev->rx_q);
+ skb_queue_purge(&hdev->cmd_q);
+
+- inquiry_cache_flush(&hdev->inq_cache);
+-
++ hci_dev_lock_bh(hdev);
++ inquiry_cache_flush(hdev);
+ hci_conn_hash_flush(hdev);
++ hci_dev_unlock_bh(hdev);
+
+ if (hdev->flush)
+ hdev->flush(hdev);
+@@ -650,7 +624,6 @@
+ tasklet_enable(&hdev->tx_task);
+ hci_req_unlock(hdev);
+ hci_dev_put(hdev);
+-
+ return ret;
+ }
+
+@@ -669,30 +642,11 @@
+ return ret;
+ }
+
+-int hci_dev_setauth(unsigned long arg)
+-{
+- struct hci_dev *hdev;
+- struct hci_dev_req dr;
+- int ret = 0;
+-
+- if (copy_from_user(&dr, (void *) arg, sizeof(dr)))
+- return -EFAULT;
+-
+- if (!(hdev = hci_dev_get(dr.dev_id)))
+- return -ENODEV;
+-
+- ret = hci_request(hdev, hci_auth_req, dr.dev_opt, HCI_INIT_TIMEOUT);
+-
+- hci_dev_put(hdev);
+-
+- return ret;
+-}
+-
+-int hci_dev_setscan(unsigned long arg)
++int hci_dev_cmd(unsigned int cmd, unsigned long arg)
+ {
+ struct hci_dev *hdev;
+ struct hci_dev_req dr;
+- int ret = 0;
++ int err = 0;
+
+ if (copy_from_user(&dr, (void *) arg, sizeof(dr)))
+ return -EFAULT;
+@@ -700,75 +654,105 @@
+ if (!(hdev = hci_dev_get(dr.dev_id)))
+ return -ENODEV;
+
+- ret = hci_request(hdev, hci_scan_req, dr.dev_opt, HCI_INIT_TIMEOUT);
+-
+- hci_dev_put(hdev);
++ switch (cmd) {
++ case HCISETAUTH:
++ err = hci_request(hdev, hci_auth_req, dr.dev_opt, HCI_INIT_TIMEOUT);
++ break;
+
+- return ret;
+-}
++ case HCISETENCRYPT:
++ if (!lmp_encrypt_capable(hdev)) {
++ err = -EOPNOTSUPP;
++ break;
++ }
+
+-int hci_dev_setptype(unsigned long arg)
+-{
+- struct hci_dev *hdev;
+- struct hci_dev_req dr;
+- int ret = 0;
++ if (!test_bit(HCI_AUTH, &hdev->flags)) {
++ /* Auth must be enabled first */
++ err = hci_request(hdev, hci_auth_req,
++ dr.dev_opt, HCI_INIT_TIMEOUT);
++ if (err)
++ break;
++ }
++
++ err = hci_request(hdev, hci_encrypt_req,
++ dr.dev_opt, HCI_INIT_TIMEOUT);
++ break;
++
++ case HCISETSCAN:
++ err = hci_request(hdev, hci_scan_req, dr.dev_opt, HCI_INIT_TIMEOUT);
++ break;
++
++ case HCISETPTYPE:
++ hdev->pkt_type = (__u16) dr.dev_opt;
++ break;
++
++ case HCISETLINKPOL:
++ hdev->link_policy = (__u16) dr.dev_opt;
++ break;
+
+- if (copy_from_user(&dr, (void *) arg, sizeof(dr)))
+- return -EFAULT;
++ case HCISETLINKMODE:
++ hdev->link_mode = ((__u16) dr.dev_opt) & (HCI_LM_MASTER | HCI_LM_ACCEPT);
++ break;
+
+- if (!(hdev = hci_dev_get(dr.dev_id)))
+- return -ENODEV;
++ case HCISETACLMTU:
++ hdev->acl_mtu = *((__u16 *)&dr.dev_opt + 1);
++ hdev->acl_pkts = *((__u16 *)&dr.dev_opt + 0);
++ break;
+
+- hdev->pkt_type = (__u16) dr.dev_opt;
++ case HCISETSCOMTU:
++ hdev->sco_mtu = *((__u16 *)&dr.dev_opt + 1);
++ hdev->sco_pkts = *((__u16 *)&dr.dev_opt + 0);
++ break;
+
++ default:
++ err = -EINVAL;
++ break;
++ }
+ hci_dev_put(hdev);
+-
+- return ret;
++ return err;
+ }
+
+-int hci_dev_list(unsigned long arg)
++int hci_get_dev_list(unsigned long arg)
+ {
+ struct hci_dev_list_req *dl;
+ struct hci_dev_req *dr;
+- struct hci_dev *hdev;
+- int i, n, size;
++ struct list_head *p;
++ int n = 0, size, err;
+ __u16 dev_num;
+
+ if (get_user(dev_num, (__u16 *) arg))
+ return -EFAULT;
+
+- /* Avoid long loop, overflow */
+- if (dev_num > 2048)
++ if (!dev_num || dev_num > (PAGE_SIZE * 2) / sizeof(*dr))
+ return -EINVAL;
+-
+- size = dev_num * sizeof(struct hci_dev_req) + sizeof(__u16);
+
+- if (verify_area(VERIFY_WRITE, (void *) arg, size))
+- return -EFAULT;
++ size = sizeof(*dl) + dev_num * sizeof(*dr);
+
+ if (!(dl = kmalloc(size, GFP_KERNEL)))
+ return -ENOMEM;
++
+ dr = dl->dev_req;
+
+- spin_lock_bh(&hdev_list_lock);
+- for (i = 0, n = 0; i < HCI_MAX_DEV && n < dev_num; i++) {
+- if ((hdev = hdev_list[i])) {
+- (dr + n)->dev_id = hdev->id;
+- (dr + n)->dev_opt = hdev->flags;
+- n++;
+- }
++ read_lock_bh(&hdev_list_lock);
++ list_for_each(p, &hdev_list) {
++ struct hci_dev *hdev;
++ hdev = list_entry(p, struct hci_dev, list);
++ (dr + n)->dev_id = hdev->id;
++ (dr + n)->dev_opt = hdev->flags;
++ if (++n >= dev_num)
++ break;
+ }
+- spin_unlock_bh(&hdev_list_lock);
++ read_unlock_bh(&hdev_list_lock);
+
+ dl->dev_num = n;
+- size = n * sizeof(struct hci_dev_req) + sizeof(__u16);
++ size = sizeof(*dl) + n * sizeof(*dr);
+
+- copy_to_user((void *) arg, dl, size);
++ err = copy_to_user((void *) arg, dl, size);
++ kfree(dl);
+
+- return 0;
++ return err ? -EFAULT : 0;
+ }
+
+-int hci_dev_info(unsigned long arg)
++int hci_get_dev_info(unsigned long arg)
+ {
+ struct hci_dev *hdev;
+ struct hci_dev_info di;
+@@ -786,9 +770,11 @@
+ di.flags = hdev->flags;
+ di.pkt_type = hdev->pkt_type;
+ di.acl_mtu = hdev->acl_mtu;
+- di.acl_max = hdev->acl_max;
++ di.acl_pkts = hdev->acl_pkts;
+ di.sco_mtu = hdev->sco_mtu;
+- di.sco_max = hdev->sco_max;
++ di.sco_pkts = hdev->sco_pkts;
++ di.link_policy = hdev->link_policy;
++ di.link_mode = hdev->link_mode;
+
+ memcpy(&di.stat, &hdev->stat, sizeof(di.stat));
+ memcpy(&di.features, &hdev->features, sizeof(di.features));
+@@ -801,258 +787,168 @@
+ return err;
+ }
+
+-__u32 hci_dev_setmode(struct hci_dev *hdev, __u32 mode)
+-{
+- __u32 omode = hdev->flags & HCI_MODE_MASK;
+-
+- hdev->flags &= ~HCI_MODE_MASK;
+- hdev->flags |= (mode & HCI_MODE_MASK);
+
+- return omode;
+-}
++/* ---- Interface to HCI drivers ---- */
+
+-__u32 hci_dev_getmode(struct hci_dev *hdev)
++/* Register HCI device */
++int hci_register_dev(struct hci_dev *hdev)
+ {
+- return hdev->flags & HCI_MODE_MASK;
+-}
++ struct list_head *head = &hdev_list, *p;
++ int id = 0;
+
+-int hci_conn_list(unsigned long arg)
+-{
+- struct hci_conn_list_req req, *cl;
+- struct hci_conn_info *ci;
+- struct hci_dev *hdev;
+- struct list_head *p;
+- int n = 0, size;
++ BT_DBG("%p name %s type %d", hdev, hdev->name, hdev->type);
+
+- if (copy_from_user(&req, (void *) arg, sizeof(req)))
+- return -EFAULT;
++ if (!hdev->open || !hdev->close || !hdev->destruct)
++ return -EINVAL;
+
+- if (!(hdev = hci_dev_get(req.dev_id)))
+- return -ENODEV;
++ write_lock_bh(&hdev_list_lock);
+
+- /* Set a limit to avoid overlong loops, and also numeric overflow - AC */
+- if(req.conn_num < 2048)
+- return -EINVAL;
++ /* Find first available device id */
++ list_for_each(p, &hdev_list) {
++ if (list_entry(p, struct hci_dev, list)->id != id)
++ break;
++ head = p; id++;
++ }
+
+- size = req.conn_num * sizeof(struct hci_conn_info) + sizeof(req);
++ sprintf(hdev->name, "hci%d", id);
++ hdev->id = id;
++ list_add(&hdev->list, head);
+
+- if (!(cl = kmalloc(size, GFP_KERNEL)))
+- return -ENOMEM;
+- ci = cl->conn_info;
+-
+- local_bh_disable();
+- conn_hash_lock(&hdev->conn_hash);
+- list_for_each(p, &hdev->conn_hash.list) {
+- register struct hci_conn *c;
+- c = list_entry(p, struct hci_conn, list);
++ atomic_set(&hdev->refcnt, 1);
++ spin_lock_init(&hdev->lock);
++
++ hdev->flags = 0;
++ hdev->pkt_type = (HCI_DM1 | HCI_DH1 | HCI_HV1);
++ hdev->link_mode = (HCI_LM_ACCEPT);
+
+- (ci + n)->handle = c->handle;
+- bacpy(&(ci + n)->bdaddr, &c->dst);
+- n++;
+- }
+- conn_hash_unlock(&hdev->conn_hash);
+- local_bh_enable();
+-
+- cl->dev_id = hdev->id;
+- cl->conn_num = n;
+- size = n * sizeof(struct hci_conn_info) + sizeof(req);
++ tasklet_init(&hdev->cmd_task, hci_cmd_task,(unsigned long) hdev);
++ tasklet_init(&hdev->rx_task, hci_rx_task, (unsigned long) hdev);
++ tasklet_init(&hdev->tx_task, hci_tx_task, (unsigned long) hdev);
+
+- hci_dev_put(hdev);
++ skb_queue_head_init(&hdev->rx_q);
++ skb_queue_head_init(&hdev->cmd_q);
++ skb_queue_head_init(&hdev->raw_q);
+
+- if(copy_to_user((void *) arg, cl, size))
+- return -EFAULT;
+- return 0;
+-}
++ init_waitqueue_head(&hdev->req_wait_q);
++ init_MUTEX(&hdev->req_lock);
+
+-int hci_inquiry(unsigned long arg)
+-{
+- struct inquiry_cache *cache;
+- struct hci_inquiry_req ir;
+- struct hci_dev *hdev;
+- int err = 0, do_inquiry = 0;
+- long timeo;
+- __u8 *buf, *ptr;
++ inquiry_cache_init(hdev);
+
+- ptr = (void *) arg;
+- if (copy_from_user(&ir, ptr, sizeof(ir)))
+- return -EFAULT;
++ conn_hash_init(hdev);
+
+- if (!(hdev = hci_dev_get(ir.dev_id)))
+- return -ENODEV;
++ memset(&hdev->stat, 0, sizeof(struct hci_dev_stats));
+
+- cache = &hdev->inq_cache;
++ atomic_set(&hdev->promisc, 0);
+
+- inquiry_cache_lock(cache);
+- if (inquiry_cache_age(cache) > INQUIRY_CACHE_AGE_MAX || ir.flags & IREQ_CACHE_FLUSH) {
+- inquiry_cache_flush(cache);
+- do_inquiry = 1;
+- }
+- inquiry_cache_unlock(cache);
++ MOD_INC_USE_COUNT;
+
+- /* Limit inquiry time, also avoid overflows */
++ write_unlock_bh(&hdev_list_lock);
+
+- if(ir.length > 2048 || ir.num_rsp > 2048)
+- {
+- err = -EINVAL;
+- goto done;
+- }
++ hci_notify(hdev, HCI_DEV_REG);
++ hci_run_hotplug(hdev->name, "register");
+
+- timeo = ir.length * 2 * HZ;
+- if (do_inquiry && (err = hci_request(hdev, hci_inq_req, (unsigned long)&ir, timeo)) < 0)
+- goto done;
++ return id;
++}
+
+- /* cache_dump can't sleep. Therefore we allocate temp buffer and then
+- * copy it to the user space.
+- */
+- if (!(buf = kmalloc(sizeof(inquiry_info) * ir.num_rsp, GFP_KERNEL))) {
+- err = -ENOMEM;
+- goto done;
+- }
+- ir.num_rsp = inquiry_cache_dump(cache, ir.num_rsp, buf);
++/* Unregister HCI device */
++int hci_unregister_dev(struct hci_dev *hdev)
++{
++ BT_DBG("%p name %s type %d", hdev, hdev->name, hdev->type);
+
+- DBG("num_rsp %d", ir.num_rsp);
++ write_lock_bh(&hdev_list_lock);
++ list_del(&hdev->list);
++ write_unlock_bh(&hdev_list_lock);
+
+- if (!verify_area(VERIFY_WRITE, ptr, sizeof(ir) + (sizeof(inquiry_info) * ir.num_rsp))) {
+- copy_to_user(ptr, &ir, sizeof(ir));
+- ptr += sizeof(ir);
+- copy_to_user(ptr, buf, sizeof(inquiry_info) * ir.num_rsp);
+- } else
+- err = -EFAULT;
++ hci_dev_do_close(hdev);
+
+- kfree(buf);
++ hci_notify(hdev, HCI_DEV_UNREG);
++ hci_run_hotplug(hdev->name, "unregister");
+
+-done:
+ hci_dev_put(hdev);
+
+- return err;
++ MOD_DEC_USE_COUNT;
++ return 0;
+ }
+
+-/* Interface to HCI drivers */
+-
+-/* Register HCI device */
+-int hci_register_dev(struct hci_dev *hdev)
++/* Suspend HCI device */
++int hci_suspend_dev(struct hci_dev *hdev)
+ {
+- int i;
++ hci_notify(hdev, HCI_DEV_SUSPEND);
++ hci_run_hotplug(hdev->name, "suspend");
++ return 0;
++}
+
+- DBG("%p name %s type %d", hdev, hdev->name, hdev->type);
++/* Resume HCI device */
++int hci_resume_dev(struct hci_dev *hdev)
++{
++ hci_notify(hdev, HCI_DEV_RESUME);
++ hci_run_hotplug(hdev->name, "resume");
++ return 0;
++}
+
+- /* Find free slot */
+- spin_lock_bh(&hdev_list_lock);
+- for (i = 0; i < HCI_MAX_DEV; i++) {
+- if (!hdev_list[i]) {
+- hdev_list[i] = hdev;
+-
+- sprintf(hdev->name, "hci%d", i);
+- atomic_set(&hdev->refcnt, 0);
+- hdev->id = i;
+- hdev->flags = HCI_NORMAL;
+-
+- hdev->pkt_type = (HCI_DM1 | HCI_DH1);
+-
+- tasklet_init(&hdev->cmd_task, hci_cmd_task, (unsigned long) hdev);
+- tasklet_init(&hdev->rx_task, hci_rx_task, (unsigned long) hdev);
+- tasklet_init(&hdev->tx_task, hci_tx_task, (unsigned long) hdev);
+-
+- skb_queue_head_init(&hdev->rx_q);
+- skb_queue_head_init(&hdev->cmd_q);
+- skb_queue_head_init(&hdev->raw_q);
+-
+- init_waitqueue_head(&hdev->req_wait_q);
+- init_MUTEX(&hdev->req_lock);
+-
+- inquiry_cache_init(&hdev->inq_cache);
+-
+- conn_hash_init(&hdev->conn_hash);
+-
+- memset(&hdev->stat, 0, sizeof(struct hci_dev_stats));
+-
+- hci_notify(hdev, HCI_DEV_REG);
+-
+- MOD_INC_USE_COUNT;
+- break;
+- }
+- }
+- spin_unlock_bh(&hdev_list_lock);
+-
+- return (i == HCI_MAX_DEV) ? -1 : i;
+-}
+-
+-/* Unregister HCI device */
+-int hci_unregister_dev(struct hci_dev *hdev)
++/* Receive frame from HCI drivers */
++int hci_recv_frame(struct sk_buff *skb)
+ {
+- int i;
+-
+- DBG("%p name %s type %d", hdev, hdev->name, hdev->type);
+-
+- if (hdev->flags & HCI_UP)
+- hci_dev_close(hdev->id);
++ struct hci_dev *hdev = (struct hci_dev *) skb->dev;
+
+- /* Find device slot */
+- spin_lock(&hdev_list_lock);
+- for (i = 0; i < HCI_MAX_DEV; i++) {
+- if (hdev_list[i] == hdev) {
+- hdev_list[i] = NULL;
+- MOD_DEC_USE_COUNT;
+- break;
+- }
++ if (!hdev || (!test_bit(HCI_UP, &hdev->flags) &&
++ !test_bit(HCI_INIT, &hdev->flags)) ) {
++ kfree_skb(skb);
++ return -1;
+ }
+- spin_unlock(&hdev_list_lock);
+
+- hci_notify(hdev, HCI_DEV_UNREG);
+-
+- /* Sleep while device is in use */
+- while (atomic_read(&hdev->refcnt)) {
+- int sleep_cnt = 100;
++ BT_DBG("%s type %d len %d", hdev->name, skb->pkt_type, skb->len);
+
+- DBG("%s sleeping on lock %d", hdev->name, atomic_read(&hdev->refcnt));
++ /* Incomming skb */
++ bluez_cb(skb)->incomming = 1;
+
+- sleep_on_timeout(&hdev->req_wait_q, HZ*10);
+- if (!(--sleep_cnt))
+- break;
+- }
++ /* Time stamp */
++ do_gettimeofday(&skb->stamp);
+
++ /* Queue frame for rx task */
++ skb_queue_tail(&hdev->rx_q, skb);
++ hci_sched_rx(hdev);
+ return 0;
+ }
+
+-/* Interface to upper protocols */
++/* ---- Interface to upper protocols ---- */
+
+ /* Register/Unregister protocols.
+- * hci_task_lock is used to ensure that no tasks are running.
+- */
+-int hci_register_proto(struct hci_proto *hproto)
++ * hci_task_lock is used to ensure that no tasks are running. */
++int hci_register_proto(struct hci_proto *hp)
+ {
+ int err = 0;
+
+- DBG("%p name %s", hproto, hproto->name);
++ BT_DBG("%p name %s id %d", hp, hp->name, hp->id);
+
+- if (hproto->id >= HCI_MAX_PROTO)
++ if (hp->id >= HCI_MAX_PROTO)
+ return -EINVAL;
+
+ write_lock_bh(&hci_task_lock);
+
+- if (!hproto_list[hproto->id])
+- hproto_list[hproto->id] = hproto;
++ if (!hci_proto[hp->id])
++ hci_proto[hp->id] = hp;
+ else
+- err = -1;
++ err = -EEXIST;
+
+ write_unlock_bh(&hci_task_lock);
+
+ return err;
+ }
+
+-int hci_unregister_proto(struct hci_proto *hproto)
++int hci_unregister_proto(struct hci_proto *hp)
+ {
+ int err = 0;
+
+- DBG("%p name %s", hproto, hproto->name);
++ BT_DBG("%p name %s id %d", hp, hp->name, hp->id);
+
+- if (hproto->id > HCI_MAX_PROTO)
++ if (hp->id >= HCI_MAX_PROTO)
+ return -EINVAL;
+
+ write_lock_bh(&hci_task_lock);
+
+- if (hproto_list[hproto->id])
+- hproto_list[hproto->id] = NULL;
++ if (hci_proto[hp->id])
++ hci_proto[hp->id] = NULL;
+ else
+ err = -ENOENT;
+
+@@ -1070,10 +966,14 @@
+ return -ENODEV;
+ }
+
+- DBG("%s type %d len %d", hdev->name, skb->pkt_type, skb->len);
++ BT_DBG("%s type %d len %d", hdev->name, skb->pkt_type, skb->len);
++
++ if (atomic_read(&hdev->promisc)) {
++ /* Time stamp */
++ do_gettimeofday(&skb->stamp);
+
+- if (hdev->flags & HCI_SOCK)
+ hci_send_to_sock(hdev, skb);
++ }
+
+ /* Get rid of skb owner, prior to sending to the driver. */
+ skb_orphan(skb);
+@@ -1081,128 +981,6 @@
+ return hdev->send(skb);
+ }
+
+-/* Connection scheduler */
+-static inline struct hci_conn *hci_low_sent(struct hci_dev *hdev, __u8 type, int *quote)
+-{
+- struct conn_hash *h = &hdev->conn_hash;
+- struct hci_conn *conn = NULL;
+- int num = 0, min = 0xffff;
+- struct list_head *p;
+-
+- conn_hash_lock(h);
+- list_for_each(p, &h->list) {
+- register struct hci_conn *c;
+-
+- c = list_entry(p, struct hci_conn, list);
+-
+- if (c->type != type || skb_queue_empty(&c->data_q))
+- continue;
+- num++;
+-
+- if (c->sent < min) {
+- min = c->sent;
+- conn = c;
+- }
+- }
+- conn_hash_unlock(h);
+-
+- if (conn) {
+- int q = hdev->acl_cnt / num;
+- *quote = q ? q : 1;
+- } else
+- *quote = 0;
+-
+- DBG("conn %p quote %d", conn, *quote);
+-
+- return conn;
+-}
+-
+-static inline void hci_sched_acl(struct hci_dev *hdev)
+-{
+- struct hci_conn *conn;
+- struct sk_buff *skb;
+- int quote;
+-
+- DBG("%s", hdev->name);
+-
+- while (hdev->acl_cnt && (conn = hci_low_sent(hdev, ACL_LINK, &quote))) {
+- while (quote && (skb = skb_dequeue(&conn->data_q))) {
+- DBG("skb %p len %d", skb, skb->len);
+-
+- hci_send_frame(skb);
+-
+- conn->sent++;
+- hdev->acl_cnt--;
+- quote--;
+- }
+- }
+-}
+-
+-/* Schedule SCO */
+-static inline void hci_sched_sco(struct hci_dev *hdev)
+-{
+- /* FIXME: For now we queue SCO packets to the raw queue
+-
+- while (hdev->sco_cnt && (skb = skb_dequeue(&conn->data_q))) {
+- hci_send_frame(skb);
+- conn->sco_sent++;
+- hdev->sco_cnt--;
+- }
+- */
+-}
+-
+-/* Get data from the previously sent command */
+-static void * hci_sent_cmd_data(struct hci_dev *hdev, __u16 ogf, __u16 ocf)
+-{
+- hci_command_hdr *hc;
+-
+- if (!hdev->sent_cmd)
+- return NULL;
+-
+- hc = (void *) hdev->sent_cmd->data;
+-
+- if (hc->opcode != __cpu_to_le16(cmd_opcode_pack(ogf, ocf)))
+- return NULL;
+-
+- DBG("%s ogf 0x%x ocf 0x%x", hdev->name, ogf, ocf);
+-
+- return hdev->sent_cmd->data + HCI_COMMAND_HDR_SIZE;
+-}
+-
+-/* Send raw HCI frame */
+-int hci_send_raw(struct sk_buff *skb)
+-{
+- struct hci_dev *hdev = (struct hci_dev *) skb->dev;
+-
+- if (!hdev) {
+- kfree_skb(skb);
+- return -ENODEV;
+- }
+-
+- DBG("%s type %d len %d", hdev->name, skb->pkt_type, skb->len);
+-
+- if (hdev->flags & HCI_NORMAL) {
+- /* Queue frame according it's type */
+- switch (skb->pkt_type) {
+- case HCI_COMMAND_PKT:
+- skb_queue_tail(&hdev->cmd_q, skb);
+- hci_sched_cmd(hdev);
+- return 0;
+-
+- case HCI_ACLDATA_PKT:
+- case HCI_SCODATA_PKT:
+- /* FIXME:
+- * Check header here and queue to apropriate connection.
+- */
+- break;
+- }
+- }
+-
+- skb_queue_tail(&hdev->raw_q, skb);
+- hci_sched_tx(hdev);
+- return 0;
+-}
+-
+ /* Send HCI command */
+ int hci_send_cmd(struct hci_dev *hdev, __u16 ogf, __u16 ocf, __u32 plen, void *param)
+ {
+@@ -1210,10 +988,10 @@
+ hci_command_hdr *hc;
+ struct sk_buff *skb;
+
+- DBG("%s ogf 0x%x ocf 0x%x plen %d", hdev->name, ogf, ocf, plen);
++ BT_DBG("%s ogf 0x%x ocf 0x%x plen %d", hdev->name, ogf, ocf, plen);
+
+ if (!(skb = bluez_skb_alloc(len, GFP_ATOMIC))) {
+- ERR("%s Can't allocate memory for HCI command", hdev->name);
++ BT_ERR("%s Can't allocate memory for HCI command", hdev->name);
+ return -ENOMEM;
+ }
+
+@@ -1224,7 +1002,7 @@
+ if (plen)
+ memcpy(skb_put(skb, plen), param, plen);
+
+- DBG("skb len %d", skb->len);
++ BT_DBG("skb len %d", skb->len);
+
+ skb->pkt_type = HCI_COMMAND_PKT;
+ skb->dev = (void *) hdev;
+@@ -1234,10 +1012,28 @@
+ return 0;
+ }
+
++/* Get data from the previously sent command */
++void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 ogf, __u16 ocf)
++{
++ hci_command_hdr *hc;
++
++ if (!hdev->sent_cmd)
++ return NULL;
++
++ hc = (void *) hdev->sent_cmd->data;
++
++ if (hc->opcode != __cpu_to_le16(cmd_opcode_pack(ogf, ocf)))
++ return NULL;
++
++ BT_DBG("%s ogf 0x%x ocf 0x%x", hdev->name, ogf, ocf);
++
++ return hdev->sent_cmd->data + HCI_COMMAND_HDR_SIZE;
++}
++
+ /* Send ACL data */
+ static void hci_add_acl_hdr(struct sk_buff *skb, __u16 handle, __u16 flags)
+ {
+- int len = skb->len;
++ int len = skb->len;
+ hci_acl_hdr *ah;
+
+ ah = (hci_acl_hdr *) skb_push(skb, HCI_ACL_HDR_SIZE);
+@@ -1252,7 +1048,7 @@
+ struct hci_dev *hdev = conn->hdev;
+ struct sk_buff *list;
+
+- DBG("%s conn %p flags 0x%x", hdev->name, conn, flags);
++ BT_DBG("%s conn %p flags 0x%x", hdev->name, conn, flags);
+
+ skb->dev = (void *) hdev;
+ skb->pkt_type = HCI_ACLDATA_PKT;
+@@ -1260,12 +1056,12 @@
+
+ if (!(list = skb_shinfo(skb)->frag_list)) {
+ /* Non fragmented */
+- DBG("%s nonfrag skb %p len %d", hdev->name, skb, skb->len);
++ BT_DBG("%s nonfrag skb %p len %d", hdev->name, skb, skb->len);
+
+ skb_queue_tail(&conn->data_q, skb);
+ } else {
+ /* Fragmented */
+- DBG("%s frag %p len %d", hdev->name, skb, skb->len);
++ BT_DBG("%s frag %p len %d", hdev->name, skb, skb->len);
+
+ skb_shinfo(skb)->frag_list = NULL;
+
+@@ -1280,7 +1076,7 @@
+ skb->pkt_type = HCI_ACLDATA_PKT;
+ hci_add_acl_hdr(skb, conn->handle, flags | ACL_CONT);
+
+- DBG("%s frag %p len %d", hdev->name, skb, skb->len);
++ BT_DBG("%s frag %p len %d", hdev->name, skb, skb->len);
+
+ __skb_queue_tail(&conn->data_q, skb);
+ } while (list);
+@@ -1298,7 +1094,7 @@
+ struct hci_dev *hdev = conn->hdev;
+ hci_sco_hdr hs;
+
+- DBG("%s len %d", hdev->name, skb->len);
++ BT_DBG("%s len %d", hdev->name, skb->len);
+
+ if (skb->len > hdev->sco_mtu) {
+ kfree_skb(skb);
+@@ -1315,544 +1111,136 @@
+ skb->pkt_type = HCI_SCODATA_PKT;
+ skb_queue_tail(&conn->data_q, skb);
+ hci_sched_tx(hdev);
+-
+ return 0;
+ }
+
+-/* Handle HCI Event packets */
+-
+-/* Command Complete OGF LINK_CTL */
+-static void hci_cc_link_ctl(struct hci_dev *hdev, __u16 ocf, struct sk_buff *skb)
+-{
+- DBG("%s ocf 0x%x", hdev->name, ocf);
+-
+- switch (ocf) {
+- default:
+- DBG("%s Command complete: ogf LINK_CTL ocf %x", hdev->name, ocf);
+- break;
+- };
+-}
+-
+-/* Command Complete OGF LINK_POLICY */
+-static void hci_cc_link_policy(struct hci_dev *hdev, __u16 ocf, struct sk_buff *skb)
+-{
+- DBG("%s ocf 0x%x", hdev->name, ocf);
+-
+- switch (ocf) {
+- default:
+- DBG("%s: Command complete: ogf LINK_POLICY ocf %x", hdev->name, ocf);
+- break;
+- };
+-}
+-
+-/* Command Complete OGF HOST_CTL */
+-static void hci_cc_host_ctl(struct hci_dev *hdev, __u16 ocf, struct sk_buff *skb)
+-{
+- __u8 status, param;
+- void *sent;
+-
+-
+- DBG("%s ocf 0x%x", hdev->name, ocf);
+-
+- switch (ocf) {
+- case OCF_RESET:
+- status = *((__u8 *) skb->data);
+-
+- hci_req_complete(hdev, status);
+- break;
+-
+- case OCF_SET_EVENT_FLT:
+- status = *((__u8 *) skb->data);
+-
+- if (status) {
+- DBG("%s SET_EVENT_FLT failed %d", hdev->name, status);
+- } else {
+- DBG("%s SET_EVENT_FLT succeseful", hdev->name);
+- }
+- break;
+-
+- case OCF_WRITE_AUTH_ENABLE:
+- if (!(sent = hci_sent_cmd_data(hdev, OGF_HOST_CTL, OCF_WRITE_AUTH_ENABLE)))
+- break;
+-
+- status = *((__u8 *) skb->data);
+- param = *((__u8 *) sent);
+-
+- if (!status) {
+- if (param == AUTH_ENABLED)
+- hdev->flags |= HCI_AUTH;
+- else
+- hdev->flags &= ~HCI_AUTH;
+- }
+- hci_req_complete(hdev, status);
+- break;
+-
+- case OCF_WRITE_CA_TIMEOUT:
+- status = *((__u8 *) skb->data);
+-
+- if (status) {
+- DBG("%s OCF_WRITE_CA_TIMEOUT failed %d", hdev->name, status);
+- } else {
+- DBG("%s OCF_WRITE_CA_TIMEOUT succeseful", hdev->name);
+- }
+- break;
+-
+- case OCF_WRITE_PG_TIMEOUT:
+- status = *((__u8 *) skb->data);
+-
+- if (status) {
+- DBG("%s OCF_WRITE_PG_TIMEOUT failed %d", hdev->name, status);
+- } else {
+- DBG("%s: OCF_WRITE_PG_TIMEOUT succeseful", hdev->name);
+- }
+- break;
+-
+- case OCF_WRITE_SCAN_ENABLE:
+- if (!(sent = hci_sent_cmd_data(hdev, OGF_HOST_CTL, OCF_WRITE_SCAN_ENABLE)))
+- break;
+- status = *((__u8 *) skb->data);
+- param = *((__u8 *) sent);
+-
+- DBG("param 0x%x", param);
+-
+- if (!status) {
+- switch (param) {
+- case IS_ENA_PS_ENA:
+- hdev->flags |= HCI_PSCAN | HCI_ISCAN;
+- break;
+-
+- case IS_ENA_PS_DIS:
+- hdev->flags &= ~HCI_PSCAN;
+- hdev->flags |= HCI_ISCAN;
+- break;
++/* ---- HCI TX task (outgoing data) ---- */
+
+- case IS_DIS_PS_ENA:
+- hdev->flags &= ~HCI_ISCAN;
+- hdev->flags |= HCI_PSCAN;
+- break;
+-
+- default:
+- hdev->flags &= ~(HCI_ISCAN | HCI_PSCAN);
+- break;
+- };
+- }
+- hci_req_complete(hdev, status);
+- break;
+-
+- default:
+- DBG("%s Command complete: ogf HOST_CTL ocf %x", hdev->name, ocf);
+- break;
+- };
+-}
+-
+-/* Command Complete OGF INFO_PARAM */
+-static void hci_cc_info_param(struct hci_dev *hdev, __u16 ocf, struct sk_buff *skb)
++/* HCI Connection scheduler */
++static inline struct hci_conn *hci_low_sent(struct hci_dev *hdev, __u8 type, int *quote)
+ {
+- read_local_features_rp *lf;
+- read_buffer_size_rp *bs;
+- read_bd_addr_rp *ba;
+-
+- DBG("%s ocf 0x%x", hdev->name, ocf);
+-
+- switch (ocf) {
+- case OCF_READ_LOCAL_FEATURES:
+- lf = (read_local_features_rp *) skb->data;
+-
+- if (lf->status) {
+- DBG("%s READ_LOCAL_FEATURES failed %d", hdev->name, lf->status);
+- break;
+- }
+-
+- memcpy(hdev->features, lf->features, sizeof(hdev->features));
+-
+- /* Adjust default settings according to features
+- * supported by device. */
+- if (hdev->features[0] & LMP_3SLOT)
+- hdev->pkt_type |= (HCI_DM3 | HCI_DH3);
+-
+- if (hdev->features[0] & LMP_5SLOT)
+- hdev->pkt_type |= (HCI_DM5 | HCI_DH5);
+-
+- DBG("%s: features 0x%x 0x%x 0x%x", hdev->name, lf->features[0], lf->features[1], lf->features[2]);
+-
+- break;
+-
+- case OCF_READ_BUFFER_SIZE:
+- bs = (read_buffer_size_rp *) skb->data;
+-
+- if (bs->status) {
+- DBG("%s READ_BUFFER_SIZE failed %d", hdev->name, bs->status);
+- break;
+- }
+-
+- hdev->acl_mtu = __le16_to_cpu(bs->acl_mtu);
+- hdev->sco_mtu = bs->sco_mtu;
+- hdev->acl_max = hdev->acl_cnt = __le16_to_cpu(bs->acl_max_pkt);
+- hdev->sco_max = hdev->sco_cnt = __le16_to_cpu(bs->sco_max_pkt);
+-
+- DBG("%s mtu: acl %d, sco %d max_pkt: acl %d, sco %d", hdev->name,
+- hdev->acl_mtu, hdev->sco_mtu, hdev->acl_max, hdev->sco_max);
++ struct conn_hash *h = &hdev->conn_hash;
++ struct hci_conn *conn = NULL;
++ int num = 0, min = ~0;
++ struct list_head *p;
+
+- break;
++ /* We don't have to lock device here. Connections are always
++ * added and removed with TX task disabled. */
++ list_for_each(p, &h->list) {
++ struct hci_conn *c;
++ c = list_entry(p, struct hci_conn, list);
+
+- case OCF_READ_BD_ADDR:
+- ba = (read_bd_addr_rp *) skb->data;
++ if (c->type != type || c->state != BT_CONNECTED
++ || skb_queue_empty(&c->data_q))
++ continue;
++ num++;
+
+- if (!ba->status) {
+- bacpy(&hdev->bdaddr, &ba->bdaddr);
+- } else {
+- DBG("%s: READ_BD_ADDR failed %d", hdev->name, ba->status);
++ if (c->sent < min) {
++ min = c->sent;
++ conn = c;
+ }
++ }
+
+- hci_req_complete(hdev, ba->status);
+- break;
++ if (conn) {
++ int cnt = (type == ACL_LINK ? hdev->acl_cnt : hdev->sco_cnt);
++ int q = cnt / num;
++ *quote = q ? q : 1;
++ } else
++ *quote = 0;
+
+- default:
+- DBG("%s Command complete: ogf INFO_PARAM ocf %x", hdev->name, ocf);
+- break;
+- };
++ BT_DBG("conn %p quote %d", conn, *quote);
++ return conn;
+ }
+
+-/* Command Status OGF LINK_CTL */
+-static void hci_cs_link_ctl(struct hci_dev *hdev, __u16 ocf, __u8 status)
++static inline void hci_acl_tx_to(struct hci_dev *hdev)
+ {
+- struct hci_proto * hp;
+-
+- DBG("%s ocf 0x%x", hdev->name, ocf);
+-
+- switch (ocf) {
+- case OCF_CREATE_CONN:
+- if (status) {
+- create_conn_cp *cc = hci_sent_cmd_data(hdev, OGF_LINK_CTL, OCF_CREATE_CONN);
+-
+- if (!cc)
+- break;
+-
+- DBG("%s Create connection error: status 0x%x %s", hdev->name,
+- status, batostr(&cc->bdaddr));
++ struct conn_hash *h = &hdev->conn_hash;
++ struct list_head *p;
++ struct hci_conn *c;
+
+- /* Notify upper protocols */
+- if ((hp = GET_HPROTO(HCI_PROTO_L2CAP)) && hp->connect_cfm) {
+- tasklet_disable(&hdev->tx_task);
+- hp->connect_cfm(hdev, &cc->bdaddr, status, NULL);
+- tasklet_enable(&hdev->tx_task);
+- }
+- }
+- break;
++ BT_ERR("%s ACL tx timeout", hdev->name);
+
+- case OCF_INQUIRY:
+- if (status) {
+- DBG("%s Inquiry error: status 0x%x", hdev->name, status);
+- hci_req_complete(hdev, status);
++ /* Kill stalled connections */
++ list_for_each(p, &h->list) {
++ c = list_entry(p, struct hci_conn, list);
++ if (c->type == ACL_LINK && c->sent) {
++ BT_ERR("%s killing stalled ACL connection %s",
++ hdev->name, batostr(&c->dst));
++ hci_acl_disconn(c, 0x13);
+ }
+- break;
+-
+- default:
+- DBG("%s Command status: ogf LINK_CTL ocf %x", hdev->name, ocf);
+- break;
+- };
+-}
+-
+-/* Command Status OGF LINK_POLICY */
+-static void hci_cs_link_policy(struct hci_dev *hdev, __u16 ocf, __u8 status)
+-{
+- DBG("%s ocf 0x%x", hdev->name, ocf);
+-
+- switch (ocf) {
+- default:
+- DBG("%s Command status: ogf HOST_POLICY ocf %x", hdev->name, ocf);
+- break;
+- };
+-}
+-
+-/* Command Status OGF HOST_CTL */
+-static void hci_cs_host_ctl(struct hci_dev *hdev, __u16 ocf, __u8 status)
+-{
+- DBG("%s ocf 0x%x", hdev->name, ocf);
+-
+- switch (ocf) {
+- default:
+- DBG("%s Command status: ogf HOST_CTL ocf %x", hdev->name, ocf);
+- break;
+- };
+-}
+-
+-/* Command Status OGF INFO_PARAM */
+-static void hci_cs_info_param(struct hci_dev *hdev, __u16 ocf, __u8 status)
+-{
+- DBG("%s: hci_cs_info_param: ocf 0x%x", hdev->name, ocf);
+-
+- switch (ocf) {
+- default:
+- DBG("%s Command status: ogf INFO_PARAM ocf %x", hdev->name, ocf);
+- break;
+- };
+-}
+-
+-/* Inquiry Complete */
+-static void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
+-{
+- __u8 status = *((__u8 *) skb->data);
+-
+- DBG("%s status %d", hdev->name, status);
+-
+- hci_req_complete(hdev, status);
++ }
+ }
+
+-/* Inquiry Result */
+-static void hci_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff *skb)
++static inline void hci_sched_acl(struct hci_dev *hdev)
+ {
+- inquiry_info *info = (inquiry_info *) (skb->data + 1);
+- int num_rsp = *((__u8 *) skb->data);
+-
+- DBG("%s num_rsp %d", hdev->name, num_rsp);
++ struct hci_conn *conn;
++ struct sk_buff *skb;
++ int quote;
+
+- for (; num_rsp; num_rsp--)
+- inquiry_cache_update(&hdev->inq_cache, info++);
+-}
++ BT_DBG("%s", hdev->name);
+
+-/* Connect Request */
+-static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
+-{
+- evt_conn_request *cr = (evt_conn_request *) skb->data;
+- struct hci_proto *hp;
+- accept_conn_req_cp ac;
+- int accept = 0;
++ /* ACL tx timeout must be longer than maximum
++ * link supervision timeout (40.9 seconds) */
++ if (!hdev->acl_cnt && (jiffies - hdev->acl_last_tx) > (HZ * 45))
++ hci_acl_tx_to(hdev);
+
+- DBG("%s Connection request: %s type 0x%x", hdev->name, batostr(&cr->bdaddr), cr->link_type);
++ while (hdev->acl_cnt && (conn = hci_low_sent(hdev, ACL_LINK, &quote))) {
++ while (quote-- && (skb = skb_dequeue(&conn->data_q))) {
++ BT_DBG("skb %p len %d", skb, skb->len);
++ hci_send_frame(skb);
++ hdev->acl_last_tx = jiffies;
+
+- /* Notify upper protocols */
+- if (cr->link_type == ACL_LINK) {
+- /* ACL link notify L2CAP */
+- if ((hp = GET_HPROTO(HCI_PROTO_L2CAP)) && hp->connect_ind) {
+- tasklet_disable(&hdev->tx_task);
+- accept = hp->connect_ind(hdev, &cr->bdaddr);
+- tasklet_enable(&hdev->tx_task);
++ hdev->acl_cnt--;
++ conn->sent++;
+ }
+- } else {
+- /* SCO link (no notification) */
+- /* FIXME: Should be accept it here or let the requester (app) accept it ? */
+- accept = 1;
+- }
+-
+- if (accept) {
+- /* Connection accepted by upper layer */
+- bacpy(&ac.bdaddr, &cr->bdaddr);
+- ac.role = 0x01; /* Remain slave */
+- hci_send_cmd(hdev, OGF_LINK_CTL, OCF_ACCEPT_CONN_REQ, ACCEPT_CONN_REQ_CP_SIZE, &ac);
+- } else {
+- /* Connection rejected by upper layer */
+- /* FIXME:
+- * Should we use HCI reject here ?
+- */
+- return;
+ }
+ }
+
+-/* Connect Complete */
+-static void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
++/* Schedule SCO */
++static inline void hci_sched_sco(struct hci_dev *hdev)
+ {
+- evt_conn_complete *cc = (evt_conn_complete *) skb->data;
+- struct hci_conn *conn = NULL;
+- struct hci_proto *hp;
+-
+- DBG("%s", hdev->name);
+-
+- tasklet_disable(&hdev->tx_task);
+-
+- if (!cc->status)
+- conn = hci_conn_add(hdev, __le16_to_cpu(cc->handle), cc->link_type, &cc->bdaddr);
++ struct hci_conn *conn;
++ struct sk_buff *skb;
++ int quote;
+
+- /* Notify upper protocols */
+- if (cc->link_type == ACL_LINK) {
+- /* ACL link notify L2CAP layer */
+- if ((hp = GET_HPROTO(HCI_PROTO_L2CAP)) && hp->connect_cfm)
+- hp->connect_cfm(hdev, &cc->bdaddr, cc->status, conn);
+- } else {
+- /* SCO link (no notification) */
+- }
++ BT_DBG("%s", hdev->name);
+
+- tasklet_enable(&hdev->tx_task);
+-}
++ while (hdev->sco_cnt && (conn = hci_low_sent(hdev, SCO_LINK, &quote))) {
++ while (quote-- && (skb = skb_dequeue(&conn->data_q))) {
++ BT_DBG("skb %p len %d", skb, skb->len);
++ hci_send_frame(skb);
+
+-/* Disconnect Complete */
+-static void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
+-{
+- evt_disconn_complete *dc = (evt_disconn_complete *) skb->data;
+- struct hci_conn *conn = NULL;
+- struct hci_proto *hp;
+- __u16 handle = __le16_to_cpu(dc->handle);
+-
+- DBG("%s", hdev->name);
+-
+- if (!dc->status && (conn = conn_hash_lookup(&hdev->conn_hash, handle))) {
+- tasklet_disable(&hdev->tx_task);
+-
+- /* Notify upper protocols */
+- if (conn->type == ACL_LINK) {
+- /* ACL link notify L2CAP layer */
+- if ((hp = GET_HPROTO(HCI_PROTO_L2CAP)) && hp->disconn_ind)
+- hp->disconn_ind(conn, dc->reason);
+- } else {
+- /* SCO link (no notification) */
++ conn->sent++;
++ if (conn->sent == ~0)
++ conn->sent = 0;
+ }
+-
+- hci_conn_del(hdev, conn);
+-
+- tasklet_enable(&hdev->tx_task);
+ }
+ }
+
+-/* Number of completed packets */
+-static void hci_num_comp_pkts_evt(struct hci_dev *hdev, struct sk_buff *skb)
++static void hci_tx_task(unsigned long arg)
+ {
+- evt_num_comp_pkts *nc = (evt_num_comp_pkts *) skb->data;
+- __u16 *ptr;
+- int i;
+-
+- skb_pull(skb, EVT_NUM_COMP_PKTS_SIZE);
+-
+- DBG("%s num_hndl %d", hdev->name, nc->num_hndl);
++ struct hci_dev *hdev = (struct hci_dev *) arg;
++ struct sk_buff *skb;
+
+- if (skb->len < nc->num_hndl * 4) {
+- DBG("%s bad parameters", hdev->name);
+- return;
+- }
++ read_lock(&hci_task_lock);
+
+- tasklet_disable(&hdev->tx_task);
++ BT_DBG("%s acl %d sco %d", hdev->name, hdev->acl_cnt, hdev->sco_cnt);
+
+- for (i = 0, ptr = (__u16 *) skb->data; i < nc->num_hndl; i++) {
+- struct hci_conn *conn;
+- __u16 handle, count;
++ /* Schedule queues and send stuff to HCI driver */
+
+- handle = __le16_to_cpu(get_unaligned(ptr++));
+- count = __le16_to_cpu(get_unaligned(ptr++));
++ hci_sched_acl(hdev);
+
+- hdev->acl_cnt += count;
++ hci_sched_sco(hdev);
+
+- if ((conn = conn_hash_lookup(&hdev->conn_hash, handle)))
+- conn->sent -= count;
+- }
++ /* Send next queued raw (unknown type) packet */
++ while ((skb = skb_dequeue(&hdev->raw_q)))
++ hci_send_frame(skb);
+
+- tasklet_enable(&hdev->tx_task);
+-
+- hci_sched_tx(hdev);
++ read_unlock(&hci_task_lock);
+ }
+
+-static inline void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
+-{
+- hci_event_hdr *he = (hci_event_hdr *) skb->data;
+- evt_cmd_status *cs;
+- evt_cmd_complete *ec;
+- __u16 opcode, ocf, ogf;
+-
+- skb_pull(skb, HCI_EVENT_HDR_SIZE);
+-
+- DBG("%s evt 0x%x", hdev->name, he->evt);
+-
+- switch (he->evt) {
+- case EVT_NUM_COMP_PKTS:
+- hci_num_comp_pkts_evt(hdev, skb);
+- break;
+-
+- case EVT_INQUIRY_COMPLETE:
+- hci_inquiry_complete_evt(hdev, skb);
+- break;
+
+- case EVT_INQUIRY_RESULT:
+- hci_inquiry_result_evt(hdev, skb);
+- break;
+-
+- case EVT_CONN_REQUEST:
+- hci_conn_request_evt(hdev, skb);
+- break;
+-
+- case EVT_CONN_COMPLETE:
+- hci_conn_complete_evt(hdev, skb);
+- break;
+-
+- case EVT_DISCONN_COMPLETE:
+- hci_disconn_complete_evt(hdev, skb);
+- break;
+-
+- case EVT_CMD_STATUS:
+- cs = (evt_cmd_status *) skb->data;
+- skb_pull(skb, EVT_CMD_STATUS_SIZE);
+-
+- opcode = __le16_to_cpu(cs->opcode);
+- ogf = cmd_opcode_ogf(opcode);
+- ocf = cmd_opcode_ocf(opcode);
+-
+- switch (ogf) {
+- case OGF_INFO_PARAM:
+- hci_cs_info_param(hdev, ocf, cs->status);
+- break;
+-
+- case OGF_HOST_CTL:
+- hci_cs_host_ctl(hdev, ocf, cs->status);
+- break;
+-
+- case OGF_LINK_CTL:
+- hci_cs_link_ctl(hdev, ocf, cs->status);
+- break;
+-
+- case OGF_LINK_POLICY:
+- hci_cs_link_policy(hdev, ocf, cs->status);
+- break;
+-
+- default:
+- DBG("%s Command Status OGF %x", hdev->name, ogf);
+- break;
+- };
+-
+- if (cs->ncmd) {
+- atomic_set(&hdev->cmd_cnt, 1);
+- if (!skb_queue_empty(&hdev->cmd_q))
+- hci_sched_cmd(hdev);
+- }
+- break;
+-
+- case EVT_CMD_COMPLETE:
+- ec = (evt_cmd_complete *) skb->data;
+- skb_pull(skb, EVT_CMD_COMPLETE_SIZE);
+-
+- opcode = __le16_to_cpu(ec->opcode);
+- ogf = cmd_opcode_ogf(opcode);
+- ocf = cmd_opcode_ocf(opcode);
+-
+- switch (ogf) {
+- case OGF_INFO_PARAM:
+- hci_cc_info_param(hdev, ocf, skb);
+- break;
+-
+- case OGF_HOST_CTL:
+- hci_cc_host_ctl(hdev, ocf, skb);
+- break;
+-
+- case OGF_LINK_CTL:
+- hci_cc_link_ctl(hdev, ocf, skb);
+- break;
+-
+- case OGF_LINK_POLICY:
+- hci_cc_link_policy(hdev, ocf, skb);
+- break;
+-
+- default:
+- DBG("%s Command Completed OGF %x", hdev->name, ogf);
+- break;
+- };
+-
+- if (ec->ncmd) {
+- atomic_set(&hdev->cmd_cnt, 1);
+- if (!skb_queue_empty(&hdev->cmd_q))
+- hci_sched_cmd(hdev);
+- }
+- break;
+- };
+-
+- kfree_skb(skb);
+- hdev->stat.evt_rx++;
+-}
++/* ----- HCI RX task (incomming data proccessing) ----- */
+
+ /* ACL data packet */
+ static inline void hci_acldata_packet(struct hci_dev *hdev, struct sk_buff *skb)
+@@ -1867,51 +1255,86 @@
+ flags = acl_flags(handle);
+ handle = acl_handle(handle);
+
+- DBG("%s len %d handle 0x%x flags 0x%x", hdev->name, skb->len, handle, flags);
++ BT_DBG("%s len %d handle 0x%x flags 0x%x", hdev->name, skb->len, handle, flags);
++
++ hdev->stat.acl_rx++;
+
+- if ((conn = conn_hash_lookup(&hdev->conn_hash, handle))) {
++ hci_dev_lock(hdev);
++ conn = conn_hash_lookup_handle(hdev, handle);
++ hci_dev_unlock(hdev);
++
++ if (conn) {
+ register struct hci_proto *hp;
+
+ /* Send to upper protocol */
+- if ((hp = GET_HPROTO(HCI_PROTO_L2CAP)) && hp->recv_acldata) {
++ if ((hp = hci_proto[HCI_PROTO_L2CAP]) && hp->recv_acldata) {
+ hp->recv_acldata(conn, skb, flags);
+- goto sent;
++ return;
+ }
+ } else {
+- ERR("%s ACL packet for unknown connection handle %d", hdev->name, handle);
++ BT_ERR("%s ACL packet for unknown connection handle %d",
++ hdev->name, handle);
+ }
+
+ kfree_skb(skb);
+-sent:
+- hdev->stat.acl_rx++;
+ }
+
+ /* SCO data packet */
+ static inline void hci_scodata_packet(struct hci_dev *hdev, struct sk_buff *skb)
+ {
+- DBG("%s len %d", hdev->name, skb->len);
++ hci_sco_hdr *sh = (void *) skb->data;
++ struct hci_conn *conn;
++ __u16 handle;
++
++ skb_pull(skb, HCI_SCO_HDR_SIZE);
++
++ handle = __le16_to_cpu(sh->handle);
++
++ BT_DBG("%s len %d handle 0x%x", hdev->name, skb->len, handle);
+
+- kfree_skb(skb);
+ hdev->stat.sco_rx++;
++
++ hci_dev_lock(hdev);
++ conn = conn_hash_lookup_handle(hdev, handle);
++ hci_dev_unlock(hdev);
++
++ if (conn) {
++ register struct hci_proto *hp;
++
++ /* Send to upper protocol */
++ if ((hp = hci_proto[HCI_PROTO_SCO]) && hp->recv_scodata) {
++ hp->recv_scodata(conn, skb);
++ return;
++ }
++ } else {
++ BT_ERR("%s SCO packet for unknown connection handle %d",
++ hdev->name, handle);
++ }
++
++ kfree_skb(skb);
+ }
+
+-/* ----- HCI tasks ----- */
+ void hci_rx_task(unsigned long arg)
+ {
+ struct hci_dev *hdev = (struct hci_dev *) arg;
+ struct sk_buff *skb;
+
+- DBG("%s", hdev->name);
++ BT_DBG("%s", hdev->name);
+
+ read_lock(&hci_task_lock);
+
+ while ((skb = skb_dequeue(&hdev->rx_q))) {
+- if (hdev->flags & HCI_SOCK) {
++ if (atomic_read(&hdev->promisc)) {
+ /* Send copy to the sockets */
+ hci_send_to_sock(hdev, skb);
+ }
+
+- if (hdev->flags & HCI_INIT) {
++ if (test_bit(HCI_RAW, &hdev->flags)) {
++ kfree_skb(skb);
++ continue;
++ }
++
++ if (test_bit(HCI_INIT, &hdev->flags)) {
+ /* Don't process data packets in this states. */
+ switch (skb->pkt_type) {
+ case HCI_ACLDATA_PKT:
+@@ -1921,64 +1344,43 @@
+ };
+ }
+
+- if (hdev->flags & HCI_NORMAL) {
+- /* Process frame */
+- switch (skb->pkt_type) {
+- case HCI_EVENT_PKT:
+- hci_event_packet(hdev, skb);
+- break;
++ /* Process frame */
++ switch (skb->pkt_type) {
++ case HCI_EVENT_PKT:
++ hci_event_packet(hdev, skb);
++ break;
+
+- case HCI_ACLDATA_PKT:
+- DBG("%s ACL data packet", hdev->name);
+- hci_acldata_packet(hdev, skb);
+- break;
++ case HCI_ACLDATA_PKT:
++ BT_DBG("%s ACL data packet", hdev->name);
++ hci_acldata_packet(hdev, skb);
++ break;
+
+- case HCI_SCODATA_PKT:
+- DBG("%s SCO data packet", hdev->name);
+- hci_scodata_packet(hdev, skb);
+- break;
++ case HCI_SCODATA_PKT:
++ BT_DBG("%s SCO data packet", hdev->name);
++ hci_scodata_packet(hdev, skb);
++ break;
+
+- default:
+- kfree_skb(skb);
+- break;
+- };
+- } else {
++ default:
+ kfree_skb(skb);
++ break;
+ }
+ }
+
+ read_unlock(&hci_task_lock);
+ }
+
+-static void hci_tx_task(unsigned long arg)
+-{
+- struct hci_dev *hdev = (struct hci_dev *) arg;
+- struct sk_buff *skb;
+-
+- read_lock(&hci_task_lock);
+-
+- DBG("%s acl %d sco %d", hdev->name, hdev->acl_cnt, hdev->sco_cnt);
+-
+- /* Schedule queues and send stuff to HCI driver */
+-
+- hci_sched_acl(hdev);
+-
+- hci_sched_sco(hdev);
+-
+- /* Send next queued raw (unknown type) packet */
+- while ((skb = skb_dequeue(&hdev->raw_q)))
+- hci_send_frame(skb);
+-
+- read_unlock(&hci_task_lock);
+-}
+-
+ static void hci_cmd_task(unsigned long arg)
+ {
+ struct hci_dev *hdev = (struct hci_dev *) arg;
+ struct sk_buff *skb;
+
+- DBG("%s cmd %d", hdev->name, atomic_read(&hdev->cmd_cnt));
++ BT_DBG("%s cmd %d", hdev->name, atomic_read(&hdev->cmd_cnt));
+
++ if (!atomic_read(&hdev->cmd_cnt) && (jiffies - hdev->cmd_last_tx) > HZ) {
++ BT_ERR("%s command tx timeout", hdev->name);
++ atomic_set(&hdev->cmd_cnt, 1);
++ }
++
+ /* Send queued commands */
+ if (atomic_read(&hdev->cmd_cnt) && (skb = skb_dequeue(&hdev->cmd_q))) {
+ if (hdev->sent_cmd)
+@@ -1987,6 +1389,7 @@
+ if ((hdev->sent_cmd = skb_clone(skb, GFP_ATOMIC))) {
+ atomic_dec(&hdev->cmd_cnt);
+ hci_send_frame(skb);
++ hdev->cmd_last_tx = jiffies;
+ } else {
+ skb_queue_head(&hdev->cmd_q, skb);
+ hci_sched_cmd(hdev);
+@@ -1994,33 +1397,10 @@
+ }
+ }
+
+-/* Receive frame from HCI drivers */
+-int hci_recv_frame(struct sk_buff *skb)
+-{
+- struct hci_dev *hdev = (struct hci_dev *) skb->dev;
+-
+- if (!hdev || !(hdev->flags & (HCI_UP | HCI_INIT))) {
+- kfree_skb(skb);
+- return -1;
+- }
+-
+- DBG("%s type %d len %d", hdev->name, skb->pkt_type, skb->len);
+-
+- /* Incomming skb */
+- bluez_cb(skb)->incomming = 1;
+-
+- /* Queue frame for rx task */
+- skb_queue_tail(&hdev->rx_q, skb);
+- hci_sched_rx(hdev);
+-
+- return 0;
+-}
++/* ---- Initialization ---- */
+
+ int hci_core_init(void)
+ {
+- /* Init locks */
+- spin_lock_init(&hdev_list_lock);
+-
+ return 0;
+ }
+
+@@ -2028,5 +1408,3 @@
+ {
+ return 0;
+ }
+-
+-MODULE_LICENSE("GPL");
+diff -urN linux-2.4.18/net/bluetooth/hci_event.c linux-2.4.18-mh15/net/bluetooth/hci_event.c
+--- linux-2.4.18/net/bluetooth/hci_event.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/net/bluetooth/hci_event.c 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,910 @@
++/*
++ BlueZ - Bluetooth protocol stack for Linux
++ Copyright (C) 2000-2001 Qualcomm Incorporated
++
++ Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License version 2 as
++ published by the Free Software Foundation;
++
++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++
++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
++ SOFTWARE IS DISCLAIMED.
++*/
++
++/*
++ * HCI Events.
++ *
++ * $Id$
++ */
++
++#include <linux/config.h>
++#include <linux/module.h>
++
++#include <linux/types.h>
++#include <linux/errno.h>
++#include <linux/kernel.h>
++#include <linux/major.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/poll.h>
++#include <linux/fcntl.h>
++#include <linux/init.h>
++#include <linux/skbuff.h>
++#include <linux/interrupt.h>
++#include <linux/notifier.h>
++#include <net/sock.h>
++
++#include <asm/system.h>
++#include <asm/uaccess.h>
++#include <asm/unaligned.h>
++
++#include <net/bluetooth/bluetooth.h>
++#include <net/bluetooth/hci_core.h>
++
++#ifndef HCI_CORE_DEBUG
++#undef BT_DBG
++#define BT_DBG( A... )
++#endif
++
++/* Handle HCI Event packets */
++
++/* Command Complete OGF LINK_CTL */
++static void hci_cc_link_ctl(struct hci_dev *hdev, __u16 ocf, struct sk_buff *skb)
++{
++ __u8 status;
++
++ BT_DBG("%s ocf 0x%x", hdev->name, ocf);
++
++ switch (ocf) {
++ case OCF_INQUIRY_CANCEL:
++ status = *((__u8 *) skb->data);
++
++ if (status) {
++ BT_DBG("%s Inquiry cancel error: status 0x%x", hdev->name, status);
++ } else {
++ clear_bit(HCI_INQUIRY, &hdev->flags);
++ hci_req_complete(hdev, status);
++ }
++ break;
++
++ default:
++ BT_DBG("%s Command complete: ogf LINK_CTL ocf %x", hdev->name, ocf);
++ break;
++ };
++}
++
++/* Command Complete OGF LINK_POLICY */
++static void hci_cc_link_policy(struct hci_dev *hdev, __u16 ocf, struct sk_buff *skb)
++{
++ struct hci_conn *conn;
++ role_discovery_rp *rd;
++
++ BT_DBG("%s ocf 0x%x", hdev->name, ocf);
++
++ switch (ocf) {
++ case OCF_ROLE_DISCOVERY:
++ rd = (void *) skb->data;
++
++ if (rd->status)
++ break;
++
++ hci_dev_lock(hdev);
++
++ conn = conn_hash_lookup_handle(hdev, __le16_to_cpu(rd->handle));
++ if (conn) {
++ if (rd->role)
++ conn->link_mode &= ~HCI_LM_MASTER;
++ else
++ conn->link_mode |= HCI_LM_MASTER;
++ }
++
++ hci_dev_unlock(hdev);
++ break;
++
++ default:
++ BT_DBG("%s: Command complete: ogf LINK_POLICY ocf %x",
++ hdev->name, ocf);
++ break;
++ };
++}
++
++/* Command Complete OGF HOST_CTL */
++static void hci_cc_host_ctl(struct hci_dev *hdev, __u16 ocf, struct sk_buff *skb)
++{
++ __u8 status, param;
++ void *sent;
++
++ BT_DBG("%s ocf 0x%x", hdev->name, ocf);
++
++ switch (ocf) {
++ case OCF_RESET:
++ status = *((__u8 *) skb->data);
++ hci_req_complete(hdev, status);
++ break;
++
++ case OCF_SET_EVENT_FLT:
++ status = *((__u8 *) skb->data);
++ if (status) {
++ BT_DBG("%s SET_EVENT_FLT failed %d", hdev->name, status);
++ } else {
++ BT_DBG("%s SET_EVENT_FLT succeseful", hdev->name);
++ }
++ break;
++
++ case OCF_WRITE_AUTH_ENABLE:
++ sent = hci_sent_cmd_data(hdev, OGF_HOST_CTL, OCF_WRITE_AUTH_ENABLE);
++ if (!sent)
++ break;
++
++ status = *((__u8 *) skb->data);
++ param = *((__u8 *) sent);
++
++ if (!status) {
++ if (param == AUTH_ENABLED)
++ set_bit(HCI_AUTH, &hdev->flags);
++ else
++ clear_bit(HCI_AUTH, &hdev->flags);
++ }
++ hci_req_complete(hdev, status);
++ break;
++
++ case OCF_WRITE_ENCRYPT_MODE:
++ sent = hci_sent_cmd_data(hdev, OGF_HOST_CTL, OCF_WRITE_ENCRYPT_MODE);
++ if (!sent)
++ break;
++
++ status = *((__u8 *) skb->data);
++ param = *((__u8 *) sent);
++
++ if (!status) {
++ if (param)
++ set_bit(HCI_ENCRYPT, &hdev->flags);
++ else
++ clear_bit(HCI_ENCRYPT, &hdev->flags);
++ }
++ hci_req_complete(hdev, status);
++ break;
++
++ case OCF_WRITE_CA_TIMEOUT:
++ status = *((__u8 *) skb->data);
++ if (status) {
++ BT_DBG("%s OCF_WRITE_CA_TIMEOUT failed %d", hdev->name, status);
++ } else {
++ BT_DBG("%s OCF_WRITE_CA_TIMEOUT succeseful", hdev->name);
++ }
++ break;
++
++ case OCF_WRITE_PG_TIMEOUT:
++ status = *((__u8 *) skb->data);
++ if (status) {
++ BT_DBG("%s OCF_WRITE_PG_TIMEOUT failed %d", hdev->name, status);
++ } else {
++ BT_DBG("%s: OCF_WRITE_PG_TIMEOUT succeseful", hdev->name);
++ }
++ break;
++
++ case OCF_WRITE_SCAN_ENABLE:
++ sent = hci_sent_cmd_data(hdev, OGF_HOST_CTL, OCF_WRITE_SCAN_ENABLE);
++ if (!sent)
++ break;
++ status = *((__u8 *) skb->data);
++ param = *((__u8 *) sent);
++
++ BT_DBG("param 0x%x", param);
++
++ if (!status) {
++ clear_bit(HCI_PSCAN, &hdev->flags);
++ clear_bit(HCI_ISCAN, &hdev->flags);
++ if (param & SCAN_INQUIRY)
++ set_bit(HCI_ISCAN, &hdev->flags);
++
++ if (param & SCAN_PAGE)
++ set_bit(HCI_PSCAN, &hdev->flags);
++ }
++ hci_req_complete(hdev, status);
++ break;
++
++ case OCF_HOST_BUFFER_SIZE:
++ status = *((__u8 *) skb->data);
++ if (status) {
++ BT_DBG("%s OCF_BUFFER_SIZE failed %d", hdev->name, status);
++ hci_req_complete(hdev, status);
++ }
++ break;
++
++ default:
++ BT_DBG("%s Command complete: ogf HOST_CTL ocf %x", hdev->name, ocf);
++ break;
++ };
++}
++
++/* Command Complete OGF INFO_PARAM */
++static void hci_cc_info_param(struct hci_dev *hdev, __u16 ocf, struct sk_buff *skb)
++{
++ read_local_features_rp *lf;
++ read_buffer_size_rp *bs;
++ read_bd_addr_rp *ba;
++
++ BT_DBG("%s ocf 0x%x", hdev->name, ocf);
++
++ switch (ocf) {
++ case OCF_READ_LOCAL_FEATURES:
++ lf = (read_local_features_rp *) skb->data;
++
++ if (lf->status) {
++ BT_DBG("%s READ_LOCAL_FEATURES failed %d", hdev->name, lf->status);
++ break;
++ }
++
++ memcpy(hdev->features, lf->features, sizeof(hdev->features));
++
++ /* Adjust default settings according to features
++ * supported by device. */
++ if (hdev->features[0] & LMP_3SLOT)
++ hdev->pkt_type |= (HCI_DM3 | HCI_DH3);
++
++ if (hdev->features[0] & LMP_5SLOT)
++ hdev->pkt_type |= (HCI_DM5 | HCI_DH5);
++
++ if (hdev->features[1] & LMP_HV2)
++ hdev->pkt_type |= (HCI_HV2);
++
++ if (hdev->features[1] & LMP_HV3)
++ hdev->pkt_type |= (HCI_HV3);
++
++ BT_DBG("%s: features 0x%x 0x%x 0x%x", hdev->name, lf->features[0], lf->features[1], lf->features[2]);
++
++ break;
++
++ case OCF_READ_BUFFER_SIZE:
++ bs = (read_buffer_size_rp *) skb->data;
++
++ if (bs->status) {
++ BT_DBG("%s READ_BUFFER_SIZE failed %d", hdev->name, bs->status);
++ hci_req_complete(hdev, bs->status);
++ break;
++ }
++
++ hdev->acl_mtu = __le16_to_cpu(bs->acl_mtu);
++ hdev->sco_mtu = bs->sco_mtu ? bs->sco_mtu : 64;
++ hdev->acl_pkts = hdev->acl_cnt = __le16_to_cpu(bs->acl_max_pkt);
++ hdev->sco_pkts = hdev->sco_cnt = __le16_to_cpu(bs->sco_max_pkt);
++
++ BT_DBG("%s mtu: acl %d, sco %d max_pkt: acl %d, sco %d", hdev->name,
++ hdev->acl_mtu, hdev->sco_mtu, hdev->acl_pkts, hdev->sco_pkts);
++ break;
++
++ case OCF_READ_BD_ADDR:
++ ba = (read_bd_addr_rp *) skb->data;
++
++ if (!ba->status) {
++ bacpy(&hdev->bdaddr, &ba->bdaddr);
++ } else {
++ BT_DBG("%s: READ_BD_ADDR failed %d", hdev->name, ba->status);
++ }
++
++ hci_req_complete(hdev, ba->status);
++ break;
++
++ default:
++ BT_DBG("%s Command complete: ogf INFO_PARAM ocf %x", hdev->name, ocf);
++ break;
++ };
++}
++
++/* Command Status OGF LINK_CTL */
++static inline void hci_cs_create_conn(struct hci_dev *hdev, __u8 status)
++{
++ struct hci_conn *conn;
++ create_conn_cp *cc = hci_sent_cmd_data(hdev, OGF_LINK_CTL, OCF_CREATE_CONN);
++
++ if (!cc)
++ return;
++
++ hci_dev_lock(hdev);
++
++ conn = conn_hash_lookup_ba(hdev, ACL_LINK, &cc->bdaddr);
++
++ BT_DBG("%s status 0x%x bdaddr %s conn %p", hdev->name,
++ status, batostr(&cc->bdaddr), conn);
++
++ if (status) {
++ if (conn && conn->state == BT_CONNECT) {
++ conn->state = BT_CLOSED;
++ hci_proto_connect_cfm(conn, status);
++ hci_conn_del(conn);
++ }
++ } else {
++ if (!conn) {
++ conn = hci_conn_add(hdev, ACL_LINK, &cc->bdaddr);
++ if (conn) {
++ conn->out = 1;
++ conn->link_mode |= HCI_LM_MASTER;
++ } else
++ BT_ERR("No memmory for new connection");
++ }
++ }
++
++ hci_dev_unlock(hdev);
++}
++
++static void hci_cs_link_ctl(struct hci_dev *hdev, __u16 ocf, __u8 status)
++{
++ BT_DBG("%s ocf 0x%x", hdev->name, ocf);
++
++ switch (ocf) {
++ case OCF_CREATE_CONN:
++ hci_cs_create_conn(hdev, status);
++ break;
++
++ case OCF_ADD_SCO:
++ if (status) {
++ struct hci_conn *acl, *sco;
++ add_sco_cp *cp = hci_sent_cmd_data(hdev,
++ OGF_LINK_CTL, OCF_ADD_SCO);
++ __u16 handle;
++
++ if (!cp)
++ break;
++
++ handle = __le16_to_cpu(cp->handle);
++
++ BT_DBG("%s Add SCO error: handle %d status 0x%x", hdev->name, handle, status);
++
++ hci_dev_lock(hdev);
++
++ acl = conn_hash_lookup_handle(hdev, handle);
++ if (acl && (sco = acl->link)) {
++ sco->state = BT_CLOSED;
++ hci_proto_connect_cfm(sco, status);
++ hci_conn_del(sco);
++ }
++
++ hci_dev_unlock(hdev);
++ }
++ break;
++
++ case OCF_INQUIRY:
++ if (status) {
++ BT_DBG("%s Inquiry error: status 0x%x", hdev->name, status);
++ hci_req_complete(hdev, status);
++ } else {
++ set_bit(HCI_INQUIRY, &hdev->flags);
++ }
++ break;
++
++ default:
++ BT_DBG("%s Command status: ogf LINK_CTL ocf %x status %d",
++ hdev->name, ocf, status);
++ break;
++ };
++}
++
++/* Command Status OGF LINK_POLICY */
++static void hci_cs_link_policy(struct hci_dev *hdev, __u16 ocf, __u8 status)
++{
++ BT_DBG("%s ocf 0x%x", hdev->name, ocf);
++
++ switch (ocf) {
++ default:
++ BT_DBG("%s Command status: ogf HOST_POLICY ocf %x", hdev->name, ocf);
++ break;
++ };
++}
++
++/* Command Status OGF HOST_CTL */
++static void hci_cs_host_ctl(struct hci_dev *hdev, __u16 ocf, __u8 status)
++{
++ BT_DBG("%s ocf 0x%x", hdev->name, ocf);
++
++ switch (ocf) {
++ default:
++ BT_DBG("%s Command status: ogf HOST_CTL ocf %x", hdev->name, ocf);
++ break;
++ };
++}
++
++/* Command Status OGF INFO_PARAM */
++static void hci_cs_info_param(struct hci_dev *hdev, __u16 ocf, __u8 status)
++{
++ BT_DBG("%s: hci_cs_info_param: ocf 0x%x", hdev->name, ocf);
++
++ switch (ocf) {
++ default:
++ BT_DBG("%s Command status: ogf INFO_PARAM ocf %x", hdev->name, ocf);
++ break;
++ };
++}
++
++/* Inquiry Complete */
++static inline void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
++{
++ __u8 status = *((__u8 *) skb->data);
++
++ BT_DBG("%s status %d", hdev->name, status);
++
++ clear_bit(HCI_INQUIRY, &hdev->flags);
++ hci_req_complete(hdev, status);
++}
++
++/* Inquiry Result */
++static inline void hci_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff *skb)
++{
++ inquiry_info *info = (inquiry_info *) (skb->data + 1);
++ int num_rsp = *((__u8 *) skb->data);
++
++ BT_DBG("%s num_rsp %d", hdev->name, num_rsp);
++
++ hci_dev_lock(hdev);
++ for (; num_rsp; num_rsp--)
++ inquiry_cache_update(hdev, info++);
++ hci_dev_unlock(hdev);
++}
++
++/* Inquiry Result With RSSI */
++static inline void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, struct sk_buff *skb)
++{
++ inquiry_info_with_rssi *info = (inquiry_info_with_rssi *) (skb->data + 1);
++ int num_rsp = *((__u8 *) skb->data);
++
++ BT_DBG("%s num_rsp %d", hdev->name, num_rsp);
++
++ hci_dev_lock(hdev);
++ for (; num_rsp; num_rsp--) {
++ inquiry_info tmp;
++ bacpy(&tmp.bdaddr, &info->bdaddr);
++ tmp.pscan_rep_mode = info->pscan_rep_mode;
++ tmp.pscan_period_mode = info->pscan_period_mode;
++ tmp.pscan_mode = 0x00;
++ memcpy(tmp.dev_class, &info->dev_class, 3);
++ tmp.clock_offset = info->clock_offset;
++ info++;
++ inquiry_cache_update(hdev, &tmp);
++ }
++ hci_dev_unlock(hdev);
++}
++
++/* Connect Request */
++static inline void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
++{
++ evt_conn_request *cr = (evt_conn_request *) skb->data;
++ int mask = hdev->link_mode;
++
++ BT_DBG("%s Connection request: %s type 0x%x", hdev->name,
++ batostr(&cr->bdaddr), cr->link_type);
++
++ mask |= hci_proto_connect_ind(hdev, &cr->bdaddr, cr->link_type);
++
++ if (mask & HCI_LM_ACCEPT) {
++ /* Connection accepted */
++ struct hci_conn *conn;
++ accept_conn_req_cp ac;
++
++ hci_dev_lock(hdev);
++ conn = conn_hash_lookup_ba(hdev, cr->link_type, &cr->bdaddr);
++ if (!conn) {
++ if (!(conn = hci_conn_add(hdev, cr->link_type, &cr->bdaddr))) {
++ BT_ERR("No memmory for new connection");
++ hci_dev_unlock(hdev);
++ return;
++ }
++ }
++ conn->state = BT_CONNECT;
++ hci_dev_unlock(hdev);
++
++ bacpy(&ac.bdaddr, &cr->bdaddr);
++
++ if (lmp_rswitch_capable(hdev) && (mask & HCI_LM_MASTER))
++ ac.role = 0x00; /* Become master */
++ else
++ ac.role = 0x01; /* Remain slave */
++
++ hci_send_cmd(hdev, OGF_LINK_CTL, OCF_ACCEPT_CONN_REQ,
++ ACCEPT_CONN_REQ_CP_SIZE, &ac);
++ } else {
++ /* Connection rejected */
++ reject_conn_req_cp rc;
++
++ bacpy(&rc.bdaddr, &cr->bdaddr);
++ rc.reason = 0x0f;
++ hci_send_cmd(hdev, OGF_LINK_CTL, OCF_REJECT_CONN_REQ,
++ REJECT_CONN_REQ_CP_SIZE, &rc);
++ }
++}
++
++/* Connect Complete */
++static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
++{
++ evt_conn_complete *cc = (evt_conn_complete *) skb->data;
++ struct hci_conn *conn = NULL;
++
++ BT_DBG("%s", hdev->name);
++
++ hci_dev_lock(hdev);
++
++ conn = conn_hash_lookup_ba(hdev, cc->link_type, &cc->bdaddr);
++ if (!conn) {
++ hci_dev_unlock(hdev);
++ return;
++ }
++
++ if (!cc->status) {
++ conn->handle = __le16_to_cpu(cc->handle);
++ conn->state = BT_CONNECTED;
++
++ if (test_bit(HCI_AUTH, &hdev->flags))
++ conn->link_mode |= HCI_LM_AUTH;
++
++ if (test_bit(HCI_ENCRYPT, &hdev->flags))
++ conn->link_mode |= HCI_LM_ENCRYPT;
++
++
++ /* Set link policy */
++ if (conn->type == ACL_LINK && hdev->link_policy) {
++ write_link_policy_cp lp;
++ lp.handle = cc->handle;
++ lp.policy = __cpu_to_le16(hdev->link_policy);
++ hci_send_cmd(hdev, OGF_LINK_POLICY, OCF_WRITE_LINK_POLICY,
++ WRITE_LINK_POLICY_CP_SIZE, &lp);
++ }
++
++ /* Set packet type for incomming connection */
++ if (!conn->out) {
++ change_conn_ptype_cp cp;
++ cp.handle = cc->handle;
++ cp.pkt_type = (conn->type == ACL_LINK) ?
++ __cpu_to_le16(hdev->pkt_type & ACL_PTYPE_MASK):
++ __cpu_to_le16(hdev->pkt_type & SCO_PTYPE_MASK);
++
++ hci_send_cmd(hdev, OGF_LINK_CTL, OCF_CHANGE_CONN_PTYPE,
++ CHANGE_CONN_PTYPE_CP_SIZE, &cp);
++ }
++ } else
++ conn->state = BT_CLOSED;
++
++ if (conn->type == ACL_LINK) {
++ struct hci_conn *sco = conn->link;
++ if (sco) {
++ if (!cc->status)
++ hci_add_sco(sco, conn->handle);
++ else {
++ hci_proto_connect_cfm(sco, cc->status);
++ hci_conn_del(sco);
++ }
++ }
++ }
++
++ hci_proto_connect_cfm(conn, cc->status);
++ if (cc->status)
++ hci_conn_del(conn);
++
++ hci_dev_unlock(hdev);
++}
++
++/* Disconnect Complete */
++static inline void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
++{
++ evt_disconn_complete *dc = (evt_disconn_complete *) skb->data;
++ struct hci_conn *conn = NULL;
++ __u16 handle = __le16_to_cpu(dc->handle);
++
++ BT_DBG("%s status %d", hdev->name, dc->status);
++
++ if (dc->status)
++ return;
++
++ hci_dev_lock(hdev);
++
++ conn = conn_hash_lookup_handle(hdev, handle);
++ if (conn) {
++ conn->state = BT_CLOSED;
++ hci_proto_disconn_ind(conn, dc->reason);
++ hci_conn_del(conn);
++ }
++
++ hci_dev_unlock(hdev);
++}
++
++/* Number of completed packets */
++static inline void hci_num_comp_pkts_evt(struct hci_dev *hdev, struct sk_buff *skb)
++{
++ evt_num_comp_pkts *nc = (evt_num_comp_pkts *) skb->data;
++ __u16 *ptr;
++ int i;
++
++ skb_pull(skb, EVT_NUM_COMP_PKTS_SIZE);
++
++ BT_DBG("%s num_hndl %d", hdev->name, nc->num_hndl);
++
++ if (skb->len < nc->num_hndl * 4) {
++ BT_DBG("%s bad parameters", hdev->name);
++ return;
++ }
++
++ tasklet_disable(&hdev->tx_task);
++
++ for (i = 0, ptr = (__u16 *) skb->data; i < nc->num_hndl; i++) {
++ struct hci_conn *conn;
++ __u16 handle, count;
++
++ handle = __le16_to_cpu(get_unaligned(ptr++));
++ count = __le16_to_cpu(get_unaligned(ptr++));
++
++ conn = conn_hash_lookup_handle(hdev, handle);
++ if (conn) {
++ conn->sent -= count;
++
++ if (conn->type == SCO_LINK) {
++ if ((hdev->sco_cnt += count) > hdev->sco_pkts)
++ hdev->sco_cnt = hdev->sco_pkts;
++ } else {
++ if ((hdev->acl_cnt += count) > hdev->acl_pkts)
++ hdev->acl_cnt = hdev->acl_pkts;
++ }
++ }
++ }
++ hci_sched_tx(hdev);
++
++ tasklet_enable(&hdev->tx_task);
++}
++
++/* Role Change */
++static inline void hci_role_change_evt(struct hci_dev *hdev, struct sk_buff *skb)
++{
++ evt_role_change *rc = (evt_role_change *) skb->data;
++ struct hci_conn *conn = NULL;
++
++ BT_DBG("%s status %d", hdev->name, rc->status);
++
++ if (rc->status)
++ return;
++
++ hci_dev_lock(hdev);
++
++ conn = conn_hash_lookup_ba(hdev, ACL_LINK, &rc->bdaddr);
++ if (conn) {
++ if (rc->role)
++ conn->link_mode &= ~HCI_LM_MASTER;
++ else
++ conn->link_mode |= HCI_LM_MASTER;
++ }
++
++ hci_dev_unlock(hdev);
++}
++
++/* Authentication Complete */
++static inline void hci_auth_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
++{
++ evt_auth_complete *ac = (evt_auth_complete *) skb->data;
++ struct hci_conn *conn = NULL;
++ __u16 handle = __le16_to_cpu(ac->handle);
++
++ BT_DBG("%s status %d", hdev->name, ac->status);
++
++ hci_dev_lock(hdev);
++
++ conn = conn_hash_lookup_handle(hdev, handle);
++ if (conn) {
++ if (!ac->status)
++ conn->link_mode |= HCI_LM_AUTH;
++ clear_bit(HCI_CONN_AUTH_PEND, &conn->pend);
++
++ hci_proto_auth_cfm(conn, ac->status);
++
++ if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend)) {
++ if (!ac->status) {
++ set_conn_encrypt_cp ce;
++ ce.handle = __cpu_to_le16(conn->handle);
++ ce.encrypt = 1;
++ hci_send_cmd(conn->hdev, OGF_LINK_CTL,
++ OCF_SET_CONN_ENCRYPT,
++ SET_CONN_ENCRYPT_CP_SIZE, &ce);
++ } else {
++ clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend);
++ hci_proto_encrypt_cfm(conn, ac->status);
++ }
++ }
++ }
++
++ hci_dev_unlock(hdev);
++}
++
++/* Encryption Change */
++static inline void hci_encrypt_change_evt(struct hci_dev *hdev, struct sk_buff *skb)
++{
++ evt_encrypt_change *ec = (evt_encrypt_change *) skb->data;
++ struct hci_conn *conn = NULL;
++ __u16 handle = __le16_to_cpu(ec->handle);
++
++ BT_DBG("%s status %d", hdev->name, ec->status);
++
++ hci_dev_lock(hdev);
++
++ conn = conn_hash_lookup_handle(hdev, handle);
++ if (conn) {
++ if (!ec->status) {
++ if (ec->encrypt)
++ conn->link_mode |= HCI_LM_ENCRYPT;
++ else
++ conn->link_mode &= ~HCI_LM_ENCRYPT;
++ }
++ clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend);
++
++ hci_proto_encrypt_cfm(conn, ec->status);
++ }
++
++ hci_dev_unlock(hdev);
++}
++
++void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
++{
++ hci_event_hdr *he = (hci_event_hdr *) skb->data;
++ evt_cmd_status *cs;
++ evt_cmd_complete *ec;
++ __u16 opcode, ocf, ogf;
++
++ skb_pull(skb, HCI_EVENT_HDR_SIZE);
++
++ BT_DBG("%s evt 0x%x", hdev->name, he->evt);
++
++ switch (he->evt) {
++ case EVT_NUM_COMP_PKTS:
++ hci_num_comp_pkts_evt(hdev, skb);
++ break;
++
++ case EVT_INQUIRY_COMPLETE:
++ hci_inquiry_complete_evt(hdev, skb);
++ break;
++
++ case EVT_INQUIRY_RESULT:
++ hci_inquiry_result_evt(hdev, skb);
++ break;
++
++ case EVT_INQUIRY_RESULT_WITH_RSSI:
++ hci_inquiry_result_with_rssi_evt(hdev, skb);
++ break;
++
++ case EVT_CONN_REQUEST:
++ hci_conn_request_evt(hdev, skb);
++ break;
++
++ case EVT_CONN_COMPLETE:
++ hci_conn_complete_evt(hdev, skb);
++ break;
++
++ case EVT_DISCONN_COMPLETE:
++ hci_disconn_complete_evt(hdev, skb);
++ break;
++
++ case EVT_ROLE_CHANGE:
++ hci_role_change_evt(hdev, skb);
++ break;
++
++ case EVT_AUTH_COMPLETE:
++ hci_auth_complete_evt(hdev, skb);
++ break;
++
++ case EVT_ENCRYPT_CHANGE:
++ hci_encrypt_change_evt(hdev, skb);
++ break;
++
++ case EVT_CMD_STATUS:
++ cs = (evt_cmd_status *) skb->data;
++ skb_pull(skb, EVT_CMD_STATUS_SIZE);
++
++ opcode = __le16_to_cpu(cs->opcode);
++ ogf = cmd_opcode_ogf(opcode);
++ ocf = cmd_opcode_ocf(opcode);
++
++ switch (ogf) {
++ case OGF_INFO_PARAM:
++ hci_cs_info_param(hdev, ocf, cs->status);
++ break;
++
++ case OGF_HOST_CTL:
++ hci_cs_host_ctl(hdev, ocf, cs->status);
++ break;
++
++ case OGF_LINK_CTL:
++ hci_cs_link_ctl(hdev, ocf, cs->status);
++ break;
++
++ case OGF_LINK_POLICY:
++ hci_cs_link_policy(hdev, ocf, cs->status);
++ break;
++
++ default:
++ BT_DBG("%s Command Status OGF %x", hdev->name, ogf);
++ break;
++ };
++
++ if (cs->ncmd) {
++ atomic_set(&hdev->cmd_cnt, 1);
++ if (!skb_queue_empty(&hdev->cmd_q))
++ hci_sched_cmd(hdev);
++ }
++ break;
++
++ case EVT_CMD_COMPLETE:
++ ec = (evt_cmd_complete *) skb->data;
++ skb_pull(skb, EVT_CMD_COMPLETE_SIZE);
++
++ opcode = __le16_to_cpu(ec->opcode);
++ ogf = cmd_opcode_ogf(opcode);
++ ocf = cmd_opcode_ocf(opcode);
++
++ switch (ogf) {
++ case OGF_INFO_PARAM:
++ hci_cc_info_param(hdev, ocf, skb);
++ break;
++
++ case OGF_HOST_CTL:
++ hci_cc_host_ctl(hdev, ocf, skb);
++ break;
++
++ case OGF_LINK_CTL:
++ hci_cc_link_ctl(hdev, ocf, skb);
++ break;
++
++ case OGF_LINK_POLICY:
++ hci_cc_link_policy(hdev, ocf, skb);
++ break;
++
++ default:
++ BT_DBG("%s Command Completed OGF %x", hdev->name, ogf);
++ break;
++ };
++
++ if (ec->ncmd) {
++ atomic_set(&hdev->cmd_cnt, 1);
++ if (!skb_queue_empty(&hdev->cmd_q))
++ hci_sched_cmd(hdev);
++ }
++ break;
++ };
++
++ kfree_skb(skb);
++ hdev->stat.evt_rx++;
++}
++
++/* General internal stack event */
++void hci_si_event(struct hci_dev *hdev, int type, int dlen, void *data)
++{
++ hci_event_hdr *eh;
++ evt_stack_internal *si;
++ struct sk_buff *skb;
++ int size;
++ void *ptr;
++
++ size = HCI_EVENT_HDR_SIZE + EVT_STACK_INTERNAL_SIZE + dlen;
++ skb = bluez_skb_alloc(size, GFP_ATOMIC);
++ if (!skb)
++ return;
++
++ ptr = skb_put(skb, size);
++
++ eh = ptr;
++ eh->evt = EVT_STACK_INTERNAL;
++ eh->plen = EVT_STACK_INTERNAL_SIZE + dlen;
++ ptr += HCI_EVENT_HDR_SIZE;
++
++ si = ptr;
++ si->type = type;
++ memcpy(si->data, data, dlen);
++
++ skb->pkt_type = HCI_EVENT_PKT;
++ skb->dev = (void *) hdev;
++ hci_send_to_sock(hdev, skb);
++ kfree_skb(skb);
++}
+diff -urN linux-2.4.18/net/bluetooth/hci_sock.c linux-2.4.18-mh15/net/bluetooth/hci_sock.c
+--- linux-2.4.18/net/bluetooth/hci_sock.c 2001-09-07 18:28:38.000000000 +0200
++++ linux-2.4.18-mh15/net/bluetooth/hci_sock.c 2004-08-01 16:26:23.000000000 +0200
+@@ -25,7 +25,7 @@
+ /*
+ * BlueZ HCI socket layer.
+ *
+- * $Id$
++ * $Id$
+ */
+
+ #include <linux/config.h>
+@@ -49,45 +49,54 @@
+
+ #include <asm/system.h>
+ #include <asm/uaccess.h>
++#include <asm/unaligned.h>
+
+ #include <net/bluetooth/bluetooth.h>
+-#include <net/bluetooth/bluez.h>
+ #include <net/bluetooth/hci_core.h>
+
+ #ifndef HCI_SOCK_DEBUG
+-#undef DBG
+-#define DBG( A... )
++#undef BT_DBG
++#define BT_DBG( A... )
+ #endif
+
+-/* HCI socket interface */
++/* ----- HCI socket interface ----- */
++
++/* Security filter */
++static struct hci_sec_filter hci_sec_filter = {
++ /* Packet types */
++ 0x10,
++ /* Events */
++ { 0x1000d9fe, 0x0000300c },
++ /* Commands */
++ {
++ { 0x0 },
++ /* OGF_LINK_CTL */
++ { 0xbe000006, 0x00000001, 0x0000, 0x00 },
++ /* OGF_LINK_POLICY */
++ { 0x00005200, 0x00000000, 0x0000, 0x00 },
++ /* OGF_HOST_CTL */
++ { 0xaab00200, 0x2b402aaa, 0x0154, 0x00 },
++ /* OGF_INFO_PARAM */
++ { 0x000002be, 0x00000000, 0x0000, 0x00 },
++ /* OGF_STATUS_PARAM */
++ { 0x000000ea, 0x00000000, 0x0000, 0x00 }
++ }
++};
+
+ static struct bluez_sock_list hci_sk_list = {
+ lock: RW_LOCK_UNLOCKED
+ };
+
+-static struct sock *hci_sock_lookup(struct hci_dev *hdev)
+-{
+- struct sock *sk;
+-
+- read_lock(&hci_sk_list.lock);
+- for (sk = hci_sk_list.head; sk; sk = sk->next) {
+- if (hci_pi(sk)->hdev == hdev)
+- break;
+- }
+- read_unlock(&hci_sk_list.lock);
+- return sk;
+-}
+-
+ /* Send frame to RAW socket */
+ void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb)
+ {
+ struct sock * sk;
+
+- DBG("hdev %p len %d", hdev, skb->len);
++ BT_DBG("hdev %p len %d", hdev, skb->len);
+
+ read_lock(&hci_sk_list.lock);
+ for (sk = hci_sk_list.head; sk; sk = sk->next) {
+- struct hci_filter *flt;
++ struct hci_filter *flt;
+ struct sk_buff *nskb;
+
+ if (sk->state != BT_BOUND || hci_pi(sk)->hdev != hdev)
+@@ -100,13 +109,19 @@
+ /* Apply filter */
+ flt = &hci_pi(sk)->filter;
+
+- if (!test_bit(skb->pkt_type, &flt->type_mask))
++ if (!hci_test_bit((skb->pkt_type & HCI_FLT_TYPE_BITS), &flt->type_mask))
+ continue;
+
+ if (skb->pkt_type == HCI_EVENT_PKT) {
+- register int evt = (*(__u8 *)skb->data & 63);
++ register int evt = (*(__u8 *)skb->data & HCI_FLT_EVENT_BITS);
++
++ if (!hci_test_bit(evt, &flt->event_mask))
++ continue;
+
+- if (!test_bit(evt, &flt->event_mask))
++ if (flt->opcode && ((evt == EVT_CMD_COMPLETE &&
++ flt->opcode != *(__u16 *)(skb->data + 3)) ||
++ (evt == EVT_CMD_STATUS &&
++ flt->opcode != *(__u16 *)(skb->data + 4))))
+ continue;
+ }
+
+@@ -116,8 +131,8 @@
+ /* Put type byte before the data */
+ memcpy(skb_push(nskb, 1), &nskb->pkt_type, 1);
+
+- skb_queue_tail(&sk->receive_queue, nskb);
+- sk->data_ready(sk, nskb->len);
++ if (sock_queue_rcv_skb(sk, nskb))
++ kfree_skb(nskb);
+ }
+ read_unlock(&hci_sk_list.lock);
+ }
+@@ -127,7 +142,7 @@
+ struct sock *sk = sock->sk;
+ struct hci_dev *hdev = hci_pi(sk)->hdev;
+
+- DBG("sock %p sk %p", sock, sk);
++ BT_DBG("sock %p sk %p", sock, sk);
+
+ if (!sk)
+ return 0;
+@@ -135,9 +150,7 @@
+ bluez_sock_unlink(&hci_sk_list, sk);
+
+ if (hdev) {
+- if (!hci_sock_lookup(hdev))
+- hdev->flags &= ~HCI_SOCK;
+-
++ atomic_dec(&hdev->promisc);
+ hci_dev_put(hdev);
+ }
+
+@@ -149,24 +162,55 @@
+ sock_put(sk);
+
+ MOD_DEC_USE_COUNT;
+-
+ return 0;
+ }
+
+-static int hci_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
++/* Ioctls that require bound socket */
++static inline int hci_sock_bound_ioctl(struct sock *sk, unsigned int cmd, unsigned long arg)
+ {
+- struct sock *sk = sock->sk;
+ struct hci_dev *hdev = hci_pi(sk)->hdev;
+- __u32 mode;
+
+- DBG("cmd %x arg %lx", cmd, arg);
++ if (!hdev)
++ return -EBADFD;
+
+ switch (cmd) {
+- case HCIGETINFO:
+- return hci_dev_info(arg);
++ case HCISETRAW:
++ if (!capable(CAP_NET_ADMIN))
++ return -EACCES;
+
++ if (arg)
++ set_bit(HCI_RAW, &hdev->flags);
++ else
++ clear_bit(HCI_RAW, &hdev->flags);
++
++ return 0;
++
++ case HCIGETCONNINFO:
++ return hci_get_conn_info(hdev, arg);
++
++ default:
++ if (hdev->ioctl)
++ return hdev->ioctl(hdev, cmd, arg);
++ return -EINVAL;
++ }
++}
++
++static int hci_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
++{
++ struct sock *sk = sock->sk;
++ int err;
++
++ BT_DBG("cmd %x arg %lx", cmd, arg);
++
++ switch (cmd) {
+ case HCIGETDEVLIST:
+- return hci_dev_list(arg);
++ return hci_get_dev_list(arg);
++
++ case HCIGETDEVINFO:
++ return hci_get_dev_info(arg);
++
++ case HCIGETCONNLIST:
++ return hci_get_conn_list(arg);
+
+ case HCIDEVUP:
+ if (!capable(CAP_NET_ADMIN))
+@@ -183,48 +227,31 @@
+ return -EACCES;
+ return hci_dev_reset(arg);
+
+- case HCIRESETSTAT:
++ case HCIDEVRESTAT:
+ if (!capable(CAP_NET_ADMIN))
+ return -EACCES;
+ return hci_dev_reset_stat(arg);
+
+ case HCISETSCAN:
+- if (!capable(CAP_NET_ADMIN))
+- return -EACCES;
+- return hci_dev_setscan(arg);
+-
+ case HCISETAUTH:
+- if (!capable(CAP_NET_ADMIN))
+- return -EACCES;
+- return hci_dev_setauth(arg);
+-
+- case HCISETRAW:
+- if (!capable(CAP_NET_ADMIN))
+- return -EACCES;
+-
+- if (!hdev)
+- return -EBADFD;
+-
+- if (arg)
+- mode = HCI_RAW;
+- else
+- mode = HCI_NORMAL;
+-
+- return hci_dev_setmode(hdev, mode);
+-
++ case HCISETENCRYPT:
+ case HCISETPTYPE:
++ case HCISETLINKPOL:
++ case HCISETLINKMODE:
++ case HCISETACLMTU:
++ case HCISETSCOMTU:
+ if (!capable(CAP_NET_ADMIN))
+ return -EACCES;
+- return hci_dev_setptype(arg);
++ return hci_dev_cmd(cmd, arg);
+
+ case HCIINQUIRY:
+ return hci_inquiry(arg);
+
+- case HCIGETCONNLIST:
+- return hci_conn_list(arg);
+-
+ default:
+- return -EINVAL;
++ lock_sock(sk);
++ err = hci_sock_bound_ioctl(sk, cmd, arg);
++ release_sock(sk);
++ return err;
+ };
+ }
+
+@@ -233,28 +260,35 @@
+ struct sockaddr_hci *haddr = (struct sockaddr_hci *) addr;
+ struct sock *sk = sock->sk;
+ struct hci_dev *hdev = NULL;
++ int err = 0;
+
+- DBG("sock %p sk %p", sock, sk);
++ BT_DBG("sock %p sk %p", sock, sk);
+
+ if (!haddr || haddr->hci_family != AF_BLUETOOTH)
+ return -EINVAL;
+
++ lock_sock(sk);
++
+ if (hci_pi(sk)->hdev) {
+- /* Already bound */
+- return 0;
++ err = -EALREADY;
++ goto done;
+ }
+
+ if (haddr->hci_dev != HCI_DEV_NONE) {
+- if (!(hdev = hci_dev_get(haddr->hci_dev)))
+- return -ENODEV;
++ if (!(hdev = hci_dev_get(haddr->hci_dev))) {
++ err = -ENODEV;
++ goto done;
++ }
+
+- hdev->flags |= HCI_SOCK;
++ atomic_inc(&hdev->promisc);
+ }
+
+ hci_pi(sk)->hdev = hdev;
+ sk->state = BT_BOUND;
+
+- return 0;
++done:
++ release_sock(sk);
++ return err;
+ }
+
+ static int hci_sock_getname(struct socket *sock, struct sockaddr *addr, int *addr_len, int peer)
+@@ -262,73 +296,44 @@
+ struct sockaddr_hci *haddr = (struct sockaddr_hci *) addr;
+ struct sock *sk = sock->sk;
+
+- DBG("sock %p sk %p", sock, sk);
++ BT_DBG("sock %p sk %p", sock, sk);
++
++ lock_sock(sk);
+
+ *addr_len = sizeof(*haddr);
+ haddr->hci_family = AF_BLUETOOTH;
+ haddr->hci_dev = hci_pi(sk)->hdev->id;
+
++ release_sock(sk);
+ return 0;
+ }
+
+-static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg, int len,
+- struct scm_cookie *scm)
+-{
+- struct sock *sk = sock->sk;
+- struct hci_dev *hdev = hci_pi(sk)->hdev;
+- struct sk_buff *skb;
+- int err;
+-
+- DBG("sock %p sk %p", sock, sk);
+-
+- if (msg->msg_flags & MSG_OOB)
+- return -EOPNOTSUPP;
+-
+- if (msg->msg_flags & ~(MSG_DONTWAIT|MSG_NOSIGNAL|MSG_ERRQUEUE))
+- return -EINVAL;
+-
+- if (!hdev)
+- return -EBADFD;
+-
+- if (!(skb = bluez_skb_send_alloc(sk, len, msg->msg_flags & MSG_DONTWAIT, &err)))
+- return err;
+-
+- if (memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len)) {
+- kfree_skb(skb);
+- return -EFAULT;
+- }
+-
+- skb->dev = (void *) hdev;
+- skb->pkt_type = *((unsigned char *) skb->data);
+- skb_pull(skb, 1);
+-
+- /* Send frame to HCI core */
+- hci_send_raw(skb);
+-
+- return len;
+-}
+-
+ static inline void hci_sock_cmsg(struct sock *sk, struct msghdr *msg, struct sk_buff *skb)
+ {
+ __u32 mask = hci_pi(sk)->cmsg_mask;
+
+ if (mask & HCI_CMSG_DIR)
+ put_cmsg(msg, SOL_HCI, HCI_CMSG_DIR, sizeof(int), &bluez_cb(skb)->incomming);
++
++ if (mask & HCI_CMSG_TSTAMP)
++ put_cmsg(msg, SOL_HCI, HCI_CMSG_TSTAMP, sizeof(skb->stamp), &skb->stamp);
+ }
+
+-static int hci_sock_recvmsg(struct socket *sock, struct msghdr *msg, int len,
+- int flags, struct scm_cookie *scm)
++static int hci_sock_recvmsg(struct socket *sock, struct msghdr *msg, int len, int flags, struct scm_cookie *scm)
+ {
+ int noblock = flags & MSG_DONTWAIT;
+ struct sock *sk = sock->sk;
+ struct sk_buff *skb;
+ int copied, err;
+
+- DBG("sock %p sk %p", sock, sk);
++ BT_DBG("sock %p, sk %p", sock, sk);
+
+- if (flags & (MSG_OOB | MSG_PEEK))
++ if (flags & (MSG_OOB))
+ return -EOPNOTSUPP;
+
++ if (sk->state == BT_CLOSED)
++ return 0;
++
+ if (!(skb = skb_recv_datagram(sk, flags, noblock, &err)))
+ return err;
+
+@@ -343,28 +348,107 @@
+ skb->h.raw = skb->data;
+ err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
+
+- if (hci_pi(sk)->cmsg_mask)
+- hci_sock_cmsg(sk, msg, skb);
+-
++ hci_sock_cmsg(sk, msg, skb);
++
+ skb_free_datagram(sk, skb);
+
+ return err ? : copied;
+ }
+
++static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg, int len,
++ struct scm_cookie *scm)
++{
++ struct sock *sk = sock->sk;
++ struct hci_dev *hdev;
++ struct sk_buff *skb;
++ int err;
++
++ BT_DBG("sock %p sk %p", sock, sk);
++
++ if (msg->msg_flags & MSG_OOB)
++ return -EOPNOTSUPP;
++
++ if (msg->msg_flags & ~(MSG_DONTWAIT|MSG_NOSIGNAL|MSG_ERRQUEUE))
++ return -EINVAL;
++
++ if (len < 4)
++ return -EINVAL;
++
++ lock_sock(sk);
++
++ if (!(hdev = hci_pi(sk)->hdev)) {
++ err = -EBADFD;
++ goto done;
++ }
++
++ if (!(skb = bluez_skb_send_alloc(sk, len, msg->msg_flags & MSG_DONTWAIT, &err)))
++ goto done;
++
++ if (memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len)) {
++ err = -EFAULT;
++ goto drop;
++ }
++
++ skb->pkt_type = *((unsigned char *) skb->data);
++ skb_pull(skb, 1);
++ skb->dev = (void *) hdev;
++
++ if (skb->pkt_type == HCI_COMMAND_PKT) {
++ u16 opcode = __le16_to_cpu(get_unaligned((u16 *)skb->data));
++ u16 ogf = cmd_opcode_ogf(opcode);
++ u16 ocf = cmd_opcode_ocf(opcode);
++
++ if (((ogf > HCI_SFLT_MAX_OGF) ||
++ !hci_test_bit(ocf & HCI_FLT_OCF_BITS, &hci_sec_filter.ocf_mask[ogf])) &&
++ !capable(CAP_NET_RAW)) {
++ err = -EPERM;
++ goto drop;
++ }
++
++ if (test_bit(HCI_RAW, &hdev->flags) || (ogf == OGF_VENDOR_CMD)) {
++ skb_queue_tail(&hdev->raw_q, skb);
++ hci_sched_tx(hdev);
++ } else {
++ skb_queue_tail(&hdev->cmd_q, skb);
++ hci_sched_cmd(hdev);
++ }
++ } else {
++ if (!capable(CAP_NET_RAW)) {
++ err = -EPERM;
++ goto drop;
++ }
++
++ skb_queue_tail(&hdev->raw_q, skb);
++ hci_sched_tx(hdev);
++ }
++
++ err = len;
++
++done:
++ release_sock(sk);
++ return err;
++
++drop:
++ kfree_skb(skb);
++ goto done;
++}
++
+ int hci_sock_setsockopt(struct socket *sock, int level, int optname, char *optval, int len)
+ {
+ struct sock *sk = sock->sk;
+- struct hci_filter flt;
++ struct hci_filter flt = { opcode: 0 };
+ int err = 0, opt = 0;
+
+- DBG("sk %p, opt %d", sk, optname);
++ BT_DBG("sk %p, opt %d", sk, optname);
+
+ lock_sock(sk);
+
+ switch (optname) {
+ case HCI_DATA_DIR:
+- if (get_user(opt, (int *)optval))
+- return -EFAULT;
++ if (get_user(opt, (int *)optval)) {
++ err = -EFAULT;
++ break;
++ }
+
+ if (opt)
+ hci_pi(sk)->cmsg_mask |= HCI_CMSG_DIR;
+@@ -372,12 +456,31 @@
+ hci_pi(sk)->cmsg_mask &= ~HCI_CMSG_DIR;
+ break;
+
++ case HCI_TIME_STAMP:
++ if (get_user(opt, (int *)optval)) {
++ err = -EFAULT;
++ break;
++ }
++
++ if (opt)
++ hci_pi(sk)->cmsg_mask |= HCI_CMSG_TSTAMP;
++ else
++ hci_pi(sk)->cmsg_mask &= ~HCI_CMSG_TSTAMP;
++ break;
++
+ case HCI_FILTER:
+ len = MIN(len, sizeof(struct hci_filter));
+ if (copy_from_user(&flt, optval, len)) {
+ err = -EFAULT;
+ break;
+ }
++
++ if (!capable(CAP_NET_RAW)) {
++ flt.type_mask &= hci_sec_filter.type_mask;
++ flt.event_mask[0] &= hci_sec_filter.event_mask[0];
++ flt.event_mask[1] &= hci_sec_filter.event_mask[1];
++ }
++
+ memcpy(&hci_pi(sk)->filter, &flt, len);
+ break;
+
+@@ -409,6 +512,16 @@
+ return -EFAULT;
+ break;
+
++ case HCI_TIME_STAMP:
++ if (hci_pi(sk)->cmsg_mask & HCI_CMSG_TSTAMP)
++ opt = 1;
++ else
++ opt = 0;
++
++ if (put_user(opt, optval))
++ return -EFAULT;
++ break;
++
+ case HCI_FILTER:
+ len = MIN(len, sizeof(struct hci_filter));
+ if (copy_to_user(optval, &hci_pi(sk)->filter, len))
+@@ -446,7 +559,7 @@
+ {
+ struct sock *sk;
+
+- DBG("sock %p", sock);
++ BT_DBG("sock %p", sock);
+
+ if (sock->type != SOCK_RAW)
+ return -ESOCKTNOSUPPORT;
+@@ -464,44 +577,31 @@
+ sk->protocol = protocol;
+ sk->state = BT_OPEN;
+
+- /* Initialize filter */
+- hci_pi(sk)->filter.type_mask = (1<<HCI_EVENT_PKT);
+- hci_pi(sk)->filter.event_mask[0] = ~0L;
+- hci_pi(sk)->filter.event_mask[1] = ~0L;
+-
+ bluez_sock_link(&hci_sk_list, sk);
+
+ MOD_INC_USE_COUNT;
+-
+ return 0;
+ }
+
+ static int hci_sock_dev_event(struct notifier_block *this, unsigned long event, void *ptr)
+ {
+ struct hci_dev *hdev = (struct hci_dev *) ptr;
+- struct sk_buff *skb;
+-
+- DBG("hdev %s event %ld", hdev->name, event);
++ evt_si_device sd;
++
++ BT_DBG("hdev %s event %ld", hdev->name, event);
+
+ /* Send event to sockets */
+- if ((skb = bluez_skb_alloc(HCI_EVENT_HDR_SIZE + EVT_HCI_DEV_EVENT_SIZE, GFP_ATOMIC))) {
+- hci_event_hdr eh = { EVT_HCI_DEV_EVENT, EVT_HCI_DEV_EVENT_SIZE };
+- evt_hci_dev_event he = { event, hdev->id };
+-
+- skb->pkt_type = HCI_EVENT_PKT;
+- memcpy(skb_put(skb, HCI_EVENT_HDR_SIZE), &eh, HCI_EVENT_HDR_SIZE);
+- memcpy(skb_put(skb, EVT_HCI_DEV_EVENT_SIZE), &he, EVT_HCI_DEV_EVENT_SIZE);
+-
+- hci_send_to_sock(NULL, skb);
+- kfree_skb(skb);
+- }
+-
++ sd.event = event;
++ sd.dev_id = hdev->id;
++ hci_si_event(NULL, EVT_SI_DEVICE, EVT_SI_DEVICE_SIZE, &sd);
++
+ if (event == HCI_DEV_UNREG) {
+ struct sock *sk;
+
+ /* Detach sockets from device */
+ read_lock(&hci_sk_list.lock);
+ for (sk = hci_sk_list.head; sk; sk = sk->next) {
++ bh_lock_sock(sk);
+ if (hci_pi(sk)->hdev == hdev) {
+ hci_pi(sk)->hdev = NULL;
+ sk->err = EPIPE;
+@@ -510,6 +610,7 @@
+
+ hci_dev_put(hdev);
+ }
++ bh_unlock_sock(sk);
+ }
+ read_unlock(&hci_sk_list.lock);
+ }
+@@ -529,21 +630,19 @@
+ int hci_sock_init(void)
+ {
+ if (bluez_sock_register(BTPROTO_HCI, &hci_sock_family_ops)) {
+- ERR("Can't register HCI socket");
++ BT_ERR("Can't register HCI socket");
+ return -EPROTO;
+ }
+
+ hci_register_notifier(&hci_sock_nblock);
+-
+ return 0;
+ }
+
+ int hci_sock_cleanup(void)
+ {
+ if (bluez_sock_unregister(BTPROTO_HCI))
+- ERR("Can't unregister HCI socket");
++ BT_ERR("Can't unregister HCI socket");
+
+ hci_unregister_notifier(&hci_sock_nblock);
+-
+ return 0;
+ }
+diff -urN linux-2.4.18/net/bluetooth/hidp/Config.in linux-2.4.18-mh15/net/bluetooth/hidp/Config.in
+--- linux-2.4.18/net/bluetooth/hidp/Config.in 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/net/bluetooth/hidp/Config.in 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,5 @@
++#
++# Bluetooth HIDP layer configuration
++#
++
++dep_tristate 'HIDP protocol support' CONFIG_BLUEZ_HIDP $CONFIG_INPUT $CONFIG_BLUEZ_L2CAP
+diff -urN linux-2.4.18/net/bluetooth/hidp/core.c linux-2.4.18-mh15/net/bluetooth/hidp/core.c
+--- linux-2.4.18/net/bluetooth/hidp/core.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/net/bluetooth/hidp/core.c 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,655 @@
++/*
++ HIDP implementation for Linux Bluetooth stack (BlueZ).
++ Copyright (C) 2003-2004 Marcel Holtmann <marcel@holtmann.org>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License version 2 as
++ published by the Free Software Foundation;
++
++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++
++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
++ SOFTWARE IS DISCLAIMED.
++*/
++
++#include <linux/config.h>
++#include <linux/module.h>
++
++#include <linux/types.h>
++#include <linux/errno.h>
++#include <linux/kernel.h>
++#include <linux/major.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/poll.h>
++#include <linux/fcntl.h>
++#include <linux/skbuff.h>
++#include <linux/socket.h>
++#include <linux/ioctl.h>
++#include <linux/file.h>
++#include <linux/init.h>
++#include <net/sock.h>
++
++#include <linux/input.h>
++
++#include <net/bluetooth/bluetooth.h>
++#include <net/bluetooth/l2cap.h>
++
++#include "hidp.h"
++
++#ifndef CONFIG_BT_HIDP_DEBUG
++#undef BT_DBG
++#define BT_DBG(D...)
++#endif
++
++#define VERSION "1.0"
++
++static DECLARE_RWSEM(hidp_session_sem);
++static LIST_HEAD(hidp_session_list);
++
++static unsigned char hidp_keycode[256] = {
++ 0, 0, 0, 0, 30, 48, 46, 32, 18, 33, 34, 35, 23, 36, 37, 38,
++ 50, 49, 24, 25, 16, 19, 31, 20, 22, 47, 17, 45, 21, 44, 2, 3,
++ 4, 5, 6, 7, 8, 9, 10, 11, 28, 1, 14, 15, 57, 12, 13, 26,
++ 27, 43, 43, 39, 40, 41, 51, 52, 53, 58, 59, 60, 61, 62, 63, 64,
++ 65, 66, 67, 68, 87, 88, 99, 70,119,110,102,104,111,107,109,106,
++ 105,108,103, 69, 98, 55, 74, 78, 96, 79, 80, 81, 75, 76, 77, 71,
++ 72, 73, 82, 83, 86,127,116,117,183,184,185,186,187,188,189,190,
++ 191,192,193,194,134,138,130,132,128,129,131,137,133,135,136,113,
++ 115,114, 0, 0, 0,121, 0, 89, 93,124, 92, 94, 95, 0, 0, 0,
++ 122,123, 90, 91, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
++ 29, 42, 56,125, 97, 54,100,126,164,166,165,163,161,115,114,113,
++ 150,158,159,128,136,177,178,176,142,152,173,140
++};
++
++static struct hidp_session *__hidp_get_session(bdaddr_t *bdaddr)
++{
++ struct hidp_session *session;
++ struct list_head *p;
++
++ BT_DBG("");
++
++ list_for_each(p, &hidp_session_list) {
++ session = list_entry(p, struct hidp_session, list);
++ if (!bacmp(bdaddr, &session->bdaddr))
++ return session;
++ }
++ return NULL;
++}
++
++static void __hidp_link_session(struct hidp_session *session)
++{
++ MOD_INC_USE_COUNT;
++ list_add(&session->list, &hidp_session_list);
++}
++
++static void __hidp_unlink_session(struct hidp_session *session)
++{
++ list_del(&session->list);
++ MOD_DEC_USE_COUNT;
++}
++
++static void __hidp_copy_session(struct hidp_session *session, struct hidp_conninfo *ci)
++{
++ bacpy(&ci->bdaddr, &session->bdaddr);
++
++ ci->flags = session->flags;
++ ci->state = session->state;
++
++ ci->vendor = 0x0000;
++ ci->product = 0x0000;
++ ci->version = 0x0000;
++ memset(ci->name, 0, 128);
++
++ if (session->input) {
++ ci->vendor = session->input->idvendor;
++ ci->product = session->input->idproduct;
++ ci->version = session->input->idversion;
++ if (session->input->name)
++ strncpy(ci->name, session->input->name, 128);
++ else
++ strncpy(ci->name, "HID Boot Device", 128);
++ }
++}
++
++static int hidp_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
++{
++ struct hidp_session *session = dev->private;
++ struct sk_buff *skb;
++ unsigned char newleds;
++
++ BT_DBG("session %p hid %p data %p size %d", session, device, data, size);
++
++ if (type != EV_LED)
++ return -1;
++
++ newleds = (!!test_bit(LED_KANA, dev->led) << 3) |
++ (!!test_bit(LED_COMPOSE, dev->led) << 3) |
++ (!!test_bit(LED_SCROLLL, dev->led) << 2) |
++ (!!test_bit(LED_CAPSL, dev->led) << 1) |
++ (!!test_bit(LED_NUML, dev->led));
++
++ if (session->leds == newleds)
++ return 0;
++
++ session->leds = newleds;
++
++ if (!(skb = alloc_skb(3, GFP_ATOMIC))) {
++ BT_ERR("Can't allocate memory for new frame");
++ return -ENOMEM;
++ }
++
++ *skb_put(skb, 1) = 0xa2;
++ *skb_put(skb, 1) = 0x01;
++ *skb_put(skb, 1) = newleds;
++
++ skb_queue_tail(&session->intr_transmit, skb);
++
++ hidp_schedule(session);
++
++ return 0;
++}
++
++static void hidp_input_report(struct hidp_session *session, struct sk_buff *skb)
++{
++ struct input_dev *dev = session->input;
++ unsigned char *keys = session->keys;
++ unsigned char *udata = skb->data + 1;
++ signed char *sdata = skb->data + 1;
++ int i, size = skb->len - 1;
++
++ switch (skb->data[0]) {
++ case 0x01: /* Keyboard report */
++ for (i = 0; i < 8; i++)
++ input_report_key(dev, hidp_keycode[i + 224], (udata[0] >> i) & 1);
++
++ for (i = 2; i < 8; i++) {
++ if (keys[i] > 3 && memscan(udata + 2, keys[i], 6) == udata + 8) {
++ if (hidp_keycode[keys[i]])
++ input_report_key(dev, hidp_keycode[keys[i]], 0);
++ else
++ BT_ERR("Unknown key (scancode %#x) released.", keys[i]);
++ }
++
++ if (udata[i] > 3 && memscan(keys + 2, udata[i], 6) == keys + 8) {
++ if (hidp_keycode[udata[i]])
++ input_report_key(dev, hidp_keycode[udata[i]], 1);
++ else
++ BT_ERR("Unknown key (scancode %#x) pressed.", udata[i]);
++ }
++ }
++
++ memcpy(keys, udata, 8);
++ break;
++
++ case 0x02: /* Mouse report */
++ input_report_key(dev, BTN_LEFT, sdata[0] & 0x01);
++ input_report_key(dev, BTN_RIGHT, sdata[0] & 0x02);
++ input_report_key(dev, BTN_MIDDLE, sdata[0] & 0x04);
++ input_report_key(dev, BTN_SIDE, sdata[0] & 0x08);
++ input_report_key(dev, BTN_EXTRA, sdata[0] & 0x10);
++
++ input_report_rel(dev, REL_X, sdata[1]);
++ input_report_rel(dev, REL_Y, sdata[2]);
++
++ if (size > 3)
++ input_report_rel(dev, REL_WHEEL, sdata[3]);
++ break;
++ }
++
++ input_event(dev, EV_RST, 0, 0);
++}
++
++static void hidp_idle_timeout(unsigned long arg)
++{
++ struct hidp_session *session = (struct hidp_session *) arg;
++
++ atomic_inc(&session->terminate);
++ hidp_schedule(session);
++}
++
++static inline void hidp_set_timer(struct hidp_session *session)
++{
++ if (session->idle_to > 0)
++ mod_timer(&session->timer, jiffies + HZ * session->idle_to);
++}
++
++static inline void hidp_del_timer(struct hidp_session *session)
++{
++ if (session->idle_to > 0)
++ del_timer(&session->timer);
++}
++
++static inline void hidp_send_message(struct hidp_session *session, unsigned char hdr)
++{
++ struct sk_buff *skb;
++
++ BT_DBG("session %p", session);
++
++ if (!(skb = alloc_skb(1, GFP_ATOMIC))) {
++ BT_ERR("Can't allocate memory for message");
++ return;
++ }
++
++ *skb_put(skb, 1) = hdr;
++
++ skb_queue_tail(&session->ctrl_transmit, skb);
++
++ hidp_schedule(session);
++}
++
++static inline int hidp_recv_frame(struct hidp_session *session, struct sk_buff *skb)
++{
++ __u8 hdr;
++
++ BT_DBG("session %p skb %p len %d", session, skb, skb->len);
++
++ hdr = skb->data[0];
++ skb_pull(skb, 1);
++
++ if (hdr == 0xa1) {
++ hidp_set_timer(session);
++
++ if (session->input)
++ hidp_input_report(session, skb);
++ } else {
++ BT_DBG("Unsupported protocol header 0x%02x", hdr);
++ }
++
++ kfree_skb(skb);
++ return 0;
++}
++
++static int hidp_send_frame(struct socket *sock, unsigned char *data, int len)
++{
++ struct iovec iv = { data, len };
++ struct msghdr msg;
++
++ BT_DBG("sock %p data %p len %d", sock, data, len);
++
++ if (!len)
++ return 0;
++
++ memset(&msg, 0, sizeof(msg));
++ msg.msg_iovlen = 1;
++ msg.msg_iov = &iv;
++
++ return sock_sendmsg(sock, &msg, len);
++}
++
++static int hidp_process_transmit(struct hidp_session *session)
++{
++ struct sk_buff *skb;
++
++ BT_DBG("session %p", session);
++
++ while ((skb = skb_dequeue(&session->ctrl_transmit))) {
++ if (hidp_send_frame(session->ctrl_sock, skb->data, skb->len) < 0) {
++ skb_queue_head(&session->ctrl_transmit, skb);
++ break;
++ }
++
++ hidp_set_timer(session);
++ kfree_skb(skb);
++ }
++
++ while ((skb = skb_dequeue(&session->intr_transmit))) {
++ if (hidp_send_frame(session->intr_sock, skb->data, skb->len) < 0) {
++ skb_queue_head(&session->intr_transmit, skb);
++ break;
++ }
++
++ hidp_set_timer(session);
++ kfree_skb(skb);
++ }
++
++ return skb_queue_len(&session->ctrl_transmit) +
++ skb_queue_len(&session->intr_transmit);
++}
++
++static int hidp_session(void *arg)
++{
++ struct hidp_session *session = arg;
++ struct sock *ctrl_sk = session->ctrl_sock->sk;
++ struct sock *intr_sk = session->intr_sock->sk;
++ struct sk_buff *skb;
++ int vendor = 0x0000, product = 0x0000;
++ wait_queue_t ctrl_wait, intr_wait;
++ unsigned long timeo = HZ;
++
++ BT_DBG("session %p", session);
++
++ if (session->input) {
++ vendor = session->input->idvendor;
++ product = session->input->idproduct;
++ }
++
++ daemonize(); reparent_to_init();
++
++ sprintf(current->comm, "khidpd_%04x%04x", vendor, product);
++
++ sigfillset(&current->blocked);
++ flush_signals(current);
++
++ current->nice = -15;
++
++ set_fs(KERNEL_DS);
++
++ init_waitqueue_entry(&ctrl_wait, current);
++ init_waitqueue_entry(&intr_wait, current);
++ add_wait_queue(ctrl_sk->sleep, &ctrl_wait);
++ add_wait_queue(intr_sk->sleep, &intr_wait);
++ while (!atomic_read(&session->terminate)) {
++ set_current_state(TASK_INTERRUPTIBLE);
++
++ if (ctrl_sk->state != BT_CONNECTED || intr_sk->state != BT_CONNECTED)
++ break;
++
++ while ((skb = skb_dequeue(&ctrl_sk->receive_queue))) {
++ skb_orphan(skb);
++ hidp_recv_frame(session, skb);
++ }
++
++ while ((skb = skb_dequeue(&intr_sk->receive_queue))) {
++ skb_orphan(skb);
++ hidp_recv_frame(session, skb);
++ }
++
++ hidp_process_transmit(session);
++
++ schedule();
++ }
++ set_current_state(TASK_RUNNING);
++ remove_wait_queue(intr_sk->sleep, &intr_wait);
++ remove_wait_queue(ctrl_sk->sleep, &ctrl_wait);
++
++ down_write(&hidp_session_sem);
++
++ hidp_del_timer(session);
++
++ if (intr_sk->state != BT_CONNECTED) {
++ init_waitqueue_entry(&ctrl_wait, current);
++ add_wait_queue(ctrl_sk->sleep, &ctrl_wait);
++ while (timeo && ctrl_sk->state != BT_CLOSED) {
++ set_current_state(TASK_INTERRUPTIBLE);
++ timeo = schedule_timeout(timeo);
++ }
++ set_current_state(TASK_RUNNING);
++ remove_wait_queue(ctrl_sk->sleep, &ctrl_wait);
++ timeo = HZ;
++ }
++
++ fput(session->ctrl_sock->file);
++
++ init_waitqueue_entry(&intr_wait, current);
++ add_wait_queue(intr_sk->sleep, &intr_wait);
++ while (timeo && intr_sk->state != BT_CLOSED) {
++ set_current_state(TASK_INTERRUPTIBLE);
++ timeo = schedule_timeout(timeo);
++ }
++ set_current_state(TASK_RUNNING);
++ remove_wait_queue(intr_sk->sleep, &intr_wait);
++
++ fput(session->intr_sock->file);
++
++ __hidp_unlink_session(session);
++
++ if (session->input) {
++ input_unregister_device(session->input);
++ kfree(session->input);
++ }
++
++ up_write(&hidp_session_sem);
++
++ kfree(session);
++ return 0;
++}
++
++static inline void hidp_setup_input(struct hidp_session *session, struct hidp_connadd_req *req)
++{
++ struct input_dev *input = session->input;
++ int i;
++
++ input->private = session;
++
++ input->idbus = BUS_BLUETOOTH;
++ input->idvendor = req->vendor;
++ input->idproduct = req->product;
++ input->idversion = req->version;
++
++ if (req->subclass & 0x40) {
++ set_bit(EV_KEY, input->evbit);
++ set_bit(EV_LED, input->evbit);
++ set_bit(EV_REP, input->evbit);
++
++ set_bit(LED_NUML, input->ledbit);
++ set_bit(LED_CAPSL, input->ledbit);
++ set_bit(LED_SCROLLL, input->ledbit);
++ set_bit(LED_COMPOSE, input->ledbit);
++ set_bit(LED_KANA, input->ledbit);
++
++ for (i = 0; i < sizeof(hidp_keycode); i++)
++ set_bit(hidp_keycode[i], input->keybit);
++ clear_bit(0, input->keybit);
++ }
++
++ if (req->subclass & 0x80) {
++ input->evbit[0] = BIT(EV_KEY) | BIT(EV_REL);
++ input->keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE);
++ input->relbit[0] = BIT(REL_X) | BIT(REL_Y);
++ input->keybit[LONG(BTN_MOUSE)] |= BIT(BTN_SIDE) | BIT(BTN_EXTRA);
++ input->relbit[0] |= BIT(REL_WHEEL);
++ }
++
++ input->event = hidp_input_event;
++
++ input_register_device(input);
++}
++
++int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, struct socket *intr_sock)
++{
++ struct hidp_session *session, *s;
++ int err;
++
++ BT_DBG("");
++
++ if (bacmp(&bluez_pi(ctrl_sock->sk)->src, &bluez_pi(intr_sock->sk)->src) ||
++ bacmp(&bluez_pi(ctrl_sock->sk)->dst, &bluez_pi(intr_sock->sk)->dst))
++ return -ENOTUNIQ;
++
++ session = kmalloc(sizeof(struct hidp_session), GFP_KERNEL);
++ if (!session)
++ return -ENOMEM;
++ memset(session, 0, sizeof(struct hidp_session));
++
++ session->input = kmalloc(sizeof(struct input_dev), GFP_KERNEL);
++ if (!session->input) {
++ kfree(session);
++ return -ENOMEM;
++ }
++ memset(session->input, 0, sizeof(struct input_dev));
++
++ down_write(&hidp_session_sem);
++
++ s = __hidp_get_session(&bluez_pi(ctrl_sock->sk)->dst);
++ if (s && s->state == BT_CONNECTED) {
++ err = -EEXIST;
++ goto failed;
++ }
++
++ bacpy(&session->bdaddr, &bluez_pi(ctrl_sock->sk)->dst);
++
++ session->ctrl_mtu = min_t(uint, l2cap_pi(ctrl_sock->sk)->omtu, l2cap_pi(ctrl_sock->sk)->imtu);
++ session->intr_mtu = min_t(uint, l2cap_pi(intr_sock->sk)->omtu, l2cap_pi(intr_sock->sk)->imtu);
++
++ BT_DBG("ctrl mtu %d intr mtu %d", session->ctrl_mtu, session->intr_mtu);
++
++ session->ctrl_sock = ctrl_sock;
++ session->intr_sock = intr_sock;
++ session->state = BT_CONNECTED;
++
++ init_timer(&session->timer);
++
++ session->timer.function = hidp_idle_timeout;
++ session->timer.data = (unsigned long) session;
++
++ skb_queue_head_init(&session->ctrl_transmit);
++ skb_queue_head_init(&session->intr_transmit);
++
++ session->flags = req->flags & (1 << HIDP_BLUETOOTH_VENDOR_ID);
++ session->idle_to = req->idle_to;
++
++ if (session->input)
++ hidp_setup_input(session, req);
++
++ __hidp_link_session(session);
++
++ hidp_set_timer(session);
++
++ err = kernel_thread(hidp_session, session, CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
++ if (err < 0)
++ goto unlink;
++
++ if (session->input) {
++ hidp_send_message(session, 0x70);
++ session->flags |= (1 << HIDP_BOOT_PROTOCOL_MODE);
++
++ session->leds = 0xff;
++ hidp_input_event(session->input, EV_LED, 0, 0);
++ }
++
++ up_write(&hidp_session_sem);
++ return 0;
++
++unlink:
++ hidp_del_timer(session);
++
++ __hidp_unlink_session(session);
++
++ if (session->input)
++ input_unregister_device(session->input);
++
++failed:
++ up_write(&hidp_session_sem);
++
++ if (session->input)
++ kfree(session->input);
++
++ kfree(session);
++ return err;
++}
++
++int hidp_del_connection(struct hidp_conndel_req *req)
++{
++ struct hidp_session *session;
++ int err = 0;
++
++ BT_DBG("");
++
++ down_read(&hidp_session_sem);
++
++ session = __hidp_get_session(&req->bdaddr);
++ if (session) {
++ if (req->flags & (1 << HIDP_VIRTUAL_CABLE_UNPLUG)) {
++ hidp_send_message(session, 0x15);
++ } else {
++ /* Flush the transmit queues */
++ skb_queue_purge(&session->ctrl_transmit);
++ skb_queue_purge(&session->intr_transmit);
++
++ /* Kill session thread */
++ atomic_inc(&session->terminate);
++ hidp_schedule(session);
++ }
++ } else
++ err = -ENOENT;
++
++ up_read(&hidp_session_sem);
++ return err;
++}
++
++int hidp_get_connlist(struct hidp_connlist_req *req)
++{
++ struct list_head *p;
++ int err = 0, n = 0;
++
++ BT_DBG("");
++
++ down_read(&hidp_session_sem);
++
++ list_for_each(p, &hidp_session_list) {
++ struct hidp_session *session;
++ struct hidp_conninfo ci;
++
++ session = list_entry(p, struct hidp_session, list);
++
++ __hidp_copy_session(session, &ci);
++
++ if (copy_to_user(req->ci, &ci, sizeof(ci))) {
++ err = -EFAULT;
++ break;
++ }
++
++ if (++n >= req->cnum)
++ break;
++
++ req->ci++;
++ }
++ req->cnum = n;
++
++ up_read(&hidp_session_sem);
++ return err;
++}
++
++int hidp_get_conninfo(struct hidp_conninfo *ci)
++{
++ struct hidp_session *session;
++ int err = 0;
++
++ down_read(&hidp_session_sem);
++
++ session = __hidp_get_session(&ci->bdaddr);
++ if (session)
++ __hidp_copy_session(session, ci);
++ else
++ err = -ENOENT;
++
++ up_read(&hidp_session_sem);
++ return err;
++}
++
++static int __init hidp_init(void)
++{
++ l2cap_load();
++
++ hidp_init_sockets();
++
++ BT_INFO("BlueZ HIDP ver %s", VERSION);
++ BT_INFO("Copyright (C) 2003-2004 Marcel Holtmann <marcel@holtmann.org>");
++
++ return 0;
++}
++
++static void __exit hidp_exit(void)
++{
++ hidp_cleanup_sockets();
++}
++
++module_init(hidp_init);
++module_exit(hidp_exit);
++
++MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
++MODULE_DESCRIPTION("Bluetooth HIDP ver " VERSION);
++MODULE_LICENSE("GPL");
+diff -urN linux-2.4.18/net/bluetooth/hidp/hidp.h linux-2.4.18-mh15/net/bluetooth/hidp/hidp.h
+--- linux-2.4.18/net/bluetooth/hidp/hidp.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/net/bluetooth/hidp/hidp.h 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,122 @@
++/*
++ HIDP implementation for Linux Bluetooth stack (BlueZ).
++ Copyright (C) 2003-2004 Marcel Holtmann <marcel@holtmann.org>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License version 2 as
++ published by the Free Software Foundation;
++
++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++
++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
++ SOFTWARE IS DISCLAIMED.
++*/
++
++#ifndef __HIDP_H
++#define __HIDP_H
++
++#include <linux/types.h>
++#include <net/bluetooth/bluetooth.h>
++
++/* HIDP ioctl defines */
++#define HIDPCONNADD _IOW('H', 200, int)
++#define HIDPCONNDEL _IOW('H', 201, int)
++#define HIDPGETCONNLIST _IOR('H', 210, int)
++#define HIDPGETCONNINFO _IOR('H', 211, int)
++
++#define HIDP_VIRTUAL_CABLE_UNPLUG 0
++#define HIDP_BOOT_PROTOCOL_MODE 1
++#define HIDP_BLUETOOTH_VENDOR_ID 9
++
++struct hidp_connadd_req {
++ int ctrl_sock; // Connected control socket
++ int intr_sock; // Connteted interrupt socket
++ __u16 parser;
++ __u16 rd_size;
++ __u8 *rd_data;
++ __u8 country;
++ __u8 subclass;
++ __u16 vendor;
++ __u16 product;
++ __u16 version;
++ __u32 flags;
++ __u32 idle_to;
++ char name[128];
++};
++
++struct hidp_conndel_req {
++ bdaddr_t bdaddr;
++ __u32 flags;
++};
++
++struct hidp_conninfo {
++ bdaddr_t bdaddr;
++ __u32 flags;
++ __u16 state;
++ __u16 vendor;
++ __u16 product;
++ __u16 version;
++ char name[128];
++};
++
++struct hidp_connlist_req {
++ __u32 cnum;
++ struct hidp_conninfo *ci;
++};
++
++int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, struct socket *intr_sock);
++int hidp_del_connection(struct hidp_conndel_req *req);
++int hidp_get_connlist(struct hidp_connlist_req *req);
++int hidp_get_conninfo(struct hidp_conninfo *ci);
++
++/* HIDP session defines */
++struct hidp_session {
++ struct list_head list;
++
++ struct socket *ctrl_sock;
++ struct socket *intr_sock;
++
++ bdaddr_t bdaddr;
++
++ unsigned long state;
++ unsigned long flags;
++ unsigned long idle_to;
++
++ uint ctrl_mtu;
++ uint intr_mtu;
++
++ atomic_t terminate;
++
++ unsigned char keys[8];
++ unsigned char leds;
++
++ struct input_dev *input;
++
++ struct timer_list timer;
++
++ struct sk_buff_head ctrl_transmit;
++ struct sk_buff_head intr_transmit;
++};
++
++static inline void hidp_schedule(struct hidp_session *session)
++{
++ struct sock *ctrl_sk = session->ctrl_sock->sk;
++ struct sock *intr_sk = session->intr_sock->sk;
++
++ wake_up_interruptible(ctrl_sk->sleep);
++ wake_up_interruptible(intr_sk->sleep);
++}
++
++/* HIDP init defines */
++extern int __init hidp_init_sockets(void);
++extern void __exit hidp_cleanup_sockets(void);
++
++#endif /* __HIDP_H */
+diff -urN linux-2.4.18/net/bluetooth/hidp/Makefile linux-2.4.18-mh15/net/bluetooth/hidp/Makefile
+--- linux-2.4.18/net/bluetooth/hidp/Makefile 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/net/bluetooth/hidp/Makefile 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,10 @@
++#
++# Makefile for the Linux Bluetooth HIDP layer
++#
++
++O_TARGET := hidp.o
++
++obj-y := core.o sock.o
++obj-m += $(O_TARGET)
++
++include $(TOPDIR)/Rules.make
+diff -urN linux-2.4.18/net/bluetooth/hidp/sock.c linux-2.4.18-mh15/net/bluetooth/hidp/sock.c
+--- linux-2.4.18/net/bluetooth/hidp/sock.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/net/bluetooth/hidp/sock.c 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,212 @@
++/*
++ HIDP implementation for Linux Bluetooth stack (BlueZ).
++ Copyright (C) 2003-2004 Marcel Holtmann <marcel@holtmann.org>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License version 2 as
++ published by the Free Software Foundation;
++
++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++
++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
++ SOFTWARE IS DISCLAIMED.
++*/
++
++#include <linux/config.h>
++#include <linux/module.h>
++
++#include <linux/types.h>
++#include <linux/errno.h>
++#include <linux/kernel.h>
++#include <linux/major.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/poll.h>
++#include <linux/fcntl.h>
++#include <linux/skbuff.h>
++#include <linux/socket.h>
++#include <linux/ioctl.h>
++#include <linux/file.h>
++#include <linux/init.h>
++#include <net/sock.h>
++
++#include "hidp.h"
++
++#ifndef CONFIG_BT_HIDP_DEBUG
++#undef BT_DBG
++#define BT_DBG(D...)
++#endif
++
++static int hidp_sock_release(struct socket *sock)
++{
++ struct sock *sk = sock->sk;
++
++ BT_DBG("sock %p sk %p", sock, sk);
++
++ if (!sk)
++ return 0;
++
++ sock_orphan(sk);
++ sock_put(sk);
++
++ MOD_DEC_USE_COUNT;
++ return 0;
++}
++
++static int hidp_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
++{
++ struct hidp_connadd_req ca;
++ struct hidp_conndel_req cd;
++ struct hidp_connlist_req cl;
++ struct hidp_conninfo ci;
++ struct socket *csock;
++ struct socket *isock;
++ int err;
++
++ BT_DBG("cmd %x arg %lx", cmd, arg);
++
++ switch (cmd) {
++ case HIDPCONNADD:
++ if (!capable(CAP_NET_ADMIN))
++ return -EACCES;
++
++ if (copy_from_user(&ca, (void *) arg, sizeof(ca)))
++ return -EFAULT;
++
++ csock = sockfd_lookup(ca.ctrl_sock, &err);
++ if (!csock)
++ return err;
++
++ isock = sockfd_lookup(ca.intr_sock, &err);
++ if (!isock) {
++ fput(csock->file);
++ return err;
++ }
++
++ if (csock->sk->state != BT_CONNECTED || isock->sk->state != BT_CONNECTED) {
++ fput(csock->file);
++ fput(isock->file);
++ return -EBADFD;
++ }
++
++ err = hidp_add_connection(&ca, csock, isock);
++ if (!err) {
++ if (copy_to_user((void *) arg, &ca, sizeof(ca)))
++ err = -EFAULT;
++ } else {
++ fput(csock->file);
++ fput(isock->file);
++ }
++
++ return err;
++
++ case HIDPCONNDEL:
++ if (!capable(CAP_NET_ADMIN))
++ return -EACCES;
++
++ if (copy_from_user(&cd, (void *) arg, sizeof(cd)))
++ return -EFAULT;
++
++ return hidp_del_connection(&cd);
++
++ case HIDPGETCONNLIST:
++ if (copy_from_user(&cl, (void *) arg, sizeof(cl)))
++ return -EFAULT;
++
++ if (cl.cnum <= 0)
++ return -EINVAL;
++
++ err = hidp_get_connlist(&cl);
++ if (!err && copy_to_user((void *) arg, &cl, sizeof(cl)))
++ return -EFAULT;
++
++ return err;
++
++ case HIDPGETCONNINFO:
++ if (copy_from_user(&ci, (void *) arg, sizeof(ci)))
++ return -EFAULT;
++
++ err = hidp_get_conninfo(&ci);
++ if (!err && copy_to_user((void *) arg, &ci, sizeof(ci)))
++ return -EFAULT;
++
++ return err;
++ }
++
++ return -EINVAL;
++}
++
++static struct proto_ops hidp_sock_ops = {
++ family: PF_BLUETOOTH,
++ release: hidp_sock_release,
++ ioctl: hidp_sock_ioctl,
++ bind: sock_no_bind,
++ getname: sock_no_getname,
++ sendmsg: sock_no_sendmsg,
++ recvmsg: sock_no_recvmsg,
++ poll: sock_no_poll,
++ listen: sock_no_listen,
++ shutdown: sock_no_shutdown,
++ setsockopt: sock_no_setsockopt,
++ getsockopt: sock_no_getsockopt,
++ connect: sock_no_connect,
++ socketpair: sock_no_socketpair,
++ accept: sock_no_accept,
++ mmap: sock_no_mmap
++};
++
++static int hidp_sock_create(struct socket *sock, int protocol)
++{
++ struct sock *sk;
++
++ BT_DBG("sock %p", sock);
++
++ if (sock->type != SOCK_RAW)
++ return -ESOCKTNOSUPPORT;
++
++ sock->ops = &hidp_sock_ops;
++
++ if (!(sk = sk_alloc(PF_BLUETOOTH, GFP_KERNEL, 1)))
++ return -ENOMEM;
++
++ MOD_INC_USE_COUNT;
++
++ sock->state = SS_UNCONNECTED;
++ sock_init_data(sock, sk);
++
++ sk->destruct = NULL;
++ sk->protocol = protocol;
++
++ return 0;
++}
++
++static struct net_proto_family hidp_sock_family_ops = {
++ family: PF_BLUETOOTH,
++ create: hidp_sock_create
++};
++
++int __init hidp_init_sockets(void)
++{
++ int err;
++
++ if ((err = bluez_sock_register(BTPROTO_HIDP, &hidp_sock_family_ops)))
++ BT_ERR("Can't register HIDP socket layer (%d)", err);
++
++ return err;
++}
++
++void __exit hidp_cleanup_sockets(void)
++{
++ int err;
++
++ if ((err = bluez_sock_unregister(BTPROTO_HIDP)))
++ BT_ERR("Can't unregister HIDP socket layer (%d)", err);
++}
+diff -urN linux-2.4.18/net/bluetooth/l2cap.c linux-2.4.18-mh15/net/bluetooth/l2cap.c
+--- linux-2.4.18/net/bluetooth/l2cap.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/net/bluetooth/l2cap.c 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,2222 @@
++/*
++ BlueZ - Bluetooth protocol stack for Linux
++ Copyright (C) 2000-2001 Qualcomm Incorporated
++
++ Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License version 2 as
++ published by the Free Software Foundation;
++
++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++
++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
++ SOFTWARE IS DISCLAIMED.
++*/
++
++/*
++ * BlueZ L2CAP core and sockets.
++ *
++ * $Id$
++ */
++#define VERSION "2.3"
++
++#include <linux/config.h>
++#include <linux/module.h>
++
++#include <linux/types.h>
++#include <linux/errno.h>
++#include <linux/kernel.h>
++#include <linux/major.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/poll.h>
++#include <linux/fcntl.h>
++#include <linux/init.h>
++#include <linux/skbuff.h>
++#include <linux/interrupt.h>
++#include <linux/socket.h>
++#include <linux/skbuff.h>
++#include <linux/proc_fs.h>
++#include <linux/list.h>
++#include <net/sock.h>
++
++#include <asm/system.h>
++#include <asm/uaccess.h>
++#include <asm/unaligned.h>
++
++#include <net/bluetooth/bluetooth.h>
++#include <net/bluetooth/hci_core.h>
++#include <net/bluetooth/l2cap.h>
++
++#ifndef L2CAP_DEBUG
++#undef BT_DBG
++#define BT_DBG( A... )
++#endif
++
++static struct proto_ops l2cap_sock_ops;
++
++struct bluez_sock_list l2cap_sk_list = {
++ lock: RW_LOCK_UNLOCKED
++};
++
++static int l2cap_conn_del(struct hci_conn *conn, int err);
++
++static inline void l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct sock *parent);
++static void l2cap_chan_del(struct sock *sk, int err);
++static int l2cap_chan_send(struct sock *sk, struct msghdr *msg, int len);
++
++static void __l2cap_sock_close(struct sock *sk, int reason);
++static void l2cap_sock_close(struct sock *sk);
++static void l2cap_sock_kill(struct sock *sk);
++
++static int l2cap_send_req(struct l2cap_conn *conn, __u8 code, __u16 len, void *data);
++static int l2cap_send_rsp(struct l2cap_conn *conn, __u8 ident, __u8 code, __u16 len, void *data);
++
++/* ----- L2CAP timers ------ */
++static void l2cap_sock_timeout(unsigned long arg)
++{
++ struct sock *sk = (struct sock *) arg;
++
++ BT_DBG("sock %p state %d", sk, sk->state);
++
++ bh_lock_sock(sk);
++ __l2cap_sock_close(sk, ETIMEDOUT);
++ bh_unlock_sock(sk);
++
++ l2cap_sock_kill(sk);
++ sock_put(sk);
++}
++
++static void l2cap_sock_set_timer(struct sock *sk, long timeout)
++{
++ BT_DBG("sk %p state %d timeout %ld", sk, sk->state, timeout);
++
++ if (!mod_timer(&sk->timer, jiffies + timeout))
++ sock_hold(sk);
++}
++
++static void l2cap_sock_clear_timer(struct sock *sk)
++{
++ BT_DBG("sock %p state %d", sk, sk->state);
++
++ if (timer_pending(&sk->timer) && del_timer(&sk->timer))
++ __sock_put(sk);
++}
++
++static void l2cap_sock_init_timer(struct sock *sk)
++{
++ init_timer(&sk->timer);
++ sk->timer.function = l2cap_sock_timeout;
++ sk->timer.data = (unsigned long)sk;
++}
++
++/* -------- L2CAP connections --------- */
++static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, __u8 status)
++{
++ struct l2cap_conn *conn;
++
++ if ((conn = hcon->l2cap_data))
++ return conn;
++
++ if (status)
++ return conn;
++
++ if (!(conn = kmalloc(sizeof(struct l2cap_conn), GFP_ATOMIC)))
++ return NULL;
++ memset(conn, 0, sizeof(struct l2cap_conn));
++
++ hcon->l2cap_data = conn;
++ conn->hcon = hcon;
++
++ conn->mtu = hcon->hdev->acl_mtu;
++ conn->src = &hcon->hdev->bdaddr;
++ conn->dst = &hcon->dst;
++
++ spin_lock_init(&conn->lock);
++ conn->chan_list.lock = RW_LOCK_UNLOCKED;
++
++ BT_DBG("hcon %p conn %p", hcon, conn);
++
++ MOD_INC_USE_COUNT;
++ return conn;
++}
++
++static int l2cap_conn_del(struct hci_conn *hcon, int err)
++{
++ struct l2cap_conn *conn;
++ struct sock *sk;
++
++ if (!(conn = hcon->l2cap_data))
++ return 0;
++
++ BT_DBG("hcon %p conn %p, err %d", hcon, conn, err);
++
++ if (conn->rx_skb)
++ kfree_skb(conn->rx_skb);
++
++ /* Kill channels */
++ while ((sk = conn->chan_list.head)) {
++ bh_lock_sock(sk);
++ l2cap_chan_del(sk, err);
++ bh_unlock_sock(sk);
++ l2cap_sock_kill(sk);
++ }
++
++ hcon->l2cap_data = NULL;
++ kfree(conn);
++
++ MOD_DEC_USE_COUNT;
++ return 0;
++}
++
++/* -------- Socket interface ---------- */
++static struct sock *__l2cap_get_sock_by_addr(__u16 psm, bdaddr_t *src)
++{
++ struct sock *sk;
++ for (sk = l2cap_sk_list.head; sk; sk = sk->next) {
++ if (sk->sport == psm && !bacmp(&bluez_pi(sk)->src, src))
++ break;
++ }
++ return sk;
++}
++
++/* Find socket with psm and source bdaddr.
++ * Returns closest match.
++ */
++static struct sock *__l2cap_get_sock_by_psm(int state, __u16 psm, bdaddr_t *src)
++{
++ struct sock *sk, *sk1 = NULL;
++
++ for (sk = l2cap_sk_list.head; sk; sk = sk->next) {
++ if (state && sk->state != state)
++ continue;
++
++ if (l2cap_pi(sk)->psm == psm) {
++ /* Exact match. */
++ if (!bacmp(&bluez_pi(sk)->src, src))
++ break;
++
++ /* Closest match */
++ if (!bacmp(&bluez_pi(sk)->src, BDADDR_ANY))
++ sk1 = sk;
++ }
++ }
++ return sk ? sk : sk1;
++}
++
++/* Find socket with given address (psm, src).
++ * Returns locked socket */
++static inline struct sock *l2cap_get_sock_by_psm(int state, __u16 psm, bdaddr_t *src)
++{
++ struct sock *s;
++ read_lock(&l2cap_sk_list.lock);
++ s = __l2cap_get_sock_by_psm(state, psm, src);
++ if (s) bh_lock_sock(s);
++ read_unlock(&l2cap_sk_list.lock);
++ return s;
++}
++
++static void l2cap_sock_destruct(struct sock *sk)
++{
++ BT_DBG("sk %p", sk);
++
++ skb_queue_purge(&sk->receive_queue);
++ skb_queue_purge(&sk->write_queue);
++
++ MOD_DEC_USE_COUNT;
++}
++
++static void l2cap_sock_cleanup_listen(struct sock *parent)
++{
++ struct sock *sk;
++
++ BT_DBG("parent %p", parent);
++
++ /* Close not yet accepted channels */
++ while ((sk = bluez_accept_dequeue(parent, NULL)))
++ l2cap_sock_close(sk);
++
++ parent->state = BT_CLOSED;
++ parent->zapped = 1;
++}
++
++/* Kill socket (only if zapped and orphan)
++ * Must be called on unlocked socket.
++ */
++static void l2cap_sock_kill(struct sock *sk)
++{
++ if (!sk->zapped || sk->socket)
++ return;
++
++ BT_DBG("sk %p state %d", sk, sk->state);
++
++ /* Kill poor orphan */
++ bluez_sock_unlink(&l2cap_sk_list, sk);
++ sk->dead = 1;
++ sock_put(sk);
++}
++
++/* Close socket.
++ */
++static void __l2cap_sock_close(struct sock *sk, int reason)
++{
++ BT_DBG("sk %p state %d socket %p", sk, sk->state, sk->socket);
++
++ switch (sk->state) {
++ case BT_LISTEN:
++ l2cap_sock_cleanup_listen(sk);
++ break;
++
++ case BT_CONNECTED:
++ case BT_CONFIG:
++ case BT_CONNECT2:
++ if (sk->type == SOCK_SEQPACKET) {
++ struct l2cap_conn *conn = l2cap_pi(sk)->conn;
++ l2cap_disconn_req req;
++
++ sk->state = BT_DISCONN;
++ l2cap_sock_set_timer(sk, sk->sndtimeo);
++
++ req.dcid = __cpu_to_le16(l2cap_pi(sk)->dcid);
++ req.scid = __cpu_to_le16(l2cap_pi(sk)->scid);
++ l2cap_send_req(conn, L2CAP_DISCONN_REQ, L2CAP_DISCONN_REQ_SIZE, &req);
++ } else {
++ l2cap_chan_del(sk, reason);
++ }
++ break;
++
++ case BT_CONNECT:
++ case BT_DISCONN:
++ l2cap_chan_del(sk, reason);
++ break;
++
++ default:
++ sk->zapped = 1;
++ break;
++ };
++}
++
++/* Must be called on unlocked socket. */
++static void l2cap_sock_close(struct sock *sk)
++{
++ l2cap_sock_clear_timer(sk);
++ lock_sock(sk);
++ __l2cap_sock_close(sk, ECONNRESET);
++ release_sock(sk);
++ l2cap_sock_kill(sk);
++}
++
++static void l2cap_sock_init(struct sock *sk, struct sock *parent)
++{
++ struct l2cap_pinfo *pi = l2cap_pi(sk);
++
++ BT_DBG("sk %p", sk);
++
++ if (parent) {
++ sk->type = parent->type;
++ pi->imtu = l2cap_pi(parent)->imtu;
++ pi->omtu = l2cap_pi(parent)->omtu;
++ pi->link_mode = l2cap_pi(parent)->link_mode;
++ } else {
++ pi->imtu = L2CAP_DEFAULT_MTU;
++ pi->omtu = 0;
++ pi->link_mode = 0;
++ }
++
++ /* Default config options */
++ pi->conf_mtu = L2CAP_DEFAULT_MTU;
++ pi->flush_to = L2CAP_DEFAULT_FLUSH_TO;
++}
++
++static struct sock *l2cap_sock_alloc(struct socket *sock, int proto, int prio)
++{
++ struct sock *sk;
++
++ if (!(sk = sk_alloc(PF_BLUETOOTH, prio, 1)))
++ return NULL;
++
++ bluez_sock_init(sock, sk);
++
++ sk->zapped = 0;
++
++ sk->destruct = l2cap_sock_destruct;
++ sk->sndtimeo = L2CAP_CONN_TIMEOUT;
++
++ sk->protocol = proto;
++ sk->state = BT_OPEN;
++
++ l2cap_sock_init_timer(sk);
++
++ bluez_sock_link(&l2cap_sk_list, sk);
++
++ MOD_INC_USE_COUNT;
++ return sk;
++}
++
++static int l2cap_sock_create(struct socket *sock, int protocol)
++{
++ struct sock *sk;
++
++ BT_DBG("sock %p", sock);
++
++ sock->state = SS_UNCONNECTED;
++
++ if (sock->type != SOCK_SEQPACKET && sock->type != SOCK_DGRAM && sock->type != SOCK_RAW)
++ return -ESOCKTNOSUPPORT;
++
++ if (sock->type == SOCK_RAW && !capable(CAP_NET_RAW))
++ return -EPERM;
++
++ sock->ops = &l2cap_sock_ops;
++
++ if (!(sk = l2cap_sock_alloc(sock, protocol, GFP_KERNEL)))
++ return -ENOMEM;
++
++ l2cap_sock_init(sk, NULL);
++ return 0;
++}
++
++static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len)
++{
++ struct sockaddr_l2 *la = (struct sockaddr_l2 *) addr;
++ struct sock *sk = sock->sk;
++ int err = 0;
++
++ BT_DBG("sk %p, %s %d", sk, batostr(&la->l2_bdaddr), la->l2_psm);
++
++ if (!addr || addr->sa_family != AF_BLUETOOTH)
++ return -EINVAL;
++
++ lock_sock(sk);
++
++ if (sk->state != BT_OPEN) {
++ err = -EBADFD;
++ goto done;
++ }
++
++ write_lock_bh(&l2cap_sk_list.lock);
++ if (la->l2_psm && __l2cap_get_sock_by_addr(la->l2_psm, &la->l2_bdaddr)) {
++ err = -EADDRINUSE;
++ } else {
++ /* Save source address */
++ bacpy(&bluez_pi(sk)->src, &la->l2_bdaddr);
++ l2cap_pi(sk)->psm = la->l2_psm;
++ sk->sport = la->l2_psm;
++ sk->state = BT_BOUND;
++ }
++ write_unlock_bh(&l2cap_sk_list.lock);
++
++done:
++ release_sock(sk);
++ return err;
++}
++
++static int l2cap_do_connect(struct sock *sk)
++{
++ bdaddr_t *src = &bluez_pi(sk)->src;
++ bdaddr_t *dst = &bluez_pi(sk)->dst;
++ struct l2cap_conn *conn;
++ struct hci_conn *hcon;
++ struct hci_dev *hdev;
++ int err = 0;
++
++ BT_DBG("%s -> %s psm 0x%2.2x", batostr(src), batostr(dst), l2cap_pi(sk)->psm);
++
++ if (!(hdev = hci_get_route(dst, src)))
++ return -EHOSTUNREACH;
++
++ hci_dev_lock_bh(hdev);
++
++ err = -ENOMEM;
++
++ hcon = hci_connect(hdev, ACL_LINK, dst);
++ if (!hcon)
++ goto done;
++
++ conn = l2cap_conn_add(hcon, 0);
++ if (!conn) {
++ hci_conn_put(hcon);
++ goto done;
++ }
++
++ err = 0;
++
++ /* Update source addr of the socket */
++ bacpy(src, conn->src);
++
++ l2cap_chan_add(conn, sk, NULL);
++
++ sk->state = BT_CONNECT;
++ l2cap_sock_set_timer(sk, sk->sndtimeo);
++
++ if (hcon->state == BT_CONNECTED) {
++ if (sk->type == SOCK_SEQPACKET) {
++ l2cap_conn_req req;
++ req.scid = __cpu_to_le16(l2cap_pi(sk)->scid);
++ req.psm = l2cap_pi(sk)->psm;
++ l2cap_send_req(conn, L2CAP_CONN_REQ, L2CAP_CONN_REQ_SIZE, &req);
++ } else {
++ l2cap_sock_clear_timer(sk);
++ sk->state = BT_CONNECTED;
++ }
++ }
++
++done:
++ hci_dev_unlock_bh(hdev);
++ hci_dev_put(hdev);
++ return err;
++}
++
++static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int alen, int flags)
++{
++ struct sockaddr_l2 *la = (struct sockaddr_l2 *) addr;
++ struct sock *sk = sock->sk;
++ int err = 0;
++
++ lock_sock(sk);
++
++ BT_DBG("sk %p", sk);
++
++ if (addr->sa_family != AF_BLUETOOTH || alen < sizeof(struct sockaddr_l2)) {
++ err = -EINVAL;
++ goto done;
++ }
++
++ if (sk->type == SOCK_SEQPACKET && !la->l2_psm) {
++ err = -EINVAL;
++ goto done;
++ }
++
++ switch(sk->state) {
++ case BT_CONNECT:
++ case BT_CONNECT2:
++ case BT_CONFIG:
++ /* Already connecting */
++ goto wait;
++
++ case BT_CONNECTED:
++ /* Already connected */
++ goto done;
++
++ case BT_OPEN:
++ case BT_BOUND:
++ /* Can connect */
++ break;
++
++ default:
++ err = -EBADFD;
++ goto done;
++ }
++
++ /* Set destination address and psm */
++ bacpy(&bluez_pi(sk)->dst, &la->l2_bdaddr);
++ l2cap_pi(sk)->psm = la->l2_psm;
++
++ if ((err = l2cap_do_connect(sk)))
++ goto done;
++
++wait:
++ err = bluez_sock_wait_state(sk, BT_CONNECTED,
++ sock_sndtimeo(sk, flags & O_NONBLOCK));
++
++done:
++ release_sock(sk);
++ return err;
++}
++
++int l2cap_sock_listen(struct socket *sock, int backlog)
++{
++ struct sock *sk = sock->sk;
++ int err = 0;
++
++ BT_DBG("sk %p backlog %d", sk, backlog);
++
++ lock_sock(sk);
++
++ if (sk->state != BT_BOUND || sock->type != SOCK_SEQPACKET) {
++ err = -EBADFD;
++ goto done;
++ }
++
++ if (!l2cap_pi(sk)->psm) {
++ err = -EINVAL;
++ goto done;
++ }
++
++ sk->max_ack_backlog = backlog;
++ sk->ack_backlog = 0;
++ sk->state = BT_LISTEN;
++
++done:
++ release_sock(sk);
++ return err;
++}
++
++int l2cap_sock_accept(struct socket *sock, struct socket *newsock, int flags)
++{
++ DECLARE_WAITQUEUE(wait, current);
++ struct sock *sk = sock->sk, *nsk;
++ long timeo;
++ int err = 0;
++
++ lock_sock(sk);
++
++ if (sk->state != BT_LISTEN) {
++ err = -EBADFD;
++ goto done;
++ }
++
++ timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK);
++
++ BT_DBG("sk %p timeo %ld", sk, timeo);
++
++ /* Wait for an incoming connection. (wake-one). */
++ add_wait_queue_exclusive(sk->sleep, &wait);
++ while (!(nsk = bluez_accept_dequeue(sk, newsock))) {
++ set_current_state(TASK_INTERRUPTIBLE);
++ if (!timeo) {
++ err = -EAGAIN;
++ break;
++ }
++
++ release_sock(sk);
++ timeo = schedule_timeout(timeo);
++ lock_sock(sk);
++
++ if (sk->state != BT_LISTEN) {
++ err = -EBADFD;
++ break;
++ }
++
++ if (signal_pending(current)) {
++ err = sock_intr_errno(timeo);
++ break;
++ }
++ }
++ set_current_state(TASK_RUNNING);
++ remove_wait_queue(sk->sleep, &wait);
++
++ if (err)
++ goto done;
++
++ newsock->state = SS_CONNECTED;
++
++ BT_DBG("new socket %p", nsk);
++
++done:
++ release_sock(sk);
++ return err;
++}
++
++static int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr, int *len, int peer)
++{
++ struct sockaddr_l2 *la = (struct sockaddr_l2 *) addr;
++ struct sock *sk = sock->sk;
++
++ BT_DBG("sock %p, sk %p", sock, sk);
++
++ addr->sa_family = AF_BLUETOOTH;
++ *len = sizeof(struct sockaddr_l2);
++
++ if (peer)
++ bacpy(&la->l2_bdaddr, &bluez_pi(sk)->dst);
++ else
++ bacpy(&la->l2_bdaddr, &bluez_pi(sk)->src);
++
++ la->l2_psm = l2cap_pi(sk)->psm;
++ return 0;
++}
++
++static int l2cap_sock_sendmsg(struct socket *sock, struct msghdr *msg, int len, struct scm_cookie *scm)
++{
++ struct sock *sk = sock->sk;
++ int err = 0;
++
++ BT_DBG("sock %p, sk %p", sock, sk);
++
++ if (sk->err)
++ return sock_error(sk);
++
++ if (msg->msg_flags & MSG_OOB)
++ return -EOPNOTSUPP;
++
++ /* Check outgoing MTU */
++ if (len > l2cap_pi(sk)->omtu)
++ return -EINVAL;
++
++ lock_sock(sk);
++
++ if (sk->state == BT_CONNECTED)
++ err = l2cap_chan_send(sk, msg, len);
++ else
++ err = -ENOTCONN;
++
++ release_sock(sk);
++ return err;
++}
++
++static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen)
++{
++ struct sock *sk = sock->sk;
++ struct l2cap_options opts;
++ int err = 0, len;
++ __u32 opt;
++
++ BT_DBG("sk %p", sk);
++
++ lock_sock(sk);
++
++ switch (optname) {
++ case L2CAP_OPTIONS:
++ len = MIN(sizeof(opts), optlen);
++ if (copy_from_user((char *)&opts, optval, len)) {
++ err = -EFAULT;
++ break;
++ }
++ l2cap_pi(sk)->imtu = opts.imtu;
++ l2cap_pi(sk)->omtu = opts.omtu;
++ break;
++
++ case L2CAP_LM:
++ if (get_user(opt, (__u32 *)optval)) {
++ err = -EFAULT;
++ break;
++ }
++
++ l2cap_pi(sk)->link_mode = opt;
++ break;
++
++ default:
++ err = -ENOPROTOOPT;
++ break;
++ }
++
++ release_sock(sk);
++ return err;
++}
++
++static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, char *optval, int *optlen)
++{
++ struct sock *sk = sock->sk;
++ struct l2cap_options opts;
++ struct l2cap_conninfo cinfo;
++ int len, err = 0;
++
++ if (get_user(len, optlen))
++ return -EFAULT;
++
++ lock_sock(sk);
++
++ switch (optname) {
++ case L2CAP_OPTIONS:
++ opts.imtu = l2cap_pi(sk)->imtu;
++ opts.omtu = l2cap_pi(sk)->omtu;
++ opts.flush_to = l2cap_pi(sk)->flush_to;
++
++ len = MIN(len, sizeof(opts));
++ if (copy_to_user(optval, (char *)&opts, len))
++ err = -EFAULT;
++
++ break;
++
++ case L2CAP_LM:
++ if (put_user(l2cap_pi(sk)->link_mode, (__u32 *)optval))
++ err = -EFAULT;
++ break;
++
++ case L2CAP_CONNINFO:
++ if (sk->state != BT_CONNECTED) {
++ err = -ENOTCONN;
++ break;
++ }
++
++ cinfo.hci_handle = l2cap_pi(sk)->conn->hcon->handle;
++
++ len = MIN(len, sizeof(cinfo));
++ if (copy_to_user(optval, (char *)&cinfo, len))
++ err = -EFAULT;
++
++ break;
++
++ default:
++ err = -ENOPROTOOPT;
++ break;
++ }
++
++ release_sock(sk);
++ return err;
++}
++
++static int l2cap_sock_shutdown(struct socket *sock, int how)
++{
++ struct sock *sk = sock->sk;
++ int err = 0;
++
++ BT_DBG("sock %p, sk %p", sock, sk);
++
++ if (!sk) return 0;
++
++ lock_sock(sk);
++ if (!sk->shutdown) {
++ sk->shutdown = SHUTDOWN_MASK;
++ l2cap_sock_clear_timer(sk);
++ __l2cap_sock_close(sk, 0);
++
++ if (sk->linger)
++ err = bluez_sock_wait_state(sk, BT_CLOSED, sk->lingertime);
++ }
++ release_sock(sk);
++ return err;
++}
++
++static int l2cap_sock_release(struct socket *sock)
++{
++ struct sock *sk = sock->sk;
++ int err;
++
++ BT_DBG("sock %p, sk %p", sock, sk);
++
++ if (!sk) return 0;
++
++ err = l2cap_sock_shutdown(sock, 2);
++
++ sock_orphan(sk);
++ l2cap_sock_kill(sk);
++ return err;
++}
++
++/* --------- L2CAP channels --------- */
++static struct sock * __l2cap_get_chan_by_dcid(struct l2cap_chan_list *l, __u16 cid)
++{
++ struct sock *s;
++ for (s = l->head; s; s = l2cap_pi(s)->next_c) {
++ if (l2cap_pi(s)->dcid == cid)
++ break;
++ }
++ return s;
++}
++
++static struct sock *__l2cap_get_chan_by_scid(struct l2cap_chan_list *l, __u16 cid)
++{
++ struct sock *s;
++ for (s = l->head; s; s = l2cap_pi(s)->next_c) {
++ if (l2cap_pi(s)->scid == cid)
++ break;
++ }
++ return s;
++}
++
++/* Find channel with given SCID.
++ * Returns locked socket */
++static inline struct sock *l2cap_get_chan_by_scid(struct l2cap_chan_list *l, __u16 cid)
++{
++ struct sock *s;
++ read_lock(&l->lock);
++ s = __l2cap_get_chan_by_scid(l, cid);
++ if (s) bh_lock_sock(s);
++ read_unlock(&l->lock);
++ return s;
++}
++
++static __u16 l2cap_alloc_cid(struct l2cap_chan_list *l)
++{
++ __u16 cid = 0x0040;
++
++ for (; cid < 0xffff; cid++) {
++ if(!__l2cap_get_chan_by_scid(l, cid))
++ return cid;
++ }
++
++ return 0;
++}
++
++static inline void __l2cap_chan_link(struct l2cap_chan_list *l, struct sock *sk)
++{
++ sock_hold(sk);
++
++ if (l->head)
++ l2cap_pi(l->head)->prev_c = sk;
++
++ l2cap_pi(sk)->next_c = l->head;
++ l2cap_pi(sk)->prev_c = NULL;
++ l->head = sk;
++}
++
++static inline void l2cap_chan_unlink(struct l2cap_chan_list *l, struct sock *sk)
++{
++ struct sock *next = l2cap_pi(sk)->next_c, *prev = l2cap_pi(sk)->prev_c;
++
++ write_lock(&l->lock);
++ if (sk == l->head)
++ l->head = next;
++
++ if (next)
++ l2cap_pi(next)->prev_c = prev;
++ if (prev)
++ l2cap_pi(prev)->next_c = next;
++ write_unlock(&l->lock);
++
++ __sock_put(sk);
++}
++
++static void __l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct sock *parent)
++{
++ struct l2cap_chan_list *l = &conn->chan_list;
++
++ BT_DBG("conn %p, psm 0x%2.2x, dcid 0x%4.4x", conn, l2cap_pi(sk)->psm, l2cap_pi(sk)->dcid);
++
++ l2cap_pi(sk)->conn = conn;
++
++ if (sk->type == SOCK_SEQPACKET) {
++ /* Alloc CID for connection-oriented socket */
++ l2cap_pi(sk)->scid = l2cap_alloc_cid(l);
++ } else if (sk->type == SOCK_DGRAM) {
++ /* Connectionless socket */
++ l2cap_pi(sk)->scid = 0x0002;
++ l2cap_pi(sk)->dcid = 0x0002;
++ l2cap_pi(sk)->omtu = L2CAP_DEFAULT_MTU;
++ } else {
++ /* Raw socket can send/recv signalling messages only */
++ l2cap_pi(sk)->scid = 0x0001;
++ l2cap_pi(sk)->dcid = 0x0001;
++ l2cap_pi(sk)->omtu = L2CAP_DEFAULT_MTU;
++ }
++
++ __l2cap_chan_link(l, sk);
++
++ if (parent)
++ bluez_accept_enqueue(parent, sk);
++}
++
++static inline void l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct sock *parent)
++{
++ struct l2cap_chan_list *l = &conn->chan_list;
++ write_lock(&l->lock);
++ __l2cap_chan_add(conn, sk, parent);
++ write_unlock(&l->lock);
++}
++
++/* Delete channel.
++ * Must be called on the locked socket. */
++static void l2cap_chan_del(struct sock *sk, int err)
++{
++ struct l2cap_conn *conn = l2cap_pi(sk)->conn;
++ struct sock *parent = bluez_pi(sk)->parent;
++
++ l2cap_sock_clear_timer(sk);
++
++ BT_DBG("sk %p, conn %p, err %d", sk, conn, err);
++
++ if (conn) {
++ /* Unlink from channel list */
++ l2cap_chan_unlink(&conn->chan_list, sk);
++ l2cap_pi(sk)->conn = NULL;
++ hci_conn_put(conn->hcon);
++ }
++
++ sk->state = BT_CLOSED;
++ sk->zapped = 1;
++
++ if (err)
++ sk->err = err;
++
++ if (parent)
++ parent->data_ready(parent, 0);
++ else
++ sk->state_change(sk);
++}
++
++static void l2cap_conn_ready(struct l2cap_conn *conn)
++{
++ struct l2cap_chan_list *l = &conn->chan_list;
++ struct sock *sk;
++
++ BT_DBG("conn %p", conn);
++
++ read_lock(&l->lock);
++
++ for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) {
++ bh_lock_sock(sk);
++
++ if (sk->type != SOCK_SEQPACKET) {
++ l2cap_sock_clear_timer(sk);
++ sk->state = BT_CONNECTED;
++ sk->state_change(sk);
++ } else if (sk->state == BT_CONNECT) {
++ l2cap_conn_req req;
++ req.scid = __cpu_to_le16(l2cap_pi(sk)->scid);
++ req.psm = l2cap_pi(sk)->psm;
++ l2cap_send_req(conn, L2CAP_CONN_REQ, L2CAP_CONN_REQ_SIZE, &req);
++ }
++
++ bh_unlock_sock(sk);
++ }
++
++ read_unlock(&l->lock);
++}
++
++/* Notify sockets that we cannot guaranty reliability anymore */
++static void l2cap_conn_unreliable(struct l2cap_conn *conn, int err)
++{
++ struct l2cap_chan_list *l = &conn->chan_list;
++ struct sock *sk;
++
++ BT_DBG("conn %p", conn);
++
++ read_lock(&l->lock);
++ for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) {
++ if (l2cap_pi(sk)->link_mode & L2CAP_LM_RELIABLE)
++ sk->err = err;
++ }
++ read_unlock(&l->lock);
++}
++
++static void l2cap_chan_ready(struct sock *sk)
++{
++ struct sock *parent = bluez_pi(sk)->parent;
++
++ BT_DBG("sk %p, parent %p", sk, parent);
++
++ l2cap_pi(sk)->conf_state = 0;
++ l2cap_sock_clear_timer(sk);
++
++ if (!parent) {
++ /* Outgoing channel.
++ * Wake up socket sleeping on connect.
++ */
++ sk->state = BT_CONNECTED;
++ sk->state_change(sk);
++ } else {
++ /* Incomming channel.
++ * Wake up socket sleeping on accept.
++ */
++ parent->data_ready(parent, 0);
++ }
++}
++
++/* Copy frame to all raw sockets on that connection */
++void l2cap_raw_recv(struct l2cap_conn *conn, struct sk_buff *skb)
++{
++ struct l2cap_chan_list *l = &conn->chan_list;
++ struct sk_buff *nskb;
++ struct sock * sk;
++
++ BT_DBG("conn %p", conn);
++
++ read_lock(&l->lock);
++ for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) {
++ if (sk->type != SOCK_RAW)
++ continue;
++
++ /* Don't send frame to the socket it came from */
++ if (skb->sk == sk)
++ continue;
++
++ if (!(nskb = skb_clone(skb, GFP_ATOMIC)))
++ continue;
++
++ if (sock_queue_rcv_skb(sk, nskb))
++ kfree_skb(nskb);
++ }
++ read_unlock(&l->lock);
++}
++
++static int l2cap_chan_send(struct sock *sk, struct msghdr *msg, int len)
++{
++ struct l2cap_conn *conn = l2cap_pi(sk)->conn;
++ struct sk_buff *skb, **frag;
++ int err, hlen, count, sent=0;
++ l2cap_hdr *lh;
++
++ BT_DBG("sk %p len %d", sk, len);
++
++ /* First fragment (with L2CAP header) */
++ if (sk->type == SOCK_DGRAM)
++ hlen = L2CAP_HDR_SIZE + 2;
++ else
++ hlen = L2CAP_HDR_SIZE;
++
++ count = MIN(conn->mtu - hlen, len);
++
++ skb = bluez_skb_send_alloc(sk, hlen + count,
++ msg->msg_flags & MSG_DONTWAIT, &err);
++ if (!skb)
++ return err;
++
++ /* Create L2CAP header */
++ lh = (l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE);
++ lh->cid = __cpu_to_le16(l2cap_pi(sk)->dcid);
++ lh->len = __cpu_to_le16(len + (hlen - L2CAP_HDR_SIZE));
++
++ if (sk->type == SOCK_DGRAM)
++ put_unaligned(l2cap_pi(sk)->psm, (__u16 *) skb_put(skb, 2));
++
++ if (memcpy_fromiovec(skb_put(skb, count), msg->msg_iov, count)) {
++ err = -EFAULT;
++ goto fail;
++ }
++
++ sent += count;
++ len -= count;
++
++ /* Continuation fragments (no L2CAP header) */
++ frag = &skb_shinfo(skb)->frag_list;
++ while (len) {
++ count = MIN(conn->mtu, len);
++
++ *frag = bluez_skb_send_alloc(sk, count, msg->msg_flags & MSG_DONTWAIT, &err);
++ if (!*frag)
++ goto fail;
++
++ if (memcpy_fromiovec(skb_put(*frag, count), msg->msg_iov, count)) {
++ err = -EFAULT;
++ goto fail;
++ }
++
++ sent += count;
++ len -= count;
++
++ frag = &(*frag)->next;
++ }
++
++ if ((err = hci_send_acl(conn->hcon, skb, 0)) < 0)
++ goto fail;
++
++ return sent;
++
++fail:
++ kfree_skb(skb);
++ return err;
++}
++
++/* --------- L2CAP signalling commands --------- */
++static inline __u8 l2cap_get_ident(struct l2cap_conn *conn)
++{
++ __u8 id;
++
++ /* Get next available identificator.
++ * 1 - 199 are used by kernel.
++ * 200 - 254 are used by utilities like l2ping, etc
++ */
++
++ spin_lock(&conn->lock);
++
++ if (++conn->tx_ident > 199)
++ conn->tx_ident = 1;
++
++ id = conn->tx_ident;
++
++ spin_unlock(&conn->lock);
++
++ return id;
++}
++
++static struct sk_buff *l2cap_build_cmd(struct l2cap_conn *conn,
++ __u8 code, __u8 ident, __u16 dlen, void *data)
++{
++ struct sk_buff *skb, **frag;
++ l2cap_cmd_hdr *cmd;
++ l2cap_hdr *lh;
++ int len, count;
++
++ BT_DBG("conn %p, code 0x%2.2x, ident 0x%2.2x, len %d", conn, code, ident, dlen);
++
++ len = L2CAP_HDR_SIZE + L2CAP_CMD_HDR_SIZE + dlen;
++ count = MIN(conn->mtu, len);
++
++ skb = bluez_skb_alloc(count, GFP_ATOMIC);
++ if (!skb)
++ return NULL;
++
++ lh = (l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE);
++ lh->len = __cpu_to_le16(L2CAP_CMD_HDR_SIZE + dlen);
++ lh->cid = __cpu_to_le16(0x0001);
++
++ cmd = (l2cap_cmd_hdr *) skb_put(skb, L2CAP_CMD_HDR_SIZE);
++ cmd->code = code;
++ cmd->ident = ident;
++ cmd->len = __cpu_to_le16(dlen);
++
++ if (dlen) {
++ count -= L2CAP_HDR_SIZE + L2CAP_CMD_HDR_SIZE;
++ memcpy(skb_put(skb, count), data, count);
++ data += count;
++ }
++
++ len -= skb->len;
++
++ /* Continuation fragments (no L2CAP header) */
++ frag = &skb_shinfo(skb)->frag_list;
++ while (len) {
++ count = MIN(conn->mtu, len);
++
++ *frag = bluez_skb_alloc(count, GFP_ATOMIC);
++ if (!*frag)
++ goto fail;
++
++ memcpy(skb_put(*frag, count), data, count);
++
++ len -= count;
++ data += count;
++
++ frag = &(*frag)->next;
++ }
++
++ return skb;
++
++fail:
++ kfree_skb(skb);
++ return NULL;
++}
++
++static int l2cap_send_req(struct l2cap_conn *conn, __u8 code, __u16 len, void *data)
++{
++ __u8 ident = l2cap_get_ident(conn);
++ struct sk_buff *skb = l2cap_build_cmd(conn, code, ident, len, data);
++
++ BT_DBG("code 0x%2.2x", code);
++
++ if (!skb)
++ return -ENOMEM;
++ return hci_send_acl(conn->hcon, skb, 0);
++}
++
++static int l2cap_send_rsp(struct l2cap_conn *conn, __u8 ident, __u8 code, __u16 len, void *data)
++{
++ struct sk_buff *skb = l2cap_build_cmd(conn, code, ident, len, data);
++
++ BT_DBG("code 0x%2.2x", code);
++
++ if (!skb)
++ return -ENOMEM;
++ return hci_send_acl(conn->hcon, skb, 0);
++}
++
++static inline int l2cap_get_conf_opt(void **ptr, int *type, int *olen, unsigned long *val)
++{
++ l2cap_conf_opt *opt = *ptr;
++ int len;
++
++ len = L2CAP_CONF_OPT_SIZE + opt->len;
++ *ptr += len;
++
++ *type = opt->type;
++ *olen = opt->len;
++
++ switch (opt->len) {
++ case 1:
++ *val = *((__u8 *) opt->val);
++ break;
++
++ case 2:
++ *val = __le16_to_cpu(*((__u16 *)opt->val));
++ break;
++
++ case 4:
++ *val = __le32_to_cpu(*((__u32 *)opt->val));
++ break;
++
++ default:
++ *val = (unsigned long) opt->val;
++ break;
++ };
++
++ BT_DBG("type 0x%2.2x len %d val 0x%lx", *type, opt->len, *val);
++ return len;
++}
++
++static inline void l2cap_parse_conf_req(struct sock *sk, void *data, int len)
++{
++ int type, hint, olen;
++ unsigned long val;
++ void *ptr = data;
++
++ BT_DBG("sk %p len %d", sk, len);
++
++ while (len >= L2CAP_CONF_OPT_SIZE) {
++ len -= l2cap_get_conf_opt(&ptr, &type, &olen, &val);
++
++ hint = type & 0x80;
++ type &= 0x7f;
++
++ switch (type) {
++ case L2CAP_CONF_MTU:
++ l2cap_pi(sk)->conf_mtu = val;
++ break;
++
++ case L2CAP_CONF_FLUSH_TO:
++ l2cap_pi(sk)->flush_to = val;
++ break;
++
++ case L2CAP_CONF_QOS:
++ break;
++
++ default:
++ if (hint)
++ break;
++
++ /* FIXME: Reject unknown option */
++ break;
++ };
++ }
++}
++
++static void l2cap_add_conf_opt(void **ptr, __u8 type, __u8 len, unsigned long val)
++{
++ register l2cap_conf_opt *opt = *ptr;
++
++ BT_DBG("type 0x%2.2x len %d val 0x%lx", type, len, val);
++
++ opt->type = type;
++ opt->len = len;
++
++ switch (len) {
++ case 1:
++ *((__u8 *) opt->val) = val;
++ break;
++
++ case 2:
++ *((__u16 *) opt->val) = __cpu_to_le16(val);
++ break;
++
++ case 4:
++ *((__u32 *) opt->val) = __cpu_to_le32(val);
++ break;
++
++ default:
++ memcpy(opt->val, (void *) val, len);
++ break;
++ };
++
++ *ptr += L2CAP_CONF_OPT_SIZE + len;
++}
++
++static int l2cap_build_conf_req(struct sock *sk, void *data)
++{
++ struct l2cap_pinfo *pi = l2cap_pi(sk);
++ l2cap_conf_req *req = (l2cap_conf_req *) data;
++ void *ptr = req->data;
++
++ BT_DBG("sk %p", sk);
++
++ if (pi->imtu != L2CAP_DEFAULT_MTU)
++ l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, pi->imtu);
++
++ /* FIXME. Need actual value of the flush timeout */
++ //if (flush_to != L2CAP_DEFAULT_FLUSH_TO)
++ // l2cap_add_conf_opt(&ptr, L2CAP_CONF_FLUSH_TO, 2, pi->flush_to);
++
++ req->dcid = __cpu_to_le16(pi->dcid);
++ req->flags = __cpu_to_le16(0);
++
++ return ptr - data;
++}
++
++static inline int l2cap_conf_output(struct sock *sk, void **ptr)
++{
++ struct l2cap_pinfo *pi = l2cap_pi(sk);
++ int result = 0;
++
++ /* Configure output options and let the other side know
++ * which ones we don't like.
++ */
++ if (pi->conf_mtu < pi->omtu) {
++ l2cap_add_conf_opt(ptr, L2CAP_CONF_MTU, 2, pi->omtu);
++ result = L2CAP_CONF_UNACCEPT;
++ } else {
++ pi->omtu = pi->conf_mtu;
++ }
++
++ BT_DBG("sk %p result %d", sk, result);
++ return result;
++}
++
++static int l2cap_build_conf_rsp(struct sock *sk, void *data, int *result)
++{
++ l2cap_conf_rsp *rsp = (l2cap_conf_rsp *) data;
++ void *ptr = rsp->data;
++ u16 flags = 0;
++
++ BT_DBG("sk %p complete %d", sk, result ? 1 : 0);
++
++ if (result)
++ *result = l2cap_conf_output(sk, &ptr);
++ else
++ flags |= 0x0001;
++
++ rsp->scid = __cpu_to_le16(l2cap_pi(sk)->dcid);
++ rsp->result = __cpu_to_le16(result ? *result : 0);
++ rsp->flags = __cpu_to_le16(flags);
++
++ return ptr - data;
++}
++
++static inline int l2cap_connect_req(struct l2cap_conn *conn, l2cap_cmd_hdr *cmd, __u8 *data)
++{
++ struct l2cap_chan_list *list = &conn->chan_list;
++ l2cap_conn_req *req = (l2cap_conn_req *) data;
++ l2cap_conn_rsp rsp;
++ struct sock *sk, *parent;
++ int result = 0, status = 0;
++
++ __u16 dcid = 0, scid = __le16_to_cpu(req->scid);
++ __u16 psm = req->psm;
++
++ BT_DBG("psm 0x%2.2x scid 0x%4.4x", psm, scid);
++
++ /* Check if we have socket listening on psm */
++ parent = l2cap_get_sock_by_psm(BT_LISTEN, psm, conn->src);
++ if (!parent) {
++ result = L2CAP_CR_BAD_PSM;
++ goto sendresp;
++ }
++
++ result = L2CAP_CR_NO_MEM;
++
++ /* Check for backlog size */
++ if (parent->ack_backlog > parent->max_ack_backlog) {
++ BT_DBG("backlog full %d", parent->ack_backlog);
++ goto response;
++ }
++
++ sk = l2cap_sock_alloc(NULL, BTPROTO_L2CAP, GFP_ATOMIC);
++ if (!sk)
++ goto response;
++
++ write_lock(&list->lock);
++
++ /* Check if we already have channel with that dcid */
++ if (__l2cap_get_chan_by_dcid(list, scid)) {
++ write_unlock(&list->lock);
++ sk->zapped = 1;
++ l2cap_sock_kill(sk);
++ goto response;
++ }
++
++ hci_conn_hold(conn->hcon);
++
++ l2cap_sock_init(sk, parent);
++ bacpy(&bluez_pi(sk)->src, conn->src);
++ bacpy(&bluez_pi(sk)->dst, conn->dst);
++ l2cap_pi(sk)->psm = psm;
++ l2cap_pi(sk)->dcid = scid;
++
++ __l2cap_chan_add(conn, sk, parent);
++ dcid = l2cap_pi(sk)->scid;
++
++ l2cap_sock_set_timer(sk, sk->sndtimeo);
++
++ /* Service level security */
++ result = L2CAP_CR_PEND;
++ status = L2CAP_CS_AUTHEN_PEND;
++ sk->state = BT_CONNECT2;
++ l2cap_pi(sk)->ident = cmd->ident;
++
++ if (l2cap_pi(sk)->link_mode & L2CAP_LM_ENCRYPT) {
++ if (!hci_conn_encrypt(conn->hcon))
++ goto done;
++ } else if (l2cap_pi(sk)->link_mode & L2CAP_LM_AUTH) {
++ if (!hci_conn_auth(conn->hcon))
++ goto done;
++ }
++
++ sk->state = BT_CONFIG;
++ result = status = 0;
++
++done:
++ write_unlock(&list->lock);
++
++response:
++ bh_unlock_sock(parent);
++
++sendresp:
++ rsp.scid = __cpu_to_le16(scid);
++ rsp.dcid = __cpu_to_le16(dcid);
++ rsp.result = __cpu_to_le16(result);
++ rsp.status = __cpu_to_le16(status);
++ l2cap_send_rsp(conn, cmd->ident, L2CAP_CONN_RSP, L2CAP_CONN_RSP_SIZE, &rsp);
++ return 0;
++}
++
++static inline int l2cap_connect_rsp(struct l2cap_conn *conn, l2cap_cmd_hdr *cmd, __u8 *data)
++{
++ l2cap_conn_rsp *rsp = (l2cap_conn_rsp *) data;
++ __u16 scid, dcid, result, status;
++ struct sock *sk;
++ char req[128];
++
++ scid = __le16_to_cpu(rsp->scid);
++ dcid = __le16_to_cpu(rsp->dcid);
++ result = __le16_to_cpu(rsp->result);
++ status = __le16_to_cpu(rsp->status);
++
++ BT_DBG("dcid 0x%4.4x scid 0x%4.4x result 0x%2.2x status 0x%2.2x", dcid, scid, result, status);
++
++ if (!(sk = l2cap_get_chan_by_scid(&conn->chan_list, scid)))
++ return -ENOENT;
++
++ switch (result) {
++ case L2CAP_CR_SUCCESS:
++ sk->state = BT_CONFIG;
++ l2cap_pi(sk)->dcid = dcid;
++ l2cap_pi(sk)->conf_state |= L2CAP_CONF_REQ_SENT;
++
++ l2cap_send_req(conn, L2CAP_CONF_REQ, l2cap_build_conf_req(sk, req), req);
++ break;
++
++ case L2CAP_CR_PEND:
++ break;
++
++ default:
++ l2cap_chan_del(sk, ECONNREFUSED);
++ break;
++ }
++
++ bh_unlock_sock(sk);
++ return 0;
++}
++
++static inline int l2cap_config_req(struct l2cap_conn *conn, l2cap_cmd_hdr *cmd, __u8 *data)
++{
++ l2cap_conf_req * req = (l2cap_conf_req *) data;
++ __u16 dcid, flags;
++ __u8 rsp[64];
++ struct sock *sk;
++ int result;
++
++ dcid = __le16_to_cpu(req->dcid);
++ flags = __le16_to_cpu(req->flags);
++
++ BT_DBG("dcid 0x%4.4x flags 0x%2.2x", dcid, flags);
++
++ if (!(sk = l2cap_get_chan_by_scid(&conn->chan_list, dcid)))
++ return -ENOENT;
++
++ l2cap_parse_conf_req(sk, req->data, cmd->len - L2CAP_CONF_REQ_SIZE);
++
++ if (flags & 0x0001) {
++ /* Incomplete config. Send empty response. */
++ l2cap_send_rsp(conn, cmd->ident, L2CAP_CONF_RSP, l2cap_build_conf_rsp(sk, rsp, NULL), rsp);
++ goto unlock;
++ }
++
++ /* Complete config. */
++ l2cap_send_rsp(conn, cmd->ident, L2CAP_CONF_RSP, l2cap_build_conf_rsp(sk, rsp, &result), rsp);
++
++ if (result)
++ goto unlock;
++
++ /* Output config done */
++ l2cap_pi(sk)->conf_state |= L2CAP_CONF_OUTPUT_DONE;
++
++ if (l2cap_pi(sk)->conf_state & L2CAP_CONF_INPUT_DONE) {
++ sk->state = BT_CONNECTED;
++ l2cap_chan_ready(sk);
++ } else if (!(l2cap_pi(sk)->conf_state & L2CAP_CONF_REQ_SENT)) {
++ char req[64];
++ l2cap_send_req(conn, L2CAP_CONF_REQ, l2cap_build_conf_req(sk, req), req);
++ }
++
++unlock:
++ bh_unlock_sock(sk);
++ return 0;
++}
++
++static inline int l2cap_config_rsp(struct l2cap_conn *conn, l2cap_cmd_hdr *cmd, __u8 *data)
++{
++ l2cap_conf_rsp *rsp = (l2cap_conf_rsp *)data;
++ __u16 scid, flags, result;
++ struct sock *sk;
++ int err = 0;
++
++ scid = __le16_to_cpu(rsp->scid);
++ flags = __le16_to_cpu(rsp->flags);
++ result = __le16_to_cpu(rsp->result);
++
++ BT_DBG("scid 0x%4.4x flags 0x%2.2x result 0x%2.2x", scid, flags, result);
++
++ if (!(sk = l2cap_get_chan_by_scid(&conn->chan_list, scid)))
++ return -ENOENT;
++
++ switch (result) {
++ case L2CAP_CONF_SUCCESS:
++ break;
++
++ case L2CAP_CONF_UNACCEPT:
++ if (++l2cap_pi(sk)->conf_retry < L2CAP_CONF_MAX_RETRIES) {
++ char req[128];
++ /*
++ It does not make sense to adjust L2CAP parameters
++ that are currently defined in the spec. We simply
++ resend config request that we sent earlier. It is
++ stupid :) but it helps qualification testing
++ which expects at least some response from us.
++ */
++ l2cap_send_req(conn, L2CAP_CONF_REQ,
++ l2cap_build_conf_req(sk, req), req);
++ goto done;
++ }
++ default:
++ sk->state = BT_DISCONN;
++ sk->err = ECONNRESET;
++ l2cap_sock_set_timer(sk, HZ * 5);
++ {
++ l2cap_disconn_req req;
++ req.dcid = __cpu_to_le16(l2cap_pi(sk)->dcid);
++ req.scid = __cpu_to_le16(l2cap_pi(sk)->scid);
++ l2cap_send_req(conn, L2CAP_DISCONN_REQ, L2CAP_DISCONN_REQ_SIZE, &req);
++ }
++ goto done;
++ }
++
++ if (flags & 0x01)
++ goto done;
++
++ /* Input config done */
++ l2cap_pi(sk)->conf_state |= L2CAP_CONF_INPUT_DONE;
++
++ if (l2cap_pi(sk)->conf_state & L2CAP_CONF_OUTPUT_DONE) {
++ sk->state = BT_CONNECTED;
++ l2cap_chan_ready(sk);
++ }
++
++done:
++ bh_unlock_sock(sk);
++ return err;
++}
++
++static inline int l2cap_disconnect_req(struct l2cap_conn *conn, l2cap_cmd_hdr *cmd, __u8 *data)
++{
++ l2cap_disconn_req *req = (l2cap_disconn_req *) data;
++ l2cap_disconn_rsp rsp;
++ __u16 dcid, scid;
++ struct sock *sk;
++
++ scid = __le16_to_cpu(req->scid);
++ dcid = __le16_to_cpu(req->dcid);
++
++ BT_DBG("scid 0x%4.4x dcid 0x%4.4x", scid, dcid);
++
++ if (!(sk = l2cap_get_chan_by_scid(&conn->chan_list, dcid)))
++ return 0;
++
++ rsp.dcid = __cpu_to_le16(l2cap_pi(sk)->scid);
++ rsp.scid = __cpu_to_le16(l2cap_pi(sk)->dcid);
++ l2cap_send_rsp(conn, cmd->ident, L2CAP_DISCONN_RSP, L2CAP_DISCONN_RSP_SIZE, &rsp);
++
++ sk->shutdown = SHUTDOWN_MASK;
++
++ l2cap_chan_del(sk, ECONNRESET);
++ bh_unlock_sock(sk);
++
++ l2cap_sock_kill(sk);
++ return 0;
++}
++
++static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, l2cap_cmd_hdr *cmd, __u8 *data)
++{
++ l2cap_disconn_rsp *rsp = (l2cap_disconn_rsp *) data;
++ __u16 dcid, scid;
++ struct sock *sk;
++
++ scid = __le16_to_cpu(rsp->scid);
++ dcid = __le16_to_cpu(rsp->dcid);
++
++ BT_DBG("dcid 0x%4.4x scid 0x%4.4x", dcid, scid);
++
++ if (!(sk = l2cap_get_chan_by_scid(&conn->chan_list, scid)))
++ return 0;
++ l2cap_chan_del(sk, 0);
++ bh_unlock_sock(sk);
++
++ l2cap_sock_kill(sk);
++ return 0;
++}
++
++static inline int l2cap_information_req(struct l2cap_conn *conn, l2cap_cmd_hdr *cmd, u8 *data)
++{
++ l2cap_info_req *req = (l2cap_info_req *) data;
++ l2cap_info_rsp rsp;
++ u16 type;
++
++ type = __le16_to_cpu(req->type);
++
++ BT_DBG("type 0x%4.4x", type);
++
++ rsp.type = __cpu_to_le16(type);
++ rsp.result = __cpu_to_le16(L2CAP_IR_NOTSUPP);
++ l2cap_send_rsp(conn, cmd->ident, L2CAP_INFO_RSP, sizeof(rsp), &rsp);
++ return 0;
++}
++
++static inline int l2cap_information_rsp(struct l2cap_conn *conn, l2cap_cmd_hdr *cmd, u8 *data)
++{
++ l2cap_info_rsp *rsp = (l2cap_info_rsp *) data;
++ u16 type, result;
++
++ type = __le16_to_cpu(rsp->type);
++ result = __le16_to_cpu(rsp->result);
++
++ BT_DBG("type 0x%4.4x result 0x%2.2x", type, result);
++
++ return 0;
++}
++
++static inline void l2cap_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb)
++{
++ __u8 *data = skb->data;
++ int len = skb->len;
++ l2cap_cmd_hdr cmd;
++ int err = 0;
++
++ l2cap_raw_recv(conn, skb);
++
++ while (len >= L2CAP_CMD_HDR_SIZE) {
++ memcpy(&cmd, data, L2CAP_CMD_HDR_SIZE);
++ data += L2CAP_CMD_HDR_SIZE;
++ len -= L2CAP_CMD_HDR_SIZE;
++
++ cmd.len = __le16_to_cpu(cmd.len);
++
++ BT_DBG("code 0x%2.2x len %d id 0x%2.2x", cmd.code, cmd.len, cmd.ident);
++
++ if (cmd.len > len || !cmd.ident) {
++ BT_DBG("corrupted command");
++ break;
++ }
++
++ switch (cmd.code) {
++ case L2CAP_COMMAND_REJ:
++ /* FIXME: We should process this */
++ break;
++
++ case L2CAP_CONN_REQ:
++ err = l2cap_connect_req(conn, &cmd, data);
++ break;
++
++ case L2CAP_CONN_RSP:
++ err = l2cap_connect_rsp(conn, &cmd, data);
++ break;
++
++ case L2CAP_CONF_REQ:
++ err = l2cap_config_req(conn, &cmd, data);
++ break;
++
++ case L2CAP_CONF_RSP:
++ err = l2cap_config_rsp(conn, &cmd, data);
++ break;
++
++ case L2CAP_DISCONN_REQ:
++ err = l2cap_disconnect_req(conn, &cmd, data);
++ break;
++
++ case L2CAP_DISCONN_RSP:
++ err = l2cap_disconnect_rsp(conn, &cmd, data);
++ break;
++
++ case L2CAP_ECHO_REQ:
++ l2cap_send_rsp(conn, cmd.ident, L2CAP_ECHO_RSP, cmd.len, data);
++ break;
++
++ case L2CAP_ECHO_RSP:
++ break;
++
++ case L2CAP_INFO_REQ:
++ err = l2cap_information_req(conn, &cmd, data);
++ break;
++
++ case L2CAP_INFO_RSP:
++ err = l2cap_information_rsp(conn, &cmd, data);
++ break;
++
++ default:
++ BT_ERR("Unknown signaling command 0x%2.2x", cmd.code);
++ err = -EINVAL;
++ break;
++ };
++
++ if (err) {
++ l2cap_cmd_rej rej;
++ BT_DBG("error %d", err);
++
++ /* FIXME: Map err to a valid reason */
++ rej.reason = __cpu_to_le16(0);
++ l2cap_send_rsp(conn, cmd.ident, L2CAP_COMMAND_REJ, L2CAP_CMD_REJ_SIZE, &rej);
++ }
++
++ data += cmd.len;
++ len -= cmd.len;
++ }
++
++ kfree_skb(skb);
++}
++
++static inline int l2cap_data_channel(struct l2cap_conn *conn, __u16 cid, struct sk_buff *skb)
++{
++ struct sock *sk;
++
++ sk = l2cap_get_chan_by_scid(&conn->chan_list, cid);
++ if (!sk) {
++ BT_DBG("unknown cid 0x%4.4x", cid);
++ goto drop;
++ }
++
++ BT_DBG("sk %p, len %d", sk, skb->len);
++
++ if (sk->state != BT_CONNECTED)
++ goto drop;
++
++ if (l2cap_pi(sk)->imtu < skb->len)
++ goto drop;
++
++ /* If socket recv buffers overflows we drop data here
++ * which is *bad* because L2CAP has to be reliable.
++ * But we don't have any other choice. L2CAP doesn't
++ * provide flow control mechanism */
++
++ if (!sock_queue_rcv_skb(sk, skb))
++ goto done;
++
++drop:
++ kfree_skb(skb);
++
++done:
++ if (sk) bh_unlock_sock(sk);
++ return 0;
++}
++
++static inline int l2cap_conless_channel(struct l2cap_conn *conn, __u16 psm, struct sk_buff *skb)
++{
++ struct sock *sk;
++
++ sk = l2cap_get_sock_by_psm(0, psm, conn->src);
++ if (!sk)
++ goto drop;
++
++ BT_DBG("sk %p, len %d", sk, skb->len);
++
++ if (sk->state != BT_BOUND && sk->state != BT_CONNECTED)
++ goto drop;
++
++ if (l2cap_pi(sk)->imtu < skb->len)
++ goto drop;
++
++ if (!sock_queue_rcv_skb(sk, skb))
++ goto done;
++
++drop:
++ kfree_skb(skb);
++
++done:
++ if (sk) bh_unlock_sock(sk);
++ return 0;
++}
++
++static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb)
++{
++ l2cap_hdr *lh = (l2cap_hdr *) skb->data;
++ __u16 cid, psm, len;
++
++ skb_pull(skb, L2CAP_HDR_SIZE);
++ cid = __le16_to_cpu(lh->cid);
++ len = __le16_to_cpu(lh->len);
++
++ BT_DBG("len %d, cid 0x%4.4x", len, cid);
++
++ switch (cid) {
++ case 0x0001:
++ l2cap_sig_channel(conn, skb);
++ break;
++
++ case 0x0002:
++ psm = get_unaligned((__u16 *) skb->data);
++ skb_pull(skb, 2);
++ l2cap_conless_channel(conn, psm, skb);
++ break;
++
++ default:
++ l2cap_data_channel(conn, cid, skb);
++ break;
++ }
++}
++
++/* ------------ L2CAP interface with lower layer (HCI) ------------- */
++
++static int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 type)
++{
++ int exact = 0, lm1 = 0, lm2 = 0;
++ register struct sock *sk;
++
++ if (type != ACL_LINK)
++ return 0;
++
++ BT_DBG("hdev %s, bdaddr %s", hdev->name, batostr(bdaddr));
++
++ /* Find listening sockets and check their link_mode */
++ read_lock(&l2cap_sk_list.lock);
++ for (sk = l2cap_sk_list.head; sk; sk = sk->next) {
++ if (sk->state != BT_LISTEN)
++ continue;
++
++ if (!bacmp(&bluez_pi(sk)->src, &hdev->bdaddr)) {
++ lm1 |= (HCI_LM_ACCEPT | l2cap_pi(sk)->link_mode);
++ exact++;
++ } else if (!bacmp(&bluez_pi(sk)->src, BDADDR_ANY))
++ lm2 |= (HCI_LM_ACCEPT | l2cap_pi(sk)->link_mode);
++ }
++ read_unlock(&l2cap_sk_list.lock);
++
++ return exact ? lm1 : lm2;
++}
++
++static int l2cap_connect_cfm(struct hci_conn *hcon, __u8 status)
++{
++ BT_DBG("hcon %p bdaddr %s status %d", hcon, batostr(&hcon->dst), status);
++
++ if (hcon->type != ACL_LINK)
++ return 0;
++
++ if (!status) {
++ struct l2cap_conn *conn;
++
++ conn = l2cap_conn_add(hcon, status);
++ if (conn)
++ l2cap_conn_ready(conn);
++ } else
++ l2cap_conn_del(hcon, bterr(status));
++
++ return 0;
++}
++
++static int l2cap_disconn_ind(struct hci_conn *hcon, __u8 reason)
++{
++ BT_DBG("hcon %p reason %d", hcon, reason);
++
++ if (hcon->type != ACL_LINK)
++ return 0;
++
++ l2cap_conn_del(hcon, bterr(reason));
++ return 0;
++}
++
++static int l2cap_auth_cfm(struct hci_conn *hcon, __u8 status)
++{
++ struct l2cap_chan_list *l;
++ struct l2cap_conn *conn;
++ l2cap_conn_rsp rsp;
++ struct sock *sk;
++ int result;
++
++ if (!(conn = hcon->l2cap_data))
++ return 0;
++ l = &conn->chan_list;
++
++ BT_DBG("conn %p", conn);
++
++ read_lock(&l->lock);
++
++ for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) {
++ bh_lock_sock(sk);
++
++ if (sk->state != BT_CONNECT2 ||
++ (l2cap_pi(sk)->link_mode & L2CAP_LM_ENCRYPT)) {
++ bh_unlock_sock(sk);
++ continue;
++ }
++
++ if (!status) {
++ sk->state = BT_CONFIG;
++ result = 0;
++ } else {
++ sk->state = BT_DISCONN;
++ l2cap_sock_set_timer(sk, HZ/10);
++ result = L2CAP_CR_SEC_BLOCK;
++ }
++
++ rsp.scid = __cpu_to_le16(l2cap_pi(sk)->dcid);
++ rsp.dcid = __cpu_to_le16(l2cap_pi(sk)->scid);
++ rsp.result = __cpu_to_le16(result);
++ rsp.status = __cpu_to_le16(0);
++ l2cap_send_rsp(conn, l2cap_pi(sk)->ident, L2CAP_CONN_RSP,
++ L2CAP_CONN_RSP_SIZE, &rsp);
++
++ bh_unlock_sock(sk);
++ }
++
++ read_unlock(&l->lock);
++ return 0;
++}
++
++static int l2cap_encrypt_cfm(struct hci_conn *hcon, __u8 status)
++{
++ struct l2cap_chan_list *l;
++ struct l2cap_conn *conn;
++ l2cap_conn_rsp rsp;
++ struct sock *sk;
++ int result;
++
++ if (!(conn = hcon->l2cap_data))
++ return 0;
++ l = &conn->chan_list;
++
++ BT_DBG("conn %p", conn);
++
++ read_lock(&l->lock);
++
++ for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) {
++ bh_lock_sock(sk);
++
++ if (sk->state != BT_CONNECT2) {
++ bh_unlock_sock(sk);
++ continue;
++ }
++
++ if (!status) {
++ sk->state = BT_CONFIG;
++ result = 0;
++ } else {
++ sk->state = BT_DISCONN;
++ l2cap_sock_set_timer(sk, HZ/10);
++ result = L2CAP_CR_SEC_BLOCK;
++ }
++
++ rsp.scid = __cpu_to_le16(l2cap_pi(sk)->dcid);
++ rsp.dcid = __cpu_to_le16(l2cap_pi(sk)->scid);
++ rsp.result = __cpu_to_le16(result);
++ rsp.status = __cpu_to_le16(0);
++ l2cap_send_rsp(conn, l2cap_pi(sk)->ident, L2CAP_CONN_RSP,
++ L2CAP_CONN_RSP_SIZE, &rsp);
++
++ bh_unlock_sock(sk);
++ }
++
++ read_unlock(&l->lock);
++ return 0;
++}
++
++static int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, __u16 flags)
++{
++ struct l2cap_conn *conn = hcon->l2cap_data;
++
++ if (!conn && !(conn = l2cap_conn_add(hcon, 0)))
++ goto drop;
++
++ BT_DBG("conn %p len %d flags 0x%x", conn, skb->len, flags);
++
++ if (flags & ACL_START) {
++ l2cap_hdr *hdr;
++ int len;
++
++ if (conn->rx_len) {
++ BT_ERR("Unexpected start frame (len %d)", skb->len);
++ kfree_skb(conn->rx_skb);
++ conn->rx_skb = NULL;
++ conn->rx_len = 0;
++ l2cap_conn_unreliable(conn, ECOMM);
++ }
++
++ if (skb->len < 2) {
++ BT_ERR("Frame is too short (len %d)", skb->len);
++ l2cap_conn_unreliable(conn, ECOMM);
++ goto drop;
++ }
++
++ hdr = (l2cap_hdr *) skb->data;
++ len = __le16_to_cpu(hdr->len) + L2CAP_HDR_SIZE;
++
++ if (len == skb->len) {
++ /* Complete frame received */
++ l2cap_recv_frame(conn, skb);
++ return 0;
++ }
++
++ BT_DBG("Start: total len %d, frag len %d", len, skb->len);
++
++ if (skb->len > len) {
++ BT_ERR("Frame is too long (len %d, expected len %d)",
++ skb->len, len);
++ l2cap_conn_unreliable(conn, ECOMM);
++ goto drop;
++ }
++
++ /* Allocate skb for the complete frame including header */
++ conn->rx_skb = bluez_skb_alloc(len, GFP_ATOMIC);
++ if (!conn->rx_skb)
++ goto drop;
++
++ memcpy(skb_put(conn->rx_skb, skb->len), skb->data, skb->len);
++ conn->rx_len = len - skb->len;
++ } else {
++ BT_DBG("Cont: frag len %d (expecting %d)", skb->len, conn->rx_len);
++
++ if (!conn->rx_len) {
++ BT_ERR("Unexpected continuation frame (len %d)", skb->len);
++ l2cap_conn_unreliable(conn, ECOMM);
++ goto drop;
++ }
++
++ if (skb->len > conn->rx_len) {
++ BT_ERR("Fragment is too long (len %d, expected %d)",
++ skb->len, conn->rx_len);
++ kfree_skb(conn->rx_skb);
++ conn->rx_skb = NULL;
++ conn->rx_len = 0;
++ l2cap_conn_unreliable(conn, ECOMM);
++ goto drop;
++ }
++
++ memcpy(skb_put(conn->rx_skb, skb->len), skb->data, skb->len);
++ conn->rx_len -= skb->len;
++
++ if (!conn->rx_len) {
++ /* Complete frame received */
++ l2cap_recv_frame(conn, conn->rx_skb);
++ conn->rx_skb = NULL;
++ }
++ }
++
++drop:
++ kfree_skb(skb);
++ return 0;
++}
++
++/* ----- Proc fs support ------ */
++static int l2cap_sock_dump(char *buf, struct bluez_sock_list *list)
++{
++ struct l2cap_pinfo *pi;
++ struct sock *sk;
++ char *ptr = buf;
++
++ read_lock_bh(&list->lock);
++
++ for (sk = list->head; sk; sk = sk->next) {
++ pi = l2cap_pi(sk);
++ ptr += sprintf(ptr, "%s %s %d %d 0x%4.4x 0x%4.4x %d %d 0x%x\n",
++ batostr(&bluez_pi(sk)->src), batostr(&bluez_pi(sk)->dst),
++ sk->state, pi->psm, pi->scid, pi->dcid, pi->imtu, pi->omtu,
++ pi->link_mode);
++ }
++
++ read_unlock_bh(&list->lock);
++
++ ptr += sprintf(ptr, "\n");
++ return ptr - buf;
++}
++
++static int l2cap_read_proc(char *buf, char **start, off_t offset, int count, int *eof, void *priv)
++{
++ char *ptr = buf;
++ int len;
++
++ BT_DBG("count %d, offset %ld", count, offset);
++
++ ptr += l2cap_sock_dump(ptr, &l2cap_sk_list);
++ len = ptr - buf;
++
++ if (len <= count + offset)
++ *eof = 1;
++
++ *start = buf + offset;
++ len -= offset;
++
++ if (len > count)
++ len = count;
++ if (len < 0)
++ len = 0;
++
++ return len;
++}
++
++static struct proto_ops l2cap_sock_ops = {
++ family: PF_BLUETOOTH,
++ release: l2cap_sock_release,
++ bind: l2cap_sock_bind,
++ connect: l2cap_sock_connect,
++ listen: l2cap_sock_listen,
++ accept: l2cap_sock_accept,
++ getname: l2cap_sock_getname,
++ sendmsg: l2cap_sock_sendmsg,
++ recvmsg: bluez_sock_recvmsg,
++ poll: bluez_sock_poll,
++ socketpair: sock_no_socketpair,
++ ioctl: sock_no_ioctl,
++ shutdown: l2cap_sock_shutdown,
++ setsockopt: l2cap_sock_setsockopt,
++ getsockopt: l2cap_sock_getsockopt,
++ mmap: sock_no_mmap
++};
++
++static struct net_proto_family l2cap_sock_family_ops = {
++ family: PF_BLUETOOTH,
++ create: l2cap_sock_create
++};
++
++static struct hci_proto l2cap_hci_proto = {
++ name: "L2CAP",
++ id: HCI_PROTO_L2CAP,
++ connect_ind: l2cap_connect_ind,
++ connect_cfm: l2cap_connect_cfm,
++ disconn_ind: l2cap_disconn_ind,
++ recv_acldata: l2cap_recv_acldata,
++ auth_cfm: l2cap_auth_cfm,
++ encrypt_cfm: l2cap_encrypt_cfm
++};
++
++int __init l2cap_init(void)
++{
++ int err;
++
++ if ((err = bluez_sock_register(BTPROTO_L2CAP, &l2cap_sock_family_ops))) {
++ BT_ERR("Can't register L2CAP socket");
++ return err;
++ }
++
++ if ((err = hci_register_proto(&l2cap_hci_proto))) {
++ BT_ERR("Can't register L2CAP protocol");
++ return err;
++ }
++
++ create_proc_read_entry("bluetooth/l2cap", 0, 0, l2cap_read_proc, NULL);
++
++ BT_INFO("BlueZ L2CAP ver %s Copyright (C) 2000,2001 Qualcomm Inc", VERSION);
++ BT_INFO("Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>");
++ return 0;
++}
++
++void l2cap_cleanup(void)
++{
++ remove_proc_entry("bluetooth/l2cap", NULL);
++
++ /* Unregister socket and protocol */
++ if (bluez_sock_unregister(BTPROTO_L2CAP))
++ BT_ERR("Can't unregister L2CAP socket");
++
++ if (hci_unregister_proto(&l2cap_hci_proto))
++ BT_ERR("Can't unregister L2CAP protocol");
++}
++
++void l2cap_load(void)
++{
++ /* Dummy function to trigger automatic L2CAP module loading by
++ other modules that use L2CAP sockets but do not use any other
++ symbols from it. */
++ return;
++}
++
++EXPORT_SYMBOL(l2cap_load);
++
++module_init(l2cap_init);
++module_exit(l2cap_cleanup);
++
++MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>");
++MODULE_DESCRIPTION("BlueZ L2CAP ver " VERSION);
++MODULE_LICENSE("GPL");
+diff -urN linux-2.4.18/net/bluetooth/l2cap_core.c linux-2.4.18-mh15/net/bluetooth/l2cap_core.c
+--- linux-2.4.18/net/bluetooth/l2cap_core.c 2001-09-30 21:26:08.000000000 +0200
++++ linux-2.4.18-mh15/net/bluetooth/l2cap_core.c 1970-01-01 01:00:00.000000000 +0100
+@@ -1,2316 +0,0 @@
+-/*
+- BlueZ - Bluetooth protocol stack for Linux
+- Copyright (C) 2000-2001 Qualcomm Incorporated
+-
+- Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
+-
+- This program is free software; you can redistribute it and/or modify
+- it under the terms of the GNU General Public License version 2 as
+- published by the Free Software Foundation;
+-
+- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+- OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+- IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+- CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
+- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+-
+- ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
+- COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
+- SOFTWARE IS DISCLAIMED.
+-*/
+-
+-/*
+- * BlueZ L2CAP core and sockets.
+- *
+- * $Id$
+- */
+-#define VERSION "1.1"
+-
+-#include <linux/config.h>
+-#include <linux/module.h>
+-
+-#include <linux/types.h>
+-#include <linux/errno.h>
+-#include <linux/kernel.h>
+-#include <linux/major.h>
+-#include <linux/sched.h>
+-#include <linux/slab.h>
+-#include <linux/poll.h>
+-#include <linux/fcntl.h>
+-#include <linux/init.h>
+-#include <linux/skbuff.h>
+-#include <linux/interrupt.h>
+-#include <linux/socket.h>
+-#include <linux/skbuff.h>
+-#include <linux/proc_fs.h>
+-#include <linux/list.h>
+-#include <net/sock.h>
+-
+-#include <asm/system.h>
+-#include <asm/uaccess.h>
+-
+-#include <net/bluetooth/bluetooth.h>
+-#include <net/bluetooth/bluez.h>
+-#include <net/bluetooth/hci_core.h>
+-#include <net/bluetooth/l2cap.h>
+-#include <net/bluetooth/l2cap_core.h>
+-
+-#ifndef L2CAP_DEBUG
+-#undef DBG
+-#define DBG( A... )
+-#endif
+-
+-struct proto_ops l2cap_sock_ops;
+-
+-struct bluez_sock_list l2cap_sk_list = {
+- lock: RW_LOCK_UNLOCKED
+-};
+-
+-struct list_head l2cap_iff_list = LIST_HEAD_INIT(l2cap_iff_list);
+-rwlock_t l2cap_rt_lock = RW_LOCK_UNLOCKED;
+-
+-static int l2cap_conn_del(struct l2cap_conn *conn, int err);
+-
+-static inline void l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct sock *parent);
+-static void l2cap_chan_del(struct sock *sk, int err);
+-static int l2cap_chan_send(struct sock *sk, struct msghdr *msg, int len);
+-
+-static void l2cap_sock_close(struct sock *sk);
+-static void l2cap_sock_kill(struct sock *sk);
+-
+-static int l2cap_send_req(struct l2cap_conn *conn, __u8 code, __u16 len, void *data);
+-static int l2cap_send_rsp(struct l2cap_conn *conn, __u8 ident, __u8 code, __u16 len, void *data);
+-
+-/* -------- L2CAP interfaces & routing --------- */
+-/* Add/delete L2CAP interface.
+- * Must be called with locked rt_lock
+- */
+-
+-static void l2cap_iff_add(struct hci_dev *hdev)
+-{
+- struct l2cap_iff *iff;
+-
+- DBG("%s", hdev->name);
+-
+- DBG("iff_list %p next %p prev %p", &l2cap_iff_list, l2cap_iff_list.next, l2cap_iff_list.prev);
+-
+- /* Allocate new interface and lock HCI device */
+- if (!(iff = kmalloc(sizeof(struct l2cap_iff), GFP_KERNEL))) {
+- ERR("Can't allocate new interface %s", hdev->name);
+- return;
+- }
+- memset(iff, 0, sizeof(struct l2cap_iff));
+-
+- hci_dev_hold(hdev);
+- hdev->l2cap_data = iff;
+- iff->hdev = hdev;
+- iff->mtu = hdev->acl_mtu - HCI_ACL_HDR_SIZE;
+- iff->bdaddr = &hdev->bdaddr;
+-
+- spin_lock_init(&iff->lock);
+- INIT_LIST_HEAD(&iff->conn_list);
+-
+- list_add(&iff->list, &l2cap_iff_list);
+-}
+-
+-static void l2cap_iff_del(struct hci_dev *hdev)
+-{
+- struct l2cap_iff *iff;
+-
+- if (!(iff = hdev->l2cap_data))
+- return;
+-
+- DBG("%s iff %p", hdev->name, iff);
+-
+- list_del(&iff->list);
+-
+- l2cap_iff_lock(iff);
+-
+- /* Drop connections */
+- while (!list_empty(&iff->conn_list)) {
+- struct l2cap_conn *c;
+-
+- c = list_entry(iff->conn_list.next, struct l2cap_conn, list);
+- l2cap_conn_del(c, ENODEV);
+- }
+-
+- l2cap_iff_unlock(iff);
+-
+- /* Unlock HCI device */
+- hdev->l2cap_data = NULL;
+- hci_dev_put(hdev);
+-
+- kfree(iff);
+-}
+-
+-/* Get route. Returns L2CAP interface.
+- * Must be called with locked rt_lock
+- */
+-static struct l2cap_iff *l2cap_get_route(bdaddr_t *src, bdaddr_t *dst)
+-{
+- struct list_head *p;
+- int use_src;
+-
+- DBG("%s -> %s", batostr(src), batostr(dst));
+-
+- use_src = bacmp(src, BDADDR_ANY) ? 0 : 1;
+-
+- /* Simple routing:
+- * No source address - find interface with bdaddr != dst
+- * Source address - find interface with bdaddr == src
+- */
+-
+- list_for_each(p, &l2cap_iff_list) {
+- struct l2cap_iff *iff;
+-
+- iff = list_entry(p, struct l2cap_iff, list);
+-
+- if (use_src && !bacmp(iff->bdaddr, src))
+- return iff;
+- else if (bacmp(iff->bdaddr, dst))
+- return iff;
+- }
+- return NULL;
+-}
+-
+-/* ----- L2CAP timers ------ */
+-static void l2cap_sock_timeout(unsigned long arg)
+-{
+- struct sock *sk = (struct sock *) arg;
+-
+- DBG("sock %p state %d", sk, sk->state);
+-
+- bh_lock_sock(sk);
+- switch (sk->state) {
+- case BT_DISCONN:
+- l2cap_chan_del(sk, ETIMEDOUT);
+- break;
+-
+- default:
+- sk->err = ETIMEDOUT;
+- sk->state_change(sk);
+- break;
+- };
+- bh_unlock_sock(sk);
+-
+- l2cap_sock_kill(sk);
+- sock_put(sk);
+-}
+-
+-static void l2cap_sock_set_timer(struct sock *sk, long timeout)
+-{
+- DBG("sock %p state %d timeout %ld", sk, sk->state, timeout);
+-
+- if (!mod_timer(&sk->timer, jiffies + timeout))
+- sock_hold(sk);
+-}
+-
+-static void l2cap_sock_clear_timer(struct sock *sk)
+-{
+- DBG("sock %p state %d", sk, sk->state);
+-
+- if (timer_pending(&sk->timer) && del_timer(&sk->timer))
+- __sock_put(sk);
+-}
+-
+-static void l2cap_sock_init_timer(struct sock *sk)
+-{
+- init_timer(&sk->timer);
+- sk->timer.function = l2cap_sock_timeout;
+- sk->timer.data = (unsigned long)sk;
+-}
+-
+-static void l2cap_conn_timeout(unsigned long arg)
+-{
+- struct l2cap_conn *conn = (void *)arg;
+-
+- DBG("conn %p state %d", conn, conn->state);
+-
+- if (conn->state == BT_CONNECTED) {
+- hci_disconnect(conn->hconn, 0x13);
+- }
+-
+- return;
+-}
+-
+-static void l2cap_conn_set_timer(struct l2cap_conn *conn, long timeout)
+-{
+- DBG("conn %p state %d timeout %ld", conn, conn->state, timeout);
+-
+- mod_timer(&conn->timer, jiffies + timeout);
+-}
+-
+-static void l2cap_conn_clear_timer(struct l2cap_conn *conn)
+-{
+- DBG("conn %p state %d", conn, conn->state);
+-
+- del_timer(&conn->timer);
+-}
+-
+-static void l2cap_conn_init_timer(struct l2cap_conn *conn)
+-{
+- init_timer(&conn->timer);
+- conn->timer.function = l2cap_conn_timeout;
+- conn->timer.data = (unsigned long)conn;
+-}
+-
+-/* -------- L2CAP connections --------- */
+-/* Add new connection to the interface.
+- * Interface must be locked
+- */
+-static struct l2cap_conn *l2cap_conn_add(struct l2cap_iff *iff, bdaddr_t *dst)
+-{
+- struct l2cap_conn *conn;
+- bdaddr_t *src = iff->bdaddr;
+-
+- if (!(conn = kmalloc(sizeof(struct l2cap_conn), GFP_KERNEL)))
+- return NULL;
+-
+- memset(conn, 0, sizeof(struct l2cap_conn));
+-
+- conn->state = BT_OPEN;
+- conn->iff = iff;
+- bacpy(&conn->src, src);
+- bacpy(&conn->dst, dst);
+-
+- spin_lock_init(&conn->lock);
+- conn->chan_list.lock = RW_LOCK_UNLOCKED;
+-
+- l2cap_conn_init_timer(conn);
+-
+- __l2cap_conn_link(iff, conn);
+-
+- DBG("%s -> %s, %p", batostr(src), batostr(dst), conn);
+-
+- MOD_INC_USE_COUNT;
+-
+- return conn;
+-}
+-
+-/* Delete connection on the interface.
+- * Interface must be locked
+- */
+-static int l2cap_conn_del(struct l2cap_conn *conn, int err)
+-{
+- struct sock *sk;
+-
+- DBG("conn %p, state %d, err %d", conn, conn->state, err);
+-
+- l2cap_conn_clear_timer(conn);
+- __l2cap_conn_unlink(conn->iff, conn);
+-
+- conn->state = BT_CLOSED;
+-
+- if (conn->rx_skb)
+- kfree_skb(conn->rx_skb);
+-
+- /* Kill channels */
+- while ((sk = conn->chan_list.head)) {
+- bh_lock_sock(sk);
+- l2cap_sock_clear_timer(sk);
+- l2cap_chan_del(sk, err);
+- bh_unlock_sock(sk);
+-
+- l2cap_sock_kill(sk);
+- }
+-
+- kfree(conn);
+-
+- MOD_DEC_USE_COUNT;
+- return 0;
+-}
+-
+-static inline struct l2cap_conn *l2cap_get_conn_by_addr(struct l2cap_iff *iff, bdaddr_t *dst)
+-{
+- struct list_head *p;
+-
+- list_for_each(p, &iff->conn_list) {
+- struct l2cap_conn *c;
+-
+- c = list_entry(p, struct l2cap_conn, list);
+- if (!bacmp(&c->dst, dst))
+- return c;
+- }
+- return NULL;
+-}
+-
+-int l2cap_connect(struct sock *sk)
+-{
+- bdaddr_t *src = &l2cap_pi(sk)->src;
+- bdaddr_t *dst = &l2cap_pi(sk)->dst;
+- struct l2cap_conn *conn;
+- struct l2cap_iff *iff;
+- int err = 0;
+-
+- DBG("%s -> %s psm 0x%2.2x", batostr(src), batostr(dst), l2cap_pi(sk)->psm);
+-
+- read_lock_bh(&l2cap_rt_lock);
+-
+- /* Get route to remote BD address */
+- if (!(iff = l2cap_get_route(src, dst))) {
+- err = -EHOSTUNREACH;
+- goto done;
+- }
+-
+- /* Update source addr of the socket */
+- bacpy(src, iff->bdaddr);
+-
+- l2cap_iff_lock(iff);
+-
+- if (!(conn = l2cap_get_conn_by_addr(iff, dst))) {
+- /* Connection doesn't exist */
+- if (!(conn = l2cap_conn_add(iff, dst))) {
+- l2cap_iff_unlock(iff);
+- err = -ENOMEM;
+- goto done;
+- }
+- conn->out = 1;
+- }
+-
+- l2cap_iff_unlock(iff);
+-
+- l2cap_chan_add(conn, sk, NULL);
+-
+- sk->state = BT_CONNECT;
+- l2cap_sock_set_timer(sk, sk->sndtimeo);
+-
+- switch (conn->state) {
+- case BT_CONNECTED:
+- if (sk->type == SOCK_SEQPACKET) {
+- l2cap_conn_req req;
+- req.scid = __cpu_to_le16(l2cap_pi(sk)->scid);
+- req.psm = l2cap_pi(sk)->psm;
+- l2cap_send_req(conn, L2CAP_CONN_REQ, L2CAP_CONN_REQ_SIZE, &req);
+- } else {
+- l2cap_sock_clear_timer(sk);
+- sk->state = BT_CONNECTED;
+- }
+- break;
+-
+- case BT_CONNECT:
+- break;
+-
+- default:
+- /* Create ACL connection */
+- conn->state = BT_CONNECT;
+- hci_connect(iff->hdev, dst);
+- break;
+- };
+-
+-done:
+- read_unlock_bh(&l2cap_rt_lock);
+- return err;
+-}
+-
+-/* ------ Channel queues for listening sockets ------ */
+-void l2cap_accept_queue(struct sock *parent, struct sock *sk)
+-{
+- struct l2cap_accept_q *q = &l2cap_pi(parent)->accept_q;
+-
+- DBG("parent %p, sk %p", parent, sk);
+-
+- sock_hold(sk);
+- l2cap_pi(sk)->parent = parent;
+- l2cap_pi(sk)->next_q = NULL;
+-
+- if (!q->head) {
+- q->head = q->tail = sk;
+- } else {
+- struct sock *tail = q->tail;
+-
+- l2cap_pi(sk)->prev_q = tail;
+- l2cap_pi(tail)->next_q = sk;
+- q->tail = sk;
+- }
+-
+- parent->ack_backlog++;
+-}
+-
+-void l2cap_accept_unlink(struct sock *sk)
+-{
+- struct sock *parent = l2cap_pi(sk)->parent;
+- struct l2cap_accept_q *q = &l2cap_pi(parent)->accept_q;
+- struct sock *next, *prev;
+-
+- DBG("sk %p", sk);
+-
+- next = l2cap_pi(sk)->next_q;
+- prev = l2cap_pi(sk)->prev_q;
+-
+- if (sk == q->head)
+- q->head = next;
+- if (sk == q->tail)
+- q->tail = prev;
+-
+- if (next)
+- l2cap_pi(next)->prev_q = prev;
+- if (prev)
+- l2cap_pi(prev)->next_q = next;
+-
+- l2cap_pi(sk)->parent = NULL;
+-
+- parent->ack_backlog--;
+- __sock_put(sk);
+-}
+-
+-/* Get next connected channel in queue. */
+-struct sock *l2cap_accept_dequeue(struct sock *parent, int state)
+-{
+- struct l2cap_accept_q *q = &l2cap_pi(parent)->accept_q;
+- struct sock *sk;
+-
+- for (sk = q->head; sk; sk = l2cap_pi(sk)->next_q) {
+- if (!state || sk->state == state) {
+- l2cap_accept_unlink(sk);
+- break;
+- }
+- }
+-
+- DBG("parent %p, sk %p", parent, sk);
+-
+- return sk;
+-}
+-
+-/* -------- Socket interface ---------- */
+-static struct sock *__l2cap_get_sock_by_addr(struct sockaddr_l2 *addr)
+-{
+- bdaddr_t *src = &addr->l2_bdaddr;
+- __u16 psm = addr->l2_psm;
+- struct sock *sk;
+-
+- for (sk = l2cap_sk_list.head; sk; sk = sk->next) {
+- if (l2cap_pi(sk)->psm == psm &&
+- !bacmp(&l2cap_pi(sk)->src, src))
+- break;
+- }
+-
+- return sk;
+-}
+-
+-/* Find socket listening on psm and source bdaddr.
+- * Returns closest match.
+- */
+-static struct sock *l2cap_get_sock_listen(bdaddr_t *src, __u16 psm)
+-{
+- struct sock *sk, *sk1 = NULL;
+-
+- read_lock(&l2cap_sk_list.lock);
+-
+- for (sk = l2cap_sk_list.head; sk; sk = sk->next) {
+- struct l2cap_pinfo *pi;
+-
+- if (sk->state != BT_LISTEN)
+- continue;
+-
+- pi = l2cap_pi(sk);
+-
+- if (pi->psm == psm) {
+- /* Exact match. */
+- if (!bacmp(&pi->src, src))
+- break;
+-
+- /* Closest match */
+- if (!bacmp(&pi->src, BDADDR_ANY))
+- sk1 = sk;
+- }
+- }
+-
+- read_unlock(&l2cap_sk_list.lock);
+-
+- return sk ? sk : sk1;
+-}
+-
+-static void l2cap_sock_destruct(struct sock *sk)
+-{
+- DBG("sk %p", sk);
+-
+- skb_queue_purge(&sk->receive_queue);
+- skb_queue_purge(&sk->write_queue);
+-
+- MOD_DEC_USE_COUNT;
+-}
+-
+-static void l2cap_sock_cleanup_listen(struct sock *parent)
+-{
+- struct sock *sk;
+-
+- DBG("parent %p", parent);
+-
+- /* Close not yet accepted channels */
+- while ((sk = l2cap_accept_dequeue(parent, 0)))
+- l2cap_sock_close(sk);
+-
+- parent->state = BT_CLOSED;
+- parent->zapped = 1;
+-}
+-
+-/* Kill socket (only if zapped and orphan)
+- * Must be called on unlocked socket.
+- */
+-static void l2cap_sock_kill(struct sock *sk)
+-{
+- if (!sk->zapped || sk->socket)
+- return;
+-
+- DBG("sk %p state %d", sk, sk->state);
+-
+- /* Kill poor orphan */
+- bluez_sock_unlink(&l2cap_sk_list, sk);
+- sk->dead = 1;
+- sock_put(sk);
+-}
+-
+-/* Close socket.
+- * Must be called on unlocked socket.
+- */
+-static void l2cap_sock_close(struct sock *sk)
+-{
+- struct l2cap_conn *conn;
+-
+- l2cap_sock_clear_timer(sk);
+-
+- lock_sock(sk);
+-
+- conn = l2cap_pi(sk)->conn;
+-
+- DBG("sk %p state %d conn %p socket %p", sk, sk->state, conn, sk->socket);
+-
+- switch (sk->state) {
+- case BT_LISTEN:
+- l2cap_sock_cleanup_listen(sk);
+- break;
+-
+- case BT_CONNECTED:
+- case BT_CONFIG:
+- if (sk->type == SOCK_SEQPACKET) {
+- l2cap_disconn_req req;
+-
+- sk->state = BT_DISCONN;
+-
+- req.dcid = __cpu_to_le16(l2cap_pi(sk)->dcid);
+- req.scid = __cpu_to_le16(l2cap_pi(sk)->scid);
+- l2cap_send_req(conn, L2CAP_DISCONN_REQ, L2CAP_DISCONN_REQ_SIZE, &req);
+-
+- l2cap_sock_set_timer(sk, sk->sndtimeo);
+- } else {
+- l2cap_chan_del(sk, ECONNRESET);
+- }
+- break;
+-
+- case BT_CONNECT:
+- case BT_DISCONN:
+- l2cap_chan_del(sk, ECONNRESET);
+- break;
+-
+- default:
+- sk->zapped = 1;
+- break;
+- };
+-
+- release_sock(sk);
+-
+- l2cap_sock_kill(sk);
+-}
+-
+-static void l2cap_sock_init(struct sock *sk, struct sock *parent)
+-{
+- struct l2cap_pinfo *pi = l2cap_pi(sk);
+-
+- DBG("sk %p", sk);
+-
+- if (parent) {
+- sk->type = parent->type;
+-
+- pi->imtu = l2cap_pi(parent)->imtu;
+- pi->omtu = l2cap_pi(parent)->omtu;
+- } else {
+- pi->imtu = L2CAP_DEFAULT_MTU;
+- pi->omtu = 0;
+- }
+-
+- /* Default config options */
+- pi->conf_mtu = L2CAP_DEFAULT_MTU;
+- pi->flush_to = L2CAP_DEFAULT_FLUSH_TO;
+-}
+-
+-static struct sock *l2cap_sock_alloc(struct socket *sock, int proto, int prio)
+-{
+- struct sock *sk;
+-
+- if (!(sk = sk_alloc(PF_BLUETOOTH, prio, 1)))
+- return NULL;
+-
+- sock_init_data(sock, sk);
+-
+- sk->zapped = 0;
+-
+- sk->destruct = l2cap_sock_destruct;
+- sk->sndtimeo = L2CAP_CONN_TIMEOUT;
+-
+- sk->protocol = proto;
+- sk->state = BT_OPEN;
+-
+- l2cap_sock_init_timer(sk);
+-
+- bluez_sock_link(&l2cap_sk_list, sk);
+-
+- MOD_INC_USE_COUNT;
+-
+- return sk;
+-}
+-
+-static int l2cap_sock_create(struct socket *sock, int protocol)
+-{
+- struct sock *sk;
+-
+- DBG("sock %p", sock);
+-
+- sock->state = SS_UNCONNECTED;
+-
+- if (sock->type != SOCK_SEQPACKET && sock->type != SOCK_RAW)
+- return -ESOCKTNOSUPPORT;
+-
+- sock->ops = &l2cap_sock_ops;
+-
+- if (!(sk = l2cap_sock_alloc(sock, protocol, GFP_KERNEL)))
+- return -ENOMEM;
+-
+- l2cap_sock_init(sk, NULL);
+-
+- return 0;
+-}
+-
+-static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len)
+-{
+- struct sockaddr_l2 *la = (struct sockaddr_l2 *) addr;
+- struct sock *sk = sock->sk;
+- int err = 0;
+-
+- DBG("sk %p, %s %d", sk, batostr(&la->l2_bdaddr), la->l2_psm);
+-
+- if (!addr || addr->sa_family != AF_BLUETOOTH)
+- return -EINVAL;
+-
+- lock_sock(sk);
+-
+- if (sk->state != BT_OPEN) {
+- err = -EBADFD;
+- goto done;
+- }
+-
+- write_lock(&l2cap_sk_list.lock);
+-
+- if (la->l2_psm && __l2cap_get_sock_by_addr(la)) {
+- err = -EADDRINUSE;
+- goto unlock;
+- }
+-
+- /* Save source address */
+- bacpy(&l2cap_pi(sk)->src, &la->l2_bdaddr);
+- l2cap_pi(sk)->psm = la->l2_psm;
+- sk->state = BT_BOUND;
+-
+-unlock:
+- write_unlock(&l2cap_sk_list.lock);
+-
+-done:
+- release_sock(sk);
+-
+- return err;
+-}
+-
+-static int l2cap_sock_w4_connect(struct sock *sk, int flags)
+-{
+- DECLARE_WAITQUEUE(wait, current);
+- long timeo = sock_sndtimeo(sk, flags & O_NONBLOCK);
+- int err = 0;
+-
+- DBG("sk %p", sk);
+-
+- add_wait_queue(sk->sleep, &wait);
+- current->state = TASK_INTERRUPTIBLE;
+-
+- while (sk->state != BT_CONNECTED) {
+- if (!timeo) {
+- err = -EAGAIN;
+- break;
+- }
+-
+- release_sock(sk);
+- timeo = schedule_timeout(timeo);
+- lock_sock(sk);
+-
+- err = 0;
+- if (sk->state == BT_CONNECTED)
+- break;
+-
+- if (sk->err) {
+- err = sock_error(sk);
+- break;
+- }
+-
+- if (signal_pending(current)) {
+- err = sock_intr_errno(timeo);
+- break;
+- }
+- }
+- current->state = TASK_RUNNING;
+- remove_wait_queue(sk->sleep, &wait);
+-
+- return err;
+-}
+-
+-static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int alen, int flags)
+-{
+- struct sockaddr_l2 *la = (struct sockaddr_l2 *) addr;
+- struct sock *sk = sock->sk;
+- int err = 0;
+-
+- lock_sock(sk);
+-
+- DBG("sk %p", sk);
+-
+- if (addr->sa_family != AF_BLUETOOTH || alen < sizeof(struct sockaddr_l2)) {
+- err = -EINVAL;
+- goto done;
+- }
+-
+- if (sk->state != BT_OPEN && sk->state != BT_BOUND) {
+- err = -EBADFD;
+- goto done;
+- }
+-
+- if (sk->type == SOCK_SEQPACKET && !la->l2_psm) {
+- err = -EINVAL;
+- goto done;
+- }
+-
+- /* Set destination address and psm */
+- bacpy(&l2cap_pi(sk)->dst, &la->l2_bdaddr);
+- l2cap_pi(sk)->psm = la->l2_psm;
+-
+- if ((err = l2cap_connect(sk)))
+- goto done;
+-
+- err = l2cap_sock_w4_connect(sk, flags);
+-
+-done:
+- release_sock(sk);
+- return err;
+-}
+-
+-int l2cap_sock_listen(struct socket *sock, int backlog)
+-{
+- struct sock *sk = sock->sk;
+- int err = 0;
+-
+- DBG("sk %p backlog %d", sk, backlog);
+-
+- lock_sock(sk);
+-
+- if (sk->state != BT_BOUND || sock->type != SOCK_SEQPACKET) {
+- err = -EBADFD;
+- goto done;
+- }
+-
+- if (!l2cap_pi(sk)->psm) {
+- err = -EINVAL;
+- goto done;
+- }
+-
+- sk->max_ack_backlog = backlog;
+- sk->ack_backlog = 0;
+- sk->state = BT_LISTEN;
+-
+-done:
+- release_sock(sk);
+- return err;
+-}
+-
+-int l2cap_sock_accept(struct socket *sock, struct socket *newsock, int flags)
+-{
+- DECLARE_WAITQUEUE(wait, current);
+- struct sock *sk = sock->sk, *ch;
+- long timeo;
+- int err = 0;
+-
+- lock_sock(sk);
+-
+- if (sk->state != BT_LISTEN) {
+- err = -EBADFD;
+- goto done;
+- }
+-
+- timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK);
+-
+- DBG("sk %p timeo %ld", sk, timeo);
+-
+- /* Wait for an incoming connection. (wake-one). */
+- add_wait_queue_exclusive(sk->sleep, &wait);
+- current->state = TASK_INTERRUPTIBLE;
+- while (!(ch = l2cap_accept_dequeue(sk, BT_CONNECTED))) {
+- if (!timeo) {
+- err = -EAGAIN;
+- break;
+- }
+-
+- release_sock(sk);
+- timeo = schedule_timeout(timeo);
+- lock_sock(sk);
+-
+- if (sk->state != BT_LISTEN) {
+- err = -EBADFD;
+- break;
+- }
+-
+- if (signal_pending(current)) {
+- err = sock_intr_errno(timeo);
+- break;
+- }
+- }
+- current->state = TASK_RUNNING;
+- remove_wait_queue(sk->sleep, &wait);
+-
+- if (err)
+- goto done;
+-
+- sock_graft(ch, newsock);
+- newsock->state = SS_CONNECTED;
+-
+- DBG("new socket %p", ch);
+-
+-done:
+- release_sock(sk);
+-
+- return err;
+-}
+-
+-static int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr, int *len, int peer)
+-{
+- struct sockaddr_l2 *la = (struct sockaddr_l2 *) addr;
+- struct sock *sk = sock->sk;
+-
+- DBG("sock %p, sk %p", sock, sk);
+-
+- addr->sa_family = AF_BLUETOOTH;
+- *len = sizeof(struct sockaddr_l2);
+-
+- if (peer)
+- bacpy(&la->l2_bdaddr, &l2cap_pi(sk)->dst);
+- else
+- bacpy(&la->l2_bdaddr, &l2cap_pi(sk)->src);
+-
+- la->l2_psm = l2cap_pi(sk)->psm;
+-
+- return 0;
+-}
+-
+-static int l2cap_sock_sendmsg(struct socket *sock, struct msghdr *msg, int len, struct scm_cookie *scm)
+-{
+- struct sock *sk = sock->sk;
+- int err = 0;
+-
+- DBG("sock %p, sk %p", sock, sk);
+-
+- if (sk->err)
+- return sock_error(sk);
+-
+- if (msg->msg_flags & MSG_OOB)
+- return -EOPNOTSUPP;
+-
+- lock_sock(sk);
+-
+- if (sk->state == BT_CONNECTED)
+- err = l2cap_chan_send(sk, msg, len);
+- else
+- err = -ENOTCONN;
+-
+- release_sock(sk);
+- return err;
+-}
+-
+-static int l2cap_sock_recvmsg(struct socket *sock, struct msghdr *msg, int len, int flags, struct scm_cookie *scm)
+-{
+- struct sock *sk = sock->sk;
+- int noblock = flags & MSG_DONTWAIT;
+- int copied, err;
+- struct sk_buff *skb;
+-
+- DBG("sock %p, sk %p", sock, sk);
+-
+- if (flags & (MSG_OOB))
+- return -EOPNOTSUPP;
+-
+- if (sk->state == BT_CLOSED)
+- return 0;
+-
+- if (!(skb = skb_recv_datagram(sk, flags, noblock, &err)))
+- return err;
+-
+- msg->msg_namelen = 0;
+-
+- copied = skb->len;
+- if (len < copied) {
+- msg->msg_flags |= MSG_TRUNC;
+- copied = len;
+- }
+-
+- skb->h.raw = skb->data;
+- err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
+-
+- skb_free_datagram(sk, skb);
+-
+- return err ? : copied;
+-}
+-
+-int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen)
+-{
+- struct sock *sk = sock->sk;
+- struct l2cap_options opts;
+- int err = 0;
+-
+- DBG("sk %p", sk);
+-
+- lock_sock(sk);
+-
+- switch (optname) {
+- case L2CAP_OPTIONS:
+- if (copy_from_user((char *)&opts, optval, optlen)) {
+- err = -EFAULT;
+- break;
+- }
+- l2cap_pi(sk)->imtu = opts.imtu;
+- l2cap_pi(sk)->omtu = opts.omtu;
+- break;
+-
+- default:
+- err = -ENOPROTOOPT;
+- break;
+- };
+-
+- release_sock(sk);
+- return err;
+-}
+-
+-int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, char *optval, int *optlen)
+-{
+- struct sock *sk = sock->sk;
+- struct l2cap_options opts;
+- struct l2cap_conninfo cinfo;
+- int len, err = 0;
+-
+- if (get_user(len, optlen))
+- return -EFAULT;
+-
+- lock_sock(sk);
+-
+- switch (optname) {
+- case L2CAP_OPTIONS:
+- opts.imtu = l2cap_pi(sk)->imtu;
+- opts.omtu = l2cap_pi(sk)->omtu;
+- opts.flush_to = l2cap_pi(sk)->flush_to;
+-
+- len = MIN(len, sizeof(opts));
+- if (copy_to_user(optval, (char *)&opts, len))
+- err = -EFAULT;
+-
+- break;
+-
+- case L2CAP_CONNINFO:
+- if (sk->state != BT_CONNECTED) {
+- err = -ENOTCONN;
+- break;
+- }
+-
+- cinfo.hci_handle = l2cap_pi(sk)->conn->hconn->handle;
+-
+- len = MIN(len, sizeof(cinfo));
+- if (copy_to_user(optval, (char *)&cinfo, len))
+- err = -EFAULT;
+-
+- break;
+-
+- default:
+- err = -ENOPROTOOPT;
+- break;
+- };
+-
+- release_sock(sk);
+- return err;
+-}
+-
+-static unsigned int l2cap_sock_poll(struct file * file, struct socket *sock, poll_table *wait)
+-{
+- struct sock *sk = sock->sk;
+- struct l2cap_accept_q *aq;
+- unsigned int mask;
+-
+- DBG("sock %p, sk %p", sock, sk);
+-
+- poll_wait(file, sk->sleep, wait);
+- mask = 0;
+-
+- if (sk->err || !skb_queue_empty(&sk->error_queue))
+- mask |= POLLERR;
+-
+- if (sk->shutdown == SHUTDOWN_MASK)
+- mask |= POLLHUP;
+-
+- aq = &l2cap_pi(sk)->accept_q;
+- if (!skb_queue_empty(&sk->receive_queue) || aq->head || (sk->shutdown & RCV_SHUTDOWN))
+- mask |= POLLIN | POLLRDNORM;
+-
+- if (sk->state == BT_CLOSED)
+- mask |= POLLHUP;
+-
+- if (sock_writeable(sk))
+- mask |= POLLOUT | POLLWRNORM | POLLWRBAND;
+- else
+- set_bit(SOCK_ASYNC_NOSPACE, &sk->socket->flags);
+-
+- return mask;
+-}
+-
+-static int l2cap_sock_release(struct socket *sock)
+-{
+- struct sock *sk = sock->sk;
+-
+- DBG("sock %p, sk %p", sock, sk);
+-
+- if (!sk)
+- return 0;
+-
+- sock_orphan(sk);
+-
+- l2cap_sock_close(sk);
+-
+- return 0;
+-}
+-
+-/* --------- L2CAP channels --------- */
+-static struct sock * __l2cap_get_chan_by_dcid(struct l2cap_chan_list *l, __u16 cid)
+-{
+- struct sock *s;
+-
+- for (s = l->head; s; s = l2cap_pi(s)->next_c) {
+- if (l2cap_pi(s)->dcid == cid)
+- break;
+- }
+-
+- return s;
+-}
+-
+-static inline struct sock *l2cap_get_chan_by_dcid(struct l2cap_chan_list *l, __u16 cid)
+-{
+- struct sock *s;
+-
+- read_lock(&l->lock);
+- s = __l2cap_get_chan_by_dcid(l, cid);
+- read_unlock(&l->lock);
+-
+- return s;
+-}
+-
+-static struct sock *__l2cap_get_chan_by_scid(struct l2cap_chan_list *l, __u16 cid)
+-{
+- struct sock *s;
+-
+- for (s = l->head; s; s = l2cap_pi(s)->next_c) {
+- if (l2cap_pi(s)->scid == cid)
+- break;
+- }
+-
+- return s;
+-}
+-static inline struct sock *l2cap_get_chan_by_scid(struct l2cap_chan_list *l, __u16 cid)
+-{
+- struct sock *s;
+-
+- read_lock(&l->lock);
+- s = __l2cap_get_chan_by_scid(l, cid);
+- read_unlock(&l->lock);
+-
+- return s;
+-}
+-
+-static struct sock *__l2cap_get_chan_by_ident(struct l2cap_chan_list *l, __u8 ident)
+-{
+- struct sock *s;
+-
+- for (s = l->head; s; s = l2cap_pi(s)->next_c) {
+- if (l2cap_pi(s)->ident == ident)
+- break;
+- }
+-
+- return s;
+-}
+-
+-static inline struct sock *l2cap_get_chan_by_ident(struct l2cap_chan_list *l, __u8 ident)
+-{
+- struct sock *s;
+-
+- read_lock(&l->lock);
+- s = __l2cap_get_chan_by_ident(l, ident);
+- read_unlock(&l->lock);
+-
+- return s;
+-}
+-
+-static __u16 l2cap_alloc_cid(struct l2cap_chan_list *l)
+-{
+- __u16 cid = 0x0040;
+-
+- for (; cid < 0xffff; cid++) {
+- if(!__l2cap_get_chan_by_scid(l, cid))
+- return cid;
+- }
+-
+- return 0;
+-}
+-
+-static inline void __l2cap_chan_link(struct l2cap_chan_list *l, struct sock *sk)
+-{
+- sock_hold(sk);
+-
+- if (l->head)
+- l2cap_pi(l->head)->prev_c = sk;
+-
+- l2cap_pi(sk)->next_c = l->head;
+- l2cap_pi(sk)->prev_c = NULL;
+- l->head = sk;
+-}
+-
+-static inline void l2cap_chan_unlink(struct l2cap_chan_list *l, struct sock *sk)
+-{
+- struct sock *next = l2cap_pi(sk)->next_c, *prev = l2cap_pi(sk)->prev_c;
+-
+- write_lock(&l->lock);
+- if (sk == l->head)
+- l->head = next;
+-
+- if (next)
+- l2cap_pi(next)->prev_c = prev;
+- if (prev)
+- l2cap_pi(prev)->next_c = next;
+- write_unlock(&l->lock);
+-
+- __sock_put(sk);
+-}
+-
+-static void __l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct sock *parent)
+-{
+- struct l2cap_chan_list *l = &conn->chan_list;
+-
+- DBG("conn %p, psm 0x%2.2x, dcid 0x%4.4x", conn, l2cap_pi(sk)->psm, l2cap_pi(sk)->dcid);
+-
+- l2cap_conn_clear_timer(conn);
+-
+- atomic_inc(&conn->refcnt);
+- l2cap_pi(sk)->conn = conn;
+-
+- if (sk->type == SOCK_SEQPACKET) {
+- /* Alloc CID for normal socket */
+- l2cap_pi(sk)->scid = l2cap_alloc_cid(l);
+- } else {
+- /* Raw socket can send only signalling messages */
+- l2cap_pi(sk)->scid = 0x0001;
+- l2cap_pi(sk)->dcid = 0x0001;
+- l2cap_pi(sk)->omtu = L2CAP_DEFAULT_MTU;
+- }
+-
+- __l2cap_chan_link(l, sk);
+-
+- if (parent)
+- l2cap_accept_queue(parent, sk);
+-}
+-
+-static inline void l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct sock *parent)
+-{
+- struct l2cap_chan_list *l = &conn->chan_list;
+-
+- write_lock(&l->lock);
+- __l2cap_chan_add(conn, sk, parent);
+- write_unlock(&l->lock);
+-}
+-
+-/* Delete channel.
+- * Must be called on the locked socket. */
+-static void l2cap_chan_del(struct sock *sk, int err)
+-{
+- struct l2cap_conn *conn;
+- struct sock *parent;
+-
+- conn = l2cap_pi(sk)->conn;
+- parent = l2cap_pi(sk)->parent;
+-
+- DBG("sk %p, conn %p, err %d", sk, conn, err);
+-
+- if (parent) {
+- /* Unlink from parent accept queue */
+- bh_lock_sock(parent);
+- l2cap_accept_unlink(sk);
+- bh_unlock_sock(parent);
+- }
+-
+- if (conn) {
+- long timeout;
+-
+- /* Unlink from channel list */
+- l2cap_chan_unlink(&conn->chan_list, sk);
+- l2cap_pi(sk)->conn = NULL;
+-
+- if (conn->out)
+- timeout = L2CAP_DISCONN_TIMEOUT;
+- else
+- timeout = L2CAP_CONN_IDLE_TIMEOUT;
+-
+- if (atomic_dec_and_test(&conn->refcnt) && conn->state == BT_CONNECTED) {
+- /* Schedule Baseband disconnect */
+- l2cap_conn_set_timer(conn, timeout);
+- }
+- }
+-
+- sk->state = BT_CLOSED;
+- sk->err = err;
+- sk->state_change(sk);
+-
+- sk->zapped = 1;
+-}
+-
+-static void l2cap_conn_ready(struct l2cap_conn *conn)
+-{
+- struct l2cap_chan_list *l = &conn->chan_list;
+- struct sock *sk;
+-
+- DBG("conn %p", conn);
+-
+- read_lock(&l->lock);
+-
+- for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) {
+- bh_lock_sock(sk);
+-
+- if (sk->type != SOCK_SEQPACKET) {
+- sk->state = BT_CONNECTED;
+- sk->state_change(sk);
+- l2cap_sock_clear_timer(sk);
+- } else if (sk->state == BT_CONNECT) {
+- l2cap_conn_req req;
+- req.scid = __cpu_to_le16(l2cap_pi(sk)->scid);
+- req.psm = l2cap_pi(sk)->psm;
+- l2cap_send_req(conn, L2CAP_CONN_REQ, L2CAP_CONN_REQ_SIZE, &req);
+-
+- l2cap_sock_set_timer(sk, sk->sndtimeo);
+- }
+-
+- bh_unlock_sock(sk);
+- }
+-
+- read_unlock(&l->lock);
+-}
+-
+-static void l2cap_chan_ready(struct sock *sk)
+-{
+- struct sock *parent = l2cap_pi(sk)->parent;
+-
+- DBG("sk %p, parent %p", sk, parent);
+-
+- l2cap_pi(sk)->conf_state = 0;
+- l2cap_sock_clear_timer(sk);
+-
+- if (!parent) {
+- /* Outgoing channel.
+- * Wake up socket sleeping on connect.
+- */
+- sk->state = BT_CONNECTED;
+- sk->state_change(sk);
+- } else {
+- /* Incomming channel.
+- * Wake up socket sleeping on accept.
+- */
+- parent->data_ready(parent, 1);
+- }
+-}
+-
+-/* Copy frame to all raw sockets on that connection */
+-void l2cap_raw_recv(struct l2cap_conn *conn, struct sk_buff *skb)
+-{
+- struct l2cap_chan_list *l = &conn->chan_list;
+- struct sk_buff *nskb;
+- struct sock * sk;
+-
+- DBG("conn %p", conn);
+-
+- read_lock(&l->lock);
+- for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) {
+- if (sk->type != SOCK_RAW)
+- continue;
+-
+- /* Don't send frame to the socket it came from */
+- if (skb->sk == sk)
+- continue;
+-
+- if (!(nskb = skb_clone(skb, GFP_ATOMIC)))
+- continue;
+-
+- skb_queue_tail(&sk->receive_queue, nskb);
+- sk->data_ready(sk, nskb->len);
+- }
+- read_unlock(&l->lock);
+-}
+-
+-static int l2cap_chan_send(struct sock *sk, struct msghdr *msg, int len)
+-{
+- struct l2cap_conn *conn = l2cap_pi(sk)->conn;
+- struct sk_buff *skb, **frag;
+- int err, size, count, sent=0;
+- l2cap_hdr *lh;
+-
+- /* Check outgoing MTU */
+- if (len > l2cap_pi(sk)->omtu)
+- return -EINVAL;
+-
+- DBG("sk %p len %d", sk, len);
+-
+- /* First fragment (with L2CAP header) */
+- count = MIN(conn->iff->mtu - L2CAP_HDR_SIZE, len);
+- size = L2CAP_HDR_SIZE + count;
+- if (!(skb = bluez_skb_send_alloc(sk, size, msg->msg_flags & MSG_DONTWAIT, &err)))
+- return err;
+-
+- /* Create L2CAP header */
+- lh = (l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE);
+- lh->len = __cpu_to_le16(len);
+- lh->cid = __cpu_to_le16(l2cap_pi(sk)->dcid);
+-
+- if (memcpy_fromiovec(skb_put(skb, count), msg->msg_iov, count)) {
+- err = -EFAULT;
+- goto fail;
+- }
+-
+- sent += count;
+- len -= count;
+-
+- /* Continuation fragments (no L2CAP header) */
+- frag = &skb_shinfo(skb)->frag_list;
+- while (len) {
+- count = MIN(conn->iff->mtu, len);
+-
+- *frag = bluez_skb_send_alloc(sk, count, msg->msg_flags & MSG_DONTWAIT, &err);
+- if (!*frag)
+- goto fail;
+-
+- if (memcpy_fromiovec(skb_put(*frag, count), msg->msg_iov, count)) {
+- err = -EFAULT;
+- goto fail;
+- }
+-
+- sent += count;
+- len -= count;
+-
+- frag = &(*frag)->next;
+- }
+-
+- if ((err = hci_send_acl(conn->hconn, skb, 0)) < 0)
+- goto fail;
+-
+- return sent;
+-
+-fail:
+- kfree_skb(skb);
+- return err;
+-}
+-
+-/* --------- L2CAP signalling commands --------- */
+-static inline __u8 l2cap_get_ident(struct l2cap_conn *conn)
+-{
+- __u8 id;
+-
+- /* Get next available identificator.
+- * 1 - 199 are used by kernel.
+- * 200 - 254 are used by utilities like l2ping, etc
+- */
+-
+- spin_lock(&conn->lock);
+-
+- if (++conn->tx_ident > 199)
+- conn->tx_ident = 1;
+-
+- id = conn->tx_ident;
+-
+- spin_unlock(&conn->lock);
+-
+- return id;
+-}
+-
+-static inline struct sk_buff *l2cap_build_cmd(__u8 code, __u8 ident, __u16 len, void *data)
+-{
+- struct sk_buff *skb;
+- l2cap_cmd_hdr *cmd;
+- l2cap_hdr *lh;
+- int size;
+-
+- DBG("code 0x%2.2x, ident 0x%2.2x, len %d", code, ident, len);
+-
+- size = L2CAP_HDR_SIZE + L2CAP_CMD_HDR_SIZE + len;
+- if (!(skb = bluez_skb_alloc(size, GFP_ATOMIC)))
+- return NULL;
+-
+- lh = (l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE);
+- lh->len = __cpu_to_le16(L2CAP_CMD_HDR_SIZE + len);
+- lh->cid = __cpu_to_le16(0x0001);
+-
+- cmd = (l2cap_cmd_hdr *) skb_put(skb, L2CAP_CMD_HDR_SIZE);
+- cmd->code = code;
+- cmd->ident = ident;
+- cmd->len = __cpu_to_le16(len);
+-
+- if (len)
+- memcpy(skb_put(skb, len), data, len);
+-
+- return skb;
+-}
+-
+-static int l2cap_send_req(struct l2cap_conn *conn, __u8 code, __u16 len, void *data)
+-{
+- struct sk_buff *skb;
+- __u8 ident;
+-
+- DBG("code 0x%2.2x", code);
+-
+- ident = l2cap_get_ident(conn);
+- if (!(skb = l2cap_build_cmd(code, ident, len, data)))
+- return -ENOMEM;
+- return hci_send_acl(conn->hconn, skb, 0);
+-}
+-
+-static int l2cap_send_rsp(struct l2cap_conn *conn, __u8 ident, __u8 code, __u16 len, void *data)
+-{
+- struct sk_buff *skb;
+-
+- DBG("code 0x%2.2x", code);
+-
+- if (!(skb = l2cap_build_cmd(code, ident, len, data)))
+- return -ENOMEM;
+- return hci_send_acl(conn->hconn, skb, 0);
+-}
+-
+-static inline int l2cap_get_conf_opt(__u8 **ptr, __u8 *type, __u32 *val)
+-{
+- l2cap_conf_opt *opt = (l2cap_conf_opt *) (*ptr);
+- int len;
+-
+- *type = opt->type;
+- switch (opt->len) {
+- case 1:
+- *val = *((__u8 *) opt->val);
+- break;
+-
+- case 2:
+- *val = __le16_to_cpu(*((__u16 *)opt->val));
+- break;
+-
+- case 4:
+- *val = __le32_to_cpu(*((__u32 *)opt->val));
+- break;
+-
+- default:
+- *val = 0L;
+- break;
+- };
+-
+- DBG("type 0x%2.2x len %d val 0x%8.8x", *type, opt->len, *val);
+-
+- len = L2CAP_CONF_OPT_SIZE + opt->len;
+-
+- *ptr += len;
+-
+- return len;
+-}
+-
+-static inline void l2cap_parse_conf_req(struct sock *sk, char *data, int len)
+-{
+- __u8 type, hint; __u32 val;
+- __u8 *ptr = data;
+-
+- DBG("sk %p len %d", sk, len);
+-
+- while (len >= L2CAP_CONF_OPT_SIZE) {
+- len -= l2cap_get_conf_opt(&ptr, &type, &val);
+-
+- hint = type & 0x80;
+- type &= 0x7f;
+-
+- switch (type) {
+- case L2CAP_CONF_MTU:
+- l2cap_pi(sk)->conf_mtu = val;
+- break;
+-
+- case L2CAP_CONF_FLUSH_TO:
+- l2cap_pi(sk)->flush_to = val;
+- break;
+-
+- case L2CAP_CONF_QOS:
+- break;
+-
+- default:
+- if (hint)
+- break;
+-
+- /* FIXME: Reject unknon option */
+- break;
+- };
+- }
+-}
+-
+-static inline void l2cap_add_conf_opt(__u8 **ptr, __u8 type, __u8 len, __u32 val)
+-{
+- register l2cap_conf_opt *opt = (l2cap_conf_opt *) (*ptr);
+-
+- DBG("type 0x%2.2x len %d val 0x%8.8x", type, len, val);
+-
+- opt->type = type;
+- opt->len = len;
+- switch (len) {
+- case 1:
+- *((__u8 *) opt->val) = val;
+- break;
+-
+- case 2:
+- *((__u16 *) opt->val) = __cpu_to_le16(val);
+- break;
+-
+- case 4:
+- *((__u32 *) opt->val) = __cpu_to_le32(val);
+- break;
+- };
+-
+- *ptr += L2CAP_CONF_OPT_SIZE + len;
+-}
+-
+-static int l2cap_build_conf_req(struct sock *sk, __u8 *data)
+-{
+- struct l2cap_pinfo *pi = l2cap_pi(sk);
+- l2cap_conf_req *req = (l2cap_conf_req *) data;
+- __u8 *ptr = req->data;
+-
+- DBG("sk %p", sk);
+-
+- if (pi->imtu != L2CAP_DEFAULT_MTU)
+- l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, pi->imtu);
+-
+- /* FIXME. Need actual value of the flush timeout */
+- //if (flush_to != L2CAP_DEFAULT_FLUSH_TO)
+- // l2cap_add_conf_opt(&ptr, L2CAP_CONF_FLUSH_TO, 2, pi->flush_to);
+-
+- req->dcid = __cpu_to_le16(pi->dcid);
+- req->flags = __cpu_to_le16(0);
+-
+- return ptr - data;
+-}
+-
+-static int l2cap_conf_output(struct sock *sk, __u8 **ptr)
+-{
+- struct l2cap_pinfo *pi = l2cap_pi(sk);
+- int result = 0;
+-
+- /* Configure output options and let other side know
+- * which ones we don't like.
+- */
+- if (pi->conf_mtu < pi->omtu) {
+- l2cap_add_conf_opt(ptr, L2CAP_CONF_MTU, 2, l2cap_pi(sk)->omtu);
+- result = L2CAP_CONF_UNACCEPT;
+- } else {
+- pi->omtu = pi->conf_mtu;
+- }
+-
+- DBG("sk %p result %d", sk, result);
+- return result;
+-}
+-
+-static int l2cap_build_conf_rsp(struct sock *sk, __u8 *data, int *result)
+-{
+- l2cap_conf_rsp *rsp = (l2cap_conf_rsp *) data;
+- __u8 *ptr = rsp->data;
+-
+- DBG("sk %p complete %d", sk, result ? 1 : 0);
+-
+- if (result)
+- *result = l2cap_conf_output(sk, &ptr);
+-
+- rsp->scid = __cpu_to_le16(l2cap_pi(sk)->dcid);
+- rsp->result = __cpu_to_le16(result ? *result : 0);
+- rsp->flags = __cpu_to_le16(0);
+-
+- return ptr - data;
+-}
+-
+-static inline int l2cap_connect_req(struct l2cap_conn *conn, l2cap_cmd_hdr *cmd, __u8 *data)
+-{
+- struct l2cap_chan_list *list = &conn->chan_list;
+- l2cap_conn_req *req = (l2cap_conn_req *) data;
+- l2cap_conn_rsp rsp;
+- struct sock *sk, *parent;
+-
+- __u16 scid = __le16_to_cpu(req->scid);
+- __u16 psm = req->psm;
+-
+- DBG("psm 0x%2.2x scid 0x%4.4x", psm, scid);
+-
+- /* Check if we have socket listening on psm */
+- if (!(parent = l2cap_get_sock_listen(&conn->src, psm)))
+- goto reject;
+-
+- bh_lock_sock(parent);
+- write_lock(&list->lock);
+-
+- /* Check if we already have channel with that dcid */
+- if (__l2cap_get_chan_by_dcid(list, scid))
+- goto unlock;
+-
+- /* Check for backlog size */
+- if (parent->ack_backlog > parent->max_ack_backlog)
+- goto unlock;
+-
+- if (!(sk = l2cap_sock_alloc(NULL, BTPROTO_L2CAP, GFP_ATOMIC)))
+- goto unlock;
+-
+- l2cap_sock_init(sk, parent);
+-
+- bacpy(&l2cap_pi(sk)->src, &conn->src);
+- bacpy(&l2cap_pi(sk)->dst, &conn->dst);
+- l2cap_pi(sk)->psm = psm;
+- l2cap_pi(sk)->dcid = scid;
+-
+- __l2cap_chan_add(conn, sk, parent);
+- sk->state = BT_CONFIG;
+-
+- write_unlock(&list->lock);
+- bh_unlock_sock(parent);
+-
+- rsp.dcid = __cpu_to_le16(l2cap_pi(sk)->scid);
+- rsp.scid = __cpu_to_le16(l2cap_pi(sk)->dcid);
+- rsp.result = __cpu_to_le16(0);
+- rsp.status = __cpu_to_le16(0);
+- l2cap_send_rsp(conn, cmd->ident, L2CAP_CONN_RSP, L2CAP_CONN_RSP_SIZE, &rsp);
+-
+- return 0;
+-
+-unlock:
+- write_unlock(&list->lock);
+- bh_unlock_sock(parent);
+-
+-reject:
+- rsp.scid = __cpu_to_le16(scid);
+- rsp.dcid = __cpu_to_le16(0);
+- rsp.status = __cpu_to_le16(0);
+- rsp.result = __cpu_to_le16(L2CAP_CONN_NO_MEM);
+- l2cap_send_rsp(conn, cmd->ident, L2CAP_CONN_RSP, L2CAP_CONN_RSP_SIZE, &rsp);
+-
+- return 0;
+-}
+-
+-static inline int l2cap_connect_rsp(struct l2cap_conn *conn, l2cap_cmd_hdr *cmd, __u8 *data)
+-{
+- l2cap_conn_rsp *rsp = (l2cap_conn_rsp *) data;
+- __u16 scid, dcid, result, status;
+- struct sock *sk;
+-
+- scid = __le16_to_cpu(rsp->scid);
+- dcid = __le16_to_cpu(rsp->dcid);
+- result = __le16_to_cpu(rsp->result);
+- status = __le16_to_cpu(rsp->status);
+-
+- DBG("dcid 0x%4.4x scid 0x%4.4x result 0x%2.2x status 0x%2.2x", dcid, scid, result, status);
+-
+- if (!(sk = l2cap_get_chan_by_scid(&conn->chan_list, scid)))
+- return -ENOENT;
+-
+- bh_lock_sock(sk);
+-
+- if (!result) {
+- char req[64];
+-
+- sk->state = BT_CONFIG;
+- l2cap_pi(sk)->dcid = dcid;
+- l2cap_pi(sk)->conf_state |= CONF_REQ_SENT;
+-
+- l2cap_send_req(conn, L2CAP_CONF_REQ, l2cap_build_conf_req(sk, req), req);
+- } else {
+- l2cap_chan_del(sk, ECONNREFUSED);
+- }
+-
+- bh_unlock_sock(sk);
+- return 0;
+-}
+-
+-static inline int l2cap_config_req(struct l2cap_conn *conn, l2cap_cmd_hdr *cmd, __u8 *data)
+-{
+- l2cap_conf_req * req = (l2cap_conf_req *) data;
+- __u16 dcid, flags;
+- __u8 rsp[64];
+- struct sock *sk;
+- int result;
+-
+- dcid = __le16_to_cpu(req->dcid);
+- flags = __le16_to_cpu(req->flags);
+-
+- DBG("dcid 0x%4.4x flags 0x%2.2x", dcid, flags);
+-
+- if (!(sk = l2cap_get_chan_by_scid(&conn->chan_list, dcid)))
+- return -ENOENT;
+-
+- bh_lock_sock(sk);
+-
+- l2cap_parse_conf_req(sk, req->data, cmd->len - L2CAP_CONF_REQ_SIZE);
+-
+- if (flags & 0x01) {
+- /* Incomplete config. Send empty response. */
+- l2cap_send_rsp(conn, cmd->ident, L2CAP_CONF_RSP, l2cap_build_conf_rsp(sk, rsp, NULL), rsp);
+- goto unlock;
+- }
+-
+- /* Complete config. */
+- l2cap_send_rsp(conn, cmd->ident, L2CAP_CONF_RSP, l2cap_build_conf_rsp(sk, rsp, &result), rsp);
+-
+- if (result)
+- goto unlock;
+-
+- /* Output config done */
+- l2cap_pi(sk)->conf_state |= CONF_OUTPUT_DONE;
+-
+- if (l2cap_pi(sk)->conf_state & CONF_INPUT_DONE) {
+- sk->state = BT_CONNECTED;
+- l2cap_chan_ready(sk);
+- } else if (!(l2cap_pi(sk)->conf_state & CONF_REQ_SENT)) {
+- char req[64];
+- l2cap_send_req(conn, L2CAP_CONF_REQ, l2cap_build_conf_req(sk, req), req);
+- }
+-
+-unlock:
+- bh_unlock_sock(sk);
+-
+- return 0;
+-}
+-
+-static inline int l2cap_config_rsp(struct l2cap_conn *conn, l2cap_cmd_hdr *cmd, __u8 *data)
+-{
+- l2cap_conf_rsp *rsp = (l2cap_conf_rsp *)data;
+- __u16 scid, flags, result;
+- struct sock *sk;
+- int err = 0;
+-
+- scid = __le16_to_cpu(rsp->scid);
+- flags = __le16_to_cpu(rsp->flags);
+- result = __le16_to_cpu(rsp->result);
+-
+- DBG("scid 0x%4.4x flags 0x%2.2x result 0x%2.2x", scid, flags, result);
+-
+- if (!(sk = l2cap_get_chan_by_scid(&conn->chan_list, scid)))
+- return -ENOENT;
+-
+- bh_lock_sock(sk);
+-
+- if (result) {
+- l2cap_disconn_req req;
+-
+- /* They didn't like our options. Well... we do not negotiate.
+- * Close channel.
+- */
+- sk->state = BT_DISCONN;
+-
+- req.dcid = __cpu_to_le16(l2cap_pi(sk)->dcid);
+- req.scid = __cpu_to_le16(l2cap_pi(sk)->scid);
+- l2cap_send_req(conn, L2CAP_DISCONN_REQ, L2CAP_DISCONN_REQ_SIZE, &req);
+-
+- l2cap_sock_set_timer(sk, sk->sndtimeo);
+- goto done;
+- }
+-
+- if (flags & 0x01)
+- goto done;
+-
+- /* Input config done */
+- l2cap_pi(sk)->conf_state |= CONF_INPUT_DONE;
+-
+- if (l2cap_pi(sk)->conf_state & CONF_OUTPUT_DONE) {
+- sk->state = BT_CONNECTED;
+- l2cap_chan_ready(sk);
+- }
+-
+-done:
+- bh_unlock_sock(sk);
+-
+- return err;
+-}
+-
+-static inline int l2cap_disconnect_req(struct l2cap_conn *conn, l2cap_cmd_hdr *cmd, __u8 *data)
+-{
+- l2cap_disconn_req *req = (l2cap_disconn_req *) data;
+- l2cap_disconn_rsp rsp;
+- __u16 dcid, scid;
+- struct sock *sk;
+-
+- scid = __le16_to_cpu(req->scid);
+- dcid = __le16_to_cpu(req->dcid);
+-
+- DBG("scid 0x%4.4x dcid 0x%4.4x", scid, dcid);
+-
+- if (!(sk = l2cap_get_chan_by_scid(&conn->chan_list, dcid)))
+- return 0;
+-
+- bh_lock_sock(sk);
+-
+- rsp.dcid = __cpu_to_le16(l2cap_pi(sk)->scid);
+- rsp.scid = __cpu_to_le16(l2cap_pi(sk)->dcid);
+- l2cap_send_rsp(conn, cmd->ident, L2CAP_DISCONN_RSP, L2CAP_DISCONN_RSP_SIZE, &rsp);
+-
+- l2cap_chan_del(sk, ECONNRESET);
+-
+- bh_unlock_sock(sk);
+-
+- l2cap_sock_kill(sk);
+-
+- return 0;
+-}
+-
+-static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, l2cap_cmd_hdr *cmd, __u8 *data)
+-{
+- l2cap_disconn_rsp *rsp = (l2cap_disconn_rsp *) data;
+- __u16 dcid, scid;
+- struct sock *sk;
+-
+- scid = __le16_to_cpu(rsp->scid);
+- dcid = __le16_to_cpu(rsp->dcid);
+-
+- DBG("dcid 0x%4.4x scid 0x%4.4x", dcid, scid);
+-
+- if (!(sk = l2cap_get_chan_by_scid(&conn->chan_list, scid)))
+- return -ENOENT;
+-
+- bh_lock_sock(sk);
+- l2cap_sock_clear_timer(sk);
+- l2cap_chan_del(sk, ECONNABORTED);
+- bh_unlock_sock(sk);
+-
+- l2cap_sock_kill(sk);
+-
+- return 0;
+-}
+-
+-static inline void l2cap_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb)
+-{
+- __u8 *data = skb->data;
+- int len = skb->len;
+- l2cap_cmd_hdr cmd;
+- int err = 0;
+-
+- while (len >= L2CAP_CMD_HDR_SIZE) {
+- memcpy(&cmd, data, L2CAP_CMD_HDR_SIZE);
+- data += L2CAP_CMD_HDR_SIZE;
+- len -= L2CAP_CMD_HDR_SIZE;
+-
+- cmd.len = __le16_to_cpu(cmd.len);
+-
+- DBG("code 0x%2.2x len %d id 0x%2.2x", cmd.code, cmd.len, cmd.ident);
+-
+- if (cmd.len > len || !cmd.ident) {
+- DBG("corrupted command");
+- break;
+- }
+-
+- switch (cmd.code) {
+- case L2CAP_CONN_REQ:
+- err = l2cap_connect_req(conn, &cmd, data);
+- break;
+-
+- case L2CAP_CONN_RSP:
+- err = l2cap_connect_rsp(conn, &cmd, data);
+- break;
+-
+- case L2CAP_CONF_REQ:
+- err = l2cap_config_req(conn, &cmd, data);
+- break;
+-
+- case L2CAP_CONF_RSP:
+- err = l2cap_config_rsp(conn, &cmd, data);
+- break;
+-
+- case L2CAP_DISCONN_REQ:
+- err = l2cap_disconnect_req(conn, &cmd, data);
+- break;
+-
+- case L2CAP_DISCONN_RSP:
+- err = l2cap_disconnect_rsp(conn, &cmd, data);
+- break;
+-
+- case L2CAP_COMMAND_REJ:
+- /* FIXME: We should process this */
+- l2cap_raw_recv(conn, skb);
+- break;
+-
+- case L2CAP_ECHO_REQ:
+- l2cap_send_rsp(conn, cmd.ident, L2CAP_ECHO_RSP, cmd.len, data);
+- break;
+-
+- case L2CAP_ECHO_RSP:
+- case L2CAP_INFO_REQ:
+- case L2CAP_INFO_RSP:
+- l2cap_raw_recv(conn, skb);
+- break;
+-
+- default:
+- ERR("Uknown signaling command 0x%2.2x", cmd.code);
+- err = -EINVAL;
+- break;
+- };
+-
+- if (err) {
+- l2cap_cmd_rej rej;
+- DBG("error %d", err);
+-
+- /* FIXME: Map err to a valid reason. */
+- rej.reason = __cpu_to_le16(0);
+- l2cap_send_rsp(conn, cmd.ident, L2CAP_COMMAND_REJ, L2CAP_CMD_REJ_SIZE, &rej);
+- }
+-
+- data += cmd.len;
+- len -= cmd.len;
+- }
+-
+- kfree_skb(skb);
+-}
+-
+-static inline int l2cap_data_channel(struct l2cap_conn *conn, __u16 cid, struct sk_buff *skb)
+-{
+- struct sock *sk;
+-
+- if (!(sk = l2cap_get_chan_by_scid(&conn->chan_list, cid))) {
+- DBG("unknown cid 0x%4.4x", cid);
+- goto drop;
+- }
+-
+- DBG("sk %p, len %d", sk, skb->len);
+-
+- if (sk->state != BT_CONNECTED)
+- goto drop;
+-
+- if (l2cap_pi(sk)->imtu < skb->len)
+- goto drop;
+-
+- skb_queue_tail(&sk->receive_queue, skb);
+- sk->data_ready(sk, skb->len);
+-
+- return 0;
+-
+-drop:
+- kfree_skb(skb);
+-
+- return 0;
+-}
+-
+-static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb)
+-{
+- l2cap_hdr *lh = (l2cap_hdr *) skb->data;
+- __u16 cid, len;
+-
+- skb_pull(skb, L2CAP_HDR_SIZE);
+- cid = __le16_to_cpu(lh->cid);
+- len = __le16_to_cpu(lh->len);
+-
+- DBG("len %d, cid 0x%4.4x", len, cid);
+-
+- if (cid == 0x0001)
+- l2cap_sig_channel(conn, skb);
+- else
+- l2cap_data_channel(conn, cid, skb);
+-}
+-
+-/* ------------ L2CAP interface with lower layer (HCI) ------------- */
+-static int l2cap_dev_event(struct notifier_block *this, unsigned long event, void *ptr)
+-{
+- struct hci_dev *hdev = (struct hci_dev *) ptr;
+-
+- DBG("hdev %s, event %ld", hdev->name, event);
+-
+- write_lock(&l2cap_rt_lock);
+-
+- switch (event) {
+- case HCI_DEV_UP:
+- l2cap_iff_add(hdev);
+- break;
+-
+- case HCI_DEV_DOWN:
+- l2cap_iff_del(hdev);
+- break;
+- };
+-
+- write_unlock(&l2cap_rt_lock);
+-
+- return NOTIFY_DONE;
+-}
+-
+-int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr)
+-{
+- struct l2cap_iff *iff;
+-
+- DBG("hdev %s, bdaddr %s", hdev->name, batostr(bdaddr));
+-
+- if (!(iff = hdev->l2cap_data)) {
+- ERR("unknown interface");
+- return 0;
+- }
+-
+- /* Always accept connection */
+- return 1;
+-}
+-
+-int l2cap_connect_cfm(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 status, struct hci_conn *hconn)
+-{
+- struct l2cap_conn *conn;
+- struct l2cap_iff *iff;
+- int err = 0;
+-
+- DBG("hdev %s bdaddr %s hconn %p", hdev->name, batostr(bdaddr), hconn);
+-
+- if (!(iff = hdev->l2cap_data)) {
+- ERR("unknown interface");
+- return 0;
+- }
+-
+- l2cap_iff_lock(iff);
+-
+- conn = l2cap_get_conn_by_addr(iff, bdaddr);
+-
+- if (conn) {
+- /* Outgoing connection */
+- DBG("Outgoing connection: %s -> %s, %p, %2.2x", batostr(iff->bdaddr), batostr(bdaddr), conn, status);
+-
+- if (!status && hconn) {
+- conn->state = BT_CONNECTED;
+- conn->hconn = hconn;
+-
+- hconn->l2cap_data = (void *)conn;
+-
+- /* Establish channels */
+- l2cap_conn_ready(conn);
+- } else {
+- l2cap_conn_del(conn, bterr(status));
+- }
+- } else {
+- /* Incomming connection */
+- DBG("Incomming connection: %s -> %s, %2.2x", batostr(iff->bdaddr), batostr(bdaddr), status);
+-
+- if (status || !hconn)
+- goto done;
+-
+- if (!(conn = l2cap_conn_add(iff, bdaddr))) {
+- err = -ENOMEM;
+- goto done;
+- }
+-
+- conn->hconn = hconn;
+- hconn->l2cap_data = (void *)conn;
+-
+- conn->state = BT_CONNECTED;
+- }
+-
+-done:
+- l2cap_iff_unlock(iff);
+-
+- return err;
+-}
+-
+-int l2cap_disconn_ind(struct hci_conn *hconn, __u8 reason)
+-{
+- struct l2cap_conn *conn = hconn->l2cap_data;
+-
+- DBG("hconn %p reason %d", hconn, reason);
+-
+- if (!conn) {
+- ERR("unknown connection");
+- return 0;
+- }
+- conn->hconn = NULL;
+-
+- l2cap_iff_lock(conn->iff);
+- l2cap_conn_del(conn, bterr(reason));
+- l2cap_iff_unlock(conn->iff);
+-
+- return 0;
+-}
+-
+-int l2cap_recv_acldata(struct hci_conn *hconn, struct sk_buff *skb, __u16 flags)
+-{
+- struct l2cap_conn *conn = hconn->l2cap_data;
+-
+- if (!conn) {
+- ERR("unknown connection %p", hconn);
+- goto drop;
+- }
+-
+- DBG("conn %p len %d flags 0x%x", conn, skb->len, flags);
+-
+- if (flags & ACL_START) {
+- int flen, tlen, size;
+- l2cap_hdr *lh;
+-
+- if (conn->rx_len) {
+- ERR("Unexpected start frame (len %d)", skb->len);
+- kfree_skb(conn->rx_skb); conn->rx_skb = NULL;
+- conn->rx_len = 0;
+- }
+-
+- if (skb->len < L2CAP_HDR_SIZE) {
+- ERR("Frame is too small (len %d)", skb->len);
+- goto drop;
+- }
+-
+- lh = (l2cap_hdr *)skb->data;
+- tlen = __le16_to_cpu(lh->len);
+- flen = skb->len - L2CAP_HDR_SIZE;
+-
+- DBG("Start: total len %d, frag len %d", tlen, flen);
+-
+- if (flen == tlen) {
+- /* Complete frame received */
+- l2cap_recv_frame(conn, skb);
+- return 0;
+- }
+-
+- /* Allocate skb for the complete frame (with header) */
+- size = L2CAP_HDR_SIZE + tlen;
+- if (!(conn->rx_skb = bluez_skb_alloc(size, GFP_ATOMIC)))
+- goto drop;
+-
+- memcpy(skb_put(conn->rx_skb, skb->len), skb->data, skb->len);
+-
+- conn->rx_len = tlen - flen;
+- } else {
+- DBG("Cont: frag len %d (expecting %d)", skb->len, conn->rx_len);
+-
+- if (!conn->rx_len) {
+- ERR("Unexpected continuation frame (len %d)", skb->len);
+- goto drop;
+- }
+-
+- if (skb->len > conn->rx_len) {
+- ERR("Fragment is too large (len %d)", skb->len);
+- kfree_skb(conn->rx_skb); conn->rx_skb = NULL;
+- goto drop;
+- }
+-
+- memcpy(skb_put(conn->rx_skb, skb->len), skb->data, skb->len);
+- conn->rx_len -= skb->len;
+-
+- if (!conn->rx_len) {
+- /* Complete frame received */
+- l2cap_recv_frame(conn, conn->rx_skb);
+- conn->rx_skb = NULL;
+- }
+- }
+-
+-drop:
+- kfree_skb(skb);
+- return 0;
+-}
+-
+-struct proto_ops l2cap_sock_ops = {
+- family: PF_BLUETOOTH,
+- release: l2cap_sock_release,
+- bind: l2cap_sock_bind,
+- connect: l2cap_sock_connect,
+- listen: l2cap_sock_listen,
+- accept: l2cap_sock_accept,
+- getname: l2cap_sock_getname,
+- sendmsg: l2cap_sock_sendmsg,
+- recvmsg: l2cap_sock_recvmsg,
+- poll: l2cap_sock_poll,
+- socketpair: sock_no_socketpair,
+- ioctl: sock_no_ioctl,
+- shutdown: sock_no_shutdown,
+- setsockopt: l2cap_sock_setsockopt,
+- getsockopt: l2cap_sock_getsockopt,
+- mmap: sock_no_mmap
+-};
+-
+-struct net_proto_family l2cap_sock_family_ops = {
+- family: PF_BLUETOOTH,
+- create: l2cap_sock_create
+-};
+-
+-struct hci_proto l2cap_hci_proto = {
+- name: "L2CAP",
+- id: HCI_PROTO_L2CAP,
+- connect_ind: l2cap_connect_ind,
+- connect_cfm: l2cap_connect_cfm,
+- disconn_ind: l2cap_disconn_ind,
+- recv_acldata: l2cap_recv_acldata,
+-};
+-
+-struct notifier_block l2cap_nblock = {
+- notifier_call: l2cap_dev_event
+-};
+-
+-int __init l2cap_init(void)
+-{
+- INF("BlueZ L2CAP ver %s Copyright (C) 2000,2001 Qualcomm Inc",
+- VERSION);
+- INF("Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>");
+-
+- if (bluez_sock_register(BTPROTO_L2CAP, &l2cap_sock_family_ops)) {
+- ERR("Can't register L2CAP socket");
+- return -EPROTO;
+- }
+-
+- if (hci_register_proto(&l2cap_hci_proto) < 0) {
+- ERR("Can't register L2CAP protocol");
+- return -EPROTO;
+- }
+-
+- hci_register_notifier(&l2cap_nblock);
+-
+- l2cap_register_proc();
+-
+- return 0;
+-}
+-
+-void l2cap_cleanup(void)
+-{
+- l2cap_unregister_proc();
+-
+- /* Unregister socket, protocol and notifier */
+- if (bluez_sock_unregister(BTPROTO_L2CAP))
+- ERR("Can't unregister L2CAP socket");
+-
+- if (hci_unregister_proto(&l2cap_hci_proto) < 0)
+- ERR("Can't unregister L2CAP protocol");
+-
+- hci_unregister_notifier(&l2cap_nblock);
+-
+- /* We _must_ not have any sockets and/or connections
+- * at this stage.
+- */
+-
+- /* Free interface list and unlock HCI devices */
+- {
+- struct list_head *list = &l2cap_iff_list;
+-
+- while (!list_empty(list)) {
+- struct l2cap_iff *iff;
+-
+- iff = list_entry(list->next, struct l2cap_iff, list);
+- l2cap_iff_del(iff->hdev);
+- }
+- }
+-}
+-
+-module_init(l2cap_init);
+-module_exit(l2cap_cleanup);
+-
+-MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>");
+-MODULE_DESCRIPTION("BlueZ L2CAP ver " VERSION);
+-MODULE_LICENSE("GPL");
+-
+diff -urN linux-2.4.18/net/bluetooth/l2cap_proc.c linux-2.4.18-mh15/net/bluetooth/l2cap_proc.c
+--- linux-2.4.18/net/bluetooth/l2cap_proc.c 2001-09-07 18:28:38.000000000 +0200
++++ linux-2.4.18-mh15/net/bluetooth/l2cap_proc.c 1970-01-01 01:00:00.000000000 +0100
+@@ -1,165 +0,0 @@
+-/*
+- BlueZ - Bluetooth protocol stack for Linux
+- Copyright (C) 2000-2001 Qualcomm Incorporated
+-
+- Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
+-
+- This program is free software; you can redistribute it and/or modify
+- it under the terms of the GNU General Public License version 2 as
+- published by the Free Software Foundation;
+-
+- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+- OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+- IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+- CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
+- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+-
+- ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
+- COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
+- SOFTWARE IS DISCLAIMED.
+-*/
+-
+-/*
+- * BlueZ L2CAP proc fs support.
+- *
+- * $Id$
+- */
+-
+-#include <linux/config.h>
+-#include <linux/module.h>
+-
+-#include <linux/types.h>
+-#include <linux/errno.h>
+-#include <linux/kernel.h>
+-#include <linux/major.h>
+-#include <linux/sched.h>
+-#include <linux/slab.h>
+-#include <linux/poll.h>
+-#include <linux/fcntl.h>
+-#include <linux/init.h>
+-#include <linux/skbuff.h>
+-#include <linux/interrupt.h>
+-#include <linux/socket.h>
+-#include <linux/skbuff.h>
+-#include <linux/proc_fs.h>
+-#include <linux/list.h>
+-#include <net/sock.h>
+-
+-#include <asm/system.h>
+-#include <asm/uaccess.h>
+-
+-#include <net/bluetooth/bluez.h>
+-#include <net/bluetooth/bluetooth.h>
+-#include <net/bluetooth/hci_core.h>
+-#include <net/bluetooth/l2cap_core.h>
+-
+-#ifndef L2CAP_DEBUG
+-#undef DBG
+-#define DBG( A... )
+-#endif
+-
+-/* ----- PROC fs support ----- */
+-static int l2cap_conn_dump(char *buf, struct l2cap_iff *iff)
+-{
+- struct list_head *p;
+- char *ptr = buf;
+-
+- list_for_each(p, &iff->conn_list) {
+- struct l2cap_conn *c;
+-
+- c = list_entry(p, struct l2cap_conn, list);
+- ptr += sprintf(ptr, " %p %d %p %p %s %s\n",
+- c, c->state, c->iff, c->hconn, batostr(&c->src), batostr(&c->dst));
+- }
+-
+- return ptr - buf;
+-}
+-
+-static int l2cap_iff_dump(char *buf)
+-{
+- struct list_head *p;
+- char *ptr = buf;
+-
+- ptr += sprintf(ptr, "Interfaces:\n");
+-
+- write_lock(&l2cap_rt_lock);
+-
+- list_for_each(p, &l2cap_iff_list) {
+- struct l2cap_iff *iff;
+-
+- iff = list_entry(p, struct l2cap_iff, list);
+-
+- ptr += sprintf(ptr, " %s %p %p\n", iff->hdev->name, iff, iff->hdev);
+-
+- l2cap_iff_lock(iff);
+- ptr += l2cap_conn_dump(ptr, iff);
+- l2cap_iff_unlock(iff);
+- }
+-
+- write_unlock(&l2cap_rt_lock);
+-
+- ptr += sprintf(ptr, "\n");
+-
+- return ptr - buf;
+-}
+-
+-static int l2cap_sock_dump(char *buf, struct bluez_sock_list *list)
+-{
+- struct l2cap_pinfo *pi;
+- struct sock *sk;
+- char *ptr = buf;
+-
+- ptr += sprintf(ptr, "Sockets:\n");
+-
+- write_lock(&list->lock);
+-
+- for (sk = list->head; sk; sk = sk->next) {
+- pi = l2cap_pi(sk);
+- ptr += sprintf(ptr, " %p %d %p %d %s %s 0x%4.4x 0x%4.4x %d %d\n", sk, sk->state, pi->conn, pi->psm,
+- batostr(&pi->src), batostr(&pi->dst), pi->scid, pi->dcid, pi->imtu, pi->omtu );
+- }
+-
+- write_unlock(&list->lock);
+-
+- ptr += sprintf(ptr, "\n");
+-
+- return ptr - buf;
+-}
+-
+-static int l2cap_read_proc(char *buf, char **start, off_t offset, int count, int *eof, void *priv)
+-{
+- char *ptr = buf;
+- int len;
+-
+- DBG("count %d, offset %ld", count, offset);
+-
+- ptr += l2cap_iff_dump(ptr);
+- ptr += l2cap_sock_dump(ptr, &l2cap_sk_list);
+- len = ptr - buf;
+-
+- if (len <= count + offset)
+- *eof = 1;
+-
+- *start = buf + offset;
+- len -= offset;
+-
+- if (len > count)
+- len = count;
+- if (len < 0)
+- len = 0;
+-
+- return len;
+-}
+-
+-void l2cap_register_proc(void)
+-{
+- create_proc_read_entry("bluetooth/l2cap", 0, 0, l2cap_read_proc, NULL);
+-}
+-
+-void l2cap_unregister_proc(void)
+-{
+- remove_proc_entry("bluetooth/l2cap", NULL);
+-}
+diff -urN linux-2.4.18/net/bluetooth/lib.c linux-2.4.18-mh15/net/bluetooth/lib.c
+--- linux-2.4.18/net/bluetooth/lib.c 2001-09-07 18:28:38.000000000 +0200
++++ linux-2.4.18-mh15/net/bluetooth/lib.c 2004-08-01 16:26:23.000000000 +0200
+@@ -25,7 +25,7 @@
+ /*
+ * BlueZ kernel library.
+ *
+- * $Id$
++ * $Id$
+ */
+
+ #include <linux/kernel.h>
+@@ -105,7 +105,7 @@
+ return EACCES;
+
+ case 0x06:
+- return EINVAL;
++ return EBADE;
+
+ case 0x07:
+ return ENOMEM;
+diff -urN linux-2.4.18/net/bluetooth/Makefile linux-2.4.18-mh15/net/bluetooth/Makefile
+--- linux-2.4.18/net/bluetooth/Makefile 2001-06-12 04:15:27.000000000 +0200
++++ linux-2.4.18-mh15/net/bluetooth/Makefile 2004-08-01 16:26:23.000000000 +0200
+@@ -1,20 +1,40 @@
+ #
+-# Makefile for the Bluetooth subsystem
++# Makefile for the Linux Bluetooth subsystem
+ #
+-O_TARGET := bluetooth.o
+
+-list-multi := hci.o l2cap.o
+-export-objs := syms.o
+-hci-objs := af_bluetooth.o hci_core.o hci_sock.o lib.o syms.o
+-l2cap-objs := l2cap_core.o l2cap_proc.o
++O_TARGET := bluetooth.o
+
+-obj-$(CONFIG_BLUEZ) += hci.o
++list-multi := bluez.o
++export-objs := syms.o l2cap.o
++
++bluez-objs := af_bluetooth.o hci_core.o hci_conn.o hci_event.o hci_sock.o lib.o syms.o
++
++obj-$(CONFIG_BLUEZ) += bluez.o
+ obj-$(CONFIG_BLUEZ_L2CAP) += l2cap.o
++obj-$(CONFIG_BLUEZ_SCO) += sco.o
+
+-include $(TOPDIR)/Rules.make
++subdir-$(CONFIG_BLUEZ_RFCOMM) += rfcomm
++subdir-$(CONFIG_BLUEZ_BNEP) += bnep
++subdir-$(CONFIG_BLUEZ_CMTP) += cmtp
++subdir-$(CONFIG_BLUEZ_HIDP) += hidp
++
++ifeq ($(CONFIG_BLUEZ_RFCOMM),y)
++obj-y += rfcomm/rfcomm.o
++endif
+
+-hci.o: $(hci-objs)
+- $(LD) -r -o $@ $(hci-objs)
++ifeq ($(CONFIG_BLUEZ_BNEP),y)
++obj-y += bnep/bnep.o
++endif
++
++ifeq ($(CONFIG_BLUEZ_CMTP),y)
++obj-y += cmtp/cmtp.o
++endif
++
++ifeq ($(CONFIG_BLUEZ_HIDP),y)
++obj-y += hidp/hidp.o
++endif
++
++include $(TOPDIR)/Rules.make
+
+-l2cap.o: $(l2cap-objs)
+- $(LD) -r -o $@ $(l2cap-objs)
++bluez.o: $(bluez-objs)
++ $(LD) -r -o $@ $(bluez-objs)
+diff -urN linux-2.4.18/net/bluetooth/rfcomm/Config.in linux-2.4.18-mh15/net/bluetooth/rfcomm/Config.in
+--- linux-2.4.18/net/bluetooth/rfcomm/Config.in 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/net/bluetooth/rfcomm/Config.in 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,10 @@
++#
++# Bluetooth RFCOMM layer configuration
++#
++
++dep_tristate 'RFCOMM protocol support' CONFIG_BLUEZ_RFCOMM $CONFIG_BLUEZ_L2CAP
++
++if [ "$CONFIG_BLUEZ_RFCOMM" != "n" ]; then
++ bool ' RFCOMM TTY support' CONFIG_BLUEZ_RFCOMM_TTY
++fi
++
+diff -urN linux-2.4.18/net/bluetooth/rfcomm/core.c linux-2.4.18-mh15/net/bluetooth/rfcomm/core.c
+--- linux-2.4.18/net/bluetooth/rfcomm/core.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/net/bluetooth/rfcomm/core.c 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,1940 @@
++/*
++ RFCOMM implementation for Linux Bluetooth stack (BlueZ).
++ Copyright (C) 2002 Maxim Krasnyansky <maxk@qualcomm.com>
++ Copyright (C) 2002 Marcel Holtmann <marcel@holtmann.org>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License version 2 as
++ published by the Free Software Foundation;
++
++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++
++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
++ SOFTWARE IS DISCLAIMED.
++*/
++
++/*
++ RPN support - Dirk Husemann <hud@zurich.ibm.com>
++*/
++
++/*
++ * RFCOMM core.
++ *
++ * $Id$
++ */
++
++#define __KERNEL_SYSCALLS__
++
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/errno.h>
++#include <linux/kernel.h>
++#include <linux/sched.h>
++#include <linux/signal.h>
++#include <linux/init.h>
++#include <linux/wait.h>
++#include <linux/net.h>
++#include <linux/proc_fs.h>
++#include <net/sock.h>
++#include <asm/uaccess.h>
++#include <asm/unaligned.h>
++
++#include <net/bluetooth/bluetooth.h>
++#include <net/bluetooth/l2cap.h>
++#include <net/bluetooth/rfcomm.h>
++
++#define VERSION "1.1"
++
++#ifndef CONFIG_BLUEZ_RFCOMM_DEBUG
++#undef BT_DBG
++#define BT_DBG(D...)
++#endif
++
++struct task_struct *rfcomm_thread;
++DECLARE_MUTEX(rfcomm_sem);
++unsigned long rfcomm_event;
++
++static LIST_HEAD(session_list);
++static atomic_t terminate, running;
++
++static int rfcomm_send_frame(struct rfcomm_session *s, u8 *data, int len);
++static int rfcomm_send_sabm(struct rfcomm_session *s, u8 dlci);
++static int rfcomm_send_disc(struct rfcomm_session *s, u8 dlci);
++static int rfcomm_queue_disc(struct rfcomm_dlc *d);
++static int rfcomm_send_nsc(struct rfcomm_session *s, int cr, u8 type);
++static int rfcomm_send_pn(struct rfcomm_session *s, int cr, struct rfcomm_dlc *d);
++static int rfcomm_send_msc(struct rfcomm_session *s, int cr, u8 dlci, u8 v24_sig);
++static int rfcomm_send_test(struct rfcomm_session *s, int cr, u8 *pattern, int len);
++static int rfcomm_send_credits(struct rfcomm_session *s, u8 addr, u8 credits);
++static void rfcomm_make_uih(struct sk_buff *skb, u8 addr);
++
++static void rfcomm_process_connect(struct rfcomm_session *s);
++
++/* ---- RFCOMM frame parsing macros ---- */
++#define __get_dlci(b) ((b & 0xfc) >> 2)
++#define __get_channel(b) ((b & 0xf8) >> 3)
++#define __get_dir(b) ((b & 0x04) >> 2)
++#define __get_type(b) ((b & 0xef))
++
++#define __test_ea(b) ((b & 0x01))
++#define __test_cr(b) ((b & 0x02))
++#define __test_pf(b) ((b & 0x10))
++
++#define __addr(cr, dlci) (((dlci & 0x3f) << 2) | (cr << 1) | 0x01)
++#define __ctrl(type, pf) (((type & 0xef) | (pf << 4)))
++#define __dlci(dir, chn) (((chn & 0x1f) << 1) | dir)
++#define __srv_channel(dlci) (dlci >> 1)
++#define __dir(dlci) (dlci & 0x01)
++
++#define __len8(len) (((len) << 1) | 1)
++#define __len16(len) ((len) << 1)
++
++/* MCC macros */
++#define __mcc_type(cr, type) (((type << 2) | (cr << 1) | 0x01))
++#define __get_mcc_type(b) ((b & 0xfc) >> 2)
++#define __get_mcc_len(b) ((b & 0xfe) >> 1)
++
++/* RPN macros */
++#define __rpn_line_settings(data, stop, parity) ((data & 0x3) | ((stop & 0x1) << 2) | ((parity & 0x3) << 3))
++#define __get_rpn_data_bits(line) ((line) & 0x3)
++#define __get_rpn_stop_bits(line) (((line) >> 2) & 0x1)
++#define __get_rpn_parity(line) (((line) >> 3) & 0x3)
++
++/* ---- RFCOMM FCS computation ---- */
++
++/* CRC on 2 bytes */
++#define __crc(data) (rfcomm_crc_table[rfcomm_crc_table[0xff ^ data[0]] ^ data[1]])
++
++/* FCS on 2 bytes */
++static inline u8 __fcs(u8 *data)
++{
++ return (0xff - __crc(data));
++}
++
++/* FCS on 3 bytes */
++static inline u8 __fcs2(u8 *data)
++{
++ return (0xff - rfcomm_crc_table[__crc(data) ^ data[2]]);
++}
++
++/* Check FCS */
++static inline int __check_fcs(u8 *data, int type, u8 fcs)
++{
++ u8 f = __crc(data);
++
++ if (type != RFCOMM_UIH)
++ f = rfcomm_crc_table[f ^ data[2]];
++
++ return rfcomm_crc_table[f ^ fcs] != 0xcf;
++}
++
++/* ---- L2CAP callbacks ---- */
++static void rfcomm_l2state_change(struct sock *sk)
++{
++ BT_DBG("%p state %d", sk, sk->state);
++ rfcomm_schedule(RFCOMM_SCHED_STATE);
++}
++
++static void rfcomm_l2data_ready(struct sock *sk, int bytes)
++{
++ BT_DBG("%p bytes %d", sk, bytes);
++ rfcomm_schedule(RFCOMM_SCHED_RX);
++}
++
++static int rfcomm_l2sock_create(struct socket **sock)
++{
++ int err;
++
++ BT_DBG("");
++
++ err = sock_create(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP, sock);
++ if (!err) {
++ struct sock *sk = (*sock)->sk;
++ sk->data_ready = rfcomm_l2data_ready;
++ sk->state_change = rfcomm_l2state_change;
++ }
++ return err;
++}
++
++/* ---- RFCOMM DLCs ---- */
++static void rfcomm_dlc_timeout(unsigned long arg)
++{
++ struct rfcomm_dlc *d = (void *) arg;
++
++ BT_DBG("dlc %p state %ld", d, d->state);
++
++ set_bit(RFCOMM_TIMED_OUT, &d->flags);
++ rfcomm_dlc_put(d);
++ rfcomm_schedule(RFCOMM_SCHED_TIMEO);
++}
++
++static void rfcomm_dlc_set_timer(struct rfcomm_dlc *d, long timeout)
++{
++ BT_DBG("dlc %p state %ld timeout %ld", d, d->state, timeout);
++
++ if (!mod_timer(&d->timer, jiffies + timeout))
++ rfcomm_dlc_hold(d);
++}
++
++static void rfcomm_dlc_clear_timer(struct rfcomm_dlc *d)
++{
++ BT_DBG("dlc %p state %ld", d, d->state);
++
++ if (timer_pending(&d->timer) && del_timer(&d->timer))
++ rfcomm_dlc_put(d);
++}
++
++static void rfcomm_dlc_clear_state(struct rfcomm_dlc *d)
++{
++ BT_DBG("%p", d);
++
++ d->state = BT_OPEN;
++ d->flags = 0;
++ d->mscex = 0;
++ d->mtu = RFCOMM_DEFAULT_MTU;
++ d->v24_sig = RFCOMM_V24_RTC | RFCOMM_V24_RTR | RFCOMM_V24_DV;
++
++ d->cfc = RFCOMM_CFC_DISABLED;
++ d->rx_credits = RFCOMM_DEFAULT_CREDITS;
++}
++
++struct rfcomm_dlc *rfcomm_dlc_alloc(int prio)
++{
++ struct rfcomm_dlc *d = kmalloc(sizeof(*d), prio);
++ if (!d)
++ return NULL;
++ memset(d, 0, sizeof(*d));
++
++ init_timer(&d->timer);
++ d->timer.function = rfcomm_dlc_timeout;
++ d->timer.data = (unsigned long) d;
++
++ skb_queue_head_init(&d->tx_queue);
++ spin_lock_init(&d->lock);
++ atomic_set(&d->refcnt, 1);
++
++ rfcomm_dlc_clear_state(d);
++
++ BT_DBG("%p", d);
++ return d;
++}
++
++void rfcomm_dlc_free(struct rfcomm_dlc *d)
++{
++ BT_DBG("%p", d);
++
++ skb_queue_purge(&d->tx_queue);
++ kfree(d);
++}
++
++static void rfcomm_dlc_link(struct rfcomm_session *s, struct rfcomm_dlc *d)
++{
++ BT_DBG("dlc %p session %p", d, s);
++
++ rfcomm_session_hold(s);
++
++ rfcomm_dlc_hold(d);
++ list_add(&d->list, &s->dlcs);
++ d->session = s;
++}
++
++static void rfcomm_dlc_unlink(struct rfcomm_dlc *d)
++{
++ struct rfcomm_session *s = d->session;
++
++ BT_DBG("dlc %p refcnt %d session %p", d, atomic_read(&d->refcnt), s);
++
++ list_del(&d->list);
++ d->session = NULL;
++ rfcomm_dlc_put(d);
++
++ rfcomm_session_put(s);
++}
++
++static struct rfcomm_dlc *rfcomm_dlc_get(struct rfcomm_session *s, u8 dlci)
++{
++ struct rfcomm_dlc *d;
++ struct list_head *p;
++
++ list_for_each(p, &s->dlcs) {
++ d = list_entry(p, struct rfcomm_dlc, list);
++ if (d->dlci == dlci)
++ return d;
++ }
++ return NULL;
++}
++
++static int __rfcomm_dlc_open(struct rfcomm_dlc *d, bdaddr_t *src, bdaddr_t *dst, u8 channel)
++{
++ struct rfcomm_session *s;
++ int err = 0;
++ u8 dlci;
++
++ BT_DBG("dlc %p state %ld %s %s channel %d",
++ d, d->state, batostr(src), batostr(dst), channel);
++
++ if (channel < 1 || channel > 30)
++ return -EINVAL;
++
++ if (d->state != BT_OPEN && d->state != BT_CLOSED)
++ return 0;
++
++ s = rfcomm_session_get(src, dst);
++ if (!s) {
++ s = rfcomm_session_create(src, dst, &err);
++ if (!s)
++ return err;
++ }
++
++ dlci = __dlci(!s->initiator, channel);
++
++ /* Check if DLCI already exists */
++ if (rfcomm_dlc_get(s, dlci))
++ return -EBUSY;
++
++ rfcomm_dlc_clear_state(d);
++
++ d->dlci = dlci;
++ d->addr = __addr(s->initiator, dlci);
++ d->priority = 7;
++
++ d->state = BT_CONFIG;
++ rfcomm_dlc_link(s, d);
++
++ d->mtu = s->mtu;
++ d->cfc = (s->cfc == RFCOMM_CFC_UNKNOWN) ? 0 : s->cfc;
++
++ if (s->state == BT_CONNECTED)
++ rfcomm_send_pn(s, 1, d);
++ rfcomm_dlc_set_timer(d, RFCOMM_CONN_TIMEOUT);
++ return 0;
++}
++
++int rfcomm_dlc_open(struct rfcomm_dlc *d, bdaddr_t *src, bdaddr_t *dst, u8 channel)
++{
++ mm_segment_t fs;
++ int r;
++
++ rfcomm_lock();
++
++ fs = get_fs(); set_fs(KERNEL_DS);
++ r = __rfcomm_dlc_open(d, src, dst, channel);
++ set_fs(fs);
++
++ rfcomm_unlock();
++ return r;
++}
++
++static int __rfcomm_dlc_close(struct rfcomm_dlc *d, int err)
++{
++ struct rfcomm_session *s = d->session;
++ if (!s)
++ return 0;
++
++ BT_DBG("dlc %p state %ld dlci %d err %d session %p",
++ d, d->state, d->dlci, err, s);
++
++ switch (d->state) {
++ case BT_CONNECTED:
++ case BT_CONFIG:
++ case BT_CONNECT:
++ d->state = BT_DISCONN;
++ if (skb_queue_empty(&d->tx_queue)) {
++ rfcomm_send_disc(s, d->dlci);
++ rfcomm_dlc_set_timer(d, RFCOMM_DISC_TIMEOUT);
++ } else {
++ rfcomm_queue_disc(d);
++ rfcomm_dlc_set_timer(d, RFCOMM_DISC_TIMEOUT * 2);
++ }
++ break;
++
++ default:
++ rfcomm_dlc_clear_timer(d);
++
++ rfcomm_dlc_lock(d);
++ d->state = BT_CLOSED;
++ d->state_change(d, err);
++ rfcomm_dlc_unlock(d);
++
++ skb_queue_purge(&d->tx_queue);
++ rfcomm_dlc_unlink(d);
++ }
++
++ return 0;
++}
++
++int rfcomm_dlc_close(struct rfcomm_dlc *d, int err)
++{
++ mm_segment_t fs;
++ int r;
++
++ rfcomm_lock();
++
++ fs = get_fs(); set_fs(KERNEL_DS);
++ r = __rfcomm_dlc_close(d, err);
++ set_fs(fs);
++
++ rfcomm_unlock();
++ return r;
++}
++
++int rfcomm_dlc_send(struct rfcomm_dlc *d, struct sk_buff *skb)
++{
++ int len = skb->len;
++
++ if (d->state != BT_CONNECTED)
++ return -ENOTCONN;
++
++ BT_DBG("dlc %p mtu %d len %d", d, d->mtu, len);
++
++ if (len > d->mtu)
++ return -EINVAL;
++
++ rfcomm_make_uih(skb, d->addr);
++ skb_queue_tail(&d->tx_queue, skb);
++
++ if (!test_bit(RFCOMM_TX_THROTTLED, &d->flags))
++ rfcomm_schedule(RFCOMM_SCHED_TX);
++ return len;
++}
++
++void __rfcomm_dlc_throttle(struct rfcomm_dlc *d)
++{
++ BT_DBG("dlc %p state %ld", d, d->state);
++
++ if (!d->cfc) {
++ d->v24_sig |= RFCOMM_V24_FC;
++ set_bit(RFCOMM_MSC_PENDING, &d->flags);
++ }
++ rfcomm_schedule(RFCOMM_SCHED_TX);
++}
++
++void __rfcomm_dlc_unthrottle(struct rfcomm_dlc *d)
++{
++ BT_DBG("dlc %p state %ld", d, d->state);
++
++ if (!d->cfc) {
++ d->v24_sig &= ~RFCOMM_V24_FC;
++ set_bit(RFCOMM_MSC_PENDING, &d->flags);
++ }
++ rfcomm_schedule(RFCOMM_SCHED_TX);
++}
++
++/*
++ Set/get modem status functions use _local_ status i.e. what we report
++ to the other side.
++ Remote status is provided by dlc->modem_status() callback.
++ */
++int rfcomm_dlc_set_modem_status(struct rfcomm_dlc *d, u8 v24_sig)
++{
++ BT_DBG("dlc %p state %ld v24_sig 0x%x",
++ d, d->state, v24_sig);
++
++ if (test_bit(RFCOMM_RX_THROTTLED, &d->flags))
++ v24_sig |= RFCOMM_V24_FC;
++ else
++ v24_sig &= ~RFCOMM_V24_FC;
++
++ d->v24_sig = v24_sig;
++
++ if (!test_and_set_bit(RFCOMM_MSC_PENDING, &d->flags))
++ rfcomm_schedule(RFCOMM_SCHED_TX);
++
++ return 0;
++}
++
++int rfcomm_dlc_get_modem_status(struct rfcomm_dlc *d, u8 *v24_sig)
++{
++ BT_DBG("dlc %p state %ld v24_sig 0x%x",
++ d, d->state, d->v24_sig);
++
++ *v24_sig = d->v24_sig;
++ return 0;
++}
++
++/* ---- RFCOMM sessions ---- */
++struct rfcomm_session *rfcomm_session_add(struct socket *sock, int state)
++{
++ struct rfcomm_session *s = kmalloc(sizeof(*s), GFP_KERNEL);
++ if (!s)
++ return NULL;
++ memset(s, 0, sizeof(*s));
++
++ BT_DBG("session %p sock %p", s, sock);
++
++ INIT_LIST_HEAD(&s->dlcs);
++ s->state = state;
++ s->sock = sock;
++
++ s->mtu = RFCOMM_DEFAULT_MTU;
++ s->cfc = RFCOMM_CFC_UNKNOWN;
++
++ list_add(&s->list, &session_list);
++
++ /* Do not increment module usage count for listeting sessions.
++ * Otherwise we won't be able to unload the module. */
++ if (state != BT_LISTEN)
++ MOD_INC_USE_COUNT;
++ return s;
++}
++
++void rfcomm_session_del(struct rfcomm_session *s)
++{
++ int state = s->state;
++
++ BT_DBG("session %p state %ld", s, s->state);
++
++ list_del(&s->list);
++
++ if (state == BT_CONNECTED)
++ rfcomm_send_disc(s, 0);
++
++ sock_release(s->sock);
++ kfree(s);
++
++ if (state != BT_LISTEN)
++ MOD_DEC_USE_COUNT;
++}
++
++struct rfcomm_session *rfcomm_session_get(bdaddr_t *src, bdaddr_t *dst)
++{
++ struct rfcomm_session *s;
++ struct list_head *p, *n;
++ struct bluez_pinfo *pi;
++ list_for_each_safe(p, n, &session_list) {
++ s = list_entry(p, struct rfcomm_session, list);
++ pi = bluez_pi(s->sock->sk);
++
++ if ((!bacmp(src, BDADDR_ANY) || !bacmp(&pi->src, src)) &&
++ !bacmp(&pi->dst, dst))
++ return s;
++ }
++ return NULL;
++}
++
++void rfcomm_session_close(struct rfcomm_session *s, int err)
++{
++ struct rfcomm_dlc *d;
++ struct list_head *p, *n;
++
++ BT_DBG("session %p state %ld err %d", s, s->state, err);
++
++ rfcomm_session_hold(s);
++
++ s->state = BT_CLOSED;
++
++ /* Close all dlcs */
++ list_for_each_safe(p, n, &s->dlcs) {
++ d = list_entry(p, struct rfcomm_dlc, list);
++ d->state = BT_CLOSED;
++ __rfcomm_dlc_close(d, err);
++ }
++
++ rfcomm_session_put(s);
++}
++
++struct rfcomm_session *rfcomm_session_create(bdaddr_t *src, bdaddr_t *dst, int *err)
++{
++ struct rfcomm_session *s = NULL;
++ struct sockaddr_l2 addr;
++ struct l2cap_options opts;
++ struct socket *sock;
++ int size;
++
++ BT_DBG("%s %s", batostr(src), batostr(dst));
++
++ *err = rfcomm_l2sock_create(&sock);
++ if (*err < 0)
++ return NULL;
++
++ bacpy(&addr.l2_bdaddr, src);
++ addr.l2_family = AF_BLUETOOTH;
++ addr.l2_psm = 0;
++ *err = sock->ops->bind(sock, (struct sockaddr *) &addr, sizeof(addr));
++ if (*err < 0)
++ goto failed;
++
++ /* Set L2CAP options */
++ size = sizeof(opts);
++ sock->ops->getsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, (void *)&opts, &size);
++
++ opts.imtu = RFCOMM_MAX_L2CAP_MTU;
++ sock->ops->setsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, (void *)&opts, size);
++
++ s = rfcomm_session_add(sock, BT_BOUND);
++ if (!s) {
++ *err = -ENOMEM;
++ goto failed;
++ }
++
++ s->initiator = 1;
++
++ bacpy(&addr.l2_bdaddr, dst);
++ addr.l2_family = AF_BLUETOOTH;
++ addr.l2_psm = htobs(RFCOMM_PSM);
++ *err = sock->ops->connect(sock, (struct sockaddr *) &addr, sizeof(addr), O_NONBLOCK);
++ if (*err == 0 || *err == -EAGAIN)
++ return s;
++
++ rfcomm_session_del(s);
++ return NULL;
++
++failed:
++ sock_release(sock);
++ return NULL;
++}
++
++void rfcomm_session_getaddr(struct rfcomm_session *s, bdaddr_t *src, bdaddr_t *dst)
++{
++ struct sock *sk = s->sock->sk;
++ if (src)
++ bacpy(src, &bluez_pi(sk)->src);
++ if (dst)
++ bacpy(dst, &bluez_pi(sk)->dst);
++}
++
++/* ---- RFCOMM frame sending ---- */
++static int rfcomm_send_frame(struct rfcomm_session *s, u8 *data, int len)
++{
++ struct socket *sock = s->sock;
++ struct iovec iv = { data, len };
++ struct msghdr msg;
++ int err;
++
++ BT_DBG("session %p len %d", s, len);
++
++ memset(&msg, 0, sizeof(msg));
++ msg.msg_iovlen = 1;
++ msg.msg_iov = &iv;
++
++ err = sock->ops->sendmsg(sock, &msg, len, 0);
++ return err;
++}
++
++static int rfcomm_send_sabm(struct rfcomm_session *s, u8 dlci)
++{
++ struct rfcomm_cmd cmd;
++
++ BT_DBG("%p dlci %d", s, dlci);
++
++ cmd.addr = __addr(s->initiator, dlci);
++ cmd.ctrl = __ctrl(RFCOMM_SABM, 1);
++ cmd.len = __len8(0);
++ cmd.fcs = __fcs2((u8 *) &cmd);
++
++ return rfcomm_send_frame(s, (void *) &cmd, sizeof(cmd));
++}
++
++static int rfcomm_send_ua(struct rfcomm_session *s, u8 dlci)
++{
++ struct rfcomm_cmd cmd;
++
++ BT_DBG("%p dlci %d", s, dlci);
++
++ cmd.addr = __addr(!s->initiator, dlci);
++ cmd.ctrl = __ctrl(RFCOMM_UA, 1);
++ cmd.len = __len8(0);
++ cmd.fcs = __fcs2((u8 *) &cmd);
++
++ return rfcomm_send_frame(s, (void *) &cmd, sizeof(cmd));
++}
++
++static int rfcomm_send_disc(struct rfcomm_session *s, u8 dlci)
++{
++ struct rfcomm_cmd cmd;
++
++ BT_DBG("%p dlci %d", s, dlci);
++
++ cmd.addr = __addr(s->initiator, dlci);
++ cmd.ctrl = __ctrl(RFCOMM_DISC, 1);
++ cmd.len = __len8(0);
++ cmd.fcs = __fcs2((u8 *) &cmd);
++
++ return rfcomm_send_frame(s, (void *) &cmd, sizeof(cmd));
++}
++
++static int rfcomm_queue_disc(struct rfcomm_dlc *d)
++{
++ struct rfcomm_cmd *cmd;
++ struct sk_buff *skb;
++
++ BT_DBG("dlc %p dlci %d", d, d->dlci);
++
++ skb = alloc_skb(sizeof(*cmd), GFP_KERNEL);
++ if (!skb)
++ return -ENOMEM;
++
++ cmd = (void *) __skb_put(skb, sizeof(*cmd));
++ cmd->addr = d->addr;
++ cmd->ctrl = __ctrl(RFCOMM_DISC, 1);
++ cmd->len = __len8(0);
++ cmd->fcs = __fcs2((u8 *) cmd);
++
++ skb_queue_tail(&d->tx_queue, skb);
++ rfcomm_schedule(RFCOMM_SCHED_TX);
++ return 0;
++}
++
++static int rfcomm_send_dm(struct rfcomm_session *s, u8 dlci)
++{
++ struct rfcomm_cmd cmd;
++
++ BT_DBG("%p dlci %d", s, dlci);
++
++ cmd.addr = __addr(!s->initiator, dlci);
++ cmd.ctrl = __ctrl(RFCOMM_DM, 1);
++ cmd.len = __len8(0);
++ cmd.fcs = __fcs2((u8 *) &cmd);
++
++ return rfcomm_send_frame(s, (void *) &cmd, sizeof(cmd));
++}
++
++static int rfcomm_send_nsc(struct rfcomm_session *s, int cr, u8 type)
++{
++ struct rfcomm_hdr *hdr;
++ struct rfcomm_mcc *mcc;
++ u8 buf[16], *ptr = buf;
++
++ BT_DBG("%p cr %d type %d", s, cr, type);
++
++ hdr = (void *) ptr; ptr += sizeof(*hdr);
++ hdr->addr = __addr(s->initiator, 0);
++ hdr->ctrl = __ctrl(RFCOMM_UIH, 0);
++ hdr->len = __len8(sizeof(*mcc) + 1);
++
++ mcc = (void *) ptr; ptr += sizeof(*mcc);
++ mcc->type = __mcc_type(cr, RFCOMM_NSC);
++ mcc->len = __len8(1);
++
++ /* Type that we didn't like */
++ *ptr = __mcc_type(cr, type); ptr++;
++
++ *ptr = __fcs(buf); ptr++;
++
++ return rfcomm_send_frame(s, buf, ptr - buf);
++}
++
++static int rfcomm_send_pn(struct rfcomm_session *s, int cr, struct rfcomm_dlc *d)
++{
++ struct rfcomm_hdr *hdr;
++ struct rfcomm_mcc *mcc;
++ struct rfcomm_pn *pn;
++ u8 buf[16], *ptr = buf;
++
++ BT_DBG("%p cr %d dlci %d mtu %d", s, cr, d->dlci, d->mtu);
++
++ hdr = (void *) ptr; ptr += sizeof(*hdr);
++ hdr->addr = __addr(s->initiator, 0);
++ hdr->ctrl = __ctrl(RFCOMM_UIH, 0);
++ hdr->len = __len8(sizeof(*mcc) + sizeof(*pn));
++
++ mcc = (void *) ptr; ptr += sizeof(*mcc);
++ mcc->type = __mcc_type(cr, RFCOMM_PN);
++ mcc->len = __len8(sizeof(*pn));
++
++ pn = (void *) ptr; ptr += sizeof(*pn);
++ pn->dlci = d->dlci;
++ pn->priority = d->priority;
++ pn->ack_timer = 0;
++ pn->max_retrans = 0;
++
++ if (s->cfc) {
++ pn->flow_ctrl = cr ? 0xf0 : 0xe0;
++ pn->credits = RFCOMM_DEFAULT_CREDITS;
++ } else {
++ pn->flow_ctrl = 0;
++ pn->credits = 0;
++ }
++
++ pn->mtu = htobs(d->mtu);
++
++ *ptr = __fcs(buf); ptr++;
++
++ return rfcomm_send_frame(s, buf, ptr - buf);
++}
++
++static int rfcomm_send_rpn(struct rfcomm_session *s, int cr, u8 dlci,
++ u8 bit_rate, u8 data_bits, u8 stop_bits,
++ u8 parity, u8 flow_ctrl_settings,
++ u8 xon_char, u8 xoff_char, u16 param_mask)
++{
++ struct rfcomm_hdr *hdr;
++ struct rfcomm_mcc *mcc;
++ struct rfcomm_rpn *rpn;
++ u8 buf[16], *ptr = buf;
++
++ BT_DBG("%p cr %d dlci %d bit_r 0x%x data_b 0x%x stop_b 0x%x parity 0x%x"
++ "flwc_s 0x%x xon_c 0x%x xoff_c 0x%x p_mask 0x%x",
++ s, cr, dlci, bit_rate, data_bits, stop_bits, parity,
++ flow_ctrl_settings, xon_char, xoff_char, param_mask);
++
++ hdr = (void *) ptr; ptr += sizeof(*hdr);
++ hdr->addr = __addr(s->initiator, 0);
++ hdr->ctrl = __ctrl(RFCOMM_UIH, 0);
++ hdr->len = __len8(sizeof(*mcc) + sizeof(*rpn));
++
++ mcc = (void *) ptr; ptr += sizeof(*mcc);
++ mcc->type = __mcc_type(cr, RFCOMM_RPN);
++ mcc->len = __len8(sizeof(*rpn));
++
++ rpn = (void *) ptr; ptr += sizeof(*rpn);
++ rpn->dlci = __addr(1, dlci);
++ rpn->bit_rate = bit_rate;
++ rpn->line_settings = __rpn_line_settings(data_bits, stop_bits, parity);
++ rpn->flow_ctrl = flow_ctrl_settings;
++ rpn->xon_char = xon_char;
++ rpn->xoff_char = xoff_char;
++ rpn->param_mask = param_mask;
++
++ *ptr = __fcs(buf); ptr++;
++
++ return rfcomm_send_frame(s, buf, ptr - buf);
++}
++
++static int rfcomm_send_rls(struct rfcomm_session *s, int cr, u8 dlci, u8 status)
++{
++ struct rfcomm_hdr *hdr;
++ struct rfcomm_mcc *mcc;
++ struct rfcomm_rls *rls;
++ u8 buf[16], *ptr = buf;
++
++ BT_DBG("%p cr %d status 0x%x", s, cr, status);
++
++ hdr = (void *) ptr; ptr += sizeof(*hdr);
++ hdr->addr = __addr(s->initiator, 0);
++ hdr->ctrl = __ctrl(RFCOMM_UIH, 0);
++ hdr->len = __len8(sizeof(*mcc) + sizeof(*rls));
++
++ mcc = (void *) ptr; ptr += sizeof(*mcc);
++ mcc->type = __mcc_type(cr, RFCOMM_RLS);
++ mcc->len = __len8(sizeof(*rls));
++
++ rls = (void *) ptr; ptr += sizeof(*rls);
++ rls->dlci = __addr(1, dlci);
++ rls->status = status;
++
++ *ptr = __fcs(buf); ptr++;
++
++ return rfcomm_send_frame(s, buf, ptr - buf);
++}
++
++static int rfcomm_send_msc(struct rfcomm_session *s, int cr, u8 dlci, u8 v24_sig)
++{
++ struct rfcomm_hdr *hdr;
++ struct rfcomm_mcc *mcc;
++ struct rfcomm_msc *msc;
++ u8 buf[16], *ptr = buf;
++
++ BT_DBG("%p cr %d v24 0x%x", s, cr, v24_sig);
++
++ hdr = (void *) ptr; ptr += sizeof(*hdr);
++ hdr->addr = __addr(s->initiator, 0);
++ hdr->ctrl = __ctrl(RFCOMM_UIH, 0);
++ hdr->len = __len8(sizeof(*mcc) + sizeof(*msc));
++
++ mcc = (void *) ptr; ptr += sizeof(*mcc);
++ mcc->type = __mcc_type(cr, RFCOMM_MSC);
++ mcc->len = __len8(sizeof(*msc));
++
++ msc = (void *) ptr; ptr += sizeof(*msc);
++ msc->dlci = __addr(1, dlci);
++ msc->v24_sig = v24_sig | 0x01;
++
++ *ptr = __fcs(buf); ptr++;
++
++ return rfcomm_send_frame(s, buf, ptr - buf);
++}
++
++static int rfcomm_send_fcoff(struct rfcomm_session *s, int cr)
++{
++ struct rfcomm_hdr *hdr;
++ struct rfcomm_mcc *mcc;
++ u8 buf[16], *ptr = buf;
++
++ BT_DBG("%p cr %d", s, cr);
++
++ hdr = (void *) ptr; ptr += sizeof(*hdr);
++ hdr->addr = __addr(s->initiator, 0);
++ hdr->ctrl = __ctrl(RFCOMM_UIH, 0);
++ hdr->len = __len8(sizeof(*mcc));
++
++ mcc = (void *) ptr; ptr += sizeof(*mcc);
++ mcc->type = __mcc_type(cr, RFCOMM_FCOFF);
++ mcc->len = __len8(0);
++
++ *ptr = __fcs(buf); ptr++;
++
++ return rfcomm_send_frame(s, buf, ptr - buf);
++}
++
++static int rfcomm_send_fcon(struct rfcomm_session *s, int cr)
++{
++ struct rfcomm_hdr *hdr;
++ struct rfcomm_mcc *mcc;
++ u8 buf[16], *ptr = buf;
++
++ BT_DBG("%p cr %d", s, cr);
++
++ hdr = (void *) ptr; ptr += sizeof(*hdr);
++ hdr->addr = __addr(s->initiator, 0);
++ hdr->ctrl = __ctrl(RFCOMM_UIH, 0);
++ hdr->len = __len8(sizeof(*mcc));
++
++ mcc = (void *) ptr; ptr += sizeof(*mcc);
++ mcc->type = __mcc_type(cr, RFCOMM_FCON);
++ mcc->len = __len8(0);
++
++ *ptr = __fcs(buf); ptr++;
++
++ return rfcomm_send_frame(s, buf, ptr - buf);
++}
++
++static int rfcomm_send_test(struct rfcomm_session *s, int cr, u8 *pattern, int len)
++{
++ struct socket *sock = s->sock;
++ struct iovec iv[3];
++ struct msghdr msg;
++ unsigned char hdr[5], crc[1];
++
++ if (len > 125)
++ return -EINVAL;
++
++ BT_DBG("%p cr %d", s, cr);
++
++ hdr[0] = __addr(s->initiator, 0);
++ hdr[1] = __ctrl(RFCOMM_UIH, 0);
++ hdr[2] = 0x01 | ((len + 2) << 1);
++ hdr[3] = 0x01 | ((cr & 0x01) << 1) | (RFCOMM_TEST << 2);
++ hdr[4] = 0x01 | (len << 1);
++
++ crc[0] = __fcs(hdr);
++
++ iv[0].iov_base = hdr;
++ iv[0].iov_len = 5;
++ iv[1].iov_base = pattern;
++ iv[1].iov_len = len;
++ iv[2].iov_base = crc;
++ iv[2].iov_len = 1;
++
++ memset(&msg, 0, sizeof(msg));
++ msg.msg_iovlen = 3;
++ msg.msg_iov = iv;
++ return sock->ops->sendmsg(sock, &msg, 6 + len, 0);
++}
++
++static int rfcomm_send_credits(struct rfcomm_session *s, u8 addr, u8 credits)
++{
++ struct rfcomm_hdr *hdr;
++ u8 buf[16], *ptr = buf;
++
++ BT_DBG("%p addr %d credits %d", s, addr, credits);
++
++ hdr = (void *) ptr; ptr += sizeof(*hdr);
++ hdr->addr = addr;
++ hdr->ctrl = __ctrl(RFCOMM_UIH, 1);
++ hdr->len = __len8(0);
++
++ *ptr = credits; ptr++;
++
++ *ptr = __fcs(buf); ptr++;
++
++ return rfcomm_send_frame(s, buf, ptr - buf);
++}
++
++static void rfcomm_make_uih(struct sk_buff *skb, u8 addr)
++{
++ struct rfcomm_hdr *hdr;
++ int len = skb->len;
++ u8 *crc;
++
++ if (len > 127) {
++ hdr = (void *) skb_push(skb, 4);
++ put_unaligned(htobs(__len16(len)), (u16 *) &hdr->len);
++ } else {
++ hdr = (void *) skb_push(skb, 3);
++ hdr->len = __len8(len);
++ }
++ hdr->addr = addr;
++ hdr->ctrl = __ctrl(RFCOMM_UIH, 0);
++
++ crc = skb_put(skb, 1);
++ *crc = __fcs((void *) hdr);
++}
++
++/* ---- RFCOMM frame reception ---- */
++static int rfcomm_recv_ua(struct rfcomm_session *s, u8 dlci)
++{
++ BT_DBG("session %p state %ld dlci %d", s, s->state, dlci);
++
++ if (dlci) {
++ /* Data channel */
++ struct rfcomm_dlc *d = rfcomm_dlc_get(s, dlci);
++ if (!d) {
++ rfcomm_send_dm(s, dlci);
++ return 0;
++ }
++
++ switch (d->state) {
++ case BT_CONNECT:
++ rfcomm_dlc_clear_timer(d);
++
++ rfcomm_dlc_lock(d);
++ d->state = BT_CONNECTED;
++ d->state_change(d, 0);
++ rfcomm_dlc_unlock(d);
++
++ rfcomm_send_msc(s, 1, dlci, d->v24_sig);
++ break;
++
++ case BT_DISCONN:
++ d->state = BT_CLOSED;
++ __rfcomm_dlc_close(d, 0);
++ break;
++ }
++ } else {
++ /* Control channel */
++ switch (s->state) {
++ case BT_CONNECT:
++ s->state = BT_CONNECTED;
++ rfcomm_process_connect(s);
++ break;
++ }
++ }
++ return 0;
++}
++
++static int rfcomm_recv_dm(struct rfcomm_session *s, u8 dlci)
++{
++ int err = 0;
++
++ BT_DBG("session %p state %ld dlci %d", s, s->state, dlci);
++
++ if (dlci) {
++ /* Data DLC */
++ struct rfcomm_dlc *d = rfcomm_dlc_get(s, dlci);
++ if (d) {
++ if (d->state == BT_CONNECT || d->state == BT_CONFIG)
++ err = ECONNREFUSED;
++ else
++ err = ECONNRESET;
++
++ d->state = BT_CLOSED;
++ __rfcomm_dlc_close(d, err);
++ }
++ } else {
++ if (s->state == BT_CONNECT)
++ err = ECONNREFUSED;
++ else
++ err = ECONNRESET;
++
++ s->state = BT_CLOSED;
++ rfcomm_session_close(s, err);
++ }
++ return 0;
++}
++
++static int rfcomm_recv_disc(struct rfcomm_session *s, u8 dlci)
++{
++ int err = 0;
++
++ BT_DBG("session %p state %ld dlci %d", s, s->state, dlci);
++
++ if (dlci) {
++ struct rfcomm_dlc *d = rfcomm_dlc_get(s, dlci);
++ if (d) {
++ rfcomm_send_ua(s, dlci);
++
++ if (d->state == BT_CONNECT || d->state == BT_CONFIG)
++ err = ECONNREFUSED;
++ else
++ err = ECONNRESET;
++
++ d->state = BT_CLOSED;
++ __rfcomm_dlc_close(d, err);
++ } else
++ rfcomm_send_dm(s, dlci);
++
++ } else {
++ rfcomm_send_ua(s, 0);
++
++ if (s->state == BT_CONNECT)
++ err = ECONNREFUSED;
++ else
++ err = ECONNRESET;
++
++ s->state = BT_CLOSED;
++ rfcomm_session_close(s, err);
++ }
++
++ return 0;
++}
++
++static int rfcomm_recv_sabm(struct rfcomm_session *s, u8 dlci)
++{
++ struct rfcomm_dlc *d;
++ u8 channel;
++
++ BT_DBG("session %p state %ld dlci %d", s, s->state, dlci);
++
++ if (!dlci) {
++ rfcomm_send_ua(s, 0);
++
++ if (s->state == BT_OPEN) {
++ s->state = BT_CONNECTED;
++ rfcomm_process_connect(s);
++ }
++ return 0;
++ }
++
++ /* Check if DLC exists */
++ d = rfcomm_dlc_get(s, dlci);
++ if (d) {
++ if (d->state == BT_OPEN) {
++ /* DLC was previously opened by PN request */
++ rfcomm_send_ua(s, dlci);
++
++ rfcomm_dlc_lock(d);
++ d->state = BT_CONNECTED;
++ d->state_change(d, 0);
++ rfcomm_dlc_unlock(d);
++
++ rfcomm_send_msc(s, 1, dlci, d->v24_sig);
++ }
++ return 0;
++ }
++
++ /* Notify socket layer about incomming connection */
++ channel = __srv_channel(dlci);
++ if (rfcomm_connect_ind(s, channel, &d)) {
++ d->dlci = dlci;
++ d->addr = __addr(s->initiator, dlci);
++ rfcomm_dlc_link(s, d);
++
++ rfcomm_send_ua(s, dlci);
++
++ rfcomm_dlc_lock(d);
++ d->state = BT_CONNECTED;
++ d->state_change(d, 0);
++ rfcomm_dlc_unlock(d);
++
++ rfcomm_send_msc(s, 1, dlci, d->v24_sig);
++ } else {
++ rfcomm_send_dm(s, dlci);
++ }
++
++ return 0;
++}
++
++static int rfcomm_apply_pn(struct rfcomm_dlc *d, int cr, struct rfcomm_pn *pn)
++{
++ struct rfcomm_session *s = d->session;
++
++ BT_DBG("dlc %p state %ld dlci %d mtu %d fc 0x%x credits %d",
++ d, d->state, d->dlci, pn->mtu, pn->flow_ctrl, pn->credits);
++
++ if (pn->flow_ctrl == 0xf0 || pn->flow_ctrl == 0xe0) {
++ d->cfc = s->cfc = RFCOMM_CFC_ENABLED;
++ d->tx_credits = pn->credits;
++ } else {
++ d->cfc = s->cfc = RFCOMM_CFC_DISABLED;
++ set_bit(RFCOMM_TX_THROTTLED, &d->flags);
++ }
++
++ d->priority = pn->priority;
++
++ d->mtu = s->mtu = btohs(pn->mtu);
++
++ return 0;
++}
++
++static int rfcomm_recv_pn(struct rfcomm_session *s, int cr, struct sk_buff *skb)
++{
++ struct rfcomm_pn *pn = (void *) skb->data;
++ struct rfcomm_dlc *d;
++ u8 dlci = pn->dlci;
++
++ BT_DBG("session %p state %ld dlci %d", s, s->state, dlci);
++
++ if (!dlci)
++ return 0;
++
++ d = rfcomm_dlc_get(s, dlci);
++ if (d) {
++ if (cr) {
++ /* PN request */
++ rfcomm_apply_pn(d, cr, pn);
++ rfcomm_send_pn(s, 0, d);
++ } else {
++ /* PN response */
++ switch (d->state) {
++ case BT_CONFIG:
++ rfcomm_apply_pn(d, cr, pn);
++
++ d->state = BT_CONNECT;
++ rfcomm_send_sabm(s, d->dlci);
++ break;
++ }
++ }
++ } else {
++ u8 channel = __srv_channel(dlci);
++
++ if (!cr)
++ return 0;
++
++ /* PN request for non existing DLC.
++ * Assume incomming connection. */
++ if (rfcomm_connect_ind(s, channel, &d)) {
++ d->dlci = dlci;
++ d->addr = __addr(s->initiator, dlci);
++ rfcomm_dlc_link(s, d);
++
++ rfcomm_apply_pn(d, cr, pn);
++
++ d->state = BT_OPEN;
++ rfcomm_send_pn(s, 0, d);
++ } else {
++ rfcomm_send_dm(s, dlci);
++ }
++ }
++ return 0;
++}
++
++static int rfcomm_recv_rpn(struct rfcomm_session *s, int cr, int len, struct sk_buff *skb)
++{
++ struct rfcomm_rpn *rpn = (void *) skb->data;
++ u8 dlci = __get_dlci(rpn->dlci);
++
++ u8 bit_rate = 0;
++ u8 data_bits = 0;
++ u8 stop_bits = 0;
++ u8 parity = 0;
++ u8 flow_ctrl = 0;
++ u8 xon_char = 0;
++ u8 xoff_char = 0;
++ u16 rpn_mask = RFCOMM_RPN_PM_ALL;
++
++ BT_DBG("dlci %d cr %d len 0x%x bitr 0x%x line 0x%x flow 0x%x xonc 0x%x xoffc 0x%x pm 0x%x",
++ dlci, cr, len, rpn->bit_rate, rpn->line_settings, rpn->flow_ctrl,
++ rpn->xon_char, rpn->xoff_char, rpn->param_mask);
++
++ if (!cr)
++ return 0;
++
++ if (len == 1) {
++ /* request: return default setting */
++ bit_rate = RFCOMM_RPN_BR_115200;
++ data_bits = RFCOMM_RPN_DATA_8;
++ stop_bits = RFCOMM_RPN_STOP_1;
++ parity = RFCOMM_RPN_PARITY_NONE;
++ flow_ctrl = RFCOMM_RPN_FLOW_NONE;
++ xon_char = RFCOMM_RPN_XON_CHAR;
++ xoff_char = RFCOMM_RPN_XOFF_CHAR;
++
++ goto rpn_out;
++ }
++ /* check for sane values: ignore/accept bit_rate, 8 bits, 1 stop bit, no parity,
++ no flow control lines, normal XON/XOFF chars */
++ if (rpn->param_mask & RFCOMM_RPN_PM_BITRATE) {
++ bit_rate = rpn->bit_rate;
++ if (bit_rate != RFCOMM_RPN_BR_115200) {
++ BT_DBG("RPN bit rate mismatch 0x%x", bit_rate);
++ bit_rate = RFCOMM_RPN_BR_115200;
++ rpn_mask ^= RFCOMM_RPN_PM_BITRATE;
++ }
++ }
++ if (rpn->param_mask & RFCOMM_RPN_PM_DATA) {
++ data_bits = __get_rpn_data_bits(rpn->line_settings);
++ if (data_bits != RFCOMM_RPN_DATA_8) {
++ BT_DBG("RPN data bits mismatch 0x%x", data_bits);
++ data_bits = RFCOMM_RPN_DATA_8;
++ rpn_mask ^= RFCOMM_RPN_PM_DATA;
++ }
++ }
++ if (rpn->param_mask & RFCOMM_RPN_PM_STOP) {
++ stop_bits = __get_rpn_stop_bits(rpn->line_settings);
++ if (stop_bits != RFCOMM_RPN_STOP_1) {
++ BT_DBG("RPN stop bits mismatch 0x%x", stop_bits);
++ stop_bits = RFCOMM_RPN_STOP_1;
++ rpn_mask ^= RFCOMM_RPN_PM_STOP;
++ }
++ }
++ if (rpn->param_mask & RFCOMM_RPN_PM_PARITY) {
++ parity = __get_rpn_parity(rpn->line_settings);
++ if (parity != RFCOMM_RPN_PARITY_NONE) {
++ BT_DBG("RPN parity mismatch 0x%x", parity);
++ parity = RFCOMM_RPN_PARITY_NONE;
++ rpn_mask ^= RFCOMM_RPN_PM_PARITY;
++ }
++ }
++ if (rpn->param_mask & RFCOMM_RPN_PM_FLOW) {
++ flow_ctrl = rpn->flow_ctrl;
++ if (flow_ctrl != RFCOMM_RPN_FLOW_NONE) {
++ BT_DBG("RPN flow ctrl mismatch 0x%x", flow_ctrl);
++ flow_ctrl = RFCOMM_RPN_FLOW_NONE;
++ rpn_mask ^= RFCOMM_RPN_PM_FLOW;
++ }
++ }
++ if (rpn->param_mask & RFCOMM_RPN_PM_XON) {
++ xon_char = rpn->xon_char;
++ if (xon_char != RFCOMM_RPN_XON_CHAR) {
++ BT_DBG("RPN XON char mismatch 0x%x", xon_char);
++ xon_char = RFCOMM_RPN_XON_CHAR;
++ rpn_mask ^= RFCOMM_RPN_PM_XON;
++ }
++ }
++ if (rpn->param_mask & RFCOMM_RPN_PM_XOFF) {
++ xoff_char = rpn->xoff_char;
++ if (xoff_char != RFCOMM_RPN_XOFF_CHAR) {
++ BT_DBG("RPN XOFF char mismatch 0x%x", xoff_char);
++ xoff_char = RFCOMM_RPN_XOFF_CHAR;
++ rpn_mask ^= RFCOMM_RPN_PM_XOFF;
++ }
++ }
++
++rpn_out:
++ rfcomm_send_rpn(s, 0, dlci,
++ bit_rate, data_bits, stop_bits, parity, flow_ctrl,
++ xon_char, xoff_char, rpn_mask);
++
++ return 0;
++}
++
++static int rfcomm_recv_rls(struct rfcomm_session *s, int cr, struct sk_buff *skb)
++{
++ struct rfcomm_rls *rls = (void *) skb->data;
++ u8 dlci = __get_dlci(rls->dlci);
++
++ BT_DBG("dlci %d cr %d status 0x%x", dlci, cr, rls->status);
++
++ if (!cr)
++ return 0;
++
++ /* FIXME: We should probably do something with this
++ information here. But for now it's sufficient just
++ to reply -- Bluetooth 1.1 says it's mandatory to
++ recognise and respond to RLS */
++
++ rfcomm_send_rls(s, 0, dlci, rls->status);
++
++ return 0;
++}
++
++static int rfcomm_recv_msc(struct rfcomm_session *s, int cr, struct sk_buff *skb)
++{
++ struct rfcomm_msc *msc = (void *) skb->data;
++ struct rfcomm_dlc *d;
++ u8 dlci = __get_dlci(msc->dlci);
++
++ BT_DBG("dlci %d cr %d v24 0x%x", dlci, cr, msc->v24_sig);
++
++ d = rfcomm_dlc_get(s, dlci);
++ if (!d)
++ return 0;
++
++ if (cr) {
++ if (msc->v24_sig & RFCOMM_V24_FC && !d->cfc)
++ set_bit(RFCOMM_TX_THROTTLED, &d->flags);
++ else
++ clear_bit(RFCOMM_TX_THROTTLED, &d->flags);
++
++ rfcomm_dlc_lock(d);
++ if (d->modem_status)
++ d->modem_status(d, msc->v24_sig);
++ rfcomm_dlc_unlock(d);
++
++ rfcomm_send_msc(s, 0, dlci, msc->v24_sig);
++
++ d->mscex |= RFCOMM_MSCEX_RX;
++ } else
++ d->mscex |= RFCOMM_MSCEX_TX;
++
++ return 0;
++}
++
++static int rfcomm_recv_mcc(struct rfcomm_session *s, struct sk_buff *skb)
++{
++ struct rfcomm_mcc *mcc = (void *) skb->data;
++ u8 type, cr, len;
++
++ cr = __test_cr(mcc->type);
++ type = __get_mcc_type(mcc->type);
++ len = __get_mcc_len(mcc->len);
++
++ BT_DBG("%p type 0x%x cr %d", s, type, cr);
++
++ skb_pull(skb, 2);
++
++ switch (type) {
++ case RFCOMM_PN:
++ rfcomm_recv_pn(s, cr, skb);
++ break;
++
++ case RFCOMM_RPN:
++ rfcomm_recv_rpn(s, cr, len, skb);
++ break;
++
++ case RFCOMM_RLS:
++ rfcomm_recv_rls(s, cr, skb);
++ break;
++
++ case RFCOMM_MSC:
++ rfcomm_recv_msc(s, cr, skb);
++ break;
++
++ case RFCOMM_FCOFF:
++ if (cr) {
++ set_bit(RFCOMM_TX_THROTTLED, &s->flags);
++ rfcomm_send_fcoff(s, 0);
++ }
++ break;
++
++ case RFCOMM_FCON:
++ if (cr) {
++ clear_bit(RFCOMM_TX_THROTTLED, &s->flags);
++ rfcomm_send_fcon(s, 0);
++ }
++ break;
++
++ case RFCOMM_TEST:
++ if (cr)
++ rfcomm_send_test(s, 0, skb->data, skb->len);
++ break;
++
++ case RFCOMM_NSC:
++ break;
++
++ default:
++ BT_ERR("Unknown control type 0x%02x", type);
++ rfcomm_send_nsc(s, cr, type);
++ break;
++ }
++ return 0;
++}
++
++static int rfcomm_recv_data(struct rfcomm_session *s, u8 dlci, int pf, struct sk_buff *skb)
++{
++ struct rfcomm_dlc *d;
++
++ BT_DBG("session %p state %ld dlci %d pf %d", s, s->state, dlci, pf);
++
++ d = rfcomm_dlc_get(s, dlci);
++ if (!d) {
++ rfcomm_send_dm(s, dlci);
++ goto drop;
++ }
++
++ if (pf && d->cfc) {
++ u8 credits = *(u8 *) skb->data; skb_pull(skb, 1);
++
++ d->tx_credits += credits;
++ if (d->tx_credits)
++ clear_bit(RFCOMM_TX_THROTTLED, &d->flags);
++ }
++
++ if (skb->len && d->state == BT_CONNECTED) {
++ rfcomm_dlc_lock(d);
++ d->rx_credits--;
++ d->data_ready(d, skb);
++ rfcomm_dlc_unlock(d);
++ return 0;
++ }
++
++drop:
++ kfree_skb(skb);
++ return 0;
++}
++
++static int rfcomm_recv_frame(struct rfcomm_session *s, struct sk_buff *skb)
++{
++ struct rfcomm_hdr *hdr = (void *) skb->data;
++ u8 type, dlci, fcs;
++
++ dlci = __get_dlci(hdr->addr);
++ type = __get_type(hdr->ctrl);
++
++ /* Trim FCS */
++ skb->len--; skb->tail--;
++ fcs = *(u8 *) skb->tail;
++
++ if (__check_fcs(skb->data, type, fcs)) {
++ BT_ERR("bad checksum in packet");
++ kfree_skb(skb);
++ return -EILSEQ;
++ }
++
++ if (__test_ea(hdr->len))
++ skb_pull(skb, 3);
++ else
++ skb_pull(skb, 4);
++
++ switch (type) {
++ case RFCOMM_SABM:
++ if (__test_pf(hdr->ctrl))
++ rfcomm_recv_sabm(s, dlci);
++ break;
++
++ case RFCOMM_DISC:
++ if (__test_pf(hdr->ctrl))
++ rfcomm_recv_disc(s, dlci);
++ break;
++
++ case RFCOMM_UA:
++ if (__test_pf(hdr->ctrl))
++ rfcomm_recv_ua(s, dlci);
++ break;
++
++ case RFCOMM_DM:
++ rfcomm_recv_dm(s, dlci);
++ break;
++
++ case RFCOMM_UIH:
++ if (dlci)
++ return rfcomm_recv_data(s, dlci, __test_pf(hdr->ctrl), skb);
++
++ rfcomm_recv_mcc(s, skb);
++ break;
++
++ default:
++ BT_ERR("Unknown packet type 0x%02x\n", type);
++ break;
++ }
++ kfree_skb(skb);
++ return 0;
++}
++
++/* ---- Connection and data processing ---- */
++
++static void rfcomm_process_connect(struct rfcomm_session *s)
++{
++ struct rfcomm_dlc *d;
++ struct list_head *p, *n;
++
++ BT_DBG("session %p state %ld", s, s->state);
++
++ list_for_each_safe(p, n, &s->dlcs) {
++ d = list_entry(p, struct rfcomm_dlc, list);
++ if (d->state == BT_CONFIG) {
++ d->mtu = s->mtu;
++ rfcomm_send_pn(s, 1, d);
++ }
++ }
++}
++
++/* Send data queued for the DLC.
++ * Return number of frames left in the queue.
++ */
++static inline int rfcomm_process_tx(struct rfcomm_dlc *d)
++{
++ struct sk_buff *skb;
++ int err;
++
++ BT_DBG("dlc %p state %ld cfc %d rx_credits %d tx_credits %d",
++ d, d->state, d->cfc, d->rx_credits, d->tx_credits);
++
++ /* Send pending MSC */
++ if (test_and_clear_bit(RFCOMM_MSC_PENDING, &d->flags))
++ rfcomm_send_msc(d->session, 1, d->dlci, d->v24_sig);
++
++ if (d->cfc) {
++ /* CFC enabled.
++ * Give them some credits */
++ if (!test_bit(RFCOMM_RX_THROTTLED, &d->flags) &&
++ d->rx_credits <= (d->cfc >> 2)) {
++ rfcomm_send_credits(d->session, d->addr, d->cfc - d->rx_credits);
++ d->rx_credits = d->cfc;
++ }
++ } else {
++ /* CFC disabled.
++ * Give ourselves some credits */
++ d->tx_credits = 5;
++ }
++
++ if (test_bit(RFCOMM_TX_THROTTLED, &d->flags))
++ return skb_queue_len(&d->tx_queue);
++
++ while (d->tx_credits && (skb = skb_dequeue(&d->tx_queue))) {
++ err = rfcomm_send_frame(d->session, skb->data, skb->len);
++ if (err < 0) {
++ skb_queue_head(&d->tx_queue, skb);
++ break;
++ }
++ kfree_skb(skb);
++ d->tx_credits--;
++ }
++
++ if (d->cfc && !d->tx_credits) {
++ /* We're out of TX credits.
++ * Set TX_THROTTLED flag to avoid unnesary wakeups by dlc_send. */
++ set_bit(RFCOMM_TX_THROTTLED, &d->flags);
++ }
++
++ return skb_queue_len(&d->tx_queue);
++}
++
++static inline void rfcomm_process_dlcs(struct rfcomm_session *s)
++{
++ struct rfcomm_dlc *d;
++ struct list_head *p, *n;
++
++ BT_DBG("session %p state %ld", s, s->state);
++
++ list_for_each_safe(p, n, &s->dlcs) {
++ d = list_entry(p, struct rfcomm_dlc, list);
++ if (test_bit(RFCOMM_TIMED_OUT, &d->flags)) {
++ __rfcomm_dlc_close(d, ETIMEDOUT);
++ continue;
++ }
++
++ if (test_bit(RFCOMM_TX_THROTTLED, &s->flags))
++ continue;
++
++ if ((d->state == BT_CONNECTED || d->state == BT_DISCONN) &&
++ d->mscex == RFCOMM_MSCEX_OK)
++ rfcomm_process_tx(d);
++ }
++}
++
++static inline void rfcomm_process_rx(struct rfcomm_session *s)
++{
++ struct socket *sock = s->sock;
++ struct sock *sk = sock->sk;
++ struct sk_buff *skb;
++
++ BT_DBG("session %p state %ld qlen %d", s, s->state, skb_queue_len(&sk->receive_queue));
++
++ /* Get data directly from socket receive queue without copying it. */
++ while ((skb = skb_dequeue(&sk->receive_queue))) {
++ skb_orphan(skb);
++ rfcomm_recv_frame(s, skb);
++ }
++
++ if (sk->state == BT_CLOSED) {
++ if (!s->initiator)
++ rfcomm_session_put(s);
++
++ rfcomm_session_close(s, sk->err);
++ }
++}
++
++static inline void rfcomm_accept_connection(struct rfcomm_session *s)
++{
++ struct socket *sock = s->sock, *nsock;
++ int err;
++
++ /* Fast check for a new connection.
++ * Avoids unnesesary socket allocations. */
++ if (list_empty(&bluez_pi(sock->sk)->accept_q))
++ return;
++
++ BT_DBG("session %p", s);
++
++ nsock = sock_alloc();
++ if (!nsock)
++ return;
++
++ nsock->type = sock->type;
++ nsock->ops = sock->ops;
++
++ err = sock->ops->accept(sock, nsock, O_NONBLOCK);
++ if (err < 0) {
++ sock_release(nsock);
++ return;
++ }
++
++ /* Set our callbacks */
++ nsock->sk->data_ready = rfcomm_l2data_ready;
++ nsock->sk->state_change = rfcomm_l2state_change;
++
++ s = rfcomm_session_add(nsock, BT_OPEN);
++ if (s) {
++ rfcomm_session_hold(s);
++ rfcomm_schedule(RFCOMM_SCHED_RX);
++ } else
++ sock_release(nsock);
++}
++
++static inline void rfcomm_check_connection(struct rfcomm_session *s)
++{
++ struct sock *sk = s->sock->sk;
++
++ BT_DBG("%p state %ld", s, s->state);
++
++ switch(sk->state) {
++ case BT_CONNECTED:
++ s->state = BT_CONNECT;
++
++ /* We can adjust MTU on outgoing sessions.
++ * L2CAP MTU minus UIH header and FCS. */
++ s->mtu = min(l2cap_pi(sk)->omtu, l2cap_pi(sk)->imtu) - 5;
++
++ rfcomm_send_sabm(s, 0);
++ break;
++
++ case BT_CLOSED:
++ s->state = BT_CLOSED;
++ rfcomm_session_close(s, sk->err);
++ break;
++ }
++}
++
++static inline void rfcomm_process_sessions(void)
++{
++ struct list_head *p, *n;
++
++ rfcomm_lock();
++
++ list_for_each_safe(p, n, &session_list) {
++ struct rfcomm_session *s;
++ s = list_entry(p, struct rfcomm_session, list);
++
++ if (s->state == BT_LISTEN) {
++ rfcomm_accept_connection(s);
++ continue;
++ }
++
++ rfcomm_session_hold(s);
++
++ switch (s->state) {
++ case BT_BOUND:
++ rfcomm_check_connection(s);
++ break;
++
++ default:
++ rfcomm_process_rx(s);
++ break;
++ }
++
++ rfcomm_process_dlcs(s);
++
++ rfcomm_session_put(s);
++ }
++
++ rfcomm_unlock();
++}
++
++static void rfcomm_worker(void)
++{
++ BT_DBG("");
++
++ daemonize(); reparent_to_init();
++ set_fs(KERNEL_DS);
++
++ while (!atomic_read(&terminate)) {
++ BT_DBG("worker loop event 0x%lx", rfcomm_event);
++
++ if (!test_bit(RFCOMM_SCHED_WAKEUP, &rfcomm_event)) {
++ /* No pending events. Let's sleep.
++ * Incomming connections and data will wake us up. */
++ set_current_state(TASK_INTERRUPTIBLE);
++ schedule();
++ }
++
++ /* Process stuff */
++ clear_bit(RFCOMM_SCHED_WAKEUP, &rfcomm_event);
++ rfcomm_process_sessions();
++ }
++ set_current_state(TASK_RUNNING);
++ return;
++}
++
++static int rfcomm_add_listener(bdaddr_t *ba)
++{
++ struct sockaddr_l2 addr;
++ struct l2cap_options opts;
++ struct socket *sock;
++ struct rfcomm_session *s;
++ int size, err = 0;
++
++ /* Create socket */
++ err = rfcomm_l2sock_create(&sock);
++ if (err < 0) {
++ BT_ERR("Create socket failed %d", err);
++ return err;
++ }
++
++ /* Bind socket */
++ bacpy(&addr.l2_bdaddr, ba);
++ addr.l2_family = AF_BLUETOOTH;
++ addr.l2_psm = htobs(RFCOMM_PSM);
++ err = sock->ops->bind(sock, (struct sockaddr *) &addr, sizeof(addr));
++ if (err < 0) {
++ BT_ERR("Bind failed %d", err);
++ goto failed;
++ }
++
++ /* Set L2CAP options */
++ size = sizeof(opts);
++ sock->ops->getsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, (void *)&opts, &size);
++
++ opts.imtu = RFCOMM_MAX_L2CAP_MTU;
++ sock->ops->setsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, (void *)&opts, size);
++
++ /* Start listening on the socket */
++ err = sock->ops->listen(sock, 10);
++ if (err) {
++ BT_ERR("Listen failed %d", err);
++ goto failed;
++ }
++
++ /* Add listening session */
++ s = rfcomm_session_add(sock, BT_LISTEN);
++ if (!s)
++ goto failed;
++
++ rfcomm_session_hold(s);
++ return 0;
++failed:
++ sock_release(sock);
++ return err;
++}
++
++static void rfcomm_kill_listener(void)
++{
++ struct rfcomm_session *s;
++ struct list_head *p, *n;
++
++ BT_DBG("");
++
++ list_for_each_safe(p, n, &session_list) {
++ s = list_entry(p, struct rfcomm_session, list);
++ rfcomm_session_del(s);
++ }
++}
++
++static int rfcomm_run(void *unused)
++{
++ rfcomm_thread = current;
++
++ atomic_inc(&running);
++
++ daemonize(); reparent_to_init();
++
++ sigfillset(&current->blocked);
++ set_fs(KERNEL_DS);
++
++ sprintf(current->comm, "krfcommd");
++
++ BT_DBG("");
++
++ rfcomm_add_listener(BDADDR_ANY);
++
++ rfcomm_worker();
++
++ rfcomm_kill_listener();
++
++ atomic_dec(&running);
++ return 0;
++}
++
++/* ---- Proc fs support ---- */
++static int rfcomm_dlc_dump(char *buf)
++{
++ struct rfcomm_session *s;
++ struct sock *sk;
++ struct list_head *p, *pp;
++ char *ptr = buf;
++
++ rfcomm_lock();
++
++ list_for_each(p, &session_list) {
++ s = list_entry(p, struct rfcomm_session, list);
++ sk = s->sock->sk;
++
++ list_for_each(pp, &s->dlcs) {
++ struct rfcomm_dlc *d;
++ d = list_entry(pp, struct rfcomm_dlc, list);
++
++ ptr += sprintf(ptr, "dlc %s %s %ld %d %d %d %d\n",
++ batostr(&bluez_pi(sk)->src), batostr(&bluez_pi(sk)->dst),
++ d->state, d->dlci, d->mtu, d->rx_credits, d->tx_credits);
++ }
++ }
++
++ rfcomm_unlock();
++
++ return ptr - buf;
++}
++
++extern int rfcomm_sock_dump(char *buf);
++
++static int rfcomm_read_proc(char *buf, char **start, off_t offset, int count, int *eof, void *priv)
++{
++ char *ptr = buf;
++ int len;
++
++ BT_DBG("count %d, offset %ld", count, offset);
++
++ ptr += rfcomm_dlc_dump(ptr);
++ ptr += rfcomm_sock_dump(ptr);
++ len = ptr - buf;
++
++ if (len <= count + offset)
++ *eof = 1;
++
++ *start = buf + offset;
++ len -= offset;
++
++ if (len > count)
++ len = count;
++ if (len < 0)
++ len = 0;
++
++ return len;
++}
++
++/* ---- Initialization ---- */
++int __init rfcomm_init(void)
++{
++ l2cap_load();
++
++ kernel_thread(rfcomm_run, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
++
++ rfcomm_init_sockets();
++
++#ifdef CONFIG_BLUEZ_RFCOMM_TTY
++ rfcomm_init_ttys();
++#endif
++
++ create_proc_read_entry("bluetooth/rfcomm", 0, 0, rfcomm_read_proc, NULL);
++
++ BT_INFO("BlueZ RFCOMM ver %s", VERSION);
++ BT_INFO("Copyright (C) 2002 Maxim Krasnyansky <maxk@qualcomm.com>");
++ BT_INFO("Copyright (C) 2002 Marcel Holtmann <marcel@holtmann.org>");
++ return 0;
++}
++
++void rfcomm_cleanup(void)
++{
++ /* Terminate working thread.
++ * ie. Set terminate flag and wake it up */
++ atomic_inc(&terminate);
++ rfcomm_schedule(RFCOMM_SCHED_STATE);
++
++ /* Wait until thread is running */
++ while (atomic_read(&running))
++ schedule();
++
++ remove_proc_entry("bluetooth/rfcomm", NULL);
++
++#ifdef CONFIG_BLUEZ_RFCOMM_TTY
++ rfcomm_cleanup_ttys();
++#endif
++
++ rfcomm_cleanup_sockets();
++ return;
++}
++
++module_init(rfcomm_init);
++module_exit(rfcomm_cleanup);
++
++MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>, Marcel Holtmann <marcel@holtmann.org>");
++MODULE_DESCRIPTION("BlueZ RFCOMM ver " VERSION);
++MODULE_LICENSE("GPL");
+diff -urN linux-2.4.18/net/bluetooth/rfcomm/crc.c linux-2.4.18-mh15/net/bluetooth/rfcomm/crc.c
+--- linux-2.4.18/net/bluetooth/rfcomm/crc.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/net/bluetooth/rfcomm/crc.c 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,71 @@
++/*
++ RFCOMM implementation for Linux Bluetooth stack (BlueZ).
++ Copyright (C) 2002 Maxim Krasnyansky <maxk@qualcomm.com>
++ Copyright (C) 2002 Marcel Holtmann <marcel@holtmann.org>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License version 2 as
++ published by the Free Software Foundation;
++
++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++
++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
++ SOFTWARE IS DISCLAIMED.
++*/
++
++/*
++ * RFCOMM FCS calculation.
++ *
++ * $Id$
++ */
++
++/* reversed, 8-bit, poly=0x07 */
++unsigned char rfcomm_crc_table[256] = {
++ 0x00, 0x91, 0xe3, 0x72, 0x07, 0x96, 0xe4, 0x75,
++ 0x0e, 0x9f, 0xed, 0x7c, 0x09, 0x98, 0xea, 0x7b,
++ 0x1c, 0x8d, 0xff, 0x6e, 0x1b, 0x8a, 0xf8, 0x69,
++ 0x12, 0x83, 0xf1, 0x60, 0x15, 0x84, 0xf6, 0x67,
++
++ 0x38, 0xa9, 0xdb, 0x4a, 0x3f, 0xae, 0xdc, 0x4d,
++ 0x36, 0xa7, 0xd5, 0x44, 0x31, 0xa0, 0xd2, 0x43,
++ 0x24, 0xb5, 0xc7, 0x56, 0x23, 0xb2, 0xc0, 0x51,
++ 0x2a, 0xbb, 0xc9, 0x58, 0x2d, 0xbc, 0xce, 0x5f,
++
++ 0x70, 0xe1, 0x93, 0x02, 0x77, 0xe6, 0x94, 0x05,
++ 0x7e, 0xef, 0x9d, 0x0c, 0x79, 0xe8, 0x9a, 0x0b,
++ 0x6c, 0xfd, 0x8f, 0x1e, 0x6b, 0xfa, 0x88, 0x19,
++ 0x62, 0xf3, 0x81, 0x10, 0x65, 0xf4, 0x86, 0x17,
++
++ 0x48, 0xd9, 0xab, 0x3a, 0x4f, 0xde, 0xac, 0x3d,
++ 0x46, 0xd7, 0xa5, 0x34, 0x41, 0xd0, 0xa2, 0x33,
++ 0x54, 0xc5, 0xb7, 0x26, 0x53, 0xc2, 0xb0, 0x21,
++ 0x5a, 0xcb, 0xb9, 0x28, 0x5d, 0xcc, 0xbe, 0x2f,
++
++ 0xe0, 0x71, 0x03, 0x92, 0xe7, 0x76, 0x04, 0x95,
++ 0xee, 0x7f, 0x0d, 0x9c, 0xe9, 0x78, 0x0a, 0x9b,
++ 0xfc, 0x6d, 0x1f, 0x8e, 0xfb, 0x6a, 0x18, 0x89,
++ 0xf2, 0x63, 0x11, 0x80, 0xf5, 0x64, 0x16, 0x87,
++
++ 0xd8, 0x49, 0x3b, 0xaa, 0xdf, 0x4e, 0x3c, 0xad,
++ 0xd6, 0x47, 0x35, 0xa4, 0xd1, 0x40, 0x32, 0xa3,
++ 0xc4, 0x55, 0x27, 0xb6, 0xc3, 0x52, 0x20, 0xb1,
++ 0xca, 0x5b, 0x29, 0xb8, 0xcd, 0x5c, 0x2e, 0xbf,
++
++ 0x90, 0x01, 0x73, 0xe2, 0x97, 0x06, 0x74, 0xe5,
++ 0x9e, 0x0f, 0x7d, 0xec, 0x99, 0x08, 0x7a, 0xeb,
++ 0x8c, 0x1d, 0x6f, 0xfe, 0x8b, 0x1a, 0x68, 0xf9,
++ 0x82, 0x13, 0x61, 0xf0, 0x85, 0x14, 0x66, 0xf7,
++
++ 0xa8, 0x39, 0x4b, 0xda, 0xaf, 0x3e, 0x4c, 0xdd,
++ 0xa6, 0x37, 0x45, 0xd4, 0xa1, 0x30, 0x42, 0xd3,
++ 0xb4, 0x25, 0x57, 0xc6, 0xb3, 0x22, 0x50, 0xc1,
++ 0xba, 0x2b, 0x59, 0xc8, 0xbd, 0x2c, 0x5e, 0xcf
++};
+diff -urN linux-2.4.18/net/bluetooth/rfcomm/Makefile linux-2.4.18-mh15/net/bluetooth/rfcomm/Makefile
+--- linux-2.4.18/net/bluetooth/rfcomm/Makefile 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/net/bluetooth/rfcomm/Makefile 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,11 @@
++#
++# Makefile for the Linux Bluetooth RFCOMM layer
++#
++
++O_TARGET := rfcomm.o
++
++obj-y := core.o sock.o crc.o
++obj-$(CONFIG_BLUEZ_RFCOMM_TTY) += tty.o
++obj-m += $(O_TARGET)
++
++include $(TOPDIR)/Rules.make
+diff -urN linux-2.4.18/net/bluetooth/rfcomm/sock.c linux-2.4.18-mh15/net/bluetooth/rfcomm/sock.c
+--- linux-2.4.18/net/bluetooth/rfcomm/sock.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/net/bluetooth/rfcomm/sock.c 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,847 @@
++/*
++ RFCOMM implementation for Linux Bluetooth stack (BlueZ).
++ Copyright (C) 2002 Maxim Krasnyansky <maxk@qualcomm.com>
++ Copyright (C) 2002 Marcel Holtmann <marcel@holtmann.org>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License version 2 as
++ published by the Free Software Foundation;
++
++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++
++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
++ SOFTWARE IS DISCLAIMED.
++*/
++
++/*
++ * RFCOMM sockets.
++ *
++ * $Id$
++ */
++
++#include <linux/config.h>
++#include <linux/module.h>
++
++#include <linux/types.h>
++#include <linux/errno.h>
++#include <linux/kernel.h>
++#include <linux/major.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/poll.h>
++#include <linux/fcntl.h>
++#include <linux/init.h>
++#include <linux/skbuff.h>
++#include <linux/interrupt.h>
++#include <linux/socket.h>
++#include <linux/skbuff.h>
++#include <linux/list.h>
++#include <net/sock.h>
++
++#include <asm/system.h>
++#include <asm/uaccess.h>
++
++#include <net/bluetooth/bluetooth.h>
++#include <net/bluetooth/rfcomm.h>
++
++#ifndef CONFIG_BLUEZ_RFCOMM_DEBUG
++#undef BT_DBG
++#define BT_DBG(D...)
++#endif
++
++static struct proto_ops rfcomm_sock_ops;
++
++static struct bluez_sock_list rfcomm_sk_list = {
++ lock: RW_LOCK_UNLOCKED
++};
++
++static void rfcomm_sock_close(struct sock *sk);
++static void rfcomm_sock_kill(struct sock *sk);
++
++/* ---- DLC callbacks ----
++ *
++ * called under rfcomm_dlc_lock()
++ */
++static void rfcomm_sk_data_ready(struct rfcomm_dlc *d, struct sk_buff *skb)
++{
++ struct sock *sk = d->owner;
++ if (!sk)
++ return;
++
++ atomic_add(skb->len, &sk->rmem_alloc);
++ skb_queue_tail(&sk->receive_queue, skb);
++ sk->data_ready(sk, skb->len);
++
++ if (atomic_read(&sk->rmem_alloc) >= sk->rcvbuf)
++ rfcomm_dlc_throttle(d);
++}
++
++static void rfcomm_sk_state_change(struct rfcomm_dlc *d, int err)
++{
++ struct sock *sk = d->owner, *parent;
++ if (!sk)
++ return;
++
++ BT_DBG("dlc %p state %ld err %d", d, d->state, err);
++
++ bh_lock_sock(sk);
++
++ if (err)
++ sk->err = err;
++ sk->state = d->state;
++
++ parent = bluez_pi(sk)->parent;
++ if (!parent) {
++ if (d->state == BT_CONNECTED)
++ rfcomm_session_getaddr(d->session, &bluez_pi(sk)->src, NULL);
++ sk->state_change(sk);
++ } else
++ parent->data_ready(parent, 0);
++
++ bh_unlock_sock(sk);
++}
++
++/* ---- Socket functions ---- */
++static struct sock *__rfcomm_get_sock_by_addr(u8 channel, bdaddr_t *src)
++{
++ struct sock *sk;
++
++ for (sk = rfcomm_sk_list.head; sk; sk = sk->next) {
++ if (rfcomm_pi(sk)->channel == channel &&
++ !bacmp(&bluez_pi(sk)->src, src))
++ break;
++ }
++
++ return sk;
++}
++
++/* Find socket with channel and source bdaddr.
++ * Returns closest match.
++ */
++static struct sock *__rfcomm_get_sock_by_channel(int state, u8 channel, bdaddr_t *src)
++{
++ struct sock *sk, *sk1 = NULL;
++
++ for (sk = rfcomm_sk_list.head; sk; sk = sk->next) {
++ if (state && sk->state != state)
++ continue;
++
++ if (rfcomm_pi(sk)->channel == channel) {
++ /* Exact match. */
++ if (!bacmp(&bluez_pi(sk)->src, src))
++ break;
++
++ /* Closest match */
++ if (!bacmp(&bluez_pi(sk)->src, BDADDR_ANY))
++ sk1 = sk;
++ }
++ }
++ return sk ? sk : sk1;
++}
++
++/* Find socket with given address (channel, src).
++ * Returns locked socket */
++static inline struct sock *rfcomm_get_sock_by_channel(int state, u8 channel, bdaddr_t *src)
++{
++ struct sock *s;
++ read_lock(&rfcomm_sk_list.lock);
++ s = __rfcomm_get_sock_by_channel(state, channel, src);
++ if (s) bh_lock_sock(s);
++ read_unlock(&rfcomm_sk_list.lock);
++ return s;
++}
++
++static void rfcomm_sock_destruct(struct sock *sk)
++{
++ struct rfcomm_dlc *d = rfcomm_pi(sk)->dlc;
++
++ BT_DBG("sk %p dlc %p", sk, d);
++
++ skb_queue_purge(&sk->receive_queue);
++ skb_queue_purge(&sk->write_queue);
++
++ rfcomm_dlc_lock(d);
++ rfcomm_pi(sk)->dlc = NULL;
++
++ /* Detach DLC if it's owned by this socket */
++ if (d->owner == sk)
++ d->owner = NULL;
++ rfcomm_dlc_unlock(d);
++
++ rfcomm_dlc_put(d);
++
++ MOD_DEC_USE_COUNT;
++}
++
++static void rfcomm_sock_cleanup_listen(struct sock *parent)
++{
++ struct sock *sk;
++
++ BT_DBG("parent %p", parent);
++
++ /* Close not yet accepted dlcs */
++ while ((sk = bluez_accept_dequeue(parent, NULL))) {
++ rfcomm_sock_close(sk);
++ rfcomm_sock_kill(sk);
++ }
++
++ parent->state = BT_CLOSED;
++ parent->zapped = 1;
++}
++
++/* Kill socket (only if zapped and orphan)
++ * Must be called on unlocked socket.
++ */
++static void rfcomm_sock_kill(struct sock *sk)
++{
++ if (!sk->zapped || sk->socket)
++ return;
++
++ BT_DBG("sk %p state %d refcnt %d", sk, sk->state, atomic_read(&sk->refcnt));
++
++ /* Kill poor orphan */
++ bluez_sock_unlink(&rfcomm_sk_list, sk);
++ sk->dead = 1;
++ sock_put(sk);
++}
++
++static void __rfcomm_sock_close(struct sock *sk)
++{
++ struct rfcomm_dlc *d = rfcomm_pi(sk)->dlc;
++
++ BT_DBG("sk %p state %d socket %p", sk, sk->state, sk->socket);
++
++ switch (sk->state) {
++ case BT_LISTEN:
++ rfcomm_sock_cleanup_listen(sk);
++ break;
++
++ case BT_CONNECT:
++ case BT_CONNECT2:
++ case BT_CONFIG:
++ case BT_CONNECTED:
++ rfcomm_dlc_close(d, 0);
++
++ default:
++ sk->zapped = 1;
++ break;
++ }
++}
++
++/* Close socket.
++ * Must be called on unlocked socket.
++ */
++static void rfcomm_sock_close(struct sock *sk)
++{
++ lock_sock(sk);
++ __rfcomm_sock_close(sk);
++ release_sock(sk);
++}
++
++static void rfcomm_sock_init(struct sock *sk, struct sock *parent)
++{
++ BT_DBG("sk %p", sk);
++
++ if (parent)
++ sk->type = parent->type;
++}
++
++static struct sock *rfcomm_sock_alloc(struct socket *sock, int proto, int prio)
++{
++ struct rfcomm_dlc *d;
++ struct sock *sk;
++
++ sk = sk_alloc(PF_BLUETOOTH, prio, 1);
++ if (!sk)
++ return NULL;
++
++ d = rfcomm_dlc_alloc(prio);
++ if (!d) {
++ sk_free(sk);
++ return NULL;
++ }
++ d->data_ready = rfcomm_sk_data_ready;
++ d->state_change = rfcomm_sk_state_change;
++
++ rfcomm_pi(sk)->dlc = d;
++ d->owner = sk;
++
++ bluez_sock_init(sock, sk);
++
++ sk->zapped = 0;
++
++ sk->destruct = rfcomm_sock_destruct;
++ sk->sndtimeo = RFCOMM_CONN_TIMEOUT;
++
++ sk->sndbuf = RFCOMM_MAX_CREDITS * RFCOMM_DEFAULT_MTU * 10;
++ sk->rcvbuf = RFCOMM_MAX_CREDITS * RFCOMM_DEFAULT_MTU * 10;
++
++ sk->protocol = proto;
++ sk->state = BT_OPEN;
++
++ bluez_sock_link(&rfcomm_sk_list, sk);
++
++ BT_DBG("sk %p", sk);
++
++ MOD_INC_USE_COUNT;
++ return sk;
++}
++
++static int rfcomm_sock_create(struct socket *sock, int protocol)
++{
++ struct sock *sk;
++
++ BT_DBG("sock %p", sock);
++
++ sock->state = SS_UNCONNECTED;
++
++ if (sock->type != SOCK_STREAM && sock->type != SOCK_RAW)
++ return -ESOCKTNOSUPPORT;
++
++ sock->ops = &rfcomm_sock_ops;
++
++ if (!(sk = rfcomm_sock_alloc(sock, protocol, GFP_KERNEL)))
++ return -ENOMEM;
++
++ rfcomm_sock_init(sk, NULL);
++ return 0;
++}
++
++static int rfcomm_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len)
++{
++ struct sockaddr_rc *sa = (struct sockaddr_rc *) addr;
++ struct sock *sk = sock->sk;
++ int err = 0;
++
++ BT_DBG("sk %p %s", sk, batostr(&sa->rc_bdaddr));
++
++ if (!addr || addr->sa_family != AF_BLUETOOTH)
++ return -EINVAL;
++
++ lock_sock(sk);
++
++ if (sk->state != BT_OPEN) {
++ err = -EBADFD;
++ goto done;
++ }
++
++ write_lock_bh(&rfcomm_sk_list.lock);
++
++ if (sa->rc_channel && __rfcomm_get_sock_by_addr(sa->rc_channel, &sa->rc_bdaddr)) {
++ err = -EADDRINUSE;
++ } else {
++ /* Save source address */
++ bacpy(&bluez_pi(sk)->src, &sa->rc_bdaddr);
++ rfcomm_pi(sk)->channel = sa->rc_channel;
++ sk->state = BT_BOUND;
++ }
++
++ write_unlock_bh(&rfcomm_sk_list.lock);
++
++done:
++ release_sock(sk);
++ return err;
++}
++
++static int rfcomm_sock_connect(struct socket *sock, struct sockaddr *addr, int alen, int flags)
++{
++ struct sockaddr_rc *sa = (struct sockaddr_rc *) addr;
++ struct sock *sk = sock->sk;
++ struct rfcomm_dlc *d = rfcomm_pi(sk)->dlc;
++ int err = 0;
++
++ BT_DBG("sk %p", sk);
++
++ if (addr->sa_family != AF_BLUETOOTH || alen < sizeof(struct sockaddr_rc))
++ return -EINVAL;
++
++ if (sk->state != BT_OPEN && sk->state != BT_BOUND)
++ return -EBADFD;
++
++ if (sk->type != SOCK_STREAM)
++ return -EINVAL;
++
++ lock_sock(sk);
++
++ sk->state = BT_CONNECT;
++ bacpy(&bluez_pi(sk)->dst, &sa->rc_bdaddr);
++ rfcomm_pi(sk)->channel = sa->rc_channel;
++
++ err = rfcomm_dlc_open(d, &bluez_pi(sk)->src, &sa->rc_bdaddr, sa->rc_channel);
++ if (!err)
++ err = bluez_sock_wait_state(sk, BT_CONNECTED,
++ sock_sndtimeo(sk, flags & O_NONBLOCK));
++
++ release_sock(sk);
++ return err;
++}
++
++int rfcomm_sock_listen(struct socket *sock, int backlog)
++{
++ struct sock *sk = sock->sk;
++ int err = 0;
++
++ BT_DBG("sk %p backlog %d", sk, backlog);
++
++ lock_sock(sk);
++
++ if (sk->state != BT_BOUND) {
++ err = -EBADFD;
++ goto done;
++ }
++
++ sk->max_ack_backlog = backlog;
++ sk->ack_backlog = 0;
++ sk->state = BT_LISTEN;
++
++done:
++ release_sock(sk);
++ return err;
++}
++
++int rfcomm_sock_accept(struct socket *sock, struct socket *newsock, int flags)
++{
++ DECLARE_WAITQUEUE(wait, current);
++ struct sock *sk = sock->sk, *nsk;
++ long timeo;
++ int err = 0;
++
++ lock_sock(sk);
++
++ if (sk->state != BT_LISTEN) {
++ err = -EBADFD;
++ goto done;
++ }
++
++ timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK);
++
++ BT_DBG("sk %p timeo %ld", sk, timeo);
++
++ /* Wait for an incoming connection. (wake-one). */
++ add_wait_queue_exclusive(sk->sleep, &wait);
++ while (!(nsk = bluez_accept_dequeue(sk, newsock))) {
++ set_current_state(TASK_INTERRUPTIBLE);
++ if (!timeo) {
++ err = -EAGAIN;
++ break;
++ }
++
++ release_sock(sk);
++ timeo = schedule_timeout(timeo);
++ lock_sock(sk);
++
++ if (sk->state != BT_LISTEN) {
++ err = -EBADFD;
++ break;
++ }
++
++ if (signal_pending(current)) {
++ err = sock_intr_errno(timeo);
++ break;
++ }
++ }
++ set_current_state(TASK_RUNNING);
++ remove_wait_queue(sk->sleep, &wait);
++
++ if (err)
++ goto done;
++
++ newsock->state = SS_CONNECTED;
++
++ BT_DBG("new socket %p", nsk);
++
++done:
++ release_sock(sk);
++ return err;
++}
++
++static int rfcomm_sock_getname(struct socket *sock, struct sockaddr *addr, int *len, int peer)
++{
++ struct sockaddr_rc *sa = (struct sockaddr_rc *) addr;
++ struct sock *sk = sock->sk;
++
++ BT_DBG("sock %p, sk %p", sock, sk);
++
++ sa->rc_family = AF_BLUETOOTH;
++ sa->rc_channel = rfcomm_pi(sk)->channel;
++ if (peer)
++ bacpy(&sa->rc_bdaddr, &bluez_pi(sk)->dst);
++ else
++ bacpy(&sa->rc_bdaddr, &bluez_pi(sk)->src);
++
++ *len = sizeof(struct sockaddr_rc);
++ return 0;
++}
++
++static int rfcomm_sock_sendmsg(struct socket *sock, struct msghdr *msg, int len,
++ struct scm_cookie *scm)
++{
++ struct sock *sk = sock->sk;
++ struct rfcomm_dlc *d = rfcomm_pi(sk)->dlc;
++ struct sk_buff *skb;
++ int err, size;
++ int sent = 0;
++
++ if (msg->msg_flags & MSG_OOB)
++ return -EOPNOTSUPP;
++
++ if (sk->shutdown & SEND_SHUTDOWN)
++ return -EPIPE;
++
++ BT_DBG("sock %p, sk %p", sock, sk);
++
++ lock_sock(sk);
++
++ while (len) {
++ size = min_t(uint, len, d->mtu);
++
++ skb = sock_alloc_send_skb(sk, size + RFCOMM_SKB_RESERVE,
++ msg->msg_flags & MSG_DONTWAIT, &err);
++ if (!skb)
++ break;
++ skb_reserve(skb, RFCOMM_SKB_HEAD_RESERVE);
++
++ err = memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size);
++ if (err) {
++ kfree_skb(skb);
++ sent = err;
++ break;
++ }
++
++ err = rfcomm_dlc_send(d, skb);
++ if (err < 0) {
++ kfree_skb(skb);
++ break;
++ }
++
++ sent += size;
++ len -= size;
++ }
++
++ release_sock(sk);
++
++ return sent ? sent : err;
++}
++
++static long rfcomm_sock_data_wait(struct sock *sk, long timeo)
++{
++ DECLARE_WAITQUEUE(wait, current);
++
++ add_wait_queue(sk->sleep, &wait);
++ for (;;) {
++ set_current_state(TASK_INTERRUPTIBLE);
++
++ if (skb_queue_len(&sk->receive_queue) || sk->err || (sk->shutdown & RCV_SHUTDOWN) ||
++ signal_pending(current) || !timeo)
++ break;
++
++ set_bit(SOCK_ASYNC_WAITDATA, &sk->socket->flags);
++ release_sock(sk);
++ timeo = schedule_timeout(timeo);
++ lock_sock(sk);
++ clear_bit(SOCK_ASYNC_WAITDATA, &sk->socket->flags);
++ }
++
++ __set_current_state(TASK_RUNNING);
++ remove_wait_queue(sk->sleep, &wait);
++ return timeo;
++}
++
++static int rfcomm_sock_recvmsg(struct socket *sock, struct msghdr *msg, int size,
++ int flags, struct scm_cookie *scm)
++{
++ struct sock *sk = sock->sk;
++ int target, err = 0, copied = 0;
++ long timeo;
++
++ if (flags & MSG_OOB)
++ return -EOPNOTSUPP;
++
++ msg->msg_namelen = 0;
++
++ BT_DBG("sk %p size %d", sk, size);
++
++ lock_sock(sk);
++
++ target = sock_rcvlowat(sk, flags & MSG_WAITALL, size);
++ timeo = sock_rcvtimeo(sk, flags & MSG_DONTWAIT);
++
++ do {
++ struct sk_buff *skb;
++ int chunk;
++
++ skb = skb_dequeue(&sk->receive_queue);
++ if (!skb) {
++ if (copied >= target)
++ break;
++
++ if ((err = sock_error(sk)) != 0)
++ break;
++ if (sk->shutdown & RCV_SHUTDOWN)
++ break;
++
++ err = -EAGAIN;
++ if (!timeo)
++ break;
++
++ timeo = rfcomm_sock_data_wait(sk, timeo);
++
++ if (signal_pending(current)) {
++ err = sock_intr_errno(timeo);
++ goto out;
++ }
++ continue;
++ }
++
++ chunk = min_t(unsigned int, skb->len, size);
++ if (memcpy_toiovec(msg->msg_iov, skb->data, chunk)) {
++ skb_queue_head(&sk->receive_queue, skb);
++ if (!copied)
++ copied = -EFAULT;
++ break;
++ }
++ copied += chunk;
++ size -= chunk;
++
++ if (!(flags & MSG_PEEK)) {
++ atomic_sub(chunk, &sk->rmem_alloc);
++
++ skb_pull(skb, chunk);
++ if (skb->len) {
++ skb_queue_head(&sk->receive_queue, skb);
++ break;
++ }
++ kfree_skb(skb);
++
++ } else {
++ /* put message back and return */
++ skb_queue_head(&sk->receive_queue, skb);
++ break;
++ }
++ } while (size);
++
++out:
++ if (atomic_read(&sk->rmem_alloc) <= (sk->rcvbuf >> 2))
++ rfcomm_dlc_unthrottle(rfcomm_pi(sk)->dlc);
++
++ release_sock(sk);
++ return copied ? : err;
++}
++
++static int rfcomm_sock_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen)
++{
++ struct sock *sk = sock->sk;
++ int err = 0;
++
++ BT_DBG("sk %p", sk);
++
++ lock_sock(sk);
++
++ switch (optname) {
++ default:
++ err = -ENOPROTOOPT;
++ break;
++ };
++
++ release_sock(sk);
++ return err;
++}
++
++static int rfcomm_sock_getsockopt(struct socket *sock, int level, int optname, char *optval, int *optlen)
++{
++ struct sock *sk = sock->sk;
++ int len, err = 0;
++
++ BT_DBG("sk %p", sk);
++
++ if (get_user(len, optlen))
++ return -EFAULT;
++
++ lock_sock(sk);
++
++ switch (optname) {
++ default:
++ err = -ENOPROTOOPT;
++ break;
++ };
++
++ release_sock(sk);
++ return err;
++}
++
++static int rfcomm_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
++{
++ struct sock *sk = sock->sk;
++ int err;
++
++ lock_sock(sk);
++
++#ifdef CONFIG_BLUEZ_RFCOMM_TTY
++ err = rfcomm_dev_ioctl(sk, cmd, arg);
++#else
++ err = -EOPNOTSUPP;
++#endif
++
++ release_sock(sk);
++
++ return err;
++}
++
++static int rfcomm_sock_shutdown(struct socket *sock, int how)
++{
++ struct sock *sk = sock->sk;
++ int err = 0;
++
++ BT_DBG("sock %p, sk %p", sock, sk);
++
++ if (!sk) return 0;
++
++ lock_sock(sk);
++ if (!sk->shutdown) {
++ sk->shutdown = SHUTDOWN_MASK;
++ __rfcomm_sock_close(sk);
++
++ if (sk->linger)
++ err = bluez_sock_wait_state(sk, BT_CLOSED, sk->lingertime);
++ }
++ release_sock(sk);
++ return err;
++}
++
++static int rfcomm_sock_release(struct socket *sock)
++{
++ struct sock *sk = sock->sk;
++ int err = 0;
++
++ BT_DBG("sock %p, sk %p", sock, sk);
++
++ if (!sk)
++ return 0;
++
++ err = rfcomm_sock_shutdown(sock, 2);
++
++ sock_orphan(sk);
++ rfcomm_sock_kill(sk);
++ return err;
++}
++
++/* ---- RFCOMM core layer callbacks ----
++ *
++ * called under rfcomm_lock()
++ */
++int rfcomm_connect_ind(struct rfcomm_session *s, u8 channel, struct rfcomm_dlc **d)
++{
++ struct sock *sk, *parent;
++ bdaddr_t src, dst;
++ int result = 0;
++
++ BT_DBG("session %p channel %d", s, channel);
++
++ rfcomm_session_getaddr(s, &src, &dst);
++
++ /* Check if we have socket listening on this channel */
++ parent = rfcomm_get_sock_by_channel(BT_LISTEN, channel, &src);
++ if (!parent)
++ return 0;
++
++ /* Check for backlog size */
++ if (parent->ack_backlog > parent->max_ack_backlog) {
++ BT_DBG("backlog full %d", parent->ack_backlog);
++ goto done;
++ }
++
++ sk = rfcomm_sock_alloc(NULL, BTPROTO_RFCOMM, GFP_ATOMIC);
++ if (!sk)
++ goto done;
++
++ rfcomm_sock_init(sk, parent);
++ bacpy(&bluez_pi(sk)->src, &src);
++ bacpy(&bluez_pi(sk)->dst, &dst);
++ rfcomm_pi(sk)->channel = channel;
++
++ sk->state = BT_CONFIG;
++ bluez_accept_enqueue(parent, sk);
++
++ /* Accept connection and return socket DLC */
++ *d = rfcomm_pi(sk)->dlc;
++ result = 1;
++
++done:
++ bh_unlock_sock(parent);
++ return result;
++}
++
++/* ---- Proc fs support ---- */
++int rfcomm_sock_dump(char *buf)
++{
++ struct bluez_sock_list *list = &rfcomm_sk_list;
++ struct rfcomm_pinfo *pi;
++ struct sock *sk;
++ char *ptr = buf;
++
++ write_lock_bh(&list->lock);
++
++ for (sk = list->head; sk; sk = sk->next) {
++ pi = rfcomm_pi(sk);
++ ptr += sprintf(ptr, "sk %s %s %d %d\n",
++ batostr(&bluez_pi(sk)->src), batostr(&bluez_pi(sk)->dst),
++ sk->state, rfcomm_pi(sk)->channel);
++ }
++
++ write_unlock_bh(&list->lock);
++
++ return ptr - buf;
++}
++
++static struct proto_ops rfcomm_sock_ops = {
++ family: PF_BLUETOOTH,
++ release: rfcomm_sock_release,
++ bind: rfcomm_sock_bind,
++ connect: rfcomm_sock_connect,
++ listen: rfcomm_sock_listen,
++ accept: rfcomm_sock_accept,
++ getname: rfcomm_sock_getname,
++ sendmsg: rfcomm_sock_sendmsg,
++ recvmsg: rfcomm_sock_recvmsg,
++ shutdown: rfcomm_sock_shutdown,
++ setsockopt: rfcomm_sock_setsockopt,
++ getsockopt: rfcomm_sock_getsockopt,
++ ioctl: rfcomm_sock_ioctl,
++ poll: bluez_sock_poll,
++ socketpair: sock_no_socketpair,
++ mmap: sock_no_mmap
++};
++
++static struct net_proto_family rfcomm_sock_family_ops = {
++ family: PF_BLUETOOTH,
++ create: rfcomm_sock_create
++};
++
++int rfcomm_init_sockets(void)
++{
++ int err;
++
++ if ((err = bluez_sock_register(BTPROTO_RFCOMM, &rfcomm_sock_family_ops))) {
++ BT_ERR("Can't register RFCOMM socket layer");
++ return err;
++ }
++
++ return 0;
++}
++
++void rfcomm_cleanup_sockets(void)
++{
++ int err;
++
++ /* Unregister socket, protocol and notifier */
++ if ((err = bluez_sock_unregister(BTPROTO_RFCOMM)))
++ BT_ERR("Can't unregister RFCOMM socket layer %d", err);
++}
+diff -urN linux-2.4.18/net/bluetooth/rfcomm/tty.c linux-2.4.18-mh15/net/bluetooth/rfcomm/tty.c
+--- linux-2.4.18/net/bluetooth/rfcomm/tty.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/net/bluetooth/rfcomm/tty.c 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,960 @@
++/*
++ RFCOMM implementation for Linux Bluetooth stack (BlueZ).
++ Copyright (C) 2002 Maxim Krasnyansky <maxk@qualcomm.com>
++ Copyright (C) 2002 Marcel Holtmann <marcel@holtmann.org>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License version 2 as
++ published by the Free Software Foundation;
++
++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++
++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
++ SOFTWARE IS DISCLAIMED.
++*/
++
++/*
++ * RFCOMM TTY.
++ *
++ * $Id$
++ */
++
++#include <linux/config.h>
++#include <linux/module.h>
++
++#include <linux/tty.h>
++#include <linux/tty_driver.h>
++#include <linux/tty_flip.h>
++
++#include <linux/slab.h>
++#include <linux/skbuff.h>
++
++#include <net/bluetooth/bluetooth.h>
++#include <net/bluetooth/rfcomm.h>
++
++#ifndef CONFIG_BLUEZ_RFCOMM_DEBUG
++#undef BT_DBG
++#define BT_DBG(D...)
++#endif
++
++#define RFCOMM_TTY_MAGIC 0x6d02 /* magic number for rfcomm struct */
++#define RFCOMM_TTY_PORTS RFCOMM_MAX_DEV /* whole lotta rfcomm devices */
++#define RFCOMM_TTY_MAJOR 216 /* device node major id of the usb/bluetooth.c driver */
++#define RFCOMM_TTY_MINOR 0
++
++struct rfcomm_dev {
++ struct list_head list;
++ atomic_t refcnt;
++
++ char name[12];
++ int id;
++ unsigned long flags;
++ int opened;
++ int err;
++
++ bdaddr_t src;
++ bdaddr_t dst;
++ u8 channel;
++
++ uint modem_status;
++
++ struct rfcomm_dlc *dlc;
++ struct tty_struct *tty;
++ wait_queue_head_t wait;
++ struct tasklet_struct wakeup_task;
++
++ atomic_t wmem_alloc;
++};
++
++static LIST_HEAD(rfcomm_dev_list);
++static rwlock_t rfcomm_dev_lock = RW_LOCK_UNLOCKED;
++
++static void rfcomm_dev_data_ready(struct rfcomm_dlc *dlc, struct sk_buff *skb);
++static void rfcomm_dev_state_change(struct rfcomm_dlc *dlc, int err);
++static void rfcomm_dev_modem_status(struct rfcomm_dlc *dlc, u8 v24_sig);
++
++static void rfcomm_tty_wakeup(unsigned long arg);
++
++/* ---- Device functions ---- */
++static void rfcomm_dev_destruct(struct rfcomm_dev *dev)
++{
++ struct rfcomm_dlc *dlc = dev->dlc;
++
++ BT_DBG("dev %p dlc %p", dev, dlc);
++
++ rfcomm_dlc_lock(dlc);
++ /* Detach DLC if it's owned by this dev */
++ if (dlc->owner == dev)
++ dlc->owner = NULL;
++ rfcomm_dlc_unlock(dlc);
++
++ rfcomm_dlc_put(dlc);
++ kfree(dev);
++
++ MOD_DEC_USE_COUNT;
++}
++
++static inline void rfcomm_dev_hold(struct rfcomm_dev *dev)
++{
++ atomic_inc(&dev->refcnt);
++}
++
++static inline void rfcomm_dev_put(struct rfcomm_dev *dev)
++{
++ /* The reason this isn't actually a race, as you no
++ doubt have a little voice screaming at you in your
++ head, is that the refcount should never actually
++ reach zero unless the device has already been taken
++ off the list, in rfcomm_dev_del(). And if that's not
++ true, we'll hit the BUG() in rfcomm_dev_destruct()
++ anyway. */
++ if (atomic_dec_and_test(&dev->refcnt))
++ rfcomm_dev_destruct(dev);
++}
++
++static struct rfcomm_dev *__rfcomm_dev_get(int id)
++{
++ struct rfcomm_dev *dev;
++ struct list_head *p;
++
++ list_for_each(p, &rfcomm_dev_list) {
++ dev = list_entry(p, struct rfcomm_dev, list);
++ if (dev->id == id)
++ return dev;
++ }
++
++ return NULL;
++}
++
++static inline struct rfcomm_dev *rfcomm_dev_get(int id)
++{
++ struct rfcomm_dev *dev;
++
++ read_lock(&rfcomm_dev_lock);
++
++ dev = __rfcomm_dev_get(id);
++ if (dev)
++ rfcomm_dev_hold(dev);
++
++ read_unlock(&rfcomm_dev_lock);
++
++ return dev;
++}
++
++static int rfcomm_dev_add(struct rfcomm_dev_req *req, struct rfcomm_dlc *dlc)
++{
++ struct rfcomm_dev *dev;
++ struct list_head *head = &rfcomm_dev_list, *p;
++ int err = 0;
++
++ BT_DBG("id %d channel %d", req->dev_id, req->channel);
++
++ dev = kmalloc(sizeof(struct rfcomm_dev), GFP_KERNEL);
++ if (!dev)
++ return -ENOMEM;
++ memset(dev, 0, sizeof(struct rfcomm_dev));
++
++ write_lock_bh(&rfcomm_dev_lock);
++
++ if (req->dev_id < 0) {
++ dev->id = 0;
++
++ list_for_each(p, &rfcomm_dev_list) {
++ if (list_entry(p, struct rfcomm_dev, list)->id != dev->id)
++ break;
++
++ dev->id++;
++ head = p;
++ }
++ } else {
++ dev->id = req->dev_id;
++
++ list_for_each(p, &rfcomm_dev_list) {
++ struct rfcomm_dev *entry = list_entry(p, struct rfcomm_dev, list);
++
++ if (entry->id == dev->id) {
++ err = -EADDRINUSE;
++ goto out;
++ }
++
++ if (entry->id > dev->id - 1)
++ break;
++
++ head = p;
++ }
++ }
++
++ if ((dev->id < 0) || (dev->id > RFCOMM_MAX_DEV - 1)) {
++ err = -ENFILE;
++ goto out;
++ }
++
++ sprintf(dev->name, "rfcomm%d", dev->id);
++
++ list_add(&dev->list, head);
++ atomic_set(&dev->refcnt, 1);
++
++ bacpy(&dev->src, &req->src);
++ bacpy(&dev->dst, &req->dst);
++ dev->channel = req->channel;
++
++ dev->flags = req->flags &
++ ((1 << RFCOMM_RELEASE_ONHUP) | (1 << RFCOMM_REUSE_DLC));
++
++ init_waitqueue_head(&dev->wait);
++ tasklet_init(&dev->wakeup_task, rfcomm_tty_wakeup, (unsigned long) dev);
++
++ rfcomm_dlc_lock(dlc);
++ dlc->data_ready = rfcomm_dev_data_ready;
++ dlc->state_change = rfcomm_dev_state_change;
++ dlc->modem_status = rfcomm_dev_modem_status;
++
++ dlc->owner = dev;
++ dev->dlc = dlc;
++ rfcomm_dlc_unlock(dlc);
++
++ MOD_INC_USE_COUNT;
++
++out:
++ write_unlock_bh(&rfcomm_dev_lock);
++
++ if (err) {
++ kfree(dev);
++ return err;
++ } else
++ return dev->id;
++}
++
++static void rfcomm_dev_del(struct rfcomm_dev *dev)
++{
++ BT_DBG("dev %p", dev);
++
++ write_lock_bh(&rfcomm_dev_lock);
++ list_del_init(&dev->list);
++ write_unlock_bh(&rfcomm_dev_lock);
++
++ rfcomm_dev_put(dev);
++}
++
++/* ---- Send buffer ---- */
++
++static inline unsigned int rfcomm_room(struct rfcomm_dlc *dlc)
++{
++ /* We can't let it be zero, because we don't get a callback
++ when tx_credits becomes nonzero, hence we'd never wake up */
++ return dlc->mtu * (dlc->tx_credits?:1);
++}
++
++static void rfcomm_wfree(struct sk_buff *skb)
++{
++ struct rfcomm_dev *dev = (void *) skb->sk;
++ atomic_sub(skb->truesize, &dev->wmem_alloc);
++ if (test_bit(RFCOMM_TTY_ATTACHED, &dev->flags))
++ tasklet_schedule(&dev->wakeup_task);
++ rfcomm_dev_put(dev);
++}
++
++static inline void rfcomm_set_owner_w(struct sk_buff *skb, struct rfcomm_dev *dev)
++{
++ rfcomm_dev_hold(dev);
++ atomic_add(skb->truesize, &dev->wmem_alloc);
++ skb->sk = (void *) dev;
++ skb->destructor = rfcomm_wfree;
++}
++
++static struct sk_buff *rfcomm_wmalloc(struct rfcomm_dev *dev, unsigned long size, int force, int priority)
++{
++ if (force || atomic_read(&dev->wmem_alloc) < rfcomm_room(dev->dlc)) {
++ struct sk_buff *skb = alloc_skb(size, priority);
++ if (skb) {
++ rfcomm_set_owner_w(skb, dev);
++ return skb;
++ }
++ }
++ return NULL;
++}
++
++/* ---- Device IOCTLs ---- */
++
++#define NOCAP_FLAGS ((1 << RFCOMM_REUSE_DLC) | (1 << RFCOMM_RELEASE_ONHUP))
++
++static int rfcomm_create_dev(struct sock *sk, unsigned long arg)
++{
++ struct rfcomm_dev_req req;
++ struct rfcomm_dlc *dlc;
++ int id;
++
++ if (copy_from_user(&req, (void *) arg, sizeof(req)))
++ return -EFAULT;
++
++ BT_DBG("sk %p dev_id %id flags 0x%x", sk, req.dev_id, req.flags);
++
++ if (req.flags != NOCAP_FLAGS && !capable(CAP_NET_ADMIN))
++ return -EPERM;
++
++ if (req.flags & (1 << RFCOMM_REUSE_DLC)) {
++ /* Socket must be connected */
++ if (sk->state != BT_CONNECTED)
++ return -EBADFD;
++
++ dlc = rfcomm_pi(sk)->dlc;
++ rfcomm_dlc_hold(dlc);
++ } else {
++ dlc = rfcomm_dlc_alloc(GFP_KERNEL);
++ if (!dlc)
++ return -ENOMEM;
++ }
++
++ id = rfcomm_dev_add(&req, dlc);
++ if (id < 0) {
++ rfcomm_dlc_put(dlc);
++ return id;
++ }
++
++ if (req.flags & (1 << RFCOMM_REUSE_DLC)) {
++ /* DLC is now used by device.
++ * Socket must be disconnected */
++ sk->state = BT_CLOSED;
++ }
++
++ return id;
++}
++
++static int rfcomm_release_dev(unsigned long arg)
++{
++ struct rfcomm_dev_req req;
++ struct rfcomm_dev *dev;
++
++ if (copy_from_user(&req, (void *) arg, sizeof(req)))
++ return -EFAULT;
++
++ BT_DBG("dev_id %id flags 0x%x", req.dev_id, req.flags);
++
++ if (!(dev = rfcomm_dev_get(req.dev_id)))
++ return -ENODEV;
++
++ if (dev->flags != NOCAP_FLAGS && !capable(CAP_NET_ADMIN)) {
++ rfcomm_dev_put(dev);
++ return -EPERM;
++ }
++
++ if (req.flags & (1 << RFCOMM_HANGUP_NOW))
++ rfcomm_dlc_close(dev->dlc, 0);
++
++ rfcomm_dev_del(dev);
++ rfcomm_dev_put(dev);
++ return 0;
++}
++
++static int rfcomm_get_dev_list(unsigned long arg)
++{
++ struct rfcomm_dev_list_req *dl;
++ struct rfcomm_dev_info *di;
++ struct list_head *p;
++ int n = 0, size, err;
++ u16 dev_num;
++
++ BT_DBG("");
++
++ if (get_user(dev_num, (u16 *) arg))
++ return -EFAULT;
++
++ if (!dev_num || dev_num > (PAGE_SIZE * 4) / sizeof(*di))
++ return -EINVAL;
++
++ size = sizeof(*dl) + dev_num * sizeof(*di);
++
++ if (!(dl = kmalloc(size, GFP_KERNEL)))
++ return -ENOMEM;
++
++ di = dl->dev_info;
++
++ read_lock_bh(&rfcomm_dev_lock);
++
++ list_for_each(p, &rfcomm_dev_list) {
++ struct rfcomm_dev *dev = list_entry(p, struct rfcomm_dev, list);
++ (di + n)->id = dev->id;
++ (di + n)->flags = dev->flags;
++ (di + n)->state = dev->dlc->state;
++ (di + n)->channel = dev->channel;
++ bacpy(&(di + n)->src, &dev->src);
++ bacpy(&(di + n)->dst, &dev->dst);
++ if (++n >= dev_num)
++ break;
++ }
++
++ read_unlock_bh(&rfcomm_dev_lock);
++
++ dl->dev_num = n;
++ size = sizeof(*dl) + n * sizeof(*di);
++
++ err = copy_to_user((void *) arg, dl, size);
++ kfree(dl);
++
++ return err ? -EFAULT : 0;
++}
++
++static int rfcomm_get_dev_info(unsigned long arg)
++{
++ struct rfcomm_dev *dev;
++ struct rfcomm_dev_info di;
++ int err = 0;
++
++ BT_DBG("");
++
++ if (copy_from_user(&di, (void *)arg, sizeof(di)))
++ return -EFAULT;
++
++ if (!(dev = rfcomm_dev_get(di.id)))
++ return -ENODEV;
++
++ di.flags = dev->flags;
++ di.channel = dev->channel;
++ di.state = dev->dlc->state;
++ bacpy(&di.src, &dev->src);
++ bacpy(&di.dst, &dev->dst);
++
++ if (copy_to_user((void *)arg, &di, sizeof(di)))
++ err = -EFAULT;
++
++ rfcomm_dev_put(dev);
++ return err;
++}
++
++int rfcomm_dev_ioctl(struct sock *sk, unsigned int cmd, unsigned long arg)
++{
++ BT_DBG("cmd %d arg %ld", cmd, arg);
++
++ switch (cmd) {
++ case RFCOMMCREATEDEV:
++ return rfcomm_create_dev(sk, arg);
++
++ case RFCOMMRELEASEDEV:
++ return rfcomm_release_dev(arg);
++
++ case RFCOMMGETDEVLIST:
++ return rfcomm_get_dev_list(arg);
++
++ case RFCOMMGETDEVINFO:
++ return rfcomm_get_dev_info(arg);
++ }
++
++ return -EINVAL;
++}
++
++/* ---- DLC callbacks ---- */
++static void rfcomm_dev_data_ready(struct rfcomm_dlc *dlc, struct sk_buff *skb)
++{
++ struct rfcomm_dev *dev = dlc->owner;
++ struct tty_struct *tty;
++
++ if (!dev || !(tty = dev->tty)) {
++ kfree_skb(skb);
++ return;
++ }
++
++ BT_DBG("dlc %p tty %p len %d", dlc, tty, skb->len);
++
++ if (test_bit(TTY_DONT_FLIP, &tty->flags)) {
++ register int i;
++ for (i = 0; i < skb->len; i++) {
++ if (tty->flip.count >= TTY_FLIPBUF_SIZE)
++ tty_flip_buffer_push(tty);
++
++ tty_insert_flip_char(tty, skb->data[i], 0);
++ }
++ tty_flip_buffer_push(tty);
++ } else
++ tty->ldisc.receive_buf(tty, skb->data, NULL, skb->len);
++
++ kfree_skb(skb);
++}
++
++static void rfcomm_dev_state_change(struct rfcomm_dlc *dlc, int err)
++{
++ struct rfcomm_dev *dev = dlc->owner;
++ if (!dev)
++ return;
++
++ BT_DBG("dlc %p dev %p err %d", dlc, dev, err);
++
++ dev->err = err;
++ wake_up_interruptible(&dev->wait);
++
++ if (dlc->state == BT_CLOSED) {
++ if (!dev->tty) {
++ if (test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags)) {
++ rfcomm_dev_hold(dev);
++ rfcomm_dev_del(dev);
++
++ /* We have to drop DLC lock here, otherwise
++ rfcomm_dev_put() will dead lock if it's
++ the last reference. */
++ rfcomm_dlc_unlock(dlc);
++ rfcomm_dev_put(dev);
++ rfcomm_dlc_lock(dlc);
++ }
++ } else
++ tty_hangup(dev->tty);
++ }
++}
++
++static void rfcomm_dev_modem_status(struct rfcomm_dlc *dlc, u8 v24_sig)
++{
++ struct rfcomm_dev *dev = dlc->owner;
++ if (!dev)
++ return;
++
++ BT_DBG("dlc %p dev %p v24_sig 0x%02x", dlc, dev, v24_sig);
++
++ dev->modem_status =
++ ((v24_sig & RFCOMM_V24_RTC) ? (TIOCM_DSR | TIOCM_DTR) : 0) |
++ ((v24_sig & RFCOMM_V24_RTR) ? (TIOCM_RTS | TIOCM_CTS) : 0) |
++ ((v24_sig & RFCOMM_V24_IC) ? TIOCM_RI : 0) |
++ ((v24_sig & RFCOMM_V24_DV) ? TIOCM_CD : 0);
++}
++
++/* ---- TTY functions ---- */
++static void rfcomm_tty_wakeup(unsigned long arg)
++{
++ struct rfcomm_dev *dev = (void *) arg;
++ struct tty_struct *tty = dev->tty;
++ if (!tty)
++ return;
++
++ BT_DBG("dev %p tty %p", dev, tty);
++
++ if (test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags) && tty->ldisc.write_wakeup)
++ (tty->ldisc.write_wakeup)(tty);
++
++ wake_up_interruptible(&tty->write_wait);
++#ifdef SERIAL_HAVE_POLL_WAIT
++ wake_up_interruptible(&tty->poll_wait);
++#endif
++}
++
++static int rfcomm_tty_open(struct tty_struct *tty, struct file *filp)
++{
++ DECLARE_WAITQUEUE(wait, current);
++ struct rfcomm_dev *dev;
++ struct rfcomm_dlc *dlc;
++ int err, id;
++
++ id = MINOR(tty->device) - tty->driver.minor_start;
++
++ BT_DBG("tty %p id %d", tty, id);
++
++ /* We don't leak this refcount. For reasons which are not entirely
++ clear, the TTY layer will call our ->close() method even if the
++ open fails. We decrease the refcount there, and decreasing it
++ here too would cause breakage. */
++ dev = rfcomm_dev_get(id);
++ if (!dev)
++ return -ENODEV;
++
++ BT_DBG("dev %p dst %s channel %d opened %d", dev, batostr(&dev->dst), dev->channel, dev->opened);
++
++ if (dev->opened++ != 0)
++ return 0;
++
++ dlc = dev->dlc;
++
++ /* Attach TTY and open DLC */
++
++ rfcomm_dlc_lock(dlc);
++ tty->driver_data = dev;
++ dev->tty = tty;
++ rfcomm_dlc_unlock(dlc);
++ set_bit(RFCOMM_TTY_ATTACHED, &dev->flags);
++
++ err = rfcomm_dlc_open(dlc, &dev->src, &dev->dst, dev->channel);
++ if (err < 0)
++ return err;
++
++ /* Wait for DLC to connect */
++ add_wait_queue(&dev->wait, &wait);
++ while (1) {
++ set_current_state(TASK_INTERRUPTIBLE);
++
++ if (dlc->state == BT_CLOSED) {
++ err = -dev->err;
++ break;
++ }
++
++ if (dlc->state == BT_CONNECTED)
++ break;
++
++ if (signal_pending(current)) {
++ err = -EINTR;
++ break;
++ }
++
++ schedule();
++ }
++ set_current_state(TASK_RUNNING);
++ remove_wait_queue(&dev->wait, &wait);
++
++ return err;
++}
++
++static void rfcomm_tty_close(struct tty_struct *tty, struct file *filp)
++{
++ struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data;
++ if (!dev)
++ return;
++
++ BT_DBG("tty %p dev %p dlc %p opened %d", tty, dev, dev->dlc, dev->opened);
++
++ if (--dev->opened == 0) {
++ /* Close DLC and dettach TTY */
++ rfcomm_dlc_close(dev->dlc, 0);
++
++ clear_bit(RFCOMM_TTY_ATTACHED, &dev->flags);
++ tasklet_kill(&dev->wakeup_task);
++
++ rfcomm_dlc_lock(dev->dlc);
++ tty->driver_data = NULL;
++ dev->tty = NULL;
++ rfcomm_dlc_unlock(dev->dlc);
++ }
++
++ rfcomm_dev_put(dev);
++}
++
++static int rfcomm_tty_write(struct tty_struct *tty, int from_user, const unsigned char *buf, int count)
++{
++ struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data;
++ struct rfcomm_dlc *dlc = dev->dlc;
++ struct sk_buff *skb;
++ int err = 0, sent = 0, size;
++
++ BT_DBG("tty %p from_user %d count %d", tty, from_user, count);
++
++ while (count) {
++ size = min_t(uint, count, dlc->mtu);
++
++ if (from_user)
++ skb = rfcomm_wmalloc(dev, size + RFCOMM_SKB_RESERVE, 0, GFP_KERNEL);
++ else
++ skb = rfcomm_wmalloc(dev, size + RFCOMM_SKB_RESERVE, 0, GFP_ATOMIC);
++
++ if (!skb)
++ break;
++
++ skb_reserve(skb, RFCOMM_SKB_HEAD_RESERVE);
++
++ if (from_user)
++ copy_from_user(skb_put(skb, size), buf + sent, size);
++ else
++ memcpy(skb_put(skb, size), buf + sent, size);
++
++ if ((err = rfcomm_dlc_send(dlc, skb)) < 0) {
++ kfree_skb(skb);
++ break;
++ }
++
++ sent += size;
++ count -= size;
++ }
++
++ return sent ? sent : err;
++}
++
++static void rfcomm_tty_put_char(struct tty_struct *tty, unsigned char ch)
++{
++ struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data;
++ struct rfcomm_dlc *dlc = dev->dlc;
++ struct sk_buff *skb;
++
++ BT_DBG("tty %p char %x", tty, ch);
++
++ skb = rfcomm_wmalloc(dev, 1 + RFCOMM_SKB_RESERVE, 1, GFP_ATOMIC);
++
++ if (!skb)
++ return;
++
++ skb_reserve(skb, RFCOMM_SKB_HEAD_RESERVE);
++
++ *(char *)skb_put(skb, 1) = ch;
++
++ if ((rfcomm_dlc_send(dlc, skb)) < 0)
++ kfree_skb(skb);
++}
++
++static int rfcomm_tty_write_room(struct tty_struct *tty)
++{
++ struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data;
++ int room;
++
++ BT_DBG("tty %p", tty);
++
++ room = rfcomm_room(dev->dlc) - atomic_read(&dev->wmem_alloc);
++ if (room < 0)
++ room = 0;
++
++ return room;
++}
++
++static int rfcomm_tty_set_modem_status(uint cmd, struct rfcomm_dlc *dlc, uint status)
++{
++ u8 v24_sig, mask;
++
++ BT_DBG("dlc %p cmd 0x%02x", dlc, cmd);
++
++ if (cmd == TIOCMSET)
++ v24_sig = 0;
++ else
++ rfcomm_dlc_get_modem_status(dlc, &v24_sig);
++
++ mask = ((status & TIOCM_DSR) ? RFCOMM_V24_RTC : 0) |
++ ((status & TIOCM_DTR) ? RFCOMM_V24_RTC : 0) |
++ ((status & TIOCM_RTS) ? RFCOMM_V24_RTR : 0) |
++ ((status & TIOCM_CTS) ? RFCOMM_V24_RTR : 0) |
++ ((status & TIOCM_RI) ? RFCOMM_V24_IC : 0) |
++ ((status & TIOCM_CD) ? RFCOMM_V24_DV : 0);
++
++ if (cmd == TIOCMBIC)
++ v24_sig &= ~mask;
++ else
++ v24_sig |= mask;
++
++ rfcomm_dlc_set_modem_status(dlc, v24_sig);
++ return 0;
++}
++
++static int rfcomm_tty_ioctl(struct tty_struct *tty, struct file *filp, unsigned int cmd, unsigned long arg)
++{
++ struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data;
++ struct rfcomm_dlc *dlc = dev->dlc;
++ uint status;
++ int err;
++
++ BT_DBG("tty %p cmd 0x%02x", tty, cmd);
++
++ switch (cmd) {
++ case TCGETS:
++ BT_DBG("TCGETS is not supported");
++ return -ENOIOCTLCMD;
++
++ case TCSETS:
++ BT_DBG("TCSETS is not supported");
++ return -ENOIOCTLCMD;
++
++ case TIOCMGET:
++ BT_DBG("TIOCMGET");
++
++ return put_user(dev->modem_status, (unsigned int *)arg);
++
++ case TIOCMSET: /* Turns on and off the lines as specified by the mask */
++ case TIOCMBIS: /* Turns on the lines as specified by the mask */
++ case TIOCMBIC: /* Turns off the lines as specified by the mask */
++ if ((err = get_user(status, (unsigned int *)arg)))
++ return err;
++ return rfcomm_tty_set_modem_status(cmd, dlc, status);
++
++ case TIOCMIWAIT:
++ BT_DBG("TIOCMIWAIT");
++ break;
++
++ case TIOCGICOUNT:
++ BT_DBG("TIOCGICOUNT");
++ break;
++
++ case TIOCGSERIAL:
++ BT_ERR("TIOCGSERIAL is not supported");
++ return -ENOIOCTLCMD;
++
++ case TIOCSSERIAL:
++ BT_ERR("TIOCSSERIAL is not supported");
++ return -ENOIOCTLCMD;
++
++ case TIOCSERGSTRUCT:
++ BT_ERR("TIOCSERGSTRUCT is not supported");
++ return -ENOIOCTLCMD;
++
++ case TIOCSERGETLSR:
++ BT_ERR("TIOCSERGETLSR is not supported");
++ return -ENOIOCTLCMD;
++
++ case TIOCSERCONFIG:
++ BT_ERR("TIOCSERCONFIG is not supported");
++ return -ENOIOCTLCMD;
++
++ default:
++ return -ENOIOCTLCMD; /* ioctls which we must ignore */
++
++ }
++
++ return -ENOIOCTLCMD;
++}
++
++#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
++
++static void rfcomm_tty_set_termios(struct tty_struct *tty, struct termios *old)
++{
++ BT_DBG("tty %p", tty);
++
++ if ((tty->termios->c_cflag == old->c_cflag) &&
++ (RELEVANT_IFLAG(tty->termios->c_iflag) == RELEVANT_IFLAG(old->c_iflag)))
++ return;
++
++ /* handle turning off CRTSCTS */
++ if ((old->c_cflag & CRTSCTS) && !(tty->termios->c_cflag & CRTSCTS)) {
++ BT_DBG("turning off CRTSCTS");
++ }
++}
++
++static void rfcomm_tty_throttle(struct tty_struct *tty)
++{
++ struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data;
++
++ BT_DBG("tty %p dev %p", tty, dev);
++
++ rfcomm_dlc_throttle(dev->dlc);
++}
++
++static void rfcomm_tty_unthrottle(struct tty_struct *tty)
++{
++ struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data;
++
++ BT_DBG("tty %p dev %p", tty, dev);
++
++ rfcomm_dlc_unthrottle(dev->dlc);
++}
++
++static int rfcomm_tty_chars_in_buffer(struct tty_struct *tty)
++{
++ struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data;
++ struct rfcomm_dlc *dlc = dev->dlc;
++
++ BT_DBG("tty %p dev %p", tty, dev);
++
++ if (skb_queue_len(&dlc->tx_queue))
++ return dlc->mtu;
++
++ return 0;
++}
++
++static void rfcomm_tty_flush_buffer(struct tty_struct *tty)
++{
++ struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data;
++ if (!dev)
++ return;
++
++ BT_DBG("tty %p dev %p", tty, dev);
++
++ skb_queue_purge(&dev->dlc->tx_queue);
++
++ if (test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags) && tty->ldisc.write_wakeup)
++ tty->ldisc.write_wakeup(tty);
++}
++
++static void rfcomm_tty_send_xchar(struct tty_struct *tty, char ch)
++{
++ BT_DBG("tty %p ch %c", tty, ch);
++}
++
++static void rfcomm_tty_wait_until_sent(struct tty_struct *tty, int timeout)
++{
++ BT_DBG("tty %p timeout %d", tty, timeout);
++}
++
++static void rfcomm_tty_hangup(struct tty_struct *tty)
++{
++ struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data;
++ if (!dev)
++ return;
++
++ BT_DBG("tty %p dev %p", tty, dev);
++
++ rfcomm_tty_flush_buffer(tty);
++
++ if (test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags))
++ rfcomm_dev_del(dev);
++}
++
++static int rfcomm_tty_read_proc(char *buf, char **start, off_t offset, int len, int *eof, void *unused)
++{
++ return 0;
++}
++
++/* ---- TTY structure ---- */
++static int rfcomm_tty_refcount; /* If we manage several devices */
++
++static struct tty_struct *rfcomm_tty_table[RFCOMM_TTY_PORTS];
++static struct termios *rfcomm_tty_termios[RFCOMM_TTY_PORTS];
++static struct termios *rfcomm_tty_termios_locked[RFCOMM_TTY_PORTS];
++
++static struct tty_driver rfcomm_tty_driver = {
++ magic: TTY_DRIVER_MAGIC,
++ driver_name: "rfcomm",
++#ifdef CONFIG_DEVFS_FS
++ name: "bluetooth/rfcomm/%d",
++#else
++ name: "rfcomm",
++#endif
++ major: RFCOMM_TTY_MAJOR,
++ minor_start: RFCOMM_TTY_MINOR,
++ num: RFCOMM_TTY_PORTS,
++ type: TTY_DRIVER_TYPE_SERIAL,
++ subtype: SERIAL_TYPE_NORMAL,
++ flags: TTY_DRIVER_REAL_RAW,
++
++ refcount: &rfcomm_tty_refcount,
++ table: rfcomm_tty_table,
++ termios: rfcomm_tty_termios,
++ termios_locked: rfcomm_tty_termios_locked,
++
++ open: rfcomm_tty_open,
++ close: rfcomm_tty_close,
++ put_char: rfcomm_tty_put_char,
++ write: rfcomm_tty_write,
++ write_room: rfcomm_tty_write_room,
++ chars_in_buffer: rfcomm_tty_chars_in_buffer,
++ flush_buffer: rfcomm_tty_flush_buffer,
++ ioctl: rfcomm_tty_ioctl,
++ throttle: rfcomm_tty_throttle,
++ unthrottle: rfcomm_tty_unthrottle,
++ set_termios: rfcomm_tty_set_termios,
++ send_xchar: rfcomm_tty_send_xchar,
++ stop: NULL,
++ start: NULL,
++ hangup: rfcomm_tty_hangup,
++ wait_until_sent: rfcomm_tty_wait_until_sent,
++ read_proc: rfcomm_tty_read_proc,
++};
++
++int rfcomm_init_ttys(void)
++{
++ int i;
++
++ /* Initalize our global data */
++ for (i = 0; i < RFCOMM_TTY_PORTS; i++)
++ rfcomm_tty_table[i] = NULL;
++
++ /* Register the TTY driver */
++ rfcomm_tty_driver.init_termios = tty_std_termios;
++ rfcomm_tty_driver.init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
++ rfcomm_tty_driver.flags = TTY_DRIVER_REAL_RAW;
++
++ if (tty_register_driver(&rfcomm_tty_driver)) {
++ BT_ERR("Can't register RFCOMM TTY driver");
++ return -1;
++ }
++
++ return 0;
++}
++
++void rfcomm_cleanup_ttys(void)
++{
++ tty_unregister_driver(&rfcomm_tty_driver);
++ return;
++}
+diff -urN linux-2.4.18/net/bluetooth/sco.c linux-2.4.18-mh15/net/bluetooth/sco.c
+--- linux-2.4.18/net/bluetooth/sco.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/net/bluetooth/sco.c 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,1019 @@
++/*
++ BlueZ - Bluetooth protocol stack for Linux
++ Copyright (C) 2000-2001 Qualcomm Incorporated
++
++ Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License version 2 as
++ published by the Free Software Foundation;
++
++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++
++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
++ SOFTWARE IS DISCLAIMED.
++*/
++
++/*
++ * BlueZ SCO sockets.
++ *
++ * $Id$
++ */
++#define VERSION "0.3"
++
++#include <linux/config.h>
++#include <linux/module.h>
++
++#include <linux/types.h>
++#include <linux/errno.h>
++#include <linux/kernel.h>
++#include <linux/major.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/poll.h>
++#include <linux/fcntl.h>
++#include <linux/init.h>
++#include <linux/skbuff.h>
++#include <linux/interrupt.h>
++#include <linux/socket.h>
++#include <linux/skbuff.h>
++#include <linux/proc_fs.h>
++#include <linux/list.h>
++#include <net/sock.h>
++
++#include <asm/system.h>
++#include <asm/uaccess.h>
++
++#include <net/bluetooth/bluetooth.h>
++#include <net/bluetooth/hci_core.h>
++#include <net/bluetooth/sco.h>
++
++#ifndef SCO_DEBUG
++#undef BT_DBG
++#define BT_DBG( A... )
++#endif
++
++static struct proto_ops sco_sock_ops;
++
++static struct bluez_sock_list sco_sk_list = {
++ lock: RW_LOCK_UNLOCKED
++};
++
++static inline int sco_chan_add(struct sco_conn *conn, struct sock *sk, struct sock *parent);
++static void sco_chan_del(struct sock *sk, int err);
++static inline struct sock * sco_chan_get(struct sco_conn *conn);
++
++static int sco_conn_del(struct hci_conn *conn, int err);
++
++static void sco_sock_close(struct sock *sk);
++static void sco_sock_kill(struct sock *sk);
++
++/* ----- SCO timers ------ */
++static void sco_sock_timeout(unsigned long arg)
++{
++ struct sock *sk = (struct sock *) arg;
++
++ BT_DBG("sock %p state %d", sk, sk->state);
++
++ bh_lock_sock(sk);
++ sk->err = ETIMEDOUT;
++ sk->state_change(sk);
++ bh_unlock_sock(sk);
++
++ sco_sock_kill(sk);
++ sock_put(sk);
++}
++
++static void sco_sock_set_timer(struct sock *sk, long timeout)
++{
++ BT_DBG("sock %p state %d timeout %ld", sk, sk->state, timeout);
++
++ if (!mod_timer(&sk->timer, jiffies + timeout))
++ sock_hold(sk);
++}
++
++static void sco_sock_clear_timer(struct sock *sk)
++{
++ BT_DBG("sock %p state %d", sk, sk->state);
++
++ if (timer_pending(&sk->timer) && del_timer(&sk->timer))
++ __sock_put(sk);
++}
++
++static void sco_sock_init_timer(struct sock *sk)
++{
++ init_timer(&sk->timer);
++ sk->timer.function = sco_sock_timeout;
++ sk->timer.data = (unsigned long)sk;
++}
++
++/* -------- SCO connections --------- */
++static struct sco_conn *sco_conn_add(struct hci_conn *hcon, __u8 status)
++{
++ struct hci_dev *hdev = hcon->hdev;
++ struct sco_conn *conn;
++
++ if ((conn = hcon->sco_data))
++ return conn;
++
++ if (status)
++ return conn;
++
++ if (!(conn = kmalloc(sizeof(struct sco_conn), GFP_ATOMIC)))
++ return NULL;
++ memset(conn, 0, sizeof(struct sco_conn));
++
++ spin_lock_init(&conn->lock);
++
++ hcon->sco_data = conn;
++ conn->hcon = hcon;
++
++ conn->src = &hdev->bdaddr;
++ conn->dst = &hcon->dst;
++
++ if (hdev->sco_mtu > 0)
++ conn->mtu = hdev->sco_mtu;
++ else
++ conn->mtu = 60;
++
++ BT_DBG("hcon %p conn %p", hcon, conn);
++
++ MOD_INC_USE_COUNT;
++ return conn;
++}
++
++static int sco_conn_del(struct hci_conn *hcon, int err)
++{
++ struct sco_conn *conn;
++ struct sock *sk;
++
++ if (!(conn = hcon->sco_data))
++ return 0;
++
++ BT_DBG("hcon %p conn %p, err %d", hcon, conn, err);
++
++ /* Kill socket */
++ if ((sk = sco_chan_get(conn))) {
++ bh_lock_sock(sk);
++ sco_sock_clear_timer(sk);
++ sco_chan_del(sk, err);
++ bh_unlock_sock(sk);
++ sco_sock_kill(sk);
++ }
++
++ hcon->sco_data = NULL;
++ kfree(conn);
++
++ MOD_DEC_USE_COUNT;
++ return 0;
++}
++
++int sco_connect(struct sock *sk)
++{
++ bdaddr_t *src = &bluez_pi(sk)->src;
++ bdaddr_t *dst = &bluez_pi(sk)->dst;
++ struct sco_conn *conn;
++ struct hci_conn *hcon;
++ struct hci_dev *hdev;
++ int err = 0;
++
++ BT_DBG("%s -> %s", batostr(src), batostr(dst));
++
++ if (!(hdev = hci_get_route(dst, src)))
++ return -EHOSTUNREACH;
++
++ hci_dev_lock_bh(hdev);
++
++ err = -ENOMEM;
++
++ hcon = hci_connect(hdev, SCO_LINK, dst);
++ if (!hcon)
++ goto done;
++
++ conn = sco_conn_add(hcon, 0);
++ if (!conn) {
++ hci_conn_put(hcon);
++ goto done;
++ }
++
++ /* Update source addr of the socket */
++ bacpy(src, conn->src);
++
++ err = sco_chan_add(conn, sk, NULL);
++ if (err)
++ goto done;
++
++ if (hcon->state == BT_CONNECTED) {
++ sco_sock_clear_timer(sk);
++ sk->state = BT_CONNECTED;
++ } else {
++ sk->state = BT_CONNECT;
++ sco_sock_set_timer(sk, sk->sndtimeo);
++ }
++done:
++ hci_dev_unlock_bh(hdev);
++ hci_dev_put(hdev);
++ return err;
++}
++
++static inline int sco_send_frame(struct sock *sk, struct msghdr *msg, int len)
++{
++ struct sco_conn *conn = sco_pi(sk)->conn;
++ struct sk_buff *skb;
++ int err, count;
++
++ /* Check outgoing MTU */
++ if (len > conn->mtu)
++ return -EINVAL;
++
++ BT_DBG("sk %p len %d", sk, len);
++
++ count = MIN(conn->mtu, len);
++ if (!(skb = bluez_skb_send_alloc(sk, count, msg->msg_flags & MSG_DONTWAIT, &err)))
++ return err;
++
++ if (memcpy_fromiovec(skb_put(skb, count), msg->msg_iov, count)) {
++ err = -EFAULT;
++ goto fail;
++ }
++
++ if ((err = hci_send_sco(conn->hcon, skb)) < 0)
++ goto fail;
++
++ return count;
++
++fail:
++ kfree_skb(skb);
++ return err;
++}
++
++static inline void sco_recv_frame(struct sco_conn *conn, struct sk_buff *skb)
++{
++ struct sock *sk = sco_chan_get(conn);
++
++ if (!sk)
++ goto drop;
++
++ BT_DBG("sk %p len %d", sk, skb->len);
++
++ if (sk->state != BT_CONNECTED)
++ goto drop;
++
++ if (!sock_queue_rcv_skb(sk, skb))
++ return;
++
++drop:
++ kfree_skb(skb);
++ return;
++}
++
++/* -------- Socket interface ---------- */
++static struct sock *__sco_get_sock_by_addr(bdaddr_t *ba)
++{
++ struct sock *sk;
++
++ for (sk = sco_sk_list.head; sk; sk = sk->next) {
++ if (!bacmp(&bluez_pi(sk)->src, ba))
++ break;
++ }
++
++ return sk;
++}
++
++/* Find socket listening on source bdaddr.
++ * Returns closest match.
++ */
++static struct sock *sco_get_sock_listen(bdaddr_t *src)
++{
++ struct sock *sk, *sk1 = NULL;
++
++ read_lock(&sco_sk_list.lock);
++
++ for (sk = sco_sk_list.head; sk; sk = sk->next) {
++ if (sk->state != BT_LISTEN)
++ continue;
++
++ /* Exact match. */
++ if (!bacmp(&bluez_pi(sk)->src, src))
++ break;
++
++ /* Closest match */
++ if (!bacmp(&bluez_pi(sk)->src, BDADDR_ANY))
++ sk1 = sk;
++ }
++
++ read_unlock(&sco_sk_list.lock);
++
++ return sk ? sk : sk1;
++}
++
++static void sco_sock_destruct(struct sock *sk)
++{
++ BT_DBG("sk %p", sk);
++
++ skb_queue_purge(&sk->receive_queue);
++ skb_queue_purge(&sk->write_queue);
++
++ MOD_DEC_USE_COUNT;
++}
++
++static void sco_sock_cleanup_listen(struct sock *parent)
++{
++ struct sock *sk;
++
++ BT_DBG("parent %p", parent);
++
++ /* Close not yet accepted channels */
++ while ((sk = bluez_accept_dequeue(parent, NULL))) {
++ sco_sock_close(sk);
++ sco_sock_kill(sk);
++ }
++
++ parent->state = BT_CLOSED;
++ parent->zapped = 1;
++}
++
++/* Kill socket (only if zapped and orphan)
++ * Must be called on unlocked socket.
++ */
++static void sco_sock_kill(struct sock *sk)
++{
++ if (!sk->zapped || sk->socket)
++ return;
++
++ BT_DBG("sk %p state %d", sk, sk->state);
++
++ /* Kill poor orphan */
++ bluez_sock_unlink(&sco_sk_list, sk);
++ sk->dead = 1;
++ sock_put(sk);
++}
++
++/* Close socket.
++ * Must be called on unlocked socket.
++ */
++static void sco_sock_close(struct sock *sk)
++{
++ struct sco_conn *conn;
++
++ sco_sock_clear_timer(sk);
++
++ lock_sock(sk);
++
++ conn = sco_pi(sk)->conn;
++
++ BT_DBG("sk %p state %d conn %p socket %p", sk, sk->state, conn, sk->socket);
++
++ switch (sk->state) {
++ case BT_LISTEN:
++ sco_sock_cleanup_listen(sk);
++ break;
++
++ case BT_CONNECTED:
++ case BT_CONFIG:
++ case BT_CONNECT:
++ case BT_DISCONN:
++ sco_chan_del(sk, ECONNRESET);
++ break;
++
++ default:
++ sk->zapped = 1;
++ break;
++ };
++
++ release_sock(sk);
++}
++
++static void sco_sock_init(struct sock *sk, struct sock *parent)
++{
++ BT_DBG("sk %p", sk);
++
++ if (parent)
++ sk->type = parent->type;
++}
++
++static struct sock *sco_sock_alloc(struct socket *sock, int proto, int prio)
++{
++ struct sock *sk;
++
++ if (!(sk = sk_alloc(PF_BLUETOOTH, prio, 1)))
++ return NULL;
++
++ bluez_sock_init(sock, sk);
++
++ sk->zapped = 0;
++
++ sk->destruct = sco_sock_destruct;
++ sk->sndtimeo = SCO_CONN_TIMEOUT;
++
++ sk->protocol = proto;
++ sk->state = BT_OPEN;
++
++ sco_sock_init_timer(sk);
++
++ bluez_sock_link(&sco_sk_list, sk);
++
++ MOD_INC_USE_COUNT;
++ return sk;
++}
++
++static int sco_sock_create(struct socket *sock, int protocol)
++{
++ struct sock *sk;
++
++ BT_DBG("sock %p", sock);
++
++ sock->state = SS_UNCONNECTED;
++
++ if (sock->type != SOCK_SEQPACKET)
++ return -ESOCKTNOSUPPORT;
++
++ sock->ops = &sco_sock_ops;
++
++ if (!(sk = sco_sock_alloc(sock, protocol, GFP_KERNEL)))
++ return -ENOMEM;
++
++ sco_sock_init(sk, NULL);
++ return 0;
++}
++
++static int sco_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len)
++{
++ struct sockaddr_sco *sa = (struct sockaddr_sco *) addr;
++ struct sock *sk = sock->sk;
++ bdaddr_t *src = &sa->sco_bdaddr;
++ int err = 0;
++
++ BT_DBG("sk %p %s", sk, batostr(&sa->sco_bdaddr));
++
++ if (!addr || addr->sa_family != AF_BLUETOOTH)
++ return -EINVAL;
++
++ lock_sock(sk);
++
++ if (sk->state != BT_OPEN) {
++ err = -EBADFD;
++ goto done;
++ }
++
++ write_lock_bh(&sco_sk_list.lock);
++
++ if (bacmp(src, BDADDR_ANY) && __sco_get_sock_by_addr(src)) {
++ err = -EADDRINUSE;
++ } else {
++ /* Save source address */
++ bacpy(&bluez_pi(sk)->src, &sa->sco_bdaddr);
++ sk->state = BT_BOUND;
++ }
++
++ write_unlock_bh(&sco_sk_list.lock);
++
++done:
++ release_sock(sk);
++
++ return err;
++}
++
++static int sco_sock_connect(struct socket *sock, struct sockaddr *addr, int alen, int flags)
++{
++ struct sockaddr_sco *sa = (struct sockaddr_sco *) addr;
++ struct sock *sk = sock->sk;
++ int err = 0;
++
++
++ BT_DBG("sk %p", sk);
++
++ if (addr->sa_family != AF_BLUETOOTH || alen < sizeof(struct sockaddr_sco))
++ return -EINVAL;
++
++ if (sk->state != BT_OPEN && sk->state != BT_BOUND)
++ return -EBADFD;
++
++ if (sk->type != SOCK_SEQPACKET)
++ return -EINVAL;
++
++ lock_sock(sk);
++
++ /* Set destination address and psm */
++ bacpy(&bluez_pi(sk)->dst, &sa->sco_bdaddr);
++
++ if ((err = sco_connect(sk)))
++ goto done;
++
++ err = bluez_sock_wait_state(sk, BT_CONNECTED,
++ sock_sndtimeo(sk, flags & O_NONBLOCK));
++
++done:
++ release_sock(sk);
++ return err;
++}
++
++int sco_sock_listen(struct socket *sock, int backlog)
++{
++ struct sock *sk = sock->sk;
++ int err = 0;
++
++ BT_DBG("sk %p backlog %d", sk, backlog);
++
++ lock_sock(sk);
++
++ if (sk->state != BT_BOUND || sock->type != SOCK_SEQPACKET) {
++ err = -EBADFD;
++ goto done;
++ }
++
++ sk->max_ack_backlog = backlog;
++ sk->ack_backlog = 0;
++ sk->state = BT_LISTEN;
++
++done:
++ release_sock(sk);
++ return err;
++}
++
++int sco_sock_accept(struct socket *sock, struct socket *newsock, int flags)
++{
++ DECLARE_WAITQUEUE(wait, current);
++ struct sock *sk = sock->sk, *ch;
++ long timeo;
++ int err = 0;
++
++ lock_sock(sk);
++
++ if (sk->state != BT_LISTEN) {
++ err = -EBADFD;
++ goto done;
++ }
++
++ timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK);
++
++ BT_DBG("sk %p timeo %ld", sk, timeo);
++
++ /* Wait for an incoming connection. (wake-one). */
++ add_wait_queue_exclusive(sk->sleep, &wait);
++ while (!(ch = bluez_accept_dequeue(sk, newsock))) {
++ set_current_state(TASK_INTERRUPTIBLE);
++ if (!timeo) {
++ err = -EAGAIN;
++ break;
++ }
++
++ release_sock(sk);
++ timeo = schedule_timeout(timeo);
++ lock_sock(sk);
++
++ if (sk->state != BT_LISTEN) {
++ err = -EBADFD;
++ break;
++ }
++
++ if (signal_pending(current)) {
++ err = sock_intr_errno(timeo);
++ break;
++ }
++ }
++ set_current_state(TASK_RUNNING);
++ remove_wait_queue(sk->sleep, &wait);
++
++ if (err)
++ goto done;
++
++ newsock->state = SS_CONNECTED;
++
++ BT_DBG("new socket %p", ch);
++
++done:
++ release_sock(sk);
++ return err;
++}
++
++static int sco_sock_getname(struct socket *sock, struct sockaddr *addr, int *len, int peer)
++{
++ struct sockaddr_sco *sa = (struct sockaddr_sco *) addr;
++ struct sock *sk = sock->sk;
++
++ BT_DBG("sock %p, sk %p", sock, sk);
++
++ addr->sa_family = AF_BLUETOOTH;
++ *len = sizeof(struct sockaddr_sco);
++
++ if (peer)
++ bacpy(&sa->sco_bdaddr, &bluez_pi(sk)->dst);
++ else
++ bacpy(&sa->sco_bdaddr, &bluez_pi(sk)->src);
++
++ return 0;
++}
++
++static int sco_sock_sendmsg(struct socket *sock, struct msghdr *msg, int len, struct scm_cookie *scm)
++{
++ struct sock *sk = sock->sk;
++ int err = 0;
++
++ BT_DBG("sock %p, sk %p", sock, sk);
++
++ if (sk->err)
++ return sock_error(sk);
++
++ if (msg->msg_flags & MSG_OOB)
++ return -EOPNOTSUPP;
++
++ lock_sock(sk);
++
++ if (sk->state == BT_CONNECTED)
++ err = sco_send_frame(sk, msg, len);
++ else
++ err = -ENOTCONN;
++
++ release_sock(sk);
++ return err;
++}
++
++int sco_sock_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen)
++{
++ struct sock *sk = sock->sk;
++ int err = 0;
++
++ BT_DBG("sk %p", sk);
++
++ lock_sock(sk);
++
++ switch (optname) {
++ default:
++ err = -ENOPROTOOPT;
++ break;
++ };
++
++ release_sock(sk);
++ return err;
++}
++
++int sco_sock_getsockopt(struct socket *sock, int level, int optname, char *optval, int *optlen)
++{
++ struct sock *sk = sock->sk;
++ struct sco_options opts;
++ struct sco_conninfo cinfo;
++ int len, err = 0;
++
++ BT_DBG("sk %p", sk);
++
++ if (get_user(len, optlen))
++ return -EFAULT;
++
++ lock_sock(sk);
++
++ switch (optname) {
++ case SCO_OPTIONS:
++ if (sk->state != BT_CONNECTED) {
++ err = -ENOTCONN;
++ break;
++ }
++
++ opts.mtu = sco_pi(sk)->conn->mtu;
++
++ BT_DBG("mtu %d", opts.mtu);
++
++ len = MIN(len, sizeof(opts));
++ if (copy_to_user(optval, (char *)&opts, len))
++ err = -EFAULT;
++
++ break;
++
++ case SCO_CONNINFO:
++ if (sk->state != BT_CONNECTED) {
++ err = -ENOTCONN;
++ break;
++ }
++
++ cinfo.hci_handle = sco_pi(sk)->conn->hcon->handle;
++
++ len = MIN(len, sizeof(cinfo));
++ if (copy_to_user(optval, (char *)&cinfo, len))
++ err = -EFAULT;
++
++ break;
++
++ default:
++ err = -ENOPROTOOPT;
++ break;
++ };
++
++ release_sock(sk);
++ return err;
++}
++
++static int sco_sock_release(struct socket *sock)
++{
++ struct sock *sk = sock->sk;
++ int err = 0;
++
++ BT_DBG("sock %p, sk %p", sock, sk);
++
++ if (!sk)
++ return 0;
++
++ sco_sock_close(sk);
++ if (sk->linger) {
++ lock_sock(sk);
++ err = bluez_sock_wait_state(sk, BT_CLOSED, sk->lingertime);
++ release_sock(sk);
++ }
++
++ sock_orphan(sk);
++ sco_sock_kill(sk);
++ return err;
++}
++
++static void __sco_chan_add(struct sco_conn *conn, struct sock *sk, struct sock *parent)
++{
++ BT_DBG("conn %p", conn);
++
++ sco_pi(sk)->conn = conn;
++ conn->sk = sk;
++
++ if (parent)
++ bluez_accept_enqueue(parent, sk);
++}
++
++static inline int sco_chan_add(struct sco_conn *conn, struct sock *sk, struct sock *parent)
++{
++ int err = 0;
++
++ sco_conn_lock(conn);
++ if (conn->sk) {
++ err = -EBUSY;
++ } else {
++ __sco_chan_add(conn, sk, parent);
++ }
++ sco_conn_unlock(conn);
++ return err;
++}
++
++static inline struct sock * sco_chan_get(struct sco_conn *conn)
++{
++ struct sock *sk = NULL;
++ sco_conn_lock(conn);
++ sk = conn->sk;
++ sco_conn_unlock(conn);
++ return sk;
++}
++
++/* Delete channel.
++ * Must be called on the locked socket. */
++static void sco_chan_del(struct sock *sk, int err)
++{
++ struct sco_conn *conn;
++
++ conn = sco_pi(sk)->conn;
++
++ BT_DBG("sk %p, conn %p, err %d", sk, conn, err);
++
++ if (conn) {
++ sco_conn_lock(conn);
++ conn->sk = NULL;
++ sco_pi(sk)->conn = NULL;
++ sco_conn_unlock(conn);
++ hci_conn_put(conn->hcon);
++ }
++
++ sk->state = BT_CLOSED;
++ sk->err = err;
++ sk->state_change(sk);
++
++ sk->zapped = 1;
++}
++
++static void sco_conn_ready(struct sco_conn *conn)
++{
++ struct sock *parent, *sk;
++
++ BT_DBG("conn %p", conn);
++
++ sco_conn_lock(conn);
++
++ if ((sk = conn->sk)) {
++ sco_sock_clear_timer(sk);
++ bh_lock_sock(sk);
++ sk->state = BT_CONNECTED;
++ sk->state_change(sk);
++ bh_unlock_sock(sk);
++ } else {
++ parent = sco_get_sock_listen(conn->src);
++ if (!parent)
++ goto done;
++
++ bh_lock_sock(parent);
++
++ sk = sco_sock_alloc(NULL, BTPROTO_SCO, GFP_ATOMIC);
++ if (!sk) {
++ bh_unlock_sock(parent);
++ goto done;
++ }
++
++ sco_sock_init(sk, parent);
++
++ bacpy(&bluez_pi(sk)->src, conn->src);
++ bacpy(&bluez_pi(sk)->dst, conn->dst);
++
++ hci_conn_hold(conn->hcon);
++ __sco_chan_add(conn, sk, parent);
++
++ sk->state = BT_CONNECTED;
++
++ /* Wake up parent */
++ parent->data_ready(parent, 1);
++
++ bh_unlock_sock(parent);
++ }
++
++done:
++ sco_conn_unlock(conn);
++}
++
++/* ----- SCO interface with lower layer (HCI) ----- */
++int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 type)
++{
++ BT_DBG("hdev %s, bdaddr %s", hdev->name, batostr(bdaddr));
++
++ /* Always accept connection */
++ return HCI_LM_ACCEPT;
++}
++
++int sco_connect_cfm(struct hci_conn *hcon, __u8 status)
++{
++ BT_DBG("hcon %p bdaddr %s status %d", hcon, batostr(&hcon->dst), status);
++
++ if (hcon->type != SCO_LINK)
++ return 0;
++
++ if (!status) {
++ struct sco_conn *conn;
++
++ conn = sco_conn_add(hcon, status);
++ if (conn)
++ sco_conn_ready(conn);
++ } else
++ sco_conn_del(hcon, bterr(status));
++
++ return 0;
++}
++
++int sco_disconn_ind(struct hci_conn *hcon, __u8 reason)
++{
++ BT_DBG("hcon %p reason %d", hcon, reason);
++
++ if (hcon->type != SCO_LINK)
++ return 0;
++
++ sco_conn_del(hcon, bterr(reason));
++ return 0;
++}
++
++int sco_recv_scodata(struct hci_conn *hcon, struct sk_buff *skb)
++{
++ struct sco_conn *conn = hcon->sco_data;
++
++ if (!conn)
++ goto drop;
++
++ BT_DBG("conn %p len %d", conn, skb->len);
++
++ if (skb->len) {
++ sco_recv_frame(conn, skb);
++ return 0;
++ }
++
++drop:
++ kfree_skb(skb);
++ return 0;
++}
++
++/* ----- Proc fs support ------ */
++static int sco_sock_dump(char *buf, struct bluez_sock_list *list)
++{
++ struct sco_pinfo *pi;
++ struct sock *sk;
++ char *ptr = buf;
++
++ write_lock_bh(&list->lock);
++
++ for (sk = list->head; sk; sk = sk->next) {
++ pi = sco_pi(sk);
++ ptr += sprintf(ptr, "%s %s %d\n",
++ batostr(&bluez_pi(sk)->src), batostr(&bluez_pi(sk)->dst),
++ sk->state);
++ }
++
++ write_unlock_bh(&list->lock);
++
++ ptr += sprintf(ptr, "\n");
++
++ return ptr - buf;
++}
++
++static int sco_read_proc(char *buf, char **start, off_t offset, int count, int *eof, void *priv)
++{
++ char *ptr = buf;
++ int len;
++
++ BT_DBG("count %d, offset %ld", count, offset);
++
++ ptr += sco_sock_dump(ptr, &sco_sk_list);
++ len = ptr - buf;
++
++ if (len <= count + offset)
++ *eof = 1;
++
++ *start = buf + offset;
++ len -= offset;
++
++ if (len > count)
++ len = count;
++ if (len < 0)
++ len = 0;
++
++ return len;
++}
++
++static struct proto_ops sco_sock_ops = {
++ family: PF_BLUETOOTH,
++ release: sco_sock_release,
++ bind: sco_sock_bind,
++ connect: sco_sock_connect,
++ listen: sco_sock_listen,
++ accept: sco_sock_accept,
++ getname: sco_sock_getname,
++ sendmsg: sco_sock_sendmsg,
++ recvmsg: bluez_sock_recvmsg,
++ poll: bluez_sock_poll,
++ socketpair: sock_no_socketpair,
++ ioctl: sock_no_ioctl,
++ shutdown: sock_no_shutdown,
++ setsockopt: sco_sock_setsockopt,
++ getsockopt: sco_sock_getsockopt,
++ mmap: sock_no_mmap
++};
++
++static struct net_proto_family sco_sock_family_ops = {
++ family: PF_BLUETOOTH,
++ create: sco_sock_create
++};
++
++static struct hci_proto sco_hci_proto = {
++ name: "SCO",
++ id: HCI_PROTO_SCO,
++ connect_ind: sco_connect_ind,
++ connect_cfm: sco_connect_cfm,
++ disconn_ind: sco_disconn_ind,
++ recv_scodata: sco_recv_scodata,
++};
++
++int __init sco_init(void)
++{
++ int err;
++
++ if ((err = bluez_sock_register(BTPROTO_SCO, &sco_sock_family_ops))) {
++ BT_ERR("Can't register SCO socket layer");
++ return err;
++ }
++
++ if ((err = hci_register_proto(&sco_hci_proto))) {
++ BT_ERR("Can't register SCO protocol");
++ return err;
++ }
++
++ create_proc_read_entry("bluetooth/sco", 0, 0, sco_read_proc, NULL);
++
++ BT_INFO("BlueZ SCO ver %s Copyright (C) 2000,2001 Qualcomm Inc", VERSION);
++ BT_INFO("Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>");
++ return 0;
++}
++
++void sco_cleanup(void)
++{
++ int err;
++
++ remove_proc_entry("bluetooth/sco", NULL);
++
++ /* Unregister socket, protocol and notifier */
++ if ((err = bluez_sock_unregister(BTPROTO_SCO)))
++ BT_ERR("Can't unregister SCO socket layer %d", err);
++
++ if ((err = hci_unregister_proto(&sco_hci_proto)))
++ BT_ERR("Can't unregister SCO protocol %d", err);
++}
++
++module_init(sco_init);
++module_exit(sco_cleanup);
++
++MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>");
++MODULE_DESCRIPTION("BlueZ SCO ver " VERSION);
++MODULE_LICENSE("GPL");
+diff -urN linux-2.4.18/net/bluetooth/syms.c linux-2.4.18-mh15/net/bluetooth/syms.c
+--- linux-2.4.18/net/bluetooth/syms.c 2001-09-07 18:28:38.000000000 +0200
++++ linux-2.4.18-mh15/net/bluetooth/syms.c 2004-08-01 16:26:23.000000000 +0200
+@@ -25,7 +25,7 @@
+ /*
+ * BlueZ symbols.
+ *
+- * $Id$
++ * $Id$
+ */
+
+ #include <linux/config.h>
+@@ -39,25 +39,28 @@
+ #include <linux/socket.h>
+
+ #include <net/bluetooth/bluetooth.h>
+-#include <net/bluetooth/bluez.h>
+ #include <net/bluetooth/hci_core.h>
+
+ /* HCI Core */
+ EXPORT_SYMBOL(hci_register_dev);
+ EXPORT_SYMBOL(hci_unregister_dev);
++EXPORT_SYMBOL(hci_suspend_dev);
++EXPORT_SYMBOL(hci_resume_dev);
++
+ EXPORT_SYMBOL(hci_register_proto);
+ EXPORT_SYMBOL(hci_unregister_proto);
+-EXPORT_SYMBOL(hci_register_notifier);
+-EXPORT_SYMBOL(hci_unregister_notifier);
+
++EXPORT_SYMBOL(hci_get_route);
+ EXPORT_SYMBOL(hci_connect);
+-EXPORT_SYMBOL(hci_disconnect);
+ EXPORT_SYMBOL(hci_dev_get);
++EXPORT_SYMBOL(hci_conn_auth);
++EXPORT_SYMBOL(hci_conn_encrypt);
+
+ EXPORT_SYMBOL(hci_recv_frame);
+ EXPORT_SYMBOL(hci_send_acl);
+ EXPORT_SYMBOL(hci_send_sco);
+-EXPORT_SYMBOL(hci_send_raw);
++EXPORT_SYMBOL(hci_send_cmd);
++EXPORT_SYMBOL(hci_si_event);
+
+ /* BlueZ lib */
+ EXPORT_SYMBOL(bluez_dump);
+@@ -68,5 +71,11 @@
+ /* BlueZ sockets */
+ EXPORT_SYMBOL(bluez_sock_register);
+ EXPORT_SYMBOL(bluez_sock_unregister);
++EXPORT_SYMBOL(bluez_sock_init);
+ EXPORT_SYMBOL(bluez_sock_link);
+ EXPORT_SYMBOL(bluez_sock_unlink);
++EXPORT_SYMBOL(bluez_sock_recvmsg);
++EXPORT_SYMBOL(bluez_sock_poll);
++EXPORT_SYMBOL(bluez_accept_enqueue);
++EXPORT_SYMBOL(bluez_accept_dequeue);
++EXPORT_SYMBOL(bluez_sock_wait_state);
+diff -urN linux-2.4.18/net/netsyms.c linux-2.4.18-mh15/net/netsyms.c
+--- linux-2.4.18/net/netsyms.c 2002-02-25 20:38:14.000000000 +0100
++++ linux-2.4.18-mh15/net/netsyms.c 2004-08-01 16:26:23.000000000 +0200
+@@ -159,6 +159,7 @@
+ EXPORT_SYMBOL(put_cmsg);
+ EXPORT_SYMBOL(sock_kmalloc);
+ EXPORT_SYMBOL(sock_kfree_s);
++EXPORT_SYMBOL(sockfd_lookup);
+
+ #ifdef CONFIG_FILTER
+ EXPORT_SYMBOL(sk_run_filter);
diff --git a/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/corgi-fbcon-logo.patch b/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/corgi-fbcon-logo.patch
index e69de29bb2..4d3b42445b 100644
--- a/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/corgi-fbcon-logo.patch
+++ b/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/corgi-fbcon-logo.patch
@@ -0,0 +1,281 @@
+--- linux/drivers/video/w100fb.c 2004-11-04 12:44:29.000000000 -0600
++++ linux/drivers/video/w100fb.c~corgi.patch 2004-11-04 12:50:27.000000000 -0600
+@@ -982,6 +982,17 @@
+ #endif
+ #endif
+
++#ifdef CONFIG_ARCH_SHARP_SL_E // English message
++#include "corgiLogoMsg.c"
++#if defined(CONFIG_FBCON_ROTATE_R) || defined(CONFIG_FBCON_ROTATE_L)
++static int logo_msg_xoff __initdata = 400;
++static int logo_msg_yoff __initdata = 100;
++#else
++static int logo_msg_xoff __initdata = 120;
++static int logo_msg_yoff __initdata = 500;
++#endif
++#endif
++
+ #endif // CONFIG_ARCH_PXA_SHEPHERD
+
+ #endif
+--- /dev/null 2004-06-13 02:32:19.000000000 +0100
++++ linux/drivers/video/corgiLogoMsg.c 2004-11-12 19:57:23.000000000 +0000
+@@ -0,0 +1,258 @@
++/* Logo Screen 16bits RGB(565) data*/
++#ifndef __initdata
++#define __initdata
++#endif
++
++static int logo_msg_width __initdata = 30;
++static int logo_msg_height __initdata = 250;
++static unsigned short logo_msg_data[] __initdata ={
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xe71c,0xad55,0x7bef,0xd69a,0xffff,0xffff,0xffff,0xffff,0xffff,0xdedb,0xc638,0xef7d,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffdf,0x738e,0x0020,0x0000,0x0000,0xa514,0xffff,0xffff,0xffff,0xffff,0xffff,0x5aeb,0x0000,0x0020,0x528a,0xe71c,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0x7bef,0x0000,0x18c3,0x738e,0x9cf3,0xdefb,0xffff,0xffff,0xffff,0xffff,0xffff,0xa514,0x52aa,0x2104,0x0000,0x2104,0xf79e,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffbf,0x1082,0x18e3,0xef3d,0xffff,0xffff,0xffff,0xffff,0xe73c,0xbdf7,0xdebb,0xffdf,0xffff,0xffff,0xffdf,0x6b6d,0x0000,0x9cf3,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xdebd,0xad17,0x0000,0x5a8c,0xef3e,0xffff,0xffff,0xffff,0xffff,0xa514,0x0000,0x7bb1,0xc5da,0xd67c,0xffbf,0xffff,0xef7d,0x0000,0x632c,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xe6dd,0xc5ba,0xb558,0x0000,0x6b4e,0xf7bf,0xffff,0xffff,0xffff,0xffff,0x9492,0x0000,0x62ec,0xce1b,0xc5ba,0xce1b,0xffff,0xf7be,0x0000,0x630c,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xc5fb,0xce1b,0xffff,0x18e3,0x2124,0xf79e,0xffff,0xffdf,0xf77e,0xef7d,0x2945,0x0000,0x10a2,0xe71c,0xdebd,0xc5ba,0xef1e,0x94b2,0x0000,0x9cd3,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffbf,0xc5ba,0xdebd,0xffff,0x8c71,0x0000,0x2945,0x8430,0x9493,0x6b0e,0x2125,0x0000,0x94b2,0x0020,0x0841,0x526a,0x4209,0x3166,0x0000,0x2104,0xf79e,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffbf,0xc5ba,0xdedd,0xffff,0xffff,0x8430,0x0020,0x0000,0x0000,0x0000,0x0000,0x8410,0xffff,0xb596,0x18e3,0x0000,0x0000,0x0000,0x3186,0xdedb,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xcdfb,0xce1b,0xffff,0xffff,0xffff,0xe71c,0x9cd3,0x630d,0x6b4f,0xa4f6,0xffbf,0xffff,0xffff,0xffff,0xbdd8,0x9474,0xc5f9,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xe6fd,0xc5ba,0xce1b,0xe6fd,0xef3e,0xe6fd,0xce1b,0xc5ba,0xe71d,0xc5da,0xc5db,0xd67c,0xde9c,0xd63c,0xc5ba,0xce1b,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xe6fd,0xc5da,0xc5ba,0xc5ba,0xc5ba,0xc5ba,0xe6dd,0xffff,0xef5e,0xcdfb,0xc5ba,0xc5ba,0xc5ba,0xce3b,0xf7bf,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffbf,0xef1e,0xe6dd,0xe71d,0xf79f,0xffff,0xffff,0xffff,0xffff,0xf79f,0xf77e,0xf7bf,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xef5d,0xd69a,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffdf,0x2124,0x2104,0xf79e,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0x8c71,0x0000,0x4a69,0xffdf,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xdedb,0x6b6d,0xffff,0xffff,0xffff,0xffff,0xffff,0xffdf,0xf79f,0xffff,0xffff,0x4a69,0x0000,0x738e,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0x52aa,0x0000,0x9cd3,0xffff,0xffff,0xffff,0xffff,0xce1b,0xce1b,0xffff,0x4208,0x632c,0x2945,0x0000,0x630c,0xffdf,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xe71c,0x1082,0x0841,0xc618,0xffff,0xffff,0xffff,0xe6fd,0xc5ba,0xd67c,0x2104,0x5aeb,0xe73c,0x2965,0x0000,0x39c7,0xe71c,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xf7bf,0xdebd,0xffff,0xffff,0xc618,0x0861,0x0861,0x94b2,0xffdf,0xffff,0xffff,0xd67c,0xc5ba,0x18e3,0x5aeb,0xffff,0xf79e,0x5aeb,0x0000,0x0861,0x8c71,0xffdf,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xd67c,0xc5ba,0xe71d,0xffff,0xffff,0xc638,0x18e3,0x0000,0x18c3,0x6b6d,0x9494,0xc5fa,0xce1b,0x18c3,0x526a,0xffff,0xffff,0xffff,0xad55,0x10a2,0x0000,0x2124,0xef7d,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffbf,0xc5fb,0xc5db,0xf77e,0xffff,0xffff,0xf79e,0x8410,0x18e3,0x0000,0x0000,0x0000,0x0861,0x0000,0x4209,0xd63c,0xffbf,0xffff,0xffff,0xef7d,0x738e,0x39c7,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xf77e,0xc5db,0xc5db,0xe71d,0xffff,0xffff,0xffff,0xffff,0xc618,0x6b0e,0x4209,0x2945,0x0000,0x4a6a,0xc5ba,0xc5db,0xe6fd,0xffff,0xffff,0xffff,0xf7be,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xf77e,0x9cb5,0x8c13,0xcdfb,0xdebd,0xef5e,0xffdf,0xffff,0xce1b,0xde9c,0xffff,0x2104,0x5aeb,0xef3e,0xc5fb,0xc5ba,0xce1b,0xffdf,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0x2104,0x528a,0xcdfb,0xc5ba,0xc5ba,0xc5ba,0xc5db,0xc5da,0xde9c,0xffff,0x2104,0x5aeb,0xffff,0xffdf,0xdebd,0xd65c,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0x2104,0x5aeb,0xffff,0xf77e,0xe6fd,0xd67c,0xce1b,0xc5ba,0xde9c,0xffff,0x2104,0x5aeb,0xffff,0xffff,0xffff,0xffff,0xf7be,0x9492,0x630c,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xf79e,0xef5e,0xffff,0x2104,0x5aeb,0xffff,0xffff,0xffff,0xffff,0xffff,0xce1b,0xde9c,0xffff,0x2104,0x5aeb,0xffff,0xffff,0xffff,0xbdf7,0x2124,0x0000,0x0841,0xef7d,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xce1b,0xde9c,0xffff,0x2104,0x528a,0xdefb,0xdefb,0xdefb,0xffdf,0xffff,0xce1b,0xde9c,0xffff,0x2104,0x5aeb,0xffff,0xffff,0x8410,0x0020,0x0020,0x6b4d,0xe73c,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xce1b,0xde9c,0xffff,0x4a49,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x5aeb,0xffdf,0x5acb,0x0000,0x18e3,0xce59,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xce1b,0xde9c,0xffff,0xdefb,0x5aeb,0x4228,0x4228,0x4228,0x4228,0x4228,0x31a7,0x20e4,0x2104,0x2104,0x73ae,0x4a6a,0x0000,0x2946,0xbd79,0xffdf,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xce1b,0xd67c,0xf7bf,0xf7bf,0xf7bf,0xffff,0xffff,0xffff,0xffff,0xffff,0xce1b,0xde9c,0xffff,0xffff,0x6b4e,0x0000,0x3187,0xd65b,0xffdf,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xd65c,0xc5ba,0xc5ba,0xc5ba,0xc5ba,0xc5ba,0xc5ba,0xc5ba,0xc5ba,0xc5ba,0xc5ba,0xde9c,0xffff,0x9473,0x0000,0x2105,0xe73d,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xf7bf,0xde9c,0xd65c,0xd65c,0xd65c,0xd65c,0xd65c,0xd65c,0xce1b,0xce1b,0xce1b,0xdebd,0xd67c,0x3166,0x0861,0xdefb,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xe6dd,0xc5ba,0xce1a,0xbdd7,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xef5d,0xd69a,0xffff,0xffff,0xffff,0xef5e,0xc5ba,0xce3b,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0x9492,0x0000,0x4228,0xad55,0xffdf,0xd65c,0xc5fb,0xffbf,0xffff,0xffff,0x2104,0x5aeb,0xffff,0xffff,0xce59,0x2945,0xc638,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xdedb,0x6b6d,0x2104,0x0000,0x10a2,0x630c,0xad55,0xf79e,0xffff,0xffff,0x2104,0x5aeb,0xffff,0xffff,0xce79,0x0000,0xb5b6,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffdf,0xdebb,0xdefb,0xdefb,0xdefb,0xdefb,0xb5b6,0x6b6d,0x31a6,0x0000,0x0841,0x39c7,0x6b6d,0x1082,0x528a,0xdefb,0xdefb,0xce59,0x0000,0x9cd3,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xe71d,0x18c3,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x6b6d,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xf7bf,0x9493,0x83d1,0x7bb1,0x7bb1,0x8c32,0x94b3,0xa514,0x8430,0x2945,0x0000,0x18e3,0x9492,0x10a2,0x39c7,0x7bb1,0x94b3,0xa514,0x2945,0x4208,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffbf,0xf7bf,0xf7bf,0xf7bf,0xf7bf,0xef5e,0xdebd,0xbdb9,0x7b90,0x3187,0x0000,0x10a2,0x734f,0xce3b,0xf7bf,0x2104,0x5acb,0xc5ba,0xe71d,0xffff,0x7bcf,0x0841,0xf79e,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xce1b,0xc5ba,0xc5ba,0xc5ba,0xc5ba,0xc5ba,0x62ee,0x0000,0x0000,0x20e4,0x7370,0xbd9a,0xc5ba,0xc5ba,0xc5ba,0x18c3,0x4209,0xc5ba,0xdebd,0xffff,0xbdd7,0x3186,0xe73c,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xef5e,0xef3e,0xef3e,0x8411,0x18e4,0xef3e,0xb577,0x526a,0x9c94,0xc5ba,0xce1b,0xe71d,0x8c33,0x9494,0xef3e,0xce3a,0xb5b7,0xb558,0xb578,0xdefb,0xdedb,0xdefb,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0x7bef,0x0000,0xef1e,0xd65c,0xc5ba,0x0000,0x738e,0xffff,0xffff,0x31a7,0x39c7,0xffff,0xdefb,0x0000,0x0000,0x0000,0x0000,0x0000,0x2104,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0x736e,0x0000,0xc5ba,0xce1b,0xe71d,0x0000,0x7bef,0xffff,0xffff,0x31a7,0x39c7,0xffff,0xdefb,0x0000,0x62ec,0x83f1,0x9cf3,0x39c7,0x2104,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xe71d,0xce1b,0xffff,0x7baf,0x0000,0xf77e,0xffff,0xffff,0x0000,0x73af,0xef5e,0xffff,0x4208,0x4208,0xf7bf,0xd6bb,0x0000,0x9cd3,0xf7bf,0xffff,0x5aeb,0x2104,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xe6dd,0xc5ba,0xffff,0x7bef,0x0000,0xc5ba,0xe6dd,0xffff,0x0000,0x6b2e,0xd65c,0xffff,0x4208,0x3187,0xc5ba,0xacf7,0x0000,0x7bb1,0xce1b,0xffff,0x5aeb,0x2104,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xe6dd,0xc5ba,0xffff,0x7bef,0x0000,0x62cd,0x736e,0x7bef,0x0000,0x3187,0x6b2e,0x7bef,0x2104,0x3187,0xdebc,0xce3a,0x0000,0x83f1,0xce1b,0xffff,0x5aeb,0x2104,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xe6dd,0xc5ba,0xffff,0x7bef,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x3187,0xef3e,0xdefb,0x0000,0x8c32,0xce1b,0xffff,0x5aeb,0x2104,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xe6dd,0xc5ba,0xffff,0x7bef,0x0000,0xc5ba,0xe6dd,0xffff,0x0000,0x6b2e,0xd65c,0xffff,0x4208,0x3187,0xef3e,0xdefb,0x0000,0x8c32,0xce1b,0xffff,0x5aeb,0x2104,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xe6dd,0xc5ba,0xe6dd,0x736e,0x0000,0xc5ba,0xd65c,0xe6dd,0x0000,0x630d,0xd65c,0xffff,0x4208,0x3187,0xef3e,0xdefb,0x0000,0x8c32,0xce1b,0xffff,0x5aeb,0x2104,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xe6dd,0xc5ba,0xc5ba,0x62cd,0x0000,0xc5ba,0xc5ba,0xc5ba,0x0000,0x62cd,0xd65c,0xffff,0x4208,0x3187,0xef3e,0xdefb,0x0000,0x738f,0xb537,0xdefb,0x528a,0x2104,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xe6dd,0xc5ba,0xffff,0x7bef,0x0000,0xc5ba,0xe6dd,0xffff,0x0000,0x6b2e,0xd65c,0xffff,0x4208,0x3187,0xef3e,0xdefb,0x0000,0x0000,0x0000,0x0000,0x0000,0x2104,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xe6dd,0xc5ba,0xffff,0x7bef,0x0000,0xc5ba,0xe6dd,0xffff,0xc638,0xbd98,0xd65c,0xffff,0x736e,0x5a8c,0xef3e,0xf79e,0xa514,0x8c32,0x83d1,0xa514,0xa514,0xad75,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xe6dd,0xc5ba,0xffff,0xe71c,0xc638,0xc5ba,0xe6dd,0xffff,0xffff,0xd65c,0xd65c,0xffff,0xf7bf,0xc5ba,0xe6fd,0xf7bf,0xf7bf,0xd67c,0xce1b,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xe6dd,0xc5ba,0xffff,0xffff,0xffff,0xc5ba,0xe6dd,0xffff,0xffff,0xd65c,0xd65c,0xffff,0xf7bf,0xc5ba,0xc5ba,0xc5ba,0xc5ba,0xc5ba,0xce1b,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xe6dd,0xc5ba,0xffff,0xdefb,0xdedb,0xf77e,0xffbf,0xffff,0xffff,0xdebd,0xdebd,0xffff,0xffff,0xef3e,0xef3e,0xef3e,0xef3e,0xef3e,0xef5e,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffbf,0xf77e,0xffff,0x5aeb,0x0000,0x39e7,0x8c71,0xbdf7,0xdefb,0xf79e,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0x9cd3,0x2965,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0xa514,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xf7bf,0xf7bf,0xffff,0xffff,0xffff,0xe73c,0xb5b6,0x9492,0x7bef,0x630c,0x5aeb,0x5aeb,0x5aeb,0x5aeb,0x5aeb,0x5aeb,0x5aeb,0x5aeb,0x528a,0x0000,0xa514,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xde9c,0xc5ba,0xd65c,0xbdd8,0x9493,0xf7bf,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xc638,0xad75,0xffff,0xdefb,0x0000,0xa514,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xe71d,0xce3b,0xc5ba,0x2945,0x1082,0xc5ba,0xc5ba,0xc5ba,0xc5ba,0xc5ba,0xc5ba,0xc5ba,0xc5ba,0xc5ba,0xc5ba,0x4209,0x18c3,0xef3e,0xdefb,0x0000,0xa514,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffdf,0x736e,0x0000,0xb577,0xde9c,0xde9c,0xd67c,0x9473,0x18e3,0xbdd9,0xde9c,0xde9c,0xde9c,0x4a4a,0x18c3,0xef3e,0xdefb,0x0000,0xa514,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xf79f,0xef1e,0xffff,0xc618,0x0000,0x8430,0xffff,0xf7be,0x2965,0x528a,0x0000,0xdefb,0xa514,0x73af,0x7bef,0x2965,0x0861,0x738f,0xad75,0x0000,0xa514,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xd63c,0xcdfb,0xffff,0xffff,0x18e3,0x39e7,0xffff,0x7bcf,0x0000,0x5aeb,0x0000,0xdefb,0x4a6a,0x0000,0x0000,0x0000,0x0000,0x0000,0x8410,0x0000,0xa514,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xdedd,0xc5ba,0xf79e,0xffff,0x73ae,0x0000,0xbdb7,0x0020,0x4a69,0xa514,0x0000,0xdefb,0x4a6a,0x10a3,0xdefb,0x4a69,0x10a3,0xce3a,0xd69a,0x0000,0xa514,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xf77e,0xc5ba,0xe6fd,0xffff,0xdefb,0x0000,0x10a2,0x0000,0xce59,0x9493,0x0000,0xc5f9,0x4a2a,0x18c3,0xe6dd,0x52ab,0x18c3,0xef3e,0xdefb,0x0000,0x39c7,0x5aeb,0xdedb,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xcdfb,0xd65c,0xffff,0xe6dd,0x39c8,0x0000,0x3187,0xf7bf,0x8c32,0x0000,0xacf7,0x4209,0x18c3,0xc5ba,0x528a,0x18c3,0xef3e,0xdefb,0x0000,0x0000,0x0000,0xc638,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xdedd,0xc5ba,0xf79e,0xc5db,0x630d,0x0000,0x62ed,0xf7bf,0x8c32,0x0000,0xd6bb,0x4a4a,0x18c3,0xf7bf,0x5acb,0x18c3,0xef3e,0xdefb,0x0000,0xa514,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xf7bf,0xc5ba,0xcdfb,0xbd9a,0x1082,0x0000,0x1082,0xef7e,0x8c32,0x0000,0xdefb,0x4a6a,0x18c3,0xffff,0x5acb,0x18c3,0xd65c,0xbdb8,0x0000,0xa514,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xd67c,0xc5ba,0x8411,0x0000,0x6b4d,0x0000,0x8410,0x8c32,0x0000,0xdefb,0x4a6a,0x18c3,0xffff,0x5acb,0x18c3,0xc5ba,0xacf7,0x0000,0xa514,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xe6dd,0xc5ba,0x39a7,0x1082,0xe6fd,0x4a2a,0x0861,0x736e,0x0000,0xdefb,0x4a6a,0x0020,0x4228,0x18c3,0x0020,0x39e8,0x94b2,0x0000,0xa514,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xc5fb,0xb558,0x0000,0x5acb,0xef3e,0xb559,0x2104,0x0020,0x0000,0xdefb,0x62ed,0x18c3,0x2104,0x0861,0x0000,0x18e4,0x8c71,0x0000,0xa514,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xef3e,0xc5ba,0x9452,0x0000,0x9493,0xef3e,0xc5ba,0xce59,0x0841,0x0000,0xdefb,0xde9c,0xce1b,0xffff,0x5acb,0x18c3,0xef3e,0xdefb,0x0000,0xa514,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xd65c,0xc5fb,0x5aeb,0x0000,0xb558,0xe6fd,0xc5ba,0xf7bf,0xa515,0x62ed,0xc5fa,0xcdfb,0xc5db,0xd65c,0x528a,0x18c3,0xef3e,0xdefb,0x0000,0xa514,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffdf,0xc5da,0xde9c,0x5aeb,0x2124,0xce1b,0xc5db,0xc5ba,0xf7bf,0xdebd,0xce1b,0xce1b,0xc5db,0xc5da,0xce1b,0x7bcf,0x4209,0xef3e,0xdefb,0x0000,0xa514,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xef3e,0xc5ba,0xef3e,0xffff,0xef7d,0xf79f,0xc5db,0xc5ba,0xf7bf,0xffff,0xffff,0xffff,0xde9c,0xce1b,0xffff,0xf7bf,0xc5ba,0xef3e,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xde9c,0xc5ba,0xffdf,0xffff,0xffff,0xffff,0xf77e,0xe6dd,0xffdf,0xffff,0xffff,0xffff,0xde9c,0xce1b,0xffff,0xf7bf,0xc5ba,0xef3e,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xde9c,0xce1b,0xffff,0xffff,0xffff,0xffff,0xef5d,0x9cd3,0x7bef,0xa534,0xef7d,0xffff,0xe6fd,0xde9c,0xffff,0xf7bf,0xc5ba,0xef3e,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffdf,0xffff,0xffff,0xffff,0xad55,0x0861,0x0000,0x0000,0x0000,0x1082,0x9cf3,0xffff,0xffff,0xffff,0xffff,0xffff,0x9492,0x2104,0xe71c,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xdefb,0x0841,0x1082,0xa534,0xdefb,0xad75,0x2965,0x0000,0x94b2,0xffff,0xffff,0xffff,0xffff,0x7bef,0x0000,0xdefb,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffdf,0x6b4e,0x0000,0xa535,0xffdf,0xffff,0xffff,0xf79e,0x2965,0x0841,0xdefb,0xffff,0xffff,0xffff,0x7bef,0x0000,0xdefb,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xef3e,0xc5db,0x4209,0x0000,0xbd9a,0xc5fb,0xef1e,0xffff,0xffff,0xd69a,0x0000,0x630c,0xe71d,0xce1b,0xffbf,0x7bef,0x0000,0xdefb,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xf7bf,0xc5db,0xc5fb,0x83f0,0x0000,0x4a6a,0x8c32,0x9474,0xb576,0xc638,0xc638,0x3186,0x0861,0xad56,0x9474,0xbdf8,0x630c,0x0000,0xad55,0xc638,0xef7d,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xdedd,0xc5ba,0xef5e,0xf79e,0x4a49,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0xc638,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xde9c,0xc5ba,0xffff,0xffff,0xffff,0xd6ba,0xad75,0x9cd3,0x7bb1,0x8c32,0xa514,0x8430,0x0000,0x528a,0x7bb1,0x9cf3,0x528a,0x0000,0x8c71,0xa514,0xe73c,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xe6fd,0xc5ba,0xd67c,0xef5e,0xef3d,0xf77e,0xf77e,0xf77e,0xce3b,0xc5db,0xf77e,0xef5e,0x0000,0x5aac,0xc5ba,0xef3e,0x7baf,0x0000,0xdefb,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xd65c,0xc5ba,0x4a2a,0x20e4,0xc5ba,0xc5ba,0xc5ba,0xc5ba,0xc5ba,0xc5ba,0xc5ba,0x18c3,0x3187,0xc5ba,0xc5ba,0x62cd,0x0000,0xdefb,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xf7bf,0x41e8,0x0841,0xef3e,0xef3e,0xef3e,0xe6fd,0xc5ba,0xde9c,0xef3e,0x18e4,0x39a7,0xc5ba,0xe6fd,0x738f,0x0000,0xdefb,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0x5acb,0x0000,0xef5d,0xffff,0xffff,0xffff,0xc5ba,0xdebc,0xffff,0x1082,0x4229,0xc5ba,0xf7bf,0x7bef,0x0000,0xdefb,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xde9c,0xce1b,0xffff,0x8c71,0x0000,0xb596,0xffff,0xffff,0xffff,0xce1b,0xd65c,0xdefb,0x0000,0x630d,0xc5ba,0xf7bf,0xe71c,0xc638,0xf7be,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xd65c,0xc5db,0xffff,0xd69a,0x0000,0x5acb,0xffff,0xffff,0xffff,0xce1b,0xd65c,0x8410,0x0000,0xa515,0xc5ba,0xf7bf,0xffff,0x94b2,0xc638,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xde9c,0xc5ba,0xffdf,0xffff,0x2965,0x0020,0xc618,0xffff,0xffff,0xc5fb,0xad57,0x0861,0x2124,0xdebc,0xc5ba,0xf7bf,0x8c71,0x0000,0x39e7,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xe6fd,0xc5ba,0xef5e,0xffff,0xce59,0x0020,0x0861,0x73ae,0x9cf3,0x526b,0x0020,0x0861,0xc638,0xffbf,0xf77e,0x7bef,0x0000,0x2124,0xe73c,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xf79f,0xc5ba,0xde9c,0xffff,0xffff,0xb5b6,0x2104,0x0000,0x0000,0x0000,0x3186,0xd69a,0xffff,0xffff,0xce5a,0x18a3,0x39e7,0xe73c,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xce3b,0xc5da,0xf77e,0xffff,0xffff,0xffff,0xce39,0x9474,0xb578,0xffff,0xffff,0xffff,0xe6fd,0xc5ba,0xbd98,0xf7be,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xf79e,0xc5da,0xc5db,0xdedd,0xef3e,0xdebc,0xc5db,0xc5db,0xf77e,0xffff,0xffff,0xe6dd,0xc5ba,0xce1b,0xffdf,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xef5e,0xce1b,0xc5ba,0xc5ba,0xc5ba,0xd63b,0xf79f,0xd6ba,0xffff,0xffdf,0xcdfb,0xd65c,0xffdf,0xef5d,0xffdf,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xf79f,0xf77e,0xffbf,0xffff,0x9cd3,0x0020,0xad55,0xffff,0xffbf,0xffff,0xffdf,0x39e7,0x4a69,0xf79e,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xc618,0x18c3,0x0000,0x8430,0xffff,0xffff,0xffdf,0x52aa,0x0000,0x3186,0xe71c,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0x73ae,0x5aeb,0x5aeb,0x5aeb,0x5acb,0x5aeb,0x5aeb,0x4228,0x0000,0x0000,0x4228,0xe71c,0xffff,0xffdf,0x7bef,0x0000,0x1082,0x9cd3,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0x2104,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0020,0x7bef,0xef7d,0xffff,0xbdf7,0x2104,0x0000,0x2965,0xd69a,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xf77e,0xcdfb,0xc5ba,0xe6fd,0xffff,0xffff,0xffff,0xad57,0x2925,0x0000,0x1082,0xd69a,0xffff,0xf7be,0x8410,0x0841,0xdedb,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xdebd,0xde9c,0xde9c,0xde9c,0xde9c,0xde9c,0xde9c,0xd65c,0xc5ba,0xc5ba,0xd65c,0xffbf,0xffff,0xffff,0xe6dd,0x83d1,0x39a7,0xe6fd,0xffff,0xffff,0xffff,0xef5d,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xce1b,0xc5ba,0xc5ba,0xc5ba,0xc5ba,0xc5ba,0xc5ba,0xc5ba,0xc5ba,0xc5ba,0x3187,0x7bb1,0xe6dd,0xffdf,0x5aeb,0x2104,0xce1b,0xc5ba,0xa516,0x7bcf,0xef7d,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0x0000,0x6b0d,0xc5ba,0xc5fb,0x5acb,0x2104,0xffff,0xe6dd,0x7bb1,0x0000,0xdefb,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xa534,0x4228,0xffdf,0x0000,0x7bef,0xef3e,0xd65c,0x5aeb,0x2104,0xffff,0xffff,0x9cf4,0x0000,0xdefb,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xef5d,0x5aeb,0x0000,0x10a3,0xd6ba,0x0000,0x6b4e,0xce1b,0xffff,0x5aeb,0x2104,0xe6dd,0xffdf,0xa514,0x0000,0xdefb,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0x4208,0x0000,0x39a8,0xc61a,0xffff,0x0000,0x6b4e,0xce1b,0xffff,0x5aeb,0x18e4,0xc5ba,0xf7bf,0xa514,0x0000,0xdefb,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xef3e,0xb577,0xa514,0xc5ba,0xe6dd,0xffff,0x0000,0x6b4e,0xce1b,0xffff,0x5aeb,0x10a3,0x9474,0xbdf8,0x7bef,0x0000,0xad55,0xc638,0xef7d,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffdf,0xd67c,0xacf7,0xcdfb,0xf79f,0xc5ba,0xe6dd,0xffff,0x0000,0x6b4e,0xce1b,0xffff,0x5aeb,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0xc638,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xd65c,0x20e4,0x4a4a,0xffbf,0xffff,0xc5ba,0xe6dd,0xffff,0x0000,0x6b4e,0xce1b,0xffff,0x5aeb,0x1082,0x7bb1,0x9cf3,0x632c,0x0000,0x8c71,0xa514,0xe73c,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xf7bf,0x18e4,0x5aeb,0xffff,0xffff,0xc5ba,0xe6dd,0xffff,0x0000,0x6b4e,0xcdfb,0xf77e,0x5aab,0x18e3,0xc5ba,0xef3e,0x9cb3,0x0000,0xdefb,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xf7bf,0xffff,0x2104,0x4228,0xc638,0xc638,0x9474,0xad56,0xc638,0x0000,0x528b,0x9474,0x9474,0x31a7,0x18c3,0xc5ba,0xc5ba,0x7bb1,0x0000,0xdefb,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xce1b,0xde9c,0xffff,0x4a69,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x18e3,0xc5ba,0xe6fd,0x9493,0x0000,0xdefb,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xce1b,0xde9c,0xffff,0xf79e,0xa534,0xa514,0xa514,0x7bb1,0x8c52,0xa514,0x0000,0x4209,0x83d1,0xa514,0x39c7,0x18e4,0xc5ba,0xf7bf,0xa514,0x0000,0xdefb,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xce1b,0xd65c,0xf77e,0xf77e,0xf77e,0xf77e,0xf77e,0xc5ba,0xde9c,0xf77e,0x0000,0x6b2e,0xce1b,0xffff,0x5aeb,0x18e4,0xc5ba,0xf7bf,0xb5b6,0x4228,0xe73c,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xd67c,0xc5ba,0xc5ba,0xc5ba,0xc5ba,0xc5ba,0xc5ba,0xc5ba,0xc5ba,0xc5ba,0xc5ba,0xc5ba,0xce1b,0xffff,0xdedb,0xb5b7,0xc5ba,0xf7bf,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xef3e,0xef3e,0xef3e,0xef3e,0xef3e,0xef3e,0xc5ba,0xd67c,0xef3e,0xef3e,0xd65c,0xce1b,0xffff,0xffff,0xef3e,0xc5ba,0xf7bf,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xc5ba,0xe6dd,0xffff,0xffff,0xde9c,0xce1b,0xffff,0xffff,0xef5e,0xa4d5,0x0000,0xa514,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xc638,0xad75,0xef5d,0xf79e,0xffff,0xffff,0xffff,0xc638,0x0000,0xa514,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0x632c,0x0000,0x0841,0x4a69,0x9cf3,0xe73c,0xffff,0xc638,0x0000,0xa514,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0x632c,0x0000,0x18c3,0x1082,0x0000,0x0000,0x3186,0x5aeb,0x0000,0xa514,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xbdf7,0x7bef,0xd6bb,0xef5e,0xffff,0xf79e,0x3186,0x0020,0xce79,0xa4f4,0x41e9,0x0841,0x0000,0x0000,0x0861,0x528a,0x9492,0xef7d,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0x6b6d,0x0000,0xbdb8,0xc5ba,0xc5db,0xd67c,0xce5a,0x0861,0x4208,0xf77e,0xc5ba,0xe6fd,0x94b2,0x0000,0x18e3,0x0000,0x0000,0xe71c,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0x4a49,0x0861,0xdebc,0xc5ba,0xcdfb,0xc5fb,0xc5ba,0x62ed,0x0000,0xa4f5,0xc5ba,0xef3e,0xc638,0x0000,0xa514,0xef5d,0xa534,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xf77e,0xe6dd,0xffbf,0x2965,0x31a6,0xffff,0xce3b,0xc5da,0xf79e,0xef3e,0xce1a,0x0020,0x4209,0xc5ba,0xc5db,0xa4f5,0x0000,0x9cf4,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xdebd,0xc5ba,0xf7bf,0x2104,0x4228,0xffff,0xffbf,0xc5db,0xd65c,0xffff,0xffff,0x4229,0x10a2,0xc5ba,0xcdfb,0x9474,0x0000,0x9cf3,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xd65c,0xc5db,0xffff,0x2104,0x4228,0xffff,0xffff,0xe6dd,0xc5ba,0xf77e,0xffff,0x738e,0x0000,0xb558,0xef3e,0xc5f8,0x0000,0xa514,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xce3b,0xce3b,0xffff,0x4208,0x18e3,0xffff,0xffff,0xffdf,0xc5db,0xde9c,0xffff,0x7bef,0x0000,0xacf7,0xef3e,0xc638,0x0000,0xa514,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xce1b,0xd65c,0xffff,0x632c,0x0000,0xef5d,0xffff,0xffff,0xd65c,0xcdfb,0xffff,0x6b4d,0x0000,0xb559,0xef3e,0xc638,0x0000,0xa514,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xce1b,0xd65c,0xffff,0xad55,0x0000,0x9cd3,0xffff,0xffff,0xdebd,0xc5ba,0xffdf,0x2124,0x2104,0xc5ba,0xef3e,0xc638,0x0000,0xa514,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xd65c,0xcdfb,0xffff,0xf7be,0x10a2,0x2104,0xf79e,0xffff,0xe6dd,0xc5ba,0xa4f4,0x0000,0x6b4d,0xc5ba,0xef3e,0xc638,0x0000,0xa514,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xdebc,0xc5ba,0xffdf,0xffff,0x9492,0x0000,0x4228,0xdedb,0xde9c,0x83d1,0x0861,0x10a2,0xdedc,0xc5ba,0xef3e,0xc638,0x0000,0xa514,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xef3e,0xc5ba,0xe71d,0xffff,0xffff,0x5aeb,0x0000,0x0000,0x0000,0x0000,0x1082,0xc618,0xf77e,0xc5ba,0xef3e,0xc638,0x0000,0xa514,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xc5fb,0xce1b,0xffff,0xffff,0xffff,0xbdd7,0x630d,0x4209,0x7bd0,0xef5d,0xffff,0xf77e,0xc5ba,0xef3e,0xe71c,0x7bef,0xd69a,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xe71d,0xc5ba,0xd65c,0xf7bf,0xffff,0xef3e,0xc5db,0xc5fb,0xffdf,0xffff,0xffff,0xf77e,0xc5ba,0xef3e,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xde9c,0xc5ba,0xc5ba,0xc5ba,0xc5ba,0xc5fb,0xf77e,0xffff,0xe71c,0x8430,0xbdd8,0xc5ba,0xef3e,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xef7e,0xdebd,0xde9c,0xe71d,0xffdf,0xffff,0xe73c,0x18c3,0x0000,0x0020,0xb557,0xf79f,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0x6b4d,0x0000,0x73ae,0x0020,0x3186,0xffdf,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffbf,0xc619,0x0020,0x528a,0xffff,0x7bcf,0x0000,0x94b2,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffdf,0xcdfb,0x41e9,0x0000,0xc639,0xffff,0xf7be,0x2104,0x1082,0xe71c,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xdebc,0x9474,0x0000,0x39c8,0xce3b,0xffff,0xffff,0xb596,0x0000,0x52aa,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xf7bf,0xc5ba,0x2104,0x0020,0xc5f9,0xc5ba,0xe71d,0xffff,0xffff,0x52aa,0x0000,0xad55,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xd67c,0x62cd,0x0000,0x6b6d,0xffff,0xce1b,0xc5fb,0xffbf,0xffff,0xe71c,0x1082,0x10a2,0xef5d,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xf77e,0x9cb5,0x0000,0x18c3,0xef7d,0xffff,0xef5e,0xc5ba,0xd67c,0xffff,0xffff,0x94b2,0x0000,0x52aa,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xc5fa,0x2105,0x0000,0xad55,0xffff,0xffff,0xffff,0xd67c,0xc5ba,0xef3e,0xffff,0xffff,0x39c7,0x0000,0xa514,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xe6dd,0x524b,0x0000,0x52aa,0xffff,0xffff,0xffff,0xffff,0xffbf,0xc5fb,0xc5fb,0xffdf,0xffff,0xdedb,0x0841,0x0861,0xdedb,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xf79f,0x7bb1,0x0000,0x18c3,0xef5d,0xffff,0xffff,0xffff,0xffff,0xffff,0xe71d,0xc5ba,0xd67c,0xffff,0xffff,0x9492,0x0000,0x4208,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xce3b,0x5acd,0x0020,0xbdd7,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xd65c,0xc5ba,0xef3e,0xffff,0xffff,0x5aeb,0xdedb,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xdebc,0xc5ba,0xd67c,0xc618,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xf7bf,0xc5db,0xc5db,0xf7bf,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xef3e,0xc5ba,0xcdfb,0xffdf,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xe71d,0xc5ba,0xd65c,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xe6dd,0xc5da,0xef7e,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xde9c,0xf7bf,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xf77e,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xce79,0x528a,0xd69a,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xc638,0x0000,0xa514,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0x73ae,0x0000,0x0000,0x39e7,0xa534,0xf7be,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xc638,0x0000,0xa514,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xce59,0x5aeb,0x0861,0x0000,0x0861,0x52aa,0xa514,0xe73c,0xffff,0xffff,0xffff,0xc638,0x0000,0xa514,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xf79e,0xd67c,0xf79f,0xffff,0xffff,0xffff,0xf79e,0xa534,0x52aa,0x0861,0x0000,0x0000,0x2104,0x39c8,0x7bd0,0x8410,0x0000,0x8c71,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xdebd,0xc5ba,0xc5ba,0xd65c,0xef3e,0xffff,0xffff,0xffff,0xffff,0xf7be,0xbdd7,0x7bcf,0x39c7,0x0841,0x0000,0x0000,0x0000,0x0000,0x0000,0x18e3,0xb596,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xf79e,0xde9c,0xc5db,0xc5ba,0xc5db,0xd67c,0xef3e,0xffdf,0xffff,0xffff,0xffff,0xf77e,0xc5ba,0xbdf8,0x8c51,0x0000,0x4208,0x4a69,0x2945,0xb596,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0x9cd4,0x1082,0x0841,0x528c,0xbd79,0xce1b,0xd67c,0xe6fd,0xe6dd,0xc5ba,0xe6fd,0xc638,0x0000,0xa514,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xe71c,0x0841,0x0020,0x10a2,0x1082,0xd65b,0xd65c,0xc5db,0x9474,0x62cd,0xb559,0xc5ba,0x9474,0x0000,0x94b3,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0x9492,0x0000,0x9cf3,0xffdf,0xbdf7,0xffff,0xffff,0xffff,0x7bf0,0x0000,0x9454,0xd65c,0xa4f5,0x0000,0x94b3,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xef3e,0xc5fb,0x4a29,0x0000,0xf7be,0xffff,0xffff,0xffff,0xffff,0xffff,0xad75,0x0000,0x7bb1,0xef3e,0xc638,0x0000,0xa514,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffbf,0xc5db,0xc5da,0x3187,0x10a3,0xffdf,0xffff,0xffff,0xf77e,0xe6dd,0xffdf,0xce79,0x0000,0x62ed,0xef3e,0xc638,0x0000,0xa514,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xe71d,0xc5ba,0xef1e,0x4228,0x2104,0xffff,0xffff,0xffff,0xe6fd,0xc5ba,0xf77e,0xdefb,0x0000,0x62cd,0xef3e,0xc638,0x0000,0xa514,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xde9c,0xc5ba,0xffff,0x4228,0x18e3,0xffff,0xffff,0xffff,0xef5e,0xc5ba,0xef3e,0xdefb,0x0000,0x62cd,0xef3e,0xe71c,0x7bef,0xd69a,0xf7be,0xe73c,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xd65c,0xcdfb,0xffff,0x5acb,0x0000,0xffdf,0xffff,0xffff,0xf79e,0xc5ba,0xe6fd,0xc638,0x0000,0x7370,0xef3e,0xffff,0xe71c,0x630c,0x10a2,0x4a69,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xd65c,0xce1b,0xffff,0x738e,0x0000,0xd69a,0xffff,0xffff,0xf7bf,0xc5ba,0xe6dd,0xad55,0x0000,0x8c12,0xef3e,0xffff,0xf79e,0x1082,0x528a,0xb5b6,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xd65c,0xcdfb,0xffff,0xa534,0x0000,0x94b2,0xffff,0xffff,0xf7bf,0xc5ba,0xe6dd,0x9cf3,0x2104,0xbdd9,0xf79f,0xffff,0xffdf,0xf7be,0xffff,0xe71c,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xde9c,0xc5ba,0xffff,0xdedb,0x5aeb,0xbdf7,0xffff,0xffff,0xf77e,0xc5ba,0xe71d,0xffff,0xffff,0xffbf,0xde9c,0xc5fb,0xd67c,0xbdf7,0x4a69,0x0020,0xbdf7,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xdebd,0xc5ba,0xf79f,0xffff,0xffff,0xffff,0xffff,0xffff,0xef3e,0xc5ba,0xef5e,0xffff,0xffff,0xffff,0xc5fb,0xd67c,0xef5e,0xa534,0x18e3,0x73ae,0xe73c,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xef3e,0xc5ba,0xe71d,0xffff,0xffff,0xffff,0xffff,0xffff,0xef1e,0xce1b,0xf7bf,0xffff,0xffff,0xffff,0xffff,0xffff,0xffbf,0xffdf,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xf7bf,0xde9c,0xf77e,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xf77e,0xd67c,0xc5da,0xf77e,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffdf,0xa534,0x528a,0x4228,0x738e,0xe71c,0xffff,0xffff,0xffff,0xef3e,0xcdfb,0x0000,0x5acb,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffdf,0x52aa,0x0000,0x0861,0x18c3,0x0000,0x18c3,0xdedb,0xffff,0xffff,0xffff,0xffff,0x0000,0x5aeb,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0x9492,0x0000,0x632c,0xf7be,0xffff,0xb5b6,0x0841,0x3186,0xffff,0xffff,0xffff,0xffff,0x0000,0x5aeb,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xe71d,0x10a2,0x20e4,0xdebc,0xffbf,0xffff,0xffff,0x73ae,0x0000,0xbdd7,0xc5ba,0xde9c,0xffff,0x0000,0x5aeb,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xd67c,0x9474,0x0000,0x7370,0xc5ba,0xcdfb,0xf7bf,0xffff,0xd6ba,0x0000,0x6b6d,0xc5ba,0xde9c,0xffff,0x0000,0x5aeb,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xe71d,0xc5ba,0x7bd0,0x0000,0xd69a,0xef5e,0xc5db,0xce3b,0xffff,0xffff,0x1082,0x4a49,0xc5ba,0xde9c,0xffff,0x0000,0x5aeb,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xcdfb,0xce1b,0x6b6d,0x0000,0xf79e,0xffff,0xdedd,0xc5ba,0xef7e,0xffff,0x2104,0x4228,0xc5ba,0xde9c,0xffff,0x0000,0x5aeb,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xf77e,0xc5ba,0xe71d,0x5aeb,0x0000,0xffff,0xffff,0xf7bf,0xc5ba,0xdebd,0xffff,0x10a2,0x4a69,0xc5ba,0xde9c,0xffff,0x0000,0x5aeb,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xe71d,0xc5ba,0xf79f,0x5aeb,0x0000,0xffff,0xffff,0xffff,0xc5fb,0xd65c,0xe71c,0x0000,0x6b6d,0xc5ba,0xde9c,0xffff,0x0000,0x5aeb,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xdebd,0xc5ba,0xffff,0x6b6d,0x0000,0xe73c,0xffff,0xffff,0xce1b,0xd65c,0x9cd3,0x0000,0xb596,0xc5ba,0xde9c,0xffff,0x0000,0x5aeb,0xdedb,0x9cd3,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xde9c,0xc5ba,0xffff,0x9492,0x0000,0xbdf7,0xffff,0xffff,0xc5fb,0xd67c,0x2965,0x1082,0xf7be,0xc5ba,0xde9c,0xd69a,0x0000,0x0841,0x0000,0x0000,0xdefb,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xde9c,0xc5ba,0xffff,0xbdf7,0x0000,0x8430,0xffff,0xffbf,0xc5ba,0x8c52,0x0000,0x8410,0xf7be,0x7370,0x2945,0x0000,0x0000,0x18c3,0x6b6d,0xb596,0xf7be,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xdebd,0xc5ba,0xffdf,0xf7be,0x4228,0x6b4d,0xffff,0xe71d,0xbd79,0x18c3,0x1082,0x6b6d,0x10a2,0x0000,0x0861,0x632d,0x0000,0x5aeb,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xe71d,0xc5ba,0xf77e,0xffff,0xffff,0xffff,0xffff,0xce3b,0x5aac,0x0000,0x0000,0x0000,0x2124,0x7370,0xbd9a,0xc5ba,0x0000,0x5acb,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xf77e,0xc5ba,0xe6fd,0xffff,0xffff,0xffff,0xef3e,0xc5ba,0x9cb4,0x0000,0x2945,0x8c33,0xc5ba,0xc5ba,0xcdfb,0xdebd,0x0000,0x5aeb,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xd65c,0xdebc,0xffff,0xffff,0xffff,0xcdfb,0xc5fb,0xdebd,0x9474,0xc5ba,0xc5fb,0xdebc,0xc5ba,0xde9c,0xffff,0x0000,0x5aeb,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xdedd,0xc5ba,0xc5ba,0xc5ba,0xce1b,0xe71d,0xffff,0xffff,0xc5ba,0xde9c,0xffff,0x7bef,0xad75,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xef5e,0xc5ba,0xce3b,0xef5e,0xffff,0xffff,0xffff,0xffff,0xc5ba,0xde9c,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xf77e,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xc5ba,0xde9c,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xdedb,0xb596,0x9cf3,0x7bef,0x7bef,0x736e,0x73af,0xa514,0xad55,0xc638,0xe71c,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xdefb,0x632c,0x18c3,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x31a6,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0x9cd3,0x0020,0x0000,0x18e3,0x632c,0xa514,0xc638,0xdefb,0xdefb,0xdefb,0xd69a,0xc638,0x9cf3,0x738e,0x8c51,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xdefb,0x0020,0x18c3,0x9cd4,0xe6fd,0xe6dd,0xe6dd,0xe6dd,0xe6dd,0xef3e,0xef3e,0xf77e,0xffbf,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xf7bf,0x8c32,0x0000,0x4a2a,0xc5ba,0xc5ba,0xc5ba,0xc5ba,0xc5ba,0xc5ba,0xc5ba,0xc5ba,0xc5ba,0xce3b,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xe71d,0xc5da,0xa4d6,0x0000,0x0000,0x4a49,0xb576,0xef7e,0xf7bf,0xf7bf,0xf79f,0xf77e,0xef1e,0xdebd,0xe6fd,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xf7bf,0xc5da,0xcdfb,0xef3e,0x94b2,0x1082,0x0000,0x0000,0x2104,0xffdf,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xef3e,0xc5ba,0xde9c,0xffff,0xffff,0xef7d,0x94b2,0x4208,0x7bcf,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xf79f,0xc5ba,0xc5ba,0xd67c,0xef7e,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xe71d,0xc5fb,0xc5ba,0xc5ba,0xce1b,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffdf,0xe71d,0xd65c,0xe6dd,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xf7be,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xe73c,0x8410,0x18e3,0xf79e,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xf79e,0xad55,0x52aa,0x0841,0x0000,0x0841,0xc618,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xe71c,0xd69a,0xbdd7,0x94b2,0x6b4d,0x39c7,0x0020,0x0000,0x0000,0x2104,0x8c51,0xef7d,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0x4208,0x0000,0x0000,0x0000,0x0000,0x0000,0x2945,0x630d,0x9c94,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0x8410,0x4228,0x630c,0x8430,0xad35,0xc5fa,0xc5db,0xc5ba,0xc5db,0xf77e,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffbf,0xf79f,0xef7e,0xe71d,0xdebc,0xd63c,0xc5da,0xc5ba,0xc5ba,0xce1b,0xe6fd,0xffdf,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xd65c,0xc5ba,0xc5ba,0xc5ba,0xc5ba,0xc5ba,0xce1b,0xdebd,0xf77e,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xe6dd,0xd65c,0xde9c,0xe6fd,0xef5e,0xffdf,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0x2104,0x0000,0x0000,0xdefb,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0x2104,0x0000,0x0000,0xdefb,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0x2104,0x0000,0x0000,0xdefb,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xce1b,0xc5ba,0xc5ba,0xf7bf,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xce1b,0xc5ba,0xc5ba,0xf7bf,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xce1b,0xc5ba,0xc5ba,0x8c51,0x7bef,0x7bef,0xef7d,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0x2104,0x0000,0x0000,0xdefb,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0x2104,0x0000,0x0000,0xdefb,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xe71d,0xe6dd,0xe6dd,0x8c71,0x7bef,0x7bef,0xef7d,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xce1b,0xc5ba,0xc5ba,0xf7bf,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xce1b,0xc5ba,0xc5ba,0xf7bf,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xe71d,0xe6dd,0xe6dd,0x2104,0x0000,0x0000,0xdefb,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0x2104,0x0000,0x0000,0xdefb,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0x2104,0x0000,0x0000,0xdefb,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xce1b,0xc5ba,0xc5ba,0xf7bf,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xce1b,0xc5ba,0xc5ba,0xf7bf,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xce1b,0xc5ba,0xc5ba,0xf7bf,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,
++ 0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff};
diff --git a/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/module_licence.patch b/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/module_licence.patch
index e69de29bb2..e70fc28031 100644
--- a/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/module_licence.patch
+++ b/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/module_licence.patch
@@ -0,0 +1,96 @@
+
+#
+# Patch managed by http://www.holgerschurig.de/patcher.html
+#
+
+--- linux/drivers/usb/device/bi/sa1100.c~modulelicence
++++ linux/drivers/usb/device/bi/sa1100.c
+@@ -43,6 +43,7 @@
+ #include "../usbd-build.h"
+ #include "../usbd-module.h"
+
++MODULE_LICENSE("GPL");
+ MODULE_AUTHOR ("sl@lineo.com, tbr@lineo.com");
+ MODULE_DESCRIPTION ("USB Device SA-1100 Bus Interface");
+
+--- linux/drivers/usb/device/usbd.c~modulelicence
++++ linux/drivers/usb/device/usbd.c
+@@ -72,6 +72,7 @@
+ #include "usbd-build.h"
+ #include "usbd-module.h"
+
++MODULE_LICENSE("GPL");
+ MODULE_AUTHOR ("sl@lineo.com, tbr@lineo.com");
+ MODULE_DESCRIPTION ("USB Device Core Support");
+
+--- linux/drivers/usb/device/usbd-monitor.c~modulelicence
++++ linux/drivers/usb/device/usbd-monitor.c
+@@ -35,6 +35,7 @@
+ #include "usbd-build.h"
+ #include "usbd-module.h"
+
++MODULE_LICENSE("GPL");
+ MODULE_AUTHOR ("sl@lineo.com, tbr@lineo.com");
+ MODULE_DESCRIPTION ("USB Device Monitor");
+ USBD_MODULE_INFO ("usbd_monitor 0.3");
+--- linux/drivers/usb/device/net_fd/net-fd.c~modulelicence
++++ linux/drivers/usb/device/net_fd/net-fd.c
+@@ -33,6 +33,7 @@
+ #include "../usbd-build.h"
+ #include "../usbd-module.h"
+
++MODULE_LICENSE("GPL");
+ MODULE_AUTHOR ("sl@lineo.com, tbr@lineo.com");
+ MODULE_DESCRIPTION ("USB Device Network Function");
+
+--- linux/arch/arm/mach-sa1100/deviceinfo.c~modulelicence
++++ linux/arch/arm/mach-sa1100/deviceinfo.c
+@@ -28,6 +28,7 @@
+
+ #define MODULE_NAME "deviceinfo"
+ #define DEVINFO_DIRNAME "deviceinfo"
++MODULE_LICENSE("GPL");
+
+ static int proc_read_deviceinfo(struct file * file, char * buf,
+ size_t nbytes, loff_t *ppos);
+--- linux/arch/arm/mach-sa1100/gpio.c~modulelicence
++++ linux/arch/arm/mach-sa1100/gpio.c
+@@ -15,7 +15,7 @@
+
+ #include <asm/hardware.h>
+
+-
++MODULE_LICENSE("GPL");
+
+ static int proc_gpio_read(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+--- linux/drivers/usb/device/serial_fd/serial.c~modulelicence
++++ linux/drivers/usb/device/serial_fd/serial.c
+@@ -80,6 +80,7 @@
+ #include "../usbd-module.h"
+
+
++MODULE_LICENSE("GPL");
+ MODULE_AUTHOR ("sl@lineo.com, tbr@lineo.com");
+ MODULE_DESCRIPTION ("USB Device Serial Function");
+
+--- linux/drivers/usb/device/usbd-serialnumber.c~modulelicence
++++ linux/drivers/usb/device/usbd-serialnumber.c
+@@ -33,6 +33,7 @@
+ #include "usbd-build.h"
+ #include "usbd-module.h"
+
++MODULE_LICENSE("GPL");
+ MODULE_AUTHOR ("sl@lineo.com, tbr@lineo.com");
+ MODULE_DESCRIPTION ("USB Device Monitor");
+ USBD_MODULE_INFO ("usbd_monitor 0.2-alpha");
+--- linux/drivers/usb/device/bi/pxa.c~modulelicence
++++ linux/drivers/usb/device/bi/pxa.c
+@@ -48,6 +48,7 @@
+ #include "../usbd-build.h"
+ #include "../usbd-module.h"
+
++MODULE_LICENSE ("GPL");
+ MODULE_AUTHOR ("sl@lineo.com, tbr@lineo.com");
+ MODULE_DESCRIPTION ("Xscale USB Device Bus Interface");
+
diff --git a/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/tosa_map.patch b/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/tosa_map.patch
index e69de29bb2..feca970614 100644
--- a/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/tosa_map.patch
+++ b/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/tosa_map.patch
@@ -0,0 +1,889 @@
+
+#
+# Patch managed by http://www.holgerschurig.de/patcher.html
+#
+
+--- linux/drivers/char/tosa_keymap.c~tosa_map.patch
++++ linux/drivers/char/tosa_keymap.c
+@@ -6,348 +6,88 @@
+ #include <linux/keyboard.h>
+ #include <linux/kd.h>
+
+-u_short plain_map[] = {
++u_short plain_map[NR_KEYS] = {
+ 0xf200, 0xfb61, 0xfb62, 0xfb63, 0xfb64, 0xfb65, 0xfb66, 0xfb67,
+ 0xfb68, 0xfb69, 0xfb6a, 0xfb6b, 0xfb6c, 0xfb6d, 0xfb6e, 0xfb6f,
+ 0xfb70, 0xfb71, 0xfb72, 0xfb73, 0xfb74, 0xfb75, 0xfb76, 0xfb77,
+- 0xfb78, 0xfb79, 0xfb7a, 0xf700, 0xf201, 0xf101, 0xf703, 0xf008,
+- 0xf208, 0xf200, 0xf01b, 0xf601, 0xf603, 0xf600, 0xf602, 0xf201,
++ 0xfb78, 0xfb79, 0xfb7a, 0xf700, 0xf201, 0xf101, 0xf200, 0xf008,
++ 0xf07c, 0xf200, 0xf01b, 0xf601, 0xf603, 0xf600, 0xf602, 0xf201,
+ 0xf200, 0xf031, 0xf032, 0xf033, 0xf034, 0xf035, 0xf036, 0xf037,
+- 0xf038, 0xf039, 0xf030, 0xf02d, 0xf05e, 0xf05c, 0xf05b, 0xf040,
+- 0xf702, 0xf703, 0xf702, 0xf02b, 0xfa00, 0xf040, 0xf03f, 0xf02c,
+- 0xf02e, 0xf009, 0xf104, 0xf105, 0xf106, 0xf02f, 0xf027, 0xf301,
+- 0xf302, 0xf303, 0xf304, 0xf305, 0xf306, 0xf307, 0xf308, 0xf309,
+- 0xf300, 0xf07f, 0xf30d, 0xf30c, 0xf200, 0xf30b, 0xf30a, 0xf30e,
+- 0xf702, 0xf703, 0xf01b, 0xf020, 0xf020, 0xf310, 0xf200, 0xf03b,
+- 0xf03a, 0xf05d, 0xf02c, 0xf02e, 0xf02f, 0xf05f, 0xf200, 0xf700,
+- 0xf114, 0xf117, 0xf118, 0xf119, 0xf701, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf702, 0xf200, 0xf200,
+- 0xf702, 0xf703, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
++ 0xf038, 0xf039, 0xf030, 0xfbe4, 0xfbfc, 0xfbf6, 0xfbc4, 0xfbdc,
++ 0xfbd6, 0xfbdf, 0xf02d, 0xf02b, 0xfa00, 0xf040, 0xf03f, 0xf02c,
++ 0xf02e, 0xf009, 0xf104, 0xf105, 0xf106, 0xf02f, 0xf027, 0xf03b,
++ 0xf022, 0xf03a, 0xf023, 0xf024, 0xf025, 0xf05f, 0xf026, 0xf02a,
++ 0xf028, 0xf07f, 0xf109, 0xf03d, 0xf029, 0xf07e, 0xf03c, 0xf03e,
++ 0xf702, 0xf703, 0xf200, 0xf020, 0xf020, 0xf200, 0xf021, 0xf200,
++ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf700,
++ 0xf702, 0xf702, 0xf703, 0xf703, 0xf701, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ };
+
+-u_short shift_map[] = {
++u_short shift_map[NR_KEYS] = {
+ 0xf200, 0xfb41, 0xfb42, 0xfb43, 0xfb44, 0xfb45, 0xfb46, 0xfb47,
+ 0xfb48, 0xfb49, 0xfb4a, 0xfb4b, 0xfb4c, 0xfb4d, 0xfb4e, 0xfb4f,
+ 0xfb50, 0xfb51, 0xfb52, 0xfb53, 0xfb54, 0xfb55, 0xfb56, 0xfb57,
+- 0xfb58, 0xfb59, 0xfb5a, 0xf700, 0xf201, 0xf101, 0xf702, 0xf008,
+- 0xf208, 0xf200, 0xf01b, 0xf601, 0xf603, 0xf600, 0xf602, 0xf201,
+- 0xf200, 0xf021, 0xf022, 0xf023, 0xf024, 0xf025, 0xf026, 0xf027,
+- 0xf028, 0xf029, 0xf07e, 0xf03d, 0xf07e, 0xf07c, 0xf07b, 0xf060,
+- 0xf702, 0xf703, 0xf702, 0xf02b, 0xfa00, 0xf040, 0xf03f, 0xf03b,
+- 0xf03a, 0xf009, 0xf104, 0xf105, 0xf106, 0xf03f, 0xf022, 0xf301,
+- 0xf302, 0xf303, 0xf304, 0xf305, 0xf306, 0xf307, 0xf308, 0xf309,
+- 0xf300, 0xf07f, 0xf30d, 0xf30c, 0xf200, 0xf30b, 0xf30a, 0xf30e,
+- 0xf702, 0xf703, 0xf01b, 0xf020, 0xf020, 0xf310, 0xf200, 0xf02b,
+- 0xf02a, 0xf07d, 0xf03c, 0xf03e, 0xf03f, 0xf05f, 0xf200, 0xf700,
+- 0xf114, 0xf117, 0xf20b, 0xf20a, 0xf701, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf702, 0xf200, 0xf200,
+- 0xf702, 0xf703, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
++ 0xfb58, 0xfb59, 0xfb5a, 0xf700, 0xf201, 0xf101, 0xf200, 0xf008,
++ 0xf07c, 0xf200, 0xf01b, 0xf601, 0xf603, 0xf600, 0xf602, 0xf201,
++ 0xf200, 0xf031, 0xf032, 0xf033, 0xf034, 0xf035, 0xf036, 0xf037,
++ 0xf038, 0xf039, 0xf030, 0xfbe4, 0xfbfc, 0xfbf6, 0xfbc4, 0xfbdc,
++ 0xfbd6, 0xfbdf, 0xf02d, 0xf02b, 0xfa00, 0xf040, 0xf03f, 0xf02c,
++ 0xf02e, 0xf009, 0xf104, 0xf105, 0xf106, 0xf02f, 0xf027, 0xf03b,
++ 0xf022, 0xf03a, 0xf023, 0xf024, 0xf025, 0xf05f, 0xf026, 0xf02a,
++ 0xf028, 0xf07f, 0xf109, 0xf03d, 0xf029, 0xf07e, 0xf03c, 0xf03e,
++ 0xf702, 0xf703, 0xf200, 0xf020, 0xf020, 0xf200, 0xf021, 0xf200,
++ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf700,
++ 0xf702, 0xf702, 0xf703, 0xf703, 0xf701, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ };
+
+-u_short ctrl_map[] = {
++u_short ctrl_map[NR_KEYS] = {
+ 0xf200, 0xf001, 0xf002, 0xf003, 0xf004, 0xf005, 0xf006, 0xf007,
+ 0xf008, 0xf009, 0xf00a, 0xf00b, 0xf00c, 0xf00d, 0xf00e, 0xf00f,
+ 0xf010, 0xf011, 0xf012, 0xf013, 0xf014, 0xf015, 0xf016, 0xf017,
+ 0xf018, 0xf019, 0xf01a, 0xf700, 0xf201, 0xf101, 0xf200, 0xf008,
+- 0xf208, 0xf200, 0xf01b, 0xf601, 0xf603, 0xf600, 0xf602, 0xf201,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf702, 0xf703, 0xf702, 0xf02b, 0xfa00, 0xf040, 0xf03f, 0xf200,
+- 0xf200, 0xf009, 0xf104, 0xf105, 0xf106, 0xf200, 0xf200, 0xf301,
+- 0xf302, 0xf303, 0xf304, 0xf305, 0xf306, 0xf307, 0xf308, 0xf309,
+- 0xf300, 0xf07f, 0xf30d, 0xf30c, 0xf200, 0xf30b, 0xf30a, 0xf30e,
+- 0xf702, 0xf703, 0xf01b, 0xf020, 0xf000, 0xf310, 0xf200, 0xf200,
++ 0xf07c, 0xf200, 0xf01b, 0xf601, 0xf603, 0xf600, 0xf602, 0xf201,
++ 0xf200, 0xf031, 0xf032, 0xf033, 0xf034, 0xf035, 0xf036, 0xf037,
++ 0xf038, 0xf039, 0xf030, 0xfbe4, 0xfbfc, 0xfbf6, 0xfbc4, 0xfbdc,
++ 0xfbd6, 0xfbdf, 0xf02d, 0xf02b, 0xfa00, 0xf040, 0xf03f, 0xf02c,
++ 0xf02e, 0xf009, 0xf104, 0xf105, 0xf106, 0xf02f, 0xf027, 0xf03b,
++ 0xf022, 0xf03a, 0xf023, 0xf024, 0xf025, 0xf05f, 0xf026, 0xf02a,
++ 0xf028, 0xf07f, 0xf109, 0xf03d, 0xf029, 0xf07e, 0xf03c, 0xf03e,
++ 0xf702, 0xf703, 0xf200, 0xf000, 0xf000, 0xf200, 0xf021, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf700,
+- 0xf114, 0xf117, 0xf118, 0xf119, 0xf701, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf702, 0xf200, 0xf200,
+- 0xf702, 0xf703, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
++ 0xf702, 0xf702, 0xf703, 0xf703, 0xf701, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ };
+
+-u_short shift_ctrl_map[] = {
++u_short shift_ctrl_map[NR_KEYS] = {
+ 0xf200, 0xf001, 0xf002, 0xf003, 0xf004, 0xf005, 0xf006, 0xf007,
+ 0xf008, 0xf009, 0xf00a, 0xf00b, 0xf00c, 0xf00d, 0xf00e, 0xf00f,
+ 0xf010, 0xf011, 0xf012, 0xf013, 0xf014, 0xf015, 0xf016, 0xf017,
+ 0xf018, 0xf019, 0xf01a, 0xf700, 0xf201, 0xf101, 0xf200, 0xf008,
+- 0xf208, 0xf200, 0xf01b, 0xf601, 0xf603, 0xf600, 0xf602, 0xf201,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf702, 0xf703, 0xf702, 0xf02b, 0xfa00, 0xf040, 0xf03f, 0xf200,
+- 0xf200, 0xf009, 0xf104, 0xf105, 0xf106, 0xf200, 0xf200, 0xf301,
+- 0xf302, 0xf303, 0xf304, 0xf305, 0xf306, 0xf307, 0xf308, 0xf309,
+- 0xf300, 0xf07f, 0xf30d, 0xf30c, 0xf200, 0xf30b, 0xf30a, 0xf30e,
+- 0xf702, 0xf703, 0xf01b, 0xf020, 0xf020, 0xf310, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf700,
+- 0xf114, 0xf117, 0xf118, 0xf119, 0xf701, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf702, 0xf200, 0xf200,
+- 0xf702, 0xf703, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+-};
+-
+-u_short alt_map[] = {
+- 0xf200, 0xf021, 0xf02d, 0xf863, 0xf023, 0xf033, 0xf024, 0xf025,
+- 0xf05f, 0xf038, 0xf026, 0xf02a, 0xf028, 0xf03d, 0xf02b, 0xf039,
+- 0xf030, 0xf031, 0xf034, 0xf040, 0xf035, 0xf037, 0xf876, 0xf032,
+- 0xf878, 0xf036, 0xf87a, 0xf700, 0xf201, 0xf101, 0xf200, 0xf07f,
+- 0xf208, 0xf200, 0xf01b, 0xf601, 0xf603, 0xf600, 0xf602, 0xf07d,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf702, 0xf703, 0xf702, 0xf02b, 0xfa00, 0xf040, 0xf03f, 0xf029,
+- 0xf03c, 0xf207, 0xf104, 0xf105, 0xf106, 0xf200, 0xf07e, 0xf301,
+- 0xf302, 0xf303, 0xf304, 0xf305, 0xf306, 0xf307, 0xf308, 0xf309,
+- 0xf300, 0xf07f, 0xf30d, 0xf30c, 0xf200, 0xf30b, 0xf30a, 0xf30e,
+- 0xf702, 0xf703, 0xf01b, 0xf000, 0xf020, 0xf310, 0xf200, 0xf200,
++ 0xf07c, 0xf200, 0xf01b, 0xf601, 0xf603, 0xf600, 0xf602, 0xf201,
++ 0xf200, 0xf031, 0xf032, 0xf033, 0xf034, 0xf035, 0xf036, 0xf037,
++ 0xf038, 0xf039, 0xf030, 0xfbe4, 0xfbfc, 0xfbf6, 0xfbc4, 0xfbdc,
++ 0xfbd6, 0xfbdf, 0xf02d, 0xf02b, 0xfa00, 0xf040, 0xf03f, 0xf02c,
++ 0xf02e, 0xf009, 0xf104, 0xf105, 0xf106, 0xf02f, 0xf027, 0xf03b,
++ 0xf022, 0xf03a, 0xf023, 0xf024, 0xf025, 0xf05f, 0xf026, 0xf02a,
++ 0xf028, 0xf07f, 0xf109, 0xf03d, 0xf029, 0xf07e, 0xf03c, 0xf03e,
++ 0xf702, 0xf703, 0xf200, 0xf020, 0xf020, 0xf200, 0xf021, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf700,
+- 0xf114, 0xf117, 0xf118, 0xf119, 0xf701, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf702, 0xf200, 0xf200,
+- 0xf702, 0xf703, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+- 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
++ 0xf702, 0xf702, 0xf703, 0xf703, 0xf701, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ };
+
+ ushort *key_maps[MAX_NR_KEYMAPS] = {
+ plain_map, shift_map, 0, 0,
+- ctrl_map, shift_ctrl_map, 0, 0,
+- alt_map, 0
++ ctrl_map, shift_ctrl_map, 0
+ };
+
+-unsigned int keymap_count = 5;
++unsigned int keymap_count = 4;
+
+
+ /*
+--- linux/drivers/char/tosa_keymap.map~tosa_map.patch
++++ linux/drivers/char/tosa_keymap.map
+@@ -8,9 +8,8 @@
+ # 1 for [SHIFT]
+ # 4 for [CTRL] , to symbol input for debug
+ # 5 for [CTRL] [SHIFT] , to symbol input for debug
+-# 8 for [ALT]
+ #
+-keymaps 0,1,4,5,8
++keymaps 0,1,4,5
+ #
+ # loadkeys --mktable iris_keymap.map
+ #
+@@ -20,60 +19,38 @@
+ #
+ #
+ keycode 1 = a
+- alt keycode 1 = exclam
+ keycode 2 = b
+- alt keycode 2 = minus
+ keycode 3 = c
+ keycode 4 = d
+- alt keycode 4 = numbersign
+ keycode 5 = e
+- alt keycode 5 = three
+ keycode 6 = f
+- alt keycode 6 = dollar
+ keycode 7 = g
+- alt keycode 7 = percent
+ keycode 8 = h
+- alt keycode 8 = underscore
+ keycode 9 = i
+- alt keycode 9 = eight
+ keycode 10 = j
+- alt keycode 10 = ampersand
+ keycode 11 = k
+- alt keycode 11 = asterisk
+ keycode 12 = l
+- alt keycode 12 = parenleft
+ keycode 13 = m
+- alt keycode 13 = equal
+ keycode 14 = n
+- alt keycode 14 = plus
+ keycode 15 = o
+- alt keycode 15 = nine
+ keycode 16 = p
+- alt keycode 16 = zero
+ keycode 17 = q
+- alt keycode 17 = one
+ keycode 18 = r
+- alt keycode 18 = four
+ keycode 19 = s
+- alt keycode 19 = at
+ keycode 20 = t
+- alt keycode 20 = five
+ keycode 21 = u
+- alt keycode 21 = seven
+ keycode 22 = v
+ keycode 23 = w
+- alt keycode 23 = two
+ keycode 24 = x
+ keycode 25 = y
+- alt keycode 25 = six
+ keycode 26 = z
+ keycode 27 = Shift
+ keycode 28 = Return
+ keycode 29 = F2
+-keycode 30 = Alt Control
++keycode 30 =
+ keycode 31 = BackSpace
+- alt keycode 31 = Delete
+-keycode 32 = Num_Lock
++# for Debug , keycode 32(F3, Sym) --> Control
++keycode 32 = bar
+ keycode 33 =
+ # (Cancel:34) F9 -> Escape
+ keycode 34 = Escape
+@@ -83,100 +60,84 @@
+ keycode 38 = Right
+ # (OK:39) F4 -> Return
+ keycode 39 = Return
+- alt keycode 39 = braceright
+ keycode 40 =
+-keycode 41 = one exclam
+-keycode 42 = two quotedbl
+-keycode 43 = three numbersign
+-keycode 44 = four dollar
+-keycode 45 = five percent
+-keycode 46 = six ampersand
+-keycode 47 = seven apostrophe
+-keycode 48 = eight parenleft
+-keycode 49 = nine parenright
+-keycode 50 = zero asciitilde
+-keycode 51 = minus equal
+-keycode 52 = asciicircum asciitilde
+-keycode 53 = backslash bar
+-keycode 54 = bracketleft braceleft
+-keycode 55 = at grave
+-keycode 56 = Control
+-keycode 57 = Alt
+-keycode 58 = Control
++keycode 41 = one
++keycode 42 = two
++keycode 43 = three
++keycode 44 = four
++keycode 45 = five
++keycode 46 = six
++keycode 47 = seven
++keycode 48 = eight
++keycode 49 = nine
++keycode 50 = zero
++keycode 51 = +adiaeresis
++keycode 52 = +udiaeresis
++keycode 53 = +odiaeresis
++keycode 54 = +Adiaeresis
++keycode 55 = +Udiaeresis
++keycode 56 = +Odiaeresis
++keycode 57 = +ssharp
++keycode 58 = minus
+ keycode 59 = plus
+ keycode 60 = Shift_Lock
+ keycode 61 = at
+ keycode 62 = question
+-keycode 63 = comma semicolon
+- alt keycode 63 = parenright
+-keycode 64 = period colon
+- alt keycode 64 = less
++keycode 63 = comma
++keycode 64 = period
+ keycode 65 = Tab
+- alt keycode 65 = Caps_Lock
+ keycode 66 = F5
+ keycode 67 = F6
+ keycode 68 = F7
+-keycode 69 = slash question
+-keycode 70 = apostrophe quotedbl
+- alt keycode 70 = asciitilde
+-keycode 71 = KP_1
+-keycode 72 = KP_2
+-keycode 73 = KP_3
+-keycode 74 = KP_4
+-keycode 75 = KP_5
+-keycode 76 = KP_6
+-keycode 77 = KP_7
+-keycode 78 = KP_8
+-keycode 79 = KP_9
+-keycode 80 = KP_0
++keycode 69 = slash
++keycode 70 = apostrophe
++keycode 71 = semicolon
++keycode 72 = quotedbl
++keycode 73 = colon
++keycode 74 = numbersign
++keycode 75 = dollar
++keycode 76 = percent
++# 2001/7/5 old keycode 77 = asciicircum
++keycode 77 = underscore
++keycode 78 = ampersand
++keycode 79 = asterisk
++keycode 80 = parenleft
+ keycode 81 = Delete
+-keycode 82 = KP_Divide
+-keycode 83 = KP_Multiply
+-keycode 84 =
+-keycode 85 = KP_Subtract
+-keycode 86 = KP_Add
+-keycode 87 = KP_Enter
++keycode 82 = F10
++keycode 83 = equal
++keycode 84 = parenright
++keycode 85 = asciitilde
++keycode 86 = less
++keycode 87 = greater
+ # (Activity:88) -> Ctrl
+ keycode 88 = Control
+ # (Contacts:89) -> Alt
+ keycode 89 = Alt
+-keycode 90 = Escape
++keycode 90 =
+ # (select:91) F11 -> space
+ keycode 91 = space
+- alt keycode 91 = nul
++ control keycode 91 = nul
+ keycode 92 = space
+ control keycode 92 = nul
+-keycode 93 = KP_Period
+-keycode 94 =
+-keycode 95 = semicolon plus
+-keycode 96 = colon asterisk
+-keycode 97 = bracketright braceright
+-keycode 98 = comma less
+-keycode 99 = period greater
+-keycode 100 = slash question
+-keycode 101 = underscore underscore
++keycode 93 =
++keycode 94 = exclam
++keycode 95 =
++keycode 96 =
++keycode 97 =
++keycode 98 =
++keycode 99 =
++keycode 100 =
++keycode 101 =
+ keycode 102 =
+ keycode 103 = Shift
+-keycode 104 = Find
+-keycode 105 = Select
+-keycode 106 = Prior
+- shift keycode 106 = Scroll_Backward
+-keycode 107 = Next
+- shift keycode 107 = Scroll_Forward
++keycode 104 = Control
++keycode 105 = Control
++keycode 106 = Alt
++keycode 107 = Alt
+ keycode 108 = AltGr
+ keycode 109 =
+ keycode 110 =
+ keycode 111 =
+-keycode 112 =
+-keycode 113 =
+-keycode 114 =
+-keycode 115 =
+-keycode 116 =
+-keycode 117 = Control
+-keycode 118 =
+-keycode 119 =
+-keycode 120 = Control
+-keycode 121 = Alt
+
+
+ string F1 = "\033[[A"
+--- linux/drivers/char/tosa_rawmap.h~tosa_map.patch
++++ linux/drivers/char/tosa_rawmap.h
+@@ -22,28 +22,156 @@
+
+
+ static unsigned char rawkeytable_table_NormalLower[(NR_KEYCODES+1)] = {
+-KEY_IGN,KEY_IGN,SLKEY_W,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_K,SLKEY_BACK_SPACE,SLKEY_P,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_OFF,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_Q,SLKEY_E,SLKEY_T,SLKEY_Y,KEY_IGN,SLKEY_O,SLKEY_I,SLKEY_COMMA,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_RECORDER,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_A,SLKEY_D,SLKEY_G,SLKEY_U,KEY_IGN,SLKEY_L,SLKEY_ENTER,SLKEY_PERIOD,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_SYNCSTART,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_Z,SLKEY_C,SLKEY_V,SLKEY_J,SLKEY_CONTACTS,SLKEY_F9,SLKEY_F11,SLKEY_F4,SLKEY_LSHIFT,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_S,SLKEY_R,SLKEY_B,SLKEY_N,SLKEY_ACTIVITY,SLKEY_HOME,SLKEY_MINUS,SLKEY_FRONTLIGHT,KEY_IGN,SLKEY_RSHIFT,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_TAB,SLKEY_SLASH,SLKEY_H,SLKEY_M,SLKEY_F2,KEY_IGN,SLKEY_UP,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_2ND,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_X,SLKEY_F,SLKEY_SPACE,SLKEY_APOSTROPHE,SLKEY_MAIL,SLKEY_LEFT,SLKEY_DOWN,SLKEY_RIGHT,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN};
++KEY_IGN, KEY_IGN, SLKEY_W, KEY_IGN, KEY_IGN, KEY_IGN, SLKEY_K, SLKEY_BACK_SPACE,
++SLKEY_P, KEY_IGN, KEY_IGN, KEY_IGN, SLKEY_OFF, KEY_IGN, KEY_IGN, KEY_IGN,
++KEY_IGN, SLKEY_Q, SLKEY_E, SLKEY_T, SLKEY_Y, KEY_IGN, SLKEY_O, SLKEY_I,
++SLKEY_COMMA, KEY_IGN, KEY_IGN, KEY_IGN, SLKEY_RECORDER, KEY_IGN, KEY_IGN, KEY_IGN,
++KEY_IGN, SLKEY_A, SLKEY_D, SLKEY_G, SLKEY_U, KEY_IGN, SLKEY_L, SLKEY_ENTER,
++SLKEY_PERIOD, KEY_IGN, KEY_IGN, KEY_IGN, SLKEY_SYNCSTART,KEY_IGN, KEY_IGN, KEY_IGN,
++KEY_IGN, SLKEY_Z, SLKEY_C, SLKEY_V, SLKEY_J, SLKEY_CONTACTS, SLKEY_F9, SLKEY_F11,
++SLKEY_F4, SLKEY_LSHIFT, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN,
++KEY_IGN, SLKEY_S, SLKEY_R, SLKEY_B, SLKEY_N, SLKEY_ACTIVITY, SLKEY_HOME,SLKEY_MINUS,
++SLKEY_FRONTLIGHT,KEY_IGN, SLKEY_RSHIFT, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN,
++KEY_IGN, SLKEY_TAB, SLKEY_SLASH, SLKEY_H, SLKEY_M, SLKEY_F2, KEY_IGN, SLKEY_UP,
++KEY_IGN, KEY_IGN, KEY_IGN, SLKEY_2ND, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN,
++KEY_IGN, SLKEY_X, SLKEY_F, SLKEY_SPACE, SLKEY_APOSTROPHE,SLKEY_MAIL, SLKEY_LEFT,SLKEY_DOWN,
++SLKEY_RIGHT, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN,
++KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN,
++KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN
++};
+
+ static unsigned char rawkeytable_table_NormalUpper[(NR_KEYCODES+1)] = {
+-KEY_IGN,KEY_IGN,SLKEY_W,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_K,SLKEY_BACK_SPACE,SLKEY_P,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_OFF,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_Q,SLKEY_E,SLKEY_T,SLKEY_Y,KEY_IGN,SLKEY_O,SLKEY_I,SLKEY_COMMA,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_RECORDER,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_A,SLKEY_D,SLKEY_G,SLKEY_U,KEY_IGN,SLKEY_L,SLKEY_ENTER,SLKEY_PERIOD,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_SYNCSTART,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_Z,SLKEY_C,SLKEY_V,SLKEY_J,SLKEY_CONTACTS,SLKEY_F9,SLKEY_F11,SLKEY_F4,SLKEY_LSHIFT,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_S,SLKEY_R,SLKEY_B,SLKEY_N,SLKEY_ACTIVITY,SLKEY_HOME,SLKEY_MINUS,SLKEY_FRONTLIGHT,KEY_IGN,SLKEY_RSHIFT,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_TAB,SLKEY_SLASH,SLKEY_H,SLKEY_M,SLKEY_F2,KEY_IGN,SLKEY_UP,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_2ND,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_X,SLKEY_F,SLKEY_SPACE,SLKEY_APOSTROPHE,SLKEY_MAIL,SLKEY_LEFT,SLKEY_DOWN,SLKEY_RIGHT,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN};
++KEY_IGN, KEY_IGN, SLKEY_W, KEY_IGN, KEY_IGN, KEY_IGN, SLKEY_K, SLKEY_BACK_SPACE,
++SLKEY_P, KEY_IGN, KEY_IGN, KEY_IGN, SLKEY_OFF, KEY_IGN, KEY_IGN, KEY_IGN,
++KEY_IGN, SLKEY_Q, SLKEY_E, SLKEY_T, SLKEY_Y, KEY_IGN, SLKEY_O, SLKEY_I,
++SLKEY_SEMICOLON,KEY_IGN, KEY_IGN, KEY_IGN, SLKEY_RECORDER, KEY_IGN, KEY_IGN, KEY_IGN,
++KEY_IGN, SLKEY_A, SLKEY_D, SLKEY_G, SLKEY_U, KEY_IGN, SLKEY_L, SLKEY_ENTER,
++SLKEY_COLON, KEY_IGN, KEY_IGN, KEY_IGN, SLKEY_SYNCSTART,KEY_IGN, KEY_IGN, KEY_IGN,
++KEY_IGN, SLKEY_Z, SLKEY_C, SLKEY_V, SLKEY_J, SLKEY_CONTACTS, SLKEY_F9, SLKEY_F11,
++SLKEY_F4, SLKEY_LSHIFT, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN,
++KEY_IGN, SLKEY_S, SLKEY_R, SLKEY_B, SLKEY_N, SLKEY_ACTIVITY, SLKEY_HOME, SLKEY_MINUS,
++SLKEY_FRONTLIGHT, KEY_IGN, SLKEY_RSHIFT, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN,
++KEY_IGN, SLKEY_TAB, SLKEY_QUESTION, SLKEY_H, SLKEY_M, SLKEY_F2, KEY_IGN, SLKEY_UP,
++KEY_IGN, KEY_IGN, KEY_IGN, SLKEY_2ND, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN,
++KEY_IGN, SLKEY_X, SLKEY_F, SLKEY_SPACE, SLKEY_QUOTEDBL, SLKEY_MAIL, SLKEY_LEFT, SLKEY_DOWN,
++SLKEY_RIGHT, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN,
++KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN,
++KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN
++};
+
+ static unsigned char rawkeytable_table_2ndLower[(NR_KEYCODES+1)] = {
+-KEY_IGN,KEY_IGN,SLKEY_W,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_K,SLKEY_BACK_SPACE,SLKEY_P,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_OFF,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_Q,SLKEY_E,SLKEY_T,SLKEY_Y,KEY_IGN,SLKEY_O,SLKEY_I,SLKEY_COMMA,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_RECORDER,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_A,SLKEY_D,SLKEY_G,SLKEY_U,KEY_IGN,SLKEY_L,SLKEY_ENTER,SLKEY_PERIOD,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_SYNCSTART,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_Z,SLKEY_C,SLKEY_V,SLKEY_J,SLKEY_CONTACTS,SLKEY_F9,SLKEY_F11,SLKEY_F4,SLKEY_LSHIFT,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_S,SLKEY_R,SLKEY_B,SLKEY_N,SLKEY_ACTIVITY,SLKEY_HOME,SLKEY_MINUS,SLKEY_FRONTLIGHT,KEY_IGN,SLKEY_RSHIFT,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_TAB,SLKEY_SLASH,SLKEY_H,SLKEY_M,SLKEY_F2,KEY_IGN,SLKEY_UP,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_2ND,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_X,SLKEY_F,SLKEY_SPACE,SLKEY_APOSTROPHE,SLKEY_MAIL,SLKEY_LEFT,SLKEY_DOWN,SLKEY_RIGHT,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN};
++KEY_IGN, KEY_IGN, SLKEY_2, KEY_IGN, KEY_IGN, KEY_IGN, SLKEY_ASTERISK, SLKEY_DELETE,
++SLKEY_0, KEY_IGN, KEY_IGN, KEY_IGN, SLKEY_OFF, KEY_IGN, KEY_IGN, KEY_IGN,
++KEY_IGN, SLKEY_1, SLKEY_3, SLKEY_5, SLKEY_6, KEY_IGN, SLKEY_9, SLKEY_8,
++SLKEY_PARENRIGHT, KEY_IGN, KEY_IGN, KEY_IGN, SLKEY_RECORDER, KEY_IGN, KEY_IGN, KEY_IGN,
++KEY_IGN, SLKEY_EXCLAM, SLKEY_NUMBERSIGN, SLKEY_PERCENT,SLKEY_7, KEY_IGN, SLKEY_PARENLEFT,SLKEY_GREATER,
++SLKEY_LESS, KEY_IGN, KEY_IGN, KEY_IGN, SLKEY_SYNCSTART,KEY_IGN, KEY_IGN, KEY_IGN,
++KEY_IGN, SLKEY_F10, SLKEY_F6, SLKEY_F7, SLKEY_AMPERSAND,SLKEY_CONTACTS, SLKEY_F9, SLKEY_F11,
++SLKEY_F4, SLKEY_LSHIFT, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN,
++KEY_IGN, SLKEY_AT, SLKEY_4, SLKEY_MINUS, SLKEY_PLUS, SLKEY_ACTIVITY, SLKEY_HOME, SLKEY_MINUS,
++SLKEY_FRONTLIGHT, KEY_IGN, SLKEY_RSHIFT, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN,
++KEY_IGN, SLKEY_CAPS_LOCK,SLKEY_NUMLOCK, SLKEY_ASCIICIRCUM, SLKEY_EQUAL, SLKEY_F2, KEY_IGN, SLKEY_UP,
++KEY_IGN, KEY_IGN, KEY_IGN, SLKEY_2ND, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN,
++KEY_IGN, SLKEY_F5, SLKEY_DOLLAR, SLKEY_SYM, SLKEY_ASCIITILDE, SLKEY_MAIL, SLKEY_LEFT, SLKEY_DOWN,
++SLKEY_RIGHT, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN,
++KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN,
++KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN
++};
+
+ static unsigned char rawkeytable_table_2ndUpper[(NR_KEYCODES+1)] = {
+-KEY_IGN,KEY_IGN,SLKEY_W,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_K,SLKEY_BACK_SPACE,SLKEY_P,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_OFF,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_Q,SLKEY_E,SLKEY_T,SLKEY_Y,KEY_IGN,SLKEY_O,SLKEY_I,SLKEY_COMMA,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_RECORDER,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_A,SLKEY_D,SLKEY_G,SLKEY_U,KEY_IGN,SLKEY_L,SLKEY_ENTER,SLKEY_PERIOD,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_SYNCSTART,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_Z,SLKEY_PRINTSCREEN,SLKEY_V,SLKEY_J,SLKEY_CONTACTS,SLKEY_F9,SLKEY_F11,SLKEY_F4,SLKEY_LSHIFT,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_S,SLKEY_R,SLKEY_B,SLKEY_N,SLKEY_ACTIVITY,SLKEY_HOME,SLKEY_MINUS,SLKEY_FRONTLIGHT,KEY_IGN,SLKEY_RSHIFT,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_TAB,SLKEY_SLASH,SLKEY_H,SLKEY_M,SLKEY_F2,KEY_IGN,SLKEY_UP,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_2ND,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_X,SLKEY_F,SLKEY_SPACE,SLKEY_APOSTROPHE,SLKEY_MAIL,SLKEY_LEFT,SLKEY_DOWN,SLKEY_RIGHT,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN};
++KEY_IGN, KEY_IGN, SLKEY_2, KEY_IGN, KEY_IGN, KEY_IGN, SLKEY_ASTERISK, SLKEY_DELETE,
++SLKEY_0, KEY_IGN, KEY_IGN, KEY_IGN, SLKEY_OFF, KEY_IGN, KEY_IGN, KEY_IGN,
++KEY_IGN, SLKEY_1, SLKEY_3, SLKEY_5, SLKEY_6, KEY_IGN, SLKEY_9, SLKEY_8,
++SLKEY_PARENRIGHT, KEY_IGN, KEY_IGN, KEY_IGN, SLKEY_RECORDER, KEY_IGN, KEY_IGN, KEY_IGN,
++KEY_IGN, SLKEY_EXCLAM, SLKEY_NUMBERSIGN, SLKEY_PERCENT,SLKEY_7, KEY_IGN, SLKEY_PARENLEFT,SLKEY_GREATER,
++SLKEY_LESS, KEY_IGN, KEY_IGN, KEY_IGN, SLKEY_SYNCSTART,KEY_IGN, KEY_IGN, KEY_IGN,
++KEY_IGN, SLKEY_F10, SLKEY_F6, SLKEY_F7, SLKEY_AMPERSAND,SLKEY_CONTACTS, SLKEY_F9, SLKEY_F11,
++SLKEY_F4, SLKEY_LSHIFT, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN,
++KEY_IGN, SLKEY_AT, SLKEY_4, SLKEY_MINUS, SLKEY_PLUS, SLKEY_ACTIVITY, SLKEY_HOME, SLKEY_MINUS,
++SLKEY_FRONTLIGHT, KEY_IGN, SLKEY_RSHIFT, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN,
++KEY_IGN, SLKEY_CAPS_LOCK,SLKEY_NUMLOCK, SLKEY_ASCIICIRCUM, SLKEY_EQUAL, SLKEY_F2, KEY_IGN, SLKEY_UP,
++KEY_IGN, KEY_IGN, KEY_IGN, SLKEY_2ND, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN,
++KEY_IGN, SLKEY_F5, SLKEY_DOLLAR, SLKEY_SYM, SLKEY_ASCIITILDE, SLKEY_MAIL, SLKEY_LEFT, SLKEY_DOWN,
++SLKEY_RIGHT, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN,
++KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN,
++KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN
++};
+
+ static unsigned char rawkeytable_table_NumlockLower[(NR_KEYCODES+1)] = {
+-KEY_IGN,KEY_IGN,SLKEY_W,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_K,SLKEY_BACK_SPACE,SLKEY_P,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_OFF,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_Q,SLKEY_E,SLKEY_T,SLKEY_Y,KEY_IGN,SLKEY_O,SLKEY_I,SLKEY_COMMA,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_RECORDER,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_A,SLKEY_D,SLKEY_G,SLKEY_U,KEY_IGN,SLKEY_L,SLKEY_ENTER,SLKEY_PERIOD,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_SYNCSTART,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_Z,SLKEY_C,SLKEY_V,SLKEY_J,SLKEY_CONTACTS,SLKEY_F9,SLKEY_F11,SLKEY_F4,SLKEY_LSHIFT,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_S,SLKEY_R,SLKEY_B,SLKEY_N,SLKEY_ACTIVITY,SLKEY_HOME,SLKEY_MINUS,SLKEY_FRONTLIGHT,KEY_IGN,SLKEY_RSHIFT,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_TAB,SLKEY_SLASH,SLKEY_H,SLKEY_M,SLKEY_F2,KEY_IGN,SLKEY_UP,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_2ND,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_X,SLKEY_F,SLKEY_SPACE,SLKEY_APOSTROPHE,SLKEY_MAIL,SLKEY_LEFT,SLKEY_DOWN,SLKEY_RIGHT,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN};
++KEY_IGN, KEY_IGN, SLKEY_2, KEY_IGN, KEY_IGN, KEY_IGN, SLKEY_ASTERISK, SLKEY_BACK_SPACE,
++SLKEY_0, KEY_IGN, KEY_IGN, KEY_IGN, SLKEY_OFF, KEY_IGN, KEY_IGN, KEY_IGN,
++KEY_IGN, SLKEY_1, SLKEY_3, SLKEY_5, SLKEY_6, KEY_IGN, SLKEY_9, SLKEY_8,
++SLKEY_PARENRIGHT, KEY_IGN, KEY_IGN, KEY_IGN, SLKEY_RECORDER, KEY_IGN, KEY_IGN, KEY_IGN,
++KEY_IGN, SLKEY_EXCLAM, SLKEY_NUMBERSIGN, SLKEY_PERCENT, SLKEY_7, KEY_IGN, SLKEY_PARENLEFT,SLKEY_GREATER,
++SLKEY_LESS, KEY_IGN, KEY_IGN, KEY_IGN, SLKEY_SYNCSTART, KEY_IGN, KEY_IGN, KEY_IGN,
++KEY_IGN, SLKEY_F10, SLKEY_F6, SLKEY_F7, SLKEY_AMPERSAND, SLKEY_CONTACTS, SLKEY_F9, SLKEY_F11,
++SLKEY_F4, SLKEY_LSHIFT, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN,
++KEY_IGN, SLKEY_AT, SLKEY_4, SLKEY_MINUS, SLKEY_PLUS, SLKEY_ACTIVITY, SLKEY_HOME, SLKEY_MINUS,
++SLKEY_FRONTLIGHT, KEY_IGN, SLKEY_RSHIFT, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN,
++KEY_IGN, SLKEY_TAB, SLKEY_SLASH, SLKEY_ASCIICIRCUM, SLKEY_EQUAL, SLKEY_F2, KEY_IGN, SLKEY_UP,
++KEY_IGN, KEY_IGN, KEY_IGN, SLKEY_2ND, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN,
++KEY_IGN, SLKEY_F5, SLKEY_DOLLAR, SLKEY_SPACE, SLKEY_ASCIITILDE, SLKEY_MAIL, SLKEY_LEFT, SLKEY_DOWN,
++SLKEY_RIGHT, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN,
++KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN,
++KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN
++};
+
+ static unsigned char rawkeytable_table_NumlockUpper[(NR_KEYCODES+1)] = {
+-KEY_IGN,KEY_IGN,SLKEY_W,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_K,SLKEY_BACK_SPACE,SLKEY_P,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_OFF,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_Q,SLKEY_E,SLKEY_T,SLKEY_Y,KEY_IGN,SLKEY_O,SLKEY_I,SLKEY_COMMA,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_RECORDER,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_A,SLKEY_D,SLKEY_G,SLKEY_U,KEY_IGN,SLKEY_L,SLKEY_ENTER,SLKEY_PERIOD,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_SYNCSTART,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_Z,SLKEY_C,SLKEY_V,SLKEY_J,SLKEY_CONTACTS,SLKEY_F9,SLKEY_F11,SLKEY_F4,SLKEY_LSHIFT,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_S,SLKEY_R,SLKEY_B,SLKEY_N,SLKEY_ACTIVITY,SLKEY_HOME,SLKEY_MINUS,SLKEY_FRONTLIGHT,KEY_IGN,SLKEY_RSHIFT,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_TAB,SLKEY_SLASH,SLKEY_H,SLKEY_M,SLKEY_F2,KEY_IGN,SLKEY_UP,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_2ND,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_X,SLKEY_F,SLKEY_SPACE,SLKEY_APOSTROPHE,SLKEY_MAIL,SLKEY_LEFT,SLKEY_DOWN,SLKEY_RIGHT,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN};
++KEY_IGN, KEY_IGN, SLKEY_2, KEY_IGN, KEY_IGN, KEY_IGN, SLKEY_ASTERISK, SLKEY_BACK_SPACE,
++SLKEY_0, KEY_IGN, KEY_IGN, KEY_IGN, SLKEY_OFF, KEY_IGN, KEY_IGN, KEY_IGN,
++KEY_IGN, SLKEY_1, SLKEY_3, SLKEY_5, SLKEY_6, KEY_IGN, SLKEY_9, SLKEY_8,
++SLKEY_PARENRIGHT, KEY_IGN, KEY_IGN, KEY_IGN, SLKEY_RECORDER, KEY_IGN, KEY_IGN, KEY_IGN,
++KEY_IGN, SLKEY_EXCLAM, SLKEY_NUMBERSIGN, SLKEY_PERCENT, SLKEY_7, KEY_IGN, SLKEY_PARENLEFT,SLKEY_GREATER,
++SLKEY_LESS, KEY_IGN, KEY_IGN, KEY_IGN, SLKEY_SYNCSTART, KEY_IGN, KEY_IGN, KEY_IGN,
++KEY_IGN, SLKEY_F10, SLKEY_F6, SLKEY_F7, SLKEY_AMPERSAND, SLKEY_CONTACTS, SLKEY_F9, SLKEY_F11,
++SLKEY_F4, SLKEY_LSHIFT, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN,
++KEY_IGN, SLKEY_AT, SLKEY_4, SLKEY_MINUS, SLKEY_PLUS, SLKEY_ACTIVITY, SLKEY_HOME, SLKEY_MINUS,
++SLKEY_FRONTLIGHT, KEY_IGN, SLKEY_RSHIFT, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN,
++KEY_IGN, SLKEY_TAB, SLKEY_QUESTION, SLKEY_ASCIICIRCUM, SLKEY_EQUAL, SLKEY_F2, KEY_IGN, SLKEY_UP,
++KEY_IGN, KEY_IGN, KEY_IGN, SLKEY_2ND, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN,
++KEY_IGN, SLKEY_F5, SLKEY_DOLLAR, SLKEY_SPACE, SLKEY_ASCIITILDE, SLKEY_MAIL, SLKEY_LEFT, SLKEY_DOWN,
++SLKEY_RIGHT, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN,
++KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN,
++KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN
++};
+
+ static unsigned char rawkeytable_table_Num2ndLower[(NR_KEYCODES+1)] = {
+-KEY_IGN,KEY_IGN,SLKEY_W,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_K,SLKEY_BACK_SPACE,SLKEY_P,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_OFF,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_Q,SLKEY_E,SLKEY_T,SLKEY_Y,KEY_IGN,SLKEY_O,SLKEY_I,SLKEY_COMMA,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_RECORDER,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_A,SLKEY_D,SLKEY_G,SLKEY_U,KEY_IGN,SLKEY_L,SLKEY_ENTER,SLKEY_PERIOD,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_SYNCSTART,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_Z,SLKEY_C,SLKEY_V,SLKEY_J,SLKEY_CONTACTS,SLKEY_F9,SLKEY_F11,SLKEY_F4,SLKEY_LSHIFT,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_S,SLKEY_R,SLKEY_B,SLKEY_N,SLKEY_ACTIVITY,SLKEY_HOME,SLKEY_MINUS,SLKEY_FRONTLIGHT,KEY_IGN,SLKEY_RSHIFT,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_TAB,SLKEY_SLASH,SLKEY_H,SLKEY_M,SLKEY_F2,KEY_IGN,SLKEY_UP,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_2ND,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_X,SLKEY_F,SLKEY_SPACE,SLKEY_APOSTROPHE,SLKEY_MAIL,SLKEY_LEFT,SLKEY_DOWN,SLKEY_RIGHT,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN};
++KEY_IGN, KEY_IGN, SLKEY_W, KEY_IGN, KEY_IGN, KEY_IGN, SLKEY_K, SLKEY_DELETE,
++SLKEY_P, KEY_IGN, KEY_IGN, KEY_IGN, SLKEY_OFF, KEY_IGN, KEY_IGN, KEY_IGN,
++KEY_IGN, SLKEY_Q, SLKEY_E, SLKEY_T, SLKEY_Y, KEY_IGN, SLKEY_O, SLKEY_I,
++SLKEY_COMMA, KEY_IGN, KEY_IGN, KEY_IGN, SLKEY_RECORDER, KEY_IGN, KEY_IGN, KEY_IGN,
++KEY_IGN, SLKEY_A, SLKEY_D, SLKEY_G, SLKEY_U, KEY_IGN, SLKEY_L, SLKEY_ENTER,
++SLKEY_PERIOD, KEY_IGN, KEY_IGN, KEY_IGN, SLKEY_SYNCSTART,KEY_IGN, KEY_IGN, KEY_IGN,
++KEY_IGN, SLKEY_Z, SLKEY_F6, SLKEY_F7, SLKEY_J, SLKEY_CONTACTS, SLKEY_F9, SLKEY_F11,
++SLKEY_F4, SLKEY_LSHIFT, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN,
++KEY_IGN, SLKEY_S, SLKEY_R, SLKEY_B, SLKEY_N, SLKEY_ACTIVITY, SLKEY_HOME,SLKEY_MINUS,
++SLKEY_FRONTLIGHT,KEY_IGN, SLKEY_RSHIFT, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN,
++KEY_IGN, SLKEY_CAPS_LOCK, SLKEY_NUMLOCK, SLKEY_H, SLKEY_M, SLKEY_F2, KEY_IGN, SLKEY_UP,
++KEY_IGN, KEY_IGN, KEY_IGN, SLKEY_2ND, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN,
++KEY_IGN, SLKEY_F5, SLKEY_F, SLKEY_SYM, SLKEY_APOSTROPHE,SLKEY_MAIL, SLKEY_LEFT,SLKEY_DOWN,
++SLKEY_RIGHT, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN,
++KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN,
++KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN
++};
+
+ static unsigned char rawkeytable_table_Num2ndUpper[(NR_KEYCODES+1)] = {
+-KEY_IGN,KEY_IGN,SLKEY_W,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_K,SLKEY_BACK_SPACE,SLKEY_P,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_OFF,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_Q,SLKEY_E,SLKEY_T,SLKEY_Y,KEY_IGN,SLKEY_O,SLKEY_I,SLKEY_COMMA,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_RECORDER,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_A,SLKEY_D,SLKEY_G,SLKEY_U,KEY_IGN,SLKEY_L,SLKEY_ENTER,SLKEY_PERIOD,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_SYNCSTART,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_Z,SLKEY_PRINTSCREEN,SLKEY_V,SLKEY_J,SLKEY_CONTACTS,SLKEY_F9,SLKEY_F11,SLKEY_F4,SLKEY_LSHIFT,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_S,SLKEY_R,SLKEY_B,SLKEY_N,SLKEY_ACTIVITY,SLKEY_HOME,SLKEY_MINUS,SLKEY_FRONTLIGHT,KEY_IGN,SLKEY_RSHIFT,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_TAB,SLKEY_SLASH,SLKEY_H,SLKEY_M,SLKEY_F2,KEY_IGN,SLKEY_UP,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_2ND,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,SLKEY_X,SLKEY_F,SLKEY_SPACE,SLKEY_APOSTROPHE,SLKEY_MAIL,SLKEY_LEFT,SLKEY_DOWN,SLKEY_RIGHT,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN,KEY_IGN};
++KEY_IGN, KEY_IGN, SLKEY_W, KEY_IGN, KEY_IGN, KEY_IGN, SLKEY_K, SLKEY_DELETE,
++SLKEY_P, KEY_IGN, KEY_IGN, KEY_IGN, SLKEY_OFF, KEY_IGN, KEY_IGN, KEY_IGN,
++KEY_IGN, SLKEY_Q, SLKEY_E, SLKEY_T, SLKEY_Y, KEY_IGN, SLKEY_O, SLKEY_I,
++SLKEY_SEMICOLON,KEY_IGN, KEY_IGN, KEY_IGN, SLKEY_RECORDER, KEY_IGN, KEY_IGN, KEY_IGN,
++KEY_IGN, SLKEY_A, SLKEY_D, SLKEY_G, SLKEY_U, KEY_IGN, SLKEY_L, SLKEY_ENTER,
++SLKEY_COLON, KEY_IGN, KEY_IGN, KEY_IGN, SLKEY_SYNCSTART,KEY_IGN, KEY_IGN, KEY_IGN,
++KEY_IGN, SLKEY_Z, SLKEY_F6, SLKEY_F7, SLKEY_J, SLKEY_CONTACTS, SLKEY_F9, SLKEY_F11,
++SLKEY_F4, SLKEY_LSHIFT, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN,
++KEY_IGN, SLKEY_S, SLKEY_R, SLKEY_B, SLKEY_N, SLKEY_ACTIVITY, SLKEY_HOME,SLKEY_MINUS,
++SLKEY_FRONTLIGHT,KEY_IGN, SLKEY_RSHIFT, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN,
++KEY_IGN, SLKEY_CAPS_LOCK, SLKEY_NUMLOCK, SLKEY_H, SLKEY_M, SLKEY_F2, KEY_IGN, SLKEY_UP,
++KEY_IGN, KEY_IGN, KEY_IGN, SLKEY_2ND, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN,
++KEY_IGN, SLKEY_F5, SLKEY_F, SLKEY_SYM, SLKEY_QUOTEDBL, SLKEY_MAIL, SLKEY_LEFT,SLKEY_DOWN,
++SLKEY_RIGHT, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN,
++KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN,
++KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN
++};
+
+
+
+@@ -334,7 +462,87 @@
+
+
+ static unsigned char *state_to_keymap[STATE_NUMS_TOTAL] = {
+-rawkeytable_table_NormalLower,rawkeytable_table_NormalUpper,rawkeytable_table_2ndLower,rawkeytable_table_2ndUpper,rawkeytable_table_2ndLower,rawkeytable_table_2ndUpper,rawkeytable_table_2ndLower,rawkeytable_table_2ndUpper,rawkeytable_table_2ndLower,rawkeytable_table_2ndUpper,rawkeytable_table_NormalLower,rawkeytable_table_NormalUpper,rawkeytable_table_2ndLower,rawkeytable_table_2ndUpper,rawkeytable_table_2ndLower,rawkeytable_table_2ndUpper,rawkeytable_table_2ndLower,rawkeytable_table_2ndUpper,rawkeytable_table_2ndLower,rawkeytable_table_2ndUpper,rawkeytable_table_NumlockLower,rawkeytable_table_NumlockUpper,rawkeytable_table_Num2ndLower,rawkeytable_table_Num2ndUpper,rawkeytable_table_Num2ndLower,rawkeytable_table_Num2ndUpper,rawkeytable_table_Num2ndLower,rawkeytable_table_Num2ndUpper,rawkeytable_table_Num2ndLower,rawkeytable_table_Num2ndUpper,rawkeytable_table_NumlockLower,rawkeytable_table_NumlockUpper,rawkeytable_table_Num2ndLower,rawkeytable_table_Num2ndUpper,rawkeytable_table_Num2ndLower,rawkeytable_table_Num2ndUpper,rawkeytable_table_Num2ndLower,rawkeytable_table_Num2ndUpper,rawkeytable_table_Num2ndLower,rawkeytable_table_Num2ndUpper,rawkeytable_table_NormalUpper,rawkeytable_table_NormalUpper,rawkeytable_table_2ndUpper,rawkeytable_table_2ndUpper,rawkeytable_table_2ndUpper,rawkeytable_table_2ndUpper,rawkeytable_table_2ndUpper,rawkeytable_table_2ndUpper,rawkeytable_table_2ndUpper,rawkeytable_table_2ndUpper,rawkeytable_table_NormalUpper,rawkeytable_table_NormalUpper,rawkeytable_table_2ndUpper,rawkeytable_table_2ndUpper,rawkeytable_table_2ndUpper,rawkeytable_table_2ndUpper,rawkeytable_table_2ndUpper,rawkeytable_table_2ndUpper,rawkeytable_table_2ndUpper,rawkeytable_table_2ndUpper,rawkeytable_table_NumlockUpper,rawkeytable_table_NumlockUpper,rawkeytable_table_Num2ndUpper,rawkeytable_table_Num2ndUpper,rawkeytable_table_Num2ndUpper,rawkeytable_table_Num2ndUpper,rawkeytable_table_Num2ndUpper,rawkeytable_table_Num2ndUpper,rawkeytable_table_Num2ndUpper,rawkeytable_table_Num2ndUpper,rawkeytable_table_NumlockUpper,rawkeytable_table_NumlockUpper,rawkeytable_table_Num2ndUpper,rawkeytable_table_Num2ndUpper,rawkeytable_table_Num2ndUpper,rawkeytable_table_Num2ndUpper,rawkeytable_table_Num2ndUpper,rawkeytable_table_Num2ndUpper,rawkeytable_table_Num2ndUpper,rawkeytable_table_Num2ndUpper};
++rawkeytable_table_NormalLower, /*S0*/
++rawkeytable_table_NormalUpper, /*S1*/
++rawkeytable_table_2ndLower, /*S2*/
++rawkeytable_table_2ndUpper, /*S3*/
++rawkeytable_table_2ndLower, /*S4*/
++rawkeytable_table_2ndUpper, /*S5*/
++rawkeytable_table_2ndLower, /*S6*/
++rawkeytable_table_2ndUpper, /*S7*/
++rawkeytable_table_2ndLower, /*S8*/
++rawkeytable_table_2ndUpper, /*S9*/
++rawkeytable_table_NormalLower, /*S10*/
++rawkeytable_table_NormalUpper, /*S11*/
++rawkeytable_table_2ndLower, /*S12*/
++rawkeytable_table_2ndUpper, /*S13*/
++rawkeytable_table_2ndLower, /*S14*/
++rawkeytable_table_2ndUpper, /*S15*/
++rawkeytable_table_2ndLower, /*S16*/
++rawkeytable_table_2ndUpper, /*S17*/
++rawkeytable_table_2ndLower, /*S18*/
++rawkeytable_table_2ndUpper, /*S19*/
++rawkeytable_table_NumlockLower, /*S20*/
++rawkeytable_table_NumlockUpper, /*S21*/
++rawkeytable_table_Num2ndLower, /*S22*/
++rawkeytable_table_Num2ndUpper, /*S23*/
++rawkeytable_table_Num2ndLower, /*S24*/
++rawkeytable_table_Num2ndUpper, /*S25*/
++rawkeytable_table_Num2ndLower, /*S26*/
++rawkeytable_table_Num2ndUpper, /*S27*/
++rawkeytable_table_Num2ndLower, /*S28*/
++rawkeytable_table_Num2ndUpper, /*S29*/
++rawkeytable_table_NumlockLower, /*S30*/
++rawkeytable_table_NumlockUpper, /*S31*/
++rawkeytable_table_Num2ndLower, /*S32*/
++rawkeytable_table_Num2ndUpper, /*S33*/
++rawkeytable_table_Num2ndLower, /*S34*/
++rawkeytable_table_Num2ndUpper, /*S35*/
++rawkeytable_table_Num2ndLower, /*S36*/
++rawkeytable_table_Num2ndUpper, /*S37*/
++rawkeytable_table_Num2ndLower, /*S38*/
++rawkeytable_table_Num2ndUpper, /*S39*/
++rawkeytable_table_NormalUpper, /*S40*/
++rawkeytable_table_NormalUpper, /*S41*/
++rawkeytable_table_2ndUpper, /*S42*/
++rawkeytable_table_2ndUpper, /*S43*/
++rawkeytable_table_2ndUpper, /*S44*/
++rawkeytable_table_2ndUpper, /*S45*/
++rawkeytable_table_2ndUpper, /*S46*/
++rawkeytable_table_2ndUpper, /*S47*/
++rawkeytable_table_2ndUpper, /*S48*/
++rawkeytable_table_2ndUpper, /*S49*/
++rawkeytable_table_NormalUpper, /*S50*/
++rawkeytable_table_NormalUpper, /*S51*/
++rawkeytable_table_2ndUpper, /*S52*/
++rawkeytable_table_2ndUpper, /*S53*/
++rawkeytable_table_2ndUpper, /*S54*/
++rawkeytable_table_2ndUpper, /*S55*/
++rawkeytable_table_2ndUpper, /*S56*/
++rawkeytable_table_2ndUpper, /*S57*/
++rawkeytable_table_2ndUpper, /*S58*/
++rawkeytable_table_2ndUpper, /*S59*/
++rawkeytable_table_NumlockUpper, /*S60*/
++rawkeytable_table_NumlockUpper, /*S61*/
++rawkeytable_table_Num2ndUpper, /*S62*/
++rawkeytable_table_Num2ndUpper, /*S63*/
++rawkeytable_table_Num2ndUpper, /*S64*/
++rawkeytable_table_Num2ndUpper, /*S65*/
++rawkeytable_table_Num2ndUpper, /*S66*/
++rawkeytable_table_Num2ndUpper, /*S67*/
++rawkeytable_table_Num2ndUpper, /*S68*/
++rawkeytable_table_Num2ndUpper, /*S69*/
++rawkeytable_table_NumlockUpper, /*S70*/
++rawkeytable_table_NumlockUpper, /*S71*/
++rawkeytable_table_Num2ndUpper, /*S72*/
++rawkeytable_table_Num2ndUpper, /*S73*/
++rawkeytable_table_Num2ndUpper, /*S74*/
++rawkeytable_table_Num2ndUpper, /*S75*/
++rawkeytable_table_Num2ndUpper, /*S76*/
++rawkeytable_table_Num2ndUpper, /*S77*/
++rawkeytable_table_Num2ndUpper, /*S78*/
++rawkeytable_table_Num2ndUpper /*S79*/
++};
+
+
+ #endif /* ! __KEYTABLE_H_INCLUDED__ */
diff --git a/linux/openzaurus-sa-2.4.18-rmk7-pxa3-embedix20030509/1764-1.patch b/linux/openzaurus-sa-2.4.18-rmk7-pxa3-embedix20030509/1764-1.patch
index e69de29bb2..0b660f3521 100644
--- a/linux/openzaurus-sa-2.4.18-rmk7-pxa3-embedix20030509/1764-1.patch
+++ b/linux/openzaurus-sa-2.4.18-rmk7-pxa3-embedix20030509/1764-1.patch
@@ -0,0 +1,16 @@
+__arch_strncpy_from_user needs to be exported if you build the framebuffer console driver as a module.
+
+Cheers,
+
+Ian.
+
+--- linux-2.6-bkpxa.orig/arch/arm/kernel/armksyms.c 2004-02-27 10:35:29.000000000 +0000
++++ linux-2.6-bkpxa/arch/arm/kernel/armksyms.c 2004-02-27 14:55:02.000000000 +0000
+@@ -187,6 +187,7 @@
+ EXPORT_SYMBOL(__arch_copy_to_user);
+ EXPORT_SYMBOL(__arch_clear_user);
+ EXPORT_SYMBOL(__arch_strnlen_user);
++EXPORT_SYMBOL(__arch_strncpy_from_user);
+
+ /* consistent area handling */
+ EXPORT_SYMBOL(consistent_alloc);
diff --git a/linux/openzaurus-sa-2.4.18-rmk7-pxa3-embedix20030509/bluetooth-2.4.18-mh15.patch b/linux/openzaurus-sa-2.4.18-rmk7-pxa3-embedix20030509/bluetooth-2.4.18-mh15.patch
index e69de29bb2..1a7fd98653 100644
--- a/linux/openzaurus-sa-2.4.18-rmk7-pxa3-embedix20030509/bluetooth-2.4.18-mh15.patch
+++ b/linux/openzaurus-sa-2.4.18-rmk7-pxa3-embedix20030509/bluetooth-2.4.18-mh15.patch
@@ -0,0 +1,32759 @@
+diff -urN linux-2.4.18/arch/alpha/config.in linux-2.4.18-mh15/arch/alpha/config.in
+--- linux-2.4.18/arch/alpha/config.in 2001-11-21 00:49:31.000000000 +0100
++++ linux-2.4.18-mh15/arch/alpha/config.in 2004-08-01 16:26:22.000000000 +0200
+@@ -371,9 +371,7 @@
+ source drivers/usb/Config.in
+ source drivers/input/Config.in
+
+-if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+- source net/bluetooth/Config.in
+-fi
++source net/bluetooth/Config.in
+
+ mainmenu_option next_comment
+ comment 'Kernel hacking'
+diff -urN linux-2.4.18/arch/arm/config.in linux-2.4.18-mh15/arch/arm/config.in
+--- linux-2.4.18/arch/arm/config.in 2001-11-09 22:58:02.000000000 +0100
++++ linux-2.4.18-mh15/arch/arm/config.in 2004-08-01 16:26:22.000000000 +0200
+@@ -584,9 +584,7 @@
+
+ source drivers/usb/Config.in
+
+-if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+- source net/bluetooth/Config.in
+-fi
++source net/bluetooth/Config.in
+
+ mainmenu_option next_comment
+ comment 'Kernel hacking'
+diff -urN linux-2.4.18/arch/i386/config.in linux-2.4.18-mh15/arch/i386/config.in
+--- linux-2.4.18/arch/i386/config.in 2002-02-25 20:37:52.000000000 +0100
++++ linux-2.4.18-mh15/arch/i386/config.in 2004-08-01 16:26:22.000000000 +0200
+@@ -407,9 +407,7 @@
+
+ source drivers/usb/Config.in
+
+-if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+- source net/bluetooth/Config.in
+-fi
++source net/bluetooth/Config.in
+
+ mainmenu_option next_comment
+ comment 'Kernel hacking'
+diff -urN linux-2.4.18/arch/ppc/config.in linux-2.4.18-mh15/arch/ppc/config.in
+--- linux-2.4.18/arch/ppc/config.in 2002-02-25 20:37:55.000000000 +0100
++++ linux-2.4.18-mh15/arch/ppc/config.in 2004-08-01 16:26:22.000000000 +0200
+@@ -389,9 +389,7 @@
+
+ source drivers/usb/Config.in
+
+-if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+- source net/bluetooth/Config.in
+-fi
++source net/bluetooth/Config.in
+
+ mainmenu_option next_comment
+ comment 'Kernel hacking'
+diff -urN linux-2.4.18/arch/sparc/config.in linux-2.4.18-mh15/arch/sparc/config.in
+--- linux-2.4.18/arch/sparc/config.in 2001-06-12 04:15:27.000000000 +0200
++++ linux-2.4.18-mh15/arch/sparc/config.in 2004-08-01 16:26:22.000000000 +0200
+@@ -251,9 +251,7 @@
+
+ source fs/Config.in
+
+-if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+- source net/bluetooth/Config.in
+-fi
++source net/bluetooth/Config.in
+
+ mainmenu_option next_comment
+ comment 'Watchdog'
+diff -urN linux-2.4.18/arch/sparc64/config.in linux-2.4.18-mh15/arch/sparc64/config.in
+--- linux-2.4.18/arch/sparc64/config.in 2001-12-21 18:41:53.000000000 +0100
++++ linux-2.4.18-mh15/arch/sparc64/config.in 2004-08-01 16:26:22.000000000 +0200
+@@ -283,9 +283,7 @@
+
+ source drivers/usb/Config.in
+
+-if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+- source net/bluetooth/Config.in
+-fi
++source net/bluetooth/Config.in
+
+ mainmenu_option next_comment
+ comment 'Watchdog'
+diff -urN linux-2.4.18/arch/sparc64/kernel/ioctl32.c linux-2.4.18-mh15/arch/sparc64/kernel/ioctl32.c
+--- linux-2.4.18/arch/sparc64/kernel/ioctl32.c 2002-02-25 20:37:56.000000000 +0100
++++ linux-2.4.18-mh15/arch/sparc64/kernel/ioctl32.c 2004-08-01 16:26:23.000000000 +0200
+@@ -92,6 +92,7 @@
+
+ #include <net/bluetooth/bluetooth.h>
+ #include <net/bluetooth/hci.h>
++#include <net/bluetooth/rfcomm.h>
+
+ #include <linux/usb.h>
+ #include <linux/usbdevice_fs.h>
+@@ -3822,6 +3823,15 @@
+ return err;
+ }
+
++/* Bluetooth ioctls */
++#define HCIUARTSETPROTO _IOW('U', 200, int)
++#define HCIUARTGETPROTO _IOR('U', 201, int)
++
++#define BNEPCONNADD _IOW('B', 200, int)
++#define BNEPCONNDEL _IOW('B', 201, int)
++#define BNEPGETCONNLIST _IOR('B', 210, int)
++#define BNEPGETCONNINFO _IOR('B', 211, int)
++
+ struct mtd_oob_buf32 {
+ u32 start;
+ u32 length;
+@@ -3878,6 +3888,16 @@
+ return ((0 == ret) ? 0 : -EFAULT);
+ }
+
++#define CMTPCONNADD _IOW('C', 200, int)
++#define CMTPCONNDEL _IOW('C', 201, int)
++#define CMTPGETCONNLIST _IOR('C', 210, int)
++#define CMTPGETCONNINFO _IOR('C', 211, int)
++
++#define HIDPCONNADD _IOW('H', 200, int)
++#define HIDPCONNDEL _IOW('H', 201, int)
++#define HIDPGETCONNLIST _IOR('H', 210, int)
++#define HIDPGETCONNINFO _IOR('H', 211, int)
++
+ struct ioctl_trans {
+ unsigned int cmd;
+ unsigned int handler;
+@@ -4540,6 +4560,25 @@
+ COMPATIBLE_IOCTL(HCISETSCAN)
+ COMPATIBLE_IOCTL(HCISETAUTH)
+ COMPATIBLE_IOCTL(HCIINQUIRY)
++COMPATIBLE_IOCTL(HCIUARTSETPROTO)
++COMPATIBLE_IOCTL(HCIUARTGETPROTO)
++COMPATIBLE_IOCTL(RFCOMMCREATEDEV)
++COMPATIBLE_IOCTL(RFCOMMRELEASEDEV)
++COMPATIBLE_IOCTL(RFCOMMGETDEVLIST)
++COMPATIBLE_IOCTL(RFCOMMGETDEVINFO)
++COMPATIBLE_IOCTL(RFCOMMSTEALDLC)
++COMPATIBLE_IOCTL(BNEPCONNADD)
++COMPATIBLE_IOCTL(BNEPCONNDEL)
++COMPATIBLE_IOCTL(BNEPGETCONNLIST)
++COMPATIBLE_IOCTL(BNEPGETCONNINFO)
++COMPATIBLE_IOCTL(CMTPCONNADD)
++COMPATIBLE_IOCTL(CMTPCONNDEL)
++COMPATIBLE_IOCTL(CMTPGETCONNLIST)
++COMPATIBLE_IOCTL(CMTPGETCONNINFO)
++COMPATIBLE_IOCTL(HIDPCONNADD)
++COMPATIBLE_IOCTL(HIDPCONNDEL)
++COMPATIBLE_IOCTL(HIDPGETCONNLIST)
++COMPATIBLE_IOCTL(HIDPGETCONNINFO)
+ /* Misc. */
+ COMPATIBLE_IOCTL(0x41545900) /* ATYIO_CLKR */
+ COMPATIBLE_IOCTL(0x41545901) /* ATYIO_CLKW */
+diff -urN linux-2.4.18/CREDITS linux-2.4.18-mh15/CREDITS
+--- linux-2.4.18/CREDITS 2002-02-25 20:37:50.000000000 +0100
++++ linux-2.4.18-mh15/CREDITS 2004-08-01 16:26:23.000000000 +0200
+@@ -1317,6 +1317,16 @@
+ S: Provo, Utah 84606-5607
+ S: USA
+
++N: Marcel Holtmann
++E: marcel@holtmann.org
++W: http://www.holtmann.org
++D: Maintainer of the Linux Bluetooth Subsystem
++D: Author and maintainer of the various Bluetooth HCI drivers
++D: Author and maintainer of the CAPI message transport protocol driver
++D: Author and maintainer of the Bluetooth HID protocol driver
++D: Various other Bluetooth related patches, cleanups and fixes
++S: Germany
++
+ N: Rob W. W. Hooft
+ E: hooft@EMBL-Heidelberg.DE
+ D: Shared libs for graphics-tools and for the f2c compiler
+@@ -2546,6 +2556,7 @@
+ N: Aristeu Sergio Rozanski Filho
+ E: aris@conectiva.com.br
+ D: Support for EtherExpress 10 ISA (i82595) in eepro driver
++D: User level driver support for input
+ S: Conectiva S.A.
+ S: R. Tocantins, 89 - Cristo Rei
+ S: 80050-430 - Curitiba - Paraná
+diff -urN linux-2.4.18/Documentation/Configure.help linux-2.4.18-mh15/Documentation/Configure.help
+--- linux-2.4.18/Documentation/Configure.help 2002-02-25 20:37:51.000000000 +0100
++++ linux-2.4.18-mh15/Documentation/Configure.help 2004-08-01 16:26:23.000000000 +0200
+@@ -2824,14 +2824,6 @@
+
+ If unsure, say N.
+
+-HCI EMU (virtual device) driver
+-CONFIG_BLUEZ_HCIEMU
+- Bluetooth Virtual HCI device driver.
+- This driver is required if you want to use HCI Emulation software.
+-
+- Say Y here to compile support for Virtual HCI devices into the
+- kernel or say M to compile it as module (hci_usb.o).
+-
+ # Choice: alphatype
+ Alpha system type
+ CONFIG_ALPHA_GENERIC
+@@ -11037,6 +11029,12 @@
+
+ If unsure, say N.
+
++Hotplug firmware loading support (EXPERIMENTAL)
++CONFIG_FW_LOADER
++ This option is provided for the case where no in-kernel-tree modules require
++ hotplug firmware loading support, but a module built outside the kernel tree
++ does.
++
+ Use PCI shared memory for NIC registers
+ CONFIG_TULIP_MMIO
+ Use PCI shared memory for the NIC registers, rather than going through
+@@ -12896,6 +12894,15 @@
+ accessible under char device 13:64+ - /dev/input/eventX in a generic
+ way. This is the future ...
+
++CONFIG_INPUT_UINPUT
++ Say Y here if you want to support user level drivers for input
++ subsystem accessible under char device 10:223 - /dev/input/uinput.
++
++ This driver 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 uinput.o. If you want to compile it as a
++ module, say M here and read <file:Documentation/modules.txt>.
++
+ USB Scanner support
+ CONFIG_USB_SCANNER
+ Say Y here if you want to connect a USB scanner to your computer's
+@@ -19870,19 +19877,22 @@
+ Bluetooth can be found at <http://www.bluetooth.com/>.
+
+ Linux Bluetooth subsystem consist of several layers:
+- HCI Core (device and connection manager, scheduler)
+- HCI Device drivers (interface to the hardware)
+- L2CAP Module (L2CAP protocol)
++ BlueZ Core (HCI device and connection manager, scheduler)
++ HCI Device drivers (Interface to the hardware)
++ SCO Module (SCO audio links)
++ L2CAP Module (Logical Link Control and Adaptation Protocol)
++ RFCOMM Module (RFCOMM Protocol)
++ BNEP Module (Bluetooth Network Encapsulation Protocol)
++ CMTP Module (CAPI Message Transport Protocol)
++ HIDP Module (Human Interface Device Protocol)
+
+- Say Y here to enable Linux Bluetooth support and to build HCI Core
+- layer.
++ Say Y here to compile Bluetooth support into the kernel or say M to
++ compile it as module (bluez.o).
+
+ To use Linux Bluetooth subsystem, you will need several user-space
+ utilities like hciconfig and hcid. These utilities and updates to
+ Bluetooth kernel modules are provided in the BlueZ package.
+- For more information, see <http://bluez.sourceforge.net/>.
+-
+- If you want to compile HCI Core as module (hci.o) say M here.
++ For more information, see <http://www.bluez.org/>.
+
+ L2CAP protocol support
+ CONFIG_BLUEZ_L2CAP
+@@ -19893,15 +19903,96 @@
+ Say Y here to compile L2CAP support into the kernel or say M to
+ compile it as module (l2cap.o).
+
++SCO links support
++CONFIG_BLUEZ_SCO
++ SCO link provides voice transport over Bluetooth. SCO support is
++ required for voice applications like Headset and Audio.
++
++ Say Y here to compile SCO support into the kernel or say M to
++ compile it as module (sco.o).
++
++RFCOMM protocol support
++CONFIG_BLUEZ_RFCOMM
++ RFCOMM provides connection oriented stream transport. RFCOMM
++ support is required for Dialup Networking, OBEX and other Bluetooth
++ applications.
++
++ Say Y here to compile RFCOMM support into the kernel or say M to
++ compile it as module (rfcomm.o).
++
++RFCOMM TTY emulation support
++CONFIG_BLUEZ_RFCOMM_TTY
++ This option enables TTY emulation support for RFCOMM channels.
++
++BNEP protocol support
++CONFIG_BLUEZ_BNEP
++ BNEP (Bluetooth Network Encapsulation Protocol) is Ethernet
++ emulation layer on top of Bluetooth. BNEP is required for
++ Bluetooth PAN (Personal Area Network).
++
++ Say Y here to compile BNEP support into the kernel or say M to
++ compile it as module (bnep.o).
++
++BNEP multicast filter support
++CONFIG_BLUEZ_BNEP_MC_FILTER
++ This option enables the multicast filter support for BNEP.
++
++BNEP protocol filter support
++CONFIG_BLUEZ_BNEP_PROTO_FILTER
++ This option enables the protocol filter support for BNEP.
++
++CMTP protocol support
++CONFIG_BLUEZ_CMTP
++ CMTP (CAPI Message Transport Protocol) is a transport layer
++ for CAPI messages. CMTP is required for the Bluetooth Common
++ ISDN Access Profile.
++
++ Say Y here to compile CMTP support into the kernel or say M to
++ compile it as module (cmtp.o).
++
++HIDP protocol support
++CONFIG_BLUEZ_HIDP
++ HIDP (Human Interface Device Protocol) is a transport layer
++ for HID reports. HIDP is required for the Bluetooth Human
++ Interface Device Profile.
++
++ Say Y here to compile HIDP support into the kernel or say M to
++ compile it as module (hidp.o).
++
+ HCI UART driver
+ CONFIG_BLUEZ_HCIUART
+ Bluetooth HCI UART driver.
+ This driver is required if you want to use Bluetooth devices with
+- serial port interface.
++ serial port interface. You will also need this driver if you have
++ UART based Bluetooth PCMCIA and CF devices like Xircom Credit Card
++ adapter and BrainBoxes Bluetooth PC Card.
+
+ Say Y here to compile support for Bluetooth UART devices into the
+ kernel or say M to compile it as module (hci_uart.o).
+
++HCI UART (H4) protocol support
++CONFIG_BLUEZ_HCIUART_H4
++ UART (H4) is serial protocol for communication between Bluetooth
++ device and host. This protocol is required for most Bluetooth devices
++ with UART interface, including PCMCIA and CF cards.
++
++ Say Y here to compile support for HCI UART (H4) protocol.
++
++HCI BCSP protocol support
++CONFIG_BLUEZ_HCIUART_BCSP
++ BCSP (BlueCore Serial Protocol) is serial protocol for communication
++ between Bluetooth device and host. This protocol is required for non
++ USB Bluetooth devices based on CSR BlueCore chip, including PCMCIA and
++ CF cards.
++
++ Say Y here to compile support for HCI BCSP protocol.
++
++HCI BCSP transmit CRC with every BCSP packet
++CONFIG_BLUEZ_HCIUART_BCSP_TXCRC
++ If you say Y here, a 16-bit CRC checksum will be transmitted along with
++ every BCSP (BlueCore Serial Protocol) packet sent to the Bluetooth chip.
++ This increases reliability, but slightly reduces efficiency.
++
+ HCI USB driver
+ CONFIG_BLUEZ_HCIUSB
+ Bluetooth HCI USB driver.
+@@ -19911,7 +20002,16 @@
+ Say Y here to compile support for Bluetooth USB devices into the
+ kernel or say M to compile it as module (hci_usb.o).
+
+-HCI VHCI virtual HCI device driver
++HCI USB SCO (voice) support
++CONFIG_BLUEZ_HCIUSB_SCO
++ This option enables the SCO support in the HCI USB driver. You need this
++ to transmit voice data with your Bluetooth USB device. And your device
++ must also support sending SCO data over the HCI layer, because some of
++ them sends the SCO data to an internal PCM adapter.
++
++ Say Y here to compile support for HCI SCO data.
++
++HCI VHCI Virtual HCI device driver
+ CONFIG_BLUEZ_HCIVHCI
+ Bluetooth Virtual HCI device driver.
+ This driver is required if you want to use HCI Emulation software.
+@@ -19919,6 +20019,63 @@
+ Say Y here to compile support for virtual HCI devices into the
+ kernel or say M to compile it as module (hci_vhci.o).
+
++HCI BFUSB device driver
++CONFIG_BLUEZ_HCIBFUSB
++ Bluetooth HCI BlueFRITZ! USB driver.
++ This driver provides support for Bluetooth USB devices with AVM
++ interface:
++ AVM BlueFRITZ! USB
++
++ Say Y here to compile support for HCI BFUSB devices into the
++ kernel or say M to compile it as module (bfusb.o).
++
++HCI DTL1 (PC Card) device driver
++CONFIG_BLUEZ_HCIDTL1
++ Bluetooth HCI DTL1 (PC Card) driver.
++ This driver provides support for Bluetooth PCMCIA devices with
++ Nokia DTL1 interface:
++ Nokia Bluetooth Card
++ Socket Bluetooth CF Card
++
++ Say Y here to compile support for HCI DTL1 devices into the
++ kernel or say M to compile it as module (dtl1_cs.o).
++
++HCI BT3C (PC Card) device driver
++CONFIG_BLUEZ_HCIBT3C
++ Bluetooth HCI BT3C (PC Card) driver.
++ This driver provides support for Bluetooth PCMCIA devices with
++ 3Com BT3C interface:
++ 3Com Bluetooth Card (3CRWB6096)
++ HP Bluetooth Card
++
++ Say Y here to compile support for HCI BT3C devices into the
++ kernel or say M to compile it as module (bt3c_cs.o).
++
++HCI BlueCard (PC Card) device driver
++CONFIG_BLUEZ_HCIBLUECARD
++ Bluetooth HCI BlueCard (PC Card) driver.
++ This driver provides support for Bluetooth PCMCIA devices with
++ Anycom BlueCard interface:
++ Anycom Bluetooth PC Card
++ Anycom Bluetooth CF Card
++
++ Say Y here to compile support for HCI BlueCard devices into the
++ kernel or say M to compile it as module (bluecard_cs.o).
++
++HCI UART (PC Card) device driver
++CONFIG_BLUEZ_HCIBTUART
++ Bluetooth HCI UART (PC Card) driver.
++ This driver provides support for Bluetooth PCMCIA devices with
++ an UART interface:
++ Xircom CreditCard Bluetooth Adapter
++ Xircom RealPort2 Bluetooth Adapter
++ Sphinx PICO Card
++ H-Soft blue+Card
++ Cyber-blue Compact Flash Card
++
++ Say Y here to compile support for HCI UART devices into the
++ kernel or say M to compile it as module (btuart_cs.o).
++
+ # The following options are for Linux when running on the Hitachi
+ # SuperH family of RISC microprocessors.
+
+diff -urN linux-2.4.18/Documentation/devices.txt linux-2.4.18-mh15/Documentation/devices.txt
+--- linux-2.4.18/Documentation/devices.txt 2001-11-07 23:46:01.000000000 +0100
++++ linux-2.4.18-mh15/Documentation/devices.txt 2004-08-01 16:26:23.000000000 +0200
+@@ -419,6 +419,7 @@
+ 220 = /dev/mptctl Message passing technology (MPT) control
+ 221 = /dev/mvista/hssdsi Montavista PICMG hot swap system driver
+ 222 = /dev/mvista/hasi Montavista PICMG high availability
++ 223 = /dev/input/uinput User level driver support for input
+ 240-255 Reserved for local use
+
+ 11 char Raw keyboard device
+diff -urN linux-2.4.18/Documentation/firmware_class/firmware_sample_driver.c linux-2.4.18-mh15/Documentation/firmware_class/firmware_sample_driver.c
+--- linux-2.4.18/Documentation/firmware_class/firmware_sample_driver.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/Documentation/firmware_class/firmware_sample_driver.c 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,121 @@
++/*
++ * firmware_sample_driver.c -
++ *
++ * Copyright (c) 2003 Manuel Estrada Sainz <ranty@debian.org>
++ *
++ * Sample code on how to use request_firmware() from drivers.
++ *
++ * Note that register_firmware() is currently useless.
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/string.h>
++
++#include "linux/firmware.h"
++
++#define WE_CAN_NEED_FIRMWARE_BEFORE_USERSPACE_IS_AVAILABLE
++#ifdef WE_CAN_NEED_FIRMWARE_BEFORE_USERSPACE_IS_AVAILABLE
++char __init inkernel_firmware[] = "let's say that this is firmware\n";
++#endif
++
++static char ghost_device[] = "ghost0";
++
++static void sample_firmware_load(char *firmware, int size)
++{
++ u8 buf[size+1];
++ memcpy(buf, firmware, size);
++ buf[size] = '\0';
++ printk("firmware_sample_driver: firmware: %s\n", buf);
++}
++
++static void sample_probe_default(void)
++{
++ /* uses the default method to get the firmware */
++ const struct firmware *fw_entry;
++ printk("firmware_sample_driver: a ghost device got inserted :)\n");
++
++ if(request_firmware(&fw_entry, "sample_driver_fw", ghost_device)!=0)
++ {
++ printk(KERN_ERR
++ "firmware_sample_driver: Firmware not available\n");
++ return;
++ }
++
++ sample_firmware_load(fw_entry->data, fw_entry->size);
++
++ release_firmware(fw_entry);
++
++ /* finish setting up the device */
++}
++static void sample_probe_specific(void)
++{
++ /* Uses some specific hotplug support to get the firmware from
++ * userspace directly into the hardware, or via some sysfs file */
++
++ /* NOTE: This currently doesn't work */
++
++ printk("firmware_sample_driver: a ghost device got inserted :)\n");
++
++ if(request_firmware(NULL, "sample_driver_fw", ghost_device)!=0)
++ {
++ printk(KERN_ERR
++ "firmware_sample_driver: Firmware load failed\n");
++ return;
++ }
++
++ /* request_firmware blocks until userspace finished, so at
++ * this point the firmware should be already in the device */
++
++ /* finish setting up the device */
++}
++static void sample_probe_async_cont(const struct firmware *fw, void *context)
++{
++ if(!fw){
++ printk(KERN_ERR
++ "firmware_sample_driver: firmware load failed\n");
++ return;
++ }
++
++ printk("firmware_sample_driver: device pointer \"%s\"\n",
++ (char *)context);
++ sample_firmware_load(fw->data, fw->size);
++}
++static void sample_probe_async(void)
++{
++ /* Let's say that I can't sleep */
++ int error;
++ error = request_firmware_nowait (THIS_MODULE,
++ "sample_driver_fw", ghost_device,
++ "my device pointer",
++ sample_probe_async_cont);
++ if(error){
++ printk(KERN_ERR
++ "firmware_sample_driver:"
++ " request_firmware_nowait failed\n");
++ }
++}
++
++static int sample_init(void)
++{
++#ifdef WE_CAN_NEED_FIRMWARE_BEFORE_USERSPACE_IS_AVAILABLE
++ register_firmware("sample_driver_fw", inkernel_firmware,
++ sizeof(inkernel_firmware));
++#endif
++ /* since there is no real hardware insertion I just call the
++ * sample probe functions here */
++ sample_probe_specific();
++ sample_probe_default();
++ sample_probe_async();
++ return 0;
++}
++static void __exit sample_exit(void)
++{
++}
++
++module_init (sample_init);
++module_exit (sample_exit);
++
++MODULE_LICENSE("GPL");
+diff -urN linux-2.4.18/Documentation/firmware_class/hotplug-script linux-2.4.18-mh15/Documentation/firmware_class/hotplug-script
+--- linux-2.4.18/Documentation/firmware_class/hotplug-script 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/Documentation/firmware_class/hotplug-script 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,16 @@
++#!/bin/sh
++
++# Simple hotplug script sample:
++#
++# Both $DEVPATH and $FIRMWARE are already provided in the environment.
++
++HOTPLUG_FW_DIR=/usr/lib/hotplug/firmware/
++
++echo 1 > /sysfs/$DEVPATH/loading
++cat $HOTPLUG_FW_DIR/$FIRMWARE > /sysfs/$DEVPATH/data
++echo 0 > /sysfs/$DEVPATH/loading
++
++# To cancel the load in case of error:
++#
++# echo -1 > /sysfs/$DEVPATH/loading
++#
+diff -urN linux-2.4.18/Documentation/firmware_class/README linux-2.4.18-mh15/Documentation/firmware_class/README
+--- linux-2.4.18/Documentation/firmware_class/README 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/Documentation/firmware_class/README 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,58 @@
++
++ request_firmware() hotplug interface:
++ ------------------------------------
++ Copyright (C) 2003 Manuel Estrada Sainz <ranty@debian.org>
++
++ Why:
++ ---
++
++ Today, the most extended way to use firmware in the Linux kernel is linking
++ it statically in a header file. Which has political and technical issues:
++
++ 1) Some firmware is not legal to redistribute.
++ 2) The firmware occupies memory permanently, even though it often is just
++ used once.
++ 3) Some people, like the Debian crowd, don't consider some firmware free
++ enough and remove entire drivers (e.g.: keyspan).
++
++ about in-kernel persistence:
++ ---------------------------
++ Under some circumstances, as explained below, it would be interesting to keep
++ firmware images in non-swappable kernel memory or even in the kernel image
++ (probably within initramfs).
++
++ Note that this functionality has not been implemented.
++
++ - Why OPTIONAL in-kernel persistence may be a good idea sometimes:
++
++ - If the device that needs the firmware is needed to access the
++ filesystem. When upon some error the device has to be reset and the
++ firmware reloaded, it won't be possible to get it from userspace.
++ e.g.:
++ - A diskless client with a network card that needs firmware.
++ - The filesystem is stored in a disk behind an scsi device
++ that needs firmware.
++ - Replacing buggy DSDT/SSDT ACPI tables on boot.
++ Note: this would require the persistent objects to be included
++ within the kernel image, probably within initramfs.
++
++ And the same device can be needed to access the filesystem or not depending
++ on the setup, so I think that the choice on what firmware to make
++ persistent should be left to userspace.
++
++ - Why register_firmware()+__init can be useful:
++ - For boot devices needing firmware.
++ - To make the transition easier:
++ The firmware can be declared __init and register_firmware()
++ called on module_init. Then the firmware is warranted to be
++ there even if "firmware hotplug userspace" is not there yet or
++ it doesn't yet provide the needed firmware.
++ Once the firmware is widely available in userspace, it can be
++ removed from the kernel. Or made optional (CONFIG_.*_FIRMWARE).
++
++ In either case, if firmware hotplug support is there, it can move the
++ firmware out of kernel memory into the real filesystem for later
++ usage.
++
++ Note: If persistence is implemented on top of initramfs,
++ register_firmware() may not be appropriate.
+diff -urN linux-2.4.18/drivers/bluetooth/bfusb.c linux-2.4.18-mh15/drivers/bluetooth/bfusb.c
+--- linux-2.4.18/drivers/bluetooth/bfusb.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/drivers/bluetooth/bfusb.c 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,782 @@
++/*
++ *
++ * AVM BlueFRITZ! USB driver
++ *
++ * Copyright (C) 2003 Marcel Holtmann <marcel@holtmann.org>
++ *
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ *
++ */
++
++#include <linux/config.h>
++#include <linux/module.h>
++
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/slab.h>
++#include <linux/types.h>
++#include <linux/sched.h>
++#include <linux/errno.h>
++#include <linux/skbuff.h>
++
++#include <linux/firmware.h>
++#include <linux/usb.h>
++
++#include <net/bluetooth/bluetooth.h>
++#include <net/bluetooth/hci_core.h>
++
++#ifndef CONFIG_BLUEZ_HCIBFUSB_DEBUG
++#undef BT_DBG
++#define BT_DBG(D...)
++#endif
++
++#define VERSION "1.1"
++
++static struct usb_device_id bfusb_table[] = {
++ /* AVM BlueFRITZ! USB */
++ { USB_DEVICE(0x057c, 0x2200) },
++
++ { } /* Terminating entry */
++};
++
++MODULE_DEVICE_TABLE(usb, bfusb_table);
++
++
++#define BFUSB_MAX_BLOCK_SIZE 256
++
++#define BFUSB_BLOCK_TIMEOUT (HZ * 3)
++
++#define BFUSB_TX_PROCESS 1
++#define BFUSB_TX_WAKEUP 2
++
++#define BFUSB_MAX_BULK_TX 1
++#define BFUSB_MAX_BULK_RX 1
++
++struct bfusb {
++ struct hci_dev hdev;
++
++ unsigned long state;
++
++ struct usb_device *udev;
++
++ unsigned int bulk_in_ep;
++ unsigned int bulk_out_ep;
++ unsigned int bulk_pkt_size;
++
++ rwlock_t lock;
++
++ struct sk_buff_head transmit_q;
++
++ struct sk_buff *reassembly;
++
++ atomic_t pending_tx;
++ struct sk_buff_head pending_q;
++ struct sk_buff_head completed_q;
++};
++
++struct bfusb_scb {
++ struct urb *urb;
++};
++
++static void bfusb_tx_complete(struct urb *urb);
++static void bfusb_rx_complete(struct urb *urb);
++
++static struct urb *bfusb_get_completed(struct bfusb *bfusb)
++{
++ struct sk_buff *skb;
++ struct urb *urb = NULL;
++
++ BT_DBG("bfusb %p", bfusb);
++
++ skb = skb_dequeue(&bfusb->completed_q);
++ if (skb) {
++ urb = ((struct bfusb_scb *) skb->cb)->urb;
++ kfree_skb(skb);
++ }
++
++ return urb;
++}
++
++static inline void bfusb_unlink_urbs(struct bfusb *bfusb)
++{
++ struct sk_buff *skb;
++ struct urb *urb;
++
++ BT_DBG("bfusb %p", bfusb);
++
++ while ((skb = skb_dequeue(&bfusb->pending_q))) {
++ urb = ((struct bfusb_scb *) skb->cb)->urb;
++ usb_unlink_urb(urb);
++ skb_queue_tail(&bfusb->completed_q, skb);
++ }
++
++ while ((urb = bfusb_get_completed(bfusb)))
++ usb_free_urb(urb);
++}
++
++
++static int bfusb_send_bulk(struct bfusb *bfusb, struct sk_buff *skb)
++{
++ struct bfusb_scb *scb = (void *) skb->cb;
++ struct urb *urb = bfusb_get_completed(bfusb);
++ int err, pipe;
++
++ BT_DBG("bfusb %p skb %p len %d", bfusb, skb, skb->len);
++
++ if (!urb && !(urb = usb_alloc_urb(0)))
++ return -ENOMEM;
++
++ pipe = usb_sndbulkpipe(bfusb->udev, bfusb->bulk_out_ep);
++
++ FILL_BULK_URB(urb, bfusb->udev, pipe, skb->data, skb->len,
++ bfusb_tx_complete, skb);
++
++ urb->transfer_flags = USB_QUEUE_BULK;
++
++ scb->urb = urb;
++
++ skb_queue_tail(&bfusb->pending_q, skb);
++
++ err = usb_submit_urb(urb);
++ if (err) {
++ BT_ERR("%s bulk tx submit failed urb %p err %d",
++ bfusb->hdev.name, urb, err);
++ skb_unlink(skb);
++ usb_free_urb(urb);
++ } else
++ atomic_inc(&bfusb->pending_tx);
++
++ return err;
++}
++
++static void bfusb_tx_wakeup(struct bfusb *bfusb)
++{
++ struct sk_buff *skb;
++
++ BT_DBG("bfusb %p", bfusb);
++
++ if (test_and_set_bit(BFUSB_TX_PROCESS, &bfusb->state)) {
++ set_bit(BFUSB_TX_WAKEUP, &bfusb->state);
++ return;
++ }
++
++ do {
++ clear_bit(BFUSB_TX_WAKEUP, &bfusb->state);
++
++ while ((atomic_read(&bfusb->pending_tx) < BFUSB_MAX_BULK_TX) &&
++ (skb = skb_dequeue(&bfusb->transmit_q))) {
++ if (bfusb_send_bulk(bfusb, skb) < 0) {
++ skb_queue_head(&bfusb->transmit_q, skb);
++ break;
++ }
++ }
++
++ } while (test_bit(BFUSB_TX_WAKEUP, &bfusb->state));
++
++ clear_bit(BFUSB_TX_PROCESS, &bfusb->state);
++}
++
++static void bfusb_tx_complete(struct urb *urb)
++{
++ struct sk_buff *skb = (struct sk_buff *) urb->context;
++ struct bfusb *bfusb = (struct bfusb *) skb->dev;
++
++ BT_DBG("bfusb %p urb %p skb %p len %d", bfusb, urb, skb, skb->len);
++
++ atomic_dec(&bfusb->pending_tx);
++
++ if (!test_bit(HCI_RUNNING, &bfusb->hdev.flags))
++ return;
++
++ if (!urb->status)
++ bfusb->hdev.stat.byte_tx += skb->len;
++ else
++ bfusb->hdev.stat.err_tx++;
++
++ read_lock(&bfusb->lock);
++
++ skb_unlink(skb);
++ skb_queue_tail(&bfusb->completed_q, skb);
++
++ bfusb_tx_wakeup(bfusb);
++
++ read_unlock(&bfusb->lock);
++}
++
++
++static int bfusb_rx_submit(struct bfusb *bfusb, struct urb *urb)
++{
++ struct bfusb_scb *scb;
++ struct sk_buff *skb;
++ int err, pipe, size = HCI_MAX_FRAME_SIZE + 32;
++
++ BT_DBG("bfusb %p urb %p", bfusb, urb);
++
++ if (!urb && !(urb = usb_alloc_urb(0)))
++ return -ENOMEM;
++
++ if (!(skb = bluez_skb_alloc(size, GFP_ATOMIC))) {
++ usb_free_urb(urb);
++ return -ENOMEM;
++ }
++
++ skb->dev = (void *) bfusb;
++
++ scb = (struct bfusb_scb *) skb->cb;
++ scb->urb = urb;
++
++ pipe = usb_rcvbulkpipe(bfusb->udev, bfusb->bulk_in_ep);
++
++ FILL_BULK_URB(urb, bfusb->udev, pipe, skb->data, size,
++ bfusb_rx_complete, skb);
++
++ urb->transfer_flags = USB_QUEUE_BULK;
++
++ skb_queue_tail(&bfusb->pending_q, skb);
++
++ err = usb_submit_urb(urb);
++ if (err) {
++ BT_ERR("%s bulk rx submit failed urb %p err %d",
++ bfusb->hdev.name, urb, err);
++ skb_unlink(skb);
++ kfree_skb(skb);
++ usb_free_urb(urb);
++ }
++
++ return err;
++}
++
++static inline int bfusb_recv_block(struct bfusb *bfusb, int hdr, unsigned char *data, int len)
++{
++ BT_DBG("bfusb %p hdr 0x%02x data %p len %d", bfusb, hdr, data, len);
++
++ if (hdr & 0x10) {
++ BT_ERR("%s error in block", bfusb->hdev.name);
++ if (bfusb->reassembly)
++ kfree_skb(bfusb->reassembly);
++ bfusb->reassembly = NULL;
++ return -EIO;
++ }
++
++ if (hdr & 0x04) {
++ struct sk_buff *skb;
++ unsigned char pkt_type;
++ int pkt_len = 0;
++
++ if (bfusb->reassembly) {
++ BT_ERR("%s unexpected start block", bfusb->hdev.name);
++ kfree_skb(bfusb->reassembly);
++ bfusb->reassembly = NULL;
++ }
++
++ if (len < 1) {
++ BT_ERR("%s no packet type found", bfusb->hdev.name);
++ return -EPROTO;
++ }
++
++ pkt_type = *data++; len--;
++
++ switch (pkt_type) {
++ case HCI_EVENT_PKT:
++ if (len >= HCI_EVENT_HDR_SIZE) {
++ hci_event_hdr *hdr = (hci_event_hdr *) data;
++ pkt_len = HCI_EVENT_HDR_SIZE + hdr->plen;
++ } else {
++ BT_ERR("%s event block is too short", bfusb->hdev.name);
++ return -EILSEQ;
++ }
++ break;
++
++ case HCI_ACLDATA_PKT:
++ if (len >= HCI_ACL_HDR_SIZE) {
++ hci_acl_hdr *hdr = (hci_acl_hdr *) data;
++ pkt_len = HCI_ACL_HDR_SIZE + __le16_to_cpu(hdr->dlen);
++ } else {
++ BT_ERR("%s data block is too short", bfusb->hdev.name);
++ return -EILSEQ;
++ }
++ break;
++
++ case HCI_SCODATA_PKT:
++ if (len >= HCI_SCO_HDR_SIZE) {
++ hci_sco_hdr *hdr = (hci_sco_hdr *) data;
++ pkt_len = HCI_SCO_HDR_SIZE + hdr->dlen;
++ } else {
++ BT_ERR("%s audio block is too short", bfusb->hdev.name);
++ return -EILSEQ;
++ }
++ break;
++ }
++
++ skb = bluez_skb_alloc(pkt_len, GFP_ATOMIC);
++ if (!skb) {
++ BT_ERR("%s no memory for the packet", bfusb->hdev.name);
++ return -ENOMEM;
++ }
++
++ skb->dev = (void *) &bfusb->hdev;
++ skb->pkt_type = pkt_type;
++
++ bfusb->reassembly = skb;
++ } else {
++ if (!bfusb->reassembly) {
++ BT_ERR("%s unexpected continuation block", bfusb->hdev.name);
++ return -EIO;
++ }
++ }
++
++ if (len > 0)
++ memcpy(skb_put(bfusb->reassembly, len), data, len);
++
++ if (hdr & 0x08) {
++ hci_recv_frame(bfusb->reassembly);
++ bfusb->reassembly = NULL;
++ }
++
++ return 0;
++}
++
++static void bfusb_rx_complete(struct urb *urb)
++{
++ struct sk_buff *skb = (struct sk_buff *) urb->context;
++ struct bfusb *bfusb = (struct bfusb *) skb->dev;
++ unsigned char *buf = urb->transfer_buffer;
++ int count = urb->actual_length;
++ int err, hdr, len;
++
++ BT_DBG("bfusb %p urb %p skb %p len %d", bfusb, urb, skb, skb->len);
++
++ read_lock(&bfusb->lock);
++
++ if (!test_bit(HCI_RUNNING, &bfusb->hdev.flags))
++ goto unlock;
++
++ if (urb->status || !count)
++ goto resubmit;
++
++ bfusb->hdev.stat.byte_rx += count;
++
++ skb_put(skb, count);
++
++ while (count) {
++ hdr = buf[0] | (buf[1] << 8);
++
++ if (hdr & 0x4000) {
++ len = 0;
++ count -= 2;
++ buf += 2;
++ } else {
++ len = (buf[2] == 0) ? 256 : buf[2];
++ count -= 3;
++ buf += 3;
++ }
++
++ if (count < len) {
++ BT_ERR("%s block extends over URB buffer ranges",
++ bfusb->hdev.name);
++ }
++
++ if ((hdr & 0xe1) == 0xc1)
++ bfusb_recv_block(bfusb, hdr, buf, len);
++
++ count -= len;
++ buf += len;
++ }
++
++ skb_unlink(skb);
++ kfree_skb(skb);
++
++ bfusb_rx_submit(bfusb, urb);
++
++ read_unlock(&bfusb->lock);
++
++ return;
++
++resubmit:
++ urb->dev = bfusb->udev;
++
++ err = usb_submit_urb(urb);
++ if (err) {
++ BT_ERR("%s bulk resubmit failed urb %p err %d",
++ bfusb->hdev.name, urb, err);
++ }
++
++unlock:
++ read_unlock(&bfusb->lock);
++}
++
++
++static int bfusb_open(struct hci_dev *hdev)
++{
++ struct bfusb *bfusb = (struct bfusb *) hdev->driver_data;
++ unsigned long flags;
++ int i, err;
++
++ BT_DBG("hdev %p bfusb %p", hdev, bfusb);
++
++ if (test_and_set_bit(HCI_RUNNING, &hdev->flags))
++ return 0;
++
++ MOD_INC_USE_COUNT;
++
++ write_lock_irqsave(&bfusb->lock, flags);
++
++ err = bfusb_rx_submit(bfusb, NULL);
++ if (!err) {
++ for (i = 1; i < BFUSB_MAX_BULK_RX; i++)
++ bfusb_rx_submit(bfusb, NULL);
++ } else {
++ clear_bit(HCI_RUNNING, &hdev->flags);
++ MOD_DEC_USE_COUNT;
++ }
++
++ write_unlock_irqrestore(&bfusb->lock, flags);
++
++ return err;
++}
++
++static int bfusb_flush(struct hci_dev *hdev)
++{
++ struct bfusb *bfusb = (struct bfusb *) hdev->driver_data;
++
++ BT_DBG("hdev %p bfusb %p", hdev, bfusb);
++
++ skb_queue_purge(&bfusb->transmit_q);
++
++ return 0;
++}
++
++static int bfusb_close(struct hci_dev *hdev)
++{
++ struct bfusb *bfusb = (struct bfusb *) hdev->driver_data;
++ unsigned long flags;
++
++ BT_DBG("hdev %p bfusb %p", hdev, bfusb);
++
++ if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
++ return 0;
++
++ write_lock_irqsave(&bfusb->lock, flags);
++
++ bfusb_unlink_urbs(bfusb);
++ bfusb_flush(hdev);
++
++ write_unlock_irqrestore(&bfusb->lock, flags);
++
++ MOD_DEC_USE_COUNT;
++
++ return 0;
++}
++
++static int bfusb_send_frame(struct sk_buff *skb)
++{
++ struct hci_dev *hdev = (struct hci_dev *) skb->dev;
++ struct bfusb *bfusb;
++ struct sk_buff *nskb;
++ unsigned char buf[3];
++ int sent = 0, size, count;
++
++ BT_DBG("hdev %p skb %p type %d len %d", hdev, skb, skb->pkt_type, skb->len);
++
++ if (!hdev) {
++ BT_ERR("Frame for unknown HCI device (hdev=NULL)");
++ return -ENODEV;
++ }
++
++ if (!test_bit(HCI_RUNNING, &hdev->flags))
++ return -EBUSY;
++
++ bfusb = (struct bfusb *) hdev->driver_data;
++
++ switch (skb->pkt_type) {
++ case HCI_COMMAND_PKT:
++ hdev->stat.cmd_tx++;
++ break;
++ case HCI_ACLDATA_PKT:
++ hdev->stat.acl_tx++;
++ break;
++ case HCI_SCODATA_PKT:
++ hdev->stat.sco_tx++;
++ break;
++ };
++
++ /* Prepend skb with frame type */
++ memcpy(skb_push(skb, 1), &(skb->pkt_type), 1);
++
++ count = skb->len;
++
++ /* Max HCI frame size seems to be 1511 + 1 */
++ if (!(nskb = bluez_skb_alloc(count + 32, GFP_ATOMIC))) {
++ BT_ERR("Can't allocate memory for new packet");
++ return -ENOMEM;
++ }
++
++ nskb->dev = (void *) bfusb;
++
++ while (count) {
++ size = min_t(uint, count, BFUSB_MAX_BLOCK_SIZE);
++
++ buf[0] = 0xc1 | ((sent == 0) ? 0x04 : 0) | ((count == size) ? 0x08 : 0);
++ buf[1] = 0x00;
++ buf[2] = (size == BFUSB_MAX_BLOCK_SIZE) ? 0 : size;
++
++ memcpy(skb_put(nskb, 3), buf, 3);
++ memcpy(skb_put(nskb, size), skb->data + sent, size);
++
++ sent += size;
++ count -= size;
++ }
++
++ /* Don't send frame with multiple size of bulk max packet */
++ if ((nskb->len % bfusb->bulk_pkt_size) == 0) {
++ buf[0] = 0xdd;
++ buf[1] = 0x00;
++ memcpy(skb_put(nskb, 2), buf, 2);
++ }
++
++ read_lock(&bfusb->lock);
++
++ skb_queue_tail(&bfusb->transmit_q, nskb);
++ bfusb_tx_wakeup(bfusb);
++
++ read_unlock(&bfusb->lock);
++
++ kfree_skb(skb);
++
++ return 0;
++}
++
++static void bfusb_destruct(struct hci_dev *hdev)
++{
++ struct bfusb *bfusb = (struct bfusb *) hdev->driver_data;
++
++ BT_DBG("hdev %p bfusb %p", hdev, bfusb);
++
++ kfree(bfusb);
++}
++
++static int bfusb_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg)
++{
++ return -ENOIOCTLCMD;
++}
++
++
++static int bfusb_load_firmware(struct bfusb *bfusb, unsigned char *firmware, int count)
++{
++ unsigned char *buf;
++ int err, pipe, len, size, sent = 0;
++
++ BT_DBG("bfusb %p udev %p firmware %p count %d", bfusb, bfusb->udev, firmware, count);
++
++ BT_INFO("BlueFRITZ! USB loading firmware");
++
++ if (usb_set_configuration(bfusb->udev, 1) < 0) {
++ BT_ERR("Can't change to loading configuration");
++ return -EBUSY;
++ }
++
++ buf = kmalloc(BFUSB_MAX_BLOCK_SIZE + 3, GFP_ATOMIC);
++ if (!buf) {
++ BT_ERR("Can't allocate memory chunk for firmware");
++ return -ENOMEM;
++ }
++
++ pipe = usb_sndbulkpipe(bfusb->udev, bfusb->bulk_out_ep);
++
++ while (count) {
++ size = min_t(uint, count, BFUSB_MAX_BLOCK_SIZE + 3);
++
++ memcpy(buf, firmware + sent, size);
++
++ err = usb_bulk_msg(bfusb->udev, pipe, buf, size,
++ &len, BFUSB_BLOCK_TIMEOUT);
++
++ if (err || (len != size)) {
++ BT_ERR("Error in firmware loading");
++ goto error;
++ }
++
++ sent += size;
++ count -= size;
++ }
++
++ if ((err = usb_bulk_msg(bfusb->udev, pipe, NULL, 0,
++ &len, BFUSB_BLOCK_TIMEOUT)) < 0) {
++ BT_ERR("Error in null packet request");
++ goto error;
++ }
++
++ if ((err = usb_set_configuration(bfusb->udev, 2)) < 0) {
++ BT_ERR("Can't change to running configuration");
++ goto error;
++ }
++
++ BT_INFO("BlueFRITZ! USB device ready");
++
++ kfree(buf);
++ return 0;
++
++error:
++ kfree(buf);
++
++ pipe = usb_sndctrlpipe(bfusb->udev, 0);
++
++ usb_control_msg(bfusb->udev, pipe, USB_REQ_SET_CONFIGURATION,
++ 0, 0, 0, NULL, 0, BFUSB_BLOCK_TIMEOUT);
++
++ return err;
++}
++
++static void *bfusb_probe(struct usb_device *udev, unsigned int ifnum, const struct usb_device_id *id)
++{
++ const struct firmware *firmware;
++ char device[16];
++ struct usb_interface *iface;
++ struct usb_interface_descriptor *iface_desc;
++ struct usb_endpoint_descriptor *bulk_out_ep;
++ struct usb_endpoint_descriptor *bulk_in_ep;
++ struct hci_dev *hdev;
++ struct bfusb *bfusb;
++
++ BT_DBG("udev %p ifnum %d id %p", udev, ifnum, id);
++
++ /* Check number of endpoints */
++ iface = &udev->actconfig->interface[0];
++ iface_desc = &iface->altsetting[0];
++
++ if (iface_desc->bNumEndpoints < 2)
++ return NULL;
++
++ bulk_out_ep = &iface_desc->endpoint[0];
++ bulk_in_ep = &iface_desc->endpoint[1];
++
++ if (!bulk_out_ep || !bulk_in_ep) {
++ BT_ERR("Bulk endpoints not found");
++ goto done;
++ }
++
++ /* Initialize control structure and load firmware */
++ if (!(bfusb = kmalloc(sizeof(struct bfusb), GFP_KERNEL))) {
++ BT_ERR("Can't allocate memory for control structure");
++ goto done;
++ }
++
++ memset(bfusb, 0, sizeof(struct bfusb));
++
++ bfusb->udev = udev;
++ bfusb->bulk_in_ep = bulk_in_ep->bEndpointAddress;
++ bfusb->bulk_out_ep = bulk_out_ep->bEndpointAddress;
++ bfusb->bulk_pkt_size = bulk_out_ep->wMaxPacketSize;
++
++ bfusb->lock = RW_LOCK_UNLOCKED;
++
++ bfusb->reassembly = NULL;
++
++ skb_queue_head_init(&bfusb->transmit_q);
++ skb_queue_head_init(&bfusb->pending_q);
++ skb_queue_head_init(&bfusb->completed_q);
++
++ snprintf(device, sizeof(device), "bfusb%3.3d%3.3d", udev->bus->busnum, udev->devnum);
++
++ if (request_firmware(&firmware, "bfubase.frm", device) < 0) {
++ BT_ERR("Firmware request failed");
++ goto error;
++ }
++
++ if (bfusb_load_firmware(bfusb, firmware->data, firmware->size) < 0) {
++ BT_ERR("Firmware loading failed");
++ goto release;
++ }
++
++ release_firmware(firmware);
++
++ /* Initialize and register HCI device */
++ hdev = &bfusb->hdev;
++
++ hdev->type = HCI_USB;
++ hdev->driver_data = bfusb;
++
++ hdev->open = bfusb_open;
++ hdev->close = bfusb_close;
++ hdev->flush = bfusb_flush;
++ hdev->send = bfusb_send_frame;
++ hdev->destruct = bfusb_destruct;
++ hdev->ioctl = bfusb_ioctl;
++
++ if (hci_register_dev(hdev) < 0) {
++ BT_ERR("Can't register HCI device");
++ goto error;
++ }
++
++ return bfusb;
++
++release:
++ release_firmware(firmware);
++
++error:
++ kfree(bfusb);
++
++done:
++ return NULL;
++}
++
++static void bfusb_disconnect(struct usb_device *udev, void *ptr)
++{
++ struct bfusb *bfusb = (struct bfusb *) ptr;
++ struct hci_dev *hdev = &bfusb->hdev;
++
++ BT_DBG("udev %p ptr %p", udev, ptr);
++
++ if (!hdev)
++ return;
++
++ bfusb_close(hdev);
++
++ if (hci_unregister_dev(hdev) < 0)
++ BT_ERR("Can't unregister HCI device %s", hdev->name);
++}
++
++static struct usb_driver bfusb_driver = {
++ name: "bfusb",
++ probe: bfusb_probe,
++ disconnect: bfusb_disconnect,
++ id_table: bfusb_table,
++};
++
++static int __init bfusb_init(void)
++{
++ int err;
++
++ BT_INFO("BlueFRITZ! USB driver ver %s", VERSION);
++ BT_INFO("Copyright (C) 2003 Marcel Holtmann <marcel@holtmann.org>");
++
++ if ((err = usb_register(&bfusb_driver)) < 0)
++ BT_ERR("Failed to register BlueFRITZ! USB driver");
++
++ return err;
++}
++
++static void __exit bfusb_cleanup(void)
++{
++ usb_deregister(&bfusb_driver);
++}
++
++module_init(bfusb_init);
++module_exit(bfusb_cleanup);
++
++MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
++MODULE_DESCRIPTION("BlueFRITZ! USB driver ver " VERSION);
++MODULE_LICENSE("GPL");
+diff -urN linux-2.4.18/drivers/bluetooth/bluecard_cs.c linux-2.4.18-mh15/drivers/bluetooth/bluecard_cs.c
+--- linux-2.4.18/drivers/bluetooth/bluecard_cs.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/drivers/bluetooth/bluecard_cs.c 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,1116 @@
++/*
++ *
++ * Bluetooth driver for the Anycom BlueCard (LSE039/LSE041)
++ *
++ * Copyright (C) 2001-2002 Marcel Holtmann <marcel@holtmann.org>
++ *
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation;
++ *
++ * Software distributed under the License is distributed on an "AS
++ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
++ * implied. See the License for the specific language governing
++ * rights and limitations under the License.
++ *
++ * The initial developer of the original code is David A. Hinds
++ * <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
++ * are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
++ *
++ */
++
++#include <linux/config.h>
++#include <linux/module.h>
++
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/slab.h>
++#include <linux/types.h>
++#include <linux/sched.h>
++#include <linux/timer.h>
++#include <linux/errno.h>
++#include <linux/ptrace.h>
++#include <linux/ioport.h>
++#include <linux/spinlock.h>
++#include <linux/skbuff.h>
++#include <asm/io.h>
++
++#include <pcmcia/version.h>
++#include <pcmcia/cs_types.h>
++#include <pcmcia/cs.h>
++#include <pcmcia/cistpl.h>
++#include <pcmcia/ciscode.h>
++#include <pcmcia/ds.h>
++#include <pcmcia/cisreg.h>
++
++#include <net/bluetooth/bluetooth.h>
++#include <net/bluetooth/hci_core.h>
++
++
++
++/* ======================== Module parameters ======================== */
++
++
++/* Bit map of interrupts to choose from */
++static u_int irq_mask = 0x86bc;
++static int irq_list[4] = { -1 };
++
++MODULE_PARM(irq_mask, "i");
++MODULE_PARM(irq_list, "1-4i");
++
++MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
++MODULE_DESCRIPTION("BlueZ driver for the Anycom BlueCard (LSE039/LSE041)");
++MODULE_LICENSE("GPL");
++
++
++
++/* ======================== Local structures ======================== */
++
++
++typedef struct bluecard_info_t {
++ dev_link_t link;
++ dev_node_t node;
++
++ struct hci_dev hdev;
++
++ spinlock_t lock; /* For serializing operations */
++ struct timer_list timer; /* For LED control */
++
++ struct sk_buff_head txq;
++ unsigned long tx_state;
++
++ unsigned long rx_state;
++ unsigned long rx_count;
++ struct sk_buff *rx_skb;
++
++ unsigned char ctrl_reg;
++ unsigned long hw_state; /* Status of the hardware and LED control */
++} bluecard_info_t;
++
++
++void bluecard_config(dev_link_t *link);
++void bluecard_release(u_long arg);
++int bluecard_event(event_t event, int priority, event_callback_args_t *args);
++
++static dev_info_t dev_info = "bluecard_cs";
++
++dev_link_t *bluecard_attach(void);
++void bluecard_detach(dev_link_t *);
++
++static dev_link_t *dev_list = NULL;
++
++
++/* Default baud rate: 57600, 115200, 230400 or 460800 */
++#define DEFAULT_BAUD_RATE 230400
++
++
++/* Hardware states */
++#define CARD_READY 1
++#define CARD_HAS_PCCARD_ID 4
++#define CARD_HAS_POWER_LED 5
++#define CARD_HAS_ACTIVITY_LED 6
++
++/* Transmit states */
++#define XMIT_SENDING 1
++#define XMIT_WAKEUP 2
++#define XMIT_BUFFER_NUMBER 5 /* unset = buffer one, set = buffer two */
++#define XMIT_BUF_ONE_READY 6
++#define XMIT_BUF_TWO_READY 7
++#define XMIT_SENDING_READY 8
++
++/* Receiver states */
++#define RECV_WAIT_PACKET_TYPE 0
++#define RECV_WAIT_EVENT_HEADER 1
++#define RECV_WAIT_ACL_HEADER 2
++#define RECV_WAIT_SCO_HEADER 3
++#define RECV_WAIT_DATA 4
++
++/* Special packet types */
++#define PKT_BAUD_RATE_57600 0x80
++#define PKT_BAUD_RATE_115200 0x81
++#define PKT_BAUD_RATE_230400 0x82
++#define PKT_BAUD_RATE_460800 0x83
++
++
++/* These are the register offsets */
++#define REG_COMMAND 0x20
++#define REG_INTERRUPT 0x21
++#define REG_CONTROL 0x22
++#define REG_RX_CONTROL 0x24
++#define REG_CARD_RESET 0x30
++#define REG_LED_CTRL 0x30
++
++/* REG_COMMAND */
++#define REG_COMMAND_TX_BUF_ONE 0x01
++#define REG_COMMAND_TX_BUF_TWO 0x02
++#define REG_COMMAND_RX_BUF_ONE 0x04
++#define REG_COMMAND_RX_BUF_TWO 0x08
++#define REG_COMMAND_RX_WIN_ONE 0x00
++#define REG_COMMAND_RX_WIN_TWO 0x10
++
++/* REG_CONTROL */
++#define REG_CONTROL_BAUD_RATE_57600 0x00
++#define REG_CONTROL_BAUD_RATE_115200 0x01
++#define REG_CONTROL_BAUD_RATE_230400 0x02
++#define REG_CONTROL_BAUD_RATE_460800 0x03
++#define REG_CONTROL_RTS 0x04
++#define REG_CONTROL_BT_ON 0x08
++#define REG_CONTROL_BT_RESET 0x10
++#define REG_CONTROL_BT_RES_PU 0x20
++#define REG_CONTROL_INTERRUPT 0x40
++#define REG_CONTROL_CARD_RESET 0x80
++
++/* REG_RX_CONTROL */
++#define RTS_LEVEL_SHIFT_BITS 0x02
++
++
++
++/* ======================== LED handling routines ======================== */
++
++
++void bluecard_activity_led_timeout(u_long arg)
++{
++ bluecard_info_t *info = (bluecard_info_t *)arg;
++ unsigned int iobase = info->link.io.BasePort1;
++
++ if (test_bit(CARD_HAS_ACTIVITY_LED, &(info->hw_state))) {
++ /* Disable activity LED */
++ outb(0x08 | 0x20, iobase + 0x30);
++ } else {
++ /* Disable power LED */
++ outb(0x00, iobase + 0x30);
++ }
++}
++
++
++static void bluecard_enable_activity_led(bluecard_info_t *info)
++{
++ unsigned int iobase = info->link.io.BasePort1;
++
++ if (test_bit(CARD_HAS_ACTIVITY_LED, &(info->hw_state))) {
++ /* Enable activity LED */
++ outb(0x10 | 0x40, iobase + 0x30);
++
++ /* Stop the LED after HZ/4 */
++ mod_timer(&(info->timer), jiffies + HZ / 4);
++ } else {
++ /* Enable power LED */
++ outb(0x08 | 0x20, iobase + 0x30);
++
++ /* Stop the LED after HZ/2 */
++ mod_timer(&(info->timer), jiffies + HZ / 2);
++ }
++}
++
++
++
++/* ======================== Interrupt handling ======================== */
++
++
++static int bluecard_write(unsigned int iobase, unsigned int offset, __u8 *buf, int len)
++{
++ int i, actual;
++
++ actual = (len > 15) ? 15 : len;
++
++ outb_p(actual, iobase + offset);
++
++ for (i = 0; i < actual; i++)
++ outb_p(buf[i], iobase + offset + i + 1);
++
++ return actual;
++}
++
++
++static void bluecard_write_wakeup(bluecard_info_t *info)
++{
++ if (!info) {
++ printk(KERN_WARNING "bluecard_cs: Call of write_wakeup for unknown device.\n");
++ return;
++ }
++
++ if (!test_bit(XMIT_SENDING_READY, &(info->tx_state)))
++ return;
++
++ if (test_and_set_bit(XMIT_SENDING, &(info->tx_state))) {
++ set_bit(XMIT_WAKEUP, &(info->tx_state));
++ return;
++ }
++
++ do {
++ register unsigned int iobase = info->link.io.BasePort1;
++ register unsigned int offset;
++ register unsigned char command;
++ register unsigned long ready_bit;
++ register struct sk_buff *skb;
++ register int len;
++
++ clear_bit(XMIT_WAKEUP, &(info->tx_state));
++
++ if (!(info->link.state & DEV_PRESENT))
++ return;
++
++ if (test_bit(XMIT_BUFFER_NUMBER, &(info->tx_state))) {
++ if (!test_bit(XMIT_BUF_TWO_READY, &(info->tx_state)))
++ break;
++ offset = 0x10;
++ command = REG_COMMAND_TX_BUF_TWO;
++ ready_bit = XMIT_BUF_TWO_READY;
++ } else {
++ if (!test_bit(XMIT_BUF_ONE_READY, &(info->tx_state)))
++ break;
++ offset = 0x00;
++ command = REG_COMMAND_TX_BUF_ONE;
++ ready_bit = XMIT_BUF_ONE_READY;
++ }
++
++ if (!(skb = skb_dequeue(&(info->txq))))
++ break;
++
++ if (skb->pkt_type & 0x80) {
++ /* Disable RTS */
++ info->ctrl_reg |= REG_CONTROL_RTS;
++ outb(info->ctrl_reg, iobase + REG_CONTROL);
++ }
++
++ /* Activate LED */
++ bluecard_enable_activity_led(info);
++
++ /* Send frame */
++ len = bluecard_write(iobase, offset, skb->data, skb->len);
++
++ /* Tell the FPGA to send the data */
++ outb_p(command, iobase + REG_COMMAND);
++
++ /* Mark the buffer as dirty */
++ clear_bit(ready_bit, &(info->tx_state));
++
++ if (skb->pkt_type & 0x80) {
++
++ wait_queue_head_t wait;
++ unsigned char baud_reg;
++
++ switch (skb->pkt_type) {
++ case PKT_BAUD_RATE_460800:
++ baud_reg = REG_CONTROL_BAUD_RATE_460800;
++ break;
++ case PKT_BAUD_RATE_230400:
++ baud_reg = REG_CONTROL_BAUD_RATE_230400;
++ break;
++ case PKT_BAUD_RATE_115200:
++ baud_reg = REG_CONTROL_BAUD_RATE_115200;
++ break;
++ case PKT_BAUD_RATE_57600:
++ /* Fall through... */
++ default:
++ baud_reg = REG_CONTROL_BAUD_RATE_57600;
++ break;
++ }
++
++ /* Wait until the command reaches the baseband */
++ init_waitqueue_head(&wait);
++ interruptible_sleep_on_timeout(&wait, HZ / 10);
++
++ /* Set baud on baseband */
++ info->ctrl_reg &= ~0x03;
++ info->ctrl_reg |= baud_reg;
++ outb(info->ctrl_reg, iobase + REG_CONTROL);
++
++ /* Enable RTS */
++ info->ctrl_reg &= ~REG_CONTROL_RTS;
++ outb(info->ctrl_reg, iobase + REG_CONTROL);
++
++ /* Wait before the next HCI packet can be send */
++ interruptible_sleep_on_timeout(&wait, HZ);
++
++ }
++
++ if (len == skb->len) {
++ kfree_skb(skb);
++ } else {
++ skb_pull(skb, len);
++ skb_queue_head(&(info->txq), skb);
++ }
++
++ info->hdev.stat.byte_tx += len;
++
++ /* Change buffer */
++ change_bit(XMIT_BUFFER_NUMBER, &(info->tx_state));
++
++ } while (test_bit(XMIT_WAKEUP, &(info->tx_state)));
++
++ clear_bit(XMIT_SENDING, &(info->tx_state));
++}
++
++
++static int bluecard_read(unsigned int iobase, unsigned int offset, __u8 *buf, int size)
++{
++ int i, n, len;
++
++ outb(REG_COMMAND_RX_WIN_ONE, iobase + REG_COMMAND);
++
++ len = inb(iobase + offset);
++ n = 0;
++ i = 1;
++
++ while (n < len) {
++
++ if (i == 16) {
++ outb(REG_COMMAND_RX_WIN_TWO, iobase + REG_COMMAND);
++ i = 0;
++ }
++
++ buf[n] = inb(iobase + offset + i);
++
++ n++;
++ i++;
++
++ }
++
++ return len;
++}
++
++
++static void bluecard_receive(bluecard_info_t *info, unsigned int offset)
++{
++ unsigned int iobase;
++ unsigned char buf[31];
++ int i, len;
++
++ if (!info) {
++ printk(KERN_WARNING "bluecard_cs: Call of receive for unknown device.\n");
++ return;
++ }
++
++ iobase = info->link.io.BasePort1;
++
++ if (test_bit(XMIT_SENDING_READY, &(info->tx_state)))
++ bluecard_enable_activity_led(info);
++
++ len = bluecard_read(iobase, offset, buf, sizeof(buf));
++
++ for (i = 0; i < len; i++) {
++
++ /* Allocate packet */
++ if (info->rx_skb == NULL) {
++ info->rx_state = RECV_WAIT_PACKET_TYPE;
++ info->rx_count = 0;
++ if (!(info->rx_skb = bluez_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC))) {
++ printk(KERN_WARNING "bluecard_cs: Can't allocate mem for new packet.\n");
++ return;
++ }
++ }
++
++ if (info->rx_state == RECV_WAIT_PACKET_TYPE) {
++
++ info->rx_skb->dev = (void *)&(info->hdev);
++ info->rx_skb->pkt_type = buf[i];
++
++ switch (info->rx_skb->pkt_type) {
++
++ case 0x00:
++ /* init packet */
++ if (offset != 0x00) {
++ set_bit(XMIT_BUF_ONE_READY, &(info->tx_state));
++ set_bit(XMIT_BUF_TWO_READY, &(info->tx_state));
++ set_bit(XMIT_SENDING_READY, &(info->tx_state));
++ bluecard_write_wakeup(info);
++ }
++
++ kfree_skb(info->rx_skb);
++ info->rx_skb = NULL;
++ break;
++
++ case HCI_EVENT_PKT:
++ info->rx_state = RECV_WAIT_EVENT_HEADER;
++ info->rx_count = HCI_EVENT_HDR_SIZE;
++ break;
++
++ case HCI_ACLDATA_PKT:
++ info->rx_state = RECV_WAIT_ACL_HEADER;
++ info->rx_count = HCI_ACL_HDR_SIZE;
++ break;
++
++ case HCI_SCODATA_PKT:
++ info->rx_state = RECV_WAIT_SCO_HEADER;
++ info->rx_count = HCI_SCO_HDR_SIZE;
++ break;
++
++ default:
++ /* unknown packet */
++ printk(KERN_WARNING "bluecard_cs: Unknown HCI packet with type 0x%02x received.\n", info->rx_skb->pkt_type);
++ info->hdev.stat.err_rx++;
++
++ kfree_skb(info->rx_skb);
++ info->rx_skb = NULL;
++ break;
++
++ }
++
++ } else {
++
++ *skb_put(info->rx_skb, 1) = buf[i];
++ info->rx_count--;
++
++ if (info->rx_count == 0) {
++
++ int dlen;
++ hci_event_hdr *eh;
++ hci_acl_hdr *ah;
++ hci_sco_hdr *sh;
++
++ switch (info->rx_state) {
++
++ case RECV_WAIT_EVENT_HEADER:
++ eh = (hci_event_hdr *)(info->rx_skb->data);
++ info->rx_state = RECV_WAIT_DATA;
++ info->rx_count = eh->plen;
++ break;
++
++ case RECV_WAIT_ACL_HEADER:
++ ah = (hci_acl_hdr *)(info->rx_skb->data);
++ dlen = __le16_to_cpu(ah->dlen);
++ info->rx_state = RECV_WAIT_DATA;
++ info->rx_count = dlen;
++ break;
++
++ case RECV_WAIT_SCO_HEADER:
++ sh = (hci_sco_hdr *)(info->rx_skb->data);
++ info->rx_state = RECV_WAIT_DATA;
++ info->rx_count = sh->dlen;
++ break;
++
++ case RECV_WAIT_DATA:
++ hci_recv_frame(info->rx_skb);
++ info->rx_skb = NULL;
++ break;
++
++ }
++
++ }
++
++ }
++
++
++ }
++
++ info->hdev.stat.byte_rx += len;
++}
++
++
++void bluecard_interrupt(int irq, void *dev_inst, struct pt_regs *regs)
++{
++ bluecard_info_t *info = dev_inst;
++ unsigned int iobase;
++ unsigned char reg;
++
++ if (!info) {
++ printk(KERN_WARNING "bluecard_cs: Call of irq %d for unknown device.\n", irq);
++ return;
++ }
++
++ if (!test_bit(CARD_READY, &(info->hw_state)))
++ return;
++
++ iobase = info->link.io.BasePort1;
++
++ spin_lock(&(info->lock));
++
++ /* Disable interrupt */
++ info->ctrl_reg &= ~REG_CONTROL_INTERRUPT;
++ outb(info->ctrl_reg, iobase + REG_CONTROL);
++
++ reg = inb(iobase + REG_INTERRUPT);
++
++ if ((reg != 0x00) && (reg != 0xff)) {
++
++ if (reg & 0x04) {
++ bluecard_receive(info, 0x00);
++ outb(0x04, iobase + REG_INTERRUPT);
++ outb(REG_COMMAND_RX_BUF_ONE, iobase + REG_COMMAND);
++ }
++
++ if (reg & 0x08) {
++ bluecard_receive(info, 0x10);
++ outb(0x08, iobase + REG_INTERRUPT);
++ outb(REG_COMMAND_RX_BUF_TWO, iobase + REG_COMMAND);
++ }
++
++ if (reg & 0x01) {
++ set_bit(XMIT_BUF_ONE_READY, &(info->tx_state));
++ outb(0x01, iobase + REG_INTERRUPT);
++ bluecard_write_wakeup(info);
++ }
++
++ if (reg & 0x02) {
++ set_bit(XMIT_BUF_TWO_READY, &(info->tx_state));
++ outb(0x02, iobase + REG_INTERRUPT);
++ bluecard_write_wakeup(info);
++ }
++
++ }
++
++ /* Enable interrupt */
++ info->ctrl_reg |= REG_CONTROL_INTERRUPT;
++ outb(info->ctrl_reg, iobase + REG_CONTROL);
++
++ spin_unlock(&(info->lock));
++}
++
++
++
++/* ======================== Device specific HCI commands ======================== */
++
++
++static int bluecard_hci_set_baud_rate(struct hci_dev *hdev, int baud)
++{
++ bluecard_info_t *info = (bluecard_info_t *)(hdev->driver_data);
++ struct sk_buff *skb;
++
++ /* Ericsson baud rate command */
++ unsigned char cmd[] = { HCI_COMMAND_PKT, 0x09, 0xfc, 0x01, 0x03 };
++
++ if (!(skb = bluez_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC))) {
++ printk(KERN_WARNING "bluecard_cs: Can't allocate mem for new packet.\n");
++ return -1;
++ }
++
++ switch (baud) {
++ case 460800:
++ cmd[4] = 0x00;
++ skb->pkt_type = PKT_BAUD_RATE_460800;
++ break;
++ case 230400:
++ cmd[4] = 0x01;
++ skb->pkt_type = PKT_BAUD_RATE_230400;
++ break;
++ case 115200:
++ cmd[4] = 0x02;
++ skb->pkt_type = PKT_BAUD_RATE_115200;
++ break;
++ case 57600:
++ /* Fall through... */
++ default:
++ cmd[4] = 0x03;
++ skb->pkt_type = PKT_BAUD_RATE_57600;
++ break;
++ }
++
++ memcpy(skb_put(skb, sizeof(cmd)), cmd, sizeof(cmd));
++
++ skb_queue_tail(&(info->txq), skb);
++
++ bluecard_write_wakeup(info);
++
++ return 0;
++}
++
++
++
++/* ======================== HCI interface ======================== */
++
++
++static int bluecard_hci_flush(struct hci_dev *hdev)
++{
++ bluecard_info_t *info = (bluecard_info_t *)(hdev->driver_data);
++
++ /* Drop TX queue */
++ skb_queue_purge(&(info->txq));
++
++ return 0;
++}
++
++
++static int bluecard_hci_open(struct hci_dev *hdev)
++{
++ bluecard_info_t *info = (bluecard_info_t *)(hdev->driver_data);
++ unsigned int iobase = info->link.io.BasePort1;
++
++ bluecard_hci_set_baud_rate(hdev, DEFAULT_BAUD_RATE);
++
++ if (test_and_set_bit(HCI_RUNNING, &(hdev->flags)))
++ return 0;
++
++ /* Enable LED */
++ outb(0x08 | 0x20, iobase + 0x30);
++
++ return 0;
++}
++
++
++static int bluecard_hci_close(struct hci_dev *hdev)
++{
++ bluecard_info_t *info = (bluecard_info_t *)(hdev->driver_data);
++ unsigned int iobase = info->link.io.BasePort1;
++
++ if (!test_and_clear_bit(HCI_RUNNING, &(hdev->flags)))
++ return 0;
++
++ bluecard_hci_flush(hdev);
++
++ /* Disable LED */
++ outb(0x00, iobase + 0x30);
++
++ return 0;
++}
++
++
++static int bluecard_hci_send_frame(struct sk_buff *skb)
++{
++ bluecard_info_t *info;
++ struct hci_dev *hdev = (struct hci_dev *)(skb->dev);
++
++ if (!hdev) {
++ printk(KERN_WARNING "bluecard_cs: Frame for unknown HCI device (hdev=NULL).");
++ return -ENODEV;
++ }
++
++ info = (bluecard_info_t *)(hdev->driver_data);
++
++ switch (skb->pkt_type) {
++ case HCI_COMMAND_PKT:
++ hdev->stat.cmd_tx++;
++ break;
++ case HCI_ACLDATA_PKT:
++ hdev->stat.acl_tx++;
++ break;
++ case HCI_SCODATA_PKT:
++ hdev->stat.sco_tx++;
++ break;
++ };
++
++ /* Prepend skb with frame type */
++ memcpy(skb_push(skb, 1), &(skb->pkt_type), 1);
++ skb_queue_tail(&(info->txq), skb);
++
++ bluecard_write_wakeup(info);
++
++ return 0;
++}
++
++
++static void bluecard_hci_destruct(struct hci_dev *hdev)
++{
++}
++
++
++static int bluecard_hci_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg)
++{
++ return -ENOIOCTLCMD;
++}
++
++
++
++/* ======================== Card services HCI interaction ======================== */
++
++
++int bluecard_open(bluecard_info_t *info)
++{
++ unsigned int iobase = info->link.io.BasePort1;
++ struct hci_dev *hdev;
++ unsigned char id;
++
++ spin_lock_init(&(info->lock));
++
++ init_timer(&(info->timer));
++ info->timer.function = &bluecard_activity_led_timeout;
++ info->timer.data = (u_long)info;
++
++ skb_queue_head_init(&(info->txq));
++
++ info->rx_state = RECV_WAIT_PACKET_TYPE;
++ info->rx_count = 0;
++ info->rx_skb = NULL;
++
++ id = inb(iobase + 0x30);
++
++ if ((id & 0x0f) == 0x02)
++ set_bit(CARD_HAS_PCCARD_ID, &(info->hw_state));
++
++ if (id & 0x10)
++ set_bit(CARD_HAS_POWER_LED, &(info->hw_state));
++
++ if (id & 0x20)
++ set_bit(CARD_HAS_ACTIVITY_LED, &(info->hw_state));
++
++ /* Reset card */
++ info->ctrl_reg = REG_CONTROL_BT_RESET | REG_CONTROL_CARD_RESET;
++ outb(info->ctrl_reg, iobase + REG_CONTROL);
++
++ /* Turn FPGA off */
++ outb(0x80, iobase + 0x30);
++
++ /* Wait some time */
++ set_current_state(TASK_INTERRUPTIBLE);
++ schedule_timeout(HZ / 100);
++
++ /* Turn FPGA on */
++ outb(0x00, iobase + 0x30);
++
++ /* Activate card */
++ info->ctrl_reg = REG_CONTROL_BT_ON | REG_CONTROL_BT_RES_PU;
++ outb(info->ctrl_reg, iobase + REG_CONTROL);
++
++ /* Enable interrupt */
++ outb(0xff, iobase + REG_INTERRUPT);
++ info->ctrl_reg |= REG_CONTROL_INTERRUPT;
++ outb(info->ctrl_reg, iobase + REG_CONTROL);
++
++ /* Start the RX buffers */
++ outb(REG_COMMAND_RX_BUF_ONE, iobase + REG_COMMAND);
++ outb(REG_COMMAND_RX_BUF_TWO, iobase + REG_COMMAND);
++
++ /* Signal that the hardware is ready */
++ set_bit(CARD_READY, &(info->hw_state));
++
++ /* Drop TX queue */
++ skb_queue_purge(&(info->txq));
++
++ /* Control the point at which RTS is enabled */
++ outb((0x0f << RTS_LEVEL_SHIFT_BITS) | 1, iobase + REG_RX_CONTROL);
++
++ /* Timeout before it is safe to send the first HCI packet */
++ set_current_state(TASK_INTERRUPTIBLE);
++ schedule_timeout((HZ * 5) / 4); // or set it to 3/2
++
++
++ /* Initialize and register HCI device */
++
++ hdev = &(info->hdev);
++
++ hdev->type = HCI_PCCARD;
++ hdev->driver_data = info;
++
++ hdev->open = bluecard_hci_open;
++ hdev->close = bluecard_hci_close;
++ hdev->flush = bluecard_hci_flush;
++ hdev->send = bluecard_hci_send_frame;
++ hdev->destruct = bluecard_hci_destruct;
++ hdev->ioctl = bluecard_hci_ioctl;
++
++ if (hci_register_dev(hdev) < 0) {
++ printk(KERN_WARNING "bluecard_cs: Can't register HCI device %s.\n", hdev->name);
++ return -ENODEV;
++ }
++
++ return 0;
++}
++
++
++int bluecard_close(bluecard_info_t *info)
++{
++ unsigned int iobase = info->link.io.BasePort1;
++ struct hci_dev *hdev = &(info->hdev);
++
++ if (info->link.state & DEV_CONFIG_PENDING)
++ return -ENODEV;
++
++ bluecard_hci_close(hdev);
++
++ clear_bit(CARD_READY, &(info->hw_state));
++
++ /* Reset card */
++ info->ctrl_reg = REG_CONTROL_BT_RESET | REG_CONTROL_CARD_RESET;
++ outb(info->ctrl_reg, iobase + REG_CONTROL);
++
++ /* Turn FPGA off */
++ outb(0x80, iobase + 0x30);
++
++ if (hci_unregister_dev(hdev) < 0)
++ printk(KERN_WARNING "bluecard_cs: Can't unregister HCI device %s.\n", hdev->name);
++
++ return 0;
++}
++
++
++
++/* ======================== Card services ======================== */
++
++
++static void cs_error(client_handle_t handle, int func, int ret)
++{
++ error_info_t err = { func, ret };
++
++ CardServices(ReportError, handle, &err);
++}
++
++
++dev_link_t *bluecard_attach(void)
++{
++ bluecard_info_t *info;
++ client_reg_t client_reg;
++ dev_link_t *link;
++ int i, ret;
++
++ /* Create new info device */
++ info = kmalloc(sizeof(*info), GFP_KERNEL);
++ if (!info)
++ return NULL;
++ memset(info, 0, sizeof(*info));
++
++ link = &info->link;
++ link->priv = info;
++
++ link->release.function = &bluecard_release;
++ link->release.data = (u_long)link;
++ link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
++ link->io.NumPorts1 = 8;
++ link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT;
++ link->irq.IRQInfo1 = IRQ_INFO2_VALID | IRQ_LEVEL_ID;
++
++ if (irq_list[0] == -1)
++ link->irq.IRQInfo2 = irq_mask;
++ else
++ for (i = 0; i < 4; i++)
++ link->irq.IRQInfo2 |= 1 << irq_list[i];
++
++ link->irq.Handler = bluecard_interrupt;
++ link->irq.Instance = info;
++
++ link->conf.Attributes = CONF_ENABLE_IRQ;
++ link->conf.Vcc = 50;
++ link->conf.IntType = INT_MEMORY_AND_IO;
++
++ /* Register with Card Services */
++ link->next = dev_list;
++ dev_list = link;
++ client_reg.dev_info = &dev_info;
++ client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE;
++ client_reg.EventMask =
++ CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
++ CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
++ CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
++ client_reg.event_handler = &bluecard_event;
++ client_reg.Version = 0x0210;
++ client_reg.event_callback_args.client_data = link;
++
++ ret = CardServices(RegisterClient, &link->handle, &client_reg);
++ if (ret != CS_SUCCESS) {
++ cs_error(link->handle, RegisterClient, ret);
++ bluecard_detach(link);
++ return NULL;
++ }
++
++ return link;
++}
++
++
++void bluecard_detach(dev_link_t *link)
++{
++ bluecard_info_t *info = link->priv;
++ dev_link_t **linkp;
++ int ret;
++
++ /* Locate device structure */
++ for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
++ if (*linkp == link)
++ break;
++
++ if (*linkp == NULL)
++ return;
++
++ del_timer(&link->release);
++ if (link->state & DEV_CONFIG)
++ bluecard_release((u_long)link);
++
++ if (link->handle) {
++ ret = CardServices(DeregisterClient, link->handle);
++ if (ret != CS_SUCCESS)
++ cs_error(link->handle, DeregisterClient, ret);
++ }
++
++ /* Unlink device structure, free bits */
++ *linkp = link->next;
++
++ kfree(info);
++}
++
++
++static int get_tuple(int fn, client_handle_t handle, tuple_t *tuple, cisparse_t *parse)
++{
++ int i;
++
++ i = CardServices(fn, handle, tuple);
++ if (i != CS_SUCCESS)
++ return CS_NO_MORE_ITEMS;
++
++ i = CardServices(GetTupleData, handle, tuple);
++ if (i != CS_SUCCESS)
++ return i;
++
++ return CardServices(ParseTuple, handle, tuple, parse);
++}
++
++
++#define first_tuple(a, b, c) get_tuple(GetFirstTuple, a, b, c)
++#define next_tuple(a, b, c) get_tuple(GetNextTuple, a, b, c)
++
++void bluecard_config(dev_link_t *link)
++{
++ client_handle_t handle = link->handle;
++ bluecard_info_t *info = link->priv;
++ tuple_t tuple;
++ u_short buf[256];
++ cisparse_t parse;
++ config_info_t config;
++ int i, n, last_ret, last_fn;
++
++ tuple.TupleData = (cisdata_t *)buf;
++ tuple.TupleOffset = 0;
++ tuple.TupleDataMax = 255;
++ tuple.Attributes = 0;
++
++ /* Get configuration register information */
++ tuple.DesiredTuple = CISTPL_CONFIG;
++ last_ret = first_tuple(handle, &tuple, &parse);
++ if (last_ret != CS_SUCCESS) {
++ last_fn = ParseTuple;
++ goto cs_failed;
++ }
++ link->conf.ConfigBase = parse.config.base;
++ link->conf.Present = parse.config.rmask[0];
++
++ /* Configure card */
++ link->state |= DEV_CONFIG;
++ i = CardServices(GetConfigurationInfo, handle, &config);
++ link->conf.Vcc = config.Vcc;
++
++ link->conf.ConfigIndex = 0x20;
++ link->io.NumPorts1 = 64;
++ link->io.IOAddrLines = 6;
++
++ for (n = 0; n < 0x400; n += 0x40) {
++ link->io.BasePort1 = n ^ 0x300;
++ i = CardServices(RequestIO, link->handle, &link->io);
++ if (i == CS_SUCCESS)
++ break;
++ }
++
++ if (i != CS_SUCCESS) {
++ cs_error(link->handle, RequestIO, i);
++ goto failed;
++ }
++
++ i = CardServices(RequestIRQ, link->handle, &link->irq);
++ if (i != CS_SUCCESS) {
++ cs_error(link->handle, RequestIRQ, i);
++ link->irq.AssignedIRQ = 0;
++ }
++
++ i = CardServices(RequestConfiguration, link->handle, &link->conf);
++ if (i != CS_SUCCESS) {
++ cs_error(link->handle, RequestConfiguration, i);
++ goto failed;
++ }
++
++ MOD_INC_USE_COUNT;
++
++ if (bluecard_open(info) != 0)
++ goto failed;
++
++ strcpy(info->node.dev_name, info->hdev.name);
++ link->dev = &info->node;
++ link->state &= ~DEV_CONFIG_PENDING;
++
++ return;
++
++cs_failed:
++ cs_error(link->handle, last_fn, last_ret);
++
++failed:
++ bluecard_release((u_long)link);
++}
++
++
++void bluecard_release(u_long arg)
++{
++ dev_link_t *link = (dev_link_t *)arg;
++ bluecard_info_t *info = link->priv;
++
++ if (link->state & DEV_PRESENT)
++ bluecard_close(info);
++
++ MOD_DEC_USE_COUNT;
++
++ link->dev = NULL;
++
++ CardServices(ReleaseConfiguration, link->handle);
++ CardServices(ReleaseIO, link->handle, &link->io);
++ CardServices(ReleaseIRQ, link->handle, &link->irq);
++
++ link->state &= ~DEV_CONFIG;
++}
++
++
++int bluecard_event(event_t event, int priority, event_callback_args_t *args)
++{
++ dev_link_t *link = args->client_data;
++ bluecard_info_t *info = link->priv;
++
++ switch (event) {
++ case CS_EVENT_CARD_REMOVAL:
++ link->state &= ~DEV_PRESENT;
++ if (link->state & DEV_CONFIG) {
++ bluecard_close(info);
++ mod_timer(&link->release, jiffies + HZ / 20);
++ }
++ break;
++ case CS_EVENT_CARD_INSERTION:
++ link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
++ bluecard_config(link);
++ break;
++ case CS_EVENT_PM_SUSPEND:
++ link->state |= DEV_SUSPEND;
++ /* Fall through... */
++ case CS_EVENT_RESET_PHYSICAL:
++ if (link->state & DEV_CONFIG)
++ CardServices(ReleaseConfiguration, link->handle);
++ break;
++ case CS_EVENT_PM_RESUME:
++ link->state &= ~DEV_SUSPEND;
++ /* Fall through... */
++ case CS_EVENT_CARD_RESET:
++ if (DEV_OK(link))
++ CardServices(RequestConfiguration, link->handle, &link->conf);
++ break;
++ }
++
++ return 0;
++}
++
++
++
++/* ======================== Module initialization ======================== */
++
++
++int __init init_bluecard_cs(void)
++{
++ servinfo_t serv;
++ int err;
++
++ CardServices(GetCardServicesInfo, &serv);
++ if (serv.Revision != CS_RELEASE_CODE) {
++ printk(KERN_NOTICE "bluecard_cs: Card Services release does not match!\n");
++ return -1;
++ }
++
++ err = register_pccard_driver(&dev_info, &bluecard_attach, &bluecard_detach);
++
++ return err;
++}
++
++
++void __exit exit_bluecard_cs(void)
++{
++ unregister_pccard_driver(&dev_info);
++
++ while (dev_list != NULL)
++ bluecard_detach(dev_list);
++}
++
++
++module_init(init_bluecard_cs);
++module_exit(exit_bluecard_cs);
++
++EXPORT_NO_SYMBOLS;
+diff -urN linux-2.4.18/drivers/bluetooth/bt3c_cs.c linux-2.4.18-mh15/drivers/bluetooth/bt3c_cs.c
+--- linux-2.4.18/drivers/bluetooth/bt3c_cs.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/drivers/bluetooth/bt3c_cs.c 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,986 @@
++/*
++ *
++ * Driver for the 3Com Bluetooth PCMCIA card
++ *
++ * Copyright (C) 2001-2002 Marcel Holtmann <marcel@holtmann.org>
++ * Jose Orlando Pereira <jop@di.uminho.pt>
++ *
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation;
++ *
++ * Software distributed under the License is distributed on an "AS
++ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
++ * implied. See the License for the specific language governing
++ * rights and limitations under the License.
++ *
++ * The initial developer of the original code is David A. Hinds
++ * <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
++ * are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
++ *
++ */
++
++#include <linux/config.h>
++#include <linux/module.h>
++
++#include <linux/kernel.h>
++#include <linux/kmod.h>
++#include <linux/init.h>
++#include <linux/slab.h>
++#include <linux/types.h>
++#include <linux/sched.h>
++#include <linux/delay.h>
++#include <linux/timer.h>
++#include <linux/errno.h>
++#include <linux/unistd.h>
++#include <linux/ptrace.h>
++#include <linux/ioport.h>
++#include <linux/spinlock.h>
++
++#include <linux/skbuff.h>
++#include <linux/string.h>
++#include <linux/serial.h>
++#include <linux/serial_reg.h>
++#include <asm/system.h>
++#include <asm/bitops.h>
++#include <asm/io.h>
++
++#include <linux/firmware.h>
++
++#include <pcmcia/version.h>
++#include <pcmcia/cs_types.h>
++#include <pcmcia/cs.h>
++#include <pcmcia/cistpl.h>
++#include <pcmcia/ciscode.h>
++#include <pcmcia/ds.h>
++#include <pcmcia/cisreg.h>
++
++#include <net/bluetooth/bluetooth.h>
++#include <net/bluetooth/hci_core.h>
++
++
++
++/* ======================== Module parameters ======================== */
++
++
++/* Bit map of interrupts to choose from */
++static u_int irq_mask = 0xffff;
++static int irq_list[4] = { -1 };
++
++MODULE_PARM(irq_mask, "i");
++MODULE_PARM(irq_list, "1-4i");
++
++MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>, Jose Orlando Pereira <jop@di.uminho.pt>");
++MODULE_DESCRIPTION("BlueZ driver for the 3Com Bluetooth PCMCIA card");
++MODULE_LICENSE("GPL");
++
++
++
++/* ======================== Local structures ======================== */
++
++
++typedef struct bt3c_info_t {
++ dev_link_t link;
++ dev_node_t node;
++
++ struct hci_dev hdev;
++
++ spinlock_t lock; /* For serializing operations */
++
++ struct sk_buff_head txq;
++ unsigned long tx_state;
++
++ unsigned long rx_state;
++ unsigned long rx_count;
++ struct sk_buff *rx_skb;
++} bt3c_info_t;
++
++
++void bt3c_config(dev_link_t *link);
++void bt3c_release(u_long arg);
++int bt3c_event(event_t event, int priority, event_callback_args_t *args);
++
++static dev_info_t dev_info = "bt3c_cs";
++
++dev_link_t *bt3c_attach(void);
++void bt3c_detach(dev_link_t *);
++
++static dev_link_t *dev_list = NULL;
++
++
++/* Transmit states */
++#define XMIT_SENDING 1
++#define XMIT_WAKEUP 2
++#define XMIT_WAITING 8
++
++/* Receiver states */
++#define RECV_WAIT_PACKET_TYPE 0
++#define RECV_WAIT_EVENT_HEADER 1
++#define RECV_WAIT_ACL_HEADER 2
++#define RECV_WAIT_SCO_HEADER 3
++#define RECV_WAIT_DATA 4
++
++
++
++/* ======================== Special I/O functions ======================== */
++
++
++#define DATA_L 0
++#define DATA_H 1
++#define ADDR_L 2
++#define ADDR_H 3
++#define CONTROL 4
++
++
++inline void bt3c_address(unsigned int iobase, unsigned short addr)
++{
++ outb(addr & 0xff, iobase + ADDR_L);
++ outb((addr >> 8) & 0xff, iobase + ADDR_H);
++}
++
++
++inline void bt3c_put(unsigned int iobase, unsigned short value)
++{
++ outb(value & 0xff, iobase + DATA_L);
++ outb((value >> 8) & 0xff, iobase + DATA_H);
++}
++
++
++inline void bt3c_io_write(unsigned int iobase, unsigned short addr, unsigned short value)
++{
++ bt3c_address(iobase, addr);
++ bt3c_put(iobase, value);
++}
++
++
++inline unsigned short bt3c_get(unsigned int iobase)
++{
++ unsigned short value = inb(iobase + DATA_L);
++
++ value |= inb(iobase + DATA_H) << 8;
++
++ return value;
++}
++
++
++inline unsigned short bt3c_read(unsigned int iobase, unsigned short addr)
++{
++ bt3c_address(iobase, addr);
++
++ return bt3c_get(iobase);
++}
++
++
++
++/* ======================== Interrupt handling ======================== */
++
++
++static int bt3c_write(unsigned int iobase, int fifo_size, __u8 *buf, int len)
++{
++ int actual = 0;
++
++ bt3c_address(iobase, 0x7080);
++
++ /* Fill FIFO with current frame */
++ while (actual < len) {
++ /* Transmit next byte */
++ bt3c_put(iobase, buf[actual]);
++ actual++;
++ }
++
++ bt3c_io_write(iobase, 0x7005, actual);
++
++ return actual;
++}
++
++
++static void bt3c_write_wakeup(bt3c_info_t *info, int from)
++{
++ unsigned long flags;
++
++ if (!info) {
++ printk(KERN_WARNING "bt3c_cs: Call of write_wakeup for unknown device.\n");
++ return;
++ }
++
++ if (test_and_set_bit(XMIT_SENDING, &(info->tx_state)))
++ return;
++
++ spin_lock_irqsave(&(info->lock), flags);
++
++ do {
++ register unsigned int iobase = info->link.io.BasePort1;
++ register struct sk_buff *skb;
++ register int len;
++
++ if (!(info->link.state & DEV_PRESENT))
++ break;
++
++
++ if (!(skb = skb_dequeue(&(info->txq)))) {
++ clear_bit(XMIT_SENDING, &(info->tx_state));
++ break;
++ }
++
++ /* Send frame */
++ len = bt3c_write(iobase, 256, skb->data, skb->len);
++
++ if (len != skb->len) {
++ printk(KERN_WARNING "bt3c_cs: very strange\n");
++ }
++
++ kfree_skb(skb);
++
++ info->hdev.stat.byte_tx += len;
++
++ } while (0);
++
++ spin_unlock_irqrestore(&(info->lock), flags);
++}
++
++
++static void bt3c_receive(bt3c_info_t *info)
++{
++ unsigned int iobase;
++ int size = 0, avail;
++
++ if (!info) {
++ printk(KERN_WARNING "bt3c_cs: Call of receive for unknown device.\n");
++ return;
++ }
++
++ iobase = info->link.io.BasePort1;
++
++ avail = bt3c_read(iobase, 0x7006);
++ //printk("bt3c_cs: receiving %d bytes\n", avail);
++
++ bt3c_address(iobase, 0x7480);
++ while (size < avail) {
++ size++;
++ info->hdev.stat.byte_rx++;
++
++ /* Allocate packet */
++ if (info->rx_skb == NULL) {
++ info->rx_state = RECV_WAIT_PACKET_TYPE;
++ info->rx_count = 0;
++ if (!(info->rx_skb = bluez_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC))) {
++ printk(KERN_WARNING "bt3c_cs: Can't allocate mem for new packet.\n");
++ return;
++ }
++ }
++
++
++ if (info->rx_state == RECV_WAIT_PACKET_TYPE) {
++
++ info->rx_skb->dev = (void *)&(info->hdev);
++ info->rx_skb->pkt_type = inb(iobase + DATA_L);
++ inb(iobase + DATA_H);
++ //printk("bt3c: PACKET_TYPE=%02x\n", info->rx_skb->pkt_type);
++
++ switch (info->rx_skb->pkt_type) {
++
++ case HCI_EVENT_PKT:
++ info->rx_state = RECV_WAIT_EVENT_HEADER;
++ info->rx_count = HCI_EVENT_HDR_SIZE;
++ break;
++
++ case HCI_ACLDATA_PKT:
++ info->rx_state = RECV_WAIT_ACL_HEADER;
++ info->rx_count = HCI_ACL_HDR_SIZE;
++ break;
++
++ case HCI_SCODATA_PKT:
++ info->rx_state = RECV_WAIT_SCO_HEADER;
++ info->rx_count = HCI_SCO_HDR_SIZE;
++ break;
++
++ default:
++ /* Unknown packet */
++ printk(KERN_WARNING "bt3c_cs: Unknown HCI packet with type 0x%02x received.\n", info->rx_skb->pkt_type);
++ info->hdev.stat.err_rx++;
++ clear_bit(HCI_RUNNING, &(info->hdev.flags));
++
++ kfree_skb(info->rx_skb);
++ info->rx_skb = NULL;
++ break;
++
++ }
++
++ } else {
++
++ __u8 x = inb(iobase + DATA_L);
++
++ *skb_put(info->rx_skb, 1) = x;
++ inb(iobase + DATA_H);
++ info->rx_count--;
++
++ if (info->rx_count == 0) {
++
++ int dlen;
++ hci_event_hdr *eh;
++ hci_acl_hdr *ah;
++ hci_sco_hdr *sh;
++
++ switch (info->rx_state) {
++
++ case RECV_WAIT_EVENT_HEADER:
++ eh = (hci_event_hdr *)(info->rx_skb->data);
++ info->rx_state = RECV_WAIT_DATA;
++ info->rx_count = eh->plen;
++ break;
++
++ case RECV_WAIT_ACL_HEADER:
++ ah = (hci_acl_hdr *)(info->rx_skb->data);
++ dlen = __le16_to_cpu(ah->dlen);
++ info->rx_state = RECV_WAIT_DATA;
++ info->rx_count = dlen;
++ break;
++
++ case RECV_WAIT_SCO_HEADER:
++ sh = (hci_sco_hdr *)(info->rx_skb->data);
++ info->rx_state = RECV_WAIT_DATA;
++ info->rx_count = sh->dlen;
++ break;
++
++ case RECV_WAIT_DATA:
++ hci_recv_frame(info->rx_skb);
++ info->rx_skb = NULL;
++ break;
++
++ }
++
++ }
++
++ }
++
++ }
++
++ bt3c_io_write(iobase, 0x7006, 0x0000);
++}
++
++
++void bt3c_interrupt(int irq, void *dev_inst, struct pt_regs *regs)
++{
++ bt3c_info_t *info = dev_inst;
++ unsigned int iobase;
++ int iir;
++
++ if (!info) {
++ printk(KERN_WARNING "bt3c_cs: Call of irq %d for unknown device.\n", irq);
++ return;
++ }
++
++ iobase = info->link.io.BasePort1;
++
++ spin_lock(&(info->lock));
++
++ iir = inb(iobase + CONTROL);
++ if (iir & 0x80) {
++ int stat = bt3c_read(iobase, 0x7001);
++
++ if ((stat & 0xff) == 0x7f) {
++ printk(KERN_WARNING "bt3c_cs: STRANGE stat=%04x\n", stat);
++ } else if ((stat & 0xff) != 0xff) {
++ if (stat & 0x0020) {
++ int stat = bt3c_read(iobase, 0x7002) & 0x10;
++ printk(KERN_WARNING "bt3c_cs: antena %s\n", stat ? "OUT" : "IN");
++ }
++ if (stat & 0x0001)
++ bt3c_receive(info);
++ if (stat & 0x0002) {
++ //printk("bt3c_cs: ACK %04x\n", stat);
++ clear_bit(XMIT_SENDING, &(info->tx_state));
++ bt3c_write_wakeup(info, 1);
++ }
++
++ bt3c_io_write(iobase, 0x7001, 0x0000);
++
++ outb(iir, iobase + CONTROL);
++ }
++ }
++
++ spin_unlock(&(info->lock));
++}
++
++
++
++
++/* ======================== HCI interface ======================== */
++
++
++static int bt3c_hci_flush(struct hci_dev *hdev)
++{
++ bt3c_info_t *info = (bt3c_info_t *)(hdev->driver_data);
++
++ /* Drop TX queue */
++ skb_queue_purge(&(info->txq));
++
++ return 0;
++}
++
++
++static int bt3c_hci_open(struct hci_dev *hdev)
++{
++ set_bit(HCI_RUNNING, &(hdev->flags));
++
++ return 0;
++}
++
++
++static int bt3c_hci_close(struct hci_dev *hdev)
++{
++ if (!test_and_clear_bit(HCI_RUNNING, &(hdev->flags)))
++ return 0;
++
++ bt3c_hci_flush(hdev);
++
++ return 0;
++}
++
++
++static int bt3c_hci_send_frame(struct sk_buff *skb)
++{
++ bt3c_info_t *info;
++ struct hci_dev *hdev = (struct hci_dev *)(skb->dev);
++
++ if (!hdev) {
++ printk(KERN_WARNING "bt3c_cs: Frame for unknown HCI device (hdev=NULL).");
++ return -ENODEV;
++ }
++
++ info = (bt3c_info_t *) (hdev->driver_data);
++
++ switch (skb->pkt_type) {
++ case HCI_COMMAND_PKT:
++ hdev->stat.cmd_tx++;
++ break;
++ case HCI_ACLDATA_PKT:
++ hdev->stat.acl_tx++;
++ break;
++ case HCI_SCODATA_PKT:
++ hdev->stat.sco_tx++;
++ break;
++ };
++
++ /* Prepend skb with frame type */
++ memcpy(skb_push(skb, 1), &(skb->pkt_type), 1);
++ skb_queue_tail(&(info->txq), skb);
++
++ bt3c_write_wakeup(info, 0);
++
++ return 0;
++}
++
++
++static void bt3c_hci_destruct(struct hci_dev *hdev)
++{
++}
++
++
++static int bt3c_hci_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg)
++{
++ return -ENOIOCTLCMD;
++}
++
++
++
++/* ======================== Card services HCI interaction ======================== */
++
++
++static int bt3c_load_firmware(bt3c_info_t *info, unsigned char *firmware, int count)
++{
++ char *ptr = (char *) firmware;
++ char b[9];
++ unsigned int iobase, size, addr, fcs, tmp;
++ int i, err = 0;
++
++ iobase = info->link.io.BasePort1;
++
++ /* Reset */
++
++ bt3c_io_write(iobase, 0x8040, 0x0404);
++ bt3c_io_write(iobase, 0x8040, 0x0400);
++
++ udelay(1);
++
++ bt3c_io_write(iobase, 0x8040, 0x0404);
++
++ udelay(17);
++
++ /* Load */
++
++ while (count) {
++ if (ptr[0] != 'S') {
++ printk(KERN_WARNING "bt3c_cs: Bad address in firmware.\n");
++ err = -EFAULT;
++ goto error;
++ }
++
++ memset(b, 0, sizeof(b));
++ memcpy(b, ptr + 2, 2);
++ size = simple_strtol(b, NULL, 16);
++
++ memset(b, 0, sizeof(b));
++ memcpy(b, ptr + 4, 8);
++ addr = simple_strtol(b, NULL, 16);
++
++ memset(b, 0, sizeof(b));
++ memcpy(b, ptr + (size * 2) + 2, 2);
++ fcs = simple_strtol(b, NULL, 16);
++
++ memset(b, 0, sizeof(b));
++ for (tmp = 0, i = 0; i < size; i++) {
++ memcpy(b, ptr + (i * 2) + 2, 2);
++ tmp += simple_strtol(b, NULL, 16);
++ }
++
++ if (((tmp + fcs) & 0xff) != 0xff) {
++ printk(KERN_WARNING "bt3c_cs: Checksum error in firmware.\n");
++ err = -EILSEQ;
++ goto error;
++ }
++
++ if (ptr[1] == '3') {
++ bt3c_address(iobase, addr);
++
++ memset(b, 0, sizeof(b));
++ for (i = 0; i < (size - 4) / 2; i++) {
++ memcpy(b, ptr + (i * 4) + 12, 4);
++ tmp = simple_strtol(b, NULL, 16);
++ bt3c_put(iobase, tmp);
++ }
++ }
++
++ ptr += (size * 2) + 6;
++ count -= (size * 2) + 6;
++ }
++
++ udelay(17);
++
++ /* Boot */
++
++ bt3c_address(iobase, 0x3000);
++ outb(inb(iobase + CONTROL) | 0x40, iobase + CONTROL);
++
++error:
++ udelay(17);
++
++ /* Clear */
++
++ bt3c_io_write(iobase, 0x7006, 0x0000);
++ bt3c_io_write(iobase, 0x7005, 0x0000);
++ bt3c_io_write(iobase, 0x7001, 0x0000);
++
++ return err;
++}
++
++
++int bt3c_open(bt3c_info_t *info)
++{
++ const struct firmware *firmware;
++ char device[16];
++ struct hci_dev *hdev;
++ int err;
++
++ spin_lock_init(&(info->lock));
++
++ skb_queue_head_init(&(info->txq));
++
++ info->rx_state = RECV_WAIT_PACKET_TYPE;
++ info->rx_count = 0;
++ info->rx_skb = NULL;
++
++ /* Load firmware */
++
++ snprintf(device, sizeof(device), "bt3c%4.4x", info->link.io.BasePort1);
++
++ err = request_firmware(&firmware, "BT3CPCC.bin", device);
++ if (err < 0) {
++ printk(KERN_WARNING "bt3c_cs: Firmware request failed.\n");
++ return err;
++ }
++
++ err = bt3c_load_firmware(info, firmware->data, firmware->size);
++
++ release_firmware(firmware);
++
++ if (err < 0) {
++ printk(KERN_WARNING "bt3c_cs: Firmware loading failed.\n");
++ return err;
++ }
++
++ /* Timeout before it is safe to send the first HCI packet */
++
++ set_current_state(TASK_INTERRUPTIBLE);
++ schedule_timeout(HZ);
++
++
++ /* Initialize and register HCI device */
++
++ hdev = &(info->hdev);
++
++ hdev->type = HCI_PCCARD;
++ hdev->driver_data = info;
++
++ hdev->open = bt3c_hci_open;
++ hdev->close = bt3c_hci_close;
++ hdev->flush = bt3c_hci_flush;
++ hdev->send = bt3c_hci_send_frame;
++ hdev->destruct = bt3c_hci_destruct;
++ hdev->ioctl = bt3c_hci_ioctl;
++
++ if (hci_register_dev(hdev) < 0) {
++ printk(KERN_WARNING "bt3c_cs: Can't register HCI device %s.\n", hdev->name);
++ return -ENODEV;
++ }
++
++ return 0;
++}
++
++
++int bt3c_close(bt3c_info_t *info)
++{
++ struct hci_dev *hdev = &(info->hdev);
++
++ if (info->link.state & DEV_CONFIG_PENDING)
++ return -ENODEV;
++
++ bt3c_hci_close(hdev);
++
++ if (hci_unregister_dev(hdev) < 0)
++ printk(KERN_WARNING "bt3c_cs: Can't unregister HCI device %s.\n", hdev->name);
++
++ return 0;
++}
++
++
++
++/* ======================== Card services ======================== */
++
++
++static void cs_error(client_handle_t handle, int func, int ret)
++{
++ error_info_t err = { func, ret };
++
++ CardServices(ReportError, handle, &err);
++}
++
++
++dev_link_t *bt3c_attach(void)
++{
++ bt3c_info_t *info;
++ client_reg_t client_reg;
++ dev_link_t *link;
++ int i, ret;
++
++ /* Create new info device */
++ info = kmalloc(sizeof(*info), GFP_KERNEL);
++ if (!info)
++ return NULL;
++ memset(info, 0, sizeof(*info));
++
++ link = &info->link;
++ link->priv = info;
++
++ link->release.function = &bt3c_release;
++ link->release.data = (u_long)link;
++ link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
++ link->io.NumPorts1 = 8;
++ link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT;
++ link->irq.IRQInfo1 = IRQ_INFO2_VALID | IRQ_LEVEL_ID;
++
++ if (irq_list[0] == -1)
++ link->irq.IRQInfo2 = irq_mask;
++ else
++ for (i = 0; i < 4; i++)
++ link->irq.IRQInfo2 |= 1 << irq_list[i];
++
++ link->irq.Handler = bt3c_interrupt;
++ link->irq.Instance = info;
++
++ link->conf.Attributes = CONF_ENABLE_IRQ;
++ link->conf.Vcc = 50;
++ link->conf.IntType = INT_MEMORY_AND_IO;
++
++ /* Register with Card Services */
++ link->next = dev_list;
++ dev_list = link;
++ client_reg.dev_info = &dev_info;
++ client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE;
++ client_reg.EventMask =
++ CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
++ CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
++ CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
++ client_reg.event_handler = &bt3c_event;
++ client_reg.Version = 0x0210;
++ client_reg.event_callback_args.client_data = link;
++
++ ret = CardServices(RegisterClient, &link->handle, &client_reg);
++ if (ret != CS_SUCCESS) {
++ cs_error(link->handle, RegisterClient, ret);
++ bt3c_detach(link);
++ return NULL;
++ }
++
++ return link;
++}
++
++
++void bt3c_detach(dev_link_t *link)
++{
++ bt3c_info_t *info = link->priv;
++ dev_link_t **linkp;
++ int ret;
++
++ /* Locate device structure */
++ for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
++ if (*linkp == link)
++ break;
++
++ if (*linkp == NULL)
++ return;
++
++ del_timer(&link->release);
++
++ if (link->state & DEV_CONFIG)
++ bt3c_release((u_long)link);
++
++ if (link->handle) {
++ ret = CardServices(DeregisterClient, link->handle);
++ if (ret != CS_SUCCESS)
++ cs_error(link->handle, DeregisterClient, ret);
++ }
++
++ /* Unlink device structure, free bits */
++ *linkp = link->next;
++
++ kfree(info);
++}
++
++
++static int get_tuple(int fn, client_handle_t handle, tuple_t *tuple, cisparse_t *parse)
++{
++ int i;
++
++ i = CardServices(fn, handle, tuple);
++ if (i != CS_SUCCESS)
++ return CS_NO_MORE_ITEMS;
++
++ i = CardServices(GetTupleData, handle, tuple);
++ if (i != CS_SUCCESS)
++ return i;
++
++ return CardServices(ParseTuple, handle, tuple, parse);
++}
++
++
++#define first_tuple(a, b, c) get_tuple(GetFirstTuple, a, b, c)
++#define next_tuple(a, b, c) get_tuple(GetNextTuple, a, b, c)
++
++void bt3c_config(dev_link_t *link)
++{
++ static ioaddr_t base[5] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, 0x0 };
++ client_handle_t handle = link->handle;
++ bt3c_info_t *info = link->priv;
++ tuple_t tuple;
++ u_short buf[256];
++ cisparse_t parse;
++ cistpl_cftable_entry_t *cf = &parse.cftable_entry;
++ config_info_t config;
++ int i, j, try, last_ret, last_fn;
++
++ tuple.TupleData = (cisdata_t *)buf;
++ tuple.TupleOffset = 0;
++ tuple.TupleDataMax = 255;
++ tuple.Attributes = 0;
++
++ /* Get configuration register information */
++ tuple.DesiredTuple = CISTPL_CONFIG;
++ last_ret = first_tuple(handle, &tuple, &parse);
++ if (last_ret != CS_SUCCESS) {
++ last_fn = ParseTuple;
++ goto cs_failed;
++ }
++ link->conf.ConfigBase = parse.config.base;
++ link->conf.Present = parse.config.rmask[0];
++
++ /* Configure card */
++ link->state |= DEV_CONFIG;
++ i = CardServices(GetConfigurationInfo, handle, &config);
++ link->conf.Vcc = config.Vcc;
++
++ /* First pass: look for a config entry that looks normal. */
++ tuple.TupleData = (cisdata_t *)buf;
++ tuple.TupleOffset = 0;
++ tuple.TupleDataMax = 255;
++ tuple.Attributes = 0;
++ tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
++ /* Two tries: without IO aliases, then with aliases */
++ for (try = 0; try < 2; try++) {
++ i = first_tuple(handle, &tuple, &parse);
++ while (i != CS_NO_MORE_ITEMS) {
++ if (i != CS_SUCCESS)
++ goto next_entry;
++ if (cf->vpp1.present & (1 << CISTPL_POWER_VNOM))
++ link->conf.Vpp1 = link->conf.Vpp2 = cf->vpp1.param[CISTPL_POWER_VNOM] / 10000;
++ if ((cf->io.nwin > 0) && (cf->io.win[0].len == 8) && (cf->io.win[0].base != 0)) {
++ link->conf.ConfigIndex = cf->index;
++ link->io.BasePort1 = cf->io.win[0].base;
++ link->io.IOAddrLines = (try == 0) ? 16 : cf->io.flags & CISTPL_IO_LINES_MASK;
++ i = CardServices(RequestIO, link->handle, &link->io);
++ if (i == CS_SUCCESS)
++ goto found_port;
++ }
++next_entry:
++ i = next_tuple(handle, &tuple, &parse);
++ }
++ }
++
++ /* Second pass: try to find an entry that isn't picky about
++ its base address, then try to grab any standard serial port
++ address, and finally try to get any free port. */
++ i = first_tuple(handle, &tuple, &parse);
++ while (i != CS_NO_MORE_ITEMS) {
++ if ((i == CS_SUCCESS) && (cf->io.nwin > 0) && ((cf->io.flags & CISTPL_IO_LINES_MASK) <= 3)) {
++ link->conf.ConfigIndex = cf->index;
++ for (j = 0; j < 5; j++) {
++ link->io.BasePort1 = base[j];
++ link->io.IOAddrLines = base[j] ? 16 : 3;
++ i = CardServices(RequestIO, link->handle, &link->io);
++ if (i == CS_SUCCESS)
++ goto found_port;
++ }
++ }
++ i = next_tuple(handle, &tuple, &parse);
++ }
++
++found_port:
++ if (i != CS_SUCCESS) {
++ printk(KERN_NOTICE "bt3c_cs: No usable port range found. Giving up.\n");
++ cs_error(link->handle, RequestIO, i);
++ goto failed;
++ }
++
++ i = CardServices(RequestIRQ, link->handle, &link->irq);
++ if (i != CS_SUCCESS) {
++ cs_error(link->handle, RequestIRQ, i);
++ link->irq.AssignedIRQ = 0;
++ }
++
++ i = CardServices(RequestConfiguration, link->handle, &link->conf);
++ if (i != CS_SUCCESS) {
++ cs_error(link->handle, RequestConfiguration, i);
++ goto failed;
++ }
++
++ MOD_INC_USE_COUNT;
++
++ if (bt3c_open(info) != 0)
++ goto failed;
++
++ strcpy(info->node.dev_name, info->hdev.name);
++ link->dev = &info->node;
++ link->state &= ~DEV_CONFIG_PENDING;
++
++ return;
++
++cs_failed:
++ cs_error(link->handle, last_fn, last_ret);
++
++failed:
++ bt3c_release((u_long)link);
++}
++
++
++void bt3c_release(u_long arg)
++{
++ dev_link_t *link = (dev_link_t *)arg;
++ bt3c_info_t *info = link->priv;
++
++ if (link->state & DEV_PRESENT)
++ bt3c_close(info);
++
++ MOD_DEC_USE_COUNT;
++
++ link->dev = NULL;
++
++ CardServices(ReleaseConfiguration, link->handle);
++ CardServices(ReleaseIO, link->handle, &link->io);
++ CardServices(ReleaseIRQ, link->handle, &link->irq);
++
++ link->state &= ~DEV_CONFIG;
++}
++
++
++int bt3c_event(event_t event, int priority, event_callback_args_t *args)
++{
++ dev_link_t *link = args->client_data;
++ bt3c_info_t *info = link->priv;
++
++ switch (event) {
++ case CS_EVENT_CARD_REMOVAL:
++ link->state &= ~DEV_PRESENT;
++ if (link->state & DEV_CONFIG) {
++ bt3c_close(info);
++ mod_timer(&link->release, jiffies + HZ / 20);
++ }
++ break;
++ case CS_EVENT_CARD_INSERTION:
++ link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
++ bt3c_config(link);
++ break;
++ case CS_EVENT_PM_SUSPEND:
++ link->state |= DEV_SUSPEND;
++ /* Fall through... */
++ case CS_EVENT_RESET_PHYSICAL:
++ if (link->state & DEV_CONFIG)
++ CardServices(ReleaseConfiguration, link->handle);
++ break;
++ case CS_EVENT_PM_RESUME:
++ link->state &= ~DEV_SUSPEND;
++ /* Fall through... */
++ case CS_EVENT_CARD_RESET:
++ if (DEV_OK(link))
++ CardServices(RequestConfiguration, link->handle, &link->conf);
++ break;
++ }
++
++ return 0;
++}
++
++
++
++/* ======================== Module initialization ======================== */
++
++
++int __init init_bt3c_cs(void)
++{
++ servinfo_t serv;
++ int err;
++
++ CardServices(GetCardServicesInfo, &serv);
++ if (serv.Revision != CS_RELEASE_CODE) {
++ printk(KERN_NOTICE "bt3c_cs: Card Services release does not match!\n");
++ return -1;
++ }
++
++ err = register_pccard_driver(&dev_info, &bt3c_attach, &bt3c_detach);
++
++ return err;
++}
++
++
++void __exit exit_bt3c_cs(void)
++{
++ unregister_pccard_driver(&dev_info);
++
++ while (dev_list != NULL)
++ bt3c_detach(dev_list);
++}
++
++
++module_init(init_bt3c_cs);
++module_exit(exit_bt3c_cs);
++
++EXPORT_NO_SYMBOLS;
+diff -urN linux-2.4.18/drivers/bluetooth/btuart_cs.c linux-2.4.18-mh15/drivers/bluetooth/btuart_cs.c
+--- linux-2.4.18/drivers/bluetooth/btuart_cs.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/drivers/bluetooth/btuart_cs.c 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,909 @@
++/*
++ *
++ * Driver for Bluetooth PCMCIA cards with HCI UART interface
++ *
++ * Copyright (C) 2001-2002 Marcel Holtmann <marcel@holtmann.org>
++ *
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation;
++ *
++ * Software distributed under the License is distributed on an "AS
++ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
++ * implied. See the License for the specific language governing
++ * rights and limitations under the License.
++ *
++ * The initial developer of the original code is David A. Hinds
++ * <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
++ * are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
++ *
++ */
++
++#include <linux/config.h>
++#include <linux/module.h>
++
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/slab.h>
++#include <linux/types.h>
++#include <linux/sched.h>
++#include <linux/timer.h>
++#include <linux/errno.h>
++#include <linux/ptrace.h>
++#include <linux/ioport.h>
++#include <linux/spinlock.h>
++
++#include <linux/skbuff.h>
++#include <linux/string.h>
++#include <linux/serial.h>
++#include <linux/serial_reg.h>
++#include <asm/system.h>
++#include <asm/bitops.h>
++#include <asm/io.h>
++
++#include <pcmcia/version.h>
++#include <pcmcia/cs_types.h>
++#include <pcmcia/cs.h>
++#include <pcmcia/cistpl.h>
++#include <pcmcia/ciscode.h>
++#include <pcmcia/ds.h>
++#include <pcmcia/cisreg.h>
++
++#include <net/bluetooth/bluetooth.h>
++#include <net/bluetooth/hci_core.h>
++
++
++
++/* ======================== Module parameters ======================== */
++
++
++/* Bit map of interrupts to choose from */
++static u_int irq_mask = 0xffff;
++static int irq_list[4] = { -1 };
++
++MODULE_PARM(irq_mask, "i");
++MODULE_PARM(irq_list, "1-4i");
++
++MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
++MODULE_DESCRIPTION("BlueZ driver for Bluetooth PCMCIA cards with HCI UART interface");
++MODULE_LICENSE("GPL");
++
++
++
++/* ======================== Local structures ======================== */
++
++
++typedef struct btuart_info_t {
++ dev_link_t link;
++ dev_node_t node;
++
++ struct hci_dev hdev;
++
++ spinlock_t lock; /* For serializing operations */
++
++ struct sk_buff_head txq;
++ unsigned long tx_state;
++
++ unsigned long rx_state;
++ unsigned long rx_count;
++ struct sk_buff *rx_skb;
++} btuart_info_t;
++
++
++void btuart_config(dev_link_t *link);
++void btuart_release(u_long arg);
++int btuart_event(event_t event, int priority, event_callback_args_t *args);
++
++static dev_info_t dev_info = "btuart_cs";
++
++dev_link_t *btuart_attach(void);
++void btuart_detach(dev_link_t *);
++
++static dev_link_t *dev_list = NULL;
++
++
++/* Maximum baud rate */
++#define SPEED_MAX 115200
++
++/* Default baud rate: 57600, 115200, 230400 or 460800 */
++#define DEFAULT_BAUD_RATE 115200
++
++
++/* Transmit states */
++#define XMIT_SENDING 1
++#define XMIT_WAKEUP 2
++#define XMIT_WAITING 8
++
++/* Receiver states */
++#define RECV_WAIT_PACKET_TYPE 0
++#define RECV_WAIT_EVENT_HEADER 1
++#define RECV_WAIT_ACL_HEADER 2
++#define RECV_WAIT_SCO_HEADER 3
++#define RECV_WAIT_DATA 4
++
++
++
++/* ======================== Interrupt handling ======================== */
++
++
++static int btuart_write(unsigned int iobase, int fifo_size, __u8 *buf, int len)
++{
++ int actual = 0;
++
++ /* Tx FIFO should be empty */
++ if (!(inb(iobase + UART_LSR) & UART_LSR_THRE))
++ return 0;
++
++ /* Fill FIFO with current frame */
++ while ((fifo_size-- > 0) && (actual < len)) {
++ /* Transmit next byte */
++ outb(buf[actual], iobase + UART_TX);
++ actual++;
++ }
++
++ return actual;
++}
++
++
++static void btuart_write_wakeup(btuart_info_t *info)
++{
++ if (!info) {
++ printk(KERN_WARNING "btuart_cs: Call of write_wakeup for unknown device.\n");
++ return;
++ }
++
++ if (test_and_set_bit(XMIT_SENDING, &(info->tx_state))) {
++ set_bit(XMIT_WAKEUP, &(info->tx_state));
++ return;
++ }
++
++ do {
++ register unsigned int iobase = info->link.io.BasePort1;
++ register struct sk_buff *skb;
++ register int len;
++
++ clear_bit(XMIT_WAKEUP, &(info->tx_state));
++
++ if (!(info->link.state & DEV_PRESENT))
++ return;
++
++ if (!(skb = skb_dequeue(&(info->txq))))
++ break;
++
++ /* Send frame */
++ len = btuart_write(iobase, 16, skb->data, skb->len);
++ set_bit(XMIT_WAKEUP, &(info->tx_state));
++
++ if (len == skb->len) {
++ kfree_skb(skb);
++ } else {
++ skb_pull(skb, len);
++ skb_queue_head(&(info->txq), skb);
++ }
++
++ info->hdev.stat.byte_tx += len;
++
++ } while (test_bit(XMIT_WAKEUP, &(info->tx_state)));
++
++ clear_bit(XMIT_SENDING, &(info->tx_state));
++}
++
++
++static void btuart_receive(btuart_info_t *info)
++{
++ unsigned int iobase;
++ int boguscount = 0;
++
++ if (!info) {
++ printk(KERN_WARNING "btuart_cs: Call of receive for unknown device.\n");
++ return;
++ }
++
++ iobase = info->link.io.BasePort1;
++
++ do {
++ info->hdev.stat.byte_rx++;
++
++ /* Allocate packet */
++ if (info->rx_skb == NULL) {
++ info->rx_state = RECV_WAIT_PACKET_TYPE;
++ info->rx_count = 0;
++ if (!(info->rx_skb = bluez_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC))) {
++ printk(KERN_WARNING "btuart_cs: Can't allocate mem for new packet.\n");
++ return;
++ }
++ }
++
++ if (info->rx_state == RECV_WAIT_PACKET_TYPE) {
++
++ info->rx_skb->dev = (void *)&(info->hdev);
++ info->rx_skb->pkt_type = inb(iobase + UART_RX);
++
++ switch (info->rx_skb->pkt_type) {
++
++ case HCI_EVENT_PKT:
++ info->rx_state = RECV_WAIT_EVENT_HEADER;
++ info->rx_count = HCI_EVENT_HDR_SIZE;
++ break;
++
++ case HCI_ACLDATA_PKT:
++ info->rx_state = RECV_WAIT_ACL_HEADER;
++ info->rx_count = HCI_ACL_HDR_SIZE;
++ break;
++
++ case HCI_SCODATA_PKT:
++ info->rx_state = RECV_WAIT_SCO_HEADER;
++ info->rx_count = HCI_SCO_HDR_SIZE;
++ break;
++
++ default:
++ /* Unknown packet */
++ printk(KERN_WARNING "btuart_cs: Unknown HCI packet with type 0x%02x received.\n", info->rx_skb->pkt_type);
++ info->hdev.stat.err_rx++;
++ clear_bit(HCI_RUNNING, &(info->hdev.flags));
++
++ kfree_skb(info->rx_skb);
++ info->rx_skb = NULL;
++ break;
++
++ }
++
++ } else {
++
++ *skb_put(info->rx_skb, 1) = inb(iobase + UART_RX);
++ info->rx_count--;
++
++ if (info->rx_count == 0) {
++
++ int dlen;
++ hci_event_hdr *eh;
++ hci_acl_hdr *ah;
++ hci_sco_hdr *sh;
++
++
++ switch (info->rx_state) {
++
++ case RECV_WAIT_EVENT_HEADER:
++ eh = (hci_event_hdr *)(info->rx_skb->data);
++ info->rx_state = RECV_WAIT_DATA;
++ info->rx_count = eh->plen;
++ break;
++
++ case RECV_WAIT_ACL_HEADER:
++ ah = (hci_acl_hdr *)(info->rx_skb->data);
++ dlen = __le16_to_cpu(ah->dlen);
++ info->rx_state = RECV_WAIT_DATA;
++ info->rx_count = dlen;
++ break;
++
++ case RECV_WAIT_SCO_HEADER:
++ sh = (hci_sco_hdr *)(info->rx_skb->data);
++ info->rx_state = RECV_WAIT_DATA;
++ info->rx_count = sh->dlen;
++ break;
++
++ case RECV_WAIT_DATA:
++ hci_recv_frame(info->rx_skb);
++ info->rx_skb = NULL;
++ break;
++
++ }
++
++ }
++
++ }
++
++ /* Make sure we don't stay here to long */
++ if (boguscount++ > 16)
++ break;
++
++ } while (inb(iobase + UART_LSR) & UART_LSR_DR);
++}
++
++
++void btuart_interrupt(int irq, void *dev_inst, struct pt_regs *regs)
++{
++ btuart_info_t *info = dev_inst;
++ unsigned int iobase;
++ int boguscount = 0;
++ int iir, lsr;
++
++ if (!info) {
++ printk(KERN_WARNING "btuart_cs: Call of irq %d for unknown device.\n", irq);
++ return;
++ }
++
++ iobase = info->link.io.BasePort1;
++
++ spin_lock(&(info->lock));
++
++ iir = inb(iobase + UART_IIR) & UART_IIR_ID;
++ while (iir) {
++
++ /* Clear interrupt */
++ lsr = inb(iobase + UART_LSR);
++
++ switch (iir) {
++ case UART_IIR_RLSI:
++ printk(KERN_NOTICE "btuart_cs: RLSI\n");
++ break;
++ case UART_IIR_RDI:
++ /* Receive interrupt */
++ btuart_receive(info);
++ break;
++ case UART_IIR_THRI:
++ if (lsr & UART_LSR_THRE) {
++ /* Transmitter ready for data */
++ btuart_write_wakeup(info);
++ }
++ break;
++ default:
++ printk(KERN_NOTICE "btuart_cs: Unhandled IIR=%#x\n", iir);
++ break;
++ }
++
++ /* Make sure we don't stay here to long */
++ if (boguscount++ > 100)
++ break;
++
++ iir = inb(iobase + UART_IIR) & UART_IIR_ID;
++
++ }
++
++ spin_unlock(&(info->lock));
++}
++
++
++static void btuart_change_speed(btuart_info_t *info, unsigned int speed)
++{
++ unsigned long flags;
++ unsigned int iobase;
++ int fcr; /* FIFO control reg */
++ int lcr; /* Line control reg */
++ int divisor;
++
++ if (!info) {
++ printk(KERN_WARNING "btuart_cs: Call of change speed for unknown device.\n");
++ return;
++ }
++
++ iobase = info->link.io.BasePort1;
++
++ spin_lock_irqsave(&(info->lock), flags);
++
++ /* Turn off interrupts */
++ outb(0, iobase + UART_IER);
++
++ divisor = SPEED_MAX / speed;
++
++ fcr = UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT;
++
++ /*
++ * Use trigger level 1 to avoid 3 ms. timeout delay at 9600 bps, and
++ * almost 1,7 ms at 19200 bps. At speeds above that we can just forget
++ * about this timeout since it will always be fast enough.
++ */
++
++ if (speed < 38400)
++ fcr |= UART_FCR_TRIGGER_1;
++ else
++ fcr |= UART_FCR_TRIGGER_14;
++
++ /* Bluetooth cards use 8N1 */
++ lcr = UART_LCR_WLEN8;
++
++ outb(UART_LCR_DLAB | lcr, iobase + UART_LCR); /* Set DLAB */
++ outb(divisor & 0xff, iobase + UART_DLL); /* Set speed */
++ outb(divisor >> 8, iobase + UART_DLM);
++ outb(lcr, iobase + UART_LCR); /* Set 8N1 */
++ outb(fcr, iobase + UART_FCR); /* Enable FIFO's */
++
++ /* Turn on interrups */
++ outb(UART_IER_RLSI | UART_IER_RDI | UART_IER_THRI, iobase + UART_IER);
++
++ spin_unlock_irqrestore(&(info->lock), flags);
++}
++
++
++
++/* ======================== HCI interface ======================== */
++
++
++static int btuart_hci_flush(struct hci_dev *hdev)
++{
++ btuart_info_t *info = (btuart_info_t *)(hdev->driver_data);
++
++ /* Drop TX queue */
++ skb_queue_purge(&(info->txq));
++
++ return 0;
++}
++
++
++static int btuart_hci_open(struct hci_dev *hdev)
++{
++ set_bit(HCI_RUNNING, &(hdev->flags));
++
++ return 0;
++}
++
++
++static int btuart_hci_close(struct hci_dev *hdev)
++{
++ if (!test_and_clear_bit(HCI_RUNNING, &(hdev->flags)))
++ return 0;
++
++ btuart_hci_flush(hdev);
++
++ return 0;
++}
++
++
++static int btuart_hci_send_frame(struct sk_buff *skb)
++{
++ btuart_info_t *info;
++ struct hci_dev *hdev = (struct hci_dev *)(skb->dev);
++
++ if (!hdev) {
++ printk(KERN_WARNING "btuart_cs: Frame for unknown HCI device (hdev=NULL).");
++ return -ENODEV;
++ }
++
++ info = (btuart_info_t *)(hdev->driver_data);
++
++ switch (skb->pkt_type) {
++ case HCI_COMMAND_PKT:
++ hdev->stat.cmd_tx++;
++ break;
++ case HCI_ACLDATA_PKT:
++ hdev->stat.acl_tx++;
++ break;
++ case HCI_SCODATA_PKT:
++ hdev->stat.sco_tx++;
++ break;
++ };
++
++ /* Prepend skb with frame type */
++ memcpy(skb_push(skb, 1), &(skb->pkt_type), 1);
++ skb_queue_tail(&(info->txq), skb);
++
++ btuart_write_wakeup(info);
++
++ return 0;
++}
++
++
++static void btuart_hci_destruct(struct hci_dev *hdev)
++{
++}
++
++
++static int btuart_hci_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg)
++{
++ return -ENOIOCTLCMD;
++}
++
++
++
++/* ======================== Card services HCI interaction ======================== */
++
++
++int btuart_open(btuart_info_t *info)
++{
++ unsigned long flags;
++ unsigned int iobase = info->link.io.BasePort1;
++ struct hci_dev *hdev;
++
++ spin_lock_init(&(info->lock));
++
++ skb_queue_head_init(&(info->txq));
++
++ info->rx_state = RECV_WAIT_PACKET_TYPE;
++ info->rx_count = 0;
++ info->rx_skb = NULL;
++
++ spin_lock_irqsave(&(info->lock), flags);
++
++ /* Reset UART */
++ outb(0, iobase + UART_MCR);
++
++ /* Turn off interrupts */
++ outb(0, iobase + UART_IER);
++
++ /* Initialize UART */
++ outb(UART_LCR_WLEN8, iobase + UART_LCR); /* Reset DLAB */
++ outb((UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2), iobase + UART_MCR);
++
++ /* Turn on interrupts */
++ // outb(UART_IER_RLSI | UART_IER_RDI | UART_IER_THRI, iobase + UART_IER);
++
++ spin_unlock_irqrestore(&(info->lock), flags);
++
++ btuart_change_speed(info, DEFAULT_BAUD_RATE);
++
++ /* Timeout before it is safe to send the first HCI packet */
++ set_current_state(TASK_INTERRUPTIBLE);
++ schedule_timeout(HZ);
++
++
++ /* Initialize and register HCI device */
++
++ hdev = &(info->hdev);
++
++ hdev->type = HCI_PCCARD;
++ hdev->driver_data = info;
++
++ hdev->open = btuart_hci_open;
++ hdev->close = btuart_hci_close;
++ hdev->flush = btuart_hci_flush;
++ hdev->send = btuart_hci_send_frame;
++ hdev->destruct = btuart_hci_destruct;
++ hdev->ioctl = btuart_hci_ioctl;
++
++ if (hci_register_dev(hdev) < 0) {
++ printk(KERN_WARNING "btuart_cs: Can't register HCI device %s.\n", hdev->name);
++ return -ENODEV;
++ }
++
++ return 0;
++}
++
++
++int btuart_close(btuart_info_t *info)
++{
++ unsigned long flags;
++ unsigned int iobase = info->link.io.BasePort1;
++ struct hci_dev *hdev = &(info->hdev);
++
++ if (info->link.state & DEV_CONFIG_PENDING)
++ return -ENODEV;
++
++ btuart_hci_close(hdev);
++
++ spin_lock_irqsave(&(info->lock), flags);
++
++ /* Reset UART */
++ outb(0, iobase + UART_MCR);
++
++ /* Turn off interrupts */
++ outb(0, iobase + UART_IER);
++
++ spin_unlock_irqrestore(&(info->lock), flags);
++
++ if (hci_unregister_dev(hdev) < 0)
++ printk(KERN_WARNING "btuart_cs: Can't unregister HCI device %s.\n", hdev->name);
++
++ return 0;
++}
++
++
++
++/* ======================== Card services ======================== */
++
++
++static void cs_error(client_handle_t handle, int func, int ret)
++{
++ error_info_t err = { func, ret };
++
++ CardServices(ReportError, handle, &err);
++}
++
++
++dev_link_t *btuart_attach(void)
++{
++ btuart_info_t *info;
++ client_reg_t client_reg;
++ dev_link_t *link;
++ int i, ret;
++
++ /* Create new info device */
++ info = kmalloc(sizeof(*info), GFP_KERNEL);
++ if (!info)
++ return NULL;
++ memset(info, 0, sizeof(*info));
++
++ link = &info->link;
++ link->priv = info;
++
++ link->release.function = &btuart_release;
++ link->release.data = (u_long)link;
++ link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
++ link->io.NumPorts1 = 8;
++ link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT;
++ link->irq.IRQInfo1 = IRQ_INFO2_VALID | IRQ_LEVEL_ID;
++
++ if (irq_list[0] == -1)
++ link->irq.IRQInfo2 = irq_mask;
++ else
++ for (i = 0; i < 4; i++)
++ link->irq.IRQInfo2 |= 1 << irq_list[i];
++
++ link->irq.Handler = btuart_interrupt;
++ link->irq.Instance = info;
++
++ link->conf.Attributes = CONF_ENABLE_IRQ;
++ link->conf.Vcc = 50;
++ link->conf.IntType = INT_MEMORY_AND_IO;
++
++ /* Register with Card Services */
++ link->next = dev_list;
++ dev_list = link;
++ client_reg.dev_info = &dev_info;
++ client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE;
++ client_reg.EventMask =
++ CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
++ CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
++ CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
++ client_reg.event_handler = &btuart_event;
++ client_reg.Version = 0x0210;
++ client_reg.event_callback_args.client_data = link;
++
++ ret = CardServices(RegisterClient, &link->handle, &client_reg);
++ if (ret != CS_SUCCESS) {
++ cs_error(link->handle, RegisterClient, ret);
++ btuart_detach(link);
++ return NULL;
++ }
++
++ return link;
++}
++
++
++void btuart_detach(dev_link_t *link)
++{
++ btuart_info_t *info = link->priv;
++ dev_link_t **linkp;
++ int ret;
++
++ /* Locate device structure */
++ for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
++ if (*linkp == link)
++ break;
++
++ if (*linkp == NULL)
++ return;
++
++ del_timer(&link->release);
++ if (link->state & DEV_CONFIG)
++ btuart_release((u_long)link);
++
++ if (link->handle) {
++ ret = CardServices(DeregisterClient, link->handle);
++ if (ret != CS_SUCCESS)
++ cs_error(link->handle, DeregisterClient, ret);
++ }
++
++ /* Unlink device structure, free bits */
++ *linkp = link->next;
++
++ kfree(info);
++}
++
++
++static int get_tuple(int fn, client_handle_t handle, tuple_t *tuple, cisparse_t *parse)
++{
++ int i;
++
++ i = CardServices(fn, handle, tuple);
++ if (i != CS_SUCCESS)
++ return CS_NO_MORE_ITEMS;
++
++ i = CardServices(GetTupleData, handle, tuple);
++ if (i != CS_SUCCESS)
++ return i;
++
++ return CardServices(ParseTuple, handle, tuple, parse);
++}
++
++
++#define first_tuple(a, b, c) get_tuple(GetFirstTuple, a, b, c)
++#define next_tuple(a, b, c) get_tuple(GetNextTuple, a, b, c)
++
++void btuart_config(dev_link_t *link)
++{
++ static ioaddr_t base[5] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, 0x0 };
++ client_handle_t handle = link->handle;
++ btuart_info_t *info = link->priv;
++ tuple_t tuple;
++ u_short buf[256];
++ cisparse_t parse;
++ cistpl_cftable_entry_t *cf = &parse.cftable_entry;
++ config_info_t config;
++ int i, j, try, last_ret, last_fn;
++
++ tuple.TupleData = (cisdata_t *)buf;
++ tuple.TupleOffset = 0;
++ tuple.TupleDataMax = 255;
++ tuple.Attributes = 0;
++
++ /* Get configuration register information */
++ tuple.DesiredTuple = CISTPL_CONFIG;
++ last_ret = first_tuple(handle, &tuple, &parse);
++ if (last_ret != CS_SUCCESS) {
++ last_fn = ParseTuple;
++ goto cs_failed;
++ }
++ link->conf.ConfigBase = parse.config.base;
++ link->conf.Present = parse.config.rmask[0];
++
++ /* Configure card */
++ link->state |= DEV_CONFIG;
++ i = CardServices(GetConfigurationInfo, handle, &config);
++ link->conf.Vcc = config.Vcc;
++
++ /* First pass: look for a config entry that looks normal. */
++ tuple.TupleData = (cisdata_t *) buf;
++ tuple.TupleOffset = 0;
++ tuple.TupleDataMax = 255;
++ tuple.Attributes = 0;
++ tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
++ /* Two tries: without IO aliases, then with aliases */
++ for (try = 0; try < 2; try++) {
++ i = first_tuple(handle, &tuple, &parse);
++ while (i != CS_NO_MORE_ITEMS) {
++ if (i != CS_SUCCESS)
++ goto next_entry;
++ if (cf->vpp1.present & (1 << CISTPL_POWER_VNOM))
++ link->conf.Vpp1 = link->conf.Vpp2 = cf->vpp1.param[CISTPL_POWER_VNOM] / 10000;
++ if ((cf->io.nwin > 0) && (cf->io.win[0].len == 8) && (cf->io.win[0].base != 0)) {
++ link->conf.ConfigIndex = cf->index;
++ link->io.BasePort1 = cf->io.win[0].base;
++ link->io.IOAddrLines = (try == 0) ? 16 : cf->io.flags & CISTPL_IO_LINES_MASK;
++ i = CardServices(RequestIO, link->handle, &link->io);
++ if (i == CS_SUCCESS)
++ goto found_port;
++ }
++next_entry:
++ i = next_tuple(handle, &tuple, &parse);
++ }
++ }
++
++ /* Second pass: try to find an entry that isn't picky about
++ its base address, then try to grab any standard serial port
++ address, and finally try to get any free port. */
++ i = first_tuple(handle, &tuple, &parse);
++ while (i != CS_NO_MORE_ITEMS) {
++ if ((i == CS_SUCCESS) && (cf->io.nwin > 0)
++ && ((cf->io.flags & CISTPL_IO_LINES_MASK) <= 3)) {
++ link->conf.ConfigIndex = cf->index;
++ for (j = 0; j < 5; j++) {
++ link->io.BasePort1 = base[j];
++ link->io.IOAddrLines = base[j] ? 16 : 3;
++ i = CardServices(RequestIO, link->handle, &link->io);
++ if (i == CS_SUCCESS)
++ goto found_port;
++ }
++ }
++ i = next_tuple(handle, &tuple, &parse);
++ }
++
++found_port:
++ if (i != CS_SUCCESS) {
++ printk(KERN_NOTICE "btuart_cs: No usable port range found. Giving up.\n");
++ cs_error(link->handle, RequestIO, i);
++ goto failed;
++ }
++
++ i = CardServices(RequestIRQ, link->handle, &link->irq);
++ if (i != CS_SUCCESS) {
++ cs_error(link->handle, RequestIRQ, i);
++ link->irq.AssignedIRQ = 0;
++ }
++
++ i = CardServices(RequestConfiguration, link->handle, &link->conf);
++ if (i != CS_SUCCESS) {
++ cs_error(link->handle, RequestConfiguration, i);
++ goto failed;
++ }
++
++ MOD_INC_USE_COUNT;
++
++ if (btuart_open(info) != 0)
++ goto failed;
++
++ strcpy(info->node.dev_name, info->hdev.name);
++ link->dev = &info->node;
++ link->state &= ~DEV_CONFIG_PENDING;
++
++ return;
++
++cs_failed:
++ cs_error(link->handle, last_fn, last_ret);
++
++failed:
++ btuart_release((u_long) link);
++}
++
++
++void btuart_release(u_long arg)
++{
++ dev_link_t *link = (dev_link_t *)arg;
++ btuart_info_t *info = link->priv;
++
++ if (link->state & DEV_PRESENT)
++ btuart_close(info);
++
++ MOD_DEC_USE_COUNT;
++
++ link->dev = NULL;
++
++ CardServices(ReleaseConfiguration, link->handle);
++ CardServices(ReleaseIO, link->handle, &link->io);
++ CardServices(ReleaseIRQ, link->handle, &link->irq);
++
++ link->state &= ~DEV_CONFIG;
++}
++
++
++int btuart_event(event_t event, int priority, event_callback_args_t *args)
++{
++ dev_link_t *link = args->client_data;
++ btuart_info_t *info = link->priv;
++
++ switch (event) {
++ case CS_EVENT_CARD_REMOVAL:
++ link->state &= ~DEV_PRESENT;
++ if (link->state & DEV_CONFIG) {
++ btuart_close(info);
++ mod_timer(&link->release, jiffies + HZ / 20);
++ }
++ break;
++ case CS_EVENT_CARD_INSERTION:
++ link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
++ btuart_config(link);
++ break;
++ case CS_EVENT_PM_SUSPEND:
++ link->state |= DEV_SUSPEND;
++ /* Fall through... */
++ case CS_EVENT_RESET_PHYSICAL:
++ if (link->state & DEV_CONFIG)
++ CardServices(ReleaseConfiguration, link->handle);
++ break;
++ case CS_EVENT_PM_RESUME:
++ link->state &= ~DEV_SUSPEND;
++ /* Fall through... */
++ case CS_EVENT_CARD_RESET:
++ if (DEV_OK(link))
++ CardServices(RequestConfiguration, link->handle, &link->conf);
++ break;
++ }
++
++ return 0;
++}
++
++
++
++/* ======================== Module initialization ======================== */
++
++
++int __init init_btuart_cs(void)
++{
++ servinfo_t serv;
++ int err;
++
++ CardServices(GetCardServicesInfo, &serv);
++ if (serv.Revision != CS_RELEASE_CODE) {
++ printk(KERN_NOTICE "btuart_cs: Card Services release does not match!\n");
++ return -1;
++ }
++
++ err = register_pccard_driver(&dev_info, &btuart_attach, &btuart_detach);
++
++ return err;
++}
++
++
++void __exit exit_btuart_cs(void)
++{
++ unregister_pccard_driver(&dev_info);
++
++ while (dev_list != NULL)
++ btuart_detach(dev_list);
++}
++
++
++module_init(init_btuart_cs);
++module_exit(exit_btuart_cs);
++
++EXPORT_NO_SYMBOLS;
+diff -urN linux-2.4.18/drivers/bluetooth/Config.in linux-2.4.18-mh15/drivers/bluetooth/Config.in
+--- linux-2.4.18/drivers/bluetooth/Config.in 2001-09-07 18:28:38.000000000 +0200
++++ linux-2.4.18-mh15/drivers/bluetooth/Config.in 2004-08-01 16:26:23.000000000 +0200
+@@ -1,8 +1,33 @@
++#
++# Bluetooth HCI device drivers configuration
++#
++
+ mainmenu_option next_comment
+ comment 'Bluetooth device drivers'
+
+ dep_tristate 'HCI USB driver' CONFIG_BLUEZ_HCIUSB $CONFIG_BLUEZ $CONFIG_USB
++if [ "$CONFIG_BLUEZ_HCIUSB" != "n" ]; then
++ bool ' SCO (voice) support' CONFIG_BLUEZ_HCIUSB_SCO
++fi
++
+ dep_tristate 'HCI UART driver' CONFIG_BLUEZ_HCIUART $CONFIG_BLUEZ
+-dep_tristate 'HCI VHCI virtual HCI device driver' CONFIG_BLUEZ_HCIVHCI $CONFIG_BLUEZ
++if [ "$CONFIG_BLUEZ_HCIUART" != "n" ]; then
++ bool ' UART (H4) protocol support' CONFIG_BLUEZ_HCIUART_H4
++ bool ' BCSP protocol support' CONFIG_BLUEZ_HCIUART_BCSP
++ dep_bool ' Transmit CRC with every BCSP packet' CONFIG_BLUEZ_HCIUART_BCSP_TXCRC $CONFIG_BLUEZ_HCIUART_BCSP
++fi
++
++dep_tristate 'HCI BlueFRITZ! USB driver' CONFIG_BLUEZ_HCIBFUSB $CONFIG_BLUEZ $CONFIG_USB
++
++dep_tristate 'HCI DTL1 (PC Card) driver' CONFIG_BLUEZ_HCIDTL1 $CONFIG_PCMCIA $CONFIG_BLUEZ
++
++dep_tristate 'HCI BT3C (PC Card) driver' CONFIG_BLUEZ_HCIBT3C $CONFIG_PCMCIA $CONFIG_BLUEZ
++
++dep_tristate 'HCI BlueCard (PC Card) driver' CONFIG_BLUEZ_HCIBLUECARD $CONFIG_PCMCIA $CONFIG_BLUEZ
++
++dep_tristate 'HCI UART (PC Card) driver' CONFIG_BLUEZ_HCIBTUART $CONFIG_PCMCIA $CONFIG_BLUEZ
++
++dep_tristate 'HCI VHCI (Virtual HCI device) driver' CONFIG_BLUEZ_HCIVHCI $CONFIG_BLUEZ
+
+ endmenu
++
+diff -urN linux-2.4.18/drivers/bluetooth/dtl1_cs.c linux-2.4.18-mh15/drivers/bluetooth/dtl1_cs.c
+--- linux-2.4.18/drivers/bluetooth/dtl1_cs.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/drivers/bluetooth/dtl1_cs.c 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,861 @@
++/*
++ *
++ * A driver for Nokia Connectivity Card DTL-1 devices
++ *
++ * Copyright (C) 2001-2002 Marcel Holtmann <marcel@holtmann.org>
++ *
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation;
++ *
++ * Software distributed under the License is distributed on an "AS
++ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
++ * implied. See the License for the specific language governing
++ * rights and limitations under the License.
++ *
++ * The initial developer of the original code is David A. Hinds
++ * <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
++ * are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
++ *
++ */
++
++#include <linux/config.h>
++#include <linux/module.h>
++
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/slab.h>
++#include <linux/types.h>
++#include <linux/sched.h>
++#include <linux/timer.h>
++#include <linux/errno.h>
++#include <linux/ptrace.h>
++#include <linux/ioport.h>
++#include <linux/spinlock.h>
++
++#include <linux/skbuff.h>
++#include <linux/string.h>
++#include <linux/serial.h>
++#include <linux/serial_reg.h>
++#include <asm/system.h>
++#include <asm/bitops.h>
++#include <asm/io.h>
++
++#include <pcmcia/version.h>
++#include <pcmcia/cs_types.h>
++#include <pcmcia/cs.h>
++#include <pcmcia/cistpl.h>
++#include <pcmcia/ciscode.h>
++#include <pcmcia/ds.h>
++#include <pcmcia/cisreg.h>
++
++#include <net/bluetooth/bluetooth.h>
++#include <net/bluetooth/hci_core.h>
++
++
++
++/* ======================== Module parameters ======================== */
++
++
++/* Bit map of interrupts to choose from */
++static u_int irq_mask = 0xffff;
++static int irq_list[4] = { -1 };
++
++MODULE_PARM(irq_mask, "i");
++MODULE_PARM(irq_list, "1-4i");
++
++MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
++MODULE_DESCRIPTION("BlueZ driver for Nokia Connectivity Card DTL-1");
++MODULE_LICENSE("GPL");
++
++
++
++/* ======================== Local structures ======================== */
++
++
++typedef struct dtl1_info_t {
++ dev_link_t link;
++ dev_node_t node;
++
++ struct hci_dev hdev;
++
++ spinlock_t lock; /* For serializing operations */
++
++ unsigned long flowmask; /* HCI flow mask */
++ int ri_latch;
++
++ struct sk_buff_head txq;
++ unsigned long tx_state;
++
++ unsigned long rx_state;
++ unsigned long rx_count;
++ struct sk_buff *rx_skb;
++} dtl1_info_t;
++
++
++void dtl1_config(dev_link_t *link);
++void dtl1_release(u_long arg);
++int dtl1_event(event_t event, int priority, event_callback_args_t *args);
++
++static dev_info_t dev_info = "dtl1_cs";
++
++dev_link_t *dtl1_attach(void);
++void dtl1_detach(dev_link_t *);
++
++static dev_link_t *dev_list = NULL;
++
++
++/* Transmit states */
++#define XMIT_SENDING 1
++#define XMIT_WAKEUP 2
++#define XMIT_WAITING 8
++
++/* Receiver States */
++#define RECV_WAIT_NSH 0
++#define RECV_WAIT_DATA 1
++
++
++typedef struct {
++ u8 type;
++ u8 zero;
++ u16 len;
++} __attribute__ ((packed)) nsh_t; /* Nokia Specific Header */
++
++#define NSHL 4 /* Nokia Specific Header Length */
++
++
++
++/* ======================== Interrupt handling ======================== */
++
++
++static int dtl1_write(unsigned int iobase, int fifo_size, __u8 *buf, int len)
++{
++ int actual = 0;
++
++ /* Tx FIFO should be empty */
++ if (!(inb(iobase + UART_LSR) & UART_LSR_THRE))
++ return 0;
++
++ /* Fill FIFO with current frame */
++ while ((fifo_size-- > 0) && (actual < len)) {
++ /* Transmit next byte */
++ outb(buf[actual], iobase + UART_TX);
++ actual++;
++ }
++
++ return actual;
++}
++
++
++static void dtl1_write_wakeup(dtl1_info_t *info)
++{
++ if (!info) {
++ printk(KERN_WARNING "dtl1_cs: Call of write_wakeup for unknown device.\n");
++ return;
++ }
++
++ if (test_bit(XMIT_WAITING, &(info->tx_state))) {
++ set_bit(XMIT_WAKEUP, &(info->tx_state));
++ return;
++ }
++
++ if (test_and_set_bit(XMIT_SENDING, &(info->tx_state))) {
++ set_bit(XMIT_WAKEUP, &(info->tx_state));
++ return;
++ }
++
++ do {
++ register unsigned int iobase = info->link.io.BasePort1;
++ register struct sk_buff *skb;
++ register int len;
++
++ clear_bit(XMIT_WAKEUP, &(info->tx_state));
++
++ if (!(info->link.state & DEV_PRESENT))
++ return;
++
++ if (!(skb = skb_dequeue(&(info->txq))))
++ break;
++
++ /* Send frame */
++ len = dtl1_write(iobase, 32, skb->data, skb->len);
++
++ if (len == skb->len) {
++ set_bit(XMIT_WAITING, &(info->tx_state));
++ kfree_skb(skb);
++ } else {
++ skb_pull(skb, len);
++ skb_queue_head(&(info->txq), skb);
++ }
++
++ info->hdev.stat.byte_tx += len;
++
++ } while (test_bit(XMIT_WAKEUP, &(info->tx_state)));
++
++ clear_bit(XMIT_SENDING, &(info->tx_state));
++}
++
++
++static void dtl1_control(dtl1_info_t *info, struct sk_buff *skb)
++{
++ u8 flowmask = *(u8 *)skb->data;
++ int i;
++
++ printk(KERN_INFO "dtl1_cs: Nokia control data = ");
++ for (i = 0; i < skb->len; i++) {
++ printk("%02x ", skb->data[i]);
++ }
++ printk("\n");
++
++ /* transition to active state */
++ if (((info->flowmask & 0x07) == 0) && ((flowmask & 0x07) != 0)) {
++ clear_bit(XMIT_WAITING, &(info->tx_state));
++ dtl1_write_wakeup(info);
++ }
++
++ info->flowmask = flowmask;
++
++ kfree_skb(skb);
++}
++
++
++static void dtl1_receive(dtl1_info_t *info)
++{
++ unsigned int iobase;
++ nsh_t *nsh;
++ int boguscount = 0;
++
++ if (!info) {
++ printk(KERN_WARNING "dtl1_cs: Call of receive for unknown device.\n");
++ return;
++ }
++
++ iobase = info->link.io.BasePort1;
++
++ do {
++ info->hdev.stat.byte_rx++;
++
++ /* Allocate packet */
++ if (info->rx_skb == NULL)
++ if (!(info->rx_skb = bluez_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC))) {
++ printk(KERN_WARNING "dtl1_cs: Can't allocate mem for new packet.\n");
++ info->rx_state = RECV_WAIT_NSH;
++ info->rx_count = NSHL;
++ return;
++ }
++
++ *skb_put(info->rx_skb, 1) = inb(iobase + UART_RX);
++ nsh = (nsh_t *)info->rx_skb->data;
++
++ info->rx_count--;
++
++ if (info->rx_count == 0) {
++
++ switch (info->rx_state) {
++ case RECV_WAIT_NSH:
++ info->rx_state = RECV_WAIT_DATA;
++ info->rx_count = nsh->len + (nsh->len & 0x0001);
++ break;
++ case RECV_WAIT_DATA:
++ info->rx_skb->pkt_type = nsh->type;
++
++ /* remove PAD byte if it exists */
++ if (nsh->len & 0x0001) {
++ info->rx_skb->tail--;
++ info->rx_skb->len--;
++ }
++
++ /* remove NSH */
++ skb_pull(info->rx_skb, NSHL);
++
++ switch (info->rx_skb->pkt_type) {
++ case 0x80:
++ /* control data for the Nokia Card */
++ dtl1_control(info, info->rx_skb);
++ break;
++ case 0x82:
++ case 0x83:
++ case 0x84:
++ /* send frame to the HCI layer */
++ info->rx_skb->dev = (void *)&(info->hdev);
++ info->rx_skb->pkt_type &= 0x0f;
++ hci_recv_frame(info->rx_skb);
++ break;
++ default:
++ /* unknown packet */
++ printk(KERN_WARNING "dtl1_cs: Unknown HCI packet with type 0x%02x received.\n", info->rx_skb->pkt_type);
++ kfree_skb(info->rx_skb);
++ break;
++ }
++
++ info->rx_state = RECV_WAIT_NSH;
++ info->rx_count = NSHL;
++ info->rx_skb = NULL;
++ break;
++ }
++
++ }
++
++ /* Make sure we don't stay here to long */
++ if (boguscount++ > 32)
++ break;
++
++ } while (inb(iobase + UART_LSR) & UART_LSR_DR);
++}
++
++
++void dtl1_interrupt(int irq, void *dev_inst, struct pt_regs *regs)
++{
++ dtl1_info_t *info = dev_inst;
++ unsigned int iobase;
++ unsigned char msr;
++ int boguscount = 0;
++ int iir, lsr;
++
++ if (!info) {
++ printk(KERN_WARNING "dtl1_cs: Call of irq %d for unknown device.\n", irq);
++ return;
++ }
++
++ iobase = info->link.io.BasePort1;
++
++ spin_lock(&(info->lock));
++
++ iir = inb(iobase + UART_IIR) & UART_IIR_ID;
++ while (iir) {
++
++ /* Clear interrupt */
++ lsr = inb(iobase + UART_LSR);
++
++ switch (iir) {
++ case UART_IIR_RLSI:
++ printk(KERN_NOTICE "dtl1_cs: RLSI\n");
++ break;
++ case UART_IIR_RDI:
++ /* Receive interrupt */
++ dtl1_receive(info);
++ break;
++ case UART_IIR_THRI:
++ if (lsr & UART_LSR_THRE) {
++ /* Transmitter ready for data */
++ dtl1_write_wakeup(info);
++ }
++ break;
++ default:
++ printk(KERN_NOTICE "dtl1_cs: Unhandled IIR=%#x\n", iir);
++ break;
++ }
++
++ /* Make sure we don't stay here to long */
++ if (boguscount++ > 100)
++ break;
++
++ iir = inb(iobase + UART_IIR) & UART_IIR_ID;
++
++ }
++
++ msr = inb(iobase + UART_MSR);
++
++ if (info->ri_latch ^ (msr & UART_MSR_RI)) {
++ info->ri_latch = msr & UART_MSR_RI;
++ clear_bit(XMIT_WAITING, &(info->tx_state));
++ dtl1_write_wakeup(info);
++ }
++
++ spin_unlock(&(info->lock));
++}
++
++
++
++/* ======================== HCI interface ======================== */
++
++
++static int dtl1_hci_open(struct hci_dev *hdev)
++{
++ set_bit(HCI_RUNNING, &(hdev->flags));
++
++ return 0;
++}
++
++
++static int dtl1_hci_flush(struct hci_dev *hdev)
++{
++ dtl1_info_t *info = (dtl1_info_t *)(hdev->driver_data);
++
++ /* Drop TX queue */
++ skb_queue_purge(&(info->txq));
++
++ return 0;
++}
++
++
++static int dtl1_hci_close(struct hci_dev *hdev)
++{
++ if (!test_and_clear_bit(HCI_RUNNING, &(hdev->flags)))
++ return 0;
++
++ dtl1_hci_flush(hdev);
++
++ return 0;
++}
++
++
++static int dtl1_hci_send_frame(struct sk_buff *skb)
++{
++ dtl1_info_t *info;
++ struct hci_dev *hdev = (struct hci_dev *)(skb->dev);
++ struct sk_buff *s;
++ nsh_t nsh;
++
++ if (!hdev) {
++ printk(KERN_WARNING "dtl1_cs: Frame for unknown HCI device (hdev=NULL).");
++ return -ENODEV;
++ }
++
++ info = (dtl1_info_t *)(hdev->driver_data);
++
++ switch (skb->pkt_type) {
++ case HCI_COMMAND_PKT:
++ hdev->stat.cmd_tx++;
++ nsh.type = 0x81;
++ break;
++ case HCI_ACLDATA_PKT:
++ hdev->stat.acl_tx++;
++ nsh.type = 0x82;
++ break;
++ case HCI_SCODATA_PKT:
++ hdev->stat.sco_tx++;
++ nsh.type = 0x83;
++ break;
++ };
++
++ nsh.zero = 0;
++ nsh.len = skb->len;
++
++ s = bluez_skb_alloc(NSHL + skb->len + 1, GFP_ATOMIC);
++ skb_reserve(s, NSHL);
++ memcpy(skb_put(s, skb->len), skb->data, skb->len);
++ if (skb->len & 0x0001)
++ *skb_put(s, 1) = 0; /* PAD */
++
++ /* Prepend skb with Nokia frame header and queue */
++ memcpy(skb_push(s, NSHL), &nsh, NSHL);
++ skb_queue_tail(&(info->txq), s);
++
++ dtl1_write_wakeup(info);
++
++ kfree_skb(skb);
++
++ return 0;
++}
++
++
++static void dtl1_hci_destruct(struct hci_dev *hdev)
++{
++}
++
++
++static int dtl1_hci_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg)
++{
++ return -ENOIOCTLCMD;
++}
++
++
++
++/* ======================== Card services HCI interaction ======================== */
++
++
++int dtl1_open(dtl1_info_t *info)
++{
++ unsigned long flags;
++ unsigned int iobase = info->link.io.BasePort1;
++ struct hci_dev *hdev;
++
++ spin_lock_init(&(info->lock));
++
++ skb_queue_head_init(&(info->txq));
++
++ info->rx_state = RECV_WAIT_NSH;
++ info->rx_count = NSHL;
++ info->rx_skb = NULL;
++
++ set_bit(XMIT_WAITING, &(info->tx_state));
++
++ spin_lock_irqsave(&(info->lock), flags);
++
++ /* Reset UART */
++ outb(0, iobase + UART_MCR);
++
++ /* Turn off interrupts */
++ outb(0, iobase + UART_IER);
++
++ /* Initialize UART */
++ outb(UART_LCR_WLEN8, iobase + UART_LCR); /* Reset DLAB */
++ outb((UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2), iobase + UART_MCR);
++
++ info->ri_latch = inb(info->link.io.BasePort1 + UART_MSR) & UART_MSR_RI;
++
++ /* Turn on interrupts */
++ outb(UART_IER_RLSI | UART_IER_RDI | UART_IER_THRI, iobase + UART_IER);
++
++ spin_unlock_irqrestore(&(info->lock), flags);
++
++ /* Timeout before it is safe to send the first HCI packet */
++ set_current_state(TASK_INTERRUPTIBLE);
++ schedule_timeout(HZ * 2);
++
++
++ /* Initialize and register HCI device */
++
++ hdev = &(info->hdev);
++
++ hdev->type = HCI_PCCARD;
++ hdev->driver_data = info;
++
++ hdev->open = dtl1_hci_open;
++ hdev->close = dtl1_hci_close;
++ hdev->flush = dtl1_hci_flush;
++ hdev->send = dtl1_hci_send_frame;
++ hdev->destruct = dtl1_hci_destruct;
++ hdev->ioctl = dtl1_hci_ioctl;
++
++ if (hci_register_dev(hdev) < 0) {
++ printk(KERN_WARNING "dtl1_cs: Can't register HCI device %s.\n", hdev->name);
++ return -ENODEV;
++ }
++
++ return 0;
++}
++
++
++int dtl1_close(dtl1_info_t *info)
++{
++ unsigned long flags;
++ unsigned int iobase = info->link.io.BasePort1;
++ struct hci_dev *hdev = &(info->hdev);
++
++ if (info->link.state & DEV_CONFIG_PENDING)
++ return -ENODEV;
++
++ dtl1_hci_close(hdev);
++
++ spin_lock_irqsave(&(info->lock), flags);
++
++ /* Reset UART */
++ outb(0, iobase + UART_MCR);
++
++ /* Turn off interrupts */
++ outb(0, iobase + UART_IER);
++
++ spin_unlock_irqrestore(&(info->lock), flags);
++
++ if (hci_unregister_dev(hdev) < 0)
++ printk(KERN_WARNING "dtl1_cs: Can't unregister HCI device %s.\n", hdev->name);
++
++ return 0;
++}
++
++
++
++/* ======================== Card services ======================== */
++
++
++static void cs_error(client_handle_t handle, int func, int ret)
++{
++ error_info_t err = { func, ret };
++
++ CardServices(ReportError, handle, &err);
++}
++
++
++dev_link_t *dtl1_attach(void)
++{
++ dtl1_info_t *info;
++ client_reg_t client_reg;
++ dev_link_t *link;
++ int i, ret;
++
++ /* Create new info device */
++ info = kmalloc(sizeof(*info), GFP_KERNEL);
++ if (!info)
++ return NULL;
++ memset(info, 0, sizeof(*info));
++
++ link = &info->link;
++ link->priv = info;
++
++ link->release.function = &dtl1_release;
++ link->release.data = (u_long)link;
++ link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
++ link->io.NumPorts1 = 8;
++ link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT;
++ link->irq.IRQInfo1 = IRQ_INFO2_VALID | IRQ_LEVEL_ID;
++
++ if (irq_list[0] == -1)
++ link->irq.IRQInfo2 = irq_mask;
++ else
++ for (i = 0; i < 4; i++)
++ link->irq.IRQInfo2 |= 1 << irq_list[i];
++
++ link->irq.Handler = dtl1_interrupt;
++ link->irq.Instance = info;
++
++ link->conf.Attributes = CONF_ENABLE_IRQ;
++ link->conf.Vcc = 50;
++ link->conf.IntType = INT_MEMORY_AND_IO;
++
++ /* Register with Card Services */
++ link->next = dev_list;
++ dev_list = link;
++ client_reg.dev_info = &dev_info;
++ client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE;
++ client_reg.EventMask =
++ CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
++ CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
++ CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
++ client_reg.event_handler = &dtl1_event;
++ client_reg.Version = 0x0210;
++ client_reg.event_callback_args.client_data = link;
++
++ ret = CardServices(RegisterClient, &link->handle, &client_reg);
++ if (ret != CS_SUCCESS) {
++ cs_error(link->handle, RegisterClient, ret);
++ dtl1_detach(link);
++ return NULL;
++ }
++
++ return link;
++}
++
++
++void dtl1_detach(dev_link_t *link)
++{
++ dtl1_info_t *info = link->priv;
++ dev_link_t **linkp;
++ int ret;
++
++ /* Locate device structure */
++ for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
++ if (*linkp == link)
++ break;
++
++ if (*linkp == NULL)
++ return;
++
++ del_timer(&link->release);
++ if (link->state & DEV_CONFIG)
++ dtl1_release((u_long)link);
++
++ if (link->handle) {
++ ret = CardServices(DeregisterClient, link->handle);
++ if (ret != CS_SUCCESS)
++ cs_error(link->handle, DeregisterClient, ret);
++ }
++
++ /* Unlink device structure, free bits */
++ *linkp = link->next;
++
++ kfree(info);
++}
++
++
++static int get_tuple(int fn, client_handle_t handle, tuple_t *tuple, cisparse_t *parse)
++{
++ int i;
++
++ i = CardServices(fn, handle, tuple);
++ if (i != CS_SUCCESS)
++ return CS_NO_MORE_ITEMS;
++
++ i = CardServices(GetTupleData, handle, tuple);
++ if (i != CS_SUCCESS)
++ return i;
++
++ return CardServices(ParseTuple, handle, tuple, parse);
++}
++
++
++#define first_tuple(a, b, c) get_tuple(GetFirstTuple, a, b, c)
++#define next_tuple(a, b, c) get_tuple(GetNextTuple, a, b, c)
++
++void dtl1_config(dev_link_t *link)
++{
++ client_handle_t handle = link->handle;
++ dtl1_info_t *info = link->priv;
++ tuple_t tuple;
++ u_short buf[256];
++ cisparse_t parse;
++ cistpl_cftable_entry_t *cf = &parse.cftable_entry;
++ config_info_t config;
++ int i, last_ret, last_fn;
++
++ tuple.TupleData = (cisdata_t *)buf;
++ tuple.TupleOffset = 0;
++ tuple.TupleDataMax = 255;
++ tuple.Attributes = 0;
++
++ /* Get configuration register information */
++ tuple.DesiredTuple = CISTPL_CONFIG;
++ last_ret = first_tuple(handle, &tuple, &parse);
++ if (last_ret != CS_SUCCESS) {
++ last_fn = ParseTuple;
++ goto cs_failed;
++ }
++ link->conf.ConfigBase = parse.config.base;
++ link->conf.Present = parse.config.rmask[0];
++
++ /* Configure card */
++ link->state |= DEV_CONFIG;
++ i = CardServices(GetConfigurationInfo, handle, &config);
++ link->conf.Vcc = config.Vcc;
++
++ tuple.TupleData = (cisdata_t *)buf;
++ tuple.TupleOffset = 0;
++ tuple.TupleDataMax = 255;
++ tuple.Attributes = 0;
++ tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
++
++ /* Look for a generic full-sized window */
++ link->io.NumPorts1 = 8;
++ i = first_tuple(handle, &tuple, &parse);
++ while (i != CS_NO_MORE_ITEMS) {
++ if ((i == CS_SUCCESS) && (cf->io.nwin == 1) && (cf->io.win[0].len > 8)) {
++ link->conf.ConfigIndex = cf->index;
++ link->io.BasePort1 = cf->io.win[0].base;
++ link->io.NumPorts1 = cf->io.win[0].len; /*yo */
++ link->io.IOAddrLines = cf->io.flags & CISTPL_IO_LINES_MASK;
++ i = CardServices(RequestIO, link->handle, &link->io);
++ if (i == CS_SUCCESS)
++ break;
++ }
++ i = next_tuple(handle, &tuple, &parse);
++ }
++
++ if (i != CS_SUCCESS) {
++ cs_error(link->handle, RequestIO, i);
++ goto failed;
++ }
++
++ i = CardServices(RequestIRQ, link->handle, &link->irq);
++ if (i != CS_SUCCESS) {
++ cs_error(link->handle, RequestIRQ, i);
++ link->irq.AssignedIRQ = 0;
++ }
++
++ i = CardServices(RequestConfiguration, link->handle, &link->conf);
++ if (i != CS_SUCCESS) {
++ cs_error(link->handle, RequestConfiguration, i);
++ goto failed;
++ }
++
++ MOD_INC_USE_COUNT;
++
++ if (dtl1_open(info) != 0)
++ goto failed;
++
++ strcpy(info->node.dev_name, info->hdev.name);
++ link->dev = &info->node;
++ link->state &= ~DEV_CONFIG_PENDING;
++
++ return;
++
++cs_failed:
++ cs_error(link->handle, last_fn, last_ret);
++
++failed:
++ dtl1_release((u_long)link);
++}
++
++
++void dtl1_release(u_long arg)
++{
++ dev_link_t *link = (dev_link_t *)arg;
++ dtl1_info_t *info = link->priv;
++
++ if (link->state & DEV_PRESENT)
++ dtl1_close(info);
++
++ MOD_DEC_USE_COUNT;
++
++ link->dev = NULL;
++
++ CardServices(ReleaseConfiguration, link->handle);
++ CardServices(ReleaseIO, link->handle, &link->io);
++ CardServices(ReleaseIRQ, link->handle, &link->irq);
++
++ link->state &= ~DEV_CONFIG;
++}
++
++
++int dtl1_event(event_t event, int priority, event_callback_args_t *args)
++{
++ dev_link_t *link = args->client_data;
++ dtl1_info_t *info = link->priv;
++
++ switch (event) {
++ case CS_EVENT_CARD_REMOVAL:
++ link->state &= ~DEV_PRESENT;
++ if (link->state & DEV_CONFIG) {
++ dtl1_close(info);
++ mod_timer(&link->release, jiffies + HZ / 20);
++ }
++ break;
++ case CS_EVENT_CARD_INSERTION:
++ link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
++ dtl1_config(link);
++ break;
++ case CS_EVENT_PM_SUSPEND:
++ link->state |= DEV_SUSPEND;
++ /* Fall through... */
++ case CS_EVENT_RESET_PHYSICAL:
++ if (link->state & DEV_CONFIG)
++ CardServices(ReleaseConfiguration, link->handle);
++ break;
++ case CS_EVENT_PM_RESUME:
++ link->state &= ~DEV_SUSPEND;
++ /* Fall through... */
++ case CS_EVENT_CARD_RESET:
++ if (DEV_OK(link))
++ CardServices(RequestConfiguration, link->handle, &link->conf);
++ break;
++ }
++
++ return 0;
++}
++
++
++
++/* ======================== Module initialization ======================== */
++
++
++int __init init_dtl1_cs(void)
++{
++ servinfo_t serv;
++ int err;
++
++ CardServices(GetCardServicesInfo, &serv);
++ if (serv.Revision != CS_RELEASE_CODE) {
++ printk(KERN_NOTICE "dtl1_cs: Card Services release does not match!\n");
++ return -1;
++ }
++
++ err = register_pccard_driver(&dev_info, &dtl1_attach, &dtl1_detach);
++
++ return err;
++}
++
++
++void __exit exit_dtl1_cs(void)
++{
++ unregister_pccard_driver(&dev_info);
++
++ while (dev_list != NULL)
++ dtl1_detach(dev_list);
++}
++
++
++module_init(init_dtl1_cs);
++module_exit(exit_dtl1_cs);
++
++EXPORT_NO_SYMBOLS;
+diff -urN linux-2.4.18/drivers/bluetooth/hci_bcsp.c linux-2.4.18-mh15/drivers/bluetooth/hci_bcsp.c
+--- linux-2.4.18/drivers/bluetooth/hci_bcsp.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/drivers/bluetooth/hci_bcsp.c 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,710 @@
++/*
++ BlueCore Serial Protocol (BCSP) for Linux Bluetooth stack (BlueZ).
++ Copyright 2002 by Fabrizio Gennari <fabrizio.gennari@philips.com>
++
++ Based on
++ hci_h4.c by Maxim Krasnyansky <maxk@qualcomm.com>
++ ABCSP by Carl Orsborn <cjo@csr.com>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License version 2 as
++ published by the Free Software Foundation;
++
++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++
++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
++ SOFTWARE IS DISCLAIMED.
++*/
++
++/*
++ * $Id$
++ */
++
++#define VERSION "0.1"
++
++#include <linux/config.h>
++#include <linux/module.h>
++
++#include <linux/version.h>
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/sched.h>
++#include <linux/types.h>
++#include <linux/fcntl.h>
++#include <linux/interrupt.h>
++#include <linux/ptrace.h>
++#include <linux/poll.h>
++
++#include <linux/slab.h>
++#include <linux/tty.h>
++#include <linux/errno.h>
++#include <linux/string.h>
++#include <linux/signal.h>
++#include <linux/ioctl.h>
++#include <linux/skbuff.h>
++
++#include <net/bluetooth/bluetooth.h>
++#include <net/bluetooth/hci_core.h>
++#include "hci_uart.h"
++#include "hci_bcsp.h"
++
++#ifndef HCI_UART_DEBUG
++#undef BT_DBG
++#define BT_DBG( A... )
++#undef BT_DMP
++#define BT_DMP( A... )
++#endif
++
++/* ---- BCSP CRC calculation ---- */
++
++/* Table for calculating CRC for polynomial 0x1021, LSB processed first,
++initial value 0xffff, bits shifted in reverse order. */
++
++static const u16 crc_table[] = {
++ 0x0000, 0x1081, 0x2102, 0x3183,
++ 0x4204, 0x5285, 0x6306, 0x7387,
++ 0x8408, 0x9489, 0xa50a, 0xb58b,
++ 0xc60c, 0xd68d, 0xe70e, 0xf78f
++};
++
++/* Initialise the crc calculator */
++#define BCSP_CRC_INIT(x) x = 0xffff
++
++/*
++ Update crc with next data byte
++
++ Implementation note
++ The data byte is treated as two nibbles. The crc is generated
++ in reverse, i.e., bits are fed into the register from the top.
++*/
++static void bcsp_crc_update(u16 *crc, u8 d)
++{
++ u16 reg = *crc;
++
++ reg = (reg >> 4) ^ crc_table[(reg ^ d) & 0x000f];
++ reg = (reg >> 4) ^ crc_table[(reg ^ (d >> 4)) & 0x000f];
++
++ *crc = reg;
++}
++
++/*
++ Get reverse of generated crc
++
++ Implementation note
++ The crc generator (bcsp_crc_init() and bcsp_crc_update())
++ creates a reversed crc, so it needs to be swapped back before
++ being passed on.
++*/
++static u16 bcsp_crc_reverse(u16 crc)
++{
++ u16 b, rev;
++
++ for (b = 0, rev = 0; b < 16; b++) {
++ rev = rev << 1;
++ rev |= (crc & 1);
++ crc = crc >> 1;
++ }
++ return (rev);
++}
++
++/* ---- BCSP core ---- */
++
++static void bcsp_slip_msgdelim(struct sk_buff *skb)
++{
++ const char pkt_delim = 0xc0;
++ memcpy(skb_put(skb, 1), &pkt_delim, 1);
++}
++
++static void bcsp_slip_one_byte(struct sk_buff *skb, u8 c)
++{
++ const char esc_c0[2] = { 0xdb, 0xdc };
++ const char esc_db[2] = { 0xdb, 0xdd };
++
++ switch (c) {
++ case 0xc0:
++ memcpy(skb_put(skb, 2), &esc_c0, 2);
++ break;
++ case 0xdb:
++ memcpy(skb_put(skb, 2), &esc_db, 2);
++ break;
++ default:
++ memcpy(skb_put(skb, 1), &c, 1);
++ }
++}
++
++static int bcsp_enqueue(struct hci_uart *hu, struct sk_buff *skb)
++{
++ struct bcsp_struct *bcsp = hu->priv;
++
++ if (skb->len > 0xFFF) {
++ BT_ERR("Packet too long");
++ kfree_skb(skb);
++ return 0;
++ }
++
++ switch (skb->pkt_type) {
++ case HCI_ACLDATA_PKT:
++ case HCI_COMMAND_PKT:
++ skb_queue_tail(&bcsp->rel, skb);
++ break;
++
++ case HCI_SCODATA_PKT:
++ skb_queue_tail(&bcsp->unrel, skb);
++ break;
++
++ default:
++ BT_ERR("Unknown packet type");
++ kfree_skb(skb);
++ break;
++ }
++ return 0;
++}
++
++static struct sk_buff *bcsp_prepare_pkt(struct bcsp_struct *bcsp, u8 *data,
++ int len, int pkt_type)
++{
++ struct sk_buff *nskb;
++ u8 hdr[4], chan;
++ int rel, i;
++
++#ifdef CONFIG_BLUEZ_HCIUART_BCSP_TXCRC
++ u16 BCSP_CRC_INIT(bcsp_txmsg_crc);
++#endif
++
++ switch (pkt_type) {
++ case HCI_ACLDATA_PKT:
++ chan = 6; /* BCSP ACL channel */
++ rel = 1; /* reliable channel */
++ break;
++ case HCI_COMMAND_PKT:
++ chan = 5; /* BCSP cmd/evt channel */
++ rel = 1; /* reliable channel */
++ break;
++ case HCI_SCODATA_PKT:
++ chan = 7; /* BCSP SCO channel */
++ rel = 0; /* unreliable channel */
++ break;
++ case BCSP_LE_PKT:
++ chan = 1; /* BCSP LE channel */
++ rel = 0; /* unreliable channel */
++ break;
++ case BCSP_ACK_PKT:
++ chan = 0; /* BCSP internal channel */
++ rel = 0; /* unreliable channel */
++ break;
++ default:
++ BT_ERR("Unknown packet type");
++ return NULL;
++ }
++
++ /* Max len of packet: (original len +4(bcsp hdr) +2(crc))*2
++ (because bytes 0xc0 and 0xdb are escaped, worst case is
++ when the packet is all made of 0xc0 and 0xdb :) )
++ + 2 (0xc0 delimiters at start and end). */
++
++ nskb = alloc_skb((len + 6) * 2 + 2, GFP_ATOMIC);
++ if (!nskb)
++ return NULL;
++
++ nskb->pkt_type = pkt_type;
++
++ bcsp_slip_msgdelim(nskb);
++
++ hdr[0] = bcsp->rxseq_txack << 3;
++ bcsp->txack_req = 0;
++ BT_DBG("We request packet no %u to card", bcsp->rxseq_txack);
++
++ if (rel) {
++ hdr[0] |= 0x80 + bcsp->msgq_txseq;
++ BT_DBG("Sending packet with seqno %u", bcsp->msgq_txseq);
++ bcsp->msgq_txseq = ++(bcsp->msgq_txseq) & 0x07;
++ }
++#ifdef CONFIG_BLUEZ_HCIUART_BCSP_TXCRC
++ hdr[0] |= 0x40;
++#endif
++
++ hdr[1] = (len << 4) & 0xFF;
++ hdr[1] |= chan;
++ hdr[2] = len >> 4;
++ hdr[3] = ~(hdr[0] + hdr[1] + hdr[2]);
++
++ /* Put BCSP header */
++ for (i = 0; i < 4; i++) {
++ bcsp_slip_one_byte(nskb, hdr[i]);
++#ifdef CONFIG_BLUEZ_HCIUART_BCSP_TXCRC
++ bcsp_crc_update(&bcsp_txmsg_crc, hdr[i]);
++#endif
++ }
++
++ /* Put payload */
++ for (i = 0; i < len; i++) {
++ bcsp_slip_one_byte(nskb, data[i]);
++#ifdef CONFIG_BLUEZ_HCIUART_BCSP_TXCRC
++ bcsp_crc_update(&bcsp_txmsg_crc, data[i]);
++#endif
++ }
++
++#ifdef CONFIG_BLUEZ_HCIUART_BCSP_TXCRC
++ /* Put CRC */
++ bcsp_txmsg_crc = bcsp_crc_reverse(bcsp_txmsg_crc);
++ bcsp_slip_one_byte(nskb, (u8) ((bcsp_txmsg_crc >> 8) & 0x00ff));
++ bcsp_slip_one_byte(nskb, (u8) (bcsp_txmsg_crc & 0x00ff));
++#endif
++
++ bcsp_slip_msgdelim(nskb);
++ return nskb;
++}
++
++/* This is a rewrite of pkt_avail in ABCSP */
++static struct sk_buff *bcsp_dequeue(struct hci_uart *hu)
++{
++ struct bcsp_struct *bcsp = (struct bcsp_struct *) hu->priv;
++ unsigned long flags;
++ struct sk_buff *skb;
++
++ /* First of all, check for unreliable messages in the queue,
++ since they have priority */
++
++ if ((skb = skb_dequeue(&bcsp->unrel)) != NULL) {
++ struct sk_buff *nskb = bcsp_prepare_pkt(bcsp, skb->data, skb->len, skb->pkt_type);
++ if (nskb) {
++ kfree_skb(skb);
++ return nskb;
++ } else {
++ skb_queue_head(&bcsp->unrel, skb);
++ BT_ERR("Could not dequeue pkt because alloc_skb failed");
++ }
++ }
++
++ /* Now, try to send a reliable pkt. We can only send a
++ reliable packet if the number of packets sent but not yet ack'ed
++ is < than the winsize */
++
++ spin_lock_irqsave(&bcsp->unack.lock, flags);
++
++ if (bcsp->unack.qlen < BCSP_TXWINSIZE && (skb = skb_dequeue(&bcsp->rel)) != NULL) {
++ struct sk_buff *nskb = bcsp_prepare_pkt(bcsp, skb->data, skb->len, skb->pkt_type);
++ if (nskb) {
++ __skb_queue_tail(&bcsp->unack, skb);
++ mod_timer(&bcsp->tbcsp, jiffies + HZ / 4);
++ spin_unlock_irqrestore(&bcsp->unack.lock, flags);
++ return nskb;
++ } else {
++ skb_queue_head(&bcsp->rel, skb);
++ BT_ERR("Could not dequeue pkt because alloc_skb failed");
++ }
++ }
++
++ spin_unlock_irqrestore(&bcsp->unack.lock, flags);
++
++
++ /* We could not send a reliable packet, either because there are
++ none or because there are too many unack'ed pkts. Did we receive
++ any packets we have not acknowledged yet ? */
++
++ if (bcsp->txack_req) {
++ /* if so, craft an empty ACK pkt and send it on BCSP unreliable
++ channel 0 */
++ struct sk_buff *nskb = bcsp_prepare_pkt(bcsp, NULL, 0, BCSP_ACK_PKT);
++ return nskb;
++ }
++
++ /* We have nothing to send */
++ return NULL;
++}
++
++static int bcsp_flush(struct hci_uart *hu)
++{
++ BT_DBG("hu %p", hu);
++ return 0;
++}
++
++/* Remove ack'ed packets */
++static void bcsp_pkt_cull(struct bcsp_struct *bcsp)
++{
++ unsigned long flags;
++ struct sk_buff *skb;
++ int i, pkts_to_be_removed;
++ u8 seqno;
++
++ spin_lock_irqsave(&bcsp->unack.lock, flags);
++
++ pkts_to_be_removed = bcsp->unack.qlen;
++ seqno = bcsp->msgq_txseq;
++
++ while (pkts_to_be_removed) {
++ if (bcsp->rxack == seqno)
++ break;
++ pkts_to_be_removed--;
++ seqno = (seqno - 1) & 0x07;
++ }
++
++ if (bcsp->rxack != seqno)
++ BT_ERR("Peer acked invalid packet");
++
++ BT_DBG("Removing %u pkts out of %u, up to seqno %u",
++ pkts_to_be_removed, bcsp->unack.qlen, (seqno - 1) & 0x07);
++
++ for (i = 0, skb = ((struct sk_buff *) &bcsp->unack)->next; i < pkts_to_be_removed
++ && skb != (struct sk_buff *) &bcsp->unack; i++) {
++ struct sk_buff *nskb;
++
++ nskb = skb->next;
++ __skb_unlink(skb, &bcsp->unack);
++ kfree_skb(skb);
++ skb = nskb;
++ }
++ if (bcsp->unack.qlen == 0)
++ del_timer(&bcsp->tbcsp);
++ spin_unlock_irqrestore(&bcsp->unack.lock, flags);
++
++ if (i != pkts_to_be_removed)
++ BT_ERR("Removed only %u out of %u pkts", i, pkts_to_be_removed);
++}
++
++/* Handle BCSP link-establishment packets. When we
++ detect a "sync" packet, symptom that the BT module has reset,
++ we do nothing :) (yet) */
++static void bcsp_handle_le_pkt(struct hci_uart *hu)
++{
++ struct bcsp_struct *bcsp = hu->priv;
++ u8 conf_pkt[4] = { 0xad, 0xef, 0xac, 0xed };
++ u8 conf_rsp_pkt[4] = { 0xde, 0xad, 0xd0, 0xd0 };
++ u8 sync_pkt[4] = { 0xda, 0xdc, 0xed, 0xed };
++
++ /* spot "conf" pkts and reply with a "conf rsp" pkt */
++ if (bcsp->rx_skb->data[1] >> 4 == 4 && bcsp->rx_skb->data[2] == 0 &&
++ !memcmp(&bcsp->rx_skb->data[4], conf_pkt, 4)) {
++ struct sk_buff *nskb = alloc_skb(4, GFP_ATOMIC);
++
++ BT_DBG("Found a LE conf pkt");
++ if (!nskb)
++ return;
++ memcpy(skb_put(nskb, 4), conf_rsp_pkt, 4);
++ nskb->pkt_type = BCSP_LE_PKT;
++
++ skb_queue_head(&bcsp->unrel, nskb);
++ hci_uart_tx_wakeup(hu);
++ }
++ /* Spot "sync" pkts. If we find one...disaster! */
++ else if (bcsp->rx_skb->data[1] >> 4 == 4 && bcsp->rx_skb->data[2] == 0 &&
++ !memcmp(&bcsp->rx_skb->data[4], sync_pkt, 4)) {
++ BT_ERR("Found a LE sync pkt, card has reset");
++ }
++}
++
++static inline void bcsp_unslip_one_byte(struct bcsp_struct *bcsp, unsigned char byte)
++{
++ const u8 c0 = 0xc0, db = 0xdb;
++
++ switch (bcsp->rx_esc_state) {
++ case BCSP_ESCSTATE_NOESC:
++ switch (byte) {
++ case 0xdb:
++ bcsp->rx_esc_state = BCSP_ESCSTATE_ESC;
++ break;
++ default:
++ memcpy(skb_put(bcsp->rx_skb, 1), &byte, 1);
++ if ((bcsp->rx_skb-> data[0] & 0x40) != 0 &&
++ bcsp->rx_state != BCSP_W4_CRC)
++ bcsp_crc_update(&bcsp->message_crc, byte);
++ bcsp->rx_count--;
++ }
++ break;
++
++ case BCSP_ESCSTATE_ESC:
++ switch (byte) {
++ case 0xdc:
++ memcpy(skb_put(bcsp->rx_skb, 1), &c0, 1);
++ if ((bcsp->rx_skb-> data[0] & 0x40) != 0 &&
++ bcsp->rx_state != BCSP_W4_CRC)
++ bcsp_crc_update(&bcsp-> message_crc, 0xc0);
++ bcsp->rx_esc_state = BCSP_ESCSTATE_NOESC;
++ bcsp->rx_count--;
++ break;
++
++ case 0xdd:
++ memcpy(skb_put(bcsp->rx_skb, 1), &db, 1);
++ if ((bcsp->rx_skb-> data[0] & 0x40) != 0 &&
++ bcsp->rx_state != BCSP_W4_CRC)
++ bcsp_crc_update(&bcsp-> message_crc, 0xdb);
++ bcsp->rx_esc_state = BCSP_ESCSTATE_NOESC;
++ bcsp->rx_count--;
++ break;
++
++ default:
++ BT_ERR ("Invalid byte %02x after esc byte", byte);
++ kfree_skb(bcsp->rx_skb);
++ bcsp->rx_skb = NULL;
++ bcsp->rx_state = BCSP_W4_PKT_DELIMITER;
++ bcsp->rx_count = 0;
++ }
++ }
++}
++
++static inline void bcsp_complete_rx_pkt(struct hci_uart *hu)
++{
++ struct bcsp_struct *bcsp = hu->priv;
++ int pass_up;
++
++ if (bcsp->rx_skb->data[0] & 0x80) { /* reliable pkt */
++ BT_DBG("Received seqno %u from card", bcsp->rxseq_txack);
++ bcsp->rxseq_txack++;
++ bcsp->rxseq_txack %= 0x8;
++ bcsp->txack_req = 1;
++
++ /* If needed, transmit an ack pkt */
++ hci_uart_tx_wakeup(hu);
++ }
++
++ bcsp->rxack = (bcsp->rx_skb->data[0] >> 3) & 0x07;
++ BT_DBG("Request for pkt %u from card", bcsp->rxack);
++
++ bcsp_pkt_cull(bcsp);
++ if ((bcsp->rx_skb->data[1] & 0x0f) == 6 &&
++ bcsp->rx_skb->data[0] & 0x80) {
++ bcsp->rx_skb->pkt_type = HCI_ACLDATA_PKT;
++ pass_up = 1;
++ } else if ((bcsp->rx_skb->data[1] & 0x0f) == 5 &&
++ bcsp->rx_skb->data[0] & 0x80) {
++ bcsp->rx_skb->pkt_type = HCI_EVENT_PKT;
++ pass_up = 1;
++ } else if ((bcsp->rx_skb->data[1] & 0x0f) == 7) {
++ bcsp->rx_skb->pkt_type = HCI_SCODATA_PKT;
++ pass_up = 1;
++ } else if ((bcsp->rx_skb->data[1] & 0x0f) == 1 &&
++ !(bcsp->rx_skb->data[0] & 0x80)) {
++ bcsp_handle_le_pkt(hu);
++ pass_up = 0;
++ } else
++ pass_up = 0;
++
++ if (!pass_up) {
++ if ((bcsp->rx_skb->data[1] & 0x0f) != 0 &&
++ (bcsp->rx_skb->data[1] & 0x0f) != 1) {
++ BT_ERR ("Packet for unknown channel (%u %s)",
++ bcsp->rx_skb->data[1] & 0x0f,
++ bcsp->rx_skb->data[0] & 0x80 ?
++ "reliable" : "unreliable");
++ }
++ kfree_skb(bcsp->rx_skb);
++ } else {
++ /* Pull out BCSP hdr */
++ skb_pull(bcsp->rx_skb, 4);
++
++ hci_recv_frame(bcsp->rx_skb);
++ }
++ bcsp->rx_state = BCSP_W4_PKT_DELIMITER;
++ bcsp->rx_skb = NULL;
++}
++
++/* Recv data */
++static int bcsp_recv(struct hci_uart *hu, void *data, int count)
++{
++ struct bcsp_struct *bcsp = hu->priv;
++ register unsigned char *ptr;
++
++ BT_DBG("hu %p count %d rx_state %ld rx_count %ld",
++ hu, count, bcsp->rx_state, bcsp->rx_count);
++
++ ptr = data;
++ while (count) {
++ if (bcsp->rx_count) {
++ if (*ptr == 0xc0) {
++ BT_ERR("Short BCSP packet");
++ kfree_skb(bcsp->rx_skb);
++ bcsp->rx_state = BCSP_W4_PKT_START;
++ bcsp->rx_count = 0;
++ } else
++ bcsp_unslip_one_byte(bcsp, *ptr);
++
++ ptr++; count--;
++ continue;
++ }
++
++ switch (bcsp->rx_state) {
++ case BCSP_W4_BCSP_HDR:
++ if ((0xff & (u8) ~ (bcsp->rx_skb->data[0] + bcsp->rx_skb->data[1] +
++ bcsp->rx_skb->data[2])) != bcsp->rx_skb->data[3]) {
++ BT_ERR("Error in BCSP hdr checksum");
++ kfree_skb(bcsp->rx_skb);
++ bcsp->rx_state = BCSP_W4_PKT_DELIMITER;
++ bcsp->rx_count = 0;
++ continue;
++ }
++ if (bcsp->rx_skb->data[0] & 0x80 /* reliable pkt */
++ && (bcsp->rx_skb->data[0] & 0x07) != bcsp->rxseq_txack) {
++ BT_ERR ("Out-of-order packet arrived, got %u expected %u",
++ bcsp->rx_skb->data[0] & 0x07, bcsp->rxseq_txack);
++
++ kfree_skb(bcsp->rx_skb);
++ bcsp->rx_state = BCSP_W4_PKT_DELIMITER;
++ bcsp->rx_count = 0;
++ continue;
++ }
++ bcsp->rx_state = BCSP_W4_DATA;
++ bcsp->rx_count = (bcsp->rx_skb->data[1] >> 4) +
++ (bcsp->rx_skb->data[2] << 4); /* May be 0 */
++ continue;
++
++ case BCSP_W4_DATA:
++ if (bcsp->rx_skb->data[0] & 0x40) { /* pkt with crc */
++ bcsp->rx_state = BCSP_W4_CRC;
++ bcsp->rx_count = 2;
++ } else
++ bcsp_complete_rx_pkt(hu);
++ continue;
++
++ case BCSP_W4_CRC:
++ if (bcsp_crc_reverse(bcsp->message_crc) !=
++ (bcsp->rx_skb->data[bcsp->rx_skb->len - 2] << 8) +
++ bcsp->rx_skb->data[bcsp->rx_skb->len - 1]) {
++
++ BT_ERR ("Checksum failed: computed %04x received %04x",
++ bcsp_crc_reverse(bcsp->message_crc),
++ (bcsp->rx_skb-> data[bcsp->rx_skb->len - 2] << 8) +
++ bcsp->rx_skb->data[bcsp->rx_skb->len - 1]);
++
++ kfree_skb(bcsp->rx_skb);
++ bcsp->rx_state = BCSP_W4_PKT_DELIMITER;
++ bcsp->rx_count = 0;
++ continue;
++ }
++ skb_trim(bcsp->rx_skb, bcsp->rx_skb->len - 2);
++ bcsp_complete_rx_pkt(hu);
++ continue;
++
++ case BCSP_W4_PKT_DELIMITER:
++ switch (*ptr) {
++ case 0xc0:
++ bcsp->rx_state = BCSP_W4_PKT_START;
++ break;
++ default:
++ /*BT_ERR("Ignoring byte %02x", *ptr);*/
++ break;
++ }
++ ptr++; count--;
++ break;
++
++ case BCSP_W4_PKT_START:
++ switch (*ptr) {
++ case 0xc0:
++ ptr++; count--;
++ break;
++
++ default:
++ bcsp->rx_state = BCSP_W4_BCSP_HDR;
++ bcsp->rx_count = 4;
++ bcsp->rx_esc_state = BCSP_ESCSTATE_NOESC;
++ BCSP_CRC_INIT(bcsp->message_crc);
++
++ /* Do not increment ptr or decrement count
++ * Allocate packet. Max len of a BCSP pkt=
++ * 0xFFF (payload) +4 (header) +2 (crc) */
++
++ bcsp->rx_skb = bluez_skb_alloc(0x1005, GFP_ATOMIC);
++ if (!bcsp->rx_skb) {
++ BT_ERR("Can't allocate mem for new packet");
++ bcsp->rx_state = BCSP_W4_PKT_DELIMITER;
++ bcsp->rx_count = 0;
++ return 0;
++ }
++ bcsp->rx_skb->dev = (void *) &hu->hdev;
++ break;
++ }
++ break;
++ }
++ }
++ return count;
++}
++
++ /* Arrange to retransmit all messages in the relq. */
++static void bcsp_timed_event(unsigned long arg)
++{
++ struct hci_uart *hu = (struct hci_uart *) arg;
++ struct bcsp_struct *bcsp = (struct bcsp_struct *) hu->priv;
++ struct sk_buff *skb;
++ unsigned long flags;
++
++ BT_DBG("hu %p retransmitting %u pkts", hu, bcsp->unack.qlen);
++
++ spin_lock_irqsave(&bcsp->unack.lock, flags);
++
++ while ((skb = __skb_dequeue_tail(&bcsp->unack)) != NULL) {
++ bcsp->msgq_txseq = (bcsp->msgq_txseq - 1) & 0x07;
++ skb_queue_head(&bcsp->rel, skb);
++ }
++
++ spin_unlock_irqrestore(&bcsp->unack.lock, flags);
++
++ hci_uart_tx_wakeup(hu);
++}
++
++static int bcsp_open(struct hci_uart *hu)
++{
++ struct bcsp_struct *bcsp;
++
++ BT_DBG("hu %p", hu);
++
++ bcsp = kmalloc(sizeof(*bcsp), GFP_ATOMIC);
++ if (!bcsp)
++ return -ENOMEM;
++ memset(bcsp, 0, sizeof(*bcsp));
++
++ hu->priv = bcsp;
++ skb_queue_head_init(&bcsp->unack);
++ skb_queue_head_init(&bcsp->rel);
++ skb_queue_head_init(&bcsp->unrel);
++
++ init_timer(&bcsp->tbcsp);
++ bcsp->tbcsp.function = bcsp_timed_event;
++ bcsp->tbcsp.data = (u_long) hu;
++
++ bcsp->rx_state = BCSP_W4_PKT_DELIMITER;
++
++ return 0;
++}
++
++static int bcsp_close(struct hci_uart *hu)
++{
++ struct bcsp_struct *bcsp = hu->priv;
++ hu->priv = NULL;
++
++ BT_DBG("hu %p", hu);
++
++ skb_queue_purge(&bcsp->unack);
++ skb_queue_purge(&bcsp->rel);
++ skb_queue_purge(&bcsp->unrel);
++ del_timer(&bcsp->tbcsp);
++
++ kfree(bcsp);
++ return 0;
++}
++
++static struct hci_uart_proto bcsp = {
++ id: HCI_UART_BCSP,
++ open: bcsp_open,
++ close: bcsp_close,
++ enqueue: bcsp_enqueue,
++ dequeue: bcsp_dequeue,
++ recv: bcsp_recv,
++ flush: bcsp_flush
++};
++
++int bcsp_init(void)
++{
++ return hci_uart_register_proto(&bcsp);
++}
++
++int bcsp_deinit(void)
++{
++ return hci_uart_unregister_proto(&bcsp);
++}
+diff -urN linux-2.4.18/drivers/bluetooth/hci_bcsp.h linux-2.4.18-mh15/drivers/bluetooth/hci_bcsp.h
+--- linux-2.4.18/drivers/bluetooth/hci_bcsp.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/drivers/bluetooth/hci_bcsp.h 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,70 @@
++/*
++ BlueCore Serial Protocol (BCSP) for Linux Bluetooth stack (BlueZ).
++ Copyright 2002 by Fabrizio Gennari <fabrizio.gennari@philips.com>
++
++ Based on
++ hci_h4.c by Maxim Krasnyansky <maxk@qualcomm.com>
++ ABCSP by Carl Orsborn <cjo@csr.com>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License version 2 as
++ published by the Free Software Foundation;
++
++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++
++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
++ SOFTWARE IS DISCLAIMED.
++*/
++
++/*
++ * $Id$
++ */
++
++#ifndef __HCI_BCSP_H__
++#define __HCI_BCSP_H__
++
++#define BCSP_TXWINSIZE 4
++
++#define BCSP_ACK_PKT 0x05
++#define BCSP_LE_PKT 0x06
++
++struct bcsp_struct {
++ struct sk_buff_head unack; /* Unack'ed packets queue */
++ struct sk_buff_head rel; /* Reliable packets queue */
++ struct sk_buff_head unrel; /* Unreliable packets queue */
++
++ unsigned long rx_count;
++ struct sk_buff *rx_skb;
++ u8 rxseq_txack; /* rxseq == txack. */
++ u8 rxack; /* Last packet sent by us that the peer ack'ed */
++ struct timer_list tbcsp;
++
++ enum {
++ BCSP_W4_PKT_DELIMITER,
++ BCSP_W4_PKT_START,
++ BCSP_W4_BCSP_HDR,
++ BCSP_W4_DATA,
++ BCSP_W4_CRC
++ } rx_state;
++
++ enum {
++ BCSP_ESCSTATE_NOESC,
++ BCSP_ESCSTATE_ESC
++ } rx_esc_state;
++
++ u16 message_crc;
++ u8 txack_req; /* Do we need to send ack's to the peer? */
++
++ /* Reliable packet sequence number - used to assign seq to each rel pkt. */
++ u8 msgq_txseq;
++};
++
++#endif /* __HCI_BCSP_H__ */
+diff -urN linux-2.4.18/drivers/bluetooth/hci_h4.c linux-2.4.18-mh15/drivers/bluetooth/hci_h4.c
+--- linux-2.4.18/drivers/bluetooth/hci_h4.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/drivers/bluetooth/hci_h4.c 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,277 @@
++/*
++ BlueZ - Bluetooth protocol stack for Linux
++ Copyright (C) 2000-2001 Qualcomm Incorporated
++
++ Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License version 2 as
++ published by the Free Software Foundation;
++
++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++
++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
++ SOFTWARE IS DISCLAIMED.
++*/
++
++/*
++ * BlueZ HCI UART(H4) protocol.
++ *
++ * $Id$
++ */
++#define VERSION "1.2"
++
++#include <linux/config.h>
++#include <linux/module.h>
++
++#include <linux/version.h>
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/sched.h>
++#include <linux/types.h>
++#include <linux/fcntl.h>
++#include <linux/interrupt.h>
++#include <linux/ptrace.h>
++#include <linux/poll.h>
++
++#include <linux/slab.h>
++#include <linux/tty.h>
++#include <linux/errno.h>
++#include <linux/string.h>
++#include <linux/signal.h>
++#include <linux/ioctl.h>
++#include <linux/skbuff.h>
++
++#include <net/bluetooth/bluetooth.h>
++#include <net/bluetooth/hci_core.h>
++#include "hci_uart.h"
++#include "hci_h4.h"
++
++#ifndef HCI_UART_DEBUG
++#undef BT_DBG
++#define BT_DBG( A... )
++#undef BT_DMP
++#define BT_DMP( A... )
++#endif
++
++/* Initialize protocol */
++static int h4_open(struct hci_uart *hu)
++{
++ struct h4_struct *h4;
++
++ BT_DBG("hu %p", hu);
++
++ h4 = kmalloc(sizeof(*h4), GFP_ATOMIC);
++ if (!h4)
++ return -ENOMEM;
++ memset(h4, 0, sizeof(*h4));
++
++ skb_queue_head_init(&h4->txq);
++
++ hu->priv = h4;
++ return 0;
++}
++
++/* Flush protocol data */
++static int h4_flush(struct hci_uart *hu)
++{
++ struct h4_struct *h4 = hu->priv;
++
++ BT_DBG("hu %p", hu);
++ skb_queue_purge(&h4->txq);
++ return 0;
++}
++
++/* Close protocol */
++static int h4_close(struct hci_uart *hu)
++{
++ struct h4_struct *h4 = hu->priv;
++ hu->priv = NULL;
++
++ BT_DBG("hu %p", hu);
++
++ skb_queue_purge(&h4->txq);
++ if (h4->rx_skb)
++ kfree_skb(h4->rx_skb);
++
++ hu->priv = NULL;
++ kfree(h4);
++ return 0;
++}
++
++/* Enqueue frame for transmittion (padding, crc, etc) */
++static int h4_enqueue(struct hci_uart *hu, struct sk_buff *skb)
++{
++ struct h4_struct *h4 = hu->priv;
++
++ BT_DBG("hu %p skb %p", hu, skb);
++
++ /* Prepend skb with frame type */
++ memcpy(skb_push(skb, 1), &skb->pkt_type, 1);
++ skb_queue_tail(&h4->txq, skb);
++ return 0;
++}
++
++static inline int h4_check_data_len(struct h4_struct *h4, int len)
++{
++ register int room = skb_tailroom(h4->rx_skb);
++
++ BT_DBG("len %d room %d", len, room);
++ if (!len) {
++ BT_DMP(h4->rx_skb->data, h4->rx_skb->len);
++ hci_recv_frame(h4->rx_skb);
++ } else if (len > room) {
++ BT_ERR("Data length is too large");
++ kfree_skb(h4->rx_skb);
++ } else {
++ h4->rx_state = H4_W4_DATA;
++ h4->rx_count = len;
++ return len;
++ }
++
++ h4->rx_state = H4_W4_PACKET_TYPE;
++ h4->rx_skb = NULL;
++ h4->rx_count = 0;
++ return 0;
++}
++
++/* Recv data */
++static int h4_recv(struct hci_uart *hu, void *data, int count)
++{
++ struct h4_struct *h4 = hu->priv;
++ register char *ptr;
++ hci_event_hdr *eh;
++ hci_acl_hdr *ah;
++ hci_sco_hdr *sh;
++ register int len, type, dlen;
++
++ BT_DBG("hu %p count %d rx_state %ld rx_count %ld",
++ hu, count, h4->rx_state, h4->rx_count);
++
++ ptr = data;
++ while (count) {
++ if (h4->rx_count) {
++ len = MIN(h4->rx_count, count);
++ memcpy(skb_put(h4->rx_skb, len), ptr, len);
++ h4->rx_count -= len; count -= len; ptr += len;
++
++ if (h4->rx_count)
++ continue;
++
++ switch (h4->rx_state) {
++ case H4_W4_DATA:
++ BT_DBG("Complete data");
++
++ BT_DMP(h4->rx_skb->data, h4->rx_skb->len);
++
++ hci_recv_frame(h4->rx_skb);
++
++ h4->rx_state = H4_W4_PACKET_TYPE;
++ h4->rx_skb = NULL;
++ continue;
++
++ case H4_W4_EVENT_HDR:
++ eh = (hci_event_hdr *) h4->rx_skb->data;
++
++ BT_DBG("Event header: evt 0x%2.2x plen %d", eh->evt, eh->plen);
++
++ h4_check_data_len(h4, eh->plen);
++ continue;
++
++ case H4_W4_ACL_HDR:
++ ah = (hci_acl_hdr *) h4->rx_skb->data;
++ dlen = __le16_to_cpu(ah->dlen);
++
++ BT_DBG("ACL header: dlen %d", dlen);
++
++ h4_check_data_len(h4, dlen);
++ continue;
++
++ case H4_W4_SCO_HDR:
++ sh = (hci_sco_hdr *) h4->rx_skb->data;
++
++ BT_DBG("SCO header: dlen %d", sh->dlen);
++
++ h4_check_data_len(h4, sh->dlen);
++ continue;
++ }
++ }
++
++ /* H4_W4_PACKET_TYPE */
++ switch (*ptr) {
++ case HCI_EVENT_PKT:
++ BT_DBG("Event packet");
++ h4->rx_state = H4_W4_EVENT_HDR;
++ h4->rx_count = HCI_EVENT_HDR_SIZE;
++ type = HCI_EVENT_PKT;
++ break;
++
++ case HCI_ACLDATA_PKT:
++ BT_DBG("ACL packet");
++ h4->rx_state = H4_W4_ACL_HDR;
++ h4->rx_count = HCI_ACL_HDR_SIZE;
++ type = HCI_ACLDATA_PKT;
++ break;
++
++ case HCI_SCODATA_PKT:
++ BT_DBG("SCO packet");
++ h4->rx_state = H4_W4_SCO_HDR;
++ h4->rx_count = HCI_SCO_HDR_SIZE;
++ type = HCI_SCODATA_PKT;
++ break;
++
++ default:
++ BT_ERR("Unknown HCI packet type %2.2x", (__u8)*ptr);
++ hu->hdev.stat.err_rx++;
++ ptr++; count--;
++ continue;
++ };
++ ptr++; count--;
++
++ /* Allocate packet */
++ h4->rx_skb = bluez_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC);
++ if (!h4->rx_skb) {
++ BT_ERR("Can't allocate mem for new packet");
++ h4->rx_state = H4_W4_PACKET_TYPE;
++ h4->rx_count = 0;
++ return 0;
++ }
++ h4->rx_skb->dev = (void *) &hu->hdev;
++ h4->rx_skb->pkt_type = type;
++ }
++ return count;
++}
++
++static struct sk_buff *h4_dequeue(struct hci_uart *hu)
++{
++ struct h4_struct *h4 = hu->priv;
++ return skb_dequeue(&h4->txq);
++}
++
++static struct hci_uart_proto h4p = {
++ id: HCI_UART_H4,
++ open: h4_open,
++ close: h4_close,
++ recv: h4_recv,
++ enqueue: h4_enqueue,
++ dequeue: h4_dequeue,
++ flush: h4_flush,
++};
++
++int h4_init(void)
++{
++ return hci_uart_register_proto(&h4p);
++}
++
++int h4_deinit(void)
++{
++ return hci_uart_unregister_proto(&h4p);
++}
+diff -urN linux-2.4.18/drivers/bluetooth/hci_h4.h linux-2.4.18-mh15/drivers/bluetooth/hci_h4.h
+--- linux-2.4.18/drivers/bluetooth/hci_h4.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/drivers/bluetooth/hci_h4.h 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,44 @@
++/*
++ BlueZ - Bluetooth protocol stack for Linux
++ Copyright (C) 2000-2001 Qualcomm Incorporated
++
++ Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License version 2 as
++ published by the Free Software Foundation;
++
++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++
++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
++ SOFTWARE IS DISCLAIMED.
++*/
++
++/*
++ * $Id$
++ */
++
++#ifdef __KERNEL__
++struct h4_struct {
++ unsigned long rx_state;
++ unsigned long rx_count;
++ struct sk_buff *rx_skb;
++ struct sk_buff_head txq;
++};
++
++/* H4 receiver States */
++#define H4_W4_PACKET_TYPE 0
++#define H4_W4_EVENT_HDR 1
++#define H4_W4_ACL_HDR 2
++#define H4_W4_SCO_HDR 3
++#define H4_W4_DATA 4
++
++#endif /* __KERNEL__ */
+diff -urN linux-2.4.18/drivers/bluetooth/hci_ldisc.c linux-2.4.18-mh15/drivers/bluetooth/hci_ldisc.c
+--- linux-2.4.18/drivers/bluetooth/hci_ldisc.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/drivers/bluetooth/hci_ldisc.c 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,579 @@
++/*
++ BlueZ - Bluetooth protocol stack for Linux
++ Copyright (C) 2000-2001 Qualcomm Incorporated
++
++ Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License version 2 as
++ published by the Free Software Foundation;
++
++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++
++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
++ SOFTWARE IS DISCLAIMED.
++*/
++
++/*
++ * BlueZ HCI UART driver.
++ *
++ * $Id$
++ */
++#define VERSION "2.1"
++
++#include <linux/config.h>
++#include <linux/module.h>
++
++#include <linux/version.h>
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/sched.h>
++#include <linux/types.h>
++#include <linux/fcntl.h>
++#include <linux/interrupt.h>
++#include <linux/ptrace.h>
++#include <linux/poll.h>
++
++#include <linux/slab.h>
++#include <linux/tty.h>
++#include <linux/errno.h>
++#include <linux/string.h>
++#include <linux/signal.h>
++#include <linux/ioctl.h>
++#include <linux/skbuff.h>
++
++#include <net/bluetooth/bluetooth.h>
++#include <net/bluetooth/hci_core.h>
++#include "hci_uart.h"
++
++#ifndef HCI_UART_DEBUG
++#undef BT_DBG
++#define BT_DBG( A... )
++#undef BT_DMP
++#define BT_DMP( A... )
++#endif
++
++static struct hci_uart_proto *hup[HCI_UART_MAX_PROTO];
++
++int hci_uart_register_proto(struct hci_uart_proto *p)
++{
++ if (p->id >= HCI_UART_MAX_PROTO)
++ return -EINVAL;
++
++ if (hup[p->id])
++ return -EEXIST;
++
++ hup[p->id] = p;
++ return 0;
++}
++
++int hci_uart_unregister_proto(struct hci_uart_proto *p)
++{
++ if (p->id >= HCI_UART_MAX_PROTO)
++ return -EINVAL;
++
++ if (!hup[p->id])
++ return -EINVAL;
++
++ hup[p->id] = NULL;
++ return 0;
++}
++
++static struct hci_uart_proto *hci_uart_get_proto(unsigned int id)
++{
++ if (id >= HCI_UART_MAX_PROTO)
++ return NULL;
++ return hup[id];
++}
++
++static inline void hci_uart_tx_complete(struct hci_uart *hu, int pkt_type)
++{
++ struct hci_dev *hdev = &hu->hdev;
++
++ /* Update HCI stat counters */
++ switch (pkt_type) {
++ case HCI_COMMAND_PKT:
++ hdev->stat.cmd_tx++;
++ break;
++
++ case HCI_ACLDATA_PKT:
++ hdev->stat.acl_tx++;
++ break;
++
++ case HCI_SCODATA_PKT:
++ hdev->stat.cmd_tx++;
++ break;
++ }
++}
++
++static inline struct sk_buff *hci_uart_dequeue(struct hci_uart *hu)
++{
++ struct sk_buff *skb = hu->tx_skb;
++ if (!skb)
++ skb = hu->proto->dequeue(hu);
++ else
++ hu->tx_skb = NULL;
++ return skb;
++}
++
++int hci_uart_tx_wakeup(struct hci_uart *hu)
++{
++ struct tty_struct *tty = hu->tty;
++ struct hci_dev *hdev = &hu->hdev;
++ struct sk_buff *skb;
++
++ if (test_and_set_bit(HCI_UART_SENDING, &hu->tx_state)) {
++ set_bit(HCI_UART_TX_WAKEUP, &hu->tx_state);
++ return 0;
++ }
++
++ BT_DBG("");
++
++restart:
++ clear_bit(HCI_UART_TX_WAKEUP, &hu->tx_state);
++
++ while ((skb = hci_uart_dequeue(hu))) {
++ int len;
++
++ set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
++ len = tty->driver.write(tty, 0, skb->data, skb->len);
++ hdev->stat.byte_tx += len;
++
++ skb_pull(skb, len);
++ if (skb->len) {
++ hu->tx_skb = skb;
++ break;
++ }
++
++ hci_uart_tx_complete(hu, skb->pkt_type);
++ kfree_skb(skb);
++ }
++
++ if (test_bit(HCI_UART_TX_WAKEUP, &hu->tx_state))
++ goto restart;
++
++ clear_bit(HCI_UART_SENDING, &hu->tx_state);
++ return 0;
++}
++
++/* ------- Interface to HCI layer ------ */
++/* Initialize device */
++static int hci_uart_open(struct hci_dev *hdev)
++{
++ BT_DBG("%s %p", hdev->name, hdev);
++
++ /* Nothing to do for UART driver */
++
++ set_bit(HCI_RUNNING, &hdev->flags);
++ return 0;
++}
++
++/* Reset device */
++static int hci_uart_flush(struct hci_dev *hdev)
++{
++ struct hci_uart *hu = (struct hci_uart *) hdev->driver_data;
++ struct tty_struct *tty = hu->tty;
++
++ BT_DBG("hdev %p tty %p", hdev, tty);
++
++ if (hu->tx_skb) {
++ kfree_skb(hu->tx_skb); hu->tx_skb = NULL;
++ }
++
++ /* Flush any pending characters in the driver and discipline. */
++ if (tty->ldisc.flush_buffer)
++ tty->ldisc.flush_buffer(tty);
++
++ if (tty->driver.flush_buffer)
++ tty->driver.flush_buffer(tty);
++
++ if (test_bit(HCI_UART_PROTO_SET, &hu->flags))
++ hu->proto->flush(hu);
++
++ return 0;
++}
++
++/* Close device */
++static int hci_uart_close(struct hci_dev *hdev)
++{
++ BT_DBG("hdev %p", hdev);
++
++ if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
++ return 0;
++
++ hci_uart_flush(hdev);
++ return 0;
++}
++
++/* Send frames from HCI layer */
++static int hci_uart_send_frame(struct sk_buff *skb)
++{
++ struct hci_dev* hdev = (struct hci_dev *) skb->dev;
++ struct tty_struct *tty;
++ struct hci_uart *hu;
++
++ if (!hdev) {
++ BT_ERR("Frame for uknown device (hdev=NULL)");
++ return -ENODEV;
++ }
++
++ if (!test_bit(HCI_RUNNING, &hdev->flags))
++ return -EBUSY;
++
++ hu = (struct hci_uart *) hdev->driver_data;
++ tty = hu->tty;
++
++ BT_DBG("%s: type %d len %d", hdev->name, skb->pkt_type, skb->len);
++
++ hu->proto->enqueue(hu, skb);
++
++ hci_uart_tx_wakeup(hu);
++ return 0;
++}
++
++static void hci_uart_destruct(struct hci_dev *hdev)
++{
++ struct hci_uart *hu;
++
++ if (!hdev) return;
++
++ BT_DBG("%s", hdev->name);
++
++ hu = (struct hci_uart *) hdev->driver_data;
++ kfree(hu);
++
++ MOD_DEC_USE_COUNT;
++}
++
++/* ------ LDISC part ------ */
++/* hci_uart_tty_open
++ *
++ * Called when line discipline changed to HCI_UART.
++ *
++ * Arguments:
++ * tty pointer to tty info structure
++ * Return Value:
++ * 0 if success, otherwise error code
++ */
++static int hci_uart_tty_open(struct tty_struct *tty)
++{
++ struct hci_uart *hu = (void *) tty->disc_data;
++
++ BT_DBG("tty %p", tty);
++
++ if (hu)
++ return -EEXIST;
++
++ if (!(hu = kmalloc(sizeof(struct hci_uart), GFP_KERNEL))) {
++ BT_ERR("Can't allocate controll structure");
++ return -ENFILE;
++ }
++ memset(hu, 0, sizeof(struct hci_uart));
++
++ tty->disc_data = hu;
++ hu->tty = tty;
++
++ spin_lock_init(&hu->rx_lock);
++
++ /* Flush any pending characters in the driver and line discipline */
++ if (tty->ldisc.flush_buffer)
++ tty->ldisc.flush_buffer(tty);
++
++ if (tty->driver.flush_buffer)
++ tty->driver.flush_buffer(tty);
++
++ MOD_INC_USE_COUNT;
++ return 0;
++}
++
++/* hci_uart_tty_close()
++ *
++ * Called when the line discipline is changed to something
++ * else, the tty is closed, or the tty detects a hangup.
++ */
++static void hci_uart_tty_close(struct tty_struct *tty)
++{
++ struct hci_uart *hu = (void *)tty->disc_data;
++
++ BT_DBG("tty %p", tty);
++
++ /* Detach from the tty */
++ tty->disc_data = NULL;
++
++ if (hu) {
++ struct hci_dev *hdev = &hu->hdev;
++ hci_uart_close(hdev);
++
++ if (test_and_clear_bit(HCI_UART_PROTO_SET, &hu->flags)) {
++ hu->proto->close(hu);
++ hci_unregister_dev(hdev);
++ }
++
++ MOD_DEC_USE_COUNT;
++ }
++}
++
++/* hci_uart_tty_wakeup()
++ *
++ * Callback for transmit wakeup. Called when low level
++ * device driver can accept more send data.
++ *
++ * Arguments: tty pointer to associated tty instance data
++ * Return Value: None
++ */
++static void hci_uart_tty_wakeup(struct tty_struct *tty)
++{
++ struct hci_uart *hu = (void *)tty->disc_data;
++
++ BT_DBG("");
++
++ if (!hu)
++ return;
++
++ clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
++
++ if (tty != hu->tty)
++ return;
++
++ if (test_bit(HCI_UART_PROTO_SET, &hu->flags))
++ hci_uart_tx_wakeup(hu);
++}
++
++/* hci_uart_tty_room()
++ *
++ * Callback function from tty driver. Return the amount of
++ * space left in the receiver's buffer to decide if remote
++ * transmitter is to be throttled.
++ *
++ * Arguments: tty pointer to associated tty instance data
++ * Return Value: number of bytes left in receive buffer
++ */
++static int hci_uart_tty_room (struct tty_struct *tty)
++{
++ return 65536;
++}
++
++/* hci_uart_tty_receive()
++ *
++ * Called by tty low level driver when receive data is
++ * available.
++ *
++ * Arguments: tty pointer to tty isntance data
++ * data pointer to received data
++ * flags pointer to flags for data
++ * count count of received data in bytes
++ *
++ * Return Value: None
++ */
++static void hci_uart_tty_receive(struct tty_struct *tty, const __u8 *data, char *flags, int count)
++{
++ struct hci_uart *hu = (void *)tty->disc_data;
++
++ if (!hu || tty != hu->tty)
++ return;
++
++ if (!test_bit(HCI_UART_PROTO_SET, &hu->flags))
++ return;
++
++ spin_lock(&hu->rx_lock);
++ hu->proto->recv(hu, (void *) data, count);
++ hu->hdev.stat.byte_rx += count;
++ spin_unlock(&hu->rx_lock);
++
++ if (test_and_clear_bit(TTY_THROTTLED,&tty->flags) && tty->driver.unthrottle)
++ tty->driver.unthrottle(tty);
++}
++
++static int hci_uart_register_dev(struct hci_uart *hu)
++{
++ struct hci_dev *hdev;
++
++ BT_DBG("");
++
++ /* Initialize and register HCI device */
++ hdev = &hu->hdev;
++
++ hdev->type = HCI_UART;
++ hdev->driver_data = hu;
++
++ hdev->open = hci_uart_open;
++ hdev->close = hci_uart_close;
++ hdev->flush = hci_uart_flush;
++ hdev->send = hci_uart_send_frame;
++ hdev->destruct = hci_uart_destruct;
++
++ if (hci_register_dev(hdev) < 0) {
++ BT_ERR("Can't register HCI device %s", hdev->name);
++ return -ENODEV;
++ }
++ MOD_INC_USE_COUNT;
++ return 0;
++}
++
++static int hci_uart_set_proto(struct hci_uart *hu, int id)
++{
++ struct hci_uart_proto *p;
++ int err;
++
++ p = hci_uart_get_proto(id);
++ if (!p)
++ return -EPROTONOSUPPORT;
++
++ err = p->open(hu);
++ if (err)
++ return err;
++
++ hu->proto = p;
++
++ err = hci_uart_register_dev(hu);
++ if (err) {
++ p->close(hu);
++ return err;
++ }
++ return 0;
++}
++
++/* hci_uart_tty_ioctl()
++ *
++ * Process IOCTL system call for the tty device.
++ *
++ * Arguments:
++ *
++ * tty pointer to tty instance data
++ * file pointer to open file object for device
++ * cmd IOCTL command code
++ * arg argument for IOCTL call (cmd dependent)
++ *
++ * Return Value: Command dependent
++ */
++static int hci_uart_tty_ioctl(struct tty_struct *tty, struct file * file,
++ unsigned int cmd, unsigned long arg)
++{
++ struct hci_uart *hu = (void *)tty->disc_data;
++ int err = 0;
++
++ BT_DBG("");
++
++ /* Verify the status of the device */
++ if (!hu)
++ return -EBADF;
++
++ switch (cmd) {
++ case HCIUARTSETPROTO:
++ if (!test_and_set_bit(HCI_UART_PROTO_SET, &hu->flags)) {
++ err = hci_uart_set_proto(hu, arg);
++ if (err) {
++ clear_bit(HCI_UART_PROTO_SET, &hu->flags);
++ return err;
++ }
++ tty->low_latency = 1;
++ } else
++ return -EBUSY;
++
++ case HCIUARTGETPROTO:
++ if (test_bit(HCI_UART_PROTO_SET, &hu->flags))
++ return hu->proto->id;
++ return -EUNATCH;
++
++ default:
++ err = n_tty_ioctl(tty, file, cmd, arg);
++ break;
++ };
++
++ return err;
++}
++
++/*
++ * We don't provide read/write/poll interface for user space.
++ */
++static ssize_t hci_uart_tty_read(struct tty_struct *tty, struct file *file, unsigned char *buf, size_t nr)
++{
++ return 0;
++}
++static ssize_t hci_uart_tty_write(struct tty_struct *tty, struct file *file, const unsigned char *data, size_t count)
++{
++ return 0;
++}
++static unsigned int hci_uart_tty_poll(struct tty_struct *tty, struct file *filp, poll_table *wait)
++{
++ return 0;
++}
++
++#ifdef CONFIG_BLUEZ_HCIUART_H4
++int h4_init(void);
++int h4_deinit(void);
++#endif
++#ifdef CONFIG_BLUEZ_HCIUART_BCSP
++int bcsp_init(void);
++int bcsp_deinit(void);
++#endif
++
++int __init hci_uart_init(void)
++{
++ static struct tty_ldisc hci_uart_ldisc;
++ int err;
++
++ BT_INFO("BlueZ HCI UART driver ver %s Copyright (C) 2000,2001 Qualcomm Inc",
++ VERSION);
++ BT_INFO("Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>");
++
++ /* Register the tty discipline */
++
++ memset(&hci_uart_ldisc, 0, sizeof (hci_uart_ldisc));
++ hci_uart_ldisc.magic = TTY_LDISC_MAGIC;
++ hci_uart_ldisc.name = "n_hci";
++ hci_uart_ldisc.open = hci_uart_tty_open;
++ hci_uart_ldisc.close = hci_uart_tty_close;
++ hci_uart_ldisc.read = hci_uart_tty_read;
++ hci_uart_ldisc.write = hci_uart_tty_write;
++ hci_uart_ldisc.ioctl = hci_uart_tty_ioctl;
++ hci_uart_ldisc.poll = hci_uart_tty_poll;
++ hci_uart_ldisc.receive_room= hci_uart_tty_room;
++ hci_uart_ldisc.receive_buf = hci_uart_tty_receive;
++ hci_uart_ldisc.write_wakeup= hci_uart_tty_wakeup;
++
++ if ((err = tty_register_ldisc(N_HCI, &hci_uart_ldisc))) {
++ BT_ERR("Can't register HCI line discipline (%d)", err);
++ return err;
++ }
++
++#ifdef CONFIG_BLUEZ_HCIUART_H4
++ h4_init();
++#endif
++#ifdef CONFIG_BLUEZ_HCIUART_BCSP
++ bcsp_init();
++#endif
++
++ return 0;
++}
++
++void hci_uart_cleanup(void)
++{
++ int err;
++
++#ifdef CONFIG_BLUEZ_HCIUART_H4
++ h4_deinit();
++#endif
++#ifdef CONFIG_BLUEZ_HCIUART_BCSP
++ bcsp_deinit();
++#endif
++
++ /* Release tty registration of line discipline */
++ if ((err = tty_register_ldisc(N_HCI, NULL)))
++ BT_ERR("Can't unregister HCI line discipline (%d)", err);
++}
++
++module_init(hci_uart_init);
++module_exit(hci_uart_cleanup);
++
++MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>");
++MODULE_DESCRIPTION("BlueZ HCI UART driver ver " VERSION);
++MODULE_LICENSE("GPL");
+diff -urN linux-2.4.18/drivers/bluetooth/hci_uart.c linux-2.4.18-mh15/drivers/bluetooth/hci_uart.c
+--- linux-2.4.18/drivers/bluetooth/hci_uart.c 2001-09-07 18:28:38.000000000 +0200
++++ linux-2.4.18-mh15/drivers/bluetooth/hci_uart.c 1970-01-01 01:00:00.000000000 +0100
+@@ -1,580 +0,0 @@
+-/*
+- BlueZ - Bluetooth protocol stack for Linux
+- Copyright (C) 2000-2001 Qualcomm Incorporated
+-
+- Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
+-
+- This program is free software; you can redistribute it and/or modify
+- it under the terms of the GNU General Public License version 2 as
+- published by the Free Software Foundation;
+-
+- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+- OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+- IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+- CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
+- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+-
+- ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
+- COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
+- SOFTWARE IS DISCLAIMED.
+-*/
+-
+-/*
+- * BlueZ HCI UART driver.
+- *
+- * $Id$
+- */
+-#define VERSION "1.0"
+-
+-#include <linux/config.h>
+-#include <linux/module.h>
+-
+-#include <linux/version.h>
+-#include <linux/config.h>
+-#include <linux/kernel.h>
+-#include <linux/init.h>
+-#include <linux/sched.h>
+-#include <linux/types.h>
+-#include <linux/fcntl.h>
+-#include <linux/interrupt.h>
+-#include <linux/ptrace.h>
+-#include <linux/poll.h>
+-
+-#include <linux/slab.h>
+-#include <linux/tty.h>
+-#include <linux/errno.h>
+-#include <linux/string.h>
+-#include <linux/signal.h>
+-#include <linux/ioctl.h>
+-#include <linux/skbuff.h>
+-
+-#include <net/bluetooth/bluetooth.h>
+-#include <net/bluetooth/bluez.h>
+-#include <net/bluetooth/hci_core.h>
+-#include <net/bluetooth/hci_uart.h>
+-
+-#ifndef HCI_UART_DEBUG
+-#undef DBG
+-#define DBG( A... )
+-#undef DMP
+-#define DMP( A... )
+-#endif
+-
+-/* ------- Interface to HCI layer ------ */
+-/* Initialize device */
+-int n_hci_open(struct hci_dev *hdev)
+-{
+- DBG("%s %p", hdev->name, hdev);
+-
+- /* Nothing to do for UART driver */
+-
+- hdev->flags |= HCI_RUNNING;
+-
+- return 0;
+-}
+-
+-/* Reset device */
+-int n_hci_flush(struct hci_dev *hdev)
+-{
+- struct n_hci *n_hci = (struct n_hci *) hdev->driver_data;
+- struct tty_struct *tty = n_hci->tty;
+-
+- DBG("hdev %p tty %p", hdev, tty);
+-
+- /* Drop TX queue */
+- skb_queue_purge(&n_hci->txq);
+-
+- /* Flush any pending characters in the driver and discipline. */
+- if (tty->ldisc.flush_buffer)
+- tty->ldisc.flush_buffer(tty);
+-
+- if (tty->driver.flush_buffer)
+- tty->driver.flush_buffer(tty);
+-
+- return 0;
+-}
+-
+-/* Close device */
+-int n_hci_close(struct hci_dev *hdev)
+-{
+- DBG("hdev %p", hdev);
+-
+- hdev->flags &= ~HCI_RUNNING;
+-
+- n_hci_flush(hdev);
+-
+- return 0;
+-}
+-
+-int n_hci_tx_wakeup(struct n_hci *n_hci)
+-{
+- register struct tty_struct *tty = n_hci->tty;
+-
+- if (test_and_set_bit(TRANS_SENDING, &n_hci->tx_state)) {
+- set_bit(TRANS_WAKEUP, &n_hci->tx_state);
+- return 0;
+- }
+-
+- DBG("");
+- do {
+- register struct sk_buff *skb;
+- register int len;
+-
+- clear_bit(TRANS_WAKEUP, &n_hci->tx_state);
+-
+- if (!(skb = skb_dequeue(&n_hci->txq)))
+- break;
+-
+- DMP(skb->data, skb->len);
+-
+- /* Send frame to TTY driver */
+- tty->flags |= (1 << TTY_DO_WRITE_WAKEUP);
+- len = tty->driver.write(tty, 0, skb->data, skb->len);
+-
+- n_hci->hdev.stat.byte_tx += len;
+-
+- DBG("sent %d", len);
+-
+- if (len == skb->len) {
+- /* Full frame was sent */
+- kfree_skb(skb);
+- } else {
+- /* Subtract sent part and requeue */
+- skb_pull(skb, len);
+- skb_queue_head(&n_hci->txq, skb);
+- }
+- } while (test_bit(TRANS_WAKEUP, &n_hci->tx_state));
+- clear_bit(TRANS_SENDING, &n_hci->tx_state);
+-
+- return 0;
+-}
+-
+-/* Send frames from HCI layer */
+-int n_hci_send_frame(struct sk_buff *skb)
+-{
+- struct hci_dev* hdev = (struct hci_dev *) skb->dev;
+- struct tty_struct *tty;
+- struct n_hci *n_hci;
+-
+- if (!hdev) {
+- ERR("Frame for uknown device (hdev=NULL)");
+- return -ENODEV;
+- }
+-
+- if (!(hdev->flags & HCI_RUNNING))
+- return -EBUSY;
+-
+- n_hci = (struct n_hci *) hdev->driver_data;
+- tty = n_hci2tty(n_hci);
+-
+- DBG("%s: type %d len %d", hdev->name, skb->pkt_type, skb->len);
+-
+- switch (skb->pkt_type) {
+- case HCI_COMMAND_PKT:
+- hdev->stat.cmd_tx++;
+- break;
+-
+- case HCI_ACLDATA_PKT:
+- hdev->stat.acl_tx++;
+- break;
+-
+- case HCI_SCODATA_PKT:
+- hdev->stat.cmd_tx++;
+- break;
+- };
+-
+- /* Prepend skb with frame type and queue */
+- memcpy(skb_push(skb, 1), &skb->pkt_type, 1);
+- skb_queue_tail(&n_hci->txq, skb);
+-
+- n_hci_tx_wakeup(n_hci);
+-
+- return 0;
+-}
+-
+-/* ------ LDISC part ------ */
+-
+-/* n_hci_tty_open
+- *
+- * Called when line discipline changed to N_HCI.
+- *
+- * Arguments:
+- * tty pointer to tty info structure
+- * Return Value:
+- * 0 if success, otherwise error code
+- */
+-static int n_hci_tty_open(struct tty_struct *tty)
+-{
+- struct n_hci *n_hci = tty2n_hci(tty);
+- struct hci_dev *hdev;
+-
+- DBG("tty %p", tty);
+-
+- if (n_hci)
+- return -EEXIST;
+-
+- if (!(n_hci = kmalloc(sizeof(struct n_hci), GFP_KERNEL))) {
+- ERR("Can't allocate controll structure");
+- return -ENFILE;
+- }
+- memset(n_hci, 0, sizeof(struct n_hci));
+-
+- /* Initialize and register HCI device */
+- hdev = &n_hci->hdev;
+-
+- hdev->type = HCI_UART;
+- hdev->driver_data = n_hci;
+-
+- hdev->open = n_hci_open;
+- hdev->close = n_hci_close;
+- hdev->flush = n_hci_flush;
+- hdev->send = n_hci_send_frame;
+-
+- if (hci_register_dev(hdev) < 0) {
+- ERR("Can't register HCI device %s", hdev->name);
+- kfree(n_hci);
+- return -ENODEV;
+- }
+-
+- tty->disc_data = n_hci;
+- n_hci->tty = tty;
+-
+- spin_lock_init(&n_hci->rx_lock);
+- n_hci->rx_state = WAIT_PACKET_TYPE;
+-
+- skb_queue_head_init(&n_hci->txq);
+-
+- MOD_INC_USE_COUNT;
+-
+- /* Flush any pending characters in the driver and discipline. */
+- if (tty->ldisc.flush_buffer)
+- tty->ldisc.flush_buffer(tty);
+-
+- if (tty->driver.flush_buffer)
+- tty->driver.flush_buffer(tty);
+-
+- return 0;
+-}
+-
+-/* n_hci_tty_close()
+- *
+- * Called when the line discipline is changed to something
+- * else, the tty is closed, or the tty detects a hangup.
+- */
+-static void n_hci_tty_close(struct tty_struct *tty)
+-{
+- struct n_hci *n_hci = tty2n_hci(tty);
+- struct hci_dev *hdev = &n_hci->hdev;
+-
+- DBG("tty %p hdev %p", tty, hdev);
+-
+- if (n_hci != NULL) {
+- n_hci_close(hdev);
+-
+- if (hci_unregister_dev(hdev) < 0) {
+- ERR("Can't unregister HCI device %s",hdev->name);
+- }
+-
+- hdev->driver_data = NULL;
+- tty->disc_data = NULL;
+- kfree(n_hci);
+-
+- MOD_DEC_USE_COUNT;
+- }
+-}
+-
+-/* n_hci_tty_wakeup()
+- *
+- * Callback for transmit wakeup. Called when low level
+- * device driver can accept more send data.
+- *
+- * Arguments: tty pointer to associated tty instance data
+- * Return Value: None
+- */
+-static void n_hci_tty_wakeup( struct tty_struct *tty )
+-{
+- struct n_hci *n_hci = tty2n_hci(tty);
+-
+- DBG("");
+-
+- if (!n_hci)
+- return;
+-
+- tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
+-
+- if (tty != n_hci->tty)
+- return;
+-
+- n_hci_tx_wakeup(n_hci);
+-}
+-
+-/* n_hci_tty_room()
+- *
+- * Callback function from tty driver. Return the amount of
+- * space left in the receiver's buffer to decide if remote
+- * transmitter is to be throttled.
+- *
+- * Arguments: tty pointer to associated tty instance data
+- * Return Value: number of bytes left in receive buffer
+- */
+-static int n_hci_tty_room (struct tty_struct *tty)
+-{
+- return 65536;
+-}
+-
+-static inline int n_hci_check_data_len(struct n_hci *n_hci, int len)
+-{
+- register int room = skb_tailroom(n_hci->rx_skb);
+-
+- DBG("len %d room %d", len, room);
+- if (!len) {
+- DMP(n_hci->rx_skb->data, n_hci->rx_skb->len);
+- hci_recv_frame(n_hci->rx_skb);
+- } else if (len > room) {
+- ERR("Data length is to large");
+- kfree_skb(n_hci->rx_skb);
+- n_hci->hdev.stat.err_rx++;
+- } else {
+- n_hci->rx_state = WAIT_DATA;
+- n_hci->rx_count = len;
+- return len;
+- }
+-
+- n_hci->rx_state = WAIT_PACKET_TYPE;
+- n_hci->rx_skb = NULL;
+- n_hci->rx_count = 0;
+- return 0;
+-}
+-
+-static inline void n_hci_rx(struct n_hci *n_hci, const __u8 * data, char *flags, int count)
+-{
+- register const char *ptr;
+- hci_event_hdr *eh;
+- hci_acl_hdr *ah;
+- hci_sco_hdr *sh;
+- register int len, type, dlen;
+-
+- DBG("count %d state %ld rx_count %ld", count, n_hci->rx_state, n_hci->rx_count);
+-
+- n_hci->hdev.stat.byte_rx += count;
+-
+- ptr = data;
+- while (count) {
+- if (n_hci->rx_count) {
+- len = MIN(n_hci->rx_count, count);
+- memcpy(skb_put(n_hci->rx_skb, len), ptr, len);
+- n_hci->rx_count -= len; count -= len; ptr += len;
+-
+- if (n_hci->rx_count)
+- continue;
+-
+- switch (n_hci->rx_state) {
+- case WAIT_DATA:
+- DBG("Complete data");
+-
+- DMP(n_hci->rx_skb->data, n_hci->rx_skb->len);
+-
+- hci_recv_frame(n_hci->rx_skb);
+-
+- n_hci->rx_state = WAIT_PACKET_TYPE;
+- n_hci->rx_skb = NULL;
+- continue;
+-
+- case WAIT_EVENT_HDR:
+- eh = (hci_event_hdr *) n_hci->rx_skb->data;
+-
+- DBG("Event header: evt 0x%2.2x plen %d", eh->evt, eh->plen);
+-
+- n_hci_check_data_len(n_hci, eh->plen);
+- continue;
+-
+- case WAIT_ACL_HDR:
+- ah = (hci_acl_hdr *) n_hci->rx_skb->data;
+- dlen = __le16_to_cpu(ah->dlen);
+-
+- DBG("ACL header: dlen %d", dlen);
+-
+- n_hci_check_data_len(n_hci, dlen);
+- continue;
+-
+- case WAIT_SCO_HDR:
+- sh = (hci_sco_hdr *) n_hci->rx_skb->data;
+-
+- DBG("SCO header: dlen %d", sh->dlen);
+-
+- n_hci_check_data_len(n_hci, sh->dlen);
+- continue;
+- };
+- }
+-
+- /* WAIT_PACKET_TYPE */
+- switch (*ptr) {
+- case HCI_EVENT_PKT:
+- DBG("Event packet");
+- n_hci->rx_state = WAIT_EVENT_HDR;
+- n_hci->rx_count = HCI_EVENT_HDR_SIZE;
+- type = HCI_EVENT_PKT;
+- break;
+-
+- case HCI_ACLDATA_PKT:
+- DBG("ACL packet");
+- n_hci->rx_state = WAIT_ACL_HDR;
+- n_hci->rx_count = HCI_ACL_HDR_SIZE;
+- type = HCI_ACLDATA_PKT;
+- break;
+-
+- case HCI_SCODATA_PKT:
+- DBG("SCO packet");
+- n_hci->rx_state = WAIT_SCO_HDR;
+- n_hci->rx_count = HCI_SCO_HDR_SIZE;
+- type = HCI_SCODATA_PKT;
+- break;
+-
+- default:
+- ERR("Unknown HCI packet type %2.2x", (__u8)*ptr);
+- n_hci->hdev.stat.err_rx++;
+- ptr++; count--;
+- continue;
+- };
+- ptr++; count--;
+-
+- /* Allocate packet */
+- if (!(n_hci->rx_skb = bluez_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC))) {
+- ERR("Can't allocate mem for new packet");
+-
+- n_hci->rx_state = WAIT_PACKET_TYPE;
+- n_hci->rx_count = 0;
+- return;
+- }
+- n_hci->rx_skb->dev = (void *) &n_hci->hdev;
+- n_hci->rx_skb->pkt_type = type;
+- }
+-}
+-
+-/* n_hci_tty_receive()
+- *
+- * Called by tty low level driver when receive data is
+- * available.
+- *
+- * Arguments: tty pointer to tty isntance data
+- * data pointer to received data
+- * flags pointer to flags for data
+- * count count of received data in bytes
+- *
+- * Return Value: None
+- */
+-static void n_hci_tty_receive(struct tty_struct *tty, const __u8 * data, char *flags, int count)
+-{
+- struct n_hci *n_hci = tty2n_hci(tty);
+-
+- if (!n_hci || tty != n_hci->tty)
+- return;
+-
+- spin_lock(&n_hci->rx_lock);
+- n_hci_rx(n_hci, data, flags, count);
+- spin_unlock(&n_hci->rx_lock);
+-
+- if (test_and_clear_bit(TTY_THROTTLED,&tty->flags) && tty->driver.unthrottle)
+- tty->driver.unthrottle(tty);
+-}
+-
+-/* n_hci_tty_ioctl()
+- *
+- * Process IOCTL system call for the tty device.
+- *
+- * Arguments:
+- *
+- * tty pointer to tty instance data
+- * file pointer to open file object for device
+- * cmd IOCTL command code
+- * arg argument for IOCTL call (cmd dependent)
+- *
+- * Return Value: Command dependent
+- */
+-static int n_hci_tty_ioctl (struct tty_struct *tty, struct file * file,
+- unsigned int cmd, unsigned long arg)
+-{
+- struct n_hci *n_hci = tty2n_hci(tty);
+- int error = 0;
+-
+- DBG("");
+-
+- /* Verify the status of the device */
+- if (!n_hci)
+- return -EBADF;
+-
+- switch (cmd) {
+- default:
+- error = n_tty_ioctl(tty, file, cmd, arg);
+- break;
+- };
+-
+- return error;
+-}
+-
+-/*
+- * We don't provide read/write/poll interface for user space.
+- */
+-static ssize_t n_hci_tty_read(struct tty_struct *tty, struct file *file, unsigned char *buf, size_t nr)
+-{
+- return 0;
+-}
+-static ssize_t n_hci_tty_write(struct tty_struct *tty, struct file *file, const unsigned char *data, size_t count)
+-{
+- return 0;
+-}
+-static unsigned int n_hci_tty_poll(struct tty_struct *tty, struct file *filp, poll_table *wait)
+-{
+- return 0;
+-}
+-
+-int __init n_hci_init(void)
+-{
+- static struct tty_ldisc n_hci_ldisc;
+- int err;
+-
+- INF("BlueZ HCI UART driver ver %s Copyright (C) 2000,2001 Qualcomm Inc",
+- VERSION);
+- INF("Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>");
+-
+- /* Register the tty discipline */
+-
+- memset(&n_hci_ldisc, 0, sizeof (n_hci_ldisc));
+- n_hci_ldisc.magic = TTY_LDISC_MAGIC;
+- n_hci_ldisc.name = "n_hci";
+- n_hci_ldisc.open = n_hci_tty_open;
+- n_hci_ldisc.close = n_hci_tty_close;
+- n_hci_ldisc.read = n_hci_tty_read;
+- n_hci_ldisc.write = n_hci_tty_write;
+- n_hci_ldisc.ioctl = n_hci_tty_ioctl;
+- n_hci_ldisc.poll = n_hci_tty_poll;
+- n_hci_ldisc.receive_room= n_hci_tty_room;
+- n_hci_ldisc.receive_buf = n_hci_tty_receive;
+- n_hci_ldisc.write_wakeup= n_hci_tty_wakeup;
+-
+- if ((err = tty_register_ldisc(N_HCI, &n_hci_ldisc))) {
+- ERR("Can't register HCI line discipline (%d)", err);
+- return err;
+- }
+-
+- return 0;
+-}
+-
+-void n_hci_cleanup(void)
+-{
+- int err;
+-
+- /* Release tty registration of line discipline */
+- if ((err = tty_register_ldisc(N_HCI, NULL)))
+- ERR("Can't unregister HCI line discipline (%d)", err);
+-}
+-
+-module_init(n_hci_init);
+-module_exit(n_hci_cleanup);
+-
+-MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>");
+-MODULE_DESCRIPTION("BlueZ HCI UART driver ver " VERSION);
+-MODULE_LICENSE("GPL");
+diff -urN linux-2.4.18/drivers/bluetooth/hci_uart.h linux-2.4.18-mh15/drivers/bluetooth/hci_uart.h
+--- linux-2.4.18/drivers/bluetooth/hci_uart.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/drivers/bluetooth/hci_uart.h 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,82 @@
++/*
++ BlueZ - Bluetooth protocol stack for Linux
++ Copyright (C) 2000-2001 Qualcomm Incorporated
++
++ Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License version 2 as
++ published by the Free Software Foundation;
++
++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++
++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
++ SOFTWARE IS DISCLAIMED.
++*/
++
++/*
++ * $Id$
++ */
++
++#ifndef N_HCI
++#define N_HCI 15
++#endif
++
++/* Ioctls */
++#define HCIUARTSETPROTO _IOW('U', 200, int)
++#define HCIUARTGETPROTO _IOR('U', 201, int)
++
++/* UART protocols */
++#define HCI_UART_MAX_PROTO 4
++
++#define HCI_UART_H4 0
++#define HCI_UART_BCSP 1
++#define HCI_UART_3WIRE 2
++#define HCI_UART_H4DS 3
++
++#ifdef __KERNEL__
++struct hci_uart;
++
++struct hci_uart_proto {
++ unsigned int id;
++ int (*open)(struct hci_uart *hu);
++ int (*close)(struct hci_uart *hu);
++ int (*flush)(struct hci_uart *hu);
++ int (*recv)(struct hci_uart *hu, void *data, int len);
++ int (*enqueue)(struct hci_uart *hu, struct sk_buff *skb);
++ struct sk_buff *(*dequeue)(struct hci_uart *hu);
++};
++
++struct hci_uart {
++ struct tty_struct *tty;
++ struct hci_dev hdev;
++ unsigned long flags;
++
++ struct hci_uart_proto *proto;
++ void *priv;
++
++ struct sk_buff *tx_skb;
++ unsigned long tx_state;
++ spinlock_t rx_lock;
++};
++
++/* HCI_UART flag bits */
++#define HCI_UART_PROTO_SET 0
++
++/* TX states */
++#define HCI_UART_SENDING 1
++#define HCI_UART_TX_WAKEUP 2
++
++int hci_uart_register_proto(struct hci_uart_proto *p);
++int hci_uart_unregister_proto(struct hci_uart_proto *p);
++int hci_uart_tx_wakeup(struct hci_uart *hu);
++
++#endif /* __KERNEL__ */
+diff -urN linux-2.4.18/drivers/bluetooth/hci_usb.c linux-2.4.18-mh15/drivers/bluetooth/hci_usb.c
+--- linux-2.4.18/drivers/bluetooth/hci_usb.c 2001-09-07 18:28:38.000000000 +0200
++++ linux-2.4.18-mh15/drivers/bluetooth/hci_usb.c 2004-08-01 16:26:23.000000000 +0200
+@@ -1,9 +1,10 @@
+ /*
+- BlueZ - Bluetooth protocol stack for Linux
++ HCI USB driver for Linux Bluetooth protocol stack (BlueZ)
+ Copyright (C) 2000-2001 Qualcomm Incorporated
+-
+ Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
+
++ Copyright (C) 2003 Maxim Krasnyansky <maxk@qualcomm.com>
++
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License version 2 as
+ published by the Free Software Foundation;
+@@ -23,598 +24,938 @@
+ */
+
+ /*
+- * BlueZ HCI USB driver.
+ * Based on original USB Bluetooth driver for Linux kernel
+ * Copyright (c) 2000 Greg Kroah-Hartman <greg@kroah.com>
+ * Copyright (c) 2000 Mark Douglas Corner <mcorner@umich.edu>
+ *
+- * $Id$
++ * $Id$
+ */
+-#define VERSION "1.0"
++#define VERSION "2.7"
+
+ #include <linux/config.h>
+ #include <linux/module.h>
+
+ #include <linux/version.h>
+-#include <linux/config.h>
+ #include <linux/kernel.h>
+ #include <linux/init.h>
+ #include <linux/sched.h>
++#include <linux/unistd.h>
+ #include <linux/types.h>
+-#include <linux/fcntl.h>
+ #include <linux/interrupt.h>
+-#include <linux/ptrace.h>
+-#include <linux/poll.h>
+
+ #include <linux/slab.h>
+-#include <linux/tty.h>
+ #include <linux/errno.h>
+ #include <linux/string.h>
+-#include <linux/signal.h>
+-#include <linux/ioctl.h>
+ #include <linux/skbuff.h>
+
+ #include <linux/usb.h>
+
+ #include <net/bluetooth/bluetooth.h>
+-#include <net/bluetooth/bluez.h>
+ #include <net/bluetooth/hci_core.h>
+-#include <net/bluetooth/hci_usb.h>
++
++#include "hci_usb.h"
+
+ #ifndef HCI_USB_DEBUG
+-#undef DBG
+-#define DBG( A... )
+-#undef DMP
+-#define DMP( A... )
++#undef BT_DBG
++#define BT_DBG( A... )
++#undef BT_DMP
++#define BT_DMP( A... )
+ #endif
+
+-static struct usb_device_id usb_bluetooth_ids [] = {
++#ifndef CONFIG_BLUEZ_HCIUSB_ZERO_PACKET
++#undef USB_ZERO_PACKET
++#define USB_ZERO_PACKET 0
++#endif
++
++static struct usb_driver hci_usb_driver;
++
++static struct usb_device_id bluetooth_ids[] = {
++ /* Generic Bluetooth USB device */
+ { USB_DEVICE_INFO(HCI_DEV_CLASS, HCI_DEV_SUBCLASS, HCI_DEV_PROTOCOL) },
++
++ /* AVM BlueFRITZ! USB v2.0 */
++ { USB_DEVICE(0x057c, 0x3800) },
++
++ /* Bluetooth Ultraport Module from IBM */
++ { USB_DEVICE(0x04bf, 0x030a) },
++
++ /* ALPS Modules with non-standard id */
++ { USB_DEVICE(0x044e, 0x3001) },
++ { USB_DEVICE(0x044e, 0x3002) },
++
++ /* Ericsson with non-standard id */
++ { USB_DEVICE(0x0bdb, 0x1002) },
++
+ { } /* Terminating entry */
+ };
+
+-MODULE_DEVICE_TABLE (usb, usb_bluetooth_ids);
++MODULE_DEVICE_TABLE (usb, bluetooth_ids);
+
+-static int hci_usb_ctrl_msg(struct hci_usb *husb, struct sk_buff *skb);
+-static int hci_usb_write_msg(struct hci_usb *husb, struct sk_buff *skb);
++static struct usb_device_id blacklist_ids[] = {
++ /* Broadcom BCM2033 without firmware */
++ { USB_DEVICE(0x0a5c, 0x2033), driver_info: HCI_IGNORE },
+
+-static void hci_usb_unlink_urbs(struct hci_usb *husb)
++ /* Broadcom BCM2035 */
++ { USB_DEVICE(0x0a5c, 0x200a), driver_info: HCI_RESET },
++
++ /* ISSC Bluetooth Adapter v3.1 */
++ { USB_DEVICE(0x1131, 0x1001), driver_info: HCI_RESET },
++
++ /* Digianswer device */
++ { USB_DEVICE(0x08fd, 0x0001), driver_info: HCI_DIGIANSWER },
++
++ /* RTX Telecom based adapter with buggy SCO support */
++ { USB_DEVICE(0x0400, 0x0807), driver_info: HCI_BROKEN_ISOC },
++
++ { } /* Terminating entry */
++};
++
++struct _urb *_urb_alloc(int isoc, int gfp)
+ {
+- usb_unlink_urb(husb->read_urb);
+- usb_unlink_urb(husb->intr_urb);
+- usb_unlink_urb(husb->ctrl_urb);
+- usb_unlink_urb(husb->write_urb);
++ struct _urb *_urb = kmalloc(sizeof(struct _urb) +
++ sizeof(iso_packet_descriptor_t) * isoc, gfp);
++ if (_urb) {
++ memset(_urb, 0, sizeof(*_urb));
++ spin_lock_init(&_urb->urb.lock);
++ }
++ return _urb;
++}
++
++struct _urb *_urb_dequeue(struct _urb_queue *q)
++{
++ struct _urb *_urb = NULL;
++ unsigned long flags;
++ spin_lock_irqsave(&q->lock, flags);
++ {
++ struct list_head *head = &q->head;
++ struct list_head *next = head->next;
++ if (next != head) {
++ _urb = list_entry(next, struct _urb, list);
++ list_del(next); _urb->queue = NULL;
++ }
++ }
++ spin_unlock_irqrestore(&q->lock, flags);
++ return _urb;
+ }
+
+-static void hci_usb_free_bufs(struct hci_usb *husb)
++static void hci_usb_rx_complete(struct urb *urb);
++static void hci_usb_tx_complete(struct urb *urb);
++
++#define __pending_tx(husb, type) (&husb->pending_tx[type-1])
++#define __pending_q(husb, type) (&husb->pending_q[type-1])
++#define __completed_q(husb, type) (&husb->completed_q[type-1])
++#define __transmit_q(husb, type) (&husb->transmit_q[type-1])
++#define __reassembly(husb, type) (husb->reassembly[type-1])
++
++static inline struct _urb *__get_completed(struct hci_usb *husb, int type)
+ {
+- if (husb->read_urb) {
+- if (husb->read_urb->transfer_buffer)
+- kfree(husb->read_urb->transfer_buffer);
+- usb_free_urb(husb->read_urb);
+- }
++ return _urb_dequeue(__completed_q(husb, type));
++}
+
+- if (husb->intr_urb) {
+- if (husb->intr_urb->transfer_buffer)
+- kfree(husb->intr_urb->transfer_buffer);
+- usb_free_urb(husb->intr_urb);
++#ifdef CONFIG_BLUEZ_HCIUSB_SCO
++static void __fill_isoc_desc(struct urb *urb, int len, int mtu)
++{
++ int offset = 0, i;
++
++ BT_DBG("len %d mtu %d", len, mtu);
++
++ for (i=0; i < HCI_MAX_ISOC_FRAMES && len >= mtu; i++, offset += mtu, len -= mtu) {
++ urb->iso_frame_desc[i].offset = offset;
++ urb->iso_frame_desc[i].length = mtu;
++ BT_DBG("desc %d offset %d len %d", i, offset, mtu);
++ }
++ if (len && i < HCI_MAX_ISOC_FRAMES) {
++ urb->iso_frame_desc[i].offset = offset;
++ urb->iso_frame_desc[i].length = len;
++ BT_DBG("desc %d offset %d len %d", i, offset, len);
++ i++;
+ }
++ urb->number_of_packets = i;
++}
++#endif
+
+- if (husb->ctrl_urb)
+- usb_free_urb(husb->ctrl_urb);
++static int hci_usb_intr_rx_submit(struct hci_usb *husb)
++{
++ struct _urb *_urb;
++ struct urb *urb;
++ int err, pipe, interval, size;
++ void *buf;
++
++ BT_DBG("%s", husb->hdev.name);
++
++ size = husb->intr_in_ep->wMaxPacketSize;
++
++ buf = kmalloc(size, GFP_ATOMIC);
++ if (!buf)
++ return -ENOMEM;
++
++ _urb = _urb_alloc(0, GFP_ATOMIC);
++ if (!_urb) {
++ kfree(buf);
++ return -ENOMEM;
++ }
++ _urb->type = HCI_EVENT_PKT;
++ _urb_queue_tail(__pending_q(husb, _urb->type), _urb);
++
++ urb = &_urb->urb;
++ pipe = usb_rcvintpipe(husb->udev, husb->intr_in_ep->bEndpointAddress);
++ interval = husb->intr_in_ep->bInterval;
++ FILL_INT_URB(urb, husb->udev, pipe, buf, size, hci_usb_rx_complete, husb, interval);
++
++ err = usb_submit_urb(urb);
++ if (err) {
++ BT_ERR("%s intr rx submit failed urb %p err %d",
++ husb->hdev.name, urb, err);
++ _urb_unlink(_urb);
++ _urb_free(_urb);
++ kfree(buf);
++ }
++ return err;
++}
+
+- if (husb->write_urb)
+- usb_free_urb(husb->write_urb);
++static int hci_usb_bulk_rx_submit(struct hci_usb *husb)
++{
++ struct _urb *_urb;
++ struct urb *urb;
++ int err, pipe, size = HCI_MAX_FRAME_SIZE;
++ void *buf;
++
++ buf = kmalloc(size, GFP_ATOMIC);
++ if (!buf)
++ return -ENOMEM;
++
++ _urb = _urb_alloc(0, GFP_ATOMIC);
++ if (!_urb) {
++ kfree(buf);
++ return -ENOMEM;
++ }
++ _urb->type = HCI_ACLDATA_PKT;
++ _urb_queue_tail(__pending_q(husb, _urb->type), _urb);
++
++ urb = &_urb->urb;
++ pipe = usb_rcvbulkpipe(husb->udev, husb->bulk_in_ep->bEndpointAddress);
++ FILL_BULK_URB(urb, husb->udev, pipe, buf, size, hci_usb_rx_complete, husb);
++ urb->transfer_flags = USB_QUEUE_BULK;
++
++ BT_DBG("%s urb %p", husb->hdev.name, urb);
++
++ err = usb_submit_urb(urb);
++ if (err) {
++ BT_ERR("%s bulk rx submit failed urb %p err %d",
++ husb->hdev.name, urb, err);
++ _urb_unlink(_urb);
++ _urb_free(_urb);
++ kfree(buf);
++ }
++ return err;
++}
+
+- if (husb->intr_skb)
+- kfree_skb(husb->intr_skb);
++#ifdef CONFIG_BLUEZ_HCIUSB_SCO
++static int hci_usb_isoc_rx_submit(struct hci_usb *husb)
++{
++ struct _urb *_urb;
++ struct urb *urb;
++ int err, mtu, size;
++ void *buf;
++
++ mtu = husb->isoc_in_ep->wMaxPacketSize;
++ size = mtu * HCI_MAX_ISOC_FRAMES;
++
++ buf = kmalloc(size, GFP_ATOMIC);
++ if (!buf)
++ return -ENOMEM;
++
++ _urb = _urb_alloc(HCI_MAX_ISOC_FRAMES, GFP_ATOMIC);
++ if (!_urb) {
++ kfree(buf);
++ return -ENOMEM;
++ }
++ _urb->type = HCI_SCODATA_PKT;
++ _urb_queue_tail(__pending_q(husb, _urb->type), _urb);
++
++ urb = &_urb->urb;
++
++ urb->context = husb;
++ urb->dev = husb->udev;
++ urb->pipe = usb_rcvisocpipe(husb->udev, husb->isoc_in_ep->bEndpointAddress);
++ urb->complete = hci_usb_rx_complete;
++
++ urb->transfer_buffer_length = size;
++ urb->transfer_buffer = buf;
++ urb->transfer_flags = USB_ISO_ASAP;
++
++ __fill_isoc_desc(urb, size, mtu);
++
++ BT_DBG("%s urb %p", husb->hdev.name, urb);
++
++ err = usb_submit_urb(urb);
++ if (err) {
++ BT_ERR("%s isoc rx submit failed urb %p err %d",
++ husb->hdev.name, urb, err);
++ _urb_unlink(_urb);
++ _urb_free(_urb);
++ kfree(buf);
++ }
++ return err;
+ }
++#endif
+
+-/* ------- Interface to HCI layer ------ */
+ /* Initialize device */
+-int hci_usb_open(struct hci_dev *hdev)
++static int hci_usb_open(struct hci_dev *hdev)
+ {
+ struct hci_usb *husb = (struct hci_usb *) hdev->driver_data;
+- int status;
++ int i, err;
++ unsigned long flags;
++
++ BT_DBG("%s", hdev->name);
+
+- DBG("%s", hdev->name);
++ if (test_and_set_bit(HCI_RUNNING, &hdev->flags))
++ return 0;
+
+- husb->read_urb->dev = husb->udev;
+- if ((status = usb_submit_urb(husb->read_urb)))
+- DBG("read submit failed. %d", status);
++ MOD_INC_USE_COUNT;
+
+- husb->intr_urb->dev = husb->udev;
+- if ((status = usb_submit_urb(husb->intr_urb)))
+- DBG("interrupt submit failed. %d", status);
++ write_lock_irqsave(&husb->completion_lock, flags);
+
+- hdev->flags |= HCI_RUNNING;
++ err = hci_usb_intr_rx_submit(husb);
++ if (!err) {
++ for (i = 0; i < HCI_MAX_BULK_RX; i++)
++ hci_usb_bulk_rx_submit(husb);
++
++#ifdef CONFIG_BLUEZ_HCIUSB_SCO
++ if (husb->isoc_iface)
++ for (i = 0; i < HCI_MAX_ISOC_RX; i++)
++ hci_usb_isoc_rx_submit(husb);
++#endif
++ } else {
++ clear_bit(HCI_RUNNING, &hdev->flags);
++ MOD_DEC_USE_COUNT;
++ }
+
+- return 0;
++ write_unlock_irqrestore(&husb->completion_lock, flags);
++ return err;
+ }
+
+ /* Reset device */
+-int hci_usb_flush(struct hci_dev *hdev)
++static int hci_usb_flush(struct hci_dev *hdev)
+ {
+ struct hci_usb *husb = (struct hci_usb *) hdev->driver_data;
++ int i;
+
+- DBG("%s", hdev->name);
+-
+- /* Drop TX queues */
+- skb_queue_purge(&husb->tx_ctrl_q);
+- skb_queue_purge(&husb->tx_write_q);
++ BT_DBG("%s", hdev->name);
+
++ for (i=0; i < 4; i++)
++ skb_queue_purge(&husb->transmit_q[i]);
+ return 0;
+ }
+
+-/* Close device */
+-int hci_usb_close(struct hci_dev *hdev)
++static void hci_usb_unlink_urbs(struct hci_usb *husb)
+ {
+- struct hci_usb *husb = (struct hci_usb *) hdev->driver_data;
++ int i;
+
+- DBG("%s", hdev->name);
++ BT_DBG("%s", husb->hdev.name);
+
+- hdev->flags &= ~HCI_RUNNING;
+- hci_usb_unlink_urbs(husb);
++ for (i=0; i < 4; i++) {
++ struct _urb *_urb;
++ struct urb *urb;
++
++ /* Kill pending requests */
++ while ((_urb = _urb_dequeue(&husb->pending_q[i]))) {
++ urb = &_urb->urb;
++ BT_DBG("%s unlinking _urb %p type %d urb %p",
++ husb->hdev.name, _urb, _urb->type, urb);
++ usb_unlink_urb(urb);
++ _urb_queue_tail(__completed_q(husb, _urb->type), _urb);
++ }
+
+- hci_usb_flush(hdev);
++ /* Release completed requests */
++ while ((_urb = _urb_dequeue(&husb->completed_q[i]))) {
++ urb = &_urb->urb;
++ BT_DBG("%s freeing _urb %p type %d urb %p",
++ husb->hdev.name, _urb, _urb->type, urb);
++ if (urb->setup_packet)
++ kfree(urb->setup_packet);
++ if (urb->transfer_buffer)
++ kfree(urb->transfer_buffer);
++ _urb_free(_urb);
++ }
+
+- return 0;
++ /* Release reassembly buffers */
++ if (husb->reassembly[i]) {
++ kfree_skb(husb->reassembly[i]);
++ husb->reassembly[i] = NULL;
++ }
++ }
+ }
+
+-void hci_usb_ctrl_wakeup(struct hci_usb *husb)
++/* Close device */
++static int hci_usb_close(struct hci_dev *hdev)
+ {
+- struct sk_buff *skb;
++ struct hci_usb *husb = (struct hci_usb *) hdev->driver_data;
++ unsigned long flags;
++
++ if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
++ return 0;
+
+- if (test_and_set_bit(HCI_TX_CTRL, &husb->tx_state))
+- return;
++ BT_DBG("%s", hdev->name);
+
+- DBG("%s", husb->hdev.name);
++ write_lock_irqsave(&husb->completion_lock, flags);
++
++ hci_usb_unlink_urbs(husb);
++ hci_usb_flush(hdev);
+
+- if (!(skb = skb_dequeue(&husb->tx_ctrl_q)))
+- goto done;
++ write_unlock_irqrestore(&husb->completion_lock, flags);
+
+- if (hci_usb_ctrl_msg(husb, skb)){
+- kfree_skb(skb);
+- goto done;
+- }
++ MOD_DEC_USE_COUNT;
++ return 0;
++}
+
+- DMP(skb->data, skb->len);
++static int __tx_submit(struct hci_usb *husb, struct _urb *_urb)
++{
++ struct urb *urb = &_urb->urb;
++ int err;
+
+- husb->hdev.stat.byte_tx += skb->len;
+- return;
++ BT_DBG("%s urb %p type %d", husb->hdev.name, urb, _urb->type);
++
++ _urb_queue_tail(__pending_q(husb, _urb->type), _urb);
++ err = usb_submit_urb(urb);
++ if (err) {
++ BT_ERR("%s tx submit failed urb %p type %d err %d",
++ husb->hdev.name, urb, _urb->type, err);
++ _urb_unlink(_urb);
++ _urb_queue_tail(__completed_q(husb, _urb->type), _urb);
++ } else
++ atomic_inc(__pending_tx(husb, _urb->type));
+
+-done:
+- clear_bit(HCI_TX_CTRL, &husb->tx_state);
+- return;
++ return err;
+ }
+
+-void hci_usb_write_wakeup(struct hci_usb *husb)
++static inline int hci_usb_send_ctrl(struct hci_usb *husb, struct sk_buff *skb)
+ {
+- struct sk_buff *skb;
++ struct _urb *_urb = __get_completed(husb, skb->pkt_type);
++ devrequest *dr;
++ struct urb *urb;
++
++ if (!_urb) {
++ _urb = _urb_alloc(0, GFP_ATOMIC);
++ if (!_urb)
++ return -ENOMEM;
++ _urb->type = skb->pkt_type;
++
++ dr = kmalloc(sizeof(*dr), GFP_ATOMIC);
++ if (!dr) {
++ _urb_free(_urb);
++ return -ENOMEM;
++ }
++ } else
++ dr = (void *) _urb->urb.setup_packet;
+
+- if (test_and_set_bit(HCI_TX_WRITE, &husb->tx_state))
+- return;
++ dr->requesttype = husb->ctrl_req;
++ dr->request = 0;
++ dr->index = 0;
++ dr->value = 0;
++ dr->length = __cpu_to_le16(skb->len);
+
+- DBG("%s", husb->hdev.name);
++ urb = &_urb->urb;
++ FILL_CONTROL_URB(urb, husb->udev, usb_sndctrlpipe(husb->udev, 0),
++ (void *) dr, skb->data, skb->len, hci_usb_tx_complete, husb);
+
+- if (!(skb = skb_dequeue(&husb->tx_write_q)))
+- goto done;
++ BT_DBG("%s skb %p len %d", husb->hdev.name, skb, skb->len);
++
++ _urb->priv = skb;
++ return __tx_submit(husb, _urb);
++}
+
+- if (hci_usb_write_msg(husb, skb)) {
+- skb_queue_head(&husb->tx_write_q, skb);
+- goto done;
++static inline int hci_usb_send_bulk(struct hci_usb *husb, struct sk_buff *skb)
++{
++ struct _urb *_urb = __get_completed(husb, skb->pkt_type);
++ struct urb *urb;
++ int pipe;
++
++ if (!_urb) {
++ _urb = _urb_alloc(0, GFP_ATOMIC);
++ if (!_urb)
++ return -ENOMEM;
++ _urb->type = skb->pkt_type;
+ }
+
+- DMP(skb->data, skb->len);
++ urb = &_urb->urb;
++ pipe = usb_sndbulkpipe(husb->udev, husb->bulk_out_ep->bEndpointAddress);
++ FILL_BULK_URB(urb, husb->udev, pipe, skb->data, skb->len,
++ hci_usb_tx_complete, husb);
++ urb->transfer_flags = USB_QUEUE_BULK | USB_ZERO_PACKET;
+
+- husb->hdev.stat.byte_tx += skb->len;
+- return;
++ BT_DBG("%s skb %p len %d", husb->hdev.name, skb, skb->len);
+
+-done:
+- clear_bit(HCI_TX_WRITE, &husb->tx_state);
+- return;
++ _urb->priv = skb;
++ return __tx_submit(husb, _urb);
+ }
+
+-/* Send frames from HCI layer */
+-int hci_usb_send_frame(struct sk_buff *skb)
++#ifdef CONFIG_BLUEZ_HCIUSB_SCO
++static inline int hci_usb_send_isoc(struct hci_usb *husb, struct sk_buff *skb)
+ {
+- struct hci_dev *hdev = (struct hci_dev *) skb->dev;
+- struct hci_usb *husb;
+-
+- if (!hdev) {
+- ERR("frame for uknown device (hdev=NULL)");
+- return -ENODEV;
++ struct _urb *_urb = __get_completed(husb, skb->pkt_type);
++ struct urb *urb;
++
++ if (!_urb) {
++ _urb = _urb_alloc(HCI_MAX_ISOC_FRAMES, GFP_ATOMIC);
++ if (!_urb)
++ return -ENOMEM;
++ _urb->type = skb->pkt_type;
+ }
+
+- if (!(hdev->flags & HCI_RUNNING))
+- return 0;
++ BT_DBG("%s skb %p len %d", husb->hdev.name, skb, skb->len);
+
+- husb = (struct hci_usb *) hdev->driver_data;
++ urb = &_urb->urb;
++
++ urb->context = husb;
++ urb->dev = husb->udev;
++ urb->pipe = usb_sndisocpipe(husb->udev, husb->isoc_out_ep->bEndpointAddress);
++ urb->complete = hci_usb_tx_complete;
++ urb->transfer_flags = USB_ISO_ASAP;
+
+- DBG("%s type %d len %d", hdev->name, skb->pkt_type, skb->len);
++ urb->transfer_buffer = skb->data;
++ urb->transfer_buffer_length = skb->len;
++
++ __fill_isoc_desc(urb, skb->len, husb->isoc_out_ep->wMaxPacketSize);
+
+- switch (skb->pkt_type) {
+- case HCI_COMMAND_PKT:
+- skb_queue_tail(&husb->tx_ctrl_q, skb);
+- hci_usb_ctrl_wakeup(husb);
+- hdev->stat.cmd_tx++;
+- return 0;
+-
+- case HCI_ACLDATA_PKT:
+- skb_queue_tail(&husb->tx_write_q, skb);
+- hci_usb_write_wakeup(husb);
+- hdev->stat.acl_tx++;
+- return 0;
+-
+- case HCI_SCODATA_PKT:
+- return -EOPNOTSUPP;
+- };
+-
+- return 0;
++ _urb->priv = skb;
++ return __tx_submit(husb, _urb);
+ }
++#endif
+
+-/* ---------- USB ------------- */
+-
+-static void hci_usb_ctrl(struct urb *urb)
++static void hci_usb_tx_process(struct hci_usb *husb)
+ {
+- struct sk_buff *skb = (struct sk_buff *) urb->context;
+- struct hci_dev *hdev;
+- struct hci_usb *husb;
++ struct sk_buff_head *q;
++ struct sk_buff *skb;
+
+- if (!skb)
+- return;
+- hdev = (struct hci_dev *) skb->dev;
+- husb = (struct hci_usb *) hdev->driver_data;
++ BT_DBG("%s", husb->hdev.name);
+
+- DBG("%s", hdev->name);
++ do {
++ clear_bit(HCI_USB_TX_WAKEUP, &husb->state);
+
+- if (urb->status)
+- DBG("%s ctrl status: %d", hdev->name, urb->status);
++ /* Process command queue */
++ q = __transmit_q(husb, HCI_COMMAND_PKT);
++ if (!atomic_read(__pending_tx(husb, HCI_COMMAND_PKT)) &&
++ (skb = skb_dequeue(q))) {
++ if (hci_usb_send_ctrl(husb, skb) < 0)
++ skb_queue_head(q, skb);
++ }
+
+- clear_bit(HCI_TX_CTRL, &husb->tx_state);
+- kfree_skb(skb);
++#ifdef CONFIG_BLUEZ_HCIUSB_SCO
++ /* Process SCO queue */
++ q = __transmit_q(husb, HCI_SCODATA_PKT);
++ if (atomic_read(__pending_tx(husb, HCI_SCODATA_PKT)) < HCI_MAX_ISOC_TX &&
++ (skb = skb_dequeue(q))) {
++ if (hci_usb_send_isoc(husb, skb) < 0)
++ skb_queue_head(q, skb);
++ }
++#endif
++
++ /* Process ACL queue */
++ q = __transmit_q(husb, HCI_ACLDATA_PKT);
++ while (atomic_read(__pending_tx(husb, HCI_ACLDATA_PKT)) < HCI_MAX_BULK_TX &&
++ (skb = skb_dequeue(q))) {
++ if (hci_usb_send_bulk(husb, skb) < 0) {
++ skb_queue_head(q, skb);
++ break;
++ }
++ }
++ } while(test_bit(HCI_USB_TX_WAKEUP, &husb->state));
++}
+
+- /* Wake up device */
+- hci_usb_ctrl_wakeup(husb);
++static inline void hci_usb_tx_wakeup(struct hci_usb *husb)
++{
++ /* Serialize TX queue processing to avoid data reordering */
++ if (!test_and_set_bit(HCI_USB_TX_PROCESS, &husb->state)) {
++ hci_usb_tx_process(husb);
++ clear_bit(HCI_USB_TX_PROCESS, &husb->state);
++ } else
++ set_bit(HCI_USB_TX_WAKEUP, &husb->state);
+ }
+
+-static void hci_usb_bulk_write(struct urb *urb)
++/* Send frames from HCI layer */
++static int hci_usb_send_frame(struct sk_buff *skb)
+ {
+- struct sk_buff *skb = (struct sk_buff *) urb->context;
+- struct hci_dev *hdev;
++ struct hci_dev *hdev = (struct hci_dev *) skb->dev;
+ struct hci_usb *husb;
+
+- if (!skb)
+- return;
+- hdev = (struct hci_dev *) skb->dev;
+- husb = (struct hci_usb *) hdev->driver_data;
++ if (!hdev) {
++ BT_ERR("frame for uknown device (hdev=NULL)");
++ return -ENODEV;
++ }
+
+- DBG("%s", hdev->name);
++ if (!test_bit(HCI_RUNNING, &hdev->flags))
++ return -EBUSY;
+
+- if (urb->status)
+- DBG("%s bulk write status: %d", hdev->name, urb->status);
++ BT_DBG("%s type %d len %d", hdev->name, skb->pkt_type, skb->len);
+
+- clear_bit(HCI_TX_WRITE, &husb->tx_state);
+- kfree_skb(skb);
++ husb = (struct hci_usb *) hdev->driver_data;
+
+- /* Wake up device */
+- hci_usb_write_wakeup(husb);
++ switch (skb->pkt_type) {
++ case HCI_COMMAND_PKT:
++ hdev->stat.cmd_tx++;
++ break;
++
++ case HCI_ACLDATA_PKT:
++ hdev->stat.acl_tx++;
++ break;
++
++#ifdef CONFIG_BLUEZ_HCIUSB_SCO
++ case HCI_SCODATA_PKT:
++ hdev->stat.sco_tx++;
++ break;
++#endif
+
+- return;
+-}
++ default:
++ kfree_skb(skb);
++ return 0;
++ }
+
+-static void hci_usb_intr(struct urb *urb)
+-{
+- struct hci_usb *husb = (struct hci_usb *) urb->context;
+- unsigned char *data = urb->transfer_buffer;
+- register int count = urb->actual_length;
+- register struct sk_buff *skb = husb->intr_skb;
+- hci_event_hdr *eh;
+- register int len;
++ read_lock(&husb->completion_lock);
+
+- if (!husb)
+- return;
++ skb_queue_tail(__transmit_q(husb, skb->pkt_type), skb);
++ hci_usb_tx_wakeup(husb);
+
+- DBG("%s count %d", husb->hdev.name, count);
++ read_unlock(&husb->completion_lock);
++ return 0;
++}
+
+- if (urb->status || !count) {
+- DBG("%s intr status %d, count %d", husb->hdev.name, urb->status, count);
+- return;
+- }
++static inline int __recv_frame(struct hci_usb *husb, int type, void *data, int count)
++{
++ BT_DBG("%s type %d data %p count %d", husb->hdev.name, type, data, count);
+
+- /* Do we really have to handle continuations here ? */
+- if (!skb) {
+- /* New frame */
+- if (count < HCI_EVENT_HDR_SIZE) {
+- DBG("%s bad frame len %d", husb->hdev.name, count);
+- return;
+- }
++ husb->hdev.stat.byte_rx += count;
+
+- eh = (hci_event_hdr *) data;
+- len = eh->plen + HCI_EVENT_HDR_SIZE;
++ while (count) {
++ struct sk_buff *skb = __reassembly(husb, type);
++ struct { int expect; } *scb;
++ int len = 0;
++
++ if (!skb) {
++ /* Start of the frame */
++
++ switch (type) {
++ case HCI_EVENT_PKT:
++ if (count >= HCI_EVENT_HDR_SIZE) {
++ hci_event_hdr *h = data;
++ len = HCI_EVENT_HDR_SIZE + h->plen;
++ } else
++ return -EILSEQ;
++ break;
+
+- if (count > len) {
+- DBG("%s corrupted frame, len %d", husb->hdev.name, count);
+- return;
+- }
++ case HCI_ACLDATA_PKT:
++ if (count >= HCI_ACL_HDR_SIZE) {
++ hci_acl_hdr *h = data;
++ len = HCI_ACL_HDR_SIZE + __le16_to_cpu(h->dlen);
++ } else
++ return -EILSEQ;
++ break;
++#ifdef CONFIG_BLUEZ_HCIUSB_SCO
++ case HCI_SCODATA_PKT:
++ if (count >= HCI_SCO_HDR_SIZE) {
++ hci_sco_hdr *h = data;
++ len = HCI_SCO_HDR_SIZE + h->dlen;
++ } else
++ return -EILSEQ;
++ break;
++#endif
++ }
++ BT_DBG("new packet len %d", len);
+
+- /* Allocate skb */
+- if (!(skb = bluez_skb_alloc(len, GFP_ATOMIC))) {
+- ERR("Can't allocate mem for new packet");
+- return;
++ skb = bluez_skb_alloc(len, GFP_ATOMIC);
++ if (!skb) {
++ BT_ERR("%s no memory for the packet", husb->hdev.name);
++ return -ENOMEM;
++ }
++ skb->dev = (void *) &husb->hdev;
++ skb->pkt_type = type;
++
++ __reassembly(husb, type) = skb;
++
++ scb = (void *) skb->cb;
++ scb->expect = len;
++ } else {
++ /* Continuation */
++ scb = (void *) skb->cb;
++ len = scb->expect;
+ }
+- skb->dev = (void *) &husb->hdev;
+- skb->pkt_type = HCI_EVENT_PKT;
+-
+- husb->intr_skb = skb;
+- husb->intr_count = len;
+- } else {
+- /* Continuation */
+- if (count > husb->intr_count) {
+- ERR("%s bad frame len %d (expected %d)", husb->hdev.name, count, husb->intr_count);
+
+- kfree_skb(skb);
+- husb->intr_skb = NULL;
+- husb->intr_count = 0;
+- return;
++ len = min(len, count);
++
++ memcpy(skb_put(skb, len), data, len);
++
++ scb->expect -= len;
++ if (!scb->expect) {
++ /* Complete frame */
++ __reassembly(husb, type) = NULL;
++ hci_recv_frame(skb);
+ }
+- }
+-
+- memcpy(skb_put(skb, count), data, count);
+- husb->intr_count -= count;
+-
+- DMP(data, count);
+-
+- if (!husb->intr_count) {
+- /* Got complete frame */
+
+- husb->hdev.stat.byte_rx += skb->len;
+- hci_recv_frame(skb);
+-
+- husb->intr_skb = NULL;
++ count -= len; data += len;
+ }
++ return 0;
+ }
+
+-static void hci_usb_bulk_read(struct urb *urb)
++static void hci_usb_rx_complete(struct urb *urb)
+ {
+- struct hci_usb *husb = (struct hci_usb *) urb->context;
+- unsigned char *data = urb->transfer_buffer;
+- int count = urb->actual_length, status;
+- struct sk_buff *skb;
+- hci_acl_hdr *ah;
+- register __u16 dlen;
+-
+- if (!husb)
+- return;
++ struct _urb *_urb = container_of(urb, struct _urb, urb);
++ struct hci_usb *husb = (void *) urb->context;
++ struct hci_dev *hdev = &husb->hdev;
++ int err, count = urb->actual_length;
+
+- DBG("%s status %d, count %d, flags %x", husb->hdev.name, urb->status, count, urb->transfer_flags);
++ BT_DBG("%s urb %p type %d status %d count %d flags %x", hdev->name, urb,
++ _urb->type, urb->status, count, urb->transfer_flags);
+
+- if (urb->status) {
+- /* Do not re-submit URB on critical errors */
+- switch (urb->status) {
+- case -ENOENT:
+- return;
+- default:
+- goto resubmit;
+- };
+- }
+- if (!count)
+- goto resubmit;
+-
+- DMP(data, count);
++ read_lock(&husb->completion_lock);
+
+- ah = (hci_acl_hdr *) data;
+- dlen = le16_to_cpu(ah->dlen);
++ if (!test_bit(HCI_RUNNING, &hdev->flags))
++ goto unlock;
+
+- /* Verify frame len and completeness */
+- if ((count - HCI_ACL_HDR_SIZE) != dlen) {
+- ERR("%s corrupted ACL packet: count %d, plen %d", husb->hdev.name, count, dlen);
++ if (urb->status || !count)
+ goto resubmit;
+- }
+
+- /* Allocate packet */
+- if (!(skb = bluez_skb_alloc(count, GFP_ATOMIC))) {
+- ERR("Can't allocate mem for new packet");
+- goto resubmit;
++ if (_urb->type == HCI_SCODATA_PKT) {
++#ifdef CONFIG_BLUEZ_HCIUSB_SCO
++ int i;
++ for (i=0; i < urb->number_of_packets; i++) {
++ BT_DBG("desc %d status %d offset %d len %d", i,
++ urb->iso_frame_desc[i].status,
++ urb->iso_frame_desc[i].offset,
++ urb->iso_frame_desc[i].actual_length);
++
++ if (!urb->iso_frame_desc[i].status)
++ __recv_frame(husb, _urb->type,
++ urb->transfer_buffer + urb->iso_frame_desc[i].offset,
++ urb->iso_frame_desc[i].actual_length);
++ }
++#else
++ ;
++#endif
++ } else {
++ err = __recv_frame(husb, _urb->type, urb->transfer_buffer, count);
++ if (err < 0) {
++ BT_ERR("%s corrupted packet: type %d count %d",
++ husb->hdev.name, _urb->type, count);
++ hdev->stat.err_rx++;
++ }
+ }
+
+- memcpy(skb_put(skb, count), data, count);
+- skb->dev = (void *) &husb->hdev;
+- skb->pkt_type = HCI_ACLDATA_PKT;
+-
+- husb->hdev.stat.byte_rx += skb->len;
+-
+- hci_recv_frame(skb);
+-
+ resubmit:
+- husb->read_urb->dev = husb->udev;
+- if ((status = usb_submit_urb(husb->read_urb)))
+- DBG("%s read URB submit failed %d", husb->hdev.name, status);
++ if (_urb->type != HCI_EVENT_PKT) {
++ urb->dev = husb->udev;
++ err = usb_submit_urb(urb);
++ BT_DBG("%s urb %p type %d resubmit status %d", hdev->name, urb,
++ _urb->type, err);
++ }
+
+- DBG("%s read URB re-submited", husb->hdev.name);
++unlock:
++ read_unlock(&husb->completion_lock);
+ }
+
+-static int hci_usb_ctrl_msg(struct hci_usb *husb, struct sk_buff *skb)
++static void hci_usb_tx_complete(struct urb *urb)
+ {
+- struct urb *urb = husb->ctrl_urb;
+- devrequest *dr = &husb->dev_req;
+- int pipe, status;
++ struct _urb *_urb = container_of(urb, struct _urb, urb);
++ struct hci_usb *husb = (void *) urb->context;
++ struct hci_dev *hdev = &husb->hdev;
+
+- DBG("%s len %d", husb->hdev.name, skb->len);
++ BT_DBG("%s urb %p status %d flags %x", hdev->name, urb,
++ urb->status, urb->transfer_flags);
+
+- pipe = usb_sndctrlpipe(husb->udev, 0);
++ atomic_dec(__pending_tx(husb, _urb->type));
+
+- dr->requesttype = HCI_CTRL_REQ;
+- dr->request = 0;
+- dr->index = 0;
+- dr->value = 0;
+- dr->length = cpu_to_le16(skb->len);
+-
+- FILL_CONTROL_URB(urb, husb->udev, pipe, (void*)dr, skb->data, skb->len,
+- hci_usb_ctrl, skb);
++ urb->transfer_buffer = NULL;
++ kfree_skb((struct sk_buff *) _urb->priv);
+
+- if ((status = usb_submit_urb(urb))) {
+- DBG("%s control URB submit failed %d", husb->hdev.name, status);
+- return status;
+- }
++ if (!test_bit(HCI_RUNNING, &hdev->flags))
++ return;
+
+- return 0;
+-}
++ if (!urb->status)
++ hdev->stat.byte_tx += urb->transfer_buffer_length;
++ else
++ hdev->stat.err_tx++;
+
+-static int hci_usb_write_msg(struct hci_usb *husb, struct sk_buff *skb)
+-{
+- struct urb *urb = husb->write_urb;
+- int pipe, status;
++ read_lock(&husb->completion_lock);
+
+- DBG("%s len %d", husb->hdev.name, skb->len);
++ _urb_unlink(_urb);
++ _urb_queue_tail(__completed_q(husb, _urb->type), _urb);
+
+- pipe = usb_sndbulkpipe(husb->udev, husb->bulk_out_ep_addr);
++ hci_usb_tx_wakeup(husb);
++
++ read_unlock(&husb->completion_lock);
++}
+
+- FILL_BULK_URB(urb, husb->udev, pipe, skb->data, skb->len,
+- hci_usb_bulk_write, skb);
+- urb->transfer_flags |= USB_QUEUE_BULK;
++static void hci_usb_destruct(struct hci_dev *hdev)
++{
++ struct hci_usb *husb = (struct hci_usb *) hdev->driver_data;
+
+- if ((status = usb_submit_urb(urb))) {
+- DBG("%s write URB submit failed %d", husb->hdev.name, status);
+- return status;
+- }
++ BT_DBG("%s", hdev->name);
+
+- return 0;
++ kfree(husb);
+ }
+
+-static void * hci_usb_probe(struct usb_device *udev, unsigned int ifnum, const struct usb_device_id *id)
++static void *hci_usb_probe(struct usb_device *udev, unsigned int ifnum, const struct usb_device_id *id)
+ {
+- struct usb_endpoint_descriptor *bulk_out_ep, *intr_in_ep, *bulk_in_ep;
++ struct usb_endpoint_descriptor *bulk_out_ep[HCI_MAX_IFACE_NUM];
++ struct usb_endpoint_descriptor *isoc_out_ep[HCI_MAX_IFACE_NUM];
++ struct usb_endpoint_descriptor *bulk_in_ep[HCI_MAX_IFACE_NUM];
++ struct usb_endpoint_descriptor *isoc_in_ep[HCI_MAX_IFACE_NUM];
++ struct usb_endpoint_descriptor *intr_in_ep[HCI_MAX_IFACE_NUM];
+ struct usb_interface_descriptor *uif;
+ struct usb_endpoint_descriptor *ep;
++ struct usb_interface *iface, *isoc_iface;
+ struct hci_usb *husb;
+ struct hci_dev *hdev;
+- int i, size, pipe;
+- __u8 * buf;
++ int i, a, e, size, ifn, isoc_ifnum, isoc_alts;
+
+- DBG("udev %p ifnum %d", udev, ifnum);
++ BT_DBG("udev %p ifnum %d", udev, ifnum);
+
+- /* Check device signature */
+- if ((udev->descriptor.bDeviceClass != HCI_DEV_CLASS) ||
+- (udev->descriptor.bDeviceSubClass != HCI_DEV_SUBCLASS)||
+- (udev->descriptor.bDeviceProtocol != HCI_DEV_PROTOCOL) )
+- return NULL;
++ iface = &udev->actconfig->interface[0];
+
+- MOD_INC_USE_COUNT;
+-
+- uif = &udev->actconfig->interface[ifnum].altsetting[0];
++ if (!id->driver_info) {
++ const struct usb_device_id *match;
++ match = usb_match_id(udev, iface, blacklist_ids);
++ if (match)
++ id = match;
++ }
+
+- if (uif->bNumEndpoints != 3) {
+- DBG("Wrong number of endpoints %d", uif->bNumEndpoints);
+- MOD_DEC_USE_COUNT;
++ if (id->driver_info & HCI_IGNORE)
+ return NULL;
+- }
+
+- bulk_out_ep = intr_in_ep = bulk_in_ep = NULL;
++ /* Check number of endpoints */
++ if (udev->actconfig->interface[ifnum].altsetting[0].bNumEndpoints < 3)
++ return NULL;
+
++ memset(bulk_out_ep, 0, sizeof(bulk_out_ep));
++ memset(isoc_out_ep, 0, sizeof(isoc_out_ep));
++ memset(bulk_in_ep, 0, sizeof(bulk_in_ep));
++ memset(isoc_in_ep, 0, sizeof(isoc_in_ep));
++ memset(intr_in_ep, 0, sizeof(intr_in_ep));
++
++ size = 0;
++ isoc_iface = NULL;
++ isoc_alts = isoc_ifnum = 0;
++
+ /* Find endpoints that we need */
+- for ( i = 0; i < uif->bNumEndpoints; ++i) {
+- ep = &uif->endpoint[i];
+
+- switch (ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
+- case USB_ENDPOINT_XFER_BULK:
+- if (ep->bEndpointAddress & USB_DIR_IN)
+- bulk_in_ep = ep;
+- else
+- bulk_out_ep = ep;
+- break;
++ ifn = MIN(udev->actconfig->bNumInterfaces, HCI_MAX_IFACE_NUM);
++ for (i = 0; i < ifn; i++) {
++ iface = &udev->actconfig->interface[i];
++ for (a = 0; a < iface->num_altsetting; a++) {
++ uif = &iface->altsetting[a];
++ for (e = 0; e < uif->bNumEndpoints; e++) {
++ ep = &uif->endpoint[e];
++
++ switch (ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
++ case USB_ENDPOINT_XFER_INT:
++ if (ep->bEndpointAddress & USB_DIR_IN)
++ intr_in_ep[i] = ep;
++ break;
++
++ case USB_ENDPOINT_XFER_BULK:
++ if (ep->bEndpointAddress & USB_DIR_IN)
++ bulk_in_ep[i] = ep;
++ else
++ bulk_out_ep[i] = ep;
++ break;
++
++#ifdef CONFIG_BLUEZ_HCIUSB_SCO
++ case USB_ENDPOINT_XFER_ISOC:
++ if (ep->wMaxPacketSize < size || a > 2)
++ break;
++ size = ep->wMaxPacketSize;
++
++ isoc_iface = iface;
++ isoc_alts = a;
++ isoc_ifnum = i;
++
++ if (ep->bEndpointAddress & USB_DIR_IN)
++ isoc_in_ep[i] = ep;
++ else
++ isoc_out_ep[i] = ep;
++ break;
++#endif
++ }
++ }
++ }
++ }
+
+- case USB_ENDPOINT_XFER_INT:
+- intr_in_ep = ep;
+- break;
+- };
++ if (!bulk_in_ep[0] || !bulk_out_ep[0] || !intr_in_ep[0]) {
++ BT_DBG("Bulk endpoints not found");
++ goto done;
+ }
+
+- if (!bulk_in_ep || !bulk_out_ep || !intr_in_ep) {
+- DBG("Endpoints not found: %p %p %p", bulk_in_ep, bulk_out_ep, intr_in_ep);
+- MOD_DEC_USE_COUNT;
+- return NULL;
++#ifdef CONFIG_BLUEZ_HCIUSB_SCO
++ if (id->driver_info & HCI_BROKEN_ISOC || !isoc_in_ep[1] || !isoc_out_ep[1]) {
++ BT_DBG("Isoc endpoints not found");
++ isoc_iface = NULL;
+ }
++#endif
+
+ if (!(husb = kmalloc(sizeof(struct hci_usb), GFP_KERNEL))) {
+- ERR("Can't allocate: control structure");
+- MOD_DEC_USE_COUNT;
+- return NULL;
++ BT_ERR("Can't allocate: control structure");
++ goto done;
+ }
+
+ memset(husb, 0, sizeof(struct hci_usb));
+
+ husb->udev = udev;
+- husb->bulk_out_ep_addr = bulk_out_ep->bEndpointAddress;
+-
+- if (!(husb->ctrl_urb = usb_alloc_urb(0))) {
+- ERR("Can't allocate: control URB");
+- goto probe_error;
+- }
+-
+- if (!(husb->write_urb = usb_alloc_urb(0))) {
+- ERR("Can't allocate: write URB");
+- goto probe_error;
+- }
+-
+- if (!(husb->read_urb = usb_alloc_urb(0))) {
+- ERR("Can't allocate: read URB");
+- goto probe_error;
+- }
+-
+- ep = bulk_in_ep;
+- pipe = usb_rcvbulkpipe(udev, ep->bEndpointAddress);
+- size = HCI_MAX_FRAME_SIZE;
+-
+- if (!(buf = kmalloc(size, GFP_KERNEL))) {
+- ERR("Can't allocate: read buffer");
+- goto probe_error;
+- }
+-
+- FILL_BULK_URB(husb->read_urb, udev, pipe, buf, size, hci_usb_bulk_read, husb);
+- husb->read_urb->transfer_flags |= USB_QUEUE_BULK;
+-
+- ep = intr_in_ep;
+- pipe = usb_rcvintpipe(udev, ep->bEndpointAddress);
+- size = usb_maxpacket(udev, pipe, usb_pipeout(pipe));
+-
+- if (!(husb->intr_urb = usb_alloc_urb(0))) {
+- ERR("Can't allocate: interrupt URB");
+- goto probe_error;
++ husb->bulk_out_ep = bulk_out_ep[0];
++ husb->bulk_in_ep = bulk_in_ep[0];
++ husb->intr_in_ep = intr_in_ep[0];
++
++ if (id->driver_info & HCI_DIGIANSWER)
++ husb->ctrl_req = HCI_DIGI_REQ;
++ else
++ husb->ctrl_req = HCI_CTRL_REQ;
++
++#ifdef CONFIG_BLUEZ_HCIUSB_SCO
++ if (isoc_iface) {
++ BT_DBG("isoc ifnum %d alts %d", isoc_ifnum, isoc_alts);
++ if (usb_set_interface(udev, isoc_ifnum, isoc_alts)) {
++ BT_ERR("Can't set isoc interface settings");
++ isoc_iface = NULL;
++ }
++ usb_driver_claim_interface(&hci_usb_driver, isoc_iface, husb);
++ husb->isoc_iface = isoc_iface;
++ husb->isoc_in_ep = isoc_in_ep[isoc_ifnum];
++ husb->isoc_out_ep = isoc_out_ep[isoc_ifnum];
+ }
++#endif
++
++ husb->completion_lock = RW_LOCK_UNLOCKED;
+
+- if (!(buf = kmalloc(size, GFP_KERNEL))) {
+- ERR("Can't allocate: interrupt buffer");
+- goto probe_error;
++ for (i = 0; i < 4; i++) {
++ skb_queue_head_init(&husb->transmit_q[i]);
++ _urb_queue_init(&husb->pending_q[i]);
++ _urb_queue_init(&husb->completed_q[i]);
+ }
+
+- FILL_INT_URB(husb->intr_urb, udev, pipe, buf, size, hci_usb_intr, husb, ep->bInterval);
+-
+- skb_queue_head_init(&husb->tx_ctrl_q);
+- skb_queue_head_init(&husb->tx_write_q);
+-
+ /* Initialize and register HCI device */
+ hdev = &husb->hdev;
+
+- hdev->type = HCI_USB;
++ hdev->type = HCI_USB;
+ hdev->driver_data = husb;
+
+ hdev->open = hci_usb_open;
+ hdev->close = hci_usb_close;
+ hdev->flush = hci_usb_flush;
+- hdev->send = hci_usb_send_frame;
++ hdev->send = hci_usb_send_frame;
++ hdev->destruct = hci_usb_destruct;
++
++ if (id->driver_info & HCI_RESET)
++ set_bit(HCI_QUIRK_RESET_ON_INIT, &hdev->quirks);
+
+ if (hci_register_dev(hdev) < 0) {
+- ERR("Can't register HCI device %s", hdev->name);
++ BT_ERR("Can't register HCI device");
+ goto probe_error;
+ }
+
+ return husb;
+
+ probe_error:
+- hci_usb_free_bufs(husb);
+ kfree(husb);
+- MOD_DEC_USE_COUNT;
++
++done:
+ return NULL;
+ }
+
+@@ -626,38 +967,34 @@
+ if (!husb)
+ return;
+
+- DBG("%s", hdev->name);
++ BT_DBG("%s", hdev->name);
+
+ hci_usb_close(hdev);
+
+- if (hci_unregister_dev(hdev) < 0) {
+- ERR("Can't unregister HCI device %s", hdev->name);
+- }
++ if (husb->isoc_iface)
++ usb_driver_release_interface(&hci_usb_driver, husb->isoc_iface);
+
+- hci_usb_free_bufs(husb);
+- kfree(husb);
+-
+- MOD_DEC_USE_COUNT;
++ if (hci_unregister_dev(hdev) < 0)
++ BT_ERR("Can't unregister HCI device %s", hdev->name);
+ }
+
+-static struct usb_driver hci_usb_driver =
+-{
++static struct usb_driver hci_usb_driver = {
+ name: "hci_usb",
+ probe: hci_usb_probe,
+ disconnect: hci_usb_disconnect,
+- id_table: usb_bluetooth_ids,
++ id_table: bluetooth_ids,
+ };
+
+ int hci_usb_init(void)
+ {
+ int err;
+
+- INF("BlueZ HCI USB driver ver %s Copyright (C) 2000,2001 Qualcomm Inc",
++ BT_INFO("BlueZ HCI USB driver ver %s Copyright (C) 2000,2001 Qualcomm Inc",
+ VERSION);
+- INF("Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>");
++ BT_INFO("Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>");
+
+ if ((err = usb_register(&hci_usb_driver)) < 0)
+- ERR("Failed to register HCI USB driver");
++ BT_ERR("Failed to register HCI USB driver");
+
+ return err;
+ }
+@@ -670,6 +1007,6 @@
+ module_init(hci_usb_init);
+ module_exit(hci_usb_cleanup);
+
+-MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>");
++MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>, Marcel Holtmann <marcel@holtmann.org>");
+ MODULE_DESCRIPTION("BlueZ HCI USB driver ver " VERSION);
+ MODULE_LICENSE("GPL");
+diff -urN linux-2.4.18/drivers/bluetooth/hci_usb.h linux-2.4.18-mh15/drivers/bluetooth/hci_usb.h
+--- linux-2.4.18/drivers/bluetooth/hci_usb.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/drivers/bluetooth/hci_usb.h 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,147 @@
++/*
++ HCI USB driver for Linux Bluetooth protocol stack (BlueZ)
++ Copyright (C) 2000-2001 Qualcomm Incorporated
++ Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
++
++ Copyright (C) 2003 Maxim Krasnyansky <maxk@qualcomm.com>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License version 2 as
++ published by the Free Software Foundation;
++
++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++
++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
++ SOFTWARE IS DISCLAIMED.
++*/
++
++/*
++ * $Id$
++ */
++
++#ifdef __KERNEL__
++
++/* Class, SubClass, and Protocol codes that describe a Bluetooth device */
++#define HCI_DEV_CLASS 0xe0 /* Wireless class */
++#define HCI_DEV_SUBCLASS 0x01 /* RF subclass */
++#define HCI_DEV_PROTOCOL 0x01 /* Bluetooth programming protocol */
++
++#define HCI_CTRL_REQ 0x20
++#define HCI_DIGI_REQ 0x40
++
++#define HCI_IGNORE 0x01
++#define HCI_RESET 0x02
++#define HCI_DIGIANSWER 0x04
++#define HCI_BROKEN_ISOC 0x08
++
++#define HCI_MAX_IFACE_NUM 3
++
++#define HCI_MAX_BULK_TX 4
++#define HCI_MAX_BULK_RX 1
++
++#define HCI_MAX_ISOC_RX 2
++#define HCI_MAX_ISOC_TX 2
++
++#define HCI_MAX_ISOC_FRAMES 10
++
++struct _urb_queue {
++ struct list_head head;
++ spinlock_t lock;
++};
++
++struct _urb {
++ struct list_head list;
++ struct _urb_queue *queue;
++ int type;
++ void *priv;
++ struct urb urb;
++};
++
++struct _urb *_urb_alloc(int isoc, int gfp);
++
++static inline void _urb_free(struct _urb *_urb)
++{
++ kfree(_urb);
++}
++
++static inline void _urb_queue_init(struct _urb_queue *q)
++{
++ INIT_LIST_HEAD(&q->head);
++ spin_lock_init(&q->lock);
++}
++
++static inline void _urb_queue_head(struct _urb_queue *q, struct _urb *_urb)
++{
++ unsigned long flags;
++ spin_lock_irqsave(&q->lock, flags);
++ list_add(&_urb->list, &q->head); _urb->queue = q;
++ spin_unlock_irqrestore(&q->lock, flags);
++}
++
++static inline void _urb_queue_tail(struct _urb_queue *q, struct _urb *_urb)
++{
++ unsigned long flags;
++ spin_lock_irqsave(&q->lock, flags);
++ list_add_tail(&_urb->list, &q->head); _urb->queue = q;
++ spin_unlock_irqrestore(&q->lock, flags);
++}
++
++static inline void _urb_unlink(struct _urb *_urb)
++{
++ struct _urb_queue *q = _urb->queue;
++ unsigned long flags;
++ if (q) {
++ spin_lock_irqsave(&q->lock, flags);
++ list_del(&_urb->list); _urb->queue = NULL;
++ spin_unlock_irqrestore(&q->lock, flags);
++ }
++}
++
++struct _urb *_urb_dequeue(struct _urb_queue *q);
++
++#ifndef container_of
++#define container_of(ptr, type, member) ({ \
++ const typeof( ((type *)0)->member ) *__mptr = (ptr); \
++ (type *)( (char *)__mptr - offsetof(type,member) );})
++#endif
++
++struct hci_usb {
++ struct hci_dev hdev;
++
++ unsigned long state;
++
++ struct usb_device *udev;
++
++ struct usb_endpoint_descriptor *bulk_in_ep;
++ struct usb_endpoint_descriptor *bulk_out_ep;
++ struct usb_endpoint_descriptor *intr_in_ep;
++
++ struct usb_interface *isoc_iface;
++ struct usb_endpoint_descriptor *isoc_out_ep;
++ struct usb_endpoint_descriptor *isoc_in_ep;
++
++ __u8 ctrl_req;
++
++ struct sk_buff_head transmit_q[4];
++ struct sk_buff *reassembly[4]; // Reassembly buffers
++
++ rwlock_t completion_lock;
++
++ atomic_t pending_tx[4]; // Number of pending requests
++ struct _urb_queue pending_q[4]; // Pending requests
++ struct _urb_queue completed_q[4]; // Completed requests
++};
++
++/* States */
++#define HCI_USB_TX_PROCESS 1
++#define HCI_USB_TX_WAKEUP 2
++
++#endif /* __KERNEL__ */
+diff -urN linux-2.4.18/drivers/bluetooth/hci_vhci.c linux-2.4.18-mh15/drivers/bluetooth/hci_vhci.c
+--- linux-2.4.18/drivers/bluetooth/hci_vhci.c 2001-09-07 18:28:38.000000000 +0200
++++ linux-2.4.18-mh15/drivers/bluetooth/hci_vhci.c 2004-08-01 16:26:23.000000000 +0200
+@@ -25,9 +25,9 @@
+ /*
+ * BlueZ HCI virtual device driver.
+ *
+- * $Id$
++ * $Id$
+ */
+-#define VERSION "1.0"
++#define VERSION "1.1"
+
+ #include <linux/config.h>
+ #include <linux/module.h>
+@@ -49,43 +49,56 @@
+ #include <asm/uaccess.h>
+
+ #include <net/bluetooth/bluetooth.h>
+-#include <net/bluetooth/bluez.h>
+ #include <net/bluetooth/hci_core.h>
+-#include <net/bluetooth/hci_vhci.h>
++#include "hci_vhci.h"
+
+ /* HCI device part */
+
+-int hci_vhci_open(struct hci_dev *hdev)
++static int hci_vhci_open(struct hci_dev *hdev)
+ {
+- hdev->flags |= HCI_RUNNING;
++ set_bit(HCI_RUNNING, &hdev->flags);
+ return 0;
+ }
+
+-int hci_vhci_flush(struct hci_dev *hdev)
++static int hci_vhci_flush(struct hci_dev *hdev)
+ {
+ struct hci_vhci_struct *hci_vhci = (struct hci_vhci_struct *) hdev->driver_data;
+ skb_queue_purge(&hci_vhci->readq);
+ return 0;
+ }
+
+-int hci_vhci_close(struct hci_dev *hdev)
++static int hci_vhci_close(struct hci_dev *hdev)
+ {
+- hdev->flags &= ~HCI_RUNNING;
++ if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
++ return 0;
++
+ hci_vhci_flush(hdev);
+ return 0;
+ }
+
+-int hci_vhci_send_frame(struct sk_buff *skb)
++static void hci_vhci_destruct(struct hci_dev *hdev)
++{
++ struct hci_vhci_struct *vhci;
++
++ if (!hdev) return;
++
++ vhci = (struct hci_vhci_struct *) hdev->driver_data;
++ kfree(vhci);
++
++ MOD_DEC_USE_COUNT;
++}
++
++static int hci_vhci_send_frame(struct sk_buff *skb)
+ {
+ struct hci_dev* hdev = (struct hci_dev *) skb->dev;
+ struct hci_vhci_struct *hci_vhci;
+
+ if (!hdev) {
+- ERR("Frame for uknown device (hdev=NULL)");
++ BT_ERR("Frame for uknown device (hdev=NULL)");
+ return -ENODEV;
+ }
+
+- if (!(hdev->flags & HCI_RUNNING))
++ if (!test_bit(HCI_RUNNING, &hdev->flags))
+ return -EBUSY;
+
+ hci_vhci = (struct hci_vhci_struct *) hdev->driver_data;
+@@ -188,7 +201,7 @@
+
+ add_wait_queue(&hci_vhci->read_wait, &wait);
+ while (count) {
+- current->state = TASK_INTERRUPTIBLE;
++ set_current_state(TASK_INTERRUPTIBLE);
+
+ /* Read frames from device queue */
+ if (!(skb = skb_dequeue(&hci_vhci->readq))) {
+@@ -214,8 +227,7 @@
+ kfree_skb(skb);
+ break;
+ }
+-
+- current->state = TASK_RUNNING;
++ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&hci_vhci->read_wait, &wait);
+
+ return ret;
+@@ -270,11 +282,13 @@
+ hdev->close = hci_vhci_close;
+ hdev->flush = hci_vhci_flush;
+ hdev->send = hci_vhci_send_frame;
++ hdev->destruct = hci_vhci_destruct;
+
+ if (hci_register_dev(hdev) < 0) {
+ kfree(hci_vhci);
+ return -EBUSY;
+ }
++ MOD_INC_USE_COUNT;
+
+ file->private_data = hci_vhci;
+ return 0;
+@@ -285,12 +299,10 @@
+ struct hci_vhci_struct *hci_vhci = (struct hci_vhci_struct *) file->private_data;
+
+ if (hci_unregister_dev(&hci_vhci->hdev) < 0) {
+- ERR("Can't unregister HCI device %s", hci_vhci->hdev.name);
++ BT_ERR("Can't unregister HCI device %s", hci_vhci->hdev.name);
+ }
+
+- kfree(hci_vhci);
+ file->private_data = NULL;
+-
+ return 0;
+ }
+
+@@ -315,12 +327,12 @@
+
+ int __init hci_vhci_init(void)
+ {
+- INF("BlueZ VHCI driver ver %s Copyright (C) 2000,2001 Qualcomm Inc",
++ BT_INFO("BlueZ VHCI driver ver %s Copyright (C) 2000,2001 Qualcomm Inc",
+ VERSION);
+- INF("Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>");
++ BT_INFO("Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>");
+
+ if (misc_register(&hci_vhci_miscdev)) {
+- ERR("Can't register misc device %d\n", VHCI_MINOR);
++ BT_ERR("Can't register misc device %d\n", VHCI_MINOR);
+ return -EIO;
+ }
+
+@@ -337,4 +349,4 @@
+
+ MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>");
+ MODULE_DESCRIPTION("BlueZ VHCI driver ver " VERSION);
+-MODULE_LICENSE("GPL");
++MODULE_LICENSE("GPL");
+diff -urN linux-2.4.18/drivers/bluetooth/hci_vhci.h linux-2.4.18-mh15/drivers/bluetooth/hci_vhci.h
+--- linux-2.4.18/drivers/bluetooth/hci_vhci.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/drivers/bluetooth/hci_vhci.h 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,50 @@
++/*
++ BlueZ - Bluetooth protocol stack for Linux
++ Copyright (C) 2000-2001 Qualcomm Incorporated
++
++ Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License version 2 as
++ published by the Free Software Foundation;
++
++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++
++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
++ SOFTWARE IS DISCLAIMED.
++*/
++
++/*
++ * $Id$
++ */
++
++#ifndef __HCI_VHCI_H
++#define __HCI_VHCI_H
++
++#ifdef __KERNEL__
++
++struct hci_vhci_struct {
++ struct hci_dev hdev;
++ __u32 flags;
++ wait_queue_head_t read_wait;
++ struct sk_buff_head readq;
++ struct fasync_struct *fasync;
++};
++
++/* VHCI device flags */
++#define VHCI_FASYNC 0x0010
++
++#endif /* __KERNEL__ */
++
++#define VHCI_DEV "/dev/vhci"
++#define VHCI_MINOR 250
++
++#endif /* __HCI_VHCI_H */
+diff -urN linux-2.4.18/drivers/bluetooth/Makefile linux-2.4.18-mh15/drivers/bluetooth/Makefile
+--- linux-2.4.18/drivers/bluetooth/Makefile 2001-09-07 18:28:38.000000000 +0200
++++ linux-2.4.18-mh15/drivers/bluetooth/Makefile 2004-08-01 16:26:23.000000000 +0200
+@@ -1,11 +1,27 @@
+ #
+-# Makefile for Bluetooth HCI device drivers.
++# Makefile for the Linux Bluetooth HCI device drivers
+ #
+
+ O_TARGET := bluetooth.o
+
++list-multi := hci_uart.o
++
+ obj-$(CONFIG_BLUEZ_HCIUSB) += hci_usb.o
+-obj-$(CONFIG_BLUEZ_HCIUART) += hci_uart.o
+ obj-$(CONFIG_BLUEZ_HCIVHCI) += hci_vhci.o
+
++obj-$(CONFIG_BLUEZ_HCIUART) += hci_uart.o
++uart-y := hci_ldisc.o
++uart-$(CONFIG_BLUEZ_HCIUART_H4) += hci_h4.o
++uart-$(CONFIG_BLUEZ_HCIUART_BCSP) += hci_bcsp.o
++
++obj-$(CONFIG_BLUEZ_HCIBFUSB) += bfusb.o
++
++obj-$(CONFIG_BLUEZ_HCIDTL1) += dtl1_cs.o
++obj-$(CONFIG_BLUEZ_HCIBT3C) += bt3c_cs.o
++obj-$(CONFIG_BLUEZ_HCIBLUECARD) += bluecard_cs.o
++obj-$(CONFIG_BLUEZ_HCIBTUART) += btuart_cs.o
++
+ include $(TOPDIR)/Rules.make
++
++hci_uart.o: $(uart-y)
++ $(LD) -r -o $@ $(uart-y)
+diff -urN linux-2.4.18/drivers/bluetooth/Makefile.lib linux-2.4.18-mh15/drivers/bluetooth/Makefile.lib
+--- linux-2.4.18/drivers/bluetooth/Makefile.lib 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/drivers/bluetooth/Makefile.lib 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,2 @@
++obj-$(CONFIG_BLUEZ_HCIBFUSB) += firmware_class.o
++obj-$(CONFIG_BLUEZ_HCIBT3C) += firmware_class.o
+diff -urN linux-2.4.18/drivers/char/pcmcia/serial_cs.c linux-2.4.18-mh15/drivers/char/pcmcia/serial_cs.c
+--- linux-2.4.18/drivers/char/pcmcia/serial_cs.c 2001-12-21 18:41:54.000000000 +0100
++++ linux-2.4.18-mh15/drivers/char/pcmcia/serial_cs.c 2004-08-01 16:26:23.000000000 +0200
+@@ -2,7 +2,7 @@
+
+ A driver for PCMCIA serial devices
+
+- serial_cs.c 1.128 2001/10/18 12:18:35
++ serial_cs.c 1.138 2002/10/25 06:24:52
+
+ The contents of this file are subject to the Mozilla Public
+ License Version 1.1 (the "License"); you may not use this file
+@@ -69,14 +69,14 @@
+ static int irq_list[4] = { -1 };
+ MODULE_PARM(irq_list, "1-4i");
+
+-/* Enable the speaker? */
+-INT_MODULE_PARM(do_sound, 1);
++INT_MODULE_PARM(do_sound, 1); /* Enable the speaker? */
++INT_MODULE_PARM(buggy_uart, 0); /* Skip strict UART tests? */
+
+ #ifdef PCMCIA_DEBUG
+ INT_MODULE_PARM(pc_debug, PCMCIA_DEBUG);
+ #define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
+ static char *version =
+-"serial_cs.c 1.128 2001/10/18 12:18:35 (David Hinds)";
++"serial_cs.c 1.138 2002/10/25 06:24:52 (David Hinds)";
+ #else
+ #define DEBUG(n, args...)
+ #endif
+@@ -95,6 +95,7 @@
+ { MANFID_OMEGA, PRODID_OMEGA_QSP_100, 4 },
+ { MANFID_QUATECH, PRODID_QUATECH_DUAL_RS232, 2 },
+ { MANFID_QUATECH, PRODID_QUATECH_DUAL_RS232_D1, 2 },
++ { MANFID_QUATECH, PRODID_QUATECH_DUAL_RS232_D2, 2 },
+ { MANFID_QUATECH, PRODID_QUATECH_QUAD_RS232, 4 },
+ { MANFID_QUATECH, PRODID_QUATECH_DUAL_RS422, 2 },
+ { MANFID_QUATECH, PRODID_QUATECH_QUAD_RS422, 4 },
+@@ -148,7 +149,7 @@
+ client_reg_t client_reg;
+ dev_link_t *link;
+ int i, ret;
+-
++
+ DEBUG(0, "serial_attach()\n");
+
+ /* Create new serial device */
+@@ -160,7 +161,7 @@
+ link->release.function = &serial_release;
+ link->release.data = (u_long)link;
+ link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+- link->io.NumPorts1 = 8;
++ link->io.Attributes2 = IO_DATA_PATH_WIDTH_8;
+ link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
+ link->irq.IRQInfo1 = IRQ_INFO2_VALID|IRQ_LEVEL_ID;
+ if (irq_list[0] == -1)
+@@ -169,13 +170,12 @@
+ for (i = 0; i < 4; i++)
+ link->irq.IRQInfo2 |= 1 << irq_list[i];
+ link->conf.Attributes = CONF_ENABLE_IRQ;
+- link->conf.Vcc = 50;
+ if (do_sound) {
+ link->conf.Attributes |= CONF_ENABLE_SPKR;
+ link->conf.Status = CCSR_AUDIO_ENA;
+ }
+ link->conf.IntType = INT_MEMORY_AND_IO;
+-
++
+ /* Register with Card Services */
+ link->next = dev_list;
+ dev_list = link;
+@@ -194,7 +194,7 @@
+ serial_detach(link);
+ return NULL;
+ }
+-
++
+ return link;
+ } /* serial_attach */
+
+@@ -214,7 +214,7 @@
+ int ret;
+
+ DEBUG(0, "serial_detach(0x%p)\n", link);
+-
++
+ /* Locate device structure */
+ for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
+ if (*linkp == link) break;
+@@ -224,17 +224,17 @@
+ del_timer(&link->release);
+ if (link->state & DEV_CONFIG)
+ serial_release((u_long)link);
+-
++
+ if (link->handle) {
+ ret = CardServices(DeregisterClient, link->handle);
+ if (ret != CS_SUCCESS)
+ cs_error(link->handle, DeregisterClient, ret);
+ }
+-
++
+ /* Unlink device structure, free bits */
+ *linkp = link->next;
+ kfree(info);
+-
++
+ } /* serial_detach */
+
+ /*====================================================================*/
+@@ -243,18 +243,20 @@
+ {
+ struct serial_struct serial;
+ int line;
+-
++
+ memset(&serial, 0, sizeof(serial));
+ serial.port = port;
+ serial.irq = irq;
+ serial.flags = ASYNC_SKIP_TEST | ASYNC_SHARE_IRQ;
++ if (buggy_uart)
++ serial.flags |= ASYNC_BUGGY_UART;
+ line = register_serial(&serial);
+ if (line < 0) {
+ printk(KERN_NOTICE "serial_cs: register_serial() at 0x%04lx,"
+ " irq %d failed\n", (u_long)serial.port, serial.irq);
+ return -1;
+ }
+-
++
+ info->line[info->ndev] = line;
+ sprintf(info->node[info->ndev].dev_name, "ttyS%d", line);
+ info->node[info->ndev].major = TTY_MAJOR;
+@@ -262,7 +264,7 @@
+ if (info->ndev > 0)
+ info->node[info->ndev-1].next = &info->node[info->ndev];
+ info->ndev++;
+-
++
+ return 0;
+ }
+
+@@ -313,7 +315,10 @@
+ return setup_serial(info, port, config.AssignedIRQ);
+ }
+ link->conf.Vcc = config.Vcc;
+-
++
++ link->io.NumPorts1 = 8;
++ link->io.NumPorts2 = 0;
++
+ /* First pass: look for a config entry that looks normal. */
+ tuple.TupleData = (cisdata_t *)buf;
+ tuple.TupleOffset = 0; tuple.TupleDataMax = 255;
+@@ -340,7 +345,7 @@
+ i = next_tuple(handle, &tuple, &parse);
+ }
+ }
+-
++
+ /* Second pass: try to find an entry that isn't picky about
+ its base address, then try to grab any standard serial port
+ address, and finally try to get any free port. */
+@@ -352,8 +357,7 @@
+ for (j = 0; j < 5; j++) {
+ link->io.BasePort1 = base[j];
+ link->io.IOAddrLines = base[j] ? 16 : 3;
+- i = CardServices(RequestIO, link->handle,
+- &link->io);
++ i = CardServices(RequestIO, link->handle, &link->io);
+ if (i == CS_SUCCESS) goto found_port;
+ }
+ }
+@@ -365,7 +369,7 @@
+ cs_error(link->handle, RequestIO, i);
+ return -1;
+ }
+-
++
+ i = CardServices(RequestIRQ, link->handle, &link->irq);
+ if (i != CS_SUCCESS) {
+ cs_error(link->handle, RequestIRQ, i);
+@@ -390,8 +394,12 @@
+ u_char buf[256];
+ cisparse_t parse;
+ cistpl_cftable_entry_t *cf = &parse.cftable_entry;
++ config_info_t config;
+ int i, base2 = 0;
+
++ CardServices(GetConfigurationInfo, handle, &config);
++ link->conf.Vcc = config.Vcc;
++
+ tuple.TupleData = (cisdata_t *)buf;
+ tuple.TupleOffset = 0; tuple.TupleDataMax = 255;
+ tuple.Attributes = 0;
+@@ -433,12 +441,12 @@
+ i = next_tuple(handle, &tuple, &parse);
+ }
+ }
+-
++
+ if (i != CS_SUCCESS) {
+- cs_error(link->handle, RequestIO, i);
+- return -1;
++ /* At worst, try to configure as a single port */
++ return simple_config(link);
+ }
+-
++
+ i = CardServices(RequestIRQ, link->handle, &link->irq);
+ if (i != CS_SUCCESS) {
+ cs_error(link->handle, RequestIRQ, i);
+@@ -454,14 +462,27 @@
+ cs_error(link->handle, RequestConfiguration, i);
+ return -1;
+ }
+-
++
++ /* The Oxford Semiconductor OXCF950 cards are in fact single-port:
++ 8 registers are for the UART, the others are extra registers */
++ if (info->manfid == MANFID_OXSEMI) {
++ if (cf->index == 1 || cf->index == 3) {
++ setup_serial(info, base2, link->irq.AssignedIRQ);
++ outb(12,link->io.BasePort1+1);
++ } else {
++ setup_serial(info, link->io.BasePort1, link->irq.AssignedIRQ);
++ outb(12,base2+1);
++ }
++ return 0;
++ }
++
+ setup_serial(info, link->io.BasePort1, link->irq.AssignedIRQ);
+ /* The Nokia cards are not really multiport cards */
+ if (info->manfid == MANFID_NOKIA)
+ return 0;
+ for (i = 0; i < info->multi-1; i++)
+ setup_serial(info, base2+(8*i), link->irq.AssignedIRQ);
+-
++
+ return 0;
+ }
+
+@@ -500,7 +521,7 @@
+ }
+ link->conf.ConfigBase = parse.config.base;
+ link->conf.Present = parse.config.rmask[0];
+-
++
+ /* Configure card */
+ link->state |= DEV_CONFIG;
+
+@@ -508,8 +529,8 @@
+ tuple.DesiredTuple = CISTPL_LONGLINK_MFC;
+ tuple.Attributes = TUPLE_RETURN_COMMON | TUPLE_RETURN_LINK;
+ info->multi = (first_tuple(handle, &tuple, &parse) == CS_SUCCESS);
+-
+- /* Is this a multiport card? */
++
++ /* Scan list of known multiport card ID's */
+ tuple.DesiredTuple = CISTPL_MANFID;
+ if (first_tuple(handle, &tuple, &parse) == CS_SUCCESS) {
+ info->manfid = le16_to_cpu(buf[0]);
+@@ -537,15 +558,15 @@
+ info->multi = 2;
+ }
+ }
+-
++
+ if (info->multi > 1)
+ multi_config(link);
+ else
+ simple_config(link);
+-
++
+ if (info->ndev == 0)
+ goto failed;
+-
++
+ if (info->manfid == MANFID_IBM) {
+ conf_reg_t reg = { 0, CS_READ, 0x800, 0 };
+ CS_CHECK(AccessConfigurationRegister, link->handle, &reg);
+@@ -562,6 +583,7 @@
+ cs_error(link->handle, last_fn, last_ret);
+ failed:
+ serial_release((u_long)link);
++ link->state &= ~DEV_CONFIG_PENDING;
+
+ } /* serial_config */
+
+@@ -569,7 +591,7 @@
+
+ After a card is removed, serial_release() will unregister the net
+ device, and release the PCMCIA configuration.
+-
++
+ ======================================================================*/
+
+ void serial_release(u_long arg)
+@@ -577,7 +599,7 @@
+ dev_link_t *link = (dev_link_t *)arg;
+ serial_info_t *info = link->priv;
+ int i;
+-
++
+ DEBUG(0, "serial_release(0x%p)\n", link);
+
+ for (i = 0; i < info->ndev; i++) {
+@@ -590,7 +612,7 @@
+ CardServices(ReleaseIO, link->handle, &link->io);
+ CardServices(ReleaseIRQ, link->handle, &link->irq);
+ }
+-
++
+ link->state &= ~DEV_CONFIG;
+
+ } /* serial_release */
+@@ -601,7 +623,7 @@
+ stuff to run after an event is received. A CARD_REMOVAL event
+ also sets some flags to discourage the serial drivers from
+ talking to the ports.
+-
++
+ ======================================================================*/
+
+ static int serial_event(event_t event, int priority,
+@@ -609,9 +631,9 @@
+ {
+ dev_link_t *link = args->client_data;
+ serial_info_t *info = link->priv;
+-
++
+ DEBUG(1, "serial_event(0x%06x)\n", event);
+-
++
+ switch (event) {
+ case CS_EVENT_CARD_REMOVAL:
+ link->state &= ~DEV_PRESENT;
+@@ -650,7 +672,7 @@
+ if (serv.Revision != CS_RELEASE_CODE) {
+ printk(KERN_NOTICE "serial_cs: Card Services release "
+ "does not match!\n");
+- return -1;
++ return -EINVAL;
+ }
+ register_pccard_driver(&dev_info, &serial_attach, &serial_detach);
+ return 0;
+diff -urN linux-2.4.18/drivers/input/Config.in linux-2.4.18-mh15/drivers/input/Config.in
+--- linux-2.4.18/drivers/input/Config.in 2001-09-13 00:34:06.000000000 +0200
++++ linux-2.4.18-mh15/drivers/input/Config.in 2004-08-01 16:26:23.000000000 +0200
+@@ -14,5 +14,6 @@
+ fi
+ dep_tristate ' Joystick support' CONFIG_INPUT_JOYDEV $CONFIG_INPUT
+ dep_tristate ' Event interface support' CONFIG_INPUT_EVDEV $CONFIG_INPUT
++dep_tristate ' User level driver support' CONFIG_INPUT_UINPUT $CONFIG_INPUT
+
+ endmenu
+diff -urN linux-2.4.18/drivers/input/keybdev.c linux-2.4.18-mh15/drivers/input/keybdev.c
+--- linux-2.4.18/drivers/input/keybdev.c 2001-10-11 18:14:32.000000000 +0200
++++ linux-2.4.18-mh15/drivers/input/keybdev.c 2004-08-01 16:26:23.000000000 +0200
+@@ -154,16 +154,18 @@
+
+ static struct input_handler keybdev_handler;
+
++static unsigned int ledstate = 0xff;
++
+ void keybdev_ledfunc(unsigned int led)
+ {
+ struct input_handle *handle;
+
+- for (handle = keybdev_handler.handle; handle; handle = handle->hnext) {
++ ledstate = led;
+
++ for (handle = keybdev_handler.handle; handle; handle = handle->hnext) {
+ input_event(handle->dev, EV_LED, LED_SCROLLL, !!(led & 0x01));
+ input_event(handle->dev, EV_LED, LED_NUML, !!(led & 0x02));
+ input_event(handle->dev, EV_LED, LED_CAPSL, !!(led & 0x04));
+-
+ }
+ }
+
+@@ -202,6 +204,12 @@
+
+ // printk(KERN_INFO "keybdev.c: Adding keyboard: input%d\n", dev->number);
+
++ if (ledstate != 0xff) {
++ input_event(dev, EV_LED, LED_SCROLLL, !!(ledstate & 0x01));
++ input_event(dev, EV_LED, LED_NUML, !!(ledstate & 0x02));
++ input_event(dev, EV_LED, LED_CAPSL, !!(ledstate & 0x04));
++ }
++
+ return handle;
+ }
+
+diff -urN linux-2.4.18/drivers/input/Makefile linux-2.4.18-mh15/drivers/input/Makefile
+--- linux-2.4.18/drivers/input/Makefile 2000-12-29 23:07:22.000000000 +0100
++++ linux-2.4.18-mh15/drivers/input/Makefile 2004-08-01 16:26:23.000000000 +0200
+@@ -24,6 +24,7 @@
+ obj-$(CONFIG_INPUT_MOUSEDEV) += mousedev.o
+ obj-$(CONFIG_INPUT_JOYDEV) += joydev.o
+ obj-$(CONFIG_INPUT_EVDEV) += evdev.o
++obj-$(CONFIG_INPUT_UINPUT) += uinput.o
+
+ # The global Rules.make.
+
+diff -urN linux-2.4.18/drivers/input/uinput.c linux-2.4.18-mh15/drivers/input/uinput.c
+--- linux-2.4.18/drivers/input/uinput.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/drivers/input/uinput.c 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,428 @@
++/*
++ * User level driver support for input subsystem
++ *
++ * Heavily based on evdev.c by Vojtech Pavlik
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ *
++ * Author: Aristeu Sergio Rozanski Filho <aris@cathedrallabs.org>
++ *
++ * Changes/Revisions:
++ * 0.1 20/06/2002
++ * - first public version
++ */
++
++#include <linux/poll.h>
++#include <linux/slab.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/input.h>
++#include <linux/smp_lock.h>
++#include <linux/fs.h>
++#include <linux/miscdevice.h>
++#include <linux/uinput.h>
++
++static int uinput_dev_open(struct input_dev *dev)
++{
++ return 0;
++}
++
++static void uinput_dev_close(struct input_dev *dev)
++{
++}
++
++static int uinput_dev_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
++{
++ struct uinput_device *udev;
++
++ udev = (struct uinput_device *)dev->private;
++
++ udev->buff[udev->head].type = type;
++ udev->buff[udev->head].code = code;
++ udev->buff[udev->head].value = value;
++ do_gettimeofday(&udev->buff[udev->head].time);
++ udev->head = (udev->head + 1) % UINPUT_BUFFER_SIZE;
++
++ wake_up_interruptible(&udev->waitq);
++
++ return 0;
++}
++
++static int uinput_dev_upload_effect(struct input_dev *dev, struct ff_effect *effect)
++{
++ return 0;
++}
++
++static int uinput_dev_erase_effect(struct input_dev *dev, int effect_id)
++{
++ return 0;
++}
++
++static int uinput_create_device(struct uinput_device *udev)
++{
++ if (!udev->dev->name) {
++ printk(KERN_DEBUG "%s: write device info first\n", UINPUT_NAME);
++ return -EINVAL;
++ }
++
++ udev->dev->open = uinput_dev_open;
++ udev->dev->close = uinput_dev_close;
++ udev->dev->event = uinput_dev_event;
++ udev->dev->upload_effect = uinput_dev_upload_effect;
++ udev->dev->erase_effect = uinput_dev_erase_effect;
++ udev->dev->private = udev;
++
++ init_waitqueue_head(&(udev->waitq));
++
++ input_register_device(udev->dev);
++
++ set_bit(UIST_CREATED, &(udev->state));
++
++ return 0;
++}
++
++static int uinput_destroy_device(struct uinput_device *udev)
++{
++ if (!test_bit(UIST_CREATED, &(udev->state))) {
++ printk(KERN_WARNING "%s: create the device first\n", UINPUT_NAME);
++ return -EINVAL;
++ }
++
++ input_unregister_device(udev->dev);
++
++ clear_bit(UIST_CREATED, &(udev->state));
++
++ return 0;
++}
++
++static int uinput_open(struct inode *inode, struct file *file)
++{
++ struct uinput_device *newdev;
++ struct input_dev *newinput;
++
++ newdev = kmalloc(sizeof(struct uinput_device), GFP_KERNEL);
++ if (!newdev)
++ goto error;
++ memset(newdev, 0, sizeof(struct uinput_device));
++
++ newinput = kmalloc(sizeof(struct input_dev), GFP_KERNEL);
++ if (!newinput)
++ goto cleanup;
++ memset(newinput, 0, sizeof(struct input_dev));
++
++ newdev->dev = newinput;
++
++ file->private_data = newdev;
++
++ return 0;
++cleanup:
++ kfree(newdev);
++error:
++ return -ENOMEM;
++}
++
++static int uinput_validate_absbits(struct input_dev *dev)
++{
++ unsigned int cnt;
++ int retval = 0;
++
++ for (cnt = 0; cnt < ABS_MAX; cnt++) {
++ if (!test_bit(cnt, dev->absbit))
++ continue;
++
++ if (/*!dev->absmin[cnt] || !dev->absmax[cnt] || */
++ (dev->absmax[cnt] <= dev->absmin[cnt])) {
++ printk(KERN_DEBUG
++ "%s: invalid abs[%02x] min:%d max:%d\n",
++ UINPUT_NAME, cnt,
++ dev->absmin[cnt], dev->absmax[cnt]);
++ retval = -EINVAL;
++ break;
++ }
++
++ if ((dev->absflat[cnt] < dev->absmin[cnt]) ||
++ (dev->absflat[cnt] > dev->absmax[cnt])) {
++ printk(KERN_DEBUG
++ "%s: absflat[%02x] out of range: %d "
++ "(min:%d/max:%d)\n",
++ UINPUT_NAME, cnt, dev->absflat[cnt],
++ dev->absmin[cnt], dev->absmax[cnt]);
++ retval = -EINVAL;
++ break;
++ }
++ }
++ return retval;
++}
++
++static int uinput_alloc_device(struct file *file, const char *buffer, size_t count)
++{
++ struct uinput_user_dev *user_dev;
++ struct input_dev *dev;
++ struct uinput_device *udev;
++ int size,
++ retval;
++
++ retval = count;
++
++ udev = (struct uinput_device *)file->private_data;
++ dev = udev->dev;
++
++ user_dev = kmalloc(sizeof(*user_dev), GFP_KERNEL);
++ if (!user_dev) {
++ retval = -ENOMEM;
++ goto exit;
++ }
++
++ if (copy_from_user(user_dev, buffer, sizeof(struct uinput_user_dev))) {
++ retval = -EFAULT;
++ goto exit;
++ }
++
++ if (NULL != dev->name)
++ kfree(dev->name);
++
++ size = strnlen(user_dev->name, UINPUT_MAX_NAME_SIZE) + 1;
++ dev->name = kmalloc(size, GFP_KERNEL);
++ if (!dev->name) {
++ retval = -ENOMEM;
++ goto exit;
++ }
++
++ strncpy(dev->name, user_dev->name, size);
++ dev->idbus = user_dev->idbus;
++ dev->idvendor = user_dev->idvendor;
++ dev->idproduct = user_dev->idproduct;
++ dev->idversion = user_dev->idversion;
++ dev->ff_effects_max = user_dev->ff_effects_max;
++
++ size = sizeof(int) * (ABS_MAX + 1);
++ memcpy(dev->absmax, user_dev->absmax, size);
++ memcpy(dev->absmin, user_dev->absmin, size);
++ memcpy(dev->absfuzz, user_dev->absfuzz, size);
++ memcpy(dev->absflat, user_dev->absflat, size);
++
++ /* check if absmin/absmax/absfuzz/absflat are filled as
++ * told in Documentation/input/input-programming.txt */
++ if (test_bit(EV_ABS, dev->evbit)) {
++ retval = uinput_validate_absbits(dev);
++ if (retval < 0)
++ kfree(dev->name);
++ }
++
++exit:
++ kfree(user_dev);
++ return retval;
++}
++
++static ssize_t uinput_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
++{
++ struct uinput_device *udev = file->private_data;
++
++ if (test_bit(UIST_CREATED, &(udev->state))) {
++ struct input_event ev;
++
++ if (copy_from_user(&ev, buffer, sizeof(struct input_event)))
++ return -EFAULT;
++ input_event(udev->dev, ev.type, ev.code, ev.value);
++ }
++ else
++ count = uinput_alloc_device(file, buffer, count);
++
++ return count;
++}
++
++static ssize_t uinput_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
++{
++ struct uinput_device *udev = file->private_data;
++ int retval = 0;
++
++ if (!test_bit(UIST_CREATED, &(udev->state)))
++ return -ENODEV;
++
++ if ((udev->head == udev->tail) && (file->f_flags & O_NONBLOCK))
++ return -EAGAIN;
++
++ retval = wait_event_interruptible(udev->waitq,
++ (udev->head != udev->tail) ||
++ !test_bit(UIST_CREATED, &(udev->state)));
++
++ if (retval)
++ return retval;
++
++ if (!test_bit(UIST_CREATED, &(udev->state)))
++ return -ENODEV;
++
++ while ((udev->head != udev->tail) &&
++ (retval + sizeof(struct input_event) <= count)) {
++ if (copy_to_user(buffer + retval, &(udev->buff[udev->tail]),
++ sizeof(struct input_event))) return -EFAULT;
++ udev->tail = (udev->tail + 1) % UINPUT_BUFFER_SIZE;
++ retval += sizeof(struct input_event);
++ }
++
++ return retval;
++}
++
++static unsigned int uinput_poll(struct file *file, poll_table *wait)
++{
++ struct uinput_device *udev = file->private_data;
++
++ poll_wait(file, &udev->waitq, wait);
++
++ if (udev->head != udev->tail)
++ return POLLIN | POLLRDNORM;
++
++ return 0;
++}
++
++static int uinput_burn_device(struct uinput_device *udev)
++{
++ if (test_bit(UIST_CREATED, &(udev->state)))
++ uinput_destroy_device(udev);
++
++ kfree(udev->dev);
++ kfree(udev);
++
++ return 0;
++}
++
++static int uinput_close(struct inode *inode, struct file *file)
++{
++ return uinput_burn_device((struct uinput_device *)file->private_data);
++}
++
++static int uinput_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
++{
++ int retval = 0;
++ struct uinput_device *udev;
++
++ udev = (struct uinput_device *)file->private_data;
++
++ /* device attributes can not be changed after the device is created */
++ if (cmd >= UI_SET_EVBIT && test_bit(UIST_CREATED, &(udev->state)))
++ return -EINVAL;
++
++ switch (cmd) {
++ case UI_DEV_CREATE:
++ retval = uinput_create_device(udev);
++ break;
++
++ case UI_DEV_DESTROY:
++ retval = uinput_destroy_device(udev);
++ break;
++
++ case UI_SET_EVBIT:
++ if (arg > EV_MAX) {
++ retval = -EINVAL;
++ break;
++ }
++ set_bit(arg, udev->dev->evbit);
++ break;
++
++ case UI_SET_KEYBIT:
++ if (arg > KEY_MAX) {
++ retval = -EINVAL;
++ break;
++ }
++ set_bit(arg, udev->dev->keybit);
++ break;
++
++ case UI_SET_RELBIT:
++ if (arg > REL_MAX) {
++ retval = -EINVAL;
++ break;
++ }
++ set_bit(arg, udev->dev->relbit);
++ break;
++
++ case UI_SET_ABSBIT:
++ if (arg > ABS_MAX) {
++ retval = -EINVAL;
++ break;
++ }
++ set_bit(arg, udev->dev->absbit);
++ break;
++
++ case UI_SET_MSCBIT:
++ if (arg > MSC_MAX) {
++ retval = -EINVAL;
++ break;
++ }
++ set_bit(arg, udev->dev->mscbit);
++ break;
++
++ case UI_SET_LEDBIT:
++ if (arg > LED_MAX) {
++ retval = -EINVAL;
++ break;
++ }
++ set_bit(arg, udev->dev->ledbit);
++ break;
++
++ case UI_SET_SNDBIT:
++ if (arg > SND_MAX) {
++ retval = -EINVAL;
++ break;
++ }
++ set_bit(arg, udev->dev->sndbit);
++ break;
++
++ case UI_SET_FFBIT:
++ if (arg > FF_MAX) {
++ retval = -EINVAL;
++ break;
++ }
++ set_bit(arg, udev->dev->ffbit);
++ break;
++
++ default:
++ retval = -EFAULT;
++ }
++ return retval;
++}
++
++struct file_operations uinput_fops = {
++ owner: THIS_MODULE,
++ open: uinput_open,
++ release: uinput_close,
++ read: uinput_read,
++ write: uinput_write,
++ poll: uinput_poll,
++ ioctl: uinput_ioctl,
++};
++
++static struct miscdevice uinput_misc = {
++ fops: &uinput_fops,
++ minor: UINPUT_MINOR,
++ name: UINPUT_NAME,
++};
++
++static int __init uinput_init(void)
++{
++ return misc_register(&uinput_misc);
++}
++
++static void __exit uinput_exit(void)
++{
++ misc_deregister(&uinput_misc);
++}
++
++MODULE_AUTHOR("Aristeu Sergio Rozanski Filho");
++MODULE_DESCRIPTION("User level driver support for input subsystem");
++MODULE_LICENSE("GPL");
++
++module_init(uinput_init);
++module_exit(uinput_exit);
++
+diff -urN linux-2.4.18/drivers/isdn/avmb1/capidrv.c linux-2.4.18-mh15/drivers/isdn/avmb1/capidrv.c
+--- linux-2.4.18/drivers/isdn/avmb1/capidrv.c 2001-12-21 18:41:54.000000000 +0100
++++ linux-2.4.18-mh15/drivers/isdn/avmb1/capidrv.c 2004-08-01 16:26:23.000000000 +0200
+@@ -514,13 +514,25 @@
+
+ static void send_message(capidrv_contr * card, _cmsg * cmsg)
+ {
+- struct sk_buff *skb;
+- size_t len;
++ struct sk_buff *skb;
++ size_t len;
++ u16 err;
++
+ capi_cmsg2message(cmsg, cmsg->buf);
+ len = CAPIMSG_LEN(cmsg->buf);
+ skb = alloc_skb(len, GFP_ATOMIC);
++ if(!skb) {
++ printk(KERN_ERR "no skb len(%d) memory\n", len);
++ return;
++ }
+ memcpy(skb_put(skb, len), cmsg->buf, len);
+- (*capifuncs->capi_put_message) (global.appid, skb);
++ err = (*capifuncs->capi_put_message) (global.appid, skb);
++ if (err) {
++ printk(KERN_WARNING "%s: capi_put_message error: %04x\n",
++ __FUNCTION__, err);
++ kfree_skb(skb);
++ return;
++ }
+ global.nsentctlpkt++;
+ }
+
+@@ -2179,10 +2191,10 @@
+ free_ncci(card, card->bchans[card->nbchan-1].nccip);
+ if (card->bchans[card->nbchan-1].plcip)
+ free_plci(card, card->bchans[card->nbchan-1].plcip);
+- if (card->plci_list)
+- printk(KERN_ERR "capidrv: bug in free_plci()\n");
+ card->nbchan--;
+ }
++ if (card->plci_list)
++ printk(KERN_ERR "capidrv: bug in free_plci()\n");
+ kfree(card->bchans);
+ card->bchans = 0;
+
+diff -urN linux-2.4.18/drivers/isdn/avmb1/kcapi.c linux-2.4.18-mh15/drivers/isdn/avmb1/kcapi.c
+--- linux-2.4.18/drivers/isdn/avmb1/kcapi.c 2001-12-21 18:41:54.000000000 +0100
++++ linux-2.4.18-mh15/drivers/isdn/avmb1/kcapi.c 2004-08-01 16:26:23.000000000 +0200
+@@ -545,7 +545,13 @@
+ static void notify_up(__u32 contr)
+ {
+ struct capi_interface_user *p;
++ __u16 appl;
+
++ for (appl = 1; appl <= CAPI_MAXAPPL; appl++) {
++ if (!VALID_APPLID(appl)) continue;
++ if (APPL(appl)->releasing) continue;
++ CARD(contr)->driver->register_appl(CARD(contr), appl, &APPL(appl)->rparam);
++ }
+ printk(KERN_NOTICE "kcapi: notify up contr %d\n", contr);
+ spin_lock(&capi_users_lock);
+ for (p = capi_users; p; p = p->next) {
+@@ -705,12 +711,16 @@
+ nextpp = &(*pp)->next;
+ }
+ }
+- APPL(appl)->releasing--;
+- if (APPL(appl)->releasing <= 0) {
+- APPL(appl)->signal = 0;
+- APPL_MARK_FREE(appl);
+- printk(KERN_INFO "kcapi: appl %d down\n", appl);
+- }
++ if (APPL(appl)->releasing) { /* only release if the application was marked for release */
++ printk(KERN_DEBUG "kcapi: appl %d releasing(%d)\n", appl, APPL(appl)->releasing);
++ APPL(appl)->releasing--;
++ if (APPL(appl)->releasing <= 0) {
++ APPL(appl)->signal = 0;
++ APPL_MARK_FREE(appl);
++ printk(KERN_INFO "kcapi: appl %d down\n", appl);
++ }
++ } else
++ printk(KERN_WARNING "kcapi: appl %d card%d released without request\n", appl, card->cnr);
+ }
+ /*
+ * ncci management
+@@ -863,16 +873,7 @@
+
+ static void controllercb_ready(struct capi_ctr * card)
+ {
+- __u16 appl;
+-
+ card->cardstate = CARD_RUNNING;
+-
+- for (appl = 1; appl <= CAPI_MAXAPPL; appl++) {
+- if (!VALID_APPLID(appl)) continue;
+- if (APPL(appl)->releasing) continue;
+- card->driver->register_appl(card, appl, &APPL(appl)->rparam);
+- }
+-
+ printk(KERN_NOTICE "kcapi: card %d \"%s\" ready.\n",
+ CARDNR(card), card->name);
+
+diff -urN linux-2.4.18/drivers/usb/Config.in linux-2.4.18-mh15/drivers/usb/Config.in
+--- linux-2.4.18/drivers/usb/Config.in 2002-02-25 20:38:07.000000000 +0100
++++ linux-2.4.18-mh15/drivers/usb/Config.in 2004-08-01 16:26:23.000000000 +0200
+@@ -31,7 +31,13 @@
+
+ comment 'USB Device Class drivers'
+ dep_tristate ' USB Audio support' CONFIG_USB_AUDIO $CONFIG_USB $CONFIG_SOUND
+-dep_tristate ' USB Bluetooth support (EXPERIMENTAL)' CONFIG_USB_BLUETOOTH $CONFIG_USB $CONFIG_EXPERIMENTAL
++if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
++ if [ "$CONFIG_BLUEZ" = "n" ]; then
++ dep_tristate ' USB Bluetooth support (EXPERIMENTAL)' CONFIG_USB_BLUETOOTH $CONFIG_USB
++ else
++ comment ' USB Bluetooth can only be used with disabled Bluetooth subsystem'
++ fi
++fi
+ if [ "$CONFIG_SCSI" = "n" ]; then
+ comment ' SCSI support is needed for USB Storage'
+ fi
+diff -urN linux-2.4.18/drivers/usb/hid-core.c linux-2.4.18-mh15/drivers/usb/hid-core.c
+--- linux-2.4.18/drivers/usb/hid-core.c 2001-12-21 18:41:55.000000000 +0100
++++ linux-2.4.18-mh15/drivers/usb/hid-core.c 2004-08-01 16:26:23.000000000 +0200
+@@ -217,6 +217,8 @@
+
+ offset = report->size;
+ report->size += parser->global.report_size * parser->global.report_count;
++ if (usages < parser->global.report_count)
++ usages = parser->global.report_count;
+
+ if (usages == 0)
+ return 0; /* ignore padding fields */
+diff -urN linux-2.4.18/include/linux/firmware.h linux-2.4.18-mh15/include/linux/firmware.h
+--- linux-2.4.18/include/linux/firmware.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/include/linux/firmware.h 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,20 @@
++#ifndef _LINUX_FIRMWARE_H
++#define _LINUX_FIRMWARE_H
++#include <linux/module.h>
++#include <linux/types.h>
++#define FIRMWARE_NAME_MAX 30
++struct firmware {
++ size_t size;
++ u8 *data;
++};
++int request_firmware (const struct firmware **fw, const char *name,
++ const char *device);
++int request_firmware_nowait (
++ struct module *module,
++ const char *name, const char *device, void *context,
++ void (*cont)(const struct firmware *fw, void *context));
++/* On 2.5 'device' is 'struct device *' */
++
++void release_firmware (const struct firmware *fw);
++void register_firmware (const char *name, const u8 *data, size_t size);
++#endif
+diff -urN linux-2.4.18/include/linux/input.h linux-2.4.18-mh15/include/linux/input.h
+--- linux-2.4.18/include/linux/input.h 2001-09-13 00:34:06.000000000 +0200
++++ linux-2.4.18-mh15/include/linux/input.h 2004-08-01 16:26:23.000000000 +0200
+@@ -468,6 +468,8 @@
+ #define BUS_PCI 0x01
+ #define BUS_ISAPNP 0x02
+ #define BUS_USB 0x03
++#define BUS_HIL 0x04
++#define BUS_BLUETOOTH 0x05
+
+ #define BUS_ISA 0x10
+ #define BUS_I8042 0x11
+diff -urN linux-2.4.18/include/linux/kernel.h linux-2.4.18-mh15/include/linux/kernel.h
+--- linux-2.4.18/include/linux/kernel.h 2002-02-25 20:38:13.000000000 +0100
++++ linux-2.4.18-mh15/include/linux/kernel.h 2004-08-01 16:26:23.000000000 +0200
+@@ -11,6 +11,7 @@
+ #include <linux/linkage.h>
+ #include <linux/stddef.h>
+ #include <linux/types.h>
++#include <linux/compiler.h>
+
+ /* Optimization barrier */
+ /* The "volatile" is due to gcc bugs */
+@@ -181,4 +182,6 @@
+ char _f[20-2*sizeof(long)-sizeof(int)]; /* Padding: libc5 uses this.. */
+ };
+
+-#endif
++#define BUG_ON(condition) do { if (unlikely((condition)!=0)) BUG(); } while(0)
++
++#endif /* _LINUX_KERNEL_H */
+diff -urN linux-2.4.18/include/linux/net.h linux-2.4.18-mh15/include/linux/net.h
+--- linux-2.4.18/include/linux/net.h 2001-11-22 20:46:19.000000000 +0100
++++ linux-2.4.18-mh15/include/linux/net.h 2004-08-01 16:26:23.000000000 +0200
+@@ -139,6 +139,7 @@
+ extern int sock_recvmsg(struct socket *, struct msghdr *m, int len, int flags);
+ extern int sock_readv_writev(int type, struct inode * inode, struct file * file,
+ const struct iovec * iov, long count, long size);
++extern struct socket *sockfd_lookup(int fd, int *err);
+
+ extern int net_ratelimit(void);
+ extern unsigned long net_random(void);
+diff -urN linux-2.4.18/include/linux/uinput.h linux-2.4.18-mh15/include/linux/uinput.h
+--- linux-2.4.18/include/linux/uinput.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/include/linux/uinput.h 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,79 @@
++/*
++ * User level driver support for input subsystem
++ *
++ * Heavily based on evdev.c by Vojtech Pavlik
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ *
++ * Author: Aristeu Sergio Rozanski Filho <aris@cathedrallabs.org>
++ *
++ * Changes/Revisions:
++ * 0.1 20/06/2002
++ * - first public version
++ */
++
++#ifndef __UINPUT_H_
++#define __UINPUT_H_
++
++#ifdef __KERNEL__
++#define UINPUT_MINOR 223
++#define UINPUT_NAME "uinput"
++#define UINPUT_BUFFER_SIZE 16
++
++/* state flags => bit index for {set|clear|test}_bit ops */
++#define UIST_CREATED 0
++
++struct uinput_device {
++ struct input_dev *dev;
++ unsigned long state;
++ wait_queue_head_t waitq;
++ unsigned char ready,
++ head,
++ tail;
++ struct input_event buff[UINPUT_BUFFER_SIZE];
++};
++#endif /* __KERNEL__ */
++
++/* ioctl */
++#define UINPUT_IOCTL_BASE 'U'
++#define UI_DEV_CREATE _IO(UINPUT_IOCTL_BASE, 1)
++#define UI_DEV_DESTROY _IO(UINPUT_IOCTL_BASE, 2)
++#define UI_SET_EVBIT _IOW(UINPUT_IOCTL_BASE, 100, int)
++#define UI_SET_KEYBIT _IOW(UINPUT_IOCTL_BASE, 101, int)
++#define UI_SET_RELBIT _IOW(UINPUT_IOCTL_BASE, 102, int)
++#define UI_SET_ABSBIT _IOW(UINPUT_IOCTL_BASE, 103, int)
++#define UI_SET_MSCBIT _IOW(UINPUT_IOCTL_BASE, 104, int)
++#define UI_SET_LEDBIT _IOW(UINPUT_IOCTL_BASE, 105, int)
++#define UI_SET_SNDBIT _IOW(UINPUT_IOCTL_BASE, 106, int)
++#define UI_SET_FFBIT _IOW(UINPUT_IOCTL_BASE, 107, int)
++
++#ifndef NBITS
++#define NBITS(x) ((((x)-1)/(sizeof(long)*8))+1)
++#endif /* NBITS */
++
++#define UINPUT_MAX_NAME_SIZE 80
++struct uinput_user_dev {
++ char name[UINPUT_MAX_NAME_SIZE];
++ unsigned short idbus;
++ unsigned short idvendor;
++ unsigned short idproduct;
++ unsigned short idversion;
++ int ff_effects_max;
++ int absmax[ABS_MAX + 1];
++ int absmin[ABS_MAX + 1];
++ int absfuzz[ABS_MAX + 1];
++ int absflat[ABS_MAX + 1];
++};
++#endif /* __UINPUT_H_ */
+diff -urN linux-2.4.18/include/net/bluetooth/bluetooth.h linux-2.4.18-mh15/include/net/bluetooth/bluetooth.h
+--- linux-2.4.18/include/net/bluetooth/bluetooth.h 2001-09-07 18:28:38.000000000 +0200
++++ linux-2.4.18-mh15/include/net/bluetooth/bluetooth.h 2004-08-01 16:26:23.000000000 +0200
+@@ -23,7 +23,7 @@
+ */
+
+ /*
+- * $Id$
++ * $Id$
+ */
+
+ #ifndef __BLUETOOTH_H
+@@ -31,17 +31,64 @@
+
+ #include <asm/types.h>
+ #include <asm/byteorder.h>
++#include <linux/poll.h>
++#include <net/sock.h>
+
+ #ifndef AF_BLUETOOTH
+ #define AF_BLUETOOTH 31
+ #define PF_BLUETOOTH AF_BLUETOOTH
+ #endif
+
++/* Reserv for core and drivers use */
++#define BLUEZ_SKB_RESERVE 8
++
++#ifndef MIN
++#define MIN(a,b) ((a) < (b) ? (a) : (b))
++#endif
++
+ #define BTPROTO_L2CAP 0
+ #define BTPROTO_HCI 1
++#define BTPROTO_SCO 2
++#define BTPROTO_RFCOMM 3
++#define BTPROTO_BNEP 4
++#define BTPROTO_CMTP 5
++#define BTPROTO_HIDP 6
+
+ #define SOL_HCI 0
+ #define SOL_L2CAP 6
++#define SOL_SCO 17
++#define SOL_RFCOMM 18
++
++/* Debugging */
++#ifdef CONFIG_BLUEZ_DEBUG
++
++#define HCI_CORE_DEBUG 1
++#define HCI_SOCK_DEBUG 1
++#define HCI_UART_DEBUG 1
++#define HCI_USB_DEBUG 1
++//#define HCI_DATA_DUMP 1
++
++#define L2CAP_DEBUG 1
++#define SCO_DEBUG 1
++#define AF_BLUETOOTH_DEBUG 1
++
++#endif /* CONFIG_BLUEZ_DEBUG */
++
++extern void bluez_dump(char *pref, __u8 *buf, int count);
++
++#if __GNUC__ <= 2 && __GNUC_MINOR__ < 95
++#define __func__ __FUNCTION__
++#endif
++
++#define BT_INFO(fmt, arg...) printk(KERN_INFO fmt "\n" , ## arg)
++#define BT_DBG(fmt, arg...) printk(KERN_INFO "%s: " fmt "\n" , __func__ , ## arg)
++#define BT_ERR(fmt, arg...) printk(KERN_ERR "%s: " fmt "\n" , __func__ , ## arg)
++
++#ifdef HCI_DATA_DUMP
++#define BT_DMP(buf, len) bluez_dump(__func__, buf, len)
++#else
++#define BT_DMP(D...)
++#endif
+
+ /* Connection and socket states */
+ enum {
+@@ -50,6 +97,7 @@
+ BT_BOUND,
+ BT_LISTEN,
+ BT_CONNECT,
++ BT_CONNECT2,
+ BT_CONFIG,
+ BT_DISCONN,
+ BT_CLOSED
+@@ -66,7 +114,8 @@
+ __u8 b[6];
+ } __attribute__((packed)) bdaddr_t;
+
+-#define BDADDR_ANY ((bdaddr_t *)"\000\000\000\000\000")
++#define BDADDR_ANY (&(bdaddr_t) {{0, 0, 0, 0, 0, 0}})
++#define BDADDR_LOCAL (&(bdaddr_t) {{0, 0, 0, 0xff, 0xff, 0xff}})
+
+ /* Copy, swap, convert BD Address */
+ static inline int bacmp(bdaddr_t *ba1, bdaddr_t *ba2)
+@@ -82,6 +131,91 @@
+ char *batostr(bdaddr_t *ba);
+ bdaddr_t *strtoba(char *str);
+
++/* Common socket structures and functions */
++
++#define bluez_pi(sk) ((struct bluez_pinfo *) &sk->protinfo)
++#define bluez_sk(pi) ((struct sock *) \
++ ((void *)pi - (unsigned long)(&((struct sock *)0)->protinfo)))
++
++struct bluez_pinfo {
++ bdaddr_t src;
++ bdaddr_t dst;
++
++ struct list_head accept_q;
++ struct sock *parent;
++};
++
++struct bluez_sock_list {
++ struct sock *head;
++ rwlock_t lock;
++};
++
++int bluez_sock_register(int proto, struct net_proto_family *ops);
++int bluez_sock_unregister(int proto);
++void bluez_sock_init(struct socket *sock, struct sock *sk);
++void bluez_sock_link(struct bluez_sock_list *l, struct sock *s);
++void bluez_sock_unlink(struct bluez_sock_list *l, struct sock *s);
++int bluez_sock_recvmsg(struct socket *sock, struct msghdr *msg, int len, int flags, struct scm_cookie *scm);
++uint bluez_sock_poll(struct file * file, struct socket *sock, poll_table *wait);
++int bluez_sock_wait_state(struct sock *sk, int state, unsigned long timeo);
++
++void bluez_accept_enqueue(struct sock *parent, struct sock *sk);
++struct sock * bluez_accept_dequeue(struct sock *parent, struct socket *newsock);
++
++/* Skb helpers */
++struct bluez_skb_cb {
++ int incomming;
++};
++#define bluez_cb(skb) ((struct bluez_skb_cb *)(skb->cb))
++
++static inline struct sk_buff *bluez_skb_alloc(unsigned int len, int how)
++{
++ struct sk_buff *skb;
++
++ if ((skb = alloc_skb(len + BLUEZ_SKB_RESERVE, how))) {
++ skb_reserve(skb, BLUEZ_SKB_RESERVE);
++ bluez_cb(skb)->incomming = 0;
++ }
++ return skb;
++}
++
++static inline struct sk_buff *bluez_skb_send_alloc(struct sock *sk, unsigned long len,
++ int nb, int *err)
++{
++ struct sk_buff *skb;
++
++ if ((skb = sock_alloc_send_skb(sk, len + BLUEZ_SKB_RESERVE, nb, err))) {
++ skb_reserve(skb, BLUEZ_SKB_RESERVE);
++ bluez_cb(skb)->incomming = 0;
++ }
++
++ return skb;
++}
++
++static inline int skb_frags_no(struct sk_buff *skb)
++{
++ register struct sk_buff *frag = skb_shinfo(skb)->frag_list;
++ register int n = 1;
++
++ for (; frag; frag=frag->next, n++);
++ return n;
++}
++
++int hci_core_init(void);
++int hci_core_cleanup(void);
++int hci_sock_init(void);
++int hci_sock_cleanup(void);
++
+ int bterr(__u16 code);
+
++#ifndef MODULE_LICENSE
++#define MODULE_LICENSE(x)
++#endif
++
++#ifndef list_for_each_safe
++#define list_for_each_safe(pos, n, head) \
++ for (pos = (head)->next, n = pos->next; pos != (head); \
++ pos = n, n = pos->next)
++#endif
++
+ #endif /* __BLUETOOTH_H */
+diff -urN linux-2.4.18/include/net/bluetooth/bluez.h linux-2.4.18-mh15/include/net/bluetooth/bluez.h
+--- linux-2.4.18/include/net/bluetooth/bluez.h 2001-09-07 18:28:38.000000000 +0200
++++ linux-2.4.18-mh15/include/net/bluetooth/bluez.h 1970-01-01 01:00:00.000000000 +0100
+@@ -1,124 +0,0 @@
+-/*
+- BlueZ - Bluetooth protocol stack for Linux
+- Copyright (C) 2000-2001 Qualcomm Incorporated
+-
+- Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
+-
+- This program is free software; you can redistribute it and/or modify
+- it under the terms of the GNU General Public License version 2 as
+- published by the Free Software Foundation;
+-
+- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+- OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+- IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+- CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
+- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+-
+- ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
+- COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
+- SOFTWARE IS DISCLAIMED.
+-*/
+-
+-/*
+- * $Id$
+- */
+-
+-#ifndef __IF_BLUEZ_H
+-#define __IF_BLUEZ_H
+-
+-#include <net/sock.h>
+-
+-#define BLUEZ_MAX_PROTO 2
+-
+-/* Reserv for core and drivers use */
+-#define BLUEZ_SKB_RESERVE 8
+-
+-#ifndef MIN
+-#define MIN(a,b) ((a) < (b) ? (a) : (b))
+-#endif
+-
+-/* Debugging */
+-#ifdef BLUEZ_DEBUG
+-
+-#define HCI_CORE_DEBUG 1
+-#define HCI_SOCK_DEBUG 1
+-#define HCI_UART_DEBUG 1
+-#define HCI_USB_DEBUG 1
+-//#define HCI_DATA_DUMP 1
+-
+-#define L2CAP_DEBUG 1
+-
+-#endif /* BLUEZ_DEBUG */
+-
+-extern void bluez_dump(char *pref, __u8 *buf, int count);
+-
+-#define INF(fmt, arg...) printk(KERN_INFO fmt "\n" , ## arg)
+-#define DBG(fmt, arg...) printk(KERN_INFO __FUNCTION__ ": " fmt "\n" , ## arg)
+-#define ERR(fmt, arg...) printk(KERN_ERR __FUNCTION__ ": " fmt "\n" , ## arg)
+-
+-#ifdef HCI_DATA_DUMP
+-#define DMP(buf, len) bluez_dump(__FUNCTION__, buf, len)
+-#else
+-#define DMP(D...)
+-#endif
+-
+-/* ----- Sockets ------ */
+-struct bluez_sock_list {
+- struct sock *head;
+- rwlock_t lock;
+-};
+-
+-extern int bluez_sock_register(int proto, struct net_proto_family *ops);
+-extern int bluez_sock_unregister(int proto);
+-
+-extern void bluez_sock_link(struct bluez_sock_list *l, struct sock *s);
+-extern void bluez_sock_unlink(struct bluez_sock_list *l, struct sock *s);
+-
+-/* ----- SKB helpers ----- */
+-struct bluez_skb_cb {
+- int incomming;
+-};
+-#define bluez_cb(skb) ((struct bluez_skb_cb *)(skb->cb))
+-
+-static inline struct sk_buff *bluez_skb_alloc(unsigned int len, int how)
+-{
+- struct sk_buff *skb;
+-
+- if ((skb = alloc_skb(len + BLUEZ_SKB_RESERVE, how))) {
+- skb_reserve(skb, BLUEZ_SKB_RESERVE);
+- bluez_cb(skb)->incomming = 0;
+- }
+- return skb;
+-}
+-
+-static inline struct sk_buff *bluez_skb_send_alloc(struct sock *sk, unsigned long len,
+- int nb, int *err)
+-{
+- struct sk_buff *skb;
+-
+- if ((skb = sock_alloc_send_skb(sk, len + BLUEZ_SKB_RESERVE, nb, err))) {
+- skb_reserve(skb, BLUEZ_SKB_RESERVE);
+- bluez_cb(skb)->incomming = 0;
+- }
+-
+- return skb;
+-}
+-
+-static inline int skb_frags_no(struct sk_buff *skb)
+-{
+- register struct sk_buff *frag = skb_shinfo(skb)->frag_list;
+- register int n = 1;
+-
+- for (; frag; frag=frag->next, n++);
+- return n;
+-}
+-
+-extern int hci_core_init(void);
+-extern int hci_core_cleanup(void);
+-extern int hci_sock_init(void);
+-extern int hci_sock_cleanup(void);
+-
+-#endif /* __IF_BLUEZ_H */
+diff -urN linux-2.4.18/include/net/bluetooth/hci_core.h linux-2.4.18-mh15/include/net/bluetooth/hci_core.h
+--- linux-2.4.18/include/net/bluetooth/hci_core.h 2001-09-07 18:28:38.000000000 +0200
++++ linux-2.4.18-mh15/include/net/bluetooth/hci_core.h 2004-08-01 16:26:23.000000000 +0200
+@@ -23,7 +23,7 @@
+ */
+
+ /*
+- * $Id$
++ * $Id$
+ */
+
+ #ifndef __HCI_CORE_H
+@@ -32,14 +32,12 @@
+ #include <net/bluetooth/hci.h>
+
+ /* HCI upper protocols */
+-#define HCI_MAX_PROTO 1
+ #define HCI_PROTO_L2CAP 0
++#define HCI_PROTO_SCO 1
+
+ #define HCI_INIT_TIMEOUT (HZ * 10)
+
+-/* ----- Inquiry cache ----- */
+-#define INQUIRY_CACHE_AGE_MAX (HZ*5) // 5 seconds
+-#define INQUIRY_ENTRY_AGE_MAX (HZ*60) // 60 seconds
++/* HCI Core structures */
+
+ struct inquiry_entry {
+ struct inquiry_entry *next;
+@@ -53,111 +51,188 @@
+ struct inquiry_entry *list;
+ };
+
+-static inline void inquiry_cache_init(struct inquiry_cache *cache)
+-{
+- spin_lock_init(&cache->lock);
+- cache->list = NULL;
+-}
++struct conn_hash {
++ struct list_head list;
++ spinlock_t lock;
++ unsigned int num;
++};
+
+-static inline void inquiry_cache_lock(struct inquiry_cache *cache)
+-{
+- spin_lock(&cache->lock);
+-}
++struct hci_dev {
++ struct list_head list;
++ spinlock_t lock;
++ atomic_t refcnt;
+
+-static inline void inquiry_cache_unlock(struct inquiry_cache *cache)
+-{
+- spin_unlock(&cache->lock);
+-}
++ char name[8];
++ unsigned long flags;
++ __u16 id;
++ __u8 type;
++ bdaddr_t bdaddr;
++ __u8 features[8];
+
+-static inline void inquiry_cache_lock_bh(struct inquiry_cache *cache)
+-{
+- spin_lock_bh(&cache->lock);
+-}
++ __u16 pkt_type;
++ __u16 link_policy;
++ __u16 link_mode;
+
+-static inline void inquiry_cache_unlock_bh(struct inquiry_cache *cache)
+-{
+- spin_unlock_bh(&cache->lock);
+-}
++ unsigned long quirks;
+
+-static inline long inquiry_cache_age(struct inquiry_cache *cache)
+-{
+- return jiffies - cache->timestamp;
+-}
++ atomic_t cmd_cnt;
++ unsigned int acl_cnt;
++ unsigned int sco_cnt;
+
+-static inline long inquiry_entry_age(struct inquiry_entry *e)
+-{
+- return jiffies - e->timestamp;
+-}
+-extern void inquiry_cache_flush(struct inquiry_cache *cache);
++ unsigned int acl_mtu;
++ unsigned int sco_mtu;
++ unsigned int acl_pkts;
++ unsigned int sco_pkts;
+
+-struct hci_dev;
++ unsigned long cmd_last_tx;
++ unsigned long acl_last_tx;
++ unsigned long sco_last_tx;
++
++ struct tasklet_struct cmd_task;
++ struct tasklet_struct rx_task;
++ struct tasklet_struct tx_task;
++
++ struct sk_buff_head rx_q;
++ struct sk_buff_head raw_q;
++ struct sk_buff_head cmd_q;
++
++ struct sk_buff *sent_cmd;
++
++ struct semaphore req_lock;
++ wait_queue_head_t req_wait_q;
++ __u32 req_status;
++ __u32 req_result;
++
++ struct inquiry_cache inq_cache;
++ struct conn_hash conn_hash;
++
++ struct hci_dev_stats stat;
++
++ void *driver_data;
++ void *core_data;
++
++ atomic_t promisc;
++
++ int (*open)(struct hci_dev *hdev);
++ int (*close)(struct hci_dev *hdev);
++ int (*flush)(struct hci_dev *hdev);
++ int (*send)(struct sk_buff *skb);
++ void (*destruct)(struct hci_dev *hdev);
++ int (*ioctl)(struct hci_dev *hdev, unsigned int cmd, unsigned long arg);
++};
+
+-/* ----- HCI Connections ----- */
+ struct hci_conn {
+ struct list_head list;
++
++ atomic_t refcnt;
++ spinlock_t lock;
++
+ bdaddr_t dst;
+ __u16 handle;
++ __u16 state;
+ __u8 type;
+- unsigned int sent;
++ __u8 out;
++ __u32 link_mode;
++ unsigned long pend;
++
++ unsigned int sent;
++
++ struct sk_buff_head data_q;
+
++ struct timer_list timer;
++
+ struct hci_dev *hdev;
+ void *l2cap_data;
++ void *sco_data;
+ void *priv;
+
+- struct sk_buff_head data_q;
++ struct hci_conn *link;
+ };
+
+-struct conn_hash {
+- struct list_head list;
+- spinlock_t lock;
+- unsigned int num;
+-};
++extern struct hci_proto *hci_proto[];
++extern struct list_head hdev_list;
++extern rwlock_t hdev_list_lock;
++
++/* ----- Inquiry cache ----- */
++#define INQUIRY_CACHE_AGE_MAX (HZ*30) // 30 seconds
++#define INQUIRY_ENTRY_AGE_MAX (HZ*60) // 60 seconds
++
++#define inquiry_cache_lock(c) spin_lock(&c->lock)
++#define inquiry_cache_unlock(c) spin_unlock(&c->lock)
++#define inquiry_cache_lock_bh(c) spin_lock_bh(&c->lock)
++#define inquiry_cache_unlock_bh(c) spin_unlock_bh(&c->lock)
+
+-static inline void conn_hash_init(struct conn_hash *h)
++static inline void inquiry_cache_init(struct hci_dev *hdev)
+ {
+- INIT_LIST_HEAD(&h->list);
+- spin_lock_init(&h->lock);
+- h->num = 0;
++ struct inquiry_cache *c = &hdev->inq_cache;
++ spin_lock_init(&c->lock);
++ c->list = NULL;
+ }
+
+-static inline void conn_hash_lock(struct conn_hash *h)
++static inline int inquiry_cache_empty(struct hci_dev *hdev)
+ {
+- spin_lock(&h->lock);
++ struct inquiry_cache *c = &hdev->inq_cache;
++ return (c->list == NULL);
+ }
+
+-static inline void conn_hash_unlock(struct conn_hash *h)
++static inline long inquiry_cache_age(struct hci_dev *hdev)
+ {
+- spin_unlock(&h->lock);
++ struct inquiry_cache *c = &hdev->inq_cache;
++ return jiffies - c->timestamp;
+ }
+
+-static inline void __conn_hash_add(struct conn_hash *h, __u16 handle, struct hci_conn *c)
++static inline long inquiry_entry_age(struct inquiry_entry *e)
+ {
+- list_add(&c->list, &h->list);
+- h->num++;
++ return jiffies - e->timestamp;
+ }
+
+-static inline void conn_hash_add(struct conn_hash *h, __u16 handle, struct hci_conn *c)
++struct inquiry_entry *inquiry_cache_lookup(struct hci_dev *hdev, bdaddr_t *bdaddr);
++void inquiry_cache_update(struct hci_dev *hdev, inquiry_info *info);
++void inquiry_cache_flush(struct hci_dev *hdev);
++int inquiry_cache_dump(struct hci_dev *hdev, int num, __u8 *buf);
++
++/* ----- HCI Connections ----- */
++enum {
++ HCI_CONN_AUTH_PEND,
++ HCI_CONN_ENCRYPT_PEND
++};
++
++#define hci_conn_lock(c) spin_lock(&c->lock)
++#define hci_conn_unlock(c) spin_unlock(&c->lock)
++#define hci_conn_lock_bh(c) spin_lock_bh(&c->lock)
++#define hci_conn_unlock_bh(c) spin_unlock_bh(&c->lock)
++
++#define conn_hash_lock(d) spin_lock(&d->conn_hash->lock)
++#define conn_hash_unlock(d) spin_unlock(&d->conn_hash->lock)
++#define conn_hash_lock_bh(d) spin_lock_bh(&d->conn_hash->lock)
++#define conn_hash_unlock_bh(d) spin_unlock_bh(&d->conn_hash->lock)
++
++static inline void conn_hash_init(struct hci_dev *hdev)
+ {
+- conn_hash_lock(h);
+- __conn_hash_add(h, handle, c);
+- conn_hash_unlock(h);
++ struct conn_hash *h = &hdev->conn_hash;
++ INIT_LIST_HEAD(&h->list);
++ spin_lock_init(&h->lock);
++ h->num = 0;
+ }
+
+-static inline void __conn_hash_del(struct conn_hash *h, struct hci_conn *c)
++static inline void conn_hash_add(struct hci_dev *hdev, struct hci_conn *c)
+ {
+- list_del(&c->list);
+- h->num--;
++ struct conn_hash *h = &hdev->conn_hash;
++ list_add(&c->list, &h->list);
++ h->num++;
+ }
+
+-static inline void conn_hash_del(struct conn_hash *h, struct hci_conn *c)
++static inline void conn_hash_del(struct hci_dev *hdev, struct hci_conn *c)
+ {
+- conn_hash_lock(h);
+- __conn_hash_del(h, c);
+- conn_hash_unlock(h);
++ struct conn_hash *h = &hdev->conn_hash;
++ list_del(&c->list);
++ h->num--;
+ }
+
+-static inline struct hci_conn *__conn_hash_lookup(struct conn_hash *h, __u16 handle)
++static inline struct hci_conn *conn_hash_lookup_handle(struct hci_dev *hdev,
++ __u16 handle)
+ {
++ register struct conn_hash *h = &hdev->conn_hash;
+ register struct list_head *p;
+ register struct hci_conn *c;
+
+@@ -169,101 +244,97 @@
+ return NULL;
+ }
+
+-static inline struct hci_conn *conn_hash_lookup(struct conn_hash *h, __u16 handle)
++static inline struct hci_conn *conn_hash_lookup_ba(struct hci_dev *hdev,
++ __u8 type, bdaddr_t *ba)
+ {
+- struct hci_conn *conn;
++ register struct conn_hash *h = &hdev->conn_hash;
++ register struct list_head *p;
++ register struct hci_conn *c;
+
+- conn_hash_lock(h);
+- conn = __conn_hash_lookup(h, handle);
+- conn_hash_unlock(h);
+- return conn;
++ list_for_each(p, &h->list) {
++ c = list_entry(p, struct hci_conn, list);
++ if (c->type == type && !bacmp(&c->dst, ba))
++ return c;
++ }
++ return NULL;
+ }
+
+-/* ----- HCI Devices ----- */
+-struct hci_dev {
+- atomic_t refcnt;
+-
+- char name[8];
+- __u32 flags;
+- __u16 id;
+- __u8 type;
+- bdaddr_t bdaddr;
+- __u8 features[8];
+-
+- __u16 pkt_type;
+-
+- atomic_t cmd_cnt;
+- unsigned int acl_cnt;
+- unsigned int sco_cnt;
+-
+- unsigned int acl_mtu;
+- unsigned int sco_mtu;
+- unsigned int acl_max;
+- unsigned int sco_max;
+-
+- void *driver_data;
+- void *l2cap_data;
+- void *priv;
+-
+- struct tasklet_struct cmd_task;
+- struct tasklet_struct rx_task;
+- struct tasklet_struct tx_task;
+-
+- struct sk_buff_head rx_q;
+- struct sk_buff_head raw_q;
+- struct sk_buff_head cmd_q;
+-
+- struct sk_buff *sent_cmd;
++void hci_acl_connect(struct hci_conn *conn);
++void hci_acl_disconn(struct hci_conn *conn, __u8 reason);
++void hci_add_sco(struct hci_conn *conn, __u16 handle);
+
+- struct semaphore req_lock;
+- wait_queue_head_t req_wait_q;
+- __u32 req_status;
+- __u32 req_result;
++struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst);
++int hci_conn_del(struct hci_conn *conn);
++void hci_conn_hash_flush(struct hci_dev *hdev);
+
+- struct inquiry_cache inq_cache;
++struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *src);
++int hci_conn_auth(struct hci_conn *conn);
++int hci_conn_encrypt(struct hci_conn *conn);
+
+- struct conn_hash conn_hash;
+-
+- struct hci_dev_stats stat;
+-
+- int (*open)(struct hci_dev *hdev);
+- int (*close)(struct hci_dev *hdev);
+- int (*flush)(struct hci_dev *hdev);
+- int (*send)(struct sk_buff *skb);
+-};
++static inline void hci_conn_set_timer(struct hci_conn *conn, long timeout)
++{
++ mod_timer(&conn->timer, jiffies + timeout);
++}
+
+-static inline void hci_dev_hold(struct hci_dev *hdev)
++static inline void hci_conn_del_timer(struct hci_conn *conn)
+ {
+- atomic_inc(&hdev->refcnt);
++ del_timer(&conn->timer);
+ }
+
+-static inline void hci_dev_put(struct hci_dev *hdev)
++static inline void hci_conn_hold(struct hci_conn *conn)
+ {
+- atomic_dec(&hdev->refcnt);
++ atomic_inc(&conn->refcnt);
++ hci_conn_del_timer(conn);
+ }
+
+-extern struct hci_dev *hci_dev_get(int index);
+-extern int hci_register_dev(struct hci_dev *hdev);
+-extern int hci_unregister_dev(struct hci_dev *hdev);
+-extern int hci_dev_open(__u16 dev);
+-extern int hci_dev_close(__u16 dev);
+-extern int hci_dev_reset(__u16 dev);
+-extern int hci_dev_reset_stat(__u16 dev);
+-extern int hci_dev_info(unsigned long arg);
+-extern int hci_dev_list(unsigned long arg);
+-extern int hci_dev_setscan(unsigned long arg);
+-extern int hci_dev_setauth(unsigned long arg);
+-extern int hci_dev_setptype(unsigned long arg);
+-extern int hci_conn_list(unsigned long arg);
+-extern int hci_inquiry(unsigned long arg);
++static inline void hci_conn_put(struct hci_conn *conn)
++{
++ if (atomic_dec_and_test(&conn->refcnt)) {
++ if (conn->type == ACL_LINK) {
++ unsigned long timeo = (conn->out) ?
++ HCI_DISCONN_TIMEOUT : HCI_DISCONN_TIMEOUT * 2;
++ hci_conn_set_timer(conn, timeo);
++ } else
++ hci_conn_set_timer(conn, HZ / 100);
++ }
++}
+
+-extern __u32 hci_dev_setmode(struct hci_dev *hdev, __u32 mode);
+-extern __u32 hci_dev_getmode(struct hci_dev *hdev);
++/* ----- HCI Devices ----- */
++static inline void hci_dev_put(struct hci_dev *d)
++{
++ if (atomic_dec_and_test(&d->refcnt))
++ d->destruct(d);
++}
++#define hci_dev_hold(d) atomic_inc(&d->refcnt)
++
++#define hci_dev_lock(d) spin_lock(&d->lock)
++#define hci_dev_unlock(d) spin_unlock(&d->lock)
++#define hci_dev_lock_bh(d) spin_lock_bh(&d->lock)
++#define hci_dev_unlock_bh(d) spin_unlock_bh(&d->lock)
++
++struct hci_dev *hci_dev_get(int index);
++struct hci_dev *hci_get_route(bdaddr_t *src, bdaddr_t *dst);
++int hci_register_dev(struct hci_dev *hdev);
++int hci_unregister_dev(struct hci_dev *hdev);
++int hci_suspend_dev(struct hci_dev *hdev);
++int hci_resume_dev(struct hci_dev *hdev);
++int hci_dev_open(__u16 dev);
++int hci_dev_close(__u16 dev);
++int hci_dev_reset(__u16 dev);
++int hci_dev_reset_stat(__u16 dev);
++int hci_dev_cmd(unsigned int cmd, unsigned long arg);
++int hci_get_dev_list(unsigned long arg);
++int hci_get_dev_info(unsigned long arg);
++int hci_get_conn_list(unsigned long arg);
++int hci_get_conn_info(struct hci_dev *hdev, unsigned long arg);
++int hci_inquiry(unsigned long arg);
+
+-extern int hci_recv_frame(struct sk_buff *skb);
++int hci_recv_frame(struct sk_buff *skb);
++void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb);
+
+ /* ----- LMP capabilities ----- */
+ #define lmp_rswitch_capable(dev) (dev->features[0] & LMP_RSWITCH)
++#define lmp_encrypt_capable(dev) (dev->features[0] & LMP_ENCRYPT)
+
+ /* ----- HCI tasks ----- */
+ static inline void hci_sched_cmd(struct hci_dev *hdev)
+@@ -284,43 +355,130 @@
+ /* ----- HCI protocols ----- */
+ struct hci_proto {
+ char *name;
+- __u32 id;
+- __u32 flags;
++ unsigned int id;
++ unsigned long flags;
+
+ void *priv;
+
+- int (*connect_ind) (struct hci_dev *hdev, bdaddr_t *bdaddr);
+- int (*connect_cfm) (struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 status, struct hci_conn *conn);
++ int (*connect_ind) (struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 type);
++ int (*connect_cfm) (struct hci_conn *conn, __u8 status);
+ int (*disconn_ind) (struct hci_conn *conn, __u8 reason);
+- int (*recv_acldata) (struct hci_conn *conn, struct sk_buff *skb , __u16 flags);
++ int (*recv_acldata) (struct hci_conn *conn, struct sk_buff *skb, __u16 flags);
+ int (*recv_scodata) (struct hci_conn *conn, struct sk_buff *skb);
++ int (*auth_cfm) (struct hci_conn *conn, __u8 status);
++ int (*encrypt_cfm) (struct hci_conn *conn, __u8 status);
+ };
+
+-extern int hci_register_proto(struct hci_proto *hproto);
+-extern int hci_unregister_proto(struct hci_proto *hproto);
+-extern int hci_register_notifier(struct notifier_block *nb);
+-extern int hci_unregister_notifier(struct notifier_block *nb);
+-extern int hci_connect(struct hci_dev * hdev, bdaddr_t * bdaddr);
+-extern int hci_disconnect(struct hci_conn *conn, __u8 reason);
+-extern int hci_send_cmd(struct hci_dev *hdev, __u16 ogf, __u16 ocf, __u32 plen, void * param);
+-extern int hci_send_raw(struct sk_buff *skb);
+-extern int hci_send_acl(struct hci_conn *conn, struct sk_buff *skb, __u16 flags);
+-extern int hci_send_sco(struct hci_conn *conn, struct sk_buff *skb);
++static inline int hci_proto_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 type)
++{
++ register struct hci_proto *hp;
++ int mask = 0;
++
++ hp = hci_proto[HCI_PROTO_L2CAP];
++ if (hp && hp->connect_ind)
++ mask |= hp->connect_ind(hdev, bdaddr, type);
++
++ hp = hci_proto[HCI_PROTO_SCO];
++ if (hp && hp->connect_ind)
++ mask |= hp->connect_ind(hdev, bdaddr, type);
++
++ return mask;
++}
++
++static inline void hci_proto_connect_cfm(struct hci_conn *conn, __u8 status)
++{
++ register struct hci_proto *hp;
++
++ hp = hci_proto[HCI_PROTO_L2CAP];
++ if (hp && hp->connect_cfm)
++ hp->connect_cfm(conn, status);
++
++ hp = hci_proto[HCI_PROTO_SCO];
++ if (hp && hp->connect_cfm)
++ hp->connect_cfm(conn, status);
++}
++
++static inline void hci_proto_disconn_ind(struct hci_conn *conn, __u8 reason)
++{
++ register struct hci_proto *hp;
++
++ hp = hci_proto[HCI_PROTO_L2CAP];
++ if (hp && hp->disconn_ind)
++ hp->disconn_ind(conn, reason);
++
++ hp = hci_proto[HCI_PROTO_SCO];
++ if (hp && hp->disconn_ind)
++ hp->disconn_ind(conn, reason);
++}
++
++static inline void hci_proto_auth_cfm(struct hci_conn *conn, __u8 status)
++{
++ register struct hci_proto *hp;
++
++ hp = hci_proto[HCI_PROTO_L2CAP];
++ if (hp && hp->auth_cfm)
++ hp->auth_cfm(conn, status);
++
++ hp = hci_proto[HCI_PROTO_SCO];
++ if (hp && hp->auth_cfm)
++ hp->auth_cfm(conn, status);
++}
++
++static inline void hci_proto_encrypt_cfm(struct hci_conn *conn, __u8 status)
++{
++ register struct hci_proto *hp;
++
++ hp = hci_proto[HCI_PROTO_L2CAP];
++ if (hp && hp->encrypt_cfm)
++ hp->encrypt_cfm(conn, status);
++
++ hp = hci_proto[HCI_PROTO_SCO];
++ if (hp && hp->encrypt_cfm)
++ hp->encrypt_cfm(conn, status);
++}
++
++int hci_register_proto(struct hci_proto *hproto);
++int hci_unregister_proto(struct hci_proto *hproto);
++int hci_register_notifier(struct notifier_block *nb);
++int hci_unregister_notifier(struct notifier_block *nb);
++
++int hci_send_cmd(struct hci_dev *hdev, __u16 ogf, __u16 ocf, __u32 plen, void *param);
++int hci_send_acl(struct hci_conn *conn, struct sk_buff *skb, __u16 flags);
++int hci_send_sco(struct hci_conn *conn, struct sk_buff *skb);
++
++void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 ogf, __u16 ocf);
++
++void hci_si_event(struct hci_dev *hdev, int type, int dlen, void *data);
+
+ /* ----- HCI Sockets ----- */
+-extern void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb);
++void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb);
+
+ /* HCI info for socket */
+-#define hci_pi(sk) ((struct hci_pinfo *) &sk->protinfo)
++#define hci_pi(sk) ((struct hci_pinfo *) &sk->tp_pinfo)
+ struct hci_pinfo {
+ struct hci_dev *hdev;
+ struct hci_filter filter;
+ __u32 cmsg_mask;
+ };
+
++/* HCI security filter */
++#define HCI_SFLT_MAX_OGF 5
++
++struct hci_sec_filter {
++ __u32 type_mask;
++ __u32 event_mask[2];
++ __u32 ocf_mask[HCI_SFLT_MAX_OGF + 1][4];
++};
++
+ /* ----- HCI requests ----- */
+ #define HCI_REQ_DONE 0
+ #define HCI_REQ_PEND 1
+ #define HCI_REQ_CANCELED 2
+
++#define hci_req_lock(d) down(&d->req_lock)
++#define hci_req_unlock(d) up(&d->req_lock)
++
++void hci_req_complete(struct hci_dev *hdev, int result);
++void hci_req_cancel(struct hci_dev *hdev, int err);
++
+ #endif /* __HCI_CORE_H */
+diff -urN linux-2.4.18/include/net/bluetooth/hci.h linux-2.4.18-mh15/include/net/bluetooth/hci.h
+--- linux-2.4.18/include/net/bluetooth/hci.h 2001-09-07 18:28:38.000000000 +0200
++++ linux-2.4.18-mh15/include/net/bluetooth/hci.h 2004-08-01 16:26:23.000000000 +0200
+@@ -23,59 +23,80 @@
+ */
+
+ /*
+- * $Id$
++ * $Id$
+ */
+
+ #ifndef __HCI_H
+ #define __HCI_H
+
+-#include <asm/byteorder.h>
+-
+-#define HCI_MAX_DEV 8
+-#define HCI_MAX_FRAME_SIZE 2048
++#define HCI_MAX_ACL_SIZE 1024
++#define HCI_MAX_SCO_SIZE 255
++#define HCI_MAX_EVENT_SIZE 260
++#define HCI_MAX_FRAME_SIZE (HCI_MAX_ACL_SIZE + 4)
+
+ /* HCI dev events */
+ #define HCI_DEV_REG 1
+ #define HCI_DEV_UNREG 2
+ #define HCI_DEV_UP 3
+ #define HCI_DEV_DOWN 4
++#define HCI_DEV_SUSPEND 5
++#define HCI_DEV_RESUME 6
+
+ /* HCI device types */
+-#define HCI_UART 0
++#define HCI_VHCI 0
+ #define HCI_USB 1
+-#define HCI_VHCI 2
+-
+-/* HCI device modes */
+-#define HCI_NORMAL 0x0001
+-#define HCI_RAW 0x0002
+-#define HCI_MODE_MASK (HCI_NORMAL | HCI_RAW)
+-#define HCI_SOCK 0x1000
+-
+-/* HCI device states */
+-#define HCI_INIT 0x0010
+-#define HCI_UP 0x0020
+-#define HCI_RUNNING 0x0040
++#define HCI_PCCARD 2
++#define HCI_UART 3
++#define HCI_RS232 4
++#define HCI_PCI 5
++
++/* HCI device quirks */
++enum {
++ HCI_QUIRK_RESET_ON_INIT
++};
+
+ /* HCI device flags */
+-#define HCI_PSCAN 0x0100
+-#define HCI_ISCAN 0x0200
+-#define HCI_AUTH 0x0400
++enum {
++ HCI_UP,
++ HCI_INIT,
++ HCI_RUNNING,
++
++ HCI_PSCAN,
++ HCI_ISCAN,
++ HCI_AUTH,
++ HCI_ENCRYPT,
++ HCI_INQUIRY,
++
++ HCI_RAW
++};
+
+-/* HCI Ioctl defines */
++/* HCI ioctl defines */
+ #define HCIDEVUP _IOW('H', 201, int)
+ #define HCIDEVDOWN _IOW('H', 202, int)
+ #define HCIDEVRESET _IOW('H', 203, int)
+-#define HCIRESETSTAT _IOW('H', 204, int)
+-#define HCIGETINFO _IOR('H', 205, int)
+-#define HCIGETDEVLIST _IOR('H', 206, int)
+-#define HCISETRAW _IOW('H', 207, int)
+-#define HCISETSCAN _IOW('H', 208, int)
+-#define HCISETAUTH _IOW('H', 209, int)
+-#define HCIINQUIRY _IOR('H', 210, int)
+-#define HCISETPTYPE _IOW('H', 211, int)
++#define HCIDEVRESTAT _IOW('H', 204, int)
++
++#define HCIGETDEVLIST _IOR('H', 210, int)
++#define HCIGETDEVINFO _IOR('H', 211, int)
+ #define HCIGETCONNLIST _IOR('H', 212, int)
++#define HCIGETCONNINFO _IOR('H', 213, int)
+
+-#ifndef __NO_HCI_DEFS
++#define HCISETRAW _IOW('H', 220, int)
++#define HCISETSCAN _IOW('H', 221, int)
++#define HCISETAUTH _IOW('H', 222, int)
++#define HCISETENCRYPT _IOW('H', 223, int)
++#define HCISETPTYPE _IOW('H', 224, int)
++#define HCISETLINKPOL _IOW('H', 225, int)
++#define HCISETLINKMODE _IOW('H', 226, int)
++#define HCISETACLMTU _IOW('H', 227, int)
++#define HCISETSCOMTU _IOW('H', 228, int)
++
++#define HCIINQUIRY _IOR('H', 240, int)
++
++/* HCI timeouts */
++#define HCI_CONN_TIMEOUT (HZ * 40)
++#define HCI_DISCONN_TIMEOUT (HZ * 2)
++#define HCI_CONN_IDLE_TIMEOUT (HZ * 60)
+
+ /* HCI Packet types */
+ #define HCI_COMMAND_PKT 0x01
+@@ -92,11 +113,18 @@
+ #define HCI_DH3 0x0800
+ #define HCI_DH5 0x8000
+
++#define HCI_HV1 0x0020
++#define HCI_HV2 0x0040
++#define HCI_HV3 0x0080
++
++#define SCO_PTYPE_MASK (HCI_HV1 | HCI_HV2 | HCI_HV3)
++#define ACL_PTYPE_MASK (~SCO_PTYPE_MASK)
++
+ /* ACL flags */
+-#define ACL_CONT 0x0001
+-#define ACL_START 0x0002
+-#define ACL_ACTIVE_BCAST 0x0010
+-#define ACL_PICO_BCAST 0x0020
++#define ACL_CONT 0x01
++#define ACL_START 0x02
++#define ACL_ACTIVE_BCAST 0x04
++#define ACL_PICO_BCAST 0x08
+
+ /* Baseband links */
+ #define SCO_LINK 0x00
+@@ -125,6 +153,20 @@
+ #define LMP_PSCHEME 0x02
+ #define LMP_PCONTROL 0x04
+
++/* Link policies */
++#define HCI_LP_RSWITCH 0x0001
++#define HCI_LP_HOLD 0x0002
++#define HCI_LP_SNIFF 0x0004
++#define HCI_LP_PARK 0x0008
++
++/* Link mode */
++#define HCI_LM_ACCEPT 0x8000
++#define HCI_LM_MASTER 0x0001
++#define HCI_LM_AUTH 0x0002
++#define HCI_LM_ENCRYPT 0x0004
++#define HCI_LM_TRUSTED 0x0008
++#define HCI_LM_RELIABLE 0x0010
++
+ /* ----- HCI Commands ----- */
+ /* OGF & OCF values */
+
+@@ -137,9 +179,10 @@
+ __u8 hci_ver;
+ __u16 hci_rev;
+ __u8 lmp_ver;
+- __u16 man_name;
+- __u16 lmp_sub;
++ __u16 manufacturer;
++ __u16 lmp_subver;
+ } __attribute__ ((packed)) read_local_version_rp;
++#define READ_LOCAL_VERSION_RP_SIZE 9
+
+ #define OCF_READ_LOCAL_FEATURES 0x0003
+ typedef struct {
+@@ -165,18 +208,24 @@
+ /* Host Controller and Baseband */
+ #define OGF_HOST_CTL 0x03
+ #define OCF_RESET 0x0003
++#define OCF_READ_AUTH_ENABLE 0x001F
+ #define OCF_WRITE_AUTH_ENABLE 0x0020
+- #define AUTH_DISABLED 0x00
+- #define AUTH_ENABLED 0x01
++ #define AUTH_DISABLED 0x00
++ #define AUTH_ENABLED 0x01
++
++#define OCF_READ_ENCRYPT_MODE 0x0021
++#define OCF_WRITE_ENCRYPT_MODE 0x0022
++ #define ENCRYPT_DISABLED 0x00
++ #define ENCRYPT_P2P 0x01
++ #define ENCRYPT_BOTH 0x02
+
+ #define OCF_WRITE_CA_TIMEOUT 0x0016
+ #define OCF_WRITE_PG_TIMEOUT 0x0018
+
+ #define OCF_WRITE_SCAN_ENABLE 0x001A
+- #define SCANS_DISABLED 0x00
+- #define IS_ENA_PS_DIS 0x01
+- #define IS_DIS_PS_ENA 0x02
+- #define IS_ENA_PS_ENA 0x03
++ #define SCAN_DISABLED 0x00
++ #define SCAN_INQUIRY 0x01
++ #define SCAN_PAGE 0x02
+
+ #define OCF_SET_EVENT_FLT 0x0005
+ typedef struct {
+@@ -226,9 +275,18 @@
+ } __attribute__ ((packed)) write_class_of_dev_cp;
+ #define WRITE_CLASS_OF_DEV_CP_SIZE 3
+
++#define OCF_HOST_BUFFER_SIZE 0x0033
++typedef struct {
++ __u16 acl_mtu;
++ __u8 sco_mtu;
++ __u16 acl_max_pkt;
++ __u16 sco_max_pkt;
++} __attribute__ ((packed)) host_buffer_size_cp;
++#define HOST_BUFFER_SIZE_CP_SIZE 7
++
+ /* Link Control */
+ #define OGF_LINK_CTL 0x01
+-#define OCF_CREATE_CONN 0x0005
++#define OCF_CREATE_CONN 0x0005
+ typedef struct {
+ bdaddr_t bdaddr;
+ __u16 pkt_type;
+@@ -246,6 +304,13 @@
+ } __attribute__ ((packed)) accept_conn_req_cp;
+ #define ACCEPT_CONN_REQ_CP_SIZE 7
+
++#define OCF_REJECT_CONN_REQ 0x000a
++typedef struct {
++ bdaddr_t bdaddr;
++ __u8 reason;
++} __attribute__ ((packed)) reject_conn_req_cp;
++#define REJECT_CONN_REQ_CP_SIZE 7
++
+ #define OCF_DISCONNECT 0x0006
+ typedef struct {
+ __u16 handle;
+@@ -253,17 +318,142 @@
+ } __attribute__ ((packed)) disconnect_cp;
+ #define DISCONNECT_CP_SIZE 3
+
++#define OCF_ADD_SCO 0x0007
++typedef struct {
++ __u16 handle;
++ __u16 pkt_type;
++} __attribute__ ((packed)) add_sco_cp;
++#define ADD_SCO_CP_SIZE 4
++
+ #define OCF_INQUIRY 0x0001
+ typedef struct {
+ __u8 lap[3];
+- __u8 lenght;
++ __u8 length;
+ __u8 num_rsp;
+ } __attribute__ ((packed)) inquiry_cp;
+ #define INQUIRY_CP_SIZE 5
+
+-#define OGF_LINK_POLICY 0x02 /* Link Policy */
++typedef struct {
++ __u8 status;
++ bdaddr_t bdaddr;
++} __attribute__ ((packed)) status_bdaddr_rp;
++#define STATUS_BDADDR_RP_SIZE 7
++
++#define OCF_INQUIRY_CANCEL 0x0002
++
++#define OCF_LINK_KEY_REPLY 0x000B
++#define OCF_LINK_KEY_NEG_REPLY 0x000C
++typedef struct {
++ bdaddr_t bdaddr;
++ __u8 link_key[16];
++} __attribute__ ((packed)) link_key_reply_cp;
++#define LINK_KEY_REPLY_CP_SIZE 22
++
++#define OCF_PIN_CODE_REPLY 0x000D
++#define OCF_PIN_CODE_NEG_REPLY 0x000E
++typedef struct {
++ bdaddr_t bdaddr;
++ __u8 pin_len;
++ __u8 pin_code[16];
++} __attribute__ ((packed)) pin_code_reply_cp;
++#define PIN_CODE_REPLY_CP_SIZE 23
++
++#define OCF_CHANGE_CONN_PTYPE 0x000F
++typedef struct {
++ __u16 handle;
++ __u16 pkt_type;
++} __attribute__ ((packed)) change_conn_ptype_cp;
++#define CHANGE_CONN_PTYPE_CP_SIZE 4
++
++#define OCF_AUTH_REQUESTED 0x0011
++typedef struct {
++ __u16 handle;
++} __attribute__ ((packed)) auth_requested_cp;
++#define AUTH_REQUESTED_CP_SIZE 2
++
++#define OCF_SET_CONN_ENCRYPT 0x0013
++typedef struct {
++ __u16 handle;
++ __u8 encrypt;
++} __attribute__ ((packed)) set_conn_encrypt_cp;
++#define SET_CONN_ENCRYPT_CP_SIZE 3
++
++#define OCF_REMOTE_NAME_REQ 0x0019
++typedef struct {
++ bdaddr_t bdaddr;
++ __u8 pscan_rep_mode;
++ __u8 pscan_mode;
++ __u16 clock_offset;
++} __attribute__ ((packed)) remote_name_req_cp;
++#define REMOTE_NAME_REQ_CP_SIZE 10
++
++#define OCF_READ_REMOTE_FEATURES 0x001B
++typedef struct {
++ __u16 handle;
++} __attribute__ ((packed)) read_remote_features_cp;
++#define READ_REMOTE_FEATURES_CP_SIZE 2
++
++#define OCF_READ_REMOTE_VERSION 0x001D
++typedef struct {
++ __u16 handle;
++} __attribute__ ((packed)) read_remote_version_cp;
++#define READ_REMOTE_VERSION_CP_SIZE 2
++
++/* Link Policy */
++#define OGF_LINK_POLICY 0x02
++#define OCF_ROLE_DISCOVERY 0x0009
++typedef struct {
++ __u16 handle;
++} __attribute__ ((packed)) role_discovery_cp;
++#define ROLE_DISCOVERY_CP_SIZE 2
++typedef struct {
++ __u8 status;
++ __u16 handle;
++ __u8 role;
++} __attribute__ ((packed)) role_discovery_rp;
++#define ROLE_DISCOVERY_RP_SIZE 4
++
++#define OCF_READ_LINK_POLICY 0x000C
++typedef struct {
++ __u16 handle;
++} __attribute__ ((packed)) read_link_policy_cp;
++#define READ_LINK_POLICY_CP_SIZE 2
++typedef struct {
++ __u8 status;
++ __u16 handle;
++ __u16 policy;
++} __attribute__ ((packed)) read_link_policy_rp;
++#define READ_LINK_POLICY_RP_SIZE 5
+
+-/* --------- HCI Events --------- */
++#define OCF_SWITCH_ROLE 0x000B
++typedef struct {
++ bdaddr_t bdaddr;
++ __u8 role;
++} __attribute__ ((packed)) switch_role_cp;
++#define SWITCH_ROLE_CP_SIZE 7
++
++#define OCF_WRITE_LINK_POLICY 0x000D
++typedef struct {
++ __u16 handle;
++ __u16 policy;
++} __attribute__ ((packed)) write_link_policy_cp;
++#define WRITE_LINK_POLICY_CP_SIZE 4
++typedef struct {
++ __u8 status;
++ __u16 handle;
++} __attribute__ ((packed)) write_link_policy_rp;
++#define WRITE_LINK_POLICY_RP_SIZE 3
++
++/* Status params */
++#define OGF_STATUS_PARAM 0x05
++
++/* Testing commands */
++#define OGF_TESTING_CMD 0x3e
++
++/* Vendor specific commands */
++#define OGF_VENDOR_CMD 0x3f
++
++/* ---- HCI Events ---- */
+ #define EVT_INQUIRY_COMPLETE 0x01
+
+ #define EVT_INQUIRY_RESULT 0x02
+@@ -272,11 +462,22 @@
+ __u8 pscan_rep_mode;
+ __u8 pscan_period_mode;
+ __u8 pscan_mode;
+- __u8 class[3];
++ __u8 dev_class[3];
+ __u16 clock_offset;
+ } __attribute__ ((packed)) inquiry_info;
+ #define INQUIRY_INFO_SIZE 14
+
++#define EVT_INQUIRY_RESULT_WITH_RSSI 0x22
++typedef struct {
++ bdaddr_t bdaddr;
++ __u8 pscan_rep_mode;
++ __u8 pscan_period_mode;
++ __u8 dev_class[3];
++ __u16 clock_offset;
++ __s8 rssi;
++} __attribute__ ((packed)) inquiry_info_with_rssi;
++#define INQUIRY_INFO_WITH_RSSI_SIZE 14
++
+ #define EVT_CONN_COMPLETE 0x03
+ typedef struct {
+ __u8 status;
+@@ -303,6 +504,44 @@
+ } __attribute__ ((packed)) evt_disconn_complete;
+ #define EVT_DISCONN_COMPLETE_SIZE 4
+
++#define EVT_AUTH_COMPLETE 0x06
++typedef struct {
++ __u8 status;
++ __u16 handle;
++} __attribute__ ((packed)) evt_auth_complete;
++#define EVT_AUTH_COMPLETE_SIZE 3
++
++#define EVT_REMOTE_NAME_REQ_COMPLETE 0x07
++typedef struct {
++ __u8 status;
++ bdaddr_t bdaddr;
++ __u8 name[248];
++} __attribute__ ((packed)) evt_remote_name_req_complete;
++#define EVT_REMOTE_NAME_REQ_COMPLETE_SIZE 255
++
++#define EVT_ENCRYPT_CHANGE 0x08
++typedef struct {
++ __u8 status;
++ __u16 handle;
++ __u8 encrypt;
++} __attribute__ ((packed)) evt_encrypt_change;
++#define EVT_ENCRYPT_CHANGE_SIZE 5
++
++#define EVT_QOS_SETUP_COMPLETE 0x0D
++typedef struct {
++ __u8 service_type;
++ __u32 token_rate;
++ __u32 peak_bandwidth;
++ __u32 latency;
++ __u32 delay_variation;
++} __attribute__ ((packed)) hci_qos;
++typedef struct {
++ __u8 status;
++ __u16 handle;
++ hci_qos qos;
++} __attribute__ ((packed)) evt_qos_setup_complete;
++#define EVT_QOS_SETUP_COMPLETE_SIZE 20
++
+ #define EVT_CMD_COMPLETE 0x0e
+ typedef struct {
+ __u8 ncmd;
+@@ -321,16 +560,78 @@
+ #define EVT_NUM_COMP_PKTS 0x13
+ typedef struct {
+ __u8 num_hndl;
+- /* variable lenght part */
++ /* variable length part */
+ } __attribute__ ((packed)) evt_num_comp_pkts;
+ #define EVT_NUM_COMP_PKTS_SIZE 1
+
+-#define EVT_HCI_DEV_EVENT 0xfd
++#define EVT_ROLE_CHANGE 0x12
++typedef struct {
++ __u8 status;
++ bdaddr_t bdaddr;
++ __u8 role;
++} __attribute__ ((packed)) evt_role_change;
++#define EVT_ROLE_CHANGE_SIZE 8
++
++#define EVT_PIN_CODE_REQ 0x16
++typedef struct {
++ bdaddr_t bdaddr;
++} __attribute__ ((packed)) evt_pin_code_req;
++#define EVT_PIN_CODE_REQ_SIZE 6
++
++#define EVT_LINK_KEY_REQ 0x17
++typedef struct {
++ bdaddr_t bdaddr;
++} __attribute__ ((packed)) evt_link_key_req;
++#define EVT_LINK_KEY_REQ_SIZE 6
++
++#define EVT_LINK_KEY_NOTIFY 0x18
++typedef struct {
++ bdaddr_t bdaddr;
++ __u8 link_key[16];
++ __u8 key_type;
++} __attribute__ ((packed)) evt_link_key_notify;
++#define EVT_LINK_KEY_NOTIFY_SIZE 23
++
++#define EVT_READ_REMOTE_FEATURES_COMPLETE 0x0B
++typedef struct {
++ __u8 status;
++ __u16 handle;
++ __u8 features[8];
++} __attribute__ ((packed)) evt_read_remote_features_complete;
++#define EVT_READ_REMOTE_FEATURES_COMPLETE_SIZE 11
++
++#define EVT_READ_REMOTE_VERSION_COMPLETE 0x0C
++typedef struct {
++ __u8 status;
++ __u16 handle;
++ __u8 lmp_ver;
++ __u16 manufacturer;
++ __u16 lmp_subver;
++} __attribute__ ((packed)) evt_read_remote_version_complete;
++#define EVT_READ_REMOTE_VERSION_COMPLETE_SIZE 8
++
++/* Internal events generated by BlueZ stack */
++#define EVT_STACK_INTERNAL 0xfd
++typedef struct {
++ __u16 type;
++ __u8 data[0];
++} __attribute__ ((packed)) evt_stack_internal;
++#define EVT_STACK_INTERNAL_SIZE 2
++
++#define EVT_SI_DEVICE 0x01
++typedef struct {
++ __u16 event;
++ __u16 dev_id;
++} __attribute__ ((packed)) evt_si_device;
++#define EVT_SI_DEVICE_SIZE 4
++
++#define EVT_SI_SECURITY 0x02
+ typedef struct {
+ __u16 event;
+- __u16 param;
+-} __attribute__ ((packed)) evt_hci_dev_event;
+-#define EVT_HCI_DEV_EVENT_SIZE 4
++ __u16 proto;
++ __u16 subproto;
++ __u8 incomming;
++} __attribute__ ((packed)) evt_si_security;
+
+ /* -------- HCI Packet structures -------- */
+ #define HCI_TYPE_LEN 1
+@@ -369,14 +670,14 @@
+ #define acl_handle(h) (h & 0x0fff)
+ #define acl_flags(h) (h >> 12)
+
+-#endif /* _NO_HCI_DEFS */
+-
+ /* HCI Socket options */
+-#define HCI_DATA_DIR 0x0001
+-#define HCI_FILTER 0x0002
++#define HCI_DATA_DIR 1
++#define HCI_FILTER 2
++#define HCI_TIME_STAMP 3
+
+ /* HCI CMSG flags */
+ #define HCI_CMSG_DIR 0x0001
++#define HCI_CMSG_TSTAMP 0x0002
+
+ struct sockaddr_hci {
+ sa_family_t hci_family;
+@@ -387,27 +688,29 @@
+ struct hci_filter {
+ __u32 type_mask;
+ __u32 event_mask[2];
++ __u16 opcode;
+ };
+
+-struct hci_dev_req {
+- __u16 dev_id;
+- __u32 dev_opt;
+-};
+-
+-struct hci_dev_list_req {
+- __u16 dev_num;
+- struct hci_dev_req dev_req[0]; /* hci_dev_req structures */
+-};
+-
+-struct hci_inquiry_req {
+- __u16 dev_id;
+- __u16 flags;
+- __u8 lap[3];
+- __u8 length;
+- __u8 num_rsp;
+-};
+-#define IREQ_CACHE_FLUSH 0x0001
++#define HCI_FLT_TYPE_BITS 31
++#define HCI_FLT_EVENT_BITS 63
++#define HCI_FLT_OGF_BITS 63
++#define HCI_FLT_OCF_BITS 127
++
++#if BITS_PER_LONG == 64
++static inline void hci_set_bit(int nr, void *addr)
++{
++ *((__u32 *) addr + (nr >> 5)) |= ((__u32) 1 << (nr & 31));
++}
++static inline int hci_test_bit(int nr, void *addr)
++{
++ return *((__u32 *) addr + (nr >> 5)) & ((__u32) 1 << (nr & 31));
++}
++#else
++#define hci_set_bit set_bit
++#define hci_test_bit test_bit
++#endif
+
++/* Ioctl requests structures */
+ struct hci_dev_stats {
+ __u32 err_rx;
+ __u32 err_tx;
+@@ -433,11 +736,13 @@
+ __u8 features[8];
+
+ __u32 pkt_type;
++ __u32 link_policy;
++ __u32 link_mode;
+
+ __u16 acl_mtu;
+- __u16 acl_max;
++ __u16 acl_pkts;
+ __u16 sco_mtu;
+- __u16 sco_max;
++ __u16 sco_pkts;
+
+ struct hci_dev_stats stat;
+ };
+@@ -445,6 +750,20 @@
+ struct hci_conn_info {
+ __u16 handle;
+ bdaddr_t bdaddr;
++ __u8 type;
++ __u8 out;
++ __u16 state;
++ __u32 link_mode;
++};
++
++struct hci_dev_req {
++ __u16 dev_id;
++ __u32 dev_opt;
++};
++
++struct hci_dev_list_req {
++ __u16 dev_num;
++ struct hci_dev_req dev_req[0]; /* hci_dev_req structures */
+ };
+
+ struct hci_conn_list_req {
+@@ -453,4 +772,26 @@
+ struct hci_conn_info conn_info[0];
+ };
+
++struct hci_conn_info_req {
++ bdaddr_t bdaddr;
++ __u8 type;
++ struct hci_conn_info conn_info[0];
++};
++
++struct hci_inquiry_req {
++ __u16 dev_id;
++ __u16 flags;
++ __u8 lap[3];
++ __u8 length;
++ __u8 num_rsp;
++};
++#define IREQ_CACHE_FLUSH 0x0001
++
++struct hci_remotename_req {
++ __u16 dev_id;
++ __u16 flags;
++ bdaddr_t bdaddr;
++ __u8 name[248];
++};
++
+ #endif /* __HCI_H */
+diff -urN linux-2.4.18/include/net/bluetooth/hci_uart.h linux-2.4.18-mh15/include/net/bluetooth/hci_uart.h
+--- linux-2.4.18/include/net/bluetooth/hci_uart.h 2001-09-07 18:28:38.000000000 +0200
++++ linux-2.4.18-mh15/include/net/bluetooth/hci_uart.h 1970-01-01 01:00:00.000000000 +0100
+@@ -1,62 +0,0 @@
+-/*
+- BlueZ - Bluetooth protocol stack for Linux
+- Copyright (C) 2000-2001 Qualcomm Incorporated
+-
+- Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
+-
+- This program is free software; you can redistribute it and/or modify
+- it under the terms of the GNU General Public License version 2 as
+- published by the Free Software Foundation;
+-
+- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+- OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+- IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+- CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
+- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+-
+- ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
+- COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
+- SOFTWARE IS DISCLAIMED.
+-*/
+-
+-/*
+- * $Id$
+- */
+-
+-#ifndef N_HCI
+-#define N_HCI 15
+-#endif
+-
+-#ifdef __KERNEL__
+-
+-#define tty2n_hci(tty) ((struct n_hci *)((tty)->disc_data))
+-#define n_hci2tty(n_hci) ((n_hci)->tty)
+-
+-struct n_hci {
+- struct tty_struct *tty;
+- struct hci_dev hdev;
+-
+- struct sk_buff_head txq;
+- unsigned long tx_state;
+-
+- spinlock_t rx_lock;
+- unsigned long rx_state;
+- unsigned long rx_count;
+- struct sk_buff *rx_skb;
+-};
+-
+-/* Transmit states */
+-#define TRANS_SENDING 1
+-#define TRANS_WAKEUP 2
+-
+-/* Receiver States */
+-#define WAIT_PACKET_TYPE 0
+-#define WAIT_EVENT_HDR 1
+-#define WAIT_ACL_HDR 2
+-#define WAIT_SCO_HDR 3
+-#define WAIT_DATA 4
+-
+-#endif /* __KERNEL__ */
+diff -urN linux-2.4.18/include/net/bluetooth/hci_usb.h linux-2.4.18-mh15/include/net/bluetooth/hci_usb.h
+--- linux-2.4.18/include/net/bluetooth/hci_usb.h 2001-09-07 18:28:38.000000000 +0200
++++ linux-2.4.18-mh15/include/net/bluetooth/hci_usb.h 1970-01-01 01:00:00.000000000 +0100
+@@ -1,68 +0,0 @@
+-/*
+- BlueZ - Bluetooth protocol stack for Linux
+- Copyright (C) 2000-2001 Qualcomm Incorporated
+-
+- Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
+-
+- This program is free software; you can redistribute it and/or modify
+- it under the terms of the GNU General Public License version 2 as
+- published by the Free Software Foundation;
+-
+- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+- OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+- IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+- CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
+- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+-
+- ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
+- COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
+- SOFTWARE IS DISCLAIMED.
+-*/
+-
+-/*
+- * $Id$
+- */
+-
+-#ifdef __KERNEL__
+-
+-/* Class, SubClass, and Protocol codes that describe a Bluetooth device */
+-#define HCI_DEV_CLASS 0xe0 /* Wireless class */
+-#define HCI_DEV_SUBCLASS 0x01 /* RF subclass */
+-#define HCI_DEV_PROTOCOL 0x01 /* Bluetooth programming protocol */
+-
+-#define HCI_CTRL_REQ 0x20
+-
+-struct hci_usb {
+- struct usb_device *udev;
+-
+- devrequest dev_req;
+- struct urb *ctrl_urb;
+- struct urb *intr_urb;
+- struct urb *read_urb;
+- struct urb *write_urb;
+-
+- __u8 *read_buf;
+- __u8 *intr_buf;
+- struct sk_buff *intr_skb;
+- int intr_count;
+-
+- __u8 bulk_out_ep_addr;
+- __u8 bulk_in_ep_addr;
+- __u8 intr_in_ep_addr;
+- __u8 intr_in_interval;
+-
+- struct hci_dev hdev;
+-
+- unsigned long tx_state;
+- struct sk_buff_head tx_ctrl_q;
+- struct sk_buff_head tx_write_q;
+-};
+-
+-/* Transmit states */
+-#define HCI_TX_CTRL 1
+-#define HCI_TX_WRITE 2
+-
+-#endif /* __KERNEL__ */
+diff -urN linux-2.4.18/include/net/bluetooth/hci_vhci.h linux-2.4.18-mh15/include/net/bluetooth/hci_vhci.h
+--- linux-2.4.18/include/net/bluetooth/hci_vhci.h 2001-09-07 18:28:38.000000000 +0200
++++ linux-2.4.18-mh15/include/net/bluetooth/hci_vhci.h 1970-01-01 01:00:00.000000000 +0100
+@@ -1,50 +0,0 @@
+-/*
+- BlueZ - Bluetooth protocol stack for Linux
+- Copyright (C) 2000-2001 Qualcomm Incorporated
+-
+- Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
+-
+- This program is free software; you can redistribute it and/or modify
+- it under the terms of the GNU General Public License version 2 as
+- published by the Free Software Foundation;
+-
+- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+- OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+- IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+- CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
+- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+-
+- ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
+- COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
+- SOFTWARE IS DISCLAIMED.
+-*/
+-
+-/*
+- * $Id$
+- */
+-
+-#ifndef __HCI_VHCI_H
+-#define __HCI_VHCI_H
+-
+-#ifdef __KERNEL__
+-
+-struct hci_vhci_struct {
+- struct hci_dev hdev;
+- __u32 flags;
+- wait_queue_head_t read_wait;
+- struct sk_buff_head readq;
+- struct fasync_struct *fasync;
+-};
+-
+-/* VHCI device flags */
+-#define VHCI_FASYNC 0x0010
+-
+-#endif /* __KERNEL__ */
+-
+-#define VHCI_DEV "/dev/vhci"
+-#define VHCI_MINOR 250
+-
+-#endif /* __HCI_VHCI_H */
+diff -urN linux-2.4.18/include/net/bluetooth/l2cap_core.h linux-2.4.18-mh15/include/net/bluetooth/l2cap_core.h
+--- linux-2.4.18/include/net/bluetooth/l2cap_core.h 2001-09-07 18:28:38.000000000 +0200
++++ linux-2.4.18-mh15/include/net/bluetooth/l2cap_core.h 1970-01-01 01:00:00.000000000 +0100
+@@ -1,144 +0,0 @@
+-/*
+- BlueZ - Bluetooth protocol stack for Linux
+- Copyright (C) 2000-2001 Qualcomm Incorporated
+-
+- Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
+-
+- This program is free software; you can redistribute it and/or modify
+- it under the terms of the GNU General Public License version 2 as
+- published by the Free Software Foundation;
+-
+- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+- OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+- IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+- CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
+- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+-
+- ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
+- COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
+- SOFTWARE IS DISCLAIMED.
+-*/
+-
+-/*
+- * $Id$
+- */
+-
+-#ifndef __L2CAP_CORE_H
+-#define __L2CAP_CORE_H
+-
+-#ifdef __KERNEL__
+-
+-/* ----- L2CAP interface ----- */
+-struct l2cap_iff {
+- struct list_head list;
+- struct hci_dev *hdev;
+- bdaddr_t *bdaddr;
+- __u16 mtu;
+- spinlock_t lock;
+- struct list_head conn_list;
+-};
+-
+-static inline void l2cap_iff_lock(struct l2cap_iff *iff)
+-{
+- spin_lock(&iff->lock);
+-}
+-
+-static inline void l2cap_iff_unlock(struct l2cap_iff *iff)
+-{
+- spin_unlock(&iff->lock);
+-}
+-
+-/* ----- L2CAP connections ----- */
+-struct l2cap_chan_list {
+- struct sock *head;
+- rwlock_t lock;
+- long num;
+-};
+-
+-struct l2cap_conn {
+- struct l2cap_iff *iff;
+- struct list_head list;
+-
+- struct hci_conn *hconn;
+-
+- __u16 state;
+- __u8 out;
+- bdaddr_t src;
+- bdaddr_t dst;
+-
+- spinlock_t lock;
+- atomic_t refcnt;
+-
+- struct sk_buff *rx_skb;
+- __u32 rx_len;
+- __u8 rx_ident;
+- __u8 tx_ident;
+-
+- struct l2cap_chan_list chan_list;
+-
+- struct timer_list timer;
+-};
+-
+-static inline void __l2cap_conn_link(struct l2cap_iff *iff, struct l2cap_conn *c)
+-{
+- list_add(&c->list, &iff->conn_list);
+-}
+-
+-static inline void __l2cap_conn_unlink(struct l2cap_iff *iff, struct l2cap_conn *c)
+-{
+- list_del(&c->list);
+-}
+-
+-/* ----- L2CAP channel and socket info ----- */
+-#define l2cap_pi(sk) ((struct l2cap_pinfo *) &sk->protinfo)
+-
+-struct l2cap_accept_q {
+- struct sock *head;
+- struct sock *tail;
+-};
+-
+-struct l2cap_pinfo {
+- bdaddr_t src;
+- bdaddr_t dst;
+- __u16 psm;
+- __u16 dcid;
+- __u16 scid;
+- __u32 flags;
+-
+- __u16 imtu;
+- __u16 omtu;
+- __u16 flush_to;
+-
+- __u8 conf_state;
+- __u16 conf_mtu;
+-
+- __u8 ident;
+-
+- struct l2cap_conn *conn;
+- struct sock *next_c;
+- struct sock *prev_c;
+-
+- struct sock *parent;
+- struct sock *next_q;
+- struct sock *prev_q;
+-
+- struct l2cap_accept_q accept_q;
+-};
+-
+-#define CONF_REQ_SENT 0x01
+-#define CONF_INPUT_DONE 0x02
+-#define CONF_OUTPUT_DONE 0x04
+-
+-extern struct bluez_sock_list l2cap_sk_list;
+-extern struct list_head l2cap_iff_list;
+-extern rwlock_t l2cap_rt_lock;
+-
+-extern void l2cap_register_proc(void);
+-extern void l2cap_unregister_proc(void);
+-
+-#endif /* __KERNEL__ */
+-
+-#endif /* __L2CAP_CORE_H */
+diff -urN linux-2.4.18/include/net/bluetooth/l2cap.h linux-2.4.18-mh15/include/net/bluetooth/l2cap.h
+--- linux-2.4.18/include/net/bluetooth/l2cap.h 2001-09-07 18:28:38.000000000 +0200
++++ linux-2.4.18-mh15/include/net/bluetooth/l2cap.h 2004-08-01 16:26:23.000000000 +0200
+@@ -23,22 +23,17 @@
+ */
+
+ /*
+- * $Id$
++ * $Id$
+ */
+
+ #ifndef __L2CAP_H
+ #define __L2CAP_H
+
+-#include <asm/types.h>
+-#include <asm/byteorder.h>
+-
+ /* L2CAP defaults */
+ #define L2CAP_DEFAULT_MTU 672
+ #define L2CAP_DEFAULT_FLUSH_TO 0xFFFF
+
+ #define L2CAP_CONN_TIMEOUT (HZ * 40)
+-#define L2CAP_DISCONN_TIMEOUT (HZ * 2)
+-#define L2CAP_CONN_IDLE_TIMEOUT (HZ * 60)
+
+ /* L2CAP socket address */
+ struct sockaddr_l2 {
+@@ -47,17 +42,12 @@
+ bdaddr_t l2_bdaddr;
+ };
+
+-/* set/get sockopt defines */
+-#define L2CAP_OPTIONS 0x01
++/* Socket options */
++#define L2CAP_OPTIONS 0x01
+ struct l2cap_options {
+ __u16 omtu;
+ __u16 imtu;
+ __u16 flush_to;
+- __u32 token_rate;
+- __u32 bucket_size;
+- __u32 pick_band;
+- __u32 latency;
+- __u32 delay_var;
+ };
+
+ #define L2CAP_CONNINFO 0x02
+@@ -65,6 +55,27 @@
+ __u16 hci_handle;
+ };
+
++#define L2CAP_LM 0x03
++#define L2CAP_LM_MASTER 0x0001
++#define L2CAP_LM_AUTH 0x0002
++#define L2CAP_LM_ENCRYPT 0x0004
++#define L2CAP_LM_TRUSTED 0x0008
++#define L2CAP_LM_RELIABLE 0x0010
++
++#define L2CAP_QOS 0x04
++struct l2cap_qos {
++ __u16 service_type;
++ __u32 token_rate;
++ __u32 token_bucket_size;
++ __u32 peak_bandwidth;
++ __u32 latency;
++ __u32 delay_variation;
++};
++
++#define L2CAP_SERV_NO_TRAFFIC 0x00
++#define L2CAP_SERV_BEST_EFFORT 0x01
++#define L2CAP_SERV_GUARANTEED 0x02
++
+ /* L2CAP command codes */
+ #define L2CAP_COMMAND_REJ 0x01
+ #define L2CAP_CONN_REQ 0x02
+@@ -79,7 +90,6 @@
+ #define L2CAP_INFO_RSP 0x0b
+
+ /* L2CAP structures */
+-
+ typedef struct {
+ __u16 len;
+ __u16 cid;
+@@ -112,11 +122,17 @@
+ } __attribute__ ((packed)) l2cap_conn_rsp;
+ #define L2CAP_CONN_RSP_SIZE 8
+
+-#define L2CAP_CONN_SUCCESS 0x0000
+-#define L2CAP_CONN_PEND 0x0001
+-#define L2CAP_CONN_BAD_PSM 0x0002
+-#define L2CAP_CONN_SEC_BLOCK 0x0003
+-#define L2CAP_CONN_NO_MEM 0x0004
++/* connect result */
++#define L2CAP_CR_SUCCESS 0x0000
++#define L2CAP_CR_PEND 0x0001
++#define L2CAP_CR_BAD_PSM 0x0002
++#define L2CAP_CR_SEC_BLOCK 0x0003
++#define L2CAP_CR_NO_MEM 0x0004
++
++/* connect status */
++#define L2CAP_CS_NO_INFO 0x0000
++#define L2CAP_CS_AUTHEN_PEND 0x0001
++#define L2CAP_CS_AUTHOR_PEND 0x0002
+
+ typedef struct {
+ __u16 dcid;
+@@ -147,6 +163,8 @@
+ #define L2CAP_CONF_FLUSH_TO 0x02
+ #define L2CAP_CONF_QOS 0x03
+
++#define L2CAP_CONF_MAX_SIZE 22
++
+ typedef struct {
+ __u16 dcid;
+ __u16 scid;
+@@ -159,4 +177,82 @@
+ } __attribute__ ((packed)) l2cap_disconn_rsp;
+ #define L2CAP_DISCONN_RSP_SIZE 4
+
++typedef struct {
++ __u16 type;
++ __u8 data[0];
++} __attribute__ ((packed)) l2cap_info_req;
++#define L2CAP_INFO_REQ_SIZE 2
++
++typedef struct {
++ __u16 type;
++ __u16 result;
++ __u8 data[0];
++} __attribute__ ((packed)) l2cap_info_rsp;
++#define L2CAP_INFO_RSP_SIZE 4
++
++/* info type */
++#define L2CAP_IT_CL_MTU 0x0001
++#define L2CAP_IT_FEAT_MASK 0x0002
++
++/* info result */
++#define L2CAP_IR_SUCCESS 0x0000
++#define L2CAP_IR_NOTSUPP 0x0001
++
++/* ----- L2CAP connections ----- */
++struct l2cap_chan_list {
++ struct sock *head;
++ rwlock_t lock;
++ long num;
++};
++
++struct l2cap_conn {
++ struct hci_conn *hcon;
++
++ bdaddr_t *dst;
++ bdaddr_t *src;
++
++ unsigned int mtu;
++
++ spinlock_t lock;
++
++ struct sk_buff *rx_skb;
++ __u32 rx_len;
++ __u8 rx_ident;
++ __u8 tx_ident;
++
++ struct l2cap_chan_list chan_list;
++};
++
++/* ----- L2CAP channel and socket info ----- */
++#define l2cap_pi(sk) ((struct l2cap_pinfo *) &sk->tp_pinfo)
++
++struct l2cap_pinfo {
++ __u16 psm;
++ __u16 dcid;
++ __u16 scid;
++
++ __u16 imtu;
++ __u16 omtu;
++ __u16 flush_to;
++
++ __u32 link_mode;
++
++ __u8 conf_state;
++ __u8 conf_retry;
++ __u16 conf_mtu;
++
++ __u8 ident;
++
++ struct l2cap_conn *conn;
++ struct sock *next_c;
++ struct sock *prev_c;
++};
++
++#define L2CAP_CONF_REQ_SENT 0x01
++#define L2CAP_CONF_INPUT_DONE 0x02
++#define L2CAP_CONF_OUTPUT_DONE 0x04
++#define L2CAP_CONF_MAX_RETRIES 2
++
++void l2cap_load(void);
++
+ #endif /* __L2CAP_H */
+diff -urN linux-2.4.18/include/net/bluetooth/rfcomm.h linux-2.4.18-mh15/include/net/bluetooth/rfcomm.h
+--- linux-2.4.18/include/net/bluetooth/rfcomm.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/include/net/bluetooth/rfcomm.h 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,361 @@
++/*
++ RFCOMM implementation for Linux Bluetooth stack (BlueZ).
++ Copyright (C) 2002 Maxim Krasnyansky <maxk@qualcomm.com>
++ Copyright (C) 2002 Marcel Holtmann <marcel@holtmann.org>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License version 2 as
++ published by the Free Software Foundation;
++
++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++
++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
++ SOFTWARE IS DISCLAIMED.
++*/
++
++/*
++ RPN support - Dirk Husemann <hud@zurich.ibm.com>
++*/
++
++/*
++ * $Id$
++ */
++
++#ifndef __RFCOMM_H
++#define __RFCOMM_H
++
++#define RFCOMM_PSM 3
++
++#define RFCOMM_CONN_TIMEOUT (HZ * 30)
++#define RFCOMM_DISC_TIMEOUT (HZ * 20)
++
++#define RFCOMM_DEFAULT_MTU 127
++#define RFCOMM_DEFAULT_CREDITS 7
++
++#define RFCOMM_MAX_L2CAP_MTU 1024
++#define RFCOMM_MAX_CREDITS 40
++
++#define RFCOMM_SKB_HEAD_RESERVE 8
++#define RFCOMM_SKB_TAIL_RESERVE 2
++#define RFCOMM_SKB_RESERVE (RFCOMM_SKB_HEAD_RESERVE + RFCOMM_SKB_TAIL_RESERVE)
++
++#define RFCOMM_SABM 0x2f
++#define RFCOMM_DISC 0x43
++#define RFCOMM_UA 0x63
++#define RFCOMM_DM 0x0f
++#define RFCOMM_UIH 0xef
++
++#define RFCOMM_TEST 0x08
++#define RFCOMM_FCON 0x28
++#define RFCOMM_FCOFF 0x18
++#define RFCOMM_MSC 0x38
++#define RFCOMM_RPN 0x24
++#define RFCOMM_RLS 0x14
++#define RFCOMM_PN 0x20
++#define RFCOMM_NSC 0x04
++
++#define RFCOMM_V24_FC 0x02
++#define RFCOMM_V24_RTC 0x04
++#define RFCOMM_V24_RTR 0x08
++#define RFCOMM_V24_IC 0x40
++#define RFCOMM_V24_DV 0x80
++
++#define RFCOMM_RPN_BR_2400 0x0
++#define RFCOMM_RPN_BR_4800 0x1
++#define RFCOMM_RPN_BR_7200 0x2
++#define RFCOMM_RPN_BR_9600 0x3
++#define RFCOMM_RPN_BR_19200 0x4
++#define RFCOMM_RPN_BR_38400 0x5
++#define RFCOMM_RPN_BR_57600 0x6
++#define RFCOMM_RPN_BR_115200 0x7
++#define RFCOMM_RPN_BR_230400 0x8
++
++#define RFCOMM_RPN_DATA_5 0x0
++#define RFCOMM_RPN_DATA_6 0x1
++#define RFCOMM_RPN_DATA_7 0x2
++#define RFCOMM_RPN_DATA_8 0x3
++
++#define RFCOMM_RPN_STOP_1 0
++#define RFCOMM_RPN_STOP_15 1
++
++#define RFCOMM_RPN_PARITY_NONE 0x0
++#define RFCOMM_RPN_PARITY_ODD 0x4
++#define RFCOMM_RPN_PARITY_EVEN 0x5
++#define RFCOMM_RPN_PARITY_MARK 0x6
++#define RFCOMM_RPN_PARITY_SPACE 0x7
++
++#define RFCOMM_RPN_FLOW_NONE 0x00
++
++#define RFCOMM_RPN_XON_CHAR 0x11
++#define RFCOMM_RPN_XOFF_CHAR 0x13
++
++#define RFCOMM_RPN_PM_BITRATE 0x0001
++#define RFCOMM_RPN_PM_DATA 0x0002
++#define RFCOMM_RPN_PM_STOP 0x0004
++#define RFCOMM_RPN_PM_PARITY 0x0008
++#define RFCOMM_RPN_PM_PARITY_TYPE 0x0010
++#define RFCOMM_RPN_PM_XON 0x0020
++#define RFCOMM_RPN_PM_XOFF 0x0040
++#define RFCOMM_RPN_PM_FLOW 0x3F00
++
++#define RFCOMM_RPN_PM_ALL 0x3F7F
++
++struct rfcomm_hdr {
++ u8 addr;
++ u8 ctrl;
++ u8 len; // Actual size can be 2 bytes
++} __attribute__ ((packed));
++
++struct rfcomm_cmd {
++ u8 addr;
++ u8 ctrl;
++ u8 len;
++ u8 fcs;
++} __attribute__ ((packed));
++
++struct rfcomm_mcc {
++ u8 type;
++ u8 len;
++} __attribute__ ((packed));
++
++struct rfcomm_pn {
++ u8 dlci;
++ u8 flow_ctrl;
++ u8 priority;
++ u8 ack_timer;
++ u16 mtu;
++ u8 max_retrans;
++ u8 credits;
++} __attribute__ ((packed));
++
++struct rfcomm_rpn {
++ u8 dlci;
++ u8 bit_rate;
++ u8 line_settings;
++ u8 flow_ctrl;
++ u8 xon_char;
++ u8 xoff_char;
++ u16 param_mask;
++} __attribute__ ((packed));
++
++struct rfcomm_rls {
++ u8 dlci;
++ u8 status;
++} __attribute__ ((packed));
++
++struct rfcomm_msc {
++ u8 dlci;
++ u8 v24_sig;
++} __attribute__ ((packed));
++
++/* ---- Core structures, flags etc ---- */
++
++struct rfcomm_session {
++ struct list_head list;
++ struct socket *sock;
++ unsigned long state;
++ unsigned long flags;
++ atomic_t refcnt;
++ int initiator;
++
++ /* Default DLC parameters */
++ int cfc;
++ uint mtu;
++
++ struct list_head dlcs;
++};
++
++struct rfcomm_dlc {
++ struct list_head list;
++ struct rfcomm_session *session;
++ struct sk_buff_head tx_queue;
++ struct timer_list timer;
++
++ spinlock_t lock;
++ unsigned long state;
++ unsigned long flags;
++ atomic_t refcnt;
++ u8 dlci;
++ u8 addr;
++ u8 priority;
++ u8 v24_sig;
++ u8 mscex;
++
++ uint mtu;
++ uint cfc;
++ uint rx_credits;
++ uint tx_credits;
++
++ void *owner;
++
++ void (*data_ready)(struct rfcomm_dlc *d, struct sk_buff *skb);
++ void (*state_change)(struct rfcomm_dlc *d, int err);
++ void (*modem_status)(struct rfcomm_dlc *d, u8 v24_sig);
++};
++
++/* DLC and session flags */
++#define RFCOMM_RX_THROTTLED 0
++#define RFCOMM_TX_THROTTLED 1
++#define RFCOMM_MSC_PENDING 2
++#define RFCOMM_TIMED_OUT 3
++
++/* Scheduling flags and events */
++#define RFCOMM_SCHED_STATE 0
++#define RFCOMM_SCHED_RX 1
++#define RFCOMM_SCHED_TX 2
++#define RFCOMM_SCHED_TIMEO 3
++#define RFCOMM_SCHED_WAKEUP 31
++
++/* MSC exchange flags */
++#define RFCOMM_MSCEX_TX 1
++#define RFCOMM_MSCEX_RX 2
++#define RFCOMM_MSCEX_OK (RFCOMM_MSCEX_TX + RFCOMM_MSCEX_RX)
++
++/* CFC states */
++#define RFCOMM_CFC_UNKNOWN -1
++#define RFCOMM_CFC_DISABLED 0
++#define RFCOMM_CFC_ENABLED RFCOMM_MAX_CREDITS
++
++extern struct task_struct *rfcomm_thread;
++extern unsigned long rfcomm_event;
++
++static inline void rfcomm_schedule(uint event)
++{
++ if (!rfcomm_thread)
++ return;
++ set_bit(RFCOMM_SCHED_WAKEUP, &rfcomm_event);
++ wake_up_process(rfcomm_thread);
++}
++
++extern struct semaphore rfcomm_sem;
++#define rfcomm_lock() down(&rfcomm_sem);
++#define rfcomm_unlock() up(&rfcomm_sem);
++
++/* ---- RFCOMM DLCs (channels) ---- */
++struct rfcomm_dlc *rfcomm_dlc_alloc(int prio);
++void rfcomm_dlc_free(struct rfcomm_dlc *d);
++int rfcomm_dlc_open(struct rfcomm_dlc *d, bdaddr_t *src, bdaddr_t *dst, u8 channel);
++int rfcomm_dlc_close(struct rfcomm_dlc *d, int reason);
++int rfcomm_dlc_send(struct rfcomm_dlc *d, struct sk_buff *skb);
++int rfcomm_dlc_set_modem_status(struct rfcomm_dlc *d, u8 v24_sig);
++int rfcomm_dlc_get_modem_status(struct rfcomm_dlc *d, u8 *v24_sig);
++
++#define rfcomm_dlc_lock(d) spin_lock(&d->lock)
++#define rfcomm_dlc_unlock(d) spin_unlock(&d->lock)
++
++static inline void rfcomm_dlc_hold(struct rfcomm_dlc *d)
++{
++ atomic_inc(&d->refcnt);
++}
++
++static inline void rfcomm_dlc_put(struct rfcomm_dlc *d)
++{
++ if (atomic_dec_and_test(&d->refcnt))
++ rfcomm_dlc_free(d);
++}
++
++extern void FASTCALL(__rfcomm_dlc_throttle(struct rfcomm_dlc *d));
++extern void FASTCALL(__rfcomm_dlc_unthrottle(struct rfcomm_dlc *d));
++
++static inline void rfcomm_dlc_throttle(struct rfcomm_dlc *d)
++{
++ if (!test_and_set_bit(RFCOMM_RX_THROTTLED, &d->flags))
++ __rfcomm_dlc_throttle(d);
++}
++
++static inline void rfcomm_dlc_unthrottle(struct rfcomm_dlc *d)
++{
++ if (test_and_clear_bit(RFCOMM_RX_THROTTLED, &d->flags))
++ __rfcomm_dlc_unthrottle(d);
++}
++
++/* ---- RFCOMM sessions ---- */
++struct rfcomm_session *rfcomm_session_add(struct socket *sock, int state);
++struct rfcomm_session *rfcomm_session_get(bdaddr_t *src, bdaddr_t *dst);
++struct rfcomm_session *rfcomm_session_create(bdaddr_t *src, bdaddr_t *dst, int *err);
++void rfcomm_session_del(struct rfcomm_session *s);
++void rfcomm_session_close(struct rfcomm_session *s, int err);
++void rfcomm_session_getaddr(struct rfcomm_session *s, bdaddr_t *src, bdaddr_t *dst);
++
++static inline void rfcomm_session_hold(struct rfcomm_session *s)
++{
++ atomic_inc(&s->refcnt);
++}
++
++static inline void rfcomm_session_put(struct rfcomm_session *s)
++{
++ if (atomic_dec_and_test(&s->refcnt))
++ rfcomm_session_del(s);
++}
++
++/* ---- RFCOMM chechsum ---- */
++extern u8 rfcomm_crc_table[];
++
++/* ---- RFCOMM sockets ---- */
++struct sockaddr_rc {
++ sa_family_t rc_family;
++ bdaddr_t rc_bdaddr;
++ u8 rc_channel;
++};
++
++#define rfcomm_pi(sk) ((struct rfcomm_pinfo *) &sk->tp_pinfo)
++
++struct rfcomm_pinfo {
++ struct rfcomm_dlc *dlc;
++ u8 channel;
++};
++
++int rfcomm_init_sockets(void);
++void rfcomm_cleanup_sockets(void);
++
++int rfcomm_connect_ind(struct rfcomm_session *s, u8 channel, struct rfcomm_dlc **d);
++
++/* ---- RFCOMM TTY ---- */
++#define RFCOMM_MAX_DEV 256
++
++#define RFCOMMCREATEDEV _IOW('R', 200, int)
++#define RFCOMMRELEASEDEV _IOW('R', 201, int)
++#define RFCOMMGETDEVLIST _IOR('R', 210, int)
++#define RFCOMMGETDEVINFO _IOR('R', 211, int)
++#define RFCOMMSTEALDLC _IOW('R', 220, int)
++
++#define RFCOMM_REUSE_DLC 0
++#define RFCOMM_RELEASE_ONHUP 1
++#define RFCOMM_HANGUP_NOW 2
++#define RFCOMM_TTY_ATTACHED 3
++
++struct rfcomm_dev_req {
++ s16 dev_id;
++ u32 flags;
++ bdaddr_t src;
++ bdaddr_t dst;
++ u8 channel;
++};
++
++struct rfcomm_dev_info {
++ s16 id;
++ u32 flags;
++ u16 state;
++ bdaddr_t src;
++ bdaddr_t dst;
++ u8 channel;
++};
++
++struct rfcomm_dev_list_req {
++ u16 dev_num;
++ struct rfcomm_dev_info dev_info[0];
++};
++
++int rfcomm_dev_ioctl(struct sock *sk, unsigned int cmd, unsigned long arg);
++int rfcomm_init_ttys(void);
++void rfcomm_cleanup_ttys(void);
++
++#endif /* __RFCOMM_H */
+diff -urN linux-2.4.18/include/net/bluetooth/sco.h linux-2.4.18-mh15/include/net/bluetooth/sco.h
+--- linux-2.4.18/include/net/bluetooth/sco.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/include/net/bluetooth/sco.h 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,81 @@
++/*
++ BlueZ - Bluetooth protocol stack for Linux
++ Copyright (C) 2000-2001 Qualcomm Incorporated
++
++ Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License version 2 as
++ published by the Free Software Foundation;
++
++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++
++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
++ SOFTWARE IS DISCLAIMED.
++*/
++
++/*
++ * $Id$
++ */
++
++#ifndef __SCO_H
++#define __SCO_H
++
++/* SCO defaults */
++#define SCO_DEFAULT_MTU 500
++#define SCO_DEFAULT_FLUSH_TO 0xFFFF
++
++#define SCO_CONN_TIMEOUT (HZ * 40)
++#define SCO_DISCONN_TIMEOUT (HZ * 2)
++#define SCO_CONN_IDLE_TIMEOUT (HZ * 60)
++
++/* SCO socket address */
++struct sockaddr_sco {
++ sa_family_t sco_family;
++ bdaddr_t sco_bdaddr;
++};
++
++/* set/get sockopt defines */
++#define SCO_OPTIONS 0x01
++struct sco_options {
++ __u16 mtu;
++};
++
++#define SCO_CONNINFO 0x02
++struct sco_conninfo {
++ __u16 hci_handle;
++};
++
++/* ---- SCO connections ---- */
++struct sco_conn {
++ struct hci_conn *hcon;
++
++ bdaddr_t *dst;
++ bdaddr_t *src;
++
++ spinlock_t lock;
++ struct sock *sk;
++
++ unsigned int mtu;
++};
++
++#define sco_conn_lock(c) spin_lock(&c->lock);
++#define sco_conn_unlock(c) spin_unlock(&c->lock);
++
++/* ----- SCO socket info ----- */
++#define sco_pi(sk) ((struct sco_pinfo *) &sk->tp_pinfo)
++
++struct sco_pinfo {
++ __u32 flags;
++ struct sco_conn *conn;
++};
++
++#endif /* __SCO_H */
+diff -urN linux-2.4.18/include/pcmcia/ciscode.h linux-2.4.18-mh15/include/pcmcia/ciscode.h
+--- linux-2.4.18/include/pcmcia/ciscode.h 2001-12-21 18:42:04.000000000 +0100
++++ linux-2.4.18-mh15/include/pcmcia/ciscode.h 2004-08-01 16:26:23.000000000 +0200
+@@ -1,5 +1,5 @@
+ /*
+- * ciscode.h 1.48 2001/08/24 12:16:12
++ * ciscode.h 1.57 2002/11/03 20:38:14
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+@@ -60,6 +60,10 @@
+ #define PRODID_INTEL_DUAL_RS232 0x0301
+ #define PRODID_INTEL_2PLUS 0x8422
+
++#define MANFID_KME 0x0032
++#define PRODID_KME_KXLC005_A 0x0704
++#define PRODID_KME_KXLC005_B 0x2904
++
+ #define MANFID_LINKSYS 0x0143
+ #define PRODID_LINKSYS_PCMLM28 0xc0ab
+ #define PRODID_LINKSYS_3400 0x3341
+@@ -94,6 +98,8 @@
+ #define PRODID_OSITECH_JACK_336 0x0007
+ #define PRODID_OSITECH_SEVEN 0x0008
+
++#define MANFID_OXSEMI 0x0279
++
+ #define MANFID_PIONEER 0x000b
+
+ #define MANFID_PSION 0x016c
+@@ -103,6 +109,7 @@
+ #define PRODID_QUATECH_SPP100 0x0003
+ #define PRODID_QUATECH_DUAL_RS232 0x0012
+ #define PRODID_QUATECH_DUAL_RS232_D1 0x0007
++#define PRODID_QUATECH_DUAL_RS232_D2 0x0052
+ #define PRODID_QUATECH_QUAD_RS232 0x001b
+ #define PRODID_QUATECH_DUAL_RS422 0x000e
+ #define PRODID_QUATECH_QUAD_RS422 0x0045
+@@ -120,9 +127,12 @@
+
+ #define MANFID_TDK 0x0105
+ #define PRODID_TDK_CF010 0x0900
++#define PRODID_TDK_GN3410 0x4815
+
+ #define MANFID_TOSHIBA 0x0098
+
++#define MANFID_UNGERMANN 0x02c0
++
+ #define MANFID_XIRCOM 0x0105
+
+ #endif /* _LINUX_CISCODE_H */
+diff -urN linux-2.4.18/kernel/ksyms.c linux-2.4.18-mh15/kernel/ksyms.c
+--- linux-2.4.18/kernel/ksyms.c 2002-02-25 20:38:13.000000000 +0100
++++ linux-2.4.18-mh15/kernel/ksyms.c 2004-08-01 16:26:23.000000000 +0200
+@@ -47,6 +47,7 @@
+ #include <linux/in6.h>
+ #include <linux/completion.h>
+ #include <linux/seq_file.h>
++#include <linux/firmware.h>
+ #include <asm/checksum.h>
+
+ #if defined(CONFIG_PROC_FS)
+@@ -538,6 +539,13 @@
+ EXPORT_SYMBOL(strspn);
+ EXPORT_SYMBOL(strsep);
+
++#ifdef CONFIG_FW_LOADER
++EXPORT_SYMBOL(release_firmware);
++EXPORT_SYMBOL(request_firmware);
++EXPORT_SYMBOL(request_firmware_nowait);
++EXPORT_SYMBOL(register_firmware);
++#endif
++
+ /* software interrupts */
+ EXPORT_SYMBOL(tasklet_hi_vec);
+ EXPORT_SYMBOL(tasklet_vec);
+diff -urN linux-2.4.18/lib/Config.in linux-2.4.18-mh15/lib/Config.in
+--- linux-2.4.18/lib/Config.in 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/lib/Config.in 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,12 @@
++#
++# Library configuration
++#
++mainmenu_option next_comment
++comment 'Library routines'
++
++if [ "$CONFIG_EXPERIMENTAL" = "y" -a \
++ "$CONFIG_HOTPLUG" = "y" ]; then
++ tristate 'Hotplug firmware loading support (EXPERIMENTAL)' CONFIG_FW_LOADER
++fi
++
++endmenu
+diff -urN linux-2.4.18/lib/firmware_class.c linux-2.4.18-mh15/lib/firmware_class.c
+--- linux-2.4.18/lib/firmware_class.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/lib/firmware_class.c 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,573 @@
++/*
++ * firmware_class.c - Multi purpose firmware loading support
++ *
++ * Copyright (c) 2003 Manuel Estrada Sainz <ranty@debian.org>
++ *
++ * Please see Documentation/firmware_class/ for more information.
++ *
++ */
++/*
++ * Based on kernel/kmod.c and drivers/usb/usb.c
++ */
++/*
++ kernel/kmod.c
++ Kirk Petersen
++
++ Reorganized not to be a daemon by Adam Richter, with guidance
++ from Greg Zornetzer.
++
++ Modified to avoid chroot and file sharing problems.
++ Mikael Pettersson
++
++ Limit the concurrent number of kmod modprobes to catch loops from
++ "modprobe needs a service that is in a module".
++ Keith Owens <kaos@ocs.com.au> December 1999
++
++ Unblock all signals when we exec a usermode process.
++ Shuu Yamaguchi <shuu@wondernetworkresources.com> December 2000
++*/
++/*
++ * drivers/usb/usb.c
++ *
++ * (C) Copyright Linus Torvalds 1999
++ * (C) Copyright Johannes Erdfelt 1999-2001
++ * (C) Copyright Andreas Gal 1999
++ * (C) Copyright Gregory P. Smith 1999
++ * (C) Copyright Deti Fliegl 1999 (new USB architecture)
++ * (C) Copyright Randy Dunlap 2000
++ * (C) Copyright David Brownell 2000 (kernel hotplug, usb_device_id)
++ * (C) Copyright Yggdrasil Computing, Inc. 2000
++ * (usb_device_id matching changes by Adam J. Richter)
++ */
++
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/string.h>
++#include <linux/types.h>
++#include <linux/init.h>
++#include <linux/slab.h>
++#include <linux/kmod.h>
++#include <linux/proc_fs.h>
++#include <linux/vmalloc.h>
++#include <asm/hardirq.h>
++
++#include "linux/firmware.h"
++
++MODULE_AUTHOR("Manuel Estrada Sainz <ranty@debian.org>");
++MODULE_DESCRIPTION("Multi purpose firmware loading support");
++MODULE_LICENSE("GPL");
++
++#define err(format, arg...) \
++ printk(KERN_ERR "%s:%s: " format "\n",__FILE__, __FUNCTION__ , ## arg)
++#define warn(format, arg...) \
++ printk(KERN_WARNING "%s:%s: " format "\n",__FILE__, __FUNCTION__ , ## arg)
++#define dbg(format, arg...) \
++ printk(KERN_DEBUG "%s:%s: " format "\n",__FILE__, __FUNCTION__ , ## arg)
++
++static int loading_timeout = 10; /* In seconds */
++static struct proc_dir_entry *proc_dir_timeout;
++static struct proc_dir_entry *proc_dir;
++
++#ifdef CONFIG_HOTPLUG
++
++static int
++call_helper(char *verb, const char *name, const char *device)
++{
++ char *argv[3], **envp, *buf, *scratch;
++ int i = 0;
++
++ int retval = 0;
++
++ if (!hotplug_path[0])
++ return -ENOENT;
++ if (in_interrupt()) {
++ err("in_interrupt");
++ return -EFAULT;
++ }
++ if (!current->fs->root) {
++ warn("call_policy %s -- no FS yet", verb);
++ return -EPERM;
++ }
++
++ if (!(envp = (char **) kmalloc(20 * sizeof (char *), GFP_KERNEL))) {
++ err("unable to allocate envp");
++ return -ENOMEM;
++ }
++ if (!(buf = kmalloc(256, GFP_KERNEL))) {
++ kfree(envp);
++ err("unable to allocate buf");
++ return -ENOMEM;
++ }
++
++ /* only one standardized param to hotplug command: type */
++ argv[0] = hotplug_path;
++ argv[1] = "firmware";
++ argv[2] = 0;
++
++ /* minimal command environment */
++ envp[i++] = "HOME=/";
++ envp[i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
++
++#ifdef DEBUG
++ /* hint that policy agent should enter no-stdout debug mode */
++ envp[i++] = "DEBUG=kernel";
++#endif
++ scratch = buf;
++
++ if (device) {
++ envp[i++] = scratch;
++ scratch += snprintf(scratch, FIRMWARE_NAME_MAX+25,
++ "DEVPATH=/driver/firmware/%s", device) + 1;
++ }
++
++ envp[i++] = scratch;
++ scratch += sprintf(scratch, "ACTION=%s", verb) + 1;
++
++ envp[i++] = scratch;
++ scratch += snprintf(scratch, FIRMWARE_NAME_MAX,
++ "FIRMWARE=%s", name) + 1;
++
++ envp[i++] = 0;
++
++#ifdef DEBUG
++ dbg("firmware: %s %s %s", argv[0], argv[1], verb);
++#endif
++
++ retval = call_usermodehelper(argv[0], argv, envp);
++ if (retval) {
++ printk("call_usermodehelper return %d\n", retval);
++ }
++
++ kfree(buf);
++ kfree(envp);
++ return retval;
++}
++#else
++
++static inline int
++call_helper(char *verb, const char *name, const char *device)
++{
++ return -ENOENT;
++}
++
++#endif /* CONFIG_HOTPLUG */
++
++struct firmware_priv {
++ struct completion completion;
++ struct proc_dir_entry *proc_dir;
++ struct proc_dir_entry *attr_data;
++ struct proc_dir_entry *attr_loading;
++ struct firmware *fw;
++ int loading;
++ int abort;
++ int alloc_size;
++ struct timer_list timeout;
++};
++
++static int
++firmware_timeout_show(char *buf, char **start, off_t off,
++ int count, int *eof, void *data)
++{
++ return sprintf(buf, "%d\n", loading_timeout);
++}
++
++/**
++ * firmware_timeout_store:
++ * Description:
++ * Sets the number of seconds to wait for the firmware. Once
++ * this expires an error will be return to the driver and no
++ * firmware will be provided.
++ *
++ * Note: zero means 'wait for ever'
++ *
++ **/
++static int
++firmware_timeout_store(struct file *file, const char *buf,
++ unsigned long count, void *data)
++{
++ loading_timeout = simple_strtol(buf, NULL, 10);
++ return count;
++}
++
++static int
++firmware_loading_show(char *buf, char **start, off_t off,
++ int count, int *eof, void *data)
++{
++ struct firmware_priv *fw_priv = data;
++ return sprintf(buf, "%d\n", fw_priv->loading);
++}
++
++/**
++ * firmware_loading_store: - loading control file
++ * Description:
++ * The relevant values are:
++ *
++ * 1: Start a load, discarding any previous partial load.
++ * 0: Conclude the load and handle the data to the driver code.
++ * -1: Conclude the load with an error and discard any written data.
++ **/
++static int
++firmware_loading_store(struct file *file, const char *buf,
++ unsigned long count, void *data)
++{
++ struct firmware_priv *fw_priv = data;
++ int prev_loading = fw_priv->loading;
++
++ fw_priv->loading = simple_strtol(buf, NULL, 10);
++
++ switch (fw_priv->loading) {
++ case -1:
++ fw_priv->abort = 1;
++ wmb();
++ complete(&fw_priv->completion);
++ break;
++ case 1:
++ kfree(fw_priv->fw->data);
++ fw_priv->fw->data = NULL;
++ fw_priv->fw->size = 0;
++ fw_priv->alloc_size = 0;
++ break;
++ case 0:
++ if (prev_loading == 1)
++ complete(&fw_priv->completion);
++ break;
++ }
++
++ return count;
++}
++
++static int
++firmware_data_read(char *buffer, char **start, off_t offset,
++ int count, int *eof, void *data)
++{
++ struct firmware_priv *fw_priv = data;
++ struct firmware *fw = fw_priv->fw;
++
++ if (offset > fw->size)
++ return 0;
++ if (offset + count > fw->size)
++ count = fw->size - offset;
++
++ memcpy(buffer, fw->data + offset, count);
++ *start = (void *) ((long) count);
++ return count;
++}
++static int
++fw_realloc_buffer(struct firmware_priv *fw_priv, int min_size)
++{
++ u8 *new_data;
++ int new_size;
++
++ if (min_size <= fw_priv->alloc_size)
++ return 0;
++ if((min_size % PAGE_SIZE) == 0)
++ new_size = min_size;
++ else
++ new_size = (min_size + PAGE_SIZE) & PAGE_MASK;
++ new_data = vmalloc(new_size);
++ if (!new_data) {
++ printk(KERN_ERR "%s: unable to alloc buffer\n", __FUNCTION__);
++ /* Make sure that we don't keep incomplete data */
++ fw_priv->abort = 1;
++ return -ENOMEM;
++ }
++ fw_priv->alloc_size = new_size;
++ if (fw_priv->fw->data) {
++ memcpy(new_data, fw_priv->fw->data, fw_priv->fw->size);
++ vfree(fw_priv->fw->data);
++ }
++ fw_priv->fw->data = new_data;
++ BUG_ON(min_size > fw_priv->alloc_size);
++ return 0;
++}
++
++/**
++ * firmware_data_write:
++ *
++ * Description:
++ *
++ * Data written to the 'data' attribute will be later handled to
++ * the driver as a firmware image.
++ **/
++static int
++firmware_data_write(struct file *file, const char *buffer,
++ unsigned long count, void *data)
++{
++ struct firmware_priv *fw_priv = data;
++ struct firmware *fw = fw_priv->fw;
++ int offset = file->f_pos;
++ int retval;
++
++ retval = fw_realloc_buffer(fw_priv, offset + count);
++ if (retval) {
++ printk("%s: retval:%d\n", __FUNCTION__, retval);
++ return retval;
++ }
++
++ memcpy(fw->data + offset, buffer, count);
++
++ fw->size = max_t(size_t, offset + count, fw->size);
++ file->f_pos += count;
++ return count;
++}
++
++static void
++firmware_class_timeout(u_long data)
++{
++ struct firmware_priv *fw_priv = (struct firmware_priv *) data;
++ fw_priv->abort = 1;
++ wmb();
++ complete(&fw_priv->completion);
++}
++static int
++fw_setup_class_device(struct firmware_priv **fw_priv_p,
++ const char *fw_name, const char *device)
++{
++ int retval;
++ struct firmware_priv *fw_priv = kmalloc(sizeof (struct firmware_priv),
++ GFP_KERNEL);
++ *fw_priv_p = fw_priv;
++ if (!fw_priv) {
++ retval = -ENOMEM;
++ goto out;
++ }
++ memset(fw_priv, 0, sizeof (*fw_priv));
++
++ init_completion(&fw_priv->completion);
++
++ fw_priv->timeout.function = firmware_class_timeout;
++ fw_priv->timeout.data = (u_long) fw_priv;
++ init_timer(&fw_priv->timeout);
++
++ retval = -EAGAIN;
++ fw_priv->proc_dir = create_proc_entry(device, 0644 | S_IFDIR, proc_dir);
++ if (!fw_priv->proc_dir)
++ goto err_free_fw_priv;
++
++ fw_priv->attr_data = create_proc_entry("data", 0644 | S_IFREG,
++ fw_priv->proc_dir);
++ if (!fw_priv->attr_data)
++ goto err_remove_dir;
++
++ fw_priv->attr_data->read_proc = firmware_data_read;
++ fw_priv->attr_data->write_proc = firmware_data_write;
++ fw_priv->attr_data->data = fw_priv;
++
++ fw_priv->attr_loading = create_proc_entry("loading", 0644 | S_IFREG,
++ fw_priv->proc_dir);
++ if (!fw_priv->attr_loading)
++ goto err_remove_data;
++
++ fw_priv->attr_loading->read_proc = firmware_loading_show;
++ fw_priv->attr_loading->write_proc = firmware_loading_store;
++ fw_priv->attr_loading->data = fw_priv;
++
++ retval = 0;
++ fw_priv->fw = kmalloc(sizeof (struct firmware), GFP_KERNEL);
++ if (!fw_priv->fw) {
++ printk(KERN_ERR "%s: kmalloc(struct firmware) failed\n",
++ __FUNCTION__);
++ retval = -ENOMEM;
++ goto err_remove_loading;
++ }
++ memset(fw_priv->fw, 0, sizeof (*fw_priv->fw));
++
++ goto out;
++
++err_remove_loading:
++ remove_proc_entry("loading", fw_priv->proc_dir);
++err_remove_data:
++ remove_proc_entry("data", fw_priv->proc_dir);
++err_remove_dir:
++ remove_proc_entry(device, proc_dir);
++err_free_fw_priv:
++ kfree(fw_priv);
++out:
++ return retval;
++}
++static void
++fw_remove_class_device(struct firmware_priv *fw_priv)
++{
++ remove_proc_entry("loading", fw_priv->proc_dir);
++ remove_proc_entry("data", fw_priv->proc_dir);
++ remove_proc_entry(fw_priv->proc_dir->name, proc_dir);
++}
++
++/**
++ * request_firmware: - request firmware to hotplug and wait for it
++ * Description:
++ * @firmware will be used to return a firmware image by the name
++ * of @name for device @device.
++ *
++ * Should be called from user context where sleeping is allowed.
++ *
++ * @name will be use as $FIRMWARE in the hotplug environment and
++ * should be distinctive enough not to be confused with any other
++ * firmware image for this or any other device.
++ **/
++int
++request_firmware(const struct firmware **firmware, const char *name,
++ const char *device)
++{
++ struct firmware_priv *fw_priv;
++ int retval;
++
++ if (!firmware) {
++ retval = -EINVAL;
++ goto out;
++ }
++ *firmware = NULL;
++
++ retval = fw_setup_class_device(&fw_priv, name, device);
++ if (retval)
++ goto out;
++
++ retval = call_helper("add", name, device);
++ if (retval)
++ goto out;
++ if (loading_timeout) {
++ fw_priv->timeout.expires = jiffies + loading_timeout * HZ;
++ add_timer(&fw_priv->timeout);
++ }
++
++ wait_for_completion(&fw_priv->completion);
++
++ del_timer(&fw_priv->timeout);
++ fw_remove_class_device(fw_priv);
++
++ if (fw_priv->fw->size && !fw_priv->abort) {
++ *firmware = fw_priv->fw;
++ } else {
++ retval = -ENOENT;
++ vfree(fw_priv->fw->data);
++ kfree(fw_priv->fw);
++ }
++out:
++ kfree(fw_priv);
++ return retval;
++}
++
++void
++release_firmware(const struct firmware *fw)
++{
++ if (fw) {
++ vfree(fw->data);
++ kfree(fw);
++ }
++}
++
++/**
++ * register_firmware: - provide a firmware image for later usage
++ *
++ * Description:
++ * Make sure that @data will be available by requesting firmware @name.
++ *
++ * Note: This will not be possible until some kind of persistence
++ * is available.
++ **/
++void
++register_firmware(const char *name, const u8 *data, size_t size)
++{
++ /* This is meaningless without firmware caching, so until we
++ * decide if firmware caching is reasonable just leave it as a
++ * noop */
++}
++
++/* Async support */
++struct firmware_work {
++ struct tq_struct work;
++ struct module *module;
++ const char *name;
++ const char *device;
++ void *context;
++ void (*cont)(const struct firmware *fw, void *context);
++};
++
++static void
++request_firmware_work_func(void *arg)
++{
++ struct firmware_work *fw_work = arg;
++ const struct firmware *fw;
++ if (!arg)
++ return;
++ request_firmware(&fw, fw_work->name, fw_work->device);
++ fw_work->cont(fw, fw_work->context);
++ release_firmware(fw);
++ __MOD_DEC_USE_COUNT(fw_work->module);
++ kfree(fw_work);
++}
++
++/**
++ * request_firmware_nowait:
++ *
++ * Description:
++ * Asynchronous variant of request_firmware() for contexts where
++ * it is not possible to sleep.
++ *
++ * @cont will be called asynchronously when the firmware request is over.
++ *
++ * @context will be passed over to @cont.
++ *
++ * @fw may be %NULL if firmware request fails.
++ *
++ **/
++int
++request_firmware_nowait(
++ struct module *module,
++ const char *name, const char *device, void *context,
++ void (*cont)(const struct firmware *fw, void *context))
++{
++ struct firmware_work *fw_work = kmalloc(sizeof (struct firmware_work),
++ GFP_ATOMIC);
++ if (!fw_work)
++ return -ENOMEM;
++ if (!try_inc_mod_count(module)) {
++ kfree(fw_work);
++ return -EFAULT;
++ }
++
++ *fw_work = (struct firmware_work) {
++ .module = module,
++ .name = name,
++ .device = device,
++ .context = context,
++ .cont = cont,
++ };
++ INIT_TQUEUE(&fw_work->work, request_firmware_work_func, fw_work);
++
++ schedule_task(&fw_work->work);
++ return 0;
++}
++
++static int __init
++firmware_class_init(void)
++{
++ proc_dir = create_proc_entry("driver/firmware", 0755 | S_IFDIR, NULL);
++ if (!proc_dir)
++ return -EAGAIN;
++ proc_dir_timeout = create_proc_entry("timeout",
++ 0644 | S_IFREG, proc_dir);
++ if (!proc_dir_timeout) {
++ remove_proc_entry("driver/firmware", NULL);
++ return -EAGAIN;
++ }
++ proc_dir_timeout->read_proc = firmware_timeout_show;
++ proc_dir_timeout->write_proc = firmware_timeout_store;
++ return 0;
++}
++static void __exit
++firmware_class_exit(void)
++{
++ remove_proc_entry("timeout", proc_dir);
++ remove_proc_entry("driver/firmware", NULL);
++}
++
++module_init(firmware_class_init);
++module_exit(firmware_class_exit);
++
++#ifndef CONFIG_FW_LOADER
++EXPORT_SYMBOL(release_firmware);
++EXPORT_SYMBOL(request_firmware);
++EXPORT_SYMBOL(request_firmware_nowait);
++EXPORT_SYMBOL(register_firmware);
++#endif
+diff -urN linux-2.4.18/lib/Makefile linux-2.4.18-mh15/lib/Makefile
+--- linux-2.4.18/lib/Makefile 2001-09-18 00:31:15.000000000 +0200
++++ linux-2.4.18-mh15/lib/Makefile 2004-08-01 16:26:23.000000000 +0200
+@@ -8,13 +8,17 @@
+
+ L_TARGET := lib.a
+
+-export-objs := cmdline.o dec_and_lock.o rwsem-spinlock.o rwsem.o
++export-objs := cmdline.o dec_and_lock.o rwsem-spinlock.o rwsem.o \
++ firmware_class.o
+
+ obj-y := errno.o ctype.o string.o vsprintf.o brlock.o cmdline.o bust_spinlocks.o rbtree.o
+
++obj-$(CONFIG_FW_LOADER) += firmware_class.o
+ obj-$(CONFIG_RWSEM_GENERIC_SPINLOCK) += rwsem-spinlock.o
+ obj-$(CONFIG_RWSEM_XCHGADD_ALGORITHM) += rwsem.o
+
++include $(TOPDIR)/drivers/bluetooth/Makefile.lib
++
+ ifneq ($(CONFIG_HAVE_DEC_LOCK),y)
+ obj-y += dec_and_lock.o
+ endif
+diff -urN linux-2.4.18/MAINTAINERS linux-2.4.18-mh15/MAINTAINERS
+--- linux-2.4.18/MAINTAINERS 2002-02-25 20:37:52.000000000 +0100
++++ linux-2.4.18-mh15/MAINTAINERS 2004-08-01 16:26:23.000000000 +0200
+@@ -252,10 +252,88 @@
+ L: linux-kernel@vger.kernel.org
+ S: Maintained
+
+-BLUETOOTH SUBSYSTEM (BlueZ)
++BLUETOOTH SUBSYSTEM
++P: Marcel Holtmann
++M: marcel@holtmann.org
+ P: Maxim Krasnyansky
+ M: maxk@qualcomm.com
++L: bluez-devel@lists.sf.net
+ W: http://bluez.sf.net
++W: http://www.bluez.org
++W: http://www.holtmann.org/linux/bluetooth/
++S: Maintained
++
++BLUETOOTH RFCOMM LAYER
++P: Marcel Holtmann
++M: marcel@holtmann.org
++P: Maxim Krasnyansky
++M: maxk@qualcomm.com
++S: Maintained
++
++BLUETOOTH BNEP LAYER
++P: Marcel Holtmann
++M: marcel@holtmann.org
++P: Maxim Krasnyansky
++M: maxk@qualcomm.com
++S: Maintained
++
++BLUETOOTH CMTP LAYER
++P: Marcel Holtmann
++M: marcel@holtmann.org
++S: Maintained
++
++BLUETOOTH HIDP LAYER
++P: Marcel Holtmann
++M: marcel@holtmann.org
++S: Maintained
++
++BLUETOOTH HCI UART DRIVER
++P: Marcel Holtmann
++M: marcel@holtmann.org
++P: Maxim Krasnyansky
++M: maxk@qualcomm.com
++S: Maintained
++
++BLUETOOTH HCI USB DRIVER
++P: Marcel Holtmann
++M: marcel@holtmann.org
++P: Maxim Krasnyansky
++M: maxk@qualcomm.com
++S: Maintained
++
++BLUETOOTH HCI BCM203X DRIVER
++P: Marcel Holtmann
++M: marcel@holtmann.org
++S: Maintained
++
++BLUETOOTH HCI BFUSB DRIVER
++P: Marcel Holtmann
++M: marcel@holtmann.org
++S: Maintained
++
++BLUETOOTH HCI DTL1 DRIVER
++P: Marcel Holtmann
++M: marcel@holtmann.org
++S: Maintained
++
++BLUETOOTH HCI BLUECARD DRIVER
++P: Marcel Holtmann
++M: marcel@holtmann.org
++S: Maintained
++
++BLUETOOTH HCI BT3C DRIVER
++P: Marcel Holtmann
++M: marcel@holtmann.org
++S: Maintained
++
++BLUETOOTH HCI BTUART DRIVER
++P: Marcel Holtmann
++M: marcel@holtmann.org
++S: Maintained
++
++BLUETOOTH HCI VHCI DRIVER
++P: Maxim Krasnyansky
++M: maxk@qualcomm.com
+ S: Maintained
+
+ BTTV VIDEO4LINUX DRIVER
+diff -urN linux-2.4.18/net/bluetooth/af_bluetooth.c linux-2.4.18-mh15/net/bluetooth/af_bluetooth.c
+--- linux-2.4.18/net/bluetooth/af_bluetooth.c 2001-09-07 18:28:38.000000000 +0200
++++ linux-2.4.18-mh15/net/bluetooth/af_bluetooth.c 2004-08-01 16:26:23.000000000 +0200
+@@ -25,14 +25,15 @@
+ /*
+ * BlueZ Bluetooth address family and sockets.
+ *
+- * $Id$
++ * $Id$
+ */
+-#define VERSION "1.1"
++#define VERSION "2.4"
+
+ #include <linux/config.h>
+ #include <linux/module.h>
+
+ #include <linux/types.h>
++#include <linux/list.h>
+ #include <linux/errno.h>
+ #include <linux/kernel.h>
+ #include <linux/major.h>
+@@ -40,6 +41,7 @@
+ #include <linux/slab.h>
+ #include <linux/skbuff.h>
+ #include <linux/init.h>
++#include <linux/poll.h>
+ #include <linux/proc_fs.h>
+ #include <net/sock.h>
+
+@@ -48,70 +50,79 @@
+ #endif
+
+ #include <net/bluetooth/bluetooth.h>
+-#include <net/bluetooth/bluez.h>
++
++#ifndef AF_BLUETOOTH_DEBUG
++#undef BT_DBG
++#define BT_DBG( A... )
++#endif
+
+ /* Bluetooth sockets */
+-static struct net_proto_family *bluez_sock[BLUEZ_MAX_PROTO];
++#define BLUEZ_MAX_PROTO 7
++static struct net_proto_family *bluez_proto[BLUEZ_MAX_PROTO];
+
+ int bluez_sock_register(int proto, struct net_proto_family *ops)
+ {
+- if (proto > BLUEZ_MAX_PROTO)
++ if (proto >= BLUEZ_MAX_PROTO)
+ return -EINVAL;
+
+- if (bluez_sock[proto])
++ if (bluez_proto[proto])
+ return -EEXIST;
+
+- bluez_sock[proto] = ops;
++ bluez_proto[proto] = ops;
+ return 0;
+ }
+
+ int bluez_sock_unregister(int proto)
+ {
+- if (proto > BLUEZ_MAX_PROTO)
++ if (proto >= BLUEZ_MAX_PROTO)
+ return -EINVAL;
+
+- if (!bluez_sock[proto])
++ if (!bluez_proto[proto])
+ return -ENOENT;
+
+- bluez_sock[proto] = NULL;
++ bluez_proto[proto] = NULL;
+ return 0;
+ }
+
+ static int bluez_sock_create(struct socket *sock, int proto)
+ {
+- if (proto > BLUEZ_MAX_PROTO)
++ if (proto >= BLUEZ_MAX_PROTO)
+ return -EINVAL;
+
+ #if defined(CONFIG_KMOD)
+- if (!bluez_sock[proto]) {
++ if (!bluez_proto[proto]) {
+ char module_name[30];
+ sprintf(module_name, "bt-proto-%d", proto);
+ request_module(module_name);
+ }
+ #endif
+
+- if (!bluez_sock[proto])
++ if (!bluez_proto[proto])
+ return -ENOENT;
+
+- return bluez_sock[proto]->create(sock, proto);
++ return bluez_proto[proto]->create(sock, proto);
++}
++
++void bluez_sock_init(struct socket *sock, struct sock *sk)
++{
++ sock_init_data(sock, sk);
++ INIT_LIST_HEAD(&bluez_pi(sk)->accept_q);
+ }
+
+ void bluez_sock_link(struct bluez_sock_list *l, struct sock *sk)
+ {
+- write_lock(&l->lock);
+-
++ write_lock_bh(&l->lock);
+ sk->next = l->head;
+ l->head = sk;
+ sock_hold(sk);
+-
+- write_unlock(&l->lock);
++ write_unlock_bh(&l->lock);
+ }
+
+ void bluez_sock_unlink(struct bluez_sock_list *l, struct sock *sk)
+ {
+ struct sock **skp;
+
+- write_lock(&l->lock);
++ write_lock_bh(&l->lock);
+ for (skp = &l->head; *skp; skp = &((*skp)->next)) {
+ if (*skp == sk) {
+ *skp = sk->next;
+@@ -119,7 +130,163 @@
+ break;
+ }
+ }
+- write_unlock(&l->lock);
++ write_unlock_bh(&l->lock);
++}
++
++void bluez_accept_enqueue(struct sock *parent, struct sock *sk)
++{
++ BT_DBG("parent %p, sk %p", parent, sk);
++
++ sock_hold(sk);
++ list_add_tail(&bluez_pi(sk)->accept_q, &bluez_pi(parent)->accept_q);
++ bluez_pi(sk)->parent = parent;
++ parent->ack_backlog++;
++}
++
++static void bluez_accept_unlink(struct sock *sk)
++{
++ BT_DBG("sk %p state %d", sk, sk->state);
++
++ list_del_init(&bluez_pi(sk)->accept_q);
++ bluez_pi(sk)->parent->ack_backlog--;
++ bluez_pi(sk)->parent = NULL;
++ sock_put(sk);
++}
++
++struct sock *bluez_accept_dequeue(struct sock *parent, struct socket *newsock)
++{
++ struct list_head *p, *n;
++ struct bluez_pinfo *pi;
++ struct sock *sk;
++
++ BT_DBG("parent %p", parent);
++
++ list_for_each_safe(p, n, &bluez_pi(parent)->accept_q) {
++ pi = list_entry(p, struct bluez_pinfo, accept_q);
++ sk = bluez_sk(pi);
++
++ lock_sock(sk);
++ if (sk->state == BT_CLOSED) {
++ release_sock(sk);
++ bluez_accept_unlink(sk);
++ continue;
++ }
++
++ if (sk->state == BT_CONNECTED || !newsock) {
++ bluez_accept_unlink(sk);
++ if (newsock)
++ sock_graft(sk, newsock);
++ release_sock(sk);
++ return sk;
++ }
++ release_sock(sk);
++ }
++ return NULL;
++}
++
++int bluez_sock_recvmsg(struct socket *sock, struct msghdr *msg, int len, int flags, struct scm_cookie *scm)
++{
++ int noblock = flags & MSG_DONTWAIT;
++ struct sock *sk = sock->sk;
++ struct sk_buff *skb;
++ int copied, err;
++
++ BT_DBG("sock %p sk %p len %d", sock, sk, len);
++
++ if (flags & (MSG_OOB))
++ return -EOPNOTSUPP;
++
++ if (!(skb = skb_recv_datagram(sk, flags, noblock, &err))) {
++ if (sk->shutdown & RCV_SHUTDOWN)
++ return 0;
++ return err;
++ }
++
++ msg->msg_namelen = 0;
++
++ copied = skb->len;
++ if (len < copied) {
++ msg->msg_flags |= MSG_TRUNC;
++ copied = len;
++ }
++
++ skb->h.raw = skb->data;
++ err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
++
++ skb_free_datagram(sk, skb);
++
++ return err ? : copied;
++}
++
++unsigned int bluez_sock_poll(struct file * file, struct socket *sock, poll_table *wait)
++{
++ struct sock *sk = sock->sk;
++ unsigned int mask = 0;
++
++ BT_DBG("sock %p, sk %p", sock, sk);
++
++ poll_wait(file, sk->sleep, wait);
++
++ if (sk->err || !skb_queue_empty(&sk->error_queue))
++ mask |= POLLERR;
++
++ if (sk->shutdown == SHUTDOWN_MASK)
++ mask |= POLLHUP;
++
++ if (!skb_queue_empty(&sk->receive_queue) ||
++ !list_empty(&bluez_pi(sk)->accept_q) ||
++ (sk->shutdown & RCV_SHUTDOWN))
++ mask |= POLLIN | POLLRDNORM;
++
++ if (sk->state == BT_CLOSED)
++ mask |= POLLHUP;
++
++ if (sk->state == BT_CONNECT ||
++ sk->state == BT_CONNECT2 ||
++ sk->state == BT_CONFIG)
++ return mask;
++
++ if (sock_writeable(sk))
++ mask |= POLLOUT | POLLWRNORM | POLLWRBAND;
++ else
++ set_bit(SOCK_ASYNC_NOSPACE, &sk->socket->flags);
++
++ return mask;
++}
++
++int bluez_sock_wait_state(struct sock *sk, int state, unsigned long timeo)
++{
++ DECLARE_WAITQUEUE(wait, current);
++ int err = 0;
++
++ BT_DBG("sk %p", sk);
++
++ add_wait_queue(sk->sleep, &wait);
++ while (sk->state != state) {
++ set_current_state(TASK_INTERRUPTIBLE);
++
++ if (!timeo) {
++ err = -EAGAIN;
++ break;
++ }
++
++ if (signal_pending(current)) {
++ err = sock_intr_errno(timeo);
++ break;
++ }
++
++ release_sock(sk);
++ timeo = schedule_timeout(timeo);
++ lock_sock(sk);
++
++ if (sk->err) {
++ err = sock_error(sk);
++ break;
++ }
++ }
++ set_current_state(TASK_RUNNING);
++ remove_wait_queue(sk->sleep, &wait);
++ return err;
+ }
+
+ struct net_proto_family bluez_sock_family_ops =
+@@ -129,9 +296,9 @@
+
+ int bluez_init(void)
+ {
+- INF("BlueZ HCI Core ver %s Copyright (C) 2000,2001 Qualcomm Inc",
++ BT_INFO("BlueZ Core ver %s Copyright (C) 2000,2001 Qualcomm Inc",
+ VERSION);
+- INF("Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>");
++ BT_INFO("Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>");
+
+ proc_mkdir("bluetooth", NULL);
+
+@@ -164,5 +331,6 @@
+ module_exit(bluez_cleanup);
+
+ MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>");
+-MODULE_DESCRIPTION("BlueZ HCI Core ver " VERSION);
++MODULE_DESCRIPTION("BlueZ Core ver " VERSION);
++MODULE_LICENSE("GPL");
+ #endif
+diff -urN linux-2.4.18/net/bluetooth/bnep/bnep.h linux-2.4.18-mh15/net/bluetooth/bnep/bnep.h
+--- linux-2.4.18/net/bluetooth/bnep/bnep.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/net/bluetooth/bnep/bnep.h 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,185 @@
++/*
++ BNEP protocol definition for Linux Bluetooth stack (BlueZ).
++ Copyright (C) 2002 Maxim Krasnyansky <maxk@qualcomm.com>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License, version 2, as
++ published by the Free Software Foundation.
++
++ This program is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ GNU General Public License for more details.
++
++ You should have received a copy of the GNU General Public License
++ along with this program; if not, write to the Free Software
++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++*/
++
++/*
++ * $Id$
++ */
++
++#ifndef _BNEP_H
++#define _BNEP_H
++
++#include <linux/types.h>
++#include <net/bluetooth/bluetooth.h>
++
++#include "crc32.h"
++
++// Limits
++#define BNEP_MAX_PROTO_FILTERS 5
++#define BNEP_MAX_MULTICAST_FILTERS 20
++
++// UUIDs
++#define BNEP_BASE_UUID 0x0000000000001000800000805F9B34FB
++#define BNEP_UUID16 0x02
++#define BNEP_UUID32 0x04
++#define BNEP_UUID128 0x16
++
++#define BNEP_SVC_PANU 0x1115
++#define BNEP_SVC_NAP 0x1116
++#define BNEP_SVC_GN 0x1117
++
++// Packet types
++#define BNEP_GENERAL 0x00
++#define BNEP_CONTROL 0x01
++#define BNEP_COMPRESSED 0x02
++#define BNEP_COMPRESSED_SRC_ONLY 0x03
++#define BNEP_COMPRESSED_DST_ONLY 0x04
++
++// Control types
++#define BNEP_CMD_NOT_UNDERSTOOD 0x00
++#define BNEP_SETUP_CONN_REQ 0x01
++#define BNEP_SETUP_CONN_RSP 0x02
++#define BNEP_FILTER_NET_TYPE_SET 0x03
++#define BNEP_FILTER_NET_TYPE_RSP 0x04
++#define BNEP_FILTER_MULTI_ADDR_SET 0x05
++#define BNEP_FILTER_MULTI_ADDR_RSP 0x06
++
++// Extension types
++#define BNEP_EXT_CONTROL 0x00
++
++// Response messages
++#define BNEP_SUCCESS 0x00
++
++#define BNEP_CONN_INVALID_DST 0x01
++#define BNEP_CONN_INVALID_SRC 0x02
++#define BNEP_CONN_INVALID_SVC 0x03
++#define BNEP_CONN_NOT_ALLOWED 0x04
++
++#define BNEP_FILTER_UNSUPPORTED_REQ 0x01
++#define BNEP_FILTER_INVALID_RANGE 0x02
++#define BNEP_FILTER_INVALID_MCADDR 0x02
++#define BNEP_FILTER_LIMIT_REACHED 0x03
++#define BNEP_FILTER_DENIED_SECURITY 0x04
++
++// L2CAP settings
++#define BNEP_MTU 1691
++#define BNEP_PSM 0x0f
++#define BNEP_FLUSH_TO 0xffff
++#define BNEP_CONNECT_TO 15
++#define BNEP_FILTER_TO 15
++
++// Headers
++#define BNEP_TYPE_MASK 0x7f
++#define BNEP_EXT_HEADER 0x80
++
++struct bnep_setup_conn_req {
++ __u8 type;
++ __u8 ctrl;
++ __u8 uuid_size;
++ __u8 service[0];
++} __attribute__((packed));
++
++struct bnep_set_filter_req {
++ __u8 type;
++ __u8 ctrl;
++ __u16 len;
++ __u8 list[0];
++} __attribute__((packed));
++
++struct bnep_control_rsp {
++ __u8 type;
++ __u8 ctrl;
++ __u16 resp;
++} __attribute__((packed));
++
++struct bnep_ext_hdr {
++ __u8 type;
++ __u8 len;
++ __u8 data[0];
++} __attribute__((packed));
++
++/* BNEP ioctl defines */
++#define BNEPCONNADD _IOW('B', 200, int)
++#define BNEPCONNDEL _IOW('B', 201, int)
++#define BNEPGETCONNLIST _IOR('B', 210, int)
++#define BNEPGETCONNINFO _IOR('B', 211, int)
++
++struct bnep_connadd_req {
++ int sock; // Connected socket
++ __u32 flags;
++ __u16 role;
++ char device[16]; // Name of the Ethernet device
++};
++
++struct bnep_conndel_req {
++ __u32 flags;
++ __u8 dst[ETH_ALEN];
++};
++
++struct bnep_conninfo {
++ __u32 flags;
++ __u16 role;
++ __u16 state;
++ __u8 dst[ETH_ALEN];
++ char device[16];
++};
++
++struct bnep_connlist_req {
++ __u32 cnum;
++ struct bnep_conninfo *ci;
++};
++
++struct bnep_proto_filter {
++ __u16 start;
++ __u16 end;
++};
++
++int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock);
++int bnep_del_connection(struct bnep_conndel_req *req);
++int bnep_get_connlist(struct bnep_connlist_req *req);
++int bnep_get_conninfo(struct bnep_conninfo *ci);
++
++// BNEP sessions
++struct bnep_session {
++ struct list_head list;
++
++ unsigned int role;
++ unsigned long state;
++ unsigned long flags;
++ atomic_t killed;
++
++ struct ethhdr eh;
++ struct msghdr msg;
++
++ struct bnep_proto_filter proto_filter[BNEP_MAX_PROTO_FILTERS];
++ u64 mc_filter;
++
++ struct socket *sock;
++ struct net_device dev;
++ struct net_device_stats stats;
++};
++
++int bnep_net_init(struct net_device *dev);
++int bnep_sock_init(void);
++int bnep_sock_cleanup(void);
++
++static inline int bnep_mc_hash(__u8 *addr)
++{
++ return (bnep_crc32(~0, addr, ETH_ALEN) >> 26);
++}
++
++#endif
+diff -urN linux-2.4.18/net/bluetooth/bnep/Config.in linux-2.4.18-mh15/net/bluetooth/bnep/Config.in
+--- linux-2.4.18/net/bluetooth/bnep/Config.in 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/net/bluetooth/bnep/Config.in 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,11 @@
++#
++# Bluetooth BNEP layer configuration
++#
++
++dep_tristate 'BNEP protocol support' CONFIG_BLUEZ_BNEP $CONFIG_BLUEZ_L2CAP
++
++if [ "$CONFIG_BLUEZ_BNEP" != "n" ]; then
++ bool ' Multicast filter support' CONFIG_BLUEZ_BNEP_MC_FILTER
++ bool ' Protocol filter support' CONFIG_BLUEZ_BNEP_PROTO_FILTER
++fi
++
+diff -urN linux-2.4.18/net/bluetooth/bnep/core.c linux-2.4.18-mh15/net/bluetooth/bnep/core.c
+--- linux-2.4.18/net/bluetooth/bnep/core.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/net/bluetooth/bnep/core.c 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,718 @@
++/*
++ BNEP implementation for Linux Bluetooth stack (BlueZ).
++ Copyright (C) 2001-2002 Inventel Systemes
++ Written 2001-2002 by
++ Clément Moreau <clement.moreau@inventel.fr>
++ David Libault <david.libault@inventel.fr>
++
++ Copyright (C) 2002 Maxim Krasnyanskiy <maxk@qualcomm.com>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License version 2 as
++ published by the Free Software Foundation;
++
++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++
++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
++ SOFTWARE IS DISCLAIMED.
++*/
++
++/*
++ * $Id$
++ */
++
++#define __KERNEL_SYSCALLS__
++
++#include <linux/config.h>
++#include <linux/module.h>
++
++#include <linux/kernel.h>
++#include <linux/sched.h>
++#include <linux/signal.h>
++#include <linux/init.h>
++#include <linux/wait.h>
++#include <linux/errno.h>
++#include <linux/smp_lock.h>
++#include <linux/net.h>
++#include <net/sock.h>
++
++#include <linux/socket.h>
++#include <linux/file.h>
++
++#include <linux/netdevice.h>
++#include <linux/etherdevice.h>
++#include <linux/skbuff.h>
++
++#include <asm/unaligned.h>
++
++#include <net/bluetooth/bluetooth.h>
++#include <net/bluetooth/l2cap.h>
++
++#include "bnep.h"
++
++#ifndef CONFIG_BLUEZ_BNEP_DEBUG
++#undef BT_DBG
++#define BT_DBG(D...)
++#endif
++
++#define VERSION "1.2"
++
++static LIST_HEAD(bnep_session_list);
++static DECLARE_RWSEM(bnep_session_sem);
++
++static struct bnep_session *__bnep_get_session(u8 *dst)
++{
++ struct bnep_session *s;
++ struct list_head *p;
++
++ BT_DBG("");
++
++ list_for_each(p, &bnep_session_list) {
++ s = list_entry(p, struct bnep_session, list);
++ if (!memcmp(dst, s->eh.h_source, ETH_ALEN))
++ return s;
++ }
++ return NULL;
++}
++
++static void __bnep_link_session(struct bnep_session *s)
++{
++ MOD_INC_USE_COUNT;
++ list_add(&s->list, &bnep_session_list);
++}
++
++static void __bnep_unlink_session(struct bnep_session *s)
++{
++ list_del(&s->list);
++ MOD_DEC_USE_COUNT;
++}
++
++static int bnep_send(struct bnep_session *s, void *data, size_t len)
++{
++ struct socket *sock = s->sock;
++ struct iovec iv = { data, len };
++ s->msg.msg_iov = &iv;
++ s->msg.msg_iovlen = 1;
++ return sock->ops->sendmsg(sock, &s->msg, len, NULL);
++}
++
++static int bnep_send_rsp(struct bnep_session *s, u8 ctrl, u16 resp)
++{
++ struct bnep_control_rsp rsp;
++ rsp.type = BNEP_CONTROL;
++ rsp.ctrl = ctrl;
++ rsp.resp = htons(resp);
++ return bnep_send(s, &rsp, sizeof(rsp));
++}
++
++#ifdef CONFIG_BLUEZ_BNEP_PROTO_FILTER
++static inline void bnep_set_default_proto_filter(struct bnep_session *s)
++{
++ /* (IPv4, ARP) */
++ s->proto_filter[0].start = htons(0x0800);
++ s->proto_filter[0].end = htons(0x0806);
++ /* (RARP, AppleTalk) */
++ s->proto_filter[1].start = htons(0x8035);
++ s->proto_filter[1].end = htons(0x80F3);
++ /* (IPX, IPv6) */
++ s->proto_filter[2].start = htons(0x8137);
++ s->proto_filter[2].end = htons(0x86DD);
++}
++#endif
++
++static int bnep_ctrl_set_netfilter(struct bnep_session *s, u16 *data, int len)
++{
++ int n;
++
++ if (len < 2)
++ return -EILSEQ;
++
++ n = ntohs(get_unaligned(data));
++ data++; len -= 2;
++
++ if (len < n)
++ return -EILSEQ;
++
++ BT_DBG("filter len %d", n);
++
++#ifdef CONFIG_BLUEZ_BNEP_PROTO_FILTER
++ n /= 4;
++ if (n <= BNEP_MAX_PROTO_FILTERS) {
++ struct bnep_proto_filter *f = s->proto_filter;
++ int i;
++
++ for (i = 0; i < n; i++) {
++ f[i].start = get_unaligned(data++);
++ f[i].end = get_unaligned(data++);
++
++ BT_DBG("proto filter start %d end %d",
++ f[i].start, f[i].end);
++ }
++
++ if (i < BNEP_MAX_PROTO_FILTERS)
++ memset(f + i, 0, sizeof(*f));
++
++ if (n == 0)
++ bnep_set_default_proto_filter(s);
++
++ bnep_send_rsp(s, BNEP_FILTER_NET_TYPE_RSP, BNEP_SUCCESS);
++ } else {
++ bnep_send_rsp(s, BNEP_FILTER_NET_TYPE_RSP, BNEP_FILTER_LIMIT_REACHED);
++ }
++#else
++ bnep_send_rsp(s, BNEP_FILTER_NET_TYPE_RSP, BNEP_FILTER_UNSUPPORTED_REQ);
++#endif
++ return 0;
++}
++
++static int bnep_ctrl_set_mcfilter(struct bnep_session *s, u8 *data, int len)
++{
++ int n;
++
++ if (len < 2)
++ return -EILSEQ;
++
++ n = ntohs(get_unaligned((u16 *) data));
++ data += 2; len -= 2;
++
++ if (len < n)
++ return -EILSEQ;
++
++ BT_DBG("filter len %d", n);
++
++#ifdef CONFIG_BLUEZ_BNEP_MC_FILTER
++ n /= (ETH_ALEN * 2);
++
++ if (n > 0) {
++ s->mc_filter = 0;
++
++ /* Always send broadcast */
++ set_bit(bnep_mc_hash(s->dev.broadcast), &s->mc_filter);
++
++ /* Add address ranges to the multicast hash */
++ for (; n > 0; n--) {
++ u8 a1[6], *a2;
++
++ memcpy(a1, data, ETH_ALEN); data += ETH_ALEN;
++ a2 = data; data += ETH_ALEN;
++
++ BT_DBG("mc filter %s -> %s",
++ batostr((void *) a1), batostr((void *) a2));
++
++ #define INCA(a) { int i = 5; while (i >=0 && ++a[i--] == 0); }
++
++ /* Iterate from a1 to a2 */
++ set_bit(bnep_mc_hash(a1), &s->mc_filter);
++ while (memcmp(a1, a2, 6) < 0 && s->mc_filter != ~0LL) {
++ INCA(a1);
++ set_bit(bnep_mc_hash(a1), &s->mc_filter);
++ }
++ }
++ }
++
++ BT_DBG("mc filter hash 0x%llx", s->mc_filter);
++
++ bnep_send_rsp(s, BNEP_FILTER_MULTI_ADDR_RSP, BNEP_SUCCESS);
++#else
++ bnep_send_rsp(s, BNEP_FILTER_MULTI_ADDR_RSP, BNEP_FILTER_UNSUPPORTED_REQ);
++#endif
++ return 0;
++}
++
++static int bnep_rx_control(struct bnep_session *s, void *data, int len)
++{
++ u8 cmd = *(u8 *)data;
++ int err = 0;
++
++ data++; len--;
++
++ switch (cmd) {
++ case BNEP_CMD_NOT_UNDERSTOOD:
++ case BNEP_SETUP_CONN_REQ:
++ case BNEP_SETUP_CONN_RSP:
++ case BNEP_FILTER_NET_TYPE_RSP:
++ case BNEP_FILTER_MULTI_ADDR_RSP:
++ /* Ignore these for now */
++ break;
++
++ case BNEP_FILTER_NET_TYPE_SET:
++ err = bnep_ctrl_set_netfilter(s, data, len);
++ break;
++
++ case BNEP_FILTER_MULTI_ADDR_SET:
++ err = bnep_ctrl_set_mcfilter(s, data, len);
++ break;
++
++ default: {
++ u8 pkt[3];
++ pkt[0] = BNEP_CONTROL;
++ pkt[1] = BNEP_CMD_NOT_UNDERSTOOD;
++ pkt[2] = cmd;
++ bnep_send(s, pkt, sizeof(pkt));
++ }
++ break;
++ }
++
++ return err;
++}
++
++static int bnep_rx_extension(struct bnep_session *s, struct sk_buff *skb)
++{
++ struct bnep_ext_hdr *h;
++ int err = 0;
++
++ do {
++ h = (void *) skb->data;
++ if (!skb_pull(skb, sizeof(*h))) {
++ err = -EILSEQ;
++ break;
++ }
++
++ BT_DBG("type 0x%x len %d", h->type, h->len);
++
++ switch (h->type & BNEP_TYPE_MASK) {
++ case BNEP_EXT_CONTROL:
++ bnep_rx_control(s, skb->data, skb->len);
++ break;
++
++ default:
++ /* Unknown extension, skip it. */
++ break;
++ }
++
++ if (!skb_pull(skb, h->len)) {
++ err = -EILSEQ;
++ break;
++ }
++ } while (!err && (h->type & BNEP_EXT_HEADER));
++
++ return err;
++}
++
++static u8 __bnep_rx_hlen[] = {
++ ETH_HLEN, /* BNEP_GENERAL */
++ 0, /* BNEP_CONTROL */
++ 2, /* BNEP_COMPRESSED */
++ ETH_ALEN + 2, /* BNEP_COMPRESSED_SRC_ONLY */
++ ETH_ALEN + 2 /* BNEP_COMPRESSED_DST_ONLY */
++};
++#define BNEP_RX_TYPES (sizeof(__bnep_rx_hlen) - 1)
++
++static inline int bnep_rx_frame(struct bnep_session *s, struct sk_buff *skb)
++{
++ struct net_device *dev = &s->dev;
++ struct sk_buff *nskb;
++ u8 type;
++
++ dev->last_rx = jiffies;
++ s->stats.rx_bytes += skb->len;
++
++ type = *(u8 *) skb->data; skb_pull(skb, 1);
++
++ if ((type & BNEP_TYPE_MASK) > BNEP_RX_TYPES)
++ goto badframe;
++
++ if ((type & BNEP_TYPE_MASK) == BNEP_CONTROL) {
++ bnep_rx_control(s, skb->data, skb->len);
++ kfree_skb(skb);
++ return 0;
++ }
++
++ skb->mac.raw = skb->data;
++
++ /* Verify and pull out header */
++ if (!skb_pull(skb, __bnep_rx_hlen[type & BNEP_TYPE_MASK]))
++ goto badframe;
++
++ s->eh.h_proto = get_unaligned((u16 *) (skb->data - 2));
++
++ if (type & BNEP_EXT_HEADER) {
++ if (bnep_rx_extension(s, skb) < 0)
++ goto badframe;
++ }
++
++ /* Strip 802.1p header */
++ if (ntohs(s->eh.h_proto) == 0x8100) {
++ if (!skb_pull(skb, 4))
++ goto badframe;
++ s->eh.h_proto = get_unaligned((u16 *) (skb->data - 2));
++ }
++
++ /* We have to alloc new skb and copy data here :(. Because original skb
++ * may not be modified and because of the alignment requirements. */
++ nskb = alloc_skb(2 + ETH_HLEN + skb->len, GFP_KERNEL);
++ if (!nskb) {
++ s->stats.rx_dropped++;
++ kfree_skb(skb);
++ return -ENOMEM;
++ }
++ skb_reserve(nskb, 2);
++
++ /* Decompress header and construct ether frame */
++ switch (type & BNEP_TYPE_MASK) {
++ case BNEP_COMPRESSED:
++ memcpy(__skb_put(nskb, ETH_HLEN), &s->eh, ETH_HLEN);
++ break;
++
++ case BNEP_COMPRESSED_SRC_ONLY:
++ memcpy(__skb_put(nskb, ETH_ALEN), s->eh.h_dest, ETH_ALEN);
++ memcpy(__skb_put(nskb, ETH_ALEN), skb->mac.raw, ETH_ALEN);
++ put_unaligned(s->eh.h_proto, (u16 *) __skb_put(nskb, 2));
++ break;
++
++ case BNEP_COMPRESSED_DST_ONLY:
++ memcpy(__skb_put(nskb, ETH_ALEN), skb->mac.raw, ETH_ALEN);
++ memcpy(__skb_put(nskb, ETH_ALEN + 2), s->eh.h_source, ETH_ALEN + 2);
++ break;
++
++ case BNEP_GENERAL:
++ memcpy(__skb_put(nskb, ETH_ALEN * 2), skb->mac.raw, ETH_ALEN * 2);
++ put_unaligned(s->eh.h_proto, (u16 *) __skb_put(nskb, 2));
++ break;
++ }
++
++ memcpy(__skb_put(nskb, skb->len), skb->data, skb->len);
++ kfree_skb(skb);
++
++ s->stats.rx_packets++;
++ nskb->dev = dev;
++ nskb->ip_summed = CHECKSUM_UNNECESSARY;
++ nskb->protocol = eth_type_trans(nskb, dev);
++ netif_rx_ni(nskb);
++ return 0;
++
++badframe:
++ s->stats.rx_errors++;
++ kfree_skb(skb);
++ return 0;
++}
++
++static u8 __bnep_tx_types[] = {
++ BNEP_GENERAL,
++ BNEP_COMPRESSED_SRC_ONLY,
++ BNEP_COMPRESSED_DST_ONLY,
++ BNEP_COMPRESSED
++};
++
++static inline int bnep_tx_frame(struct bnep_session *s, struct sk_buff *skb)
++{
++ struct ethhdr *eh = (void *) skb->data;
++ struct socket *sock = s->sock;
++ struct iovec iv[3];
++ int len = 0, il = 0;
++ u8 type = 0;
++
++ BT_DBG("skb %p dev %p type %d", skb, skb->dev, skb->pkt_type);
++
++ if (!skb->dev) {
++ /* Control frame sent by us */
++ goto send;
++ }
++
++ iv[il++] = (struct iovec) { &type, 1 };
++ len++;
++
++ if (!memcmp(eh->h_dest, s->eh.h_source, ETH_ALEN))
++ type |= 0x01;
++
++ if (!memcmp(eh->h_source, s->eh.h_dest, ETH_ALEN))
++ type |= 0x02;
++
++ if (type)
++ skb_pull(skb, ETH_ALEN * 2);
++
++ type = __bnep_tx_types[type];
++ switch (type) {
++ case BNEP_COMPRESSED_SRC_ONLY:
++ iv[il++] = (struct iovec) { eh->h_source, ETH_ALEN };
++ len += ETH_ALEN;
++ break;
++
++ case BNEP_COMPRESSED_DST_ONLY:
++ iv[il++] = (struct iovec) { eh->h_dest, ETH_ALEN };
++ len += ETH_ALEN;
++ break;
++ }
++
++send:
++ iv[il++] = (struct iovec) { skb->data, skb->len };
++ len += skb->len;
++
++ /* FIXME: linearize skb */
++
++ s->msg.msg_iov = iv;
++ s->msg.msg_iovlen = il;
++ len = sock->ops->sendmsg(sock, &s->msg, len, NULL);
++ kfree_skb(skb);
++
++ if (len > 0) {
++ s->stats.tx_bytes += len;
++ s->stats.tx_packets++;
++ return 0;
++ }
++
++ return len;
++}
++
++static int bnep_session(void *arg)
++{
++ struct bnep_session *s = arg;
++ struct net_device *dev = &s->dev;
++ struct sock *sk = s->sock->sk;
++ struct sk_buff *skb;
++ wait_queue_t wait;
++
++ BT_DBG("");
++
++ daemonize(); reparent_to_init();
++
++ sprintf(current->comm, "kbnepd %s", dev->name);
++
++ sigfillset(&current->blocked);
++ flush_signals(current);
++
++ current->nice = -15;
++
++ set_fs(KERNEL_DS);
++
++ init_waitqueue_entry(&wait, current);
++ add_wait_queue(sk->sleep, &wait);
++ while (!atomic_read(&s->killed)) {
++ set_current_state(TASK_INTERRUPTIBLE);
++
++ // RX
++ while ((skb = skb_dequeue(&sk->receive_queue))) {
++ skb_orphan(skb);
++ bnep_rx_frame(s, skb);
++ }
++
++ if (sk->state != BT_CONNECTED)
++ break;
++
++ // TX
++ while ((skb = skb_dequeue(&sk->write_queue)))
++ if (bnep_tx_frame(s, skb))
++ break;
++ netif_wake_queue(dev);
++
++ schedule();
++ }
++ set_current_state(TASK_RUNNING);
++ remove_wait_queue(sk->sleep, &wait);
++
++ /* Cleanup session */
++ down_write(&bnep_session_sem);
++
++ /* Delete network device */
++ unregister_netdev(dev);
++
++ /* Release the socket */
++ fput(s->sock->file);
++
++ __bnep_unlink_session(s);
++
++ up_write(&bnep_session_sem);
++ kfree(s);
++ return 0;
++}
++
++int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock)
++{
++ struct net_device *dev;
++ struct bnep_session *s, *ss;
++ u8 dst[ETH_ALEN], src[ETH_ALEN];
++ int err;
++
++ BT_DBG("");
++
++ baswap((void *) dst, &bluez_pi(sock->sk)->dst);
++ baswap((void *) src, &bluez_pi(sock->sk)->src);
++
++ s = kmalloc(sizeof(struct bnep_session), GFP_KERNEL);
++ if (!s)
++ return -ENOMEM;
++ memset(s, 0, sizeof(struct bnep_session));
++
++ down_write(&bnep_session_sem);
++
++ ss = __bnep_get_session(dst);
++ if (ss && ss->state == BT_CONNECTED) {
++ err = -EEXIST;
++ goto failed;
++ }
++
++ dev = &s->dev;
++
++ if (*req->device)
++ strcpy(dev->name, req->device);
++ else
++ strcpy(dev->name, "bnep%d");
++
++ memset(dev->broadcast, 0xff, ETH_ALEN);
++
++ /* This is rx header therefor addresses are swaped.
++ * ie eh.h_dest is our local address. */
++ memcpy(s->eh.h_dest, &src, ETH_ALEN);
++ memcpy(s->eh.h_source, &dst, ETH_ALEN);
++
++ s->sock = sock;
++ s->role = req->role;
++ s->state = BT_CONNECTED;
++
++ s->msg.msg_flags = MSG_NOSIGNAL;
++
++#ifdef CONFIG_BLUEZ_BNEP_MC_FILTER
++ /* Set default mc filter */
++ set_bit(bnep_mc_hash(dev->broadcast), &s->mc_filter);
++#endif
++
++#ifdef CONFIG_BLUEZ_BNEP_PROTO_FILTER
++ /* Set default protocol filter */
++ bnep_set_default_proto_filter(s);
++#endif
++
++ dev->init = bnep_net_init;
++ dev->priv = s;
++ err = register_netdev(dev);
++ if (err) {
++ goto failed;
++ }
++
++ __bnep_link_session(s);
++
++ err = kernel_thread(bnep_session, s, CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
++ if (err < 0) {
++ /* Session thread start failed, gotta cleanup. */
++ unregister_netdev(dev);
++ __bnep_unlink_session(s);
++ goto failed;
++ }
++
++ up_write(&bnep_session_sem);
++ strcpy(req->device, dev->name);
++ return 0;
++
++failed:
++ up_write(&bnep_session_sem);
++ kfree(s);
++ return err;
++}
++
++int bnep_del_connection(struct bnep_conndel_req *req)
++{
++ struct bnep_session *s;
++ int err = 0;
++
++ BT_DBG("");
++
++ down_read(&bnep_session_sem);
++
++ s = __bnep_get_session(req->dst);
++ if (s) {
++ /* Wakeup user-space which is polling for socket errors.
++ * This is temporary hack untill we have shutdown in L2CAP */
++ s->sock->sk->err = EUNATCH;
++
++ /* Kill session thread */
++ atomic_inc(&s->killed);
++ wake_up_interruptible(s->sock->sk->sleep);
++ } else
++ err = -ENOENT;
++
++ up_read(&bnep_session_sem);
++ return err;
++}
++
++static void __bnep_copy_ci(struct bnep_conninfo *ci, struct bnep_session *s)
++{
++ memcpy(ci->dst, s->eh.h_source, ETH_ALEN);
++ strcpy(ci->device, s->dev.name);
++ ci->flags = s->flags;
++ ci->state = s->state;
++ ci->role = s->role;
++}
++
++int bnep_get_connlist(struct bnep_connlist_req *req)
++{
++ struct list_head *p;
++ int err = 0, n = 0;
++
++ down_read(&bnep_session_sem);
++
++ list_for_each(p, &bnep_session_list) {
++ struct bnep_session *s;
++ struct bnep_conninfo ci;
++
++ s = list_entry(p, struct bnep_session, list);
++
++ __bnep_copy_ci(&ci, s);
++
++ if (copy_to_user(req->ci, &ci, sizeof(ci))) {
++ err = -EFAULT;
++ break;
++ }
++
++ if (++n >= req->cnum)
++ break;
++
++ req->ci++;
++ }
++ req->cnum = n;
++
++ up_read(&bnep_session_sem);
++ return err;
++}
++
++int bnep_get_conninfo(struct bnep_conninfo *ci)
++{
++ struct bnep_session *s;
++ int err = 0;
++
++ down_read(&bnep_session_sem);
++
++ s = __bnep_get_session(ci->dst);
++ if (s)
++ __bnep_copy_ci(ci, s);
++ else
++ err = -ENOENT;
++
++ up_read(&bnep_session_sem);
++ return err;
++}
++
++static int __init bnep_init_module(void)
++{
++ l2cap_load();
++
++ bnep_crc32_init();
++ bnep_sock_init();
++
++ BT_INFO("BlueZ BNEP ver %s", VERSION);
++ BT_INFO("Copyright (C) 2001,2002 Inventel Systemes");
++ BT_INFO("Written 2001,2002 by Clement Moreau <clement.moreau@inventel.fr>");
++ BT_INFO("Written 2001,2002 by David Libault <david.libault@inventel.fr>");
++ BT_INFO("Copyright (C) 2002 Maxim Krasnyanskiy <maxk@qualcomm.com>");
++
++ return 0;
++}
++
++static void __exit bnep_cleanup_module(void)
++{
++ bnep_sock_cleanup();
++ bnep_crc32_cleanup();
++}
++
++module_init(bnep_init_module);
++module_exit(bnep_cleanup_module);
++
++MODULE_DESCRIPTION("BlueZ BNEP ver " VERSION);
++MODULE_AUTHOR("David Libault <david.libault@inventel.fr>, Maxim Krasnyanskiy <maxk@qualcomm.com>");
++MODULE_LICENSE("GPL");
+diff -urN linux-2.4.18/net/bluetooth/bnep/crc32.c linux-2.4.18-mh15/net/bluetooth/bnep/crc32.c
+--- linux-2.4.18/net/bluetooth/bnep/crc32.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/net/bluetooth/bnep/crc32.c 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,59 @@
++/*
++ * Based on linux-2.5/lib/crc32 by Matt Domsch <Matt_Domsch@dell.com>
++ *
++ * FIXME: Remove in 2.5
++ */
++
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/slab.h>
++#include <linux/init.h>
++#include <asm/atomic.h>
++
++#include "crc32.h"
++
++#define CRCPOLY_BE 0x04c11db7
++#define CRC_BE_BITS 8
++
++static u32 *bnep_crc32_table;
++
++/*
++ * This code is in the public domain; copyright abandoned.
++ * Liability for non-performance of this code is limited to the amount
++ * you paid for it. Since it is distributed for free, your refund will
++ * be very very small. If it breaks, you get to keep both pieces.
++ */
++u32 bnep_crc32(u32 crc, unsigned char const *p, size_t len)
++{
++ while (len--)
++ crc = (crc << 8) ^ bnep_crc32_table[(crc >> 24) ^ *p++];
++
++ return crc;
++}
++
++int __init bnep_crc32_init(void)
++{
++ unsigned i, j;
++ u32 crc = 0x80000000;
++
++ bnep_crc32_table = kmalloc((1 << CRC_BE_BITS) * sizeof(u32), GFP_KERNEL);
++ if (!bnep_crc32_table)
++ return -ENOMEM;
++
++ bnep_crc32_table[0] = 0;
++
++ for (i = 1; i < 1 << CRC_BE_BITS; i <<= 1) {
++ crc = (crc << 1) ^ ((crc & 0x80000000) ? CRCPOLY_BE : 0);
++ for (j = 0; j < i; j++)
++ bnep_crc32_table[i + j] = crc ^ bnep_crc32_table[j];
++ }
++ return 0;
++}
++
++void __exit bnep_crc32_cleanup(void)
++{
++ if (bnep_crc32_table)
++ kfree(bnep_crc32_table);
++ bnep_crc32_table = NULL;
++}
+diff -urN linux-2.4.18/net/bluetooth/bnep/crc32.h linux-2.4.18-mh15/net/bluetooth/bnep/crc32.h
+--- linux-2.4.18/net/bluetooth/bnep/crc32.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/net/bluetooth/bnep/crc32.h 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,10 @@
++/*
++ * crc32.h
++ * See crc32.c for license and changes
++ *
++ * FIXME: Remove in 2.5
++ */
++
++int bnep_crc32_init(void);
++void bnep_crc32_cleanup(void);
++u32 bnep_crc32(u32 crc, unsigned char const *p, size_t len);
+diff -urN linux-2.4.18/net/bluetooth/bnep/Makefile linux-2.4.18-mh15/net/bluetooth/bnep/Makefile
+--- linux-2.4.18/net/bluetooth/bnep/Makefile 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/net/bluetooth/bnep/Makefile 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,10 @@
++#
++# Makefile for the Linux Bluetooth BNEP layer
++#
++
++O_TARGET := bnep.o
++
++obj-y := core.o sock.o netdev.o crc32.o
++obj-m += $(O_TARGET)
++
++include $(TOPDIR)/Rules.make
+diff -urN linux-2.4.18/net/bluetooth/bnep/netdev.c linux-2.4.18-mh15/net/bluetooth/bnep/netdev.c
+--- linux-2.4.18/net/bluetooth/bnep/netdev.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/net/bluetooth/bnep/netdev.c 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,254 @@
++/*
++ BNEP implementation for Linux Bluetooth stack (BlueZ).
++ Copyright (C) 2001-2002 Inventel Systemes
++ Written 2001-2002 by
++ Clément Moreau <clement.moreau@inventel.fr>
++ David Libault <david.libault@inventel.fr>
++
++ Copyright (C) 2002 Maxim Krasnyanskiy <maxk@qualcomm.com>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License version 2 as
++ published by the Free Software Foundation;
++
++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++
++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
++ SOFTWARE IS DISCLAIMED.
++*/
++
++/*
++ * $Id$
++ */
++
++#include <linux/config.h>
++#include <linux/module.h>
++
++#include <linux/socket.h>
++#include <linux/netdevice.h>
++#include <linux/etherdevice.h>
++#include <linux/skbuff.h>
++#include <linux/wait.h>
++
++#include <asm/unaligned.h>
++
++#include <net/bluetooth/bluetooth.h>
++#include <net/bluetooth/hci_core.h>
++#include <net/bluetooth/l2cap.h>
++
++#include "bnep.h"
++
++#ifndef CONFIG_BLUEZ_BNEP_DEBUG
++#undef BT_DBG
++#define BT_DBG( A... )
++#endif
++
++#define BNEP_TX_QUEUE_LEN 20
++
++static int bnep_net_open(struct net_device *dev)
++{
++ netif_start_queue(dev);
++ return 0;
++}
++
++static int bnep_net_close(struct net_device *dev)
++{
++ netif_stop_queue(dev);
++ return 0;
++}
++
++static struct net_device_stats *bnep_net_get_stats(struct net_device *dev)
++{
++ struct bnep_session *s = dev->priv;
++ return &s->stats;
++}
++
++static void bnep_net_set_mc_list(struct net_device *dev)
++{
++#ifdef CONFIG_BLUEZ_BNEP_MC_FILTER
++ struct bnep_session *s = dev->priv;
++ struct sock *sk = s->sock->sk;
++ struct bnep_set_filter_req *r;
++ struct sk_buff *skb;
++ int size;
++
++ BT_DBG("%s mc_count %d", dev->name, dev->mc_count);
++
++ size = sizeof(*r) + (BNEP_MAX_MULTICAST_FILTERS + 1) * ETH_ALEN * 2;
++ skb = alloc_skb(size, GFP_ATOMIC);
++ if (!skb) {
++ BT_ERR("%s Multicast list allocation failed", dev->name);
++ return;
++ }
++
++ r = (void *) skb->data;
++ __skb_put(skb, sizeof(*r));
++
++ r->type = BNEP_CONTROL;
++ r->ctrl = BNEP_FILTER_MULTI_ADDR_SET;
++
++ if (dev->flags & (IFF_PROMISC | IFF_ALLMULTI)) {
++ u8 start[ETH_ALEN] = { 0x01 };
++
++ /* Request all addresses */
++ memcpy(__skb_put(skb, ETH_ALEN), start, ETH_ALEN);
++ memcpy(__skb_put(skb, ETH_ALEN), dev->broadcast, ETH_ALEN);
++ r->len = htons(ETH_ALEN * 2);
++ } else {
++ struct dev_mc_list *dmi = dev->mc_list;
++ int i, len = skb->len;
++
++ if (dev->flags & IFF_BROADCAST) {
++ memcpy(__skb_put(skb, ETH_ALEN), dev->broadcast, ETH_ALEN);
++ memcpy(__skb_put(skb, ETH_ALEN), dev->broadcast, ETH_ALEN);
++ }
++
++ /* FIXME: We should group addresses here. */
++
++ for (i = 0; i < dev->mc_count && i < BNEP_MAX_MULTICAST_FILTERS; i++) {
++ memcpy(__skb_put(skb, ETH_ALEN), dmi->dmi_addr, ETH_ALEN);
++ memcpy(__skb_put(skb, ETH_ALEN), dmi->dmi_addr, ETH_ALEN);
++ dmi = dmi->next;
++ }
++ r->len = htons(skb->len - len);
++ }
++
++ skb_queue_tail(&sk->write_queue, skb);
++ wake_up_interruptible(sk->sleep);
++#endif
++}
++
++static int bnep_net_set_mac_addr(struct net_device *dev, void *arg)
++{
++ BT_DBG("%s", dev->name);
++ return 0;
++}
++
++static void bnep_net_timeout(struct net_device *dev)
++{
++ BT_DBG("net_timeout");
++ netif_wake_queue(dev);
++}
++
++static int bnep_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
++{
++ return -EINVAL;
++}
++
++#ifdef CONFIG_BLUEZ_BNEP_MC_FILTER
++static inline int bnep_net_mc_filter(struct sk_buff *skb, struct bnep_session *s)
++{
++ struct ethhdr *eh = (void *) skb->data;
++
++ if ((eh->h_dest[0] & 1) && !test_bit(bnep_mc_hash(eh->h_dest), &s->mc_filter)) {
++ BT_DBG("BNEP: filtered skb %p, dst %.2x:%.2x:%.2x:%.2x:%.2x:%.2x", skb,
++ eh->h_dest[0], eh->h_dest[1], eh->h_dest[2],
++ eh->h_dest[3], eh->h_dest[4], eh->h_dest[5]);
++ return 1;
++ }
++ return 0;
++}
++#endif
++
++#ifdef CONFIG_BLUEZ_BNEP_PROTO_FILTER
++/* Determine ether protocol. Based on eth_type_trans. */
++static inline u16 bnep_net_eth_proto(struct sk_buff *skb)
++{
++ struct ethhdr *eh = (void *) skb->data;
++
++ if (ntohs(eh->h_proto) >= 1536)
++ return eh->h_proto;
++
++ if (get_unaligned((u16 *) skb->data) == 0xFFFF)
++ return htons(ETH_P_802_3);
++
++ return htons(ETH_P_802_2);
++}
++
++static inline int bnep_net_proto_filter(struct sk_buff *skb, struct bnep_session *s)
++{
++ u16 proto = bnep_net_eth_proto(skb);
++ struct bnep_proto_filter *f = s->proto_filter;
++ int i;
++
++ for (i = 0; i < BNEP_MAX_PROTO_FILTERS && f[i].end; i++) {
++ if (proto >= f[i].start && proto <= f[i].end)
++ return 0;
++ }
++
++ BT_DBG("BNEP: filtered skb %p, proto 0x%.4x", skb, proto);
++ return 1;
++}
++#endif
++
++static int bnep_net_xmit(struct sk_buff *skb, struct net_device *dev)
++{
++ struct bnep_session *s = dev->priv;
++ struct sock *sk = s->sock->sk;
++
++ BT_DBG("skb %p, dev %p", skb, dev);
++
++#ifdef CONFIG_BLUEZ_BNEP_MC_FILTER
++ if (bnep_net_mc_filter(skb, s)) {
++ kfree_skb(skb);
++ return 0;
++ }
++#endif
++
++#ifdef CONFIG_BLUEZ_BNEP_PROTO_FILTER
++ if (bnep_net_proto_filter(skb, s)) {
++ kfree_skb(skb);
++ return 0;
++ }
++#endif
++
++ /*
++ * We cannot send L2CAP packets from here as we are potentially in a bh.
++ * So we have to queue them and wake up session thread which is sleeping
++ * on the sk->sleep.
++ */
++ dev->trans_start = jiffies;
++ skb_queue_tail(&sk->write_queue, skb);
++ wake_up_interruptible(sk->sleep);
++
++ if (skb_queue_len(&sk->write_queue) >= BNEP_TX_QUEUE_LEN) {
++ BT_DBG("tx queue is full");
++
++ /* Stop queuing.
++ * Session thread will do netif_wake_queue() */
++ netif_stop_queue(dev);
++ }
++
++ return 0;
++}
++
++int bnep_net_init(struct net_device *dev)
++{
++ struct bnep_session *s = dev->priv;
++
++ memcpy(dev->dev_addr, s->eh.h_dest, ETH_ALEN);
++ dev->addr_len = ETH_ALEN;
++
++ ether_setup(dev);
++
++ dev->open = bnep_net_open;
++ dev->stop = bnep_net_close;
++ dev->hard_start_xmit = bnep_net_xmit;
++ dev->get_stats = bnep_net_get_stats;
++ dev->do_ioctl = bnep_net_ioctl;
++ dev->set_mac_address = bnep_net_set_mac_addr;
++ dev->set_multicast_list = bnep_net_set_mc_list;
++
++ dev->watchdog_timeo = HZ * 2;
++ dev->tx_timeout = bnep_net_timeout;
++
++ return 0;
++}
+diff -urN linux-2.4.18/net/bluetooth/bnep/sock.c linux-2.4.18-mh15/net/bluetooth/bnep/sock.c
+--- linux-2.4.18/net/bluetooth/bnep/sock.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/net/bluetooth/bnep/sock.c 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,210 @@
++/*
++ BNEP implementation for Linux Bluetooth stack (BlueZ).
++ Copyright (C) 2001-2002 Inventel Systemes
++ Written 2001-2002 by
++ David Libault <david.libault@inventel.fr>
++
++ Copyright (C) 2002 Maxim Krasnyanskiy <maxk@qualcomm.com>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License version 2 as
++ published by the Free Software Foundation;
++
++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++
++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
++ SOFTWARE IS DISCLAIMED.
++*/
++
++/*
++ * $Id$
++ */
++
++#include <linux/config.h>
++#include <linux/module.h>
++
++#include <linux/types.h>
++#include <linux/errno.h>
++#include <linux/kernel.h>
++#include <linux/major.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/poll.h>
++#include <linux/fcntl.h>
++#include <linux/skbuff.h>
++#include <linux/socket.h>
++#include <linux/ioctl.h>
++#include <linux/file.h>
++#include <net/sock.h>
++
++#include <asm/system.h>
++#include <asm/uaccess.h>
++
++#include "bnep.h"
++
++#ifndef CONFIG_BLUEZ_BNEP_DEBUG
++#undef BT_DBG
++#define BT_DBG( A... )
++#endif
++
++static int bnep_sock_release(struct socket *sock)
++{
++ struct sock *sk = sock->sk;
++
++ BT_DBG("sock %p sk %p", sock, sk);
++
++ if (!sk)
++ return 0;
++
++ sock_orphan(sk);
++ sock_put(sk);
++
++ MOD_DEC_USE_COUNT;
++ return 0;
++}
++
++static int bnep_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
++{
++ struct bnep_connlist_req cl;
++ struct bnep_connadd_req ca;
++ struct bnep_conndel_req cd;
++ struct bnep_conninfo ci;
++ struct socket *nsock;
++ int err;
++
++ BT_DBG("cmd %x arg %lx", cmd, arg);
++
++ switch (cmd) {
++ case BNEPCONNADD:
++ if (!capable(CAP_NET_ADMIN))
++ return -EACCES;
++
++ if (copy_from_user(&ca, (void *) arg, sizeof(ca)))
++ return -EFAULT;
++
++ nsock = sockfd_lookup(ca.sock, &err);
++ if (!nsock)
++ return err;
++
++ if (nsock->sk->state != BT_CONNECTED) {
++ fput(nsock->file);
++ return -EBADFD;
++ }
++
++ err = bnep_add_connection(&ca, nsock);
++ if (!err) {
++ if (copy_to_user((void *) arg, &ca, sizeof(ca)))
++ err = -EFAULT;
++ } else
++ fput(nsock->file);
++
++ return err;
++
++ case BNEPCONNDEL:
++ if (!capable(CAP_NET_ADMIN))
++ return -EACCES;
++
++ if (copy_from_user(&cd, (void *) arg, sizeof(cd)))
++ return -EFAULT;
++
++ return bnep_del_connection(&cd);
++
++ case BNEPGETCONNLIST:
++ if (copy_from_user(&cl, (void *) arg, sizeof(cl)))
++ return -EFAULT;
++
++ if (cl.cnum <= 0)
++ return -EINVAL;
++
++ err = bnep_get_connlist(&cl);
++ if (!err && copy_to_user((void *) arg, &cl, sizeof(cl)))
++ return -EFAULT;
++
++ return err;
++
++ case BNEPGETCONNINFO:
++ if (copy_from_user(&ci, (void *) arg, sizeof(ci)))
++ return -EFAULT;
++
++ err = bnep_get_conninfo(&ci);
++ if (!err && copy_to_user((void *) arg, &ci, sizeof(ci)))
++ return -EFAULT;
++
++ return err;
++
++ default:
++ return -EINVAL;
++ }
++
++ return 0;
++}
++
++static struct proto_ops bnep_sock_ops = {
++ family: PF_BLUETOOTH,
++ release: bnep_sock_release,
++ ioctl: bnep_sock_ioctl,
++ bind: sock_no_bind,
++ getname: sock_no_getname,
++ sendmsg: sock_no_sendmsg,
++ recvmsg: sock_no_recvmsg,
++ poll: sock_no_poll,
++ listen: sock_no_listen,
++ shutdown: sock_no_shutdown,
++ setsockopt: sock_no_setsockopt,
++ getsockopt: sock_no_getsockopt,
++ connect: sock_no_connect,
++ socketpair: sock_no_socketpair,
++ accept: sock_no_accept,
++ mmap: sock_no_mmap
++};
++
++static int bnep_sock_create(struct socket *sock, int protocol)
++{
++ struct sock *sk;
++
++ BT_DBG("sock %p", sock);
++
++ if (sock->type != SOCK_RAW)
++ return -ESOCKTNOSUPPORT;
++
++ sock->ops = &bnep_sock_ops;
++
++ if (!(sk = sk_alloc(PF_BLUETOOTH, GFP_KERNEL, 1)))
++ return -ENOMEM;
++
++ MOD_INC_USE_COUNT;
++
++ sock->state = SS_UNCONNECTED;
++ sock_init_data(sock, sk);
++
++ sk->destruct = NULL;
++ sk->protocol = protocol;
++
++ return 0;
++}
++
++static struct net_proto_family bnep_sock_family_ops = {
++ family: PF_BLUETOOTH,
++ create: bnep_sock_create
++};
++
++int bnep_sock_init(void)
++{
++ bluez_sock_register(BTPROTO_BNEP, &bnep_sock_family_ops);
++ return 0;
++}
++
++int bnep_sock_cleanup(void)
++{
++ if (bluez_sock_unregister(BTPROTO_BNEP))
++ BT_ERR("Can't unregister BNEP socket");
++ return 0;
++}
+diff -urN linux-2.4.18/net/bluetooth/cmtp/capi.c linux-2.4.18-mh15/net/bluetooth/cmtp/capi.c
+--- linux-2.4.18/net/bluetooth/cmtp/capi.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/net/bluetooth/cmtp/capi.c 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,707 @@
++/*
++ CMTP implementation for Linux Bluetooth stack (BlueZ).
++ Copyright (C) 2002-2003 Marcel Holtmann <marcel@holtmann.org>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License version 2 as
++ published by the Free Software Foundation;
++
++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++
++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
++ SOFTWARE IS DISCLAIMED.
++*/
++
++#include <linux/config.h>
++#include <linux/module.h>
++
++#include <linux/types.h>
++#include <linux/errno.h>
++#include <linux/kernel.h>
++#include <linux/major.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/poll.h>
++#include <linux/fcntl.h>
++#include <linux/skbuff.h>
++#include <linux/socket.h>
++#include <linux/ioctl.h>
++#include <linux/file.h>
++#include <net/sock.h>
++
++#include <linux/capi.h>
++
++#include "../drivers/isdn/avmb1/capilli.h"
++#include "../drivers/isdn/avmb1/capicmd.h"
++#include "../drivers/isdn/avmb1/capiutil.h"
++
++#include "cmtp.h"
++
++#ifndef CONFIG_BLUEZ_CMTP_DEBUG
++#undef BT_DBG
++#define BT_DBG(D...)
++#endif
++
++#define REVISION "1.0"
++
++#define CAPI_INTEROPERABILITY 0x20
++
++#define CAPI_INTEROPERABILITY_REQ CAPICMD(CAPI_INTEROPERABILITY, CAPI_REQ)
++#define CAPI_INTEROPERABILITY_CONF CAPICMD(CAPI_INTEROPERABILITY, CAPI_CONF)
++#define CAPI_INTEROPERABILITY_IND CAPICMD(CAPI_INTEROPERABILITY, CAPI_IND)
++#define CAPI_INTEROPERABILITY_RESP CAPICMD(CAPI_INTEROPERABILITY, CAPI_RESP)
++
++#define CAPI_INTEROPERABILITY_REQ_LEN (CAPI_MSG_BASELEN + 2)
++#define CAPI_INTEROPERABILITY_CONF_LEN (CAPI_MSG_BASELEN + 4)
++#define CAPI_INTEROPERABILITY_IND_LEN (CAPI_MSG_BASELEN + 2)
++#define CAPI_INTEROPERABILITY_RESP_LEN (CAPI_MSG_BASELEN + 2)
++
++#define CAPI_FUNCTION_REGISTER 0
++#define CAPI_FUNCTION_RELEASE 1
++#define CAPI_FUNCTION_GET_PROFILE 2
++#define CAPI_FUNCTION_GET_MANUFACTURER 3
++#define CAPI_FUNCTION_GET_VERSION 4
++#define CAPI_FUNCTION_GET_SERIAL_NUMBER 5
++#define CAPI_FUNCTION_MANUFACTURER 6
++#define CAPI_FUNCTION_LOOPBACK 7
++
++static struct capi_driver_interface *di;
++
++
++#define CMTP_MSGNUM 1
++#define CMTP_APPLID 2
++#define CMTP_MAPPING 3
++
++static struct cmtp_application *cmtp_application_add(struct cmtp_session *session, __u16 appl)
++{
++ struct cmtp_application *app = kmalloc(sizeof(*app), GFP_KERNEL);
++
++ BT_DBG("session %p application %p appl %d", session, app, appl);
++
++ if (!app)
++ return NULL;
++
++ memset(app, 0, sizeof(*app));
++
++ app->state = BT_OPEN;
++ app->appl = appl;
++
++ list_add_tail(&app->list, &session->applications);
++
++ return app;
++}
++
++static void cmtp_application_del(struct cmtp_session *session, struct cmtp_application *app)
++{
++ BT_DBG("session %p application %p", session, app);
++
++ if (app) {
++ list_del(&app->list);
++ kfree(app);
++ }
++}
++
++static struct cmtp_application *cmtp_application_get(struct cmtp_session *session, int pattern, __u16 value)
++{
++ struct cmtp_application *app;
++ struct list_head *p, *n;
++
++ list_for_each_safe(p, n, &session->applications) {
++ app = list_entry(p, struct cmtp_application, list);
++ switch (pattern) {
++ case CMTP_MSGNUM:
++ if (app->msgnum == value)
++ return app;
++ break;
++ case CMTP_APPLID:
++ if (app->appl == value)
++ return app;
++ break;
++ case CMTP_MAPPING:
++ if (app->mapping == value)
++ return app;
++ break;
++ }
++ }
++
++ return NULL;
++}
++
++static int cmtp_msgnum_get(struct cmtp_session *session)
++{
++ session->msgnum++;
++
++ if ((session->msgnum & 0xff) > 200)
++ session->msgnum = CMTP_INITIAL_MSGNUM + 1;
++
++ return session->msgnum;
++}
++
++
++static void cmtp_send_interopmsg(struct cmtp_session *session,
++ __u8 subcmd, __u16 appl, __u16 msgnum,
++ __u16 function, unsigned char *buf, int len)
++{
++ struct sk_buff *skb;
++ unsigned char *s;
++
++ BT_DBG("session %p subcmd 0x%02x appl %d msgnum %d", session, subcmd, appl, msgnum);
++
++ if (!(skb = alloc_skb(CAPI_MSG_BASELEN + 6 + len, GFP_ATOMIC))) {
++ BT_ERR("Can't allocate memory for interoperability packet");
++ return;
++ }
++
++ s = skb_put(skb, CAPI_MSG_BASELEN + 6 + len);
++
++ capimsg_setu16(s, 0, CAPI_MSG_BASELEN + 6 + len);
++ capimsg_setu16(s, 2, appl);
++ capimsg_setu8 (s, 4, CAPI_INTEROPERABILITY);
++ capimsg_setu8 (s, 5, subcmd);
++ capimsg_setu16(s, 6, msgnum);
++
++ /* Interoperability selector (Bluetooth Device Management) */
++ capimsg_setu16(s, 8, 0x0001);
++
++ capimsg_setu8 (s, 10, 3 + len);
++ capimsg_setu16(s, 11, function);
++ capimsg_setu8 (s, 13, len);
++
++ if (len > 0)
++ memcpy(s + 14, buf, len);
++
++ cmtp_send_capimsg(session, skb);
++}
++
++static void cmtp_recv_interopmsg(struct cmtp_session *session, struct sk_buff *skb)
++{
++ struct capi_ctr *ctrl = session->ctrl;
++ struct cmtp_application *application;
++ __u16 appl, msgnum, func, info;
++ __u32 controller;
++
++ BT_DBG("session %p skb %p len %d", session, skb, skb->len);
++
++ switch (CAPIMSG_SUBCOMMAND(skb->data)) {
++ case CAPI_CONF:
++ func = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 5);
++ info = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 8);
++
++ switch (func) {
++ case CAPI_FUNCTION_REGISTER:
++ msgnum = CAPIMSG_MSGID(skb->data);
++
++ application = cmtp_application_get(session, CMTP_MSGNUM, msgnum);
++ if (application) {
++ application->state = BT_CONNECTED;
++ application->msgnum = 0;
++ application->mapping = CAPIMSG_APPID(skb->data);
++ wake_up_interruptible(&session->wait);
++ }
++
++ break;
++
++ case CAPI_FUNCTION_RELEASE:
++ appl = CAPIMSG_APPID(skb->data);
++
++ application = cmtp_application_get(session, CMTP_MAPPING, appl);
++ if (application) {
++ application->state = BT_CLOSED;
++ application->msgnum = 0;
++ wake_up_interruptible(&session->wait);
++ }
++
++ break;
++
++ case CAPI_FUNCTION_GET_PROFILE:
++ controller = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 11);
++ msgnum = CAPIMSG_MSGID(skb->data);
++
++ if (!info && (msgnum == CMTP_INITIAL_MSGNUM)) {
++ session->ncontroller = controller;
++ wake_up_interruptible(&session->wait);
++ break;
++ }
++
++ if (!info && ctrl) {
++ memcpy(&ctrl->profile,
++ skb->data + CAPI_MSG_BASELEN + 11,
++ sizeof(capi_profile));
++ session->state = BT_CONNECTED;
++ ctrl->ready(ctrl);
++ }
++
++ break;
++
++ case CAPI_FUNCTION_GET_MANUFACTURER:
++ controller = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 10);
++
++ if (!info && ctrl) {
++ strncpy(ctrl->manu,
++ skb->data + CAPI_MSG_BASELEN + 15,
++ skb->data[CAPI_MSG_BASELEN + 14]);
++ }
++
++ break;
++
++ case CAPI_FUNCTION_GET_VERSION:
++ controller = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 12);
++
++ if (!info && ctrl) {
++ ctrl->version.majorversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 16);
++ ctrl->version.minorversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 20);
++ ctrl->version.majormanuversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 24);
++ ctrl->version.minormanuversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 28);
++ }
++
++ break;
++
++ case CAPI_FUNCTION_GET_SERIAL_NUMBER:
++ controller = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 12);
++
++ if (!info && ctrl) {
++ memset(ctrl->serial, 0, CAPI_SERIAL_LEN);
++ strncpy(ctrl->serial,
++ skb->data + CAPI_MSG_BASELEN + 17,
++ skb->data[CAPI_MSG_BASELEN + 16]);
++ }
++
++ break;
++ }
++
++ break;
++
++ case CAPI_IND:
++ func = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 3);
++
++ if (func == CAPI_FUNCTION_LOOPBACK) {
++ appl = CAPIMSG_APPID(skb->data);
++ msgnum = CAPIMSG_MSGID(skb->data);
++ cmtp_send_interopmsg(session, CAPI_RESP, appl, msgnum, func,
++ skb->data + CAPI_MSG_BASELEN + 6,
++ skb->data[CAPI_MSG_BASELEN + 5]);
++ }
++
++ break;
++ }
++
++ kfree_skb(skb);
++}
++
++void cmtp_recv_capimsg(struct cmtp_session *session, struct sk_buff *skb)
++{
++ struct capi_ctr *ctrl = session->ctrl;
++ struct cmtp_application *application;
++ __u16 cmd, appl, info;
++ __u32 ncci, contr;
++
++ BT_DBG("session %p skb %p len %d", session, skb, skb->len);
++
++ if (CAPIMSG_COMMAND(skb->data) == CAPI_INTEROPERABILITY) {
++ cmtp_recv_interopmsg(session, skb);
++ return;
++ }
++
++ if (session->flags & (1 << CMTP_LOOPBACK)) {
++ kfree_skb(skb);
++ return;
++ }
++
++ cmd = CAPICMD(CAPIMSG_COMMAND(skb->data), CAPIMSG_SUBCOMMAND(skb->data));
++ appl = CAPIMSG_APPID(skb->data);
++ contr = CAPIMSG_CONTROL(skb->data);
++
++ application = cmtp_application_get(session, CMTP_MAPPING, appl);
++ if (application) {
++ appl = application->appl;
++ CAPIMSG_SETAPPID(skb->data, appl);
++ } else {
++ BT_ERR("Can't find application with id %d", appl);
++ kfree_skb(skb);
++ return;
++ }
++
++ if ((contr & 0x7f) == 0x01) {
++ contr = (contr & 0xffffff80) | session->num;
++ CAPIMSG_SETCONTROL(skb->data, contr);
++ }
++
++ if (!ctrl) {
++ BT_ERR("Can't find controller %d for message", session->num);
++ kfree_skb(skb);
++ return;
++ }
++
++ switch (cmd) {
++ case CAPI_CONNECT_B3_CONF:
++ ncci = CAPIMSG_NCCI(skb->data);
++ info = CAPIMSG_U16(skb->data, 12);
++
++ BT_DBG("CONNECT_B3_CONF ncci 0x%02x info 0x%02x", ncci, info);
++
++ if (info == 0)
++ ctrl->new_ncci(ctrl, appl, ncci, 8);
++
++ ctrl->handle_capimsg(ctrl, appl, skb);
++ break;
++
++ case CAPI_CONNECT_B3_IND:
++ ncci = CAPIMSG_NCCI(skb->data);
++
++ BT_DBG("CONNECT_B3_IND ncci 0x%02x", ncci);
++
++ ctrl->new_ncci(ctrl, appl, ncci, 8);
++ ctrl->handle_capimsg(ctrl, appl, skb);
++ break;
++
++ case CAPI_DISCONNECT_B3_IND:
++ ncci = CAPIMSG_NCCI(skb->data);
++
++ BT_DBG("DISCONNECT_B3_IND ncci 0x%02x", ncci);
++
++ if (ncci == 0xffffffff)
++ BT_ERR("DISCONNECT_B3_IND with ncci 0xffffffff");
++
++ ctrl->handle_capimsg(ctrl, appl, skb);
++ ctrl->free_ncci(ctrl, appl, ncci);
++ break;
++
++ default:
++ ctrl->handle_capimsg(ctrl, appl, skb);
++ break;
++ }
++}
++
++void cmtp_send_capimsg(struct cmtp_session *session, struct sk_buff *skb)
++{
++ struct cmtp_scb *scb = (void *) skb->cb;
++
++ BT_DBG("session %p skb %p len %d", session, skb, skb->len);
++
++ scb->id = -1;
++ scb->data = (CAPIMSG_COMMAND(skb->data) == CAPI_DATA_B3);
++
++ skb_queue_tail(&session->transmit, skb);
++
++ cmtp_schedule(session);
++}
++
++
++static int cmtp_load_firmware(struct capi_ctr *ctrl, capiloaddata *data)
++{
++ BT_DBG("ctrl %p data %p", ctrl, data);
++
++ return -EIO;
++}
++
++static void cmtp_reset_ctr(struct capi_ctr *ctrl)
++{
++ BT_DBG("ctrl %p", ctrl);
++
++ ctrl->reseted(ctrl);
++}
++
++static void cmtp_remove_ctr(struct capi_ctr *ctrl)
++{
++ struct cmtp_session *session = ctrl->driverdata;
++
++ BT_DBG("ctrl %p", ctrl);
++
++ ctrl->suspend_output(ctrl);
++
++ atomic_inc(&session->terminate);
++ cmtp_schedule(session);
++}
++
++static void cmtp_register_appl(struct capi_ctr *ctrl, __u16 appl, capi_register_params *rp)
++{
++ DECLARE_WAITQUEUE(wait, current);
++ struct cmtp_session *session = ctrl->driverdata;
++ struct cmtp_application *application;
++ unsigned long timeo = CMTP_INTEROP_TIMEOUT;
++ unsigned char buf[8];
++ int err = 0, nconn, want = rp->level3cnt;
++
++ BT_DBG("ctrl %p appl %d level3cnt %d datablkcnt %d datablklen %d",
++ ctrl, appl, rp->level3cnt, rp->datablkcnt, rp->datablklen);
++
++ application = cmtp_application_add(session, appl);
++ if (!application) {
++ BT_ERR("Can't allocate memory for new application");
++ ctrl->appl_released(ctrl, appl);
++ return;
++ }
++
++ if (want < 0)
++ nconn = ctrl->profile.nbchannel * -want;
++ else
++ nconn = want;
++
++ if (nconn == 0)
++ nconn = ctrl->profile.nbchannel;
++
++ capimsg_setu16(buf, 0, nconn);
++ capimsg_setu16(buf, 2, rp->datablkcnt);
++ capimsg_setu16(buf, 4, rp->datablklen);
++
++ application->state = BT_CONFIG;
++ application->msgnum = cmtp_msgnum_get(session);
++
++ cmtp_send_interopmsg(session, CAPI_REQ, 0x0000, application->msgnum,
++ CAPI_FUNCTION_REGISTER, buf, 6);
++
++ add_wait_queue(&session->wait, &wait);
++ while (1) {
++ set_current_state(TASK_INTERRUPTIBLE);
++
++ if (!timeo) {
++ err = -EAGAIN;
++ break;
++ }
++
++ if (application->state == BT_CLOSED) {
++ err = -application->err;
++ break;
++ }
++
++ if (application->state == BT_CONNECTED)
++ break;
++
++ if (signal_pending(current)) {
++ err = -EINTR;
++ break;
++ }
++
++ timeo = schedule_timeout(timeo);
++ }
++ set_current_state(TASK_RUNNING);
++ remove_wait_queue(&session->wait, &wait);
++
++ if (err) {
++ ctrl->appl_released(ctrl, appl);
++ cmtp_application_del(session, application);
++ return;
++ }
++
++ ctrl->appl_registered(ctrl, appl);
++}
++
++static void cmtp_release_appl(struct capi_ctr *ctrl, __u16 appl)
++{
++ DECLARE_WAITQUEUE(wait, current);
++ struct cmtp_session *session = ctrl->driverdata;
++ struct cmtp_application *application;
++ unsigned long timeo = CMTP_INTEROP_TIMEOUT;
++
++ BT_DBG("ctrl %p appl %d", ctrl, appl);
++
++ application = cmtp_application_get(session, CMTP_APPLID, appl);
++ if (!application) {
++ BT_ERR("Can't find application");
++ return;
++ }
++
++ application->msgnum = cmtp_msgnum_get(session);
++
++ cmtp_send_interopmsg(session, CAPI_REQ, application->mapping, application->msgnum,
++ CAPI_FUNCTION_RELEASE, NULL, 0);
++
++ add_wait_queue(&session->wait, &wait);
++ while (timeo) {
++ set_current_state(TASK_INTERRUPTIBLE);
++
++ if (application->state == BT_CLOSED)
++ break;
++
++ if (signal_pending(current))
++ break;
++
++ timeo = schedule_timeout(timeo);
++ }
++ set_current_state(TASK_RUNNING);
++ remove_wait_queue(&session->wait, &wait);
++
++ cmtp_application_del(session, application);
++ ctrl->appl_released(ctrl, appl);
++}
++
++static void cmtp_send_message(struct capi_ctr *ctrl, struct sk_buff *skb)
++{
++ struct cmtp_session *session = ctrl->driverdata;
++ struct cmtp_application *application;
++ __u16 appl;
++ __u32 contr;
++
++ BT_DBG("ctrl %p skb %p", ctrl, skb);
++
++ appl = CAPIMSG_APPID(skb->data);
++ contr = CAPIMSG_CONTROL(skb->data);
++
++ application = cmtp_application_get(session, CMTP_APPLID, appl);
++ if ((!application) || (application->state != BT_CONNECTED)) {
++ BT_ERR("Can't find application with id %d", appl);
++ kfree_skb(skb);
++ return;
++ }
++
++ CAPIMSG_SETAPPID(skb->data, application->mapping);
++
++ if ((contr & 0x7f) == session->num) {
++ contr = (contr & 0xffffff80) | 0x01;
++ CAPIMSG_SETCONTROL(skb->data, contr);
++ }
++
++ cmtp_send_capimsg(session, skb);
++}
++
++static char *cmtp_procinfo(struct capi_ctr *ctrl)
++{
++ return "CAPI Message Transport Protocol";
++}
++
++static int cmtp_ctr_read_proc(char *page, char **start, off_t off, int count, int *eof, struct capi_ctr *ctrl)
++{
++ struct cmtp_session *session = ctrl->driverdata;
++ struct cmtp_application *app;
++ struct list_head *p, *n;
++ int len = 0;
++
++ len += sprintf(page + len, "%s (Revision %s)\n\n", cmtp_procinfo(ctrl), REVISION);
++ len += sprintf(page + len, "addr %s\n", session->name);
++ len += sprintf(page + len, "ctrl %d\n", session->num);
++
++ list_for_each_safe(p, n, &session->applications) {
++ app = list_entry(p, struct cmtp_application, list);
++ len += sprintf(page + len, "appl %d -> %d\n", app->appl, app->mapping);
++ }
++
++ if (off + count >= len)
++ *eof = 1;
++
++ if (len < off)
++ return 0;
++
++ *start = page + off;
++
++ return ((count < len - off) ? count : len - off);
++}
++
++static struct capi_driver cmtp_driver = {
++ name: "cmtp",
++ revision: REVISION,
++ load_firmware: cmtp_load_firmware,
++ reset_ctr: cmtp_reset_ctr,
++ remove_ctr: cmtp_remove_ctr,
++ register_appl: cmtp_register_appl,
++ release_appl: cmtp_release_appl,
++ send_message: cmtp_send_message,
++ procinfo: cmtp_procinfo,
++ ctr_read_proc: cmtp_ctr_read_proc,
++
++ driver_read_proc: 0,
++ add_card: 0,
++};
++
++
++int cmtp_attach_device(struct cmtp_session *session)
++{
++ DECLARE_WAITQUEUE(wait, current);
++ unsigned long timeo = CMTP_INTEROP_TIMEOUT;
++ unsigned char buf[4];
++
++ BT_DBG("session %p", session);
++
++ capimsg_setu32(buf, 0, 0);
++
++ cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, CMTP_INITIAL_MSGNUM,
++ CAPI_FUNCTION_GET_PROFILE, buf, 4);
++
++ add_wait_queue(&session->wait, &wait);
++ while (timeo) {
++ set_current_state(TASK_INTERRUPTIBLE);
++
++ if (session->ncontroller)
++ break;
++
++ if (signal_pending(current))
++ break;
++
++ timeo = schedule_timeout(timeo);
++ }
++ set_current_state(TASK_RUNNING);
++ remove_wait_queue(&session->wait, &wait);
++
++ BT_INFO("Found %d CAPI controller(s) on device %s", session->ncontroller, session->name);
++
++ if (!timeo)
++ return -ETIMEDOUT;
++
++ if (!session->ncontroller)
++ return -ENODEV;
++
++
++ if (session->ncontroller > 1)
++ BT_INFO("Setting up only CAPI controller 1");
++
++ if (!(session->ctrl = di->attach_ctr(&cmtp_driver, session->name, session))) {
++ BT_ERR("Can't attach new controller");
++ return -EBUSY;
++ }
++
++ session->num = session->ctrl->cnr;
++
++ BT_DBG("session %p ctrl %p num %d", session, session->ctrl, session->num);
++
++ capimsg_setu32(buf, 0, 1);
++
++ cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session),
++ CAPI_FUNCTION_GET_MANUFACTURER, buf, 4);
++
++ cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session),
++ CAPI_FUNCTION_GET_VERSION, buf, 4);
++
++ cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session),
++ CAPI_FUNCTION_GET_SERIAL_NUMBER, buf, 4);
++
++ cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session),
++ CAPI_FUNCTION_GET_PROFILE, buf, 4);
++
++ return 0;
++}
++
++void cmtp_detach_device(struct cmtp_session *session)
++{
++ struct capi_ctr *ctrl = session->ctrl;
++
++ BT_DBG("session %p ctrl %p", session, ctrl);
++
++ if (!ctrl)
++ return;
++
++ ctrl->reseted(ctrl);
++
++ di->detach_ctr(ctrl);
++}
++
++int cmtp_init_capi(void)
++{
++ if (!(di = attach_capi_driver(&cmtp_driver))) {
++ BT_ERR("Can't attach CAPI driver");
++ return -EIO;
++ }
++
++ return 0;
++}
++
++void cmtp_cleanup_capi(void)
++{
++ detach_capi_driver(&cmtp_driver);
++}
+diff -urN linux-2.4.18/net/bluetooth/cmtp/cmtp.h linux-2.4.18-mh15/net/bluetooth/cmtp/cmtp.h
+--- linux-2.4.18/net/bluetooth/cmtp/cmtp.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/net/bluetooth/cmtp/cmtp.h 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,138 @@
++/*
++ CMTP implementation for Linux Bluetooth stack (BlueZ).
++ Copyright (C) 2002-2003 Marcel Holtmann <marcel@holtmann.org>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License version 2 as
++ published by the Free Software Foundation;
++
++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++
++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
++ SOFTWARE IS DISCLAIMED.
++*/
++
++#ifndef __CMTP_H
++#define __CMTP_H
++
++#include <linux/types.h>
++#include <net/bluetooth/bluetooth.h>
++
++#define BTNAMSIZ 18
++
++/* CMTP ioctl defines */
++#define CMTPCONNADD _IOW('C', 200, int)
++#define CMTPCONNDEL _IOW('C', 201, int)
++#define CMTPGETCONNLIST _IOR('C', 210, int)
++#define CMTPGETCONNINFO _IOR('C', 211, int)
++
++#define CMTP_LOOPBACK 0
++
++struct cmtp_connadd_req {
++ int sock; // Connected socket
++ __u32 flags;
++};
++
++struct cmtp_conndel_req {
++ bdaddr_t bdaddr;
++ __u32 flags;
++};
++
++struct cmtp_conninfo {
++ bdaddr_t bdaddr;
++ __u32 flags;
++ __u16 state;
++ int num;
++};
++
++struct cmtp_connlist_req {
++ __u32 cnum;
++ struct cmtp_conninfo *ci;
++};
++
++int cmtp_add_connection(struct cmtp_connadd_req *req, struct socket *sock);
++int cmtp_del_connection(struct cmtp_conndel_req *req);
++int cmtp_get_connlist(struct cmtp_connlist_req *req);
++int cmtp_get_conninfo(struct cmtp_conninfo *ci);
++
++/* CMTP session defines */
++#define CMTP_INTEROP_TIMEOUT (HZ * 5)
++#define CMTP_INITIAL_MSGNUM 0xff00
++
++struct cmtp_session {
++ struct list_head list;
++
++ struct socket *sock;
++
++ bdaddr_t bdaddr;
++
++ unsigned long state;
++ unsigned long flags;
++
++ uint mtu;
++
++ char name[BTNAMSIZ];
++
++ atomic_t terminate;
++
++ wait_queue_head_t wait;
++
++ int ncontroller;
++ int num;
++ struct capi_ctr *ctrl;
++
++ struct list_head applications;
++
++ unsigned long blockids;
++ int msgnum;
++
++ struct sk_buff_head transmit;
++
++ struct sk_buff *reassembly[16];
++};
++
++struct cmtp_application {
++ struct list_head list;
++
++ unsigned long state;
++ int err;
++
++ __u16 appl;
++ __u16 mapping;
++
++ __u16 msgnum;
++};
++
++struct cmtp_scb {
++ int id;
++ int data;
++};
++
++int cmtp_attach_device(struct cmtp_session *session);
++void cmtp_detach_device(struct cmtp_session *session);
++
++void cmtp_recv_capimsg(struct cmtp_session *session, struct sk_buff *skb);
++void cmtp_send_capimsg(struct cmtp_session *session, struct sk_buff *skb);
++
++static inline void cmtp_schedule(struct cmtp_session *session)
++{
++ struct sock *sk = session->sock->sk;
++
++ wake_up_interruptible(sk->sleep);
++}
++
++/* CMTP init defines */
++int cmtp_init_capi(void);
++int cmtp_init_sockets(void);
++void cmtp_cleanup_capi(void);
++void cmtp_cleanup_sockets(void);
++
++#endif /* __CMTP_H */
+diff -urN linux-2.4.18/net/bluetooth/cmtp/Config.in linux-2.4.18-mh15/net/bluetooth/cmtp/Config.in
+--- linux-2.4.18/net/bluetooth/cmtp/Config.in 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/net/bluetooth/cmtp/Config.in 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,7 @@
++#
++# Bluetooth CMTP layer configuration
++#
++
++if [ "$CONFIG_ISDN" = "y" -o "$CONFIG_ISDN" = "m" ]; then
++ dep_tristate 'CMTP protocol support' CONFIG_BLUEZ_CMTP $CONFIG_ISDN_CAPI $CONFIG_BLUEZ_L2CAP
++fi
+diff -urN linux-2.4.18/net/bluetooth/cmtp/core.c linux-2.4.18-mh15/net/bluetooth/cmtp/core.c
+--- linux-2.4.18/net/bluetooth/cmtp/core.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/net/bluetooth/cmtp/core.c 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,515 @@
++/*
++ CMTP implementation for Linux Bluetooth stack (BlueZ).
++ Copyright (C) 2002-2003 Marcel Holtmann <marcel@holtmann.org>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License version 2 as
++ published by the Free Software Foundation;
++
++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++
++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
++ SOFTWARE IS DISCLAIMED.
++*/
++
++#include <linux/config.h>
++#include <linux/module.h>
++
++#include <linux/types.h>
++#include <linux/errno.h>
++#include <linux/kernel.h>
++#include <linux/major.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/poll.h>
++#include <linux/fcntl.h>
++#include <linux/skbuff.h>
++#include <linux/socket.h>
++#include <linux/ioctl.h>
++#include <linux/file.h>
++#include <linux/init.h>
++#include <net/sock.h>
++
++#include <net/bluetooth/bluetooth.h>
++#include <net/bluetooth/l2cap.h>
++
++#include "cmtp.h"
++
++#ifndef CONFIG_BLUEZ_CMTP_DEBUG
++#undef BT_DBG
++#define BT_DBG(D...)
++#endif
++
++#define VERSION "1.0"
++
++static DECLARE_RWSEM(cmtp_session_sem);
++static LIST_HEAD(cmtp_session_list);
++
++static struct cmtp_session *__cmtp_get_session(bdaddr_t *bdaddr)
++{
++ struct cmtp_session *session;
++ struct list_head *p;
++
++ BT_DBG("");
++
++ list_for_each(p, &cmtp_session_list) {
++ session = list_entry(p, struct cmtp_session, list);
++ if (!bacmp(bdaddr, &session->bdaddr))
++ return session;
++ }
++ return NULL;
++}
++
++static void __cmtp_link_session(struct cmtp_session *session)
++{
++ MOD_INC_USE_COUNT;
++ list_add(&session->list, &cmtp_session_list);
++}
++
++static void __cmtp_unlink_session(struct cmtp_session *session)
++{
++ list_del(&session->list);
++ MOD_DEC_USE_COUNT;
++}
++
++static void __cmtp_copy_session(struct cmtp_session *session, struct cmtp_conninfo *ci)
++{
++ bacpy(&ci->bdaddr, &session->bdaddr);
++
++ ci->flags = session->flags;
++ ci->state = session->state;
++
++ ci->num = session->num;
++}
++
++
++static inline int cmtp_alloc_block_id(struct cmtp_session *session)
++{
++ int i, id = -1;
++
++ for (i = 0; i < 16; i++)
++ if (!test_and_set_bit(i, &session->blockids)) {
++ id = i;
++ break;
++ }
++
++ return id;
++}
++
++static inline void cmtp_free_block_id(struct cmtp_session *session, int id)
++{
++ clear_bit(id, &session->blockids);
++}
++
++static inline void cmtp_add_msgpart(struct cmtp_session *session, int id, const unsigned char *buf, int count)
++{
++ struct sk_buff *skb = session->reassembly[id], *nskb;
++ int size;
++
++ BT_DBG("session %p buf %p count %d", session, buf, count);
++
++ size = (skb) ? skb->len + count : count;
++
++ if (!(nskb = alloc_skb(size, GFP_ATOMIC))) {
++ BT_ERR("Can't allocate memory for CAPI message");
++ return;
++ }
++
++ if (skb && (skb->len > 0))
++ memcpy(skb_put(nskb, skb->len), skb->data, skb->len);
++
++ memcpy(skb_put(nskb, count), buf, count);
++
++ session->reassembly[id] = nskb;
++
++ if (skb)
++ kfree_skb(skb);
++}
++
++static inline int cmtp_recv_frame(struct cmtp_session *session, struct sk_buff *skb)
++{
++ __u8 hdr, hdrlen, id;
++ __u16 len;
++
++ BT_DBG("session %p skb %p len %d", session, skb, skb->len);
++
++ while (skb->len > 0) {
++ hdr = skb->data[0];
++
++ switch (hdr & 0xc0) {
++ case 0x40:
++ hdrlen = 2;
++ len = skb->data[1];
++ break;
++ case 0x80:
++ hdrlen = 3;
++ len = skb->data[1] | (skb->data[2] << 8);
++ break;
++ default:
++ hdrlen = 1;
++ len = 0;
++ break;
++ }
++
++ id = (hdr & 0x3c) >> 2;
++
++ BT_DBG("hdr 0x%02x hdrlen %d len %d id %d", hdr, hdrlen, len, id);
++
++ if (hdrlen + len > skb->len) {
++ BT_ERR("Wrong size or header information in CMTP frame");
++ break;
++ }
++
++ if (len == 0) {
++ skb_pull(skb, hdrlen);
++ continue;
++ }
++
++ switch (hdr & 0x03) {
++ case 0x00:
++ cmtp_add_msgpart(session, id, skb->data + hdrlen, len);
++ cmtp_recv_capimsg(session, session->reassembly[id]);
++ session->reassembly[id] = NULL;
++ break;
++ case 0x01:
++ cmtp_add_msgpart(session, id, skb->data + hdrlen, len);
++ break;
++ default:
++ if (session->reassembly[id] != NULL)
++ kfree_skb(session->reassembly[id]);
++ session->reassembly[id] = NULL;
++ break;
++ }
++
++ skb_pull(skb, hdrlen + len);
++ }
++
++ kfree_skb(skb);
++ return 0;
++}
++
++static int cmtp_send_frame(struct cmtp_session *session, unsigned char *data, int len)
++{
++ struct socket *sock = session->sock;
++ struct iovec iv = { data, len };
++ struct msghdr msg;
++ int err;
++
++ BT_DBG("session %p data %p len %d", session, data, len);
++
++ if (!len)
++ return 0;
++
++ memset(&msg, 0, sizeof(msg));
++ msg.msg_iovlen = 1;
++ msg.msg_iov = &iv;
++
++ err = sock->ops->sendmsg(sock, &msg, len, 0);
++ return err;
++}
++
++static int cmtp_process_transmit(struct cmtp_session *session)
++{
++ struct sk_buff *skb, *nskb;
++ unsigned char *hdr;
++ unsigned int size, tail;
++
++ BT_DBG("session %p", session);
++
++ if (!(nskb = alloc_skb(session->mtu, GFP_ATOMIC))) {
++ BT_ERR("Can't allocate memory for new frame");
++ return -ENOMEM;
++ }
++
++ while ((skb = skb_dequeue(&session->transmit))) {
++ struct cmtp_scb *scb = (void *) skb->cb;
++
++ if ((tail = (session->mtu - nskb->len)) < 5) {
++ cmtp_send_frame(session, nskb->data, nskb->len);
++ skb_trim(nskb, 0);
++ tail = session->mtu;
++ }
++
++ size = min_t(uint, ((tail < 258) ? (tail - 2) : (tail - 3)), skb->len);
++
++ if ((scb->id < 0) && ((scb->id = cmtp_alloc_block_id(session)) < 0)) {
++ skb_queue_head(&session->transmit, skb);
++ break;
++ }
++
++ if (size < 256) {
++ hdr = skb_put(nskb, 2);
++ hdr[0] = 0x40
++ | ((scb->id << 2) & 0x3c)
++ | ((skb->len == size) ? 0x00 : 0x01);
++ hdr[1] = size;
++ } else {
++ hdr = skb_put(nskb, 3);
++ hdr[0] = 0x80
++ | ((scb->id << 2) & 0x3c)
++ | ((skb->len == size) ? 0x00 : 0x01);
++ hdr[1] = size & 0xff;
++ hdr[2] = size >> 8;
++ }
++
++ memcpy(skb_put(nskb, size), skb->data, size);
++ skb_pull(skb, size);
++
++ if (skb->len > 0) {
++ skb_queue_head(&session->transmit, skb);
++ } else {
++ cmtp_free_block_id(session, scb->id);
++ if (scb->data) {
++ cmtp_send_frame(session, nskb->data, nskb->len);
++ skb_trim(nskb, 0);
++ }
++ kfree_skb(skb);
++ }
++ }
++
++ cmtp_send_frame(session, nskb->data, nskb->len);
++
++ kfree_skb(nskb);
++
++ return skb_queue_len(&session->transmit);
++}
++
++static int cmtp_session(void *arg)
++{
++ struct cmtp_session *session = arg;
++ struct sock *sk = session->sock->sk;
++ struct sk_buff *skb;
++ wait_queue_t wait;
++
++ BT_DBG("session %p", session);
++
++ daemonize(); reparent_to_init();
++
++ sprintf(current->comm, "kcmtpd_ctr_%d", session->num);
++
++ sigfillset(&current->blocked);
++ flush_signals(current);
++
++ current->nice = -15;
++
++ set_fs(KERNEL_DS);
++
++ init_waitqueue_entry(&wait, current);
++ add_wait_queue(sk->sleep, &wait);
++ while (!atomic_read(&session->terminate)) {
++ set_current_state(TASK_INTERRUPTIBLE);
++
++ if (sk->state != BT_CONNECTED)
++ break;
++
++ while ((skb = skb_dequeue(&sk->receive_queue))) {
++ skb_orphan(skb);
++ cmtp_recv_frame(session, skb);
++ }
++
++ cmtp_process_transmit(session);
++
++ schedule();
++ }
++ set_current_state(TASK_RUNNING);
++ remove_wait_queue(sk->sleep, &wait);
++
++ down_write(&cmtp_session_sem);
++
++ if (!(session->flags & (1 << CMTP_LOOPBACK)))
++ cmtp_detach_device(session);
++
++ fput(session->sock->file);
++
++ __cmtp_unlink_session(session);
++
++ up_write(&cmtp_session_sem);
++
++ kfree(session);
++ return 0;
++}
++
++int cmtp_add_connection(struct cmtp_connadd_req *req, struct socket *sock)
++{
++ struct cmtp_session *session, *s;
++ bdaddr_t src, dst;
++ int i, err;
++
++ BT_DBG("");
++
++ baswap(&src, &bluez_pi(sock->sk)->src);
++ baswap(&dst, &bluez_pi(sock->sk)->dst);
++
++ session = kmalloc(sizeof(struct cmtp_session), GFP_KERNEL);
++ if (!session)
++ return -ENOMEM;
++ memset(session, 0, sizeof(struct cmtp_session));
++
++ down_write(&cmtp_session_sem);
++
++ s = __cmtp_get_session(&bluez_pi(sock->sk)->dst);
++ if (s && s->state == BT_CONNECTED) {
++ err = -EEXIST;
++ goto failed;
++ }
++
++ bacpy(&session->bdaddr, &bluez_pi(sock->sk)->dst);
++
++ session->mtu = min_t(uint, l2cap_pi(sock->sk)->omtu, l2cap_pi(sock->sk)->imtu);
++
++ BT_DBG("mtu %d", session->mtu);
++
++ sprintf(session->name, "%s", batostr(&dst));
++
++ session->sock = sock;
++ session->state = BT_CONFIG;
++
++ init_waitqueue_head(&session->wait);
++
++ session->ctrl = NULL;
++ session->msgnum = CMTP_INITIAL_MSGNUM;
++
++ INIT_LIST_HEAD(&session->applications);
++
++ skb_queue_head_init(&session->transmit);
++
++ for (i = 0; i < 16; i++)
++ session->reassembly[i] = NULL;
++
++ session->flags = req->flags;
++
++ __cmtp_link_session(session);
++
++ err = kernel_thread(cmtp_session, session, CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
++ if (err < 0)
++ goto unlink;
++
++ if (!(session->flags & (1 << CMTP_LOOPBACK))) {
++ err = cmtp_attach_device(session);
++ if (err < 0)
++ goto detach;
++ }
++
++ up_write(&cmtp_session_sem);
++ return 0;
++
++detach:
++ cmtp_detach_device(session);
++
++unlink:
++ __cmtp_unlink_session(session);
++
++failed:
++ up_write(&cmtp_session_sem);
++ kfree(session);
++ return err;
++}
++
++int cmtp_del_connection(struct cmtp_conndel_req *req)
++{
++ struct cmtp_session *session;
++ int err = 0;
++
++ BT_DBG("");
++
++ down_read(&cmtp_session_sem);
++
++ session = __cmtp_get_session(&req->bdaddr);
++ if (session) {
++ /* Flush the transmit queue */
++ skb_queue_purge(&session->transmit);
++
++ /* Kill session thread */
++ atomic_inc(&session->terminate);
++ cmtp_schedule(session);
++ } else
++ err = -ENOENT;
++
++ up_read(&cmtp_session_sem);
++ return err;
++}
++
++int cmtp_get_connlist(struct cmtp_connlist_req *req)
++{
++ struct list_head *p;
++ int err = 0, n = 0;
++
++ BT_DBG("");
++
++ down_read(&cmtp_session_sem);
++
++ list_for_each(p, &cmtp_session_list) {
++ struct cmtp_session *session;
++ struct cmtp_conninfo ci;
++
++ session = list_entry(p, struct cmtp_session, list);
++
++ __cmtp_copy_session(session, &ci);
++
++ if (copy_to_user(req->ci, &ci, sizeof(ci))) {
++ err = -EFAULT;
++ break;
++ }
++
++ if (++n >= req->cnum)
++ break;
++
++ req->ci++;
++ }
++ req->cnum = n;
++
++ up_read(&cmtp_session_sem);
++ return err;
++}
++
++int cmtp_get_conninfo(struct cmtp_conninfo *ci)
++{
++ struct cmtp_session *session;
++ int err = 0;
++
++ down_read(&cmtp_session_sem);
++
++ session = __cmtp_get_session(&ci->bdaddr);
++ if (session)
++ __cmtp_copy_session(session, ci);
++ else
++ err = -ENOENT;
++
++ up_read(&cmtp_session_sem);
++ return err;
++}
++
++
++int __init init_cmtp(void)
++{
++ l2cap_load();
++
++ cmtp_init_capi();
++ cmtp_init_sockets();
++
++ BT_INFO("BlueZ CMTP ver %s", VERSION);
++ BT_INFO("Copyright (C) 2002-2003 Marcel Holtmann <marcel@holtmann.org>");
++
++ return 0;
++}
++
++void __exit exit_cmtp(void)
++{
++ cmtp_cleanup_sockets();
++ cmtp_cleanup_capi();
++}
++
++module_init(init_cmtp);
++module_exit(exit_cmtp);
++
++MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
++MODULE_DESCRIPTION("BlueZ CMTP ver " VERSION);
++MODULE_LICENSE("GPL");
+diff -urN linux-2.4.18/net/bluetooth/cmtp/Makefile linux-2.4.18-mh15/net/bluetooth/cmtp/Makefile
+--- linux-2.4.18/net/bluetooth/cmtp/Makefile 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/net/bluetooth/cmtp/Makefile 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,10 @@
++#
++# Makefile for the Linux Bluetooth CMTP layer
++#
++
++O_TARGET := cmtp.o
++
++obj-y := core.o sock.o capi.o
++obj-m += $(O_TARGET)
++
++include $(TOPDIR)/Rules.make
+diff -urN linux-2.4.18/net/bluetooth/cmtp/sock.c linux-2.4.18-mh15/net/bluetooth/cmtp/sock.c
+--- linux-2.4.18/net/bluetooth/cmtp/sock.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/net/bluetooth/cmtp/sock.c 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,208 @@
++/*
++ CMTP implementation for Linux Bluetooth stack (BlueZ).
++ Copyright (C) 2002-2003 Marcel Holtmann <marcel@holtmann.org>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License version 2 as
++ published by the Free Software Foundation;
++
++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++
++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
++ SOFTWARE IS DISCLAIMED.
++*/
++
++#include <linux/config.h>
++#include <linux/module.h>
++
++#include <linux/types.h>
++#include <linux/errno.h>
++#include <linux/kernel.h>
++#include <linux/major.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/poll.h>
++#include <linux/fcntl.h>
++#include <linux/skbuff.h>
++#include <linux/socket.h>
++#include <linux/ioctl.h>
++#include <linux/file.h>
++#include <net/sock.h>
++
++#include <asm/system.h>
++#include <asm/uaccess.h>
++
++#include "cmtp.h"
++
++#ifndef CONFIG_BLUEZ_CMTP_DEBUG
++#undef BT_DBG
++#define BT_DBG(D...)
++#endif
++
++static int cmtp_sock_release(struct socket *sock)
++{
++ struct sock *sk = sock->sk;
++
++ BT_DBG("sock %p sk %p", sock, sk);
++
++ if (!sk)
++ return 0;
++
++ sock_orphan(sk);
++ sock_put(sk);
++
++ MOD_DEC_USE_COUNT;
++ return 0;
++}
++
++static int cmtp_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
++{
++ struct cmtp_connadd_req ca;
++ struct cmtp_conndel_req cd;
++ struct cmtp_connlist_req cl;
++ struct cmtp_conninfo ci;
++ struct socket *nsock;
++ int err;
++
++ BT_DBG("cmd %x arg %lx", cmd, arg);
++
++ switch (cmd) {
++ case CMTPCONNADD:
++ if (!capable(CAP_NET_ADMIN))
++ return -EACCES;
++
++ if (copy_from_user(&ca, (void *) arg, sizeof(ca)))
++ return -EFAULT;
++
++ nsock = sockfd_lookup(ca.sock, &err);
++ if (!nsock)
++ return err;
++
++ if (nsock->sk->state != BT_CONNECTED) {
++ fput(nsock->file);
++ return -EBADFD;
++ }
++
++ err = cmtp_add_connection(&ca, nsock);
++ if (!err) {
++ if (copy_to_user((void *) arg, &ca, sizeof(ca)))
++ err = -EFAULT;
++ } else
++ fput(nsock->file);
++
++ return err;
++
++ case CMTPCONNDEL:
++ if (!capable(CAP_NET_ADMIN))
++ return -EACCES;
++
++ if (copy_from_user(&cd, (void *) arg, sizeof(cd)))
++ return -EFAULT;
++
++ return cmtp_del_connection(&cd);
++
++ case CMTPGETCONNLIST:
++ if (copy_from_user(&cl, (void *) arg, sizeof(cl)))
++ return -EFAULT;
++
++ if (cl.cnum <= 0)
++ return -EINVAL;
++
++ err = cmtp_get_connlist(&cl);
++ if (!err && copy_to_user((void *) arg, &cl, sizeof(cl)))
++ return -EFAULT;
++
++ return err;
++
++ case CMTPGETCONNINFO:
++ if (copy_from_user(&ci, (void *) arg, sizeof(ci)))
++ return -EFAULT;
++
++ err = cmtp_get_conninfo(&ci);
++ if (!err && copy_to_user((void *) arg, &ci, sizeof(ci)))
++ return -EFAULT;
++
++ return err;
++ }
++
++ return -EINVAL;
++}
++
++static struct proto_ops cmtp_sock_ops = {
++ family: PF_BLUETOOTH,
++ release: cmtp_sock_release,
++ ioctl: cmtp_sock_ioctl,
++ bind: sock_no_bind,
++ getname: sock_no_getname,
++ sendmsg: sock_no_sendmsg,
++ recvmsg: sock_no_recvmsg,
++ poll: sock_no_poll,
++ listen: sock_no_listen,
++ shutdown: sock_no_shutdown,
++ setsockopt: sock_no_setsockopt,
++ getsockopt: sock_no_getsockopt,
++ connect: sock_no_connect,
++ socketpair: sock_no_socketpair,
++ accept: sock_no_accept,
++ mmap: sock_no_mmap
++};
++
++static int cmtp_sock_create(struct socket *sock, int protocol)
++{
++ struct sock *sk;
++
++ BT_DBG("sock %p", sock);
++
++ if (sock->type != SOCK_RAW)
++ return -ESOCKTNOSUPPORT;
++
++ sock->ops = &cmtp_sock_ops;
++
++ if (!(sk = sk_alloc(PF_BLUETOOTH, GFP_KERNEL, 1)))
++ return -ENOMEM;
++
++ MOD_INC_USE_COUNT;
++
++ sock->state = SS_UNCONNECTED;
++ sock_init_data(sock, sk);
++
++ sk->destruct = NULL;
++ sk->protocol = protocol;
++
++ return 0;
++}
++
++static struct net_proto_family cmtp_sock_family_ops = {
++ family: PF_BLUETOOTH,
++ create: cmtp_sock_create
++};
++
++int cmtp_init_sockets(void)
++{
++ int err;
++
++ if ((err = bluez_sock_register(BTPROTO_CMTP, &cmtp_sock_family_ops))) {
++ BT_ERR("Can't register CMTP socket layer (%d)", err);
++ return err;
++ }
++
++ return 0;
++}
++
++void cmtp_cleanup_sockets(void)
++{
++ int err;
++
++ if ((err = bluez_sock_unregister(BTPROTO_CMTP)))
++ BT_ERR("Can't unregister CMTP socket layer (%d)", err);
++
++ return;
++}
+diff -urN linux-2.4.18/net/bluetooth/Config.in linux-2.4.18-mh15/net/bluetooth/Config.in
+--- linux-2.4.18/net/bluetooth/Config.in 2001-06-12 04:15:27.000000000 +0200
++++ linux-2.4.18-mh15/net/bluetooth/Config.in 2004-08-01 16:26:23.000000000 +0200
+@@ -1,16 +1,23 @@
+ #
+-# Bluetooth configuration
++# Bluetooth subsystem configuration
+ #
+
+ if [ "$CONFIG_NET" != "n" ]; then
++
+ mainmenu_option next_comment
+ comment 'Bluetooth support'
+ dep_tristate 'Bluetooth subsystem support' CONFIG_BLUEZ $CONFIG_NET
+
+ if [ "$CONFIG_BLUEZ" != "n" ]; then
+ dep_tristate 'L2CAP protocol support' CONFIG_BLUEZ_L2CAP $CONFIG_BLUEZ
++ dep_tristate 'SCO links support' CONFIG_BLUEZ_SCO $CONFIG_BLUEZ
++ source net/bluetooth/rfcomm/Config.in
++ source net/bluetooth/bnep/Config.in
++ source net/bluetooth/cmtp/Config.in
++ source net/bluetooth/hidp/Config.in
+ source drivers/bluetooth/Config.in
+ fi
++
+ endmenu
+ fi
+
+diff -urN linux-2.4.18/net/bluetooth/hci_conn.c linux-2.4.18-mh15/net/bluetooth/hci_conn.c
+--- linux-2.4.18/net/bluetooth/hci_conn.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/net/bluetooth/hci_conn.c 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,435 @@
++/*
++ BlueZ - Bluetooth protocol stack for Linux
++ Copyright (C) 2000-2001 Qualcomm Incorporated
++
++ Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License version 2 as
++ published by the Free Software Foundation;
++
++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++
++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
++ SOFTWARE IS DISCLAIMED.
++*/
++
++/*
++ * HCI Connection handling.
++ *
++ * $Id$
++ */
++
++#include <linux/config.h>
++#include <linux/module.h>
++
++#include <linux/types.h>
++#include <linux/errno.h>
++#include <linux/kernel.h>
++#include <linux/major.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/poll.h>
++#include <linux/fcntl.h>
++#include <linux/init.h>
++#include <linux/skbuff.h>
++#include <linux/interrupt.h>
++#include <linux/notifier.h>
++#include <net/sock.h>
++
++#include <asm/system.h>
++#include <asm/uaccess.h>
++#include <asm/unaligned.h>
++
++#include <net/bluetooth/bluetooth.h>
++#include <net/bluetooth/hci_core.h>
++
++#ifndef HCI_CORE_DEBUG
++#undef BT_DBG
++#define BT_DBG( A... )
++#endif
++
++void hci_acl_connect(struct hci_conn *conn)
++{
++ struct hci_dev *hdev = conn->hdev;
++ struct inquiry_entry *ie;
++ create_conn_cp cp;
++
++ BT_DBG("%p", conn);
++
++ conn->state = BT_CONNECT;
++ conn->out = 1;
++ conn->link_mode = HCI_LM_MASTER;
++
++ memset(&cp, 0, sizeof(cp));
++ bacpy(&cp.bdaddr, &conn->dst);
++ cp.pscan_rep_mode = 0x02;
++
++ if ((ie = inquiry_cache_lookup(hdev, &conn->dst)) &&
++ inquiry_entry_age(ie) <= INQUIRY_ENTRY_AGE_MAX) {
++ cp.pscan_rep_mode = ie->info.pscan_rep_mode;
++ cp.pscan_mode = ie->info.pscan_mode;
++ cp.clock_offset = ie->info.clock_offset | __cpu_to_le16(0x8000);
++ }
++
++ cp.pkt_type = __cpu_to_le16(hdev->pkt_type & ACL_PTYPE_MASK);
++ if (lmp_rswitch_capable(hdev) && !(hdev->link_mode & HCI_LM_MASTER))
++ cp.role_switch = 0x01;
++ else
++ cp.role_switch = 0x00;
++
++ hci_send_cmd(hdev, OGF_LINK_CTL, OCF_CREATE_CONN,
++ CREATE_CONN_CP_SIZE, &cp);
++}
++
++void hci_acl_disconn(struct hci_conn *conn, __u8 reason)
++{
++ disconnect_cp cp;
++
++ BT_DBG("%p", conn);
++
++ conn->state = BT_DISCONN;
++
++ cp.handle = __cpu_to_le16(conn->handle);
++ cp.reason = reason;
++ hci_send_cmd(conn->hdev, OGF_LINK_CTL, OCF_DISCONNECT,
++ DISCONNECT_CP_SIZE, &cp);
++}
++
++void hci_add_sco(struct hci_conn *conn, __u16 handle)
++{
++ struct hci_dev *hdev = conn->hdev;
++ add_sco_cp cp;
++
++ BT_DBG("%p", conn);
++
++ conn->state = BT_CONNECT;
++ conn->out = 1;
++
++ cp.pkt_type = __cpu_to_le16(hdev->pkt_type & SCO_PTYPE_MASK);
++ cp.handle = __cpu_to_le16(handle);
++
++ hci_send_cmd(hdev, OGF_LINK_CTL, OCF_ADD_SCO, ADD_SCO_CP_SIZE, &cp);
++}
++
++static void hci_conn_timeout(unsigned long arg)
++{
++ struct hci_conn *conn = (void *)arg;
++ struct hci_dev *hdev = conn->hdev;
++
++ BT_DBG("conn %p state %d", conn, conn->state);
++
++ if (atomic_read(&conn->refcnt))
++ return;
++
++ hci_dev_lock(hdev);
++ if (conn->state == BT_CONNECTED)
++ hci_acl_disconn(conn, 0x13);
++ else
++ conn->state = BT_CLOSED;
++ hci_dev_unlock(hdev);
++ return;
++}
++
++static void hci_conn_init_timer(struct hci_conn *conn)
++{
++ init_timer(&conn->timer);
++ conn->timer.function = hci_conn_timeout;
++ conn->timer.data = (unsigned long)conn;
++}
++
++struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst)
++{
++ struct hci_conn *conn;
++
++ BT_DBG("%s dst %s", hdev->name, batostr(dst));
++
++ if (!(conn = kmalloc(sizeof(struct hci_conn), GFP_ATOMIC)))
++ return NULL;
++ memset(conn, 0, sizeof(struct hci_conn));
++
++ bacpy(&conn->dst, dst);
++ conn->type = type;
++ conn->hdev = hdev;
++ conn->state = BT_OPEN;
++
++ skb_queue_head_init(&conn->data_q);
++ hci_conn_init_timer(conn);
++
++ atomic_set(&conn->refcnt, 0);
++
++ hci_dev_hold(hdev);
++
++ tasklet_disable(&hdev->tx_task);
++ conn_hash_add(hdev, conn);
++ tasklet_enable(&hdev->tx_task);
++
++ return conn;
++}
++
++int hci_conn_del(struct hci_conn *conn)
++{
++ struct hci_dev *hdev = conn->hdev;
++
++ BT_DBG("%s conn %p handle %d", hdev->name, conn, conn->handle);
++
++ hci_conn_del_timer(conn);
++
++ if (conn->type == SCO_LINK) {
++ struct hci_conn *acl = conn->link;
++ if (acl) {
++ acl->link = NULL;
++ hci_conn_put(acl);
++ }
++ } else {
++ struct hci_conn *sco = conn->link;
++ if (sco)
++ sco->link = NULL;
++
++ /* Unacked frames */
++ hdev->acl_cnt += conn->sent;
++ }
++
++ tasklet_disable(&hdev->tx_task);
++ conn_hash_del(hdev, conn);
++ tasklet_enable(&hdev->tx_task);
++
++ skb_queue_purge(&conn->data_q);
++
++ hci_dev_put(hdev);
++
++ kfree(conn);
++ return 0;
++}
++
++struct hci_dev *hci_get_route(bdaddr_t *dst, bdaddr_t *src)
++{
++ int use_src = bacmp(src, BDADDR_ANY);
++ struct hci_dev *hdev = NULL;
++ struct list_head *p;
++
++ BT_DBG("%s -> %s", batostr(src), batostr(dst));
++
++ read_lock_bh(&hdev_list_lock);
++
++ list_for_each(p, &hdev_list) {
++ struct hci_dev *d;
++ d = list_entry(p, struct hci_dev, list);
++
++ if (!test_bit(HCI_UP, &d->flags))
++ continue;
++
++ /* Simple routing:
++ * No source address - find interface with bdaddr != dst
++ * Source address - find interface with bdaddr == src
++ */
++
++ if (use_src) {
++ if (!bacmp(&d->bdaddr, src)) {
++ hdev = d; break;
++ }
++ } else {
++ if (bacmp(&d->bdaddr, dst)) {
++ hdev = d; break;
++ }
++ }
++ }
++
++ if (hdev)
++ hci_dev_hold(hdev);
++
++ read_unlock_bh(&hdev_list_lock);
++ return hdev;
++}
++
++/* Create SCO or ACL connection.
++ * Device _must_ be locked */
++struct hci_conn * hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst)
++{
++ struct hci_conn *acl;
++
++ BT_DBG("%s dst %s", hdev->name, batostr(dst));
++
++ if (!(acl = conn_hash_lookup_ba(hdev, ACL_LINK, dst))) {
++ if (!(acl = hci_conn_add(hdev, ACL_LINK, dst)))
++ return NULL;
++ }
++
++ hci_conn_hold(acl);
++
++ if (acl->state == BT_OPEN || acl->state == BT_CLOSED)
++ hci_acl_connect(acl);
++
++ if (type == SCO_LINK) {
++ struct hci_conn *sco;
++
++ if (!(sco = conn_hash_lookup_ba(hdev, SCO_LINK, dst))) {
++ if (!(sco = hci_conn_add(hdev, SCO_LINK, dst))) {
++ hci_conn_put(acl);
++ return NULL;
++ }
++ }
++ acl->link = sco;
++ sco->link = acl;
++
++ hci_conn_hold(sco);
++
++ if (acl->state == BT_CONNECTED &&
++ (sco->state == BT_OPEN || sco->state == BT_CLOSED))
++ hci_add_sco(sco, acl->handle);
++
++ return sco;
++ } else {
++ return acl;
++ }
++}
++
++/* Authenticate remote device */
++int hci_conn_auth(struct hci_conn *conn)
++{
++ BT_DBG("conn %p", conn);
++
++ if (conn->link_mode & HCI_LM_AUTH)
++ return 1;
++
++ if (!test_and_set_bit(HCI_CONN_AUTH_PEND, &conn->pend)) {
++ auth_requested_cp ar;
++ ar.handle = __cpu_to_le16(conn->handle);
++ hci_send_cmd(conn->hdev, OGF_LINK_CTL, OCF_AUTH_REQUESTED,
++ AUTH_REQUESTED_CP_SIZE, &ar);
++ }
++ return 0;
++}
++
++/* Enable encryption */
++int hci_conn_encrypt(struct hci_conn *conn)
++{
++ BT_DBG("conn %p", conn);
++
++ if (conn->link_mode & HCI_LM_ENCRYPT)
++ return 1;
++
++ if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend))
++ return 0;
++
++ if (hci_conn_auth(conn)) {
++ set_conn_encrypt_cp ce;
++ ce.handle = __cpu_to_le16(conn->handle);
++ ce.encrypt = 1;
++ hci_send_cmd(conn->hdev, OGF_LINK_CTL, OCF_SET_CONN_ENCRYPT,
++ SET_CONN_ENCRYPT_CP_SIZE, &ce);
++ }
++ return 0;
++}
++
++/* Drop all connection on the device */
++void hci_conn_hash_flush(struct hci_dev *hdev)
++{
++ struct conn_hash *h = &hdev->conn_hash;
++ struct list_head *p;
++
++ BT_DBG("hdev %s", hdev->name);
++
++ p = h->list.next;
++ while (p != &h->list) {
++ struct hci_conn *c;
++
++ c = list_entry(p, struct hci_conn, list);
++ p = p->next;
++
++ c->state = BT_CLOSED;
++
++ hci_proto_disconn_ind(c, 0x16);
++ hci_conn_del(c);
++ }
++}
++
++int hci_get_conn_list(unsigned long arg)
++{
++ struct hci_conn_list_req req, *cl;
++ struct hci_conn_info *ci;
++ struct hci_dev *hdev;
++ struct list_head *p;
++ int n = 0, size, err;
++
++ if (copy_from_user(&req, (void *) arg, sizeof(req)))
++ return -EFAULT;
++
++ if (!req.conn_num || req.conn_num > (PAGE_SIZE * 2) / sizeof(*ci))
++ return -EINVAL;
++
++ size = sizeof(req) + req.conn_num * sizeof(*ci);
++
++ if (!(cl = (void *) kmalloc(size, GFP_KERNEL)))
++ return -ENOMEM;
++
++ if (!(hdev = hci_dev_get(req.dev_id))) {
++ kfree(cl);
++ return -ENODEV;
++ }
++
++ ci = cl->conn_info;
++
++ hci_dev_lock_bh(hdev);
++ list_for_each(p, &hdev->conn_hash.list) {
++ register struct hci_conn *c;
++ c = list_entry(p, struct hci_conn, list);
++
++ bacpy(&(ci + n)->bdaddr, &c->dst);
++ (ci + n)->handle = c->handle;
++ (ci + n)->type = c->type;
++ (ci + n)->out = c->out;
++ (ci + n)->state = c->state;
++ (ci + n)->link_mode = c->link_mode;
++ if (++n >= req.conn_num)
++ break;
++ }
++ hci_dev_unlock_bh(hdev);
++
++ cl->dev_id = hdev->id;
++ cl->conn_num = n;
++ size = sizeof(req) + n * sizeof(*ci);
++
++ hci_dev_put(hdev);
++
++ err = copy_to_user((void *) arg, cl, size);
++ kfree(cl);
++
++ return err ? -EFAULT : 0;
++}
++
++int hci_get_conn_info(struct hci_dev *hdev, unsigned long arg)
++{
++ struct hci_conn_info_req req;
++ struct hci_conn_info ci;
++ struct hci_conn *conn;
++ char *ptr = (void *) arg + sizeof(req);
++
++ if (copy_from_user(&req, (void *) arg, sizeof(req)))
++ return -EFAULT;
++
++ hci_dev_lock_bh(hdev);
++ conn = conn_hash_lookup_ba(hdev, req.type, &req.bdaddr);
++ if (conn) {
++ bacpy(&ci.bdaddr, &conn->dst);
++ ci.handle = conn->handle;
++ ci.type = conn->type;
++ ci.out = conn->out;
++ ci.state = conn->state;
++ ci.link_mode = conn->link_mode;
++ }
++ hci_dev_unlock_bh(hdev);
++
++ if (!conn)
++ return -ENOENT;
++
++ return copy_to_user(ptr, &ci, sizeof(ci)) ? -EFAULT : 0;
++}
+diff -urN linux-2.4.18/net/bluetooth/hci_core.c linux-2.4.18-mh15/net/bluetooth/hci_core.c
+--- linux-2.4.18/net/bluetooth/hci_core.c 2001-11-09 23:21:21.000000000 +0100
++++ linux-2.4.18-mh15/net/bluetooth/hci_core.c 2004-08-01 16:26:23.000000000 +0200
+@@ -25,11 +25,12 @@
+ /*
+ * BlueZ HCI Core.
+ *
+- * $Id$
++ * $Id$
+ */
+
+ #include <linux/config.h>
+ #include <linux/module.h>
++#include <linux/kmod.h>
+
+ #include <linux/types.h>
+ #include <linux/errno.h>
+@@ -50,12 +51,11 @@
+ #include <asm/unaligned.h>
+
+ #include <net/bluetooth/bluetooth.h>
+-#include <net/bluetooth/bluez.h>
+ #include <net/bluetooth/hci_core.h>
+
+ #ifndef HCI_CORE_DEBUG
+-#undef DBG
+-#define DBG( A... )
++#undef BT_DBG
++#define BT_DBG( A... )
+ #endif
+
+ static void hci_cmd_task(unsigned long arg);
+@@ -63,279 +63,69 @@
+ static void hci_tx_task(unsigned long arg);
+ static void hci_notify(struct hci_dev *hdev, int event);
+
+-static rwlock_t hci_task_lock = RW_LOCK_UNLOCKED;
++rwlock_t hci_task_lock = RW_LOCK_UNLOCKED;
+
+ /* HCI device list */
+-struct hci_dev *hdev_list[HCI_MAX_DEV];
+-spinlock_t hdev_list_lock;
+-#define GET_HDEV(a) (hdev_list[a])
+-
+-/* HCI protocol list */
+-struct hci_proto *hproto_list[HCI_MAX_PROTO];
+-#define GET_HPROTO(a) (hproto_list[a])
++LIST_HEAD(hdev_list);
++rwlock_t hdev_list_lock = RW_LOCK_UNLOCKED;
+
+-/* HCI notifiers list */
+-struct notifier_block *hci_dev_notifier;
+-
+-/* HCI device notifications */
+-int hci_register_notifier(struct notifier_block *nb)
+-{
+- int err, i;
+- struct hci_dev *hdev;
+-
+- if ((err = notifier_chain_register(&hci_dev_notifier, nb)))
+- return err;
+-
+- /* Notify about already registered devices */
+- spin_lock(&hdev_list_lock);
+- for (i = 0; i < HCI_MAX_DEV; i++) {
+- if (!(hdev = GET_HDEV(i)))
+- continue;
+- if (hdev->flags & HCI_UP)
+- (*nb->notifier_call)(nb, HCI_DEV_UP, hdev);
+- }
+- spin_unlock(&hdev_list_lock);
+-
+- return 0;
+-}
+-
+-int hci_unregister_notifier(struct notifier_block *nb)
+-{
+- return notifier_chain_unregister(&hci_dev_notifier, nb);
+-}
+-
+-static inline void hci_notify(struct hci_dev *hdev, int event)
+-{
+- notifier_call_chain(&hci_dev_notifier, event, hdev);
+-}
+-
+-/* Get HCI device by index (device is locked on return)*/
+-struct hci_dev *hci_dev_get(int index)
+-{
+- struct hci_dev *hdev;
+- DBG("%d", index);
+-
+- if (index < 0 || index >= HCI_MAX_DEV)
+- return NULL;
+-
+- spin_lock(&hdev_list_lock);
+- if ((hdev = GET_HDEV(index)))
+- hci_dev_hold(hdev);
+- spin_unlock(&hdev_list_lock);
+-
+- return hdev;
+-}
+-
+-/* Flush inquiry cache */
+-void inquiry_cache_flush(struct inquiry_cache *cache)
+-{
+- struct inquiry_entry *next = cache->list, *e;
+-
+- DBG("cache %p", cache);
+-
+- cache->list = NULL;
+- while ((e = next)) {
+- next = e->next;
+- kfree(e);
+- }
+-}
+-
+-/* Lookup by bdaddr.
+- * Cache must be locked. */
+-static struct inquiry_entry * __inquiry_cache_lookup(struct inquiry_cache *cache, bdaddr_t *bdaddr)
+-{
+- struct inquiry_entry *e;
+-
+- DBG("cache %p, %s", cache, batostr(bdaddr));
+-
+- for (e = cache->list; e; e = e->next)
+- if (!bacmp(&e->info.bdaddr, bdaddr))
+- break;
+-
+- return e;
+-}
+-
+-static void inquiry_cache_update(struct inquiry_cache *cache, inquiry_info *info)
+-{
+- struct inquiry_entry *e;
+-
+- DBG("cache %p, %s", cache, batostr(&info->bdaddr));
+-
+- inquiry_cache_lock(cache);
+-
+- if (!(e = __inquiry_cache_lookup(cache, &info->bdaddr))) {
+- /* Entry not in the cache. Add new one. */
+- if (!(e = kmalloc(sizeof(struct inquiry_entry), GFP_ATOMIC)))
+- goto unlock;
+- memset(e, 0, sizeof(struct inquiry_entry));
+- e->next = cache->list;
+- cache->list = e;
+- }
+-
+- memcpy(&e->info, info, sizeof(inquiry_info));
+- e->timestamp = jiffies;
+- cache->timestamp = jiffies;
+-unlock:
+- inquiry_cache_unlock(cache);
+-}
+-
+-static int inquiry_cache_dump(struct inquiry_cache *cache, int num, __u8 *buf)
+-{
+- inquiry_info *info = (inquiry_info *) buf;
+- struct inquiry_entry *e;
+- int copied = 0;
++/* HCI protocols */
++#define HCI_MAX_PROTO 2
++struct hci_proto *hci_proto[HCI_MAX_PROTO];
+
+- inquiry_cache_lock(cache);
+-
+- for (e = cache->list; e && copied < num; e = e->next, copied++)
+- memcpy(info++, &e->info, sizeof(inquiry_info));
++/* HCI notifiers list */
++static struct notifier_block *hci_notifier;
+
+- inquiry_cache_unlock(cache);
+
+- DBG("cache %p, copied %d", cache, copied);
+- return copied;
+-}
++/* ---- HCI notifications ---- */
+
+-/* --------- BaseBand connections --------- */
+-static struct hci_conn *hci_conn_add(struct hci_dev *hdev, __u16 handle, __u8 type, bdaddr_t *dst)
++int hci_register_notifier(struct notifier_block *nb)
+ {
+- struct hci_conn *conn;
+-
+- DBG("%s handle %d dst %s", hdev->name, handle, batostr(dst));
+-
+- if ( conn_hash_lookup(&hdev->conn_hash, handle)) {
+- ERR("%s handle 0x%x already exists", hdev->name, handle);
+- return NULL;
+- }
+-
+- if (!(conn = kmalloc(sizeof(struct hci_conn), GFP_ATOMIC)))
+- return NULL;
+- memset(conn, 0, sizeof(struct hci_conn));
+-
+- bacpy(&conn->dst, dst);
+- conn->handle = handle;
+- conn->type = type;
+- conn->hdev = hdev;
+-
+- skb_queue_head_init(&conn->data_q);
+-
+- hci_dev_hold(hdev);
+- conn_hash_add(&hdev->conn_hash, handle, conn);
+-
+- return conn;
++ return notifier_chain_register(&hci_notifier, nb);
+ }
+
+-static int hci_conn_del(struct hci_dev *hdev, struct hci_conn *conn)
++int hci_unregister_notifier(struct notifier_block *nb)
+ {
+- DBG("%s conn %p handle %d", hdev->name, conn, conn->handle);
+-
+- conn_hash_del(&hdev->conn_hash, conn);
+- hci_dev_put(hdev);
+-
+- /* Unacked frames */
+- hdev->acl_cnt += conn->sent;
+-
+- skb_queue_purge(&conn->data_q);
+-
+- kfree(conn);
+- return 0;
++ return notifier_chain_unregister(&hci_notifier, nb);
+ }
+
+-/* Drop all connection on the device */
+-static void hci_conn_hash_flush(struct hci_dev *hdev)
++void hci_notify(struct hci_dev *hdev, int event)
+ {
+- struct conn_hash *h = &hdev->conn_hash;
+- struct hci_proto *hp;
+- struct list_head *p;
+-
+- DBG("hdev %s", hdev->name);
+-
+- p = h->list.next;
+- while (p != &h->list) {
+- struct hci_conn *c;
+-
+- c = list_entry(p, struct hci_conn, list);
+- p = p->next;
+-
+- if (c->type == ACL_LINK) {
+- /* ACL link notify L2CAP layer */
+- if ((hp = GET_HPROTO(HCI_PROTO_L2CAP)) && hp->disconn_ind)
+- hp->disconn_ind(c, 0x16);
+- } else {
+- /* SCO link (no notification) */
+- }
+-
+- hci_conn_del(hdev, c);
+- }
++ notifier_call_chain(&hci_notifier, event, hdev);
+ }
+
+-int hci_connect(struct hci_dev *hdev, bdaddr_t *bdaddr)
+-{
+- struct inquiry_cache *cache = &hdev->inq_cache;
+- struct inquiry_entry *e;
+- create_conn_cp cc;
+- __u16 clock_offset;
+-
+- DBG("%s bdaddr %s", hdev->name, batostr(bdaddr));
+-
+- if (!(hdev->flags & HCI_UP))
+- return -ENODEV;
+-
+- inquiry_cache_lock_bh(cache);
+-
+- if (!(e = __inquiry_cache_lookup(cache, bdaddr)) || inquiry_entry_age(e) > INQUIRY_ENTRY_AGE_MAX) {
+- cc.pscan_rep_mode = 0;
+- cc.pscan_mode = 0;
+- clock_offset = 0;
+- } else {
+- cc.pscan_rep_mode = e->info.pscan_rep_mode;
+- cc.pscan_mode = e->info.pscan_mode;
+- clock_offset = __le16_to_cpu(e->info.clock_offset) & 0x8000;
+- }
+-
+- inquiry_cache_unlock_bh(cache);
+-
+- bacpy(&cc.bdaddr, bdaddr);
+- cc.pkt_type = __cpu_to_le16(hdev->pkt_type);
+- cc.clock_offset = __cpu_to_le16(clock_offset);
+-
+- if (lmp_rswitch_capable(hdev))
+- cc.role_switch = 0x01;
+- else
+- cc.role_switch = 0x00;
+-
+- hci_send_cmd(hdev, OGF_LINK_CTL, OCF_CREATE_CONN, CREATE_CONN_CP_SIZE, &cc);
++/* ---- HCI hotplug support ---- */
+
+- return 0;
+-}
++#ifdef CONFIG_HOTPLUG
+
+-int hci_disconnect(struct hci_conn *conn, __u8 reason)
++static int hci_run_hotplug(char *dev, char *action)
+ {
+- disconnect_cp dc;
+-
+- DBG("conn %p handle %d", conn, conn->handle);
++ char *argv[3], *envp[5], dstr[20], astr[32];
+
+- dc.handle = __cpu_to_le16(conn->handle);
+- dc.reason = reason;
+- hci_send_cmd(conn->hdev, OGF_LINK_CTL, OCF_DISCONNECT, DISCONNECT_CP_SIZE, &dc);
++ sprintf(dstr, "DEVICE=%s", dev);
++ sprintf(astr, "ACTION=%s", action);
+
+- return 0;
+-}
++ argv[0] = hotplug_path;
++ argv[1] = "bluetooth";
++ argv[2] = NULL;
+
+-/* --------- HCI request handling ------------ */
+-static inline void hci_req_lock(struct hci_dev *hdev)
+-{
+- down(&hdev->req_lock);
++ envp[0] = "HOME=/";
++ envp[1] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
++ envp[2] = dstr;
++ envp[3] = astr;
++ envp[4] = NULL;
++
++ return call_usermodehelper(argv[0], argv, envp);
+ }
++#else
++#define hci_run_hotplug(A...)
++#endif
+
+-static inline void hci_req_unlock(struct hci_dev *hdev)
+-{
+- up(&hdev->req_lock);
+-}
++/* ---- HCI requests ---- */
+
+-static inline void hci_req_complete(struct hci_dev *hdev, int result)
++void hci_req_complete(struct hci_dev *hdev, int result)
+ {
+- DBG("%s result 0x%2.2x", hdev->name, result);
++ BT_DBG("%s result 0x%2.2x", hdev->name, result);
+
+ if (hdev->req_status == HCI_REQ_PEND) {
+ hdev->req_result = result;
+@@ -344,9 +134,9 @@
+ }
+ }
+
+-static inline void hci_req_cancel(struct hci_dev *hdev, int err)
++void hci_req_cancel(struct hci_dev *hdev, int err)
+ {
+- DBG("%s err 0x%2.2x", hdev->name, err);
++ BT_DBG("%s err 0x%2.2x", hdev->name, err);
+
+ if (hdev->req_status == HCI_REQ_PEND) {
+ hdev->req_result = err;
+@@ -356,23 +146,22 @@
+ }
+
+ /* Execute request and wait for completion. */
+-static int __hci_request(struct hci_dev *hdev, void (*req)(struct hci_dev *hdev, unsigned long opt),
+- unsigned long opt, __u32 timeout)
++static int __hci_request(struct hci_dev *hdev, void (*req)(struct hci_dev *hdev, unsigned long opt), unsigned long opt, __u32 timeout)
+ {
+ DECLARE_WAITQUEUE(wait, current);
+ int err = 0;
+
+- DBG("%s start", hdev->name);
++ BT_DBG("%s start", hdev->name);
+
+ hdev->req_status = HCI_REQ_PEND;
+
+ add_wait_queue(&hdev->req_wait_q, &wait);
+- current->state = TASK_INTERRUPTIBLE;
++ set_current_state(TASK_INTERRUPTIBLE);
+
+ req(hdev, opt);
+ schedule_timeout(timeout);
+
+- current->state = TASK_RUNNING;
++ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&hdev->req_wait_q, &wait);
+
+ if (signal_pending(current))
+@@ -394,7 +183,7 @@
+
+ hdev->req_status = hdev->req_result = 0;
+
+- DBG("%s end: err %d", hdev->name, err);
++ BT_DBG("%s end: err %d", hdev->name, err);
+
+ return err;
+ }
+@@ -412,10 +201,9 @@
+ return ret;
+ }
+
+-/* --------- HCI requests ---------- */
+ static void hci_reset_req(struct hci_dev *hdev, unsigned long opt)
+ {
+- DBG("%s %ld", hdev->name, opt);
++ BT_DBG("%s %ld", hdev->name, opt);
+
+ /* Reset device */
+ hci_send_cmd(hdev, OGF_HOST_CTL, OCF_RESET, 0, NULL);
+@@ -423,27 +211,44 @@
+
+ static void hci_init_req(struct hci_dev *hdev, unsigned long opt)
+ {
+- set_event_flt_cp ec;
++ set_event_flt_cp ef;
+ __u16 param;
+
+- DBG("%s %ld", hdev->name, opt);
++ BT_DBG("%s %ld", hdev->name, opt);
+
+ /* Mandatory initialization */
+
++ /* Reset */
++ if (test_bit(HCI_QUIRK_RESET_ON_INIT, &hdev->quirks))
++ hci_send_cmd(hdev, OGF_HOST_CTL, OCF_RESET, 0, NULL);
++
+ /* Read Local Supported Features */
+ hci_send_cmd(hdev, OGF_INFO_PARAM, OCF_READ_LOCAL_FEATURES, 0, NULL);
+
+ /* Read Buffer Size (ACL mtu, max pkt, etc.) */
+ hci_send_cmd(hdev, OGF_INFO_PARAM, OCF_READ_BUFFER_SIZE, 0, NULL);
+
++#if 0
++ /* Host buffer size */
++ {
++ host_buffer_size_cp bs;
++ bs.acl_mtu = __cpu_to_le16(HCI_MAX_ACL_SIZE);
++ bs.sco_mtu = HCI_MAX_SCO_SIZE;
++ bs.acl_max_pkt = __cpu_to_le16(0xffff);
++ bs.sco_max_pkt = __cpu_to_le16(0xffff);
++ hci_send_cmd(hdev, OGF_HOST_CTL, OCF_HOST_BUFFER_SIZE,
++ HOST_BUFFER_SIZE_CP_SIZE, &bs);
++ }
++#endif
++
+ /* Read BD Address */
+ hci_send_cmd(hdev, OGF_INFO_PARAM, OCF_READ_BD_ADDR, 0, NULL);
+
+ /* Optional initialization */
+
+ /* Clear Event Filters */
+- ec.flt_type = FLT_CLEAR_ALL;
+- hci_send_cmd(hdev, OGF_HOST_CTL, OCF_SET_EVENT_FLT, 1, &ec);
++ ef.flt_type = FLT_CLEAR_ALL;
++ hci_send_cmd(hdev, OGF_HOST_CTL, OCF_SET_EVENT_FLT, 1, &ef);
+
+ /* Page timeout ~20 secs */
+ param = __cpu_to_le16(0x8000);
+@@ -458,7 +263,7 @@
+ {
+ __u8 scan = opt;
+
+- DBG("%s %x", hdev->name, scan);
++ BT_DBG("%s %x", hdev->name, scan);
+
+ /* Inquiry and Page scans */
+ hci_send_cmd(hdev, OGF_HOST_CTL, OCF_WRITE_SCAN_ENABLE, 1, &scan);
+@@ -468,116 +273,273 @@
+ {
+ __u8 auth = opt;
+
+- DBG("%s %x", hdev->name, auth);
++ BT_DBG("%s %x", hdev->name, auth);
+
+ /* Authentication */
+ hci_send_cmd(hdev, OGF_HOST_CTL, OCF_WRITE_AUTH_ENABLE, 1, &auth);
+ }
+
+-static void hci_inq_req(struct hci_dev *hdev, unsigned long opt)
++static void hci_encrypt_req(struct hci_dev *hdev, unsigned long opt)
+ {
+- struct hci_inquiry_req *ir = (struct hci_inquiry_req *) opt;
+- inquiry_cp ic;
++ __u8 encrypt = opt;
+
+- DBG("%s", hdev->name);
++ BT_DBG("%s %x", hdev->name, encrypt);
+
+- /* Start Inquiry */
+- memcpy(&ic.lap, &ir->lap, 3);
+- ic.lenght = ir->length;
+- ic.num_rsp = ir->num_rsp;
+- hci_send_cmd(hdev, OGF_LINK_CTL, OCF_INQUIRY, INQUIRY_CP_SIZE, &ic);
++ /* Authentication */
++ hci_send_cmd(hdev, OGF_HOST_CTL, OCF_WRITE_ENCRYPT_MODE, 1, &encrypt);
+ }
+
+-/* HCI ioctl helpers */
+-int hci_dev_open(__u16 dev)
++/* Get HCI device by index.
++ * Device is locked on return. */
++struct hci_dev *hci_dev_get(int index)
+ {
+ struct hci_dev *hdev;
+- int ret = 0;
+-
+- if (!(hdev = hci_dev_get(dev)))
+- return -ENODEV;
++ struct list_head *p;
+
+- DBG("%s %p", hdev->name, hdev);
++ BT_DBG("%d", index);
+
+- hci_req_lock(hdev);
++ if (index < 0)
++ return NULL;
+
+- if (hdev->flags & HCI_UP) {
+- ret = -EALREADY;
+- goto done;
++ read_lock(&hdev_list_lock);
++ list_for_each(p, &hdev_list) {
++ hdev = list_entry(p, struct hci_dev, list);
++ if (hdev->id == index) {
++ hci_dev_hold(hdev);
++ goto done;
++ }
+ }
++ hdev = NULL;
++done:
++ read_unlock(&hdev_list_lock);
++ return hdev;
++}
+
+- if (hdev->open(hdev)) {
+- ret = -EIO;
+- goto done;
+- }
++/* ---- Inquiry support ---- */
++void inquiry_cache_flush(struct hci_dev *hdev)
++{
++ struct inquiry_cache *cache = &hdev->inq_cache;
++ struct inquiry_entry *next = cache->list, *e;
+
+- if (hdev->flags & HCI_NORMAL) {
+- atomic_set(&hdev->cmd_cnt, 1);
+- hdev->flags |= HCI_INIT;
++ BT_DBG("cache %p", cache);
+
+- //__hci_request(hdev, hci_reset_req, 0, HZ);
+- ret = __hci_request(hdev, hci_init_req, 0, HCI_INIT_TIMEOUT);
+-
+- hdev->flags &= ~HCI_INIT;
++ cache->list = NULL;
++ while ((e = next)) {
++ next = e->next;
++ kfree(e);
+ }
++}
+
+- if (!ret) {
+- hdev->flags |= HCI_UP;
+- hci_notify(hdev, HCI_DEV_UP);
+- } else {
+- /* Init failed, cleanup */
+- tasklet_kill(&hdev->rx_task);
+- tasklet_kill(&hdev->tx_task);
+- tasklet_kill(&hdev->cmd_task);
++struct inquiry_entry *inquiry_cache_lookup(struct hci_dev *hdev, bdaddr_t *bdaddr)
++{
++ struct inquiry_cache *cache = &hdev->inq_cache;
++ struct inquiry_entry *e;
+
+- skb_queue_purge(&hdev->cmd_q);
+- skb_queue_purge(&hdev->rx_q);
++ BT_DBG("cache %p, %s", cache, batostr(bdaddr));
+
+- if (hdev->flush)
+- hdev->flush(hdev);
++ for (e = cache->list; e; e = e->next)
++ if (!bacmp(&e->info.bdaddr, bdaddr))
++ break;
++ return e;
++}
+
+- if (hdev->sent_cmd) {
+- kfree_skb(hdev->sent_cmd);
+- hdev->sent_cmd = NULL;
+- }
++void inquiry_cache_update(struct hci_dev *hdev, inquiry_info *info)
++{
++ struct inquiry_cache *cache = &hdev->inq_cache;
++ struct inquiry_entry *e;
+
+- hdev->close(hdev);
+- }
++ BT_DBG("cache %p, %s", cache, batostr(&info->bdaddr));
+
+-done:
+- hci_req_unlock(hdev);
+- hci_dev_put(hdev);
++ if (!(e = inquiry_cache_lookup(hdev, &info->bdaddr))) {
++ /* Entry not in the cache. Add new one. */
++ if (!(e = kmalloc(sizeof(struct inquiry_entry), GFP_ATOMIC)))
++ return;
++ memset(e, 0, sizeof(struct inquiry_entry));
++ e->next = cache->list;
++ cache->list = e;
++ }
+
+- return ret;
++ memcpy(&e->info, info, sizeof(inquiry_info));
++ e->timestamp = jiffies;
++ cache->timestamp = jiffies;
+ }
+
+-int hci_dev_close(__u16 dev)
++int inquiry_cache_dump(struct hci_dev *hdev, int num, __u8 *buf)
+ {
+- struct hci_dev *hdev;
+-
+- if (!(hdev = hci_dev_get(dev)))
+- return -ENODEV;
++ struct inquiry_cache *cache = &hdev->inq_cache;
++ inquiry_info *info = (inquiry_info *) buf;
++ struct inquiry_entry *e;
++ int copied = 0;
+
+- DBG("%s %p", hdev->name, hdev);
++ for (e = cache->list; e && copied < num; e = e->next, copied++)
++ memcpy(info++, &e->info, sizeof(inquiry_info));
+
+- hci_req_cancel(hdev, ENODEV);
+- hci_req_lock(hdev);
++ BT_DBG("cache %p, copied %d", cache, copied);
++ return copied;
++}
+
+- if (!(hdev->flags & HCI_UP))
+- goto done;
++static void hci_inq_req(struct hci_dev *hdev, unsigned long opt)
++{
++ struct hci_inquiry_req *ir = (struct hci_inquiry_req *) opt;
++ inquiry_cp ic;
+
+- /* Kill RX and TX tasks */
+- tasklet_kill(&hdev->rx_task);
+- tasklet_kill(&hdev->tx_task);
++ BT_DBG("%s", hdev->name);
+
+- inquiry_cache_flush(&hdev->inq_cache);
++ if (test_bit(HCI_INQUIRY, &hdev->flags))
++ return;
+
+- hci_conn_hash_flush(hdev);
++ /* Start Inquiry */
++ memcpy(&ic.lap, &ir->lap, 3);
++ ic.length = ir->length;
++ ic.num_rsp = ir->num_rsp;
++ hci_send_cmd(hdev, OGF_LINK_CTL, OCF_INQUIRY, INQUIRY_CP_SIZE, &ic);
++}
+
+- /* Clear flags */
+- hdev->flags &= HCI_SOCK;
+- hdev->flags |= HCI_NORMAL;
++int hci_inquiry(unsigned long arg)
++{
++ struct hci_inquiry_req ir;
++ struct hci_dev *hdev;
++ int err = 0, do_inquiry = 0, max_rsp;
++ long timeo;
++ __u8 *buf, *ptr;
+
++ ptr = (void *) arg;
++ if (copy_from_user(&ir, ptr, sizeof(ir)))
++ return -EFAULT;
++
++ if (!(hdev = hci_dev_get(ir.dev_id)))
++ return -ENODEV;
++
++ hci_dev_lock_bh(hdev);
++ if (inquiry_cache_age(hdev) > INQUIRY_CACHE_AGE_MAX ||
++ inquiry_cache_empty(hdev) ||
++ ir.flags & IREQ_CACHE_FLUSH) {
++ inquiry_cache_flush(hdev);
++ do_inquiry = 1;
++ }
++ hci_dev_unlock_bh(hdev);
++
++ timeo = ir.length * 2 * HZ;
++ if (do_inquiry && (err = hci_request(hdev, hci_inq_req, (unsigned long)&ir, timeo)) < 0)
++ goto done;
++
++ /* for unlimited number of responses we will use buffer with 255 entries */
++ max_rsp = (ir.num_rsp == 0) ? 255 : ir.num_rsp;
++
++ /* cache_dump can't sleep. Therefore we allocate temp buffer and then
++ * copy it to the user space.
++ */
++ if (!(buf = kmalloc(sizeof(inquiry_info) * max_rsp, GFP_KERNEL))) {
++ err = -ENOMEM;
++ goto done;
++ }
++
++ hci_dev_lock_bh(hdev);
++ ir.num_rsp = inquiry_cache_dump(hdev, max_rsp, buf);
++ hci_dev_unlock_bh(hdev);
++
++ BT_DBG("num_rsp %d", ir.num_rsp);
++
++ if (!verify_area(VERIFY_WRITE, ptr, sizeof(ir) +
++ (sizeof(inquiry_info) * ir.num_rsp))) {
++ copy_to_user(ptr, &ir, sizeof(ir));
++ ptr += sizeof(ir);
++ copy_to_user(ptr, buf, sizeof(inquiry_info) * ir.num_rsp);
++ } else
++ err = -EFAULT;
++
++ kfree(buf);
++
++done:
++ hci_dev_put(hdev);
++ return err;
++}
++
++/* ---- HCI ioctl helpers ---- */
++
++int hci_dev_open(__u16 dev)
++{
++ struct hci_dev *hdev;
++ int ret = 0;
++
++ if (!(hdev = hci_dev_get(dev)))
++ return -ENODEV;
++
++ BT_DBG("%s %p", hdev->name, hdev);
++
++ hci_req_lock(hdev);
++
++ if (test_bit(HCI_UP, &hdev->flags)) {
++ ret = -EALREADY;
++ goto done;
++ }
++
++ if (hdev->open(hdev)) {
++ ret = -EIO;
++ goto done;
++ }
++
++ if (!test_bit(HCI_RAW, &hdev->flags)) {
++ atomic_set(&hdev->cmd_cnt, 1);
++ set_bit(HCI_INIT, &hdev->flags);
++
++ //__hci_request(hdev, hci_reset_req, 0, HZ);
++ ret = __hci_request(hdev, hci_init_req, 0, HCI_INIT_TIMEOUT);
++
++ clear_bit(HCI_INIT, &hdev->flags);
++ }
++
++ if (!ret) {
++ set_bit(HCI_UP, &hdev->flags);
++ hci_notify(hdev, HCI_DEV_UP);
++ } else {
++ /* Init failed, cleanup */
++ tasklet_kill(&hdev->rx_task);
++ tasklet_kill(&hdev->tx_task);
++ tasklet_kill(&hdev->cmd_task);
++
++ skb_queue_purge(&hdev->cmd_q);
++ skb_queue_purge(&hdev->rx_q);
++
++ if (hdev->flush)
++ hdev->flush(hdev);
++
++ if (hdev->sent_cmd) {
++ kfree_skb(hdev->sent_cmd);
++ hdev->sent_cmd = NULL;
++ }
++
++ hdev->close(hdev);
++ hdev->flags = 0;
++ }
++
++done:
++ hci_req_unlock(hdev);
++ hci_dev_put(hdev);
++ return ret;
++}
++
++static int hci_dev_do_close(struct hci_dev *hdev)
++{
++ BT_DBG("%s %p", hdev->name, hdev);
++
++ hci_req_cancel(hdev, ENODEV);
++ hci_req_lock(hdev);
++
++ if (!test_and_clear_bit(HCI_UP, &hdev->flags)) {
++ hci_req_unlock(hdev);
++ return 0;
++ }
++
++ /* Kill RX and TX tasks */
++ tasklet_kill(&hdev->rx_task);
++ tasklet_kill(&hdev->tx_task);
++
++ hci_dev_lock_bh(hdev);
++ inquiry_cache_flush(hdev);
++ hci_conn_hash_flush(hdev);
++ hci_dev_unlock_bh(hdev);
++
+ hci_notify(hdev, HCI_DEV_DOWN);
+
+ if (hdev->flush)
+@@ -586,9 +548,9 @@
+ /* Reset device */
+ skb_queue_purge(&hdev->cmd_q);
+ atomic_set(&hdev->cmd_cnt, 1);
+- hdev->flags |= HCI_INIT;
+- __hci_request(hdev, hci_reset_req, 0, HZ);
+- hdev->flags &= ~HCI_INIT;
++ set_bit(HCI_INIT, &hdev->flags);
++ __hci_request(hdev, hci_reset_req, 0, HZ/4);
++ clear_bit(HCI_INIT, &hdev->flags);
+
+ /* Kill cmd task */
+ tasklet_kill(&hdev->cmd_task);
+@@ -605,17 +567,28 @@
+ }
+
+ /* After this point our queues are empty
+- * and no tasks are scheduled.
+- */
++ * and no tasks are scheduled. */
+ hdev->close(hdev);
+
+-done:
+- hci_req_unlock(hdev);
+- hci_dev_put(hdev);
++ /* Clear flags */
++ hdev->flags = 0;
+
++ hci_req_unlock(hdev);
+ return 0;
+ }
+
++int hci_dev_close(__u16 dev)
++{
++ struct hci_dev *hdev;
++ int err;
++
++ if (!(hdev = hci_dev_get(dev)))
++ return -ENODEV;
++ err = hci_dev_do_close(hdev);
++ hci_dev_put(hdev);
++ return err;
++}
++
+ int hci_dev_reset(__u16 dev)
+ {
+ struct hci_dev *hdev;
+@@ -627,16 +600,17 @@
+ hci_req_lock(hdev);
+ tasklet_disable(&hdev->tx_task);
+
+- if (!(hdev->flags & HCI_UP))
++ if (!test_bit(HCI_UP, &hdev->flags))
+ goto done;
+
+ /* Drop queues */
+ skb_queue_purge(&hdev->rx_q);
+ skb_queue_purge(&hdev->cmd_q);
+
+- inquiry_cache_flush(&hdev->inq_cache);
+-
++ hci_dev_lock_bh(hdev);
++ inquiry_cache_flush(hdev);
+ hci_conn_hash_flush(hdev);
++ hci_dev_unlock_bh(hdev);
+
+ if (hdev->flush)
+ hdev->flush(hdev);
+@@ -650,7 +624,6 @@
+ tasklet_enable(&hdev->tx_task);
+ hci_req_unlock(hdev);
+ hci_dev_put(hdev);
+-
+ return ret;
+ }
+
+@@ -669,30 +642,11 @@
+ return ret;
+ }
+
+-int hci_dev_setauth(unsigned long arg)
+-{
+- struct hci_dev *hdev;
+- struct hci_dev_req dr;
+- int ret = 0;
+-
+- if (copy_from_user(&dr, (void *) arg, sizeof(dr)))
+- return -EFAULT;
+-
+- if (!(hdev = hci_dev_get(dr.dev_id)))
+- return -ENODEV;
+-
+- ret = hci_request(hdev, hci_auth_req, dr.dev_opt, HCI_INIT_TIMEOUT);
+-
+- hci_dev_put(hdev);
+-
+- return ret;
+-}
+-
+-int hci_dev_setscan(unsigned long arg)
++int hci_dev_cmd(unsigned int cmd, unsigned long arg)
+ {
+ struct hci_dev *hdev;
+ struct hci_dev_req dr;
+- int ret = 0;
++ int err = 0;
+
+ if (copy_from_user(&dr, (void *) arg, sizeof(dr)))
+ return -EFAULT;
+@@ -700,75 +654,105 @@
+ if (!(hdev = hci_dev_get(dr.dev_id)))
+ return -ENODEV;
+
+- ret = hci_request(hdev, hci_scan_req, dr.dev_opt, HCI_INIT_TIMEOUT);
+-
+- hci_dev_put(hdev);
++ switch (cmd) {
++ case HCISETAUTH:
++ err = hci_request(hdev, hci_auth_req, dr.dev_opt, HCI_INIT_TIMEOUT);
++ break;
+
+- return ret;
+-}
++ case HCISETENCRYPT:
++ if (!lmp_encrypt_capable(hdev)) {
++ err = -EOPNOTSUPP;
++ break;
++ }
+
+-int hci_dev_setptype(unsigned long arg)
+-{
+- struct hci_dev *hdev;
+- struct hci_dev_req dr;
+- int ret = 0;
++ if (!test_bit(HCI_AUTH, &hdev->flags)) {
++ /* Auth must be enabled first */
++ err = hci_request(hdev, hci_auth_req,
++ dr.dev_opt, HCI_INIT_TIMEOUT);
++ if (err)
++ break;
++ }
++
++ err = hci_request(hdev, hci_encrypt_req,
++ dr.dev_opt, HCI_INIT_TIMEOUT);
++ break;
++
++ case HCISETSCAN:
++ err = hci_request(hdev, hci_scan_req, dr.dev_opt, HCI_INIT_TIMEOUT);
++ break;
++
++ case HCISETPTYPE:
++ hdev->pkt_type = (__u16) dr.dev_opt;
++ break;
++
++ case HCISETLINKPOL:
++ hdev->link_policy = (__u16) dr.dev_opt;
++ break;
+
+- if (copy_from_user(&dr, (void *) arg, sizeof(dr)))
+- return -EFAULT;
++ case HCISETLINKMODE:
++ hdev->link_mode = ((__u16) dr.dev_opt) & (HCI_LM_MASTER | HCI_LM_ACCEPT);
++ break;
+
+- if (!(hdev = hci_dev_get(dr.dev_id)))
+- return -ENODEV;
++ case HCISETACLMTU:
++ hdev->acl_mtu = *((__u16 *)&dr.dev_opt + 1);
++ hdev->acl_pkts = *((__u16 *)&dr.dev_opt + 0);
++ break;
+
+- hdev->pkt_type = (__u16) dr.dev_opt;
++ case HCISETSCOMTU:
++ hdev->sco_mtu = *((__u16 *)&dr.dev_opt + 1);
++ hdev->sco_pkts = *((__u16 *)&dr.dev_opt + 0);
++ break;
+
++ default:
++ err = -EINVAL;
++ break;
++ }
+ hci_dev_put(hdev);
+-
+- return ret;
++ return err;
+ }
+
+-int hci_dev_list(unsigned long arg)
++int hci_get_dev_list(unsigned long arg)
+ {
+ struct hci_dev_list_req *dl;
+ struct hci_dev_req *dr;
+- struct hci_dev *hdev;
+- int i, n, size;
++ struct list_head *p;
++ int n = 0, size, err;
+ __u16 dev_num;
+
+ if (get_user(dev_num, (__u16 *) arg))
+ return -EFAULT;
+
+- /* Avoid long loop, overflow */
+- if (dev_num > 2048)
++ if (!dev_num || dev_num > (PAGE_SIZE * 2) / sizeof(*dr))
+ return -EINVAL;
+-
+- size = dev_num * sizeof(struct hci_dev_req) + sizeof(__u16);
+
+- if (verify_area(VERIFY_WRITE, (void *) arg, size))
+- return -EFAULT;
++ size = sizeof(*dl) + dev_num * sizeof(*dr);
+
+ if (!(dl = kmalloc(size, GFP_KERNEL)))
+ return -ENOMEM;
++
+ dr = dl->dev_req;
+
+- spin_lock_bh(&hdev_list_lock);
+- for (i = 0, n = 0; i < HCI_MAX_DEV && n < dev_num; i++) {
+- if ((hdev = hdev_list[i])) {
+- (dr + n)->dev_id = hdev->id;
+- (dr + n)->dev_opt = hdev->flags;
+- n++;
+- }
++ read_lock_bh(&hdev_list_lock);
++ list_for_each(p, &hdev_list) {
++ struct hci_dev *hdev;
++ hdev = list_entry(p, struct hci_dev, list);
++ (dr + n)->dev_id = hdev->id;
++ (dr + n)->dev_opt = hdev->flags;
++ if (++n >= dev_num)
++ break;
+ }
+- spin_unlock_bh(&hdev_list_lock);
++ read_unlock_bh(&hdev_list_lock);
+
+ dl->dev_num = n;
+- size = n * sizeof(struct hci_dev_req) + sizeof(__u16);
++ size = sizeof(*dl) + n * sizeof(*dr);
+
+- copy_to_user((void *) arg, dl, size);
++ err = copy_to_user((void *) arg, dl, size);
++ kfree(dl);
+
+- return 0;
++ return err ? -EFAULT : 0;
+ }
+
+-int hci_dev_info(unsigned long arg)
++int hci_get_dev_info(unsigned long arg)
+ {
+ struct hci_dev *hdev;
+ struct hci_dev_info di;
+@@ -786,9 +770,11 @@
+ di.flags = hdev->flags;
+ di.pkt_type = hdev->pkt_type;
+ di.acl_mtu = hdev->acl_mtu;
+- di.acl_max = hdev->acl_max;
++ di.acl_pkts = hdev->acl_pkts;
+ di.sco_mtu = hdev->sco_mtu;
+- di.sco_max = hdev->sco_max;
++ di.sco_pkts = hdev->sco_pkts;
++ di.link_policy = hdev->link_policy;
++ di.link_mode = hdev->link_mode;
+
+ memcpy(&di.stat, &hdev->stat, sizeof(di.stat));
+ memcpy(&di.features, &hdev->features, sizeof(di.features));
+@@ -801,258 +787,168 @@
+ return err;
+ }
+
+-__u32 hci_dev_setmode(struct hci_dev *hdev, __u32 mode)
+-{
+- __u32 omode = hdev->flags & HCI_MODE_MASK;
+-
+- hdev->flags &= ~HCI_MODE_MASK;
+- hdev->flags |= (mode & HCI_MODE_MASK);
+
+- return omode;
+-}
++/* ---- Interface to HCI drivers ---- */
+
+-__u32 hci_dev_getmode(struct hci_dev *hdev)
++/* Register HCI device */
++int hci_register_dev(struct hci_dev *hdev)
+ {
+- return hdev->flags & HCI_MODE_MASK;
+-}
++ struct list_head *head = &hdev_list, *p;
++ int id = 0;
+
+-int hci_conn_list(unsigned long arg)
+-{
+- struct hci_conn_list_req req, *cl;
+- struct hci_conn_info *ci;
+- struct hci_dev *hdev;
+- struct list_head *p;
+- int n = 0, size;
++ BT_DBG("%p name %s type %d", hdev, hdev->name, hdev->type);
+
+- if (copy_from_user(&req, (void *) arg, sizeof(req)))
+- return -EFAULT;
++ if (!hdev->open || !hdev->close || !hdev->destruct)
++ return -EINVAL;
+
+- if (!(hdev = hci_dev_get(req.dev_id)))
+- return -ENODEV;
++ write_lock_bh(&hdev_list_lock);
+
+- /* Set a limit to avoid overlong loops, and also numeric overflow - AC */
+- if(req.conn_num < 2048)
+- return -EINVAL;
++ /* Find first available device id */
++ list_for_each(p, &hdev_list) {
++ if (list_entry(p, struct hci_dev, list)->id != id)
++ break;
++ head = p; id++;
++ }
+
+- size = req.conn_num * sizeof(struct hci_conn_info) + sizeof(req);
++ sprintf(hdev->name, "hci%d", id);
++ hdev->id = id;
++ list_add(&hdev->list, head);
+
+- if (!(cl = kmalloc(size, GFP_KERNEL)))
+- return -ENOMEM;
+- ci = cl->conn_info;
+-
+- local_bh_disable();
+- conn_hash_lock(&hdev->conn_hash);
+- list_for_each(p, &hdev->conn_hash.list) {
+- register struct hci_conn *c;
+- c = list_entry(p, struct hci_conn, list);
++ atomic_set(&hdev->refcnt, 1);
++ spin_lock_init(&hdev->lock);
++
++ hdev->flags = 0;
++ hdev->pkt_type = (HCI_DM1 | HCI_DH1 | HCI_HV1);
++ hdev->link_mode = (HCI_LM_ACCEPT);
+
+- (ci + n)->handle = c->handle;
+- bacpy(&(ci + n)->bdaddr, &c->dst);
+- n++;
+- }
+- conn_hash_unlock(&hdev->conn_hash);
+- local_bh_enable();
+-
+- cl->dev_id = hdev->id;
+- cl->conn_num = n;
+- size = n * sizeof(struct hci_conn_info) + sizeof(req);
++ tasklet_init(&hdev->cmd_task, hci_cmd_task,(unsigned long) hdev);
++ tasklet_init(&hdev->rx_task, hci_rx_task, (unsigned long) hdev);
++ tasklet_init(&hdev->tx_task, hci_tx_task, (unsigned long) hdev);
+
+- hci_dev_put(hdev);
++ skb_queue_head_init(&hdev->rx_q);
++ skb_queue_head_init(&hdev->cmd_q);
++ skb_queue_head_init(&hdev->raw_q);
+
+- if(copy_to_user((void *) arg, cl, size))
+- return -EFAULT;
+- return 0;
+-}
++ init_waitqueue_head(&hdev->req_wait_q);
++ init_MUTEX(&hdev->req_lock);
+
+-int hci_inquiry(unsigned long arg)
+-{
+- struct inquiry_cache *cache;
+- struct hci_inquiry_req ir;
+- struct hci_dev *hdev;
+- int err = 0, do_inquiry = 0;
+- long timeo;
+- __u8 *buf, *ptr;
++ inquiry_cache_init(hdev);
+
+- ptr = (void *) arg;
+- if (copy_from_user(&ir, ptr, sizeof(ir)))
+- return -EFAULT;
++ conn_hash_init(hdev);
+
+- if (!(hdev = hci_dev_get(ir.dev_id)))
+- return -ENODEV;
++ memset(&hdev->stat, 0, sizeof(struct hci_dev_stats));
+
+- cache = &hdev->inq_cache;
++ atomic_set(&hdev->promisc, 0);
+
+- inquiry_cache_lock(cache);
+- if (inquiry_cache_age(cache) > INQUIRY_CACHE_AGE_MAX || ir.flags & IREQ_CACHE_FLUSH) {
+- inquiry_cache_flush(cache);
+- do_inquiry = 1;
+- }
+- inquiry_cache_unlock(cache);
++ MOD_INC_USE_COUNT;
+
+- /* Limit inquiry time, also avoid overflows */
++ write_unlock_bh(&hdev_list_lock);
+
+- if(ir.length > 2048 || ir.num_rsp > 2048)
+- {
+- err = -EINVAL;
+- goto done;
+- }
++ hci_notify(hdev, HCI_DEV_REG);
++ hci_run_hotplug(hdev->name, "register");
+
+- timeo = ir.length * 2 * HZ;
+- if (do_inquiry && (err = hci_request(hdev, hci_inq_req, (unsigned long)&ir, timeo)) < 0)
+- goto done;
++ return id;
++}
+
+- /* cache_dump can't sleep. Therefore we allocate temp buffer and then
+- * copy it to the user space.
+- */
+- if (!(buf = kmalloc(sizeof(inquiry_info) * ir.num_rsp, GFP_KERNEL))) {
+- err = -ENOMEM;
+- goto done;
+- }
+- ir.num_rsp = inquiry_cache_dump(cache, ir.num_rsp, buf);
++/* Unregister HCI device */
++int hci_unregister_dev(struct hci_dev *hdev)
++{
++ BT_DBG("%p name %s type %d", hdev, hdev->name, hdev->type);
+
+- DBG("num_rsp %d", ir.num_rsp);
++ write_lock_bh(&hdev_list_lock);
++ list_del(&hdev->list);
++ write_unlock_bh(&hdev_list_lock);
+
+- if (!verify_area(VERIFY_WRITE, ptr, sizeof(ir) + (sizeof(inquiry_info) * ir.num_rsp))) {
+- copy_to_user(ptr, &ir, sizeof(ir));
+- ptr += sizeof(ir);
+- copy_to_user(ptr, buf, sizeof(inquiry_info) * ir.num_rsp);
+- } else
+- err = -EFAULT;
++ hci_dev_do_close(hdev);
+
+- kfree(buf);
++ hci_notify(hdev, HCI_DEV_UNREG);
++ hci_run_hotplug(hdev->name, "unregister");
+
+-done:
+ hci_dev_put(hdev);
+
+- return err;
++ MOD_DEC_USE_COUNT;
++ return 0;
+ }
+
+-/* Interface to HCI drivers */
+-
+-/* Register HCI device */
+-int hci_register_dev(struct hci_dev *hdev)
++/* Suspend HCI device */
++int hci_suspend_dev(struct hci_dev *hdev)
+ {
+- int i;
++ hci_notify(hdev, HCI_DEV_SUSPEND);
++ hci_run_hotplug(hdev->name, "suspend");
++ return 0;
++}
+
+- DBG("%p name %s type %d", hdev, hdev->name, hdev->type);
++/* Resume HCI device */
++int hci_resume_dev(struct hci_dev *hdev)
++{
++ hci_notify(hdev, HCI_DEV_RESUME);
++ hci_run_hotplug(hdev->name, "resume");
++ return 0;
++}
+
+- /* Find free slot */
+- spin_lock_bh(&hdev_list_lock);
+- for (i = 0; i < HCI_MAX_DEV; i++) {
+- if (!hdev_list[i]) {
+- hdev_list[i] = hdev;
+-
+- sprintf(hdev->name, "hci%d", i);
+- atomic_set(&hdev->refcnt, 0);
+- hdev->id = i;
+- hdev->flags = HCI_NORMAL;
+-
+- hdev->pkt_type = (HCI_DM1 | HCI_DH1);
+-
+- tasklet_init(&hdev->cmd_task, hci_cmd_task, (unsigned long) hdev);
+- tasklet_init(&hdev->rx_task, hci_rx_task, (unsigned long) hdev);
+- tasklet_init(&hdev->tx_task, hci_tx_task, (unsigned long) hdev);
+-
+- skb_queue_head_init(&hdev->rx_q);
+- skb_queue_head_init(&hdev->cmd_q);
+- skb_queue_head_init(&hdev->raw_q);
+-
+- init_waitqueue_head(&hdev->req_wait_q);
+- init_MUTEX(&hdev->req_lock);
+-
+- inquiry_cache_init(&hdev->inq_cache);
+-
+- conn_hash_init(&hdev->conn_hash);
+-
+- memset(&hdev->stat, 0, sizeof(struct hci_dev_stats));
+-
+- hci_notify(hdev, HCI_DEV_REG);
+-
+- MOD_INC_USE_COUNT;
+- break;
+- }
+- }
+- spin_unlock_bh(&hdev_list_lock);
+-
+- return (i == HCI_MAX_DEV) ? -1 : i;
+-}
+-
+-/* Unregister HCI device */
+-int hci_unregister_dev(struct hci_dev *hdev)
++/* Receive frame from HCI drivers */
++int hci_recv_frame(struct sk_buff *skb)
+ {
+- int i;
+-
+- DBG("%p name %s type %d", hdev, hdev->name, hdev->type);
+-
+- if (hdev->flags & HCI_UP)
+- hci_dev_close(hdev->id);
++ struct hci_dev *hdev = (struct hci_dev *) skb->dev;
+
+- /* Find device slot */
+- spin_lock(&hdev_list_lock);
+- for (i = 0; i < HCI_MAX_DEV; i++) {
+- if (hdev_list[i] == hdev) {
+- hdev_list[i] = NULL;
+- MOD_DEC_USE_COUNT;
+- break;
+- }
++ if (!hdev || (!test_bit(HCI_UP, &hdev->flags) &&
++ !test_bit(HCI_INIT, &hdev->flags)) ) {
++ kfree_skb(skb);
++ return -1;
+ }
+- spin_unlock(&hdev_list_lock);
+
+- hci_notify(hdev, HCI_DEV_UNREG);
+-
+- /* Sleep while device is in use */
+- while (atomic_read(&hdev->refcnt)) {
+- int sleep_cnt = 100;
++ BT_DBG("%s type %d len %d", hdev->name, skb->pkt_type, skb->len);
+
+- DBG("%s sleeping on lock %d", hdev->name, atomic_read(&hdev->refcnt));
++ /* Incomming skb */
++ bluez_cb(skb)->incomming = 1;
+
+- sleep_on_timeout(&hdev->req_wait_q, HZ*10);
+- if (!(--sleep_cnt))
+- break;
+- }
++ /* Time stamp */
++ do_gettimeofday(&skb->stamp);
+
++ /* Queue frame for rx task */
++ skb_queue_tail(&hdev->rx_q, skb);
++ hci_sched_rx(hdev);
+ return 0;
+ }
+
+-/* Interface to upper protocols */
++/* ---- Interface to upper protocols ---- */
+
+ /* Register/Unregister protocols.
+- * hci_task_lock is used to ensure that no tasks are running.
+- */
+-int hci_register_proto(struct hci_proto *hproto)
++ * hci_task_lock is used to ensure that no tasks are running. */
++int hci_register_proto(struct hci_proto *hp)
+ {
+ int err = 0;
+
+- DBG("%p name %s", hproto, hproto->name);
++ BT_DBG("%p name %s id %d", hp, hp->name, hp->id);
+
+- if (hproto->id >= HCI_MAX_PROTO)
++ if (hp->id >= HCI_MAX_PROTO)
+ return -EINVAL;
+
+ write_lock_bh(&hci_task_lock);
+
+- if (!hproto_list[hproto->id])
+- hproto_list[hproto->id] = hproto;
++ if (!hci_proto[hp->id])
++ hci_proto[hp->id] = hp;
+ else
+- err = -1;
++ err = -EEXIST;
+
+ write_unlock_bh(&hci_task_lock);
+
+ return err;
+ }
+
+-int hci_unregister_proto(struct hci_proto *hproto)
++int hci_unregister_proto(struct hci_proto *hp)
+ {
+ int err = 0;
+
+- DBG("%p name %s", hproto, hproto->name);
++ BT_DBG("%p name %s id %d", hp, hp->name, hp->id);
+
+- if (hproto->id > HCI_MAX_PROTO)
++ if (hp->id >= HCI_MAX_PROTO)
+ return -EINVAL;
+
+ write_lock_bh(&hci_task_lock);
+
+- if (hproto_list[hproto->id])
+- hproto_list[hproto->id] = NULL;
++ if (hci_proto[hp->id])
++ hci_proto[hp->id] = NULL;
+ else
+ err = -ENOENT;
+
+@@ -1070,10 +966,14 @@
+ return -ENODEV;
+ }
+
+- DBG("%s type %d len %d", hdev->name, skb->pkt_type, skb->len);
++ BT_DBG("%s type %d len %d", hdev->name, skb->pkt_type, skb->len);
++
++ if (atomic_read(&hdev->promisc)) {
++ /* Time stamp */
++ do_gettimeofday(&skb->stamp);
+
+- if (hdev->flags & HCI_SOCK)
+ hci_send_to_sock(hdev, skb);
++ }
+
+ /* Get rid of skb owner, prior to sending to the driver. */
+ skb_orphan(skb);
+@@ -1081,128 +981,6 @@
+ return hdev->send(skb);
+ }
+
+-/* Connection scheduler */
+-static inline struct hci_conn *hci_low_sent(struct hci_dev *hdev, __u8 type, int *quote)
+-{
+- struct conn_hash *h = &hdev->conn_hash;
+- struct hci_conn *conn = NULL;
+- int num = 0, min = 0xffff;
+- struct list_head *p;
+-
+- conn_hash_lock(h);
+- list_for_each(p, &h->list) {
+- register struct hci_conn *c;
+-
+- c = list_entry(p, struct hci_conn, list);
+-
+- if (c->type != type || skb_queue_empty(&c->data_q))
+- continue;
+- num++;
+-
+- if (c->sent < min) {
+- min = c->sent;
+- conn = c;
+- }
+- }
+- conn_hash_unlock(h);
+-
+- if (conn) {
+- int q = hdev->acl_cnt / num;
+- *quote = q ? q : 1;
+- } else
+- *quote = 0;
+-
+- DBG("conn %p quote %d", conn, *quote);
+-
+- return conn;
+-}
+-
+-static inline void hci_sched_acl(struct hci_dev *hdev)
+-{
+- struct hci_conn *conn;
+- struct sk_buff *skb;
+- int quote;
+-
+- DBG("%s", hdev->name);
+-
+- while (hdev->acl_cnt && (conn = hci_low_sent(hdev, ACL_LINK, &quote))) {
+- while (quote && (skb = skb_dequeue(&conn->data_q))) {
+- DBG("skb %p len %d", skb, skb->len);
+-
+- hci_send_frame(skb);
+-
+- conn->sent++;
+- hdev->acl_cnt--;
+- quote--;
+- }
+- }
+-}
+-
+-/* Schedule SCO */
+-static inline void hci_sched_sco(struct hci_dev *hdev)
+-{
+- /* FIXME: For now we queue SCO packets to the raw queue
+-
+- while (hdev->sco_cnt && (skb = skb_dequeue(&conn->data_q))) {
+- hci_send_frame(skb);
+- conn->sco_sent++;
+- hdev->sco_cnt--;
+- }
+- */
+-}
+-
+-/* Get data from the previously sent command */
+-static void * hci_sent_cmd_data(struct hci_dev *hdev, __u16 ogf, __u16 ocf)
+-{
+- hci_command_hdr *hc;
+-
+- if (!hdev->sent_cmd)
+- return NULL;
+-
+- hc = (void *) hdev->sent_cmd->data;
+-
+- if (hc->opcode != __cpu_to_le16(cmd_opcode_pack(ogf, ocf)))
+- return NULL;
+-
+- DBG("%s ogf 0x%x ocf 0x%x", hdev->name, ogf, ocf);
+-
+- return hdev->sent_cmd->data + HCI_COMMAND_HDR_SIZE;
+-}
+-
+-/* Send raw HCI frame */
+-int hci_send_raw(struct sk_buff *skb)
+-{
+- struct hci_dev *hdev = (struct hci_dev *) skb->dev;
+-
+- if (!hdev) {
+- kfree_skb(skb);
+- return -ENODEV;
+- }
+-
+- DBG("%s type %d len %d", hdev->name, skb->pkt_type, skb->len);
+-
+- if (hdev->flags & HCI_NORMAL) {
+- /* Queue frame according it's type */
+- switch (skb->pkt_type) {
+- case HCI_COMMAND_PKT:
+- skb_queue_tail(&hdev->cmd_q, skb);
+- hci_sched_cmd(hdev);
+- return 0;
+-
+- case HCI_ACLDATA_PKT:
+- case HCI_SCODATA_PKT:
+- /* FIXME:
+- * Check header here and queue to apropriate connection.
+- */
+- break;
+- }
+- }
+-
+- skb_queue_tail(&hdev->raw_q, skb);
+- hci_sched_tx(hdev);
+- return 0;
+-}
+-
+ /* Send HCI command */
+ int hci_send_cmd(struct hci_dev *hdev, __u16 ogf, __u16 ocf, __u32 plen, void *param)
+ {
+@@ -1210,10 +988,10 @@
+ hci_command_hdr *hc;
+ struct sk_buff *skb;
+
+- DBG("%s ogf 0x%x ocf 0x%x plen %d", hdev->name, ogf, ocf, plen);
++ BT_DBG("%s ogf 0x%x ocf 0x%x plen %d", hdev->name, ogf, ocf, plen);
+
+ if (!(skb = bluez_skb_alloc(len, GFP_ATOMIC))) {
+- ERR("%s Can't allocate memory for HCI command", hdev->name);
++ BT_ERR("%s Can't allocate memory for HCI command", hdev->name);
+ return -ENOMEM;
+ }
+
+@@ -1224,7 +1002,7 @@
+ if (plen)
+ memcpy(skb_put(skb, plen), param, plen);
+
+- DBG("skb len %d", skb->len);
++ BT_DBG("skb len %d", skb->len);
+
+ skb->pkt_type = HCI_COMMAND_PKT;
+ skb->dev = (void *) hdev;
+@@ -1234,10 +1012,28 @@
+ return 0;
+ }
+
++/* Get data from the previously sent command */
++void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 ogf, __u16 ocf)
++{
++ hci_command_hdr *hc;
++
++ if (!hdev->sent_cmd)
++ return NULL;
++
++ hc = (void *) hdev->sent_cmd->data;
++
++ if (hc->opcode != __cpu_to_le16(cmd_opcode_pack(ogf, ocf)))
++ return NULL;
++
++ BT_DBG("%s ogf 0x%x ocf 0x%x", hdev->name, ogf, ocf);
++
++ return hdev->sent_cmd->data + HCI_COMMAND_HDR_SIZE;
++}
++
+ /* Send ACL data */
+ static void hci_add_acl_hdr(struct sk_buff *skb, __u16 handle, __u16 flags)
+ {
+- int len = skb->len;
++ int len = skb->len;
+ hci_acl_hdr *ah;
+
+ ah = (hci_acl_hdr *) skb_push(skb, HCI_ACL_HDR_SIZE);
+@@ -1252,7 +1048,7 @@
+ struct hci_dev *hdev = conn->hdev;
+ struct sk_buff *list;
+
+- DBG("%s conn %p flags 0x%x", hdev->name, conn, flags);
++ BT_DBG("%s conn %p flags 0x%x", hdev->name, conn, flags);
+
+ skb->dev = (void *) hdev;
+ skb->pkt_type = HCI_ACLDATA_PKT;
+@@ -1260,12 +1056,12 @@
+
+ if (!(list = skb_shinfo(skb)->frag_list)) {
+ /* Non fragmented */
+- DBG("%s nonfrag skb %p len %d", hdev->name, skb, skb->len);
++ BT_DBG("%s nonfrag skb %p len %d", hdev->name, skb, skb->len);
+
+ skb_queue_tail(&conn->data_q, skb);
+ } else {
+ /* Fragmented */
+- DBG("%s frag %p len %d", hdev->name, skb, skb->len);
++ BT_DBG("%s frag %p len %d", hdev->name, skb, skb->len);
+
+ skb_shinfo(skb)->frag_list = NULL;
+
+@@ -1280,7 +1076,7 @@
+ skb->pkt_type = HCI_ACLDATA_PKT;
+ hci_add_acl_hdr(skb, conn->handle, flags | ACL_CONT);
+
+- DBG("%s frag %p len %d", hdev->name, skb, skb->len);
++ BT_DBG("%s frag %p len %d", hdev->name, skb, skb->len);
+
+ __skb_queue_tail(&conn->data_q, skb);
+ } while (list);
+@@ -1298,7 +1094,7 @@
+ struct hci_dev *hdev = conn->hdev;
+ hci_sco_hdr hs;
+
+- DBG("%s len %d", hdev->name, skb->len);
++ BT_DBG("%s len %d", hdev->name, skb->len);
+
+ if (skb->len > hdev->sco_mtu) {
+ kfree_skb(skb);
+@@ -1315,544 +1111,136 @@
+ skb->pkt_type = HCI_SCODATA_PKT;
+ skb_queue_tail(&conn->data_q, skb);
+ hci_sched_tx(hdev);
+-
+ return 0;
+ }
+
+-/* Handle HCI Event packets */
+-
+-/* Command Complete OGF LINK_CTL */
+-static void hci_cc_link_ctl(struct hci_dev *hdev, __u16 ocf, struct sk_buff *skb)
+-{
+- DBG("%s ocf 0x%x", hdev->name, ocf);
+-
+- switch (ocf) {
+- default:
+- DBG("%s Command complete: ogf LINK_CTL ocf %x", hdev->name, ocf);
+- break;
+- };
+-}
+-
+-/* Command Complete OGF LINK_POLICY */
+-static void hci_cc_link_policy(struct hci_dev *hdev, __u16 ocf, struct sk_buff *skb)
+-{
+- DBG("%s ocf 0x%x", hdev->name, ocf);
+-
+- switch (ocf) {
+- default:
+- DBG("%s: Command complete: ogf LINK_POLICY ocf %x", hdev->name, ocf);
+- break;
+- };
+-}
+-
+-/* Command Complete OGF HOST_CTL */
+-static void hci_cc_host_ctl(struct hci_dev *hdev, __u16 ocf, struct sk_buff *skb)
+-{
+- __u8 status, param;
+- void *sent;
+-
+-
+- DBG("%s ocf 0x%x", hdev->name, ocf);
+-
+- switch (ocf) {
+- case OCF_RESET:
+- status = *((__u8 *) skb->data);
+-
+- hci_req_complete(hdev, status);
+- break;
+-
+- case OCF_SET_EVENT_FLT:
+- status = *((__u8 *) skb->data);
+-
+- if (status) {
+- DBG("%s SET_EVENT_FLT failed %d", hdev->name, status);
+- } else {
+- DBG("%s SET_EVENT_FLT succeseful", hdev->name);
+- }
+- break;
+-
+- case OCF_WRITE_AUTH_ENABLE:
+- if (!(sent = hci_sent_cmd_data(hdev, OGF_HOST_CTL, OCF_WRITE_AUTH_ENABLE)))
+- break;
+-
+- status = *((__u8 *) skb->data);
+- param = *((__u8 *) sent);
+-
+- if (!status) {
+- if (param == AUTH_ENABLED)
+- hdev->flags |= HCI_AUTH;
+- else
+- hdev->flags &= ~HCI_AUTH;
+- }
+- hci_req_complete(hdev, status);
+- break;
+-
+- case OCF_WRITE_CA_TIMEOUT:
+- status = *((__u8 *) skb->data);
+-
+- if (status) {
+- DBG("%s OCF_WRITE_CA_TIMEOUT failed %d", hdev->name, status);
+- } else {
+- DBG("%s OCF_WRITE_CA_TIMEOUT succeseful", hdev->name);
+- }
+- break;
+-
+- case OCF_WRITE_PG_TIMEOUT:
+- status = *((__u8 *) skb->data);
+-
+- if (status) {
+- DBG("%s OCF_WRITE_PG_TIMEOUT failed %d", hdev->name, status);
+- } else {
+- DBG("%s: OCF_WRITE_PG_TIMEOUT succeseful", hdev->name);
+- }
+- break;
+-
+- case OCF_WRITE_SCAN_ENABLE:
+- if (!(sent = hci_sent_cmd_data(hdev, OGF_HOST_CTL, OCF_WRITE_SCAN_ENABLE)))
+- break;
+- status = *((__u8 *) skb->data);
+- param = *((__u8 *) sent);
+-
+- DBG("param 0x%x", param);
+-
+- if (!status) {
+- switch (param) {
+- case IS_ENA_PS_ENA:
+- hdev->flags |= HCI_PSCAN | HCI_ISCAN;
+- break;
+-
+- case IS_ENA_PS_DIS:
+- hdev->flags &= ~HCI_PSCAN;
+- hdev->flags |= HCI_ISCAN;
+- break;
++/* ---- HCI TX task (outgoing data) ---- */
+
+- case IS_DIS_PS_ENA:
+- hdev->flags &= ~HCI_ISCAN;
+- hdev->flags |= HCI_PSCAN;
+- break;
+-
+- default:
+- hdev->flags &= ~(HCI_ISCAN | HCI_PSCAN);
+- break;
+- };
+- }
+- hci_req_complete(hdev, status);
+- break;
+-
+- default:
+- DBG("%s Command complete: ogf HOST_CTL ocf %x", hdev->name, ocf);
+- break;
+- };
+-}
+-
+-/* Command Complete OGF INFO_PARAM */
+-static void hci_cc_info_param(struct hci_dev *hdev, __u16 ocf, struct sk_buff *skb)
++/* HCI Connection scheduler */
++static inline struct hci_conn *hci_low_sent(struct hci_dev *hdev, __u8 type, int *quote)
+ {
+- read_local_features_rp *lf;
+- read_buffer_size_rp *bs;
+- read_bd_addr_rp *ba;
+-
+- DBG("%s ocf 0x%x", hdev->name, ocf);
+-
+- switch (ocf) {
+- case OCF_READ_LOCAL_FEATURES:
+- lf = (read_local_features_rp *) skb->data;
+-
+- if (lf->status) {
+- DBG("%s READ_LOCAL_FEATURES failed %d", hdev->name, lf->status);
+- break;
+- }
+-
+- memcpy(hdev->features, lf->features, sizeof(hdev->features));
+-
+- /* Adjust default settings according to features
+- * supported by device. */
+- if (hdev->features[0] & LMP_3SLOT)
+- hdev->pkt_type |= (HCI_DM3 | HCI_DH3);
+-
+- if (hdev->features[0] & LMP_5SLOT)
+- hdev->pkt_type |= (HCI_DM5 | HCI_DH5);
+-
+- DBG("%s: features 0x%x 0x%x 0x%x", hdev->name, lf->features[0], lf->features[1], lf->features[2]);
+-
+- break;
+-
+- case OCF_READ_BUFFER_SIZE:
+- bs = (read_buffer_size_rp *) skb->data;
+-
+- if (bs->status) {
+- DBG("%s READ_BUFFER_SIZE failed %d", hdev->name, bs->status);
+- break;
+- }
+-
+- hdev->acl_mtu = __le16_to_cpu(bs->acl_mtu);
+- hdev->sco_mtu = bs->sco_mtu;
+- hdev->acl_max = hdev->acl_cnt = __le16_to_cpu(bs->acl_max_pkt);
+- hdev->sco_max = hdev->sco_cnt = __le16_to_cpu(bs->sco_max_pkt);
+-
+- DBG("%s mtu: acl %d, sco %d max_pkt: acl %d, sco %d", hdev->name,
+- hdev->acl_mtu, hdev->sco_mtu, hdev->acl_max, hdev->sco_max);
++ struct conn_hash *h = &hdev->conn_hash;
++ struct hci_conn *conn = NULL;
++ int num = 0, min = ~0;
++ struct list_head *p;
+
+- break;
++ /* We don't have to lock device here. Connections are always
++ * added and removed with TX task disabled. */
++ list_for_each(p, &h->list) {
++ struct hci_conn *c;
++ c = list_entry(p, struct hci_conn, list);
+
+- case OCF_READ_BD_ADDR:
+- ba = (read_bd_addr_rp *) skb->data;
++ if (c->type != type || c->state != BT_CONNECTED
++ || skb_queue_empty(&c->data_q))
++ continue;
++ num++;
+
+- if (!ba->status) {
+- bacpy(&hdev->bdaddr, &ba->bdaddr);
+- } else {
+- DBG("%s: READ_BD_ADDR failed %d", hdev->name, ba->status);
++ if (c->sent < min) {
++ min = c->sent;
++ conn = c;
+ }
++ }
+
+- hci_req_complete(hdev, ba->status);
+- break;
++ if (conn) {
++ int cnt = (type == ACL_LINK ? hdev->acl_cnt : hdev->sco_cnt);
++ int q = cnt / num;
++ *quote = q ? q : 1;
++ } else
++ *quote = 0;
+
+- default:
+- DBG("%s Command complete: ogf INFO_PARAM ocf %x", hdev->name, ocf);
+- break;
+- };
++ BT_DBG("conn %p quote %d", conn, *quote);
++ return conn;
+ }
+
+-/* Command Status OGF LINK_CTL */
+-static void hci_cs_link_ctl(struct hci_dev *hdev, __u16 ocf, __u8 status)
++static inline void hci_acl_tx_to(struct hci_dev *hdev)
+ {
+- struct hci_proto * hp;
+-
+- DBG("%s ocf 0x%x", hdev->name, ocf);
+-
+- switch (ocf) {
+- case OCF_CREATE_CONN:
+- if (status) {
+- create_conn_cp *cc = hci_sent_cmd_data(hdev, OGF_LINK_CTL, OCF_CREATE_CONN);
+-
+- if (!cc)
+- break;
+-
+- DBG("%s Create connection error: status 0x%x %s", hdev->name,
+- status, batostr(&cc->bdaddr));
++ struct conn_hash *h = &hdev->conn_hash;
++ struct list_head *p;
++ struct hci_conn *c;
+
+- /* Notify upper protocols */
+- if ((hp = GET_HPROTO(HCI_PROTO_L2CAP)) && hp->connect_cfm) {
+- tasklet_disable(&hdev->tx_task);
+- hp->connect_cfm(hdev, &cc->bdaddr, status, NULL);
+- tasklet_enable(&hdev->tx_task);
+- }
+- }
+- break;
++ BT_ERR("%s ACL tx timeout", hdev->name);
+
+- case OCF_INQUIRY:
+- if (status) {
+- DBG("%s Inquiry error: status 0x%x", hdev->name, status);
+- hci_req_complete(hdev, status);
++ /* Kill stalled connections */
++ list_for_each(p, &h->list) {
++ c = list_entry(p, struct hci_conn, list);
++ if (c->type == ACL_LINK && c->sent) {
++ BT_ERR("%s killing stalled ACL connection %s",
++ hdev->name, batostr(&c->dst));
++ hci_acl_disconn(c, 0x13);
+ }
+- break;
+-
+- default:
+- DBG("%s Command status: ogf LINK_CTL ocf %x", hdev->name, ocf);
+- break;
+- };
+-}
+-
+-/* Command Status OGF LINK_POLICY */
+-static void hci_cs_link_policy(struct hci_dev *hdev, __u16 ocf, __u8 status)
+-{
+- DBG("%s ocf 0x%x", hdev->name, ocf);
+-
+- switch (ocf) {
+- default:
+- DBG("%s Command status: ogf HOST_POLICY ocf %x", hdev->name, ocf);
+- break;
+- };
+-}
+-
+-/* Command Status OGF HOST_CTL */
+-static void hci_cs_host_ctl(struct hci_dev *hdev, __u16 ocf, __u8 status)
+-{
+- DBG("%s ocf 0x%x", hdev->name, ocf);
+-
+- switch (ocf) {
+- default:
+- DBG("%s Command status: ogf HOST_CTL ocf %x", hdev->name, ocf);
+- break;
+- };
+-}
+-
+-/* Command Status OGF INFO_PARAM */
+-static void hci_cs_info_param(struct hci_dev *hdev, __u16 ocf, __u8 status)
+-{
+- DBG("%s: hci_cs_info_param: ocf 0x%x", hdev->name, ocf);
+-
+- switch (ocf) {
+- default:
+- DBG("%s Command status: ogf INFO_PARAM ocf %x", hdev->name, ocf);
+- break;
+- };
+-}
+-
+-/* Inquiry Complete */
+-static void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
+-{
+- __u8 status = *((__u8 *) skb->data);
+-
+- DBG("%s status %d", hdev->name, status);
+-
+- hci_req_complete(hdev, status);
++ }
+ }
+
+-/* Inquiry Result */
+-static void hci_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff *skb)
++static inline void hci_sched_acl(struct hci_dev *hdev)
+ {
+- inquiry_info *info = (inquiry_info *) (skb->data + 1);
+- int num_rsp = *((__u8 *) skb->data);
+-
+- DBG("%s num_rsp %d", hdev->name, num_rsp);
++ struct hci_conn *conn;
++ struct sk_buff *skb;
++ int quote;
+
+- for (; num_rsp; num_rsp--)
+- inquiry_cache_update(&hdev->inq_cache, info++);
+-}
++ BT_DBG("%s", hdev->name);
+
+-/* Connect Request */
+-static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
+-{
+- evt_conn_request *cr = (evt_conn_request *) skb->data;
+- struct hci_proto *hp;
+- accept_conn_req_cp ac;
+- int accept = 0;
++ /* ACL tx timeout must be longer than maximum
++ * link supervision timeout (40.9 seconds) */
++ if (!hdev->acl_cnt && (jiffies - hdev->acl_last_tx) > (HZ * 45))
++ hci_acl_tx_to(hdev);
+
+- DBG("%s Connection request: %s type 0x%x", hdev->name, batostr(&cr->bdaddr), cr->link_type);
++ while (hdev->acl_cnt && (conn = hci_low_sent(hdev, ACL_LINK, &quote))) {
++ while (quote-- && (skb = skb_dequeue(&conn->data_q))) {
++ BT_DBG("skb %p len %d", skb, skb->len);
++ hci_send_frame(skb);
++ hdev->acl_last_tx = jiffies;
+
+- /* Notify upper protocols */
+- if (cr->link_type == ACL_LINK) {
+- /* ACL link notify L2CAP */
+- if ((hp = GET_HPROTO(HCI_PROTO_L2CAP)) && hp->connect_ind) {
+- tasklet_disable(&hdev->tx_task);
+- accept = hp->connect_ind(hdev, &cr->bdaddr);
+- tasklet_enable(&hdev->tx_task);
++ hdev->acl_cnt--;
++ conn->sent++;
+ }
+- } else {
+- /* SCO link (no notification) */
+- /* FIXME: Should be accept it here or let the requester (app) accept it ? */
+- accept = 1;
+- }
+-
+- if (accept) {
+- /* Connection accepted by upper layer */
+- bacpy(&ac.bdaddr, &cr->bdaddr);
+- ac.role = 0x01; /* Remain slave */
+- hci_send_cmd(hdev, OGF_LINK_CTL, OCF_ACCEPT_CONN_REQ, ACCEPT_CONN_REQ_CP_SIZE, &ac);
+- } else {
+- /* Connection rejected by upper layer */
+- /* FIXME:
+- * Should we use HCI reject here ?
+- */
+- return;
+ }
+ }
+
+-/* Connect Complete */
+-static void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
++/* Schedule SCO */
++static inline void hci_sched_sco(struct hci_dev *hdev)
+ {
+- evt_conn_complete *cc = (evt_conn_complete *) skb->data;
+- struct hci_conn *conn = NULL;
+- struct hci_proto *hp;
+-
+- DBG("%s", hdev->name);
+-
+- tasklet_disable(&hdev->tx_task);
+-
+- if (!cc->status)
+- conn = hci_conn_add(hdev, __le16_to_cpu(cc->handle), cc->link_type, &cc->bdaddr);
++ struct hci_conn *conn;
++ struct sk_buff *skb;
++ int quote;
+
+- /* Notify upper protocols */
+- if (cc->link_type == ACL_LINK) {
+- /* ACL link notify L2CAP layer */
+- if ((hp = GET_HPROTO(HCI_PROTO_L2CAP)) && hp->connect_cfm)
+- hp->connect_cfm(hdev, &cc->bdaddr, cc->status, conn);
+- } else {
+- /* SCO link (no notification) */
+- }
++ BT_DBG("%s", hdev->name);
+
+- tasklet_enable(&hdev->tx_task);
+-}
++ while (hdev->sco_cnt && (conn = hci_low_sent(hdev, SCO_LINK, &quote))) {
++ while (quote-- && (skb = skb_dequeue(&conn->data_q))) {
++ BT_DBG("skb %p len %d", skb, skb->len);
++ hci_send_frame(skb);
+
+-/* Disconnect Complete */
+-static void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
+-{
+- evt_disconn_complete *dc = (evt_disconn_complete *) skb->data;
+- struct hci_conn *conn = NULL;
+- struct hci_proto *hp;
+- __u16 handle = __le16_to_cpu(dc->handle);
+-
+- DBG("%s", hdev->name);
+-
+- if (!dc->status && (conn = conn_hash_lookup(&hdev->conn_hash, handle))) {
+- tasklet_disable(&hdev->tx_task);
+-
+- /* Notify upper protocols */
+- if (conn->type == ACL_LINK) {
+- /* ACL link notify L2CAP layer */
+- if ((hp = GET_HPROTO(HCI_PROTO_L2CAP)) && hp->disconn_ind)
+- hp->disconn_ind(conn, dc->reason);
+- } else {
+- /* SCO link (no notification) */
++ conn->sent++;
++ if (conn->sent == ~0)
++ conn->sent = 0;
+ }
+-
+- hci_conn_del(hdev, conn);
+-
+- tasklet_enable(&hdev->tx_task);
+ }
+ }
+
+-/* Number of completed packets */
+-static void hci_num_comp_pkts_evt(struct hci_dev *hdev, struct sk_buff *skb)
++static void hci_tx_task(unsigned long arg)
+ {
+- evt_num_comp_pkts *nc = (evt_num_comp_pkts *) skb->data;
+- __u16 *ptr;
+- int i;
+-
+- skb_pull(skb, EVT_NUM_COMP_PKTS_SIZE);
+-
+- DBG("%s num_hndl %d", hdev->name, nc->num_hndl);
++ struct hci_dev *hdev = (struct hci_dev *) arg;
++ struct sk_buff *skb;
+
+- if (skb->len < nc->num_hndl * 4) {
+- DBG("%s bad parameters", hdev->name);
+- return;
+- }
++ read_lock(&hci_task_lock);
+
+- tasklet_disable(&hdev->tx_task);
++ BT_DBG("%s acl %d sco %d", hdev->name, hdev->acl_cnt, hdev->sco_cnt);
+
+- for (i = 0, ptr = (__u16 *) skb->data; i < nc->num_hndl; i++) {
+- struct hci_conn *conn;
+- __u16 handle, count;
++ /* Schedule queues and send stuff to HCI driver */
+
+- handle = __le16_to_cpu(get_unaligned(ptr++));
+- count = __le16_to_cpu(get_unaligned(ptr++));
++ hci_sched_acl(hdev);
+
+- hdev->acl_cnt += count;
++ hci_sched_sco(hdev);
+
+- if ((conn = conn_hash_lookup(&hdev->conn_hash, handle)))
+- conn->sent -= count;
+- }
++ /* Send next queued raw (unknown type) packet */
++ while ((skb = skb_dequeue(&hdev->raw_q)))
++ hci_send_frame(skb);
+
+- tasklet_enable(&hdev->tx_task);
+-
+- hci_sched_tx(hdev);
++ read_unlock(&hci_task_lock);
+ }
+
+-static inline void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
+-{
+- hci_event_hdr *he = (hci_event_hdr *) skb->data;
+- evt_cmd_status *cs;
+- evt_cmd_complete *ec;
+- __u16 opcode, ocf, ogf;
+-
+- skb_pull(skb, HCI_EVENT_HDR_SIZE);
+-
+- DBG("%s evt 0x%x", hdev->name, he->evt);
+-
+- switch (he->evt) {
+- case EVT_NUM_COMP_PKTS:
+- hci_num_comp_pkts_evt(hdev, skb);
+- break;
+-
+- case EVT_INQUIRY_COMPLETE:
+- hci_inquiry_complete_evt(hdev, skb);
+- break;
+
+- case EVT_INQUIRY_RESULT:
+- hci_inquiry_result_evt(hdev, skb);
+- break;
+-
+- case EVT_CONN_REQUEST:
+- hci_conn_request_evt(hdev, skb);
+- break;
+-
+- case EVT_CONN_COMPLETE:
+- hci_conn_complete_evt(hdev, skb);
+- break;
+-
+- case EVT_DISCONN_COMPLETE:
+- hci_disconn_complete_evt(hdev, skb);
+- break;
+-
+- case EVT_CMD_STATUS:
+- cs = (evt_cmd_status *) skb->data;
+- skb_pull(skb, EVT_CMD_STATUS_SIZE);
+-
+- opcode = __le16_to_cpu(cs->opcode);
+- ogf = cmd_opcode_ogf(opcode);
+- ocf = cmd_opcode_ocf(opcode);
+-
+- switch (ogf) {
+- case OGF_INFO_PARAM:
+- hci_cs_info_param(hdev, ocf, cs->status);
+- break;
+-
+- case OGF_HOST_CTL:
+- hci_cs_host_ctl(hdev, ocf, cs->status);
+- break;
+-
+- case OGF_LINK_CTL:
+- hci_cs_link_ctl(hdev, ocf, cs->status);
+- break;
+-
+- case OGF_LINK_POLICY:
+- hci_cs_link_policy(hdev, ocf, cs->status);
+- break;
+-
+- default:
+- DBG("%s Command Status OGF %x", hdev->name, ogf);
+- break;
+- };
+-
+- if (cs->ncmd) {
+- atomic_set(&hdev->cmd_cnt, 1);
+- if (!skb_queue_empty(&hdev->cmd_q))
+- hci_sched_cmd(hdev);
+- }
+- break;
+-
+- case EVT_CMD_COMPLETE:
+- ec = (evt_cmd_complete *) skb->data;
+- skb_pull(skb, EVT_CMD_COMPLETE_SIZE);
+-
+- opcode = __le16_to_cpu(ec->opcode);
+- ogf = cmd_opcode_ogf(opcode);
+- ocf = cmd_opcode_ocf(opcode);
+-
+- switch (ogf) {
+- case OGF_INFO_PARAM:
+- hci_cc_info_param(hdev, ocf, skb);
+- break;
+-
+- case OGF_HOST_CTL:
+- hci_cc_host_ctl(hdev, ocf, skb);
+- break;
+-
+- case OGF_LINK_CTL:
+- hci_cc_link_ctl(hdev, ocf, skb);
+- break;
+-
+- case OGF_LINK_POLICY:
+- hci_cc_link_policy(hdev, ocf, skb);
+- break;
+-
+- default:
+- DBG("%s Command Completed OGF %x", hdev->name, ogf);
+- break;
+- };
+-
+- if (ec->ncmd) {
+- atomic_set(&hdev->cmd_cnt, 1);
+- if (!skb_queue_empty(&hdev->cmd_q))
+- hci_sched_cmd(hdev);
+- }
+- break;
+- };
+-
+- kfree_skb(skb);
+- hdev->stat.evt_rx++;
+-}
++/* ----- HCI RX task (incomming data proccessing) ----- */
+
+ /* ACL data packet */
+ static inline void hci_acldata_packet(struct hci_dev *hdev, struct sk_buff *skb)
+@@ -1867,51 +1255,86 @@
+ flags = acl_flags(handle);
+ handle = acl_handle(handle);
+
+- DBG("%s len %d handle 0x%x flags 0x%x", hdev->name, skb->len, handle, flags);
++ BT_DBG("%s len %d handle 0x%x flags 0x%x", hdev->name, skb->len, handle, flags);
++
++ hdev->stat.acl_rx++;
+
+- if ((conn = conn_hash_lookup(&hdev->conn_hash, handle))) {
++ hci_dev_lock(hdev);
++ conn = conn_hash_lookup_handle(hdev, handle);
++ hci_dev_unlock(hdev);
++
++ if (conn) {
+ register struct hci_proto *hp;
+
+ /* Send to upper protocol */
+- if ((hp = GET_HPROTO(HCI_PROTO_L2CAP)) && hp->recv_acldata) {
++ if ((hp = hci_proto[HCI_PROTO_L2CAP]) && hp->recv_acldata) {
+ hp->recv_acldata(conn, skb, flags);
+- goto sent;
++ return;
+ }
+ } else {
+- ERR("%s ACL packet for unknown connection handle %d", hdev->name, handle);
++ BT_ERR("%s ACL packet for unknown connection handle %d",
++ hdev->name, handle);
+ }
+
+ kfree_skb(skb);
+-sent:
+- hdev->stat.acl_rx++;
+ }
+
+ /* SCO data packet */
+ static inline void hci_scodata_packet(struct hci_dev *hdev, struct sk_buff *skb)
+ {
+- DBG("%s len %d", hdev->name, skb->len);
++ hci_sco_hdr *sh = (void *) skb->data;
++ struct hci_conn *conn;
++ __u16 handle;
++
++ skb_pull(skb, HCI_SCO_HDR_SIZE);
++
++ handle = __le16_to_cpu(sh->handle);
++
++ BT_DBG("%s len %d handle 0x%x", hdev->name, skb->len, handle);
+
+- kfree_skb(skb);
+ hdev->stat.sco_rx++;
++
++ hci_dev_lock(hdev);
++ conn = conn_hash_lookup_handle(hdev, handle);
++ hci_dev_unlock(hdev);
++
++ if (conn) {
++ register struct hci_proto *hp;
++
++ /* Send to upper protocol */
++ if ((hp = hci_proto[HCI_PROTO_SCO]) && hp->recv_scodata) {
++ hp->recv_scodata(conn, skb);
++ return;
++ }
++ } else {
++ BT_ERR("%s SCO packet for unknown connection handle %d",
++ hdev->name, handle);
++ }
++
++ kfree_skb(skb);
+ }
+
+-/* ----- HCI tasks ----- */
+ void hci_rx_task(unsigned long arg)
+ {
+ struct hci_dev *hdev = (struct hci_dev *) arg;
+ struct sk_buff *skb;
+
+- DBG("%s", hdev->name);
++ BT_DBG("%s", hdev->name);
+
+ read_lock(&hci_task_lock);
+
+ while ((skb = skb_dequeue(&hdev->rx_q))) {
+- if (hdev->flags & HCI_SOCK) {
++ if (atomic_read(&hdev->promisc)) {
+ /* Send copy to the sockets */
+ hci_send_to_sock(hdev, skb);
+ }
+
+- if (hdev->flags & HCI_INIT) {
++ if (test_bit(HCI_RAW, &hdev->flags)) {
++ kfree_skb(skb);
++ continue;
++ }
++
++ if (test_bit(HCI_INIT, &hdev->flags)) {
+ /* Don't process data packets in this states. */
+ switch (skb->pkt_type) {
+ case HCI_ACLDATA_PKT:
+@@ -1921,64 +1344,43 @@
+ };
+ }
+
+- if (hdev->flags & HCI_NORMAL) {
+- /* Process frame */
+- switch (skb->pkt_type) {
+- case HCI_EVENT_PKT:
+- hci_event_packet(hdev, skb);
+- break;
++ /* Process frame */
++ switch (skb->pkt_type) {
++ case HCI_EVENT_PKT:
++ hci_event_packet(hdev, skb);
++ break;
+
+- case HCI_ACLDATA_PKT:
+- DBG("%s ACL data packet", hdev->name);
+- hci_acldata_packet(hdev, skb);
+- break;
++ case HCI_ACLDATA_PKT:
++ BT_DBG("%s ACL data packet", hdev->name);
++ hci_acldata_packet(hdev, skb);
++ break;
+
+- case HCI_SCODATA_PKT:
+- DBG("%s SCO data packet", hdev->name);
+- hci_scodata_packet(hdev, skb);
+- break;
++ case HCI_SCODATA_PKT:
++ BT_DBG("%s SCO data packet", hdev->name);
++ hci_scodata_packet(hdev, skb);
++ break;
+
+- default:
+- kfree_skb(skb);
+- break;
+- };
+- } else {
++ default:
+ kfree_skb(skb);
++ break;
+ }
+ }
+
+ read_unlock(&hci_task_lock);
+ }
+
+-static void hci_tx_task(unsigned long arg)
+-{
+- struct hci_dev *hdev = (struct hci_dev *) arg;
+- struct sk_buff *skb;
+-
+- read_lock(&hci_task_lock);
+-
+- DBG("%s acl %d sco %d", hdev->name, hdev->acl_cnt, hdev->sco_cnt);
+-
+- /* Schedule queues and send stuff to HCI driver */
+-
+- hci_sched_acl(hdev);
+-
+- hci_sched_sco(hdev);
+-
+- /* Send next queued raw (unknown type) packet */
+- while ((skb = skb_dequeue(&hdev->raw_q)))
+- hci_send_frame(skb);
+-
+- read_unlock(&hci_task_lock);
+-}
+-
+ static void hci_cmd_task(unsigned long arg)
+ {
+ struct hci_dev *hdev = (struct hci_dev *) arg;
+ struct sk_buff *skb;
+
+- DBG("%s cmd %d", hdev->name, atomic_read(&hdev->cmd_cnt));
++ BT_DBG("%s cmd %d", hdev->name, atomic_read(&hdev->cmd_cnt));
+
++ if (!atomic_read(&hdev->cmd_cnt) && (jiffies - hdev->cmd_last_tx) > HZ) {
++ BT_ERR("%s command tx timeout", hdev->name);
++ atomic_set(&hdev->cmd_cnt, 1);
++ }
++
+ /* Send queued commands */
+ if (atomic_read(&hdev->cmd_cnt) && (skb = skb_dequeue(&hdev->cmd_q))) {
+ if (hdev->sent_cmd)
+@@ -1987,6 +1389,7 @@
+ if ((hdev->sent_cmd = skb_clone(skb, GFP_ATOMIC))) {
+ atomic_dec(&hdev->cmd_cnt);
+ hci_send_frame(skb);
++ hdev->cmd_last_tx = jiffies;
+ } else {
+ skb_queue_head(&hdev->cmd_q, skb);
+ hci_sched_cmd(hdev);
+@@ -1994,33 +1397,10 @@
+ }
+ }
+
+-/* Receive frame from HCI drivers */
+-int hci_recv_frame(struct sk_buff *skb)
+-{
+- struct hci_dev *hdev = (struct hci_dev *) skb->dev;
+-
+- if (!hdev || !(hdev->flags & (HCI_UP | HCI_INIT))) {
+- kfree_skb(skb);
+- return -1;
+- }
+-
+- DBG("%s type %d len %d", hdev->name, skb->pkt_type, skb->len);
+-
+- /* Incomming skb */
+- bluez_cb(skb)->incomming = 1;
+-
+- /* Queue frame for rx task */
+- skb_queue_tail(&hdev->rx_q, skb);
+- hci_sched_rx(hdev);
+-
+- return 0;
+-}
++/* ---- Initialization ---- */
+
+ int hci_core_init(void)
+ {
+- /* Init locks */
+- spin_lock_init(&hdev_list_lock);
+-
+ return 0;
+ }
+
+@@ -2028,5 +1408,3 @@
+ {
+ return 0;
+ }
+-
+-MODULE_LICENSE("GPL");
+diff -urN linux-2.4.18/net/bluetooth/hci_event.c linux-2.4.18-mh15/net/bluetooth/hci_event.c
+--- linux-2.4.18/net/bluetooth/hci_event.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/net/bluetooth/hci_event.c 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,910 @@
++/*
++ BlueZ - Bluetooth protocol stack for Linux
++ Copyright (C) 2000-2001 Qualcomm Incorporated
++
++ Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License version 2 as
++ published by the Free Software Foundation;
++
++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++
++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
++ SOFTWARE IS DISCLAIMED.
++*/
++
++/*
++ * HCI Events.
++ *
++ * $Id$
++ */
++
++#include <linux/config.h>
++#include <linux/module.h>
++
++#include <linux/types.h>
++#include <linux/errno.h>
++#include <linux/kernel.h>
++#include <linux/major.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/poll.h>
++#include <linux/fcntl.h>
++#include <linux/init.h>
++#include <linux/skbuff.h>
++#include <linux/interrupt.h>
++#include <linux/notifier.h>
++#include <net/sock.h>
++
++#include <asm/system.h>
++#include <asm/uaccess.h>
++#include <asm/unaligned.h>
++
++#include <net/bluetooth/bluetooth.h>
++#include <net/bluetooth/hci_core.h>
++
++#ifndef HCI_CORE_DEBUG
++#undef BT_DBG
++#define BT_DBG( A... )
++#endif
++
++/* Handle HCI Event packets */
++
++/* Command Complete OGF LINK_CTL */
++static void hci_cc_link_ctl(struct hci_dev *hdev, __u16 ocf, struct sk_buff *skb)
++{
++ __u8 status;
++
++ BT_DBG("%s ocf 0x%x", hdev->name, ocf);
++
++ switch (ocf) {
++ case OCF_INQUIRY_CANCEL:
++ status = *((__u8 *) skb->data);
++
++ if (status) {
++ BT_DBG("%s Inquiry cancel error: status 0x%x", hdev->name, status);
++ } else {
++ clear_bit(HCI_INQUIRY, &hdev->flags);
++ hci_req_complete(hdev, status);
++ }
++ break;
++
++ default:
++ BT_DBG("%s Command complete: ogf LINK_CTL ocf %x", hdev->name, ocf);
++ break;
++ };
++}
++
++/* Command Complete OGF LINK_POLICY */
++static void hci_cc_link_policy(struct hci_dev *hdev, __u16 ocf, struct sk_buff *skb)
++{
++ struct hci_conn *conn;
++ role_discovery_rp *rd;
++
++ BT_DBG("%s ocf 0x%x", hdev->name, ocf);
++
++ switch (ocf) {
++ case OCF_ROLE_DISCOVERY:
++ rd = (void *) skb->data;
++
++ if (rd->status)
++ break;
++
++ hci_dev_lock(hdev);
++
++ conn = conn_hash_lookup_handle(hdev, __le16_to_cpu(rd->handle));
++ if (conn) {
++ if (rd->role)
++ conn->link_mode &= ~HCI_LM_MASTER;
++ else
++ conn->link_mode |= HCI_LM_MASTER;
++ }
++
++ hci_dev_unlock(hdev);
++ break;
++
++ default:
++ BT_DBG("%s: Command complete: ogf LINK_POLICY ocf %x",
++ hdev->name, ocf);
++ break;
++ };
++}
++
++/* Command Complete OGF HOST_CTL */
++static void hci_cc_host_ctl(struct hci_dev *hdev, __u16 ocf, struct sk_buff *skb)
++{
++ __u8 status, param;
++ void *sent;
++
++ BT_DBG("%s ocf 0x%x", hdev->name, ocf);
++
++ switch (ocf) {
++ case OCF_RESET:
++ status = *((__u8 *) skb->data);
++ hci_req_complete(hdev, status);
++ break;
++
++ case OCF_SET_EVENT_FLT:
++ status = *((__u8 *) skb->data);
++ if (status) {
++ BT_DBG("%s SET_EVENT_FLT failed %d", hdev->name, status);
++ } else {
++ BT_DBG("%s SET_EVENT_FLT succeseful", hdev->name);
++ }
++ break;
++
++ case OCF_WRITE_AUTH_ENABLE:
++ sent = hci_sent_cmd_data(hdev, OGF_HOST_CTL, OCF_WRITE_AUTH_ENABLE);
++ if (!sent)
++ break;
++
++ status = *((__u8 *) skb->data);
++ param = *((__u8 *) sent);
++
++ if (!status) {
++ if (param == AUTH_ENABLED)
++ set_bit(HCI_AUTH, &hdev->flags);
++ else
++ clear_bit(HCI_AUTH, &hdev->flags);
++ }
++ hci_req_complete(hdev, status);
++ break;
++
++ case OCF_WRITE_ENCRYPT_MODE:
++ sent = hci_sent_cmd_data(hdev, OGF_HOST_CTL, OCF_WRITE_ENCRYPT_MODE);
++ if (!sent)
++ break;
++
++ status = *((__u8 *) skb->data);
++ param = *((__u8 *) sent);
++
++ if (!status) {
++ if (param)
++ set_bit(HCI_ENCRYPT, &hdev->flags);
++ else
++ clear_bit(HCI_ENCRYPT, &hdev->flags);
++ }
++ hci_req_complete(hdev, status);
++ break;
++
++ case OCF_WRITE_CA_TIMEOUT:
++ status = *((__u8 *) skb->data);
++ if (status) {
++ BT_DBG("%s OCF_WRITE_CA_TIMEOUT failed %d", hdev->name, status);
++ } else {
++ BT_DBG("%s OCF_WRITE_CA_TIMEOUT succeseful", hdev->name);
++ }
++ break;
++
++ case OCF_WRITE_PG_TIMEOUT:
++ status = *((__u8 *) skb->data);
++ if (status) {
++ BT_DBG("%s OCF_WRITE_PG_TIMEOUT failed %d", hdev->name, status);
++ } else {
++ BT_DBG("%s: OCF_WRITE_PG_TIMEOUT succeseful", hdev->name);
++ }
++ break;
++
++ case OCF_WRITE_SCAN_ENABLE:
++ sent = hci_sent_cmd_data(hdev, OGF_HOST_CTL, OCF_WRITE_SCAN_ENABLE);
++ if (!sent)
++ break;
++ status = *((__u8 *) skb->data);
++ param = *((__u8 *) sent);
++
++ BT_DBG("param 0x%x", param);
++
++ if (!status) {
++ clear_bit(HCI_PSCAN, &hdev->flags);
++ clear_bit(HCI_ISCAN, &hdev->flags);
++ if (param & SCAN_INQUIRY)
++ set_bit(HCI_ISCAN, &hdev->flags);
++
++ if (param & SCAN_PAGE)
++ set_bit(HCI_PSCAN, &hdev->flags);
++ }
++ hci_req_complete(hdev, status);
++ break;
++
++ case OCF_HOST_BUFFER_SIZE:
++ status = *((__u8 *) skb->data);
++ if (status) {
++ BT_DBG("%s OCF_BUFFER_SIZE failed %d", hdev->name, status);
++ hci_req_complete(hdev, status);
++ }
++ break;
++
++ default:
++ BT_DBG("%s Command complete: ogf HOST_CTL ocf %x", hdev->name, ocf);
++ break;
++ };
++}
++
++/* Command Complete OGF INFO_PARAM */
++static void hci_cc_info_param(struct hci_dev *hdev, __u16 ocf, struct sk_buff *skb)
++{
++ read_local_features_rp *lf;
++ read_buffer_size_rp *bs;
++ read_bd_addr_rp *ba;
++
++ BT_DBG("%s ocf 0x%x", hdev->name, ocf);
++
++ switch (ocf) {
++ case OCF_READ_LOCAL_FEATURES:
++ lf = (read_local_features_rp *) skb->data;
++
++ if (lf->status) {
++ BT_DBG("%s READ_LOCAL_FEATURES failed %d", hdev->name, lf->status);
++ break;
++ }
++
++ memcpy(hdev->features, lf->features, sizeof(hdev->features));
++
++ /* Adjust default settings according to features
++ * supported by device. */
++ if (hdev->features[0] & LMP_3SLOT)
++ hdev->pkt_type |= (HCI_DM3 | HCI_DH3);
++
++ if (hdev->features[0] & LMP_5SLOT)
++ hdev->pkt_type |= (HCI_DM5 | HCI_DH5);
++
++ if (hdev->features[1] & LMP_HV2)
++ hdev->pkt_type |= (HCI_HV2);
++
++ if (hdev->features[1] & LMP_HV3)
++ hdev->pkt_type |= (HCI_HV3);
++
++ BT_DBG("%s: features 0x%x 0x%x 0x%x", hdev->name, lf->features[0], lf->features[1], lf->features[2]);
++
++ break;
++
++ case OCF_READ_BUFFER_SIZE:
++ bs = (read_buffer_size_rp *) skb->data;
++
++ if (bs->status) {
++ BT_DBG("%s READ_BUFFER_SIZE failed %d", hdev->name, bs->status);
++ hci_req_complete(hdev, bs->status);
++ break;
++ }
++
++ hdev->acl_mtu = __le16_to_cpu(bs->acl_mtu);
++ hdev->sco_mtu = bs->sco_mtu ? bs->sco_mtu : 64;
++ hdev->acl_pkts = hdev->acl_cnt = __le16_to_cpu(bs->acl_max_pkt);
++ hdev->sco_pkts = hdev->sco_cnt = __le16_to_cpu(bs->sco_max_pkt);
++
++ BT_DBG("%s mtu: acl %d, sco %d max_pkt: acl %d, sco %d", hdev->name,
++ hdev->acl_mtu, hdev->sco_mtu, hdev->acl_pkts, hdev->sco_pkts);
++ break;
++
++ case OCF_READ_BD_ADDR:
++ ba = (read_bd_addr_rp *) skb->data;
++
++ if (!ba->status) {
++ bacpy(&hdev->bdaddr, &ba->bdaddr);
++ } else {
++ BT_DBG("%s: READ_BD_ADDR failed %d", hdev->name, ba->status);
++ }
++
++ hci_req_complete(hdev, ba->status);
++ break;
++
++ default:
++ BT_DBG("%s Command complete: ogf INFO_PARAM ocf %x", hdev->name, ocf);
++ break;
++ };
++}
++
++/* Command Status OGF LINK_CTL */
++static inline void hci_cs_create_conn(struct hci_dev *hdev, __u8 status)
++{
++ struct hci_conn *conn;
++ create_conn_cp *cc = hci_sent_cmd_data(hdev, OGF_LINK_CTL, OCF_CREATE_CONN);
++
++ if (!cc)
++ return;
++
++ hci_dev_lock(hdev);
++
++ conn = conn_hash_lookup_ba(hdev, ACL_LINK, &cc->bdaddr);
++
++ BT_DBG("%s status 0x%x bdaddr %s conn %p", hdev->name,
++ status, batostr(&cc->bdaddr), conn);
++
++ if (status) {
++ if (conn && conn->state == BT_CONNECT) {
++ conn->state = BT_CLOSED;
++ hci_proto_connect_cfm(conn, status);
++ hci_conn_del(conn);
++ }
++ } else {
++ if (!conn) {
++ conn = hci_conn_add(hdev, ACL_LINK, &cc->bdaddr);
++ if (conn) {
++ conn->out = 1;
++ conn->link_mode |= HCI_LM_MASTER;
++ } else
++ BT_ERR("No memmory for new connection");
++ }
++ }
++
++ hci_dev_unlock(hdev);
++}
++
++static void hci_cs_link_ctl(struct hci_dev *hdev, __u16 ocf, __u8 status)
++{
++ BT_DBG("%s ocf 0x%x", hdev->name, ocf);
++
++ switch (ocf) {
++ case OCF_CREATE_CONN:
++ hci_cs_create_conn(hdev, status);
++ break;
++
++ case OCF_ADD_SCO:
++ if (status) {
++ struct hci_conn *acl, *sco;
++ add_sco_cp *cp = hci_sent_cmd_data(hdev,
++ OGF_LINK_CTL, OCF_ADD_SCO);
++ __u16 handle;
++
++ if (!cp)
++ break;
++
++ handle = __le16_to_cpu(cp->handle);
++
++ BT_DBG("%s Add SCO error: handle %d status 0x%x", hdev->name, handle, status);
++
++ hci_dev_lock(hdev);
++
++ acl = conn_hash_lookup_handle(hdev, handle);
++ if (acl && (sco = acl->link)) {
++ sco->state = BT_CLOSED;
++ hci_proto_connect_cfm(sco, status);
++ hci_conn_del(sco);
++ }
++
++ hci_dev_unlock(hdev);
++ }
++ break;
++
++ case OCF_INQUIRY:
++ if (status) {
++ BT_DBG("%s Inquiry error: status 0x%x", hdev->name, status);
++ hci_req_complete(hdev, status);
++ } else {
++ set_bit(HCI_INQUIRY, &hdev->flags);
++ }
++ break;
++
++ default:
++ BT_DBG("%s Command status: ogf LINK_CTL ocf %x status %d",
++ hdev->name, ocf, status);
++ break;
++ };
++}
++
++/* Command Status OGF LINK_POLICY */
++static void hci_cs_link_policy(struct hci_dev *hdev, __u16 ocf, __u8 status)
++{
++ BT_DBG("%s ocf 0x%x", hdev->name, ocf);
++
++ switch (ocf) {
++ default:
++ BT_DBG("%s Command status: ogf HOST_POLICY ocf %x", hdev->name, ocf);
++ break;
++ };
++}
++
++/* Command Status OGF HOST_CTL */
++static void hci_cs_host_ctl(struct hci_dev *hdev, __u16 ocf, __u8 status)
++{
++ BT_DBG("%s ocf 0x%x", hdev->name, ocf);
++
++ switch (ocf) {
++ default:
++ BT_DBG("%s Command status: ogf HOST_CTL ocf %x", hdev->name, ocf);
++ break;
++ };
++}
++
++/* Command Status OGF INFO_PARAM */
++static void hci_cs_info_param(struct hci_dev *hdev, __u16 ocf, __u8 status)
++{
++ BT_DBG("%s: hci_cs_info_param: ocf 0x%x", hdev->name, ocf);
++
++ switch (ocf) {
++ default:
++ BT_DBG("%s Command status: ogf INFO_PARAM ocf %x", hdev->name, ocf);
++ break;
++ };
++}
++
++/* Inquiry Complete */
++static inline void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
++{
++ __u8 status = *((__u8 *) skb->data);
++
++ BT_DBG("%s status %d", hdev->name, status);
++
++ clear_bit(HCI_INQUIRY, &hdev->flags);
++ hci_req_complete(hdev, status);
++}
++
++/* Inquiry Result */
++static inline void hci_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff *skb)
++{
++ inquiry_info *info = (inquiry_info *) (skb->data + 1);
++ int num_rsp = *((__u8 *) skb->data);
++
++ BT_DBG("%s num_rsp %d", hdev->name, num_rsp);
++
++ hci_dev_lock(hdev);
++ for (; num_rsp; num_rsp--)
++ inquiry_cache_update(hdev, info++);
++ hci_dev_unlock(hdev);
++}
++
++/* Inquiry Result With RSSI */
++static inline void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, struct sk_buff *skb)
++{
++ inquiry_info_with_rssi *info = (inquiry_info_with_rssi *) (skb->data + 1);
++ int num_rsp = *((__u8 *) skb->data);
++
++ BT_DBG("%s num_rsp %d", hdev->name, num_rsp);
++
++ hci_dev_lock(hdev);
++ for (; num_rsp; num_rsp--) {
++ inquiry_info tmp;
++ bacpy(&tmp.bdaddr, &info->bdaddr);
++ tmp.pscan_rep_mode = info->pscan_rep_mode;
++ tmp.pscan_period_mode = info->pscan_period_mode;
++ tmp.pscan_mode = 0x00;
++ memcpy(tmp.dev_class, &info->dev_class, 3);
++ tmp.clock_offset = info->clock_offset;
++ info++;
++ inquiry_cache_update(hdev, &tmp);
++ }
++ hci_dev_unlock(hdev);
++}
++
++/* Connect Request */
++static inline void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
++{
++ evt_conn_request *cr = (evt_conn_request *) skb->data;
++ int mask = hdev->link_mode;
++
++ BT_DBG("%s Connection request: %s type 0x%x", hdev->name,
++ batostr(&cr->bdaddr), cr->link_type);
++
++ mask |= hci_proto_connect_ind(hdev, &cr->bdaddr, cr->link_type);
++
++ if (mask & HCI_LM_ACCEPT) {
++ /* Connection accepted */
++ struct hci_conn *conn;
++ accept_conn_req_cp ac;
++
++ hci_dev_lock(hdev);
++ conn = conn_hash_lookup_ba(hdev, cr->link_type, &cr->bdaddr);
++ if (!conn) {
++ if (!(conn = hci_conn_add(hdev, cr->link_type, &cr->bdaddr))) {
++ BT_ERR("No memmory for new connection");
++ hci_dev_unlock(hdev);
++ return;
++ }
++ }
++ conn->state = BT_CONNECT;
++ hci_dev_unlock(hdev);
++
++ bacpy(&ac.bdaddr, &cr->bdaddr);
++
++ if (lmp_rswitch_capable(hdev) && (mask & HCI_LM_MASTER))
++ ac.role = 0x00; /* Become master */
++ else
++ ac.role = 0x01; /* Remain slave */
++
++ hci_send_cmd(hdev, OGF_LINK_CTL, OCF_ACCEPT_CONN_REQ,
++ ACCEPT_CONN_REQ_CP_SIZE, &ac);
++ } else {
++ /* Connection rejected */
++ reject_conn_req_cp rc;
++
++ bacpy(&rc.bdaddr, &cr->bdaddr);
++ rc.reason = 0x0f;
++ hci_send_cmd(hdev, OGF_LINK_CTL, OCF_REJECT_CONN_REQ,
++ REJECT_CONN_REQ_CP_SIZE, &rc);
++ }
++}
++
++/* Connect Complete */
++static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
++{
++ evt_conn_complete *cc = (evt_conn_complete *) skb->data;
++ struct hci_conn *conn = NULL;
++
++ BT_DBG("%s", hdev->name);
++
++ hci_dev_lock(hdev);
++
++ conn = conn_hash_lookup_ba(hdev, cc->link_type, &cc->bdaddr);
++ if (!conn) {
++ hci_dev_unlock(hdev);
++ return;
++ }
++
++ if (!cc->status) {
++ conn->handle = __le16_to_cpu(cc->handle);
++ conn->state = BT_CONNECTED;
++
++ if (test_bit(HCI_AUTH, &hdev->flags))
++ conn->link_mode |= HCI_LM_AUTH;
++
++ if (test_bit(HCI_ENCRYPT, &hdev->flags))
++ conn->link_mode |= HCI_LM_ENCRYPT;
++
++
++ /* Set link policy */
++ if (conn->type == ACL_LINK && hdev->link_policy) {
++ write_link_policy_cp lp;
++ lp.handle = cc->handle;
++ lp.policy = __cpu_to_le16(hdev->link_policy);
++ hci_send_cmd(hdev, OGF_LINK_POLICY, OCF_WRITE_LINK_POLICY,
++ WRITE_LINK_POLICY_CP_SIZE, &lp);
++ }
++
++ /* Set packet type for incomming connection */
++ if (!conn->out) {
++ change_conn_ptype_cp cp;
++ cp.handle = cc->handle;
++ cp.pkt_type = (conn->type == ACL_LINK) ?
++ __cpu_to_le16(hdev->pkt_type & ACL_PTYPE_MASK):
++ __cpu_to_le16(hdev->pkt_type & SCO_PTYPE_MASK);
++
++ hci_send_cmd(hdev, OGF_LINK_CTL, OCF_CHANGE_CONN_PTYPE,
++ CHANGE_CONN_PTYPE_CP_SIZE, &cp);
++ }
++ } else
++ conn->state = BT_CLOSED;
++
++ if (conn->type == ACL_LINK) {
++ struct hci_conn *sco = conn->link;
++ if (sco) {
++ if (!cc->status)
++ hci_add_sco(sco, conn->handle);
++ else {
++ hci_proto_connect_cfm(sco, cc->status);
++ hci_conn_del(sco);
++ }
++ }
++ }
++
++ hci_proto_connect_cfm(conn, cc->status);
++ if (cc->status)
++ hci_conn_del(conn);
++
++ hci_dev_unlock(hdev);
++}
++
++/* Disconnect Complete */
++static inline void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
++{
++ evt_disconn_complete *dc = (evt_disconn_complete *) skb->data;
++ struct hci_conn *conn = NULL;
++ __u16 handle = __le16_to_cpu(dc->handle);
++
++ BT_DBG("%s status %d", hdev->name, dc->status);
++
++ if (dc->status)
++ return;
++
++ hci_dev_lock(hdev);
++
++ conn = conn_hash_lookup_handle(hdev, handle);
++ if (conn) {
++ conn->state = BT_CLOSED;
++ hci_proto_disconn_ind(conn, dc->reason);
++ hci_conn_del(conn);
++ }
++
++ hci_dev_unlock(hdev);
++}
++
++/* Number of completed packets */
++static inline void hci_num_comp_pkts_evt(struct hci_dev *hdev, struct sk_buff *skb)
++{
++ evt_num_comp_pkts *nc = (evt_num_comp_pkts *) skb->data;
++ __u16 *ptr;
++ int i;
++
++ skb_pull(skb, EVT_NUM_COMP_PKTS_SIZE);
++
++ BT_DBG("%s num_hndl %d", hdev->name, nc->num_hndl);
++
++ if (skb->len < nc->num_hndl * 4) {
++ BT_DBG("%s bad parameters", hdev->name);
++ return;
++ }
++
++ tasklet_disable(&hdev->tx_task);
++
++ for (i = 0, ptr = (__u16 *) skb->data; i < nc->num_hndl; i++) {
++ struct hci_conn *conn;
++ __u16 handle, count;
++
++ handle = __le16_to_cpu(get_unaligned(ptr++));
++ count = __le16_to_cpu(get_unaligned(ptr++));
++
++ conn = conn_hash_lookup_handle(hdev, handle);
++ if (conn) {
++ conn->sent -= count;
++
++ if (conn->type == SCO_LINK) {
++ if ((hdev->sco_cnt += count) > hdev->sco_pkts)
++ hdev->sco_cnt = hdev->sco_pkts;
++ } else {
++ if ((hdev->acl_cnt += count) > hdev->acl_pkts)
++ hdev->acl_cnt = hdev->acl_pkts;
++ }
++ }
++ }
++ hci_sched_tx(hdev);
++
++ tasklet_enable(&hdev->tx_task);
++}
++
++/* Role Change */
++static inline void hci_role_change_evt(struct hci_dev *hdev, struct sk_buff *skb)
++{
++ evt_role_change *rc = (evt_role_change *) skb->data;
++ struct hci_conn *conn = NULL;
++
++ BT_DBG("%s status %d", hdev->name, rc->status);
++
++ if (rc->status)
++ return;
++
++ hci_dev_lock(hdev);
++
++ conn = conn_hash_lookup_ba(hdev, ACL_LINK, &rc->bdaddr);
++ if (conn) {
++ if (rc->role)
++ conn->link_mode &= ~HCI_LM_MASTER;
++ else
++ conn->link_mode |= HCI_LM_MASTER;
++ }
++
++ hci_dev_unlock(hdev);
++}
++
++/* Authentication Complete */
++static inline void hci_auth_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
++{
++ evt_auth_complete *ac = (evt_auth_complete *) skb->data;
++ struct hci_conn *conn = NULL;
++ __u16 handle = __le16_to_cpu(ac->handle);
++
++ BT_DBG("%s status %d", hdev->name, ac->status);
++
++ hci_dev_lock(hdev);
++
++ conn = conn_hash_lookup_handle(hdev, handle);
++ if (conn) {
++ if (!ac->status)
++ conn->link_mode |= HCI_LM_AUTH;
++ clear_bit(HCI_CONN_AUTH_PEND, &conn->pend);
++
++ hci_proto_auth_cfm(conn, ac->status);
++
++ if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend)) {
++ if (!ac->status) {
++ set_conn_encrypt_cp ce;
++ ce.handle = __cpu_to_le16(conn->handle);
++ ce.encrypt = 1;
++ hci_send_cmd(conn->hdev, OGF_LINK_CTL,
++ OCF_SET_CONN_ENCRYPT,
++ SET_CONN_ENCRYPT_CP_SIZE, &ce);
++ } else {
++ clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend);
++ hci_proto_encrypt_cfm(conn, ac->status);
++ }
++ }
++ }
++
++ hci_dev_unlock(hdev);
++}
++
++/* Encryption Change */
++static inline void hci_encrypt_change_evt(struct hci_dev *hdev, struct sk_buff *skb)
++{
++ evt_encrypt_change *ec = (evt_encrypt_change *) skb->data;
++ struct hci_conn *conn = NULL;
++ __u16 handle = __le16_to_cpu(ec->handle);
++
++ BT_DBG("%s status %d", hdev->name, ec->status);
++
++ hci_dev_lock(hdev);
++
++ conn = conn_hash_lookup_handle(hdev, handle);
++ if (conn) {
++ if (!ec->status) {
++ if (ec->encrypt)
++ conn->link_mode |= HCI_LM_ENCRYPT;
++ else
++ conn->link_mode &= ~HCI_LM_ENCRYPT;
++ }
++ clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend);
++
++ hci_proto_encrypt_cfm(conn, ec->status);
++ }
++
++ hci_dev_unlock(hdev);
++}
++
++void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
++{
++ hci_event_hdr *he = (hci_event_hdr *) skb->data;
++ evt_cmd_status *cs;
++ evt_cmd_complete *ec;
++ __u16 opcode, ocf, ogf;
++
++ skb_pull(skb, HCI_EVENT_HDR_SIZE);
++
++ BT_DBG("%s evt 0x%x", hdev->name, he->evt);
++
++ switch (he->evt) {
++ case EVT_NUM_COMP_PKTS:
++ hci_num_comp_pkts_evt(hdev, skb);
++ break;
++
++ case EVT_INQUIRY_COMPLETE:
++ hci_inquiry_complete_evt(hdev, skb);
++ break;
++
++ case EVT_INQUIRY_RESULT:
++ hci_inquiry_result_evt(hdev, skb);
++ break;
++
++ case EVT_INQUIRY_RESULT_WITH_RSSI:
++ hci_inquiry_result_with_rssi_evt(hdev, skb);
++ break;
++
++ case EVT_CONN_REQUEST:
++ hci_conn_request_evt(hdev, skb);
++ break;
++
++ case EVT_CONN_COMPLETE:
++ hci_conn_complete_evt(hdev, skb);
++ break;
++
++ case EVT_DISCONN_COMPLETE:
++ hci_disconn_complete_evt(hdev, skb);
++ break;
++
++ case EVT_ROLE_CHANGE:
++ hci_role_change_evt(hdev, skb);
++ break;
++
++ case EVT_AUTH_COMPLETE:
++ hci_auth_complete_evt(hdev, skb);
++ break;
++
++ case EVT_ENCRYPT_CHANGE:
++ hci_encrypt_change_evt(hdev, skb);
++ break;
++
++ case EVT_CMD_STATUS:
++ cs = (evt_cmd_status *) skb->data;
++ skb_pull(skb, EVT_CMD_STATUS_SIZE);
++
++ opcode = __le16_to_cpu(cs->opcode);
++ ogf = cmd_opcode_ogf(opcode);
++ ocf = cmd_opcode_ocf(opcode);
++
++ switch (ogf) {
++ case OGF_INFO_PARAM:
++ hci_cs_info_param(hdev, ocf, cs->status);
++ break;
++
++ case OGF_HOST_CTL:
++ hci_cs_host_ctl(hdev, ocf, cs->status);
++ break;
++
++ case OGF_LINK_CTL:
++ hci_cs_link_ctl(hdev, ocf, cs->status);
++ break;
++
++ case OGF_LINK_POLICY:
++ hci_cs_link_policy(hdev, ocf, cs->status);
++ break;
++
++ default:
++ BT_DBG("%s Command Status OGF %x", hdev->name, ogf);
++ break;
++ };
++
++ if (cs->ncmd) {
++ atomic_set(&hdev->cmd_cnt, 1);
++ if (!skb_queue_empty(&hdev->cmd_q))
++ hci_sched_cmd(hdev);
++ }
++ break;
++
++ case EVT_CMD_COMPLETE:
++ ec = (evt_cmd_complete *) skb->data;
++ skb_pull(skb, EVT_CMD_COMPLETE_SIZE);
++
++ opcode = __le16_to_cpu(ec->opcode);
++ ogf = cmd_opcode_ogf(opcode);
++ ocf = cmd_opcode_ocf(opcode);
++
++ switch (ogf) {
++ case OGF_INFO_PARAM:
++ hci_cc_info_param(hdev, ocf, skb);
++ break;
++
++ case OGF_HOST_CTL:
++ hci_cc_host_ctl(hdev, ocf, skb);
++ break;
++
++ case OGF_LINK_CTL:
++ hci_cc_link_ctl(hdev, ocf, skb);
++ break;
++
++ case OGF_LINK_POLICY:
++ hci_cc_link_policy(hdev, ocf, skb);
++ break;
++
++ default:
++ BT_DBG("%s Command Completed OGF %x", hdev->name, ogf);
++ break;
++ };
++
++ if (ec->ncmd) {
++ atomic_set(&hdev->cmd_cnt, 1);
++ if (!skb_queue_empty(&hdev->cmd_q))
++ hci_sched_cmd(hdev);
++ }
++ break;
++ };
++
++ kfree_skb(skb);
++ hdev->stat.evt_rx++;
++}
++
++/* General internal stack event */
++void hci_si_event(struct hci_dev *hdev, int type, int dlen, void *data)
++{
++ hci_event_hdr *eh;
++ evt_stack_internal *si;
++ struct sk_buff *skb;
++ int size;
++ void *ptr;
++
++ size = HCI_EVENT_HDR_SIZE + EVT_STACK_INTERNAL_SIZE + dlen;
++ skb = bluez_skb_alloc(size, GFP_ATOMIC);
++ if (!skb)
++ return;
++
++ ptr = skb_put(skb, size);
++
++ eh = ptr;
++ eh->evt = EVT_STACK_INTERNAL;
++ eh->plen = EVT_STACK_INTERNAL_SIZE + dlen;
++ ptr += HCI_EVENT_HDR_SIZE;
++
++ si = ptr;
++ si->type = type;
++ memcpy(si->data, data, dlen);
++
++ skb->pkt_type = HCI_EVENT_PKT;
++ skb->dev = (void *) hdev;
++ hci_send_to_sock(hdev, skb);
++ kfree_skb(skb);
++}
+diff -urN linux-2.4.18/net/bluetooth/hci_sock.c linux-2.4.18-mh15/net/bluetooth/hci_sock.c
+--- linux-2.4.18/net/bluetooth/hci_sock.c 2001-09-07 18:28:38.000000000 +0200
++++ linux-2.4.18-mh15/net/bluetooth/hci_sock.c 2004-08-01 16:26:23.000000000 +0200
+@@ -25,7 +25,7 @@
+ /*
+ * BlueZ HCI socket layer.
+ *
+- * $Id$
++ * $Id$
+ */
+
+ #include <linux/config.h>
+@@ -49,45 +49,54 @@
+
+ #include <asm/system.h>
+ #include <asm/uaccess.h>
++#include <asm/unaligned.h>
+
+ #include <net/bluetooth/bluetooth.h>
+-#include <net/bluetooth/bluez.h>
+ #include <net/bluetooth/hci_core.h>
+
+ #ifndef HCI_SOCK_DEBUG
+-#undef DBG
+-#define DBG( A... )
++#undef BT_DBG
++#define BT_DBG( A... )
+ #endif
+
+-/* HCI socket interface */
++/* ----- HCI socket interface ----- */
++
++/* Security filter */
++static struct hci_sec_filter hci_sec_filter = {
++ /* Packet types */
++ 0x10,
++ /* Events */
++ { 0x1000d9fe, 0x0000300c },
++ /* Commands */
++ {
++ { 0x0 },
++ /* OGF_LINK_CTL */
++ { 0xbe000006, 0x00000001, 0x0000, 0x00 },
++ /* OGF_LINK_POLICY */
++ { 0x00005200, 0x00000000, 0x0000, 0x00 },
++ /* OGF_HOST_CTL */
++ { 0xaab00200, 0x2b402aaa, 0x0154, 0x00 },
++ /* OGF_INFO_PARAM */
++ { 0x000002be, 0x00000000, 0x0000, 0x00 },
++ /* OGF_STATUS_PARAM */
++ { 0x000000ea, 0x00000000, 0x0000, 0x00 }
++ }
++};
+
+ static struct bluez_sock_list hci_sk_list = {
+ lock: RW_LOCK_UNLOCKED
+ };
+
+-static struct sock *hci_sock_lookup(struct hci_dev *hdev)
+-{
+- struct sock *sk;
+-
+- read_lock(&hci_sk_list.lock);
+- for (sk = hci_sk_list.head; sk; sk = sk->next) {
+- if (hci_pi(sk)->hdev == hdev)
+- break;
+- }
+- read_unlock(&hci_sk_list.lock);
+- return sk;
+-}
+-
+ /* Send frame to RAW socket */
+ void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb)
+ {
+ struct sock * sk;
+
+- DBG("hdev %p len %d", hdev, skb->len);
++ BT_DBG("hdev %p len %d", hdev, skb->len);
+
+ read_lock(&hci_sk_list.lock);
+ for (sk = hci_sk_list.head; sk; sk = sk->next) {
+- struct hci_filter *flt;
++ struct hci_filter *flt;
+ struct sk_buff *nskb;
+
+ if (sk->state != BT_BOUND || hci_pi(sk)->hdev != hdev)
+@@ -100,13 +109,19 @@
+ /* Apply filter */
+ flt = &hci_pi(sk)->filter;
+
+- if (!test_bit(skb->pkt_type, &flt->type_mask))
++ if (!hci_test_bit((skb->pkt_type & HCI_FLT_TYPE_BITS), &flt->type_mask))
+ continue;
+
+ if (skb->pkt_type == HCI_EVENT_PKT) {
+- register int evt = (*(__u8 *)skb->data & 63);
++ register int evt = (*(__u8 *)skb->data & HCI_FLT_EVENT_BITS);
++
++ if (!hci_test_bit(evt, &flt->event_mask))
++ continue;
+
+- if (!test_bit(evt, &flt->event_mask))
++ if (flt->opcode && ((evt == EVT_CMD_COMPLETE &&
++ flt->opcode != *(__u16 *)(skb->data + 3)) ||
++ (evt == EVT_CMD_STATUS &&
++ flt->opcode != *(__u16 *)(skb->data + 4))))
+ continue;
+ }
+
+@@ -116,8 +131,8 @@
+ /* Put type byte before the data */
+ memcpy(skb_push(nskb, 1), &nskb->pkt_type, 1);
+
+- skb_queue_tail(&sk->receive_queue, nskb);
+- sk->data_ready(sk, nskb->len);
++ if (sock_queue_rcv_skb(sk, nskb))
++ kfree_skb(nskb);
+ }
+ read_unlock(&hci_sk_list.lock);
+ }
+@@ -127,7 +142,7 @@
+ struct sock *sk = sock->sk;
+ struct hci_dev *hdev = hci_pi(sk)->hdev;
+
+- DBG("sock %p sk %p", sock, sk);
++ BT_DBG("sock %p sk %p", sock, sk);
+
+ if (!sk)
+ return 0;
+@@ -135,9 +150,7 @@
+ bluez_sock_unlink(&hci_sk_list, sk);
+
+ if (hdev) {
+- if (!hci_sock_lookup(hdev))
+- hdev->flags &= ~HCI_SOCK;
+-
++ atomic_dec(&hdev->promisc);
+ hci_dev_put(hdev);
+ }
+
+@@ -149,24 +162,55 @@
+ sock_put(sk);
+
+ MOD_DEC_USE_COUNT;
+-
+ return 0;
+ }
+
+-static int hci_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
++/* Ioctls that require bound socket */
++static inline int hci_sock_bound_ioctl(struct sock *sk, unsigned int cmd, unsigned long arg)
+ {
+- struct sock *sk = sock->sk;
+ struct hci_dev *hdev = hci_pi(sk)->hdev;
+- __u32 mode;
+
+- DBG("cmd %x arg %lx", cmd, arg);
++ if (!hdev)
++ return -EBADFD;
+
+ switch (cmd) {
+- case HCIGETINFO:
+- return hci_dev_info(arg);
++ case HCISETRAW:
++ if (!capable(CAP_NET_ADMIN))
++ return -EACCES;
+
++ if (arg)
++ set_bit(HCI_RAW, &hdev->flags);
++ else
++ clear_bit(HCI_RAW, &hdev->flags);
++
++ return 0;
++
++ case HCIGETCONNINFO:
++ return hci_get_conn_info(hdev, arg);
++
++ default:
++ if (hdev->ioctl)
++ return hdev->ioctl(hdev, cmd, arg);
++ return -EINVAL;
++ }
++}
++
++static int hci_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
++{
++ struct sock *sk = sock->sk;
++ int err;
++
++ BT_DBG("cmd %x arg %lx", cmd, arg);
++
++ switch (cmd) {
+ case HCIGETDEVLIST:
+- return hci_dev_list(arg);
++ return hci_get_dev_list(arg);
++
++ case HCIGETDEVINFO:
++ return hci_get_dev_info(arg);
++
++ case HCIGETCONNLIST:
++ return hci_get_conn_list(arg);
+
+ case HCIDEVUP:
+ if (!capable(CAP_NET_ADMIN))
+@@ -183,48 +227,31 @@
+ return -EACCES;
+ return hci_dev_reset(arg);
+
+- case HCIRESETSTAT:
++ case HCIDEVRESTAT:
+ if (!capable(CAP_NET_ADMIN))
+ return -EACCES;
+ return hci_dev_reset_stat(arg);
+
+ case HCISETSCAN:
+- if (!capable(CAP_NET_ADMIN))
+- return -EACCES;
+- return hci_dev_setscan(arg);
+-
+ case HCISETAUTH:
+- if (!capable(CAP_NET_ADMIN))
+- return -EACCES;
+- return hci_dev_setauth(arg);
+-
+- case HCISETRAW:
+- if (!capable(CAP_NET_ADMIN))
+- return -EACCES;
+-
+- if (!hdev)
+- return -EBADFD;
+-
+- if (arg)
+- mode = HCI_RAW;
+- else
+- mode = HCI_NORMAL;
+-
+- return hci_dev_setmode(hdev, mode);
+-
++ case HCISETENCRYPT:
+ case HCISETPTYPE:
++ case HCISETLINKPOL:
++ case HCISETLINKMODE:
++ case HCISETACLMTU:
++ case HCISETSCOMTU:
+ if (!capable(CAP_NET_ADMIN))
+ return -EACCES;
+- return hci_dev_setptype(arg);
++ return hci_dev_cmd(cmd, arg);
+
+ case HCIINQUIRY:
+ return hci_inquiry(arg);
+
+- case HCIGETCONNLIST:
+- return hci_conn_list(arg);
+-
+ default:
+- return -EINVAL;
++ lock_sock(sk);
++ err = hci_sock_bound_ioctl(sk, cmd, arg);
++ release_sock(sk);
++ return err;
+ };
+ }
+
+@@ -233,28 +260,35 @@
+ struct sockaddr_hci *haddr = (struct sockaddr_hci *) addr;
+ struct sock *sk = sock->sk;
+ struct hci_dev *hdev = NULL;
++ int err = 0;
+
+- DBG("sock %p sk %p", sock, sk);
++ BT_DBG("sock %p sk %p", sock, sk);
+
+ if (!haddr || haddr->hci_family != AF_BLUETOOTH)
+ return -EINVAL;
+
++ lock_sock(sk);
++
+ if (hci_pi(sk)->hdev) {
+- /* Already bound */
+- return 0;
++ err = -EALREADY;
++ goto done;
+ }
+
+ if (haddr->hci_dev != HCI_DEV_NONE) {
+- if (!(hdev = hci_dev_get(haddr->hci_dev)))
+- return -ENODEV;
++ if (!(hdev = hci_dev_get(haddr->hci_dev))) {
++ err = -ENODEV;
++ goto done;
++ }
+
+- hdev->flags |= HCI_SOCK;
++ atomic_inc(&hdev->promisc);
+ }
+
+ hci_pi(sk)->hdev = hdev;
+ sk->state = BT_BOUND;
+
+- return 0;
++done:
++ release_sock(sk);
++ return err;
+ }
+
+ static int hci_sock_getname(struct socket *sock, struct sockaddr *addr, int *addr_len, int peer)
+@@ -262,73 +296,44 @@
+ struct sockaddr_hci *haddr = (struct sockaddr_hci *) addr;
+ struct sock *sk = sock->sk;
+
+- DBG("sock %p sk %p", sock, sk);
++ BT_DBG("sock %p sk %p", sock, sk);
++
++ lock_sock(sk);
+
+ *addr_len = sizeof(*haddr);
+ haddr->hci_family = AF_BLUETOOTH;
+ haddr->hci_dev = hci_pi(sk)->hdev->id;
+
++ release_sock(sk);
+ return 0;
+ }
+
+-static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg, int len,
+- struct scm_cookie *scm)
+-{
+- struct sock *sk = sock->sk;
+- struct hci_dev *hdev = hci_pi(sk)->hdev;
+- struct sk_buff *skb;
+- int err;
+-
+- DBG("sock %p sk %p", sock, sk);
+-
+- if (msg->msg_flags & MSG_OOB)
+- return -EOPNOTSUPP;
+-
+- if (msg->msg_flags & ~(MSG_DONTWAIT|MSG_NOSIGNAL|MSG_ERRQUEUE))
+- return -EINVAL;
+-
+- if (!hdev)
+- return -EBADFD;
+-
+- if (!(skb = bluez_skb_send_alloc(sk, len, msg->msg_flags & MSG_DONTWAIT, &err)))
+- return err;
+-
+- if (memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len)) {
+- kfree_skb(skb);
+- return -EFAULT;
+- }
+-
+- skb->dev = (void *) hdev;
+- skb->pkt_type = *((unsigned char *) skb->data);
+- skb_pull(skb, 1);
+-
+- /* Send frame to HCI core */
+- hci_send_raw(skb);
+-
+- return len;
+-}
+-
+ static inline void hci_sock_cmsg(struct sock *sk, struct msghdr *msg, struct sk_buff *skb)
+ {
+ __u32 mask = hci_pi(sk)->cmsg_mask;
+
+ if (mask & HCI_CMSG_DIR)
+ put_cmsg(msg, SOL_HCI, HCI_CMSG_DIR, sizeof(int), &bluez_cb(skb)->incomming);
++
++ if (mask & HCI_CMSG_TSTAMP)
++ put_cmsg(msg, SOL_HCI, HCI_CMSG_TSTAMP, sizeof(skb->stamp), &skb->stamp);
+ }
+
+-static int hci_sock_recvmsg(struct socket *sock, struct msghdr *msg, int len,
+- int flags, struct scm_cookie *scm)
++static int hci_sock_recvmsg(struct socket *sock, struct msghdr *msg, int len, int flags, struct scm_cookie *scm)
+ {
+ int noblock = flags & MSG_DONTWAIT;
+ struct sock *sk = sock->sk;
+ struct sk_buff *skb;
+ int copied, err;
+
+- DBG("sock %p sk %p", sock, sk);
++ BT_DBG("sock %p, sk %p", sock, sk);
+
+- if (flags & (MSG_OOB | MSG_PEEK))
++ if (flags & (MSG_OOB))
+ return -EOPNOTSUPP;
+
++ if (sk->state == BT_CLOSED)
++ return 0;
++
+ if (!(skb = skb_recv_datagram(sk, flags, noblock, &err)))
+ return err;
+
+@@ -343,28 +348,107 @@
+ skb->h.raw = skb->data;
+ err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
+
+- if (hci_pi(sk)->cmsg_mask)
+- hci_sock_cmsg(sk, msg, skb);
+-
++ hci_sock_cmsg(sk, msg, skb);
++
+ skb_free_datagram(sk, skb);
+
+ return err ? : copied;
+ }
+
++static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg, int len,
++ struct scm_cookie *scm)
++{
++ struct sock *sk = sock->sk;
++ struct hci_dev *hdev;
++ struct sk_buff *skb;
++ int err;
++
++ BT_DBG("sock %p sk %p", sock, sk);
++
++ if (msg->msg_flags & MSG_OOB)
++ return -EOPNOTSUPP;
++
++ if (msg->msg_flags & ~(MSG_DONTWAIT|MSG_NOSIGNAL|MSG_ERRQUEUE))
++ return -EINVAL;
++
++ if (len < 4)
++ return -EINVAL;
++
++ lock_sock(sk);
++
++ if (!(hdev = hci_pi(sk)->hdev)) {
++ err = -EBADFD;
++ goto done;
++ }
++
++ if (!(skb = bluez_skb_send_alloc(sk, len, msg->msg_flags & MSG_DONTWAIT, &err)))
++ goto done;
++
++ if (memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len)) {
++ err = -EFAULT;
++ goto drop;
++ }
++
++ skb->pkt_type = *((unsigned char *) skb->data);
++ skb_pull(skb, 1);
++ skb->dev = (void *) hdev;
++
++ if (skb->pkt_type == HCI_COMMAND_PKT) {
++ u16 opcode = __le16_to_cpu(get_unaligned((u16 *)skb->data));
++ u16 ogf = cmd_opcode_ogf(opcode);
++ u16 ocf = cmd_opcode_ocf(opcode);
++
++ if (((ogf > HCI_SFLT_MAX_OGF) ||
++ !hci_test_bit(ocf & HCI_FLT_OCF_BITS, &hci_sec_filter.ocf_mask[ogf])) &&
++ !capable(CAP_NET_RAW)) {
++ err = -EPERM;
++ goto drop;
++ }
++
++ if (test_bit(HCI_RAW, &hdev->flags) || (ogf == OGF_VENDOR_CMD)) {
++ skb_queue_tail(&hdev->raw_q, skb);
++ hci_sched_tx(hdev);
++ } else {
++ skb_queue_tail(&hdev->cmd_q, skb);
++ hci_sched_cmd(hdev);
++ }
++ } else {
++ if (!capable(CAP_NET_RAW)) {
++ err = -EPERM;
++ goto drop;
++ }
++
++ skb_queue_tail(&hdev->raw_q, skb);
++ hci_sched_tx(hdev);
++ }
++
++ err = len;
++
++done:
++ release_sock(sk);
++ return err;
++
++drop:
++ kfree_skb(skb);
++ goto done;
++}
++
+ int hci_sock_setsockopt(struct socket *sock, int level, int optname, char *optval, int len)
+ {
+ struct sock *sk = sock->sk;
+- struct hci_filter flt;
++ struct hci_filter flt = { opcode: 0 };
+ int err = 0, opt = 0;
+
+- DBG("sk %p, opt %d", sk, optname);
++ BT_DBG("sk %p, opt %d", sk, optname);
+
+ lock_sock(sk);
+
+ switch (optname) {
+ case HCI_DATA_DIR:
+- if (get_user(opt, (int *)optval))
+- return -EFAULT;
++ if (get_user(opt, (int *)optval)) {
++ err = -EFAULT;
++ break;
++ }
+
+ if (opt)
+ hci_pi(sk)->cmsg_mask |= HCI_CMSG_DIR;
+@@ -372,12 +456,31 @@
+ hci_pi(sk)->cmsg_mask &= ~HCI_CMSG_DIR;
+ break;
+
++ case HCI_TIME_STAMP:
++ if (get_user(opt, (int *)optval)) {
++ err = -EFAULT;
++ break;
++ }
++
++ if (opt)
++ hci_pi(sk)->cmsg_mask |= HCI_CMSG_TSTAMP;
++ else
++ hci_pi(sk)->cmsg_mask &= ~HCI_CMSG_TSTAMP;
++ break;
++
+ case HCI_FILTER:
+ len = MIN(len, sizeof(struct hci_filter));
+ if (copy_from_user(&flt, optval, len)) {
+ err = -EFAULT;
+ break;
+ }
++
++ if (!capable(CAP_NET_RAW)) {
++ flt.type_mask &= hci_sec_filter.type_mask;
++ flt.event_mask[0] &= hci_sec_filter.event_mask[0];
++ flt.event_mask[1] &= hci_sec_filter.event_mask[1];
++ }
++
+ memcpy(&hci_pi(sk)->filter, &flt, len);
+ break;
+
+@@ -409,6 +512,16 @@
+ return -EFAULT;
+ break;
+
++ case HCI_TIME_STAMP:
++ if (hci_pi(sk)->cmsg_mask & HCI_CMSG_TSTAMP)
++ opt = 1;
++ else
++ opt = 0;
++
++ if (put_user(opt, optval))
++ return -EFAULT;
++ break;
++
+ case HCI_FILTER:
+ len = MIN(len, sizeof(struct hci_filter));
+ if (copy_to_user(optval, &hci_pi(sk)->filter, len))
+@@ -446,7 +559,7 @@
+ {
+ struct sock *sk;
+
+- DBG("sock %p", sock);
++ BT_DBG("sock %p", sock);
+
+ if (sock->type != SOCK_RAW)
+ return -ESOCKTNOSUPPORT;
+@@ -464,44 +577,31 @@
+ sk->protocol = protocol;
+ sk->state = BT_OPEN;
+
+- /* Initialize filter */
+- hci_pi(sk)->filter.type_mask = (1<<HCI_EVENT_PKT);
+- hci_pi(sk)->filter.event_mask[0] = ~0L;
+- hci_pi(sk)->filter.event_mask[1] = ~0L;
+-
+ bluez_sock_link(&hci_sk_list, sk);
+
+ MOD_INC_USE_COUNT;
+-
+ return 0;
+ }
+
+ static int hci_sock_dev_event(struct notifier_block *this, unsigned long event, void *ptr)
+ {
+ struct hci_dev *hdev = (struct hci_dev *) ptr;
+- struct sk_buff *skb;
+-
+- DBG("hdev %s event %ld", hdev->name, event);
++ evt_si_device sd;
++
++ BT_DBG("hdev %s event %ld", hdev->name, event);
+
+ /* Send event to sockets */
+- if ((skb = bluez_skb_alloc(HCI_EVENT_HDR_SIZE + EVT_HCI_DEV_EVENT_SIZE, GFP_ATOMIC))) {
+- hci_event_hdr eh = { EVT_HCI_DEV_EVENT, EVT_HCI_DEV_EVENT_SIZE };
+- evt_hci_dev_event he = { event, hdev->id };
+-
+- skb->pkt_type = HCI_EVENT_PKT;
+- memcpy(skb_put(skb, HCI_EVENT_HDR_SIZE), &eh, HCI_EVENT_HDR_SIZE);
+- memcpy(skb_put(skb, EVT_HCI_DEV_EVENT_SIZE), &he, EVT_HCI_DEV_EVENT_SIZE);
+-
+- hci_send_to_sock(NULL, skb);
+- kfree_skb(skb);
+- }
+-
++ sd.event = event;
++ sd.dev_id = hdev->id;
++ hci_si_event(NULL, EVT_SI_DEVICE, EVT_SI_DEVICE_SIZE, &sd);
++
+ if (event == HCI_DEV_UNREG) {
+ struct sock *sk;
+
+ /* Detach sockets from device */
+ read_lock(&hci_sk_list.lock);
+ for (sk = hci_sk_list.head; sk; sk = sk->next) {
++ bh_lock_sock(sk);
+ if (hci_pi(sk)->hdev == hdev) {
+ hci_pi(sk)->hdev = NULL;
+ sk->err = EPIPE;
+@@ -510,6 +610,7 @@
+
+ hci_dev_put(hdev);
+ }
++ bh_unlock_sock(sk);
+ }
+ read_unlock(&hci_sk_list.lock);
+ }
+@@ -529,21 +630,19 @@
+ int hci_sock_init(void)
+ {
+ if (bluez_sock_register(BTPROTO_HCI, &hci_sock_family_ops)) {
+- ERR("Can't register HCI socket");
++ BT_ERR("Can't register HCI socket");
+ return -EPROTO;
+ }
+
+ hci_register_notifier(&hci_sock_nblock);
+-
+ return 0;
+ }
+
+ int hci_sock_cleanup(void)
+ {
+ if (bluez_sock_unregister(BTPROTO_HCI))
+- ERR("Can't unregister HCI socket");
++ BT_ERR("Can't unregister HCI socket");
+
+ hci_unregister_notifier(&hci_sock_nblock);
+-
+ return 0;
+ }
+diff -urN linux-2.4.18/net/bluetooth/hidp/Config.in linux-2.4.18-mh15/net/bluetooth/hidp/Config.in
+--- linux-2.4.18/net/bluetooth/hidp/Config.in 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/net/bluetooth/hidp/Config.in 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,5 @@
++#
++# Bluetooth HIDP layer configuration
++#
++
++dep_tristate 'HIDP protocol support' CONFIG_BLUEZ_HIDP $CONFIG_INPUT $CONFIG_BLUEZ_L2CAP
+diff -urN linux-2.4.18/net/bluetooth/hidp/core.c linux-2.4.18-mh15/net/bluetooth/hidp/core.c
+--- linux-2.4.18/net/bluetooth/hidp/core.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/net/bluetooth/hidp/core.c 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,655 @@
++/*
++ HIDP implementation for Linux Bluetooth stack (BlueZ).
++ Copyright (C) 2003-2004 Marcel Holtmann <marcel@holtmann.org>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License version 2 as
++ published by the Free Software Foundation;
++
++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++
++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
++ SOFTWARE IS DISCLAIMED.
++*/
++
++#include <linux/config.h>
++#include <linux/module.h>
++
++#include <linux/types.h>
++#include <linux/errno.h>
++#include <linux/kernel.h>
++#include <linux/major.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/poll.h>
++#include <linux/fcntl.h>
++#include <linux/skbuff.h>
++#include <linux/socket.h>
++#include <linux/ioctl.h>
++#include <linux/file.h>
++#include <linux/init.h>
++#include <net/sock.h>
++
++#include <linux/input.h>
++
++#include <net/bluetooth/bluetooth.h>
++#include <net/bluetooth/l2cap.h>
++
++#include "hidp.h"
++
++#ifndef CONFIG_BT_HIDP_DEBUG
++#undef BT_DBG
++#define BT_DBG(D...)
++#endif
++
++#define VERSION "1.0"
++
++static DECLARE_RWSEM(hidp_session_sem);
++static LIST_HEAD(hidp_session_list);
++
++static unsigned char hidp_keycode[256] = {
++ 0, 0, 0, 0, 30, 48, 46, 32, 18, 33, 34, 35, 23, 36, 37, 38,
++ 50, 49, 24, 25, 16, 19, 31, 20, 22, 47, 17, 45, 21, 44, 2, 3,
++ 4, 5, 6, 7, 8, 9, 10, 11, 28, 1, 14, 15, 57, 12, 13, 26,
++ 27, 43, 43, 39, 40, 41, 51, 52, 53, 58, 59, 60, 61, 62, 63, 64,
++ 65, 66, 67, 68, 87, 88, 99, 70,119,110,102,104,111,107,109,106,
++ 105,108,103, 69, 98, 55, 74, 78, 96, 79, 80, 81, 75, 76, 77, 71,
++ 72, 73, 82, 83, 86,127,116,117,183,184,185,186,187,188,189,190,
++ 191,192,193,194,134,138,130,132,128,129,131,137,133,135,136,113,
++ 115,114, 0, 0, 0,121, 0, 89, 93,124, 92, 94, 95, 0, 0, 0,
++ 122,123, 90, 91, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
++ 29, 42, 56,125, 97, 54,100,126,164,166,165,163,161,115,114,113,
++ 150,158,159,128,136,177,178,176,142,152,173,140
++};
++
++static struct hidp_session *__hidp_get_session(bdaddr_t *bdaddr)
++{
++ struct hidp_session *session;
++ struct list_head *p;
++
++ BT_DBG("");
++
++ list_for_each(p, &hidp_session_list) {
++ session = list_entry(p, struct hidp_session, list);
++ if (!bacmp(bdaddr, &session->bdaddr))
++ return session;
++ }
++ return NULL;
++}
++
++static void __hidp_link_session(struct hidp_session *session)
++{
++ MOD_INC_USE_COUNT;
++ list_add(&session->list, &hidp_session_list);
++}
++
++static void __hidp_unlink_session(struct hidp_session *session)
++{
++ list_del(&session->list);
++ MOD_DEC_USE_COUNT;
++}
++
++static void __hidp_copy_session(struct hidp_session *session, struct hidp_conninfo *ci)
++{
++ bacpy(&ci->bdaddr, &session->bdaddr);
++
++ ci->flags = session->flags;
++ ci->state = session->state;
++
++ ci->vendor = 0x0000;
++ ci->product = 0x0000;
++ ci->version = 0x0000;
++ memset(ci->name, 0, 128);
++
++ if (session->input) {
++ ci->vendor = session->input->idvendor;
++ ci->product = session->input->idproduct;
++ ci->version = session->input->idversion;
++ if (session->input->name)
++ strncpy(ci->name, session->input->name, 128);
++ else
++ strncpy(ci->name, "HID Boot Device", 128);
++ }
++}
++
++static int hidp_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
++{
++ struct hidp_session *session = dev->private;
++ struct sk_buff *skb;
++ unsigned char newleds;
++
++ BT_DBG("session %p hid %p data %p size %d", session, device, data, size);
++
++ if (type != EV_LED)
++ return -1;
++
++ newleds = (!!test_bit(LED_KANA, dev->led) << 3) |
++ (!!test_bit(LED_COMPOSE, dev->led) << 3) |
++ (!!test_bit(LED_SCROLLL, dev->led) << 2) |
++ (!!test_bit(LED_CAPSL, dev->led) << 1) |
++ (!!test_bit(LED_NUML, dev->led));
++
++ if (session->leds == newleds)
++ return 0;
++
++ session->leds = newleds;
++
++ if (!(skb = alloc_skb(3, GFP_ATOMIC))) {
++ BT_ERR("Can't allocate memory for new frame");
++ return -ENOMEM;
++ }
++
++ *skb_put(skb, 1) = 0xa2;
++ *skb_put(skb, 1) = 0x01;
++ *skb_put(skb, 1) = newleds;
++
++ skb_queue_tail(&session->intr_transmit, skb);
++
++ hidp_schedule(session);
++
++ return 0;
++}
++
++static void hidp_input_report(struct hidp_session *session, struct sk_buff *skb)
++{
++ struct input_dev *dev = session->input;
++ unsigned char *keys = session->keys;
++ unsigned char *udata = skb->data + 1;
++ signed char *sdata = skb->data + 1;
++ int i, size = skb->len - 1;
++
++ switch (skb->data[0]) {
++ case 0x01: /* Keyboard report */
++ for (i = 0; i < 8; i++)
++ input_report_key(dev, hidp_keycode[i + 224], (udata[0] >> i) & 1);
++
++ for (i = 2; i < 8; i++) {
++ if (keys[i] > 3 && memscan(udata + 2, keys[i], 6) == udata + 8) {
++ if (hidp_keycode[keys[i]])
++ input_report_key(dev, hidp_keycode[keys[i]], 0);
++ else
++ BT_ERR("Unknown key (scancode %#x) released.", keys[i]);
++ }
++
++ if (udata[i] > 3 && memscan(keys + 2, udata[i], 6) == keys + 8) {
++ if (hidp_keycode[udata[i]])
++ input_report_key(dev, hidp_keycode[udata[i]], 1);
++ else
++ BT_ERR("Unknown key (scancode %#x) pressed.", udata[i]);
++ }
++ }
++
++ memcpy(keys, udata, 8);
++ break;
++
++ case 0x02: /* Mouse report */
++ input_report_key(dev, BTN_LEFT, sdata[0] & 0x01);
++ input_report_key(dev, BTN_RIGHT, sdata[0] & 0x02);
++ input_report_key(dev, BTN_MIDDLE, sdata[0] & 0x04);
++ input_report_key(dev, BTN_SIDE, sdata[0] & 0x08);
++ input_report_key(dev, BTN_EXTRA, sdata[0] & 0x10);
++
++ input_report_rel(dev, REL_X, sdata[1]);
++ input_report_rel(dev, REL_Y, sdata[2]);
++
++ if (size > 3)
++ input_report_rel(dev, REL_WHEEL, sdata[3]);
++ break;
++ }
++
++ input_event(dev, EV_RST, 0, 0);
++}
++
++static void hidp_idle_timeout(unsigned long arg)
++{
++ struct hidp_session *session = (struct hidp_session *) arg;
++
++ atomic_inc(&session->terminate);
++ hidp_schedule(session);
++}
++
++static inline void hidp_set_timer(struct hidp_session *session)
++{
++ if (session->idle_to > 0)
++ mod_timer(&session->timer, jiffies + HZ * session->idle_to);
++}
++
++static inline void hidp_del_timer(struct hidp_session *session)
++{
++ if (session->idle_to > 0)
++ del_timer(&session->timer);
++}
++
++static inline void hidp_send_message(struct hidp_session *session, unsigned char hdr)
++{
++ struct sk_buff *skb;
++
++ BT_DBG("session %p", session);
++
++ if (!(skb = alloc_skb(1, GFP_ATOMIC))) {
++ BT_ERR("Can't allocate memory for message");
++ return;
++ }
++
++ *skb_put(skb, 1) = hdr;
++
++ skb_queue_tail(&session->ctrl_transmit, skb);
++
++ hidp_schedule(session);
++}
++
++static inline int hidp_recv_frame(struct hidp_session *session, struct sk_buff *skb)
++{
++ __u8 hdr;
++
++ BT_DBG("session %p skb %p len %d", session, skb, skb->len);
++
++ hdr = skb->data[0];
++ skb_pull(skb, 1);
++
++ if (hdr == 0xa1) {
++ hidp_set_timer(session);
++
++ if (session->input)
++ hidp_input_report(session, skb);
++ } else {
++ BT_DBG("Unsupported protocol header 0x%02x", hdr);
++ }
++
++ kfree_skb(skb);
++ return 0;
++}
++
++static int hidp_send_frame(struct socket *sock, unsigned char *data, int len)
++{
++ struct iovec iv = { data, len };
++ struct msghdr msg;
++
++ BT_DBG("sock %p data %p len %d", sock, data, len);
++
++ if (!len)
++ return 0;
++
++ memset(&msg, 0, sizeof(msg));
++ msg.msg_iovlen = 1;
++ msg.msg_iov = &iv;
++
++ return sock_sendmsg(sock, &msg, len);
++}
++
++static int hidp_process_transmit(struct hidp_session *session)
++{
++ struct sk_buff *skb;
++
++ BT_DBG("session %p", session);
++
++ while ((skb = skb_dequeue(&session->ctrl_transmit))) {
++ if (hidp_send_frame(session->ctrl_sock, skb->data, skb->len) < 0) {
++ skb_queue_head(&session->ctrl_transmit, skb);
++ break;
++ }
++
++ hidp_set_timer(session);
++ kfree_skb(skb);
++ }
++
++ while ((skb = skb_dequeue(&session->intr_transmit))) {
++ if (hidp_send_frame(session->intr_sock, skb->data, skb->len) < 0) {
++ skb_queue_head(&session->intr_transmit, skb);
++ break;
++ }
++
++ hidp_set_timer(session);
++ kfree_skb(skb);
++ }
++
++ return skb_queue_len(&session->ctrl_transmit) +
++ skb_queue_len(&session->intr_transmit);
++}
++
++static int hidp_session(void *arg)
++{
++ struct hidp_session *session = arg;
++ struct sock *ctrl_sk = session->ctrl_sock->sk;
++ struct sock *intr_sk = session->intr_sock->sk;
++ struct sk_buff *skb;
++ int vendor = 0x0000, product = 0x0000;
++ wait_queue_t ctrl_wait, intr_wait;
++ unsigned long timeo = HZ;
++
++ BT_DBG("session %p", session);
++
++ if (session->input) {
++ vendor = session->input->idvendor;
++ product = session->input->idproduct;
++ }
++
++ daemonize(); reparent_to_init();
++
++ sprintf(current->comm, "khidpd_%04x%04x", vendor, product);
++
++ sigfillset(&current->blocked);
++ flush_signals(current);
++
++ current->nice = -15;
++
++ set_fs(KERNEL_DS);
++
++ init_waitqueue_entry(&ctrl_wait, current);
++ init_waitqueue_entry(&intr_wait, current);
++ add_wait_queue(ctrl_sk->sleep, &ctrl_wait);
++ add_wait_queue(intr_sk->sleep, &intr_wait);
++ while (!atomic_read(&session->terminate)) {
++ set_current_state(TASK_INTERRUPTIBLE);
++
++ if (ctrl_sk->state != BT_CONNECTED || intr_sk->state != BT_CONNECTED)
++ break;
++
++ while ((skb = skb_dequeue(&ctrl_sk->receive_queue))) {
++ skb_orphan(skb);
++ hidp_recv_frame(session, skb);
++ }
++
++ while ((skb = skb_dequeue(&intr_sk->receive_queue))) {
++ skb_orphan(skb);
++ hidp_recv_frame(session, skb);
++ }
++
++ hidp_process_transmit(session);
++
++ schedule();
++ }
++ set_current_state(TASK_RUNNING);
++ remove_wait_queue(intr_sk->sleep, &intr_wait);
++ remove_wait_queue(ctrl_sk->sleep, &ctrl_wait);
++
++ down_write(&hidp_session_sem);
++
++ hidp_del_timer(session);
++
++ if (intr_sk->state != BT_CONNECTED) {
++ init_waitqueue_entry(&ctrl_wait, current);
++ add_wait_queue(ctrl_sk->sleep, &ctrl_wait);
++ while (timeo && ctrl_sk->state != BT_CLOSED) {
++ set_current_state(TASK_INTERRUPTIBLE);
++ timeo = schedule_timeout(timeo);
++ }
++ set_current_state(TASK_RUNNING);
++ remove_wait_queue(ctrl_sk->sleep, &ctrl_wait);
++ timeo = HZ;
++ }
++
++ fput(session->ctrl_sock->file);
++
++ init_waitqueue_entry(&intr_wait, current);
++ add_wait_queue(intr_sk->sleep, &intr_wait);
++ while (timeo && intr_sk->state != BT_CLOSED) {
++ set_current_state(TASK_INTERRUPTIBLE);
++ timeo = schedule_timeout(timeo);
++ }
++ set_current_state(TASK_RUNNING);
++ remove_wait_queue(intr_sk->sleep, &intr_wait);
++
++ fput(session->intr_sock->file);
++
++ __hidp_unlink_session(session);
++
++ if (session->input) {
++ input_unregister_device(session->input);
++ kfree(session->input);
++ }
++
++ up_write(&hidp_session_sem);
++
++ kfree(session);
++ return 0;
++}
++
++static inline void hidp_setup_input(struct hidp_session *session, struct hidp_connadd_req *req)
++{
++ struct input_dev *input = session->input;
++ int i;
++
++ input->private = session;
++
++ input->idbus = BUS_BLUETOOTH;
++ input->idvendor = req->vendor;
++ input->idproduct = req->product;
++ input->idversion = req->version;
++
++ if (req->subclass & 0x40) {
++ set_bit(EV_KEY, input->evbit);
++ set_bit(EV_LED, input->evbit);
++ set_bit(EV_REP, input->evbit);
++
++ set_bit(LED_NUML, input->ledbit);
++ set_bit(LED_CAPSL, input->ledbit);
++ set_bit(LED_SCROLLL, input->ledbit);
++ set_bit(LED_COMPOSE, input->ledbit);
++ set_bit(LED_KANA, input->ledbit);
++
++ for (i = 0; i < sizeof(hidp_keycode); i++)
++ set_bit(hidp_keycode[i], input->keybit);
++ clear_bit(0, input->keybit);
++ }
++
++ if (req->subclass & 0x80) {
++ input->evbit[0] = BIT(EV_KEY) | BIT(EV_REL);
++ input->keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE);
++ input->relbit[0] = BIT(REL_X) | BIT(REL_Y);
++ input->keybit[LONG(BTN_MOUSE)] |= BIT(BTN_SIDE) | BIT(BTN_EXTRA);
++ input->relbit[0] |= BIT(REL_WHEEL);
++ }
++
++ input->event = hidp_input_event;
++
++ input_register_device(input);
++}
++
++int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, struct socket *intr_sock)
++{
++ struct hidp_session *session, *s;
++ int err;
++
++ BT_DBG("");
++
++ if (bacmp(&bluez_pi(ctrl_sock->sk)->src, &bluez_pi(intr_sock->sk)->src) ||
++ bacmp(&bluez_pi(ctrl_sock->sk)->dst, &bluez_pi(intr_sock->sk)->dst))
++ return -ENOTUNIQ;
++
++ session = kmalloc(sizeof(struct hidp_session), GFP_KERNEL);
++ if (!session)
++ return -ENOMEM;
++ memset(session, 0, sizeof(struct hidp_session));
++
++ session->input = kmalloc(sizeof(struct input_dev), GFP_KERNEL);
++ if (!session->input) {
++ kfree(session);
++ return -ENOMEM;
++ }
++ memset(session->input, 0, sizeof(struct input_dev));
++
++ down_write(&hidp_session_sem);
++
++ s = __hidp_get_session(&bluez_pi(ctrl_sock->sk)->dst);
++ if (s && s->state == BT_CONNECTED) {
++ err = -EEXIST;
++ goto failed;
++ }
++
++ bacpy(&session->bdaddr, &bluez_pi(ctrl_sock->sk)->dst);
++
++ session->ctrl_mtu = min_t(uint, l2cap_pi(ctrl_sock->sk)->omtu, l2cap_pi(ctrl_sock->sk)->imtu);
++ session->intr_mtu = min_t(uint, l2cap_pi(intr_sock->sk)->omtu, l2cap_pi(intr_sock->sk)->imtu);
++
++ BT_DBG("ctrl mtu %d intr mtu %d", session->ctrl_mtu, session->intr_mtu);
++
++ session->ctrl_sock = ctrl_sock;
++ session->intr_sock = intr_sock;
++ session->state = BT_CONNECTED;
++
++ init_timer(&session->timer);
++
++ session->timer.function = hidp_idle_timeout;
++ session->timer.data = (unsigned long) session;
++
++ skb_queue_head_init(&session->ctrl_transmit);
++ skb_queue_head_init(&session->intr_transmit);
++
++ session->flags = req->flags & (1 << HIDP_BLUETOOTH_VENDOR_ID);
++ session->idle_to = req->idle_to;
++
++ if (session->input)
++ hidp_setup_input(session, req);
++
++ __hidp_link_session(session);
++
++ hidp_set_timer(session);
++
++ err = kernel_thread(hidp_session, session, CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
++ if (err < 0)
++ goto unlink;
++
++ if (session->input) {
++ hidp_send_message(session, 0x70);
++ session->flags |= (1 << HIDP_BOOT_PROTOCOL_MODE);
++
++ session->leds = 0xff;
++ hidp_input_event(session->input, EV_LED, 0, 0);
++ }
++
++ up_write(&hidp_session_sem);
++ return 0;
++
++unlink:
++ hidp_del_timer(session);
++
++ __hidp_unlink_session(session);
++
++ if (session->input)
++ input_unregister_device(session->input);
++
++failed:
++ up_write(&hidp_session_sem);
++
++ if (session->input)
++ kfree(session->input);
++
++ kfree(session);
++ return err;
++}
++
++int hidp_del_connection(struct hidp_conndel_req *req)
++{
++ struct hidp_session *session;
++ int err = 0;
++
++ BT_DBG("");
++
++ down_read(&hidp_session_sem);
++
++ session = __hidp_get_session(&req->bdaddr);
++ if (session) {
++ if (req->flags & (1 << HIDP_VIRTUAL_CABLE_UNPLUG)) {
++ hidp_send_message(session, 0x15);
++ } else {
++ /* Flush the transmit queues */
++ skb_queue_purge(&session->ctrl_transmit);
++ skb_queue_purge(&session->intr_transmit);
++
++ /* Kill session thread */
++ atomic_inc(&session->terminate);
++ hidp_schedule(session);
++ }
++ } else
++ err = -ENOENT;
++
++ up_read(&hidp_session_sem);
++ return err;
++}
++
++int hidp_get_connlist(struct hidp_connlist_req *req)
++{
++ struct list_head *p;
++ int err = 0, n = 0;
++
++ BT_DBG("");
++
++ down_read(&hidp_session_sem);
++
++ list_for_each(p, &hidp_session_list) {
++ struct hidp_session *session;
++ struct hidp_conninfo ci;
++
++ session = list_entry(p, struct hidp_session, list);
++
++ __hidp_copy_session(session, &ci);
++
++ if (copy_to_user(req->ci, &ci, sizeof(ci))) {
++ err = -EFAULT;
++ break;
++ }
++
++ if (++n >= req->cnum)
++ break;
++
++ req->ci++;
++ }
++ req->cnum = n;
++
++ up_read(&hidp_session_sem);
++ return err;
++}
++
++int hidp_get_conninfo(struct hidp_conninfo *ci)
++{
++ struct hidp_session *session;
++ int err = 0;
++
++ down_read(&hidp_session_sem);
++
++ session = __hidp_get_session(&ci->bdaddr);
++ if (session)
++ __hidp_copy_session(session, ci);
++ else
++ err = -ENOENT;
++
++ up_read(&hidp_session_sem);
++ return err;
++}
++
++static int __init hidp_init(void)
++{
++ l2cap_load();
++
++ hidp_init_sockets();
++
++ BT_INFO("BlueZ HIDP ver %s", VERSION);
++ BT_INFO("Copyright (C) 2003-2004 Marcel Holtmann <marcel@holtmann.org>");
++
++ return 0;
++}
++
++static void __exit hidp_exit(void)
++{
++ hidp_cleanup_sockets();
++}
++
++module_init(hidp_init);
++module_exit(hidp_exit);
++
++MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
++MODULE_DESCRIPTION("Bluetooth HIDP ver " VERSION);
++MODULE_LICENSE("GPL");
+diff -urN linux-2.4.18/net/bluetooth/hidp/hidp.h linux-2.4.18-mh15/net/bluetooth/hidp/hidp.h
+--- linux-2.4.18/net/bluetooth/hidp/hidp.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/net/bluetooth/hidp/hidp.h 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,122 @@
++/*
++ HIDP implementation for Linux Bluetooth stack (BlueZ).
++ Copyright (C) 2003-2004 Marcel Holtmann <marcel@holtmann.org>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License version 2 as
++ published by the Free Software Foundation;
++
++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++
++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
++ SOFTWARE IS DISCLAIMED.
++*/
++
++#ifndef __HIDP_H
++#define __HIDP_H
++
++#include <linux/types.h>
++#include <net/bluetooth/bluetooth.h>
++
++/* HIDP ioctl defines */
++#define HIDPCONNADD _IOW('H', 200, int)
++#define HIDPCONNDEL _IOW('H', 201, int)
++#define HIDPGETCONNLIST _IOR('H', 210, int)
++#define HIDPGETCONNINFO _IOR('H', 211, int)
++
++#define HIDP_VIRTUAL_CABLE_UNPLUG 0
++#define HIDP_BOOT_PROTOCOL_MODE 1
++#define HIDP_BLUETOOTH_VENDOR_ID 9
++
++struct hidp_connadd_req {
++ int ctrl_sock; // Connected control socket
++ int intr_sock; // Connteted interrupt socket
++ __u16 parser;
++ __u16 rd_size;
++ __u8 *rd_data;
++ __u8 country;
++ __u8 subclass;
++ __u16 vendor;
++ __u16 product;
++ __u16 version;
++ __u32 flags;
++ __u32 idle_to;
++ char name[128];
++};
++
++struct hidp_conndel_req {
++ bdaddr_t bdaddr;
++ __u32 flags;
++};
++
++struct hidp_conninfo {
++ bdaddr_t bdaddr;
++ __u32 flags;
++ __u16 state;
++ __u16 vendor;
++ __u16 product;
++ __u16 version;
++ char name[128];
++};
++
++struct hidp_connlist_req {
++ __u32 cnum;
++ struct hidp_conninfo *ci;
++};
++
++int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, struct socket *intr_sock);
++int hidp_del_connection(struct hidp_conndel_req *req);
++int hidp_get_connlist(struct hidp_connlist_req *req);
++int hidp_get_conninfo(struct hidp_conninfo *ci);
++
++/* HIDP session defines */
++struct hidp_session {
++ struct list_head list;
++
++ struct socket *ctrl_sock;
++ struct socket *intr_sock;
++
++ bdaddr_t bdaddr;
++
++ unsigned long state;
++ unsigned long flags;
++ unsigned long idle_to;
++
++ uint ctrl_mtu;
++ uint intr_mtu;
++
++ atomic_t terminate;
++
++ unsigned char keys[8];
++ unsigned char leds;
++
++ struct input_dev *input;
++
++ struct timer_list timer;
++
++ struct sk_buff_head ctrl_transmit;
++ struct sk_buff_head intr_transmit;
++};
++
++static inline void hidp_schedule(struct hidp_session *session)
++{
++ struct sock *ctrl_sk = session->ctrl_sock->sk;
++ struct sock *intr_sk = session->intr_sock->sk;
++
++ wake_up_interruptible(ctrl_sk->sleep);
++ wake_up_interruptible(intr_sk->sleep);
++}
++
++/* HIDP init defines */
++extern int __init hidp_init_sockets(void);
++extern void __exit hidp_cleanup_sockets(void);
++
++#endif /* __HIDP_H */
+diff -urN linux-2.4.18/net/bluetooth/hidp/Makefile linux-2.4.18-mh15/net/bluetooth/hidp/Makefile
+--- linux-2.4.18/net/bluetooth/hidp/Makefile 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/net/bluetooth/hidp/Makefile 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,10 @@
++#
++# Makefile for the Linux Bluetooth HIDP layer
++#
++
++O_TARGET := hidp.o
++
++obj-y := core.o sock.o
++obj-m += $(O_TARGET)
++
++include $(TOPDIR)/Rules.make
+diff -urN linux-2.4.18/net/bluetooth/hidp/sock.c linux-2.4.18-mh15/net/bluetooth/hidp/sock.c
+--- linux-2.4.18/net/bluetooth/hidp/sock.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/net/bluetooth/hidp/sock.c 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,212 @@
++/*
++ HIDP implementation for Linux Bluetooth stack (BlueZ).
++ Copyright (C) 2003-2004 Marcel Holtmann <marcel@holtmann.org>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License version 2 as
++ published by the Free Software Foundation;
++
++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++
++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
++ SOFTWARE IS DISCLAIMED.
++*/
++
++#include <linux/config.h>
++#include <linux/module.h>
++
++#include <linux/types.h>
++#include <linux/errno.h>
++#include <linux/kernel.h>
++#include <linux/major.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/poll.h>
++#include <linux/fcntl.h>
++#include <linux/skbuff.h>
++#include <linux/socket.h>
++#include <linux/ioctl.h>
++#include <linux/file.h>
++#include <linux/init.h>
++#include <net/sock.h>
++
++#include "hidp.h"
++
++#ifndef CONFIG_BT_HIDP_DEBUG
++#undef BT_DBG
++#define BT_DBG(D...)
++#endif
++
++static int hidp_sock_release(struct socket *sock)
++{
++ struct sock *sk = sock->sk;
++
++ BT_DBG("sock %p sk %p", sock, sk);
++
++ if (!sk)
++ return 0;
++
++ sock_orphan(sk);
++ sock_put(sk);
++
++ MOD_DEC_USE_COUNT;
++ return 0;
++}
++
++static int hidp_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
++{
++ struct hidp_connadd_req ca;
++ struct hidp_conndel_req cd;
++ struct hidp_connlist_req cl;
++ struct hidp_conninfo ci;
++ struct socket *csock;
++ struct socket *isock;
++ int err;
++
++ BT_DBG("cmd %x arg %lx", cmd, arg);
++
++ switch (cmd) {
++ case HIDPCONNADD:
++ if (!capable(CAP_NET_ADMIN))
++ return -EACCES;
++
++ if (copy_from_user(&ca, (void *) arg, sizeof(ca)))
++ return -EFAULT;
++
++ csock = sockfd_lookup(ca.ctrl_sock, &err);
++ if (!csock)
++ return err;
++
++ isock = sockfd_lookup(ca.intr_sock, &err);
++ if (!isock) {
++ fput(csock->file);
++ return err;
++ }
++
++ if (csock->sk->state != BT_CONNECTED || isock->sk->state != BT_CONNECTED) {
++ fput(csock->file);
++ fput(isock->file);
++ return -EBADFD;
++ }
++
++ err = hidp_add_connection(&ca, csock, isock);
++ if (!err) {
++ if (copy_to_user((void *) arg, &ca, sizeof(ca)))
++ err = -EFAULT;
++ } else {
++ fput(csock->file);
++ fput(isock->file);
++ }
++
++ return err;
++
++ case HIDPCONNDEL:
++ if (!capable(CAP_NET_ADMIN))
++ return -EACCES;
++
++ if (copy_from_user(&cd, (void *) arg, sizeof(cd)))
++ return -EFAULT;
++
++ return hidp_del_connection(&cd);
++
++ case HIDPGETCONNLIST:
++ if (copy_from_user(&cl, (void *) arg, sizeof(cl)))
++ return -EFAULT;
++
++ if (cl.cnum <= 0)
++ return -EINVAL;
++
++ err = hidp_get_connlist(&cl);
++ if (!err && copy_to_user((void *) arg, &cl, sizeof(cl)))
++ return -EFAULT;
++
++ return err;
++
++ case HIDPGETCONNINFO:
++ if (copy_from_user(&ci, (void *) arg, sizeof(ci)))
++ return -EFAULT;
++
++ err = hidp_get_conninfo(&ci);
++ if (!err && copy_to_user((void *) arg, &ci, sizeof(ci)))
++ return -EFAULT;
++
++ return err;
++ }
++
++ return -EINVAL;
++}
++
++static struct proto_ops hidp_sock_ops = {
++ family: PF_BLUETOOTH,
++ release: hidp_sock_release,
++ ioctl: hidp_sock_ioctl,
++ bind: sock_no_bind,
++ getname: sock_no_getname,
++ sendmsg: sock_no_sendmsg,
++ recvmsg: sock_no_recvmsg,
++ poll: sock_no_poll,
++ listen: sock_no_listen,
++ shutdown: sock_no_shutdown,
++ setsockopt: sock_no_setsockopt,
++ getsockopt: sock_no_getsockopt,
++ connect: sock_no_connect,
++ socketpair: sock_no_socketpair,
++ accept: sock_no_accept,
++ mmap: sock_no_mmap
++};
++
++static int hidp_sock_create(struct socket *sock, int protocol)
++{
++ struct sock *sk;
++
++ BT_DBG("sock %p", sock);
++
++ if (sock->type != SOCK_RAW)
++ return -ESOCKTNOSUPPORT;
++
++ sock->ops = &hidp_sock_ops;
++
++ if (!(sk = sk_alloc(PF_BLUETOOTH, GFP_KERNEL, 1)))
++ return -ENOMEM;
++
++ MOD_INC_USE_COUNT;
++
++ sock->state = SS_UNCONNECTED;
++ sock_init_data(sock, sk);
++
++ sk->destruct = NULL;
++ sk->protocol = protocol;
++
++ return 0;
++}
++
++static struct net_proto_family hidp_sock_family_ops = {
++ family: PF_BLUETOOTH,
++ create: hidp_sock_create
++};
++
++int __init hidp_init_sockets(void)
++{
++ int err;
++
++ if ((err = bluez_sock_register(BTPROTO_HIDP, &hidp_sock_family_ops)))
++ BT_ERR("Can't register HIDP socket layer (%d)", err);
++
++ return err;
++}
++
++void __exit hidp_cleanup_sockets(void)
++{
++ int err;
++
++ if ((err = bluez_sock_unregister(BTPROTO_HIDP)))
++ BT_ERR("Can't unregister HIDP socket layer (%d)", err);
++}
+diff -urN linux-2.4.18/net/bluetooth/l2cap.c linux-2.4.18-mh15/net/bluetooth/l2cap.c
+--- linux-2.4.18/net/bluetooth/l2cap.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/net/bluetooth/l2cap.c 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,2222 @@
++/*
++ BlueZ - Bluetooth protocol stack for Linux
++ Copyright (C) 2000-2001 Qualcomm Incorporated
++
++ Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License version 2 as
++ published by the Free Software Foundation;
++
++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++
++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
++ SOFTWARE IS DISCLAIMED.
++*/
++
++/*
++ * BlueZ L2CAP core and sockets.
++ *
++ * $Id$
++ */
++#define VERSION "2.3"
++
++#include <linux/config.h>
++#include <linux/module.h>
++
++#include <linux/types.h>
++#include <linux/errno.h>
++#include <linux/kernel.h>
++#include <linux/major.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/poll.h>
++#include <linux/fcntl.h>
++#include <linux/init.h>
++#include <linux/skbuff.h>
++#include <linux/interrupt.h>
++#include <linux/socket.h>
++#include <linux/skbuff.h>
++#include <linux/proc_fs.h>
++#include <linux/list.h>
++#include <net/sock.h>
++
++#include <asm/system.h>
++#include <asm/uaccess.h>
++#include <asm/unaligned.h>
++
++#include <net/bluetooth/bluetooth.h>
++#include <net/bluetooth/hci_core.h>
++#include <net/bluetooth/l2cap.h>
++
++#ifndef L2CAP_DEBUG
++#undef BT_DBG
++#define BT_DBG( A... )
++#endif
++
++static struct proto_ops l2cap_sock_ops;
++
++struct bluez_sock_list l2cap_sk_list = {
++ lock: RW_LOCK_UNLOCKED
++};
++
++static int l2cap_conn_del(struct hci_conn *conn, int err);
++
++static inline void l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct sock *parent);
++static void l2cap_chan_del(struct sock *sk, int err);
++static int l2cap_chan_send(struct sock *sk, struct msghdr *msg, int len);
++
++static void __l2cap_sock_close(struct sock *sk, int reason);
++static void l2cap_sock_close(struct sock *sk);
++static void l2cap_sock_kill(struct sock *sk);
++
++static int l2cap_send_req(struct l2cap_conn *conn, __u8 code, __u16 len, void *data);
++static int l2cap_send_rsp(struct l2cap_conn *conn, __u8 ident, __u8 code, __u16 len, void *data);
++
++/* ----- L2CAP timers ------ */
++static void l2cap_sock_timeout(unsigned long arg)
++{
++ struct sock *sk = (struct sock *) arg;
++
++ BT_DBG("sock %p state %d", sk, sk->state);
++
++ bh_lock_sock(sk);
++ __l2cap_sock_close(sk, ETIMEDOUT);
++ bh_unlock_sock(sk);
++
++ l2cap_sock_kill(sk);
++ sock_put(sk);
++}
++
++static void l2cap_sock_set_timer(struct sock *sk, long timeout)
++{
++ BT_DBG("sk %p state %d timeout %ld", sk, sk->state, timeout);
++
++ if (!mod_timer(&sk->timer, jiffies + timeout))
++ sock_hold(sk);
++}
++
++static void l2cap_sock_clear_timer(struct sock *sk)
++{
++ BT_DBG("sock %p state %d", sk, sk->state);
++
++ if (timer_pending(&sk->timer) && del_timer(&sk->timer))
++ __sock_put(sk);
++}
++
++static void l2cap_sock_init_timer(struct sock *sk)
++{
++ init_timer(&sk->timer);
++ sk->timer.function = l2cap_sock_timeout;
++ sk->timer.data = (unsigned long)sk;
++}
++
++/* -------- L2CAP connections --------- */
++static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, __u8 status)
++{
++ struct l2cap_conn *conn;
++
++ if ((conn = hcon->l2cap_data))
++ return conn;
++
++ if (status)
++ return conn;
++
++ if (!(conn = kmalloc(sizeof(struct l2cap_conn), GFP_ATOMIC)))
++ return NULL;
++ memset(conn, 0, sizeof(struct l2cap_conn));
++
++ hcon->l2cap_data = conn;
++ conn->hcon = hcon;
++
++ conn->mtu = hcon->hdev->acl_mtu;
++ conn->src = &hcon->hdev->bdaddr;
++ conn->dst = &hcon->dst;
++
++ spin_lock_init(&conn->lock);
++ conn->chan_list.lock = RW_LOCK_UNLOCKED;
++
++ BT_DBG("hcon %p conn %p", hcon, conn);
++
++ MOD_INC_USE_COUNT;
++ return conn;
++}
++
++static int l2cap_conn_del(struct hci_conn *hcon, int err)
++{
++ struct l2cap_conn *conn;
++ struct sock *sk;
++
++ if (!(conn = hcon->l2cap_data))
++ return 0;
++
++ BT_DBG("hcon %p conn %p, err %d", hcon, conn, err);
++
++ if (conn->rx_skb)
++ kfree_skb(conn->rx_skb);
++
++ /* Kill channels */
++ while ((sk = conn->chan_list.head)) {
++ bh_lock_sock(sk);
++ l2cap_chan_del(sk, err);
++ bh_unlock_sock(sk);
++ l2cap_sock_kill(sk);
++ }
++
++ hcon->l2cap_data = NULL;
++ kfree(conn);
++
++ MOD_DEC_USE_COUNT;
++ return 0;
++}
++
++/* -------- Socket interface ---------- */
++static struct sock *__l2cap_get_sock_by_addr(__u16 psm, bdaddr_t *src)
++{
++ struct sock *sk;
++ for (sk = l2cap_sk_list.head; sk; sk = sk->next) {
++ if (sk->sport == psm && !bacmp(&bluez_pi(sk)->src, src))
++ break;
++ }
++ return sk;
++}
++
++/* Find socket with psm and source bdaddr.
++ * Returns closest match.
++ */
++static struct sock *__l2cap_get_sock_by_psm(int state, __u16 psm, bdaddr_t *src)
++{
++ struct sock *sk, *sk1 = NULL;
++
++ for (sk = l2cap_sk_list.head; sk; sk = sk->next) {
++ if (state && sk->state != state)
++ continue;
++
++ if (l2cap_pi(sk)->psm == psm) {
++ /* Exact match. */
++ if (!bacmp(&bluez_pi(sk)->src, src))
++ break;
++
++ /* Closest match */
++ if (!bacmp(&bluez_pi(sk)->src, BDADDR_ANY))
++ sk1 = sk;
++ }
++ }
++ return sk ? sk : sk1;
++}
++
++/* Find socket with given address (psm, src).
++ * Returns locked socket */
++static inline struct sock *l2cap_get_sock_by_psm(int state, __u16 psm, bdaddr_t *src)
++{
++ struct sock *s;
++ read_lock(&l2cap_sk_list.lock);
++ s = __l2cap_get_sock_by_psm(state, psm, src);
++ if (s) bh_lock_sock(s);
++ read_unlock(&l2cap_sk_list.lock);
++ return s;
++}
++
++static void l2cap_sock_destruct(struct sock *sk)
++{
++ BT_DBG("sk %p", sk);
++
++ skb_queue_purge(&sk->receive_queue);
++ skb_queue_purge(&sk->write_queue);
++
++ MOD_DEC_USE_COUNT;
++}
++
++static void l2cap_sock_cleanup_listen(struct sock *parent)
++{
++ struct sock *sk;
++
++ BT_DBG("parent %p", parent);
++
++ /* Close not yet accepted channels */
++ while ((sk = bluez_accept_dequeue(parent, NULL)))
++ l2cap_sock_close(sk);
++
++ parent->state = BT_CLOSED;
++ parent->zapped = 1;
++}
++
++/* Kill socket (only if zapped and orphan)
++ * Must be called on unlocked socket.
++ */
++static void l2cap_sock_kill(struct sock *sk)
++{
++ if (!sk->zapped || sk->socket)
++ return;
++
++ BT_DBG("sk %p state %d", sk, sk->state);
++
++ /* Kill poor orphan */
++ bluez_sock_unlink(&l2cap_sk_list, sk);
++ sk->dead = 1;
++ sock_put(sk);
++}
++
++/* Close socket.
++ */
++static void __l2cap_sock_close(struct sock *sk, int reason)
++{
++ BT_DBG("sk %p state %d socket %p", sk, sk->state, sk->socket);
++
++ switch (sk->state) {
++ case BT_LISTEN:
++ l2cap_sock_cleanup_listen(sk);
++ break;
++
++ case BT_CONNECTED:
++ case BT_CONFIG:
++ case BT_CONNECT2:
++ if (sk->type == SOCK_SEQPACKET) {
++ struct l2cap_conn *conn = l2cap_pi(sk)->conn;
++ l2cap_disconn_req req;
++
++ sk->state = BT_DISCONN;
++ l2cap_sock_set_timer(sk, sk->sndtimeo);
++
++ req.dcid = __cpu_to_le16(l2cap_pi(sk)->dcid);
++ req.scid = __cpu_to_le16(l2cap_pi(sk)->scid);
++ l2cap_send_req(conn, L2CAP_DISCONN_REQ, L2CAP_DISCONN_REQ_SIZE, &req);
++ } else {
++ l2cap_chan_del(sk, reason);
++ }
++ break;
++
++ case BT_CONNECT:
++ case BT_DISCONN:
++ l2cap_chan_del(sk, reason);
++ break;
++
++ default:
++ sk->zapped = 1;
++ break;
++ };
++}
++
++/* Must be called on unlocked socket. */
++static void l2cap_sock_close(struct sock *sk)
++{
++ l2cap_sock_clear_timer(sk);
++ lock_sock(sk);
++ __l2cap_sock_close(sk, ECONNRESET);
++ release_sock(sk);
++ l2cap_sock_kill(sk);
++}
++
++static void l2cap_sock_init(struct sock *sk, struct sock *parent)
++{
++ struct l2cap_pinfo *pi = l2cap_pi(sk);
++
++ BT_DBG("sk %p", sk);
++
++ if (parent) {
++ sk->type = parent->type;
++ pi->imtu = l2cap_pi(parent)->imtu;
++ pi->omtu = l2cap_pi(parent)->omtu;
++ pi->link_mode = l2cap_pi(parent)->link_mode;
++ } else {
++ pi->imtu = L2CAP_DEFAULT_MTU;
++ pi->omtu = 0;
++ pi->link_mode = 0;
++ }
++
++ /* Default config options */
++ pi->conf_mtu = L2CAP_DEFAULT_MTU;
++ pi->flush_to = L2CAP_DEFAULT_FLUSH_TO;
++}
++
++static struct sock *l2cap_sock_alloc(struct socket *sock, int proto, int prio)
++{
++ struct sock *sk;
++
++ if (!(sk = sk_alloc(PF_BLUETOOTH, prio, 1)))
++ return NULL;
++
++ bluez_sock_init(sock, sk);
++
++ sk->zapped = 0;
++
++ sk->destruct = l2cap_sock_destruct;
++ sk->sndtimeo = L2CAP_CONN_TIMEOUT;
++
++ sk->protocol = proto;
++ sk->state = BT_OPEN;
++
++ l2cap_sock_init_timer(sk);
++
++ bluez_sock_link(&l2cap_sk_list, sk);
++
++ MOD_INC_USE_COUNT;
++ return sk;
++}
++
++static int l2cap_sock_create(struct socket *sock, int protocol)
++{
++ struct sock *sk;
++
++ BT_DBG("sock %p", sock);
++
++ sock->state = SS_UNCONNECTED;
++
++ if (sock->type != SOCK_SEQPACKET && sock->type != SOCK_DGRAM && sock->type != SOCK_RAW)
++ return -ESOCKTNOSUPPORT;
++
++ if (sock->type == SOCK_RAW && !capable(CAP_NET_RAW))
++ return -EPERM;
++
++ sock->ops = &l2cap_sock_ops;
++
++ if (!(sk = l2cap_sock_alloc(sock, protocol, GFP_KERNEL)))
++ return -ENOMEM;
++
++ l2cap_sock_init(sk, NULL);
++ return 0;
++}
++
++static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len)
++{
++ struct sockaddr_l2 *la = (struct sockaddr_l2 *) addr;
++ struct sock *sk = sock->sk;
++ int err = 0;
++
++ BT_DBG("sk %p, %s %d", sk, batostr(&la->l2_bdaddr), la->l2_psm);
++
++ if (!addr || addr->sa_family != AF_BLUETOOTH)
++ return -EINVAL;
++
++ lock_sock(sk);
++
++ if (sk->state != BT_OPEN) {
++ err = -EBADFD;
++ goto done;
++ }
++
++ write_lock_bh(&l2cap_sk_list.lock);
++ if (la->l2_psm && __l2cap_get_sock_by_addr(la->l2_psm, &la->l2_bdaddr)) {
++ err = -EADDRINUSE;
++ } else {
++ /* Save source address */
++ bacpy(&bluez_pi(sk)->src, &la->l2_bdaddr);
++ l2cap_pi(sk)->psm = la->l2_psm;
++ sk->sport = la->l2_psm;
++ sk->state = BT_BOUND;
++ }
++ write_unlock_bh(&l2cap_sk_list.lock);
++
++done:
++ release_sock(sk);
++ return err;
++}
++
++static int l2cap_do_connect(struct sock *sk)
++{
++ bdaddr_t *src = &bluez_pi(sk)->src;
++ bdaddr_t *dst = &bluez_pi(sk)->dst;
++ struct l2cap_conn *conn;
++ struct hci_conn *hcon;
++ struct hci_dev *hdev;
++ int err = 0;
++
++ BT_DBG("%s -> %s psm 0x%2.2x", batostr(src), batostr(dst), l2cap_pi(sk)->psm);
++
++ if (!(hdev = hci_get_route(dst, src)))
++ return -EHOSTUNREACH;
++
++ hci_dev_lock_bh(hdev);
++
++ err = -ENOMEM;
++
++ hcon = hci_connect(hdev, ACL_LINK, dst);
++ if (!hcon)
++ goto done;
++
++ conn = l2cap_conn_add(hcon, 0);
++ if (!conn) {
++ hci_conn_put(hcon);
++ goto done;
++ }
++
++ err = 0;
++
++ /* Update source addr of the socket */
++ bacpy(src, conn->src);
++
++ l2cap_chan_add(conn, sk, NULL);
++
++ sk->state = BT_CONNECT;
++ l2cap_sock_set_timer(sk, sk->sndtimeo);
++
++ if (hcon->state == BT_CONNECTED) {
++ if (sk->type == SOCK_SEQPACKET) {
++ l2cap_conn_req req;
++ req.scid = __cpu_to_le16(l2cap_pi(sk)->scid);
++ req.psm = l2cap_pi(sk)->psm;
++ l2cap_send_req(conn, L2CAP_CONN_REQ, L2CAP_CONN_REQ_SIZE, &req);
++ } else {
++ l2cap_sock_clear_timer(sk);
++ sk->state = BT_CONNECTED;
++ }
++ }
++
++done:
++ hci_dev_unlock_bh(hdev);
++ hci_dev_put(hdev);
++ return err;
++}
++
++static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int alen, int flags)
++{
++ struct sockaddr_l2 *la = (struct sockaddr_l2 *) addr;
++ struct sock *sk = sock->sk;
++ int err = 0;
++
++ lock_sock(sk);
++
++ BT_DBG("sk %p", sk);
++
++ if (addr->sa_family != AF_BLUETOOTH || alen < sizeof(struct sockaddr_l2)) {
++ err = -EINVAL;
++ goto done;
++ }
++
++ if (sk->type == SOCK_SEQPACKET && !la->l2_psm) {
++ err = -EINVAL;
++ goto done;
++ }
++
++ switch(sk->state) {
++ case BT_CONNECT:
++ case BT_CONNECT2:
++ case BT_CONFIG:
++ /* Already connecting */
++ goto wait;
++
++ case BT_CONNECTED:
++ /* Already connected */
++ goto done;
++
++ case BT_OPEN:
++ case BT_BOUND:
++ /* Can connect */
++ break;
++
++ default:
++ err = -EBADFD;
++ goto done;
++ }
++
++ /* Set destination address and psm */
++ bacpy(&bluez_pi(sk)->dst, &la->l2_bdaddr);
++ l2cap_pi(sk)->psm = la->l2_psm;
++
++ if ((err = l2cap_do_connect(sk)))
++ goto done;
++
++wait:
++ err = bluez_sock_wait_state(sk, BT_CONNECTED,
++ sock_sndtimeo(sk, flags & O_NONBLOCK));
++
++done:
++ release_sock(sk);
++ return err;
++}
++
++int l2cap_sock_listen(struct socket *sock, int backlog)
++{
++ struct sock *sk = sock->sk;
++ int err = 0;
++
++ BT_DBG("sk %p backlog %d", sk, backlog);
++
++ lock_sock(sk);
++
++ if (sk->state != BT_BOUND || sock->type != SOCK_SEQPACKET) {
++ err = -EBADFD;
++ goto done;
++ }
++
++ if (!l2cap_pi(sk)->psm) {
++ err = -EINVAL;
++ goto done;
++ }
++
++ sk->max_ack_backlog = backlog;
++ sk->ack_backlog = 0;
++ sk->state = BT_LISTEN;
++
++done:
++ release_sock(sk);
++ return err;
++}
++
++int l2cap_sock_accept(struct socket *sock, struct socket *newsock, int flags)
++{
++ DECLARE_WAITQUEUE(wait, current);
++ struct sock *sk = sock->sk, *nsk;
++ long timeo;
++ int err = 0;
++
++ lock_sock(sk);
++
++ if (sk->state != BT_LISTEN) {
++ err = -EBADFD;
++ goto done;
++ }
++
++ timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK);
++
++ BT_DBG("sk %p timeo %ld", sk, timeo);
++
++ /* Wait for an incoming connection. (wake-one). */
++ add_wait_queue_exclusive(sk->sleep, &wait);
++ while (!(nsk = bluez_accept_dequeue(sk, newsock))) {
++ set_current_state(TASK_INTERRUPTIBLE);
++ if (!timeo) {
++ err = -EAGAIN;
++ break;
++ }
++
++ release_sock(sk);
++ timeo = schedule_timeout(timeo);
++ lock_sock(sk);
++
++ if (sk->state != BT_LISTEN) {
++ err = -EBADFD;
++ break;
++ }
++
++ if (signal_pending(current)) {
++ err = sock_intr_errno(timeo);
++ break;
++ }
++ }
++ set_current_state(TASK_RUNNING);
++ remove_wait_queue(sk->sleep, &wait);
++
++ if (err)
++ goto done;
++
++ newsock->state = SS_CONNECTED;
++
++ BT_DBG("new socket %p", nsk);
++
++done:
++ release_sock(sk);
++ return err;
++}
++
++static int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr, int *len, int peer)
++{
++ struct sockaddr_l2 *la = (struct sockaddr_l2 *) addr;
++ struct sock *sk = sock->sk;
++
++ BT_DBG("sock %p, sk %p", sock, sk);
++
++ addr->sa_family = AF_BLUETOOTH;
++ *len = sizeof(struct sockaddr_l2);
++
++ if (peer)
++ bacpy(&la->l2_bdaddr, &bluez_pi(sk)->dst);
++ else
++ bacpy(&la->l2_bdaddr, &bluez_pi(sk)->src);
++
++ la->l2_psm = l2cap_pi(sk)->psm;
++ return 0;
++}
++
++static int l2cap_sock_sendmsg(struct socket *sock, struct msghdr *msg, int len, struct scm_cookie *scm)
++{
++ struct sock *sk = sock->sk;
++ int err = 0;
++
++ BT_DBG("sock %p, sk %p", sock, sk);
++
++ if (sk->err)
++ return sock_error(sk);
++
++ if (msg->msg_flags & MSG_OOB)
++ return -EOPNOTSUPP;
++
++ /* Check outgoing MTU */
++ if (len > l2cap_pi(sk)->omtu)
++ return -EINVAL;
++
++ lock_sock(sk);
++
++ if (sk->state == BT_CONNECTED)
++ err = l2cap_chan_send(sk, msg, len);
++ else
++ err = -ENOTCONN;
++
++ release_sock(sk);
++ return err;
++}
++
++static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen)
++{
++ struct sock *sk = sock->sk;
++ struct l2cap_options opts;
++ int err = 0, len;
++ __u32 opt;
++
++ BT_DBG("sk %p", sk);
++
++ lock_sock(sk);
++
++ switch (optname) {
++ case L2CAP_OPTIONS:
++ len = MIN(sizeof(opts), optlen);
++ if (copy_from_user((char *)&opts, optval, len)) {
++ err = -EFAULT;
++ break;
++ }
++ l2cap_pi(sk)->imtu = opts.imtu;
++ l2cap_pi(sk)->omtu = opts.omtu;
++ break;
++
++ case L2CAP_LM:
++ if (get_user(opt, (__u32 *)optval)) {
++ err = -EFAULT;
++ break;
++ }
++
++ l2cap_pi(sk)->link_mode = opt;
++ break;
++
++ default:
++ err = -ENOPROTOOPT;
++ break;
++ }
++
++ release_sock(sk);
++ return err;
++}
++
++static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, char *optval, int *optlen)
++{
++ struct sock *sk = sock->sk;
++ struct l2cap_options opts;
++ struct l2cap_conninfo cinfo;
++ int len, err = 0;
++
++ if (get_user(len, optlen))
++ return -EFAULT;
++
++ lock_sock(sk);
++
++ switch (optname) {
++ case L2CAP_OPTIONS:
++ opts.imtu = l2cap_pi(sk)->imtu;
++ opts.omtu = l2cap_pi(sk)->omtu;
++ opts.flush_to = l2cap_pi(sk)->flush_to;
++
++ len = MIN(len, sizeof(opts));
++ if (copy_to_user(optval, (char *)&opts, len))
++ err = -EFAULT;
++
++ break;
++
++ case L2CAP_LM:
++ if (put_user(l2cap_pi(sk)->link_mode, (__u32 *)optval))
++ err = -EFAULT;
++ break;
++
++ case L2CAP_CONNINFO:
++ if (sk->state != BT_CONNECTED) {
++ err = -ENOTCONN;
++ break;
++ }
++
++ cinfo.hci_handle = l2cap_pi(sk)->conn->hcon->handle;
++
++ len = MIN(len, sizeof(cinfo));
++ if (copy_to_user(optval, (char *)&cinfo, len))
++ err = -EFAULT;
++
++ break;
++
++ default:
++ err = -ENOPROTOOPT;
++ break;
++ }
++
++ release_sock(sk);
++ return err;
++}
++
++static int l2cap_sock_shutdown(struct socket *sock, int how)
++{
++ struct sock *sk = sock->sk;
++ int err = 0;
++
++ BT_DBG("sock %p, sk %p", sock, sk);
++
++ if (!sk) return 0;
++
++ lock_sock(sk);
++ if (!sk->shutdown) {
++ sk->shutdown = SHUTDOWN_MASK;
++ l2cap_sock_clear_timer(sk);
++ __l2cap_sock_close(sk, 0);
++
++ if (sk->linger)
++ err = bluez_sock_wait_state(sk, BT_CLOSED, sk->lingertime);
++ }
++ release_sock(sk);
++ return err;
++}
++
++static int l2cap_sock_release(struct socket *sock)
++{
++ struct sock *sk = sock->sk;
++ int err;
++
++ BT_DBG("sock %p, sk %p", sock, sk);
++
++ if (!sk) return 0;
++
++ err = l2cap_sock_shutdown(sock, 2);
++
++ sock_orphan(sk);
++ l2cap_sock_kill(sk);
++ return err;
++}
++
++/* --------- L2CAP channels --------- */
++static struct sock * __l2cap_get_chan_by_dcid(struct l2cap_chan_list *l, __u16 cid)
++{
++ struct sock *s;
++ for (s = l->head; s; s = l2cap_pi(s)->next_c) {
++ if (l2cap_pi(s)->dcid == cid)
++ break;
++ }
++ return s;
++}
++
++static struct sock *__l2cap_get_chan_by_scid(struct l2cap_chan_list *l, __u16 cid)
++{
++ struct sock *s;
++ for (s = l->head; s; s = l2cap_pi(s)->next_c) {
++ if (l2cap_pi(s)->scid == cid)
++ break;
++ }
++ return s;
++}
++
++/* Find channel with given SCID.
++ * Returns locked socket */
++static inline struct sock *l2cap_get_chan_by_scid(struct l2cap_chan_list *l, __u16 cid)
++{
++ struct sock *s;
++ read_lock(&l->lock);
++ s = __l2cap_get_chan_by_scid(l, cid);
++ if (s) bh_lock_sock(s);
++ read_unlock(&l->lock);
++ return s;
++}
++
++static __u16 l2cap_alloc_cid(struct l2cap_chan_list *l)
++{
++ __u16 cid = 0x0040;
++
++ for (; cid < 0xffff; cid++) {
++ if(!__l2cap_get_chan_by_scid(l, cid))
++ return cid;
++ }
++
++ return 0;
++}
++
++static inline void __l2cap_chan_link(struct l2cap_chan_list *l, struct sock *sk)
++{
++ sock_hold(sk);
++
++ if (l->head)
++ l2cap_pi(l->head)->prev_c = sk;
++
++ l2cap_pi(sk)->next_c = l->head;
++ l2cap_pi(sk)->prev_c = NULL;
++ l->head = sk;
++}
++
++static inline void l2cap_chan_unlink(struct l2cap_chan_list *l, struct sock *sk)
++{
++ struct sock *next = l2cap_pi(sk)->next_c, *prev = l2cap_pi(sk)->prev_c;
++
++ write_lock(&l->lock);
++ if (sk == l->head)
++ l->head = next;
++
++ if (next)
++ l2cap_pi(next)->prev_c = prev;
++ if (prev)
++ l2cap_pi(prev)->next_c = next;
++ write_unlock(&l->lock);
++
++ __sock_put(sk);
++}
++
++static void __l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct sock *parent)
++{
++ struct l2cap_chan_list *l = &conn->chan_list;
++
++ BT_DBG("conn %p, psm 0x%2.2x, dcid 0x%4.4x", conn, l2cap_pi(sk)->psm, l2cap_pi(sk)->dcid);
++
++ l2cap_pi(sk)->conn = conn;
++
++ if (sk->type == SOCK_SEQPACKET) {
++ /* Alloc CID for connection-oriented socket */
++ l2cap_pi(sk)->scid = l2cap_alloc_cid(l);
++ } else if (sk->type == SOCK_DGRAM) {
++ /* Connectionless socket */
++ l2cap_pi(sk)->scid = 0x0002;
++ l2cap_pi(sk)->dcid = 0x0002;
++ l2cap_pi(sk)->omtu = L2CAP_DEFAULT_MTU;
++ } else {
++ /* Raw socket can send/recv signalling messages only */
++ l2cap_pi(sk)->scid = 0x0001;
++ l2cap_pi(sk)->dcid = 0x0001;
++ l2cap_pi(sk)->omtu = L2CAP_DEFAULT_MTU;
++ }
++
++ __l2cap_chan_link(l, sk);
++
++ if (parent)
++ bluez_accept_enqueue(parent, sk);
++}
++
++static inline void l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct sock *parent)
++{
++ struct l2cap_chan_list *l = &conn->chan_list;
++ write_lock(&l->lock);
++ __l2cap_chan_add(conn, sk, parent);
++ write_unlock(&l->lock);
++}
++
++/* Delete channel.
++ * Must be called on the locked socket. */
++static void l2cap_chan_del(struct sock *sk, int err)
++{
++ struct l2cap_conn *conn = l2cap_pi(sk)->conn;
++ struct sock *parent = bluez_pi(sk)->parent;
++
++ l2cap_sock_clear_timer(sk);
++
++ BT_DBG("sk %p, conn %p, err %d", sk, conn, err);
++
++ if (conn) {
++ /* Unlink from channel list */
++ l2cap_chan_unlink(&conn->chan_list, sk);
++ l2cap_pi(sk)->conn = NULL;
++ hci_conn_put(conn->hcon);
++ }
++
++ sk->state = BT_CLOSED;
++ sk->zapped = 1;
++
++ if (err)
++ sk->err = err;
++
++ if (parent)
++ parent->data_ready(parent, 0);
++ else
++ sk->state_change(sk);
++}
++
++static void l2cap_conn_ready(struct l2cap_conn *conn)
++{
++ struct l2cap_chan_list *l = &conn->chan_list;
++ struct sock *sk;
++
++ BT_DBG("conn %p", conn);
++
++ read_lock(&l->lock);
++
++ for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) {
++ bh_lock_sock(sk);
++
++ if (sk->type != SOCK_SEQPACKET) {
++ l2cap_sock_clear_timer(sk);
++ sk->state = BT_CONNECTED;
++ sk->state_change(sk);
++ } else if (sk->state == BT_CONNECT) {
++ l2cap_conn_req req;
++ req.scid = __cpu_to_le16(l2cap_pi(sk)->scid);
++ req.psm = l2cap_pi(sk)->psm;
++ l2cap_send_req(conn, L2CAP_CONN_REQ, L2CAP_CONN_REQ_SIZE, &req);
++ }
++
++ bh_unlock_sock(sk);
++ }
++
++ read_unlock(&l->lock);
++}
++
++/* Notify sockets that we cannot guaranty reliability anymore */
++static void l2cap_conn_unreliable(struct l2cap_conn *conn, int err)
++{
++ struct l2cap_chan_list *l = &conn->chan_list;
++ struct sock *sk;
++
++ BT_DBG("conn %p", conn);
++
++ read_lock(&l->lock);
++ for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) {
++ if (l2cap_pi(sk)->link_mode & L2CAP_LM_RELIABLE)
++ sk->err = err;
++ }
++ read_unlock(&l->lock);
++}
++
++static void l2cap_chan_ready(struct sock *sk)
++{
++ struct sock *parent = bluez_pi(sk)->parent;
++
++ BT_DBG("sk %p, parent %p", sk, parent);
++
++ l2cap_pi(sk)->conf_state = 0;
++ l2cap_sock_clear_timer(sk);
++
++ if (!parent) {
++ /* Outgoing channel.
++ * Wake up socket sleeping on connect.
++ */
++ sk->state = BT_CONNECTED;
++ sk->state_change(sk);
++ } else {
++ /* Incomming channel.
++ * Wake up socket sleeping on accept.
++ */
++ parent->data_ready(parent, 0);
++ }
++}
++
++/* Copy frame to all raw sockets on that connection */
++void l2cap_raw_recv(struct l2cap_conn *conn, struct sk_buff *skb)
++{
++ struct l2cap_chan_list *l = &conn->chan_list;
++ struct sk_buff *nskb;
++ struct sock * sk;
++
++ BT_DBG("conn %p", conn);
++
++ read_lock(&l->lock);
++ for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) {
++ if (sk->type != SOCK_RAW)
++ continue;
++
++ /* Don't send frame to the socket it came from */
++ if (skb->sk == sk)
++ continue;
++
++ if (!(nskb = skb_clone(skb, GFP_ATOMIC)))
++ continue;
++
++ if (sock_queue_rcv_skb(sk, nskb))
++ kfree_skb(nskb);
++ }
++ read_unlock(&l->lock);
++}
++
++static int l2cap_chan_send(struct sock *sk, struct msghdr *msg, int len)
++{
++ struct l2cap_conn *conn = l2cap_pi(sk)->conn;
++ struct sk_buff *skb, **frag;
++ int err, hlen, count, sent=0;
++ l2cap_hdr *lh;
++
++ BT_DBG("sk %p len %d", sk, len);
++
++ /* First fragment (with L2CAP header) */
++ if (sk->type == SOCK_DGRAM)
++ hlen = L2CAP_HDR_SIZE + 2;
++ else
++ hlen = L2CAP_HDR_SIZE;
++
++ count = MIN(conn->mtu - hlen, len);
++
++ skb = bluez_skb_send_alloc(sk, hlen + count,
++ msg->msg_flags & MSG_DONTWAIT, &err);
++ if (!skb)
++ return err;
++
++ /* Create L2CAP header */
++ lh = (l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE);
++ lh->cid = __cpu_to_le16(l2cap_pi(sk)->dcid);
++ lh->len = __cpu_to_le16(len + (hlen - L2CAP_HDR_SIZE));
++
++ if (sk->type == SOCK_DGRAM)
++ put_unaligned(l2cap_pi(sk)->psm, (__u16 *) skb_put(skb, 2));
++
++ if (memcpy_fromiovec(skb_put(skb, count), msg->msg_iov, count)) {
++ err = -EFAULT;
++ goto fail;
++ }
++
++ sent += count;
++ len -= count;
++
++ /* Continuation fragments (no L2CAP header) */
++ frag = &skb_shinfo(skb)->frag_list;
++ while (len) {
++ count = MIN(conn->mtu, len);
++
++ *frag = bluez_skb_send_alloc(sk, count, msg->msg_flags & MSG_DONTWAIT, &err);
++ if (!*frag)
++ goto fail;
++
++ if (memcpy_fromiovec(skb_put(*frag, count), msg->msg_iov, count)) {
++ err = -EFAULT;
++ goto fail;
++ }
++
++ sent += count;
++ len -= count;
++
++ frag = &(*frag)->next;
++ }
++
++ if ((err = hci_send_acl(conn->hcon, skb, 0)) < 0)
++ goto fail;
++
++ return sent;
++
++fail:
++ kfree_skb(skb);
++ return err;
++}
++
++/* --------- L2CAP signalling commands --------- */
++static inline __u8 l2cap_get_ident(struct l2cap_conn *conn)
++{
++ __u8 id;
++
++ /* Get next available identificator.
++ * 1 - 199 are used by kernel.
++ * 200 - 254 are used by utilities like l2ping, etc
++ */
++
++ spin_lock(&conn->lock);
++
++ if (++conn->tx_ident > 199)
++ conn->tx_ident = 1;
++
++ id = conn->tx_ident;
++
++ spin_unlock(&conn->lock);
++
++ return id;
++}
++
++static struct sk_buff *l2cap_build_cmd(struct l2cap_conn *conn,
++ __u8 code, __u8 ident, __u16 dlen, void *data)
++{
++ struct sk_buff *skb, **frag;
++ l2cap_cmd_hdr *cmd;
++ l2cap_hdr *lh;
++ int len, count;
++
++ BT_DBG("conn %p, code 0x%2.2x, ident 0x%2.2x, len %d", conn, code, ident, dlen);
++
++ len = L2CAP_HDR_SIZE + L2CAP_CMD_HDR_SIZE + dlen;
++ count = MIN(conn->mtu, len);
++
++ skb = bluez_skb_alloc(count, GFP_ATOMIC);
++ if (!skb)
++ return NULL;
++
++ lh = (l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE);
++ lh->len = __cpu_to_le16(L2CAP_CMD_HDR_SIZE + dlen);
++ lh->cid = __cpu_to_le16(0x0001);
++
++ cmd = (l2cap_cmd_hdr *) skb_put(skb, L2CAP_CMD_HDR_SIZE);
++ cmd->code = code;
++ cmd->ident = ident;
++ cmd->len = __cpu_to_le16(dlen);
++
++ if (dlen) {
++ count -= L2CAP_HDR_SIZE + L2CAP_CMD_HDR_SIZE;
++ memcpy(skb_put(skb, count), data, count);
++ data += count;
++ }
++
++ len -= skb->len;
++
++ /* Continuation fragments (no L2CAP header) */
++ frag = &skb_shinfo(skb)->frag_list;
++ while (len) {
++ count = MIN(conn->mtu, len);
++
++ *frag = bluez_skb_alloc(count, GFP_ATOMIC);
++ if (!*frag)
++ goto fail;
++
++ memcpy(skb_put(*frag, count), data, count);
++
++ len -= count;
++ data += count;
++
++ frag = &(*frag)->next;
++ }
++
++ return skb;
++
++fail:
++ kfree_skb(skb);
++ return NULL;
++}
++
++static int l2cap_send_req(struct l2cap_conn *conn, __u8 code, __u16 len, void *data)
++{
++ __u8 ident = l2cap_get_ident(conn);
++ struct sk_buff *skb = l2cap_build_cmd(conn, code, ident, len, data);
++
++ BT_DBG("code 0x%2.2x", code);
++
++ if (!skb)
++ return -ENOMEM;
++ return hci_send_acl(conn->hcon, skb, 0);
++}
++
++static int l2cap_send_rsp(struct l2cap_conn *conn, __u8 ident, __u8 code, __u16 len, void *data)
++{
++ struct sk_buff *skb = l2cap_build_cmd(conn, code, ident, len, data);
++
++ BT_DBG("code 0x%2.2x", code);
++
++ if (!skb)
++ return -ENOMEM;
++ return hci_send_acl(conn->hcon, skb, 0);
++}
++
++static inline int l2cap_get_conf_opt(void **ptr, int *type, int *olen, unsigned long *val)
++{
++ l2cap_conf_opt *opt = *ptr;
++ int len;
++
++ len = L2CAP_CONF_OPT_SIZE + opt->len;
++ *ptr += len;
++
++ *type = opt->type;
++ *olen = opt->len;
++
++ switch (opt->len) {
++ case 1:
++ *val = *((__u8 *) opt->val);
++ break;
++
++ case 2:
++ *val = __le16_to_cpu(*((__u16 *)opt->val));
++ break;
++
++ case 4:
++ *val = __le32_to_cpu(*((__u32 *)opt->val));
++ break;
++
++ default:
++ *val = (unsigned long) opt->val;
++ break;
++ };
++
++ BT_DBG("type 0x%2.2x len %d val 0x%lx", *type, opt->len, *val);
++ return len;
++}
++
++static inline void l2cap_parse_conf_req(struct sock *sk, void *data, int len)
++{
++ int type, hint, olen;
++ unsigned long val;
++ void *ptr = data;
++
++ BT_DBG("sk %p len %d", sk, len);
++
++ while (len >= L2CAP_CONF_OPT_SIZE) {
++ len -= l2cap_get_conf_opt(&ptr, &type, &olen, &val);
++
++ hint = type & 0x80;
++ type &= 0x7f;
++
++ switch (type) {
++ case L2CAP_CONF_MTU:
++ l2cap_pi(sk)->conf_mtu = val;
++ break;
++
++ case L2CAP_CONF_FLUSH_TO:
++ l2cap_pi(sk)->flush_to = val;
++ break;
++
++ case L2CAP_CONF_QOS:
++ break;
++
++ default:
++ if (hint)
++ break;
++
++ /* FIXME: Reject unknown option */
++ break;
++ };
++ }
++}
++
++static void l2cap_add_conf_opt(void **ptr, __u8 type, __u8 len, unsigned long val)
++{
++ register l2cap_conf_opt *opt = *ptr;
++
++ BT_DBG("type 0x%2.2x len %d val 0x%lx", type, len, val);
++
++ opt->type = type;
++ opt->len = len;
++
++ switch (len) {
++ case 1:
++ *((__u8 *) opt->val) = val;
++ break;
++
++ case 2:
++ *((__u16 *) opt->val) = __cpu_to_le16(val);
++ break;
++
++ case 4:
++ *((__u32 *) opt->val) = __cpu_to_le32(val);
++ break;
++
++ default:
++ memcpy(opt->val, (void *) val, len);
++ break;
++ };
++
++ *ptr += L2CAP_CONF_OPT_SIZE + len;
++}
++
++static int l2cap_build_conf_req(struct sock *sk, void *data)
++{
++ struct l2cap_pinfo *pi = l2cap_pi(sk);
++ l2cap_conf_req *req = (l2cap_conf_req *) data;
++ void *ptr = req->data;
++
++ BT_DBG("sk %p", sk);
++
++ if (pi->imtu != L2CAP_DEFAULT_MTU)
++ l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, pi->imtu);
++
++ /* FIXME. Need actual value of the flush timeout */
++ //if (flush_to != L2CAP_DEFAULT_FLUSH_TO)
++ // l2cap_add_conf_opt(&ptr, L2CAP_CONF_FLUSH_TO, 2, pi->flush_to);
++
++ req->dcid = __cpu_to_le16(pi->dcid);
++ req->flags = __cpu_to_le16(0);
++
++ return ptr - data;
++}
++
++static inline int l2cap_conf_output(struct sock *sk, void **ptr)
++{
++ struct l2cap_pinfo *pi = l2cap_pi(sk);
++ int result = 0;
++
++ /* Configure output options and let the other side know
++ * which ones we don't like.
++ */
++ if (pi->conf_mtu < pi->omtu) {
++ l2cap_add_conf_opt(ptr, L2CAP_CONF_MTU, 2, pi->omtu);
++ result = L2CAP_CONF_UNACCEPT;
++ } else {
++ pi->omtu = pi->conf_mtu;
++ }
++
++ BT_DBG("sk %p result %d", sk, result);
++ return result;
++}
++
++static int l2cap_build_conf_rsp(struct sock *sk, void *data, int *result)
++{
++ l2cap_conf_rsp *rsp = (l2cap_conf_rsp *) data;
++ void *ptr = rsp->data;
++ u16 flags = 0;
++
++ BT_DBG("sk %p complete %d", sk, result ? 1 : 0);
++
++ if (result)
++ *result = l2cap_conf_output(sk, &ptr);
++ else
++ flags |= 0x0001;
++
++ rsp->scid = __cpu_to_le16(l2cap_pi(sk)->dcid);
++ rsp->result = __cpu_to_le16(result ? *result : 0);
++ rsp->flags = __cpu_to_le16(flags);
++
++ return ptr - data;
++}
++
++static inline int l2cap_connect_req(struct l2cap_conn *conn, l2cap_cmd_hdr *cmd, __u8 *data)
++{
++ struct l2cap_chan_list *list = &conn->chan_list;
++ l2cap_conn_req *req = (l2cap_conn_req *) data;
++ l2cap_conn_rsp rsp;
++ struct sock *sk, *parent;
++ int result = 0, status = 0;
++
++ __u16 dcid = 0, scid = __le16_to_cpu(req->scid);
++ __u16 psm = req->psm;
++
++ BT_DBG("psm 0x%2.2x scid 0x%4.4x", psm, scid);
++
++ /* Check if we have socket listening on psm */
++ parent = l2cap_get_sock_by_psm(BT_LISTEN, psm, conn->src);
++ if (!parent) {
++ result = L2CAP_CR_BAD_PSM;
++ goto sendresp;
++ }
++
++ result = L2CAP_CR_NO_MEM;
++
++ /* Check for backlog size */
++ if (parent->ack_backlog > parent->max_ack_backlog) {
++ BT_DBG("backlog full %d", parent->ack_backlog);
++ goto response;
++ }
++
++ sk = l2cap_sock_alloc(NULL, BTPROTO_L2CAP, GFP_ATOMIC);
++ if (!sk)
++ goto response;
++
++ write_lock(&list->lock);
++
++ /* Check if we already have channel with that dcid */
++ if (__l2cap_get_chan_by_dcid(list, scid)) {
++ write_unlock(&list->lock);
++ sk->zapped = 1;
++ l2cap_sock_kill(sk);
++ goto response;
++ }
++
++ hci_conn_hold(conn->hcon);
++
++ l2cap_sock_init(sk, parent);
++ bacpy(&bluez_pi(sk)->src, conn->src);
++ bacpy(&bluez_pi(sk)->dst, conn->dst);
++ l2cap_pi(sk)->psm = psm;
++ l2cap_pi(sk)->dcid = scid;
++
++ __l2cap_chan_add(conn, sk, parent);
++ dcid = l2cap_pi(sk)->scid;
++
++ l2cap_sock_set_timer(sk, sk->sndtimeo);
++
++ /* Service level security */
++ result = L2CAP_CR_PEND;
++ status = L2CAP_CS_AUTHEN_PEND;
++ sk->state = BT_CONNECT2;
++ l2cap_pi(sk)->ident = cmd->ident;
++
++ if (l2cap_pi(sk)->link_mode & L2CAP_LM_ENCRYPT) {
++ if (!hci_conn_encrypt(conn->hcon))
++ goto done;
++ } else if (l2cap_pi(sk)->link_mode & L2CAP_LM_AUTH) {
++ if (!hci_conn_auth(conn->hcon))
++ goto done;
++ }
++
++ sk->state = BT_CONFIG;
++ result = status = 0;
++
++done:
++ write_unlock(&list->lock);
++
++response:
++ bh_unlock_sock(parent);
++
++sendresp:
++ rsp.scid = __cpu_to_le16(scid);
++ rsp.dcid = __cpu_to_le16(dcid);
++ rsp.result = __cpu_to_le16(result);
++ rsp.status = __cpu_to_le16(status);
++ l2cap_send_rsp(conn, cmd->ident, L2CAP_CONN_RSP, L2CAP_CONN_RSP_SIZE, &rsp);
++ return 0;
++}
++
++static inline int l2cap_connect_rsp(struct l2cap_conn *conn, l2cap_cmd_hdr *cmd, __u8 *data)
++{
++ l2cap_conn_rsp *rsp = (l2cap_conn_rsp *) data;
++ __u16 scid, dcid, result, status;
++ struct sock *sk;
++ char req[128];
++
++ scid = __le16_to_cpu(rsp->scid);
++ dcid = __le16_to_cpu(rsp->dcid);
++ result = __le16_to_cpu(rsp->result);
++ status = __le16_to_cpu(rsp->status);
++
++ BT_DBG("dcid 0x%4.4x scid 0x%4.4x result 0x%2.2x status 0x%2.2x", dcid, scid, result, status);
++
++ if (!(sk = l2cap_get_chan_by_scid(&conn->chan_list, scid)))
++ return -ENOENT;
++
++ switch (result) {
++ case L2CAP_CR_SUCCESS:
++ sk->state = BT_CONFIG;
++ l2cap_pi(sk)->dcid = dcid;
++ l2cap_pi(sk)->conf_state |= L2CAP_CONF_REQ_SENT;
++
++ l2cap_send_req(conn, L2CAP_CONF_REQ, l2cap_build_conf_req(sk, req), req);
++ break;
++
++ case L2CAP_CR_PEND:
++ break;
++
++ default:
++ l2cap_chan_del(sk, ECONNREFUSED);
++ break;
++ }
++
++ bh_unlock_sock(sk);
++ return 0;
++}
++
++static inline int l2cap_config_req(struct l2cap_conn *conn, l2cap_cmd_hdr *cmd, __u8 *data)
++{
++ l2cap_conf_req * req = (l2cap_conf_req *) data;
++ __u16 dcid, flags;
++ __u8 rsp[64];
++ struct sock *sk;
++ int result;
++
++ dcid = __le16_to_cpu(req->dcid);
++ flags = __le16_to_cpu(req->flags);
++
++ BT_DBG("dcid 0x%4.4x flags 0x%2.2x", dcid, flags);
++
++ if (!(sk = l2cap_get_chan_by_scid(&conn->chan_list, dcid)))
++ return -ENOENT;
++
++ l2cap_parse_conf_req(sk, req->data, cmd->len - L2CAP_CONF_REQ_SIZE);
++
++ if (flags & 0x0001) {
++ /* Incomplete config. Send empty response. */
++ l2cap_send_rsp(conn, cmd->ident, L2CAP_CONF_RSP, l2cap_build_conf_rsp(sk, rsp, NULL), rsp);
++ goto unlock;
++ }
++
++ /* Complete config. */
++ l2cap_send_rsp(conn, cmd->ident, L2CAP_CONF_RSP, l2cap_build_conf_rsp(sk, rsp, &result), rsp);
++
++ if (result)
++ goto unlock;
++
++ /* Output config done */
++ l2cap_pi(sk)->conf_state |= L2CAP_CONF_OUTPUT_DONE;
++
++ if (l2cap_pi(sk)->conf_state & L2CAP_CONF_INPUT_DONE) {
++ sk->state = BT_CONNECTED;
++ l2cap_chan_ready(sk);
++ } else if (!(l2cap_pi(sk)->conf_state & L2CAP_CONF_REQ_SENT)) {
++ char req[64];
++ l2cap_send_req(conn, L2CAP_CONF_REQ, l2cap_build_conf_req(sk, req), req);
++ }
++
++unlock:
++ bh_unlock_sock(sk);
++ return 0;
++}
++
++static inline int l2cap_config_rsp(struct l2cap_conn *conn, l2cap_cmd_hdr *cmd, __u8 *data)
++{
++ l2cap_conf_rsp *rsp = (l2cap_conf_rsp *)data;
++ __u16 scid, flags, result;
++ struct sock *sk;
++ int err = 0;
++
++ scid = __le16_to_cpu(rsp->scid);
++ flags = __le16_to_cpu(rsp->flags);
++ result = __le16_to_cpu(rsp->result);
++
++ BT_DBG("scid 0x%4.4x flags 0x%2.2x result 0x%2.2x", scid, flags, result);
++
++ if (!(sk = l2cap_get_chan_by_scid(&conn->chan_list, scid)))
++ return -ENOENT;
++
++ switch (result) {
++ case L2CAP_CONF_SUCCESS:
++ break;
++
++ case L2CAP_CONF_UNACCEPT:
++ if (++l2cap_pi(sk)->conf_retry < L2CAP_CONF_MAX_RETRIES) {
++ char req[128];
++ /*
++ It does not make sense to adjust L2CAP parameters
++ that are currently defined in the spec. We simply
++ resend config request that we sent earlier. It is
++ stupid :) but it helps qualification testing
++ which expects at least some response from us.
++ */
++ l2cap_send_req(conn, L2CAP_CONF_REQ,
++ l2cap_build_conf_req(sk, req), req);
++ goto done;
++ }
++ default:
++ sk->state = BT_DISCONN;
++ sk->err = ECONNRESET;
++ l2cap_sock_set_timer(sk, HZ * 5);
++ {
++ l2cap_disconn_req req;
++ req.dcid = __cpu_to_le16(l2cap_pi(sk)->dcid);
++ req.scid = __cpu_to_le16(l2cap_pi(sk)->scid);
++ l2cap_send_req(conn, L2CAP_DISCONN_REQ, L2CAP_DISCONN_REQ_SIZE, &req);
++ }
++ goto done;
++ }
++
++ if (flags & 0x01)
++ goto done;
++
++ /* Input config done */
++ l2cap_pi(sk)->conf_state |= L2CAP_CONF_INPUT_DONE;
++
++ if (l2cap_pi(sk)->conf_state & L2CAP_CONF_OUTPUT_DONE) {
++ sk->state = BT_CONNECTED;
++ l2cap_chan_ready(sk);
++ }
++
++done:
++ bh_unlock_sock(sk);
++ return err;
++}
++
++static inline int l2cap_disconnect_req(struct l2cap_conn *conn, l2cap_cmd_hdr *cmd, __u8 *data)
++{
++ l2cap_disconn_req *req = (l2cap_disconn_req *) data;
++ l2cap_disconn_rsp rsp;
++ __u16 dcid, scid;
++ struct sock *sk;
++
++ scid = __le16_to_cpu(req->scid);
++ dcid = __le16_to_cpu(req->dcid);
++
++ BT_DBG("scid 0x%4.4x dcid 0x%4.4x", scid, dcid);
++
++ if (!(sk = l2cap_get_chan_by_scid(&conn->chan_list, dcid)))
++ return 0;
++
++ rsp.dcid = __cpu_to_le16(l2cap_pi(sk)->scid);
++ rsp.scid = __cpu_to_le16(l2cap_pi(sk)->dcid);
++ l2cap_send_rsp(conn, cmd->ident, L2CAP_DISCONN_RSP, L2CAP_DISCONN_RSP_SIZE, &rsp);
++
++ sk->shutdown = SHUTDOWN_MASK;
++
++ l2cap_chan_del(sk, ECONNRESET);
++ bh_unlock_sock(sk);
++
++ l2cap_sock_kill(sk);
++ return 0;
++}
++
++static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, l2cap_cmd_hdr *cmd, __u8 *data)
++{
++ l2cap_disconn_rsp *rsp = (l2cap_disconn_rsp *) data;
++ __u16 dcid, scid;
++ struct sock *sk;
++
++ scid = __le16_to_cpu(rsp->scid);
++ dcid = __le16_to_cpu(rsp->dcid);
++
++ BT_DBG("dcid 0x%4.4x scid 0x%4.4x", dcid, scid);
++
++ if (!(sk = l2cap_get_chan_by_scid(&conn->chan_list, scid)))
++ return 0;
++ l2cap_chan_del(sk, 0);
++ bh_unlock_sock(sk);
++
++ l2cap_sock_kill(sk);
++ return 0;
++}
++
++static inline int l2cap_information_req(struct l2cap_conn *conn, l2cap_cmd_hdr *cmd, u8 *data)
++{
++ l2cap_info_req *req = (l2cap_info_req *) data;
++ l2cap_info_rsp rsp;
++ u16 type;
++
++ type = __le16_to_cpu(req->type);
++
++ BT_DBG("type 0x%4.4x", type);
++
++ rsp.type = __cpu_to_le16(type);
++ rsp.result = __cpu_to_le16(L2CAP_IR_NOTSUPP);
++ l2cap_send_rsp(conn, cmd->ident, L2CAP_INFO_RSP, sizeof(rsp), &rsp);
++ return 0;
++}
++
++static inline int l2cap_information_rsp(struct l2cap_conn *conn, l2cap_cmd_hdr *cmd, u8 *data)
++{
++ l2cap_info_rsp *rsp = (l2cap_info_rsp *) data;
++ u16 type, result;
++
++ type = __le16_to_cpu(rsp->type);
++ result = __le16_to_cpu(rsp->result);
++
++ BT_DBG("type 0x%4.4x result 0x%2.2x", type, result);
++
++ return 0;
++}
++
++static inline void l2cap_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb)
++{
++ __u8 *data = skb->data;
++ int len = skb->len;
++ l2cap_cmd_hdr cmd;
++ int err = 0;
++
++ l2cap_raw_recv(conn, skb);
++
++ while (len >= L2CAP_CMD_HDR_SIZE) {
++ memcpy(&cmd, data, L2CAP_CMD_HDR_SIZE);
++ data += L2CAP_CMD_HDR_SIZE;
++ len -= L2CAP_CMD_HDR_SIZE;
++
++ cmd.len = __le16_to_cpu(cmd.len);
++
++ BT_DBG("code 0x%2.2x len %d id 0x%2.2x", cmd.code, cmd.len, cmd.ident);
++
++ if (cmd.len > len || !cmd.ident) {
++ BT_DBG("corrupted command");
++ break;
++ }
++
++ switch (cmd.code) {
++ case L2CAP_COMMAND_REJ:
++ /* FIXME: We should process this */
++ break;
++
++ case L2CAP_CONN_REQ:
++ err = l2cap_connect_req(conn, &cmd, data);
++ break;
++
++ case L2CAP_CONN_RSP:
++ err = l2cap_connect_rsp(conn, &cmd, data);
++ break;
++
++ case L2CAP_CONF_REQ:
++ err = l2cap_config_req(conn, &cmd, data);
++ break;
++
++ case L2CAP_CONF_RSP:
++ err = l2cap_config_rsp(conn, &cmd, data);
++ break;
++
++ case L2CAP_DISCONN_REQ:
++ err = l2cap_disconnect_req(conn, &cmd, data);
++ break;
++
++ case L2CAP_DISCONN_RSP:
++ err = l2cap_disconnect_rsp(conn, &cmd, data);
++ break;
++
++ case L2CAP_ECHO_REQ:
++ l2cap_send_rsp(conn, cmd.ident, L2CAP_ECHO_RSP, cmd.len, data);
++ break;
++
++ case L2CAP_ECHO_RSP:
++ break;
++
++ case L2CAP_INFO_REQ:
++ err = l2cap_information_req(conn, &cmd, data);
++ break;
++
++ case L2CAP_INFO_RSP:
++ err = l2cap_information_rsp(conn, &cmd, data);
++ break;
++
++ default:
++ BT_ERR("Unknown signaling command 0x%2.2x", cmd.code);
++ err = -EINVAL;
++ break;
++ };
++
++ if (err) {
++ l2cap_cmd_rej rej;
++ BT_DBG("error %d", err);
++
++ /* FIXME: Map err to a valid reason */
++ rej.reason = __cpu_to_le16(0);
++ l2cap_send_rsp(conn, cmd.ident, L2CAP_COMMAND_REJ, L2CAP_CMD_REJ_SIZE, &rej);
++ }
++
++ data += cmd.len;
++ len -= cmd.len;
++ }
++
++ kfree_skb(skb);
++}
++
++static inline int l2cap_data_channel(struct l2cap_conn *conn, __u16 cid, struct sk_buff *skb)
++{
++ struct sock *sk;
++
++ sk = l2cap_get_chan_by_scid(&conn->chan_list, cid);
++ if (!sk) {
++ BT_DBG("unknown cid 0x%4.4x", cid);
++ goto drop;
++ }
++
++ BT_DBG("sk %p, len %d", sk, skb->len);
++
++ if (sk->state != BT_CONNECTED)
++ goto drop;
++
++ if (l2cap_pi(sk)->imtu < skb->len)
++ goto drop;
++
++ /* If socket recv buffers overflows we drop data here
++ * which is *bad* because L2CAP has to be reliable.
++ * But we don't have any other choice. L2CAP doesn't
++ * provide flow control mechanism */
++
++ if (!sock_queue_rcv_skb(sk, skb))
++ goto done;
++
++drop:
++ kfree_skb(skb);
++
++done:
++ if (sk) bh_unlock_sock(sk);
++ return 0;
++}
++
++static inline int l2cap_conless_channel(struct l2cap_conn *conn, __u16 psm, struct sk_buff *skb)
++{
++ struct sock *sk;
++
++ sk = l2cap_get_sock_by_psm(0, psm, conn->src);
++ if (!sk)
++ goto drop;
++
++ BT_DBG("sk %p, len %d", sk, skb->len);
++
++ if (sk->state != BT_BOUND && sk->state != BT_CONNECTED)
++ goto drop;
++
++ if (l2cap_pi(sk)->imtu < skb->len)
++ goto drop;
++
++ if (!sock_queue_rcv_skb(sk, skb))
++ goto done;
++
++drop:
++ kfree_skb(skb);
++
++done:
++ if (sk) bh_unlock_sock(sk);
++ return 0;
++}
++
++static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb)
++{
++ l2cap_hdr *lh = (l2cap_hdr *) skb->data;
++ __u16 cid, psm, len;
++
++ skb_pull(skb, L2CAP_HDR_SIZE);
++ cid = __le16_to_cpu(lh->cid);
++ len = __le16_to_cpu(lh->len);
++
++ BT_DBG("len %d, cid 0x%4.4x", len, cid);
++
++ switch (cid) {
++ case 0x0001:
++ l2cap_sig_channel(conn, skb);
++ break;
++
++ case 0x0002:
++ psm = get_unaligned((__u16 *) skb->data);
++ skb_pull(skb, 2);
++ l2cap_conless_channel(conn, psm, skb);
++ break;
++
++ default:
++ l2cap_data_channel(conn, cid, skb);
++ break;
++ }
++}
++
++/* ------------ L2CAP interface with lower layer (HCI) ------------- */
++
++static int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 type)
++{
++ int exact = 0, lm1 = 0, lm2 = 0;
++ register struct sock *sk;
++
++ if (type != ACL_LINK)
++ return 0;
++
++ BT_DBG("hdev %s, bdaddr %s", hdev->name, batostr(bdaddr));
++
++ /* Find listening sockets and check their link_mode */
++ read_lock(&l2cap_sk_list.lock);
++ for (sk = l2cap_sk_list.head; sk; sk = sk->next) {
++ if (sk->state != BT_LISTEN)
++ continue;
++
++ if (!bacmp(&bluez_pi(sk)->src, &hdev->bdaddr)) {
++ lm1 |= (HCI_LM_ACCEPT | l2cap_pi(sk)->link_mode);
++ exact++;
++ } else if (!bacmp(&bluez_pi(sk)->src, BDADDR_ANY))
++ lm2 |= (HCI_LM_ACCEPT | l2cap_pi(sk)->link_mode);
++ }
++ read_unlock(&l2cap_sk_list.lock);
++
++ return exact ? lm1 : lm2;
++}
++
++static int l2cap_connect_cfm(struct hci_conn *hcon, __u8 status)
++{
++ BT_DBG("hcon %p bdaddr %s status %d", hcon, batostr(&hcon->dst), status);
++
++ if (hcon->type != ACL_LINK)
++ return 0;
++
++ if (!status) {
++ struct l2cap_conn *conn;
++
++ conn = l2cap_conn_add(hcon, status);
++ if (conn)
++ l2cap_conn_ready(conn);
++ } else
++ l2cap_conn_del(hcon, bterr(status));
++
++ return 0;
++}
++
++static int l2cap_disconn_ind(struct hci_conn *hcon, __u8 reason)
++{
++ BT_DBG("hcon %p reason %d", hcon, reason);
++
++ if (hcon->type != ACL_LINK)
++ return 0;
++
++ l2cap_conn_del(hcon, bterr(reason));
++ return 0;
++}
++
++static int l2cap_auth_cfm(struct hci_conn *hcon, __u8 status)
++{
++ struct l2cap_chan_list *l;
++ struct l2cap_conn *conn;
++ l2cap_conn_rsp rsp;
++ struct sock *sk;
++ int result;
++
++ if (!(conn = hcon->l2cap_data))
++ return 0;
++ l = &conn->chan_list;
++
++ BT_DBG("conn %p", conn);
++
++ read_lock(&l->lock);
++
++ for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) {
++ bh_lock_sock(sk);
++
++ if (sk->state != BT_CONNECT2 ||
++ (l2cap_pi(sk)->link_mode & L2CAP_LM_ENCRYPT)) {
++ bh_unlock_sock(sk);
++ continue;
++ }
++
++ if (!status) {
++ sk->state = BT_CONFIG;
++ result = 0;
++ } else {
++ sk->state = BT_DISCONN;
++ l2cap_sock_set_timer(sk, HZ/10);
++ result = L2CAP_CR_SEC_BLOCK;
++ }
++
++ rsp.scid = __cpu_to_le16(l2cap_pi(sk)->dcid);
++ rsp.dcid = __cpu_to_le16(l2cap_pi(sk)->scid);
++ rsp.result = __cpu_to_le16(result);
++ rsp.status = __cpu_to_le16(0);
++ l2cap_send_rsp(conn, l2cap_pi(sk)->ident, L2CAP_CONN_RSP,
++ L2CAP_CONN_RSP_SIZE, &rsp);
++
++ bh_unlock_sock(sk);
++ }
++
++ read_unlock(&l->lock);
++ return 0;
++}
++
++static int l2cap_encrypt_cfm(struct hci_conn *hcon, __u8 status)
++{
++ struct l2cap_chan_list *l;
++ struct l2cap_conn *conn;
++ l2cap_conn_rsp rsp;
++ struct sock *sk;
++ int result;
++
++ if (!(conn = hcon->l2cap_data))
++ return 0;
++ l = &conn->chan_list;
++
++ BT_DBG("conn %p", conn);
++
++ read_lock(&l->lock);
++
++ for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) {
++ bh_lock_sock(sk);
++
++ if (sk->state != BT_CONNECT2) {
++ bh_unlock_sock(sk);
++ continue;
++ }
++
++ if (!status) {
++ sk->state = BT_CONFIG;
++ result = 0;
++ } else {
++ sk->state = BT_DISCONN;
++ l2cap_sock_set_timer(sk, HZ/10);
++ result = L2CAP_CR_SEC_BLOCK;
++ }
++
++ rsp.scid = __cpu_to_le16(l2cap_pi(sk)->dcid);
++ rsp.dcid = __cpu_to_le16(l2cap_pi(sk)->scid);
++ rsp.result = __cpu_to_le16(result);
++ rsp.status = __cpu_to_le16(0);
++ l2cap_send_rsp(conn, l2cap_pi(sk)->ident, L2CAP_CONN_RSP,
++ L2CAP_CONN_RSP_SIZE, &rsp);
++
++ bh_unlock_sock(sk);
++ }
++
++ read_unlock(&l->lock);
++ return 0;
++}
++
++static int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, __u16 flags)
++{
++ struct l2cap_conn *conn = hcon->l2cap_data;
++
++ if (!conn && !(conn = l2cap_conn_add(hcon, 0)))
++ goto drop;
++
++ BT_DBG("conn %p len %d flags 0x%x", conn, skb->len, flags);
++
++ if (flags & ACL_START) {
++ l2cap_hdr *hdr;
++ int len;
++
++ if (conn->rx_len) {
++ BT_ERR("Unexpected start frame (len %d)", skb->len);
++ kfree_skb(conn->rx_skb);
++ conn->rx_skb = NULL;
++ conn->rx_len = 0;
++ l2cap_conn_unreliable(conn, ECOMM);
++ }
++
++ if (skb->len < 2) {
++ BT_ERR("Frame is too short (len %d)", skb->len);
++ l2cap_conn_unreliable(conn, ECOMM);
++ goto drop;
++ }
++
++ hdr = (l2cap_hdr *) skb->data;
++ len = __le16_to_cpu(hdr->len) + L2CAP_HDR_SIZE;
++
++ if (len == skb->len) {
++ /* Complete frame received */
++ l2cap_recv_frame(conn, skb);
++ return 0;
++ }
++
++ BT_DBG("Start: total len %d, frag len %d", len, skb->len);
++
++ if (skb->len > len) {
++ BT_ERR("Frame is too long (len %d, expected len %d)",
++ skb->len, len);
++ l2cap_conn_unreliable(conn, ECOMM);
++ goto drop;
++ }
++
++ /* Allocate skb for the complete frame including header */
++ conn->rx_skb = bluez_skb_alloc(len, GFP_ATOMIC);
++ if (!conn->rx_skb)
++ goto drop;
++
++ memcpy(skb_put(conn->rx_skb, skb->len), skb->data, skb->len);
++ conn->rx_len = len - skb->len;
++ } else {
++ BT_DBG("Cont: frag len %d (expecting %d)", skb->len, conn->rx_len);
++
++ if (!conn->rx_len) {
++ BT_ERR("Unexpected continuation frame (len %d)", skb->len);
++ l2cap_conn_unreliable(conn, ECOMM);
++ goto drop;
++ }
++
++ if (skb->len > conn->rx_len) {
++ BT_ERR("Fragment is too long (len %d, expected %d)",
++ skb->len, conn->rx_len);
++ kfree_skb(conn->rx_skb);
++ conn->rx_skb = NULL;
++ conn->rx_len = 0;
++ l2cap_conn_unreliable(conn, ECOMM);
++ goto drop;
++ }
++
++ memcpy(skb_put(conn->rx_skb, skb->len), skb->data, skb->len);
++ conn->rx_len -= skb->len;
++
++ if (!conn->rx_len) {
++ /* Complete frame received */
++ l2cap_recv_frame(conn, conn->rx_skb);
++ conn->rx_skb = NULL;
++ }
++ }
++
++drop:
++ kfree_skb(skb);
++ return 0;
++}
++
++/* ----- Proc fs support ------ */
++static int l2cap_sock_dump(char *buf, struct bluez_sock_list *list)
++{
++ struct l2cap_pinfo *pi;
++ struct sock *sk;
++ char *ptr = buf;
++
++ read_lock_bh(&list->lock);
++
++ for (sk = list->head; sk; sk = sk->next) {
++ pi = l2cap_pi(sk);
++ ptr += sprintf(ptr, "%s %s %d %d 0x%4.4x 0x%4.4x %d %d 0x%x\n",
++ batostr(&bluez_pi(sk)->src), batostr(&bluez_pi(sk)->dst),
++ sk->state, pi->psm, pi->scid, pi->dcid, pi->imtu, pi->omtu,
++ pi->link_mode);
++ }
++
++ read_unlock_bh(&list->lock);
++
++ ptr += sprintf(ptr, "\n");
++ return ptr - buf;
++}
++
++static int l2cap_read_proc(char *buf, char **start, off_t offset, int count, int *eof, void *priv)
++{
++ char *ptr = buf;
++ int len;
++
++ BT_DBG("count %d, offset %ld", count, offset);
++
++ ptr += l2cap_sock_dump(ptr, &l2cap_sk_list);
++ len = ptr - buf;
++
++ if (len <= count + offset)
++ *eof = 1;
++
++ *start = buf + offset;
++ len -= offset;
++
++ if (len > count)
++ len = count;
++ if (len < 0)
++ len = 0;
++
++ return len;
++}
++
++static struct proto_ops l2cap_sock_ops = {
++ family: PF_BLUETOOTH,
++ release: l2cap_sock_release,
++ bind: l2cap_sock_bind,
++ connect: l2cap_sock_connect,
++ listen: l2cap_sock_listen,
++ accept: l2cap_sock_accept,
++ getname: l2cap_sock_getname,
++ sendmsg: l2cap_sock_sendmsg,
++ recvmsg: bluez_sock_recvmsg,
++ poll: bluez_sock_poll,
++ socketpair: sock_no_socketpair,
++ ioctl: sock_no_ioctl,
++ shutdown: l2cap_sock_shutdown,
++ setsockopt: l2cap_sock_setsockopt,
++ getsockopt: l2cap_sock_getsockopt,
++ mmap: sock_no_mmap
++};
++
++static struct net_proto_family l2cap_sock_family_ops = {
++ family: PF_BLUETOOTH,
++ create: l2cap_sock_create
++};
++
++static struct hci_proto l2cap_hci_proto = {
++ name: "L2CAP",
++ id: HCI_PROTO_L2CAP,
++ connect_ind: l2cap_connect_ind,
++ connect_cfm: l2cap_connect_cfm,
++ disconn_ind: l2cap_disconn_ind,
++ recv_acldata: l2cap_recv_acldata,
++ auth_cfm: l2cap_auth_cfm,
++ encrypt_cfm: l2cap_encrypt_cfm
++};
++
++int __init l2cap_init(void)
++{
++ int err;
++
++ if ((err = bluez_sock_register(BTPROTO_L2CAP, &l2cap_sock_family_ops))) {
++ BT_ERR("Can't register L2CAP socket");
++ return err;
++ }
++
++ if ((err = hci_register_proto(&l2cap_hci_proto))) {
++ BT_ERR("Can't register L2CAP protocol");
++ return err;
++ }
++
++ create_proc_read_entry("bluetooth/l2cap", 0, 0, l2cap_read_proc, NULL);
++
++ BT_INFO("BlueZ L2CAP ver %s Copyright (C) 2000,2001 Qualcomm Inc", VERSION);
++ BT_INFO("Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>");
++ return 0;
++}
++
++void l2cap_cleanup(void)
++{
++ remove_proc_entry("bluetooth/l2cap", NULL);
++
++ /* Unregister socket and protocol */
++ if (bluez_sock_unregister(BTPROTO_L2CAP))
++ BT_ERR("Can't unregister L2CAP socket");
++
++ if (hci_unregister_proto(&l2cap_hci_proto))
++ BT_ERR("Can't unregister L2CAP protocol");
++}
++
++void l2cap_load(void)
++{
++ /* Dummy function to trigger automatic L2CAP module loading by
++ other modules that use L2CAP sockets but do not use any other
++ symbols from it. */
++ return;
++}
++
++EXPORT_SYMBOL(l2cap_load);
++
++module_init(l2cap_init);
++module_exit(l2cap_cleanup);
++
++MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>");
++MODULE_DESCRIPTION("BlueZ L2CAP ver " VERSION);
++MODULE_LICENSE("GPL");
+diff -urN linux-2.4.18/net/bluetooth/l2cap_core.c linux-2.4.18-mh15/net/bluetooth/l2cap_core.c
+--- linux-2.4.18/net/bluetooth/l2cap_core.c 2001-09-30 21:26:08.000000000 +0200
++++ linux-2.4.18-mh15/net/bluetooth/l2cap_core.c 1970-01-01 01:00:00.000000000 +0100
+@@ -1,2316 +0,0 @@
+-/*
+- BlueZ - Bluetooth protocol stack for Linux
+- Copyright (C) 2000-2001 Qualcomm Incorporated
+-
+- Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
+-
+- This program is free software; you can redistribute it and/or modify
+- it under the terms of the GNU General Public License version 2 as
+- published by the Free Software Foundation;
+-
+- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+- OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+- IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+- CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
+- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+-
+- ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
+- COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
+- SOFTWARE IS DISCLAIMED.
+-*/
+-
+-/*
+- * BlueZ L2CAP core and sockets.
+- *
+- * $Id$
+- */
+-#define VERSION "1.1"
+-
+-#include <linux/config.h>
+-#include <linux/module.h>
+-
+-#include <linux/types.h>
+-#include <linux/errno.h>
+-#include <linux/kernel.h>
+-#include <linux/major.h>
+-#include <linux/sched.h>
+-#include <linux/slab.h>
+-#include <linux/poll.h>
+-#include <linux/fcntl.h>
+-#include <linux/init.h>
+-#include <linux/skbuff.h>
+-#include <linux/interrupt.h>
+-#include <linux/socket.h>
+-#include <linux/skbuff.h>
+-#include <linux/proc_fs.h>
+-#include <linux/list.h>
+-#include <net/sock.h>
+-
+-#include <asm/system.h>
+-#include <asm/uaccess.h>
+-
+-#include <net/bluetooth/bluetooth.h>
+-#include <net/bluetooth/bluez.h>
+-#include <net/bluetooth/hci_core.h>
+-#include <net/bluetooth/l2cap.h>
+-#include <net/bluetooth/l2cap_core.h>
+-
+-#ifndef L2CAP_DEBUG
+-#undef DBG
+-#define DBG( A... )
+-#endif
+-
+-struct proto_ops l2cap_sock_ops;
+-
+-struct bluez_sock_list l2cap_sk_list = {
+- lock: RW_LOCK_UNLOCKED
+-};
+-
+-struct list_head l2cap_iff_list = LIST_HEAD_INIT(l2cap_iff_list);
+-rwlock_t l2cap_rt_lock = RW_LOCK_UNLOCKED;
+-
+-static int l2cap_conn_del(struct l2cap_conn *conn, int err);
+-
+-static inline void l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct sock *parent);
+-static void l2cap_chan_del(struct sock *sk, int err);
+-static int l2cap_chan_send(struct sock *sk, struct msghdr *msg, int len);
+-
+-static void l2cap_sock_close(struct sock *sk);
+-static void l2cap_sock_kill(struct sock *sk);
+-
+-static int l2cap_send_req(struct l2cap_conn *conn, __u8 code, __u16 len, void *data);
+-static int l2cap_send_rsp(struct l2cap_conn *conn, __u8 ident, __u8 code, __u16 len, void *data);
+-
+-/* -------- L2CAP interfaces & routing --------- */
+-/* Add/delete L2CAP interface.
+- * Must be called with locked rt_lock
+- */
+-
+-static void l2cap_iff_add(struct hci_dev *hdev)
+-{
+- struct l2cap_iff *iff;
+-
+- DBG("%s", hdev->name);
+-
+- DBG("iff_list %p next %p prev %p", &l2cap_iff_list, l2cap_iff_list.next, l2cap_iff_list.prev);
+-
+- /* Allocate new interface and lock HCI device */
+- if (!(iff = kmalloc(sizeof(struct l2cap_iff), GFP_KERNEL))) {
+- ERR("Can't allocate new interface %s", hdev->name);
+- return;
+- }
+- memset(iff, 0, sizeof(struct l2cap_iff));
+-
+- hci_dev_hold(hdev);
+- hdev->l2cap_data = iff;
+- iff->hdev = hdev;
+- iff->mtu = hdev->acl_mtu - HCI_ACL_HDR_SIZE;
+- iff->bdaddr = &hdev->bdaddr;
+-
+- spin_lock_init(&iff->lock);
+- INIT_LIST_HEAD(&iff->conn_list);
+-
+- list_add(&iff->list, &l2cap_iff_list);
+-}
+-
+-static void l2cap_iff_del(struct hci_dev *hdev)
+-{
+- struct l2cap_iff *iff;
+-
+- if (!(iff = hdev->l2cap_data))
+- return;
+-
+- DBG("%s iff %p", hdev->name, iff);
+-
+- list_del(&iff->list);
+-
+- l2cap_iff_lock(iff);
+-
+- /* Drop connections */
+- while (!list_empty(&iff->conn_list)) {
+- struct l2cap_conn *c;
+-
+- c = list_entry(iff->conn_list.next, struct l2cap_conn, list);
+- l2cap_conn_del(c, ENODEV);
+- }
+-
+- l2cap_iff_unlock(iff);
+-
+- /* Unlock HCI device */
+- hdev->l2cap_data = NULL;
+- hci_dev_put(hdev);
+-
+- kfree(iff);
+-}
+-
+-/* Get route. Returns L2CAP interface.
+- * Must be called with locked rt_lock
+- */
+-static struct l2cap_iff *l2cap_get_route(bdaddr_t *src, bdaddr_t *dst)
+-{
+- struct list_head *p;
+- int use_src;
+-
+- DBG("%s -> %s", batostr(src), batostr(dst));
+-
+- use_src = bacmp(src, BDADDR_ANY) ? 0 : 1;
+-
+- /* Simple routing:
+- * No source address - find interface with bdaddr != dst
+- * Source address - find interface with bdaddr == src
+- */
+-
+- list_for_each(p, &l2cap_iff_list) {
+- struct l2cap_iff *iff;
+-
+- iff = list_entry(p, struct l2cap_iff, list);
+-
+- if (use_src && !bacmp(iff->bdaddr, src))
+- return iff;
+- else if (bacmp(iff->bdaddr, dst))
+- return iff;
+- }
+- return NULL;
+-}
+-
+-/* ----- L2CAP timers ------ */
+-static void l2cap_sock_timeout(unsigned long arg)
+-{
+- struct sock *sk = (struct sock *) arg;
+-
+- DBG("sock %p state %d", sk, sk->state);
+-
+- bh_lock_sock(sk);
+- switch (sk->state) {
+- case BT_DISCONN:
+- l2cap_chan_del(sk, ETIMEDOUT);
+- break;
+-
+- default:
+- sk->err = ETIMEDOUT;
+- sk->state_change(sk);
+- break;
+- };
+- bh_unlock_sock(sk);
+-
+- l2cap_sock_kill(sk);
+- sock_put(sk);
+-}
+-
+-static void l2cap_sock_set_timer(struct sock *sk, long timeout)
+-{
+- DBG("sock %p state %d timeout %ld", sk, sk->state, timeout);
+-
+- if (!mod_timer(&sk->timer, jiffies + timeout))
+- sock_hold(sk);
+-}
+-
+-static void l2cap_sock_clear_timer(struct sock *sk)
+-{
+- DBG("sock %p state %d", sk, sk->state);
+-
+- if (timer_pending(&sk->timer) && del_timer(&sk->timer))
+- __sock_put(sk);
+-}
+-
+-static void l2cap_sock_init_timer(struct sock *sk)
+-{
+- init_timer(&sk->timer);
+- sk->timer.function = l2cap_sock_timeout;
+- sk->timer.data = (unsigned long)sk;
+-}
+-
+-static void l2cap_conn_timeout(unsigned long arg)
+-{
+- struct l2cap_conn *conn = (void *)arg;
+-
+- DBG("conn %p state %d", conn, conn->state);
+-
+- if (conn->state == BT_CONNECTED) {
+- hci_disconnect(conn->hconn, 0x13);
+- }
+-
+- return;
+-}
+-
+-static void l2cap_conn_set_timer(struct l2cap_conn *conn, long timeout)
+-{
+- DBG("conn %p state %d timeout %ld", conn, conn->state, timeout);
+-
+- mod_timer(&conn->timer, jiffies + timeout);
+-}
+-
+-static void l2cap_conn_clear_timer(struct l2cap_conn *conn)
+-{
+- DBG("conn %p state %d", conn, conn->state);
+-
+- del_timer(&conn->timer);
+-}
+-
+-static void l2cap_conn_init_timer(struct l2cap_conn *conn)
+-{
+- init_timer(&conn->timer);
+- conn->timer.function = l2cap_conn_timeout;
+- conn->timer.data = (unsigned long)conn;
+-}
+-
+-/* -------- L2CAP connections --------- */
+-/* Add new connection to the interface.
+- * Interface must be locked
+- */
+-static struct l2cap_conn *l2cap_conn_add(struct l2cap_iff *iff, bdaddr_t *dst)
+-{
+- struct l2cap_conn *conn;
+- bdaddr_t *src = iff->bdaddr;
+-
+- if (!(conn = kmalloc(sizeof(struct l2cap_conn), GFP_KERNEL)))
+- return NULL;
+-
+- memset(conn, 0, sizeof(struct l2cap_conn));
+-
+- conn->state = BT_OPEN;
+- conn->iff = iff;
+- bacpy(&conn->src, src);
+- bacpy(&conn->dst, dst);
+-
+- spin_lock_init(&conn->lock);
+- conn->chan_list.lock = RW_LOCK_UNLOCKED;
+-
+- l2cap_conn_init_timer(conn);
+-
+- __l2cap_conn_link(iff, conn);
+-
+- DBG("%s -> %s, %p", batostr(src), batostr(dst), conn);
+-
+- MOD_INC_USE_COUNT;
+-
+- return conn;
+-}
+-
+-/* Delete connection on the interface.
+- * Interface must be locked
+- */
+-static int l2cap_conn_del(struct l2cap_conn *conn, int err)
+-{
+- struct sock *sk;
+-
+- DBG("conn %p, state %d, err %d", conn, conn->state, err);
+-
+- l2cap_conn_clear_timer(conn);
+- __l2cap_conn_unlink(conn->iff, conn);
+-
+- conn->state = BT_CLOSED;
+-
+- if (conn->rx_skb)
+- kfree_skb(conn->rx_skb);
+-
+- /* Kill channels */
+- while ((sk = conn->chan_list.head)) {
+- bh_lock_sock(sk);
+- l2cap_sock_clear_timer(sk);
+- l2cap_chan_del(sk, err);
+- bh_unlock_sock(sk);
+-
+- l2cap_sock_kill(sk);
+- }
+-
+- kfree(conn);
+-
+- MOD_DEC_USE_COUNT;
+- return 0;
+-}
+-
+-static inline struct l2cap_conn *l2cap_get_conn_by_addr(struct l2cap_iff *iff, bdaddr_t *dst)
+-{
+- struct list_head *p;
+-
+- list_for_each(p, &iff->conn_list) {
+- struct l2cap_conn *c;
+-
+- c = list_entry(p, struct l2cap_conn, list);
+- if (!bacmp(&c->dst, dst))
+- return c;
+- }
+- return NULL;
+-}
+-
+-int l2cap_connect(struct sock *sk)
+-{
+- bdaddr_t *src = &l2cap_pi(sk)->src;
+- bdaddr_t *dst = &l2cap_pi(sk)->dst;
+- struct l2cap_conn *conn;
+- struct l2cap_iff *iff;
+- int err = 0;
+-
+- DBG("%s -> %s psm 0x%2.2x", batostr(src), batostr(dst), l2cap_pi(sk)->psm);
+-
+- read_lock_bh(&l2cap_rt_lock);
+-
+- /* Get route to remote BD address */
+- if (!(iff = l2cap_get_route(src, dst))) {
+- err = -EHOSTUNREACH;
+- goto done;
+- }
+-
+- /* Update source addr of the socket */
+- bacpy(src, iff->bdaddr);
+-
+- l2cap_iff_lock(iff);
+-
+- if (!(conn = l2cap_get_conn_by_addr(iff, dst))) {
+- /* Connection doesn't exist */
+- if (!(conn = l2cap_conn_add(iff, dst))) {
+- l2cap_iff_unlock(iff);
+- err = -ENOMEM;
+- goto done;
+- }
+- conn->out = 1;
+- }
+-
+- l2cap_iff_unlock(iff);
+-
+- l2cap_chan_add(conn, sk, NULL);
+-
+- sk->state = BT_CONNECT;
+- l2cap_sock_set_timer(sk, sk->sndtimeo);
+-
+- switch (conn->state) {
+- case BT_CONNECTED:
+- if (sk->type == SOCK_SEQPACKET) {
+- l2cap_conn_req req;
+- req.scid = __cpu_to_le16(l2cap_pi(sk)->scid);
+- req.psm = l2cap_pi(sk)->psm;
+- l2cap_send_req(conn, L2CAP_CONN_REQ, L2CAP_CONN_REQ_SIZE, &req);
+- } else {
+- l2cap_sock_clear_timer(sk);
+- sk->state = BT_CONNECTED;
+- }
+- break;
+-
+- case BT_CONNECT:
+- break;
+-
+- default:
+- /* Create ACL connection */
+- conn->state = BT_CONNECT;
+- hci_connect(iff->hdev, dst);
+- break;
+- };
+-
+-done:
+- read_unlock_bh(&l2cap_rt_lock);
+- return err;
+-}
+-
+-/* ------ Channel queues for listening sockets ------ */
+-void l2cap_accept_queue(struct sock *parent, struct sock *sk)
+-{
+- struct l2cap_accept_q *q = &l2cap_pi(parent)->accept_q;
+-
+- DBG("parent %p, sk %p", parent, sk);
+-
+- sock_hold(sk);
+- l2cap_pi(sk)->parent = parent;
+- l2cap_pi(sk)->next_q = NULL;
+-
+- if (!q->head) {
+- q->head = q->tail = sk;
+- } else {
+- struct sock *tail = q->tail;
+-
+- l2cap_pi(sk)->prev_q = tail;
+- l2cap_pi(tail)->next_q = sk;
+- q->tail = sk;
+- }
+-
+- parent->ack_backlog++;
+-}
+-
+-void l2cap_accept_unlink(struct sock *sk)
+-{
+- struct sock *parent = l2cap_pi(sk)->parent;
+- struct l2cap_accept_q *q = &l2cap_pi(parent)->accept_q;
+- struct sock *next, *prev;
+-
+- DBG("sk %p", sk);
+-
+- next = l2cap_pi(sk)->next_q;
+- prev = l2cap_pi(sk)->prev_q;
+-
+- if (sk == q->head)
+- q->head = next;
+- if (sk == q->tail)
+- q->tail = prev;
+-
+- if (next)
+- l2cap_pi(next)->prev_q = prev;
+- if (prev)
+- l2cap_pi(prev)->next_q = next;
+-
+- l2cap_pi(sk)->parent = NULL;
+-
+- parent->ack_backlog--;
+- __sock_put(sk);
+-}
+-
+-/* Get next connected channel in queue. */
+-struct sock *l2cap_accept_dequeue(struct sock *parent, int state)
+-{
+- struct l2cap_accept_q *q = &l2cap_pi(parent)->accept_q;
+- struct sock *sk;
+-
+- for (sk = q->head; sk; sk = l2cap_pi(sk)->next_q) {
+- if (!state || sk->state == state) {
+- l2cap_accept_unlink(sk);
+- break;
+- }
+- }
+-
+- DBG("parent %p, sk %p", parent, sk);
+-
+- return sk;
+-}
+-
+-/* -------- Socket interface ---------- */
+-static struct sock *__l2cap_get_sock_by_addr(struct sockaddr_l2 *addr)
+-{
+- bdaddr_t *src = &addr->l2_bdaddr;
+- __u16 psm = addr->l2_psm;
+- struct sock *sk;
+-
+- for (sk = l2cap_sk_list.head; sk; sk = sk->next) {
+- if (l2cap_pi(sk)->psm == psm &&
+- !bacmp(&l2cap_pi(sk)->src, src))
+- break;
+- }
+-
+- return sk;
+-}
+-
+-/* Find socket listening on psm and source bdaddr.
+- * Returns closest match.
+- */
+-static struct sock *l2cap_get_sock_listen(bdaddr_t *src, __u16 psm)
+-{
+- struct sock *sk, *sk1 = NULL;
+-
+- read_lock(&l2cap_sk_list.lock);
+-
+- for (sk = l2cap_sk_list.head; sk; sk = sk->next) {
+- struct l2cap_pinfo *pi;
+-
+- if (sk->state != BT_LISTEN)
+- continue;
+-
+- pi = l2cap_pi(sk);
+-
+- if (pi->psm == psm) {
+- /* Exact match. */
+- if (!bacmp(&pi->src, src))
+- break;
+-
+- /* Closest match */
+- if (!bacmp(&pi->src, BDADDR_ANY))
+- sk1 = sk;
+- }
+- }
+-
+- read_unlock(&l2cap_sk_list.lock);
+-
+- return sk ? sk : sk1;
+-}
+-
+-static void l2cap_sock_destruct(struct sock *sk)
+-{
+- DBG("sk %p", sk);
+-
+- skb_queue_purge(&sk->receive_queue);
+- skb_queue_purge(&sk->write_queue);
+-
+- MOD_DEC_USE_COUNT;
+-}
+-
+-static void l2cap_sock_cleanup_listen(struct sock *parent)
+-{
+- struct sock *sk;
+-
+- DBG("parent %p", parent);
+-
+- /* Close not yet accepted channels */
+- while ((sk = l2cap_accept_dequeue(parent, 0)))
+- l2cap_sock_close(sk);
+-
+- parent->state = BT_CLOSED;
+- parent->zapped = 1;
+-}
+-
+-/* Kill socket (only if zapped and orphan)
+- * Must be called on unlocked socket.
+- */
+-static void l2cap_sock_kill(struct sock *sk)
+-{
+- if (!sk->zapped || sk->socket)
+- return;
+-
+- DBG("sk %p state %d", sk, sk->state);
+-
+- /* Kill poor orphan */
+- bluez_sock_unlink(&l2cap_sk_list, sk);
+- sk->dead = 1;
+- sock_put(sk);
+-}
+-
+-/* Close socket.
+- * Must be called on unlocked socket.
+- */
+-static void l2cap_sock_close(struct sock *sk)
+-{
+- struct l2cap_conn *conn;
+-
+- l2cap_sock_clear_timer(sk);
+-
+- lock_sock(sk);
+-
+- conn = l2cap_pi(sk)->conn;
+-
+- DBG("sk %p state %d conn %p socket %p", sk, sk->state, conn, sk->socket);
+-
+- switch (sk->state) {
+- case BT_LISTEN:
+- l2cap_sock_cleanup_listen(sk);
+- break;
+-
+- case BT_CONNECTED:
+- case BT_CONFIG:
+- if (sk->type == SOCK_SEQPACKET) {
+- l2cap_disconn_req req;
+-
+- sk->state = BT_DISCONN;
+-
+- req.dcid = __cpu_to_le16(l2cap_pi(sk)->dcid);
+- req.scid = __cpu_to_le16(l2cap_pi(sk)->scid);
+- l2cap_send_req(conn, L2CAP_DISCONN_REQ, L2CAP_DISCONN_REQ_SIZE, &req);
+-
+- l2cap_sock_set_timer(sk, sk->sndtimeo);
+- } else {
+- l2cap_chan_del(sk, ECONNRESET);
+- }
+- break;
+-
+- case BT_CONNECT:
+- case BT_DISCONN:
+- l2cap_chan_del(sk, ECONNRESET);
+- break;
+-
+- default:
+- sk->zapped = 1;
+- break;
+- };
+-
+- release_sock(sk);
+-
+- l2cap_sock_kill(sk);
+-}
+-
+-static void l2cap_sock_init(struct sock *sk, struct sock *parent)
+-{
+- struct l2cap_pinfo *pi = l2cap_pi(sk);
+-
+- DBG("sk %p", sk);
+-
+- if (parent) {
+- sk->type = parent->type;
+-
+- pi->imtu = l2cap_pi(parent)->imtu;
+- pi->omtu = l2cap_pi(parent)->omtu;
+- } else {
+- pi->imtu = L2CAP_DEFAULT_MTU;
+- pi->omtu = 0;
+- }
+-
+- /* Default config options */
+- pi->conf_mtu = L2CAP_DEFAULT_MTU;
+- pi->flush_to = L2CAP_DEFAULT_FLUSH_TO;
+-}
+-
+-static struct sock *l2cap_sock_alloc(struct socket *sock, int proto, int prio)
+-{
+- struct sock *sk;
+-
+- if (!(sk = sk_alloc(PF_BLUETOOTH, prio, 1)))
+- return NULL;
+-
+- sock_init_data(sock, sk);
+-
+- sk->zapped = 0;
+-
+- sk->destruct = l2cap_sock_destruct;
+- sk->sndtimeo = L2CAP_CONN_TIMEOUT;
+-
+- sk->protocol = proto;
+- sk->state = BT_OPEN;
+-
+- l2cap_sock_init_timer(sk);
+-
+- bluez_sock_link(&l2cap_sk_list, sk);
+-
+- MOD_INC_USE_COUNT;
+-
+- return sk;
+-}
+-
+-static int l2cap_sock_create(struct socket *sock, int protocol)
+-{
+- struct sock *sk;
+-
+- DBG("sock %p", sock);
+-
+- sock->state = SS_UNCONNECTED;
+-
+- if (sock->type != SOCK_SEQPACKET && sock->type != SOCK_RAW)
+- return -ESOCKTNOSUPPORT;
+-
+- sock->ops = &l2cap_sock_ops;
+-
+- if (!(sk = l2cap_sock_alloc(sock, protocol, GFP_KERNEL)))
+- return -ENOMEM;
+-
+- l2cap_sock_init(sk, NULL);
+-
+- return 0;
+-}
+-
+-static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len)
+-{
+- struct sockaddr_l2 *la = (struct sockaddr_l2 *) addr;
+- struct sock *sk = sock->sk;
+- int err = 0;
+-
+- DBG("sk %p, %s %d", sk, batostr(&la->l2_bdaddr), la->l2_psm);
+-
+- if (!addr || addr->sa_family != AF_BLUETOOTH)
+- return -EINVAL;
+-
+- lock_sock(sk);
+-
+- if (sk->state != BT_OPEN) {
+- err = -EBADFD;
+- goto done;
+- }
+-
+- write_lock(&l2cap_sk_list.lock);
+-
+- if (la->l2_psm && __l2cap_get_sock_by_addr(la)) {
+- err = -EADDRINUSE;
+- goto unlock;
+- }
+-
+- /* Save source address */
+- bacpy(&l2cap_pi(sk)->src, &la->l2_bdaddr);
+- l2cap_pi(sk)->psm = la->l2_psm;
+- sk->state = BT_BOUND;
+-
+-unlock:
+- write_unlock(&l2cap_sk_list.lock);
+-
+-done:
+- release_sock(sk);
+-
+- return err;
+-}
+-
+-static int l2cap_sock_w4_connect(struct sock *sk, int flags)
+-{
+- DECLARE_WAITQUEUE(wait, current);
+- long timeo = sock_sndtimeo(sk, flags & O_NONBLOCK);
+- int err = 0;
+-
+- DBG("sk %p", sk);
+-
+- add_wait_queue(sk->sleep, &wait);
+- current->state = TASK_INTERRUPTIBLE;
+-
+- while (sk->state != BT_CONNECTED) {
+- if (!timeo) {
+- err = -EAGAIN;
+- break;
+- }
+-
+- release_sock(sk);
+- timeo = schedule_timeout(timeo);
+- lock_sock(sk);
+-
+- err = 0;
+- if (sk->state == BT_CONNECTED)
+- break;
+-
+- if (sk->err) {
+- err = sock_error(sk);
+- break;
+- }
+-
+- if (signal_pending(current)) {
+- err = sock_intr_errno(timeo);
+- break;
+- }
+- }
+- current->state = TASK_RUNNING;
+- remove_wait_queue(sk->sleep, &wait);
+-
+- return err;
+-}
+-
+-static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int alen, int flags)
+-{
+- struct sockaddr_l2 *la = (struct sockaddr_l2 *) addr;
+- struct sock *sk = sock->sk;
+- int err = 0;
+-
+- lock_sock(sk);
+-
+- DBG("sk %p", sk);
+-
+- if (addr->sa_family != AF_BLUETOOTH || alen < sizeof(struct sockaddr_l2)) {
+- err = -EINVAL;
+- goto done;
+- }
+-
+- if (sk->state != BT_OPEN && sk->state != BT_BOUND) {
+- err = -EBADFD;
+- goto done;
+- }
+-
+- if (sk->type == SOCK_SEQPACKET && !la->l2_psm) {
+- err = -EINVAL;
+- goto done;
+- }
+-
+- /* Set destination address and psm */
+- bacpy(&l2cap_pi(sk)->dst, &la->l2_bdaddr);
+- l2cap_pi(sk)->psm = la->l2_psm;
+-
+- if ((err = l2cap_connect(sk)))
+- goto done;
+-
+- err = l2cap_sock_w4_connect(sk, flags);
+-
+-done:
+- release_sock(sk);
+- return err;
+-}
+-
+-int l2cap_sock_listen(struct socket *sock, int backlog)
+-{
+- struct sock *sk = sock->sk;
+- int err = 0;
+-
+- DBG("sk %p backlog %d", sk, backlog);
+-
+- lock_sock(sk);
+-
+- if (sk->state != BT_BOUND || sock->type != SOCK_SEQPACKET) {
+- err = -EBADFD;
+- goto done;
+- }
+-
+- if (!l2cap_pi(sk)->psm) {
+- err = -EINVAL;
+- goto done;
+- }
+-
+- sk->max_ack_backlog = backlog;
+- sk->ack_backlog = 0;
+- sk->state = BT_LISTEN;
+-
+-done:
+- release_sock(sk);
+- return err;
+-}
+-
+-int l2cap_sock_accept(struct socket *sock, struct socket *newsock, int flags)
+-{
+- DECLARE_WAITQUEUE(wait, current);
+- struct sock *sk = sock->sk, *ch;
+- long timeo;
+- int err = 0;
+-
+- lock_sock(sk);
+-
+- if (sk->state != BT_LISTEN) {
+- err = -EBADFD;
+- goto done;
+- }
+-
+- timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK);
+-
+- DBG("sk %p timeo %ld", sk, timeo);
+-
+- /* Wait for an incoming connection. (wake-one). */
+- add_wait_queue_exclusive(sk->sleep, &wait);
+- current->state = TASK_INTERRUPTIBLE;
+- while (!(ch = l2cap_accept_dequeue(sk, BT_CONNECTED))) {
+- if (!timeo) {
+- err = -EAGAIN;
+- break;
+- }
+-
+- release_sock(sk);
+- timeo = schedule_timeout(timeo);
+- lock_sock(sk);
+-
+- if (sk->state != BT_LISTEN) {
+- err = -EBADFD;
+- break;
+- }
+-
+- if (signal_pending(current)) {
+- err = sock_intr_errno(timeo);
+- break;
+- }
+- }
+- current->state = TASK_RUNNING;
+- remove_wait_queue(sk->sleep, &wait);
+-
+- if (err)
+- goto done;
+-
+- sock_graft(ch, newsock);
+- newsock->state = SS_CONNECTED;
+-
+- DBG("new socket %p", ch);
+-
+-done:
+- release_sock(sk);
+-
+- return err;
+-}
+-
+-static int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr, int *len, int peer)
+-{
+- struct sockaddr_l2 *la = (struct sockaddr_l2 *) addr;
+- struct sock *sk = sock->sk;
+-
+- DBG("sock %p, sk %p", sock, sk);
+-
+- addr->sa_family = AF_BLUETOOTH;
+- *len = sizeof(struct sockaddr_l2);
+-
+- if (peer)
+- bacpy(&la->l2_bdaddr, &l2cap_pi(sk)->dst);
+- else
+- bacpy(&la->l2_bdaddr, &l2cap_pi(sk)->src);
+-
+- la->l2_psm = l2cap_pi(sk)->psm;
+-
+- return 0;
+-}
+-
+-static int l2cap_sock_sendmsg(struct socket *sock, struct msghdr *msg, int len, struct scm_cookie *scm)
+-{
+- struct sock *sk = sock->sk;
+- int err = 0;
+-
+- DBG("sock %p, sk %p", sock, sk);
+-
+- if (sk->err)
+- return sock_error(sk);
+-
+- if (msg->msg_flags & MSG_OOB)
+- return -EOPNOTSUPP;
+-
+- lock_sock(sk);
+-
+- if (sk->state == BT_CONNECTED)
+- err = l2cap_chan_send(sk, msg, len);
+- else
+- err = -ENOTCONN;
+-
+- release_sock(sk);
+- return err;
+-}
+-
+-static int l2cap_sock_recvmsg(struct socket *sock, struct msghdr *msg, int len, int flags, struct scm_cookie *scm)
+-{
+- struct sock *sk = sock->sk;
+- int noblock = flags & MSG_DONTWAIT;
+- int copied, err;
+- struct sk_buff *skb;
+-
+- DBG("sock %p, sk %p", sock, sk);
+-
+- if (flags & (MSG_OOB))
+- return -EOPNOTSUPP;
+-
+- if (sk->state == BT_CLOSED)
+- return 0;
+-
+- if (!(skb = skb_recv_datagram(sk, flags, noblock, &err)))
+- return err;
+-
+- msg->msg_namelen = 0;
+-
+- copied = skb->len;
+- if (len < copied) {
+- msg->msg_flags |= MSG_TRUNC;
+- copied = len;
+- }
+-
+- skb->h.raw = skb->data;
+- err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
+-
+- skb_free_datagram(sk, skb);
+-
+- return err ? : copied;
+-}
+-
+-int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen)
+-{
+- struct sock *sk = sock->sk;
+- struct l2cap_options opts;
+- int err = 0;
+-
+- DBG("sk %p", sk);
+-
+- lock_sock(sk);
+-
+- switch (optname) {
+- case L2CAP_OPTIONS:
+- if (copy_from_user((char *)&opts, optval, optlen)) {
+- err = -EFAULT;
+- break;
+- }
+- l2cap_pi(sk)->imtu = opts.imtu;
+- l2cap_pi(sk)->omtu = opts.omtu;
+- break;
+-
+- default:
+- err = -ENOPROTOOPT;
+- break;
+- };
+-
+- release_sock(sk);
+- return err;
+-}
+-
+-int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, char *optval, int *optlen)
+-{
+- struct sock *sk = sock->sk;
+- struct l2cap_options opts;
+- struct l2cap_conninfo cinfo;
+- int len, err = 0;
+-
+- if (get_user(len, optlen))
+- return -EFAULT;
+-
+- lock_sock(sk);
+-
+- switch (optname) {
+- case L2CAP_OPTIONS:
+- opts.imtu = l2cap_pi(sk)->imtu;
+- opts.omtu = l2cap_pi(sk)->omtu;
+- opts.flush_to = l2cap_pi(sk)->flush_to;
+-
+- len = MIN(len, sizeof(opts));
+- if (copy_to_user(optval, (char *)&opts, len))
+- err = -EFAULT;
+-
+- break;
+-
+- case L2CAP_CONNINFO:
+- if (sk->state != BT_CONNECTED) {
+- err = -ENOTCONN;
+- break;
+- }
+-
+- cinfo.hci_handle = l2cap_pi(sk)->conn->hconn->handle;
+-
+- len = MIN(len, sizeof(cinfo));
+- if (copy_to_user(optval, (char *)&cinfo, len))
+- err = -EFAULT;
+-
+- break;
+-
+- default:
+- err = -ENOPROTOOPT;
+- break;
+- };
+-
+- release_sock(sk);
+- return err;
+-}
+-
+-static unsigned int l2cap_sock_poll(struct file * file, struct socket *sock, poll_table *wait)
+-{
+- struct sock *sk = sock->sk;
+- struct l2cap_accept_q *aq;
+- unsigned int mask;
+-
+- DBG("sock %p, sk %p", sock, sk);
+-
+- poll_wait(file, sk->sleep, wait);
+- mask = 0;
+-
+- if (sk->err || !skb_queue_empty(&sk->error_queue))
+- mask |= POLLERR;
+-
+- if (sk->shutdown == SHUTDOWN_MASK)
+- mask |= POLLHUP;
+-
+- aq = &l2cap_pi(sk)->accept_q;
+- if (!skb_queue_empty(&sk->receive_queue) || aq->head || (sk->shutdown & RCV_SHUTDOWN))
+- mask |= POLLIN | POLLRDNORM;
+-
+- if (sk->state == BT_CLOSED)
+- mask |= POLLHUP;
+-
+- if (sock_writeable(sk))
+- mask |= POLLOUT | POLLWRNORM | POLLWRBAND;
+- else
+- set_bit(SOCK_ASYNC_NOSPACE, &sk->socket->flags);
+-
+- return mask;
+-}
+-
+-static int l2cap_sock_release(struct socket *sock)
+-{
+- struct sock *sk = sock->sk;
+-
+- DBG("sock %p, sk %p", sock, sk);
+-
+- if (!sk)
+- return 0;
+-
+- sock_orphan(sk);
+-
+- l2cap_sock_close(sk);
+-
+- return 0;
+-}
+-
+-/* --------- L2CAP channels --------- */
+-static struct sock * __l2cap_get_chan_by_dcid(struct l2cap_chan_list *l, __u16 cid)
+-{
+- struct sock *s;
+-
+- for (s = l->head; s; s = l2cap_pi(s)->next_c) {
+- if (l2cap_pi(s)->dcid == cid)
+- break;
+- }
+-
+- return s;
+-}
+-
+-static inline struct sock *l2cap_get_chan_by_dcid(struct l2cap_chan_list *l, __u16 cid)
+-{
+- struct sock *s;
+-
+- read_lock(&l->lock);
+- s = __l2cap_get_chan_by_dcid(l, cid);
+- read_unlock(&l->lock);
+-
+- return s;
+-}
+-
+-static struct sock *__l2cap_get_chan_by_scid(struct l2cap_chan_list *l, __u16 cid)
+-{
+- struct sock *s;
+-
+- for (s = l->head; s; s = l2cap_pi(s)->next_c) {
+- if (l2cap_pi(s)->scid == cid)
+- break;
+- }
+-
+- return s;
+-}
+-static inline struct sock *l2cap_get_chan_by_scid(struct l2cap_chan_list *l, __u16 cid)
+-{
+- struct sock *s;
+-
+- read_lock(&l->lock);
+- s = __l2cap_get_chan_by_scid(l, cid);
+- read_unlock(&l->lock);
+-
+- return s;
+-}
+-
+-static struct sock *__l2cap_get_chan_by_ident(struct l2cap_chan_list *l, __u8 ident)
+-{
+- struct sock *s;
+-
+- for (s = l->head; s; s = l2cap_pi(s)->next_c) {
+- if (l2cap_pi(s)->ident == ident)
+- break;
+- }
+-
+- return s;
+-}
+-
+-static inline struct sock *l2cap_get_chan_by_ident(struct l2cap_chan_list *l, __u8 ident)
+-{
+- struct sock *s;
+-
+- read_lock(&l->lock);
+- s = __l2cap_get_chan_by_ident(l, ident);
+- read_unlock(&l->lock);
+-
+- return s;
+-}
+-
+-static __u16 l2cap_alloc_cid(struct l2cap_chan_list *l)
+-{
+- __u16 cid = 0x0040;
+-
+- for (; cid < 0xffff; cid++) {
+- if(!__l2cap_get_chan_by_scid(l, cid))
+- return cid;
+- }
+-
+- return 0;
+-}
+-
+-static inline void __l2cap_chan_link(struct l2cap_chan_list *l, struct sock *sk)
+-{
+- sock_hold(sk);
+-
+- if (l->head)
+- l2cap_pi(l->head)->prev_c = sk;
+-
+- l2cap_pi(sk)->next_c = l->head;
+- l2cap_pi(sk)->prev_c = NULL;
+- l->head = sk;
+-}
+-
+-static inline void l2cap_chan_unlink(struct l2cap_chan_list *l, struct sock *sk)
+-{
+- struct sock *next = l2cap_pi(sk)->next_c, *prev = l2cap_pi(sk)->prev_c;
+-
+- write_lock(&l->lock);
+- if (sk == l->head)
+- l->head = next;
+-
+- if (next)
+- l2cap_pi(next)->prev_c = prev;
+- if (prev)
+- l2cap_pi(prev)->next_c = next;
+- write_unlock(&l->lock);
+-
+- __sock_put(sk);
+-}
+-
+-static void __l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct sock *parent)
+-{
+- struct l2cap_chan_list *l = &conn->chan_list;
+-
+- DBG("conn %p, psm 0x%2.2x, dcid 0x%4.4x", conn, l2cap_pi(sk)->psm, l2cap_pi(sk)->dcid);
+-
+- l2cap_conn_clear_timer(conn);
+-
+- atomic_inc(&conn->refcnt);
+- l2cap_pi(sk)->conn = conn;
+-
+- if (sk->type == SOCK_SEQPACKET) {
+- /* Alloc CID for normal socket */
+- l2cap_pi(sk)->scid = l2cap_alloc_cid(l);
+- } else {
+- /* Raw socket can send only signalling messages */
+- l2cap_pi(sk)->scid = 0x0001;
+- l2cap_pi(sk)->dcid = 0x0001;
+- l2cap_pi(sk)->omtu = L2CAP_DEFAULT_MTU;
+- }
+-
+- __l2cap_chan_link(l, sk);
+-
+- if (parent)
+- l2cap_accept_queue(parent, sk);
+-}
+-
+-static inline void l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct sock *parent)
+-{
+- struct l2cap_chan_list *l = &conn->chan_list;
+-
+- write_lock(&l->lock);
+- __l2cap_chan_add(conn, sk, parent);
+- write_unlock(&l->lock);
+-}
+-
+-/* Delete channel.
+- * Must be called on the locked socket. */
+-static void l2cap_chan_del(struct sock *sk, int err)
+-{
+- struct l2cap_conn *conn;
+- struct sock *parent;
+-
+- conn = l2cap_pi(sk)->conn;
+- parent = l2cap_pi(sk)->parent;
+-
+- DBG("sk %p, conn %p, err %d", sk, conn, err);
+-
+- if (parent) {
+- /* Unlink from parent accept queue */
+- bh_lock_sock(parent);
+- l2cap_accept_unlink(sk);
+- bh_unlock_sock(parent);
+- }
+-
+- if (conn) {
+- long timeout;
+-
+- /* Unlink from channel list */
+- l2cap_chan_unlink(&conn->chan_list, sk);
+- l2cap_pi(sk)->conn = NULL;
+-
+- if (conn->out)
+- timeout = L2CAP_DISCONN_TIMEOUT;
+- else
+- timeout = L2CAP_CONN_IDLE_TIMEOUT;
+-
+- if (atomic_dec_and_test(&conn->refcnt) && conn->state == BT_CONNECTED) {
+- /* Schedule Baseband disconnect */
+- l2cap_conn_set_timer(conn, timeout);
+- }
+- }
+-
+- sk->state = BT_CLOSED;
+- sk->err = err;
+- sk->state_change(sk);
+-
+- sk->zapped = 1;
+-}
+-
+-static void l2cap_conn_ready(struct l2cap_conn *conn)
+-{
+- struct l2cap_chan_list *l = &conn->chan_list;
+- struct sock *sk;
+-
+- DBG("conn %p", conn);
+-
+- read_lock(&l->lock);
+-
+- for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) {
+- bh_lock_sock(sk);
+-
+- if (sk->type != SOCK_SEQPACKET) {
+- sk->state = BT_CONNECTED;
+- sk->state_change(sk);
+- l2cap_sock_clear_timer(sk);
+- } else if (sk->state == BT_CONNECT) {
+- l2cap_conn_req req;
+- req.scid = __cpu_to_le16(l2cap_pi(sk)->scid);
+- req.psm = l2cap_pi(sk)->psm;
+- l2cap_send_req(conn, L2CAP_CONN_REQ, L2CAP_CONN_REQ_SIZE, &req);
+-
+- l2cap_sock_set_timer(sk, sk->sndtimeo);
+- }
+-
+- bh_unlock_sock(sk);
+- }
+-
+- read_unlock(&l->lock);
+-}
+-
+-static void l2cap_chan_ready(struct sock *sk)
+-{
+- struct sock *parent = l2cap_pi(sk)->parent;
+-
+- DBG("sk %p, parent %p", sk, parent);
+-
+- l2cap_pi(sk)->conf_state = 0;
+- l2cap_sock_clear_timer(sk);
+-
+- if (!parent) {
+- /* Outgoing channel.
+- * Wake up socket sleeping on connect.
+- */
+- sk->state = BT_CONNECTED;
+- sk->state_change(sk);
+- } else {
+- /* Incomming channel.
+- * Wake up socket sleeping on accept.
+- */
+- parent->data_ready(parent, 1);
+- }
+-}
+-
+-/* Copy frame to all raw sockets on that connection */
+-void l2cap_raw_recv(struct l2cap_conn *conn, struct sk_buff *skb)
+-{
+- struct l2cap_chan_list *l = &conn->chan_list;
+- struct sk_buff *nskb;
+- struct sock * sk;
+-
+- DBG("conn %p", conn);
+-
+- read_lock(&l->lock);
+- for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) {
+- if (sk->type != SOCK_RAW)
+- continue;
+-
+- /* Don't send frame to the socket it came from */
+- if (skb->sk == sk)
+- continue;
+-
+- if (!(nskb = skb_clone(skb, GFP_ATOMIC)))
+- continue;
+-
+- skb_queue_tail(&sk->receive_queue, nskb);
+- sk->data_ready(sk, nskb->len);
+- }
+- read_unlock(&l->lock);
+-}
+-
+-static int l2cap_chan_send(struct sock *sk, struct msghdr *msg, int len)
+-{
+- struct l2cap_conn *conn = l2cap_pi(sk)->conn;
+- struct sk_buff *skb, **frag;
+- int err, size, count, sent=0;
+- l2cap_hdr *lh;
+-
+- /* Check outgoing MTU */
+- if (len > l2cap_pi(sk)->omtu)
+- return -EINVAL;
+-
+- DBG("sk %p len %d", sk, len);
+-
+- /* First fragment (with L2CAP header) */
+- count = MIN(conn->iff->mtu - L2CAP_HDR_SIZE, len);
+- size = L2CAP_HDR_SIZE + count;
+- if (!(skb = bluez_skb_send_alloc(sk, size, msg->msg_flags & MSG_DONTWAIT, &err)))
+- return err;
+-
+- /* Create L2CAP header */
+- lh = (l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE);
+- lh->len = __cpu_to_le16(len);
+- lh->cid = __cpu_to_le16(l2cap_pi(sk)->dcid);
+-
+- if (memcpy_fromiovec(skb_put(skb, count), msg->msg_iov, count)) {
+- err = -EFAULT;
+- goto fail;
+- }
+-
+- sent += count;
+- len -= count;
+-
+- /* Continuation fragments (no L2CAP header) */
+- frag = &skb_shinfo(skb)->frag_list;
+- while (len) {
+- count = MIN(conn->iff->mtu, len);
+-
+- *frag = bluez_skb_send_alloc(sk, count, msg->msg_flags & MSG_DONTWAIT, &err);
+- if (!*frag)
+- goto fail;
+-
+- if (memcpy_fromiovec(skb_put(*frag, count), msg->msg_iov, count)) {
+- err = -EFAULT;
+- goto fail;
+- }
+-
+- sent += count;
+- len -= count;
+-
+- frag = &(*frag)->next;
+- }
+-
+- if ((err = hci_send_acl(conn->hconn, skb, 0)) < 0)
+- goto fail;
+-
+- return sent;
+-
+-fail:
+- kfree_skb(skb);
+- return err;
+-}
+-
+-/* --------- L2CAP signalling commands --------- */
+-static inline __u8 l2cap_get_ident(struct l2cap_conn *conn)
+-{
+- __u8 id;
+-
+- /* Get next available identificator.
+- * 1 - 199 are used by kernel.
+- * 200 - 254 are used by utilities like l2ping, etc
+- */
+-
+- spin_lock(&conn->lock);
+-
+- if (++conn->tx_ident > 199)
+- conn->tx_ident = 1;
+-
+- id = conn->tx_ident;
+-
+- spin_unlock(&conn->lock);
+-
+- return id;
+-}
+-
+-static inline struct sk_buff *l2cap_build_cmd(__u8 code, __u8 ident, __u16 len, void *data)
+-{
+- struct sk_buff *skb;
+- l2cap_cmd_hdr *cmd;
+- l2cap_hdr *lh;
+- int size;
+-
+- DBG("code 0x%2.2x, ident 0x%2.2x, len %d", code, ident, len);
+-
+- size = L2CAP_HDR_SIZE + L2CAP_CMD_HDR_SIZE + len;
+- if (!(skb = bluez_skb_alloc(size, GFP_ATOMIC)))
+- return NULL;
+-
+- lh = (l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE);
+- lh->len = __cpu_to_le16(L2CAP_CMD_HDR_SIZE + len);
+- lh->cid = __cpu_to_le16(0x0001);
+-
+- cmd = (l2cap_cmd_hdr *) skb_put(skb, L2CAP_CMD_HDR_SIZE);
+- cmd->code = code;
+- cmd->ident = ident;
+- cmd->len = __cpu_to_le16(len);
+-
+- if (len)
+- memcpy(skb_put(skb, len), data, len);
+-
+- return skb;
+-}
+-
+-static int l2cap_send_req(struct l2cap_conn *conn, __u8 code, __u16 len, void *data)
+-{
+- struct sk_buff *skb;
+- __u8 ident;
+-
+- DBG("code 0x%2.2x", code);
+-
+- ident = l2cap_get_ident(conn);
+- if (!(skb = l2cap_build_cmd(code, ident, len, data)))
+- return -ENOMEM;
+- return hci_send_acl(conn->hconn, skb, 0);
+-}
+-
+-static int l2cap_send_rsp(struct l2cap_conn *conn, __u8 ident, __u8 code, __u16 len, void *data)
+-{
+- struct sk_buff *skb;
+-
+- DBG("code 0x%2.2x", code);
+-
+- if (!(skb = l2cap_build_cmd(code, ident, len, data)))
+- return -ENOMEM;
+- return hci_send_acl(conn->hconn, skb, 0);
+-}
+-
+-static inline int l2cap_get_conf_opt(__u8 **ptr, __u8 *type, __u32 *val)
+-{
+- l2cap_conf_opt *opt = (l2cap_conf_opt *) (*ptr);
+- int len;
+-
+- *type = opt->type;
+- switch (opt->len) {
+- case 1:
+- *val = *((__u8 *) opt->val);
+- break;
+-
+- case 2:
+- *val = __le16_to_cpu(*((__u16 *)opt->val));
+- break;
+-
+- case 4:
+- *val = __le32_to_cpu(*((__u32 *)opt->val));
+- break;
+-
+- default:
+- *val = 0L;
+- break;
+- };
+-
+- DBG("type 0x%2.2x len %d val 0x%8.8x", *type, opt->len, *val);
+-
+- len = L2CAP_CONF_OPT_SIZE + opt->len;
+-
+- *ptr += len;
+-
+- return len;
+-}
+-
+-static inline void l2cap_parse_conf_req(struct sock *sk, char *data, int len)
+-{
+- __u8 type, hint; __u32 val;
+- __u8 *ptr = data;
+-
+- DBG("sk %p len %d", sk, len);
+-
+- while (len >= L2CAP_CONF_OPT_SIZE) {
+- len -= l2cap_get_conf_opt(&ptr, &type, &val);
+-
+- hint = type & 0x80;
+- type &= 0x7f;
+-
+- switch (type) {
+- case L2CAP_CONF_MTU:
+- l2cap_pi(sk)->conf_mtu = val;
+- break;
+-
+- case L2CAP_CONF_FLUSH_TO:
+- l2cap_pi(sk)->flush_to = val;
+- break;
+-
+- case L2CAP_CONF_QOS:
+- break;
+-
+- default:
+- if (hint)
+- break;
+-
+- /* FIXME: Reject unknon option */
+- break;
+- };
+- }
+-}
+-
+-static inline void l2cap_add_conf_opt(__u8 **ptr, __u8 type, __u8 len, __u32 val)
+-{
+- register l2cap_conf_opt *opt = (l2cap_conf_opt *) (*ptr);
+-
+- DBG("type 0x%2.2x len %d val 0x%8.8x", type, len, val);
+-
+- opt->type = type;
+- opt->len = len;
+- switch (len) {
+- case 1:
+- *((__u8 *) opt->val) = val;
+- break;
+-
+- case 2:
+- *((__u16 *) opt->val) = __cpu_to_le16(val);
+- break;
+-
+- case 4:
+- *((__u32 *) opt->val) = __cpu_to_le32(val);
+- break;
+- };
+-
+- *ptr += L2CAP_CONF_OPT_SIZE + len;
+-}
+-
+-static int l2cap_build_conf_req(struct sock *sk, __u8 *data)
+-{
+- struct l2cap_pinfo *pi = l2cap_pi(sk);
+- l2cap_conf_req *req = (l2cap_conf_req *) data;
+- __u8 *ptr = req->data;
+-
+- DBG("sk %p", sk);
+-
+- if (pi->imtu != L2CAP_DEFAULT_MTU)
+- l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, pi->imtu);
+-
+- /* FIXME. Need actual value of the flush timeout */
+- //if (flush_to != L2CAP_DEFAULT_FLUSH_TO)
+- // l2cap_add_conf_opt(&ptr, L2CAP_CONF_FLUSH_TO, 2, pi->flush_to);
+-
+- req->dcid = __cpu_to_le16(pi->dcid);
+- req->flags = __cpu_to_le16(0);
+-
+- return ptr - data;
+-}
+-
+-static int l2cap_conf_output(struct sock *sk, __u8 **ptr)
+-{
+- struct l2cap_pinfo *pi = l2cap_pi(sk);
+- int result = 0;
+-
+- /* Configure output options and let other side know
+- * which ones we don't like.
+- */
+- if (pi->conf_mtu < pi->omtu) {
+- l2cap_add_conf_opt(ptr, L2CAP_CONF_MTU, 2, l2cap_pi(sk)->omtu);
+- result = L2CAP_CONF_UNACCEPT;
+- } else {
+- pi->omtu = pi->conf_mtu;
+- }
+-
+- DBG("sk %p result %d", sk, result);
+- return result;
+-}
+-
+-static int l2cap_build_conf_rsp(struct sock *sk, __u8 *data, int *result)
+-{
+- l2cap_conf_rsp *rsp = (l2cap_conf_rsp *) data;
+- __u8 *ptr = rsp->data;
+-
+- DBG("sk %p complete %d", sk, result ? 1 : 0);
+-
+- if (result)
+- *result = l2cap_conf_output(sk, &ptr);
+-
+- rsp->scid = __cpu_to_le16(l2cap_pi(sk)->dcid);
+- rsp->result = __cpu_to_le16(result ? *result : 0);
+- rsp->flags = __cpu_to_le16(0);
+-
+- return ptr - data;
+-}
+-
+-static inline int l2cap_connect_req(struct l2cap_conn *conn, l2cap_cmd_hdr *cmd, __u8 *data)
+-{
+- struct l2cap_chan_list *list = &conn->chan_list;
+- l2cap_conn_req *req = (l2cap_conn_req *) data;
+- l2cap_conn_rsp rsp;
+- struct sock *sk, *parent;
+-
+- __u16 scid = __le16_to_cpu(req->scid);
+- __u16 psm = req->psm;
+-
+- DBG("psm 0x%2.2x scid 0x%4.4x", psm, scid);
+-
+- /* Check if we have socket listening on psm */
+- if (!(parent = l2cap_get_sock_listen(&conn->src, psm)))
+- goto reject;
+-
+- bh_lock_sock(parent);
+- write_lock(&list->lock);
+-
+- /* Check if we already have channel with that dcid */
+- if (__l2cap_get_chan_by_dcid(list, scid))
+- goto unlock;
+-
+- /* Check for backlog size */
+- if (parent->ack_backlog > parent->max_ack_backlog)
+- goto unlock;
+-
+- if (!(sk = l2cap_sock_alloc(NULL, BTPROTO_L2CAP, GFP_ATOMIC)))
+- goto unlock;
+-
+- l2cap_sock_init(sk, parent);
+-
+- bacpy(&l2cap_pi(sk)->src, &conn->src);
+- bacpy(&l2cap_pi(sk)->dst, &conn->dst);
+- l2cap_pi(sk)->psm = psm;
+- l2cap_pi(sk)->dcid = scid;
+-
+- __l2cap_chan_add(conn, sk, parent);
+- sk->state = BT_CONFIG;
+-
+- write_unlock(&list->lock);
+- bh_unlock_sock(parent);
+-
+- rsp.dcid = __cpu_to_le16(l2cap_pi(sk)->scid);
+- rsp.scid = __cpu_to_le16(l2cap_pi(sk)->dcid);
+- rsp.result = __cpu_to_le16(0);
+- rsp.status = __cpu_to_le16(0);
+- l2cap_send_rsp(conn, cmd->ident, L2CAP_CONN_RSP, L2CAP_CONN_RSP_SIZE, &rsp);
+-
+- return 0;
+-
+-unlock:
+- write_unlock(&list->lock);
+- bh_unlock_sock(parent);
+-
+-reject:
+- rsp.scid = __cpu_to_le16(scid);
+- rsp.dcid = __cpu_to_le16(0);
+- rsp.status = __cpu_to_le16(0);
+- rsp.result = __cpu_to_le16(L2CAP_CONN_NO_MEM);
+- l2cap_send_rsp(conn, cmd->ident, L2CAP_CONN_RSP, L2CAP_CONN_RSP_SIZE, &rsp);
+-
+- return 0;
+-}
+-
+-static inline int l2cap_connect_rsp(struct l2cap_conn *conn, l2cap_cmd_hdr *cmd, __u8 *data)
+-{
+- l2cap_conn_rsp *rsp = (l2cap_conn_rsp *) data;
+- __u16 scid, dcid, result, status;
+- struct sock *sk;
+-
+- scid = __le16_to_cpu(rsp->scid);
+- dcid = __le16_to_cpu(rsp->dcid);
+- result = __le16_to_cpu(rsp->result);
+- status = __le16_to_cpu(rsp->status);
+-
+- DBG("dcid 0x%4.4x scid 0x%4.4x result 0x%2.2x status 0x%2.2x", dcid, scid, result, status);
+-
+- if (!(sk = l2cap_get_chan_by_scid(&conn->chan_list, scid)))
+- return -ENOENT;
+-
+- bh_lock_sock(sk);
+-
+- if (!result) {
+- char req[64];
+-
+- sk->state = BT_CONFIG;
+- l2cap_pi(sk)->dcid = dcid;
+- l2cap_pi(sk)->conf_state |= CONF_REQ_SENT;
+-
+- l2cap_send_req(conn, L2CAP_CONF_REQ, l2cap_build_conf_req(sk, req), req);
+- } else {
+- l2cap_chan_del(sk, ECONNREFUSED);
+- }
+-
+- bh_unlock_sock(sk);
+- return 0;
+-}
+-
+-static inline int l2cap_config_req(struct l2cap_conn *conn, l2cap_cmd_hdr *cmd, __u8 *data)
+-{
+- l2cap_conf_req * req = (l2cap_conf_req *) data;
+- __u16 dcid, flags;
+- __u8 rsp[64];
+- struct sock *sk;
+- int result;
+-
+- dcid = __le16_to_cpu(req->dcid);
+- flags = __le16_to_cpu(req->flags);
+-
+- DBG("dcid 0x%4.4x flags 0x%2.2x", dcid, flags);
+-
+- if (!(sk = l2cap_get_chan_by_scid(&conn->chan_list, dcid)))
+- return -ENOENT;
+-
+- bh_lock_sock(sk);
+-
+- l2cap_parse_conf_req(sk, req->data, cmd->len - L2CAP_CONF_REQ_SIZE);
+-
+- if (flags & 0x01) {
+- /* Incomplete config. Send empty response. */
+- l2cap_send_rsp(conn, cmd->ident, L2CAP_CONF_RSP, l2cap_build_conf_rsp(sk, rsp, NULL), rsp);
+- goto unlock;
+- }
+-
+- /* Complete config. */
+- l2cap_send_rsp(conn, cmd->ident, L2CAP_CONF_RSP, l2cap_build_conf_rsp(sk, rsp, &result), rsp);
+-
+- if (result)
+- goto unlock;
+-
+- /* Output config done */
+- l2cap_pi(sk)->conf_state |= CONF_OUTPUT_DONE;
+-
+- if (l2cap_pi(sk)->conf_state & CONF_INPUT_DONE) {
+- sk->state = BT_CONNECTED;
+- l2cap_chan_ready(sk);
+- } else if (!(l2cap_pi(sk)->conf_state & CONF_REQ_SENT)) {
+- char req[64];
+- l2cap_send_req(conn, L2CAP_CONF_REQ, l2cap_build_conf_req(sk, req), req);
+- }
+-
+-unlock:
+- bh_unlock_sock(sk);
+-
+- return 0;
+-}
+-
+-static inline int l2cap_config_rsp(struct l2cap_conn *conn, l2cap_cmd_hdr *cmd, __u8 *data)
+-{
+- l2cap_conf_rsp *rsp = (l2cap_conf_rsp *)data;
+- __u16 scid, flags, result;
+- struct sock *sk;
+- int err = 0;
+-
+- scid = __le16_to_cpu(rsp->scid);
+- flags = __le16_to_cpu(rsp->flags);
+- result = __le16_to_cpu(rsp->result);
+-
+- DBG("scid 0x%4.4x flags 0x%2.2x result 0x%2.2x", scid, flags, result);
+-
+- if (!(sk = l2cap_get_chan_by_scid(&conn->chan_list, scid)))
+- return -ENOENT;
+-
+- bh_lock_sock(sk);
+-
+- if (result) {
+- l2cap_disconn_req req;
+-
+- /* They didn't like our options. Well... we do not negotiate.
+- * Close channel.
+- */
+- sk->state = BT_DISCONN;
+-
+- req.dcid = __cpu_to_le16(l2cap_pi(sk)->dcid);
+- req.scid = __cpu_to_le16(l2cap_pi(sk)->scid);
+- l2cap_send_req(conn, L2CAP_DISCONN_REQ, L2CAP_DISCONN_REQ_SIZE, &req);
+-
+- l2cap_sock_set_timer(sk, sk->sndtimeo);
+- goto done;
+- }
+-
+- if (flags & 0x01)
+- goto done;
+-
+- /* Input config done */
+- l2cap_pi(sk)->conf_state |= CONF_INPUT_DONE;
+-
+- if (l2cap_pi(sk)->conf_state & CONF_OUTPUT_DONE) {
+- sk->state = BT_CONNECTED;
+- l2cap_chan_ready(sk);
+- }
+-
+-done:
+- bh_unlock_sock(sk);
+-
+- return err;
+-}
+-
+-static inline int l2cap_disconnect_req(struct l2cap_conn *conn, l2cap_cmd_hdr *cmd, __u8 *data)
+-{
+- l2cap_disconn_req *req = (l2cap_disconn_req *) data;
+- l2cap_disconn_rsp rsp;
+- __u16 dcid, scid;
+- struct sock *sk;
+-
+- scid = __le16_to_cpu(req->scid);
+- dcid = __le16_to_cpu(req->dcid);
+-
+- DBG("scid 0x%4.4x dcid 0x%4.4x", scid, dcid);
+-
+- if (!(sk = l2cap_get_chan_by_scid(&conn->chan_list, dcid)))
+- return 0;
+-
+- bh_lock_sock(sk);
+-
+- rsp.dcid = __cpu_to_le16(l2cap_pi(sk)->scid);
+- rsp.scid = __cpu_to_le16(l2cap_pi(sk)->dcid);
+- l2cap_send_rsp(conn, cmd->ident, L2CAP_DISCONN_RSP, L2CAP_DISCONN_RSP_SIZE, &rsp);
+-
+- l2cap_chan_del(sk, ECONNRESET);
+-
+- bh_unlock_sock(sk);
+-
+- l2cap_sock_kill(sk);
+-
+- return 0;
+-}
+-
+-static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, l2cap_cmd_hdr *cmd, __u8 *data)
+-{
+- l2cap_disconn_rsp *rsp = (l2cap_disconn_rsp *) data;
+- __u16 dcid, scid;
+- struct sock *sk;
+-
+- scid = __le16_to_cpu(rsp->scid);
+- dcid = __le16_to_cpu(rsp->dcid);
+-
+- DBG("dcid 0x%4.4x scid 0x%4.4x", dcid, scid);
+-
+- if (!(sk = l2cap_get_chan_by_scid(&conn->chan_list, scid)))
+- return -ENOENT;
+-
+- bh_lock_sock(sk);
+- l2cap_sock_clear_timer(sk);
+- l2cap_chan_del(sk, ECONNABORTED);
+- bh_unlock_sock(sk);
+-
+- l2cap_sock_kill(sk);
+-
+- return 0;
+-}
+-
+-static inline void l2cap_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb)
+-{
+- __u8 *data = skb->data;
+- int len = skb->len;
+- l2cap_cmd_hdr cmd;
+- int err = 0;
+-
+- while (len >= L2CAP_CMD_HDR_SIZE) {
+- memcpy(&cmd, data, L2CAP_CMD_HDR_SIZE);
+- data += L2CAP_CMD_HDR_SIZE;
+- len -= L2CAP_CMD_HDR_SIZE;
+-
+- cmd.len = __le16_to_cpu(cmd.len);
+-
+- DBG("code 0x%2.2x len %d id 0x%2.2x", cmd.code, cmd.len, cmd.ident);
+-
+- if (cmd.len > len || !cmd.ident) {
+- DBG("corrupted command");
+- break;
+- }
+-
+- switch (cmd.code) {
+- case L2CAP_CONN_REQ:
+- err = l2cap_connect_req(conn, &cmd, data);
+- break;
+-
+- case L2CAP_CONN_RSP:
+- err = l2cap_connect_rsp(conn, &cmd, data);
+- break;
+-
+- case L2CAP_CONF_REQ:
+- err = l2cap_config_req(conn, &cmd, data);
+- break;
+-
+- case L2CAP_CONF_RSP:
+- err = l2cap_config_rsp(conn, &cmd, data);
+- break;
+-
+- case L2CAP_DISCONN_REQ:
+- err = l2cap_disconnect_req(conn, &cmd, data);
+- break;
+-
+- case L2CAP_DISCONN_RSP:
+- err = l2cap_disconnect_rsp(conn, &cmd, data);
+- break;
+-
+- case L2CAP_COMMAND_REJ:
+- /* FIXME: We should process this */
+- l2cap_raw_recv(conn, skb);
+- break;
+-
+- case L2CAP_ECHO_REQ:
+- l2cap_send_rsp(conn, cmd.ident, L2CAP_ECHO_RSP, cmd.len, data);
+- break;
+-
+- case L2CAP_ECHO_RSP:
+- case L2CAP_INFO_REQ:
+- case L2CAP_INFO_RSP:
+- l2cap_raw_recv(conn, skb);
+- break;
+-
+- default:
+- ERR("Uknown signaling command 0x%2.2x", cmd.code);
+- err = -EINVAL;
+- break;
+- };
+-
+- if (err) {
+- l2cap_cmd_rej rej;
+- DBG("error %d", err);
+-
+- /* FIXME: Map err to a valid reason. */
+- rej.reason = __cpu_to_le16(0);
+- l2cap_send_rsp(conn, cmd.ident, L2CAP_COMMAND_REJ, L2CAP_CMD_REJ_SIZE, &rej);
+- }
+-
+- data += cmd.len;
+- len -= cmd.len;
+- }
+-
+- kfree_skb(skb);
+-}
+-
+-static inline int l2cap_data_channel(struct l2cap_conn *conn, __u16 cid, struct sk_buff *skb)
+-{
+- struct sock *sk;
+-
+- if (!(sk = l2cap_get_chan_by_scid(&conn->chan_list, cid))) {
+- DBG("unknown cid 0x%4.4x", cid);
+- goto drop;
+- }
+-
+- DBG("sk %p, len %d", sk, skb->len);
+-
+- if (sk->state != BT_CONNECTED)
+- goto drop;
+-
+- if (l2cap_pi(sk)->imtu < skb->len)
+- goto drop;
+-
+- skb_queue_tail(&sk->receive_queue, skb);
+- sk->data_ready(sk, skb->len);
+-
+- return 0;
+-
+-drop:
+- kfree_skb(skb);
+-
+- return 0;
+-}
+-
+-static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb)
+-{
+- l2cap_hdr *lh = (l2cap_hdr *) skb->data;
+- __u16 cid, len;
+-
+- skb_pull(skb, L2CAP_HDR_SIZE);
+- cid = __le16_to_cpu(lh->cid);
+- len = __le16_to_cpu(lh->len);
+-
+- DBG("len %d, cid 0x%4.4x", len, cid);
+-
+- if (cid == 0x0001)
+- l2cap_sig_channel(conn, skb);
+- else
+- l2cap_data_channel(conn, cid, skb);
+-}
+-
+-/* ------------ L2CAP interface with lower layer (HCI) ------------- */
+-static int l2cap_dev_event(struct notifier_block *this, unsigned long event, void *ptr)
+-{
+- struct hci_dev *hdev = (struct hci_dev *) ptr;
+-
+- DBG("hdev %s, event %ld", hdev->name, event);
+-
+- write_lock(&l2cap_rt_lock);
+-
+- switch (event) {
+- case HCI_DEV_UP:
+- l2cap_iff_add(hdev);
+- break;
+-
+- case HCI_DEV_DOWN:
+- l2cap_iff_del(hdev);
+- break;
+- };
+-
+- write_unlock(&l2cap_rt_lock);
+-
+- return NOTIFY_DONE;
+-}
+-
+-int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr)
+-{
+- struct l2cap_iff *iff;
+-
+- DBG("hdev %s, bdaddr %s", hdev->name, batostr(bdaddr));
+-
+- if (!(iff = hdev->l2cap_data)) {
+- ERR("unknown interface");
+- return 0;
+- }
+-
+- /* Always accept connection */
+- return 1;
+-}
+-
+-int l2cap_connect_cfm(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 status, struct hci_conn *hconn)
+-{
+- struct l2cap_conn *conn;
+- struct l2cap_iff *iff;
+- int err = 0;
+-
+- DBG("hdev %s bdaddr %s hconn %p", hdev->name, batostr(bdaddr), hconn);
+-
+- if (!(iff = hdev->l2cap_data)) {
+- ERR("unknown interface");
+- return 0;
+- }
+-
+- l2cap_iff_lock(iff);
+-
+- conn = l2cap_get_conn_by_addr(iff, bdaddr);
+-
+- if (conn) {
+- /* Outgoing connection */
+- DBG("Outgoing connection: %s -> %s, %p, %2.2x", batostr(iff->bdaddr), batostr(bdaddr), conn, status);
+-
+- if (!status && hconn) {
+- conn->state = BT_CONNECTED;
+- conn->hconn = hconn;
+-
+- hconn->l2cap_data = (void *)conn;
+-
+- /* Establish channels */
+- l2cap_conn_ready(conn);
+- } else {
+- l2cap_conn_del(conn, bterr(status));
+- }
+- } else {
+- /* Incomming connection */
+- DBG("Incomming connection: %s -> %s, %2.2x", batostr(iff->bdaddr), batostr(bdaddr), status);
+-
+- if (status || !hconn)
+- goto done;
+-
+- if (!(conn = l2cap_conn_add(iff, bdaddr))) {
+- err = -ENOMEM;
+- goto done;
+- }
+-
+- conn->hconn = hconn;
+- hconn->l2cap_data = (void *)conn;
+-
+- conn->state = BT_CONNECTED;
+- }
+-
+-done:
+- l2cap_iff_unlock(iff);
+-
+- return err;
+-}
+-
+-int l2cap_disconn_ind(struct hci_conn *hconn, __u8 reason)
+-{
+- struct l2cap_conn *conn = hconn->l2cap_data;
+-
+- DBG("hconn %p reason %d", hconn, reason);
+-
+- if (!conn) {
+- ERR("unknown connection");
+- return 0;
+- }
+- conn->hconn = NULL;
+-
+- l2cap_iff_lock(conn->iff);
+- l2cap_conn_del(conn, bterr(reason));
+- l2cap_iff_unlock(conn->iff);
+-
+- return 0;
+-}
+-
+-int l2cap_recv_acldata(struct hci_conn *hconn, struct sk_buff *skb, __u16 flags)
+-{
+- struct l2cap_conn *conn = hconn->l2cap_data;
+-
+- if (!conn) {
+- ERR("unknown connection %p", hconn);
+- goto drop;
+- }
+-
+- DBG("conn %p len %d flags 0x%x", conn, skb->len, flags);
+-
+- if (flags & ACL_START) {
+- int flen, tlen, size;
+- l2cap_hdr *lh;
+-
+- if (conn->rx_len) {
+- ERR("Unexpected start frame (len %d)", skb->len);
+- kfree_skb(conn->rx_skb); conn->rx_skb = NULL;
+- conn->rx_len = 0;
+- }
+-
+- if (skb->len < L2CAP_HDR_SIZE) {
+- ERR("Frame is too small (len %d)", skb->len);
+- goto drop;
+- }
+-
+- lh = (l2cap_hdr *)skb->data;
+- tlen = __le16_to_cpu(lh->len);
+- flen = skb->len - L2CAP_HDR_SIZE;
+-
+- DBG("Start: total len %d, frag len %d", tlen, flen);
+-
+- if (flen == tlen) {
+- /* Complete frame received */
+- l2cap_recv_frame(conn, skb);
+- return 0;
+- }
+-
+- /* Allocate skb for the complete frame (with header) */
+- size = L2CAP_HDR_SIZE + tlen;
+- if (!(conn->rx_skb = bluez_skb_alloc(size, GFP_ATOMIC)))
+- goto drop;
+-
+- memcpy(skb_put(conn->rx_skb, skb->len), skb->data, skb->len);
+-
+- conn->rx_len = tlen - flen;
+- } else {
+- DBG("Cont: frag len %d (expecting %d)", skb->len, conn->rx_len);
+-
+- if (!conn->rx_len) {
+- ERR("Unexpected continuation frame (len %d)", skb->len);
+- goto drop;
+- }
+-
+- if (skb->len > conn->rx_len) {
+- ERR("Fragment is too large (len %d)", skb->len);
+- kfree_skb(conn->rx_skb); conn->rx_skb = NULL;
+- goto drop;
+- }
+-
+- memcpy(skb_put(conn->rx_skb, skb->len), skb->data, skb->len);
+- conn->rx_len -= skb->len;
+-
+- if (!conn->rx_len) {
+- /* Complete frame received */
+- l2cap_recv_frame(conn, conn->rx_skb);
+- conn->rx_skb = NULL;
+- }
+- }
+-
+-drop:
+- kfree_skb(skb);
+- return 0;
+-}
+-
+-struct proto_ops l2cap_sock_ops = {
+- family: PF_BLUETOOTH,
+- release: l2cap_sock_release,
+- bind: l2cap_sock_bind,
+- connect: l2cap_sock_connect,
+- listen: l2cap_sock_listen,
+- accept: l2cap_sock_accept,
+- getname: l2cap_sock_getname,
+- sendmsg: l2cap_sock_sendmsg,
+- recvmsg: l2cap_sock_recvmsg,
+- poll: l2cap_sock_poll,
+- socketpair: sock_no_socketpair,
+- ioctl: sock_no_ioctl,
+- shutdown: sock_no_shutdown,
+- setsockopt: l2cap_sock_setsockopt,
+- getsockopt: l2cap_sock_getsockopt,
+- mmap: sock_no_mmap
+-};
+-
+-struct net_proto_family l2cap_sock_family_ops = {
+- family: PF_BLUETOOTH,
+- create: l2cap_sock_create
+-};
+-
+-struct hci_proto l2cap_hci_proto = {
+- name: "L2CAP",
+- id: HCI_PROTO_L2CAP,
+- connect_ind: l2cap_connect_ind,
+- connect_cfm: l2cap_connect_cfm,
+- disconn_ind: l2cap_disconn_ind,
+- recv_acldata: l2cap_recv_acldata,
+-};
+-
+-struct notifier_block l2cap_nblock = {
+- notifier_call: l2cap_dev_event
+-};
+-
+-int __init l2cap_init(void)
+-{
+- INF("BlueZ L2CAP ver %s Copyright (C) 2000,2001 Qualcomm Inc",
+- VERSION);
+- INF("Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>");
+-
+- if (bluez_sock_register(BTPROTO_L2CAP, &l2cap_sock_family_ops)) {
+- ERR("Can't register L2CAP socket");
+- return -EPROTO;
+- }
+-
+- if (hci_register_proto(&l2cap_hci_proto) < 0) {
+- ERR("Can't register L2CAP protocol");
+- return -EPROTO;
+- }
+-
+- hci_register_notifier(&l2cap_nblock);
+-
+- l2cap_register_proc();
+-
+- return 0;
+-}
+-
+-void l2cap_cleanup(void)
+-{
+- l2cap_unregister_proc();
+-
+- /* Unregister socket, protocol and notifier */
+- if (bluez_sock_unregister(BTPROTO_L2CAP))
+- ERR("Can't unregister L2CAP socket");
+-
+- if (hci_unregister_proto(&l2cap_hci_proto) < 0)
+- ERR("Can't unregister L2CAP protocol");
+-
+- hci_unregister_notifier(&l2cap_nblock);
+-
+- /* We _must_ not have any sockets and/or connections
+- * at this stage.
+- */
+-
+- /* Free interface list and unlock HCI devices */
+- {
+- struct list_head *list = &l2cap_iff_list;
+-
+- while (!list_empty(list)) {
+- struct l2cap_iff *iff;
+-
+- iff = list_entry(list->next, struct l2cap_iff, list);
+- l2cap_iff_del(iff->hdev);
+- }
+- }
+-}
+-
+-module_init(l2cap_init);
+-module_exit(l2cap_cleanup);
+-
+-MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>");
+-MODULE_DESCRIPTION("BlueZ L2CAP ver " VERSION);
+-MODULE_LICENSE("GPL");
+-
+diff -urN linux-2.4.18/net/bluetooth/l2cap_proc.c linux-2.4.18-mh15/net/bluetooth/l2cap_proc.c
+--- linux-2.4.18/net/bluetooth/l2cap_proc.c 2001-09-07 18:28:38.000000000 +0200
++++ linux-2.4.18-mh15/net/bluetooth/l2cap_proc.c 1970-01-01 01:00:00.000000000 +0100
+@@ -1,165 +0,0 @@
+-/*
+- BlueZ - Bluetooth protocol stack for Linux
+- Copyright (C) 2000-2001 Qualcomm Incorporated
+-
+- Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
+-
+- This program is free software; you can redistribute it and/or modify
+- it under the terms of the GNU General Public License version 2 as
+- published by the Free Software Foundation;
+-
+- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+- OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+- IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+- CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
+- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+-
+- ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
+- COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
+- SOFTWARE IS DISCLAIMED.
+-*/
+-
+-/*
+- * BlueZ L2CAP proc fs support.
+- *
+- * $Id$
+- */
+-
+-#include <linux/config.h>
+-#include <linux/module.h>
+-
+-#include <linux/types.h>
+-#include <linux/errno.h>
+-#include <linux/kernel.h>
+-#include <linux/major.h>
+-#include <linux/sched.h>
+-#include <linux/slab.h>
+-#include <linux/poll.h>
+-#include <linux/fcntl.h>
+-#include <linux/init.h>
+-#include <linux/skbuff.h>
+-#include <linux/interrupt.h>
+-#include <linux/socket.h>
+-#include <linux/skbuff.h>
+-#include <linux/proc_fs.h>
+-#include <linux/list.h>
+-#include <net/sock.h>
+-
+-#include <asm/system.h>
+-#include <asm/uaccess.h>
+-
+-#include <net/bluetooth/bluez.h>
+-#include <net/bluetooth/bluetooth.h>
+-#include <net/bluetooth/hci_core.h>
+-#include <net/bluetooth/l2cap_core.h>
+-
+-#ifndef L2CAP_DEBUG
+-#undef DBG
+-#define DBG( A... )
+-#endif
+-
+-/* ----- PROC fs support ----- */
+-static int l2cap_conn_dump(char *buf, struct l2cap_iff *iff)
+-{
+- struct list_head *p;
+- char *ptr = buf;
+-
+- list_for_each(p, &iff->conn_list) {
+- struct l2cap_conn *c;
+-
+- c = list_entry(p, struct l2cap_conn, list);
+- ptr += sprintf(ptr, " %p %d %p %p %s %s\n",
+- c, c->state, c->iff, c->hconn, batostr(&c->src), batostr(&c->dst));
+- }
+-
+- return ptr - buf;
+-}
+-
+-static int l2cap_iff_dump(char *buf)
+-{
+- struct list_head *p;
+- char *ptr = buf;
+-
+- ptr += sprintf(ptr, "Interfaces:\n");
+-
+- write_lock(&l2cap_rt_lock);
+-
+- list_for_each(p, &l2cap_iff_list) {
+- struct l2cap_iff *iff;
+-
+- iff = list_entry(p, struct l2cap_iff, list);
+-
+- ptr += sprintf(ptr, " %s %p %p\n", iff->hdev->name, iff, iff->hdev);
+-
+- l2cap_iff_lock(iff);
+- ptr += l2cap_conn_dump(ptr, iff);
+- l2cap_iff_unlock(iff);
+- }
+-
+- write_unlock(&l2cap_rt_lock);
+-
+- ptr += sprintf(ptr, "\n");
+-
+- return ptr - buf;
+-}
+-
+-static int l2cap_sock_dump(char *buf, struct bluez_sock_list *list)
+-{
+- struct l2cap_pinfo *pi;
+- struct sock *sk;
+- char *ptr = buf;
+-
+- ptr += sprintf(ptr, "Sockets:\n");
+-
+- write_lock(&list->lock);
+-
+- for (sk = list->head; sk; sk = sk->next) {
+- pi = l2cap_pi(sk);
+- ptr += sprintf(ptr, " %p %d %p %d %s %s 0x%4.4x 0x%4.4x %d %d\n", sk, sk->state, pi->conn, pi->psm,
+- batostr(&pi->src), batostr(&pi->dst), pi->scid, pi->dcid, pi->imtu, pi->omtu );
+- }
+-
+- write_unlock(&list->lock);
+-
+- ptr += sprintf(ptr, "\n");
+-
+- return ptr - buf;
+-}
+-
+-static int l2cap_read_proc(char *buf, char **start, off_t offset, int count, int *eof, void *priv)
+-{
+- char *ptr = buf;
+- int len;
+-
+- DBG("count %d, offset %ld", count, offset);
+-
+- ptr += l2cap_iff_dump(ptr);
+- ptr += l2cap_sock_dump(ptr, &l2cap_sk_list);
+- len = ptr - buf;
+-
+- if (len <= count + offset)
+- *eof = 1;
+-
+- *start = buf + offset;
+- len -= offset;
+-
+- if (len > count)
+- len = count;
+- if (len < 0)
+- len = 0;
+-
+- return len;
+-}
+-
+-void l2cap_register_proc(void)
+-{
+- create_proc_read_entry("bluetooth/l2cap", 0, 0, l2cap_read_proc, NULL);
+-}
+-
+-void l2cap_unregister_proc(void)
+-{
+- remove_proc_entry("bluetooth/l2cap", NULL);
+-}
+diff -urN linux-2.4.18/net/bluetooth/lib.c linux-2.4.18-mh15/net/bluetooth/lib.c
+--- linux-2.4.18/net/bluetooth/lib.c 2001-09-07 18:28:38.000000000 +0200
++++ linux-2.4.18-mh15/net/bluetooth/lib.c 2004-08-01 16:26:23.000000000 +0200
+@@ -25,7 +25,7 @@
+ /*
+ * BlueZ kernel library.
+ *
+- * $Id$
++ * $Id$
+ */
+
+ #include <linux/kernel.h>
+@@ -105,7 +105,7 @@
+ return EACCES;
+
+ case 0x06:
+- return EINVAL;
++ return EBADE;
+
+ case 0x07:
+ return ENOMEM;
+diff -urN linux-2.4.18/net/bluetooth/Makefile linux-2.4.18-mh15/net/bluetooth/Makefile
+--- linux-2.4.18/net/bluetooth/Makefile 2001-06-12 04:15:27.000000000 +0200
++++ linux-2.4.18-mh15/net/bluetooth/Makefile 2004-08-01 16:26:23.000000000 +0200
+@@ -1,20 +1,40 @@
+ #
+-# Makefile for the Bluetooth subsystem
++# Makefile for the Linux Bluetooth subsystem
+ #
+-O_TARGET := bluetooth.o
+
+-list-multi := hci.o l2cap.o
+-export-objs := syms.o
+-hci-objs := af_bluetooth.o hci_core.o hci_sock.o lib.o syms.o
+-l2cap-objs := l2cap_core.o l2cap_proc.o
++O_TARGET := bluetooth.o
+
+-obj-$(CONFIG_BLUEZ) += hci.o
++list-multi := bluez.o
++export-objs := syms.o l2cap.o
++
++bluez-objs := af_bluetooth.o hci_core.o hci_conn.o hci_event.o hci_sock.o lib.o syms.o
++
++obj-$(CONFIG_BLUEZ) += bluez.o
+ obj-$(CONFIG_BLUEZ_L2CAP) += l2cap.o
++obj-$(CONFIG_BLUEZ_SCO) += sco.o
+
+-include $(TOPDIR)/Rules.make
++subdir-$(CONFIG_BLUEZ_RFCOMM) += rfcomm
++subdir-$(CONFIG_BLUEZ_BNEP) += bnep
++subdir-$(CONFIG_BLUEZ_CMTP) += cmtp
++subdir-$(CONFIG_BLUEZ_HIDP) += hidp
++
++ifeq ($(CONFIG_BLUEZ_RFCOMM),y)
++obj-y += rfcomm/rfcomm.o
++endif
+
+-hci.o: $(hci-objs)
+- $(LD) -r -o $@ $(hci-objs)
++ifeq ($(CONFIG_BLUEZ_BNEP),y)
++obj-y += bnep/bnep.o
++endif
++
++ifeq ($(CONFIG_BLUEZ_CMTP),y)
++obj-y += cmtp/cmtp.o
++endif
++
++ifeq ($(CONFIG_BLUEZ_HIDP),y)
++obj-y += hidp/hidp.o
++endif
++
++include $(TOPDIR)/Rules.make
+
+-l2cap.o: $(l2cap-objs)
+- $(LD) -r -o $@ $(l2cap-objs)
++bluez.o: $(bluez-objs)
++ $(LD) -r -o $@ $(bluez-objs)
+diff -urN linux-2.4.18/net/bluetooth/rfcomm/Config.in linux-2.4.18-mh15/net/bluetooth/rfcomm/Config.in
+--- linux-2.4.18/net/bluetooth/rfcomm/Config.in 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/net/bluetooth/rfcomm/Config.in 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,10 @@
++#
++# Bluetooth RFCOMM layer configuration
++#
++
++dep_tristate 'RFCOMM protocol support' CONFIG_BLUEZ_RFCOMM $CONFIG_BLUEZ_L2CAP
++
++if [ "$CONFIG_BLUEZ_RFCOMM" != "n" ]; then
++ bool ' RFCOMM TTY support' CONFIG_BLUEZ_RFCOMM_TTY
++fi
++
+diff -urN linux-2.4.18/net/bluetooth/rfcomm/core.c linux-2.4.18-mh15/net/bluetooth/rfcomm/core.c
+--- linux-2.4.18/net/bluetooth/rfcomm/core.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/net/bluetooth/rfcomm/core.c 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,1940 @@
++/*
++ RFCOMM implementation for Linux Bluetooth stack (BlueZ).
++ Copyright (C) 2002 Maxim Krasnyansky <maxk@qualcomm.com>
++ Copyright (C) 2002 Marcel Holtmann <marcel@holtmann.org>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License version 2 as
++ published by the Free Software Foundation;
++
++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++
++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
++ SOFTWARE IS DISCLAIMED.
++*/
++
++/*
++ RPN support - Dirk Husemann <hud@zurich.ibm.com>
++*/
++
++/*
++ * RFCOMM core.
++ *
++ * $Id$
++ */
++
++#define __KERNEL_SYSCALLS__
++
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/errno.h>
++#include <linux/kernel.h>
++#include <linux/sched.h>
++#include <linux/signal.h>
++#include <linux/init.h>
++#include <linux/wait.h>
++#include <linux/net.h>
++#include <linux/proc_fs.h>
++#include <net/sock.h>
++#include <asm/uaccess.h>
++#include <asm/unaligned.h>
++
++#include <net/bluetooth/bluetooth.h>
++#include <net/bluetooth/l2cap.h>
++#include <net/bluetooth/rfcomm.h>
++
++#define VERSION "1.1"
++
++#ifndef CONFIG_BLUEZ_RFCOMM_DEBUG
++#undef BT_DBG
++#define BT_DBG(D...)
++#endif
++
++struct task_struct *rfcomm_thread;
++DECLARE_MUTEX(rfcomm_sem);
++unsigned long rfcomm_event;
++
++static LIST_HEAD(session_list);
++static atomic_t terminate, running;
++
++static int rfcomm_send_frame(struct rfcomm_session *s, u8 *data, int len);
++static int rfcomm_send_sabm(struct rfcomm_session *s, u8 dlci);
++static int rfcomm_send_disc(struct rfcomm_session *s, u8 dlci);
++static int rfcomm_queue_disc(struct rfcomm_dlc *d);
++static int rfcomm_send_nsc(struct rfcomm_session *s, int cr, u8 type);
++static int rfcomm_send_pn(struct rfcomm_session *s, int cr, struct rfcomm_dlc *d);
++static int rfcomm_send_msc(struct rfcomm_session *s, int cr, u8 dlci, u8 v24_sig);
++static int rfcomm_send_test(struct rfcomm_session *s, int cr, u8 *pattern, int len);
++static int rfcomm_send_credits(struct rfcomm_session *s, u8 addr, u8 credits);
++static void rfcomm_make_uih(struct sk_buff *skb, u8 addr);
++
++static void rfcomm_process_connect(struct rfcomm_session *s);
++
++/* ---- RFCOMM frame parsing macros ---- */
++#define __get_dlci(b) ((b & 0xfc) >> 2)
++#define __get_channel(b) ((b & 0xf8) >> 3)
++#define __get_dir(b) ((b & 0x04) >> 2)
++#define __get_type(b) ((b & 0xef))
++
++#define __test_ea(b) ((b & 0x01))
++#define __test_cr(b) ((b & 0x02))
++#define __test_pf(b) ((b & 0x10))
++
++#define __addr(cr, dlci) (((dlci & 0x3f) << 2) | (cr << 1) | 0x01)
++#define __ctrl(type, pf) (((type & 0xef) | (pf << 4)))
++#define __dlci(dir, chn) (((chn & 0x1f) << 1) | dir)
++#define __srv_channel(dlci) (dlci >> 1)
++#define __dir(dlci) (dlci & 0x01)
++
++#define __len8(len) (((len) << 1) | 1)
++#define __len16(len) ((len) << 1)
++
++/* MCC macros */
++#define __mcc_type(cr, type) (((type << 2) | (cr << 1) | 0x01))
++#define __get_mcc_type(b) ((b & 0xfc) >> 2)
++#define __get_mcc_len(b) ((b & 0xfe) >> 1)
++
++/* RPN macros */
++#define __rpn_line_settings(data, stop, parity) ((data & 0x3) | ((stop & 0x1) << 2) | ((parity & 0x3) << 3))
++#define __get_rpn_data_bits(line) ((line) & 0x3)
++#define __get_rpn_stop_bits(line) (((line) >> 2) & 0x1)
++#define __get_rpn_parity(line) (((line) >> 3) & 0x3)
++
++/* ---- RFCOMM FCS computation ---- */
++
++/* CRC on 2 bytes */
++#define __crc(data) (rfcomm_crc_table[rfcomm_crc_table[0xff ^ data[0]] ^ data[1]])
++
++/* FCS on 2 bytes */
++static inline u8 __fcs(u8 *data)
++{
++ return (0xff - __crc(data));
++}
++
++/* FCS on 3 bytes */
++static inline u8 __fcs2(u8 *data)
++{
++ return (0xff - rfcomm_crc_table[__crc(data) ^ data[2]]);
++}
++
++/* Check FCS */
++static inline int __check_fcs(u8 *data, int type, u8 fcs)
++{
++ u8 f = __crc(data);
++
++ if (type != RFCOMM_UIH)
++ f = rfcomm_crc_table[f ^ data[2]];
++
++ return rfcomm_crc_table[f ^ fcs] != 0xcf;
++}
++
++/* ---- L2CAP callbacks ---- */
++static void rfcomm_l2state_change(struct sock *sk)
++{
++ BT_DBG("%p state %d", sk, sk->state);
++ rfcomm_schedule(RFCOMM_SCHED_STATE);
++}
++
++static void rfcomm_l2data_ready(struct sock *sk, int bytes)
++{
++ BT_DBG("%p bytes %d", sk, bytes);
++ rfcomm_schedule(RFCOMM_SCHED_RX);
++}
++
++static int rfcomm_l2sock_create(struct socket **sock)
++{
++ int err;
++
++ BT_DBG("");
++
++ err = sock_create(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP, sock);
++ if (!err) {
++ struct sock *sk = (*sock)->sk;
++ sk->data_ready = rfcomm_l2data_ready;
++ sk->state_change = rfcomm_l2state_change;
++ }
++ return err;
++}
++
++/* ---- RFCOMM DLCs ---- */
++static void rfcomm_dlc_timeout(unsigned long arg)
++{
++ struct rfcomm_dlc *d = (void *) arg;
++
++ BT_DBG("dlc %p state %ld", d, d->state);
++
++ set_bit(RFCOMM_TIMED_OUT, &d->flags);
++ rfcomm_dlc_put(d);
++ rfcomm_schedule(RFCOMM_SCHED_TIMEO);
++}
++
++static void rfcomm_dlc_set_timer(struct rfcomm_dlc *d, long timeout)
++{
++ BT_DBG("dlc %p state %ld timeout %ld", d, d->state, timeout);
++
++ if (!mod_timer(&d->timer, jiffies + timeout))
++ rfcomm_dlc_hold(d);
++}
++
++static void rfcomm_dlc_clear_timer(struct rfcomm_dlc *d)
++{
++ BT_DBG("dlc %p state %ld", d, d->state);
++
++ if (timer_pending(&d->timer) && del_timer(&d->timer))
++ rfcomm_dlc_put(d);
++}
++
++static void rfcomm_dlc_clear_state(struct rfcomm_dlc *d)
++{
++ BT_DBG("%p", d);
++
++ d->state = BT_OPEN;
++ d->flags = 0;
++ d->mscex = 0;
++ d->mtu = RFCOMM_DEFAULT_MTU;
++ d->v24_sig = RFCOMM_V24_RTC | RFCOMM_V24_RTR | RFCOMM_V24_DV;
++
++ d->cfc = RFCOMM_CFC_DISABLED;
++ d->rx_credits = RFCOMM_DEFAULT_CREDITS;
++}
++
++struct rfcomm_dlc *rfcomm_dlc_alloc(int prio)
++{
++ struct rfcomm_dlc *d = kmalloc(sizeof(*d), prio);
++ if (!d)
++ return NULL;
++ memset(d, 0, sizeof(*d));
++
++ init_timer(&d->timer);
++ d->timer.function = rfcomm_dlc_timeout;
++ d->timer.data = (unsigned long) d;
++
++ skb_queue_head_init(&d->tx_queue);
++ spin_lock_init(&d->lock);
++ atomic_set(&d->refcnt, 1);
++
++ rfcomm_dlc_clear_state(d);
++
++ BT_DBG("%p", d);
++ return d;
++}
++
++void rfcomm_dlc_free(struct rfcomm_dlc *d)
++{
++ BT_DBG("%p", d);
++
++ skb_queue_purge(&d->tx_queue);
++ kfree(d);
++}
++
++static void rfcomm_dlc_link(struct rfcomm_session *s, struct rfcomm_dlc *d)
++{
++ BT_DBG("dlc %p session %p", d, s);
++
++ rfcomm_session_hold(s);
++
++ rfcomm_dlc_hold(d);
++ list_add(&d->list, &s->dlcs);
++ d->session = s;
++}
++
++static void rfcomm_dlc_unlink(struct rfcomm_dlc *d)
++{
++ struct rfcomm_session *s = d->session;
++
++ BT_DBG("dlc %p refcnt %d session %p", d, atomic_read(&d->refcnt), s);
++
++ list_del(&d->list);
++ d->session = NULL;
++ rfcomm_dlc_put(d);
++
++ rfcomm_session_put(s);
++}
++
++static struct rfcomm_dlc *rfcomm_dlc_get(struct rfcomm_session *s, u8 dlci)
++{
++ struct rfcomm_dlc *d;
++ struct list_head *p;
++
++ list_for_each(p, &s->dlcs) {
++ d = list_entry(p, struct rfcomm_dlc, list);
++ if (d->dlci == dlci)
++ return d;
++ }
++ return NULL;
++}
++
++static int __rfcomm_dlc_open(struct rfcomm_dlc *d, bdaddr_t *src, bdaddr_t *dst, u8 channel)
++{
++ struct rfcomm_session *s;
++ int err = 0;
++ u8 dlci;
++
++ BT_DBG("dlc %p state %ld %s %s channel %d",
++ d, d->state, batostr(src), batostr(dst), channel);
++
++ if (channel < 1 || channel > 30)
++ return -EINVAL;
++
++ if (d->state != BT_OPEN && d->state != BT_CLOSED)
++ return 0;
++
++ s = rfcomm_session_get(src, dst);
++ if (!s) {
++ s = rfcomm_session_create(src, dst, &err);
++ if (!s)
++ return err;
++ }
++
++ dlci = __dlci(!s->initiator, channel);
++
++ /* Check if DLCI already exists */
++ if (rfcomm_dlc_get(s, dlci))
++ return -EBUSY;
++
++ rfcomm_dlc_clear_state(d);
++
++ d->dlci = dlci;
++ d->addr = __addr(s->initiator, dlci);
++ d->priority = 7;
++
++ d->state = BT_CONFIG;
++ rfcomm_dlc_link(s, d);
++
++ d->mtu = s->mtu;
++ d->cfc = (s->cfc == RFCOMM_CFC_UNKNOWN) ? 0 : s->cfc;
++
++ if (s->state == BT_CONNECTED)
++ rfcomm_send_pn(s, 1, d);
++ rfcomm_dlc_set_timer(d, RFCOMM_CONN_TIMEOUT);
++ return 0;
++}
++
++int rfcomm_dlc_open(struct rfcomm_dlc *d, bdaddr_t *src, bdaddr_t *dst, u8 channel)
++{
++ mm_segment_t fs;
++ int r;
++
++ rfcomm_lock();
++
++ fs = get_fs(); set_fs(KERNEL_DS);
++ r = __rfcomm_dlc_open(d, src, dst, channel);
++ set_fs(fs);
++
++ rfcomm_unlock();
++ return r;
++}
++
++static int __rfcomm_dlc_close(struct rfcomm_dlc *d, int err)
++{
++ struct rfcomm_session *s = d->session;
++ if (!s)
++ return 0;
++
++ BT_DBG("dlc %p state %ld dlci %d err %d session %p",
++ d, d->state, d->dlci, err, s);
++
++ switch (d->state) {
++ case BT_CONNECTED:
++ case BT_CONFIG:
++ case BT_CONNECT:
++ d->state = BT_DISCONN;
++ if (skb_queue_empty(&d->tx_queue)) {
++ rfcomm_send_disc(s, d->dlci);
++ rfcomm_dlc_set_timer(d, RFCOMM_DISC_TIMEOUT);
++ } else {
++ rfcomm_queue_disc(d);
++ rfcomm_dlc_set_timer(d, RFCOMM_DISC_TIMEOUT * 2);
++ }
++ break;
++
++ default:
++ rfcomm_dlc_clear_timer(d);
++
++ rfcomm_dlc_lock(d);
++ d->state = BT_CLOSED;
++ d->state_change(d, err);
++ rfcomm_dlc_unlock(d);
++
++ skb_queue_purge(&d->tx_queue);
++ rfcomm_dlc_unlink(d);
++ }
++
++ return 0;
++}
++
++int rfcomm_dlc_close(struct rfcomm_dlc *d, int err)
++{
++ mm_segment_t fs;
++ int r;
++
++ rfcomm_lock();
++
++ fs = get_fs(); set_fs(KERNEL_DS);
++ r = __rfcomm_dlc_close(d, err);
++ set_fs(fs);
++
++ rfcomm_unlock();
++ return r;
++}
++
++int rfcomm_dlc_send(struct rfcomm_dlc *d, struct sk_buff *skb)
++{
++ int len = skb->len;
++
++ if (d->state != BT_CONNECTED)
++ return -ENOTCONN;
++
++ BT_DBG("dlc %p mtu %d len %d", d, d->mtu, len);
++
++ if (len > d->mtu)
++ return -EINVAL;
++
++ rfcomm_make_uih(skb, d->addr);
++ skb_queue_tail(&d->tx_queue, skb);
++
++ if (!test_bit(RFCOMM_TX_THROTTLED, &d->flags))
++ rfcomm_schedule(RFCOMM_SCHED_TX);
++ return len;
++}
++
++void __rfcomm_dlc_throttle(struct rfcomm_dlc *d)
++{
++ BT_DBG("dlc %p state %ld", d, d->state);
++
++ if (!d->cfc) {
++ d->v24_sig |= RFCOMM_V24_FC;
++ set_bit(RFCOMM_MSC_PENDING, &d->flags);
++ }
++ rfcomm_schedule(RFCOMM_SCHED_TX);
++}
++
++void __rfcomm_dlc_unthrottle(struct rfcomm_dlc *d)
++{
++ BT_DBG("dlc %p state %ld", d, d->state);
++
++ if (!d->cfc) {
++ d->v24_sig &= ~RFCOMM_V24_FC;
++ set_bit(RFCOMM_MSC_PENDING, &d->flags);
++ }
++ rfcomm_schedule(RFCOMM_SCHED_TX);
++}
++
++/*
++ Set/get modem status functions use _local_ status i.e. what we report
++ to the other side.
++ Remote status is provided by dlc->modem_status() callback.
++ */
++int rfcomm_dlc_set_modem_status(struct rfcomm_dlc *d, u8 v24_sig)
++{
++ BT_DBG("dlc %p state %ld v24_sig 0x%x",
++ d, d->state, v24_sig);
++
++ if (test_bit(RFCOMM_RX_THROTTLED, &d->flags))
++ v24_sig |= RFCOMM_V24_FC;
++ else
++ v24_sig &= ~RFCOMM_V24_FC;
++
++ d->v24_sig = v24_sig;
++
++ if (!test_and_set_bit(RFCOMM_MSC_PENDING, &d->flags))
++ rfcomm_schedule(RFCOMM_SCHED_TX);
++
++ return 0;
++}
++
++int rfcomm_dlc_get_modem_status(struct rfcomm_dlc *d, u8 *v24_sig)
++{
++ BT_DBG("dlc %p state %ld v24_sig 0x%x",
++ d, d->state, d->v24_sig);
++
++ *v24_sig = d->v24_sig;
++ return 0;
++}
++
++/* ---- RFCOMM sessions ---- */
++struct rfcomm_session *rfcomm_session_add(struct socket *sock, int state)
++{
++ struct rfcomm_session *s = kmalloc(sizeof(*s), GFP_KERNEL);
++ if (!s)
++ return NULL;
++ memset(s, 0, sizeof(*s));
++
++ BT_DBG("session %p sock %p", s, sock);
++
++ INIT_LIST_HEAD(&s->dlcs);
++ s->state = state;
++ s->sock = sock;
++
++ s->mtu = RFCOMM_DEFAULT_MTU;
++ s->cfc = RFCOMM_CFC_UNKNOWN;
++
++ list_add(&s->list, &session_list);
++
++ /* Do not increment module usage count for listeting sessions.
++ * Otherwise we won't be able to unload the module. */
++ if (state != BT_LISTEN)
++ MOD_INC_USE_COUNT;
++ return s;
++}
++
++void rfcomm_session_del(struct rfcomm_session *s)
++{
++ int state = s->state;
++
++ BT_DBG("session %p state %ld", s, s->state);
++
++ list_del(&s->list);
++
++ if (state == BT_CONNECTED)
++ rfcomm_send_disc(s, 0);
++
++ sock_release(s->sock);
++ kfree(s);
++
++ if (state != BT_LISTEN)
++ MOD_DEC_USE_COUNT;
++}
++
++struct rfcomm_session *rfcomm_session_get(bdaddr_t *src, bdaddr_t *dst)
++{
++ struct rfcomm_session *s;
++ struct list_head *p, *n;
++ struct bluez_pinfo *pi;
++ list_for_each_safe(p, n, &session_list) {
++ s = list_entry(p, struct rfcomm_session, list);
++ pi = bluez_pi(s->sock->sk);
++
++ if ((!bacmp(src, BDADDR_ANY) || !bacmp(&pi->src, src)) &&
++ !bacmp(&pi->dst, dst))
++ return s;
++ }
++ return NULL;
++}
++
++void rfcomm_session_close(struct rfcomm_session *s, int err)
++{
++ struct rfcomm_dlc *d;
++ struct list_head *p, *n;
++
++ BT_DBG("session %p state %ld err %d", s, s->state, err);
++
++ rfcomm_session_hold(s);
++
++ s->state = BT_CLOSED;
++
++ /* Close all dlcs */
++ list_for_each_safe(p, n, &s->dlcs) {
++ d = list_entry(p, struct rfcomm_dlc, list);
++ d->state = BT_CLOSED;
++ __rfcomm_dlc_close(d, err);
++ }
++
++ rfcomm_session_put(s);
++}
++
++struct rfcomm_session *rfcomm_session_create(bdaddr_t *src, bdaddr_t *dst, int *err)
++{
++ struct rfcomm_session *s = NULL;
++ struct sockaddr_l2 addr;
++ struct l2cap_options opts;
++ struct socket *sock;
++ int size;
++
++ BT_DBG("%s %s", batostr(src), batostr(dst));
++
++ *err = rfcomm_l2sock_create(&sock);
++ if (*err < 0)
++ return NULL;
++
++ bacpy(&addr.l2_bdaddr, src);
++ addr.l2_family = AF_BLUETOOTH;
++ addr.l2_psm = 0;
++ *err = sock->ops->bind(sock, (struct sockaddr *) &addr, sizeof(addr));
++ if (*err < 0)
++ goto failed;
++
++ /* Set L2CAP options */
++ size = sizeof(opts);
++ sock->ops->getsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, (void *)&opts, &size);
++
++ opts.imtu = RFCOMM_MAX_L2CAP_MTU;
++ sock->ops->setsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, (void *)&opts, size);
++
++ s = rfcomm_session_add(sock, BT_BOUND);
++ if (!s) {
++ *err = -ENOMEM;
++ goto failed;
++ }
++
++ s->initiator = 1;
++
++ bacpy(&addr.l2_bdaddr, dst);
++ addr.l2_family = AF_BLUETOOTH;
++ addr.l2_psm = htobs(RFCOMM_PSM);
++ *err = sock->ops->connect(sock, (struct sockaddr *) &addr, sizeof(addr), O_NONBLOCK);
++ if (*err == 0 || *err == -EAGAIN)
++ return s;
++
++ rfcomm_session_del(s);
++ return NULL;
++
++failed:
++ sock_release(sock);
++ return NULL;
++}
++
++void rfcomm_session_getaddr(struct rfcomm_session *s, bdaddr_t *src, bdaddr_t *dst)
++{
++ struct sock *sk = s->sock->sk;
++ if (src)
++ bacpy(src, &bluez_pi(sk)->src);
++ if (dst)
++ bacpy(dst, &bluez_pi(sk)->dst);
++}
++
++/* ---- RFCOMM frame sending ---- */
++static int rfcomm_send_frame(struct rfcomm_session *s, u8 *data, int len)
++{
++ struct socket *sock = s->sock;
++ struct iovec iv = { data, len };
++ struct msghdr msg;
++ int err;
++
++ BT_DBG("session %p len %d", s, len);
++
++ memset(&msg, 0, sizeof(msg));
++ msg.msg_iovlen = 1;
++ msg.msg_iov = &iv;
++
++ err = sock->ops->sendmsg(sock, &msg, len, 0);
++ return err;
++}
++
++static int rfcomm_send_sabm(struct rfcomm_session *s, u8 dlci)
++{
++ struct rfcomm_cmd cmd;
++
++ BT_DBG("%p dlci %d", s, dlci);
++
++ cmd.addr = __addr(s->initiator, dlci);
++ cmd.ctrl = __ctrl(RFCOMM_SABM, 1);
++ cmd.len = __len8(0);
++ cmd.fcs = __fcs2((u8 *) &cmd);
++
++ return rfcomm_send_frame(s, (void *) &cmd, sizeof(cmd));
++}
++
++static int rfcomm_send_ua(struct rfcomm_session *s, u8 dlci)
++{
++ struct rfcomm_cmd cmd;
++
++ BT_DBG("%p dlci %d", s, dlci);
++
++ cmd.addr = __addr(!s->initiator, dlci);
++ cmd.ctrl = __ctrl(RFCOMM_UA, 1);
++ cmd.len = __len8(0);
++ cmd.fcs = __fcs2((u8 *) &cmd);
++
++ return rfcomm_send_frame(s, (void *) &cmd, sizeof(cmd));
++}
++
++static int rfcomm_send_disc(struct rfcomm_session *s, u8 dlci)
++{
++ struct rfcomm_cmd cmd;
++
++ BT_DBG("%p dlci %d", s, dlci);
++
++ cmd.addr = __addr(s->initiator, dlci);
++ cmd.ctrl = __ctrl(RFCOMM_DISC, 1);
++ cmd.len = __len8(0);
++ cmd.fcs = __fcs2((u8 *) &cmd);
++
++ return rfcomm_send_frame(s, (void *) &cmd, sizeof(cmd));
++}
++
++static int rfcomm_queue_disc(struct rfcomm_dlc *d)
++{
++ struct rfcomm_cmd *cmd;
++ struct sk_buff *skb;
++
++ BT_DBG("dlc %p dlci %d", d, d->dlci);
++
++ skb = alloc_skb(sizeof(*cmd), GFP_KERNEL);
++ if (!skb)
++ return -ENOMEM;
++
++ cmd = (void *) __skb_put(skb, sizeof(*cmd));
++ cmd->addr = d->addr;
++ cmd->ctrl = __ctrl(RFCOMM_DISC, 1);
++ cmd->len = __len8(0);
++ cmd->fcs = __fcs2((u8 *) cmd);
++
++ skb_queue_tail(&d->tx_queue, skb);
++ rfcomm_schedule(RFCOMM_SCHED_TX);
++ return 0;
++}
++
++static int rfcomm_send_dm(struct rfcomm_session *s, u8 dlci)
++{
++ struct rfcomm_cmd cmd;
++
++ BT_DBG("%p dlci %d", s, dlci);
++
++ cmd.addr = __addr(!s->initiator, dlci);
++ cmd.ctrl = __ctrl(RFCOMM_DM, 1);
++ cmd.len = __len8(0);
++ cmd.fcs = __fcs2((u8 *) &cmd);
++
++ return rfcomm_send_frame(s, (void *) &cmd, sizeof(cmd));
++}
++
++static int rfcomm_send_nsc(struct rfcomm_session *s, int cr, u8 type)
++{
++ struct rfcomm_hdr *hdr;
++ struct rfcomm_mcc *mcc;
++ u8 buf[16], *ptr = buf;
++
++ BT_DBG("%p cr %d type %d", s, cr, type);
++
++ hdr = (void *) ptr; ptr += sizeof(*hdr);
++ hdr->addr = __addr(s->initiator, 0);
++ hdr->ctrl = __ctrl(RFCOMM_UIH, 0);
++ hdr->len = __len8(sizeof(*mcc) + 1);
++
++ mcc = (void *) ptr; ptr += sizeof(*mcc);
++ mcc->type = __mcc_type(cr, RFCOMM_NSC);
++ mcc->len = __len8(1);
++
++ /* Type that we didn't like */
++ *ptr = __mcc_type(cr, type); ptr++;
++
++ *ptr = __fcs(buf); ptr++;
++
++ return rfcomm_send_frame(s, buf, ptr - buf);
++}
++
++static int rfcomm_send_pn(struct rfcomm_session *s, int cr, struct rfcomm_dlc *d)
++{
++ struct rfcomm_hdr *hdr;
++ struct rfcomm_mcc *mcc;
++ struct rfcomm_pn *pn;
++ u8 buf[16], *ptr = buf;
++
++ BT_DBG("%p cr %d dlci %d mtu %d", s, cr, d->dlci, d->mtu);
++
++ hdr = (void *) ptr; ptr += sizeof(*hdr);
++ hdr->addr = __addr(s->initiator, 0);
++ hdr->ctrl = __ctrl(RFCOMM_UIH, 0);
++ hdr->len = __len8(sizeof(*mcc) + sizeof(*pn));
++
++ mcc = (void *) ptr; ptr += sizeof(*mcc);
++ mcc->type = __mcc_type(cr, RFCOMM_PN);
++ mcc->len = __len8(sizeof(*pn));
++
++ pn = (void *) ptr; ptr += sizeof(*pn);
++ pn->dlci = d->dlci;
++ pn->priority = d->priority;
++ pn->ack_timer = 0;
++ pn->max_retrans = 0;
++
++ if (s->cfc) {
++ pn->flow_ctrl = cr ? 0xf0 : 0xe0;
++ pn->credits = RFCOMM_DEFAULT_CREDITS;
++ } else {
++ pn->flow_ctrl = 0;
++ pn->credits = 0;
++ }
++
++ pn->mtu = htobs(d->mtu);
++
++ *ptr = __fcs(buf); ptr++;
++
++ return rfcomm_send_frame(s, buf, ptr - buf);
++}
++
++static int rfcomm_send_rpn(struct rfcomm_session *s, int cr, u8 dlci,
++ u8 bit_rate, u8 data_bits, u8 stop_bits,
++ u8 parity, u8 flow_ctrl_settings,
++ u8 xon_char, u8 xoff_char, u16 param_mask)
++{
++ struct rfcomm_hdr *hdr;
++ struct rfcomm_mcc *mcc;
++ struct rfcomm_rpn *rpn;
++ u8 buf[16], *ptr = buf;
++
++ BT_DBG("%p cr %d dlci %d bit_r 0x%x data_b 0x%x stop_b 0x%x parity 0x%x"
++ "flwc_s 0x%x xon_c 0x%x xoff_c 0x%x p_mask 0x%x",
++ s, cr, dlci, bit_rate, data_bits, stop_bits, parity,
++ flow_ctrl_settings, xon_char, xoff_char, param_mask);
++
++ hdr = (void *) ptr; ptr += sizeof(*hdr);
++ hdr->addr = __addr(s->initiator, 0);
++ hdr->ctrl = __ctrl(RFCOMM_UIH, 0);
++ hdr->len = __len8(sizeof(*mcc) + sizeof(*rpn));
++
++ mcc = (void *) ptr; ptr += sizeof(*mcc);
++ mcc->type = __mcc_type(cr, RFCOMM_RPN);
++ mcc->len = __len8(sizeof(*rpn));
++
++ rpn = (void *) ptr; ptr += sizeof(*rpn);
++ rpn->dlci = __addr(1, dlci);
++ rpn->bit_rate = bit_rate;
++ rpn->line_settings = __rpn_line_settings(data_bits, stop_bits, parity);
++ rpn->flow_ctrl = flow_ctrl_settings;
++ rpn->xon_char = xon_char;
++ rpn->xoff_char = xoff_char;
++ rpn->param_mask = param_mask;
++
++ *ptr = __fcs(buf); ptr++;
++
++ return rfcomm_send_frame(s, buf, ptr - buf);
++}
++
++static int rfcomm_send_rls(struct rfcomm_session *s, int cr, u8 dlci, u8 status)
++{
++ struct rfcomm_hdr *hdr;
++ struct rfcomm_mcc *mcc;
++ struct rfcomm_rls *rls;
++ u8 buf[16], *ptr = buf;
++
++ BT_DBG("%p cr %d status 0x%x", s, cr, status);
++
++ hdr = (void *) ptr; ptr += sizeof(*hdr);
++ hdr->addr = __addr(s->initiator, 0);
++ hdr->ctrl = __ctrl(RFCOMM_UIH, 0);
++ hdr->len = __len8(sizeof(*mcc) + sizeof(*rls));
++
++ mcc = (void *) ptr; ptr += sizeof(*mcc);
++ mcc->type = __mcc_type(cr, RFCOMM_RLS);
++ mcc->len = __len8(sizeof(*rls));
++
++ rls = (void *) ptr; ptr += sizeof(*rls);
++ rls->dlci = __addr(1, dlci);
++ rls->status = status;
++
++ *ptr = __fcs(buf); ptr++;
++
++ return rfcomm_send_frame(s, buf, ptr - buf);
++}
++
++static int rfcomm_send_msc(struct rfcomm_session *s, int cr, u8 dlci, u8 v24_sig)
++{
++ struct rfcomm_hdr *hdr;
++ struct rfcomm_mcc *mcc;
++ struct rfcomm_msc *msc;
++ u8 buf[16], *ptr = buf;
++
++ BT_DBG("%p cr %d v24 0x%x", s, cr, v24_sig);
++
++ hdr = (void *) ptr; ptr += sizeof(*hdr);
++ hdr->addr = __addr(s->initiator, 0);
++ hdr->ctrl = __ctrl(RFCOMM_UIH, 0);
++ hdr->len = __len8(sizeof(*mcc) + sizeof(*msc));
++
++ mcc = (void *) ptr; ptr += sizeof(*mcc);
++ mcc->type = __mcc_type(cr, RFCOMM_MSC);
++ mcc->len = __len8(sizeof(*msc));
++
++ msc = (void *) ptr; ptr += sizeof(*msc);
++ msc->dlci = __addr(1, dlci);
++ msc->v24_sig = v24_sig | 0x01;
++
++ *ptr = __fcs(buf); ptr++;
++
++ return rfcomm_send_frame(s, buf, ptr - buf);
++}
++
++static int rfcomm_send_fcoff(struct rfcomm_session *s, int cr)
++{
++ struct rfcomm_hdr *hdr;
++ struct rfcomm_mcc *mcc;
++ u8 buf[16], *ptr = buf;
++
++ BT_DBG("%p cr %d", s, cr);
++
++ hdr = (void *) ptr; ptr += sizeof(*hdr);
++ hdr->addr = __addr(s->initiator, 0);
++ hdr->ctrl = __ctrl(RFCOMM_UIH, 0);
++ hdr->len = __len8(sizeof(*mcc));
++
++ mcc = (void *) ptr; ptr += sizeof(*mcc);
++ mcc->type = __mcc_type(cr, RFCOMM_FCOFF);
++ mcc->len = __len8(0);
++
++ *ptr = __fcs(buf); ptr++;
++
++ return rfcomm_send_frame(s, buf, ptr - buf);
++}
++
++static int rfcomm_send_fcon(struct rfcomm_session *s, int cr)
++{
++ struct rfcomm_hdr *hdr;
++ struct rfcomm_mcc *mcc;
++ u8 buf[16], *ptr = buf;
++
++ BT_DBG("%p cr %d", s, cr);
++
++ hdr = (void *) ptr; ptr += sizeof(*hdr);
++ hdr->addr = __addr(s->initiator, 0);
++ hdr->ctrl = __ctrl(RFCOMM_UIH, 0);
++ hdr->len = __len8(sizeof(*mcc));
++
++ mcc = (void *) ptr; ptr += sizeof(*mcc);
++ mcc->type = __mcc_type(cr, RFCOMM_FCON);
++ mcc->len = __len8(0);
++
++ *ptr = __fcs(buf); ptr++;
++
++ return rfcomm_send_frame(s, buf, ptr - buf);
++}
++
++static int rfcomm_send_test(struct rfcomm_session *s, int cr, u8 *pattern, int len)
++{
++ struct socket *sock = s->sock;
++ struct iovec iv[3];
++ struct msghdr msg;
++ unsigned char hdr[5], crc[1];
++
++ if (len > 125)
++ return -EINVAL;
++
++ BT_DBG("%p cr %d", s, cr);
++
++ hdr[0] = __addr(s->initiator, 0);
++ hdr[1] = __ctrl(RFCOMM_UIH, 0);
++ hdr[2] = 0x01 | ((len + 2) << 1);
++ hdr[3] = 0x01 | ((cr & 0x01) << 1) | (RFCOMM_TEST << 2);
++ hdr[4] = 0x01 | (len << 1);
++
++ crc[0] = __fcs(hdr);
++
++ iv[0].iov_base = hdr;
++ iv[0].iov_len = 5;
++ iv[1].iov_base = pattern;
++ iv[1].iov_len = len;
++ iv[2].iov_base = crc;
++ iv[2].iov_len = 1;
++
++ memset(&msg, 0, sizeof(msg));
++ msg.msg_iovlen = 3;
++ msg.msg_iov = iv;
++ return sock->ops->sendmsg(sock, &msg, 6 + len, 0);
++}
++
++static int rfcomm_send_credits(struct rfcomm_session *s, u8 addr, u8 credits)
++{
++ struct rfcomm_hdr *hdr;
++ u8 buf[16], *ptr = buf;
++
++ BT_DBG("%p addr %d credits %d", s, addr, credits);
++
++ hdr = (void *) ptr; ptr += sizeof(*hdr);
++ hdr->addr = addr;
++ hdr->ctrl = __ctrl(RFCOMM_UIH, 1);
++ hdr->len = __len8(0);
++
++ *ptr = credits; ptr++;
++
++ *ptr = __fcs(buf); ptr++;
++
++ return rfcomm_send_frame(s, buf, ptr - buf);
++}
++
++static void rfcomm_make_uih(struct sk_buff *skb, u8 addr)
++{
++ struct rfcomm_hdr *hdr;
++ int len = skb->len;
++ u8 *crc;
++
++ if (len > 127) {
++ hdr = (void *) skb_push(skb, 4);
++ put_unaligned(htobs(__len16(len)), (u16 *) &hdr->len);
++ } else {
++ hdr = (void *) skb_push(skb, 3);
++ hdr->len = __len8(len);
++ }
++ hdr->addr = addr;
++ hdr->ctrl = __ctrl(RFCOMM_UIH, 0);
++
++ crc = skb_put(skb, 1);
++ *crc = __fcs((void *) hdr);
++}
++
++/* ---- RFCOMM frame reception ---- */
++static int rfcomm_recv_ua(struct rfcomm_session *s, u8 dlci)
++{
++ BT_DBG("session %p state %ld dlci %d", s, s->state, dlci);
++
++ if (dlci) {
++ /* Data channel */
++ struct rfcomm_dlc *d = rfcomm_dlc_get(s, dlci);
++ if (!d) {
++ rfcomm_send_dm(s, dlci);
++ return 0;
++ }
++
++ switch (d->state) {
++ case BT_CONNECT:
++ rfcomm_dlc_clear_timer(d);
++
++ rfcomm_dlc_lock(d);
++ d->state = BT_CONNECTED;
++ d->state_change(d, 0);
++ rfcomm_dlc_unlock(d);
++
++ rfcomm_send_msc(s, 1, dlci, d->v24_sig);
++ break;
++
++ case BT_DISCONN:
++ d->state = BT_CLOSED;
++ __rfcomm_dlc_close(d, 0);
++ break;
++ }
++ } else {
++ /* Control channel */
++ switch (s->state) {
++ case BT_CONNECT:
++ s->state = BT_CONNECTED;
++ rfcomm_process_connect(s);
++ break;
++ }
++ }
++ return 0;
++}
++
++static int rfcomm_recv_dm(struct rfcomm_session *s, u8 dlci)
++{
++ int err = 0;
++
++ BT_DBG("session %p state %ld dlci %d", s, s->state, dlci);
++
++ if (dlci) {
++ /* Data DLC */
++ struct rfcomm_dlc *d = rfcomm_dlc_get(s, dlci);
++ if (d) {
++ if (d->state == BT_CONNECT || d->state == BT_CONFIG)
++ err = ECONNREFUSED;
++ else
++ err = ECONNRESET;
++
++ d->state = BT_CLOSED;
++ __rfcomm_dlc_close(d, err);
++ }
++ } else {
++ if (s->state == BT_CONNECT)
++ err = ECONNREFUSED;
++ else
++ err = ECONNRESET;
++
++ s->state = BT_CLOSED;
++ rfcomm_session_close(s, err);
++ }
++ return 0;
++}
++
++static int rfcomm_recv_disc(struct rfcomm_session *s, u8 dlci)
++{
++ int err = 0;
++
++ BT_DBG("session %p state %ld dlci %d", s, s->state, dlci);
++
++ if (dlci) {
++ struct rfcomm_dlc *d = rfcomm_dlc_get(s, dlci);
++ if (d) {
++ rfcomm_send_ua(s, dlci);
++
++ if (d->state == BT_CONNECT || d->state == BT_CONFIG)
++ err = ECONNREFUSED;
++ else
++ err = ECONNRESET;
++
++ d->state = BT_CLOSED;
++ __rfcomm_dlc_close(d, err);
++ } else
++ rfcomm_send_dm(s, dlci);
++
++ } else {
++ rfcomm_send_ua(s, 0);
++
++ if (s->state == BT_CONNECT)
++ err = ECONNREFUSED;
++ else
++ err = ECONNRESET;
++
++ s->state = BT_CLOSED;
++ rfcomm_session_close(s, err);
++ }
++
++ return 0;
++}
++
++static int rfcomm_recv_sabm(struct rfcomm_session *s, u8 dlci)
++{
++ struct rfcomm_dlc *d;
++ u8 channel;
++
++ BT_DBG("session %p state %ld dlci %d", s, s->state, dlci);
++
++ if (!dlci) {
++ rfcomm_send_ua(s, 0);
++
++ if (s->state == BT_OPEN) {
++ s->state = BT_CONNECTED;
++ rfcomm_process_connect(s);
++ }
++ return 0;
++ }
++
++ /* Check if DLC exists */
++ d = rfcomm_dlc_get(s, dlci);
++ if (d) {
++ if (d->state == BT_OPEN) {
++ /* DLC was previously opened by PN request */
++ rfcomm_send_ua(s, dlci);
++
++ rfcomm_dlc_lock(d);
++ d->state = BT_CONNECTED;
++ d->state_change(d, 0);
++ rfcomm_dlc_unlock(d);
++
++ rfcomm_send_msc(s, 1, dlci, d->v24_sig);
++ }
++ return 0;
++ }
++
++ /* Notify socket layer about incomming connection */
++ channel = __srv_channel(dlci);
++ if (rfcomm_connect_ind(s, channel, &d)) {
++ d->dlci = dlci;
++ d->addr = __addr(s->initiator, dlci);
++ rfcomm_dlc_link(s, d);
++
++ rfcomm_send_ua(s, dlci);
++
++ rfcomm_dlc_lock(d);
++ d->state = BT_CONNECTED;
++ d->state_change(d, 0);
++ rfcomm_dlc_unlock(d);
++
++ rfcomm_send_msc(s, 1, dlci, d->v24_sig);
++ } else {
++ rfcomm_send_dm(s, dlci);
++ }
++
++ return 0;
++}
++
++static int rfcomm_apply_pn(struct rfcomm_dlc *d, int cr, struct rfcomm_pn *pn)
++{
++ struct rfcomm_session *s = d->session;
++
++ BT_DBG("dlc %p state %ld dlci %d mtu %d fc 0x%x credits %d",
++ d, d->state, d->dlci, pn->mtu, pn->flow_ctrl, pn->credits);
++
++ if (pn->flow_ctrl == 0xf0 || pn->flow_ctrl == 0xe0) {
++ d->cfc = s->cfc = RFCOMM_CFC_ENABLED;
++ d->tx_credits = pn->credits;
++ } else {
++ d->cfc = s->cfc = RFCOMM_CFC_DISABLED;
++ set_bit(RFCOMM_TX_THROTTLED, &d->flags);
++ }
++
++ d->priority = pn->priority;
++
++ d->mtu = s->mtu = btohs(pn->mtu);
++
++ return 0;
++}
++
++static int rfcomm_recv_pn(struct rfcomm_session *s, int cr, struct sk_buff *skb)
++{
++ struct rfcomm_pn *pn = (void *) skb->data;
++ struct rfcomm_dlc *d;
++ u8 dlci = pn->dlci;
++
++ BT_DBG("session %p state %ld dlci %d", s, s->state, dlci);
++
++ if (!dlci)
++ return 0;
++
++ d = rfcomm_dlc_get(s, dlci);
++ if (d) {
++ if (cr) {
++ /* PN request */
++ rfcomm_apply_pn(d, cr, pn);
++ rfcomm_send_pn(s, 0, d);
++ } else {
++ /* PN response */
++ switch (d->state) {
++ case BT_CONFIG:
++ rfcomm_apply_pn(d, cr, pn);
++
++ d->state = BT_CONNECT;
++ rfcomm_send_sabm(s, d->dlci);
++ break;
++ }
++ }
++ } else {
++ u8 channel = __srv_channel(dlci);
++
++ if (!cr)
++ return 0;
++
++ /* PN request for non existing DLC.
++ * Assume incomming connection. */
++ if (rfcomm_connect_ind(s, channel, &d)) {
++ d->dlci = dlci;
++ d->addr = __addr(s->initiator, dlci);
++ rfcomm_dlc_link(s, d);
++
++ rfcomm_apply_pn(d, cr, pn);
++
++ d->state = BT_OPEN;
++ rfcomm_send_pn(s, 0, d);
++ } else {
++ rfcomm_send_dm(s, dlci);
++ }
++ }
++ return 0;
++}
++
++static int rfcomm_recv_rpn(struct rfcomm_session *s, int cr, int len, struct sk_buff *skb)
++{
++ struct rfcomm_rpn *rpn = (void *) skb->data;
++ u8 dlci = __get_dlci(rpn->dlci);
++
++ u8 bit_rate = 0;
++ u8 data_bits = 0;
++ u8 stop_bits = 0;
++ u8 parity = 0;
++ u8 flow_ctrl = 0;
++ u8 xon_char = 0;
++ u8 xoff_char = 0;
++ u16 rpn_mask = RFCOMM_RPN_PM_ALL;
++
++ BT_DBG("dlci %d cr %d len 0x%x bitr 0x%x line 0x%x flow 0x%x xonc 0x%x xoffc 0x%x pm 0x%x",
++ dlci, cr, len, rpn->bit_rate, rpn->line_settings, rpn->flow_ctrl,
++ rpn->xon_char, rpn->xoff_char, rpn->param_mask);
++
++ if (!cr)
++ return 0;
++
++ if (len == 1) {
++ /* request: return default setting */
++ bit_rate = RFCOMM_RPN_BR_115200;
++ data_bits = RFCOMM_RPN_DATA_8;
++ stop_bits = RFCOMM_RPN_STOP_1;
++ parity = RFCOMM_RPN_PARITY_NONE;
++ flow_ctrl = RFCOMM_RPN_FLOW_NONE;
++ xon_char = RFCOMM_RPN_XON_CHAR;
++ xoff_char = RFCOMM_RPN_XOFF_CHAR;
++
++ goto rpn_out;
++ }
++ /* check for sane values: ignore/accept bit_rate, 8 bits, 1 stop bit, no parity,
++ no flow control lines, normal XON/XOFF chars */
++ if (rpn->param_mask & RFCOMM_RPN_PM_BITRATE) {
++ bit_rate = rpn->bit_rate;
++ if (bit_rate != RFCOMM_RPN_BR_115200) {
++ BT_DBG("RPN bit rate mismatch 0x%x", bit_rate);
++ bit_rate = RFCOMM_RPN_BR_115200;
++ rpn_mask ^= RFCOMM_RPN_PM_BITRATE;
++ }
++ }
++ if (rpn->param_mask & RFCOMM_RPN_PM_DATA) {
++ data_bits = __get_rpn_data_bits(rpn->line_settings);
++ if (data_bits != RFCOMM_RPN_DATA_8) {
++ BT_DBG("RPN data bits mismatch 0x%x", data_bits);
++ data_bits = RFCOMM_RPN_DATA_8;
++ rpn_mask ^= RFCOMM_RPN_PM_DATA;
++ }
++ }
++ if (rpn->param_mask & RFCOMM_RPN_PM_STOP) {
++ stop_bits = __get_rpn_stop_bits(rpn->line_settings);
++ if (stop_bits != RFCOMM_RPN_STOP_1) {
++ BT_DBG("RPN stop bits mismatch 0x%x", stop_bits);
++ stop_bits = RFCOMM_RPN_STOP_1;
++ rpn_mask ^= RFCOMM_RPN_PM_STOP;
++ }
++ }
++ if (rpn->param_mask & RFCOMM_RPN_PM_PARITY) {
++ parity = __get_rpn_parity(rpn->line_settings);
++ if (parity != RFCOMM_RPN_PARITY_NONE) {
++ BT_DBG("RPN parity mismatch 0x%x", parity);
++ parity = RFCOMM_RPN_PARITY_NONE;
++ rpn_mask ^= RFCOMM_RPN_PM_PARITY;
++ }
++ }
++ if (rpn->param_mask & RFCOMM_RPN_PM_FLOW) {
++ flow_ctrl = rpn->flow_ctrl;
++ if (flow_ctrl != RFCOMM_RPN_FLOW_NONE) {
++ BT_DBG("RPN flow ctrl mismatch 0x%x", flow_ctrl);
++ flow_ctrl = RFCOMM_RPN_FLOW_NONE;
++ rpn_mask ^= RFCOMM_RPN_PM_FLOW;
++ }
++ }
++ if (rpn->param_mask & RFCOMM_RPN_PM_XON) {
++ xon_char = rpn->xon_char;
++ if (xon_char != RFCOMM_RPN_XON_CHAR) {
++ BT_DBG("RPN XON char mismatch 0x%x", xon_char);
++ xon_char = RFCOMM_RPN_XON_CHAR;
++ rpn_mask ^= RFCOMM_RPN_PM_XON;
++ }
++ }
++ if (rpn->param_mask & RFCOMM_RPN_PM_XOFF) {
++ xoff_char = rpn->xoff_char;
++ if (xoff_char != RFCOMM_RPN_XOFF_CHAR) {
++ BT_DBG("RPN XOFF char mismatch 0x%x", xoff_char);
++ xoff_char = RFCOMM_RPN_XOFF_CHAR;
++ rpn_mask ^= RFCOMM_RPN_PM_XOFF;
++ }
++ }
++
++rpn_out:
++ rfcomm_send_rpn(s, 0, dlci,
++ bit_rate, data_bits, stop_bits, parity, flow_ctrl,
++ xon_char, xoff_char, rpn_mask);
++
++ return 0;
++}
++
++static int rfcomm_recv_rls(struct rfcomm_session *s, int cr, struct sk_buff *skb)
++{
++ struct rfcomm_rls *rls = (void *) skb->data;
++ u8 dlci = __get_dlci(rls->dlci);
++
++ BT_DBG("dlci %d cr %d status 0x%x", dlci, cr, rls->status);
++
++ if (!cr)
++ return 0;
++
++ /* FIXME: We should probably do something with this
++ information here. But for now it's sufficient just
++ to reply -- Bluetooth 1.1 says it's mandatory to
++ recognise and respond to RLS */
++
++ rfcomm_send_rls(s, 0, dlci, rls->status);
++
++ return 0;
++}
++
++static int rfcomm_recv_msc(struct rfcomm_session *s, int cr, struct sk_buff *skb)
++{
++ struct rfcomm_msc *msc = (void *) skb->data;
++ struct rfcomm_dlc *d;
++ u8 dlci = __get_dlci(msc->dlci);
++
++ BT_DBG("dlci %d cr %d v24 0x%x", dlci, cr, msc->v24_sig);
++
++ d = rfcomm_dlc_get(s, dlci);
++ if (!d)
++ return 0;
++
++ if (cr) {
++ if (msc->v24_sig & RFCOMM_V24_FC && !d->cfc)
++ set_bit(RFCOMM_TX_THROTTLED, &d->flags);
++ else
++ clear_bit(RFCOMM_TX_THROTTLED, &d->flags);
++
++ rfcomm_dlc_lock(d);
++ if (d->modem_status)
++ d->modem_status(d, msc->v24_sig);
++ rfcomm_dlc_unlock(d);
++
++ rfcomm_send_msc(s, 0, dlci, msc->v24_sig);
++
++ d->mscex |= RFCOMM_MSCEX_RX;
++ } else
++ d->mscex |= RFCOMM_MSCEX_TX;
++
++ return 0;
++}
++
++static int rfcomm_recv_mcc(struct rfcomm_session *s, struct sk_buff *skb)
++{
++ struct rfcomm_mcc *mcc = (void *) skb->data;
++ u8 type, cr, len;
++
++ cr = __test_cr(mcc->type);
++ type = __get_mcc_type(mcc->type);
++ len = __get_mcc_len(mcc->len);
++
++ BT_DBG("%p type 0x%x cr %d", s, type, cr);
++
++ skb_pull(skb, 2);
++
++ switch (type) {
++ case RFCOMM_PN:
++ rfcomm_recv_pn(s, cr, skb);
++ break;
++
++ case RFCOMM_RPN:
++ rfcomm_recv_rpn(s, cr, len, skb);
++ break;
++
++ case RFCOMM_RLS:
++ rfcomm_recv_rls(s, cr, skb);
++ break;
++
++ case RFCOMM_MSC:
++ rfcomm_recv_msc(s, cr, skb);
++ break;
++
++ case RFCOMM_FCOFF:
++ if (cr) {
++ set_bit(RFCOMM_TX_THROTTLED, &s->flags);
++ rfcomm_send_fcoff(s, 0);
++ }
++ break;
++
++ case RFCOMM_FCON:
++ if (cr) {
++ clear_bit(RFCOMM_TX_THROTTLED, &s->flags);
++ rfcomm_send_fcon(s, 0);
++ }
++ break;
++
++ case RFCOMM_TEST:
++ if (cr)
++ rfcomm_send_test(s, 0, skb->data, skb->len);
++ break;
++
++ case RFCOMM_NSC:
++ break;
++
++ default:
++ BT_ERR("Unknown control type 0x%02x", type);
++ rfcomm_send_nsc(s, cr, type);
++ break;
++ }
++ return 0;
++}
++
++static int rfcomm_recv_data(struct rfcomm_session *s, u8 dlci, int pf, struct sk_buff *skb)
++{
++ struct rfcomm_dlc *d;
++
++ BT_DBG("session %p state %ld dlci %d pf %d", s, s->state, dlci, pf);
++
++ d = rfcomm_dlc_get(s, dlci);
++ if (!d) {
++ rfcomm_send_dm(s, dlci);
++ goto drop;
++ }
++
++ if (pf && d->cfc) {
++ u8 credits = *(u8 *) skb->data; skb_pull(skb, 1);
++
++ d->tx_credits += credits;
++ if (d->tx_credits)
++ clear_bit(RFCOMM_TX_THROTTLED, &d->flags);
++ }
++
++ if (skb->len && d->state == BT_CONNECTED) {
++ rfcomm_dlc_lock(d);
++ d->rx_credits--;
++ d->data_ready(d, skb);
++ rfcomm_dlc_unlock(d);
++ return 0;
++ }
++
++drop:
++ kfree_skb(skb);
++ return 0;
++}
++
++static int rfcomm_recv_frame(struct rfcomm_session *s, struct sk_buff *skb)
++{
++ struct rfcomm_hdr *hdr = (void *) skb->data;
++ u8 type, dlci, fcs;
++
++ dlci = __get_dlci(hdr->addr);
++ type = __get_type(hdr->ctrl);
++
++ /* Trim FCS */
++ skb->len--; skb->tail--;
++ fcs = *(u8 *) skb->tail;
++
++ if (__check_fcs(skb->data, type, fcs)) {
++ BT_ERR("bad checksum in packet");
++ kfree_skb(skb);
++ return -EILSEQ;
++ }
++
++ if (__test_ea(hdr->len))
++ skb_pull(skb, 3);
++ else
++ skb_pull(skb, 4);
++
++ switch (type) {
++ case RFCOMM_SABM:
++ if (__test_pf(hdr->ctrl))
++ rfcomm_recv_sabm(s, dlci);
++ break;
++
++ case RFCOMM_DISC:
++ if (__test_pf(hdr->ctrl))
++ rfcomm_recv_disc(s, dlci);
++ break;
++
++ case RFCOMM_UA:
++ if (__test_pf(hdr->ctrl))
++ rfcomm_recv_ua(s, dlci);
++ break;
++
++ case RFCOMM_DM:
++ rfcomm_recv_dm(s, dlci);
++ break;
++
++ case RFCOMM_UIH:
++ if (dlci)
++ return rfcomm_recv_data(s, dlci, __test_pf(hdr->ctrl), skb);
++
++ rfcomm_recv_mcc(s, skb);
++ break;
++
++ default:
++ BT_ERR("Unknown packet type 0x%02x\n", type);
++ break;
++ }
++ kfree_skb(skb);
++ return 0;
++}
++
++/* ---- Connection and data processing ---- */
++
++static void rfcomm_process_connect(struct rfcomm_session *s)
++{
++ struct rfcomm_dlc *d;
++ struct list_head *p, *n;
++
++ BT_DBG("session %p state %ld", s, s->state);
++
++ list_for_each_safe(p, n, &s->dlcs) {
++ d = list_entry(p, struct rfcomm_dlc, list);
++ if (d->state == BT_CONFIG) {
++ d->mtu = s->mtu;
++ rfcomm_send_pn(s, 1, d);
++ }
++ }
++}
++
++/* Send data queued for the DLC.
++ * Return number of frames left in the queue.
++ */
++static inline int rfcomm_process_tx(struct rfcomm_dlc *d)
++{
++ struct sk_buff *skb;
++ int err;
++
++ BT_DBG("dlc %p state %ld cfc %d rx_credits %d tx_credits %d",
++ d, d->state, d->cfc, d->rx_credits, d->tx_credits);
++
++ /* Send pending MSC */
++ if (test_and_clear_bit(RFCOMM_MSC_PENDING, &d->flags))
++ rfcomm_send_msc(d->session, 1, d->dlci, d->v24_sig);
++
++ if (d->cfc) {
++ /* CFC enabled.
++ * Give them some credits */
++ if (!test_bit(RFCOMM_RX_THROTTLED, &d->flags) &&
++ d->rx_credits <= (d->cfc >> 2)) {
++ rfcomm_send_credits(d->session, d->addr, d->cfc - d->rx_credits);
++ d->rx_credits = d->cfc;
++ }
++ } else {
++ /* CFC disabled.
++ * Give ourselves some credits */
++ d->tx_credits = 5;
++ }
++
++ if (test_bit(RFCOMM_TX_THROTTLED, &d->flags))
++ return skb_queue_len(&d->tx_queue);
++
++ while (d->tx_credits && (skb = skb_dequeue(&d->tx_queue))) {
++ err = rfcomm_send_frame(d->session, skb->data, skb->len);
++ if (err < 0) {
++ skb_queue_head(&d->tx_queue, skb);
++ break;
++ }
++ kfree_skb(skb);
++ d->tx_credits--;
++ }
++
++ if (d->cfc && !d->tx_credits) {
++ /* We're out of TX credits.
++ * Set TX_THROTTLED flag to avoid unnesary wakeups by dlc_send. */
++ set_bit(RFCOMM_TX_THROTTLED, &d->flags);
++ }
++
++ return skb_queue_len(&d->tx_queue);
++}
++
++static inline void rfcomm_process_dlcs(struct rfcomm_session *s)
++{
++ struct rfcomm_dlc *d;
++ struct list_head *p, *n;
++
++ BT_DBG("session %p state %ld", s, s->state);
++
++ list_for_each_safe(p, n, &s->dlcs) {
++ d = list_entry(p, struct rfcomm_dlc, list);
++ if (test_bit(RFCOMM_TIMED_OUT, &d->flags)) {
++ __rfcomm_dlc_close(d, ETIMEDOUT);
++ continue;
++ }
++
++ if (test_bit(RFCOMM_TX_THROTTLED, &s->flags))
++ continue;
++
++ if ((d->state == BT_CONNECTED || d->state == BT_DISCONN) &&
++ d->mscex == RFCOMM_MSCEX_OK)
++ rfcomm_process_tx(d);
++ }
++}
++
++static inline void rfcomm_process_rx(struct rfcomm_session *s)
++{
++ struct socket *sock = s->sock;
++ struct sock *sk = sock->sk;
++ struct sk_buff *skb;
++
++ BT_DBG("session %p state %ld qlen %d", s, s->state, skb_queue_len(&sk->receive_queue));
++
++ /* Get data directly from socket receive queue without copying it. */
++ while ((skb = skb_dequeue(&sk->receive_queue))) {
++ skb_orphan(skb);
++ rfcomm_recv_frame(s, skb);
++ }
++
++ if (sk->state == BT_CLOSED) {
++ if (!s->initiator)
++ rfcomm_session_put(s);
++
++ rfcomm_session_close(s, sk->err);
++ }
++}
++
++static inline void rfcomm_accept_connection(struct rfcomm_session *s)
++{
++ struct socket *sock = s->sock, *nsock;
++ int err;
++
++ /* Fast check for a new connection.
++ * Avoids unnesesary socket allocations. */
++ if (list_empty(&bluez_pi(sock->sk)->accept_q))
++ return;
++
++ BT_DBG("session %p", s);
++
++ nsock = sock_alloc();
++ if (!nsock)
++ return;
++
++ nsock->type = sock->type;
++ nsock->ops = sock->ops;
++
++ err = sock->ops->accept(sock, nsock, O_NONBLOCK);
++ if (err < 0) {
++ sock_release(nsock);
++ return;
++ }
++
++ /* Set our callbacks */
++ nsock->sk->data_ready = rfcomm_l2data_ready;
++ nsock->sk->state_change = rfcomm_l2state_change;
++
++ s = rfcomm_session_add(nsock, BT_OPEN);
++ if (s) {
++ rfcomm_session_hold(s);
++ rfcomm_schedule(RFCOMM_SCHED_RX);
++ } else
++ sock_release(nsock);
++}
++
++static inline void rfcomm_check_connection(struct rfcomm_session *s)
++{
++ struct sock *sk = s->sock->sk;
++
++ BT_DBG("%p state %ld", s, s->state);
++
++ switch(sk->state) {
++ case BT_CONNECTED:
++ s->state = BT_CONNECT;
++
++ /* We can adjust MTU on outgoing sessions.
++ * L2CAP MTU minus UIH header and FCS. */
++ s->mtu = min(l2cap_pi(sk)->omtu, l2cap_pi(sk)->imtu) - 5;
++
++ rfcomm_send_sabm(s, 0);
++ break;
++
++ case BT_CLOSED:
++ s->state = BT_CLOSED;
++ rfcomm_session_close(s, sk->err);
++ break;
++ }
++}
++
++static inline void rfcomm_process_sessions(void)
++{
++ struct list_head *p, *n;
++
++ rfcomm_lock();
++
++ list_for_each_safe(p, n, &session_list) {
++ struct rfcomm_session *s;
++ s = list_entry(p, struct rfcomm_session, list);
++
++ if (s->state == BT_LISTEN) {
++ rfcomm_accept_connection(s);
++ continue;
++ }
++
++ rfcomm_session_hold(s);
++
++ switch (s->state) {
++ case BT_BOUND:
++ rfcomm_check_connection(s);
++ break;
++
++ default:
++ rfcomm_process_rx(s);
++ break;
++ }
++
++ rfcomm_process_dlcs(s);
++
++ rfcomm_session_put(s);
++ }
++
++ rfcomm_unlock();
++}
++
++static void rfcomm_worker(void)
++{
++ BT_DBG("");
++
++ daemonize(); reparent_to_init();
++ set_fs(KERNEL_DS);
++
++ while (!atomic_read(&terminate)) {
++ BT_DBG("worker loop event 0x%lx", rfcomm_event);
++
++ if (!test_bit(RFCOMM_SCHED_WAKEUP, &rfcomm_event)) {
++ /* No pending events. Let's sleep.
++ * Incomming connections and data will wake us up. */
++ set_current_state(TASK_INTERRUPTIBLE);
++ schedule();
++ }
++
++ /* Process stuff */
++ clear_bit(RFCOMM_SCHED_WAKEUP, &rfcomm_event);
++ rfcomm_process_sessions();
++ }
++ set_current_state(TASK_RUNNING);
++ return;
++}
++
++static int rfcomm_add_listener(bdaddr_t *ba)
++{
++ struct sockaddr_l2 addr;
++ struct l2cap_options opts;
++ struct socket *sock;
++ struct rfcomm_session *s;
++ int size, err = 0;
++
++ /* Create socket */
++ err = rfcomm_l2sock_create(&sock);
++ if (err < 0) {
++ BT_ERR("Create socket failed %d", err);
++ return err;
++ }
++
++ /* Bind socket */
++ bacpy(&addr.l2_bdaddr, ba);
++ addr.l2_family = AF_BLUETOOTH;
++ addr.l2_psm = htobs(RFCOMM_PSM);
++ err = sock->ops->bind(sock, (struct sockaddr *) &addr, sizeof(addr));
++ if (err < 0) {
++ BT_ERR("Bind failed %d", err);
++ goto failed;
++ }
++
++ /* Set L2CAP options */
++ size = sizeof(opts);
++ sock->ops->getsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, (void *)&opts, &size);
++
++ opts.imtu = RFCOMM_MAX_L2CAP_MTU;
++ sock->ops->setsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, (void *)&opts, size);
++
++ /* Start listening on the socket */
++ err = sock->ops->listen(sock, 10);
++ if (err) {
++ BT_ERR("Listen failed %d", err);
++ goto failed;
++ }
++
++ /* Add listening session */
++ s = rfcomm_session_add(sock, BT_LISTEN);
++ if (!s)
++ goto failed;
++
++ rfcomm_session_hold(s);
++ return 0;
++failed:
++ sock_release(sock);
++ return err;
++}
++
++static void rfcomm_kill_listener(void)
++{
++ struct rfcomm_session *s;
++ struct list_head *p, *n;
++
++ BT_DBG("");
++
++ list_for_each_safe(p, n, &session_list) {
++ s = list_entry(p, struct rfcomm_session, list);
++ rfcomm_session_del(s);
++ }
++}
++
++static int rfcomm_run(void *unused)
++{
++ rfcomm_thread = current;
++
++ atomic_inc(&running);
++
++ daemonize(); reparent_to_init();
++
++ sigfillset(&current->blocked);
++ set_fs(KERNEL_DS);
++
++ sprintf(current->comm, "krfcommd");
++
++ BT_DBG("");
++
++ rfcomm_add_listener(BDADDR_ANY);
++
++ rfcomm_worker();
++
++ rfcomm_kill_listener();
++
++ atomic_dec(&running);
++ return 0;
++}
++
++/* ---- Proc fs support ---- */
++static int rfcomm_dlc_dump(char *buf)
++{
++ struct rfcomm_session *s;
++ struct sock *sk;
++ struct list_head *p, *pp;
++ char *ptr = buf;
++
++ rfcomm_lock();
++
++ list_for_each(p, &session_list) {
++ s = list_entry(p, struct rfcomm_session, list);
++ sk = s->sock->sk;
++
++ list_for_each(pp, &s->dlcs) {
++ struct rfcomm_dlc *d;
++ d = list_entry(pp, struct rfcomm_dlc, list);
++
++ ptr += sprintf(ptr, "dlc %s %s %ld %d %d %d %d\n",
++ batostr(&bluez_pi(sk)->src), batostr(&bluez_pi(sk)->dst),
++ d->state, d->dlci, d->mtu, d->rx_credits, d->tx_credits);
++ }
++ }
++
++ rfcomm_unlock();
++
++ return ptr - buf;
++}
++
++extern int rfcomm_sock_dump(char *buf);
++
++static int rfcomm_read_proc(char *buf, char **start, off_t offset, int count, int *eof, void *priv)
++{
++ char *ptr = buf;
++ int len;
++
++ BT_DBG("count %d, offset %ld", count, offset);
++
++ ptr += rfcomm_dlc_dump(ptr);
++ ptr += rfcomm_sock_dump(ptr);
++ len = ptr - buf;
++
++ if (len <= count + offset)
++ *eof = 1;
++
++ *start = buf + offset;
++ len -= offset;
++
++ if (len > count)
++ len = count;
++ if (len < 0)
++ len = 0;
++
++ return len;
++}
++
++/* ---- Initialization ---- */
++int __init rfcomm_init(void)
++{
++ l2cap_load();
++
++ kernel_thread(rfcomm_run, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
++
++ rfcomm_init_sockets();
++
++#ifdef CONFIG_BLUEZ_RFCOMM_TTY
++ rfcomm_init_ttys();
++#endif
++
++ create_proc_read_entry("bluetooth/rfcomm", 0, 0, rfcomm_read_proc, NULL);
++
++ BT_INFO("BlueZ RFCOMM ver %s", VERSION);
++ BT_INFO("Copyright (C) 2002 Maxim Krasnyansky <maxk@qualcomm.com>");
++ BT_INFO("Copyright (C) 2002 Marcel Holtmann <marcel@holtmann.org>");
++ return 0;
++}
++
++void rfcomm_cleanup(void)
++{
++ /* Terminate working thread.
++ * ie. Set terminate flag and wake it up */
++ atomic_inc(&terminate);
++ rfcomm_schedule(RFCOMM_SCHED_STATE);
++
++ /* Wait until thread is running */
++ while (atomic_read(&running))
++ schedule();
++
++ remove_proc_entry("bluetooth/rfcomm", NULL);
++
++#ifdef CONFIG_BLUEZ_RFCOMM_TTY
++ rfcomm_cleanup_ttys();
++#endif
++
++ rfcomm_cleanup_sockets();
++ return;
++}
++
++module_init(rfcomm_init);
++module_exit(rfcomm_cleanup);
++
++MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>, Marcel Holtmann <marcel@holtmann.org>");
++MODULE_DESCRIPTION("BlueZ RFCOMM ver " VERSION);
++MODULE_LICENSE("GPL");
+diff -urN linux-2.4.18/net/bluetooth/rfcomm/crc.c linux-2.4.18-mh15/net/bluetooth/rfcomm/crc.c
+--- linux-2.4.18/net/bluetooth/rfcomm/crc.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/net/bluetooth/rfcomm/crc.c 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,71 @@
++/*
++ RFCOMM implementation for Linux Bluetooth stack (BlueZ).
++ Copyright (C) 2002 Maxim Krasnyansky <maxk@qualcomm.com>
++ Copyright (C) 2002 Marcel Holtmann <marcel@holtmann.org>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License version 2 as
++ published by the Free Software Foundation;
++
++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++
++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
++ SOFTWARE IS DISCLAIMED.
++*/
++
++/*
++ * RFCOMM FCS calculation.
++ *
++ * $Id$
++ */
++
++/* reversed, 8-bit, poly=0x07 */
++unsigned char rfcomm_crc_table[256] = {
++ 0x00, 0x91, 0xe3, 0x72, 0x07, 0x96, 0xe4, 0x75,
++ 0x0e, 0x9f, 0xed, 0x7c, 0x09, 0x98, 0xea, 0x7b,
++ 0x1c, 0x8d, 0xff, 0x6e, 0x1b, 0x8a, 0xf8, 0x69,
++ 0x12, 0x83, 0xf1, 0x60, 0x15, 0x84, 0xf6, 0x67,
++
++ 0x38, 0xa9, 0xdb, 0x4a, 0x3f, 0xae, 0xdc, 0x4d,
++ 0x36, 0xa7, 0xd5, 0x44, 0x31, 0xa0, 0xd2, 0x43,
++ 0x24, 0xb5, 0xc7, 0x56, 0x23, 0xb2, 0xc0, 0x51,
++ 0x2a, 0xbb, 0xc9, 0x58, 0x2d, 0xbc, 0xce, 0x5f,
++
++ 0x70, 0xe1, 0x93, 0x02, 0x77, 0xe6, 0x94, 0x05,
++ 0x7e, 0xef, 0x9d, 0x0c, 0x79, 0xe8, 0x9a, 0x0b,
++ 0x6c, 0xfd, 0x8f, 0x1e, 0x6b, 0xfa, 0x88, 0x19,
++ 0x62, 0xf3, 0x81, 0x10, 0x65, 0xf4, 0x86, 0x17,
++
++ 0x48, 0xd9, 0xab, 0x3a, 0x4f, 0xde, 0xac, 0x3d,
++ 0x46, 0xd7, 0xa5, 0x34, 0x41, 0xd0, 0xa2, 0x33,
++ 0x54, 0xc5, 0xb7, 0x26, 0x53, 0xc2, 0xb0, 0x21,
++ 0x5a, 0xcb, 0xb9, 0x28, 0x5d, 0xcc, 0xbe, 0x2f,
++
++ 0xe0, 0x71, 0x03, 0x92, 0xe7, 0x76, 0x04, 0x95,
++ 0xee, 0x7f, 0x0d, 0x9c, 0xe9, 0x78, 0x0a, 0x9b,
++ 0xfc, 0x6d, 0x1f, 0x8e, 0xfb, 0x6a, 0x18, 0x89,
++ 0xf2, 0x63, 0x11, 0x80, 0xf5, 0x64, 0x16, 0x87,
++
++ 0xd8, 0x49, 0x3b, 0xaa, 0xdf, 0x4e, 0x3c, 0xad,
++ 0xd6, 0x47, 0x35, 0xa4, 0xd1, 0x40, 0x32, 0xa3,
++ 0xc4, 0x55, 0x27, 0xb6, 0xc3, 0x52, 0x20, 0xb1,
++ 0xca, 0x5b, 0x29, 0xb8, 0xcd, 0x5c, 0x2e, 0xbf,
++
++ 0x90, 0x01, 0x73, 0xe2, 0x97, 0x06, 0x74, 0xe5,
++ 0x9e, 0x0f, 0x7d, 0xec, 0x99, 0x08, 0x7a, 0xeb,
++ 0x8c, 0x1d, 0x6f, 0xfe, 0x8b, 0x1a, 0x68, 0xf9,
++ 0x82, 0x13, 0x61, 0xf0, 0x85, 0x14, 0x66, 0xf7,
++
++ 0xa8, 0x39, 0x4b, 0xda, 0xaf, 0x3e, 0x4c, 0xdd,
++ 0xa6, 0x37, 0x45, 0xd4, 0xa1, 0x30, 0x42, 0xd3,
++ 0xb4, 0x25, 0x57, 0xc6, 0xb3, 0x22, 0x50, 0xc1,
++ 0xba, 0x2b, 0x59, 0xc8, 0xbd, 0x2c, 0x5e, 0xcf
++};
+diff -urN linux-2.4.18/net/bluetooth/rfcomm/Makefile linux-2.4.18-mh15/net/bluetooth/rfcomm/Makefile
+--- linux-2.4.18/net/bluetooth/rfcomm/Makefile 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/net/bluetooth/rfcomm/Makefile 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,11 @@
++#
++# Makefile for the Linux Bluetooth RFCOMM layer
++#
++
++O_TARGET := rfcomm.o
++
++obj-y := core.o sock.o crc.o
++obj-$(CONFIG_BLUEZ_RFCOMM_TTY) += tty.o
++obj-m += $(O_TARGET)
++
++include $(TOPDIR)/Rules.make
+diff -urN linux-2.4.18/net/bluetooth/rfcomm/sock.c linux-2.4.18-mh15/net/bluetooth/rfcomm/sock.c
+--- linux-2.4.18/net/bluetooth/rfcomm/sock.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/net/bluetooth/rfcomm/sock.c 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,847 @@
++/*
++ RFCOMM implementation for Linux Bluetooth stack (BlueZ).
++ Copyright (C) 2002 Maxim Krasnyansky <maxk@qualcomm.com>
++ Copyright (C) 2002 Marcel Holtmann <marcel@holtmann.org>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License version 2 as
++ published by the Free Software Foundation;
++
++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++
++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
++ SOFTWARE IS DISCLAIMED.
++*/
++
++/*
++ * RFCOMM sockets.
++ *
++ * $Id$
++ */
++
++#include <linux/config.h>
++#include <linux/module.h>
++
++#include <linux/types.h>
++#include <linux/errno.h>
++#include <linux/kernel.h>
++#include <linux/major.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/poll.h>
++#include <linux/fcntl.h>
++#include <linux/init.h>
++#include <linux/skbuff.h>
++#include <linux/interrupt.h>
++#include <linux/socket.h>
++#include <linux/skbuff.h>
++#include <linux/list.h>
++#include <net/sock.h>
++
++#include <asm/system.h>
++#include <asm/uaccess.h>
++
++#include <net/bluetooth/bluetooth.h>
++#include <net/bluetooth/rfcomm.h>
++
++#ifndef CONFIG_BLUEZ_RFCOMM_DEBUG
++#undef BT_DBG
++#define BT_DBG(D...)
++#endif
++
++static struct proto_ops rfcomm_sock_ops;
++
++static struct bluez_sock_list rfcomm_sk_list = {
++ lock: RW_LOCK_UNLOCKED
++};
++
++static void rfcomm_sock_close(struct sock *sk);
++static void rfcomm_sock_kill(struct sock *sk);
++
++/* ---- DLC callbacks ----
++ *
++ * called under rfcomm_dlc_lock()
++ */
++static void rfcomm_sk_data_ready(struct rfcomm_dlc *d, struct sk_buff *skb)
++{
++ struct sock *sk = d->owner;
++ if (!sk)
++ return;
++
++ atomic_add(skb->len, &sk->rmem_alloc);
++ skb_queue_tail(&sk->receive_queue, skb);
++ sk->data_ready(sk, skb->len);
++
++ if (atomic_read(&sk->rmem_alloc) >= sk->rcvbuf)
++ rfcomm_dlc_throttle(d);
++}
++
++static void rfcomm_sk_state_change(struct rfcomm_dlc *d, int err)
++{
++ struct sock *sk = d->owner, *parent;
++ if (!sk)
++ return;
++
++ BT_DBG("dlc %p state %ld err %d", d, d->state, err);
++
++ bh_lock_sock(sk);
++
++ if (err)
++ sk->err = err;
++ sk->state = d->state;
++
++ parent = bluez_pi(sk)->parent;
++ if (!parent) {
++ if (d->state == BT_CONNECTED)
++ rfcomm_session_getaddr(d->session, &bluez_pi(sk)->src, NULL);
++ sk->state_change(sk);
++ } else
++ parent->data_ready(parent, 0);
++
++ bh_unlock_sock(sk);
++}
++
++/* ---- Socket functions ---- */
++static struct sock *__rfcomm_get_sock_by_addr(u8 channel, bdaddr_t *src)
++{
++ struct sock *sk;
++
++ for (sk = rfcomm_sk_list.head; sk; sk = sk->next) {
++ if (rfcomm_pi(sk)->channel == channel &&
++ !bacmp(&bluez_pi(sk)->src, src))
++ break;
++ }
++
++ return sk;
++}
++
++/* Find socket with channel and source bdaddr.
++ * Returns closest match.
++ */
++static struct sock *__rfcomm_get_sock_by_channel(int state, u8 channel, bdaddr_t *src)
++{
++ struct sock *sk, *sk1 = NULL;
++
++ for (sk = rfcomm_sk_list.head; sk; sk = sk->next) {
++ if (state && sk->state != state)
++ continue;
++
++ if (rfcomm_pi(sk)->channel == channel) {
++ /* Exact match. */
++ if (!bacmp(&bluez_pi(sk)->src, src))
++ break;
++
++ /* Closest match */
++ if (!bacmp(&bluez_pi(sk)->src, BDADDR_ANY))
++ sk1 = sk;
++ }
++ }
++ return sk ? sk : sk1;
++}
++
++/* Find socket with given address (channel, src).
++ * Returns locked socket */
++static inline struct sock *rfcomm_get_sock_by_channel(int state, u8 channel, bdaddr_t *src)
++{
++ struct sock *s;
++ read_lock(&rfcomm_sk_list.lock);
++ s = __rfcomm_get_sock_by_channel(state, channel, src);
++ if (s) bh_lock_sock(s);
++ read_unlock(&rfcomm_sk_list.lock);
++ return s;
++}
++
++static void rfcomm_sock_destruct(struct sock *sk)
++{
++ struct rfcomm_dlc *d = rfcomm_pi(sk)->dlc;
++
++ BT_DBG("sk %p dlc %p", sk, d);
++
++ skb_queue_purge(&sk->receive_queue);
++ skb_queue_purge(&sk->write_queue);
++
++ rfcomm_dlc_lock(d);
++ rfcomm_pi(sk)->dlc = NULL;
++
++ /* Detach DLC if it's owned by this socket */
++ if (d->owner == sk)
++ d->owner = NULL;
++ rfcomm_dlc_unlock(d);
++
++ rfcomm_dlc_put(d);
++
++ MOD_DEC_USE_COUNT;
++}
++
++static void rfcomm_sock_cleanup_listen(struct sock *parent)
++{
++ struct sock *sk;
++
++ BT_DBG("parent %p", parent);
++
++ /* Close not yet accepted dlcs */
++ while ((sk = bluez_accept_dequeue(parent, NULL))) {
++ rfcomm_sock_close(sk);
++ rfcomm_sock_kill(sk);
++ }
++
++ parent->state = BT_CLOSED;
++ parent->zapped = 1;
++}
++
++/* Kill socket (only if zapped and orphan)
++ * Must be called on unlocked socket.
++ */
++static void rfcomm_sock_kill(struct sock *sk)
++{
++ if (!sk->zapped || sk->socket)
++ return;
++
++ BT_DBG("sk %p state %d refcnt %d", sk, sk->state, atomic_read(&sk->refcnt));
++
++ /* Kill poor orphan */
++ bluez_sock_unlink(&rfcomm_sk_list, sk);
++ sk->dead = 1;
++ sock_put(sk);
++}
++
++static void __rfcomm_sock_close(struct sock *sk)
++{
++ struct rfcomm_dlc *d = rfcomm_pi(sk)->dlc;
++
++ BT_DBG("sk %p state %d socket %p", sk, sk->state, sk->socket);
++
++ switch (sk->state) {
++ case BT_LISTEN:
++ rfcomm_sock_cleanup_listen(sk);
++ break;
++
++ case BT_CONNECT:
++ case BT_CONNECT2:
++ case BT_CONFIG:
++ case BT_CONNECTED:
++ rfcomm_dlc_close(d, 0);
++
++ default:
++ sk->zapped = 1;
++ break;
++ }
++}
++
++/* Close socket.
++ * Must be called on unlocked socket.
++ */
++static void rfcomm_sock_close(struct sock *sk)
++{
++ lock_sock(sk);
++ __rfcomm_sock_close(sk);
++ release_sock(sk);
++}
++
++static void rfcomm_sock_init(struct sock *sk, struct sock *parent)
++{
++ BT_DBG("sk %p", sk);
++
++ if (parent)
++ sk->type = parent->type;
++}
++
++static struct sock *rfcomm_sock_alloc(struct socket *sock, int proto, int prio)
++{
++ struct rfcomm_dlc *d;
++ struct sock *sk;
++
++ sk = sk_alloc(PF_BLUETOOTH, prio, 1);
++ if (!sk)
++ return NULL;
++
++ d = rfcomm_dlc_alloc(prio);
++ if (!d) {
++ sk_free(sk);
++ return NULL;
++ }
++ d->data_ready = rfcomm_sk_data_ready;
++ d->state_change = rfcomm_sk_state_change;
++
++ rfcomm_pi(sk)->dlc = d;
++ d->owner = sk;
++
++ bluez_sock_init(sock, sk);
++
++ sk->zapped = 0;
++
++ sk->destruct = rfcomm_sock_destruct;
++ sk->sndtimeo = RFCOMM_CONN_TIMEOUT;
++
++ sk->sndbuf = RFCOMM_MAX_CREDITS * RFCOMM_DEFAULT_MTU * 10;
++ sk->rcvbuf = RFCOMM_MAX_CREDITS * RFCOMM_DEFAULT_MTU * 10;
++
++ sk->protocol = proto;
++ sk->state = BT_OPEN;
++
++ bluez_sock_link(&rfcomm_sk_list, sk);
++
++ BT_DBG("sk %p", sk);
++
++ MOD_INC_USE_COUNT;
++ return sk;
++}
++
++static int rfcomm_sock_create(struct socket *sock, int protocol)
++{
++ struct sock *sk;
++
++ BT_DBG("sock %p", sock);
++
++ sock->state = SS_UNCONNECTED;
++
++ if (sock->type != SOCK_STREAM && sock->type != SOCK_RAW)
++ return -ESOCKTNOSUPPORT;
++
++ sock->ops = &rfcomm_sock_ops;
++
++ if (!(sk = rfcomm_sock_alloc(sock, protocol, GFP_KERNEL)))
++ return -ENOMEM;
++
++ rfcomm_sock_init(sk, NULL);
++ return 0;
++}
++
++static int rfcomm_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len)
++{
++ struct sockaddr_rc *sa = (struct sockaddr_rc *) addr;
++ struct sock *sk = sock->sk;
++ int err = 0;
++
++ BT_DBG("sk %p %s", sk, batostr(&sa->rc_bdaddr));
++
++ if (!addr || addr->sa_family != AF_BLUETOOTH)
++ return -EINVAL;
++
++ lock_sock(sk);
++
++ if (sk->state != BT_OPEN) {
++ err = -EBADFD;
++ goto done;
++ }
++
++ write_lock_bh(&rfcomm_sk_list.lock);
++
++ if (sa->rc_channel && __rfcomm_get_sock_by_addr(sa->rc_channel, &sa->rc_bdaddr)) {
++ err = -EADDRINUSE;
++ } else {
++ /* Save source address */
++ bacpy(&bluez_pi(sk)->src, &sa->rc_bdaddr);
++ rfcomm_pi(sk)->channel = sa->rc_channel;
++ sk->state = BT_BOUND;
++ }
++
++ write_unlock_bh(&rfcomm_sk_list.lock);
++
++done:
++ release_sock(sk);
++ return err;
++}
++
++static int rfcomm_sock_connect(struct socket *sock, struct sockaddr *addr, int alen, int flags)
++{
++ struct sockaddr_rc *sa = (struct sockaddr_rc *) addr;
++ struct sock *sk = sock->sk;
++ struct rfcomm_dlc *d = rfcomm_pi(sk)->dlc;
++ int err = 0;
++
++ BT_DBG("sk %p", sk);
++
++ if (addr->sa_family != AF_BLUETOOTH || alen < sizeof(struct sockaddr_rc))
++ return -EINVAL;
++
++ if (sk->state != BT_OPEN && sk->state != BT_BOUND)
++ return -EBADFD;
++
++ if (sk->type != SOCK_STREAM)
++ return -EINVAL;
++
++ lock_sock(sk);
++
++ sk->state = BT_CONNECT;
++ bacpy(&bluez_pi(sk)->dst, &sa->rc_bdaddr);
++ rfcomm_pi(sk)->channel = sa->rc_channel;
++
++ err = rfcomm_dlc_open(d, &bluez_pi(sk)->src, &sa->rc_bdaddr, sa->rc_channel);
++ if (!err)
++ err = bluez_sock_wait_state(sk, BT_CONNECTED,
++ sock_sndtimeo(sk, flags & O_NONBLOCK));
++
++ release_sock(sk);
++ return err;
++}
++
++int rfcomm_sock_listen(struct socket *sock, int backlog)
++{
++ struct sock *sk = sock->sk;
++ int err = 0;
++
++ BT_DBG("sk %p backlog %d", sk, backlog);
++
++ lock_sock(sk);
++
++ if (sk->state != BT_BOUND) {
++ err = -EBADFD;
++ goto done;
++ }
++
++ sk->max_ack_backlog = backlog;
++ sk->ack_backlog = 0;
++ sk->state = BT_LISTEN;
++
++done:
++ release_sock(sk);
++ return err;
++}
++
++int rfcomm_sock_accept(struct socket *sock, struct socket *newsock, int flags)
++{
++ DECLARE_WAITQUEUE(wait, current);
++ struct sock *sk = sock->sk, *nsk;
++ long timeo;
++ int err = 0;
++
++ lock_sock(sk);
++
++ if (sk->state != BT_LISTEN) {
++ err = -EBADFD;
++ goto done;
++ }
++
++ timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK);
++
++ BT_DBG("sk %p timeo %ld", sk, timeo);
++
++ /* Wait for an incoming connection. (wake-one). */
++ add_wait_queue_exclusive(sk->sleep, &wait);
++ while (!(nsk = bluez_accept_dequeue(sk, newsock))) {
++ set_current_state(TASK_INTERRUPTIBLE);
++ if (!timeo) {
++ err = -EAGAIN;
++ break;
++ }
++
++ release_sock(sk);
++ timeo = schedule_timeout(timeo);
++ lock_sock(sk);
++
++ if (sk->state != BT_LISTEN) {
++ err = -EBADFD;
++ break;
++ }
++
++ if (signal_pending(current)) {
++ err = sock_intr_errno(timeo);
++ break;
++ }
++ }
++ set_current_state(TASK_RUNNING);
++ remove_wait_queue(sk->sleep, &wait);
++
++ if (err)
++ goto done;
++
++ newsock->state = SS_CONNECTED;
++
++ BT_DBG("new socket %p", nsk);
++
++done:
++ release_sock(sk);
++ return err;
++}
++
++static int rfcomm_sock_getname(struct socket *sock, struct sockaddr *addr, int *len, int peer)
++{
++ struct sockaddr_rc *sa = (struct sockaddr_rc *) addr;
++ struct sock *sk = sock->sk;
++
++ BT_DBG("sock %p, sk %p", sock, sk);
++
++ sa->rc_family = AF_BLUETOOTH;
++ sa->rc_channel = rfcomm_pi(sk)->channel;
++ if (peer)
++ bacpy(&sa->rc_bdaddr, &bluez_pi(sk)->dst);
++ else
++ bacpy(&sa->rc_bdaddr, &bluez_pi(sk)->src);
++
++ *len = sizeof(struct sockaddr_rc);
++ return 0;
++}
++
++static int rfcomm_sock_sendmsg(struct socket *sock, struct msghdr *msg, int len,
++ struct scm_cookie *scm)
++{
++ struct sock *sk = sock->sk;
++ struct rfcomm_dlc *d = rfcomm_pi(sk)->dlc;
++ struct sk_buff *skb;
++ int err, size;
++ int sent = 0;
++
++ if (msg->msg_flags & MSG_OOB)
++ return -EOPNOTSUPP;
++
++ if (sk->shutdown & SEND_SHUTDOWN)
++ return -EPIPE;
++
++ BT_DBG("sock %p, sk %p", sock, sk);
++
++ lock_sock(sk);
++
++ while (len) {
++ size = min_t(uint, len, d->mtu);
++
++ skb = sock_alloc_send_skb(sk, size + RFCOMM_SKB_RESERVE,
++ msg->msg_flags & MSG_DONTWAIT, &err);
++ if (!skb)
++ break;
++ skb_reserve(skb, RFCOMM_SKB_HEAD_RESERVE);
++
++ err = memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size);
++ if (err) {
++ kfree_skb(skb);
++ sent = err;
++ break;
++ }
++
++ err = rfcomm_dlc_send(d, skb);
++ if (err < 0) {
++ kfree_skb(skb);
++ break;
++ }
++
++ sent += size;
++ len -= size;
++ }
++
++ release_sock(sk);
++
++ return sent ? sent : err;
++}
++
++static long rfcomm_sock_data_wait(struct sock *sk, long timeo)
++{
++ DECLARE_WAITQUEUE(wait, current);
++
++ add_wait_queue(sk->sleep, &wait);
++ for (;;) {
++ set_current_state(TASK_INTERRUPTIBLE);
++
++ if (skb_queue_len(&sk->receive_queue) || sk->err || (sk->shutdown & RCV_SHUTDOWN) ||
++ signal_pending(current) || !timeo)
++ break;
++
++ set_bit(SOCK_ASYNC_WAITDATA, &sk->socket->flags);
++ release_sock(sk);
++ timeo = schedule_timeout(timeo);
++ lock_sock(sk);
++ clear_bit(SOCK_ASYNC_WAITDATA, &sk->socket->flags);
++ }
++
++ __set_current_state(TASK_RUNNING);
++ remove_wait_queue(sk->sleep, &wait);
++ return timeo;
++}
++
++static int rfcomm_sock_recvmsg(struct socket *sock, struct msghdr *msg, int size,
++ int flags, struct scm_cookie *scm)
++{
++ struct sock *sk = sock->sk;
++ int target, err = 0, copied = 0;
++ long timeo;
++
++ if (flags & MSG_OOB)
++ return -EOPNOTSUPP;
++
++ msg->msg_namelen = 0;
++
++ BT_DBG("sk %p size %d", sk, size);
++
++ lock_sock(sk);
++
++ target = sock_rcvlowat(sk, flags & MSG_WAITALL, size);
++ timeo = sock_rcvtimeo(sk, flags & MSG_DONTWAIT);
++
++ do {
++ struct sk_buff *skb;
++ int chunk;
++
++ skb = skb_dequeue(&sk->receive_queue);
++ if (!skb) {
++ if (copied >= target)
++ break;
++
++ if ((err = sock_error(sk)) != 0)
++ break;
++ if (sk->shutdown & RCV_SHUTDOWN)
++ break;
++
++ err = -EAGAIN;
++ if (!timeo)
++ break;
++
++ timeo = rfcomm_sock_data_wait(sk, timeo);
++
++ if (signal_pending(current)) {
++ err = sock_intr_errno(timeo);
++ goto out;
++ }
++ continue;
++ }
++
++ chunk = min_t(unsigned int, skb->len, size);
++ if (memcpy_toiovec(msg->msg_iov, skb->data, chunk)) {
++ skb_queue_head(&sk->receive_queue, skb);
++ if (!copied)
++ copied = -EFAULT;
++ break;
++ }
++ copied += chunk;
++ size -= chunk;
++
++ if (!(flags & MSG_PEEK)) {
++ atomic_sub(chunk, &sk->rmem_alloc);
++
++ skb_pull(skb, chunk);
++ if (skb->len) {
++ skb_queue_head(&sk->receive_queue, skb);
++ break;
++ }
++ kfree_skb(skb);
++
++ } else {
++ /* put message back and return */
++ skb_queue_head(&sk->receive_queue, skb);
++ break;
++ }
++ } while (size);
++
++out:
++ if (atomic_read(&sk->rmem_alloc) <= (sk->rcvbuf >> 2))
++ rfcomm_dlc_unthrottle(rfcomm_pi(sk)->dlc);
++
++ release_sock(sk);
++ return copied ? : err;
++}
++
++static int rfcomm_sock_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen)
++{
++ struct sock *sk = sock->sk;
++ int err = 0;
++
++ BT_DBG("sk %p", sk);
++
++ lock_sock(sk);
++
++ switch (optname) {
++ default:
++ err = -ENOPROTOOPT;
++ break;
++ };
++
++ release_sock(sk);
++ return err;
++}
++
++static int rfcomm_sock_getsockopt(struct socket *sock, int level, int optname, char *optval, int *optlen)
++{
++ struct sock *sk = sock->sk;
++ int len, err = 0;
++
++ BT_DBG("sk %p", sk);
++
++ if (get_user(len, optlen))
++ return -EFAULT;
++
++ lock_sock(sk);
++
++ switch (optname) {
++ default:
++ err = -ENOPROTOOPT;
++ break;
++ };
++
++ release_sock(sk);
++ return err;
++}
++
++static int rfcomm_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
++{
++ struct sock *sk = sock->sk;
++ int err;
++
++ lock_sock(sk);
++
++#ifdef CONFIG_BLUEZ_RFCOMM_TTY
++ err = rfcomm_dev_ioctl(sk, cmd, arg);
++#else
++ err = -EOPNOTSUPP;
++#endif
++
++ release_sock(sk);
++
++ return err;
++}
++
++static int rfcomm_sock_shutdown(struct socket *sock, int how)
++{
++ struct sock *sk = sock->sk;
++ int err = 0;
++
++ BT_DBG("sock %p, sk %p", sock, sk);
++
++ if (!sk) return 0;
++
++ lock_sock(sk);
++ if (!sk->shutdown) {
++ sk->shutdown = SHUTDOWN_MASK;
++ __rfcomm_sock_close(sk);
++
++ if (sk->linger)
++ err = bluez_sock_wait_state(sk, BT_CLOSED, sk->lingertime);
++ }
++ release_sock(sk);
++ return err;
++}
++
++static int rfcomm_sock_release(struct socket *sock)
++{
++ struct sock *sk = sock->sk;
++ int err = 0;
++
++ BT_DBG("sock %p, sk %p", sock, sk);
++
++ if (!sk)
++ return 0;
++
++ err = rfcomm_sock_shutdown(sock, 2);
++
++ sock_orphan(sk);
++ rfcomm_sock_kill(sk);
++ return err;
++}
++
++/* ---- RFCOMM core layer callbacks ----
++ *
++ * called under rfcomm_lock()
++ */
++int rfcomm_connect_ind(struct rfcomm_session *s, u8 channel, struct rfcomm_dlc **d)
++{
++ struct sock *sk, *parent;
++ bdaddr_t src, dst;
++ int result = 0;
++
++ BT_DBG("session %p channel %d", s, channel);
++
++ rfcomm_session_getaddr(s, &src, &dst);
++
++ /* Check if we have socket listening on this channel */
++ parent = rfcomm_get_sock_by_channel(BT_LISTEN, channel, &src);
++ if (!parent)
++ return 0;
++
++ /* Check for backlog size */
++ if (parent->ack_backlog > parent->max_ack_backlog) {
++ BT_DBG("backlog full %d", parent->ack_backlog);
++ goto done;
++ }
++
++ sk = rfcomm_sock_alloc(NULL, BTPROTO_RFCOMM, GFP_ATOMIC);
++ if (!sk)
++ goto done;
++
++ rfcomm_sock_init(sk, parent);
++ bacpy(&bluez_pi(sk)->src, &src);
++ bacpy(&bluez_pi(sk)->dst, &dst);
++ rfcomm_pi(sk)->channel = channel;
++
++ sk->state = BT_CONFIG;
++ bluez_accept_enqueue(parent, sk);
++
++ /* Accept connection and return socket DLC */
++ *d = rfcomm_pi(sk)->dlc;
++ result = 1;
++
++done:
++ bh_unlock_sock(parent);
++ return result;
++}
++
++/* ---- Proc fs support ---- */
++int rfcomm_sock_dump(char *buf)
++{
++ struct bluez_sock_list *list = &rfcomm_sk_list;
++ struct rfcomm_pinfo *pi;
++ struct sock *sk;
++ char *ptr = buf;
++
++ write_lock_bh(&list->lock);
++
++ for (sk = list->head; sk; sk = sk->next) {
++ pi = rfcomm_pi(sk);
++ ptr += sprintf(ptr, "sk %s %s %d %d\n",
++ batostr(&bluez_pi(sk)->src), batostr(&bluez_pi(sk)->dst),
++ sk->state, rfcomm_pi(sk)->channel);
++ }
++
++ write_unlock_bh(&list->lock);
++
++ return ptr - buf;
++}
++
++static struct proto_ops rfcomm_sock_ops = {
++ family: PF_BLUETOOTH,
++ release: rfcomm_sock_release,
++ bind: rfcomm_sock_bind,
++ connect: rfcomm_sock_connect,
++ listen: rfcomm_sock_listen,
++ accept: rfcomm_sock_accept,
++ getname: rfcomm_sock_getname,
++ sendmsg: rfcomm_sock_sendmsg,
++ recvmsg: rfcomm_sock_recvmsg,
++ shutdown: rfcomm_sock_shutdown,
++ setsockopt: rfcomm_sock_setsockopt,
++ getsockopt: rfcomm_sock_getsockopt,
++ ioctl: rfcomm_sock_ioctl,
++ poll: bluez_sock_poll,
++ socketpair: sock_no_socketpair,
++ mmap: sock_no_mmap
++};
++
++static struct net_proto_family rfcomm_sock_family_ops = {
++ family: PF_BLUETOOTH,
++ create: rfcomm_sock_create
++};
++
++int rfcomm_init_sockets(void)
++{
++ int err;
++
++ if ((err = bluez_sock_register(BTPROTO_RFCOMM, &rfcomm_sock_family_ops))) {
++ BT_ERR("Can't register RFCOMM socket layer");
++ return err;
++ }
++
++ return 0;
++}
++
++void rfcomm_cleanup_sockets(void)
++{
++ int err;
++
++ /* Unregister socket, protocol and notifier */
++ if ((err = bluez_sock_unregister(BTPROTO_RFCOMM)))
++ BT_ERR("Can't unregister RFCOMM socket layer %d", err);
++}
+diff -urN linux-2.4.18/net/bluetooth/rfcomm/tty.c linux-2.4.18-mh15/net/bluetooth/rfcomm/tty.c
+--- linux-2.4.18/net/bluetooth/rfcomm/tty.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/net/bluetooth/rfcomm/tty.c 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,960 @@
++/*
++ RFCOMM implementation for Linux Bluetooth stack (BlueZ).
++ Copyright (C) 2002 Maxim Krasnyansky <maxk@qualcomm.com>
++ Copyright (C) 2002 Marcel Holtmann <marcel@holtmann.org>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License version 2 as
++ published by the Free Software Foundation;
++
++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++
++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
++ SOFTWARE IS DISCLAIMED.
++*/
++
++/*
++ * RFCOMM TTY.
++ *
++ * $Id$
++ */
++
++#include <linux/config.h>
++#include <linux/module.h>
++
++#include <linux/tty.h>
++#include <linux/tty_driver.h>
++#include <linux/tty_flip.h>
++
++#include <linux/slab.h>
++#include <linux/skbuff.h>
++
++#include <net/bluetooth/bluetooth.h>
++#include <net/bluetooth/rfcomm.h>
++
++#ifndef CONFIG_BLUEZ_RFCOMM_DEBUG
++#undef BT_DBG
++#define BT_DBG(D...)
++#endif
++
++#define RFCOMM_TTY_MAGIC 0x6d02 /* magic number for rfcomm struct */
++#define RFCOMM_TTY_PORTS RFCOMM_MAX_DEV /* whole lotta rfcomm devices */
++#define RFCOMM_TTY_MAJOR 216 /* device node major id of the usb/bluetooth.c driver */
++#define RFCOMM_TTY_MINOR 0
++
++struct rfcomm_dev {
++ struct list_head list;
++ atomic_t refcnt;
++
++ char name[12];
++ int id;
++ unsigned long flags;
++ int opened;
++ int err;
++
++ bdaddr_t src;
++ bdaddr_t dst;
++ u8 channel;
++
++ uint modem_status;
++
++ struct rfcomm_dlc *dlc;
++ struct tty_struct *tty;
++ wait_queue_head_t wait;
++ struct tasklet_struct wakeup_task;
++
++ atomic_t wmem_alloc;
++};
++
++static LIST_HEAD(rfcomm_dev_list);
++static rwlock_t rfcomm_dev_lock = RW_LOCK_UNLOCKED;
++
++static void rfcomm_dev_data_ready(struct rfcomm_dlc *dlc, struct sk_buff *skb);
++static void rfcomm_dev_state_change(struct rfcomm_dlc *dlc, int err);
++static void rfcomm_dev_modem_status(struct rfcomm_dlc *dlc, u8 v24_sig);
++
++static void rfcomm_tty_wakeup(unsigned long arg);
++
++/* ---- Device functions ---- */
++static void rfcomm_dev_destruct(struct rfcomm_dev *dev)
++{
++ struct rfcomm_dlc *dlc = dev->dlc;
++
++ BT_DBG("dev %p dlc %p", dev, dlc);
++
++ rfcomm_dlc_lock(dlc);
++ /* Detach DLC if it's owned by this dev */
++ if (dlc->owner == dev)
++ dlc->owner = NULL;
++ rfcomm_dlc_unlock(dlc);
++
++ rfcomm_dlc_put(dlc);
++ kfree(dev);
++
++ MOD_DEC_USE_COUNT;
++}
++
++static inline void rfcomm_dev_hold(struct rfcomm_dev *dev)
++{
++ atomic_inc(&dev->refcnt);
++}
++
++static inline void rfcomm_dev_put(struct rfcomm_dev *dev)
++{
++ /* The reason this isn't actually a race, as you no
++ doubt have a little voice screaming at you in your
++ head, is that the refcount should never actually
++ reach zero unless the device has already been taken
++ off the list, in rfcomm_dev_del(). And if that's not
++ true, we'll hit the BUG() in rfcomm_dev_destruct()
++ anyway. */
++ if (atomic_dec_and_test(&dev->refcnt))
++ rfcomm_dev_destruct(dev);
++}
++
++static struct rfcomm_dev *__rfcomm_dev_get(int id)
++{
++ struct rfcomm_dev *dev;
++ struct list_head *p;
++
++ list_for_each(p, &rfcomm_dev_list) {
++ dev = list_entry(p, struct rfcomm_dev, list);
++ if (dev->id == id)
++ return dev;
++ }
++
++ return NULL;
++}
++
++static inline struct rfcomm_dev *rfcomm_dev_get(int id)
++{
++ struct rfcomm_dev *dev;
++
++ read_lock(&rfcomm_dev_lock);
++
++ dev = __rfcomm_dev_get(id);
++ if (dev)
++ rfcomm_dev_hold(dev);
++
++ read_unlock(&rfcomm_dev_lock);
++
++ return dev;
++}
++
++static int rfcomm_dev_add(struct rfcomm_dev_req *req, struct rfcomm_dlc *dlc)
++{
++ struct rfcomm_dev *dev;
++ struct list_head *head = &rfcomm_dev_list, *p;
++ int err = 0;
++
++ BT_DBG("id %d channel %d", req->dev_id, req->channel);
++
++ dev = kmalloc(sizeof(struct rfcomm_dev), GFP_KERNEL);
++ if (!dev)
++ return -ENOMEM;
++ memset(dev, 0, sizeof(struct rfcomm_dev));
++
++ write_lock_bh(&rfcomm_dev_lock);
++
++ if (req->dev_id < 0) {
++ dev->id = 0;
++
++ list_for_each(p, &rfcomm_dev_list) {
++ if (list_entry(p, struct rfcomm_dev, list)->id != dev->id)
++ break;
++
++ dev->id++;
++ head = p;
++ }
++ } else {
++ dev->id = req->dev_id;
++
++ list_for_each(p, &rfcomm_dev_list) {
++ struct rfcomm_dev *entry = list_entry(p, struct rfcomm_dev, list);
++
++ if (entry->id == dev->id) {
++ err = -EADDRINUSE;
++ goto out;
++ }
++
++ if (entry->id > dev->id - 1)
++ break;
++
++ head = p;
++ }
++ }
++
++ if ((dev->id < 0) || (dev->id > RFCOMM_MAX_DEV - 1)) {
++ err = -ENFILE;
++ goto out;
++ }
++
++ sprintf(dev->name, "rfcomm%d", dev->id);
++
++ list_add(&dev->list, head);
++ atomic_set(&dev->refcnt, 1);
++
++ bacpy(&dev->src, &req->src);
++ bacpy(&dev->dst, &req->dst);
++ dev->channel = req->channel;
++
++ dev->flags = req->flags &
++ ((1 << RFCOMM_RELEASE_ONHUP) | (1 << RFCOMM_REUSE_DLC));
++
++ init_waitqueue_head(&dev->wait);
++ tasklet_init(&dev->wakeup_task, rfcomm_tty_wakeup, (unsigned long) dev);
++
++ rfcomm_dlc_lock(dlc);
++ dlc->data_ready = rfcomm_dev_data_ready;
++ dlc->state_change = rfcomm_dev_state_change;
++ dlc->modem_status = rfcomm_dev_modem_status;
++
++ dlc->owner = dev;
++ dev->dlc = dlc;
++ rfcomm_dlc_unlock(dlc);
++
++ MOD_INC_USE_COUNT;
++
++out:
++ write_unlock_bh(&rfcomm_dev_lock);
++
++ if (err) {
++ kfree(dev);
++ return err;
++ } else
++ return dev->id;
++}
++
++static void rfcomm_dev_del(struct rfcomm_dev *dev)
++{
++ BT_DBG("dev %p", dev);
++
++ write_lock_bh(&rfcomm_dev_lock);
++ list_del_init(&dev->list);
++ write_unlock_bh(&rfcomm_dev_lock);
++
++ rfcomm_dev_put(dev);
++}
++
++/* ---- Send buffer ---- */
++
++static inline unsigned int rfcomm_room(struct rfcomm_dlc *dlc)
++{
++ /* We can't let it be zero, because we don't get a callback
++ when tx_credits becomes nonzero, hence we'd never wake up */
++ return dlc->mtu * (dlc->tx_credits?:1);
++}
++
++static void rfcomm_wfree(struct sk_buff *skb)
++{
++ struct rfcomm_dev *dev = (void *) skb->sk;
++ atomic_sub(skb->truesize, &dev->wmem_alloc);
++ if (test_bit(RFCOMM_TTY_ATTACHED, &dev->flags))
++ tasklet_schedule(&dev->wakeup_task);
++ rfcomm_dev_put(dev);
++}
++
++static inline void rfcomm_set_owner_w(struct sk_buff *skb, struct rfcomm_dev *dev)
++{
++ rfcomm_dev_hold(dev);
++ atomic_add(skb->truesize, &dev->wmem_alloc);
++ skb->sk = (void *) dev;
++ skb->destructor = rfcomm_wfree;
++}
++
++static struct sk_buff *rfcomm_wmalloc(struct rfcomm_dev *dev, unsigned long size, int force, int priority)
++{
++ if (force || atomic_read(&dev->wmem_alloc) < rfcomm_room(dev->dlc)) {
++ struct sk_buff *skb = alloc_skb(size, priority);
++ if (skb) {
++ rfcomm_set_owner_w(skb, dev);
++ return skb;
++ }
++ }
++ return NULL;
++}
++
++/* ---- Device IOCTLs ---- */
++
++#define NOCAP_FLAGS ((1 << RFCOMM_REUSE_DLC) | (1 << RFCOMM_RELEASE_ONHUP))
++
++static int rfcomm_create_dev(struct sock *sk, unsigned long arg)
++{
++ struct rfcomm_dev_req req;
++ struct rfcomm_dlc *dlc;
++ int id;
++
++ if (copy_from_user(&req, (void *) arg, sizeof(req)))
++ return -EFAULT;
++
++ BT_DBG("sk %p dev_id %id flags 0x%x", sk, req.dev_id, req.flags);
++
++ if (req.flags != NOCAP_FLAGS && !capable(CAP_NET_ADMIN))
++ return -EPERM;
++
++ if (req.flags & (1 << RFCOMM_REUSE_DLC)) {
++ /* Socket must be connected */
++ if (sk->state != BT_CONNECTED)
++ return -EBADFD;
++
++ dlc = rfcomm_pi(sk)->dlc;
++ rfcomm_dlc_hold(dlc);
++ } else {
++ dlc = rfcomm_dlc_alloc(GFP_KERNEL);
++ if (!dlc)
++ return -ENOMEM;
++ }
++
++ id = rfcomm_dev_add(&req, dlc);
++ if (id < 0) {
++ rfcomm_dlc_put(dlc);
++ return id;
++ }
++
++ if (req.flags & (1 << RFCOMM_REUSE_DLC)) {
++ /* DLC is now used by device.
++ * Socket must be disconnected */
++ sk->state = BT_CLOSED;
++ }
++
++ return id;
++}
++
++static int rfcomm_release_dev(unsigned long arg)
++{
++ struct rfcomm_dev_req req;
++ struct rfcomm_dev *dev;
++
++ if (copy_from_user(&req, (void *) arg, sizeof(req)))
++ return -EFAULT;
++
++ BT_DBG("dev_id %id flags 0x%x", req.dev_id, req.flags);
++
++ if (!(dev = rfcomm_dev_get(req.dev_id)))
++ return -ENODEV;
++
++ if (dev->flags != NOCAP_FLAGS && !capable(CAP_NET_ADMIN)) {
++ rfcomm_dev_put(dev);
++ return -EPERM;
++ }
++
++ if (req.flags & (1 << RFCOMM_HANGUP_NOW))
++ rfcomm_dlc_close(dev->dlc, 0);
++
++ rfcomm_dev_del(dev);
++ rfcomm_dev_put(dev);
++ return 0;
++}
++
++static int rfcomm_get_dev_list(unsigned long arg)
++{
++ struct rfcomm_dev_list_req *dl;
++ struct rfcomm_dev_info *di;
++ struct list_head *p;
++ int n = 0, size, err;
++ u16 dev_num;
++
++ BT_DBG("");
++
++ if (get_user(dev_num, (u16 *) arg))
++ return -EFAULT;
++
++ if (!dev_num || dev_num > (PAGE_SIZE * 4) / sizeof(*di))
++ return -EINVAL;
++
++ size = sizeof(*dl) + dev_num * sizeof(*di);
++
++ if (!(dl = kmalloc(size, GFP_KERNEL)))
++ return -ENOMEM;
++
++ di = dl->dev_info;
++
++ read_lock_bh(&rfcomm_dev_lock);
++
++ list_for_each(p, &rfcomm_dev_list) {
++ struct rfcomm_dev *dev = list_entry(p, struct rfcomm_dev, list);
++ (di + n)->id = dev->id;
++ (di + n)->flags = dev->flags;
++ (di + n)->state = dev->dlc->state;
++ (di + n)->channel = dev->channel;
++ bacpy(&(di + n)->src, &dev->src);
++ bacpy(&(di + n)->dst, &dev->dst);
++ if (++n >= dev_num)
++ break;
++ }
++
++ read_unlock_bh(&rfcomm_dev_lock);
++
++ dl->dev_num = n;
++ size = sizeof(*dl) + n * sizeof(*di);
++
++ err = copy_to_user((void *) arg, dl, size);
++ kfree(dl);
++
++ return err ? -EFAULT : 0;
++}
++
++static int rfcomm_get_dev_info(unsigned long arg)
++{
++ struct rfcomm_dev *dev;
++ struct rfcomm_dev_info di;
++ int err = 0;
++
++ BT_DBG("");
++
++ if (copy_from_user(&di, (void *)arg, sizeof(di)))
++ return -EFAULT;
++
++ if (!(dev = rfcomm_dev_get(di.id)))
++ return -ENODEV;
++
++ di.flags = dev->flags;
++ di.channel = dev->channel;
++ di.state = dev->dlc->state;
++ bacpy(&di.src, &dev->src);
++ bacpy(&di.dst, &dev->dst);
++
++ if (copy_to_user((void *)arg, &di, sizeof(di)))
++ err = -EFAULT;
++
++ rfcomm_dev_put(dev);
++ return err;
++}
++
++int rfcomm_dev_ioctl(struct sock *sk, unsigned int cmd, unsigned long arg)
++{
++ BT_DBG("cmd %d arg %ld", cmd, arg);
++
++ switch (cmd) {
++ case RFCOMMCREATEDEV:
++ return rfcomm_create_dev(sk, arg);
++
++ case RFCOMMRELEASEDEV:
++ return rfcomm_release_dev(arg);
++
++ case RFCOMMGETDEVLIST:
++ return rfcomm_get_dev_list(arg);
++
++ case RFCOMMGETDEVINFO:
++ return rfcomm_get_dev_info(arg);
++ }
++
++ return -EINVAL;
++}
++
++/* ---- DLC callbacks ---- */
++static void rfcomm_dev_data_ready(struct rfcomm_dlc *dlc, struct sk_buff *skb)
++{
++ struct rfcomm_dev *dev = dlc->owner;
++ struct tty_struct *tty;
++
++ if (!dev || !(tty = dev->tty)) {
++ kfree_skb(skb);
++ return;
++ }
++
++ BT_DBG("dlc %p tty %p len %d", dlc, tty, skb->len);
++
++ if (test_bit(TTY_DONT_FLIP, &tty->flags)) {
++ register int i;
++ for (i = 0; i < skb->len; i++) {
++ if (tty->flip.count >= TTY_FLIPBUF_SIZE)
++ tty_flip_buffer_push(tty);
++
++ tty_insert_flip_char(tty, skb->data[i], 0);
++ }
++ tty_flip_buffer_push(tty);
++ } else
++ tty->ldisc.receive_buf(tty, skb->data, NULL, skb->len);
++
++ kfree_skb(skb);
++}
++
++static void rfcomm_dev_state_change(struct rfcomm_dlc *dlc, int err)
++{
++ struct rfcomm_dev *dev = dlc->owner;
++ if (!dev)
++ return;
++
++ BT_DBG("dlc %p dev %p err %d", dlc, dev, err);
++
++ dev->err = err;
++ wake_up_interruptible(&dev->wait);
++
++ if (dlc->state == BT_CLOSED) {
++ if (!dev->tty) {
++ if (test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags)) {
++ rfcomm_dev_hold(dev);
++ rfcomm_dev_del(dev);
++
++ /* We have to drop DLC lock here, otherwise
++ rfcomm_dev_put() will dead lock if it's
++ the last reference. */
++ rfcomm_dlc_unlock(dlc);
++ rfcomm_dev_put(dev);
++ rfcomm_dlc_lock(dlc);
++ }
++ } else
++ tty_hangup(dev->tty);
++ }
++}
++
++static void rfcomm_dev_modem_status(struct rfcomm_dlc *dlc, u8 v24_sig)
++{
++ struct rfcomm_dev *dev = dlc->owner;
++ if (!dev)
++ return;
++
++ BT_DBG("dlc %p dev %p v24_sig 0x%02x", dlc, dev, v24_sig);
++
++ dev->modem_status =
++ ((v24_sig & RFCOMM_V24_RTC) ? (TIOCM_DSR | TIOCM_DTR) : 0) |
++ ((v24_sig & RFCOMM_V24_RTR) ? (TIOCM_RTS | TIOCM_CTS) : 0) |
++ ((v24_sig & RFCOMM_V24_IC) ? TIOCM_RI : 0) |
++ ((v24_sig & RFCOMM_V24_DV) ? TIOCM_CD : 0);
++}
++
++/* ---- TTY functions ---- */
++static void rfcomm_tty_wakeup(unsigned long arg)
++{
++ struct rfcomm_dev *dev = (void *) arg;
++ struct tty_struct *tty = dev->tty;
++ if (!tty)
++ return;
++
++ BT_DBG("dev %p tty %p", dev, tty);
++
++ if (test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags) && tty->ldisc.write_wakeup)
++ (tty->ldisc.write_wakeup)(tty);
++
++ wake_up_interruptible(&tty->write_wait);
++#ifdef SERIAL_HAVE_POLL_WAIT
++ wake_up_interruptible(&tty->poll_wait);
++#endif
++}
++
++static int rfcomm_tty_open(struct tty_struct *tty, struct file *filp)
++{
++ DECLARE_WAITQUEUE(wait, current);
++ struct rfcomm_dev *dev;
++ struct rfcomm_dlc *dlc;
++ int err, id;
++
++ id = MINOR(tty->device) - tty->driver.minor_start;
++
++ BT_DBG("tty %p id %d", tty, id);
++
++ /* We don't leak this refcount. For reasons which are not entirely
++ clear, the TTY layer will call our ->close() method even if the
++ open fails. We decrease the refcount there, and decreasing it
++ here too would cause breakage. */
++ dev = rfcomm_dev_get(id);
++ if (!dev)
++ return -ENODEV;
++
++ BT_DBG("dev %p dst %s channel %d opened %d", dev, batostr(&dev->dst), dev->channel, dev->opened);
++
++ if (dev->opened++ != 0)
++ return 0;
++
++ dlc = dev->dlc;
++
++ /* Attach TTY and open DLC */
++
++ rfcomm_dlc_lock(dlc);
++ tty->driver_data = dev;
++ dev->tty = tty;
++ rfcomm_dlc_unlock(dlc);
++ set_bit(RFCOMM_TTY_ATTACHED, &dev->flags);
++
++ err = rfcomm_dlc_open(dlc, &dev->src, &dev->dst, dev->channel);
++ if (err < 0)
++ return err;
++
++ /* Wait for DLC to connect */
++ add_wait_queue(&dev->wait, &wait);
++ while (1) {
++ set_current_state(TASK_INTERRUPTIBLE);
++
++ if (dlc->state == BT_CLOSED) {
++ err = -dev->err;
++ break;
++ }
++
++ if (dlc->state == BT_CONNECTED)
++ break;
++
++ if (signal_pending(current)) {
++ err = -EINTR;
++ break;
++ }
++
++ schedule();
++ }
++ set_current_state(TASK_RUNNING);
++ remove_wait_queue(&dev->wait, &wait);
++
++ return err;
++}
++
++static void rfcomm_tty_close(struct tty_struct *tty, struct file *filp)
++{
++ struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data;
++ if (!dev)
++ return;
++
++ BT_DBG("tty %p dev %p dlc %p opened %d", tty, dev, dev->dlc, dev->opened);
++
++ if (--dev->opened == 0) {
++ /* Close DLC and dettach TTY */
++ rfcomm_dlc_close(dev->dlc, 0);
++
++ clear_bit(RFCOMM_TTY_ATTACHED, &dev->flags);
++ tasklet_kill(&dev->wakeup_task);
++
++ rfcomm_dlc_lock(dev->dlc);
++ tty->driver_data = NULL;
++ dev->tty = NULL;
++ rfcomm_dlc_unlock(dev->dlc);
++ }
++
++ rfcomm_dev_put(dev);
++}
++
++static int rfcomm_tty_write(struct tty_struct *tty, int from_user, const unsigned char *buf, int count)
++{
++ struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data;
++ struct rfcomm_dlc *dlc = dev->dlc;
++ struct sk_buff *skb;
++ int err = 0, sent = 0, size;
++
++ BT_DBG("tty %p from_user %d count %d", tty, from_user, count);
++
++ while (count) {
++ size = min_t(uint, count, dlc->mtu);
++
++ if (from_user)
++ skb = rfcomm_wmalloc(dev, size + RFCOMM_SKB_RESERVE, 0, GFP_KERNEL);
++ else
++ skb = rfcomm_wmalloc(dev, size + RFCOMM_SKB_RESERVE, 0, GFP_ATOMIC);
++
++ if (!skb)
++ break;
++
++ skb_reserve(skb, RFCOMM_SKB_HEAD_RESERVE);
++
++ if (from_user)
++ copy_from_user(skb_put(skb, size), buf + sent, size);
++ else
++ memcpy(skb_put(skb, size), buf + sent, size);
++
++ if ((err = rfcomm_dlc_send(dlc, skb)) < 0) {
++ kfree_skb(skb);
++ break;
++ }
++
++ sent += size;
++ count -= size;
++ }
++
++ return sent ? sent : err;
++}
++
++static void rfcomm_tty_put_char(struct tty_struct *tty, unsigned char ch)
++{
++ struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data;
++ struct rfcomm_dlc *dlc = dev->dlc;
++ struct sk_buff *skb;
++
++ BT_DBG("tty %p char %x", tty, ch);
++
++ skb = rfcomm_wmalloc(dev, 1 + RFCOMM_SKB_RESERVE, 1, GFP_ATOMIC);
++
++ if (!skb)
++ return;
++
++ skb_reserve(skb, RFCOMM_SKB_HEAD_RESERVE);
++
++ *(char *)skb_put(skb, 1) = ch;
++
++ if ((rfcomm_dlc_send(dlc, skb)) < 0)
++ kfree_skb(skb);
++}
++
++static int rfcomm_tty_write_room(struct tty_struct *tty)
++{
++ struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data;
++ int room;
++
++ BT_DBG("tty %p", tty);
++
++ room = rfcomm_room(dev->dlc) - atomic_read(&dev->wmem_alloc);
++ if (room < 0)
++ room = 0;
++
++ return room;
++}
++
++static int rfcomm_tty_set_modem_status(uint cmd, struct rfcomm_dlc *dlc, uint status)
++{
++ u8 v24_sig, mask;
++
++ BT_DBG("dlc %p cmd 0x%02x", dlc, cmd);
++
++ if (cmd == TIOCMSET)
++ v24_sig = 0;
++ else
++ rfcomm_dlc_get_modem_status(dlc, &v24_sig);
++
++ mask = ((status & TIOCM_DSR) ? RFCOMM_V24_RTC : 0) |
++ ((status & TIOCM_DTR) ? RFCOMM_V24_RTC : 0) |
++ ((status & TIOCM_RTS) ? RFCOMM_V24_RTR : 0) |
++ ((status & TIOCM_CTS) ? RFCOMM_V24_RTR : 0) |
++ ((status & TIOCM_RI) ? RFCOMM_V24_IC : 0) |
++ ((status & TIOCM_CD) ? RFCOMM_V24_DV : 0);
++
++ if (cmd == TIOCMBIC)
++ v24_sig &= ~mask;
++ else
++ v24_sig |= mask;
++
++ rfcomm_dlc_set_modem_status(dlc, v24_sig);
++ return 0;
++}
++
++static int rfcomm_tty_ioctl(struct tty_struct *tty, struct file *filp, unsigned int cmd, unsigned long arg)
++{
++ struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data;
++ struct rfcomm_dlc *dlc = dev->dlc;
++ uint status;
++ int err;
++
++ BT_DBG("tty %p cmd 0x%02x", tty, cmd);
++
++ switch (cmd) {
++ case TCGETS:
++ BT_DBG("TCGETS is not supported");
++ return -ENOIOCTLCMD;
++
++ case TCSETS:
++ BT_DBG("TCSETS is not supported");
++ return -ENOIOCTLCMD;
++
++ case TIOCMGET:
++ BT_DBG("TIOCMGET");
++
++ return put_user(dev->modem_status, (unsigned int *)arg);
++
++ case TIOCMSET: /* Turns on and off the lines as specified by the mask */
++ case TIOCMBIS: /* Turns on the lines as specified by the mask */
++ case TIOCMBIC: /* Turns off the lines as specified by the mask */
++ if ((err = get_user(status, (unsigned int *)arg)))
++ return err;
++ return rfcomm_tty_set_modem_status(cmd, dlc, status);
++
++ case TIOCMIWAIT:
++ BT_DBG("TIOCMIWAIT");
++ break;
++
++ case TIOCGICOUNT:
++ BT_DBG("TIOCGICOUNT");
++ break;
++
++ case TIOCGSERIAL:
++ BT_ERR("TIOCGSERIAL is not supported");
++ return -ENOIOCTLCMD;
++
++ case TIOCSSERIAL:
++ BT_ERR("TIOCSSERIAL is not supported");
++ return -ENOIOCTLCMD;
++
++ case TIOCSERGSTRUCT:
++ BT_ERR("TIOCSERGSTRUCT is not supported");
++ return -ENOIOCTLCMD;
++
++ case TIOCSERGETLSR:
++ BT_ERR("TIOCSERGETLSR is not supported");
++ return -ENOIOCTLCMD;
++
++ case TIOCSERCONFIG:
++ BT_ERR("TIOCSERCONFIG is not supported");
++ return -ENOIOCTLCMD;
++
++ default:
++ return -ENOIOCTLCMD; /* ioctls which we must ignore */
++
++ }
++
++ return -ENOIOCTLCMD;
++}
++
++#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
++
++static void rfcomm_tty_set_termios(struct tty_struct *tty, struct termios *old)
++{
++ BT_DBG("tty %p", tty);
++
++ if ((tty->termios->c_cflag == old->c_cflag) &&
++ (RELEVANT_IFLAG(tty->termios->c_iflag) == RELEVANT_IFLAG(old->c_iflag)))
++ return;
++
++ /* handle turning off CRTSCTS */
++ if ((old->c_cflag & CRTSCTS) && !(tty->termios->c_cflag & CRTSCTS)) {
++ BT_DBG("turning off CRTSCTS");
++ }
++}
++
++static void rfcomm_tty_throttle(struct tty_struct *tty)
++{
++ struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data;
++
++ BT_DBG("tty %p dev %p", tty, dev);
++
++ rfcomm_dlc_throttle(dev->dlc);
++}
++
++static void rfcomm_tty_unthrottle(struct tty_struct *tty)
++{
++ struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data;
++
++ BT_DBG("tty %p dev %p", tty, dev);
++
++ rfcomm_dlc_unthrottle(dev->dlc);
++}
++
++static int rfcomm_tty_chars_in_buffer(struct tty_struct *tty)
++{
++ struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data;
++ struct rfcomm_dlc *dlc = dev->dlc;
++
++ BT_DBG("tty %p dev %p", tty, dev);
++
++ if (skb_queue_len(&dlc->tx_queue))
++ return dlc->mtu;
++
++ return 0;
++}
++
++static void rfcomm_tty_flush_buffer(struct tty_struct *tty)
++{
++ struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data;
++ if (!dev)
++ return;
++
++ BT_DBG("tty %p dev %p", tty, dev);
++
++ skb_queue_purge(&dev->dlc->tx_queue);
++
++ if (test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags) && tty->ldisc.write_wakeup)
++ tty->ldisc.write_wakeup(tty);
++}
++
++static void rfcomm_tty_send_xchar(struct tty_struct *tty, char ch)
++{
++ BT_DBG("tty %p ch %c", tty, ch);
++}
++
++static void rfcomm_tty_wait_until_sent(struct tty_struct *tty, int timeout)
++{
++ BT_DBG("tty %p timeout %d", tty, timeout);
++}
++
++static void rfcomm_tty_hangup(struct tty_struct *tty)
++{
++ struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data;
++ if (!dev)
++ return;
++
++ BT_DBG("tty %p dev %p", tty, dev);
++
++ rfcomm_tty_flush_buffer(tty);
++
++ if (test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags))
++ rfcomm_dev_del(dev);
++}
++
++static int rfcomm_tty_read_proc(char *buf, char **start, off_t offset, int len, int *eof, void *unused)
++{
++ return 0;
++}
++
++/* ---- TTY structure ---- */
++static int rfcomm_tty_refcount; /* If we manage several devices */
++
++static struct tty_struct *rfcomm_tty_table[RFCOMM_TTY_PORTS];
++static struct termios *rfcomm_tty_termios[RFCOMM_TTY_PORTS];
++static struct termios *rfcomm_tty_termios_locked[RFCOMM_TTY_PORTS];
++
++static struct tty_driver rfcomm_tty_driver = {
++ magic: TTY_DRIVER_MAGIC,
++ driver_name: "rfcomm",
++#ifdef CONFIG_DEVFS_FS
++ name: "bluetooth/rfcomm/%d",
++#else
++ name: "rfcomm",
++#endif
++ major: RFCOMM_TTY_MAJOR,
++ minor_start: RFCOMM_TTY_MINOR,
++ num: RFCOMM_TTY_PORTS,
++ type: TTY_DRIVER_TYPE_SERIAL,
++ subtype: SERIAL_TYPE_NORMAL,
++ flags: TTY_DRIVER_REAL_RAW,
++
++ refcount: &rfcomm_tty_refcount,
++ table: rfcomm_tty_table,
++ termios: rfcomm_tty_termios,
++ termios_locked: rfcomm_tty_termios_locked,
++
++ open: rfcomm_tty_open,
++ close: rfcomm_tty_close,
++ put_char: rfcomm_tty_put_char,
++ write: rfcomm_tty_write,
++ write_room: rfcomm_tty_write_room,
++ chars_in_buffer: rfcomm_tty_chars_in_buffer,
++ flush_buffer: rfcomm_tty_flush_buffer,
++ ioctl: rfcomm_tty_ioctl,
++ throttle: rfcomm_tty_throttle,
++ unthrottle: rfcomm_tty_unthrottle,
++ set_termios: rfcomm_tty_set_termios,
++ send_xchar: rfcomm_tty_send_xchar,
++ stop: NULL,
++ start: NULL,
++ hangup: rfcomm_tty_hangup,
++ wait_until_sent: rfcomm_tty_wait_until_sent,
++ read_proc: rfcomm_tty_read_proc,
++};
++
++int rfcomm_init_ttys(void)
++{
++ int i;
++
++ /* Initalize our global data */
++ for (i = 0; i < RFCOMM_TTY_PORTS; i++)
++ rfcomm_tty_table[i] = NULL;
++
++ /* Register the TTY driver */
++ rfcomm_tty_driver.init_termios = tty_std_termios;
++ rfcomm_tty_driver.init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
++ rfcomm_tty_driver.flags = TTY_DRIVER_REAL_RAW;
++
++ if (tty_register_driver(&rfcomm_tty_driver)) {
++ BT_ERR("Can't register RFCOMM TTY driver");
++ return -1;
++ }
++
++ return 0;
++}
++
++void rfcomm_cleanup_ttys(void)
++{
++ tty_unregister_driver(&rfcomm_tty_driver);
++ return;
++}
+diff -urN linux-2.4.18/net/bluetooth/sco.c linux-2.4.18-mh15/net/bluetooth/sco.c
+--- linux-2.4.18/net/bluetooth/sco.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.4.18-mh15/net/bluetooth/sco.c 2004-08-01 16:26:23.000000000 +0200
+@@ -0,0 +1,1019 @@
++/*
++ BlueZ - Bluetooth protocol stack for Linux
++ Copyright (C) 2000-2001 Qualcomm Incorporated
++
++ Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License version 2 as
++ published by the Free Software Foundation;
++
++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++
++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
++ SOFTWARE IS DISCLAIMED.
++*/
++
++/*
++ * BlueZ SCO sockets.
++ *
++ * $Id$
++ */
++#define VERSION "0.3"
++
++#include <linux/config.h>
++#include <linux/module.h>
++
++#include <linux/types.h>
++#include <linux/errno.h>
++#include <linux/kernel.h>
++#include <linux/major.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/poll.h>
++#include <linux/fcntl.h>
++#include <linux/init.h>
++#include <linux/skbuff.h>
++#include <linux/interrupt.h>
++#include <linux/socket.h>
++#include <linux/skbuff.h>
++#include <linux/proc_fs.h>
++#include <linux/list.h>
++#include <net/sock.h>
++
++#include <asm/system.h>
++#include <asm/uaccess.h>
++
++#include <net/bluetooth/bluetooth.h>
++#include <net/bluetooth/hci_core.h>
++#include <net/bluetooth/sco.h>
++
++#ifndef SCO_DEBUG
++#undef BT_DBG
++#define BT_DBG( A... )
++#endif
++
++static struct proto_ops sco_sock_ops;
++
++static struct bluez_sock_list sco_sk_list = {
++ lock: RW_LOCK_UNLOCKED
++};
++
++static inline int sco_chan_add(struct sco_conn *conn, struct sock *sk, struct sock *parent);
++static void sco_chan_del(struct sock *sk, int err);
++static inline struct sock * sco_chan_get(struct sco_conn *conn);
++
++static int sco_conn_del(struct hci_conn *conn, int err);
++
++static void sco_sock_close(struct sock *sk);
++static void sco_sock_kill(struct sock *sk);
++
++/* ----- SCO timers ------ */
++static void sco_sock_timeout(unsigned long arg)
++{
++ struct sock *sk = (struct sock *) arg;
++
++ BT_DBG("sock %p state %d", sk, sk->state);
++
++ bh_lock_sock(sk);
++ sk->err = ETIMEDOUT;
++ sk->state_change(sk);
++ bh_unlock_sock(sk);
++
++ sco_sock_kill(sk);
++ sock_put(sk);
++}
++
++static void sco_sock_set_timer(struct sock *sk, long timeout)
++{
++ BT_DBG("sock %p state %d timeout %ld", sk, sk->state, timeout);
++
++ if (!mod_timer(&sk->timer, jiffies + timeout))
++ sock_hold(sk);
++}
++
++static void sco_sock_clear_timer(struct sock *sk)
++{
++ BT_DBG("sock %p state %d", sk, sk->state);
++
++ if (timer_pending(&sk->timer) && del_timer(&sk->timer))
++ __sock_put(sk);
++}
++
++static void sco_sock_init_timer(struct sock *sk)
++{
++ init_timer(&sk->timer);
++ sk->timer.function = sco_sock_timeout;
++ sk->timer.data = (unsigned long)sk;
++}
++
++/* -------- SCO connections --------- */
++static struct sco_conn *sco_conn_add(struct hci_conn *hcon, __u8 status)
++{
++ struct hci_dev *hdev = hcon->hdev;
++ struct sco_conn *conn;
++
++ if ((conn = hcon->sco_data))
++ return conn;
++
++ if (status)
++ return conn;
++
++ if (!(conn = kmalloc(sizeof(struct sco_conn), GFP_ATOMIC)))
++ return NULL;
++ memset(conn, 0, sizeof(struct sco_conn));
++
++ spin_lock_init(&conn->lock);
++
++ hcon->sco_data = conn;
++ conn->hcon = hcon;
++
++ conn->src = &hdev->bdaddr;
++ conn->dst = &hcon->dst;
++
++ if (hdev->sco_mtu > 0)
++ conn->mtu = hdev->sco_mtu;
++ else
++ conn->mtu = 60;
++
++ BT_DBG("hcon %p conn %p", hcon, conn);
++
++ MOD_INC_USE_COUNT;
++ return conn;
++}
++
++static int sco_conn_del(struct hci_conn *hcon, int err)
++{
++ struct sco_conn *conn;
++ struct sock *sk;
++
++ if (!(conn = hcon->sco_data))
++ return 0;
++
++ BT_DBG("hcon %p conn %p, err %d", hcon, conn, err);
++
++ /* Kill socket */
++ if ((sk = sco_chan_get(conn))) {
++ bh_lock_sock(sk);
++ sco_sock_clear_timer(sk);
++ sco_chan_del(sk, err);
++ bh_unlock_sock(sk);
++ sco_sock_kill(sk);
++ }
++
++ hcon->sco_data = NULL;
++ kfree(conn);
++
++ MOD_DEC_USE_COUNT;
++ return 0;
++}
++
++int sco_connect(struct sock *sk)
++{
++ bdaddr_t *src = &bluez_pi(sk)->src;
++ bdaddr_t *dst = &bluez_pi(sk)->dst;
++ struct sco_conn *conn;
++ struct hci_conn *hcon;
++ struct hci_dev *hdev;
++ int err = 0;
++
++ BT_DBG("%s -> %s", batostr(src), batostr(dst));
++
++ if (!(hdev = hci_get_route(dst, src)))
++ return -EHOSTUNREACH;
++
++ hci_dev_lock_bh(hdev);
++
++ err = -ENOMEM;
++
++ hcon = hci_connect(hdev, SCO_LINK, dst);
++ if (!hcon)
++ goto done;
++
++ conn = sco_conn_add(hcon, 0);
++ if (!conn) {
++ hci_conn_put(hcon);
++ goto done;
++ }
++
++ /* Update source addr of the socket */
++ bacpy(src, conn->src);
++
++ err = sco_chan_add(conn, sk, NULL);
++ if (err)
++ goto done;
++
++ if (hcon->state == BT_CONNECTED) {
++ sco_sock_clear_timer(sk);
++ sk->state = BT_CONNECTED;
++ } else {
++ sk->state = BT_CONNECT;
++ sco_sock_set_timer(sk, sk->sndtimeo);
++ }
++done:
++ hci_dev_unlock_bh(hdev);
++ hci_dev_put(hdev);
++ return err;
++}
++
++static inline int sco_send_frame(struct sock *sk, struct msghdr *msg, int len)
++{
++ struct sco_conn *conn = sco_pi(sk)->conn;
++ struct sk_buff *skb;
++ int err, count;
++
++ /* Check outgoing MTU */
++ if (len > conn->mtu)
++ return -EINVAL;
++
++ BT_DBG("sk %p len %d", sk, len);
++
++ count = MIN(conn->mtu, len);
++ if (!(skb = bluez_skb_send_alloc(sk, count, msg->msg_flags & MSG_DONTWAIT, &err)))
++ return err;
++
++ if (memcpy_fromiovec(skb_put(skb, count), msg->msg_iov, count)) {
++ err = -EFAULT;
++ goto fail;
++ }
++
++ if ((err = hci_send_sco(conn->hcon, skb)) < 0)
++ goto fail;
++
++ return count;
++
++fail:
++ kfree_skb(skb);
++ return err;
++}
++
++static inline void sco_recv_frame(struct sco_conn *conn, struct sk_buff *skb)
++{
++ struct sock *sk = sco_chan_get(conn);
++
++ if (!sk)
++ goto drop;
++
++ BT_DBG("sk %p len %d", sk, skb->len);
++
++ if (sk->state != BT_CONNECTED)
++ goto drop;
++
++ if (!sock_queue_rcv_skb(sk, skb))
++ return;
++
++drop:
++ kfree_skb(skb);
++ return;
++}
++
++/* -------- Socket interface ---------- */
++static struct sock *__sco_get_sock_by_addr(bdaddr_t *ba)
++{
++ struct sock *sk;
++
++ for (sk = sco_sk_list.head; sk; sk = sk->next) {
++ if (!bacmp(&bluez_pi(sk)->src, ba))
++ break;
++ }
++
++ return sk;
++}
++
++/* Find socket listening on source bdaddr.
++ * Returns closest match.
++ */
++static struct sock *sco_get_sock_listen(bdaddr_t *src)
++{
++ struct sock *sk, *sk1 = NULL;
++
++ read_lock(&sco_sk_list.lock);
++
++ for (sk = sco_sk_list.head; sk; sk = sk->next) {
++ if (sk->state != BT_LISTEN)
++ continue;
++
++ /* Exact match. */
++ if (!bacmp(&bluez_pi(sk)->src, src))
++ break;
++
++ /* Closest match */
++ if (!bacmp(&bluez_pi(sk)->src, BDADDR_ANY))
++ sk1 = sk;
++ }
++
++ read_unlock(&sco_sk_list.lock);
++
++ return sk ? sk : sk1;
++}
++
++static void sco_sock_destruct(struct sock *sk)
++{
++ BT_DBG("sk %p", sk);
++
++ skb_queue_purge(&sk->receive_queue);
++ skb_queue_purge(&sk->write_queue);
++
++ MOD_DEC_USE_COUNT;
++}
++
++static void sco_sock_cleanup_listen(struct sock *parent)
++{
++ struct sock *sk;
++
++ BT_DBG("parent %p", parent);
++
++ /* Close not yet accepted channels */
++ while ((sk = bluez_accept_dequeue(parent, NULL))) {
++ sco_sock_close(sk);
++ sco_sock_kill(sk);
++ }
++
++ parent->state = BT_CLOSED;
++ parent->zapped = 1;
++}
++
++/* Kill socket (only if zapped and orphan)
++ * Must be called on unlocked socket.
++ */
++static void sco_sock_kill(struct sock *sk)
++{
++ if (!sk->zapped || sk->socket)
++ return;
++
++ BT_DBG("sk %p state %d", sk, sk->state);
++
++ /* Kill poor orphan */
++ bluez_sock_unlink(&sco_sk_list, sk);
++ sk->dead = 1;
++ sock_put(sk);
++}
++
++/* Close socket.
++ * Must be called on unlocked socket.
++ */
++static void sco_sock_close(struct sock *sk)
++{
++ struct sco_conn *conn;
++
++ sco_sock_clear_timer(sk);
++
++ lock_sock(sk);
++
++ conn = sco_pi(sk)->conn;
++
++ BT_DBG("sk %p state %d conn %p socket %p", sk, sk->state, conn, sk->socket);
++
++ switch (sk->state) {
++ case BT_LISTEN:
++ sco_sock_cleanup_listen(sk);
++ break;
++
++ case BT_CONNECTED:
++ case BT_CONFIG:
++ case BT_CONNECT:
++ case BT_DISCONN:
++ sco_chan_del(sk, ECONNRESET);
++ break;
++
++ default:
++ sk->zapped = 1;
++ break;
++ };
++
++ release_sock(sk);
++}
++
++static void sco_sock_init(struct sock *sk, struct sock *parent)
++{
++ BT_DBG("sk %p", sk);
++
++ if (parent)
++ sk->type = parent->type;
++}
++
++static struct sock *sco_sock_alloc(struct socket *sock, int proto, int prio)
++{
++ struct sock *sk;
++
++ if (!(sk = sk_alloc(PF_BLUETOOTH, prio, 1)))
++ return NULL;
++
++ bluez_sock_init(sock, sk);
++
++ sk->zapped = 0;
++
++ sk->destruct = sco_sock_destruct;
++ sk->sndtimeo = SCO_CONN_TIMEOUT;
++
++ sk->protocol = proto;
++ sk->state = BT_OPEN;
++
++ sco_sock_init_timer(sk);
++
++ bluez_sock_link(&sco_sk_list, sk);
++
++ MOD_INC_USE_COUNT;
++ return sk;
++}
++
++static int sco_sock_create(struct socket *sock, int protocol)
++{
++ struct sock *sk;
++
++ BT_DBG("sock %p", sock);
++
++ sock->state = SS_UNCONNECTED;
++
++ if (sock->type != SOCK_SEQPACKET)
++ return -ESOCKTNOSUPPORT;
++
++ sock->ops = &sco_sock_ops;
++
++ if (!(sk = sco_sock_alloc(sock, protocol, GFP_KERNEL)))
++ return -ENOMEM;
++
++ sco_sock_init(sk, NULL);
++ return 0;
++}
++
++static int sco_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len)
++{
++ struct sockaddr_sco *sa = (struct sockaddr_sco *) addr;
++ struct sock *sk = sock->sk;
++ bdaddr_t *src = &sa->sco_bdaddr;
++ int err = 0;
++
++ BT_DBG("sk %p %s", sk, batostr(&sa->sco_bdaddr));
++
++ if (!addr || addr->sa_family != AF_BLUETOOTH)
++ return -EINVAL;
++
++ lock_sock(sk);
++
++ if (sk->state != BT_OPEN) {
++ err = -EBADFD;
++ goto done;
++ }
++
++ write_lock_bh(&sco_sk_list.lock);
++
++ if (bacmp(src, BDADDR_ANY) && __sco_get_sock_by_addr(src)) {
++ err = -EADDRINUSE;
++ } else {
++ /* Save source address */
++ bacpy(&bluez_pi(sk)->src, &sa->sco_bdaddr);
++ sk->state = BT_BOUND;
++ }
++
++ write_unlock_bh(&sco_sk_list.lock);
++
++done:
++ release_sock(sk);
++
++ return err;
++}
++
++static int sco_sock_connect(struct socket *sock, struct sockaddr *addr, int alen, int flags)
++{
++ struct sockaddr_sco *sa = (struct sockaddr_sco *) addr;
++ struct sock *sk = sock->sk;
++ int err = 0;
++
++
++ BT_DBG("sk %p", sk);
++
++ if (addr->sa_family != AF_BLUETOOTH || alen < sizeof(struct sockaddr_sco))
++ return -EINVAL;
++
++ if (sk->state != BT_OPEN && sk->state != BT_BOUND)
++ return -EBADFD;
++
++ if (sk->type != SOCK_SEQPACKET)
++ return -EINVAL;
++
++ lock_sock(sk);
++
++ /* Set destination address and psm */
++ bacpy(&bluez_pi(sk)->dst, &sa->sco_bdaddr);
++
++ if ((err = sco_connect(sk)))
++ goto done;
++
++ err = bluez_sock_wait_state(sk, BT_CONNECTED,
++ sock_sndtimeo(sk, flags & O_NONBLOCK));
++
++done:
++ release_sock(sk);
++ return err;
++}
++
++int sco_sock_listen(struct socket *sock, int backlog)
++{
++ struct sock *sk = sock->sk;
++ int err = 0;
++
++ BT_DBG("sk %p backlog %d", sk, backlog);
++
++ lock_sock(sk);
++
++ if (sk->state != BT_BOUND || sock->type != SOCK_SEQPACKET) {
++ err = -EBADFD;
++ goto done;
++ }
++
++ sk->max_ack_backlog = backlog;
++ sk->ack_backlog = 0;
++ sk->state = BT_LISTEN;
++
++done:
++ release_sock(sk);
++ return err;
++}
++
++int sco_sock_accept(struct socket *sock, struct socket *newsock, int flags)
++{
++ DECLARE_WAITQUEUE(wait, current);
++ struct sock *sk = sock->sk, *ch;
++ long timeo;
++ int err = 0;
++
++ lock_sock(sk);
++
++ if (sk->state != BT_LISTEN) {
++ err = -EBADFD;
++ goto done;
++ }
++
++ timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK);
++
++ BT_DBG("sk %p timeo %ld", sk, timeo);
++
++ /* Wait for an incoming connection. (wake-one). */
++ add_wait_queue_exclusive(sk->sleep, &wait);
++ while (!(ch = bluez_accept_dequeue(sk, newsock))) {
++ set_current_state(TASK_INTERRUPTIBLE);
++ if (!timeo) {
++ err = -EAGAIN;
++ break;
++ }
++
++ release_sock(sk);
++ timeo = schedule_timeout(timeo);
++ lock_sock(sk);
++
++ if (sk->state != BT_LISTEN) {
++ err = -EBADFD;
++ break;
++ }
++
++ if (signal_pending(current)) {
++ err = sock_intr_errno(timeo);
++ break;
++ }
++ }
++ set_current_state(TASK_RUNNING);
++ remove_wait_queue(sk->sleep, &wait);
++
++ if (err)
++ goto done;
++
++ newsock->state = SS_CONNECTED;
++
++ BT_DBG("new socket %p", ch);
++
++done:
++ release_sock(sk);
++ return err;
++}
++
++static int sco_sock_getname(struct socket *sock, struct sockaddr *addr, int *len, int peer)
++{
++ struct sockaddr_sco *sa = (struct sockaddr_sco *) addr;
++ struct sock *sk = sock->sk;
++
++ BT_DBG("sock %p, sk %p", sock, sk);
++
++ addr->sa_family = AF_BLUETOOTH;
++ *len = sizeof(struct sockaddr_sco);
++
++ if (peer)
++ bacpy(&sa->sco_bdaddr, &bluez_pi(sk)->dst);
++ else
++ bacpy(&sa->sco_bdaddr, &bluez_pi(sk)->src);
++
++ return 0;
++}
++
++static int sco_sock_sendmsg(struct socket *sock, struct msghdr *msg, int len, struct scm_cookie *scm)
++{
++ struct sock *sk = sock->sk;
++ int err = 0;
++
++ BT_DBG("sock %p, sk %p", sock, sk);
++
++ if (sk->err)
++ return sock_error(sk);
++
++ if (msg->msg_flags & MSG_OOB)
++ return -EOPNOTSUPP;
++
++ lock_sock(sk);
++
++ if (sk->state == BT_CONNECTED)
++ err = sco_send_frame(sk, msg, len);
++ else
++ err = -ENOTCONN;
++
++ release_sock(sk);
++ return err;
++}
++
++int sco_sock_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen)
++{
++ struct sock *sk = sock->sk;
++ int err = 0;
++
++ BT_DBG("sk %p", sk);
++
++ lock_sock(sk);
++
++ switch (optname) {
++ default:
++ err = -ENOPROTOOPT;
++ break;
++ };
++
++ release_sock(sk);
++ return err;
++}
++
++int sco_sock_getsockopt(struct socket *sock, int level, int optname, char *optval, int *optlen)
++{
++ struct sock *sk = sock->sk;
++ struct sco_options opts;
++ struct sco_conninfo cinfo;
++ int len, err = 0;
++
++ BT_DBG("sk %p", sk);
++
++ if (get_user(len, optlen))
++ return -EFAULT;
++
++ lock_sock(sk);
++
++ switch (optname) {
++ case SCO_OPTIONS:
++ if (sk->state != BT_CONNECTED) {
++ err = -ENOTCONN;
++ break;
++ }
++
++ opts.mtu = sco_pi(sk)->conn->mtu;
++
++ BT_DBG("mtu %d", opts.mtu);
++
++ len = MIN(len, sizeof(opts));
++ if (copy_to_user(optval, (char *)&opts, len))
++ err = -EFAULT;
++
++ break;
++
++ case SCO_CONNINFO:
++ if (sk->state != BT_CONNECTED) {
++ err = -ENOTCONN;
++ break;
++ }
++
++ cinfo.hci_handle = sco_pi(sk)->conn->hcon->handle;
++
++ len = MIN(len, sizeof(cinfo));
++ if (copy_to_user(optval, (char *)&cinfo, len))
++ err = -EFAULT;
++
++ break;
++
++ default:
++ err = -ENOPROTOOPT;
++ break;
++ };
++
++ release_sock(sk);
++ return err;
++}
++
++static int sco_sock_release(struct socket *sock)
++{
++ struct sock *sk = sock->sk;
++ int err = 0;
++
++ BT_DBG("sock %p, sk %p", sock, sk);
++
++ if (!sk)
++ return 0;
++
++ sco_sock_close(sk);
++ if (sk->linger) {
++ lock_sock(sk);
++ err = bluez_sock_wait_state(sk, BT_CLOSED, sk->lingertime);
++ release_sock(sk);
++ }
++
++ sock_orphan(sk);
++ sco_sock_kill(sk);
++ return err;
++}
++
++static void __sco_chan_add(struct sco_conn *conn, struct sock *sk, struct sock *parent)
++{
++ BT_DBG("conn %p", conn);
++
++ sco_pi(sk)->conn = conn;
++ conn->sk = sk;
++
++ if (parent)
++ bluez_accept_enqueue(parent, sk);
++}
++
++static inline int sco_chan_add(struct sco_conn *conn, struct sock *sk, struct sock *parent)
++{
++ int err = 0;
++
++ sco_conn_lock(conn);
++ if (conn->sk) {
++ err = -EBUSY;
++ } else {
++ __sco_chan_add(conn, sk, parent);
++ }
++ sco_conn_unlock(conn);
++ return err;
++}
++
++static inline struct sock * sco_chan_get(struct sco_conn *conn)
++{
++ struct sock *sk = NULL;
++ sco_conn_lock(conn);
++ sk = conn->sk;
++ sco_conn_unlock(conn);
++ return sk;
++}
++
++/* Delete channel.
++ * Must be called on the locked socket. */
++static void sco_chan_del(struct sock *sk, int err)
++{
++ struct sco_conn *conn;
++
++ conn = sco_pi(sk)->conn;
++
++ BT_DBG("sk %p, conn %p, err %d", sk, conn, err);
++
++ if (conn) {
++ sco_conn_lock(conn);
++ conn->sk = NULL;
++ sco_pi(sk)->conn = NULL;
++ sco_conn_unlock(conn);
++ hci_conn_put(conn->hcon);
++ }
++
++ sk->state = BT_CLOSED;
++ sk->err = err;
++ sk->state_change(sk);
++
++ sk->zapped = 1;
++}
++
++static void sco_conn_ready(struct sco_conn *conn)
++{
++ struct sock *parent, *sk;
++
++ BT_DBG("conn %p", conn);
++
++ sco_conn_lock(conn);
++
++ if ((sk = conn->sk)) {
++ sco_sock_clear_timer(sk);
++ bh_lock_sock(sk);
++ sk->state = BT_CONNECTED;
++ sk->state_change(sk);
++ bh_unlock_sock(sk);
++ } else {
++ parent = sco_get_sock_listen(conn->src);
++ if (!parent)
++ goto done;
++
++ bh_lock_sock(parent);
++
++ sk = sco_sock_alloc(NULL, BTPROTO_SCO, GFP_ATOMIC);
++ if (!sk) {
++ bh_unlock_sock(parent);
++ goto done;
++ }
++
++ sco_sock_init(sk, parent);
++
++ bacpy(&bluez_pi(sk)->src, conn->src);
++ bacpy(&bluez_pi(sk)->dst, conn->dst);
++
++ hci_conn_hold(conn->hcon);
++ __sco_chan_add(conn, sk, parent);
++
++ sk->state = BT_CONNECTED;
++
++ /* Wake up parent */
++ parent->data_ready(parent, 1);
++
++ bh_unlock_sock(parent);
++ }
++
++done:
++ sco_conn_unlock(conn);
++}
++
++/* ----- SCO interface with lower layer (HCI) ----- */
++int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 type)
++{
++ BT_DBG("hdev %s, bdaddr %s", hdev->name, batostr(bdaddr));
++
++ /* Always accept connection */
++ return HCI_LM_ACCEPT;
++}
++
++int sco_connect_cfm(struct hci_conn *hcon, __u8 status)
++{
++ BT_DBG("hcon %p bdaddr %s status %d", hcon, batostr(&hcon->dst), status);
++
++ if (hcon->type != SCO_LINK)
++ return 0;
++
++ if (!status) {
++ struct sco_conn *conn;
++
++ conn = sco_conn_add(hcon, status);
++ if (conn)
++ sco_conn_ready(conn);
++ } else
++ sco_conn_del(hcon, bterr(status));
++
++ return 0;
++}
++
++int sco_disconn_ind(struct hci_conn *hcon, __u8 reason)
++{
++ BT_DBG("hcon %p reason %d", hcon, reason);
++
++ if (hcon->type != SCO_LINK)
++ return 0;
++
++ sco_conn_del(hcon, bterr(reason));
++ return 0;
++}
++
++int sco_recv_scodata(struct hci_conn *hcon, struct sk_buff *skb)
++{
++ struct sco_conn *conn = hcon->sco_data;
++
++ if (!conn)
++ goto drop;
++
++ BT_DBG("conn %p len %d", conn, skb->len);
++
++ if (skb->len) {
++ sco_recv_frame(conn, skb);
++ return 0;
++ }
++
++drop:
++ kfree_skb(skb);
++ return 0;
++}
++
++/* ----- Proc fs support ------ */
++static int sco_sock_dump(char *buf, struct bluez_sock_list *list)
++{
++ struct sco_pinfo *pi;
++ struct sock *sk;
++ char *ptr = buf;
++
++ write_lock_bh(&list->lock);
++
++ for (sk = list->head; sk; sk = sk->next) {
++ pi = sco_pi(sk);
++ ptr += sprintf(ptr, "%s %s %d\n",
++ batostr(&bluez_pi(sk)->src), batostr(&bluez_pi(sk)->dst),
++ sk->state);
++ }
++
++ write_unlock_bh(&list->lock);
++
++ ptr += sprintf(ptr, "\n");
++
++ return ptr - buf;
++}
++
++static int sco_read_proc(char *buf, char **start, off_t offset, int count, int *eof, void *priv)
++{
++ char *ptr = buf;
++ int len;
++
++ BT_DBG("count %d, offset %ld", count, offset);
++
++ ptr += sco_sock_dump(ptr, &sco_sk_list);
++ len = ptr - buf;
++
++ if (len <= count + offset)
++ *eof = 1;
++
++ *start = buf + offset;
++ len -= offset;
++
++ if (len > count)
++ len = count;
++ if (len < 0)
++ len = 0;
++
++ return len;
++}
++
++static struct proto_ops sco_sock_ops = {
++ family: PF_BLUETOOTH,
++ release: sco_sock_release,
++ bind: sco_sock_bind,
++ connect: sco_sock_connect,
++ listen: sco_sock_listen,
++ accept: sco_sock_accept,
++ getname: sco_sock_getname,
++ sendmsg: sco_sock_sendmsg,
++ recvmsg: bluez_sock_recvmsg,
++ poll: bluez_sock_poll,
++ socketpair: sock_no_socketpair,
++ ioctl: sock_no_ioctl,
++ shutdown: sock_no_shutdown,
++ setsockopt: sco_sock_setsockopt,
++ getsockopt: sco_sock_getsockopt,
++ mmap: sock_no_mmap
++};
++
++static struct net_proto_family sco_sock_family_ops = {
++ family: PF_BLUETOOTH,
++ create: sco_sock_create
++};
++
++static struct hci_proto sco_hci_proto = {
++ name: "SCO",
++ id: HCI_PROTO_SCO,
++ connect_ind: sco_connect_ind,
++ connect_cfm: sco_connect_cfm,
++ disconn_ind: sco_disconn_ind,
++ recv_scodata: sco_recv_scodata,
++};
++
++int __init sco_init(void)
++{
++ int err;
++
++ if ((err = bluez_sock_register(BTPROTO_SCO, &sco_sock_family_ops))) {
++ BT_ERR("Can't register SCO socket layer");
++ return err;
++ }
++
++ if ((err = hci_register_proto(&sco_hci_proto))) {
++ BT_ERR("Can't register SCO protocol");
++ return err;
++ }
++
++ create_proc_read_entry("bluetooth/sco", 0, 0, sco_read_proc, NULL);
++
++ BT_INFO("BlueZ SCO ver %s Copyright (C) 2000,2001 Qualcomm Inc", VERSION);
++ BT_INFO("Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>");
++ return 0;
++}
++
++void sco_cleanup(void)
++{
++ int err;
++
++ remove_proc_entry("bluetooth/sco", NULL);
++
++ /* Unregister socket, protocol and notifier */
++ if ((err = bluez_sock_unregister(BTPROTO_SCO)))
++ BT_ERR("Can't unregister SCO socket layer %d", err);
++
++ if ((err = hci_unregister_proto(&sco_hci_proto)))
++ BT_ERR("Can't unregister SCO protocol %d", err);
++}
++
++module_init(sco_init);
++module_exit(sco_cleanup);
++
++MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>");
++MODULE_DESCRIPTION("BlueZ SCO ver " VERSION);
++MODULE_LICENSE("GPL");
+diff -urN linux-2.4.18/net/bluetooth/syms.c linux-2.4.18-mh15/net/bluetooth/syms.c
+--- linux-2.4.18/net/bluetooth/syms.c 2001-09-07 18:28:38.000000000 +0200
++++ linux-2.4.18-mh15/net/bluetooth/syms.c 2004-08-01 16:26:23.000000000 +0200
+@@ -25,7 +25,7 @@
+ /*
+ * BlueZ symbols.
+ *
+- * $Id$
++ * $Id$
+ */
+
+ #include <linux/config.h>
+@@ -39,25 +39,28 @@
+ #include <linux/socket.h>
+
+ #include <net/bluetooth/bluetooth.h>
+-#include <net/bluetooth/bluez.h>
+ #include <net/bluetooth/hci_core.h>
+
+ /* HCI Core */
+ EXPORT_SYMBOL(hci_register_dev);
+ EXPORT_SYMBOL(hci_unregister_dev);
++EXPORT_SYMBOL(hci_suspend_dev);
++EXPORT_SYMBOL(hci_resume_dev);
++
+ EXPORT_SYMBOL(hci_register_proto);
+ EXPORT_SYMBOL(hci_unregister_proto);
+-EXPORT_SYMBOL(hci_register_notifier);
+-EXPORT_SYMBOL(hci_unregister_notifier);
+
++EXPORT_SYMBOL(hci_get_route);
+ EXPORT_SYMBOL(hci_connect);
+-EXPORT_SYMBOL(hci_disconnect);
+ EXPORT_SYMBOL(hci_dev_get);
++EXPORT_SYMBOL(hci_conn_auth);
++EXPORT_SYMBOL(hci_conn_encrypt);
+
+ EXPORT_SYMBOL(hci_recv_frame);
+ EXPORT_SYMBOL(hci_send_acl);
+ EXPORT_SYMBOL(hci_send_sco);
+-EXPORT_SYMBOL(hci_send_raw);
++EXPORT_SYMBOL(hci_send_cmd);
++EXPORT_SYMBOL(hci_si_event);
+
+ /* BlueZ lib */
+ EXPORT_SYMBOL(bluez_dump);
+@@ -68,5 +71,11 @@
+ /* BlueZ sockets */
+ EXPORT_SYMBOL(bluez_sock_register);
+ EXPORT_SYMBOL(bluez_sock_unregister);
++EXPORT_SYMBOL(bluez_sock_init);
+ EXPORT_SYMBOL(bluez_sock_link);
+ EXPORT_SYMBOL(bluez_sock_unlink);
++EXPORT_SYMBOL(bluez_sock_recvmsg);
++EXPORT_SYMBOL(bluez_sock_poll);
++EXPORT_SYMBOL(bluez_accept_enqueue);
++EXPORT_SYMBOL(bluez_accept_dequeue);
++EXPORT_SYMBOL(bluez_sock_wait_state);
+diff -urN linux-2.4.18/net/netsyms.c linux-2.4.18-mh15/net/netsyms.c
+--- linux-2.4.18/net/netsyms.c 2002-02-25 20:38:14.000000000 +0100
++++ linux-2.4.18-mh15/net/netsyms.c 2004-08-01 16:26:23.000000000 +0200
+@@ -159,6 +159,7 @@
+ EXPORT_SYMBOL(put_cmsg);
+ EXPORT_SYMBOL(sock_kmalloc);
+ EXPORT_SYMBOL(sock_kfree_s);
++EXPORT_SYMBOL(sockfd_lookup);
+
+ #ifdef CONFIG_FILTER
+ EXPORT_SYMBOL(sk_run_filter);
diff --git a/linux/unslung-kernel-2.3r25/ext3flash-on-disk1.patch b/linux/unslung-kernel-2.3r25/ext3flash-on-disk1.patch
index e69de29bb2..80170f08ce 100644
--- a/linux/unslung-kernel-2.3r25/ext3flash-on-disk1.patch
+++ b/linux/unslung-kernel-2.3r25/ext3flash-on-disk1.patch
@@ -0,0 +1,41 @@
+--- linux-2.4.22/drivers/scsi/scsi_scan.c.orig 2004-11-06 14:01:22.000000000 +1030
++++ linux-2.4.22/drivers/scsi/scsi_scan.c 2004-11-06 15:00:33.000000000 +1030
+@@ -704,6 +704,7 @@
+ /* restrict device on certain port grant */
+ SDpnt->host->hostt->removable = SDpnt->removable;
+
++#if 0
+ if ( SDpnt->removable && (USB_DEVPATH_1 == SDpnt->host->hostt->port) )
+ {
+ create_proc_read_entry("usb_err",
+@@ -718,8 +719,9 @@
+ shpnt->porttype = 1;
+ return 0;
+ }
++#endif
+ // add by super
+- if (SDpnt->removable){
++ if (SDpnt->removable && (USB_DEVPATH_2 == SDpnt->host->hostt->port)){
+ printk("is removable disk \n");
+ *IXP425_GPIO_GPOUTR &= 0xfffb;
+ create_proc_read_entry("usb_conn",
+--- linux-2.4.22/drivers/usb/storage/usb.c.orig 2004-11-06 14:01:06.000000000 +1030
++++ linux-2.4.22/drivers/usb/storage/usb.c 2004-11-06 15:01:23.000000000 +1030
+@@ -787,6 +787,7 @@
+ ss->htmplt.removable = 1;
+ }
+ #endif
++#if 0
+ /* restrict device on certain port */
+ if ( ss->htmplt.removable && (USB_DEVPATH_1 == dev->devpath[0]))
+ if ( ss->htmplt.removable ^ (USB_DEVPATH_2 == dev->devpath[0]) )
+@@ -803,7 +804,8 @@
+ free_us_data( pre_ss, ss );
+ return NULL;
+ }
+- if (ss->htmplt.removable){
++#endif
++ if (ss->htmplt.removable && (USB_DEVPATH_2 == dev->devpath[0])){
+ printk("is removable disk \n");
+ *IXP425_GPIO_GPOUTR &= 0xfffb;
+ create_proc_read_entry("usb_conn",
diff --git a/lirc/files/lirc_sir-sa1100.patch b/lirc/files/lirc_sir-sa1100.patch
index e69de29bb2..245b28ac51 100644
--- a/lirc/files/lirc_sir-sa1100.patch
+++ b/lirc/files/lirc_sir-sa1100.patch
@@ -0,0 +1,10 @@
+--- lirc/drivers/lirc_sir/lirc_sir.c.orig 2004-11-18 17:30:17 +0100
++++ lirc/drivers/lirc_sir/lirc_sir.c 2004-11-18 17:30:21 +0100
+@@ -628,6 +628,7 @@
+ struct timeval curr_tv;
+ static unsigned long deltv;
+ #ifdef LIRC_ON_SA1100
++ unsigned long deltintrtv;
+ int status;
+ static int n=0;
+
diff --git a/m4/m4/make.patch b/m4/m4/make.patch
index e69de29bb2..3577b0b7db 100644
--- a/m4/m4/make.patch
+++ b/m4/m4/make.patch
@@ -0,0 +1,41 @@
+
+#
+# Patch managed by http://www.holgerschurig.de/patcher.html
+#
+
+--- m4-1.4.2/doc/Makefile.in~make
++++ m4-1.4.2/doc/Makefile.in
+@@ -57,9 +57,9 @@
+ date > $(srcdir)/stamp-vti
+
+ install: all
+- $(srcdir)/../mkinstalldirs $(infodir)
++ $(srcdir)/../mkinstalldirs $(DESTDIR)$(infodir)
+ cd $(srcdir) && for file in m4.info*; do \
+- $(INSTALL_DATA) $$file $(infodir)/$$file; \
++ $(INSTALL_DATA) $$file $(DESTDIR)$(infodir)/$$file; \
+ done
+
+ uninstall:
+--- m4-1.4.2/src/Makefile.in~make
++++ m4-1.4.2/src/Makefile.in
+@@ -35,7 +35,7 @@
+ prefix = @prefix@
+ exec_prefix = @exec_prefix@
+ transform = @program_transform_name@
+-bindir = $(exec_prefix)/bin
++bindir = @bindir@
+
+ COMPILE = $(CC) -c $(DEFS) $(INCLUDES) $(CPPFLAGS) $(CFLAGS)
+ LINK = $(CC) $(LDFLAGS) -o $@
+@@ -84,8 +84,8 @@
+ $(LINK) ansi2knr.o $(LIBS)
+
+ install: all
+- $(srcdir)/../mkinstalldirs $(bindir)
+- $(INSTALL_PROGRAM) m4 $(bindir)/`echo m4 | sed '$(transform)'`
++ $(srcdir)/../mkinstalldirs $(DESTDIR)$(bindir)
++ $(INSTALL_PROGRAM) m4 $(DESTDIR)$(bindir)/`echo m4 | sed '$(transform)'`
+
+ uninstall:
+ rm -f $(bindir)/`echo m4 | sed '$(transform)'`
diff --git a/make/make-3.80/SCCS.patch b/make/make-3.80/SCCS.patch
index e69de29bb2..c998e118ed 100644
--- a/make/make-3.80/SCCS.patch
+++ b/make/make-3.80/SCCS.patch
@@ -0,0 +1,16 @@
+
+#
+# Patch managed by http://www.mn-logistik.de/unsupported/pxa250/patcher
+#
+
+--- make-3.80/configure.in~SCCS
++++ make-3.80/configure.in
+@@ -264,7 +264,7 @@
+ [Define this if the SCCS 'get' command understands the '-G<file>' option.]);;
+ esac
+ fi
+-rm -f s.conftest conftoast
++rm -f s.conftest conftoast SCCS/*s.conftest
+
+ # Check the system to see if it provides GNU glob. If not, use our
+ # local version.
diff --git a/mozilla/firefox-1.0/extensions-hack.patch b/mozilla/firefox-1.0/extensions-hack.patch
index e69de29bb2..b3906b2be6 100644
--- a/mozilla/firefox-1.0/extensions-hack.patch
+++ b/mozilla/firefox-1.0/extensions-hack.patch
@@ -0,0 +1,10 @@
+--- mozilla/toolkit/mozapps/extensions/src/nsExtensionManager.js.in.old 2004-08-03 11:45:51.000000000 +0100
++++ mozilla/toolkit/mozapps/extensions/src/nsExtensionManager.js.in 2004-08-03 11:46:04.000000000 +0100
+@@ -1429,6 +1429,7 @@
+ win.close();
+
+ }
++ needsRestart = false;
+ return needsRestart;
+ },
+
diff --git a/mozilla/firefox-1.0/mozconfig b/mozilla/firefox-1.0/mozconfig
index e69de29bb2..025b396ac7 100644
--- a/mozilla/firefox-1.0/mozconfig
+++ b/mozilla/firefox-1.0/mozconfig
@@ -0,0 +1,53 @@
+. $topsrcdir/browser/config/mozconfig
+
+# use GTK+-2 widget set with XFT font rendering
+ac_add_options --enable-default-toolkit=gtk2
+ac_add_options --enable-xft
+ac_add_options --disable-freetype2
+
+# enable minimal profile support
+ac_add_options --disable-profilesharing
+ac_add_options --disable-profilelocking
+ac_add_options --enable-single-profile
+
+ac_add_options --with-system-zlib
+ac_add_options --with-system-jpeg
+ac_add_options --with-system-png
+
+ac_add_options --disable-accessibility
+ac_add_options --disable-composer
+#ac_add_options --enable-plaintext-editor-only
+ac_add_options --disable-mailnews
+ac_add_options --disable-ldap
+#ac_add_options --disable-postscript
+ac_add_options --disable-mathml
+ac_add_options --disable-jsd
+ac_add_options --disable-installer
+ac_add_options --disable-xprint
+ac_add_options --disable-necko-disk-cache
+
+# configure necko to allocate smaller network buffers
+ac_add_options --enable-necko-small-buffers
+
+# disable debug logging and tests
+#ac_add_options --disable-dtd-debug
+ac_add_options --disable-logging
+ac_add_options --disable-debug
+ac_add_options --disable-gtktest
+ac_add_options --disable-tests
+
+# build crypto module (PSM + NSS)
+ac_add_options --enable-crypto
+
+# build minimal set of protocol handlers
+ac_add_options --enable-necko-protocols=http,file,res,jar,ftp,about,viewsource
+
+# build minimal set of image decoders
+ac_add_options --enable-image-decoders=png,gif,jpeg
+
+#ac_add_options --enable-reorder
+#ac_add_options --enable-elf-dynstr-gc
+
+# enable static build
+#ac_add_options --disable-shared
+#ac_add_options --enable-static
diff --git a/netkit-rpc/netkit-rpc/gcc4.patch b/netkit-rpc/netkit-rpc/gcc4.patch
index e69de29bb2..c3ba7d94c3 100644
--- a/netkit-rpc/netkit-rpc/gcc4.patch
+++ b/netkit-rpc/netkit-rpc/gcc4.patch
@@ -0,0 +1,37 @@
+
+#
+# Patch managed by http://www.holgerschurig.de/patcher.html
+#
+
+--- netkit-rpc-0.17/rpcgen/rpc_cout.c~gcc4
++++ netkit-rpc-0.17/rpcgen/rpc_cout.c
+@@ -101,8 +101,6 @@
+ case DEF_TYPEDEF:
+ emit_typedef(def);
+ break;
+- default:
+- /* can't happen */
+ }
+ print_trailer();
+ }
+@@ -664,9 +662,6 @@
+ decl->name,decl->array_max);
+ emit_single_in_line(decl,flag,REL_VECTOR);
+ f_print(fout,"\t\t }\n\t\t };\n");
+-
+- default:
+- /* ?... do nothing I guess */
+ }
+ }
+
+--- netkit-rpc-0.17/rpcgen/rpc_hout.c~gcc4
++++ netkit-rpc-0.17/rpcgen/rpc_hout.c
+@@ -106,8 +106,6 @@
+ f_print(fout, "\n");
+ pprogramdef(def);
+ break;
+- default:
+- /* ?... shouldn't happen I guess */
+ }
+ }
+
diff --git a/netkit-tftp/netkit-tftp-0.17/mconfig.patch b/netkit-tftp/netkit-tftp-0.17/mconfig.patch
index e69de29bb2..26f2f2b6b6 100644
--- a/netkit-tftp/netkit-tftp-0.17/mconfig.patch
+++ b/netkit-tftp/netkit-tftp-0.17/mconfig.patch
@@ -0,0 +1,20 @@
+diff -urNd netkit-base-0.17/MCONFIG netkit-base-0.17-new/MCONFIG
+--- netkit-base-0.17/MCONFIG 1969-12-31 18:00:00.000000000 -0600
++++ netkit-base-0.17-new/MCONFIG 2002-10-12 13:33:16.000000000 -0500
+@@ -0,0 +1,16 @@
++BINDIR=/usr/bin
++SBINDIR=/usr/sbin
++MANDIR=/usr/man
++ROOTBINDIR=/bin
++BINMODE=755
++DAEMONMODE=755
++MANMODE=644
++SUIDMODE=4755
++PREFIX=/usr
++EXECPREFIX=/usr
++ROOTPREFIX=
++INSTALLROOT=
++CC=arm-linux-gcc
++CFLAGS=-I/usr/src/coding/projects/userspace/buildroot/output/staging/usr/include -O2 -Wall -W -Wpointer-arith -Wbad-function-cast -Wcast-qual -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations -Wnested-externs -Winline
++LDFLAGS=
++LIBS=
diff --git a/netkit-tftp/netkit-tftp-0.17/pack_tftphdr.patch b/netkit-tftp/netkit-tftp-0.17/pack_tftphdr.patch
index e69de29bb2..ea0fc8663f 100644
--- a/netkit-tftp/netkit-tftp-0.17/pack_tftphdr.patch
+++ b/netkit-tftp/netkit-tftp-0.17/pack_tftphdr.patch
@@ -0,0 +1,52 @@
+
+#
+# Patch managed by http://www.holgerschurig.de/patcher.html
+#
+
+--- netkit-tftp-0.17/include/arpa/tftp.h~pack_tftphdr.patch
++++ netkit-tftp-0.17/include/arpa/tftp.h
+@@ -10,10 +10,6 @@
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+- * 3. All advertising materials mentioning features or use of this software
+- * must display the following acknowledgement:
+- * This product includes software developed by the University of
+- * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+@@ -30,10 +26,7 @@
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+- * From: @(#)tftp.h 8.1 (Berkeley) 6/2/93
+- * $Id$
+- *
+- * (Fixed for netkit to have well-defined field sizes)
++ * @(#)tftp.h 8.1 (Berkeley) 6/2/93
+ */
+
+ #ifndef _ARPA_TFTP_H
+@@ -54,14 +47,14 @@
+ #define ERROR 05 /* error code */
+
+ struct tftphdr {
+- u_int16_t th_opcode; /* packet type */
++ short th_opcode; /* packet type */
+ union {
+- u_int16_t tu_block; /* block # */
+- u_int16_t tu_code; /* error code */
+- char tu_stuff[1]; /* request packet stuff */
+- } th_u;
+- char th_data[1]; /* data or error string */
+-};
++ unsigned short tu_block; /* block # */
++ short tu_code; /* error code */
++ char tu_stuff[1]; /* request packet stuff */
++ } __attribute__ ((__packed__)) th_u;
++ char th_data[1]; /* data or error string */
++} __attribute__ ((__packed__));
+
+ #define th_block th_u.tu_block
+ #define th_code th_u.tu_code
diff --git a/netkit-tftp/netkit-tftp-0.17/tftpd_add_debug.patch b/netkit-tftp/netkit-tftp-0.17/tftpd_add_debug.patch
index e69de29bb2..f7dfd58ed4 100644
--- a/netkit-tftp/netkit-tftp-0.17/tftpd_add_debug.patch
+++ b/netkit-tftp/netkit-tftp-0.17/tftpd_add_debug.patch
@@ -0,0 +1,28 @@
+
+#
+# Patch managed by http://www.holgerschurig.de/patcher.html
+#
+
+--- netkit-tftp-0.17/tftpd/tftpd.c~tftpd_add_debug
++++ netkit-tftp-0.17/tftpd/tftpd.c
+@@ -252,6 +252,8 @@
+ char *filename, *mode = NULL;
+
+ filename = cp = tp->th_stuff;
++ syslog(LOG_NOTICE, "tp->th_opcode=%x, tp = %p, &(tp->th_stuff)=%p, filename=%s", tp->th_opcode,tp, &(tp->th_stuff),filename);
++
+ again:
+ while (cp < buf + size) {
+ if (*cp == '\0')
+@@ -340,8 +342,10 @@
+ return(EACCESS);
+ }
+ }
+- if (stat(filename, &stbuf) < 0)
++ if (stat(filename, &stbuf) < 0) {
++ syslog(LOG_NOTICE, "file %s not found", filename);
+ return (errno == ENOENT ? ENOTFOUND : EACCESS);
++ }
+ #if 0
+ /*
+ * The idea is that symlinks are dangerous. However, a symlink
diff --git a/notez/notez-1.1.0/use-homedir.patch b/notez/notez-1.1.0/use-homedir.patch
index e69de29bb2..375b15449f 100644
--- a/notez/notez-1.1.0/use-homedir.patch
+++ b/notez/notez-1.1.0/use-homedir.patch
@@ -0,0 +1,92 @@
+--- notez-1.1.0/mainWindow.cpp.orig 2002-11-17 19:43:34 +0100
++++ notez-1.1.0/mainWindow.cpp 2004-11-21 22:55:06 +0100
+@@ -7,11 +7,11 @@
+
+ // check if the directory plain exists, if not
+ // create it
+- QString d1("Documents/text");
++ QString d1(QDir::homeDirPath() + "/Documents/text");
+ QDir pd1(d1);
+ if (!pd1.exists())
+ {
+- QDir pd1("Documents");
++ QDir pd1(QDir::homeDirPath() + "/Documents");
+ if (!pd1.mkdir("text", FALSE))
+ {
+ QMessageBox::critical( 0, "noteZ",
+@@ -22,11 +22,11 @@
+ }
+ }
+
+- QString d2("Documents/text/plain");
++ QString d2(QDir::homeDirPath() + "/Documents/text/plain");
+ QDir pd2(d2);
+ if (!pd2.exists())
+ {
+- QDir pd2("Documents/text");
++ QDir pd2(QDir::homeDirPath() + "/Documents/text");
+ if (!pd2.mkdir("plain", FALSE))
+ {
+ QMessageBox::critical( 0, "noteZ",
+@@ -40,14 +40,14 @@
+
+ unsigned int item, convertCount = 0;
+ QString strippedName;
+- QDir moveDir("Documents/text");
++ QDir moveDir(QDir::homeDirPath() + "/Documents/text");
+ moveDir.setFilter(QDir::Files);
+ moveDir.setNameFilter("[0-9]*-[0-9]*-[0-9]*-[0-9]*-[0-9]*-[0-9]*.txt");
+
+ for (item = 0; item < moveDir.count(); item++) {
+ strippedName = moveDir[item].left(moveDir[item].find("."));
+- QFile oldFile("Documents/text/" + moveDir[item]);
+- QFile newFile("Documents/text/plain/" + strippedName + ".ntz");
++ QFile oldFile(QDir::homeDirPath() + "/Documents/text/" + moveDir[item]);
++ QFile newFile(QDir::homeDirPath() + "/Documents/text/plain/" + strippedName + ".ntz");
+ if (!oldFile.open(IO_ReadOnly))
+ QMessageBox::warning(0, "File i/o error", "Could not read\nDocuments/text/" + moveDir[item] + "\nfor conversion.");
+
+@@ -104,7 +104,7 @@
+
+ case QMessageBox::Yes:
+ this->selected = notesList->currentItem();
+- QFile::remove("Documents/text/plain/" + fileList[notesList->currentItem()]);
++ QFile::remove(QDir::homeDirPath() + "/Documents/text/plain/" + fileList[notesList->currentItem()]);
+ refreshList(notesList);
+ break;
+
+@@ -146,7 +146,7 @@
+ QDate today(QDate::currentDate());
+ QTime now(QTime::currentTime());
+ QString name;
+- this->editFile(name.sprintf("Documents/text/plain/%4i-%02i-%02i-%02i-%02i-%02i.ntz", today.year(), today.month(), today.day(), now.hour(), now.minute(), now.second()),
++ this->editFile(name.sprintf(QDir::homeDirPath() + "/Documents/text/plain/%4i-%02i-%02i-%02i-%02i-%02i.ntz", today.year(), today.month(), today.day(), now.hour(), now.minute(), now.second()),
+ true );
+
+ }
+@@ -159,7 +159,7 @@
+ if( notesList->count() > 0 )
+ {
+ this->selected = number;
+- this->editFile("Documents/text/plain/" + fileList[number],
++ this->editFile(QDir::homeDirPath() + "/Documents/text/plain/" + fileList[number],
+ false);
+ }
+
+@@ -173,14 +173,14 @@
+
+ liste->clear();
+
+- fileList.setPath("Documents/text/plain/");
++ fileList.setPath(QDir::homeDirPath() + "/Documents/text/plain/");
+ fileList.setFilter(QDir::Files);
+ fileList.setSorting(QDir::Name);
+ fileList.setNameFilter("*.ntz");
+
+ for (item = 0; item < fileList.count(); item++) {
+
+- QFile datei("Documents/text/plain/" + fileList[item]);
++ QFile datei(QDir::homeDirPath() + "/Documents/text/plain/" + fileList[item]);
+
+ if (!datei.open(IO_ReadOnly)) {
+
diff --git a/nslu2-binary-only/unslung-ramdisk-2.3r25/able/mount_usbdevfs.patch b/nslu2-binary-only/unslung-ramdisk-2.3r25/able/mount_usbdevfs.patch
index e69de29bb2..744636d0d9 100644
--- a/nslu2-binary-only/unslung-ramdisk-2.3r25/able/mount_usbdevfs.patch
+++ b/nslu2-binary-only/unslung-ramdisk-2.3r25/able/mount_usbdevfs.patch
@@ -0,0 +1,10 @@
+--- nslu2-linksys-ramdisk-2.3r25/etc/rc.orig 2004-11-03 14:38:45.838082343 +1100
++++ nslu2-linksys-ramdisk-2.3r25/etc/rc 2004-11-03 16:27:08.572763797 +1100
+@@ -4,6 +4,7 @@
+
+ /usr/bin/Set_Led r_blinking &
+ mount -t proc proc /proc
++mount -t usbdevfs usbdevfs /proc/bus/usb
+ mount -o remount,rw /dev/root /
+ ifconfig lo 127.0.0.1
+ route add -net 127.0.0.0 netmask 255.255.255.0 lo
diff --git a/ode/files/config.h b/ode/files/config.h
index e69de29bb2..8711bd91b2 100644
--- a/ode/files/config.h
+++ b/ode/files/config.h
@@ -0,0 +1,48 @@
+/* per-machine configuration. this file is automatically generated. */
+
+#ifndef _ODE_CONFIG_H_
+#define _ODE_CONFIG_H_
+
+/* standard system headers */
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+#include <stdarg.h>
+#include <malloc.h>
+#include <alloca.h>
+#include <values.h>
+#include <float.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* is this a pentium on a gcc-based platform? */
+#undef PENTIUM
+
+/* integer types (we assume int >= 32 bits) */
+typedef char int8;
+typedef unsigned char uint8;
+typedef short int16;
+typedef unsigned short uint16;
+typedef int int32;
+typedef unsigned int uint32;
+
+/* an integer type that we can safely cast a pointer to and
+ * from without loss of bits.
+ */
+typedef unsigned int intP;
+
+/* select the base floating point type */
+#define dDOUBLE 1
+
+/* the floating point infinity */
+#define dInfinity DBL_MAX
+
+/* available functions */
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/popt/popt-1.7/intl.patch b/popt/popt-1.7/intl.patch
index e69de29bb2..a7becb1b1a 100644
--- a/popt/popt-1.7/intl.patch
+++ b/popt/popt-1.7/intl.patch
@@ -0,0 +1,15 @@
+
+#
+# Patch managed by http://www.holgerschurig.de/patcher.html
+#
+
+--- popt-1.7/Makefile.am~intl
++++ popt-1.7/Makefile.am
+@@ -33,6 +33,7 @@
+
+ include_HEADERS = popt.h
+ lib_LTLIBRARIES = libpopt.la
++libpopt_la_LIBADD = $(LTLIBINTL)
+ libpopt_la_SOURCES = popt.c findme.c poptparse.c poptconfig.c popthelp.c
+
+ man_MANS = popt.3
diff --git a/qpdf2/files/hack-the-hack.patch b/qpdf2/files/hack-the-hack.patch
index e69de29bb2..11df4e6027 100644
--- a/qpdf2/files/hack-the-hack.patch
+++ b/qpdf2/files/hack-the-hack.patch
@@ -0,0 +1,173 @@
+
+#
+# Patch managed by http://www.holgerschurig.de/patcher.html
+#
+
+--- qpdf2_2.2.1/qpdfview.cpp~hack-the-hack.patch
++++ qpdf2_2.2.1/qpdfview.cpp
+@@ -4,7 +4,7 @@
+ #ifdef __GNUC__
+ #pragma implementation
+ #endif
+-
++#define private public
+ #include "aconf.h"
+
+ #include "qpdf.h"
+--- qpdf2_2.2.1/qpdf.h~hack-the-hack.patch
++++ qpdf2_2.2.1/qpdf.h
+@@ -1,3 +1,4 @@
++#define private public
+ #ifndef __QPDF_H__
+ #define __QPDF_H__
+
+@@ -12,6 +13,8 @@
+
+ #include "signal.h"
+
++#define private public
++#include <qobject.h>
+ #include <qpe/qpeapplication.h>
+ #include <qpe/resource.h>
+ #include <qpe/applnk.h>
+--- qpdf2_2.2.1/qpdf.cpp~hack-the-hack.patch
++++ qpdf2_2.2.1/qpdf.cpp
+@@ -12,11 +12,11 @@
+ //
+ //========================================================================
+
+-
++#define private public
+ #include "qpdf.h"
+ #include <qpe/qpemenubar.h>
+ #include <qdir.h>
+-
++#include <errno.h>
+ #ifdef AUTOTEST
+ #define MAX_ZOOM 1.2
+ #else
+@@ -2636,15 +2636,6 @@
+ setRenameBar( false );
+ }
+
+-
+-#ifdef ARM
+-extern int errno;
+-#else
+-extern "C" {
+- int errno;
+-};
+-#endif
+-
+ void
+ QPDFWindow::writeConfig()
+ {
+--- qpdf2_2.2.1/textedit.cpp~hack-the-hack.patch
++++ qpdf2_2.2.1/textedit.cpp
+@@ -19,7 +19,7 @@
+ **********************************************************************/
+
+ #include <stdlib.h> //getenv
+-
++#define private public
+ #include <qpe/global.h>
+ #include <qpe/fileselector.h>
+ #include <qpe/applnk.h>
+--- qpdf2_2.2.1/autotest.cpp~hack-the-hack.patch
++++ qpdf2_2.2.1/autotest.cpp
+@@ -7,7 +7,7 @@
+ qobject.h - make the activate_xxx functions public
+
+ */
+-
++#define private public
+ #include "qpdf.h"
+ #ifdef AUTOTEST
+ //#define MEM_LEAKS_EVENT_COUNT 50000
+--- qpdf2_2.2.1/qpdffileselector.cpp~hack-the-hack.patch
++++ qpdf2_2.2.1/qpdffileselector.cpp
+@@ -3,7 +3,7 @@
+
+
+ */
+-
++#define private public
+ #include "qpdf.h"
+
+ #include <qwmatrix.h>
+--- qpdf2_2.2.1/qpdfconfig.cpp~hack-the-hack.patch
++++ qpdf2_2.2.1/qpdfconfig.cpp
+@@ -1,3 +1,4 @@
++#define private public
+ #include "qpdf.h"
+ #include <qpushbutton.h>
+ #include <qradiobutton.h>
+--- qpdf2_2.2.1/qqimage.cpp~hack-the-hack.patch
++++ qpdf2_2.2.1/qqimage.cpp
+@@ -8,7 +8,7 @@
+ "protected:" and add "friend class QQImage" to class QImage.
+
+ */
+-
++#define private public
+
+ /****************************************************************************
+ ** $Id$
+--- qpdf2_2.2.1/qdocnotes.cpp~hack-the-hack.patch
++++ qpdf2_2.2.1/qdocnotes.cpp
+@@ -5,7 +5,7 @@
+ #include <qpe/stringutil.h>
+ #include <qfile.h>
+ #include <qfileinfo.h>
+-
++#include <errno.h>
+ #define assert(X)
+
+ QDocNote::QDocNote()
+@@ -698,18 +698,6 @@
+ return(save(m_filepath, state));
+ }
+
+-
+-#ifdef ARM
+-extern int errno;
+-extern char *strerror(int);
+-#else
+-extern "C" {
+- int errno;
+- extern char *strerror(int);
+-}
+-#endif
+-
+-
+ bool
+ QDocNotes::save(const QString &filepath, QDocState *state)
+ {
+--- qpdf2_2.2.1/xpdf/PDFDoc.cc~hack-the-hack.patch
++++ qpdf2_2.2.1/xpdf/PDFDoc.cc
+@@ -7,7 +7,7 @@
+ //========================================================================
+
+ #include <aconf.h>
+-
++#include <errno.h>
+ #ifdef USE_GCC_PRAGMAS
+ #pragma implementation
+ #endif
+@@ -205,16 +205,6 @@
+ }
+ }
+
+-#ifdef ARM
+-extern int errno;
+-extern char *strerror(int);
+-#else
+-extern "C" {
+- int errno;
+- extern char *strerror(int);
+-}
+-#endif
+-
+ void PDFDoc::displayPage(OutputDev *out, int page, fouble hDPI, fouble vDPI,
+ int rotate, GBool doLinks,
+ GBool (*abortCheckCbk)(void *data),
diff --git a/qpealarmclock/qpealarmclock-1.0.4/fix-compile.patch b/qpealarmclock/qpealarmclock-1.0.4/fix-compile.patch
index e69de29bb2..bd69318a5c 100644
--- a/qpealarmclock/qpealarmclock-1.0.4/fix-compile.patch
+++ b/qpealarmclock/qpealarmclock-1.0.4/fix-compile.patch
@@ -0,0 +1,83 @@
+
+#
+# Patch managed by http://www.holgerschurig.de/patcher.html
+#
+
+--- qpealarmclock_V1.0.4/AlarmDetails.h~fix-compile
++++ qpealarmclock_V1.0.4/AlarmDetails.h
+@@ -64,25 +64,8 @@
+ WEEKDAYS_IGNORE = 0xFF // do not change any days
+ };
+
+- static const Weekdays flags[] =
+- {
+- WEEKDAYS_MONDAY,
+- WEEKDAYS_TUESDAY,
+- WEEKDAYS_WEDNSDAY,
+- WEEKDAYS_THURSDAY,
+- WEEKDAYS_FRIDAY,
+- WEEKDAYS_SATURDAY,
+- WEEKDAYS_SUNDAY
+- };
+-
+- static const AlarmDetails::Weekdays weekdaysGroups[] =
+- {
+- AlarmDetails::WEEKDAYS_NONE,
+- AlarmDetails::WEEKDAYS_ALL,
+- AlarmDetails::WEEKDAYS_WORKDAYS,
+- AlarmDetails::WEEKDAYS_WEEKEND,
+- AlarmDetails::WEEKDAYS_IGNORE
+- };
++ static const Weekdays flags[];
++ static const AlarmDetails::Weekdays weekdaysGroups[];
+
+ AlarmDetails(int alarmNumberInit);
+ ~AlarmDetails();
+--- qpealarmclock_V1.0.4/AlarmDetails.cpp~fix-compile
++++ qpealarmclock_V1.0.4/AlarmDetails.cpp
+@@ -3,22 +3,39 @@
+ *
+ * Copyright (C) 2002 Dafydd Walters <dwalters@users.sourceforge.net>
+ * Modified by Anton Maslovsky <my-zaurus@narod.ru> 2003/01/28
+- *
++ *
+ * This program may be modified or redistributed under the terms of the GNU
+- * General Public License (GPL), version 2 or later. A copy of the GNU GPL
+- * should have accompanied the source code of this program. If not, please
++ * General Public License (GPL), version 2 or later. A copy of the GNU GPL
++ * should have accompanied the source code of this program. If not, please
+ * write to the Free Software Foundation Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * This program is distributed without any warranty.
+- */
++ */
+
+ #include "AlarmDetails.h"
+ #include <qpe/config.h>
+ #include <qpe/alarmserver.h>
+
+-const AlarmDetails::Weekdays AlarmDetails::flags[];
+-const AlarmDetails::Weekdays AlarmDetails::weekdaysGroups[];
++const AlarmDetails::Weekdays AlarmDetails::flags[] =
++{
++ WEEKDAYS_MONDAY,
++ WEEKDAYS_TUESDAY,
++ WEEKDAYS_WEDNSDAY,
++ WEEKDAYS_THURSDAY,
++ WEEKDAYS_FRIDAY,
++ WEEKDAYS_SATURDAY,
++ WEEKDAYS_SUNDAY
++};
++
++const AlarmDetails::Weekdays AlarmDetails::weekdaysGroups[] =
++{
++ AlarmDetails::WEEKDAYS_NONE,
++ AlarmDetails::WEEKDAYS_ALL,
++ AlarmDetails::WEEKDAYS_WORKDAYS,
++ AlarmDetails::WEEKDAYS_WEEKEND,
++ AlarmDetails::WEEKDAYS_IGNORE
++};
+
+ AlarmDetails::AlarmDetails(int alarmNumberInit) : alarmNumber(alarmNumberInit)
+ {
diff --git a/rdesktop/rdesktop-1.3.1/configure.patch b/rdesktop/rdesktop-1.3.1/configure.patch
index e69de29bb2..7a662a26a6 100644
--- a/rdesktop/rdesktop-1.3.1/configure.patch
+++ b/rdesktop/rdesktop-1.3.1/configure.patch
@@ -0,0 +1,134 @@
+--- rdesktop-1.3.1/configure 2003-12-11 07:59:54.000000000 -0700
++++ rdesktop-1.3.1.new/configure 2004-05-25 11:17:14.661455560 -0700
+@@ -53,6 +53,9 @@
+ --with-libvncserver-config=*)
+ vncserverconfig=$optarg
+ ;;
++ --with-oss)
++ withoss=yes
++ ;;
+ --with-debug)
+ cflags="$cflags -g -DWITH_DEBUG"
+ ;;
+@@ -83,6 +86,7 @@
+ echo " --with-libvncserver make rdp2vnc"
+ echo " --with-libvncserver-config=CMD"
+ echo " use CMD as libvncserver-config"
++ echo " --with-oss Specify that we should use the OSS system"
+ echo " --with-debug enable protocol debugging output"
+ echo " --with-debug-kbd enable debugging of keyboard handling"
+ echo " --with-debug-rdp5 enable debugging of RDP5 code"
+@@ -139,7 +143,9 @@
+
+ # Find X installation
+
+-xdirs="$extraxdir /usr/X11R6 /usr/X11 /usr/openwin /usr /usr/local/X11R6 /usr/local/X11 /usr/local"
++#xdirs="$extraxdir /usr/X11R6 /usr/X11 /usr/openwin /usr /usr/local/X11R6 /usr/local/X11 /usr/local"
++
++xdirs="$extraxdir"
+
+ for dir in $xdirs; do
+ if [ -f $dir/include/X11/Xlib.h ]; then
+@@ -178,7 +184,10 @@
+ #withoutopenssl not currently supported
+ #if [ -z "$withoutopenssl" ]; then
+ # Find OpenSSL installation if available
+- ssldirs="$extrassldir /usr/openssl /usr/ssl /usr /usr/local/openssl /usr/local/ssl /usr/local"
++
++#ssldirs="$extrassldir /usr/openssl /usr/ssl /usr /usr/local/openssl /usr/local/ssl /usr/local"
++
++ssldirs="$extrassldir"
+
+ for dir in $ssldirs; do
+ if [ -f $dir/include/openssl/rc4.h ]; then
+@@ -259,22 +268,27 @@
+
+ # Check for OSS sound support
+
+-if [ -f /usr/include/sys/soundcard.h ]; then
+- echo Sound support enabled: Open Sound System
+- echo
+- echo "SOUNDOBJ = rdpsnd.o rdpsnd_oss.o" >>Makeconf
+- cflags="$cflags -DWITH_RDPSND"
+-elif [ -f /usr/include/sys/audioio.h ]; then
+- echo Sound support enabled: Sun/BSD
+- echo
+- echo "SOUNDOBJ = rdpsnd.o rdpsnd_sun.o" >>Makeconf
+- cflags="$cflags -DWITH_RDPSND"
+-else
+- echo "WARNING: sound support disabled (no /usr/include/sys/soundcard.h or /usr/include/sys/audio.h)"
+- echo "Currently supported systems are Open Sound System and Sun"
+- echo
++if [ -z "$withoss" ]; then
++ if [ -f /usr/include/sys/soundcard.h ]; then
++ withoss=yes
++ elif [ -f /usr/include/sys/audioio.h ]; then
++ echo Sound support enabled: Sun/BSD
++ echo
++ echo "SOUNDOBJ = rdpsnd.o rdpsnd_sun.o" >>Makeconf
++ cflags="$cflags -DWITH_RDPSND"
++ else
++ echo "WARNING: sound support disabled (no /usr/include/sys/soundcard.h or /usr/include/sys/audio.h)"
++ echo "Currently supported systems are Open Sound System and Sun"
++ echo
++ fi
+ fi
+
++if [ ! -z "$withoss" ]; then
++ echo Sound support enabled: Open Sound System
++ echo
++ echo "SOUNDOBJ = rdpsnd.o rdpsnd_oss.o" >>Makeconf
++ cflags="$cflags -DWITH_RDPSND"
++fi
+
+ # Platform-specific options
+
+@@ -294,8 +308,8 @@
+ esac
+
+
+-echo "CFLAGS = $cflags" >>Makeconf
+-echo "LDFLAGS = $ldflags" >>Makeconf
++echo "LCFLAGS = $cflags" >>Makeconf
++echo "LLDFLAGS = $ldflags" >>Makeconf
+ echo "TARGETS = $targets" >>Makeconf
+
+ echo "configure complete - now run make"
+--- rdesktop-1.3.1/Makefile 2004-01-21 20:26:01.000000000 -0700
++++ rdesktop-1.3.1.new/Makefile 2004-05-25 11:16:59.338784960 -0700
+@@ -25,22 +25,22 @@
+ all: $(TARGETS)
+
+ rdesktop: $(X11OBJ) $(SOUNDOBJ) $(RDPOBJ) $(CRYPTOBJ)
+- $(CC) $(CFLAGS) -o rdesktop $(X11OBJ) $(SOUNDOBJ) $(RDPOBJ) $(CRYPTOBJ) $(LDFLAGS) -lX11
++ $(CC) $(CFLAGS) $(LCFLAGS) -o rdesktop $(X11OBJ) $(SOUNDOBJ) $(RDPOBJ) $(CRYPTOBJ) $(LDFLAGS) $(LLDFLAGS) -lX11
+
+ rdp2vnc: $(VNCOBJ) $(SOUNDOBJ) $(RDPOBJ) $(CRYPTOBJ)
+- $(CCLD) $(CFLAGS) -o rdp2vnc $(VNCOBJ) $(SOUNDOBJ) $(RDPOBJ) $(CRYPTOBJ) $(LDFLAGS) $(LDVNC)
++ $(CCLD) $(CFLAGS) $(LCFLAGS) -o rdp2vnc $(VNCOBJ) $(SOUNDOBJ) $(RDPOBJ) $(CRYPTOBJ) $(LDFLAGS) $(LLDFLAGS) $(LDVNC)
+
+ vnc/rdp2vnc.o: rdesktop.c
+- $(CC) $(CFLAGS) $(VNCINC) -DRDP2VNC -o vnc/rdp2vnc.o -c rdesktop.c
++ $(CC) $(CFLAGS) $(LCFLAGS) $(VNCINC) -DRDP2VNC -o vnc/rdp2vnc.o -c rdesktop.c
+
+ vnc/vnc.o: vnc/vnc.c
+- $(CC) $(CFLAGS) $(VNCINC) -DRDP2VNC -o vnc/vnc.o -c vnc/vnc.c
++ $(CC) $(CFLAGS) $(LCFLAGS) $(VNCINC) -DRDP2VNC -o vnc/vnc.o -c vnc/vnc.c
+
+ vnc/xkeymap.o: xkeymap.c
+- $(CC) $(CFLAGS) $(VNCINC) -DRDP2VNC -o vnc/xkeymap.o -c xkeymap.c
++ $(CC) $(CFLAGS) $(LCFLAGS) $(VNCINC) -DRDP2VNC -o vnc/xkeymap.o -c xkeymap.c
+
+ vnc/x11stubs.o: vnc/x11stubs.c
+- $(CC) $(CFLAGS) $(VNCINC) -o vnc/x11stubs.o -c vnc/x11stubs.c
++ $(CC) $(CFLAGS) $(LCFLAGS) $(VNCINC) -o vnc/x11stubs.o -c vnc/x11stubs.c
+
+ Makeconf:
+ ./configure
+@@ -106,5 +106,5 @@
+ .SUFFIXES: .c .o
+
+ .c.o:
+- $(CC) $(CFLAGS) -o $@ -c $<
++ $(CC) $(CFLAGS) $(LCFLAGS) -o $@ -c $<
+
diff --git a/t1lib/t1lib-5.0.2/configure.patch b/t1lib/t1lib-5.0.2/configure.patch
index e69de29bb2..f2405c6bc7 100644
--- a/t1lib/t1lib-5.0.2/configure.patch
+++ b/t1lib/t1lib-5.0.2/configure.patch
@@ -0,0 +1,122 @@
+
+#
+# Patch managed by http://www.holgerschurig.de/patcher.html
+#
+
+--- t1lib-5.0.2/configure.in~configure.patch
++++ t1lib-5.0.2/configure.in
+@@ -75,9 +75,9 @@
+ T1LIB_VERSIONSTRING=\"MACRO_T1LIB_IDENTIFIER\"
+
+
+-dnl We use this file by Andreas Zeller to check for libXaw
+-builtin(include, ac-tools/ice_find_athena.m4)
+-builtin(include, ac-tools/aclocal.m4)
++#dnl We use this file by Andreas Zeller to check for libXaw
++#builtin(include, ac-tools/ice_find_athena.m4)
++#builtin(include, ac-tools/aclocal.m4)
+
+ dnl We want these before the checks, so the checks can modify their values.
+ test -z "$LDLIBS" && LDLIBS=-lm AC_SUBST(LDLIBS)
+@@ -161,6 +161,7 @@
+
+ dnl **** Check for underscore on external symbols ****
+
++AH_TEMPLATE([NEED_UNDERSCORE_PREFIX], [Define if external symbols need an underscore prefix])
+ AC_CACHE_CHECK("whether external symbols need an underscore prefix",
+ ac_cv_c_extern_prefix,
+ [saved_libs=$LIBS
+@@ -178,64 +179,46 @@
+ AC_DEFINE(NEED_UNDERSCORE_PREFIX)
+ fi
+
+-dnl **** Check which ANSI integer type is 16 bit
++dnl **** Check which ANSI integer type is 16 bit
++T1_AA_TYPE16=""
++for type in short int; do
++ if test x"`eval echo \\$ac_cv_sizeof_$type`" = "x2"; then
++ T1_AA_TYPE16="-DT1_AA_TYPE16=$type"
++ break
++ fi
++done
+
+-AC_CACHE_CHECK( "which ANSI integer type is 16 bit", ac_16bit_type,
+- AC_TRY_RUN([
+-int main(void) {
+- if (sizeof(short)==2)
+- return(0);
+- else if (sizeof(int)==2)
+- return(1);
+- else
+- return(2);
+-}], ac_16bit_type="short", ac_16bit_type="int", ac_16bit_type=))
+-if test "$ac_16bit_type" = "short"
+-then
+- T1_AA_TYPE16="-DT1_AA_TYPE16=short"
+-else
+- T1_AA_TYPE16="-DT1_AA_TYPE16=int"
++if test x"$T1_AA_TYPE16" = "x"; then
++ AC_MSG_ERROR("Unable to determine which integer type is 16 bit.")
+ fi
+
+-
+ dnl **** Check which ANSI integer type is 32 bit
++T1_AA_TYPE32=""
++for type in int long; do
++ if test x"`eval echo \\$ac_cv_sizeof_$type`" = "x4"; then
++ T1_AA_TYPE32="-DT1_AA_TYPE32=$type"
++ break
++ fi
++done
+
+-AC_CACHE_CHECK( "which ANSI integer type is 32 bit", ac_32bit_type,
+- AC_TRY_RUN([
+-int main(void) {
+- if (sizeof(int)==4)
+- return(0);
+- else if (sizeof(long)==4)
+- return(1);
+- else
+- return(2);
+-}], ac_32bit_type="int", ac_32bit_type="long", ac_32bit_type=))
+-if test "$ac_32bit_type" = "int"
+-then
+- T1_AA_TYPE32="-DT1_AA_TYPE32=int"
+-else
+- T1_AA_TYPE32="-DT1_AA_TYPE32=long"
++if test x"$T1_AA_TYPE32" = "x"; then
++ AC_MSG_ERROR("Unable to determine which integer type is 32 bit.")
+ fi
+
+-
+ dnl **** Check which ANSI integer type is 64 bit
++T1_AA_TYPE64=""
++for type in long long_long; do
++ if test x"`eval echo \\$ac_cv_sizeof_$type`" = "x8"; then
++ AC_MSG_WARN("$type is 64 bit")
++ T1_AA_TYPE64="-DT1_AA_TYPE64=\"`echo $type|tr '_' ' '`\""
++ break
++ fi
++done
+
+-AC_CACHE_CHECK( "which ANSI integer type is 64 bit", ac_64bit_type,
+- AC_TRY_RUN([
+-int main(void) {
+- if (sizeof(long)==8)
+- return(0);
+- else
+- return(1);
+-}], ac_64bit_type="long", ac_64bit_type="<none>"))
+-if test "$ac_64bit_type" = "long"
+-then
+- T1_AA_TYPE64="-DT1_AA_TYPE64=long"
+-else
+- T1_AA_TYPE64=
++if test x"$T1_AA_TYPE64" = "x"; then
++ AC_MSG_ERROR("Unable to determine which integer type is 64 bit.")
+ fi
+
+-
+ CFLAGS="${CFLAGS} -DT1LIB_IDENT=\"\\\"${T1LIB_IDENTIFIER}\\\"\" -DGLOBAL_CONFIG_DIR=\"\\\"${T1LIB_DATA_DIR}\\\"\""
+
+ dnl **** Check for functions and header files ****
diff --git a/t1lib/t1lib-5.0.2/install.patch b/t1lib/t1lib-5.0.2/install.patch
index e69de29bb2..3d8843861c 100644
--- a/t1lib/t1lib-5.0.2/install.patch
+++ b/t1lib/t1lib-5.0.2/install.patch
@@ -0,0 +1,163 @@
+
+#
+# Patch managed by http://www.mn-logistik.de/unsupported/pxa250/patcher
+#
+
+--- t1lib-5.0.1/Makefile.in~install 2001-01-15 17:37:11.000000000 -0500
++++ t1lib-5.0.1/Makefile.in 2004-02-13 12:29:21.000000000 -0500
+@@ -44,6 +44,7 @@
+ INSTALL_PROGRAM = @INSTALL_PROGRAM@
+ INSTALL_DATA = @INSTALL_DATA@
+ MKINSTALLDIRS = @top_srcdir@/ac-tools/mkinstalldirs
++DESTDIR = @DESTDIR@
+ prefix = @prefix@
+ exec_prefix = @exec_prefix@
+ includedir = @includedir@
+--- t1lib-5.0.1/doc/Makefile.in~install 2001-12-15 14:10:46.000000000 -0500
++++ t1lib-5.0.1/doc/Makefile.in 2004-02-13 12:29:28.000000000 -0500
+@@ -43,6 +43,7 @@
+ INSTALL_PROGRAM = @INSTALL_PROGRAM@
+ INSTALL_DATA = @INSTALL_DATA@
+ MKINSTALLDIRS = @top_srcdir@/ac-tools/mkinstalldirs
++DESTDIR = @DESTDIR@
+ prefix = @prefix@
+ exec_prefix = @exec_prefix@
+ includedir = @includedir@
+@@ -91,18 +92,18 @@
+ install: dummy
+ if (test -f t1lib_doc.ps) \
+ then \
+- $(MKINSTALLDIRS) @T1LIB_DATA_DIR@/doc; \
+- $(INSTALL_DATA) t1lib_doc.ps @T1LIB_DATA_DIR@/doc; \
++ $(MKINSTALLDIRS) $(DESTDIR)@T1LIB_DATA_DIR@/doc; \
++ $(INSTALL_DATA) t1lib_doc.ps $(DESTDIR)@T1LIB_DATA_DIR@/doc; \
+ fi;
+ if (test -f t1lib_doc.pdf) \
+ then \
+- $(MKINSTALLDIRS) @T1LIB_DATA_DIR@/doc; \
+- $(INSTALL_DATA) t1lib_doc.pdf @T1LIB_DATA_DIR@/doc; \
++ $(MKINSTALLDIRS) $(DESTDIR)@T1LIB_DATA_DIR@/doc; \
++ $(INSTALL_DATA) t1lib_doc.pdf $(DESTDIR)@T1LIB_DATA_DIR@/doc; \
+ fi;
+
+
+ uninstall: dummy
+- $(RM) -R @T1LIB_DATA_DIR@/doc
++ $(RM) -R $(DESTDIR)@T1LIB_DATA_DIR@/doc
+
+
+ dummy:
+--- t1lib-5.0.1/examples/Makefile.in~install 2001-01-15 17:11:47.000000000 -0500
++++ t1lib-5.0.1/examples/Makefile.in 2004-02-13 12:29:33.000000000 -0500
+@@ -40,6 +40,7 @@
+ INSTALL_PROGRAM = @INSTALL_PROGRAM@
+ INSTALL_DATA = @INSTALL_DATA@
+ MKINSTALLDIRS = @top_srcdir@/ac-tools/mkinstalldirs
++DESTDIR = @DESTDIR@
+ prefix = @prefix@
+ exec_prefix = @exec_prefix@
+ includedir = @includedir@
+--- t1lib-5.0.1/lib/Makefile.in~install 2002-06-24 22:15:30.000000000 -0400
++++ t1lib-5.0.1/lib/Makefile.in 2004-02-13 12:29:35.000000000 -0500
+@@ -40,6 +40,7 @@
+ INSTALL_PROGRAM = @INSTALL_PROGRAM@
+ INSTALL_DATA = @INSTALL_DATA@
+ MKINSTALLDIRS = @top_srcdir@/ac-tools/mkinstalldirs
++DESTDIR = @DESTDIR@
+ prefix = @prefix@
+ exec_prefix = @exec_prefix@
+ includedir = @includedir@
+@@ -160,27 +161,27 @@
+
+
+ install: dummy
+- $(MKINSTALLDIRS) $(libdir)
+- $(MKINSTALLDIRS) $(includedir)
+- $(MKINSTALLDIRS) @T1LIB_DATA_DIR@
++ $(MKINSTALLDIRS) $(DESTDIR)$(libdir)
++ $(MKINSTALLDIRS) $(DESTDIR)$(includedir)
++ $(MKINSTALLDIRS) $(DESTDIR)@T1LIB_DATA_DIR@
+ $(LIBTOOL) --mode=install \
+- $(INSTALL_DATA) $(MAIN_TARGET) $(libdir)
++ $(INSTALL_DATA) $(MAIN_TARGET) $(DESTDIR)$(libdir)
+ $(LIBTOOL) --mode=install \
+- $(INSTALL_DATA) $(MAIN_HEADER) $(includedir)
+- if (test -f @T1LIB_DATA_DIR@/t1lib.config) \
++ $(INSTALL_DATA) $(MAIN_HEADER) $(DESTDIR)$(includedir)
++ if (test -f $(DESTDIR)@T1LIB_DATA_DIR@/t1lib.config) \
+ then \
+ echo "Alert: Global configuration file exists, installation skipped!"; \
+ else \
+- $(LIBTOOL) --mode=install $(INSTALL_DATA) t1lib.config @T1LIB_DATA_DIR@; \
++ $(LIBTOOL) --mode=install $(INSTALL_DATA) t1lib.config $(DESTDIR)@T1LIB_DATA_DIR@; \
+ fi;
+
+
+ uninstall: dummy
+- -$(LIBTOOL) --mode=uninstall $(RM) $(libdir)/libt1.la
+- -$(LIBTOOL) --mode=uninstall $(RM) $(libdir)/libt1x.la
+- -$(LIBTOOL) --mode=uninstall $(RM) $(includedir)/t1lib.h
+- -$(LIBTOOL) --mode=uninstall $(RM) $(includedir)/t1libx.h
+- -$(LIBTOOL) --mode=uninstall $(RM) @T1LIB_DATA_DIR@/t1lib.config
++ -$(LIBTOOL) --mode=uninstall $(RM) $(DESTDIR)$(libdir)/libt1.la
++ -$(LIBTOOL) --mode=uninstall $(RM) $(DESTDIR)$(libdir)/libt1x.la
++ -$(LIBTOOL) --mode=uninstall $(RM) $(DESTDIR)$(includedir)/t1lib.h
++ -$(LIBTOOL) --mode=uninstall $(RM) $(DESTDIR)$(includedir)/t1libx.h
++ -$(LIBTOOL) --mode=uninstall $(RM) $(DESTDIR)@T1LIB_DATA_DIR@/t1lib.config
+
+
+ dummy:
+--- t1lib-5.0.1/type1afm/Makefile.in~install 2001-01-15 17:08:23.000000000 -0500
++++ t1lib-5.0.1/type1afm/Makefile.in 2004-02-13 12:29:40.000000000 -0500
+@@ -42,6 +42,7 @@
+ INSTALL_PROGRAM = @INSTALL_PROGRAM@
+ INSTALL_DATA = @INSTALL_DATA@
+ MKINSTALLDIRS = @top_srcdir@/ac-tools/mkinstalldirs
++DESTDIR = @DESTDIR@
+ prefix = @prefix@
+ exec_prefix = @exec_prefix@
+ includedir = @includedir@
+@@ -87,14 +88,14 @@
+ -rmdir .libs
+
+ install: dummy
+- $(MKINSTALLDIRS) $(bindir)
++ $(MKINSTALLDIRS) $(DESTDIR)$(bindir)
+ $(LIBTOOL) --mode=install \
+- $(INSTALL_PROGRAM) type1afm $(bindir)/type1afm
++ $(INSTALL_PROGRAM) type1afm $(DESTDIR)$(bindir)/type1afm
+
+
+ uninstall: dummy
+ $(LIBTOOL) --mode=uninstall \
+- $(RM) $(bindir)/type1afm
++ $(RM) $(DESTDIR)$(bindir)/type1afm
+
+
+ dummy:
+--- t1lib-5.0.1/xglyph/Makefile.in~install 2001-01-15 17:06:53.000000000 -0500
++++ t1lib-5.0.1/xglyph/Makefile.in 2004-02-13 12:29:44.000000000 -0500
+@@ -42,6 +42,7 @@
+ INSTALL_PROGRAM = @INSTALL_PROGRAM@
+ INSTALL_DATA = @INSTALL_DATA@
+ MKINSTALLDIRS = @top_srcdir@/ac-tools/mkinstalldirs
++DESTDIR = @DESTDIR@
+ prefix = @prefix@
+ exec_prefix = @exec_prefix@
+ includedir = @includedir@
+@@ -94,12 +95,12 @@
+
+
+ install: dummy
+- $(MKINSTALLDIRS) $(bindir)
+- $(LIBTOOL) --mode=install $(INSTALL_PROGRAM) xglyph $(bindir)/xglyph
++ $(MKINSTALLDIRS) $(DESTDIR)$(bindir)
++ $(LIBTOOL) --mode=install $(INSTALL_PROGRAM) xglyph $(DESTDIR)$(bindir)/xglyph
+
+
+ uninstall: dummy
+- $(LIBTOOL) --mode=uninstall $(RM) -f $(bindir)/xglyph
++ $(LIBTOOL) --mode=uninstall $(RM) -f $(DESTDIR)$(bindir)/xglyph
+
+
+ dummy:
diff --git a/t1lib/t1lib-5.0.2/libtool.patch b/t1lib/t1lib-5.0.2/libtool.patch
index e69de29bb2..e050025739 100644
--- a/t1lib/t1lib-5.0.2/libtool.patch
+++ b/t1lib/t1lib-5.0.2/libtool.patch
@@ -0,0 +1,116 @@
+
+#
+# Patch managed by http://www.holgerschurig.de/patcher.html
+#
+
+--- t1lib-5.0.2/Makefile.in~libtool
++++ t1lib-5.0.2/Makefile.in
+@@ -18,7 +18,8 @@
+ # Last modified: 2001-04-01
+ #
+
+-SHELL = /bin/sh
++SHELL = @SHELL@
++LIBTOOL = @LIBTOOL@
+ CC = @CC@
+ CPP = @CPP@
+ CFLAGS = @CFLAGS@
+@@ -150,7 +151,7 @@
+ $(RM) examples/Makefile
+ $(RM) xglyph/Makefile
+ $(RM) doc/Makefile
+- $(RM) libtool
++ $(RM) $(LIBTOOL)
+
+
+ # The following two targets are for building and cleaning the python-
+--- t1lib-5.0.2/examples/Makefile.in~libtool
++++ t1lib-5.0.2/examples/Makefile.in
+@@ -14,9 +14,10 @@
+ # Last modified: 2001-04-01
+ #
+
++top_builddir = @top_builddir@
+
+-LIBTOOL = ../libtool
+-SHELL = /bin/sh
++LIBTOOL = @LIBTOOL@
++SHELL = @SHELL@
+ CC = @CC@
+ CPP = @CPP@
+ CFLAGS = @CFLAGS@
+--- t1lib-5.0.2/lib/Makefile.in~libtool
++++ t1lib-5.0.2/lib/Makefile.in
+@@ -15,8 +15,10 @@
+ #
+ # Contributions by H.Kakugawa to use libtool!
+
+-LIBTOOL = ../libtool
+-SHELL = /bin/sh
++top_builddir = @top_builddir@
++
++LIBTOOL = @LIBTOOL@
++SHELL = @SHELL@
+ CC = @CC@
+ CPP = @CPP@
+ CFLAGS = @CFLAGS@
+--- t1lib-5.0.2/lib/t1lib/Makefile.in~libtool
++++ t1lib-5.0.2/lib/t1lib/Makefile.in
+@@ -14,8 +14,10 @@
+ #
+ # Modified by H.Kakugawa to use libtool
+
+-LIBTOOL = ../../libtool
+-SHELL = /bin/sh
++top_builddir = @top_builddir@
++
++LIBTOOL = @LIBTOOL@
++SHELL = @SHELL@
+ CC = @CC@
+ CPP = @CPP@
+ CFLAGS = @CFLAGS@
+--- t1lib-5.0.2/lib/type1/Makefile.in~libtool
++++ t1lib-5.0.2/lib/type1/Makefile.in
+@@ -14,8 +14,10 @@
+ #
+ # Modified by H.Kakugawa to use libtool
+
+-LIBTOOL = ../../libtool
+-SHELL = /bin/sh
++top_builddir = @top_builddir@
++
++LIBTOOL = @LIBTOOL@
++SHELL = @SHELL@
+ CC = @CC@
+ CPP = @CPP@
+ CFLAGS = @CFLAGS@
+--- t1lib-5.0.2/type1afm/Makefile.in~libtool
++++ t1lib-5.0.2/type1afm/Makefile.in
+@@ -15,8 +15,10 @@
+ #
+ # Modified by H.Kakugawa to use libtool
+
+-LIBTOOL = ../libtool
+-SHELL = /bin/sh
++top_builddir = @top_builddir@
++
++LIBTOOL = @LIBTOOL@
++SHELL = @SHELL@
+ CC = @CC@
+ CPP = @CPP@
+ CFLAGS = @CFLAGS@
+--- t1lib-5.0.2/xglyph/Makefile.in~libtool
++++ t1lib-5.0.2/xglyph/Makefile.in
+@@ -15,8 +15,10 @@
+ #
+ # Modified by H.Kakugawa to use libtool
+
+-LIBTOOL = ../libtool
+-SHELL = /bin/sh
++top_builddir = @top_builddir@
++
++LIBTOOL = @LIBTOOL@
++SHELL = @SHELL@
+ CC = @CC@
+ CPP = @CPP@
+ CFLAGS = @CFLAGS@
diff --git a/thttpd/files/acinclude.m4 b/thttpd/files/acinclude.m4
index e69de29bb2..56e4bcb89e 100644
--- a/thttpd/files/acinclude.m4
+++ b/thttpd/files/acinclude.m4
@@ -0,0 +1,188 @@
+dnl
+dnl Improved version of AC_CHECK_LIB
+dnl
+dnl Thanks to John Hawkinson (jhawk@mit.edu)
+dnl
+dnl usage:
+dnl
+dnl AC_LBL_CHECK_LIB(LIBRARY, FUNCTION [, ACTION-IF-FOUND [,
+dnl ACTION-IF-NOT-FOUND [, OTHER-LIBRARIES]]])
+dnl
+dnl results:
+dnl
+dnl LIBS
+dnl
+
+define(AC_LBL_CHECK_LIB,
+[AC_MSG_CHECKING([for $2 in -l$1])
+dnl Use a cache variable name containing both the library and function name,
+dnl because the test really is for library $1 defining function $2, not
+dnl just for library $1. Separate tests with the same $1 and different $2's
+dnl may have different results.
+ac_lib_var=`echo $1['_']$2['_']$5 | sed 'y%./+- %__p__%'`
+AC_CACHE_VAL(ac_cv_lbl_lib_$ac_lib_var,
+[ac_save_LIBS="$LIBS"
+LIBS="-l$1 $5 $LIBS"
+AC_TRY_LINK(dnl
+ifelse([$2], [main], , dnl Avoid conflicting decl of main.
+[/* Override any gcc2 internal prototype to avoid an error. */
+]AC_LANG_CASE(c++, [#ifdef __cplusplus
+extern "C"
+#endif
+])dnl
+[/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char $2();
+]),
+ [$2()],
+ eval "ac_cv_lbl_lib_$ac_lib_var=yes",
+ eval "ac_cv_lbl_lib_$ac_lib_var=no")
+LIBS="$ac_save_LIBS"
+])dnl
+if eval "test \"`echo '$ac_cv_lbl_lib_'$ac_lib_var`\" = yes"; then
+ AC_MSG_RESULT(yes)
+ ifelse([$3], ,
+[changequote(, )dnl
+ ac_tr_lib=HAVE_LIB`echo $1 | sed -e 's/[^a-zA-Z0-9_]/_/g' \
+ -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'`
+changequote([, ])dnl
+ AC_DEFINE_UNQUOTED($ac_tr_lib)
+ LIBS="-l$1 $LIBS"
+], [$3])
+else
+ AC_MSG_RESULT(no)
+ifelse([$4], , , [$4
+])dnl
+fi
+])
+
+dnl
+dnl AC_LBL_LIBRARY_NET
+dnl
+dnl This test is for network applications that need socket() and
+dnl gethostbyname() -ish functions. Under Solaris, those applications
+dnl need to link with "-lsocket -lnsl". Under IRIX, they need to link
+dnl with "-lnsl" but should *not* link with "-lsocket" because
+dnl libsocket.a breaks a number of things (for instance:
+dnl gethostbyname() under IRIX 5.2, and snoop sockets under most
+dnl versions of IRIX).
+dnl
+dnl Unfortunately, many application developers are not aware of this,
+dnl and mistakenly write tests that cause -lsocket to be used under
+dnl IRIX. It is also easy to write tests that cause -lnsl to be used
+dnl under operating systems where neither are necessary (or useful),
+dnl such as SunOS 4.1.4, which uses -lnsl for TLI.
+dnl
+dnl This test exists so that every application developer does not test
+dnl this in a different, and subtly broken fashion.
+
+dnl It has been argued that this test should be broken up into two
+dnl seperate tests, one for the resolver libraries, and one for the
+dnl libraries necessary for using Sockets API. Unfortunately, the two
+dnl are carefully intertwined and allowing the autoconf user to use
+dnl them independantly potentially results in unfortunate ordering
+dnl dependancies -- as such, such component macros would have to
+dnl carefully use indirection and be aware if the other components were
+dnl executed. Since other autoconf macros do not go to this trouble,
+dnl and almost no applications use sockets without the resolver, this
+dnl complexity has not been implemented.
+dnl
+dnl The check for libresolv is in case you are attempting to link
+dnl statically and happen to have a libresolv.a lying around (and no
+dnl libnsl.a).
+dnl
+AC_DEFUN([AC_LBL_LIBRARY_NET], [
+ # Most operating systems have gethostbyname() in the default searched
+ # libraries (i.e. libc):
+ AC_CHECK_FUNC(gethostbyname, ,
+ # Some OSes (eg. Solaris) place it in libnsl:
+ AC_LBL_CHECK_LIB(nsl, gethostbyname, ,
+ # Some strange OSes (SINIX) have it in libsocket:
+ AC_LBL_CHECK_LIB(socket, gethostbyname, ,
+ # Unfortunately libsocket sometimes depends on libnsl.
+ # AC_CHECK_LIB's API is essentially broken so the
+ # following ugliness is necessary:
+ AC_LBL_CHECK_LIB(socket, gethostbyname,
+ LIBS="-lsocket -lnsl $LIBS",
+ AC_CHECK_LIB(resolv, gethostbyname),
+ -lnsl))))
+ AC_CHECK_FUNC(socket, , AC_CHECK_LIB(socket, socket, ,
+ AC_LBL_CHECK_LIB(socket, socket, LIBS="-lsocket -lnsl $LIBS", ,
+ -lnsl)))
+ # DLPI needs putmsg under HPUX so test for -lstr while we're at it
+ AC_CHECK_LIB(str, putmsg)
+ ])
+
+dnl
+dnl Checks to see if struct tm has the BSD tm_gmtoff member
+dnl
+dnl usage:
+dnl
+dnl AC_ACME_TM_GMTOFF
+dnl
+dnl results:
+dnl
+dnl HAVE_TM_GMTOFF (defined)
+dnl
+AC_DEFUN([AC_ACME_TM_GMTOFF],
+ [AC_MSG_CHECKING(if struct tm has tm_gmtoff member)
+ AC_CACHE_VAL(ac_cv_acme_tm_has_tm_gmtoff,
+ AC_TRY_COMPILE([
+# include <sys/types.h>
+# include <time.h>],
+ [u_int i = sizeof(((struct tm *)0)->tm_gmtoff)],
+ ac_cv_acme_tm_has_tm_gmtoff=yes,
+ ac_cv_acme_tm_has_tm_gmtoff=no))
+ AC_MSG_RESULT($ac_cv_acme_tm_has_tm_gmtoff)
+ if test $ac_cv_acme_tm_has_tm_gmtoff = yes ; then
+ AC_DEFINE(HAVE_TM_GMTOFF)
+ fi])
+
+dnl
+dnl Checks to see if int64_t exists
+dnl
+dnl usage:
+dnl
+dnl AC_ACME_INT64T
+dnl
+dnl results:
+dnl
+dnl HAVE_INT64T (defined)
+dnl
+AC_DEFUN([AC_ACME_INT64T],
+ [AC_MSG_CHECKING(if int64_t exists)
+ AC_CACHE_VAL(ac_cv_acme_int64_t,
+ AC_TRY_COMPILE([
+# include <sys/types.h>],
+ [int64_t i64],
+ ac_cv_acme_int64_t=yes,
+ ac_cv_acme_int64_t=no))
+ AC_MSG_RESULT($ac_cv_acme_int64_t)
+ if test $ac_cv_acme_int64_t = yes ; then
+ AC_DEFINE(HAVE_INT64T)
+ fi])
+
+dnl
+dnl Checks to see if socklen_t exists
+dnl
+dnl usage:
+dnl
+dnl AC_ACME_SOCKLENT
+dnl
+dnl results:
+dnl
+dnl HAVE_SOCKLENT (defined)
+dnl
+AC_DEFUN([AC_ACME_SOCKLENT],
+ [AC_MSG_CHECKING(if socklen_t exists)
+ AC_CACHE_VAL(ac_cv_acme_socklen_t,
+ AC_TRY_COMPILE([
+# include <sys/types.h>
+# include <sys/socket.h>],
+ [socklen_t slen],
+ ac_cv_acme_socklen_t=yes,
+ ac_cv_acme_socklen_t=no))
+ AC_MSG_RESULT($ac_cv_acme_socklen_t)
+ if test $ac_cv_acme_socklen_t = yes ; then
+ AC_DEFINE(HAVE_SOCKLENT)
+ fi])
diff --git a/thttpd/files/install.patch b/thttpd/files/install.patch
index e69de29bb2..cdf92a982e 100644
--- a/thttpd/files/install.patch
+++ b/thttpd/files/install.patch
@@ -0,0 +1,87 @@
+
+#
+# Patch managed by http://www.holgerschurig.de/patcher.html
+#
+
+--- thttpd-2.25b/Makefile.in~install.patch
++++ thttpd-2.25b/Makefile.in
+@@ -27,6 +27,7 @@
+ # Various configurable paths (remember to edit Makefile.in, not Makefile)
+
+ # Top level hierarchy.
++DESTDIR =
+ prefix = @prefix@
+ exec_prefix = @exec_prefix@
+ # Pathname of directory to install the binary.
+@@ -110,11 +111,11 @@
+
+ installthis:
+ -mkdir -p $(DESTDIR)$(BINDIR)
+- $(INSTALL) -m 555 -o bin -g bin thttpd $(DESTDIR)$(BINDIR)
++ $(INSTALL) -m 755 thttpd $(DESTDIR)$(BINDIR)
+
+ install-man:
+ -mkdir -p $(DESTDIR)$(MANDIR)/man8
+- $(INSTALL) -m 444 -o bin -g bin thttpd.8 $(DESTDIR)$(MANDIR)/man8
++ $(INSTALL) -m 644 thttpd.8 $(DESTDIR)$(MANDIR)/man8
+
+ installsubdirs:
+ for i in $(SUBDIRS) ; do ( \
+--- thttpd-2.25b/extras/Makefile.in~install.patch
++++ thttpd-2.25b/extras/Makefile.in
+@@ -66,18 +66,16 @@
+
+
+ install: all
+- rm -f $(BINDIR)/makeweb $(BINDIR)/htpasswd $(BINDIR)/syslogtocern
+- cp makeweb $(BINDIR)/makeweb
+- chgrp $(WEBGROUP) $(BINDIR)/makeweb
+- chmod 2755 $(BINDIR)/makeweb
+- cp htpasswd $(BINDIR)/htpasswd
+- cp syslogtocern $(BINDIR)/syslogtocern
+- rm -f $(MANDIR)/man1/makeweb.1
+- cp makeweb.1 $(MANDIR)/man1/makeweb.1
+- rm -f $(MANDIR)/man1/htpasswd.1
+- cp htpasswd.1 $(MANDIR)/man1/htpasswd.1
+- rm -f $(MANDIR)/man8/syslogtocern.8
+- cp syslogtocern.8 $(MANDIR)/man8/syslogtocern.8
++ install -d $(DESTDIR)$(BINDIR)
++# install -g $(WEBGROUP) -m 2755 makeweb $(DESTDIR)$(BINDIR)/makeweb
++ install -m 2755 makeweb $(DESTDIR)$(BINDIR)/makeweb
++ install -m 0755 htpasswd $(DESTDIR)$(BINDIR)/htpasswd
++ install -m 0755 syslogtocern $(DESTDIR)$(BINDIR)/syslogtocern
++ install -d $(DESTDIR)$(MANDIR)/man1 \
++ $(DESTDIR)$(MANDIR)/man8
++ install -m 0644 makeweb.1 $(DESTDIR)$(MANDIR)/man1/makeweb.1
++ install -m 0644 htpasswd.1 $(DESTDIR)$(MANDIR)/man1/htpasswd.1
++ install -m 0644 syslogtocern.8 $(DESTDIR)$(MANDIR)/man8/syslogtocern.8
+
+ clean:
+ rm -f $(CLEANFILES)
+--- thttpd-2.25b/cgi-src/Makefile.in~install.patch
++++ thttpd-2.25b/cgi-src/Makefile.in
+@@ -67,17 +67,13 @@
+ cd .. ; $(MAKE) $(MFLAGS) strerror.o
+
+ install: all
+- -mkdir -p $(CGIBINDIR)
+- rm -f $(CGIBINDIR)/redirect
+- cp redirect $(CGIBINDIR)/redirect
+- rm -f $(MANDIR)/man8/redirect.8
+- cp redirect.8 $(MANDIR)/man8/redirect.8
+- rm -f $(CGIBINDIR)/ssi
+- cp ssi $(CGIBINDIR)/ssi
+- rm -f $(MANDIR)/man8/ssi.8
+- cp ssi.8 $(MANDIR)/man8/ssi.8
+- rm -f $(CGIBINDIR)/phf
+- cp phf $(CGIBINDIR)/phf
++ install -d $(DESTDIR)$(CGIBINDIR)
++ install -m 0755 redirect $(DESTDIR)$(CGIBINDIR)/redirect
++ install -d $(DESTDIR)$(MANDIR)/man8
++ install -m 0644 redirect.8 $(DESTDIR)$(MANDIR)/man8/redirect.8
++ install -m 0755 ssi $(DESTDIR)$(CGIBINDIR)/ssi
++ install -m 0644 ssi.8 $(DESTDIR)$(MANDIR)/man8/ssi.8
++ install -m 0755 phf $(DESTDIR)$(CGIBINDIR)/phf
+
+ clean:
+ rm -f $(CLEANFILES)
diff --git a/tslib/tslib/initialize_djs.patch b/tslib/tslib/initialize_djs.patch
index e69de29bb2..bfd8d1cc18 100644
--- a/tslib/tslib/initialize_djs.patch
+++ b/tslib/tslib/initialize_djs.patch
@@ -0,0 +1,20 @@
+Sometimes after ts_open/ts_close/ts_open on the first
+sample(s) dejitter will crash (sorry no backtrace)
+
+memsetting tslib_dejitter so djt->nr is not random anymore
+
+
+#
+# Patch managed by http://www.holgerschurig.de/patcher.html
+#
+
+--- tslib/plugins/dejitter.c~initialize_djs
++++ tslib/plugins/dejitter.c
+@@ -205,6 +205,7 @@
+ if (djt == NULL)
+ return NULL;
+
++ memset(djt, 0, sizeof(struct tslib_dejitter));
+ djt->module.ops = &dejitter_ops;
+
+ djt->delta = 100;
diff --git a/tslib/tslib/omap1610h2/tslib.sh b/tslib/tslib/omap1610h2/tslib.sh
index e69de29bb2..040f4de3d5 100644
--- a/tslib/tslib/omap1610h2/tslib.sh
+++ b/tslib/tslib/omap1610h2/tslib.sh
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+TSLIB_TSDEVICE=/dev/input/event1
+
+export TSLIB_TSDEVICE
diff --git a/unionfs/unionfs-1.0.3/gcc2-hack.patch b/unionfs/unionfs-1.0.3/gcc2-hack.patch
index e69de29bb2..7c1f4dd32e 100644
--- a/unionfs/unionfs-1.0.3/gcc2-hack.patch
+++ b/unionfs/unionfs-1.0.3/gcc2-hack.patch
@@ -0,0 +1,28 @@
+
+#
+# Patch managed by http://www.mn-logistik.de/unsupported/pxa250/patcher
+#
+
+--- unionfs-1.0.3/fist.h~gcc2 2004-11-10 09:38:58.000000000 -0500
++++ unionfs-1.0.3/fist.h 2004-11-14 05:11:47.601022344 -0500
+@@ -212,11 +212,20 @@
+ } while (0)
+
+ /* Call if you encounter a bug. */
++
++#if __GNUC__ < 3
++#define FISTBUG(arg...) \
++do { \
++ printk("<0>FISTBUG " ##arg); \
++ (*((char *)0))=0; \
++} while (0)
++#else
+ #define FISTBUG(fmt, args...) \
+ do { \
+ printk("<0>FISTBUG at %s:%s:%d " fmt, __FILE__, __FUNCTION__, __LINE__, ##args); \
+ (*((char *)0))=0; \
+ } while (0);
++#endif
+
+ /* The if (0 ...) is so that we can make sure that you don't pass this
+ * define a non-pointer. gcc should optimize it away. */
diff --git a/unionfs/unionfs-1.0.3/make.patch b/unionfs/unionfs-1.0.3/make.patch
index e69de29bb2..dc3faf981a 100644
--- a/unionfs/unionfs-1.0.3/make.patch
+++ b/unionfs/unionfs-1.0.3/make.patch
@@ -0,0 +1,124 @@
+
+#
+# Patch managed by http://www.mn-logistik.de/unsupported/pxa250/patcher
+#
+
+--- unionfs-1.0.3/Makefile~make.patch 2004-11-10 09:38:58.000000000 -0500
++++ unionfs-1.0.3/Makefile 2004-11-14 02:41:12.812518968 -0500
+@@ -3,20 +3,32 @@
+ #
+
+ # this should point to where your kernel headers are
+-TOPINC = -I/lib/modules/`uname -r`/build/include
++KERNELVER = $(shell uname -r)
++KERNELINC = -I/lib/modules/$(KERNELVER)/build/include
++
+ # This is where the make install target will put stuff
+-PREFIX = /usr/local
++DESTDIR =
++prefix = /usr/local
++sbindir = $(prefix)/sbin
++mandir = $(prefix)/man
++man4dir = $(mandir)/man4
++man8dir = $(mandir)/man8
++moduledir = /lib/modules/$(KERNELVER)
++
+ VERSION = 1.0.3
+
+-# allow custmom override of TOPINC for fistgen developers
++# allow custmom override of KERNELINC for fistgen developers
+ -include fistdev.mk
+
+-CC = gcc
+-CFLAGS = -D__KERNEL__ -DMODULE -DFISTGEN -I. ${TOPINC} -Wall -Wno-unused-label -Werror -g ${EXTRACFLAGS} -O2
+-UCFLAGS = -DFISTGEN -I. ${TOPINC} -g -O2 -Wall -Wno-unused -Werror ${EXTRAUCFLAGS}
++CC = gcc
++LD = ld
++R_CFLAGS = -D__KERNEL__ -DMODULE -DFISTGEN -I.
++CFLAGS = $(KERNELINC) -Wall -Wno-unused-label -Werror -g $(EXTRACFLAGS) -O2
++R_UCFLAGS = -DFISTGEN -I.
++UCFLAGS = $(KERNELINC) -g -O2 -Wall -Wno-unused -Werror $(EXTRAUCFLAGS)
+
+ O_TARGET := unionfs.o
+-ADD_M_SRC =
++ADD_M_SRC =
+ # vm_area.o is not needed if we use generic_file_mmap
+ # mmap.o and attach.o are optionally added
+ O_OBJS := subr.o dentry.o file.o inode.o main.o super.o print.o \
+@@ -27,16 +39,19 @@
+
+ BINS := unionctl uniondbg
+
+-all: ${O_TARGET} ${BINS}
++all: $(O_TARGET) $(BINS)
+
+-${O_TARGET}: ${O_OBJS}
+- ld -o ${O_TARGET} -r ${O_OBJS}
++$(O_TARGET): $(O_OBJS)
++ $(LD) -o $(O_TARGET) -r $(O_OBJS)
++
++%.o: %.c
++ $(CC) -c -o $@ $< $(R_CFLAGS) $(CFLAGS)
+
+ unionctl: unionctl.c
+- ${CC} -o $@ $? ${UCFLAGS}
++ $(CC) -o $@ $? $(R_UCFLAGS) $(UCFLAGS)
+
+ uniondbg: uniondbg.c
+- ${CC} -o $@ $? ${UCFLAGS}
++ $(CC) -o $@ $? $(R_UCFLAGS) $(UCFLAGS)
+
+ memtest:
+ perl match-malloc.pl log.txt
+@@ -48,29 +63,30 @@
+ ctags *.[ch]
+
+ clean:
+- rm -f ${O_TARGET} ${O_OBJS} *.d .*.flags \#* *~
+- rm -f ${BINS} TAGS tags
+- rm -f unionfs-${VERSION}.tar.gz
++ rm -f $(O_TARGET) $(O_OBJS) *.d .*.flags \#* *~
++ rm -f $(BINS) TAGS tags
++ rm -f unionfs-$(VERSION).tar.gz
+
+-${O_OBJS}: unionfs.h fist.h
++$(O_OBJS): unionfs.h fist.h
+
+ install: all
+- mkdir -p ${PREFIX}/sbin
+- cp unionctl ${PREFIX}/sbin
+- cp uniondbg ${PREFIX}/sbin
+- mkdir -p ${PREFIX}/man/man4
+- mkdir -p ${PREFIX}/man/man8
+- cp man/unionfs.4 ${PREFIX}/man/man4
+- cp man/unionctl.8 ${PREFIX}/man/man8
+- cp man/uniondbg.8 ${PREFIX}/man/man8
+- cp unionfs.o /lib/modules/`uname -r`/kernel/fs
++ mkdir -p $(DESTDIR)$(sbindir)
++ cp unionctl $(DESTDIR)$(sbindir)
++ cp uniondbg $(DESTDIR)$(sbindir)
++ mkdir -p $(DESTDIR)$(man4dir)
++ mkdir -p $(DESTDIR)$(man8dir)
++ cp man/unionfs.4 $(DESTDIR)$(man4dir)
++ cp man/unionctl.8 $(DESTDIR)$(man8dir)
++ cp man/uniondbg.8 $(DESTDIR)$(man8dir)
++ mkdir -p $(DESTDIR)$(moduledir)/kernel/fs
++ cp unionfs.o $(DESTDIR)$(moduledir)/kernel/fs
+
+-release: unionfs-${VERSION}.tar.gz
++release: unionfs-$(VERSION).tar.gz
+
+-unionfs-${VERSION}.tar.gz:
+- mkdir unionfs-${VERSION}
+- cp COPYING README NEWS doit.sh undoit.sh *.pl Makefile *.[ch] misc/snapmerge unionfs-${VERSION}
+- mkdir unionfs-${VERSION}/man
+- cp man/*.[0-9] unionfs-${VERSION}/man
+- tar czvf unionfs-${VERSION}.tar.gz unionfs-${VERSION}
+- rm -rf unionfs-${VERSION}
++unionfs-$(VERSION).tar.gz:
++ mkdir unionfs-$(VERSION)
++ cp COPYING README NEWS doit.sh undoit.sh *.pl Makefile *.[ch] misc/snapmerge unionfs-$(VERSION)
++ mkdir unionfs-$(VERSION)/man
++ cp man/*.[0-9] unionfs-$(VERSION)/man
++ tar czvf unionfs-$(VERSION).tar.gz unionfs-$(VERSION)
++ rm -rf unionfs-$(VERSION)
diff --git a/usbutils/usbutils-0.11+cvs20041108/usbutils-0.11+cvs20041108.patch b/usbutils/usbutils-0.11+cvs20041108/usbutils-0.11+cvs20041108.patch
index e69de29bb2..1898eaef4b 100644
--- a/usbutils/usbutils-0.11+cvs20041108/usbutils-0.11+cvs20041108.patch
+++ b/usbutils/usbutils-0.11+cvs20041108/usbutils-0.11+cvs20041108.patch
@@ -0,0 +1,7817 @@
+
+#
+# Patch managed by http://www.holgerschurig.de/patcher.html
+#
+
+--- usbutils-0.11/ChangeLog~usbutils-0.11+cvs20041108
++++ usbutils-0.11/ChangeLog
+@@ -1,3 +1,30 @@
++2004-10-20 David Brownell <dbrownell@users.sourceforge.net>
++ * lsusb.c: minor formatting updates; add a warning when those HID
++ descriptors aren't available.
++
++2004-10-20 Aurelien Jarno <aurelien@aurel32.net>
++ * lsusb.c: bugfixes for reading HID descriptors
++
++2004-10-15 David Brownell <dbrownell@users.sourceforge.net>
++ * lsusb.c: USB 2.0 updates for dual-speed and OTG devices, hubs.
++ Display all descriptors in the right sequence, and dump ones
++ we don't (yet) recognize. Minor cleanups.
++ * usb.ids: update to current version
++
++2004-02-20 Thomas Sailer <sailer@eldrich.ee.ethz.ch>
++ Move to CVS at linux-usb.sf.net
++ Label as version 0.12.
++
++2003-12-06 Aurelien Jarno <aurelien@aurel32.net>
++ Update Debian with libusb based version.
++
++2003-08-31 David Brownell <dbrownell@users.sourceforge.net>
++ * lsusb.c: (against 0.11) recognize CDC descriptors, USB 2.0 updates,
++ string handling updates,
++
++2003-??-?? Aurelien Jarno <aurelien@aurel32.net>
++ Convert to latest libusb, supporting BSD and Darwin.
++
+ 2000-11-06 Thomas Sailer <sailer@eldrich.ee.ethz.ch>
+
+ * names.c, lsusb.c: Fixup of Gunther Mayer's patch; do not export
+--- usbutils-0.11/Makefile.am~usbutils-0.11+cvs20041108
++++ usbutils-0.11/Makefile.am
+@@ -1,19 +1,20 @@
+-SUBDIRS = libusb
++SUBDIRS =
+
+ INCLUDES =
+
+ sbin_PROGRAMS = lsusb usbmodules
++sbin_SCRIPTS = update-usbids
+
+-noinst_HEADERS = names.h usb.h usbdevice_fs.h devtree.h list.h usbmodules.h
++noinst_HEADERS = names.h usbdevice_fs.h devtree.h list.h usbmodules.h usbmisc.h
+
+-lsusb_SOURCES = lsusb.c names.c devtree.c
++lsusb_SOURCES = lsusb.c names.c devtree.c usbmisc.c
+ lsusb_LDADD = @LIBOBJS@
+
+-usbmodules_SOURCES = usbmodules.c
++usbmodules_SOURCES = usbmodules.c usbmisc.c
+ usbmodules_LDADD = @LIBOBJS@
+
+ data_DATA = usb.ids
+
+-man_MANS = lsusb.8 usbmodules.8
++man_MANS = lsusb.8 usbmodules.8 update-usbids.8
+
+ EXTRA_DIST = $(man_MANS) $(data_DATA) usbutils.spec getopt.h getopt.c getopt1.c
+--- usbutils-0.11/NEWS~usbutils-0.11+cvs20041108
++++ usbutils-0.11/NEWS
+@@ -1 +1,2 @@
+-Created :)
++Thanks to a patch from Aurelien Jarno, usbutils now uses libusb to
++access USB devices
+--- usbutils-0.11/configure.in~usbutils-0.11+cvs20041108
++++ usbutils-0.11/configure.in
+@@ -1,40 +1,75 @@
+-AC_INIT(lsusb.c)
+-AC_CANONICAL_SYSTEM
++dnl Process this file with autoconf to produce a configure script.
+
+-AM_INIT_AUTOMAKE(usbutils, 0.11)
++# Initialization
++AC_INIT(Makefile.am)
+ AM_CONFIG_HEADER(config.h)
+
+-dnl AC_CHECK_TOOL()
++AM_INIT_AUTOMAKE(usbutils, 0.12)
+
+-AC_PROG_MAKE_SET
+-AC_ISC_POSIX
++# determine the system type
++AC_CANONICAL_HOST
++
++# build time sanity check...
++AM_SANITY_CHECK
++
++# checks for programs
+ AC_PROG_CC
+-AM_PROG_CC_STDC
+-dnl AC_PROG_RANLIB
++AC_PROG_RANLIB
++AC_PROG_LN_S
++AC_PROG_INSTALL
++
++# checks for header files
++AC_HEADER_STDC
++AC_CHECK_HEADERS(getopt.h sys/ioctl.h syslog.h errno.h)
++
++# checks for typedefs, structures, and compiler characteristics
+ AC_C_CONST
+ AC_C_INLINE
+-AC_HEADER_STDC
++AC_STRUCT_TM
+
+-AC_CHECK_PROG(RANLIB, ranlib, ranlib, :)
+-AC_CHECK_PROG(DLLTOOL, dlltool, dlltool, dlltool)
+-AC_CHECK_PROG(AS, as, as, as, as)
+-AC_CHECK_PROG(AR, ar, ar, ar, ar)
++# checks for library functions
++AC_CHECK_FUNCS([getopt_long],,[AC_LIBOBJ([getopt]) AC_LIBOBJ([getopt1])])
+
+-AC_CHECK_HEADERS(getopt.h sys/ioctl.h syslog.h errno.h linux/usb.h linux/usbdevice_fs.h)
+-AC_CHECK_FUNCS(getopt_long,,LIBOBJS="$LIBOBJS getopt.o getopt1.o")
++dnl checks for libusb-config
++AC_CHECK_PROG(LIBUSB_CONFIG, libusb-config, yes, no)
++if test "$LIBUSB_CONFIG" = "yes"; then
++ LIBUSB_LDFLAGS=`libusb-config --libs`
++ LIBUSB_CFLAGS=`libusb-config --cflags`
++ LIBUSB_VERSION=`libusb-config --version`
++else
++ AC_MSG_ERROR([libusb not found!])
++fi
++
+
+ dnl set USBIDS_FILE in config.h.
+ if test "x${datadir}" = 'x${prefix}/share'; then
+ if test "x${prefix}" = "xNONE"; then
+- AC_DEFINE_UNQUOTED(USBIDS_FILE, "${ac_default_prefix}/share/usb.ids")
++ AC_DEFINE_UNQUOTED(USBIDS_FILE, "${ac_default_prefix}/share/usb.ids", [location of usb.ids])
+ else
+- AC_DEFINE_UNQUOTED(USBIDS_FILE, "${prefix}/share/usb.ids")
++ AC_DEFINE_UNQUOTED(USBIDS_FILE, "${prefix}/share/usb.ids", [location of usb.ids])
+ fi
+ else
+- AC_DEFINE_UNQUOTED(USBIDS_FILE, "${datadir}/usb.ids")
++ AC_DEFINE_UNQUOTED(USBIDS_FILE, "${datadir}/usb.ids", [location of usb.ids])
+ fi
+
+-AC_CONFIG_SUBDIRS([libusb])
++# some extra flags
++CFLAGS="$CFLAGS -Wall $LIBUSB_CFLAGS"
++LDFLAGS="$LDFLAGS $LIBUSB_LDFLAGS"
++
++# pass flags
++AC_SUBST(CFLAGS)
++AC_SUBST(LDFLAGS)
+
+-AC_SUBST(LIBOBJS)
+ AC_OUTPUT([Makefile])
++
++AC_MSG_RESULT([
++
++usbutils-$VERSION is now configured for $canonical_host_type
++
++ Build: $build
++ Host: $host
++ Source directory: $srcdir
++ Installation prefix: $prefix
++ C compiler: $CC $CFLAGS
++
++])
+--- usbutils-0.11/lsusb.8~usbutils-0.11+cvs20041108
++++ usbutils-0.11/lsusb.8
+@@ -1,4 +1,4 @@
+-.TH lsusb 8 "14 September 1999" "usbutils-0.9" "Linux USB Utilities"
++.TH lsusb 8 "19 November 2003" "usbutils-0.11" "Linux USB Utilities"
+ .IX lsusb
+ .SH NAME
+ lsusb \- list all USB devices
+@@ -29,7 +29,7 @@
+ Tells
+ .I lsusb
+ to be very verbose and display even more information (actually everything the
+-PCI device is able to tell).
++USB device is able to tell).
+ You must be root for this to work.
+ .TP
+ \fB\-s\fP [[\fIbus\fP]\fB:\fP][\fIdevnum\fP]
+@@ -39,13 +39,9 @@
+ .I devnum.
+ Both ID's are given in hexadecimal and may be omitted.
+ .TP
+-\fB\-d\fP \fIvendor\fP\fB:\fP[\fIproduct\fP]
++\fB\-d\fP [\fIvendor\fP]\fB:\fP[\fIproduct\fP]
+ Show only devices with the specified vendor and product ID.
+-Both ID's are given in hexadecimal;
+-the product ID may be omitted, but the vendor ID must be given.
+-.TP
+-.B \-p \fIprocpath\fP
+-Use another path instead of /proc/bus/usb.
++Both ID's are given in hexadecimal.
+ .TP
+ .B \-D \fIdevice\fP
+ Do not scan the /proc/bus/usb directory,
+@@ -80,7 +76,7 @@
+
+ .SH FILES
+ .TP
+-.B /usr/share/usb.ids
++.B /usr/share/misc/usb.ids
+ A list of all known USB ID's (vendors, products, classes, subclasses and protocols).
+ .TP
+ .B /proc/bus/usb
+--- usbutils-0.11/lsusb.c~usbutils-0.11+cvs20041108
++++ usbutils-0.11/lsusb.c
+@@ -3,7 +3,8 @@
+ /*
+ * lsusb.c -- lspci like utility for the USB bus
+ *
+- * Copyright (C) 1999, 2000, 2001 Thomas Sailer (sailer@ife.ee.ethz.ch)
++ * Copyright (C) 1999, 2000, 2001, 2003
++ * Thomas Sailer (t.sailer@alumni.ethz.ch)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -24,57 +25,65 @@
+
+ /*****************************************************************************/
+
++#ifdef HAVE_CONFIG_H
++#include "config.h"
++#endif
++
+ #include <sys/types.h>
+ #include <sys/stat.h>
+-#include <sys/ioctl.h>
+ #include <fcntl.h>
+-#include <dirent.h>
+ #include <string.h>
+ #include <errno.h>
+-#include <stdlib.h>
+-#include <unistd.h>
+ #include <stdio.h>
+ #include <stdarg.h>
+-
+-#include <linux/types.h>
+-#ifdef HAVE_LINUX_USBDEVICE_FS_H
+-#include <linux/usbdevice_fs.h>
+-#else
+-#include "usbdevice_fs.h"
+-#endif
+-#ifdef HAVE_LINUX_USB_H
+-#include <linux/usb.h>
+-#else
+-#include "usb.h"
+-#endif
+-
+-#ifdef HAVE_CONFIG_H
+-#include "config.h"
+-#endif
++#include <usb.h>
+
+ #include "names.h"
+ #include "devtree.h"
++#include "usbmisc.h"
+
+ #define _GNU_SOURCE
+ #include <getopt.h>
+
+-#define CTRL_RETRIES 50
+-#define CTRL_TIMEOUT 100 /* milliseconds */
+
+-#define USB_DT_CS_DEVICE 0x21
+-#define USB_DT_CS_CONFIG 0x22
+-#define USB_DT_CS_STRING 0x23
++/* from USB 2.0 spec and updates */
++#define USB_DT_DEVICE_QUALIFIER 0x06
++#define USB_DT_OTHER_SPEED_CONFIG 0x07
++#define USB_DT_OTG 0x09
++#define USB_DT_DEBUG 0x0a
++#define USB_DT_INTERFACE_ASSOCIATION 0x0b
++
++/* convention suggested by common class spec */
+ #define USB_DT_CS_INTERFACE 0x24
+ #define USB_DT_CS_ENDPOINT 0x25
+
+ #define VERBLEVEL_DEFAULT 0 /* 0 gives lspci behaviour; 1, lsusb-0.9 */
+
++#define CTRL_RETRIES 2
++#define CTRL_TIMEOUT (5*1000) /* milliseconds */
++
+ static const char *procbususb = "/proc/bus/usb";
+ static unsigned int verblevel = VERBLEVEL_DEFAULT;
+ static int do_report_desc = 1;
+-static int open_mode = O_RDONLY;
+
+-static void dump_junk2(unsigned char *, unsigned int);
++static void dump_interface(struct usb_dev_handle *dev, struct usb_interface *interface);
++static void dump_endpoint(struct usb_dev_handle *dev, struct usb_interface_descriptor *interface, struct usb_endpoint_descriptor *endpoint);
++static void dump_audiocontrol_interface(struct usb_dev_handle *dev, unsigned char *buf);
++static void dump_audiostreaming_interface(struct usb_dev_handle *dev, unsigned char *buf);
++static void dump_midistreaming_interface(struct usb_dev_handle *dev, unsigned char *buf);
++static char *dump_comm_descriptor(struct usb_dev_handle *dev, unsigned char *buf, char *indent);
++static void dump_hid_device(struct usb_dev_handle *dev, struct usb_interface_descriptor *interface, unsigned char *buf);
++static void dump_audiostreaming_endpoint(struct usb_dev_handle *dev, unsigned char *buf);
++static void dump_midistreaming_endpoint(struct usb_dev_handle *dev, unsigned char *buf);
++static void dump_hub(char *prefix, unsigned char *p, int has_tt);
++static void dump_ccid_device(unsigned char *buf);
++
++/* ---------------------------------------------------------------------- */
++
++static unsigned int convert_le_u32 (const unsigned char *buf)
++{
++ return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
++}
+
+ /* ---------------------------------------------------------------------- */
+
+@@ -95,66 +104,24 @@
+
+ /* ---------------------------------------------------------------------- */
+
+-static int usb_control_msg(int fd, u_int8_t requesttype, u_int8_t request, u_int16_t value,
+- u_int16_t index, unsigned int size, void *data)
+-{
+- struct usbdevfs_ctrltransfer ctrl;
+- int result, try;
+-
+- ctrl.requesttype = requesttype;
+- ctrl.request = request;
+- ctrl.value = value;
+- ctrl.index = index;
+- ctrl.length = size;
+- ctrl.timeout = 1000;
+- ctrl.data = data;
+- ctrl.timeout = CTRL_TIMEOUT;
+- try = 0;
+- do {
+- result = ioctl(fd, USBDEVFS_CONTROL, &ctrl);
+- try++;
+- } while (try < CTRL_RETRIES && result == -1 && errno == ETIMEDOUT);
+- return result;
+-}
+-
+-/* ---------------------------------------------------------------------- */
+-
+-static int get_string(int fd, char *buf, size_t size, u_int8_t id, u_int16_t lang)
++static int get_string(struct usb_dev_handle *dev, char* buf, size_t size, u_int8_t id)
+ {
+- unsigned char b[256];
+- wchar_t w[128];
+- unsigned int i;
+ int ret;
+-
+- if (size < 1)
+- return 0;
+- *buf = 0;
+- /* string ID 0 means no string */
+- if (!id || fd == -1)
+- return 0;
+-
+- b[0] = b[1] = 0xbf;
+- ret = usb_control_msg(fd, USB_DIR_IN, USB_REQ_GET_DESCRIPTOR, (USB_DT_STRING << 8) | id, 0, sizeof(b), b);
+- if (ret < 0) {
+- if (open_mode == O_RDWR)
+- fprintf(stderr, "cannot get string descriptor %d, error = %s(%d)\n", id, strerror(errno), errno);
+- return 0;
+- }
+- if (ret < 2 || b[0] < 2 || b[1] != USB_DT_STRING) {
+- fprintf(stderr, "string descriptor %d invalid (%02x %02x; len=%d)\n", id, b[0], b[1], ret);
++
++ if (id) {
++ ret = usb_get_string_simple(dev, id, buf, size);
++ if (ret <= 0) {
++ buf[0] = 0;
++ return 0;
++ }
++ else
++ return ret;
++
++ }
++ else {
++ buf[0] = 0;
+ return 0;
+ }
+-#if 0
+- for (i = 0; i < ((b[0] - 2) / 2); i++)
+- w[i] = b[2+2*i] | (b[3+2*i] << 8);
+- w[i] = 0;
+- return wcstombs(buf, w, size);
+-#else
+- for (i = 0; i < ((b[0] - 2) / 2); i++)
+- buf[i] = b[2+2*i];
+- buf[i] = 0;
+- return i;
+-#endif
+ }
+
+ static int get_vendor_string(char *buf, size_t size, u_int16_t vid)
+@@ -231,7 +198,7 @@
+
+ /* ---------------------------------------------------------------------- */
+
+-static void dump_junk2(unsigned char *buf, unsigned int len)
++static void dump_bytes(unsigned char *buf, unsigned int len)
+ {
+ unsigned int i;
+
+@@ -256,27 +223,20 @@
+ * General config descriptor dump
+ */
+
+-static void dump_device(int fd, unsigned char *buf, u_int16_t lang)
++static void dump_device(struct usb_dev_handle *dev, struct usb_device_descriptor *descriptor, unsigned int flags)
+ {
+- unsigned int vid, pid;
+ char vendor[128], product[128];
+ char cls[128], subcls[128], proto[128];
+ char mfg[128], prod[128], serial[128];
+
+- if (buf[1] != USB_DT_DEVICE)
+- printf(" Warning: Invalid descriptor\n");
+- else if (buf[0] < 18)
+- printf(" Warning: Descriptor too short\n");
+- vid = buf[8] | (buf[9] << 8);
+- pid = buf[10] | (buf[11] << 8);
+- get_vendor_string(vendor, sizeof(vendor), vid);
+- get_product_string(product, sizeof(product), vid, pid);
+- get_class_string(cls, sizeof(cls), buf[4]);
+- get_subclass_string(subcls, sizeof(subcls), buf[4], buf[5]);
+- get_protocol_string(proto, sizeof(proto), buf[4], buf[5], buf[6]);
+- get_string(fd, mfg, sizeof(mfg), buf[14], lang);
+- get_string(fd, prod, sizeof(prod), buf[15], lang);
+- get_string(fd, serial, sizeof(serial), buf[16], lang);
++ get_vendor_string(vendor, sizeof(vendor), descriptor->idVendor);
++ get_product_string(product, sizeof(product), descriptor->idVendor, descriptor->idProduct);
++ get_class_string(cls, sizeof(cls), descriptor->bDeviceClass);
++ get_subclass_string(subcls, sizeof(subcls), descriptor->bDeviceClass, descriptor->bDeviceSubClass);
++ get_protocol_string(proto, sizeof(proto), descriptor->bDeviceClass, descriptor->bDeviceSubClass, descriptor->bDeviceProtocol);
++ get_string(dev, mfg, sizeof(mfg), descriptor->iManufacturer);
++ get_string(dev, prod, sizeof(prod), descriptor->iProduct);
++ get_string(dev, serial, sizeof(serial), descriptor->iSerialNumber);
+ printf("Device Descriptor:\n"
+ " bLength %5u\n"
+ " bDescriptorType %5u\n"
+@@ -292,47 +252,76 @@
+ " iProduct %5u %s\n"
+ " iSerial %5u %s\n"
+ " bNumConfigurations %5u\n",
+- buf[0], buf[1], buf[3], buf[2], buf[4], cls, buf[5], subcls, buf[6], proto, buf[7],
+- vid, vendor, pid, product, buf[13], buf[12], buf[14], mfg, buf[15], prod, buf[16], serial, buf[17]);
+- dump_junk(buf, " ", 18);
++ descriptor->bLength, descriptor->bDescriptorType, descriptor->bcdUSB >> 8, descriptor->bcdUSB & 0xff,
++ descriptor->bDeviceClass, cls, descriptor->bDeviceSubClass, subcls, descriptor->bDeviceProtocol, proto,
++ descriptor->bMaxPacketSize0, descriptor->idVendor, vendor, descriptor->idProduct, product,
++ descriptor->bcdDevice >> 8, descriptor->bcdDevice & 0xff, descriptor->iManufacturer, mfg,
++ descriptor->iProduct, prod, descriptor->iSerialNumber, serial, descriptor->bNumConfigurations);
+ }
+
+-static void dump_config(int fd, unsigned char *buf)
++static void dump_config(struct usb_dev_handle *dev, struct usb_config_descriptor *config)
+ {
+- if (buf[1] != USB_DT_CONFIG)
+- printf(" Warning: Invalid descriptor\n");
+- else if (buf[0] < 9)
+- printf(" Warning: Descriptor too short\n");
++ char cfg[128];
++ int i;
++
++ get_string(dev, cfg, sizeof(cfg), config->iConfiguration);
+ printf(" Configuration Descriptor:\n"
+ " bLength %5u\n"
+ " bDescriptorType %5u\n"
+ " wTotalLength %5u\n"
+ " bNumInterfaces %5u\n"
+ " bConfigurationValue %5u\n"
+- " iConfiguration %5u\n"
++ " iConfiguration %5u %s\n"
+ " bmAttributes 0x%02x\n",
+- buf[0], buf[1], buf[2] | (buf[3] << 8), buf[4], buf[5], buf[6], buf[7]);
+- if (buf[7] & 0x40)
++ config->bLength, config->bDescriptorType, config->wTotalLength,
++ config->bNumInterfaces, config->bConfigurationValue, config->iConfiguration,
++ cfg, config->bmAttributes);
++ if (config->bmAttributes & 0x40)
+ printf(" Self Powered\n");
+- if (buf[7] & 0x20)
++ if (config->bmAttributes & 0x20)
+ printf(" Remote Wakeup\n");
+- printf(" MaxPower %5umA\n", buf[8] * 2);
+- dump_junk(buf, " ", 9);
++ printf(" MaxPower %5umA\n", config->MaxPower * 2);
++
++ /* avoid re-ordering or hiding descriptors for display */
++ if (config->extralen) {
++ int size = config->extralen;
++ unsigned char *buf = config->extra;
++
++ while (size >= 2) {
++ if (buf[0] < 2) {
++ dump_junk(" ", buf, size);
++ break;
++ }
++ switch (buf[1]) {
++ case USB_DT_OTG:
++ /* handled separately */
++ break;
++ default:
++ /* often a misplaced class descriptor */
++ printf(" UNRECOGNIZED: ");
++ dump_bytes(buf, size);
++ break;
++ }
++ size -= buf[0];
++ buf += buf[0];
++ }
++ }
++ for (i = 0 ; i < config->bNumInterfaces ; i++)
++ dump_interface(dev, &config->interface[i]);
+ }
+
+-static void dump_interface(int fd, unsigned char *buf, u_int16_t lang)
++static void dump_altsetting(struct usb_dev_handle *dev, struct usb_interface_descriptor *interface)
+ {
+ char cls[128], subcls[128], proto[128];
+ char ifstr[128];
++
++ char *buf;
++ int size, i;
+
+- if (buf[1] != USB_DT_INTERFACE)
+- printf(" Warning: Invalid descriptor\n");
+- else if (buf[0] < 9)
+- printf(" Warning: Descriptor too short\n");
+- get_class_string(cls, sizeof(cls), buf[5]);
+- get_subclass_string(subcls, sizeof(subcls), buf[5], buf[6]);
+- get_protocol_string(proto, sizeof(proto), buf[5], buf[6], buf[7]);
+- get_string(fd, ifstr, sizeof(ifstr), buf[8], lang);
++ get_class_string(cls, sizeof(cls), interface->bInterfaceClass);
++ get_subclass_string(subcls, sizeof(subcls), interface->bInterfaceClass, interface->bInterfaceSubClass);
++ get_protocol_string(proto, sizeof(proto), interface->bInterfaceClass, interface->bInterfaceSubClass, interface->bInterfaceProtocol);
++ get_string(dev, ifstr, sizeof(ifstr), interface->iInterface);
+ printf(" Interface Descriptor:\n"
+ " bLength %5u\n"
+ " bDescriptorType %5u\n"
+@@ -343,20 +332,84 @@
+ " bInterfaceSubClass %5u %s\n"
+ " bInterfaceProtocol %5u %s\n"
+ " iInterface %5u %s\n",
+- buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], cls,
+- buf[6], subcls, buf[7], proto, buf[8], ifstr);
+- dump_junk(buf, " ", 9);
++ interface->bLength, interface->bDescriptorType, interface->bInterfaceNumber,
++ interface->bAlternateSetting, interface->bNumEndpoints, interface->bInterfaceClass, cls,
++ interface->bInterfaceSubClass, subcls, interface->bInterfaceProtocol, proto,
++ interface->iInterface, ifstr);
++
++ /* avoid re-ordering or hiding descriptors for display */
++ if (interface->extralen)
++ {
++ size = interface->extralen;
++ buf = interface->extra;
++ while (size >= 2 * sizeof(u_int8_t))
++ {
++ if (buf[0] < 2) {
++ dump_junk(" ", buf, size);
++ break;
++ }
++ switch (buf[1]) {
++ case USB_DT_CS_INTERFACE:
++ switch(interface->bInterfaceClass) {
++ case USB_CLASS_AUDIO:
++ switch(interface->bInterfaceSubClass) {
++ case 1:
++ dump_audiocontrol_interface(dev, buf);
++ break;
++ case 2:
++ dump_audiostreaming_interface(dev, buf);
++ break;
++ case 3:
++ dump_midistreaming_interface(dev, buf);
++ break;
++ }
++ break;
++ case USB_CLASS_COMM:
++ dump_comm_descriptor(dev, buf,
++ " ");
++ break;
++ }
++ break;
++ case USB_DT_HID:
++ if (interface->bInterfaceClass == USB_CLASS_HID)
++ dump_hid_device(dev, interface, buf);
++ if (interface->bInterfaceClass == 0x0b)
++ dump_ccid_device(buf);
++ break;
++ case USB_DT_OTG:
++ /* handled separately */
++ break;
++ default:
++ /* often a misplaced class descriptor */
++ printf(" UNRECOGNIZED: ");
++ dump_bytes(buf, size);
++ break;
++ }
++ size -= buf[0];
++ buf += buf[0];
++ }
++ }
++ for (i = 0 ; i < interface->bNumEndpoints ; i++)
++ dump_endpoint(dev, interface, &interface->endpoint[i]);
+ }
+
+-static void dump_endpoint(int fd, unsigned char *buf)
++static void dump_interface(struct usb_dev_handle *dev, struct usb_interface *interface)
++{
++ int i;
++
++ for (i = 0; i < interface->num_altsetting; i++)
++ dump_altsetting(dev, &interface->altsetting[i]);
++}
++
++static void dump_endpoint(struct usb_dev_handle *dev, struct usb_interface_descriptor *interface, struct usb_endpoint_descriptor *endpoint)
+ {
+ static const char *typeattr[] = { "Control", "Isochronous", "Bulk", "Interrupt" };
+- static const char *syncattr[] = { "none", "Asynchronous", "Adaptive", "Synchronous" };
++ static const char *syncattr[] = { "None", "Asynchronous", "Adaptive", "Synchronous" };
++ static const char *usage[] = { "Data", "Feedback", "Implicit feedback Data", "(reserved)" };
++ static const char *hb[] = { "1x", "2x", "3x", "(?\?)" };
++ char *buf;
++ int size;
+
+- if (buf[1] != USB_DT_ENDPOINT)
+- printf(" Warning: Invalid descriptor\n");
+- else if (buf[0] < 7)
+- printf(" Warning: Descriptor too short\n");
+ printf(" Endpoint Descriptor:\n"
+ " bLength %5u\n"
+ " bDescriptorType %5u\n"
+@@ -364,19 +417,53 @@
+ " bmAttributes %5u\n"
+ " Transfer Type %s\n"
+ " Synch Type %s\n"
+- " wMaxPacketSize %5u\n"
++ " Usage Type %s\n"
++ " wMaxPacketSize 0x%04x %s %d bytes\n"
+ " bInterval %5u\n",
+- buf[0], buf[1], buf[2], buf[2] & 15, (buf[2] & 0x80) ? "IN" : "OUT",
+- buf[3], typeattr[buf[3] & 3], syncattr[(buf[3] >> 2) & 3],
+- buf[4] | (buf[5] << 8), buf[6]);
+- if (buf[0] < 9) {
+- dump_junk(buf, " ", 7);
+- return;
+- }
++ endpoint->bLength, endpoint->bDescriptorType, endpoint->bEndpointAddress, endpoint->bEndpointAddress & 0x0f,
++ (endpoint->bEndpointAddress & 0x80) ? "IN" : "OUT", endpoint->bmAttributes,
++ typeattr[endpoint->bmAttributes & 3], syncattr[(endpoint->bmAttributes >> 2) & 3],
++ usage[(endpoint->bmAttributes >> 4) & 3], endpoint->wMaxPacketSize,
++ hb[(endpoint->wMaxPacketSize >> 11) & 3],
++ endpoint->wMaxPacketSize & 0x3ff,
++ endpoint->bInterval);
++ /* only for audio endpoints */
++ if (endpoint->bLength == 9)
+ printf(" bRefresh %5u\n"
+ " bSynchAddress %5u\n",
+- buf[7], buf[8]);
+- dump_junk(buf, " ", 9);
++ endpoint->bRefresh, endpoint->bSynchAddress);
++
++ /* avoid re-ordering or hiding descriptors for display */
++ if (endpoint->extralen)
++ {
++ size = endpoint->extralen;
++ buf = endpoint->extra;
++ while (size >= 2 * sizeof(u_int8_t))
++ {
++ if (buf[0] < 2) {
++ dump_junk(" ", buf, size);
++ break;
++ }
++ switch (buf[1]) {
++ case USB_DT_CS_ENDPOINT:
++ if (interface->bInterfaceClass == 1 && interface->bInterfaceSubClass == 2)
++ dump_audiostreaming_endpoint(dev, buf);
++ else if (interface->bInterfaceClass == 1 && interface->bInterfaceSubClass == 3)
++ dump_midistreaming_endpoint(dev, buf);
++ break;
++ case USB_DT_OTG:
++ /* handled separately */
++ break;
++ default:
++ /* often a misplaced class descriptor */
++ printf(" UNRECOGNIZED: ");
++ dump_bytes(buf, size);
++ break;
++ }
++ size -= buf[0];
++ buf += buf[0];
++ }
++ }
+ }
+
+ /* ---------------------------------------------------------------------- */
+@@ -385,7 +472,7 @@
+ * Audio Class descriptor dump
+ */
+
+-static void dump_audiocontrol_interface(int fd, unsigned char *buf, u_int16_t lang)
++static void dump_audiocontrol_interface(struct usb_dev_handle *dev, unsigned char *buf)
+ {
+ static const char *chconfig[] = {
+ "Left Front (L)", "Right Front (R)", "Center Front (C)", "Low Freqency Enhancement (LFE)",
+@@ -423,8 +510,8 @@
+
+ case 0x02: /* INPUT_TERMINAL */
+ printf("(INPUT_TERMINAL)\n");
+- get_string(fd, chnames, sizeof(chnames), buf[10], lang);
+- get_string(fd, term, sizeof(term), buf[11], lang);
++ get_string(dev, chnames, sizeof(chnames), buf[10]);
++ get_string(dev, term, sizeof(term), buf[11]);
+ termt = buf[4] | (buf[5] << 8);
+ get_audioterminal_string(termts, sizeof(termts), termt);
+ if (buf[0] < 12)
+@@ -447,7 +534,7 @@
+
+ case 0x03: /* OUTPUT_TERMINAL */
+ printf("(OUTPUT_TERMINAL)\n");
+- get_string(fd, term, sizeof(term), buf[8], lang);
++ get_string(dev, term, sizeof(term), buf[8]);
+ termt = buf[4] | (buf[5] << 8);
+ get_audioterminal_string(termts, sizeof(termts), termt);
+ if (buf[0] < 9)
+@@ -471,8 +558,8 @@
+ } else {
+ N = 1+(j*k-1)/8;
+ }
+- get_string(fd, chnames, sizeof(chnames), buf[8+j], lang);
+- get_string(fd, term, sizeof(term), buf[9+j+N], lang);
++ get_string(dev, chnames, sizeof(chnames), buf[8+j]);
++ get_string(dev, term, sizeof(term), buf[9+j+N]);
+ if (buf[0] < 10+j+N)
+ printf(" Warning: Descriptor too short\n");
+ chcfg = buf[6+j] | (buf[7+j] << 8);
+@@ -499,7 +586,7 @@
+ printf("(SELECTOR_UNIT)\n");
+ if (buf[0] < 6+buf[4])
+ printf(" Warning: Descriptor too short\n");
+- get_string(fd, term, sizeof(term), buf[5+buf[4]], lang);
++ get_string(dev, term, sizeof(term), buf[5+buf[4]]);
+
+ printf(" bUnitID %5u\n"
+ " bNrInPins %5u\n",
+@@ -519,7 +606,7 @@
+ k = (buf[0] - 7) / j;
+ if (buf[0] < 7+buf[5]*k)
+ printf(" Warning: Descriptor too short\n");
+- get_string(fd, term, sizeof(term), buf[6+buf[5]*k], lang);
++ get_string(dev, term, sizeof(term), buf[6+buf[5]*k]);
+ printf(" bUnitID %5u\n"
+ " bSourceID %5u\n"
+ " bControlSize %5u\n",
+@@ -529,7 +616,7 @@
+ if (buf[5] > 1)
+ chcfg |= (buf[7+buf[5]*i] << 8);
+ for (j = 0; j < buf[5]; j++)
+- printf(" bmaControls(%2u) 0x%02x\n", j, buf[6+buf[5]*i+j]);
++ printf(" bmaControls(%2u) 0x%02x\n", i, buf[6+buf[5]*i+j]);
+ for (j = 0; j < 10; j++)
+ if ((chcfg >> j) & 1)
+ printf(" %s\n", chftrcontrols[j]);
+@@ -542,8 +629,8 @@
+ printf("(PROCESSING_UNIT)\n");
+ j = buf[6];
+ k = buf[11+j];
+- get_string(fd, chnames, sizeof(chnames), buf[10+j], lang);
+- get_string(fd, term, sizeof(term), buf[12+j+k], lang);
++ get_string(dev, chnames, sizeof(chnames), buf[10+j]);
++ get_string(dev, term, sizeof(term), buf[12+j+k]);
+ chcfg = buf[8+j] | (buf[9+j] << 8);
+ if (buf[0] < 13+j+k)
+ printf(" Warning: Descriptor too short\n");
+@@ -566,15 +653,15 @@
+ printf(" Enable Processing\n");
+ printf(" iProcessing %5u %s\n"
+ " Process-Specific ", buf[12+j+k], term);
+- dump_junk2(buf+(13+j+k), buf[0]-(13+j+k));
++ dump_bytes(buf+(13+j+k), buf[0]-(13+j+k));
+ break;
+
+ case 0x08: /* EXTENSION_UNIT */
+ printf("(EXTENSION_UNIT)\n");
+ j = buf[6];
+ k = buf[11+j];
+- get_string(fd, chnames, sizeof(chnames), buf[10+j], lang);
+- get_string(fd, term, sizeof(term), buf[12+j+k], lang);
++ get_string(dev, chnames, sizeof(chnames), buf[10+j]);
++ get_string(dev, term, sizeof(term), buf[12+j+k]);
+ chcfg = buf[8+j] | (buf[9+j] << 8);
+ if (buf[0] < 13+j+k)
+ printf(" Warning: Descriptor too short\n");
+@@ -603,12 +690,12 @@
+ default:
+ printf("(unknown)\n"
+ " Invalid desc subtype:");
+- dump_junk2(buf+3, buf[0]-3);
++ dump_bytes(buf+3, buf[0]-3);
+ break;
+ }
+ }
+
+-static void dump_audiostreaming_interface(int fd, unsigned char *buf)
++static void dump_audiostreaming_interface(struct usb_dev_handle *dev, unsigned char *buf)
+ {
+ static const char *fmtItag[] = { "TYPE_I_UNDEFINED", "PCM", "PCM8", "IEEE_FLOAT", "ALAW", "MULAW" };
+ static const char *fmtIItag[] = { "TYPE_II_UNDEFINED", "MPEG", "AC-3" };
+@@ -622,7 +709,7 @@
+ printf(" Warning: Invalid descriptor\n");
+ else if (buf[0] < 3)
+ printf(" Warning: Descriptor too short\n");
+- printf(" AudioControl Interface Descriptor:\n"
++ printf(" AudioStreaming Interface Descriptor:\n"
+ " bLength %5u\n"
+ " bDescriptorType %5u\n"
+ " bDescriptorSubtype %5u ",
+@@ -717,7 +804,7 @@
+ default:
+ printf("(unknown)\n"
+ " Invalid desc format type:");
+- dump_junk2(buf+4, buf[0]-4);
++ dump_bytes(buf+4, buf[0]-4);
+ }
+ break;
+
+@@ -833,18 +920,18 @@
+ default:
+ printf("(unknown)\n"
+ " Invalid desc format type:");
+- dump_junk2(buf+4, buf[0]-4);
++ dump_bytes(buf+4, buf[0]-4);
+ }
+ break;
+
+ default:
+ printf(" Invalid desc subtype:");
+- dump_junk2(buf+3, buf[0]-3);
++ dump_bytes(buf+3, buf[0]-3);
+ break;
+ }
+ }
+
+-static void dump_audiostreaming_endpoint(int fd, unsigned char *buf)
++static void dump_audiostreaming_endpoint(struct usb_dev_handle *dev, unsigned char *buf)
+ {
+ static const char *lockdelunits[] = { "Undefined", "Milliseconds", "Decoded PCM samples", "Reserved" };
+ unsigned int lckdelidx;
+@@ -874,7 +961,7 @@
+ dump_junk(buf, " ", 7);
+ }
+
+-static void dump_midistreaming_interface(int fd, unsigned char *buf, u_int16_t lang)
++static void dump_midistreaming_interface(struct usb_dev_handle *dev, unsigned char *buf)
+ {
+ static const char *jacktypes[] = {"Undefined", "Embedded", "External"};
+ char jackstr[128];
+@@ -906,7 +993,7 @@
+ printf("(MIDI_IN_JACK)\n");
+ if (buf[0] < 6)
+ printf(" Warning: Descriptor too short\n");
+- get_string(fd, jackstr, sizeof(jackstr), buf[5], lang);
++ get_string(dev, jackstr, sizeof(jackstr), buf[5]);
+ printf( " bJackType %5u %s\n"
+ " bJackID %5u\n"
+ " iJack %5u %s\n",
+@@ -930,7 +1017,7 @@
+ j, buf[2*j+6], j, buf[2*j+7]);
+ }
+ j = 6+buf[5]*2; /* midi10.pdf says, incorrectly: 5+2*p */
+- get_string(fd, jackstr, sizeof(jackstr), buf[j], lang);
++ get_string(dev, jackstr, sizeof(jackstr), buf[j]);
+ printf( " iJack %5u %s\n",
+ buf[j], jackstr);
+ dump_junk(buf, " ", j+1);
+@@ -985,19 +1072,19 @@
+ if (caps & 0x800)
+ printf( " DLS2 (Downloadable Sounds Level 2)\n");
+ j = 9+2*buf[4]+capssize;
+- get_string(fd, jackstr, sizeof(jackstr), buf[j], lang);
++ get_string(dev, jackstr, sizeof(jackstr), buf[j]);
+ printf( " iElement %5u %s\n", buf[j], jackstr);
+ dump_junk(buf, " ", j+1);
+ break;
+
+ default:
+ printf("\n Invalid desc subtype: ");
+- dump_junk2(buf+3, buf[0]-3);
++ dump_bytes(buf+3, buf[0]-3);
+ break;
+ }
+ }
+
+-static void dump_midistreaming_endpoint(int fd, unsigned char *buf)
++static void dump_midistreaming_endpoint(struct usb_dev_handle *dev, unsigned char *buf)
+ {
+ unsigned int j;
+
+@@ -1017,26 +1104,196 @@
+ dump_junk(buf, " ", 4+buf[3]);
+ }
+
+-
+-static void dump_hub(char *p)
++static void dump_hub(char *prefix, unsigned char *p, int has_tt)
+ {
+ unsigned int l, i, j;
++ unsigned int wHubChar = (p[4] << 8) | p[3];
+
+- printf(" Hub Descriptor:\n");
+- printf(" bLength %3u\n",p[0]);
+- printf(" bDesriptorType %3u\n",p[1]);
+- printf(" nNbrPorts %3u\n",p[2]);
+- printf(" wHubCharacteristic 0x%02x 0x%02x\n", p[3],p[4]);
+- printf(" bPwrOn2PwrGood %3u * 2 milli seconds\n",p[5]);
+- printf(" bHubContrCurrent %3u milli Ampere\n",p[6]);
++ printf("%sHub Descriptor:\n", prefix);
++ printf("%s bLength %3u\n", prefix, p[0]);
++ printf("%s bDescriptorType %3u\n", prefix, p[1]);
++ printf("%s nNbrPorts %3u\n", prefix, p[2]);
++ printf("%s wHubCharacteristic 0x%04x\n", prefix, wHubChar);
++ switch (wHubChar & 0x03) {
++ case 0:
++ printf("%s Ganged power switching\n", prefix);
++ break;
++ case 1:
++ printf("%s Per-port power switching\n", prefix);
++ break;
++ default:
++ printf("%s No power switching (usb 1.0)\n", prefix);
++ break;
++ }
++ if (wHubChar & 0x04)
++ printf("%s Compound device\n", prefix);
++ switch ((wHubChar >> 3) & 0x03) {
++ case 0:
++ printf("%s Ganged overcurrent protection\n", prefix);
++ break;
++ case 1:
++ printf("%s Per-port overcurrent protection\n", prefix);
++ break;
++ default:
++ printf("%s No overcurrent protection\n", prefix);
++ break;
++ }
++ if (has_tt) {
++ l = (wHubChar >> 5) & 0x03;
++ printf("%s TT think time %d FS bits\n", prefix, (l + 1) * 8);
++ }
++ if (wHubChar & (1<<7))
++ printf("%s Port indicators\n", prefix);
++ printf("%s bPwrOn2PwrGood %3u * 2 milli seconds\n", prefix, p[5]);
++ printf("%s bHubContrCurrent %3u milli Ampere\n", prefix, p[6]);
+ l= (p[2] >> 3) + 1; /* this determines the variable number of bytes following */
+- printf(" DeviceRemovable ");
++ printf("%s DeviceRemovable ", prefix);
+ for(i = 0; i < l; i++)
+ printf(" 0x%02x", p[7+i]);
+- printf("\n PortPwrCtrlMask ");
+- for(j = 0; j < l; j++)
+- printf(" 0x%02x ", p[7+i+j]);
+- printf("\n");
++ printf("\n%s PortPwrCtrlMask ", prefix);
++ for(j = 0; j < l; j++)
++ printf(" 0x%02x ", p[7+i+j]);
++ printf("\n");
++}
++
++static void dump_ccid_device(unsigned char *buf)
++{
++ unsigned int us;
++
++ if (buf[0] < 54) {
++ printf(" Warning: Descriptor too short\n");
++ return;
++ }
++ printf(" ChipCard Interface Descriptor:\n"
++ " bLength %5u\n"
++ " bDescriptorType %5u\n"
++ " bcdCCID %2x.%02x",
++ buf[0], buf[1], buf[3], buf[2]);
++ if (buf[3] != 1 || buf[2] != 0)
++ fputs(" (Warning: Only accurate for version 1.0)", stdout);
++ putchar('\n');
++
++ printf(" nMaxSlotIndex %5u\n"
++ " bVoltageSupport %5u %s\n",
++ buf[4],
++ buf[5], (buf[5] == 1? "5.0V" : buf[5] == 2? "3.0V"
++ : buf[5] == 3? "1.8V":"?"));
++
++ us = convert_le_u32 (buf+6);
++ printf(" dwProtocols %5u ", us);
++ if ((us & 1))
++ fputs(" T=0", stdout);
++ if ((us & 2))
++ fputs(" T=1", stdout);
++ if ((us & ~3))
++ fputs(" (Invalid values detected)", stdout);
++ putchar('\n');
++
++ us = convert_le_u32(buf+10);
++ printf(" dwDefaultClock %5u\n", us);
++ us = convert_le_u32(buf+14);
++ printf(" dwMaxiumumClock %5u\n", us);
++ printf(" bNumClockSupported %5u\n", buf[18]);
++ us = convert_le_u32(buf+19);
++ printf(" dwDataRate %7u bps\n", us);
++ us = convert_le_u32(buf+23);
++ printf(" dwMaxDataRate %7u bps\n", us);
++ printf(" bNumDataRatesSupp. %5u\n", buf[27]);
++
++ us = convert_le_u32(buf+28);
++ printf(" dwMaxIFSD %5u\n", us);
++
++ us = convert_le_u32(buf+32);
++ printf(" dwSyncProtocols %08X ", us);
++ if ((us&1))
++ fputs(" 2-wire", stdout);
++ if ((us&2))
++ fputs(" 3-wire", stdout);
++ if ((us&4))
++ fputs(" I2C", stdout);
++ putchar('\n');
++
++ us = convert_le_u32(buf+36);
++ printf(" dwMechanical %08X ", us);
++ if ((us & 1))
++ fputs(" accept", stdout);
++ if ((us & 2))
++ fputs(" eject", stdout);
++ if ((us & 4))
++ fputs(" capture", stdout);
++ if ((us & 8))
++ fputs(" lock", stdout);
++ putchar('\n');
++
++ us = convert_le_u32(buf+40);
++ printf(" dwFeatures %08X\n", us);
++ if ((us & 0x0002))
++ fputs(" Auto configuration based on ATR\n",stdout);
++ if ((us & 0x0004))
++ fputs(" Auto activation on insert\n",stdout);
++ if ((us & 0x0008))
++ fputs(" Auto voltage selection\n",stdout);
++ if ((us & 0x0010))
++ fputs(" Auto clock change\n",stdout);
++ if ((us & 0x0020))
++ fputs(" Auto baud rate change\n",stdout);
++ if ((us & 0x0040))
++ fputs(" Auto parameter negotation made by CCID\n",stdout);
++ else if ((us & 0x0080))
++ fputs(" Auto PPS made by CCID\n",stdout);
++ else if ((us & (0x0040 | 0x0080)))
++ fputs(" WARNING: conflicting negotation features\n",stdout);
++
++ if ((us & 0x0100))
++ fputs(" CCID can set ICC in clock stop mode\n",stdout);
++ if ((us & 0x0200))
++ fputs(" NAD value other than 0x00 accpeted\n",stdout);
++ if ((us & 0x0400))
++ fputs(" Auto IFSD exchange\n",stdout);
++
++ if ((us & 0x00010000))
++ fputs(" TPDU level exchange\n",stdout);
++ else if ((us & 0x00020000))
++ fputs(" Short APDU level exchange\n",stdout);
++ else if ((us & 0x00040000))
++ fputs(" Short and extended APDU level exchange\n",stdout);
++ else if ((us & 0x00070000))
++ fputs(" WARNING: conflicting exchange levels\n",stdout);
++
++ us = convert_le_u32(buf+44);
++ printf(" dwMaxCCIDMsgLen %5u\n", us);
++
++ printf(" bClassGetResponse ");
++ if (buf[48] == 0xff)
++ fputs("echo\n", stdout);
++ else
++ printf(" %02X\n", buf[48]);
++
++ printf(" bClassEnvelope ");
++ if (buf[49] == 0xff)
++ fputs("echo\n", stdout);
++ else
++ printf(" %02X\n", buf[48]);
++
++ printf(" wlcdLayout ");
++ if (!buf[50] && !buf[51])
++ fputs("none\n", stdout);
++ else
++ printf("%u cols %u lines\n", buf[50], buf[51]);
++
++ printf(" bPINSupport %5u ", buf[52]);
++ if ((buf[52] & 1))
++ fputs(" verification", stdout);
++ if ((buf[52] & 2))
++ fputs(" modification", stdout);
++ putchar('\n');
++
++ printf(" bMaxCCIDBusySlots %5u\n", buf[53]);
++
++ if (buf[0] > 54) {
++ fputs(" junk ", stdout);
++ dump_bytes(buf+54, buf[0]-54);
++ }
+ }
+
+ /* ---------------------------------------------------------------------- */
+@@ -1047,21 +1304,21 @@
+
+ static void dump_report_desc(unsigned char *b, int l)
+ {
+- unsigned int t, j, bsize, btag, btype, data, hut;
++ unsigned int t, j, bsize, btag, btype, data = 0xffff, hut = 0xffff;
+ int i;
+ char *types[4] = { "Main", "Global", "Local", "reserved" };
+ char indent[] = " ";
+
+- printf(" Report Descriptor: (length is %d)\n", l);
++ printf(" Report Descriptor: (length is %d)\n", l);
+ for(i = 0; i < l; ) {
+ t = b[i];
+- bsize= b[i] & 0x03;
++ bsize = b[i] & 0x03;
+ if (bsize == 3)
+- bsize=4;
++ bsize = 4;
+ btype = b[i] & (0x03 << 2);
+ btag = b[i] & ~0x03; /* 2 LSB bits encode length */
+ printf(" Item(%-6s): %s, data=", types[btype>>2], names_reporttag(btag));
+- if(bsize > 0) {
++ if (bsize > 0) {
+ printf(" [ ");
+ data = 0;
+ for(j = 0; j < bsize; j++) {
+@@ -1101,9 +1358,9 @@
+
+ default:
+ if(data & 0x80)
+- printf("Vendor definened\n");
++ printf("Vendor defined\n");
+ else
+- printf("Reserved for future use.\n");
++ printf("Reserved for future use.\n");
+ }
+ break;
+ case 0x80: /* Input */
+@@ -1121,19 +1378,19 @@
+ data & 0x40 ? "Null_State": "No_Null_Position",
+ data & 0x80 ? "Volatile": "Non_Volatile",
+ data &0x100 ? "Buffered Bytes": "Bitfield"
+- );
++ );
+ }
+ i += 1 + bsize;
+ }
+ }
+
+-static void dump_hid_device(int fd, unsigned char *buf,int interface_number)
++static void dump_hid_device(struct usb_dev_handle *dev, struct usb_interface_descriptor *interface, unsigned char *buf)
+ {
+ unsigned int i, len;
+ int n;
+ unsigned char dbuf[8192];
+
+- if (buf[1] != USB_DT_CS_DEVICE)
++ if (buf[1] != USB_DT_HID)
+ printf(" Warning: Invalid descriptor\n");
+ else if (buf[0] < 6+3*buf[5])
+ printf(" Warning: Descriptor too short\n");
+@@ -1141,306 +1398,340 @@
+ " bLength %5u\n"
+ " bDescriptorType %5u\n"
+ " bcdHID %2x.%02x\n"
+- " bCountryCode %5u\n"
++ " bCountryCode %5u %s\n"
+ " bNumDescriptors %5u\n",
+- buf[0], buf[1], buf[3], buf[2], buf[4], buf[5]);
++ buf[0], buf[1], buf[3], buf[2], buf[4],
++ names_countrycode(buf[4]) ? : "Unknown", buf[5]);
+ for (i = 0; i < buf[5]; i++)
+ printf(" bDescriptorType %5u %s\n"
+ " wDescriptorLength %5u\n",
+- buf[6+3*i], names_hid(buf[6+3*i]), buf[7+3*i] | (buf[8+3*i] << 8));
++ buf[6+3*i], names_hid(buf[6+3*i]),
++ buf[7+3*i] | (buf[8+3*i] << 8));
+ dump_junk(buf, " ", 6+3*buf[5]);
+ if (!do_report_desc)
+ return;
++
+ for (i = 0; i < buf[5]; i++) {
+- if (buf[6+3*i] != 0x22) /* we are just interested in report descriptors*/
++ /* we are just interested in report descriptors*/
++ if (buf[6+3*i] != USB_DT_REPORT)
+ continue;
+ len = buf[7+3*i] | (buf[8+3*i] << 8);
+ if (len > sizeof(dbuf)) {
+ printf("report descriptor too long\n");
+ continue;
+ }
+- if ((n = usb_control_msg(fd, 0x81 , 0x06, 0x22<<8, interface_number, len, dbuf)) < 0) {
+- if (open_mode == O_RDWR)
+- printf("cannot get report descriptor\n");
+- continue;
++ if (usb_claim_interface(dev, interface->bInterfaceNumber) == 0) {
++ if ((n = usb_control_msg(dev,
++ USB_ENDPOINT_IN | USB_TYPE_STANDARD
++ | USB_RECIP_INTERFACE,
++ USB_REQ_GET_DESCRIPTOR,
++ (USB_DT_REPORT << 8),
++ interface->bInterfaceNumber,
++ dbuf,
++ len,
++ CTRL_TIMEOUT)) > 0)
++ dump_report_desc(dbuf, n);
++ usb_release_interface(dev, interface->bInterfaceNumber);
++ } else {
++ /* recent Linuxes require claim() for RECIP_INTERFACE,
++ * so "rmmod hid" will often make these available.
++ */
++ printf( " Report Descriptors: \n"
++ " ** UNAVAILABLE **\n");
+ }
+- dump_report_desc(dbuf, n);
+ }
+ }
+
+-/* ---------------------------------------------------------------------- */
+-
+-static void do_config(int fd, unsigned int nr, u_int16_t lang)
++static char *
++dump_comm_descriptor(struct usb_dev_handle *dev, unsigned char *buf, char *indent)
+ {
+- unsigned char buf[1024],*p;
+- unsigned int sz,curinterface;
+- int l;
+- u_int8_t curclass = 0xff, cursubclass = 0xff;
++ int tmp;
++ char str [128];
+
+- if (usb_control_msg(fd, USB_DIR_IN, USB_REQ_GET_DESCRIPTOR, (USB_DT_CONFIG << 8) | nr,
+- 0, USB_DT_CONFIG_SIZE, buf) < 0) {
+- if (open_mode == O_RDWR)
+- fprintf(stdout, "cannot get config descriptor %d, %s (%d)\n", nr, strerror(errno), errno);
+- return;
+- }
+- if (buf[0] < USB_DT_CONFIG_SIZE || buf[1] != USB_DT_CONFIG)
+- fprintf(stderr, "Warning: invalid config descriptor\n");
+- sz = buf[2] | buf[3] << 8;
+- if (sz > sizeof(buf)) {
+- fprintf(stderr, "Config %d descriptor too long, truncating\n", nr);
+- sz = sizeof(buf);
+- }
+- if (usb_control_msg(fd, USB_DIR_IN, USB_REQ_GET_DESCRIPTOR, (USB_DT_CONFIG << 8) | nr,
+- 0, sz, buf) < 0) {
+- if (open_mode == O_RDWR)
+- fprintf(stdout, "cannot get config descriptor %d, %s (%d)\n", nr, strerror(errno), errno);
+- return;
++ switch (buf[2]) {
++ case 0:
++ if (buf[0] != 5)
++ goto bad;
++ printf("%sCDC Header:\n"
++ "%s bcdCDC %x.%02x\n",
++ indent,
++ indent, buf[4], buf[3]);
++ break;
++ case 0x01: /* call management functional desc */
++ if (buf [0] != 5)
++ goto bad;
++ printf("%sCDC Call Management:\n"
++ "%s bmCapabilities 0x%02x\n",
++ indent,
++ indent, buf[3]);
++ if (buf[3] & 0x01)
++ printf("%s call management\n", indent);
++ if (buf[3] & 0x02)
++ printf("%s use DataInterface\n", indent);
++ printf("%s bDataInterface %d\n", indent, buf[4]);
++ break;
++ case 0x02: /* acm functional desc */
++ if (buf [0] != 4)
++ goto bad;
++ printf("%sCDC ACM:\n"
++ "%s bmCapabilities 0x%02x\n",
++ indent,
++ indent, buf[3]);
++ if (buf[3] & 0x08)
++ printf("%s connection notifications\n", indent);
++ if (buf[3] & 0x04)
++ printf("%s sends break\n", indent);
++ if (buf[3] & 0x02)
++ printf("%s line coding and serial state\n", indent);
++ if (buf[3] & 0x01)
++ printf("%s get/set/clear comm features\n", indent);
++ break;
++ case 0x06: /* union desc */
++ if (buf [0] < 5)
++ goto bad;
++ printf("%sCDC Union:\n"
++ "%s bMasterInterface %d\n"
++ "%s bSlaveInterface ",
++ indent,
++ indent, buf [3],
++ indent);
++ for (tmp = 4; tmp < buf [0]; tmp++)
++ printf("%d ", buf [tmp]);
++ printf("\n");
++ break;
++ case 0x0f: /* ethernet functional desc */
++ if (buf [0] != 13)
++ goto bad;
++ get_string(dev, str, sizeof str, buf[3]);
++ tmp = buf [7] << 8;
++ tmp |= buf [6]; tmp <<= 8;
++ tmp |= buf [5]; tmp <<= 8;
++ tmp |= buf [4];
++ printf("%sCDC Ethernet:\n"
++ "%s iMacAddress %10d %s\n"
++ "%s bmEthernetStatistics 0x%08x\n",
++ indent,
++ indent, buf[3], (buf[3] && *str) ? str : "(?\?)",
++ indent, tmp);
++ /* FIXME dissect ALL 28 bits */
++ printf("%s wMaxSegmentSize %10d\n"
++ "%s wNumberMCFilters 0x%04x\n"
++ "%s bNumberPowerFilters %10d\n",
++ indent, (buf[9]<<8)|buf[8],
++ indent, (buf[11]<<8)|buf[10],
++ indent, buf[12]);
++ break;
++ default:
++ /* FIXME there are about a dozen more descriptor types */
++ printf("%sUNRECOGNIZED: ", indent);
++ dump_bytes(buf, buf[0]);
++ return "unrecognized comm descriptor";
+ }
+- p = buf;
+- while (sz >= 2) {
+- if (p[0] < 2)
+- break;
+- if (p[0] > sz) {
+- printf(" descriptor length past end:");
+- dump_junk2(p, sz);
+- sz = 0;
+- break;
+- }
+- switch (p[1]) {
+- case USB_DT_DEVICE:
+- dump_device(fd, p, lang);
+- curclass = p[4];
+- cursubclass = p[5];
+- break;
+-
+- case USB_DT_CONFIG:
+- dump_config(fd, p);
+- break;
+-
+- case USB_DT_INTERFACE:
+- dump_interface(fd, p, lang);
+- curclass = p[5];
+- cursubclass = p[6];
+- curinterface = p[2];
+- break;
+-
+- case USB_DT_ENDPOINT:
+- dump_endpoint(fd, p);
+- break;
++ return 0;
+
+- case USB_DT_CS_DEVICE:
+- if (curclass == 3) {
+- dump_hid_device(fd, p, curinterface);
+- break;
+- }
+- printf(" unknown descriptor type:");
+- dump_junk2(p, p[0]);
+- break;
++ bad:
++ printf("%sinvalid: ", indent);
++ dump_bytes(buf, buf[0]);
++ return "corrupt comm descriptor";
++}
+
+- case USB_DT_CS_CONFIG:
+- printf(" unknown descriptor type:");
+- dump_junk2(p, p[0]);
+- break;
++/* ---------------------------------------------------------------------- */
+
+- case USB_DT_CS_STRING:
+- printf(" unknown descriptor type:");
+- dump_junk2(p, p[0]);
+- break;
++static void do_hub(struct usb_dev_handle *fd, unsigned has_tt)
++{
++ unsigned char buf [7];
++ int ret;
++
++ ret = usb_control_msg(fd,
++ USB_ENDPOINT_IN | USB_TYPE_CLASS | USB_RECIP_DEVICE,
++ USB_REQ_GET_DESCRIPTOR,
++ 0x29 << 8, 0,
++ buf, sizeof buf, CTRL_TIMEOUT);
++ if (ret != sizeof buf) {
++ /* Linux returns this for suspended devices */
++ if (errno != EHOSTUNREACH)
++ perror ("can't get hub descriptor");
++ return;
++ }
++ dump_hub("", buf, has_tt);
++}
+
+- case USB_DT_CS_INTERFACE:
+- if (curclass == 1 && cursubclass == 1) {
+- dump_audiocontrol_interface(fd, p, lang);
+- break;
+- }
+- if (curclass == 1 && cursubclass == 2) {
+- dump_audiostreaming_interface(fd, p);
+- break;
+- }
+- if (curclass == 1 && cursubclass == 3) {
+- dump_midistreaming_interface(fd, p, lang);
+- break;
+- }
+- printf(" unknown descriptor type:");
+- dump_junk2(p, p[0]);
+- break;
++static void do_dualspeed(struct usb_dev_handle *fd)
++{
++ unsigned char buf [10];
++ char cls[128], subcls[128], proto[128];
++ int ret;
++
++ ret = usb_control_msg(fd,
++ USB_ENDPOINT_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE,
++ USB_REQ_GET_DESCRIPTOR,
++ USB_DT_DEVICE_QUALIFIER << 8, 0,
++ buf, sizeof buf, CTRL_TIMEOUT);
+
+- case USB_DT_CS_ENDPOINT:
+- if (curclass == 1 && cursubclass == 2) {
+- dump_audiostreaming_endpoint(fd, p);
+- break;
+- }
+- if (curclass == 1 && cursubclass == 3) {
+- dump_midistreaming_endpoint(fd, p);
+- break;
+- }
+- printf(" unknown descriptor type:");
+- dump_junk2(p, p[0]);
+- break;
++ /* all dual-speed devices have a qualifier */
++ if (ret != sizeof buf
++ || buf[0] != ret
++ || buf[1] != USB_DT_DEVICE_QUALIFIER)
++ return;
+
+- case USB_DT_HUB:
+- dump_hub(p);
+- break;
++ get_class_string(cls, sizeof(cls),
++ buf[4]);
++ get_subclass_string(subcls, sizeof(subcls),
++ buf[4], buf[5]);
++ get_protocol_string(proto, sizeof(proto),
++ buf[4], buf[5], buf[6]);
++ printf("Device Qualifier (for other device speed):\n"
++ " bLength %5u\n"
++ " bDescriptorType %5u\n"
++ " bcdUSB %2x.%02x\n"
++ " bDeviceClass %5u %s\n"
++ " bDeviceSubClass %5u %s\n"
++ " bDeviceProtocol %5u %s\n"
++ " bMaxPacketSize0 %5u\n"
++ " bNumConfigurations %5u\n",
++ buf[0], buf[1],
++ buf[3], buf[2],
++ buf[4], cls,
++ buf[5], subcls,
++ buf[6], proto,
++ buf[7], buf[8]);
+
+- default:
+- printf(" unknown descriptor type:");
+- dump_junk2(p, p[0]);
+- }
+- sz -= p[0];
+- p += p[0];
+- }
+- if (sz > 0) {
+- printf(" junk at config descriptor end:");
+- dump_junk2(p, sz);
+- }
++ /* FIXME also show the OTHER_SPEED_CONFIG descriptors */
+ }
+
+-/* ---------------------------------------------------------------------- */
++static unsigned char *find_otg(unsigned char *buf, int buflen)
++{
++ if (!buf)
++ return 0;
++ while (buflen >= 3) {
++ if (buf[0] == 3 && buf[1] == USB_DT_OTG)
++ return buf;
++ if (buf[0] > buflen)
++ return 0;
++ buflen -= buf[0];
++ buf += buf[0];
++ }
++ return 0;
++}
+
+-/* returns first lang ID */
+-static u_int16_t dump_langids(int fd, int quiet)
++static void do_otg(struct usb_config_descriptor *config)
+ {
+- unsigned char b[256];
+- int i, l;
+- u_int16_t lang;
++ unsigned i, j, k;
++ unsigned char *desc;
+
+- b[0] = b[1] = 0xbf;
+- l = usb_control_msg(fd, USB_DIR_IN, USB_REQ_GET_DESCRIPTOR, USB_DT_STRING << 8, 0, sizeof(b), b);
++ /* each config of an otg device has an OTG descriptor */
++ desc = find_otg(config->extra, config->extralen);
++ for (i = 0; !desc && i < config->bNumInterfaces; i++) {
++ struct usb_interface *intf;
+
+- if (l < 0) {
+- if (open_mode == O_RDWR)
+- printf(" Language IDs: none (cannot get min. string descriptor; got len=%d, error=%d:%s)\n",
+- l, errno, strerror(errno));
+- return 0;
+- }
+- if (l < 4 || b[0] != l) {
+- printf(" Language IDs: none (invalid length string descriptor %02x; len=%d)\n", b[0], l);
+- return 0;
+- }
+- /* save first language ID for further get_string_descriptors */
+- lang = b[2] | (b[3] << 8);
+-#if 0
+- printf ("dump_langids: ret=%d:%d, lang=0x%x, length=%d\n", l, errno, lang, b[0]);
+- dump_junk2 (b, 32);
+-#endif
+- if(quiet)
+- return lang;
+- printf(" Language IDs: (length=%d)\n", b[0]);
+- for (i = 0; i < ((b[0] - 2) / 2); i++) {
+- l = b[2+2*i] | (b[3+2*i] << 8);
+- printf(" %04x %s(%s)\n", l, names_langid(l & 0x3ff), names_langid(l));
++ intf = &config->interface [i];
++ for (j = 0; !desc && j < intf->num_altsetting; j++) {
++ struct usb_interface_descriptor *alt;
++
++ alt = &intf->altsetting[j];
++ desc = find_otg(alt->extra, alt->extralen);
++ for (k = 0; !desc && k < alt->bNumEndpoints; k++) {
++ struct usb_endpoint_descriptor *ep;
++
++ ep = &alt->endpoint[k];
++ desc = find_otg(ep->extra, ep->extralen);
++ }
++ }
+ }
+- return lang;
+-}
++ if (!desc)
++ return;
+
+-/* ---------------------------------------------------------------------- */
++ printf("OTG Descriptor:\n"
++ " bLength %3u\n"
++ " bDescriptorType %3u\n"
++ " bmAttributes 0x%02x\n"
++ "%s%s",
++ desc[0], desc[1], desc[2],
++ (desc[2] & 0x01)
++ ? " SRP (Session Request Protocol)\n" : "",
++ (desc[2] & 0x02)
++ ? " HNP (Host Negotiation Protocol)\n" : "");
++}
+
+-static void dumpdev(unsigned char *devdesc, int fd, unsigned int flags)
++static void dumpdev(struct usb_device *dev, unsigned int flags)
+ {
+- unsigned int i, maxcfg;
+- u_int16_t lang;
++ struct usb_dev_handle *udev;
++ int i;
+
+- maxcfg = devdesc[17];
+- if (devdesc[0] < 18 || devdesc[1] != USB_DT_DEVICE)
+- maxcfg = 1;
+- lang = dump_langids(fd, 1);
+- dump_device(fd, devdesc, lang);
+- for (i = 0; i < maxcfg; i++)
+- do_config(fd, i, lang);
+- lang = dump_langids(fd, 0);
++ udev = usb_open(dev);
++ if (udev) {
++ dump_device(udev, &dev->descriptor, flags);
++ if (dev->config) {
++ for (i = 0; i < dev->descriptor.bNumConfigurations;
++ i++) {
++ dump_config(udev, &dev->config[i]);
++ }
++ do_otg(&dev->config[0]);
++ }
++ if (dev->descriptor.bDeviceClass == USB_CLASS_HUB)
++ do_hub(udev, dev->descriptor.bDeviceProtocol);
++ if (dev->descriptor.bcdUSB >= 0x0200)
++ do_dualspeed(udev);
++ usb_close(udev);
++ }
++ else
++ fprintf(stderr, "Couldn't open device\n");
+ }
+
+ /* ---------------------------------------------------------------------- */
+
+ static int dump_one_device(const char *path, unsigned int flags)
+ {
+- unsigned char buf[USB_DT_DEVICE_SIZE];
+- unsigned int vid, pid;
++ struct usb_device *dev;
+ char vendor[128], product[128];
+- int fd;
+- int status;
+-
+- status=1;
+
+- if ((fd = open(path, O_RDWR)) == -1) {
+- fprintf(stderr, "cannot open %s, %s (%d)\n", path, strerror(errno), errno);
+- return(1);
+- }
+- if (usb_control_msg(fd, USB_DIR_IN, USB_REQ_GET_DESCRIPTOR, (USB_DT_DEVICE << 8), 0, USB_DT_DEVICE_SIZE, buf) < 0) {
+- if (open_mode == O_RDWR)
+- fprintf(stderr, "cannot get config descriptor, %s (%d)\n", strerror(errno), errno);
+- goto err;
++ dev = get_usb_device(path);
++ if (!dev) {
++ fprintf(stderr, "Cannot open %s\n", path);
++ return 1;
+ }
+- vid = buf[8] | (buf[9] << 8);
+- pid = buf[10] | (buf[11] << 8);
+- get_vendor_string(vendor, sizeof(vendor), vid);
+- get_product_string(product, sizeof(product), vid, pid);
+- printf("Device: ID %04x:%04x %s %s\n", vid, pid, vendor, product);
+- dumpdev(buf, fd, flags);
+- status=0;
+- err:
+- close(fd);
+- return(status);
++ get_vendor_string(vendor, sizeof(vendor), dev->descriptor.idVendor);
++ get_product_string(product, sizeof(product), dev->descriptor.idVendor, dev->descriptor.idProduct);
++ printf("Device: ID %04x:%04x %s %s\n", dev->descriptor.idVendor,
++ dev->descriptor.idProduct,
++ vendor,
++ product);
++ dumpdev(dev, flags);
++ return 0;
+ }
+
+-/* ---------------------------------------------------------------------- */
+-
+-static int list_devices(int bus, int devnum, int vendorid, int productid, unsigned int flags)
++static int list_devices(int busnum, int devnum, int vendorid, int productid, unsigned int flags)
+ {
+- DIR *d, *d2;
+- struct dirent *de, *de2;
+- unsigned char buf[256];
+- unsigned int vid, pid;
++ struct usb_bus *bus;
++ struct usb_device *dev;
+ char vendor[128], product[128];
+- int fd;
+ int status;
+
+ status=1; /* 1 device not found, 0 device found */
+-
+- d = opendir(procbususb);
+- if (!d) {
+- fprintf(stderr, "cannot open %s, %s (%d)\n", procbususb, strerror(errno), errno);
+- return(1);
+- }
+- while ((de = readdir(d))) {
+- if (de->d_name[0] < '0' || de->d_name[0] > '9')
+- continue;
+- if (bus != -1 && strtoul(de->d_name, NULL, 0) != bus)
++
++ for (bus = usb_busses; bus; bus = bus->next) {
++ if (busnum != -1 && strtoul(bus->dirname, NULL, 0) != busnum)
+ continue;
+- snprintf(buf, sizeof(buf), "%s/%s/", procbususb, de->d_name);
+- if (!(d2 = opendir(buf)))
+- continue;
+- while ((de2 = readdir(d2))) {
+- if (de2->d_name[0] == '.')
+- continue;
+- if (devnum != -1 && strtoul(de2->d_name, NULL, 0) != devnum)
++ for (dev = bus->devices; dev; dev = dev->next) {
++ if (devnum != -1 && strtoul(dev->filename, NULL, 0) != devnum)
+ continue;
+- snprintf(buf, sizeof(buf), "%s/%s/%s", procbususb, de->d_name, de2->d_name);
+- if ((fd = open(buf, open_mode)) == -1) {
+- fprintf(stderr, "cannot open %s, %s (%d)\n", buf, strerror(errno), errno);
++ if ((vendorid != -1 && vendorid != dev->descriptor.idVendor) || (productid != -1 && productid != dev->descriptor.idProduct))
+ continue;
+- }
+- if (read(fd, buf, USB_DT_DEVICE_SIZE) != USB_DT_DEVICE_SIZE) {
+- fprintf(stderr, "cannot read device descriptor %s (%d)\n", strerror(errno), errno);
+- goto err;
+- }
+- vid = buf[8] | (buf[9] << 8);
+- pid = buf[10] | (buf[11] << 8);
+- if (buf[0] >= USB_DT_DEVICE_SIZE && ((vendorid != -1 && vendorid != vid) || (productid != -1 && productid != pid)))
+- goto err;
+- status=0;
+- get_vendor_string(vendor, sizeof(vendor), vid);
+- get_product_string(product, sizeof(product), vid, pid);
++ status = 0;
++ get_vendor_string(vendor, sizeof(vendor), dev->descriptor.idVendor);
++ get_product_string(product, sizeof(product), dev->descriptor.idVendor, dev->descriptor.idProduct);
+ if (verblevel > 0)
+ printf("\n");
+- printf("Bus %s Device %s: ID %04x:%04x %s %s\n", de->d_name, de2->d_name, vid, pid, vendor, product);
++ printf("Bus %s Device %s: ID %04x:%04x %s %s\n", bus->dirname,
++ dev->filename,
++ dev->descriptor.idVendor,
++ dev->descriptor.idProduct,
++ vendor,
++ product);
+ if (verblevel > 0)
+- dumpdev(buf, fd, flags);
+- err:
+- close(fd);
+- }
+- closedir(d2);
+- }
+- closedir(d);
++ dumpdev(dev, flags);
++ }
++ }
+ return(status);
+ }
+
+@@ -1487,7 +1778,6 @@
+ { "verbose", 0, 0, 'v' },
+ { 0, 0, 0, 0 }
+ };
+- static const char usage[] = "usage: lsusb [-v] [-x] [-p <procpath>] [-s [<bus>:][<devnum>]] [-d [<vendor>]:[<device>]]\n";
+ int c, err = 0;
+ unsigned int allowctrlmsg = 0, treemode = 0;
+ int bus = -1, devnum = -1, vendor = -1, product = -1;
+@@ -1495,7 +1785,8 @@
+ char *cp;
+ int status;
+
+- while ((c = getopt_long(argc, argv, "D:vxtP:p:s:d:V", long_options, NULL)) != EOF) {
++ while ((c = getopt_long(argc, argv, "D:vxtP:p:s:d:V",
++ long_options, NULL)) != EOF) {
+ switch(c) {
+ case 'V':
+ printf("lsusb (" PACKAGE ") " VERSION "\n");
+@@ -1503,7 +1794,6 @@
+
+ case 'v':
+ verblevel++;
+- open_mode = O_RDWR;
+ break;
+
+ case 'x':
+@@ -1514,11 +1804,6 @@
+ treemode = 1;
+ break;
+
+- case 'P': /* lspci uses -P; but lsusb 0.9, -p */
+- case 'p':
+- procbususb = optarg;
+- break;
+-
+ case 's':
+ cp = strchr(optarg, ':');
+ if (cp) {
+@@ -1556,15 +1841,43 @@
+ }
+ }
+ if (err || argc > optind) {
+- fprintf(stderr, usage);
++ fprintf(stderr, "Usage: lsusb [options]...\n"
++ "List all USB devices\n"
++ "\n"
++ "OPTIONS\n"
++ " -v\n"
++ " Increase verbosity\n"
++ " -s [[bus]:][devnum]\n"
++ " Show only devices in specified bus and/or devnum\n"
++ " -d vendor:[product]\n"
++ " Show only devices with the specified vendor and product ID\n"
++ " -D device\n"
++ " Selects which device lsusb will examine\n"
++ " -t\n"
++ " Dump the physical USB device hierarchy as a tree\n"
++ " -V, --version\n"
++ " Show version of program\n"
++ "\n");
+ exit(1);
+ }
+- if ((err = names_init("./usb.ids")) != 0)
+- if(err = names_init(USBIDS_FILE)) {
+- printf("Error, cannot open USBIDS File \"%s\", %s\n", USBIDS_FILE, strerror(err));
+- exit(1);
++
++ /* by default, print names as well as numbers */
++ if ((err = names_init("./usb.ids")) != 0) {
++ if ((err = names_init(USBIDS_FILE)) != 0) {
++ fprintf(stderr, "%s: cannot open \"%s\", %s\n",
++ argv[0],
++ USBIDS_FILE,
++ strerror(err));
++ }
+ }
+ status = 0;
++
++ usb_init();
++
++ usb_find_busses();
++ usb_find_devices();
++
++
+ if (treemode) {
+ /* treemode requires at least verblevel 1 */
+ verblevel += 1 - VERBLEVEL_DEFAULT;
+--- usbutils-0.11/names.c~usbutils-0.11+cvs20041108
++++ usbutils-0.11/names.c
+@@ -116,6 +116,7 @@
+ static struct genericstrtable *physdess[HASHSZ] = { NULL, };
+ static struct genericstrtable *hutus[HASHSZ] = { NULL, };
+ static struct genericstrtable *langids[HASHSZ] = { NULL, };
++static struct genericstrtable *countrycodes[HASHSZ] = { NULL, };
+
+ /* ---------------------------------------------------------------------- */
+
+@@ -164,6 +165,11 @@
+ return names_genericstrtable(biass, b);
+ }
+
++const char *names_countrycode(unsigned int countrycode)
++{
++ return names_genericstrtable(countrycodes, countrycode);
++}
++
+ const char *names_vendor(u_int16_t vendorid)
+ {
+ struct vendor *v;
+@@ -402,6 +408,11 @@
+ return new_genericstrtable(biass, name, b);
+ }
+
++static int new_countrycode(const char *name, unsigned int countrycode)
++{
++ return new_genericstrtable(countrycodes, name, countrycode);
++}
++
+ /* ---------------------------------------------------------------------- */
+
+ #define DBG(x)
+@@ -553,6 +564,27 @@
+ DBG(printf("line %5u audio terminal type %02x %s\n", linectr, u, cp));
+ continue;
+ }
++ if (buf[0] == 'H' && buf[1] == 'C' && buf[2] == 'C' && isspace(buf[3])) {
++ /* HID Descriptor bCountryCode */
++ cp = buf+3;
++ while (isspace(*cp))
++ cp++;
++ if (!isxdigit(*cp)) {
++ fprintf(stderr, "Invalid HID country code at line %u\n", linectr);
++ continue;
++ }
++ u = strtoul(cp, &cp, 10);
++ while (isspace(*cp))
++ cp++;
++ if (!*cp) {
++ fprintf(stderr, "Invalid HID country code at line %u\n", linectr);
++ continue;
++ }
++ if (new_countrycode(cp, u))
++ fprintf(stderr, "Duplicate HID country code at line %u country %02u %s\n", linectr, u, cp);
++ DBG(printf("line %5u keyboard country code %02u %s\n", linectr, u, cp));
++ continue;
++ }
+ if (isxdigit(*cp)) {
+ /* vendor */
+ u = strtoul(cp, &cp, 16);
+--- usbutils-0.11/names.h~usbutils-0.11+cvs20041108
++++ usbutils-0.11/names.h
+@@ -44,6 +44,7 @@
+ extern const char *names_langid(u_int16_t langid);
+ extern const char *names_physdes(u_int8_t ph);
+ extern const char *names_bias(u_int8_t b);
++extern const char *names_countrycode(unsigned int countrycode);
+ extern int names_init(char *n);
+
+ /* ---------------------------------------------------------------------- */
+--- usbutils-0.11/usb.h
++++ /dev/null
+@@ -1,768 +0,0 @@
+-#ifndef __LINUX_USB_H
+-#define __LINUX_USB_H
+-
+-/* USB constants */
+-
+-/*
+- * Device and/or Interface Class codes
+- */
+-#define USB_CLASS_PER_INTERFACE 0 /* for DeviceClass */
+-#define USB_CLASS_AUDIO 1
+-#define USB_CLASS_COMM 2
+-#define USB_CLASS_HID 3
+-#define USB_CLASS_PRINTER 7
+-#define USB_CLASS_MASS_STORAGE 8
+-#define USB_CLASS_HUB 9
+-#define USB_CLASS_DATA 10
+-#define USB_CLASS_VENDOR_SPEC 0xff
+-
+-/*
+- * USB types
+- */
+-#define USB_TYPE_STANDARD (0x00 << 5)
+-#define USB_TYPE_CLASS (0x01 << 5)
+-#define USB_TYPE_VENDOR (0x02 << 5)
+-#define USB_TYPE_RESERVED (0x03 << 5)
+-
+-/*
+- * USB recipients
+- */
+-#define USB_RECIP_DEVICE 0x00
+-#define USB_RECIP_INTERFACE 0x01
+-#define USB_RECIP_ENDPOINT 0x02
+-#define USB_RECIP_OTHER 0x03
+-
+-/*
+- * USB directions
+- */
+-#define USB_DIR_OUT 0
+-#define USB_DIR_IN 0x80
+-
+-/*
+- * Descriptor types
+- */
+-#define USB_DT_DEVICE 0x01
+-#define USB_DT_CONFIG 0x02
+-#define USB_DT_STRING 0x03
+-#define USB_DT_INTERFACE 0x04
+-#define USB_DT_ENDPOINT 0x05
+-
+-#define USB_DT_HID (USB_TYPE_CLASS | 0x01)
+-#define USB_DT_REPORT (USB_TYPE_CLASS | 0x02)
+-#define USB_DT_PHYSICAL (USB_TYPE_CLASS | 0x03)
+-#define USB_DT_HUB (USB_TYPE_CLASS | 0x09)
+-
+-/*
+- * Descriptor sizes per descriptor type
+- */
+-#define USB_DT_DEVICE_SIZE 18
+-#define USB_DT_CONFIG_SIZE 9
+-#define USB_DT_INTERFACE_SIZE 9
+-#define USB_DT_ENDPOINT_SIZE 7
+-#define USB_DT_ENDPOINT_AUDIO_SIZE 9 /* Audio extension */
+-#define USB_DT_HUB_NONVAR_SIZE 7
+-#define USB_DT_HID_SIZE 9
+-
+-/*
+- * Endpoints
+- */
+-#define USB_ENDPOINT_NUMBER_MASK 0x0f /* in bEndpointAddress */
+-#define USB_ENDPOINT_DIR_MASK 0x80
+-
+-#define USB_ENDPOINT_XFERTYPE_MASK 0x03 /* in bmAttributes */
+-#define USB_ENDPOINT_XFER_CONTROL 0
+-#define USB_ENDPOINT_XFER_ISOC 1
+-#define USB_ENDPOINT_XFER_BULK 2
+-#define USB_ENDPOINT_XFER_INT 3
+-
+-/*
+- * USB Packet IDs (PIDs)
+- */
+-#define USB_PID_UNDEF_0 0xf0
+-#define USB_PID_OUT 0xe1
+-#define USB_PID_ACK 0xd2
+-#define USB_PID_DATA0 0xc3
+-#define USB_PID_PING 0xb4 /* USB 2.0 */
+-#define USB_PID_SOF 0xa5
+-#define USB_PID_NYET 0x96 /* USB 2.0 */
+-#define USB_PID_DATA2 0x87 /* USB 2.0 */
+-#define USB_PID_SPLIT 0x78 /* USB 2.0 */
+-#define USB_PID_IN 0x69
+-#define USB_PID_NAK 0x5a
+-#define USB_PID_DATA1 0x4b
+-#define USB_PID_PREAMBLE 0x3c /* Token mode */
+-#define USB_PID_ERR 0x3c /* USB 2.0: handshake mode */
+-#define USB_PID_SETUP 0x2d
+-#define USB_PID_STALL 0x1e
+-#define USB_PID_MDATA 0x0f /* USB 2.0 */
+-
+-/*
+- * Standard requests
+- */
+-#define USB_REQ_GET_STATUS 0x00
+-#define USB_REQ_CLEAR_FEATURE 0x01
+-#define USB_REQ_SET_FEATURE 0x03
+-#define USB_REQ_SET_ADDRESS 0x05
+-#define USB_REQ_GET_DESCRIPTOR 0x06
+-#define USB_REQ_SET_DESCRIPTOR 0x07
+-#define USB_REQ_GET_CONFIGURATION 0x08
+-#define USB_REQ_SET_CONFIGURATION 0x09
+-#define USB_REQ_GET_INTERFACE 0x0A
+-#define USB_REQ_SET_INTERFACE 0x0B
+-#define USB_REQ_SYNCH_FRAME 0x0C
+-
+-/*
+- * HID requests
+- */
+-#define USB_REQ_GET_REPORT 0x01
+-#define USB_REQ_GET_IDLE 0x02
+-#define USB_REQ_GET_PROTOCOL 0x03
+-#define USB_REQ_SET_REPORT 0x09
+-#define USB_REQ_SET_IDLE 0x0A
+-#define USB_REQ_SET_PROTOCOL 0x0B
+-
+-
+-#ifdef __KERNEL__
+-
+-#include <linux/types.h>
+-#include <linux/ioctl.h>
+-#include <linux/version.h>
+-#include <linux/sched.h>
+-#include <linux/delay.h>
+-#include <linux/interrupt.h> /* for in_interrupt() */
+-#include <linux/config.h>
+-#include <linux/list.h>
+-
+-#define USB_MAJOR 180
+-
+-static __inline__ void wait_ms(unsigned int ms)
+-{
+- if(!in_interrupt()) {
+- current->state = TASK_UNINTERRUPTIBLE;
+- schedule_timeout(1 + ms * HZ / 1000);
+- }
+- else
+- mdelay(ms);
+-}
+-
+-typedef struct {
+- __u8 requesttype;
+- __u8 request;
+- __u16 value;
+- __u16 index;
+- __u16 length;
+-} devrequest __attribute__ ((packed));
+-
+-/*
+- * USB-status codes:
+- * USB_ST* maps to -E* and should go away in the future
+- */
+-
+-#define USB_ST_NOERROR 0
+-#define USB_ST_CRC (-EILSEQ)
+-#define USB_ST_BITSTUFF (-EPROTO)
+-#define USB_ST_NORESPONSE (-ETIMEDOUT) /* device not responding/handshaking */
+-#define USB_ST_DATAOVERRUN (-EOVERFLOW)
+-#define USB_ST_DATAUNDERRUN (-EREMOTEIO)
+-#define USB_ST_BUFFEROVERRUN (-ECOMM)
+-#define USB_ST_BUFFERUNDERRUN (-ENOSR)
+-#define USB_ST_INTERNALERROR (-EPROTO) /* unknown error */
+-#define USB_ST_SHORT_PACKET (-EREMOTEIO)
+-#define USB_ST_PARTIAL_ERROR (-EXDEV) /* ISO transfer only partially completed */
+-#define USB_ST_URB_KILLED (-ENOENT) /* URB canceled by user */
+-#define USB_ST_URB_PENDING (-EINPROGRESS)
+-#define USB_ST_REMOVED (-ENODEV) /* device not existing or removed */
+-#define USB_ST_TIMEOUT (-ETIMEDOUT) /* communication timed out, also in urb->status**/
+-#define USB_ST_NOTSUPPORTED (-ENOSYS)
+-#define USB_ST_BANDWIDTH_ERROR (-ENOSPC) /* too much bandwidth used */
+-#define USB_ST_URB_INVALID_ERROR (-EINVAL) /* invalid value/transfer type */
+-#define USB_ST_URB_REQUEST_ERROR (-ENXIO) /* invalid endpoint */
+-#define USB_ST_STALL (-EPIPE) /* pipe stalled, also in urb->status*/
+-
+-/*
+- * USB device number allocation bitmap. There's one bitmap
+- * per USB tree.
+- */
+-struct usb_devmap {
+- unsigned long devicemap[128 / (8*sizeof(unsigned long))];
+-};
+-
+-#define USB_MAXBUS 64
+-
+-struct usb_busmap {
+- unsigned long busmap[USB_MAXBUS / (8*sizeof(unsigned long))];
+-};
+-
+-/*
+- * This is a USB device descriptor.
+- *
+- * USB device information
+- */
+-
+-/* Everything but the endpoint maximums are aribtrary */
+-#define USB_MAXCONFIG 8
+-#define USB_ALTSETTINGALLOC 4
+-#define USB_MAXALTSETTING 128 /* Hard limit */
+-#define USB_MAXINTERFACES 32
+-#define USB_MAXENDPOINTS 32
+-
+-/* All standard descriptors have these 2 fields in common */
+-struct usb_descriptor_header {
+- __u8 bLength;
+- __u8 bDescriptorType;
+-} __attribute__ ((packed));
+-
+-/* Device descriptor */
+-struct usb_device_descriptor {
+- __u8 bLength;
+- __u8 bDescriptorType;
+- __u16 bcdUSB;
+- __u8 bDeviceClass;
+- __u8 bDeviceSubClass;
+- __u8 bDeviceProtocol;
+- __u8 bMaxPacketSize0;
+- __u16 idVendor;
+- __u16 idProduct;
+- __u16 bcdDevice;
+- __u8 iManufacturer;
+- __u8 iProduct;
+- __u8 iSerialNumber;
+- __u8 bNumConfigurations;
+-} __attribute__ ((packed));
+-
+-/* Endpoint descriptor */
+-struct usb_endpoint_descriptor {
+- __u8 bLength;
+- __u8 bDescriptorType;
+- __u8 bEndpointAddress;
+- __u8 bmAttributes;
+- __u16 wMaxPacketSize;
+- __u8 bInterval;
+- __u8 bRefresh;
+- __u8 bSynchAddress;
+-
+- unsigned char *extra; /* Extra descriptors */
+- int extralen;
+-} __attribute__ ((packed));
+-
+-/* Interface descriptor */
+-struct usb_interface_descriptor {
+- __u8 bLength;
+- __u8 bDescriptorType;
+- __u8 bInterfaceNumber;
+- __u8 bAlternateSetting;
+- __u8 bNumEndpoints;
+- __u8 bInterfaceClass;
+- __u8 bInterfaceSubClass;
+- __u8 bInterfaceProtocol;
+- __u8 iInterface;
+-
+- struct usb_endpoint_descriptor *endpoint;
+-
+- unsigned char *extra;
+- int extralen;
+-} __attribute__ ((packed));
+-
+-struct usb_interface {
+- struct usb_interface_descriptor *altsetting;
+-
+- int act_altsetting; /* active alternate setting */
+- int num_altsetting; /* number of alternate settings */
+- int max_altsetting; /* total memory allocated */
+-
+- struct usb_driver *driver; /* driver */
+- void *private_data;
+-};
+-
+-/* Configuration descriptor information.. */
+-struct usb_config_descriptor {
+- __u8 bLength;
+- __u8 bDescriptorType;
+- __u16 wTotalLength;
+- __u8 bNumInterfaces;
+- __u8 bConfigurationValue;
+- __u8 iConfiguration;
+- __u8 bmAttributes;
+- __u8 MaxPower;
+-
+- struct usb_interface *interface;
+-} __attribute__ ((packed));
+-
+-/* String descriptor */
+-struct usb_string_descriptor {
+- __u8 bLength;
+- __u8 bDescriptorType;
+- __u16 wData[1];
+-} __attribute__ ((packed));
+-
+-struct usb_device;
+-
+-struct usb_driver {
+- const char *name;
+-
+- void * (*probe)(struct usb_device *, unsigned int);
+- void (*disconnect)(struct usb_device *, void *);
+-
+- struct list_head driver_list;
+-
+- struct file_operations *fops;
+- int minor;
+-};
+-
+-/*
+- * Pointer to a device endpoint interrupt function -greg
+- * Parameters:
+- * int status - This needs to be defined. Right now each HCD
+- * passes different transfer status bits back. Don't use it
+- * until we come up with a common meaning.
+- * void *buffer - This is a pointer to the data used in this
+- * USB transfer.
+- * int length - This is the number of bytes transferred in or out
+- * of the buffer by this transfer. (-1 = unknown/unsupported)
+- * void *dev_id - This is a user defined pointer set when the IRQ
+- * is requested that is passed back.
+- *
+- * Special Cases:
+- * if (status == USB_ST_REMOVED), don't trust buffer or len.
+- */
+-typedef int (*usb_device_irq)(int, void *, int, void *);
+-
+-/*----------------------------------------------------------------------------*
+- * New USB Structures *
+- *----------------------------------------------------------------------------*/
+-
+-#define USB_DISABLE_SPD 0x0001
+-#define USB_ISO_ASAP 0x0002
+-#define USB_URB_EARLY_COMPLETE 0x0004
+-#define USB_ASYNC_UNLINK 0x0008
+-#define USB_QUEUE_BULK 0x0010
+-#define USB_TIMEOUT_KILLED 0x1000 // only set by HCD!
+-
+-typedef struct
+-{
+- unsigned int offset;
+- unsigned int length; // expected length
+- unsigned int actual_length;
+- unsigned int status;
+-} iso_packet_descriptor_t, *piso_packet_descriptor_t;
+-
+-struct urb;
+-typedef void (*usb_complete_t)(struct urb *);
+-
+-typedef struct urb
+-{
+- spinlock_t lock; // lock for the URB
+- void *hcpriv; // private data for host controller
+- struct list_head urb_list; // list pointer to all active urbs
+- struct urb *next; // pointer to next URB
+- struct usb_device *dev; // pointer to associated USB device
+- unsigned int pipe; // pipe information
+- int status; // returned status
+- unsigned int transfer_flags; // USB_DISABLE_SPD | USB_ISO_ASAP | USB_URB_EARLY_COMPLETE
+- void *transfer_buffer; // associated data buffer
+- int transfer_buffer_length; // data buffer length
+- int actual_length; // actual data buffer length
+- unsigned char *setup_packet; // setup packet (control only)
+- //
+- int start_frame; // start frame (iso/irq only)
+- int number_of_packets; // number of packets in this request (iso/irq only)
+- int interval; // polling interval (irq only)
+- int error_count; // number of errors in this transfer (iso only)
+- int timeout; // timeout (in jiffies)
+- //
+- void *context; // context for completion routine
+- usb_complete_t complete; // pointer to completion routine
+- //
+- iso_packet_descriptor_t iso_frame_desc[0];
+-} urb_t, *purb_t;
+-
+-#define FILL_CONTROL_URB(a,aa,b,c,d,e,f,g) \
+- do {\
+- spin_lock_init(&(a)->lock);\
+- (a)->dev=aa;\
+- (a)->pipe=b;\
+- (a)->setup_packet=c;\
+- (a)->transfer_buffer=d;\
+- (a)->transfer_buffer_length=e;\
+- (a)->complete=f;\
+- (a)->context=g;\
+- } while (0)
+-
+-#define FILL_BULK_URB(a,aa,b,c,d,e,f) \
+- do {\
+- spin_lock_init(&(a)->lock);\
+- (a)->dev=aa;\
+- (a)->pipe=b;\
+- (a)->transfer_buffer=c;\
+- (a)->transfer_buffer_length=d;\
+- (a)->complete=e;\
+- (a)->context=f;\
+- } while (0)
+-
+-#define FILL_INT_URB(a,aa,b,c,d,e,f,g) \
+- do {\
+- spin_lock_init(&(a)->lock);\
+- (a)->dev=aa;\
+- (a)->pipe=b;\
+- (a)->transfer_buffer=c;\
+- (a)->transfer_buffer_length=d;\
+- (a)->complete=e;\
+- (a)->context=f;\
+- (a)->interval=g;\
+- (a)->start_frame=-1;\
+- } while (0)
+-
+-#define FILL_CONTROL_URB_TO(a,aa,b,c,d,e,f,g,h) \
+- do {\
+- spin_lock_init(&(a)->lock);\
+- (a)->dev=aa;\
+- (a)->pipe=b;\
+- (a)->setup_packet=c;\
+- (a)->transfer_buffer=d;\
+- (a)->transfer_buffer_length=e;\
+- (a)->complete=f;\
+- (a)->context=g;\
+- (a)->timeout=h;\
+- } while (0)
+-
+-#define FILL_BULK_URB_TO(a,aa,b,c,d,e,f,g) \
+- do {\
+- spin_lock_init(&(a)->lock);\
+- (a)->dev=aa;\
+- (a)->pipe=b;\
+- (a)->transfer_buffer=c;\
+- (a)->transfer_buffer_length=d;\
+- (a)->complete=e;\
+- (a)->context=f;\
+- (a)->timeout=g;\
+- } while (0)
+-
+-purb_t usb_alloc_urb(int iso_packets);
+-void usb_free_urb (purb_t purb);
+-int usb_submit_urb(purb_t purb);
+-int usb_unlink_urb(purb_t purb);
+-int usb_internal_control_msg(struct usb_device *usb_dev, unsigned int pipe, devrequest *cmd, void *data, int len, int timeout);
+-int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, void *data, int len, int *actual_length, int timeout);
+-
+-/*-------------------------------------------------------------------*
+- * COMPATIBILITY STUFF *
+- *-------------------------------------------------------------------*/
+-typedef struct
+-{
+- wait_queue_head_t *wakeup;
+-
+- usb_device_irq handler;
+- void* stuff;
+- /* more to follow */
+-} api_wrapper_data;
+-
+-struct irq_wrapper_data {
+- void *context;
+- usb_device_irq handler;
+-};
+-
+-/* -------------------------------------------------------------------------- */
+-
+-struct usb_operations {
+- int (*allocate)(struct usb_device *);
+- int (*deallocate)(struct usb_device *);
+- int (*get_frame_number) (struct usb_device *usb_dev);
+- int (*submit_urb) (struct urb* purb);
+- int (*unlink_urb) (struct urb* purb);
+-};
+-
+-/*
+- * Allocated per bus we have
+- */
+-struct usb_bus {
+- int busnum; /* Bus number (in order of reg) */
+-
+- struct usb_devmap devmap; /* Device map */
+- struct usb_operations *op; /* Operations (specific to the HC) */
+- struct usb_device *root_hub; /* Root hub */
+- struct list_head bus_list;
+- void *hcpriv; /* Host Controller private data */
+-
+- unsigned int bandwidth_allocated; /* on this Host Controller; */
+- /* applies to Int. and Isoc. pipes; */
+- /* measured in microseconds/frame; */
+- /* range is 0..900, where 900 = */
+- /* 90% of a 1-millisecond frame */
+- int bandwidth_int_reqs; /* number of Interrupt requesters */
+- int bandwidth_isoc_reqs; /* number of Isoc. requesters */
+-
+- /* usbdevfs inode list */
+- struct list_head inodes;
+-};
+-
+-#define USB_MAXCHILDREN (8) /* This is arbitrary */
+-
+-struct usb_device {
+- int devnum; /* Device number on USB bus */
+- int slow; /* Slow device? */
+-
+- atomic_t refcnt; /* Reference count */
+-
+- unsigned int toggle[2]; /* one bit for each endpoint ([0] = IN, [1] = OUT) */
+- unsigned int halted[2]; /* endpoint halts; one bit per endpoint # & direction; */
+- /* [0] = IN, [1] = OUT */
+- struct usb_config_descriptor *actconfig;/* the active configuration */
+- int epmaxpacketin[16]; /* INput endpoint specific maximums */
+- int epmaxpacketout[16]; /* OUTput endpoint specific maximums */
+-
+- struct usb_device *parent;
+- struct usb_bus *bus; /* Bus we're part of */
+-
+- struct usb_device_descriptor descriptor;/* Descriptor */
+- struct usb_config_descriptor *config; /* All of the configs */
+-
+- int have_langid; /* whether string_langid is valid yet */
+- int string_langid; /* language ID for strings */
+-
+- void *hcpriv; /* Host Controller private data */
+-
+- /* usbdevfs inode list */
+- struct list_head inodes;
+- struct list_head filelist;
+-
+- /*
+- * Child devices - these can be either new devices
+- * (if this is a hub device), or different instances
+- * of this same device.
+- *
+- * Each instance needs its own set of data structures.
+- */
+-
+- int maxchild; /* Number of ports if hub */
+- struct usb_device *children[USB_MAXCHILDREN];
+-};
+-
+-extern int usb_register(struct usb_driver *);
+-extern void usb_deregister(struct usb_driver *);
+-
+-/* used these for multi-interface device registration */
+-extern void usb_driver_claim_interface(struct usb_driver *driver, struct usb_interface *iface, void* priv);
+-extern int usb_interface_claimed(struct usb_interface *iface);
+-extern void usb_driver_release_interface(struct usb_driver *driver, struct usb_interface *iface);
+-
+-extern struct usb_bus *usb_alloc_bus(struct usb_operations *);
+-extern void usb_free_bus(struct usb_bus *);
+-extern void usb_register_bus(struct usb_bus *);
+-extern void usb_deregister_bus(struct usb_bus *);
+-
+-extern struct usb_device *usb_alloc_dev(struct usb_device *parent, struct usb_bus *);
+-extern void usb_free_dev(struct usb_device *);
+-extern void usb_inc_dev_use(struct usb_device *);
+-#define usb_dec_dev_use usb_free_dev
+-extern void usb_release_bandwidth(struct usb_device *, int);
+-
+-extern int usb_control_msg(struct usb_device *dev, unsigned int pipe, __u8 request, __u8 requesttype, __u16 value, __u16 index, void *data, __u16 size, int timeout);
+-
+-extern void usb_init_root_hub(struct usb_device *dev);
+-extern int usb_root_hub_string(int id, int serial, char *type, __u8 *data, int len);
+-extern void usb_connect(struct usb_device *dev);
+-extern void usb_disconnect(struct usb_device **);
+-
+-extern void usb_destroy_configuration(struct usb_device *dev);
+-
+-int usb_get_current_frame_number (struct usb_device *usb_dev);
+-
+-/*
+- * Calling this entity a "pipe" is glorifying it. A USB pipe
+- * is something embarrassingly simple: it basically consists
+- * of the following information:
+- * - device number (7 bits)
+- * - endpoint number (4 bits)
+- * - current Data0/1 state (1 bit)
+- * - direction (1 bit)
+- * - speed (1 bit)
+- * - max packet size (2 bits: 8, 16, 32 or 64) [Historical; now gone.]
+- * - pipe type (2 bits: control, interrupt, bulk, isochronous)
+- *
+- * That's 18 bits. Really. Nothing more. And the USB people have
+- * documented these eighteen bits as some kind of glorious
+- * virtual data structure.
+- *
+- * Let's not fall in that trap. We'll just encode it as a simple
+- * unsigned int. The encoding is:
+- *
+- * - max size: bits 0-1 (00 = 8, 01 = 16, 10 = 32, 11 = 64) [Historical; now gone.]
+- * - direction: bit 7 (0 = Host-to-Device [Out], 1 = Device-to-Host [In])
+- * - device: bits 8-14
+- * - endpoint: bits 15-18
+- * - Data0/1: bit 19
+- * - speed: bit 26 (0 = Full, 1 = Low Speed)
+- * - pipe type: bits 30-31 (00 = isochronous, 01 = interrupt, 10 = control, 11 = bulk)
+- *
+- * Why? Because it's arbitrary, and whatever encoding we select is really
+- * up to us. This one happens to share a lot of bit positions with the UHCI
+- * specification, so that much of the uhci driver can just mask the bits
+- * appropriately.
+- */
+-
+-#define PIPE_ISOCHRONOUS 0
+-#define PIPE_INTERRUPT 1
+-#define PIPE_CONTROL 2
+-#define PIPE_BULK 3
+-
+-#define usb_maxpacket(dev, pipe, out) (out \
+- ? (dev)->epmaxpacketout[usb_pipeendpoint(pipe)] \
+- : (dev)->epmaxpacketin [usb_pipeendpoint(pipe)] )
+-#define usb_packetid(pipe) (((pipe) & USB_DIR_IN) ? USB_PID_IN : USB_PID_OUT)
+-
+-#define usb_pipeout(pipe) ((((pipe) >> 7) & 1) ^ 1)
+-#define usb_pipein(pipe) (((pipe) >> 7) & 1)
+-#define usb_pipedevice(pipe) (((pipe) >> 8) & 0x7f)
+-#define usb_pipe_endpdev(pipe) (((pipe) >> 8) & 0x7ff)
+-#define usb_pipeendpoint(pipe) (((pipe) >> 15) & 0xf)
+-#define usb_pipedata(pipe) (((pipe) >> 19) & 1)
+-#define usb_pipeslow(pipe) (((pipe) >> 26) & 1)
+-#define usb_pipetype(pipe) (((pipe) >> 30) & 3)
+-#define usb_pipeisoc(pipe) (usb_pipetype((pipe)) == PIPE_ISOCHRONOUS)
+-#define usb_pipeint(pipe) (usb_pipetype((pipe)) == PIPE_INTERRUPT)
+-#define usb_pipecontrol(pipe) (usb_pipetype((pipe)) == PIPE_CONTROL)
+-#define usb_pipebulk(pipe) (usb_pipetype((pipe)) == PIPE_BULK)
+-
+-#define PIPE_DEVEP_MASK 0x0007ff00
+-
+-/* The D0/D1 toggle bits */
+-#define usb_gettoggle(dev, ep, out) (((dev)->toggle[out] >> ep) & 1)
+-#define usb_dotoggle(dev, ep, out) ((dev)->toggle[out] ^= (1 << ep))
+-#define usb_settoggle(dev, ep, out, bit) ((dev)->toggle[out] = ((dev)->toggle[out] & ~(1 << ep)) | ((bit) << ep))
+-
+-/* Endpoint halt control/status */
+-#define usb_endpoint_out(ep_dir) (((ep_dir >> 7) & 1) ^ 1)
+-#define usb_endpoint_halt(dev, ep, out) ((dev)->halted[out] |= (1 << (ep)))
+-#define usb_endpoint_running(dev, ep, out) ((dev)->halted[out] &= ~(1 << (ep)))
+-#define usb_endpoint_halted(dev, ep, out) ((dev)->halted[out] & (1 << (ep)))
+-
+-static inline unsigned int __create_pipe(struct usb_device *dev, unsigned int endpoint)
+-{
+- return (dev->devnum << 8) | (endpoint << 15) | (dev->slow << 26);
+-}
+-
+-static inline unsigned int __default_pipe(struct usb_device *dev)
+-{
+- return (dev->slow << 26);
+-}
+-
+-/* Create various pipes... */
+-#define usb_sndctrlpipe(dev,endpoint) ((PIPE_CONTROL << 30) | __create_pipe(dev,endpoint))
+-#define usb_rcvctrlpipe(dev,endpoint) ((PIPE_CONTROL << 30) | __create_pipe(dev,endpoint) | USB_DIR_IN)
+-#define usb_sndisocpipe(dev,endpoint) ((PIPE_ISOCHRONOUS << 30) | __create_pipe(dev,endpoint))
+-#define usb_rcvisocpipe(dev,endpoint) ((PIPE_ISOCHRONOUS << 30) | __create_pipe(dev,endpoint) | USB_DIR_IN)
+-#define usb_sndbulkpipe(dev,endpoint) ((PIPE_BULK << 30) | __create_pipe(dev,endpoint))
+-#define usb_rcvbulkpipe(dev,endpoint) ((PIPE_BULK << 30) | __create_pipe(dev,endpoint) | USB_DIR_IN)
+-#define usb_sndintpipe(dev,endpoint) ((PIPE_INTERRUPT << 30) | __create_pipe(dev,endpoint))
+-#define usb_rcvintpipe(dev,endpoint) ((PIPE_INTERRUPT << 30) | __create_pipe(dev,endpoint) | USB_DIR_IN)
+-#define usb_snddefctrl(dev) ((PIPE_CONTROL << 30) | __default_pipe(dev))
+-#define usb_rcvdefctrl(dev) ((PIPE_CONTROL << 30) | __default_pipe(dev) | USB_DIR_IN)
+-
+-/*
+- * Send and receive control messages..
+- */
+-int usb_new_device(struct usb_device *dev);
+-int usb_reset_device(struct usb_device *dev);
+-int usb_set_address(struct usb_device *dev);
+-int usb_get_descriptor(struct usb_device *dev, unsigned char desctype,
+- unsigned char descindex, void *buf, int size);
+-int usb_get_class_descriptor(struct usb_device *dev, int ifnum, unsigned char desctype,
+- unsigned char descindex, void *buf, int size);
+-int usb_get_device_descriptor(struct usb_device *dev);
+-int __usb_get_extra_descriptor(char *buffer, unsigned size, unsigned char type, void **ptr);
+-int usb_get_status(struct usb_device *dev, int type, int target, void *data);
+-int usb_get_protocol(struct usb_device *dev, int ifnum);
+-int usb_set_protocol(struct usb_device *dev, int ifnum, int protocol);
+-int usb_set_interface(struct usb_device *dev, int ifnum, int alternate);
+-int usb_set_idle(struct usb_device *dev, int ifnum, int duration, int report_id);
+-int usb_set_configuration(struct usb_device *dev, int configuration);
+-int usb_get_report(struct usb_device *dev, int ifnum, unsigned char type,
+- unsigned char id, void *buf, int size);
+-int usb_set_report(struct usb_device *dev, int ifnum, unsigned char type,
+- unsigned char id, void *buf, int size);
+-int usb_string(struct usb_device *dev, int index, char *buf, size_t size);
+-int usb_clear_halt(struct usb_device *dev, int pipe);
+-
+-#define usb_get_extra_descriptor(ifpoint,type,ptr)\
+- __usb_get_extra_descriptor((ifpoint)->extra,(ifpoint)->extralen,type,(void**)ptr)
+-
+-/*
+- * Some USB bandwidth allocation constants.
+- */
+-#define BW_HOST_DELAY 1000L /* nanoseconds */
+-#define BW_HUB_LS_SETUP 333L /* nanoseconds */
+- /* 4 full-speed bit times (est.) */
+-
+-#define FRAME_TIME_BITS 12000L /* frame = 1 millisecond */
+-#define FRAME_TIME_MAX_BITS_ALLOC (90L * FRAME_TIME_BITS / 100L)
+-#define FRAME_TIME_USECS 1000L
+-#define FRAME_TIME_MAX_USECS_ALLOC (90L * FRAME_TIME_USECS / 100L)
+-
+-#define BitTime(bytecount) (7 * 8 * bytecount / 6) /* with integer truncation */
+- /* Trying not to use worst-case bit-stuffing
+- of (7/6 * 8 * bytecount) = 9.33 * bytecount */
+- /* bytecount = data payload byte count */
+-
+-#define NS_TO_US(ns) ((ns + 500L) / 1000L)
+- /* convert & round nanoseconds to microseconds */
+-
+-/*
+- * Debugging helpers..
+- */
+-void usb_show_device_descriptor(struct usb_device_descriptor *);
+-void usb_show_config_descriptor(struct usb_config_descriptor *);
+-void usb_show_interface_descriptor(struct usb_interface_descriptor *);
+-void usb_show_endpoint_descriptor(struct usb_endpoint_descriptor *);
+-void usb_show_device(struct usb_device *);
+-void usb_show_string(struct usb_device *dev, char *id, int index);
+-
+-#ifdef DEBUG
+-#define dbg(format, arg...) printk(KERN_DEBUG __FILE__ ": " format "\n" , ## arg)
+-#else
+-#define dbg(format, arg...) do {} while (0)
+-#endif
+-#define err(format, arg...) printk(KERN_ERR __FILE__ ": " format "\n" , ## arg)
+-#define info(format, arg...) printk(KERN_INFO __FILE__ ": " format "\n" , ## arg)
+-#define warn(format, arg...) printk(KERN_WARNING __FILE__ ": " format "\n" , ## arg)
+-
+-
+-/*
+- * bus and driver list
+- */
+-
+-extern struct list_head usb_driver_list;
+-extern struct list_head usb_bus_list;
+-
+-/*
+- * USB device fs stuff
+- */
+-
+-#ifdef CONFIG_USB_DEVICEFS
+-
+-/*
+- * these are expected to be called from the USB core/hub thread
+- * with the kernel lock held
+- */
+-extern void usbdevfs_add_bus(struct usb_bus *bus);
+-extern void usbdevfs_remove_bus(struct usb_bus *bus);
+-extern void usbdevfs_add_device(struct usb_device *dev);
+-extern void usbdevfs_remove_device(struct usb_device *dev);
+-
+-extern int usbdevfs_init(void);
+-extern void usbdevfs_cleanup(void);
+-
+-#else /* CONFIG_USB_DEVICEFS */
+-
+-extern inline void usbdevfs_add_bus(struct usb_bus *bus) {}
+-extern inline void usbdevfs_remove_bus(struct usb_bus *bus) {}
+-extern inline void usbdevfs_add_device(struct usb_device *dev) {}
+-extern inline void usbdevfs_remove_device(struct usb_device *dev) {}
+-
+-extern inline int usbdevfs_init(void) { return 0; }
+-extern inline void usbdevfs_cleanup(void) { }
+-
+-#endif /* CONFIG_USB_DEVICEFS */
+-
+-#endif /* __KERNEL__ */
+-
+-#endif
+--- usbutils-0.11/usb.ids~usbutils-0.11+cvs20041108
++++ usbutils-0.11/usb.ids
+@@ -3,8 +3,10 @@
+ #
+ # Maintained by Vojtech Pavlik <vojtech@suse.cz>
+ # If you have any new entries, send them to the maintainer.
++# The latest version can be obtained from
++# http://www.linux-usb.org/usb.ids
+ #
+-# $Id$
++# $Id$
+ #
+
+ # Vendors, devices and interfaces. Please keep sorted.
+@@ -14,22 +16,26 @@
+ # device device_name <-- single tab
+ # interface interface_name <-- two tabs
+
+-0000 Virtual
+- 0000 Hub
+ 0386 LTS
+ 0001 PSX for USB Converter
+-03e8 AOX Inc.
++03e8 EndPoints, Inc.
+ 0004 SE401 WebCam
+ 0008 101 Ethernet [klsi]
+ 03e9 Thesys Microelectronics
+ 03ea Data Broadcasting Corp.
+ 03eb Atmel Corp.
+ 3301 4-port Hub
+- 7603 AT76c503a D-Link DWL-120
+-03ec Iwatsu America Inc.
++ 3312 4-port Hub
++ 7603 AT76c503a D-Link DWL-120 802.11b Adapter
++ 7605 AT76c503a 802.11b Adapter
++ 7606 AT76c505 802.11b dapter
++03ec Iwatsu America, Inc.
+ 03ed Mitel Corp.
+ 03ee Mitsumi
+ 0000 CD-R/RW Drive
++ 641f WIF-0402C Bluetooth Adapter
++ 6440 WML-C52APR Bluetooth Adapter
++ 6901 SmartDisk FDD
+ 03f0 Hewlett-Packard
+ 0004 DeskJet 895c
+ 0101 ScanJet 4100c
+@@ -39,6 +45,8 @@
+ 0107 CD-Writer Plus
+ 010c Multimedia Keyboard Hub
+ 0111 G55xi Printer/Scanner/Copier
++ 011c hn210w 802.11b Adapter
++ 0121 HP49g+ Calculator
+ 0201 ScanJet 6200c
+ 0202 PhotoSmart S20
+ 0204 DeskJet 815c
+@@ -46,30 +54,80 @@
+ 0207 CD-Writer Plus 8200e
+ 020c Multimedia Keyboard
+ 0304 DeskJet 810c/812c
++ 0305 ScanJet 4300c
+ 0311 OfficeJet G85xi
+ 0317 LaserJet 1200
+ 0401 ScanJet 5200c
+ 0404 DeskJet 830c/832c
+ 0405 ScanJet 3400cse
+ 0504 DeskJet 885c
++ 0505 ScanJet 2100c
++ 0517 LaserJet 1000
+ 0601 ScanJet 6300c
+ 0604 DeskJet 840c
+ 0605 ScanJet 2200c
+ 0701 ScanJet 5300c/5370c
++ 0704 DeskJet 825c
+ 0705 ScanJet 4400c
++ 0712 DeskJet 1180c
++ 0801 ScanJet 7400c
+ 0804 DeskJet 816c
++ 0901 ScanJet 2300c
++ 0904 DeskJet 845c
+ 1004 DeskJet 970c/970cse
+- 1104 Deskjet 959C
++ 1005 ScanJet 5400c
++ 1016 Jornada 548 Pocket PC
++ 1104 DeskJet 959c
+ 1105 ScanJet 5470c
++ 1116 Jornada 568 Pocket PC
+ 1151 750xi Printer/Scanner/Copier
+ 1204 DeskJet 930c
++ 1305 ScanJet 4570c
++ 1317 LaserJet 1005
++ 1405 Scanjet 3670
++ 1504 DeskJet 920c
++ 1604 DeskJet 940c
++ 1904 DeskJet 3820
++ 1e11 PSC-950
+ 2004 DeskJet 640c
++ 2005 ScanJet 3570c
++ 2104 DeskJet 630c
++ 2205 ScanJet 3500c
++ 2304 DeskJet 656c
++ 2305 ScanJet 3970c
++ 2811 PSC-2100
+ 3102 PhotoSmart P1100 Printer w/ Card Reader
+- 4102 PhotoSmart 618 Camera
+- 6202 PhotoSmart 215 Camera
+- 6302 PhotoSmart 318/612 Camera
+- efbe NEC Picty900
++ 3104 DeskJet 960c
++ 3304 DeskJet 990c
++ 3404 DeskJet 6122
++ 3504 DeskJet 6127c
++ 3c02 PhotoSmart 7350
++ 3f11 PSC-1315
++ 4002 PhotoSmart 720 / PhotoSmart 935 (storage)
++ 4102 PhotoSmart 618
++ 4202 PhotoSmart 812
++ 4302 PhotoSmart 850 (ptp)
++ 4402 PhotoSmart 935 (ptp)
++ 5004 DeskJet 995c
++ 6004 DeskJet 5550
++ 6104 DeskJet 5650c
++ 6202 PhotoSmart 215
++ 6204 DeskJet 5150c
++ 6302 PhotoSmart 318/612
++ 6402 PhotoSmart 715 (ptp)
++ 6502 PhotoSmart 120 (ptp)
++ 6602 PhotoSmart 320
++ 6702 PhotoSmart 720 (ptp)
++ 6802 PhotoSmart 620 (ptp)
++ 6a02 PhotoSmart 735 (ptp)
++ 7004 DeskJet 3320c
++ 7104 DeskJet 3420c
++ 7202 PhotoSmart 43x (ptp)
++ 7204 DeskJet 36xx
++ 7304 DeskJet 35xx
++ a004 DeskJet 5850c
+ bef4 NEC Picty760
++ efbe NEC Picty900
+ f0be NEC Picty920
+ f1be NEC Picty800
+ 03f1 Genoa Technology
+@@ -79,32 +137,35 @@
+ 03f5 Siemens Electromechanical
+ 03f8 Epson Imaging Technology Center
+ 03f9 KeyTronic Corp.
+-03fb OPTi Inc.
++03fb OPTi, Inc.
+ 03fc Elitegroup Computer Systems
+-03fd Xilinx Inc.
++03fd Xilinx, Inc.
+ 03fe Farallon Comunications
+-0400 National Semiconductor
+- 1000 BearPaw 1200 Scanner
+- 1001 BearPaw 2400 Scanner
+-0401 National Registry Inc.
+-0402 Acer Labs Inc.
+-0403 Future Technology Devices
++0400 National Semiconductor Corp.
++ 1000 Mustek BearPaw 1200 Scanner
++ 1001 Mustek BearPaw 2400 Scanner
++0401 National Registry, Inc.
++0402 ALi Corp.
++0403 Future Technology Devices International, Ltd
+ 0000 H4SMK 7 Port Hub
+- 8070 7 Port Hub
++ 6001 8-bit FIFO
+ 8040 4 Port Hub
++ 8070 7 Port Hub
+ 8370 7 Port Hub
+ 8371 PS/2 Keyboard And Mouse
+ 8372 FT8U100AX Serial Port
++ F208 Papenmeier Braille-Display
+ 0404 NCR Corp.
+-0405 inSilicon
++0405 Synopsys, Inc.
+ 0406 Fujitsu-ICL Computers
+ 0407 Fujitsu Personal Systems, Inc.
+-0408 Quanta Computer Inc.
+-0409 NEC Systems
++0408 Quanta Computer, Inc.
++0409 NEC Corp.
+ 0012 ATerm IT75DSU ISDN TA
+ 0014 Japanese Keyboard
+ 0027 MultiSync Monitor
+- 0058 USB2.0 Hub Controller
++ 0058 HighSpeed Hub
++ 0059 HighSpeed Hub
+ 55aa Hub
+ 55ab Hub [iMac kbd]
+ efbe P!cty 900 [HP DJ]
+@@ -122,21 +183,45 @@
+ 0131 DC-5000
+ 0132 DC-3400
+ 0140 DC-4800
++ 0160 DC4800
++ 0170 DX3900
+ 0300 EZ-200
+ 0400 MC3
++ 0500 DX3500
++ 0510 DX3600
++ 0525 DX3215
++ 0530 DX3700
++ 0535 EasyShare CX4230 Camera
++ 0540 LS420
++ 0550 DX4900
++ 0555 DX4330
++ 0560 CX4200
++ 0565 CX4210
++ 0566 CX4300
++ 0568 LS443
++ 0569 LS663
++ 0570 DX6340
++ 0571 CX6330
++ 0572 DX6440
++ 0573 CX6230
++ 0574 CX6200
++ 0575 DX6490
++ 0576 DX4530
+ 040b Weltrend Semiconductor
+-040c VTech Computers Ltd.
++040c VTech Computers, Ltd
+ 040d VIA Technologies, Inc.
+ 040e MCCI
+ 040f Echo Speech Corp.
+-0411 Melco, Inc.
++0411 MelCo., Inc.
+ 0001 LUA-TX Ethernet [pegasus]
++ 0016 WLI-USB-S11 802.11b Adapter
++ 0027 WLI-USB-KS11G 802.11b Adapter
+ 0412 Award Software International
+-0413 Leadtek Research Inc.
+-0414 Giga-Byte Technology Co., Ltd.
++0413 Leadtek Research, Inc.
++0414 Giga-Byte Technology Co., Ltd
+ 0416 Winbond Electronics Corp.
+ 0961 AVL Flash Card Reader
+- 5518 Hub
++ 5518 4-Port Hub
+ 551a PC Sync Keypad
+ 551b PC Async Keypad
+ 551c Sync Tenkey
+@@ -144,36 +229,58 @@
+ 551e Keyboard
+ 551f Keyboard w/ Sys and Media
+ 5521 Keyboard
++ 7723 SD Card Reader
+ 6481 16-bit Scanner
+ 0417 Symbios Logic
+ 0418 AST Research
+-0419 Samsung Info. Systems America Inc.
+-041a Phoenix Technologies, Ltd.
++0419 Samsung Info. Systems America, Inc.
++ 3001 Xerox P1202 Laser Printer
++ 8002 SyncMaster 757DFX HID Device
++041a Phoenix Technologies, Ltd
+ 041b d'TV
+ 041d S3, Inc.
+-041e Creative Labs
++041e Creative Technology, Ltd
+ 1002 Nomad II MP3 Player
+ 1003 Blaster GamePad Cobra
+ 1050 GamePad Cobra
++ 3020 SoundBlaster Audigy 2 NX
+ 4003 VideoBlaster WebCam Go Plus [W9967CF]
+ 4004 Nomad II MG MP3 Player
+ 400a PC-Cam 300
+ 400b PC-Cam 600
+ 400c WebCam 5 [pwc]
++ 400d WebCam PD1001
++ 4011 WebCam PRO eX
++ 4013 PC-Cam 750
++ 4015 CardCam Value
++ 4017 WebCam Mobile
++ 4018 WebCam Vista
++ 401c Creative WebCam NX [PD1110]
++ 4100 Nomad Jukebox 2 MP3 player
++ 4101 Nomad Jukebox 3 MP3 player
++ 4106 Nomad MuVo MP3 Player
++ 4108 Nomad Jukebox Zen MP3 player
++ 410b Nomad Jukebox Zen USB 2.0 MP3 player
++ 4109 Nomad Jukebox Zen NX MP3 player
++ 4110 Nomad Jukebox Zen Xtra MP3 player
++ 4111 Dell Digital Jukebox
+ 041f LCS Telegraphics
+ 0420 Chips and Technologies
+ 0421 Nokia Mobile Phones
+-0422 ADI Systems Inc.
++ 0401 6650 GSM Phone
++ 0800 Connectivity Cable DKU-5
++0422 ADI Systems, Inc.
+ 0423 Computer Access Technology Corp.
+ 000a NetMate Ethernet
+ 000c NetMate2 Ethernet
+ 000d USB Chief Analyzer
+ 1237 Andromeda Hub
+ 0424 Standard Microsystems Corp.
+-0425 Motorola Semiconductors HK, Ltd.
+-0426 Integrated Device Technology
+-0427 Motorola Electronics Taiwan Ltd.
+-0428 Advanced Gravis Computer Tech. Ltd.
++ 223a 8-in-1 Card Reader
++0425 Motorola Semiconductors HK, Ltd
++0426 Integrated Device Technology, Inc.
++0427 Motorola Electronics Taiwan, Ltd
++0428 Advanced Gravis Computer Tech, Ltd
+ 4001 GamePad Pro
+ 0429 Cirrus Logic
+ 042a Ericsson Austrian, AG
+@@ -182,29 +289,38 @@
+ 042d Micronics
+ 042e Acer, Inc.
+ 042f Molex, Inc.
+-0430 Sun Microsystems
++0430 Sun Microsystems, Inc.
+ 0005 Type 6 Keyboard
+ 0100 3-button Mouse
+ 0431 Itac Systems, Inc.
+ 0432 Unisys Corp.
+-0433 Alps Electric Inc.
++0433 Alps Electric, Inc.
+ 1101 IBM Game Controller
+-0434 Samsung Info. Systems America Inc.
++0434 Samsung Info. Systems America, Inc.
+ 0435 Hyundai Electronics America
+ 0436 Taugagreining HF
+ 0437 Framatome Connectors USA
+-0438 Advanced Micro Devices
++0438 Advanced Micro Devices, Inc.
+ 0439 Voice Technologies Group
+-043d Lexmark International Inc.
++043d Lexmark International, Inc.
+ 0002 Optra E310 Printer
+ 0009 Optra S2450 Printer
+ 000c Optra E312 Printer
+ 0017 Z32 printer
+ 0018 Z52 Printer
++ 001c Kodak Personal Picture Maker 200 Printer
++ 001f Kodak Personal Picture Maker 200 Card Reader
+ 0020 Z51 Printer
++ 0021 Z33 Printer
++ 002d X70/X73 Scan/Print/Copy
+ 003d X83 Scan/Print/Copy
+-043e LG Electronics USA Inc.
++ 0057 Z35 Printer
++ 0060 X74/X75 Scanner
++ 0061 X74 Hub
++ 0069 X74 Printer
++043e LG Electronics USA, Inc.
+ 42bd Flatron 795FT Plus Monitor
++ 4a4d Flatron 915FT Plus Monitor
+ 7001 MF-PD100 Soul Digital MP3 Player
+ 8484 LPC-U30 Webcam II
+ 8585 LPC-UC35 Webcam
+@@ -212,49 +328,69 @@
+ 0440 Eizo Nanao Corp.
+ 0441 Winbond Systems Lab.
+ 1456 Hub
+-0442 Ericsson Inc.
+-0443 Gateway 2000
+-0445 Lucent Technologies
+-0446 NMB Technologies, Inc.
++0442 Ericsson, Inc.
++0443 Gateway, Inc.
++0445 Lucent Technologies, Inc.
++0446 NMB Technologies Corp.
+ 0447 Momentum Microsystems
+-044a Shamrock Tech. Co., Ltd.
++044a Shamrock Tech. Co., Ltd
+ 044b WSI
+ 044c CCL/ITRI
+-044d Siemens Nixdorf
+-044e Alps Electric Co.
++044d Siemens Nixdorf AG
++044e Alps Electric Co., Ltd
+ 2002 MD-5500 Printer
++ 3001 UGTZ4 Bluetooth
+ 044f ThrustMaster, Inc.
++ 0400 HOTAS Cougar
+ a0a3 Fusion Digital GamePad
++ b203 360 Modena Pro Wheel
+ b300 Firestorm Dual Power
+-0450 DFI Inc.
+-0451 Texas Instruments
++ b304 Firestorm Dual Power
++0450 DFI, Inc.
++0451 Texas Instruments, Inc.
+ 1428 Hub
+ 1446 TUSB2040/2070 Hub
+ 2036 TUSB2036 Hub
+ 2046 TUSB2046 Hub
+ 2077 TUSB2077 Hub
++ 6000 AU5 ADSL Modem (pre-reenum)
++ 6001 AU5 ADSL Modem
+ e001 GraphLink
++ e004 TI-89 Titanium Calculator
++ e008 TI-84 Plus Silver Calculator
+ 0452 Mitsubishi Electronics America, Inc.
++ 0050 Diamond Pro 900u CRT Monitor
++ 0051 Integrated Hub
+ 0453 CMD Technology
+ 0454 Vobis Microcomputer AG
+ 0455 Telematics International, Inc.
+ 0456 Analog Devices, Inc.
+ 0457 Silicon Integrated Systems Corp.
+-0458 KYE Systems Corp.(Mouse Systems)
++0458 KYE Systems Corp. (Mouse Systems)
+ 0001 Mouse
+ 0002 Genius NetMouse Pro
+ 0003 Genius NetScroll+
++ 000e VideoCAM Web
++ 001a Genius WebScroll+
+ 0100 EasyPen Tablet
+ 0101 CueCat
+ 1003 Genius VideoCam
+ 1004 Flight2000 F-23 Joystick
++ 100a Aashima Technology Trust Sight Fighter Vibration Feedback Joystick
+ 2001 ColorPage-Vivid Pro Scanner
++ 2007 ColorPage-HR6 V2 Scanner
++ 2008 ColorPage-HR6 V2 Scanner
++ 2009 ColorPage-HR6A Scanner
++ 2011 ColorPage-Vivid3x Scanner
++ 2013 ColorPage-HR7 Scanner
++ 2015 ColorPage-HR7LE Scanner
++ 2016 ColorPage-HR6X Scanner
+ 0459 Adobe Systems, Inc.
+-045a Diamond Multimedia Systems
++045a SONICblue, Inc.
+ 0b4a SupraMax 2890 56K Modem [Lucent Atlas]
+ 0b68 SupraMax 56K Modem
+-045b Hitachi, Ltd.
+-045d Nortel Networks
++045b Hitachi, Ltd
++045d Nortel Networks, Ltd
+ 045e Microsoft Corp.
+ 0008 SideWinder Precision Pro
+ 0009 IntelliMouse
+@@ -264,6 +400,7 @@
+ 001b SideWinder Force Feedback 2 Joystick
+ 001d Natural Keyboard Pro
+ 001e IntelliMouse Explorer
++ 0023 Trackball Optical
+ 0024 Trackball Explorer
+ 0025 IntelliEye Mouse
+ 0026 SideWinder GamePad Pro
+@@ -271,58 +408,81 @@
+ 0028 SideWinder Dual Strike
+ 0029 IntelliMouse Optical
+ 002b Internet Keyboard Pro
+- 0034 SideWinder Force Feedback Wheel
+ 0033 Sidewinder Strategic Commander
++ 0034 SideWinder Force Feedback Wheel
+ 0038 SideWinder Precision 2
+ 0039 IntelliMouse Optical
+ 003b SideWinder Game Voice
+ 003c SideWinder Joystick
+-0460 Ace Cad Enterprise Co., Ltd.
+-0461 Primax Electronics
++ 0040 Wheel Mouse Optical
++ 0047 IntelliMouse Explorer 3.0
++ 0059 Wireless IntelliMouse Explorer
++ 006e MN510 802.11b Adapter
++ 007d Notebook Optical Mouse
++ 007e Wireless Transceiver for Bluetooth
++ 008a Wireless Keyboard and Mouse
++ 0284 Xbox DVD Playback Kit
++0460 Ace Cad Enterprise Co., Ltd
++0461 Primax Electronics, Ltd
+ 0300 G2-300 Scanner
+ 0301 G2E-300 Scanner
+ 0302 G2-300 #2 Scanner
+ 0303 G2E-300 #2 Scanner
+ 0340 Colorado 9600 Scanner
+ 0341 Colorado 600u Scanner
++ 0345 Visioneer 6200 Scanner
+ 0346 Memorex Maxx 6136u Scanner
+- 0347 Visioneer 4400 Scanner
++ 0347 Primascan Colorodao 2600u/Visioneer 4400 Scanner
+ 0360 Colorado 19200 Scanner
+ 0361 Colorado 1200u Scanner
++ 0364 LG Electronics Scanworks 600U Scanner
++ 0371 Visioneer Onetouch 8920 Scanner
++ 0377 Medion MD 5345 Scanner
++ 037b Medion MD 6190 Scanner
+ 0380 G2-600 Scanner
+ 0381 ReadyScan 636i Scanner
+ 0382 G2-600 #2 Scanner
+ 0383 G2E-600 Scanner
+ 0813 IBM UltraPort Camera
++ 0815 Micro Innovations WebCam
+ 081a Fujifilm IX-30 Camera
++ 081c Elitegroup ECS-C11 Camera
++ 081d Elitegroup ECS-C11 Storage
+ 4d01 Comfort Keyboard
+ 4d02 Mouse-in-a-Box
+ 4d03 Kensington Mouse-in-a-box
+ 4d04 Mouse
+ 0463 MGE UPS Systems
+- ffff Ellipse UPS
+-0464 AMP Incorporated
++ 0001 UPS
++ ffff UPS
++0464 AMP/Tycoelectronics Corp.
+ 0467 AT&T Paradyne
+-0468 Wieson Electronic Co., Ltd.
+-046a Cherry Mikroschalter GmbH
++0468 Wieson Technologies Co., Ltd
++046a Cherry GmbH
+ 0001 My3000 Keyboard
+ 0003 My3000 Hub
+-046b American Megatrends
++046b American Megatrends, Inc.
+ 046c Toshiba Corp., Digital Media Equipment
+-046d Logitech Inc.
++046d Logitech, Inc.
+ 0203 M2452 Keyboard
+ 0301 M4848 Mouse
+ 0401 HP PageScan
+ 0402 NEC PageScan
+- 040F Logitech/Storm PageScan
++ 040f Logitech/Storm PageScan
+ 0801 QuickCam Home
+ 0810 QuickCam Pro
+ 0840 QuickCam Express
+ 0850 QuickCam Web
+ 0870 QuickCam Express
++ 08a0 QuickCam IM
+ 08b0 QuickCam 3000 Pro [pwc]
++ 08b2 QuickCam Pro 4000
++ 08b3 QuickCam Zoom
++ 08b4 QuickCam Zoom
+ 0900 ClickSmart 310
+ 0901 ClickSmart 510
++ 0921 Labtec WebCam
++ 0950 Pocket Camera
+ c000 N43 [Pilot Mouse]
+ c001 N48/M-BB48 [FirstMouse Plus]
+ c002 M-BA47 [MouseMan Plus]
+@@ -331,6 +491,9 @@
+ c00c Optical Wheel Mouse
+ c00e Optical Mouse
+ c012 Optical Mouse
++ c016 Optical Mouse
++ c01b MX310 Optical Mouse
++ c025 MX500 Optical Mouse
+ c030 iFeel Mouse
+ c032 MouseMan iFeel
+ c202 WingMan Formula
+@@ -343,15 +506,22 @@
+ c283 WingMan Force 3D
+ c285 WingMan Strike Force 3D
+ c291 WingMan Formula Force
++ c293 WingMan Formula Force GP
+ c295 Momo Force Steering Wheel
+ c2a0 Wingman Force Feedback Mouse
++ c303 iTouch Keyboard
+ c308 Internet Navigator Keyboard
++ c309 Internet Keyboard
+ c401 TrackMan Marble Wheel
++ c402 Marble Mouse (2-button)
++ c404 TrackMan Wheel
++ c408 Marble Mouse (4-button)
+ c501 Cordless Mouse Receiver
+ c503 Cordless Mouse+Keyboard Receiver
+ c504 Cordless Mouse+Keyboard Receiver
++ c505 Cordless Mouse+Keyboard Receiver
+ d001 QuickCam Pro
+-046e Behavior Tech. Computer
++046e Behavior Tech. Computer Corp.
+ 6782 BTC 7932 mouse+keyboard
+ 046f Crystal Semiconductor
+ 0471 Philips
+@@ -364,26 +534,29 @@
+ 0304 Askey VC010 WebCam [pwc]
+ 0307 PCVC675K WebCam [pwc]
+ 0308 PCVC680K WebCam [pwc]
+- 030C PCVC690K WebCam [pwc]
++ 030c PCVC690K WebCam [pwc]
+ 0310 PCVC730K WebCam [pwc]
+ 0311 PCVC740K ToUcam Pro [pwc]
+ 0312 PCVC750K WebCam [pwc]
+ 0471 Digital Speaker System
+ 0601 OVU1020 IR Dongle (Kbd+Mouse)
+ 0701 150P1 TFT Display
++ 0811 JR24 CDRW
++ 1120 Creative Rhomba MP3 player
+ 1801 Diva MP3 player
+-0472 Chicony
++0472 Chicony Electronics Co., Ltd
+ 0065 PFU-65 Keyboard
+-0473 Sanyo Information Business Co., Ltd.
+-0474 Sanyo Electric Co. Ltd.
++0473 Sanyo Information Business Co., Ltd
++0474 Sanyo Electric Co., Ltd
++ 0701 SCP-4900 Cellphone
+ 0475 Relisys/Teco Information System
+ 0476 AESP
+-0477 Seagate Technology
++0477 Seagate Technology, Inc.
+ 0478 Connectix Corp.
+ 0001 QuickCam
+ 0002 QuickClip
+ 0479 Advanced Peripheral Laboratories
+-047a USAR Systems
++047a Semtech Corp.
+ 047b Silitek Corp.
+ 0002 Keyboard and Mouse
+ 0101 BlueTooth Keyboard and Mouse
+@@ -392,43 +565,45 @@
+ 047d Kensington
+ 1003 Orbit TrackBall
+ 1005 TurboBall
+- 5002 VideoCam CABO II
+- 5003 VideoCam
+- 4006 Gravis Eliminator AfterShock
+ 4005 Gravis Eliminator GamePad Pro
++ 4006 Gravis Eliminator AfterShock
+ 4008 Gravis Destroyer TiltPad
+-047e Agere (Lucent)
++ 5002 VideoCam CABO II
++ 5003 VideoCam
++047e Agere Systems, Inc. (Lucent)
+ 1001 USS720 Parallel Port
+ f101 Atlas Modem
+ 047f Plantronics, Inc.
+ 0480 Toshiba America Info. Systems, Inc.
+ 0481 Zenith Data Systems
+-0482 Kyocera Electronics, Inc.
++0482 Kyocera Corp.
+ 0483 SGS Thomson Microelectronics
++ 1307 Cytronix 6in1 card reader
++ 163d Cool Icam Digi-MP3
+ 7554 56k SoftModem
+ 0484 Specialix
+ 0485 Nokia Monitors
+-0486 ASUS Computers Inc.
++0486 ASUS Computers, Inc.
+ 0487 Stewart Connector
+ 0488 Cirque Corp.
+ 0489 Foxconn / Hon Hai
+ 0502 SmartMedia Card Reader Firmware Loader
+ 0503 SmartMedia Card Reader
+ 048a S-MOS Systems, Inc.
+-048c Alps Electric Ireland Ltd.
+-048d Integrated Technology Express
++048c Alps Electric Ireland, Ltd
++048d Integrated Technology Express, Inc.
+ 048f Eicon Tech.
+ 0490 United Microelectronics Corp.
+ 0491 Capetronic
+ 0492 Samsung SemiConductor, Inc.
+-0493 MAG Technology Co., Ltd.
++0493 MAG Technology Co., Ltd
+ 0495 ESS Technology, Inc.
+ 0496 Micron Electronics
+ 0497 Smile International
+ 0498 Capetronic (Kaohsiung) Corp.
+ 0499 Yamaha Corp.
+ 6001 CRW2200UX Lightspeed 2 External CD-RW Drive
+-049a Gandalf Technologies Ltd.
++049a Gandalf Technologies, Ltd
+ 049b Curtis Computer Products
+ 049c Acer Advanced Labs, Inc.
+ 0002 Keyboard (???)
+@@ -436,15 +611,18 @@
+ 049f Compaq Computer Corp.
+ 0003 iPAQ PocketPC
+ 000e Internet Keyboard
+- 0033 Evo N600c Builtin Wireless Ethernet [orinoco]
++ 0018 PA-1/PA-2 MP3 Player
++ 001a S4 100 Scanner
++ 0021 S200 Scanner
++ 0033 801.11b Adapter [orinoco]
+ 505a SA-11x0 based Linux Device, or Itsy (experimental)
+ 8511 iPAQ Networking 10/100 Ethernet [pegasus2]
+ 04a0 Digital Equipment Corp.
+ 04a1 SystemSoft Corp.
+ 04a2 FirePower Systems
+-04a3 Trident Microsystems Inc.
+-04a4 Hitachi, Ltd.
+-04a5 Acer Peripherals Inc.
++04a3 Trident Microsystems, Inc.
++04a4 Hitachi, Ltd
++04a5 Acer Peripherals Inc. (now BenQ Corp.)
+ 0001 Keyboard
+ 12a6 AcerScan C310U
+ 1a20 Prisa 310U
+@@ -457,41 +635,97 @@
+ 20be Prisa 640BT
+ 20c0 Prisa 1240UT
+ 20de S2W 4300U+
++ 20fc Benq 5000
++ 20fe SW2 5300U
++ 3003 Benq WebCam
+ 9213 Kbd Hub
+ 04a6 Nokia Display Products
+ 04a7 Visioneer
+ 0211 OneTouch 7600 Scanner
+ 0221 OneTouch 5300 Scanner
+- 0224 Microtek Scanport 3000
++ 0224 OneTouch 4800 USB/Microtek Scanport 3000
++ 0226 OneTouch 5300 USB
+ 0231 6100 Scanner
+ 0311 6200 EPP/USB Scanner
+ 0321 OneTouch 8100 EPP/USB Scanner
+ 0331 OneTouch 8600 EPP/USB Scanner
+ 04a8 Multivideo Labs, Inc.
+-04a9 Canon Inc.
++04a9 Canon, Inc.
+ 1051 BJC-3000 Color Printer
+ 1056 BJC-2110 Color Printer
++ 105b S600 Printer
+ 105d S450 Printer
+ 1062 S500 Printer
+ 1064 S300 Printer
++ 106b S520 Printer
++ 106d S750 Printer
++ 1074 S330 Printer
++ 2201 CanoScan FB320U
++ 2202 CanoScan FB620U
+ 2204 CanoScan FB630U
+ 2205 CanoScan FB1210U
+- 2206 CanoScan N650U
++ 2206 CanoScan N650U/N656U
+ 2207 CanoScan 1220U
+ 2208 CanoScan D660U
+- 220d CanoScan N670U
++ 220b CanoScan D646U
++ 220c CanoScan D1250U2
++ 220d CanoScan N670U/N676U/LiDE 20
++ 220e CanoScan N1240U/LiDE 30
++ 2213 LiDE 50
+ 3041 PowerShot S10
++ 3042 CanoScan FS4000US Film Scanner
+ 3043 PowerShot S20
++ 3044 EOS D30
+ 3045 PowerShot S100
++ 3046 IXY Digital
+ 3047 Digital IXUS
+ 3048 PowerShot G1
+ 3049 PowerShot Pro90 IS
+ 304b IXY Digital 300
++ 304c PowerShot S300
++ 304d Digital IXUS 300
+ 304e PowerShot A20
+ 304f PowerShot A10
++ 3051 PowerShot S110
++ 3052 Digital IXUS V
++ 3055 PowerShot G2
+ 3056 PowerShot S40
++ 3057 PowerShot S30
+ 3058 PowerShot A40
+-04aa DaeWoo Telecom, Ltd.
++ 3059 PowerShot A30
++ 305b ZR45MC Digital Camcorder
++ 3060 EOS D60
++ 3061 PowerShot A100
++ 3062 PowerShot A200
++ 3065 PowerShot S200
++ 3066 Digital IXUS 330
++ 3067 MV550i Digital Video Camera
++ 3069 PowerShot G3
++ 306b MVX2i Digital Video Camera
++ 306c PowerShot S45
++ 306d PowerShot S45 PtP Mode
++ 306f PowerShot G3 (ptp)
++ 3070 PowerShot S230
++ 3071 PowerShot S230 (ptp)
++ 3072 PowerShot SD100 / Digital IXUS 2 (ptp)
++ 3073 PowerShot A70 (ptp)
++ 3074 PowerShot A60 (ptp)
++ 3075 IXUS 400 Camera
++ 3076 PowerShot A300
++ 3077 PowerShot S50
++ 3078 ZR70MC Digital Camcorder
++ 307b MV630i Difital Video Camera
++ 307f Optura 20
++ 3081 Optura 10
++ 3083 EOS 10D
++ 3084 EOS 300D
++ 3085 PowerShot G5
++ 3099 EOS 300D (ptp)
++ 309a PowerShot A80
++ 309b Digital IXUS (ptp)
++ 309c PowerShot S1 IS
++ 30ba PowerShot S410 Digital Elph
++04aa DaeWoo Telecom, Ltd
+ 04ab Chromatic Research
+ 04ac Micro Audiometrics Corp.
+ 04ad Dooin Electronics
+@@ -502,10 +736,31 @@
+ 0104 Coolpix 995
+ 0106 Coolpix 775
+ 0107 Coolpix 5000
++ 0108 Coolpix 2500
++ 0109 Coolpix 2500 (ptp)
++ 010a Coolpix 4500
++ 010b Coolpix 4500 (ptp)
++ 010d Coolpix 5700 (ptp)
++ 010e Coolpix 4300 (storage)
++ 010f Coolpix 4300 (ptp)
++ 0111 Coolpix 3500 (ptp)
++ 0112 Coolpix 885 (ptp)
++ 0113 Coolpix 5000 (ptp)
++ 0114 Coolpix 3100 (storage)
++ 0115 Coolpix 3100 (ptp)
++ 0117 Coolpix 2100 (ptp)
++ 0119 Coolpix 5400 (ptp)
++ 0202 Coolpix SQ (ptp)
++ 0301 Coolpix 2000 (storage)
++ 0302 Coolpix 2000 (ptp)
++ 0402 DSC D100 (ptp)
++ 4000 Coolscan LS 40 ED
+ 04b1 Pan International
+ 04b3 IBM Corp.
+ 3004 Media Access Pro Keyboard
+ 3100 NetVista Mouse
++ 3103 ScrollPoint Pro Mouse
++ 3109 Optical ScrollPoint Pro Mouse
+ 4427 Portable CD ROM
+ 4525 Double sided CRT
+ 4550 NVRAM (128 KB)
+@@ -514,14 +769,18 @@
+ 4581 4800-2xx Hub w/ Cash Drawer
+ 4604 Keyboard w/ Card Reader
+ 4671 4820 LCD w/ MSR/KB
+-04b4 Cypress Semiconductor
++04b4 Cypress Semiconductor Corp.
+ 0000 Dacal DC-101 CD Library
+ 0001 Mouse
+ 0002 CY7C63x0x Thermometer
+ 1002 CY7C63001 R100 FM Radio
++ 5500 HID->COM RS232 Adapter
++ 6560 CY7C65640 USB-2.0 "TetraHub"
++ 6830 USB-2.0 IDE Adapter
+ 8613 CY7C68013 EZ-USB FX2 USB 2.0 Development Kit
+ d5d5 CY7C63x0x Zoltrix Z-Boxer GamePad
+-04b5 ROHM LSI Systems, Inc.
++ f000 CY30700 Licorice evaluation board
++04b5 ROHM LSI Systems USA, LLC
+ 04b6 Hint Corp.
+ 04b7 Compal Electronics, Inc.
+ 04b8 Seiko Epson Corp.
+@@ -530,28 +789,42 @@
+ 0003 ISD Smart Cable
+ 0005 Stylus Printer
+ 0101 Perfection 636
++ 0102 GT-2200
+ 0103 Perfection 610
+ 0104 Perfection 1200
++ 0105 StylusScan 2000
+ 0106 Stylus Scan 2500
+ 0107 Expression 1600U
++ 0109 Expression 1640 XL
+ 010a Perfection 1640SU
+ 010b Perfection 1240
+ 010c Perfection 640
++ 010e Perfection 1680
+ 010f Perfection 1250
+ 0110 Perfection 1650
++ 0112 Perfection 2450
++ 0114 Perfection 660
++ 011b Perfection 2400 Photo
++ 011c Perfection 3200
++ 011d Perfection 1260 Photo
++ 011e Perfection 1660 Photo
++ 011f Perfection 1670
+ 0202 Receipt Printer M129C
+ 0601 Stylus Photo 875DC Card Reader
+ 0602 Stylus Photo 895 Card Reader
++ 0801 Stylus CX5200
++ 0802 Stylus CX3200
+ 04b9 Rainbow Technologies, Inc.
+ 1000 iKey 1000 Token
+ 1001 iKey 1200 Token
+ 1200 iKey 2000 Token
+ 1202 iKey 2032 Token
+ 1300 iKey 3000 Token
+-04ba Toucan Systems Ltd.
++04ba Toucan Systems, Ltd
+ 04bb I-O Data Device, Inc.
+ 0904 ET/TX Ethernet [pegasus]
+ 0913 ET/TX-S Ethernet [pegasus2]
++ 0922 IOData AirPort WN-B11/USBS 802.11b
+ 04bd Toshiba Electronics Taiwan Corp.
+ 04be Telia Research AB
+ 04bf TDK Corp.
+@@ -561,150 +834,202 @@
+ 008f Pro ISDN TA
+ 009d HomeConnect WebCam [vicam]
+ 3021 56k Voice FaxModem Pro
+-04c2 Methode Electronics Far East PTE Ltd.
++04c2 Methode Electronics Far East PTE, Ltd
+ 04c3 Maxi Switch, Inc.
+ 04c4 Lockheed Martin Energy Research
+-04c5 Fujitsu Ltd.
++04c5 Fujitsu, Ltd
++ 1029 fi-4010c Scanner
++ 1041 fi-4120c Scanner
++ 1042 fi-4220c Scanner
+ 04c6 Toshiba America Electronic Components
+ 04c7 Micro Macro Technologies
+ 04c8 Konica Corp.
+ 0720 Digital Color Camera
+ 0721 e-miniD Camera
+ 0723 KD-200Z Camera
++ 0726 KD-310Z Camera
+ 04ca Lite-On Technology Corp.
+-04cb Fuji Photo Film Co., Ltd.
+- 0100 FinePix 1300 / 1400 / 4700 Zoom digital camrea
++04cb Fuji Photo Film Co., Ltd
++ 0100 FinePix 1300 / 1400 / 4700 Zoom digital camera
+ 0103 FinePix NX-700 printer
++ 0104 FinePix A101/2600 Zoom (PC-Cam Mode)
++ 0108 FinePix F601 Zoom (Disk mode)
++ 0109 FinePix F601 Zoom (PC-Cam mode)
++ 010a FinePix S602 Zoom (Disk mode)
++ 010b FinePix S602 Zoom (PC-Cam mode)
++ 0114 FinePix F401 Zoom (Disk mode)
++ 0115 FinePix F401 Zoom (PC-Cam mode)
++ 0116 FinePix A203 (Disk mode)
++ 0117 FinePix A203 (PC-Cam mode)
++ 011a FinePix S304/3800 (Disk mode)
++ 011b FinePix S304/3800 (PC-Cam mode)
++ 011c FinePix 2650 (Disk mode)
++ 0130 Finepix S5000 Camera (Disk mode)
++ 0131 Finepix S5000 Camera (PC-Cam mode)
+ 04cc Philips Semiconductors
+ 1122 Hub
++ 1521 USB 2.0 Hub
+ 8116 Camera
+ 04cd Tatung Co. Of America
+ 04ce ScanLogic Corp.
+ 0002 SL11R-IDE IDE Bridge
+-04cf Myson Technology Inc.
++04cf Myson Century, Inc.
+ 04d0 Digi International
+ 04d1 ITT Canon
+ 04d2 Altec Lansing Technologies
+ 0311 ADA-310 Speakers
+ ff05 ADA-305 Speakers
+ 04d3 VidUS, Inc.
+-04d4 LSI Logic Corp.
++04d4 LSI Logic, Inc.
+ 04d5 Forte Technologies, Inc.
+ 04d6 Mentor Graphics
+ 04d7 Oki Semiconductor
+-04d8 Microchip Technology Inc.
++04d8 Microchip Technology, Inc.
++ 8000 In-Circuit Debugger
+ 04d9 Holtek Semiconductor, Inc.
+ 04da Panasonic (Matsushita)
+-04db Hypertec Pty Ltd.
+-04dc Huan Hsin Co.
++04db Hypertec Pty, Ltd
++04dc Huan Hsin Holdings, Ltd
+ 04dd Sharp Corp.
+- 8004 Zaurus SL-5000D PDA
++ 7004 VE-CG40U Digital Still Camera
++ 8004 Zaurus SL-5000D/SL-5500 PDA
++ 8005 Zaurus A-300
++ 8006 Zaurus SL-B500/SL-5600 PDA
++ 8007 Zaurus C-700 PDA
++ 9014 IM-DR80 Portable NetMD Player
++ 9031 Zaurus C-750/C-760 PDA
++ 9032 Zaurus SL-6000
++ 9050 Zaurus C-860 PDA
+ 04de MindShare, Inc.
+ 04df Interlink Electronics
+-04e1 Iiyama North America Inc.
++04e1 Iiyama North America, Inc.
+ 0201 Monitor Hub
+ 04e2 Exar Corp.
+-04e3 Zilog
++04e3 Zilog, Inc.
+ 04e4 ACC Microelectronics
+ 04e5 Promise Technology
+-04e6 Shuttle Technology Inc.
++04e6 SCM Microsystems, Inc.
+ 0001 E-USB ATA Bridge
+ 0002 eUSCSI SCSI Bridge
+ 0003 eUSB SmartMedia Card Reader
+ 0005 eUSB SmartMedia/CompactFlash Card Reader
+ 0006 eUSB SmartMedia Card Reader
+ 0007 Hifd
+- 0101 E-USB ATA Bridge
++ 0101 eUSB ATA Bridge
++ 0325 eUSB ORCA Quad Reader
+ 1010 USBAT-2 CompactFlash Card Reader
+ 04e7 Elo TouchSystems
+- 0001 TouchScreen
+-04e8 Samsung Electronics Co., Ltd.
++ 0001 TouchScreen
++04e8 Samsung Electronics Co., Ltd
++ 1003 MP3 Player and Recorder
++ 300c ML-1210 Printer
+ 5a03 Yepp MP3 Player
+-04e9 PC-Tel Inc.
++ 6601 Z100 Mobile Phone
++04e9 PC-Tel, Inc.
+ 04ea Brooktree Corp.
+ 04eb Northstar Systems, Inc.
+-04ec Tokyo Electron Limited
++04ec Tokyo Electron Device, Ltd
+ 04ed Annabooks
+ 04ef Pacific Electronic International, Inc.
+-04f0 Daewoo Electronics Co., Ltd.
+-04f1 Victor Company of Japan (JVC)
++04f0 Daewoo Electronics Co., Ltd
++04f1 Victor Company of Japan, Ltd
+ 0001 GC-QX3 Digital Still Camera
+ 0004 GR-DVL815U Digital Video Camera
+-04f2 Chicony Electronics Co., Ltd.
++ 0009 GR-DX25EK Digital Video Camera
++04f2 Chicony Electronics Co., Ltd
+ 0001 KU-8933 Keyboard
+ 0002 NT68P81 Keyboard
++ 0110 KU-2971 Keyboard
+ 0112 KU-8933 Keyboard with PS/2 Mouse port
+-04f3 Elan Microelectronics Corportation
+-04f4 Harting Elektronik Inc.
++04f3 Elan Microelectronics Corp.
++04f4 Harting Elektronik, Inc.
+ 04f5 Fujitsu-ICL Systems, Inc.
+ 04f6 Norand Corp.
+ 04f7 Newnex Technology Corp.
+ 04f8 FuturePlus Systems
+-04f9 Brother Industries, Ltd.
++04f9 Brother Industries, Ltd
+ 0002 HL-1050 Laser Printer
+ 0007 HL-1250 Laser Printer
++ 0008 HL-1270 Laser Printer
++ 000d HL-1440 Laser Printer
++ 010f MFC 5100C
++ 0111 MFC 6800
++ 2004 PT-2300/2310 p-Touch Laber Printer
+ 04fa Dallas Semiconductor
+ 2490 DS1490F 2-in-1 Fob, 1-Wire adapter
+ 4201 DS4201 Audio DAC
+-04fb Biostar Microtech Int'l Corp.
+-04fc Sunplus Technology Co.
++04fb Biostar Microtech International Corp.
++04fc Sunplus Technology Co., Ltd
++ 0003 CM1092 Optical Scroller Mouse
++ 504a SPCA504a Digital Camera
++ 504b Aiptek, 1.3 mega PockerCam
+ 04fd Soliton Systems, K.K.
+-04fe PFU Limited
++04fe PFU, Ltd
+ 04ff E-CMOS Corp.
+ 0500 Siam United Hi-Tech
+-0501 DDK Electronics, Inc.
++0501 Fujikura DDK, Ltd
+ 0502 Acer, Inc.
+ d001 Divio NW801/DVC-V6+ Digital Camera
+-0503 Hitachi America Ltd.
++0503 Hitachi America, Ltd
+ 0504 Hayes Microcomputer Products
+ 0506 3Com Corp.
++ 00a0 3CREB96 Bluetooth Adapter
+ 03e8 3C19250 Ethernet [klsi]
+- 4601 3C460B USB 10/100 Ethernet Adaptor
++ 4601 3C460B 10/100 Ethernet Adapter
+ f002 3CP4218 ADSL Modem (pre-init)
+ f003 3CP4218 ADSL Modem
+ f100 3CP4218 ADSL Modem (pre-init)
+ 0507 Hosiden Corp.
+-0508 Clarion Co., Ltd.
+-0509 Aztech Systems Ltd.
++0508 Clarion Co., Ltd
++0509 Aztech Systems, Ltd
+ 050a Cinch Connectors
+ 050b Cable System International
+ 050c InnoMedia, Inc.
+ 050d Belkin Components
+ 0103 F5U103 Serial Adapter [etek]
+- 0109 F5U109 PDA Adapter
++ 0109 F5U109/F5U409 PDA Adapter
+ 0115 SCSI Adapter
+ 0121 F5D5050 100Mbps Ethernet
+- 0208 Video Adapter [nt1004]
++ 0208 USBView II Video Adapter [nt1004]
++ 0224 F5U224 USB 2.0 4-Port Hub
++ 0234 F5U234 USB 2.0 4-Port Hub
+ 0805 Nostromo N50 GamePad
+ 1203 F5U120-PC Serial Port
+ 050e Neon Technology, Inc.
+-050f KC Technology Inc.
++050f KC Technology, Inc.
+ 0003 KC82C160S Hub
+ 0180 KC-180 IrDA Dongle
+-0510 Sejin Electron Inc.
++0510 Sejin Electron, Inc.
+ 0511 N'Able (DataBook) Technologies, Inc.
+ 0512 Hualon Microelectronics Corp.
+ 0513 digital-X, Inc.
+-0514 FCI/Berg Electronics Group
++0514 FCI Electronics
+ 0515 ACTC
+ 0516 Longwell Electronics
+ 0517 Butterfly Communications
+ 0518 EzKEY Corp.
+-0519 Star Micronics Co., Ltd.
++0519 Star Micronics Co., Ltd
+ 051a WYSE Technology
+ 051b Silicon Graphics
+-051c Shuttle Inc.
++051c Shuttle, Inc.
+ 051d American Power Conversion
+- 0002 Back-UPS Pro 500
+-051e Scientific Atlanta
++ 0002 Back-UPS Pro 500/1000/1500
++051e Scientific Atlanta, Inc.
+ 051f IO Systems (Elite Electronics), Inc.
+ 0520 Taiwan Semiconductor Manufacturing Co.
+ 0521 Airborn Connectors
+-0522 Advanced Connectek USA Inc.
++0522 Advanced Connectek, Inc.
+ 0523 ATEN GmbH
+ 0524 Sola Electronics
+-0525 Netchip Technology Inc.
++0525 Netchip Technology, Inc.
+ 1080 NET1080 USB-USB Bridge
++ a4a0 Linux-USB "Gadget Zero"
++ a4a1 Linux-USB Ethernet Gadget
++ a4a2 Linux-USB Ethernet/RNDIS Gadget
++ a4a3 Linux-USB user-mode isochronous source/sink
++ a4a4 Linux-USB user-mode bulk source/sink
++ a4a5 Linux-USB File Storage Gadget
++ a4a6 Linux-USB Serial Gadget
+ 0526 Temic MHS S.A.
+ 0527 ALTRA
+ 0528 ATI Technologies, Inc.
+@@ -715,27 +1040,30 @@
+ 0313 eToken R1 v3.2.3.x
+ 031b eToken R1 v3.3.3.x
+ 0323 eToken R1 v3.4.3.x
+- 050c eToken Pro v4.1.5.x
+ 0412 eToken R2 v2.2.4.x
+ 041a eToken R2 v2.2.4.x
+ 0422 eToken R2 v2.4.4.x
+ 042a eToken R2 v2.5.4.x
++ 050c eToken Pro v4.1.5.x
++ 0514 eToken Pro v4.2.5.4
+ 052a Crescent Heart Software
+ 052b Tekom Technologies, Inc.
+-052c Canon Information System
++ 1513 Aosta CX100 WebCam
++ 1514 Aosta CX100 WebCam Storage
++052c Canon Information Systems, Inc.
+ 052d Avid Electronics Corp.
+ 052e Standard Microsystems Corp.
+ 052f Unicore Software, Inc.
+-0530 American Microsystems Inc.
++0530 American Microsystems, Inc.
+ 0531 Wacom Technology Corp.
+ 0532 Systech Corp.
+ 0533 Alcatel Mobile Phones
+-0534 Motorola
+-0535 LIH TZU Electric Co., Ltd.
+-0536 Welch Allyn Inc.
++0534 Motorola, Inc.
++0535 LIH TZU Electric Co., Ltd
++0536 Hand Held Products (Welch Allyn, Inc.)
+ 0537 Inventec Corp.
+-0538 Santa Cruz Operation
+-0539 Shyh Shiun Terminals Co. Ltd.
++0538 Caldera International, Inc. (SCO)
++0539 Shyh Shiun Terminals Co., Ltd
+ 053a Preh Werke GmbH & Co. KG
+ 053b Global Village Communication
+ 053c Institut of Microelectronic & Mechatronic Systems
+@@ -749,13 +1077,15 @@
+ 00fe G773 Monitor Hub
+ 00ff P815 Monitor Hub
+ 4153 ViewSonic G773 Control (?)
+-0544 Cristie Electronics Ltd.
++0544 Cristie Electronics, Ltd
+ 0545 Xirlink, Inc.
+ 8002 IBM NetCamera
+ 800c Veo StingRay
+ 8080 IBM C-It WebCam
++ 810a Veo Advanced Connect WebCam
+ 0546 Polaroid Corp.
+-0547 Anchor Chips Inc.
++ 1bed PDC 1320 Camera
++0547 Anchor Chips, Inc.
+ 2131 AN2131 EZUSB Microcontroller
+ 2235 AN2235 EZUSB-FX Microcontroller
+ 2720 AN2720 USB-USB Bridge
+@@ -775,24 +1105,29 @@
+ 002e Sony HandyCam MemoryStick Reader
+ 0032 MemoryStick MSC-U01 Reader
+ 0038 Clie PEG-S300/D PalmOS PDA
++ 004e DSC-xxx (ptp)
+ 0058 Clie PEG-N7x0C PalmOS PDA Mass Storage
+- 0066 Clie PEG-N7x0C PalmOS PDA Serial
++ 0066 Clie PEG-N7x0C/PEG-T425 PalmOS PDA Serial
+ 0069 Memorystick MSC-U03 Reader
++ 006d Clie PEG-T425 PDA Mass Storage
+ 0099 Clie NR70 PDA Mass Storage
+ 009a Clie NR70 PDA Serial
++ 00c8 MZ-N710 Minidisc Walkman
++ 0107 VCC-U01 Visual Communication Camera
+ 054d Try Corp.
+ 054e Proside Corp.
+ 054f WYSE Technology Taiwan
+-0550 Fuji Xerox Co., Ltd.
++0550 Fuji Xerox Co., Ltd
+ 0551 CompuTrend Systems, Inc.
+ 0552 Philips Monitors
+-0553 VLSI Vision Ltd.
++0553 STMicroelectronics Imaging Division (VLSI Vision)
+ 0002 CPiA WebCam
+ 0202 Aiptek PenCam 1
+ 0554 Dictaphone Corp.
+-0555 ANAM S&T Co., Ltd.
+-0556 Asahi Kasei Microsystems Co., Ltd.
+-0557 ATEN International Co. Ltd.
++0555 ANAM S&T Co., Ltd
++0556 Asahi Kasei Microsystems Co., Ltd
++ 0001 AK5370 I/F A/D Converter
++0557 ATEN International Co., Ltd
+ 2001 UC-1284 Printer Port
+ 2002 10Mbps Ethernet [klsi]
+ 2004 UC-100KM PS/2 Mouse and Keyboard adapter
+@@ -811,17 +1146,35 @@
+ 9000 AnyCam [pwc]
+ 9001 MPC-C30 AnyCam Premium for Notebooks [pwc]
+ 055e CTX Opto-Electronics Corp.
+-055f Mustek Systems Inc.
++055f Mustek Systems, Inc.
+ 0001 ScanExpress 1200 CU
+ 0002 ScanExpress 600 CU
+ 0003 ScanExpress 1200 USB
+ 0006 ScanExpress 1200 UB
++ 0007 ScanExpress 1200 USB Plus
+ 0008 ScanExpress 1200 CU Plus
++ 0010 BearPaw 1200F
++ 0210 ScanExpress A3 USB
+ 0218 BearPaw 2400 TA
++ 0219 BearPaw 2400 TA Plus
++ 021c BearPaw 1200 CU Plus
++ 021d BearPaw 2400 CU Plus
++ 021e BearPaw 1200 TA/CS
++ 0400 BearPaw 2400 TA Pro
++ 0401 P 3600 A3 Pro
++ 0873 ScanExpress 600 USB
++ 1000 BearPaw 4800 TA Pro
++ a350 gSmart 350
+ a800 MDC 800 Camera
+-0560 Interface Co., Ltd.
++ b500 MDC 3000 Camera
++ c200 gSmart 300
++ c220 gSmart mini
++ c420 gSmart mini 2
++ c520 gSmart mini 3
++ d001 WCam 300
++0560 Interface Corp.
+ 0561 Oasis Design, Inc.
+-0562 Telex Communications Inc.
++0562 Telex Communications, Inc.
+ 0001 Enhanced Microphone
+ 0563 Immersion Corp.
+ 0564 Chinon Industries, Inc.
+@@ -830,10 +1183,11 @@
+ 0002 Enet Ethernet [klsi]
+ 0003 @Home Networks Ethernet [klsi]
+ 0005 Enet2 Ethernet [klsi]
+-0567 Xyratex Int'l Ltd.
++0566 Monterey International Corp.
++0567 Xyratex International, Ltd
+ 0568 Quartz Ingenierie
+ 0569 SegaSoft
+-056a Wacom Co., Ltd.
++056a Wacom Co., Ltd
+ 0000 PenPartner
+ 0010 Graphire
+ 0011 Graphire 2
+@@ -844,28 +1198,47 @@
+ 0024 Intuos 12x18
+ 0031 PL500
+ 0043 Intuos 2
+-056b Decicon Incorporated
++056b Decicon, Inc.
+ 056c eTEK Labs
+ 8007 Kwik232 Serial Port
+ 8101 KwikLink USB-USB Bridge
+ 056d EIZO Corp.
+ 0000 Hub
+ 0001 Monitor
+-056e Elecom Co., Ltd.
++056e Elecom Co., Ltd
+ 0002 29UO Mouse
+ 4002 Laneed 100Mbps Ethernet LD-USB/TX [pegasus]
+-056f Korea Data Systems Co., Ltd.
++056f Korea Data Systems Co., Ltd
+ 0570 Epson America
+ 0571 Interex, Inc.
+ 0572 Conexant Systems (Rockwell), Inc.
+ 0001 Ezcam II WebCam
+ 0002 Ezcam II WebCam
++ 0040 Wondereye CP-115 WebCam
+ 1232 V.90 modem
+-0573 Nogatech Ltd.
++ cafe AccessRunner ADSL Modem
++0573 Zoran Co. Personal Media Division (Nogatech)
++ 0003 USBGear USBG-V1
++ 0400 D-Link V100
+ 2000 X10 va10a Wireless Camera
+- 4d01 Hauppauge USB TV
+- 4d02 NT1003 Frame Grabber
+- 4d11 NT1003 Frame Grabber
++ 2101 Zoran Co. PMD (Nogatech) AV-grabber Manhattan
++ 4100 USB-TV FM (NTSC)
++ 4450 PixelView PlayTv-USB PRO (PAL) FM
++ 4d00 Hauppauge WinTV-USB USA
++ 4d01 Hauppauge WinTV-USB
++ 4d02 Hauppauge WinTV-USB UK
++ 4d03 Hauppauge WinTV-USB France
++ 4d10 Hauppauge WinTV-USB with FM USA radio
++ 4d11 Hauppauge WinTV-USB (PAL) with FM radio
++ 4d12 Hauppauge WinTV-USB UK with FM Radio
++ 4d20 Hauppauge WinTV-USB II (PAL) with FM radio
++ 4d21 Hauppauge WinTV-USB II (PAL)
++ 4d22 Hauppauge WinTV-USB II (PAL) Model 566
++ 4d23 Hauppauge WinTV-USB France 4D23
++ 4d30 Hauppauge WinTV-USB with FM USA radio Model 602
++ 4d31 Hauppauge WinTV-USB III (PAL) with FM radio Model 568
++ 4d32 Hauppauge WinTV-USB III (PAL) FM Model 573
++ 4d35 Hauppauge WinTV-USB III (PAL) FM Model 597
+ 0574 City University of Hong Kong
+ 0575 Philips Creative Display Solutions
+ 0576 BAFO/Quality Computer Accessories
+@@ -875,55 +1248,66 @@
+ 057a Samsung Electronics America
+ 057b Y-E Data, Inc.
+ 0000 FlashBuster-U Floppy
++ 0001 Tri-Media Reader Floppy
++ 0006 Tri-Media Reader Card Reader
++ 0010 Memory Stick Reader Writer
++ 0020 HEXA Media Drive 6-in-1 Card Reader Writer
++ 0030 Memory Card Viewer (TV)
+ 057c AVM GmbH
+ 2800 ISDN-Connector TA
+-057d Shark Multimedia Inc.
+-057e Nintendo Co., Ltd.
+-057f QuickShot Limited
+-0580 Denron Inc.
++057d Shark Multimedia, Inc.
++057e Nintendo Co., Ltd
++057f QuickShot, Ltd
++0580 Denron, Inc.
+ 0581 Racal Data Group
+ 0582 Roland Corp.
+ 0002 MPU64 Midi Interface
+ 0003 Sound Canvas SC-8850
+-0583 Padix (Rockfire) Co. Ltd.
++ 0005 Edirol UM-2 MIDI Adapter
++ 0011 Edirol UA-5 Sound Capture
++0583 Padix Co., Ltd (Rockfire)
+ 2030 RM-203 USB Nest [mode 1]
+ 2031 RM-203 USB Nest [mode 2]
+ 2032 RM-203 USB Nest [mode 3]
+ 2033 RM-203 USB Nest [mode 4]
++ 2050 PX-205 PSX Bridge
+ 3050 QF-305u Gamepad
+- 7070 QF-707u Joystick [Bazooka]
+-0584 RATOC System Inc.
++ 688f QF-688uv Windstorm Pro Joystick
++ 7070 QF-707u Bazooka Joystick
++0584 RATOC System, Inc.
+ 0585 FlashPoint Technology, Inc.
+ 0586 ZyXEL Communications Corp.
+ 1000 Omni NET Modem / ISDN TA
+-0587 America Kotobuki Electronics Ind.
++0587 America Kotobuki Electronics Industries, Inc.
+ 0588 Sapien Design
+ 0589 Victron
+ 058a Nohau Corp.
+ 058b Infineon Technologies
+ 058c In Focus Systems
+ 058d Micrel Semiconductor
+-058e Tripath Technology Inc.
+-058f Alcor Micro, Inc.
++058e Tripath Technology, Inc.
++058f Alcor Micro Corp.
+ 2802 Monterey Keyboard
++ 5492 Hub
+ 9213 MacAlly Kbd Hub
+ 9215 AU9814 Hub
+ 9254 Hub
+- 9410 MicroConnectors/StrongMan Keyboard
+- 9472 Monterey/NEC Kbd Hub
++ 9380 USB Flash drive
++ 9410 Keyboard
++ 9472 Keyboard Hub
+ 0590 Omron Corp.
+ 0004 Cable Modem
+ 0591 Questra Consulting
+ 0592 Powerware Corp.
+ 0593 Incite
+ 0594 Princeton Graphic Systems
+-0595 Zoran Microelectronics Ltd.
+-0596 MicroTouch Systems Inc.
++0595 Zoran Microelectronics, Ltd
++0596 MicroTouch Systems, Inc.
+ 0001 Touchscreen
+ 0597 Trisignal Communications
+ 0598 Niigata Canotec Co., Inc.
+-0599 Brilliance Semiconductor Inc.
+-059a Spectrum Signal Processing Inc.
++0599 Brilliance Semiconductor, Inc.
++059a Spectrum Signal Processing, Inc.
+ 059b Iomega Corp.
+ 0001 Zip 100 (Type 1)
+ 000b Zip 100 (Type 2)
+@@ -932,135 +1316,172 @@
+ 0032 Zip 250 (Ver 2)
+ 0040 SCSI Bridge
+ 0050 Zip CD 650 Writer
++ 0053 CDRW55292EXT CD-RW External Drive
+ 006d HipZip MP3 Player
+-059c A-Trend Technology Co., Ltd.
++059c A-Trend Technology Co., Ltd
+ 059d Advanced Input Devices
+ 059e Intelligent Instrumentation
+-059f LaCie
++059f LaCie, Ltd
++ 0212 PocketDrive
+ a601 HardDrive
+ 05a0 Vetronix Corp.
+ 05a1 USC Corp.
+-05a2 Fuji Film Microdevices Co. Ltd.
+-05a3 V Automation Inc.
++05a2 Fuji Film Microdevices Co., Ltd
++05a3 ARC International
+ 05a4 Ortek Technology, Inc.
+ 9731 MCK-600W Keyboard
+ 05a5 Sampo Technology Corp.
+-05a6 Cisco Systems
++05a6 Cisco Systems, Inc.
+ 05a7 Bose Corp.
+ 05a8 Spacetec IMC Corp.
+ 05a9 OmniVision Technologies, Inc.
+ 0511 OV511 WebCam
+ 0518 OV518 WebCam
+ a511 OV511+ WebCam
+-05aa Utilux South China Ltd.
++05aa Utilux South China, Ltd
+ 05ab In-System Design
+ 0002 Parallel Port
+ 0031 ATA Bridge
+- 0060 USB 2.0 Bridge
++ 0060 USB 2.0 ATA Bridge
+ 0200 USS725 ATA Bridge
+ 0202 ATA Bridge
+ 081a ATA Bridge
+-05ac Apple Computer
++ 0cda ATA Bridge for CD-R/RW
++05ac Apple Computer, Inc.
+ 0201 iMac Keyboard [ALPS M2452]
+ 0202 Apple Keyboard [ALPS]
++ 0205 Apple Extended Keyboard [Mitsumi]
+ 0206 Apple Extended Keyboard [Mitsumi]
+ 0301 iMac Mouse [Mitsumi/Logitech]
+ 0302 Apple Optical Mouse [Fujitsu]
+ 1001 Apple Keyboard Hub [ALPS]
+ 1002 Apple Extended Keyboard Hub [Mitsumi]
+-05ad Y.C.Cable U.S.A., Inc.
++ 1101 Speakers
++ 1201 3G iPod
++05ad Y.C. Cable U.S.A., Inc.
+ 05ae Synopsys, Inc.
+-05af Jing-Mold Enterprise Co., Ltd.
++05af Jing-Mold Enterprise Co., Ltd
+ 05b0 Fountain Technologies, Inc.
+ 05b1 First International Computer, Inc.
+-05b4 LG Semicon Co., Ltd.
+-05b5 Dialogic Corp
++05b4 LG Semicon Co., Ltd
++ 4857 M-Any DAH-210
++ 6001 Digisette DUO-MP3 AR-100
++05b5 Dialogic Corp.
+ 05b6 Proxima Corp.
+ 05b7 Medianix Semiconductor, Inc.
+ 05b8 Agiler, Inc.
+ 3002 Scroll Mouse
+ 05b9 Philips Research Laboratories
+ 05ba DigitalPersona, Inc.
++05bc 3G Green Green Globe Co., Ltd
++ 0004 Trackball
+ 05bd RAFI GmbH & Co. KG
+-05be Raychem Corp.
++05be Tyco Electronics (Raychem)
+ 05bf S & S Research
+ 05c0 Keil Software
+-05c1 Kawasaki Steel
++05c1 Kawasaki Microelectronics, Inc.
+ 05c2 Media Phonics (Suisse) S.A.
+-05c5 Digi International Inc.
++05c5 Digi International, Inc.
+ 05c6 Qualcomm, Inc.
+ 3100 CDMA Wireless Modem/Phone
+ 3196 CDMA Wireless Modem
+ 3197 CDMA Wireless Modem/Phone
+-05c7 Qtronix Corp
++05c7 Qtronix Corp.
+ 1001 Lynx Mouse
+- 2011 Scorpius Keyboard
+-05c8 Cheng Uei Precision Industry Co., Ltd.
++ 2011 SCorpius Keyboard
++05c8 Cheng Uei Precision Industry Co., Ltd (Foxlink)
+ 05c9 Semtech Corp.
+-05ca Ricoh Company Ltd.
+- 0101 RDC-5300 Digital Camera
+- 2201 RDC-7 Digital Camera
+-05cb PowerVision Technologies Inc.
++05ca Ricoh Co., Ltd
++ 0101 RDC-5300 Camera
++ 2201 RDC-7 Camera
++ 2205 Caplio RR30 / Medion MD 6126 Camera
++05cb PowerVision Technologies, Inc.
+ 1483 Trust CombiScan 19200
+ 05cc ELSA AG
+ 2100 MicroLink ISDN Office
+ 2219 MicroLink ISDN
+ 2265 MicroLink 56k
++ 2267 MicroLink 56k (V.250)
+ 2280 MicroLink 56k Fun
+ 3000 Micolink USB2Ethernet [pegasus]
+-05cd Silicom Ltd.
+-05ce SICAN GmbH
+-05cf Sung Forn Co. Ltd.
+-05d0 Lunar Corp.
+-05d1 Brainboxes Limited
++ 3363 MicroLink ADSL Fun
++05cd Silicom, Ltd
++05ce sci-worx GmbH
++05cf Sung Forn Co., Ltd
++05d0 GE Medical Systems Lunar
++05d1 Brainboxes, Ltd
+ 05d2 Wave Systems Corp.
+ 05d6 Philips Semiconductors, CICT
+ 05d7 Thomas & Betts Corp.
+ 0099 10Mbps Ethernet [klsi]
+ 05d8 Ultima Electronics Corp.
+- 4002 Lifetec LT9385 Scanner
++ 4001 Artec Ultima 2000
++ 4002 Artec Ultima 2000 (GT6801 based)/Lifetec LT9385 Scanner
++ 4003 Artec E+ 48U
++ 4004 Artec E+ Pro
++ 4008 Trust Easy Webscan 19200
++ 4009 Umax Astraslim
+ 05d9 Axiohm Transaction Solutions
+-05da Microtek International Inc.
++05da Microtek International, Inc.
+ 0093 ScanMaker V6USL
+ 0094 Phantom 336CX/C3
+ 0099 ScanMaker X6/X6U
+ 009a Phantom C6
+ 00a0 Phantom 336CX/C3 (#2)
+ 00b6 ScanMaker V6UPL
++ 1011 NHJ Che-ez! Kiss Digital Camera
++ 30ce ScanMaker 3800
+ 30cf ScanMaker 4800
++ 30e6 ScanMaker i320
+ 40ca ScanMaker 3600
+ 80a3 ScanMaker V6USL (#2)
+ 80ac ScanMaker V6UL/SpicyU
++05db Sun Corp. (Suntac?)
+ 05dc Lexar Media, Inc.
+-05dd Delta Electronics Inc.
++ 0080 Jumpdrive Secure 64MB
++ b018 Multi-Card Reader
++05dd Delta Electronics, Inc.
+ 05e0 Symbol Technologies
++05e1 Syntek Semiconductor Co., Ltd
+ 05e3 Genesys Logic, Inc.
+- 000a Keyboard with PS/2 Port
+- 000b Karna Razor BoomSlang 2000 Mouse
+- 0120 Pacific Image Electronics PrimeFilm 1800u slide/negative scanner
+- 0300 GLUSB98PT Parallel Port
+- 0502 GL620USB GeneLink USB-USB Bridge
+- 0700 SIIG US2256 CompactFlash Card Reader
+-05e5 Fuji Electric Co., Ltd.
++ 000a Keyboard with PS/2 Port
++ 000b Mouse
++ 0120 Pacific Image Electronics PrimeFilm 1800u slide/negative scanner
++ 0300 GLUSB98PT Parallel Port
++ 0406 Hub
++ 0502 GL620USB GeneLink USB-USB Bridge
++ 0660 USB 2.0 Hub
++ 0700 SIIG US2256 CompactFlash Card Reader
++ 0701 USB 2.0 IDE Adapter
++ 0702 USB 2.0 IDE Adapter
++ 0703 Card Reader
++ 0760 Card Reader
++ 07A0 Pen Flash
++ 1205 Afilias Optical Mouse H3003
++05e5 Fuji Electric Co., Ltd
+ 05e6 Keithley Instruments
+ 05e9 Kawasaki LSI
+ 0008 KL5KUSB101B Ethernet [klsi]
+ 0009 Sony 10Mbps Ethernet [pegasus]
+-05eb FFC Limited
++05eb FFC, Ltd
+ 05ef AVB, Inc. [anko?]
+ 020a Top Shot Pegasus Joystick
+ 8884 Mag Turbo Force Wheel
+ 8888 Top Shot Force Feedback Racing Wheel
+-05f0 Canopus Co., Ltd.
++05f0 Canopus Co., Ltd
+ 0101 DA-Port DAC
+-05f2 Dexin Corp., Ltd.
++05f2 Dexin Corp., Ltd
+ 05f3 PI Engineering, Inc.
++ 0007 Kinesis Advantage PRO MPC/USB Keyboard
++ 0081 Kinesis Integrated Hub
+ 020b PS2 Adapter
+-05f5 Unixtar Technology Inc.
++ 0232 X-Keys Switch Interface, Programming Mode
++ 0264 X-Keys Switch Interface, Composite Mode
++05f5 Unixtar Technology, Inc.
+ 05f6 AOC International
+-05f7 RFC Distribution(s) PTE Ltd.
++05f7 RFC Distribution(s) PTE, Ltd
+ 05f9 PSC Scanning, Inc.
+-05fa Siemens Telecommunications Systems Limited
++05fa Siemens Telecommunications Systems, Ltd
+ 05fc Harman Multimedia
+ 05fd InterAct, Inc.
+ 0251 Raider Pro
+@@ -1075,47 +1496,51 @@
+ 05ff LeCroy Corp.
+ 0600 Barco Display Systems
+ 0601 Jazz Hipster Corp.
+-0602 Vista Imaging Inc.
++0602 Vista Imaging, Inc.
+ 1001 ViCam WebCam
+ 0603 Novatek Microelectronics Corp.
+ 6871 Mouse
+-0604 Jean Co, Ltd.
+-0606 Royal Information Electronics Co., Ltd.
+-0607 Bridge Information Co., Ltd.
+-0609 SMK Manufacturing Inc.
++0604 Jean Co., Ltd
++0606 Royal Information Electronics Co., Ltd
++0607 Bridge Information Co., Ltd
++0609 SMK Manufacturing, Inc.
+ 060a Worthington Data Solutions, Inc.
+ 060b Solid Year (?)
+ 0001 MacAlly Keyboard
+ 2101 Solid Year Keyboard
+ 060c EEH Datalink GmbH
+-060f Joinsoon Electronics Mfg. Co., Ltd.
+-0611 Totoku Electric Co., Ltd.
+-0613 Ithaca Peripherals
++060f Joinsoon Electronics Mfg. Co., Ltd
++0611 Totoku Electric Co., Ltd
++0613 TransAct Technologies, Inc.
+ 0614 Bio-Rad Laboratories
+-0616 Future Techno Designs PVT. Ltd.
++0616 Future Techno Designs PVT, Ltd
+ 0618 MacAlly
+ 0101 Mouse
+-0619 Seiko Instruments Inc.
+-061c Act Labs, Ltd.
++0619 Seiko Instruments, Inc.
++061c Act Labs, Ltd
+ 061d Quatech, Inc.
+ 061e Nissei Electric Co.
+ 0620 Alaris, Inc.
+ 0621 ODU-Steckverbindungssysteme GmbH & Co. KG
++0623 Littelfuse, Inc.
+ 0624 Apex, Inc.
+-0626 Nippon Systems Development Co., Ltd.
+-0629 Zida Technologies Limited
+-062b Greatlink Electronics Taiwan Ltd.
+-062d Taiwan Tai-Hao Enterprises Co. Ltd.
+-062e Mainsuper Enterprises Co., Ltd.
+-062f Sin Sheng Terminal & Machine Inc.
++0626 Nippon Systems Development Co., Ltd
++0629 Zida Technologies, Ltd
++062a Creative Labs
++ 0001 Notebook Optical Mouse
++062b Greatlink Electronics Taiwan, Ltd
++062d Taiwan Tai-Hao Enterprises Co., Ltd
++062e Mainsuper Enterprises Co., Ltd
++062f Sin Sheng Terminal & Machine, Inc.
+ 0634 Micron Technology, Inc.
+ 0636 Sierra Imaging, Inc.
+ 0638 Avision, Inc.
+ 0268 iVina 1200U Scanner
+ 026a Minolta Dimage Scan Dual II
++ 0a10 iVina FB1600/UMAX Astra 4500
+ 4004 Minolta Dimage Scan Elite II
+-063d Fong Kai Industrial Co., Ltd.
+-063f New Technology Cable Ltd.
++063d Fong Kai Industrial Co., Ltd
++063f New Technology Cable, Ltd
+ 0640 Hitex Development Tools
+ 0641 Woods Industries, Inc.
+ 0642 VIA Medical Corp.
+@@ -1123,107 +1548,165 @@
+ 0000 Floppy
+ 0645 Who? Vision Systems, Inc.
+ 0646 UMAX
++0647 Acton Research Corp.
++ 0100 ARC SpectraPro UV/VIS/IR Monochromator/Spectrograph
++ 0101 ARC AM-VM Mono Airpath/Vacuum Monochromator/Spectrograph
++ 0102 ARC Inspectrum Mono
++ 0103 ARC Filterwheel
++ 03e9 Inspectrum 128x1024 F VIS Spectrograph
++ 03ea Inspectrum 256x1024 F VIS Spectrograph
++ 03eb Inspectrum 128x1024 B VIS Spectrograph
++ 03ec Inspectrum 256x1024 B VIS Spectrograph
+ 0648 Inside Out Networks
+ 064b White Mountain DSP, Inc.
+-064c Ji-Haw Industrial Co., Ltd.
++064c Ji-Haw Industrial Co., Ltd
++064e Suyin Corp.
+ 064f WIBU-Systems AG
+ 0651 Likom Technology Sdn. Bhd.
+ 0652 Stargate Solutions, Inc.
+ 0654 Granite Microsystems, Inc.
+-0655 Space Shuttle Hi-Fi Wire & Cable Industry Co, Ltd.
+-0656 Glory Mark Electronic Ltd.
+-0657 Tekcon American Corp.
+-065a Optoelectronics Co., Ltd.
++0655 Space Shuttle Hi-Tech Co., Ltd
++0656 Glory Mark Electronic, Ltd
++0657 Tekcon Electronics Corp.
++065a Optoelectronics Co., Ltd
++ 0001 Barcode scanner
+ 065e Silicon Graphics
+-065f Good Way Industrial Co, Ltd & GWC Technology Inc.
++065f Good Way Technology Co., Ltd & GWC technology Inc.
++0660 TSAY-E (BVI) International, Inc.
+ 0661 Hamamatsu Photonics K.K.
+-0663 Topmax Electronic Co., Ltd.
+-0667 Aiwa Co., Ltd.
++0663 Topmax Electronic Co., Ltd
++ 0103 CobraPad
++0667 Aiwa Co., Ltd
+ 0fa1 TD-U8000 Tape Drive
+ 0668 WordWand
+ 0669 Oce' Printing Systems GmbH
+-066a Total Technologies, Ltd.
+-066b Linksys Inc.
++066a Total Technologies, Ltd
++066b Linksys, Inc.
+ 0105 SCM eUSB SmartMedia Card Reader
+ 010a Melco MCR-U2 SmartMedia / CompactFlash Reader
+ 2202 USB10TX Ethernet [pegasus]
+ 2203 USB100TX Ethernet [pegasus]
+ 2204 USB100TX HomePNA Ethernet [pegasus]
+ 2206 USB Ethernet [pegasus]
+- 2211 WUSB11 Wireless Ethernet
+-066d Entrega Inc.
++ 2211 WUSB11 802.11b Adapter
++ 2212 WUSB11v2.5 802.11b Adapter
++ 2213 WUSB12v1.1 802.11b Adapter
++066d Entrega, Inc.
+ 066e Acer Semiconductor America, Inc.
+ 066f SigmaTel, Inc.
++ 3400 STMP3400 D-Major MP3 Player
++ 3410 STMP3410 D-Major MP3 Player
+ 4200 STIr4200 IrDA Bridge
+-0672 Labtec Inc.
++ 8202 Jens of Sweden / I-BEAD 150M/150H MP3 player
++0672 Labtec, Inc.
+ 1041 LCS1040 Speaker System
+ 5000 SpaceBall 4000 FLX
+ 0673 HCL
+ 5000 Keyboard
++0674 Key Mouse Electronic Enterprise Co., Ltd
+ 0675 Draytech
+ 0110 Vigor 128 ISDN TA
+ 0676 Teles AG
+-0677 Aiwa Co., Ltd.
++0677 Aiwa Co., Ltd
+ 0fa1 TD-U8000 Tape Drive
+ 0678 ACard Technology Corp.
+ 067b Prolific Technology, Inc.
+ 0000 PL2301 USB-USB Bridge
+ 0001 PL2302 USB-USB Bridge
+- 2307 PL2307 USB-ATAPI4 Bridge
+ 2303 PL2303 Serial Port
++ 2305 PL2305 Parallel Port
++ 2307 PL2307 USB-ATAPI4 Bridge
++ 2315 Flash Disk Embedded Hub
++ 2316 Flash Disk Security Device
++ 2317 Mass Storage Device
++ 2501 PL2501 USB-USB Bridge (USB 2.0)
++ 2515 Flash Disk Embedded Hub
++ 2517 Flash Disk Mass Storage Device
+ 067c Efficient Networks, Inc.
+ 1001 Siemens SpeedStream 100MBps Ethernet
+- 1022 Siemens 802.11b Wireless Ethernet
++ 1022 Siemens SpeedStream 1022 802.11b Adapter
+ 4060 Alcatel Speedstream 4060 ADSL Modem
+ 067d Hohner Corp.
+ 067e Intermec
+-067f Virata Ltd.
+-0680 Avance Logic, Inc.
++067f Virata, Ltd
++0680 Realtek Semiconductor Corp., CPP Div. (Avance Logic)
++ 0002 Arowana Optical Wheel Mouse MSOP-01
+ 0681 Siemens Information and Communication Products
+ 0002 Gigaset 3075 Passive ISDN
+ 0005 Mouse with Fingerprint Reader
++ 0012 I-Gate 802.11b Adapter
+ 0684 Actiontec Electronics, Inc.
+-0686 Minolta Co., Ltd.
++0686 Minolta Co., Ltd
++ 4003 Dimage 2330 Zoom
++ 4004 Scan Elite II
+ 4006 Dimage 7 digital still camera
++ 4007 Dimage S304 digital still camera
+ 4009 Dimage X digital still camera
+-068a Pertech Inc.
++ 400a Dimage S404 digital still camera
++ 400b Dimage 7i digital still camera
++ 400d Scan Dual III
++ 4014 Dimage S414 digital still camera
++068a Pertech, Inc.
+ 068e CH Products, Inc.
++ 00e2 HFX OEM Joystick
++ 00f2 Flight Sim Pedals
++ 00ff Flight Sim Yoke
+ 0500 GameStick 3D
++ 0501 CH Pro Pedals
+ 0504 F-16 Combat Stick
+-0690 Golden Bridge Electric Co., Ltd.
+-0693 Hagiwara Sys-Com
++0690 Golden Bridge Electech, Inc.
++0693 Hagiwara Sys-Com Co., Ltd
+ 0002 FlashGate SmartMedia Card Reader
+- 0003 FlasgGate CompactFlash Card Reader
++ 0003 FlashGate CompactFlash Card Reader
+ 0005 FlashGate
+ 0694 Lego Group
+ 0001 Mindstorms Tower
+ 0698 Chuntex (CTX)
+ 1786 1300ex Monitor
+ 9999 VLxxxx Monitor+Hub
++0699 Tektronix, Inc.
+ 069a Askey Computer Corp.
+ 0001 VC010 WebCam [pwc]
+-069b Thomson Consumer Electronics
++ 0321 Dynalink WLL013 / Compex WLU11A 802.11b Adapter
++ 0821 BT Voyager 1010 802.11b Adapter
++069b Thomson, Inc.
++ 0704 DCM245 Cable Modem
++ 2220 RCA Kazoo RD1000 MP3 Player
+ 069d Hughes Network Systems (HNS)
+ 0002 Satellite Device
+ 069e Marx
+ 0005 CryptoBox v1.2
++069f Allied Data Technologies BV
++ 0010 Tornado Speakerphone FaxModem 56.0
++06a2 Topro Technology, Inc.
+ 06a3 Saitek PLC
+ 0006 Cyborg Gold Joystick
++ 0200 Xbox Adrenalin Hub
++ 0241 Xbox Adrenalin Gamepad
++ 0422 ST90 Joystick
+ 052d P750 Gamepad
+ 053f X36F Flightstick
+ 100a SP550 Pad and Joystick Combo
+ 100b SP550 Pad
++06a4 Xiamen Doowell Electron Co., Ltd
+ 06a5 Divio
++ 0000 Typhoon Webcam 100k [nw8000]
+ d001 ProLink DS3303u WebCam
+-06aa Sysgration Ltd.
+-06ac Fujitsu PC Corp.
+-06ad Greatland Electronics Taiwan Ltd.
++ d800 Chicony TwinkleCam
++06a8 Topaz Systems, Inc.
++ 0042 SignatureGem 1X5 Pad
++ 0043 SignatureGem 1X5-HID Pad
++06a9 Westell
++06aa Sysgration, Ltd
++06ac Fujitsu Laboratories of America, Inc.
++06ad Greatland Electronics Taiwan, Ltd
+ 06ae Professional Multimedia Testing Centre
+-06b8 Pixela Corproation
++06b8 Pixela Corp.
+ 06b9 Alcatel Telecom
+- 4061 Speed Touch ISDN
+-06ba Smooth Cord & Connector Co., Ltd.
+-06bb EDA Inc.
++ 4061 Speed Touch ISDN or ADSL Modem
++ a5a5 DynaMiTe Modem
++06ba Smooth Cord & Connector Co., Ltd
++06bb EDA, Inc.
+ 06bc Oki Data Corp.
+ 06bd AGFA-Gevaert NV
+ 0001 SnapScan 1212U
+@@ -1232,17 +1715,38 @@
+ 0403 ePhoto CL18 Camera
+ 0404 ePhoto CL20 Camera
+ 2061 SnapScan 1212U (?)
++ 208d Snapscan e40
+ 208f SnapScan e50
++ 2091 SnapScan e20
++ 2093 SnapScan e10
++ 2095 SnapScan e25
++ 2097 SnapScan e26
+ 20fd SnapScan e52
+-06be Asia Microelectronic Development, Inc.
++ 20ff SnapScan e42
++06be AME Optimedia Technology Co., Ltd
+ 06bf Leoco Corp.
++06c2 GLAB Chester
++ 0030 RFID Reader
++ 0038 4-Motor PhidgetServo v3.0
++ 0039 1-Motor PhidgetServo v3.0
++ 003b 8-Motor PhidgetServo
++ 0040 Interface Kit 884
++ 0041 Interface Kit 088
++ 0042 Interface Kit 32-32-0
++ 0043 Interface Kit 0-256-0
++ 0048 Receiver Ver 1.0
++ 0049 PhidgetLED Ver 1.0
++ 004b PhidgetEncoder Ver 1.0
++ 004e PhidgetPower Ver 1.01
++ 0050 PhidgetTextLCD ECMA1010 Ver 1.0
++ 0058 PhidgetGraphicLCD Ver 1.0
+ 06c4 Bizlink International Corp.
+ 06c5 Hagenuk, GmbH
+-06c6 Infowave Software Inc.
++06c6 Infowave Software, Inc.
+ 06c8 SIIG, Inc.
+-06c9 Taxan (Europe) Ltd.
++06c9 Taxan (Europe), Ltd
+ 06ca Newer Technology, Inc.
+-06cb Synaptics
++06cb Synaptics, Inc.
+ 06cc Terayon Communication Systems
+ 06cd Keyspan
+ 0101 USA-28 PDA [preenum]
+@@ -1265,20 +1769,23 @@
+ 1012 PanoCam 12/12X
+ 06d0 LapLink, Inc.
+ 0622 LapLink Gold USB-USB Bridge [net1080]
+-06d1 Daewoo Electronics Co Ltd.
++06d1 Daewoo Electronics Co., Ltd
+ 06d3 Mitsubishi Electric Corp.
+ 06d5 Toshiba
+ 4000 Japanese Keyboard
+ 06d6 Aashima Technology B.V.
+ 06d7 Network Computing Devices (NCD)
+ 06d8 Technical Marketing Research, Inc.
+-06da Phoenixtec Power Co., Ltd.
++06da Phoenixtec Power Co., Ltd
+ 06db Paradyne
+-06dc Compeye Corp.
+-06de Heisei Electronics Co. Ltd.
++06dc Foxlink Image Technology Co., Ltd
++ 0014 Prolink Winscan Pro 2448U
++06de Heisei Electronics Co., Ltd
+ 06e0 Multi-Tech Systems, Inc.
+- f101 MT5634ZBA MultiModem 56k Intl.
+- f104 MT5634ZBA MultiModem 56k
++ f101 MT5634ZBA-USB MultiModemUSB (old firmware)
++ f103 MT5634MU MultiMobileUSB
++ f104 MT5634ZBA-USB MultiModemUSB (new firmware)
++ f107 MT5634ZBA-USB-V92 MultiModemUSB
+ 06e1 ADS Technologies, Inc.
+ 0008 UBS-10BT Ethernet [klsi]
+ 06e4 Alcatel Microelectronics
+@@ -1287,22 +1794,25 @@
+ 0001 NetCom Roadster II 56k
+ 0002 Roadster II 56k
+ 06ef I.A.C. Geometrische Ingenieurs B.V.
+-06f0 T.N.C Industrial Co., Ltd.
+-06f1 Opcode Systems Inc.
+-06f2 Machkey International (USA)
+-06f6 Wintrend Technology Co., Ltd.
++06f0 T.N.C Industrial Co., Ltd
++06f1 Opcode Systems, Inc.
++06f2 Emine Technology Co.
++06f6 Wintrend Technology Co., Ltd
+ 06fa HSD S.r.L
+ 06fd Boston Acoustics
+ 06fe Gallant Computer, Inc.
+ 0701 Supercomal Wire & Cable SDN. BHD.
+-0703 Bencent Tzeng Industry Co., Ltd.
+-0707 Standard Microsystems Corp
++0703 Bvtech Industry, Inc.
++0705 NKK Corp.
++0707 Standard Microsystems Corp.
+ 0100 2202 Ethernet [klsi]
+ 0200 2202 Ethernet [pegasus]
+-0709 Silicon Systems Ltd. (SSL)
+-070a Oki Electric Industry Co., Ltd.
+-070d Comoss Electronic Co., Ltd.
+-0710 Connect Tech Inc.
++0708 Putercom Co., Ltd
++0709 Silicon Systems, Ltd (SSL)
++070a Oki Electric Industry Co., Ltd
++070d Comoss Electronic Co., Ltd
++070e Excel Cell Electronic Co., Ltd
++0710 Connect Tech, Inc.
+ 0001 WhiteHeat (fake ID)
+ 8001 WhiteHeat
+ 0711 Magic Control Technology Corp.
+@@ -1317,21 +1827,25 @@
+ 0714 NewMotion, Inc.
+ 0003 ADB to USB convertor
+ 0718 Imation Corp.
+-0719 Tremon Enterprises Co., Ltd.
++0719 Tremon Enterprises Co., Ltd
+ 071b Domain Technologies, Inc.
+ 0002 DTI-56362-USB Digital Interface Unit
+ 0101 Audio4-USB DSP Data Acquisition Unit
+ 0201 Audio4-5410 DSP Data Acquisition Unit
+ 0301 SB-USB JTAG Emulator
+ 071c Xionics Document Technologies, Inc.
+-071d Eicon Technology Corp.
++071d Eicon Networks Corp.
+ 1000 Diva ISDN TA
+ 0723 Centillium Communications Corp.
+ 0726 Vanguard International Semiconductor-America
+ 0729 Amitm
+ 1000 USC-1000 Serial Port
+-072f ACS, Ltd.
+- 0001 ACR20U SmartCard Reader
++072e Sunix Co., Ltd
++072f Advanced Card Systems, Ltd
++ 0001 AC1030-based SmartCard Reader
++ 9000 ACR38 AC1038-based Smart Card Reader
++0731 Susteen, Inc.
++0732 Goldfull Electronics & Telecommunications Corp.
+ 0733 ViewQuest Technologies, Inc.
+ 0401 CS330 WebCam
+ 0402 M-318B WebCam
+@@ -1340,46 +1854,58 @@
+ 0001 560V Modem
+ 0735 Asuscom Network
+ c541 ISDN TA 280
++0736 Lorom Industrial Co., Ltd
+ 0738 Mad Catz, Inc.
++073b Suncom Technologies
++073d Eutron S.p.a.
++ 0005 Crypto Token
+ 073e NEC, Inc.
+ 0301 Game Pad
+-073b Suncom Technologies
+-0745 Syntech Information Co., Ltd.
++0745 Syntech Information Co., Ltd
+ 0746 Onkyo Corp.
+ 0747 Labway Corp.
+-0748 Strong Man Enterprise Co., Ltd.
++0748 Strong Man Enterprise Co., Ltd
+ 0749 EVer Electronics Corp.
+-074a Ming Fortune Industry Co., Ltd.
++074a Ming Fortune Industry Co., Ltd
+ 074b Polestar Tech. Corp.
+ 074c C-C-C Group PLC
+-074d Micronas Intermetall GmbH
++074d Micronas GmbH
+ 074e Digital Stream Corp.
+ 0001 PS/2 Adapter
+ 0002 PS/2 Adapter
+ 0755 Aureal Semiconductor
++0757 Network Technologies, Inc.
+ 0763 Midiman
+ 1001 Midisport 2x2
+-0757 Network Technologies, Inc.
++ 1010 Midisport 1x1
++ 1020 Midisport 4x4
++ 1030 Midisport 8x8
+ 0764 Cyber Power System, Inc.
+-0765 X-Rite Incorporated
+-0766 Destech Solutions, Inc.
++0765 X-Rite, Inc.
++0766 Jess-Link Products Co., Ltd
+ 0768 Camtel Technology Corp.
+ 0769 Surecom Technology Corp.
+ 076a Smart Technology Enablers, Inc.
+-076b Utimaco Safeware AG
++076b OmniKey AG
++ 0596 CardMan 2020
++ 1784 CardMan 6020
++076c Partner Tech
+ 076d Denso Corp.
+-076e Kuan Tech Enterprise Co., Ltd.
+-076f Jhen Vei Enterprise Co., Ltd.
+-0774 AmTRAN Technology Co., Ltd.
++076e Kuan Tech Enterprise Co., Ltd
++076f Jhen Vei Electronic Co., Ltd
++0774 AmTRAN Technology Co., Ltd
+ 0775 Longshine Electronics Corp.
+ 0776 Inalways Corp.
+ 0777 Comda Enterprise Corp.
+ 0779 Fairchild Semiconductor
+-077a Sankyo Seiki Mfg. Co., Ltd.
++077a Sankyo Seiki Mfg. Co., Ltd
+ 077b Linksys
+-077c Forward Electronics Co., Ltd.
++ 2219 WUSB11 V2.6 802.11b Adapter
++ 2226 USB200M 100baseTX Adapter
++077c Forward Electronics Co., Ltd
+ 0005 NEC Keyboard
+ 077d Griffin Technology
++ 0410 PowerMate
+ 0223 IMic Audio In/Out
+ 077f Well Excellent & Most Corp.
+ 0781 SanDisk Corp.
+@@ -1387,27 +1913,40 @@
+ 0002 SDDR-31 ImageMate II CompactFlash Reader
+ 0005 SDDR-05b (CF II) ImageMate CompactFlash Reader
+ 0200 SDDR-09 (SSFDC) ImageMate SmartMedia Reader [eusb]
++ 0400 SecureMate SD/MMC Reader
++ 0621 SDDR-86 Imagemate 6-in-1 Reader
++ 0810 SDDR-75 ImageMate CF-SM Reader
++ 0830 ImageMate CF/MMC/SD Reader
++ 8185 SDCZ2-nnn Cruzer Mini flash-RAM drive
++ 8889 SDDR-88 Imagemate 8-in-1 Reader
+ 0782 Trackerball
+-0784 Vivitar Inc.
+- 0100 ViviCam 2655
++0784 Vivitar, Inc.
++ 0100 Vivicam 2655
+ 1310 Vivicam 3305
+- 5260 Werlisa Sport PX 100
++ 1688 Vivicam 3665
++ 2888 Polaroid DC700
++ 3330 Nytec ND-3200 Camera
++ 5260 Werlisa Sport PX 100 / JVC GC-A33 Camera
++ 5300 Pretec dc530
+ 0785 NTT-ME
+ 0001 MN128mini-V ISDN TA
+ 0003 MN128mini-J ISDN TA
++0789 Logitec Corp.
+ 078b Happ Controls, Inc.
+ 0010 Driving UGCI
+ 0020 Flying UGCI
+ 0030 Fighting UGCI
+-078e Brimax Inc.
+-0790 Pro-Image Manufacturing Co., Ltd.
++078e Brincom, Inc.
++0790 Pro-Image Manufacturing Co., Ltd
+ 0791 Copartner Wire and Cable Mfg. Corp.
+ 0792 Axis Communications AB
+-0793 Wha Yu Industrial Co., Ltd.
++0793 Wha Yu Industrial Co., Ltd
+ 0794 ABL Electronics Corp.
+-0795 RealChip Inc.
++0795 RealChip, Inc.
+ 0796 Certicom Corp.
+ 0797 Grandtech Semiconductor Corp.
++ 8001 SmartCam
++ 801c Meade Binoculars/Camera
+ 079b Sagem
+ 079d Alfadata Computer Corp.
+ 0201 GamePort Adapter
+@@ -1415,15 +1954,17 @@
+ d952 Palladio USB V.92 Modem
+ 07a2 National Technical Systems
+ 07a3 Onnto Corp.
+-07a4 Be Incorporated
+-07a6 ADMtek Incorporated
++07a4 Be, Inc.
++07a6 ADMtek, Inc.
+ 0986 AN986 Pegasus Ethernet
+ 8511 ADM8511 Pegasus II Ethernet
+-07aa Correga K.K.
++07aa Corega K.K.
+ 0001 Ether USB-T Ethernet [klsi]
+ 0004 FEther USB-TX Ethernet [pegasus]
+-07ab Freecom
++ 0012 Stick-11 802.11b Adapter
++07ab Freecom Technologies
+ fc01 IDE bridge
++ fc03 USB2-IDE IDE bridge
+ 07af Microtech
+ 0004 SCSI-DB25 SCSI Bridge [shuttle]
+ 0005 SCSI-HD50 SCSI Bridge [shuttle]
+@@ -1431,224 +1972,294 @@
+ 07b0 Trust Technologies
+ 0001 ISDN TA
+ 07b1 IMP, Inc.
+-07b2 Motorola ING
++07b2 Motorola BCS, Inc.
+ 4100 SB4100 Cable Modem
+ 07b3 Plustek, Inc.
+ 0001 OpticPro 1212U Scanner
+-07b4 Olympus Optical Co., Ltd.
++ 0010 OpticPro U12 Scanner
++ 0011 OpticPro U24 Scanner
++ 0013 OpticPro UT12 Scanner
++ 0015 OpticPro U24 Scanner
++ 0017 OpticPro UT12/16/24 Scanner
++ 0400 OpticPro 1248U Scanner
++ 0401 OpticPro 1248U Scanner #2
++ 0403 OpticPro U16B Scanner
++07b4 Olympus Optical Co., Ltd
+ 0100 Camedia C-2100/C-3000 Ultra Zoom Camera
+- 0102 Camedia E-10 Camera
+- 0105 Camedia C-700/C-3040 Zoom Camera
+-07b5 Mega World International Ltd.
++ 0102 Camedia E-10/C-220/C-50 Camera
++ 0105 Camedia C-700/C-3040/C-4000 Zoom Camera
++ 0114 C-350Z Camera
++ 0203 Digital Voice Recorder DW-90
++ 0206 Digital Voice Recorder DS-330
++ 0207 Digital Voice Recorder & Camera W-10
++07b5 Mega World International, Ltd
+ 9902 GamePad
+ 07b6 Marubun Corp.
+-07b7 TIME Interconect Ltd.
+-07b8 D-Link, Inc.
+- abc1 DU-E10 Ethernet [pegasus]
++07b7 TIME Interconnect, Ltd
++07b8 D-Link Corp.
+ 4000 DU-E10 Ethernet [klsi]
+ 4002 DU-E100 Ethernet [pegasus]
++ abc1 DU-E10 Ethernet [pegasus]
+ f101 DSB-560 Modem [atlas]
+-07bc Canon Computer Sytems, Inc.
+-07bd Webgear Inc.
++07bc Canon Computer Systems, Inc.
++07bd Webgear, Inc.
+ 07be Veridicom
+-07c4 DataFab Systems, Inc.
++07c0 Code Mercenaries Hard- und Software GmbH
++ 1500 IO-Warrior 40
++ 1501 IO-Warrior 24
++ 1502 IO-Warrior 48
++ 1503 IO-Warrior 28
++07c4 Datafab Systems, Inc.
+ a000 CompactFlash Card Reader
+ a001 CompactFlash & SmartMedia Card Reader [eusb]
+ a002 Disk Drive
+ a005 CompactFlash & SmartMedia Card Reader
+ a006 SmartMedia Card Reader
+ a109 LC1 CompactFlash & SmartMedia Card Reader
++ a200 DF-UT-06 Hama MMC/SD Reader
+ a400 CompactFlash & Microdrive Reader
++ b004 MMC/SD Reader
+ 07c5 APG Cash Drawer
+-07c6 Share Wave, Inc.
+-07c7 Powertech Industrial Co., Ltd.
++07c6 ShareWave, Inc.
++07c7 Powertech Industrial Co., Ltd
+ 07c8 B.U.G., Inc.
+ 07c9 Allied Telesyn International
+ 07ca AVerMedia Technologies, Inc.
+-07cb Kingmax Technology Inc.
+-07cc Carry Inc.
++07cb Kingmax Technology, Inc.
++07cc Carry Computer Eng., Co., Ltd
+ 0000 CF Card Reader
+ 0003 SM Card Reader
+ 0004 SM/CF/PCMCIA Card Reader
+ 0006 SM/CF/PCMCIA Card Reader
+ 000c SM/CF Card Reader
+ 000d SM/CF Card Reader
++ 0200 6-in-1 Card Reader
++ 0301 6-in-1 Card Reader
+ 07cd Elektor
+ 0001 USBuart Serial Port
+-07cf Casio Computer Co., Ltd.
+- 1001 QV-8000SX/3000EX Digicam
++07cf Casio Computer Co., Ltd
++ 1001 QV-8000SX/5700/3000EX Digicam
+ 2002 E-125 Cassiopeia Pocket PC
+ 3801 Casio WMP-1 MP3-Watch
++ 4500 LV-20 Digital Camera
++07d0 Dazzle
++ 0001 Digital Video Creator I
++ 0002 Global Village VideoFX Grabber
++ 0004 DVC-800 (PAL) Grabber
+ 07d1 D-Link System
+-07d2 Aptio Products Inc.
++07d2 Aptio Products, Inc.
+ 07d3 Cyberdata Corp.
+ 07d7 GCC Technologies, Inc.
+ 07da Arasan Chip Systems
++07df David Electronics Co., Ltd
+ 07e1 Ambient Technologies, Inc.
+ 5201 V.90 Modem
+-07e2 Elmeg GmbH & Co., Ltd.
++07e2 Elmeg GmbH & Co., Ltd
+ 07e3 Planex Communications, Inc.
+-07e4 Movado Enterprise Co., Ltd.
++07e4 Movado Enterprise Co., Ltd
+ 07e5 QPS, Inc.
+ 5c01 Que! CDRW
+ 07e6 Allied Cable Corp.
+ 07e7 Mirvo Toys, Inc.
+ 07e8 Labsystems
+-07eb Double-H Technology Co., Ltd.
+-07ec Taiyo Electrical Wire & Cable Co., Ltd.
++07ea Iwatsu Electric Co., Ltd
++07eb Double-H Technology Co., Ltd
++07ec Taiyo Electric Wire & Cable Co., Ltd
++07f6 Circuit Assembly Corp.
++07f7 Century Corp.
++07f9 Dotop Technology, Inc.
+ 07fa Draytek
+ 0778 miniVigor 128 ISDN TA
+-07f6 Circuit Assembly Corp
+-07f7 Century Corp.
+ 07fd Mark of the Unicorn
+ 0000 FastLane MIDI Interface
+ 0801 Mag-Tek
+-0802 Tritheim Technologies, Inc.
++0802 Mako Technologies, LLC
+ 0803 Zoom Telephonics, Inc.
+ 9700 2986L FaxModem
+-0809 Genicom Corp.
+-080a Evermuch Technology Co., Ltd.
+-080d Teco Image Systems Co., Ltd.
++0809 Genicom Technology, Inc.
++080a Evermuch Technology Co., Ltd
++080d Teco Image Systems Co., Ltd
++ 0102 Hercules Scan@home 48
++0810 Personal Communication Systems, Inc.
+ 0813 Mattel, Inc.
++ 0001 Intel Play QX3 Microscope
+ 081a MG Logic
+ 1000 Duo Pen Tablet
++081b Indigita Corp.
+ 081c Mipsys
+-081e Intelligent Peripheral Devices, Inc.
++081e AlphaSmart, Inc.
+ 0822 Reudo Corp.
+ 0825 GC Protronics
+ 0826 Data Transit
+ 0827 BroadLogic, Inc.
+ 0828 Sato Corp.
+-0829 Telocity, Inc.
++0829 DirecTV Broadband, Inc. (Telocity)
+ 082d Handspring
+ 0100 Visor
+-0830 Palm Computing, Inc.
++ 0300 Treo 600
++0830 Palm, Inc.
+ 0002 Palm M505
+ 0003 Palm M515
+ 0020 Palm I705
+ 0040 Palm M125
+ 0050 Palm M130
++ 0060 Palm Tungsten T
+ 0080 Palm
+ 0832 Kouwell Electronics Corp.
+ 0833 Sourcenext Corp.
+-0835 Action Star Enterprise Co., Ltd.
+-0839 Samsung Aerospace Industries Ltd.
++0835 Action Star Enterprise Co., Ltd
++0839 Samsung Techwin Co., Ltd
+ 0005 Digimax Camera
++ 0008 Digimax 230 Camera
++ 1003 Digimax 210SE
++ 1012 6500 Document Camera
++ 1542 Digimax 50 Duo
+ 083a Accton Technology Corp.
+ 1046 10/100 Ethernet [pegasus]
+ 5046 SpeedStream 10/100 Ethernet [pegasus]
+ 083f Global Village
+ b100 TelePort V.90 Fax/Modem
+-0841 Rioport.com Inc.
++0840 Argosy Research, Inc.
++0841 Rioport.com, Inc.
+ 0001 Rio 500
++0844 Welland Industrial Co., Ltd
+ 0846 NetGear, Inc.
+ 1001 EA101 Ethernet [klsi]
+ 084d Minton Optic Industry Co., Inc.
+ 0003 S-Cam F5 Digital Camera
++ 0011 Argus DC3500 Digital Camera
+ 084e KB Gear
+ 1002 Pablo Tablet
+ 084f Empeg
+ 0001 Empeg-Car Mark I/II Player
+ 0850 Fast Point Technologies, Inc.
+-0851 Macronix International Co., Ltd.
++0851 Macronix International Co., Ltd
++ 1543 Maxell WS30 Slim Digital Camera
+ 0852 CSEM
+-0854 ActiveWire Inc.
++0854 ActiveWire, Inc.
+ 0100 I/O Board
+ 0101 I/O Board, rev1
+-0858 Hitachi Maxell Ltd.
++0858 Hitachi Maxell, Ltd
+ 0859 Minolta Systems Laboratory, Inc.
+ 085a Xircom
+ 0001 Portstation Dual Serial Port
++ 0299 Colorvision, Inc. Monitor Spyder
+ 8027 PGSDB9 Serial Port
+ 0862 Teletrol Systems, Inc.
+ 0863 Filanet Corp.
+ 0864 NetGear, Inc.
+- 4100 MA101 Wireless Adapter
++ 4100 MA101 802.11b Adapter
++ 4102 MA101 802.11b Adapter
+ 086a Emagic Soft-und Hardware GmbH
+ 086c DeTeWe - Deutsche Telephonwerke AG & Co.
+ 1001 Eumex 504PC ISDN TA
+-086e System TALKS Inc.
+-086f MEC IMEX Inc/HPT
++086e System TALKS, Inc.
++086f MEC IMEX, Inc.
+ 0870 Metricom
+ 0871 SanDisk, Inc.
+ 0001 SDDR-01 Compact Flash Reader
+ 0002 SDDR-31 Compact Flash Reader
+ 0005 SDDR-05 Compact Flash Reader
+-0873 Xpeed Inc.
++0873 Xpeed, Inc.
++0874 A-Tec Subsystem, Inc.
+ 0879 Comtrol Corp.
+-087c Adesso/Kbtek America Inc.
++087c Adesso/Kbtek America, Inc.
+ 087d Jaton Corp.
+ 087e Fujitsu Computer Products of America
+ 087f Virtual IP Group, Inc.
+-0880 APT Technologies Inc.
++0880 APT Technologies, Inc.
+ 0883 Recording Industry Association of America (RIAA)
+ 0885 Boca Research, Inc.
+ 0886 XAC Automation Corp.
+ 0887 Hannstar Electronics Corp.
+ 088b MassWorks, Inc.
+ 4944 MassWorks ID-75 TouchScreen
+-0892 DioGraphy Inc.
++0892 DioGraphy, Inc.
+ 089c United Technologies Research Cntr.
+-089d Icron Systems Inc.
+-089e NST Co., Ltd.
+-08a5 e9 Inc.
++089d Icron Technologies Corp.
++089e NST Co., Ltd
++089f Primex Aerospace Co.
++08a5 e9, Inc.
+ 08a8 Andrea Electronics
++08ae Macally (Mace Group, Inc.)
++08b4 Sorenson Vision, Inc.
+ 08b8 J. Gordon Electronic Design, Inc.
+ 01f4 USBSIMM1
+-08b9 Tandy Corp./Radio Shack
+-08bb Burr-Brown Japan, Ltd.
+-08bd Citizen Watch Co., Ltd.
++08b9 RadioShack Corp. (Tandy)
++08bb Texas Instruments Japan
++08bd Citizen Watch Co., Ltd
+ 08c3 Precise Biometrics
+ 0101 Precise 100 MC FingerPrint and SmartCard Reader
+ 08c4 Proxim, Inc.
+-08c7 Key Nice Enterprise Co., Ltd.
++08c7 Key Nice Enterprise Co., Ltd
+ 08c8 2Wire, Inc.
+ 08c9 Nippon Telegraph and Telephone Corp.
+-08ca AIPTEK International Inc.
++08ca Aiptek International, Inc.
++ 0010 Tablet
+ 0020 APT-6000U Tablet
++ 0021 APT-2 Tablet
++ 0022 Tablet
++ 0023 Tablet
++ 0024 Tablet
+ 0103 Pocket DV Digital Camera
++ 0111 PenCam VGA Plus
++ 2010 Pocket CAM 3 Mega (webcam)
++ 2011 Pocket CAM 3 Mega (storage)
+ 08cd Jue Hsun Ind. Corp.
+ 08ce Long Well Electronics Corp.
+ 08cf Productivity Enhancement Products
+ 08d1 smartBridges, Inc.
+ 0001 smartNIC Ethernet [catc]
+ 08d3 Virtual Ink
+-08d4 Siemens PC Systems
++08d4 Fujitsu Siemens Computers
++ 0009 SCR SmartCard Reader
++08d9 Increment P Corp.
+ 08dd Billionton Systems, Inc.
+ 0986 USB-100N Ethernet [pegasus]
+ 0987 USBLP-100 HomePNA Ethernet [pegasus]
+ 0988 USBEL-100 Ethernet [pegasus]
+ 8511 USBE-100 Ethernet [pegasus2]
++08de ???
++ 7a01 802.11b Adapter
++08df Spyrus, Inc.
+ 08e3 Olitec, Inc.
+ 0002 USB-RS232 Bridge
++08e4 Pioneer Corp.
+ 08e5 Litronic
+-08e6 GemPlus
++08e6 Gemplus
++ 0430 GemPC430 SmartCard Reader
++ 0432 GemPC432 SmartCard Reader
++ 0435 GemPC435 SmartCard Reader
+ 08e7 Pan-International Wire & Cable
+ 08e8 Integrated Memory Logic
+ 08e9 Extended Systems, Inc.
+ 0100 XTNDAccess IrDA Dongle
+-08ea Ericsson Inc., Blue Ridge Labs
+-08ec M-Systems
++08ea Ericsson, Inc., Blue Ridge Labs
++08ec M-Systems Flash Disk Pioneers
+ 0010 DiskOnKey
++08ee CCSI/Hesso
++08f0 Corex Technologies
+ 08f1 CTI Electronics Corp.
+-08f5 SYSTEC Co., Ltd.
+-08f6 Logic 3 International Limited
+-08f8 Keen Top International Enterprise Co., Ltd.
+-08f9 EnThink, Inc.
++08f5 SysTec Co., Ltd
++08f6 Logic 3 International, Ltd
++08f8 Keen Top International Enterprise Co., Ltd
++08f9 Wipro Technologies
+ 08fa Caere
+ 08fb Socket Communications
+-08fc Sicon Cable Technology Co. Ltd.
++08fc Sicon Cable Technology Co., Ltd
+ 08fd Digianswer A/S
+ 08ff AuthenTec, Inc.
+ 0900 Pinnacle Systems, Inc.
+ 0901 VST Technologies
+ 0906 Faraday Technology Corp.
+-090a Trumpion Microelectronics Inc.
++0909 Audio-Technica Corp.
++090a Trumpion Microelectronics, Inc.
++ 1540 Digitex Container Flash Disk
+ 090b Neurosmith
+ 090c Feiya Technology Corp.
+ 090d Multiport Computer Vertriebs GmbH
+ 090e Shining Technology, Inc.
+-090f Fujitsu Devices Inc.
++090f Fujitsu Devices, Inc.
+ 0910 Alation Systems, Inc.
+ 0911 Philips Speech Processing
+ 0912 Voquette, Inc.
+@@ -1656,49 +2267,64 @@
+ 0917 SmartDisk Corp.
+ 0919 Tiger Electronics
+ 0100 Fast Flicks Digital Camera
++091e Garmin International
++ 0003 GPSmap 60C
+ 0920 Echelon Co.
+ 0921 GoHubs, Inc.
+ 0922 Dymo-CoStar Corp.
++ 0007 LabelWriter 330
++ 0009 LabelWriter 310
+ 0923 IC Media Corp.
++ 010f SIIG MobileCam
+ 0924 Xerox
+-0927 Summus, Ltd.
+-0928 Oxford Semiconductor Ltd.
+-0929 American Biometric Company
++0925 Wisegroup, Ltd
++ 8101 1-Motor PhidgetServo v2.0
++ 8104 4-Motor PhidgetServo v2.0
++ 8866 MP-8866 Dual USB Joypad
++0927 Summus, Ltd
++0928 Oxford Semiconductor, Ltd
++0929 American Biometric Co.
++092a Toshiba Information & Industrial Sys. And Services
++092b Sena Technologies, Inc.
+ 0930 Toshiba Corp.
+-0931 Harmonic Data Systems Ltd.
++0931 Harmonic Data Systems, Ltd
+ 0932 Crescentec Corp.
+ 0933 Quantum Corp.
+ 0934 Netcom Systems
+ 0939 Lumberg, Inc.
+ 093a Pixart Imaging, Inc.
+-093b Plextor
+-093e J.S.T. Mfg. Co., Ltd.
++093b Plextor Corp.
++093d InnoSync, Inc.
++093e J.S.T. Mfg. Co., Ltd
+ 093f Olympia Telecom Vertriebs GmbH
+-0940 Japan Storage Battery Co., Ltd.
++0940 Japan Storage Battery Co., Ltd
+ 0941 Photobit Corp.
+ 0942 i2Go.com, LLC
+-0943 HCL Technologies India Private Limited
++0943 HCL Technologies India Private, Ltd
+ 0944 KORG, Inc.
+ 0945 Pasco Scientific
+ 0948 Kronauer music in digital
+- 1105 USB One
+ 0301 USB Pro (24/48)
+ 0302 USB Pro (24/96 playback)
+ 0303 USB Pro (24/96 record)
+ 0304 USB Pro (16/48)
++ 1105 USB One
++094b Linkup Systems Corp.
+ 094d Cable Television Laboratories
+ 0951 Kingston Technology
+ 000a KNU101TX 100baseTX Ethernet
+ 0954 RPM Systems Corp.
+-0955 NVidia
+-0956 BlueWater Systems, Inc.
++0955 NVidia Corp.
++0956 BSquare Corp.
+ 0957 Agilent Technologies, Inc.
+ 0958 CompuLink Research, Inc.
+-0959 Cologne Chip Designs GmbH
++0959 Cologne Chip AG
+ 095a Portsmith
+ 095b Medialogic Corp.
+ 095c K-Tec Electronics
+ 095d Polycom, Inc.
++0967 Acer (??)
++ 0204 WarpLink 802.11b Adapter
+ 0968 Catalyst Enterprises, Inc.
+ 0971 Gretag-Macbeth AG
+ 0973 Schlumberger
+@@ -1707,19 +2333,21 @@
+ 0976 Adirondack Wire & Cable
+ 0977 Lightsurf Technologies
+ 0978 Beckhoff GmbH
+-0979 Teracom International Inc.
++0979 Jeilin Technology Corp., Ltd
+ 097a Minds At Work LLC
+-097b Knudsen Engineering Limited
+-097c Marunix Co., Ltd.
++097b Knudsen Engineering, Ltd
++097c Marunix Co., Ltd
+ 097d Rosun Technologies, Inc.
+-0981 Oak Technology Ltd.
++097f Barun Electronics Co., Ltd
++0981 Oak Technology, Ltd
++0984 Apricorn
+ 0985 cab Produkttechnik GmbH & Co KG
+ 00a3 A3/200 or A3/300 Label Printer
+ 098c Vitana Corp.
+ 098d INDesign
+-098e Integrated Intellectual Property Inc.
++098e Integrated Intellectual Property, Inc.
+ 098f Kenwood TMI Corp.
+-0993 Gemstar eBook Group, Ltd.
++0993 Gemstar eBook Group, Ltd
+ 0001 REB1100 eBook Reader
+ 0996 Integrated Telecom Express, Inc.
+ 09a3 PairGain Technologies
+@@ -1727,14 +2355,18 @@
+ 09a5 VCON Telecommunications
+ 09a6 Poinchips
+ 09a7 Data Transmission Network Corp.
+-09a8 Shinestar Enterprise Co., Ltd.
+-09a9 Smart Card Technologies Co., Ltd.
++09a8 Lin Shiung Enterprise Co., Ltd
++09a9 Smart Card Technologies Co., Ltd
+ 09aa Intersil Corp.
+-09b2 Franklin Electronic Publishers
+- 0001 eBookman Palm Computer
++ 3642 Prism2.x 802.11b Adapter
++09ae Tripp Lite
++09b2 Franklin Electronic Publishers, Inc.
++ 0001 eBookman Palm Computer
+ 09b3 Altius Solutions, Inc.
+ 09b4 MDS Telephone Systems
+-09b5 Celltrix Technology Co., Ltd.
++09b5 Celltrix Technology Co., Ltd
++09bc Grundig
++ 0002 MPaxx MP150 MP3 Player
+ 09be MySmart.Com
+ 0001 MySmartPad
+ 09bf Auerswald GmbH & Co. KG
+@@ -1742,129 +2374,1002 @@
+ 00db COMpact 4410/2206 ISDN ISDN
+ 00f1 COMfort System Telephones
+ 09c1 Arris Interactive LLC
+-09c3 ActivCard, INC.
++09c2 Nisca Corp.
++09c3 ActivCard, Inc.
+ 09c4 ACTiSYS Corp.
+- 0011 ACT-IR2000U IrDA Dongle
++ 0011 ACT-IR2000U IrDA Dongle
+ 09c5 Memory Corp.
+ 09cc Workbit Corp.
+-09cd Psion Dacom Home Networks Ltd.
+-09ce City Electronics Ltd.
++09cd Psion Dacom Home Networks, Ltd
++09ce City Electronics, Ltd
+ 09cf Electronics Testing Center, Taiwan
+-09d1 NeoMagic Inc.
+-09d2 Vreelin Engineering Inc.
++09d1 NeoMagic, Inc.
++09d2 Vreelin Engineering, Inc.
+ 09d3 Com One
+-09d9 KRF Tech Ltd.
+-09da A4 Tech Co., Ltd.
+-09db ComputerBoards Inc.
++ 0001 ISDN TA
++09d9 KRF Tech, Ltd
++09da A4 Tech Co., Ltd
++ 001a Wireless Mouse & RXM-15 Receiver
++09db Measurement Computing Corp.
+ 09dc Aimex Corp.
+-09dd Fellowes Manufacturing Co.
++09dd Fellowes, Inc.
+ 09df Addonics Technologies Corp.
++09e1 Intellon Corp.
+ 09e5 Jo-Dan International, Inc.
+ 09e6 Silutia, Inc.
+ 09e7 Real 3D, Inc.
+-09e8 AKAI Professional M.I. Corp.
+-09e9 Chen-Source Inc.
++09e8 AKAI Professional M.I. Corp.
++09e9 Chen-Source, Inc.
++09eb IM Networks, Inc.
++ 4331 iRhythm Tuner Remote
+ 09ef Xitel
+- 0101 MD-Port DG2 MiniDisc Interface
++ 0101 MD-Port DG2 MiniDisc Interface
+ 09f5 AresCom
+ 09f6 RocketChips, Inc.
+-09f7 Edu-Science (H.K.) Ltd.
+-09f8 SoftConnex
++09f7 Edu-Science (H.K.), Ltd
++09f8 SoftConnex Technologies, Inc.
+ 09f9 Bay Associates
+ 09fa Mtek Vision
+ 09fb Altera
+ 09ff Gain Technology Corp.
+ 0a00 Liquid Audio
+ 0a01 ViA, Inc.
+-0a11 Xentec Incorporated
+-0a12 Cambridge Silicon Radio Ltd.
+-0a13 Telebyte Inc.
+-0a14 Spacelabs Medical Inc.
++0a07 Ontrak Control Systems Inc.
++ 0064 ADU100 Data Acquisition Interface
++ 00c8 ADU200 Relay I/O Interface
++ 00d0 ADU208 Data Acquisition Interface
++0a0b Cybex Computer Products Co.
++0a11 Xentec, Inc.
++0a12 Cambridge Silicon Radio, Ltd
++ 0001 Bluetooth Dongle
++0a13 Telebyte, Inc.
++0a14 Spacelabs Medical, Inc.
+ 0a15 Scalar Corp.
+-0a16 Trek Technology (S) PTE Ltd.
++0a16 Trek Technology (S) PTE, Ltd
+ 9988 Trek2000 TD-G2
+-0a17 Asahi Optical Co., Ltd.
++0a17 Pentax Corp.
+ 0004 Pentax Optio 330
++ 0006 Pentax Optio S
+ 0a18 Heidelberger Druckmaschinen AG
+-0a19 Hua Geng Technologies Inc.
+-0a43 Boca Systems Inc.
++0a19 Hua Geng Technologies, Inc.
++0a21 Medtronic Physio Control Corp.
++0a22 Century Semiconductor USA, Inc.
++0a2c AK-Modul-Bus Computer GmbH
++ 0008 GPIO Ports
++0a39 Gilat Satellite Networks, Ltd
++0a3a PentaMedia Co., Ltd
++0a3c NTT DoCoMo, Inc.
++0a3d Varo Vision
++0a43 Boca Systems, Inc.
++0a46 Davicom Semiconductor, Inc.
++0a47 Hirose Electric
++0a48 I/O Interconnect
++ 3258 Dane Elec zMate SD Reader
++ 3259 Dane Elec zMate CF Reader
++0a4b Fujitsu Media Devices, Ltd
++0a4c Computex Co., Ltd
++0a4d Evolution Electronics, Ltd
++ 008e MK-249C MIDI Keyboard
++0a4e Steinberg Soft-und Hardware GmbH
++0a4f Litton Systems, Inc.
++0a50 Mimaki Engineering Co., Ltd
++0a51 Sony Electronics, Inc.
++0a52 Jebsee Electronics Co., Ltd
++0a53 Portable Peripheral Co., Ltd
++0a5a Electronics For Imaging, Inc.
++0a5b EAsics NV
+ 0a5c Broadcom Corp.
++ 2033 BCM2033 Bluetooth
++ 2035 BCM2035 Bluetooth
++0a5d Diatrend Corp.
+ 0a5f Zebra
+ 0009 LP2844 Printer
+-0a6b Green House
++0a62 MPMan
++ 0010 MPMan MP-F40 MP3 Player
++0a66 ClearCube Technology
++0a67 Medeli Electronics Co., Ltd
++0a68 Comaide Corp.
++0a69 Chroma ate, Inc.
++0a6b Green House Co., Ltd
+ 0001 Compact Flash R/W with MP3 player
++0a6c Integrated Circuit Systems, Inc.
++0a6d UPS Manufacturing
++0a6e Benwin
++0a6f Core Technology, Inc.
++0a70 International Game Technology
++0a72 Sanwa Denshi
++0a7d NSTL, Inc.
++0a7e Octagon Systems Corp.
++0a80 Rexon Technology Corp., Ltd
++0a81 Chesen Electronics Corp.
++ 0101 Keyboard
++ 0203 Mouse
++0a82 Syscan
++ 4600 TravelScan 460/464
++0a83 NextComm, Inc.
++0a84 Maui Innovative Peripherals
++0a85 Idexx Labs
++0a86 NITGen Co., Ltd
++0a8d Picturetel
++0a8e Japan Aviation Electronics Industry, Ltd
++0a90 Candy Technology Co., Ltd
++0a91 Globlink Technology, Inc.
++0a92 EGO SYStems, Inc.
++0a93 C Technologies AB
++0a94 Intersense
++0aa3 Lava Computer Mfg., Inc.
++0aa4 Develco Elektronik
++0aa5 First International Digital
++0aa6 Perception Digital, Ltd
++ 0101 Hercules Jukebox
++0aa7 Wincor Nixdorf GmbH & Co KG
++0aa8 TriGem Computer, Inc.
++0aa9 Baromtec Co.
++ f01b Medion MD 6242 MP3 Player
++0aaa Japan CBM Corp.
++0aab Vision Shape Europe SA
++0aac iCompression, Inc.
++0aad Rohde & Schwarz GmbH & Co. KG
++0aae NEC infrontia Corp. (Nitsuko)
++0aaf Digitalway Co., Ltd
++0ab0 Arrow Strong Electronics Co., Ltd
++0aba Ellisys
++ 8001 USB Tracker 110 Protocol Analyzer
+ 0abe Stereo-Link
+ 0101 SL1200 DAC
+-0aec Neodio
++0ac3 Sanyo Semiconductor Company Micro
++0ac4 Leco Corp.
++0ac5 I & C Corp.
++0ac6 Singing Electrons, Inc.
++0ac7 Panwest Corp.
++0ac8 Z-Star Microelectronics Corp.
++0ac9 Micro Solutions, Inc.
++ 0000 Backpack CD-ReWriter
++ 0011 Backpack 40GB Hard Drive
++0acc Koga Electronics Co.
++0acd ID Tech
++0acf Intoto, Inc.
++0ad0 Intellix Corp.
++0ad1 Remotec Technology, Ltd
++0ad2 Service & Quality Technology Co., Ltd
++0ae3 Allion Computer, Inc.
++0ae4 Taito Corp.
++0ae7 Neodym Systems, Inc.
++0ae8 System Support Co., Ltd
++0ae9 North Shore Circuit Design L.L.P.
++0aea SciEssence, LLC
++0aeb TTP Communications, Ltd
++0aec Neodio Technologies Corp.
++ 3050 ND3050 8-in-1 Card Reader
+ 5010 ND5010 Card Reader
++0af0 Option
++ 5000 UMTS Card
++0af6 Silver I Co., Ltd
++0af7 B2C2, Inc.
+ 0af9 Hama, Inc.
+ 0010 USB SightCam 100
++0afc Zaptronix Ltd
++0afd Tateno Dennou, Inc.
++0afe Cummins Engine Co.
++0aff Jump Zone Network Products, Inc.
++0b05 ASUSTek Computer, Inc.
++0b0e GN Netcom
++0b0f AVID Technology
++0b10 Pcally
++0b11 I Tech Solutions Co., Ltd
++0b1e Electronic Warfare Assoc., Inc. (EWA)
++0b1f Insyde Software Corp.
++0b20 TransDimension, Inc.
++0b21 Yokogawa Electric Corp.
++0b22 Japan System Development Co., Ltd
++0b23 Pan-Asia Electronics Co., Ltd
++0b24 Link Evolution Corp.
++0b27 Ritek Corp.
++0b28 Kenwood Corp.
++0b2c Village Center, Inc.
+ 0b30 PNY Technologies, Inc.
+ 0006 SM Media-Shuttle Card Reader
+-0b3b Tekram Technology, Co., Ltd.
+- 1612 AIR.Mate 2@net
+-0c70 MCT Elektronikladen
+- 0000 USB08 Development board
+-0c76 JMTek, LLC.
+- 0003 USBdisk
+- 0005 USBdisk
+-0bda RealTek Semiconductor Corp.
++0b33 Contour Design, Inc.
++0b37 Hitachi ULSI Systems Co., Ltd
++0b39 Omnidirectional Control Technology, Inc.
++0b3a IPaxess
++0b3b Tekram Technology Co., Ltd
++ 1601 Allnet 0193 802.11b Adapter
++ 1602 ZyXEL ZyAIR B200 802.11b Adapter
++ 1612 AIR.Mate 2@net 802.11b Adapter
++0b3c Olivetti Techcenter
++0b3e Kikusui Electronics Corp.
++0b41 Hal Corp.
++0b43 Play.com, Inc.
++ 0003 PS2 Controller Converter
++0b47 Sportbug.com, Inc.
++0b48 TechnoTrend AG
++0b49 ASCII Corp.
++0b4b Pine Corp. Ltd.
++ 0100 D'music MP3 Player
++0b4e Musical Electronics, Ltd
++0b50 Dumpries Co., Ltd
++0b52 Colorado MicroDisplay, Inc.
++0b54 Sinbon Electronics Co., Ltd
++0b56 TYI Systems, Ltd
++0b57 Beijing HanwangTechnology Co., Ltd
++0b59 Lake Communications, Ltd
++0b5a Corel Corp.
++0b5f Green Electronics Co., Ltd
++0b60 Nsine, Ltd
++0b61 NEC Viewtechnology, Ltd
++0b62 Orange Micro, Inc.
++ 0059 iBOT2 WebCam
++0b63 ADLink Technology, Inc.
++0b64 Wonderful Wire Cable Co., Ltd
++0b65 Expert Magnetics Corp.
++0b69 CacheVision
++0b6a Maxim Integrated Products
++0b6f Nagano Japan Radio Co., Ltd
++0b70 PortalPlayer, Inc.
++0b71 SHIN-EI Sangyo Co., Ltd
++0b72 Embedded Wireless Technology Co., Ltd
++0b73 Computone Corp.
++0b75 Roland DG Corp.
++0b79 Sunrise Telecom, Inc.
++0b7a Zeevo, Inc.
++0b7b Taiko Denki Co., Ltd
++0b7c ITRAN Communications, Ltd
++0b7d Astrodesign, Inc.
++0b84 Rextron Technology, Inc.
++0b85 Elkat Electronics, Sdn., Bhd.
++0b86 Exputer Systems, Inc.
++0b87 Plus-One I & T, Inc.
++0b88 Sigma Koki Co., Ltd, Technology Center
++0b89 Advanced Digital Broadcast, Ltd
++0b95 ASIX Electronics Corp.
++0b96 Sewon Telecom
++0b97 O2 Micro, Inc.
++0b98 Playmates Toys, Inc.
++0b99 Audio International, Inc.
++0b9d Softprotec Co.
++0b9f Chippo Technologies
++0baf U.S. Robotics
++ 00eb USR1120 802.11b Adapter
++ 6112 FaxModem Model 5633
++0bb0 Concord Camera Corp.
++0bb1 Infinilink Corp.
++0bb2 Ambit Microsystems Corp.
++ 6098 USB Cable Modem
++0bb3 Ofuji Technology
++0bb4 High Tech Computer Corp.
++ 00ce mmO2 XDA GSM/GPRS Pocket PC
++ 0a02 Himalaya GSM/GPRS Pocket PC
++0bb5 Murata Manufacturing Co., Ltd
++0bb6 Network Alchemy
++0bb7 Joytech Computer Co., Ltd
++0bb8 Hitachi Semiconductor and Devices Sales Co., Ltd
++0bb9 Eiger M&C Co., Ltd
++0bba ZAccess Systems
++0bbb General Meters Corp.
++0bbc Assistive Technology, Inc.
++0bbd System Connection, Inc.
++0bc0 Knilink Technology, Inc.
++0bc1 Fuw Yng Electronics Co., Ltd
++0bc2 Seagate RSS LLC
++0bc3 IPWireless, Inc.
++0bc4 Microcube Corp.
++0bc5 JCN Co., Ltd
++0bc6 ExWAY, Inc.
++0bc7 X10 Wireless Technology, Inc.
++ 0004 X10 Receiver
++0bc8 Telmax Communications
++0bc9 ECI Telecom, Ltd
++0bca Startek Engineering, Inc.
++0bcb Perfect Technic Enterprise Co., Ltd
++0bda Realtek Semiconductor Corp.
+ 8150 RTL8150 Fast Ethernet Adapter
+- 8151 RTL8151 HomePNA Adapter
++ 8151 RTL8151 Adapteon Business Mobile Networks BV
++0bdb Ericsson Business Mobile Networks BV
++0bdc Y Media Corp.
++0bdd Orange PCS
++0be2 Kanda Tsushin Kogyo Co., Ltd
++0be3 TOYO Corp.
++0be4 Elka International, Ltd
++0be5 DOME imaging systems, Inc.
++0be6 Dong Guan Humen Wonderful Wire Cable Factory
++0bee LTK Industries, Ltd
++0bef Way2Call Communications
++0bf0 Pace Micro Technology PLC
++0bf1 Intracom S.A.
++0bf2 Konexx
+ 0bf6 Addonics Technologies, Inc.
+ a002 IDE Bridge
++0bf7 Sunny Giken, Inc.
++0bf8 Fujitsu Siemens Computers
++0c04 MOTO Development Group, Inc.
++0c05 Appian Graphics
++0c06 Hasbro Games, Inc.
++0c07 Infinite Data Storage, Ltd
+ 0c08 Agate
+ 0378 Q 16MB Storage Device
+-0c0b Acomdata
+- b004 MMC/SD Reader and Writer
++0c09 Comjet Information System
++0c0a Highpoint Technologies, Inc.
++0c0b Dura Micro, Inc. (Acomdata)
++ 27cb 6-in-1 Flash Reader and Writer
+ a109 CF/SM Reader and Writer
+ a10c SD/MS Reader and Writer
++ b004 MMC/SD Reader and Writer
++0c12 Zeroplus
++ 0005 PSX Vibration Feedback Converter
++0c15 Iris Graphics
++0c16 Gyration, Inc.
++0c17 Cyberboard A/S
++0c18 SynerTek Korea, Inc.
++0c19 cyberPIXIE, Inc.
++0c1a Silicon Motion, Inc.
++0c1b MIPS Technologies
++0c1c Hang Zhou Silan Electronics Co., Ltd
++0c22 Tally Printer Corp.
++0c23 Lernout + Hauspie
++0c24 Taiyo Yuden
++0c25 Sampo Corp.
++0c35 Eagletron, Inc.
++0c36 E Ink Corp.
++0c37 e.Digital
++0c38 Der An Electric Wire & Cable Co., Ltd
++0c39 IFR
++0c3a Furui Precise Component (Kunshan) Co., Ltd
++0c3b Komatsu, Ltd
++0c3c Radius Co., Ltd
++0c3d Innocom, Inc.
++0c3e Nextcell, Inc.
++0c44 Motorola iDEN
++0c45 Microdia
++ 1060 iFlash SM-Direct Card Reader
++ 6001 Genius VideoCAM NB
++ 6029 Triplex i-mini PC Camera
++ 602a Meade ETX-105EC Camera
++0c46 WaveRider Communications, Inc.
++0c52 Sealevel Systems, Inc.
++0c53 ViewPLUS, Inc.
++0c54 Glory, Ltd
++0c55 Spectrum Digital, Inc.
++ 0510 Spectrum Digital XDS510 JTAG Debugger
++0c56 Billion Bright, Ltd
++0c57 Imaginative Design Operation Co., Ltd
++0c58 Vidar Systems Corp.
++0c59 Dong Guan Shinko Wire Co., Ltd
++0c5a TRS International Mfg., Inc.
+ 0c5e Xytronix Research & Design
++0c62 Chant Sincere Co., Ltd
++0c63 Toko, Inc.
++0c64 Signality System Engineering Co., Ltd
++0c65 Eminence Enterprise Co., Ltd
++0c66 Rexon Electronics Corp.
++0c67 Concept Telecom, Ltd
++0c70 MCT Elektronikladen
++ 0000 USB08 Development board
++0c74 Optronic Laboratories Inc.
++ 0002 OL 700-30 Goniometer
++0c76 JMTek, LLC.
++ 0003 USBdisk
++ 0005 USBdisk
++ 0006 Transcend JetFlash
++0c77 Sipix Group, Ltd
++0c78 Detto Corp.
++0c79 NuConnex Technologies Pte., Ltd
++0c7a Wing-Span Enterprise Co., Ltd
+ 0c86 NDA Technologies, Inc.
++0c88 Kyocera Wireless Corp.
++0c89 Honda Tsushin Kogyo Co., Ltd
++0c8a Pathway Connectivity, Inc.
++0c8b Wavefly Corp.
++0c8c Coactive Networks
++0c8d Tempo
++0c8e Cesscom Co., Ltd
++0c8f Applied Microsystems
++0c99 Innochips Co., Ltd
++0c9a Hanwool Robotics Corp.
++0c9b Jobin Yvon, Inc.
++0ca2 Zyfer
++0ca3 Sega Corp.
++0ca4 ST&T Instrument Corp.
++0ca5 BAE Systems Canada, Inc.
++0ca6 Castles Technology Co., Ltd
++0ca7 Information Systems Laboratories
++0cad Motorola CGISS
++0cae Ascom Business Systems, Ltd
++0caf Buslink
++ 3a00 Hard Drive
++0cb0 Flying Pig Systems
++0cb1 Innovonics, Inc.
++0cb6 Celestix Networks, Pte., Ltd
++0cb7 Singatron Enterprise Co., Ltd
++0cb8 Opticis Co., Ltd
++0cba Trust Electronic (Shanghai) Co., Ltd
++0cbb Shanghai Darong Electronics Co., Ltd
++0cbc Palmax Technology Co., Ltd
++0cbd Pentel Co., Ltd (Electronics Equipment Div.)
++0cbe Keryx Technologies, Inc.
++0cbf Union Genius Computer Co., Ltd
++0cc0 Kuon Yi Industrial Corp.
++0cc1 Given Imaging, Ltd
++0cc2 Timex Corp.
++0cc3 Rimage Corp.
++0cc4 emsys GmbH
++0cc5 Sendo
++0cc6 Intermagic Corp.
++0cc7 Kontron Medical AG
++0cc8 Technotools Corp.
++0cc9 BroadMAX Technologies, Inc.
++0cca Amphenol
++0ccb SKNet Co., Ltd
++0ccc Domex Technology Corp.
++0ccd TerraTec Electronic GmbH
++0cd4 Bang Olufsen
++ 0101 BeolinkPC2
++0cd7 NewChip S.r.l.
++0cd8 JS Digitech, Inc.
++0cd9 Hitachi Shin Din Cable, Ltd
++0cde Z-Com
++ 0002 XI-725/726 Prism2.5 802.11b Adapter
++ 0005 XI-735 Prism3 802.11b Adapter
++0cf1 e-Conn Electronic Co., Ltd
++0cf2 ENE Technology, Inc.
++0cf3 Atheros Communications, Inc.
++0cf4 Fomtex Corp.
++0cf5 Cellink Co., Ltd
++0cf6 Compucable Corp.
++0cf7 ishoni Networks
++0cf8 Clarisys, Inc.
++0cf9 Central System Research Co., Ltd
++0cfa Inviso, Inc.
++0cfc Minolta-QMS, Inc.
+ 0d06 telos EDV Systementwicklung GmbH
+-0d7d Apacer
+- 0100 HandyDrive 64MB
+-0d8e Repotec
+- 7100 Wireless 802.11b Ethernet
+-0d96 Traveler
+- 3300 SX330z Digital Camera
+-0dbf Pocketec
+-0dcd NetworkFab Corporation
++0d0b Contemporary Controls
++0d0c Astron Electronics Co., Ltd
++0d0d MKNet Corp.
++0d0e Hybrid Networks, Inc.
++0d0f Feng Shin Cable Co., Ltd
++0d10 Elastic Networks
++0d11 Maspro Denkoh Corp.
++0d12 Hansol Electronics, Inc.
++0d13 BMF Corp.
++0d14 Array Comm, Inc.
++0d15 OnStream b.v.
++0d16 Hi-Touch Imaging Technologies Co., Ltd
++0d17 NALTEC, Inc.
++0d18 coaXmedia
++0d19 Hank Connection Industrial Co., Ltd
++0d32 Leo Hui Electric Wire & Cable Co., Ltd
++0d33 AirSpeak, Inc.
++0d34 Rearden Steel Technologies
++0d35 Dah Kun Co., Ltd
++0d3c Sri Cable Technology, Ltd
++0d3d Tangtop Technology Co., Ltd
++0d3e Fitcom, inc.
++0d3f MTS Systems Corp.
++0d40 Ascor, Inc.
++0d41 Ta Yun Terminals Industrial Co., Ltd
++0d42 Full Der Co., Ltd
++0d49 Maxtor
++0d4a NF Corp.
++0d4b Grape Systems, Inc.
++0d4c Tedas AG
++0d4d Coherent, Inc.
++0d4e Agere Systems Netherland BV
++0d4f EADS Airbus France
++0d50 Cleware GmbH
++0d51 Volex (Asia) Pte., Ltd
++0d53 HMI Co., Ltd
++0d54 Holon Corp.
++0d55 ASKA Technologies, Inc.
++0d56 AVLAB Technology, Inc.
++0d57 Solomon Microtech, Ltd
++0d5c Belkin
++ a002 F5D6050 802.11b Adapter
++0d5e Myacom, Ltd
++0d5f CSI, Inc.
++0d60 IVL Technologies, Ltd
++0d61 Meilu Electronics (Shenzhen) Co., Ltd
++0d62 Darfon Electronics Corp.
++ a100 Benq Mouse
++0d63 Fritz Gegauf AG
++0d64 DXG Technology Corp.
++ 0107 Horus MT-409 Camera
++0d65 KMJP Co., Ltd
++0d66 TMT
++0d67 Advanet, Inc.
++0d68 Super Link Electronics Co., Ltd
++0d69 NSI
++0d6a Megapower International Corp.
++0d6b And-Or Logic
++0d70 Try Computer Co., Ltd
++0d71 Hirakawa Hewtech Corp.
++0d72 Winmate Communication, Inc.
++0d73 Hit's Communications, Inc.
++0d76 MFP Korea, Inc.
++0d77 Power Sentry/Newpoint
++0d78 Japan Distributor Corp.
++0d7a MARX Datentechnik GmbH
++0d7b Wellco Technology Co., Ltd
++0d7c Taiwan Line Tek Electronic Co., Ltd
++0d7d Phison Electronics Corp.
++ 0100 PS1001/1011/1006/1026 Flash Disk
++ 0110 Gigabyte FlexDrive
++ 0240 I/O Magic Drive
++ 110E NEC uPD720121/130 USB-ATA/ATAPI Bridge
++ 1240 Apacer 6-in-1 Card Reader 2.0
++ 1300 Flash Disk
++ 1320 PS2031 Flash Disk
++ 1420 PS2044 Pen Drive
++0d7e American Computer & Digital Components
++0d7f Essential Reality LLC
++0d80 H.R. Silvine Electronics, Inc.
++0d81 TechnoVision
++0d83 Think Outside, Inc.
++0d89 Oz Software
++0d8a King Jim Co., Ltd
++0d8b Ascom Telecommunications, Ltd
++0d8c C-Media Electronics, Inc.
++0d8d Promotion & Display Technology, Ltd
++0d8e Global Sun Technology, Inc.
++ 7100 802.11b Adapter
++ 7a01 PRISM25 802.11b Adapter
++0d8f Pitney Bowes
++0d90 Sure-Fire Electrical Corp.
++0d96 Skanhex Technology, Inc.
++ 3300 SX330z Camera
++ 4100 SX410z Camera
++ 4102 MD 9700 Camera
++ 5200 SX-520z Camera
++0d97 Santa Barbara Instrument Group
++ 0001 SBIG Astronomy Camera (without firmware)
++ 0101 SBIG Astronomy Camera (with firmware)
++0d98 Mars Semiconductor Corp.
++0d99 Trazer Technologies, Inc.
++0d9a RTX Telecom AS
++0d9b Tat Shing Electrical Co.
++0d9c Chee Chen Hi-Technology Co., Ltd
++0d9d Sanwa Supply, Inc.
++0d9e Avaya
++0d9f Powercom Co., Ltd
++0da0 Danger Research
++0da1 Suzhou Peter's Precise Industrial Co., Ltd
++0da2 Land Instruments International, Ltd
++0da3 Nippon Electro-Sensory Devices Corp.
++0da4 Polar Electro OY
++0da7 IOGear, Inc.
++0dab Cubig Group
++ 0100 DVR/CVR-M140 MP3 Player
++0dad Westover Scientific
++0db0 Micro Star International
++ 6982 Medion Flash XL V2.7A Card Reader
++0db1 Wen Te Electronics Co., Ltd
++0db2 Shian Hwi Plug Parts, Plastic Factory
++0db3 Tekram Technology Co., Ltd
++0db4 Chung Fu Chen Yeh Enterprise Corp.
++0dbe Jiuh Shiuh Precision Industry Co., Ltd
++0dbf Quik Tech Solutions
++0dc0 Great Notions
++0dc1 Tamagawa Seiki Co., Ltd
++0dc3 Athena Smartcard Solutions, Inc.
++0dc4 Macpower Peripherals, Ltd
++0dc5 SDK Co., Ltd
++0dc6 Precision Squared Technology Corp.
++0dc7 First Cable Line, Inc.
++0dcd NetworkFab Corp.
+ 0001 Remote Interface Adapter
+ 0002 High Bandwidth Codec
++0dd1 Contek Electronics Co., Ltd
++0dd2 Power Quotient International Co., Ltd
++0dd3 MediaQ
++0dd4 Custom Engineering SPA
++0dd5 California Micro Devices
++0dd7 Kocom Co., Ltd
++0dd9 HighSpeed Surfing
++0dda Integrated Circuit Solution, Inc.
++0ddb Tamarack, Inc.
++0ddd Datelink Technology Co., Ltd
++0dde Ubicom, Inc.
++0de0 BD Consumer Healthcare
++0ded Novasonics
++0dee Lifetime Memory Products
++0def Full Rise Electronic Co., Ltd
++0df6 Sitecom Europe B.V.
++0df7 Mobile Action Technology, Inc.
++ 0620 MA-620 USB Infrared Adapter
++0dfa Toyo Communication Equipment Co., Ltd
++0dfc GeneralTouch Technology Co., Ltd
++ 0001 Touchscreen
++0e03 Nippon Systemware Co., Ltd
++0e08 Winbest Technology Co., Ltd
+ 0e0c Gesytec
+ 0101 LonUSB LonTalk Network Adapter
++0e16 JMTek, LLC
++0e17 Walex Electronic, Ltd
+ 0e1b Crewave
++0e21 Cowon Systems, Inc.
++ 0300 iAudio CW200
++0e23 Liou Yuane Enterprise Co., Ltd
++0e25 VinChip Systems, Inc.
++0e26 J-Phone East Co., Ltd
++0e30 HeartMath LLC
++0e34 Micro Computer Control Corp.
++0e35 3Pea Technologies, Inc.
++0e36 TiePie engineering
++0e38 Stratitec, Inc.
++0e39 Smart Modular Technologies, Inc.
++0e3a Neostar Technology Co., Ltd
+ 1100 CW-1100 Wireless Network Adapter
+-0e48 Julia Corp., Ltd.
++0e3b Mansella, Ltd
++0e48 Julia Corp., Ltd
+ 0100 CardPro SmartCard Reader
++0e4a Shenzhen Bao Hing Electric Wire & Cable Mfr. Co.
++0e4c Radica Games, Ltd
++0e55 Speed Dragon Multimedia, Ltd
++0e5a Active Co., Ltd
++0e5b Union Power Information Industrial Co., Ltd
++0e5c Bitland Information Technology Co., Ltd
++0e5d Neltron Industrial Co., Ltd
+ 0e66 Hawking
+ 400c UF100 Ethernet [pegasus2]
+-0e75 TVS Electronics, Ltd.
+-0ef7 Tulip Computers International
++0e6a Megawin Technology Co., Ltd
++0e70 Tokyo Electronic Industry Co., Ltd
++0e72 Hsi-Chin Electronics Co., Ltd
++0e75 TVS Electronics, Ltd
++0e7b On-Tech Industry Co., Ltd
++0e7e Gmate Inc.
++ 0001 Yopy 3000 PDA
++0e82 Ching Tai Electric Wire & Cable Co., Ltd
++0e8c Well Force Electronic Co., Ltd
++0e90 WiebeTech, LLC
++0e91 VTech Engineering Canada, Ltd
++0e92 C's Glory Enterprise Co., Ltd
++0e93 eM Technics Co., Ltd
++0e95 Future Technology Co., Ltd
++0e96 Aplux Communications, Ltd
++0e97 Fingerworks, Inc.
++0e98 Advanced Analogic Technologies, Inc.
++0e99 Parallel Dice Co., Ltd
++0e9a TA HSING Industries, Ltd
++0e9b ADTEC Corp.
++0e9f Tamura Corp.
++0ea0 Ours Technology, Inc.
++ 2168 Transcend JetFlash 2.0
++ 6803 OTI-6803 Flash Disk
++ 6808 OTI-6808 Flash Disk
++ 6828 OTI-6828 Flash Disk
++0ea6 Nihon Computer Co., Ltd
++0ea7 MSL Enterprises Corp.
++0ea8 CenDyne, Inc.
++0ead Humax Co., Ltd
++0eb1 WIS Technologies, Inc.
++0eb2 Y-S Electronic Co., Ltd
++0eb3 Saint Technology Corp.
++0eb7 Endor AG
++0ebe VWeb Corp.
++0ebf Omega Technology of Taiwan, Inc.
++0ec0 LHI Technology (China) Co., Ltd
++0ec1 Abit Computer Corp.
++0ec2 Sweetray Industrial, Ltd
++0ec3 Axell Co., Ltd
++0ec4 Ballracing Developments, Ltd
++0ec5 GT Information System Co., Ltd
++0ec6 InnoVISION Multimedia, Ltd
++0ec7 Theta Link Corp.
++ 1008 So., Show 301 Digital Camera
++0ecd Lite-On IT Corp.
++0ece TaiSol Electronics Co., Ltd
++0ecf Phogenix Imaging, LLC
++0ed1 WinMaxGroup
++ 6660 USB Flash Disk 64M-C
++0ed2 Kyoto Micro Computer Co., Ltd
++0ed3 Wing-Tech Enterprise Co., Ltd
++0eda Noriake Itron Corp.
++0edf e-MDT Co., Ltd
++0ee0 Shima Seiki Mfg., Ltd
++0ee1 Sarotech Co., Ltd
++0ee2 AMI Semiconductor, Inc.
++0ee3 ComTrue Technology Corp.
++ 1000 Image Tank 1.5
++0ee4 Sunrich Technology, Ltd
++0eee Digital Stream Technology, Inc.
++0eef D-WAV Scientific Co., Ltd
++ 0001 eGalax TouchScreen
++0ef0 Hitachi Cable, Ltd
++0ef1 Aichi Micro Intelligent Corp.
++0ef2 I/O Magic Corp.
++0ef3 Lynn Products, Inc.
++0ef4 DSI Datotech
++0ef5 PointChips
++ 2202 Flash Disk
++0ef6 Yield Microelectronics Corp.
++0ef7 SM Tech Co., Ltd (Tulip)
++0efe Wem Technology, Inc.
++0efd Oasis Semiconductor
++0f06 Visual Frontier Enterprise Co., Ltd
++0f08 CSL Wire & Plug (Shen Zhen) Co.
++0f0c CAS Corp.
++0f0d Hori Co., Ltd
++0f0e Energy Full Corp.
++0f12 Mars Engineering Corp.
++0f13 Acetek Technology Co., Ltd
++0f19 Oracom Co., Ltd
++0f1b Onset Computer Corp.
++0f1c Funai Electric Co., Ltd
++0f1d Iwill Corp.
++0f21 IOI Technology Corp.
++0f22 Senior Industries, Inc.
++0f23 Leader Tech Manufacturer Co., Ltd
++0f24 Flex-P Industries, Snd., Bhd.
++0f2d ViPower, Inc.
++0f2e Geniality Maple Technology Co., Ltd
++0f2f Priva Design Services
++0f30 Jess Technology Co., Ltd
++0f31 Chrysalis Development
++0f32 YFC-BonEagle Electric Co., Ltd
++0f37 Kokuyo Co., Ltd
++0f38 Nien-Yi Industrial Corp.
++0f41 RDC Semiconductor Co., Ltd
++0f42 Nital Consulting Services, Inc.
++0f4b St. John Technology Co., Ltd
++0f4c WorldWide Cable Opto Corp.
++0f4d Microtune, Inc.
++0f4e Freedom Scientific
++0f52 Wing Key Electrical Co., Ltd
++0f53 Dongguan White Horse Cable Factory, Ltd
++0f54 Kawai Musical Instruments Mfg. Co., Ltd
++0f55 AmbiCom, Inc.
++0f5c Prairiecomm, Inc.
++0f5d NewAge International, LLC
++0f5f Key Technology Corp.
++0f60 NTK, Ltd
++0f61 Varian, Inc.
++0f62 Acrox Technologies Co., Ltd
++0f68 Kobe Steel, Ltd
++0f69 Dionex Corp.
++0f6a Vibren Technologies, Inc.
++0f73 DFI
++0f7c DQ Technology, Inc.
++0f7d NetBotz, Inc.
++0f7e Fluke Corp.
++0f88 VTech Holdings, Ltd
++0f8b Yazaki Corp.
++0f8c Young Generation International Corp.
++0f8d Uniwill Computer Corp.
++0f8e Kingnet Technology Co., Ltd
++0f8f Soma Networks
++0f97 CviLux Corp.
++0f98 CyberBank Corp.
++0f9e Lucent Technologies
++0fa3 Starconn Electronic Co., Ltd
++0fa4 ATL Technology
++0fa5 Sotec Co., Ltd
++0fa7 Epox Computer Co., Ltd
++0fa8 Logic Controls, Inc.
++0faf Winpoint Electronic Corp.
++0fb0 Haurtian Wire & Cable Co., Ltd
++0fb1 Inclose Design, Inc.
++0fb2 Juan-Chern Industrial Co., Ltd
++0fb8 Wistron Corp.
++0fb9 AACom Corp.
++0fba San Shing Electronics Co., Ltd
++0fbb Bitwise Systems, Inc.
++0fc1 Mitac Internatinal Corp.
++0fc2 Plug and Jack Industrial, Inc.
++0fc5 Delcom Engineering
++ 1222 I/O Development Board
++0fc6 Dataplus Supplies, Inc.
++0fca Research In Motion, Ltd.
++ 0001 Blackberry Handheld
++0fce Sony Ericsson Mobile Communications AB
++0fcf Dynastream Innovations, Inc.
++0fd0 Tulip Computers B.V.
++0fd4 Tenovis GmbH & Co., KG
++0fd5 Direct Access Technology, Inc.
++0fdc Micro Plus
++0fe4 IN-Tech Electronics, Ltd
++0fe5 Greenconn (U.S.A.), Inc.
++0fea United Computer Accessories
++0feb CRS Electronic Co., Ltd
++0fec UMC Electronics Co., Ltd
++0fed Access Co., Ltd
++0fee Xsido Corp.
++0fef MJ Research, Inc.
++0ff6 Core Valley Co., Ltd
++0ff7 CHI SHING Computer Accessories Co., Ltd
++0fff Aopen, Inc.
++1000 Speed Tech Corp.
++1001 Ritronics Components (S) Pte., Ltd
++1003 Sigma Corp.
++1004 LG Electronics, Inc.
++ 6000 VX4400/VX6000 Cellphone
++ 6800 CDMA Modem
++1005 Apacer Technology, Inc.
++ b113 Handy Steno 2.0 (256MB)
++1006 iRiver, Ltd.
++ 3002 iHP-140 mp3 player
++1009 Emuzed, Inc.
++100a AV Chaseway, Ltd
++100b Chou Chin Industrial Co., Ltd
++100d Netopia, Inc.
++1010 Fukuda Denshi Co., Ltd
++1011 Mobile Media Tech.
++1012 SDKM Fibres, Wires & Cables Berhad
++1013 TST-Touchless Sensor Technology AG
++1014 Densitron Technologies PLC
++1015 Softronics Pty., Ltd
++1016 Xiamen Hung's Enterprise Co., Ltd
++1017 Speedy Industrial Supplies, Pte., Ltd
++1022 Shinko Shoji Co., Ltd
++1025 Hyper-Paltek
++1026 Newly Corp.
++1027 Time Domain
++1028 Inovys Corp.
++1029 Atlantic Coast Telesys
++102a Ramos Technology Co., Ltd
++102b Infotronic America, Inc.
++102c Etoms Electronics Corp.
++102d Winic Corp.
++1031 Comax Technology, Inc.
++1032 C-One Technology Corp.
++1033 Nucam Corp.
++1043 iCreate Technologies Corp.
++1044 Chu Yuen Enterprise Co., Ltd
+ 1046 Winbond Electronics Corp. [hex]
+ 9967 W9967CF/W9968CF WebCam IC
+-1063 Motorola Electronics Taiwan Ltd. [hex]
++104c AMCO TEC International, Inc.
++1053 Immanuel Electronics Co., Ltd
++1054 BMS International Beheer N.V.
++1055 Complex Micro Interconnection Co., Ltd
++1056 Hsin Chen Ent Co., Ltd
++1057 ON Semiconductor
++1058 Western Digital Technologies, Inc.
++1059 Giesecke & Devrient GmbH
++105c Hong Ji Electric Wire & Cable (Dongguan) Co., Ltd
++105d Delkin Devices, Inc.
++105e Valence Semiconductor Design, Ltd
++105f Chin Shong Enterprise Co., Ltd
++1060 Easthome Industrial Co., Ltd
++1063 Motorola Electronics Taiwan, Ltd [hex]
+ 1555 MC141555 Hub
+ 1065 CCYU Technology
+ 2136 EasyDisk ED1064
++106a Loyal Legend, Ltd
++106c Curitel Communications, Inc.
++ 2101 AudioVox 8900 Cell Phone
++106d San Chieh Manufacturing, Ltd
++106e ConectL
++106f Money Controls
++1076 GCT Semiconductor, Inc.
++107d Arlec Australia, Ltd
++107e Midoriya Electric Co., Ltd
++107f KidzMouse, Inc.
++1082 Shin-Etsukaken Co., Ltd
++1083 Canon Electronics, Inc.
++1084 Pantech Co., Ltd
++108a Chloride Power Protection
++108b Grand-tek Technology Co., Ltd
++108c Robert Bosch GmbH
++1099 Surface Optics Corp.
++109a DATASOFT Systems GmbH
++109f eSOL Co., Ltd
++10a0 Hirotech, Inc.
++10a3 Mitsubishi Materials Corp.
++10a9 SK Teletech Co., Ltd
++10aa Cables To Go
++10ab USI Co., Ltd
+ 10ac Honeywell, Inc.
+-10b5 PLX
++10ae Princeton Technology Corp.
++10b5 Comodo (PLX?)
+ 9060 Test Board
++10bb TM Technology, Inc.
++10bc Dinging Technology Co., Ltd
++10bd TMT Technology, Inc.
++10c4 Cygnal Integrated Products, Inc.
++10c5 Sanei Electric, Inc.
++10c6 Intec, Inc.
++10cb Eratech
++10cc GBM Connector Co., Ltd
++10cd Kycon, Inc.
++10d1 Hottinger Baldwin Measurement
++ 0101 USB-Module for Spider8, CP32
++ 0202 CP22 - Communication Processor
++ 0301 CP42 - Communication Processor
++10d4 Man Boon Manufactory, Ltd
++10d5 Uni Class Technology Co., Ltd
++10d6 Actions Semiconductor Co., Ltd
++ 1000 MP3 Player
++10de Authenex, Inc.
++10df In-Win Development, Inc.
++10e0 Post-Op Video, Inc.
++10e1 CablePlus, Ltd
++10e2 Nada Electronics, Ltd
++10ec Vast Technologies, Inc.
++10fb Pictos Technologies, Inc.
++10fd Anubis Electronics, Ltd
++ 804d Typhoon Webshot II Webcam [zc0301]
++1a0a ...
++ badd USB OTG Compliance test device
++1100 VirTouch, Ltd
++ 0001 VTPlayer VTP-1 Braille Mouse
++1101 EasyPass Industrial Co., Ltd
++ 0001 FSK Electronics Super GSM Reader
++1108 Brightcom Technologies, Ltd
++1110 Analog Devices Canada, Ltd (Allied Telesyn)
++ 900f AT-AR215 DSL Modem
++1112 YM ELECTRIC CO., Ltd
++1113 Medion AG
++111e VSO Electric Co., Ltd
++112e Master Hill Electric Wire and Cable Co., Ltd
++112f Cellon International, Inc.
++1130 Tenx Technology, Inc.
++1131 Integrated System Solution Corp.
+ 1132 Toshiba Corp., Digital Media Equipment [hex]
+ 4331 PDR-M4/M5/M70 Digital Camera
+- 4432 PDR-M60 Digital Camera
+-1183 Compaq Computer Corp. [hex]
+- 4008 56k FaxModem
++ 4332 PDR-M60 Digital Camera
++113c Arin Tech Co., Ltd
++113d Mapower Electronics Co., Ltd
++1141 V One Multimedia, Pte., Ltd
++1142 CyberScan Technologies, Inc.
++1147 Ever Great Electric Wire and Cable Co., Ltd
++114c Tinius Olsen Testing Machine Co., Inc.
++114d Alpha Imaging Technology Corp.
++1162 Secugen Corp.
++1163 DeLorme Publishing, Inc.
++1164 YUAN High-Tech Development Co., Ltd
++1165 Telson Electronics Co., Ltd
++1166 Bantam Interactive Technologies
++1167 Salient Systems Corp.
++1168 BizConn International Corp.
++116e Gigastorage Corp.
++116f Silicon 10 Technology Corp.
++1175 Shengyih Steel Mold Co., Ltd
++117d Santa Electronic, Inc.
++117e JNC, Inc.
++1182 Venture Corp., Ltd
++1183 Compaq Computer Corp. [hex] (Digital Dream ??)
+ 19c7 ISDN TA
++ 4008 56k FaxModem
+ 504a PJB-100 Personal Jukebox
++1184 Kyocera Elco Corp.
++118f You Yang Technology Co., Ltd
+ 1190 Tripace
+-120e Hudson Soft Co., Ltd.
++1191 Loyalty Founder Enterprise Co., Ltd
++1197 Technoimagia Co., Ltd
++1198 StarShine Technology Corp.
++1199 Sierra Wireless, Inc.
++119a ZHAN QI Technology Co., Ltd
++11a3 Technovas Co., Ltd
++11aa GlobalMedia Group, LLC
++11ab Exito Electronics Co., Ltd
++11db Topfield Co., Ltd.
++ 1000 PVR
++ 1100 PVR
++1209 InterBiometrics
++ 1001 USB Hub
++ 1002 USB Relais
++ 1003 IBSecureCam-P
++ 1004 IBSecureCam-O
++ 1005 IBSecureCam-N
++120e Hudson Soft Co., Ltd
++121e Jungsoft Co., Ltd
++1241 Belkin (?)
++ 1111 Mouse
++1267 Logic3 / SpectraVideo plc
++ a001 JP260 PC Game Pad
++126e Strobe Data, Inc.
++126f TwinMOS
++ 1325 Mobile Disk
++ 2168 Mobile Disk III
++1275 Xaxero Marine Software Engineering, Ltd.
++ 0002 WeatherFax 2000 Demodulator
++ 0080 SkyEye Weather Satellite Receiver
+ 1292 Innomedia
+ 0258 Creative Labs VoIP Blaster
+ 1293 Belkin Components [hex]
+ 0002 F5U002 Parallel Port [uss720]
+ 2101 104-key keyboard
++12fd AIN Comm. Technology Co., Ltd
++ 1001 AWU2000b 802.11b Stick
++1312 ICS Electronics
+ 1342 Mobility
+ 0200 EasiDock 200 Hub
+ 0201 EasiDock 200 Keyboard and Mouse Port
+@@ -1872,18 +3377,31 @@
+ 0203 EasiDock 200 Printer Port
+ 13d2 Shark Multimedia
+ 0400 Pocket Ethernet [klsi]
++147a Formosa Industrial Computing, Inc.
+ 1484 Elsa AG [hex]
+ 1746 Ecomo 19H99 Monitor
+ 7616 Elsa Hub
++14c2 Gemlight Computer, Ltd
++1520 Bitwire Corp.
++1554 Prolink Microsystems Corp.
++1568 Sunf Pu Technology Co., Ltd
++15c6 Laboratoires MXM
+ 15e8 SohoWare
+ 9100 NUB100 Ethernet [pegasus]
++15e9 Pacific Digital Corp.
+ 1604 Tascam
+ 8000 US-428 Audio/Midi Controller (without fw)
+ 8001 US-428 Audio/Midi Controller
++ 8004 US-224 Audio/Midi Controller (without fw)
++ 8005 US-224 Audio/Midi Controller
++ 8006 US-122 Audio/Midi Interface (without fw)
++ 8007 US-122 Audio/Midi Interface
+ 1606 Umax [hex]
+ 0010 Astra 1220U
+ 0030 Astra 2000U
+ 0060 Astra 3400U
++ 0130 Astra 2100U
++ 0160 Astra 5400U
+ 0230 Astra 2200/2200SU
+ 2020 AstraCam 1000
+ 1608 Inside Out Networks [hex]
+@@ -1903,55 +3421,113 @@
+ 8093 PortGear Serial Port
+ 1668 Actiontec Electronics, Inc. [hex]
+ 0333 Modem
+- 0421 802.11b Wireless Adapter
++ 0408 Prism2.5 802.11b Adapter
++ 0421 Prism2.5 802.11b Adapter
+ 1690 Askey Computer Corp. [hex]
+ 0101 Creative Modem Blaster DE5670
+ 0103 Askey 1456 VQE-R3 Modem [conexant]
+ 0109 Askey MagicXpress V.90 Pocket Modem [conexant]
+-2001 D-Link Corp [hex]
++1696 Hitachi Video and Information System, Inc.
++1697 VTec Test, Inc.
++1733 Cellink Technology Co., Ltd
++ 0101 RF Wireless Optical Mouse OP-701
++1894 Topseed
++ 5632 Atek Tote Remote
++ 5641 TSAM-004 Presentation Remote
++1ebb NuCORE Technology, Inc.
++2001 D-Link Corp. [hex]
++ 3200 DWL-120 802.11b (Atmel RFMD503A) [usbvnetr]
++ 3700 DWL-122 802.11b
+ 4000 DSB-650C Ethernet [klsi]
+ 4001 DSB-650TX Ethernet [pegasus]
+ 4002 DSB-650TX Ethernet [pegasus]
+ 4003 DSB-650TX-PNA Ethernet [pegasus]
+ abc1 DSB-650 Ethernet [pegasus]
++2162 Creative (?)
++ 500c DE5771 Modem Blaster
+ 2222 MacAlly
+ 0004 iWebKey Keyboard
+ 22b8 Motorola PCS
+- 1005 Ti280e GSM/GPRS Phone
++ 0005 V.60c/V.60i GSM Phone
++ 1005 T280e GSM/GPRS Phone
++ 2821 T720 GSM Phone
++ 2822 V.120e GSM Phone
++ 3002 A835 GSM Phone
++ 3802 C330 GSM Phone
++22b9 eTurboTouch Technology, Inc.
++22ba Technology Innovation Holdings, Ltd
+ 2304 Pinnacle Systems, Inc. [hex]
+- 0111 Studio PCTV (PAL) Frame Grabber
+- 0112 Studio PCTV (NTSC) Frame Grabber
+- 0210 Studio PCTV (PAL) Frame Grabber
+- 0212 Studio PCTV (NTSC) Frame Grabber
+-2318 Shining Technologies Inc. [hex]
++ 0109 Pinnacle Studio PCTV USB (SECAM)
++ 0110 Pinnacle Studio PCTV USB (PAL)
++ 0111 Miro PCTV USB
++ 0112 Pinnacle Studio PCTV USB (NTSC) with FM radio
++ 0210 Pinnacle Studio PCTV USB (PAL) with FM radio
++ 0212 Pinnacle Studio PCTV USB (NTSC)
++ 0214 Pinnacle Studio PCTV USB (PAL) with FM radio
++ 0300 Pinnacle Studio Linx Video input cable (NTSC)
++ 0419 Pinnacle PCTV Bungee USB (PAL) with FM radio
++2318 Shining Technologies, Inc. [hex]
+ 0011 CitiDISK Jr. IDE Enclosure
+ 2375 Digit@lway, Inc.
+ 0001 Digital Audio Player
++2632 TwinMOS
++ 3209 7-in-1 Card Reader
++2650 Electronics For Imaging, Inc. [hex]
++2770 NHJ, Ltd
++ 9120 Che-ez! Snap / iClick Tiny VGA Digital Camera
++2899 Toptronic Industrial Co., Ltd
+ 3125 Eagletron
+ 0001 TrackerPod Camera Stand
++3176 Whanam Electronics Co., Ltd
+ 3504 Micro Star
+ f110 Security Key
++3538 Power Quotient International Co., Ltd
++ 0001 Travel Flash
++3579 DIVA
++ 6901 Media Reader
++3636 InVibro
++3838 WEM
++ 0001 5-in-1 Card Reader
++3923 National Instruments Corp.
++4102 iRiver, Ltd.
++ 1001 iFP-100 series mp3 player
++ 1003 iFP-300 series mp3 player
++ 1005 iFP-500 series mp3 player
++ 1007 iFP-700 series mp3/ogg vorbis player
++ 1008 iFP-800 series mp3/ogg vorbis player
++ 100A iFP-1000 series mp3/ogg vorbis player
++ 1101 iFP-100 series mp3 player (ums firmware)
++ 1103 iFP-300 series mp3 player (ums firmware)
++ 1105 iFP-500 series mp3 player (ums firmware)
++413c Dell Computer Corp.
++ 8100 TrueMobile 1180 802.11b Adapter
+ 4242 USB Design by Example
+ 4201 Buttons and Lights HID device
+ 4220 Echo 1 Camera
+ 544d Transmeta Corp.
+-55aa OnSpec Electronic Inc.
++5543 UC-Logic Technology Corp.
++ 0002 SuperPen WP3325U Tablet
++55aa OnSpec Electronic, Inc.
+ 1234 ATAPI Bridge
+ a103 Sandisk SDDR-55 SmartMedia Card Reader
+-5543 UC-Logic Technology, Corp.
+- 0002 SuperPen WP3325U Tablet
+ 636c CoreLogic, Inc.
+ 6666 Prototype product Vendor ID
+ 0667 Smart Joy PSX, PS-PC Smart JoyPad
+-6a75 Shanghai Jujo Electronics Co., Ltd.
++6a75 Shanghai Jujo Electronics Co., Ltd
+ 8086 Intel Corp.
+ 0110 Easy PC Camera
+ 0431 Intel Pro Video PC Camera
+ 0510 Digital Movie Creator
+ 0630 Pocket PC Camera
++ 07d3 BLOB boot loader firmware
++ 1111 PRO/Wireless 2011B 802.11b Adapter
+ 9890 82930 Test Board
+ c013 Wireless HID Station
+-c251 Keil Software
++8341 EGO Systems, Inc.
++ 2000 Flashdisk
++c251 Keil Software, Inc.
++eb1a eMPIA Technology, Inc.
++ 2801 GrabBeeX+ Video Encoder
+
+ # List of known device classes, subclasses and protocols
+
+@@ -1960,22 +3536,32 @@
+ # subclass subclass_name <-- single tab
+ # protocol protocol_name <-- two tabs
+
+-C 00 Interface
++C 00 (Defined at Interface level)
+ C 01 Audio
+ 01 Control Device
+ 02 Streaming
+- 03 Non Streaming
++ 03 MIDI Streaming
+ C 02 Communications
+ 01 Direct Line
+ 02 Abstract (modem)
+ 00 None
+- 01 AT-commands
+- ff Vendor Specific
++ 01 AT-commands (v.25ter)
++ 02 AT-commands (PCCA101)
++ 03 AT-commands (PCCA101 + wakeup)
++ 04 AT-commands (GSM)
++ 05 AT-commands (3G)
++ 06 AT-commands (CDMA)
++ fe Defined by command set descriptor
++ ff Vendor Specific (MSFT RNDIS?)
+ 03 Telephone
+ 04 Multi-Channel
+ 05 CAPI Control
+ 06 Ethernet Networking
+ 07 ATM Networking
++ 08 Wireless Handset Control
++ 09 Device Management
++ 0a Mobile Direct Line
++ 0b OBEX
+ C 03 Human Interface Devices
+ 00 No Subclass
+ 00 None
+@@ -2012,20 +3598,44 @@
+ 01 Control/Bulk
+ 50 Bulk (Zip)
+ C 09 Hub
+-C 10 Data
+- 30 I.430 ISDN BRI
+- 31 HDLC
+- 32 Transparent
+- 50 Q.921M
+- 51 Q.921
+- 52 Q.921TM
+- 90 V.42bis
+- 91 Q.932 EuroISDN
+- 92 V.120 V.24 rate ISDN
+- 93 CAPI 2.0
+- fd Host Based Driver
+- fe CDC PUF
+- ff Vendor specific
++ 00 Unused
++ 01 Single TT
++ 02 TT per port
++C 0a Data
++ 00 Unused
++ 30 I.430 ISDN BRI
++ 31 HDLC
++ 32 Transparent
++ 50 Q.921M
++ 51 Q.921
++ 52 Q.921TM
++ 90 V.42bis
++ 91 Q.932 EuroISDN
++ 92 V.120 V.24 rate ISDN
++ 93 CAPI 2.0
++ fd Host Based Driver
++ fe CDC PUF
++ ff Vendor specific
++C 0b Chip/SmartCard
++C 0d Content Security
++C 0e Video
++ 00 Undefined
++ 01 Video Control
++ 02 Video Streaming
++ 03 Video Interface Collection
++C dc Diagnostic
++ 01 Reprogrammable Diagnostics
++ 01 USB2 Compliance
++C e0 Wireless
++ 01 Radio Frequency
++ 01 Bluetooth
++C ef Miscellaneous Device
++ 02 Common Class
++ 01 Interface Association
++C fe Application Specific Interface
++ 01 Device Firmware Update
++ 02 IRDA Bridge
++ 03 Test and Measurement
+ C ff Vendor Specific Class
+ ff Vendor Specific Subclass
+ ff Vendor Specific Protocol
+@@ -2644,10 +4254,12 @@
+ 001 Button 1 (Primary)
+ 002 Button 2 (Secondary)
+ 003 Button 3 (Tertiary)
++ 004 Button 4
++ 005 Button 5
+ HUT 0a Ordinal
+ 001 Instance 1
+ 002 Instance 2
+- 002 Instance 3
++ 003 Instance 3
+ HUT 0b Telephony
+ 000 Unassigned
+ 001 Phone
+@@ -3177,14 +4789,191 @@
+ 0b0 Settings
+ 0ca On Screen Display (OSD)
+ 0d4 Stereo Mode
+-HUT 84 Power Pages
+-HUT 85 Power Pages
++HUT 84 Power Device Page
++ 000 Undefined
++ 001 iName
++ 002 Present Status
++ 003 Changed Status
++ 004 UPS
++ 005 Power Supply
++ 010 Battery System
++ 011 Battery System ID
++ 012 Battery
++ 013 Battery ID
++ 014 Charger
++ 015 Charger ID
++ 016 Power Converter
++ 017 Power Converter ID
++ 018 Outlet System
++ 019 Outlet System ID
++ 01a Input
++ 01b Input ID
++ 01c Output
++ 01d Output ID
++ 01e Flow
++ 01f Flow ID
++ 020 Outlet
++ 021 Outlet ID
++ 022 Gang
++ 023 Gang ID
++ 024 Power Summary
++ 025 Power Summary ID
++ 030 Voltage
++ 031 Current
++ 032 Frequency
++ 033 Apparent Power
++ 034 Active Power
++ 035 Percent Load
++ 036 Temperature
++ 037 Humidity
++ 038 Bad Count
++ 040 Config Voltage
++ 041 Config Current
++ 042 Config Frequency
++ 043 Config Apparent Power
++ 044 Config Active Power
++ 045 Config Percent Load
++ 046 Config Temperature
++ 047 Config Humidity
++ 050 Switch On Control
++ 051 Switch Off Control
++ 052 Toggle Control
++ 053 Low Voltage Transfer
++ 054 High Voltage Transfer
++ 055 Delay Before Reboot
++ 056 Delay Before Startup
++ 057 Delay Before Shutdown
++ 058 Test
++ 059 Module Reset
++ 05a Audible Alarm Control
++ 060 Present
++ 061 Good
++ 062 Internal Failure
++ 063 Voltage out of range
++ 064 Frequency out of range
++ 065 Overload
++ 066 Over Charged
++ 067 Over Temperature
++ 068 Shutdown Requested
++ 069 Shutdown Imminent
++ 06a Reserved
++ 06b Switch On/Off
++ 06c Switchable
++ 06d Used
++ 06e Boost
++ 06f Buck
++ 070 Initialized
++ 071 Tested
++ 072 Awaiting Power
++ 073 Communication Lost
++ 0fd iManufacturer
++ 0fe iProduct
++ 0ff iSerialNumber
++HUT 85 Battery System Page
++ 000 Undefined
++ 001 SMB Battery Mode
++ 002 SMB Battery Status
++ 003 SMB Alarm Warning
++ 004 SMB Charger Mode
++ 005 SMB Charger Status
++ 006 SMB Charger Spec Info
++ 007 SMB Selector State
++ 008 SMB Selector Presets
++ 009 SMB Selector Info
++ 010 Optional Mfg. Function 1
++ 011 Optional Mfg. Function 2
++ 012 Optional Mfg. Function 3
++ 013 Optional Mfg. Function 4
++ 014 Optional Mfg. Function 5
++ 015 Connection to SMBus
++ 016 Output Connection
++ 017 Charger Connection
++ 018 Battery Insertion
++ 019 Use Next
++ 01a OK to use
++ 01b Battery Supported
++ 01c SelectorRevision
++ 01d Charging Indicator
++ 028 Manufacturer Access
++ 029 Remaining Capacity Limit
++ 02a Remaining Time Limit
++ 02b At Rate
++ 02c Capacity Mode
++ 02d Broadcast To Charger
++ 02e Primary Battery
++ 02f Charge Controller
++ 040 Terminate Charge
++ 041 Terminate Discharge
++ 042 Below Remaining Capacity Limit
++ 043 Remaining Time Limit Expired
++ 044 Charging
++ 045 Discharging
++ 046 Fully Charged
++ 047 Fully Discharged
++ 048 Conditioning Flag
++ 049 At Rate OK
++ 04a SMB Error Code
++ 04b Need Replacement
++ 060 At Rate Time To Full
++ 061 At Rate Time To Empty
++ 062 Average Current
++ 063 Max Error
++ 064 Relative State Of Charge
++ 065 Absolute State Of Charge
++ 066 Remaining Capacity
++ 067 Full Charge Capacity
++ 068 Run Time To Empty
++ 069 Average Time To Empty
++ 06a Average Time To Full
++ 06b Cycle Count
++ 080 Batt. Pack Model Level
++ 081 Internal Charge Controller
++ 082 Primary Battery Support
++ 083 Design Capacity
++ 084 Specification Info
++ 085 Manufacturer Date
++ 086 Serial Number
++ 087 iManufacturerName
++ 088 iDeviceName
++ 089 iDeviceChemistry
++ 08a Manufacturer Data
++ 08b Rechargeable
++ 08c Warning Capacity Limit
++ 08d Capacity Granularity 1
++ 08e Capacity Granularity 2
++ 08f iOEMInformation
++ 0c0 Inhibit Charge
++ 0c1 Enable Polling
++ 0c2 Reset To Zero
++ 0d0 AC Present
++ 0d1 Battery Present
++ 0d2 Power Fail
++ 0d3 Alarm Inhibited
++ 0d4 Thermistor Under Range
++ 0d5 Thermistor Hot
++ 0d6 Thermistor Cold
++ 0d7 Thermistor Over Range
++ 0d8 Voltage Out Of Range
++ 0d9 Current Out Of Range
++ 0da Current Not Regulated
++ 0db Voltage Not Regulated
++ 0dc Master Mode
++ 0f0 Charger Selector Support
++ 0f1 Charger Spec
++ 0f2 Level 2
++ 0f3 Level 3
+ HUT 86 Power Pages
+ HUT 87 Power Pages
+ HUT 8c Bar Code Scanner Page (POS)
+ HUT 8d Scale Page (POS)
+ HUT 90 Camera Control Page
+ HUT 91 Arcade Control Page
++HUT f0 Cash Device
++ 0f1 Cash Drawer
++ 0f2 Cash Drawer Number
++ 0f3 Cash Drawer Set
++ 0f4 Cash Drawer Status
++HUT ff Vendor Specific
+
+ # List of Languages
+
+@@ -3285,8 +5074,8 @@
+ 01 Bokmal
+ 02 Nynorsk
+ L 0015 Polish
+-L 0016 Portugese
+- 01 Portugese
++L 0016 Portuguese
++ 01 Portuguese
+ 02 Brazilian
+ L 0017 forgotten
+ L 0018 Romanian
+@@ -3354,3 +5143,46 @@
+ 02 India
+ L 0061 Nepali
+ 02 India
++
++# HID Descriptor bCountryCode
++# HID Specification 1.11 (2001-06-27) page 23
++#
++# Syntax:
++# HCC country_code keymap_type
++
++HCC 00 Not supported
++HCC 01 Arabic
++HCC 02 Belgian
++HCC 03 Canadian-Bilingual
++HCC 04 Canadian-French
++HCC 05 Czech Republic
++HCC 06 Danish
++HCC 07 Finnish
++HCC 08 French
++HCC 09 German
++HCC 10 Greek
++HCC 11 Hebrew
++HCC 12 Hungary
++HCC 13 International (ISO)
++HCC 14 Italian
++HCC 15 Japan (Katakana)
++HCC 16 Korean
++HCC 17 Latin American
++HCC 18 Netherlands/Dutch
++HCC 19 Norwegian
++HCC 20 Persian (Farsi)
++HCC 21 Poland
++HCC 22 Portuguese
++HCC 23 Russia
++HCC 24 Slovakia
++HCC 25 Spanish
++HCC 26 Swedish
++HCC 27 Swiss/French
++HCC 28 Swiss/German
++HCC 29 Switzerland
++HCC 30 Taiwan
++HCC 31 Turkish-Q
++HCC 32 UK
++HCC 33 US
++HCC 34 Yugoslavia
++HCC 35 Turkish-F
+--- usbutils-0.11/usbdevice_fs.h~usbutils-0.11+cvs20041108
++++ usbutils-0.11/usbdevice_fs.h
+@@ -23,7 +23,7 @@
+ * History:
+ * 0.1 04.01.2000 Created
+ *
+- * $Id$
++ * $Id$
+ */
+
+ /*****************************************************************************/
+@@ -31,6 +31,8 @@
+ #ifndef _LINUX_USBDEVICE_FS_H
+ #define _LINUX_USBDEVICE_FS_H
+
++#include <linux/types.h>
++
+ /* --------------------------------------------------------------------- */
+
+ #define USBDEVICE_SUPER_MAGIC 0x9fa2
+@@ -64,8 +66,21 @@
+ void *context;
+ };
+
++#define USBDEVFS_MAXDRIVERNAME 255
++
++struct usbdevfs_getdriver {
++ unsigned int interface;
++ char driver[USBDEVFS_MAXDRIVERNAME + 1];
++};
++
++struct usbdevfs_connectinfo {
++ unsigned int devnum;
++ unsigned char slow;
++};
++
+ #define USBDEVFS_URB_DISABLE_SPD 1
+ #define USBDEVFS_URB_ISO_ASAP 2
++#define USBDEVFS_URB_QUEUE_BULK 0x10
+
+ #define USBDEVFS_URB_TYPE_ISO 0
+ #define USBDEVFS_URB_TYPE_INTERRUPT 1
+@@ -94,11 +109,27 @@
+ struct usbdevfs_iso_packet_desc iso_frame_desc[0];
+ };
+
++/* ioctls for talking to drivers in the usbcore module: */
++struct usbdevfs_ioctl {
++ int ifno; /* interface 0..N ; negative numbers reserved */
++ int ioctl_code; /* MUST encode size + direction of data so the
++ * macros in <asm/ioctl.h> give correct values */
++ void *data; /* param buffer (in, or out) */
++};
++
++/* You can do most things with hubs just through control messages,
++ * except find out what device connects to what port. */
++struct usbdevfs_hub_portinfo {
++ char nports; /* number of downstream ports in this hub */
++ char port [127]; /* e.g. port 3 connects to device 27 */
++};
++
+ #define USBDEVFS_CONTROL _IOWR('U', 0, struct usbdevfs_ctrltransfer)
+ #define USBDEVFS_BULK _IOWR('U', 2, struct usbdevfs_bulktransfer)
+ #define USBDEVFS_RESETEP _IOR('U', 3, unsigned int)
+ #define USBDEVFS_SETINTERFACE _IOR('U', 4, struct usbdevfs_setinterface)
+ #define USBDEVFS_SETCONFIGURATION _IOR('U', 5, unsigned int)
++#define USBDEVFS_GETDRIVER _IOW('U', 8, struct usbdevfs_getdriver)
+ #define USBDEVFS_SUBMITURB _IOR('U', 10, struct usbdevfs_urb)
+ #define USBDEVFS_DISCARDURB _IO('U', 11)
+ #define USBDEVFS_REAPURB _IOW('U', 12, void *)
+@@ -106,6 +137,11 @@
+ #define USBDEVFS_DISCSIGNAL _IOR('U', 14, struct usbdevfs_disconnectsignal)
+ #define USBDEVFS_CLAIMINTERFACE _IOR('U', 15, unsigned int)
+ #define USBDEVFS_RELEASEINTERFACE _IOR('U', 16, unsigned int)
++#define USBDEVFS_CONNECTINFO _IOW('U', 17, struct usbdevfs_connectinfo)
++#define USBDEVFS_IOCTL _IOWR('U', 18, struct usbdevfs_ioctl)
++#define USBDEVFS_HUB_PORTINFO _IOR('U', 19, struct usbdevfs_hub_portinfo)
++#define USBDEVFS_RESET _IO('U', 20)
++#define USBDEVFS_CLEAR_HALT _IOR('U', 21, unsigned int)
+
+ /* --------------------------------------------------------------------- */
+
+@@ -126,18 +162,6 @@
+
+ #define IROOT 1
+
+-/*
+- * sigh. rwsemaphores do not (yet) work from modules
+- */
+-
+-#define rw_semaphore semaphore
+-#define init_rwsem init_MUTEX
+-#define down_read down
+-#define down_write down
+-#define up_read up
+-#define up_write up
+-
+-
+ struct dev_state {
+ struct list_head list; /* state list */
+ struct rw_semaphore devsem; /* protects modifications to dev (dev == NULL indicating disconnect) */
+@@ -163,7 +187,6 @@
+ extern struct file_operations usbdevfs_bus_file_operations;
+ extern void usbdevfs_conn_disc_event(void);
+
+-
+ #endif /* __KERNEL__ */
+
+ /* --------------------------------------------------------------------- */
+--- /dev/null
++++ usbutils-0.11/usbmisc.c
+@@ -0,0 +1,141 @@
++/*****************************************************************************/
++/*
++ * usbmisc.c -- Misc USB routines
++ *
++ * Copyright (C) 2003 Aurelien Jarno (aurelien@aurel32.net)
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
++ *
++ *
++ */
++
++/*****************************************************************************/
++
++#include <stdio.h>
++#include <string.h>
++
++#ifdef HAVE_CONFIG_H
++#include "config.h"
++#endif
++
++#include "usbmisc.h"
++
++/* ---------------------------------------------------------------------- */
++
++static const char *procbususb = "/proc/bus/usb";
++
++/* ---------------------------------------------------------------------- */
++
++static int readlink_recursive(const char *path, char *buf, size_t bufsize)
++{
++ char temp[PATH_MAX + 1];
++ char *ptemp;
++ int ret;
++
++ ret = readlink(path, buf, bufsize);
++
++ if (ret > 0) {
++ buf[ret] = 0;
++ if (*buf != '/')
++ {
++ strncpy(temp, path, sizeof(temp));
++ ptemp = temp + strlen(temp);
++ while (*ptemp != '/' && ptemp != temp) ptemp--;
++ ptemp++;
++ strncpy(ptemp, buf, bufsize + temp - ptemp);
++ }
++ else
++ strncpy(temp, buf, sizeof(temp));
++ return readlink_recursive(temp, buf, bufsize);
++ }
++ else {
++ strncpy(buf, path, bufsize);
++ return strlen(buf);
++ }
++}
++
++static char *get_absolute_path(const char *path, char *result, size_t result_size)
++{
++ const char *ppath; /* pointer on the input string */
++ char *presult; /* pointer on the output string */
++
++ ppath = path;
++ presult = result;
++ result[0] = 0;
++
++ if (path == NULL)
++ return result;
++
++ if (*ppath != '/')
++ {
++ getcwd(result, result_size);
++ presult += strlen(result);
++ result_size -= strlen(result);
++
++ *presult++ = '/';
++ result_size--;
++ }
++
++ while (*ppath != 0 && result_size > 1) {
++ if (*ppath == '/') {
++ do ppath++; while (*ppath == '/');
++ *presult++ = '/';
++ result_size--;
++ }
++ else if (*ppath == '.' && *(ppath + 1) == '.' && *(ppath + 2) == '/' && *(presult - 1) == '/') {
++ if ((presult - 1) != result)
++ {
++ /* go one directory upper */
++ do {
++ presult--;
++ result_size++;
++ } while (*(presult - 1) != '/');
++ }
++ ppath += 3;
++ }
++ else if (*ppath == '.' && *(ppath + 1) == '/' && *(presult - 1) == '/') {
++ ppath += 2;
++ }
++ else {
++ *presult++ = *ppath++;
++ result_size--;
++ }
++ }
++ /* Don't forget to mark the end of the string! */
++ *presult = 0;
++
++ return result;
++}
++
++struct usb_device *get_usb_device(const char *path)
++{
++ struct usb_bus *bus;
++ struct usb_device *dev;
++ char device_path[PATH_MAX + 1];
++ char absolute_path[PATH_MAX + 1];
++
++ readlink_recursive(path, device_path, sizeof(device_path));
++ get_absolute_path(device_path, absolute_path, sizeof(absolute_path));
++
++ for (bus = usb_busses; bus; bus = bus->next) {
++ for (dev = bus->devices; dev; dev = dev->next) {
++ snprintf(device_path, sizeof(device_path), "%s/%s/%s", procbususb, bus->dirname, dev->filename);
++ if (!strcmp(device_path, absolute_path))
++ return dev;
++ }
++ }
++ return NULL;
++}
++
+--- /dev/null
++++ usbutils-0.11/usbmisc.h
+@@ -0,0 +1,36 @@
++/*****************************************************************************/
++/*
++ * usbmisc.h -- Misc USB routines
++ *
++ * Copyright (C) 2003 Aurelien Jarno (aurelien@aurel32.net)
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
++ *
++ *
++ */
++
++/*****************************************************************************/
++
++#ifndef _USBMISC_H
++#define _USBMISC_H
++
++#include <usb.h>
++
++/* ---------------------------------------------------------------------- */
++
++extern struct usb_device *get_usb_device(const char *path);
++
++/* ---------------------------------------------------------------------- */
++#endif /* _USBMISC_H */
+--- usbutils-0.11/usbmodules.8~usbutils-0.11+cvs20041108
++++ usbutils-0.11/usbmodules.8
+@@ -5,7 +5,8 @@
+ in USB device
+ .SH SYNOPSIS
+ .B usbmodules
+-.RB [ "--device " /proc/bus/bus/NNN/NNN ]
++.RB [ "--device " /proc/bus/bus/NNN/NNN
++.RB | " --product " xx/xx/xx " --type " dd/dd/dd " --interface " dd/dd/dd ]
+ .RB [ "--check " modulename ]
+ .RB [ --help ]
+ .RB [ "--mapfile " pathname ]
+@@ -27,11 +28,21 @@
+ .IP
+ done
+ .PP
++or
++.IP
++ for module in $(usbmodules --product $PRODUCT --type $TYPE --interface $INTERFACE) ; do
++.IP
++ modprobe -s -k "$module"
++.IP
++ done
++.PP
+ The DEVICE environment variable is passed from the kernel to /sbin/hotplug
+ during USB hotplugging if the kernel was configured using
+ .I usbdevfs.
++The environment variables PRODUCT, TYPE and INTERFACE are set when
++/sbin/hotplug is called during hotplugging.
+ .B usbmodules
+-currently requires usbdevfs to operate.
++can operate with both configurations.
+ .PP
+ When a USB device is removed from the system, the Linux kernel will
+ decrement a usage count on USB driver module. If this count drops
+@@ -52,7 +63,20 @@
+ .BI "--device " /proc/bus/usb/MMM/NNN
+ Selects which device
+ .B usbmodules
+-will examine. The argument is currently mandatory.
++will examine. The argument is mandatory unless
++.B --procuct
++,
++.B --type
++and
++.B --interface
++are used together.
++.TP
++.BI "--product " xx/xx/xx " --type " dd/dd/dd " --interface " dd/dd/dd
++Alternative way to select the device
++.B usbmodules
++will examine. These arguments are mandatory unless
++.B --device
++is given.
+ .TP
+ .B --help, -h
+ Print a help message
+--- usbutils-0.11/usbmodules.c~usbutils-0.11+cvs20041108
++++ usbutils-0.11/usbmodules.c
+@@ -11,7 +11,7 @@
+ *
+ * The code in usbmodules not derived from elsewhere was written by
+ * Adam J. Richter. David Brownell added the --mapfile and --version
+- * options.
++ * options. Aurelien Jarno modified the code to use libusb.
+ *
+ * Copyright (C) 2000, 2001 Yggdrasil Computing, Inc.
+ * Copyright (C) 1999 Thomas Sailer (sailer@ife.ee.ethz.ch)
+@@ -38,8 +38,6 @@
+ #include <sys/types.h>
+ #include <sys/stat.h>
+ #include <sys/ioctl.h>
+-#include <fcntl.h>
+-#include <dirent.h>
+ #include <string.h>
+ #include <errno.h>
+ #include <stdlib.h>
+@@ -48,36 +46,27 @@
+ #include <stdarg.h>
+ #include <sys/param.h>
+ #include <sys/utsname.h>
+-
+-#include <linux/types.h>
+-#ifdef HAVE_LINUX_USBDEVICE_FS_H
+-#include <linux/usbdevice_fs.h>
+-#else
+-#include "usbdevice_fs.h"
+-#endif
+-#ifdef HAVE_LINUX_USB_H
+-#include <linux/usb.h>
+-#else
+-#include "usb.h"
+-#endif
++#include <usb.h>
+
+ #ifdef HAVE_CONFIG_H
+ #include "config.h"
+ #endif
+
+ #include "usbmodules.h"
+-#include "names.h"
+-#include "devtree.h"
++#include "usbmisc.h"
+
+ #define _GNU_SOURCE
+ #include <getopt.h>
+
+-#define OPT_STRING "c:d:hm:v"
++#define OPT_STRING "c:d:hi:m:p:t:v"
+ static struct option long_options[] = {
+ {"check", required_argument, NULL, 'c'},
+ {"device", required_argument, NULL, 'd'},
+ {"help", no_argument, NULL, 'h'},
++ {"interface", required_argument, NULL, 'i'},
+ {"mapfile", required_argument, NULL, 'm'},
++ {"product", required_argument, NULL, 'p'},
++ {"type", required_argument, NULL, 't'},
+ {"version", no_argument, NULL, 'v'},
+ { 0, 0, NULL, 0}
+ };
+@@ -87,23 +76,7 @@
+
+ #define LINELENGTH 8000
+
+-#define CTRL_RETRIES 50
+-#define CTRL_TIMEOUT 100 /* milliseconds */
+-
+-#define USB_DT_CS_DEVICE 0x21
+-#define USB_DT_CS_CONFIG 0x22
+-#define USB_DT_CS_STRING 0x23
+-#define USB_DT_CS_INTERFACE 0x24
+-#define USB_DT_CS_ENDPOINT 0x25
+-
+ static char *checkname = NULL;
+-
+-static int idVendor;
+-static int idProduct;
+-static int bcdDevice;
+-static int bDeviceClass;
+-static int bDeviceSubClass;
+-static int bDeviceProtocol;
+ struct usbmap_entry *usbmap_list;
+
+ static void *
+@@ -218,6 +191,9 @@
+ if (line[0] == '#')
+ continue;
+
++ if (line[0] == '\n')
++ continue;
++
+ entry = xmalloc(sizeof(struct usbmap_entry));
+
+ if (!scan_with_flags(line, entry, name) &&
+@@ -257,52 +233,51 @@
+ */
+
+ static void
+-match_modules(int bInterfaceClass,
+- int bInterfaceSubClass,
+- int bInterfaceProtocol)
++match_modules(struct usb_device_descriptor *device_descriptor,
++ struct usb_interface_descriptor *interface_descriptor)
+ {
+ struct usbmap_entry *mod;
+
+ for (mod = usbmap_list; mod != NULL; mod = mod->next) {
+
+ if ((mod->match_flags & USB_MATCH_VENDOR) &&
+- mod->idVendor != idVendor)
++ mod->idVendor != device_descriptor->idVendor)
+ continue;
+
+ if ((mod->match_flags & USB_MATCH_PRODUCT) &&
+- mod->idProduct != idProduct)
++ mod->idProduct != device_descriptor->idProduct)
+ continue;
+
+ if ((mod->match_flags & USB_MATCH_DEV_LO) &&
+- mod->bcdDevice_lo > bcdDevice)
++ mod->bcdDevice_lo > device_descriptor->bcdDevice)
+ continue;
+
+ if ((mod->match_flags & USB_MATCH_DEV_HI) &&
+- mod->bcdDevice_hi < bcdDevice)
++ mod->bcdDevice_hi < device_descriptor->bcdDevice)
+ continue;
+
+ if ((mod->match_flags & USB_MATCH_DEV_CLASS) &&
+- mod->bDeviceClass != bDeviceClass)
++ mod->bDeviceClass != device_descriptor->bDeviceClass)
+ continue;
+
+ if ((mod->match_flags & USB_MATCH_DEV_SUBCLASS) &&
+- mod->bDeviceSubClass != bDeviceSubClass)
++ mod->bDeviceSubClass != device_descriptor->bDeviceSubClass)
+ continue;
+
+ if ((mod->match_flags & USB_MATCH_DEV_PROTOCOL) &&
+- mod->bDeviceProtocol != bDeviceProtocol)
++ mod->bDeviceProtocol != device_descriptor->bDeviceProtocol)
+ continue;
+
+ if ((mod->match_flags & USB_MATCH_INT_CLASS) &&
+- mod->bInterfaceClass != bInterfaceClass)
++ mod->bInterfaceClass != interface_descriptor->bInterfaceClass)
+ continue;
+
+ if ((mod->match_flags & USB_MATCH_INT_SUBCLASS) &&
+- mod->bInterfaceSubClass != bInterfaceSubClass)
++ mod->bInterfaceSubClass != interface_descriptor->bInterfaceSubClass)
+ continue;
+
+ if ((mod->match_flags & USB_MATCH_INT_PROTOCOL) &&
+- mod->bInterfaceProtocol != bInterfaceProtocol)
++ mod->bInterfaceProtocol != interface_descriptor->bInterfaceProtocol)
+ continue;
+
+ if (checkname != NULL) {
+@@ -315,161 +290,154 @@
+ }
+ }
+
+-static int usb_control_msg(int fd,
+- u_int8_t requesttype,
+- u_int8_t request,
+- u_int16_t value,
+- u_int16_t index,
+- unsigned int size,
+- void *data)
++static void process_device(const char *path)
+ {
+- int result;
+- int try;
+-
+- struct usbdevfs_ctrltransfer ctrl;
+-
+- ctrl.requesttype = requesttype;
+- ctrl.request = request;
+- ctrl.value = value;
+- ctrl.index = index;
+- ctrl.length = size;
+- ctrl.data = data;
+- ctrl.timeout = CTRL_TIMEOUT;
+-
+- /* At least on UHCI controllers, this ioctl gets a lot of
+- ETIMEDOUT errors which can often be retried with success
+- one is persistent enough. So, we try 100 times, which work
+- on one machine, but not on my notebook computer.
+- --Adam J. Richter (adam@yggdrasil.com) 2000 November 03. */
+-
+- try = 0;
+- do {
+- result = ioctl(fd, USBDEVFS_CONTROL, &ctrl);
+- try++;
+- } while (try < CTRL_RETRIES && result == -1 && errno == ETIMEDOUT);
+- return result;
+-}
++ struct usb_device *dev;
++ struct usb_dev_handle *udev;
++ int i, j, k;
+
++ dev = get_usb_device(path);
+
+-static void do_config(int fd, unsigned int nr)
+-{
+- unsigned char buf[1024], *p;
+- unsigned int sz;
++ if (!dev) {
++ fprintf(stderr, "Cannot open %s\n", path);
++ return;
++ }
++
++
++ udev = usb_open(dev);
++
++ for (i = 0 ; i < dev->descriptor.bNumConfigurations ; i++)
++ for (j = 0 ; j < dev->config[i].bNumInterfaces ; j++)
++ for (k = 0 ; k < dev->config[i].interface[j].num_altsetting ; k++)
++ match_modules(&dev->descriptor,
++ &dev->config[i].interface[j].altsetting[k]);
+
+- if (usb_control_msg(fd, USB_DIR_IN, USB_REQ_GET_DESCRIPTOR,
+- (USB_DT_CONFIG << 8) | nr,
+- 0, USB_DT_CONFIG_SIZE, buf) < 0) {
+- fprintf(stderr ,"cannot get config descriptor %d, %s (%d)\n",
+- nr, strerror(errno), errno);
+- return;
+- }
+- if (buf[0] < USB_DT_CONFIG_SIZE || buf[1] != USB_DT_CONFIG)
+- fprintf(stderr, "Warning: invalid config descriptor\n");
+- sz = buf[2] | buf[3] << 8;
+- if (sz > sizeof(buf)) {
+- fprintf(stderr,
+- "Config %d descriptor too long, truncating\n", nr);
+- sz = sizeof(buf);
+- }
+- if (usb_control_msg(fd, USB_DIR_IN, USB_REQ_GET_DESCRIPTOR,
+- (USB_DT_CONFIG << 8) | nr, 0, sz, buf) < 0) {
+- fprintf(stderr, "cannot get config descriptor %d, %s (%d)\n",
+- nr, strerror(errno), errno);
+- return;
+- }
+- p = buf;
+- while (sz >= 2 && p[0] >= 2 && p[0] < sz) {
+- if (p[1] == USB_DT_INTERFACE) {
+- const int intClass = p[5];
+- const int intSubClass = p[6];
+- const int intProto = p[7];
+- match_modules(intClass, intSubClass, intProto);
+- }
+- sz -= p[0];
+- p += p[0];
+- }
++ usb_close(udev);
+ }
+
+-static void process_device(const char *path)
++static void process_args(char *product,
++ char *type,
++ char *interface)
+ {
+- unsigned char buf[USB_DT_DEVICE_SIZE];
+- int fd;
+- unsigned int i, maxcfg;
+-
+- if ((fd = open(path, O_RDWR)) == -1) {
+- fprintf(stderr, "cannot open %s, %s (%d)\n",
+- path, strerror(errno), errno);
+- return;
+- }
+- if (usb_control_msg(fd, USB_DIR_IN, USB_REQ_GET_DESCRIPTOR,
+- (USB_DT_DEVICE << 8), 0, USB_DT_DEVICE_SIZE, buf)
+- < 0) {
+- perror("cannot get config descriptor");
+- goto err;
+- }
+- bDeviceClass = buf[4];
+- bDeviceSubClass = buf[5];
+- bDeviceProtocol = buf[6];
+- idVendor = buf[8] | (buf[9] << 8);
+- idProduct = buf[10] | (buf[11] << 8);
+- bcdDevice = buf[12] | (buf[13] << 8);
+-
+- maxcfg = buf[17];
+- if (buf[0] < 18 || buf[1] != USB_DT_DEVICE)
+- maxcfg = 1;
++ int a, b, c;
++ struct usb_device_descriptor dd;
++ struct usb_interface_descriptor id;
+
+- for (i = 0; i < maxcfg; i++)
+- do_config(fd, i);
+- err:
+- close(fd);
++ memset(&dd, 0, sizeof(dd));
++ memset(&id, 0, sizeof(id));
++ if (product == NULL ||
++ sscanf(product, "%hx/%hx/%hx", &dd.idVendor, &dd.idProduct, &dd.bcdDevice) != 3) {
++ fprintf(stderr, "Bad product format: '%s'\n", product);
++ return;
++ }
++ if (type == NULL || sscanf(type, "%d/%d/%d", &a, &b, &c) != 3) {
++ fprintf(stderr, "Bad type format: '%s'", type);
++ return;
++ }
++ dd.bDeviceClass = a;
++ dd.bDeviceSubClass = b;
++ dd.bDeviceProtocol = c;
++ if (dd.bDeviceClass == 0) {
++ /* interface must be specified for device class 0 */
++ if (interface == NULL ||
++ sscanf(interface, "%d/%d/%d", &a, &b, &c) != 3) {
++ fprintf(stderr, "Bad interface format: '%s'\n", interface);
++ return;
++ }
++ id.bInterfaceClass = a;
++ id.bInterfaceSubClass = b;
++ id.bInterfaceProtocol = c;
++ } else {
++ /* interface maybe given. if so, check and use arg */
++ if (interface != NULL && *interface != '\0' &&
++ sscanf(interface, "%d/%d/%d", &a, &b, &c) != 3) {
++ fprintf(stderr, "Bad interface format: '%s'\n", interface);
++ return;
++ }
++ id.bInterfaceClass = a;
++ id.bInterfaceSubClass = b;
++ id.bInterfaceProtocol = c;
++ }
++ match_modules(&dd, &id);
+ }
+
+-
+-int
+-main (int argc, char **argv)
++int main (int argc, char *argv[])
+ {
+ int opt_index = 0;
+ int opt;
+ char *device = NULL;
+ char *pathname = NULL;
++ char *product = NULL, *type = NULL, *interface = NULL;
+
+- while ((opt = getopt_long(argc, argv, OPT_STRING, long_options,
+- &opt_index)) != -1) {
++ while ((opt = getopt_long(argc, argv, OPT_STRING, long_options, &opt_index)) != -1) {
+ switch(opt) {
+- case 'c':
+- checkname = optarg;
+- break;
+- case 'd':
+- device = optarg;
+- break;
+- case 'h':
+- printf ("Usage: usbmodules [--help] [--device /proc/bus/usb/NNN/NNN] [--check module]\n"
+- "\t[--mapfile pathname] [--version]\n"
+- " Lists kernel modules corresponding to USB devices currently plugged\n"
+- " into the computer.\n");
+- return 0;
+- case 'm':
+- pathname = optarg;
+- break;
+- case 'v':
+- puts (VERSION);
+- return 0;
+- default:
+- fprintf(stderr,
+- "Unknown argument character \"%c\".\n",
+- opt);
+- return 1;
++ case 'c':
++ checkname = optarg;
++ break;
++ case 'd':
++ device = optarg;
++ break;
++ case 'h':
++ printf ("Usage: usbmodules [options]...\n"
++ "Lists kernel modules corresponding to USB devices currently plugged\n"
++ "\n"
++ "OPTIONS\n"
++ " -d, --device /proc/bus/usb/NNN/NNN\n"
++ " Selects which device usbmodules will examine\n"
++ " -c, --check module\n"
++ " Check if the given module's exported USB ID patterns matches\n"
++ " -m, --mapfile /etc/hotplug/usb.handmap\n"
++ " Specify a mapfile\n"
++ " -p, --product xx/xx/xx\n"
++ " -t, --type dd/dd/dd\n"
++ " -i, --interface dd/dd/dd\n"
++ " -h, --help\n"
++ " Print help screen\n"
++ " -v, --version\n"
++ " Show version of program\n"
++ "\n");
++ return 0;
++ case 'm':
++ pathname = optarg;
++ break;
++ case 'i':
++ interface = optarg;
++ break;
++ case 'p':
++ product = optarg;
++ break;
++ case 't':
++ type = optarg;
++ break;
++ case 'v':
++ puts (VERSION);
++ return 0;
++ default:
++ fprintf(stderr,
++ "Unknown argument character \"%c\".\n",
++ opt);
++ return 1;
+ }
+ }
+
+- if (device == NULL) {
++ if (device == NULL &&
++ (product == NULL || type == NULL || interface == NULL) ) {
+ fprintf (stderr,
+ "You must specify a device with something like:\n"
+- "\tusbmodules --device /proc/bus/usb/001/009\n");
+- return 1;
++ "\tusbmodules --device /proc/bus/usb/001/009\n"
++ "or\n"
++ "\tusbmodules --product 82d/100/100 --type 0/0/0 --interface 0/0/0\n");
++ return 1;
+ }
++
+ read_modules_usbmap(pathname);
+- process_device(device);
++ usb_init();
++ usb_find_busses();
++ usb_find_devices();
++ if (device != NULL)
++ process_device(device);
++ if (product != NULL && type != NULL)
++ process_args(product, type, interface);
+
+ if (checkname != NULL)
+ return 1; /* The module being checked was not needed */
+--- usbutils-0.11/usbutils.spec~usbutils-0.11+cvs20041108
++++ usbutils-0.11/usbutils.spec
+@@ -1,5 +1,5 @@
+ %define name usbutils
+-%define version 0.11
++%define version 0.12
+ %define release 1
+ Name: %{name}
+ Version: %{version}
+@@ -10,6 +10,7 @@
+ ExclusiveOS: Linux
+ Summary: Linux USB utilities.
+ Group: Applications/System
++BuildPrereq: libusb-devel
+
+ %description
+ This package contains an utility for inspecting
+--- /dev/null
++++ usbutils-0.11/update-usbids
+@@ -0,0 +1,37 @@
++#!/bin/sh
++# This is a script stolen from pciutils. Thanks to Martin Mares <mj@atrey.karlin.mff.cuni.cz>
++# for writing and publishing it under the GNU General Public License.
++
++set -e
++
++SRC="http://linux-usb.sourceforge.net/usb.ids"
++DEST=/var/lib/usbutils//usb.ids
++
++umask 022
++
++if which wget >/dev/null ; then
++ DL="wget -O $DEST.new $SRC"
++elif which lynx >/dev/null ; then
++ DL="eval lynx -source $SRC >$DEST.new"
++else
++ echo >&2 "update-usbids: cannot find wget nor lynx"
++ exit 1
++fi
++
++if ! $DL ; then
++ echo >&2 "update-usbids: download failed"
++ rm -f $DEST.new
++ exit 1
++fi
++
++if ! grep >/dev/null "^C " $DEST.new ; then
++ echo >&2 "update-usbids: missing class info, probably truncated file"
++ exit 1
++fi
++
++if [ -f $DEST ] ; then
++ mv $DEST $DEST.old
++fi
++mv $DEST.new $DEST
++
++echo "Done."
+--- /dev/null
++++ usbutils-0.11/update-usbids.8
+@@ -0,0 +1,27 @@
++.TH update-usbids 8 "27 October 2004" "usbutils-0.11" "Linux USB Utilities"
++.IX update-usbids
++
++.SH NAME
++update-usbids \- download new version of the USB ID list
++
++.SH SYNOPSIS
++.B update-usbids
++
++.SH DESCRIPTION
++.B update-usbids
++fetches the current version of the usb.ids file from the primary distribution
++site and installs it.
++
++This utility requires either wget or lynx to be installed. If gzip or bzip2
++are available, it automatically downloads the compressed version of the list.
++
++.SH FILES
++.TP
++.B /var/lib/usbutils/usb.ids
++Here we install the new list.
++
++.SH SEE ALSO
++.BR lsusb(8).
++
++.SH AUTHOR
++Thomas Sailer, <sailer@ife.ee.ethz.ch>.
diff --git a/util-linux/util-linux-2.12/ioctl.diff b/util-linux/util-linux-2.12/ioctl.diff
index e69de29bb2..06cf9c02d9 100644
--- a/util-linux/util-linux-2.12/ioctl.diff
+++ b/util-linux/util-linux-2.12/ioctl.diff
@@ -0,0 +1,52 @@
+diff -Naur util-linux-2.12/disk-utils/blockdev.c util-linux-2.12-ok/disk-utils/blockdev.c
+--- util-linux-2.12/disk-utils/blockdev.c 2002-03-08 23:57:02.000000000 +0100
++++ util-linux-2.12-ok/disk-utils/blockdev.c 2004-11-09 14:21:13.000000000 +0100
+@@ -24,8 +24,8 @@
+ #define BLKRASET _IO(0x12,98)
+ #define BLKRAGET _IO(0x12,99)
+ #define BLKSSZGET _IO(0x12,104)
+-#define BLKBSZGET _IOR(0x12,112,sizeof(int))
+-#define BLKBSZSET _IOW(0x12,113,sizeof(int))
++#define BLKBSZGET _IOR(0x12,112,int)
++#define BLKBSZSET _IOW(0x12,113,int)
+ #endif
+
+ /* Maybe <linux/hdreg.h> could be included */
+diff -Naur util-linux-2.12/disk-utils/elvtune.c util-linux-2.12-ok/disk-utils/elvtune.c
+--- util-linux-2.12/disk-utils/elvtune.c 2002-03-08 23:57:49.000000000 +0100
++++ util-linux-2.12-ok/disk-utils/elvtune.c 2004-11-09 14:21:44.000000000 +0100
+@@ -37,8 +37,8 @@
+ int max_bomb_segments;
+ } blkelv_ioctl_arg_t;
+
+-#define BLKELVGET _IOR(0x12,106,sizeof(blkelv_ioctl_arg_t))
+-#define BLKELVSET _IOW(0x12,107,sizeof(blkelv_ioctl_arg_t))
++#define BLKELVGET _IOR(0x12,106,blkelv_ioctl_arg_t)
++#define BLKELVSET _IOW(0x12,107,blkelv_ioctl_arg_t)
+
+ static void
+ usage(void) {
+diff -Naur util-linux-2.12/fdisk/common.h util-linux-2.12-ok/fdisk/common.h
+--- util-linux-2.12/fdisk/common.h 2003-07-13 15:59:53.000000000 +0200
++++ util-linux-2.12-ok/fdisk/common.h 2004-11-09 14:37:41.000000000 +0100
+@@ -6,7 +6,7 @@
+ #define BLKGETSIZE _IO(0x12,96) /* return device size */
+ #define BLKFLSBUF _IO(0x12,97) /* flush buffer cache */
+ #define BLKSSZGET _IO(0x12,104) /* get block device sector size */
+-#define BLKGETSIZE64 _IOR(0x12,114,8) /* 8 = sizeof(u64) */
++#define BLKGETSIZE64 _IOR(0x12,114,unsigned long long)/* 8 = sizeof(u64) */
+
+ /* including <linux/hdreg.h> also fails */
+ struct hd_geometry {
+diff -Naur util-linux-2.12/partx/gpt.c util-linux-2.12-ok/partx/gpt.c
+--- util-linux-2.12/partx/gpt.c 2002-08-04 02:20:16.000000000 +0200
++++ util-linux-2.12-ok/partx/gpt.c 2004-11-09 14:23:18.000000000 +0100
+@@ -41,7 +41,7 @@
+ #define BLKGETLASTSECT _IO(0x12,108) /* get last sector of block device */
+ #define BLKGETSIZE _IO(0x12,96) /* return device size */
+ #define BLKSSZGET _IO(0x12,104) /* get block device sector size */
+-#define BLKGETSIZE64 _IOR(0x12,114,sizeof(uint64_t)) /* return device size in bytes (u64 *arg) */
++#define BLKGETSIZE64 _IOR(0x12,114,uint64_t) /* return device size in bytes (u64 *arg) */
+
+ struct blkdev_ioctl_param {
+ unsigned int block;
diff --git a/visiscript/files/qcleanuphandler.h b/visiscript/files/qcleanuphandler.h
index e69de29bb2..037f911064 100644
--- a/visiscript/files/qcleanuphandler.h
+++ b/visiscript/files/qcleanuphandler.h
@@ -0,0 +1,127 @@
+/****************************************************************************
+** $Id$
+**
+** ...
+**
+** Copyright (C) 2001-2002 Trolltech AS. All rights reserved.
+**
+** This file is part of the tools module of the Qt GUI Toolkit.
+**
+** This file may be distributed under the terms of the Q Public License
+** as defined by Trolltech AS of Norway and appearing in the file
+** LICENSE.QPL included in the packaging of this file.
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU General Public License version 2 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.
+**
+** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition
+** licenses may use this file in accordance with the Qt Commercial License
+** Agreement provided with the Software.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
+** information about Qt Commercial License Agreements.
+** See http://www.trolltech.com/qpl/ for QPL licensing information.
+** See http://www.trolltech.com/gpl/ for GPL licensing information.
+**
+** Contact info@trolltech.com if any conditions of this licensing are
+** not clear to you.
+**
+**********************************************************************/
+
+#ifndef QCLEANUPHANDLER_H
+#define QCLEANUPHANDLER_H
+
+#ifndef QT_H
+#include "qptrlist.h"
+#endif // QT_H
+
+template<class Type>
+class QCleanupHandler
+{
+public:
+ QCleanupHandler() : cleanupObjects( 0 ) {}
+ ~QCleanupHandler() { clear(); }
+
+ Type* add( Type **object ) {
+ if ( !cleanupObjects )
+ cleanupObjects = new QPtrList<Type*>;
+ cleanupObjects->insert( 0, object );
+ return *object;
+ }
+
+ void remove( Type **object ) {
+ if ( !cleanupObjects )
+ return;
+ if ( cleanupObjects->findRef( object ) >= 0 )
+ (void) cleanupObjects->take();
+ }
+
+ bool isEmpty() const {
+ return cleanupObjects ? cleanupObjects->isEmpty() : TRUE;
+ }
+
+ void clear() {
+ if ( !cleanupObjects )
+ return;
+ QPtrListIterator<Type*> it( *cleanupObjects );
+ Type **object;
+ while ( ( object = it.current() ) ) {
+ delete *object;
+ *object = 0;
+ cleanupObjects->remove( object );
+ }
+ delete cleanupObjects;
+ cleanupObjects = 0;
+ }
+
+private:
+ QPtrList<Type*> *cleanupObjects;
+};
+
+template<class Type>
+class QSingleCleanupHandler
+{
+public:
+ QSingleCleanupHandler() : object( 0 ) {}
+ ~QSingleCleanupHandler() {
+ if ( object ) {
+ delete *object;
+ *object = 0;
+ }
+ }
+ Type* set( Type **o ) {
+ object = o;
+ return *object;
+ }
+ void reset() { object = 0; }
+private:
+ Type **object;
+};
+
+template<class Type>
+class QSharedCleanupHandler
+{
+public:
+ QSharedCleanupHandler() : object( 0 ) {}
+ ~QSharedCleanupHandler() {
+ if ( object ) {
+ if ( (*object)->deref() )
+ delete *object;
+ *object = 0;
+ }
+ }
+ Type* set( Type **o ) {
+ object = o;
+ return *object;
+ }
+ void reset() { object = 0; }
+private:
+ Type **object;
+};
+
+#endif //QCLEANUPHANDLER_H
diff --git a/visiscript/files/qptrlist.h b/visiscript/files/qptrlist.h
index e69de29bb2..9a07e1eda6 100644
--- a/visiscript/files/qptrlist.h
+++ b/visiscript/files/qptrlist.h
@@ -0,0 +1,9 @@
+#ifndef QTCOMPAT_QPTRLIST_H
+#define QTCOMPAT_QPTRLIST_H
+
+#include <qlist.h>
+
+#define QPtrList QList
+#define QPtrListIterator QListIterator
+
+#endif
diff --git a/wlan-ng/files/post-down b/wlan-ng/files/post-down
index e69de29bb2..bf4ba0ae60 100644
--- a/wlan-ng/files/post-down
+++ b/wlan-ng/files/post-down
@@ -0,0 +1,31 @@
+#!/bin/sh
+#
+# This script takes care of bringing down wlan-ng devices.
+# It is run by ifdown.
+
+WLANCTL=/sbin/wlanctl-ng
+
+if [ ! -x $WLANCTL ] ; then
+ exit 0
+fi
+
+case $IFACE in
+wlan?)
+ # Reset device to make sure it is not trying to generate
+ # interrupts.
+ result=`$WLANCTL $IFACE lnxreq_ifstate ifstate=disable`
+
+ # Remove modules. Of course, for all I know they are still in use
+ # by another interface, in which case these rmmods will fail. Also,
+ # depending on how modutils is set up, different prism2 modules
+ # might be loaded.
+ (
+ rmmod prism2_pci || rmmod prism2_cs || rmmod prism2_usb || \
+ rmmod prism2_plx || true
+ rmmod p80211 || true
+ ) 2>/dev/null
+
+ # Remove lock file, just in case.
+ rm -f /var/lock/wlan-$IFACE.lock
+;;
+esac
diff --git a/wlan-ng/files/resume b/wlan-ng/files/resume
index e69de29bb2..f875939da6 100644
--- a/wlan-ng/files/resume
+++ b/wlan-ng/files/resume
@@ -0,0 +1,22 @@
+#!/bin/sh
+# This script is run on resume to force a down and up of any linux-wlanng
+# iterfaces. Otherwise they could get into a weird state.
+IFACES=$(ifconfig | grep -B 1 ^wlan | cut -d ' ' -f 1)
+for iface in $IFACES; do
+ echo down $ifcase >>/tmp/log
+ ifdown $iface || ifconfig $iface down
+done
+
+rmmod prism2_pci 2>/dev/null
+rmmod prism2_usb 2>/dev/null
+rmmod prism2_cs 2>/dev/null
+rmmod prism2_plx 2>/dev/null
+rmmod p80211 2>/dev/null
+
+# There is a race here for some reason.
+sleep 2
+
+for iface in $IFACES; do
+ ifup $iface
+done
+
diff --git a/x11/files/autofoo.patch b/x11/files/autofoo.patch
index e69de29bb2..8bdba09768 100644
--- a/x11/files/autofoo.patch
+++ b/x11/files/autofoo.patch
@@ -0,0 +1,22 @@
+
+#
+# Patch managed by http://www.holgerschurig.de/patcher.html
+#
+
+--- libX11-6.2.1/configure.ac~autofoo
++++ libX11-6.2.1/configure.ac
+@@ -7,12 +7,12 @@
+ [jg@freedesktop.org],
+ libX11)
+ AC_CONFIG_SRCDIR([Makefile.am])
+-AM_INIT_AUTOMAKE([dist-bzip2])
++AC_CONFIG_AUX_DIR(.)
+
++AM_INIT_AUTOMAKE([dist-bzip2])
+ AM_MAINTAINER_MODE
+
+ AM_CONFIG_HEADER([src/config.h])
+-AC_CONFIG_AUX_DIR(.)
+
+ # Checks for programs.
+ AC_PROG_LIBTOOL
diff --git a/xau/files/autofoo.patch b/xau/files/autofoo.patch
index e69de29bb2..1e42eb974d 100644
--- a/xau/files/autofoo.patch
+++ b/xau/files/autofoo.patch
@@ -0,0 +1,21 @@
+
+#
+# Patch managed by http://www.holgerschurig.de/patcher.html
+#
+
+--- libXau-0.1.1/configure.ac~autofoo
++++ libXau-0.1.1/configure.ac
+@@ -25,11 +25,10 @@
+
+ AC_PREREQ([2.57])
+ AC_INIT(libXau, [0.1.1], [keithp@keithp.com],libXau)
+-AM_INIT_AUTOMAKE([dist-bzip2])
+-AM_MAINTAINER_MODE
+-
+ AM_CONFIG_HEADER(config.h)
+ AC_CONFIG_AUX_DIR(.)
++AM_INIT_AUTOMAKE([dist-bzip2])
++AM_MAINTAINER_MODE
+
+ AC_PROG_CC
+ AC_PROG_INSTALL
diff --git a/xdmcp/files/autofoo.patch b/xdmcp/files/autofoo.patch
index e69de29bb2..ff1a76ad04 100644
--- a/xdmcp/files/autofoo.patch
+++ b/xdmcp/files/autofoo.patch
@@ -0,0 +1,22 @@
+
+#
+# Patch managed by http://www.holgerschurig.de/patcher.html
+#
+
+--- libXdmcp-0.1.3/configure.ac~autofoo
++++ libXdmcp-0.1.3/configure.ac
+@@ -25,12 +25,11 @@
+
+ AC_PREREQ([2.57])
+ AC_INIT(libXdmcp, [0.1.3], [keithp@keithp.com],libXdmcp)
++AC_CONFIG_AUX_DIR(.)
++AC_CONFIG_HEADERS([include/X11/Xdmcpconf.h])
+ AM_INIT_AUTOMAKE([dist-bzip2])
+ AM_MAINTAINER_MODE
+-
+ AM_CONFIG_HEADER(config.h)
+-AC_CONFIG_AUX_DIR(.)
+-AC_CONFIG_HEADERS([include/X11/Xdmcpconf.h])
+
+ AC_PROG_CC
+ AC_PROG_INSTALL
diff --git a/xext/files/autofoo.patch b/xext/files/autofoo.patch
index e69de29bb2..c006884526 100644
--- a/xext/files/autofoo.patch
+++ b/xext/files/autofoo.patch
@@ -0,0 +1,18 @@
+
+#
+# Patch managed by http://www.holgerschurig.de/patcher.html
+#
+
+--- Xext/configure.ac~autofoo
++++ Xext/configure.ac
+@@ -8,9 +8,8 @@
+ libXext)
+
+ AC_CONFIG_SRCDIR([Makefile.am])
+-AM_INIT_AUTOMAKE([dist-bzip2])
+-
+ AC_CONFIG_AUX_DIR(.)
++AM_INIT_AUTOMAKE([dist-bzip2])
+
+ AM_MAINTAINER_MODE
+
diff --git a/xqt/files/imake-staging.patch b/xqt/files/imake-staging.patch
index e69de29bb2..bf2e350830 100644
--- a/xqt/files/imake-staging.patch
+++ b/xqt/files/imake-staging.patch
@@ -0,0 +1,38 @@
+--- xc/config/cf/Imake.tmpl.orig 2004-07-28 04:24:29.000000000 +0100
++++ xc/config/cf/Imake.tmpl 2004-09-14 21:03:06.000000000 +0100
+@@ -2038,11 +2038,11 @@
+ */
+ ALLINCLUDES = $(INCLUDES) $(EXTRA_INCLUDES) $(TOP_INCLUDES) $(INSTALLED_INCLUDES) $(STD_INCLUDES)
+ ALLDEFINES = $(ALLINCLUDES) $(STD_DEFINES) $(PROTO_DEFINES) $(THREADS_DEFINES) $(MODULE_DEFINES) $(DEFINES) $(EXTRA_DEFINES)
+- CFLAGS = $(CDEBUGFLAGS) $(CCOPTIONS) $(THREADS_CFLAGS) $(MODULE_CFLAGS) $(ALLDEFINES)
++ CFLAGS = $(CDEBUGFLAGS) $(CCOPTIONS) $(THREADS_CFLAGS) $(MODULE_CFLAGS) $(ALLDEFINES) $(CC_STAGING)
+ LINTFLAGS = $(LINTOPTS) -DLINT $(ALLDEFINES) $(DEPEND_DEFINES)
+ LDPRELIB = LdPreLib $(INSTALLED_LIBS)
+ LDPOSTLIB = LdPostLib
+- LDOPTIONS = $(CDEBUGFLAGS) $(CCOPTIONS) $(EXTRA_LDOPTIONS) $(THREADS_LDFLAGS) $(LOCAL_LDFLAGS) $(LDPRELIBS)
++ LDOPTIONS = $(CDEBUGFLAGS) $(CCOPTIONS) $(EXTRA_LDOPTIONS) $(THREADS_LDFLAGS) $(LOCAL_LDFLAGS) $(LDPRELIBS) $(LD_STAGING)
+ CXXLDOPTIONS = $(CXXDEBUGFLAGS) $(CXXOPTIONS) $(EXTRA_LDOPTIONS) $(THREADS_CXXLDFLAGS) $(LOCAL_LDFLAGS) $(LDPRELIBS)
+
+ LDLIBS = $(LDPOSTLIBS) $(THREADS_LIBS) $(SYS_LIBRARIES) $(EXTRA_LIBRARIES)
+--- xc/config/cf/Library.tmpl~ 2004-05-24 20:06:57.000000000 +0100
++++ xc/config/cf/Library.tmpl 2004-09-14 21:10:29.000000000 +0100
+@@ -114,7 +114,7 @@
+ STD_DEFINES = LibraryDefines $(PROJECT_DEFINES)
+ CDEBUGFLAGS = LibraryCDebugFlags
+ CLIBDEBUGFLAGS = LibraryDebugOpt
+- CFLAGS = $(CDEBUGFLAGS) $(CLIBDEBUGFLAGS) $(CCOPTIONS) $(THREADS_CFLAGS) $(ALLDEFINES)
++ CFLAGS = $(CDEBUGFLAGS) $(CLIBDEBUGFLAGS) $(CCOPTIONS) $(THREADS_CFLAGS) $(ALLDEFINES) $(CC_STAGING)
+ # if defined(LargePICTable) && LargePICTable && defined(LargePositionIndependentCFlags)
+ PICFLAGS = LargePositionIndependentCFlags
+ # endif
+--- xc/config/cf/X11.tmpl~ 2004-09-03 17:18:18.000000000 +0100
++++ xc/config/cf/X11.tmpl 2004-09-14 21:30:30.000000000 +0100
+@@ -3359,7 +3359,7 @@
+ FREETYPE2LIBDIR = Freetype2LibDir
+ FREETYPE2INCDIR = Freetype2IncDir
+ #if Freetype2LibDirStandard
+-FREETYPE2LIB = -lfreetype
++FREETYPE2LIB = $(LD_STAGING) -lfreetype
+ #else
+ FREETYPE2LIB = -L$(FREETYPE2LIBDIR) LinkerRuntimeLibraryPathFlag($(FREETYPE2LIBDIR)) -lfreetype
+ #endif
diff --git a/xqt/files/moc_call.patch b/xqt/files/moc_call.patch
index e69de29bb2..bde386e2bc 100644
--- a/xqt/files/moc_call.patch
+++ b/xqt/files/moc_call.patch
@@ -0,0 +1,16 @@
+
+#
+# Patch managed by http://www.holgerschurig.de/patcher.html
+#
+
+--- xc/programs/Xserver/hw/xqt/Imakefile~moc_call
++++ xc/programs/Xserver/hw/xqt/Imakefile
+@@ -12,7 +12,7 @@
+
+ QTDIR = QtDir
+
+-MOC = QtDir/bin/moc
++MOC = MocBin
+
+ INCLUDES = -I. \
+ -I$(XBUILDINCDIR) -I$(FONTINCSRC) \
diff --git a/xserver/xserver-kdrive/fbdev-not-fix.patch b/xserver/xserver-kdrive/fbdev-not-fix.patch
index e69de29bb2..f87e7cc2cd 100644
--- a/xserver/xserver-kdrive/fbdev-not-fix.patch
+++ b/xserver/xserver-kdrive/fbdev-not-fix.patch
@@ -0,0 +1,14 @@
+--- xserver/hw/kdrive/fbdev/fbdev.c~ 2004-09-15 00:08:10.000000000 +0100
++++ xserver/hw/kdrive/fbdev/fbdev.c 2004-11-13 17:47:02.000000000 +0000
+@@ -198,6 +198,11 @@
+ return FALSE;
+ }
+
++ /* Re-get the "fixed" parameters since they might have changed */
++ k = ioctl (priv->fd, FBIOGET_FSCREENINFO, &priv->fix);
++ if (k < 0)
++ perror ("FBIOGET_FSCREENINFO");
++
+ /* Now get the new screeninfo */
+ ioctl (priv->fd, FBIOGET_VSCREENINFO, &priv->var);
+ depth = priv->var.bits_per_pixel;